Commit 99d736a2 authored by Maarten Lankhorst's avatar Maarten Lankhorst Committed by Jani Nikula
Browse files

drm/i915: Calculate haswell plane workaround, v5.



This needs to be done last after all modesets have been calculated.

A modeset first disables all crtc's, so any crtc that undergoes a
modeset counts as inactive.

If no modeset's done, or > 1 crtc's stay w/a doesn't apply.
Apply workaround on the first crtc if 1 crtc stays active.
Apply workaround on the second crtc if no crtc was active.

Changes since v1:
 - Use intel_crtc->atomic as a place to put hsw_workaround_pipe.
 - Make sure quirk only applies to haswell.
 - Use first loop to iterate over newly enabled crtc's only.
   This increases readability.
Changes since v2:
 - Move hsw_workaround_pipe back to crtc_state.
Changes since v3:
 - Return errors from haswell_mode_set_planes_workaround.
Changes since v4:
- Clean up commit message.

Signed-off-by: default avatarMaarten Lankhorst <maarten.lankhorst@linux.intel.com>
Reviewed-by: default avatarMatt Roper <matthew.d.roper@intel.com>
Signed-off-by: default avatarJani Nikula <jani.nikula@intel.com>
parent f77076c9
Loading
Loading
Loading
Loading
+81 −32
Original line number Diff line number Diff line
@@ -4864,42 +4864,15 @@ static bool hsw_crtc_supports_ips(struct intel_crtc *crtc)
	return HAS_IPS(crtc->base.dev) && crtc->pipe == PIPE_A;
}

/*
 * This implements the workaround described in the "notes" section of the mode
 * set sequence documentation. When going from no pipes or single pipe to
 * multiple pipes, and planes are enabled after the pipe, we need to wait at
 * least 2 vblanks on the first pipe before enabling planes on the second pipe.
 */
static void haswell_mode_set_planes_workaround(struct intel_crtc *crtc)
{
	struct drm_device *dev = crtc->base.dev;
	struct intel_crtc *crtc_it, *other_active_crtc = NULL;

	/* We want to get the other_active_crtc only if there's only 1 other
	 * active crtc. */
	for_each_intel_crtc(dev, crtc_it) {
		if (!crtc_it->active || crtc_it == crtc)
			continue;

		if (other_active_crtc)
			return;

		other_active_crtc = crtc_it;
	}
	if (!other_active_crtc)
		return;

	intel_wait_for_vblank(dev, other_active_crtc->pipe);
	intel_wait_for_vblank(dev, other_active_crtc->pipe);
}

static void haswell_crtc_enable(struct drm_crtc *crtc)
{
	struct drm_device *dev = crtc->dev;
	struct drm_i915_private *dev_priv = dev->dev_private;
	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
	struct intel_encoder *encoder;
	int pipe = intel_crtc->pipe;
	int pipe = intel_crtc->pipe, hsw_workaround_pipe;
	struct intel_crtc_state *pipe_config =
		to_intel_crtc_state(crtc->state);

	if (WARN_ON(intel_crtc->active))
		return;
@@ -4976,7 +4949,11 @@ static void haswell_crtc_enable(struct drm_crtc *crtc)

	/* If we change the relative order between pipe/planes enabling, we need
	 * to change the workaround. */
	haswell_mode_set_planes_workaround(intel_crtc);
	hsw_workaround_pipe = pipe_config->hsw_workaround_pipe;
	if (IS_HASWELL(dev) && hsw_workaround_pipe != INVALID_PIPE) {
		intel_wait_for_vblank(dev, hsw_workaround_pipe);
		intel_wait_for_vblank(dev, hsw_workaround_pipe);
	}
}

static void ironlake_pfit_disable(struct intel_crtc *crtc)
@@ -12818,6 +12795,71 @@ static int intel_modeset_setup_plls(struct drm_atomic_state *state)
	return ret;
}

/*
 * This implements the workaround described in the "notes" section of the mode
 * set sequence documentation. When going from no pipes or single pipe to
 * multiple pipes, and planes are enabled after the pipe, we need to wait at
 * least 2 vblanks on the first pipe before enabling planes on the second pipe.
 */
static int haswell_mode_set_planes_workaround(struct drm_atomic_state *state)
{
	struct drm_crtc_state *crtc_state;
	struct intel_crtc *intel_crtc;
	struct drm_crtc *crtc;
	struct intel_crtc_state *first_crtc_state = NULL;
	struct intel_crtc_state *other_crtc_state = NULL;
	enum pipe first_pipe = INVALID_PIPE, enabled_pipe = INVALID_PIPE;
	int i;

	/* look at all crtc's that are going to be enabled in during modeset */
	for_each_crtc_in_state(state, crtc, crtc_state, i) {
		intel_crtc = to_intel_crtc(crtc);

		if (!crtc_state->active || !needs_modeset(crtc_state))
			continue;

		if (first_crtc_state) {
			other_crtc_state = to_intel_crtc_state(crtc_state);
			break;
		} else {
			first_crtc_state = to_intel_crtc_state(crtc_state);
			first_pipe = intel_crtc->pipe;
		}
	}

	/* No workaround needed? */
	if (!first_crtc_state)
		return 0;

	/* w/a possibly needed, check how many crtc's are already enabled. */
	for_each_intel_crtc(state->dev, intel_crtc) {
		struct intel_crtc_state *pipe_config;

		pipe_config = intel_atomic_get_crtc_state(state, intel_crtc);
		if (IS_ERR(pipe_config))
			return PTR_ERR(pipe_config);

		pipe_config->hsw_workaround_pipe = INVALID_PIPE;

		if (!pipe_config->base.active ||
		    needs_modeset(&pipe_config->base))
			continue;

		/* 2 or more enabled crtcs means no need for w/a */
		if (enabled_pipe != INVALID_PIPE)
			return 0;

		enabled_pipe = intel_crtc->pipe;
	}

	if (enabled_pipe != INVALID_PIPE)
		first_crtc_state->hsw_workaround_pipe = enabled_pipe;
	else if (other_crtc_state)
		other_crtc_state->hsw_workaround_pipe = first_pipe;

	return 0;
}

/* Code that should eventually be part of atomic_check() */
static int intel_modeset_checks(struct drm_atomic_state *state)
{
@@ -12841,7 +12883,14 @@ static int intel_modeset_checks(struct drm_atomic_state *state)
			return ret;
	}

	return intel_modeset_setup_plls(state);
	ret = intel_modeset_setup_plls(state);
	if (ret)
		return ret;

	if (IS_HASWELL(dev))
		ret = haswell_mode_set_planes_workaround(state);

	return ret;
}

static int
+3 −0
Original line number Diff line number Diff line
@@ -443,6 +443,9 @@ struct intel_crtc_state {
	int pbn;

	struct intel_crtc_scaler_state scaler_state;

	/* w/a for waiting 2 vblanks during crtc enable */
	enum pipe hsw_workaround_pipe;
};

struct intel_pipe_wm {