1、如何看亮灭屏时间
adb shell
kmsgcat |grep fb_blank
2、code简介
在kernel/drivers/video/msm/mdss/mdss_fb.c中,
static intmdss_fb_blank_sub(int blank_mode, struct fb_info *info, int op_enable)
blank_mode这个参数的值主要有两个,FB_BLANK_UNBLANK和 FB_BLANK_POWERDOWN ,
FB_BLANK_UNBLANK是亮屏操作;
FB_BLANK_POWERDOWN 是灭屏操作;
当要亮屏的时候,传入参数FB_BLANK_UNBLANK时,会执行下面这些函数,
if (!mfd->panel_power_on &&mfd->mdp.on_fnc) {
ret = mfd->mdp.on_fnc(mfd);
if (ret == 0) {
mfd->panel_power_on = true;
mfd->panel_info->panel_dead= false;
}
mutex_lock(&mfd->update.lock);
mfd->update.type =NOTIFY_TYPE_UPDATE;
mfd->update.is_suspend = 0;
mutex_unlock(&mfd->update.lock);
/* Start the work thread tosignal idle time */
if (mfd->idle_time)
schedule_delayed_work(&mfd->idle_notify_work,
msecs_to_jiffies(mfd->idle_time));
}
mutex_lock(&mfd->bl_lock);
if (!mfd->bl_updated) {
mfd->bl_updated = 1;
mdss_fb_set_backlight(mfd,mfd->bl_level_prev_scaled);
}
mutex_unlock(&mfd->bl_lock);
}
当执行ret =mfd->mdp.on_fnc(mfd);会调用(上一篇文章http://blog.csdn.net/liwei16611/article/details/52830483)注册的函数mdss_mdp_overlay_on;
在mdss_mdp_overlay_on函数中,
staticint mdss_mdp_overlay_on(struct msm_fb_data_type *mfd)
{
..................
if (!mdp5_data->ctl) {
ctl =__mdss_mdp_overlay_ctl_init(mfd);
if (IS_ERR_OR_NULL(ctl))
return PTR_ERR(ctl);
mdp5_data->ctl = ctl;
}
if (!mfd->panel_info->cont_splash_enabled&&
(mfd->panel_info->type !=DTV_PANEL)) {
rc =mdss_mdp_overlay_start(mfd);
if (!IS_ERR_VALUE(rc)&&
(mfd->panel_info->type!= WRITEBACK_PANEL)) {
atomic_inc(&mfd->mdp_sync_pt_data.commit_cnt);
rc= mdss_mdp_overlay_kickoff(mfd, NULL);
}
} else {
rc =mdss_mdp_ctl_setup(mdp5_data->ctl);
if (rc)
return rc;
}
......
}
在这里我们看到如果mdp5_data->ctl 还没有被初始化,则对他进行初始化,实际上在fb_probe 已经被初始化了, 那在这判断的作用,是为了如果有动态切换的话, mdp5_data->ctl 会被置成NULL,当要亮屏的时候回去重新初始化。
如果连续显示打开的话则mfd->panel_info->cont_splash_enabled会被置1,那么就不去执行mdss_mdp_overlay_start,因为它在fb_probe中已经被执行了。
执行mdss_mdp_ctl_setup会对pipe 及Mixer 进行一些配置,为传入的帧数据做准备。
当要显示数据的帧的时候,会从hal 层传入commit的命令,调用 mdss_mdp_display_commit 函数,然后调用display_fnc,
这个回调函数是在mdss_mdp_video_start中注册的;
ctl->display_fnc= mdss_mdp_video_display;
在mdss_mdp_video_display函数中,
staticint mdss_mdp_video_display(struct mdss_mdp_ctl *ctl, void *arg)
{
......................
if(!ctx->timegen_en) {
rc =mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_UNBLANK, NULL);
if (rc) {
pr_warn("intf #%dunblank error (%d)\n",
ctl->intf_num,rc);
video_vsync_irq_disable(ctl);
ctx->wait_pending =0;
return rc;
}
pr_debug("enabling timinggen for intf=%d\n", ctl->intf_num);
if((pdata->panel_info.cont_splash_enabled &&
!ctl->mfd->splash_info.splash_logo_enabled)
||(ctl->mfd->splash_info.splash_logo_enabled
&&!is_mdss_iommu_attached())) {
rc =wait_for_completion_timeout(&ctx->vsync_comp,
usecs_to_jiffies(VSYNC_TIMEOUT_US));
}
rc = mdss_iommu_ctrl(1);
if (IS_ERR_VALUE(rc)) {
pr_err("IOMMUattach failed\n");
return rc;
}
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON,false);
mdss_mdp_irq_enable(MDSS_MDP_IRQ_INTF_UNDER_RUN, ctl->intf_num);
mdss_bus_bandwidth_ctrl(true);
mdp_video_write(ctx,MDSS_MDP_REG_INTF_TIMING_ENGINE_EN, 1);
wmb();
rc = wait_for_completion_timeout(&ctx->vsync_comp,
usecs_to_jiffies(VSYNC_TIMEOUT_US));
WARN(rc == 0, "timeout(%d) enabling timegen on ctl=%d\n",
rc, ctl->num);
ctx->timegen_en = true;
rc =mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_PANEL_ON, NULL);
WARN(rc,"intf %d panel on error (%d)\n", ctl->intf_num, rc);
......................
}
通过mdss_mdp_ctl_intf_event(ctl,MDSS_EVENT_UNBLANK, NULL); 发送一个event事件,
dsi_event_handler 会接收到这个事件,去执行dsi_on,mdss_dsi_op_mode_config;
之后通过mdss_mdp_ctl_intf_event(ctl,MDSS_EVENT_PANEL_ON, NULL);
dsi_event_handler会接收到这个事件,去执行mdss_dsi_unblank;
当要灭屏的时候,从hal层会发送stop命令,当驱动接收到这个命令会执行mdss_mdp_video_stop函数;
staticint mdss_mdp_video_stop(struct mdss_mdp_ctl *ctl)
{
rc = mdss_mdp_ctl_intf_event(ctl,MDSS_EVENT_BLANK, NULL);
if (rc == -EBUSY) {
pr_debug("intf #%dbusy don't turn off\n",
ctl->intf_num);
return rc;
}
WARN(rc, "intf %d blankerror (%d)\n", ctl->intf_num, rc);
mdp_video_write(ctx,MDSS_MDP_REG_INTF_TIMING_ENGINE_EN, 0);
/* wait for at least one VSYNCon HDMI intf for proper TG OFF */
if (MDSS_INTF_HDMI ==ctx->intf_type) {
frame_rate =mdss_panel_get_framerate
(&(ctl->panel_data->panel_info));
if (!(frame_rate >= 24&& frame_rate <= 240))
frame_rate = 24;
msleep((1000/frame_rate)+ 1);
}
mdss_iommu_ctrl(0);
mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF,false);
ctx->timegen_en = false;
rc =mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_PANEL_OFF, NULL);
WARN(rc, "intf %dtimegen off error (%d)\n", ctl->intf_num, rc);
mdss_mdp_irq_disable(MDSS_MDP_IRQ_INTF_UNDER_RUN,
ctl->intf_num);
sctl =mdss_mdp_get_split_ctl(ctl);
if (sctl)
mdss_mdp_irq_disable(MDSS_MDP_IRQ_INTF_UNDER_RUN, sctl->intf_num);
mdss_bus_bandwidth_ctrl(false);
----------------------
}
通过mdss_mdp_ctl_intf_event(ctl,MDSS_EVENT_BLANK, NULL);发送灭屏的事件,
dsi_event_handler会接收到这个事件,会去执行mdss_dsi_blank。
通过mdss_mdp_ctl_intf_event(ctl,MDSS_EVENT_PANEL_OFF, NULL);发送事件,同样dsi_event_handler 会接收到这个事件,会去执行mdss_dsi_off。
最后在mdss_fb_suspend_sub中发送FB_BLANK_POWERDOWN,最后屏熄灭。