| 一、概述 
 1.1 簡介 本文檔主要包括LCD模塊的驅(qū)動流程分析、Framebuffer相關(guān)知識、Gralloc等相關(guān)內(nèi)容,以及LCD調(diào)試的一些經(jīng)驗(yàn)和相關(guān)bug的分析和講解。 
 1.2 開發(fā)環(huán)境 Android:4.0 Kernel: Linux3.0 Ubuntu:需要 10.04以及之后的版本 Gcc: 4.4.3 toolchain 
 1.3 硬件平臺 Msm8x25,pmic(pm8029) 
 1.4 操作系統(tǒng) Android:4.0, Kernel: 3.0 
 1.5 開發(fā)工具 VIM,SourceInsight,JTAG,ADB 
 
 二、LCD驅(qū)動流程分析 
 2.1 幀緩沖 2.1.1幀緩沖概念 幀緩沖(framebuffer)是Linux系統(tǒng)為顯示設(shè)備提供的一個接口,它將顯示緩沖區(qū)抽象,屏蔽圖像硬件的底層差異,允許上層應(yīng)用程序在圖形模式下直接對顯示緩沖區(qū)進(jìn)行讀寫操作。用戶不必關(guān)系物理顯示緩沖區(qū)的具體位置及存放方式,這些都由幀緩沖設(shè)備驅(qū)動本身來完成。對于幀緩沖設(shè)備而言,只要在顯示緩沖區(qū)中與顯示點(diǎn)對應(yīng)的區(qū)域?qū)懭腩伾?,對?yīng)的顏色會自動在屏幕上顯示。幀緩沖為標(biāo)準(zhǔn)字符設(shè)備,主設(shè)備號為29,對應(yīng)于/dev/fbn。 
 2.1.2 fb_info結(jié)構(gòu)體 幀緩沖設(shè)備最關(guān)鍵的一個數(shù)據(jù)結(jié)構(gòu)體是fb_info結(jié)構(gòu),為了便于記憶,簡稱FBI,這個機(jī)構(gòu)體在fb.h文件中定義了。FBI中包括了關(guān)于幀緩沖設(shè)備屬性和操作的完整描述,這個結(jié)構(gòu)體的定義如下所示。 
 其中fb_ops、fb_var_screeninfo和fb_fix_screeninfo這三個結(jié)構(gòu)極為重要。FBI的成員變量fbops為指向底層操作的函數(shù)指針,這些函數(shù)是需要驅(qū)動程序開發(fā)人員編寫的,不過高通平臺已經(jīng)定義好這些接口了,我們只需了解下這些接口的功能,不必修改。 fb_var_screeninfo記錄用戶可修改的顯示控制參數(shù),包括屏幕分辨率和每個像素點(diǎn)的比特數(shù)。fb_var_screeninfo中的xres定義屏幕一行有多少個點(diǎn),yres定義屏幕一列有多少個點(diǎn),bits_per_pixel定義每個點(diǎn)用多少個字節(jié)表示。而fb_fix_screeninfo中記錄用戶不能修改的顯示控制器的參數(shù),如屏幕緩沖區(qū)的物理地址、長度。當(dāng)對幀緩沖設(shè)備進(jìn)行映射操作時,就是從fb_fix_screeninfo中取得緩沖區(qū)物理地址的。上述結(jié)構(gòu)體都需要在驅(qū)動程序中初始化和設(shè)置,在后面的流程分析中會作具體的講解。 
 2.1.3 幀緩沖設(shè)備驅(qū)動結(jié)構(gòu) 
 從上圖可以看出,注冊framebuffer時需要用到fb_info結(jié)構(gòu)體,fb_info結(jié)構(gòu)體又包含了fb_ops結(jié)構(gòu)體,而fb_ops結(jié)構(gòu)體中的fb_read、fb_write用于應(yīng)用層對framebuffer的讀寫操作,fb_mmap用于應(yīng)用進(jìn)程和framebuffer之間的內(nèi)存映射,fb_ioctl用于應(yīng)用層對framebuffer進(jìn)行的一些控制操作,具體的操作會在后面的流程分析中講到。fb_info結(jié)構(gòu)中的fb_check_var和fb_set_par分別用于獲取和設(shè)置framebuffer的顯示參數(shù)。 
 2.2 LCD driver的注冊以及framebuffer的建立 在分析LCD的流程時,從底層往上一層層的分析,這樣更容易理解驅(qū)動層每一層的作用。 2.2.1 LCD驅(qū)動的注冊以及LCDC device的創(chuàng)建 在注冊LCD驅(qū)動前需要設(shè)置一些參數(shù),包括分辨率大小、bpp、像素時鐘頻率等如下圖, 
 
 在probe函數(shù)中會執(zhí)行msm_fb_add_device這個接口,這個接口在msm_fb.c中定義,這個接口的功能就是傳遞LCD driver的相關(guān)參數(shù)并根據(jù)LCD的類型(這里假設(shè)是RGB接口)創(chuàng)建一個LCDC device,此外還會創(chuàng)建一個framebuffer結(jié)構(gòu)體,并將其添加到全局的framebuffer列表fb_list里面。 
 2.2.2 MDP device的創(chuàng)建 在根據(jù)LCD的類型創(chuàng)建新設(shè)備時,會去執(zhí)行lcdc.c中的probe函數(shù),這個接口會創(chuàng)建一個mdp device,然后設(shè)置mdp device的一些顯示參數(shù)以及on和off接口,并將lcdc的pdev結(jié)構(gòu)體的next指針指向mdp device的設(shè)備結(jié)構(gòu)體,即mdp device是lcdc device的父節(jié)點(diǎn)。 
 2.2.3 msm_fbdevice的創(chuàng)建 在創(chuàng)建MDP device時,會去執(zhí)行mdp.c中的probe函數(shù),初始化MDP相關(guān)參數(shù)并創(chuàng)建msm_fb device,其next指針指向mdp device的設(shè)備結(jié)構(gòu)體,即msm_fb device是mdp device的父節(jié)點(diǎn)。 
 2.2.4 fb0的創(chuàng)建 在創(chuàng)建msmfb_device時,會去執(zhí)行msm_fb.c中的probe函數(shù),此接口中最重要的一個函數(shù)就是msm_fb_register,該接口會對前面講到的fb_info結(jié)構(gòu)體進(jìn)行填充,設(shè)置fb_var_screen和fb_fix_screen結(jié)構(gòu)體的顯示參數(shù),包括圖像顯示格式、可見分辨率、虛擬分辨率、紅綠藍(lán)色域的偏移、幀率、虛擬基地址等等一些參數(shù),并將高通平臺自帶的fb_ops接口填充到fb_info結(jié)構(gòu)體里面,然后調(diào)用register_framebuffer來創(chuàng)建fb0 device。至此,fb0的建立已經(jīng)完成,應(yīng)用層可以對fb0節(jié)點(diǎn)的控制來操作framebuffer緩沖區(qū)。 
 2.2.5 fb設(shè)備創(chuàng)建流程圖 
 從上圖可清楚的看出從注冊LCD驅(qū)動到創(chuàng)建framebuffer的流程。 
 2.3 fb設(shè)備的打開及framebuffer的使用 上面分析從LCD驅(qū)動的注冊到fb0建立的流程,那么fb0創(chuàng)建好后,怎么使用它呢?現(xiàn)在來分析下打開fb0操作framebuffer的流程。 2.3.1 gralloc設(shè)備的打開過程 顯示模塊在初始化時會去通過hw_get_module加載gralloc庫,該庫存在于/system/lib/hw中,在加載成功gralloc庫后,會調(diào)用framebuffer_open接口,這個接口最終會被指向framebuffer.cpp文件中的fb_device_open函數(shù)。執(zhí)行fb_device_open時,首先會去打開先前已經(jīng)加載成功的gralloc庫。 Gralloc模塊在在文件hardware/libhardware/include/hardware/gralloc.h中定義了一個幫助函數(shù)gralloc_open,用來打開gralloc設(shè)備。gralloc_open最終會指向gralloc.cpp中的gralloc_device_open函數(shù)。 
 intgralloc_device_open(consthw_module_t* module, const char* name, hw_device_t** device) { int status = -EINVAL; if (!strcmp(name, GRALLOC_HARDWARE_GPU0)) { gralloc_context_t *dev; dev = (gralloc_context_t*)malloc(sizeof(*dev)); 
 /* initialize our state here */ memset(dev, 0, sizeof(*dev)); 
 /* initialize the procs */ dev->device.common.tag = HARDWARE_DEVICE_TAG; dev->device.common.version = 0; dev->device.common.module = const_cast<hw_module_t*>(module); dev->device.common.close = gralloc_close; 
 dev->device.alloc = gralloc_alloc; dev->device.free = gralloc_free; 
 *device = &dev->device.common; status = 0; } else { status = fb_device_open(module, name, device); } return status; } 這個函數(shù)主要是用來創(chuàng)建一個gralloc_context_t結(jié)構(gòu)體,并且對它的成員變量device進(jìn)行初始化。結(jié)構(gòu)體gralloc_context_t的成員變量device的類型為gralloc_device_t,它用來描述一個gralloc設(shè)備。gralloc設(shè)備是用來分配和釋放圖形緩沖區(qū)的,這是通過調(diào)用它的成員函數(shù)alloc和free來實(shí)現(xiàn)的。 
 2.3.2 fb設(shè)備的打開過程 在打開gralloc設(shè)備后,會去執(zhí)行fb_device_open來打開fb設(shè)備。fb設(shè)備使用結(jié)構(gòu)體framebuffer_device_t來描述。結(jié)構(gòu)體framebuffer_device_t是用來描述系統(tǒng)幀緩沖區(qū)的信息,它定義在文hardware/libhardware/include/hardware/fb.h。 
 typedefstructframebuffer_device_t { structhw_device_t common; /* flags describing some attributes of the framebuffer */ const uint32_t flags;//記錄系統(tǒng)幀緩沖區(qū)的標(biāo)志 /* dimensions of the framebuffer in pixels */ const uint32_t width; //描述設(shè)備顯示屏的寬度 const uint32_t height; //描述設(shè)備顯示屏的高度 /* frambuffer stride in pixels */ constint stride; //描述設(shè)備顯示屏的一行有多少個像素點(diǎn) /* framebuffer pixel format */ constint format; //描述系統(tǒng)幀緩沖區(qū)的像素格式 /* resolution of the framebuffer's display panel in pixel per inch*/ const float xdpi; //描述設(shè)備顯示屏在寬度上的密度 const float ydpi; //描述設(shè)備顯示屏在高度上的密度 /* framebuffer's display panel refresh rate in frames per second */ const float fps; //描述設(shè)備顯示屏的刷新頻率,它的單位是幀每秒 /* min swap interval supported by this framebuffer */ constintminSwapInterval; //描述幀緩沖區(qū)交換前后兩個圖形緩沖區(qū)的最小時間間隔 /* max swap interval supported by this framebuffer */ constintmaxSwapInterval; //描述幀緩沖區(qū)交換前后兩個圖形緩沖區(qū)的最大時間間隔 /* number of framebuffers */ constintnumFramebuffers; int reserved[7]; int (*setSwapInterval)(structframebuffer_device_t* window,int interval); int (*setUpdateRect)(structframebuffer_device_t* window,int left, int top, int width, int height); int (*post)(structframebuffer_device_t* dev, buffer_handle_t buffer); int (*compositionComplete)(structframebuffer_device_t* dev); int (*lockBuffer) (structframebuffer_device_t* dev, int); void (*dump)(structframebuffer_device_t* dev, char *buff, intbuff_len); int (*enableScreen)(structframebuffer_device_t* dev, int enable); int (*perform) (structframebuffer_device_t* dev, int event, int value); } framebuffer_device_t; 
 Gralloc模塊在在文件hardware/libhardware/include/hardware/fb.h中定義了一個幫助函數(shù)framebuffer_open,用來打開fb設(shè)備。這個接口最終會被指向framebuffer.cpp文件中的fb_device_open函數(shù)。 intfb_device_open(hw_module_tconst* module, const char* name, hw_device_t** device) { int status = -EINVAL; if (!strcmp(name, GRALLOC_HARDWARE_FB0)) { alloc_device_t* gralloc_device; status = gralloc_open(module, &gralloc_device); if (status < 0) return status; 
 /* initialize our state here */ fb_context_t *dev = (fb_context_t*)malloc(sizeof(*dev)); memset(dev, 0, sizeof(*dev)); 
 /* initialize the procs */ dev->device.common.tag = HARDWARE_DEVICE_TAG; dev->device.common.version = 0; dev->device.common.module = const_cast<hw_module_t*>(module); dev->device.common.close = fb_close; dev->device.setSwapInterval = fb_setSwapInterval; dev->device.post = fb_post; dev->device.setUpdateRect = 0; dev->device.compositionComplete = fb_compositionComplete; dev->device.lockBuffer = fb_lockBuffer; #if defined(HDMI_DUAL_DISPLAY) dev->device.perform = fb_perform; #endif 
 private_module_t* m = (private_module_t*)module; status = mapFrameBuffer(m); if (status >= 0) { int stride = m->finfo.line_length / (m->info.bits_per_pixel>> 3); const_cast<uint32_t&>(dev->device.flags) = 0; const_cast<uint32_t&>(dev->device.width) = m->info.xres; const_cast<uint32_t&>(dev->device.height) = m->info.yres; const_cast<int&>(dev->device.stride) = stride; const_cast<int&>(dev->device.format) = m->fbFormat; const_cast<float&>(dev->device.xdpi) = m->xdpi; const_cast<float&>(dev->device.ydpi) = m->ydpi; const_cast<float&>(dev->device.fps) = m->fps; const_cast<int&>(dev->device.minSwapInterval) = private_module_t::PRIV_MIN_SWAP_INTERVAL; const_cast<int&>(dev->device.maxSwapInterval) = private_module_t::PRIV_MAX_SWAP_INTERVAL; const_cast<int&>(dev->device.numFramebuffers) = m->numBuffers; if (m->finfo.reserved[0] == 0x5444 && m->finfo.reserved[1] == 0x5055) { dev->device.setUpdateRect = fb_setUpdateRect; LOGD("UPDATE_ON_DEMAND supported"); } *device = &dev->device.common; } // Close the gralloc module gralloc_close(gralloc_device); } return status; } fb_device_open用來創(chuàng)建一個fb_context_t結(jié)構(gòu)體,并且對它的成員變量device進(jìn)行初始化。結(jié)構(gòu)體fb_context_t的成員變量device的類型為framebuffer_device_t,前面提到,它是用來描述fb設(shè)備的。fb設(shè)備主要是用來渲染圖形緩沖區(qū)的,這是通過調(diào)用它的成員函數(shù)post來實(shí)現(xiàn)的。從這里可以看出,函數(shù)fb_device_open所打開的fb設(shè)備的成員函數(shù)post被設(shè)置為Gralloc模塊中的函數(shù)fb_post.。 函數(shù)fb_device_open在打開fb設(shè)備的過程中,會調(diào)用另外一個函數(shù)mapFrameBuffer來獲得系統(tǒng)幀緩沖區(qū)的信息,并且將這些信息保存在參數(shù)module所描述的一個private_module_t結(jié)構(gòu)體的各個成員變量中。有了系統(tǒng)幀緩沖區(qū)的信息之后,函數(shù)fb_device_open接下來就可以對前面所打開的一個fb設(shè)備的各個成員變量進(jìn)行初始化。這些成員變量的含義可以參考前面對結(jié)構(gòu)體framebuffer_device_t的介紹。函數(shù)mapFrameBuffer除了用來獲得系統(tǒng)幀緩沖區(qū)的信息之外,還會將系統(tǒng)幀緩沖區(qū)映射到當(dāng)前進(jìn)程的地址空間來。 
 函數(shù)mapFrameBuffer實(shí)現(xiàn)在文件hardware/libhardware/modules/gralloc/framebuffer.cpp,如下所示: staticintmapFrameBuffer(structprivate_module_t* module) { pthread_mutex_lock(&module->lock); int err = mapFrameBufferLocked(module); pthread_mutex_unlock(&module->lock); return err; } 這個函數(shù)調(diào)用了同一個文件中的另外一個函數(shù)mapFrameBufferLocked來初始化參數(shù)module以及將系統(tǒng)幀緩沖區(qū)映射到當(dāng)前進(jìn)程的地址空間來。 intmapFrameBufferLocked(structprivate_module_t* module) { // already initialized... if (module->framebuffer) { return 0; } char const * constdevice_template[] = { "/dev/graphics/fb%u", "/dev/fb%u", 0 }; 
 intfd = -1; int i=0; char name[64]; char property[PROPERTY_VALUE_MAX]; 
 /* 首先在系統(tǒng)中檢查是否存在設(shè)備文件/dev/graphics/fb0或者/dev/fb0。如果存在的話,那么就調(diào)用函數(shù)open來打開它,并且將得到的文件描述符保存在變量fd中。這樣,接下來函數(shù)mapFrameBufferLocked就可以通過文件描述符fd來與內(nèi)核中的幀緩沖區(qū)驅(qū)動程序交互*/ 
 while ((fd==-1) &&device_template[i]) { snprintf(name, 64, device_template[i], 0); fd = open(name, O_RDWR, 0); i++; } if (fd< 0) return -errno; 
 /* 以下幾行代碼分別通過IO控制命令FBIOGET_FSCREENINFO和FBIOGET_VSCREENINFO來獲得系統(tǒng)幀緩沖區(qū)的信息,分別保存在fb_fix_screeninfo結(jié)構(gòu)體finfo和fb_var_screeninfo結(jié)構(gòu)體info中*/ structfb_fix_screeninfofinfo; if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1) return -errno; 
 structfb_var_screeninfo info; if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1) return -errno; 
 /*設(shè)置設(shè)備顯示屏的虛擬分辨率,結(jié)構(gòu)體fb_var_screeninfo的成員變量xres和yres用來描述顯示屏的可視分辨率,而成員變量xres_virtual和yres_virtual用來描述顯示屏的虛擬分辨率。這里保持可視分辨率以及虛擬分辨率的寬度值不變,而將虛擬分辨率的高度值設(shè)置為可視分辨率的高度值的NUM_BUFFERS倍。NUM_BUFFERS是一個宏,它的值被定義為2。這樣,我們就可以將系統(tǒng)幀緩沖區(qū)劃分為兩個圖形緩沖區(qū)來使用,即可以通過硬件來實(shí)現(xiàn)雙緩沖技術(shù)。在結(jié)構(gòu)體fb_var_screeninfo中,與顯示屏的可視分辨率和虛擬分辨率相關(guān)的另外兩個成員變量是xoffset和yoffset,它們用來告訴幀緩沖區(qū)當(dāng)前要渲染的圖形緩沖區(qū)是哪一個*/ info.reserved[0] = 0; info.reserved[1] = 0; info.reserved[2] = 0; info.xoffset = 0; info.yoffset = 0; info.activate = FB_ACTIVATE_NOW; 
 if(info.bits_per_pixel == 32) { /* * Explicitly request RGBA_8888 */ info.bits_per_pixel = 32; info.red.offset = 24; info.red.length = 8; info.green.offset = 16; info.green.length = 8; info.blue.offset = 8; info.blue.length = 8; info.transp.offset = 0; info.transp.length = 8; 
 /* Note: the GL driver does not have a r=8 g=8 b=8 a=0 config, so if we do * not use the MDP for composition (i.e. hw composition == 0), ask for * RGBA instead of RGBX. */ if (property_get("debug.sf.hw", property, NULL) > 0 &&atoi(property) == 0) module->fbFormat = HAL_PIXEL_FORMAT_RGBX_8888; else if(property_get("debug.composition.type", property, NULL) > 0 && (strncmp(property, "mdp", 3) == 0)) module->fbFormat = HAL_PIXEL_FORMAT_RGBX_8888; else module->fbFormat = HAL_PIXEL_FORMAT_RGBA_8888; } else { info.bits_per_pixel = 16; info.red.offset = 11; info.red.length = 5; info.green.offset = 5; info.green.length = 6; info.blue.offset = 0; info.blue.length = 5; info.transp.offset = 0; info.transp.length = 0; module->fbFormat = HAL_PIXEL_FORMAT_RGB_565; } 
 //adreno needs 4k aligned offsets. Max hole size is 4096-1 int size = roundUpToPageSize(info.yres * info.xres * (info.bits_per_pixel/8)); 
 /* * Request NUM_BUFFERS screens (at lest 2 for page flipping) */ intnumberOfBuffers = (int)(finfo.smem_len/size); LOGV("num supported framebuffers in kernel = %d", numberOfBuffers); 
 if (property_get("debug.gr.numframebuffers", property, NULL) > 0) { intnum = atoi(property); if ((num>= NUM_FRAMEBUFFERS_MIN) && (num<= NUM_FRAMEBUFFERS_MAX)) { numberOfBuffers = num; } } if (numberOfBuffers> NUM_FRAMEBUFFERS_MAX) numberOfBuffers = NUM_FRAMEBUFFERS_MAX; 
 LOGV("We support %d buffers", numberOfBuffers); 
 //consider the included hole by 4k alignment uint32_t line_length = (info.xres * info.bits_per_pixel / 8); info.yres_virtual = (size * numberOfBuffers) / line_length; 
 /*通過IO控制命令FBIOPUT_VSCREENINFO來設(shè)置設(shè)備顯示屏的虛擬分辨率以及像素格式,如果設(shè)置失敗,即調(diào)用函數(shù)ioctl的返回值等于-1,那么很可能是因?yàn)橄到y(tǒng)幀緩沖區(qū)在硬件上不支持雙緩沖,因此,接下來的代碼就會重新將顯示屏的虛擬分辨率的高度值設(shè)置為可視分辨率的高度值,并且將變量flags的PAGE_FLIP位置為0 */ uint32_t flags = PAGE_FLIP; if (ioctl(fd, FBIOPUT_VSCREENINFO, &info) == -1) { info.yres_virtual = size / line_length; flags&= ~PAGE_FLIP; LOGW("FBIOPUT_VSCREENINFO failed, page flipping not supported"); } 
 /* 另一方面,如果調(diào)用函數(shù)ioctl成功,但是最終獲得的顯示屏的虛擬分辨率的高度值小于可視分辨率的高度值的2倍,那么也說明系統(tǒng)幀緩沖區(qū)在硬件上不支持雙緩沖。在這種情況下,接下來的代碼也會重新將顯示屏的虛擬分辨率的高度值設(shè)置為可視分辨率的高度值,并且將變量flags的PAGE_FLIP位置為0。*/ if (info.yres_virtual< ((size * 2) / line_length) ) { // we need at least 2 for page-flipping info.yres_virtual = size / line_length; flags&= ~PAGE_FLIP; LOGW("page flipping not supported (yres_virtual=%d, requested=%d)", info.yres_virtual, info.yres*2); } 
 if (ioctl(fd, FBIOGET_VSCREENINFO, &info) == -1) return -errno; 
 if (int(info.width) <= 0 || int(info.height) <= 0) { // the driver doesn't return that information // default to 160 dpi info.width = ((info.xres * 25.4f)/160.0f + 0.5f); info.height = ((info.yres * 25.4f)/160.0f + 0.5f); } 
 /* 首先計算顯示屏的密度,即每英寸有多少個像素點(diǎn),分別寬度和高度兩個維度,分別保存在變量xdpi和ydpi中。注意,fb_var_screeninfo結(jié)構(gòu)體info的成員變量width和height用來描述顯示屏的寬度和高度,它們是以毫米(mm)為單位的。*/ floatxdpi = (info.xres * 25.4f) / info.width; floatydpi = (info.yres * 25.4f) / info.height; //The reserved[4] field is used to store FPS by the driver. float fps = info.reserved[4]; 
 /*通過IO控制命令FBIOGET_FSCREENINFO來獲得系統(tǒng)幀緩沖區(qū)的固定信息,并且保存在fb_fix_screeninfo結(jié)構(gòu)體finfo中,接下來再使用fb_fix_screeninfo結(jié)構(gòu)體finfo以及前面得到的系統(tǒng)幀緩沖區(qū)的其它信息來初始化參數(shù)module所描述的一個private_module_t結(jié)構(gòu)體 */ if (ioctl(fd, FBIOGET_FSCREENINFO, &finfo) == -1) return -errno; 
 module->flags = flags; module->info = info; module->finfo = finfo; module->xdpi = xdpi; module->ydpi = ydpi; module->fps = fps; 
 /*表達(dá)式info.yres_virtual / info.yres計算的是整個系統(tǒng)幀緩沖區(qū)可以劃分為多少個圖形緩沖區(qū)來使用 */ int err; module->numBuffers = info.yres_virtual / info.yres; /*bufferMask的值接著被設(shè)置為0,表示系統(tǒng)幀緩沖區(qū)中的所有圖形緩沖區(qū)都是處于空閑狀態(tài) */ module->bufferMask = 0; size_tfbSize = roundUpToPageSize(finfo.line_length * info.yres) * module->numBuffers; module->framebuffer = new private_handle_t(fd, fbSize, private_handle_t::PRIV_FLAGS_USES_PMEM, BUFFER_TYPE_UI,module->fbFormat, info.xres, info.yres); 
 /* 系統(tǒng)幀緩沖區(qū)是通過調(diào)用函數(shù)mmap來映射到當(dāng)前進(jìn)程的地址空間來的。映射后得到的地址空間使用一個private_handle_t結(jié)構(gòu)體來描述,這個結(jié)構(gòu)體的成員變量base保存的即為系統(tǒng)幀緩沖區(qū)在當(dāng)前進(jìn)程的地址空間中的起始地址。這樣,Gralloc模塊以后就可以從這塊地址空間中分配圖形緩沖區(qū)給當(dāng)前進(jìn)程使用 */ void* vaddr = mmap(0, fbSize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); if (vaddr == MAP_FAILED) { LOGE("Error mapping the framebuffer (%s)", strerror(errno)); return -errno; } module->framebuffer->base = intptr_t(vaddr); memset(vaddr, 0, fbSize); 
 return 0; } 
 至此,fb設(shè)備的打開過程的分析完成了,系統(tǒng)緩沖區(qū)已經(jīng)被映射到應(yīng)用層的進(jìn)程空間了。在fb設(shè)備被打開后,應(yīng)用層就可以通過ioctl接口對底層的framebuffer進(jìn)行操作了。 
 2.3.3 fb設(shè)備打開流程圖 
 
 
 
 三、LCD調(diào)試經(jīng)驗(yàn) 
 3.1 移植驅(qū)動代碼 在調(diào)試之前,需要先將LCD的驅(qū)動代碼移植好,使LCD的接口和平臺的接口對應(yīng)上,并根據(jù)硬件原理圖,修改gpio的配置,修改上電相關(guān)的接口,給LCD加載正確的供電。移植代碼包括板:板文件的修改、驅(qū)動源文件的添加、Konfig和Makefile的修改、工程mk的修改。代碼移植OK后就可以開始準(zhǔn)備調(diào)試LCD了。 
 3.2點(diǎn)亮背光 調(diào)試LCD時,在效果出來之前首先要保證背光能點(diǎn)亮。目前背光的控制方式有兩種:數(shù)字脈沖和PWM?,F(xiàn)在用得比較多的是PWM方式,PWM提供波形的源也有兩種,一種是從PMIC提供的,另一種是從LCD內(nèi)部出來的,CABC控制方式。 使用PMIC提供的源時,需要硬件上將pmic的PWM輸出腳(高通平臺是gpio01)連接到LCD的背光控制腳,在系統(tǒng)起來時,調(diào)用pwm初始化接口,之后調(diào)用pwm設(shè)置背光等級的接口就可以設(shè)置背光亮度了。 使用CABC控制方式時,只需設(shè)置LCD的相關(guān)寄存器就可以控制背光的打開和關(guān)閉,以及背光亮度的調(diào)試,具體設(shè)置請參考對應(yīng)的文檔。 
 3.3點(diǎn)亮LCD 點(diǎn)亮LCD時,首先要給LCD上電(一般情況下,對上電時序沒有嚴(yán)格的要求),LCD的上電接口在板文件中實(shí)現(xiàn)了。上完電后要使LCD復(fù)位,之后再往LCD的寄存器里面寫參數(shù),對LCD進(jìn)行初始化。初始化參數(shù)一般是由LCD屏廠提供的,在拿到初始化代碼后需要對照spec進(jìn)行適當(dāng)?shù)男薷?,?span style="font-family:Calibri">LCD的極性設(shè)置為當(dāng)前項(xiàng)目平臺的極性。當(dāng)LCD點(diǎn)亮后,可能還會出現(xiàn)顯示相關(guān)的問題,比如顯示區(qū)域有偏移,顯示的顏色不正確等等,需要對照spec修改相應(yīng)的寄存器進(jìn)行優(yōu)化。 
 
 四、問題總結(jié) 
 4.1 LCD相關(guān)問題 4.1.1驅(qū)動代碼移植完后,調(diào)試LCD,背光亮了,屏幕沒有任何顯示,一片漆黑 問題定位:測量LCD的相關(guān)腳的電壓,是否達(dá)到正常工作所需電壓; (1)若工作電壓沒有達(dá)到LCD的正常工作電壓 現(xiàn)象分析:LCD上電不成功,沒有正常工作; 解決方法:對照硬件原理圖,修改軟件代碼,修改上電接口的參數(shù); (2) 若工作電壓已經(jīng)達(dá)到LCD的正常工作電壓 現(xiàn)象分析:LCD正常工作了,但是LCD的極性不對; 解決方法:對照LCD的spec,參考當(dāng)前平臺的其他LCD驅(qū)動極性的設(shè)置,修改極性相關(guān)的寄存器。 
 4.1.2 屏幕點(diǎn)亮了,但是顏色顯示不正確,紅色和藍(lán)色互換了 問題定位:LCD有個寄存器是用來設(shè)置顯示模式的,紅色和藍(lán)色互換,說明對應(yīng)的寄存器設(shè)置不正確; 解決方法:對照LCD的spec,修改寄存器配置參數(shù); 
 4.1.3 LCD屏閃,偶爾還會出現(xiàn)不明顯的線條 問題定位:修改代碼,提高pclk到一個較高的頻率; (1)若提高pclk后,屏閃現(xiàn)象消失 現(xiàn)象分析:說明此現(xiàn)象是刷新頻率較低引起的; 解決方法:將pclk提高到一個合適的較高頻率; (2) 若提高pclk后,屏閃現(xiàn)象仍然存在 現(xiàn)象分析:說明此現(xiàn)象不是刷新頻率較低引起的,這種情況是LCD極性設(shè)置不正確造成的; 解決方法:對照spec修改LCD的極性; 
 4.1.4當(dāng)屏為24位LCD時,24位效果不明顯,和18位LCD一樣 問題定位:檢查LCD驅(qū)動配置是否正確,包括LCD寄存器的配置,驅(qū)動參數(shù) Bpp和顏色顯示模式fb_img_type;以及LCD的gpio配置,是否支持24位顯示模式 (1) 若檢查上述配置后,發(fā)現(xiàn)有些配置不滿足要求 現(xiàn)象分析:說明底層驅(qū)動的配置不合理,需要修改; 解決方法:將不合理的配置修改為正確的配置,若仍有問題,參考(2); (2) 若檢查上述配置后,所有配置都正確 現(xiàn)象分析:說明底層驅(qū)動的配置都是OK的,那么應(yīng)該跟上層有關(guān); 解決方法:聯(lián)系軟件部的同事,檢查并修改應(yīng)用層的參數(shù)設(shè)置; 
 4.2 平臺相關(guān)問題 4.2.1開機(jī)時,在開機(jī)logo和開機(jī)動畫之間會閃一下屏 現(xiàn)象分析:開機(jī)時,在kernel起來前是LK在支持LCD的顯示,kernel起來后會關(guān)掉LK那邊的電源和clk,然后打開 kernel這邊的電源和clk等,這個時候如果點(diǎn)亮背光的接口的調(diào)用比LCD初始化接口的調(diào)用早,就會引 起屏幕閃爍一下的現(xiàn)象 解決方法:調(diào)整背光接口和LCD初始化接口的調(diào)用順序,在需要的地方加上適當(dāng)?shù)难訒r 
 4.2.2做LCD兼容功能時,讀取到的ADC值老是有波動 問題定位:因?yàn)?span style="font-family:Calibri">LK從共享內(nèi)存中讀取到的ADC值是modem那邊讀取后存到共享內(nèi)存里面的,因此首先檢查共享 內(nèi)存的配置和使用是否正常 (1) 若共享內(nèi)存的設(shè)置和讀取沒有問題 現(xiàn)象分析:說明不是共享內(nèi)存?zhèn)髦凳?span style="font-family:Calibri">OK的,那么應(yīng)該是ADC讀取接口的問題,有波動說明可以讀取到數(shù)據(jù)但是 不準(zhǔn)確,那么應(yīng)該是adc通道的初始化沒有完成或者不正確引起的; 解決方法:將adc通道的初始化代碼放到比較靠前的地方,保證在調(diào)用adc接口讀取adc值時,adc通道已經(jīng)被正 確的初始化了; (2) 若共享內(nèi)存的設(shè)置和讀取有問題 解決方法:檢查共享內(nèi)存的申請和使用,注意,目前高通平臺支持三個id可以被客戶使用,分別是vendor0、 vendor1和vendor2; 
 4.2.3修改開機(jī)logo后,系統(tǒng)起不來,串口沒有任何log輸出 現(xiàn)象分析:系統(tǒng)啟動時,會給變量分配內(nèi)存,若開機(jī)logo太大,有可能在分配開機(jī)logo的內(nèi)存空間時將系統(tǒng)的 某些重要內(nèi)存區(qū)域覆蓋掉,這樣就會造成系統(tǒng)無法啟動了 解決方法:修改開機(jī)logo時,要控制轉(zhuǎn)換后logo數(shù)組的大小,使數(shù)組盡可能的小,這樣還可以節(jié)省刷logo的時 間,如果logo的背景是黑色的,則需要要顯示logo的彩色區(qū)域就可以了; 
 4.2.4開機(jī)logo和開機(jī)動畫之間有一段較長的黑屏?xí)r間 現(xiàn)象分析:kernel啟動時,會關(guān)掉所有的clk,然后重新初始化需要使用的clk,Mdp相關(guān)的clk會在kernel啟動時被 關(guān)掉,而在mdp初始化時才被打開,因此存在一段空白期; 解決方法:在kernel中對mdp相關(guān)的clk進(jìn)行設(shè)置,使其不被關(guān)閉; | 
|  |