Revert "drm/i915: disable PCH ports if needed when disabling a CRTC"
[linux-flexiantxendom0-natty.git] / drivers / gpu / drm / i915 / intel_display.c
index aba1c33..841558b 100644 (file)
@@ -642,26 +642,23 @@ static const intel_limit_t intel_limits_ironlake_display_port = {
         .find_pll = intel_find_pll_ironlake_dp,
 };
 
-static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc)
+static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc,
+                                               int refclk)
 {
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        const intel_limit_t *limit;
-       int refclk = 120;
 
        if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
-               if (dev_priv->lvds_use_ssc && dev_priv->lvds_ssc_freq == 100)
-                       refclk = 100;
-
                if ((I915_READ(PCH_LVDS) & LVDS_CLKB_POWER_MASK) ==
                    LVDS_CLKB_POWER_UP) {
                        /* LVDS dual channel */
-                       if (refclk == 100)
+                       if (refclk == 100000)
                                limit = &intel_limits_ironlake_dual_lvds_100m;
                        else
                                limit = &intel_limits_ironlake_dual_lvds;
                } else {
-                       if (refclk == 100)
+                       if (refclk == 100000)
                                limit = &intel_limits_ironlake_single_lvds_100m;
                        else
                                limit = &intel_limits_ironlake_single_lvds;
@@ -702,13 +699,13 @@ static const intel_limit_t *intel_g4x_limit(struct drm_crtc *crtc)
        return limit;
 }
 
-static const intel_limit_t *intel_limit(struct drm_crtc *crtc)
+static const intel_limit_t *intel_limit(struct drm_crtc *crtc, int refclk)
 {
        struct drm_device *dev = crtc->dev;
        const intel_limit_t *limit;
 
        if (HAS_PCH_SPLIT(dev))
-               limit = intel_ironlake_limit(crtc);
+               limit = intel_ironlake_limit(crtc, refclk);
        else if (IS_G4X(dev)) {
                limit = intel_g4x_limit(crtc);
        } else if (IS_PINEVIEW(dev)) {
@@ -773,11 +770,10 @@ bool intel_pipe_has_type(struct drm_crtc *crtc, int type)
  * the given connectors.
  */
 
-static bool intel_PLL_is_valid(struct drm_crtc *crtc, intel_clock_t *clock)
+static bool intel_PLL_is_valid(struct drm_device *dev,
+                              const intel_limit_t *limit,
+                              const intel_clock_t *clock)
 {
-       const intel_limit_t *limit = intel_limit (crtc);
-       struct drm_device *dev = crtc->dev;
-
        if (clock->p1  < limit->p1.min  || limit->p1.max  < clock->p1)
                INTELPllInvalid ("p1 out of range\n");
        if (clock->p   < limit->p.min   || limit->p.max   < clock->p)
@@ -849,8 +845,8 @@ intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
                                        int this_err;
 
                                        intel_clock(dev, refclk, &clock);
-
-                                       if (!intel_PLL_is_valid(crtc, &clock))
+                                       if (!intel_PLL_is_valid(dev, limit,
+                                                               &clock))
                                                continue;
 
                                        this_err = abs(clock.dot - target);
@@ -912,9 +908,11 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
                                        int this_err;
 
                                        intel_clock(dev, refclk, &clock);
-                                       if (!intel_PLL_is_valid(crtc, &clock))
+                                       if (!intel_PLL_is_valid(dev, limit,
+                                                               &clock))
                                                continue;
-                                       this_err = abs(clock.dot - target) ;
+
+                                       this_err = abs(clock.dot - target);
                                        if (this_err < err_most) {
                                                *best_clock = clock;
                                                err_most = this_err;
@@ -1215,6 +1213,26 @@ static bool g4x_fbc_enabled(struct drm_device *dev)
        return I915_READ(DPFC_CONTROL) & DPFC_CTL_EN;
 }
 
+static void sandybridge_blit_fbc_update(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 blt_ecoskpd;
+
+       /* Make sure blitter notifies FBC of writes */
+       __gen6_gt_force_wake_get(dev_priv);
+       blt_ecoskpd = I915_READ(GEN6_BLITTER_ECOSKPD);
+       blt_ecoskpd |= GEN6_BLITTER_FBC_NOTIFY <<
+               GEN6_BLITTER_LOCK_SHIFT;
+       I915_WRITE(GEN6_BLITTER_ECOSKPD, blt_ecoskpd);
+       blt_ecoskpd |= GEN6_BLITTER_FBC_NOTIFY;
+       I915_WRITE(GEN6_BLITTER_ECOSKPD, blt_ecoskpd);
+       blt_ecoskpd &= ~(GEN6_BLITTER_FBC_NOTIFY <<
+                        GEN6_BLITTER_LOCK_SHIFT);
+       I915_WRITE(GEN6_BLITTER_ECOSKPD, blt_ecoskpd);
+       POSTING_READ(GEN6_BLITTER_ECOSKPD);
+       __gen6_gt_force_wake_put(dev_priv);
+}
+
 static void ironlake_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
 {
        struct drm_device *dev = crtc->dev;
@@ -1264,6 +1282,13 @@ static void ironlake_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
        /* enable it... */
        I915_WRITE(ILK_DPFC_CONTROL, dpfc_ctl | DPFC_CTL_EN);
 
+       if (IS_GEN6(dev)) {
+               I915_WRITE(SNB_DPFC_CTL_SA,
+                          SNB_CPU_FENCE_ENABLE | dev_priv->cfb_fence);
+               I915_WRITE(DPFC_CPU_FENCE_OFFSET, crtc->y);
+               sandybridge_blit_fbc_update(dev);
+       }
+
        DRM_DEBUG_KMS("enabled fbc on plane %d\n", intel_crtc->plane);
 }
 
@@ -1605,19 +1630,19 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
                struct drm_i915_gem_object *obj = to_intel_framebuffer(old_fb)->obj;
 
                wait_event(dev_priv->pending_flip_queue,
+                          atomic_read(&dev_priv->mm.wedged) ||
                           atomic_read(&obj->pending_flip) == 0);
 
                /* Big Hammer, we also need to ensure that any pending
                 * MI_WAIT_FOR_EVENT inside a user batch buffer on the
                 * current scanout is retired before unpinning the old
                 * framebuffer.
+                *
+                * This should only fail upon a hung GPU, in which case we
+                * can safely continue.
                 */
                ret = i915_gem_object_flush_gpu(obj, false);
-               if (ret) {
-                       i915_gem_object_unpin(to_intel_framebuffer(crtc->fb)->obj);
-                       mutex_unlock(&dev->struct_mutex);
-                       return ret;
-               }
+               (void) ret;
        }
 
        ret = intel_pipe_set_base_atomic(crtc, crtc->fb, x, y,
@@ -1628,8 +1653,10 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
                return ret;
        }
 
-       if (old_fb)
+       if (old_fb) {
+               intel_wait_for_vblank(dev, intel_crtc->pipe);
                i915_gem_object_unpin(to_intel_framebuffer(old_fb)->obj);
+       }
 
        mutex_unlock(&dev->struct_mutex);
 
@@ -2018,6 +2045,31 @@ static void intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc)
                   atomic_read(&obj->pending_flip) == 0);
 }
 
+static bool intel_crtc_driving_pch(struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_mode_config *mode_config = &dev->mode_config;
+       struct intel_encoder *encoder;
+
+       /*
+        * If there's a non-PCH eDP on this crtc, it must be DP_A, and that
+        * must be driven by its own crtc; no sharing is possible.
+        */
+       list_for_each_entry(encoder, &mode_config->encoder_list, base.head) {
+               if (encoder->base.crtc != crtc)
+                       continue;
+
+               switch (encoder->type) {
+               case INTEL_OUTPUT_EDP:
+                       if (!intel_encoder_is_pch_edp(&encoder->base))
+                               return false;
+                       continue;
+               }
+       }
+
+       return true;
+}
+
 static void ironlake_crtc_enable(struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
@@ -2026,6 +2078,7 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
        int pipe = intel_crtc->pipe;
        int plane = intel_crtc->plane;
        u32 reg, temp;
+       bool is_pch_port = false;
 
        if (intel_crtc->active)
                return;
@@ -2039,7 +2092,56 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
                        I915_WRITE(PCH_LVDS, temp | LVDS_PORT_EN);
        }
 
-       ironlake_fdi_enable(crtc);
+       is_pch_port = intel_crtc_driving_pch(crtc);
+
+       if (is_pch_port)
+               ironlake_fdi_enable(crtc);
+       else {
+               /* disable CPU FDI tx and PCH FDI rx */
+               reg = FDI_TX_CTL(pipe);
+               temp = I915_READ(reg);
+               I915_WRITE(reg, temp & ~FDI_TX_ENABLE);
+               POSTING_READ(reg);
+
+               reg = FDI_RX_CTL(pipe);
+               temp = I915_READ(reg);
+               temp &= ~(0x7 << 16);
+               temp |= (I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK) << 11;
+               I915_WRITE(reg, temp & ~FDI_RX_ENABLE);
+
+               POSTING_READ(reg);
+               udelay(100);
+
+               /* Ironlake workaround, disable clock pointer after downing FDI */
+               if (HAS_PCH_IBX(dev))
+                       I915_WRITE(FDI_RX_CHICKEN(pipe),
+                                  I915_READ(FDI_RX_CHICKEN(pipe) &
+                                            ~FDI_RX_PHASE_SYNC_POINTER_ENABLE));
+
+               /* still set train pattern 1 */
+               reg = FDI_TX_CTL(pipe);
+               temp = I915_READ(reg);
+               temp &= ~FDI_LINK_TRAIN_NONE;
+               temp |= FDI_LINK_TRAIN_PATTERN_1;
+               I915_WRITE(reg, temp);
+
+               reg = FDI_RX_CTL(pipe);
+               temp = I915_READ(reg);
+               if (HAS_PCH_CPT(dev)) {
+                       temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
+                       temp |= FDI_LINK_TRAIN_PATTERN_1_CPT;
+               } else {
+                       temp &= ~FDI_LINK_TRAIN_NONE;
+                       temp |= FDI_LINK_TRAIN_PATTERN_1;
+               }
+               /* BPC in FDI rx is consistent with that in PIPECONF */
+               temp &= ~(0x07 << 16);
+               temp |= (I915_READ(PIPECONF(pipe)) & PIPE_BPC_MASK) << 11;
+               I915_WRITE(reg, temp);
+
+               POSTING_READ(reg);
+               udelay(100);
+       }
 
        /* Enable panel fitting for LVDS */
        if (dev_priv->pch_pf_size &&
@@ -2073,6 +2175,10 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
                intel_flush_display_plane(dev, plane);
        }
 
+       /* Skip the PCH stuff if possible */
+       if (!is_pch_port)
+               goto done;
+
        /* For PCH output, training FDI link */
        if (IS_GEN6(dev))
                gen6_fdi_link_train(crtc);
@@ -2157,7 +2263,7 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
        I915_WRITE(reg, temp | TRANS_ENABLE);
        if (wait_for(I915_READ(reg) & TRANS_STATE_ENABLE, 100))
                DRM_ERROR("failed to enable transcoder %d\n", pipe);
-
+done:
        intel_crtc_load_lut(crtc);
        intel_update_fbc(dev);
        intel_crtc_update_cursor(crtc, true);
@@ -2845,6 +2951,39 @@ static struct intel_watermark_params ironlake_cursor_srwm_info = {
        ILK_FIFO_LINE_SIZE
 };
 
+static struct intel_watermark_params sandybridge_display_wm_info = {
+       SNB_DISPLAY_FIFO,
+       SNB_DISPLAY_MAXWM,
+       SNB_DISPLAY_DFTWM,
+       2,
+       SNB_FIFO_LINE_SIZE
+};
+
+static struct intel_watermark_params sandybridge_cursor_wm_info = {
+       SNB_CURSOR_FIFO,
+       SNB_CURSOR_MAXWM,
+       SNB_CURSOR_DFTWM,
+       2,
+       SNB_FIFO_LINE_SIZE
+};
+
+static struct intel_watermark_params sandybridge_display_srwm_info = {
+       SNB_DISPLAY_SR_FIFO,
+       SNB_DISPLAY_MAX_SRWM,
+       SNB_DISPLAY_DFT_SRWM,
+       2,
+       SNB_FIFO_LINE_SIZE
+};
+
+static struct intel_watermark_params sandybridge_cursor_srwm_info = {
+       SNB_CURSOR_SR_FIFO,
+       SNB_CURSOR_MAX_SRWM,
+       SNB_CURSOR_DFT_SRWM,
+       2,
+       SNB_FIFO_LINE_SIZE
+};
+
+
 /**
  * intel_calculate_wm - calculate watermark level
  * @clock_in_khz: pixel clock
@@ -3378,12 +3517,17 @@ static void i830_update_wm(struct drm_device *dev, int planea_clock, int unused,
 
 static bool ironlake_compute_wm0(struct drm_device *dev,
                                 int pipe,
+                                const struct intel_watermark_params *display,
+                                int display_latency_ns,
+                                const struct intel_watermark_params *cursor,
+                                int cursor_latency_ns,
                                 int *plane_wm,
                                 int *cursor_wm)
 {
        struct drm_crtc *crtc;
-       int htotal, hdisplay, clock, pixel_size = 0;
-       int line_time_us, line_count, entries;
+       int htotal, hdisplay, clock, pixel_size;
+       int line_time_us, line_count;
+       int entries, tlb_miss;
 
        crtc = intel_get_crtc_for_pipe(dev, pipe);
        if (crtc->fb == NULL || !crtc->enabled)
@@ -3395,37 +3539,141 @@ static bool ironlake_compute_wm0(struct drm_device *dev,
        pixel_size = crtc->fb->bits_per_pixel / 8;
 
        /* Use the small buffer method to calculate plane watermark */
-       entries = ((clock * pixel_size / 1000) * ILK_LP0_PLANE_LATENCY) / 1000;
-       entries = DIV_ROUND_UP(entries,
-                              ironlake_display_wm_info.cacheline_size);
-       *plane_wm = entries + ironlake_display_wm_info.guard_size;
-       if (*plane_wm > (int)ironlake_display_wm_info.max_wm)
-               *plane_wm = ironlake_display_wm_info.max_wm;
+       entries = ((clock * pixel_size / 1000) * display_latency_ns) / 1000;
+       tlb_miss = display->fifo_size*display->cacheline_size - hdisplay * 8;
+       if (tlb_miss > 0)
+               entries += tlb_miss;
+       entries = DIV_ROUND_UP(entries, display->cacheline_size);
+       *plane_wm = entries + display->guard_size;
+       if (*plane_wm > (int)display->max_wm)
+               *plane_wm = display->max_wm;
 
        /* Use the large buffer method to calculate cursor watermark */
        line_time_us = ((htotal * 1000) / clock);
-       line_count = (ILK_LP0_CURSOR_LATENCY / line_time_us + 1000) / 1000;
+       line_count = (cursor_latency_ns / line_time_us + 1000) / 1000;
        entries = line_count * 64 * pixel_size;
-       entries = DIV_ROUND_UP(entries,
-                              ironlake_cursor_wm_info.cacheline_size);
-       *cursor_wm = entries + ironlake_cursor_wm_info.guard_size;
-       if (*cursor_wm > ironlake_cursor_wm_info.max_wm)
-               *cursor_wm = ironlake_cursor_wm_info.max_wm;
+       tlb_miss = cursor->fifo_size*cursor->cacheline_size - hdisplay * 8;
+       if (tlb_miss > 0)
+               entries += tlb_miss;
+       entries = DIV_ROUND_UP(entries, cursor->cacheline_size);
+       *cursor_wm = entries + cursor->guard_size;
+       if (*cursor_wm > (int)cursor->max_wm)
+               *cursor_wm = (int)cursor->max_wm;
 
        return true;
 }
 
+/*
+ * Check the wm result.
+ *
+ * If any calculated watermark values is larger than the maximum value that
+ * can be programmed into the associated watermark register, that watermark
+ * must be disabled.
+ */
+static bool ironlake_check_srwm(struct drm_device *dev, int level,
+                               int fbc_wm, int display_wm, int cursor_wm,
+                               const struct intel_watermark_params *display,
+                               const struct intel_watermark_params *cursor)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       DRM_DEBUG_KMS("watermark %d: display plane %d, fbc lines %d,"
+                     " cursor %d\n", level, display_wm, fbc_wm, cursor_wm);
+
+       if (fbc_wm > SNB_FBC_MAX_SRWM) {
+               DRM_DEBUG_KMS("fbc watermark(%d) is too large(%d), disabling wm%d+\n",
+                             fbc_wm, SNB_FBC_MAX_SRWM, level);
+
+               /* fbc has it's own way to disable FBC WM */
+               I915_WRITE(DISP_ARB_CTL,
+                          I915_READ(DISP_ARB_CTL) | DISP_FBC_WM_DIS);
+               return false;
+       }
+
+       if (display_wm > display->max_wm) {
+               DRM_DEBUG_KMS("display watermark(%d) is too large(%d), disabling wm%d+\n",
+                             display_wm, SNB_DISPLAY_MAX_SRWM, level);
+               return false;
+       }
+
+       if (cursor_wm > cursor->max_wm) {
+               DRM_DEBUG_KMS("cursor watermark(%d) is too large(%d), disabling wm%d+\n",
+                             cursor_wm, SNB_CURSOR_MAX_SRWM, level);
+               return false;
+       }
+
+       if (!(fbc_wm || display_wm || cursor_wm)) {
+               DRM_DEBUG_KMS("latency %d is 0, disabling wm%d+\n", level, level);
+               return false;
+       }
+
+       return true;
+}
+
+/*
+ * Compute watermark values of WM[1-3],
+ */
+static bool ironlake_compute_srwm(struct drm_device *dev, int level,
+                                 int hdisplay, int htotal,
+                                 int pixel_size, int clock, int latency_ns,
+                                 const struct intel_watermark_params *display,
+                                 const struct intel_watermark_params *cursor,
+                                 int *fbc_wm, int *display_wm, int *cursor_wm)
+{
+
+       unsigned long line_time_us;
+       int line_count, line_size;
+       int small, large;
+       int entries;
+
+       if (!latency_ns) {
+               *fbc_wm = *display_wm = *cursor_wm = 0;
+               return false;
+       }
+
+       line_time_us = (htotal * 1000) / clock;
+       line_count = (latency_ns / line_time_us + 1000) / 1000;
+       line_size = hdisplay * pixel_size;
+
+       /* Use the minimum of the small and large buffer method for primary */
+       small = ((clock * pixel_size / 1000) * latency_ns) / 1000;
+       large = line_count * line_size;
+
+       entries = DIV_ROUND_UP(min(small, large), display->cacheline_size);
+       *display_wm = entries + display->guard_size;
+
+       /*
+        * Spec says:
+        * FBC WM = ((Final Primary WM * 64) / number of bytes per line) + 2
+        */
+       *fbc_wm = DIV_ROUND_UP(*display_wm * 64, line_size) + 2;
+
+       /* calculate the self-refresh watermark for display cursor */
+       entries = line_count * pixel_size * 64;
+       entries = DIV_ROUND_UP(entries, cursor->cacheline_size);
+       *cursor_wm = entries + cursor->guard_size;
+
+       return ironlake_check_srwm(dev, level,
+                                  *fbc_wm, *display_wm, *cursor_wm,
+                                  display, cursor);
+}
+
 static void ironlake_update_wm(struct drm_device *dev,
                               int planea_clock, int planeb_clock,
-                              int sr_hdisplay, int sr_htotal,
+                              int hdisplay, int htotal,
                               int pixel_size)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       int plane_wm, cursor_wm, enabled;
-       int tmp;
+       int fbc_wm, plane_wm, cursor_wm, enabled;
+       int clock;
 
        enabled = 0;
-       if (ironlake_compute_wm0(dev, 0, &plane_wm, &cursor_wm)) {
+       if (ironlake_compute_wm0(dev, 0,
+                                &ironlake_display_wm_info,
+                                ILK_LP0_PLANE_LATENCY,
+                                &ironlake_cursor_wm_info,
+                                ILK_LP0_CURSOR_LATENCY,
+                                &plane_wm, &cursor_wm)) {
                I915_WRITE(WM0_PIPEA_ILK,
                           (plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm);
                DRM_DEBUG_KMS("FIFO watermarks For pipe A -"
@@ -3434,7 +3682,12 @@ static void ironlake_update_wm(struct drm_device *dev,
                enabled++;
        }
 
-       if (ironlake_compute_wm0(dev, 1, &plane_wm, &cursor_wm)) {
+       if (ironlake_compute_wm0(dev, 1,
+                                &ironlake_display_wm_info,
+                                ILK_LP0_PLANE_LATENCY,
+                                &ironlake_cursor_wm_info,
+                                ILK_LP0_CURSOR_LATENCY,
+                                &plane_wm, &cursor_wm)) {
                I915_WRITE(WM0_PIPEB_ILK,
                           (plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm);
                DRM_DEBUG_KMS("FIFO watermarks For pipe B -"
@@ -3447,57 +3700,151 @@ static void ironlake_update_wm(struct drm_device *dev,
         * Calculate and update the self-refresh watermark only when one
         * display plane is used.
         */
-       tmp = 0;
-       if (enabled == 1) {
-               unsigned long line_time_us;
-               int small, large, plane_fbc;
-               int sr_clock, entries;
-               int line_count, line_size;
-               /* Read the self-refresh latency. The unit is 0.5us */
-               int ilk_sr_latency = I915_READ(MLTR_ILK) & ILK_SRLT_MASK;
+       I915_WRITE(WM3_LP_ILK, 0);
+       I915_WRITE(WM2_LP_ILK, 0);
+       I915_WRITE(WM1_LP_ILK, 0);
 
-               sr_clock = planea_clock ? planea_clock : planeb_clock;
-               line_time_us = (sr_htotal * 1000) / sr_clock;
+       if (enabled != 1)
+               return;
 
-               /* Use ns/us then divide to preserve precision */
-               line_count = ((ilk_sr_latency * 500) / line_time_us + 1000)
-                       / 1000;
-               line_size = sr_hdisplay * pixel_size;
+       clock = planea_clock ? planea_clock : planeb_clock;
 
-               /* Use the minimum of the small and large buffer method for primary */
-               small = ((sr_clock * pixel_size / 1000) * (ilk_sr_latency * 500)) / 1000;
-               large = line_count * line_size;
+       /* WM1 */
+       if (!ironlake_compute_srwm(dev, 1, hdisplay, htotal, pixel_size,
+                                  clock, ILK_READ_WM1_LATENCY() * 500,
+                                  &ironlake_display_srwm_info,
+                                  &ironlake_cursor_srwm_info,
+                                  &fbc_wm, &plane_wm, &cursor_wm))
+               return;
 
-               entries = DIV_ROUND_UP(min(small, large),
-                                      ironlake_display_srwm_info.cacheline_size);
+       I915_WRITE(WM1_LP_ILK,
+                  WM1_LP_SR_EN |
+                  (ILK_READ_WM1_LATENCY() << WM1_LP_LATENCY_SHIFT) |
+                  (fbc_wm << WM1_LP_FBC_SHIFT) |
+                  (plane_wm << WM1_LP_SR_SHIFT) |
+                  cursor_wm);
+
+       /* WM2 */
+       if (!ironlake_compute_srwm(dev, 2, hdisplay, htotal, pixel_size,
+                                  clock, ILK_READ_WM2_LATENCY() * 500,
+                                  &ironlake_display_srwm_info,
+                                  &ironlake_cursor_srwm_info,
+                                  &fbc_wm, &plane_wm, &cursor_wm))
+               return;
 
-               plane_fbc = entries * 64;
-               plane_fbc = DIV_ROUND_UP(plane_fbc, line_size);
+       I915_WRITE(WM2_LP_ILK,
+                  WM2_LP_EN |
+                  (ILK_READ_WM2_LATENCY() << WM1_LP_LATENCY_SHIFT) |
+                  (fbc_wm << WM1_LP_FBC_SHIFT) |
+                  (plane_wm << WM1_LP_SR_SHIFT) |
+                  cursor_wm);
 
-               plane_wm = entries + ironlake_display_srwm_info.guard_size;
-               if (plane_wm > (int)ironlake_display_srwm_info.max_wm)
-                       plane_wm = ironlake_display_srwm_info.max_wm;
+       /*
+        * WM3 is unsupported on ILK, probably because we don't have latency
+        * data for that power state
+        */
+}
 
-               /* calculate the self-refresh watermark for display cursor */
-               entries = line_count * pixel_size * 64;
-               entries = DIV_ROUND_UP(entries,
-                                      ironlake_cursor_srwm_info.cacheline_size);
+static void sandybridge_update_wm(struct drm_device *dev,
+                              int planea_clock, int planeb_clock,
+                              int hdisplay, int htotal,
+                              int pixel_size)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int latency = SNB_READ_WM0_LATENCY() * 100;     /* In unit 0.1us */
+       int fbc_wm, plane_wm, cursor_wm, enabled;
+       int clock;
 
-               cursor_wm = entries + ironlake_cursor_srwm_info.guard_size;
-               if (cursor_wm > (int)ironlake_cursor_srwm_info.max_wm)
-                       cursor_wm = ironlake_cursor_srwm_info.max_wm;
+       enabled = 0;
+       if (ironlake_compute_wm0(dev, 0,
+                                &sandybridge_display_wm_info, latency,
+                                &sandybridge_cursor_wm_info, latency,
+                                &plane_wm, &cursor_wm)) {
+               I915_WRITE(WM0_PIPEA_ILK,
+                          (plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm);
+               DRM_DEBUG_KMS("FIFO watermarks For pipe A -"
+                             " plane %d, " "cursor: %d\n",
+                             plane_wm, cursor_wm);
+               enabled++;
+       }
 
-               /* configure watermark and enable self-refresh */
-               tmp = (WM1_LP_SR_EN |
-                      (ilk_sr_latency << WM1_LP_LATENCY_SHIFT) |
-                      (plane_fbc << WM1_LP_FBC_SHIFT) |
-                      (plane_wm << WM1_LP_SR_SHIFT) |
-                      cursor_wm);
-               DRM_DEBUG_KMS("self-refresh watermark: display plane %d, fbc lines %d,"
-                             " cursor %d\n", plane_wm, plane_fbc, cursor_wm);
+       if (ironlake_compute_wm0(dev, 1,
+                                &sandybridge_display_wm_info, latency,
+                                &sandybridge_cursor_wm_info, latency,
+                                &plane_wm, &cursor_wm)) {
+               I915_WRITE(WM0_PIPEB_ILK,
+                          (plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm);
+               DRM_DEBUG_KMS("FIFO watermarks For pipe B -"
+                             " plane %d, cursor: %d\n",
+                             plane_wm, cursor_wm);
+               enabled++;
        }
-       I915_WRITE(WM1_LP_ILK, tmp);
-       /* XXX setup WM2 and WM3 */
+
+       /*
+        * Calculate and update the self-refresh watermark only when one
+        * display plane is used.
+        *
+        * SNB support 3 levels of watermark.
+        *
+        * WM1/WM2/WM2 watermarks have to be enabled in the ascending order,
+        * and disabled in the descending order
+        *
+        */
+       I915_WRITE(WM3_LP_ILK, 0);
+       I915_WRITE(WM2_LP_ILK, 0);
+       I915_WRITE(WM1_LP_ILK, 0);
+
+       if (enabled != 1)
+               return;
+
+       clock = planea_clock ? planea_clock : planeb_clock;
+
+       /* WM1 */
+       if (!ironlake_compute_srwm(dev, 1, hdisplay, htotal, pixel_size,
+                                  clock, SNB_READ_WM1_LATENCY() * 500,
+                                  &sandybridge_display_srwm_info,
+                                  &sandybridge_cursor_srwm_info,
+                                  &fbc_wm, &plane_wm, &cursor_wm))
+               return;
+
+       I915_WRITE(WM1_LP_ILK,
+                  WM1_LP_SR_EN |
+                  (SNB_READ_WM1_LATENCY() << WM1_LP_LATENCY_SHIFT) |
+                  (fbc_wm << WM1_LP_FBC_SHIFT) |
+                  (plane_wm << WM1_LP_SR_SHIFT) |
+                  cursor_wm);
+
+       /* WM2 */
+       if (!ironlake_compute_srwm(dev, 2,
+                                  hdisplay, htotal, pixel_size,
+                                  clock, SNB_READ_WM2_LATENCY() * 500,
+                                  &sandybridge_display_srwm_info,
+                                  &sandybridge_cursor_srwm_info,
+                                  &fbc_wm, &plane_wm, &cursor_wm))
+               return;
+
+       I915_WRITE(WM2_LP_ILK,
+                  WM2_LP_EN |
+                  (SNB_READ_WM2_LATENCY() << WM1_LP_LATENCY_SHIFT) |
+                  (fbc_wm << WM1_LP_FBC_SHIFT) |
+                  (plane_wm << WM1_LP_SR_SHIFT) |
+                  cursor_wm);
+
+       /* WM3 */
+       if (!ironlake_compute_srwm(dev, 3,
+                                  hdisplay, htotal, pixel_size,
+                                  clock, SNB_READ_WM3_LATENCY() * 500,
+                                  &sandybridge_display_srwm_info,
+                                  &sandybridge_cursor_srwm_info,
+                                  &fbc_wm, &plane_wm, &cursor_wm))
+               return;
+
+       I915_WRITE(WM3_LP_ILK,
+                  WM3_LP_EN |
+                  (SNB_READ_WM3_LATENCY() << WM1_LP_LATENCY_SHIFT) |
+                  (fbc_wm << WM1_LP_FBC_SHIFT) |
+                  (plane_wm << WM1_LP_SR_SHIFT) |
+                  cursor_wm);
 }
 
 /**
@@ -3575,6 +3922,11 @@ static void intel_update_watermarks(struct drm_device *dev)
                                    sr_hdisplay, sr_htotal, pixel_size);
 }
 
+static inline bool intel_panel_use_ssc(struct drm_i915_private *dev_priv)
+{
+       return dev_priv->lvds_use_ssc && i915_panel_use_ssc;
+}
+
 static int intel_crtc_mode_set(struct drm_crtc *crtc,
                               struct drm_display_mode *mode,
                               struct drm_display_mode *adjusted_mode,
@@ -3637,7 +3989,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                num_connectors++;
        }
 
-       if (is_lvds && dev_priv->lvds_use_ssc && num_connectors < 2) {
+       if (is_lvds && intel_panel_use_ssc(dev_priv) && num_connectors < 2) {
                refclk = dev_priv->lvds_ssc_freq * 1000;
                DRM_DEBUG_KMS("using SSC reference clock of %d MHz\n",
                              refclk / 1000);
@@ -3655,7 +4007,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
         * refclk, or FALSE.  The returned values represent the clock equation:
         * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2.
         */
-       limit = intel_limit(crtc);
+       limit = intel_limit(crtc, refclk);
        ok = limit->find_pll(limit, crtc, adjusted_mode->clock, refclk, &clock);
        if (!ok) {
                DRM_ERROR("Couldn't find PLL settings for mode!\n");
@@ -3709,7 +4061,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                int lane = 0, link_bw, bpp;
                /* CPU eDP doesn't require FDI link, so just set DP M/N
                   according to current link config */
-               if (has_edp_encoder && !intel_encoder_is_pch_edp(&encoder->base)) {
+               if (has_edp_encoder && !intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
                        target_clock = mode->clock;
                        intel_edp_link_config(has_edp_encoder,
                                              &lane, &link_bw);
@@ -3812,7 +4164,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                udelay(200);
 
                if (has_edp_encoder) {
-                       if (dev_priv->lvds_use_ssc) {
+                       if (intel_panel_use_ssc(dev_priv)) {
                                temp |= DREF_SSC1_ENABLE;
                                I915_WRITE(PCH_DREF_CONTROL, temp);
 
@@ -3823,13 +4175,13 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
 
                        /* Enable CPU source on CPU attached eDP */
                        if (!intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
-                               if (dev_priv->lvds_use_ssc)
+                               if (intel_panel_use_ssc(dev_priv))
                                        temp |= DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD;
                                else
                                        temp |= DREF_CPU_SOURCE_OUTPUT_NONSPREAD;
                        } else {
                                /* Enable SSC on PCH eDP if needed */
-                               if (dev_priv->lvds_use_ssc) {
+                               if (intel_panel_use_ssc(dev_priv)) {
                                        DRM_ERROR("enabling SSC on PCH\n");
                                        temp |= DREF_SUPERSPREAD_SOURCE_ENABLE;
                                }
@@ -3857,7 +4209,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                int factor = 21;
 
                if (is_lvds) {
-                       if ((dev_priv->lvds_use_ssc &&
+                       if ((intel_panel_use_ssc(dev_priv) &&
                             dev_priv->lvds_ssc_freq == 100) ||
                            (I915_READ(PCH_LVDS) & LVDS_CLKB_POWER_MASK) == LVDS_CLKB_POWER_UP)
                                factor = 25;
@@ -3936,7 +4288,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                /* XXX: just matching BIOS for now */
                /*      dpll |= PLL_REF_INPUT_TVCLKINBC; */
                dpll |= 3;
-       else if (is_lvds && dev_priv->lvds_use_ssc && num_connectors < 2)
+       else if (is_lvds && intel_panel_use_ssc(dev_priv) && num_connectors < 2)
                dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN;
        else
                dpll |= PLL_REF_INPUT_DREFCLK;
@@ -4758,8 +5110,14 @@ static void intel_gpu_idle_timer(unsigned long arg)
        struct drm_device *dev = (struct drm_device *)arg;
        drm_i915_private_t *dev_priv = dev->dev_private;
 
-       dev_priv->busy = false;
+       if (!list_empty(&dev_priv->mm.active_list)) {
+               /* Still processing requests, so just re-arm the timer. */
+               mod_timer(&dev_priv->idle_timer, jiffies +
+                         msecs_to_jiffies(GPU_IDLE_TIMEOUT));
+               return;
+       }
 
+       dev_priv->busy = false;
        queue_work(dev_priv->wq, &dev_priv->idle_work);
 }
 
@@ -4770,9 +5128,17 @@ static void intel_crtc_idle_timer(unsigned long arg)
        struct intel_crtc *intel_crtc = (struct intel_crtc *)arg;
        struct drm_crtc *crtc = &intel_crtc->base;
        drm_i915_private_t *dev_priv = crtc->dev->dev_private;
+       struct intel_framebuffer *intel_fb;
 
-       intel_crtc->busy = false;
+       intel_fb = to_intel_framebuffer(crtc->fb);
+       if (intel_fb && intel_fb->obj->active) {
+               /* The framebuffer is still being accessed by the GPU. */
+               mod_timer(&intel_crtc->idle_timer, jiffies +
+                         msecs_to_jiffies(CRTC_IDLE_TIMEOUT));
+               return;
+       }
 
+       intel_crtc->busy = false;
        queue_work(dev_priv->wq, &dev_priv->idle_work);
 }
 
@@ -4782,8 +5148,8 @@ static void intel_increase_pllclock(struct drm_crtc *crtc)
        drm_i915_private_t *dev_priv = dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        int pipe = intel_crtc->pipe;
-       int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B;
-       int dpll = I915_READ(dpll_reg);
+       int dpll_reg = DPLL(pipe);
+       int dpll;
 
        if (HAS_PCH_SPLIT(dev))
                return;
@@ -4791,17 +5157,19 @@ static void intel_increase_pllclock(struct drm_crtc *crtc)
        if (!dev_priv->lvds_downclock_avail)
                return;
 
+       dpll = I915_READ(dpll_reg);
        if (!HAS_PIPE_CXSR(dev) && (dpll & DISPLAY_RATE_SELECT_FPA1)) {
                DRM_DEBUG_DRIVER("upclocking LVDS\n");
 
                /* Unlock panel regs */
-               I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) |
-                          PANEL_UNLOCK_REGS);
+               I915_WRITE(PP_CONTROL,
+                          I915_READ(PP_CONTROL) | PANEL_UNLOCK_REGS);
 
                dpll &= ~DISPLAY_RATE_SELECT_FPA1;
                I915_WRITE(dpll_reg, dpll);
-               dpll = I915_READ(dpll_reg);
+               POSTING_READ(dpll_reg);
                intel_wait_for_vblank(dev, pipe);
+
                dpll = I915_READ(dpll_reg);
                if (dpll & DISPLAY_RATE_SELECT_FPA1)
                        DRM_DEBUG_DRIVER("failed to upclock LVDS!\n");
@@ -5003,13 +5371,15 @@ static void do_intel_finish_page_flip(struct drm_device *dev,
        struct intel_unpin_work *work;
        struct drm_i915_gem_object *obj;
        struct drm_pending_vblank_event *e;
-       struct timeval now;
+       struct timeval tnow, tvbl;
        unsigned long flags;
 
        /* Ignore early vblank irqs */
        if (intel_crtc == NULL)
                return;
 
+       do_gettimeofday(&tnow);
+
        spin_lock_irqsave(&dev->event_lock, flags);
        work = intel_crtc->unpin_work;
        if (work == NULL || !work->pending) {
@@ -5018,19 +5388,40 @@ static void do_intel_finish_page_flip(struct drm_device *dev,
        }
 
        intel_crtc->unpin_work = NULL;
-       drm_vblank_put(dev, intel_crtc->pipe);
 
        if (work->event) {
                e = work->event;
-               do_gettimeofday(&now);
-               e->event.sequence = drm_vblank_count(dev, intel_crtc->pipe);
-               e->event.tv_sec = now.tv_sec;
-               e->event.tv_usec = now.tv_usec;
+               e->event.sequence = drm_vblank_count_and_time(dev, intel_crtc->pipe, &tvbl);
+
+               /* Called before vblank count and timestamps have
+                * been updated for the vblank interval of flip
+                * completion? Need to increment vblank count and
+                * add one videorefresh duration to returned timestamp
+                * to account for this. We assume this happened if we
+                * get called over 0.9 frame durations after the last
+                * timestamped vblank.
+                *
+                * This calculation can not be used with vrefresh rates
+                * below 5Hz (10Hz to be on the safe side) without
+                * promoting to 64 integers.
+                */
+               if (10 * (timeval_to_ns(&tnow) - timeval_to_ns(&tvbl)) >
+                   9 * crtc->framedur_ns) {
+                       e->event.sequence++;
+                       tvbl = ns_to_timeval(timeval_to_ns(&tvbl) +
+                                            crtc->framedur_ns);
+               }
+
+               e->event.tv_sec = tvbl.tv_sec;
+               e->event.tv_usec = tvbl.tv_usec;
+
                list_add_tail(&e->base.link,
                              &e->base.file_priv->event_list);
                wake_up_interruptible(&e->base.file_priv->event_wait);
        }
 
+       drm_vblank_put(dev, intel_crtc->pipe);
+
        spin_unlock_irqrestore(&dev->event_lock, flags);
 
        obj = work->old_fb_obj;
@@ -5239,25 +5630,6 @@ cleanup_work:
        return ret;
 }
 
-static struct drm_crtc_helper_funcs intel_helper_funcs = {
-       .dpms = intel_crtc_dpms,
-       .mode_fixup = intel_crtc_mode_fixup,
-       .mode_set = intel_crtc_mode_set,
-       .mode_set_base = intel_pipe_set_base,
-       .mode_set_base_atomic = intel_pipe_set_base_atomic,
-       .load_lut = intel_crtc_load_lut,
-       .disable = intel_crtc_disable,
-};
-
-static const struct drm_crtc_funcs intel_crtc_funcs = {
-       .cursor_set = intel_crtc_cursor_set,
-       .cursor_move = intel_crtc_cursor_move,
-       .gamma_set = intel_crtc_gamma_set,
-       .set_config = drm_crtc_helper_set_config,
-       .destroy = intel_crtc_destroy,
-       .page_flip = intel_crtc_page_flip,
-};
-
 static void intel_sanitize_modesetting(struct drm_device *dev,
                                       int pipe, int plane)
 {
@@ -5308,6 +5680,42 @@ static void intel_sanitize_modesetting(struct drm_device *dev,
        }
 }
 
+static void intel_crtc_reset(struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+       /* Reset flags back to the 'unknown' status so that they
+        * will be correctly set on the initial modeset.
+        */
+       intel_crtc->dpms_mode = -1;
+
+       /* We need to fix up any BIOS configuration that conflicts with
+        * our expectations.
+        */
+       intel_sanitize_modesetting(dev, intel_crtc->pipe, intel_crtc->plane);
+}
+
+static struct drm_crtc_helper_funcs intel_helper_funcs = {
+       .dpms = intel_crtc_dpms,
+       .mode_fixup = intel_crtc_mode_fixup,
+       .mode_set = intel_crtc_mode_set,
+       .mode_set_base = intel_pipe_set_base,
+       .mode_set_base_atomic = intel_pipe_set_base_atomic,
+       .load_lut = intel_crtc_load_lut,
+       .disable = intel_crtc_disable,
+};
+
+static const struct drm_crtc_funcs intel_crtc_funcs = {
+       .reset = intel_crtc_reset,
+       .cursor_set = intel_crtc_cursor_set,
+       .cursor_move = intel_crtc_cursor_move,
+       .gamma_set = intel_crtc_gamma_set,
+       .set_config = drm_crtc_helper_set_config,
+       .destroy = intel_crtc_destroy,
+       .page_flip = intel_crtc_page_flip,
+};
+
 static void intel_crtc_init(struct drm_device *dev, int pipe)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
@@ -5340,8 +5748,7 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
        dev_priv->plane_to_crtc_mapping[intel_crtc->plane] = &intel_crtc->base;
        dev_priv->pipe_to_crtc_mapping[intel_crtc->pipe] = &intel_crtc->base;
 
-       intel_crtc->cursor_addr = 0;
-       intel_crtc->dpms_mode = -1;
+       intel_crtc_reset(&intel_crtc->base);
        intel_crtc->active = true; /* force the pipe off on setup_init_config */
 
        if (HAS_PCH_SPLIT(dev)) {
@@ -5358,8 +5765,6 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
 
        setup_timer(&intel_crtc->idle_timer, intel_crtc_idle_timer,
                    (unsigned long)intel_crtc);
-
-       intel_sanitize_modesetting(dev, intel_crtc->pipe, intel_crtc->plane);
 }
 
 int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data,
@@ -5404,6 +5809,23 @@ static int intel_encoder_clones(struct drm_device *dev, int type_mask)
        return index_mask;
 }
 
+static bool has_edp_a(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       if (!IS_MOBILE(dev))
+               return false;
+
+       if ((I915_READ(DP_A) & DP_DETECTED) == 0)
+               return false;
+
+       if (IS_GEN5(dev) &&
+           (I915_READ(ILK_DISPLAY_CHICKEN_FUSES) & ILK_eDP_A_DISABLE))
+               return false;
+
+       return true;
+}
+
 static void intel_setup_outputs(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -5421,7 +5843,7 @@ static void intel_setup_outputs(struct drm_device *dev)
        if (HAS_PCH_SPLIT(dev)) {
                dpd_is_edp = intel_dpd_is_edp(dev);
 
-               if (IS_MOBILE(dev) && (I915_READ(DP_A) & DP_DETECTED))
+               if (has_edp_a(dev))
                        intel_dp_init(dev, DP_A);
 
                if (dpd_is_edp && (I915_READ(PCH_DP_D) & DP_DETECTED))
@@ -5506,6 +5928,8 @@ static void intel_setup_outputs(struct drm_device *dev)
                encoder->base.possible_clones =
                        intel_encoder_clones(dev, encoder->clone_mask);
        }
+
+       intel_panel_setup_backlight(dev);
 }
 
 static void intel_user_framebuffer_destroy(struct drm_framebuffer *fb)
@@ -5581,8 +6005,10 @@ intel_user_framebuffer_create(struct drm_device *dev,
                return ERR_PTR(-ENOENT);
 
        intel_fb = kzalloc(sizeof(*intel_fb), GFP_KERNEL);
-       if (!intel_fb)
+       if (!intel_fb) {
+               drm_gem_object_unreference_unlocked(&obj->base);
                return ERR_PTR(-ENOMEM);
+       }
 
        ret = intel_framebuffer_init(dev, intel_fb, mode_cmd, obj);
        if (ret) {
@@ -5742,6 +6168,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;
@@ -5828,7 +6273,123 @@ void intel_init_emon(struct drm_device *dev)
        dev_priv->corr = (lcfuse & LCFUSE_HIV_MASK);
 }
 
-void intel_init_clock_gating(struct drm_device *dev)
+void gen6_enable_rps(struct drm_i915_private *dev_priv)
+{
+       u32 rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
+       u32 gt_perf_status = I915_READ(GEN6_GT_PERF_STATUS);
+       u32 pcu_mbox;
+       int cur_freq, min_freq, max_freq;
+       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_gt_force_wake_get(dev_priv);
+
+       /* disable the counters and set deterministic 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_EI_MODE(1) |
+                  GEN6_RC_CTL_HW_ENABLE);
+
+       I915_WRITE(GEN6_RPNSWREQ,
+                  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");
+
+       min_freq = (rp_state_cap & 0xff0000) >> 16;
+       max_freq = rp_state_cap & 0xff;
+       cur_freq = (gt_perf_status & 0xff00) >> 8;
+
+       /* Check for overclock support */
+       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_MAILBOX, GEN6_READ_OC_PARAMS);
+       pcu_mbox = I915_READ(GEN6_PCODE_DATA);
+       if (wait_for((I915_READ(GEN6_PCODE_MAILBOX) & GEN6_PCODE_READY) == 0,
+                    500))
+               DRM_ERROR("timeout waiting for pcode mailbox to finish\n");
+       if (pcu_mbox & (1<<31)) { /* OC supported */
+               max_freq = pcu_mbox & 0xff;
+               DRM_DEBUG_DRIVER("overclocking supported, adjusting frequency max to %dMHz\n", pcu_mbox * 100);
+       }
+
+       /* In units of 100MHz */
+       dev_priv->max_delay = max_freq;
+       dev_priv->min_delay = min_freq;
+       dev_priv->cur_delay = cur_freq;
+
+       /* 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);
+       I915_WRITE(GEN6_PMIMR, 0);
+       /* enable all PM interrupts */
+       I915_WRITE(GEN6_PMINTRMSK, 0);
+
+       __gen6_gt_force_wake_put(dev_priv);
+}
+
+void intel_enable_clock_gating(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
 
@@ -5841,13 +6402,17 @@ void intel_init_clock_gating(struct drm_device *dev)
 
                if (IS_GEN5(dev)) {
                        /* Required for FBC */
-                       dspclk_gate |= DPFDUNIT_CLOCK_GATE_DISABLE;
+                       dspclk_gate |= DPFCUNIT_CLOCK_GATE_DISABLE |
+                               DPFCRUNIT_CLOCK_GATE_DISABLE |
+                               DPFDUNIT_CLOCK_GATE_DISABLE;
                        /* Required for CxSR */
                        dspclk_gate |= DPARBUNIT_CLOCK_GATE_DISABLE;
 
                        I915_WRITE(PCH_3DCGDIS0,
                                   MARIUNIT_CLOCK_GATE_DISABLE |
                                   SVSMUNIT_CLOCK_GATE_DISABLE);
+                       I915_WRITE(PCH_3DCGDIS1,
+                                  VFMUNIT_CLOCK_GATE_DISABLE);
                }
 
                I915_WRITE(PCH_DSPCLK_GATE_D, dspclk_gate);
@@ -5876,9 +6441,9 @@ void intel_init_clock_gating(struct drm_device *dev)
                        I915_WRITE(DISP_ARB_CTL,
                                        (I915_READ(DISP_ARB_CTL) |
                                                DISP_FBC_WM_DIS));
-               I915_WRITE(WM3_LP_ILK, 0);
-               I915_WRITE(WM2_LP_ILK, 0);
-               I915_WRITE(WM1_LP_ILK, 0);
+                       I915_WRITE(WM3_LP_ILK, 0);
+                       I915_WRITE(WM2_LP_ILK, 0);
+                       I915_WRITE(WM1_LP_ILK, 0);
                }
                /*
                 * Based on the document from hardware guys the following bits
@@ -5910,6 +6475,39 @@ void intel_init_clock_gating(struct drm_device *dev)
                                   _3D_CHICKEN2_WM_READ_PIPELINED << 16 |
                                   _3D_CHICKEN2_WM_READ_PIPELINED);
                }
+
+               if (IS_GEN6(dev)) {
+                       I915_WRITE(WM3_LP_ILK, 0);
+                       I915_WRITE(WM2_LP_ILK, 0);
+                       I915_WRITE(WM1_LP_ILK, 0);
+
+                       /*
+                        * According to the spec the following bits should be
+                        * set in order to enable memory self-refresh and fbc:
+                        * The bit21 and bit22 of 0x42000
+                        * The bit21 and bit22 of 0x42004
+                        * The bit5 and bit7 of 0x42020
+                        * The bit14 of 0x70180
+                        * The bit14 of 0x71180
+                        */
+                       I915_WRITE(ILK_DISPLAY_CHICKEN1,
+                                  I915_READ(ILK_DISPLAY_CHICKEN1) |
+                                  ILK_FBCQ_DIS | ILK_PABSTRETCH_DIS);
+                       I915_WRITE(ILK_DISPLAY_CHICKEN2,
+                                  I915_READ(ILK_DISPLAY_CHICKEN2) |
+                                  ILK_DPARB_GATE | ILK_VSDPFD_FULL);
+                       I915_WRITE(ILK_DSPCLK_GATE,
+                                  I915_READ(ILK_DSPCLK_GATE) |
+                                  ILK_DPARB_CLK_GATE  |
+                                  ILK_DPFD_CLK_GATE);
+
+                       I915_WRITE(DSPACNTR,
+                                  I915_READ(DSPACNTR) |
+                                  DISPPLANE_TRICKLE_FEED_DISABLE);
+                       I915_WRITE(DSPBCNTR,
+                                  I915_READ(DSPBCNTR) |
+                                  DISPPLANE_TRICKLE_FEED_DISABLE);
+               }
        } else if (IS_G4X(dev)) {
                uint32_t dspclk_gate;
                I915_WRITE(RENCLK_GATE_D1, 0);
@@ -5947,44 +6545,106 @@ void intel_init_clock_gating(struct drm_device *dev)
        } else if (IS_I830(dev)) {
                I915_WRITE(DSPCLK_GATE_D, OVRUNIT_CLOCK_GATE_DISABLE);
        }
+}
+
+static void ironlake_teardown_rc6(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       if (dev_priv->renderctx) {
+               i915_gem_object_unpin(dev_priv->renderctx);
+               drm_gem_object_unreference(&dev_priv->renderctx->base);
+               dev_priv->renderctx = NULL;
+       }
+
+       if (dev_priv->pwrctx) {
+               i915_gem_object_unpin(dev_priv->pwrctx);
+               drm_gem_object_unreference(&dev_priv->pwrctx->base);
+               dev_priv->pwrctx = NULL;
+       }
+}
+
+static void ironlake_disable_rc6(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       if (I915_READ(PWRCTXA)) {
+               /* Wake the GPU, prevent RC6, then restore RSTDBYCTL */
+               I915_WRITE(RSTDBYCTL, I915_READ(RSTDBYCTL) | RCX_SW_EXIT);
+               wait_for(((I915_READ(RSTDBYCTL) & RSX_STATUS_MASK) == RSX_STATUS_ON),
+                        50);
+
+               I915_WRITE(PWRCTXA, 0);
+               POSTING_READ(PWRCTXA);
+
+               I915_WRITE(RSTDBYCTL, I915_READ(RSTDBYCTL) & ~RCX_SW_EXIT);
+               POSTING_READ(RSTDBYCTL);
+       }
+
+       ironlake_teardown_rc6(dev);
+}
+
+static int ironlake_setup_rc6(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       if (dev_priv->renderctx == NULL)
+               dev_priv->renderctx = intel_alloc_context_page(dev);
+       if (!dev_priv->renderctx)
+               return -ENOMEM;
+
+       if (dev_priv->pwrctx == NULL)
+               dev_priv->pwrctx = intel_alloc_context_page(dev);
+       if (!dev_priv->pwrctx) {
+               ironlake_teardown_rc6(dev);
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+void ironlake_enable_rc6(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int ret;
+
+       /* rc6 disabled by default due to repeated reports of hanging during
+        * boot and resume.
+        */
+       if (!i915_enable_rc6)
+               return;
+
+       ret = ironlake_setup_rc6(dev);
+       if (ret)
+               return;
 
        /*
         * GPU can automatically power down the render unit if given a page
         * to save state.
         */
-       if (IS_IRONLAKE_M(dev)) {
-               if (dev_priv->renderctx == NULL)
-                       dev_priv->renderctx = intel_alloc_context_page(dev);
-               if (dev_priv->renderctx) {
-                       struct drm_i915_gem_object *obj = dev_priv->renderctx;
-                       if (BEGIN_LP_RING(4) == 0) {
-                               OUT_RING(MI_SET_CONTEXT);
-                               OUT_RING(obj->gtt_offset |
-                                        MI_MM_SPACE_GTT |
-                                        MI_SAVE_EXT_STATE_EN |
-                                        MI_RESTORE_EXT_STATE_EN |
-                                        MI_RESTORE_INHIBIT);
-                               OUT_RING(MI_NOOP);
-                               OUT_RING(MI_FLUSH);
-                               ADVANCE_LP_RING();
-                       }
-               } else
-                       DRM_DEBUG_KMS("Failed to allocate render context."
-                                      "Disable RC6\n");
-       }
-
-       if (IS_GEN4(dev) && IS_MOBILE(dev)) {
-               if (dev_priv->pwrctx == NULL)
-                       dev_priv->pwrctx = intel_alloc_context_page(dev);
-               if (dev_priv->pwrctx) {
-                       struct drm_i915_gem_object *obj = dev_priv->pwrctx;
-                       I915_WRITE(PWRCTXA, obj->gtt_offset | PWRCTX_EN);
-                       I915_WRITE(MCHBAR_RENDER_STANDBY,
-                                  I915_READ(MCHBAR_RENDER_STANDBY) & ~RCX_SW_EXIT);
-               }
+       ret = BEGIN_LP_RING(6);
+       if (ret) {
+               ironlake_teardown_rc6(dev);
+               return;
        }
+
+       OUT_RING(MI_SUSPEND_FLUSH | MI_SUSPEND_FLUSH_EN);
+       OUT_RING(MI_SET_CONTEXT);
+       OUT_RING(dev_priv->renderctx->gtt_offset |
+                MI_MM_SPACE_GTT |
+                MI_SAVE_EXT_STATE_EN |
+                MI_RESTORE_EXT_STATE_EN |
+                MI_RESTORE_INHIBIT);
+       OUT_RING(MI_SUSPEND_FLUSH);
+       OUT_RING(MI_NOOP);
+       OUT_RING(MI_FLUSH);
+       ADVANCE_LP_RING();
+
+       I915_WRITE(PWRCTXA, dev_priv->pwrctx->gtt_offset | PWRCTX_EN);
+       I915_WRITE(RSTDBYCTL, I915_READ(RSTDBYCTL) & ~RCX_SW_EXIT);
 }
 
+
 /* Set up chip specific display functions */
 static void intel_init_display(struct drm_device *dev)
 {
@@ -5997,7 +6657,7 @@ static void intel_init_display(struct drm_device *dev)
                dev_priv->display.dpms = i9xx_crtc_dpms;
 
        if (I915_HAS_FBC(dev)) {
-               if (IS_IRONLAKE_M(dev)) {
+               if (HAS_PCH_SPLIT(dev)) {
                        dev_priv->display.fbc_enabled = ironlake_fbc_enabled;
                        dev_priv->display.enable_fbc = ironlake_enable_fbc;
                        dev_priv->display.disable_fbc = ironlake_disable_fbc;
@@ -6046,6 +6706,14 @@ static void intel_init_display(struct drm_device *dev)
                                              "Disable CxSR\n");
                                dev_priv->display.update_wm = NULL;
                        }
+               } else if (IS_GEN6(dev)) {
+                       if (SNB_READ_WM0_LATENCY()) {
+                               dev_priv->display.update_wm = sandybridge_update_wm;
+                       } else {
+                               DRM_DEBUG_KMS("Failed to read display plane latency. "
+                                             "Disable CxSR\n");
+                               dev_priv->display.update_wm = NULL;
+                       }
                } else
                        dev_priv->display.update_wm = NULL;
        } else if (IS_PINEVIEW(dev)) {
@@ -6191,12 +6859,7 @@ void intel_modeset_init(struct drm_device *dev)
                dev->mode_config.max_width = 8192;
                dev->mode_config.max_height = 8192;
        }
-
-       /* set memory base */
-       if (IS_GEN2(dev))
-               dev->mode_config.fb_base = pci_resource_start(dev->pdev, 0);
-       else
-               dev->mode_config.fb_base = pci_resource_start(dev->pdev, 2);
+       dev->mode_config.fb_base = dev->agp->base;
 
        if (IS_MOBILE(dev) || !IS_GEN2(dev))
                dev_priv->num_pipe = 2;
@@ -6211,7 +6874,7 @@ void intel_modeset_init(struct drm_device *dev)
 
        intel_setup_outputs(dev);
 
-       intel_init_clock_gating(dev);
+       intel_enable_clock_gating(dev);
 
        /* Just disable it once at startup */
        i915_disable_vga(dev);
@@ -6221,6 +6884,12 @@ void intel_modeset_init(struct drm_device *dev)
                intel_init_emon(dev);
        }
 
+       if (IS_GEN6(dev))
+               gen6_enable_rps(dev_priv);
+
+       if (IS_IRONLAKE_M(dev))
+               ironlake_enable_rc6(dev);
+
        INIT_WORK(&dev_priv->idle_work, intel_idle_update);
        setup_timer(&dev_priv->idle_timer, intel_gpu_idle_timer,
                    (unsigned long)dev);
@@ -6252,30 +6921,13 @@ void intel_modeset_cleanup(struct drm_device *dev)
        if (dev_priv->display.disable_fbc)
                dev_priv->display.disable_fbc(dev);
 
-       if (dev_priv->renderctx) {
-               struct drm_i915_gem_object *obj = dev_priv->renderctx;
-
-               I915_WRITE(CCID, obj->gtt_offset &~ CCID_EN);
-               POSTING_READ(CCID);
-
-               i915_gem_object_unpin(obj);
-               drm_gem_object_unreference(&obj->base);
-               dev_priv->renderctx = NULL;
-       }
-
-       if (dev_priv->pwrctx) {
-               struct drm_i915_gem_object *obj = dev_priv->pwrctx;
-
-               I915_WRITE(PWRCTXA, obj->gtt_offset &~ PWRCTX_EN);
-               POSTING_READ(PWRCTXA);
-
-               i915_gem_object_unpin(obj);
-               drm_gem_object_unreference(&obj->base);
-               dev_priv->pwrctx = NULL;
-       }
-
        if (IS_IRONLAKE_M(dev))
                ironlake_disable_drps(dev);
+       if (IS_GEN6(dev))
+               gen6_disable_rps(dev);
+
+       if (IS_IRONLAKE_M(dev))
+               ironlake_disable_rc6(dev);
 
        mutex_unlock(&dev->struct_mutex);