Loading drivers/video/msm/mdss/mdss_dsi.c +8 −7 Original line number Diff line number Diff line Loading @@ -184,9 +184,9 @@ error: return ret; } static int mdss_dsi_panel_power_doze(struct mdss_panel_data *pdata, int enable) static int mdss_dsi_panel_power_lp(struct mdss_panel_data *pdata, int enable) { /* Panel power control when entering/exiting doze mode */ /* Panel power control when entering/exiting lp mode */ return 0; } Loading Loading @@ -223,12 +223,13 @@ static int mdss_dsi_panel_power_ctrl(struct mdss_panel_data *pdata, break; case MDSS_PANEL_POWER_ON: if (mdss_dsi_is_panel_on_lp(pdata)) ret = mdss_dsi_panel_power_doze(pdata, false); ret = mdss_dsi_panel_power_lp(pdata, false); else ret = mdss_dsi_panel_power_on(pdata); break; case MDSS_PANEL_POWER_DOZE: ret = mdss_dsi_panel_power_doze(pdata, true); case MDSS_PANEL_POWER_LP1: case MDSS_PANEL_POWER_LP2: ret = mdss_dsi_panel_power_lp(pdata, true); break; default: pr_err("%s: unknown panel power state requested (%d)\n", Loading Loading @@ -474,7 +475,7 @@ static int mdss_dsi_off(struct mdss_panel_data *pdata, int power_state) goto end; } if (power_state != MDSS_PANEL_POWER_OFF) { if (mdss_panel_is_power_on(power_state)) { pr_debug("%s: dsi_off with panel always on\n", __func__); goto panel_power_ctrl; } Loading Loading @@ -747,7 +748,7 @@ static int mdss_dsi_blank(struct mdss_panel_data *pdata, int power_state) mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 1); if (power_state == MDSS_PANEL_POWER_DOZE) { if (mdss_panel_is_power_on_lp(power_state)) { pr_debug("%s: low power state requested\n", __func__); if (ctrl_pdata->low_power_config) ret = ctrl_pdata->low_power_config(pdata, true); Loading drivers/video/msm/mdss/mdss_fb.c +129 −73 Original line number Diff line number Diff line Loading @@ -65,6 +65,10 @@ #endif #define MAX_FBI_LIST 32 #define BLANK_FLAG_LP FB_BLANK_VSYNC_SUSPEND #define BLANK_FLAG_ULP FB_BLANK_NORMAL static struct fb_info *fbi_list[MAX_FBI_LIST]; static int fbi_list_index; Loading Loading @@ -853,10 +857,11 @@ static int mdss_fb_suspend_sub(struct msm_fb_data_type *mfd) /* * Ideally, display should have either been blanked by now, or * should have transitioned to a low power state. If not, then * as a fall back option, blank the display. * as a fall back option, enter ulp state to leave the display * on, but turn off all interface clocks. */ if (mdss_fb_is_power_on_interactive(mfd)) { ret = mdss_fb_blank_sub(FB_BLANK_POWERDOWN, mfd->fbi, if (mdss_fb_is_power_on(mfd)) { ret = mdss_fb_blank_sub(BLANK_FLAG_ULP, mfd->fbi, mfd->suspend.op_enable); if (ret) { pr_err("can't turn off display!\n"); Loading Loading @@ -892,13 +897,16 @@ static int mdss_fb_resume_sub(struct msm_fb_data_type *mfd) mfd->op_enable = mfd->suspend.op_enable; /* * If the fb was explicitly blanked during suspend, then unblank it * during resume. * If the fb was explicitly blanked or transitioned to ulp during * suspend, then undo it during resume with the appropriate unblank * flag. If fb was in ulp state when entering suspend, then nothing * needs to be done. */ if (mdss_panel_is_power_on(mfd->suspend.panel_power_state)) { if (mdss_panel_is_power_on(mfd->suspend.panel_power_state) && !mdss_panel_is_power_on_ulp(mfd->suspend.panel_power_state)) { int unblank_flag = mdss_panel_is_power_on_interactive( mfd->suspend.panel_power_state) ? FB_BLANK_UNBLANK : FB_BLANK_VSYNC_SUSPEND; BLANK_FLAG_LP; ret = mdss_fb_blank_sub(unblank_flag, mfd->fbi, mfd->op_enable); if (ret) Loading Loading @@ -1179,7 +1187,61 @@ static void mdss_panel_validate_debugfs_info(struct msm_fb_data_type *mfd) } } static int mdss_fb_unblank_sub(struct msm_fb_data_type *mfd) static int mdss_fb_blank_blank(struct msm_fb_data_type *mfd, int req_power_state) { int ret = 0; int cur_power_state; if (!mfd) return -EINVAL; if (!mdss_fb_is_power_on(mfd) || !mfd->mdp.off_fnc) return 0; cur_power_state = mfd->panel_power_state; pr_debug("Transitioning from %d --> %d\n", cur_power_state, req_power_state); if (cur_power_state == req_power_state) { pr_debug("No change in power state\n"); return 0; } mutex_lock(&mfd->update.lock); mfd->update.type = NOTIFY_TYPE_SUSPEND; mfd->update.is_suspend = 1; mutex_unlock(&mfd->update.lock); complete(&mfd->update.comp); del_timer(&mfd->no_update.timer); mfd->no_update.value = NOTIFY_TYPE_SUSPEND; complete(&mfd->no_update.comp); mfd->op_enable = false; mutex_lock(&mfd->bl_lock); if (mdss_panel_is_power_off(req_power_state)) { /* Stop Display thread */ if (mfd->disp_thread) mdss_fb_stop_disp_thread(mfd); mdss_fb_set_backlight(mfd, 0); mfd->bl_updated = 0; } mfd->panel_power_state = req_power_state; mutex_unlock(&mfd->bl_lock); ret = mfd->mdp.off_fnc(mfd); if (ret) mfd->panel_power_state = cur_power_state; else if (mdss_panel_is_power_off(req_power_state)) mdss_fb_release_fences(mfd); mfd->op_enable = true; complete(&mfd->power_off_comp); return ret; } static int mdss_fb_blank_unblank(struct msm_fb_data_type *mfd) { int ret = 0; int cur_power_state; Loading @@ -1198,16 +1260,23 @@ static int mdss_fb_unblank_sub(struct msm_fb_data_type *mfd) } cur_power_state = mfd->panel_power_state; if (!mdss_panel_is_power_on_interactive(cur_power_state) && mfd->mdp.on_fnc) { pr_debug("Transitioning from %d --> %d\n", cur_power_state, MDSS_PANEL_POWER_ON); if (mdss_panel_is_power_on_interactive(cur_power_state)) { pr_debug("No change in power state\n"); return 0; } if (mfd->mdp.on_fnc) { ret = mfd->mdp.on_fnc(mfd); if (ret == 0) { mfd->panel_power_state = MDSS_PANEL_POWER_ON; mfd->panel_info->panel_dead = false; } else if (mfd->disp_thread) { if (ret) { mdss_fb_stop_disp_thread(mfd); goto error; } mfd->panel_power_state = MDSS_PANEL_POWER_ON; mfd->panel_info->panel_dead = false; mutex_lock(&mfd->update.lock); mfd->update.type = NOTIFY_TYPE_UPDATE; mfd->update.is_suspend = 0; Loading Loading @@ -1252,82 +1321,67 @@ static int mdss_fb_blank_sub(int blank_mode, struct fb_info *info, cur_power_state = mfd->panel_power_state; /* * If doze mode is requested for video mode panels, treat * the request as full unblank as there are no low power mode * settings for video mode panels. * Low power (lp) and ultra low pwoer (ulp) modes are currently only * supported for command mode panels. For all other panel, treat lp * mode as full unblank and ulp mode as full blank. */ if ((FB_BLANK_VSYNC_SUSPEND == blank_mode) && (mfd->panel_info->type != MIPI_CMD_PANEL)) { pr_debug("Doze mode only valid for cmd mode panels\n"); if (mdss_panel_is_power_on(cur_power_state)) if (mfd->panel_info->type != MIPI_CMD_PANEL) { if (BLANK_FLAG_LP == blank_mode) { pr_debug("lp mode only valid for cmd mode panels\n"); if (mdss_fb_is_power_on_interactive(mfd)) return 0; else blank_mode = FB_BLANK_UNBLANK; } else if (BLANK_FLAG_ULP == blank_mode) { pr_debug("ulp mode valid for cmd mode panels\n"); if (mdss_fb_is_power_off(mfd)) return 0; else blank_mode = FB_BLANK_POWERDOWN; } } switch (blank_mode) { case FB_BLANK_UNBLANK: pr_debug("unblank called. cur pwr state=%d\n", cur_power_state); ret = mdss_fb_unblank_sub(mfd); ret = mdss_fb_blank_unblank(mfd); break; case BLANK_FLAG_ULP: req_power_state = MDSS_PANEL_POWER_LP2; pr_debug("ultra low power mode requested\n"); if (mdss_fb_is_power_off(mfd)) { pr_debug("Unsupp transition: off --> ulp\n"); return 0; } case FB_BLANK_VSYNC_SUSPEND: req_power_state = MDSS_PANEL_POWER_DOZE; pr_debug("Doze power mode requested\n"); ret = mdss_fb_blank_blank(mfd, req_power_state); break; case BLANK_FLAG_LP: req_power_state = MDSS_PANEL_POWER_LP1; pr_debug(" power mode requested\n"); /* * If doze mode is requested when panel is already off, * then first unblank the panel before entering doze mode * If low power mode is requested when panel is already off, * then first unblank the panel before entering low power mode */ if (mdss_fb_is_power_off(mfd) && mfd->mdp.on_fnc) { pr_debug("off --> doze. switch to on first\n"); ret = mdss_fb_unblank_sub(mfd); pr_debug("off --> lp. switch to on first\n"); ret = mdss_fb_blank_unblank(mfd); if (ret) break; } /* Enter doze mode only if unblank succeeded */ if (ret) ret = mdss_fb_blank_blank(mfd, req_power_state); break; case FB_BLANK_HSYNC_SUSPEND: case FB_BLANK_NORMAL: case FB_BLANK_POWERDOWN: default: pr_debug("blank powerdown called. cur mode=%d, req mode=%d\n", cur_power_state, req_power_state); if (mdss_fb_is_power_on(mfd) && mfd->mdp.off_fnc) { cur_power_state = mfd->panel_power_state; mutex_lock(&mfd->update.lock); mfd->update.type = NOTIFY_TYPE_SUSPEND; mfd->update.is_suspend = 1; mutex_unlock(&mfd->update.lock); complete(&mfd->update.comp); del_timer(&mfd->no_update.timer); mfd->no_update.value = NOTIFY_TYPE_SUSPEND; complete(&mfd->no_update.comp); mfd->op_enable = false; mutex_lock(&mfd->bl_lock); if (mdss_panel_is_power_off(req_power_state)) { /* Stop Display thread */ if (mfd->disp_thread) mdss_fb_stop_disp_thread(mfd); mdss_fb_set_backlight(mfd, 0); mfd->bl_updated = 0; } mfd->panel_power_state = req_power_state; mutex_unlock(&mfd->bl_lock); ret = mfd->mdp.off_fnc(mfd); if (ret) mfd->panel_power_state = cur_power_state; else if (mdss_panel_is_power_off(req_power_state)) mdss_fb_release_fences(mfd); mfd->op_enable = true; complete(&mfd->power_off_comp); } req_power_state = MDSS_PANEL_POWER_OFF; pr_debug("blank powerdown called\n"); ret = mdss_fb_blank_blank(mfd, req_power_state); break; } /* Notify listeners */ sysfs_notify(&mfd->fbi->dev->kobj, NULL, "show_blank_event"); Loading @@ -1343,8 +1397,10 @@ static int mdss_fb_blank(int blank_mode, struct fb_info *info) if (mfd->op_enable == 0) { if (blank_mode == FB_BLANK_UNBLANK) mfd->suspend.panel_power_state = MDSS_PANEL_POWER_ON; else if (blank_mode == FB_BLANK_VSYNC_SUSPEND) mfd->suspend.panel_power_state = MDSS_PANEL_POWER_DOZE; else if (blank_mode == BLANK_FLAG_ULP) mfd->suspend.panel_power_state = MDSS_PANEL_POWER_LP2; else if (blank_mode == BLANK_FLAG_LP) mfd->suspend.panel_power_state = MDSS_PANEL_POWER_LP1; else mfd->suspend.panel_power_state = MDSS_PANEL_POWER_OFF; return 0; Loading drivers/video/msm/mdss/mdss_mdp_ctl.c +2 −1 Original line number Diff line number Diff line Loading @@ -2550,11 +2550,12 @@ int mdss_mdp_ctl_stop(struct mdss_mdp_ctl *ctl, int power_state) mdss_mdp_ctl_write(ctl, off, 0); } ctl->power_state = power_state; ctl->play_cnt = 0; mdss_mdp_ctl_perf_update(ctl, 0); end: if (!ret) ctl->power_state = power_state; mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF); mutex_unlock(&ctl->lock); Loading drivers/video/msm/mdss/mdss_mdp_intf_cmd.c +87 −28 Original line number Diff line number Diff line Loading @@ -731,6 +731,8 @@ static int mdss_mdp_cmd_panel_on(struct mdss_mdp_ctl *ctl, mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_REGISTER_RECOVERY_HANDLER, (void *)&ctx->intf_recovery); ctx->intf_stopped = 0; } else { pr_err("%s: Panel already on\n", __func__); } Loading Loading @@ -878,7 +880,6 @@ int mdss_mdp_cmd_intfs_stop(struct mdss_mdp_ctl *ctl, int session, pr_err("invalid ctx session: %d\n", session); return -ENODEV; } ctx->ref_cnt--; /* intf stopped, no more kickoff */ ctx->intf_stopped = 1; Loading Loading @@ -919,6 +920,11 @@ int mdss_mdp_cmd_intfs_stop(struct mdss_mdp_ctl *ctl, int session, flush_work(&ctx->pp_done_work); mdss_mdp_cmd_tearcheck_setup(ctl, false); if (mdss_panel_is_power_on(panel_power_state)) { pr_debug("%s: intf stopped with panel on\n", __func__); goto end; } mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_PING_PONG_RD_PTR, ctx->pp_num, NULL, NULL); mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_PING_PONG_COMP, Loading @@ -926,6 +932,7 @@ int mdss_mdp_cmd_intfs_stop(struct mdss_mdp_ctl *ctl, int session, memset(ctx, 0, sizeof(*ctx)); end: pr_debug("%s:-\n", __func__); return 0; Loading @@ -944,17 +951,6 @@ static int mdss_mdp_cmd_stop_sub(struct mdss_mdp_ctl *ctl, return -ENODEV; } /* if power state already updated, skip this */ if (ctx->panel_power_state == panel_power_state) return 0; /* * If the panel will be left on, then we do not need to turn off * interface clocks since we may continue to get display updates. */ if (mdss_panel_is_power_on(panel_power_state)) return 0; list_for_each_entry_safe(handle, tmp, &ctx->vsync_handlers, list) mdss_mdp_cmd_remove_vsync_handler(ctl, handle); MDSS_XLOG(ctl->num, atomic_read(&ctx->koff_cnt), ctx->clk_enabled, Loading @@ -969,19 +965,78 @@ int mdss_mdp_cmd_stop(struct mdss_mdp_ctl *ctl, int panel_power_state) { struct mdss_mdp_cmd_ctx *ctx = ctl->priv_data; struct mdss_mdp_ctl *sctl = mdss_mdp_get_split_ctl(ctl); int ret; bool panel_off = false; bool turn_off_clocks = false; bool send_panel_events = false; int ret = 0; if (!ctx) { pr_err("invalid ctx\n"); return -ENODEV; } if (ctx->panel_power_state != panel_power_state) { if (ctx->panel_power_state == panel_power_state) return 0; if (__mdss_mdp_cmd_is_panel_power_off(ctx)) { pr_debug("%s: panel already off\n", __func__); return 0; } if (ctx->panel_power_state == panel_power_state) { pr_debug("%s: no transition needed %d --> %d\n", __func__, ctx->panel_power_state, panel_power_state); return 0; } pr_debug("%s: transition from %d --> %d\n", __func__, ctx->panel_power_state, panel_power_state); if (__mdss_mdp_cmd_is_panel_power_on_interactive(ctx)) { if (mdss_panel_is_power_on_lp(panel_power_state)) { /* * If we are transitioning from interactive to low * power, then we need to send events to the interface * so that the panel can be configured in low power * mode. */ send_panel_events = true; if (mdss_panel_is_power_on_ulp(panel_power_state)) turn_off_clocks = true; } else if (mdss_panel_is_power_off(panel_power_state)) { send_panel_events = true; turn_off_clocks = true; panel_off = true; } } else { if (mdss_panel_is_power_on_ulp(panel_power_state)) { /* * If we are transitioning from low power to ultra low * power mode, no more display updates are expected. * Turn off the interface clocks. */ pr_debug("%s: turn off clocks\n", __func__); turn_off_clocks = true; } else { /* * Transition from ultra low power to low power does * not require any special handling. The clocks would * get turned on when the first update comes. */ pr_debug("%s: nothing to be done.\n", __func__); return 0; } } if (!turn_off_clocks) goto panel_events; pr_debug("%s: turn off interface clocks\n", __func__); ret = mdss_mdp_cmd_stop_sub(ctl, panel_power_state); if (IS_ERR_VALUE(ret)) { pr_err("%s: unable to stop interface: %d\n", __func__, ret); return ret; goto end; } if (sctl) { Loading @@ -989,10 +1044,13 @@ int mdss_mdp_cmd_stop(struct mdss_mdp_ctl *ctl, int panel_power_state) if (IS_ERR_VALUE(ret)) { pr_err("%s: unable to stop slave intf: %d\n", __func__, ret); return ret; goto end; } } panel_events: if ((ctl->num == 0) && send_panel_events) { pr_debug("%s: send panel events\n", __func__); ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_BLANK, (void *) (long int) panel_power_state); WARN(ret, "intf %d unblank error (%d)\n", ctl->intf_num, ret); Loading @@ -1004,11 +1062,12 @@ int mdss_mdp_cmd_stop(struct mdss_mdp_ctl *ctl, int panel_power_state) ctx->panel_power_state = panel_power_state; if (mdss_panel_is_power_on(panel_power_state)) { if (!panel_off) { pr_debug("%s: cmd_stop with panel always on\n", __func__); goto end; } pr_debug("%s: turn off panel\n", __func__); ctl->priv_data = NULL; ctl->stop_fnc = NULL; ctl->display_fnc = NULL; Loading @@ -1021,7 +1080,7 @@ end: ctx->rdptr_enabled, XLOG_FUNC_EXIT); pr_debug("%s:-\n", __func__); return 0; return ret; } static int mdss_mdp_cmd_intfs_setup(struct mdss_mdp_ctl *ctl, Loading drivers/video/msm/mdss/mdss_panel.h +17 −3 Original line number Diff line number Diff line Loading @@ -91,7 +91,8 @@ enum { enum { MDSS_PANEL_POWER_OFF = 0, MDSS_PANEL_POWER_ON, MDSS_PANEL_POWER_DOZE, MDSS_PANEL_POWER_LP1, MDSS_PANEL_POWER_LP2, }; enum { Loading Loading @@ -625,8 +626,8 @@ static inline bool mdss_panel_is_power_on(int panel_power_state) * @panel_power_state: enum identifying the power state to be checked * * This function returns true if the panel is in an intermediate low power * state where it is still on but not fully interactive. It may still accept * commands and display updates but would be operating in a low power mode. * state where it is still on but not fully interactive. It may or may not * accept any commands and display updates. */ static inline bool mdss_panel_is_power_on_lp(int panel_power_state) { Loading @@ -634,6 +635,19 @@ static inline bool mdss_panel_is_power_on_lp(int panel_power_state) !mdss_panel_is_power_on_interactive(panel_power_state); } /** * mdss_panel_is_panel_power_on_ulp: - checks if panel is in ultra low power mode * @pdata: pointer to the panel struct associated to the panel * @panel_power_state: enum identifying the power state to be checked * * This function returns true if the panel is in a ultra low power * state where it is still on but cannot recieve any display updates. */ static inline bool mdss_panel_is_power_on_ulp(int panel_power_state) { return panel_power_state == MDSS_PANEL_POWER_LP2; } /** * mdss_panel_intf_type: - checks if a given intf type is primary * @intf_val: panel interface type of the individual controller Loading Loading
drivers/video/msm/mdss/mdss_dsi.c +8 −7 Original line number Diff line number Diff line Loading @@ -184,9 +184,9 @@ error: return ret; } static int mdss_dsi_panel_power_doze(struct mdss_panel_data *pdata, int enable) static int mdss_dsi_panel_power_lp(struct mdss_panel_data *pdata, int enable) { /* Panel power control when entering/exiting doze mode */ /* Panel power control when entering/exiting lp mode */ return 0; } Loading Loading @@ -223,12 +223,13 @@ static int mdss_dsi_panel_power_ctrl(struct mdss_panel_data *pdata, break; case MDSS_PANEL_POWER_ON: if (mdss_dsi_is_panel_on_lp(pdata)) ret = mdss_dsi_panel_power_doze(pdata, false); ret = mdss_dsi_panel_power_lp(pdata, false); else ret = mdss_dsi_panel_power_on(pdata); break; case MDSS_PANEL_POWER_DOZE: ret = mdss_dsi_panel_power_doze(pdata, true); case MDSS_PANEL_POWER_LP1: case MDSS_PANEL_POWER_LP2: ret = mdss_dsi_panel_power_lp(pdata, true); break; default: pr_err("%s: unknown panel power state requested (%d)\n", Loading Loading @@ -474,7 +475,7 @@ static int mdss_dsi_off(struct mdss_panel_data *pdata, int power_state) goto end; } if (power_state != MDSS_PANEL_POWER_OFF) { if (mdss_panel_is_power_on(power_state)) { pr_debug("%s: dsi_off with panel always on\n", __func__); goto panel_power_ctrl; } Loading Loading @@ -747,7 +748,7 @@ static int mdss_dsi_blank(struct mdss_panel_data *pdata, int power_state) mdss_dsi_clk_ctrl(ctrl_pdata, DSI_ALL_CLKS, 1); if (power_state == MDSS_PANEL_POWER_DOZE) { if (mdss_panel_is_power_on_lp(power_state)) { pr_debug("%s: low power state requested\n", __func__); if (ctrl_pdata->low_power_config) ret = ctrl_pdata->low_power_config(pdata, true); Loading
drivers/video/msm/mdss/mdss_fb.c +129 −73 Original line number Diff line number Diff line Loading @@ -65,6 +65,10 @@ #endif #define MAX_FBI_LIST 32 #define BLANK_FLAG_LP FB_BLANK_VSYNC_SUSPEND #define BLANK_FLAG_ULP FB_BLANK_NORMAL static struct fb_info *fbi_list[MAX_FBI_LIST]; static int fbi_list_index; Loading Loading @@ -853,10 +857,11 @@ static int mdss_fb_suspend_sub(struct msm_fb_data_type *mfd) /* * Ideally, display should have either been blanked by now, or * should have transitioned to a low power state. If not, then * as a fall back option, blank the display. * as a fall back option, enter ulp state to leave the display * on, but turn off all interface clocks. */ if (mdss_fb_is_power_on_interactive(mfd)) { ret = mdss_fb_blank_sub(FB_BLANK_POWERDOWN, mfd->fbi, if (mdss_fb_is_power_on(mfd)) { ret = mdss_fb_blank_sub(BLANK_FLAG_ULP, mfd->fbi, mfd->suspend.op_enable); if (ret) { pr_err("can't turn off display!\n"); Loading Loading @@ -892,13 +897,16 @@ static int mdss_fb_resume_sub(struct msm_fb_data_type *mfd) mfd->op_enable = mfd->suspend.op_enable; /* * If the fb was explicitly blanked during suspend, then unblank it * during resume. * If the fb was explicitly blanked or transitioned to ulp during * suspend, then undo it during resume with the appropriate unblank * flag. If fb was in ulp state when entering suspend, then nothing * needs to be done. */ if (mdss_panel_is_power_on(mfd->suspend.panel_power_state)) { if (mdss_panel_is_power_on(mfd->suspend.panel_power_state) && !mdss_panel_is_power_on_ulp(mfd->suspend.panel_power_state)) { int unblank_flag = mdss_panel_is_power_on_interactive( mfd->suspend.panel_power_state) ? FB_BLANK_UNBLANK : FB_BLANK_VSYNC_SUSPEND; BLANK_FLAG_LP; ret = mdss_fb_blank_sub(unblank_flag, mfd->fbi, mfd->op_enable); if (ret) Loading Loading @@ -1179,7 +1187,61 @@ static void mdss_panel_validate_debugfs_info(struct msm_fb_data_type *mfd) } } static int mdss_fb_unblank_sub(struct msm_fb_data_type *mfd) static int mdss_fb_blank_blank(struct msm_fb_data_type *mfd, int req_power_state) { int ret = 0; int cur_power_state; if (!mfd) return -EINVAL; if (!mdss_fb_is_power_on(mfd) || !mfd->mdp.off_fnc) return 0; cur_power_state = mfd->panel_power_state; pr_debug("Transitioning from %d --> %d\n", cur_power_state, req_power_state); if (cur_power_state == req_power_state) { pr_debug("No change in power state\n"); return 0; } mutex_lock(&mfd->update.lock); mfd->update.type = NOTIFY_TYPE_SUSPEND; mfd->update.is_suspend = 1; mutex_unlock(&mfd->update.lock); complete(&mfd->update.comp); del_timer(&mfd->no_update.timer); mfd->no_update.value = NOTIFY_TYPE_SUSPEND; complete(&mfd->no_update.comp); mfd->op_enable = false; mutex_lock(&mfd->bl_lock); if (mdss_panel_is_power_off(req_power_state)) { /* Stop Display thread */ if (mfd->disp_thread) mdss_fb_stop_disp_thread(mfd); mdss_fb_set_backlight(mfd, 0); mfd->bl_updated = 0; } mfd->panel_power_state = req_power_state; mutex_unlock(&mfd->bl_lock); ret = mfd->mdp.off_fnc(mfd); if (ret) mfd->panel_power_state = cur_power_state; else if (mdss_panel_is_power_off(req_power_state)) mdss_fb_release_fences(mfd); mfd->op_enable = true; complete(&mfd->power_off_comp); return ret; } static int mdss_fb_blank_unblank(struct msm_fb_data_type *mfd) { int ret = 0; int cur_power_state; Loading @@ -1198,16 +1260,23 @@ static int mdss_fb_unblank_sub(struct msm_fb_data_type *mfd) } cur_power_state = mfd->panel_power_state; if (!mdss_panel_is_power_on_interactive(cur_power_state) && mfd->mdp.on_fnc) { pr_debug("Transitioning from %d --> %d\n", cur_power_state, MDSS_PANEL_POWER_ON); if (mdss_panel_is_power_on_interactive(cur_power_state)) { pr_debug("No change in power state\n"); return 0; } if (mfd->mdp.on_fnc) { ret = mfd->mdp.on_fnc(mfd); if (ret == 0) { mfd->panel_power_state = MDSS_PANEL_POWER_ON; mfd->panel_info->panel_dead = false; } else if (mfd->disp_thread) { if (ret) { mdss_fb_stop_disp_thread(mfd); goto error; } mfd->panel_power_state = MDSS_PANEL_POWER_ON; mfd->panel_info->panel_dead = false; mutex_lock(&mfd->update.lock); mfd->update.type = NOTIFY_TYPE_UPDATE; mfd->update.is_suspend = 0; Loading Loading @@ -1252,82 +1321,67 @@ static int mdss_fb_blank_sub(int blank_mode, struct fb_info *info, cur_power_state = mfd->panel_power_state; /* * If doze mode is requested for video mode panels, treat * the request as full unblank as there are no low power mode * settings for video mode panels. * Low power (lp) and ultra low pwoer (ulp) modes are currently only * supported for command mode panels. For all other panel, treat lp * mode as full unblank and ulp mode as full blank. */ if ((FB_BLANK_VSYNC_SUSPEND == blank_mode) && (mfd->panel_info->type != MIPI_CMD_PANEL)) { pr_debug("Doze mode only valid for cmd mode panels\n"); if (mdss_panel_is_power_on(cur_power_state)) if (mfd->panel_info->type != MIPI_CMD_PANEL) { if (BLANK_FLAG_LP == blank_mode) { pr_debug("lp mode only valid for cmd mode panels\n"); if (mdss_fb_is_power_on_interactive(mfd)) return 0; else blank_mode = FB_BLANK_UNBLANK; } else if (BLANK_FLAG_ULP == blank_mode) { pr_debug("ulp mode valid for cmd mode panels\n"); if (mdss_fb_is_power_off(mfd)) return 0; else blank_mode = FB_BLANK_POWERDOWN; } } switch (blank_mode) { case FB_BLANK_UNBLANK: pr_debug("unblank called. cur pwr state=%d\n", cur_power_state); ret = mdss_fb_unblank_sub(mfd); ret = mdss_fb_blank_unblank(mfd); break; case BLANK_FLAG_ULP: req_power_state = MDSS_PANEL_POWER_LP2; pr_debug("ultra low power mode requested\n"); if (mdss_fb_is_power_off(mfd)) { pr_debug("Unsupp transition: off --> ulp\n"); return 0; } case FB_BLANK_VSYNC_SUSPEND: req_power_state = MDSS_PANEL_POWER_DOZE; pr_debug("Doze power mode requested\n"); ret = mdss_fb_blank_blank(mfd, req_power_state); break; case BLANK_FLAG_LP: req_power_state = MDSS_PANEL_POWER_LP1; pr_debug(" power mode requested\n"); /* * If doze mode is requested when panel is already off, * then first unblank the panel before entering doze mode * If low power mode is requested when panel is already off, * then first unblank the panel before entering low power mode */ if (mdss_fb_is_power_off(mfd) && mfd->mdp.on_fnc) { pr_debug("off --> doze. switch to on first\n"); ret = mdss_fb_unblank_sub(mfd); pr_debug("off --> lp. switch to on first\n"); ret = mdss_fb_blank_unblank(mfd); if (ret) break; } /* Enter doze mode only if unblank succeeded */ if (ret) ret = mdss_fb_blank_blank(mfd, req_power_state); break; case FB_BLANK_HSYNC_SUSPEND: case FB_BLANK_NORMAL: case FB_BLANK_POWERDOWN: default: pr_debug("blank powerdown called. cur mode=%d, req mode=%d\n", cur_power_state, req_power_state); if (mdss_fb_is_power_on(mfd) && mfd->mdp.off_fnc) { cur_power_state = mfd->panel_power_state; mutex_lock(&mfd->update.lock); mfd->update.type = NOTIFY_TYPE_SUSPEND; mfd->update.is_suspend = 1; mutex_unlock(&mfd->update.lock); complete(&mfd->update.comp); del_timer(&mfd->no_update.timer); mfd->no_update.value = NOTIFY_TYPE_SUSPEND; complete(&mfd->no_update.comp); mfd->op_enable = false; mutex_lock(&mfd->bl_lock); if (mdss_panel_is_power_off(req_power_state)) { /* Stop Display thread */ if (mfd->disp_thread) mdss_fb_stop_disp_thread(mfd); mdss_fb_set_backlight(mfd, 0); mfd->bl_updated = 0; } mfd->panel_power_state = req_power_state; mutex_unlock(&mfd->bl_lock); ret = mfd->mdp.off_fnc(mfd); if (ret) mfd->panel_power_state = cur_power_state; else if (mdss_panel_is_power_off(req_power_state)) mdss_fb_release_fences(mfd); mfd->op_enable = true; complete(&mfd->power_off_comp); } req_power_state = MDSS_PANEL_POWER_OFF; pr_debug("blank powerdown called\n"); ret = mdss_fb_blank_blank(mfd, req_power_state); break; } /* Notify listeners */ sysfs_notify(&mfd->fbi->dev->kobj, NULL, "show_blank_event"); Loading @@ -1343,8 +1397,10 @@ static int mdss_fb_blank(int blank_mode, struct fb_info *info) if (mfd->op_enable == 0) { if (blank_mode == FB_BLANK_UNBLANK) mfd->suspend.panel_power_state = MDSS_PANEL_POWER_ON; else if (blank_mode == FB_BLANK_VSYNC_SUSPEND) mfd->suspend.panel_power_state = MDSS_PANEL_POWER_DOZE; else if (blank_mode == BLANK_FLAG_ULP) mfd->suspend.panel_power_state = MDSS_PANEL_POWER_LP2; else if (blank_mode == BLANK_FLAG_LP) mfd->suspend.panel_power_state = MDSS_PANEL_POWER_LP1; else mfd->suspend.panel_power_state = MDSS_PANEL_POWER_OFF; return 0; Loading
drivers/video/msm/mdss/mdss_mdp_ctl.c +2 −1 Original line number Diff line number Diff line Loading @@ -2550,11 +2550,12 @@ int mdss_mdp_ctl_stop(struct mdss_mdp_ctl *ctl, int power_state) mdss_mdp_ctl_write(ctl, off, 0); } ctl->power_state = power_state; ctl->play_cnt = 0; mdss_mdp_ctl_perf_update(ctl, 0); end: if (!ret) ctl->power_state = power_state; mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF); mutex_unlock(&ctl->lock); Loading
drivers/video/msm/mdss/mdss_mdp_intf_cmd.c +87 −28 Original line number Diff line number Diff line Loading @@ -731,6 +731,8 @@ static int mdss_mdp_cmd_panel_on(struct mdss_mdp_ctl *ctl, mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_REGISTER_RECOVERY_HANDLER, (void *)&ctx->intf_recovery); ctx->intf_stopped = 0; } else { pr_err("%s: Panel already on\n", __func__); } Loading Loading @@ -878,7 +880,6 @@ int mdss_mdp_cmd_intfs_stop(struct mdss_mdp_ctl *ctl, int session, pr_err("invalid ctx session: %d\n", session); return -ENODEV; } ctx->ref_cnt--; /* intf stopped, no more kickoff */ ctx->intf_stopped = 1; Loading Loading @@ -919,6 +920,11 @@ int mdss_mdp_cmd_intfs_stop(struct mdss_mdp_ctl *ctl, int session, flush_work(&ctx->pp_done_work); mdss_mdp_cmd_tearcheck_setup(ctl, false); if (mdss_panel_is_power_on(panel_power_state)) { pr_debug("%s: intf stopped with panel on\n", __func__); goto end; } mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_PING_PONG_RD_PTR, ctx->pp_num, NULL, NULL); mdss_mdp_set_intr_callback(MDSS_MDP_IRQ_PING_PONG_COMP, Loading @@ -926,6 +932,7 @@ int mdss_mdp_cmd_intfs_stop(struct mdss_mdp_ctl *ctl, int session, memset(ctx, 0, sizeof(*ctx)); end: pr_debug("%s:-\n", __func__); return 0; Loading @@ -944,17 +951,6 @@ static int mdss_mdp_cmd_stop_sub(struct mdss_mdp_ctl *ctl, return -ENODEV; } /* if power state already updated, skip this */ if (ctx->panel_power_state == panel_power_state) return 0; /* * If the panel will be left on, then we do not need to turn off * interface clocks since we may continue to get display updates. */ if (mdss_panel_is_power_on(panel_power_state)) return 0; list_for_each_entry_safe(handle, tmp, &ctx->vsync_handlers, list) mdss_mdp_cmd_remove_vsync_handler(ctl, handle); MDSS_XLOG(ctl->num, atomic_read(&ctx->koff_cnt), ctx->clk_enabled, Loading @@ -969,19 +965,78 @@ int mdss_mdp_cmd_stop(struct mdss_mdp_ctl *ctl, int panel_power_state) { struct mdss_mdp_cmd_ctx *ctx = ctl->priv_data; struct mdss_mdp_ctl *sctl = mdss_mdp_get_split_ctl(ctl); int ret; bool panel_off = false; bool turn_off_clocks = false; bool send_panel_events = false; int ret = 0; if (!ctx) { pr_err("invalid ctx\n"); return -ENODEV; } if (ctx->panel_power_state != panel_power_state) { if (ctx->panel_power_state == panel_power_state) return 0; if (__mdss_mdp_cmd_is_panel_power_off(ctx)) { pr_debug("%s: panel already off\n", __func__); return 0; } if (ctx->panel_power_state == panel_power_state) { pr_debug("%s: no transition needed %d --> %d\n", __func__, ctx->panel_power_state, panel_power_state); return 0; } pr_debug("%s: transition from %d --> %d\n", __func__, ctx->panel_power_state, panel_power_state); if (__mdss_mdp_cmd_is_panel_power_on_interactive(ctx)) { if (mdss_panel_is_power_on_lp(panel_power_state)) { /* * If we are transitioning from interactive to low * power, then we need to send events to the interface * so that the panel can be configured in low power * mode. */ send_panel_events = true; if (mdss_panel_is_power_on_ulp(panel_power_state)) turn_off_clocks = true; } else if (mdss_panel_is_power_off(panel_power_state)) { send_panel_events = true; turn_off_clocks = true; panel_off = true; } } else { if (mdss_panel_is_power_on_ulp(panel_power_state)) { /* * If we are transitioning from low power to ultra low * power mode, no more display updates are expected. * Turn off the interface clocks. */ pr_debug("%s: turn off clocks\n", __func__); turn_off_clocks = true; } else { /* * Transition from ultra low power to low power does * not require any special handling. The clocks would * get turned on when the first update comes. */ pr_debug("%s: nothing to be done.\n", __func__); return 0; } } if (!turn_off_clocks) goto panel_events; pr_debug("%s: turn off interface clocks\n", __func__); ret = mdss_mdp_cmd_stop_sub(ctl, panel_power_state); if (IS_ERR_VALUE(ret)) { pr_err("%s: unable to stop interface: %d\n", __func__, ret); return ret; goto end; } if (sctl) { Loading @@ -989,10 +1044,13 @@ int mdss_mdp_cmd_stop(struct mdss_mdp_ctl *ctl, int panel_power_state) if (IS_ERR_VALUE(ret)) { pr_err("%s: unable to stop slave intf: %d\n", __func__, ret); return ret; goto end; } } panel_events: if ((ctl->num == 0) && send_panel_events) { pr_debug("%s: send panel events\n", __func__); ret = mdss_mdp_ctl_intf_event(ctl, MDSS_EVENT_BLANK, (void *) (long int) panel_power_state); WARN(ret, "intf %d unblank error (%d)\n", ctl->intf_num, ret); Loading @@ -1004,11 +1062,12 @@ int mdss_mdp_cmd_stop(struct mdss_mdp_ctl *ctl, int panel_power_state) ctx->panel_power_state = panel_power_state; if (mdss_panel_is_power_on(panel_power_state)) { if (!panel_off) { pr_debug("%s: cmd_stop with panel always on\n", __func__); goto end; } pr_debug("%s: turn off panel\n", __func__); ctl->priv_data = NULL; ctl->stop_fnc = NULL; ctl->display_fnc = NULL; Loading @@ -1021,7 +1080,7 @@ end: ctx->rdptr_enabled, XLOG_FUNC_EXIT); pr_debug("%s:-\n", __func__); return 0; return ret; } static int mdss_mdp_cmd_intfs_setup(struct mdss_mdp_ctl *ctl, Loading
drivers/video/msm/mdss/mdss_panel.h +17 −3 Original line number Diff line number Diff line Loading @@ -91,7 +91,8 @@ enum { enum { MDSS_PANEL_POWER_OFF = 0, MDSS_PANEL_POWER_ON, MDSS_PANEL_POWER_DOZE, MDSS_PANEL_POWER_LP1, MDSS_PANEL_POWER_LP2, }; enum { Loading Loading @@ -625,8 +626,8 @@ static inline bool mdss_panel_is_power_on(int panel_power_state) * @panel_power_state: enum identifying the power state to be checked * * This function returns true if the panel is in an intermediate low power * state where it is still on but not fully interactive. It may still accept * commands and display updates but would be operating in a low power mode. * state where it is still on but not fully interactive. It may or may not * accept any commands and display updates. */ static inline bool mdss_panel_is_power_on_lp(int panel_power_state) { Loading @@ -634,6 +635,19 @@ static inline bool mdss_panel_is_power_on_lp(int panel_power_state) !mdss_panel_is_power_on_interactive(panel_power_state); } /** * mdss_panel_is_panel_power_on_ulp: - checks if panel is in ultra low power mode * @pdata: pointer to the panel struct associated to the panel * @panel_power_state: enum identifying the power state to be checked * * This function returns true if the panel is in a ultra low power * state where it is still on but cannot recieve any display updates. */ static inline bool mdss_panel_is_power_on_ulp(int panel_power_state) { return panel_power_state == MDSS_PANEL_POWER_LP2; } /** * mdss_panel_intf_type: - checks if a given intf type is primary * @intf_val: panel interface type of the individual controller Loading