drm/i915: Enable RC6 autodownclocking on Sandybridge
authorChris Wilson <chris@chris-wilson.co.uk>
Wed, 8 Dec 2010 18:40:43 +0000 (18:40 +0000)
committerChris Wilson <chris@chris-wilson.co.uk>
Thu, 9 Dec 2010 19:46:24 +0000 (19:46 +0000)
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/intel_display.c

index 2a36e05f1b85dab617f701bff7f0871b5eb05b1c..b284c13fd6320564d0d6343a3e4d05a273f0dcb6 100644 (file)
 #define RING_CTL(base)         ((base)+0x3c)
 #define RING_SYNC_0(base)      ((base)+0x40)
 #define RING_SYNC_1(base)      ((base)+0x44)
+#define RING_MAX_IDLE(base)    ((base)+0x54)
 #define RING_HWS_PGA(base)     ((base)+0x80)
 #define RING_HWS_PGA_GEN6(base)        ((base)+0x2080)
 #define RING_ACTHD(base)       ((base)+0x74)
 
 #define  FORCEWAKE                             0xA18C
 #define  FORCEWAKE_ACK                         0x130090
+
+#define GEN6_RC_NORMAL_FREQ                    0xA008
+#define   GEN6_TURBO_DISABLE                   (1<<31)
+#define   GEN6_FREQUENCY(x)                    ((x)<<25)
+#define   GEN6_OFFSET(x)                       ((x)<<19)
+#define   GEN6_AGGRESSIVE_TURBO                        (0<<15)
+#define GEN6_RC_VIDEO_FREQ                     0xA00C
+#define GEN6_RC_CONTROL                                0xA090
+#define   GEN6_RC_CTL_RC6pp_ENABLE             (1<<16)
+#define   GEN6_RC_CTL_RC6p_ENABLE              (1<<17)
+#define   GEN6_RC_CTL_RC6_ENABLE               (1<<18)
+#define   GEN6_RC_CTL_RC1e_ENABLE              (1<<20)
+#define   GEN6_RC_CTL_RC7_ENABLE               (1<<22)
+#define   GEN6_RC_CTL_EI_MODE(x)               ((x)<<27)
+#define   GEN6_RC_CTL_HW_ENABLE                        (1<<31)
+#define GEN6_RP_DOWN_TIMEOUT                   0xA010
+#define GEN6_RP_INTERRUPT_LIMITS               0xA014
+#define GEN6_RP_CONTROL                                0xA024
+#define   GEN6_RP_MEDIA_TURBO                  (1<<11)
+#define   GEN6_RP_USE_NORMAL_FREQ              (1<<9)
+#define   GEN6_RP_MEDIA_IS_GFX                 (1<<8)
+#define   GEN6_RP_ENABLE                       (1<<7)
+#define   GEN6_RP_UP_BUSY_MAX                  (0x2<<3)
+#define   GEN6_RP_DOWN_BUSY_MIN                        (0x2<<0)
+#define GEN6_RP_UP_THRESHOLD                   0xA02C
+#define GEN6_RP_DOWN_THRESHOLD                 0xA030
+#define GEN6_RP_UP_EI                          0xA068
+#define GEN6_RP_DOWN_EI                                0xA06C
+#define GEN6_RP_IDLE_HYSTERSIS                 0xA070
+#define GEN6_RC_STATE                          0xA094
+#define GEN6_RC1_WAKE_RATE_LIMIT               0xA098
+#define GEN6_RC6_WAKE_RATE_LIMIT               0xA09C
+#define GEN6_RC6pp_WAKE_RATE_LIMIT             0xA0A0
+#define GEN6_RC_EVALUATION_INTERVAL            0xA0A8
+#define GEN6_RC_IDLE_HYSTERSIS                 0xA0AC
+#define GEN6_RC_SLEEP                          0xA0B0
+#define GEN6_RC1e_THRESHOLD                    0xA0B4
+#define GEN6_RC6_THRESHOLD                     0xA0B8
+#define GEN6_RC6p_THRESHOLD                    0xA0BC
+#define GEN6_RC6pp_THRESHOLD                   0xA0C0
+
+#define GEN6_PMISR                             0x44020
+#define GEN6_PMIMR                             0x44024
+#define GEN6_PMIIR                             0x44028
+#define GEN6_PMIER                             0x4402C
+#define  GEN6_PM_MBOX_EVENT                    (1<<25)
+#define  GEN6_PM_THERMAL_EVENT                 (1<<24)
+#define  GEN6_PM_RP_DOWN_TIMEOUT               (1<<6)
+#define  GEN6_PM_RP_UP_THRESHOLD               (1<<5)
+#define  GEN6_PM_RP_DOWN_THRESHOLD             (1<<4)
+#define  GEN6_PM_RP_UP_EI_EXPIRED              (1<<2)
+#define  GEN6_PM_RP_DOWN_EI_EXPIRED            (1<<1)
+
+#define GEN6_PCODE_MAILBOX                     0x138124
+#define   GEN6_PCODE_READY                     (1<<31)
+#define   GEN6_PCODE_WRITE_MIN_FREQ_TABLE      0x9
+#define GEN6_PCODE_DATA                                0x138128
+
 #endif /* _I915_REG_H_ */
index 6d4faff8121b8c5d2aad2d69a2b711ab03d5475c..17c213fef0ecadba022d8d9e9bc5ce96f76377cb 100644 (file)
@@ -5842,6 +5842,91 @@ 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)
+{
+       int i;
+
+       /* Here begins a magic sequence of register writes to enable
+        * auto-downclocking.
+        *
+        * Perhaps there might be some value in exposing these to
+        * userspace...
+        */
+       I915_WRITE(GEN6_RC_STATE, 0);
+       __gen6_force_wake_get(dev_priv);
+
+       /* disable the counters and set determistic thresholds */
+       I915_WRITE(GEN6_RC_CONTROL, 0);
+
+       I915_WRITE(GEN6_RC1_WAKE_RATE_LIMIT, 1000 << 16);
+       I915_WRITE(GEN6_RC6_WAKE_RATE_LIMIT, 40 << 16 | 30);
+       I915_WRITE(GEN6_RC6pp_WAKE_RATE_LIMIT, 30);
+       I915_WRITE(GEN6_RC_EVALUATION_INTERVAL, 125000);
+       I915_WRITE(GEN6_RC_IDLE_HYSTERSIS, 25);
+
+       for (i = 0; i < I915_NUM_RINGS; i++)
+               I915_WRITE(RING_MAX_IDLE(dev_priv->ring[i].mmio_base), 10);
+
+       I915_WRITE(GEN6_RC_SLEEP, 0);
+       I915_WRITE(GEN6_RC1e_THRESHOLD, 1000);
+       I915_WRITE(GEN6_RC6_THRESHOLD, 50000);
+       I915_WRITE(GEN6_RC6p_THRESHOLD, 100000);
+       I915_WRITE(GEN6_RC6pp_THRESHOLD, 64000); /* unused */
+
+       I915_WRITE(GEN6_RC_CONTROL,
+                  GEN6_RC_CTL_RC6p_ENABLE |
+                  GEN6_RC_CTL_RC6_ENABLE |
+                  GEN6_RC_CTL_HW_ENABLE);
+
+       I915_WRITE(GEN6_RC_NORMAL_FREQ,
+                  GEN6_FREQUENCY(10) |
+                  GEN6_OFFSET(0) |
+                  GEN6_AGGRESSIVE_TURBO);
+       I915_WRITE(GEN6_RC_VIDEO_FREQ,
+                  GEN6_FREQUENCY(12));
+
+       I915_WRITE(GEN6_RP_DOWN_TIMEOUT, 1000000);
+       I915_WRITE(GEN6_RP_INTERRUPT_LIMITS,
+                  18 << 24 |
+                  6 << 16);
+       I915_WRITE(GEN6_RP_UP_THRESHOLD, 90000);
+       I915_WRITE(GEN6_RP_DOWN_THRESHOLD, 100000);
+       I915_WRITE(GEN6_RP_UP_EI, 100000);
+       I915_WRITE(GEN6_RP_DOWN_EI, 300000);
+       I915_WRITE(GEN6_RP_IDLE_HYSTERSIS, 10);
+       I915_WRITE(GEN6_RP_CONTROL,
+                  GEN6_RP_MEDIA_TURBO |
+                  GEN6_RP_USE_NORMAL_FREQ |
+                  GEN6_RP_MEDIA_IS_GFX |
+                  GEN6_RP_ENABLE |
+                  GEN6_RP_UP_BUSY_MAX |
+                  GEN6_RP_DOWN_BUSY_MIN);
+
+       if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) & GEN6_PCODE_READY) == 0,
+                    500))
+               DRM_ERROR("timeout waiting for pcode mailbox to become idle\n");
+
+       I915_WRITE(GEN6_PCODE_DATA, 0);
+       I915_WRITE(GEN6_PCODE_MAILBOX,
+                  GEN6_PCODE_READY |
+                  GEN6_PCODE_WRITE_MIN_FREQ_TABLE);
+       if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) & GEN6_PCODE_READY) == 0,
+                    500))
+               DRM_ERROR("timeout waiting for pcode mailbox to finish\n");
+
+       /* requires MSI enabled */
+       I915_WRITE(GEN6_PMIER,
+                  GEN6_PM_MBOX_EVENT |
+                  GEN6_PM_THERMAL_EVENT |
+                  GEN6_PM_RP_DOWN_TIMEOUT |
+                  GEN6_PM_RP_UP_THRESHOLD |
+                  GEN6_PM_RP_DOWN_THRESHOLD |
+                  GEN6_PM_RP_UP_EI_EXPIRED |
+                  GEN6_PM_RP_DOWN_EI_EXPIRED);
+
+       __gen6_force_wake_put(dev_priv);
+}
+
 void intel_enable_clock_gating(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -5924,6 +6009,7 @@ void intel_enable_clock_gating(struct drm_device *dev)
                                   _3D_CHICKEN2_WM_READ_PIPELINED << 16 |
                                   _3D_CHICKEN2_WM_READ_PIPELINED);
                }
+
        } else if (IS_G4X(dev)) {
                uint32_t dspclk_gate;
                I915_WRITE(RENCLK_GATE_D1, 0);
@@ -5997,6 +6083,9 @@ 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)