drm/i915: support DDI training in FDI mode
authorEugeni Dodonov <eugeni.dodonov@intel.com>
Wed, 9 May 2012 18:37:21 +0000 (15:37 -0300)
committerDaniel Vetter <daniel.vetter@ffwll.ch>
Sat, 19 May 2012 20:39:48 +0000 (22:39 +0200)
Starting with Haswell, DDI ports can work in FDI mode to support
connectivity with the outputs located on the PCH.

This commit adds support for such connections in the intel_ddi module, and
provides Haswell-specific functionality to make it work.

v2: simplify the commit as per Daniel Vetter suggestion.

Signed-off-by: Eugeni Dodonov <eugeni.dodonov@intel.com>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
drivers/gpu/drm/i915/intel_ddi.c
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_drv.h

index 08f210b..f44aae1 100644 (file)
@@ -105,3 +105,118 @@ void intel_prepare_ddi(struct drm_device *dev)
                intel_prepare_ddi_buffers(dev, PORT_E, true);
        }
 }
+
+static const long hsw_ddi_buf_ctl_values[] = {
+       DDI_BUF_EMP_400MV_0DB_HSW,
+       DDI_BUF_EMP_400MV_3_5DB_HSW,
+       DDI_BUF_EMP_400MV_6DB_HSW,
+       DDI_BUF_EMP_400MV_9_5DB_HSW,
+       DDI_BUF_EMP_600MV_0DB_HSW,
+       DDI_BUF_EMP_600MV_3_5DB_HSW,
+       DDI_BUF_EMP_600MV_6DB_HSW,
+       DDI_BUF_EMP_800MV_0DB_HSW,
+       DDI_BUF_EMP_800MV_3_5DB_HSW
+};
+
+
+/* Starting with Haswell, different DDI ports can work in FDI mode for
+ * connection to the PCH-located connectors. For this, it is necessary to train
+ * both the DDI port and PCH receiver for the desired DDI buffer settings.
+ *
+ * The recommended port to work in FDI mode is DDI E, which we use here. Also,
+ * please note that when FDI mode is active on DDI E, it shares 2 lines with
+ * DDI A (which is used for eDP)
+ */
+
+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;
+
+       /* 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 */
+               I915_WRITE(DP_TP_CTL(PORT_E),
+                                       DP_TP_CTL_FDI_AUTOTRAIN |
+                                       DP_TP_CTL_ENHANCED_FRAME_ENABLE |
+                                       DP_TP_CTL_LINK_TRAIN_PAT1 |
+                                       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]);
+
+               udelay(600);
+
+               /* 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);
+
+               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);
+
+                       /* 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);
+
+                       /* 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);
+
+                       /* 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;
+               }
+       }
+
+       DRM_DEBUG_KMS("FDI train done.\n");
+}
index 096a1b1..3edf8f6 100644 (file)
@@ -6607,6 +6607,8 @@ static void intel_init_display(struct drm_device *dev)
                        /* FIXME: detect B0+ stepping and use auto training */
                        dev_priv->display.fdi_link_train = ivb_manual_fdi_link_train;
                        dev_priv->display.write_eld = ironlake_write_eld;
+               } else if (IS_HASWELL(dev)) {
+                       dev_priv->display.fdi_link_train = hsw_fdi_link_train;
                } else
                        dev_priv->display.update_wm = NULL;
        } else if (IS_VALLEYVIEW(dev)) {
index e6ce02b..a5bb5b6 100644 (file)
@@ -447,6 +447,7 @@ extern void intel_write_eld(struct drm_encoder *encoder,
                            struct drm_display_mode *mode);
 extern void intel_cpt_verify_modeset(struct drm_device *dev, int pipe);
 extern void intel_prepare_ddi(struct drm_device *dev);
+extern void hsw_fdi_link_train(struct drm_crtc *crtc);
 
 /* For use by IVB LP watermark workaround in intel_sprite.c */
 extern void intel_update_watermarks(struct drm_device *dev);