drm/i915: split DP link training across panel power sequencing
authorJesse Barnes <jbarnes@virtuousgeek.org>
Wed, 8 Sep 2010 19:42:02 +0000 (12:42 -0700)
committerChris Wilson <chris@chris-wilson.co.uk>
Wed, 8 Sep 2010 20:20:24 +0000 (21:20 +0100)
Mode set sequence requires that we start training, then enable the
panel, then complete training.  So split the DP training function into
two parts; the first enables the DP port and sets training pattern 1 and
the second completes the training.

As part of this, remove some redundant function args from the various DP
handling functions and use the intel_dp fields everywhere we can.

Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
[ickle: removed first ironlake_edp_backlight_on() on advice of jbarnes]
Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
drivers/gpu/drm/i915/intel_dp.c

index 3a4e2a639872e13b7525bf76473e76a245c54cf9..effbbe0915ecf84186235de3125b359e0ca546c2 100644 (file)
@@ -58,6 +58,8 @@ struct intel_dp {
        struct i2c_adapter adapter;
        struct i2c_algo_dp_aux_data algo;
        bool is_pch_edp;
+       uint8_t train_set[4];
+       uint8_t link_status[DP_LINK_STATUS_SIZE];
 };
 
 static struct intel_dp *enc_to_intel_dp(struct drm_encoder *encoder)
@@ -65,7 +67,8 @@ static struct intel_dp *enc_to_intel_dp(struct drm_encoder *encoder)
        return container_of(enc_to_intel_encoder(encoder), struct intel_dp, base);
 }
 
-static void intel_dp_link_train(struct intel_dp *intel_dp);
+static void intel_dp_start_link_train(struct intel_dp *intel_dp);
+static void intel_dp_complete_link_train(struct intel_dp *intel_dp);
 static void intel_dp_link_down(struct intel_dp *intel_dp);
 
 void
@@ -901,16 +904,16 @@ static void intel_dp_commit(struct drm_encoder *encoder)
 {
        struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
        struct drm_device *dev = encoder->dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       uint32_t dp_reg = I915_READ(intel_dp->output_reg);
 
-       if (!(dp_reg & DP_PORT_EN)) {
-               intel_dp_link_train(intel_dp);
-       }
-       if (IS_eDP(intel_dp) || IS_PCH_eDP(intel_dp)) {
+       intel_dp_start_link_train(intel_dp);
+
+       if (IS_eDP(intel_dp) || IS_PCH_eDP(intel_dp))
                ironlake_edp_panel_on(dev);
+
+       intel_dp_complete_link_train(intel_dp);
+
+       if (IS_eDP(intel_dp) || IS_PCH_eDP(intel_dp))
                ironlake_edp_backlight_on(dev);
-       }
 }
 
 static void
@@ -932,9 +935,10 @@ intel_dp_dpms(struct drm_encoder *encoder, int mode)
                        ironlake_edp_pll_off(encoder);
        } else {
                if (!(dp_reg & DP_PORT_EN)) {
+                       intel_dp_start_link_train(intel_dp);
                        if (IS_eDP(intel_dp) || IS_PCH_eDP(intel_dp))
                                ironlake_edp_panel_on(dev);
-                       intel_dp_link_train(intel_dp);
+                       intel_dp_complete_link_train(intel_dp);
                        if (IS_eDP(intel_dp) || IS_PCH_eDP(intel_dp))
                                ironlake_edp_backlight_on(dev);
                }
@@ -947,14 +951,13 @@ intel_dp_dpms(struct drm_encoder *encoder, int mode)
  * link status information
  */
 static bool
-intel_dp_get_link_status(struct intel_dp *intel_dp,
-                        uint8_t link_status[DP_LINK_STATUS_SIZE])
+intel_dp_get_link_status(struct intel_dp *intel_dp)
 {
        int ret;
 
        ret = intel_dp_aux_native_read(intel_dp,
                                       DP_LANE0_1_STATUS,
-                                      link_status, DP_LINK_STATUS_SIZE);
+                                      intel_dp->link_status, DP_LINK_STATUS_SIZE);
        if (ret != DP_LINK_STATUS_SIZE)
                return false;
        return true;
@@ -1029,18 +1032,15 @@ intel_dp_pre_emphasis_max(uint8_t voltage_swing)
 }
 
 static void
-intel_get_adjust_train(struct intel_dp *intel_dp,
-                      uint8_t link_status[DP_LINK_STATUS_SIZE],
-                      int lane_count,
-                      uint8_t train_set[4])
+intel_get_adjust_train(struct intel_dp *intel_dp)
 {
        uint8_t v = 0;
        uint8_t p = 0;
        int lane;
 
-       for (lane = 0; lane < lane_count; lane++) {
-               uint8_t this_v = intel_get_adjust_request_voltage(link_status, lane);
-               uint8_t this_p = intel_get_adjust_request_pre_emphasis(link_status, lane);
+       for (lane = 0; lane < intel_dp->lane_count; lane++) {
+               uint8_t this_v = intel_get_adjust_request_voltage(intel_dp->link_status, lane);
+               uint8_t this_p = intel_get_adjust_request_pre_emphasis(intel_dp->link_status, lane);
 
                if (this_v > v)
                        v = this_v;
@@ -1055,7 +1055,7 @@ intel_get_adjust_train(struct intel_dp *intel_dp,
                p = intel_dp_pre_emphasis_max(v) | DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
 
        for (lane = 0; lane < 4; lane++)
-               train_set[lane] = v | p;
+               intel_dp->train_set[lane] = v | p;
 }
 
 static uint32_t
@@ -1146,18 +1146,18 @@ intel_clock_recovery_ok(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane_count
                         DP_LANE_CHANNEL_EQ_DONE|\
                         DP_LANE_SYMBOL_LOCKED)
 static bool
-intel_channel_eq_ok(uint8_t link_status[DP_LINK_STATUS_SIZE], int lane_count)
+intel_channel_eq_ok(struct intel_dp *intel_dp)
 {
        uint8_t lane_align;
        uint8_t lane_status;
        int lane;
 
-       lane_align = intel_dp_link_status(link_status,
+       lane_align = intel_dp_link_status(intel_dp->link_status,
                                          DP_LANE_ALIGN_STATUS_UPDATED);
        if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0)
                return false;
-       for (lane = 0; lane < lane_count; lane++) {
-               lane_status = intel_get_lane_status(link_status, lane);
+       for (lane = 0; lane < intel_dp->lane_count; lane++) {
+               lane_status = intel_get_lane_status(intel_dp->link_status, lane);
                if ((lane_status & CHANNEL_EQ_BITS) != CHANNEL_EQ_BITS)
                        return false;
        }
@@ -1168,7 +1168,6 @@ static bool
 intel_dp_set_link_train(struct intel_dp *intel_dp,
                        uint32_t dp_reg_value,
                        uint8_t dp_train_pat,
-                       uint8_t train_set[4],
                        bool first)
 {
        struct drm_device *dev = intel_dp->base.enc.dev;
@@ -1186,24 +1185,21 @@ intel_dp_set_link_train(struct intel_dp *intel_dp,
                                    dp_train_pat);
 
        ret = intel_dp_aux_native_write(intel_dp,
-                                       DP_TRAINING_LANE0_SET, train_set, 4);
+                                       DP_TRAINING_LANE0_SET, intel_dp->train_set, 4);
        if (ret != 4)
                return false;
 
        return true;
 }
 
+/* Enable corresponding port and start training pattern 1 */
 static void
-intel_dp_link_train(struct intel_dp *intel_dp)
+intel_dp_start_link_train(struct intel_dp *intel_dp)
 {
        struct drm_device *dev = intel_dp->base.enc.dev;
-       struct drm_i915_private *dev_priv = dev->dev_private;
-       uint8_t train_set[4];
-       uint8_t link_status[DP_LINK_STATUS_SIZE];
        int i;
        uint8_t voltage;
        bool clock_recovery = false;
-       bool channel_eq = false;
        bool first = true;
        int tries;
        u32 reg;
@@ -1219,18 +1215,18 @@ intel_dp_link_train(struct intel_dp *intel_dp)
                DP &= ~DP_LINK_TRAIN_MASK_CPT;
        else
                DP &= ~DP_LINK_TRAIN_MASK;
-       memset(train_set, 0, 4);
+       memset(intel_dp->train_set, 0, 4);
        voltage = 0xff;
        tries = 0;
        clock_recovery = false;
        for (;;) {
-               /* Use train_set[0] to set the voltage and pre emphasis values */
+               /* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */
                uint32_t    signal_levels;
                if (IS_GEN6(dev) && IS_eDP(intel_dp)) {
-                       signal_levels = intel_gen6_edp_signal_levels(train_set[0]);
+                       signal_levels = intel_gen6_edp_signal_levels(intel_dp->train_set[0]);
                        DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_SNB) | signal_levels;
                } else {
-                       signal_levels = intel_dp_signal_levels(train_set[0], intel_dp->lane_count);
+                       signal_levels = intel_dp_signal_levels(intel_dp->train_set[0], intel_dp->lane_count);
                        DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels;
                }
 
@@ -1240,52 +1236,65 @@ intel_dp_link_train(struct intel_dp *intel_dp)
                        reg = DP | DP_LINK_TRAIN_PAT_1;
 
                if (!intel_dp_set_link_train(intel_dp, reg,
-                                            DP_TRAINING_PATTERN_1, train_set, first))
+                                            DP_TRAINING_PATTERN_1, first))
                        break;
                first = false;
                /* Set training pattern 1 */
 
                udelay(100);
-               if (!intel_dp_get_link_status(intel_dp, link_status))
+               if (!intel_dp_get_link_status(intel_dp))
                        break;
 
-               if (intel_clock_recovery_ok(link_status, intel_dp->lane_count)) {
+               if (intel_clock_recovery_ok(intel_dp->link_status, intel_dp->lane_count)) {
                        clock_recovery = true;
                        break;
                }
 
                /* Check to see if we've tried the max voltage */
                for (i = 0; i < intel_dp->lane_count; i++)
-                       if ((train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0)
+                       if ((intel_dp->train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0)
                                break;
                if (i == intel_dp->lane_count)
                        break;
 
                /* Check to see if we've tried the same voltage 5 times */
-               if ((train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) {
+               if ((intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) {
                        ++tries;
                        if (tries == 5)
                                break;
                } else
                        tries = 0;
-               voltage = train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
+               voltage = intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
 
-               /* Compute new train_set as requested by target */
-               intel_get_adjust_train(intel_dp, link_status, intel_dp->lane_count, train_set);
+               /* Compute new intel_dp->train_set as requested by target */
+               intel_get_adjust_train(intel_dp);
        }
 
+       intel_dp->DP = DP;
+}
+
+static void
+intel_dp_complete_link_train(struct intel_dp *intel_dp)
+{
+       struct drm_device *dev = intel_dp->base.enc.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       bool channel_eq = false;
+       int tries;
+       u32 reg;
+       uint32_t DP = intel_dp->DP;
+
        /* channel equalization */
        tries = 0;
        channel_eq = false;
        for (;;) {
-               /* Use train_set[0] to set the voltage and pre emphasis values */
+               /* Use intel_dp->train_set[0] to set the voltage and pre emphasis values */
                uint32_t    signal_levels;
 
                if (IS_GEN6(dev) && IS_eDP(intel_dp)) {
-                       signal_levels = intel_gen6_edp_signal_levels(train_set[0]);
+                       signal_levels = intel_gen6_edp_signal_levels(intel_dp->train_set[0]);
                        DP = (DP & ~EDP_LINK_TRAIN_VOL_EMP_MASK_SNB) | signal_levels;
                } else {
-                       signal_levels = intel_dp_signal_levels(train_set[0], intel_dp->lane_count);
+                       signal_levels = intel_dp_signal_levels(intel_dp->train_set[0], intel_dp->lane_count);
                        DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels;
                }
 
@@ -1296,15 +1305,15 @@ intel_dp_link_train(struct intel_dp *intel_dp)
 
                /* channel eq pattern */
                if (!intel_dp_set_link_train(intel_dp, reg,
-                                            DP_TRAINING_PATTERN_2, train_set,
+                                            DP_TRAINING_PATTERN_2,
                                             false))
                        break;
 
                udelay(400);
-               if (!intel_dp_get_link_status(intel_dp, link_status))
+               if (!intel_dp_get_link_status(intel_dp))
                        break;
 
-               if (intel_channel_eq_ok(link_status, intel_dp->lane_count)) {
+               if (intel_channel_eq_ok(intel_dp)) {
                        channel_eq = true;
                        break;
                }
@@ -1313,8 +1322,8 @@ intel_dp_link_train(struct intel_dp *intel_dp)
                if (tries > 5)
                        break;
 
-               /* Compute new train_set as requested by target */
-               intel_get_adjust_train(intel_dp, link_status, intel_dp->lane_count, train_set);
+               /* Compute new intel_dp->train_set as requested by target */
+               intel_get_adjust_train(intel_dp);
                ++tries;
        }
 
@@ -1375,18 +1384,18 @@ intel_dp_link_down(struct intel_dp *intel_dp)
 static void
 intel_dp_check_link_status(struct intel_dp *intel_dp)
 {
-       uint8_t link_status[DP_LINK_STATUS_SIZE];
-
        if (!intel_dp->base.enc.crtc)
                return;
 
-       if (!intel_dp_get_link_status(intel_dp, link_status)) {
+       if (!intel_dp_get_link_status(intel_dp)) {
                intel_dp_link_down(intel_dp);
                return;
        }
 
-       if (!intel_channel_eq_ok(link_status, intel_dp->lane_count))
-               intel_dp_link_train(intel_dp);
+       if (!intel_channel_eq_ok(intel_dp)) {
+               intel_dp_start_link_train(intel_dp);
+               intel_dp_complete_link_train(intel_dp);
+       }
 }
 
 static enum drm_connector_status