Android display架構分析

Android display架構分析(一)

http://hi.baidu.com/leowenj/blog/item/429c2dd6ac1480c851da4b95.html

高通7系列硬件架構分析

1

如上圖,高通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上。

Android display架構分析(二)

http://hi.baidu.com/leowenj/blog/item/3fe59f740a6fee17b051b991.html

Android display SW架構分析

2

3

下面簡單介紹一下上圖中的各個Layer:

*藍色部分-用戶空間應用程序

應用程序層,其中包括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中,應用程序直接操作這些接口即可,具體的接口如下:

  1. struct copybit_context_t *ctx = malloc(sizeof(struct copybit_context_t));  
  2. memset(ctx, 0, sizeof(*ctx));  
  3. ctx->device.common.tag = HARDWARE_DEVICE_TAG;  
  4. ctx->device.common.version = 0;  
  5. ctx->device.common.module = module;  
  6. ctx->device.common.close = close_copybit;  
  7. ctx->device.set_parameter = set_parameter_copybit;//設置參數  
  8. ctx->device.get = get;  
  9. ctx->device.blit = blit_copybit;//傳送顯示數據  
  10. ctx->device.stretch = stretch_copybit;  
  11. ctx->mAlpha = MDP_ALPHA_NOP;  
  12. ctx->mFlags = 0;  
  13. ctx->mFD = open("/dev/graphics/fb0", O_RDWR, 0);//打開設備  


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。

Android display架構分析(三)

http://hi.baidu.com/leowenj/blog/item/76411bf6237dc429bc31099f.html

Kernel Space Display架構介紹
4

如上圖所示,除了上層的圖形應用程序外,和Kernel空間有關的包括Linux FB設備層以及和具體HW相關的驅動層,對應的源文件分別是fb_mem.c、msm_fb.c、mddi_toshiba.c。下面會一一介紹。

函數和數據結構介紹

這個文件包含了Linux Fb設備的所有接口,主要函數接口和數據結構如下:

A、Fb設備的文件操作接口

5

B、3個重要的數據結構

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架構分析(四)

http://hi.baidu.com/leowenj/blog/item/37e1a8521e35522842a75b99.html

函數和數據結構介紹

該文件爲高通顯卡的驅動文件,比較重要的函數接口和數據結構如下:

A、高通msm fb設備的文件操作函數接口

  1. static struct fb_ops msm_fb_ops = {  
  2. .owner = THIS_MODULE,  
  3. .fb_open = msm_fb_open,  
  4. .fb_release = msm_fb_release,  
  5. .fb_read = NULL,  
  6. .fb_write = NULL,  
  7. .fb_cursor = NULL,  
  8. .fb_check_var = msm_fb_check_var,     /* 參數檢查 */  
  9. .fb_set_par = msm_fb_set_par,       /* 設置顯示相關參數 */  
  10. .fb_setcolreg = NULL, /* set color register */  
  11. .fb_blank = NULL,       /* blank display */  
  12. .fb_pan_display = msm_fb_pan_display,       /* 顯示 */  
  13. .fb_fillrect = msm_fb_fillrect,     /* Draws a rectangle */  
  14. .fb_copyarea = msm_fb_copyarea, /* Copy data from area to another */  
  15. .fb_imageblit = msm_fb_imageblit,   /* Draws a image to the display */  
  16. .fb_cursor = NULL,  
  17. .fb_rotate = NULL,  
  18. .fb_sync = NULL, /* wait for blit idle, optional */  
  19. .fb_ioctl = msm_fb_ioctl,    /* perform fb specific ioctl (optional) */  
  20. .fb_mmap = NULL,  
  21. };  


B、高通msm fb的driver接口

  1. static struct platform_driver msm_fb_driver = {  
  2. .probe = msm_fb_probe,//驅動探測函數  
  3. .remove = msm_fb_remove,  
  4. #ifndef CONFIG_ANDROID_POWER  
  5. .suspend = msm_fb_suspend,  
  6. .suspend_late = NULL,  
  7. .resume_early = NULL,  
  8. .resume = msm_fb_resume,  
  9. #endif  
  10. .shutdown = NULL,  
  11. .driver = {  
  12. /* Driver name must match the device name added in platform.c. */  
  13. .name = "msm_fb",  
  14. },  
  15. };  


C、msm_fb_init()

向系統註冊msm fb的driver,初始化時會調用

D、msm_fb_add_device

向系統中添加新的lcd設備,在mddi_toshiba.c中會被調用

函數和數據結構介紹

該文件包含了所有和具體LCD(Toshiba)相關的信息和驅動,重點的數據結構和函數結構如下:

A、LCD設備相關信息

  1. static struct platform_device this_device_0 = {p>  
  2. .name   = "mddi_toshiba_vga",  
  3. .id   = TOSHIBA_VGA_PRIM,  
  4. .dev       = {  
  5. .platform_data = &toshiba_panel_data0,  
  6. }  
  7. };  


其中toshiba_panel_data0包含了硬件LCD的控制函數,如開關、初始化等等

B、LCD driver接口

  1. static struct platform_driver this_driver = {  
  2. .probe = mddi_toshiba_lcd_probe,  
  3. .driver = {  
  4. .name   = "mddi_toshiba_vga",  
  5. },  
  6. };  


其中mddi_toshiba_lcd_probe中會調用msm_fb_add_device接口把具體LCD添加到系統中去。

C、mddi_toshiba_lcd_init

註冊LCD設備及driver到系統中去,同時也把LCD的固有信息(大小、格式、位率等)一併註冊到系統中去。

D、LCD相關控制函數

toshiba_common_initial_setup():初始化MDDI bridge

toshiba_prim_start():初始化LCD

數據流分析

本部分來看一下應用層以下,顯示數據的流程是怎樣的。

先來分析一下傳統的Linux平臺下FB設備是如果調用的,如下圖所示:

上層調用FB API(主要是fb_ioctl()),fb_ioctl()會調用具體顯卡的驅動,這裏是高通的顯卡驅動,其實就是MDP DMA的驅動,通過MDP DMA把顯示數據經MDDI接口送到外圍LCD組件。

Note:這裏的MDP DMA並不對數據進行任何處理(可以完成簡單的格式轉換,如RGB565->RGB666)。

6

接下來再分析一下Android平臺下顯示數據是如何處理的,如下圖所示:

7

同樣上層也是調用FB API,不過這裏其實把FB bypass了,相當於直接調用的是高通MDP PPP的驅動,然後數據經PPP處理後再經MDDI接口送出到外圍LCD組件。

Note:這裏的MDP PPP可以完成很多顯示數據處理功能,如YUV->RGB、Scale、Rotate、Blending等。

初始化過程分析

   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函數)。

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_0)向系統中註冊設備,名字爲mddi_toshiba_vga,其中this_device_0包含了一些操作LCD的接口,如on/off。

Note:設備和driver的name需要一致纔可以綁定;另外,如果某些設備不需要讓platform的總線來管理,那麼只需要註冊驅動即可,而無須向系統中註冊device,如msm_touch。

Android display架構分析(五)

http://hi.baidu.com/leowenj/blog/item/7a12ecb77067737f8ad4b266.html

Display接口介紹

、User Space display接口

在Android平臺下,應用程序面對的顯示部分的接口就是HAL,參考copybit.c,具體接口如下介紹:

  1. open_copybit  


初始化相關變量,並調用open("/dev/graphics/fb0", O_RDWR, 0);打開fb設備。

  1. set_parameter_copybit  


設置各種操作參數,如rotate、alpha、dither等。

  1. stretch_copybit  


Copy一塊數據(Rectangle)到顯存,然後並命令msm_fb進行顯示。

  1. close_copybit  


調用close(ctx->mFD);關閉fb設備。

Note:另外,應用程序在使用上面接口之前,需要調用mapFrameBuffer接口(EGLDisplaySurface.cpp),其功能如下:

1、 初始化顯示相關參數,並設置到底層。

2、 映射出顯存的虛擬地址。

、Kernel display接口

Kernel部分顯示的接口全部都在fbmem.c中,這裏詳細介紹一下:

fb_open

打開Linux下fb設備。

fb_read/fb_write

讀寫顯存中的數據

fb_ioctl

對顯示設備的命令操作。如get或set一些顯示參數、通知底層進行刷屏等。

在典型應用中,畫屏的一般步驟如下:

1. 打開/dev/fb設備文件。

2. 用ioctrl操作取得當前顯示屏幕的參數,如屏幕分辨率,每個像素點的比特數。根據屏幕參數可計算屏幕緩衝區的大小。

3. 將屏幕緩衝區映射到用戶空間。

4. 映射後就可以直接讀寫屏幕緩衝區,進行繪圖和圖片顯示了。

典型程序段如下:

  1. #include  
  2. int main()  
  3. {  
  4.     int fbfd = 0;  
  5.     struct fb_var_screeninfo vinfo;  
  6.     struct fb_fix_screeninfo finfo;  
  7.     long int screensize = 0;  
  1. /*打開設備文件*/  
  2. fbfd = open("/dev/fb0", O_RDWR);  
  1. /*取得屏幕相關參數*/  
  2. ioctl(fbfd, FBIOGET_FSCREENINFO, &finfo); ioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo);  
  1. /*計算屏幕緩衝區大小*/  
  2. screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8;  
  1. /*映射屏幕緩衝區到用戶地址空間*/  
  2. fbp=(char*)mmap(0,screensize,PROT_READ|PROT_WRITE,MAP_SHARED, fbfd, 0);  
  1.     /*下面可通過fbp指針讀寫緩衝區*/  
  2.     ...  
  3. }  


典型應用flow分析

在不同應用程序中,上層的調用會有所不同,比如Andriod下會選擇應用程序跳過Linux fb操作層,直接操作顯卡驅動層,稱之爲BLT accelerator。

下面看一下Android平臺下畫屏的操作流程。

1、 通過mapFrameBuffer直接把用戶空間的數據映射到顯存中。

2、 調用HAL中的stretch函數直接命令MSM設備提取顯存數據然後送入MDP PPP進行處理並經MDDI接口送到外圍LCD組件。

具體的函數調用流程如下:

copybit_open();//打開BlitEngine,同時也打開fb設備

mapFrameBuffer();//設置顯示參數,同時得到顯存虛擬地址

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 HW operation

Android display架構分析(六)

http://hi.baidu.com/leowenj/blog/item/78c068dc443c961f48540361.html

介紹

Note:

本部分介紹的完全是用戶空間顯示部分的架構,與kernel並沒有直接的聯繫,主要是JNI以下到HAL以上的部分。

、Surface manager(surface flinger)簡介

Surface manager是用戶空間中framework下libraries中負責顯示相關的一個模塊。如下:

8

當系統同時執行多個應用程序時,Surface Manager會負責管理顯示與存取操作間的互動,另外也負責將2D繪圖與3D繪圖進行顯示上的合成。

    surface manager 可以準備一塊 surface(可以看作一個layer),把 surface 的 fd (一塊內存) 傳給一個 app,讓 app 可以在上面作畫。典型應用如下:

9 
10

2架構分析

Android中的圖形系統採用Client/Server架構,如下:

Client端:應用程序相關部分。代碼分爲兩部分,一部分是由Java提供的供應用使用的api,另一部分則是由c++寫成的底層實現。

Server端:即SurfaceFlinger,負責合成並送入buffer顯示。其主要由c++代碼編寫而成。

Client和Server之前通過Binder的IPC方式進行通信,總體結構圖如下:

如上圖所示,Surface的client部分其實是提供給各應用程序進行畫圖操作的一個橋樑,該橋樑通過binder通向server端的Surfaceflinger,Surfaceflinger負責合成各個surface,然後把buffer傳送到framebuffer端進行底層顯示。其中每個surface對應2個buffer,一個front buffer, 一個back buffer,更新時,數據更新在back buffer上,需要顯示時,則將back buffer和front buffer互換。

下一部分我們重點研究一下Surfaceflinger。

Android display架構分析(七-1)

http://hi.baidu.com/leowenj/blog/item/7abbe33a309367ff3b87ce6f.html

流程分析
根據前面的介紹,surfaceflinger作爲一個server process,上層的應用程序(作爲client)通過Binder方式與其進行通信。Surfaceflinger作爲一個thread,這裏把它分爲3個部分,如下:

1、 Thread本身處理部分,包括初始化以及thread loop。

2、 Binder部分,負責接收上層應用的各個設置和命令,並反饋狀態標誌給上層。

3、 與底層的交互,負責調用底層接口(HAL)。

結構圖如下:

11

註釋:

a、 Binder接收到應用程序的命令(如創建surface、設置參數等),傳遞給flinger。

b、 Flinger完成對應命令後將相關結果狀態反饋給上層。

c、 在處理上層命令過程中,根據需要設置event(主要和顯示有關),通知Thread Loop進行處理。

d、 Flinger根據上層命令通知底層進行處理(主要是設置一些參數,Layer、position等)

e、 Thread Loop中進行surface的合成並通知底層進行顯示(Post buffer)。

f、 DisplayHardware層根據flinger命令調用HAL進行HW的操作。

下面來具體分析一些SurfaceFlinger中重要的處理函數以及surface、Layer的屬性

1)、readToRun

SurfaceFlinger thread的初始化函數,主要任務是分配內存和設置底層接口(EGL&HAL)。

  1. status_t SurfaceFlinger::readyToRun()  
  2. {  
  3. …  
  4. …  
  5. mServerHeap = new MemoryDealer(4096, MemoryDealer::READ_ONLY);//爲IPC分配共享內存  
  6. …  
  7. mSurfaceHeapManager = new SurfaceHeapManager(this, 8 << 20);//爲flinger分配heap,大小爲8M,存放具體的顯示數據  
  8. {  
  9. // initialize the main display  
  10. GraphicPlane& plane(graphicPlane(dpy));  
  11. DisplayHardware* const hw = new DisplayHardware(this, dpy);  
  12. plane.setDisplayHardware(hw);//保存顯示接口  
  13. }  
  14. //獲取顯示相關參數  
  15. const GraphicPlane& plane(graphicPlane(dpy));  
  16. const DisplayHardware& hw = plane.displayHardware();  
  17. const uint32_t w = hw.getWidth();  
  18. const uint32_t h = hw.getHeight();  
  19. const uint32_t f = hw.getFormat();  
  20. …  
  21. …  
  22. // Initialize OpenGL|ES  
  23. glActiveTexture(GL_TEXTURE0);  
  24. glBindTexture(GL_TEXTURE_2D, 0);  
  25. glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);  
  26. glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);  
  27. glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);  
  28. …  
  29. …  
  30. }  


2)、ThreadLoop

Surfaceflinger的loop函數,主要是等待其他接口發送的event,進行顯示數據的合成以及顯示。

  1. bool SurfaceFlinger::threadLoop()  
  2. {  
  3. waitForEvent();//等待其他接口的signal event  
  4. …  
  5. …  
  6. // post surfaces (if needed)  
  7. handlePageFlip();//處理翻頁機制  
  8. const DisplayHardware& hw(graphicPlane(0).displayHardware());  
  9. if (LIKELY(hw.canDraw()))  
  10. {  
  11. // repaint the framebuffer (if needed)  
  12. handleRepaint();//合併所有layer並填充到buffer中去  
  13. …  
  14. …  
  15. postFramebuffer();//互換front buffer和back buffer,調用EGL接口進行顯示  
  16. }  
  17. …  
  18. …  
  19. }  


3)、createSurface

提供給應用程序的主要接口,該接口可以創建一個surface,底層會根據參數創建layer以及分配內存,surface相關參數會反饋給上層

  1. sp SurfaceFlinger::createSurface(ClientID clientId, int pid,  
  2. ISurfaceFlingerClient::surface_data_t* params,  
  3. DisplayID d, uint32_t w, uint32_t h, PixelFormat format,  
  4. uint32_t flags)  
  5. {  
  6. …  
  7. …  
  8. int32_t id = c->generateId(pid);  
  9. if (uint32_t(id) >= NUM_LAYERS_MAX) //NUM_LAYERS_MAX=31  
  10. {  
  11. LOGE("createSurface() failed, generateId = %d", id);  
  12. return  
  13. }  
  14. …  
  15. layer = createNormalSurfaceLocked(c, d, id, w, h, format, flags);//創建layer,根據參數(寬高格式)分配內存(共2個buffer:front/back buffer)  
  16. if (layer)  
  17. {  
  18. setTransactionFlags(eTransactionNeeded);  
  19. surfaceHandle = layer->getSurface();//創建surface  
  20. if (surfaceHandle != 0)  
  21. surfaceHandle->getSurfaceData(params);//創建的surface參數反饋給應用層  
  22. }  
  23. }  


待續。。。

Android display架構分析(七-2)

http://hi.baidu.com/leowenj/blog/item/ba4c5d6378a5da48eaf8f86a.html

4)、setClientState

處理上層的各個命令,並根據flag設置event通知Threadloop進行處理

  1. status_t SurfaceFlinger::setClientState(  
  2. ClientID cid,  
  3. int32_t count,  
  4. const layer_state_t* states)  
  5. {  
  6. Mutex::Autolock _l(mStateLock);  
  7. uint32_t flags = 0;  
  8. cid <<= 16;  
  9. for (int i=0 ; i  
  10. {  
  11. const layer_state_t& s = states[i];  
  12. LayerBaseClient* layer = getLayerUser_l(s.surface | cid);  
  13. if (layer)  
  14. {  
  15. const uint32_t what = s.what;  
  16.       // 檢測應用層是否設置各個標誌,如果有則通知底層完成對應操作,並通知ThreadLoop做對應的處理  
  17.    if (what & eDestroyed) //刪除該層Layer  
  18.      {  
  19. if (removeLayer_l(layer) == NO_ERROR)  
  20.    {  
  21. flags |= eTransactionNeeded;  
  22. continue;  
  23. }  
  24. }  
  25. if (what & ePositionChanged) //顯示位置變化  
  26.      {  
  27. if (layer->setPosition(s.x, s.y))  
  28. flags |= eTraversalNeeded;  
  29. }  
  30. if (what & eLayerChanged) //Layer改變  
  31.      {  
  32. if (layer->setLayer(s.z))  
  33.     {  
  34. mCurrentState.layersSortedByZ.reorder(  
  35. layer, &Layer::compareCurrentStateZ);  
  36. flags |= eTransactionNeeded|eTraversalNeeded;  
  37. }  
  38. }  
  39. if (what & eSizeChanged)  
  40.       {  
  41. if (layer->setSize(s.w, s.h))//設置寬高變化  
  42. flags |= eTraversalNeeded;  
  43. }  
  44. if (what & eAlphaChanged) {//設置Alpha效果  
  45. if (layer->setAlpha(uint8_t(255.0f*s.alpha+0.5f)))  
  46.                   flags |= eTraversalNeeded;  
  47. }  
  48. if (what & eMatrixChanged) {//矩陣參數變化  
  49. if (layer->setMatrix(s.matrix))  
  50. flags |= eTraversalNeeded;  
  51. }  
  52. if (what & eTransparentRegionChanged) {//顯示區域變化  
  53. if (layer->setTransparentRegionHint(s.transparentRegion))  
  54. flags |= eTraversalNeeded;  
  55. }  
  56. if (what & eVisibilityChanged) {//是否顯示  
  57. if (layer->setFlags(s.flags, s.mask))  
  58. flags |= eTraversalNeeded;  
  59. }  
  60. }  
  61. }  
  62. if (flags)  
  63. {  
  64. setTransactionFlags(flags);//通過signal通知ThreadLoop  
  65. }  
  66. return NO_ERROR;  
  67. }  


5)、composeSurfaces

該接口在Threadloop中被調用,負責將所有存在的surface進行合併,OpenGl模塊負責這個部分。

6)、postFramebuffer

該接口在Threadloop中被調用,負責將合成好的數據(存於back buffer中)推入在front buffer中,然後調用HAL接口命令底層顯示。

7)、從3中可知,上層每創建一個surface的時候,底層都會同時創建一個layer,下面看一下surface及layer的相關屬性。

Note:code中相關結構體太大,就不全部羅列出來了

   A、Surface相關屬性(詳細參考文件surface.h)

       a1:SurfaceID:根據此ID把相關surface和layer對應起來

      a2:SurfaceInfo

包括寬高格式等信息

a3:2個buffer指針、buffer索引等信息

   B、Layer相關屬性(詳細參考文件layer.h/layerbase.h/layerbitmap.h)

包括Layer的ID、寬高、位置、layer、alpha指、前後buffer地址及索引、layer的狀態信息(如eFlipRequested、eBusy、eLocked等)

Android display架構分析(八)

http://hi.baidu.com/leowenj/blog/item/03aae36137acb8d1e6113a75.html

開發的經驗分享

1Display Driver的工作內容

參考上面linux下fb設備的軟件架構,可以知道,要加入一個新的MDDI 接口的LCM,Driver的工作就是要提供自己的mddi_xxxx.c(在這次porting的過程中,爲了節省時間,我們直接修改了mddi_toshiba.c),並且完成和這個lcd相關的HWr的初始化。主要的工作包括:

A、初始化和LCD / LCD背光相關的IO以及電源;

B、編寫初始化函數 。主要是初始化LCD控制器,這個一般LCD廠商會提供;然後分配顯存,這個高通release過來的code已經包含這個動作了,最後是初始化一個fb_info的結構體,在這裏主要是把LCD的一些信息登記進來。

C、把LCD的設備以及驅動註冊到系統中去。(這裏因爲是替換現有的驅動,所以相關修改的部分不多。)

上述B、C部分代碼請參考kernel/drivers/video/msm/mddi_toshiba.c。

開發過程
1.2.1配置Power和IO

更改一些GPIO的配置以及一些電源的電平配置;然後通過實際測量,確保一下信號正常:

A、供給LCD以及MDDI Bridge的電源;

B、MDDI Bridge以及LCD reset信號;

C、控制背光IC的GPIO工作正常(背光不打開,無法調試LCD)。

1.2.2Porting LCD初始化序列

LCD init的code以及外圍MDDI Bridge的初始化code,都可以之前Boston Windows Mobile系統的code base中獲得;把這部分code移植到mddi_Toshiba.c中,並更改相應的圖像格式、分辨率等配置,編譯通過。LCD初始化部分就算基本完成。

1.2.3LCD初始化過程的調試

由於硬件在之前Boston load是可以工作的,可以認爲硬件連接等沒有問題,所以只需關注軟件部分就行。

Display部分軟件調試過程如下:

A、 開機後,量一下GPIO是否爲code中配置預期的狀態(可確保code中的

GPIO接口工作正常);

B、 量一下各個電源是否都處於Code中定義的電平值。這些都OK後,背光

是會亮的(背光的控制比較簡單,一個GPIO即可);

C、 這個時候如果LCD以及MDDI Bridge有被正常初始化的話,屏幕上是會

看出來的。反之,如果屏幕沒有顯示,需要用JTAG跟一下mddi_Toshiba.c中的初始化函數是否在開機的時候有被調用過。

目前版本中,是根據外圍MDDI Bridge中讀到的的廠商號來決定加載哪個驅動模塊的。在本次調試中,bootloader中可以正確讀到廠商號,所以bootloader中對於LCD的初始化是有做的,所以屏幕看到的狀態就是LCD初始化後的樣子(花屏)。 但Kernel起來後,並沒有其他顯示,用JTAG跟了後發現,Kernel中MODULE INIT中讀不到正確的廠商號,所以說後面的driver沒有被加載。接着發現如果在bootloader中如果不做MDDI Bridge的初始化,的話後面的MODULE INIT就可正常運行,該問題目前還沒有澄清(現在暫時先把bootloader中的init disable掉)。

1.2.4LCD的調整

初始化正常後,屏幕會顯示UI的相關畫面,但明顯顏色、位置都不對。

這個可能是數據類型配置不對導致的,即MDP輸出的類型、MDDI配置的類型以、LCD接收的類型不匹配導致,也有可能是RGB的順序不對導致(可配置成BGR)。經過調試後,把MDP端輸出的格式配置成RGB565,同時外圍MDDI Bridge以及LCD的input格式也配置成RGB565,這時顯示色彩正常了。

如果位置或者方向不對,比如說上下或是左右顛倒,可以更改LCD的配置中的掃描方向即可。

1.2.5其他

後續發現一個問題,播放video的時候顏色都是黑白的。

這個問題很容易讓人誤解,按照正常的理解,video decode出來的數據爲YCbCr,Y爲亮度信號,CbCr爲色差信號,如果只有Y信號的話顏色應該就是黑白的。所以有2個懷疑點,一個是decode出來的數據有誤,另一個是MDDI Bridge誤把輸入的YcbCr信號當作RGB信號進行出來,這個也是有可能的。但很快第二個懷疑點被排除了(因爲單更改MDDI input格式後還是不能解決問題)。

後來又詳細的看了顯示部分的代碼,並用JTAG追蹤video播放的時候用的顯示接口,發現目前所有的顯示接口輸出的格式都是RGB格式,也就是說在通過MDP之前YcbCr已經被轉化過;而MDP裏的轉換功能並沒有使用,MDP只是被當作一個DMA完成數據的直接傳輸,文檔中叫做Bypasse。

YcbCr到RGB的轉換是由Android的lib來完成。發了個SR給高通,高通的回覆也確認了,在6.3.50中,Android上層缺少這個lib(copybit.default.so),6.3.60之後的版本經解決了這個問題。

 

高通Android平臺下關於display部分的幾個關鍵問題

http://hi.baidu.com/leowenj/blog/item/06f8c0000763b37a3812bb03.html

顯示部分的幾個問題這幾天通過實際測試澄清了一下,主要是下圖中各個模塊的使用狀況以及HAL層幾個模塊的調用流程。以問題的方式描述如下:

1、 Ap是怎麼進行顯示的?

Surfaceflinger負責所有上層的顯示處理,對於AP(2D或是3D的應用程序)而言,只要到surfaceflinger中創建surface,設置好參數,接下來都是統一交給surfaceflinger進行處理

2、 Surface是怎麼管理多個surface的?

不管有多少個surface,最終送到顯示部分的只能是屏幕大小數據,surfaceflinger中利用MDP或是GPU進行多個surface的合成處理,普通的合成MDP就可完成,但如果是複雜的比如3D的應用等就必須使用GPU,最終合成的好數據會被送到framebuffer中。

3、 Framebuffer是什麼?

Framebuffer是Linux中爲顯示數據分配的一塊顯存(fb設備中),通常大小是一整個屏幕數據的兩倍,對於上層AP而言,只需要將要顯示的數據丟到framebuffer中就OK了,但此時顯示數據並未真正的被送到LCD上,而是暫存在framebuffer中而已。

4、 上層是通過什麼方式將顯示內容送到framebuffer的?

有2個方式(二選一,不會同時在運行):

A、 普通的顯示,使用copybit(MDP)(未使用GPU)

Surfaceflinger通過copybit將要顯示的數據送到framebuffer。

Note:copybit可以看做是MDP PPP的接口,它提供了MDP的功能,如多個layer合成,scale、rotate等。

其接口在:android/hardware/msm7k/libcopybit/copybit.cpp

B、 使用GPU(即使用圖中的Graphics driver)

當進行復雜的顯示處理時,比如3D的應用,GPU把處理好的數據直接丟到framebuffer中,和MDP沒有任何關係

5、 Framebuffer中的數據是如何被送到LCD顯示的?

圖中的Gralloc完成的。

Gralloc有2個功能:

一個是和copybit相同的,裏面有MDP PPP的接口(目前沒有使用)

另一個則是刷屏(整屏刷)的接口,即將framebuffer中的數據送到lcd上,調用的是MDP DMA的接口

這部分的code在android/hardware/msm7k/libgralloc-qsd8k目錄下,之前沒有留意,以爲沒有使用。現在可以看出開機初始化後就創建了disp_loop thread,裏面的操作就是調用系統接口

ioctl(m->framebuffer->fd, FBIOPUT_VSCREENINFO, &m->info)

將數據送到lcd

Note:送數據的時候是2個buffer切換的

另外,上層surfaceflinger也是通過Gralloc中的接口獲知屏幕的大小,調用接口爲

ioctl(fd, FBIOGET_VSCREENINFO, &info),info中的屏幕寬高對應的就是底層driver設置的寬高值

6、 OpenGL是什麼?

它是一個圖像處理引擎,當需要一些複雜的顯示(2D/3D)操作時會用到它。它分爲SW方案和HW方案,軟件方案就是圖中的libagl.so,對應到目前項目中是libGLES_android.so,它可以完成簡單的2D(文字,icon等)處理,通過trace看目前大部分顯示操作都是它來完成的。

Note:它是軟件方案,處理好的數據是通過copybit送到framebuffer的,而不是GPU。

其接口部分參考:android/frameworks/base/opengl/libagl

HW方案就是圖中的Graphics driver,它通過使用GPU硬件來完成圖像處理,處理後的數據直接送到framebuffer中。其接口部分參考:android/frameworks/base/opengl/libs(有幾個版本)

7、 OpenGL在項目中是如何配置的?

在android/vendor/qcom/msm7627_ffa目錄下有一個egl.cfg文件,裏面指定了當前版本中的OpenGL信息,目前如下:

0 0 android

0 1 adreno200

第一行代表該codebase支持SW 方案的OpenGL,是android default的

第二行代表該codebase也支持HW方案的OpenGL,是高通的adreno引擎

如果該cfg文件爲空,則只支持default的SW方案。

如果2個方案都在,上層將根據實際應用自行選擇使用其一。

該部分請參考:android/frameworks/base/opengl/libs/EGL/loader.cpp

63468f3ba1f59dfed4622529

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章