drm/i915: set/unset the DDI eDP backlight
[deliverable/linux.git] / drivers / gpu / drm / i915 / intel_ddi.c
index bfe375466a0e03b953022248e65cfafdf3a17874..efd1a728390ed8a32b7f858acac318d634d63dd1 100644 (file)
@@ -58,6 +58,28 @@ static const u32 hsw_ddi_translations_fdi[] = {
        0x00FFFFFF, 0x00040006          /* HDMI parameters */
 };
 
+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_DISPLAYPORT || type == INTEL_OUTPUT_EDP) {
+               struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
+               return intel_dp->port;
+
+       } else if (type == INTEL_OUTPUT_HDMI) {
+               struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
+               return intel_hdmi->ddi_port;
+
+       } else if (type == INTEL_OUTPUT_ANALOG) {
+               return PORT_E;
+
+       } else {
+               DRM_ERROR("Invalid DDI encoder type %d\n", type);
+               BUG();
+       }
+}
+
 /* On Haswell, DDI port buffers must be programmed with correct values
  * in advance. The buffer values are different for FDI and DP modes,
  * but the HDMI/DVI fields are shared among those. So we program the DDI
@@ -136,20 +158,6 @@ void hsw_fdi_link_train(struct drm_crtc *crtc)
        int pipe = intel_crtc->pipe;
        u32 reg, temp, i;
 
-       /* Configure CPU PLL, wait for warmup */
-       I915_WRITE(SPLL_CTL,
-                       SPLL_PLL_ENABLE |
-                       SPLL_PLL_FREQ_1350MHz |
-                       SPLL_PLL_SCC);
-
-       /* Use SPLL to drive the output when in FDI mode */
-       I915_WRITE(PORT_CLK_SEL(PORT_E),
-                       PORT_CLK_SEL_SPLL);
-       I915_WRITE(PIPE_CLK_SEL(pipe),
-                       PIPE_CLK_SEL_PORT(PORT_E));
-
-       udelay(20);
-
        /* Start the training iterating through available voltages and emphasis */
        for (i=0; i < ARRAY_SIZE(hsw_ddi_buf_ctl_values); i++) {
                /* Configure DP_TP_CTL with auto-training */
@@ -203,15 +211,6 @@ void hsw_fdi_link_train(struct drm_crtc *crtc)
                                                DP_TP_CTL_ENHANCED_FRAME_ENABLE |
                                                DP_TP_CTL_ENABLE);
 
-                       /* Enable PIPE_DDI_FUNC_CTL for the pipe to work in FDI mode */
-                       temp = I915_READ(DDI_FUNC_CTL(pipe));
-                       temp &= ~PIPE_DDI_PORT_MASK;
-                       temp |= PIPE_DDI_SELECT_PORT(PORT_E) |
-                                       PIPE_DDI_MODE_SELECT_FDI |
-                                       PIPE_DDI_FUNC_ENABLE |
-                                       PIPE_DDI_PORT_WIDTH_X2;
-                       I915_WRITE(DDI_FUNC_CTL(pipe),
-                                       temp);
                        break;
                } else {
                        DRM_ERROR("Error training BUF_CTL %d\n", i);
@@ -649,112 +648,383 @@ void intel_ddi_mode_set(struct drm_encoder *encoder,
                                struct drm_display_mode *mode,
                                struct drm_display_mode *adjusted_mode)
 {
-       struct drm_device *dev = encoder->dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
        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 p, n2, r2;
-       u32 temp, i;
+       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 (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) {
+               struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
+
+               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;
+               }
+
+               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 *
+intel_ddi_get_crtc_encoder(struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       struct intel_encoder *intel_encoder, *ret = NULL;
+       int num_encoders = 0;
+
+       for_each_encoder_on_crtc(dev, crtc, intel_encoder) {
+               ret = intel_encoder;
+               num_encoders++;
+       }
+
+       if (num_encoders != 1)
+               WARN(1, "%d encoders on crtc for pipe %d\n", num_encoders,
+                    intel_crtc->pipe);
+
+       BUG_ON(ret == NULL);
+       return ret;
+}
+
+void intel_ddi_put_crtc_pll(struct drm_crtc *crtc)
+{
+       struct drm_i915_private *dev_priv = crtc->dev->dev_private;
+       struct intel_ddi_plls *plls = &dev_priv->ddi_plls;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       uint32_t val;
+
+       switch (intel_crtc->ddi_pll_sel) {
+       case PORT_CLK_SEL_SPLL:
+               plls->spll_refcount--;
+               if (plls->spll_refcount == 0) {
+                       DRM_DEBUG_KMS("Disabling SPLL\n");
+                       val = I915_READ(SPLL_CTL);
+                       WARN_ON(!(val & SPLL_PLL_ENABLE));
+                       I915_WRITE(SPLL_CTL, val & ~SPLL_PLL_ENABLE);
+                       POSTING_READ(SPLL_CTL);
+               }
+               break;
+       case PORT_CLK_SEL_WRPLL1:
+               plls->wrpll1_refcount--;
+               if (plls->wrpll1_refcount == 0) {
+                       DRM_DEBUG_KMS("Disabling WRPLL 1\n");
+                       val = I915_READ(WRPLL_CTL1);
+                       WARN_ON(!(val & WRPLL_PLL_ENABLE));
+                       I915_WRITE(WRPLL_CTL1, val & ~WRPLL_PLL_ENABLE);
+                       POSTING_READ(WRPLL_CTL1);
+               }
+               break;
+       case PORT_CLK_SEL_WRPLL2:
+               plls->wrpll2_refcount--;
+               if (plls->wrpll2_refcount == 0) {
+                       DRM_DEBUG_KMS("Disabling WRPLL 2\n");
+                       val = I915_READ(WRPLL_CTL2);
+                       WARN_ON(!(val & WRPLL_PLL_ENABLE));
+                       I915_WRITE(WRPLL_CTL2, val & ~WRPLL_PLL_ENABLE);
+                       POSTING_READ(WRPLL_CTL2);
+               }
+               break;
+       }
+
+       WARN(plls->spll_refcount < 0, "Invalid SPLL refcount\n");
+       WARN(plls->wrpll1_refcount < 0, "Invalid WRPLL1 refcount\n");
+       WARN(plls->wrpll2_refcount < 0, "Invalid WRPLL2 refcount\n");
+
+       intel_crtc->ddi_pll_sel = PORT_CLK_SEL_NONE;
+}
+
+static void intel_ddi_calculate_wrpll(int clock, int *p, int *n2, int *r2)
+{
+       u32 i;
 
        for (i = 0; i < ARRAY_SIZE(wrpll_tmds_clock_table); i++)
-               if (crtc->mode.clock <= wrpll_tmds_clock_table[i].clock)
+               if (clock <= wrpll_tmds_clock_table[i].clock)
                        break;
 
        if (i == ARRAY_SIZE(wrpll_tmds_clock_table))
                i--;
 
-       p = wrpll_tmds_clock_table[i].p;
-       n2 = wrpll_tmds_clock_table[i].n2;
-       r2 = wrpll_tmds_clock_table[i].r2;
+       *p = wrpll_tmds_clock_table[i].p;
+       *n2 = wrpll_tmds_clock_table[i].n2;
+       *r2 = wrpll_tmds_clock_table[i].r2;
 
-       if (wrpll_tmds_clock_table[i].clock != crtc->mode.clock)
-               DRM_INFO("WR PLL: using settings for %dKHz on %dKHz mode\n",
-                        wrpll_tmds_clock_table[i].clock, crtc->mode.clock);
+       if (wrpll_tmds_clock_table[i].clock != clock)
+               DRM_INFO("WRPLL: using settings for %dKHz on %dKHz mode\n",
+                        wrpll_tmds_clock_table[i].clock, clock);
 
-       DRM_DEBUG_KMS("WR PLL: %dKHz refresh rate with p=%d, n2=%d r2=%d\n",
-                     crtc->mode.clock, p, n2, r2);
+       DRM_DEBUG_KMS("WRPLL: %dKHz refresh rate with p=%d, n2=%d r2=%d\n",
+                     clock, *p, *n2, *r2);
+}
 
-       /* Enable LCPLL if disabled */
-       temp = I915_READ(LCPLL_CTL);
-       if (temp & LCPLL_PLL_DISABLE)
-               I915_WRITE(LCPLL_CTL,
-                               temp & ~LCPLL_PLL_DISABLE);
+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;
+       enum pipe pipe = intel_crtc->pipe;
+       uint32_t reg, val;
 
-       /* Configure WR PLL 1, program the correct divider values for
-        * the desired frequency and wait for warmup */
-       I915_WRITE(WRPLL_CTL1,
-                       WRPLL_PLL_ENABLE |
-                       WRPLL_PLL_SELECT_LCPLL_2700 |
-                       WRPLL_DIVIDER_REFERENCE(r2) |
-                       WRPLL_DIVIDER_FEEDBACK(n2) |
-                       WRPLL_DIVIDER_POST(p));
+       /* TODO: reuse PLLs when possible (compare values) */
 
-       udelay(20);
+       intel_ddi_put_crtc_pll(crtc);
 
-       /* Use WRPLL1 clock to drive the output to the port, and tell the pipe to use
-        * this port for connection.
-        */
-       I915_WRITE(PORT_CLK_SEL(port),
-                       PORT_CLK_SEL_WRPLL1);
-       I915_WRITE(PIPE_CLK_SEL(pipe),
-                       PIPE_CLK_SEL_PORT(port));
+       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) {
+                       DRM_DEBUG_KMS("Using WRPLL 1 on pipe %c\n",
+                                     pipe_name(pipe));
+                       plls->wrpll1_refcount++;
+                       reg = WRPLL_CTL1;
+                       intel_crtc->ddi_pll_sel = PORT_CLK_SEL_WRPLL1;
+               } else if (plls->wrpll2_refcount == 0) {
+                       DRM_DEBUG_KMS("Using WRPLL 2 on pipe %c\n",
+                                     pipe_name(pipe));
+                       plls->wrpll2_refcount++;
+                       reg = WRPLL_CTL2;
+                       intel_crtc->ddi_pll_sel = PORT_CLK_SEL_WRPLL2;
+               } else {
+                       DRM_ERROR("No WRPLLs available!\n");
+                       return false;
+               }
+
+               WARN(I915_READ(reg) & WRPLL_PLL_ENABLE,
+                    "WRPLL already enabled\n");
+
+               intel_ddi_calculate_wrpll(clock, &p, &n2, &r2);
+
+               val = WRPLL_PLL_ENABLE | WRPLL_PLL_SELECT_LCPLL_2700 |
+                     WRPLL_DIVIDER_REFERENCE(r2) | WRPLL_DIVIDER_FEEDBACK(n2) |
+                     WRPLL_DIVIDER_POST(p);
+
+       } else if (type == INTEL_OUTPUT_ANALOG) {
+               if (plls->spll_refcount == 0) {
+                       DRM_DEBUG_KMS("Using SPLL on pipe %c\n",
+                                     pipe_name(pipe));
+                       plls->spll_refcount++;
+                       reg = SPLL_CTL;
+                       intel_crtc->ddi_pll_sel = PORT_CLK_SEL_SPLL;
+               }
 
+               WARN(I915_READ(reg) & SPLL_PLL_ENABLE,
+                    "SPLL already enabled\n");
+
+               val = SPLL_PLL_ENABLE | SPLL_PLL_FREQ_1350MHz | SPLL_PLL_SSC;
+
+       } else {
+               WARN(1, "Invalid DDI encoder type %d\n", type);
+               return false;
+       }
+
+       I915_WRITE(reg, val);
        udelay(20);
 
-       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));
+       return true;
+}
 
-               /* write eld */
-               DRM_DEBUG_DRIVER("HDMI audio: write eld information\n");
-               intel_write_eld(encoder, adjusted_mode);
+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);
        }
+}
 
-       /* Enable PIPE_DDI_FUNC_CTL for the pipe to work in HDMI mode */
-       temp = PIPE_DDI_FUNC_ENABLE | PIPE_DDI_SELECT_PORT(port);
+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;
+       int type = intel_encoder->type;
+       uint32_t temp;
+
+       /* Enable TRANS_DDI_FUNC_CTL for the pipe to work in HDMI mode */
+       temp = TRANS_DDI_FUNC_ENABLE;
 
        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 (intel_hdmi->has_hdmi_sink)
-               temp |= PIPE_DDI_MODE_SELECT_HDMI;
-       else
-               temp |= PIPE_DDI_MODE_SELECT_DVI;
+       if (crtc->mode.flags & DRM_MODE_FLAG_PVSYNC)
+               temp |= TRANS_DDI_PVSYNC;
+       if (crtc->mode.flags & DRM_MODE_FLAG_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 (type == INTEL_OUTPUT_HDMI) {
+               struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
+
+               if (intel_hdmi->has_hdmi_sink)
+                       temp |= TRANS_DDI_MODE_SELECT_HDMI;
+               else
+                       temp |= TRANS_DDI_MODE_SELECT_DVI;
 
-       if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
-               temp |= PIPE_DDI_PVSYNC;
-       if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
-               temp |= PIPE_DDI_PHSYNC;
+               temp |= TRANS_DDI_SELECT_PORT(intel_hdmi->ddi_port);
 
-       I915_WRITE(DDI_FUNC_CTL(pipe), temp);
+       } else if (type == INTEL_OUTPUT_ANALOG) {
+               temp |= TRANS_DDI_MODE_SELECT_FDI;
+               temp |= TRANS_DDI_SELECT_PORT(PORT_E);
 
-       intel_hdmi->set_infoframes(encoder, adjusted_mode);
+       } 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;
+               temp |= TRANS_DDI_SELECT_PORT(intel_dp->port);
+
+               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);
+               }
+
+       } else {
+               WARN(1, "Invalid encoder type %d for pipe %d\n",
+                    intel_encoder->type, pipe);
+       }
+
+       I915_WRITE(TRANS_DDI_FUNC_CTL(cpu_transcoder), temp);
+}
+
+void intel_ddi_disable_transcoder_func(struct drm_i915_private *dev_priv,
+                                      enum transcoder cpu_transcoder)
+{
+       uint32_t reg = TRANS_DDI_FUNC_CTL(cpu_transcoder);
+       uint32_t val = I915_READ(reg);
+
+       val &= ~(TRANS_DDI_FUNC_ENABLE | TRANS_DDI_PORT_MASK);
+       val |= TRANS_DDI_PORT_NONE;
+       I915_WRITE(reg, val);
 }
 
 bool intel_ddi_get_hw_state(struct intel_encoder *encoder,
@@ -762,58 +1032,291 @@ bool intel_ddi_get_hw_state(struct intel_encoder *encoder,
 {
        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;
 }
 
-void intel_enable_ddi(struct intel_encoder *encoder)
+static uint32_t intel_ddi_get_crtc_pll(struct drm_i915_private *dev_priv,
+                                      enum pipe pipe)
+{
+       uint32_t temp, ret;
+       enum port port;
+       enum transcoder cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv,
+                                                                     pipe);
+       int 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));
+
+       DRM_DEBUG_KMS("Pipe %c connected to port %c using clock 0x%08x\n",
+                     pipe_name(pipe), port_name(port), ret);
+
+       return ret;
+}
+
+void intel_ddi_setup_hw_pll_state(struct drm_device *dev)
 {
-       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);
-       int port = intel_hdmi->ddi_port;
-       u32 temp;
+       enum pipe pipe;
+       struct intel_crtc *intel_crtc;
 
-       temp = I915_READ(DDI_BUF_CTL(port));
-       temp |= DDI_BUF_CTL_ENABLE;
+       for_each_pipe(pipe) {
+               intel_crtc =
+                       to_intel_crtc(dev_priv->pipe_to_crtc_mapping[pipe]);
 
-       /* 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), temp);
+               if (!intel_crtc->active)
+                       continue;
+
+               intel_crtc->ddi_pll_sel = intel_ddi_get_crtc_pll(dev_priv,
+                                                                pipe);
+
+               switch (intel_crtc->ddi_pll_sel) {
+               case PORT_CLK_SEL_SPLL:
+                       dev_priv->ddi_plls.spll_refcount++;
+                       break;
+               case PORT_CLK_SEL_WRPLL1:
+                       dev_priv->ddi_plls.wrpll1_refcount++;
+                       break;
+               case PORT_CLK_SEL_WRPLL2:
+                       dev_priv->ddi_plls.wrpll2_refcount++;
+                       break;
+               }
+       }
 }
 
-void intel_disable_ddi(struct intel_encoder *encoder)
+void intel_ddi_enable_pipe_clock(struct intel_crtc *intel_crtc)
 {
-       struct drm_device *dev = encoder->base.dev;
+       struct drm_crtc *crtc = &intel_crtc->base;
+       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;
+
+       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;
+
+       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)
+{
+       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);
+
+       WARN_ON(intel_crtc->ddi_pll_sel == PORT_CLK_SEL_NONE);
+
+       I915_WRITE(PORT_CLK_SEL(port), intel_crtc->ddi_pll_sel);
+
+       if (intel_encoder->type == INTEL_OUTPUT_DISPLAYPORT) {
+               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,
+                                   enum port port)
+{
+       uint32_t reg = DDI_BUF_CTL(port);
+       int i;
+
+       for (i = 0; i < 8; i++) {
+               udelay(1);
+               if (I915_READ(reg) & DDI_BUF_IS_IDLE)
+                       return;
+       }
+       DRM_ERROR("Timeout waiting for DDI BUF %c idle bit\n", port_name(port));
+}
+
+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);
+       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);
+
+       I915_WRITE(PORT_CLK_SEL(port), PORT_CLK_SEL_NONE);
+}
+
+void intel_enable_ddi(struct intel_encoder *intel_encoder)
+{
+       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;
-       u32 temp;
+       enum port port = intel_ddi_get_encoder_port(intel_encoder);
+       int type = intel_encoder->type;
+
+       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 *intel_encoder)
+{
+       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);
+       }
+}
+
+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
+               return 540;
+}
+
+void intel_ddi_pll_init(struct drm_device *dev)
+{
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       uint32_t val = I915_READ(LCPLL_CTL);
+
+       /* The LCPLL register should be turned on by the BIOS. For now let's
+        * just check its state and print errors in case something is wrong.
+        * Don't even try to turn it on.
+        */
+
+       DRM_DEBUG_KMS("CDCLK running at %dMHz\n",
+                     intel_ddi_get_cdclk_freq(dev_priv));
+
+       if (val & LCPLL_CD_SOURCE_FCLK)
+               DRM_ERROR("CDCLK source is not LCPLL\n");
+
+       if (val & LCPLL_PLL_DISABLE)
+               DRM_ERROR("LCPLL is disabled\n");
+}
+
+void intel_ddi_prepare_link_retrain(struct drm_encoder *encoder)
+{
+       struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
+       struct drm_i915_private *dev_priv = encoder->dev->dev_private;
+       enum port port = intel_dp->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));
 
-       temp = I915_READ(DDI_BUF_CTL(port));
-       temp &= ~DDI_BUF_CTL_ENABLE;
+       intel_dp->DP |= DDI_BUF_CTL_ENABLE;
+       I915_WRITE(DDI_BUF_CTL(port), intel_dp->DP);
+       POSTING_READ(DDI_BUF_CTL(port));
 
-       I915_WRITE(DDI_BUF_CTL(port), temp);
+       udelay(600);
 }
This page took 0.035747 seconds and 5 git commands to generate.