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

Commit f3f00696 authored by Clarence Ip's avatar Clarence Ip Committed by Gerrit - the friendly Code Review server
Browse files

drm/msm/sde: flush recovery commit while in reset



For video mode panels, a vsync may be received after
the hardware recovery has completed, but before the
final "no pipes" commit has been flushed by the driver.
This can result in the hardware retrying the previous
commit configuration.

To avoid this, prepare the "no pipes" commit while the
hardware is still held in reset, and attempt to complete
the "no pipes" flush near the start of the first frame
after reset is released.

Change-Id: Icb071219ef1ec2a3756edd6ea33ad487c9ad0233
Signed-off-by: default avatarClarence Ip <cip@codeaurora.org>
parent 37857c01
Loading
Loading
Loading
Loading
+37 −14
Original line number Diff line number Diff line
@@ -3497,13 +3497,14 @@ static void _sde_crtc_remove_pipe_flush(struct sde_crtc *sde_crtc)
{
	struct sde_crtc_mixer *mixer;
	struct sde_hw_ctl *ctl;
	u32 i, flush_mask;
	u32 i, n, flush_mask;

	if (!sde_crtc)
		return;

	mixer = sde_crtc->mixers;
	for (i = 0; i < sde_crtc->num_mixers; i++) {
	n = min_t(size_t, sde_crtc->num_mixers, ARRAY_SIZE(sde_crtc->mixers));
	for (i = 0; i < n; i++) {
		ctl = mixer[i].hw_ctl;
		if (!ctl || !ctl->ops.get_pending_flush ||
				!ctl->ops.clear_pending_flush ||
@@ -3529,16 +3530,19 @@ static int _sde_crtc_reset_hw(struct drm_crtc *crtc,
{
	struct drm_plane *plane_halt[MAX_PLANES];
	struct drm_plane *plane;
	struct drm_encoder *encoder;
	const struct drm_plane_state *pstate;
	struct sde_crtc *sde_crtc;
	struct sde_crtc_state *cstate;
	struct sde_hw_ctl *ctl;
	enum sde_ctl_rot_op_mode old_rot_op_mode;
	signed int i, plane_count;
	signed int i, n, plane_count;
	int rc;

	if (!crtc || !old_state)
	if (!crtc || !crtc->dev || !old_state || !crtc->state)
		return -EINVAL;
	sde_crtc = to_sde_crtc(crtc);
	cstate = to_sde_crtc_state(crtc->state);

	old_rot_op_mode = to_sde_crtc_state(old_state)->sbuf_cfg.rot_op_mode;
	SDE_EVT32(DRMID(crtc), old_rot_op_mode,
@@ -3550,7 +3554,8 @@ static int _sde_crtc_reset_hw(struct drm_crtc *crtc,
	/* optionally generate a panic instead of performing a h/w reset */
	SDE_DBG_CTRL("stop_ftrace", "reset_hw_panic");

	for (i = 0; i < sde_crtc->num_mixers; ++i) {
	n = min_t(size_t, sde_crtc->num_mixers, ARRAY_SIZE(sde_crtc->mixers));
	for (i = 0; i < n; ++i) {
		ctl = sde_crtc->mixers[i].hw_ctl;
		if (!ctl || !ctl->ops.reset)
			continue;
@@ -3575,14 +3580,13 @@ static int _sde_crtc_reset_hw(struct drm_crtc *crtc,
	 * depending on the rotation mode; don't handle this for now
	 * and just force a hard reset in those cases.
	 */
	if (i == sde_crtc->num_mixers &&
			old_rot_op_mode == SDE_CTL_ROT_OP_MODE_OFFLINE)
	if (i == n && old_rot_op_mode == SDE_CTL_ROT_OP_MODE_OFFLINE)
		return false;

	SDE_DEBUG("crtc%d: issuing hard reset\n", DRMID(crtc));

	/* force all components in the system into reset at the same time */
	for (i = 0; i < sde_crtc->num_mixers; ++i) {
	for (i = 0; i < n; ++i) {
		ctl = sde_crtc->mixers[i].hw_ctl;
		if (!ctl || !ctl->ops.hard_reset)
			continue;
@@ -3618,11 +3622,26 @@ static int _sde_crtc_reset_hw(struct drm_crtc *crtc,
		sde_plane_reset_rot(plane, (struct drm_plane_state *)pstate);
	}

	/* provide safe "border color only" commit configuration for later */
	cstate->sbuf_cfg.rot_op_mode = SDE_CTL_ROT_OP_MODE_OFFLINE;
	_sde_crtc_commit_kickoff_rot(crtc, cstate);
	_sde_crtc_remove_pipe_flush(sde_crtc);
	_sde_crtc_blend_setup(crtc, false);

	/* take h/w components out of reset */
	for (i = plane_count - 1; i >= 0; --i)
		sde_plane_halt_requests(plane_halt[i], false);

	for (i = 0; i < sde_crtc->num_mixers; ++i) {
	/* attempt to poll for start of frame cycle before reset release */
	list_for_each_entry(encoder,
			&crtc->dev->mode_config.encoder_list, head) {
		if (encoder->crtc != crtc)
			continue;
		if (sde_encoder_get_intf_mode(encoder) == INTF_MODE_VIDEO)
			sde_encoder_poll_line_counts(encoder);
	}

	for (i = 0; i < n; ++i) {
		ctl = sde_crtc->mixers[i].hw_ctl;
		if (!ctl || !ctl->ops.hard_reset)
			continue;
@@ -3630,6 +3649,15 @@ static int _sde_crtc_reset_hw(struct drm_crtc *crtc,
		ctl->ops.hard_reset(ctl, false);
	}

	list_for_each_entry(encoder,
			&crtc->dev->mode_config.encoder_list, head) {
		if (encoder->crtc != crtc)
			continue;

		if (sde_encoder_get_intf_mode(encoder) == INTF_MODE_VIDEO)
			sde_encoder_kickoff(encoder, false);
	}

	return -EAGAIN;
}

@@ -3754,11 +3782,6 @@ void sde_crtc_commit_kickoff(struct drm_crtc *crtc,
		if (_sde_crtc_reset_hw(crtc, old_state,
					!sde_crtc->reset_request))
			is_error = true;

		/* force offline rotation mode since the commit has no pipes */
		if (is_error)
			cstate->sbuf_cfg.rot_op_mode =
				SDE_CTL_ROT_OP_MODE_OFFLINE;
	}
	sde_crtc->reset_request = reset_req;

+45 −0
Original line number Diff line number Diff line
@@ -3443,6 +3443,51 @@ static void sde_encoder_vsync_event_work_handler(struct kthread_work *work)
	_sde_encoder_power_enable(sde_enc, false);
}

int sde_encoder_poll_line_counts(struct drm_encoder *drm_enc)
{
	static const uint64_t timeout_us = 50000;
	static const uint64_t sleep_us = 20;
	struct sde_encoder_virt *sde_enc;
	ktime_t cur_ktime, exp_ktime;
	uint32_t line_count, tmp, i;

	if (!drm_enc) {
		SDE_ERROR("invalid encoder\n");
		return -EINVAL;
	}
	sde_enc = to_sde_encoder_virt(drm_enc);
	if (!sde_enc->cur_master ||
			!sde_enc->cur_master->ops.get_line_count) {
		SDE_DEBUG_ENC(sde_enc, "can't get master line count\n");
		SDE_EVT32(DRMID(drm_enc), SDE_EVTLOG_ERROR);
		return -EINVAL;
	}

	exp_ktime = ktime_add_ms(ktime_get(), timeout_us / 1000);

	line_count = sde_enc->cur_master->ops.get_line_count(
			sde_enc->cur_master);

	for (i = 0; i < (timeout_us * 2 / sleep_us); ++i) {
		tmp = line_count;
		line_count = sde_enc->cur_master->ops.get_line_count(
				sde_enc->cur_master);
		if (line_count < tmp) {
			SDE_EVT32(DRMID(drm_enc), line_count);
			return 0;
		}

		cur_ktime = ktime_get();
		if (ktime_compare_safe(exp_ktime, cur_ktime) <= 0)
			break;

		usleep_range(sleep_us / 2, sleep_us);
	}

	SDE_EVT32(DRMID(drm_enc), line_count, SDE_EVTLOG_ERROR);
	return -ETIMEDOUT;
}

int sde_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc,
		struct sde_encoder_kickoff_params *params)
{
+7 −0
Original line number Diff line number Diff line
@@ -107,6 +107,13 @@ void sde_encoder_register_frame_event_callback(struct drm_encoder *encoder,
 */
struct sde_rsc_client *sde_encoder_get_rsc_client(struct drm_encoder *encoder);

/**
 * sde_encoder_poll_line_counts - poll encoder line counts for start of frame
 * @encoder:	encoder pointer
 * @Returns:	zero on success
 */
int sde_encoder_poll_line_counts(struct drm_encoder *encoder);

/**
 * sde_encoder_prepare_for_kickoff - schedule double buffer flip of the ctl
 *	path (i.e. ctl flush and start) at next appropriate time.