http://blog.csdn.net/wealoong/article/details/7711922
参考:
Android display架构分析三-Kernel Space Display架构介绍
Android display架构分析四-msm_fb.c 函数和数据结构介绍。。。。。
高通Android平台下关于display部分的几个关键问题
转自 http://disanji.net/2011/03/03/android-2-2-display-sw-structure/
高通7系列硬件架构分析
高通7系列 Display的硬件部分主要由下面几个部分组成:
A、MDP
高通MSM7200A内部模块,主要负责显示数据的转换和部分图像处理功能理,如YUV转RGB,放大缩小、旋转等。MDP内部的MDP DMA负责数据从DDR到MDDI Host的传输(可以完成RGB之间的转换,如RGB565转成RGB666,这个转换工能载目前的code 中没有使用)。
B、MDDI
一种采用差分信号的高速的串行数据传输总线,只负责数据传输,无其它功能;其中的MDDI Hosat提供并行数据和串行数据之间的转换和缓冲功能。由于外面是VGA的屏幕,数据量较大,为了减少对EBI2总线的影响,传输总线使用MDDI,而非之前的EBI2。
C、MDDI Bridge
由于现在采用的外接LCD并不支持MDDI接口,故需要外加MDDI转换器,即MDDI bridge,来把MDDI数据转换成RGB接口数据。这里采用的EPSON MDDIBridge还有LCD Controller功能,可以完成其它一些数据处理的功能,如数据格式转换、支持TV-OUT、PIP等;并且还可以提供一定数量的GPIO。目前我们主要用它把HOST端MDDI传递过来的显示数据和控制数据(初始化配置等)转换成并行的数据传递给LCD。
D、LCD module
主要是LCD Driver IC 和TFT Panel,负责把MDDI Bridge传来的显存中的图像示在自己的Panel上。
一、Overview
上图的原型取自高通的文档,由于原图无法描述现有的架构,我在原图的基础了做了些修改,主要是增加了overlay部分,另外其他部分根据现有的软件也做了些许改动。下面先对上图做个大概的介绍,后面会针对重点部分做详细的分析。
最上面一层为应用程序,根据数据类型以及应用的不同可以分为几种。
第一种是最普通的应用,如UI界面的显示,这部分通常数据类型为RGB格式,数据无须再经过特殊的处理。该应用可以说遍布各个应用程序,几乎是实时存在的。
第二种是针对大块YUV数据的应用,如camera的preview、视频的播放等。该应用只针对特定的应用程序,开启时通过overlay直接把大块的YUV数据送到kernel显示。
第三种其实和第一种类似,只不过由于应用的需求在显示之前需要对数据进行2D、3D的处理(使用OpenGL、OpenVG、SVG、SKIA),处理之后的流程和普通的显示就没什么差别了。一般在Game、地图、Flash等应用中会用到。
应用之下是framework,其中最核心的就是surfaceflinger了,它为所有的应用程序的显示提供服务。由于overlay的接口挂在surfaceflinger里面(虽然2者在功能上不相干),所有使用overlay的AP需要通过surfaceflinger才可以访问overlay;另外,由于surfaceflinger需要使用OpenGL来compose surface,这也就是为什么surfacelfinger会调用EGL wrapper了,EGL wrapper是对Graphics HAL的封装,除了surfaceflinger会调用它来compose surface外,上层的2D、3D应用也会调用它来进行图形处理。
再下一层就是HAL了。
首先一个是overlay模块,对上提供control channel和data channel;对下则通过系统调用到kernel中的MDP driver。
再一个是Gralloc模块,注意它是和overlay并列的,它包含2个部分,一部分是为上层提供pmem的接口,另一部分则是对framebuffer进行刷新,这里的framebuffer其实就是UI的数据。由此可见上层有2个通道把显示数据送到kernel中,framebuffer是传统的方式,overlay是android(éclair以后)后增加的。
红色及右边部分是OpenGL的HAL,其中红色部分代表HW solution,高通提供的,这部分是没有源码的;右边的software graphics library是SW solution,android自身的。HW和SW solution可以同时存在也可以只有一个,后面会讲解。
再往下就是kernel中的driver了,最主要的就是fb设备驱动以及MDP4 overlay的驱动,从硬件上看2者是并列的,framebuffer最终也是通过overlay方式送入MDP的。PMEM和KGSL分别对应kernel中pmem的driver(/dev/pmem)和Adreno220的driver。
二、Surfaceflinger详解
1.overview
Surfaceflinger可以说是Android显示系统中的核心,在android当中它是一个service,提供系统范围内的surface composer 功能,它能够将各种应用程序的2D 、3D surface 进行组合,合并最终得到的一个main surface数据会送入显存。简单的说,surfaceflinger就像是画布,它不关心画上去的内容,只是一味的执行合成功能,当然要根据画的位置、大小以及效果等参数。这很像Photoshop中的各个Layer,你可以在不同的layer画任意的内容,每个layer可以设置位置、大小、效果参数等,最终通过merge合成一个layer。
从应用的角度看,每个应用程序可能对应一个或多个图形界面,每个界面可以看作是一个surface。首先每个surface有它的位置、大小、内容等元素,这些元素是可以随便变化的;另外不同的surface的位置会有重叠,会涉及到透明度等效果处理问题,这些都是通过surfaceflinger来完成的。当然了,surfaceflinger担任是一个管理的职责,对于效果处理及合成它是通过OpenGL来做的,但前提是surfaceflinger需要把相关参数计算好,如重叠的位置等。
2.Surfaceflinger在系统中的位置
Android中的图形系统采用Client/Server架构。服务端负责Surface的合成等处理工作,客户端提供接口给上层操作自己的Surface,并向服务端发送消息完成实际处理工作。服务端 (即SurfaceFlinger)主要由c++代码编写而成。客户端端代码分为两部分,一部分是由Java提供的供应用使用的api,另一部分则是由c++写成的底层实现。如下图所示:
除去最上层的应用不算,surface最上层的接口就是java surface了,文件路径如下:
frameworks/base/core/java/android/view/Surface.java,该文件中的接口会被应用间接调用。
我们从JNI开始看,surface的JNI文件路径如下:
frameworks/base/core/jni/android_view_Surface.cpp,里面的接口大概分为2类,一类是负责管理ibinder通信的;另一类才是和显示控制相关的,第二类接口会直接调用C实现函数。
C实现的文件路径如下:
frameworks/base/libs/ui/Surface.cpp
我们来看看JNI中一些重要的接口:
SurfaceSession_init:本接口只会被调用一次,负责创建surfacecomposerclient,主要为进程间通信做准备。对应的销毁函数有SurfaceSession_destroy和SurfaceSession_kill。
Surface_init:负责创建surface,最终会调用到surfaceflinger中的createSurface,对应的销毁函数有Surface_destroy和Surface_release。
Surface_lockCanvas:当对一个surface进行绘图之前要调用的,将该surface锁定,并且得到surface的back buffer,应用可以绘图。
Surface_unlockCanvasAndPost:当上层绘图完毕后,通过该函数通知底层back buffer已绘制完毕,可以更新
Surface_setPosition\
Surface_setSize\
Surface_hide\
Surface_show\
Surface_setOrientation\
Surface_freeze\
Surface_unfreeze
Surface_setFlags\
Surface_setAlpha\
Surface_setMatrix:设置surface的一些属性,如大小、位置、方位、截取范围,Z-order等。其最终改变的都是surface的结构体属性,如下:
uint32_t what;//哪一项属性改变
int32_t x;//显示位置
int32_t y; //显示位置
uint32_t z; //layer顺序
uint32_t w;//宽度
uint32_t h;//高度
float alpha;//透明度
uint32_t tint;//色彩,未使用
uint8_t flags;// 标志
uint8_t mask;//屏蔽命令
uint8_t reserved;
matrix22_t matrix;//截取范围
Region transparentRegion;//透明度设置
3.JNI与Surfaceflinger的连接通讯
由于JNI及C函数实现与surfaceflinger不在同一个进程(一个在应用端-客户端,另一个在服务端),android中通过IPC(Binder)方式实现进程间通信,下图来源于网上,不过我修改了里面的一些错误,它演示了JNI和surfaceflinger建立连接以及创建surface的流程。
JNI和C函数实现我们看作是一个部分
这里看到一个比较重要的部分——SurfaceComposerClient,它是surfacelinger的客户端,通过它上层才可以和surfaceflinger使用Binder联系到一起,IsurfaceComposer和IsurfaceFlingerClient都是用来实现Binder通信的。具体流程讲解 如下:
应用程序通过JNI接口SurfaceSession_init创建SurfaceComposerClient。通过SurfaceComposerClient函数中调用getComposerService获得IsurfaceComposer的IBinder对象,然后通过这个对象的createConnection又获得IsurfaceFlingerClient的IBinder,通过这个IBinder,JNI就可以调用Surfaceflinger中的接口了,如createSurface。由于采用Binder方式,代码部分稍微复杂一些,需要多看几遍才能把流程理清楚。
4.Surfaceflinger与libui、OpenGL、显示设备的连接
这里不得不提到android对媒体框架中一个很重要的部分,那就是libui,它是一个框架库提供对底层操作的接口,比如会调用Gralloc、Overlay等HAL层接口。其他的库类继承的方式来调用libui,surfaceflinger就是这样和显示设备连接的(包括写显存和对pmem的使用)
Surfaceflinger使用OpenGL来合成surface,所以surfaceflinger会直接调用到OpenGL的接口。
它们的架构如下:
这部分的流程比较复杂,主要是各个类的继承绕的比较多,我也是看了很多遍代码以及参考了些资料才理出来,下面来详细解释下这个图:
Surfaceflinger在设计时考虑到支持多个屏幕,但目前的版本只支持一个,在surfaceflinger当中一个显示设备对应一个图中的DisplayHardware,surfaceflinger在初始化时会新建Displayhardware(请参考surfaceflinger.cpp中的readyToRun函数),它完成的主要任务一个是建立FramebufferNativeWindow,确定数据输出设备接口(请参考FramebufferNativeWindow.cpp),再一个就是初始化OpenGL,并创建main surface,后续surfaceflinger中所有的layer最终都将被画到这个main surface上(请参考displayhardware.cpp的init函数)。这样main surface、OpenGL和libui中的FramebufferNativeWindow接口就绑定在一起。
由于libEGL负责所有layer的最终合成,所以最后数据送往HAL一定要libEGL来触发,对应的函数流程是:
postFrameBuffer(surfaceflinger)->Flip(displayhardware)-> eglSwapBuffers(OpenGL)-> queueBuffer(libui)->fb_post(gralloc)
另外图中的GraphicBuffer是libui中提供的对pmem的操作接口,它会直接调用gralloc模块。关于OpenGL和Grall
Android display架构分析二-SW架构分析
*蓝色部分-用户空间应用程序
应用程序层,其中包括Android应用程序以及框架和系统运行库,和底层相关的是系统运行库,而其中和显示相关的就是Android的Surface Manager, 它负责对显示子系统的管理,并且为多个应用程序提 供了2D和3D图层的无缝融合。
*黑色部分-HAL层,在2.2.1部分会有介绍
*红色部分-Linux kernel层
Linux kernel,其中和显示部分相关的就是Linux的FrameBuffer,它是Linux系统中的显示部分驱动程序接口。Linux工作在保护模式下,User空间的应用程序无法直接调用显卡的驱动程序来直接画屏,FrameBuffer机制模仿显卡的功能,将显卡硬件结构抽象掉,可以通过 Framebuffer的读写直接对显存进行操作。用户可以将Framebuffer看成是显示内存的一个映像,将其映射到进程地址空间之后,就可以直接进行读写操作,而写操作可以立即反应在屏幕上。这种操作是抽象的,统一的。用户不必关心物理显存的位置、换页机制等等具体细节。这些都是由 Framebuffer设备驱动来完成的。
*绿色部分-HW驱动层
该部分可以看作高通显卡的驱动程序,和高通显示部分硬件相关以及外围LCD相关的驱动都被定义在这边,比如上述的显卡的一些特性都是在这边被初始化的,同样MDP和MDDI相关的驱动也都定义在这里
User Space Display功能介绍
这里的User Space就是与应用程序相关的上层部分(参考上图中的蓝色部分),其中与Kernel空间交互的部分称之为HAL-HW Abstraction Layer。
HAL其实就是用户空间的驱动程序。如果想要将 Android 在某硬件平台上执行,基本上完成这些驱动程序就行了。其内定义了 Android 对各硬件装置例如显示芯片、声音、数字相机、GPS、GSM 等等的需求。
HAL存在的几个原因:
1、 并不是所有的硬件设备都有标准的linux kernel的接口。
2、 Kernel driver涉及到GPL的版权。某些设备制造商并不原因公开硬件驱动,所以才去HAL方式绕过GPL。
3、 针对某些硬件,Android有一些特殊的需求。
在display部分,HAL的实现code在copybit.c中,应用程序直接操作这些接口即可,具体的接口如下
- struct copybit_context_t *ctx = malloc(sizeof(struct copybit_context_t));
- memset(ctx, 0, sizeof(*ctx));
- ctx->device.common.tag = HARDWARE_DEVICE_TAG;
- ctx->device.common.version = 0;
- ctx->device.common.module = module;
- ctx->device.common.close = close_copybit;
- ctx->device.set_parameter = set_parameter_copybit;//设置参数
- ctx->device.get = get;
- ctx->device.blit = blit_copybit;//传送显示数据
- ctx->device.stretch = stretch_copybit;
- ctx->mAlpha = MDP_ALPHA_NOP;
- ctx->mFlags = 0;
- ctx->mFD = open("/dev/graphics/fb0", O_RDWR, 0);//打开设备
Android display架构分析三-Kernel Space Display架构介绍
Kernel Space Display功能介绍
这里的Kernel空间(与Display相关)是Linux平台下的FB设备(参考上图中的红色部分)。下面介绍一下FB设备。
Fb即FrameBuffer的简称。framebuffer 是一种能够提取图形的硬件设备,是用户进入图形界面很好的接口。有了framebuffer,用户的应用程序不需要对底层驱动有深入了解就能够做出很好的图形。对于用户而言,它和/dev 下面的其他设备没有什么区别,用户可以把
framebuffer 看成一块内存,既可以向这块内存中写入数据,也可以从这块内存中读取数据。它允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。这种操作是抽象的,统一的。用户不必关心物理显存的位置、换页机制等等具体细节。这些都是由Framebuffer设备驱动来完成的。
从用户的角度看,帧缓冲设备和其他位于/dev下面的设备类似,它是一个字符设备,通常主设备号是29,次设备号定义帧缓冲的个数。
在LINUX系统中,设备被当作文件来处理,所有的文件包括设备文件,Linux都提供了统一的操作函数接口。上面的结构体就是Linux为FB设备提供的操作函数接口。
1)、读写(read/write)接口,即读写屏幕缓冲区(应用程序不一定会调用该接口)
2)、映射(map)操作(用户空间不能直接访问显存物理空间,需map成虚拟地址后才可以)
由于Linux工作在保护模式,每个应用程序都有自己的虚拟地址空间,在应用程序中是不能直接访问物理缓冲区地址的。为此,Linux在文件操作 file_operations结构中提供了mmap函数,可将文件的内容映射到用户空间。对于帧缓冲设备,则可通过映射操作,可将屏幕缓冲区的物理地址映射到用户空间的一段虚拟地址中,之后用户就可以通过读写这段虚拟地址访问屏幕缓冲区,在屏幕上绘图了。实际上,使用帧缓冲设备的应用程序都是通过映射操作来显示图形的。由于映射操作都是由内核来完成,下面我们将看到,帧缓冲驱动留给开发人员的工作并不多
3)、I/O控制:对于帧缓冲设备,对设备文件的ioctl操作可读取/设置显示设备及屏幕的参数,如分辨率,显示颜色数,屏幕大小等等。ioctl的操作是由底层的驱动程序来完成
Note:上述部分请参考文件fbmem.c。
如上图所示,除了上层的图形应用程序外,和Kernel空间有关的包括Linux FB设备层以及和具体HW相关的驱动层,对应的源文件分别是fb_mem.c、msm_fb.c、mddi_toshiba.c。下面会一一介绍。
自己的一点理解:安卓的display架构是在linux 的framebuff下增加了不同的平台显卡驱动(比如高通显卡驱动MSM FB),然后基于此开发具体的LCD驱动,目前有三种接口方式,分别是lcdc、mipi、midi,可以查看driver\video\msm目录下的驱动文件,比如Toshiba LCD驱动采用midi,midi_toshiba.c文件就是其提供的具体驱动代码
fb_mem.c 函数和数据结构介绍这个文件包含了Linux Fb设备的所有接口,主要函数接口和数据结构如下:
A、Fb设备的文件操作接口 fb_fops
FrameBuffer中有3个重要的结构体,fb.h中定义,如下:
1) 、frame_var_screeninfo
该结构体定义了显卡的一些可变的特性,这些特性在程序运行期间可以由应用程序动态改变,比较典型的如xrex和yres表示在显示屏上显示的真实分辨率、显示的bit数等,该结构体user space可以访问。
2) 、frame_fix_screeninfo
该结构体定义了显卡的一些固定的特性,这些特性在硬件初始化时就被定义了以后不可以更改。其中最重要的成员就是smem_len和smem_start,前者指示显存的大小(目前程序中定义的显存大小为整屏数据RGB565大小的2倍),后者给出了显存的物理地址。该结构体user space可以访问。
Note:smem_start是显存的物理地址,应用程序是不可以直接访问的,必须通过fb_ops中的mmp函数映射成虚拟地址后,应用程序方可访问。
3) 、fb_info
FrameBuffer中最重要的结构体,它只能在内核空间内访问。内部定义了fb_ops结构体(包含一系列FrameBuffer的操作函数,Open/read/write、地址映射等).
C、其他
1)、一个重要的全局变量
struct fb_info *registered_fb[FB_MAX];
这变量记录了所有fb_info 结构的实例,fb_info 结构描述显卡的当前状态,所有设备对应的fb_info 结构都保存在这个数组中,当一个FrameBuffer设备驱动向系统注册自己时,其对应的fb_info 结构就会添加到这个结构中,同时num_registered_fb 为自动加1。
2)、注册framebuffer函数
- register_framebuffer(struct fb_info *fb_info);
- unregister_framebuffer(struct fb_info *fb_info);
这两个是提供给下层FrameBuffer设备驱动的接口,设备驱动通过这两函数向系统注册或注销自己。几乎底层设备驱动所要做的所有事情就是填充fb_info结构然后向系统注册或注销它.
Android display架构分析四-msm_fb.c 函数和数据结构介绍 (高通显卡的驱动文件)
msm_fb.c文件为高通显卡的驱动文件,比较重要的函数接口和数据结构如下:
A、高通msm fb设备的文件操作函数接口 msm_fb_ops
- static struct fb_ops msm_fb_ops = {
- .owner = THIS_MODULE,
- .fb_open = msm_fb_open,
- .fb_release = msm_fb_release,
- .fb_read = NULL,
- .fb_write = NULL,
- .fb_cursor = NULL,
- .fb_check_var = msm_fb_check_var, /* 参数检查 */
- .fb_set_par = msm_fb_set_par, /* 设置显示相关参数 */
- .fb_setcolreg = NULL, /* set color register */
- .fb_blank = NULL, /* blank display */
- .fb_pan_display = msm_fb_pan_display, /* 显示 */
- .fb_fillrect = msm_fb_fillrect, /* Draws a rectangle */
- .fb_copyarea = msm_fb_copyarea, /* Copy data from area to another */
- .fb_imageblit = msm_fb_imageblit, /* Draws a image to the display */
- .fb_cursor = NULL,
- .fb_rotate = NULL,
- .fb_sync = NULL, /* wait for blit idle, optional */
- .fb_ioctl = msm_fb_ioctl, /* perform fb specific ioctl (optional) */
- .fb_mmap = NULL,
- };
B、高通msm fb的driver接口 (驱动接口)msm_fb_driver
- static struct platform_driver msm_fb_driver = {
- .probe = msm_fb_probe,//驱动探测函数
- .remove = msm_fb_remove,
- #ifndef CONFIG_ANDROID_POWER
- .suspend = msm_fb_suspend,
- .suspend_late = NULL,
- .resume_early = NULL,
- .resume = msm_fb_resume,
- #endif
- .shutdown = NULL,
- .driver = {
- /* Driver name must match the device name added in platform.c. */
- .name = "msm_fb",
- },
- };
C、msm_fb_init()
向系统注册msm fb的driver,Msm_fb.c文件中的初始化时会调用:module_init(msm_fb_init);
D、msm_fb_add_device ---- probe函数中会被调用:
向系统中添加新的lcd设备,在mddi_toshiba.c中的probe函数中会被调用:
static int __devinit mddi_toshiba_lcd_probe(struct platform_device *pdev)
{ ..............
msm_fb_add_device(pdev);
return 0;
}
mddi_toshiba.c文件中 函数和数据结构介绍
该文件包含了所有和具体LCD(Toshiba)相关的信息和驱动,重点的数据结构和函数结构如下:
A、LCD设备相关信息-----------platform_device结构
- static struct platform_device this_device = {
- .name = "mddi_toshiba_vga",
- .id = TOSHIBA_VGA_PRIM,
- .dev = {
- .platform_data = &toshiba_panel_data,
- }
- };
其中toshiba_panel_data包含了硬件LCD的控制函数,如开关、初始化等等,定义如下;
staticstruct msm_fb_panel_data toshiba_panel_data = {
.on =mddi_toshiba_lcd_on,
.off =mddi_toshiba_lcd_off,
};
B、LCD driver接口(驱动接口)----------platform_driver结构
- static struct platform_driver this_driver = {
- .probe = mddi_toshiba_lcd_probe,
- .driver = {
- .name = "mddi_toshiba_vga",
- },
- };
其中mddi_toshiba_lcd_probe中会调用msm_fb_add_device接口把具体LCD添加到系统中去。
C、mddi_toshiba_lcd_init
注册LCD设备及driver到系统中去,同时也把LCD的固有信息(大小、格式、位率等)一并注册到系统中去。
staticint __init mddi_toshiba_lcd_init(void)
{
returnplatform_driver_register(&this_driver);
}
module_init(mddi_toshiba_lcd_init);
D、LCD相关控制函数
toshiba_common_initial_setup():初始化MDDI bridge
toshiba_prim_start():初始化LCD
Display Kernel数据流分析:
本部分来看一下应用层以下,显示数据的流程是怎样的。
先来分析一下传统的Linux平台下FB设备是如果调用的,如下图所示:
上层调用FB API(主要是fb_ioctl()),fb_ioctl()会调用具体显卡的驱动,这里是高通的显卡驱动,其实就是MDP DMA的驱动,通过MDP DMA把显示数据经MDDI接口送到外围LCD组件。
Note:这里的MDP DMA并不对数据进行任何处理(可以完成简单的格式转换,如RGB565->RGB666)。
接下来再分析一下Android平台下显示数据是如何处理的,如下图所示:
同样上层也是调用FB API,不过这里其实把FB bypass了,相当于直接调用的是高通MDP PPP的驱动,然后数据经PPP处理后再经MDDI接口送出到外围LCD组件。
Note:这里的MDP PPP可以完成很多显示数据处理功能,如YUV->RGB、Scale、Rotate、Blending等。
Display Kernel初始化过程分析
Kernel部分display的初始化包含下面几个步骤:
1)、在linux fb设备初始化时会向系统中注册msm_fb_driver。Name为msm_fb。
msm_fb_init-> msm_fb_register_driver-> platform_driver_register(&msm_fb_driver)
其中的probe函数会对msm fb进行初始化,分配显存等(见msm_fb_probe函数)。
static struct platform_driver msm_fb_driver = {
.probe = msm_fb_probe,
.remove = msm_fb_remove,
#ifndef CONFIG_HAS_EARLYSUSPEND
.suspend = msm_fb_suspend,
.resume = msm_fb_resume,
#endif
.shutdown = NULL,
.driver = {
/* Driver name must match the device name added in platform.c. */
.name = "msm_fb",
.pm = &msm_fb_dev_pm_ops,
},
};
2)、在LCD模块初始化时会先向系统中注册驱动(在mddi_toshiba_lcd_init函数中)()
platform_driver_register(&this_driver);名字为mddi_toshiba_vga;
this_driver的probe函数为mddi_toshiba_lcd_probe,其内部会调用msm_fb_add_device向系统中添加MSM fb设备。
3)、调用platform_device_register(&this_device)向系统中注册设备,名字为mddi_toshiba_vga,其中this_device_0包含了一些操作LCD的接口,如on/off。
Note:设备和driver的name需要一致才可以绑定;另外,如果某些设备不需要让platform的总线来管理,那么只需要注册驱动即可,而无须向系统中注册device,如msm_touch