记录了Qualcomm MSM8xxx MDP4上Overlay和Framebuffer显示过程,仅涉及驱动中部分,应用层参考Overlay HAL。
Overlay设置和提交过程
msmfb_overlay_set(struct fb_info *info, void __user *p)
è mdp4_overlay_set(struct fb_info *info, struct mdp_overlay *req)
申请pipe,并设置pipe的clock和bandwidth。
int msmfb_overlay_play(struct fb_info *info, unsigned long *argp)
è int mdp4_overlay_play(struct fb_info *info, struct msmfb_overlay_data *req)
计算pipe输入缓冲地址、参数配置等;将request暂存,如对DSI CMD屏,是使用void mdp4_dsi_cmd_pipe_queue(int cndx, struct mdp4_overlay_pipe *pipe),而DSI VIDEO屏是使用void mdp4_dsi_video_pipe_queue(int cndx, struct mdp4_overlay_pipe *pipe)。
base_pipe设置和提交,Layer Mix, DMA
当送FBIOPUT_VSCREENINFO或FBIOPAN_DISPLAY命令给FB设备时,最终会调用msm_fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
msm_fb_pan_display
è mdp_set_dma_pan_info(info, dirtyPtr, (var->activate == FB_ACTIVATE_VBL)); //设置DMA区域
è mdp_dma_pan_update(info); //启动DMA,包括Layer Mixer合成和DMA输出
void mdp_dma_pan_update(struct fb_info *info)
è mfd->dma_fnc(mfd);
对MIPI DSI CMD屏,该DMA函数是mdp4_dsi_cmd_overlay。
- void mdp4_dsi_cmd_overlay(struct msm_fb_data_type *mfd)
- {
- ……
- pipe = vctrl->base_pipe;
- ……
- if (pipe->mixer_stage == MDP4_MIXER_STAGE_BASE) { // it is certain for base_pipe
- // it is setup in mdp4_overlay_update_dsi_cmd when dsi cmd panel is on.
- mdp4_mipi_vsync_enable(mfd, pipe, 0);
- mdp4_overlay_setup_pipe_addr(mfd, pipe);
- mdp4_dsi_cmd_pipe_queue(0, pipe); // arm the base pipe with framebuffer
- }
- mdp4_overlay_mdp_perf_upd(mfd, 1); // level up the mdp performance
- mutex_lock(&mfd->dma->ov_mutex);
- mdp4_dsi_cmd_pipe_commit(cndx, 0); // fire the overlay layer mixer and dma
- mdp4_overlay_mdp_perf_upd(mfd, 0);
- mutex_unlock(&mfd->dma->ov_mutex);
- }
其余相关代码引用并注释如下:
- int mdp4_dsi_cmd_pipe_commit(int cndx, int wait)
- {
- int i, undx;
- int mixer = 0;
- struct vsycn_ctrl *vctrl;
- struct vsync_update *vp;
- struct mdp4_overlay_pipe *pipe;
- struct mdp4_overlay_pipe *real_pipe;
- unsigned long flags;
- int need_dmap_wait = 0;
- int need_ov_wait = 0;
- int cnt = 0;
- /**
- *static struct vsycn_ctrl { … } vsync_ctrl_db [MAX_CONTROLLER]
- * 在不同的显示器文件中有不同的定义,注意其是static的。
- * 不同显示应用时关联不同的LayerMixer,所以同一种应用中,
- * 使用具有同一mixer_num的pipe,mixer_num由应用分配的主pipe即该应用对应的
- * vsync_ctrl_db的base_pipe指定。所有pipe的LayerMixer0是驱动中固定指定的。
- */
- vctrl = &vsync_ctrl_db[0];
- mutex_lock(&vctrl->update_lock);
- undx = vctrl->update_ndx;
- vp = &vctrl->vlist[undx];
- pipe = vctrl->base_pipe;
- mixer = pipe->mixer_num; // the Layer mixer used, LayerMixer0 here.
- if (vp->update_cnt == 0) {
- mutex_unlock(&vctrl->update_lock);
- return cnt;
- }
- //vctrl->update_ndx++;
- //vctrl->update_ndx &= 0x01;
- vp->update_cnt = 0; /* reset */
- if (vctrl->blt_free) {
- vctrl->blt_free--;
- if (vctrl->blt_free == 0)
- mdp4_free_writeback_buf(vctrl->mfd, mixer);
- }
- mutex_unlock(&vctrl->update_lock);
- /* free previous committed iommu back to pool */
- mdp4_overlay_iommu_unmap_freelist(mixer);
- spin_lock_irqsave(&vctrl->spin_lock, flags);
- if (pipe->ov_blt_addr) {
- /* Blt */
- if (vctrl->blt_wait)
- need_dmap_wait = 1;
- if (vctrl->ov_koff != vctrl->ov_done) {
- INIT_COMPLETION(vctrl->ov_comp);
- need_ov_wait = 1;
- }
- } else {
- /* direct out */
- if (vctrl->dmap_koff != vctrl->dmap_done) {
- INIT_COMPLETION(vctrl->dmap_comp);
- pr_debug("%s: wait, ok=%d od=%d dk=%d dd=%d cpu=%d\n",
- __func__, vctrl->ov_koff, vctrl->ov_done,
- vctrl->dmap_koff, vctrl->dmap_done, smp_processor_id());
- need_dmap_wait = 1;
- }
- }
- spin_unlock_irqrestore(&vctrl->spin_lock, flags);
- /* setup completion for dmap wait in DIRECTOUT mode or overlay wait in BLT mode */
- if (need_dmap_wait) {
- pr_debug("%s: wait4dmap\n", __func__);
- mdp4_dsi_cmd_wait4dmap(0);
- }
- if (need_ov_wait) {
- pr_debug("%s: wait4ov\n", __func__);
- mdp4_dsi_cmd_wait4ov(0);
- }
- if (pipe->ov_blt_addr) {
- if (vctrl->blt_end) {
- vctrl->blt_end = 0;
- pipe->ov_blt_addr = 0;
- pipe->dma_blt_addr = 0;
- pr_info("%s: reset ov_blt_addr and dma_blt_addr\n", __func__);
- }
- }
- /* if blt has some change, reconfig overlay proccessor and dmap*/
- if (vctrl->blt_change) {
- mdp4_overlayproc_cfg(pipe);
- mdp4_overlay_dmap_xy(pipe);
- vctrl->blt_change = 0;
- }
- pipe = vp->plist;
- /*
- * All pipes used here is targeted to LayerMixer0.
- * These pipes are allocated with MIXER0 indeed,
- * and queued in vctrl->vlist[undx].plist[pipe->pipe_ndx - 1] again.
- * Obvious with target to MIXER0.
- */
- for (i = 0; i < OVERLAY_PIPE_MAX; i++, pipe++) {
- if (pipe->pipe_used) {
- /* pipes in vp->plist maybe point to same physical pipe */
- cnt++;
- real_pipe = mdp4_overlay_ndx2pipe(pipe->pipe_ndx);
- if (real_pipe && real_pipe->pipe_used) {
- /* pipe not unset */
- mdp4_overlay_vsync_commit(pipe);
- }
- /* free previous iommu to freelist
- * which will be freed at next
- * pipe_commit
- */
- mdp4_overlay_iommu_pipe_free(pipe->pipe_ndx, 0);
- pipe->pipe_used = 0; /* clear */
- }
- }
- /* tx dcs command if had any */
- mipi_dsi_cmdlist_commit(1);
- /* mixer here is MIXER0. Setup mixer’s each stage with pipe */
- mdp4_mixer_stage_commit(mixer);
- pipe = vctrl->base_pipe;
- spin_lock_irqsave(&vctrl->spin_lock, flags);
- if (pipe->ov_blt_addr) {
- mdp4_dsi_cmd_blt_ov_update(pipe);
- pipe->ov_cnt++;
- vctrl->ov_koff++;
- INIT_COMPLETION(vctrl->ov_comp);
- vsync_irq_enable(INTR_OVERLAY0_DONE, MDP_OVERLAY0_TERM);
- } else {
- INIT_COMPLETION(vctrl->dmap_comp);
- vsync_irq_enable(INTR_DMA_P_DONE, MDP_DMAP_TERM);
- vctrl->dmap_koff++;
- }
- pr_debug("%s: kickoff, pid=%d\n", __func__, current->pid);
- /* kickoff overlay engine */
- mdp4_stat.kickoff_ov0++;
- outpdw(MDP_BASE + 0x0004, 0);
- mb();
- spin_unlock_irqrestore(&vctrl->spin_lock, flags);
- mdp4_stat.overlay_commit[pipe->mixer_num]++;
- /* wait for vsync */
- if (wait) {
- long long tick;
- mdp4_dsi_cmd_wait4vsync(cndx, &tick);
- }
- return cnt;
- }
- void mdp4_overlay_vsync_commit(struct mdp4_overlay_pipe *pipe)
- {
- if (pipe->pipe_type == OVERLAY_TYPE_VIDEO)
- mdp4_overlay_vg_setup(pipe); /* video/graphic pipe */
- else
- mdp4_overlay_rgb_setup(pipe); /* rgb pipe */
- pr_debug("%s: pipe=%x ndx=%d num=%d used=%d\n", __func__,
- (int) pipe, pipe->pipe_ndx, pipe->pipe_num, pipe->pipe_used);
- /* figure out the flush value to fill register */
- mdp4_overlay_reg_flush(pipe, 1);
- /* stage setup but not commit */
- mdp4_mixer_stage_up(pipe, 0);
- }
- void mdp4_mixer_stage_commit(int mixer)
- {
- struct mdp4_overlay_pipe *pipe;
- int i, num;
- u32 data, stage;
- int off;
- unsigned long flags;
- data = 0;
- for (i = MDP4_MIXER_STAGE_BASE; i < MDP4_MIXER_STAGE_MAX; i++) {
- pipe = ctrl->stage[mixer][i];
- if (pipe == NULL)
- continue;
- pr_debug("%s: mixer=%d ndx=%d stage=%d\n", __func__,
- mixer, pipe->pipe_ndx, i);
- stage = pipe->mixer_stage;
- if (mixer >= MDP4_MIXER1)
- stage += 8;
- stage <<= (4 * pipe->pipe_num);
- data |= stage;
- }
- /*
- * stage_commit may be called from overlay_unset
- * for command panel, mdp clocks may be off at this time.
- * so mdp clock enabled is necessary
- */
- mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
- mdp_clk_ctrl(1);
- mdp4_mixer_blend_setup(mixer);
- off = 0;
- if (data != ctrl->mixer_cfg[mixer]) {
- ctrl->mixer_cfg[mixer] = data;
- if (mixer >= MDP4_MIXER2) {
- /* MDP_LAYERMIXER2_IN_CFG */
- off = 0x100f0;
- } else {
- /* mixer 0 or 1 */
- num = mixer + 1;
- num &= 0x01;
- data |= ctrl->mixer_cfg[num];
- off = 0x10100;
- }
- pr_debug("%s: mixer=%d data=%x flush=%x pid=%d\n", __func__,
- mixer, data, ctrl->flush[mixer], current->pid);
- }
- local_irq_save(flags);
- if (off)
- outpdw(MDP_BASE + off, data);
- if (ctrl->flush[mixer]) {
- outpdw(MDP_BASE + 0x18000, ctrl->flush[mixer]);
- ctrl->flush[mixer] = 0;
- }
- local_irq_restore(flags);
- mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
- mdp_clk_ctrl(0);
- }