MTK 平臺LCD 驅動的整個過程的講解(轉)

MTK_LCD_Driver
代碼的路是在/mediatek/source/kernel/driver/vedio/mtkfb.c
module_init(mtkfb_init);模塊初始化函數
int__init mtkfb_init(void)
{
int r = 0;

MSG_FUNC_ENTER();

/* Register the driver withLDM */

if(platform_driver_register(&mtkfb_driver)) {//以platform方式進行註冊mtkfb driver
PRNERR("failed toregister mtkfb driver\n");
r = -ENODEV;
goto exit;
}
#ifdefCONFIG_HAS_EARLYSUSPEND
register_early_suspend(&mtkfb_early_suspend_handler);//LCD是以erly_suspend的方式註冊的,這個涉及到power_management的內容
#endif

DBG_Init();

exit:
MSG_FUNC_LEAVE();
return r;
知道設備模型的人應該知道platformbus總線的match函數的規則是device和driver的名字必須相同,當name匹配一樣的時候,我們就會調用driver裏面的probe函數,這個函數是LCDdriver的核心入口函數。
進入LCDprobe世界。
staticint mtkfb_probe(struct device *dev)
{
struct platform_device *pdev;
struct mtkfb_device *fbdev= NULL;
struct fb_info *fbi;
int init_state;
int r = 0;
char*p = NULL;
MSG_FUNC_ENTER();
printk("%s,%s\n", __func__, saved_command_line);
p= strstr(saved_command_line, "fps=");
if(p== NULL){
lcd_fps= 6000;
printk("[FBdriver]can not get fps from uboot\n");
}
else{
p+= 4;
lcd_fps= simple_strtol(p, NULL, 10);
}

…........................
…........................

r = register_framebuffer(fbi);
if (r != 0) {
PRNERR("register_framebufferfailed\n");
goto cleanup;
}

fbdev->state =MTKFB_ACTIVE;

MSG(INFO, "MTKframebuffer initialized vram=%lu\n", fbdev->fb_size_in_byte);

MSG_FUNC_LEAVE();
return 0;

cleanup:
mtkfb_free_resources(fbdev,init_state);

MSG_FUNC_LEAVE();
return r;
}
這個函數比較長,下面我們一一對這個Probe函數進行講解。
1、開始driver接受從uboot中傳遞過來的參數,saved_command_line變量,進行參數的取值。
2、DISP_IsContextInited這個函數判斷lcm_params、disp_drv、lcm_drv是不是都進行初始化了,如果都初始化了,那麼就返回TRUE如果有一個沒有進行初始化的話,那麼就返回FALSE。其實這裏在uboot中已經進行了初始化了,我們這裏假設這裏還沒有初始化,如果是FALSE的話,那麼就會調用mtkfb_find_lcm_driver這個function函數。
mtkfb_find_lcm_driver
BOOLmtkfb_find_lcm_driver(void)
{
BOOLret = FALSE;
char*p, *q;

p= strstr(saved_command_line,"lcm=");//這裏我們會找出”lcm=”這個字符串在saved_command_line中第一次出現的位置,取出這個指針。
if(p== NULL)
{
//we can't find lcm string in the command line, the uboot should be oldversion
returnDISP_SelectDevice(NULL);
}

p+= 4;
if((p- saved_command_line) > strlen(saved_command_line+1))
{
ret= FALSE;
gotodone;
}

printk("%s,%s\n", __func__, p);
q= p;
while(*q!= ' ' && *q != '\0')
q++;

memset((void*)mtkfb_lcm_name,0, sizeof(mtkfb_lcm_name));
strncpy((char*)mtkfb_lcm_name,(const char*)p, (int)(q-p));//這裏會找出lcm的名字,接下來會將這個Name股指給mtkfb_lcm_name這個變量,下面會將這個變量傳遞給mtkfb_lcm_name這個函數。

printk("%s,%s\n", __func__, mtkfb_lcm_name);
if(DISP_SelectDevice(mtkfb_lcm_name))
ret= TRUE;

done:
returnret;
}
continuetrace code:DISP_SelectDevice(mtkfb_lcm_name)
DISP_SelectDevice(mtkfb_lcm_name):
BOOLDISP_SelectDevice(const char* lcm_name)
{
LCD_STATUSret;

ret= LCD_Init();
printk("retof LCD_Init() = %d\n", ret);

lcm_drv= disp_drv_get_lcm_driver(lcm_name);
if(NULL == lcm_drv)
{
printk("%s,disp_drv_get_lcm_driver() returns NULL\n", __func__);
returnFALSE;
}

disp_dump_lcm_parameters(lcm_params);
returndisp_drv_init_context();
}
上面的函數還是比較複雜的,我們下面會進行一一的講解:
2.1:LCD_Init
LCD_STATUSLCD_Init(void)
{
LCD_STATUS ret =LCD_STATUS_OK;

memset(&_lcdContext, 0,sizeof(_lcdContext));//我們可以發現-lcdContext這個變量是一個數組,而且是static類型的,C語言中我們定義了這種類型的變量的話,那麼就會爲這個變量分配一個地址

// LCD controller would NOTreset register as default values
// Do it by SW here
//
ResetBackupedLCDRegisterValues();這裏我們會爲上面的_lcdContext.regBackup裏面的值進行賦值,我們所用的是LCD_OUTREG32,函數進行賦值的,開始我一直以爲這個變量根本就沒有賦值啊,我往哪裏的地址寫呢,換位思考下,我們定義了一個變量後就肯定有地址,向地址寫值就是將這個變量所指向的指針寫值,也就是賦值。regs->SERIAL_CFG;regs->;PARALLEL_CFG[0];regs->;PARALLEL_CFG[1];regs->;PARALLEL_CFG[2]

ret =LCD_PowerOn();//通過配置regiter的值將LCD打開,這裏就不具體糾結這個細節了,要深入的話,可以自己去看下。

LCD_OUTREG32(&LCD_REG->SYNC_LCM_SIZE,0x00010001);
LCD_OUTREG32(&LCD_REG->SYNC_CNT,0x1);

ASSERT(ret == LCD_STATUS_OK);

#ifENABLE_LCD_INTERRUPT
if(request_irq(MT6577_LCD_IRQ_ID,
_LCD_InterruptHandler,IRQF_TRIGGER_LOW, "mtklcd", NULL) < 0)//申請LCD的中斷處理函數,當有新的數據需要刷新到屏上面的時候,我們就會調用這個中斷處理函數,關於這個中斷處理函數我們下面會講解。這裏我一直很奇怪的是這個interrupt到底做了什麼事情,接下來我會聯繫lcm_update函數進行統一講解,這樣我就能夠將所有的流程串起來了。
{
DBI_LOG("[LCD][ERROR]fail to request LCD irq\n");
return LCD_STATUS_ERROR;
}
// mt65xx_irq_unmask(MT6577_LCD_IRQ_ID);//下面是設置一些寄存器的值
// enable_irq(MT6577_LCD_IRQ_ID);
init_waitqueue_head(&_lcd_wait_queue);
LCD_REG->INT_ENABLE.COMPLETED= 1;
// LCD_REG->INT_ENABLE.REG_COMPLETED= 1;
LCD_REG->INT_ENABLE.CMDQ_COMPLETED= 1;
LCD_REG->INT_ENABLE.HTT= 1;
LCD_REG->INT_ENABLE.SYNC =1;
#endif
return LCD_STATUS_OK;
}
2.2disp_drv_get_lcm_driver(lcm_name)
disp_drv_get_lcm_driver(lcm_name):這會將我們在uboot中得到的Lcm的名字傳遞過來。
constLCM_DRIVER *disp_drv_get_lcm_driver(const char *lcm_name)
{
LCM_DRIVER*lcm = NULL;
printk("[LCMAuto Detect], we have %d lcm drivers built in\n", lcm_count);
printk("[LCMAuto Detect], try to find driver for [%s]\n",
(lcm_name==NULL)?"unknown":lcm_name);

if(lcm_count==1)//進行判斷Lcm_count的值,這個值是通過計算lcm_driver_list裏面大小進行判斷的,如果我們需要新添加一個新的lcm進去的話,那麼就需要在這個數組裏面添加新的IC廠商的lcm,添加代碼的路徑是在/meidatek/custom/common/kernel/lcm/mt65xx_lcm_list.c裏面進行添加
{
//we need to verify whether the lcm is connected
//even there is only one lcm type defined
lcm= lcm_driver_list[0];//如果這個lcm_driver_list中只有一個Lcm的話,那麼就默認的就只取地一個就可以了
lcm->set_util_funcs(&lcm_utils);//沒一個lcm結構裏面都自己對應的定義成員,這裏會調用lcm->set_util_funcs函數,傳遞進去的參數是在disp_drv.c裏面定義好的結構,這個結構是mediatek的自己實現的display_driver:/mediatek/source/kernel/driver/video/disp_drv.c,我們調用lcm.set_util_funcs函數就是完成將display_driver裏面的結構賦值給lcm_util這個static結構。
lcm->get_params(&s_lcm_params);//調用Lcm.get_params函數將s_lcm_params這個變量進行初始化,所賦的值,我們都都在每一個lcm進行參數的初始化。

lcm_params= &s_lcm_params;
lcm_drv= lcm;

{
isLCMFound= TRUE;
}

printk("[LCMSpecified]\t[%s]\n", (lcm->name==NULL)?"unknown":lcm->name);

gotodone;
}
else
{
inti;

for(i= 0;i < lcm_count;i++)
{
lcm_params= &s_lcm_params;
lcm= lcm_driver_list;

printk("[LCMAuto Detect] [%d] - [%s]\t", i,(lcm->name==NULL)?"unknown":lcm->name);

lcm->set_util_funcs(&lcm_utils);
memset((void*)lcm_params,0, sizeof(LCM_PARAMS));
lcm->get_params(lcm_params);//上面的函數和count等於1的status是一樣的,參考上面就可以了。

disp_drv_init_ctrl_if();//初始化dispaly的controlinterface有串口和並口等
disp_drv_set_driving_current(lcm_params);//從函數的命名定義上面,我們是在設置lcd的電流,但是我根據地址查詢mediatek的socdatasheet並沒有找到相關的定義。
disp_drv_init_io_pad(lcm_params);//查看datasheet是說在設置這個register就可以設置pin腳的值

if(lcm_name!= NULL)
{
if(!strcmp(lcm_name,lcm->name))//將我們從uboot中得到的lcm_name和我們沒一個的lcm裏面的name進行對比,如果一樣的話,那麼就代表我們已經找到了我們使用的Lcm
{
printk("\t\t[success]\n");
isLCMFound= TRUE;
lcm_drv= lcm;

gotodone;
}
else
{
printk("\t\t[fail]\n");
}
}
else
{
if(LCM_TYPE_DSI== lcm_params->type){
init_dsi(FALSE);//初始化dsi這種模式
}

if(lcm->compare_id!= NULL &&lcm->compare_id())如果發現我們的傳遞的lcm_name是NULL;並且Lcm_list又不止一個的話,那麼就會調用沒一個lcm的compare_id函數,這個函數是我們lcm裏面實現好的,我這裏用的是r61408這個IC屏,我們就看看這個lcm的compare_id函數是如何實現的。我們R61408這個IC裏面直接是讀取IC裏面的devicecode register進行和這個LCM默認的值進行對比而得到的,當然不同deIC可能compare_id函數實現也不一樣,只要滿足一個判斷的標準就可以了。
{
printk("\t\t[success]\n");
isLCMFound= TRUE;
lcm_drv= lcm;

gotodone;
}
else
{
if(LCM_TYPE_DSI== lcm_params->type)
DSI_Deinit();
printk("\t\t[fail]\n");
}
}
}
#ifdefHQ_PROJECT_A75
/*HQ ynn 2012-06-29 modified for no lcd poweron*/
if(FALSE== isLCMFound)// ynn
{
lcm= lcm_driver_list[0];
lcm->set_util_funcs(&lcm_utils);
lcm->get_params(&s_lcm_params);
lcm_params= &s_lcm_params;
lcm_drv= lcm;
isLCMFound= TRUE;
}
#endif
}
done:
returnlcm_drv;
}//當找到這個lcm的話,就將找到的lcm賦值給lc_drv;將獲得的s_lcm_params賦值給lcm_params,並將isLCMFound這個標誌變量設置爲TRUE,代表我已經找到了LCM。
2.3disp_dump_lcm_parameters(lcm_params)
disp_dump_lcm_parameters(lcm_params):將獲得的lcm_params進行打印出來
2.4disp_drv_init_context
disp_drv_init_context:

staticBOOL disp_drv_init_context(void)
{
if(disp_drv != NULL && lcm_drv != NULL){
returnTRUE;
}

if(!isLCMFound)
DISP_DetectDevice();//進而判斷我們有沒有找到設備,如果沒有的話,調用這個函數再進一次找設備的過程,和上面的selectDevice函數是一樣的
disp_drv_init_ctrl_if();//和上面的一樣是在初始化control接口

switch(lcm_params->type)//下面是在根據我們的lcm的類型,取出disp_drv的值,這裏我們會將我們屬於哪一種模式的Lcm就將DISP_DRIVER這個結構賦值過去,這個代碼的路徑是在/mediatek/source/kernel/drivers/video/這個路徑下面有每一種模式的結構的定義。
{
caseLCM_TYPE_DBI : disp_drv = DISP_GetDriverDBI(); break;
caseLCM_TYPE_DPI : disp_drv = DISP_GetDriverDPI(); break;
caseLCM_TYPE_DSI : disp_drv = DISP_GetDriverDSI(); break;
default: ASSERT(0);
}

if(!disp_drv) return FALSE;

returnTRUE;
}
到這裏的話,我們的DISP_SelectDevice這個函數就已經講解完了。那麼我們的mtk_find_lcm_driver這個函數也就講解完了。
在這裏我們就返回到mtkfb.c中的Probe函數了,講解下面的內容。
接下來:
MTK_FB_XRES = DISP_GetScreenWidth();
MTK_FB_YRES =DISP_GetScreenHeight();
fb_xres_update= MTK_FB_XRES;
fb_yres_update= MTK_FB_YRES;

printk("[MTKFB]XRES=%d, YRES=%d\n", MTK_FB_XRES, MTK_FB_YRES);

MTK_FB_BPP =DISP_GetScreenBpp();
MTK_FB_PAGES =DISP_GetPages();
init_waitqueue_head(&screen_update_wq);
上面是獲得mtkfb的參數和創建等待隊列。
screen_update_task= kthread_create(
screen_update_kthread,NULL, "screen_update_kthread");//創建一個線程,這個線程根據名字可以大概的猜測出來是用來update屏幕的內容的。
L既然看到了這個線程的話,那麼就看下這個線程到底在幹嘛吧?
screen_update_kthread:屏幕刷新的線程
screen_update_kthread
staticint screen_update_kthread(void *data)
{
structsched_param param = { .sched_priority = RTPM_PRIO_SCRN_UPDATE};//設置線程的優先級,下面會用到
sched_setscheduler(current,SCHED_RR, &para;m);設置當前線程的優先級別
for( ;; ) {
wait_event_interruptible(screen_update_wq,atomic_read(&has_pending_update));//這裏是一個for的無限循環,我們將當前線程進入screen_update_wq等待隊列,這個時候我們就會進入睡眠,既然有進入睡眠的函數,那麼就有地方將他喚醒,在下面我們會講解喚醒的函數。並且設置autio機制,只能被一個線程佔有。
MTKFB_LOG("wqwakeup\n");
mtkfb_update_screen_impl();一旦這個線程被喚醒的話,那麼就會調用這個函數。

atomic_set(&has_pending_update,0);
if (kthread_should_stop())
break;
}

return 0;
}
3.1mtkfb_update_screen_impl
mtkfb_update_screen_impl
staticvoid mtkfb_update_screen_impl(void)
{
BOOLdown_sem = FALSE;
MTKFB_FUNC();
//printk("\n\n\n\n mtkfb_update_screen_impl in init current->pid\n\n\n\n",current->pid);

if(down_interruptible(&sem_overlay_buffer)){//嘗試獲得信號量,如果獲得不成功的話,那麼就進入睡眠
printk("[FB Driver]can't get semaphore in mtkfb_update_screen_impl()\n");
}
else{
down_sem= TRUE;
sem_overlay_buffer_cnt--;
}

#ifdefined(MTK_M4U_SUPPORT)
{
unsigned int i;
/// check if the MVAaddress is invalid, turn off the layer
for(i=0;i<FB_LAYER;i++){
if(LCD_IsLayerEnable((LCD_LAYER_ID)i)){
if(!MTKFB_SearchMVA(LCD_LayerGetAddress(i))){
LCD_LayerEnable((LCD_LAYER_ID)i,FALSE);
}
}
}
}
#endif

DISP_CHECK_RET(DISP_UpdateScreen(0,0, fb_xres_update, fb_yres_update));//調用DISP_UpdateScreen函數進行update
if(down_sem){
sem_overlay_buffer_cnt++;
up(&sem_overlay_buffer);
}
}
3.1.2:DISP_STATUSDISP_UpdateScreen
DISP_STATUSDISP_UpdateScreen(UINT32 x, UINT32 y, UINT32 width, UINT32 height)
{
DISP_LOG("updatescreen, (%d,%d),(%d,%d)\n", x, y, width, height);
if(down_interruptible(&sem_update_screen)) {
printk("ERROR:Can't get sem_update_screen in DISP_UpdateScreen()\n");
returnDISP_STATUS_ERROR;
}
#ifdefined(MTK_LCD_HW_3D_SUPPORT)//判斷我們的屏幕是否支持3D效果
LCD_CHECK_RET(DISP_Set3DPWM(DISP_Is3DEnabled(), DISP_is3DLandscapeMode() ));
#endif
//if LCM is powered down, LCD would never recieve the TE signal
//
if(is_lcm_in_suspend_mode || is_engine_in_suspend_mode) gotoEnd;//判斷有沒有進入suspend狀態下

LCD_CHECK_RET(LCD_WaitForNotBusy());
if(lcm_params->type==LCM_TYPE_DSI&& lcm_params->dsi.mode == CMD_MODE)
DSI_CHECK_RET(DSI_WaitForNotBusy());

if(lcm_drv->update) {//調用lcm_drv.update函數進行更新參數,在下面會進行講解
lcm_drv->update(x,y, width, height);
}

LCD_CHECK_RET(LCD_SetRoiWindow(x,y, width, height));//設置屏幕的顯示窗口的大小

LCD_CHECK_RET(LCD_FBSetStartCoord(x,y));

if(-1 != direct_link_layer) {
//MT6516IDP_EnableDirectLink(); // FIXME
}else {
disp_drv->update_screen();//調用對應的displaydriver的update_screen函數在下面會進行講解。
}

End:
up(&sem_update_screen);

returnDISP_STATUS_OK;
}
3.1.2.1:lcm_update
.update = lcm_update,
staticvoid lcm_update(unsigned int x, unsigned int y,
unsignedint width, unsigned int height)
{
unsignedint x0 = x;
unsignedint y0 = y;
unsignedint x1 = x0 + width - 1;
unsignedint y1 = y0 + height - 1;

unsignedchar x0_MSB = ((x0>>8)&0xFF);
unsignedchar x0_LSB = (x0&0xFF);
unsignedchar x1_MSB = ((x1>>8)&0xFF);
unsignedchar x1_LSB = (x1&0xFF);
unsignedchar y0_MSB = ((y0>>8)&0xFF);
unsignedchar y0_LSB = (y0&0xFF);
unsignedchar y1_MSB = ((y1>>8)&0xFF);
unsignedchar y1_LSB = (y1&0xFF);

unsignedint data_array[16];

data_array[0]=0x00053902;
data_array[1]=(x1_MSB<<24)|(x0_LSB<<16)|(x0_MSB<<8)|0x2a;
data_array[2]=(x1_LSB);
data_array[3]=0x00053902;
data_array[4]=(y1_MSB<<24)|(y0_LSB<<16)|(y0_MSB<<8)|0x2b;
data_array[5]=(y1_LSB);
data_array[6]=0x002c3909;
// data_array[6]=0x002c3901;
//上面是設置傳輸數據的信息,這些地址是寫死的,當有數據的時候,我們就會觸發DMA中斷,DMA直接將數據放到目的地,我們就會將數據顯示到LCD上面
dsi_set_cmdq(data_array,7, 0);//最終調用到dsi_set_cmdq函數

}
看下dsi_set_cmdq函數的實現:
#definedsi_set_cmdq(pdata, queue_size,force_update) lcm_util.dsi_set_cmdq(pdata, queue_size, force_update)
調用的是lcm_util.dsi_set_cmdq函數。
在disp_drv.c裏面
.dsi_set_cmdq =(void (*)(unsigned int *, unsigned int, unsigned char))DSI_set_cmdq
上面的kthread_create函數只是在創建一個新的線程,但是這個線程不會立刻執行,需要調用wake_up_process函數,這個線程纔會真正的進行執行,但是當執行的時候,我們會執行wait_event_interruptible(screen_update_wq,atomic_read(&has_pending_update));這個函數,如果atomic_read(&has_pending_update)這個contion不是true的話,那麼就會將當前的線程加入screen_update_wq這個等待隊列。所以這個線程又會進入睡眠了,不知道你們有沒有想到如果進入睡眠的話,那麼是誰在喚醒這個線程呢??對了就是我們在進面申請的中斷,這個中斷處理函數會進行喚醒這個線程將LCD進行update數據的動作。
4LCD中斷和Lcm_update函數之間的銜接
4.1:在上面的代碼中我們講解了kthread_create函數創建的線程在幹嘛?下面繼續我們的代碼執行過程。
wake_up_process(screen_update_task);

{
///registerLCD complete interrupt callback
DISP_INTERRUPT_CALLBACK_STRUCTcbStruct;
cbStruct.pFunc= mtkfb_lcd_complete_interrupt;//爲cbStruct.pFunc成員進行賦值,下面會使用這個裏面的成員
cbStruct.pParam= NULL;
///regster callback
if(DISP_STATUS_OK !=DISP_SetInterruptCallback(DISP_LCD_TRANSFER_COMPLETE_INT,&cbStruct))//4.1.1會進行講解這個函數在幹嘛?,我們會將傻瓜你買你的cdStruct變量傳遞給這個函數
{
ASSERT(0);
}
}
4.1.1DISP_SetInterruptCallback(DISP_LCD_TRANSFER_COMPLETE_INT,&cbStruct)
DISP_STATUSDISP_SetInterruptCallback(DISP_INTERRUPT_EVENTS eventID,DISP_INTERRUPT_CALLBACK_STRUCT *pCBStruct)
{
UINT32 offset;
ASSERT(pCBStruct != NULL);
disp_drv_init_context();//這個函數我們在前面的代碼中已經講解過了,這裏就不再講解了

if(eventID >=DISP_LCD_INTERRUPT_EVENTS_START && eventID <=DISP_LCD_INTERRUPT_EVENTS_END )
//我們會根據我們設置的id的不同。而進行不同的case的執行,我只講解一種case,執行流程是一樣的
{
///register callback
offset = eventID -DISP_LCD_INTERRUPT_EVENTS_START;
DISP_CallbackArray[offset].pFunc= pCBStruct->pFunc;//根據我們的不同的Id,我們爲DISP_CallbackArray數組裏面的成員賦值
DISP_CallbackArray[offset].pParam= pCBStruct->pParam;
LCD_CHECK_RET(LCD_SetInterruptCallback(_—DISP_InterruptCallbackProxy));//看下這個函數在做什麼?LCD_STATUSLCD_SetInterruptCallback(void (*pCB)(DISP_INTERRUPT_EVENTS))
{ lcdContext.pIntCallback= pCB;

returnLCD_STATUS_OK;
}
//上面其實是將——DISP_InterruptCallbackProxy函數賦值給lcdContext.pIntCallback函數,這個函數我們會在LCD的中斷處理函數中進行調用,下面我們看下這個函數到底在幹嘛
LCD_CHECK_RET(LCD_EnableInterrupt(eventID));
}
else if(eventID >=DISP_DSI_INTERRUPT_EVENTS_START && eventID <=DISP_DSI_INTERRUPT_EVENTS_END )
{
///register callback
offset = eventID -DISP_DSI_INTERRUPT_EVENTS_START + DISP_LCD_INTERRUPT_EVENTS_NUMBER;
DISP_CallbackArray[offset].pFunc= pCBStruct->pFunc;
DISP_CallbackArray[offset].pParam= pCBStruct->pParam;

DSI_CHECK_RET(DSI_SetInterruptCallback(_DISP_InterruptCallbackProxy));
DSI_CHECK_RET(DSI_EnableInterrupt(eventID));//根據我們的event的類型不同,我們去控制我們的LCD的寄存器
}
else if(eventID >=DISP_DPI_INTERRUPT_EVENTS_START && eventID <=DISP_DPI_INTERRUPT_EVENTS_END )
{
offset = eventID -DISP_DPI_INTERRUPT_EVENTS_START + DISP_LCD_INTERRUPT_EVENTS_NUMBER +DISP_DSI_INTERRUPT_EVENTS_NUMBER;
DISP_CallbackArray[offset].pFunc= pCBStruct->pFunc;
DISP_CallbackArray[offset].pParam= pCBStruct->pParam;
DPI_CHECK_RET(DPI_SetInterruptCallback(_DISP_InterruptCallbackProxy));
DPI_CHECK_RET(DPI_EnableInterrupt(eventID));
}
else
{
DISP_LOG("Invalidevent id: %d\n", eventID);
ASSERT(0);
return DISP_STATUS_ERROR; ///TODO: error log
}
return DISP_STATUS_OK;
}
4.1.1.1_DISP_InterruptCallbackProxy
staticvoid _DISP_InterruptCallbackProxy(DISP_INTERRUPT_EVENTS eventID)
{
UINT32 offset;

if(eventID >=DISP_LCD_INTERRUPT_EVENTS_START && eventID <=DISP_LCD_INTERRUPT_EVENTS_END )
{
offset = eventID -DISP_LCD_INTERRUPT_EVENTS_START;
if(DISP_CallbackArray[offset].pFunc)
{
DISP_CallbackArray[offset].pFunc(DISP_CallbackArray[offset].pParam);
}
}
…...................
…..................
}
看到沒有,我們會根據Id的值從DISP_CallbackArray數組中取對應的pFun函數,這個函數也就是cbStruct.pFunc =mtkfb_lcd_complete_interrupt;
cbStruct.pParam = NULL;
以上的函數和參數,再執行這個函數。
4.1.1.2:mtkfb_lcd_complete_interrupt
mtkfb_lcd_complete_interrupt:
staticvoid mtkfb_lcd_complete_interrupt(void *param)
{
if(atomic_read(&has_pending_update))
{
wake_up_interruptible(&screen_update_wq);//這裏會進行喚醒我們在上面創建更新LCD的線程,
}

#ifdefined(MTK_HDMI_SUPPORT)
hdmi_source_buffer_switch();
if(is_hdmi_active())
{
hdmi_update();
}
#endif
到這裏還沒有將我們的Interrupt函數聯繫以來。下面來聯繫:
5:LCD的中斷處理函數
我們再次回到/mediatek/platform/mt6577/kernel/driver/video/lcd_drv.c裏面
request_irq(MT6577_LCD_IRQ_ID,_LCD_InterruptHandler,IRQF_TRIGGER_LOW, "mtklcd", NULL)
回顧下中斷處理函數:
staticirqreturn_t _LCD_InterruptHandler(int irq, void *dev_id)
{
LCD_REG_INTERRUPT status =LCD_REG->INT_STATUS;

if (status.COMPLETED)
{
#ifdefCONFIG_MTPROF_APPLAUNCH // eng enable, user disable
LOG_PRINT(ANDROID_LOG_INFO,"AppLaunch", "LCD frame buffer update done !\n");
#endif
wake_up_interruptible(&_lcd_wait_queue);

if(_lcdContext.pIntCallback)
_lcdContext.pIntCallback(DISP_LCD_TRANSFER_COMPLETE_INT);

DBG_OnLcdDone();
}

if (status.SYNC)// this is TEmode 0 interrupt
{
if(_lcdContext.pIntCallback)
_lcdContext.pIntCallback(DISP_LCD_SYNC_INT);
DBG_OnTeDelayDone();
lcd_esd_check= false;
}
#if0 //TE mode 1
if(status.HTT)
{
if(_lcdContext.pIntCallback)
_lcdContext.pIntCallback(DISP_LCD_HTT_INT);//看到沒有,我們這裏就會調用pINtCallback函數,也就是我們在上面的mtkfb_lcd_complete_interrupt函數,只有調用了這個函數,那麼我們的lcd更新畫面的線程纔會被調用
DBG_OnTeDelayDone();
}
#endif
LCD_OUTREG32(&LCD_REG->INT_STATUS,0);
return IRQ_HANDLED;
}
#endif
到目前爲止,LCD的代碼的執行的整個過程就已經講解完了。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章