drm/i915: add dynamic performance control support for Ironlake
authorJesse Barnes <jbarnes@virtuousgeek.org>
Fri, 29 Jan 2010 19:27:07 +0000 (11:27 -0800)
committerEric Anholt <eric@anholt.net>
Mon, 22 Feb 2010 16:46:54 +0000 (11:46 -0500)
Ironlake (and 965GM, which this patch doesn't support) supports a
hardware performance and power management feature that allows it to
adjust to changes in GPU load over time with software help.  The goal
if this is to maximize performance/power for a given workload.

This patch enables that feature, which is also a requirement for
supporting Intelligent Power Sharing, a feature which allows for
dynamic budgeting of power between the CPU and GPU in Arrandale
platforms.

Tested-by: ykzhao <yakui.zhao@intel.com>
[anholt: Resolved against the irq handler loop removal]
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Signed-off-by: Eric Anholt <eric@anholt.net>
drivers/gpu/drm/i915/i915_debugfs.c
drivers/gpu/drm/i915/i915_drv.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 a894ade030937c506e13322c2687b3c93b30420f..55340de618ea7b20939ca20705b87a1d62ae9772 100644 (file)
@@ -386,6 +386,97 @@ out:
        return 0;
 }
 
+static int i915_rstdby_delays(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 crstanddelay = I915_READ16(CRSTANDVID);
+
+       seq_printf(m, "w/ctx: %d, w/o ctx: %d\n", (crstanddelay >> 8) & 0x3f, (crstanddelay & 0x3f));
+
+       return 0;
+}
+
+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);
+
+       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);
+
+       return 0;
+}
+
+static int i915_delayfreq_table(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;
+       u32 delayfreq;
+       int i;
+
+       for (i = 0; i < 16; i++) {
+               delayfreq = I915_READ(PXVFREQ_BASE + i * 4);
+               seq_printf(m, "P%02dVIDFREQ: 0x%08x\n", i, delayfreq);
+       }
+
+       return 0;
+}
+
+static inline int MAP_TO_MV(int map)
+{
+       return 1250 - (map * 25);
+}
+
+static int i915_inttoext_table(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;
+       u32 inttoext;
+       int i;
+
+       for (i = 1; i <= 32; i++) {
+               inttoext = I915_READ(INTTOEXT_BASE_ILK + i * 4);
+               seq_printf(m, "INTTOEXT%02d: 0x%08x\n", i, inttoext);
+       }
+
+       return 0;
+}
+
+static int i915_drpc_info(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;
+       u32 rgvmodectl = I915_READ(MEMMODECTL);
+
+       seq_printf(m, "HD boost: %s\n", (rgvmodectl & MEMMODE_BOOST_EN) ?
+                  "yes" : "no");
+       seq_printf(m, "Boost freq: %d\n",
+                  (rgvmodectl & MEMMODE_BOOST_FREQ_MASK) >>
+                  MEMMODE_BOOST_FREQ_SHIFT);
+       seq_printf(m, "HW control enabled: %s\n",
+                  rgvmodectl & MEMMODE_HWIDLE_EN ? "yes" : "no");
+       seq_printf(m, "SW control enabled: %s\n",
+                  rgvmodectl & MEMMODE_SWMODE_EN ? "yes" : "no");
+       seq_printf(m, "Gated voltage change: %s\n",
+                  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",
+                  (rgvmodectl & MEMMODE_FMAX_MASK) >> MEMMODE_FMAX_SHIFT);
+       seq_printf(m, "Min frequency: P%d\n", (rgvmodectl & MEMMODE_FMIN_MASK));
+
+       return 0;
+}
+
 static int
 i915_wedged_open(struct inode *inode,
                 struct file *filp)
@@ -503,6 +594,11 @@ static struct drm_info_list i915_debugfs_list[] = {
        {"i915_ringbuffer_info", i915_ringbuffer_info, 0},
        {"i915_batchbuffers", i915_batchbuffer_info, 0},
        {"i915_error_state", i915_error_state, 0},
+       {"i915_rstdby_delays", i915_rstdby_delays, 0},
+       {"i915_cur_delayinfo", i915_cur_delayinfo, 0},
+       {"i915_delayfreq_table", i915_delayfreq_table, 0},
+       {"i915_inttoext_table", i915_inttoext_table, 0},
+       {"i915_drpc_info", i915_drpc_info, 0},
 };
 #define I915_DEBUGFS_ENTRIES ARRAY_SIZE(i915_debugfs_list)
 
index 79beffcf5936cb2e0ab1fa51a19e29a9a09c3ea7..89f1cb86c32b6d0dd604b70060e58ce8fff488ab 100644 (file)
@@ -583,6 +583,11 @@ static int __init i915_init(void)
                driver.driver_features &= ~DRIVER_MODESET;
 #endif
 
+       if (!(driver.driver_features & DRIVER_MODESET)) {
+               driver.suspend = i915_suspend;
+               driver.resume = i915_resume;
+       }
+
        return drm_init(&driver);
 }
 
index f2742d6d3f54b59e011d61642518c8133dfa9de1..2e493ec1042b31e08264c4535f050f715cca478c 100644 (file)
@@ -452,6 +452,7 @@ typedef struct drm_i915_private {
        u32 savePIPEB_DATA_N1;
        u32 savePIPEB_LINK_M1;
        u32 savePIPEB_LINK_N1;
+       u32 saveRSTDBYCTL;
 
        struct {
                struct drm_mm gtt_space;
@@ -590,7 +591,12 @@ typedef struct drm_i915_private {
        int child_dev_num;
        struct child_device_config *child_dev;
        struct drm_connector *int_lvds_connector;
+
        bool mchbar_need_disable;
+
+       u8 cur_delay;
+       u8 min_delay;
+       u8 max_delay;
 } drm_i915_private_t;
 
 /** driver private structure attached to each drm_gem_object */
index a17d6bdfe63e6efe15d329755049e48ec9ee87cc..8b35f5e1c511bd3fb52677ea024b6109c599f7ea 100644 (file)
@@ -269,6 +269,57 @@ static void i915_hotplug_work_func(struct work_struct *work)
        drm_sysfs_hotplug_event(dev);
 }
 
+static void i915_handle_rps_change(struct drm_device *dev)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       u32 slow_up, slow_down, max_avg, min_avg;
+       u16 rgvswctl;
+       u8 new_delay = dev_priv->cur_delay;
+
+       I915_WRITE(MEMINTRSTS, I915_READ(MEMINTRSTS) & ~MEMINT_EVAL_CHG);
+       slow_up = I915_READ(RCPREVBSYTUPAVG);
+       slow_down = I915_READ(RCPREVBSYTDNAVG);
+       max_avg = I915_READ(RCBMAXAVG);
+       min_avg = I915_READ(RCBMINAVG);
+
+       /* Handle RCS change request from hw */
+       if (slow_up > max_avg) {
+               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 (slow_down < min_avg) {
+               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;
+       }
+
+       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 slow, RCS change rejected\n");
+               return; /* still slow 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");
+
+       return;
+}
+
 irqreturn_t ironlake_irq_handler(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
@@ -331,6 +382,11 @@ irqreturn_t ironlake_irq_handler(struct drm_device *dev)
                queue_work(dev_priv->wq, &dev_priv->hotplug_work);
        }
 
+       if (de_iir & DE_PCU_EVENT) {
+               I915_WRITE(MEMINTRSTS, I915_READ(MEMINTRSTS));
+               i915_handle_rps_change(dev);
+       }
+
        /* should clear PCH hotplug event before clear CPU irq */
        I915_WRITE(SDEIIR, pch_iir);
        I915_WRITE(GTIIR, gt_iir);
@@ -1064,6 +1120,13 @@ static int ironlake_irq_postinstall(struct drm_device *dev)
        I915_WRITE(SDEIER, dev_priv->pch_irq_enable_reg);
        (void) I915_READ(SDEIER);
 
+       if (IS_IRONLAKE_M(dev)) {
+               /* Clear & enable PCU event interrupts */
+               I915_WRITE(DEIIR, DE_PCU_EVENT);
+               I915_WRITE(DEIER, I915_READ(DEIER) | DE_PCU_EVENT);
+               ironlake_enable_display_irq(dev_priv, DE_PCU_EVENT);
+       }
+
        return 0;
 }
 
index 6defb7f4734852665e33cb4f756a0ad264ff32f1..c3948ee37c13976e1a5f89e01416748a4b0e34a9 100644 (file)
@@ -61,6 +61,7 @@
 #define   GC_CLOCK_100_200             (1 << 0)
 #define   GC_CLOCK_100_133             (2 << 0)
 #define   GC_CLOCK_166_250             (3 << 0)
+#define GCFGC2 0xda
 #define GCFGC  0xf0 /* 915+ only */
 #define   GC_LOW_FREQUENCY_ENABLE      (1 << 7)
 #define   GC_DISPLAY_CLOCK_190_200_MHZ (0 << 4)
 #define   I915_PIPE_CONTROL_NOTIFY_INTERRUPT           (1<<18)
 #define   I915_DISPLAY_PORT_INTERRUPT                  (1<<17)
 #define   I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT   (1<<15)
-#define   I915_GMCH_THERMAL_SENSOR_EVENT_INTERRUPT     (1<<14)
+#define   I915_GMCH_THERMAL_SENSOR_EVENT_INTERRUPT     (1<<14) /* p-state */
 #define   I915_HWB_OOM_INTERRUPT                       (1<<13)
 #define   I915_SYNC_STATUS_INTERRUPT                   (1<<12)
 #define   I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT  (1<<11)
 #define CLKCFG_MEM_800                                 (3 << 4)
 #define CLKCFG_MEM_MASK                                        (7 << 4)
 
-/** GM965 GM45 render standby register */
-#define MCHBAR_RENDER_STANDBY  0x111B8
+#define CRSTANDVID             0x11100
+#define PXVFREQ_BASE           0x11110 /* P[0-15]VIDFREQ (0x1114c) (Ironlake) */
+#define   PXVFREQ_PX_MASK      0x7f000000
+#define   PXVFREQ_PX_SHIFT     24
+#define VIDFREQ_BASE           0x11110
+#define VIDFREQ1               0x11110 /* VIDFREQ1-4 (0x1111c) (Cantiga) */
+#define VIDFREQ2               0x11114
+#define VIDFREQ3               0x11118
+#define VIDFREQ4               0x1111c
+#define   VIDFREQ_P0_MASK      0x1f000000
+#define   VIDFREQ_P0_SHIFT     24
+#define   VIDFREQ_P0_CSCLK_MASK        0x00f00000
+#define   VIDFREQ_P0_CSCLK_SHIFT 20
+#define   VIDFREQ_P0_CRCLK_MASK        0x000f0000
+#define   VIDFREQ_P0_CRCLK_SHIFT 16
+#define   VIDFREQ_P1_MASK      0x00001f00
+#define   VIDFREQ_P1_SHIFT     8
+#define   VIDFREQ_P1_CSCLK_MASK        0x000000f0
+#define   VIDFREQ_P1_CSCLK_SHIFT 4
+#define   VIDFREQ_P1_CRCLK_MASK        0x0000000f
+#define INTTOEXT_BASE_ILK      0x11300
+#define INTTOEXT_BASE          0x11120 /* INTTOEXT1-8 (0x1113c) */
+#define   INTTOEXT_MAP3_SHIFT  24
+#define   INTTOEXT_MAP3_MASK   (0x1f << INTTOEXT_MAP3_SHIFT)
+#define   INTTOEXT_MAP2_SHIFT  16
+#define   INTTOEXT_MAP2_MASK   (0x1f << INTTOEXT_MAP2_SHIFT)
+#define   INTTOEXT_MAP1_SHIFT  8
+#define   INTTOEXT_MAP1_MASK   (0x1f << INTTOEXT_MAP1_SHIFT)
+#define   INTTOEXT_MAP0_SHIFT  0
+#define   INTTOEXT_MAP0_MASK   (0x1f << INTTOEXT_MAP0_SHIFT)
+#define MEMSWCTL               0x11170 /* Ironlake only */
+#define   MEMCTL_CMD_MASK      0xe000
+#define   MEMCTL_CMD_SHIFT     13
+#define   MEMCTL_CMD_RCLK_OFF  0
+#define   MEMCTL_CMD_RCLK_ON   1
+#define   MEMCTL_CMD_CHFREQ    2
+#define   MEMCTL_CMD_CHVID     3
+#define   MEMCTL_CMD_VMMOFF    4
+#define   MEMCTL_CMD_VMMON     5
+#define   MEMCTL_CMD_STS       (1<<12) /* write 1 triggers command, clears
+                                          when command complete */
+#define   MEMCTL_FREQ_MASK     0x0f00 /* jitter, from 0-15 */
+#define   MEMCTL_FREQ_SHIFT    8
+#define   MEMCTL_SFCAVM                (1<<7)
+#define   MEMCTL_TGT_VID_MASK  0x007f
+#define MEMIHYST               0x1117c
+#define MEMINTREN              0x11180 /* 16 bits */
+#define   MEMINT_RSEXIT_EN     (1<<8)
+#define   MEMINT_CX_SUPR_EN    (1<<7)
+#define   MEMINT_CONT_BUSY_EN  (1<<6)
+#define   MEMINT_AVG_BUSY_EN   (1<<5)
+#define   MEMINT_EVAL_CHG_EN   (1<<4)
+#define   MEMINT_MON_IDLE_EN   (1<<3)
+#define   MEMINT_UP_EVAL_EN    (1<<2)
+#define   MEMINT_DOWN_EVAL_EN  (1<<1)
+#define   MEMINT_SW_CMD_EN     (1<<0)
+#define MEMINTRSTR             0x11182 /* 16 bits */
+#define   MEM_RSEXIT_MASK      0xc000
+#define   MEM_RSEXIT_SHIFT     14
+#define   MEM_CONT_BUSY_MASK   0x3000
+#define   MEM_CONT_BUSY_SHIFT  12
+#define   MEM_AVG_BUSY_MASK    0x0c00
+#define   MEM_AVG_BUSY_SHIFT   10
+#define   MEM_EVAL_CHG_MASK    0x0300
+#define   MEM_EVAL_BUSY_SHIFT  8
+#define   MEM_MON_IDLE_MASK    0x00c0
+#define   MEM_MON_IDLE_SHIFT   6
+#define   MEM_UP_EVAL_MASK     0x0030
+#define   MEM_UP_EVAL_SHIFT    4
+#define   MEM_DOWN_EVAL_MASK   0x000c
+#define   MEM_DOWN_EVAL_SHIFT  2
+#define   MEM_SW_CMD_MASK      0x0003
+#define   MEM_INT_STEER_GFX    0
+#define   MEM_INT_STEER_CMR    1
+#define   MEM_INT_STEER_SMI    2
+#define   MEM_INT_STEER_SCI    3
+#define MEMINTRSTS             0x11184
+#define   MEMINT_RSEXIT                (1<<7)
+#define   MEMINT_CONT_BUSY     (1<<6)
+#define   MEMINT_AVG_BUSY      (1<<5)
+#define   MEMINT_EVAL_CHG      (1<<4)
+#define   MEMINT_MON_IDLE      (1<<3)
+#define   MEMINT_UP_EVAL       (1<<2)
+#define   MEMINT_DOWN_EVAL     (1<<1)
+#define   MEMINT_SW_CMD                (1<<0)
+#define MEMMODECTL             0x11190
+#define   MEMMODE_BOOST_EN     (1<<31)
+#define   MEMMODE_BOOST_FREQ_MASK 0x0f000000 /* jitter for boost, 0-15 */
+#define   MEMMODE_BOOST_FREQ_SHIFT 24
+#define   MEMMODE_IDLE_MODE_MASK 0x00030000
+#define   MEMMODE_IDLE_MODE_SHIFT 16
+#define   MEMMODE_IDLE_MODE_EVAL 0
+#define   MEMMODE_IDLE_MODE_CONT 1
+#define   MEMMODE_HWIDLE_EN    (1<<15)
+#define   MEMMODE_SWMODE_EN    (1<<14)
+#define   MEMMODE_RCLK_GATE    (1<<13)
+#define   MEMMODE_HW_UPDATE    (1<<12)
+#define   MEMMODE_FSTART_MASK  0x00000f00 /* starting jitter, 0-15 */
+#define   MEMMODE_FSTART_SHIFT 8
+#define   MEMMODE_FMAX_MASK    0x000000f0 /* max jitter, 0-15 */
+#define   MEMMODE_FMAX_SHIFT   4
+#define   MEMMODE_FMIN_MASK    0x0000000f /* min jitter, 0-15 */
+#define RCBMAXAVG              0x1119c
+#define MEMSWCTL2              0x1119e /* Cantiga only */
+#define   SWMEMCMD_RENDER_OFF  (0 << 13)
+#define   SWMEMCMD_RENDER_ON   (1 << 13)
+#define   SWMEMCMD_SWFREQ      (2 << 13)
+#define   SWMEMCMD_TARVID      (3 << 13)
+#define   SWMEMCMD_VRM_OFF     (4 << 13)
+#define   SWMEMCMD_VRM_ON      (5 << 13)
+#define   CMDSTS               (1<<12)
+#define   SFCAVM               (1<<11)
+#define   SWFREQ_MASK          0x0380 /* P0-7 */
+#define   SWFREQ_SHIFT         7
+#define   TARVID_MASK          0x001f
+#define MEMSTAT_CTG            0x111a0
+#define RCBMINAVG              0x111a0
+#define RCUPEI                 0x111b0
+#define RCDNEI                 0x111b4
+#define RSTDBYCTL              0x111b8
 #define   RCX_SW_EXIT          (1<<23)
 #define   RSX_STATUS_MASK      0x00700000
+#define VIDCTL                 0x111c0
+#define VIDSTS                 0x111c8
+#define VIDSTART               0x111cc /* 8 bits */
+#define MEMSTAT_ILK                    0x111f8
+#define   MEMSTAT_VID_MASK     0x7f00
+#define   MEMSTAT_VID_SHIFT    8
+#define   MEMSTAT_PSTATE_MASK  0x00f8
+#define   MEMSTAT_PSTATE_SHIFT  3
+#define   MEMSTAT_MON_ACTV     (1<<2)
+#define   MEMSTAT_SRC_CTL_MASK 0x0003
+#define   MEMSTAT_SRC_CTL_CORE 0
+#define   MEMSTAT_SRC_CTL_TRB  1
+#define   MEMSTAT_SRC_CTL_THM  2
+#define   MEMSTAT_SRC_CTL_STDBY 3
+#define RCPREVBSYTUPAVG                0x113b8
+#define RCPREVBSYTDNAVG                0x113bc
 #define PEG_BAND_GAP_DATA      0x14d68
 
 /*
index a3b90c9561dc6623b6671ad4aaefbfd957e65ed8..2c346645acfac38a7919cedf28315877f65b4ebc 100644 (file)
@@ -682,6 +682,7 @@ void i915_restore_display(struct drm_device *dev)
                I915_WRITE(PCH_PP_OFF_DELAYS, dev_priv->savePP_OFF_DELAYS);
                I915_WRITE(PCH_PP_DIVISOR, dev_priv->savePP_DIVISOR);
                I915_WRITE(PCH_PP_CONTROL, dev_priv->savePP_CONTROL);
+               I915_WRITE(RSTDBYCTL, dev_priv->saveRSTDBYCTL);
        } else {
                I915_WRITE(PFIT_PGM_RATIOS, dev_priv->savePFIT_PGM_RATIOS);
                I915_WRITE(BLC_PWM_CTL, dev_priv->saveBLC_PWM_CTL);
@@ -745,11 +746,15 @@ int i915_save_state(struct drm_device *dev)
                dev_priv->saveGTIMR = I915_READ(GTIMR);
                dev_priv->saveFDI_RXA_IMR = I915_READ(FDI_RXA_IMR);
                dev_priv->saveFDI_RXB_IMR = I915_READ(FDI_RXB_IMR);
+               dev_priv->saveRSTDBYCTL = I915_READ(RSTDBYCTL);
        } else {
                dev_priv->saveIER = I915_READ(IER);
                dev_priv->saveIMR = I915_READ(IMR);
        }
 
+       if (IS_IRONLAKE_M(dev))
+               ironlake_disable_drps(dev);
+
        /* Cache mode state */
        dev_priv->saveCACHE_MODE_0 = I915_READ(CACHE_MODE_0);
 
@@ -820,6 +825,9 @@ int i915_restore_state(struct drm_device *dev)
        /* Clock gating state */
        intel_init_clock_gating(dev);
 
+       if (IS_IRONLAKE_M(dev))
+               ironlake_enable_drps(dev);
+
        /* Cache mode state */
        I915_WRITE (CACHE_MODE_0, dev_priv->saveCACHE_MODE_0 | 0xffff0000);
 
index af9ec217cd1d98735523ba44a973fc746f84decb..4a93f7a0f58d4b1de85afa6146c5df15f60d1f37 100644 (file)
@@ -4616,6 +4616,91 @@ err_unref:
        return NULL;
 }
 
+void ironlake_enable_drps(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 rgvmodectl = I915_READ(MEMMODECTL), rgvswctl;
+       u8 fmax, fmin, fstart, vstart;
+       int i = 0;
+
+       /* 100ms RC evaluation intervals */
+       I915_WRITE(RCUPEI, 100000);
+       I915_WRITE(RCDNEI, 100000);
+
+       /* Set max/min thresholds to 90ms and 80ms respectively */
+       I915_WRITE(RCBMAXAVG, 90000);
+       I915_WRITE(RCBMINAVG, 80000);
+
+       I915_WRITE(MEMIHYST, 1);
+
+       /* Set up min, max, and cur for interrupt handling */
+       fmax = (rgvmodectl & MEMMODE_FMAX_MASK) >> MEMMODE_FMAX_SHIFT;
+       fmin = (rgvmodectl & MEMMODE_FMIN_MASK);
+       fstart = (rgvmodectl & MEMMODE_FSTART_MASK) >>
+               MEMMODE_FSTART_SHIFT;
+       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->min_delay = fmin;
+       dev_priv->cur_delay = fstart;
+
+       I915_WRITE(MEMINTREN, MEMINT_CX_SUPR_EN | MEMINT_EVAL_CHG_EN);
+
+       /*
+        * Interrupts will be enabled in ironlake_irq_postinstall
+        */
+
+       I915_WRITE(VIDSTART, vstart);
+       POSTING_READ(VIDSTART);
+
+       rgvmodectl |= MEMMODE_SWMODE_EN;
+       I915_WRITE(MEMMODECTL, rgvmodectl);
+
+       while (I915_READ(MEMSWCTL) & MEMCTL_CMD_STS) {
+               if (i++ > 100) {
+                       DRM_ERROR("stuck trying to change perf mode\n");
+                       break;
+               }
+               msleep(1);
+       }
+       msleep(1);
+
+       rgvswctl = (MEMCTL_CMD_CHFREQ << MEMCTL_CMD_SHIFT) |
+               (fstart << MEMCTL_FREQ_SHIFT) | MEMCTL_SFCAVM;
+       I915_WRITE(MEMSWCTL, rgvswctl);
+       POSTING_READ(MEMSWCTL);
+
+       rgvswctl |= MEMCTL_CMD_STS;
+       I915_WRITE(MEMSWCTL, rgvswctl);
+}
+
+void ironlake_disable_drps(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 rgvswctl;
+       u8 fstart;
+
+       /* Ack interrupts, disable EFC interrupt */
+       I915_WRITE(MEMINTREN, I915_READ(MEMINTREN) & ~MEMINT_EVAL_CHG_EN);
+       I915_WRITE(MEMINTRSTS, MEMINT_EVAL_CHG);
+       I915_WRITE(DEIER, I915_READ(DEIER) & ~DE_PCU_EVENT);
+       I915_WRITE(DEIIR, DE_PCU_EVENT);
+       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);
+       I915_WRITE(MEMSWCTL, rgvswctl);
+       msleep(1);
+       rgvswctl |= MEMCTL_CMD_STS;
+       I915_WRITE(MEMSWCTL, rgvswctl);
+       msleep(1);
+
+}
+
 void intel_init_clock_gating(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -4685,8 +4770,8 @@ void intel_init_clock_gating(struct drm_device *dev)
 
                if (obj_priv) {
                        I915_WRITE(PWRCTXA, obj_priv->gtt_offset | PWRCTX_EN);
-                       I915_WRITE(MCHBAR_RENDER_STANDBY,
-                                  I915_READ(MCHBAR_RENDER_STANDBY) & ~RCX_SW_EXIT);
+                       I915_WRITE(RSTDBYCTL, I915_READ(RSTDBYCTL) &
+                                  ~RCX_SW_EXIT);
                }
        }
 }
@@ -4799,11 +4884,6 @@ void intel_modeset_init(struct drm_device *dev)
        DRM_DEBUG_KMS("%d display pipe%s available.\n",
                  num_pipe, num_pipe > 1 ? "s" : "");
 
-       if (IS_I85X(dev))
-               pci_read_config_word(dev->pdev, HPLLCC, &dev_priv->orig_clock);
-       else if (IS_I9XX(dev) || IS_G4X(dev))
-               pci_read_config_word(dev->pdev, GCFGC, &dev_priv->orig_clock);
-
        for (i = 0; i < num_pipe; i++) {
                intel_crtc_init(dev, i);
        }
@@ -4812,6 +4892,9 @@ void intel_modeset_init(struct drm_device *dev)
 
        intel_init_clock_gating(dev);
 
+       if (IS_IRONLAKE_M(dev))
+               ironlake_enable_drps(dev);
+
        INIT_WORK(&dev_priv->idle_work, intel_idle_update);
        setup_timer(&dev_priv->idle_timer, intel_gpu_idle_timer,
                    (unsigned long)dev);
@@ -4859,6 +4942,9 @@ void intel_modeset_cleanup(struct drm_device *dev)
                drm_gem_object_unreference(dev_priv->pwrctx);
        }
 
+       if (IS_IRONLAKE_M(dev))
+               ironlake_disable_drps(dev);
+
        mutex_unlock(&dev->struct_mutex);
 
        drm_mode_config_cleanup(dev);
index a51573da1ff601f5cd868adf7485249b3708d45a..3a467ca578579a09f6af9731608611bcacd904e0 100644 (file)
@@ -209,6 +209,8 @@ extern void intel_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
 extern void intel_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
                                    u16 *blue, int regno);
 extern void intel_init_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 int intel_framebuffer_create(struct drm_device *dev,
                                    struct drm_mode_fb_cmd *mode_cmd,