概述
接下来:
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=%dn", 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, ¶m);设置当前线程的优先级别
for( ;; ) {
wait_event_interruptible(screen_update_wq,atomic_read(&has_pending_update));//这里是一个for的无限循环,我们将当前线程进入screen_update_wq等待队列,这个时候我们就会进入睡眠,既然有进入睡眠的函数,那么就有地方将他唤醒,在下面我们会讲解唤醒的函数。并且设置autio机制,只能被一个线程占有。
MTKFB_LOG("wqwakeupn");
mtkfb_update_screen_impl();一旦这个线程被唤醒的话,那么就会调用这个函数。
atomic_set(&has_pending_update,0);
if (kthread_should_stop())
break;
}
return 0;
}
3.1:mtkfb_update_screen_impl
mtkfb_update_screen_impl
staticvoid mtkfb_update_screen_impl(void)
{
BOOLdown_sem = FALSE;
MTKFB_FUNC();
//printk("nnnn mtkfb_update_screen_impl in init current->pidnnnn",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
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数据的动作。
4、LCD中断和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.1:DISP_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: %dn", 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的代码的执行的整个过程就已经讲解完了。
最后
以上就是优秀康乃馨为你收集整理的MTK平台LCM驱动详细分析(二)的全部内容,希望文章能够帮你解决MTK平台LCM驱动详细分析(二)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复