diff options
author | Chris Wilson <chris@chris-wilson.co.uk> | 2010-12-03 10:37:31 -0500 |
---|---|---|
committer | Chris Wilson <chris@chris-wilson.co.uk> | 2010-12-03 12:50:55 -0500 |
commit | 47f1c6c9ffdec0c0e5a2c2709bd63c7380b325c4 (patch) | |
tree | cd3682cfff419c4d15639dbbb0f758c5ca0c91f2 /drivers/gpu | |
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
Diffstat (limited to 'drivers/gpu')
-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 9d3af3cb5a0b..e5badadbdcd2 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 = { | |||
5238 | .page_flip = intel_crtc_page_flip, | 5238 | .page_flip = intel_crtc_page_flip, |
5239 | }; | 5239 | }; |
5240 | 5240 | ||
5241 | static void intel_sanitize_modesetting(struct drm_device *dev, | ||
5242 | int pipe, int plane) | ||
5243 | { | ||
5244 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
5245 | u32 reg, val; | ||
5246 | |||
5247 | if (HAS_PCH_SPLIT(dev)) | ||
5248 | return; | ||
5249 | |||
5250 | /* Who knows what state these registers were left in by the BIOS or | ||
5251 | * grub? | ||
5252 | * | ||
5253 | * If we leave the registers in a conflicting state (e.g. with the | ||
5254 | * display plane reading from the other pipe than the one we intend | ||
5255 | * to use) then when we attempt to teardown the active mode, we will | ||
5256 | * not disable the pipes and planes in the correct order -- leaving | ||
5257 | * a plane reading from a disabled pipe and possibly leading to | ||
5258 | * undefined behaviour. | ||
5259 | */ | ||
5260 | |||
5261 | reg = DSPCNTR(plane); | ||
5262 | val = I915_READ(reg); | ||
5263 | |||
5264 | if ((val & DISPLAY_PLANE_ENABLE) == 0) | ||
5265 | return; | ||
5266 | if (!!(val & DISPPLANE_SEL_PIPE_MASK) == pipe) | ||
5267 | return; | ||
5268 | |||
5269 | /* This display plane is active and attached to the other CPU pipe. */ | ||
5270 | pipe = !pipe; | ||
5271 | |||
5272 | /* Disable the plane and wait for it to stop reading from the pipe. */ | ||
5273 | I915_WRITE(reg, val & ~DISPLAY_PLANE_ENABLE); | ||
5274 | intel_flush_display_plane(dev, plane); | ||
5275 | |||
5276 | if (IS_GEN2(dev)) | ||
5277 | intel_wait_for_vblank(dev, pipe); | ||
5278 | |||
5279 | if (pipe == 0 && (dev_priv->quirks & QUIRK_PIPEA_FORCE)) | ||
5280 | return; | ||
5281 | |||
5282 | /* Switch off the pipe. */ | ||
5283 | reg = PIPECONF(pipe); | ||
5284 | val = I915_READ(reg); | ||
5285 | if (val & PIPECONF_ENABLE) { | ||
5286 | I915_WRITE(reg, val & ~PIPECONF_ENABLE); | ||
5287 | intel_wait_for_pipe_off(dev, pipe); | ||
5288 | } | ||
5289 | } | ||
5241 | 5290 | ||
5242 | static void intel_crtc_init(struct drm_device *dev, int pipe) | 5291 | static void intel_crtc_init(struct drm_device *dev, int pipe) |
5243 | { | 5292 | { |
@@ -5289,6 +5338,8 @@ static void intel_crtc_init(struct drm_device *dev, int pipe) | |||
5289 | 5338 | ||
5290 | setup_timer(&intel_crtc->idle_timer, intel_crtc_idle_timer, | 5339 | setup_timer(&intel_crtc->idle_timer, intel_crtc_idle_timer, |
5291 | (unsigned long)intel_crtc); | 5340 | (unsigned long)intel_crtc); |
5341 | |||
5342 | intel_sanitize_modesetting(dev, intel_crtc->pipe, intel_crtc->plane); | ||
5292 | } | 5343 | } |
5293 | 5344 | ||
5294 | int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data, | 5345 | int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data, |