1、概述數組
1.1 簡介數據結構
本文檔主要包括LCD模塊的驅動流程分析、Framebuffer相關知識、Gralloc等相關內容,以及LCD調試的一些經驗和相關bug的分析和講解。app
1.2 開發環境ide
Android:4.0函數
Kernel: Linux3.0工具
Ubuntu:須要 10.04以及以後的版本post
Gcc: 4.4.3 toolchain開發工具
1.3 硬件平臺優化
Msm8x25,pmic(pm8029)動畫
1.4 操做系統
Android:4.0, Kernel: 3.0
1.5 開發工具
VIM,SourceInsight,JTAG,ADB
2、LCD驅動流程分析
2.1 幀緩衝
2.1.1幀緩衝概念
幀緩衝(framebuffer)是Linux系 統爲顯示設備提供的一個接口,它將顯示緩衝區抽象,屏蔽圖像硬件的底層差別,容許上層應用程序在圖形模式下直接對顯示緩衝區進行讀寫操做。用戶沒必要關係物 理顯示緩衝區的具體位置及存放方式,這些都由幀緩衝設備驅動自己來完成。對於幀緩衝設備而言,只要在顯示緩衝區中與顯示點對應的區域寫入顏色值,對應的顏 色會自動在屏幕上顯示。幀緩衝爲標準字符設備,主設備號爲29,對應於/dev/fbn。
2.1.2 fb_info結構體
幀緩衝設備最關鍵的一個數據結構體是fb_info結構,爲了便於記憶,簡稱FBI,這個機構體在fb.h文件中定義了。FBI中包括了關於幀緩衝設備屬性和操做的完整描述,這個結構體的定義以下所示。
其中fb_ops、fb_var_screeninfo和fb_fix_screeninfo這三個結構極爲重要。FBI的成員變量fbops爲指向底層操做的函數指針,這些函數是須要驅動程序開發人員編寫的,不太高通平臺已經定義好這些接口了,咱們只需瞭解下這些接口的功能,沒必要修改。
fb_var_screeninfo記錄用戶可修改的顯示控制參數,包括屏幕分辨率和每一個像素點的比特數。fb_var_screeninfo中的xres定義屏幕一行有多少個點,yres定義屏幕一列有多少個點,bits_per_pixel定義每一個點用多少個字節表示。而fb_fix_screeninfo中記錄用戶不能修改的顯示控制器的參數,如屏幕緩衝區的物理地址、長度。當對幀緩衝設備進行映射操做時,就是從fb_fix_screeninfo中取得緩衝區物理地址的。上述結構體都須要在驅動程序中初始化和設置,在後面的流程分析中會做具體的講解。
2.1.3 幀緩衝設備驅動結構
從上圖能夠看出,註冊framebuffer時須要用到fb_info結構體,fb_info結構體又包含了fb_ops結構體,而fb_ops結構體中的fb_read、fb_write用於應用層對framebuffer的讀寫操做,fb_mmap用於應用進程和framebuffer之間的內存映射,fb_ioctl用於應用層對framebuffer進行的一些控制操做,具體的操做會在後面的流程分析中講到。fb_info結構中的fb_check_var和fb_set_par分別用於獲取和設置framebuffer的顯示參數。
2.2 LCD driver的註冊以及framebuffer的創建
在分析LCD的流程時,從底層往上一層層的分析,這樣更容易理解驅動層每一層的做用。
2.2.1 LCD驅動的註冊以及LCDC device的建立
在註冊LCD驅動前須要設置一些參數,包括分辨率大小、bpp、像素時鐘頻率等以下圖,
在probe函數中會執行msm_fb_add_device這個接口,這個接口在msm_fb.c中定義,這個接口的功能就是傳遞LCD driver的相關參數並根據LCD的類型(這裏假設是RGB接口)建立一個LCDC device,此外還會建立一個framebuffer結構體,並將其添加到全局的framebuffer列表fb_list裏面。
2.2.2 MDP device的建立
在根據LCD的類型建立新設備時,會去執行lcdc.c中的probe函數,這個接口會建立一個mdp device,而後設置mdp device的一些顯示參數以及on和off接口,並將lcdc的pdev結構體的next指針指向mdp device的設備結構體,即mdp device是lcdc device的父節點。
2.2.3 msm_fbdevice的建立
在建立MDP device時,會去執行mdp.c中的probe函數,初始化MDP相關參數並建立msm_fb device,其next指針指向mdp device的設備結構體,即msm_fb device是mdp device的父節點。
2.2.4 fb0的建立
在建立msmfb_device時,會去執行msm_fb.c中的probe函數,此接口中最重要的一個函數就是msm_fb_register,該接口會對前面講到的fb_info結構體進行填充,設置fb_var_screen和fb_fix_screen結構體的顯示參數,包括圖像顯示格式、可見分辨率、虛擬分辨率、紅綠藍色域的偏移、幀率、虛擬基地址等等一些參數,並將高通平臺自帶的fb_ops接口填充到fb_info結構體裏面,而後調用register_framebuffer來建立fb0 device。至此,fb0的創建已經完成,應用層能夠對fb0節點的控制來操做framebuffer緩衝區。
2.2.5 fb設備建立流程圖
從上圖可清楚的看出從註冊LCD驅動到建立framebuffer的流程。
2.3 fb設備的打開及framebuffer的使用
上面分析從LCD驅動的註冊到fb0創建的流程,那麼fb0建立好後,怎麼使用它呢?如今來分析下打開fb0操做framebuffer的流程。
2.3.1 gralloc設備的打開過程
顯示模塊在初始化時會去經過hw_get_module加載gralloc庫,該庫存在於/system/lib/hw中,在加載成功gralloc庫後,會調用framebuffer_open接口,這個接口最終會被指向framebuffer.cpp文件中的fb_device_open函數。執行fb_device_open時,首先會去打開先前已經加載成功的gralloc庫。
Gralloc模塊在在文件hardware/libhardware/include/hardware/gralloc.h中定義了一個幫助函數gralloc_open,用來打開gralloc設備。gralloc_open最終會指向gralloc.cpp中的gralloc_device_open函數。
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;
}
這個函數主要是用來建立一個gralloc_context_t結構體,而且對它的成員變量device進行初始化。結構體gralloc_context_t的成員變量device的類型爲gralloc_device_t,它用來描述一個gralloc設備。gralloc設備是用來分配和釋放圖形緩衝區的,這是經過調用它的成員函數alloc和free來實現的。
2.3.2 fb設備的打開過程
在打開gralloc設備後,會去執行fb_device_open來打開fb設備。fb設備使用結構體framebuffer_device_t來描述。結構體framebuffer_device_t是用來描述系統幀緩衝區的信息,它定義在文hardware/libhardware/include/hardware/fb.h。
typedefstructframebuffer_device_t {
structhw_device_t common;
/* flags describing some attributes of the framebuffer */
const uint32_t flags;//記錄系統幀緩衝區的標誌
/* dimensions of the framebuffer in pixels */
const uint32_t width; //描述設備顯示屏的寬度
const uint32_t height; //描述設備顯示屏的高度
/* frambuffer stride in pixels */
constint stride; //描述設備顯示屏的一行有多少個像素點
/* framebuffer pixel format */
constint format; //描述系統幀緩衝區的像素格式
/* resolution of the framebuffer's display panel in pixel per inch*/
const float xdpi; //描述設備顯示屏在寬度上的密度
const float ydpi; //描述設備顯示屏在高度上的密度
/* framebuffer's display panel refresh rate in frames per second */
const float fps; //描述設備顯示屏的刷新頻率,它的單位是幀每秒
/* min swap interval supported by this framebuffer */
constintminSwapInterval; //描述幀緩衝區交換先後兩個圖形緩衝區的最小時間間隔
/* max swap interval supported by this framebuffer */
constintmaxSwapInterval; //描述幀緩衝區交換先後兩個圖形緩衝區的最大時間間隔
/* 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中定義了一個幫助函數framebuffer_open,用來打開fb設備。這個接口最終會被指向framebuffer.cpp文件中的fb_device_open函數。
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用來建立一個fb_context_t結構體,而且對它的成員變量device進行初始化。結構體fb_context_t的成員變量device的類型爲framebuffer_device_t,前面提到,它是用來描述fb設備的。fb設備主要是用來渲染圖形緩衝區的,這是經過調用它的成員函數post來實現的。從這裏能夠看出,函數fb_device_open所打開的fb設備的成員函數post被設置爲Gralloc模塊中的函數fb_post.。
函數fb_device_open在打開fb設備的過程當中,會調用另一個函數mapFrameBuffer來得到系統幀緩衝區的信息,而且將這些信息保存在參數module所描述的一個private_module_t結構體的各個成員變量中。有了系統幀緩衝區的信息以後,函數fb_device_open接下來就能夠對前面所打開的一個fb設備的各個成員變量進行初始化。這些成員變量的含義能夠參考前面對結構體framebuffer_device_t的介紹。函數mapFrameBuffer除了用來得到系統幀緩衝區的信息以外,還會將系統幀緩衝區映射到當前進程的地址空間來。
函數mapFrameBuffer實如今文件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;
}
這個函數調用了同一個文件中的另一個函數mapFrameBufferLocked來初始化參數module以及將系統幀緩衝區映射到當前進程的地址空間來。
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];
/* 首先在系統中檢查是否存在設備文件/dev/graphics/fb0或者/dev/fb0。若是存在的話,那麼就調用函數open來打開它,而且將獲得的文件描述符保存在變量fd中。這樣,接下來函數mapFrameBufferLocked就能夠經過文件描述符fd來與內核中的幀緩衝區驅動程序交互*/
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來得到系統幀緩衝區的信息,分別保存在fb_fix_screeninfo結構體finfo和fb_var_screeninfo結構體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;
/*設置設備顯示屏的虛擬分辨率,結構體fb_var_screeninfo的成員變量xres和yres用來描述顯示屏的可視分辨率,而成員變量xres_virtual和yres_virtual用來描述顯示屏的虛擬分辨率。這裏保持可視分辨率以及虛擬分辨率的寬度值不變,而將虛擬分辨率的高度值設置爲可視分辨率的高度值的NUM_BUFFERS倍。NUM_BUFFERS是一個宏,它的值被定義爲2。這樣,咱們就能夠將系統幀緩衝區劃分爲兩個圖形緩衝區來使用,便可以經過硬件來實現雙緩衝技術。在結構體fb_var_screeninfo中,與顯示屏的可視分辨率和虛擬分辨率相關的另外兩個成員變量是xoffset和yoffset,它們用來告訴幀緩衝區當前要渲染的圖形緩衝區是哪個*/
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來設置設備顯示屏的虛擬分辨率以及像素格式,若是設置失敗,即調用函數ioctl的返回值等於-1,那麼極可能是由於系統幀緩衝區在硬件上不支持雙緩衝,所以,接下來的代碼就會從新將顯示屏的虛擬分辨率的高度值設置爲可視分辨率的高度值,而且將變量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");
}
/* 另外一方面,若是調用函數ioctl成功,可是最終得到的顯示屏的虛擬分辨率的高度值小於可視分辨率的高度值的2倍,那麼也說明系統幀緩衝區在硬件上不支持雙緩衝。在這種狀況下,接下來的代碼也會從新將顯示屏的虛擬分辨率的高度值設置爲可視分辨率的高度值,而且將變量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);
}
/* 首先計算顯示屏的密度,即每英寸有多少個像素點,分別寬度和高度兩個維度,分別保存在變量xdpi和ydpi中。注意,fb_var_screeninfo結構體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來得到系統幀緩衝區的固定信息,而且保存在fb_fix_screeninfo結構體finfo中,接下來再使用fb_fix_screeninfo結構體finfo以及前面獲得的系統幀緩衝區的其它信息來初始化參數module所描述的一個private_module_t結構體 */
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;
/*表達式info.yres_virtual / info.yres計算的是整個系統幀緩衝區能夠劃分爲多少個圖形緩衝區來使用 */
int err;
module->numBuffers = info.yres_virtual / info.yres;
/*bufferMask的值接着被設置爲0,表示系統幀緩衝區中的全部圖形緩衝區都是處於空閒狀態 */
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);
/* 系統幀緩衝區是經過調用函數mmap來映射到當前進程的地址空間來的。映射後獲得的地址空間使用一個private_handle_t結構體來描述,這個結構體的成員變量base保存的即爲系統幀緩衝區在當前進程的地址空間中的起始地址。這樣,Gralloc模塊之後就能夠從這塊地址空間中分配圖形緩衝區給當前進程使用 */
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設備的打開過程的分析完成了,系統緩衝區已經被映射到應用層的進程空間了。在fb設備被打開後,應用層就能夠經過ioctl接口對底層的framebuffer進行操做了。
2.3.3 fb設備打開流程圖
3、LCD調試經驗
3.1 移植驅動代碼
在調試以前,須要先將LCD的驅動代碼移植好,使LCD的接口和平臺的接口對應上,並根據硬件原理圖,修改gpio的配置,修改上電相關的接口,給LCD加載正確的供電。移植代碼包括板:板文件的修改、驅動源文件的添加、Konfig和Makefile的修改、工程mk的修改。代碼移植OK後就能夠開始準備調試LCD了。
3.2點亮背光
調試LCD時,在效果出來以前首先要保證背光能點亮。目前背光的控制方式有兩種:數字脈衝和PWM。如今用得比較多的是PWM方式,PWM提供波形的源也有兩種,一種是從PMIC提供的,另外一種是從LCD內部出來的,CABC控制方式。
使用PMIC提供的源時,須要硬件上將pmic的PWM輸出腳(高通平臺是gpio01)鏈接到LCD的背光控制腳,在系統起來時,調用pwm初始化接口,以後調用pwm設置背光等級的接口就能夠設置背光亮度了。
使用CABC控制方式時,只需設置LCD的相關寄存器就能夠控制背光的打開和關閉,以及背光亮度的調試,具體設置請參考對應的文檔。
3.3點亮LCD
點亮LCD時,首先要給LCD上電(通常狀況下,對上電時序沒有嚴格的要求),LCD的上電接口在板文件中實現了。上完電後要使LCD復位,以後再往LCD的寄存器裏面寫參數,對LCD進行初始化。初始化參數通常是由LCD屏廠提供的,在拿到初始化代碼後須要對照spec進行適當的修改,將LCD的極性設置爲當前項目平臺的極性。當LCD點亮後,可能還會出現顯示相關的問題,好比顯示區域有偏移,顯示的顏色不正確等等,須要對照spec修改相應的寄存器進行優化。
4、問題總結
4.1 LCD相關問題
4.1.1驅動代碼移植完後,調試LCD,背光亮了,屏幕沒有任何顯示,一片漆黑
問題定位:測量LCD的相關腳的電壓,是否達到正常工做所需電壓;
(1)若工做電壓沒有達到LCD的正常工做電壓
現象分析:LCD上電不成功,沒有正常工做;
解決方法:對照硬件原理圖,修改軟件代碼,修改上電接口的參數;
(2) 若工做電壓已經達到LCD的正常工做電壓
現象分析:LCD正常工做了,可是LCD的極性不對;
解決方法:對照LCD的spec,參考當前平臺的其餘LCD驅動極性的設置,修改極性相關的寄存器。
4.1.2 屏幕點亮了,可是顏色顯示不正確,紅色和藍色互換了
問題定位:LCD有個寄存器是用來設置顯示模式的,紅色和藍色互換,說明對應的寄存器設置不正確;
解決方法:對照LCD的spec,修改寄存器配置參數;
4.1.3 LCD屏閃,偶爾還會出現不明顯的線條
問題定位:修改代碼,提升pclk到一個較高的頻率;
(1)若提升pclk後,屏閃現象消失
現象分析:說明此現象是刷新頻率較低引發的;
解決方法:將pclk提升到一個合適的較高頻率;
(2) 若提升pclk後,屏閃現象仍然存在
現象分析:說明此現象不是刷新頻率較低引發的,這種狀況是LCD極性設置不正確形成的;
解決方法:對照spec修改LCD的極性;
4.1.4當屏爲24位LCD時,24位效果不明顯,和18位LCD同樣
問題定位:檢查LCD驅動配置是否正確,包括LCD寄存器的配置,驅動參數
Bpp和顏色顯示模式fb_img_type;以及LCD的gpio配置,是否支持24位顯示模式
(1) 若檢查上述配置後,發現有些配置不知足要求
現象分析:說明底層驅動的配置不合理,須要修改;
解決方法:將不合理的配置修改成正確的配置,若仍有問題,參考(2);
(2) 若檢查上述配置後,全部配置都正確
現象分析:說明底層驅動的配置都是OK的,那麼應該跟上層有關;
解決方法:聯繫軟件部的同事,檢查並修改應用層的參數設置;
4.2 平臺相關問題
4.2.1開機時,在開機logo和開機動畫之間會閃一下屏
現象分析:開機時,在kernel起來前是LK在支持LCD的顯示,kernel起來後會關掉LK那邊的電源和clk,而後打開
kernel這邊的電源和clk等,這個時候若是點亮背光的接口的調用比LCD初始化接口的調用早,就會引
起屏幕閃爍一下的現象
解決方法:調整背光接口和LCD初始化接口的調用順序,在須要的地方加上適當的延時
4.2.2作LCD兼容功能時,讀取到的ADC值總是有波動
問題定位:由於LK從共享內存中讀取到的ADC值是modem那邊讀取後存到共享內存裏面的,所以首先檢查共享
內存的配置和使用是否正常
(1) 若共享內存的設置和讀取沒有問題
現象分析:說明不是共享內存傳值是OK的,那麼應該是ADC讀取接口的問題,有波動說明能夠讀取到數據可是
不許確,那麼應該是adc通道的初始化沒有完成或者不正確引發的;
解決方法:將adc通道的初始化代碼放到比較靠前的地方,保證在調用adc接口讀取adc值時,adc通道已經被正
確的初始化了;
(2) 若共享內存的設置和讀取有問題
解決方法:檢查共享內存的申請和使用,注意,目前高通平臺支持三個id能夠被客戶使用,分別是vendor0、
vendor1和vendor2;
4.2.3修改開機logo後,系統起不來,串口沒有任何log輸出
現象分析:系統啓動時,會給變量分配內存,若開機logo太大,有可能在分配開機logo的內存空間時將系統的
某些重要內存區域覆蓋掉,這樣就會形成系統沒法啓動了
解決方法:修改開機logo時,要控制轉換後logo數組的大小,使數組儘量的小,這樣還能夠節省刷logo的時
間,若是logo的背景是黑色的,則須要要顯示logo的彩色區域就能夠了;
4.2.4開機logo和開機動畫之間有一段較長的黑屏時間
現象分析:kernel啓動時,會關掉全部的clk,而後從新初始化須要使用的clk,Mdp相關的clk會在kernel啓動時被
關掉,而在mdp初始化時才被打開,所以存在一段空白期;
解決方法:在kernel中對mdp相關的clk進行設置,使其不被關閉;