drm/i915/cnl: Implement voltage swing sequence.
authorRodrigo Vivi <rodrigo.vivi@intel.com>
Fri, 9 Jun 2017 22:26:08 +0000 (15:26 -0700)
committerRodrigo Vivi <rodrigo.vivi@intel.com>
Mon, 12 Jun 2017 16:44:00 +0000 (09:44 -0700)
This is an important part of the DDI initalization as well as
for changing the voltage during DisplayPort link training.

This new sequence for Cannonlake is more like Broxton style
but still with different registers, different table and
different steps.

v2: Do not write to DW4_GRP to avoid overwrite individual loadgen.
    Fix PORT_CL_DW5 SUS Clock Config set.
v3: As previous platforms use only eDP table if low voltage was
    requested.
v4: fix Werror:maybe uninitialized (Paulo)
v5: Rebase on top of dw2_swing_sel changes
    on previous patches.
v6: Using flexible SCALING_MODE_SEL(x).

Cc: Manasi Navare <manasi.d.navare@intel.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Rodrigo Vivi <rodrigo.vivi@intel.com>
Reviewed-by: Manasi Navare <manasi.d.navare@intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/1497047175-27250-11-git-send-email-rodrigo.vivi@intel.com
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/intel_ddi.c
drivers/gpu/drm/i915/intel_dp.c

index d9d54113be10e8cbc52c90417318e7b0f30f3df2..88e4707f571dd9d96113626285c734517385e4cf 100644 (file)
@@ -1668,6 +1668,7 @@ enum skl_disp_power_wells {
 
 #define CNL_PORT_CL1CM_DW5             _MMIO(0x162014)
 #define   CL_POWER_DOWN_ENABLE         (1 << 4)
+#define   SUS_CLOCK_CONFIG             (3 << 0)
 
 #define _PORT_CL1CM_DW9_A              0x162024
 #define _PORT_CL1CM_DW9_BC             0x6C024
index ee75d3ba89ae0a5cae863aad2a0ca6dce0ae8923..9eb2940fc889fe036f2dca528b54896e3a8ee3fe 100644 (file)
@@ -1720,6 +1720,173 @@ u8 intel_ddi_dp_voltage_max(struct intel_encoder *encoder)
                DP_TRAIN_VOLTAGE_SWING_MASK;
 }
 
+static const struct cnl_ddi_buf_trans *
+cnl_get_buf_trans_hdmi(struct drm_i915_private *dev_priv,
+                      u32 voltage, int *n_entries)
+{
+       if (voltage == VOLTAGE_INFO_0_85V) {
+               *n_entries = ARRAY_SIZE(cnl_ddi_translations_hdmi_0_85V);
+               return cnl_ddi_translations_hdmi_0_85V;
+       } else if (voltage == VOLTAGE_INFO_0_95V) {
+               *n_entries = ARRAY_SIZE(cnl_ddi_translations_hdmi_0_95V);
+               return cnl_ddi_translations_hdmi_0_95V;
+       } else if (voltage == VOLTAGE_INFO_1_05V) {
+               *n_entries = ARRAY_SIZE(cnl_ddi_translations_hdmi_1_05V);
+               return cnl_ddi_translations_hdmi_1_05V;
+       }
+       return NULL;
+}
+
+static const struct cnl_ddi_buf_trans *
+cnl_get_buf_trans_dp(struct drm_i915_private *dev_priv,
+                    u32 voltage, int *n_entries)
+{
+       if (voltage == VOLTAGE_INFO_0_85V) {
+               *n_entries = ARRAY_SIZE(cnl_ddi_translations_dp_0_85V);
+               return cnl_ddi_translations_dp_0_85V;
+       } else if (voltage == VOLTAGE_INFO_0_95V) {
+               *n_entries = ARRAY_SIZE(cnl_ddi_translations_dp_0_95V);
+               return cnl_ddi_translations_dp_0_95V;
+       } else if (voltage == VOLTAGE_INFO_1_05V) {
+               *n_entries = ARRAY_SIZE(cnl_ddi_translations_dp_1_05V);
+               return cnl_ddi_translations_dp_1_05V;
+       }
+       return NULL;
+}
+
+static const struct cnl_ddi_buf_trans *
+cnl_get_buf_trans_edp(struct drm_i915_private *dev_priv,
+                     u32 voltage, int *n_entries)
+{
+       if (dev_priv->vbt.edp.low_vswing) {
+               if (voltage == VOLTAGE_INFO_0_85V) {
+                       *n_entries = ARRAY_SIZE(cnl_ddi_translations_edp_0_85V);
+                       return cnl_ddi_translations_dp_0_85V;
+               } else if (voltage == VOLTAGE_INFO_0_95V) {
+                       *n_entries = ARRAY_SIZE(cnl_ddi_translations_edp_0_95V);
+                       return cnl_ddi_translations_edp_0_95V;
+               } else if (voltage == VOLTAGE_INFO_1_05V) {
+                       *n_entries = ARRAY_SIZE(cnl_ddi_translations_edp_1_05V);
+                       return cnl_ddi_translations_edp_1_05V;
+               }
+               return NULL;
+       } else {
+               return cnl_get_buf_trans_dp(dev_priv, voltage, n_entries);
+       }
+}
+
+static void cnl_ddi_vswing_program(struct drm_i915_private *dev_priv,
+                                   u32 level, enum port port, int type)
+{
+       const struct cnl_ddi_buf_trans *ddi_translations = NULL;
+       u32 n_entries, val, voltage;
+       int ln;
+
+       /*
+        * Values for each port type are listed in
+        * voltage swing programming tables.
+        * Vccio voltage found in PORT_COMP_DW3.
+        */
+       voltage = I915_READ(CNL_PORT_COMP_DW3) & VOLTAGE_INFO_MASK;
+
+       if (type == INTEL_OUTPUT_HDMI) {
+               ddi_translations = cnl_get_buf_trans_hdmi(dev_priv,
+                                                         voltage, &n_entries);
+       } else if (type == INTEL_OUTPUT_DP) {
+               ddi_translations = cnl_get_buf_trans_dp(dev_priv,
+                                                       voltage, &n_entries);
+       } else if (type == INTEL_OUTPUT_EDP) {
+               ddi_translations = cnl_get_buf_trans_edp(dev_priv,
+                                                        voltage, &n_entries);
+       }
+
+       if (ddi_translations == NULL) {
+               MISSING_CASE(voltage);
+               return;
+       }
+
+       if (level >= n_entries) {
+               DRM_DEBUG_KMS("DDI translation not found for level %d. Using %d instead.", level, n_entries - 1);
+               level = n_entries - 1;
+       }
+
+       /* Set PORT_TX_DW5 Scaling Mode Sel to 010b. */
+       val = I915_READ(CNL_PORT_TX_DW5_LN0(port));
+       val |= SCALING_MODE_SEL(2);
+       I915_WRITE(CNL_PORT_TX_DW5_GRP(port), val);
+
+       /* Program PORT_TX_DW2 */
+       val = I915_READ(CNL_PORT_TX_DW2_LN0(port));
+       val |= SWING_SEL_UPPER(ddi_translations[level].dw2_swing_sel);
+       val |= SWING_SEL_LOWER(ddi_translations[level].dw2_swing_sel);
+       /* Rcomp scalar is fixed as 0x98 for every table entry */
+       val |= RCOMP_SCALAR(0x98);
+       I915_WRITE(CNL_PORT_TX_DW2_GRP(port), val);
+
+        /* Program PORT_TX_DW4 */
+       /* We cannot write to GRP. It would overrite individual loadgen */
+       for (ln = 0; ln < 4; ln++) {
+               val = I915_READ(CNL_PORT_TX_DW4_LN(port, ln));
+               val |= POST_CURSOR_1(ddi_translations[level].dw4_post_cursor_1);
+               val |= POST_CURSOR_2(ddi_translations[level].dw4_post_cursor_2);
+               val |= CURSOR_COEFF(ddi_translations[level].dw4_cursor_coeff);
+               I915_WRITE(CNL_PORT_TX_DW4_LN(port, ln), val);
+       }
+
+        /* Program PORT_TX_DW5 */
+       /* All DW5 values are fixed for every table entry */
+       val = I915_READ(CNL_PORT_TX_DW5_LN0(port));
+       val |= RTERM_SELECT(6);
+       val |= TAP3_DISABLE;
+       I915_WRITE(CNL_PORT_TX_DW5_GRP(port), val);
+
+        /* Program PORT_TX_DW7 */
+       val = I915_READ(CNL_PORT_TX_DW7_LN0(port));
+       val |= N_SCALAR(ddi_translations[level].dw7_n_scalar);
+       I915_WRITE(CNL_PORT_TX_DW7_GRP(port), val);
+}
+
+static void cnl_ddi_vswing_sequence(struct drm_i915_private *dev_priv,
+                                   u32 level, enum port port, int type)
+{
+       u32 val;
+
+       /*
+        * 1. If port type is eDP or DP,
+        * set PORT_PCS_DW1 cmnkeeper_enable to 1b,
+        * else clear to 0b.
+        */
+       val = I915_READ(CNL_PORT_PCS_DW1_LN0(port));
+       if (type == INTEL_OUTPUT_EDP || type == INTEL_OUTPUT_DP)
+               val |= COMMON_KEEPER_EN;
+       else
+               val &= ~COMMON_KEEPER_EN;
+       I915_WRITE(CNL_PORT_PCS_DW1_GRP(port), val);
+
+       /* 2. Program loadgen select */
+       /*
+        * FIXME: Program PORT_TX_DW4_LN depending on Bit rate and used lanes
+        */
+
+       /* 3. Set PORT_CL_DW5 SUS Clock Config to 11b */
+       val = I915_READ(CNL_PORT_CL1CM_DW5);
+       val |= SUS_CLOCK_CONFIG;
+       I915_WRITE(CNL_PORT_CL1CM_DW5, val);
+
+       /* 4. Clear training enable to change swing values */
+       val = I915_READ(CNL_PORT_TX_DW5_LN0(port));
+       val &= ~TX_TRAINING_EN;
+       I915_WRITE(CNL_PORT_TX_DW5_GRP(port), val);
+
+       /* 5. Program swing and de-emphasis */
+       cnl_ddi_vswing_program(dev_priv, level, port, type);
+
+       /* 6. Set training enable to trigger update */
+       val = I915_READ(CNL_PORT_TX_DW5_LN0(port));
+       val |= TX_TRAINING_EN;
+       I915_WRITE(CNL_PORT_TX_DW5_GRP(port), val);
+}
+
 static uint32_t translate_signal_level(int signal_levels)
 {
        int i;
@@ -1752,7 +1919,11 @@ uint32_t ddi_signal_levels(struct intel_dp *intel_dp)
                skl_ddi_set_iboost(encoder, level);
        else if (IS_GEN9_LP(dev_priv))
                bxt_ddi_vswing_sequence(dev_priv, level, port, encoder->type);
-
+       else if (IS_CANNONLAKE(dev_priv)) {
+               cnl_ddi_vswing_sequence(dev_priv, level, port, encoder->type);
+               /* DDI_BUF_CTL bits 27:24 are reserved on CNL */
+               return 0;
+       }
        return DDI_BUF_TRANS_SELECT(level);
 }
 
@@ -1850,6 +2021,9 @@ static void intel_ddi_pre_enable_hdmi(struct intel_encoder *encoder,
        else if (IS_GEN9_LP(dev_priv))
                bxt_ddi_vswing_sequence(dev_priv, level, port,
                                        INTEL_OUTPUT_HDMI);
+       else if (IS_CANNONLAKE(dev_priv))
+               cnl_ddi_vswing_sequence(dev_priv, level, port,
+                                       INTEL_OUTPUT_HDMI);
 
        intel_hdmi->set_infoframes(drm_encoder,
                                   has_hdmi_sink,
index d1ee278064b7fadcb66d63404cdcc9578cbbca2a..7a3a42c95381d22e6e262112a24ada8194d52d96 100644 (file)
@@ -3492,7 +3492,7 @@ intel_dp_set_signal_levels(struct intel_dp *intel_dp)
        if (HAS_DDI(dev_priv)) {
                signal_levels = ddi_signal_levels(intel_dp);
 
-               if (IS_GEN9_LP(dev_priv))
+               if (IS_GEN9_LP(dev_priv) || IS_CANNONLAKE(dev_priv))
                        signal_levels = 0;
                else
                        mask = DDI_BUF_EMP_MASK;