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

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

drm/msm/sde: add hardware reset during kickoff errors



Attempt to reset hardware in the event of detected errors
during the commit kickoff. The current commit configuration
is potentially discarded, along with any pending inline
rotations.

Change-Id: I882c93537fa56c28d4ab88b6ca43116a6434ea25
Signed-off-by: default avatarClarence Ip <cip@codeaurora.org>
parent d0c491f1
Loading
Loading
Loading
Loading
+124 −8
Original line number Diff line number Diff line
@@ -1432,7 +1432,7 @@ static void _sde_crtc_swap_mixers_for_right_partial_update(
 * _sde_crtc_blend_setup - configure crtc mixers
 * @crtc: Pointer to drm crtc structure
 */
static void _sde_crtc_blend_setup(struct drm_crtc *crtc)
static void _sde_crtc_blend_setup(struct drm_crtc *crtc, bool add_planes)
{
	struct sde_crtc *sde_crtc;
	struct sde_crtc_state *sde_crtc_state;
@@ -1478,6 +1478,7 @@ static void _sde_crtc_blend_setup(struct drm_crtc *crtc)
	/* initialize stage cfg */
	memset(&sde_crtc->stage_cfg, 0, sizeof(struct sde_hw_stage_cfg));

	if (add_planes)
		_sde_crtc_blend_setup_mixer(crtc, sde_crtc, mixer);

	for (i = 0; i < sde_crtc->num_mixers; i++) {
@@ -3093,7 +3094,7 @@ static void sde_crtc_atomic_begin(struct drm_crtc *crtc,
	if (unlikely(!sde_crtc->num_mixers))
		return;

	_sde_crtc_blend_setup(crtc);
	_sde_crtc_blend_setup(crtc, true);
	_sde_crtc_dest_scaler_setup(crtc);

	/*
@@ -3384,7 +3385,104 @@ static void _sde_crtc_remove_pipe_flush(struct sde_crtc *sde_crtc)
	}
}

void sde_crtc_commit_kickoff(struct drm_crtc *crtc)
/**
 * _sde_crtc_reset_hw - attempt hardware reset on errors
 * @crtc: Pointer to DRM crtc instance
 * @old_state: Pointer to crtc state for previous commit
 * Returns: Zero if current commit should still be attempted
 */
static int _sde_crtc_reset_hw(struct drm_crtc *crtc,
		struct drm_crtc_state *old_state)
{
	struct drm_plane *plane_halt[MAX_PLANES];
	struct drm_plane *plane;
	const struct drm_plane_state *pstate;
	struct sde_crtc *sde_crtc;
	struct sde_hw_ctl *ctl;
	signed int i, plane_count;
	int rc;

	if (!crtc || !old_state)
		return -EINVAL;
	sde_crtc = to_sde_crtc(crtc);

	for (i = 0; i < sde_crtc->num_mixers; ++i) {
		ctl = sde_crtc->mixers[i].hw_ctl;
		if (!ctl || !ctl->ops.reset)
			continue;

		rc = ctl->ops.reset(ctl);
		if (rc) {
			SDE_DEBUG("crtc%d: ctl%d reset failure\n",
					crtc->base.id, ctl->idx - CTL_0);
			SDE_EVT32(DRMID(crtc), ctl->idx - CTL_0,
					SDE_EVTLOG_ERROR);
			break;
		}
	}

	/* early out if simple ctl reset succeeded */
	if (i == sde_crtc->num_mixers) {
		SDE_EVT32(DRMID(crtc), i);
		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) {
		ctl = sde_crtc->mixers[i].hw_ctl;
		if (!ctl || !ctl->ops.hard_reset)
			continue;

		SDE_EVT32(DRMID(crtc), ctl->idx - CTL_0);
		ctl->ops.hard_reset(ctl, true);
	}

	plane_count = 0;
	drm_atomic_crtc_state_for_each_plane(plane, old_state) {
		if (plane_count >= ARRAY_SIZE(plane_halt))
			break;

		plane_halt[plane_count++] = plane;
		sde_plane_halt_requests(plane, true);
		sde_plane_set_revalidate(plane, true);
	}

	/* reset both previous... */
	for_each_plane_in_state(old_state->state, plane, pstate, i) {
		if (pstate->crtc != crtc)
			continue;

		sde_plane_reset_rot(plane, (struct drm_plane_state *)pstate);
	}

	/* ...and current rotation attempts, if applicable */
	drm_atomic_crtc_for_each_plane(plane, crtc) {
		pstate = plane->state;
		if (!pstate)
			continue;

		sde_plane_reset_rot(plane, (struct drm_plane_state *)pstate);
	}

	/* 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) {
		ctl = sde_crtc->mixers[i].hw_ctl;
		if (!ctl || !ctl->ops.hard_reset)
			continue;

		ctl->ops.hard_reset(ctl, false);
	}

	return -EAGAIN;
}

void sde_crtc_commit_kickoff(struct drm_crtc *crtc,
		struct drm_crtc_state *old_state)
{
	struct drm_encoder *encoder;
	struct drm_device *dev;
@@ -3392,7 +3490,7 @@ void sde_crtc_commit_kickoff(struct drm_crtc *crtc)
	struct msm_drm_private *priv;
	struct sde_kms *sde_kms;
	struct sde_crtc_state *cstate;
	bool is_error;
	bool is_error, reset_req;
	int ret;

	if (!crtc) {
@@ -3403,6 +3501,7 @@ void sde_crtc_commit_kickoff(struct drm_crtc *crtc)
	sde_crtc = to_sde_crtc(crtc);
	sde_kms = _sde_crtc_get_kms(crtc);
	is_error = false;
	reset_req = false;

	if (!sde_kms || !sde_kms->dev || !sde_kms->dev->dev_private) {
		SDE_ERROR("invalid argument\n");
@@ -3439,7 +3538,8 @@ void sde_crtc_commit_kickoff(struct drm_crtc *crtc)
		params.inline_rotate_prefill = cstate->sbuf_prefill_line;
		params.affected_displays = _sde_crtc_get_displays_affected(crtc,
				crtc->state);
		sde_encoder_prepare_for_kickoff(encoder, &params);
		if (sde_encoder_prepare_for_kickoff(encoder, &params))
			reset_req = true;

		/*
		 * For inline ASYNC modes, the flush bits are not written
@@ -3462,6 +3562,20 @@ void sde_crtc_commit_kickoff(struct drm_crtc *crtc)
		if (_sde_crtc_commit_kickoff_rot(crtc, cstate))
			is_error = true;

	/*
	 * Optionally attempt h/w recovery if any errors were detected while
	 * preparing for the kickoff
	 */
	if (reset_req) {
		if (_sde_crtc_reset_hw(crtc, old_state))
			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;
	}

	/* wait for frame_event_done completion */
	SDE_ATRACE_BEGIN("wait_for_frame_done_event");
	ret = _sde_crtc_wait_for_frame_done(crtc);
@@ -3502,14 +3616,16 @@ void sde_crtc_commit_kickoff(struct drm_crtc *crtc)

	sde_vbif_clear_errors(sde_kms);

	if (is_error)
	if (is_error) {
		_sde_crtc_remove_pipe_flush(sde_crtc);
		_sde_crtc_blend_setup(crtc, false);
	}

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

		sde_encoder_kickoff(encoder, is_error);
		sde_encoder_kickoff(encoder, false);
	}

	reinit_completion(&sde_crtc->frame_done_comp);
+3 −1
Original line number Diff line number Diff line
@@ -508,8 +508,10 @@ int sde_crtc_vblank(struct drm_crtc *crtc, bool en);
/**
 * sde_crtc_commit_kickoff - trigger kickoff of the commit for this crtc
 * @crtc: Pointer to drm crtc object
 * @old_state: Pointer to drm crtc old state object
 */
void sde_crtc_commit_kickoff(struct drm_crtc *crtc);
void sde_crtc_commit_kickoff(struct drm_crtc *crtc,
		struct drm_crtc_state *old_state);

/**
 * sde_crtc_prepare_commit - callback to prepare for output fences
+6 −11
Original line number Diff line number Diff line
@@ -2466,11 +2466,12 @@ static inline void _sde_encoder_trigger_flush(struct drm_encoder *drm_enc,
	phys->ops.trigger_flush(phys);

	if (ctl->ops.get_pending_flush)
		SDE_EVT32(DRMID(drm_enc), phys->intf_idx, pending_kickoff_cnt,
			ctl->idx, ctl->ops.get_pending_flush(ctl));
		SDE_EVT32(DRMID(drm_enc), phys->intf_idx - INTF_0,
			pending_kickoff_cnt, ctl->idx - CTL_0,
			ctl->ops.get_pending_flush(ctl));
	else
		SDE_EVT32(DRMID(drm_enc), phys->intf_idx, ctl->idx,
						pending_kickoff_cnt);
		SDE_EVT32(DRMID(drm_enc), phys->intf_idx - INTF_0,
			ctl->idx - CTL_0, pending_kickoff_cnt);
}

/**
@@ -2529,7 +2530,7 @@ void sde_encoder_helper_trigger_start(struct sde_encoder_phys *phys_enc)
	ctl = phys_enc->hw_ctl;
	if (ctl && ctl->ops.trigger_start) {
		ctl->ops.trigger_start(ctl);
		SDE_EVT32(DRMID(phys_enc->parent), ctl->idx);
		SDE_EVT32(DRMID(phys_enc->parent), ctl->idx - CTL_0);
	}
}

@@ -2594,12 +2595,6 @@ void sde_encoder_helper_hw_reset(struct sde_encoder_phys *phys_enc)
		}
	}

	rc = ctl->ops.reset(ctl);
	if (rc) {
		SDE_ERROR_ENC(sde_enc, "ctl %d reset failure\n",  ctl->idx);
		SDE_DBG_DUMP("all", "dbg_bus", "vbif_dbg_bus", "panic");
	}

	phys_enc->enable_state = SDE_ENC_ENABLED;
}

+2 −0
Original line number Diff line number Diff line
@@ -296,6 +296,7 @@ static inline int sde_encoder_phys_inc_pending(struct sde_encoder_phys *phys)
 * @hw_intf:	Hardware interface to the intf registers
 * @timing_params: Current timing parameter
 * @rot_fetch:	Prefill for inline rotation
 * @error_count: Number of consecutive kickoffs that experienced an error
 * @rot_fetch_valid: true if rot_fetch is updated (reset in enc enable)
 */
struct sde_encoder_phys_vid {
@@ -303,6 +304,7 @@ struct sde_encoder_phys_vid {
	struct sde_hw_intf *hw_intf;
	struct intf_timing_params timing_params;
	struct intf_prog_fetch rot_fetch;
	int error_count;
	bool rot_fetch_valid;
};

+12 −11
Original line number Diff line number Diff line
@@ -32,7 +32,7 @@
#define to_sde_encoder_phys_cmd(x) \
	container_of(x, struct sde_encoder_phys_cmd, base)

#define PP_TIMEOUT_MAX_TRIALS	10
#define PP_TIMEOUT_MAX_TRIALS	2

/*
 * Tearcheck sync start and continue thresholds are empirically found
@@ -418,26 +418,26 @@ static int _sde_encoder_phys_cmd_handle_ppdone_timeout(
			to_sde_encoder_phys_cmd(phys_enc);
	u32 frame_event = SDE_ENCODER_FRAME_EVENT_ERROR
				| SDE_ENCODER_FRAME_EVENT_SIGNAL_RELEASE_FENCE;
	bool do_log = false;

	if (!phys_enc || !phys_enc->hw_pp || !phys_enc->hw_ctl)
		return -EINVAL;

	cmd_enc->pp_timeout_report_cnt++;
	if (cmd_enc->pp_timeout_report_cnt == PP_TIMEOUT_MAX_TRIALS) {
		frame_event |= SDE_ENCODER_FRAME_EVENT_PANEL_DEAD;
		do_log = true;
	} else if (cmd_enc->pp_timeout_report_cnt == 1) {
		do_log = true;
	}

	SDE_EVT32(DRMID(phys_enc->parent), phys_enc->hw_pp->idx - PINGPONG_0,
			cmd_enc->pp_timeout_report_cnt,
			atomic_read(&phys_enc->pending_kickoff_cnt),
			frame_event);

	if (cmd_enc->pp_timeout_report_cnt >= PP_TIMEOUT_MAX_TRIALS) {
		cmd_enc->pp_timeout_report_cnt = PP_TIMEOUT_MAX_TRIALS;
		frame_event |= SDE_ENCODER_FRAME_EVENT_PANEL_DEAD;

		sde_encoder_helper_unregister_irq(phys_enc, INTR_IDX_RDPTR);
		SDE_DBG_DUMP("panic");
		sde_encoder_helper_register_irq(phys_enc, INTR_IDX_RDPTR);
	} else if (cmd_enc->pp_timeout_report_cnt == 1) {
		/* to avoid flooding, only log first time, and "dead" time */
	if (do_log) {
		SDE_ERROR_CMDENC(cmd_enc,
				"pp:%d kickoff timed out ctl %d cnt %d koff_cnt %d\n",
				phys_enc->hw_pp->idx - PINGPONG_0,
@@ -448,7 +448,8 @@ static int _sde_encoder_phys_cmd_handle_ppdone_timeout(
		SDE_EVT32(DRMID(phys_enc->parent), SDE_EVTLOG_FATAL);

		sde_encoder_helper_unregister_irq(phys_enc, INTR_IDX_RDPTR);
		SDE_DBG_DUMP("all", "dbg_bus", "vbif_dbg_bus", "panic");
		SDE_DBG_DUMP("all", "dbg_bus", "vbif_dbg_bus");
		sde_encoder_helper_register_irq(phys_enc, INTR_IDX_RDPTR);
	}

	atomic_add_unless(&phys_enc->pending_kickoff_cnt, -1, 0);
Loading