struct mdp_dma_data {
boolean busy;
boolean waiting;
struct mutex ov_mutex;
struct semaphore mutex;
struct completion comp;
};
static struct mdp_dma_data dma2_data;
static struct mdp_dma_data dma_s_data;
static struct mdp_dma_data dma_e_data;
static struct mdp_dma_data dma3_data;
static int mdp_probe(struct platform_device *pdev)
+-- if ((pdev->id == 0) && (pdev->num_resources > 0)) {
mdp_pdata = pdev->dev.platform_data;
size = resource_size(&pdev->resource[0]);
msm_mdp_base = ioremap(pdev->resource[0].start, size);
rc = mdp_irq_clk_setup(); //这里会注册INT_MDP中断
mdp_hw_init();
mdp_resource_initialized = 1;
return 0;
}
+-- ...
static int mdp_irq_clk_setup(void)
?-- ret = request_irq(INT_MDP, mdp4_isr, IRQF_DISABLED, "MDP", 0);
?-- ret = request_irq(INT_MDP, mdp_isr, IRQF_DISABLED, "MDP", 0); //INT_MDP --> mdp_isr
+-- disable_irq(INT_MDP);
+-- mdp_clk = clk_get(NULL, "mdp_clk"); //主时钟
+-- mdp_pclk = clk_get(NULL, "mdp_pclk"); //像素时钟
//mdp_clk should greater than mdp_pclk always
+-- if (mdp_pdata && mdp_pdata->mdp_core_clk_rate)
clk_set_rate(mdp_clk, mdp_pdata->mdp_core_clk_rate);
@arch/arm/mach-msm/include/mach/irqs-7x30.h
#define INT_MDP (64 + 16)
irqreturn_t
mdp_isr(int irq, void *ptr)
{
uint32 mdp_interrupt = 0;
struct mdp_dma_data *dma;
mdp_is_in_isr = TRUE;
do {
mdp_interrupt = inp32(MDP_INTR_STATUS);
outp32(MDP_INTR_CLEAR, mdp_interrupt);
mdp_interrupt &= mdp_intr_mask;
if (mdp_interrupt & TV_ENC_UNDERRUN) {
mdp_interrupt &= ~(TV_ENC_UNDERRUN);
mdp_tv_underflow_cnt++;
}
if (!mdp_interrupt)
break;
...
/* LCDC UnderFlow */
if (mdp_interrupt & LCDC_UNDERFLOW) {
mdp_lcdc_underflow_cnt++;
/*when underflow happens HW resets all the histogram
registers that were set before so restore them back
to normal.*/
MDP_OUTP(MDP_BASE + 0x94010, 1);
MDP_OUTP(MDP_BASE + 0x9401c, 2);
}
/* LCDC Frame Start */
if (mdp_interrupt & LCDC_FRAME_START) {
/* let's disable LCDC interrupt */
mdp_intr_mask &= ~LCDC_FRAME_START;
outp32(MDP_INTR_ENABLE, mdp_intr_mask);
dma = &dma2_data;
if (dma->waiting) {
dma->waiting = FALSE;
complete(&dma->comp); //通知等待&dma->comp的进程
}
}
/* DMA2 LCD-Out Complete */
if (mdp_interrupt & MDP_DMA_S_DONE) {
dma = &dma_s_data;
dma->busy = FALSE;
mdp_pipe_ctrl(MDP_DMA_S_BLOCK, MDP_BLOCK_POWER_OFF,
TRUE);
complete(&dma->comp);
}
...
} while (1);
mdp_is_in_isr = FALSE;
return IRQ_HANDLED;
}
//那么又是谁在等待&dma->comp这个completion呢? 可以在不同的文件中查找到下列函数:
//@drivers/video/msm/mdp_dma.c
static void mdp_dma2_update_sub(struct msm_fb_data_type *mfd)
void mdp_dma2_update(struct msm_fb_data_type *mfd)
//@drivers/video/msm/mdp_dma_lcdc.c
void mdp_lcdc_update(struct msm_fb_data_type *mfd)
//@drivers/video/msm/mdp_dma_s.c
void mdp_dma_s_update(struct msm_fb_data_type *mfd)
//@drivers/video/msm/mdp_dma_tv.c
void mdp_dma3_update(struct msm_fb_data_type *mfd)
//这些函数都调用了同一个函数来等待&dma->comp这个completion:
//wait_for_completion_killable(&mfd->dma->comp);
//而在mdp_probe()中,会根据panel_type将mfd->dma_func()初始化为这些函数其中之一,其参数mfd->dma也将进行相应的初始化
static int
mdp_probe(struct platform_device *pdev)
+-- switch (mfd->panel.type) {
case EXT_MDDI_PANEL:
case MDDI_PANEL:
case EBI2_PANEL:
INIT_WORK(&mfd->dma_update_worker, mdp_lcd_update_workqueue_handler); //这里初始化dma_update_worker
INIT_WORK(&mfd->vsync_resync_worker, mdp_vsync_resync_workqueue_handler);
mfd->hw_refresh = FALSE;
...
if (mfd->panel_info.pdest == DISPLAY_1) {
mfd->dma_fnc = mdp4_mddi_overlay;
...
mfd->dma = &dma2_data;
...
} else {
mfd->dma_fnc = mdp_dma_s_update;
mfd->dma = &dma_s_data;
}
...
mdp_config_vsync(mfd);
break;
...
case HDMI_PANEL:
case LCDC_PANEL:
...
#ifdef CONFIG_FB_MSM_OVERLAY
mfd->dma_fnc = mdp4_lcdc_overlay;
#else
mfd->dma_fnc = mdp_lcdc_update;
#endif
mfd->dma = &dma2_data;
...
...
}
+-- ...
//由此综合可以得出结论,等待&dma->comp这个completion的函数对上的接口统一为:mfd->dma_func()
//而mfd->dma_func()只有两个函数调用,再往上走可以得到两个不同的调用路径
void mdp_lcd_update_workqueue_handler(struct work_struct *work) //EXT_MDDI&MDDI&EBI2_PANEL
void mdp_dma_pan_update(struct fb_info *info) //HDMI&LCDC
+-- mfd->dma_fnc(mfd);
//这两个调用路径最终归结到fb_ops.fb_fillrect()和fb_ops.fb_ioctl()两个成员函数里面
static struct fb_ops msm_fb_ops = {
.owner = THIS_MODULE,
.fb_open = msm_fb_open,
.fb_release = msm_fb_release,
.fb_read = NULL,
.fb_write = NULL,
.fb_cursor = NULL,
.fb_check_var = msm_fb_check_var, /* vinfo check */
.fb_set_par = msm_fb_set_par, /* set the video mode according to info->var */
.fb_setcolreg = NULL, /* set color register */
.fb_blank = msm_fb_blank, /* blank display */
.fb_pan_display = msm_fb_pan_display, /* pan display */
.fb_fillrect = msm_fb_fillrect, // Draws a rectangle ...
.fb_copyarea = msm_fb_copyarea, /* Copy data from area to another */
.fb_imageblit = msm_fb_imageblit, /* Draws a image to the display */
.fb_rotate = NULL,
.fb_sync = NULL, /* wait for blit idle, optional */
.fb_ioctl = msm_fb_ioctl, // perform fb specific ioctl (optional)...
.fb_mmap = msm_fb_mmap,
};
//path 1 : fb_ops.fb_ioctl() >> msm_fb_ioctl()
static int msm_fb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
+-- msm_fb_resume_sw_refresher(mfd); //MSMFB_RESUME_SW_REFRESHE
+-- mdp_refresh_screen((unsigned long)mfd); //mdp_refresh_screen...
void mdp_refresh_screen(unsigned long data)
{
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)data;
if ((mfd->sw_currently_refreshing) && (mfd->sw_refreshing_enable)) {
init_timer(&mfd->refresh_timer);
mfd->refresh_timer.function = mdp_refresh_screen; //递归调用,因此,此函数的执行是周期性的
mfd->refresh_timer.data = data;
//函数执行的周期为1ms或者refresh_timer_duration
if (mfd->dma->busy)
/* come back in 1 msec */
mfd->refresh_timer.expires = jiffies + (HZ / 1000);
else
mfd->refresh_timer.expires = jiffies + mfd->refresh_timer_duration;
add_timer(&mfd->refresh_timer);
if (!mfd->dma->busy) {
//将mfd->dma_update_worker这个work挂入mdp_dma_wq工作队列.
if (!queue_work(mdp_dma_wq, &mfd->dma_update_worker)) {
MSM_FB_DEBUG("mdp_dma: can't queue_work! -> \
MDP/MDDI/LCD clock speed needs to be increased\n");
}
}
} else {
if (!mfd->hw_refresh)
complete(&mfd->refresher_comp);
}
}
//这个函数实际上就是在线程环境下周期性的执行&mfd->dma_update_worker这个work关联的函数.
//那么mfd->dma_update_worker这个work关联的函数做了什么呢? 看mdp_probe()中是如何初始化&mfd->dma_update_worker的
//mdp_probe()中,根据panel_type初始化mfd->dma_func()及其参数mfd->dma
static int mdp_probe(struct platform_device *pdev)
+-- switch (mfd->panel.type) {
case EXT_MDDI_PANEL:
case MDDI_PANEL:
case EBI2_PANEL:
INIT_WORK(&mfd->dma_update_worker, mdp_lcd_update_workqueue_handler); //这里初始化dma_update_worker
INIT_WORK(&mfd->vsync_resync_worker, mdp_vsync_resync_workqueue_handler);
...
break;
...
case HDMI_PANEL:
case LCDC_PANEL:
...
...
}
+-- ...
void mdp_lcd_update_workqueue_handler(struct work_struct *work)
+-- mfd->dma_fnc(mfd);
//path 2 : fb_ops.fb_fillrect() >> msm_fb_fillrect()
@drivers/video/msm/msm_fb.c
static void msm_fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
+-- cfb_fillrect(info, rect);
+-- var = info->var;
+-- var.reserved[0] = 0x54445055;
+-- var.reserved[1] = (rect->dy << 16) | (rect->dx);
+-- var.reserved[2] = ((rect->dy + rect->height) << 16) | (rect->dx + rect->width);
+-- msm_fb_pan_display(&var, info);
+-- mdp_set_dma_pan_info(info, dirtyPtr,(var->activate == FB_ACTIVATE_VBL));
+-- mdp_dma_pan_update(info);
+-- if (mfd->sw_currently_refreshing) {
/* we need to wait for the pending update */
mfd->pan_waiting = TRUE;
if (!mfd->ibuf_flushed) {
wait_for_completion_killable(&mfd->pan_comp);
}
/* waiting for this update to complete */
mfd->pan_waiting = TRUE;
wait_for_completion_killable(&mfd->pan_comp);
} else
mfd->dma_fnc(mfd);