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

Commit 47f1c6c9 authored by Chris Wilson's avatar Chris Wilson
Browse files

drm/i915: Clean conflicting modesetting registers upon init



If we leave the registers in a conflicting state then when we attempt
to teardown the active mode, we will not disable the pipes and planes
in the correct order -- leaving a plane reading from a disabled pipe and
possibly leading to undefined behaviour.

Reported-and-tested-by: default avatarAndy Whitcroft <apw@canonical.com>
Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=32078


Signed-off-by: default avatarChris Wilson <chris@chris-wilson.co.uk>
Cc: stable@kernel.org
parent 5bddd17f
Loading
Loading
Loading
Loading
+51 −0
Original line number Diff line number Diff line
@@ -5238,6 +5238,55 @@ static const struct drm_crtc_funcs intel_crtc_funcs = {
	.page_flip = intel_crtc_page_flip,
};

static void intel_sanitize_modesetting(struct drm_device *dev,
				       int pipe, int plane)
{
	struct drm_i915_private *dev_priv = dev->dev_private;
	u32 reg, val;

	if (HAS_PCH_SPLIT(dev))
		return;

	/* Who knows what state these registers were left in by the BIOS or
	 * grub?
	 *
	 * If we leave the registers in a conflicting state (e.g. with the
	 * display plane reading from the other pipe than the one we intend
	 * to use) then when we attempt to teardown the active mode, we will
	 * not disable the pipes and planes in the correct order -- leaving
	 * a plane reading from a disabled pipe and possibly leading to
	 * undefined behaviour.
	 */

	reg = DSPCNTR(plane);
	val = I915_READ(reg);

	if ((val & DISPLAY_PLANE_ENABLE) == 0)
		return;
	if (!!(val & DISPPLANE_SEL_PIPE_MASK) == pipe)
		return;

	/* This display plane is active and attached to the other CPU pipe. */
	pipe = !pipe;

	/* Disable the plane and wait for it to stop reading from the pipe. */
	I915_WRITE(reg, val & ~DISPLAY_PLANE_ENABLE);
	intel_flush_display_plane(dev, plane);

	if (IS_GEN2(dev))
		intel_wait_for_vblank(dev, pipe);

	if (pipe == 0 && (dev_priv->quirks & QUIRK_PIPEA_FORCE))
		return;

	/* Switch off the pipe. */
	reg = PIPECONF(pipe);
	val = I915_READ(reg);
	if (val & PIPECONF_ENABLE) {
		I915_WRITE(reg, val & ~PIPECONF_ENABLE);
		intel_wait_for_pipe_off(dev, pipe);
	}
}

static void intel_crtc_init(struct drm_device *dev, int pipe)
{
@@ -5289,6 +5338,8 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)

	setup_timer(&intel_crtc->idle_timer, intel_crtc_idle_timer,
		    (unsigned long)intel_crtc);

	intel_sanitize_modesetting(dev, intel_crtc->pipe, intel_crtc->plane);
}

int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data,