LCD驅動及Framebuffer相關

1.名詞解釋 java

GPU:Graphic Processing Unit (圖形處理器)

OpenGL:Open Graphic Library 定義了一個跨編程語言、跨平臺的編程接口的規格,不一樣廠商會有不一樣的實現方法,它主要用於三維圖象(二維的亦可)繪製。

SurfaceFlinger:Android中負責Surface之間疊加、混合操做的動態庫

Skia:Android中的2D圖形庫

libagl:Android中經過軟件方法實現的一套OpenGL動態庫

libhgl:爲區別libagl,自定義的一種叫法。特指GPU廠商提供的硬件實現的OpenGL

composition:特指SurfaceFlinger對各個Surface之間的疊加、混合操做

render:特指使用OpenGL動態庫進行3D渲染

copybit:Android使用2D引擎來加速圖形操做(主要是Surface之間的composition操做)的一種技術,對應着一個或幾個動態庫。

pmem:Android特有驅動,從linux內核中reserve物理連續內存,能夠爲2d、3d引擎、vpu等設備分配物理連續內存。

Android在啓動後,會在運行時根據配置文件加載OpenGL(libagl & libhgl)的實現,若是有libhgl實現,默認使用libhgl實現,不然使用libagl實現。

OpenGL在Android中兩個做用:

1. 用於Surface的composition操做。

SurfaceFlinger 會調用到OpenGL中,經過libagl或者libhgl作Surface的組合、疊加操做。

2. 用於圖形圖像的渲染

Android framework會對OpenGL實現進行java層次的簡單封裝,在java應用程序中對OpenGL的調用最終會調用到libagl或者libhgl中去。

不少第三方遊戲、3D圖庫、某些launcher會使用OpenGL實現比較炫麗UI的特效。 node

Copybit在Android中的做用

Copybit在Android中主要用於Surface的composition操做。

Skia在Android中的做用

Skia是Android的2D圖形庫,用於繪製文字、幾何圖形、圖像等。

Skia的設備後端:Raster、OpenGL、PDF

雙緩衝:
在計算機上的動畫與實際的動畫有些不一樣:實際的動畫都是先畫好了,播放的時候直接拿出來顯示就行。計算機動畫則是畫一張,就拿出來一張,再畫下一張,再拿 出來。若是所須要繪製的圖形很簡單,那麼這樣也沒什麼問題。但一旦圖形比較複雜,繪製須要的時間較長,問題就會變得突出。

讓咱們把計算機想象成一個畫圖比較快的人,假如他直接在屏幕上畫圖,而圖形比較複雜,則有可能在他只畫了某幅圖的一半的時候就被觀衆看到。然後面雖然他把 畫補全了,但觀衆的眼睛卻又沒有反應過來,還停留在原來那個殘缺的畫面上。也就是說,有時候觀衆看到完整的圖象,有時卻又只看到殘缺的圖象,這樣就形成了 屏幕的閃爍。

如何解決這一問題呢?咱們設想有兩塊畫板,畫圖的人在旁邊畫,畫好之後把他手裏的畫板與掛在屏幕上的畫板相交換。這樣以來,觀衆就不會看到殘缺的畫了。這 一技術被應用到計算機圖形中,稱爲雙緩衝技術。即:在存儲器(頗有多是顯存)中開闢兩塊區域,一塊做爲發送到顯示器的數據,一塊做爲繪畫的區域,在適當 的時候交換它們。因爲交換兩塊內存區域實際上只須要交換兩個指針,這一方法效率很是高,因此被普遍的採用。


(以上這段是網上摘的)
------------------------------------------------------------------------------------------
LCD調光方式有:
 一線脈衝計數調光
 PWM調光

調試LCD須要注意的:
SCLK
SDA
CS
RESET
上面這幾路線是發命令和參數用的,因此首先要調通,這幾路OK後屏應該亮或花屏

PCLK
HSYNC
VSYNC
DE
這幾路是刷數據用的,屏亮後顯示不正常就調這幾路的參數

320x480的,參數爲:
PCLK    10MHz
HSYNC    30KHz(行刷新)
VSYNC    60Hz(列刷新,即一屏)
SCLK    70KHz
DE    30KHz(Data Enable RGB寫數據時用到)
CS    SPI寫命令或參數時用,往屏上刷數據時用不到

常見的問題:
---
lk亮屏了,可是白屏,查下極性,看是否是反了
極性: lcdc側的Pclk,Hsync,Vsync,DE是高有效,那麼panel driver的
這些信號也應該是高有效,在往寄存器寫參數時要覈對下
---
要注意這些東西,HFP,HBP,VFP,VBP 即一般所講的前沿,後沿屏幕圖像左右抖動,不用說,確定是HFP/HBP不對。
前沿後沿就是給hsync,vsync往屏幕刷數據提供時間,能夠看你的LCD IC SPEC瞭解。
另外關於前沿後沿(前消隱/後消隱)的概念, 感受下面這一段是比較靠譜的:
正常的TFT一行的顯示週期是 前消隱+實際點輸出+後消隱
HBP、HFP 表明先後消隱所須要的週期

若是前消隱設置小了,後消隱設置大了,LCD控制器的實際輸出就會被當作消隱而不會實際顯示出來,看到的效果就是圖像左移,反之圖像右移.若是先後消隱都 設置小了,理論上第二行的圖像可能會被當作第一行的圖像顯示,形成屏幕歪斜不一樣步,可是有些TFT中內部的時序電路會自動補上缺乏的時鐘,因此也不必定會 看到不一樣步的畫面.場的消隱同理.

至於爲何要消隱是爲了兼容CRT顯示器的顯示原理,CRT顯示器每個掃描行完成後,電子槍須要回掃,這段時間不能顯示,因此這段時間的視頻信號須要暫停一下,就是消隱.

調試步驟(這段是網上摘的):
1)調試lcd背光,背光主要分爲PMIC自帶的和單獨的DCDC,若是爲PMIC自帶的背光,通常平臺廠商已經作好,
直接調用接口便可,若是爲單獨的DCDC驅動,則須要用GPIO控制DCDC的EN端
 
2)確認lcd的模擬電,io電是否正常
 
3)根據lcd的分辨率,RGB/CPU/MIPI等不一樣的接口,配置控制寄存器接口
 
4)根據lcd spec配置PCLK的頻率,配置PCLK,VSYNC,HSYNC,DE等控制線的極性
 
5)使用示波器測試全部clk的波形,確認頻率,極性是否符合要求
 
6)使用示波器測試data線,看是否有數據輸出,bpp的設置是否正確
 
7)若是lcd須要初始化,配置spi的接口,通常分爲cpu自帶的spi控制器,和gpio模擬的spi。
 
8)根據lcd spec中的初始化代碼進行lcd的初始化
 
9)用示波器測量lcd的spi clk及數據線,確認是否正常輸出
 
10)正常狀況下,此時lcd應該能夠點亮。若是沒有點亮,按照上述步驟1到9,逐項進行檢查測試,重點檢查第5項,clk的極性
 
11)若是lcd點亮,可是花屏。則須要先確認數據格式是否正確,而後確認fb裏的數據是否正常,有如下幾種方法確認fb裏的數據
i)cat /dev/graphics/fb0 > /sdcard/fb0,而後將/sdcard/fb0 >到另外一臺相同分辨率及相同格式的手機上,看圖片顯示是否正常
ii)使用irfanview軟件顯示cat /dev/graphics/fb0出來的raw數據,注意要正確設置分辨率及格式,不然顯示花屏
iii)若是adb鏈接正常,可使用豌豆莢等軟件,查看fb中的數據是否正常
 
經過以上三種途徑,若是確認fb中的數據正常顯示,則極可能爲lcd初始化代碼的問題,或者clk極性的問題,若是fb數據不正常,則可能爲lcd控制寄存器配置不正常致使。
 
LCD屏的調試注意事項
 
1. Pix clock是否在規定的範圍內。
 
2. Pclk是否極性正確。上升沿仍是降低沿。
 
3. 變頻引發的閃屏問題。能夠經過鎖定頻率來試驗是不是變頻引發。

要確保每一步都正確,那確定能夠顯示出圖像來
------------------------------------------------------------------------------------------
framebuffer

咱們如今用的是Adreno系列,Adreno是什麼?就至關於PC機上的顯卡。顯卡,也叫GPU,它的好壞會對手機的多媒體播放和3D遊戲性能有着直接的影響。framebuffer即幀緩衝區,這個幀緩衝區就位於顯卡的內部,幀緩衝區是顯卡上固化的存儲器,其中存放的是當前屏幕的內容。你要作高通平臺,那麼還會遇到MDP, MDP(Mobile Display Processor), MDP是什麼? 比方說你用的data format是RGB666,那麼這裏的data format輸出是多少位是由MDP決定的,MDP1.1/MDP1.2只能輸出16或18bpp,而MDP1.3能夠輸出24bpp。還有圖形的旋轉等效果,上面說的Copybit進行composition的動做我想底層就是由MDP支持的。咱們也能夠選擇是GPU來composition仍是MDP 來composition。 那麼GPU和MDP是什麼關係?GPU是顯卡,GPU處理要顯示的數據,即把矢量轉換爲bit,MDP負責把數據轉成接口信號送給相關的panel。在MDP裏有DMA,DMA會把framebuffer裏的內容放到panel上。後面分析代碼會看到,咱們把數據寫到MDP就無論了,它內部應該是把數據再調整,而後再寫到各個接口(MIPI/LCDC)鏈接的panel上。這些東西都是SoC,因此你在板子上是看不到的。

顯卡對CPU來講是外設,外設會經過I/O接口鏈接到I/O總線上,I/O總線再鏈接到CPU。 I/O接口包含多個I/O端口。每一個鏈接到I/O總線上的設備都有本身的I/O地址集,一般稱爲I/O端口(LKD part13 原話)。爲I/O編程提供統一的方法,但又不犧牲性能,因此設計者們把一組I/O端口(即一地址集)組織成一組專用的寄存器。這也就是咱們一般所說的I/O端口即寄存器的由來。因此每一個外設都是經過讀寫其寄存器來控制的。

I/O接口是處於一組I/O端口和對應的設備控制器之間的一種硬件電路。它起翻譯器的做用,即把I/O端口中的值轉換成設備所須要的命令和數據。還能夠經過一條IRQ線把這種電路鏈接到可編程中斷控制器上,以使它表明相應的設備發出中斷請求。

硬件組織上了解後咱們看軟件上怎麼處理。咱們要作的工做是,檢測哪些I/O端口已經分配給I/O設備。 一般來說,I/O設備驅動程序爲了探測硬件設備,須要盲目地向某一I/O端口寫入數據,可是,若是其餘硬件設備已經使用了這個端口,那麼系統就會崩潰。因此,linux的設計者們便想出用"資源"來記錄分配給每一個硬件設備的I/O端口。

資源(resource)被互斥地分配給設備驅動程序,一個資源表示I/O端口地址的一個範圍。

我爲何會說I/O呢,由於幀緩衝區就是I/O內存,和I/O有關,拋磚引玉。關於I/O端口和I/O內存的區別但是有學問的,你知道嗎?不知道是哪位大俠創做或整理的文章,粉好,你要是知道這二者的區別,那就不用看了,你要是不知道,那不妨看一看吧:http://blog.csdn.net/insoonior/article/details/8011192#t0

說到這裏咱們須要看代碼了:
static struct resource msm_fb_resources[] = {
        {
                .flags  = IORESOURCE_DMA,
        }
};

static struct platform_device msm_fb_device = {
        .name   = "msm_fb",
        .id     = 0,
        .num_resources  = ARRAY_SIZE(msm_fb_resources),
        .resource       = msm_fb_resources,
        .dev    = {
                .platform_data = &msm_fb_pdata,
        }
};

void __init msm_msm7627a_allocate_memory_regions(void)
{
        addr = alloc_bootmem_align(fb_size, 0x1000); //這就是framebuffer的物理地址,在framebuffer的驅動中會用到
        msm_fb_resources[0].start = __pa(addr);
        msm_fb_resources[0].end = msm_fb_resources[0].start + fb_size - 1;
}



系統啓動並初始化時會經過如下調用,把device 的 resource 加入到resource樹中:
  # TO tag         FROM line  in file/text
  1  1 platform_add_devices  1455  arch/arm/mach-msm/board-qrd7627a.c
  2  1 platform_device_register   114  drivers/base/platform.c
  3  1 platform_device_add   337  drivers/base/platform.c
  4  1 insert_resource   275  drivers/base/platform.c
  5  1 insert_resource_conflict   667  conflict = insert_resource_conflict(parent, new);
  6  1 __insert_resource   651  conflict = __insert_resource(parent, new);

有了以上的資源後就清楚了:
static int msm_fb_probe(struct platform_device *pdev)
{
                fbram_size =
                        pdev->resource[0].end - pdev->resource[0].start + 1;
                fbram_phys = (char *)pdev->resource[0].start;
                fbram = __va(fbram_phys);
}

static int msm_fb_register(struct msm_fb_data_type *mfd)
{
        struct fb_fix_screeninfo *fix;
        struct fb_var_screeninfo *var;    //主要是根據panel的信息初始化這兩個數據結構

        fbram_offset = PAGE_ALIGN((int)fbram)-(int)fbram;
        fbram += fbram_offset;
        fbram_phys += fbram_offset;
        fbram_size -= fbram_offset;
        fbi->screen_base = fbram;
    fbi->fix.smem_start = (unsigned long)fbram_phys;
}



smem_start在後面分析mdp_lcdc_update時會用到.
------------------------------------------------------------------------------------------
咱們看下數據到framebuffer後是怎麼顯示到屏幕上的。先從HAL層分析數據的走向,再往上的邏輯待定。

android/hardware/msm7k/libgralloc-qsd8k/ 這是display subsys的HAL層
// 上層調用這句ioctl把數據推給driver,放到framebuffer裏
ioctl(m->framebuffer->fd, FBIOPUT_VSCREENINFO, &m->info)


// 上層經過這句能夠獲知屏幕的大小
ioctl(fd, FBIOGET_VSCREENINFO, &info)



ioctl一路調用下來會到
drivers/video/fbmem.c
static long do_fb_ioctl(struct fb_info *info, unsigned int cmd,
                        unsigned long arg)
        case FBIOPUT_VSCREENINFO:
                if (copy_from_user(&var, argp, sizeof(var)))
                        return -EFAULT;
                if (!lock_fb_info(info))
                        return -ENODEV;
                console_lock();
                info->flags |= FBINFO_MISC_USEREVENT;
                ret = fb_set_var(info, &var);    //struct fb_info info, struct fb_var_screeninfo var
                info->flags &= ~FBINFO_MISC_USEREVENT;
                console_unlock();
                unlock_fb_info(info);
                if (!ret && copy_to_user(argp, &var, sizeof(var)))
                        ret = -EFAULT;
                break;


int fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var)
    fb_pan_display(info, &info->var);


int fb_pan_display(struct fb_info *info, struct fb_var_screeninfo *var)
        if ((err = info->fbops->fb_pan_display(var, info)))
                return err;




drivers/video/msm/msm_fb.c
static int msm_fb_pan_display(struct fb_var_screeninfo *var,
                              struct fb_info *info)
        if (info->node == 0 && !(mfd->cont_splash_done)) { /* primary */    /* 若是未上電,給panel上電 */
                mdp_set_dma_pan_info(info, NULL, TRUE);
                if (msm_fb_blank_sub(FB_BLANK_UNBLANK, info, mfd->op_enable)) {
                        pr_err("%s: can't turn on display!\n", __func__);
                        return -EINVAL;
                }
        }
    mdp_dma_pan_update(info);    /* 這句是把數據經過MDP更新到panel */




drivers/video/msm/mdp_dma.c
void mdp_dma_pan_update(struct fb_info *info)
    struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
    mfd->dma_fnc(mfd);



drivers/video/msm/mdp.c
static int mdp_probe(struct platform_device *pdev)
    case LCDC_PANEL:
        mfd->dma_fnc = mdp_lcdc_update;



drivers/video/msm/mdp_dma_lcdc.c
void mdp_lcdc_update(struct msm_fb_data_type *mfd)
        struct fb_info *fbi = mfd->fbi;
        uint8 *buf;
        int bpp;

        bpp = fbi->var.bits_per_pixel / 8;
        buf = (uint8 *) fbi->fix.smem_start;

        buf += calc_fb_offset(mfd, fbi, bpp);

    MDP_OUTP(MDP_BASE + dma_base + 0x8, (uint32) buf);    /* 最終會調到這句,上層拋過來的一堆數據存在buf指針指向的區域,而後把這個地址寫到MDP BASE,而後MDP再寫到panel上 */



drivers/video/msm/mdp.h
#define MDP_OUTP(addr, data) outpdw((addr), (data))



drivers/video/msm/msm_fb_def.h
#define outpdw(port, val)      writel(val, port)
------------------------------------------------------------------------------------------ framebuffer還有好多方方面面沒提到的,網上一搜一大把,就不提了。我只寫些本身認爲須要整理的東西,算是作個整理吧。 初始化過程:fbmem-->msm_fb-->mdp-->lcdc-->panel 初始化好以後,panel device的註冊過程:panel-->lcdc-->mdp-->msm_fb-->fbmem on/off: msm_fb-->mdp-->lcdc-->panel msm_fb的pdata是mdp mdp的pdata是lcdc lcdc的pdata是panel panel driver probe裏初始化了一個lcdc_dev,在lcdc driver probe裏初始化了一個mdp dev,在mdp driver probe裏初始化了一個msm_fb dev。最後調用msm_fb_register-->register_framebuffer最終和fbmem關聯上。 遺留問題: addr = alloc_bootmem_align(fb_size, 0x1000)分配的內存怎麼與幀緩衝區這個IO內存對應上的? 在手機的GPU上,可能framebuffer並未在GPU裏,就是一塊從物理內存上分配獲得的。 gpu確實有塊緩存,adreno 205配置了256KB圖形緩存(GMEM)專用於顏色緩衝,Z緩衝和模板緩衝等。顯然這個緩存不是framebuffer,不然過小了。 因此這個問題也不攻自破了,在手機設備上也許根本不存在幀緩衝區這個IO內存。
相關文章
相關標籤/搜索