]> rtime.felk.cvut.cz Git - linux-imx.git/blobdiff - drivers/gpu/drm/i915/intel_ddi.c
drm/i915: add HAS_DDI check
[linux-imx.git] / drivers / gpu / drm / i915 / intel_ddi.c
index a78860a04a5664ccd51c9e8a7936278089344234..197dede1870af8cdf1a54bd41833debc2a69b585 100644 (file)
@@ -60,14 +60,18 @@ static const u32 hsw_ddi_translations_fdi[] = {
 
 static enum port intel_ddi_get_encoder_port(struct intel_encoder *intel_encoder)
 {
+       struct drm_encoder *encoder = &intel_encoder->base;
        int type = intel_encoder->type;
 
-       if (type == INTEL_OUTPUT_HDMI) {
-               struct intel_hdmi *intel_hdmi =
-                       enc_to_intel_hdmi(&intel_encoder->base);
-               return intel_hdmi->ddi_port;
+       if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP ||
+           type == INTEL_OUTPUT_HDMI || type == INTEL_OUTPUT_UNKNOWN) {
+               struct intel_digital_port *intel_dig_port =
+                       enc_to_dig_port(encoder);
+               return intel_dig_port->port;
+
        } else if (type == INTEL_OUTPUT_ANALOG) {
                return PORT_E;
+
        } else {
                DRM_ERROR("Invalid DDI encoder type %d\n", type);
                BUG();
@@ -80,7 +84,8 @@ static enum port intel_ddi_get_encoder_port(struct intel_encoder *intel_encoder)
  * in either FDI or DP modes only, as HDMI connections will work with both
  * of those
  */
-void intel_prepare_ddi_buffers(struct drm_device *dev, enum port port, bool use_fdi_mode)
+static void intel_prepare_ddi_buffers(struct drm_device *dev, enum port port,
+                                     bool use_fdi_mode)
 {
        struct drm_i915_private *dev_priv = dev->dev_private;
        u32 reg;
@@ -110,7 +115,7 @@ void intel_prepare_ddi(struct drm_device *dev)
 {
        int port;
 
-       if (IS_HASWELL(dev)) {
+       if (HAS_DDI(dev)) {
                for (port = PORT_A; port < PORT_E; port++)
                        intel_prepare_ddi_buffers(dev, port, false);
 
@@ -149,11 +154,34 @@ void hsw_fdi_link_train(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;
-       u32 reg, temp, i;
+       u32 temp, i, rx_ctl_val;
 
-       /* Start the training iterating through available voltages and emphasis */
-       for (i=0; i < ARRAY_SIZE(hsw_ddi_buf_ctl_values); i++) {
+       /* Set the FDI_RX_MISC pwrdn lanes and the 2 workarounds listed at the
+        * mode set "sequence for CRT port" document:
+        * - TP1 to TP2 time with the default value
+        * - FDI delay to 90h
+        */
+       I915_WRITE(_FDI_RXA_MISC, FDI_RX_PWRDN_LANE1_VAL(2) |
+                                 FDI_RX_PWRDN_LANE0_VAL(2) |
+                                 FDI_RX_TP1_TO_TP2_48 | FDI_RX_FDI_DELAY_90);
+
+       /* Enable the PCH Receiver FDI PLL */
+       rx_ctl_val = FDI_RX_PLL_ENABLE | FDI_RX_ENHANCE_FRAME_ENABLE |
+                    ((intel_crtc->fdi_lanes - 1) << 19);
+       I915_WRITE(_FDI_RXA_CTL, rx_ctl_val);
+       POSTING_READ(_FDI_RXA_CTL);
+       udelay(220);
+
+       /* Switch from Rawclk to PCDclk */
+       rx_ctl_val |= FDI_PCDCLK;
+       I915_WRITE(_FDI_RXA_CTL, rx_ctl_val);
+
+       /* Configure Port Clock Select */
+       I915_WRITE(PORT_CLK_SEL(PORT_E), intel_crtc->ddi_pll_sel);
+
+       /* Start the training iterating through available voltages and emphasis,
+        * testing each value twice. */
+       for (i = 0; i < ARRAY_SIZE(hsw_ddi_buf_ctl_values) * 2; i++) {
                /* Configure DP_TP_CTL with auto-training */
                I915_WRITE(DP_TP_CTL(PORT_E),
                                        DP_TP_CTL_FDI_AUTOTRAIN |
@@ -162,94 +190,63 @@ void hsw_fdi_link_train(struct drm_crtc *crtc)
                                        DP_TP_CTL_ENABLE);
 
                /* Configure and enable DDI_BUF_CTL for DDI E with next voltage */
-               temp = I915_READ(DDI_BUF_CTL(PORT_E));
-               temp = (temp & ~DDI_BUF_EMP_MASK);
                I915_WRITE(DDI_BUF_CTL(PORT_E),
-                               temp |
-                               DDI_BUF_CTL_ENABLE |
-                               DDI_PORT_WIDTH_X2 |
-                               hsw_ddi_buf_ctl_values[i]);
+                          DDI_BUF_CTL_ENABLE |
+                          ((intel_crtc->fdi_lanes - 1) << 1) |
+                          hsw_ddi_buf_ctl_values[i / 2]);
+               POSTING_READ(DDI_BUF_CTL(PORT_E));
 
                udelay(600);
 
-               /* We need to program FDI_RX_MISC with the default TP1 to TP2
-                * values before enabling the receiver, and configure the delay
-                * for the FDI timing generator to 90h. Luckily, all the other
-                * bits are supposed to be zeroed, so we can write those values
-                * directly.
-                */
-               I915_WRITE(FDI_RX_MISC(pipe), FDI_RX_TP1_TO_TP2_48 |
-                               FDI_RX_FDI_DELAY_90);
-
-               /* Enable CPU FDI Receiver with auto-training */
-               reg = FDI_RX_CTL(pipe);
-               I915_WRITE(reg,
-                               I915_READ(reg) |
-                                       FDI_LINK_TRAIN_AUTO |
-                                       FDI_RX_ENABLE |
-                                       FDI_LINK_TRAIN_PATTERN_1_CPT |
-                                       FDI_RX_ENHANCE_FRAME_ENABLE |
-                                       FDI_PORT_WIDTH_2X_LPT |
-                                       FDI_RX_PLL_ENABLE);
-               POSTING_READ(reg);
-               udelay(100);
+               /* Program PCH FDI Receiver TU */
+               I915_WRITE(_FDI_RXA_TUSIZE1, TU_SIZE(64));
+
+               /* Enable PCH FDI Receiver with auto-training */
+               rx_ctl_val |= FDI_RX_ENABLE | FDI_LINK_TRAIN_AUTO;
+               I915_WRITE(_FDI_RXA_CTL, rx_ctl_val);
+               POSTING_READ(_FDI_RXA_CTL);
+
+               /* Wait for FDI receiver lane calibration */
+               udelay(30);
+
+               /* Unset FDI_RX_MISC pwrdn lanes */
+               temp = I915_READ(_FDI_RXA_MISC);
+               temp &= ~(FDI_RX_PWRDN_LANE1_MASK | FDI_RX_PWRDN_LANE0_MASK);
+               I915_WRITE(_FDI_RXA_MISC, temp);
+               POSTING_READ(_FDI_RXA_MISC);
+
+               /* Wait for FDI auto training time */
+               udelay(5);
 
                temp = I915_READ(DP_TP_STATUS(PORT_E));
                if (temp & DP_TP_STATUS_AUTOTRAIN_DONE) {
-                       DRM_DEBUG_DRIVER("BUF_CTL training done on %d step\n", i);
+                       DRM_DEBUG_KMS("FDI link training done on step %d\n", i);
 
                        /* Enable normal pixel sending for FDI */
                        I915_WRITE(DP_TP_CTL(PORT_E),
-                                               DP_TP_CTL_FDI_AUTOTRAIN |
-                                               DP_TP_CTL_LINK_TRAIN_NORMAL |
-                                               DP_TP_CTL_ENHANCED_FRAME_ENABLE |
-                                               DP_TP_CTL_ENABLE);
-
-                       break;
-               } else {
-                       DRM_ERROR("Error training BUF_CTL %d\n", i);
+                                  DP_TP_CTL_FDI_AUTOTRAIN |
+                                  DP_TP_CTL_LINK_TRAIN_NORMAL |
+                                  DP_TP_CTL_ENHANCED_FRAME_ENABLE |
+                                  DP_TP_CTL_ENABLE);
 
-                       /* Disable DP_TP_CTL and FDI_RX_CTL) and retry */
-                       I915_WRITE(DP_TP_CTL(PORT_E),
-                                       I915_READ(DP_TP_CTL(PORT_E)) &
-                                               ~DP_TP_CTL_ENABLE);
-                       I915_WRITE(FDI_RX_CTL(pipe),
-                                       I915_READ(FDI_RX_CTL(pipe)) &
-                                               ~FDI_RX_PLL_ENABLE);
-                       continue;
+                       return;
                }
-       }
 
-       DRM_DEBUG_KMS("FDI train done.\n");
-}
+               /* Disable DP_TP_CTL and FDI_RX_CTL and retry */
+               I915_WRITE(DP_TP_CTL(PORT_E),
+                          I915_READ(DP_TP_CTL(PORT_E)) & ~DP_TP_CTL_ENABLE);
 
-/* For DDI connections, it is possible to support different outputs over the
- * same DDI port, such as HDMI or DP or even VGA via FDI. So we don't know by
- * the time the output is detected what exactly is on the other end of it. This
- * function aims at providing support for this detection and proper output
- * configuration.
- */
-void intel_ddi_init(struct drm_device *dev, enum port port)
-{
-       /* For now, we don't do any proper output detection and assume that we
-        * handle HDMI only */
+               rx_ctl_val &= ~FDI_RX_ENABLE;
+               I915_WRITE(_FDI_RXA_CTL, rx_ctl_val);
 
-       switch(port){
-       case PORT_A:
-               /* We don't handle eDP and DP yet */
-               DRM_DEBUG_DRIVER("Found digital output on DDI port A\n");
-               break;
-       /* Assume that the  ports B, C and D are working in HDMI mode for now */
-       case PORT_B:
-       case PORT_C:
-       case PORT_D:
-               intel_hdmi_init(dev, DDI_BUF_CTL(port), port);
-               break;
-       default:
-               DRM_DEBUG_DRIVER("No handlers defined for port %d, skipping DDI initialization\n",
-                               port);
-               break;
+               /* Reset FDI_RX_MISC pwrdn lanes */
+               temp = I915_READ(_FDI_RXA_MISC);
+               temp &= ~(FDI_RX_PWRDN_LANE1_MASK | FDI_RX_PWRDN_LANE0_MASK);
+               temp |= FDI_RX_PWRDN_LANE1_VAL(2) | FDI_RX_PWRDN_LANE0_VAL(2);
+               I915_WRITE(_FDI_RXA_MISC, temp);
        }
+
+       DRM_ERROR("FDI link training failed!\n");
 }
 
 /* WRPLL clock dividers */
@@ -638,34 +635,70 @@ static const struct wrpll_tmds_clock wrpll_tmds_clock_table[] = {
        {298000,        2,      21,     19},
 };
 
-void intel_ddi_mode_set(struct drm_encoder *encoder,
-                               struct drm_display_mode *mode,
-                               struct drm_display_mode *adjusted_mode)
+static void intel_ddi_mode_set(struct drm_encoder *encoder,
+                              struct drm_display_mode *mode,
+                              struct drm_display_mode *adjusted_mode)
 {
        struct drm_crtc *crtc = encoder->crtc;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
-       struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
-       int port = intel_hdmi->ddi_port;
+       struct intel_encoder *intel_encoder = to_intel_encoder(encoder);
+       int port = intel_ddi_get_encoder_port(intel_encoder);
        int pipe = intel_crtc->pipe;
+       int type = intel_encoder->type;
 
-       /* On Haswell, we need to enable the clocks and prepare DDI function to
-        * work in HDMI mode for this pipe.
-        */
-       DRM_DEBUG_KMS("Preparing HDMI DDI mode for Haswell on port %c, pipe %c\n", port_name(port), pipe_name(pipe));
+       DRM_DEBUG_KMS("Preparing DDI mode for Haswell on port %c, pipe %c\n",
+                     port_name(port), pipe_name(pipe));
 
-       if (intel_hdmi->has_audio) {
-               /* Proper support for digital audio needs a new logic and a new set
-                * of registers, so we leave it for future patch bombing.
-                */
-               DRM_DEBUG_DRIVER("HDMI audio on pipe %c on DDI\n",
-                                pipe_name(intel_crtc->pipe));
+       if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) {
+               struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
 
-               /* write eld */
-               DRM_DEBUG_DRIVER("HDMI audio: write eld information\n");
-               intel_write_eld(encoder, adjusted_mode);
-       }
+               intel_dp->DP = DDI_BUF_CTL_ENABLE | DDI_BUF_EMP_400MV_0DB_HSW;
+               switch (intel_dp->lane_count) {
+               case 1:
+                       intel_dp->DP |= DDI_PORT_WIDTH_X1;
+                       break;
+               case 2:
+                       intel_dp->DP |= DDI_PORT_WIDTH_X2;
+                       break;
+               case 4:
+                       intel_dp->DP |= DDI_PORT_WIDTH_X4;
+                       break;
+               default:
+                       intel_dp->DP |= DDI_PORT_WIDTH_X4;
+                       WARN(1, "Unexpected DP lane count %d\n",
+                            intel_dp->lane_count);
+                       break;
+               }
+
+               if (intel_dp->has_audio) {
+                       DRM_DEBUG_DRIVER("DP audio on pipe %c on DDI\n",
+                                        pipe_name(intel_crtc->pipe));
+
+                       /* write eld */
+                       DRM_DEBUG_DRIVER("DP audio: write eld information\n");
+                       intel_write_eld(encoder, adjusted_mode);
+               }
 
-       intel_hdmi->set_infoframes(encoder, adjusted_mode);
+               intel_dp_init_link_config(intel_dp);
+
+       } else if (type == INTEL_OUTPUT_HDMI) {
+               struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
+
+               if (intel_hdmi->has_audio) {
+                       /* Proper support for digital audio needs a new logic
+                        * and a new set of registers, so we leave it for future
+                        * patch bombing.
+                        */
+                       DRM_DEBUG_DRIVER("HDMI audio on pipe %c on DDI\n",
+                                        pipe_name(intel_crtc->pipe));
+
+                       /* write eld */
+                       DRM_DEBUG_DRIVER("HDMI audio: write eld information\n");
+                       intel_write_eld(encoder, adjusted_mode);
+               }
+
+               intel_hdmi->set_infoframes(encoder, adjusted_mode);
+       }
 }
 
 static struct intel_encoder *
@@ -763,6 +796,7 @@ bool intel_ddi_pll_mode_set(struct drm_crtc *crtc, int clock)
 {
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        struct intel_encoder *intel_encoder = intel_ddi_get_crtc_encoder(crtc);
+       struct drm_encoder *encoder = &intel_encoder->base;
        struct drm_i915_private *dev_priv = crtc->dev->dev_private;
        struct intel_ddi_plls *plls = &dev_priv->ddi_plls;
        int type = intel_encoder->type;
@@ -773,7 +807,29 @@ bool intel_ddi_pll_mode_set(struct drm_crtc *crtc, int clock)
 
        intel_ddi_put_crtc_pll(crtc);
 
-       if (type == INTEL_OUTPUT_HDMI) {
+       if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) {
+               struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
+
+               switch (intel_dp->link_bw) {
+               case DP_LINK_BW_1_62:
+                       intel_crtc->ddi_pll_sel = PORT_CLK_SEL_LCPLL_810;
+                       break;
+               case DP_LINK_BW_2_7:
+                       intel_crtc->ddi_pll_sel = PORT_CLK_SEL_LCPLL_1350;
+                       break;
+               case DP_LINK_BW_5_4:
+                       intel_crtc->ddi_pll_sel = PORT_CLK_SEL_LCPLL_2700;
+                       break;
+               default:
+                       DRM_ERROR("Link bandwidth %d unsupported\n",
+                                 intel_dp->link_bw);
+                       return false;
+               }
+
+               /* We don't need to turn any PLL on because we'll use LCPLL. */
+               return true;
+
+       } else if (type == INTEL_OUTPUT_HDMI) {
                int p, n2, r2;
 
                if (plls->wrpll1_refcount == 0) {
@@ -827,97 +883,233 @@ bool intel_ddi_pll_mode_set(struct drm_crtc *crtc, int clock)
        return true;
 }
 
+void intel_ddi_set_pipe_settings(struct drm_crtc *crtc)
+{
+       struct drm_i915_private *dev_priv = crtc->dev->dev_private;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       struct intel_encoder *intel_encoder = intel_ddi_get_crtc_encoder(crtc);
+       enum transcoder cpu_transcoder = intel_crtc->cpu_transcoder;
+       int type = intel_encoder->type;
+       uint32_t temp;
+
+       if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) {
+
+               temp = TRANS_MSA_SYNC_CLK;
+               switch (intel_crtc->bpp) {
+               case 18:
+                       temp |= TRANS_MSA_6_BPC;
+                       break;
+               case 24:
+                       temp |= TRANS_MSA_8_BPC;
+                       break;
+               case 30:
+                       temp |= TRANS_MSA_10_BPC;
+                       break;
+               case 36:
+                       temp |= TRANS_MSA_12_BPC;
+                       break;
+               default:
+                       temp |= TRANS_MSA_8_BPC;
+                       WARN(1, "%d bpp unsupported by DDI function\n",
+                            intel_crtc->bpp);
+               }
+               I915_WRITE(TRANS_MSA_MISC(cpu_transcoder), temp);
+       }
+}
+
 void intel_ddi_enable_pipe_func(struct drm_crtc *crtc)
 {
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        struct intel_encoder *intel_encoder = intel_ddi_get_crtc_encoder(crtc);
+       struct drm_encoder *encoder = &intel_encoder->base;
        struct drm_i915_private *dev_priv = crtc->dev->dev_private;
        enum pipe pipe = intel_crtc->pipe;
+       enum transcoder cpu_transcoder = intel_crtc->cpu_transcoder;
+       enum port port = intel_ddi_get_encoder_port(intel_encoder);
+       int type = intel_encoder->type;
        uint32_t temp;
 
-       /* Enable PIPE_DDI_FUNC_CTL for the pipe to work in HDMI mode */
-       temp = PIPE_DDI_FUNC_ENABLE;
+       /* Enable TRANS_DDI_FUNC_CTL for the pipe to work in HDMI mode */
+       temp = TRANS_DDI_FUNC_ENABLE;
+       temp |= TRANS_DDI_SELECT_PORT(port);
 
        switch (intel_crtc->bpp) {
        case 18:
-               temp |= PIPE_DDI_BPC_6;
+               temp |= TRANS_DDI_BPC_6;
                break;
        case 24:
-               temp |= PIPE_DDI_BPC_8;
+               temp |= TRANS_DDI_BPC_8;
                break;
        case 30:
-               temp |= PIPE_DDI_BPC_10;
+               temp |= TRANS_DDI_BPC_10;
                break;
        case 36:
-               temp |= PIPE_DDI_BPC_12;
+               temp |= TRANS_DDI_BPC_12;
                break;
        default:
-               WARN(1, "%d bpp unsupported by pipe DDI function\n",
+               WARN(1, "%d bpp unsupported by transcoder DDI function\n",
                     intel_crtc->bpp);
        }
 
        if (crtc->mode.flags & DRM_MODE_FLAG_PVSYNC)
-               temp |= PIPE_DDI_PVSYNC;
+               temp |= TRANS_DDI_PVSYNC;
        if (crtc->mode.flags & DRM_MODE_FLAG_PHSYNC)
-               temp |= PIPE_DDI_PHSYNC;
+               temp |= TRANS_DDI_PHSYNC;
+
+       if (cpu_transcoder == TRANSCODER_EDP) {
+               switch (pipe) {
+               case PIPE_A:
+                       temp |= TRANS_DDI_EDP_INPUT_A_ONOFF;
+                       break;
+               case PIPE_B:
+                       temp |= TRANS_DDI_EDP_INPUT_B_ONOFF;
+                       break;
+               case PIPE_C:
+                       temp |= TRANS_DDI_EDP_INPUT_C_ONOFF;
+                       break;
+               default:
+                       BUG();
+                       break;
+               }
+       }
 
-       if (intel_encoder->type == INTEL_OUTPUT_HDMI) {
-               struct intel_hdmi *intel_hdmi =
-                       enc_to_intel_hdmi(&intel_encoder->base);
+       if (type == INTEL_OUTPUT_HDMI) {
+               struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
 
                if (intel_hdmi->has_hdmi_sink)
-                       temp |= PIPE_DDI_MODE_SELECT_HDMI;
+                       temp |= TRANS_DDI_MODE_SELECT_HDMI;
                else
-                       temp |= PIPE_DDI_MODE_SELECT_DVI;
+                       temp |= TRANS_DDI_MODE_SELECT_DVI;
+
+       } else if (type == INTEL_OUTPUT_ANALOG) {
+               temp |= TRANS_DDI_MODE_SELECT_FDI;
+               temp |= (intel_crtc->fdi_lanes - 1) << 1;
+
+       } else if (type == INTEL_OUTPUT_DISPLAYPORT ||
+                  type == INTEL_OUTPUT_EDP) {
+               struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
+
+               temp |= TRANS_DDI_MODE_SELECT_DP_SST;
+
+               switch (intel_dp->lane_count) {
+               case 1:
+                       temp |= TRANS_DDI_PORT_WIDTH_X1;
+                       break;
+               case 2:
+                       temp |= TRANS_DDI_PORT_WIDTH_X2;
+                       break;
+               case 4:
+                       temp |= TRANS_DDI_PORT_WIDTH_X4;
+                       break;
+               default:
+                       temp |= TRANS_DDI_PORT_WIDTH_X4;
+                       WARN(1, "Unsupported lane count %d\n",
+                            intel_dp->lane_count);
+               }
 
-               temp |= PIPE_DDI_SELECT_PORT(intel_hdmi->ddi_port);
-       } else if (intel_encoder->type == INTEL_OUTPUT_ANALOG) {
-               temp |= PIPE_DDI_MODE_SELECT_FDI;
-               temp |= PIPE_DDI_SELECT_PORT(PORT_E);
        } else {
                WARN(1, "Invalid encoder type %d for pipe %d\n",
                     intel_encoder->type, pipe);
        }
 
-       I915_WRITE(DDI_FUNC_CTL(pipe), temp);
+       I915_WRITE(TRANS_DDI_FUNC_CTL(cpu_transcoder), temp);
 }
 
-void intel_ddi_disable_pipe_func(struct drm_i915_private *dev_priv,
-                                enum pipe pipe)
+void intel_ddi_disable_transcoder_func(struct drm_i915_private *dev_priv,
+                                      enum transcoder cpu_transcoder)
 {
-       uint32_t reg = DDI_FUNC_CTL(pipe);
+       uint32_t reg = TRANS_DDI_FUNC_CTL(cpu_transcoder);
        uint32_t val = I915_READ(reg);
 
-       val &= ~(PIPE_DDI_FUNC_ENABLE | PIPE_DDI_PORT_MASK);
-       val |= PIPE_DDI_PORT_NONE;
+       val &= ~(TRANS_DDI_FUNC_ENABLE | TRANS_DDI_PORT_MASK);
+       val |= TRANS_DDI_PORT_NONE;
        I915_WRITE(reg, val);
 }
 
+bool intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector)
+{
+       struct drm_device *dev = intel_connector->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct intel_encoder *intel_encoder = intel_connector->encoder;
+       int type = intel_connector->base.connector_type;
+       enum port port = intel_ddi_get_encoder_port(intel_encoder);
+       enum pipe pipe = 0;
+       enum transcoder cpu_transcoder;
+       uint32_t tmp;
+
+       if (!intel_encoder->get_hw_state(intel_encoder, &pipe))
+               return false;
+
+       if (port == PORT_A)
+               cpu_transcoder = TRANSCODER_EDP;
+       else
+               cpu_transcoder = pipe;
+
+       tmp = I915_READ(TRANS_DDI_FUNC_CTL(cpu_transcoder));
+
+       switch (tmp & TRANS_DDI_MODE_SELECT_MASK) {
+       case TRANS_DDI_MODE_SELECT_HDMI:
+       case TRANS_DDI_MODE_SELECT_DVI:
+               return (type == DRM_MODE_CONNECTOR_HDMIA);
+
+       case TRANS_DDI_MODE_SELECT_DP_SST:
+               if (type == DRM_MODE_CONNECTOR_eDP)
+                       return true;
+       case TRANS_DDI_MODE_SELECT_DP_MST:
+               return (type == DRM_MODE_CONNECTOR_DisplayPort);
+
+       case TRANS_DDI_MODE_SELECT_FDI:
+               return (type == DRM_MODE_CONNECTOR_VGA);
+
+       default:
+               return false;
+       }
+}
+
 bool intel_ddi_get_hw_state(struct intel_encoder *encoder,
                            enum pipe *pipe)
 {
        struct drm_device *dev = encoder->base.dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
+       enum port port = intel_ddi_get_encoder_port(encoder);
        u32 tmp;
        int i;
 
-       tmp = I915_READ(DDI_BUF_CTL(intel_hdmi->ddi_port));
+       tmp = I915_READ(DDI_BUF_CTL(port));
 
        if (!(tmp & DDI_BUF_CTL_ENABLE))
                return false;
 
-       for_each_pipe(i) {
-               tmp = I915_READ(DDI_FUNC_CTL(i));
+       if (port == PORT_A) {
+               tmp = I915_READ(TRANS_DDI_FUNC_CTL(TRANSCODER_EDP));
 
-               if ((tmp & PIPE_DDI_PORT_MASK)
-                   == PIPE_DDI_SELECT_PORT(intel_hdmi->ddi_port)) {
-                       *pipe = i;
-                       return true;
+               switch (tmp & TRANS_DDI_EDP_INPUT_MASK) {
+               case TRANS_DDI_EDP_INPUT_A_ON:
+               case TRANS_DDI_EDP_INPUT_A_ONOFF:
+                       *pipe = PIPE_A;
+                       break;
+               case TRANS_DDI_EDP_INPUT_B_ONOFF:
+                       *pipe = PIPE_B;
+                       break;
+               case TRANS_DDI_EDP_INPUT_C_ONOFF:
+                       *pipe = PIPE_C;
+                       break;
+               }
+
+               return true;
+       } else {
+               for (i = TRANSCODER_A; i <= TRANSCODER_C; i++) {
+                       tmp = I915_READ(TRANS_DDI_FUNC_CTL(i));
+
+                       if ((tmp & TRANS_DDI_PORT_MASK)
+                           == TRANS_DDI_SELECT_PORT(port)) {
+                               *pipe = i;
+                               return true;
+                       }
                }
        }
 
-       DRM_DEBUG_KMS("No pipe for ddi port %i found\n", intel_hdmi->ddi_port);
+       DRM_DEBUG_KMS("No pipe for ddi port %i found\n", port);
 
        return true;
 }
@@ -927,13 +1119,20 @@ static uint32_t intel_ddi_get_crtc_pll(struct drm_i915_private *dev_priv,
 {
        uint32_t temp, ret;
        enum port port;
+       enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv,
+                                                                     pipe);
        int i;
 
-       temp = I915_READ(DDI_FUNC_CTL(pipe));
-       temp &= PIPE_DDI_PORT_MASK;
-       for (i = PORT_A; i <= PORT_E; i++)
-               if (temp == PIPE_DDI_SELECT_PORT(i))
-                       port = i;
+       if (cpu_transcoder == TRANSCODER_EDP) {
+               port = PORT_A;
+       } else {
+               temp = I915_READ(TRANS_DDI_FUNC_CTL(cpu_transcoder));
+               temp &= TRANS_DDI_PORT_MASK;
+
+               for (i = PORT_B; i <= PORT_E; i++)
+                       if (temp == TRANS_DDI_SELECT_PORT(i))
+                               port = i;
+       }
 
        ret = I915_READ(PORT_CLK_SEL(port));
 
@@ -979,27 +1178,49 @@ void intel_ddi_enable_pipe_clock(struct intel_crtc *intel_crtc)
        struct drm_i915_private *dev_priv = crtc->dev->dev_private;
        struct intel_encoder *intel_encoder = intel_ddi_get_crtc_encoder(crtc);
        enum port port = intel_ddi_get_encoder_port(intel_encoder);
+       enum transcoder cpu_transcoder = intel_crtc->cpu_transcoder;
 
-       I915_WRITE(PIPE_CLK_SEL(intel_crtc->pipe), PIPE_CLK_SEL_PORT(port));
+       if (cpu_transcoder != TRANSCODER_EDP)
+               I915_WRITE(TRANS_CLK_SEL(cpu_transcoder),
+                          TRANS_CLK_SEL_PORT(port));
 }
 
 void intel_ddi_disable_pipe_clock(struct intel_crtc *intel_crtc)
 {
        struct drm_i915_private *dev_priv = intel_crtc->base.dev->dev_private;
+       enum transcoder cpu_transcoder = intel_crtc->cpu_transcoder;
 
-       I915_WRITE(PIPE_CLK_SEL(intel_crtc->pipe), PIPE_CLK_SEL_DISABLED);
+       if (cpu_transcoder != TRANSCODER_EDP)
+               I915_WRITE(TRANS_CLK_SEL(cpu_transcoder),
+                          TRANS_CLK_SEL_DISABLED);
 }
 
-void intel_ddi_pre_enable(struct intel_encoder *intel_encoder)
+static void intel_ddi_pre_enable(struct intel_encoder *intel_encoder)
 {
-       struct drm_crtc *crtc = intel_encoder->base.crtc;
-       struct drm_i915_private *dev_priv = crtc->dev->dev_private;
+       struct drm_encoder *encoder = &intel_encoder->base;
+       struct drm_crtc *crtc = encoder->crtc;
+       struct drm_i915_private *dev_priv = encoder->dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        enum port port = intel_ddi_get_encoder_port(intel_encoder);
+       int type = intel_encoder->type;
 
-       WARN_ON(intel_crtc->ddi_pll_sel == PORT_CLK_SEL_NONE);
+       if (type == INTEL_OUTPUT_EDP) {
+               struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
+               ironlake_edp_panel_vdd_on(intel_dp);
+               ironlake_edp_panel_on(intel_dp);
+               ironlake_edp_panel_vdd_off(intel_dp, true);
+       }
 
+       WARN_ON(intel_crtc->ddi_pll_sel == PORT_CLK_SEL_NONE);
        I915_WRITE(PORT_CLK_SEL(port), intel_crtc->ddi_pll_sel);
+
+       if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) {
+               struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
+
+               intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
+               intel_dp_start_link_train(intel_dp);
+               intel_dp_complete_link_train(intel_dp);
+       }
 }
 
 static void intel_wait_ddi_buf_idle(struct drm_i915_private *dev_priv,
@@ -1016,49 +1237,81 @@ static void intel_wait_ddi_buf_idle(struct drm_i915_private *dev_priv,
        DRM_ERROR("Timeout waiting for DDI BUF %c idle bit\n", port_name(port));
 }
 
-void intel_ddi_post_disable(struct intel_encoder *intel_encoder)
+static void intel_ddi_post_disable(struct intel_encoder *intel_encoder)
 {
        struct drm_encoder *encoder = &intel_encoder->base;
        struct drm_i915_private *dev_priv = encoder->dev->dev_private;
        enum port port = intel_ddi_get_encoder_port(intel_encoder);
+       int type = intel_encoder->type;
        uint32_t val;
+       bool wait = false;
 
        val = I915_READ(DDI_BUF_CTL(port));
        if (val & DDI_BUF_CTL_ENABLE) {
                val &= ~DDI_BUF_CTL_ENABLE;
                I915_WRITE(DDI_BUF_CTL(port), val);
+               wait = true;
+       }
+
+       val = I915_READ(DP_TP_CTL(port));
+       val &= ~(DP_TP_CTL_ENABLE | DP_TP_CTL_LINK_TRAIN_MASK);
+       val |= DP_TP_CTL_LINK_TRAIN_PAT1;
+       I915_WRITE(DP_TP_CTL(port), val);
+
+       if (wait)
                intel_wait_ddi_buf_idle(dev_priv, port);
+
+       if (type == INTEL_OUTPUT_EDP) {
+               struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
+               ironlake_edp_panel_vdd_on(intel_dp);
+               ironlake_edp_panel_off(intel_dp);
        }
 
        I915_WRITE(PORT_CLK_SEL(port), PORT_CLK_SEL_NONE);
 }
 
-void intel_enable_ddi(struct intel_encoder *encoder)
+static void intel_enable_ddi(struct intel_encoder *intel_encoder)
 {
-       struct drm_device *dev = encoder->base.dev;
+       struct drm_encoder *encoder = &intel_encoder->base;
+       struct drm_device *dev = encoder->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
-       struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
-       int port = intel_hdmi->ddi_port;
+       enum port port = intel_ddi_get_encoder_port(intel_encoder);
+       int type = intel_encoder->type;
 
-       /* Enable DDI_BUF_CTL. In HDMI/DVI mode, the port width,
-        * and swing/emphasis values are ignored so nothing special needs
-        * to be done besides enabling the port.
-        */
-       I915_WRITE(DDI_BUF_CTL(port), DDI_BUF_CTL_ENABLE);
+       if (type == INTEL_OUTPUT_HDMI) {
+               /* In HDMI/DVI mode, the port width, and swing/emphasis values
+                * are ignored so nothing special needs to be done besides
+                * enabling the port.
+                */
+               I915_WRITE(DDI_BUF_CTL(port), DDI_BUF_CTL_ENABLE);
+       } else if (type == INTEL_OUTPUT_EDP) {
+               struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
+
+               ironlake_edp_backlight_on(intel_dp);
+       }
 }
 
-void intel_disable_ddi(struct intel_encoder *encoder)
+static void intel_disable_ddi(struct intel_encoder *intel_encoder)
 {
-       /* This will be needed in the future, so leave it here for now */
+       struct drm_encoder *encoder = &intel_encoder->base;
+       int type = intel_encoder->type;
+
+       if (type == INTEL_OUTPUT_EDP) {
+               struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
+
+               ironlake_edp_backlight_off(intel_dp);
+       }
 }
 
-static int intel_ddi_get_cdclk_freq(struct drm_i915_private *dev_priv)
+int intel_ddi_get_cdclk_freq(struct drm_i915_private *dev_priv)
 {
        if (I915_READ(HSW_FUSE_STRAP) & HSW_CDCLK_LIMIT)
                return 450;
        else if ((I915_READ(LCPLL_CTL) & LCPLL_CLK_FREQ_MASK) ==
                 LCPLL_CLK_FREQ_450)
                return 450;
+       else if (IS_ULT(dev_priv->dev))
+               return 338;
        else
                return 540;
 }
@@ -1082,3 +1335,168 @@ void intel_ddi_pll_init(struct drm_device *dev)
        if (val & LCPLL_PLL_DISABLE)
                DRM_ERROR("LCPLL is disabled\n");
 }
+
+void intel_ddi_prepare_link_retrain(struct drm_encoder *encoder)
+{
+       struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder);
+       struct intel_dp *intel_dp = &intel_dig_port->dp;
+       struct drm_i915_private *dev_priv = encoder->dev->dev_private;
+       enum port port = intel_dig_port->port;
+       bool wait;
+       uint32_t val;
+
+       if (I915_READ(DP_TP_CTL(port)) & DP_TP_CTL_ENABLE) {
+               val = I915_READ(DDI_BUF_CTL(port));
+               if (val & DDI_BUF_CTL_ENABLE) {
+                       val &= ~DDI_BUF_CTL_ENABLE;
+                       I915_WRITE(DDI_BUF_CTL(port), val);
+                       wait = true;
+               }
+
+               val = I915_READ(DP_TP_CTL(port));
+               val &= ~(DP_TP_CTL_ENABLE | DP_TP_CTL_LINK_TRAIN_MASK);
+               val |= DP_TP_CTL_LINK_TRAIN_PAT1;
+               I915_WRITE(DP_TP_CTL(port), val);
+               POSTING_READ(DP_TP_CTL(port));
+
+               if (wait)
+                       intel_wait_ddi_buf_idle(dev_priv, port);
+       }
+
+       val = DP_TP_CTL_ENABLE | DP_TP_CTL_MODE_SST |
+             DP_TP_CTL_LINK_TRAIN_PAT1 | DP_TP_CTL_SCRAMBLE_DISABLE;
+       if (intel_dp->link_configuration[1] & DP_LANE_COUNT_ENHANCED_FRAME_EN)
+               val |= DP_TP_CTL_ENHANCED_FRAME_ENABLE;
+       I915_WRITE(DP_TP_CTL(port), val);
+       POSTING_READ(DP_TP_CTL(port));
+
+       intel_dp->DP |= DDI_BUF_CTL_ENABLE;
+       I915_WRITE(DDI_BUF_CTL(port), intel_dp->DP);
+       POSTING_READ(DDI_BUF_CTL(port));
+
+       udelay(600);
+}
+
+void intel_ddi_fdi_disable(struct drm_crtc *crtc)
+{
+       struct drm_i915_private *dev_priv = crtc->dev->dev_private;
+       struct intel_encoder *intel_encoder = intel_ddi_get_crtc_encoder(crtc);
+       uint32_t val;
+
+       intel_ddi_post_disable(intel_encoder);
+
+       val = I915_READ(_FDI_RXA_CTL);
+       val &= ~FDI_RX_ENABLE;
+       I915_WRITE(_FDI_RXA_CTL, val);
+
+       val = I915_READ(_FDI_RXA_MISC);
+       val &= ~(FDI_RX_PWRDN_LANE1_MASK | FDI_RX_PWRDN_LANE0_MASK);
+       val |= FDI_RX_PWRDN_LANE1_VAL(2) | FDI_RX_PWRDN_LANE0_VAL(2);
+       I915_WRITE(_FDI_RXA_MISC, val);
+
+       val = I915_READ(_FDI_RXA_CTL);
+       val &= ~FDI_PCDCLK;
+       I915_WRITE(_FDI_RXA_CTL, val);
+
+       val = I915_READ(_FDI_RXA_CTL);
+       val &= ~FDI_RX_PLL_ENABLE;
+       I915_WRITE(_FDI_RXA_CTL, val);
+}
+
+static void intel_ddi_hot_plug(struct intel_encoder *intel_encoder)
+{
+       struct intel_dp *intel_dp = enc_to_intel_dp(&intel_encoder->base);
+       int type = intel_encoder->type;
+
+       if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP)
+               intel_dp_check_link_status(intel_dp);
+}
+
+static void intel_ddi_destroy(struct drm_encoder *encoder)
+{
+       /* HDMI has nothing special to destroy, so we can go with this. */
+       intel_dp_encoder_destroy(encoder);
+}
+
+static bool intel_ddi_mode_fixup(struct drm_encoder *encoder,
+                                const struct drm_display_mode *mode,
+                                struct drm_display_mode *adjusted_mode)
+{
+       struct intel_encoder *intel_encoder = to_intel_encoder(encoder);
+       int type = intel_encoder->type;
+
+       WARN(type == INTEL_OUTPUT_UNKNOWN, "mode_fixup() on unknown output!\n");
+
+       if (type == INTEL_OUTPUT_HDMI)
+               return intel_hdmi_mode_fixup(encoder, mode, adjusted_mode);
+       else
+               return intel_dp_mode_fixup(encoder, mode, adjusted_mode);
+}
+
+static const struct drm_encoder_funcs intel_ddi_funcs = {
+       .destroy = intel_ddi_destroy,
+};
+
+static const struct drm_encoder_helper_funcs intel_ddi_helper_funcs = {
+       .mode_fixup = intel_ddi_mode_fixup,
+       .mode_set = intel_ddi_mode_set,
+       .disable = intel_encoder_noop,
+};
+
+void intel_ddi_init(struct drm_device *dev, enum port port)
+{
+       struct intel_digital_port *intel_dig_port;
+       struct intel_encoder *intel_encoder;
+       struct drm_encoder *encoder;
+       struct intel_connector *hdmi_connector = NULL;
+       struct intel_connector *dp_connector = NULL;
+
+       intel_dig_port = kzalloc(sizeof(struct intel_digital_port), GFP_KERNEL);
+       if (!intel_dig_port)
+               return;
+
+       dp_connector = kzalloc(sizeof(struct intel_connector), GFP_KERNEL);
+       if (!dp_connector) {
+               kfree(intel_dig_port);
+               return;
+       }
+
+       if (port != PORT_A) {
+               hdmi_connector = kzalloc(sizeof(struct intel_connector),
+                                        GFP_KERNEL);
+               if (!hdmi_connector) {
+                       kfree(dp_connector);
+                       kfree(intel_dig_port);
+                       return;
+               }
+       }
+
+       intel_encoder = &intel_dig_port->base;
+       encoder = &intel_encoder->base;
+
+       drm_encoder_init(dev, encoder, &intel_ddi_funcs,
+                        DRM_MODE_ENCODER_TMDS);
+       drm_encoder_helper_add(encoder, &intel_ddi_helper_funcs);
+
+       intel_encoder->enable = intel_enable_ddi;
+       intel_encoder->pre_enable = intel_ddi_pre_enable;
+       intel_encoder->disable = intel_disable_ddi;
+       intel_encoder->post_disable = intel_ddi_post_disable;
+       intel_encoder->get_hw_state = intel_ddi_get_hw_state;
+
+       intel_dig_port->port = port;
+       if (hdmi_connector)
+               intel_dig_port->hdmi.sdvox_reg = DDI_BUF_CTL(port);
+       else
+               intel_dig_port->hdmi.sdvox_reg = 0;
+       intel_dig_port->dp.output_reg = DDI_BUF_CTL(port);
+
+       intel_encoder->type = INTEL_OUTPUT_UNKNOWN;
+       intel_encoder->crtc_mask =  (1 << 0) | (1 << 1) | (1 << 2);
+       intel_encoder->cloneable = false;
+       intel_encoder->hot_plug = intel_ddi_hot_plug;
+
+       if (hdmi_connector)
+               intel_hdmi_init_connector(intel_dig_port, hdmi_connector);
+       intel_dp_init_connector(intel_dig_port, dp_connector);
+}