LCD 驅動過程詳解

                                                                                                    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函數的規則是devicedriver的名字必須相同,當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變量,進行參數的取值。

2DISP_IsContextInited這個函數判斷lcm_paramsdisp_drvlcm_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.1LCD_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_CFGregs->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[i];


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等於1status是一樣的,參考上面就可以了。


disp_drv_init_ctrl_if();//初始化dispalycontrolinterface有串口和並口等

disp_drv_set_driving_current(lcm_params);//從函數的命名定義上面,我們是在設置lcd的電流,但是我根據地址查詢mediateksocdatasheet並沒有找到相關的定義。

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_nameNULL;並且Lcm_list又不止一個的話,那麼就會調用沒一個lcmcompare_id函數,這個函數是我們lcm裏面實現好的,我這裏用的是r61408這個IC屏,我們就看看這個lcmcompare_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, &param);設置當前線程的優先級別

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();//調用對應的displaydriverupdate_screen函數在下面會進行講解。

}


End:

up(&sem_update_screen);


returnDISP_STATUS_OK;

}

3.1.2.1lcm_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函數聯繫以來。下面來聯繫:

5LCD的中斷處理函數

我們再次回到/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的代碼的執行的整個過程就已經講解完了。



發佈了18 篇原創文章 · 獲贊 11 · 訪問量 37萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章