Android display架構(gòu)分析(一)
http://hi.baidu.com/leowenj/blog/item/429c2dd6ac1480c851da4b95.html
高通7系列硬件架構(gòu)分析
如上圖,高通7系列 Display的硬件部分主要由下面幾個(gè)部分組成:
A、MDP
高通MSM7200A內(nèi)部模塊,主要負(fù)責(zé)顯示數(shù)據(jù)的轉(zhuǎn)換和部分圖像處理功能理,如YUV轉(zhuǎn)RGB,放大縮小、旋轉(zhuǎn)等。MDP內(nèi)部的MDPDMA負(fù)責(zé)數(shù)據(jù)從DDR到MDDI Host的傳輸(可以完成RGB之間的轉(zhuǎn)換,如RGB565轉(zhuǎn)成RGB666,這個(gè)轉(zhuǎn)換工能載目前的code中沒(méi)有使用)。
B、MDDI
一種采用差分信號(hào)的高速的串行數(shù)據(jù)傳輸總線(xiàn),只負(fù)責(zé)數(shù)據(jù)傳輸,無(wú)其它功能;其中的MDDI Hosat提供并行數(shù)據(jù)和串行數(shù)據(jù)之間的轉(zhuǎn)換和緩沖功能。由于外面是VGA的屏幕,數(shù)據(jù)量較大,為了減少對(duì)EBI2總線(xiàn)的影響,傳輸總線(xiàn)使用MDDI,而非之前的EBI2。
C、MDDI Bridge
由于現(xiàn)在采用的外接LCD并不支持MDDI接口,故需要外加MDDI轉(zhuǎn)換器,即MDDIbridge,來(lái)把MDDI數(shù)據(jù)轉(zhuǎn)換成RGB接口數(shù)據(jù)。這里采用的EPSON MDDIBridge還有LCDController功能,可以完成其它一些數(shù)據(jù)處理的功能,如數(shù)據(jù)格式轉(zhuǎn)換、支持TV-OUT、PIP等;并且還可以提供一定數(shù)量的GPIO。目前我們主要用它把HOST端MDDI傳遞過(guò)來(lái)的顯示數(shù)據(jù)和控制數(shù)據(jù)(初始化配置等)轉(zhuǎn)換成并行的數(shù)據(jù)傳遞給LCD。
D、LCD module
主要是LCD Driver IC 和TFT Panel,負(fù)責(zé)把MDDI Bridge傳來(lái)的顯存中的圖像示在自己的 Panel上。
Android display架構(gòu)分析(二)
http://hi.baidu.com/leowenj/blog/item/3fe59f740a6fee17b051b991.html
Android display SW架構(gòu)分析
下面簡(jiǎn)單介紹一下上圖中的各個(gè)Layer:
*藍(lán)色部分-用戶(hù)空間應(yīng)用程序
應(yīng)用程序?qū)樱渲邪ˋndroid應(yīng)用程序以及框架和系統(tǒng)運(yùn)行庫(kù),和底層相關(guān)的是系統(tǒng)運(yùn)行庫(kù),而其中和顯示相關(guān)的就是Android的Surface Manager, 它負(fù)責(zé)對(duì)顯示子系統(tǒng)的管理,并且為多個(gè)應(yīng)用程序提 供了2D和3D圖層的無(wú)縫融合。
*黑色部分-HAL層,在2.2.1部分會(huì)有介紹
*紅色部分-Linux kernel層
Linuxkernel,其中和顯示部分相關(guān)的就是Linux的FrameBuffer,它是Linux系統(tǒng)中的顯示部分驅(qū)動(dòng)程序接口。Linux工作在保護(hù)模式下,User空間的應(yīng)用程序無(wú)法直接調(diào)用顯卡的驅(qū)動(dòng)程序來(lái)直接畫(huà)屏,F(xiàn)rameBuffer機(jī)制模仿顯卡的功能,將顯卡硬件結(jié)構(gòu)抽象掉,可以通過(guò)Framebuffer的讀寫(xiě)直接對(duì)顯存進(jìn)行操作。用戶(hù)可以將Framebuffer看成是顯示內(nèi)存的一個(gè)映像,將其映射到進(jìn)程地址空間之后,就可以直接進(jìn)行讀寫(xiě)操作,而寫(xiě)操作可以立即反應(yīng)在屏幕上。這種操作是抽象的,統(tǒng)一的。用戶(hù)不必關(guān)心物理顯存的位置、換頁(yè)機(jī)制等等具體細(xì)節(jié)。這些都是由Framebuffer設(shè)備驅(qū)動(dòng)來(lái)完成的。
*綠色部分-HW驅(qū)動(dòng)層
該部分可以看作高通顯卡的驅(qū)動(dòng)程序,和高通顯示部分硬件相關(guān)以及外圍LCD相關(guān)的驅(qū)動(dòng)都被定義在這邊,比如上述的顯卡的一些特性都是在這邊被初始化的,同樣MDP和MDDI相關(guān)的驅(qū)動(dòng)也都定義在這里
這里的User Space就是與應(yīng)用程序相關(guān)的上層部分(參考上圖中的藍(lán)色部分),其中與Kernel空間交互的部分稱(chēng)之為HAL-HW Abstraction Layer。
HAL其實(shí)就是用戶(hù)空間的驅(qū)動(dòng)程序。如果想要將 Android 在某硬件平臺(tái)上執(zhí)行,基本上完成這些驅(qū)動(dòng)程序就行了。其內(nèi)定義了 Android 對(duì)各硬件裝置例如顯示芯片、聲音、數(shù)字相機(jī)、GPS、GSM 等等的需求。
HAL存在的幾個(gè)原因:
1、 并不是所有的硬件設(shè)備都有標(biāo)準(zhǔn)的linux kernel的接口。
2、 Kernel driver涉及到GPL的版權(quán)。某些設(shè)備制造商并不原因公開(kāi)硬件驅(qū)動(dòng),所以才去HAL方式繞過(guò)GPL。
3、 針對(duì)某些硬件,Android有一些特殊的需求。
在display部分,HAL的實(shí)現(xiàn)code在copybit.c中,應(yīng)用程序直接操作這些接口即可,具體的接口如下:
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;//設(shè)置參數(shù)ctx->device.get = get;ctx->device.blit = blit_copybit;//傳送顯示數(shù)據(jù)ctx->device.stretch = stretch_copybit;ctx->mAlpha = MDP_ALPHA_NOP;ctx->mFlags = 0;ctx->mFD = open("/dev/graphics/fb0", O_RDWR, 0);//打開(kāi)設(shè)備
這里的Kernel空間(與Display相關(guān))是Linux平臺(tái)下的FB設(shè)備(參考上圖中的紅色部分)。下面介紹一下FB設(shè)備。
Fb即FrameBuffer的簡(jiǎn)稱(chēng)。framebuffer是一種能夠提取圖形的硬件設(shè)備,是用戶(hù)進(jìn)入圖形界面很好的接口。有了framebuffer,用戶(hù)的應(yīng)用程序不需要對(duì)底層驅(qū)動(dòng)有深入了解就能夠做出很好的圖形。對(duì)于用戶(hù)而言,它和/dev 下面的其他設(shè)備沒(méi)有什么區(qū)別,用戶(hù)可以把
framebuffer看成一塊內(nèi)存,既可以向這塊內(nèi)存中寫(xiě)入數(shù)據(jù),也可以從這塊內(nèi)存中讀取數(shù)據(jù)。它允許上層應(yīng)用程序在圖形模式下直接對(duì)顯示緩沖區(qū)進(jìn)行讀寫(xiě)操作。這種操作是抽象的,統(tǒng)一的。用戶(hù)不必關(guān)心物理顯存的位置、換頁(yè)機(jī)制等等具體細(xì)節(jié)。這些都是由Framebuffer設(shè)備驅(qū)動(dòng)來(lái)完成的。
從用戶(hù)的角度看,幀緩沖設(shè)備和其他位于/dev下面的設(shè)備類(lèi)似,它是一個(gè)字符設(shè)備,通常主設(shè)備號(hào)是29,次設(shè)備號(hào)定義幀緩沖的個(gè)數(shù)。
在LINUX系統(tǒng)中,設(shè)備被當(dāng)作文件來(lái)處理,所有的文件包括設(shè)備文件,Linux都提供了統(tǒng)一的操作函數(shù)接口。上面的結(jié)構(gòu)體就是Linux為FB設(shè)備提供的操作函數(shù)接口。
1)、讀寫(xiě)(read/write)接口,即讀寫(xiě)屏幕緩沖區(qū)(應(yīng)用程序不一定會(huì)調(diào)用該接口)
2)、映射(map)操作(用戶(hù)空間不能直接訪(fǎng)問(wèn)顯存物理空間,需map成虛擬地址后才可以)
由于Linux工作在保護(hù)模式,每個(gè)應(yīng)用程序都有自己的虛擬地址空間,在應(yīng)用程序中是不能直接訪(fǎng)問(wèn)物理緩沖區(qū)地址的。為此,Linux在文件操作file_operations結(jié)構(gòu)中提供了mmap函數(shù),可將文件的內(nèi)容映射到用戶(hù)空間。對(duì)于幀緩沖設(shè)備,則可通過(guò)映射操作,可將屏幕緩沖區(qū)的物理地址映射到用戶(hù)空間的一段虛擬地址中,之后用戶(hù)就可以通過(guò)讀寫(xiě)這段虛擬地址訪(fǎng)問(wèn)屏幕緩沖區(qū),在屏幕上繪圖了。實(shí)際上,使用幀緩沖設(shè)備的應(yīng)用程序都是通過(guò)映射操作來(lái)顯示圖形的。由于映射操作都是由內(nèi)核來(lái)完成,下面我們將看到,幀緩沖驅(qū)動(dòng)留給開(kāi)發(fā)人員的工作并不多
3)、I/O控制:對(duì)于幀緩沖設(shè)備,對(duì)設(shè)備文件的ioctl操作可讀取/設(shè)置顯示設(shè)備及屏幕的參數(shù),如分辨率,顯示顏色數(shù),屏幕大小等等。ioctl的操作是由底層的驅(qū)動(dòng)程序來(lái)完成
Note:上述部分請(qǐng)參考文件fbmem.c。
Android display架構(gòu)分析(三)
http://hi.baidu.com/leowenj/blog/item/76411bf6237dc429bc31099f.html
如上圖所示,除了上層的圖形應(yīng)用程序外,和Kernel空間有關(guān)的包括Linux FB設(shè)備層以及和具體HW相關(guān)的驅(qū)動(dòng)層,對(duì)應(yīng)的源文件分別是fb_mem.c、msm_fb.c、mddi_toshiba.c。下面會(huì)一一介紹。
這個(gè)文件包含了Linux Fb設(shè)備的所有接口,主要函數(shù)接口和數(shù)據(jù)結(jié)構(gòu)如下:
A、Fb設(shè)備的文件操作接口
B、3個(gè)重要的數(shù)據(jù)結(jié)構(gòu)
FrameBuffer中有3個(gè)重要的結(jié)構(gòu)體,fb.h中定義,如下:
1) 、frame_var_screeninfo
該結(jié)構(gòu)體定義了顯卡的一些可變的特性,這些特性在程序運(yùn)行期間可以由應(yīng)用程序動(dòng)態(tài)改變,比較典型的如xrex和yres表示在顯示屏上顯示的真實(shí)分辨率、顯示的bit數(shù)等,該結(jié)構(gòu)體user space可以訪(fǎng)問(wèn)。
2) 、frame_fix_screeninfo
該結(jié)構(gòu)體定義了顯卡的一些固定的特性,這些特性在硬件初始化時(shí)就被定義了以后不可以更改。其中最重要的成員就是smem_len和smem_start,前者指示顯存的大?。壳俺绦蛑卸x的顯存大小為整屏數(shù)據(jù)RGB565大小的2倍),后者給出了顯存的物理地址。該結(jié)構(gòu)體user space可以訪(fǎng)問(wèn)。
Note:smem_start是顯存的物理地址,應(yīng)用程序是不可以直接訪(fǎng)問(wèn)的,必須通過(guò)fb_ops中的mmp函數(shù)映射成虛擬地址后,應(yīng)用程序方可訪(fǎng)問(wèn)。
3) 、fb_info
FrameBuffer中最重要的結(jié)構(gòu)體,它只能在內(nèi)核空間內(nèi)訪(fǎng)問(wèn)。內(nèi)部定義了fb_ops結(jié)構(gòu)體(包含一系列FrameBuffer的操作函數(shù),Open/read/write、地址映射等).
C、其他
1)、一個(gè)重要的全局變量
struct fb_info *registered_fb[FB_MAX];
這變量記錄了所有fb_info 結(jié)構(gòu)的實(shí)例,fb_info 結(jié)構(gòu)描述顯卡的當(dāng)前狀態(tài),所有設(shè)備對(duì)應(yīng)的fb_info 結(jié)構(gòu)都保存在這個(gè)數(shù)組中,當(dāng)一個(gè)FrameBuffer設(shè)備驅(qū)動(dòng)向系統(tǒng)注冊(cè)自己時(shí),其對(duì)應(yīng)的fb_info 結(jié)構(gòu)就會(huì)添加到這個(gè)結(jié)構(gòu)中,同時(shí)num_registered_fb 為自動(dòng)加1。
2)、注冊(cè)framebuffer函數(shù)
register_framebuffer(struct fb_info *fb_info);
unregister_framebuffer(struct fb_info *fb_info);
這兩個(gè)是提供給下層FrameBuffer設(shè)備驅(qū)動(dòng)的接口,設(shè)備驅(qū)動(dòng)通過(guò)這兩函數(shù)向系統(tǒng)注冊(cè)或注銷(xiāo)自己。幾乎底層設(shè)備驅(qū)動(dòng)所要做的所有事情就是填充fb_info結(jié)構(gòu)然后向系統(tǒng)注冊(cè)或注銷(xiāo)它
Android display架構(gòu)分析(四)
http://hi.baidu.com/leowenj/blog/item/37e1a8521e35522842a75b99.html
該文件為高通顯卡的驅(qū)動(dòng)文件,比較重要的函數(shù)接口和數(shù)據(jù)結(jié)構(gòu)如下:
A、高通msm fb設(shè)備的文件操作函數(shù)接口
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, /* 參數(shù)檢查 */
.fb_set_par = msm_fb_set_par, /* 設(shè)置顯示相關(guān)參數(shù) */
.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接口
static struct platform_driver msm_fb_driver = {
.probe = msm_fb_probe,//驅(qū)動(dòng)探測(cè)函數(shù)
.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()
向系統(tǒng)注冊(cè)msm fb的driver,初始化時(shí)會(huì)調(diào)用
D、msm_fb_add_device
向系統(tǒng)中添加新的lcd設(shè)備,在mddi_toshiba.c中會(huì)被調(diào)用
該文件包含了所有和具體LCD(Toshiba)相關(guān)的信息和驅(qū)動(dòng),重點(diǎn)的數(shù)據(jù)結(jié)構(gòu)和函數(shù)結(jié)構(gòu)如下:
A、LCD設(shè)備相關(guān)信息
static struct platform_device this_device_0 = {p>
.name = "mddi_toshiba_vga",
.id = TOSHIBA_VGA_PRIM,
.dev = {
.platform_data = &toshiba_panel_data0,
}
};
其中toshiba_panel_data0包含了硬件LCD的控制函數(shù),如開(kāi)關(guān)、初始化等等
B、LCD driver接口
static struct platform_driver this_driver = {
.probe = mddi_toshiba_lcd_probe,
.driver = {
.name = "mddi_toshiba_vga",
},
};
其中mddi_toshiba_lcd_probe中會(huì)調(diào)用msm_fb_add_device接口把具體LCD添加到系統(tǒng)中去。
C、mddi_toshiba_lcd_init
注冊(cè)LCD設(shè)備及driver到系統(tǒng)中去,同時(shí)也把LCD的固有信息(大小、格式、位率等)一并注冊(cè)到系統(tǒng)中去。
D、LCD相關(guān)控制函數(shù)
toshiba_common_initial_setup():初始化MDDI bridge
toshiba_prim_start():初始化LCD
本部分來(lái)看一下應(yīng)用層以下,顯示數(shù)據(jù)的流程是怎樣的。
先來(lái)分析一下傳統(tǒng)的Linux平臺(tái)下FB設(shè)備是如果調(diào)用的,如下圖所示:
上層調(diào)用FB API(主要是fb_ioctl()),fb_ioctl()會(huì)調(diào)用具體顯卡的驅(qū)動(dòng),這里是高通的顯卡驅(qū)動(dòng),其實(shí)就是MDP DMA的驅(qū)動(dòng),通過(guò)MDP DMA把顯示數(shù)據(jù)經(jīng)MDDI接口送到外圍LCD組件。
Note:這里的MDP DMA并不對(duì)數(shù)據(jù)進(jìn)行任何處理(可以完成簡(jiǎn)單的格式轉(zhuǎn)換,如RGB565->RGB666)。
接下來(lái)再分析一下Android平臺(tái)下顯示數(shù)據(jù)是如何處理的,如下圖所示:
同樣上層也是調(diào)用FB API,不過(guò)這里其實(shí)把FB bypass了,相當(dāng)于直接調(diào)用的是高通MDP PPP的驅(qū)動(dòng),然后數(shù)據(jù)經(jīng)PPP處理后再經(jīng)MDDI接口送出到外圍LCD組件。
Note:這里的MDP PPP可以完成很多顯示數(shù)據(jù)處理功能,如YUV->RGB、Scale、Rotate、Blending等。
Kernel部分display的初始化包含下面幾個(gè)步驟:
1)、在linux fb設(shè)備初始化時(shí)會(huì)向系統(tǒng)中注冊(cè)msm_fb_driver。Name為msm_fb。
msm_fb_init-> msm_fb_register_driver-> platform_driver_register(&msm_fb_driver)
其中的probe函數(shù)會(huì)對(duì)msm fb進(jìn)行初始化,分配顯存等(見(jiàn)msm_fb_probe函數(shù))。
2)、在LCD模塊初始化時(shí)會(huì)先向系統(tǒng)中注冊(cè)驅(qū)動(dòng)(在mddi_toshiba_lcd_init函數(shù)中)
platform_driver_register(&this_driver);名字為mddi_toshiba_vga;
this_driver的probe函數(shù)為mddi_toshiba_lcd_probe,其內(nèi)部會(huì)調(diào)用msm_fb_add_device向系統(tǒng)中添加MSM fb設(shè)備。
3)、調(diào)用platform_device_register(&this_device_0)向系統(tǒng)中注冊(cè)設(shè)備,名字為mddi_toshiba_vga,其中this_device_0包含了一些操作LCD的接口,如on/off。
Note:設(shè)備和driver的name需要一致才可以綁定;另外,如果某些設(shè)備不需要讓platform的總線(xiàn)來(lái)管理,那么只需要注冊(cè)驅(qū)動(dòng)即可,而無(wú)須向系統(tǒng)中注冊(cè)device,如msm_touch。
Android display架構(gòu)分析(五)
http://hi.baidu.com/leowenj/blog/item/7a12ecb77067737f8ad4b266.html
Display接口介紹
在Android平臺(tái)下,應(yīng)用程序面對(duì)的顯示部分的接口就是HAL,參考copybit.c,具體接口如下介紹:
open_copybit
初始化相關(guān)變量,并調(diào)用open("/dev/graphics/fb0", O_RDWR, 0);打開(kāi)fb設(shè)備。
set_parameter_copybit
設(shè)置各種操作參數(shù),如rotate、alpha、dither等。
stretch_copybit
Copy一塊數(shù)據(jù)(Rectangle)到顯存,然后并命令msm_fb進(jìn)行顯示。
close_copybit
調(diào)用close(ctx->mFD);關(guān)閉fb設(shè)備。
Note:另外,應(yīng)用程序在使用上面接口之前,需要調(diào)用mapFrameBuffer接口(EGLDisplaySurface.cpp),其功能如下:
1、 初始化顯示相關(guān)參數(shù),并設(shè)置到底層。
2、 映射出顯存的虛擬地址。
Kernel部分顯示的接口全部都在fbmem.c中,這里詳細(xì)介紹一下:
fb_open
打開(kāi)Linux下fb設(shè)備。
fb_read/fb_write
讀寫(xiě)顯存中的數(shù)據(jù)
fb_ioctl
對(duì)顯示設(shè)備的命令操作。如get或set一些顯示參數(shù)、通知底層進(jìn)行刷屏等。
在典型應(yīng)用中,畫(huà)屏的一般步驟如下:
1. 打開(kāi)/dev/fb設(shè)備文件。
2. 用ioctrl操作取得當(dāng)前顯示屏幕的參數(shù),如屏幕分辨率,每個(gè)像素點(diǎn)的比特?cái)?shù)。根據(jù)屏幕參數(shù)可計(jì)算屏幕緩沖區(qū)的大小。
3. 將屏幕緩沖區(qū)映射到用戶(hù)空間。
4. 映射后就可以直接讀寫(xiě)屏幕緩沖區(qū),進(jìn)行繪圖和圖片顯示了。
典型程序段如下:
#include
int main()
{
int fbfd = 0;
struct fb_var_screeninfo vinfo;
struct fb_fix_screeninfo finfo;
long int screensize = 0;
/*打開(kāi)設(shè)備文件*/
fbfd = open("/dev/fb0", O_RDWR);
/*取得屏幕相關(guān)參數(shù)*/
ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo); ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo);
/*計(jì)算屏幕緩沖區(qū)大小*/
screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;
/*映射屏幕緩沖區(qū)到用戶(hù)地址空間*/
fbp=(char*)mmap(0,screensize,PROT_READ|PROT_WRITE,MAP_SHARED, fbfd, 0);
/*下面可通過(guò)fbp指針讀寫(xiě)緩沖區(qū)*/
...
}
在不同應(yīng)用程序中,上層的調(diào)用會(huì)有所不同,比如Andriod下會(huì)選擇應(yīng)用程序跳過(guò)Linux fb操作層,直接操作顯卡驅(qū)動(dòng)層,稱(chēng)之為BLT accelerator。
下面看一下Android平臺(tái)下畫(huà)屏的操作流程。
1、 通過(guò)mapFrameBuffer直接把用戶(hù)空間的數(shù)據(jù)映射到顯存中。
2、 調(diào)用HAL中的stretch函數(shù)直接命令MSM設(shè)備提取顯存數(shù)據(jù)然后送入MDP PPP進(jìn)行處理并經(jīng)MDDI接口送到外圍LCD組件。
具體的函數(shù)調(diào)用流程如下:
copybit_open();//打開(kāi)BlitEngine,同時(shí)也打開(kāi)fb設(shè)備
mapFrameBuffer();//設(shè)置顯示參數(shù),同時(shí)得到顯存虛擬地址
copybit->stretch(copybit, &dst, &src, &sdrect, &sdrect, &it);//通知底層去刷屏
接下的流程是:
stretch_copybit-> msm_copybit->fb_ioctl()->msm_fb_ioctl(MSMFB_BLIT)-> msmfb_blit->mdp_blit-> mdp_ppp_blit->mdp_start_ppp->MDP&MDDI HWoperation
Android display架構(gòu)分析(六)
http://hi.baidu.com/leowenj/blog/item/78c068dc443c961f48540361.html
Note:
本部分介紹的完全是用戶(hù)空間顯示部分的架構(gòu),與kernel并沒(méi)有直接的聯(lián)系,主要是JNI以下到HAL以上的部分。
Surface manager是用戶(hù)空間中framework下libraries中負(fù)責(zé)顯示相關(guān)的一個(gè)模塊。如下:
當(dāng)系統(tǒng)同時(shí)執(zhí)行多個(gè)應(yīng)用程序時(shí),Surface Manager會(huì)負(fù)責(zé)管理顯示與存取操作間的互動(dòng),另外也負(fù)責(zé)將2D繪圖與3D繪圖進(jìn)行顯示上的合成。
surface manager 可以準(zhǔn)備一塊 surface(可以看作一個(gè)layer),把 surface 的 fd (一塊內(nèi)存) 傳給一個(gè) app,讓 app 可以在上面作畫(huà)。典型應(yīng)用如下:
Android中的圖形系統(tǒng)采用Client/Server架構(gòu),如下:
Client端:應(yīng)用程序相關(guān)部分。代碼分為兩部分,一部分是由Java提供的供應(yīng)用使用的api,另一部分則是由c++寫(xiě)成的底層實(shí)現(xiàn)。
Server端:即SurfaceFlinger,負(fù)責(zé)合成并送入buffer顯示。其主要由c++代碼編寫(xiě)而成。
Client和Server之前通過(guò)Binder的IPC方式進(jìn)行通信,總體結(jié)構(gòu)圖如下:
如上圖所示,Surface的client部分其實(shí)是提供給各應(yīng)用程序進(jìn)行畫(huà)圖操作的一個(gè)橋梁,該橋梁通過(guò)binder通向server端的Surfaceflinger,Surfaceflinger負(fù)責(zé)合成各個(gè)surface,然后把buffer傳送到framebuffer端進(jìn)行底層顯示。其中每個(gè)surface對(duì)應(yīng)2個(gè)buffer,一個(gè)front buffer, 一個(gè)back buffer,更新時(shí),數(shù)據(jù)更新在backbuffer上,需要顯示時(shí),則將back buffer和front buffer互換。
下一部分我們重點(diǎn)研究一下Surfaceflinger。
Android display架構(gòu)分析(七-1)
http://hi.baidu.com/leowenj/blog/item/7abbe33a309367ff3b87ce6f.html
1、 Thread本身處理部分,包括初始化以及thread loop。
2、 Binder部分,負(fù)責(zé)接收上層應(yīng)用的各個(gè)設(shè)置和命令,并反饋狀態(tài)標(biāo)志給上層。
3、 與底層的交互,負(fù)責(zé)調(diào)用底層接口(HAL)。
結(jié)構(gòu)圖如下:
注釋?zhuān)?/p>
a、 Binder接收到應(yīng)用程序的命令(如創(chuàng)建surface、設(shè)置參數(shù)等),傳遞給flinger。
b、 Flinger完成對(duì)應(yīng)命令后將相關(guān)結(jié)果狀態(tài)反饋給上層。
c、 在處理上層命令過(guò)程中,根據(jù)需要設(shè)置event(主要和顯示有關(guān)),通知Thread Loop進(jìn)行處理。
d、 Flinger根據(jù)上層命令通知底層進(jìn)行處理(主要是設(shè)置一些參數(shù),Layer、position等)
e、 Thread Loop中進(jìn)行surface的合成并通知底層進(jìn)行顯示(Post buffer)。
f、 DisplayHardware層根據(jù)flinger命令調(diào)用HAL進(jìn)行HW的操作。
下面來(lái)具體分析一些SurfaceFlinger中重要的處理函數(shù)以及surface、Layer的屬性
1)、readToRun
SurfaceFlinger thread的初始化函數(shù),主要任務(wù)是分配內(nèi)存和設(shè)置底層接口(EGL&HAL)。
status_t SurfaceFlinger::readyToRun()
{
…
…
mServerHeap = new MemoryDealer(4096, MemoryDealer::READ_ONLY);//為IPC分配共享內(nèi)存
…
mSurfaceHeapManager = new SurfaceHeapManager(this, 8 << 20);//為flinger分配heap,大小為8M,存放具體的顯示數(shù)據(jù)
{
// initialize the main display
GraphicPlane& plane(graphicPlane(dpy));
DisplayHardware* const hw = new DisplayHardware(this, dpy);
plane.setDisplayHardware(hw);//保存顯示接口
}
//獲取顯示相關(guān)參數(shù)
const GraphicPlane& plane(graphicPlane(dpy));
const DisplayHardware& hw = plane.displayHardware();
const uint32_t w = hw.getWidth();
const uint32_t h = hw.getHeight();
const uint32_t f = hw.getFormat();
…
…
// Initialize OpenGL|ES
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
…
…
}
2)、ThreadLoop
Surfaceflinger的loop函數(shù),主要是等待其他接口發(fā)送的event,進(jìn)行顯示數(shù)據(jù)的合成以及顯示。
bool SurfaceFlinger::threadLoop()
{
waitForEvent();//等待其他接口的signal event
…
…
// post surfaces (if needed)
handlePageFlip();//處理翻頁(yè)機(jī)制
const DisplayHardware& hw(graphicPlane(0).displayHardware());
if (LIKELY(hw.canDraw()))
{
// repaint the framebuffer (if needed)
handleRepaint();//合并所有l(wèi)ayer并填充到buffer中去
…
…
postFramebuffer();//互換front buffer和back buffer,調(diào)用EGL接口進(jìn)行顯示
}
…
…
}
3)、createSurface
提供給應(yīng)用程序的主要接口,該接口可以創(chuàng)建一個(gè)surface,底層會(huì)根據(jù)參數(shù)創(chuàng)建layer以及分配內(nèi)存,surface相關(guān)參數(shù)會(huì)反饋給上層
sp SurfaceFlinger::createSurface(ClientID clientId, int pid,
ISurfaceFlingerClient::surface_data_t* params,
DisplayID d, uint32_t w, uint32_t h, PixelFormat format,
uint32_t flags)
{
…
…
int32_t id = c->generateId(pid);
if (uint32_t(id) >= NUM_LAYERS_MAX) //NUM_LAYERS_MAX=31
{
LOGE("createSurface() failed, generateId = %d", id);
return
}
…
layer = createNormalSurfaceLocked(c, d, id, w, h, format, flags);//創(chuàng)建layer,根據(jù)參數(shù)(寬高格式)分配內(nèi)存(共2個(gè)buffer:front/back buffer)
if (layer)
{
setTransactionFlags(eTransactionNeeded);
surfaceHandle = layer->getSurface();//創(chuàng)建surface
if (surfaceHandle != 0)
surfaceHandle->getSurfaceData(params);//創(chuàng)建的surface參數(shù)反饋給應(yīng)用層
}
}
待續(xù)。。。
Android display架構(gòu)分析(七-2)
http://hi.baidu.com/leowenj/blog/item/ba4c5d6378a5da48eaf8f86a.html
4)、setClientState
處理上層的各個(gè)命令,并根據(jù)flag設(shè)置event通知Threadloop進(jìn)行處理
status_t SurfaceFlinger::setClientState(
ClientID cid,
int32_t count,
const layer_state_t* states)
{
Mutex::Autolock _l(mStateLock);
uint32_t flags = 0;
cid <<= 16;
for (int i=0 ; i
{
const layer_state_t& s = states[i];
LayerBaseClient* layer = getLayerUser_l(s.surface | cid);
if (layer)
{
const uint32_t what = s.what;
// 檢測(cè)應(yīng)用層是否設(shè)置各個(gè)標(biāo)志,如果有則通知底層完成對(duì)應(yīng)操作,并通知ThreadLoop做對(duì)應(yīng)的處理
if (what & eDestroyed) //刪除該層Layer
{
if (removeLayer_l(layer) == NO_ERROR)
{
flags |= eTransactionNeeded;
continue;
}
}
if (what & ePositionChanged) //顯示位置變化
{
if (layer->setPosition(s.x, s.y))
flags |= eTraversalNeeded;
}
if (what & eLayerChanged) //Layer改變
{
if (layer->setLayer(s.z))
{
mCurrentState.layersSortedByZ.reorder(
layer, &Layer::compareCurrentStateZ);
flags |= eTransactionNeeded|eTraversalNeeded;
}
}
if (what & eSizeChanged)
{
if (layer->setSize(s.w, s.h))//設(shè)置寬高變化
flags |= eTraversalNeeded;
}
if (what & eAlphaChanged) {//設(shè)置Alpha效果
if (layer->setAlpha(uint8_t(255.0f*s.alpha+0.5f)))
flags |= eTraversalNeeded;
}
if (what & eMatrixChanged) {//矩陣參數(shù)變化
if (layer->setMatrix(s.matrix))
flags |= eTraversalNeeded;
}
if (what & eTransparentRegionChanged) {//顯示區(qū)域變化
if (layer->setTransparentRegionHint(s.transparentRegion))
flags |= eTraversalNeeded;
}
if (what & eVisibilityChanged) {//是否顯示
if (layer->setFlags(s.flags, s.mask))
flags |= eTraversalNeeded;
}
}
}
if (flags)
{
setTransactionFlags(flags);//通過(guò)signal通知ThreadLoop
}
return NO_ERROR;
}
5)、composeSurfaces
該接口在Threadloop中被調(diào)用,負(fù)責(zé)將所有存在的surface進(jìn)行合并,OpenGl模塊負(fù)責(zé)這個(gè)部分。
6)、postFramebuffer
該接口在Threadloop中被調(diào)用,負(fù)責(zé)將合成好的數(shù)據(jù)(存于back buffer中)推入在front buffer中,然后調(diào)用HAL接口命令底層顯示。
7)、從3中可知,上層每創(chuàng)建一個(gè)surface的時(shí)候,底層都會(huì)同時(shí)創(chuàng)建一個(gè)layer,下面看一下surface及l(fā)ayer的相關(guān)屬性。
Note:code中相關(guān)結(jié)構(gòu)體太大,就不全部羅列出來(lái)了
A、Surface相關(guān)屬性(詳細(xì)參考文件surface.h)
a1:SurfaceID:根據(jù)此ID把相關(guān)surface和layer對(duì)應(yīng)起來(lái)
a2:SurfaceInfo
包括寬高格式等信息
a3:2個(gè)buffer指針、buffer索引等信息
B、Layer相關(guān)屬性(詳細(xì)參考文件layer.h/layerbase.h/layerbitmap.h)
包括Layer的ID、寬高、位置、layer、alpha指、前后buffer地址及索引、layer的狀態(tài)信息(如eFlipRequested、eBusy、eLocked等)
Android display架構(gòu)分析(八)
http://hi.baidu.com/leowenj/blog/item/03aae36137acb8d1e6113a75.html
參考上面linux下fb設(shè)備的軟件架構(gòu),可以知道,要加入一個(gè)新的MDDI接口的LCM,Driver的工作就是要提供自己的mddi_xxxx.c(在這次porting的過(guò)程中,為了節(jié)省時(shí)間,我們直接修改了mddi_toshiba.c),并且完成和這個(gè)lcd相關(guān)的HWr的初始化。主要的工作包括:
A、初始化和LCD / LCD背光相關(guān)的IO以及電源;
B、編寫(xiě)初始化函數(shù) 。主要是初始化LCD控制器,這個(gè)一般LCD廠(chǎng)商會(huì)提供;然后分配顯存,這個(gè)高通release過(guò)來(lái)的code已經(jīng)包含這個(gè)動(dòng)作了,最后是初始化一個(gè)fb_info的結(jié)構(gòu)體,在這里主要是把LCD的一些信息登記進(jìn)來(lái)。
C、把LCD的設(shè)備以及驅(qū)動(dòng)注冊(cè)到系統(tǒng)中去。(這里因?yàn)槭翘鎿Q現(xiàn)有的驅(qū)動(dòng),所以相關(guān)修改的部分不多。)
上述B、C部分代碼請(qǐng)參考kernel\drivers\video\msm\mddi_toshiba.c。
更改一些GPIO的配置以及一些電源的電平配置;然后通過(guò)實(shí)際測(cè)量,確保一下信號(hào)正常:
A、供給LCD以及MDDI Bridge的電源;
B、MDDI Bridge以及LCD reset信號(hào);
C、控制背光IC的GPIO工作正常(背光不打開(kāi),無(wú)法調(diào)試LCD)。
LCD init的code以及外圍MDDI Bridge的初始化code,都可以之前Boston WindowsMobile系統(tǒng)的codebase中獲得;把這部分code移植到mddi_Toshiba.c中,并更改相應(yīng)的圖像格式、分辨率等配置,編譯通過(guò)。LCD初始化部分就算基本完成。
由于硬件在之前Boston load是可以工作的,可以認(rèn)為硬件連接等沒(méi)有問(wèn)題,所以只需關(guān)注軟件部分就行。
Display部分軟件調(diào)試過(guò)程如下:
A、 開(kāi)機(jī)后,量一下GPIO是否為code中配置預(yù)期的狀態(tài)(可確保code中的
GPIO接口工作正常);
B、 量一下各個(gè)電源是否都處于Code中定義的電平值。這些都OK后,背光
是會(huì)亮的(背光的控制比較簡(jiǎn)單,一個(gè)GPIO即可);
C、 這個(gè)時(shí)候如果LCD以及MDDI Bridge有被正常初始化的話(huà),屏幕上是會(huì)
看出來(lái)的。反之,如果屏幕沒(méi)有顯示,需要用JTAG跟一下mddi_Toshiba.c中的初始化函數(shù)是否在開(kāi)機(jī)的時(shí)候有被調(diào)用過(guò)。
目前版本中,是根據(jù)外圍MDDIBridge中讀到的的廠(chǎng)商號(hào)來(lái)決定加載哪個(gè)驅(qū)動(dòng)模塊的。在本次調(diào)試中,bootloader中可以正確讀到廠(chǎng)商號(hào),所以bootloader中對(duì)于LCD的初始化是有做的,所以屏幕看到的狀態(tài)就是LCD初始化后的樣子(花屏)。但Kernel起來(lái)后,并沒(méi)有其他顯示,用JTAG跟了后發(fā)現(xiàn),Kernel中MODULEINIT中讀不到正確的廠(chǎng)商號(hào),所以說(shuō)后面的driver沒(méi)有被加載。接著發(fā)現(xiàn)如果在bootloader中如果不做MDDIBridge的初始化,的話(huà)后面的MODULE INIT就可正常運(yùn)行,該問(wèn)題目前還沒(méi)有澄清(現(xiàn)在暫時(shí)先把bootloader中的initdisable掉)。
初始化正常后,屏幕會(huì)顯示UI的相關(guān)畫(huà)面,但明顯顏色、位置都不對(duì)。
這個(gè)可能是數(shù)據(jù)類(lèi)型配置不對(duì)導(dǎo)致的,即MDP輸出的類(lèi)型、MDDI配置的類(lèi)型以、LCD接收的類(lèi)型不匹配導(dǎo)致,也有可能是RGB的順序不對(duì)導(dǎo)致(可配置成BGR)。經(jīng)過(guò)調(diào)試后,把MDP端輸出的格式配置成RGB565,同時(shí)外圍MDDIBridge以及LCD的input格式也配置成RGB565,這時(shí)顯示色彩正常了。
如果位置或者方向不對(duì),比如說(shuō)上下或是左右顛倒,可以更改LCD的配置中的掃描方向即可。
后續(xù)發(fā)現(xiàn)一個(gè)問(wèn)題,播放video的時(shí)候顏色都是黑白的。
這個(gè)問(wèn)題很容易讓人誤解,按照正常的理解,videodecode出來(lái)的數(shù)據(jù)為YCbCr,Y為亮度信號(hào),CbCr為色差信號(hào),如果只有Y信號(hào)的話(huà)顏色應(yīng)該就是黑白的。所以有2個(gè)懷疑點(diǎn),一個(gè)是decode出來(lái)的數(shù)據(jù)有誤,另一個(gè)是MDDIBridge誤把輸入的YcbCr信號(hào)當(dāng)作RGB信號(hào)進(jìn)行出來(lái),這個(gè)也是有可能的。但很快第二個(gè)懷疑點(diǎn)被排除了(因?yàn)閱胃腗DDIinput格式后還是不能解決問(wèn)題)。
后來(lái)又詳細(xì)的看了顯示部分的代碼,并用JTAG追蹤video播放的時(shí)候用的顯示接口,發(fā)現(xiàn)目前所有的顯示接口輸出的格式都是RGB格式,也就是說(shuō)在通過(guò)MDP之前YcbCr已經(jīng)被轉(zhuǎn)化過(guò);而MDP里的轉(zhuǎn)換功能并沒(méi)有使用,MDP只是被當(dāng)作一個(gè)DMA完成數(shù)據(jù)的直接傳輸,文檔中叫做Bypasse。
YcbCr到RGB的轉(zhuǎn)換是由Android的lib來(lái)完成。發(fā)了個(gè)SR給高通,高通的回復(fù)也確認(rèn)了,在6.3.50中,Android上層缺少這個(gè)lib(copybit.default.so),6.3.60之后的版本經(jīng)解決了這個(gè)問(wèn)題。
高通Android平臺(tái)下關(guān)于display部分的幾個(gè)關(guān)鍵問(wèn)題
http://hi.baidu.com/leowenj/blog/item/06f8c0000763b37a3812bb03.html
顯示部分的幾個(gè)問(wèn)題這幾天通過(guò)實(shí)際測(cè)試澄清了一下,主要是下圖中各個(gè)模塊的使用狀況以及HAL層幾個(gè)模塊的調(diào)用流程。以問(wèn)題的方式描述如下:
1、 Ap是怎么進(jìn)行顯示的?
Surfaceflinger負(fù)責(zé)所有上層的顯示處理,對(duì)于AP(2D或是3D的應(yīng)用程序)而言,只要到surfaceflinger中創(chuàng)建surface,設(shè)置好參數(shù),接下來(lái)都是統(tǒng)一交給surfaceflinger進(jìn)行處理
2、 Surface是怎么管理多個(gè)surface的?
不管有多少個(gè)surface,最終送到顯示部分的只能是屏幕大小數(shù)據(jù),surfaceflinger中利用MDP或是GPU進(jìn)行多個(gè)surface的合成處理,普通的合成MDP就可完成,但如果是復(fù)雜的比如3D的應(yīng)用等就必須使用GPU,最終合成的好數(shù)據(jù)會(huì)被送到framebuffer中。
3、 Framebuffer是什么?
Framebuffer是Linux中為顯示數(shù)據(jù)分配的一塊顯存(fb設(shè)備中),通常大小是一整個(gè)屏幕數(shù)據(jù)的兩倍,對(duì)于上層AP而言,只需要將要顯示的數(shù)據(jù)丟到framebuffer中就OK了,但此時(shí)顯示數(shù)據(jù)并未真正的被送到LCD上,而是暫存在framebuffer中而已。
4、 上層是通過(guò)什么方式將顯示內(nèi)容送到framebuffer的?
有2個(gè)方式(二選一,不會(huì)同時(shí)在運(yùn)行):
A、 普通的顯示,使用copybit(MDP)(未使用GPU)
Surfaceflinger通過(guò)copybit將要顯示的數(shù)據(jù)送到framebuffer。
Note:copybit可以看做是MDP PPP的接口,它提供了MDP的功能,如多個(gè)layer合成,scale、rotate等。
其接口在:android\hardware\msm7k\libcopybit\copybit.cpp
B、 使用GPU(即使用圖中的Graphics driver)
當(dāng)進(jìn)行復(fù)雜的顯示處理時(shí),比如3D的應(yīng)用,GPU把處理好的數(shù)據(jù)直接丟到framebuffer中,和MDP沒(méi)有任何關(guān)系
5、 Framebuffer中的數(shù)據(jù)是如何被送到LCD顯示的?
圖中的Gralloc完成的。
Gralloc有2個(gè)功能:
一個(gè)是和copybit相同的,里面有MDP PPP的接口(目前沒(méi)有使用)
另一個(gè)則是刷屏(整屏刷)的接口,即將framebuffer中的數(shù)據(jù)送到lcd上,調(diào)用的是MDP DMA的接口
這部分的code在android\hardware\msm7k\libgralloc-qsd8k目錄下,之前沒(méi)有留意,以為沒(méi)有使用。現(xiàn)在可以看出開(kāi)機(jī)初始化后就創(chuàng)建了disp_loop thread,里面的操作就是調(diào)用系統(tǒng)接口
ioctl(m->framebuffer->fd, FBIOPUT_VSCREENINFO, &m->info)
將數(shù)據(jù)送到lcd
Note:送數(shù)據(jù)的時(shí)候是2個(gè)buffer切換的
另外,上層surfaceflinger也是通過(guò)Gralloc中的接口獲知屏幕的大小,調(diào)用接口為
ioctl(fd, FBIOGET_VSCREENINFO, &info),info中的屏幕寬高對(duì)應(yīng)的就是底層driver設(shè)置的寬高值
6、 OpenGL是什么?
它是一個(gè)圖像處理引擎,當(dāng)需要一些復(fù)雜的顯示(2D/3D)操作時(shí)會(huì)用到它。它分為SW方案和HW方案,軟件方案就是圖中的libagl.so,對(duì)應(yīng)到目前項(xiàng)目中是libGLES_android.so,它可以完成簡(jiǎn)單的2D(文字,icon等)處理,通過(guò)trace看目前大部分顯示操作都是它來(lái)完成的。
Note:它是軟件方案,處理好的數(shù)據(jù)是通過(guò)copybit送到framebuffer的,而不是GPU。
其接口部分參考:android\frameworks\base\opengl\libagl
HW方案就是圖中的Graphics driver,它通過(guò)使用GPU硬件來(lái)完成圖像處理,處理后的數(shù)據(jù)直接送到framebuffer中。其接口部分參考:android\frameworks\base\opengl\libs(有幾個(gè)版本)
7、 OpenGL在項(xiàng)目中是如何配置的?
在android\vendor\qcom\msm7627_ffa目錄下有一個(gè)egl.cfg文件,里面指定了當(dāng)前版本中的OpenGL信息,目前如下:
0 0 android
0 1 adreno200
第一行代表該codebase支持SW 方案的OpenGL,是android default的
第二行代表該codebase也支持HW方案的OpenGL,是高通的adreno引擎
如果該cfg文件為空,則只支持default的SW方案。
如果2個(gè)方案都在,上層將根據(jù)實(shí)際應(yīng)用自行選擇使用其一。
該部分請(qǐng)參考:android\frameworks\base\opengl\libs\EGL\loader.cpp
聯(lián)系客服