drm/i915: enable memory self refresh on 9xx
authorLi Peng <peng.li@linux.intel.com>
Wed, 27 Jan 2010 11:01:11 +0000 (19:01 +0800)
committerEric Anholt <eric@anholt.net>
Mon, 22 Feb 2010 16:46:50 +0000 (11:46 -0500)
Enabling memory self refresh (SR) on 9xx needs to set additional
register bits. On 945, we need bit 31 of FW_BLC_SELF to enable the
write to self refresh bit and bit 16 to enable the write of self
refresh watermark. On 915, bit 12 of INSTPM is used to enable SR.

SR will take effect when CPU enters C3+ state and its entry/exit
should be automatically controlled by H/W, driver only needs to set
SR enable bits in wm update. But this isn't safe in my test on 945
because GPU is hung. So this patch explicitly enables SR when GPU
is idle, and disables SR when it is busy. In my test on a netbook of
945GSE chipset, it saves about 0.8W idle power.

Signed-off-by: Li Peng <peng.li@intel.com>
[anholt: rebased against 33c5fd121eabbccc9103daf6cda36941eb3c349f
by adding disable of INSTPM SR bit on 915GM for two pipe setup]
Signed-off-by: Eric Anholt <eric@anholt.net>
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/intel_display.c

index ab1bd2d..6defb7f 100644 (file)
 #define   I915_ERROR_MEMORY_REFRESH                    (1<<1)
 #define   I915_ERROR_INSTRUCTION                       (1<<0)
 #define INSTPM         0x020c0
+#define   INSTPM_SELF_EN (1<<12) /* 915GM only */
 #define ACTHD          0x020c8
 #define FW_BLC         0x020d8
 #define FW_BLC2                0x020dc
 #define FW_BLC_SELF    0x020e0 /* 915+ only */
-#define   FW_BLC_SELF_EN (1<<15)
+#define   FW_BLC_SELF_EN_MASK      (1<<31)
+#define   FW_BLC_SELF_FIFO_MASK    (1<<16) /* 945 only */
+#define   FW_BLC_SELF_EN           (1<<15) /* 945 only */
 #define MM_BURST_LENGTH     0x00700000
 #define MM_FIFO_WATERMARK   0x0001F000
 #define LM_BURST_LENGTH     0x00000700
index b27202d..af9ec21 100644 (file)
@@ -2757,11 +2757,22 @@ static void i9xx_update_wm(struct drm_device *dev, int planea_clock,
                srwm = total_size - sr_entries;
                if (srwm < 0)
                        srwm = 1;
-               I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN | (srwm & 0x3f));
+
+               if (IS_I945G(dev) || IS_I945GM(dev))
+                       I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_FIFO_MASK | (srwm & 0xff));
+               else if (IS_I915GM(dev)) {
+                       /* 915M has a smaller SRWM field */
+                       I915_WRITE(FW_BLC_SELF, srwm & 0x3f);
+                       I915_WRITE(INSTPM, I915_READ(INSTPM) | INSTPM_SELF_EN);
+               }
        } else {
                /* Turn off self refresh if both pipes are enabled */
-               I915_WRITE(FW_BLC_SELF, I915_READ(FW_BLC_SELF)
-                                       & ~FW_BLC_SELF_EN);
+               if (IS_I945G(dev) || IS_I945GM(dev)) {
+                       I915_WRITE(FW_BLC_SELF, I915_READ(FW_BLC_SELF)
+                                  & ~FW_BLC_SELF_EN);
+               } else if (IS_I915GM(dev)) {
+                       I915_WRITE(INSTPM, I915_READ(INSTPM) & ~INSTPM_SELF_EN);
+               }
        }
 
        DRM_DEBUG_KMS("Setting FIFO watermarks - A: %d, B: %d, C: %d, SR %d\n",
@@ -4011,6 +4022,11 @@ static void intel_idle_update(struct work_struct *work)
 
        mutex_lock(&dev->struct_mutex);
 
+       if (IS_I945G(dev) || IS_I945GM(dev)) {
+               DRM_DEBUG_DRIVER("enable memory self refresh on 945\n");
+               I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN_MASK | FW_BLC_SELF_EN);
+       }
+
        list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
                /* Skip inactive CRTCs */
                if (!crtc->fb)
@@ -4044,6 +4060,15 @@ void intel_mark_busy(struct drm_device *dev, struct drm_gem_object *obj)
        if (!drm_core_check_feature(dev, DRIVER_MODESET))
                return;
 
+       if (IS_I945G(dev) || IS_I945GM(dev)) {
+               u32 fw_blc_self;
+
+               DRM_DEBUG_DRIVER("disable memory self refresh on 945\n");
+               fw_blc_self = I915_READ(FW_BLC_SELF);
+               fw_blc_self &= ~FW_BLC_SELF_EN;
+               I915_WRITE(FW_BLC_SELF, fw_blc_self | FW_BLC_SELF_EN_MASK);
+       }
+
        if (!dev_priv->busy)
                dev_priv->busy = true;
        else