drm/i915: dynamic render p-state support for Sandy Bridge
authorJesse Barnes <jbarnes@virtuousgeek.org>
Fri, 17 Dec 2010 22:19:02 +0000 (14:19 -0800)
committerChris Wilson <chris@chris-wilson.co.uk>
Sat, 18 Dec 2010 11:07:02 +0000 (11:07 +0000)
Add an interrupt handler for switching graphics frequencies and handling
PM interrupts.  This should allow for increased performance when busy
and lower power consumption when idle.

Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
drivers/gpu/drm/i915/i915_debugfs.c
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/i915_irq.c
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/i915_suspend.c
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_drv.h

index 864e75d762e6d4285cf42e7e4eef0aa0ae882c08..92f75782c33287e9275cd9c3e26b5e44d9095263 100644 (file)
@@ -797,15 +797,51 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused)
        struct drm_info_node *node = (struct drm_info_node *) m->private;
        struct drm_device *dev = node->minor->dev;
        drm_i915_private_t *dev_priv = dev->dev_private;
-       u16 rgvswctl = I915_READ16(MEMSWCTL);
-       u16 rgvstat = I915_READ16(MEMSTAT_ILK);
-
-       seq_printf(m, "Requested P-state: %d\n", (rgvswctl >> 8) & 0xf);
-       seq_printf(m, "Requested VID: %d\n", rgvswctl & 0x3f);
-       seq_printf(m, "Current VID: %d\n", (rgvstat & MEMSTAT_VID_MASK) >>
-                  MEMSTAT_VID_SHIFT);
-       seq_printf(m, "Current P-state: %d\n",
-                  (rgvstat & MEMSTAT_PSTATE_MASK) >> MEMSTAT_PSTATE_SHIFT);
+
+       if (IS_GEN5(dev)) {
+               u16 rgvswctl = I915_READ16(MEMSWCTL);
+               u16 rgvstat = I915_READ16(MEMSTAT_ILK);
+
+               seq_printf(m, "Requested P-state: %d\n", (rgvswctl >> 8) & 0xf);
+               seq_printf(m, "Requested VID: %d\n", rgvswctl & 0x3f);
+               seq_printf(m, "Current VID: %d\n", (rgvstat & MEMSTAT_VID_MASK) >>
+                          MEMSTAT_VID_SHIFT);
+               seq_printf(m, "Current P-state: %d\n",
+                          (rgvstat & MEMSTAT_PSTATE_MASK) >> MEMSTAT_PSTATE_SHIFT);
+       } else if (IS_GEN6(dev)) {
+               u32 gt_perf_status = I915_READ(GEN6_GT_PERF_STATUS);
+               u32 rp_state_limits = I915_READ(GEN6_RP_STATE_LIMITS);
+               u32 rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
+               int max_freq;
+
+               /* RPSTAT1 is in the GT power well */
+               __gen6_force_wake_get(dev_priv);
+
+               seq_printf(m, "GT_PERF_STATUS: 0x%08x\n", gt_perf_status);
+               seq_printf(m, "RPSTAT1: 0x%08x\n", I915_READ(GEN6_RPSTAT1));
+               seq_printf(m, "Render p-state ratio: %d\n",
+                          (gt_perf_status & 0xff00) >> 8);
+               seq_printf(m, "Render p-state VID: %d\n",
+                          gt_perf_status & 0xff);
+               seq_printf(m, "Render p-state limit: %d\n",
+                          rp_state_limits & 0xff);
+
+               max_freq = (rp_state_cap & 0xff0000) >> 16;
+               seq_printf(m, "Lowest (RPN) frequency: %dMHz\n",
+                          max_freq * 100);
+
+               max_freq = (rp_state_cap & 0xff00) >> 8;
+               seq_printf(m, "Nominal (RP1) frequency: %dMHz\n",
+                          max_freq * 100);
+
+               max_freq = rp_state_cap & 0xff;
+               seq_printf(m, "Max non-overclocked (RP0) frequency: %dMHz\n",
+                          max_freq * 100);
+
+               __gen6_force_wake_put(dev_priv);
+       } else {
+               seq_printf(m, "no P-state info available\n");
+       }
 
        return 0;
 }
index 53dfc8398a9666e4ee553f4943f21ea682f814dc..2a653cc803951eeb25e4bcc32be75a4867e4bf82 100644 (file)
@@ -1264,6 +1264,7 @@ extern void intel_disable_fbc(struct drm_device *dev);
 extern void intel_enable_fbc(struct drm_crtc *crtc, unsigned long interval);
 extern bool intel_fbc_enabled(struct drm_device *dev);
 extern bool ironlake_set_drps(struct drm_device *dev, u8 val);
+extern void gen6_set_rps(struct drm_device *dev, u8 val);
 extern void intel_detect_pch (struct drm_device *dev);
 extern int intel_trans_dp_port_sel (struct drm_crtc *crtc);
 
index adf983f01dda262ba6b83c48dc174b5f35563a05..0dadc025b77b6d8ec17236086beef4b700f2ba06 100644 (file)
@@ -397,11 +397,49 @@ static void notify_ring(struct drm_device *dev,
                  jiffies + msecs_to_jiffies(DRM_I915_HANGCHECK_PERIOD));
 }
 
+static void gen6_pm_irq_handler(struct drm_device *dev)
+{
+       drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+       u8 new_delay = dev_priv->cur_delay;
+       u32 pm_iir;
+
+       pm_iir = I915_READ(GEN6_PMIIR);
+       if (!pm_iir)
+               return;
+
+       if (pm_iir & GEN6_PM_RP_UP_THRESHOLD) {
+               if (dev_priv->cur_delay != dev_priv->max_delay)
+                       new_delay = dev_priv->cur_delay + 1;
+               if (new_delay > dev_priv->max_delay)
+                       new_delay = dev_priv->max_delay;
+       } else if (pm_iir & (GEN6_PM_RP_DOWN_THRESHOLD | GEN6_PM_RP_DOWN_TIMEOUT)) {
+               if (dev_priv->cur_delay != dev_priv->min_delay)
+                       new_delay = dev_priv->cur_delay - 1;
+               if (new_delay < dev_priv->min_delay) {
+                       new_delay = dev_priv->min_delay;
+                       I915_WRITE(GEN6_RP_INTERRUPT_LIMITS,
+                                  I915_READ(GEN6_RP_INTERRUPT_LIMITS) |
+                                  ((new_delay << 16) & 0x3f0000));
+               } else {
+                       /* Make sure we continue to get down interrupts
+                        * until we hit the minimum frequency */
+                       I915_WRITE(GEN6_RP_INTERRUPT_LIMITS,
+                                  I915_READ(GEN6_RP_INTERRUPT_LIMITS) & ~0x3f0000);
+               }
+
+       }
+
+       gen6_set_rps(dev, new_delay);
+       dev_priv->cur_delay = new_delay;
+
+       I915_WRITE(GEN6_PMIIR, pm_iir);
+}
+
 static irqreturn_t ironlake_irq_handler(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
        int ret = IRQ_NONE;
-       u32 de_iir, gt_iir, de_ier, pch_iir;
+       u32 de_iir, gt_iir, de_ier, pch_iir, pm_iir;
        u32 hotplug_mask;
        struct drm_i915_master_private *master_priv;
        u32 bsd_usr_interrupt = GT_BSD_USER_INTERRUPT;
@@ -417,8 +455,10 @@ static irqreturn_t ironlake_irq_handler(struct drm_device *dev)
        de_iir = I915_READ(DEIIR);
        gt_iir = I915_READ(GTIIR);
        pch_iir = I915_READ(SDEIIR);
+       pm_iir = I915_READ(GEN6_PMIIR);
 
-       if (de_iir == 0 && gt_iir == 0 && pch_iir == 0)
+       if (de_iir == 0 && gt_iir == 0 && pch_iir == 0 &&
+           (!IS_GEN6(dev) || pm_iir == 0))
                goto done;
 
        if (HAS_PCH_CPT(dev))
@@ -470,6 +510,9 @@ static irqreturn_t ironlake_irq_handler(struct drm_device *dev)
                i915_handle_rps_change(dev);
        }
 
+       if (IS_GEN6(dev))
+               gen6_pm_irq_handler(dev);
+
        /* should clear PCH hotplug event before clear CPU irq */
        I915_WRITE(SDEIIR, pch_iir);
        I915_WRITE(GTIIR, gt_iir);
index c2231f7d2d97751537740279a108bfd5505ed93c..d60860ec8cf41b36efa69a32ee42b181b0894836 100644 (file)
 #define DDRMPLL1               0X12c20
 #define PEG_BAND_GAP_DATA      0x14d68
 
+#define GEN6_GT_PERF_STATUS    0x145948
+#define GEN6_RP_STATE_LIMITS   0x145994
+#define GEN6_RP_STATE_CAP      0x145998
+
 /*
  * Logical Context regs
  */
 #define  FORCEWAKE                             0xA18C
 #define  FORCEWAKE_ACK                         0x130090
 
-#define GEN6_RC_NORMAL_FREQ                    0xA008
+#define GEN6_RPNSWREQ                          0xA008
 #define   GEN6_TURBO_DISABLE                   (1<<31)
 #define   GEN6_FREQUENCY(x)                    ((x)<<25)
 #define   GEN6_OFFSET(x)                       ((x)<<19)
 #define   GEN6_RC_CTL_HW_ENABLE                        (1<<31)
 #define GEN6_RP_DOWN_TIMEOUT                   0xA010
 #define GEN6_RP_INTERRUPT_LIMITS               0xA014
+#define GEN6_RPSTAT1                           0xA01C
 #define GEN6_RP_CONTROL                                0xA024
 #define   GEN6_RP_MEDIA_TURBO                  (1<<11)
 #define   GEN6_RP_USE_NORMAL_FREQ              (1<<9)
 #define GEN6_RC6_THRESHOLD                     0xA0B8
 #define GEN6_RC6p_THRESHOLD                    0xA0BC
 #define GEN6_RC6pp_THRESHOLD                   0xA0C0
+#define GEN6_PMINTRMSK                         0xA168
 
 #define GEN6_PMISR                             0x44020
 #define GEN6_PMIMR                             0x44024
index a311809f3c80fdce01e591a584b373d560df0e87..f623efdb11512396a309db16ee4d289d62adf11b 100644 (file)
@@ -817,8 +817,10 @@ int i915_save_state(struct drm_device *dev)
                dev_priv->saveIMR = I915_READ(IMR);
        }
 
-       if (HAS_PCH_SPLIT(dev))
+       if (IS_IRONLAKE_M(dev))
                ironlake_disable_drps(dev);
+       if (IS_GEN6(dev))
+               gen6_disable_rps(dev);
 
        intel_disable_clock_gating(dev);
 
@@ -867,11 +869,14 @@ int i915_restore_state(struct drm_device *dev)
        /* Clock gating state */
        intel_enable_clock_gating(dev);
 
-       if (HAS_PCH_SPLIT(dev)) {
+       if (IS_IRONLAKE_M(dev)) {
                ironlake_enable_drps(dev);
                intel_init_emon(dev);
        }
 
+       if (IS_GEN6(dev))
+               gen6_enable_rps(dev_priv);
+
        /* Cache mode state */
        I915_WRITE (CACHE_MODE_0, dev_priv->saveCACHE_MODE_0 | 0xffff0000);
 
index c79bee4b4d565520232b2cbe6de5a0a9b8d2ea3b..880659680d0a368ed43a5a1c308067ec61d5841e 100644 (file)
@@ -6021,6 +6021,25 @@ void ironlake_disable_drps(struct drm_device *dev)
 
 }
 
+void gen6_set_rps(struct drm_device *dev, u8 val)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 swreq;
+
+       swreq = (val & 0x3ff) << 25;
+       I915_WRITE(GEN6_RPNSWREQ, swreq);
+}
+
+void gen6_disable_rps(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       I915_WRITE(GEN6_RPNSWREQ, 1 << 31);
+       I915_WRITE(GEN6_PMINTRMSK, 0xffffffff);
+       I915_WRITE(GEN6_PMIER, 0);
+       I915_WRITE(GEN6_PMIIR, I915_READ(GEN6_PMIIR));
+}
+
 static unsigned long intel_pxfreq(u32 vidfreq)
 {
        unsigned long freq;
@@ -6107,7 +6126,7 @@ void intel_init_emon(struct drm_device *dev)
        dev_priv->corr = (lcfuse & LCFUSE_HIV_MASK);
 }
 
-static void gen6_enable_rc6(struct drm_i915_private *dev_priv)
+void gen6_enable_rps(struct drm_i915_private *dev_priv)
 {
        int i;
 
@@ -6120,7 +6139,7 @@ static void gen6_enable_rc6(struct drm_i915_private *dev_priv)
        I915_WRITE(GEN6_RC_STATE, 0);
        __gen6_force_wake_get(dev_priv);
 
-       /* disable the counters and set determistic thresholds */
+       /* disable the counters and set deterministic thresholds */
        I915_WRITE(GEN6_RC_CONTROL, 0);
 
        I915_WRITE(GEN6_RC1_WAKE_RATE_LIMIT, 1000 << 16);
@@ -6144,7 +6163,7 @@ static void gen6_enable_rc6(struct drm_i915_private *dev_priv)
                   GEN6_RC_CTL_EI_MODE(1) |
                   GEN6_RC_CTL_HW_ENABLE);
 
-       I915_WRITE(GEN6_RC_NORMAL_FREQ,
+       I915_WRITE(GEN6_RPNSWREQ,
                   GEN6_FREQUENCY(10) |
                   GEN6_OFFSET(0) |
                   GEN6_AGGRESSIVE_TURBO);
@@ -6189,6 +6208,9 @@ static void gen6_enable_rc6(struct drm_i915_private *dev_priv)
                   GEN6_PM_RP_DOWN_THRESHOLD |
                   GEN6_PM_RP_UP_EI_EXPIRED |
                   GEN6_PM_RP_DOWN_EI_EXPIRED);
+       I915_WRITE(GEN6_PMIMR, 0);
+       /* enable all PM interrupts */
+       I915_WRITE(GEN6_PMINTRMSK, 0);
 
        __gen6_force_wake_put(dev_priv);
 }
@@ -6381,9 +6403,6 @@ void intel_enable_clock_gating(struct drm_device *dev)
                                   I915_READ(MCHBAR_RENDER_STANDBY) & ~RCX_SW_EXIT);
                }
        }
-
-       if (IS_GEN6(dev))
-               gen6_enable_rc6(dev_priv);
 }
 
 void intel_disable_clock_gating(struct drm_device *dev)
@@ -6657,6 +6676,9 @@ void intel_modeset_init(struct drm_device *dev)
                intel_init_emon(dev);
        }
 
+       if (IS_GEN6(dev))
+               gen6_enable_rps(dev_priv);
+
        INIT_WORK(&dev_priv->idle_work, intel_idle_update);
        setup_timer(&dev_priv->idle_timer, intel_gpu_idle_timer,
                    (unsigned long)dev);
@@ -6690,6 +6712,8 @@ void intel_modeset_cleanup(struct drm_device *dev)
 
        if (IS_IRONLAKE_M(dev))
                ironlake_disable_drps(dev);
+       if (IS_GEN6(dev))
+               gen6_disable_rps(dev);
 
        intel_disable_clock_gating(dev);
 
index acdea6549ec4f37adb35a23ec5546199a6031b31..d782ad9fd6dbb4aa7a18c40d90d9552e3d70309b 100644 (file)
@@ -298,6 +298,8 @@ extern void intel_enable_clock_gating(struct drm_device *dev);
 extern void intel_disable_clock_gating(struct drm_device *dev);
 extern void ironlake_enable_drps(struct drm_device *dev);
 extern void ironlake_disable_drps(struct drm_device *dev);
+extern void gen6_enable_rps(struct drm_i915_private *dev_priv);
+extern void gen6_disable_rps(struct drm_device *dev);
 extern void intel_init_emon(struct drm_device *dev);
 
 extern int intel_pin_and_fence_fb_obj(struct drm_device *dev,