Merge branch 'drm-intel-fixes' into drm-intel-next
[pandora-kernel.git] / drivers / gpu / drm / i915 / intel_display.c
index 19daead..2e9191d 100644 (file)
@@ -43,7 +43,7 @@
 
 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);
+static void intel_increase_pllclock(struct drm_crtc *crtc);
 static void intel_crtc_update_cursor(struct drm_crtc *crtc);
 
 typedef struct {
@@ -342,6 +342,13 @@ static bool
 intel_find_pll_ironlake_dp(const intel_limit_t *, struct drm_crtc *crtc,
                           int target, int refclk, intel_clock_t *best_clock);
 
+static inline u32 /* units of 100MHz */
+intel_fdi_link_freq(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       return (I915_READ(FDI_PLL_BIOS_0) & FDI_PLL_FB_CLOCK_MASK) + 2;
+}
+
 static const intel_limit_t intel_limits_i8xx_dvo = {
         .dot = { .min = I8XX_DOT_MIN,          .max = I8XX_DOT_MAX },
         .vco = { .min = I8XX_VCO_MIN,          .max = I8XX_VCO_MAX },
@@ -744,20 +751,17 @@ static void intel_clock(struct drm_device *dev, int refclk, intel_clock_t *clock
 /**
  * Returns whether any output on the specified pipe is of the specified type
  */
-bool intel_pipe_has_type (struct drm_crtc *crtc, int type)
+bool intel_pipe_has_type(struct drm_crtc *crtc, int type)
 {
-    struct drm_device *dev = crtc->dev;
-    struct drm_mode_config *mode_config = &dev->mode_config;
-    struct drm_encoder *l_entry;
-
-    list_for_each_entry(l_entry, &mode_config->encoder_list, head) {
-           if (l_entry && l_entry->crtc == crtc) {
-                   struct intel_encoder *intel_encoder = enc_to_intel_encoder(l_entry);
-                   if (intel_encoder->type == type)
-                           return true;
-           }
-    }
-    return false;
+       struct drm_device *dev = crtc->dev;
+       struct drm_mode_config *mode_config = &dev->mode_config;
+       struct intel_encoder *encoder;
+
+       list_for_each_entry(encoder, &mode_config->encoder_list, base.head)
+               if (encoder->base.crtc == crtc && encoder->type == type)
+                       return true;
+
+       return false;
 }
 
 #define INTELPllInvalid(s)   do { /* DRM_DEBUG(s); */ return false; } while (0)
@@ -1007,9 +1011,9 @@ void intel_wait_for_vblank(struct drm_device *dev, int pipe)
                   I915_READ(pipestat_reg) | PIPE_VBLANK_INTERRUPT_STATUS);
 
        /* Wait for vblank interrupt bit to set */
-       if (wait_for((I915_READ(pipestat_reg) &
-                     PIPE_VBLANK_INTERRUPT_STATUS),
-                    50, 0))
+       if (wait_for(I915_READ(pipestat_reg) &
+                    PIPE_VBLANK_INTERRUPT_STATUS,
+                    50))
                DRM_DEBUG_KMS("vblank wait timed out\n");
 }
 
@@ -1108,7 +1112,7 @@ void i8xx_disable_fbc(struct drm_device *dev)
        I915_WRITE(FBC_CONTROL, fbc_ctl);
 
        /* Wait for compressing bit to clear */
-       if (wait_for((I915_READ(FBC_STATUS) & FBC_STAT_COMPRESSING) == 0, 10, 0)) {
+       if (wait_for((I915_READ(FBC_STATUS) & FBC_STAT_COMPRESSING) == 0, 10)) {
                DRM_DEBUG_KMS("FBC idle timed out\n");
                return;
        }
@@ -1527,7 +1531,7 @@ intel_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb,
                intel_update_fbc(crtc, &crtc->mode);
 
        intel_wait_for_vblank(dev, intel_crtc->pipe);
-       intel_increase_pllclock(crtc, true);
+       intel_increase_pllclock(crtc);
 
        return 0;
 }
@@ -1644,6 +1648,7 @@ static void ironlake_set_pll_edp (struct drm_crtc *crtc, int clock)
                dpa_ctl |= DP_PLL_FREQ_270MHZ;
        }
        I915_WRITE(DP_A, dpa_ctl);
+       POSTING_READ(DP_A);
 
        udelay(500);
 }
@@ -1711,6 +1716,7 @@ static void ironlake_fdi_link_train(struct drm_crtc *crtc)
        temp &= ~FDI_LINK_TRAIN_NONE;
        temp |= FDI_LINK_TRAIN_PATTERN_2;
        I915_WRITE(fdi_rx_reg, temp);
+       POSTING_READ(fdi_rx_reg);
        udelay(150);
 
        tries = 0;
@@ -1791,6 +1797,7 @@ static void gen6_fdi_link_train(struct drm_crtc *crtc)
                temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
                temp |= snb_b_fdi_train_param[i];
                I915_WRITE(fdi_tx_reg, temp);
+               POSTING_READ(fdi_tx_reg);
                udelay(500);
 
                temp = I915_READ(fdi_rx_iir_reg);
@@ -1826,6 +1833,7 @@ static void gen6_fdi_link_train(struct drm_crtc *crtc)
                temp |= FDI_LINK_TRAIN_PATTERN_2;
        }
        I915_WRITE(fdi_rx_reg, temp);
+       POSTING_READ(fdi_rx_reg);
        udelay(150);
 
        for (i = 0; i < 4; i++ ) {
@@ -1833,6 +1841,7 @@ static void gen6_fdi_link_train(struct drm_crtc *crtc)
                temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
                temp |= snb_b_fdi_train_param[i];
                I915_WRITE(fdi_tx_reg, temp);
+               POSTING_READ(fdi_tx_reg);
                udelay(500);
 
                temp = I915_READ(fdi_rx_iir_reg);
@@ -1851,7 +1860,57 @@ static void gen6_fdi_link_train(struct drm_crtc *crtc)
        DRM_DEBUG_KMS("FDI train done.\n");
 }
 
-static void ironlake_crtc_dpms(struct drm_crtc *crtc, int mode)
+static void ironlake_fdi_enable(struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       int pipe = intel_crtc->pipe;
+       int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
+       int fdi_tx_reg = (pipe == 0) ? FDI_TXA_CTL : FDI_TXB_CTL;
+       int fdi_rx_reg = (pipe == 0) ? FDI_RXA_CTL : FDI_RXB_CTL;
+       int data_m1_reg = (pipe == 0) ? PIPEA_DATA_M1 : PIPEB_DATA_M1;
+       u32 temp;
+       u32 pipe_bpc;
+       u32 tx_size;
+
+       temp = I915_READ(pipeconf_reg);
+       pipe_bpc = temp & PIPE_BPC_MASK;
+
+       /* Write the TU size bits so error detection works */
+       tx_size = I915_READ(data_m1_reg) & TU_SIZE_MASK;
+       I915_WRITE(FDI_RXA_TUSIZE1, tx_size);
+
+       /* enable PCH FDI RX PLL, wait warmup plus DMI latency */
+       temp = I915_READ(fdi_rx_reg);
+       /*
+        * make the BPC in FDI Rx be consistent with that in
+        * pipeconf reg.
+        */
+       temp &= ~(0x7 << 16);
+       temp |= (pipe_bpc << 11);
+       temp &= ~(7 << 19);
+       temp |= (intel_crtc->fdi_lanes - 1) << 19;
+       I915_WRITE(fdi_rx_reg, temp | FDI_RX_PLL_ENABLE);
+       I915_READ(fdi_rx_reg);
+       udelay(200);
+
+       /* Switch from Rawclk to PCDclk */
+       temp = I915_READ(fdi_rx_reg);
+       I915_WRITE(fdi_rx_reg, temp | FDI_SEL_PCDCLK);
+       I915_READ(fdi_rx_reg);
+       udelay(200);
+
+       /* Enable CPU FDI TX PLL, always on for Ironlake */
+       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);
+       }
+}
+
+static void ironlake_crtc_enable(struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -1884,375 +1943,356 @@ static void ironlake_crtc_dpms(struct drm_crtc *crtc, int mode)
        temp = I915_READ(pipeconf_reg);
        pipe_bpc = temp & PIPE_BPC_MASK;
 
-       /* 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.
-        */
-       switch (mode) {
-       case DRM_MODE_DPMS_ON:
-       case DRM_MODE_DPMS_STANDBY:
-       case DRM_MODE_DPMS_SUSPEND:
-               DRM_DEBUG_KMS("crtc %d/%d dpms on\n", pipe, plane);
-
-               if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
-                       temp = I915_READ(PCH_LVDS);
-                       if ((temp & LVDS_PORT_EN) == 0) {
-                               I915_WRITE(PCH_LVDS, temp | LVDS_PORT_EN);
-                               POSTING_READ(PCH_LVDS);
-                       }
+       if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
+               temp = I915_READ(PCH_LVDS);
+               if ((temp & LVDS_PORT_EN) == 0) {
+                       I915_WRITE(PCH_LVDS, temp | LVDS_PORT_EN);
+                       POSTING_READ(PCH_LVDS);
                }
+       }
 
-               if (!HAS_eDP) {
-
-                       /* enable PCH FDI RX PLL, wait warmup plus DMI latency */
-                       temp = I915_READ(fdi_rx_reg);
-                       /*
-                        * make the BPC in FDI Rx be consistent with that in
-                        * pipeconf reg.
-                        */
-                       temp &= ~(0x7 << 16);
-                       temp |= (pipe_bpc << 11);
-                       temp &= ~(7 << 19);
-                       temp |= (intel_crtc->fdi_lanes - 1) << 19;
-                       I915_WRITE(fdi_rx_reg, temp | FDI_RX_PLL_ENABLE);
-                       I915_READ(fdi_rx_reg);
-                       udelay(200);
-
-                       /* Switch from Rawclk to PCDclk */
-                       temp = I915_READ(fdi_rx_reg);
-                       I915_WRITE(fdi_rx_reg, temp | FDI_SEL_PCDCLK);
-                       I915_READ(fdi_rx_reg);
-                       udelay(200);
-
-                       /* Enable CPU FDI TX PLL, always on for Ironlake */
-                       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);
-                       }
-               }
+       ironlake_fdi_enable(crtc);
 
-               /* Enable panel fitting for LVDS */
-               if (dev_priv->pch_pf_size &&
-                   (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)
-                   || HAS_eDP || intel_pch_has_edp(crtc))) {
-                       /* Force use of hard-coded filter coefficients
-                        * as some pre-programmed values are broken,
-                        * e.g. x201.
-                        */
-                       I915_WRITE(pipe ? PFB_CTL_1 : PFA_CTL_1,
-                                  PF_ENABLE | PF_FILTER_MED_3x3);
-                       I915_WRITE(pipe ? PFB_WIN_POS : PFA_WIN_POS,
-                                  dev_priv->pch_pf_pos);
-                       I915_WRITE(pipe ? PFB_WIN_SZ : PFA_WIN_SZ,
-                                  dev_priv->pch_pf_size);
-               }
+       /* Enable panel fitting for LVDS */
+       if (dev_priv->pch_pf_size &&
+           (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)
+            || HAS_eDP || intel_pch_has_edp(crtc))) {
+               /* Force use of hard-coded filter coefficients
+                * as some pre-programmed values are broken,
+                * e.g. x201.
+                */
+               I915_WRITE(pipe ? PFB_CTL_1 : PFA_CTL_1,
+                          PF_ENABLE | PF_FILTER_MED_3x3);
+               I915_WRITE(pipe ? PFB_WIN_POS : PFA_WIN_POS,
+                          dev_priv->pch_pf_pos);
+               I915_WRITE(pipe ? PFB_WIN_SZ : PFA_WIN_SZ,
+                          dev_priv->pch_pf_size);
+       }
 
-               /* Enable CPU pipe */
-               temp = I915_READ(pipeconf_reg);
-               if ((temp & PIPEACONF_ENABLE) == 0) {
-                       I915_WRITE(pipeconf_reg, temp | PIPEACONF_ENABLE);
-                       I915_READ(pipeconf_reg);
-                       udelay(100);
-               }
+       /* Enable CPU pipe */
+       temp = I915_READ(pipeconf_reg);
+       if ((temp & PIPEACONF_ENABLE) == 0) {
+               I915_WRITE(pipeconf_reg, temp | PIPEACONF_ENABLE);
+               I915_READ(pipeconf_reg);
+               udelay(100);
+       }
 
-               /* configure and enable CPU plane */
-               temp = I915_READ(dspcntr_reg);
-               if ((temp & DISPLAY_PLANE_ENABLE) == 0) {
-                       I915_WRITE(dspcntr_reg, temp | DISPLAY_PLANE_ENABLE);
-                       /* Flush the plane changes */
-                       I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
-               }
+       /* configure and enable CPU plane */
+       temp = I915_READ(dspcntr_reg);
+       if ((temp & DISPLAY_PLANE_ENABLE) == 0) {
+               I915_WRITE(dspcntr_reg, temp | DISPLAY_PLANE_ENABLE);
+               /* Flush the plane changes */
+               I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
+       }
 
-               if (!HAS_eDP) {
-                       /* For PCH output, training FDI link */
-                       if (IS_GEN6(dev))
-                               gen6_fdi_link_train(crtc);
-                       else
-                               ironlake_fdi_link_train(crtc);
+       /* For PCH output, training FDI link */
+       if (IS_GEN6(dev))
+               gen6_fdi_link_train(crtc);
+       else
+               ironlake_fdi_link_train(crtc);
 
-                       /* 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);
-                       }
-                       udelay(200);
+       /* 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);
+               udelay(200);
+       }
 
-                       if (HAS_PCH_CPT(dev)) {
-                               /* Be sure PCH DPLL SEL is set */
-                               temp = I915_READ(PCH_DPLL_SEL);
-                               if (trans_dpll_sel == 0 &&
-                                               (temp & TRANSA_DPLL_ENABLE) == 0)
-                                       temp |= (TRANSA_DPLL_ENABLE | TRANSA_DPLLA_SEL);
-                               else if (trans_dpll_sel == 1 &&
-                                               (temp & TRANSB_DPLL_ENABLE) == 0)
-                                       temp |= (TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL);
-                               I915_WRITE(PCH_DPLL_SEL, temp);
-                               I915_READ(PCH_DPLL_SEL);
-                       }
+       if (HAS_PCH_CPT(dev)) {
+               /* Be sure PCH DPLL SEL is set */
+               temp = I915_READ(PCH_DPLL_SEL);
+               if (trans_dpll_sel == 0 &&
+                   (temp & TRANSA_DPLL_ENABLE) == 0)
+                       temp |= (TRANSA_DPLL_ENABLE | TRANSA_DPLLA_SEL);
+               else if (trans_dpll_sel == 1 &&
+                        (temp & TRANSB_DPLL_ENABLE) == 0)
+                       temp |= (TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL);
+               I915_WRITE(PCH_DPLL_SEL, temp);
+               I915_READ(PCH_DPLL_SEL);
+       }
+       /* 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 normal train */
+       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);
 
-                       /* enable normal train */
-                       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);
+       if (HAS_PCH_CPT(dev)) {
+               temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
+               temp |= FDI_LINK_TRAIN_NORMAL_CPT;
+       } else {
+               temp &= ~FDI_LINK_TRAIN_NONE;
+               temp |= FDI_LINK_TRAIN_NONE;
+       }
+       I915_WRITE(fdi_rx_reg, temp | FDI_RX_ENHANCE_FRAME_ENABLE);
+       I915_READ(fdi_rx_reg);
 
-                       temp = I915_READ(fdi_rx_reg);
-                       if (HAS_PCH_CPT(dev)) {
-                               temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
-                               temp |= FDI_LINK_TRAIN_NORMAL_CPT;
-                       } else {
-                               temp &= ~FDI_LINK_TRAIN_NONE;
-                               temp |= FDI_LINK_TRAIN_NONE;
-                       }
-                       I915_WRITE(fdi_rx_reg, temp | FDI_RX_ENHANCE_FRAME_ENABLE);
-                       I915_READ(fdi_rx_reg);
+       /* wait one idle pattern time */
+       udelay(100);
+
+       /* For PCH DP, enable TRANS_DP_CTL */
+       if (HAS_PCH_CPT(dev) &&
+           intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) {
+               int trans_dp_ctl = (pipe == 0) ? TRANS_DP_CTL_A : TRANS_DP_CTL_B;
+               int reg;
+
+               reg = I915_READ(trans_dp_ctl);
+               reg &= ~(TRANS_DP_PORT_SEL_MASK |
+                        TRANS_DP_SYNC_MASK);
+               reg |= (TRANS_DP_OUTPUT_ENABLE |
+                       TRANS_DP_ENH_FRAMING);
+
+               if (crtc->mode.flags & DRM_MODE_FLAG_PHSYNC)
+                       reg |= TRANS_DP_HSYNC_ACTIVE_HIGH;
+               if (crtc->mode.flags & DRM_MODE_FLAG_PVSYNC)
+                       reg |= TRANS_DP_VSYNC_ACTIVE_HIGH;
+
+               switch (intel_trans_dp_port_sel(crtc)) {
+               case PCH_DP_B:
+                       reg |= TRANS_DP_PORT_SEL_B;
+                       break;
+               case PCH_DP_C:
+                       reg |= TRANS_DP_PORT_SEL_C;
+                       break;
+               case PCH_DP_D:
+                       reg |= TRANS_DP_PORT_SEL_D;
+                       break;
+               default:
+                       DRM_DEBUG_KMS("Wrong PCH DP port return. Guess port B\n");
+                       reg |= TRANS_DP_PORT_SEL_B;
+                       break;
+               }
 
-                       /* wait one idle pattern time */
-                       udelay(100);
-
-                       /* For PCH DP, enable TRANS_DP_CTL */
-                       if (HAS_PCH_CPT(dev) &&
-                           intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) {
-                               int trans_dp_ctl = (pipe == 0) ? TRANS_DP_CTL_A : TRANS_DP_CTL_B;
-                               int reg;
-
-                               reg = I915_READ(trans_dp_ctl);
-                               reg &= ~(TRANS_DP_PORT_SEL_MASK |
-                                        TRANS_DP_SYNC_MASK);
-                               reg |= (TRANS_DP_OUTPUT_ENABLE |
-                                       TRANS_DP_ENH_FRAMING);
-
-                               if (crtc->mode.flags & DRM_MODE_FLAG_PHSYNC)
-                                     reg |= TRANS_DP_HSYNC_ACTIVE_HIGH;
-                               if (crtc->mode.flags & DRM_MODE_FLAG_PVSYNC)
-                                     reg |= TRANS_DP_VSYNC_ACTIVE_HIGH;
-
-                               switch (intel_trans_dp_port_sel(crtc)) {
-                               case PCH_DP_B:
-                                       reg |= TRANS_DP_PORT_SEL_B;
-                                       break;
-                               case PCH_DP_C:
-                                       reg |= TRANS_DP_PORT_SEL_C;
-                                       break;
-                               case PCH_DP_D:
-                                       reg |= TRANS_DP_PORT_SEL_D;
-                                       break;
-                               default:
-                                       DRM_DEBUG_KMS("Wrong PCH DP port return. Guess port B\n");
-                                       reg |= TRANS_DP_PORT_SEL_B;
-                                       break;
-                               }
+               I915_WRITE(trans_dp_ctl, reg);
+               POSTING_READ(trans_dp_ctl);
+       }
 
-                               I915_WRITE(trans_dp_ctl, reg);
-                               POSTING_READ(trans_dp_ctl);
-                       }
+       /* enable PCH transcoder */
+       temp = I915_READ(transconf_reg);
+       /*
+        * make the BPC in transcoder be consistent with
+        * that in pipeconf reg.
+        */
+       temp &= ~PIPE_BPC_MASK;
+       temp |= pipe_bpc;
+       I915_WRITE(transconf_reg, temp | TRANS_ENABLE);
+       I915_READ(transconf_reg);
 
-                       /* enable PCH transcoder */
-                       temp = I915_READ(transconf_reg);
-                       /*
-                        * make the BPC in transcoder be consistent with
-                        * that in pipeconf reg.
-                        */
-                       temp &= ~PIPE_BPC_MASK;
-                       temp |= pipe_bpc;
-                       I915_WRITE(transconf_reg, temp | TRANS_ENABLE);
-                       I915_READ(transconf_reg);
+       if (wait_for(I915_READ(transconf_reg) & TRANS_STATE_ENABLE, 100))
+               DRM_ERROR("failed to enable transcoder\n");
 
-                       if (wait_for(I915_READ(transconf_reg) & TRANS_STATE_ENABLE, 100, 1))
-                               DRM_ERROR("failed to enable transcoder\n");
-               }
+       intel_crtc_load_lut(crtc);
 
-               intel_crtc_load_lut(crtc);
+       intel_update_fbc(crtc, &crtc->mode);
+}
 
-               intel_update_fbc(crtc, &crtc->mode);
-               break;
+static void ironlake_crtc_disable(struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       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 pch_dpll_reg = (pipe == 0) ? PCH_DPLL_A : PCH_DPLL_B;
+       int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
+       int dspcntr_reg = (plane == 0) ? DSPACNTR : DSPBCNTR;
+       int dspbase_reg = (plane == 0) ? DSPAADDR : DSPBADDR;
+       int fdi_tx_reg = (pipe == 0) ? FDI_TXA_CTL : FDI_TXB_CTL;
+       int fdi_rx_reg = (pipe == 0) ? FDI_RXA_CTL : FDI_RXB_CTL;
+       int transconf_reg = (pipe == 0) ? TRANSACONF : TRANSBCONF;
+       int trans_dpll_sel = (pipe == 0) ? 0 : 1;
+       u32 temp;
+       u32 pipe_bpc;
 
-       case DRM_MODE_DPMS_OFF:
-               DRM_DEBUG_KMS("crtc %d/%d dpms off\n", pipe, plane);
+       temp = I915_READ(pipeconf_reg);
+       pipe_bpc = temp & PIPE_BPC_MASK;
 
-               drm_vblank_off(dev, pipe);
-               /* Disable display plane */
-               temp = I915_READ(dspcntr_reg);
-               if ((temp & DISPLAY_PLANE_ENABLE) != 0) {
-                       I915_WRITE(dspcntr_reg, temp & ~DISPLAY_PLANE_ENABLE);
-                       /* Flush the plane changes */
-                       I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
-                       I915_READ(dspbase_reg);
-               }
+       drm_vblank_off(dev, pipe);
+       /* Disable display plane */
+       temp = I915_READ(dspcntr_reg);
+       if ((temp & DISPLAY_PLANE_ENABLE) != 0) {
+               I915_WRITE(dspcntr_reg, temp & ~DISPLAY_PLANE_ENABLE);
+               /* Flush the plane changes */
+               I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
+               I915_READ(dspbase_reg);
+       }
 
-               if (dev_priv->cfb_plane == plane &&
-                   dev_priv->display.disable_fbc)
-                       dev_priv->display.disable_fbc(dev);
+       if (dev_priv->cfb_plane == plane &&
+           dev_priv->display.disable_fbc)
+               dev_priv->display.disable_fbc(dev);
 
-               /* disable cpu pipe, disable after all planes disabled */
-               temp = I915_READ(pipeconf_reg);
-               if ((temp & PIPEACONF_ENABLE) != 0) {
-                       I915_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE);
+       /* disable cpu pipe, disable after all planes disabled */
+       temp = I915_READ(pipeconf_reg);
+       if ((temp & PIPEACONF_ENABLE) != 0) {
+               I915_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE);
 
-                       /* wait for cpu pipe off, pipe state */
-                       if (wait_for((I915_READ(pipeconf_reg) & I965_PIPECONF_ACTIVE) == 0, 50, 1))
-                               DRM_ERROR("failed to turn off cpu pipe\n");
-               } else
-                       DRM_DEBUG_KMS("crtc %d is disabled\n", pipe);
+               /* wait for cpu pipe off, pipe state */
+               if (wait_for((I915_READ(pipeconf_reg) & I965_PIPECONF_ACTIVE) == 0, 50))
+                       DRM_ERROR("failed to turn off cpu pipe\n");
+       } else
+               DRM_DEBUG_KMS("crtc %d is disabled\n", pipe);
 
-               udelay(100);
+       /* Disable PF */
+       I915_WRITE(pipe ? PFB_CTL_1 : PFA_CTL_1, 0);
+       I915_WRITE(pipe ? PFB_WIN_SZ : PFA_WIN_SZ, 0);
 
-               /* Disable PF */
-               I915_WRITE(pipe ? PFB_CTL_1 : PFA_CTL_1, 0);
-               I915_WRITE(pipe ? PFB_WIN_SZ : PFA_WIN_SZ, 0);
+       /* disable CPU FDI tx and PCH FDI rx */
+       temp = I915_READ(fdi_tx_reg);
+       I915_WRITE(fdi_tx_reg, temp & ~FDI_TX_ENABLE);
+       I915_READ(fdi_tx_reg);
 
-               /* disable CPU FDI tx and PCH FDI rx */
-               temp = I915_READ(fdi_tx_reg);
-               I915_WRITE(fdi_tx_reg, temp & ~FDI_TX_ENABLE);
-               I915_READ(fdi_tx_reg);
+       temp = I915_READ(fdi_rx_reg);
+       /* BPC in FDI rx is consistent with that in pipeconf */
+       temp &= ~(0x07 << 16);
+       temp |= (pipe_bpc << 11);
+       I915_WRITE(fdi_rx_reg, temp & ~FDI_RX_ENABLE);
+       I915_READ(fdi_rx_reg);
 
-               temp = I915_READ(fdi_rx_reg);
-               /* BPC in FDI rx is consistent with that in pipeconf */
-               temp &= ~(0x07 << 16);
-               temp |= (pipe_bpc << 11);
-               I915_WRITE(fdi_rx_reg, temp & ~FDI_RX_ENABLE);
-               I915_READ(fdi_rx_reg);
+       udelay(100);
 
-               udelay(100);
+       /* still set train pattern 1 */
+       temp = I915_READ(fdi_tx_reg);
+       temp &= ~FDI_LINK_TRAIN_NONE;
+       temp |= FDI_LINK_TRAIN_PATTERN_1;
+       I915_WRITE(fdi_tx_reg, temp);
+       POSTING_READ(fdi_tx_reg);
 
-               /* still set train pattern 1 */
-               temp = I915_READ(fdi_tx_reg);
+       temp = I915_READ(fdi_rx_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;
-               I915_WRITE(fdi_tx_reg, temp);
-               POSTING_READ(fdi_tx_reg);
+       }
+       I915_WRITE(fdi_rx_reg, temp);
+       POSTING_READ(fdi_rx_reg);
 
-               temp = I915_READ(fdi_rx_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;
-               }
-               I915_WRITE(fdi_rx_reg, temp);
-               POSTING_READ(fdi_rx_reg);
+       udelay(100);
 
+       if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
+               temp = I915_READ(PCH_LVDS);
+               I915_WRITE(PCH_LVDS, temp & ~LVDS_PORT_EN);
+               I915_READ(PCH_LVDS);
                udelay(100);
+       }
 
-               if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
-                       temp = I915_READ(PCH_LVDS);
-                       I915_WRITE(PCH_LVDS, temp & ~LVDS_PORT_EN);
-                       I915_READ(PCH_LVDS);
-                       udelay(100);
-               }
+       /* disable PCH transcoder */
+       temp = I915_READ(transconf_reg);
+       if ((temp & TRANS_ENABLE) != 0) {
+               I915_WRITE(transconf_reg, temp & ~TRANS_ENABLE);
 
-               /* disable PCH transcoder */
-               temp = I915_READ(transconf_reg);
-               if ((temp & TRANS_ENABLE) != 0) {
-                       I915_WRITE(transconf_reg, temp & ~TRANS_ENABLE);
+               /* wait for PCH transcoder off, transcoder state */
+               if (wait_for((I915_READ(transconf_reg) & TRANS_STATE_ENABLE) == 0, 50))
+                       DRM_ERROR("failed to disable transcoder\n");
+       }
 
-                       /* wait for PCH transcoder off, transcoder state */
-                       if (wait_for((I915_READ(transconf_reg) & TRANS_STATE_ENABLE) == 0, 50, 1))
-                               DRM_ERROR("failed to disable transcoder\n");
-               }
+       temp = I915_READ(transconf_reg);
+       /* BPC in transcoder is consistent with that in pipeconf */
+       temp &= ~PIPE_BPC_MASK;
+       temp |= pipe_bpc;
+       I915_WRITE(transconf_reg, temp);
+       I915_READ(transconf_reg);
+       udelay(100);
 
-               temp = I915_READ(transconf_reg);
-               /* BPC in transcoder is consistent with that in pipeconf */
-               temp &= ~PIPE_BPC_MASK;
-               temp |= pipe_bpc;
-               I915_WRITE(transconf_reg, temp);
-               I915_READ(transconf_reg);
-               udelay(100);
+       if (HAS_PCH_CPT(dev)) {
+               /* disable TRANS_DP_CTL */
+               int trans_dp_ctl = (pipe == 0) ? TRANS_DP_CTL_A : TRANS_DP_CTL_B;
+               int reg;
 
-               if (HAS_PCH_CPT(dev)) {
-                       /* disable TRANS_DP_CTL */
-                       int trans_dp_ctl = (pipe == 0) ? TRANS_DP_CTL_A : TRANS_DP_CTL_B;
-                       int reg;
+               reg = I915_READ(trans_dp_ctl);
+               reg &= ~(TRANS_DP_OUTPUT_ENABLE | TRANS_DP_PORT_SEL_MASK);
+               I915_WRITE(trans_dp_ctl, reg);
+               POSTING_READ(trans_dp_ctl);
 
-                       reg = I915_READ(trans_dp_ctl);
-                       reg &= ~(TRANS_DP_OUTPUT_ENABLE | TRANS_DP_PORT_SEL_MASK);
-                       I915_WRITE(trans_dp_ctl, reg);
-                       POSTING_READ(trans_dp_ctl);
+               /* disable DPLL_SEL */
+               temp = I915_READ(PCH_DPLL_SEL);
+               if (trans_dpll_sel == 0)
+                       temp &= ~(TRANSA_DPLL_ENABLE | TRANSA_DPLLB_SEL);
+               else
+                       temp &= ~(TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL);
+               I915_WRITE(PCH_DPLL_SEL, temp);
+               I915_READ(PCH_DPLL_SEL);
 
-                       /* disable DPLL_SEL */
-                       temp = I915_READ(PCH_DPLL_SEL);
-                       if (trans_dpll_sel == 0)
-                               temp &= ~(TRANSA_DPLL_ENABLE | TRANSA_DPLLB_SEL);
-                       else
-                               temp &= ~(TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL);
-                       I915_WRITE(PCH_DPLL_SEL, temp);
-                       I915_READ(PCH_DPLL_SEL);
+       }
 
-               }
+       /* disable PCH DPLL */
+       temp = I915_READ(pch_dpll_reg);
+       I915_WRITE(pch_dpll_reg, temp & ~DPLL_VCO_ENABLE);
+       I915_READ(pch_dpll_reg);
 
-               /* disable PCH DPLL */
-               temp = I915_READ(pch_dpll_reg);
-               I915_WRITE(pch_dpll_reg, temp & ~DPLL_VCO_ENABLE);
-               I915_READ(pch_dpll_reg);
+       /* Switch from PCDclk to Rawclk */
+       temp = I915_READ(fdi_rx_reg);
+       temp &= ~FDI_SEL_PCDCLK;
+       I915_WRITE(fdi_rx_reg, temp);
+       I915_READ(fdi_rx_reg);
 
-               /* Switch from PCDclk to Rawclk */
-               temp = I915_READ(fdi_rx_reg);
-               temp &= ~FDI_SEL_PCDCLK;
-               I915_WRITE(fdi_rx_reg, temp);
-               I915_READ(fdi_rx_reg);
+       /* Disable CPU FDI TX PLL */
+       temp = I915_READ(fdi_tx_reg);
+       I915_WRITE(fdi_tx_reg, temp & ~FDI_TX_PLL_ENABLE);
+       I915_READ(fdi_tx_reg);
+       udelay(100);
 
-               /* Disable CPU FDI TX PLL */
-               temp = I915_READ(fdi_tx_reg);
-               I915_WRITE(fdi_tx_reg, temp & ~FDI_TX_PLL_ENABLE);
-               I915_READ(fdi_tx_reg);
-               udelay(100);
+       temp = I915_READ(fdi_rx_reg);
+       temp &= ~FDI_RX_PLL_ENABLE;
+       I915_WRITE(fdi_rx_reg, temp);
+       I915_READ(fdi_rx_reg);
 
-               temp = I915_READ(fdi_rx_reg);
-               temp &= ~FDI_RX_PLL_ENABLE;
-               I915_WRITE(fdi_rx_reg, temp);
-               I915_READ(fdi_rx_reg);
+       /* Wait for the clocks to turn off. */
+       udelay(100);
+}
 
-               /* Wait for the clocks to turn off. */
-               udelay(100);
+static void ironlake_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       int pipe = intel_crtc->pipe;
+       int plane = intel_crtc->plane;
+
+       /* 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.
+        */
+       switch (mode) {
+       case DRM_MODE_DPMS_ON:
+       case DRM_MODE_DPMS_STANDBY:
+       case DRM_MODE_DPMS_SUSPEND:
+               DRM_DEBUG_KMS("crtc %d/%d dpms on\n", pipe, plane);
+               ironlake_crtc_enable(crtc);
+               break;
+
+       case DRM_MODE_DPMS_OFF:
+               DRM_DEBUG_KMS("crtc %d/%d dpms off\n", pipe, plane);
+               ironlake_crtc_disable(crtc);
                break;
        }
 }
 
 static void intel_crtc_dpms_overlay(struct intel_crtc *intel_crtc, bool enable)
 {
-       struct intel_overlay *overlay;
-       int ret;
-
        if (!enable && intel_crtc->overlay) {
-               overlay = intel_crtc->overlay;
-               mutex_lock(&overlay->dev->struct_mutex);
-               for (;;) {
-                       ret = intel_overlay_switch_off(overlay);
-                       if (ret == 0)
-                               break;
+               struct drm_device *dev = intel_crtc->base.dev;
 
-                       ret = intel_overlay_recover_from_interrupt(overlay, 0);
-                       if (ret != 0) {
-                               /* overlay doesn't react anymore. Usually
-                                * results in a black screen and an unkillable
-                                * X server. */
-                               BUG();
-                               overlay->hw_wedged = HW_WEDGED;
-                               break;
-                       }
-               }
-               mutex_unlock(&overlay->dev->struct_mutex);
+               mutex_lock(&dev->struct_mutex);
+               (void) intel_overlay_switch_off(intel_crtc->overlay, false);
+               mutex_unlock(&dev->struct_mutex);
        }
-       /* Let userspace switch the overlay on again. In most cases userspace
-        * has to recompute where to put it anyway. */
 
-       return;
+       /* Let userspace switch the overlay on again. In most cases userspace
+        * has to recompute where to put it anyway.
+        */
 }
 
-static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode)
+static void i9xx_crtc_enable(struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -2265,99 +2305,142 @@ static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode)
        int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
        u32 temp;
 
-       /* 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.
-        */
-       switch (mode) {
-       case DRM_MODE_DPMS_ON:
-       case DRM_MODE_DPMS_STANDBY:
-       case DRM_MODE_DPMS_SUSPEND:
-               /* Enable the DPLL */
-               temp = I915_READ(dpll_reg);
-               if ((temp & DPLL_VCO_ENABLE) == 0) {
-                       I915_WRITE(dpll_reg, temp);
-                       I915_READ(dpll_reg);
-                       /* Wait for the clocks to stabilize. */
-                       udelay(150);
-                       I915_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE);
-                       I915_READ(dpll_reg);
-                       /* Wait for the clocks to stabilize. */
-                       udelay(150);
-                       I915_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE);
-                       I915_READ(dpll_reg);
-                       /* Wait for the clocks to stabilize. */
-                       udelay(150);
-               }
+       /* Enable the DPLL */
+       temp = I915_READ(dpll_reg);
+       if ((temp & DPLL_VCO_ENABLE) == 0) {
+               I915_WRITE(dpll_reg, temp);
+               I915_READ(dpll_reg);
+               /* Wait for the clocks to stabilize. */
+               udelay(150);
+               I915_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE);
+               I915_READ(dpll_reg);
+               /* Wait for the clocks to stabilize. */
+               udelay(150);
+               I915_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE);
+               I915_READ(dpll_reg);
+               /* Wait for the clocks to stabilize. */
+               udelay(150);
+       }
 
-               /* Enable the pipe */
-               temp = I915_READ(pipeconf_reg);
-               if ((temp & PIPEACONF_ENABLE) == 0)
-                       I915_WRITE(pipeconf_reg, temp | PIPEACONF_ENABLE);
-
-               /* Enable the plane */
-               temp = I915_READ(dspcntr_reg);
-               if ((temp & DISPLAY_PLANE_ENABLE) == 0) {
-                       I915_WRITE(dspcntr_reg, temp | DISPLAY_PLANE_ENABLE);
-                       /* Flush the plane changes */
-                       I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
-               }
+       /* Enable the pipe */
+       temp = I915_READ(pipeconf_reg);
+       if ((temp & PIPEACONF_ENABLE) == 0)
+               I915_WRITE(pipeconf_reg, temp | PIPEACONF_ENABLE);
 
-               intel_crtc_load_lut(crtc);
+       /* Enable the plane */
+       temp = I915_READ(dspcntr_reg);
+       if ((temp & DISPLAY_PLANE_ENABLE) == 0) {
+               I915_WRITE(dspcntr_reg, temp | DISPLAY_PLANE_ENABLE);
+               /* Flush the plane changes */
+               I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
+       }
 
-               if ((IS_I965G(dev) || plane == 0))
-                       intel_update_fbc(crtc, &crtc->mode);
+       intel_crtc_load_lut(crtc);
 
-               /* Give the overlay scaler a chance to enable if it's on this pipe */
-               intel_crtc_dpms_overlay(intel_crtc, true);
-       break;
-       case DRM_MODE_DPMS_OFF:
-               /* Give the overlay scaler a chance to disable if it's on this pipe */
-               intel_crtc_dpms_overlay(intel_crtc, false);
-               drm_vblank_off(dev, pipe);
-
-               if (dev_priv->cfb_plane == plane &&
-                   dev_priv->display.disable_fbc)
-                       dev_priv->display.disable_fbc(dev);
-
-               /* Disable display plane */
-               temp = I915_READ(dspcntr_reg);
-               if ((temp & DISPLAY_PLANE_ENABLE) != 0) {
-                       I915_WRITE(dspcntr_reg, temp & ~DISPLAY_PLANE_ENABLE);
-                       /* Flush the plane changes */
-                       I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
-                       I915_READ(dspbase_reg);
-               }
+       if ((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_overlay(intel_crtc, true);
+}
 
+static void i9xx_crtc_disable(struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       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 = (plane == 0) ? DSPACNTR : DSPBCNTR;
+       int dspbase_reg = (plane == 0) ? DSPAADDR : DSPBADDR;
+       int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
+       u32 temp;
+
+       /* Give the overlay scaler a chance to disable if it's on this pipe */
+       intel_crtc_dpms_overlay(intel_crtc, false);
+       drm_vblank_off(dev, pipe);
+
+       if (dev_priv->cfb_plane == plane &&
+           dev_priv->display.disable_fbc)
+               dev_priv->display.disable_fbc(dev);
+
+       /* Disable display plane */
+       temp = I915_READ(dspcntr_reg);
+       if ((temp & DISPLAY_PLANE_ENABLE) != 0) {
+               I915_WRITE(dspcntr_reg, temp & ~DISPLAY_PLANE_ENABLE);
+               /* Flush the plane changes */
+               I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
+               I915_READ(dspbase_reg);
+       }
+
+       if (!IS_I9XX(dev)) {
                /* Wait for vblank for the disable to take effect */
                intel_wait_for_vblank_off(dev, pipe);
+       }
 
-               /* Don't disable pipe A or pipe A PLLs if needed */
-               if (pipeconf_reg == PIPEACONF &&
-                   (dev_priv->quirks & QUIRK_PIPEA_FORCE))
-                       goto skip_pipe_off;
+       /* Don't disable pipe A or pipe A PLLs if needed */
+       if (pipeconf_reg == PIPEACONF &&
+           (dev_priv->quirks & QUIRK_PIPEA_FORCE))
+               goto skip_pipe_off;
 
-               /* Next, disable display pipes */
-               temp = I915_READ(pipeconf_reg);
-               if ((temp & PIPEACONF_ENABLE) != 0) {
-                       I915_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE);
-                       I915_READ(pipeconf_reg);
-               }
+       /* Next, disable display pipes */
+       temp = I915_READ(pipeconf_reg);
+       if ((temp & PIPEACONF_ENABLE) != 0) {
+               I915_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE);
+               I915_READ(pipeconf_reg);
+       }
 
-               /* Wait for vblank for the disable to take effect. */
-               intel_wait_for_vblank_off(dev, pipe);
+       /* Wait for vblank for the disable to take effect. */
+       intel_wait_for_vblank_off(dev, pipe);
 
-               temp = I915_READ(dpll_reg);
-               if ((temp & DPLL_VCO_ENABLE) != 0) {
-                       I915_WRITE(dpll_reg, temp & ~DPLL_VCO_ENABLE);
-                       I915_READ(dpll_reg);
-               }
-       skip_pipe_off:
-               /* Wait for the clocks to turn off. */
-               udelay(150);
+       temp = I915_READ(dpll_reg);
+       if ((temp & DPLL_VCO_ENABLE) != 0) {
+               I915_WRITE(dpll_reg, temp & ~DPLL_VCO_ENABLE);
+               I915_READ(dpll_reg);
+       }
+skip_pipe_off:
+       /* Wait for the clocks to turn off. */
+       udelay(150);
+}
+
+static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+       /* 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.
+        */
+       switch (mode) {
+       case DRM_MODE_DPMS_ON:
+       case DRM_MODE_DPMS_STANDBY:
+       case DRM_MODE_DPMS_SUSPEND:
+               i9xx_crtc_enable(crtc);
+               break;
+       case DRM_MODE_DPMS_OFF:
+               i9xx_crtc_disable(crtc);
                break;
        }
 }
 
+/*
+ * When we disable a pipe, we need to clear any pending scanline wait events
+ * to avoid hanging the ring, which we assume we are waiting on.
+ */
+static void intel_clear_scanline_wait(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       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);
+       }
+}
+
 /**
  * Sets the power management mode of the pipe and plane.
  */
@@ -2380,7 +2463,8 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
         * with multiple pipes prior to enabling to new pipe.
         *
         * When switching off the display, make sure the cursor is
-        * properly hidden prior to disabling the pipe.
+        * properly hidden and there are no pending waits prior to
+        * disabling the pipe.
         */
        if (mode == DRM_MODE_DPMS_ON)
                intel_update_watermarks(dev);
@@ -2391,8 +2475,14 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
 
        if (mode == DRM_MODE_DPMS_ON)
                intel_crtc_update_cursor(crtc);
-       else
+       else {
+               /* XXX Note that this is not a complete solution, but a hack
+                * to avoid the most frequently hit hang.
+                */
+               intel_clear_scanline_wait(dev);
+
                intel_update_watermarks(dev);
+       }
 
        if (!dev->primary->master)
                return;
@@ -2418,16 +2508,60 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
        }
 }
 
-static void intel_crtc_prepare (struct drm_crtc *crtc)
+/* Prepare for a mode set.
+ *
+ * Note we could be a lot smarter here.  We need to figure out which outputs
+ * will be enabled, which disabled (in short, how the config will changes)
+ * and perform the minimum necessary steps to accomplish that, e.g. updating
+ * watermarks, FBC configuration, making sure PLLs are programmed correctly,
+ * panel fitting is in the proper state, etc.
+ */
+static void i9xx_crtc_prepare(struct drm_crtc *crtc)
 {
-       struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
-       crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
+       struct drm_device *dev = crtc->dev;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+       intel_crtc->cursor_on = false;
+       intel_crtc_update_cursor(crtc);
+
+       i9xx_crtc_disable(crtc);
+       intel_clear_scanline_wait(dev);
 }
 
-static void intel_crtc_commit (struct drm_crtc *crtc)
+static void i9xx_crtc_commit(struct drm_crtc *crtc)
 {
-       struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
-       crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
+       struct drm_device *dev = crtc->dev;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+       intel_update_watermarks(dev);
+       i9xx_crtc_enable(crtc);
+
+       intel_crtc->cursor_on = true;
+       intel_crtc_update_cursor(crtc);
+}
+
+static void ironlake_crtc_prepare(struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+       intel_crtc->cursor_on = false;
+       intel_crtc_update_cursor(crtc);
+
+       ironlake_crtc_disable(crtc);
+       intel_clear_scanline_wait(dev);
+}
+
+static void ironlake_crtc_commit(struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+
+       intel_update_watermarks(dev);
+       ironlake_crtc_enable(crtc);
+
+       intel_crtc->cursor_on = true;
+       intel_crtc_update_cursor(crtc);
 }
 
 void intel_encoder_prepare (struct drm_encoder *encoder)
@@ -2446,7 +2580,7 @@ void intel_encoder_commit (struct drm_encoder *encoder)
 
 void intel_encoder_destroy(struct drm_encoder *encoder)
 {
-       struct intel_encoder *intel_encoder = enc_to_intel_encoder(encoder);
+       struct intel_encoder *intel_encoder = to_intel_encoder(encoder);
 
        if (intel_encoder->ddc_bus)
                intel_i2c_destroy(intel_encoder->ddc_bus);
@@ -3516,7 +3650,6 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
        int trans_dpll_sel = (pipe == 0) ? 0 : 1;
        int lvds_reg = LVDS;
        u32 temp;
-       int sdvo_pixel_multiply;
        int target_clock;
 
        drm_vblank_pre_modeset(dev, pipe);
@@ -3527,7 +3660,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                if (encoder->crtc != crtc)
                        continue;
 
-               intel_encoder = enc_to_intel_encoder(encoder);
+               intel_encoder = to_intel_encoder(encoder);
                switch (intel_encoder->type) {
                case INTEL_OUTPUT_LVDS:
                        is_lvds = true;
@@ -3640,7 +3773,15 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                                target_clock = mode->clock;
                        else
                                target_clock = adjusted_mode->clock;
-                       link_bw = 270000;
+
+                       /* FDI is a binary signal running at ~2.7GHz, encoding
+                        * each output octet as 10 bits. The actual frequency
+                        * is stored as a divider into a 100MHz clock, and the
+                        * mode pixel clock is stored in units of 1KHz.
+                        * Hence the bw of each lane in terms of the mode signal
+                        * is:
+                        */
+                       link_bw = intel_fdi_link_freq(dev) * MHz(100)/KHz(1)/10;
                }
 
                /* determine panel color depth */
@@ -3767,12 +3908,14 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                else
                        dpll |= DPLLB_MODE_DAC_SERIAL;
                if (is_sdvo) {
+                       int pixel_multiplier = intel_mode_get_pixel_multiplier(adjusted_mode);
+                       if (pixel_multiplier > 1) {
+                               if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev))
+                                       dpll |= (pixel_multiplier - 1) << SDVO_MULTIPLIER_SHIFT_HIRES;
+                               else if (HAS_PCH_SPLIT(dev))
+                                       dpll |= (pixel_multiplier - 1) << PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT;
+                       }
                        dpll |= DPLL_DVO_HIGH_SPEED;
-                       sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
-                       if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev))
-                               dpll |= (sdvo_pixel_multiply - 1) << SDVO_MULTIPLIER_SHIFT_HIRES;
-                       else if (HAS_PCH_SPLIT(dev))
-                               dpll |= (sdvo_pixel_multiply - 1) << PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT;
                }
                if (is_dp)
                        dpll |= DPLL_DVO_HIGH_SPEED;
@@ -3894,11 +4037,6 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                udelay(150);
        }
 
-       if (HAS_PCH_SPLIT(dev)) {
-               pipeconf &= ~PIPE_ENABLE_DITHER;
-               pipeconf &= ~PIPE_DITHER_TYPE_MASK;
-       }
-
        /* 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.
@@ -3936,23 +4074,27 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
                 * appropriately here, but we need to look more thoroughly into how
                 * panels behave in the two modes.
                 */
-               /* set the dithering flag */
-               if (IS_I965G(dev)) {
-                       if (dev_priv->lvds_dither) {
-                               if (HAS_PCH_SPLIT(dev)) {
-                                       pipeconf |= PIPE_ENABLE_DITHER;
-                                       pipeconf |= PIPE_DITHER_TYPE_ST01;
-                               } else
-                                       lvds |= LVDS_ENABLE_DITHER;
-                       } else {
-                               if (!HAS_PCH_SPLIT(dev)) {
-                                       lvds &= ~LVDS_ENABLE_DITHER;
-                               }
-                       }
+               /* set the dithering flag on non-PCH LVDS as needed */
+               if (IS_I965G(dev) && !HAS_PCH_SPLIT(dev)) {
+                       if (dev_priv->lvds_dither)
+                               lvds |= LVDS_ENABLE_DITHER;
+                       else
+                               lvds &= ~LVDS_ENABLE_DITHER;
                }
                I915_WRITE(lvds_reg, lvds);
                I915_READ(lvds_reg);
        }
+
+       /* set the dithering flag and clear for anything other than a panel. */
+       if (HAS_PCH_SPLIT(dev)) {
+               pipeconf &= ~PIPECONF_DITHER_EN;
+               pipeconf &= ~PIPECONF_DITHER_TYPE_MASK;
+               if (dev_priv->lvds_dither && (is_lvds || has_edp_encoder)) {
+                       pipeconf |= PIPECONF_DITHER_EN;
+                       pipeconf |= PIPECONF_DITHER_TYPE_ST1;
+               }
+       }
+
        if (is_dp)
                intel_dp_set_m_n(crtc, mode, adjusted_mode);
        else if (HAS_PCH_SPLIT(dev)) {
@@ -3979,9 +4121,15 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
 
                if (IS_I965G(dev) && !HAS_PCH_SPLIT(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));
+                               int pixel_multiplier = intel_mode_get_pixel_multiplier(adjusted_mode);
+                               if (pixel_multiplier > 1)
+                                       pixel_multiplier = (pixel_multiplier - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT;
+                               else
+                                       pixel_multiplier = 0;
+
+                               I915_WRITE(dpll_md_reg,
+                                          (0 << DPLL_MD_UDI_DIVIDER_SHIFT) |
+                                          pixel_multiplier);
                        } else
                                I915_WRITE(dpll_md_reg, 0);
                } else {
@@ -4045,7 +4193,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
 
        if (HAS_PCH_SPLIT(dev)) {
                I915_WRITE(data_m1_reg, TU_SIZE(m_n.tu) | m_n.gmch_m);
-               I915_WRITE(data_n1_reg, TU_SIZE(m_n.tu) | m_n.gmch_n);
+               I915_WRITE(data_n1_reg, m_n.gmch_n);
                I915_WRITE(link_m1_reg, m_n.link_m);
                I915_WRITE(link_n1_reg, m_n.link_n);
 
@@ -4410,7 +4558,7 @@ struct drm_crtc *intel_get_load_detect_pipe(struct intel_encoder *intel_encoder,
        struct intel_crtc *intel_crtc;
        struct drm_crtc *possible_crtc;
        struct drm_crtc *supported_crtc =NULL;
-       struct drm_encoder *encoder = &intel_encoder->enc;
+       struct drm_encoder *encoder = &intel_encoder->base;
        struct drm_crtc *crtc = NULL;
        struct drm_device *dev = encoder->dev;
        struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
@@ -4491,7 +4639,7 @@ struct drm_crtc *intel_get_load_detect_pipe(struct intel_encoder *intel_encoder,
 void intel_release_load_detect_pipe(struct intel_encoder *intel_encoder,
                                    struct drm_connector *connector, int dpms_mode)
 {
-       struct drm_encoder *encoder = &intel_encoder->enc;
+       struct drm_encoder *encoder = &intel_encoder->base;
        struct drm_device *dev = encoder->dev;
        struct drm_crtc *crtc = encoder->crtc;
        struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
@@ -4663,7 +4811,7 @@ static void intel_crtc_idle_timer(unsigned long arg)
        queue_work(dev_priv->wq, &dev_priv->idle_work);
 }
 
-static void intel_increase_pllclock(struct drm_crtc *crtc, bool schedule)
+static void intel_increase_pllclock(struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
        drm_i915_private_t *dev_priv = dev->dev_private;
@@ -4698,9 +4846,8 @@ static void intel_increase_pllclock(struct drm_crtc *crtc, bool schedule)
        }
 
        /* Schedule downclock */
-       if (schedule)
-               mod_timer(&intel_crtc->idle_timer, jiffies +
-                         msecs_to_jiffies(CRTC_IDLE_TIMEOUT));
+       mod_timer(&intel_crtc->idle_timer, jiffies +
+                 msecs_to_jiffies(CRTC_IDLE_TIMEOUT));
 }
 
 static void intel_decrease_pllclock(struct drm_crtc *crtc)
@@ -4836,7 +4983,7 @@ void intel_mark_busy(struct drm_device *dev, struct drm_gem_object *obj)
                                        I915_WRITE(FW_BLC_SELF, fw_blc_self | FW_BLC_SELF_EN_MASK);
                                }
                                /* Non-busy -> busy, upclock */
-                               intel_increase_pllclock(crtc, true);
+                               intel_increase_pllclock(crtc);
                                intel_crtc->busy = true;
                        } else {
                                /* Busy -> busy, put off timer */
@@ -4850,8 +4997,22 @@ void intel_mark_busy(struct drm_device *dev, struct drm_gem_object *obj)
 static void intel_crtc_destroy(struct drm_crtc *crtc)
 {
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       struct drm_device *dev = crtc->dev;
+       struct intel_unpin_work *work;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->event_lock, flags);
+       work = intel_crtc->unpin_work;
+       intel_crtc->unpin_work = NULL;
+       spin_unlock_irqrestore(&dev->event_lock, flags);
+
+       if (work) {
+               cancel_work_sync(&work->work);
+               kfree(work);
+       }
 
        drm_crtc_cleanup(crtc);
+
        kfree(intel_crtc);
 }
 
@@ -5104,14 +5265,12 @@ cleanup_work:
        return ret;
 }
 
-static const struct drm_crtc_helper_funcs intel_helper_funcs = {
+static struct drm_crtc_helper_funcs intel_helper_funcs = {
        .dpms = intel_crtc_dpms,
        .mode_fixup = intel_crtc_mode_fixup,
        .mode_set = intel_crtc_mode_set,
        .mode_set_base = intel_pipe_set_base,
        .mode_set_base_atomic = intel_pipe_set_base_atomic,
-       .prepare = intel_crtc_prepare,
-       .commit = intel_crtc_commit,
        .load_lut = intel_crtc_load_lut,
 };
 
@@ -5161,6 +5320,15 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
 
        intel_crtc->cursor_addr = 0;
        intel_crtc->dpms_mode = -1;
+
+       if (HAS_PCH_SPLIT(dev)) {
+               intel_helper_funcs.prepare = ironlake_crtc_prepare;
+               intel_helper_funcs.commit = ironlake_crtc_commit;
+       } else {
+               intel_helper_funcs.prepare = i9xx_crtc_prepare;
+               intel_helper_funcs.commit = i9xx_crtc_commit;
+       }
+
        drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs);
 
        intel_crtc->busy = false;
@@ -5196,38 +5364,25 @@ int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data,
        return 0;
 }
 
-struct drm_crtc *intel_get_crtc_from_pipe(struct drm_device *dev, int pipe)
-{
-       struct drm_crtc *crtc = NULL;
-
-       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
-               struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-               if (intel_crtc->pipe == pipe)
-                       break;
-       }
-       return crtc;
-}
-
 static int intel_encoder_clones(struct drm_device *dev, int type_mask)
 {
+       struct intel_encoder *encoder;
        int index_mask = 0;
-       struct drm_encoder *encoder;
        int entry = 0;
 
-        list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
-               struct intel_encoder *intel_encoder = enc_to_intel_encoder(encoder);
-               if (type_mask & intel_encoder->clone_mask)
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) {
+               if (type_mask & encoder->clone_mask)
                        index_mask |= (1 << entry);
                entry++;
        }
+
        return index_mask;
 }
 
-
 static void intel_setup_outputs(struct drm_device *dev)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct drm_encoder *encoder;
+       struct intel_encoder *encoder;
        bool dpd_is_edp = false;
 
        if (IS_MOBILE(dev) && !IS_I830(dev))
@@ -5316,12 +5471,10 @@ static void intel_setup_outputs(struct drm_device *dev)
        if (SUPPORTS_TV(dev))
                intel_tv_init(dev);
 
-       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
-               struct intel_encoder *intel_encoder = enc_to_intel_encoder(encoder);
-
-               encoder->possible_crtcs = intel_encoder->crtc_mask;
-               encoder->possible_clones = intel_encoder_clones(dev,
-                                               intel_encoder->clone_mask);
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) {
+               encoder->base.possible_crtcs = encoder->crtc_mask;
+               encoder->base.possible_clones =
+                       intel_encoder_clones(dev, encoder->clone_mask);
        }
 }
 
@@ -5355,8 +5508,25 @@ int intel_framebuffer_init(struct drm_device *dev,
                           struct drm_mode_fb_cmd *mode_cmd,
                           struct drm_gem_object *obj)
 {
+       struct drm_i915_gem_object *obj_priv = to_intel_bo(obj);
        int ret;
 
+       if (obj_priv->tiling_mode == I915_TILING_Y)
+               return -EINVAL;
+
+       if (mode_cmd->pitch & 63)
+               return -EINVAL;
+
+       switch (mode_cmd->bpp) {
+       case 8:
+       case 16:
+       case 24:
+       case 32:
+               break;
+       default:
+               return -EINVAL;
+       }
+
        ret = drm_framebuffer_init(dev, &intel_fb->base, &intel_fb_funcs);
        if (ret) {
                DRM_ERROR("framebuffer init failed %d\n", ret);
@@ -5507,7 +5677,7 @@ void ironlake_enable_drps(struct drm_device *dev)
        rgvmodectl |= MEMMODE_SWMODE_EN;
        I915_WRITE(MEMMODECTL, rgvmodectl);
 
-       if (wait_for((I915_READ(MEMSWCTL) & MEMCTL_CMD_STS) == 0, 10))
+       if (wait_for((I915_READ(MEMSWCTL) & MEMCTL_CMD_STS) == 0, 10))
                DRM_ERROR("stuck trying to change perf mode\n");
        msleep(1);
 
@@ -6041,12 +6211,9 @@ void intel_modeset_cleanup(struct drm_device *dev)
                        continue;
 
                intel_crtc = to_intel_crtc(crtc);
-               intel_increase_pllclock(crtc, false);
-               del_timer_sync(&intel_crtc->idle_timer);
+               intel_increase_pllclock(crtc);
        }
 
-       del_timer_sync(&dev_priv->idle_timer);
-
        if (dev_priv->display.disable_fbc)
                dev_priv->display.disable_fbc(dev);
 
@@ -6075,33 +6242,36 @@ void intel_modeset_cleanup(struct drm_device *dev)
 
        mutex_unlock(&dev->struct_mutex);
 
+       /* Disable the irq before mode object teardown, for the irq might
+        * enqueue unpin/hotplug work. */
+       drm_irq_uninstall(dev);
+       cancel_work_sync(&dev_priv->hotplug_work);
+
+       /* Shut off idle work before the crtcs get freed. */
+       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+               intel_crtc = to_intel_crtc(crtc);
+               del_timer_sync(&intel_crtc->idle_timer);
+       }
+       del_timer_sync(&dev_priv->idle_timer);
+       cancel_work_sync(&dev_priv->idle_work);
+
        drm_mode_config_cleanup(dev);
 }
 
-
 /*
  * Return which encoder is currently attached for connector.
  */
-struct drm_encoder *intel_attached_encoder (struct drm_connector *connector)
+struct drm_encoder *intel_best_encoder(struct drm_connector *connector)
 {
-       struct drm_mode_object *obj;
-       struct drm_encoder *encoder;
-       int i;
-
-       for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
-               if (connector->encoder_ids[i] == 0)
-                       break;
-
-               obj = drm_mode_object_find(connector->dev,
-                                           connector->encoder_ids[i],
-                                           DRM_MODE_OBJECT_ENCODER);
-               if (!obj)
-                       continue;
+       return &intel_attached_encoder(connector)->base;
+}
 
-               encoder = obj_to_encoder(obj);
-               return encoder;
-       }
-       return NULL;
+void intel_connector_attach_encoder(struct intel_connector *connector,
+                                   struct intel_encoder *encoder)
+{
+       connector->encoder = encoder;
+       drm_mode_connector_attach_encoder(&connector->base,
+                                         &encoder->base);
 }
 
 /*