记录了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); }