]> Pileus Git - ~andy/linux/blobdiff - drivers/gpu/drm/i915/intel_display.c
drm/i915: Add interfaces to read out encoder/connector hw state
[~andy/linux] / drivers / gpu / drm / i915 / intel_display.c
index f6159765f1ebe8cf8f6844c092deddd37bd44040..7e7569b68039790f83ca2b0d05037f5dbb278373 100644 (file)
@@ -869,6 +869,7 @@ intel_vlv_find_best_pll(const intel_limit_t *limit, struct drm_crtc *crtc,
        unsigned long bestppm, ppm, absppm;
        int dotclk, flag;
 
+       flag = 0;
        dotclk = target * 1000;
        bestppm = 1000000;
        ppm = absppm = 0;
@@ -1383,7 +1384,7 @@ static void assert_pch_hdmi_disabled(struct drm_i915_private *dev_priv,
                                     enum pipe pipe, int reg)
 {
        u32 val = I915_READ(reg);
-       WARN(hdmi_pipe_enabled(dev_priv, val, pipe),
+       WARN(hdmi_pipe_enabled(dev_priv, pipe, val),
             "PCH HDMI (0x%08x) enabled on transcoder %c, should be disabled\n",
             reg, pipe_name(pipe));
 
@@ -1403,13 +1404,13 @@ static void assert_pch_ports_disabled(struct drm_i915_private *dev_priv,
 
        reg = PCH_ADPA;
        val = I915_READ(reg);
-       WARN(adpa_pipe_enabled(dev_priv, val, pipe),
+       WARN(adpa_pipe_enabled(dev_priv, pipe, val),
             "PCH VGA enabled on transcoder %c, should be disabled\n",
             pipe_name(pipe));
 
        reg = PCH_LVDS;
        val = I915_READ(reg);
-       WARN(lvds_pipe_enabled(dev_priv, val, pipe),
+       WARN(lvds_pipe_enabled(dev_priv, pipe, val),
             "PCH LVDS enabled on transcoder %c, should be disabled\n",
             pipe_name(pipe));
 
@@ -1428,6 +1429,8 @@ static void assert_pch_ports_disabled(struct drm_i915_private *dev_priv,
  * protect mechanism may be enabled.
  *
  * Note!  This is for pre-ILK only.
+ *
+ * Unfortunately needed by dvo_ns2501 since the dvo depends on it running.
  */
 static void intel_enable_pll(struct drm_i915_private *dev_priv, enum pipe pipe)
 {
@@ -1871,7 +1874,7 @@ static void disable_pch_hdmi(struct drm_i915_private *dev_priv,
                             enum pipe pipe, int reg)
 {
        u32 val = I915_READ(reg);
-       if (hdmi_pipe_enabled(dev_priv, val, pipe)) {
+       if (hdmi_pipe_enabled(dev_priv, pipe, val)) {
                DRM_DEBUG_KMS("Disabling pch HDMI %x on pipe %d\n",
                              reg, pipe);
                I915_WRITE(reg, val & ~PORT_ENABLE);
@@ -1893,12 +1896,12 @@ static void intel_disable_pch_ports(struct drm_i915_private *dev_priv,
 
        reg = PCH_ADPA;
        val = I915_READ(reg);
-       if (adpa_pipe_enabled(dev_priv, val, pipe))
+       if (adpa_pipe_enabled(dev_priv, pipe, val))
                I915_WRITE(reg, val & ~ADPA_DAC_ENABLE);
 
        reg = PCH_LVDS;
        val = I915_READ(reg);
-       if (lvds_pipe_enabled(dev_priv, val, pipe)) {
+       if (lvds_pipe_enabled(dev_priv, pipe, val)) {
                DRM_DEBUG_KMS("disable lvds on pipe %d val 0x%08x\n", pipe, val);
                I915_WRITE(reg, val & ~LVDS_PORT_EN);
                POSTING_READ(reg);
@@ -2706,11 +2709,10 @@ static void ivb_manual_fdi_link_train(struct drm_crtc *crtc)
        DRM_DEBUG_KMS("FDI train done.\n");
 }
 
-static void ironlake_fdi_pll_enable(struct drm_crtc *crtc)
+static void ironlake_fdi_pll_enable(struct intel_crtc *intel_crtc)
 {
-       struct drm_device *dev = crtc->dev;
+       struct drm_device *dev = intel_crtc->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        int pipe = intel_crtc->pipe;
        u32 reg, temp;
 
@@ -2751,6 +2753,35 @@ static void ironlake_fdi_pll_enable(struct drm_crtc *crtc)
        }
 }
 
+static void ironlake_fdi_pll_disable(struct intel_crtc *intel_crtc)
+{
+       struct drm_device *dev = intel_crtc->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       int pipe = intel_crtc->pipe;
+       u32 reg, temp;
+
+       /* Switch from PCDclk to Rawclk */
+       reg = FDI_RX_CTL(pipe);
+       temp = I915_READ(reg);
+       I915_WRITE(reg, temp & ~FDI_PCDCLK);
+
+       /* Disable CPU FDI TX PLL */
+       reg = FDI_TX_CTL(pipe);
+       temp = I915_READ(reg);
+       I915_WRITE(reg, temp & ~FDI_TX_PLL_ENABLE);
+
+       POSTING_READ(reg);
+       udelay(100);
+
+       reg = FDI_RX_CTL(pipe);
+       temp = I915_READ(reg);
+       I915_WRITE(reg, temp & ~FDI_RX_PLL_ENABLE);
+
+       /* Wait for the clocks to turn off. */
+       POSTING_READ(reg);
+       udelay(100);
+}
+
 static void cpt_phase_pointer_disable(struct drm_device *dev, int pipe)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
@@ -2835,13 +2866,13 @@ static void intel_crtc_wait_for_pending_flips(struct drm_crtc *crtc)
 static bool intel_crtc_driving_pch(struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
-       struct intel_encoder *encoder;
+       struct intel_encoder *intel_encoder;
 
        /*
         * If there's a non-PCH eDP on this crtc, it must be DP_A, and that
         * must be driven by its own crtc; no sharing is possible.
         */
-       for_each_encoder_on_crtc(dev, crtc, encoder) {
+       for_each_encoder_on_crtc(dev, crtc, intel_encoder) {
 
                /* On Haswell, LPT PCH handles the VGA connection via FDI, and Haswell
                 * CPU handles all others */
@@ -2849,19 +2880,19 @@ static bool intel_crtc_driving_pch(struct drm_crtc *crtc)
                        /* It is still unclear how this will work on PPT, so throw up a warning */
                        WARN_ON(!HAS_PCH_LPT(dev));
 
-                       if (encoder->type == DRM_MODE_ENCODER_DAC) {
+                       if (intel_encoder->type == INTEL_OUTPUT_ANALOG) {
                                DRM_DEBUG_KMS("Haswell detected DAC encoder, assuming is PCH\n");
                                return true;
                        } else {
                                DRM_DEBUG_KMS("Haswell detected encoder %d, assuming is CPU\n",
-                                               encoder->type);
+                                             intel_encoder->type);
                                return false;
                        }
                }
 
-               switch (encoder->type) {
+               switch (intel_encoder->type) {
                case INTEL_OUTPUT_EDP:
-                       if (!intel_encoder_is_pch_edp(&encoder->base))
+                       if (!intel_encoder_is_pch_edp(&intel_encoder->base))
                                return false;
                        continue;
                }
@@ -3178,13 +3209,18 @@ static void ironlake_crtc_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);
+       struct intel_encoder *encoder;
        int pipe = intel_crtc->pipe;
        int plane = intel_crtc->plane;
        u32 temp;
        bool is_pch_port;
 
+       WARN_ON(!crtc->enabled);
+
+       /* XXX: For compatability with the crtc helper code, call the encoder's
+        * enable function unconditionally for now. */
        if (intel_crtc->active)
-               return;
+               goto encoders;
 
        intel_crtc->active = true;
        intel_update_watermarks(dev);
@@ -3198,7 +3234,7 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
        is_pch_port = intel_crtc_driving_pch(crtc);
 
        if (is_pch_port)
-               ironlake_fdi_pll_enable(crtc);
+               ironlake_fdi_pll_enable(intel_crtc);
        else
                ironlake_fdi_disable(crtc);
 
@@ -3231,6 +3267,13 @@ static void ironlake_crtc_enable(struct drm_crtc *crtc)
        mutex_unlock(&dev->struct_mutex);
 
        intel_crtc_update_cursor(crtc, true);
+
+encoders:
+       for_each_encoder_on_crtc(dev, crtc, encoder)
+               encoder->enable(encoder);
+
+       if (HAS_PCH_CPT(dev))
+               intel_cpt_verify_modeset(dev, intel_crtc->pipe);
 }
 
 static void ironlake_crtc_disable(struct drm_crtc *crtc)
@@ -3238,10 +3281,16 @@ 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);
+       struct intel_encoder *encoder;
        int pipe = intel_crtc->pipe;
        int plane = intel_crtc->plane;
        u32 reg, temp;
 
+       /* XXX: For compatability with the crtc helper code, call the encoder's
+        * disable function unconditionally for now. */
+       for_each_encoder_on_crtc(dev, crtc, encoder)
+               encoder->disable(encoder);
+
        if (!intel_crtc->active)
                return;
 
@@ -3301,26 +3350,7 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
        /* disable PCH DPLL */
        intel_disable_pch_pll(intel_crtc);
 
-       /* Switch from PCDclk to Rawclk */
-       reg = FDI_RX_CTL(pipe);
-       temp = I915_READ(reg);
-       I915_WRITE(reg, temp & ~FDI_PCDCLK);
-
-       /* Disable CPU FDI TX PLL */
-       reg = FDI_TX_CTL(pipe);
-       temp = I915_READ(reg);
-       I915_WRITE(reg, temp & ~FDI_TX_PLL_ENABLE);
-
-       POSTING_READ(reg);
-       udelay(100);
-
-       reg = FDI_RX_CTL(pipe);
-       temp = I915_READ(reg);
-       I915_WRITE(reg, temp & ~FDI_RX_PLL_ENABLE);
-
-       /* Wait for the clocks to turn off. */
-       POSTING_READ(reg);
-       udelay(100);
+       ironlake_fdi_pll_disable(intel_crtc);
 
        intel_crtc->active = false;
        intel_update_watermarks(dev);
@@ -3330,30 +3360,6 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
        mutex_unlock(&dev->struct_mutex);
 }
 
-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 ironlake_crtc_off(struct drm_crtc *crtc)
 {
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
@@ -3383,11 +3389,16 @@ static void i9xx_crtc_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);
+       struct intel_encoder *encoder;
        int pipe = intel_crtc->pipe;
        int plane = intel_crtc->plane;
 
+       WARN_ON(!crtc->enabled);
+
+       /* XXX: For compatability with the crtc helper code, call the encoder's
+        * enable function unconditionally for now. */
        if (intel_crtc->active)
-               return;
+               goto encoders;
 
        intel_crtc->active = true;
        intel_update_watermarks(dev);
@@ -3402,6 +3413,10 @@ static void i9xx_crtc_enable(struct drm_crtc *crtc)
        /* Give the overlay scaler a chance to enable if it's on this pipe */
        intel_crtc_dpms_overlay(intel_crtc, true);
        intel_crtc_update_cursor(crtc, true);
+
+encoders:
+       for_each_encoder_on_crtc(dev, crtc, encoder)
+               encoder->enable(encoder);
 }
 
 static void i9xx_crtc_disable(struct drm_crtc *crtc)
@@ -3409,9 +3424,15 @@ 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);
+       struct intel_encoder *encoder;
        int pipe = intel_crtc->pipe;
        int plane = intel_crtc->plane;
 
+       /* XXX: For compatability with the crtc helper code, call the encoder's
+        * disable function unconditionally for now. */
+       for_each_encoder_on_crtc(dev, crtc, encoder)
+               encoder->disable(encoder);
+
        if (!intel_crtc->active)
                return;
 
@@ -3433,23 +3454,6 @@ static void i9xx_crtc_disable(struct drm_crtc *crtc)
        intel_update_watermarks(dev);
 }
 
-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;
-       }
-}
-
 static void i9xx_crtc_off(struct drm_crtc *crtc)
 {
 }
@@ -3457,21 +3461,31 @@ static void i9xx_crtc_off(struct drm_crtc *crtc)
 /**
  * Sets the power management mode of the pipe and plane.
  */
-static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
+void intel_crtc_update_dpms(struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_i915_master_private *master_priv;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       struct intel_encoder *intel_encoder;
        int pipe = intel_crtc->pipe;
-       bool enabled;
+       bool enabled, enable = false;
+       int mode;
+
+       for_each_encoder_on_crtc(dev, crtc, intel_encoder)
+               enable |= intel_encoder->connectors_active;
+
+       mode = enable ? DRM_MODE_DPMS_ON : DRM_MODE_DPMS_OFF;
 
        if (intel_crtc->dpms_mode == mode)
                return;
 
        intel_crtc->dpms_mode = mode;
 
-       dev_priv->display.dpms(crtc, mode);
+       if (enable)
+               dev_priv->display.crtc_enable(crtc);
+       else
+               dev_priv->display.crtc_disable(crtc);
 
        if (!dev->primary->master)
                return;
@@ -3480,7 +3494,7 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
        if (!master_priv->sarea_priv)
                return;
 
-       enabled = crtc->enabled && mode != DRM_MODE_DPMS_OFF;
+       enabled = crtc->enabled && enable;
 
        switch (pipe) {
        case 0:
@@ -3499,11 +3513,12 @@ static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
 
 static void intel_crtc_disable(struct drm_crtc *crtc)
 {
-       struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
 
-       crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
+       /* crtc->disable is only called when we have no encoders, hence this
+        * will disable the pipe. */
+       intel_crtc_update_dpms(crtc);
        dev_priv->display.off(crtc);
 
        assert_plane_disabled(dev->dev_private, to_intel_crtc(crtc)->plane);
@@ -3516,60 +3531,68 @@ static void intel_crtc_disable(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)
+void intel_encoder_disable(struct drm_encoder *encoder)
 {
-       i9xx_crtc_disable(crtc);
-}
+       struct intel_encoder *intel_encoder = to_intel_encoder(encoder);
 
-static void i9xx_crtc_commit(struct drm_crtc *crtc)
-{
-       i9xx_crtc_enable(crtc);
+       intel_encoder->disable(intel_encoder);
 }
 
-static void ironlake_crtc_prepare(struct drm_crtc *crtc)
+void intel_encoder_destroy(struct drm_encoder *encoder)
 {
-       ironlake_crtc_disable(crtc);
-}
+       struct intel_encoder *intel_encoder = to_intel_encoder(encoder);
 
-static void ironlake_crtc_commit(struct drm_crtc *crtc)
-{
-       ironlake_crtc_enable(crtc);
+       drm_encoder_cleanup(encoder);
+       kfree(intel_encoder);
 }
 
-void intel_encoder_prepare(struct drm_encoder *encoder)
+/* Simple dpms helper for encodres with just one connector, no cloning and only
+ * one kind of off state. It clamps all !ON modes to fully OFF and changes the
+ * state of the entire output pipe. */
+void intel_encoder_dpms(struct intel_encoder *encoder, int mode)
 {
-       struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
-       /* lvds has its own version of prepare see intel_lvds_prepare */
-       encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
+       if (mode == DRM_MODE_DPMS_ON) {
+               encoder->connectors_active = true;
+
+               intel_crtc_update_dpms(encoder->base.crtc);
+       } else {
+               encoder->connectors_active = false;
+
+               intel_crtc_update_dpms(encoder->base.crtc);
+       }
 }
 
-void intel_encoder_commit(struct drm_encoder *encoder)
+/* Even simpler default implementation, if there's really no special case to
+ * consider. */
+void intel_connector_dpms(struct drm_connector *connector, int mode)
 {
-       struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
-       struct drm_device *dev = encoder->dev;
-       struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
+       struct intel_encoder *encoder = intel_attached_encoder(connector);
 
-       /* lvds has its own version of commit see intel_lvds_commit */
-       encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
+       /* All the simple cases only support two dpms states. */
+       if (mode != DRM_MODE_DPMS_ON)
+               mode = DRM_MODE_DPMS_OFF;
 
-       if (HAS_PCH_CPT(dev))
-               intel_cpt_verify_modeset(dev, intel_crtc->pipe);
+       if (mode == connector->dpms)
+               return;
+
+       connector->dpms = mode;
+
+       /* Only need to change hw state when actually enabled */
+       if (encoder->base.crtc)
+               intel_encoder_dpms(encoder, mode);
+       else
+               encoder->connectors_active = false;
 }
 
-void intel_encoder_destroy(struct drm_encoder *encoder)
+/* Simple connector->get_hw_state implementation for encoders that support only
+ * one connector and no cloning and hence the encoder state determines the state
+ * of the connector. */
+bool intel_connector_get_hw_state(struct intel_connector *connector)
 {
-       struct intel_encoder *intel_encoder = to_intel_encoder(encoder);
+       enum pipe pipe;
+       struct intel_encoder *encoder = connector->encoder;
 
-       drm_encoder_cleanup(encoder);
-       kfree(intel_encoder);
+       return encoder->get_hw_state(encoder, &pipe);
 }
 
 static bool intel_crtc_mode_fixup(struct drm_crtc *crtc,
@@ -3753,17 +3776,6 @@ static bool intel_choose_pipe_bpp_dither(struct drm_crtc *crtc,
                        continue;
                }
 
-               if (intel_encoder->type == INTEL_OUTPUT_EDP) {
-                       /* Use VBT settings if we have an eDP panel */
-                       unsigned int edp_bpc = dev_priv->edp.bpp / 3;
-
-                       if (edp_bpc < display_bpc) {
-                               DRM_DEBUG_KMS("clamping display bpc (was %d) to eDP (%d)\n", display_bpc, edp_bpc);
-                               display_bpc = edp_bpc;
-                       }
-                       continue;
-               }
-
                /* Not one of the known troublemakers, check the EDID */
                list_for_each_entry(connector, &dev->mode_config.connector_list,
                                    head) {
@@ -5065,6 +5077,91 @@ static void g4x_write_eld(struct drm_connector *connector,
        I915_WRITE(G4X_AUD_CNTL_ST, i);
 }
 
+static void haswell_write_eld(struct drm_connector *connector,
+                                    struct drm_crtc *crtc)
+{
+       struct drm_i915_private *dev_priv = connector->dev->dev_private;
+       uint8_t *eld = connector->eld;
+       struct drm_device *dev = crtc->dev;
+       uint32_t eldv;
+       uint32_t i;
+       int len;
+       int pipe = to_intel_crtc(crtc)->pipe;
+       int tmp;
+
+       int hdmiw_hdmiedid = HSW_AUD_EDID_DATA(pipe);
+       int aud_cntl_st = HSW_AUD_DIP_ELD_CTRL(pipe);
+       int aud_config = HSW_AUD_CFG(pipe);
+       int aud_cntrl_st2 = HSW_AUD_PIN_ELD_CP_VLD;
+
+
+       DRM_DEBUG_DRIVER("HDMI: Haswell Audio initialize....\n");
+
+       /* Audio output enable */
+       DRM_DEBUG_DRIVER("HDMI audio: enable codec\n");
+       tmp = I915_READ(aud_cntrl_st2);
+       tmp |= (AUDIO_OUTPUT_ENABLE_A << (pipe * 4));
+       I915_WRITE(aud_cntrl_st2, tmp);
+
+       /* Wait for 1 vertical blank */
+       intel_wait_for_vblank(dev, pipe);
+
+       /* Set ELD valid state */
+       tmp = I915_READ(aud_cntrl_st2);
+       DRM_DEBUG_DRIVER("HDMI audio: pin eld vld status=0x%8x\n", tmp);
+       tmp |= (AUDIO_ELD_VALID_A << (pipe * 4));
+       I915_WRITE(aud_cntrl_st2, tmp);
+       tmp = I915_READ(aud_cntrl_st2);
+       DRM_DEBUG_DRIVER("HDMI audio: eld vld status=0x%8x\n", tmp);
+
+       /* Enable HDMI mode */
+       tmp = I915_READ(aud_config);
+       DRM_DEBUG_DRIVER("HDMI audio: audio conf: 0x%8x\n", tmp);
+       /* clear N_programing_enable and N_value_index */
+       tmp &= ~(AUD_CONFIG_N_VALUE_INDEX | AUD_CONFIG_N_PROG_ENABLE);
+       I915_WRITE(aud_config, tmp);
+
+       DRM_DEBUG_DRIVER("ELD on pipe %c\n", pipe_name(pipe));
+
+       eldv = AUDIO_ELD_VALID_A << (pipe * 4);
+
+       if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) {
+               DRM_DEBUG_DRIVER("ELD: DisplayPort detected\n");
+               eld[5] |= (1 << 2);     /* Conn_Type, 0x1 = DisplayPort */
+               I915_WRITE(aud_config, AUD_CONFIG_N_VALUE_INDEX); /* 0x1 = DP */
+       } else
+               I915_WRITE(aud_config, 0);
+
+       if (intel_eld_uptodate(connector,
+                              aud_cntrl_st2, eldv,
+                              aud_cntl_st, IBX_ELD_ADDRESS,
+                              hdmiw_hdmiedid))
+               return;
+
+       i = I915_READ(aud_cntrl_st2);
+       i &= ~eldv;
+       I915_WRITE(aud_cntrl_st2, i);
+
+       if (!eld[0])
+               return;
+
+       i = I915_READ(aud_cntl_st);
+       i &= ~IBX_ELD_ADDRESS;
+       I915_WRITE(aud_cntl_st, i);
+       i = (i >> 29) & DIP_PORT_SEL_MASK;              /* DIP_Port_Select, 0x1 = PortB */
+       DRM_DEBUG_DRIVER("port num:%d\n", i);
+
+       len = min_t(uint8_t, eld[2], 21);       /* 84 bytes of hw ELD buffer */
+       DRM_DEBUG_DRIVER("ELD size %d\n", len);
+       for (i = 0; i < len; i++)
+               I915_WRITE(hdmiw_hdmiedid, *((uint32_t *)eld + i));
+
+       i = I915_READ(aud_cntrl_st2);
+       i |= eldv;
+       I915_WRITE(aud_cntrl_st2, i);
+
+}
+
 static void ironlake_write_eld(struct drm_connector *connector,
                                     struct drm_crtc *crtc)
 {
@@ -5077,28 +5174,24 @@ static void ironlake_write_eld(struct drm_connector *connector,
        int aud_config;
        int aud_cntl_st;
        int aud_cntrl_st2;
+       int pipe = to_intel_crtc(crtc)->pipe;
 
        if (HAS_PCH_IBX(connector->dev)) {
-               hdmiw_hdmiedid = IBX_HDMIW_HDMIEDID_A;
-               aud_config = IBX_AUD_CONFIG_A;
-               aud_cntl_st = IBX_AUD_CNTL_ST_A;
+               hdmiw_hdmiedid = IBX_HDMIW_HDMIEDID(pipe);
+               aud_config = IBX_AUD_CFG(pipe);
+               aud_cntl_st = IBX_AUD_CNTL_ST(pipe);
                aud_cntrl_st2 = IBX_AUD_CNTL_ST2;
        } else {
-               hdmiw_hdmiedid = CPT_HDMIW_HDMIEDID_A;
-               aud_config = CPT_AUD_CONFIG_A;
-               aud_cntl_st = CPT_AUD_CNTL_ST_A;
+               hdmiw_hdmiedid = CPT_HDMIW_HDMIEDID(pipe);
+               aud_config = CPT_AUD_CFG(pipe);
+               aud_cntl_st = CPT_AUD_CNTL_ST(pipe);
                aud_cntrl_st2 = CPT_AUD_CNTRL_ST2;
        }
 
-       i = to_intel_crtc(crtc)->pipe;
-       hdmiw_hdmiedid += i * 0x100;
-       aud_cntl_st += i * 0x100;
-       aud_config += i * 0x100;
-
-       DRM_DEBUG_DRIVER("ELD on pipe %c\n", pipe_name(i));
+       DRM_DEBUG_DRIVER("ELD on pipe %c\n", pipe_name(pipe));
 
        i = I915_READ(aud_cntl_st);
-       i = (i >> 29) & 0x3;            /* DIP_Port_Select, 0x1 = PortB */
+       i = (i >> 29) & DIP_PORT_SEL_MASK;              /* DIP_Port_Select, 0x1 = PortB */
        if (!i) {
                DRM_DEBUG_DRIVER("Audio directed to unknown port\n");
                /* operate blindly on all ports */
@@ -5592,12 +5685,13 @@ mode_fits_in_fbdev(struct drm_device *dev,
        return fb;
 }
 
-bool intel_get_load_detect_pipe(struct intel_encoder *intel_encoder,
-                               struct drm_connector *connector,
+bool intel_get_load_detect_pipe(struct drm_connector *connector,
                                struct drm_display_mode *mode,
                                struct intel_load_detect_pipe *old)
 {
        struct intel_crtc *intel_crtc;
+       struct intel_encoder *intel_encoder =
+               intel_attached_encoder(connector);
        struct drm_crtc *possible_crtc;
        struct drm_encoder *encoder = &intel_encoder->base;
        struct drm_crtc *crtc = NULL;
@@ -5623,21 +5717,12 @@ bool intel_get_load_detect_pipe(struct intel_encoder *intel_encoder,
        if (encoder->crtc) {
                crtc = encoder->crtc;
 
-               intel_crtc = to_intel_crtc(crtc);
-               old->dpms_mode = intel_crtc->dpms_mode;
+               old->dpms_mode = connector->dpms;
                old->load_detect_temp = false;
 
                /* Make sure the crtc and connector are running */
-               if (intel_crtc->dpms_mode != DRM_MODE_DPMS_ON) {
-                       struct drm_encoder_helper_funcs *encoder_funcs;
-                       struct drm_crtc_helper_funcs *crtc_funcs;
-
-                       crtc_funcs = crtc->helper_private;
-                       crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
-
-                       encoder_funcs = encoder->helper_private;
-                       encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
-               }
+               if (connector->dpms != DRM_MODE_DPMS_ON)
+                       connector->funcs->dpms(connector, DRM_MODE_DPMS_ON);
 
                return true;
        }
@@ -5665,7 +5750,7 @@ bool intel_get_load_detect_pipe(struct intel_encoder *intel_encoder,
        connector->encoder = encoder;
 
        intel_crtc = to_intel_crtc(crtc);
-       old->dpms_mode = intel_crtc->dpms_mode;
+       old->dpms_mode = connector->dpms;
        old->load_detect_temp = true;
        old->release_fb = NULL;
 
@@ -5690,33 +5775,34 @@ bool intel_get_load_detect_pipe(struct intel_encoder *intel_encoder,
                DRM_DEBUG_KMS("reusing fbdev for load-detection framebuffer\n");
        if (IS_ERR(crtc->fb)) {
                DRM_DEBUG_KMS("failed to allocate framebuffer for load-detection\n");
-               crtc->fb = old_fb;
-               return false;
+               goto fail;
        }
 
-       if (!drm_crtc_helper_set_mode(crtc, mode, 0, 0, old_fb)) {
+       if (!intel_set_mode(crtc, mode, 0, 0, old_fb)) {
                DRM_DEBUG_KMS("failed to set mode on load-detect pipe\n");
                if (old->release_fb)
                        old->release_fb->funcs->destroy(old->release_fb);
-               crtc->fb = old_fb;
-               return false;
+               goto fail;
        }
 
        /* let the connector get through one full cycle before testing */
        intel_wait_for_vblank(dev, intel_crtc->pipe);
 
        return true;
+fail:
+       connector->encoder = NULL;
+       encoder->crtc = NULL;
+       crtc->fb = old_fb;
+       return false;
 }
 
-void intel_release_load_detect_pipe(struct intel_encoder *intel_encoder,
-                                   struct drm_connector *connector,
+void intel_release_load_detect_pipe(struct drm_connector *connector,
                                    struct intel_load_detect_pipe *old)
 {
+       struct intel_encoder *intel_encoder =
+               intel_attached_encoder(connector);
        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;
-       struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
 
        DRM_DEBUG_KMS("[CONNECTOR:%d:%s], [ENCODER:%d:%s]\n",
                      connector->base.id, drm_get_connector_name(connector),
@@ -5724,6 +5810,7 @@ void intel_release_load_detect_pipe(struct intel_encoder *intel_encoder,
 
        if (old->load_detect_temp) {
                connector->encoder = NULL;
+               encoder->crtc = NULL;
                drm_helper_disable_unused_functions(dev);
 
                if (old->release_fb)
@@ -5733,10 +5820,8 @@ void intel_release_load_detect_pipe(struct intel_encoder *intel_encoder,
        }
 
        /* Switch crtc and encoder back off if necessary */
-       if (old->dpms_mode != DRM_MODE_DPMS_ON) {
-               encoder_funcs->dpms(encoder, old->dpms_mode);
-               crtc_funcs->dpms(crtc, old->dpms_mode);
-       }
+       if (old->dpms_mode != DRM_MODE_DPMS_ON)
+               connector->funcs->dpms(connector, old->dpms_mode);
 }
 
 /* Returns the clock of the currently programmed mode of the given pipe. */
@@ -5858,46 +5943,6 @@ 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;
-
-       if (!list_empty(&dev_priv->mm.active_list)) {
-               /* Still processing requests, so just re-arm the timer. */
-               mod_timer(&dev_priv->idle_timer, jiffies +
-                         msecs_to_jiffies(GPU_IDLE_TIMEOUT));
-               return;
-       }
-
-       dev_priv->busy = false;
-       queue_work(dev_priv->wq, &dev_priv->idle_work);
-}
-
-#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;
-       struct intel_framebuffer *intel_fb;
-
-       intel_fb = to_intel_framebuffer(crtc->fb);
-       if (intel_fb && intel_fb->obj->active) {
-               /* The framebuffer is still being accessed by the GPU. */
-               mod_timer(&intel_crtc->idle_timer, jiffies +
-                         msecs_to_jiffies(CRTC_IDLE_TIMEOUT));
-               return;
-       }
-
-       intel_crtc->busy = false;
-       queue_work(dev_priv->wq, &dev_priv->idle_work);
-}
-
 static void intel_increase_pllclock(struct drm_crtc *crtc)
 {
        struct drm_device *dev = crtc->dev;
@@ -5927,10 +5972,6 @@ static void intel_increase_pllclock(struct drm_crtc *crtc)
                if (dpll & DISPLAY_RATE_SELECT_FPA1)
                        DRM_DEBUG_DRIVER("failed to upclock LVDS!\n");
        }
-
-       /* Schedule downclock */
-       mod_timer(&intel_crtc->idle_timer, jiffies +
-                 msecs_to_jiffies(CRTC_IDLE_TIMEOUT));
 }
 
 static void intel_decrease_pllclock(struct drm_crtc *crtc)
@@ -5969,89 +6010,46 @@ static void intel_decrease_pllclock(struct drm_crtc *crtc)
 
 }
 
-/**
- * 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)
+void intel_mark_busy(struct drm_device *dev)
+{
+       i915_update_gfx_val(dev->dev_private);
+}
+
+void intel_mark_idle(struct drm_device *dev)
+{
+}
+
+void intel_mark_fb_busy(struct drm_i915_gem_object *obj)
 {
-       drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t,
-                                                   idle_work);
-       struct drm_device *dev = dev_priv->dev;
+       struct drm_device *dev = obj->base.dev;
        struct drm_crtc *crtc;
-       struct intel_crtc *intel_crtc;
 
        if (!i915_powersave)
                return;
 
-       mutex_lock(&dev->struct_mutex);
-
-       i915_update_gfx_val(dev_priv);
-
        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);
+               if (to_intel_framebuffer(crtc->fb)->obj == obj)
+                       intel_increase_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_i915_gem_object *obj)
+void intel_mark_fb_idle(struct drm_i915_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;
-
-       if (!dev_priv->busy) {
-               intel_sanitize_pm(dev);
-               dev_priv->busy = true;
-       } else
-               mod_timer(&dev_priv->idle_timer, jiffies +
-                         msecs_to_jiffies(GPU_IDLE_TIMEOUT));
+       struct drm_device *dev = obj->base.dev;
+       struct drm_crtc *crtc;
 
-       if (obj == NULL)
+       if (!i915_powersave)
                return;
 
        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);
-                               intel_crtc->busy = true;
-                       } else {
-                               /* Busy -> busy, put off timer */
-                               mod_timer(&intel_crtc->idle_timer, jiffies +
-                                         msecs_to_jiffies(CRTC_IDLE_TIMEOUT));
-                       }
-               }
+               if (to_intel_framebuffer(crtc->fb)->obj == obj)
+                       intel_decrease_pllclock(crtc);
        }
 }
 
@@ -6402,7 +6400,7 @@ static int intel_gen7_queue_flip(struct drm_device *dev,
        default:
                WARN_ONCE(1, "unknown plane in flip command\n");
                ret = -ENODEV;
-               goto err;
+               goto err_unpin;
        }
 
        ret = intel_ring_begin(ring, 4);
@@ -6510,7 +6508,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc,
                goto cleanup_pending;
 
        intel_disable_fbc(dev);
-       intel_mark_busy(dev, obj);
+       intel_mark_fb_busy(obj);
        mutex_unlock(&dev->struct_mutex);
 
        trace_i915_flip_request(intel_crtc->plane, obj);
@@ -6595,21 +6593,424 @@ static void intel_crtc_reset(struct drm_crtc *crtc)
 }
 
 static struct drm_crtc_helper_funcs intel_helper_funcs = {
-       .dpms = intel_crtc_dpms,
-       .mode_fixup = intel_crtc_mode_fixup,
-       .mode_set = intel_crtc_mode_set,
-       .mode_set_base = intel_pipe_set_base,
        .mode_set_base_atomic = intel_pipe_set_base_atomic,
        .load_lut = intel_crtc_load_lut,
        .disable = intel_crtc_disable,
 };
 
+static bool intel_encoder_crtc_ok(struct drm_encoder *encoder,
+                                 struct drm_crtc *crtc)
+{
+       struct drm_device *dev;
+       struct drm_crtc *tmp;
+       int crtc_mask = 1;
+
+       WARN(!crtc, "checking null crtc?\n");
+
+       dev = crtc->dev;
+
+       list_for_each_entry(tmp, &dev->mode_config.crtc_list, head) {
+               if (tmp == crtc)
+                       break;
+               crtc_mask <<= 1;
+       }
+
+       if (encoder->possible_crtcs & crtc_mask)
+               return true;
+       return false;
+}
+
+static int
+intel_crtc_helper_disable(struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       struct drm_connector *connector;
+       struct drm_encoder *encoder;
+
+       /* Decouple all encoders and their attached connectors from this crtc */
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+               if (encoder->crtc != crtc)
+                       continue;
+
+               list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+                       if (connector->encoder != encoder)
+                               continue;
+
+                       connector->encoder = NULL;
+               }
+       }
+
+       drm_helper_disable_unused_functions(dev);
+       return 0;
+}
+
+static void
+intel_crtc_prepare_encoders(struct drm_device *dev)
+{
+       struct intel_encoder *encoder;
+
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) {
+               /* Disable unused encoders */
+               if (encoder->base.crtc == NULL)
+                       encoder->disable(encoder);
+       }
+}
+
+bool intel_set_mode(struct drm_crtc *crtc,
+                   struct drm_display_mode *mode,
+                   int x, int y, struct drm_framebuffer *old_fb)
+{
+       struct drm_device *dev = crtc->dev;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       struct drm_display_mode *adjusted_mode, saved_mode, saved_hwmode;
+       struct drm_encoder_helper_funcs *encoder_funcs;
+       int saved_x, saved_y;
+       struct drm_encoder *encoder;
+       bool ret = true;
+
+       crtc->enabled = drm_helper_crtc_in_use(crtc);
+       if (!crtc->enabled)
+               return true;
+
+       adjusted_mode = drm_mode_duplicate(dev, mode);
+       if (!adjusted_mode)
+               return false;
+
+       saved_hwmode = crtc->hwmode;
+       saved_mode = crtc->mode;
+       saved_x = crtc->x;
+       saved_y = crtc->y;
+
+       /* Update crtc values up front so the driver can rely on them for mode
+        * setting.
+        */
+       crtc->mode = *mode;
+       crtc->x = x;
+       crtc->y = y;
+
+       /* Pass our mode to the connectors and the CRTC to give them a chance to
+        * adjust it according to limitations or connector properties, and also
+        * a chance to reject the mode entirely.
+        */
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+
+               if (encoder->crtc != crtc)
+                       continue;
+               encoder_funcs = encoder->helper_private;
+               if (!(ret = encoder_funcs->mode_fixup(encoder, mode,
+                                                     adjusted_mode))) {
+                       DRM_DEBUG_KMS("Encoder fixup failed\n");
+                       goto done;
+               }
+       }
+
+       if (!(ret = intel_crtc_mode_fixup(crtc, mode, adjusted_mode))) {
+               DRM_DEBUG_KMS("CRTC fixup failed\n");
+               goto done;
+       }
+       DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id);
+
+       intel_crtc_prepare_encoders(dev);
+
+       dev_priv->display.crtc_disable(crtc);
+
+       /* Set up the DPLL and any encoders state that needs to adjust or depend
+        * on the DPLL.
+        */
+       ret = !intel_crtc_mode_set(crtc, mode, adjusted_mode, x, y, old_fb);
+       if (!ret)
+           goto done;
+
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+
+               if (encoder->crtc != crtc)
+                       continue;
+
+               DRM_DEBUG_KMS("[ENCODER:%d:%s] set [MODE:%d:%s]\n",
+                       encoder->base.id, drm_get_encoder_name(encoder),
+                       mode->base.id, mode->name);
+               encoder_funcs = encoder->helper_private;
+               encoder_funcs->mode_set(encoder, mode, adjusted_mode);
+       }
+
+       /* Now enable the clocks, plane, pipe, and connectors that we set up. */
+       dev_priv->display.crtc_enable(crtc);
+
+       /* Store real post-adjustment hardware mode. */
+       crtc->hwmode = *adjusted_mode;
+
+       /* Calculate and store various constants which
+        * are later needed by vblank and swap-completion
+        * timestamping. They are derived from true hwmode.
+        */
+       drm_calc_timestamping_constants(crtc);
+
+       /* FIXME: add subpixel order */
+done:
+       drm_mode_destroy(dev, adjusted_mode);
+       if (!ret) {
+               crtc->hwmode = saved_hwmode;
+               crtc->mode = saved_mode;
+               crtc->x = saved_x;
+               crtc->y = saved_y;
+       }
+
+       return ret;
+}
+
+static int intel_crtc_set_config(struct drm_mode_set *set)
+{
+       struct drm_device *dev;
+       struct drm_crtc *save_crtcs, *new_crtc, *crtc;
+       struct drm_encoder *save_encoders, *new_encoder, *encoder;
+       struct drm_framebuffer *old_fb = NULL;
+       bool mode_changed = false; /* if true do a full mode set */
+       bool fb_changed = false; /* if true and !mode_changed just do a flip */
+       struct drm_connector *save_connectors, *connector;
+       int count = 0, ro;
+       struct drm_mode_set save_set;
+       int ret;
+       int i;
+
+       DRM_DEBUG_KMS("\n");
+
+       if (!set)
+               return -EINVAL;
+
+       if (!set->crtc)
+               return -EINVAL;
+
+       if (!set->crtc->helper_private)
+               return -EINVAL;
+
+       if (!set->mode)
+               set->fb = NULL;
+
+       if (set->fb) {
+               DRM_DEBUG_KMS("[CRTC:%d] [FB:%d] #connectors=%d (x y) (%i %i)\n",
+                               set->crtc->base.id, set->fb->base.id,
+                               (int)set->num_connectors, set->x, set->y);
+       } else {
+               DRM_DEBUG_KMS("[CRTC:%d] [NOFB]\n", set->crtc->base.id);
+               return intel_crtc_helper_disable(set->crtc);
+       }
+
+       dev = set->crtc->dev;
+
+       /* Allocate space for the backup of all (non-pointer) crtc, encoder and
+        * connector data. */
+       save_crtcs = kzalloc(dev->mode_config.num_crtc *
+                            sizeof(struct drm_crtc), GFP_KERNEL);
+       if (!save_crtcs)
+               return -ENOMEM;
+
+       save_encoders = kzalloc(dev->mode_config.num_encoder *
+                               sizeof(struct drm_encoder), GFP_KERNEL);
+       if (!save_encoders) {
+               kfree(save_crtcs);
+               return -ENOMEM;
+       }
+
+       save_connectors = kzalloc(dev->mode_config.num_connector *
+                               sizeof(struct drm_connector), GFP_KERNEL);
+       if (!save_connectors) {
+               kfree(save_crtcs);
+               kfree(save_encoders);
+               return -ENOMEM;
+       }
+
+       /* Copy data. Note that driver private data is not affected.
+        * Should anything bad happen only the expected state is
+        * restored, not the drivers personal bookkeeping.
+        */
+       count = 0;
+       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+               save_crtcs[count++] = *crtc;
+       }
+
+       count = 0;
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+               save_encoders[count++] = *encoder;
+       }
+
+       count = 0;
+       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+               save_connectors[count++] = *connector;
+       }
+
+       save_set.crtc = set->crtc;
+       save_set.mode = &set->crtc->mode;
+       save_set.x = set->crtc->x;
+       save_set.y = set->crtc->y;
+       save_set.fb = set->crtc->fb;
+
+       /* We should be able to check here if the fb has the same properties
+        * and then just flip_or_move it */
+       if (set->crtc->fb != set->fb) {
+               /* If we have no fb then treat it as a full mode set */
+               if (set->crtc->fb == NULL) {
+                       DRM_DEBUG_KMS("crtc has no fb, full mode set\n");
+                       mode_changed = true;
+               } else if (set->fb == NULL) {
+                       mode_changed = true;
+               } else if (set->fb->depth != set->crtc->fb->depth) {
+                       mode_changed = true;
+               } else if (set->fb->bits_per_pixel !=
+                          set->crtc->fb->bits_per_pixel) {
+                       mode_changed = true;
+               } else
+                       fb_changed = true;
+       }
+
+       if (set->x != set->crtc->x || set->y != set->crtc->y)
+               fb_changed = true;
+
+       if (set->mode && !drm_mode_equal(set->mode, &set->crtc->mode)) {
+               DRM_DEBUG_KMS("modes are different, full mode set\n");
+               drm_mode_debug_printmodeline(&set->crtc->mode);
+               drm_mode_debug_printmodeline(set->mode);
+               mode_changed = true;
+       }
+
+       /* a) traverse passed in connector list and get encoders for them */
+       count = 0;
+       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+               new_encoder = connector->encoder;
+               for (ro = 0; ro < set->num_connectors; ro++) {
+                       if (set->connectors[ro] == connector) {
+                               new_encoder =
+                                       &intel_attached_encoder(connector)->base;
+                               break;
+                       }
+               }
+
+               if (new_encoder != connector->encoder) {
+                       DRM_DEBUG_KMS("encoder changed, full mode switch\n");
+                       mode_changed = true;
+                       /* If the encoder is reused for another connector, then
+                        * the appropriate crtc will be set later.
+                        */
+                       if (connector->encoder)
+                               connector->encoder->crtc = NULL;
+                       connector->encoder = new_encoder;
+               }
+       }
+
+       count = 0;
+       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+               if (!connector->encoder)
+                       continue;
+
+               if (connector->encoder->crtc == set->crtc)
+                       new_crtc = NULL;
+               else
+                       new_crtc = connector->encoder->crtc;
+
+               for (ro = 0; ro < set->num_connectors; ro++) {
+                       if (set->connectors[ro] == connector)
+                               new_crtc = set->crtc;
+               }
+
+               /* Make sure the new CRTC will work with the encoder */
+               if (new_crtc &&
+                   !intel_encoder_crtc_ok(connector->encoder, new_crtc)) {
+                       ret = -EINVAL;
+                       goto fail;
+               }
+               if (new_crtc != connector->encoder->crtc) {
+                       DRM_DEBUG_KMS("crtc changed, full mode switch\n");
+                       mode_changed = true;
+                       connector->encoder->crtc = new_crtc;
+               }
+               if (new_crtc) {
+                       DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [CRTC:%d]\n",
+                               connector->base.id, drm_get_connector_name(connector),
+                               new_crtc->base.id);
+               } else {
+                       DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [NOCRTC]\n",
+                               connector->base.id, drm_get_connector_name(connector));
+               }
+       }
+
+       if (mode_changed) {
+               set->crtc->enabled = drm_helper_crtc_in_use(set->crtc);
+               if (set->crtc->enabled) {
+                       DRM_DEBUG_KMS("attempting to set mode from"
+                                       " userspace\n");
+                       drm_mode_debug_printmodeline(set->mode);
+                       old_fb = set->crtc->fb;
+                       set->crtc->fb = set->fb;
+                       if (!intel_set_mode(set->crtc, set->mode,
+                                           set->x, set->y, old_fb)) {
+                               DRM_ERROR("failed to set mode on [CRTC:%d]\n",
+                                         set->crtc->base.id);
+                               set->crtc->fb = old_fb;
+                               ret = -EINVAL;
+                               goto fail;
+                       }
+                       DRM_DEBUG_KMS("Setting connector DPMS state to on\n");
+                       for (i = 0; i < set->num_connectors; i++) {
+                               DRM_DEBUG_KMS("\t[CONNECTOR:%d:%s] set DPMS on\n", set->connectors[i]->base.id,
+                                             drm_get_connector_name(set->connectors[i]));
+                               set->connectors[i]->funcs->dpms(set->connectors[i], DRM_MODE_DPMS_ON);
+                       }
+               }
+               drm_helper_disable_unused_functions(dev);
+       } else if (fb_changed) {
+               set->crtc->x = set->x;
+               set->crtc->y = set->y;
+
+               old_fb = set->crtc->fb;
+               if (set->crtc->fb != set->fb)
+                       set->crtc->fb = set->fb;
+               ret = intel_pipe_set_base(set->crtc,
+                                         set->x, set->y, old_fb);
+               if (ret != 0) {
+                       set->crtc->fb = old_fb;
+                       goto fail;
+               }
+       }
+
+       kfree(save_connectors);
+       kfree(save_encoders);
+       kfree(save_crtcs);
+       return 0;
+
+fail:
+       /* Restore all previous data. */
+       count = 0;
+       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+               *crtc = save_crtcs[count++];
+       }
+
+       count = 0;
+       list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+               *encoder = save_encoders[count++];
+       }
+
+       count = 0;
+       list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+               *connector = save_connectors[count++];
+       }
+
+       /* Try to restore the config */
+       if (mode_changed &&
+           !intel_set_mode(save_set.crtc, save_set.mode,
+                           save_set.x, save_set.y, save_set.fb))
+               DRM_ERROR("failed to restore config after modeset failure\n");
+
+       kfree(save_connectors);
+       kfree(save_encoders);
+       kfree(save_crtcs);
+       return ret;
+}
+
 static const struct drm_crtc_funcs intel_crtc_funcs = {
        .reset = intel_crtc_reset,
        .cursor_set = intel_crtc_cursor_set,
        .cursor_move = intel_crtc_cursor_move,
        .gamma_set = intel_crtc_gamma_set,
-       .set_config = drm_crtc_helper_set_config,
+       .set_config = intel_crtc_set_config,
        .destroy = intel_crtc_destroy,
        .page_flip = intel_crtc_page_flip,
 };
@@ -6667,20 +7068,7 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
        intel_crtc->active = true; /* force the pipe off on setup_init_config */
        intel_crtc->bpp = 24; /* default for pre-Ironlake */
 
-       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;
-
-       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,
@@ -6707,15 +7095,23 @@ int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data,
        return 0;
 }
 
-static int intel_encoder_clones(struct drm_device *dev, int type_mask)
+static int intel_encoder_clones(struct intel_encoder *encoder)
 {
-       struct intel_encoder *encoder;
+       struct drm_device *dev = encoder->base.dev;
+       struct intel_encoder *source_encoder;
        int index_mask = 0;
        int entry = 0;
 
-       list_for_each_entry(encoder, &dev->mode_config.encoder_list, base.head) {
-               if (type_mask & encoder->clone_mask)
+       list_for_each_entry(source_encoder,
+                           &dev->mode_config.encoder_list, base.head) {
+
+               if (encoder == source_encoder)
+                       index_mask |= (1 << entry);
+
+               /* Intel hw has only one MUX where enocoders could be cloned. */
+               if (encoder->cloneable && source_encoder->cloneable)
                        index_mask |= (1 << entry);
+
                entry++;
        }
 
@@ -6756,10 +7152,10 @@ static void intel_setup_outputs(struct drm_device *dev)
                dpd_is_edp = intel_dpd_is_edp(dev);
 
                if (has_edp_a(dev))
-                       intel_dp_init(dev, DP_A);
+                       intel_dp_init(dev, DP_A, PORT_A);
 
                if (dpd_is_edp && (I915_READ(PCH_DP_D) & DP_DETECTED))
-                       intel_dp_init(dev, PCH_DP_D);
+                       intel_dp_init(dev, PCH_DP_D, PORT_D);
        }
 
        intel_crt_init(dev);
@@ -6790,22 +7186,22 @@ static void intel_setup_outputs(struct drm_device *dev)
                        /* PCH SDVOB multiplex with HDMIB */
                        found = intel_sdvo_init(dev, PCH_SDVOB, true);
                        if (!found)
-                               intel_hdmi_init(dev, HDMIB);
+                               intel_hdmi_init(dev, HDMIB, PORT_B);
                        if (!found && (I915_READ(PCH_DP_B) & DP_DETECTED))
-                               intel_dp_init(dev, PCH_DP_B);
+                               intel_dp_init(dev, PCH_DP_B, PORT_B);
                }
 
                if (I915_READ(HDMIC) & PORT_DETECTED)
-                       intel_hdmi_init(dev, HDMIC);
+                       intel_hdmi_init(dev, HDMIC, PORT_C);
 
                if (!dpd_is_edp && I915_READ(HDMID) & PORT_DETECTED)
-                       intel_hdmi_init(dev, HDMID);
+                       intel_hdmi_init(dev, HDMID, PORT_D);
 
                if (I915_READ(PCH_DP_C) & DP_DETECTED)
-                       intel_dp_init(dev, PCH_DP_C);
+                       intel_dp_init(dev, PCH_DP_C, PORT_C);
 
                if (!dpd_is_edp && (I915_READ(PCH_DP_D) & DP_DETECTED))
-                       intel_dp_init(dev, PCH_DP_D);
+                       intel_dp_init(dev, PCH_DP_D, PORT_D);
        } else if (IS_VALLEYVIEW(dev)) {
                int found;
 
@@ -6813,17 +7209,17 @@ static void intel_setup_outputs(struct drm_device *dev)
                        /* SDVOB multiplex with HDMIB */
                        found = intel_sdvo_init(dev, SDVOB, true);
                        if (!found)
-                               intel_hdmi_init(dev, SDVOB);
+                               intel_hdmi_init(dev, SDVOB, PORT_B);
                        if (!found && (I915_READ(DP_B) & DP_DETECTED))
-                               intel_dp_init(dev, DP_B);
+                               intel_dp_init(dev, DP_B, PORT_B);
                }
 
                if (I915_READ(SDVOC) & PORT_DETECTED)
-                       intel_hdmi_init(dev, SDVOC);
+                       intel_hdmi_init(dev, SDVOC, PORT_C);
 
                /* Shares lanes with HDMI on SDVOC */
                if (I915_READ(DP_C) & DP_DETECTED)
-                       intel_dp_init(dev, DP_C);
+                       intel_dp_init(dev, DP_C, PORT_C);
        } else if (SUPPORTS_DIGITAL_OUTPUTS(dev)) {
                bool found = false;
 
@@ -6832,12 +7228,12 @@ static void intel_setup_outputs(struct drm_device *dev)
                        found = intel_sdvo_init(dev, SDVOB, true);
                        if (!found && SUPPORTS_INTEGRATED_HDMI(dev)) {
                                DRM_DEBUG_KMS("probing HDMI on SDVOB\n");
-                               intel_hdmi_init(dev, SDVOB);
+                               intel_hdmi_init(dev, SDVOB, PORT_B);
                        }
 
                        if (!found && SUPPORTS_INTEGRATED_DP(dev)) {
                                DRM_DEBUG_KMS("probing DP_B\n");
-                               intel_dp_init(dev, DP_B);
+                               intel_dp_init(dev, DP_B, PORT_B);
                        }
                }
 
@@ -6852,18 +7248,18 @@ static void intel_setup_outputs(struct drm_device *dev)
 
                        if (SUPPORTS_INTEGRATED_HDMI(dev)) {
                                DRM_DEBUG_KMS("probing HDMI on SDVOC\n");
-                               intel_hdmi_init(dev, SDVOC);
+                               intel_hdmi_init(dev, SDVOC, PORT_C);
                        }
                        if (SUPPORTS_INTEGRATED_DP(dev)) {
                                DRM_DEBUG_KMS("probing DP_C\n");
-                               intel_dp_init(dev, DP_C);
+                               intel_dp_init(dev, DP_C, PORT_C);
                        }
                }
 
                if (SUPPORTS_INTEGRATED_DP(dev) &&
                    (I915_READ(DP_D) & DP_DETECTED)) {
                        DRM_DEBUG_KMS("probing DP_D\n");
-                       intel_dp_init(dev, DP_D);
+                       intel_dp_init(dev, DP_D, PORT_D);
                }
        } else if (IS_GEN2(dev))
                intel_dvo_init(dev);
@@ -6874,7 +7270,7 @@ static void intel_setup_outputs(struct drm_device *dev)
        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);
+                       intel_encoder_clones(encoder);
        }
 
        /* disable all the possible outputs/crtcs before entering KMS mode */
@@ -6981,13 +7377,15 @@ static void intel_init_display(struct drm_device *dev)
 
        /* We always want a DPMS function */
        if (HAS_PCH_SPLIT(dev)) {
-               dev_priv->display.dpms = ironlake_crtc_dpms;
                dev_priv->display.crtc_mode_set = ironlake_crtc_mode_set;
+               dev_priv->display.crtc_enable = ironlake_crtc_enable;
+               dev_priv->display.crtc_disable = ironlake_crtc_disable;
                dev_priv->display.off = ironlake_crtc_off;
                dev_priv->display.update_plane = ironlake_update_plane;
        } else {
-               dev_priv->display.dpms = i9xx_crtc_dpms;
                dev_priv->display.crtc_mode_set = i9xx_crtc_mode_set;
+               dev_priv->display.crtc_enable = i9xx_crtc_enable;
+               dev_priv->display.crtc_disable = i9xx_crtc_disable;
                dev_priv->display.off = i9xx_crtc_off;
                dev_priv->display.update_plane = i9xx_update_plane;
        }
@@ -7031,7 +7429,7 @@ static void intel_init_display(struct drm_device *dev)
                        dev_priv->display.write_eld = ironlake_write_eld;
                } else if (IS_HASWELL(dev)) {
                        dev_priv->display.fdi_link_train = hsw_fdi_link_train;
-                       dev_priv->display.write_eld = ironlake_write_eld;
+                       dev_priv->display.write_eld = haswell_write_eld;
                } else
                        dev_priv->display.update_wm = NULL;
        } else if (IS_G4X(dev)) {
@@ -7109,21 +7507,16 @@ static struct intel_quirk intel_quirks[] = {
        /* HP Mini needs pipe A force quirk (LP: #322104) */
        { 0x27ae, 0x103c, 0x361a, quirk_pipea_force },
 
-       /* Thinkpad R31 needs pipe A force quirk */
-       { 0x3577, 0x1014, 0x0505, quirk_pipea_force },
        /* Toshiba Protege R-205, S-209 needs pipe A force quirk */
        { 0x2592, 0x1179, 0x0001, quirk_pipea_force },
 
-       /* ThinkPad X30 needs pipe A force quirk (LP: #304614) */
-       { 0x3577,  0x1014, 0x0513, quirk_pipea_force },
-       /* ThinkPad X40 needs pipe A force quirk */
-
        /* ThinkPad T60 needs pipe A force quirk (bug #16494) */
        { 0x2782, 0x17aa, 0x201a, quirk_pipea_force },
 
        /* 855 & before need to leave pipe A & dpll A up */
        { 0x3582, PCI_ANY_ID, PCI_ANY_ID, quirk_pipea_force },
        { 0x2562, PCI_ANY_ID, PCI_ANY_ID, quirk_pipea_force },
+       { 0x3577, PCI_ANY_ID, PCI_ANY_ID, quirk_pipea_force },
 
        /* Lenovo U160 cannot use SSC on LVDS */
        { 0x0046, 0x17aa, 0x3920, quirk_ssc_force_disable },
@@ -7239,10 +7632,6 @@ void intel_modeset_init(struct drm_device *dev)
        /* Just disable it once at startup */
        i915_disable_vga(dev);
        intel_setup_outputs(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_gem_init(struct drm_device *dev)
@@ -7288,19 +7677,11 @@ void intel_modeset_cleanup(struct drm_device *dev)
         * enqueue unpin/hotplug work. */
        drm_irq_uninstall(dev);
        cancel_work_sync(&dev_priv->hotplug_work);
-       cancel_work_sync(&dev_priv->rps_work);
+       cancel_work_sync(&dev_priv->rps.work);
 
        /* flush any delayed tasks or pending work */
        flush_scheduled_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);
 }
 
@@ -7346,7 +7727,7 @@ struct intel_display_error_state {
                u32 position;
                u32 base;
                u32 size;
-       } cursor[2];
+       } cursor[I915_MAX_PIPES];
 
        struct intel_pipe_error_state {
                u32 conf;
@@ -7358,7 +7739,7 @@ struct intel_display_error_state {
                u32 vtotal;
                u32 vblank;
                u32 vsync;
-       } pipe[2];
+       } pipe[I915_MAX_PIPES];
 
        struct intel_plane_error_state {
                u32 control;
@@ -7368,7 +7749,7 @@ struct intel_display_error_state {
                u32 addr;
                u32 surface;
                u32 tile_offset;
-       } plane[2];
+       } plane[I915_MAX_PIPES];
 };
 
 struct intel_display_error_state *
@@ -7382,7 +7763,7 @@ intel_display_capture_error_state(struct drm_device *dev)
        if (error == NULL)
                return NULL;
 
-       for (i = 0; i < 2; i++) {
+       for_each_pipe(i) {
                error->cursor[i].control = I915_READ(CURCNTR(i));
                error->cursor[i].position = I915_READ(CURPOS(i));
                error->cursor[i].base = I915_READ(CURBASE(i));
@@ -7415,9 +7796,11 @@ intel_display_print_error_state(struct seq_file *m,
                                struct drm_device *dev,
                                struct intel_display_error_state *error)
 {
+       drm_i915_private_t *dev_priv = dev->dev_private;
        int i;
 
-       for (i = 0; i < 2; i++) {
+       seq_printf(m, "Num Pipes: %d\n", dev_priv->num_pipe);
+       for_each_pipe(i) {
                seq_printf(m, "Pipe [%d]:\n", i);
                seq_printf(m, "  CONF: %08x\n", error->pipe[i].conf);
                seq_printf(m, "  SRC: %08x\n", error->pipe[i].source);