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

Commit 29544fd9 authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

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

parents 72385a55 569d5af9
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