]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/gpu/drm/i915/intel_display.c
Merge tag 'v2.6.38' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[mv-sheeva.git] / drivers / gpu / drm / i915 / intel_display.c
index fca523288acad035b9f3d130fe32e9c3c7719989..49fb54fd9a1879d2185b32bd1fb3597d82ee31a9 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;
@@ -1066,13 +1064,13 @@ static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_framebuffer *fb = crtc->fb;
        struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
-       struct drm_i915_gem_object *obj_priv = to_intel_bo(intel_fb->obj);
+       struct drm_i915_gem_object *obj = intel_fb->obj;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        int plane, i;
        u32 fbc_ctl, fbc_ctl2;
 
        if (fb->pitch == dev_priv->cfb_pitch &&
-           obj_priv->fence_reg == dev_priv->cfb_fence &&
+           obj->fence_reg == dev_priv->cfb_fence &&
            intel_crtc->plane == dev_priv->cfb_plane &&
            I915_READ(FBC_CONTROL) & FBC_CTL_EN)
                return;
@@ -1086,7 +1084,7 @@ static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
 
        /* FBC_CTL wants 64B units */
        dev_priv->cfb_pitch = (dev_priv->cfb_pitch / 64) - 1;
-       dev_priv->cfb_fence = obj_priv->fence_reg;
+       dev_priv->cfb_fence = obj->fence_reg;
        dev_priv->cfb_plane = intel_crtc->plane;
        plane = dev_priv->cfb_plane == 0 ? FBC_CTL_PLANEA : FBC_CTL_PLANEB;
 
@@ -1096,7 +1094,7 @@ static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
 
        /* Set it up... */
        fbc_ctl2 = FBC_CTL_FENCE_DBL | FBC_CTL_IDLE_IMM | plane;
-       if (obj_priv->tiling_mode != I915_TILING_NONE)
+       if (obj->tiling_mode != I915_TILING_NONE)
                fbc_ctl2 |= FBC_CTL_CPU_FENCE;
        I915_WRITE(FBC_CONTROL2, fbc_ctl2);
        I915_WRITE(FBC_FENCE_OFF, crtc->y);
@@ -1107,7 +1105,7 @@ static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
                fbc_ctl |= FBC_CTL_C3_IDLE; /* 945 needs special SR handling */
        fbc_ctl |= (dev_priv->cfb_pitch & 0xff) << FBC_CTL_STRIDE_SHIFT;
        fbc_ctl |= (interval & 0x2fff) << FBC_CTL_INTERVAL_SHIFT;
-       if (obj_priv->tiling_mode != I915_TILING_NONE)
+       if (obj->tiling_mode != I915_TILING_NONE)
                fbc_ctl |= dev_priv->cfb_fence;
        I915_WRITE(FBC_CONTROL, fbc_ctl);
 
@@ -1150,7 +1148,7 @@ static void g4x_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_framebuffer *fb = crtc->fb;
        struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
-       struct drm_i915_gem_object *obj_priv = to_intel_bo(intel_fb->obj);
+       struct drm_i915_gem_object *obj = intel_fb->obj;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        int plane = intel_crtc->plane == 0 ? DPFC_CTL_PLANEA : DPFC_CTL_PLANEB;
        unsigned long stall_watermark = 200;
@@ -1159,7 +1157,7 @@ static void g4x_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
        dpfc_ctl = I915_READ(DPFC_CONTROL);
        if (dpfc_ctl & DPFC_CTL_EN) {
                if (dev_priv->cfb_pitch == dev_priv->cfb_pitch / 64 - 1 &&
-                   dev_priv->cfb_fence == obj_priv->fence_reg &&
+                   dev_priv->cfb_fence == obj->fence_reg &&
                    dev_priv->cfb_plane == intel_crtc->plane &&
                    dev_priv->cfb_y == crtc->y)
                        return;
@@ -1170,12 +1168,12 @@ static void g4x_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
        }
 
        dev_priv->cfb_pitch = (dev_priv->cfb_pitch / 64) - 1;
-       dev_priv->cfb_fence = obj_priv->fence_reg;
+       dev_priv->cfb_fence = obj->fence_reg;
        dev_priv->cfb_plane = intel_crtc->plane;
        dev_priv->cfb_y = crtc->y;
 
        dpfc_ctl = plane | DPFC_SR_EN | DPFC_CTL_LIMIT_1X;
-       if (obj_priv->tiling_mode != I915_TILING_NONE) {
+       if (obj->tiling_mode != I915_TILING_NONE) {
                dpfc_ctl |= DPFC_CTL_FENCE_EN | dev_priv->cfb_fence;
                I915_WRITE(DPFC_CHICKEN, DPFC_HT_MODIFY);
        } else {
@@ -1215,13 +1213,33 @@ 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;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_framebuffer *fb = crtc->fb;
        struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
-       struct drm_i915_gem_object *obj_priv = to_intel_bo(intel_fb->obj);
+       struct drm_i915_gem_object *obj = intel_fb->obj;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        int plane = intel_crtc->plane == 0 ? DPFC_CTL_PLANEA : DPFC_CTL_PLANEB;
        unsigned long stall_watermark = 200;
@@ -1230,9 +1248,9 @@ static void ironlake_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
        dpfc_ctl = I915_READ(ILK_DPFC_CONTROL);
        if (dpfc_ctl & DPFC_CTL_EN) {
                if (dev_priv->cfb_pitch == dev_priv->cfb_pitch / 64 - 1 &&
-                   dev_priv->cfb_fence == obj_priv->fence_reg &&
+                   dev_priv->cfb_fence == obj->fence_reg &&
                    dev_priv->cfb_plane == intel_crtc->plane &&
-                   dev_priv->cfb_offset == obj_priv->gtt_offset &&
+                   dev_priv->cfb_offset == obj->gtt_offset &&
                    dev_priv->cfb_y == crtc->y)
                        return;
 
@@ -1242,14 +1260,14 @@ static void ironlake_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
        }
 
        dev_priv->cfb_pitch = (dev_priv->cfb_pitch / 64) - 1;
-       dev_priv->cfb_fence = obj_priv->fence_reg;
+       dev_priv->cfb_fence = obj->fence_reg;
        dev_priv->cfb_plane = intel_crtc->plane;
-       dev_priv->cfb_offset = obj_priv->gtt_offset;
+       dev_priv->cfb_offset = obj->gtt_offset;
        dev_priv->cfb_y = crtc->y;
 
        dpfc_ctl &= DPFC_RESERVED;
        dpfc_ctl |= (plane | DPFC_CTL_LIMIT_1X);
-       if (obj_priv->tiling_mode != I915_TILING_NONE) {
+       if (obj->tiling_mode != I915_TILING_NONE) {
                dpfc_ctl |= (DPFC_CTL_FENCE_EN | dev_priv->cfb_fence);
                I915_WRITE(ILK_DPFC_CHICKEN, DPFC_HT_MODIFY);
        } else {
@@ -1260,10 +1278,17 @@ static void ironlake_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
                   (stall_watermark << DPFC_RECOMP_STALL_WM_SHIFT) |
                   (interval << DPFC_RECOMP_TIMER_COUNT_SHIFT));
        I915_WRITE(ILK_DPFC_FENCE_YOFF, crtc->y);
-       I915_WRITE(ILK_FBC_RT_BASE, obj_priv->gtt_offset | ILK_FBC_RT_VALID);
+       I915_WRITE(ILK_FBC_RT_BASE, obj->gtt_offset | ILK_FBC_RT_VALID);
        /* 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);
 }
 
@@ -1345,7 +1370,7 @@ static void intel_update_fbc(struct drm_device *dev)
        struct intel_crtc *intel_crtc;
        struct drm_framebuffer *fb;
        struct intel_framebuffer *intel_fb;
-       struct drm_i915_gem_object *obj_priv;
+       struct drm_i915_gem_object *obj;
 
        DRM_DEBUG_KMS("\n");
 
@@ -1384,9 +1409,9 @@ static void intel_update_fbc(struct drm_device *dev)
        intel_crtc = to_intel_crtc(crtc);
        fb = crtc->fb;
        intel_fb = to_intel_framebuffer(fb);
-       obj_priv = to_intel_bo(intel_fb->obj);
+       obj = intel_fb->obj;
 
-       if (intel_fb->obj->size > dev_priv->cfb_size) {
+       if (intel_fb->obj->base.size > dev_priv->cfb_size) {
                DRM_DEBUG_KMS("framebuffer too large, disabling "
                              "compression\n");
                dev_priv->no_fbc_reason = FBC_STOLEN_TOO_SMALL;
@@ -1410,7 +1435,7 @@ static void intel_update_fbc(struct drm_device *dev)
                dev_priv->no_fbc_reason = FBC_BAD_PLANE;
                goto out_disable;
        }
-       if (obj_priv->tiling_mode != I915_TILING_X) {
+       if (obj->tiling_mode != I915_TILING_X) {
                DRM_DEBUG_KMS("framebuffer not tiled, disabling compression\n");
                dev_priv->no_fbc_reason = FBC_NOT_TILED;
                goto out_disable;
@@ -1433,14 +1458,13 @@ out_disable:
 
 int
 intel_pin_and_fence_fb_obj(struct drm_device *dev,
-                          struct drm_gem_object *obj,
-                          bool pipelined)
+                          struct drm_i915_gem_object *obj,
+                          struct intel_ring_buffer *pipelined)
 {
-       struct drm_i915_gem_object *obj_priv = to_intel_bo(obj);
        u32 alignment;
        int ret;
 
-       switch (obj_priv->tiling_mode) {
+       switch (obj->tiling_mode) {
        case I915_TILING_NONE:
                if (IS_BROADWATER(dev) || IS_CRESTLINE(dev))
                        alignment = 128 * 1024;
@@ -1461,7 +1485,7 @@ intel_pin_and_fence_fb_obj(struct drm_device *dev,
                BUG();
        }
 
-       ret = i915_gem_object_pin(obj, alignment);
+       ret = i915_gem_object_pin(obj, alignment, true);
        if (ret)
                return ret;
 
@@ -1474,9 +1498,8 @@ intel_pin_and_fence_fb_obj(struct drm_device *dev,
         * framebuffer compression.  For simplicity, we always install
         * a fence as the cost is not that onerous.
         */
-       if (obj_priv->fence_reg == I915_FENCE_REG_NONE &&
-           obj_priv->tiling_mode != I915_TILING_NONE) {
-               ret = i915_gem_object_get_fence_reg(obj, false);
+       if (obj->tiling_mode != I915_TILING_NONE) {
+               ret = i915_gem_object_get_fence(obj, pipelined, false);
                if (ret)
                        goto err_unpin;
        }
@@ -1497,8 +1520,7 @@ intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb,
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        struct intel_framebuffer *intel_fb;
-       struct drm_i915_gem_object *obj_priv;
-       struct drm_gem_object *obj;
+       struct drm_i915_gem_object *obj;
        int plane = intel_crtc->plane;
        unsigned long Start, Offset;
        u32 dspcntr;
@@ -1515,7 +1537,6 @@ intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb,
 
        intel_fb = to_intel_framebuffer(fb);
        obj = intel_fb->obj;
-       obj_priv = to_intel_bo(obj);
 
        reg = DSPCNTR(plane);
        dspcntr = I915_READ(reg);
@@ -1540,7 +1561,7 @@ intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb,
                return -EINVAL;
        }
        if (INTEL_INFO(dev)->gen >= 4) {
-               if (obj_priv->tiling_mode != I915_TILING_NONE)
+               if (obj->tiling_mode != I915_TILING_NONE)
                        dspcntr |= DISPPLANE_TILED;
                else
                        dspcntr &= ~DISPPLANE_TILED;
@@ -1552,7 +1573,7 @@ intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb,
 
        I915_WRITE(reg, dspcntr);
 
-       Start = obj_priv->gtt_offset;
+       Start = obj->gtt_offset;
        Offset = y * fb->pitch + x * (fb->bits_per_pixel / 8);
 
        DRM_DEBUG_KMS("Writing base %08lX %08lX %d %d %d\n",
@@ -1598,7 +1619,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
        mutex_lock(&dev->struct_mutex);
        ret = intel_pin_and_fence_fb_obj(dev,
                                         to_intel_framebuffer(crtc->fb)->obj,
-                                        false);
+                                        NULL);
        if (ret != 0) {
                mutex_unlock(&dev->struct_mutex);
                return ret;
@@ -1606,23 +1627,22 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
 
        if (old_fb) {
                struct drm_i915_private *dev_priv = dev->dev_private;
-               struct drm_gem_object *obj = to_intel_framebuffer(old_fb)->obj;
-               struct drm_i915_gem_object *obj_priv = to_intel_bo(obj);
+               struct drm_i915_gem_object *obj = to_intel_framebuffer(old_fb)->obj;
 
                wait_event(dev_priv->pending_flip_queue,
-                          atomic_read(&obj_priv->pending_flip) == 0);
+                          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_priv, false);
-               if (ret) {
-                       i915_gem_object_unpin(to_intel_framebuffer(crtc->fb)->obj);
-                       mutex_unlock(&dev->struct_mutex);
-                       return ret;
-               }
+               ret = i915_gem_object_flush_gpu(obj, false);
+               (void) ret;
        }
 
        ret = intel_pipe_set_base_atomic(crtc, crtc->fb, x, y,
@@ -1633,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);
 
@@ -1996,31 +2018,56 @@ static void intel_flush_display_plane(struct drm_device *dev,
 static void intel_clear_scanline_wait(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_ring_buffer *ring;
        u32 tmp;
 
        if (IS_GEN2(dev))
                /* Can't break the hang on i8xx */
                return;
 
-       tmp = I915_READ(PRB0_CTL);
-       if (tmp & RING_WAIT) {
-               I915_WRITE(PRB0_CTL, tmp);
-               POSTING_READ(PRB0_CTL);
-       }
+       ring = LP_RING(dev_priv);
+       tmp = I915_READ_CTL(ring);
+       if (tmp & RING_WAIT)
+               I915_WRITE_CTL(ring, tmp);
 }
 
 static void intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc)
 {
-       struct drm_i915_gem_object *obj_priv;
+       struct drm_i915_gem_object *obj;
        struct drm_i915_private *dev_priv;
 
        if (crtc->fb == NULL)
                return;
 
-       obj_priv = to_intel_bo(to_intel_framebuffer(crtc->fb)->obj);
+       obj = to_intel_framebuffer(crtc->fb)->obj;
        dev_priv = crtc->dev->dev_private;
        wait_event(dev_priv->pending_flip_queue,
-                  atomic_read(&obj_priv->pending_flip) == 0);
+                  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)
@@ -2031,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;
@@ -2044,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 &&
@@ -2078,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);
@@ -2162,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);
@@ -2850,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
@@ -3383,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)
@@ -3400,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 -"
@@ -3439,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 -"
@@ -3452,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 && /* XXX disabled due to buggy implmentation? */ 0) {
-               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);
 }
 
 /**
@@ -3580,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,
@@ -3642,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);
@@ -3660,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");
@@ -3714,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);
@@ -3817,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);
 
@@ -3828,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,6 +4204,22 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                                reduced_clock.m2;
        }
 
+       /* Enable autotuning of the PLL clock (if permissible) */
+       if (HAS_PCH_SPLIT(dev)) {
+               int factor = 21;
+
+               if (is_lvds) {
+                       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;
+               } else if (is_sdvo && is_tv)
+                       factor = 20;
+
+               if (clock.m1 < factor * clock.n)
+                       fp |= FP_CB_TUNE;
+       }
+
        dpll = 0;
        if (!HAS_PCH_SPLIT(dev))
                dpll = DPLL_VGA_MODE_DIS;
@@ -3925,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;
@@ -4071,7 +4434,6 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
        }
 
        if (!has_edp_encoder || intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
-               I915_WRITE(fp_reg, fp);
                I915_WRITE(dpll_reg, dpll);
 
                /* Wait for the clocks to stabilize. */
@@ -4089,13 +4451,13 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                        }
                        I915_WRITE(DPLL_MD(pipe), temp);
                } else {
-                       /* write it again -- the BIOS does, after all */
+                       /* The pixel multiplier can only be updated once the
+                        * DPLL is enabled and the clocks are stable.
+                        *
+                        * So write it again.
+                        */
                        I915_WRITE(dpll_reg, dpll);
                }
-
-               /* Wait for the clocks to stabilize. */
-               POSTING_READ(dpll_reg);
-               udelay(150);
        }
 
        intel_crtc->lowfreq_avail = false;
@@ -4331,15 +4693,14 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc,
 }
 
 static int intel_crtc_cursor_set(struct drm_crtc *crtc,
-                                struct drm_file *file_priv,
+                                struct drm_file *file,
                                 uint32_t handle,
                                 uint32_t width, uint32_t height)
 {
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-       struct drm_gem_object *bo;
-       struct drm_i915_gem_object *obj_priv;
+       struct drm_i915_gem_object *obj;
        uint32_t addr;
        int ret;
 
@@ -4349,7 +4710,7 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
        if (!handle) {
                DRM_DEBUG_KMS("cursor off\n");
                addr = 0;
-               bo = NULL;
+               obj = NULL;
                mutex_lock(&dev->struct_mutex);
                goto finish;
        }
@@ -4360,13 +4721,11 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
                return -EINVAL;
        }
 
-       bo = drm_gem_object_lookup(dev, file_priv, handle);
-       if (!bo)
+       obj = to_intel_bo(drm_gem_object_lookup(dev, file, handle));
+       if (!obj)
                return -ENOENT;
 
-       obj_priv = to_intel_bo(bo);
-
-       if (bo->size < width * height * 4) {
+       if (obj->base.size < width * height * 4) {
                DRM_ERROR("buffer is to small\n");
                ret = -ENOMEM;
                goto fail;
@@ -4375,29 +4734,41 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
        /* we only need to pin inside GTT if cursor is non-phy */
        mutex_lock(&dev->struct_mutex);
        if (!dev_priv->info->cursor_needs_physical) {
-               ret = i915_gem_object_pin(bo, PAGE_SIZE);
+               if (obj->tiling_mode) {
+                       DRM_ERROR("cursor cannot be tiled\n");
+                       ret = -EINVAL;
+                       goto fail_locked;
+               }
+
+               ret = i915_gem_object_pin(obj, PAGE_SIZE, true);
                if (ret) {
                        DRM_ERROR("failed to pin cursor bo\n");
                        goto fail_locked;
                }
 
-               ret = i915_gem_object_set_to_gtt_domain(bo, 0);
+               ret = i915_gem_object_set_to_gtt_domain(obj, 0);
                if (ret) {
                        DRM_ERROR("failed to move cursor bo into the GTT\n");
                        goto fail_unpin;
                }
 
-               addr = obj_priv->gtt_offset;
+               ret = i915_gem_object_put_fence(obj);
+               if (ret) {
+                       DRM_ERROR("failed to move cursor bo into the GTT\n");
+                       goto fail_unpin;
+               }
+
+               addr = obj->gtt_offset;
        } else {
                int align = IS_I830(dev) ? 16 * 1024 : 256;
-               ret = i915_gem_attach_phys_object(dev, bo,
+               ret = i915_gem_attach_phys_object(dev, obj,
                                                  (intel_crtc->pipe == 0) ? I915_GEM_PHYS_CURSOR_0 : I915_GEM_PHYS_CURSOR_1,
                                                  align);
                if (ret) {
                        DRM_ERROR("failed to attach phys object\n");
                        goto fail_locked;
                }
-               addr = obj_priv->phys_obj->handle->busaddr;
+               addr = obj->phys_obj->handle->busaddr;
        }
 
        if (IS_GEN2(dev))
@@ -4406,17 +4777,17 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
  finish:
        if (intel_crtc->cursor_bo) {
                if (dev_priv->info->cursor_needs_physical) {
-                       if (intel_crtc->cursor_bo != bo)
+                       if (intel_crtc->cursor_bo != obj)
                                i915_gem_detach_phys_object(dev, intel_crtc->cursor_bo);
                } else
                        i915_gem_object_unpin(intel_crtc->cursor_bo);
-               drm_gem_object_unreference(intel_crtc->cursor_bo);
+               drm_gem_object_unreference(&intel_crtc->cursor_bo->base);
        }
 
        mutex_unlock(&dev->struct_mutex);
 
        intel_crtc->cursor_addr = addr;
-       intel_crtc->cursor_bo = bo;
+       intel_crtc->cursor_bo = obj;
        intel_crtc->cursor_width = width;
        intel_crtc->cursor_height = height;
 
@@ -4424,11 +4795,11 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
 
        return 0;
 fail_unpin:
-       i915_gem_object_unpin(bo);
+       i915_gem_object_unpin(obj);
 fail_locked:
        mutex_unlock(&dev->struct_mutex);
 fail:
-       drm_gem_object_unreference_unlocked(bo);
+       drm_gem_object_unreference_unlocked(&obj->base);
        return ret;
 }
 
@@ -4739,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);
 }
 
@@ -4751,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);
 }
 
@@ -4763,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;
@@ -4772,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");
@@ -4888,7 +5275,7 @@ static void intel_idle_update(struct work_struct *work)
  * buffer), we'll also mark the display as busy, so we know to increase its
  * clock frequency.
  */
-void intel_mark_busy(struct drm_device *dev, struct drm_gem_object *obj)
+void intel_mark_busy(struct drm_device *dev, struct drm_i915_gem_object *obj)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
        struct drm_crtc *crtc = NULL;
@@ -4969,8 +5356,9 @@ static void intel_unpin_work_fn(struct work_struct *__work)
 
        mutex_lock(&work->dev->struct_mutex);
        i915_gem_object_unpin(work->old_fb_obj);
-       drm_gem_object_unreference(work->pending_flip_obj);
-       drm_gem_object_unreference(work->old_fb_obj);
+       drm_gem_object_unreference(&work->pending_flip_obj->base);
+       drm_gem_object_unreference(&work->old_fb_obj->base);
+
        mutex_unlock(&work->dev->struct_mutex);
        kfree(work);
 }
@@ -4981,15 +5369,17 @@ static void do_intel_finish_page_flip(struct drm_device *dev,
        drm_i915_private_t *dev_priv = dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        struct intel_unpin_work *work;
-       struct drm_i915_gem_object *obj_priv;
+       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) {
@@ -4998,26 +5388,49 @@ 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_priv = to_intel_bo(work->old_fb_obj);
+       obj = work->old_fb_obj;
+
        atomic_clear_mask(1 << intel_crtc->plane,
-                         &obj_priv->pending_flip.counter);
-       if (atomic_read(&obj_priv->pending_flip) == 0)
+                         &obj->pending_flip.counter);
+       if (atomic_read(&obj->pending_flip) == 0)
                wake_up(&dev_priv->pending_flip_queue);
+
        schedule_work(&work->work);
 
        trace_i915_flip_complete(intel_crtc->plane, work->pending_flip_obj);
@@ -5063,8 +5476,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_framebuffer *intel_fb;
-       struct drm_i915_gem_object *obj_priv;
-       struct drm_gem_object *obj;
+       struct drm_i915_gem_object *obj;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        struct intel_unpin_work *work;
        unsigned long flags, offset;
@@ -5098,13 +5510,13 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
        obj = intel_fb->obj;
 
        mutex_lock(&dev->struct_mutex);
-       ret = intel_pin_and_fence_fb_obj(dev, obj, true);
+       ret = intel_pin_and_fence_fb_obj(dev, obj, LP_RING(dev_priv));
        if (ret)
                goto cleanup_work;
 
        /* Reference the objects for the scheduled work. */
-       drm_gem_object_reference(work->old_fb_obj);
-       drm_gem_object_reference(obj);
+       drm_gem_object_reference(&work->old_fb_obj->base);
+       drm_gem_object_reference(&obj->base);
 
        crtc->fb = fb;
 
@@ -5112,22 +5524,16 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
        if (ret)
                goto cleanup_objs;
 
-       /* Block clients from rendering to the new back buffer until
-        * the flip occurs and the object is no longer visible.
-        */
-       atomic_add(1 << intel_crtc->plane,
-                  &to_intel_bo(work->old_fb_obj)->pending_flip);
-
-       work->pending_flip_obj = obj;
-       obj_priv = to_intel_bo(obj);
-
        if (IS_GEN3(dev) || IS_GEN2(dev)) {
                u32 flip_mask;
 
                /* Can't queue multiple flips, so wait for the previous
                 * one to finish before executing the next.
                 */
-               BEGIN_LP_RING(2);
+               ret = BEGIN_LP_RING(2);
+               if (ret)
+                       goto cleanup_objs;
+
                if (intel_crtc->plane)
                        flip_mask = MI_WAIT_FOR_PLANE_B_FLIP;
                else
@@ -5137,18 +5543,28 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
                ADVANCE_LP_RING();
        }
 
+       work->pending_flip_obj = obj;
+
        work->enable_stall_check = true;
 
        /* Offset into the new buffer for cases of shared fbs between CRTCs */
        offset = crtc->y * fb->pitch + crtc->x * fb->bits_per_pixel/8;
 
-       BEGIN_LP_RING(4);
-       switch(INTEL_INFO(dev)->gen) {
+       ret = BEGIN_LP_RING(4);
+       if (ret)
+               goto cleanup_objs;
+
+       /* Block clients from rendering to the new back buffer until
+        * the flip occurs and the object is no longer visible.
+        */
+       atomic_add(1 << intel_crtc->plane, &work->old_fb_obj->pending_flip);
+
+       switch (INTEL_INFO(dev)->gen) {
        case 2:
                OUT_RING(MI_DISPLAY_FLIP |
                         MI_DISPLAY_FLIP_PLANE(intel_crtc->plane));
                OUT_RING(fb->pitch);
-               OUT_RING(obj_priv->gtt_offset + offset);
+               OUT_RING(obj->gtt_offset + offset);
                OUT_RING(MI_NOOP);
                break;
 
@@ -5156,7 +5572,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
                OUT_RING(MI_DISPLAY_FLIP_I915 |
                         MI_DISPLAY_FLIP_PLANE(intel_crtc->plane));
                OUT_RING(fb->pitch);
-               OUT_RING(obj_priv->gtt_offset + offset);
+               OUT_RING(obj->gtt_offset + offset);
                OUT_RING(MI_NOOP);
                break;
 
@@ -5169,7 +5585,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
                OUT_RING(MI_DISPLAY_FLIP |
                         MI_DISPLAY_FLIP_PLANE(intel_crtc->plane));
                OUT_RING(fb->pitch);
-               OUT_RING(obj_priv->gtt_offset | obj_priv->tiling_mode);
+               OUT_RING(obj->gtt_offset | obj->tiling_mode);
 
                /* XXX Enabling the panel-fitter across page-flip is so far
                 * untested on non-native modes, so ignore it for now.
@@ -5183,8 +5599,8 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
        case 6:
                OUT_RING(MI_DISPLAY_FLIP |
                         MI_DISPLAY_FLIP_PLANE(intel_crtc->plane));
-               OUT_RING(fb->pitch | obj_priv->tiling_mode);
-               OUT_RING(obj_priv->gtt_offset);
+               OUT_RING(fb->pitch | obj->tiling_mode);
+               OUT_RING(obj->gtt_offset);
 
                pf = I915_READ(pipe == 0 ? PFA_CTL_1 : PFB_CTL_1) & PF_ENABLE;
                pipesrc = I915_READ(pipe == 0 ? PIPEASRC : PIPEBSRC) & 0x0fff0fff;
@@ -5200,8 +5616,8 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
        return 0;
 
 cleanup_objs:
-       drm_gem_object_unreference(work->old_fb_obj);
-       drm_gem_object_unreference(obj);
+       drm_gem_object_unreference(&work->old_fb_obj->base);
+       drm_gem_object_unreference(&obj->base);
 cleanup_work:
        mutex_unlock(&dev->struct_mutex);
 
@@ -5214,6 +5630,16 @@ cleanup_work:
        return ret;
 }
 
+static void intel_crtc_reset(struct drm_crtc *crtc)
+{
+       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;
+}
+
 static struct drm_crtc_helper_funcs intel_helper_funcs = {
        .dpms = intel_crtc_dpms,
        .mode_fixup = intel_crtc_mode_fixup,
@@ -5225,6 +5651,7 @@ static struct drm_crtc_helper_funcs intel_helper_funcs = {
 };
 
 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,
@@ -5315,8 +5742,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)) {
@@ -5338,7 +5764,7 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
 }
 
 int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data,
-                               struct drm_file *file_priv)
+                               struct drm_file *file)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
        struct drm_i915_get_pipe_from_crtc_id *pipe_from_crtc_id = data;
@@ -5498,6 +5924,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)
@@ -5505,19 +5933,19 @@ static void intel_user_framebuffer_destroy(struct drm_framebuffer *fb)
        struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
 
        drm_framebuffer_cleanup(fb);
-       drm_gem_object_unreference_unlocked(intel_fb->obj);
+       drm_gem_object_unreference_unlocked(&intel_fb->obj->base);
 
        kfree(intel_fb);
 }
 
 static int intel_user_framebuffer_create_handle(struct drm_framebuffer *fb,
-                                               struct drm_file *file_priv,
+                                               struct drm_file *file,
                                                unsigned int *handle)
 {
        struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
-       struct drm_gem_object *object = intel_fb->obj;
+       struct drm_i915_gem_object *obj = intel_fb->obj;
 
-       return drm_gem_handle_create(file_priv, object, handle);
+       return drm_gem_handle_create(file, &obj->base, handle);
 }
 
 static const struct drm_framebuffer_funcs intel_fb_funcs = {
@@ -5528,12 +5956,11 @@ static const struct drm_framebuffer_funcs intel_fb_funcs = {
 int intel_framebuffer_init(struct drm_device *dev,
                           struct intel_framebuffer *intel_fb,
                           struct drm_mode_fb_cmd *mode_cmd,
-                          struct drm_gem_object *obj)
+                          struct drm_i915_gem_object *obj)
 {
-       struct drm_i915_gem_object *obj_priv = to_intel_bo(obj);
        int ret;
 
-       if (obj_priv->tiling_mode == I915_TILING_Y)
+       if (obj->tiling_mode == I915_TILING_Y)
                return -EINVAL;
 
        if (mode_cmd->pitch & 63)
@@ -5565,11 +5992,11 @@ intel_user_framebuffer_create(struct drm_device *dev,
                              struct drm_file *filp,
                              struct drm_mode_fb_cmd *mode_cmd)
 {
-       struct drm_gem_object *obj;
+       struct drm_i915_gem_object *obj;
        struct intel_framebuffer *intel_fb;
        int ret;
 
-       obj = drm_gem_object_lookup(dev, filp, mode_cmd->handle);
+       obj = to_intel_bo(drm_gem_object_lookup(dev, filp, mode_cmd->handle));
        if (!obj)
                return ERR_PTR(-ENOENT);
 
@@ -5577,10 +6004,9 @@ intel_user_framebuffer_create(struct drm_device *dev,
        if (!intel_fb)
                return ERR_PTR(-ENOMEM);
 
-       ret = intel_framebuffer_init(dev, intel_fb,
-                                    mode_cmd, obj);
+       ret = intel_framebuffer_init(dev, intel_fb, mode_cmd, obj);
        if (ret) {
-               drm_gem_object_unreference_unlocked(obj);
+               drm_gem_object_unreference_unlocked(&obj->base);
                kfree(intel_fb);
                return ERR_PTR(ret);
        }
@@ -5593,10 +6019,10 @@ static const struct drm_mode_config_funcs intel_mode_funcs = {
        .output_poll_changed = intel_fb_output_poll_changed,
 };
 
-static struct drm_gem_object *
+static struct drm_i915_gem_object *
 intel_alloc_context_page(struct drm_device *dev)
 {
-       struct drm_gem_object *ctx;
+       struct drm_i915_gem_object *ctx;
        int ret;
 
        ctx = i915_gem_alloc_object(dev, 4096);
@@ -5606,7 +6032,7 @@ intel_alloc_context_page(struct drm_device *dev)
        }
 
        mutex_lock(&dev->struct_mutex);
-       ret = i915_gem_object_pin(ctx, 4096);
+       ret = i915_gem_object_pin(ctx, 4096, true);
        if (ret) {
                DRM_ERROR("failed to pin power context: %d\n", ret);
                goto err_unref;
@@ -5624,7 +6050,7 @@ intel_alloc_context_page(struct drm_device *dev)
 err_unpin:
        i915_gem_object_unpin(ctx);
 err_unref:
-       drm_gem_object_unreference(ctx);
+       drm_gem_object_unreference(&ctx->base);
        mutex_unlock(&dev->struct_mutex);
        return NULL;
 }
@@ -5736,6 +6162,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;
@@ -5822,7 +6267,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;
 
@@ -5835,7 +6396,9 @@ 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;
 
@@ -5872,9 +6435,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
@@ -5896,7 +6459,49 @@ void intel_init_clock_gating(struct drm_device *dev)
                                   ILK_DPFC_DIS2 |
                                   ILK_CLK_FBC);
                }
-               return;
+
+               I915_WRITE(ILK_DISPLAY_CHICKEN2,
+                          I915_READ(ILK_DISPLAY_CHICKEN2) |
+                          ILK_ELPIN_409_SELECT);
+
+               if (IS_GEN5(dev)) {
+                       I915_WRITE(_3D_CHICKEN2,
+                                  _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);
@@ -5934,57 +6539,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_priv;
-                       obj_priv = to_intel_bo(dev_priv->renderctx);
-                       if (obj_priv) {
-                               BEGIN_LP_RING(4);
-                               OUT_RING(MI_SET_CONTEXT);
-                               OUT_RING(obj_priv->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");
+       ret = BEGIN_LP_RING(6);
+       if (ret) {
+               ironlake_teardown_rc6(dev);
+               return;
        }
 
-       if (I915_HAS_RC6(dev) && drm_core_check_feature(dev, DRIVER_MODESET)) {
-               struct drm_i915_gem_object *obj_priv = NULL;
-
-               if (dev_priv->pwrctx) {
-                       obj_priv = to_intel_bo(dev_priv->pwrctx);
-               } else {
-                       struct drm_gem_object *pwrctx;
-
-                       pwrctx = intel_alloc_context_page(dev);
-                       if (pwrctx) {
-                               dev_priv->pwrctx = pwrctx;
-                               obj_priv = to_intel_bo(pwrctx);
-                       }
-               }
+       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();
 
-               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(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 +6651,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 +6700,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 +6853,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 +6868,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 +6878,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,28 +6915,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_priv;
-
-               obj_priv = to_intel_bo(dev_priv->renderctx);
-               I915_WRITE(CCID, obj_priv->gtt_offset &~ CCID_EN);
-               I915_READ(CCID);
-               i915_gem_object_unpin(dev_priv->renderctx);
-               drm_gem_object_unreference(dev_priv->renderctx);
-       }
-
-       if (dev_priv->pwrctx) {
-               struct drm_i915_gem_object *obj_priv;
-
-               obj_priv = to_intel_bo(dev_priv->pwrctx);
-               I915_WRITE(PWRCTXA, obj_priv->gtt_offset &~ PWRCTX_EN);
-               I915_READ(PWRCTXA);
-               i915_gem_object_unpin(dev_priv->pwrctx);
-               drm_gem_object_unreference(dev_priv->pwrctx);
-       }
-
        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);
 
@@ -6325,3 +6973,113 @@ int intel_modeset_vga_set_state(struct drm_device *dev, bool state)
        pci_write_config_word(dev_priv->bridge_dev, INTEL_GMCH_CTRL, gmch_ctrl);
        return 0;
 }
+
+#ifdef CONFIG_DEBUG_FS
+#include <linux/seq_file.h>
+
+struct intel_display_error_state {
+       struct intel_cursor_error_state {
+               u32 control;
+               u32 position;
+               u32 base;
+               u32 size;
+       } cursor[2];
+
+       struct intel_pipe_error_state {
+               u32 conf;
+               u32 source;
+
+               u32 htotal;
+               u32 hblank;
+               u32 hsync;
+               u32 vtotal;
+               u32 vblank;
+               u32 vsync;
+       } pipe[2];
+
+       struct intel_plane_error_state {
+               u32 control;
+               u32 stride;
+               u32 size;
+               u32 pos;
+               u32 addr;
+               u32 surface;
+               u32 tile_offset;
+       } plane[2];
+};
+
+struct intel_display_error_state *
+intel_display_capture_error_state(struct drm_device *dev)
+{
+        drm_i915_private_t *dev_priv = dev->dev_private;
+       struct intel_display_error_state *error;
+       int i;
+
+       error = kmalloc(sizeof(*error), GFP_ATOMIC);
+       if (error == NULL)
+               return NULL;
+
+       for (i = 0; i < 2; i++) {
+               error->cursor[i].control = I915_READ(CURCNTR(i));
+               error->cursor[i].position = I915_READ(CURPOS(i));
+               error->cursor[i].base = I915_READ(CURBASE(i));
+
+               error->plane[i].control = I915_READ(DSPCNTR(i));
+               error->plane[i].stride = I915_READ(DSPSTRIDE(i));
+               error->plane[i].size = I915_READ(DSPSIZE(i));
+               error->plane[i].pos= I915_READ(DSPPOS(i));
+               error->plane[i].addr = I915_READ(DSPADDR(i));
+               if (INTEL_INFO(dev)->gen >= 4) {
+                       error->plane[i].surface = I915_READ(DSPSURF(i));
+                       error->plane[i].tile_offset = I915_READ(DSPTILEOFF(i));
+               }
+
+               error->pipe[i].conf = I915_READ(PIPECONF(i));
+               error->pipe[i].source = I915_READ(PIPESRC(i));
+               error->pipe[i].htotal = I915_READ(HTOTAL(i));
+               error->pipe[i].hblank = I915_READ(HBLANK(i));
+               error->pipe[i].hsync = I915_READ(HSYNC(i));
+               error->pipe[i].vtotal = I915_READ(VTOTAL(i));
+               error->pipe[i].vblank = I915_READ(VBLANK(i));
+               error->pipe[i].vsync = I915_READ(VSYNC(i));
+       }
+
+       return error;
+}
+
+void
+intel_display_print_error_state(struct seq_file *m,
+                               struct drm_device *dev,
+                               struct intel_display_error_state *error)
+{
+       int i;
+
+       for (i = 0; i < 2; i++) {
+               seq_printf(m, "Pipe [%d]:\n", i);
+               seq_printf(m, "  CONF: %08x\n", error->pipe[i].conf);
+               seq_printf(m, "  SRC: %08x\n", error->pipe[i].source);
+               seq_printf(m, "  HTOTAL: %08x\n", error->pipe[i].htotal);
+               seq_printf(m, "  HBLANK: %08x\n", error->pipe[i].hblank);
+               seq_printf(m, "  HSYNC: %08x\n", error->pipe[i].hsync);
+               seq_printf(m, "  VTOTAL: %08x\n", error->pipe[i].vtotal);
+               seq_printf(m, "  VBLANK: %08x\n", error->pipe[i].vblank);
+               seq_printf(m, "  VSYNC: %08x\n", error->pipe[i].vsync);
+
+               seq_printf(m, "Plane [%d]:\n", i);
+               seq_printf(m, "  CNTR: %08x\n", error->plane[i].control);
+               seq_printf(m, "  STRIDE: %08x\n", error->plane[i].stride);
+               seq_printf(m, "  SIZE: %08x\n", error->plane[i].size);
+               seq_printf(m, "  POS: %08x\n", error->plane[i].pos);
+               seq_printf(m, "  ADDR: %08x\n", error->plane[i].addr);
+               if (INTEL_INFO(dev)->gen >= 4) {
+                       seq_printf(m, "  SURF: %08x\n", error->plane[i].surface);
+                       seq_printf(m, "  TILEOFF: %08x\n", error->plane[i].tile_offset);
+               }
+
+               seq_printf(m, "Cursor [%d]:\n", i);
+               seq_printf(m, "  CNTR: %08x\n", error->cursor[i].control);
+               seq_printf(m, "  POS: %08x\n", error->cursor[i].position);
+               seq_printf(m, "  BASE: %08x\n", error->cursor[i].base);
+       }
+}
+#endif