drm/i915: add render standby support
authorJesse Barnes <jbarnes@virtuousgeek.org>
Thu, 8 Oct 2009 17:16:48 +0000 (10:16 -0700)
committerEric Anholt <eric@anholt.net>
Thu, 5 Nov 2009 22:47:06 +0000 (14:47 -0800)
Render standy allows the GPU to power down the render unit when idle.
In order for this to work, it needs a page of graphics memory to save
state.  This patch allocates that page and enables the feature on
supported chipsets.

Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Signed-off-by: Eric Anholt <eric@anholt.net>
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/i915_suspend.c
drivers/gpu/drm/i915/intel_display.c

index 57204e2..9539119 100644 (file)
@@ -187,6 +187,7 @@ typedef struct drm_i915_private {
        unsigned int status_gfx_addr;
        drm_local_map_t hws_map;
        struct drm_gem_object *hws_obj;
+       struct drm_gem_object *pwrctx;
 
        struct resource mch_res;
 
@@ -280,6 +281,7 @@ typedef struct drm_i915_private {
        u32 saveDSPBCNTR;
        u32 saveDSPARB;
        u32 saveRENDERSTANDBY;
+       u32 savePWRCTXA;
        u32 saveHWS;
        u32 savePIPEACONF;
        u32 savePIPEBCONF;
@@ -1019,6 +1021,7 @@ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
                           (IS_I9XX(dev) || IS_GM45(dev)) && \
                           !IS_IGD(dev) && \
                           !IS_IGDNG(dev))
+#define I915_HAS_RC6(dev) (IS_I965GM(dev) || IS_GM45(dev) || IS_IGDNG_M(dev))
 
 #define PRIMARY_RINGBUFFER_SIZE         (128*1024)
 
index 1687edf..51fd152 100644 (file)
 #define HWS_PGA                0x02080
 #define HWS_ADDRESS_MASK       0xfffff000
 #define HWS_START_ADDRESS_SHIFT        4
+#define PWRCTXA                0x2088 /* 965GM+ only */
+#define   PWRCTX_EN    (1<<0)
 #define IPEIR          0x02088
 #define IPEHR          0x0208c
 #define INSTDONE       0x02090
 
 /** GM965 GM45 render standby register */
 #define MCHBAR_RENDER_STANDBY  0x111B8
-
+#define   RCX_SW_EXIT          (1<<23)
+#define   RSX_STATUS_MASK      0x00700000
 #define PEG_BAND_GAP_DATA      0x14d68
 
 /*
index 992d561..cd10d9b 100644 (file)
@@ -699,8 +699,10 @@ int i915_save_state(struct drm_device *dev)
        pci_read_config_byte(dev->pdev, LBB, &dev_priv->saveLBB);
 
        /* Render Standby */
-       if (IS_I965G(dev) && IS_MOBILE(dev))
+       if (I915_HAS_RC6(dev)) {
                dev_priv->saveRENDERSTANDBY = I915_READ(MCHBAR_RENDER_STANDBY);
+               dev_priv->savePWRCTXA = I915_READ(PWRCTXA);
+       }
 
        /* Hardware status page */
        dev_priv->saveHWS = I915_READ(HWS_PGA);
@@ -762,8 +764,10 @@ int i915_restore_state(struct drm_device *dev)
        pci_write_config_byte(dev->pdev, LBB, dev_priv->saveLBB);
 
        /* Render Standby */
-       if (IS_I965G(dev) && IS_MOBILE(dev))
+       if (I915_HAS_RC6(dev)) {
                I915_WRITE(MCHBAR_RENDER_STANDBY, dev_priv->saveRENDERSTANDBY);
+               I915_WRITE(PWRCTXA, dev_priv->savePWRCTXA);
+       }
 
        /* Hardware status page */
        I915_WRITE(HWS_PGA, dev_priv->saveHWS);
index 099f420..8945656 100644 (file)
@@ -4296,6 +4296,42 @@ void intel_init_clock_gating(struct drm_device *dev)
        } else if (IS_I830(dev)) {
                I915_WRITE(DSPCLK_GATE_D, OVRUNIT_CLOCK_GATE_DISABLE);
        }
+
+       /*
+        * GPU can automatically power down the render unit if given a page
+        * to save state.
+        */
+       if (I915_HAS_RC6(dev)) {
+               struct drm_gem_object *pwrctx;
+               struct drm_i915_gem_object *obj_priv;
+               int ret;
+
+               pwrctx = drm_gem_object_alloc(dev, 4096);
+               if (!pwrctx) {
+                       DRM_DEBUG("failed to alloc power context, RC6 disabled\n");
+                       goto out;
+               }
+
+               ret = i915_gem_object_pin(pwrctx, 4096);
+               if (ret) {
+                       DRM_ERROR("failed to pin power context: %d\n", ret);
+                       drm_gem_object_unreference(pwrctx);
+                       goto out;
+               }
+
+               i915_gem_object_set_to_gtt_domain(pwrctx, 1);
+
+               obj_priv = pwrctx->driver_private;
+
+               I915_WRITE(PWRCTXA, obj_priv->gtt_offset | PWRCTX_EN);
+               I915_WRITE(MCHBAR_RENDER_STANDBY,
+                          I915_READ(MCHBAR_RENDER_STANDBY) & ~RCX_SW_EXIT);
+
+               dev_priv->pwrctx = pwrctx;
+       }
+
+out:
+       return;
 }
 
 /* Set up chip specific display functions */
@@ -4450,6 +4486,11 @@ void intel_modeset_cleanup(struct drm_device *dev)
        if (dev_priv->display.disable_fbc)
                dev_priv->display.disable_fbc(dev);
 
+       if (dev_priv->pwrctx) {
+               i915_gem_object_unpin(dev_priv->pwrctx);
+               drm_gem_object_unreference(dev_priv->pwrctx);
+       }
+
        drm_mode_config_cleanup(dev);
 }