summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMario Kleiner <mario.kleiner.de@gmail.com>2014-07-29 04:36:44 +0400
committerBen Skeggs <bskeggs@redhat.com>2014-08-09 23:12:51 +0400
commit9cba5efab5a8145ae6c52ea273553f069c294482 (patch)
tree50e8cf26a36ad9619201574bac162fde64abe429
parent95713d4a16ad46ce9dd8ccef45cc67af0f161ae2 (diff)
downloadlinux-9cba5efab5a8145ae6c52ea273553f069c294482.tar.xz
drm/nouveau: Dis/Enable vblank irqs during suspend/resume.
Vblank irqs don't get disabled during suspend or driver unload, which causes irq delivery after "suspend" or driver unload, at least until the gpu is powered off. This could race with drm_vblank_cleanup() in the case of nouveau and cause a use-after-free bug if the driver is unloaded. More annoyingly during everyday use, at least on nv50 display engine (likely also others), vblank irqs are off after a resume from suspend, but the drm doesn't know this, so all vblank related functionality is dead after a resume. E.g., all windowed OpenGL clients will hang at swapbuffers time, as well as many fullscreen clients in many cases. This makes suspend/resume useless if one wants to use any OpenGL apps after the resume. In Linux 3.16, drm_vblank_on() was added, complementing the older drm_vblank_off() to solve these problems elegantly, so use those calls in nouveaus suspend/resume code. For kernels 3.8 - 3.15, we need to cherry-pick the drm_vblank_on() patch to support this patch. Signed-off-by: Mario Kleiner <mario.kleiner.de@gmail.com> Cc: <stable@vger.kernel.org> #v3.16 Cc: <stable@vger.kernel.org> #v3.8+: f275228: drm: Add drm_vblank_on() Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_display.c11
1 files changed, 11 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
index 47ad74255bf1..dd469dbeaae1 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.c
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
@@ -404,6 +404,11 @@ nouveau_display_fini(struct drm_device *dev)
{
struct nouveau_display *disp = nouveau_display(dev);
struct drm_connector *connector;
+ int head;
+
+ /* Make sure that drm and hw vblank irqs get properly disabled. */
+ for (head = 0; head < dev->mode_config.num_crtc; head++)
+ drm_vblank_off(dev, head);
/* disable hotplug interrupts */
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
@@ -620,6 +625,8 @@ void
nouveau_display_resume(struct drm_device *dev)
{
struct drm_crtc *crtc;
+ int head;
+
nouveau_display_init(dev);
/* Force CLUT to get re-loaded during modeset */
@@ -629,6 +636,10 @@ nouveau_display_resume(struct drm_device *dev)
nv_crtc->lut.depth = 0;
}
+ /* Make sure that drm and hw vblank irqs get resumed if needed. */
+ for (head = 0; head < dev->mode_config.num_crtc; head++)
+ drm_vblank_on(dev, head);
+
drm_helper_resume_force_mode(dev);
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {