drm/i915: Periodically sanity check power management
authorChris Wilson <chris@chris-wilson.co.uk>
Thu, 26 Apr 2012 10:28:42 +0000 (11:28 +0100)
committerDaniel Vetter <daniel.vetter@ffwll.ch>
Thu, 3 May 2012 15:22:36 +0000 (17:22 +0200)
Every time we use the device after a period of idleness, check that the
power management setup is still sane. This is to workaround a bug
whereby it seems that we begin suppressing power management interrupts,
preventing SandyBridge+ from going into turbo mode.

This patch does have a side-effect. It removes the mark-busy for just
moving the cursor - we don't want to increase the render clock just for
the sprite, though we may want to bump the display frequency. I'd argue
that we do not, and certainly don't want to take the struct_mutex here
due to the large latencies that introduces.

References: https://bugs.freedesktop.org/show_bug.cgi?id=44006
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_drv.h
drivers/gpu/drm/i915/intel_pm.c

index 1f251093101990d5bb7dd9503384d5295746bd8d..00a20e985d28f41a2bcc2fc35d31c0983bb2c24d 100644 (file)
@@ -240,6 +240,7 @@ struct drm_i915_display_funcs {
        void (*update_wm)(struct drm_device *dev);
        void (*update_sprite_wm)(struct drm_device *dev, int pipe,
                                 uint32_t sprite_width, int pixel_size);
+       void (*sanitize_pm)(struct drm_device *dev);
        int (*crtc_mode_set)(struct drm_crtc *crtc,
                             struct drm_display_mode *mode,
                             struct drm_display_mode *adjusted_mode,
index 6ab195c0097f9bb78a8361863d71ddef0ed73a82..ba04447926ee77c9e2621f739edb1ca2f351de66 100644 (file)
@@ -4834,9 +4834,6 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc,
                else
                        i9xx_update_cursor(crtc, base);
        }
-
-       if (visible)
-               intel_mark_busy(dev, to_intel_framebuffer(crtc->fb)->obj);
 }
 
 static int intel_crtc_cursor_set(struct drm_crtc *crtc,
@@ -5530,9 +5527,10 @@ void intel_mark_busy(struct drm_device *dev, struct drm_i915_gem_object *obj)
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                return;
 
-       if (!dev_priv->busy)
+       if (!dev_priv->busy) {
+               intel_sanitize_pm(dev);
                dev_priv->busy = true;
-       else
+       else
                mod_timer(&dev_priv->idle_timer, jiffies +
                          msecs_to_jiffies(GPU_IDLE_TIMEOUT));
 
index d9ffe381c72344e3b0e0090c2f4984dd1c4f4b44..cfec4842e0c446f75e77b6e436878af744ce4836 100644 (file)
@@ -344,6 +344,8 @@ extern int intel_plane_init(struct drm_device *dev, enum pipe pipe);
 extern void intel_flush_display_plane(struct drm_i915_private *dev_priv,
                                      enum plane plane);
 
+void intel_sanitize_pm(struct drm_device *dev);
+
 /* intel_panel.c */
 extern void intel_fixed_panel_mode(struct drm_display_mode *fixed_mode,
                                   struct drm_display_mode *adjusted_mode);
index 6ddf807743355521bc7d26bac2611bec9f3088fb..43892341079acfdf1e8e38e92655a456205fd397 100644 (file)
@@ -3525,6 +3525,41 @@ void intel_init_clock_gating(struct drm_device *dev)
                dev_priv->display.init_pch_clock_gating(dev);
 }
 
+static void gen6_sanitize_pm(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 limits, delay, old;
+
+       gen6_gt_force_wake_get(dev_priv);
+
+       old = limits = I915_READ(GEN6_RP_INTERRUPT_LIMITS);
+       /* Make sure we continue to get interrupts
+        * until we hit the minimum or maximum frequencies.
+        */
+       limits &= ~(0x3f << 16 | 0x3f << 24);
+       delay = dev_priv->cur_delay;
+       if (delay < dev_priv->max_delay)
+               limits |= (dev_priv->max_delay & 0x3f) << 24;
+       if (delay > dev_priv->min_delay)
+               limits |= (dev_priv->min_delay & 0x3f) << 16;
+
+       if (old != limits) {
+               DRM_ERROR("Power management discrepancy: GEN6_RP_INTERRUPT_LIMITS expected %08x, was %08x\n",
+                         limits, old);
+               I915_WRITE(GEN6_RP_INTERRUPT_LIMITS, limits);
+       }
+
+       gen6_gt_force_wake_put(dev_priv);
+}
+
+void intel_sanitize_pm(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       if (dev_priv->display.sanitize_pm)
+               dev_priv->display.sanitize_pm(dev);
+}
+
 /* Set up chip specific power management-related functions */
 void intel_init_pm(struct drm_device *dev)
 {
@@ -3607,6 +3642,7 @@ void intel_init_pm(struct drm_device *dev)
                                dev_priv->display.update_wm = NULL;
                        }
                        dev_priv->display.init_clock_gating = gen6_init_clock_gating;
+                       dev_priv->display.sanitize_pm = gen6_sanitize_pm;
                } else if (IS_IVYBRIDGE(dev)) {
                        /* FIXME: detect B0+ stepping and use auto training */
                        if (SNB_READ_WM0_LATENCY()) {
@@ -3618,6 +3654,7 @@ void intel_init_pm(struct drm_device *dev)
                                dev_priv->display.update_wm = NULL;
                        }
                        dev_priv->display.init_clock_gating = ivybridge_init_clock_gating;
+                       dev_priv->display.sanitize_pm = gen6_sanitize_pm;
                } else
                        dev_priv->display.update_wm = NULL;
        } else if (IS_VALLEYVIEW(dev)) {