From 50b9827d5bf63a5348845abdacedc122bc04693b Mon Sep 17 00:00:00 2001 From: Jeevan Shriram Date: Mon, 4 May 2015 16:29:24 -0700 Subject: msm: mdss: reset MDP ctl path for pingpong timeout recovery When pingpong timeout is received, it could be due to some bad configuration on MDP pipeline. In order to recover and have clean HW for next frames, perform SW reset on the faulting ctl path. Also make sure all the pipes in the bad configuration are halted properly and does not have any residue transaction for the new frames. Conflicts: drivers/video/msm/mdss/mdss_mdp_pipe.c Change-Id: I9a6c6cb6d004fd65c24a7fefe7457ee68af9273a Signed-off-by: Ujwal Patel Signed-off-by: Naseer Ahmed Signed-off-by: Jeevan Shriram --- drivers/video/fbdev/msm/mdss_mdp.h | 3 ++ drivers/video/fbdev/msm/mdss_mdp_ctl.c | 87 ++++++++++++++++++++++++++++-- drivers/video/fbdev/msm/mdss_mdp_overlay.c | 3 ++ drivers/video/fbdev/msm/mdss_mdp_pipe.c | 47 ++++++++++------ 4 files changed, 120 insertions(+), 20 deletions(-) diff --git a/drivers/video/fbdev/msm/mdss_mdp.h b/drivers/video/fbdev/msm/mdss_mdp.h index 5a05661c1b93..e4dbea3bf4a0 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.h +++ b/drivers/video/fbdev/msm/mdss_mdp.h @@ -342,6 +342,8 @@ struct mdss_mdp_mixer { bool is_right_mixer; struct mdss_mdp_ctl *ctl; struct mdss_mdp_pipe *stage_pipe[MAX_PIPES_PER_LM]; + u32 next_pipe_map; + u32 pipe_mapped; }; struct mdss_mdp_format_params { @@ -1144,6 +1146,7 @@ int mdss_mdp_ctl_addr_setup(struct mdss_data_type *mdata, u32 *ctl_offsets, int mdss_mdp_wb_addr_setup(struct mdss_data_type *mdata, u32 num_wb, u32 num_intf_wb); +void mdss_mdp_pipe_clk_force_off(struct mdss_mdp_pipe *pipe); int mdss_mdp_pipe_fetch_halt(struct mdss_mdp_pipe *pipe); int mdss_mdp_pipe_panic_signal_ctrl(struct mdss_mdp_pipe *pipe, bool enable); void mdss_mdp_config_pipe_panic_lut(struct mdss_data_type *mdata); diff --git a/drivers/video/fbdev/msm/mdss_mdp_ctl.c b/drivers/video/fbdev/msm/mdss_mdp_ctl.c index 73b44b7ea3e6..197961315efb 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_ctl.c +++ b/drivers/video/fbdev/msm/mdss_mdp_ctl.c @@ -2123,6 +2123,8 @@ struct mdss_mdp_mixer *mdss_mdp_mixer_alloc( mixer->ref_cnt++; mixer->params_changed++; mixer->ctl = ctl; + mixer->next_pipe_map = 0; + mixer->pipe_mapped = 0; pr_debug("alloc mixer num %d for ctl=%d\n", mixer->num, ctl->num); } @@ -3284,6 +3286,32 @@ end: return ret; } +/* + * mdss_mdp_pipe_reset() - Halts all the pipes during ctl reset. + * @mixer: Mixer from which to reset all pipes. + * This function called during control path reset and will halt + * all the pipes staged on the mixer. + */ +static void mdss_mdp_pipe_reset(struct mdss_mdp_mixer *mixer) +{ + unsigned long pipe_map = mixer->pipe_mapped; + u32 bit = 0; + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + bool sw_rst_avail = mdss_mdp_pipe_is_sw_reset_available(mdata); + + pr_debug("pipe_map=0x%lx\n", pipe_map); + for_each_set_bit_from(bit, &pipe_map, MAX_PIPES_PER_LM) { + struct mdss_mdp_pipe *pipe; + + pipe = mdss_mdp_pipe_search(mdata, 1 << bit); + if (pipe) { + mdss_mdp_pipe_fetch_halt(pipe); + if (sw_rst_avail) + mdss_mdp_pipe_clk_force_off(pipe); + } + } +} + /* * mdss_mdp_ctl_reset() - reset mdp ctl path. * @ctl: mdp controller. @@ -3296,6 +3324,7 @@ int mdss_mdp_ctl_reset(struct mdss_mdp_ctl *ctl) { u32 status = 1; int cnt = 20; + struct mdss_mdp_mixer *mixer = ctl->mixer_left; mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_SW_RESET, 1); @@ -3311,11 +3340,18 @@ int mdss_mdp_ctl_reset(struct mdss_mdp_ctl *ctl) pr_debug("status=%x\n", status); cnt--; if (cnt == 0) { - pr_err("timeout\n"); + pr_err("ctl%d reset timedout\n", ctl->num); return -EAGAIN; } } while (status); + if (mixer) { + mdss_mdp_pipe_reset(mixer); + + if (ctl->mfd->split_mode == MDP_DUAL_LM_SINGLE_DISPLAY) + mdss_mdp_pipe_reset(ctl->mixer_right); + } + return 0; } @@ -3342,6 +3378,28 @@ static void mdss_mdp_set_mixer_roi(struct mdss_mdp_ctl *ctl, ctl->num, ctl->roi.x, ctl->roi.y, ctl->roi.w, ctl->roi.h); } +/* + * mdss_mdp_mixer_update_pipe_map() - keep track of pipe configuration in mixer + * @master_ctl: mdp controller. + * + * This function keeps track of the current mixer configuration in the hardware. + * It's callers responsibility to call with master control. + */ +static void mdss_mdp_mixer_update_pipe_map(struct mdss_mdp_ctl *master_ctl, + int mixer_mux) +{ + struct mdss_mdp_mixer *mixer = mdss_mdp_mixer_get(master_ctl, + mixer_mux); + + if (!mixer) + return; + + pr_debug("mixer%d pipe_mapped=0x%x next_pipes=0x%x\n", + mixer->num, mixer->pipe_mapped, mixer->next_pipe_map); + + mixer->pipe_mapped = mixer->next_pipe_map; +} + static inline u32 mdss_mdp_mpq_pipe_num_map(u32 pipe_num) { u32 mpq_num; @@ -3463,6 +3521,13 @@ static void mdss_mdp_mixer_setup(struct mdss_mdp_ctl *master_ctl, continue; } + /* + * pipe which is staged on both LMs will be tracked through + * left mixer only. + */ + if (!pipe->src_split_req || !mixer->is_right_mixer) + mixer->next_pipe_map |= pipe->ndx; + blend_stage = stage - MDSS_MDP_STAGE_0; off = MDSS_MDP_REG_LM_BLEND_OFFSET(blend_stage); @@ -4106,6 +4171,7 @@ int mdss_mdp_display_wait4pingpong(struct mdss_mdp_ctl *ctl, bool use_lock) { struct mdss_mdp_ctl *sctl = NULL; int ret; + bool recovery_needed = false; if (use_lock) { ret = mutex_lock_interruptible(&ctl->lock); @@ -4120,15 +4186,27 @@ int mdss_mdp_display_wait4pingpong(struct mdss_mdp_ctl *ctl, bool use_lock) } ATRACE_BEGIN("wait_pingpong"); - ctl->ops.wait_pingpong(ctl, NULL); + ret = ctl->ops.wait_pingpong(ctl, NULL); ATRACE_END("wait_pingpong"); + if (ret) + recovery_needed = true; sctl = mdss_mdp_get_split_ctl(ctl); if (sctl && sctl->ops.wait_pingpong) { ATRACE_BEGIN("wait_pingpong sctl"); - sctl->ops.wait_pingpong(sctl, NULL); + ret = sctl->ops.wait_pingpong(sctl, NULL); ATRACE_END("wait_pingpong sctl"); + if (ret) + recovery_needed = true; + } + + if (recovery_needed) { + mdss_mdp_ctl_reset(ctl); + if (sctl) + mdss_mdp_ctl_reset(sctl); + + pr_debug("pingpong timeout recovery finished\n"); } if (use_lock) @@ -4386,6 +4464,9 @@ int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg, ctl->flush_reg_data = ctl_flush_bits; ctl->flush_bits = 0; + mdss_mdp_mixer_update_pipe_map(ctl, MDSS_MDP_MIXER_MUX_LEFT); + mdss_mdp_mixer_update_pipe_map(ctl, MDSS_MDP_MIXER_MUX_RIGHT); + if (sctl && !ctl->valid_roi && sctl->valid_roi) { /* * Seperate kickoff on DSI1 is needed only when we have diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c index 426de7be12e4..a6aea1023128 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c +++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c @@ -1244,6 +1244,9 @@ static void mdss_mdp_overlay_cleanup(struct msm_fb_data_type *mfd, mdss_mdp_mixer_pipe_unstage(pipe, pipe->mixer_right); } __overlay_pipe_cleanup(mfd, pipe); + ctl->mixer_left->next_pipe_map &= ~pipe->ndx; + if (ctl->mixer_right) + ctl->mixer_right->next_pipe_map &= ~pipe->ndx; } mutex_unlock(&mdp5_data->list_lock); } diff --git a/drivers/video/fbdev/msm/mdss_mdp_pipe.c b/drivers/video/fbdev/msm/mdss_mdp_pipe.c index 15f47ed4ae93..cc1eabee1c64 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_pipe.c +++ b/drivers/video/fbdev/msm/mdss_mdp_pipe.c @@ -909,7 +909,6 @@ static void mdss_mdp_fixed_qos_arbiter_setup(struct mdss_data_type *mdata, static int mdss_mdp_pipe_init_config(struct mdss_mdp_pipe *pipe, struct mdss_mdp_mixer *mixer, bool pipe_share) { - u32 reg_val, force_off_mask; bool is_realtime; int rc = 0; struct mdss_data_type *mdata; @@ -927,22 +926,8 @@ static int mdss_mdp_pipe_init_config(struct mdss_mdp_pipe *pipe, mdss_mdp_pipe_panic_signal_ctrl(pipe, false); - if (pipe && mdss_mdp_pipe_is_sw_reset_available(mdata)) { - force_off_mask = - BIT(pipe->clk_ctrl.bit_off + CLK_FORCE_OFF_OFFSET); - - mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON); - mutex_lock(&mdata->reg_lock); - reg_val = readl_relaxed(mdata->mdp_base + - pipe->clk_ctrl.reg_off); - if (reg_val & force_off_mask) { - reg_val &= ~force_off_mask; - writel_relaxed(reg_val, - mdata->mdp_base + pipe->clk_ctrl.reg_off); - } - mutex_unlock(&mdata->reg_lock); - mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF); - } + if (pipe && mdss_mdp_pipe_is_sw_reset_available(mdata)) + mdss_mdp_pipe_clk_force_off(pipe); if (pipe) { pr_debug("type=%x pnum=%d\n", pipe->type, pipe->num); @@ -1336,6 +1321,34 @@ exit: return is_idle; } +/* + * mdss_mdp_pipe_clk_force_off() - check force off mask and reset for the pipe. + * @pipe: pointer to the pipe data structure which needs to be checked for clk. + * + * This function would be called where software reset is available for pipe + * clocks. + */ + +void mdss_mdp_pipe_clk_force_off(struct mdss_mdp_pipe *pipe) +{ + u32 reg_val, force_off_mask; + struct mdss_data_type *mdata = mdss_mdp_get_mdata(); + + force_off_mask = + BIT(pipe->clk_ctrl.bit_off + CLK_FORCE_OFF_OFFSET); + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON); + mutex_lock(&mdata->reg_lock); + reg_val = readl_relaxed(mdata->mdp_base + + pipe->clk_ctrl.reg_off); + if (reg_val & force_off_mask) { + reg_val &= ~force_off_mask; + writel_relaxed(reg_val, + mdata->mdp_base + pipe->clk_ctrl.reg_off); + } + mutex_unlock(&mdata->reg_lock); + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF); +} + /** * mdss_mdp_pipe_fetch_halt() - Halt VBIF client corresponding to specified pipe * @pipe: pointer to the pipe data structure which needs to be halted. -- cgit v1.2.3