drm/i915: add power monitoring support
authorJesse Barnes <jbarnes@virtuousgeek.org>
Thu, 20 May 2010 21:28:11 +0000 (14:28 -0700)
committerEric Anholt <eric@anholt.net>
Wed, 26 May 2010 21:10:01 +0000 (14:10 -0700)
Add power monitoring support to the i915 driver for use by the IPS
driver.  Export the available power info to the IPS driver through a few
new inter-driver hooks.  When used together, the IPS driver and this
patch can significantly increase graphics performance on Ironlake class
chips.

Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
[anholt: Fixed 32-bit compile.  stupid obfuscating div_u64()]
Signed-off-by: Eric Anholt <eric@anholt.net>
drivers/gpu/drm/i915/i915_debugfs.c
drivers/gpu/drm/i915/i915_dma.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/intel_display.c

index c864858d506415d42a66f6915947ff456f876fa4..eb11e1e049cbcce6d7347782defd18e92d6622e9 100644 (file)
@@ -491,11 +491,14 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused)
        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, "Last command: 0x%01x\n", (rgvswctl >> 13) & 0x3);
-       seq_printf(m, "Command status: %d\n", (rgvswctl >> 12) & 1);
-       seq_printf(m, "P%d DELAY 0x%02x\n", (rgvswctl >> 8) & 0xf,
-                  rgvswctl & 0x3f);
+       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);
 
        return 0;
 }
@@ -510,7 +513,8 @@ static int i915_delayfreq_table(struct seq_file *m, void *unused)
 
        for (i = 0; i < 16; i++) {
                delayfreq = I915_READ(PXVFREQ_BASE + i * 4);
-               seq_printf(m, "P%02dVIDFREQ: 0x%08x\n", i, delayfreq);
+               seq_printf(m, "P%02dVIDFREQ: 0x%08x (VID: %d)\n", i, delayfreq,
+                          (delayfreq & PXVFREQ_PX_MASK) >> PXVFREQ_PX_SHIFT);
        }
 
        return 0;
@@ -543,6 +547,8 @@ static int i915_drpc_info(struct seq_file *m, void *unused)
        struct drm_device *dev = node->minor->dev;
        drm_i915_private_t *dev_priv = dev->dev_private;
        u32 rgvmodectl = I915_READ(MEMMODECTL);
+       u32 rstdbyctl = I915_READ(MCHBAR_RENDER_STANDBY);
+       u16 crstandvid = I915_READ16(CRSTANDVID);
 
        seq_printf(m, "HD boost: %s\n", (rgvmodectl & MEMMODE_BOOST_EN) ?
                   "yes" : "no");
@@ -557,9 +563,13 @@ static int i915_drpc_info(struct seq_file *m, void *unused)
                   rgvmodectl & MEMMODE_RCLK_GATE ? "yes" : "no");
        seq_printf(m, "Starting frequency: P%d\n",
                   (rgvmodectl & MEMMODE_FSTART_MASK) >> MEMMODE_FSTART_SHIFT);
-       seq_printf(m, "Max frequency: P%d\n",
+       seq_printf(m, "Max P-state: P%d\n",
                   (rgvmodectl & MEMMODE_FMAX_MASK) >> MEMMODE_FMAX_SHIFT);
-       seq_printf(m, "Min frequency: P%d\n", (rgvmodectl & MEMMODE_FMIN_MASK));
+       seq_printf(m, "Min P-state: P%d\n", (rgvmodectl & MEMMODE_FMIN_MASK));
+       seq_printf(m, "RS1 VID: %d\n", (crstandvid & 0x3f));
+       seq_printf(m, "RS2 VID: %d\n", ((crstandvid >> 8) & 0x3f));
+       seq_printf(m, "Render standby enabled: %s\n",
+                  (rstdbyctl & RCX_SW_EXIT) ? "no" : "yes");
 
        return 0;
 }
@@ -623,6 +633,36 @@ static int i915_sr_status(struct seq_file *m, void *unused)
        return 0;
 }
 
+static int i915_emon_status(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;
+       unsigned long temp, chipset, gfx;
+
+       temp = i915_mch_val(dev_priv);
+       chipset = i915_chipset_val(dev_priv);
+       gfx = i915_gfx_val(dev_priv);
+
+       seq_printf(m, "GMCH temp: %ld\n", temp);
+       seq_printf(m, "Chipset power: %ld\n", chipset);
+       seq_printf(m, "GFX power: %ld\n", gfx);
+       seq_printf(m, "Total power: %ld\n", chipset + gfx);
+
+       return 0;
+}
+
+static int i915_gfxec(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;
+
+       seq_printf(m, "GFXEC: %ld\n", (unsigned long)I915_READ(0x112f4));
+
+       return 0;
+}
+
 static int
 i915_wedged_open(struct inode *inode,
                 struct file *filp)
@@ -745,6 +785,8 @@ static struct drm_info_list i915_debugfs_list[] = {
        {"i915_delayfreq_table", i915_delayfreq_table, 0},
        {"i915_inttoext_table", i915_inttoext_table, 0},
        {"i915_drpc_info", i915_drpc_info, 0},
+       {"i915_emon_status", i915_emon_status, 0},
+       {"i915_gfxec", i915_gfxec, 0},
        {"i915_fbc_status", i915_fbc_status, 0},
        {"i915_sr_status", i915_sr_status, 0},
 };
index 1dbed700800e622311488b7b0809efec435e7fff..12e92f2cc3a78067870f8d420c25ca2ec2834d28 100644 (file)
@@ -1458,14 +1458,11 @@ void i915_master_destroy(struct drm_device *dev, struct drm_master *master)
        master->driver_priv = NULL;
 }
 
-static void i915_get_mem_freq(struct drm_device *dev)
+static void i915_pineview_get_mem_freq(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
        u32 tmp;
 
-       if (!IS_PINEVIEW(dev))
-               return;
-
        tmp = I915_READ(CLKCFG);
 
        switch (tmp & CLKCFG_FSB_MASK) {
@@ -1496,6 +1493,519 @@ static void i915_get_mem_freq(struct drm_device *dev)
        }
 }
 
+static void i915_ironlake_get_mem_freq(struct drm_device *dev)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       u16 ddrpll, csipll;
+
+       ddrpll = I915_READ16(DDRMPLL1);
+       csipll = I915_READ16(CSIPLL0);
+
+       switch (ddrpll & 0xff) {
+       case 0xc:
+               dev_priv->mem_freq = 800;
+               break;
+       case 0x10:
+               dev_priv->mem_freq = 1066;
+               break;
+       case 0x14:
+               dev_priv->mem_freq = 1333;
+               break;
+       case 0x18:
+               dev_priv->mem_freq = 1600;
+               break;
+       default:
+               DRM_DEBUG_DRIVER("unknown memory frequency 0x%02x\n",
+                                ddrpll & 0xff);
+               dev_priv->mem_freq = 0;
+               break;
+       }
+
+       dev_priv->r_t = dev_priv->mem_freq;
+
+       switch (csipll & 0x3ff) {
+       case 0x00c:
+               dev_priv->fsb_freq = 3200;
+               break;
+       case 0x00e:
+               dev_priv->fsb_freq = 3733;
+               break;
+       case 0x010:
+               dev_priv->fsb_freq = 4266;
+               break;
+       case 0x012:
+               dev_priv->fsb_freq = 4800;
+               break;
+       case 0x014:
+               dev_priv->fsb_freq = 5333;
+               break;
+       case 0x016:
+               dev_priv->fsb_freq = 5866;
+               break;
+       case 0x018:
+               dev_priv->fsb_freq = 6400;
+               break;
+       default:
+               DRM_DEBUG_DRIVER("unknown fsb frequency 0x%04x\n",
+                                csipll & 0x3ff);
+               dev_priv->fsb_freq = 0;
+               break;
+       }
+
+       if (dev_priv->fsb_freq == 3200) {
+               dev_priv->c_m = 0;
+       } else if (dev_priv->fsb_freq > 3200 && dev_priv->fsb_freq <= 4800) {
+               dev_priv->c_m = 1;
+       } else {
+               dev_priv->c_m = 2;
+       }
+}
+
+struct v_table {
+       u8 vid;
+       unsigned long vd; /* in .1 mil */
+       unsigned long vm; /* in .1 mil */
+       u8 pvid;
+};
+
+static struct v_table v_table[] = {
+       { 0, 16125, 15000, 0x7f, },
+       { 1, 16000, 14875, 0x7e, },
+       { 2, 15875, 14750, 0x7d, },
+       { 3, 15750, 14625, 0x7c, },
+       { 4, 15625, 14500, 0x7b, },
+       { 5, 15500, 14375, 0x7a, },
+       { 6, 15375, 14250, 0x79, },
+       { 7, 15250, 14125, 0x78, },
+       { 8, 15125, 14000, 0x77, },
+       { 9, 15000, 13875, 0x76, },
+       { 10, 14875, 13750, 0x75, },
+       { 11, 14750, 13625, 0x74, },
+       { 12, 14625, 13500, 0x73, },
+       { 13, 14500, 13375, 0x72, },
+       { 14, 14375, 13250, 0x71, },
+       { 15, 14250, 13125, 0x70, },
+       { 16, 14125, 13000, 0x6f, },
+       { 17, 14000, 12875, 0x6e, },
+       { 18, 13875, 12750, 0x6d, },
+       { 19, 13750, 12625, 0x6c, },
+       { 20, 13625, 12500, 0x6b, },
+       { 21, 13500, 12375, 0x6a, },
+       { 22, 13375, 12250, 0x69, },
+       { 23, 13250, 12125, 0x68, },
+       { 24, 13125, 12000, 0x67, },
+       { 25, 13000, 11875, 0x66, },
+       { 26, 12875, 11750, 0x65, },
+       { 27, 12750, 11625, 0x64, },
+       { 28, 12625, 11500, 0x63, },
+       { 29, 12500, 11375, 0x62, },
+       { 30, 12375, 11250, 0x61, },
+       { 31, 12250, 11125, 0x60, },
+       { 32, 12125, 11000, 0x5f, },
+       { 33, 12000, 10875, 0x5e, },
+       { 34, 11875, 10750, 0x5d, },
+       { 35, 11750, 10625, 0x5c, },
+       { 36, 11625, 10500, 0x5b, },
+       { 37, 11500, 10375, 0x5a, },
+       { 38, 11375, 10250, 0x59, },
+       { 39, 11250, 10125, 0x58, },
+       { 40, 11125, 10000, 0x57, },
+       { 41, 11000, 9875, 0x56, },
+       { 42, 10875, 9750, 0x55, },
+       { 43, 10750, 9625, 0x54, },
+       { 44, 10625, 9500, 0x53, },
+       { 45, 10500, 9375, 0x52, },
+       { 46, 10375, 9250, 0x51, },
+       { 47, 10250, 9125, 0x50, },
+       { 48, 10125, 9000, 0x4f, },
+       { 49, 10000, 8875, 0x4e, },
+       { 50, 9875, 8750, 0x4d, },
+       { 51, 9750, 8625, 0x4c, },
+       { 52, 9625, 8500, 0x4b, },
+       { 53, 9500, 8375, 0x4a, },
+       { 54, 9375, 8250, 0x49, },
+       { 55, 9250, 8125, 0x48, },
+       { 56, 9125, 8000, 0x47, },
+       { 57, 9000, 7875, 0x46, },
+       { 58, 8875, 7750, 0x45, },
+       { 59, 8750, 7625, 0x44, },
+       { 60, 8625, 7500, 0x43, },
+       { 61, 8500, 7375, 0x42, },
+       { 62, 8375, 7250, 0x41, },
+       { 63, 8250, 7125, 0x40, },
+       { 64, 8125, 7000, 0x3f, },
+       { 65, 8000, 6875, 0x3e, },
+       { 66, 7875, 6750, 0x3d, },
+       { 67, 7750, 6625, 0x3c, },
+       { 68, 7625, 6500, 0x3b, },
+       { 69, 7500, 6375, 0x3a, },
+       { 70, 7375, 6250, 0x39, },
+       { 71, 7250, 6125, 0x38, },
+       { 72, 7125, 6000, 0x37, },
+       { 73, 7000, 5875, 0x36, },
+       { 74, 6875, 5750, 0x35, },
+       { 75, 6750, 5625, 0x34, },
+       { 76, 6625, 5500, 0x33, },
+       { 77, 6500, 5375, 0x32, },
+       { 78, 6375, 5250, 0x31, },
+       { 79, 6250, 5125, 0x30, },
+       { 80, 6125, 5000, 0x2f, },
+       { 81, 6000, 4875, 0x2e, },
+       { 82, 5875, 4750, 0x2d, },
+       { 83, 5750, 4625, 0x2c, },
+       { 84, 5625, 4500, 0x2b, },
+       { 85, 5500, 4375, 0x2a, },
+       { 86, 5375, 4250, 0x29, },
+       { 87, 5250, 4125, 0x28, },
+       { 88, 5125, 4000, 0x27, },
+       { 89, 5000, 3875, 0x26, },
+       { 90, 4875, 3750, 0x25, },
+       { 91, 4750, 3625, 0x24, },
+       { 92, 4625, 3500, 0x23, },
+       { 93, 4500, 3375, 0x22, },
+       { 94, 4375, 3250, 0x21, },
+       { 95, 4250, 3125, 0x20, },
+       { 96, 4125, 3000, 0x1f, },
+       { 97, 4125, 3000, 0x1e, },
+       { 98, 4125, 3000, 0x1d, },
+       { 99, 4125, 3000, 0x1c, },
+       { 100, 4125, 3000, 0x1b, },
+       { 101, 4125, 3000, 0x1a, },
+       { 102, 4125, 3000, 0x19, },
+       { 103, 4125, 3000, 0x18, },
+       { 104, 4125, 3000, 0x17, },
+       { 105, 4125, 3000, 0x16, },
+       { 106, 4125, 3000, 0x15, },
+       { 107, 4125, 3000, 0x14, },
+       { 108, 4125, 3000, 0x13, },
+       { 109, 4125, 3000, 0x12, },
+       { 110, 4125, 3000, 0x11, },
+       { 111, 4125, 3000, 0x10, },
+       { 112, 4125, 3000, 0x0f, },
+       { 113, 4125, 3000, 0x0e, },
+       { 114, 4125, 3000, 0x0d, },
+       { 115, 4125, 3000, 0x0c, },
+       { 116, 4125, 3000, 0x0b, },
+       { 117, 4125, 3000, 0x0a, },
+       { 118, 4125, 3000, 0x09, },
+       { 119, 4125, 3000, 0x08, },
+       { 120, 1125, 0, 0x07, },
+       { 121, 1000, 0, 0x06, },
+       { 122, 875, 0, 0x05, },
+       { 123, 750, 0, 0x04, },
+       { 124, 625, 0, 0x03, },
+       { 125, 500, 0, 0x02, },
+       { 126, 375, 0, 0x01, },
+       { 127, 0, 0, 0x00, },
+};
+
+struct cparams {
+       int i;
+       int t;
+       int m;
+       int c;
+};
+
+static struct cparams cparams[] = {
+       { 1, 1333, 301, 28664 },
+       { 1, 1066, 294, 24460 },
+       { 1, 800, 294, 25192 },
+       { 0, 1333, 276, 27605 },
+       { 0, 1066, 276, 27605 },
+       { 0, 800, 231, 23784 },
+};
+
+unsigned long i915_chipset_val(struct drm_i915_private *dev_priv)
+{
+       u64 total_count, diff, ret;
+       u32 count1, count2, count3, m = 0, c = 0;
+       unsigned long now = jiffies_to_msecs(jiffies), diff1;
+       int i;
+
+       diff1 = now - dev_priv->last_time1;
+
+       count1 = I915_READ(DMIEC);
+       count2 = I915_READ(DDREC);
+       count3 = I915_READ(CSIEC);
+
+       total_count = count1 + count2 + count3;
+
+       /* FIXME: handle per-counter overflow */
+       if (total_count < dev_priv->last_count1) {
+               diff = ~0UL - dev_priv->last_count1;
+               diff += total_count;
+       } else {
+               diff = total_count - dev_priv->last_count1;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(cparams); i++) {
+               if (cparams[i].i == dev_priv->c_m &&
+                   cparams[i].t == dev_priv->r_t) {
+                       m = cparams[i].m;
+                       c = cparams[i].c;
+                       break;
+               }
+       }
+
+       div_u64(diff, diff1);
+       ret = ((m * diff) + c);
+       div_u64(ret, 10);
+
+       dev_priv->last_count1 = total_count;
+       dev_priv->last_time1 = now;
+
+       return ret;
+}
+
+unsigned long i915_mch_val(struct drm_i915_private *dev_priv)
+{
+       unsigned long m, x, b;
+       u32 tsfs;
+
+       tsfs = I915_READ(TSFS);
+
+       m = ((tsfs & TSFS_SLOPE_MASK) >> TSFS_SLOPE_SHIFT);
+       x = I915_READ8(TR1);
+
+       b = tsfs & TSFS_INTR_MASK;
+
+       return ((m * x) / 127) - b;
+}
+
+static unsigned long pvid_to_extvid(struct drm_i915_private *dev_priv, u8 pxvid)
+{
+       unsigned long val = 0;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(v_table); i++) {
+               if (v_table[i].pvid == pxvid) {
+                       if (IS_MOBILE(dev_priv->dev))
+                               val = v_table[i].vm;
+                       else
+                               val = v_table[i].vd;
+               }
+       }
+
+       return val;
+}
+
+void i915_update_gfx_val(struct drm_i915_private *dev_priv)
+{
+       struct timespec now, diff1;
+       u64 diff;
+       unsigned long diffms;
+       u32 count;
+
+       getrawmonotonic(&now);
+       diff1 = timespec_sub(now, dev_priv->last_time2);
+
+       /* Don't divide by 0 */
+       diffms = diff1.tv_sec * 1000 + diff1.tv_nsec / 1000000;
+       if (!diffms)
+               return;
+
+       count = I915_READ(GFXEC);
+
+       if (count < dev_priv->last_count2) {
+               diff = ~0UL - dev_priv->last_count2;
+               diff += count;
+       } else {
+               diff = count - dev_priv->last_count2;
+       }
+
+       dev_priv->last_count2 = count;
+       dev_priv->last_time2 = now;
+
+       /* More magic constants... */
+       diff = diff * 1181;
+       div_u64(diff, diffms * 10);
+       dev_priv->gfx_power = diff;
+}
+
+unsigned long i915_gfx_val(struct drm_i915_private *dev_priv)
+{
+       unsigned long t, corr, state1, corr2, state2;
+       u32 pxvid, ext_v;
+
+       pxvid = I915_READ(PXVFREQ_BASE + (dev_priv->cur_delay * 4));
+       pxvid = (pxvid >> 24) & 0x7f;
+       ext_v = pvid_to_extvid(dev_priv, pxvid);
+
+       state1 = ext_v;
+
+       t = i915_mch_val(dev_priv);
+
+       /* Revel in the empirically derived constants */
+
+       /* Correction factor in 1/100000 units */
+       if (t > 80)
+               corr = ((t * 2349) + 135940);
+       else if (t >= 50)
+               corr = ((t * 964) + 29317);
+       else /* < 50 */
+               corr = ((t * 301) + 1004);
+
+       corr = corr * ((150142 * state1) / 10000 - 78642);
+       corr /= 100000;
+       corr2 = (corr * dev_priv->corr);
+
+       state2 = (corr2 * state1) / 10000;
+       state2 /= 100; /* convert to mW */
+
+       i915_update_gfx_val(dev_priv);
+
+       return dev_priv->gfx_power + state2;
+}
+
+/* Global for IPS driver to get at the current i915 device */
+static struct drm_i915_private *i915_mch_dev;
+/*
+ * Lock protecting IPS related data structures
+ *   - i915_mch_dev
+ *   - dev_priv->max_delay
+ *   - dev_priv->min_delay
+ *   - dev_priv->fmax
+ *   - dev_priv->gpu_busy
+ */
+DEFINE_SPINLOCK(mchdev_lock);
+
+/**
+ * i915_read_mch_val - return value for IPS use
+ *
+ * Calculate and return a value for the IPS driver to use when deciding whether
+ * we have thermal and power headroom to increase CPU or GPU power budget.
+ */
+unsigned long i915_read_mch_val(void)
+{
+       struct drm_i915_private *dev_priv;
+       unsigned long chipset_val, graphics_val, ret = 0;
+
+       spin_lock(&mchdev_lock);
+       if (!i915_mch_dev)
+               goto out_unlock;
+       dev_priv = i915_mch_dev;
+
+       chipset_val = i915_chipset_val(dev_priv);
+       graphics_val = i915_gfx_val(dev_priv);
+
+       ret = chipset_val + graphics_val;
+
+out_unlock:
+       spin_unlock(&mchdev_lock);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(i915_read_mch_val);
+
+/**
+ * i915_gpu_raise - raise GPU frequency limit
+ *
+ * Raise the limit; IPS indicates we have thermal headroom.
+ */
+bool i915_gpu_raise(void)
+{
+       struct drm_i915_private *dev_priv;
+       bool ret = true;
+
+       spin_lock(&mchdev_lock);
+       if (!i915_mch_dev) {
+               ret = false;
+               goto out_unlock;
+       }
+       dev_priv = i915_mch_dev;
+
+       if (dev_priv->max_delay > dev_priv->fmax)
+               dev_priv->max_delay--;
+
+out_unlock:
+       spin_unlock(&mchdev_lock);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(i915_gpu_raise);
+
+/**
+ * i915_gpu_lower - lower GPU frequency limit
+ *
+ * IPS indicates we're close to a thermal limit, so throttle back the GPU
+ * frequency maximum.
+ */
+bool i915_gpu_lower(void)
+{
+       struct drm_i915_private *dev_priv;
+       bool ret = true;
+
+       spin_lock(&mchdev_lock);
+       if (!i915_mch_dev) {
+               ret = false;
+               goto out_unlock;
+       }
+       dev_priv = i915_mch_dev;
+
+       if (dev_priv->max_delay < dev_priv->min_delay)
+               dev_priv->max_delay++;
+
+out_unlock:
+       spin_unlock(&mchdev_lock);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(i915_gpu_lower);
+
+/**
+ * i915_gpu_busy - indicate GPU business to IPS
+ *
+ * Tell the IPS driver whether or not the GPU is busy.
+ */
+bool i915_gpu_busy(void)
+{
+       struct drm_i915_private *dev_priv;
+       bool ret = false;
+
+       spin_lock(&mchdev_lock);
+       if (!i915_mch_dev)
+               goto out_unlock;
+       dev_priv = i915_mch_dev;
+
+       ret = dev_priv->busy;
+
+out_unlock:
+       spin_unlock(&mchdev_lock);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(i915_gpu_busy);
+
+/**
+ * i915_gpu_turbo_disable - disable graphics turbo
+ *
+ * Disable graphics turbo by resetting the max frequency and setting the
+ * current frequency to the default.
+ */
+bool i915_gpu_turbo_disable(void)
+{
+       struct drm_i915_private *dev_priv;
+       bool ret = true;
+
+       spin_lock(&mchdev_lock);
+       if (!i915_mch_dev) {
+               ret = false;
+               goto out_unlock;
+       }
+       dev_priv = i915_mch_dev;
+
+       dev_priv->max_delay = dev_priv->fstart;
+
+       if (!ironlake_set_drps(dev_priv->dev, dev_priv->fstart))
+               ret = false;
+
+out_unlock:
+       spin_unlock(&mchdev_lock);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(i915_gpu_turbo_disable);
+
 /**
  * i915_driver_load - setup chip and create an initial config
  * @dev: DRM device
@@ -1616,7 +2126,10 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
                        goto out_workqueue_free;
        }
 
-       i915_get_mem_freq(dev);
+       if (IS_PINEVIEW(dev))
+               i915_pineview_get_mem_freq(dev);
+       else if (IS_IRONLAKE(dev))
+               i915_ironlake_get_mem_freq(dev);
 
        /* On the 945G/GM, the chipset reports the MSI capability on the
         * integrated graphics even though the support isn't actually there
@@ -1662,6 +2175,12 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
 
        setup_timer(&dev_priv->hangcheck_timer, i915_hangcheck_elapsed,
                    (unsigned long) dev);
+
+       spin_lock(&mchdev_lock);
+       i915_mch_dev = dev_priv;
+       dev_priv->mchdev_lock = &mchdev_lock;
+       spin_unlock(&mchdev_lock);
+
        return 0;
 
 out_workqueue_free:
@@ -1683,6 +2202,10 @@ int i915_driver_unload(struct drm_device *dev)
 
        i915_destroy_error_state(dev);
 
+       spin_lock(&mchdev_lock);
+       i915_mch_dev = NULL;
+       spin_unlock(&mchdev_lock);
+
        destroy_workqueue(dev_priv->wq);
        del_timer_sync(&dev_priv->hangcheck_timer);
 
index 6bc0fc080f2b48aa30660bcf0f33a297b531c762..91b3a1c20ef8223f6c69f742cb4f890fb8ef7356 100644 (file)
@@ -619,6 +619,18 @@ typedef struct drm_i915_private {
        u8 cur_delay;
        u8 min_delay;
        u8 max_delay;
+       u8 fmax;
+       u8 fstart;
+
+       u64 last_count1;
+       unsigned long last_time1;
+       u64 last_count2;
+       struct timespec last_time2;
+       unsigned long gfx_power;
+       int c_m;
+       int r_t;
+       u8 corr;
+       spinlock_t *mchdev_lock;
 
        enum no_fbc_reason no_fbc_reason;
 
@@ -802,6 +814,11 @@ extern int i915_emit_box(struct drm_device *dev,
                         struct drm_clip_rect *boxes,
                         int i, int DR1, int DR4);
 extern int i965_reset(struct drm_device *dev, u8 flags);
+extern unsigned long i915_chipset_val(struct drm_i915_private *dev_priv);
+extern unsigned long i915_mch_val(struct drm_i915_private *dev_priv);
+extern unsigned long i915_gfx_val(struct drm_i915_private *dev_priv);
+extern void i915_update_gfx_val(struct drm_i915_private *dev_priv);
+
 
 /* i915_irq.c */
 void i915_hangcheck_elapsed(unsigned long data);
@@ -1005,7 +1022,7 @@ extern void g4x_disable_fbc(struct drm_device *dev);
 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 intel_detect_pch (struct drm_device *dev);
 extern int intel_trans_dp_port_sel (struct drm_crtc *crtc);
 
@@ -1030,6 +1047,7 @@ extern int intel_trans_dp_port_sel (struct drm_crtc *crtc);
 #define I915_WRITE64(reg, val) writeq(val, dev_priv->regs + (reg))
 #define I915_READ64(reg)       readq(dev_priv->regs + (reg))
 #define POSTING_READ(reg)      (void)I915_READ(reg)
+#define POSTING_READ16(reg)    (void)I915_READ16(reg)
 
 #define I915_VERBOSE 0
 
index 0a3a5806a12ed58b66235ab7fc831bae3fd82505..b5dba4795f6f5441a3ac68aff606de49481f8279 100644 (file)
@@ -278,10 +278,9 @@ static void i915_handle_rps_change(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
        u32 busy_up, busy_down, max_avg, min_avg;
-       u16 rgvswctl;
        u8 new_delay = dev_priv->cur_delay;
 
-       I915_WRITE(MEMINTRSTS, I915_READ(MEMINTRSTS) & ~MEMINT_EVAL_CHG);
+       I915_WRITE16(MEMINTRSTS, MEMINT_EVAL_CHG);
        busy_up = I915_READ(RCPREVBSYTUPAVG);
        busy_down = I915_READ(RCPREVBSYTDNAVG);
        max_avg = I915_READ(RCBMAXAVG);
@@ -300,27 +299,8 @@ static void i915_handle_rps_change(struct drm_device *dev)
                        new_delay = dev_priv->min_delay;
        }
 
-       DRM_DEBUG("rps change requested: %d -> %d\n",
-                 dev_priv->cur_delay, new_delay);
-
-       rgvswctl = I915_READ(MEMSWCTL);
-       if (rgvswctl & MEMCTL_CMD_STS) {
-               DRM_ERROR("gpu busy, RCS change rejected\n");
-               return; /* still busy with another command */
-       }
-
-       /* Program the new state */
-       rgvswctl = (MEMCTL_CMD_CHFREQ << MEMCTL_CMD_SHIFT) |
-               (new_delay << MEMCTL_FREQ_SHIFT) | MEMCTL_SFCAVM;
-       I915_WRITE(MEMSWCTL, rgvswctl);
-       POSTING_READ(MEMSWCTL);
-
-       rgvswctl |= MEMCTL_CMD_STS;
-       I915_WRITE(MEMSWCTL, rgvswctl);
-
-       dev_priv->cur_delay = new_delay;
-
-       DRM_DEBUG("rps changed\n");
+       if (ironlake_set_drps(dev, new_delay))
+               dev_priv->cur_delay = new_delay;
 
        return;
 }
@@ -392,7 +372,7 @@ irqreturn_t ironlake_irq_handler(struct drm_device *dev)
        }
 
        if (de_iir & DE_PCU_EVENT) {
-               I915_WRITE(MEMINTRSTS, I915_READ(MEMINTRSTS));
+               I915_WRITE16(MEMINTRSTS, I915_READ(MEMINTRSTS));
                i915_handle_rps_change(dev);
        }
 
index 7a726840efa7339114a226b6006cca43d40e4e0f..df7224de0aed0be745c2d078c9f4c4c078a3faa5 100644 (file)
 #define CLKCFG_MEM_800                                 (3 << 4)
 #define CLKCFG_MEM_MASK                                        (7 << 4)
 
+#define TR1                    0x11006
+#define TSFS                   0x11020
+#define   TSFS_SLOPE_MASK      0x0000ff00
+#define   TSFS_SLOPE_SHIFT     8
+#define   TSFS_INTR_MASK       0x000000ff
+
 #define CRSTANDVID             0x11100
 #define PXVFREQ_BASE           0x11110 /* P[0-15]VIDFREQ (0x1114c) (Ironlake) */
 #define   PXVFREQ_PX_MASK      0x7f000000
 #define   MEMSTAT_SRC_CTL_STDBY 3
 #define RCPREVBSYTUPAVG                0x113b8
 #define RCPREVBSYTDNAVG                0x113bc
+#define SDEW                   0x1124c
+#define CSIEW0                 0x11250
+#define CSIEW1                 0x11254
+#define CSIEW2                 0x11258
+#define PEW                    0x1125c
+#define DEW                    0x11270
+#define MCHAFE                 0x112c0
+#define CSIEC                  0x112e0
+#define DMIEC                  0x112e4
+#define DDREC                  0x112e8
+#define PEG0EC                 0x112ec
+#define PEG1EC                 0x112f0
+#define GFXEC                  0x112f4
+#define RPPREVBSYTUPAVG                0x113b8
+#define RPPREVBSYTDNAVG                0x113bc
+#define ECR                    0x11600
+#define   ECR_GPFE             (1<<31)
+#define   ECR_IMONE            (1<<30)
+#define   ECR_CAP_MASK         0x0000001f /* Event range, 0-31 */
+#define OGW0                   0x11608
+#define OGW1                   0x1160c
+#define EG0                    0x11610
+#define EG1                    0x11614
+#define EG2                    0x11618
+#define EG3                    0x1161c
+#define EG4                    0x11620
+#define EG5                    0x11624
+#define EG6                    0x11628
+#define EG7                    0x1162c
+#define PXW                    0x11664
+#define PXWL                   0x11680
+#define LCFUSE02               0x116c0
+#define   LCFUSE_HIV_MASK      0x000000ff
+#define CSIPLL0                        0x12c10
+#define DDRMPLL1               0X12c20
 #define PEG_BAND_GAP_DATA      0x14d68
 
 /*
index 4c7c151114f7515a201481e908951c67493c1bc1..e94d1db35364e6bc983ae2fa28bb81e0aa89a939 100644 (file)
@@ -4459,6 +4459,8 @@ static void intel_idle_update(struct work_struct *work)
 
        mutex_lock(&dev->struct_mutex);
 
+       i915_update_gfx_val(dev_priv);
+
        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);
@@ -5045,10 +5047,32 @@ err_unref:
        return NULL;
 }
 
+bool ironlake_set_drps(struct drm_device *dev, u8 val)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u16 rgvswctl;
+
+       rgvswctl = I915_READ16(MEMSWCTL);
+       if (rgvswctl & MEMCTL_CMD_STS) {
+               DRM_DEBUG("gpu busy, RCS change rejected\n");
+               return false; /* still busy with another command */
+       }
+
+       rgvswctl = (MEMCTL_CMD_CHFREQ << MEMCTL_CMD_SHIFT) |
+               (val << MEMCTL_FREQ_SHIFT) | MEMCTL_SFCAVM;
+       I915_WRITE16(MEMSWCTL, rgvswctl);
+       POSTING_READ16(MEMSWCTL);
+
+       rgvswctl |= MEMCTL_CMD_STS;
+       I915_WRITE16(MEMSWCTL, rgvswctl);
+
+       return true;
+}
+
 void ironlake_enable_drps(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       u32 rgvmodectl = I915_READ(MEMMODECTL), rgvswctl;
+       u32 rgvmodectl = I915_READ(MEMMODECTL);
        u8 fmax, fmin, fstart, vstart;
        int i = 0;
 
@@ -5067,13 +5091,21 @@ void ironlake_enable_drps(struct drm_device *dev)
        fmin = (rgvmodectl & MEMMODE_FMIN_MASK);
        fstart = (rgvmodectl & MEMMODE_FSTART_MASK) >>
                MEMMODE_FSTART_SHIFT;
+       fstart = fmax;
+
        vstart = (I915_READ(PXVFREQ_BASE + (fstart * 4)) & PXVFREQ_PX_MASK) >>
                PXVFREQ_PX_SHIFT;
 
-       dev_priv->max_delay = fstart; /* can't go to fmax w/o IPS */
+       dev_priv->fmax = fstart; /* IPS callback will increase this */
+       dev_priv->fstart = fstart;
+
+       dev_priv->max_delay = fmax;
        dev_priv->min_delay = fmin;
        dev_priv->cur_delay = fstart;
 
+       DRM_DEBUG_DRIVER("fmax: %d, fmin: %d, fstart: %d\n", fmax, fmin,
+                        fstart);
+
        I915_WRITE(MEMINTREN, MEMINT_CX_SUPR_EN | MEMINT_EVAL_CHG_EN);
 
        /*
@@ -5095,20 +5127,19 @@ void ironlake_enable_drps(struct drm_device *dev)
        }
        msleep(1);
 
-       rgvswctl = (MEMCTL_CMD_CHFREQ << MEMCTL_CMD_SHIFT) |
-               (fstart << MEMCTL_FREQ_SHIFT) | MEMCTL_SFCAVM;
-       I915_WRITE(MEMSWCTL, rgvswctl);
-       POSTING_READ(MEMSWCTL);
+       ironlake_set_drps(dev, fstart);
 
-       rgvswctl |= MEMCTL_CMD_STS;
-       I915_WRITE(MEMSWCTL, rgvswctl);
+       dev_priv->last_count1 = I915_READ(0x112e4) + I915_READ(0x112e8) +
+               I915_READ(0x112e0);
+       dev_priv->last_time1 = jiffies_to_msecs(jiffies);
+       dev_priv->last_count2 = I915_READ(0x112f4);
+       getrawmonotonic(&dev_priv->last_time2);
 }
 
 void ironlake_disable_drps(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       u32 rgvswctl;
-       u8 fstart;
+       u16 rgvswctl = I915_READ16(MEMSWCTL);
 
        /* Ack interrupts, disable EFC interrupt */
        I915_WRITE(MEMINTREN, I915_READ(MEMINTREN) & ~MEMINT_EVAL_CHG_EN);
@@ -5118,11 +5149,7 @@ void ironlake_disable_drps(struct drm_device *dev)
        I915_WRITE(DEIMR, I915_READ(DEIMR) | DE_PCU_EVENT);
 
        /* Go back to the starting frequency */
-       fstart = (I915_READ(MEMMODECTL) & MEMMODE_FSTART_MASK) >>
-               MEMMODE_FSTART_SHIFT;
-       rgvswctl = (MEMCTL_CMD_CHFREQ << MEMCTL_CMD_SHIFT) |
-               (fstart << MEMCTL_FREQ_SHIFT) | MEMCTL_SFCAVM;
-       I915_WRITE(MEMSWCTL, rgvswctl);
+       ironlake_set_drps(dev, dev_priv->fstart);
        msleep(1);
        rgvswctl |= MEMCTL_CMD_STS;
        I915_WRITE(MEMSWCTL, rgvswctl);
@@ -5130,6 +5157,92 @@ void ironlake_disable_drps(struct drm_device *dev)
 
 }
 
+static unsigned long intel_pxfreq(u32 vidfreq)
+{
+       unsigned long freq;
+       int div = (vidfreq & 0x3f0000) >> 16;
+       int post = (vidfreq & 0x3000) >> 12;
+       int pre = (vidfreq & 0x7);
+
+       if (!pre)
+               return 0;
+
+       freq = ((div * 133333) / ((1<<post) * pre));
+
+       return freq;
+}
+
+void intel_init_emon(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 lcfuse;
+       u8 pxw[16];
+       int i;
+
+       /* Disable to program */
+       I915_WRITE(ECR, 0);
+       POSTING_READ(ECR);
+
+       /* Program energy weights for various events */
+       I915_WRITE(SDEW, 0x15040d00);
+       I915_WRITE(CSIEW0, 0x007f0000);
+       I915_WRITE(CSIEW1, 0x1e220004);
+       I915_WRITE(CSIEW2, 0x04000004);
+
+       for (i = 0; i < 5; i++)
+               I915_WRITE(PEW + (i * 4), 0);
+       for (i = 0; i < 3; i++)
+               I915_WRITE(DEW + (i * 4), 0);
+
+       /* Program P-state weights to account for frequency power adjustment */
+       for (i = 0; i < 16; i++) {
+               u32 pxvidfreq = I915_READ(PXVFREQ_BASE + (i * 4));
+               unsigned long freq = intel_pxfreq(pxvidfreq);
+               unsigned long vid = (pxvidfreq & PXVFREQ_PX_MASK) >>
+                       PXVFREQ_PX_SHIFT;
+               unsigned long val;
+
+               val = vid * vid;
+               val *= (freq / 1000);
+               val *= 255;
+               val /= (127*127*900);
+               if (val > 0xff)
+                       DRM_ERROR("bad pxval: %ld\n", val);
+               pxw[i] = val;
+       }
+       /* Render standby states get 0 weight */
+       pxw[14] = 0;
+       pxw[15] = 0;
+
+       for (i = 0; i < 4; i++) {
+               u32 val = (pxw[i*4] << 24) | (pxw[(i*4)+1] << 16) |
+                       (pxw[(i*4)+2] << 8) | (pxw[(i*4)+3]);
+               I915_WRITE(PXW + (i * 4), val);
+       }
+
+       /* Adjust magic regs to magic values (more experimental results) */
+       I915_WRITE(OGW0, 0);
+       I915_WRITE(OGW1, 0);
+       I915_WRITE(EG0, 0x00007f00);
+       I915_WRITE(EG1, 0x0000000e);
+       I915_WRITE(EG2, 0x000e0000);
+       I915_WRITE(EG3, 0x68000300);
+       I915_WRITE(EG4, 0x42000000);
+       I915_WRITE(EG5, 0x00140031);
+       I915_WRITE(EG6, 0);
+       I915_WRITE(EG7, 0);
+
+       for (i = 0; i < 8; i++)
+               I915_WRITE(PXWL + (i * 4), 0);
+
+       /* Enable PMON + select events */
+       I915_WRITE(ECR, 0x80000019);
+
+       lcfuse = I915_READ(LCFUSE02);
+
+       dev_priv->corr = (lcfuse & LCFUSE_HIV_MASK);
+}
+
 void intel_init_clock_gating(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -5376,8 +5489,10 @@ void intel_modeset_init(struct drm_device *dev)
 
        intel_init_clock_gating(dev);
 
-       if (IS_IRONLAKE_M(dev))
+       if (IS_IRONLAKE_M(dev)) {
                ironlake_enable_drps(dev);
+               intel_init_emon(dev);
+       }
 
        INIT_WORK(&dev_priv->idle_work, intel_idle_update);
        setup_timer(&dev_priv->idle_timer, intel_gpu_idle_timer,