Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1aa7be2c authored by Jayant Shekhar's avatar Jayant Shekhar
Browse files

msm: mdss: Handle case when pipe is not idle during cleanup.



When pipe is not idle during cleanup, MDP can go into an unrecoverable
state, which can lead into other issues. If it gets to this point we
need to recover even if it means changing display contents for a
fraction of time. To handle this follow recovery sequence:
1. Setup the pipes that are not idle to solid fill mode so that nothing
   is fetched from memory on these pipes.
2. Stage them into mixer, flush and wait for a vsync to happen and reset
   sequence to take effect on these.
3. Finally, reattach the current frame pipes to restore display back to
   normal.

Change-Id: If545b56bf32a46699871381d86e80c1980ec3cc7
Signed-off-by: default avatarJayant Shekhar <jshekhar@codeaurora.org>
Signed-off-by: default avatarAdrian Salido-Moreno <adrianm@codeaurora.org>
parent 879cdf53
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -612,6 +612,7 @@ int mdss_mdp_mixer_pipe_update(struct mdss_mdp_pipe *pipe,
	struct mdss_mdp_mixer *mixer, int params_changed);
int mdss_mdp_mixer_pipe_unstage(struct mdss_mdp_pipe *pipe,
	struct mdss_mdp_mixer *mixer);
void mdss_mdp_mixer_unstage_all(struct mdss_mdp_mixer *mixer);
int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg);
int mdss_mdp_display_wait4comp(struct mdss_mdp_ctl *ctl);
int mdss_mdp_display_wait4pingpong(struct mdss_mdp_ctl *ctl);
+27 −5
Original line number Diff line number Diff line
@@ -2370,6 +2370,33 @@ int mdss_mdp_mixer_pipe_update(struct mdss_mdp_pipe *pipe,
	return 0;
}

/**
 * mdss_mdp_mixer_unstage_all() - Unstage all pipes from mixer
 * @mixer:	Mixer from which to unstage all pipes
 *
 * Unstage any pipes that are currently attached to mixer.
 *
 * NOTE: this will not update the pipe structure, and thus a full
 * deinitialization or reconfiguration of all pipes is expected after this call.
 */
void mdss_mdp_mixer_unstage_all(struct mdss_mdp_mixer *mixer)
{
	struct mdss_mdp_pipe *tmp;
	int i;

	if (!mixer)
		return;

	for (i = 0; i < MAX_PIPES_PER_LM; i++) {
		tmp = mixer->stage_pipe[i];
		if (tmp) {
			mixer->stage_pipe[i] = NULL;
			mixer->params_changed++;
			tmp->params_changed++;
		}
	}
}

int mdss_mdp_mixer_pipe_unstage(struct mdss_mdp_pipe *pipe,
	struct mdss_mdp_mixer *mixer)
{
@@ -2390,11 +2417,6 @@ int mdss_mdp_mixer_pipe_unstage(struct mdss_mdp_pipe *pipe,

		mixer->params_changed++;
		mixer->stage_pipe[index] = NULL;

		if (mixer->is_right_mixer)
			pipe->mixer_right = NULL;
		else
			pipe->mixer_left = NULL;
	}

	return 0;
+103 −58
Original line number Diff line number Diff line
@@ -62,6 +62,7 @@ static int mdss_mdp_overlay_free_fb_pipe(struct msm_fb_data_type *mfd);
static int mdss_mdp_overlay_fb_parse_dt(struct msm_fb_data_type *mfd);
static int mdss_mdp_overlay_off(struct msm_fb_data_type *mfd);
static int mdss_mdp_overlay_splash_parse_dt(struct msm_fb_data_type *mfd);
static void __overlay_kickoff_requeue(struct msm_fb_data_type *mfd);

static inline bool is_ov_right_blend(struct mdp_rect *left_blend,
	struct mdp_rect *right_blend, u32 left_lm_w)
@@ -613,6 +614,10 @@ static int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd,
			pipe->is_right_blend = true;
		}
	} else if (pipe->is_right_blend) {
		/*
		 * pipe used to be right blend need to update mixer
		 * configuration to remove it as a right blend
		 */
		mdss_mdp_mixer_pipe_unstage(pipe, pipe->mixer_left);
		mdss_mdp_mixer_pipe_unstage(pipe, pipe->mixer_right);
		pipe->is_right_blend = false;
@@ -660,9 +665,11 @@ static int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd,
				pipe->src_split_req = true;
			}
		} else {
			if (pipe->src_split_req)
			if (pipe->src_split_req) {
				mdss_mdp_mixer_pipe_unstage(pipe,
					pipe->mixer_right);
				pipe->mixer_right = NULL;
			}
			pipe->src_split_req = false;
		}
	}
@@ -942,6 +949,8 @@ static void mdss_mdp_overlay_cleanup(struct msm_fb_data_type *mfd)
{
	struct mdss_mdp_pipe *pipe, *tmp;
	struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
	struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd);
	bool recovery_mode = false;
	LIST_HEAD(destroy_pipes);

	mutex_lock(&mfd->lock);
@@ -950,7 +959,27 @@ static void mdss_mdp_overlay_cleanup(struct msm_fb_data_type *mfd)
		list_move(&pipe->cleanup_list, &destroy_pipes);

		/* make sure pipe fetch has been halted before freeing buffer */
		mdss_mdp_pipe_fetch_halt(pipe);
		if (mdss_mdp_pipe_fetch_halt(pipe)) {
			/*
			 * if pipe is not able to halt. Enter recovery mode,
			 * by un-staging any pipes that are attached to mixer
			 * so that any freed pipes that are not able to halt
			 * can be staged in solid fill mode and be reset
			 * with next vsync
			 */
			if (!recovery_mode) {
				recovery_mode = true;
				mdss_mdp_mixer_unstage_all(ctl->mixer_left);
				mdss_mdp_mixer_unstage_all(ctl->mixer_right);
			}
			pipe->params_changed++;
			mdss_mdp_pipe_queue_data(pipe, NULL);
		}
	}

	if (recovery_mode) {
		pr_warn("performing recovery sequence for fb%d\n", mfd->index);
		__overlay_kickoff_requeue(mfd);
	}

	__mdss_mdp_overlay_free_list_purge(mfd);
@@ -1136,61 +1165,13 @@ static void mdss_mdp_overlay_update_pm(struct mdss_overlay_private *mdp5_data)
	activate_event_timer(mdp5_data->cpu_pm_hdl, wakeup_time);
}

int mdss_mdp_overlay_kickoff(struct msm_fb_data_type *mfd,
				struct mdp_display_commit *data)
static int __overlay_queue_pipes(struct msm_fb_data_type *mfd)
{
	struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
	struct mdss_mdp_pipe *pipe;
	struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd);
	struct mdss_mdp_ctl *tmp;
	int ret = 0;
	int sd_in_pipe = 0;

	if (!ctl) {
		pr_warn("kickoff on fb=%d without a ctl attched\n", mfd->index);
		return ret;
	}

	if (ctl->shared_lock)
		mutex_lock(ctl->shared_lock);

	mutex_lock(&mdp5_data->ov_lock);
	mutex_lock(&mfd->lock);

	/*
	 * check if there is a secure display session
	 */
	list_for_each_entry(pipe, &mdp5_data->pipes_used, used_list) {
		if (pipe->flags & MDP_SECURE_DISPLAY_OVERLAY_SESSION) {
			sd_in_pipe = 1;
			pr_debug("Secure pipe: %u : %08X\n",
					pipe->num, pipe->flags);
		}
	}
	/*
	 * If there is no secure display session and sd_enabled, disable the
	 * secure display session
	 */
	if (!sd_in_pipe && mdp5_data->sd_enabled) {
		if (0 == mdss_mdp_overlay_sd_ctrl(mfd, 0))
			mdp5_data->sd_enabled = 0;
	}

	mdss_mdp_ctl_notify(ctl, MDP_NOTIFY_FRAME_BEGIN);
	mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);

	if (data)
		mdss_mdp_set_roi(ctl, data);

	/*
	 * Setup pipe in solid fill before unstaging,
	 * to ensure no fetches are happening after dettach or reattach.
	 */
	list_for_each_entry(pipe, &mdp5_data->pipes_cleanup, cleanup_list) {
		mdss_mdp_pipe_queue_data(pipe, NULL);
		mdss_mdp_mixer_pipe_unstage(pipe, pipe->mixer_left);
		mdss_mdp_mixer_pipe_unstage(pipe, pipe->mixer_right);
	}

	list_for_each_entry(pipe, &mdp5_data->pipes_used, used_list) {
		struct mdss_mdp_data *buf;
@@ -1221,17 +1202,13 @@ int mdss_mdp_overlay_kickoff(struct msm_fb_data_type *mfd,
			if (ret) {
				pr_err("can't reset DMA pipe ret=%d ctl=%d\n",
					ret, ctl->num);
				mutex_unlock(&mfd->lock);
				goto commit_fail;
				return ret;
			}

			tmp = mdss_mdp_ctl_mixer_switch(ctl,
					MDSS_MDP_WB_CTL_TYPE_LINE);
			if (!tmp) {
				mutex_unlock(&mfd->lock);
				ret = -EINVAL;
				goto commit_fail;
			}
			if (!tmp)
				return -EINVAL;
			pipe->mixer_left = mdss_mdp_mixer_get(tmp,
					MDSS_MDP_MIXER_MUX_DEFAULT);
		}
@@ -1260,6 +1237,74 @@ int mdss_mdp_overlay_kickoff(struct msm_fb_data_type *mfd,
		}
	}

	return 0;
}

static void __overlay_kickoff_requeue(struct msm_fb_data_type *mfd)
{
	struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd);

	mdss_mdp_display_commit(ctl, NULL);
	mdss_mdp_display_wait4comp(ctl);

	__overlay_queue_pipes(mfd);

	mdss_mdp_display_commit(ctl, NULL);
	mdss_mdp_display_wait4comp(ctl);
}

int mdss_mdp_overlay_kickoff(struct msm_fb_data_type *mfd,
				struct mdp_display_commit *data)
{
	struct mdss_overlay_private *mdp5_data = mfd_to_mdp5_data(mfd);
	struct mdss_mdp_pipe *pipe;
	struct mdss_mdp_ctl *ctl = mfd_to_ctl(mfd);
	int ret = 0;
	int sd_in_pipe = 0;

	if (ctl->shared_lock)
		mutex_lock(ctl->shared_lock);

	mutex_lock(&mdp5_data->ov_lock);
	mutex_lock(&mfd->lock);

	/*
	 * check if there is a secure display session
	 */
	list_for_each_entry(pipe, &mdp5_data->pipes_used, used_list) {
		if (pipe->flags & MDP_SECURE_DISPLAY_OVERLAY_SESSION) {
			sd_in_pipe = 1;
			pr_debug("Secure pipe: %u : %08X\n",
					pipe->num, pipe->flags);
		}
	}
	/*
	 * If there is no secure display session and sd_enabled, disable the
	 * secure display session
	 */
	if (!sd_in_pipe && mdp5_data->sd_enabled) {
		if (0 == mdss_mdp_overlay_sd_ctrl(mfd, 0))
			mdp5_data->sd_enabled = 0;
	}

	mdss_mdp_ctl_notify(ctl, MDP_NOTIFY_FRAME_BEGIN);
	mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false);

	if (data)
		mdss_mdp_set_roi(ctl, data);

	/*
	 * Setup pipe in solid fill before unstaging,
	 * to ensure no fetches are happening after dettach or reattach.
	 */
	list_for_each_entry(pipe, &mdp5_data->pipes_cleanup, cleanup_list) {
		mdss_mdp_pipe_queue_data(pipe, NULL);
		mdss_mdp_mixer_pipe_unstage(pipe, pipe->mixer_left);
		mdss_mdp_mixer_pipe_unstage(pipe, pipe->mixer_right);
	}

	ret = __overlay_queue_pipes(mfd);

	if (mfd->panel.type == WRITEBACK_PANEL)
		ret = mdss_mdp_wb_kickoff(mfd);
	else