diff options
author | Chris Wilson <chris@chris-wilson.co.uk> | 2010-12-03 15:37:31 +0000 |
---|---|---|
committer | Chris Wilson <chris@chris-wilson.co.uk> | 2010-12-03 17:50:55 +0000 |
commit | 47f1c6c9ffdec0c0e5a2c2709bd63c7380b325c4 (patch) | |
tree | cd3682cfff419c4d15639dbbb0f758c5ca0c91f2 | |
parent | 5bddd17fec58f253cddd0bc9eab2cd9eb1bbab4a (diff) |
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: Andy Whitcroft <apw@canonical.com>
Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=32078
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Cc: stable@kernel.org
-rw-r--r-- | drivers/gpu/drm/i915/intel_display.c | 51 |
1 files changed, 51 insertions, 0 deletions
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 9d3af3cb5a0..e5badadbdcd 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -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, |