drm/i915: correct FBC update when pipe base update occurs
[safe/jmp/linux-2.6] / drivers / gpu / drm / i915 / intel_display.c
index 890f710..cb0f4f9 100644 (file)
@@ -24,6 +24,8 @@
  *     Eric Anholt <eric@anholt.net>
  */
 
+#include <linux/module.h>
+#include <linux/input.h>
 #include <linux/i2c.h>
 #include <linux/kernel.h>
 #include "drmP.h"
 
 #include "drm_crtc_helper.h"
 
+#define HAS_eDP (intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP))
+
 bool intel_pipe_has_type (struct drm_crtc *crtc, int type);
 static void intel_update_watermarks(struct drm_device *dev);
+static void intel_increase_pllclock(struct drm_crtc *crtc, bool schedule);
 
 typedef struct {
     /* given values */
@@ -65,6 +70,8 @@ struct intel_limit {
     intel_p2_t     p2;
     bool (* find_pll)(const intel_limit_t *, struct drm_crtc *,
                      int, int, intel_clock_t *);
+    bool (* find_reduced_pll)(const intel_limit_t *, struct drm_crtc *,
+                             int, int, intel_clock_t *);
 };
 
 #define I8XX_DOT_MIN             25000
@@ -88,7 +95,7 @@ struct intel_limit {
 #define I8XX_P2_SLOW                 4
 #define I8XX_P2_FAST                 2
 #define I8XX_P2_LVDS_SLOW            14
-#define I8XX_P2_LVDS_FAST            14 /* No fast option */
+#define I8XX_P2_LVDS_FAST            7
 #define I8XX_P2_SLOW_LIMIT      165000
 
 #define I9XX_DOT_MIN             20000
@@ -259,6 +266,9 @@ static bool
 intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
                    int target, int refclk, intel_clock_t *best_clock);
 static bool
+intel_find_best_reduced_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
+                           int target, int refclk, intel_clock_t *best_clock);
+static bool
 intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
                        int target, int refclk, intel_clock_t *best_clock);
 static bool
@@ -268,6 +278,9 @@ intel_igdng_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
 static bool
 intel_find_pll_g4x_dp(const intel_limit_t *, struct drm_crtc *crtc,
                      int target, int refclk, intel_clock_t *best_clock);
+static bool
+intel_find_pll_igdng_dp(const intel_limit_t *, struct drm_crtc *crtc,
+                     int target, int refclk, intel_clock_t *best_clock);
 
 static const intel_limit_t intel_limits_i8xx_dvo = {
         .dot = { .min = I8XX_DOT_MIN,          .max = I8XX_DOT_MAX },
@@ -281,6 +294,7 @@ static const intel_limit_t intel_limits_i8xx_dvo = {
        .p2  = { .dot_limit = I8XX_P2_SLOW_LIMIT,
                 .p2_slow = I8XX_P2_SLOW,       .p2_fast = I8XX_P2_FAST },
        .find_pll = intel_find_best_PLL,
+       .find_reduced_pll = intel_find_best_reduced_PLL,
 };
 
 static const intel_limit_t intel_limits_i8xx_lvds = {
@@ -295,6 +309,7 @@ static const intel_limit_t intel_limits_i8xx_lvds = {
        .p2  = { .dot_limit = I8XX_P2_SLOW_LIMIT,
                 .p2_slow = I8XX_P2_LVDS_SLOW,  .p2_fast = I8XX_P2_LVDS_FAST },
        .find_pll = intel_find_best_PLL,
+       .find_reduced_pll = intel_find_best_reduced_PLL,
 };
        
 static const intel_limit_t intel_limits_i9xx_sdvo = {
@@ -309,6 +324,7 @@ static const intel_limit_t intel_limits_i9xx_sdvo = {
        .p2  = { .dot_limit = I9XX_P2_SDVO_DAC_SLOW_LIMIT,
                 .p2_slow = I9XX_P2_SDVO_DAC_SLOW,      .p2_fast = I9XX_P2_SDVO_DAC_FAST },
        .find_pll = intel_find_best_PLL,
+       .find_reduced_pll = intel_find_best_reduced_PLL,
 };
 
 static const intel_limit_t intel_limits_i9xx_lvds = {
@@ -326,6 +342,7 @@ static const intel_limit_t intel_limits_i9xx_lvds = {
        .p2  = { .dot_limit = I9XX_P2_LVDS_SLOW_LIMIT,
                 .p2_slow = I9XX_P2_LVDS_SLOW,  .p2_fast = I9XX_P2_LVDS_FAST },
        .find_pll = intel_find_best_PLL,
+       .find_reduced_pll = intel_find_best_reduced_PLL,
 };
 
     /* below parameter and function is for G4X Chipset Family*/
@@ -343,6 +360,7 @@ static const intel_limit_t intel_limits_g4x_sdvo = {
                 .p2_fast = G4X_P2_SDVO_FAST
        },
        .find_pll = intel_g4x_find_best_PLL,
+       .find_reduced_pll = intel_g4x_find_best_PLL,
 };
 
 static const intel_limit_t intel_limits_g4x_hdmi = {
@@ -359,6 +377,7 @@ static const intel_limit_t intel_limits_g4x_hdmi = {
                 .p2_fast = G4X_P2_HDMI_DAC_FAST
        },
        .find_pll = intel_g4x_find_best_PLL,
+       .find_reduced_pll = intel_g4x_find_best_PLL,
 };
 
 static const intel_limit_t intel_limits_g4x_single_channel_lvds = {
@@ -383,6 +402,7 @@ static const intel_limit_t intel_limits_g4x_single_channel_lvds = {
                 .p2_fast = G4X_P2_SINGLE_CHANNEL_LVDS_FAST
        },
        .find_pll = intel_g4x_find_best_PLL,
+       .find_reduced_pll = intel_g4x_find_best_PLL,
 };
 
 static const intel_limit_t intel_limits_g4x_dual_channel_lvds = {
@@ -407,6 +427,7 @@ static const intel_limit_t intel_limits_g4x_dual_channel_lvds = {
                 .p2_fast = G4X_P2_DUAL_CHANNEL_LVDS_FAST
        },
        .find_pll = intel_g4x_find_best_PLL,
+       .find_reduced_pll = intel_g4x_find_best_PLL,
 };
 
 static const intel_limit_t intel_limits_g4x_display_port = {
@@ -444,6 +465,7 @@ static const intel_limit_t intel_limits_igd_sdvo = {
        .p2  = { .dot_limit = I9XX_P2_SDVO_DAC_SLOW_LIMIT,
                 .p2_slow = I9XX_P2_SDVO_DAC_SLOW,      .p2_fast = I9XX_P2_SDVO_DAC_FAST },
        .find_pll = intel_find_best_PLL,
+       .find_reduced_pll = intel_find_best_reduced_PLL,
 };
 
 static const intel_limit_t intel_limits_igd_lvds = {
@@ -459,6 +481,7 @@ static const intel_limit_t intel_limits_igd_lvds = {
        .p2  = { .dot_limit = I9XX_P2_LVDS_SLOW_LIMIT,
                 .p2_slow = I9XX_P2_LVDS_SLOW,  .p2_fast = I9XX_P2_LVDS_SLOW },
        .find_pll = intel_find_best_PLL,
+       .find_reduced_pll = intel_find_best_reduced_PLL,
 };
 
 static const intel_limit_t intel_limits_igdng_sdvo = {
@@ -598,6 +621,23 @@ bool intel_pipe_has_type (struct drm_crtc *crtc, int type)
     return false;
 }
 
+struct drm_connector *
+intel_pipe_get_output (struct drm_crtc *crtc)
+{
+    struct drm_device *dev = crtc->dev;
+    struct drm_mode_config *mode_config = &dev->mode_config;
+    struct drm_connector *l_entry, *ret = NULL;
+
+    list_for_each_entry(l_entry, &mode_config->connector_list, head) {
+           if (l_entry->encoder &&
+               l_entry->encoder->crtc == crtc) {
+                   ret = l_entry;
+                   break;
+           }
+    }
+    return ret;
+}
+
 #define INTELPllInvalid(s)   do { /* DRM_DEBUG(s); */ return false; } while (0)
 /**
  * Returns whether the given set of divisors are valid for a given refclk with
@@ -644,7 +684,7 @@ intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
        intel_clock_t clock;
        int err = target;
 
-       if (IS_I9XX(dev) && intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) &&
+       if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) &&
            (I915_READ(LVDS)) != 0) {
                /*
                 * For LVDS, if the panel is on, just rely on its current
@@ -666,15 +706,16 @@ intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
 
        memset (best_clock, 0, sizeof (*best_clock));
 
-       for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; clock.m1++) {
-               for (clock.m2 = limit->m2.min; clock.m2 <= limit->m2.max; clock.m2++) {
-                       /* m1 is always 0 in IGD */
-                       if (clock.m2 >= clock.m1 && !IS_IGD(dev))
-                               break;
-                       for (clock.n = limit->n.min; clock.n <= limit->n.max;
-                            clock.n++) {
-                               for (clock.p1 = limit->p1.min;
-                                    clock.p1 <= limit->p1.max; clock.p1++) {
+       for (clock.p1 = limit->p1.max; clock.p1 >= limit->p1.min; clock.p1--) {
+               for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max;
+                    clock.m1++) {
+                       for (clock.m2 = limit->m2.min;
+                            clock.m2 <= limit->m2.max; clock.m2++) {
+                               /* m1 is always 0 in IGD */
+                               if (clock.m2 >= clock.m1 && !IS_IGD(dev))
+                                       break;
+                               for (clock.n = limit->n.min;
+                                    clock.n <= limit->n.max; clock.n++) {
                                        int this_err;
 
                                        intel_clock(dev, refclk, &clock);
@@ -695,6 +736,46 @@ intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
        return (err != target);
 }
 
+
+static bool
+intel_find_best_reduced_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
+                           int target, int refclk, intel_clock_t *best_clock)
+
+{
+       struct drm_device *dev = crtc->dev;
+       intel_clock_t clock;
+       int err = target;
+       bool found = false;
+
+       memcpy(&clock, best_clock, sizeof(intel_clock_t));
+
+       for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; clock.m1++) {
+               for (clock.m2 = limit->m2.min; clock.m2 <= limit->m2.max; clock.m2++) {
+                       /* m1 is always 0 in IGD */
+                       if (clock.m2 >= clock.m1 && !IS_IGD(dev))
+                               break;
+                       for (clock.n = limit->n.min; clock.n <= limit->n.max;
+                            clock.n++) {
+                               int this_err;
+
+                               intel_clock(dev, refclk, &clock);
+
+                               if (!intel_PLL_is_valid(crtc, &clock))
+                                       continue;
+
+                               this_err = abs(clock.dot - target);
+                               if (this_err < err) {
+                                       *best_clock = clock;
+                                       err = this_err;
+                                       found = true;
+                               }
+                       }
+               }
+       }
+
+       return found;
+}
+
 static bool
 intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
                        int target, int refclk, intel_clock_t *best_clock)
@@ -725,7 +806,7 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
        max_n = limit->n.max;
        /* based on hardware requriment prefer smaller n to precision */
        for (clock.n = limit->n.min; clock.n <= max_n; clock.n++) {
-               /* based on hardware requirment prefere larger m1,m2, p1 */
+               /* based on hardware requirment prefere larger m1,m2 */
                for (clock.m1 = limit->m1.max;
                     clock.m1 >= limit->m1.min; clock.m1--) {
                        for (clock.m2 = limit->m2.max;
@@ -752,6 +833,30 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
 }
 
 static bool
+intel_find_pll_igdng_dp(const intel_limit_t *limit, struct drm_crtc *crtc,
+                     int target, int refclk, intel_clock_t *best_clock)
+{
+       struct drm_device *dev = crtc->dev;
+       intel_clock_t clock;
+       if (target < 200000) {
+               clock.n = 1;
+               clock.p1 = 2;
+               clock.p2 = 10;
+               clock.m1 = 12;
+               clock.m2 = 9;
+       } else {
+               clock.n = 2;
+               clock.p1 = 1;
+               clock.p2 = 10;
+               clock.m1 = 14;
+               clock.m2 = 8;
+       }
+       intel_clock(dev, refclk, &clock);
+       memcpy(best_clock, &clock, sizeof(intel_clock_t));
+       return true;
+}
+
+static bool
 intel_igdng_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
                        int target, int refclk, intel_clock_t *best_clock)
 {
@@ -763,8 +868,16 @@ intel_igdng_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
        int err_most = 47;
        found = false;
 
+       /* eDP has only 2 clock choice, no n/m/p setting */
+       if (HAS_eDP)
+               return true;
+
+       if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT))
+               return intel_find_pll_igdng_dp(limit, crtc, target,
+                                              refclk, best_clock);
+
        if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
-               if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) ==
+               if ((I915_READ(PCH_LVDS) & LVDS_CLKB_POWER_MASK) ==
                    LVDS_CLKB_POWER_UP)
                        clock.p2 = limit->p2.p2_fast;
                else
@@ -778,15 +891,14 @@ intel_igdng_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
 
        memset(best_clock, 0, sizeof(*best_clock));
        max_n = limit->n.max;
-       /* based on hardware requriment prefer smaller n to precision */
-       for (clock.n = limit->n.min; clock.n <= max_n; clock.n++) {
-               /* based on hardware requirment prefere larger m1,m2, p1 */
-               for (clock.m1 = limit->m1.max;
-                    clock.m1 >= limit->m1.min; clock.m1--) {
-                       for (clock.m2 = limit->m2.max;
-                            clock.m2 >= limit->m2.min; clock.m2--) {
-                               for (clock.p1 = limit->p1.max;
-                                    clock.p1 >= limit->p1.min; clock.p1--) {
+       for (clock.p1 = limit->p1.max; clock.p1 >= limit->p1.min; clock.p1--) {
+               /* based on hardware requriment prefer smaller n to precision */
+               for (clock.n = limit->n.min; clock.n <= max_n; clock.n++) {
+                       /* based on hardware requirment prefere larger m1,m2 */
+                       for (clock.m1 = limit->m1.max;
+                            clock.m1 >= limit->m1.min; clock.m1--) {
+                               for (clock.m2 = limit->m2.max;
+                                    clock.m2 >= limit->m2.min; clock.m2--) {
                                        int this_err;
 
                                        intel_clock(dev, refclk, &clock);
@@ -842,6 +954,177 @@ intel_wait_for_vblank(struct drm_device *dev)
        mdelay(20);
 }
 
+/* Parameters have changed, update FBC info */
+static void i8xx_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 = intel_fb->obj->driver_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       int plane, i;
+       u32 fbc_ctl, fbc_ctl2;
+
+       dev_priv->cfb_pitch = dev_priv->cfb_size / FBC_LL_SIZE;
+
+       if (fb->pitch < dev_priv->cfb_pitch)
+               dev_priv->cfb_pitch = fb->pitch;
+
+       /* 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_plane = intel_crtc->plane;
+       plane = dev_priv->cfb_plane == 0 ? FBC_CTL_PLANEA : FBC_CTL_PLANEB;
+
+       /* Clear old tags */
+       for (i = 0; i < (FBC_LL_SIZE / 32) + 1; i++)
+               I915_WRITE(FBC_TAG + (i * 4), 0);
+
+       /* Set it up... */
+       fbc_ctl2 = FBC_CTL_FENCE_DBL | FBC_CTL_IDLE_IMM | plane;
+       if (obj_priv->tiling_mode != I915_TILING_NONE)
+               fbc_ctl2 |= FBC_CTL_CPU_FENCE;
+       I915_WRITE(FBC_CONTROL2, fbc_ctl2);
+       I915_WRITE(FBC_FENCE_OFF, crtc->y);
+
+       /* enable it... */
+       fbc_ctl = FBC_CTL_EN | FBC_CTL_PERIODIC;
+       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)
+               fbc_ctl |= dev_priv->cfb_fence;
+       I915_WRITE(FBC_CONTROL, fbc_ctl);
+
+       DRM_DEBUG("enabled FBC, pitch %ld, yoff %d, plane %d, ",
+                 dev_priv->cfb_pitch, crtc->y, dev_priv->cfb_plane);
+}
+
+void i8xx_disable_fbc(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 fbc_ctl;
+
+       if (!I915_HAS_FBC(dev))
+               return;
+
+       /* Disable compression */
+       fbc_ctl = I915_READ(FBC_CONTROL);
+       fbc_ctl &= ~FBC_CTL_EN;
+       I915_WRITE(FBC_CONTROL, fbc_ctl);
+
+       /* Wait for compressing bit to clear */
+       while (I915_READ(FBC_STATUS) & FBC_STAT_COMPRESSING)
+               ; /* nothing */
+
+       intel_wait_for_vblank(dev);
+
+       DRM_DEBUG("disabled FBC\n");
+}
+
+static bool i8xx_fbc_enabled(struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       return I915_READ(FBC_CONTROL) & FBC_CTL_EN;
+}
+
+/**
+ * intel_update_fbc - enable/disable FBC as needed
+ * @crtc: CRTC to point the compressor at
+ * @mode: mode in use
+ *
+ * Set up the framebuffer compression hardware at mode set time.  We
+ * enable it if possible:
+ *   - plane A only (on pre-965)
+ *   - no pixel mulitply/line duplication
+ *   - no alpha buffer discard
+ *   - no dual wide
+ *   - framebuffer <= 2048 in width, 1536 in height
+ *
+ * We can't assume that any compression will take place (worst case),
+ * so the compressed buffer has to be the same size as the uncompressed
+ * one.  It also must reside (along with the line length buffer) in
+ * stolen memory.
+ *
+ * We need to enable/disable FBC on a global basis.
+ */
+static void intel_update_fbc(struct drm_crtc *crtc,
+                            struct drm_display_mode *mode)
+{
+       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;
+       struct drm_i915_gem_object *obj_priv;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       int plane = intel_crtc->plane;
+
+       if (!i915_powersave)
+               return;
+
+       if (!crtc->fb)
+               return;
+
+       intel_fb = to_intel_framebuffer(fb);
+       obj_priv = intel_fb->obj->driver_private;
+
+       /*
+        * If FBC is already on, we just have to verify that we can
+        * keep it that way...
+        * Need to disable if:
+        *   - changing FBC params (stride, fence, mode)
+        *   - new fb is too large to fit in compressed buffer
+        *   - going to an unsupported config (interlace, pixel multiply, etc.)
+        */
+       if (intel_fb->obj->size > dev_priv->cfb_size) {
+               DRM_DEBUG("framebuffer too large, disabling compression\n");
+               goto out_disable;
+       }
+       if ((mode->flags & DRM_MODE_FLAG_INTERLACE) ||
+           (mode->flags & DRM_MODE_FLAG_DBLSCAN)) {
+               DRM_DEBUG("mode incompatible with compression, disabling\n");
+               goto out_disable;
+       }
+       if ((mode->hdisplay > 2048) ||
+           (mode->vdisplay > 1536)) {
+               DRM_DEBUG("mode too large for compression, disabling\n");
+               goto out_disable;
+       }
+       if (IS_I9XX(dev) && plane != 0) {
+               DRM_DEBUG("plane not 0, disabling compression\n");
+               goto out_disable;
+       }
+       if (obj_priv->tiling_mode != I915_TILING_X) {
+               DRM_DEBUG("framebuffer not tiled, disabling compression\n");
+               goto out_disable;
+       }
+
+       if (i8xx_fbc_enabled(crtc)) {
+               /* We can re-enable it in this case, but need to update pitch */
+               if (fb->pitch > dev_priv->cfb_pitch)
+                       i8xx_disable_fbc(dev);
+               if (obj_priv->fence_reg != dev_priv->cfb_fence)
+                       i8xx_disable_fbc(dev);
+               if (plane != dev_priv->cfb_plane)
+                       i8xx_disable_fbc(dev);
+       }
+
+       if (!i8xx_fbc_enabled(crtc)) {
+               /* Now try to turn it back on if possible */
+               i8xx_enable_fbc(crtc, 500);
+       }
+
+       return;
+
+out_disable:
+       DRM_DEBUG("unsupported config, disabling FBC\n");
+       /* Multiple disables should be harmless */
+       if (i8xx_fbc_enabled(crtc))
+               i8xx_disable_fbc(dev);
+}
+
 static int
 intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
                    struct drm_framebuffer *old_fb)
@@ -854,12 +1137,13 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
        struct drm_i915_gem_object *obj_priv;
        struct drm_gem_object *obj;
        int pipe = intel_crtc->pipe;
+       int plane = intel_crtc->plane;
        unsigned long Start, Offset;
-       int dspbase = (pipe == 0 ? DSPAADDR : DSPBADDR);
-       int dspsurf = (pipe == 0 ? DSPASURF : DSPBSURF);
-       int dspstride = (pipe == 0) ? DSPASTRIDE : DSPBSTRIDE;
-       int dsptileoff = (pipe == 0 ? DSPATILEOFF : DSPBTILEOFF);
-       int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR;
+       int dspbase = (plane == 0 ? DSPAADDR : DSPBADDR);
+       int dspsurf = (plane == 0 ? DSPASURF : DSPBSURF);
+       int dspstride = (plane == 0) ? DSPASTRIDE : DSPBSTRIDE;
+       int dsptileoff = (plane == 0 ? DSPATILEOFF : DSPBTILEOFF);
+       int dspcntr_reg = (plane == 0) ? DSPACNTR : DSPBCNTR;
        u32 dspcntr, alignment;
        int ret;
 
@@ -869,12 +1153,12 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
                return 0;
        }
 
-       switch (pipe) {
+       switch (plane) {
        case 0:
        case 1:
                break;
        default:
-               DRM_ERROR("Can't update pipe %d in SAREA\n", pipe);
+               DRM_ERROR("Can't update plane %d in SAREA\n", plane);
                return -EINVAL;
        }
 
@@ -954,6 +1238,10 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
                        dspcntr &= ~DISPPLANE_TILED;
        }
 
+       if (IS_IGDNG(dev))
+               /* must disable */
+               dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE;
+
        I915_WRITE(dspcntr_reg, dspcntr);
 
        Start = obj_priv->gtt_offset;
@@ -972,12 +1260,18 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
                I915_READ(dspbase);
        }
 
+       if (I915_HAS_FBC(dev) && (IS_I965G(dev) || plane == 0))
+               intel_update_fbc(crtc, &crtc->mode);
+
        intel_wait_for_vblank(dev);
 
        if (old_fb) {
                intel_fb = to_intel_framebuffer(old_fb);
+               obj_priv = intel_fb->obj->driver_private;
                i915_gem_object_unpin(intel_fb->obj);
        }
+       intel_increase_pllclock(crtc, true);
+
        mutex_unlock(&dev->struct_mutex);
 
        if (!dev->primary->master)
@@ -998,6 +1292,90 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
        return 0;
 }
 
+/* Disable the VGA plane that we never use */
+static void i915_disable_vga (struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u8 sr1;
+       u32 vga_reg;
+
+       if (IS_IGDNG(dev))
+               vga_reg = CPU_VGACNTRL;
+       else
+               vga_reg = VGACNTRL;
+
+       if (I915_READ(vga_reg) & VGA_DISP_DISABLE)
+               return;
+
+       I915_WRITE8(VGA_SR_INDEX, 1);
+       sr1 = I915_READ8(VGA_SR_DATA);
+       I915_WRITE8(VGA_SR_DATA, sr1 | (1 << 5));
+       udelay(100);
+
+       I915_WRITE(vga_reg, VGA_DISP_DISABLE);
+}
+
+static void igdng_disable_pll_edp (struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 dpa_ctl;
+
+       DRM_DEBUG("\n");
+       dpa_ctl = I915_READ(DP_A);
+       dpa_ctl &= ~DP_PLL_ENABLE;
+       I915_WRITE(DP_A, dpa_ctl);
+}
+
+static void igdng_enable_pll_edp (struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 dpa_ctl;
+
+       dpa_ctl = I915_READ(DP_A);
+       dpa_ctl |= DP_PLL_ENABLE;
+       I915_WRITE(DP_A, dpa_ctl);
+       udelay(200);
+}
+
+
+static void igdng_set_pll_edp (struct drm_crtc *crtc, int clock)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 dpa_ctl;
+
+       DRM_DEBUG("eDP PLL enable for clock %d\n", clock);
+       dpa_ctl = I915_READ(DP_A);
+       dpa_ctl &= ~DP_PLL_FREQ_MASK;
+
+       if (clock < 200000) {
+               u32 temp;
+               dpa_ctl |= DP_PLL_FREQ_160MHZ;
+               /* workaround for 160Mhz:
+                  1) program 0x4600c bits 15:0 = 0x8124
+                  2) program 0x46010 bit 0 = 1
+                  3) program 0x46034 bit 24 = 1
+                  4) program 0x64000 bit 14 = 1
+                  */
+               temp = I915_READ(0x4600c);
+               temp &= 0xffff0000;
+               I915_WRITE(0x4600c, temp | 0x8124);
+
+               temp = I915_READ(0x46010);
+               I915_WRITE(0x46010, temp | 1);
+
+               temp = I915_READ(0x46034);
+               I915_WRITE(0x46034, temp | (1 << 24));
+       } else {
+               dpa_ctl |= DP_PLL_FREQ_270MHZ;
+       }
+       I915_WRITE(DP_A, dpa_ctl);
+
+       udelay(500);
+}
+
 static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
 {
        struct drm_device *dev = crtc->dev;
@@ -1015,6 +1393,8 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
        int fdi_rx_imr_reg = (pipe == 0) ? FDI_RXA_IMR : FDI_RXB_IMR;
        int transconf_reg = (pipe == 0) ? TRANSACONF : TRANSBCONF;
        int pf_ctl_reg = (pipe == 0) ? PFA_CTL_1 : PFB_CTL_1;
+       int pf_win_size = (pipe == 0) ? PFA_WIN_SZ : PFB_WIN_SZ;
+       int pf_win_pos = (pipe == 0) ? PFA_WIN_POS : PFB_WIN_POS;
        int cpu_htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B;
        int cpu_hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B;
        int cpu_hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B;
@@ -1028,7 +1408,7 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
        int trans_vblank_reg = (pipe == 0) ? TRANS_VBLANK_A : TRANS_VBLANK_B;
        int trans_vsync_reg = (pipe == 0) ? TRANS_VSYNC_A : TRANS_VSYNC_B;
        u32 temp;
-       int tries = 5, j;
+       int tries = 5, j, n;
 
        /* XXX: When our outputs are all unaware of DPMS modes other than off
         * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC.
@@ -1038,27 +1418,45 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
        case DRM_MODE_DPMS_STANDBY:
        case DRM_MODE_DPMS_SUSPEND:
                DRM_DEBUG("crtc %d dpms on\n", pipe);
-               /* enable PCH DPLL */
-               temp = I915_READ(pch_dpll_reg);
-               if ((temp & DPLL_VCO_ENABLE) == 0) {
-                       I915_WRITE(pch_dpll_reg, temp | DPLL_VCO_ENABLE);
-                       I915_READ(pch_dpll_reg);
+               if (HAS_eDP) {
+                       /* enable eDP PLL */
+                       igdng_enable_pll_edp(crtc);
+               } else {
+                       /* enable PCH DPLL */
+                       temp = I915_READ(pch_dpll_reg);
+                       if ((temp & DPLL_VCO_ENABLE) == 0) {
+                               I915_WRITE(pch_dpll_reg, temp | DPLL_VCO_ENABLE);
+                               I915_READ(pch_dpll_reg);
+                       }
+
+                       /* enable PCH FDI RX PLL, wait warmup plus DMI latency */
+                       temp = I915_READ(fdi_rx_reg);
+                       I915_WRITE(fdi_rx_reg, temp | FDI_RX_PLL_ENABLE |
+                                       FDI_SEL_PCDCLK |
+                                       FDI_DP_PORT_WIDTH_X4); /* default 4 lanes */
+                       I915_READ(fdi_rx_reg);
+                       udelay(200);
+
+                       /* Enable CPU FDI TX PLL, always on for IGDNG */
+                       temp = I915_READ(fdi_tx_reg);
+                       if ((temp & FDI_TX_PLL_ENABLE) == 0) {
+                               I915_WRITE(fdi_tx_reg, temp | FDI_TX_PLL_ENABLE);
+                               I915_READ(fdi_tx_reg);
+                               udelay(100);
+                       }
                }
 
-               /* enable PCH FDI RX PLL, wait warmup plus DMI latency */
-               temp = I915_READ(fdi_rx_reg);
-               I915_WRITE(fdi_rx_reg, temp | FDI_RX_PLL_ENABLE |
-                               FDI_SEL_PCDCLK |
-                               FDI_DP_PORT_WIDTH_X4); /* default 4 lanes */
-               I915_READ(fdi_rx_reg);
-               udelay(200);
+               /* Enable panel fitting for LVDS */
+               if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
+                       temp = I915_READ(pf_ctl_reg);
+                       I915_WRITE(pf_ctl_reg, temp | PF_ENABLE);
 
-               /* Enable CPU FDI TX PLL, always on for IGDNG */
-               temp = I915_READ(fdi_tx_reg);
-               if ((temp & FDI_TX_PLL_ENABLE) == 0) {
-                       I915_WRITE(fdi_tx_reg, temp | FDI_TX_PLL_ENABLE);
-                       I915_READ(fdi_tx_reg);
-                       udelay(100);
+                       /* currently full aspect */
+                       I915_WRITE(pf_win_pos, 0);
+
+                       I915_WRITE(pf_win_size,
+                                  (dev_priv->panel_fixed_mode->hdisplay << 16) |
+                                  (dev_priv->panel_fixed_mode->vdisplay));
                }
 
                /* Enable CPU pipe */
@@ -1077,122 +1475,126 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
                        I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
                }
 
-               /* enable CPU FDI TX and PCH FDI RX */
-               temp = I915_READ(fdi_tx_reg);
-               temp |= FDI_TX_ENABLE;
-               temp |= FDI_DP_PORT_WIDTH_X4; /* default */
-               temp &= ~FDI_LINK_TRAIN_NONE;
-               temp |= FDI_LINK_TRAIN_PATTERN_1;
-               I915_WRITE(fdi_tx_reg, temp);
-               I915_READ(fdi_tx_reg);
+               if (!HAS_eDP) {
+                       /* enable CPU FDI TX and PCH FDI RX */
+                       temp = I915_READ(fdi_tx_reg);
+                       temp |= FDI_TX_ENABLE;
+                       temp |= FDI_DP_PORT_WIDTH_X4; /* default */
+                       temp &= ~FDI_LINK_TRAIN_NONE;
+                       temp |= FDI_LINK_TRAIN_PATTERN_1;
+                       I915_WRITE(fdi_tx_reg, temp);
+                       I915_READ(fdi_tx_reg);
 
-               temp = I915_READ(fdi_rx_reg);
-               temp &= ~FDI_LINK_TRAIN_NONE;
-               temp |= FDI_LINK_TRAIN_PATTERN_1;
-               I915_WRITE(fdi_rx_reg, temp | FDI_RX_ENABLE);
-               I915_READ(fdi_rx_reg);
+                       temp = I915_READ(fdi_rx_reg);
+                       temp &= ~FDI_LINK_TRAIN_NONE;
+                       temp |= FDI_LINK_TRAIN_PATTERN_1;
+                       I915_WRITE(fdi_rx_reg, temp | FDI_RX_ENABLE);
+                       I915_READ(fdi_rx_reg);
 
-               udelay(150);
+                       udelay(150);
 
-               /* Train FDI. */
-               /* umask FDI RX Interrupt symbol_lock and bit_lock bit
-                  for train result */
-               temp = I915_READ(fdi_rx_imr_reg);
-               temp &= ~FDI_RX_SYMBOL_LOCK;
-               temp &= ~FDI_RX_BIT_LOCK;
-               I915_WRITE(fdi_rx_imr_reg, temp);
-               I915_READ(fdi_rx_imr_reg);
-               udelay(150);
+                       /* Train FDI. */
+                       /* umask FDI RX Interrupt symbol_lock and bit_lock bit
+                          for train result */
+                       temp = I915_READ(fdi_rx_imr_reg);
+                       temp &= ~FDI_RX_SYMBOL_LOCK;
+                       temp &= ~FDI_RX_BIT_LOCK;
+                       I915_WRITE(fdi_rx_imr_reg, temp);
+                       I915_READ(fdi_rx_imr_reg);
+                       udelay(150);
 
-               temp = I915_READ(fdi_rx_iir_reg);
-               DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
+                       temp = I915_READ(fdi_rx_iir_reg);
+                       DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
 
-               if ((temp & FDI_RX_BIT_LOCK) == 0) {
-                       for (j = 0; j < tries; j++) {
-                               temp = I915_READ(fdi_rx_iir_reg);
-                               DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
-                               if (temp & FDI_RX_BIT_LOCK)
-                                       break;
-                               udelay(200);
-                       }
-                       if (j != tries)
+                       if ((temp & FDI_RX_BIT_LOCK) == 0) {
+                               for (j = 0; j < tries; j++) {
+                                       temp = I915_READ(fdi_rx_iir_reg);
+                                       DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
+                                       if (temp & FDI_RX_BIT_LOCK)
+                                               break;
+                                       udelay(200);
+                               }
+                               if (j != tries)
+                                       I915_WRITE(fdi_rx_iir_reg,
+                                                       temp | FDI_RX_BIT_LOCK);
+                               else
+                                       DRM_DEBUG("train 1 fail\n");
+                       } else {
                                I915_WRITE(fdi_rx_iir_reg,
                                                temp | FDI_RX_BIT_LOCK);
-                       else
-                               DRM_DEBUG("train 1 fail\n");
-               } else {
-                       I915_WRITE(fdi_rx_iir_reg,
-                                       temp | FDI_RX_BIT_LOCK);
-                       DRM_DEBUG("train 1 ok 2!\n");
-               }
-               temp = I915_READ(fdi_tx_reg);
-               temp &= ~FDI_LINK_TRAIN_NONE;
-               temp |= FDI_LINK_TRAIN_PATTERN_2;
-               I915_WRITE(fdi_tx_reg, temp);
-
-               temp = I915_READ(fdi_rx_reg);
-               temp &= ~FDI_LINK_TRAIN_NONE;
-               temp |= FDI_LINK_TRAIN_PATTERN_2;
-               I915_WRITE(fdi_rx_reg, temp);
+                               DRM_DEBUG("train 1 ok 2!\n");
+                       }
+                       temp = I915_READ(fdi_tx_reg);
+                       temp &= ~FDI_LINK_TRAIN_NONE;
+                       temp |= FDI_LINK_TRAIN_PATTERN_2;
+                       I915_WRITE(fdi_tx_reg, temp);
+
+                       temp = I915_READ(fdi_rx_reg);
+                       temp &= ~FDI_LINK_TRAIN_NONE;
+                       temp |= FDI_LINK_TRAIN_PATTERN_2;
+                       I915_WRITE(fdi_rx_reg, temp);
 
-               udelay(150);
+                       udelay(150);
 
-               temp = I915_READ(fdi_rx_iir_reg);
-               DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
+                       temp = I915_READ(fdi_rx_iir_reg);
+                       DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
 
-               if ((temp & FDI_RX_SYMBOL_LOCK) == 0) {
-                       for (j = 0; j < tries; j++) {
-                               temp = I915_READ(fdi_rx_iir_reg);
-                               DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
-                               if (temp & FDI_RX_SYMBOL_LOCK)
-                                       break;
-                               udelay(200);
-                       }
-                       if (j != tries) {
+                       if ((temp & FDI_RX_SYMBOL_LOCK) == 0) {
+                               for (j = 0; j < tries; j++) {
+                                       temp = I915_READ(fdi_rx_iir_reg);
+                                       DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
+                                       if (temp & FDI_RX_SYMBOL_LOCK)
+                                               break;
+                                       udelay(200);
+                               }
+                               if (j != tries) {
+                                       I915_WRITE(fdi_rx_iir_reg,
+                                                       temp | FDI_RX_SYMBOL_LOCK);
+                                       DRM_DEBUG("train 2 ok 1!\n");
+                               } else
+                                       DRM_DEBUG("train 2 fail\n");
+                       } else {
                                I915_WRITE(fdi_rx_iir_reg,
                                                temp | FDI_RX_SYMBOL_LOCK);
-                               DRM_DEBUG("train 2 ok 1!\n");
-                       } else
-                               DRM_DEBUG("train 2 fail\n");
-               } else {
-                       I915_WRITE(fdi_rx_iir_reg, temp | FDI_RX_SYMBOL_LOCK);
-                       DRM_DEBUG("train 2 ok 2!\n");
-               }
-               DRM_DEBUG("train done\n");
+                               DRM_DEBUG("train 2 ok 2!\n");
+                       }
+                       DRM_DEBUG("train done\n");
 
-               /* set transcoder timing */
-               I915_WRITE(trans_htot_reg, I915_READ(cpu_htot_reg));
-               I915_WRITE(trans_hblank_reg, I915_READ(cpu_hblank_reg));
-               I915_WRITE(trans_hsync_reg, I915_READ(cpu_hsync_reg));
+                       /* set transcoder timing */
+                       I915_WRITE(trans_htot_reg, I915_READ(cpu_htot_reg));
+                       I915_WRITE(trans_hblank_reg, I915_READ(cpu_hblank_reg));
+                       I915_WRITE(trans_hsync_reg, I915_READ(cpu_hsync_reg));
 
-               I915_WRITE(trans_vtot_reg, I915_READ(cpu_vtot_reg));
-               I915_WRITE(trans_vblank_reg, I915_READ(cpu_vblank_reg));
-               I915_WRITE(trans_vsync_reg, I915_READ(cpu_vsync_reg));
+                       I915_WRITE(trans_vtot_reg, I915_READ(cpu_vtot_reg));
+                       I915_WRITE(trans_vblank_reg, I915_READ(cpu_vblank_reg));
+                       I915_WRITE(trans_vsync_reg, I915_READ(cpu_vsync_reg));
 
-               /* enable PCH transcoder */
-               temp = I915_READ(transconf_reg);
-               I915_WRITE(transconf_reg, temp | TRANS_ENABLE);
-               I915_READ(transconf_reg);
+                       /* enable PCH transcoder */
+                       temp = I915_READ(transconf_reg);
+                       I915_WRITE(transconf_reg, temp | TRANS_ENABLE);
+                       I915_READ(transconf_reg);
 
-               while ((I915_READ(transconf_reg) & TRANS_STATE_ENABLE) == 0)
-                       ;
+                       while ((I915_READ(transconf_reg) & TRANS_STATE_ENABLE) == 0)
+                               ;
 
-               /* enable normal */
+                       /* enable normal */
 
-               temp = I915_READ(fdi_tx_reg);
-               temp &= ~FDI_LINK_TRAIN_NONE;
-               I915_WRITE(fdi_tx_reg, temp | FDI_LINK_TRAIN_NONE |
-                               FDI_TX_ENHANCE_FRAME_ENABLE);
-               I915_READ(fdi_tx_reg);
+                       temp = I915_READ(fdi_tx_reg);
+                       temp &= ~FDI_LINK_TRAIN_NONE;
+                       I915_WRITE(fdi_tx_reg, temp | FDI_LINK_TRAIN_NONE |
+                                       FDI_TX_ENHANCE_FRAME_ENABLE);
+                       I915_READ(fdi_tx_reg);
 
-               temp = I915_READ(fdi_rx_reg);
-               temp &= ~FDI_LINK_TRAIN_NONE;
-               I915_WRITE(fdi_rx_reg, temp | FDI_LINK_TRAIN_NONE |
-                               FDI_RX_ENHANCE_FRAME_ENABLE);
-               I915_READ(fdi_rx_reg);
+                       temp = I915_READ(fdi_rx_reg);
+                       temp &= ~FDI_LINK_TRAIN_NONE;
+                       I915_WRITE(fdi_rx_reg, temp | FDI_LINK_TRAIN_NONE |
+                                       FDI_RX_ENHANCE_FRAME_ENABLE);
+                       I915_READ(fdi_rx_reg);
 
-               /* wait one idle pattern time */
-               udelay(100);
+                       /* wait one idle pattern time */
+                       udelay(100);
+
+               }
 
                intel_crtc_load_lut(crtc);
 
@@ -1200,8 +1602,7 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
        case DRM_MODE_DPMS_OFF:
                DRM_DEBUG("crtc %d dpms off\n", pipe);
 
-               /* Disable the VGA plane that we never use */
-               I915_WRITE(CPU_VGACNTRL, VGA_DISP_DISABLE);
+               i915_disable_vga(dev);
 
                /* Disable display plane */
                temp = I915_READ(dspcntr_reg);
@@ -1217,17 +1618,23 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
                if ((temp & PIPEACONF_ENABLE) != 0) {
                        I915_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE);
                        I915_READ(pipeconf_reg);
+                       n = 0;
                        /* wait for cpu pipe off, pipe state */
-                       while ((I915_READ(pipeconf_reg) & I965_PIPECONF_ACTIVE) != 0)
-                               ;
+                       while ((I915_READ(pipeconf_reg) & I965_PIPECONF_ACTIVE) != 0) {
+                               n++;
+                               if (n < 60) {
+                                       udelay(500);
+                                       continue;
+                               } else {
+                                       DRM_DEBUG("pipe %d off delay\n", pipe);
+                                       break;
+                               }
+                       }
                } else
                        DRM_DEBUG("crtc %d is disabled\n", pipe);
 
-               /* IGDNG-A : disable cpu panel fitter ? */
-               temp = I915_READ(pf_ctl_reg);
-               if ((temp & PF_ENABLE) != 0) {
-                       I915_WRITE(pf_ctl_reg, temp & ~PF_ENABLE);
-                       I915_READ(pf_ctl_reg);
+               if (HAS_eDP) {
+                       igdng_disable_pll_edp(crtc);
                }
 
                /* disable CPU FDI tx and PCH FDI rx */
@@ -1239,6 +1646,8 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
                I915_WRITE(fdi_rx_reg, temp & ~FDI_RX_ENABLE);
                I915_READ(fdi_rx_reg);
 
+               udelay(100);
+
                /* still set train pattern 1 */
                temp = I915_READ(fdi_tx_reg);
                temp &= ~FDI_LINK_TRAIN_NONE;
@@ -1250,14 +1659,25 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
                temp |= FDI_LINK_TRAIN_PATTERN_1;
                I915_WRITE(fdi_rx_reg, temp);
 
+               udelay(100);
+
                /* disable PCH transcoder */
                temp = I915_READ(transconf_reg);
                if ((temp & TRANS_ENABLE) != 0) {
                        I915_WRITE(transconf_reg, temp & ~TRANS_ENABLE);
                        I915_READ(transconf_reg);
+                       n = 0;
                        /* wait for PCH transcoder off, transcoder state */
-                       while ((I915_READ(transconf_reg) & TRANS_STATE_ENABLE) != 0)
-                               ;
+                       while ((I915_READ(transconf_reg) & TRANS_STATE_ENABLE) != 0) {
+                               n++;
+                               if (n < 60) {
+                                       udelay(500);
+                                       continue;
+                               } else {
+                                       DRM_DEBUG("transcoder %d off delay\n", pipe);
+                                       break;
+                               }
+                       }
                }
 
                /* disable PCH DPLL */
@@ -1275,6 +1695,22 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
                        I915_READ(fdi_rx_reg);
                }
 
+               /* Disable CPU FDI TX PLL */
+               temp = I915_READ(fdi_tx_reg);
+               if ((temp & FDI_TX_PLL_ENABLE) != 0) {
+                       I915_WRITE(fdi_tx_reg, temp & ~FDI_TX_PLL_ENABLE);
+                       I915_READ(fdi_tx_reg);
+                       udelay(100);
+               }
+
+               /* Disable PF */
+               temp = I915_READ(pf_ctl_reg);
+               if ((temp & PF_ENABLE) != 0) {
+                       I915_WRITE(pf_ctl_reg, temp & ~PF_ENABLE);
+                       I915_READ(pf_ctl_reg);
+               }
+               I915_WRITE(pf_win_size, 0);
+
                /* Wait for the clocks to turn off. */
                udelay(150);
                break;
@@ -1287,9 +1723,10 @@ static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode)
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        int pipe = intel_crtc->pipe;
+       int plane = intel_crtc->plane;
        int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B;
-       int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR;
-       int dspbase_reg = (pipe == 0) ? DSPAADDR : DSPBADDR;
+       int dspcntr_reg = (plane == 0) ? DSPACNTR : DSPBCNTR;
+       int dspbase_reg = (plane == 0) ? DSPAADDR : DSPBADDR;
        int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
        u32 temp;
 
@@ -1332,6 +1769,9 @@ static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode)
 
                intel_crtc_load_lut(crtc);
 
+               if (I915_HAS_FBC(dev) && (IS_I965G(dev) || plane == 0))
+                       intel_update_fbc(crtc, &crtc->mode);
+
                /* Give the overlay scaler a chance to enable if it's on this pipe */
                //intel_crtc_dpms_video(crtc, true); TODO
                intel_update_watermarks(dev);
@@ -1341,8 +1781,11 @@ static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode)
                /* Give the overlay scaler a chance to disable if it's on this pipe */
                //intel_crtc_dpms_video(crtc, FALSE); TODO
 
+               if (dev_priv->cfb_plane == plane)
+                       i8xx_disable_fbc(dev);
+
                /* Disable the VGA plane that we never use */
-               I915_WRITE(VGACNTRL, VGA_DISP_DISABLE);
+               i915_disable_vga(dev);
 
                /* Disable display plane */
                temp = I915_READ(dspcntr_reg);
@@ -1399,6 +1842,8 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
        else
                i9xx_crtc_dpms(crtc, mode);
 
+       intel_crtc->dpms_mode = mode;
+
        if (!dev->primary->master)
                return;
 
@@ -1421,8 +1866,6 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
                DRM_ERROR("Can't update pipe %d in SAREA\n", pipe);
                break;
        }
-
-       intel_crtc->dpms_mode = mode;
 }
 
 static void intel_crtc_prepare (struct drm_crtc *crtc)
@@ -1676,7 +2119,14 @@ static unsigned long intel_calculate_wm(unsigned long clock_in_khz,
 {
        long entries_required, wm_size;
 
-       entries_required = (clock_in_khz * pixel_size * latency_ns) / 1000000;
+       /*
+        * Note: we need to make sure we don't overflow for various clock &
+        * latency values.
+        * clocks go from a few thousand to several hundred thousand.
+        * latency is usually a few thousand
+        */
+       entries_required = ((clock_in_khz / 1000) * pixel_size * latency_ns) /
+               1000;
        entries_required /= wm->cacheline_size;
 
        DRM_DEBUG("FIFO entries required for mode: %d\n", entries_required);
@@ -1741,14 +2191,13 @@ static struct cxsr_latency *intel_get_cxsr_latency(int is_desktop, int fsb,
        for (i = 0; i < ARRAY_SIZE(cxsr_latency_table); i++) {
                latency = &cxsr_latency_table[i];
                if (is_desktop == latency->is_desktop &&
-                       fsb == latency->fsb_freq && mem == latency->mem_freq)
-                       break;
-       }
-       if (i >= ARRAY_SIZE(cxsr_latency_table)) {
-               DRM_DEBUG("Unknown FSB/MEM found, disable CxSR\n");
-               return NULL;
+                   fsb == latency->fsb_freq && mem == latency->mem_freq)
+                       return latency;
        }
-       return latency;
+
+       DRM_DEBUG("Unknown FSB/MEM found, disable CxSR\n");
+
+       return NULL;
 }
 
 static void igd_disable_cxsr(struct drm_device *dev)
@@ -1823,7 +2272,21 @@ static void igd_enable_cxsr(struct drm_device *dev, unsigned long clock,
        return;
 }
 
-const static int latency_ns = 3000; /* default for non-igd platforms */
+/*
+ * Latency for FIFO fetches is dependent on several factors:
+ *   - memory configuration (speed, channels)
+ *   - chipset
+ *   - current MCH state
+ * It can be fairly high in some situations, so here we assume a fairly
+ * pessimal value.  It's a tradeoff between extra memory fetches (if we
+ * set this value too high, the FIFO will fetch frequently to stay full)
+ * and power consumption (set it too low to save power and we might see
+ * FIFO underruns and display "flicker").
+ *
+ * A value of 5us seems to be a good balance; safe for very low end
+ * platforms but not overly aggressive on lower latency configs.
+ */
+const static int latency_ns = 5000;
 
 static int intel_get_fifo_size(struct drm_device *dev, int plane)
 {
@@ -1844,6 +2307,9 @@ static int intel_get_fifo_size(struct drm_device *dev, int plane)
                        size = ((dsparb >> DSPARB_BEND_SHIFT) & 0x1ff) -
                                (dsparb & 0x1ff);
                size >>= 1; /* Convert to cachelines */
+       } else if (IS_845G(dev)) {
+               size = dsparb & 0x7f;
+               size >>= 2; /* Convert to cachelines */
        } else {
                size = dsparb & 0x7f;
                size >>= 1; /* Convert to cachelines */
@@ -1855,6 +2321,18 @@ static int intel_get_fifo_size(struct drm_device *dev, int plane)
        return size;
 }
 
+static void g4x_update_wm(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 fw_blc_self = I915_READ(FW_BLC_SELF);
+
+       if (i915_powersave)
+               fw_blc_self |= FW_BLC_SELF_EN;
+       else
+               fw_blc_self &= ~FW_BLC_SELF_EN;
+       I915_WRITE(FW_BLC_SELF, fw_blc_self);
+}
+
 static void i965_update_wm(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1906,7 +2384,8 @@ static void i9xx_update_wm(struct drm_device *dev, int planea_clock,
        cwm = 2;
 
        /* Calc sr entries for one plane configs */
-       if (!planea_clock || !planeb_clock) {
+       if (HAS_FW_BLC(dev) && sr_hdisplay &&
+           (!planea_clock || !planeb_clock)) {
                /* self-refresh has much higher latency */
                const static int sr_latency_ns = 6000;
 
@@ -1921,6 +2400,7 @@ static void i9xx_update_wm(struct drm_device *dev, int planea_clock,
                srwm = total_size - sr_entries;
                if (srwm < 0)
                        srwm = 1;
+               I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN | (srwm & 0x3f));
        }
 
        DRM_DEBUG("Setting FIFO watermarks - A: %d, B: %d, C: %d, SR %d\n",
@@ -1935,22 +2415,22 @@ static void i9xx_update_wm(struct drm_device *dev, int planea_clock,
 
        I915_WRITE(FW_BLC, fwater_lo);
        I915_WRITE(FW_BLC2, fwater_hi);
-       if (IS_I9XX(dev))
-               I915_WRITE(FW_BLC_SELF, (srwm & 0x3f));
 }
 
 static void i830_update_wm(struct drm_device *dev, int planea_clock,
                           int pixel_size)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       uint32_t fwater_lo = I915_READ(FW_BLC) & MM_FIFO_WATERMARK;
+       uint32_t fwater_lo = I915_READ(FW_BLC) & ~0xfff;
        int planea_wm;
 
        i830_wm_info.fifo_size = intel_get_fifo_size(dev, 0);
 
        planea_wm = intel_calculate_wm(planea_clock, &i830_wm_info,
                                       pixel_size, latency_ns);
-       fwater_lo = fwater_lo | planea_wm;
+       fwater_lo |= (3<<8) | planea_wm;
+
+       DRM_DEBUG("Setting FIFO watermarks - A: %d\n", planea_wm);
 
        I915_WRITE(FW_BLC, fwater_lo);
 }
@@ -1994,9 +2474,6 @@ static void intel_update_watermarks(struct drm_device *dev)
        unsigned long planea_clock = 0, planeb_clock = 0, sr_clock = 0;
        int enabled = 0, pixel_size = 0;
 
-       if (DSPARB_HWCONTROL(dev))
-               return;
-
        /* Get the clock config from both planes */
        list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
                intel_crtc = to_intel_crtc(crtc);
@@ -2029,7 +2506,9 @@ static void intel_update_watermarks(struct drm_device *dev)
        else if (IS_IGD(dev))
                igd_disable_cxsr(dev);
 
-       if (IS_I965G(dev))
+       if (IS_G4X(dev))
+               g4x_update_wm(dev);
+       else if (IS_I965G(dev))
                i965_update_wm(dev);
        else if (IS_I9XX(dev) || IS_MOBILE(dev))
                i9xx_update_wm(dev, planea_clock, planeb_clock, sr_hdisplay,
@@ -2048,10 +2527,11 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        int pipe = intel_crtc->pipe;
+       int plane = intel_crtc->plane;
        int fp_reg = (pipe == 0) ? FPA0 : FPB0;
        int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B;
        int dpll_md_reg = (intel_crtc->pipe == 0) ? DPLL_A_MD : DPLL_B_MD;
-       int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR;
+       int dspcntr_reg = (plane == 0) ? DSPACNTR : DSPBCNTR;
        int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
        int htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B;
        int hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B;
@@ -2059,14 +2539,15 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
        int vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B;
        int vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B;
        int vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B;
-       int dspsize_reg = (pipe == 0) ? DSPASIZE : DSPBSIZE;
-       int dsppos_reg = (pipe == 0) ? DSPAPOS : DSPBPOS;
+       int dspsize_reg = (plane == 0) ? DSPASIZE : DSPBSIZE;
+       int dsppos_reg = (plane == 0) ? DSPAPOS : DSPBPOS;
        int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC;
        int refclk, num_outputs = 0;
-       intel_clock_t clock;
-       u32 dpll = 0, fp = 0, dspcntr, pipeconf;
-       bool ok, is_sdvo = false, is_dvo = false;
+       intel_clock_t clock, reduced_clock;
+       u32 dpll = 0, fp = 0, fp2 = 0, dspcntr, pipeconf;
+       bool ok, has_reduced_clock = false, is_sdvo = false, is_dvo = false;
        bool is_crt = false, is_lvds = false, is_tv = false, is_dp = false;
+       bool is_edp = false;
        struct drm_mode_config *mode_config = &dev->mode_config;
        struct drm_connector *connector;
        const intel_limit_t *limit;
@@ -2082,6 +2563,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
        int lvds_reg = LVDS;
        u32 temp;
        int sdvo_pixel_multiply;
+       int target_clock;
 
        drm_vblank_pre_modeset(dev, pipe);
 
@@ -2113,6 +2595,9 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                case INTEL_OUTPUT_DISPLAYPORT:
                        is_dp = true;
                        break;
+               case INTEL_OUTPUT_EDP:
+                       is_edp = true;
+                       break;
                }
 
                num_outputs++;
@@ -2143,6 +2628,14 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                return -EINVAL;
        }
 
+       if (limit->find_reduced_pll && dev_priv->lvds_downclock_avail) {
+               memcpy(&reduced_clock, &clock, sizeof(intel_clock_t));
+               has_reduced_clock = limit->find_reduced_pll(limit, crtc,
+                                                           (adjusted_mode->clock*3/4),
+                                                           refclk,
+                                                           &reduced_clock);
+       }
+
        /* SDVO TV has fixed PLL values depend on its clock range,
           this mirrors vbios setting. */
        if (is_sdvo && is_tv) {
@@ -2164,16 +2657,41 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
        }
 
        /* FDI link */
-       if (IS_IGDNG(dev))
-               igdng_compute_m_n(3, 4, /* lane num 4 */
-                               adjusted_mode->clock,
-                               270000, /* lane clock */
-                               &m_n);
+       if (IS_IGDNG(dev)) {
+               int lane, link_bw;
+               /* eDP doesn't require FDI link, so just set DP M/N
+                  according to current link config */
+               if (is_edp) {
+                       struct drm_connector *edp;
+                       target_clock = mode->clock;
+                       edp = intel_pipe_get_output(crtc);
+                       intel_edp_link_config(to_intel_output(edp),
+                                       &lane, &link_bw);
+               } else {
+                       /* DP over FDI requires target mode clock
+                          instead of link clock */
+                       if (is_dp)
+                               target_clock = mode->clock;
+                       else
+                               target_clock = adjusted_mode->clock;
+                       lane = 4;
+                       link_bw = 270000;
+               }
+               igdng_compute_m_n(3, lane, target_clock,
+                                 link_bw, &m_n);
+       }
 
-       if (IS_IGD(dev))
+       if (IS_IGD(dev)) {
                fp = (1 << clock.n) << 16 | clock.m1 << 8 | clock.m2;
-       else
+               if (has_reduced_clock)
+                       fp2 = (1 << reduced_clock.n) << 16 |
+                               reduced_clock.m1 << 8 | reduced_clock.m2;
+       } else {
                fp = clock.n << 16 | clock.m1 << 8 | clock.m2;
+               if (has_reduced_clock)
+                       fp2 = reduced_clock.n << 16 | reduced_clock.m1 << 8 |
+                               reduced_clock.m2;
+       }
 
        if (!IS_IGDNG(dev))
                dpll = DPLL_VGA_MODE_DIS;
@@ -2186,7 +2704,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                if (is_sdvo) {
                        dpll |= DPLL_DVO_HIGH_SPEED;
                        sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
-                       if (IS_I945G(dev) || IS_I945GM(dev))
+                       if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev))
                                dpll |= (sdvo_pixel_multiply - 1) << SDVO_MULTIPLIER_SHIFT_HIRES;
                        else if (IS_IGDNG(dev))
                                dpll |= (sdvo_pixel_multiply - 1) << PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT;
@@ -2202,6 +2720,8 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                        /* also FPA1 */
                        if (IS_IGDNG(dev))
                                dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT;
+                       if (IS_G4X(dev) && has_reduced_clock)
+                               dpll |= (1 << (reduced_clock.p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT;
                }
                switch (clock.p2) {
                case 5:
@@ -2253,7 +2773,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
           enable color space conversion */
        if (!IS_IGDNG(dev)) {
                if (pipe == 0)
-                       dspcntr |= DISPPLANE_SEL_PIPE_A;
+                       dspcntr &= ~DISPPLANE_SEL_PIPE_MASK;
                else
                        dspcntr |= DISPPLANE_SEL_PIPE_B;
        }
@@ -2289,29 +2809,15 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                dpll_reg = pch_dpll_reg;
        }
 
-       if (dpll & DPLL_VCO_ENABLE) {
+       if (is_edp) {
+               igdng_disable_pll_edp(crtc);
+       } else if ((dpll & DPLL_VCO_ENABLE)) {
                I915_WRITE(fp_reg, fp);
                I915_WRITE(dpll_reg, dpll & ~DPLL_VCO_ENABLE);
                I915_READ(dpll_reg);
                udelay(150);
        }
 
-       if (IS_IGDNG(dev)) {
-               /* enable PCH clock reference source */
-               /* XXX need to change the setting for other outputs */
-               u32 temp;
-               temp = I915_READ(PCH_DREF_CONTROL);
-               temp &= ~DREF_NONSPREAD_SOURCE_MASK;
-               temp |= DREF_NONSPREAD_CK505_ENABLE;
-               temp &= ~DREF_SSC_SOURCE_MASK;
-               temp |= DREF_SSC_SOURCE_ENABLE;
-               temp &= ~DREF_SSC1_ENABLE;
-               /* if no eDP, disable source output to CPU */
-               temp &= ~DREF_CPU_SOURCE_OUTPUT_MASK;
-               temp |= DREF_CPU_SOURCE_OUTPUT_DISABLE;
-               I915_WRITE(PCH_DREF_CONTROL, temp);
-       }
-
        /* The LVDS pin pair needs to be on before the DPLLs are enabled.
         * This is an exception to the general rule that mode_set doesn't turn
         * things on.
@@ -2343,23 +2849,44 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
        if (is_dp)
                intel_dp_set_m_n(crtc, mode, adjusted_mode);
 
-       I915_WRITE(fp_reg, fp);
-       I915_WRITE(dpll_reg, dpll);
-       I915_READ(dpll_reg);
-       /* Wait for the clocks to stabilize. */
-       udelay(150);
+       if (!is_edp) {
+               I915_WRITE(fp_reg, fp);
+               I915_WRITE(dpll_reg, dpll);
+               I915_READ(dpll_reg);
+               /* Wait for the clocks to stabilize. */
+               udelay(150);
+
+               if (IS_I965G(dev) && !IS_IGDNG(dev)) {
+                       if (is_sdvo) {
+                               sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
+                               I915_WRITE(dpll_md_reg, (0 << DPLL_MD_UDI_DIVIDER_SHIFT) |
+                                       ((sdvo_pixel_multiply - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT));
+                       } else
+                               I915_WRITE(dpll_md_reg, 0);
+               } else {
+                       /* write it again -- the BIOS does, after all */
+                       I915_WRITE(dpll_reg, dpll);
+               }
+               I915_READ(dpll_reg);
+               /* Wait for the clocks to stabilize. */
+               udelay(150);
+       }
 
-       if (IS_I965G(dev) && !IS_IGDNG(dev)) {
-               sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
-               I915_WRITE(dpll_md_reg, (0 << DPLL_MD_UDI_DIVIDER_SHIFT) |
-                          ((sdvo_pixel_multiply - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT));
+       if (is_lvds && has_reduced_clock && i915_powersave) {
+               I915_WRITE(fp_reg + 4, fp2);
+               intel_crtc->lowfreq_avail = true;
+               if (HAS_PIPE_CXSR(dev)) {
+                       DRM_DEBUG("enabling CxSR downclocking\n");
+                       pipeconf |= PIPECONF_CXSR_DOWNCLOCK;
+               }
        } else {
-               /* write it again -- the BIOS does, after all */
-               I915_WRITE(dpll_reg, dpll);
+               I915_WRITE(fp_reg + 4, fp);
+               intel_crtc->lowfreq_avail = false;
+               if (HAS_PIPE_CXSR(dev)) {
+                       DRM_DEBUG("disabling CxSR downclocking\n");
+                       pipeconf &= ~PIPECONF_CXSR_DOWNCLOCK;
+               }
        }
-       I915_READ(dpll_reg);
-       /* Wait for the clocks to stabilize. */
-       udelay(150);
 
        I915_WRITE(htot_reg, (adjusted_mode->crtc_hdisplay - 1) |
                   ((adjusted_mode->crtc_htotal - 1) << 16));
@@ -2389,10 +2916,14 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                I915_WRITE(link_m1_reg, m_n.link_m);
                I915_WRITE(link_n1_reg, m_n.link_n);
 
-                /* enable FDI RX PLL too */
-               temp = I915_READ(fdi_rx_reg);
-               I915_WRITE(fdi_rx_reg, temp | FDI_RX_PLL_ENABLE);
-               udelay(200);
+               if (is_edp) {
+                       igdng_set_pll_edp(crtc, adjusted_mode->clock);
+               } else {
+                       /* enable FDI RX PLL too */
+                       temp = I915_READ(fdi_rx_reg);
+                       I915_WRITE(fdi_rx_reg, temp | FDI_RX_PLL_ENABLE);
+                       udelay(200);
+               }
        }
 
        I915_WRITE(pipeconf_reg, pipeconf);
@@ -2400,11 +2931,19 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
 
        intel_wait_for_vblank(dev);
 
+       if (IS_IGDNG(dev)) {
+               /* enable address swizzle for tiling buffer */
+               temp = I915_READ(DISP_ARB_CTL);
+               I915_WRITE(DISP_ARB_CTL, temp | DISP_TILE_SURFACE_SWIZZLING);
+       }
+
        I915_WRITE(dspcntr_reg, dspcntr);
 
        /* Flush the plane changes */
        ret = intel_pipe_set_base(crtc, x, y, old_fb);
 
+       if (I915_HAS_FBC(dev) && (IS_I965G(dev) || plane == 0))
+               intel_update_fbc(crtc, &crtc->mode);
        intel_update_watermarks(dev);
 
        drm_vblank_post_modeset(dev, pipe);
@@ -2449,6 +2988,7 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
        struct drm_gem_object *bo;
        struct drm_i915_gem_object *obj_priv;
        int pipe = intel_crtc->pipe;
+       int plane = intel_crtc->plane;
        uint32_t control = (pipe == 0) ? CURACNTR : CURBCNTR;
        uint32_t base = (pipe == 0) ? CURABASE : CURBBASE;
        uint32_t temp = I915_READ(control);
@@ -2534,6 +3074,10 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
                        i915_gem_object_unpin(intel_crtc->cursor_bo);
                drm_gem_object_unreference(intel_crtc->cursor_bo);
        }
+
+       if (I915_HAS_FBC(dev) && (IS_I965G(dev) || plane == 0))
+               intel_update_fbc(crtc, &crtc->mode);
+
        mutex_unlock(&dev->struct_mutex);
 
        intel_crtc->cursor_addr = addr;
@@ -2553,10 +3097,16 @@ static int intel_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
        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 intel_framebuffer *intel_fb;
        int pipe = intel_crtc->pipe;
        uint32_t temp = 0;
        uint32_t adder;
 
+       if (crtc->fb) {
+               intel_fb = to_intel_framebuffer(crtc->fb);
+               intel_mark_busy(dev, intel_fb->obj);
+       }
+
        if (x < 0) {
                temp |= CURSOR_POS_SIGN << CURSOR_X_SHIFT;
                x = -x;
@@ -2854,12 +3404,319 @@ struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev,
        return mode;
 }
 
+#define GPU_IDLE_TIMEOUT 500 /* ms */
+
+/* When this timer fires, we've been idle for awhile */
+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;
+
+       DRM_DEBUG("idle timer fired, downclocking\n");
+
+       dev_priv->busy = false;
+
+       queue_work(dev_priv->wq, &dev_priv->idle_work);
+}
+
+void intel_increase_renderclock(struct drm_device *dev, bool schedule)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+
+       if (IS_IGDNG(dev))
+               return;
+
+       if (!dev_priv->render_reclock_avail) {
+               DRM_DEBUG("not reclocking render clock\n");
+               return;
+       }
+
+       /* Restore render clock frequency to original value */
+       if (IS_G4X(dev) || IS_I9XX(dev))
+               pci_write_config_word(dev->pdev, GCFGC, dev_priv->orig_clock);
+       else if (IS_I85X(dev))
+               pci_write_config_word(dev->pdev, HPLLCC, dev_priv->orig_clock);
+       DRM_DEBUG("increasing render clock frequency\n");
+
+       /* Schedule downclock */
+       if (schedule)
+               mod_timer(&dev_priv->idle_timer, jiffies +
+                         msecs_to_jiffies(GPU_IDLE_TIMEOUT));
+}
+
+void intel_decrease_renderclock(struct drm_device *dev)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+
+       if (IS_IGDNG(dev))
+               return;
+
+       if (!dev_priv->render_reclock_avail) {
+               DRM_DEBUG("not reclocking render clock\n");
+               return;
+       }
+
+       if (IS_G4X(dev)) {
+               u16 gcfgc;
+
+               /* Adjust render clock... */
+               pci_read_config_word(dev->pdev, GCFGC, &gcfgc);
+
+               /* Down to minimum... */
+               gcfgc &= ~GM45_GC_RENDER_CLOCK_MASK;
+               gcfgc |= GM45_GC_RENDER_CLOCK_266_MHZ;
+
+               pci_write_config_word(dev->pdev, GCFGC, gcfgc);
+       } else if (IS_I965G(dev)) {
+               u16 gcfgc;
+
+               /* Adjust render clock... */
+               pci_read_config_word(dev->pdev, GCFGC, &gcfgc);
+
+               /* Down to minimum... */
+               gcfgc &= ~I965_GC_RENDER_CLOCK_MASK;
+               gcfgc |= I965_GC_RENDER_CLOCK_267_MHZ;
+
+               pci_write_config_word(dev->pdev, GCFGC, gcfgc);
+       } else if (IS_I945G(dev) || IS_I945GM(dev)) {
+               u16 gcfgc;
+
+               /* Adjust render clock... */
+               pci_read_config_word(dev->pdev, GCFGC, &gcfgc);
+
+               /* Down to minimum... */
+               gcfgc &= ~I945_GC_RENDER_CLOCK_MASK;
+               gcfgc |= I945_GC_RENDER_CLOCK_166_MHZ;
+
+               pci_write_config_word(dev->pdev, GCFGC, gcfgc);
+       } else if (IS_I915G(dev)) {
+               u16 gcfgc;
+
+               /* Adjust render clock... */
+               pci_read_config_word(dev->pdev, GCFGC, &gcfgc);
+
+               /* Down to minimum... */
+               gcfgc &= ~I915_GC_RENDER_CLOCK_MASK;
+               gcfgc |= I915_GC_RENDER_CLOCK_166_MHZ;
+
+               pci_write_config_word(dev->pdev, GCFGC, gcfgc);
+       } else if (IS_I85X(dev)) {
+               u16 hpllcc;
+
+               /* Adjust render clock... */
+               pci_read_config_word(dev->pdev, HPLLCC, &hpllcc);
+
+               /* Up to maximum... */
+               hpllcc &= ~GC_CLOCK_CONTROL_MASK;
+               hpllcc |= GC_CLOCK_133_200;
+
+               pci_write_config_word(dev->pdev, HPLLCC, hpllcc);
+       }
+       DRM_DEBUG("decreasing render clock frequency\n");
+}
+
+/* Note that no increase function is needed for this - increase_renderclock()
+ *  will also rewrite these bits
+ */
+void intel_decrease_displayclock(struct drm_device *dev)
+{
+       if (IS_IGDNG(dev))
+               return;
+
+       if (IS_I945G(dev) || IS_I945GM(dev) || IS_I915G(dev) ||
+           IS_I915GM(dev)) {
+               u16 gcfgc;
+
+               /* Adjust render clock... */
+               pci_read_config_word(dev->pdev, GCFGC, &gcfgc);
+
+               /* Down to minimum... */
+               gcfgc &= ~0xf0;
+               gcfgc |= 0x80;
+
+               pci_write_config_word(dev->pdev, GCFGC, gcfgc);
+       }
+}
+
+#define CRTC_IDLE_TIMEOUT 1000 /* ms */
+
+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;
+
+       DRM_DEBUG("idle timer fired, downclocking\n");
+
+       intel_crtc->busy = false;
+
+       queue_work(dev_priv->wq, &dev_priv->idle_work);
+}
+
+static void intel_increase_pllclock(struct drm_crtc *crtc, bool schedule)
+{
+       struct drm_device *dev = crtc->dev;
+       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);
+
+       if (IS_IGDNG(dev))
+               return;
+
+       if (!dev_priv->lvds_downclock_avail)
+               return;
+
+       if (!HAS_PIPE_CXSR(dev) && (dpll & DISPLAY_RATE_SELECT_FPA1)) {
+               DRM_DEBUG("upclocking LVDS\n");
+
+               /* Unlock panel regs */
+               I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) | (0xabcd << 16));
+
+               dpll &= ~DISPLAY_RATE_SELECT_FPA1;
+               I915_WRITE(dpll_reg, dpll);
+               dpll = I915_READ(dpll_reg);
+               intel_wait_for_vblank(dev);
+               dpll = I915_READ(dpll_reg);
+               if (dpll & DISPLAY_RATE_SELECT_FPA1)
+                       DRM_DEBUG("failed to upclock LVDS!\n");
+
+               /* ...and lock them again */
+               I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) & 0x3);
+       }
+
+       /* Schedule downclock */
+       if (schedule)
+               mod_timer(&intel_crtc->idle_timer, jiffies +
+                         msecs_to_jiffies(CRTC_IDLE_TIMEOUT));
+}
+
+static void intel_decrease_pllclock(struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       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);
+
+       if (IS_IGDNG(dev))
+               return;
+
+       if (!dev_priv->lvds_downclock_avail)
+               return;
+
+       /*
+        * Since this is called by a timer, we should never get here in
+        * the manual case.
+        */
+       if (!HAS_PIPE_CXSR(dev) && intel_crtc->lowfreq_avail) {
+               DRM_DEBUG("downclocking LVDS\n");
+
+               /* Unlock panel regs */
+               I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) | (0xabcd << 16));
+
+               dpll |= DISPLAY_RATE_SELECT_FPA1;
+               I915_WRITE(dpll_reg, dpll);
+               dpll = I915_READ(dpll_reg);
+               intel_wait_for_vblank(dev);
+               dpll = I915_READ(dpll_reg);
+               if (!(dpll & DISPLAY_RATE_SELECT_FPA1))
+                       DRM_DEBUG("failed to downclock LVDS!\n");
+
+               /* ...and lock them again */
+               I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) & 0x3);
+       }
+
+}
+
+/**
+ * intel_idle_update - adjust clocks for idleness
+ * @work: work struct
+ *
+ * Either the GPU or display (or both) went idle.  Check the busy status
+ * here and adjust the CRTC and GPU clocks as necessary.
+ */
+static void intel_idle_update(struct work_struct *work)
+{
+       drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t,
+                                                   idle_work);
+       struct drm_device *dev = dev_priv->dev;
+       struct drm_crtc *crtc;
+       struct intel_crtc *intel_crtc;
+
+       if (!i915_powersave)
+               return;
+
+       mutex_lock(&dev->struct_mutex);
+
+       /* GPU isn't processing, downclock it. */
+       if (!dev_priv->busy) {
+               intel_decrease_renderclock(dev);
+               intel_decrease_displayclock(dev);
+       }
+
+       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+               /* Skip inactive CRTCs */
+               if (!crtc->fb)
+                       continue;
+
+               intel_crtc = to_intel_crtc(crtc);
+               if (!intel_crtc->busy)
+                       intel_decrease_pllclock(crtc);
+       }
+
+       mutex_unlock(&dev->struct_mutex);
+}
+
+/**
+ * intel_mark_busy - mark the GPU and possibly the display busy
+ * @dev: drm device
+ * @obj: object we're operating on
+ *
+ * Callers can use this function to indicate that the GPU is busy processing
+ * commands.  If @obj matches one of the CRTC objects (i.e. it's a scanout
+ * 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)
+{
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       struct drm_crtc *crtc = NULL;
+       struct intel_framebuffer *intel_fb;
+       struct intel_crtc *intel_crtc;
+
+       if (!drm_core_check_feature(dev, DRIVER_MODESET))
+               return;
+
+       dev_priv->busy = true;
+       intel_increase_renderclock(dev, true);
+
+       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+               if (!crtc->fb)
+                       continue;
+
+               intel_crtc = to_intel_crtc(crtc);
+               intel_fb = to_intel_framebuffer(crtc->fb);
+               if (intel_fb->obj == obj) {
+                       if (!intel_crtc->busy) {
+                               /* Non-busy -> busy, upclock */
+                               intel_increase_pllclock(crtc, true);
+                               intel_crtc->busy = true;
+                       } else {
+                               /* Busy -> busy, put off timer */
+                               mod_timer(&intel_crtc->idle_timer, jiffies +
+                                         msecs_to_jiffies(CRTC_IDLE_TIMEOUT));
+                       }
+               }
+       }
+}
+
 static void intel_crtc_destroy(struct drm_crtc *crtc)
 {
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
 
-       if (intel_crtc->mode_set.mode)
-               drm_mode_destroy(crtc->dev, intel_crtc->mode_set.mode);
        drm_crtc_cleanup(crtc);
        kfree(intel_crtc);
 }
@@ -2902,19 +3759,22 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
                intel_crtc->lut_b[i] = i;
        }
 
+       /* Swap pipes & planes for FBC on pre-965 */
+       intel_crtc->pipe = pipe;
+       intel_crtc->plane = pipe;
+       if (IS_MOBILE(dev) && (IS_I9XX(dev) && !IS_I965G(dev))) {
+               DRM_DEBUG("swapping pipes & planes for FBC\n");
+               intel_crtc->plane = ((pipe == 0) ? 1 : 0);
+       }
+
        intel_crtc->cursor_addr = 0;
        intel_crtc->dpms_mode = DRM_MODE_DPMS_OFF;
        drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs);
 
-       intel_crtc->mode_set.crtc = &intel_crtc->base;
-       intel_crtc->mode_set.connectors = (struct drm_connector **)(intel_crtc + 1);
-       intel_crtc->mode_set.num_connectors = 0;
+       intel_crtc->busy = false;
 
-       if (i915_fbpercrtc) {
-
-
-
-       }
+       setup_timer(&intel_crtc->idle_timer, intel_crtc_idle_timer,
+                   (unsigned long)intel_crtc);
 }
 
 int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data,
@@ -2922,30 +3782,26 @@ int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data,
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
        struct drm_i915_get_pipe_from_crtc_id *pipe_from_crtc_id = data;
-       struct drm_crtc *crtc = NULL;
-       int pipe = -1;
+       struct drm_mode_object *drmmode_obj;
+       struct intel_crtc *crtc;
 
        if (!dev_priv) {
                DRM_ERROR("called with no initialization\n");
                return -EINVAL;
        }
 
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
-               struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-               if (crtc->base.id == pipe_from_crtc_id->crtc_id) {
-                       pipe = intel_crtc->pipe;
-                       break;
-               }
-       }
+       drmmode_obj = drm_mode_object_find(dev, pipe_from_crtc_id->crtc_id,
+                       DRM_MODE_OBJECT_CRTC);
 
-       if (pipe == -1) {
+       if (!drmmode_obj) {
                DRM_ERROR("no such CRTC id\n");
                return -EINVAL;
        }
 
-       pipe_from_crtc_id->pipe = pipe;
+       crtc = to_intel_crtc(obj_to_crtc(drmmode_obj));
+       pipe_from_crtc_id->pipe = crtc->pipe;
 
-       return 0;
+       return 0;
 }
 
 struct drm_crtc *intel_get_crtc_from_pipe(struct drm_device *dev, int pipe)
@@ -2968,7 +3824,7 @@ static int intel_connector_clones(struct drm_device *dev, int type_mask)
 
         list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
                struct intel_output *intel_output = to_intel_output(connector);
-               if (type_mask & (1 << intel_output->type))
+               if (type_mask & intel_output->clone_mask)
                        index_mask |= (1 << entry);
                entry++;
        }
@@ -2990,12 +3846,17 @@ static void intel_setup_outputs(struct drm_device *dev)
        if (IS_IGDNG(dev)) {
                int found;
 
+               if (IS_MOBILE(dev) && (I915_READ(DP_A) & DP_DETECTED))
+                       intel_dp_init(dev, DP_A);
+
                if (I915_READ(HDMIB) & PORT_DETECTED) {
                        /* check SDVOB */
                        /* found = intel_sdvo_init(dev, HDMIB); */
                        found = 0;
                        if (!found)
                                intel_hdmi_init(dev, HDMIB);
+                       if (!found && (I915_READ(PCH_DP_B) & DP_DETECTED))
+                               intel_dp_init(dev, PCH_DP_B);
                }
 
                if (I915_READ(HDMIC) & PORT_DETECTED)
@@ -3004,31 +3865,37 @@ static void intel_setup_outputs(struct drm_device *dev)
                if (I915_READ(HDMID) & PORT_DETECTED)
                        intel_hdmi_init(dev, HDMID);
 
+               if (I915_READ(PCH_DP_C) & DP_DETECTED)
+                       intel_dp_init(dev, PCH_DP_C);
+
+               if (I915_READ(PCH_DP_D) & DP_DETECTED)
+                       intel_dp_init(dev, PCH_DP_D);
+
        } else if (IS_I9XX(dev)) {
-               int found;
-               u32 reg;
+               bool found = false;
 
                if (I915_READ(SDVOB) & SDVO_DETECTED) {
                        found = intel_sdvo_init(dev, SDVOB);
                        if (!found && SUPPORTS_INTEGRATED_HDMI(dev))
                                intel_hdmi_init(dev, SDVOB);
+
                        if (!found && SUPPORTS_INTEGRATED_DP(dev))
                                intel_dp_init(dev, DP_B);
                }
 
                /* Before G4X SDVOC doesn't have its own detect register */
-               if (IS_G4X(dev))
-                       reg = SDVOC;
-               else
-                       reg = SDVOB;
 
-               if (I915_READ(reg) & SDVO_DETECTED) {
+               if (I915_READ(SDVOB) & SDVO_DETECTED)
                        found = intel_sdvo_init(dev, SDVOC);
-                       if (!found && SUPPORTS_INTEGRATED_HDMI(dev))
+
+               if (!found && (I915_READ(SDVOC) & SDVO_DETECTED)) {
+
+                       if (SUPPORTS_INTEGRATED_HDMI(dev))
                                intel_hdmi_init(dev, SDVOC);
-                       if (!found && SUPPORTS_INTEGRATED_DP(dev))
+                       if (SUPPORTS_INTEGRATED_DP(dev))
                                intel_dp_init(dev, DP_C);
                }
+
                if (SUPPORTS_INTEGRATED_DP(dev) && (I915_READ(DP_D) & DP_DETECTED))
                        intel_dp_init(dev, DP_D);
        } else
@@ -3040,47 +3907,10 @@ static void intel_setup_outputs(struct drm_device *dev)
        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
                struct intel_output *intel_output = to_intel_output(connector);
                struct drm_encoder *encoder = &intel_output->enc;
-               int crtc_mask = 0, clone_mask = 0;
 
-               /* valid crtcs */
-               switch(intel_output->type) {
-               case INTEL_OUTPUT_HDMI:
-                       crtc_mask = ((1 << 0)|
-                                    (1 << 1));
-                       clone_mask = ((1 << INTEL_OUTPUT_HDMI));
-                       break;
-               case INTEL_OUTPUT_DVO:
-               case INTEL_OUTPUT_SDVO:
-                       crtc_mask = ((1 << 0)|
-                                    (1 << 1));
-                       clone_mask = ((1 << INTEL_OUTPUT_ANALOG) |
-                                     (1 << INTEL_OUTPUT_DVO) |
-                                     (1 << INTEL_OUTPUT_SDVO));
-                       break;
-               case INTEL_OUTPUT_ANALOG:
-                       crtc_mask = ((1 << 0)|
-                                    (1 << 1));
-                       clone_mask = ((1 << INTEL_OUTPUT_ANALOG) |
-                                     (1 << INTEL_OUTPUT_DVO) |
-                                     (1 << INTEL_OUTPUT_SDVO));
-                       break;
-               case INTEL_OUTPUT_LVDS:
-                       crtc_mask = (1 << 1);
-                       clone_mask = (1 << INTEL_OUTPUT_LVDS);
-                       break;
-               case INTEL_OUTPUT_TVOUT:
-                       crtc_mask = ((1 << 0) |
-                                    (1 << 1));
-                       clone_mask = (1 << INTEL_OUTPUT_TVOUT);
-                       break;
-               case INTEL_OUTPUT_DISPLAYPORT:
-                       crtc_mask = ((1 << 0) |
-                                    (1 << 1));
-                       clone_mask = (1 << INTEL_OUTPUT_DISPLAYPORT);
-                       break;
-               }
-               encoder->possible_crtcs = crtc_mask;
-               encoder->possible_clones = intel_connector_clones(dev, clone_mask);
+               encoder->possible_crtcs = intel_output->crtc_mask;
+               encoder->possible_clones = intel_connector_clones(dev,
+                                               intel_output->clone_mask);
        }
 }
 
@@ -3172,8 +4002,56 @@ static const struct drm_mode_config_funcs intel_mode_funcs = {
        .fb_changed = intelfb_probe,
 };
 
+void intel_init_clock_gating(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+
+       /*
+        * Disable clock gating reported to work incorrectly according to the
+        * specs, but enable as much else as we can.
+        */
+       if (IS_G4X(dev)) {
+               uint32_t dspclk_gate;
+               I915_WRITE(RENCLK_GATE_D1, 0);
+               I915_WRITE(RENCLK_GATE_D2, VF_UNIT_CLOCK_GATE_DISABLE |
+                      GS_UNIT_CLOCK_GATE_DISABLE |
+                      CL_UNIT_CLOCK_GATE_DISABLE);
+               I915_WRITE(RAMCLK_GATE_D, 0);
+               dspclk_gate = VRHUNIT_CLOCK_GATE_DISABLE |
+                       OVRUNIT_CLOCK_GATE_DISABLE |
+                       OVCUNIT_CLOCK_GATE_DISABLE;
+               if (IS_GM45(dev))
+                       dspclk_gate |= DSSUNIT_CLOCK_GATE_DISABLE;
+               I915_WRITE(DSPCLK_GATE_D, dspclk_gate);
+       } else if (IS_I965GM(dev)) {
+               I915_WRITE(RENCLK_GATE_D1, I965_RCC_CLOCK_GATE_DISABLE);
+               I915_WRITE(RENCLK_GATE_D2, 0);
+               I915_WRITE(DSPCLK_GATE_D, 0);
+               I915_WRITE(RAMCLK_GATE_D, 0);
+               I915_WRITE16(DEUC, 0);
+       } else if (IS_I965G(dev)) {
+               I915_WRITE(RENCLK_GATE_D1, I965_RCZ_CLOCK_GATE_DISABLE |
+                      I965_RCC_CLOCK_GATE_DISABLE |
+                      I965_RCPB_CLOCK_GATE_DISABLE |
+                      I965_ISC_CLOCK_GATE_DISABLE |
+                      I965_FBC_CLOCK_GATE_DISABLE);
+               I915_WRITE(RENCLK_GATE_D2, 0);
+       } else if (IS_I9XX(dev)) {
+               u32 dstate = I915_READ(D_STATE);
+
+               dstate |= DSTATE_PLL_D3_OFF | DSTATE_GFX_CLOCK_GATING |
+                       DSTATE_DOT_CLOCK_GATING;
+               I915_WRITE(D_STATE, dstate);
+       } else if (IS_I855(dev) || IS_I865G(dev)) {
+               I915_WRITE(RENCLK_GATE_D1, SV_CLOCK_GATE_DISABLE);
+       } else if (IS_I830(dev)) {
+               I915_WRITE(DSPCLK_GATE_D, OVRUNIT_CLOCK_GATE_DISABLE);
+       }
+}
+
 void intel_modeset_init(struct drm_device *dev)
 {
+       struct drm_i915_private *dev_priv = dev->dev_private;
        int num_pipe;
        int i;
 
@@ -3208,15 +4086,48 @@ void intel_modeset_init(struct drm_device *dev)
        DRM_DEBUG("%d display pipe%s available.\n",
                  num_pipe, num_pipe > 1 ? "s" : "");
 
+       if (IS_I85X(dev))
+               pci_read_config_word(dev->pdev, HPLLCC, &dev_priv->orig_clock);
+       else if (IS_I9XX(dev) || IS_G4X(dev))
+               pci_read_config_word(dev->pdev, GCFGC, &dev_priv->orig_clock);
+
        for (i = 0; i < num_pipe; i++) {
                intel_crtc_init(dev, i);
        }
 
        intel_setup_outputs(dev);
+
+       intel_init_clock_gating(dev);
+
+       INIT_WORK(&dev_priv->idle_work, intel_idle_update);
+       setup_timer(&dev_priv->idle_timer, intel_gpu_idle_timer,
+                   (unsigned long)dev);
 }
 
 void intel_modeset_cleanup(struct drm_device *dev)
 {
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_crtc *crtc;
+       struct intel_crtc *intel_crtc;
+
+       mutex_lock(&dev->struct_mutex);
+
+       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+               /* Skip inactive CRTCs */
+               if (!crtc->fb)
+                       continue;
+
+               intel_crtc = to_intel_crtc(crtc);
+               intel_increase_pllclock(crtc, false);
+               del_timer_sync(&intel_crtc->idle_timer);
+       }
+
+       intel_increase_renderclock(dev, false);
+       del_timer_sync(&dev_priv->idle_timer);
+
+       mutex_unlock(&dev->struct_mutex);
+
+       i8xx_disable_fbc(dev);
        drm_mode_config_cleanup(dev);
 }