硬件執(zhí)行流程1 硬件執(zhí)行流程2 這兩幅圖的差別在于mix的位置,mix的功能包括alpha blending,color-key,圖層處理等。流程1,這些工作是有軟件完成的,流程2是由硬件完成的。 這個網(wǎng)上找個圖啊,不過lcd驅(qū)動基本都是這樣的架構(gòu)。fbmem.c是已經(jīng)實現(xiàn)的,×××fb.c是需要我們自己實現(xiàn)的,這關(guān)系到具體的lcd屏。 首先,lcd驅(qū)動的初始化分為3個部分, 1 lcd設(shè)備的注冊 platform_add_devices(devices, ARRAY_SIZE(devices)); 2 lcd系統(tǒng)自帶驅(qū)動的注冊(fbmem.c) register_chrdev(FB_MAJOR,"fb",&fb_fops); 3 lcd自己寫的驅(qū)動的注冊(***fb.c) platform_driver_register(&sep0611fb_driver); 驅(qū)動部分為什么一分為二,為了方便編寫吧,移植等等,驅(qū)動開發(fā)也只需要編寫下面的一段而已,工作量減小了。那么其他驅(qū)動是否也是這樣安排的呢? 關(guān)于設(shè)備部分呢,主要就是關(guān)于具體設(shè)備的參數(shù),比如說lcd屏,就有一些參數(shù),這些參數(shù)根據(jù)屏的不同而不同,比如時序參數(shù),使用到GPIO,屏的寬度,屏的分辨率,還有屏控制器寄存器所占的物理地址,lcd用的中斷等。如果沒有l(wèi)cd,那么物理地址空間也不會看到lcd寄存器,lcd寄存器的物理地址也是需要申請的,還會映射為虛擬地址,便于操作。這些其實都是一個從無到有的過程。為了驅(qū)動lcd屏而存在。在文件系統(tǒng)里面,設(shè)備也是抽象的,它只包含具體設(shè)備的信息,驅(qū)動要操作具體的設(shè)備,就要先獲取相關(guān)的信息,找個就是它啊,好比,找到控制這個設(shè)備的寄存器,利用寄存器來控制外設(shè)。所以注冊的設(shè)備信息與設(shè)備必須一一對應(yīng),才能被驅(qū)動所用。這也解釋了驅(qū)動為什么能驅(qū)動硬件的問題。 fbmem.c中的read,write是給用戶調(diào)用的,然后再調(diào)用***fb.c中的具體實施環(huán)節(jié)。自己寫的***fb.c通過register_framebuffer()與系統(tǒng)中的fbmem.c聯(lián)系起來,共同組成驅(qū)動部分。 register_framebuffer(struct fb_info *fb_info) { ....... fb_info->dev = device_create(fb_class, fb_info->device, MKDEV(FB_MAJOR, i), NULL, "fb%d", i); //在/dev下面注冊設(shè)備,申請次設(shè)備號 ....... registered_fb[i] = fb_info; ....... } 這就把fbmem.c與×××fb.c聯(lián)系起來了。當(dāng)用戶使用fbmem.c中的open("/dev/fb0"),就會獲得fb的主設(shè)備號29,和次設(shè)備號0,通過主設(shè)備號找到fbmem.c中的read,write等,通過次設(shè)備號,可以決定你操作的是那個framebuffer(即那個圖層)。用戶只需要把數(shù)據(jù)寫入framebuffer,就等于寫入顯存,從硬件流程圖可知,數(shù)據(jù)從framebuffer到顯存是由DMA硬件完成的,所以在用戶看來,操作framebuffer就是操作顯存,使得圖片可以立即被輸出顯示。(framebuffer是內(nèi)核空間的一段內(nèi)存,各個圖層可以共用同一個framebuffer,也可以各自擁有一個獨立的framebuffer) 為什么不讓用戶直接操作顯存呢? 顯存本身就是一個設(shè)備,也有對應(yīng)的物理地址,這樣看是可以直接操作的。之所以使用framebuffer,我認(rèn)識用戶寫的數(shù)據(jù),需要處理才能顯示,而如果直接寫入顯存,就直接流入lcd屏了。所以要把用戶數(shù)據(jù)緩沖下,以便處理(圖層疊加啊,yuv改變?yōu)閞gb啊,使用colorkey,alpha-bleeding等),處理完再送入顯存,直接通過hdmi等接口送給lcd屏。 init部分主要完成的任務(wù)是:屏的初始化,顯示的初始化,最后打開lcd,背光,等待圖片數(shù)據(jù)輸入,然后輸出。(產(chǎn)生fbinfo結(jié)構(gòu)體給fbmem.c使用) 用戶操作的流程包括:ioctl控制圖片的顯示屬性(讀取初始化時的配置或者使用bsp操作寄存器改變顯示設(shè)置)和顯示圖片的數(shù)據(jù)源。(概括說就是控制和數(shù)據(jù)兩條線) 這一節(jié)主要分析init部分,就是屬于***fb.c文件的函數(shù)。 驅(qū)動程序綁定由內(nèi)核自動執(zhí)行,當(dāng)內(nèi)核發(fā)現(xiàn)一個驅(qū)動程序與一個設(shè)備匹配時,將調(diào)用驅(qū)動程序的probe函數(shù),完成對設(shè)備的探測及初始化等工作。device與driver是用名字搭配的,其實注冊并部分先后,device注冊時會去找driver,driver注冊的時候會去找device,當(dāng)配對成功后,driver就從device中獲取必要的數(shù)據(jù)(使用到資源或者一些參數(shù)),然后對設(shè)備進(jìn)行初始化。搭建必要的設(shè)備運行環(huán)境,比如lcd需要在內(nèi)存中申請一段空間,用于接收用戶發(fā)送過來的數(shù)據(jù)。只有都準(zhǔn)備完畢了,才可以打開設(shè)備,等待用戶操作。(lcd driver還產(chǎn)生了一個結(jié)構(gòu)體fbinfo,存放了用戶可能用到的所有信息,放到fbmem.c中)這樣probe函數(shù)的入口參數(shù)就可以解釋了。 kzalloc
用kzalloc申請內(nèi)存的時候, 效果等同于先是用 kmalloc() 申請空間 , 然后用 memset() 來初始化 ,所有申請的元素都被初始化為 0.
首先為什么要使用這個函數(shù),目的是在堆空間分配一段內(nèi)存給一個指針,然后初始化為0。其實完成同樣的功能也可以這樣,先定義一個結(jié)構(gòu)體實例,并初始化為0,然后把這個對象賦值給那個指針。結(jié)果都是一個指針指向了一段大小一樣空的內(nèi)存,只不過一個在堆,一個在棧。不過在堆中更容易控制,可以自己free掉,而不需要等待系統(tǒng)去幫你free。有個不好就是把指針給別人用,別人卻不去free它,那就糟了,這一點還是比不上結(jié)構(gòu)體實例的。 platform_get_resource
struct resource *platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num) { int i; for (i = 0; i < dev->num_resources; i++) { //不管你想找哪一類資源,都要從第一個資源看是比對,所以效率嗎貌似有點低,不過一般總資源數(shù)也不多。 struct resource *r = &dev->resource[i]; if (type == resource_type(r) && num-- == 0) return r; //找到匹配的之后把整個結(jié)構(gòu)體返回。 } return NULL; //沒找到就返回空。 } //這里取出的資源包括中斷號,io端口的寄存器物理地址等。這些資源是設(shè)備需要用的,驅(qū)動取出后用于靜態(tài)申請(就是已知物理地址,中斷號的情況下去申請物理地址和中斷號),可以理解為, //告訴系統(tǒng)這段物理地址和中斷號被占用,不能再分配給其他設(shè)備了。如果沒有l(wèi)cd,這些資源就可以給別人用了。這些都是系統(tǒng)資源啊,大家共用的。 info->mem = request_mem_region(res->start, resource_size(res), pdev->name);
//這就是申請指定的io物理地址空間,告訴系統(tǒng),這段物理地址被lcd占用。(后來使用寄存器的物理地址)
info->io = ioremap(res->start, resource_size(res));
//把申請到的物理地址空間映射到虛擬地址空間中,后來操作寄存器都是使用的虛擬地址。
framebuffer_alloc()
framebuffer_alloc()功能是向內(nèi)核申請一段大小為sizeof(struct fb_info) + size的空間,其中size的大小代表設(shè)備的私有數(shù)據(jù)空間,并用fb_info的par域指向該私有空間。為什要價size大小,因為這樣才是整個fbinfo的大小,私有數(shù)據(jù)空間需要與fbinfo一起free,就是說這段數(shù)據(jù)是不可缺少的,那么為什么不用結(jié)構(gòu)體來存放,這樣與fbinfo就是一個整體了啊,一起free更有說服力。大家注意一下par是個void型的指針,為什么要是void型的呢,因為他自己都不知道他將要指向的私有數(shù)據(jù)類型,那你用什么結(jié)構(gòu)體來存放這莫名的私有數(shù)據(jù)呢。fbinfo這個結(jié)構(gòu)體里面的成員是定好的,他是與fbmem.c的接口,這個接口結(jié)構(gòu)體中就只有這樣一個私有數(shù)據(jù)指針是可變的,是給用戶發(fā)揮的。這就解釋了為什么par是void類型的指針。既然fbinfo是在***fb.c中申請的,作為數(shù)據(jù)接口給fbmem.c,那么在init結(jié)束后也是不能釋放的,所以放在堆里面很安全,在注銷驅(qū)動的時候在remove函數(shù)內(nèi)會free掉,很方便是不是? 關(guān)于android lcd doublebuffer問題 DMA從SDRAM FIFO中取數(shù)據(jù),送給顯存buffer的時候,中間需要進(jìn)行處理,比如mix alphableeding,colorkey等工作,這些時間并不長,但是在進(jìn)行處理時,SDRAM FIFO中的數(shù)據(jù)無法更新,可以理解為是基于此FIFO進(jìn)行合成等工作的,合成完了才會送到顯存FIFO,送完后等待取出下一張要顯示的圖片,取圖片又是需要時間的,兩個時間加起來就無法忽略了,有明顯的延遲。所以使用doublebuffer,就是行為原來的兩倍,可以存兩張圖片,一個用來合成,一個用來繪制,兩個交替使用,解決延遲的問題。(這也就是通常說的pan_display) |
|