Qcom MDP 之一 -- 总体架构

 
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);           


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值