[4/7] drm/i915: split DP link training across panel power sequencing
diff mbox

Message ID 1283974925-2913-5-git-send-email-jbarnes@virtuousgeek.org
State New, archived
Headers show

Commit Message

Jesse Barnes Sept. 8, 2010, 7:42 p.m. UTC
None

Patch
diff mbox

diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index d7a854f..5053391 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -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
@@ -902,15 +905,15 @@  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);
-	}
+	intel_dp_start_link_train(intel_dp);
 	if (IS_eDP(intel_dp) || IS_PCH_eDP(intel_dp)) {
 		ironlake_edp_panel_on(dev);
 		ironlake_edp_backlight_on(dev);
 	}
+	intel_dp_complete_link_train(intel_dp);
+	if (IS_eDP(intel_dp) || IS_PCH_eDP(intel_dp))
+		ironlake_edp_backlight_on(encoder->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