All the mail mirrored from lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/8] [RFC] Link training test
@ 2015-09-08 12:27 Ander Conselvan de Oliveira
  2015-09-08 12:27 ` [PATCH 1/8] drm/i915: Rename DP link training functions Ander Conselvan de Oliveira
                   ` (9 more replies)
  0 siblings, 10 replies; 31+ messages in thread
From: Ander Conselvan de Oliveira @ 2015-09-08 12:27 UTC (permalink / raw)
  To: intel-gfx; +Cc: jani.nikula, Ander Conselvan de Oliveira

Hi,

There's been some discussion on improving our link training code, with
one of the ideas being a complete rewrite of the state machine. However,
a concern was raised over the risk of regressions. The code we have has
seen a lot of real world testing, and it would take a long time for any
new code to get that same exposure. On top of that, there is no explicit
igt coverage for link training since it is very dependent on the hw
setup.

Since gathering an extensive set of hardware combinations for getting the
appropriate test coverage is really difficult, I believe that we should
aim at testing the link training code in isolation. That's what this RFC
is about.

Most of the kernel patches in this series are small changes to the hw
independent part of the link training code that make it possible to move
it to a separate file.  Then, in the igt patch, a test is created the
wraps a fake DP sink device around the link training code. The behavior
of that sink device is pretty simple for now, it just tries to get the
maximum voltage swing and pre-emphasis level supported. We could do some
git archeology of the link training code to try to find some more tests
that would make sense.

I had to make a lot of hacks to get that thing to compile, so that's
one of the things I'd like to hear comments on. And also on the general
approach.

Thanks,
Ander

-- 
2.4.3

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [PATCH 1/8] drm/i915: Rename DP link training functions
  2015-09-08 12:27 [PATCH 0/8] [RFC] Link training test Ander Conselvan de Oliveira
@ 2015-09-08 12:27 ` Ander Conselvan de Oliveira
  2015-09-08 12:27 ` [PATCH 2/8] drm/i915: Don't pass *DP around to " Ander Conselvan de Oliveira
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 31+ messages in thread
From: Ander Conselvan de Oliveira @ 2015-09-08 12:27 UTC (permalink / raw)
  To: intel-gfx; +Cc: jani.nikula, Ander Conselvan de Oliveira

The link training functions had confusing names. The start function
actually does the clock recovery phase of the link training, and the
complete function does the channel equalization. So call them that
instead. Also, every call to intel_dp_start_link_train() was followed
by a call to intel_dp_complete_link_train(), so add a new start
function that calls clock_recory and channel_equalization.

Signed-off-by: Ander Conselvan de Oliveira <ander.conselvan.de.oliveira@intel.com>
---
 drivers/gpu/drm/i915/intel_ddi.c    |  1 -
 drivers/gpu/drm/i915/intel_dp.c     | 22 +++++++++++++---------
 drivers/gpu/drm/i915/intel_dp_mst.c |  1 -
 drivers/gpu/drm/i915/intel_drv.h    |  1 -
 4 files changed, 13 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_ddi.c b/drivers/gpu/drm/i915/intel_ddi.c
index 4823184..f9992e7 100644
--- a/drivers/gpu/drm/i915/intel_ddi.c
+++ b/drivers/gpu/drm/i915/intel_ddi.c
@@ -2294,7 +2294,6 @@ static void intel_ddi_pre_enable(struct intel_encoder *intel_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);
 		if (port != PORT_A || INTEL_INFO(dev)->gen >= 9)
 			intel_dp_stop_link_train(intel_dp);
 	} else if (type == INTEL_OUTPUT_HDMI) {
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 45ab25e..3d19404 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -2603,7 +2603,6 @@ static void intel_enable_dp(struct intel_encoder *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);
 	intel_dp_stop_link_train(intel_dp);
 
 	if (crtc->config->has_audio) {
@@ -3695,8 +3694,8 @@ static void intel_dp_set_idle_link_train(struct intel_dp *intel_dp)
 }
 
 /* Enable corresponding port and start training pattern 1 */
-void
-intel_dp_start_link_train(struct intel_dp *intel_dp)
+static void
+intel_dp_link_training_clock_recovery(struct intel_dp *intel_dp)
 {
 	struct drm_encoder *encoder = &dp_to_dig_port(intel_dp)->base.base;
 	struct drm_device *dev = encoder->dev;
@@ -3809,8 +3808,8 @@ intel_dp_start_link_train(struct intel_dp *intel_dp)
 	intel_dp->DP = DP;
 }
 
-void
-intel_dp_complete_link_train(struct intel_dp *intel_dp)
+static void
+intel_dp_link_training_channel_equalization(struct intel_dp *intel_dp)
 {
 	struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
 	struct drm_device *dev = dig_port->base.base.dev;
@@ -3863,7 +3862,7 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp)
 		if (!drm_dp_clock_recovery_ok(link_status,
 					      intel_dp->lane_count)) {
 			intel_dp->train_set_valid = false;
-			intel_dp_start_link_train(intel_dp);
+			intel_dp_link_training_clock_recovery(intel_dp);
 			intel_dp_set_link_train(intel_dp, &DP,
 						training_pattern |
 						DP_LINK_SCRAMBLING_DISABLE);
@@ -3880,7 +3879,7 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp)
 		/* Try 5 times, then try clock recovery if that fails */
 		if (tries > 5) {
 			intel_dp->train_set_valid = false;
-			intel_dp_start_link_train(intel_dp);
+			intel_dp_link_training_clock_recovery(intel_dp);
 			intel_dp_set_link_train(intel_dp, &DP,
 						training_pattern |
 						DP_LINK_SCRAMBLING_DISABLE);
@@ -3913,6 +3912,13 @@ void intel_dp_stop_link_train(struct intel_dp *intel_dp)
 				DP_TRAINING_PATTERN_DISABLE);
 }
 
+void
+intel_dp_start_link_train(struct intel_dp *intel_dp)
+{
+	intel_dp_link_training_clock_recovery(intel_dp);
+	intel_dp_link_training_channel_equalization(intel_dp);
+}
+
 static void
 intel_dp_link_down(struct intel_dp *intel_dp)
 {
@@ -4381,7 +4387,6 @@ go_again:
 			    !drm_dp_channel_eq_ok(&esi[10], intel_dp->lane_count)) {
 				DRM_DEBUG_KMS("channel EQ not ok, retraining\n");
 				intel_dp_start_link_train(intel_dp);
-				intel_dp_complete_link_train(intel_dp);
 				intel_dp_stop_link_train(intel_dp);
 			}
 
@@ -4472,7 +4477,6 @@ intel_dp_check_link_status(struct intel_dp *intel_dp)
 		DRM_DEBUG_KMS("%s: channel EQ not ok, retraining\n",
 			      intel_encoder->base.name);
 		intel_dp_start_link_train(intel_dp);
-		intel_dp_complete_link_train(intel_dp);
 		intel_dp_stop_link_train(intel_dp);
 	}
 }
diff --git a/drivers/gpu/drm/i915/intel_dp_mst.c b/drivers/gpu/drm/i915/intel_dp_mst.c
index 677d70e..dafccd9 100644
--- a/drivers/gpu/drm/i915/intel_dp_mst.c
+++ b/drivers/gpu/drm/i915/intel_dp_mst.c
@@ -183,7 +183,6 @@ static void intel_mst_pre_enable_dp(struct intel_encoder *encoder)
 
 
 		intel_dp_start_link_train(intel_dp);
-		intel_dp_complete_link_train(intel_dp);
 		intel_dp_stop_link_train(intel_dp);
 	}
 
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 46484e4..6bac79b 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -1180,7 +1180,6 @@ bool intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
 void intel_dp_set_link_params(struct intel_dp *intel_dp,
 			      const struct intel_crtc_state *pipe_config);
 void intel_dp_start_link_train(struct intel_dp *intel_dp);
-void intel_dp_complete_link_train(struct intel_dp *intel_dp);
 void intel_dp_stop_link_train(struct intel_dp *intel_dp);
 void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode);
 void intel_dp_encoder_destroy(struct drm_encoder *encoder);
-- 
2.4.3

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 2/8] drm/i915: Don't pass *DP around to link training functions
  2015-09-08 12:27 [PATCH 0/8] [RFC] Link training test Ander Conselvan de Oliveira
  2015-09-08 12:27 ` [PATCH 1/8] drm/i915: Rename DP link training functions Ander Conselvan de Oliveira
@ 2015-09-08 12:27 ` Ander Conselvan de Oliveira
  2015-09-08 12:27 ` [PATCH 3/8] drm/i915: Split intel_dp_update_link_train() Ander Conselvan de Oliveira
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 31+ messages in thread
From: Ander Conselvan de Oliveira @ 2015-09-08 12:27 UTC (permalink / raw)
  To: intel-gfx; +Cc: jani.nikula, Ander Conselvan de Oliveira

It just makes the code more confusing, so just reference intel_dp_>DP
directly. The old behavior is preserved by saving the previous value of
DP in the stack and restoring the old value in case of failure.

--

I'm not sure the old behavior is correct, but to err in the side of
caution I tried not to change it.
---
 drivers/gpu/drm/i915/intel_dp.c | 66 ++++++++++++++++++++---------------------
 1 file changed, 33 insertions(+), 33 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 3d19404..58efdca 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -3600,7 +3600,6 @@ intel_dp_set_signal_levels(struct intel_dp *intel_dp, uint32_t *DP)
 
 static bool
 intel_dp_set_link_train(struct intel_dp *intel_dp,
-			uint32_t *DP,
 			uint8_t dp_train_pat)
 {
 	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
@@ -3609,9 +3608,9 @@ intel_dp_set_link_train(struct intel_dp *intel_dp,
 	uint8_t buf[sizeof(intel_dp->train_set) + 1];
 	int ret, len;
 
-	_intel_dp_set_link_train(intel_dp, DP, dp_train_pat);
+	_intel_dp_set_link_train(intel_dp, &intel_dp->DP, dp_train_pat);
 
-	I915_WRITE(intel_dp->output_reg, *DP);
+	I915_WRITE(intel_dp->output_reg, intel_dp->DP);
 	POSTING_READ(intel_dp->output_reg);
 
 	buf[0] = dp_train_pat;
@@ -3632,17 +3631,17 @@ intel_dp_set_link_train(struct intel_dp *intel_dp,
 }
 
 static bool
-intel_dp_reset_link_train(struct intel_dp *intel_dp, uint32_t *DP,
+intel_dp_reset_link_train(struct intel_dp *intel_dp,
 			uint8_t dp_train_pat)
 {
 	if (!intel_dp->train_set_valid)
 		memset(intel_dp->train_set, 0, sizeof(intel_dp->train_set));
-	intel_dp_set_signal_levels(intel_dp, DP);
-	return intel_dp_set_link_train(intel_dp, DP, dp_train_pat);
+	intel_dp_set_signal_levels(intel_dp, &intel_dp->DP);
+	return intel_dp_set_link_train(intel_dp, dp_train_pat);
 }
 
 static bool
-intel_dp_update_link_train(struct intel_dp *intel_dp, uint32_t *DP,
+intel_dp_update_link_train(struct intel_dp *intel_dp,
 			   const uint8_t link_status[DP_LINK_STATUS_SIZE])
 {
 	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
@@ -3651,9 +3650,9 @@ intel_dp_update_link_train(struct intel_dp *intel_dp, uint32_t *DP,
 	int ret;
 
 	intel_get_adjust_train(intel_dp, link_status);
-	intel_dp_set_signal_levels(intel_dp, DP);
+	intel_dp_set_signal_levels(intel_dp, &intel_dp->DP);
 
-	I915_WRITE(intel_dp->output_reg, *DP);
+	I915_WRITE(intel_dp->output_reg, intel_dp->DP);
 	POSTING_READ(intel_dp->output_reg);
 
 	ret = drm_dp_dpcd_write(&intel_dp->aux, DP_TRAINING_LANE0_SET,
@@ -3694,7 +3693,7 @@ static void intel_dp_set_idle_link_train(struct intel_dp *intel_dp)
 }
 
 /* Enable corresponding port and start training pattern 1 */
-static void
+static bool
 intel_dp_link_training_clock_recovery(struct intel_dp *intel_dp)
 {
 	struct drm_encoder *encoder = &dp_to_dig_port(intel_dp)->base.base;
@@ -3702,9 +3701,9 @@ intel_dp_link_training_clock_recovery(struct intel_dp *intel_dp)
 	int i;
 	uint8_t voltage;
 	int voltage_tries, loop_tries;
-	uint32_t DP = intel_dp->DP;
 	uint8_t link_config[2];
 	uint8_t link_bw, rate_select;
+	uint8_t link_status[DP_LINK_STATUS_SIZE];
 
 	if (HAS_DDI(dev))
 		intel_ddi_prepare_link_retrain(encoder);
@@ -3726,22 +3725,20 @@ intel_dp_link_training_clock_recovery(struct intel_dp *intel_dp)
 	link_config[1] = DP_SET_ANSI_8B10B;
 	drm_dp_dpcd_write(&intel_dp->aux, DP_DOWNSPREAD_CTRL, link_config, 2);
 
-	DP |= DP_PORT_EN;
+	intel_dp->DP |= DP_PORT_EN;
 
 	/* clock recovery */
-	if (!intel_dp_reset_link_train(intel_dp, &DP,
+	if (!intel_dp_reset_link_train(intel_dp,
 				       DP_TRAINING_PATTERN_1 |
 				       DP_LINK_SCRAMBLING_DISABLE)) {
 		DRM_ERROR("failed to enable link training\n");
-		return;
+		return false;
 	}
 
 	voltage = 0xff;
 	voltage_tries = 0;
 	loop_tries = 0;
 	for (;;) {
-		uint8_t link_status[DP_LINK_STATUS_SIZE];
-
 		drm_dp_link_train_clock_recovery_delay(intel_dp->dpcd);
 		if (!intel_dp_get_link_status(intel_dp, link_status)) {
 			DRM_ERROR("failed to get link status\n");
@@ -3761,11 +3758,11 @@ intel_dp_link_training_clock_recovery(struct intel_dp *intel_dp)
 			DRM_DEBUG_KMS("clock recovery not ok, reset");
 			/* clear the flag as we are not reusing train set */
 			intel_dp->train_set_valid = false;
-			if (!intel_dp_reset_link_train(intel_dp, &DP,
+			if (!intel_dp_reset_link_train(intel_dp,
 						       DP_TRAINING_PATTERN_1 |
 						       DP_LINK_SCRAMBLING_DISABLE)) {
 				DRM_ERROR("failed to enable link training\n");
-				return;
+				return false;
 			}
 			continue;
 		}
@@ -3780,7 +3777,7 @@ intel_dp_link_training_clock_recovery(struct intel_dp *intel_dp)
 				DRM_ERROR("too many full retries, give up\n");
 				break;
 			}
-			intel_dp_reset_link_train(intel_dp, &DP,
+			intel_dp_reset_link_train(intel_dp,
 						  DP_TRAINING_PATTERN_1 |
 						  DP_LINK_SCRAMBLING_DISABLE);
 			voltage_tries = 0;
@@ -3799,23 +3796,22 @@ intel_dp_link_training_clock_recovery(struct intel_dp *intel_dp)
 		voltage = intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
 
 		/* Update training set as requested by target */
-		if (!intel_dp_update_link_train(intel_dp, &DP, link_status)) {
+		if (!intel_dp_update_link_train(intel_dp, link_status)) {
 			DRM_ERROR("failed to update link training\n");
 			break;
 		}
 	}
 
-	intel_dp->DP = DP;
+	return drm_dp_clock_recovery_ok(link_status, intel_dp->lane_count);
 }
 
-static void
+static bool
 intel_dp_link_training_channel_equalization(struct intel_dp *intel_dp)
 {
 	struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
 	struct drm_device *dev = dig_port->base.base.dev;
 	bool channel_eq = false;
 	int tries, cr_tries;
-	uint32_t DP = intel_dp->DP;
 	uint32_t training_pattern = DP_TRAINING_PATTERN_2;
 
 	/*
@@ -3834,11 +3830,11 @@ intel_dp_link_training_channel_equalization(struct intel_dp *intel_dp)
 		DRM_ERROR("5.4 Gbps link rate without HBR2/TPS3 support\n");
 
 	/* channel equalization */
-	if (!intel_dp_set_link_train(intel_dp, &DP,
+	if (!intel_dp_set_link_train(intel_dp,
 				     training_pattern |
 				     DP_LINK_SCRAMBLING_DISABLE)) {
 		DRM_ERROR("failed to start channel equalization\n");
-		return;
+		return false;
 	}
 
 	tries = 0;
@@ -3863,7 +3859,7 @@ intel_dp_link_training_channel_equalization(struct intel_dp *intel_dp)
 					      intel_dp->lane_count)) {
 			intel_dp->train_set_valid = false;
 			intel_dp_link_training_clock_recovery(intel_dp);
-			intel_dp_set_link_train(intel_dp, &DP,
+			intel_dp_set_link_train(intel_dp,
 						training_pattern |
 						DP_LINK_SCRAMBLING_DISABLE);
 			cr_tries++;
@@ -3880,7 +3876,7 @@ intel_dp_link_training_channel_equalization(struct intel_dp *intel_dp)
 		if (tries > 5) {
 			intel_dp->train_set_valid = false;
 			intel_dp_link_training_clock_recovery(intel_dp);
-			intel_dp_set_link_train(intel_dp, &DP,
+			intel_dp_set_link_train(intel_dp,
 						training_pattern |
 						DP_LINK_SCRAMBLING_DISABLE);
 			tries = 0;
@@ -3889,7 +3885,7 @@ intel_dp_link_training_channel_equalization(struct intel_dp *intel_dp)
 		}
 
 		/* Update training set as requested by target */
-		if (!intel_dp_update_link_train(intel_dp, &DP, link_status)) {
+		if (!intel_dp_update_link_train(intel_dp, link_status)) {
 			DRM_ERROR("failed to update link training\n");
 			break;
 		}
@@ -3898,25 +3894,29 @@ intel_dp_link_training_channel_equalization(struct intel_dp *intel_dp)
 
 	intel_dp_set_idle_link_train(intel_dp);
 
-	intel_dp->DP = DP;
-
 	if (channel_eq) {
 		intel_dp->train_set_valid = true;
 		DRM_DEBUG_KMS("Channel EQ done. DP Training successful\n");
+		return true;
+	} else {
+		return false;
 	}
 }
 
 void intel_dp_stop_link_train(struct intel_dp *intel_dp)
 {
-	intel_dp_set_link_train(intel_dp, &intel_dp->DP,
+	intel_dp_set_link_train(intel_dp,
 				DP_TRAINING_PATTERN_DISABLE);
 }
 
 void
 intel_dp_start_link_train(struct intel_dp *intel_dp)
 {
-	intel_dp_link_training_clock_recovery(intel_dp);
-	intel_dp_link_training_channel_equalization(intel_dp);
+	uint32_t DP = intel_dp->DP;
+
+	if (!intel_dp_link_training_clock_recovery(intel_dp) ||
+	    !intel_dp_link_training_channel_equalization(intel_dp))
+		intel_dp->DP = DP;
 }
 
 static void
-- 
2.4.3

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 3/8] drm/i915: Split intel_dp_update_link_train()
  2015-09-08 12:27 [PATCH 0/8] [RFC] Link training test Ander Conselvan de Oliveira
  2015-09-08 12:27 ` [PATCH 1/8] drm/i915: Rename DP link training functions Ander Conselvan de Oliveira
  2015-09-08 12:27 ` [PATCH 2/8] drm/i915: Don't pass *DP around to " Ander Conselvan de Oliveira
@ 2015-09-08 12:27 ` Ander Conselvan de Oliveira
  2015-09-08 12:27 ` [PATCH 4/8] drm/i915: Split write of pattern to DP reg from intel_dp_set_link_train Ander Conselvan de Oliveira
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 31+ messages in thread
From: Ander Conselvan de Oliveira @ 2015-09-08 12:27 UTC (permalink / raw)
  To: intel-gfx; +Cc: jani.nikula, Ander Conselvan de Oliveira

Separate the bit that writes to DP register in its own function and move
the update of the train_set based on the adjust request to the sink out.
The objective is to split the generic link training logic from the i915
specific part.
---
 drivers/gpu/drm/i915/intel_dp.c | 21 ++++++++++++++-------
 1 file changed, 14 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 58efdca..d7988a6 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -3640,20 +3640,25 @@ intel_dp_reset_link_train(struct intel_dp *intel_dp,
 	return intel_dp_set_link_train(intel_dp, dp_train_pat);
 }
 
-static bool
-intel_dp_update_link_train(struct intel_dp *intel_dp,
-			   const uint8_t link_status[DP_LINK_STATUS_SIZE])
+static void
+intel_dp_update_signal_levels(struct intel_dp *intel_dp)
 {
 	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
 	struct drm_i915_private *dev_priv =
 		to_i915(intel_dig_port->base.base.dev);
-	int ret;
 
-	intel_get_adjust_train(intel_dp, link_status);
 	intel_dp_set_signal_levels(intel_dp, &intel_dp->DP);
 
 	I915_WRITE(intel_dp->output_reg, intel_dp->DP);
 	POSTING_READ(intel_dp->output_reg);
+}
+
+static bool
+intel_dp_update_link_train(struct intel_dp *intel_dp)
+{
+	int ret;
+
+	intel_dp_update_signal_levels(intel_dp);
 
 	ret = drm_dp_dpcd_write(&intel_dp->aux, DP_TRAINING_LANE0_SET,
 				intel_dp->train_set, intel_dp->lane_count);
@@ -3796,7 +3801,8 @@ intel_dp_link_training_clock_recovery(struct intel_dp *intel_dp)
 		voltage = intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
 
 		/* Update training set as requested by target */
-		if (!intel_dp_update_link_train(intel_dp, link_status)) {
+		intel_get_adjust_train(intel_dp, link_status);
+		if (!intel_dp_update_link_train(intel_dp)) {
 			DRM_ERROR("failed to update link training\n");
 			break;
 		}
@@ -3885,7 +3891,8 @@ intel_dp_link_training_channel_equalization(struct intel_dp *intel_dp)
 		}
 
 		/* Update training set as requested by target */
-		if (!intel_dp_update_link_train(intel_dp, link_status)) {
+		intel_get_adjust_train(intel_dp, link_status);
+		if (!intel_dp_update_link_train(intel_dp)) {
 			DRM_ERROR("failed to update link training\n");
 			break;
 		}
-- 
2.4.3

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 4/8] drm/i915: Split write of pattern to DP reg from intel_dp_set_link_train
  2015-09-08 12:27 [PATCH 0/8] [RFC] Link training test Ander Conselvan de Oliveira
                   ` (2 preceding siblings ...)
  2015-09-08 12:27 ` [PATCH 3/8] drm/i915: Split intel_dp_update_link_train() Ander Conselvan de Oliveira
@ 2015-09-08 12:27 ` Ander Conselvan de Oliveira
  2015-09-08 12:27 ` [PATCH 5/8] drm/i915: Don't call intel_dp_set_signal_levels() on link train reset Ander Conselvan de Oliveira
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 31+ messages in thread
From: Ander Conselvan de Oliveira @ 2015-09-08 12:27 UTC (permalink / raw)
  To: intel-gfx; +Cc: jani.nikula, Ander Conselvan de Oliveira

Split the register write with the new link training pattern out of
intel_dp_set_link_train(), so that the i915 specific code is in a
separate function.
---
 drivers/gpu/drm/i915/intel_dp.c | 18 +++++++++++++-----
 1 file changed, 13 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index d7988a6..a51ae48 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -3598,20 +3598,28 @@ intel_dp_set_signal_levels(struct intel_dp *intel_dp, uint32_t *DP)
 	*DP = (*DP & ~mask) | signal_levels;
 }
 
-static bool
-intel_dp_set_link_train(struct intel_dp *intel_dp,
-			uint8_t dp_train_pat)
+static void
+intel_dp_program_link_training_pattern(struct intel_dp *intel_dp,
+				       uint8_t dp_train_pat)
 {
 	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
 	struct drm_i915_private *dev_priv =
 		to_i915(intel_dig_port->base.base.dev);
-	uint8_t buf[sizeof(intel_dp->train_set) + 1];
-	int ret, len;
 
 	_intel_dp_set_link_train(intel_dp, &intel_dp->DP, dp_train_pat);
 
 	I915_WRITE(intel_dp->output_reg, intel_dp->DP);
 	POSTING_READ(intel_dp->output_reg);
+}
+
+static bool
+intel_dp_set_link_train(struct intel_dp *intel_dp,
+			uint8_t dp_train_pat)
+{
+	uint8_t buf[sizeof(intel_dp->train_set) + 1];
+	int ret, len;
+
+	intel_dp_program_link_training_pattern(intel_dp, dp_train_pat);
 
 	buf[0] = dp_train_pat;
 	if ((dp_train_pat & DP_TRAINING_PATTERN_MASK) ==
-- 
2.4.3

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 5/8] drm/i915: Don't call intel_dp_set_signal_levels() on link train reset
  2015-09-08 12:27 [PATCH 0/8] [RFC] Link training test Ander Conselvan de Oliveira
                   ` (3 preceding siblings ...)
  2015-09-08 12:27 ` [PATCH 4/8] drm/i915: Split write of pattern to DP reg from intel_dp_set_link_train Ander Conselvan de Oliveira
@ 2015-09-08 12:27 ` Ander Conselvan de Oliveira
  2015-09-08 12:27 ` [PATCH 6/8] drm/i915: Move generic link training code to a separate file Ander Conselvan de Oliveira
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 31+ messages in thread
From: Ander Conselvan de Oliveira @ 2015-09-08 12:27 UTC (permalink / raw)
  To: intel-gfx; +Cc: jani.nikula, Ander Conselvan de Oliveira

Instead of calling intel_dp_set_signal_levels(), which writes the
desired signal levels mask to a variable, just call the new function
intel_dp_update_signal_levels(). The effect should be the same, except
for an extra register write, but this creates a better split between the
generic link training logic and the i915 specific part.
---
 drivers/gpu/drm/i915/intel_dp.c | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index a51ae48..d5c1b0a 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -3638,16 +3638,6 @@ intel_dp_set_link_train(struct intel_dp *intel_dp,
 	return ret == len;
 }
 
-static bool
-intel_dp_reset_link_train(struct intel_dp *intel_dp,
-			uint8_t dp_train_pat)
-{
-	if (!intel_dp->train_set_valid)
-		memset(intel_dp->train_set, 0, sizeof(intel_dp->train_set));
-	intel_dp_set_signal_levels(intel_dp, &intel_dp->DP);
-	return intel_dp_set_link_train(intel_dp, dp_train_pat);
-}
-
 static void
 intel_dp_update_signal_levels(struct intel_dp *intel_dp)
 {
@@ -3662,6 +3652,16 @@ intel_dp_update_signal_levels(struct intel_dp *intel_dp)
 }
 
 static bool
+intel_dp_reset_link_train(struct intel_dp *intel_dp,
+			uint8_t dp_train_pat)
+{
+	if (!intel_dp->train_set_valid)
+		memset(intel_dp->train_set, 0, sizeof(intel_dp->train_set));
+	intel_dp_update_signal_levels(intel_dp);
+	return intel_dp_set_link_train(intel_dp, dp_train_pat);
+}
+
+static bool
 intel_dp_update_link_train(struct intel_dp *intel_dp)
 {
 	int ret;
-- 
2.4.3

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 6/8] drm/i915: Move generic link training code to a separate file
  2015-09-08 12:27 [PATCH 0/8] [RFC] Link training test Ander Conselvan de Oliveira
                   ` (4 preceding siblings ...)
  2015-09-08 12:27 ` [PATCH 5/8] drm/i915: Don't call intel_dp_set_signal_levels() on link train reset Ander Conselvan de Oliveira
@ 2015-09-08 12:27 ` Ander Conselvan de Oliveira
  2015-09-08 12:27 ` [PATCH 7/8] drm/i915: Move max voltage and pre emphasis to intel_dp_link_training.c Ander Conselvan de Oliveira
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 31+ messages in thread
From: Ander Conselvan de Oliveira @ 2015-09-08 12:27 UTC (permalink / raw)
  To: intel-gfx; +Cc: jani.nikula, Ander Conselvan de Oliveira

No functional changes, just moving code around.
---
 drivers/gpu/drm/i915/Makefile                 |   1 +
 drivers/gpu/drm/i915/intel_dp.c               | 328 +------------------------
 drivers/gpu/drm/i915/intel_dp_link_training.c | 336 ++++++++++++++++++++++++++
 drivers/gpu/drm/i915/intel_drv.h              |  16 ++
 4 files changed, 362 insertions(+), 319 deletions(-)
 create mode 100644 drivers/gpu/drm/i915/intel_dp_link_training.c

diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
index 44d290a..0851de07 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -77,6 +77,7 @@ i915-y += dvo_ch7017.o \
 	  dvo_tfp410.o \
 	  intel_crt.o \
 	  intel_ddi.o \
+	  intel_dp_link_training.o \
 	  intel_dp_mst.o \
 	  intel_dp.o \
 	  intel_dsi.o \
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index d5c1b0a..5778059 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -1188,7 +1188,7 @@ intel_dp_sink_rates(struct intel_dp *intel_dp, const int **sink_rates)
 	return (intel_dp_max_link_bw(intel_dp) >> 3) + 1;
 }
 
-static bool intel_dp_source_supports_hbr2(struct drm_device *dev)
+bool intel_dp_source_supports_hbr2(struct drm_device *dev)
 {
 	/* WaDisableHBR2:skl */
 	if (IS_SKYLAKE(dev) && INTEL_REVID(dev) <= SKL_REVID_B0)
@@ -1364,8 +1364,8 @@ int intel_dp_rate_select(struct intel_dp *intel_dp, int rate)
 	return rate_to_index(rate, intel_dp->sink_rates);
 }
 
-static void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock,
-				  uint8_t *link_bw, uint8_t *rate_select)
+void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock,
+			   uint8_t *link_bw, uint8_t *rate_select)
 {
 	if (intel_dp->num_sink_rates) {
 		*link_bw = 0;
@@ -3045,7 +3045,7 @@ intel_dp_dpcd_read_wake(struct drm_dp_aux *aux, unsigned int offset,
  * Fetch AUX CH registers 0x202 - 0x207 which contain
  * link status information
  */
-static bool
+bool
 intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_STATUS_SIZE])
 {
 	return intel_dp_dpcd_read_wake(&intel_dp->aux,
@@ -3055,7 +3055,7 @@ intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_
 }
 
 /* These are source-specific values. */
-static uint8_t
+uint8_t
 intel_dp_voltage_max(struct intel_dp *intel_dp)
 {
 	struct drm_device *dev = intel_dp_to_dev(intel_dp);
@@ -3078,7 +3078,7 @@ intel_dp_voltage_max(struct intel_dp *intel_dp)
 		return DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
 }
 
-static uint8_t
+uint8_t
 intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t voltage_swing)
 {
 	struct drm_device *dev = intel_dp_to_dev(intel_dp);
@@ -3425,38 +3425,6 @@ static uint32_t chv_signal_levels(struct intel_dp *intel_dp)
 	return 0;
 }
 
-static void
-intel_get_adjust_train(struct intel_dp *intel_dp,
-		       const uint8_t link_status[DP_LINK_STATUS_SIZE])
-{
-	uint8_t v = 0;
-	uint8_t p = 0;
-	int lane;
-	uint8_t voltage_max;
-	uint8_t preemph_max;
-
-	for (lane = 0; lane < intel_dp->lane_count; lane++) {
-		uint8_t this_v = drm_dp_get_adjust_request_voltage(link_status, lane);
-		uint8_t this_p = drm_dp_get_adjust_request_pre_emphasis(link_status, lane);
-
-		if (this_v > v)
-			v = this_v;
-		if (this_p > p)
-			p = this_p;
-	}
-
-	voltage_max = intel_dp_voltage_max(intel_dp);
-	if (v >= voltage_max)
-		v = voltage_max | DP_TRAIN_MAX_SWING_REACHED;
-
-	preemph_max = intel_dp_pre_emphasis_max(intel_dp, v);
-	if (p >= preemph_max)
-		p = preemph_max | DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
-
-	for (lane = 0; lane < 4; lane++)
-		intel_dp->train_set[lane] = v | p;
-}
-
 static uint32_t
 gen4_signal_levels(uint8_t train_set)
 {
@@ -3598,7 +3566,7 @@ intel_dp_set_signal_levels(struct intel_dp *intel_dp, uint32_t *DP)
 	*DP = (*DP & ~mask) | signal_levels;
 }
 
-static void
+void
 intel_dp_program_link_training_pattern(struct intel_dp *intel_dp,
 				       uint8_t dp_train_pat)
 {
@@ -3612,33 +3580,7 @@ intel_dp_program_link_training_pattern(struct intel_dp *intel_dp,
 	POSTING_READ(intel_dp->output_reg);
 }
 
-static bool
-intel_dp_set_link_train(struct intel_dp *intel_dp,
-			uint8_t dp_train_pat)
-{
-	uint8_t buf[sizeof(intel_dp->train_set) + 1];
-	int ret, len;
-
-	intel_dp_program_link_training_pattern(intel_dp, dp_train_pat);
-
-	buf[0] = dp_train_pat;
-	if ((dp_train_pat & DP_TRAINING_PATTERN_MASK) ==
-	    DP_TRAINING_PATTERN_DISABLE) {
-		/* don't write DP_TRAINING_LANEx_SET on disable */
-		len = 1;
-	} else {
-		/* DP_TRAINING_LANEx_SET follow DP_TRAINING_PATTERN_SET */
-		memcpy(buf + 1, intel_dp->train_set, intel_dp->lane_count);
-		len = intel_dp->lane_count + 1;
-	}
-
-	ret = drm_dp_dpcd_write(&intel_dp->aux, DP_TRAINING_PATTERN_SET,
-				buf, len);
-
-	return ret == len;
-}
-
-static void
+void
 intel_dp_update_signal_levels(struct intel_dp *intel_dp)
 {
 	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
@@ -3651,30 +3593,7 @@ intel_dp_update_signal_levels(struct intel_dp *intel_dp)
 	POSTING_READ(intel_dp->output_reg);
 }
 
-static bool
-intel_dp_reset_link_train(struct intel_dp *intel_dp,
-			uint8_t dp_train_pat)
-{
-	if (!intel_dp->train_set_valid)
-		memset(intel_dp->train_set, 0, sizeof(intel_dp->train_set));
-	intel_dp_update_signal_levels(intel_dp);
-	return intel_dp_set_link_train(intel_dp, dp_train_pat);
-}
-
-static bool
-intel_dp_update_link_train(struct intel_dp *intel_dp)
-{
-	int ret;
-
-	intel_dp_update_signal_levels(intel_dp);
-
-	ret = drm_dp_dpcd_write(&intel_dp->aux, DP_TRAINING_LANE0_SET,
-				intel_dp->train_set, intel_dp->lane_count);
-
-	return ret == intel_dp->lane_count;
-}
-
-static void intel_dp_set_idle_link_train(struct intel_dp *intel_dp)
+void intel_dp_set_idle_link_train(struct intel_dp *intel_dp)
 {
 	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
 	struct drm_device *dev = intel_dig_port->base.base.dev;
@@ -3705,235 +3624,6 @@ static void intel_dp_set_idle_link_train(struct intel_dp *intel_dp)
 		DRM_ERROR("Timed out waiting for DP idle patterns\n");
 }
 
-/* Enable corresponding port and start training pattern 1 */
-static bool
-intel_dp_link_training_clock_recovery(struct intel_dp *intel_dp)
-{
-	struct drm_encoder *encoder = &dp_to_dig_port(intel_dp)->base.base;
-	struct drm_device *dev = encoder->dev;
-	int i;
-	uint8_t voltage;
-	int voltage_tries, loop_tries;
-	uint8_t link_config[2];
-	uint8_t link_bw, rate_select;
-	uint8_t link_status[DP_LINK_STATUS_SIZE];
-
-	if (HAS_DDI(dev))
-		intel_ddi_prepare_link_retrain(encoder);
-
-	intel_dp_compute_rate(intel_dp, intel_dp->link_rate,
-			      &link_bw, &rate_select);
-
-	/* Write the link configuration data */
-	link_config[0] = link_bw;
-	link_config[1] = intel_dp->lane_count;
-	if (drm_dp_enhanced_frame_cap(intel_dp->dpcd))
-		link_config[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
-	drm_dp_dpcd_write(&intel_dp->aux, DP_LINK_BW_SET, link_config, 2);
-	if (intel_dp->num_sink_rates)
-		drm_dp_dpcd_write(&intel_dp->aux, DP_LINK_RATE_SET,
-				  &rate_select, 1);
-
-	link_config[0] = 0;
-	link_config[1] = DP_SET_ANSI_8B10B;
-	drm_dp_dpcd_write(&intel_dp->aux, DP_DOWNSPREAD_CTRL, link_config, 2);
-
-	intel_dp->DP |= DP_PORT_EN;
-
-	/* clock recovery */
-	if (!intel_dp_reset_link_train(intel_dp,
-				       DP_TRAINING_PATTERN_1 |
-				       DP_LINK_SCRAMBLING_DISABLE)) {
-		DRM_ERROR("failed to enable link training\n");
-		return false;
-	}
-
-	voltage = 0xff;
-	voltage_tries = 0;
-	loop_tries = 0;
-	for (;;) {
-		drm_dp_link_train_clock_recovery_delay(intel_dp->dpcd);
-		if (!intel_dp_get_link_status(intel_dp, link_status)) {
-			DRM_ERROR("failed to get link status\n");
-			break;
-		}
-
-		if (drm_dp_clock_recovery_ok(link_status, intel_dp->lane_count)) {
-			DRM_DEBUG_KMS("clock recovery OK\n");
-			break;
-		}
-
-		/*
-		 * if we used previously trained voltage and pre-emphasis values
-		 * and we don't get clock recovery, reset link training values
-		 */
-		if (intel_dp->train_set_valid) {
-			DRM_DEBUG_KMS("clock recovery not ok, reset");
-			/* clear the flag as we are not reusing train set */
-			intel_dp->train_set_valid = false;
-			if (!intel_dp_reset_link_train(intel_dp,
-						       DP_TRAINING_PATTERN_1 |
-						       DP_LINK_SCRAMBLING_DISABLE)) {
-				DRM_ERROR("failed to enable link training\n");
-				return false;
-			}
-			continue;
-		}
-
-		/* Check to see if we've tried the max voltage */
-		for (i = 0; i < intel_dp->lane_count; i++)
-			if ((intel_dp->train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0)
-				break;
-		if (i == intel_dp->lane_count) {
-			++loop_tries;
-			if (loop_tries == 5) {
-				DRM_ERROR("too many full retries, give up\n");
-				break;
-			}
-			intel_dp_reset_link_train(intel_dp,
-						  DP_TRAINING_PATTERN_1 |
-						  DP_LINK_SCRAMBLING_DISABLE);
-			voltage_tries = 0;
-			continue;
-		}
-
-		/* Check to see if we've tried the same voltage 5 times */
-		if ((intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) {
-			++voltage_tries;
-			if (voltage_tries == 5) {
-				DRM_ERROR("too many voltage retries, give up\n");
-				break;
-			}
-		} else
-			voltage_tries = 0;
-		voltage = intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
-
-		/* Update training set as requested by target */
-		intel_get_adjust_train(intel_dp, link_status);
-		if (!intel_dp_update_link_train(intel_dp)) {
-			DRM_ERROR("failed to update link training\n");
-			break;
-		}
-	}
-
-	return drm_dp_clock_recovery_ok(link_status, intel_dp->lane_count);
-}
-
-static bool
-intel_dp_link_training_channel_equalization(struct intel_dp *intel_dp)
-{
-	struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
-	struct drm_device *dev = dig_port->base.base.dev;
-	bool channel_eq = false;
-	int tries, cr_tries;
-	uint32_t training_pattern = DP_TRAINING_PATTERN_2;
-
-	/*
-	 * Training Pattern 3 for HBR2 or 1.2 devices that support it.
-	 *
-	 * Intel platforms that support HBR2 also support TPS3. TPS3 support is
-	 * also mandatory for downstream devices that support HBR2.
-	 *
-	 * Due to WaDisableHBR2 SKL < B0 is the only exception where TPS3 is
-	 * supported but still not enabled.
-	 */
-	if (intel_dp_source_supports_hbr2(dev) &&
-	    drm_dp_tps3_supported(intel_dp->dpcd))
-		training_pattern = DP_TRAINING_PATTERN_3;
-	else if (intel_dp->link_rate == 540000)
-		DRM_ERROR("5.4 Gbps link rate without HBR2/TPS3 support\n");
-
-	/* channel equalization */
-	if (!intel_dp_set_link_train(intel_dp,
-				     training_pattern |
-				     DP_LINK_SCRAMBLING_DISABLE)) {
-		DRM_ERROR("failed to start channel equalization\n");
-		return false;
-	}
-
-	tries = 0;
-	cr_tries = 0;
-	channel_eq = false;
-	for (;;) {
-		uint8_t link_status[DP_LINK_STATUS_SIZE];
-
-		if (cr_tries > 5) {
-			DRM_ERROR("failed to train DP, aborting\n");
-			break;
-		}
-
-		drm_dp_link_train_channel_eq_delay(intel_dp->dpcd);
-		if (!intel_dp_get_link_status(intel_dp, link_status)) {
-			DRM_ERROR("failed to get link status\n");
-			break;
-		}
-
-		/* Make sure clock is still ok */
-		if (!drm_dp_clock_recovery_ok(link_status,
-					      intel_dp->lane_count)) {
-			intel_dp->train_set_valid = false;
-			intel_dp_link_training_clock_recovery(intel_dp);
-			intel_dp_set_link_train(intel_dp,
-						training_pattern |
-						DP_LINK_SCRAMBLING_DISABLE);
-			cr_tries++;
-			continue;
-		}
-
-		if (drm_dp_channel_eq_ok(link_status,
-					 intel_dp->lane_count)) {
-			channel_eq = true;
-			break;
-		}
-
-		/* Try 5 times, then try clock recovery if that fails */
-		if (tries > 5) {
-			intel_dp->train_set_valid = false;
-			intel_dp_link_training_clock_recovery(intel_dp);
-			intel_dp_set_link_train(intel_dp,
-						training_pattern |
-						DP_LINK_SCRAMBLING_DISABLE);
-			tries = 0;
-			cr_tries++;
-			continue;
-		}
-
-		/* Update training set as requested by target */
-		intel_get_adjust_train(intel_dp, link_status);
-		if (!intel_dp_update_link_train(intel_dp)) {
-			DRM_ERROR("failed to update link training\n");
-			break;
-		}
-		++tries;
-	}
-
-	intel_dp_set_idle_link_train(intel_dp);
-
-	if (channel_eq) {
-		intel_dp->train_set_valid = true;
-		DRM_DEBUG_KMS("Channel EQ done. DP Training successful\n");
-		return true;
-	} else {
-		return false;
-	}
-}
-
-void intel_dp_stop_link_train(struct intel_dp *intel_dp)
-{
-	intel_dp_set_link_train(intel_dp,
-				DP_TRAINING_PATTERN_DISABLE);
-}
-
-void
-intel_dp_start_link_train(struct intel_dp *intel_dp)
-{
-	uint32_t DP = intel_dp->DP;
-
-	if (!intel_dp_link_training_clock_recovery(intel_dp) ||
-	    !intel_dp_link_training_channel_equalization(intel_dp))
-		intel_dp->DP = DP;
-}
-
 static void
 intel_dp_link_down(struct intel_dp *intel_dp)
 {
diff --git a/drivers/gpu/drm/i915/intel_dp_link_training.c b/drivers/gpu/drm/i915/intel_dp_link_training.c
new file mode 100644
index 0000000..f33cbbb
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_dp_link_training.c
@@ -0,0 +1,336 @@
+/*
+ * Copyright © 2008-2015 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include "intel_drv.h"
+
+static void
+intel_get_adjust_train(struct intel_dp *intel_dp,
+		       const uint8_t link_status[DP_LINK_STATUS_SIZE])
+{
+	uint8_t v = 0;
+	uint8_t p = 0;
+	int lane;
+	uint8_t voltage_max;
+	uint8_t preemph_max;
+
+	for (lane = 0; lane < intel_dp->lane_count; lane++) {
+		uint8_t this_v = drm_dp_get_adjust_request_voltage(link_status, lane);
+		uint8_t this_p = drm_dp_get_adjust_request_pre_emphasis(link_status, lane);
+
+		if (this_v > v)
+			v = this_v;
+		if (this_p > p)
+			p = this_p;
+	}
+
+	voltage_max = intel_dp_voltage_max(intel_dp);
+	if (v >= voltage_max)
+		v = voltage_max | DP_TRAIN_MAX_SWING_REACHED;
+
+	preemph_max = intel_dp_pre_emphasis_max(intel_dp, v);
+	if (p >= preemph_max)
+		p = preemph_max | DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
+
+	for (lane = 0; lane < 4; lane++)
+		intel_dp->train_set[lane] = v | p;
+}
+
+static bool
+intel_dp_set_link_train(struct intel_dp *intel_dp,
+			uint8_t dp_train_pat)
+{
+	uint8_t buf[sizeof(intel_dp->train_set) + 1];
+	int ret, len;
+
+	intel_dp_program_link_training_pattern(intel_dp, dp_train_pat);
+
+	buf[0] = dp_train_pat;
+	if ((dp_train_pat & DP_TRAINING_PATTERN_MASK) ==
+	    DP_TRAINING_PATTERN_DISABLE) {
+		/* don't write DP_TRAINING_LANEx_SET on disable */
+		len = 1;
+	} else {
+		/* DP_TRAINING_LANEx_SET follow DP_TRAINING_PATTERN_SET */
+		memcpy(buf + 1, intel_dp->train_set, intel_dp->lane_count);
+		len = intel_dp->lane_count + 1;
+	}
+
+	ret = drm_dp_dpcd_write(&intel_dp->aux, DP_TRAINING_PATTERN_SET,
+				buf, len);
+
+	return ret == len;
+}
+
+static bool
+intel_dp_reset_link_train(struct intel_dp *intel_dp,
+			uint8_t dp_train_pat)
+{
+	if (!intel_dp->train_set_valid)
+		memset(intel_dp->train_set, 0, sizeof(intel_dp->train_set));
+	intel_dp_update_signal_levels(intel_dp);
+	return intel_dp_set_link_train(intel_dp, dp_train_pat);
+}
+
+static bool
+intel_dp_update_link_train(struct intel_dp *intel_dp)
+{
+	int ret;
+
+	intel_dp_update_signal_levels(intel_dp);
+
+	ret = drm_dp_dpcd_write(&intel_dp->aux, DP_TRAINING_LANE0_SET,
+				intel_dp->train_set, intel_dp->lane_count);
+
+	return ret == intel_dp->lane_count;
+}
+
+/* Enable corresponding port and start training pattern 1 */
+static bool
+intel_dp_link_training_clock_recovery(struct intel_dp *intel_dp)
+{
+	struct drm_encoder *encoder = &dp_to_dig_port(intel_dp)->base.base;
+	struct drm_device *dev = encoder->dev;
+	int i;
+	uint8_t voltage;
+	int voltage_tries, loop_tries;
+	uint8_t link_config[2];
+	uint8_t link_bw, rate_select;
+	uint8_t link_status[DP_LINK_STATUS_SIZE];
+
+	if (HAS_DDI(dev))
+		intel_ddi_prepare_link_retrain(encoder);
+
+	intel_dp_compute_rate(intel_dp, intel_dp->link_rate,
+			      &link_bw, &rate_select);
+
+	/* Write the link configuration data */
+	link_config[0] = link_bw;
+	link_config[1] = intel_dp->lane_count;
+	if (drm_dp_enhanced_frame_cap(intel_dp->dpcd))
+		link_config[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
+	drm_dp_dpcd_write(&intel_dp->aux, DP_LINK_BW_SET, link_config, 2);
+	if (intel_dp->num_sink_rates)
+		drm_dp_dpcd_write(&intel_dp->aux, DP_LINK_RATE_SET,
+				  &rate_select, 1);
+
+	link_config[0] = 0;
+	link_config[1] = DP_SET_ANSI_8B10B;
+	drm_dp_dpcd_write(&intel_dp->aux, DP_DOWNSPREAD_CTRL, link_config, 2);
+
+	intel_dp->DP |= DP_PORT_EN;
+
+	/* clock recovery */
+	if (!intel_dp_reset_link_train(intel_dp,
+				       DP_TRAINING_PATTERN_1 |
+				       DP_LINK_SCRAMBLING_DISABLE)) {
+		DRM_ERROR("failed to enable link training\n");
+		return false;
+	}
+
+	voltage = 0xff;
+	voltage_tries = 0;
+	loop_tries = 0;
+	for (;;) {
+		drm_dp_link_train_clock_recovery_delay(intel_dp->dpcd);
+		if (!intel_dp_get_link_status(intel_dp, link_status)) {
+			DRM_ERROR("failed to get link status\n");
+			break;
+		}
+
+		if (drm_dp_clock_recovery_ok(link_status, intel_dp->lane_count)) {
+			DRM_DEBUG_KMS("clock recovery OK\n");
+			break;
+		}
+
+		/*
+		 * if we used previously trained voltage and pre-emphasis values
+		 * and we don't get clock recovery, reset link training values
+		 */
+		if (intel_dp->train_set_valid) {
+			DRM_DEBUG_KMS("clock recovery not ok, reset");
+			/* clear the flag as we are not reusing train set */
+			intel_dp->train_set_valid = false;
+			if (!intel_dp_reset_link_train(intel_dp,
+						       DP_TRAINING_PATTERN_1 |
+						       DP_LINK_SCRAMBLING_DISABLE)) {
+				DRM_ERROR("failed to enable link training\n");
+				return false;
+			}
+			continue;
+		}
+
+		/* Check to see if we've tried the max voltage */
+		for (i = 0; i < intel_dp->lane_count; i++)
+			if ((intel_dp->train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0)
+				break;
+		if (i == intel_dp->lane_count) {
+			++loop_tries;
+			if (loop_tries == 5) {
+				DRM_ERROR("too many full retries, give up\n");
+				break;
+			}
+			intel_dp_reset_link_train(intel_dp,
+						  DP_TRAINING_PATTERN_1 |
+						  DP_LINK_SCRAMBLING_DISABLE);
+			voltage_tries = 0;
+			continue;
+		}
+
+		/* Check to see if we've tried the same voltage 5 times */
+		if ((intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) {
+			++voltage_tries;
+			if (voltage_tries == 5) {
+				DRM_ERROR("too many voltage retries, give up\n");
+				break;
+			}
+		} else
+			voltage_tries = 0;
+		voltage = intel_dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
+
+		/* Update training set as requested by target */
+		intel_get_adjust_train(intel_dp, link_status);
+		if (!intel_dp_update_link_train(intel_dp)) {
+			DRM_ERROR("failed to update link training\n");
+			break;
+		}
+	}
+
+	return drm_dp_clock_recovery_ok(link_status, intel_dp->lane_count);
+}
+
+static bool
+intel_dp_link_training_channel_equalization(struct intel_dp *intel_dp)
+{
+	struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
+	struct drm_device *dev = dig_port->base.base.dev;
+	bool channel_eq = false;
+	int tries, cr_tries;
+	uint32_t training_pattern = DP_TRAINING_PATTERN_2;
+
+	/*
+	 * Training Pattern 3 for HBR2 or 1.2 devices that support it.
+	 *
+	 * Intel platforms that support HBR2 also support TPS3. TPS3 support is
+	 * also mandatory for downstream devices that support HBR2.
+	 *
+	 * Due to WaDisableHBR2 SKL < B0 is the only exception where TPS3 is
+	 * supported but still not enabled.
+	 */
+	if (intel_dp_source_supports_hbr2(dev) &&
+	    drm_dp_tps3_supported(intel_dp->dpcd))
+		training_pattern = DP_TRAINING_PATTERN_3;
+	else if (intel_dp->link_rate == 540000)
+		DRM_ERROR("5.4 Gbps link rate without HBR2/TPS3 support\n");
+
+	/* channel equalization */
+	if (!intel_dp_set_link_train(intel_dp,
+				     training_pattern |
+				     DP_LINK_SCRAMBLING_DISABLE)) {
+		DRM_ERROR("failed to start channel equalization\n");
+		return false;
+	}
+
+	tries = 0;
+	cr_tries = 0;
+	channel_eq = false;
+	for (;;) {
+		uint8_t link_status[DP_LINK_STATUS_SIZE];
+
+		if (cr_tries > 5) {
+			DRM_ERROR("failed to train DP, aborting\n");
+			break;
+		}
+
+		drm_dp_link_train_channel_eq_delay(intel_dp->dpcd);
+		if (!intel_dp_get_link_status(intel_dp, link_status)) {
+			DRM_ERROR("failed to get link status\n");
+			break;
+		}
+
+		/* Make sure clock is still ok */
+		if (!drm_dp_clock_recovery_ok(link_status,
+					      intel_dp->lane_count)) {
+			intel_dp->train_set_valid = false;
+			intel_dp_link_training_clock_recovery(intel_dp);
+			intel_dp_set_link_train(intel_dp,
+						training_pattern |
+						DP_LINK_SCRAMBLING_DISABLE);
+			cr_tries++;
+			continue;
+		}
+
+		if (drm_dp_channel_eq_ok(link_status,
+					 intel_dp->lane_count)) {
+			channel_eq = true;
+			break;
+		}
+
+		/* Try 5 times, then try clock recovery if that fails */
+		if (tries > 5) {
+			intel_dp->train_set_valid = false;
+			intel_dp_link_training_clock_recovery(intel_dp);
+			intel_dp_set_link_train(intel_dp,
+						training_pattern |
+						DP_LINK_SCRAMBLING_DISABLE);
+			tries = 0;
+			cr_tries++;
+			continue;
+		}
+
+		/* Update training set as requested by target */
+		intel_get_adjust_train(intel_dp, link_status);
+		if (!intel_dp_update_link_train(intel_dp)) {
+			DRM_ERROR("failed to update link training\n");
+			break;
+		}
+		++tries;
+	}
+
+	intel_dp_set_idle_link_train(intel_dp);
+
+	if (channel_eq) {
+		intel_dp->train_set_valid = true;
+		DRM_DEBUG_KMS("Channel EQ done. DP Training successful\n");
+		return true;
+	} else {
+		return false;
+	}
+}
+
+void intel_dp_stop_link_train(struct intel_dp *intel_dp)
+{
+	intel_dp_set_link_train(intel_dp,
+				DP_TRAINING_PATTERN_DISABLE);
+}
+
+void
+intel_dp_start_link_train(struct intel_dp *intel_dp)
+{
+	uint32_t DP = intel_dp->DP;
+
+	if (!intel_dp_link_training_clock_recovery(intel_dp) ||
+	    !intel_dp_link_training_channel_equalization(intel_dp))
+		intel_dp->DP = DP;
+}
+
+
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 6bac79b..29ae4bb 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -1210,6 +1210,22 @@ void intel_edp_drrs_invalidate(struct drm_device *dev,
 void intel_edp_drrs_flush(struct drm_device *dev, unsigned frontbuffer_bits);
 void hsw_dp_set_ddi_pll_sel(struct intel_crtc_state *pipe_config);
 
+void
+intel_dp_program_link_training_pattern(struct intel_dp *intel_dp,
+				       uint8_t dp_train_pat);
+void
+intel_dp_update_signal_levels(struct intel_dp *intel_dp);
+void intel_dp_set_idle_link_train(struct intel_dp *intel_dp);
+uint8_t
+intel_dp_voltage_max(struct intel_dp *intel_dp);
+uint8_t
+intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t voltage_swing);
+void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock,
+			   uint8_t *link_bw, uint8_t *rate_select);
+bool intel_dp_source_supports_hbr2(struct drm_device *dev);
+bool
+intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_STATUS_SIZE]);
+
 /* intel_dp_mst.c */
 int intel_dp_mst_encoder_init(struct intel_digital_port *intel_dig_port, int conn_id);
 void intel_dp_mst_encoder_cleanup(struct intel_digital_port *intel_dig_port);
-- 
2.4.3

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 7/8] drm/i915: Move max voltage and pre emphasis to intel_dp_link_training.c
  2015-09-08 12:27 [PATCH 0/8] [RFC] Link training test Ander Conselvan de Oliveira
                   ` (5 preceding siblings ...)
  2015-09-08 12:27 ` [PATCH 6/8] drm/i915: Move generic link training code to a separate file Ander Conselvan de Oliveira
@ 2015-09-08 12:27 ` Ander Conselvan de Oliveira
  2015-09-08 13:01   ` Ville Syrjälä
  2015-09-08 12:27 ` [PATCH 8/8] drm/i915: Extract intel_dev_info.[ch] Ander Conselvan de Oliveira
                   ` (2 subsequent siblings)
  9 siblings, 1 reply; 31+ messages in thread
From: Ander Conselvan de Oliveira @ 2015-09-08 12:27 UTC (permalink / raw)
  To: intel-gfx; +Cc: jani.nikula, Ander Conselvan de Oliveira

So link training tests can use real hardware limits.
---
 drivers/gpu/drm/i915/intel_dp.c               | 99 ---------------------------
 drivers/gpu/drm/i915/intel_dp_link_training.c | 92 +++++++++++++++++++++++++
 drivers/gpu/drm/i915/intel_drv.h              | 11 +--
 3 files changed, 99 insertions(+), 103 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 5778059..da87aef 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -111,13 +111,6 @@ static bool is_edp(struct intel_dp *intel_dp)
 	return intel_dig_port->base.type == INTEL_OUTPUT_EDP;
 }
 
-static struct drm_device *intel_dp_to_dev(struct intel_dp *intel_dp)
-{
-	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
-
-	return intel_dig_port->base.base.dev;
-}
-
 static struct intel_dp *intel_attached_dp(struct drm_connector *connector)
 {
 	return enc_to_intel_dp(&intel_attached_encoder(connector)->base);
@@ -3054,98 +3047,6 @@ intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_
 				       DP_LINK_STATUS_SIZE) == DP_LINK_STATUS_SIZE;
 }
 
-/* These are source-specific values. */
-uint8_t
-intel_dp_voltage_max(struct intel_dp *intel_dp)
-{
-	struct drm_device *dev = intel_dp_to_dev(intel_dp);
-	struct drm_i915_private *dev_priv = dev->dev_private;
-	enum port port = dp_to_dig_port(intel_dp)->port;
-
-	if (IS_BROXTON(dev))
-		return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
-	else if (INTEL_INFO(dev)->gen >= 9) {
-		if (dev_priv->edp_low_vswing && port == PORT_A)
-			return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
-		return DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
-	} else if (IS_VALLEYVIEW(dev))
-		return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
-	else if (IS_GEN7(dev) && port == PORT_A)
-		return DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
-	else if (HAS_PCH_CPT(dev) && port != PORT_A)
-		return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
-	else
-		return DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
-}
-
-uint8_t
-intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t voltage_swing)
-{
-	struct drm_device *dev = intel_dp_to_dev(intel_dp);
-	enum port port = dp_to_dig_port(intel_dp)->port;
-
-	if (INTEL_INFO(dev)->gen >= 9) {
-		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
-		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
-			return DP_TRAIN_PRE_EMPH_LEVEL_3;
-		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
-			return DP_TRAIN_PRE_EMPH_LEVEL_2;
-		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
-			return DP_TRAIN_PRE_EMPH_LEVEL_1;
-		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
-			return DP_TRAIN_PRE_EMPH_LEVEL_0;
-		default:
-			return DP_TRAIN_PRE_EMPH_LEVEL_0;
-		}
-	} else if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
-		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
-		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
-			return DP_TRAIN_PRE_EMPH_LEVEL_3;
-		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
-			return DP_TRAIN_PRE_EMPH_LEVEL_2;
-		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
-			return DP_TRAIN_PRE_EMPH_LEVEL_1;
-		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
-		default:
-			return DP_TRAIN_PRE_EMPH_LEVEL_0;
-		}
-	} else if (IS_VALLEYVIEW(dev)) {
-		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
-		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
-			return DP_TRAIN_PRE_EMPH_LEVEL_3;
-		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
-			return DP_TRAIN_PRE_EMPH_LEVEL_2;
-		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
-			return DP_TRAIN_PRE_EMPH_LEVEL_1;
-		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
-		default:
-			return DP_TRAIN_PRE_EMPH_LEVEL_0;
-		}
-	} else if (IS_GEN7(dev) && port == PORT_A) {
-		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
-		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
-			return DP_TRAIN_PRE_EMPH_LEVEL_2;
-		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
-		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
-			return DP_TRAIN_PRE_EMPH_LEVEL_1;
-		default:
-			return DP_TRAIN_PRE_EMPH_LEVEL_0;
-		}
-	} else {
-		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
-		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
-			return DP_TRAIN_PRE_EMPH_LEVEL_2;
-		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
-			return DP_TRAIN_PRE_EMPH_LEVEL_2;
-		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
-			return DP_TRAIN_PRE_EMPH_LEVEL_1;
-		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
-		default:
-			return DP_TRAIN_PRE_EMPH_LEVEL_0;
-		}
-	}
-}
-
 static uint32_t vlv_signal_levels(struct intel_dp *intel_dp)
 {
 	struct drm_device *dev = intel_dp_to_dev(intel_dp);
diff --git a/drivers/gpu/drm/i915/intel_dp_link_training.c b/drivers/gpu/drm/i915/intel_dp_link_training.c
index f33cbbb..8d27dce 100644
--- a/drivers/gpu/drm/i915/intel_dp_link_training.c
+++ b/drivers/gpu/drm/i915/intel_dp_link_training.c
@@ -23,6 +23,98 @@
 
 #include "intel_drv.h"
 
+/* These are source-specific values. */
+static uint8_t
+intel_dp_voltage_max(struct intel_dp *intel_dp)
+{
+	struct drm_device *dev = intel_dp_to_dev(intel_dp);
+	struct drm_i915_private *dev_priv = dev->dev_private;
+	enum port port = dp_to_dig_port(intel_dp)->port;
+
+	if (IS_BROXTON(dev))
+		return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
+	else if (INTEL_INFO(dev)->gen >= 9) {
+		if (dev_priv->edp_low_vswing && port == PORT_A)
+			return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
+		return DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
+	} else if (IS_VALLEYVIEW(dev))
+		return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
+	else if (IS_GEN7(dev) && port == PORT_A)
+		return DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
+	else if (HAS_PCH_CPT(dev) && port != PORT_A)
+		return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
+	else
+		return DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
+}
+
+static uint8_t
+intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t voltage_swing)
+{
+	struct drm_device *dev = intel_dp_to_dev(intel_dp);
+	enum port port = dp_to_dig_port(intel_dp)->port;
+
+	if (INTEL_INFO(dev)->gen >= 9) {
+		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
+		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
+			return DP_TRAIN_PRE_EMPH_LEVEL_3;
+		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
+			return DP_TRAIN_PRE_EMPH_LEVEL_2;
+		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
+			return DP_TRAIN_PRE_EMPH_LEVEL_1;
+		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
+			return DP_TRAIN_PRE_EMPH_LEVEL_0;
+		default:
+			return DP_TRAIN_PRE_EMPH_LEVEL_0;
+		}
+	} else if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
+		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
+		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
+			return DP_TRAIN_PRE_EMPH_LEVEL_3;
+		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
+			return DP_TRAIN_PRE_EMPH_LEVEL_2;
+		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
+			return DP_TRAIN_PRE_EMPH_LEVEL_1;
+		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
+		default:
+			return DP_TRAIN_PRE_EMPH_LEVEL_0;
+		}
+	} else if (IS_VALLEYVIEW(dev)) {
+		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
+		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
+			return DP_TRAIN_PRE_EMPH_LEVEL_3;
+		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
+			return DP_TRAIN_PRE_EMPH_LEVEL_2;
+		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
+			return DP_TRAIN_PRE_EMPH_LEVEL_1;
+		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
+		default:
+			return DP_TRAIN_PRE_EMPH_LEVEL_0;
+		}
+	} else if (IS_GEN7(dev) && port == PORT_A) {
+		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
+		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
+			return DP_TRAIN_PRE_EMPH_LEVEL_2;
+		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
+		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
+			return DP_TRAIN_PRE_EMPH_LEVEL_1;
+		default:
+			return DP_TRAIN_PRE_EMPH_LEVEL_0;
+		}
+	} else {
+		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
+		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
+			return DP_TRAIN_PRE_EMPH_LEVEL_2;
+		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
+			return DP_TRAIN_PRE_EMPH_LEVEL_2;
+		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
+			return DP_TRAIN_PRE_EMPH_LEVEL_1;
+		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
+		default:
+			return DP_TRAIN_PRE_EMPH_LEVEL_0;
+		}
+	}
+}
+
 static void
 intel_get_adjust_train(struct intel_dp *intel_dp,
 		       const uint8_t link_status[DP_LINK_STATUS_SIZE])
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 29ae4bb..671a20f 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -1216,15 +1216,18 @@ intel_dp_program_link_training_pattern(struct intel_dp *intel_dp,
 void
 intel_dp_update_signal_levels(struct intel_dp *intel_dp);
 void intel_dp_set_idle_link_train(struct intel_dp *intel_dp);
-uint8_t
-intel_dp_voltage_max(struct intel_dp *intel_dp);
-uint8_t
-intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t voltage_swing);
 void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock,
 			   uint8_t *link_bw, uint8_t *rate_select);
 bool intel_dp_source_supports_hbr2(struct drm_device *dev);
 bool
 intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_STATUS_SIZE]);
+static inline struct drm_device *intel_dp_to_dev(struct intel_dp *intel_dp)
+{
+	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+
+	return intel_dig_port->base.base.dev;
+}
+
 
 /* intel_dp_mst.c */
 int intel_dp_mst_encoder_init(struct intel_digital_port *intel_dig_port, int conn_id);
-- 
2.4.3

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 8/8] drm/i915: Extract intel_dev_info.[ch]
  2015-09-08 12:27 [PATCH 0/8] [RFC] Link training test Ander Conselvan de Oliveira
                   ` (6 preceding siblings ...)
  2015-09-08 12:27 ` [PATCH 7/8] drm/i915: Move max voltage and pre emphasis to intel_dp_link_training.c Ander Conselvan de Oliveira
@ 2015-09-08 12:27 ` Ander Conselvan de Oliveira
  2015-09-08 12:28 ` [PATCH i-g-t] Add a link training test Ander Conselvan de Oliveira
  2015-09-22 15:12 ` [PATCH 0/8] [RFC] Link " Daniel Vetter
  9 siblings, 0 replies; 31+ messages in thread
From: Ander Conselvan de Oliveira @ 2015-09-08 12:27 UTC (permalink / raw)
  To: intel-gfx; +Cc: jani.nikula, Ander Conselvan de Oliveira

Put all device information related stuff in separate files to that it is
easy to pull them for the link training tests in i-g-t.
---
 drivers/gpu/drm/i915/i915_drv.c         | 395 +-----------------------------
 drivers/gpu/drm/i915/i915_drv.h         | 286 +---------------------
 drivers/gpu/drm/i915/intel_dev_info.c   | 422 ++++++++++++++++++++++++++++++++
 drivers/gpu/drm/i915/intel_dev_info.h   | 331 +++++++++++++++++++++++++
 drivers/gpu/drm/i915/intel_ringbuffer.h |  11 +-
 5 files changed, 758 insertions(+), 687 deletions(-)
 create mode 100644 drivers/gpu/drm/i915/intel_dev_info.c
 create mode 100644 drivers/gpu/drm/i915/intel_dev_info.h

diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index bdec64c..6684f9b 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -42,399 +42,8 @@
 
 static struct drm_driver driver;
 
-#define GEN_DEFAULT_PIPEOFFSETS \
-	.pipe_offsets = { PIPE_A_OFFSET, PIPE_B_OFFSET, \
-			  PIPE_C_OFFSET, PIPE_EDP_OFFSET }, \
-	.trans_offsets = { TRANSCODER_A_OFFSET, TRANSCODER_B_OFFSET, \
-			   TRANSCODER_C_OFFSET, TRANSCODER_EDP_OFFSET }, \
-	.palette_offsets = { PALETTE_A_OFFSET, PALETTE_B_OFFSET }
-
-#define GEN_CHV_PIPEOFFSETS \
-	.pipe_offsets = { PIPE_A_OFFSET, PIPE_B_OFFSET, \
-			  CHV_PIPE_C_OFFSET }, \
-	.trans_offsets = { TRANSCODER_A_OFFSET, TRANSCODER_B_OFFSET, \
-			   CHV_TRANSCODER_C_OFFSET, }, \
-	.palette_offsets = { PALETTE_A_OFFSET, PALETTE_B_OFFSET, \
-			     CHV_PALETTE_C_OFFSET }
-
-#define CURSOR_OFFSETS \
-	.cursor_offsets = { CURSOR_A_OFFSET, CURSOR_B_OFFSET, CHV_CURSOR_C_OFFSET }
-
-#define IVB_CURSOR_OFFSETS \
-	.cursor_offsets = { CURSOR_A_OFFSET, IVB_CURSOR_B_OFFSET, IVB_CURSOR_C_OFFSET }
-
-static const struct intel_device_info intel_i830_info = {
-	.gen = 2, .is_mobile = 1, .cursor_needs_physical = 1, .num_pipes = 2,
-	.has_overlay = 1, .overlay_needs_physical = 1,
-	.ring_mask = RENDER_RING,
-	GEN_DEFAULT_PIPEOFFSETS,
-	CURSOR_OFFSETS,
-};
-
-static const struct intel_device_info intel_845g_info = {
-	.gen = 2, .num_pipes = 1,
-	.has_overlay = 1, .overlay_needs_physical = 1,
-	.ring_mask = RENDER_RING,
-	GEN_DEFAULT_PIPEOFFSETS,
-	CURSOR_OFFSETS,
-};
-
-static const struct intel_device_info intel_i85x_info = {
-	.gen = 2, .is_i85x = 1, .is_mobile = 1, .num_pipes = 2,
-	.cursor_needs_physical = 1,
-	.has_overlay = 1, .overlay_needs_physical = 1,
-	.has_fbc = 1,
-	.ring_mask = RENDER_RING,
-	GEN_DEFAULT_PIPEOFFSETS,
-	CURSOR_OFFSETS,
-};
-
-static const struct intel_device_info intel_i865g_info = {
-	.gen = 2, .num_pipes = 1,
-	.has_overlay = 1, .overlay_needs_physical = 1,
-	.ring_mask = RENDER_RING,
-	GEN_DEFAULT_PIPEOFFSETS,
-	CURSOR_OFFSETS,
-};
-
-static const struct intel_device_info intel_i915g_info = {
-	.gen = 3, .is_i915g = 1, .cursor_needs_physical = 1, .num_pipes = 2,
-	.has_overlay = 1, .overlay_needs_physical = 1,
-	.ring_mask = RENDER_RING,
-	GEN_DEFAULT_PIPEOFFSETS,
-	CURSOR_OFFSETS,
-};
-static const struct intel_device_info intel_i915gm_info = {
-	.gen = 3, .is_mobile = 1, .num_pipes = 2,
-	.cursor_needs_physical = 1,
-	.has_overlay = 1, .overlay_needs_physical = 1,
-	.supports_tv = 1,
-	.has_fbc = 1,
-	.ring_mask = RENDER_RING,
-	GEN_DEFAULT_PIPEOFFSETS,
-	CURSOR_OFFSETS,
-};
-static const struct intel_device_info intel_i945g_info = {
-	.gen = 3, .has_hotplug = 1, .cursor_needs_physical = 1, .num_pipes = 2,
-	.has_overlay = 1, .overlay_needs_physical = 1,
-	.ring_mask = RENDER_RING,
-	GEN_DEFAULT_PIPEOFFSETS,
-	CURSOR_OFFSETS,
-};
-static const struct intel_device_info intel_i945gm_info = {
-	.gen = 3, .is_i945gm = 1, .is_mobile = 1, .num_pipes = 2,
-	.has_hotplug = 1, .cursor_needs_physical = 1,
-	.has_overlay = 1, .overlay_needs_physical = 1,
-	.supports_tv = 1,
-	.has_fbc = 1,
-	.ring_mask = RENDER_RING,
-	GEN_DEFAULT_PIPEOFFSETS,
-	CURSOR_OFFSETS,
-};
-
-static const struct intel_device_info intel_i965g_info = {
-	.gen = 4, .is_broadwater = 1, .num_pipes = 2,
-	.has_hotplug = 1,
-	.has_overlay = 1,
-	.ring_mask = RENDER_RING,
-	GEN_DEFAULT_PIPEOFFSETS,
-	CURSOR_OFFSETS,
-};
-
-static const struct intel_device_info intel_i965gm_info = {
-	.gen = 4, .is_crestline = 1, .num_pipes = 2,
-	.is_mobile = 1, .has_fbc = 1, .has_hotplug = 1,
-	.has_overlay = 1,
-	.supports_tv = 1,
-	.ring_mask = RENDER_RING,
-	GEN_DEFAULT_PIPEOFFSETS,
-	CURSOR_OFFSETS,
-};
-
-static const struct intel_device_info intel_g33_info = {
-	.gen = 3, .is_g33 = 1, .num_pipes = 2,
-	.need_gfx_hws = 1, .has_hotplug = 1,
-	.has_overlay = 1,
-	.ring_mask = RENDER_RING,
-	GEN_DEFAULT_PIPEOFFSETS,
-	CURSOR_OFFSETS,
-};
-
-static const struct intel_device_info intel_g45_info = {
-	.gen = 4, .is_g4x = 1, .need_gfx_hws = 1, .num_pipes = 2,
-	.has_pipe_cxsr = 1, .has_hotplug = 1,
-	.ring_mask = RENDER_RING | BSD_RING,
-	GEN_DEFAULT_PIPEOFFSETS,
-	CURSOR_OFFSETS,
-};
-
-static const struct intel_device_info intel_gm45_info = {
-	.gen = 4, .is_g4x = 1, .num_pipes = 2,
-	.is_mobile = 1, .need_gfx_hws = 1, .has_fbc = 1,
-	.has_pipe_cxsr = 1, .has_hotplug = 1,
-	.supports_tv = 1,
-	.ring_mask = RENDER_RING | BSD_RING,
-	GEN_DEFAULT_PIPEOFFSETS,
-	CURSOR_OFFSETS,
-};
-
-static const struct intel_device_info intel_pineview_info = {
-	.gen = 3, .is_g33 = 1, .is_pineview = 1, .is_mobile = 1, .num_pipes = 2,
-	.need_gfx_hws = 1, .has_hotplug = 1,
-	.has_overlay = 1,
-	GEN_DEFAULT_PIPEOFFSETS,
-	CURSOR_OFFSETS,
-};
-
-static const struct intel_device_info intel_ironlake_d_info = {
-	.gen = 5, .num_pipes = 2,
-	.need_gfx_hws = 1, .has_hotplug = 1,
-	.ring_mask = RENDER_RING | BSD_RING,
-	GEN_DEFAULT_PIPEOFFSETS,
-	CURSOR_OFFSETS,
-};
-
-static const struct intel_device_info intel_ironlake_m_info = {
-	.gen = 5, .is_mobile = 1, .num_pipes = 2,
-	.need_gfx_hws = 1, .has_hotplug = 1,
-	.has_fbc = 1,
-	.ring_mask = RENDER_RING | BSD_RING,
-	GEN_DEFAULT_PIPEOFFSETS,
-	CURSOR_OFFSETS,
-};
-
-static const struct intel_device_info intel_sandybridge_d_info = {
-	.gen = 6, .num_pipes = 2,
-	.need_gfx_hws = 1, .has_hotplug = 1,
-	.has_fbc = 1,
-	.ring_mask = RENDER_RING | BSD_RING | BLT_RING,
-	.has_llc = 1,
-	GEN_DEFAULT_PIPEOFFSETS,
-	CURSOR_OFFSETS,
-};
-
-static const struct intel_device_info intel_sandybridge_m_info = {
-	.gen = 6, .is_mobile = 1, .num_pipes = 2,
-	.need_gfx_hws = 1, .has_hotplug = 1,
-	.has_fbc = 1,
-	.ring_mask = RENDER_RING | BSD_RING | BLT_RING,
-	.has_llc = 1,
-	GEN_DEFAULT_PIPEOFFSETS,
-	CURSOR_OFFSETS,
-};
-
-#define GEN7_FEATURES  \
-	.gen = 7, .num_pipes = 3, \
-	.need_gfx_hws = 1, .has_hotplug = 1, \
-	.has_fbc = 1, \
-	.ring_mask = RENDER_RING | BSD_RING | BLT_RING, \
-	.has_llc = 1
-
-static const struct intel_device_info intel_ivybridge_d_info = {
-	GEN7_FEATURES,
-	.is_ivybridge = 1,
-	GEN_DEFAULT_PIPEOFFSETS,
-	IVB_CURSOR_OFFSETS,
-};
-
-static const struct intel_device_info intel_ivybridge_m_info = {
-	GEN7_FEATURES,
-	.is_ivybridge = 1,
-	.is_mobile = 1,
-	GEN_DEFAULT_PIPEOFFSETS,
-	IVB_CURSOR_OFFSETS,
-};
-
-static const struct intel_device_info intel_ivybridge_q_info = {
-	GEN7_FEATURES,
-	.is_ivybridge = 1,
-	.num_pipes = 0, /* legal, last one wins */
-	GEN_DEFAULT_PIPEOFFSETS,
-	IVB_CURSOR_OFFSETS,
-};
-
-static const struct intel_device_info intel_valleyview_m_info = {
-	GEN7_FEATURES,
-	.is_mobile = 1,
-	.num_pipes = 2,
-	.is_valleyview = 1,
-	.display_mmio_offset = VLV_DISPLAY_BASE,
-	.has_fbc = 0, /* legal, last one wins */
-	.has_llc = 0, /* legal, last one wins */
-	GEN_DEFAULT_PIPEOFFSETS,
-	CURSOR_OFFSETS,
-};
-
-static const struct intel_device_info intel_valleyview_d_info = {
-	GEN7_FEATURES,
-	.num_pipes = 2,
-	.is_valleyview = 1,
-	.display_mmio_offset = VLV_DISPLAY_BASE,
-	.has_fbc = 0, /* legal, last one wins */
-	.has_llc = 0, /* legal, last one wins */
-	GEN_DEFAULT_PIPEOFFSETS,
-	CURSOR_OFFSETS,
-};
-
-static const struct intel_device_info intel_haswell_d_info = {
-	GEN7_FEATURES,
-	.is_haswell = 1,
-	.has_ddi = 1,
-	.has_fpga_dbg = 1,
-	.ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
-	GEN_DEFAULT_PIPEOFFSETS,
-	IVB_CURSOR_OFFSETS,
-};
-
-static const struct intel_device_info intel_haswell_m_info = {
-	GEN7_FEATURES,
-	.is_haswell = 1,
-	.is_mobile = 1,
-	.has_ddi = 1,
-	.has_fpga_dbg = 1,
-	.ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
-	GEN_DEFAULT_PIPEOFFSETS,
-	IVB_CURSOR_OFFSETS,
-};
-
-static const struct intel_device_info intel_broadwell_d_info = {
-	.gen = 8, .num_pipes = 3,
-	.need_gfx_hws = 1, .has_hotplug = 1,
-	.ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
-	.has_llc = 1,
-	.has_ddi = 1,
-	.has_fpga_dbg = 1,
-	.has_fbc = 1,
-	GEN_DEFAULT_PIPEOFFSETS,
-	IVB_CURSOR_OFFSETS,
-};
-
-static const struct intel_device_info intel_broadwell_m_info = {
-	.gen = 8, .is_mobile = 1, .num_pipes = 3,
-	.need_gfx_hws = 1, .has_hotplug = 1,
-	.ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
-	.has_llc = 1,
-	.has_ddi = 1,
-	.has_fpga_dbg = 1,
-	.has_fbc = 1,
-	GEN_DEFAULT_PIPEOFFSETS,
-	IVB_CURSOR_OFFSETS,
-};
-
-static const struct intel_device_info intel_broadwell_gt3d_info = {
-	.gen = 8, .num_pipes = 3,
-	.need_gfx_hws = 1, .has_hotplug = 1,
-	.ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING | BSD2_RING,
-	.has_llc = 1,
-	.has_ddi = 1,
-	.has_fpga_dbg = 1,
-	.has_fbc = 1,
-	GEN_DEFAULT_PIPEOFFSETS,
-	IVB_CURSOR_OFFSETS,
-};
-
-static const struct intel_device_info intel_broadwell_gt3m_info = {
-	.gen = 8, .is_mobile = 1, .num_pipes = 3,
-	.need_gfx_hws = 1, .has_hotplug = 1,
-	.ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING | BSD2_RING,
-	.has_llc = 1,
-	.has_ddi = 1,
-	.has_fpga_dbg = 1,
-	.has_fbc = 1,
-	GEN_DEFAULT_PIPEOFFSETS,
-	IVB_CURSOR_OFFSETS,
-};
-
-static const struct intel_device_info intel_cherryview_info = {
-	.gen = 8, .num_pipes = 3,
-	.need_gfx_hws = 1, .has_hotplug = 1,
-	.ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
-	.is_valleyview = 1,
-	.display_mmio_offset = VLV_DISPLAY_BASE,
-	GEN_CHV_PIPEOFFSETS,
-	CURSOR_OFFSETS,
-};
-
-static const struct intel_device_info intel_skylake_info = {
-	.is_skylake = 1,
-	.gen = 9, .num_pipes = 3,
-	.need_gfx_hws = 1, .has_hotplug = 1,
-	.ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
-	.has_llc = 1,
-	.has_ddi = 1,
-	.has_fpga_dbg = 1,
-	.has_fbc = 1,
-	GEN_DEFAULT_PIPEOFFSETS,
-	IVB_CURSOR_OFFSETS,
-};
-
-static const struct intel_device_info intel_skylake_gt3_info = {
-	.is_skylake = 1,
-	.gen = 9, .num_pipes = 3,
-	.need_gfx_hws = 1, .has_hotplug = 1,
-	.ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING | BSD2_RING,
-	.has_llc = 1,
-	.has_ddi = 1,
-	.has_fpga_dbg = 1,
-	.has_fbc = 1,
-	GEN_DEFAULT_PIPEOFFSETS,
-	IVB_CURSOR_OFFSETS,
-};
-
-static const struct intel_device_info intel_broxton_info = {
-	.is_preliminary = 1,
-	.gen = 9,
-	.need_gfx_hws = 1, .has_hotplug = 1,
-	.ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
-	.num_pipes = 3,
-	.has_ddi = 1,
-	.has_fpga_dbg = 1,
-	.has_fbc = 1,
-	GEN_DEFAULT_PIPEOFFSETS,
-	IVB_CURSOR_OFFSETS,
-};
-
-/*
- * Make sure any device matches here are from most specific to most
- * general.  For example, since the Quanta match is based on the subsystem
- * and subvendor IDs, we need it to come before the more general IVB
- * PCI ID matches, otherwise we'll use the wrong info struct above.
- */
-#define INTEL_PCI_IDS \
-	INTEL_I830_IDS(&intel_i830_info),	\
-	INTEL_I845G_IDS(&intel_845g_info),	\
-	INTEL_I85X_IDS(&intel_i85x_info),	\
-	INTEL_I865G_IDS(&intel_i865g_info),	\
-	INTEL_I915G_IDS(&intel_i915g_info),	\
-	INTEL_I915GM_IDS(&intel_i915gm_info),	\
-	INTEL_I945G_IDS(&intel_i945g_info),	\
-	INTEL_I945GM_IDS(&intel_i945gm_info),	\
-	INTEL_I965G_IDS(&intel_i965g_info),	\
-	INTEL_G33_IDS(&intel_g33_info),		\
-	INTEL_I965GM_IDS(&intel_i965gm_info),	\
-	INTEL_GM45_IDS(&intel_gm45_info), 	\
-	INTEL_G45_IDS(&intel_g45_info), 	\
-	INTEL_PINEVIEW_IDS(&intel_pineview_info),	\
-	INTEL_IRONLAKE_D_IDS(&intel_ironlake_d_info),	\
-	INTEL_IRONLAKE_M_IDS(&intel_ironlake_m_info),	\
-	INTEL_SNB_D_IDS(&intel_sandybridge_d_info),	\
-	INTEL_SNB_M_IDS(&intel_sandybridge_m_info),	\
-	INTEL_IVB_Q_IDS(&intel_ivybridge_q_info), /* must be first IVB */ \
-	INTEL_IVB_M_IDS(&intel_ivybridge_m_info),	\
-	INTEL_IVB_D_IDS(&intel_ivybridge_d_info),	\
-	INTEL_HSW_D_IDS(&intel_haswell_d_info), \
-	INTEL_HSW_M_IDS(&intel_haswell_m_info), \
-	INTEL_VLV_M_IDS(&intel_valleyview_m_info),	\
-	INTEL_VLV_D_IDS(&intel_valleyview_d_info),	\
-	INTEL_BDW_GT12M_IDS(&intel_broadwell_m_info),	\
-	INTEL_BDW_GT12D_IDS(&intel_broadwell_d_info),	\
-	INTEL_BDW_GT3M_IDS(&intel_broadwell_gt3m_info),	\
-	INTEL_BDW_GT3D_IDS(&intel_broadwell_gt3d_info), \
-	INTEL_CHV_IDS(&intel_cherryview_info),	\
-	INTEL_SKL_GT1_IDS(&intel_skylake_info),	\
-	INTEL_SKL_GT2_IDS(&intel_skylake_info),	\
-	INTEL_SKL_GT3_IDS(&intel_skylake_gt3_info),	\
-	INTEL_BXT_IDS(&intel_broxton_info)
+/* FIXME: there's got to be a better way to do this. */
+#include "intel_dev_info.c"
 
 static const struct pci_device_id pciidlist[] = {		/* aka */
 	INTEL_PCI_IDS,
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 503dff5..6221cb1 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -51,6 +51,7 @@
 #include <linux/kref.h>
 #include <linux/pm_qos.h>
 #include "intel_guc.h"
+#include "intel_dev_info.h"
 
 /* General customization:
  */
@@ -111,25 +112,6 @@ static inline const char *yesno(bool v)
 	return v ? "yes" : "no";
 }
 
-enum pipe {
-	INVALID_PIPE = -1,
-	PIPE_A = 0,
-	PIPE_B,
-	PIPE_C,
-	_PIPE_EDP,
-	I915_MAX_PIPES = _PIPE_EDP
-};
-#define pipe_name(p) ((p) + 'A')
-
-enum transcoder {
-	TRANSCODER_A = 0,
-	TRANSCODER_B,
-	TRANSCODER_C,
-	TRANSCODER_EDP,
-	I915_MAX_TRANSCODERS
-};
-#define transcoder_name(t) ((t) + 'A')
-
 /*
  * This is the maximum (across all platforms) number of planes (primary +
  * sprites) that can be active at the same time on one pipe.
@@ -761,66 +743,6 @@ struct intel_csr {
 	enum csr_state state;
 };
 
-#define DEV_INFO_FOR_EACH_FLAG(func, sep) \
-	func(is_mobile) sep \
-	func(is_i85x) sep \
-	func(is_i915g) sep \
-	func(is_i945gm) sep \
-	func(is_g33) sep \
-	func(need_gfx_hws) sep \
-	func(is_g4x) sep \
-	func(is_pineview) sep \
-	func(is_broadwater) sep \
-	func(is_crestline) sep \
-	func(is_ivybridge) sep \
-	func(is_valleyview) sep \
-	func(is_haswell) sep \
-	func(is_skylake) sep \
-	func(is_preliminary) sep \
-	func(has_fbc) sep \
-	func(has_pipe_cxsr) sep \
-	func(has_hotplug) sep \
-	func(cursor_needs_physical) sep \
-	func(has_overlay) sep \
-	func(overlay_needs_physical) sep \
-	func(supports_tv) sep \
-	func(has_llc) sep \
-	func(has_ddi) sep \
-	func(has_fpga_dbg)
-
-#define DEFINE_FLAG(name) u8 name:1
-#define SEP_SEMICOLON ;
-
-struct intel_device_info {
-	u32 display_mmio_offset;
-	u16 device_id;
-	u8 num_pipes:3;
-	u8 num_sprites[I915_MAX_PIPES];
-	u8 gen;
-	u8 ring_mask; /* Rings supported by the HW */
-	DEV_INFO_FOR_EACH_FLAG(DEFINE_FLAG, SEP_SEMICOLON);
-	/* Register offsets for the various display pipes and transcoders */
-	int pipe_offsets[I915_MAX_TRANSCODERS];
-	int trans_offsets[I915_MAX_TRANSCODERS];
-	int palette_offsets[I915_MAX_PIPES];
-	int cursor_offsets[I915_MAX_PIPES];
-
-	/* Slice/subslice/EU info */
-	u8 slice_total;
-	u8 subslice_total;
-	u8 subslice_per_slice;
-	u8 eu_total;
-	u8 eu_per_subslice;
-	/* For each slice, which subslice(s) has(have) 7 EUs (bitfield)? */
-	u8 subslice_7eu[3];
-	u8 has_slice_pg:1;
-	u8 has_subslice_pg:1;
-	u8 has_eu_pg:1;
-};
-
-#undef DEFINE_FLAG
-#undef SEP_SEMICOLON
-
 enum i915_cache_level {
 	I915_CACHE_NONE = 0,
 	I915_CACHE_LLC, /* also used for snoopable memory on non-LLC */
@@ -995,15 +917,6 @@ struct i915_psr {
 	bool aux_frame_sync;
 };
 
-enum intel_pch {
-	PCH_NONE = 0,	/* No PCH present */
-	PCH_IBX,	/* Ibexpeak PCH */
-	PCH_CPT,	/* Cougarpoint PCH */
-	PCH_LPT,	/* Lynxpoint PCH */
-	PCH_SPT,        /* Sunrisepoint PCH */
-	PCH_NOP,
-};
-
 enum intel_sbi_destination {
 	SBI_ICLK,
 	SBI_MPHY,
@@ -2420,203 +2333,6 @@ struct drm_i915_cmd_table {
 	int count;
 };
 
-/* Note that the (struct drm_i915_private *) cast is just to shut up gcc. */
-#define __I915__(p) ({ \
-	struct drm_i915_private *__p; \
-	if (__builtin_types_compatible_p(typeof(*p), struct drm_i915_private)) \
-		__p = (struct drm_i915_private *)p; \
-	else if (__builtin_types_compatible_p(typeof(*p), struct drm_device)) \
-		__p = to_i915((struct drm_device *)p); \
-	else \
-		BUILD_BUG(); \
-	__p; \
-})
-#define INTEL_INFO(p) 	(&__I915__(p)->info)
-#define INTEL_DEVID(p)	(INTEL_INFO(p)->device_id)
-#define INTEL_REVID(p)	(__I915__(p)->dev->pdev->revision)
-
-#define IS_I830(dev)		(INTEL_DEVID(dev) == 0x3577)
-#define IS_845G(dev)		(INTEL_DEVID(dev) == 0x2562)
-#define IS_I85X(dev)		(INTEL_INFO(dev)->is_i85x)
-#define IS_I865G(dev)		(INTEL_DEVID(dev) == 0x2572)
-#define IS_I915G(dev)		(INTEL_INFO(dev)->is_i915g)
-#define IS_I915GM(dev)		(INTEL_DEVID(dev) == 0x2592)
-#define IS_I945G(dev)		(INTEL_DEVID(dev) == 0x2772)
-#define IS_I945GM(dev)		(INTEL_INFO(dev)->is_i945gm)
-#define IS_BROADWATER(dev)	(INTEL_INFO(dev)->is_broadwater)
-#define IS_CRESTLINE(dev)	(INTEL_INFO(dev)->is_crestline)
-#define IS_GM45(dev)		(INTEL_DEVID(dev) == 0x2A42)
-#define IS_G4X(dev)		(INTEL_INFO(dev)->is_g4x)
-#define IS_PINEVIEW_G(dev)	(INTEL_DEVID(dev) == 0xa001)
-#define IS_PINEVIEW_M(dev)	(INTEL_DEVID(dev) == 0xa011)
-#define IS_PINEVIEW(dev)	(INTEL_INFO(dev)->is_pineview)
-#define IS_G33(dev)		(INTEL_INFO(dev)->is_g33)
-#define IS_IRONLAKE_M(dev)	(INTEL_DEVID(dev) == 0x0046)
-#define IS_IVYBRIDGE(dev)	(INTEL_INFO(dev)->is_ivybridge)
-#define IS_IVB_GT1(dev)		(INTEL_DEVID(dev) == 0x0156 || \
-				 INTEL_DEVID(dev) == 0x0152 || \
-				 INTEL_DEVID(dev) == 0x015a)
-#define IS_VALLEYVIEW(dev)	(INTEL_INFO(dev)->is_valleyview)
-#define IS_CHERRYVIEW(dev)	(INTEL_INFO(dev)->is_valleyview && IS_GEN8(dev))
-#define IS_HASWELL(dev)	(INTEL_INFO(dev)->is_haswell)
-#define IS_BROADWELL(dev)	(!INTEL_INFO(dev)->is_valleyview && IS_GEN8(dev))
-#define IS_SKYLAKE(dev)	(INTEL_INFO(dev)->is_skylake)
-#define IS_BROXTON(dev)	(!INTEL_INFO(dev)->is_skylake && IS_GEN9(dev))
-#define IS_MOBILE(dev)		(INTEL_INFO(dev)->is_mobile)
-#define IS_HSW_EARLY_SDV(dev)	(IS_HASWELL(dev) && \
-				 (INTEL_DEVID(dev) & 0xFF00) == 0x0C00)
-#define IS_BDW_ULT(dev)		(IS_BROADWELL(dev) && \
-				 ((INTEL_DEVID(dev) & 0xf) == 0x6 ||	\
-				 (INTEL_DEVID(dev) & 0xf) == 0xb ||	\
-				 (INTEL_DEVID(dev) & 0xf) == 0xe))
-/* ULX machines are also considered ULT. */
-#define IS_BDW_ULX(dev)		(IS_BROADWELL(dev) && \
-				 (INTEL_DEVID(dev) & 0xf) == 0xe)
-#define IS_BDW_GT3(dev)		(IS_BROADWELL(dev) && \
-				 (INTEL_DEVID(dev) & 0x00F0) == 0x0020)
-#define IS_HSW_ULT(dev)		(IS_HASWELL(dev) && \
-				 (INTEL_DEVID(dev) & 0xFF00) == 0x0A00)
-#define IS_HSW_GT3(dev)		(IS_HASWELL(dev) && \
-				 (INTEL_DEVID(dev) & 0x00F0) == 0x0020)
-/* ULX machines are also considered ULT. */
-#define IS_HSW_ULX(dev)		(INTEL_DEVID(dev) == 0x0A0E || \
-				 INTEL_DEVID(dev) == 0x0A1E)
-#define IS_SKL_ULT(dev)		(INTEL_DEVID(dev) == 0x1906 || \
-				 INTEL_DEVID(dev) == 0x1913 || \
-				 INTEL_DEVID(dev) == 0x1916 || \
-				 INTEL_DEVID(dev) == 0x1921 || \
-				 INTEL_DEVID(dev) == 0x1926)
-#define IS_SKL_ULX(dev)		(INTEL_DEVID(dev) == 0x190E || \
-				 INTEL_DEVID(dev) == 0x1915 || \
-				 INTEL_DEVID(dev) == 0x191E)
-#define IS_PRELIMINARY_HW(intel_info) ((intel_info)->is_preliminary)
-
-#define SKL_REVID_A0		(0x0)
-#define SKL_REVID_B0		(0x1)
-#define SKL_REVID_C0		(0x2)
-#define SKL_REVID_D0		(0x3)
-#define SKL_REVID_E0		(0x4)
-#define SKL_REVID_F0		(0x5)
-
-#define BXT_REVID_A0		(0x0)
-#define BXT_REVID_B0		(0x3)
-#define BXT_REVID_C0		(0x6)
-
-/*
- * The genX designation typically refers to the render engine, so render
- * capability related checks should use IS_GEN, while display and other checks
- * have their own (e.g. HAS_PCH_SPLIT for ILK+ display, IS_foo for particular
- * chips, etc.).
- */
-#define IS_GEN2(dev)	(INTEL_INFO(dev)->gen == 2)
-#define IS_GEN3(dev)	(INTEL_INFO(dev)->gen == 3)
-#define IS_GEN4(dev)	(INTEL_INFO(dev)->gen == 4)
-#define IS_GEN5(dev)	(INTEL_INFO(dev)->gen == 5)
-#define IS_GEN6(dev)	(INTEL_INFO(dev)->gen == 6)
-#define IS_GEN7(dev)	(INTEL_INFO(dev)->gen == 7)
-#define IS_GEN8(dev)	(INTEL_INFO(dev)->gen == 8)
-#define IS_GEN9(dev)	(INTEL_INFO(dev)->gen == 9)
-
-#define RENDER_RING		(1<<RCS)
-#define BSD_RING		(1<<VCS)
-#define BLT_RING		(1<<BCS)
-#define VEBOX_RING		(1<<VECS)
-#define BSD2_RING		(1<<VCS2)
-#define HAS_BSD(dev)		(INTEL_INFO(dev)->ring_mask & BSD_RING)
-#define HAS_BSD2(dev)		(INTEL_INFO(dev)->ring_mask & BSD2_RING)
-#define HAS_BLT(dev)		(INTEL_INFO(dev)->ring_mask & BLT_RING)
-#define HAS_VEBOX(dev)		(INTEL_INFO(dev)->ring_mask & VEBOX_RING)
-#define HAS_LLC(dev)		(INTEL_INFO(dev)->has_llc)
-#define HAS_WT(dev)		((IS_HASWELL(dev) || IS_BROADWELL(dev)) && \
-				 __I915__(dev)->ellc_size)
-#define I915_NEED_GFX_HWS(dev)	(INTEL_INFO(dev)->need_gfx_hws)
-
-#define HAS_HW_CONTEXTS(dev)	(INTEL_INFO(dev)->gen >= 6)
-#define HAS_LOGICAL_RING_CONTEXTS(dev)	(INTEL_INFO(dev)->gen >= 8)
-#define USES_PPGTT(dev)		(i915.enable_ppgtt)
-#define USES_FULL_PPGTT(dev)	(i915.enable_ppgtt >= 2)
-#define USES_FULL_48BIT_PPGTT(dev)	(i915.enable_ppgtt == 3)
-
-#define HAS_OVERLAY(dev)		(INTEL_INFO(dev)->has_overlay)
-#define OVERLAY_NEEDS_PHYSICAL(dev)	(INTEL_INFO(dev)->overlay_needs_physical)
-
-/* Early gen2 have a totally busted CS tlb and require pinned batches. */
-#define HAS_BROKEN_CS_TLB(dev)		(IS_I830(dev) || IS_845G(dev))
-/*
- * dp aux and gmbus irq on gen4 seems to be able to generate legacy interrupts
- * even when in MSI mode. This results in spurious interrupt warnings if the
- * legacy irq no. is shared with another device. The kernel then disables that
- * interrupt source and so prevents the other device from working properly.
- */
-#define HAS_AUX_IRQ(dev) (INTEL_INFO(dev)->gen >= 5)
-#define HAS_GMBUS_IRQ(dev) (INTEL_INFO(dev)->gen >= 5)
-
-/* With the 945 and later, Y tiling got adjusted so that it was 32 128-byte
- * rows, which changed the alignment requirements and fence programming.
- */
-#define HAS_128_BYTE_Y_TILING(dev) (!IS_GEN2(dev) && !(IS_I915G(dev) || \
-						      IS_I915GM(dev)))
-#define SUPPORTS_TV(dev)		(INTEL_INFO(dev)->supports_tv)
-#define I915_HAS_HOTPLUG(dev)		 (INTEL_INFO(dev)->has_hotplug)
-
-#define HAS_FW_BLC(dev) (INTEL_INFO(dev)->gen > 2)
-#define HAS_PIPE_CXSR(dev) (INTEL_INFO(dev)->has_pipe_cxsr)
-#define HAS_FBC(dev) (INTEL_INFO(dev)->has_fbc)
-
-#define HAS_IPS(dev)		(IS_HSW_ULT(dev) || IS_BROADWELL(dev))
-
-#define HAS_DP_MST(dev)		(IS_HASWELL(dev) || IS_BROADWELL(dev) || \
-				 INTEL_INFO(dev)->gen >= 9)
-
-#define HAS_DDI(dev)		(INTEL_INFO(dev)->has_ddi)
-#define HAS_FPGA_DBG_UNCLAIMED(dev)	(INTEL_INFO(dev)->has_fpga_dbg)
-#define HAS_PSR(dev)		(IS_HASWELL(dev) || IS_BROADWELL(dev) || \
-				 IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev) || \
-				 IS_SKYLAKE(dev))
-#define HAS_RUNTIME_PM(dev)	(IS_GEN6(dev) || IS_HASWELL(dev) || \
-				 IS_BROADWELL(dev) || IS_VALLEYVIEW(dev) || \
-				 IS_SKYLAKE(dev))
-#define HAS_RC6(dev)		(INTEL_INFO(dev)->gen >= 6)
-#define HAS_RC6p(dev)		(INTEL_INFO(dev)->gen == 6 || IS_IVYBRIDGE(dev))
-
-#define HAS_CSR(dev)	(IS_SKYLAKE(dev))
-
-#define HAS_GUC_UCODE(dev)	(IS_GEN9(dev))
-#define HAS_GUC_SCHED(dev)	(IS_GEN9(dev))
-
-#define HAS_RESOURCE_STREAMER(dev) (IS_HASWELL(dev) || \
-				    INTEL_INFO(dev)->gen >= 8)
-
-#define HAS_CORE_RING_FREQ(dev)	(INTEL_INFO(dev)->gen >= 6 && \
-				 !IS_VALLEYVIEW(dev) && !IS_BROXTON(dev))
-
-#define INTEL_PCH_DEVICE_ID_MASK		0xff00
-#define INTEL_PCH_IBX_DEVICE_ID_TYPE		0x3b00
-#define INTEL_PCH_CPT_DEVICE_ID_TYPE		0x1c00
-#define INTEL_PCH_PPT_DEVICE_ID_TYPE		0x1e00
-#define INTEL_PCH_LPT_DEVICE_ID_TYPE		0x8c00
-#define INTEL_PCH_LPT_LP_DEVICE_ID_TYPE		0x9c00
-#define INTEL_PCH_SPT_DEVICE_ID_TYPE		0xA100
-#define INTEL_PCH_SPT_LP_DEVICE_ID_TYPE		0x9D00
-
-#define INTEL_PCH_TYPE(dev) (__I915__(dev)->pch_type)
-#define HAS_PCH_SPT(dev) (INTEL_PCH_TYPE(dev) == PCH_SPT)
-#define HAS_PCH_LPT(dev) (INTEL_PCH_TYPE(dev) == PCH_LPT)
-#define HAS_PCH_LPT_LP(dev) (__I915__(dev)->pch_id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE)
-#define HAS_PCH_CPT(dev) (INTEL_PCH_TYPE(dev) == PCH_CPT)
-#define HAS_PCH_IBX(dev) (INTEL_PCH_TYPE(dev) == PCH_IBX)
-#define HAS_PCH_NOP(dev) (INTEL_PCH_TYPE(dev) == PCH_NOP)
-#define HAS_PCH_SPLIT(dev) (INTEL_PCH_TYPE(dev) != PCH_NONE)
-
-#define HAS_GMCH_DISPLAY(dev) (INTEL_INFO(dev)->gen < 5 || IS_VALLEYVIEW(dev))
-
-/* DPF == dynamic parity feature */
-#define HAS_L3_DPF(dev) (IS_IVYBRIDGE(dev) || IS_HASWELL(dev))
-#define NUM_L3_SLICES(dev) (IS_HSW_GT3(dev) ? 2 : HAS_L3_DPF(dev))
-
-#define GT_FREQUENCY_MULTIPLIER 50
-#define GEN9_FREQ_SCALER 3
-
 #include "i915_trace.h"
 
 extern const struct drm_ioctl_desc i915_ioctls[];
diff --git a/drivers/gpu/drm/i915/intel_dev_info.c b/drivers/gpu/drm/i915/intel_dev_info.c
new file mode 100644
index 0000000..562f542
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_dev_info.c
@@ -0,0 +1,422 @@
+/*
+ * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
+ * All Rights Reserved.
+ * Copyright © 2015 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+ * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+#define GEN_DEFAULT_PIPEOFFSETS \
+	.pipe_offsets = { PIPE_A_OFFSET, PIPE_B_OFFSET, \
+			  PIPE_C_OFFSET, PIPE_EDP_OFFSET }, \
+	.trans_offsets = { TRANSCODER_A_OFFSET, TRANSCODER_B_OFFSET, \
+			   TRANSCODER_C_OFFSET, TRANSCODER_EDP_OFFSET }, \
+	.palette_offsets = { PALETTE_A_OFFSET, PALETTE_B_OFFSET }
+
+#define GEN_CHV_PIPEOFFSETS \
+	.pipe_offsets = { PIPE_A_OFFSET, PIPE_B_OFFSET, \
+			  CHV_PIPE_C_OFFSET }, \
+	.trans_offsets = { TRANSCODER_A_OFFSET, TRANSCODER_B_OFFSET, \
+			   CHV_TRANSCODER_C_OFFSET, }, \
+	.palette_offsets = { PALETTE_A_OFFSET, PALETTE_B_OFFSET, \
+			     CHV_PALETTE_C_OFFSET }
+
+#define CURSOR_OFFSETS \
+	.cursor_offsets = { CURSOR_A_OFFSET, CURSOR_B_OFFSET, CHV_CURSOR_C_OFFSET }
+
+#define IVB_CURSOR_OFFSETS \
+	.cursor_offsets = { CURSOR_A_OFFSET, IVB_CURSOR_B_OFFSET, IVB_CURSOR_C_OFFSET }
+
+static const struct intel_device_info intel_i830_info = {
+	.gen = 2, .is_mobile = 1, .cursor_needs_physical = 1, .num_pipes = 2,
+	.has_overlay = 1, .overlay_needs_physical = 1,
+	.ring_mask = RENDER_RING,
+	GEN_DEFAULT_PIPEOFFSETS,
+	CURSOR_OFFSETS,
+};
+
+static const struct intel_device_info intel_845g_info = {
+	.gen = 2, .num_pipes = 1,
+	.has_overlay = 1, .overlay_needs_physical = 1,
+	.ring_mask = RENDER_RING,
+	GEN_DEFAULT_PIPEOFFSETS,
+	CURSOR_OFFSETS,
+};
+
+static const struct intel_device_info intel_i85x_info = {
+	.gen = 2, .is_i85x = 1, .is_mobile = 1, .num_pipes = 2,
+	.cursor_needs_physical = 1,
+	.has_overlay = 1, .overlay_needs_physical = 1,
+	.has_fbc = 1,
+	.ring_mask = RENDER_RING,
+	GEN_DEFAULT_PIPEOFFSETS,
+	CURSOR_OFFSETS,
+};
+
+static const struct intel_device_info intel_i865g_info = {
+	.gen = 2, .num_pipes = 1,
+	.has_overlay = 1, .overlay_needs_physical = 1,
+	.ring_mask = RENDER_RING,
+	GEN_DEFAULT_PIPEOFFSETS,
+	CURSOR_OFFSETS,
+};
+
+static const struct intel_device_info intel_i915g_info = {
+	.gen = 3, .is_i915g = 1, .cursor_needs_physical = 1, .num_pipes = 2,
+	.has_overlay = 1, .overlay_needs_physical = 1,
+	.ring_mask = RENDER_RING,
+	GEN_DEFAULT_PIPEOFFSETS,
+	CURSOR_OFFSETS,
+};
+static const struct intel_device_info intel_i915gm_info = {
+	.gen = 3, .is_mobile = 1, .num_pipes = 2,
+	.cursor_needs_physical = 1,
+	.has_overlay = 1, .overlay_needs_physical = 1,
+	.supports_tv = 1,
+	.has_fbc = 1,
+	.ring_mask = RENDER_RING,
+	GEN_DEFAULT_PIPEOFFSETS,
+	CURSOR_OFFSETS,
+};
+static const struct intel_device_info intel_i945g_info = {
+	.gen = 3, .has_hotplug = 1, .cursor_needs_physical = 1, .num_pipes = 2,
+	.has_overlay = 1, .overlay_needs_physical = 1,
+	.ring_mask = RENDER_RING,
+	GEN_DEFAULT_PIPEOFFSETS,
+	CURSOR_OFFSETS,
+};
+static const struct intel_device_info intel_i945gm_info = {
+	.gen = 3, .is_i945gm = 1, .is_mobile = 1, .num_pipes = 2,
+	.has_hotplug = 1, .cursor_needs_physical = 1,
+	.has_overlay = 1, .overlay_needs_physical = 1,
+	.supports_tv = 1,
+	.has_fbc = 1,
+	.ring_mask = RENDER_RING,
+	GEN_DEFAULT_PIPEOFFSETS,
+	CURSOR_OFFSETS,
+};
+
+static const struct intel_device_info intel_i965g_info = {
+	.gen = 4, .is_broadwater = 1, .num_pipes = 2,
+	.has_hotplug = 1,
+	.has_overlay = 1,
+	.ring_mask = RENDER_RING,
+	GEN_DEFAULT_PIPEOFFSETS,
+	CURSOR_OFFSETS,
+};
+
+static const struct intel_device_info intel_i965gm_info = {
+	.gen = 4, .is_crestline = 1, .num_pipes = 2,
+	.is_mobile = 1, .has_fbc = 1, .has_hotplug = 1,
+	.has_overlay = 1,
+	.supports_tv = 1,
+	.ring_mask = RENDER_RING,
+	GEN_DEFAULT_PIPEOFFSETS,
+	CURSOR_OFFSETS,
+};
+
+static const struct intel_device_info intel_g33_info = {
+	.gen = 3, .is_g33 = 1, .num_pipes = 2,
+	.need_gfx_hws = 1, .has_hotplug = 1,
+	.has_overlay = 1,
+	.ring_mask = RENDER_RING,
+	GEN_DEFAULT_PIPEOFFSETS,
+	CURSOR_OFFSETS,
+};
+
+static const struct intel_device_info intel_g45_info = {
+	.gen = 4, .is_g4x = 1, .need_gfx_hws = 1, .num_pipes = 2,
+	.has_pipe_cxsr = 1, .has_hotplug = 1,
+	.ring_mask = RENDER_RING | BSD_RING,
+	GEN_DEFAULT_PIPEOFFSETS,
+	CURSOR_OFFSETS,
+};
+
+static const struct intel_device_info intel_gm45_info = {
+	.gen = 4, .is_g4x = 1, .num_pipes = 2,
+	.is_mobile = 1, .need_gfx_hws = 1, .has_fbc = 1,
+	.has_pipe_cxsr = 1, .has_hotplug = 1,
+	.supports_tv = 1,
+	.ring_mask = RENDER_RING | BSD_RING,
+	GEN_DEFAULT_PIPEOFFSETS,
+	CURSOR_OFFSETS,
+};
+
+static const struct intel_device_info intel_pineview_info = {
+	.gen = 3, .is_g33 = 1, .is_pineview = 1, .is_mobile = 1, .num_pipes = 2,
+	.need_gfx_hws = 1, .has_hotplug = 1,
+	.has_overlay = 1,
+	GEN_DEFAULT_PIPEOFFSETS,
+	CURSOR_OFFSETS,
+};
+
+static const struct intel_device_info intel_ironlake_d_info = {
+	.gen = 5, .num_pipes = 2,
+	.need_gfx_hws = 1, .has_hotplug = 1,
+	.ring_mask = RENDER_RING | BSD_RING,
+	GEN_DEFAULT_PIPEOFFSETS,
+	CURSOR_OFFSETS,
+};
+
+static const struct intel_device_info intel_ironlake_m_info = {
+	.gen = 5, .is_mobile = 1, .num_pipes = 2,
+	.need_gfx_hws = 1, .has_hotplug = 1,
+	.has_fbc = 1,
+	.ring_mask = RENDER_RING | BSD_RING,
+	GEN_DEFAULT_PIPEOFFSETS,
+	CURSOR_OFFSETS,
+};
+
+static const struct intel_device_info intel_sandybridge_d_info = {
+	.gen = 6, .num_pipes = 2,
+	.need_gfx_hws = 1, .has_hotplug = 1,
+	.has_fbc = 1,
+	.ring_mask = RENDER_RING | BSD_RING | BLT_RING,
+	.has_llc = 1,
+	GEN_DEFAULT_PIPEOFFSETS,
+	CURSOR_OFFSETS,
+};
+
+static const struct intel_device_info intel_sandybridge_m_info = {
+	.gen = 6, .is_mobile = 1, .num_pipes = 2,
+	.need_gfx_hws = 1, .has_hotplug = 1,
+	.has_fbc = 1,
+	.ring_mask = RENDER_RING | BSD_RING | BLT_RING,
+	.has_llc = 1,
+	GEN_DEFAULT_PIPEOFFSETS,
+	CURSOR_OFFSETS,
+};
+
+#define GEN7_FEATURES  \
+	.gen = 7, .num_pipes = 3, \
+	.need_gfx_hws = 1, .has_hotplug = 1, \
+	.has_fbc = 1, \
+	.ring_mask = RENDER_RING | BSD_RING | BLT_RING, \
+	.has_llc = 1
+
+static const struct intel_device_info intel_ivybridge_d_info = {
+	GEN7_FEATURES,
+	.is_ivybridge = 1,
+	GEN_DEFAULT_PIPEOFFSETS,
+	IVB_CURSOR_OFFSETS,
+};
+
+static const struct intel_device_info intel_ivybridge_m_info = {
+	GEN7_FEATURES,
+	.is_ivybridge = 1,
+	.is_mobile = 1,
+	GEN_DEFAULT_PIPEOFFSETS,
+	IVB_CURSOR_OFFSETS,
+};
+
+static const struct intel_device_info intel_ivybridge_q_info = {
+	GEN7_FEATURES,
+	.is_ivybridge = 1,
+	.num_pipes = 0, /* legal, last one wins */
+	GEN_DEFAULT_PIPEOFFSETS,
+	IVB_CURSOR_OFFSETS,
+};
+
+static const struct intel_device_info intel_valleyview_m_info = {
+	GEN7_FEATURES,
+	.is_mobile = 1,
+	.num_pipes = 2,
+	.is_valleyview = 1,
+	.display_mmio_offset = VLV_DISPLAY_BASE,
+	.has_fbc = 0, /* legal, last one wins */
+	.has_llc = 0, /* legal, last one wins */
+	GEN_DEFAULT_PIPEOFFSETS,
+	CURSOR_OFFSETS,
+};
+
+static const struct intel_device_info intel_valleyview_d_info = {
+	GEN7_FEATURES,
+	.num_pipes = 2,
+	.is_valleyview = 1,
+	.display_mmio_offset = VLV_DISPLAY_BASE,
+	.has_fbc = 0, /* legal, last one wins */
+	.has_llc = 0, /* legal, last one wins */
+	GEN_DEFAULT_PIPEOFFSETS,
+	CURSOR_OFFSETS,
+};
+
+static const struct intel_device_info intel_haswell_d_info = {
+	GEN7_FEATURES,
+	.is_haswell = 1,
+	.has_ddi = 1,
+	.has_fpga_dbg = 1,
+	.ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
+	GEN_DEFAULT_PIPEOFFSETS,
+	IVB_CURSOR_OFFSETS,
+};
+
+static const struct intel_device_info intel_haswell_m_info = {
+	GEN7_FEATURES,
+	.is_haswell = 1,
+	.is_mobile = 1,
+	.has_ddi = 1,
+	.has_fpga_dbg = 1,
+	.ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
+	GEN_DEFAULT_PIPEOFFSETS,
+	IVB_CURSOR_OFFSETS,
+};
+
+static const struct intel_device_info intel_broadwell_d_info = {
+	.gen = 8, .num_pipes = 3,
+	.need_gfx_hws = 1, .has_hotplug = 1,
+	.ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
+	.has_llc = 1,
+	.has_ddi = 1,
+	.has_fpga_dbg = 1,
+	.has_fbc = 1,
+	GEN_DEFAULT_PIPEOFFSETS,
+	IVB_CURSOR_OFFSETS,
+};
+
+static const struct intel_device_info intel_broadwell_m_info = {
+	.gen = 8, .is_mobile = 1, .num_pipes = 3,
+	.need_gfx_hws = 1, .has_hotplug = 1,
+	.ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
+	.has_llc = 1,
+	.has_ddi = 1,
+	.has_fpga_dbg = 1,
+	.has_fbc = 1,
+	GEN_DEFAULT_PIPEOFFSETS,
+	IVB_CURSOR_OFFSETS,
+};
+
+static const struct intel_device_info intel_broadwell_gt3d_info = {
+	.gen = 8, .num_pipes = 3,
+	.need_gfx_hws = 1, .has_hotplug = 1,
+	.ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING | BSD2_RING,
+	.has_llc = 1,
+	.has_ddi = 1,
+	.has_fpga_dbg = 1,
+	.has_fbc = 1,
+	GEN_DEFAULT_PIPEOFFSETS,
+	IVB_CURSOR_OFFSETS,
+};
+
+static const struct intel_device_info intel_broadwell_gt3m_info = {
+	.gen = 8, .is_mobile = 1, .num_pipes = 3,
+	.need_gfx_hws = 1, .has_hotplug = 1,
+	.ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING | BSD2_RING,
+	.has_llc = 1,
+	.has_ddi = 1,
+	.has_fpga_dbg = 1,
+	.has_fbc = 1,
+	GEN_DEFAULT_PIPEOFFSETS,
+	IVB_CURSOR_OFFSETS,
+};
+
+static const struct intel_device_info intel_cherryview_info = {
+	.gen = 8, .num_pipes = 3,
+	.need_gfx_hws = 1, .has_hotplug = 1,
+	.ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
+	.is_valleyview = 1,
+	.display_mmio_offset = VLV_DISPLAY_BASE,
+	GEN_CHV_PIPEOFFSETS,
+	CURSOR_OFFSETS,
+};
+
+static const struct intel_device_info intel_skylake_info = {
+	.is_skylake = 1,
+	.gen = 9, .num_pipes = 3,
+	.need_gfx_hws = 1, .has_hotplug = 1,
+	.ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
+	.has_llc = 1,
+	.has_ddi = 1,
+	.has_fpga_dbg = 1,
+	.has_fbc = 1,
+	GEN_DEFAULT_PIPEOFFSETS,
+	IVB_CURSOR_OFFSETS,
+};
+
+static const struct intel_device_info intel_skylake_gt3_info = {
+	.is_skylake = 1,
+	.gen = 9, .num_pipes = 3,
+	.need_gfx_hws = 1, .has_hotplug = 1,
+	.ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING | BSD2_RING,
+	.has_llc = 1,
+	.has_ddi = 1,
+	.has_fpga_dbg = 1,
+	.has_fbc = 1,
+	GEN_DEFAULT_PIPEOFFSETS,
+	IVB_CURSOR_OFFSETS,
+};
+
+static const struct intel_device_info intel_broxton_info = {
+	.is_preliminary = 1,
+	.gen = 9,
+	.need_gfx_hws = 1, .has_hotplug = 1,
+	.ring_mask = RENDER_RING | BSD_RING | BLT_RING | VEBOX_RING,
+	.num_pipes = 3,
+	.has_ddi = 1,
+	.has_fpga_dbg = 1,
+	.has_fbc = 1,
+	GEN_DEFAULT_PIPEOFFSETS,
+	IVB_CURSOR_OFFSETS,
+};
+
+/*
+ * Make sure any device matches here are from most specific to most
+ * general.  For example, since the Quanta match is based on the subsystem
+ * and subvendor IDs, we need it to come before the more general IVB
+ * PCI ID matches, otherwise we'll use the wrong info struct above.
+ */
+#define INTEL_PCI_IDS \
+	INTEL_I830_IDS(&intel_i830_info),	\
+	INTEL_I845G_IDS(&intel_845g_info),	\
+	INTEL_I85X_IDS(&intel_i85x_info),	\
+	INTEL_I865G_IDS(&intel_i865g_info),	\
+	INTEL_I915G_IDS(&intel_i915g_info),	\
+	INTEL_I915GM_IDS(&intel_i915gm_info),	\
+	INTEL_I945G_IDS(&intel_i945g_info),	\
+	INTEL_I945GM_IDS(&intel_i945gm_info),	\
+	INTEL_I965G_IDS(&intel_i965g_info),	\
+	INTEL_G33_IDS(&intel_g33_info),		\
+	INTEL_I965GM_IDS(&intel_i965gm_info),	\
+	INTEL_GM45_IDS(&intel_gm45_info), 	\
+	INTEL_G45_IDS(&intel_g45_info), 	\
+	INTEL_PINEVIEW_IDS(&intel_pineview_info),	\
+	INTEL_IRONLAKE_D_IDS(&intel_ironlake_d_info),	\
+	INTEL_IRONLAKE_M_IDS(&intel_ironlake_m_info),	\
+	INTEL_SNB_D_IDS(&intel_sandybridge_d_info),	\
+	INTEL_SNB_M_IDS(&intel_sandybridge_m_info),	\
+	INTEL_IVB_Q_IDS(&intel_ivybridge_q_info), /* must be first IVB */ \
+	INTEL_IVB_M_IDS(&intel_ivybridge_m_info),	\
+	INTEL_IVB_D_IDS(&intel_ivybridge_d_info),	\
+	INTEL_HSW_D_IDS(&intel_haswell_d_info), \
+	INTEL_HSW_M_IDS(&intel_haswell_m_info), \
+	INTEL_VLV_M_IDS(&intel_valleyview_m_info),	\
+	INTEL_VLV_D_IDS(&intel_valleyview_d_info),	\
+	INTEL_BDW_GT12M_IDS(&intel_broadwell_m_info),	\
+	INTEL_BDW_GT12D_IDS(&intel_broadwell_d_info),	\
+	INTEL_BDW_GT3M_IDS(&intel_broadwell_gt3m_info),	\
+	INTEL_BDW_GT3D_IDS(&intel_broadwell_gt3d_info), \
+	INTEL_CHV_IDS(&intel_cherryview_info),	\
+	INTEL_SKL_GT1_IDS(&intel_skylake_info),	\
+	INTEL_SKL_GT2_IDS(&intel_skylake_info),	\
+	INTEL_SKL_GT3_IDS(&intel_skylake_gt3_info),	\
+	INTEL_BXT_IDS(&intel_broxton_info)
+
+
diff --git a/drivers/gpu/drm/i915/intel_dev_info.h b/drivers/gpu/drm/i915/intel_dev_info.h
new file mode 100644
index 0000000..2840b2c
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_dev_info.h
@@ -0,0 +1,331 @@
+/*
+ * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas.
+ * All Rights Reserved.
+ * Copyright © 2015 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+ * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+
+#ifndef _INTEL_DEV_INFO_H_
+#define _INTEL_DEV_INFO_H_
+
+enum pipe {
+	INVALID_PIPE = -1,
+	PIPE_A = 0,
+	PIPE_B,
+	PIPE_C,
+	_PIPE_EDP,
+	I915_MAX_PIPES = _PIPE_EDP
+};
+#define pipe_name(p) ((p) + 'A')
+
+enum transcoder {
+	TRANSCODER_A = 0,
+	TRANSCODER_B,
+	TRANSCODER_C,
+	TRANSCODER_EDP,
+	I915_MAX_TRANSCODERS
+};
+#define transcoder_name(t) ((t) + 'A')
+
+
+enum intel_ring_id {
+	RCS = 0x0,
+	VCS,
+	BCS,
+	VECS,
+	VCS2
+};
+#define I915_NUM_RINGS 5
+#define LAST_USER_RING (VECS + 1)
+
+
+/* Note that the (struct drm_i915_private *) cast is just to shut up gcc. */
+#define __I915__(p) ({ \
+	struct drm_i915_private *__p; \
+	if (__builtin_types_compatible_p(typeof(*p), struct drm_i915_private)) \
+		__p = (struct drm_i915_private *)p; \
+	else if (__builtin_types_compatible_p(typeof(*p), struct drm_device)) \
+		__p = to_i915((struct drm_device *)p); \
+	else \
+		BUILD_BUG(); \
+	__p; \
+})
+#define INTEL_INFO(p) 	(&__I915__(p)->info)
+#define INTEL_DEVID(p)	(INTEL_INFO(p)->device_id)
+#define INTEL_REVID(p)	(__I915__(p)->dev->pdev->revision)
+
+#define IS_I830(dev)		(INTEL_DEVID(dev) == 0x3577)
+#define IS_845G(dev)		(INTEL_DEVID(dev) == 0x2562)
+#define IS_I85X(dev)		(INTEL_INFO(dev)->is_i85x)
+#define IS_I865G(dev)		(INTEL_DEVID(dev) == 0x2572)
+#define IS_I915G(dev)		(INTEL_INFO(dev)->is_i915g)
+#define IS_I915GM(dev)		(INTEL_DEVID(dev) == 0x2592)
+#define IS_I945G(dev)		(INTEL_DEVID(dev) == 0x2772)
+#define IS_I945GM(dev)		(INTEL_INFO(dev)->is_i945gm)
+#define IS_BROADWATER(dev)	(INTEL_INFO(dev)->is_broadwater)
+#define IS_CRESTLINE(dev)	(INTEL_INFO(dev)->is_crestline)
+#define IS_GM45(dev)		(INTEL_DEVID(dev) == 0x2A42)
+#define IS_G4X(dev)		(INTEL_INFO(dev)->is_g4x)
+#define IS_PINEVIEW_G(dev)	(INTEL_DEVID(dev) == 0xa001)
+#define IS_PINEVIEW_M(dev)	(INTEL_DEVID(dev) == 0xa011)
+#define IS_PINEVIEW(dev)	(INTEL_INFO(dev)->is_pineview)
+#define IS_G33(dev)		(INTEL_INFO(dev)->is_g33)
+#define IS_IRONLAKE_M(dev)	(INTEL_DEVID(dev) == 0x0046)
+#define IS_IVYBRIDGE(dev)	(INTEL_INFO(dev)->is_ivybridge)
+#define IS_IVB_GT1(dev)		(INTEL_DEVID(dev) == 0x0156 || \
+				 INTEL_DEVID(dev) == 0x0152 || \
+				 INTEL_DEVID(dev) == 0x015a)
+#define IS_VALLEYVIEW(dev)	(INTEL_INFO(dev)->is_valleyview)
+#define IS_CHERRYVIEW(dev)	(INTEL_INFO(dev)->is_valleyview && IS_GEN8(dev))
+#define IS_HASWELL(dev)	(INTEL_INFO(dev)->is_haswell)
+#define IS_BROADWELL(dev)	(!INTEL_INFO(dev)->is_valleyview && IS_GEN8(dev))
+#define IS_SKYLAKE(dev)	(INTEL_INFO(dev)->is_skylake)
+#define IS_BROXTON(dev)	(!INTEL_INFO(dev)->is_skylake && IS_GEN9(dev))
+#define IS_MOBILE(dev)		(INTEL_INFO(dev)->is_mobile)
+#define IS_HSW_EARLY_SDV(dev)	(IS_HASWELL(dev) && \
+				 (INTEL_DEVID(dev) & 0xFF00) == 0x0C00)
+#define IS_BDW_ULT(dev)		(IS_BROADWELL(dev) && \
+				 ((INTEL_DEVID(dev) & 0xf) == 0x6 ||	\
+				 (INTEL_DEVID(dev) & 0xf) == 0xb ||	\
+				 (INTEL_DEVID(dev) & 0xf) == 0xe))
+/* ULX machines are also considered ULT. */
+#define IS_BDW_ULX(dev)		(IS_BROADWELL(dev) && \
+				 (INTEL_DEVID(dev) & 0xf) == 0xe)
+#define IS_BDW_GT3(dev)		(IS_BROADWELL(dev) && \
+				 (INTEL_DEVID(dev) & 0x00F0) == 0x0020)
+#define IS_HSW_ULT(dev)		(IS_HASWELL(dev) && \
+				 (INTEL_DEVID(dev) & 0xFF00) == 0x0A00)
+#define IS_HSW_GT3(dev)		(IS_HASWELL(dev) && \
+				 (INTEL_DEVID(dev) & 0x00F0) == 0x0020)
+/* ULX machines are also considered ULT. */
+#define IS_HSW_ULX(dev)		(INTEL_DEVID(dev) == 0x0A0E || \
+				 INTEL_DEVID(dev) == 0x0A1E)
+#define IS_SKL_ULT(dev)		(INTEL_DEVID(dev) == 0x1906 || \
+				 INTEL_DEVID(dev) == 0x1913 || \
+				 INTEL_DEVID(dev) == 0x1916 || \
+				 INTEL_DEVID(dev) == 0x1921 || \
+				 INTEL_DEVID(dev) == 0x1926)
+#define IS_SKL_ULX(dev)		(INTEL_DEVID(dev) == 0x190E || \
+				 INTEL_DEVID(dev) == 0x1915 || \
+				 INTEL_DEVID(dev) == 0x191E)
+#define IS_PRELIMINARY_HW(intel_info) ((intel_info)->is_preliminary)
+
+#define SKL_REVID_A0		(0x0)
+#define SKL_REVID_B0		(0x1)
+#define SKL_REVID_C0		(0x2)
+#define SKL_REVID_D0		(0x3)
+#define SKL_REVID_E0		(0x4)
+#define SKL_REVID_F0		(0x5)
+
+#define BXT_REVID_A0		(0x0)
+#define BXT_REVID_B0		(0x3)
+#define BXT_REVID_C0		(0x6)
+
+/*
+ * The genX designation typically refers to the render engine, so render
+ * capability related checks should use IS_GEN, while display and other checks
+ * have their own (e.g. HAS_PCH_SPLIT for ILK+ display, IS_foo for particular
+ * chips, etc.).
+ */
+#define IS_GEN2(dev)	(INTEL_INFO(dev)->gen == 2)
+#define IS_GEN3(dev)	(INTEL_INFO(dev)->gen == 3)
+#define IS_GEN4(dev)	(INTEL_INFO(dev)->gen == 4)
+#define IS_GEN5(dev)	(INTEL_INFO(dev)->gen == 5)
+#define IS_GEN6(dev)	(INTEL_INFO(dev)->gen == 6)
+#define IS_GEN7(dev)	(INTEL_INFO(dev)->gen == 7)
+#define IS_GEN8(dev)	(INTEL_INFO(dev)->gen == 8)
+#define IS_GEN9(dev)	(INTEL_INFO(dev)->gen == 9)
+
+#define RENDER_RING		(1<<RCS)
+#define BSD_RING		(1<<VCS)
+#define BLT_RING		(1<<BCS)
+#define VEBOX_RING		(1<<VECS)
+#define BSD2_RING		(1<<VCS2)
+#define HAS_BSD(dev)		(INTEL_INFO(dev)->ring_mask & BSD_RING)
+#define HAS_BSD2(dev)		(INTEL_INFO(dev)->ring_mask & BSD2_RING)
+#define HAS_BLT(dev)		(INTEL_INFO(dev)->ring_mask & BLT_RING)
+#define HAS_VEBOX(dev)		(INTEL_INFO(dev)->ring_mask & VEBOX_RING)
+#define HAS_LLC(dev)		(INTEL_INFO(dev)->has_llc)
+#define HAS_WT(dev)		((IS_HASWELL(dev) || IS_BROADWELL(dev)) && \
+				 __I915__(dev)->ellc_size)
+#define I915_NEED_GFX_HWS(dev)	(INTEL_INFO(dev)->need_gfx_hws)
+
+#define HAS_HW_CONTEXTS(dev)	(INTEL_INFO(dev)->gen >= 6)
+#define HAS_LOGICAL_RING_CONTEXTS(dev)	(INTEL_INFO(dev)->gen >= 8)
+#define USES_PPGTT(dev)		(i915.enable_ppgtt)
+#define USES_FULL_PPGTT(dev)	(i915.enable_ppgtt >= 2)
+#define USES_FULL_48BIT_PPGTT(dev)	(i915.enable_ppgtt == 3)
+
+#define HAS_OVERLAY(dev)		(INTEL_INFO(dev)->has_overlay)
+#define OVERLAY_NEEDS_PHYSICAL(dev)	(INTEL_INFO(dev)->overlay_needs_physical)
+
+/* Early gen2 have a totally busted CS tlb and require pinned batches. */
+#define HAS_BROKEN_CS_TLB(dev)		(IS_I830(dev) || IS_845G(dev))
+/*
+ * dp aux and gmbus irq on gen4 seems to be able to generate legacy interrupts
+ * even when in MSI mode. This results in spurious interrupt warnings if the
+ * legacy irq no. is shared with another device. The kernel then disables that
+ * interrupt source and so prevents the other device from working properly.
+ */
+#define HAS_AUX_IRQ(dev) (INTEL_INFO(dev)->gen >= 5)
+#define HAS_GMBUS_IRQ(dev) (INTEL_INFO(dev)->gen >= 5)
+
+/* With the 945 and later, Y tiling got adjusted so that it was 32 128-byte
+ * rows, which changed the alignment requirements and fence programming.
+ */
+#define HAS_128_BYTE_Y_TILING(dev) (!IS_GEN2(dev) && !(IS_I915G(dev) || \
+						      IS_I915GM(dev)))
+#define SUPPORTS_TV(dev)		(INTEL_INFO(dev)->supports_tv)
+#define I915_HAS_HOTPLUG(dev)		 (INTEL_INFO(dev)->has_hotplug)
+
+#define HAS_FW_BLC(dev) (INTEL_INFO(dev)->gen > 2)
+#define HAS_PIPE_CXSR(dev) (INTEL_INFO(dev)->has_pipe_cxsr)
+#define HAS_FBC(dev) (INTEL_INFO(dev)->has_fbc)
+
+#define HAS_IPS(dev)		(IS_HSW_ULT(dev) || IS_BROADWELL(dev))
+
+#define HAS_DP_MST(dev)		(IS_HASWELL(dev) || IS_BROADWELL(dev) || \
+				 INTEL_INFO(dev)->gen >= 9)
+
+#define HAS_DDI(dev)		(INTEL_INFO(dev)->has_ddi)
+#define HAS_FPGA_DBG_UNCLAIMED(dev)	(INTEL_INFO(dev)->has_fpga_dbg)
+#define HAS_PSR(dev)		(IS_HASWELL(dev) || IS_BROADWELL(dev) || \
+				 IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev) || \
+				 IS_SKYLAKE(dev))
+#define HAS_RUNTIME_PM(dev)	(IS_GEN6(dev) || IS_HASWELL(dev) || \
+				 IS_BROADWELL(dev) || IS_VALLEYVIEW(dev) || \
+				 IS_SKYLAKE(dev))
+#define HAS_RC6(dev)		(INTEL_INFO(dev)->gen >= 6)
+#define HAS_RC6p(dev)		(INTEL_INFO(dev)->gen == 6 || IS_IVYBRIDGE(dev))
+
+#define HAS_CSR(dev)	(IS_SKYLAKE(dev))
+
+#define HAS_GUC_UCODE(dev)	(IS_GEN9(dev))
+#define HAS_GUC_SCHED(dev)	(IS_GEN9(dev))
+
+#define HAS_RESOURCE_STREAMER(dev) (IS_HASWELL(dev) || \
+				    INTEL_INFO(dev)->gen >= 8)
+
+#define HAS_CORE_RING_FREQ(dev)	(INTEL_INFO(dev)->gen >= 6 && \
+				 !IS_VALLEYVIEW(dev) && !IS_BROXTON(dev))
+
+#define INTEL_PCH_DEVICE_ID_MASK		0xff00
+#define INTEL_PCH_IBX_DEVICE_ID_TYPE		0x3b00
+#define INTEL_PCH_CPT_DEVICE_ID_TYPE		0x1c00
+#define INTEL_PCH_PPT_DEVICE_ID_TYPE		0x1e00
+#define INTEL_PCH_LPT_DEVICE_ID_TYPE		0x8c00
+#define INTEL_PCH_LPT_LP_DEVICE_ID_TYPE		0x9c00
+#define INTEL_PCH_SPT_DEVICE_ID_TYPE		0xA100
+#define INTEL_PCH_SPT_LP_DEVICE_ID_TYPE		0x9D00
+
+#define INTEL_PCH_TYPE(dev) (__I915__(dev)->pch_type)
+#define HAS_PCH_SPT(dev) (INTEL_PCH_TYPE(dev) == PCH_SPT)
+#define HAS_PCH_LPT(dev) (INTEL_PCH_TYPE(dev) == PCH_LPT)
+#define HAS_PCH_LPT_LP(dev) (__I915__(dev)->pch_id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE)
+#define HAS_PCH_CPT(dev) (INTEL_PCH_TYPE(dev) == PCH_CPT)
+#define HAS_PCH_IBX(dev) (INTEL_PCH_TYPE(dev) == PCH_IBX)
+#define HAS_PCH_NOP(dev) (INTEL_PCH_TYPE(dev) == PCH_NOP)
+#define HAS_PCH_SPLIT(dev) (INTEL_PCH_TYPE(dev) != PCH_NONE)
+
+#define HAS_GMCH_DISPLAY(dev) (INTEL_INFO(dev)->gen < 5 || IS_VALLEYVIEW(dev))
+
+/* DPF == dynamic parity feature */
+#define HAS_L3_DPF(dev) (IS_IVYBRIDGE(dev) || IS_HASWELL(dev))
+#define NUM_L3_SLICES(dev) (IS_HSW_GT3(dev) ? 2 : HAS_L3_DPF(dev))
+
+#define GT_FREQUENCY_MULTIPLIER 50
+#define GEN9_FREQ_SCALER 3
+
+
+#define DEV_INFO_FOR_EACH_FLAG(func, sep) \
+	func(is_mobile) sep \
+	func(is_i85x) sep \
+	func(is_i915g) sep \
+	func(is_i945gm) sep \
+	func(is_g33) sep \
+	func(need_gfx_hws) sep \
+	func(is_g4x) sep \
+	func(is_pineview) sep \
+	func(is_broadwater) sep \
+	func(is_crestline) sep \
+	func(is_ivybridge) sep \
+	func(is_valleyview) sep \
+	func(is_haswell) sep \
+	func(is_skylake) sep \
+	func(is_preliminary) sep \
+	func(has_fbc) sep \
+	func(has_pipe_cxsr) sep \
+	func(has_hotplug) sep \
+	func(cursor_needs_physical) sep \
+	func(has_overlay) sep \
+	func(overlay_needs_physical) sep \
+	func(supports_tv) sep \
+	func(has_llc) sep \
+	func(has_ddi) sep \
+	func(has_fpga_dbg)
+
+#define DEFINE_FLAG(name) u8 name:1
+#define SEP_SEMICOLON ;
+
+struct intel_device_info {
+	u32 display_mmio_offset;
+	u16 device_id;
+	u8 num_pipes:3;
+	u8 num_sprites[I915_MAX_PIPES];
+	u8 gen;
+	u8 ring_mask; /* Rings supported by the HW */
+	DEV_INFO_FOR_EACH_FLAG(DEFINE_FLAG, SEP_SEMICOLON);
+	/* Register offsets for the various display pipes and transcoders */
+	int pipe_offsets[I915_MAX_TRANSCODERS];
+	int trans_offsets[I915_MAX_TRANSCODERS];
+	int palette_offsets[I915_MAX_PIPES];
+	int cursor_offsets[I915_MAX_PIPES];
+
+	/* Slice/subslice/EU info */
+	u8 slice_total;
+	u8 subslice_total;
+	u8 subslice_per_slice;
+	u8 eu_total;
+	u8 eu_per_subslice;
+	/* For each slice, which subslice(s) has(have) 7 EUs (bitfield)? */
+	u8 subslice_7eu[3];
+	u8 has_slice_pg:1;
+	u8 has_subslice_pg:1;
+	u8 has_eu_pg:1;
+};
+
+#undef DEFINE_FLAG
+#undef SEP_SEMICOLON
+
+enum intel_pch {
+	PCH_NONE = 0,	/* No PCH present */
+	PCH_IBX,	/* Ibexpeak PCH */
+	PCH_CPT,	/* Cougarpoint PCH */
+	PCH_LPT,	/* Lynxpoint PCH */
+	PCH_SPT,        /* Sunrisepoint PCH */
+	PCH_NOP,
+};
+
+
+#endif /* _INTEL_DEV_INFO_H_ */
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.h b/drivers/gpu/drm/i915/intel_ringbuffer.h
index 49fa41d..074f61f 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.h
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.h
@@ -3,6 +3,7 @@
 
 #include <linux/hashtable.h>
 #include "i915_gem_batch_pool.h"
+#include "intel_dev_info.h"
 
 #define I915_CMD_HASH_ORDER 9
 
@@ -145,15 +146,7 @@ struct  i915_ctx_workarounds {
 
 struct  intel_engine_cs {
 	const char	*name;
-	enum intel_ring_id {
-		RCS = 0x0,
-		VCS,
-		BCS,
-		VECS,
-		VCS2
-	} id;
-#define I915_NUM_RINGS 5
-#define LAST_USER_RING (VECS + 1)
+	enum intel_ring_id id;
 	u32		mmio_base;
 	struct		drm_device *dev;
 	struct intel_ringbuffer *buffer;
-- 
2.4.3

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH i-g-t] Add a link training test
  2015-09-08 12:27 [PATCH 0/8] [RFC] Link training test Ander Conselvan de Oliveira
                   ` (7 preceding siblings ...)
  2015-09-08 12:27 ` [PATCH 8/8] drm/i915: Extract intel_dev_info.[ch] Ander Conselvan de Oliveira
@ 2015-09-08 12:28 ` Ander Conselvan de Oliveira
  2015-09-08 13:11   ` Ville Syrjälä
  2015-09-09 10:33   ` Thomas Wood
  2015-09-22 15:12 ` [PATCH 0/8] [RFC] Link " Daniel Vetter
  9 siblings, 2 replies; 31+ messages in thread
From: Ander Conselvan de Oliveira @ 2015-09-08 12:28 UTC (permalink / raw)
  To: intel-gfx; +Cc: jani.nikula, Ander Conselvan de Oliveira

This adds a test that compiles the link training code from i915 into a
separate executable and uses it to train a fake sink device. The test
also uses device information from i915 to exercise the different code
paths for different hardwdare generations.

In order to get the code to compile a lot of stubbing was necessary. It
was also easier to copy a few functions from the drm_dp_helpers instead
of getting the whole thing to compile as part of the test.
---
 link-training-test/Makefile             |  40 +++
 link-training-test/drm_dp_helper.c      | 115 +++++++++
 link-training-test/intel_drv.h          | 148 ++++++++++++
 link-training-test/link_training_test.c | 414 ++++++++++++++++++++++++++++++++
 4 files changed, 717 insertions(+)
 create mode 100644 link-training-test/Makefile
 create mode 100644 link-training-test/drm_dp_helper.c
 create mode 100644 link-training-test/intel_drv.h
 create mode 100644 link-training-test/link_training_test.c

diff --git a/link-training-test/Makefile b/link-training-test/Makefile
new file mode 100644
index 0000000..07a9914
--- /dev/null
+++ b/link-training-test/Makefile
@@ -0,0 +1,40 @@
+KERNEL_SRC_DIR=/home/aconselv/linux
+
+# Files copied from i915 source tree
+COPIED_SOURCES = \
+	intel_dp_link_training.c \
+	intel_dev_info.c \
+	intel_dev_info.h \
+	i915_reg.h
+
+INTEL_DP_LINK_TRAINING_C = $(KERNEL_SRC_DIR)/drivers/gpu/drm/i915/intel_dp_link_training.c
+
+INCLUDEDIR = \
+	-I$(KERNEL_SRC_DIR)/include/drm \
+	-I$(KERNEL_SRC_DIR)/ \
+	-I.
+
+DEFINES = \
+	-D__KERNEL__
+
+HEADER_FILES = \
+	intel_drv.h
+
+SOURCE_FILES = \
+	intel_dp_link_training.c \
+	link_training_test.c \
+	drm_dp_helper.c
+
+all: link_training_test
+
+#intel_dp_link_training.c: $(INTEL_DP_LINK_TRAINING_C)
+#	cp $(INTEL_DP_LINK_TRAINING_C) .
+
+$(COPIED_SOURCES): %: $(KERNEL_SRC_DIR)/drivers/gpu/drm/i915/%
+	cp $< $@
+
+link_training_test: $(COPIED_SOURCES) $(SOURCE_FILES) $(HEADER_FILES)
+	gcc -g3 -O0 -std=gnu99 -o link_training_test $(SOURCE_FILES) $(INCLUDEDIR) $(DEFINES)
+
+clean:
+	rm link_training_test $(COPIED_SOURCES)
diff --git a/link-training-test/drm_dp_helper.c b/link-training-test/drm_dp_helper.c
new file mode 100644
index 0000000..a8db7f9
--- /dev/null
+++ b/link-training-test/drm_dp_helper.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright © 2009 Keith Packard
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include "intel_drv.h"
+
+/* TODO: Get rid of this copy of drm_dp_helper functions. */
+
+static u8 dp_link_status(const u8 link_status[DP_LINK_STATUS_SIZE], int r)
+{
+        return link_status[r - DP_LANE0_1_STATUS];
+}
+
+static u8 dp_get_lane_status(const u8 link_status[DP_LINK_STATUS_SIZE],
+                             int lane)
+{
+        int i = DP_LANE0_1_STATUS + (lane >> 1);
+        int s = (lane & 1) * 4;
+        u8 l = dp_link_status(link_status, i);
+        return (l >> s) & 0xf;
+}
+
+bool drm_dp_channel_eq_ok(const u8 link_status[DP_LINK_STATUS_SIZE],
+                          int lane_count)
+{
+        u8 lane_align;
+        u8 lane_status;
+        int lane;
+
+        lane_align = dp_link_status(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 = dp_get_lane_status(link_status, lane);
+                if ((lane_status & DP_CHANNEL_EQ_BITS) != DP_CHANNEL_EQ_BITS)
+                        return false;
+        }
+        return true;
+}
+
+bool drm_dp_clock_recovery_ok(const u8 link_status[DP_LINK_STATUS_SIZE],
+                              int lane_count)
+{
+        int lane;
+        u8 lane_status;
+
+        for (lane = 0; lane < lane_count; lane++) {
+                lane_status = dp_get_lane_status(link_status, lane);
+                if ((lane_status & DP_LANE_CR_DONE) == 0)
+                        return false;
+        }
+        return true;
+}
+
+u8 drm_dp_get_adjust_request_voltage(const u8 link_status[DP_LINK_STATUS_SIZE],
+                                     int lane)
+{
+        int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
+        int s = ((lane & 1) ?
+                 DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT :
+                 DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT);
+        u8 l = dp_link_status(link_status, i);
+
+        return ((l >> s) & 0x3) << DP_TRAIN_VOLTAGE_SWING_SHIFT;
+}
+
+u8 drm_dp_get_adjust_request_pre_emphasis(const u8 link_status[DP_LINK_STATUS_SIZE],
+                                          int lane)
+{
+        int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
+        int s = ((lane & 1) ?
+                 DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT :
+                 DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT);
+        u8 l = dp_link_status(link_status, i);
+
+        return ((l >> s) & 0x3) << DP_TRAIN_PRE_EMPHASIS_SHIFT;
+}
+
+/* FIXME: */
+static void udelay() {}
+static void mdelay() {}
+
+void drm_dp_link_train_clock_recovery_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) {
+        if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] == 0)
+                udelay(100);
+        else
+                mdelay(dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4);
+}
+
+void drm_dp_link_train_channel_eq_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) {
+        if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] == 0)
+                udelay(400);
+        else
+                mdelay(dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4);
+}
+
diff --git a/link-training-test/intel_drv.h b/link-training-test/intel_drv.h
new file mode 100644
index 0000000..f7a6a6c
--- /dev/null
+++ b/link-training-test/intel_drv.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2006 Dave Airlie <airlied@linux.ie>
+ * Copyright (c) 2007-2015 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef FAKE_INTEL_DRV_H
+#define FAKE_INTEL_DRV_H
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <tools/include/linux/compiler.h>
+
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned int u32;
+typedef unsigned long size_t;
+typedef long ssize_t;
+
+struct drm_device {
+	void *dev_private;
+};
+
+static inline struct drm_i915_private *to_i915(const struct drm_device *dev)
+{
+	return dev->dev_private;
+}
+
+#define BUILD_BUG()
+
+#include "intel_dev_info.h"
+
+struct drm_i915_private {
+	struct intel_device_info info;
+	bool edp_low_vswing;
+	enum intel_pch pch_type;
+};
+
+
+struct i2c_adapter {
+};
+
+struct mutex {
+};
+
+#include <drm_dp_helper.h>
+
+#define DRM_ERROR printf
+#define DRM_DEBUG_KMS printf
+
+enum port {
+        PORT_A = 0,
+        PORT_B,
+        PORT_C,
+        PORT_D,
+        PORT_E,
+        I915_MAX_PORTS
+};
+#define port_name(p) ((p) + 'A')
+
+struct drm_encoder {
+	void *dev;
+};
+
+struct intel_encoder {
+	struct drm_encoder base;
+};
+
+struct intel_dp {
+	int link_rate;
+	int lane_count;
+	uint8_t link_bw;
+	uint8_t num_sink_rates;
+	uint8_t train_set[4];
+	bool train_set_valid;
+	struct drm_dp_aux aux;
+	uint8_t dpcd[DP_RECEIVER_CAP_SIZE];
+	uint32_t DP;
+	bool use_tps3;
+
+	/* Hold test private data */
+	void *priv;
+};
+
+struct intel_digital_port {
+	struct intel_encoder base;
+	struct intel_dp dp;
+	enum port port;
+};
+
+#define offsetof(type, member)  __builtin_offsetof (type, member)
+#define container_of(ptr, type, member) ({			\
+	const typeof( ((type *)0)->member ) *__mptr = (ptr);	\
+	(type *)( (char *)__mptr - offsetof(type,member) );})
+
+static inline struct intel_digital_port *
+dp_to_dig_port(struct intel_dp *intel_dp)
+{
+	return container_of(intel_dp, struct intel_digital_port, dp);
+}
+
+void
+intel_dp_program_link_training_pattern(struct intel_dp *intel_dp,
+                                      uint8_t dp_train_pat);
+void
+intel_dp_update_signal_levels(struct intel_dp *intel_dp);
+void intel_dp_set_idle_link_train(struct intel_dp *intel_dp);
+void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock,
+                          uint8_t *link_bw, uint8_t *rate_select);
+bool
+intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_STATUS_SIZE]);
+void intel_ddi_prepare_link_retrain(struct drm_encoder *encoder);
+void
+intel_dp_start_link_train(struct intel_dp *intel_dp);
+void
+intel_dp_stop_link_train(struct intel_dp *intel_dp);
+bool intel_dp_source_supports_hbr2(struct drm_device *dev);
+
+static inline struct drm_device *intel_dp_to_dev(struct intel_dp *intel_dp)
+{
+	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+
+	return intel_dig_port->base.base.dev;
+}
+
+#include "i915_reg.h"
+
+#endif /* FAKE_INTEL_DRV_H */
diff --git a/link-training-test/link_training_test.c b/link-training-test/link_training_test.c
new file mode 100644
index 0000000..aa73b9e
--- /dev/null
+++ b/link-training-test/link_training_test.c
@@ -0,0 +1,414 @@
+/*
+ * Copyright © 2015 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Ander Conselvan de Oliveira <ander.conselvan.de.oliveira@intel.com>
+ *
+ */
+
+#include <stdlib.h>
+#include <assert.h>
+
+#include "intel_drv.h"
+#include "i915_reg.h"
+
+struct sink_device {
+	ssize_t (*dpcd_write)(struct sink_device *sink, unsigned int offset,
+			      void *buffer, size_t size);
+	bool (*get_link_status)(struct sink_device *sink,
+				uint8_t link_status[DP_LINK_STATUS_SIZE]);
+
+	struct {
+		bool lane_count_and_bw_set;
+		bool training_pattern_1_set;
+		bool started_with_non_zero_levels;
+		bool cr_done;
+		bool channel_eq_done;
+
+		uint8_t dpcd[0x3000];
+	} data;
+};
+
+/* Fake sink device implementation */
+
+static uint8_t
+sink_device_lane_count(struct sink_device *sink)
+{
+	return sink->data.dpcd[DP_LANE_COUNT_SET];
+}
+
+static uint8_t
+sink_device_get_training_pattern(struct sink_device *sink)
+{
+	return sink->data.dpcd[DP_TRAINING_PATTERN_SET] & DP_TRAINING_PATTERN_MASK;
+}
+
+static uint8_t
+sink_device_get_voltage_swing(struct sink_device *sink, int lane)
+{
+	return sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] &
+		DP_TRAIN_VOLTAGE_SWING_MASK;
+}
+
+static uint8_t
+sink_device_get_pre_emphasis_level(struct sink_device *sink, int lane)
+{
+	return sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] &
+		 DP_TRAIN_PRE_EMPHASIS_MASK;
+}
+
+static void
+sink_device_check_lane_count_and_bw(struct sink_device *sink)
+{
+	if (sink->data.lane_count_and_bw_set)
+		return;
+
+	assert(sink->data.dpcd[DP_TRAINING_PATTERN_SET] == 0);
+
+	if (sink->data.dpcd[DP_LINK_BW_SET] != 0 &&
+	    sink->data.dpcd[DP_LANE_COUNT_SET] != 0)
+		sink->data.lane_count_and_bw_set = true;
+}
+
+static void
+sink_device_check_pattern_1_set(struct sink_device *sink)
+{
+	if (!sink->data.lane_count_and_bw_set ||
+	    sink->data.training_pattern_1_set)
+		return;
+
+	assert(sink_device_get_training_pattern(sink) <= DP_TRAINING_PATTERN_1);
+
+	if (sink_device_get_training_pattern(sink) != DP_TRAINING_PATTERN_1)
+		return;
+
+	assert(sink->data.dpcd[DP_LINK_BW_SET] == DP_LINK_BW_1_62 ||
+	       sink->data.dpcd[DP_LINK_BW_SET] == DP_LINK_BW_2_7);
+
+	assert(sink->data.dpcd[DP_LANE_COUNT_SET] == 1 ||
+	       sink->data.dpcd[DP_LANE_COUNT_SET] == 2 ||
+	       sink->data.dpcd[DP_LANE_COUNT_SET] == 4);
+
+	for (int lane = 0; lane < sink_device_lane_count(sink); lane++) {
+		if (sink_device_get_voltage_swing(sink, lane) != DP_TRAIN_VOLTAGE_SWING_LEVEL_0 ||
+		    sink_device_get_pre_emphasis_level(sink, lane) != DP_TRAIN_PRE_EMPH_LEVEL_0)
+			sink->data.started_with_non_zero_levels = true;
+	}
+
+	sink->data.training_pattern_1_set = true;
+}
+
+static void
+sink_device_check_pattern_2_set(struct sink_device *sink)
+{
+	if (!sink->data.cr_done)
+		return;
+
+	assert(sink_device_get_training_pattern(sink) == DP_TRAINING_PATTERN_2);
+}
+
+static void
+sink_device_check_pattern_disable(struct sink_device *sink)
+{
+	if (!sink->data.cr_done || ! sink->data.channel_eq_done)
+		return;
+
+	assert(sink_device_get_training_pattern(sink) == DP_TRAINING_PATTERN_DISABLE);
+}
+
+static ssize_t
+sink_device_dpcd_write(struct sink_device *sink, unsigned int offset,
+		       void *buffer, size_t size)
+{
+	memcpy(sink->data.dpcd + offset, buffer, size);
+
+	sink_device_check_lane_count_and_bw(sink);
+
+	if (!sink->data.cr_done)
+		sink_device_check_pattern_1_set(sink);
+	else if (!sink->data.channel_eq_done)
+		sink_device_check_pattern_2_set(sink);
+	else
+		sink_device_check_pattern_disable(sink);
+
+	return size;
+}
+
+static bool
+sink_device_max_voltage_reached(struct sink_device *sink, int lane)
+{
+	return (sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] & DP_TRAIN_MAX_SWING_REACHED) ==
+		DP_TRAIN_MAX_SWING_REACHED;
+}
+
+static bool
+sink_device_max_pre_emphasis_reached(struct sink_device *sink, int lane)
+{
+	return (sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] & DP_TRAIN_MAX_PRE_EMPHASIS_REACHED) ==
+		DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
+}
+
+static bool
+sink_device_request_higher_voltage_swing(struct sink_device *sink)
+{
+	bool max_reached;
+
+	for (int lane = 0; lane < sink_device_lane_count(sink); lane++) {
+		if (sink_device_max_voltage_reached(sink, lane)) {
+			max_reached = true;
+			break;
+		}
+	}
+
+	if (max_reached)
+		return false;
+
+	for (int lane = 0; lane < sink_device_lane_count(sink); lane++) {
+		sink->data.dpcd[DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1)] |=
+			(sink_device_get_voltage_swing(sink, lane) + 1) <<
+				(DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT * (lane & 1));
+	}
+
+	return true;
+}
+
+static bool
+sink_device_request_higher_pre_emphasis(struct sink_device *sink)
+{
+	bool max_reached;
+
+	for (int lane = 0; lane < sink_device_lane_count(sink); lane++) {
+		if (sink_device_max_pre_emphasis_reached(sink, lane)) {
+			max_reached = true;
+			break;
+		}
+	}
+
+	if (max_reached)
+		return false;
+
+	for (int lane = 0; lane < sink_device_lane_count(sink); lane++) {
+		sink->data.dpcd[DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1)] |=
+			(sink_device_get_pre_emphasis_level(sink, lane) + 1) <<
+				(DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT +
+				 (DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT * (lane & 1)));
+	}
+
+	return true;
+}
+
+static void
+sink_device_mark_cr_done(struct sink_device *sink)
+{
+	for (int lane = 0; lane < sink_device_lane_count(sink); lane++)
+		sink->data.dpcd[DP_LANE0_1_STATUS + (lane >> 1)] |=
+			DP_LANE_CR_DONE << (4 * (lane & 1));
+
+	sink->data.cr_done = true;
+}
+
+static void
+sink_device_mark_channel_eq_done(struct sink_device *sink)
+{
+	for (int lane = 0; lane < sink_device_lane_count(sink); lane++) {
+		uint8_t mask = (DP_LANE_CHANNEL_EQ_DONE | DP_LANE_SYMBOL_LOCKED);
+		sink->data.dpcd[DP_LANE0_1_STATUS + (lane >> 1)] |=
+			mask << (4 * (lane & 1));
+	}
+
+	sink->data.dpcd[DP_LANE_ALIGN_STATUS_UPDATED] |= DP_INTERLANE_ALIGN_DONE;
+
+	sink->data.channel_eq_done = true;
+}
+
+static bool
+sink_device_get_link_status(struct sink_device *sink,
+			    uint8_t link_status[DP_LINK_STATUS_SIZE])
+{
+	if (!sink->data.cr_done) {
+		if (!sink_device_request_higher_voltage_swing(sink))
+			sink_device_mark_cr_done(sink);
+	} else if (!sink->data.channel_eq_done) {
+		if (!sink_device_request_higher_pre_emphasis(sink))
+			sink_device_mark_channel_eq_done(sink);
+	}
+
+	memcpy(link_status, sink->data.dpcd + DP_LANE0_1_STATUS,
+	       DP_LINK_STATUS_SIZE);
+
+	return true;
+}
+
+static struct sink_device simple_sink = {
+	.get_link_status = sink_device_get_link_status,
+	.dpcd_write = sink_device_dpcd_write,
+};
+
+/* Glue code */
+
+void intel_ddi_prepare_link_retrain(struct drm_encoder *encoder)
+{
+}
+
+void intel_dp_set_idle_link_train(struct intel_dp *intel_dp)
+{
+}
+
+bool intel_dp_source_supports_hbr2(struct drm_device *dev)
+{
+	return false;
+}
+
+bool
+intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_STATUS_SIZE])
+{
+	struct sink_device *sink = intel_dp->priv;
+	return sink->get_link_status(sink, link_status);
+}
+
+void
+intel_dp_update_signal_levels(struct intel_dp *intel_dp)
+{
+}
+
+void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock,
+			   uint8_t *link_bw, uint8_t *rate_select)
+{
+	*link_bw = intel_dp->link_bw;
+	*rate_select = 0;
+}
+
+void
+intel_dp_program_link_training_pattern(struct intel_dp *intel_dp,
+				       uint8_t dp_train_pat)
+{
+}
+
+ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset,
+			  void *buffer, size_t size)
+{
+	struct intel_dp *intel_dp =
+		container_of(aux, struct intel_dp, aux);
+	struct sink_device *sink = intel_dp->priv;
+
+	return sink->dpcd_write(sink, offset, buffer, size);
+}
+
+/* --- */
+
+static struct intel_dp *
+intel_dp_create(struct drm_device *dev, int lanes, uint8_t link_bw)
+{
+	struct intel_digital_port *dig_port;
+
+	dig_port = calloc(1, sizeof *dig_port);
+	dig_port->base.base.dev = dev;
+	dig_port->dp.lane_count = lanes;
+	dig_port->dp.link_bw = link_bw;
+
+	return &dig_port->dp;
+}
+
+static void
+intel_dp_destroy(struct intel_dp *intel_dp)
+{
+	struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
+	free(dig_port);
+}
+
+/* FIXME: Yikes! */
+#define BITS_PER_LONG 64
+#include <include/linux/mod_devicetable.h>
+#include <i915_pciids.h>
+#include "intel_dev_info.c"
+
+static const struct pci_device_id pciidlist[] = {
+	INTEL_PCI_IDS,
+	{0, 0, 0}
+};
+
+struct drm_device *
+drm_device_for_pci_id(const struct pci_device_id *id)
+{
+	struct drm_device *dev;
+	struct drm_i915_private *dev_priv;
+
+	dev = calloc(1, sizeof *dev);
+	dev_priv = calloc(1, sizeof *dev_priv);
+	assert(dev && dev_priv);
+
+	dev->dev_private = dev_priv;
+
+	memcpy(&dev_priv->info, (void *) id->driver_data, sizeof dev_priv->info);
+	dev_priv->info.device_id = id->device;
+
+	/* TODO: set dev_priv->pch_type with an appropriate value */
+	dev_priv->pch_type = PCH_NONE;
+
+	return dev;
+}
+
+void
+drm_device_destroy(struct drm_device *dev)
+{
+	free(dev->dev_private);
+	free(dev);
+}
+
+int
+main(int argc, char *argv[])
+{
+	const struct pci_device_id *id;
+
+	for (id = &pciidlist[0];
+	     id->vendor != 0 && id->device != 0;
+	     id++) {
+		struct drm_device *dev = drm_device_for_pci_id(id);
+		struct intel_dp *intel_dp =
+			intel_dp_create(dev, 4, DP_LINK_BW_2_7);
+
+		if (IS_GEN2(dev) || IS_PINEVIEW(dev))
+			continue;
+
+		printf("Testing with device id %04x, gen %d\n",
+		       INTEL_DEVID(dev), INTEL_INFO(dev)->gen);
+
+		intel_dp->priv = &simple_sink;
+		memset(&simple_sink.data, 0, sizeof simple_sink.data);
+		simple_sink.data.dpcd[DP_MAX_LINK_RATE] = 0x0A;
+		simple_sink.data.dpcd[DP_MAX_LANE_COUNT] = 0x04;
+
+		intel_dp_start_link_train(intel_dp);
+		intel_dp_stop_link_train(intel_dp);
+
+		for (int lane = 0; lane < intel_dp->lane_count; lane++)
+			printf("lane %i: vswing: %d, pre-emph: %d\n", lane,
+			       sink_device_get_voltage_swing(&simple_sink, lane),
+			       sink_device_get_pre_emphasis_level(&simple_sink, lane));
+		printf("\n");
+
+		intel_dp_destroy(intel_dp);
+		drm_device_destroy(dev);
+	}
+
+	return 0;
+}
-- 
2.4.3

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* Re: [PATCH 7/8] drm/i915: Move max voltage and pre emphasis to intel_dp_link_training.c
  2015-09-08 12:27 ` [PATCH 7/8] drm/i915: Move max voltage and pre emphasis to intel_dp_link_training.c Ander Conselvan de Oliveira
@ 2015-09-08 13:01   ` Ville Syrjälä
  2015-10-19  5:14     ` Thulasimani, Sivakumar
  0 siblings, 1 reply; 31+ messages in thread
From: Ville Syrjälä @ 2015-09-08 13:01 UTC (permalink / raw)
  To: Ander Conselvan de Oliveira; +Cc: jani.nikula, intel-gfx

On Tue, Sep 08, 2015 at 03:27:58PM +0300, Ander Conselvan de Oliveira wrote:
> So link training tests can use real hardware limits.

These need to be kept in sync with the _signal_levels() functions, so
moving them to a separate file is a bit questionable.

I suggest that we should attempt to restructure this information as
some kind of tables, from which we can look up the max values as
well as the hardware specific values for each setting.

That of course won't solve your problem. So far I don't know what your
test even does and why does it need this information, so I guess I'll
need to read on...

> ---
>  drivers/gpu/drm/i915/intel_dp.c               | 99 ---------------------------
>  drivers/gpu/drm/i915/intel_dp_link_training.c | 92 +++++++++++++++++++++++++
>  drivers/gpu/drm/i915/intel_drv.h              | 11 +--
>  3 files changed, 99 insertions(+), 103 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
> index 5778059..da87aef 100644
> --- a/drivers/gpu/drm/i915/intel_dp.c
> +++ b/drivers/gpu/drm/i915/intel_dp.c
> @@ -111,13 +111,6 @@ static bool is_edp(struct intel_dp *intel_dp)
>  	return intel_dig_port->base.type == INTEL_OUTPUT_EDP;
>  }
>  
> -static struct drm_device *intel_dp_to_dev(struct intel_dp *intel_dp)
> -{
> -	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
> -
> -	return intel_dig_port->base.base.dev;
> -}
> -
>  static struct intel_dp *intel_attached_dp(struct drm_connector *connector)
>  {
>  	return enc_to_intel_dp(&intel_attached_encoder(connector)->base);
> @@ -3054,98 +3047,6 @@ intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_
>  				       DP_LINK_STATUS_SIZE) == DP_LINK_STATUS_SIZE;
>  }
>  
> -/* These are source-specific values. */
> -uint8_t
> -intel_dp_voltage_max(struct intel_dp *intel_dp)
> -{
> -	struct drm_device *dev = intel_dp_to_dev(intel_dp);
> -	struct drm_i915_private *dev_priv = dev->dev_private;
> -	enum port port = dp_to_dig_port(intel_dp)->port;
> -
> -	if (IS_BROXTON(dev))
> -		return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
> -	else if (INTEL_INFO(dev)->gen >= 9) {
> -		if (dev_priv->edp_low_vswing && port == PORT_A)
> -			return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
> -		return DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
> -	} else if (IS_VALLEYVIEW(dev))
> -		return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
> -	else if (IS_GEN7(dev) && port == PORT_A)
> -		return DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
> -	else if (HAS_PCH_CPT(dev) && port != PORT_A)
> -		return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
> -	else
> -		return DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
> -}
> -
> -uint8_t
> -intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t voltage_swing)
> -{
> -	struct drm_device *dev = intel_dp_to_dev(intel_dp);
> -	enum port port = dp_to_dig_port(intel_dp)->port;
> -
> -	if (INTEL_INFO(dev)->gen >= 9) {
> -		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
> -			return DP_TRAIN_PRE_EMPH_LEVEL_3;
> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
> -			return DP_TRAIN_PRE_EMPH_LEVEL_2;
> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
> -			return DP_TRAIN_PRE_EMPH_LEVEL_1;
> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
> -			return DP_TRAIN_PRE_EMPH_LEVEL_0;
> -		default:
> -			return DP_TRAIN_PRE_EMPH_LEVEL_0;
> -		}
> -	} else if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
> -		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
> -			return DP_TRAIN_PRE_EMPH_LEVEL_3;
> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
> -			return DP_TRAIN_PRE_EMPH_LEVEL_2;
> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
> -			return DP_TRAIN_PRE_EMPH_LEVEL_1;
> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
> -		default:
> -			return DP_TRAIN_PRE_EMPH_LEVEL_0;
> -		}
> -	} else if (IS_VALLEYVIEW(dev)) {
> -		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
> -			return DP_TRAIN_PRE_EMPH_LEVEL_3;
> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
> -			return DP_TRAIN_PRE_EMPH_LEVEL_2;
> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
> -			return DP_TRAIN_PRE_EMPH_LEVEL_1;
> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
> -		default:
> -			return DP_TRAIN_PRE_EMPH_LEVEL_0;
> -		}
> -	} else if (IS_GEN7(dev) && port == PORT_A) {
> -		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
> -			return DP_TRAIN_PRE_EMPH_LEVEL_2;
> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
> -			return DP_TRAIN_PRE_EMPH_LEVEL_1;
> -		default:
> -			return DP_TRAIN_PRE_EMPH_LEVEL_0;
> -		}
> -	} else {
> -		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
> -			return DP_TRAIN_PRE_EMPH_LEVEL_2;
> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
> -			return DP_TRAIN_PRE_EMPH_LEVEL_2;
> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
> -			return DP_TRAIN_PRE_EMPH_LEVEL_1;
> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
> -		default:
> -			return DP_TRAIN_PRE_EMPH_LEVEL_0;
> -		}
> -	}
> -}
> -
>  static uint32_t vlv_signal_levels(struct intel_dp *intel_dp)
>  {
>  	struct drm_device *dev = intel_dp_to_dev(intel_dp);
> diff --git a/drivers/gpu/drm/i915/intel_dp_link_training.c b/drivers/gpu/drm/i915/intel_dp_link_training.c
> index f33cbbb..8d27dce 100644
> --- a/drivers/gpu/drm/i915/intel_dp_link_training.c
> +++ b/drivers/gpu/drm/i915/intel_dp_link_training.c
> @@ -23,6 +23,98 @@
>  
>  #include "intel_drv.h"
>  
> +/* These are source-specific values. */
> +static uint8_t
> +intel_dp_voltage_max(struct intel_dp *intel_dp)
> +{
> +	struct drm_device *dev = intel_dp_to_dev(intel_dp);
> +	struct drm_i915_private *dev_priv = dev->dev_private;
> +	enum port port = dp_to_dig_port(intel_dp)->port;
> +
> +	if (IS_BROXTON(dev))
> +		return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
> +	else if (INTEL_INFO(dev)->gen >= 9) {
> +		if (dev_priv->edp_low_vswing && port == PORT_A)
> +			return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
> +		return DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
> +	} else if (IS_VALLEYVIEW(dev))
> +		return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
> +	else if (IS_GEN7(dev) && port == PORT_A)
> +		return DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
> +	else if (HAS_PCH_CPT(dev) && port != PORT_A)
> +		return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
> +	else
> +		return DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
> +}
> +
> +static uint8_t
> +intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t voltage_swing)
> +{
> +	struct drm_device *dev = intel_dp_to_dev(intel_dp);
> +	enum port port = dp_to_dig_port(intel_dp)->port;
> +
> +	if (INTEL_INFO(dev)->gen >= 9) {
> +		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
> +			return DP_TRAIN_PRE_EMPH_LEVEL_3;
> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
> +			return DP_TRAIN_PRE_EMPH_LEVEL_2;
> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
> +			return DP_TRAIN_PRE_EMPH_LEVEL_1;
> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
> +			return DP_TRAIN_PRE_EMPH_LEVEL_0;
> +		default:
> +			return DP_TRAIN_PRE_EMPH_LEVEL_0;
> +		}
> +	} else if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
> +		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
> +			return DP_TRAIN_PRE_EMPH_LEVEL_3;
> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
> +			return DP_TRAIN_PRE_EMPH_LEVEL_2;
> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
> +			return DP_TRAIN_PRE_EMPH_LEVEL_1;
> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
> +		default:
> +			return DP_TRAIN_PRE_EMPH_LEVEL_0;
> +		}
> +	} else if (IS_VALLEYVIEW(dev)) {
> +		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
> +			return DP_TRAIN_PRE_EMPH_LEVEL_3;
> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
> +			return DP_TRAIN_PRE_EMPH_LEVEL_2;
> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
> +			return DP_TRAIN_PRE_EMPH_LEVEL_1;
> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
> +		default:
> +			return DP_TRAIN_PRE_EMPH_LEVEL_0;
> +		}
> +	} else if (IS_GEN7(dev) && port == PORT_A) {
> +		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
> +			return DP_TRAIN_PRE_EMPH_LEVEL_2;
> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
> +			return DP_TRAIN_PRE_EMPH_LEVEL_1;
> +		default:
> +			return DP_TRAIN_PRE_EMPH_LEVEL_0;
> +		}
> +	} else {
> +		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
> +			return DP_TRAIN_PRE_EMPH_LEVEL_2;
> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
> +			return DP_TRAIN_PRE_EMPH_LEVEL_2;
> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
> +			return DP_TRAIN_PRE_EMPH_LEVEL_1;
> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
> +		default:
> +			return DP_TRAIN_PRE_EMPH_LEVEL_0;
> +		}
> +	}
> +}
> +
>  static void
>  intel_get_adjust_train(struct intel_dp *intel_dp,
>  		       const uint8_t link_status[DP_LINK_STATUS_SIZE])
> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> index 29ae4bb..671a20f 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -1216,15 +1216,18 @@ intel_dp_program_link_training_pattern(struct intel_dp *intel_dp,
>  void
>  intel_dp_update_signal_levels(struct intel_dp *intel_dp);
>  void intel_dp_set_idle_link_train(struct intel_dp *intel_dp);
> -uint8_t
> -intel_dp_voltage_max(struct intel_dp *intel_dp);
> -uint8_t
> -intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t voltage_swing);
>  void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock,
>  			   uint8_t *link_bw, uint8_t *rate_select);
>  bool intel_dp_source_supports_hbr2(struct drm_device *dev);
>  bool
>  intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_STATUS_SIZE]);
> +static inline struct drm_device *intel_dp_to_dev(struct intel_dp *intel_dp)
> +{
> +	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
> +
> +	return intel_dig_port->base.base.dev;
> +}
> +
>  
>  /* intel_dp_mst.c */
>  int intel_dp_mst_encoder_init(struct intel_digital_port *intel_dig_port, int conn_id);
> -- 
> 2.4.3

-- 
Ville Syrjälä
Intel OTC
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH i-g-t] Add a link training test
  2015-09-08 12:28 ` [PATCH i-g-t] Add a link training test Ander Conselvan de Oliveira
@ 2015-09-08 13:11   ` Ville Syrjälä
  2015-09-08 13:26     ` Ander Conselvan De Oliveira
  2015-09-09 10:33   ` Thomas Wood
  1 sibling, 1 reply; 31+ messages in thread
From: Ville Syrjälä @ 2015-09-08 13:11 UTC (permalink / raw)
  To: Ander Conselvan de Oliveira; +Cc: jani.nikula, intel-gfx

On Tue, Sep 08, 2015 at 03:28:00PM +0300, Ander Conselvan de Oliveira wrote:
> This adds a test that compiles the link training code from i915 into a
> separate executable and uses it to train a fake sink device. The test
> also uses device information from i915 to exercise the different code
> paths for different hardwdare generations.
> 
> In order to get the code to compile a lot of stubbing was necessary. It
> was also easier to copy a few functions from the drm_dp_helpers instead
> of getting the whole thing to compile as part of the test.

Hmm. So the only device spefic information you really need is:
- supported link rates
- max vswing/pre-emph settings
- DDI vs. not

Cooking up a few configurations with varying options would seem fairly
easy without having to pull in the whole device info, and the hw
specific max vswing/pre-emphasis stuff.

> ---
>  link-training-test/Makefile             |  40 +++
>  link-training-test/drm_dp_helper.c      | 115 +++++++++
>  link-training-test/intel_drv.h          | 148 ++++++++++++
>  link-training-test/link_training_test.c | 414 ++++++++++++++++++++++++++++++++
>  4 files changed, 717 insertions(+)
>  create mode 100644 link-training-test/Makefile
>  create mode 100644 link-training-test/drm_dp_helper.c
>  create mode 100644 link-training-test/intel_drv.h
>  create mode 100644 link-training-test/link_training_test.c
> 
> diff --git a/link-training-test/Makefile b/link-training-test/Makefile
> new file mode 100644
> index 0000000..07a9914
> --- /dev/null
> +++ b/link-training-test/Makefile
> @@ -0,0 +1,40 @@
> +KERNEL_SRC_DIR=/home/aconselv/linux
> +
> +# Files copied from i915 source tree
> +COPIED_SOURCES = \
> +	intel_dp_link_training.c \
> +	intel_dev_info.c \
> +	intel_dev_info.h \
> +	i915_reg.h
> +
> +INTEL_DP_LINK_TRAINING_C = $(KERNEL_SRC_DIR)/drivers/gpu/drm/i915/intel_dp_link_training.c
> +
> +INCLUDEDIR = \
> +	-I$(KERNEL_SRC_DIR)/include/drm \
> +	-I$(KERNEL_SRC_DIR)/ \
> +	-I.
> +
> +DEFINES = \
> +	-D__KERNEL__
> +
> +HEADER_FILES = \
> +	intel_drv.h
> +
> +SOURCE_FILES = \
> +	intel_dp_link_training.c \
> +	link_training_test.c \
> +	drm_dp_helper.c
> +
> +all: link_training_test
> +
> +#intel_dp_link_training.c: $(INTEL_DP_LINK_TRAINING_C)
> +#	cp $(INTEL_DP_LINK_TRAINING_C) .
> +
> +$(COPIED_SOURCES): %: $(KERNEL_SRC_DIR)/drivers/gpu/drm/i915/%
> +	cp $< $@
> +
> +link_training_test: $(COPIED_SOURCES) $(SOURCE_FILES) $(HEADER_FILES)
> +	gcc -g3 -O0 -std=gnu99 -o link_training_test $(SOURCE_FILES) $(INCLUDEDIR) $(DEFINES)
> +
> +clean:
> +	rm link_training_test $(COPIED_SOURCES)
> diff --git a/link-training-test/drm_dp_helper.c b/link-training-test/drm_dp_helper.c
> new file mode 100644
> index 0000000..a8db7f9
> --- /dev/null
> +++ b/link-training-test/drm_dp_helper.c
> @@ -0,0 +1,115 @@
> +/*
> + * Copyright © 2009 Keith Packard
> + *
> + * Permission to use, copy, modify, distribute, and sell this software and its
> + * documentation for any purpose is hereby granted without fee, provided that
> + * the above copyright notice appear in all copies and that both that copyright
> + * notice and this permission notice appear in supporting documentation, and
> + * that the name of the copyright holders not be used in advertising or
> + * publicity pertaining to distribution of the software without specific,
> + * written prior permission.  The copyright holders make no representations
> + * about the suitability of this software for any purpose.  It is provided "as
> + * is" without express or implied warranty.
> + *
> + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
> + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
> + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
> + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
> + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
> + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
> + * OF THIS SOFTWARE.
> + */
> +
> +#include "intel_drv.h"
> +
> +/* TODO: Get rid of this copy of drm_dp_helper functions. */
> +
> +static u8 dp_link_status(const u8 link_status[DP_LINK_STATUS_SIZE], int r)
> +{
> +        return link_status[r - DP_LANE0_1_STATUS];
> +}
> +
> +static u8 dp_get_lane_status(const u8 link_status[DP_LINK_STATUS_SIZE],
> +                             int lane)
> +{
> +        int i = DP_LANE0_1_STATUS + (lane >> 1);
> +        int s = (lane & 1) * 4;
> +        u8 l = dp_link_status(link_status, i);
> +        return (l >> s) & 0xf;
> +}
> +
> +bool drm_dp_channel_eq_ok(const u8 link_status[DP_LINK_STATUS_SIZE],
> +                          int lane_count)
> +{
> +        u8 lane_align;
> +        u8 lane_status;
> +        int lane;
> +
> +        lane_align = dp_link_status(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 = dp_get_lane_status(link_status, lane);
> +                if ((lane_status & DP_CHANNEL_EQ_BITS) != DP_CHANNEL_EQ_BITS)
> +                        return false;
> +        }
> +        return true;
> +}
> +
> +bool drm_dp_clock_recovery_ok(const u8 link_status[DP_LINK_STATUS_SIZE],
> +                              int lane_count)
> +{
> +        int lane;
> +        u8 lane_status;
> +
> +        for (lane = 0; lane < lane_count; lane++) {
> +                lane_status = dp_get_lane_status(link_status, lane);
> +                if ((lane_status & DP_LANE_CR_DONE) == 0)
> +                        return false;
> +        }
> +        return true;
> +}
> +
> +u8 drm_dp_get_adjust_request_voltage(const u8 link_status[DP_LINK_STATUS_SIZE],
> +                                     int lane)
> +{
> +        int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
> +        int s = ((lane & 1) ?
> +                 DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT :
> +                 DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT);
> +        u8 l = dp_link_status(link_status, i);
> +
> +        return ((l >> s) & 0x3) << DP_TRAIN_VOLTAGE_SWING_SHIFT;
> +}
> +
> +u8 drm_dp_get_adjust_request_pre_emphasis(const u8 link_status[DP_LINK_STATUS_SIZE],
> +                                          int lane)
> +{
> +        int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
> +        int s = ((lane & 1) ?
> +                 DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT :
> +                 DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT);
> +        u8 l = dp_link_status(link_status, i);
> +
> +        return ((l >> s) & 0x3) << DP_TRAIN_PRE_EMPHASIS_SHIFT;
> +}
> +
> +/* FIXME: */
> +static void udelay() {}
> +static void mdelay() {}
> +
> +void drm_dp_link_train_clock_recovery_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) {
> +        if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] == 0)
> +                udelay(100);
> +        else
> +                mdelay(dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4);
> +}
> +
> +void drm_dp_link_train_channel_eq_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) {
> +        if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] == 0)
> +                udelay(400);
> +        else
> +                mdelay(dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4);
> +}
> +
> diff --git a/link-training-test/intel_drv.h b/link-training-test/intel_drv.h
> new file mode 100644
> index 0000000..f7a6a6c
> --- /dev/null
> +++ b/link-training-test/intel_drv.h
> @@ -0,0 +1,148 @@
> +/*
> + * Copyright (c) 2006 Dave Airlie <airlied@linux.ie>
> + * Copyright (c) 2007-2015 Intel Corporation
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the next
> + * paragraph) shall be included in all copies or substantial portions of the
> + * Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> + * IN THE SOFTWARE.
> + */
> +
> +#ifndef FAKE_INTEL_DRV_H
> +#define FAKE_INTEL_DRV_H
> +
> +#include <stdio.h>
> +#include <stdint.h>
> +#include <stdbool.h>
> +#include <string.h>
> +#include <tools/include/linux/compiler.h>
> +
> +typedef unsigned char u8;
> +typedef unsigned short u16;
> +typedef unsigned int u32;
> +typedef unsigned long size_t;
> +typedef long ssize_t;
> +
> +struct drm_device {
> +	void *dev_private;
> +};
> +
> +static inline struct drm_i915_private *to_i915(const struct drm_device *dev)
> +{
> +	return dev->dev_private;
> +}
> +
> +#define BUILD_BUG()
> +
> +#include "intel_dev_info.h"
> +
> +struct drm_i915_private {
> +	struct intel_device_info info;
> +	bool edp_low_vswing;
> +	enum intel_pch pch_type;
> +};
> +
> +
> +struct i2c_adapter {
> +};
> +
> +struct mutex {
> +};
> +
> +#include <drm_dp_helper.h>
> +
> +#define DRM_ERROR printf
> +#define DRM_DEBUG_KMS printf
> +
> +enum port {
> +        PORT_A = 0,
> +        PORT_B,
> +        PORT_C,
> +        PORT_D,
> +        PORT_E,
> +        I915_MAX_PORTS
> +};
> +#define port_name(p) ((p) + 'A')
> +
> +struct drm_encoder {
> +	void *dev;
> +};
> +
> +struct intel_encoder {
> +	struct drm_encoder base;
> +};
> +
> +struct intel_dp {
> +	int link_rate;
> +	int lane_count;
> +	uint8_t link_bw;
> +	uint8_t num_sink_rates;
> +	uint8_t train_set[4];
> +	bool train_set_valid;
> +	struct drm_dp_aux aux;
> +	uint8_t dpcd[DP_RECEIVER_CAP_SIZE];
> +	uint32_t DP;
> +	bool use_tps3;
> +
> +	/* Hold test private data */
> +	void *priv;
> +};
> +
> +struct intel_digital_port {
> +	struct intel_encoder base;
> +	struct intel_dp dp;
> +	enum port port;
> +};
> +
> +#define offsetof(type, member)  __builtin_offsetof (type, member)
> +#define container_of(ptr, type, member) ({			\
> +	const typeof( ((type *)0)->member ) *__mptr = (ptr);	\
> +	(type *)( (char *)__mptr - offsetof(type,member) );})
> +
> +static inline struct intel_digital_port *
> +dp_to_dig_port(struct intel_dp *intel_dp)
> +{
> +	return container_of(intel_dp, struct intel_digital_port, dp);
> +}
> +
> +void
> +intel_dp_program_link_training_pattern(struct intel_dp *intel_dp,
> +                                      uint8_t dp_train_pat);
> +void
> +intel_dp_update_signal_levels(struct intel_dp *intel_dp);
> +void intel_dp_set_idle_link_train(struct intel_dp *intel_dp);
> +void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock,
> +                          uint8_t *link_bw, uint8_t *rate_select);
> +bool
> +intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_STATUS_SIZE]);
> +void intel_ddi_prepare_link_retrain(struct drm_encoder *encoder);
> +void
> +intel_dp_start_link_train(struct intel_dp *intel_dp);
> +void
> +intel_dp_stop_link_train(struct intel_dp *intel_dp);
> +bool intel_dp_source_supports_hbr2(struct drm_device *dev);
> +
> +static inline struct drm_device *intel_dp_to_dev(struct intel_dp *intel_dp)
> +{
> +	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
> +
> +	return intel_dig_port->base.base.dev;
> +}
> +
> +#include "i915_reg.h"
> +
> +#endif /* FAKE_INTEL_DRV_H */
> diff --git a/link-training-test/link_training_test.c b/link-training-test/link_training_test.c
> new file mode 100644
> index 0000000..aa73b9e
> --- /dev/null
> +++ b/link-training-test/link_training_test.c
> @@ -0,0 +1,414 @@
> +/*
> + * Copyright © 2015 Intel Corporation
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the next
> + * paragraph) shall be included in all copies or substantial portions of the
> + * Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> + * IN THE SOFTWARE.
> + *
> + * Authors:
> + *    Ander Conselvan de Oliveira <ander.conselvan.de.oliveira@intel.com>
> + *
> + */
> +
> +#include <stdlib.h>
> +#include <assert.h>
> +
> +#include "intel_drv.h"
> +#include "i915_reg.h"
> +
> +struct sink_device {
> +	ssize_t (*dpcd_write)(struct sink_device *sink, unsigned int offset,
> +			      void *buffer, size_t size);
> +	bool (*get_link_status)(struct sink_device *sink,
> +				uint8_t link_status[DP_LINK_STATUS_SIZE]);
> +
> +	struct {
> +		bool lane_count_and_bw_set;
> +		bool training_pattern_1_set;
> +		bool started_with_non_zero_levels;
> +		bool cr_done;
> +		bool channel_eq_done;
> +
> +		uint8_t dpcd[0x3000];
> +	} data;
> +};
> +
> +/* Fake sink device implementation */
> +
> +static uint8_t
> +sink_device_lane_count(struct sink_device *sink)
> +{
> +	return sink->data.dpcd[DP_LANE_COUNT_SET];
> +}
> +
> +static uint8_t
> +sink_device_get_training_pattern(struct sink_device *sink)
> +{
> +	return sink->data.dpcd[DP_TRAINING_PATTERN_SET] & DP_TRAINING_PATTERN_MASK;
> +}
> +
> +static uint8_t
> +sink_device_get_voltage_swing(struct sink_device *sink, int lane)
> +{
> +	return sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] &
> +		DP_TRAIN_VOLTAGE_SWING_MASK;
> +}
> +
> +static uint8_t
> +sink_device_get_pre_emphasis_level(struct sink_device *sink, int lane)
> +{
> +	return sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] &
> +		 DP_TRAIN_PRE_EMPHASIS_MASK;
> +}
> +
> +static void
> +sink_device_check_lane_count_and_bw(struct sink_device *sink)
> +{
> +	if (sink->data.lane_count_and_bw_set)
> +		return;
> +
> +	assert(sink->data.dpcd[DP_TRAINING_PATTERN_SET] == 0);
> +
> +	if (sink->data.dpcd[DP_LINK_BW_SET] != 0 &&
> +	    sink->data.dpcd[DP_LANE_COUNT_SET] != 0)
> +		sink->data.lane_count_and_bw_set = true;
> +}
> +
> +static void
> +sink_device_check_pattern_1_set(struct sink_device *sink)
> +{
> +	if (!sink->data.lane_count_and_bw_set ||
> +	    sink->data.training_pattern_1_set)
> +		return;
> +
> +	assert(sink_device_get_training_pattern(sink) <= DP_TRAINING_PATTERN_1);
> +
> +	if (sink_device_get_training_pattern(sink) != DP_TRAINING_PATTERN_1)
> +		return;
> +
> +	assert(sink->data.dpcd[DP_LINK_BW_SET] == DP_LINK_BW_1_62 ||
> +	       sink->data.dpcd[DP_LINK_BW_SET] == DP_LINK_BW_2_7);
> +
> +	assert(sink->data.dpcd[DP_LANE_COUNT_SET] == 1 ||
> +	       sink->data.dpcd[DP_LANE_COUNT_SET] == 2 ||
> +	       sink->data.dpcd[DP_LANE_COUNT_SET] == 4);
> +
> +	for (int lane = 0; lane < sink_device_lane_count(sink); lane++) {
> +		if (sink_device_get_voltage_swing(sink, lane) != DP_TRAIN_VOLTAGE_SWING_LEVEL_0 ||
> +		    sink_device_get_pre_emphasis_level(sink, lane) != DP_TRAIN_PRE_EMPH_LEVEL_0)
> +			sink->data.started_with_non_zero_levels = true;
> +	}
> +
> +	sink->data.training_pattern_1_set = true;
> +}
> +
> +static void
> +sink_device_check_pattern_2_set(struct sink_device *sink)
> +{
> +	if (!sink->data.cr_done)
> +		return;
> +
> +	assert(sink_device_get_training_pattern(sink) == DP_TRAINING_PATTERN_2);
> +}
> +
> +static void
> +sink_device_check_pattern_disable(struct sink_device *sink)
> +{
> +	if (!sink->data.cr_done || ! sink->data.channel_eq_done)
> +		return;
> +
> +	assert(sink_device_get_training_pattern(sink) == DP_TRAINING_PATTERN_DISABLE);
> +}
> +
> +static ssize_t
> +sink_device_dpcd_write(struct sink_device *sink, unsigned int offset,
> +		       void *buffer, size_t size)
> +{
> +	memcpy(sink->data.dpcd + offset, buffer, size);
> +
> +	sink_device_check_lane_count_and_bw(sink);
> +
> +	if (!sink->data.cr_done)
> +		sink_device_check_pattern_1_set(sink);
> +	else if (!sink->data.channel_eq_done)
> +		sink_device_check_pattern_2_set(sink);
> +	else
> +		sink_device_check_pattern_disable(sink);
> +
> +	return size;
> +}
> +
> +static bool
> +sink_device_max_voltage_reached(struct sink_device *sink, int lane)
> +{
> +	return (sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] & DP_TRAIN_MAX_SWING_REACHED) ==
> +		DP_TRAIN_MAX_SWING_REACHED;
> +}
> +
> +static bool
> +sink_device_max_pre_emphasis_reached(struct sink_device *sink, int lane)
> +{
> +	return (sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] & DP_TRAIN_MAX_PRE_EMPHASIS_REACHED) ==
> +		DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
> +}
> +
> +static bool
> +sink_device_request_higher_voltage_swing(struct sink_device *sink)
> +{
> +	bool max_reached;
> +
> +	for (int lane = 0; lane < sink_device_lane_count(sink); lane++) {
> +		if (sink_device_max_voltage_reached(sink, lane)) {
> +			max_reached = true;
> +			break;
> +		}
> +	}
> +
> +	if (max_reached)
> +		return false;
> +
> +	for (int lane = 0; lane < sink_device_lane_count(sink); lane++) {
> +		sink->data.dpcd[DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1)] |=
> +			(sink_device_get_voltage_swing(sink, lane) + 1) <<
> +				(DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT * (lane & 1));
> +	}
> +
> +	return true;
> +}
> +
> +static bool
> +sink_device_request_higher_pre_emphasis(struct sink_device *sink)
> +{
> +	bool max_reached;
> +
> +	for (int lane = 0; lane < sink_device_lane_count(sink); lane++) {
> +		if (sink_device_max_pre_emphasis_reached(sink, lane)) {
> +			max_reached = true;
> +			break;
> +		}
> +	}
> +
> +	if (max_reached)
> +		return false;
> +
> +	for (int lane = 0; lane < sink_device_lane_count(sink); lane++) {
> +		sink->data.dpcd[DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1)] |=
> +			(sink_device_get_pre_emphasis_level(sink, lane) + 1) <<
> +				(DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT +
> +				 (DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT * (lane & 1)));
> +	}
> +
> +	return true;
> +}
> +
> +static void
> +sink_device_mark_cr_done(struct sink_device *sink)
> +{
> +	for (int lane = 0; lane < sink_device_lane_count(sink); lane++)
> +		sink->data.dpcd[DP_LANE0_1_STATUS + (lane >> 1)] |=
> +			DP_LANE_CR_DONE << (4 * (lane & 1));
> +
> +	sink->data.cr_done = true;
> +}
> +
> +static void
> +sink_device_mark_channel_eq_done(struct sink_device *sink)
> +{
> +	for (int lane = 0; lane < sink_device_lane_count(sink); lane++) {
> +		uint8_t mask = (DP_LANE_CHANNEL_EQ_DONE | DP_LANE_SYMBOL_LOCKED);
> +		sink->data.dpcd[DP_LANE0_1_STATUS + (lane >> 1)] |=
> +			mask << (4 * (lane & 1));
> +	}
> +
> +	sink->data.dpcd[DP_LANE_ALIGN_STATUS_UPDATED] |= DP_INTERLANE_ALIGN_DONE;
> +
> +	sink->data.channel_eq_done = true;
> +}
> +
> +static bool
> +sink_device_get_link_status(struct sink_device *sink,
> +			    uint8_t link_status[DP_LINK_STATUS_SIZE])
> +{
> +	if (!sink->data.cr_done) {
> +		if (!sink_device_request_higher_voltage_swing(sink))
> +			sink_device_mark_cr_done(sink);
> +	} else if (!sink->data.channel_eq_done) {
> +		if (!sink_device_request_higher_pre_emphasis(sink))
> +			sink_device_mark_channel_eq_done(sink);
> +	}
> +
> +	memcpy(link_status, sink->data.dpcd + DP_LANE0_1_STATUS,
> +	       DP_LINK_STATUS_SIZE);
> +
> +	return true;
> +}
> +
> +static struct sink_device simple_sink = {
> +	.get_link_status = sink_device_get_link_status,
> +	.dpcd_write = sink_device_dpcd_write,
> +};
> +
> +/* Glue code */
> +
> +void intel_ddi_prepare_link_retrain(struct drm_encoder *encoder)
> +{
> +}
> +
> +void intel_dp_set_idle_link_train(struct intel_dp *intel_dp)
> +{
> +}
> +
> +bool intel_dp_source_supports_hbr2(struct drm_device *dev)
> +{
> +	return false;
> +}
> +
> +bool
> +intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_STATUS_SIZE])
> +{
> +	struct sink_device *sink = intel_dp->priv;
> +	return sink->get_link_status(sink, link_status);
> +}
> +
> +void
> +intel_dp_update_signal_levels(struct intel_dp *intel_dp)
> +{
> +}
> +
> +void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock,
> +			   uint8_t *link_bw, uint8_t *rate_select)
> +{
> +	*link_bw = intel_dp->link_bw;
> +	*rate_select = 0;
> +}
> +
> +void
> +intel_dp_program_link_training_pattern(struct intel_dp *intel_dp,
> +				       uint8_t dp_train_pat)
> +{
> +}
> +
> +ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset,
> +			  void *buffer, size_t size)
> +{
> +	struct intel_dp *intel_dp =
> +		container_of(aux, struct intel_dp, aux);
> +	struct sink_device *sink = intel_dp->priv;
> +
> +	return sink->dpcd_write(sink, offset, buffer, size);
> +}
> +
> +/* --- */
> +
> +static struct intel_dp *
> +intel_dp_create(struct drm_device *dev, int lanes, uint8_t link_bw)
> +{
> +	struct intel_digital_port *dig_port;
> +
> +	dig_port = calloc(1, sizeof *dig_port);
> +	dig_port->base.base.dev = dev;
> +	dig_port->dp.lane_count = lanes;
> +	dig_port->dp.link_bw = link_bw;
> +
> +	return &dig_port->dp;
> +}
> +
> +static void
> +intel_dp_destroy(struct intel_dp *intel_dp)
> +{
> +	struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
> +	free(dig_port);
> +}
> +
> +/* FIXME: Yikes! */
> +#define BITS_PER_LONG 64
> +#include <include/linux/mod_devicetable.h>
> +#include <i915_pciids.h>
> +#include "intel_dev_info.c"
> +
> +static const struct pci_device_id pciidlist[] = {
> +	INTEL_PCI_IDS,
> +	{0, 0, 0}
> +};
> +
> +struct drm_device *
> +drm_device_for_pci_id(const struct pci_device_id *id)
> +{
> +	struct drm_device *dev;
> +	struct drm_i915_private *dev_priv;
> +
> +	dev = calloc(1, sizeof *dev);
> +	dev_priv = calloc(1, sizeof *dev_priv);
> +	assert(dev && dev_priv);
> +
> +	dev->dev_private = dev_priv;
> +
> +	memcpy(&dev_priv->info, (void *) id->driver_data, sizeof dev_priv->info);
> +	dev_priv->info.device_id = id->device;
> +
> +	/* TODO: set dev_priv->pch_type with an appropriate value */
> +	dev_priv->pch_type = PCH_NONE;
> +
> +	return dev;
> +}
> +
> +void
> +drm_device_destroy(struct drm_device *dev)
> +{
> +	free(dev->dev_private);
> +	free(dev);
> +}
> +
> +int
> +main(int argc, char *argv[])
> +{
> +	const struct pci_device_id *id;
> +
> +	for (id = &pciidlist[0];
> +	     id->vendor != 0 && id->device != 0;
> +	     id++) {
> +		struct drm_device *dev = drm_device_for_pci_id(id);
> +		struct intel_dp *intel_dp =
> +			intel_dp_create(dev, 4, DP_LINK_BW_2_7);
> +
> +		if (IS_GEN2(dev) || IS_PINEVIEW(dev))
> +			continue;
> +
> +		printf("Testing with device id %04x, gen %d\n",
> +		       INTEL_DEVID(dev), INTEL_INFO(dev)->gen);
> +
> +		intel_dp->priv = &simple_sink;
> +		memset(&simple_sink.data, 0, sizeof simple_sink.data);
> +		simple_sink.data.dpcd[DP_MAX_LINK_RATE] = 0x0A;
> +		simple_sink.data.dpcd[DP_MAX_LANE_COUNT] = 0x04;
> +
> +		intel_dp_start_link_train(intel_dp);
> +		intel_dp_stop_link_train(intel_dp);
> +
> +		for (int lane = 0; lane < intel_dp->lane_count; lane++)
> +			printf("lane %i: vswing: %d, pre-emph: %d\n", lane,
> +			       sink_device_get_voltage_swing(&simple_sink, lane),
> +			       sink_device_get_pre_emphasis_level(&simple_sink, lane));
> +		printf("\n");
> +
> +		intel_dp_destroy(intel_dp);
> +		drm_device_destroy(dev);
> +	}
> +
> +	return 0;
> +}
> -- 
> 2.4.3

-- 
Ville Syrjälä
Intel OTC
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH i-g-t] Add a link training test
  2015-09-08 13:11   ` Ville Syrjälä
@ 2015-09-08 13:26     ` Ander Conselvan De Oliveira
  0 siblings, 0 replies; 31+ messages in thread
From: Ander Conselvan De Oliveira @ 2015-09-08 13:26 UTC (permalink / raw)
  To: Ville Syrjälä; +Cc: jani.nikula, intel-gfx

On Tue, 2015-09-08 at 16:11 +0300, Ville Syrjälä wrote:
> On Tue, Sep 08, 2015 at 03:28:00PM +0300, Ander Conselvan de Oliveira wrote:
> > This adds a test that compiles the link training code from i915 into a
> > separate executable and uses it to train a fake sink device. The test
> > also uses device information from i915 to exercise the different code
> > paths for different hardwdare generations.
> > 
> > In order to get the code to compile a lot of stubbing was necessary. It
> > was also easier to copy a few functions from the drm_dp_helpers instead
> > of getting the whole thing to compile as part of the test.
> 
> Hmm. So the only device spefic information you really need is:
> - supported link rates
> - max vswing/pre-emph settings
> - DDI vs. not
> 
> Cooking up a few configurations with varying options would seem fairly
> easy without having to pull in the whole device info, and the hw
> specific max vswing/pre-emphasis stuff.

Yeah. I actually implemented it first with a custom pair of intel_dp_{voltage,pre_emphasis}_max. The
device info was an exercise to see if it was doable to pull that info from the driver instead of
duplicating it locally, but it is certainly not worth it for this case.

Ander

> 
> > ---
> >  link-training-test/Makefile             |  40 +++
> >  link-training-test/drm_dp_helper.c      | 115 +++++++++
> >  link-training-test/intel_drv.h          | 148 ++++++++++++
> >  link-training-test/link_training_test.c | 414 ++++++++++++++++++++++++++++++++
> >  4 files changed, 717 insertions(+)
> >  create mode 100644 link-training-test/Makefile
> >  create mode 100644 link-training-test/drm_dp_helper.c
> >  create mode 100644 link-training-test/intel_drv.h
> >  create mode 100644 link-training-test/link_training_test.c
> > 
> > diff --git a/link-training-test/Makefile b/link-training-test/Makefile
> > new file mode 100644
> > index 0000000..07a9914
> > --- /dev/null
> > +++ b/link-training-test/Makefile
> > @@ -0,0 +1,40 @@
> > +KERNEL_SRC_DIR=/home/aconselv/linux
> > +
> > +# Files copied from i915 source tree
> > +COPIED_SOURCES = \
> > +	intel_dp_link_training.c \
> > +	intel_dev_info.c \
> > +	intel_dev_info.h \
> > +	i915_reg.h
> > +
> > +INTEL_DP_LINK_TRAINING_C = $(KERNEL_SRC_DIR)/drivers/gpu/drm/i915/intel_dp_link_training.c
> > +
> > +INCLUDEDIR = \
> > +	-I$(KERNEL_SRC_DIR)/include/drm \
> > +	-I$(KERNEL_SRC_DIR)/ \
> > +	-I.
> > +
> > +DEFINES = \
> > +	-D__KERNEL__
> > +
> > +HEADER_FILES = \
> > +	intel_drv.h
> > +
> > +SOURCE_FILES = \
> > +	intel_dp_link_training.c \
> > +	link_training_test.c \
> > +	drm_dp_helper.c
> > +
> > +all: link_training_test
> > +
> > +#intel_dp_link_training.c: $(INTEL_DP_LINK_TRAINING_C)
> > +#	cp $(INTEL_DP_LINK_TRAINING_C) .
> > +
> > +$(COPIED_SOURCES): %: $(KERNEL_SRC_DIR)/drivers/gpu/drm/i915/%
> > +	cp $< $@
> > +
> > +link_training_test: $(COPIED_SOURCES) $(SOURCE_FILES) $(HEADER_FILES)
> > +	gcc -g3 -O0 -std=gnu99 -o link_training_test $(SOURCE_FILES) $(INCLUDEDIR) $(DEFINES)
> > +
> > +clean:
> > +	rm link_training_test $(COPIED_SOURCES)
> > diff --git a/link-training-test/drm_dp_helper.c b/link-training-test/drm_dp_helper.c
> > new file mode 100644
> > index 0000000..a8db7f9
> > --- /dev/null
> > +++ b/link-training-test/drm_dp_helper.c
> > @@ -0,0 +1,115 @@
> > +/*
> > + * Copyright © 2009 Keith Packard
> > + *
> > + * Permission to use, copy, modify, distribute, and sell this software and its
> > + * documentation for any purpose is hereby granted without fee, provided that
> > + * the above copyright notice appear in all copies and that both that copyright
> > + * notice and this permission notice appear in supporting documentation, and
> > + * that the name of the copyright holders not be used in advertising or
> > + * publicity pertaining to distribution of the software without specific,
> > + * written prior permission.  The copyright holders make no representations
> > + * about the suitability of this software for any purpose.  It is provided "as
> > + * is" without express or implied warranty.
> > + *
> > + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
> > + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
> > + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
> > + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
> > + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
> > + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
> > + * OF THIS SOFTWARE.
> > + */
> > +
> > +#include "intel_drv.h"
> > +
> > +/* TODO: Get rid of this copy of drm_dp_helper functions. */
> > +
> > +static u8 dp_link_status(const u8 link_status[DP_LINK_STATUS_SIZE], int r)
> > +{
> > +        return link_status[r - DP_LANE0_1_STATUS];
> > +}
> > +
> > +static u8 dp_get_lane_status(const u8 link_status[DP_LINK_STATUS_SIZE],
> > +                             int lane)
> > +{
> > +        int i = DP_LANE0_1_STATUS + (lane >> 1);
> > +        int s = (lane & 1) * 4;
> > +        u8 l = dp_link_status(link_status, i);
> > +        return (l >> s) & 0xf;
> > +}
> > +
> > +bool drm_dp_channel_eq_ok(const u8 link_status[DP_LINK_STATUS_SIZE],
> > +                          int lane_count)
> > +{
> > +        u8 lane_align;
> > +        u8 lane_status;
> > +        int lane;
> > +
> > +        lane_align = dp_link_status(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 = dp_get_lane_status(link_status, lane);
> > +                if ((lane_status & DP_CHANNEL_EQ_BITS) != DP_CHANNEL_EQ_BITS)
> > +                        return false;
> > +        }
> > +        return true;
> > +}
> > +
> > +bool drm_dp_clock_recovery_ok(const u8 link_status[DP_LINK_STATUS_SIZE],
> > +                              int lane_count)
> > +{
> > +        int lane;
> > +        u8 lane_status;
> > +
> > +        for (lane = 0; lane < lane_count; lane++) {
> > +                lane_status = dp_get_lane_status(link_status, lane);
> > +                if ((lane_status & DP_LANE_CR_DONE) == 0)
> > +                        return false;
> > +        }
> > +        return true;
> > +}
> > +
> > +u8 drm_dp_get_adjust_request_voltage(const u8 link_status[DP_LINK_STATUS_SIZE],
> > +                                     int lane)
> > +{
> > +        int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
> > +        int s = ((lane & 1) ?
> > +                 DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT :
> > +                 DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT);
> > +        u8 l = dp_link_status(link_status, i);
> > +
> > +        return ((l >> s) & 0x3) << DP_TRAIN_VOLTAGE_SWING_SHIFT;
> > +}
> > +
> > +u8 drm_dp_get_adjust_request_pre_emphasis(const u8 link_status[DP_LINK_STATUS_SIZE],
> > +                                          int lane)
> > +{
> > +        int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
> > +        int s = ((lane & 1) ?
> > +                 DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT :
> > +                 DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT);
> > +        u8 l = dp_link_status(link_status, i);
> > +
> > +        return ((l >> s) & 0x3) << DP_TRAIN_PRE_EMPHASIS_SHIFT;
> > +}
> > +
> > +/* FIXME: */
> > +static void udelay() {}
> > +static void mdelay() {}
> > +
> > +void drm_dp_link_train_clock_recovery_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) {
> > +        if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] == 0)
> > +                udelay(100);
> > +        else
> > +                mdelay(dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4);
> > +}
> > +
> > +void drm_dp_link_train_channel_eq_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) {
> > +        if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] == 0)
> > +                udelay(400);
> > +        else
> > +                mdelay(dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4);
> > +}
> > +
> > diff --git a/link-training-test/intel_drv.h b/link-training-test/intel_drv.h
> > new file mode 100644
> > index 0000000..f7a6a6c
> > --- /dev/null
> > +++ b/link-training-test/intel_drv.h
> > @@ -0,0 +1,148 @@
> > +/*
> > + * Copyright (c) 2006 Dave Airlie <airlied@linux.ie>
> > + * Copyright (c) 2007-2015 Intel Corporation
> > + *
> > + * Permission is hereby granted, free of charge, to any person obtaining a
> > + * copy of this software and associated documentation files (the "Software"),
> > + * to deal in the Software without restriction, including without limitation
> > + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> > + * and/or sell copies of the Software, and to permit persons to whom the
> > + * Software is furnished to do so, subject to the following conditions:
> > + *
> > + * The above copyright notice and this permission notice (including the next
> > + * paragraph) shall be included in all copies or substantial portions of the
> > + * Software.
> > + *
> > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> > + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> > + * IN THE SOFTWARE.
> > + */
> > +
> > +#ifndef FAKE_INTEL_DRV_H
> > +#define FAKE_INTEL_DRV_H
> > +
> > +#include <stdio.h>
> > +#include <stdint.h>
> > +#include <stdbool.h>
> > +#include <string.h>
> > +#include <tools/include/linux/compiler.h>
> > +
> > +typedef unsigned char u8;
> > +typedef unsigned short u16;
> > +typedef unsigned int u32;
> > +typedef unsigned long size_t;
> > +typedef long ssize_t;
> > +
> > +struct drm_device {
> > +	void *dev_private;
> > +};
> > +
> > +static inline struct drm_i915_private *to_i915(const struct drm_device *dev)
> > +{
> > +	return dev->dev_private;
> > +}
> > +
> > +#define BUILD_BUG()
> > +
> > +#include "intel_dev_info.h"
> > +
> > +struct drm_i915_private {
> > +	struct intel_device_info info;
> > +	bool edp_low_vswing;
> > +	enum intel_pch pch_type;
> > +};
> > +
> > +
> > +struct i2c_adapter {
> > +};
> > +
> > +struct mutex {
> > +};
> > +
> > +#include <drm_dp_helper.h>
> > +
> > +#define DRM_ERROR printf
> > +#define DRM_DEBUG_KMS printf
> > +
> > +enum port {
> > +        PORT_A = 0,
> > +        PORT_B,
> > +        PORT_C,
> > +        PORT_D,
> > +        PORT_E,
> > +        I915_MAX_PORTS
> > +};
> > +#define port_name(p) ((p) + 'A')
> > +
> > +struct drm_encoder {
> > +	void *dev;
> > +};
> > +
> > +struct intel_encoder {
> > +	struct drm_encoder base;
> > +};
> > +
> > +struct intel_dp {
> > +	int link_rate;
> > +	int lane_count;
> > +	uint8_t link_bw;
> > +	uint8_t num_sink_rates;
> > +	uint8_t train_set[4];
> > +	bool train_set_valid;
> > +	struct drm_dp_aux aux;
> > +	uint8_t dpcd[DP_RECEIVER_CAP_SIZE];
> > +	uint32_t DP;
> > +	bool use_tps3;
> > +
> > +	/* Hold test private data */
> > +	void *priv;
> > +};
> > +
> > +struct intel_digital_port {
> > +	struct intel_encoder base;
> > +	struct intel_dp dp;
> > +	enum port port;
> > +};
> > +
> > +#define offsetof(type, member)  __builtin_offsetof (type, member)
> > +#define container_of(ptr, type, member) ({			\
> > +	const typeof( ((type *)0)->member ) *__mptr = (ptr);	\
> > +	(type *)( (char *)__mptr - offsetof(type,member) );})
> > +
> > +static inline struct intel_digital_port *
> > +dp_to_dig_port(struct intel_dp *intel_dp)
> > +{
> > +	return container_of(intel_dp, struct intel_digital_port, dp);
> > +}
> > +
> > +void
> > +intel_dp_program_link_training_pattern(struct intel_dp *intel_dp,
> > +                                      uint8_t dp_train_pat);
> > +void
> > +intel_dp_update_signal_levels(struct intel_dp *intel_dp);
> > +void intel_dp_set_idle_link_train(struct intel_dp *intel_dp);
> > +void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock,
> > +                          uint8_t *link_bw, uint8_t *rate_select);
> > +bool
> > +intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_STATUS_SIZE]);
> > +void intel_ddi_prepare_link_retrain(struct drm_encoder *encoder);
> > +void
> > +intel_dp_start_link_train(struct intel_dp *intel_dp);
> > +void
> > +intel_dp_stop_link_train(struct intel_dp *intel_dp);
> > +bool intel_dp_source_supports_hbr2(struct drm_device *dev);
> > +
> > +static inline struct drm_device *intel_dp_to_dev(struct intel_dp *intel_dp)
> > +{
> > +	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
> > +
> > +	return intel_dig_port->base.base.dev;
> > +}
> > +
> > +#include "i915_reg.h"
> > +
> > +#endif /* FAKE_INTEL_DRV_H */
> > diff --git a/link-training-test/link_training_test.c b/link-training-test/link_training_test.c
> > new file mode 100644
> > index 0000000..aa73b9e
> > --- /dev/null
> > +++ b/link-training-test/link_training_test.c
> > @@ -0,0 +1,414 @@
> > +/*
> > + * Copyright © 2015 Intel Corporation
> > + *
> > + * Permission is hereby granted, free of charge, to any person obtaining a
> > + * copy of this software and associated documentation files (the "Software"),
> > + * to deal in the Software without restriction, including without limitation
> > + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> > + * and/or sell copies of the Software, and to permit persons to whom the
> > + * Software is furnished to do so, subject to the following conditions:
> > + *
> > + * The above copyright notice and this permission notice (including the next
> > + * paragraph) shall be included in all copies or substantial portions of the
> > + * Software.
> > + *
> > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> > + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> > + * IN THE SOFTWARE.
> > + *
> > + * Authors:
> > + *    Ander Conselvan de Oliveira <ander.conselvan.de.oliveira@intel.com>
> > + *
> > + */
> > +
> > +#include <stdlib.h>
> > +#include <assert.h>
> > +
> > +#include "intel_drv.h"
> > +#include "i915_reg.h"
> > +
> > +struct sink_device {
> > +	ssize_t (*dpcd_write)(struct sink_device *sink, unsigned int offset,
> > +			      void *buffer, size_t size);
> > +	bool (*get_link_status)(struct sink_device *sink,
> > +				uint8_t link_status[DP_LINK_STATUS_SIZE]);
> > +
> > +	struct {
> > +		bool lane_count_and_bw_set;
> > +		bool training_pattern_1_set;
> > +		bool started_with_non_zero_levels;
> > +		bool cr_done;
> > +		bool channel_eq_done;
> > +
> > +		uint8_t dpcd[0x3000];
> > +	} data;
> > +};
> > +
> > +/* Fake sink device implementation */
> > +
> > +static uint8_t
> > +sink_device_lane_count(struct sink_device *sink)
> > +{
> > +	return sink->data.dpcd[DP_LANE_COUNT_SET];
> > +}
> > +
> > +static uint8_t
> > +sink_device_get_training_pattern(struct sink_device *sink)
> > +{
> > +	return sink->data.dpcd[DP_TRAINING_PATTERN_SET] & DP_TRAINING_PATTERN_MASK;
> > +}
> > +
> > +static uint8_t
> > +sink_device_get_voltage_swing(struct sink_device *sink, int lane)
> > +{
> > +	return sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] &
> > +		DP_TRAIN_VOLTAGE_SWING_MASK;
> > +}
> > +
> > +static uint8_t
> > +sink_device_get_pre_emphasis_level(struct sink_device *sink, int lane)
> > +{
> > +	return sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] &
> > +		 DP_TRAIN_PRE_EMPHASIS_MASK;
> > +}
> > +
> > +static void
> > +sink_device_check_lane_count_and_bw(struct sink_device *sink)
> > +{
> > +	if (sink->data.lane_count_and_bw_set)
> > +		return;
> > +
> > +	assert(sink->data.dpcd[DP_TRAINING_PATTERN_SET] == 0);
> > +
> > +	if (sink->data.dpcd[DP_LINK_BW_SET] != 0 &&
> > +	    sink->data.dpcd[DP_LANE_COUNT_SET] != 0)
> > +		sink->data.lane_count_and_bw_set = true;
> > +}
> > +
> > +static void
> > +sink_device_check_pattern_1_set(struct sink_device *sink)
> > +{
> > +	if (!sink->data.lane_count_and_bw_set ||
> > +	    sink->data.training_pattern_1_set)
> > +		return;
> > +
> > +	assert(sink_device_get_training_pattern(sink) <= DP_TRAINING_PATTERN_1);
> > +
> > +	if (sink_device_get_training_pattern(sink) != DP_TRAINING_PATTERN_1)
> > +		return;
> > +
> > +	assert(sink->data.dpcd[DP_LINK_BW_SET] == DP_LINK_BW_1_62 ||
> > +	       sink->data.dpcd[DP_LINK_BW_SET] == DP_LINK_BW_2_7);
> > +
> > +	assert(sink->data.dpcd[DP_LANE_COUNT_SET] == 1 ||
> > +	       sink->data.dpcd[DP_LANE_COUNT_SET] == 2 ||
> > +	       sink->data.dpcd[DP_LANE_COUNT_SET] == 4);
> > +
> > +	for (int lane = 0; lane < sink_device_lane_count(sink); lane++) {
> > +		if (sink_device_get_voltage_swing(sink, lane) != DP_TRAIN_VOLTAGE_SWING_LEVEL_0 
> > ||
> > +		    sink_device_get_pre_emphasis_level(sink, lane) != 
> > DP_TRAIN_PRE_EMPH_LEVEL_0)
> > +			sink->data.started_with_non_zero_levels = true;
> > +	}
> > +
> > +	sink->data.training_pattern_1_set = true;
> > +}
> > +
> > +static void
> > +sink_device_check_pattern_2_set(struct sink_device *sink)
> > +{
> > +	if (!sink->data.cr_done)
> > +		return;
> > +
> > +	assert(sink_device_get_training_pattern(sink) == DP_TRAINING_PATTERN_2);
> > +}
> > +
> > +static void
> > +sink_device_check_pattern_disable(struct sink_device *sink)
> > +{
> > +	if (!sink->data.cr_done || ! sink->data.channel_eq_done)
> > +		return;
> > +
> > +	assert(sink_device_get_training_pattern(sink) == DP_TRAINING_PATTERN_DISABLE);
> > +}
> > +
> > +static ssize_t
> > +sink_device_dpcd_write(struct sink_device *sink, unsigned int offset,
> > +		       void *buffer, size_t size)
> > +{
> > +	memcpy(sink->data.dpcd + offset, buffer, size);
> > +
> > +	sink_device_check_lane_count_and_bw(sink);
> > +
> > +	if (!sink->data.cr_done)
> > +		sink_device_check_pattern_1_set(sink);
> > +	else if (!sink->data.channel_eq_done)
> > +		sink_device_check_pattern_2_set(sink);
> > +	else
> > +		sink_device_check_pattern_disable(sink);
> > +
> > +	return size;
> > +}
> > +
> > +static bool
> > +sink_device_max_voltage_reached(struct sink_device *sink, int lane)
> > +{
> > +	return (sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] & DP_TRAIN_MAX_SWING_REACHED) ==
> > +		DP_TRAIN_MAX_SWING_REACHED;
> > +}
> > +
> > +static bool
> > +sink_device_max_pre_emphasis_reached(struct sink_device *sink, int lane)
> > +{
> > +	return (sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] & 
> > DP_TRAIN_MAX_PRE_EMPHASIS_REACHED) ==
> > +		DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
> > +}
> > +
> > +static bool
> > +sink_device_request_higher_voltage_swing(struct sink_device *sink)
> > +{
> > +	bool max_reached;
> > +
> > +	for (int lane = 0; lane < sink_device_lane_count(sink); lane++) {
> > +		if (sink_device_max_voltage_reached(sink, lane)) {
> > +			max_reached = true;
> > +			break;
> > +		}
> > +	}
> > +
> > +	if (max_reached)
> > +		return false;
> > +
> > +	for (int lane = 0; lane < sink_device_lane_count(sink); lane++) {
> > +		sink->data.dpcd[DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1)] |=
> > +			(sink_device_get_voltage_swing(sink, lane) + 1) <<
> > +				(DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT * (lane & 1));
> > +	}
> > +
> > +	return true;
> > +}
> > +
> > +static bool
> > +sink_device_request_higher_pre_emphasis(struct sink_device *sink)
> > +{
> > +	bool max_reached;
> > +
> > +	for (int lane = 0; lane < sink_device_lane_count(sink); lane++) {
> > +		if (sink_device_max_pre_emphasis_reached(sink, lane)) {
> > +			max_reached = true;
> > +			break;
> > +		}
> > +	}
> > +
> > +	if (max_reached)
> > +		return false;
> > +
> > +	for (int lane = 0; lane < sink_device_lane_count(sink); lane++) {
> > +		sink->data.dpcd[DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1)] |=
> > +			(sink_device_get_pre_emphasis_level(sink, lane) + 1) <<
> > +				(DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT +
> > +				 (DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT * (lane & 1)));
> > +	}
> > +
> > +	return true;
> > +}
> > +
> > +static void
> > +sink_device_mark_cr_done(struct sink_device *sink)
> > +{
> > +	for (int lane = 0; lane < sink_device_lane_count(sink); lane++)
> > +		sink->data.dpcd[DP_LANE0_1_STATUS + (lane >> 1)] |=
> > +			DP_LANE_CR_DONE << (4 * (lane & 1));
> > +
> > +	sink->data.cr_done = true;
> > +}
> > +
> > +static void
> > +sink_device_mark_channel_eq_done(struct sink_device *sink)
> > +{
> > +	for (int lane = 0; lane < sink_device_lane_count(sink); lane++) {
> > +		uint8_t mask = (DP_LANE_CHANNEL_EQ_DONE | DP_LANE_SYMBOL_LOCKED);
> > +		sink->data.dpcd[DP_LANE0_1_STATUS + (lane >> 1)] |=
> > +			mask << (4 * (lane & 1));
> > +	}
> > +
> > +	sink->data.dpcd[DP_LANE_ALIGN_STATUS_UPDATED] |= DP_INTERLANE_ALIGN_DONE;
> > +
> > +	sink->data.channel_eq_done = true;
> > +}
> > +
> > +static bool
> > +sink_device_get_link_status(struct sink_device *sink,
> > +			    uint8_t link_status[DP_LINK_STATUS_SIZE])
> > +{
> > +	if (!sink->data.cr_done) {
> > +		if (!sink_device_request_higher_voltage_swing(sink))
> > +			sink_device_mark_cr_done(sink);
> > +	} else if (!sink->data.channel_eq_done) {
> > +		if (!sink_device_request_higher_pre_emphasis(sink))
> > +			sink_device_mark_channel_eq_done(sink);
> > +	}
> > +
> > +	memcpy(link_status, sink->data.dpcd + DP_LANE0_1_STATUS,
> > +	       DP_LINK_STATUS_SIZE);
> > +
> > +	return true;
> > +}
> > +
> > +static struct sink_device simple_sink = {
> > +	.get_link_status = sink_device_get_link_status,
> > +	.dpcd_write = sink_device_dpcd_write,
> > +};
> > +
> > +/* Glue code */
> > +
> > +void intel_ddi_prepare_link_retrain(struct drm_encoder *encoder)
> > +{
> > +}
> > +
> > +void intel_dp_set_idle_link_train(struct intel_dp *intel_dp)
> > +{
> > +}
> > +
> > +bool intel_dp_source_supports_hbr2(struct drm_device *dev)
> > +{
> > +	return false;
> > +}
> > +
> > +bool
> > +intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_STATUS_SIZE])
> > +{
> > +	struct sink_device *sink = intel_dp->priv;
> > +	return sink->get_link_status(sink, link_status);
> > +}
> > +
> > +void
> > +intel_dp_update_signal_levels(struct intel_dp *intel_dp)
> > +{
> > +}
> > +
> > +void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock,
> > +			   uint8_t *link_bw, uint8_t *rate_select)
> > +{
> > +	*link_bw = intel_dp->link_bw;
> > +	*rate_select = 0;
> > +}
> > +
> > +void
> > +intel_dp_program_link_training_pattern(struct intel_dp *intel_dp,
> > +				       uint8_t dp_train_pat)
> > +{
> > +}
> > +
> > +ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset,
> > +			  void *buffer, size_t size)
> > +{
> > +	struct intel_dp *intel_dp =
> > +		container_of(aux, struct intel_dp, aux);
> > +	struct sink_device *sink = intel_dp->priv;
> > +
> > +	return sink->dpcd_write(sink, offset, buffer, size);
> > +}
> > +
> > +/* --- */
> > +
> > +static struct intel_dp *
> > +intel_dp_create(struct drm_device *dev, int lanes, uint8_t link_bw)
> > +{
> > +	struct intel_digital_port *dig_port;
> > +
> > +	dig_port = calloc(1, sizeof *dig_port);
> > +	dig_port->base.base.dev = dev;
> > +	dig_port->dp.lane_count = lanes;
> > +	dig_port->dp.link_bw = link_bw;
> > +
> > +	return &dig_port->dp;
> > +}
> > +
> > +static void
> > +intel_dp_destroy(struct intel_dp *intel_dp)
> > +{
> > +	struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
> > +	free(dig_port);
> > +}
> > +
> > +/* FIXME: Yikes! */
> > +#define BITS_PER_LONG 64
> > +#include <include/linux/mod_devicetable.h>
> > +#include <i915_pciids.h>
> > +#include "intel_dev_info.c"
> > +
> > +static const struct pci_device_id pciidlist[] = {
> > +	INTEL_PCI_IDS,
> > +	{0, 0, 0}
> > +};
> > +
> > +struct drm_device *
> > +drm_device_for_pci_id(const struct pci_device_id *id)
> > +{
> > +	struct drm_device *dev;
> > +	struct drm_i915_private *dev_priv;
> > +
> > +	dev = calloc(1, sizeof *dev);
> > +	dev_priv = calloc(1, sizeof *dev_priv);
> > +	assert(dev && dev_priv);
> > +
> > +	dev->dev_private = dev_priv;
> > +
> > +	memcpy(&dev_priv->info, (void *) id->driver_data, sizeof dev_priv->info);
> > +	dev_priv->info.device_id = id->device;
> > +
> > +	/* TODO: set dev_priv->pch_type with an appropriate value */
> > +	dev_priv->pch_type = PCH_NONE;
> > +
> > +	return dev;
> > +}
> > +
> > +void
> > +drm_device_destroy(struct drm_device *dev)
> > +{
> > +	free(dev->dev_private);
> > +	free(dev);
> > +}
> > +
> > +int
> > +main(int argc, char *argv[])
> > +{
> > +	const struct pci_device_id *id;
> > +
> > +	for (id = &pciidlist[0];
> > +	     id->vendor != 0 && id->device != 0;
> > +	     id++) {
> > +		struct drm_device *dev = drm_device_for_pci_id(id);
> > +		struct intel_dp *intel_dp =
> > +			intel_dp_create(dev, 4, DP_LINK_BW_2_7);
> > +
> > +		if (IS_GEN2(dev) || IS_PINEVIEW(dev))
> > +			continue;
> > +
> > +		printf("Testing with device id %04x, gen %d\n",
> > +		       INTEL_DEVID(dev), INTEL_INFO(dev)->gen);
> > +
> > +		intel_dp->priv = &simple_sink;
> > +		memset(&simple_sink.data, 0, sizeof simple_sink.data);
> > +		simple_sink.data.dpcd[DP_MAX_LINK_RATE] = 0x0A;
> > +		simple_sink.data.dpcd[DP_MAX_LANE_COUNT] = 0x04;
> > +
> > +		intel_dp_start_link_train(intel_dp);
> > +		intel_dp_stop_link_train(intel_dp);
> > +
> > +		for (int lane = 0; lane < intel_dp->lane_count; lane++)
> > +			printf("lane %i: vswing: %d, pre-emph: %d\n", lane,
> > +			       sink_device_get_voltage_swing(&simple_sink, lane),
> > +			       sink_device_get_pre_emphasis_level(&simple_sink, lane));
> > +		printf("\n");
> > +
> > +		intel_dp_destroy(intel_dp);
> > +		drm_device_destroy(dev);
> > +	}
> > +
> > +	return 0;
> > +}
> > -- 
> > 2.4.3
> 
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH i-g-t] Add a link training test
  2015-09-08 12:28 ` [PATCH i-g-t] Add a link training test Ander Conselvan de Oliveira
  2015-09-08 13:11   ` Ville Syrjälä
@ 2015-09-09 10:33   ` Thomas Wood
  2015-09-11 14:11     ` [PATCH] Add a link training test (v2) Ander Conselvan de Oliveira
  1 sibling, 1 reply; 31+ messages in thread
From: Thomas Wood @ 2015-09-09 10:33 UTC (permalink / raw)
  To: Ander Conselvan de Oliveira; +Cc: Jani Nikula, Intel Graphics Development

On 8 September 2015 at 13:28, Ander Conselvan de Oliveira
<ander.conselvan.de.oliveira@intel.com> wrote:
> This adds a test that compiles the link training code from i915 into a
> separate executable and uses it to train a fake sink device. The test
> also uses device information from i915 to exercise the different code
> paths for different hardwdare generations.
>
> In order to get the code to compile a lot of stubbing was necessary. It
> was also easier to copy a few functions from the drm_dp_helpers instead
> of getting the whole thing to compile as part of the test.
> ---
>  link-training-test/Makefile             |  40 +++
>  link-training-test/drm_dp_helper.c      | 115 +++++++++
>  link-training-test/intel_drv.h          | 148 ++++++++++++
>  link-training-test/link_training_test.c | 414 ++++++++++++++++++++++++++++++++
>  4 files changed, 717 insertions(+)
>  create mode 100644 link-training-test/Makefile
>  create mode 100644 link-training-test/drm_dp_helper.c
>  create mode 100644 link-training-test/intel_drv.h
>  create mode 100644 link-training-test/link_training_test.c
>
> diff --git a/link-training-test/Makefile b/link-training-test/Makefile

If this is meant to be part of the test suite, then it needs to be in
the tests directory and use the igt test infrastructure. Otherwise it
should be placed in tools or tools/link-training-test.


> new file mode 100644
> index 0000000..07a9914
> --- /dev/null
> +++ b/link-training-test/Makefile
> @@ -0,0 +1,40 @@
> +KERNEL_SRC_DIR=/home/aconselv/linux

It may be necassary to make the building of this test optional in
configure and provide a way of specifying the kernel source directory.


> +
> +# Files copied from i915 source tree
> +COPIED_SOURCES = \
> +       intel_dp_link_training.c \
> +       intel_dev_info.c \
> +       intel_dev_info.h \
> +       i915_reg.h
> +
> +INTEL_DP_LINK_TRAINING_C = $(KERNEL_SRC_DIR)/drivers/gpu/drm/i915/intel_dp_link_training.c
> +
> +INCLUDEDIR = \
> +       -I$(KERNEL_SRC_DIR)/include/drm \
> +       -I$(KERNEL_SRC_DIR)/ \
> +       -I.
> +
> +DEFINES = \
> +       -D__KERNEL__
> +
> +HEADER_FILES = \
> +       intel_drv.h
> +
> +SOURCE_FILES = \
> +       intel_dp_link_training.c \
> +       link_training_test.c \
> +       drm_dp_helper.c
> +
> +all: link_training_test
> +
> +#intel_dp_link_training.c: $(INTEL_DP_LINK_TRAINING_C)
> +#      cp $(INTEL_DP_LINK_TRAINING_C) .
> +
> +$(COPIED_SOURCES): %: $(KERNEL_SRC_DIR)/drivers/gpu/drm/i915/%
> +       cp $< $@
> +
> +link_training_test: $(COPIED_SOURCES) $(SOURCE_FILES) $(HEADER_FILES)
> +       gcc -g3 -O0 -std=gnu99 -o link_training_test $(SOURCE_FILES) $(INCLUDEDIR) $(DEFINES)
> +
> +clean:
> +       rm link_training_test $(COPIED_SOURCES)
> diff --git a/link-training-test/drm_dp_helper.c b/link-training-test/drm_dp_helper.c
> new file mode 100644
> index 0000000..a8db7f9
> --- /dev/null
> +++ b/link-training-test/drm_dp_helper.c
> @@ -0,0 +1,115 @@
> +/*
> + * Copyright © 2009 Keith Packard
> + *
> + * Permission to use, copy, modify, distribute, and sell this software and its
> + * documentation for any purpose is hereby granted without fee, provided that
> + * the above copyright notice appear in all copies and that both that copyright
> + * notice and this permission notice appear in supporting documentation, and
> + * that the name of the copyright holders not be used in advertising or
> + * publicity pertaining to distribution of the software without specific,
> + * written prior permission.  The copyright holders make no representations
> + * about the suitability of this software for any purpose.  It is provided "as
> + * is" without express or implied warranty.
> + *
> + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
> + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
> + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
> + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
> + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
> + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
> + * OF THIS SOFTWARE.
> + */
> +
> +#include "intel_drv.h"
> +
> +/* TODO: Get rid of this copy of drm_dp_helper functions. */
> +
> +static u8 dp_link_status(const u8 link_status[DP_LINK_STATUS_SIZE], int r)
> +{
> +        return link_status[r - DP_LANE0_1_STATUS];
> +}
> +
> +static u8 dp_get_lane_status(const u8 link_status[DP_LINK_STATUS_SIZE],
> +                             int lane)
> +{
> +        int i = DP_LANE0_1_STATUS + (lane >> 1);
> +        int s = (lane & 1) * 4;
> +        u8 l = dp_link_status(link_status, i);
> +        return (l >> s) & 0xf;
> +}
> +
> +bool drm_dp_channel_eq_ok(const u8 link_status[DP_LINK_STATUS_SIZE],
> +                          int lane_count)
> +{
> +        u8 lane_align;
> +        u8 lane_status;
> +        int lane;
> +
> +        lane_align = dp_link_status(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 = dp_get_lane_status(link_status, lane);
> +                if ((lane_status & DP_CHANNEL_EQ_BITS) != DP_CHANNEL_EQ_BITS)
> +                        return false;
> +        }
> +        return true;
> +}
> +
> +bool drm_dp_clock_recovery_ok(const u8 link_status[DP_LINK_STATUS_SIZE],
> +                              int lane_count)
> +{
> +        int lane;
> +        u8 lane_status;
> +
> +        for (lane = 0; lane < lane_count; lane++) {
> +                lane_status = dp_get_lane_status(link_status, lane);
> +                if ((lane_status & DP_LANE_CR_DONE) == 0)
> +                        return false;
> +        }
> +        return true;
> +}
> +
> +u8 drm_dp_get_adjust_request_voltage(const u8 link_status[DP_LINK_STATUS_SIZE],
> +                                     int lane)
> +{
> +        int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
> +        int s = ((lane & 1) ?
> +                 DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT :
> +                 DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT);
> +        u8 l = dp_link_status(link_status, i);
> +
> +        return ((l >> s) & 0x3) << DP_TRAIN_VOLTAGE_SWING_SHIFT;
> +}
> +
> +u8 drm_dp_get_adjust_request_pre_emphasis(const u8 link_status[DP_LINK_STATUS_SIZE],
> +                                          int lane)
> +{
> +        int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
> +        int s = ((lane & 1) ?
> +                 DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT :
> +                 DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT);
> +        u8 l = dp_link_status(link_status, i);
> +
> +        return ((l >> s) & 0x3) << DP_TRAIN_PRE_EMPHASIS_SHIFT;
> +}
> +
> +/* FIXME: */
> +static void udelay() {}
> +static void mdelay() {}
> +
> +void drm_dp_link_train_clock_recovery_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) {
> +        if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] == 0)
> +                udelay(100);
> +        else
> +                mdelay(dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4);
> +}
> +
> +void drm_dp_link_train_channel_eq_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) {
> +        if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] == 0)
> +                udelay(400);
> +        else
> +                mdelay(dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4);
> +}
> +
> diff --git a/link-training-test/intel_drv.h b/link-training-test/intel_drv.h
> new file mode 100644
> index 0000000..f7a6a6c
> --- /dev/null
> +++ b/link-training-test/intel_drv.h
> @@ -0,0 +1,148 @@
> +/*
> + * Copyright (c) 2006 Dave Airlie <airlied@linux.ie>
> + * Copyright (c) 2007-2015 Intel Corporation
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the next
> + * paragraph) shall be included in all copies or substantial portions of the
> + * Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> + * IN THE SOFTWARE.
> + */
> +
> +#ifndef FAKE_INTEL_DRV_H
> +#define FAKE_INTEL_DRV_H
> +
> +#include <stdio.h>
> +#include <stdint.h>
> +#include <stdbool.h>
> +#include <string.h>
> +#include <tools/include/linux/compiler.h>
> +
> +typedef unsigned char u8;
> +typedef unsigned short u16;
> +typedef unsigned int u32;
> +typedef unsigned long size_t;
> +typedef long ssize_t;
> +
> +struct drm_device {
> +       void *dev_private;
> +};
> +
> +static inline struct drm_i915_private *to_i915(const struct drm_device *dev)
> +{
> +       return dev->dev_private;
> +}
> +
> +#define BUILD_BUG()
> +
> +#include "intel_dev_info.h"
> +
> +struct drm_i915_private {
> +       struct intel_device_info info;
> +       bool edp_low_vswing;
> +       enum intel_pch pch_type;
> +};
> +
> +
> +struct i2c_adapter {
> +};
> +
> +struct mutex {
> +};
> +
> +#include <drm_dp_helper.h>
> +
> +#define DRM_ERROR printf
> +#define DRM_DEBUG_KMS printf
> +
> +enum port {
> +        PORT_A = 0,
> +        PORT_B,
> +        PORT_C,
> +        PORT_D,
> +        PORT_E,
> +        I915_MAX_PORTS
> +};
> +#define port_name(p) ((p) + 'A')
> +
> +struct drm_encoder {
> +       void *dev;
> +};
> +
> +struct intel_encoder {
> +       struct drm_encoder base;
> +};
> +
> +struct intel_dp {
> +       int link_rate;
> +       int lane_count;
> +       uint8_t link_bw;
> +       uint8_t num_sink_rates;
> +       uint8_t train_set[4];
> +       bool train_set_valid;
> +       struct drm_dp_aux aux;
> +       uint8_t dpcd[DP_RECEIVER_CAP_SIZE];
> +       uint32_t DP;
> +       bool use_tps3;
> +
> +       /* Hold test private data */
> +       void *priv;
> +};
> +
> +struct intel_digital_port {
> +       struct intel_encoder base;
> +       struct intel_dp dp;
> +       enum port port;
> +};
> +
> +#define offsetof(type, member)  __builtin_offsetof (type, member)
> +#define container_of(ptr, type, member) ({                     \
> +       const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
> +       (type *)( (char *)__mptr - offsetof(type,member) );})
> +
> +static inline struct intel_digital_port *
> +dp_to_dig_port(struct intel_dp *intel_dp)
> +{
> +       return container_of(intel_dp, struct intel_digital_port, dp);
> +}
> +
> +void
> +intel_dp_program_link_training_pattern(struct intel_dp *intel_dp,
> +                                      uint8_t dp_train_pat);
> +void
> +intel_dp_update_signal_levels(struct intel_dp *intel_dp);
> +void intel_dp_set_idle_link_train(struct intel_dp *intel_dp);
> +void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock,
> +                          uint8_t *link_bw, uint8_t *rate_select);
> +bool
> +intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_STATUS_SIZE]);
> +void intel_ddi_prepare_link_retrain(struct drm_encoder *encoder);
> +void
> +intel_dp_start_link_train(struct intel_dp *intel_dp);
> +void
> +intel_dp_stop_link_train(struct intel_dp *intel_dp);
> +bool intel_dp_source_supports_hbr2(struct drm_device *dev);
> +
> +static inline struct drm_device *intel_dp_to_dev(struct intel_dp *intel_dp)
> +{
> +       struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
> +
> +       return intel_dig_port->base.base.dev;
> +}
> +
> +#include "i915_reg.h"
> +
> +#endif /* FAKE_INTEL_DRV_H */
> diff --git a/link-training-test/link_training_test.c b/link-training-test/link_training_test.c
> new file mode 100644
> index 0000000..aa73b9e
> --- /dev/null
> +++ b/link-training-test/link_training_test.c
> @@ -0,0 +1,414 @@
> +/*
> + * Copyright © 2015 Intel Corporation
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the next
> + * paragraph) shall be included in all copies or substantial portions of the
> + * Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> + * IN THE SOFTWARE.
> + *
> + * Authors:
> + *    Ander Conselvan de Oliveira <ander.conselvan.de.oliveira@intel.com>
> + *
> + */
> +
> +#include <stdlib.h>
> +#include <assert.h>
> +
> +#include "intel_drv.h"
> +#include "i915_reg.h"
> +
> +struct sink_device {
> +       ssize_t (*dpcd_write)(struct sink_device *sink, unsigned int offset,
> +                             void *buffer, size_t size);
> +       bool (*get_link_status)(struct sink_device *sink,
> +                               uint8_t link_status[DP_LINK_STATUS_SIZE]);
> +
> +       struct {
> +               bool lane_count_and_bw_set;
> +               bool training_pattern_1_set;
> +               bool started_with_non_zero_levels;
> +               bool cr_done;
> +               bool channel_eq_done;
> +
> +               uint8_t dpcd[0x3000];
> +       } data;
> +};
> +
> +/* Fake sink device implementation */
> +
> +static uint8_t
> +sink_device_lane_count(struct sink_device *sink)
> +{
> +       return sink->data.dpcd[DP_LANE_COUNT_SET];
> +}
> +
> +static uint8_t
> +sink_device_get_training_pattern(struct sink_device *sink)
> +{
> +       return sink->data.dpcd[DP_TRAINING_PATTERN_SET] & DP_TRAINING_PATTERN_MASK;
> +}
> +
> +static uint8_t
> +sink_device_get_voltage_swing(struct sink_device *sink, int lane)
> +{
> +       return sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] &
> +               DP_TRAIN_VOLTAGE_SWING_MASK;
> +}
> +
> +static uint8_t
> +sink_device_get_pre_emphasis_level(struct sink_device *sink, int lane)
> +{
> +       return sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] &
> +                DP_TRAIN_PRE_EMPHASIS_MASK;
> +}
> +
> +static void
> +sink_device_check_lane_count_and_bw(struct sink_device *sink)
> +{
> +       if (sink->data.lane_count_and_bw_set)
> +               return;
> +
> +       assert(sink->data.dpcd[DP_TRAINING_PATTERN_SET] == 0);
> +
> +       if (sink->data.dpcd[DP_LINK_BW_SET] != 0 &&
> +           sink->data.dpcd[DP_LANE_COUNT_SET] != 0)
> +               sink->data.lane_count_and_bw_set = true;
> +}
> +
> +static void
> +sink_device_check_pattern_1_set(struct sink_device *sink)
> +{
> +       if (!sink->data.lane_count_and_bw_set ||
> +           sink->data.training_pattern_1_set)
> +               return;
> +
> +       assert(sink_device_get_training_pattern(sink) <= DP_TRAINING_PATTERN_1);
> +
> +       if (sink_device_get_training_pattern(sink) != DP_TRAINING_PATTERN_1)
> +               return;
> +
> +       assert(sink->data.dpcd[DP_LINK_BW_SET] == DP_LINK_BW_1_62 ||
> +              sink->data.dpcd[DP_LINK_BW_SET] == DP_LINK_BW_2_7);
> +
> +       assert(sink->data.dpcd[DP_LANE_COUNT_SET] == 1 ||
> +              sink->data.dpcd[DP_LANE_COUNT_SET] == 2 ||
> +              sink->data.dpcd[DP_LANE_COUNT_SET] == 4);
> +
> +       for (int lane = 0; lane < sink_device_lane_count(sink); lane++) {
> +               if (sink_device_get_voltage_swing(sink, lane) != DP_TRAIN_VOLTAGE_SWING_LEVEL_0 ||
> +                   sink_device_get_pre_emphasis_level(sink, lane) != DP_TRAIN_PRE_EMPH_LEVEL_0)
> +                       sink->data.started_with_non_zero_levels = true;
> +       }
> +
> +       sink->data.training_pattern_1_set = true;
> +}
> +
> +static void
> +sink_device_check_pattern_2_set(struct sink_device *sink)
> +{
> +       if (!sink->data.cr_done)
> +               return;
> +
> +       assert(sink_device_get_training_pattern(sink) == DP_TRAINING_PATTERN_2);
> +}
> +
> +static void
> +sink_device_check_pattern_disable(struct sink_device *sink)
> +{
> +       if (!sink->data.cr_done || ! sink->data.channel_eq_done)
> +               return;
> +
> +       assert(sink_device_get_training_pattern(sink) == DP_TRAINING_PATTERN_DISABLE);
> +}
> +
> +static ssize_t
> +sink_device_dpcd_write(struct sink_device *sink, unsigned int offset,
> +                      void *buffer, size_t size)
> +{
> +       memcpy(sink->data.dpcd + offset, buffer, size);
> +
> +       sink_device_check_lane_count_and_bw(sink);
> +
> +       if (!sink->data.cr_done)
> +               sink_device_check_pattern_1_set(sink);
> +       else if (!sink->data.channel_eq_done)
> +               sink_device_check_pattern_2_set(sink);
> +       else
> +               sink_device_check_pattern_disable(sink);
> +
> +       return size;
> +}
> +
> +static bool
> +sink_device_max_voltage_reached(struct sink_device *sink, int lane)
> +{
> +       return (sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] & DP_TRAIN_MAX_SWING_REACHED) ==
> +               DP_TRAIN_MAX_SWING_REACHED;
> +}
> +
> +static bool
> +sink_device_max_pre_emphasis_reached(struct sink_device *sink, int lane)
> +{
> +       return (sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] & DP_TRAIN_MAX_PRE_EMPHASIS_REACHED) ==
> +               DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
> +}
> +
> +static bool
> +sink_device_request_higher_voltage_swing(struct sink_device *sink)
> +{
> +       bool max_reached;
> +
> +       for (int lane = 0; lane < sink_device_lane_count(sink); lane++) {
> +               if (sink_device_max_voltage_reached(sink, lane)) {
> +                       max_reached = true;
> +                       break;
> +               }
> +       }
> +
> +       if (max_reached)
> +               return false;
> +
> +       for (int lane = 0; lane < sink_device_lane_count(sink); lane++) {
> +               sink->data.dpcd[DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1)] |=
> +                       (sink_device_get_voltage_swing(sink, lane) + 1) <<
> +                               (DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT * (lane & 1));
> +       }
> +
> +       return true;
> +}
> +
> +static bool
> +sink_device_request_higher_pre_emphasis(struct sink_device *sink)
> +{
> +       bool max_reached;
> +
> +       for (int lane = 0; lane < sink_device_lane_count(sink); lane++) {
> +               if (sink_device_max_pre_emphasis_reached(sink, lane)) {
> +                       max_reached = true;
> +                       break;
> +               }
> +       }
> +
> +       if (max_reached)
> +               return false;
> +
> +       for (int lane = 0; lane < sink_device_lane_count(sink); lane++) {
> +               sink->data.dpcd[DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1)] |=
> +                       (sink_device_get_pre_emphasis_level(sink, lane) + 1) <<
> +                               (DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT +
> +                                (DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT * (lane & 1)));
> +       }
> +
> +       return true;
> +}
> +
> +static void
> +sink_device_mark_cr_done(struct sink_device *sink)
> +{
> +       for (int lane = 0; lane < sink_device_lane_count(sink); lane++)
> +               sink->data.dpcd[DP_LANE0_1_STATUS + (lane >> 1)] |=
> +                       DP_LANE_CR_DONE << (4 * (lane & 1));
> +
> +       sink->data.cr_done = true;
> +}
> +
> +static void
> +sink_device_mark_channel_eq_done(struct sink_device *sink)
> +{
> +       for (int lane = 0; lane < sink_device_lane_count(sink); lane++) {
> +               uint8_t mask = (DP_LANE_CHANNEL_EQ_DONE | DP_LANE_SYMBOL_LOCKED);
> +               sink->data.dpcd[DP_LANE0_1_STATUS + (lane >> 1)] |=
> +                       mask << (4 * (lane & 1));
> +       }
> +
> +       sink->data.dpcd[DP_LANE_ALIGN_STATUS_UPDATED] |= DP_INTERLANE_ALIGN_DONE;
> +
> +       sink->data.channel_eq_done = true;
> +}
> +
> +static bool
> +sink_device_get_link_status(struct sink_device *sink,
> +                           uint8_t link_status[DP_LINK_STATUS_SIZE])
> +{
> +       if (!sink->data.cr_done) {
> +               if (!sink_device_request_higher_voltage_swing(sink))
> +                       sink_device_mark_cr_done(sink);
> +       } else if (!sink->data.channel_eq_done) {
> +               if (!sink_device_request_higher_pre_emphasis(sink))
> +                       sink_device_mark_channel_eq_done(sink);
> +       }
> +
> +       memcpy(link_status, sink->data.dpcd + DP_LANE0_1_STATUS,
> +              DP_LINK_STATUS_SIZE);
> +
> +       return true;
> +}
> +
> +static struct sink_device simple_sink = {
> +       .get_link_status = sink_device_get_link_status,
> +       .dpcd_write = sink_device_dpcd_write,
> +};
> +
> +/* Glue code */
> +
> +void intel_ddi_prepare_link_retrain(struct drm_encoder *encoder)
> +{
> +}
> +
> +void intel_dp_set_idle_link_train(struct intel_dp *intel_dp)
> +{
> +}
> +
> +bool intel_dp_source_supports_hbr2(struct drm_device *dev)
> +{
> +       return false;
> +}
> +
> +bool
> +intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_STATUS_SIZE])
> +{
> +       struct sink_device *sink = intel_dp->priv;
> +       return sink->get_link_status(sink, link_status);
> +}
> +
> +void
> +intel_dp_update_signal_levels(struct intel_dp *intel_dp)
> +{
> +}
> +
> +void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock,
> +                          uint8_t *link_bw, uint8_t *rate_select)
> +{
> +       *link_bw = intel_dp->link_bw;
> +       *rate_select = 0;
> +}
> +
> +void
> +intel_dp_program_link_training_pattern(struct intel_dp *intel_dp,
> +                                      uint8_t dp_train_pat)
> +{
> +}
> +
> +ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset,
> +                         void *buffer, size_t size)
> +{
> +       struct intel_dp *intel_dp =
> +               container_of(aux, struct intel_dp, aux);
> +       struct sink_device *sink = intel_dp->priv;
> +
> +       return sink->dpcd_write(sink, offset, buffer, size);
> +}
> +
> +/* --- */
> +
> +static struct intel_dp *
> +intel_dp_create(struct drm_device *dev, int lanes, uint8_t link_bw)
> +{
> +       struct intel_digital_port *dig_port;
> +
> +       dig_port = calloc(1, sizeof *dig_port);
> +       dig_port->base.base.dev = dev;
> +       dig_port->dp.lane_count = lanes;
> +       dig_port->dp.link_bw = link_bw;
> +
> +       return &dig_port->dp;
> +}
> +
> +static void
> +intel_dp_destroy(struct intel_dp *intel_dp)
> +{
> +       struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
> +       free(dig_port);
> +}
> +
> +/* FIXME: Yikes! */
> +#define BITS_PER_LONG 64
> +#include <include/linux/mod_devicetable.h>
> +#include <i915_pciids.h>
> +#include "intel_dev_info.c"
> +
> +static const struct pci_device_id pciidlist[] = {
> +       INTEL_PCI_IDS,
> +       {0, 0, 0}
> +};
> +
> +struct drm_device *
> +drm_device_for_pci_id(const struct pci_device_id *id)
> +{
> +       struct drm_device *dev;
> +       struct drm_i915_private *dev_priv;
> +
> +       dev = calloc(1, sizeof *dev);
> +       dev_priv = calloc(1, sizeof *dev_priv);
> +       assert(dev && dev_priv);
> +
> +       dev->dev_private = dev_priv;
> +
> +       memcpy(&dev_priv->info, (void *) id->driver_data, sizeof dev_priv->info);
> +       dev_priv->info.device_id = id->device;
> +
> +       /* TODO: set dev_priv->pch_type with an appropriate value */
> +       dev_priv->pch_type = PCH_NONE;
> +
> +       return dev;
> +}
> +
> +void
> +drm_device_destroy(struct drm_device *dev)
> +{
> +       free(dev->dev_private);
> +       free(dev);
> +}
> +
> +int
> +main(int argc, char *argv[])
> +{
> +       const struct pci_device_id *id;
> +
> +       for (id = &pciidlist[0];
> +            id->vendor != 0 && id->device != 0;
> +            id++) {
> +               struct drm_device *dev = drm_device_for_pci_id(id);
> +               struct intel_dp *intel_dp =
> +                       intel_dp_create(dev, 4, DP_LINK_BW_2_7);
> +
> +               if (IS_GEN2(dev) || IS_PINEVIEW(dev))
> +                       continue;
> +
> +               printf("Testing with device id %04x, gen %d\n",
> +                      INTEL_DEVID(dev), INTEL_INFO(dev)->gen);
> +
> +               intel_dp->priv = &simple_sink;
> +               memset(&simple_sink.data, 0, sizeof simple_sink.data);
> +               simple_sink.data.dpcd[DP_MAX_LINK_RATE] = 0x0A;
> +               simple_sink.data.dpcd[DP_MAX_LANE_COUNT] = 0x04;
> +
> +               intel_dp_start_link_train(intel_dp);
> +               intel_dp_stop_link_train(intel_dp);
> +
> +               for (int lane = 0; lane < intel_dp->lane_count; lane++)
> +                       printf("lane %i: vswing: %d, pre-emph: %d\n", lane,
> +                              sink_device_get_voltage_swing(&simple_sink, lane),
> +                              sink_device_get_pre_emphasis_level(&simple_sink, lane));
> +               printf("\n");
> +
> +               intel_dp_destroy(intel_dp);
> +               drm_device_destroy(dev);
> +       }
> +
> +       return 0;
> +}
> --
> 2.4.3
>
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/intel-gfx
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [PATCH] Add a link training test (v2)
  2015-09-09 10:33   ` Thomas Wood
@ 2015-09-11 14:11     ` Ander Conselvan de Oliveira
  2015-09-14 11:51       ` [PATCH] drm/i915: Add link training test Ander Conselvan de Oliveira
  0 siblings, 1 reply; 31+ messages in thread
From: Ander Conselvan de Oliveira @ 2015-09-11 14:11 UTC (permalink / raw)
  To: thomas.wood; +Cc: Ander Conselvan de Oliveira, intel-gfx

This adds a test that compiles the link training code from i915 into a
separate executable and uses it to train a fake sink device. The test
iterates over possible combinations of max voltage swing, pre emphasis
and bit rates, with the sink attemting to get the highest values
possible. The test doesn't cover HBR2, however.

Adding several stubs was necessary to get the code to compile. Stubs are
placed in the unit-test-helper/include subdirectory. Those headers are
replacement for the kernel headers, or they perform some stubbing and
then include the original header from the kernel src tree.

The stubs for building the drm_dp_helper are not there yet, so the few
functions necessary were copied to the test. I still haven't figured out
the best way to solve this issue.

v2: Proper autotools integration (Thomas)
    Move to ./tests and make it a proper igt test. (Thomas)
    Don't pull in device_info. (Ville)
---

On Wed, 2015-09-09 at 11:33 +0100, Thomas Wood wrote:
> On 8 September 2015 at 13:28, Ander Conselvan de Oliveira
> <ander.conselvan.de.oliveira@intel.com> wrote:
> > 

> > diff --git a/link-training-test/Makefile b/link-training-test/Makefile
> 
> If this is meant to be part of the test suite, then it needs to be in
> the tests directory and use the igt test infrastructure. Otherwise it
> should be placed in tools or tools/link-training-test.

I made the test use the igt infrastructure, but I'm not sure if this is
a good fit for it. The dependency on the kernel is on build time, but
once compiled this can be run on any machine. This can also introduce
build failures if the test is not kept in sync with the driver source.
Ideally that a failure to build this would be reported as the test
failing, but I have no idea of how to achieve that.


> > --- /dev/nul
> > +++ b/link-training-test/Makefile
> > @@ -0,0 +1,40 @@
> > +KERNEL_SRC_DIR=/home/aconselv/linux
> 
> It may be necassary to make the building of this test optional in
> configure and provide a way of specifying the kernel source directory.

I did the integration with autotools. It mostly works, but on the first
build make needs to called with -k, since a .Po file isn't created for
the source file copied from i915's tree. I still need to look into this
issue.

Thanks,
Ander



 Makefile.am                                      |   4 +
 configure.ac                                     |  20 +
 tests/Makefile.am                                |  14 +
 tests/Makefile.sources                           |  11 +
 tests/unit_dp_link_training.c                    | 577 +++++++++++++++++++++++
 unit-test-helper/Makefile.am                     |   1 +
 unit-test-helper/i915/Makefile.am                |   9 +
 unit-test-helper/include/build_magic.h           |  27 ++
 unit-test-helper/include/drm/drmP.h              |  24 +
 unit-test-helper/include/drm/drm_dp_helper.h     |  14 +
 unit-test-helper/include/drm/drm_dp_mst_helper.h |   7 +
 unit-test-helper/include/linux/delay.h           |   0
 12 files changed, 708 insertions(+)
 create mode 100644 tests/unit_dp_link_training.c
 create mode 100644 unit-test-helper/Makefile.am
 create mode 100644 unit-test-helper/i915/Makefile.am
 create mode 100644 unit-test-helper/include/build_magic.h
 create mode 100644 unit-test-helper/include/drm/drmP.h
 create mode 100644 unit-test-helper/include/drm/drm_dp_helper.h
 create mode 100644 unit-test-helper/include/drm/drm_dp_mst_helper.h
 create mode 100644 unit-test-helper/include/linux/delay.h

diff --git a/Makefile.am b/Makefile.am
index 4f71a3a..5ce9d58 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -31,6 +31,10 @@ if BUILD_SHADER_DEBUGGER
 SUBDIRS += debugger
 endif
 
+if HAVE_KERNEL_SRC_DIR
+SUBDIRS += unit-test-helper
+endif
+
 if BUILD_TESTS
 SUBDIRS += tests
 endif
diff --git a/configure.ac b/configure.ac
index 19f6fa4..53e1f6d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -199,6 +199,17 @@ if test "x$with_libunwind" = xyes; then
 			  AC_MSG_ERROR([libunwind not found. Use --without-libunwind to disable libunwind support.]))
 fi
 
+AC_ARG_WITH(kernel-src-dir,
+	    AS_HELP_STRING([--with-kernel-src-dir],
+			   [Kernel src directory, used by the link training test]),
+	    [KERNEL_SRC_DIR="$withval"], [KERNEL_SRC_DIR=""])
+if test -n "$KERNEL_SRC_DIR"; then
+	I915_SRC_DIR="${KERNEL_SRC_DIR}/drivers/gpu/drm/i915"
+	AC_SUBST([KERNEL_SRC_DIR])
+	AC_SUBST([I915_SRC_DIR])
+fi
+AM_CONDITIONAL([HAVE_KERNEL_SRC_DIR], test -n "$KERNEL_SRC_DIR")
+
 # enable debug symbols
 AC_ARG_ENABLE(debug,
 	      AS_HELP_STRING([--disable-debug],
@@ -264,10 +275,16 @@ AC_CONFIG_FILES([
 		 assembler/test/Makefile
 		 assembler/intel-gen4asm.pc
 		 overlay/Makefile
+		 unit-test-helper/Makefile
+		 unit-test-helper/i915/Makefile
 		 ])
 
 AC_CONFIG_FILES([tools/intel_aubdump], [chmod +x tools/intel_aubdump])
 
+if test -n "${KERNEL_SRC_DIR}"; then
+AC_CONFIG_LINKS([unit-test-helper/kernel-src:"${KERNEL_SRC_DIR}"])
+fi
+
 AC_OUTPUT
 
 # Print a summary of the compilation
@@ -287,6 +304,9 @@ echo "       Debugger           : ${enable_debugger}"
 echo "       Python dumper      : ${DUMPER}"
 echo "       Overlay            : X: ${enable_overlay_xlib}, Xv: ${enable_overlay_xvlib}"
 echo ""
+echo " • Variables:"
+echo "       Kernel src dir     : ${KERNEL_SRC_DIR}"
+echo ""
 echo " • API-Documentation      : ${enable_gtk_doc}"
 echo ""
 
diff --git a/tests/Makefile.am b/tests/Makefile.am
index dcac2f3..9602a67 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -4,6 +4,10 @@ if HAVE_NOUVEAU
     TESTS_progs_M += $(NOUVEAU_TESTS_M)
 endif
 
+if HAVE_KERNEL_SRC_DIR
+    TESTS_progs_M += $(UNIT_TESTS_M)
+endif
+
 if BUILD_TESTS
 test-list.txt: Makefile.sources
 	@echo TESTLIST > $@
@@ -53,6 +57,11 @@ LDADD = ../lib/libintel_tools.la $(PCIACCESS_LIBS) $(DRM_LIBS) $(LIBUNWIND_LIBS)
 LDADD += $(CAIRO_LIBS) $(LIBUDEV_LIBS) $(GLIB_LIBS) -lm
 AM_CFLAGS += $(CAIRO_CFLAGS) $(LIBUDEV_CFLAGS) $(GLIB_CFLAGS)
 
+UNIT_CFLAGS = \
+	-I$(top_srcdir)/unit-test-helper/include \
+	-iquote $(I915_SRC_DIR) \
+	-iquote $(top_builddir)
+
 drm_import_export_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
 drm_import_export_LDADD = $(LDADD) -lpthread
 gem_close_race_CFLAGS = $(AM_CFLAGS) $(THREAD_CFLAGS)
@@ -91,5 +100,10 @@ prime_nv_api_CFLAGS = $(AM_CFLAGS) $(DRM_NOUVEAU_CFLAGS)
 prime_nv_api_LDADD = $(LDADD) $(DRM_NOUVEAU_LIBS)
 prime_nv_pcopy_CFLAGS = $(AM_CFLAGS) $(DRM_NOUVEAU_CFLAGS)
 prime_nv_pcopy_LDADD = $(LDADD) $(DRM_NOUVEAU_LIBS)
+
+if HAVE_KERNEL_SRC_DIR
+unit_dp_link_training_CFLAGS = $(UNIT_CFLAGS) $(AM_CFLAGS)
+endif
+
 endif
 
diff --git a/tests/Makefile.sources b/tests/Makefile.sources
index ef69299..b33a4a9 100644
--- a/tests/Makefile.sources
+++ b/tests/Makefile.sources
@@ -185,6 +185,16 @@ TESTS_scripts = \
 	tools_test \
 	$(NULL)
 
+UNIT_TESTS_M = \
+	unit_dp_link_training
+
+if HAVE_KERNEL_SRC_DIR
+unit_dp_link_training_SOURCES = \
+	unit_dp_link_training.c \
+	$(top_builddir)/unit-test-helper/i915/intel_dp_link_training.c
+endif
+
+#
 # This target contains testcases which support automagic subtest enumeration
 # from the piglit testrunner with --list-subtests and running individual
 # subtests with --run-subtest <testname>
@@ -194,6 +204,7 @@ TESTS_scripts = \
 multi_kernel_tests = \
 	$(TESTS_progs_M) \
 	$(TESTS_scripts_M) \
+	$(UNIT_TESTS_progs_M) \
 	$(NULL)
 
 # This target is for simple testcase which don't expose any subtest.
diff --git a/tests/unit_dp_link_training.c b/tests/unit_dp_link_training.c
new file mode 100644
index 0000000..683b2bf
--- /dev/null
+++ b/tests/unit_dp_link_training.c
@@ -0,0 +1,577 @@
+/*
+ * Copyright © 2015 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Ander Conselvan de Oliveira <ander.conselvan.de.oliveira@intel.com>
+ *
+ */
+
+#include "igt_core.h"
+
+#include "unit-test-helper/kernel-src/drivers/gpu/drm/i915/intel_dp.h"
+
+
+/**
+ * ARRAY_SIZE:
+ * @arr: static array
+ *
+ * Macro to compute the size of the static array @arr.
+ */
+#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0]))
+
+
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#define container_of(ptr, type, member) ({			\
+	const typeof(((type *)0)->member) * __mptr = (ptr);	\
+	(type *)((char *)__mptr - offsetof(type, member)); })
+
+
+struct sink_device {
+	ssize_t (*dpcd_write)(struct sink_device *sink, unsigned int offset,
+			      void *buffer, size_t size);
+	bool (*get_link_status)(struct sink_device *sink,
+				uint8_t link_status[DP_LINK_STATUS_SIZE]);
+
+	struct {
+		bool lane_count_and_bw_set;
+		bool training_pattern_1_set;
+		bool started_with_non_zero_levels;
+		bool cr_done;
+		bool channel_eq_done;
+
+		uint8_t dpcd[0x3000];
+	} data;
+};
+
+/* Fake sink device implementation */
+
+static uint8_t
+sink_device_lane_count(struct sink_device *sink)
+{
+	return sink->data.dpcd[DP_LANE_COUNT_SET];
+}
+
+static uint8_t
+sink_device_get_training_pattern(struct sink_device *sink)
+{
+	return sink->data.dpcd[DP_TRAINING_PATTERN_SET] & DP_TRAINING_PATTERN_MASK;
+}
+
+static uint8_t
+sink_device_get_voltage_swing(struct sink_device *sink, int lane)
+{
+	return sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] &
+		DP_TRAIN_VOLTAGE_SWING_MASK;
+}
+
+static uint8_t
+sink_device_get_pre_emphasis_level(struct sink_device *sink, int lane)
+{
+	return (sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] &
+		 DP_TRAIN_PRE_EMPHASIS_MASK) >> DP_TRAIN_PRE_EMPHASIS_SHIFT;
+}
+
+static void
+sink_device_check_lane_count_and_bw(struct sink_device *sink)
+{
+	if (sink->data.lane_count_and_bw_set)
+		return;
+
+	igt_assert(sink->data.dpcd[DP_TRAINING_PATTERN_SET] == 0);
+
+	if (sink->data.dpcd[DP_LINK_BW_SET] != 0 &&
+	    sink->data.dpcd[DP_LANE_COUNT_SET] != 0)
+		sink->data.lane_count_and_bw_set = true;
+}
+
+static void
+sink_device_check_pattern_1_set(struct sink_device *sink)
+{
+	if (!sink->data.lane_count_and_bw_set ||
+	    sink->data.training_pattern_1_set)
+		return;
+
+	igt_assert(sink_device_get_training_pattern(sink) <= DP_TRAINING_PATTERN_1);
+
+	if (sink_device_get_training_pattern(sink) != DP_TRAINING_PATTERN_1)
+		return;
+
+	igt_assert(sink->data.dpcd[DP_LINK_BW_SET] == DP_LINK_BW_1_62 ||
+	       sink->data.dpcd[DP_LINK_BW_SET] == DP_LINK_BW_2_7);
+
+	igt_assert(sink->data.dpcd[DP_LANE_COUNT_SET] == 1 ||
+		   sink->data.dpcd[DP_LANE_COUNT_SET] == 2 ||
+		   sink->data.dpcd[DP_LANE_COUNT_SET] == 4);
+
+	for (int lane = 0; lane < sink_device_lane_count(sink); lane++) {
+		if (sink_device_get_voltage_swing(sink, lane) != DP_TRAIN_VOLTAGE_SWING_LEVEL_0 ||
+		    sink_device_get_pre_emphasis_level(sink, lane) != DP_TRAIN_PRE_EMPH_LEVEL_0)
+			sink->data.started_with_non_zero_levels = true;
+	}
+
+	sink->data.training_pattern_1_set = true;
+}
+
+static void
+sink_device_check_pattern_2_set(struct sink_device *sink)
+{
+	if (!sink->data.cr_done)
+		return;
+
+	igt_assert(sink_device_get_training_pattern(sink) == DP_TRAINING_PATTERN_2);
+}
+
+static void
+sink_device_check_pattern_disable(struct sink_device *sink)
+{
+	if (!sink->data.cr_done || ! sink->data.channel_eq_done)
+		return;
+
+	igt_assert(sink_device_get_training_pattern(sink) == DP_TRAINING_PATTERN_DISABLE);
+}
+
+static ssize_t
+sink_device_dpcd_write(struct sink_device *sink, unsigned int offset,
+		       void *buffer, size_t size)
+{
+	memcpy(sink->data.dpcd + offset, buffer, size);
+
+	sink_device_check_lane_count_and_bw(sink);
+
+	if (!sink->data.cr_done)
+		sink_device_check_pattern_1_set(sink);
+	else if (!sink->data.channel_eq_done)
+		sink_device_check_pattern_2_set(sink);
+	else
+		sink_device_check_pattern_disable(sink);
+
+	return size;
+}
+
+static bool
+sink_device_max_voltage_reached(struct sink_device *sink, int lane)
+{
+	return (sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] & DP_TRAIN_MAX_SWING_REACHED) ==
+		DP_TRAIN_MAX_SWING_REACHED;
+}
+
+static bool
+sink_device_max_pre_emphasis_reached(struct sink_device *sink, int lane)
+{
+	return (sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] & DP_TRAIN_MAX_PRE_EMPHASIS_REACHED) ==
+		DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
+}
+
+static void
+sink_device_set_adjust_voltage(struct sink_device *sink,
+			       int lane, uint8_t level)
+{
+	int shift = DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT * (lane & 1);
+
+	sink->data.dpcd[DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1)] &=
+		~(DP_ADJUST_VOLTAGE_SWING_LANE0_MASK << shift);
+	sink->data.dpcd[DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1)] |=
+		level << shift;
+}
+
+static void
+sink_device_set_adjust_pre_emphasis(struct sink_device *sink,
+				    int lane, uint8_t level)
+{
+	int shift = DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT * (lane & 1);
+
+	sink->data.dpcd[DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1)] &=
+		~(DP_ADJUST_PRE_EMPHASIS_LANE0_MASK << shift);
+	sink->data.dpcd[DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1)] |=
+		level << (DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT + shift);
+}
+
+static bool
+sink_device_request_higher_voltage_swing(struct sink_device *sink)
+{
+	bool max_reached = false;
+
+	for (int lane = 0; lane < sink_device_lane_count(sink); lane++) {
+		if (sink_device_max_voltage_reached(sink, lane)) {
+			max_reached = true;
+			break;
+		}
+	}
+
+	if (max_reached)
+		return false;
+
+	for (int lane = 0; lane < sink_device_lane_count(sink); lane++) {
+		uint8_t new_voltage =
+			sink_device_get_voltage_swing(sink, lane) + 1;
+
+		sink_device_set_adjust_voltage(sink, lane, new_voltage);
+	}
+
+	return true;
+}
+
+static bool
+sink_device_request_higher_pre_emphasis(struct sink_device *sink)
+{
+	bool max_reached = false;
+
+	for (int lane = 0; lane < sink_device_lane_count(sink); lane++) {
+		if (sink_device_max_pre_emphasis_reached(sink, lane)) {
+			max_reached = true;
+			break;
+		}
+	}
+
+	if (max_reached)
+		return false;
+
+	for (int lane = 0; lane < sink_device_lane_count(sink); lane++) {
+		uint8_t new_pre_emphasis =
+			sink_device_get_pre_emphasis_level(sink, lane) + 1;
+
+		sink_device_set_adjust_pre_emphasis(sink, lane, new_pre_emphasis);
+	}
+
+	return true;
+}
+
+static void
+sink_device_mark_cr_done(struct sink_device *sink)
+{
+	for (int lane = 0; lane < sink_device_lane_count(sink); lane++)
+		sink->data.dpcd[DP_LANE0_1_STATUS + (lane >> 1)] |=
+			DP_LANE_CR_DONE << (4 * (lane & 1));
+
+	sink->data.cr_done = true;
+}
+
+static void
+sink_device_mark_channel_eq_done(struct sink_device *sink)
+{
+	for (int lane = 0; lane < sink_device_lane_count(sink); lane++) {
+		uint8_t mask = (DP_LANE_CHANNEL_EQ_DONE | DP_LANE_SYMBOL_LOCKED);
+		sink->data.dpcd[DP_LANE0_1_STATUS + (lane >> 1)] |=
+			mask << (4 * (lane & 1));
+	}
+
+	sink->data.dpcd[DP_LANE_ALIGN_STATUS_UPDATED] |= DP_INTERLANE_ALIGN_DONE;
+
+	sink->data.channel_eq_done = true;
+}
+
+static bool
+sink_device_get_link_status(struct sink_device *sink,
+			    uint8_t link_status[DP_LINK_STATUS_SIZE])
+{
+	if (!sink->data.cr_done) {
+		if (!sink_device_request_higher_voltage_swing(sink))
+			sink_device_mark_cr_done(sink);
+	} else if (!sink->data.channel_eq_done) {
+		if (!sink_device_request_higher_pre_emphasis(sink))
+			sink_device_mark_channel_eq_done(sink);
+	}
+
+	memcpy(link_status, sink->data.dpcd + DP_LANE0_1_STATUS,
+	       DP_LINK_STATUS_SIZE);
+
+	return true;
+}
+
+static void
+sink_device_reset(struct sink_device *sink, int lanes, uint8_t link_bw)
+{
+	memset(&sink->data, 0, sizeof sink->data);
+	sink->data.dpcd[DP_MAX_LINK_RATE] = link_bw;
+	sink->data.dpcd[DP_MAX_LANE_COUNT] = lanes;
+}
+
+static struct sink_device simple_sink = {
+	.get_link_status = sink_device_get_link_status,
+	.dpcd_write = sink_device_dpcd_write,
+};
+
+/* Glue code */
+
+struct test_intel_dp {
+	struct intel_dp dp;
+	struct sink_device *sink;
+	uint8_t link_bw;
+
+	uint8_t max_voltage;
+	uint8_t max_pre_emphasis;
+};
+
+static struct test_intel_dp *
+to_test_intel_dp(struct intel_dp *dp)
+{
+	return container_of(dp, struct test_intel_dp, dp);
+}
+
+void intel_dp_set_idle_link_train(struct intel_dp *intel_dp)
+{
+}
+
+bool intel_dp_source_supports_hbr2(struct intel_dp *intel_dp)
+{
+	return false;
+}
+
+bool
+intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_STATUS_SIZE])
+{
+	struct sink_device *sink = to_test_intel_dp(intel_dp)->sink;
+	return sink->get_link_status(sink, link_status);
+}
+
+void
+intel_dp_update_signal_levels(struct intel_dp *intel_dp)
+{
+}
+
+void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock,
+			   uint8_t *link_bw, uint8_t *rate_select)
+{
+	*link_bw = to_test_intel_dp(intel_dp)->link_bw;
+	*rate_select = 0;
+}
+
+void
+intel_dp_program_link_training_pattern(struct intel_dp *intel_dp,
+				       uint8_t dp_train_pat)
+{
+}
+
+uint8_t
+intel_dp_voltage_max(struct intel_dp *intel_dp)
+{
+	return to_test_intel_dp(intel_dp)->max_voltage <<
+		DP_TRAIN_VOLTAGE_SWING_SHIFT;
+}
+
+uint8_t
+intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t voltage_swing)
+{
+	return to_test_intel_dp(intel_dp)->max_pre_emphasis <<
+		DP_TRAIN_PRE_EMPHASIS_SHIFT;
+}
+
+ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset,
+			  void *buffer, size_t size)
+{
+	struct intel_dp *intel_dp =
+		container_of(aux, struct intel_dp, aux);
+	struct sink_device *sink = to_test_intel_dp(intel_dp)->sink;
+
+	return sink->dpcd_write(sink, offset, buffer, size);
+}
+
+/* --- */
+
+static struct test_intel_dp test_dp;
+
+static void
+do_test(struct sink_device *sink, int lanes, uint8_t link_bw,
+	     uint8_t max_voltage, uint8_t max_pre_emphasis)
+{
+	memset(&test_dp, 0, sizeof test_dp);
+	test_dp.dp.lane_count = lanes;
+	test_dp.link_bw = link_bw;
+	test_dp.sink = sink;
+	test_dp.max_voltage =
+		max_voltage >> DP_TRAIN_VOLTAGE_SWING_SHIFT;
+	test_dp.max_pre_emphasis =
+		max_pre_emphasis >> DP_TRAIN_PRE_EMPHASIS_SHIFT;
+
+	sink_device_reset(sink, lanes, link_bw);
+
+	intel_dp_start_link_train(&test_dp.dp);
+	intel_dp_stop_link_train(&test_dp.dp);
+
+	igt_assert(sink->data.cr_done);
+	igt_assert(sink->data.channel_eq_done);
+
+	for (int lane = 0; lane < test_dp.dp.lane_count; lane++) {
+		uint8_t cur_v = sink_device_get_voltage_swing(sink, lane);
+		uint8_t cur_p = sink_device_get_pre_emphasis_level(sink, lane);
+
+		igt_assert_eq(cur_v, test_dp.max_voltage);
+		igt_assert_eq(cur_p, test_dp.max_pre_emphasis);
+	}
+}
+
+int test_lanes[] = {
+	1, 2, 4,
+};
+
+uint8_t test_bw[] = {
+	DP_LINK_BW_1_62,
+	DP_LINK_BW_2_7,
+};
+
+uint8_t test_max_voltage[] = {
+	DP_TRAIN_VOLTAGE_SWING_LEVEL_0,
+	DP_TRAIN_VOLTAGE_SWING_LEVEL_1,
+	DP_TRAIN_VOLTAGE_SWING_LEVEL_2,
+	DP_TRAIN_VOLTAGE_SWING_LEVEL_3,
+};
+
+uint8_t test_max_pre_emphasis[] = {
+	DP_TRAIN_PRE_EMPH_LEVEL_0,
+	DP_TRAIN_PRE_EMPH_LEVEL_1,
+	DP_TRAIN_PRE_EMPH_LEVEL_2,
+	DP_TRAIN_PRE_EMPH_LEVEL_3,
+};
+
+igt_main
+{
+	for (int lane = 0; lane < ARRAY_SIZE(test_lanes); lane++)
+	for (int bw = 0; bw < ARRAY_SIZE(test_bw); bw++)
+	for (int voltage = 0; voltage < ARRAY_SIZE(test_max_voltage); voltage++)
+	for (int emph = 0; emph < ARRAY_SIZE(test_max_pre_emphasis); emph++)
+		igt_subtest_f("l%d-bw%d-v%d-pe%d",
+			      test_lanes[lane],
+			      test_bw[bw],
+			      test_max_voltage[voltage],
+			      test_max_pre_emphasis[emph])
+			do_test(&simple_sink,
+				test_lanes[lane],
+				test_bw[bw],
+				test_max_voltage[voltage],
+				test_max_pre_emphasis[emph]);
+}
+
+/* TODO: Get rid of this copy of drm_dp_helper functions. */
+
+/*
+ * Copyright © 2009 Keith Packard
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that copyright
+ * notice and this permission notice appear in supporting documentation, and
+ * that the name of the copyright holders not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission.  The copyright holders make no representations
+ * about the suitability of this software for any purpose.  It is provided "as
+ * is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+ * OF THIS SOFTWARE.
+ */
+
+#include <drm/drm_dp_helper.h>
+
+static u8 dp_link_status(const u8 link_status[DP_LINK_STATUS_SIZE], int r)
+{
+        return link_status[r - DP_LANE0_1_STATUS];
+}
+
+static u8 dp_get_lane_status(const u8 link_status[DP_LINK_STATUS_SIZE],
+                             int lane)
+{
+        int i = DP_LANE0_1_STATUS + (lane >> 1);
+        int s = (lane & 1) * 4;
+        u8 l = dp_link_status(link_status, i);
+        return (l >> s) & 0xf;
+}
+
+bool drm_dp_channel_eq_ok(const u8 link_status[DP_LINK_STATUS_SIZE],
+                          int lane_count)
+{
+        u8 lane_align;
+        u8 lane_status;
+        int lane;
+
+        lane_align = dp_link_status(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 = dp_get_lane_status(link_status, lane);
+                if ((lane_status & DP_CHANNEL_EQ_BITS) != DP_CHANNEL_EQ_BITS)
+                        return false;
+        }
+        return true;
+}
+
+bool drm_dp_clock_recovery_ok(const u8 link_status[DP_LINK_STATUS_SIZE],
+                              int lane_count)
+{
+        int lane;
+        u8 lane_status;
+
+        for (lane = 0; lane < lane_count; lane++) {
+                lane_status = dp_get_lane_status(link_status, lane);
+                if ((lane_status & DP_LANE_CR_DONE) == 0)
+                        return false;
+        }
+        return true;
+}
+
+u8 drm_dp_get_adjust_request_voltage(const u8 link_status[DP_LINK_STATUS_SIZE],
+                                     int lane)
+{
+        int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
+        int s = ((lane & 1) ?
+                 DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT :
+                 DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT);
+        u8 l = dp_link_status(link_status, i);
+
+        return ((l >> s) & 0x3) << DP_TRAIN_VOLTAGE_SWING_SHIFT;
+}
+
+u8 drm_dp_get_adjust_request_pre_emphasis(const u8 link_status[DP_LINK_STATUS_SIZE],
+                                          int lane)
+{
+        int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
+        int s = ((lane & 1) ?
+                 DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT :
+                 DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT);
+        u8 l = dp_link_status(link_status, i);
+
+        return ((l >> s) & 0x3) << DP_TRAIN_PRE_EMPHASIS_SHIFT;
+}
+
+/* FIXME: */
+static void udelay(unsigned long usecs) {}
+static void mdelay(unsigned long msecs) {}
+
+void drm_dp_link_train_clock_recovery_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) {
+        if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] == 0)
+                udelay(100);
+        else
+                mdelay(dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4);
+}
+
+void drm_dp_link_train_channel_eq_delay(const u8 dpcd[DP_RECEIVER_CAP_SIZE]) {
+        if (dpcd[DP_TRAINING_AUX_RD_INTERVAL] == 0)
+                udelay(400);
+        else
+                mdelay(dpcd[DP_TRAINING_AUX_RD_INTERVAL] * 4);
+}
+
diff --git a/unit-test-helper/Makefile.am b/unit-test-helper/Makefile.am
new file mode 100644
index 0000000..cf54f39
--- /dev/null
+++ b/unit-test-helper/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = i915
diff --git a/unit-test-helper/i915/Makefile.am b/unit-test-helper/i915/Makefile.am
new file mode 100644
index 0000000..cc14269
--- /dev/null
+++ b/unit-test-helper/i915/Makefile.am
@@ -0,0 +1,9 @@
+if HAVE_KERNEL_SRC_DIR
+
+BUILT_SOURCES = \
+	intel_dp_link_training.c
+
+%.c: $(I915_SRC_DIR)/%.c
+	cp $< $@
+
+endif
diff --git a/unit-test-helper/include/build_magic.h b/unit-test-helper/include/build_magic.h
new file mode 100644
index 0000000..a4a3836
--- /dev/null
+++ b/unit-test-helper/include/build_magic.h
@@ -0,0 +1,27 @@
+#ifndef _IGT_BUILD_MAGIC_H
+#define _IGT_BUILD_MAGIC_H
+
+#ifndef __KERNEL__
+#define __KERNEL__
+#endif
+
+#include "unit-test-helper/kernel-src/tools/include/linux/compiler.h"
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint32_t u32;
+typedef uint64_t u64;
+typedef unsigned long size_t;
+typedef long ssize_t;
+
+struct delayed_work {
+};
+
+struct notifier_block {
+};
+
+#endif /* _IGT_BUILD_MAGIC_H */
diff --git a/unit-test-helper/include/drm/drmP.h b/unit-test-helper/include/drm/drmP.h
new file mode 100644
index 0000000..fe48518
--- /dev/null
+++ b/unit-test-helper/include/drm/drmP.h
@@ -0,0 +1,24 @@
+#ifndef _IGT_DRM_P_H_
+#define _IGT_DRM_P_H_
+
+#include "igt_core.h"
+
+struct drm_device {
+};
+
+struct drm_connector {
+};
+
+struct drm_encoder {
+};
+
+struct drm_crtc {
+};
+
+struct drm_plane {
+};
+
+#define DRM_ERROR(x) igt_warn(x)
+#define DRM_DEBUG_KMS(x) igt_debug(x)
+
+#endif /* _IGT_DRM_P_H_ */
diff --git a/unit-test-helper/include/drm/drm_dp_helper.h b/unit-test-helper/include/drm/drm_dp_helper.h
new file mode 100644
index 0000000..54f297e
--- /dev/null
+++ b/unit-test-helper/include/drm/drm_dp_helper.h
@@ -0,0 +1,14 @@
+#ifndef _IGT_DRM_DP_HELPER_H_
+#define _IGT_DRM_DP_HELPER_H_
+
+#include <build_magic.h>
+
+struct i2c_adapter {
+};
+
+struct mutex {
+};
+
+#include "unit-test-helper/kernel-src/include/drm/drm_dp_helper.h"
+
+#endif /* _IGT_DRM_DP_HELPER_H_ */
diff --git a/unit-test-helper/include/drm/drm_dp_mst_helper.h b/unit-test-helper/include/drm/drm_dp_mst_helper.h
new file mode 100644
index 0000000..ac9d362
--- /dev/null
+++ b/unit-test-helper/include/drm/drm_dp_mst_helper.h
@@ -0,0 +1,7 @@
+#ifndef _IGT_DRM_DP_MST_HELPER_H_
+#define _IGT_DRM_DP_MST_HELPER_H_
+
+struct drm_dp_mst_topology_mgr {
+};
+
+#endif /* _IGT_DRM_DP_MST_HELPER_H_ */
diff --git a/unit-test-helper/include/linux/delay.h b/unit-test-helper/include/linux/delay.h
new file mode 100644
index 0000000..e69de29
-- 
2.4.3

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH] drm/i915: Add link training test
  2015-09-11 14:11     ` [PATCH] Add a link training test (v2) Ander Conselvan de Oliveira
@ 2015-09-14 11:51       ` Ander Conselvan de Oliveira
  2015-09-14 13:11         ` Daniel Vetter
  0 siblings, 1 reply; 31+ messages in thread
From: Ander Conselvan de Oliveira @ 2015-09-14 11:51 UTC (permalink / raw)
  To: intel-gfx; +Cc: Ander Conselvan de Oliveira, thomas.wood

---

On Fri, 2015-09-11 at 17:11 +0300, Ander Conselvan de Oliveira wrote:
> On Wed, 2015-09-09 at 11:33 +0100, Thomas Wood wrote:
> > On 8 September 2015 at 13:28, Ander Conselvan de Oliveira
> > <ander.conselvan.de.oliveira@intel.com> wrote:
> > > 
> 
> > > diff --git a/link-training-test/Makefile b/link-training-test/Makefile
> > 
> > If this is meant to be part of the test suite, then it needs to be in
> > the tests directory and use the igt test infrastructure. Otherwise it
> > should be placed in tools or tools/link-training-test.
> 
> I made the test use the igt infrastructure, but I'm not sure if this is
> a good fit for it. The dependency on the kernel is on build time, but
> once compiled this can be run on any machine. This can also introduce
> build failures if the test is not kept in sync with the driver source.
> Ideally that a failure to build this would be reported as the test
> failing, but I have no idea of how to achieve that.

Alternatively, this could be in the kernel source tree directly. This
patch adds a test subdir to the i915 source dir, containing the link
training test. The test is compiled as part of the normal build using
the extra-y variable so that it doesn't get linked to the final kernel.

When make is run from the tests directory, a thin wrapper around the
tests is built and linked to the object file compiled as part of the
kernel build. Running make run_tests from the test dir runs the test
and reports success or failure.

Any thoughts?

Ander

---
 drivers/gpu/drm/i915/Makefile                      |   2 +
 drivers/gpu/drm/i915/tests/Makefile                |  39 ++
 drivers/gpu/drm/i915/tests/empty.c                 |   0
 drivers/gpu/drm/i915/tests/loader.c                | 108 +++++
 .../drm/i915/tests/test_intel_dp_link_training.c   | 464 +++++++++++++++++++++
 5 files changed, 613 insertions(+)
 create mode 100644 drivers/gpu/drm/i915/tests/Makefile
 create mode 100644 drivers/gpu/drm/i915/tests/empty.c
 create mode 100644 drivers/gpu/drm/i915/tests/loader.c
 create mode 100644 drivers/gpu/drm/i915/tests/test_intel_dp_link_training.c

diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
index 0851de07..7298ce5 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -97,6 +97,8 @@ i915-y += i915_vgpu.o
 # legacy horrors
 i915-y += i915_dma.o
 
+obj-y += tests/
+
 obj-$(CONFIG_DRM_I915)  += i915.o
 
 CFLAGS_i915_trace_points.o := -I$(src)
diff --git a/drivers/gpu/drm/i915/tests/Makefile b/drivers/gpu/drm/i915/tests/Makefile
new file mode 100644
index 0000000..3062741
--- /dev/null
+++ b/drivers/gpu/drm/i915/tests/Makefile
@@ -0,0 +1,39 @@
+# Please kbuild
+# TODO: check for a better way to include this directory in the build
+# without forcing the need for this empty file
+obj-y += empty.o
+
+
+# FIXME: The stack protector code causes tests to crash due to an improperly
+# initialized %gs, so keep it disabled for now.
+ccflags-y := -fstack-protector-explicit
+
+extra-y += test_intel_dp_link_training.o
+
+# If make is run from this directory
+ifeq (0, $(MAKELEVEL))
+
+# For strlcpy
+LDFLAGS = -lbsd
+
+TEST_PROGS = \
+	test_intel_dp_link_training
+
+all: tests
+
+tests: $(TEST_PROGS)
+
+run_tests: tests
+	@for TEST in $(TEST_PROGS); do \
+		(./$$TEST && echo "$$TEST [PASS]") || echo "$$TEST [FAIL]"; \
+	done
+
+test_%.o: test_%.c ../%.c
+	make -C ../../../../../ drivers/gpu/drm/i915/tests/$@
+
+loader.o: loader.c
+	$(CC) $(CFLAGS) -c -o $@ $<
+
+%: %.o loader.o
+	$(CC) -o $@ $^ $(LDFLAGS)
+endif
diff --git a/drivers/gpu/drm/i915/tests/empty.c b/drivers/gpu/drm/i915/tests/empty.c
new file mode 100644
index 0000000..e69de29
diff --git a/drivers/gpu/drm/i915/tests/loader.c b/drivers/gpu/drm/i915/tests/loader.c
new file mode 100644
index 0000000..9e38816
--- /dev/null
+++ b/drivers/gpu/drm/i915/tests/loader.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright © 2015 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Ander Conselvan de Oliveira <ander.conselvan.de.oliveira@intel.com>
+ *
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+void run_test(void);
+
+void assert(bool condition)
+{
+	if (!condition) {
+		printf("Assert failed\n");
+		abort();
+	}
+}
+
+struct {
+	uint8_t padding[32];
+} param_ops_int;
+
+unsigned int drm_debug = 0;	/* 1 to enable debug output */
+
+void drm_err(const char *format, ...)
+{
+	va_list args;
+
+	va_start(args, format);
+	vprintf(format, args);
+	va_end(args);
+
+}
+
+void drm_ut_debug_printk(const char *function_name, const char *format, ...)
+{
+	va_list args;
+
+	va_start(args, format);
+	printf("[%s] ", function_name);
+	vprintf(format, args);
+	va_end(args);
+}
+
+void __const_udelay(unsigned long xloops)
+{
+}
+
+struct mutex;
+void mutex_lock_nested(struct mutex *lock, unsigned int subclass)
+{
+}
+
+void mutex_unlock(struct mutex *lock)
+{
+}
+
+struct lock_class_key;
+void
+__mutex_init(struct mutex *lock, const char *name, struct lock_class_key *key)
+{
+}
+
+void usleep_range(unsigned int min, unsigned int max)
+{
+}
+
+struct i2c_adapter;
+int i2c_add_adapter(struct i2c_adapter *adapter)
+{
+	return 0;
+}
+
+void i2c_del_adapter(struct i2c_adapter *adapater)
+{
+}
+
+int
+main(int argc, char *argv[])
+{
+	run_test();
+	return 0;
+}
diff --git a/drivers/gpu/drm/i915/tests/test_intel_dp_link_training.c b/drivers/gpu/drm/i915/tests/test_intel_dp_link_training.c
new file mode 100644
index 0000000..11868f8
--- /dev/null
+++ b/drivers/gpu/drm/i915/tests/test_intel_dp_link_training.c
@@ -0,0 +1,464 @@
+/*
+ * Copyright © 2015 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Ander Conselvan de Oliveira <ander.conselvan.de.oliveira@intel.com>
+ *
+ */
+
+#include "../intel_dp_link_training.c"
+
+#define drm_dp_dpcd_write	real_drm_dp_dpcd_write
+#include "../../drm_dp_helper.c"
+#undef drm_dp_dpcd_write
+
+void assert(bool condition);
+
+
+struct sink_device {
+	ssize_t (*dpcd_write)(struct sink_device *sink, unsigned int offset,
+			      void *buffer, size_t size);
+	bool (*get_link_status)(struct sink_device *sink,
+				uint8_t link_status[DP_LINK_STATUS_SIZE]);
+
+	struct {
+		bool lane_count_and_bw_set;
+		bool training_pattern_1_set;
+		bool started_with_non_zero_levels;
+		bool cr_done;
+		bool channel_eq_done;
+
+		uint8_t dpcd[0x3000];
+	} data;
+};
+
+/* Fake sink device implementation */
+
+static uint8_t
+sink_device_lane_count(struct sink_device *sink)
+{
+	return sink->data.dpcd[DP_LANE_COUNT_SET];
+}
+
+static uint8_t
+sink_device_get_training_pattern(struct sink_device *sink)
+{
+	return sink->data.dpcd[DP_TRAINING_PATTERN_SET] & DP_TRAINING_PATTERN_MASK;
+}
+
+static uint8_t
+sink_device_get_voltage_swing(struct sink_device *sink, int lane)
+{
+	return sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] &
+		DP_TRAIN_VOLTAGE_SWING_MASK;
+}
+
+static uint8_t
+sink_device_get_pre_emphasis_level(struct sink_device *sink, int lane)
+{
+	return (sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] &
+		 DP_TRAIN_PRE_EMPHASIS_MASK) >> DP_TRAIN_PRE_EMPHASIS_SHIFT;
+}
+
+static void
+sink_device_check_lane_count_and_bw(struct sink_device *sink)
+{
+	if (sink->data.lane_count_and_bw_set)
+		return;
+
+	assert(sink->data.dpcd[DP_TRAINING_PATTERN_SET] == 0);
+
+	if (sink->data.dpcd[DP_LINK_BW_SET] != 0 &&
+	    sink->data.dpcd[DP_LANE_COUNT_SET] != 0)
+		sink->data.lane_count_and_bw_set = true;
+}
+
+static void
+sink_device_check_pattern_1_set(struct sink_device *sink)
+{
+	int lane;
+
+	if (!sink->data.lane_count_and_bw_set ||
+	    sink->data.training_pattern_1_set)
+		return;
+
+	assert(sink_device_get_training_pattern(sink) <= DP_TRAINING_PATTERN_1);
+
+	if (sink_device_get_training_pattern(sink) != DP_TRAINING_PATTERN_1)
+		return;
+
+	assert(sink->data.dpcd[DP_LINK_BW_SET] == DP_LINK_BW_1_62 ||
+	       sink->data.dpcd[DP_LINK_BW_SET] == DP_LINK_BW_2_7);
+
+	assert(sink->data.dpcd[DP_LANE_COUNT_SET] == 1 ||
+		   sink->data.dpcd[DP_LANE_COUNT_SET] == 2 ||
+		   sink->data.dpcd[DP_LANE_COUNT_SET] == 4);
+
+	for (lane = 0; lane < sink_device_lane_count(sink); lane++) {
+		if (sink_device_get_voltage_swing(sink, lane) != DP_TRAIN_VOLTAGE_SWING_LEVEL_0 ||
+		    sink_device_get_pre_emphasis_level(sink, lane) != DP_TRAIN_PRE_EMPH_LEVEL_0)
+			sink->data.started_with_non_zero_levels = true;
+	}
+
+	sink->data.training_pattern_1_set = true;
+}
+
+static void
+sink_device_check_pattern_2_set(struct sink_device *sink)
+{
+	if (!sink->data.cr_done)
+		return;
+
+	assert(sink_device_get_training_pattern(sink) == DP_TRAINING_PATTERN_2);
+}
+
+static void
+sink_device_check_pattern_disable(struct sink_device *sink)
+{
+	if (!sink->data.cr_done || ! sink->data.channel_eq_done)
+		return;
+
+	assert(sink_device_get_training_pattern(sink) == DP_TRAINING_PATTERN_DISABLE);
+}
+
+static ssize_t
+sink_device_dpcd_write(struct sink_device *sink, unsigned int offset,
+		       void *buffer, size_t size)
+{
+	memcpy(sink->data.dpcd + offset, buffer, size);
+
+	sink_device_check_lane_count_and_bw(sink);
+
+	if (!sink->data.cr_done)
+		sink_device_check_pattern_1_set(sink);
+	else if (!sink->data.channel_eq_done)
+		sink_device_check_pattern_2_set(sink);
+	else
+		sink_device_check_pattern_disable(sink);
+
+	return size;
+}
+
+static bool
+sink_device_max_voltage_reached(struct sink_device *sink, int lane)
+{
+	return (sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] & DP_TRAIN_MAX_SWING_REACHED) ==
+		DP_TRAIN_MAX_SWING_REACHED;
+}
+
+static bool
+sink_device_max_pre_emphasis_reached(struct sink_device *sink, int lane)
+{
+	return (sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] & DP_TRAIN_MAX_PRE_EMPHASIS_REACHED) ==
+		DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
+}
+
+static void
+sink_device_set_adjust_voltage(struct sink_device *sink,
+			       int lane, uint8_t level)
+{
+	int shift = DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT * (lane & 1);
+
+	sink->data.dpcd[DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1)] &=
+		~(DP_ADJUST_VOLTAGE_SWING_LANE0_MASK << shift);
+	sink->data.dpcd[DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1)] |=
+		level << shift;
+}
+
+static void
+sink_device_set_adjust_pre_emphasis(struct sink_device *sink,
+				    int lane, uint8_t level)
+{
+	int shift = DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT * (lane & 1);
+
+	sink->data.dpcd[DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1)] &=
+		~(DP_ADJUST_PRE_EMPHASIS_LANE0_MASK << shift);
+	sink->data.dpcd[DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1)] |=
+		level << (DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT + shift);
+}
+
+static bool
+sink_device_request_higher_voltage_swing(struct sink_device *sink)
+{
+	bool max_reached = false;
+	int lane;
+
+	for (lane = 0; lane < sink_device_lane_count(sink); lane++) {
+		if (sink_device_max_voltage_reached(sink, lane)) {
+			max_reached = true;
+			break;
+		}
+	}
+
+	if (max_reached)
+		return false;
+
+	for (lane = 0; lane < sink_device_lane_count(sink); lane++) {
+		uint8_t new_voltage =
+			sink_device_get_voltage_swing(sink, lane) + 1;
+
+		sink_device_set_adjust_voltage(sink, lane, new_voltage);
+	}
+
+	return true;
+}
+
+static bool
+sink_device_request_higher_pre_emphasis(struct sink_device *sink)
+{
+	bool max_reached = false;
+	int lane;
+
+	for (lane = 0; lane < sink_device_lane_count(sink); lane++) {
+		if (sink_device_max_pre_emphasis_reached(sink, lane)) {
+			max_reached = true;
+			break;
+		}
+	}
+
+	if (max_reached)
+		return false;
+
+	for (lane = 0; lane < sink_device_lane_count(sink); lane++) {
+		uint8_t new_pre_emphasis =
+			sink_device_get_pre_emphasis_level(sink, lane) + 1;
+
+		sink_device_set_adjust_pre_emphasis(sink, lane, new_pre_emphasis);
+	}
+
+	return true;
+}
+
+static void
+sink_device_mark_cr_done(struct sink_device *sink)
+{
+	int lane;
+
+	for (lane = 0; lane < sink_device_lane_count(sink); lane++)
+		sink->data.dpcd[DP_LANE0_1_STATUS + (lane >> 1)] |=
+			DP_LANE_CR_DONE << (4 * (lane & 1));
+
+	sink->data.cr_done = true;
+}
+
+static void
+sink_device_mark_channel_eq_done(struct sink_device *sink)
+{
+	int lane;
+
+	for (lane = 0; lane < sink_device_lane_count(sink); lane++) {
+		uint8_t mask = (DP_LANE_CHANNEL_EQ_DONE | DP_LANE_SYMBOL_LOCKED);
+		sink->data.dpcd[DP_LANE0_1_STATUS + (lane >> 1)] |=
+			mask << (4 * (lane & 1));
+	}
+
+	sink->data.dpcd[DP_LANE_ALIGN_STATUS_UPDATED] |= DP_INTERLANE_ALIGN_DONE;
+
+	sink->data.channel_eq_done = true;
+}
+
+static bool
+sink_device_get_link_status(struct sink_device *sink,
+			    uint8_t link_status[DP_LINK_STATUS_SIZE])
+{
+	if (!sink->data.cr_done) {
+		if (!sink_device_request_higher_voltage_swing(sink))
+			sink_device_mark_cr_done(sink);
+	} else if (!sink->data.channel_eq_done) {
+		if (!sink_device_request_higher_pre_emphasis(sink))
+			sink_device_mark_channel_eq_done(sink);
+	}
+
+	memcpy(link_status, sink->data.dpcd + DP_LANE0_1_STATUS,
+	       DP_LINK_STATUS_SIZE);
+
+	return true;
+}
+
+static void
+sink_device_reset(struct sink_device *sink, int lanes, uint8_t link_bw)
+{
+	memset(&sink->data, 0, sizeof sink->data);
+	sink->data.dpcd[DP_MAX_LINK_RATE] = link_bw;
+	sink->data.dpcd[DP_MAX_LANE_COUNT] = lanes;
+}
+
+static struct sink_device simple_sink = {
+	.get_link_status = sink_device_get_link_status,
+	.dpcd_write = sink_device_dpcd_write,
+};
+
+/* Glue code */
+
+struct test_intel_dp {
+	struct intel_dp dp;
+	struct sink_device *sink;
+	uint8_t link_bw;
+
+	uint8_t max_voltage;
+	uint8_t max_pre_emphasis;
+};
+
+static struct test_intel_dp *
+to_test_intel_dp(struct intel_dp *dp)
+{
+	return container_of(dp, struct test_intel_dp, dp);
+}
+
+void intel_dp_set_idle_link_train(struct intel_dp *intel_dp)
+{
+}
+
+bool intel_dp_source_supports_hbr2(struct intel_dp *intel_dp)
+{
+	return false;
+}
+
+bool
+intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_STATUS_SIZE])
+{
+	struct sink_device *sink = to_test_intel_dp(intel_dp)->sink;
+	return sink->get_link_status(sink, link_status);
+}
+
+void
+intel_dp_update_signal_levels(struct intel_dp *intel_dp)
+{
+}
+
+void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock,
+			   uint8_t *link_bw, uint8_t *rate_select)
+{
+	*link_bw = to_test_intel_dp(intel_dp)->link_bw;
+	*rate_select = 0;
+}
+
+void
+intel_dp_program_link_training_pattern(struct intel_dp *intel_dp,
+				       uint8_t dp_train_pat)
+{
+}
+
+uint8_t
+intel_dp_voltage_max(struct intel_dp *intel_dp)
+{
+	return to_test_intel_dp(intel_dp)->max_voltage <<
+		DP_TRAIN_VOLTAGE_SWING_SHIFT;
+}
+
+uint8_t
+intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t voltage_swing)
+{
+	return to_test_intel_dp(intel_dp)->max_pre_emphasis <<
+		DP_TRAIN_PRE_EMPHASIS_SHIFT;
+}
+
+ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset,
+			  void *buffer, size_t size)
+{
+	struct intel_dp *intel_dp =
+		container_of(aux, struct intel_dp, aux);
+	struct sink_device *sink = to_test_intel_dp(intel_dp)->sink;
+
+	return sink->dpcd_write(sink, offset, buffer, size);
+}
+
+/* --- */
+
+static struct test_intel_dp test_dp;
+
+static void
+do_test(struct sink_device *sink, int lanes, uint8_t link_bw,
+	     uint8_t max_voltage, uint8_t max_pre_emphasis)
+{
+	int lane;
+
+	memset(&test_dp, 0, sizeof test_dp);
+	test_dp.dp.lane_count = lanes;
+	test_dp.link_bw = link_bw;
+	test_dp.sink = sink;
+	test_dp.max_voltage =
+		max_voltage >> DP_TRAIN_VOLTAGE_SWING_SHIFT;
+	test_dp.max_pre_emphasis =
+		max_pre_emphasis >> DP_TRAIN_PRE_EMPHASIS_SHIFT;
+
+	sink_device_reset(sink, lanes, link_bw);
+
+	intel_dp_start_link_train(&test_dp.dp);
+	intel_dp_stop_link_train(&test_dp.dp);
+
+	assert(sink->data.cr_done);
+	assert(sink->data.channel_eq_done);
+
+	for (lane = 0; lane < test_dp.dp.lane_count; lane++) {
+		uint8_t cur_v = sink_device_get_voltage_swing(sink, lane);
+		uint8_t cur_p = sink_device_get_pre_emphasis_level(sink, lane);
+
+		assert(cur_v == test_dp.max_voltage);
+		assert(cur_p == test_dp.max_pre_emphasis);
+	}
+}
+
+int test_lanes[] = {
+	1, 2, 4,
+};
+
+uint8_t test_bw[] = {
+	DP_LINK_BW_1_62,
+	DP_LINK_BW_2_7,
+};
+
+uint8_t test_max_voltage[] = {
+	DP_TRAIN_VOLTAGE_SWING_LEVEL_0,
+	DP_TRAIN_VOLTAGE_SWING_LEVEL_1,
+	DP_TRAIN_VOLTAGE_SWING_LEVEL_2,
+	DP_TRAIN_VOLTAGE_SWING_LEVEL_3,
+};
+
+uint8_t test_max_pre_emphasis[] = {
+	DP_TRAIN_PRE_EMPH_LEVEL_0,
+	DP_TRAIN_PRE_EMPH_LEVEL_1,
+	DP_TRAIN_PRE_EMPH_LEVEL_2,
+	DP_TRAIN_PRE_EMPH_LEVEL_3,
+};
+
+void
+run_test(void)
+{
+	int lane, bw, voltage, emph;
+
+	for (lane = 0; lane < ARRAY_SIZE(test_lanes); lane++)
+	for (bw = 0; bw < ARRAY_SIZE(test_bw); bw++)
+	for (voltage = 0; voltage < ARRAY_SIZE(test_max_voltage); voltage++)
+	for (emph = 0; emph < ARRAY_SIZE(test_max_pre_emphasis); emph++) {
+		DRM_DEBUG_KMS("l%d-bw%d-v%d-pe%d",
+			      test_lanes[lane],
+			      test_bw[bw],
+			      test_max_voltage[voltage],
+			      test_max_pre_emphasis[emph]);
+		do_test(&simple_sink,
+			test_lanes[lane],
+			test_bw[bw],
+			test_max_voltage[voltage],
+			test_max_pre_emphasis[emph]);
+	}
+}
-- 
2.4.3

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* Re: [PATCH] drm/i915: Add link training test
  2015-09-14 11:51       ` [PATCH] drm/i915: Add link training test Ander Conselvan de Oliveira
@ 2015-09-14 13:11         ` Daniel Vetter
  2015-09-14 13:38           ` Ander Conselvan De Oliveira
  0 siblings, 1 reply; 31+ messages in thread
From: Daniel Vetter @ 2015-09-14 13:11 UTC (permalink / raw)
  To: Ander Conselvan de Oliveira; +Cc: intel-gfx, thomas.wood

On Mon, Sep 14, 2015 at 02:51:51PM +0300, Ander Conselvan de Oliveira wrote:
> ---
> 
> On Fri, 2015-09-11 at 17:11 +0300, Ander Conselvan de Oliveira wrote:
> > On Wed, 2015-09-09 at 11:33 +0100, Thomas Wood wrote:
> > > On 8 September 2015 at 13:28, Ander Conselvan de Oliveira
> > > <ander.conselvan.de.oliveira@intel.com> wrote:
> > > > 
> > 
> > > > diff --git a/link-training-test/Makefile b/link-training-test/Makefile
> > > 
> > > If this is meant to be part of the test suite, then it needs to be in
> > > the tests directory and use the igt test infrastructure. Otherwise it
> > > should be placed in tools or tools/link-training-test.
> > 
> > I made the test use the igt infrastructure, but I'm not sure if this is
> > a good fit for it. The dependency on the kernel is on build time, but
> > once compiled this can be run on any machine. This can also introduce
> > build failures if the test is not kept in sync with the driver source.
> > Ideally that a failure to build this would be reported as the test
> > failing, but I have no idea of how to achieve that.
> 
> Alternatively, this could be in the kernel source tree directly. This
> patch adds a test subdir to the i915 source dir, containing the link
> training test. The test is compiled as part of the normal build using
> the extra-y variable so that it doesn't get linked to the final kernel.
> 
> When make is run from the tests directory, a thin wrapper around the
> tests is built and linked to the object file compiled as part of the
> kernel build. Running make run_tests from the test dir runs the test
> and reports success or failure.
> 
> Any thoughts?

I think there's some precedence in other subsystems to integrate unit
tests directly in the kernel, e.g. locking selftest or similar things.
Usual approach is to either have a special module (but that often means
piles of EXPORT_SYMBOL only for that selftest module). Or just a y/n
Kconfig option which enables that code and runs all the self/unit tests
when the module loads.

I'd go with that approach since it's simpler. And we'd only need to tell
QA to enable that Kconfig option for more testing.

Long-term we might go more fancy with some static debugfs interface (i.e.
in the top-level drm/ directory, no in the numbered device subdirs in
debugfs) to run specific testcases so that we can report out the results.

But as long as module reload is part of BAT we don't need that, at least
not for a start.

Anyway I think having the unit tests in the kernel makes more sense, the
igt ones will likely get out of sync fast. And igt is supposed to work
with any kernel version (hence all the igt_require checks).
-Daniel

> 
> Ander
> 
> ---
>  drivers/gpu/drm/i915/Makefile                      |   2 +
>  drivers/gpu/drm/i915/tests/Makefile                |  39 ++
>  drivers/gpu/drm/i915/tests/empty.c                 |   0
>  drivers/gpu/drm/i915/tests/loader.c                | 108 +++++
>  .../drm/i915/tests/test_intel_dp_link_training.c   | 464 +++++++++++++++++++++
>  5 files changed, 613 insertions(+)
>  create mode 100644 drivers/gpu/drm/i915/tests/Makefile
>  create mode 100644 drivers/gpu/drm/i915/tests/empty.c
>  create mode 100644 drivers/gpu/drm/i915/tests/loader.c
>  create mode 100644 drivers/gpu/drm/i915/tests/test_intel_dp_link_training.c
> 
> diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
> index 0851de07..7298ce5 100644
> --- a/drivers/gpu/drm/i915/Makefile
> +++ b/drivers/gpu/drm/i915/Makefile
> @@ -97,6 +97,8 @@ i915-y += i915_vgpu.o
>  # legacy horrors
>  i915-y += i915_dma.o
>  
> +obj-y += tests/
> +
>  obj-$(CONFIG_DRM_I915)  += i915.o
>  
>  CFLAGS_i915_trace_points.o := -I$(src)
> diff --git a/drivers/gpu/drm/i915/tests/Makefile b/drivers/gpu/drm/i915/tests/Makefile
> new file mode 100644
> index 0000000..3062741
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/tests/Makefile
> @@ -0,0 +1,39 @@
> +# Please kbuild
> +# TODO: check for a better way to include this directory in the build
> +# without forcing the need for this empty file
> +obj-y += empty.o
> +
> +
> +# FIXME: The stack protector code causes tests to crash due to an improperly
> +# initialized %gs, so keep it disabled for now.
> +ccflags-y := -fstack-protector-explicit
> +
> +extra-y += test_intel_dp_link_training.o
> +
> +# If make is run from this directory
> +ifeq (0, $(MAKELEVEL))
> +
> +# For strlcpy
> +LDFLAGS = -lbsd
> +
> +TEST_PROGS = \
> +	test_intel_dp_link_training
> +
> +all: tests
> +
> +tests: $(TEST_PROGS)
> +
> +run_tests: tests
> +	@for TEST in $(TEST_PROGS); do \
> +		(./$$TEST && echo "$$TEST [PASS]") || echo "$$TEST [FAIL]"; \
> +	done
> +
> +test_%.o: test_%.c ../%.c
> +	make -C ../../../../../ drivers/gpu/drm/i915/tests/$@
> +
> +loader.o: loader.c
> +	$(CC) $(CFLAGS) -c -o $@ $<
> +
> +%: %.o loader.o
> +	$(CC) -o $@ $^ $(LDFLAGS)
> +endif
> diff --git a/drivers/gpu/drm/i915/tests/empty.c b/drivers/gpu/drm/i915/tests/empty.c
> new file mode 100644
> index 0000000..e69de29
> diff --git a/drivers/gpu/drm/i915/tests/loader.c b/drivers/gpu/drm/i915/tests/loader.c
> new file mode 100644
> index 0000000..9e38816
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/tests/loader.c
> @@ -0,0 +1,108 @@
> +/*
> + * Copyright © 2015 Intel Corporation
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the next
> + * paragraph) shall be included in all copies or substantial portions of the
> + * Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> + * IN THE SOFTWARE.
> + *
> + * Authors:
> + *    Ander Conselvan de Oliveira <ander.conselvan.de.oliveira@intel.com>
> + *
> + */
> +
> +#include <stdbool.h>
> +#include <stdint.h>
> +#include <stdlib.h>
> +#include <stdio.h>
> +#include <stdarg.h>
> +
> +void run_test(void);
> +
> +void assert(bool condition)
> +{
> +	if (!condition) {
> +		printf("Assert failed\n");
> +		abort();
> +	}
> +}
> +
> +struct {
> +	uint8_t padding[32];
> +} param_ops_int;
> +
> +unsigned int drm_debug = 0;	/* 1 to enable debug output */
> +
> +void drm_err(const char *format, ...)
> +{
> +	va_list args;
> +
> +	va_start(args, format);
> +	vprintf(format, args);
> +	va_end(args);
> +
> +}
> +
> +void drm_ut_debug_printk(const char *function_name, const char *format, ...)
> +{
> +	va_list args;
> +
> +	va_start(args, format);
> +	printf("[%s] ", function_name);
> +	vprintf(format, args);
> +	va_end(args);
> +}
> +
> +void __const_udelay(unsigned long xloops)
> +{
> +}
> +
> +struct mutex;
> +void mutex_lock_nested(struct mutex *lock, unsigned int subclass)
> +{
> +}
> +
> +void mutex_unlock(struct mutex *lock)
> +{
> +}
> +
> +struct lock_class_key;
> +void
> +__mutex_init(struct mutex *lock, const char *name, struct lock_class_key *key)
> +{
> +}
> +
> +void usleep_range(unsigned int min, unsigned int max)
> +{
> +}
> +
> +struct i2c_adapter;
> +int i2c_add_adapter(struct i2c_adapter *adapter)
> +{
> +	return 0;
> +}
> +
> +void i2c_del_adapter(struct i2c_adapter *adapater)
> +{
> +}
> +
> +int
> +main(int argc, char *argv[])
> +{
> +	run_test();
> +	return 0;
> +}
> diff --git a/drivers/gpu/drm/i915/tests/test_intel_dp_link_training.c b/drivers/gpu/drm/i915/tests/test_intel_dp_link_training.c
> new file mode 100644
> index 0000000..11868f8
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/tests/test_intel_dp_link_training.c
> @@ -0,0 +1,464 @@
> +/*
> + * Copyright © 2015 Intel Corporation
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice (including the next
> + * paragraph) shall be included in all copies or substantial portions of the
> + * Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> + * IN THE SOFTWARE.
> + *
> + * Authors:
> + *    Ander Conselvan de Oliveira <ander.conselvan.de.oliveira@intel.com>
> + *
> + */
> +
> +#include "../intel_dp_link_training.c"
> +
> +#define drm_dp_dpcd_write	real_drm_dp_dpcd_write
> +#include "../../drm_dp_helper.c"
> +#undef drm_dp_dpcd_write
> +
> +void assert(bool condition);
> +
> +
> +struct sink_device {
> +	ssize_t (*dpcd_write)(struct sink_device *sink, unsigned int offset,
> +			      void *buffer, size_t size);
> +	bool (*get_link_status)(struct sink_device *sink,
> +				uint8_t link_status[DP_LINK_STATUS_SIZE]);
> +
> +	struct {
> +		bool lane_count_and_bw_set;
> +		bool training_pattern_1_set;
> +		bool started_with_non_zero_levels;
> +		bool cr_done;
> +		bool channel_eq_done;
> +
> +		uint8_t dpcd[0x3000];
> +	} data;
> +};
> +
> +/* Fake sink device implementation */
> +
> +static uint8_t
> +sink_device_lane_count(struct sink_device *sink)
> +{
> +	return sink->data.dpcd[DP_LANE_COUNT_SET];
> +}
> +
> +static uint8_t
> +sink_device_get_training_pattern(struct sink_device *sink)
> +{
> +	return sink->data.dpcd[DP_TRAINING_PATTERN_SET] & DP_TRAINING_PATTERN_MASK;
> +}
> +
> +static uint8_t
> +sink_device_get_voltage_swing(struct sink_device *sink, int lane)
> +{
> +	return sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] &
> +		DP_TRAIN_VOLTAGE_SWING_MASK;
> +}
> +
> +static uint8_t
> +sink_device_get_pre_emphasis_level(struct sink_device *sink, int lane)
> +{
> +	return (sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] &
> +		 DP_TRAIN_PRE_EMPHASIS_MASK) >> DP_TRAIN_PRE_EMPHASIS_SHIFT;
> +}
> +
> +static void
> +sink_device_check_lane_count_and_bw(struct sink_device *sink)
> +{
> +	if (sink->data.lane_count_and_bw_set)
> +		return;
> +
> +	assert(sink->data.dpcd[DP_TRAINING_PATTERN_SET] == 0);
> +
> +	if (sink->data.dpcd[DP_LINK_BW_SET] != 0 &&
> +	    sink->data.dpcd[DP_LANE_COUNT_SET] != 0)
> +		sink->data.lane_count_and_bw_set = true;
> +}
> +
> +static void
> +sink_device_check_pattern_1_set(struct sink_device *sink)
> +{
> +	int lane;
> +
> +	if (!sink->data.lane_count_and_bw_set ||
> +	    sink->data.training_pattern_1_set)
> +		return;
> +
> +	assert(sink_device_get_training_pattern(sink) <= DP_TRAINING_PATTERN_1);
> +
> +	if (sink_device_get_training_pattern(sink) != DP_TRAINING_PATTERN_1)
> +		return;
> +
> +	assert(sink->data.dpcd[DP_LINK_BW_SET] == DP_LINK_BW_1_62 ||
> +	       sink->data.dpcd[DP_LINK_BW_SET] == DP_LINK_BW_2_7);
> +
> +	assert(sink->data.dpcd[DP_LANE_COUNT_SET] == 1 ||
> +		   sink->data.dpcd[DP_LANE_COUNT_SET] == 2 ||
> +		   sink->data.dpcd[DP_LANE_COUNT_SET] == 4);
> +
> +	for (lane = 0; lane < sink_device_lane_count(sink); lane++) {
> +		if (sink_device_get_voltage_swing(sink, lane) != DP_TRAIN_VOLTAGE_SWING_LEVEL_0 ||
> +		    sink_device_get_pre_emphasis_level(sink, lane) != DP_TRAIN_PRE_EMPH_LEVEL_0)
> +			sink->data.started_with_non_zero_levels = true;
> +	}
> +
> +	sink->data.training_pattern_1_set = true;
> +}
> +
> +static void
> +sink_device_check_pattern_2_set(struct sink_device *sink)
> +{
> +	if (!sink->data.cr_done)
> +		return;
> +
> +	assert(sink_device_get_training_pattern(sink) == DP_TRAINING_PATTERN_2);
> +}
> +
> +static void
> +sink_device_check_pattern_disable(struct sink_device *sink)
> +{
> +	if (!sink->data.cr_done || ! sink->data.channel_eq_done)
> +		return;
> +
> +	assert(sink_device_get_training_pattern(sink) == DP_TRAINING_PATTERN_DISABLE);
> +}
> +
> +static ssize_t
> +sink_device_dpcd_write(struct sink_device *sink, unsigned int offset,
> +		       void *buffer, size_t size)
> +{
> +	memcpy(sink->data.dpcd + offset, buffer, size);
> +
> +	sink_device_check_lane_count_and_bw(sink);
> +
> +	if (!sink->data.cr_done)
> +		sink_device_check_pattern_1_set(sink);
> +	else if (!sink->data.channel_eq_done)
> +		sink_device_check_pattern_2_set(sink);
> +	else
> +		sink_device_check_pattern_disable(sink);
> +
> +	return size;
> +}
> +
> +static bool
> +sink_device_max_voltage_reached(struct sink_device *sink, int lane)
> +{
> +	return (sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] & DP_TRAIN_MAX_SWING_REACHED) ==
> +		DP_TRAIN_MAX_SWING_REACHED;
> +}
> +
> +static bool
> +sink_device_max_pre_emphasis_reached(struct sink_device *sink, int lane)
> +{
> +	return (sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] & DP_TRAIN_MAX_PRE_EMPHASIS_REACHED) ==
> +		DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
> +}
> +
> +static void
> +sink_device_set_adjust_voltage(struct sink_device *sink,
> +			       int lane, uint8_t level)
> +{
> +	int shift = DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT * (lane & 1);
> +
> +	sink->data.dpcd[DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1)] &=
> +		~(DP_ADJUST_VOLTAGE_SWING_LANE0_MASK << shift);
> +	sink->data.dpcd[DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1)] |=
> +		level << shift;
> +}
> +
> +static void
> +sink_device_set_adjust_pre_emphasis(struct sink_device *sink,
> +				    int lane, uint8_t level)
> +{
> +	int shift = DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT * (lane & 1);
> +
> +	sink->data.dpcd[DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1)] &=
> +		~(DP_ADJUST_PRE_EMPHASIS_LANE0_MASK << shift);
> +	sink->data.dpcd[DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1)] |=
> +		level << (DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT + shift);
> +}
> +
> +static bool
> +sink_device_request_higher_voltage_swing(struct sink_device *sink)
> +{
> +	bool max_reached = false;
> +	int lane;
> +
> +	for (lane = 0; lane < sink_device_lane_count(sink); lane++) {
> +		if (sink_device_max_voltage_reached(sink, lane)) {
> +			max_reached = true;
> +			break;
> +		}
> +	}
> +
> +	if (max_reached)
> +		return false;
> +
> +	for (lane = 0; lane < sink_device_lane_count(sink); lane++) {
> +		uint8_t new_voltage =
> +			sink_device_get_voltage_swing(sink, lane) + 1;
> +
> +		sink_device_set_adjust_voltage(sink, lane, new_voltage);
> +	}
> +
> +	return true;
> +}
> +
> +static bool
> +sink_device_request_higher_pre_emphasis(struct sink_device *sink)
> +{
> +	bool max_reached = false;
> +	int lane;
> +
> +	for (lane = 0; lane < sink_device_lane_count(sink); lane++) {
> +		if (sink_device_max_pre_emphasis_reached(sink, lane)) {
> +			max_reached = true;
> +			break;
> +		}
> +	}
> +
> +	if (max_reached)
> +		return false;
> +
> +	for (lane = 0; lane < sink_device_lane_count(sink); lane++) {
> +		uint8_t new_pre_emphasis =
> +			sink_device_get_pre_emphasis_level(sink, lane) + 1;
> +
> +		sink_device_set_adjust_pre_emphasis(sink, lane, new_pre_emphasis);
> +	}
> +
> +	return true;
> +}
> +
> +static void
> +sink_device_mark_cr_done(struct sink_device *sink)
> +{
> +	int lane;
> +
> +	for (lane = 0; lane < sink_device_lane_count(sink); lane++)
> +		sink->data.dpcd[DP_LANE0_1_STATUS + (lane >> 1)] |=
> +			DP_LANE_CR_DONE << (4 * (lane & 1));
> +
> +	sink->data.cr_done = true;
> +}
> +
> +static void
> +sink_device_mark_channel_eq_done(struct sink_device *sink)
> +{
> +	int lane;
> +
> +	for (lane = 0; lane < sink_device_lane_count(sink); lane++) {
> +		uint8_t mask = (DP_LANE_CHANNEL_EQ_DONE | DP_LANE_SYMBOL_LOCKED);
> +		sink->data.dpcd[DP_LANE0_1_STATUS + (lane >> 1)] |=
> +			mask << (4 * (lane & 1));
> +	}
> +
> +	sink->data.dpcd[DP_LANE_ALIGN_STATUS_UPDATED] |= DP_INTERLANE_ALIGN_DONE;
> +
> +	sink->data.channel_eq_done = true;
> +}
> +
> +static bool
> +sink_device_get_link_status(struct sink_device *sink,
> +			    uint8_t link_status[DP_LINK_STATUS_SIZE])
> +{
> +	if (!sink->data.cr_done) {
> +		if (!sink_device_request_higher_voltage_swing(sink))
> +			sink_device_mark_cr_done(sink);
> +	} else if (!sink->data.channel_eq_done) {
> +		if (!sink_device_request_higher_pre_emphasis(sink))
> +			sink_device_mark_channel_eq_done(sink);
> +	}
> +
> +	memcpy(link_status, sink->data.dpcd + DP_LANE0_1_STATUS,
> +	       DP_LINK_STATUS_SIZE);
> +
> +	return true;
> +}
> +
> +static void
> +sink_device_reset(struct sink_device *sink, int lanes, uint8_t link_bw)
> +{
> +	memset(&sink->data, 0, sizeof sink->data);
> +	sink->data.dpcd[DP_MAX_LINK_RATE] = link_bw;
> +	sink->data.dpcd[DP_MAX_LANE_COUNT] = lanes;
> +}
> +
> +static struct sink_device simple_sink = {
> +	.get_link_status = sink_device_get_link_status,
> +	.dpcd_write = sink_device_dpcd_write,
> +};
> +
> +/* Glue code */
> +
> +struct test_intel_dp {
> +	struct intel_dp dp;
> +	struct sink_device *sink;
> +	uint8_t link_bw;
> +
> +	uint8_t max_voltage;
> +	uint8_t max_pre_emphasis;
> +};
> +
> +static struct test_intel_dp *
> +to_test_intel_dp(struct intel_dp *dp)
> +{
> +	return container_of(dp, struct test_intel_dp, dp);
> +}
> +
> +void intel_dp_set_idle_link_train(struct intel_dp *intel_dp)
> +{
> +}
> +
> +bool intel_dp_source_supports_hbr2(struct intel_dp *intel_dp)
> +{
> +	return false;
> +}
> +
> +bool
> +intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_STATUS_SIZE])
> +{
> +	struct sink_device *sink = to_test_intel_dp(intel_dp)->sink;
> +	return sink->get_link_status(sink, link_status);
> +}
> +
> +void
> +intel_dp_update_signal_levels(struct intel_dp *intel_dp)
> +{
> +}
> +
> +void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock,
> +			   uint8_t *link_bw, uint8_t *rate_select)
> +{
> +	*link_bw = to_test_intel_dp(intel_dp)->link_bw;
> +	*rate_select = 0;
> +}
> +
> +void
> +intel_dp_program_link_training_pattern(struct intel_dp *intel_dp,
> +				       uint8_t dp_train_pat)
> +{
> +}
> +
> +uint8_t
> +intel_dp_voltage_max(struct intel_dp *intel_dp)
> +{
> +	return to_test_intel_dp(intel_dp)->max_voltage <<
> +		DP_TRAIN_VOLTAGE_SWING_SHIFT;
> +}
> +
> +uint8_t
> +intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t voltage_swing)
> +{
> +	return to_test_intel_dp(intel_dp)->max_pre_emphasis <<
> +		DP_TRAIN_PRE_EMPHASIS_SHIFT;
> +}
> +
> +ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset,
> +			  void *buffer, size_t size)
> +{
> +	struct intel_dp *intel_dp =
> +		container_of(aux, struct intel_dp, aux);
> +	struct sink_device *sink = to_test_intel_dp(intel_dp)->sink;
> +
> +	return sink->dpcd_write(sink, offset, buffer, size);
> +}
> +
> +/* --- */
> +
> +static struct test_intel_dp test_dp;
> +
> +static void
> +do_test(struct sink_device *sink, int lanes, uint8_t link_bw,
> +	     uint8_t max_voltage, uint8_t max_pre_emphasis)
> +{
> +	int lane;
> +
> +	memset(&test_dp, 0, sizeof test_dp);
> +	test_dp.dp.lane_count = lanes;
> +	test_dp.link_bw = link_bw;
> +	test_dp.sink = sink;
> +	test_dp.max_voltage =
> +		max_voltage >> DP_TRAIN_VOLTAGE_SWING_SHIFT;
> +	test_dp.max_pre_emphasis =
> +		max_pre_emphasis >> DP_TRAIN_PRE_EMPHASIS_SHIFT;
> +
> +	sink_device_reset(sink, lanes, link_bw);
> +
> +	intel_dp_start_link_train(&test_dp.dp);
> +	intel_dp_stop_link_train(&test_dp.dp);
> +
> +	assert(sink->data.cr_done);
> +	assert(sink->data.channel_eq_done);
> +
> +	for (lane = 0; lane < test_dp.dp.lane_count; lane++) {
> +		uint8_t cur_v = sink_device_get_voltage_swing(sink, lane);
> +		uint8_t cur_p = sink_device_get_pre_emphasis_level(sink, lane);
> +
> +		assert(cur_v == test_dp.max_voltage);
> +		assert(cur_p == test_dp.max_pre_emphasis);
> +	}
> +}
> +
> +int test_lanes[] = {
> +	1, 2, 4,
> +};
> +
> +uint8_t test_bw[] = {
> +	DP_LINK_BW_1_62,
> +	DP_LINK_BW_2_7,
> +};
> +
> +uint8_t test_max_voltage[] = {
> +	DP_TRAIN_VOLTAGE_SWING_LEVEL_0,
> +	DP_TRAIN_VOLTAGE_SWING_LEVEL_1,
> +	DP_TRAIN_VOLTAGE_SWING_LEVEL_2,
> +	DP_TRAIN_VOLTAGE_SWING_LEVEL_3,
> +};
> +
> +uint8_t test_max_pre_emphasis[] = {
> +	DP_TRAIN_PRE_EMPH_LEVEL_0,
> +	DP_TRAIN_PRE_EMPH_LEVEL_1,
> +	DP_TRAIN_PRE_EMPH_LEVEL_2,
> +	DP_TRAIN_PRE_EMPH_LEVEL_3,
> +};
> +
> +void
> +run_test(void)
> +{
> +	int lane, bw, voltage, emph;
> +
> +	for (lane = 0; lane < ARRAY_SIZE(test_lanes); lane++)
> +	for (bw = 0; bw < ARRAY_SIZE(test_bw); bw++)
> +	for (voltage = 0; voltage < ARRAY_SIZE(test_max_voltage); voltage++)
> +	for (emph = 0; emph < ARRAY_SIZE(test_max_pre_emphasis); emph++) {
> +		DRM_DEBUG_KMS("l%d-bw%d-v%d-pe%d",
> +			      test_lanes[lane],
> +			      test_bw[bw],
> +			      test_max_voltage[voltage],
> +			      test_max_pre_emphasis[emph]);
> +		do_test(&simple_sink,
> +			test_lanes[lane],
> +			test_bw[bw],
> +			test_max_voltage[voltage],
> +			test_max_pre_emphasis[emph]);
> +	}
> +}
> -- 
> 2.4.3
> 
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/intel-gfx

-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH] drm/i915: Add link training test
  2015-09-14 13:11         ` Daniel Vetter
@ 2015-09-14 13:38           ` Ander Conselvan De Oliveira
  2015-09-15 13:08             ` Ander Conselvan De Oliveira
  2015-09-15 13:11             ` [PATCH 1/2] drm/i915: Make link training state machine code use function pointers Ander Conselvan de Oliveira
  0 siblings, 2 replies; 31+ messages in thread
From: Ander Conselvan De Oliveira @ 2015-09-14 13:38 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: intel-gfx, thomas.wood

On Mon, 2015-09-14 at 15:11 +0200, Daniel Vetter wrote:
> On Mon, Sep 14, 2015 at 02:51:51PM +0300, Ander Conselvan de Oliveira wrote:
> > ---
> > 
> > On Fri, 2015-09-11 at 17:11 +0300, Ander Conselvan de Oliveira wrote:
> > > On Wed, 2015-09-09 at 11:33 +0100, Thomas Wood wrote:
> > > > On 8 September 2015 at 13:28, Ander Conselvan de Oliveira
> > > > <ander.conselvan.de.oliveira@intel.com> wrote:
> > > > > 
> > > 
> > > > > diff --git a/link-training-test/Makefile b/link-training-test/Makefile
> > > > 
> > > > If this is meant to be part of the test suite, then it needs to be in
> > > > the tests directory and use the igt test infrastructure. Otherwise it
> > > > should be placed in tools or tools/link-training-test.
> > > 
> > > I made the test use the igt infrastructure, but I'm not sure if this is
> > > a good fit for it. The dependency on the kernel is on build time, but
> > > once compiled this can be run on any machine. This can also introduce
> > > build failures if the test is not kept in sync with the driver source.
> > > Ideally that a failure to build this would be reported as the test
> > > failing, but I have no idea of how to achieve that.
> > 
> > Alternatively, this could be in the kernel source tree directly. This
> > patch adds a test subdir to the i915 source dir, containing the link
> > training test. The test is compiled as part of the normal build using
> > the extra-y variable so that it doesn't get linked to the final kernel.
> > 
> > When make is run from the tests directory, a thin wrapper around the
> > tests is built and linked to the object file compiled as part of the
> > kernel build. Running make run_tests from the test dir runs the test
> > and reports success or failure.
> > 
> > Any thoughts?
> 
> I think there's some precedence in other subsystems to integrate unit
> tests directly in the kernel, e.g. locking selftest or similar things.
> Usual approach is to either have a special module (but that often means
> piles of EXPORT_SYMBOL only for that selftest module). Or just a y/n
> Kconfig option which enables that code and runs all the self/unit tests
> when the module loads.
> 
> I'd go with that approach since it's simpler. And we'd only need to tell
> QA to enable that Kconfig option for more testing.

I'll have a look into that Kconfig approach, but there's a couple of things
I like about having the unit test as user space binaries:

  - there's no need to boot the newly compiled kernel, so doing a test run
    is super fast;
  - the binaries can be debugged with gdb just like other user space stuff.

The downside is the stubbing necessary. In this particular case it wasn't
that bad, but I guess it could get really ugly.

> Long-term we might go more fancy with some static debugfs interface (i.e.
> in the top-level drm/ directory, no in the numbered device subdirs in
> debugfs) to run specific testcases so that we can report out the results.
> 
> But as long as module reload is part of BAT we don't need that, at least
> not for a start.
> 
> Anyway I think having the unit tests in the kernel makes more sense, the
> igt ones will likely get out of sync fast. And igt is supposed to work
> with any kernel version (hence all the igt_require checks).

Yeah, agreed.

Ander

> -Daniel
> 
> > 
> > Ander
> > 
> > ---
> >  drivers/gpu/drm/i915/Makefile                      |   2 +
> >  drivers/gpu/drm/i915/tests/Makefile                |  39 ++
> >  drivers/gpu/drm/i915/tests/empty.c                 |   0
> >  drivers/gpu/drm/i915/tests/loader.c                | 108 +++++
> >  .../drm/i915/tests/test_intel_dp_link_training.c   | 464 +++++++++++++++++++++
> >  5 files changed, 613 insertions(+)
> >  create mode 100644 drivers/gpu/drm/i915/tests/Makefile
> >  create mode 100644 drivers/gpu/drm/i915/tests/empty.c
> >  create mode 100644 drivers/gpu/drm/i915/tests/loader.c
> >  create mode 100644 drivers/gpu/drm/i915/tests/test_intel_dp_link_training.c
> > 
> > diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
> > index 0851de07..7298ce5 100644
> > --- a/drivers/gpu/drm/i915/Makefile
> > +++ b/drivers/gpu/drm/i915/Makefile
> > @@ -97,6 +97,8 @@ i915-y += i915_vgpu.o
> >  # legacy horrors
> >  i915-y += i915_dma.o
> >  
> > +obj-y += tests/
> > +
> >  obj-$(CONFIG_DRM_I915)  += i915.o
> >  
> >  CFLAGS_i915_trace_points.o := -I$(src)
> > diff --git a/drivers/gpu/drm/i915/tests/Makefile b/drivers/gpu/drm/i915/tests/Makefile
> > new file mode 100644
> > index 0000000..3062741
> > --- /dev/null
> > +++ b/drivers/gpu/drm/i915/tests/Makefile
> > @@ -0,0 +1,39 @@
> > +# Please kbuild
> > +# TODO: check for a better way to include this directory in the build
> > +# without forcing the need for this empty file
> > +obj-y += empty.o
> > +
> > +
> > +# FIXME: The stack protector code causes tests to crash due to an improperly
> > +# initialized %gs, so keep it disabled for now.
> > +ccflags-y := -fstack-protector-explicit
> > +
> > +extra-y += test_intel_dp_link_training.o
> > +
> > +# If make is run from this directory
> > +ifeq (0, $(MAKELEVEL))
> > +
> > +# For strlcpy
> > +LDFLAGS = -lbsd
> > +
> > +TEST_PROGS = \
> > +	test_intel_dp_link_training
> > +
> > +all: tests
> > +
> > +tests: $(TEST_PROGS)
> > +
> > +run_tests: tests
> > +	@for TEST in $(TEST_PROGS); do \
> > +		(./$$TEST && echo "$$TEST [PASS]") || echo "$$TEST [FAIL]"; \
> > +	done
> > +
> > +test_%.o: test_%.c ../%.c
> > +	make -C ../../../../../ drivers/gpu/drm/i915/tests/$@
> > +
> > +loader.o: loader.c
> > +	$(CC) $(CFLAGS) -c -o $@ $<
> > +
> > +%: %.o loader.o
> > +	$(CC) -o $@ $^ $(LDFLAGS)
> > +endif
> > diff --git a/drivers/gpu/drm/i915/tests/empty.c b/drivers/gpu/drm/i915/tests/empty.c
> > new file mode 100644
> > index 0000000..e69de29
> > diff --git a/drivers/gpu/drm/i915/tests/loader.c b/drivers/gpu/drm/i915/tests/loader.c
> > new file mode 100644
> > index 0000000..9e38816
> > --- /dev/null
> > +++ b/drivers/gpu/drm/i915/tests/loader.c
> > @@ -0,0 +1,108 @@
> > +/*
> > + * Copyright © 2015 Intel Corporation
> > + *
> > + * Permission is hereby granted, free of charge, to any person obtaining a
> > + * copy of this software and associated documentation files (the "Software"),
> > + * to deal in the Software without restriction, including without limitation
> > + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> > + * and/or sell copies of the Software, and to permit persons to whom the
> > + * Software is furnished to do so, subject to the following conditions:
> > + *
> > + * The above copyright notice and this permission notice (including the next
> > + * paragraph) shall be included in all copies or substantial portions of the
> > + * Software.
> > + *
> > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> > + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> > + * IN THE SOFTWARE.
> > + *
> > + * Authors:
> > + *    Ander Conselvan de Oliveira <ander.conselvan.de.oliveira@intel.com>
> > + *
> > + */
> > +
> > +#include <stdbool.h>
> > +#include <stdint.h>
> > +#include <stdlib.h>
> > +#include <stdio.h>
> > +#include <stdarg.h>
> > +
> > +void run_test(void);
> > +
> > +void assert(bool condition)
> > +{
> > +	if (!condition) {
> > +		printf("Assert failed\n");
> > +		abort();
> > +	}
> > +}
> > +
> > +struct {
> > +	uint8_t padding[32];
> > +} param_ops_int;
> > +
> > +unsigned int drm_debug = 0;	/* 1 to enable debug output */
> > +
> > +void drm_err(const char *format, ...)
> > +{
> > +	va_list args;
> > +
> > +	va_start(args, format);
> > +	vprintf(format, args);
> > +	va_end(args);
> > +
> > +}
> > +
> > +void drm_ut_debug_printk(const char *function_name, const char *format, ...)
> > +{
> > +	va_list args;
> > +
> > +	va_start(args, format);
> > +	printf("[%s] ", function_name);
> > +	vprintf(format, args);
> > +	va_end(args);
> > +}
> > +
> > +void __const_udelay(unsigned long xloops)
> > +{
> > +}
> > +
> > +struct mutex;
> > +void mutex_lock_nested(struct mutex *lock, unsigned int subclass)
> > +{
> > +}
> > +
> > +void mutex_unlock(struct mutex *lock)
> > +{
> > +}
> > +
> > +struct lock_class_key;
> > +void
> > +__mutex_init(struct mutex *lock, const char *name, struct lock_class_key *key)
> > +{
> > +}
> > +
> > +void usleep_range(unsigned int min, unsigned int max)
> > +{
> > +}
> > +
> > +struct i2c_adapter;
> > +int i2c_add_adapter(struct i2c_adapter *adapter)
> > +{
> > +	return 0;
> > +}
> > +
> > +void i2c_del_adapter(struct i2c_adapter *adapater)
> > +{
> > +}
> > +
> > +int
> > +main(int argc, char *argv[])
> > +{
> > +	run_test();
> > +	return 0;
> > +}
> > diff --git a/drivers/gpu/drm/i915/tests/test_intel_dp_link_training.c 
> > b/drivers/gpu/drm/i915/tests/test_intel_dp_link_training.c
> > new file mode 100644
> > index 0000000..11868f8
> > --- /dev/null
> > +++ b/drivers/gpu/drm/i915/tests/test_intel_dp_link_training.c
> > @@ -0,0 +1,464 @@
> > +/*
> > + * Copyright © 2015 Intel Corporation
> > + *
> > + * Permission is hereby granted, free of charge, to any person obtaining a
> > + * copy of this software and associated documentation files (the "Software"),
> > + * to deal in the Software without restriction, including without limitation
> > + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> > + * and/or sell copies of the Software, and to permit persons to whom the
> > + * Software is furnished to do so, subject to the following conditions:
> > + *
> > + * The above copyright notice and this permission notice (including the next
> > + * paragraph) shall be included in all copies or substantial portions of the
> > + * Software.
> > + *
> > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> > + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> > + * IN THE SOFTWARE.
> > + *
> > + * Authors:
> > + *    Ander Conselvan de Oliveira <ander.conselvan.de.oliveira@intel.com>
> > + *
> > + */
> > +
> > +#include "../intel_dp_link_training.c"
> > +
> > +#define drm_dp_dpcd_write	real_drm_dp_dpcd_write
> > +#include "../../drm_dp_helper.c"
> > +#undef drm_dp_dpcd_write
> > +
> > +void assert(bool condition);
> > +
> > +
> > +struct sink_device {
> > +	ssize_t (*dpcd_write)(struct sink_device *sink, unsigned int offset,
> > +			      void *buffer, size_t size);
> > +	bool (*get_link_status)(struct sink_device *sink,
> > +				uint8_t link_status[DP_LINK_STATUS_SIZE]);
> > +
> > +	struct {
> > +		bool lane_count_and_bw_set;
> > +		bool training_pattern_1_set;
> > +		bool started_with_non_zero_levels;
> > +		bool cr_done;
> > +		bool channel_eq_done;
> > +
> > +		uint8_t dpcd[0x3000];
> > +	} data;
> > +};
> > +
> > +/* Fake sink device implementation */
> > +
> > +static uint8_t
> > +sink_device_lane_count(struct sink_device *sink)
> > +{
> > +	return sink->data.dpcd[DP_LANE_COUNT_SET];
> > +}
> > +
> > +static uint8_t
> > +sink_device_get_training_pattern(struct sink_device *sink)
> > +{
> > +	return sink->data.dpcd[DP_TRAINING_PATTERN_SET] & DP_TRAINING_PATTERN_MASK;
> > +}
> > +
> > +static uint8_t
> > +sink_device_get_voltage_swing(struct sink_device *sink, int lane)
> > +{
> > +	return sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] &
> > +		DP_TRAIN_VOLTAGE_SWING_MASK;
> > +}
> > +
> > +static uint8_t
> > +sink_device_get_pre_emphasis_level(struct sink_device *sink, int lane)
> > +{
> > +	return (sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] &
> > +		 DP_TRAIN_PRE_EMPHASIS_MASK) >> DP_TRAIN_PRE_EMPHASIS_SHIFT;
> > +}
> > +
> > +static void
> > +sink_device_check_lane_count_and_bw(struct sink_device *sink)
> > +{
> > +	if (sink->data.lane_count_and_bw_set)
> > +		return;
> > +
> > +	assert(sink->data.dpcd[DP_TRAINING_PATTERN_SET] == 0);
> > +
> > +	if (sink->data.dpcd[DP_LINK_BW_SET] != 0 &&
> > +	    sink->data.dpcd[DP_LANE_COUNT_SET] != 0)
> > +		sink->data.lane_count_and_bw_set = true;
> > +}
> > +
> > +static void
> > +sink_device_check_pattern_1_set(struct sink_device *sink)
> > +{
> > +	int lane;
> > +
> > +	if (!sink->data.lane_count_and_bw_set ||
> > +	    sink->data.training_pattern_1_set)
> > +		return;
> > +
> > +	assert(sink_device_get_training_pattern(sink) <= DP_TRAINING_PATTERN_1);
> > +
> > +	if (sink_device_get_training_pattern(sink) != DP_TRAINING_PATTERN_1)
> > +		return;
> > +
> > +	assert(sink->data.dpcd[DP_LINK_BW_SET] == DP_LINK_BW_1_62 ||
> > +	       sink->data.dpcd[DP_LINK_BW_SET] == DP_LINK_BW_2_7);
> > +
> > +	assert(sink->data.dpcd[DP_LANE_COUNT_SET] == 1 ||
> > +		   sink->data.dpcd[DP_LANE_COUNT_SET] == 2 ||
> > +		   sink->data.dpcd[DP_LANE_COUNT_SET] == 4);
> > +
> > +	for (lane = 0; lane < sink_device_lane_count(sink); lane++) {
> > +		if (sink_device_get_voltage_swing(sink, lane) != DP_TRAIN_VOLTAGE_SWING_LEVEL_0 
> > ||
> > +		    sink_device_get_pre_emphasis_level(sink, lane) != 
> > DP_TRAIN_PRE_EMPH_LEVEL_0)
> > +			sink->data.started_with_non_zero_levels = true;
> > +	}
> > +
> > +	sink->data.training_pattern_1_set = true;
> > +}
> > +
> > +static void
> > +sink_device_check_pattern_2_set(struct sink_device *sink)
> > +{
> > +	if (!sink->data.cr_done)
> > +		return;
> > +
> > +	assert(sink_device_get_training_pattern(sink) == DP_TRAINING_PATTERN_2);
> > +}
> > +
> > +static void
> > +sink_device_check_pattern_disable(struct sink_device *sink)
> > +{
> > +	if (!sink->data.cr_done || ! sink->data.channel_eq_done)
> > +		return;
> > +
> > +	assert(sink_device_get_training_pattern(sink) == DP_TRAINING_PATTERN_DISABLE);
> > +}
> > +
> > +static ssize_t
> > +sink_device_dpcd_write(struct sink_device *sink, unsigned int offset,
> > +		       void *buffer, size_t size)
> > +{
> > +	memcpy(sink->data.dpcd + offset, buffer, size);
> > +
> > +	sink_device_check_lane_count_and_bw(sink);
> > +
> > +	if (!sink->data.cr_done)
> > +		sink_device_check_pattern_1_set(sink);
> > +	else if (!sink->data.channel_eq_done)
> > +		sink_device_check_pattern_2_set(sink);
> > +	else
> > +		sink_device_check_pattern_disable(sink);
> > +
> > +	return size;
> > +}
> > +
> > +static bool
> > +sink_device_max_voltage_reached(struct sink_device *sink, int lane)
> > +{
> > +	return (sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] & DP_TRAIN_MAX_SWING_REACHED) ==
> > +		DP_TRAIN_MAX_SWING_REACHED;
> > +}
> > +
> > +static bool
> > +sink_device_max_pre_emphasis_reached(struct sink_device *sink, int lane)
> > +{
> > +	return (sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] & 
> > DP_TRAIN_MAX_PRE_EMPHASIS_REACHED) ==
> > +		DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
> > +}
> > +
> > +static void
> > +sink_device_set_adjust_voltage(struct sink_device *sink,
> > +			       int lane, uint8_t level)
> > +{
> > +	int shift = DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT * (lane & 1);
> > +
> > +	sink->data.dpcd[DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1)] &=
> > +		~(DP_ADJUST_VOLTAGE_SWING_LANE0_MASK << shift);
> > +	sink->data.dpcd[DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1)] |=
> > +		level << shift;
> > +}
> > +
> > +static void
> > +sink_device_set_adjust_pre_emphasis(struct sink_device *sink,
> > +				    int lane, uint8_t level)
> > +{
> > +	int shift = DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT * (lane & 1);
> > +
> > +	sink->data.dpcd[DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1)] &=
> > +		~(DP_ADJUST_PRE_EMPHASIS_LANE0_MASK << shift);
> > +	sink->data.dpcd[DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1)] |=
> > +		level << (DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT + shift);
> > +}
> > +
> > +static bool
> > +sink_device_request_higher_voltage_swing(struct sink_device *sink)
> > +{
> > +	bool max_reached = false;
> > +	int lane;
> > +
> > +	for (lane = 0; lane < sink_device_lane_count(sink); lane++) {
> > +		if (sink_device_max_voltage_reached(sink, lane)) {
> > +			max_reached = true;
> > +			break;
> > +		}
> > +	}
> > +
> > +	if (max_reached)
> > +		return false;
> > +
> > +	for (lane = 0; lane < sink_device_lane_count(sink); lane++) {
> > +		uint8_t new_voltage =
> > +			sink_device_get_voltage_swing(sink, lane) + 1;
> > +
> > +		sink_device_set_adjust_voltage(sink, lane, new_voltage);
> > +	}
> > +
> > +	return true;
> > +}
> > +
> > +static bool
> > +sink_device_request_higher_pre_emphasis(struct sink_device *sink)
> > +{
> > +	bool max_reached = false;
> > +	int lane;
> > +
> > +	for (lane = 0; lane < sink_device_lane_count(sink); lane++) {
> > +		if (sink_device_max_pre_emphasis_reached(sink, lane)) {
> > +			max_reached = true;
> > +			break;
> > +		}
> > +	}
> > +
> > +	if (max_reached)
> > +		return false;
> > +
> > +	for (lane = 0; lane < sink_device_lane_count(sink); lane++) {
> > +		uint8_t new_pre_emphasis =
> > +			sink_device_get_pre_emphasis_level(sink, lane) + 1;
> > +
> > +		sink_device_set_adjust_pre_emphasis(sink, lane, new_pre_emphasis);
> > +	}
> > +
> > +	return true;
> > +}
> > +
> > +static void
> > +sink_device_mark_cr_done(struct sink_device *sink)
> > +{
> > +	int lane;
> > +
> > +	for (lane = 0; lane < sink_device_lane_count(sink); lane++)
> > +		sink->data.dpcd[DP_LANE0_1_STATUS + (lane >> 1)] |=
> > +			DP_LANE_CR_DONE << (4 * (lane & 1));
> > +
> > +	sink->data.cr_done = true;
> > +}
> > +
> > +static void
> > +sink_device_mark_channel_eq_done(struct sink_device *sink)
> > +{
> > +	int lane;
> > +
> > +	for (lane = 0; lane < sink_device_lane_count(sink); lane++) {
> > +		uint8_t mask = (DP_LANE_CHANNEL_EQ_DONE | DP_LANE_SYMBOL_LOCKED);
> > +		sink->data.dpcd[DP_LANE0_1_STATUS + (lane >> 1)] |=
> > +			mask << (4 * (lane & 1));
> > +	}
> > +
> > +	sink->data.dpcd[DP_LANE_ALIGN_STATUS_UPDATED] |= DP_INTERLANE_ALIGN_DONE;
> > +
> > +	sink->data.channel_eq_done = true;
> > +}
> > +
> > +static bool
> > +sink_device_get_link_status(struct sink_device *sink,
> > +			    uint8_t link_status[DP_LINK_STATUS_SIZE])
> > +{
> > +	if (!sink->data.cr_done) {
> > +		if (!sink_device_request_higher_voltage_swing(sink))
> > +			sink_device_mark_cr_done(sink);
> > +	} else if (!sink->data.channel_eq_done) {
> > +		if (!sink_device_request_higher_pre_emphasis(sink))
> > +			sink_device_mark_channel_eq_done(sink);
> > +	}
> > +
> > +	memcpy(link_status, sink->data.dpcd + DP_LANE0_1_STATUS,
> > +	       DP_LINK_STATUS_SIZE);
> > +
> > +	return true;
> > +}
> > +
> > +static void
> > +sink_device_reset(struct sink_device *sink, int lanes, uint8_t link_bw)
> > +{
> > +	memset(&sink->data, 0, sizeof sink->data);
> > +	sink->data.dpcd[DP_MAX_LINK_RATE] = link_bw;
> > +	sink->data.dpcd[DP_MAX_LANE_COUNT] = lanes;
> > +}
> > +
> > +static struct sink_device simple_sink = {
> > +	.get_link_status = sink_device_get_link_status,
> > +	.dpcd_write = sink_device_dpcd_write,
> > +};
> > +
> > +/* Glue code */
> > +
> > +struct test_intel_dp {
> > +	struct intel_dp dp;
> > +	struct sink_device *sink;
> > +	uint8_t link_bw;
> > +
> > +	uint8_t max_voltage;
> > +	uint8_t max_pre_emphasis;
> > +};
> > +
> > +static struct test_intel_dp *
> > +to_test_intel_dp(struct intel_dp *dp)
> > +{
> > +	return container_of(dp, struct test_intel_dp, dp);
> > +}
> > +
> > +void intel_dp_set_idle_link_train(struct intel_dp *intel_dp)
> > +{
> > +}
> > +
> > +bool intel_dp_source_supports_hbr2(struct intel_dp *intel_dp)
> > +{
> > +	return false;
> > +}
> > +
> > +bool
> > +intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_STATUS_SIZE])
> > +{
> > +	struct sink_device *sink = to_test_intel_dp(intel_dp)->sink;
> > +	return sink->get_link_status(sink, link_status);
> > +}
> > +
> > +void
> > +intel_dp_update_signal_levels(struct intel_dp *intel_dp)
> > +{
> > +}
> > +
> > +void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock,
> > +			   uint8_t *link_bw, uint8_t *rate_select)
> > +{
> > +	*link_bw = to_test_intel_dp(intel_dp)->link_bw;
> > +	*rate_select = 0;
> > +}
> > +
> > +void
> > +intel_dp_program_link_training_pattern(struct intel_dp *intel_dp,
> > +				       uint8_t dp_train_pat)
> > +{
> > +}
> > +
> > +uint8_t
> > +intel_dp_voltage_max(struct intel_dp *intel_dp)
> > +{
> > +	return to_test_intel_dp(intel_dp)->max_voltage <<
> > +		DP_TRAIN_VOLTAGE_SWING_SHIFT;
> > +}
> > +
> > +uint8_t
> > +intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t voltage_swing)
> > +{
> > +	return to_test_intel_dp(intel_dp)->max_pre_emphasis <<
> > +		DP_TRAIN_PRE_EMPHASIS_SHIFT;
> > +}
> > +
> > +ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset,
> > +			  void *buffer, size_t size)
> > +{
> > +	struct intel_dp *intel_dp =
> > +		container_of(aux, struct intel_dp, aux);
> > +	struct sink_device *sink = to_test_intel_dp(intel_dp)->sink;
> > +
> > +	return sink->dpcd_write(sink, offset, buffer, size);
> > +}
> > +
> > +/* --- */
> > +
> > +static struct test_intel_dp test_dp;
> > +
> > +static void
> > +do_test(struct sink_device *sink, int lanes, uint8_t link_bw,
> > +	     uint8_t max_voltage, uint8_t max_pre_emphasis)
> > +{
> > +	int lane;
> > +
> > +	memset(&test_dp, 0, sizeof test_dp);
> > +	test_dp.dp.lane_count = lanes;
> > +	test_dp.link_bw = link_bw;
> > +	test_dp.sink = sink;
> > +	test_dp.max_voltage =
> > +		max_voltage >> DP_TRAIN_VOLTAGE_SWING_SHIFT;
> > +	test_dp.max_pre_emphasis =
> > +		max_pre_emphasis >> DP_TRAIN_PRE_EMPHASIS_SHIFT;
> > +
> > +	sink_device_reset(sink, lanes, link_bw);
> > +
> > +	intel_dp_start_link_train(&test_dp.dp);
> > +	intel_dp_stop_link_train(&test_dp.dp);
> > +
> > +	assert(sink->data.cr_done);
> > +	assert(sink->data.channel_eq_done);
> > +
> > +	for (lane = 0; lane < test_dp.dp.lane_count; lane++) {
> > +		uint8_t cur_v = sink_device_get_voltage_swing(sink, lane);
> > +		uint8_t cur_p = sink_device_get_pre_emphasis_level(sink, lane);
> > +
> > +		assert(cur_v == test_dp.max_voltage);
> > +		assert(cur_p == test_dp.max_pre_emphasis);
> > +	}
> > +}
> > +
> > +int test_lanes[] = {
> > +	1, 2, 4,
> > +};
> > +
> > +uint8_t test_bw[] = {
> > +	DP_LINK_BW_1_62,
> > +	DP_LINK_BW_2_7,
> > +};
> > +
> > +uint8_t test_max_voltage[] = {
> > +	DP_TRAIN_VOLTAGE_SWING_LEVEL_0,
> > +	DP_TRAIN_VOLTAGE_SWING_LEVEL_1,
> > +	DP_TRAIN_VOLTAGE_SWING_LEVEL_2,
> > +	DP_TRAIN_VOLTAGE_SWING_LEVEL_3,
> > +};
> > +
> > +uint8_t test_max_pre_emphasis[] = {
> > +	DP_TRAIN_PRE_EMPH_LEVEL_0,
> > +	DP_TRAIN_PRE_EMPH_LEVEL_1,
> > +	DP_TRAIN_PRE_EMPH_LEVEL_2,
> > +	DP_TRAIN_PRE_EMPH_LEVEL_3,
> > +};
> > +
> > +void
> > +run_test(void)
> > +{
> > +	int lane, bw, voltage, emph;
> > +
> > +	for (lane = 0; lane < ARRAY_SIZE(test_lanes); lane++)
> > +	for (bw = 0; bw < ARRAY_SIZE(test_bw); bw++)
> > +	for (voltage = 0; voltage < ARRAY_SIZE(test_max_voltage); voltage++)
> > +	for (emph = 0; emph < ARRAY_SIZE(test_max_pre_emphasis); emph++) {
> > +		DRM_DEBUG_KMS("l%d-bw%d-v%d-pe%d",
> > +			      test_lanes[lane],
> > +			      test_bw[bw],
> > +			      test_max_voltage[voltage],
> > +			      test_max_pre_emphasis[emph]);
> > +		do_test(&simple_sink,
> > +			test_lanes[lane],
> > +			test_bw[bw],
> > +			test_max_voltage[voltage],
> > +			test_max_pre_emphasis[emph]);
> > +	}
> > +}
> > -- 
> > 2.4.3
> > 
> > _______________________________________________
> > Intel-gfx mailing list
> > Intel-gfx@lists.freedesktop.org
> > http://lists.freedesktop.org/mailman/listinfo/intel-gfx
> 
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH] drm/i915: Add link training test
  2015-09-14 13:38           ` Ander Conselvan De Oliveira
@ 2015-09-15 13:08             ` Ander Conselvan De Oliveira
  2015-09-23  8:24               ` Daniel Vetter
  2015-09-15 13:11             ` [PATCH 1/2] drm/i915: Make link training state machine code use function pointers Ander Conselvan de Oliveira
  1 sibling, 1 reply; 31+ messages in thread
From: Ander Conselvan De Oliveira @ 2015-09-15 13:08 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: intel-gfx, thomas.wood

On Mon, 2015-09-14 at 16:38 +0300, Ander Conselvan De Oliveira wrote:
> On Mon, 2015-09-14 at 15:11 +0200, Daniel Vetter wrote:
> > On Mon, Sep 14, 2015 at 02:51:51PM +0300, Ander Conselvan de Oliveira wrote:
> > > ---
> > > 
> > > On Fri, 2015-09-11 at 17:11 +0300, Ander Conselvan de Oliveira wrote:
> > > > On Wed, 2015-09-09 at 11:33 +0100, Thomas Wood wrote:
> > > > > On 8 September 2015 at 13:28, Ander Conselvan de Oliveira
> > > > > <ander.conselvan.de.oliveira@intel.com> wrote:
> > > > > > 
> > > > 
> > > > > > diff --git a/link-training-test/Makefile b/link-training-test/Makefile
> > > > > 
> > > > > If this is meant to be part of the test suite, then it needs to be in
> > > > > the tests directory and use the igt test infrastructure. Otherwise it
> > > > > should be placed in tools or tools/link-training-test.
> > > > 
> > > > I made the test use the igt infrastructure, but I'm not sure if this is
> > > > a good fit for it. The dependency on the kernel is on build time, but
> > > > once compiled this can be run on any machine. This can also introduce
> > > > build failures if the test is not kept in sync with the driver source.
> > > > Ideally that a failure to build this would be reported as the test
> > > > failing, but I have no idea of how to achieve that.
> > > 
> > > Alternatively, this could be in the kernel source tree directly. This
> > > patch adds a test subdir to the i915 source dir, containing the link
> > > training test. The test is compiled as part of the normal build using
> > > the extra-y variable so that it doesn't get linked to the final kernel.
> > > 
> > > When make is run from the tests directory, a thin wrapper around the
> > > tests is built and linked to the object file compiled as part of the
> > > kernel build. Running make run_tests from the test dir runs the test
> > > and reports success or failure.
> > > 
> > > Any thoughts?
> > 
> > I think there's some precedence in other subsystems to integrate unit
> > tests directly in the kernel, e.g. locking selftest or similar things.
> > Usual approach is to either have a special module (but that often means
> > piles of EXPORT_SYMBOL only for that selftest module). Or just a y/n
> > Kconfig option which enables that code and runs all the self/unit tests
> > when the module loads.
> > 
> > I'd go with that approach since it's simpler. And we'd only need to tell
> > QA to enable that Kconfig option for more testing.
> 
> I'll have a look into that Kconfig approach, but there's a couple of things
> I like about having the unit test as user space binaries:
> 
>   - there's no need to boot the newly compiled kernel, so doing a test run
>     is super fast;
>   - the binaries can be debugged with gdb just like other user space stuff.

I implemented the test using the Kconfig approach, and it seems to work well
without impacting the points above. I added the call to run the test as the
first thing in i915_init(), and with the driver compiled built-in, running
the kernel under qemu will run the tests. And qemu can also provide a gdb
remote target.

One thing might be a problem though. With the previous approach, the
functions overriden by the test where simply reimplemented in the new binary.
But now the test is linked to the entire driver, so that's not possible. To
work around that, I had to add function pointers to all the functions called
by the link training state machine to intel_dp. I don't think that method
scales well.

I'll send update patches for reference as replies to this mail.


Ander

> The downside is the stubbing necessary. In this particular case it wasn't
> that bad, but I guess it could get really ugly.
> 
> > Long-term we might go more fancy with some static debugfs interface (i.e.
> > in the top-level drm/ directory, no in the numbered device subdirs in
> > debugfs) to run specific testcases so that we can report out the results.
> > 
> > But as long as module reload is part of BAT we don't need that, at least
> > not for a start.
> > 
> > Anyway I think having the unit tests in the kernel makes more sense, the
> > igt ones will likely get out of sync fast. And igt is supposed to work
> > with any kernel version (hence all the igt_require checks).
> 
> Yeah, agreed.
> 
> Ander
> 
> > -Daniel
> > 
> > > 
> > > Ander
> > > 
> > > ---
> > >  drivers/gpu/drm/i915/Makefile                      |   2 +
> > >  drivers/gpu/drm/i915/tests/Makefile                |  39 ++
> > >  drivers/gpu/drm/i915/tests/empty.c                 |   0
> > >  drivers/gpu/drm/i915/tests/loader.c                | 108 +++++
> > >  .../drm/i915/tests/test_intel_dp_link_training.c   | 464 +++++++++++++++++++++
> > >  5 files changed, 613 insertions(+)
> > >  create mode 100644 drivers/gpu/drm/i915/tests/Makefile
> > >  create mode 100644 drivers/gpu/drm/i915/tests/empty.c
> > >  create mode 100644 drivers/gpu/drm/i915/tests/loader.c
> > >  create mode 100644 drivers/gpu/drm/i915/tests/test_intel_dp_link_training.c
> > > 
> > > diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
> > > index 0851de07..7298ce5 100644
> > > --- a/drivers/gpu/drm/i915/Makefile
> > > +++ b/drivers/gpu/drm/i915/Makefile
> > > @@ -97,6 +97,8 @@ i915-y += i915_vgpu.o
> > >  # legacy horrors
> > >  i915-y += i915_dma.o
> > >  
> > > +obj-y += tests/
> > > +
> > >  obj-$(CONFIG_DRM_I915)  += i915.o
> > >  
> > >  CFLAGS_i915_trace_points.o := -I$(src)
> > > diff --git a/drivers/gpu/drm/i915/tests/Makefile b/drivers/gpu/drm/i915/tests/Makefile
> > > new file mode 100644
> > > index 0000000..3062741
> > > --- /dev/null
> > > +++ b/drivers/gpu/drm/i915/tests/Makefile
> > > @@ -0,0 +1,39 @@
> > > +# Please kbuild
> > > +# TODO: check for a better way to include this directory in the build
> > > +# without forcing the need for this empty file
> > > +obj-y += empty.o
> > > +
> > > +
> > > +# FIXME: The stack protector code causes tests to crash due to an improperly
> > > +# initialized %gs, so keep it disabled for now.
> > > +ccflags-y := -fstack-protector-explicit
> > > +
> > > +extra-y += test_intel_dp_link_training.o
> > > +
> > > +# If make is run from this directory
> > > +ifeq (0, $(MAKELEVEL))
> > > +
> > > +# For strlcpy
> > > +LDFLAGS = -lbsd
> > > +
> > > +TEST_PROGS = \
> > > +	test_intel_dp_link_training
> > > +
> > > +all: tests
> > > +
> > > +tests: $(TEST_PROGS)
> > > +
> > > +run_tests: tests
> > > +	@for TEST in $(TEST_PROGS); do \
> > > +		(./$$TEST && echo "$$TEST [PASS]") || echo "$$TEST [FAIL]"; \
> > > +	done
> > > +
> > > +test_%.o: test_%.c ../%.c
> > > +	make -C ../../../../../ drivers/gpu/drm/i915/tests/$@
> > > +
> > > +loader.o: loader.c
> > > +	$(CC) $(CFLAGS) -c -o $@ $<
> > > +
> > > +%: %.o loader.o
> > > +	$(CC) -o $@ $^ $(LDFLAGS)
> > > +endif
> > > diff --git a/drivers/gpu/drm/i915/tests/empty.c b/drivers/gpu/drm/i915/tests/empty.c
> > > new file mode 100644
> > > index 0000000..e69de29
> > > diff --git a/drivers/gpu/drm/i915/tests/loader.c b/drivers/gpu/drm/i915/tests/loader.c
> > > new file mode 100644
> > > index 0000000..9e38816
> > > --- /dev/null
> > > +++ b/drivers/gpu/drm/i915/tests/loader.c
> > > @@ -0,0 +1,108 @@
> > > +/*
> > > + * Copyright © 2015 Intel Corporation
> > > + *
> > > + * Permission is hereby granted, free of charge, to any person obtaining a
> > > + * copy of this software and associated documentation files (the "Software"),
> > > + * to deal in the Software without restriction, including without limitation
> > > + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> > > + * and/or sell copies of the Software, and to permit persons to whom the
> > > + * Software is furnished to do so, subject to the following conditions:
> > > + *
> > > + * The above copyright notice and this permission notice (including the next
> > > + * paragraph) shall be included in all copies or substantial portions of the
> > > + * Software.
> > > + *
> > > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> > > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> > > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> > > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> > > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> > > + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> > > + * IN THE SOFTWARE.
> > > + *
> > > + * Authors:
> > > + *    Ander Conselvan de Oliveira <ander.conselvan.de.oliveira@intel.com>
> > > + *
> > > + */
> > > +
> > > +#include <stdbool.h>
> > > +#include <stdint.h>
> > > +#include <stdlib.h>
> > > +#include <stdio.h>
> > > +#include <stdarg.h>
> > > +
> > > +void run_test(void);
> > > +
> > > +void assert(bool condition)
> > > +{
> > > +	if (!condition) {
> > > +		printf("Assert failed\n");
> > > +		abort();
> > > +	}
> > > +}
> > > +
> > > +struct {
> > > +	uint8_t padding[32];
> > > +} param_ops_int;
> > > +
> > > +unsigned int drm_debug = 0;	/* 1 to enable debug output */
> > > +
> > > +void drm_err(const char *format, ...)
> > > +{
> > > +	va_list args;
> > > +
> > > +	va_start(args, format);
> > > +	vprintf(format, args);
> > > +	va_end(args);
> > > +
> > > +}
> > > +
> > > +void drm_ut_debug_printk(const char *function_name, const char *format, ...)
> > > +{
> > > +	va_list args;
> > > +
> > > +	va_start(args, format);
> > > +	printf("[%s] ", function_name);
> > > +	vprintf(format, args);
> > > +	va_end(args);
> > > +}
> > > +
> > > +void __const_udelay(unsigned long xloops)
> > > +{
> > > +}
> > > +
> > > +struct mutex;
> > > +void mutex_lock_nested(struct mutex *lock, unsigned int subclass)
> > > +{
> > > +}
> > > +
> > > +void mutex_unlock(struct mutex *lock)
> > > +{
> > > +}
> > > +
> > > +struct lock_class_key;
> > > +void
> > > +__mutex_init(struct mutex *lock, const char *name, struct lock_class_key *key)
> > > +{
> > > +}
> > > +
> > > +void usleep_range(unsigned int min, unsigned int max)
> > > +{
> > > +}
> > > +
> > > +struct i2c_adapter;
> > > +int i2c_add_adapter(struct i2c_adapter *adapter)
> > > +{
> > > +	return 0;
> > > +}
> > > +
> > > +void i2c_del_adapter(struct i2c_adapter *adapater)
> > > +{
> > > +}
> > > +
> > > +int
> > > +main(int argc, char *argv[])
> > > +{
> > > +	run_test();
> > > +	return 0;
> > > +}
> > > diff --git a/drivers/gpu/drm/i915/tests/test_intel_dp_link_training.c 
> > > b/drivers/gpu/drm/i915/tests/test_intel_dp_link_training.c
> > > new file mode 100644
> > > index 0000000..11868f8
> > > --- /dev/null
> > > +++ b/drivers/gpu/drm/i915/tests/test_intel_dp_link_training.c
> > > @@ -0,0 +1,464 @@
> > > +/*
> > > + * Copyright © 2015 Intel Corporation
> > > + *
> > > + * Permission is hereby granted, free of charge, to any person obtaining a
> > > + * copy of this software and associated documentation files (the "Software"),
> > > + * to deal in the Software without restriction, including without limitation
> > > + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> > > + * and/or sell copies of the Software, and to permit persons to whom the
> > > + * Software is furnished to do so, subject to the following conditions:
> > > + *
> > > + * The above copyright notice and this permission notice (including the next
> > > + * paragraph) shall be included in all copies or substantial portions of the
> > > + * Software.
> > > + *
> > > + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> > > + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> > > + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
> > > + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> > > + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> > > + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> > > + * IN THE SOFTWARE.
> > > + *
> > > + * Authors:
> > > + *    Ander Conselvan de Oliveira <ander.conselvan.de.oliveira@intel.com>
> > > + *
> > > + */
> > > +
> > > +#include "../intel_dp_link_training.c"
> > > +
> > > +#define drm_dp_dpcd_write	real_drm_dp_dpcd_write
> > > +#include "../../drm_dp_helper.c"
> > > +#undef drm_dp_dpcd_write
> > > +
> > > +void assert(bool condition);
> > > +
> > > +
> > > +struct sink_device {
> > > +	ssize_t (*dpcd_write)(struct sink_device *sink, unsigned int offset,
> > > +			      void *buffer, size_t size);
> > > +	bool (*get_link_status)(struct sink_device *sink,
> > > +				uint8_t link_status[DP_LINK_STATUS_SIZE]);
> > > +
> > > +	struct {
> > > +		bool lane_count_and_bw_set;
> > > +		bool training_pattern_1_set;
> > > +		bool started_with_non_zero_levels;
> > > +		bool cr_done;
> > > +		bool channel_eq_done;
> > > +
> > > +		uint8_t dpcd[0x3000];
> > > +	} data;
> > > +};
> > > +
> > > +/* Fake sink device implementation */
> > > +
> > > +static uint8_t
> > > +sink_device_lane_count(struct sink_device *sink)
> > > +{
> > > +	return sink->data.dpcd[DP_LANE_COUNT_SET];
> > > +}
> > > +
> > > +static uint8_t
> > > +sink_device_get_training_pattern(struct sink_device *sink)
> > > +{
> > > +	return sink->data.dpcd[DP_TRAINING_PATTERN_SET] & DP_TRAINING_PATTERN_MASK;
> > > +}
> > > +
> > > +static uint8_t
> > > +sink_device_get_voltage_swing(struct sink_device *sink, int lane)
> > > +{
> > > +	return sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] &
> > > +		DP_TRAIN_VOLTAGE_SWING_MASK;
> > > +}
> > > +
> > > +static uint8_t
> > > +sink_device_get_pre_emphasis_level(struct sink_device *sink, int lane)
> > > +{
> > > +	return (sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] &
> > > +		 DP_TRAIN_PRE_EMPHASIS_MASK) >> DP_TRAIN_PRE_EMPHASIS_SHIFT;
> > > +}
> > > +
> > > +static void
> > > +sink_device_check_lane_count_and_bw(struct sink_device *sink)
> > > +{
> > > +	if (sink->data.lane_count_and_bw_set)
> > > +		return;
> > > +
> > > +	assert(sink->data.dpcd[DP_TRAINING_PATTERN_SET] == 0);
> > > +
> > > +	if (sink->data.dpcd[DP_LINK_BW_SET] != 0 &&
> > > +	    sink->data.dpcd[DP_LANE_COUNT_SET] != 0)
> > > +		sink->data.lane_count_and_bw_set = true;
> > > +}
> > > +
> > > +static void
> > > +sink_device_check_pattern_1_set(struct sink_device *sink)
> > > +{
> > > +	int lane;
> > > +
> > > +	if (!sink->data.lane_count_and_bw_set ||
> > > +	    sink->data.training_pattern_1_set)
> > > +		return;
> > > +
> > > +	assert(sink_device_get_training_pattern(sink) <= DP_TRAINING_PATTERN_1);
> > > +
> > > +	if (sink_device_get_training_pattern(sink) != DP_TRAINING_PATTERN_1)
> > > +		return;
> > > +
> > > +	assert(sink->data.dpcd[DP_LINK_BW_SET] == DP_LINK_BW_1_62 ||
> > > +	       sink->data.dpcd[DP_LINK_BW_SET] == DP_LINK_BW_2_7);
> > > +
> > > +	assert(sink->data.dpcd[DP_LANE_COUNT_SET] == 1 ||
> > > +		   sink->data.dpcd[DP_LANE_COUNT_SET] == 2 ||
> > > +		   sink->data.dpcd[DP_LANE_COUNT_SET] == 4);
> > > +
> > > +	for (lane = 0; lane < sink_device_lane_count(sink); lane++) {
> > > +		if (sink_device_get_voltage_swing(sink, lane) != 
> > > DP_TRAIN_VOLTAGE_SWING_LEVEL_0 
> > > > > 
> > > +		    sink_device_get_pre_emphasis_level(sink, lane) != 
> > > DP_TRAIN_PRE_EMPH_LEVEL_0)
> > > +			sink->data.started_with_non_zero_levels = true;
> > > +	}
> > > +
> > > +	sink->data.training_pattern_1_set = true;
> > > +}
> > > +
> > > +static void
> > > +sink_device_check_pattern_2_set(struct sink_device *sink)
> > > +{
> > > +	if (!sink->data.cr_done)
> > > +		return;
> > > +
> > > +	assert(sink_device_get_training_pattern(sink) == DP_TRAINING_PATTERN_2);
> > > +}
> > > +
> > > +static void
> > > +sink_device_check_pattern_disable(struct sink_device *sink)
> > > +{
> > > +	if (!sink->data.cr_done || ! sink->data.channel_eq_done)
> > > +		return;
> > > +
> > > +	assert(sink_device_get_training_pattern(sink) == DP_TRAINING_PATTERN_DISABLE);
> > > +}
> > > +
> > > +static ssize_t
> > > +sink_device_dpcd_write(struct sink_device *sink, unsigned int offset,
> > > +		       void *buffer, size_t size)
> > > +{
> > > +	memcpy(sink->data.dpcd + offset, buffer, size);
> > > +
> > > +	sink_device_check_lane_count_and_bw(sink);
> > > +
> > > +	if (!sink->data.cr_done)
> > > +		sink_device_check_pattern_1_set(sink);
> > > +	else if (!sink->data.channel_eq_done)
> > > +		sink_device_check_pattern_2_set(sink);
> > > +	else
> > > +		sink_device_check_pattern_disable(sink);
> > > +
> > > +	return size;
> > > +}
> > > +
> > > +static bool
> > > +sink_device_max_voltage_reached(struct sink_device *sink, int lane)
> > > +{
> > > +	return (sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] & DP_TRAIN_MAX_SWING_REACHED) 
> > > ==
> > > +		DP_TRAIN_MAX_SWING_REACHED;
> > > +}
> > > +
> > > +static bool
> > > +sink_device_max_pre_emphasis_reached(struct sink_device *sink, int lane)
> > > +{
> > > +	return (sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] & 
> > > DP_TRAIN_MAX_PRE_EMPHASIS_REACHED) ==
> > > +		DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
> > > +}
> > > +
> > > +static void
> > > +sink_device_set_adjust_voltage(struct sink_device *sink,
> > > +			       int lane, uint8_t level)
> > > +{
> > > +	int shift = DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT * (lane & 1);
> > > +
> > > +	sink->data.dpcd[DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1)] &=
> > > +		~(DP_ADJUST_VOLTAGE_SWING_LANE0_MASK << shift);
> > > +	sink->data.dpcd[DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1)] |=
> > > +		level << shift;
> > > +}
> > > +
> > > +static void
> > > +sink_device_set_adjust_pre_emphasis(struct sink_device *sink,
> > > +				    int lane, uint8_t level)
> > > +{
> > > +	int shift = DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT * (lane & 1);
> > > +
> > > +	sink->data.dpcd[DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1)] &=
> > > +		~(DP_ADJUST_PRE_EMPHASIS_LANE0_MASK << shift);
> > > +	sink->data.dpcd[DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1)] |=
> > > +		level << (DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT + shift);
> > > +}
> > > +
> > > +static bool
> > > +sink_device_request_higher_voltage_swing(struct sink_device *sink)
> > > +{
> > > +	bool max_reached = false;
> > > +	int lane;
> > > +
> > > +	for (lane = 0; lane < sink_device_lane_count(sink); lane++) {
> > > +		if (sink_device_max_voltage_reached(sink, lane)) {
> > > +			max_reached = true;
> > > +			break;
> > > +		}
> > > +	}
> > > +
> > > +	if (max_reached)
> > > +		return false;
> > > +
> > > +	for (lane = 0; lane < sink_device_lane_count(sink); lane++) {
> > > +		uint8_t new_voltage =
> > > +			sink_device_get_voltage_swing(sink, lane) + 1;
> > > +
> > > +		sink_device_set_adjust_voltage(sink, lane, new_voltage);
> > > +	}
> > > +
> > > +	return true;
> > > +}
> > > +
> > > +static bool
> > > +sink_device_request_higher_pre_emphasis(struct sink_device *sink)
> > > +{
> > > +	bool max_reached = false;
> > > +	int lane;
> > > +
> > > +	for (lane = 0; lane < sink_device_lane_count(sink); lane++) {
> > > +		if (sink_device_max_pre_emphasis_reached(sink, lane)) {
> > > +			max_reached = true;
> > > +			break;
> > > +		}
> > > +	}
> > > +
> > > +	if (max_reached)
> > > +		return false;
> > > +
> > > +	for (lane = 0; lane < sink_device_lane_count(sink); lane++) {
> > > +		uint8_t new_pre_emphasis =
> > > +			sink_device_get_pre_emphasis_level(sink, lane) + 1;
> > > +
> > > +		sink_device_set_adjust_pre_emphasis(sink, lane, new_pre_emphasis);
> > > +	}
> > > +
> > > +	return true;
> > > +}
> > > +
> > > +static void
> > > +sink_device_mark_cr_done(struct sink_device *sink)
> > > +{
> > > +	int lane;
> > > +
> > > +	for (lane = 0; lane < sink_device_lane_count(sink); lane++)
> > > +		sink->data.dpcd[DP_LANE0_1_STATUS + (lane >> 1)] |=
> > > +			DP_LANE_CR_DONE << (4 * (lane & 1));
> > > +
> > > +	sink->data.cr_done = true;
> > > +}
> > > +
> > > +static void
> > > +sink_device_mark_channel_eq_done(struct sink_device *sink)
> > > +{
> > > +	int lane;
> > > +
> > > +	for (lane = 0; lane < sink_device_lane_count(sink); lane++) {
> > > +		uint8_t mask = (DP_LANE_CHANNEL_EQ_DONE | DP_LANE_SYMBOL_LOCKED);
> > > +		sink->data.dpcd[DP_LANE0_1_STATUS + (lane >> 1)] |=
> > > +			mask << (4 * (lane & 1));
> > > +	}
> > > +
> > > +	sink->data.dpcd[DP_LANE_ALIGN_STATUS_UPDATED] |= DP_INTERLANE_ALIGN_DONE;
> > > +
> > > +	sink->data.channel_eq_done = true;
> > > +}
> > > +
> > > +static bool
> > > +sink_device_get_link_status(struct sink_device *sink,
> > > +			    uint8_t link_status[DP_LINK_STATUS_SIZE])
> > > +{
> > > +	if (!sink->data.cr_done) {
> > > +		if (!sink_device_request_higher_voltage_swing(sink))
> > > +			sink_device_mark_cr_done(sink);
> > > +	} else if (!sink->data.channel_eq_done) {
> > > +		if (!sink_device_request_higher_pre_emphasis(sink))
> > > +			sink_device_mark_channel_eq_done(sink);
> > > +	}
> > > +
> > > +	memcpy(link_status, sink->data.dpcd + DP_LANE0_1_STATUS,
> > > +	       DP_LINK_STATUS_SIZE);
> > > +
> > > +	return true;
> > > +}
> > > +
> > > +static void
> > > +sink_device_reset(struct sink_device *sink, int lanes, uint8_t link_bw)
> > > +{
> > > +	memset(&sink->data, 0, sizeof sink->data);
> > > +	sink->data.dpcd[DP_MAX_LINK_RATE] = link_bw;
> > > +	sink->data.dpcd[DP_MAX_LANE_COUNT] = lanes;
> > > +}
> > > +
> > > +static struct sink_device simple_sink = {
> > > +	.get_link_status = sink_device_get_link_status,
> > > +	.dpcd_write = sink_device_dpcd_write,
> > > +};
> > > +
> > > +/* Glue code */
> > > +
> > > +struct test_intel_dp {
> > > +	struct intel_dp dp;
> > > +	struct sink_device *sink;
> > > +	uint8_t link_bw;
> > > +
> > > +	uint8_t max_voltage;
> > > +	uint8_t max_pre_emphasis;
> > > +};
> > > +
> > > +static struct test_intel_dp *
> > > +to_test_intel_dp(struct intel_dp *dp)
> > > +{
> > > +	return container_of(dp, struct test_intel_dp, dp);
> > > +}
> > > +
> > > +void intel_dp_set_idle_link_train(struct intel_dp *intel_dp)
> > > +{
> > > +}
> > > +
> > > +bool intel_dp_source_supports_hbr2(struct intel_dp *intel_dp)
> > > +{
> > > +	return false;
> > > +}
> > > +
> > > +bool
> > > +intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_STATUS_SIZE])
> > > +{
> > > +	struct sink_device *sink = to_test_intel_dp(intel_dp)->sink;
> > > +	return sink->get_link_status(sink, link_status);
> > > +}
> > > +
> > > +void
> > > +intel_dp_update_signal_levels(struct intel_dp *intel_dp)
> > > +{
> > > +}
> > > +
> > > +void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock,
> > > +			   uint8_t *link_bw, uint8_t *rate_select)
> > > +{
> > > +	*link_bw = to_test_intel_dp(intel_dp)->link_bw;
> > > +	*rate_select = 0;
> > > +}
> > > +
> > > +void
> > > +intel_dp_program_link_training_pattern(struct intel_dp *intel_dp,
> > > +				       uint8_t dp_train_pat)
> > > +{
> > > +}
> > > +
> > > +uint8_t
> > > +intel_dp_voltage_max(struct intel_dp *intel_dp)
> > > +{
> > > +	return to_test_intel_dp(intel_dp)->max_voltage <<
> > > +		DP_TRAIN_VOLTAGE_SWING_SHIFT;
> > > +}
> > > +
> > > +uint8_t
> > > +intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t voltage_swing)
> > > +{
> > > +	return to_test_intel_dp(intel_dp)->max_pre_emphasis <<
> > > +		DP_TRAIN_PRE_EMPHASIS_SHIFT;
> > > +}
> > > +
> > > +ssize_t drm_dp_dpcd_write(struct drm_dp_aux *aux, unsigned int offset,
> > > +			  void *buffer, size_t size)
> > > +{
> > > +	struct intel_dp *intel_dp =
> > > +		container_of(aux, struct intel_dp, aux);
> > > +	struct sink_device *sink = to_test_intel_dp(intel_dp)->sink;
> > > +
> > > +	return sink->dpcd_write(sink, offset, buffer, size);
> > > +}
> > > +
> > > +/* --- */
> > > +
> > > +static struct test_intel_dp test_dp;
> > > +
> > > +static void
> > > +do_test(struct sink_device *sink, int lanes, uint8_t link_bw,
> > > +	     uint8_t max_voltage, uint8_t max_pre_emphasis)
> > > +{
> > > +	int lane;
> > > +
> > > +	memset(&test_dp, 0, sizeof test_dp);
> > > +	test_dp.dp.lane_count = lanes;
> > > +	test_dp.link_bw = link_bw;
> > > +	test_dp.sink = sink;
> > > +	test_dp.max_voltage =
> > > +		max_voltage >> DP_TRAIN_VOLTAGE_SWING_SHIFT;
> > > +	test_dp.max_pre_emphasis =
> > > +		max_pre_emphasis >> DP_TRAIN_PRE_EMPHASIS_SHIFT;
> > > +
> > > +	sink_device_reset(sink, lanes, link_bw);
> > > +
> > > +	intel_dp_start_link_train(&test_dp.dp);
> > > +	intel_dp_stop_link_train(&test_dp.dp);
> > > +
> > > +	assert(sink->data.cr_done);
> > > +	assert(sink->data.channel_eq_done);
> > > +
> > > +	for (lane = 0; lane < test_dp.dp.lane_count; lane++) {
> > > +		uint8_t cur_v = sink_device_get_voltage_swing(sink, lane);
> > > +		uint8_t cur_p = sink_device_get_pre_emphasis_level(sink, lane);
> > > +
> > > +		assert(cur_v == test_dp.max_voltage);
> > > +		assert(cur_p == test_dp.max_pre_emphasis);
> > > +	}
> > > +}
> > > +
> > > +int test_lanes[] = {
> > > +	1, 2, 4,
> > > +};
> > > +
> > > +uint8_t test_bw[] = {
> > > +	DP_LINK_BW_1_62,
> > > +	DP_LINK_BW_2_7,
> > > +};
> > > +
> > > +uint8_t test_max_voltage[] = {
> > > +	DP_TRAIN_VOLTAGE_SWING_LEVEL_0,
> > > +	DP_TRAIN_VOLTAGE_SWING_LEVEL_1,
> > > +	DP_TRAIN_VOLTAGE_SWING_LEVEL_2,
> > > +	DP_TRAIN_VOLTAGE_SWING_LEVEL_3,
> > > +};
> > > +
> > > +uint8_t test_max_pre_emphasis[] = {
> > > +	DP_TRAIN_PRE_EMPH_LEVEL_0,
> > > +	DP_TRAIN_PRE_EMPH_LEVEL_1,
> > > +	DP_TRAIN_PRE_EMPH_LEVEL_2,
> > > +	DP_TRAIN_PRE_EMPH_LEVEL_3,
> > > +};
> > > +
> > > +void
> > > +run_test(void)
> > > +{
> > > +	int lane, bw, voltage, emph;
> > > +
> > > +	for (lane = 0; lane < ARRAY_SIZE(test_lanes); lane++)
> > > +	for (bw = 0; bw < ARRAY_SIZE(test_bw); bw++)
> > > +	for (voltage = 0; voltage < ARRAY_SIZE(test_max_voltage); voltage++)
> > > +	for (emph = 0; emph < ARRAY_SIZE(test_max_pre_emphasis); emph++) {
> > > +		DRM_DEBUG_KMS("l%d-bw%d-v%d-pe%d",
> > > +			      test_lanes[lane],
> > > +			      test_bw[bw],
> > > +			      test_max_voltage[voltage],
> > > +			      test_max_pre_emphasis[emph]);
> > > +		do_test(&simple_sink,
> > > +			test_lanes[lane],
> > > +			test_bw[bw],
> > > +			test_max_voltage[voltage],
> > > +			test_max_pre_emphasis[emph]);
> > > +	}
> > > +}
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 31+ messages in thread

* [PATCH 1/2] drm/i915: Make link training state machine code use function pointers
  2015-09-14 13:38           ` Ander Conselvan De Oliveira
  2015-09-15 13:08             ` Ander Conselvan De Oliveira
@ 2015-09-15 13:11             ` Ander Conselvan de Oliveira
  2015-09-15 13:11               ` [PATCH 2/2] drm/i915: Add a link training test Ander Conselvan de Oliveira
  1 sibling, 1 reply; 31+ messages in thread
From: Ander Conselvan de Oliveira @ 2015-09-15 13:11 UTC (permalink / raw)
  To: intel-gfx; +Cc: Ander Conselvan de Oliveira, thomas.wood

Create a series of function pointers in intel_dp for the consumption of
the link training state machine, instead of calling the functions from
intel_dp.c direcly. This creates the insertion points for a link
training unit test, that will be added in the next patch.
---
 drivers/gpu/drm/i915/intel_dp.c               | 36 ++++++++++++++++++++-------
 drivers/gpu/drm/i915/intel_dp_link_training.c | 36 +++++++++++++--------------
 drivers/gpu/drm/i915/intel_drv.h              | 29 ++++++++++-----------
 3 files changed, 58 insertions(+), 43 deletions(-)

diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index d914f95..6422b8a 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -1189,7 +1189,7 @@ intel_dp_sink_rates(struct intel_dp *intel_dp, const int **sink_rates)
 	return (intel_dp_max_link_bw(intel_dp) >> 3) + 1;
 }
 
-bool intel_dp_source_supports_hbr2(struct intel_dp *intel_dp)
+static bool intel_dp_source_supports_hbr2(struct intel_dp *intel_dp)
 {
 	struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
 	struct drm_device *dev = dig_port->base.base.dev;
@@ -1368,8 +1368,8 @@ int intel_dp_rate_select(struct intel_dp *intel_dp, int rate)
 	return rate_to_index(rate, intel_dp->sink_rates);
 }
 
-void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock,
-			   uint8_t *link_bw, uint8_t *rate_select)
+static void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock,
+				  uint8_t *link_bw, uint8_t *rate_select)
 {
 	if (intel_dp->num_sink_rates) {
 		*link_bw = 0;
@@ -3049,7 +3049,7 @@ intel_dp_dpcd_read_wake(struct drm_dp_aux *aux, unsigned int offset,
  * Fetch AUX CH registers 0x202 - 0x207 which contain
  * link status information
  */
-bool
+static bool
 intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_STATUS_SIZE])
 {
 	return intel_dp_dpcd_read_wake(&intel_dp->aux,
@@ -3059,7 +3059,7 @@ intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_
 }
 
 /* These are source-specific values. */
-uint8_t
+static uint8_t
 intel_dp_voltage_max(struct intel_dp *intel_dp)
 {
 	struct drm_device *dev = intel_dp_to_dev(intel_dp);
@@ -3082,7 +3082,7 @@ intel_dp_voltage_max(struct intel_dp *intel_dp)
 		return DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
 }
 
-uint8_t
+static uint8_t
 intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t voltage_swing)
 {
 	struct drm_device *dev = intel_dp_to_dev(intel_dp);
@@ -3570,7 +3570,7 @@ intel_dp_set_signal_levels(struct intel_dp *intel_dp, uint32_t *DP)
 	*DP = (*DP & ~mask) | signal_levels;
 }
 
-void
+static void
 intel_dp_program_link_training_pattern(struct intel_dp *intel_dp,
 				       uint8_t dp_train_pat)
 {
@@ -3584,7 +3584,7 @@ intel_dp_program_link_training_pattern(struct intel_dp *intel_dp,
 	POSTING_READ(intel_dp->output_reg);
 }
 
-void
+static void
 intel_dp_update_signal_levels(struct intel_dp *intel_dp)
 {
 	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
@@ -3597,7 +3597,14 @@ intel_dp_update_signal_levels(struct intel_dp *intel_dp)
 	POSTING_READ(intel_dp->output_reg);
 }
 
-void intel_dp_set_idle_link_train(struct intel_dp *intel_dp)
+static ssize_t
+intel_dp_dpcd_write(struct intel_dp *intel_dp, unsigned int offset,
+		    void *buffer, size_t size)
+{
+	return drm_dp_dpcd_write(&intel_dp->aux, offset, buffer, size);
+}
+
+static void intel_dp_set_idle_link_train(struct intel_dp *intel_dp)
 {
 	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
 	struct drm_device *dev = intel_dig_port->base.base.dev;
@@ -5742,6 +5749,17 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
 	if (HAS_DDI(dev))
 		intel_dp->prepare_link_retrain = intel_ddi_prepare_link_retrain;
 
+	intel_dp->get_link_status = intel_dp_get_link_status;
+	intel_dp->update_signal_levels = intel_dp_update_signal_levels;
+	intel_dp->compute_rate = intel_dp_compute_rate;
+	intel_dp->program_link_training_pattern =
+		intel_dp_program_link_training_pattern;
+	intel_dp->set_idle_link_train = intel_dp_set_idle_link_train;
+	intel_dp->voltage_max = intel_dp_voltage_max;
+	intel_dp->pre_emphasis_max = intel_dp_pre_emphasis_max;
+	intel_dp->dpcd_write = intel_dp_dpcd_write;
+	intel_dp->source_supports_hbr2 = intel_dp_source_supports_hbr2;
+
 	/* Preserve the current hw state. */
 	intel_dp->DP = I915_READ(intel_dp->output_reg);
 	intel_dp->attached_connector = intel_connector;
diff --git a/drivers/gpu/drm/i915/intel_dp_link_training.c b/drivers/gpu/drm/i915/intel_dp_link_training.c
index 4d2bdc0..c2982f8 100644
--- a/drivers/gpu/drm/i915/intel_dp_link_training.c
+++ b/drivers/gpu/drm/i915/intel_dp_link_training.c
@@ -43,11 +43,11 @@ intel_get_adjust_train(struct intel_dp *intel_dp,
 			p = this_p;
 	}
 
-	voltage_max = intel_dp_voltage_max(intel_dp);
+	voltage_max = intel_dp->voltage_max(intel_dp);
 	if (v >= voltage_max)
 		v = voltage_max | DP_TRAIN_MAX_SWING_REACHED;
 
-	preemph_max = intel_dp_pre_emphasis_max(intel_dp, v);
+	preemph_max = intel_dp->pre_emphasis_max(intel_dp, v);
 	if (p >= preemph_max)
 		p = preemph_max | DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
 
@@ -62,7 +62,7 @@ intel_dp_set_link_train(struct intel_dp *intel_dp,
 	uint8_t buf[sizeof(intel_dp->train_set) + 1];
 	int ret, len;
 
-	intel_dp_program_link_training_pattern(intel_dp, dp_train_pat);
+	intel_dp->program_link_training_pattern(intel_dp, dp_train_pat);
 
 	buf[0] = dp_train_pat;
 	if ((dp_train_pat & DP_TRAINING_PATTERN_MASK) ==
@@ -75,8 +75,8 @@ intel_dp_set_link_train(struct intel_dp *intel_dp,
 		len = intel_dp->lane_count + 1;
 	}
 
-	ret = drm_dp_dpcd_write(&intel_dp->aux, DP_TRAINING_PATTERN_SET,
-				buf, len);
+	ret = intel_dp->dpcd_write(intel_dp, DP_TRAINING_PATTERN_SET,
+				   buf, len);
 
 	return ret == len;
 }
@@ -87,7 +87,7 @@ intel_dp_reset_link_train(struct intel_dp *intel_dp,
 {
 	if (!intel_dp->train_set_valid)
 		memset(intel_dp->train_set, 0, sizeof(intel_dp->train_set));
-	intel_dp_update_signal_levels(intel_dp);
+	intel_dp->update_signal_levels(intel_dp);
 	return intel_dp_set_link_train(intel_dp, dp_train_pat);
 }
 
@@ -96,10 +96,10 @@ intel_dp_update_link_train(struct intel_dp *intel_dp)
 {
 	int ret;
 
-	intel_dp_update_signal_levels(intel_dp);
+	intel_dp->update_signal_levels(intel_dp);
 
-	ret = drm_dp_dpcd_write(&intel_dp->aux, DP_TRAINING_LANE0_SET,
-				intel_dp->train_set, intel_dp->lane_count);
+	ret = intel_dp->dpcd_write(intel_dp, DP_TRAINING_LANE0_SET,
+				   intel_dp->train_set, intel_dp->lane_count);
 
 	return ret == intel_dp->lane_count;
 }
@@ -118,22 +118,22 @@ intel_dp_link_training_clock_recovery(struct intel_dp *intel_dp)
 	if (intel_dp->prepare_link_retrain)
 		intel_dp->prepare_link_retrain(intel_dp);
 
-	intel_dp_compute_rate(intel_dp, intel_dp->link_rate,
-			      &link_bw, &rate_select);
+	intel_dp->compute_rate(intel_dp, intel_dp->link_rate,
+			       &link_bw, &rate_select);
 
 	/* Write the link configuration data */
 	link_config[0] = link_bw;
 	link_config[1] = intel_dp->lane_count;
 	if (drm_dp_enhanced_frame_cap(intel_dp->dpcd))
 		link_config[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
-	drm_dp_dpcd_write(&intel_dp->aux, DP_LINK_BW_SET, link_config, 2);
+	intel_dp->dpcd_write(intel_dp, DP_LINK_BW_SET, link_config, 2);
 	if (intel_dp->num_sink_rates)
-		drm_dp_dpcd_write(&intel_dp->aux, DP_LINK_RATE_SET,
+		intel_dp->dpcd_write(intel_dp, DP_LINK_RATE_SET,
 				  &rate_select, 1);
 
 	link_config[0] = 0;
 	link_config[1] = DP_SET_ANSI_8B10B;
-	drm_dp_dpcd_write(&intel_dp->aux, DP_DOWNSPREAD_CTRL, link_config, 2);
+	intel_dp->dpcd_write(intel_dp, DP_DOWNSPREAD_CTRL, link_config, 2);
 
 	intel_dp->DP |= DP_PORT_EN;
 
@@ -150,7 +150,7 @@ intel_dp_link_training_clock_recovery(struct intel_dp *intel_dp)
 	loop_tries = 0;
 	for (;;) {
 		drm_dp_link_train_clock_recovery_delay(intel_dp->dpcd);
-		if (!intel_dp_get_link_status(intel_dp, link_status)) {
+		if (!intel_dp->get_link_status(intel_dp, link_status)) {
 			DRM_ERROR("failed to get link status\n");
 			break;
 		}
@@ -232,7 +232,7 @@ intel_dp_link_training_channel_equalization(struct intel_dp *intel_dp)
 	 * Due to WaDisableHBR2 SKL < B0 is the only exception where TPS3 is
 	 * supported but still not enabled.
 	 */
-	if (intel_dp_source_supports_hbr2(intel_dp) &&
+	if (intel_dp->source_supports_hbr2(intel_dp) &&
 	    drm_dp_tps3_supported(intel_dp->dpcd))
 		training_pattern = DP_TRAINING_PATTERN_3;
 	else if (intel_dp->link_rate == 540000)
@@ -258,7 +258,7 @@ intel_dp_link_training_channel_equalization(struct intel_dp *intel_dp)
 		}
 
 		drm_dp_link_train_channel_eq_delay(intel_dp->dpcd);
-		if (!intel_dp_get_link_status(intel_dp, link_status)) {
+		if (!intel_dp->get_link_status(intel_dp, link_status)) {
 			DRM_ERROR("failed to get link status\n");
 			break;
 		}
@@ -302,7 +302,7 @@ intel_dp_link_training_channel_equalization(struct intel_dp *intel_dp)
 		++tries;
 	}
 
-	intel_dp_set_idle_link_train(intel_dp);
+	intel_dp->set_idle_link_train(intel_dp);
 
 	if (channel_eq) {
 		intel_dp->train_set_valid = true;
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 35ffe27e..bc0de6c 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -771,6 +771,19 @@ struct intel_dp {
 
 	/* This is called before a link training is starterd */
 	void (*prepare_link_retrain)(struct intel_dp *intel_dp);
+	bool (*get_link_status)(struct intel_dp *intel_dp,
+				uint8_t link_status[DP_LINK_STATUS_SIZE]);
+	void (*update_signal_levels)(struct intel_dp *intel_dp);
+	void (*compute_rate)(struct intel_dp *intel_dp, int port_clock,
+			     uint8_t *link_bw, uint8_t *rate_select);
+	void (*program_link_training_pattern)(struct intel_dp *intel_dp,
+					      uint8_t dp_train_pat);
+	void (*set_idle_link_train)(struct intel_dp *intel_dp);
+	uint8_t (*voltage_max)(struct intel_dp *intel_dp);
+	uint8_t (*pre_emphasis_max)(struct intel_dp *intel_dp, uint8_t voltage);
+	ssize_t (*dpcd_write)(struct intel_dp *intel_dp, unsigned int offset,
+			      void *buffer, size_t size);
+	bool (*source_supports_hbr2)(struct intel_dp *intel_dp);
 
 	bool train_set_valid;
 
@@ -1217,22 +1230,6 @@ void intel_edp_drrs_invalidate(struct drm_device *dev,
 void intel_edp_drrs_flush(struct drm_device *dev, unsigned frontbuffer_bits);
 void hsw_dp_set_ddi_pll_sel(struct intel_crtc_state *pipe_config);
 
-void
-intel_dp_program_link_training_pattern(struct intel_dp *intel_dp,
-				       uint8_t dp_train_pat);
-void
-intel_dp_update_signal_levels(struct intel_dp *intel_dp);
-void intel_dp_set_idle_link_train(struct intel_dp *intel_dp);
-uint8_t
-intel_dp_voltage_max(struct intel_dp *intel_dp);
-uint8_t
-intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t voltage_swing);
-void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock,
-			   uint8_t *link_bw, uint8_t *rate_select);
-bool intel_dp_source_supports_hbr2(struct intel_dp *intel_dp);
-bool
-intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_STATUS_SIZE]);
-
 /* intel_dp_mst.c */
 int intel_dp_mst_encoder_init(struct intel_digital_port *intel_dig_port, int conn_id);
 void intel_dp_mst_encoder_cleanup(struct intel_digital_port *intel_dig_port);
-- 
2.4.3

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [PATCH 2/2] drm/i915: Add a link training test
  2015-09-15 13:11             ` [PATCH 1/2] drm/i915: Make link training state machine code use function pointers Ander Conselvan de Oliveira
@ 2015-09-15 13:11               ` Ander Conselvan de Oliveira
  0 siblings, 0 replies; 31+ messages in thread
From: Ander Conselvan de Oliveira @ 2015-09-15 13:11 UTC (permalink / raw)
  To: intel-gfx; +Cc: Ander Conselvan de Oliveira, thomas.wood

---
 drivers/gpu/drm/i915/Kconfig                       |  10 +
 drivers/gpu/drm/i915/Makefile                      |   5 +
 drivers/gpu/drm/i915/i915_drv.c                    |   2 +
 drivers/gpu/drm/i915/i915_drv.h                    |   8 +
 .../drm/i915/tests/test_intel_dp_link_training.c   | 467 +++++++++++++++++++++
 drivers/gpu/drm/i915/tests/tests.c                 |   7 +
 drivers/gpu/drm/i915/tests/tests.h                 |   6 +
 7 files changed, 505 insertions(+)
 create mode 100644 drivers/gpu/drm/i915/tests/test_intel_dp_link_training.c
 create mode 100644 drivers/gpu/drm/i915/tests/tests.c
 create mode 100644 drivers/gpu/drm/i915/tests/tests.h

diff --git a/drivers/gpu/drm/i915/Kconfig b/drivers/gpu/drm/i915/Kconfig
index 051eab3..fb42a9f 100644
--- a/drivers/gpu/drm/i915/Kconfig
+++ b/drivers/gpu/drm/i915/Kconfig
@@ -47,3 +47,13 @@ config DRM_I915_PRELIMINARY_HW_SUPPORT
 	  option changes the default for that module option.
 
 	  If in doubt, say "N".
+
+config DRM_I915_UNIT_TESTS
+	bool "Enable execution of unit tests on module load"
+	depends on DRM_I915
+	default n
+	help
+	  Choose this option to run a series of unit tests when the i915
+	  module is loaded.
+
+	  If in doubt, say "N".
diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
index 0851de07..bd7e283 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -97,6 +97,11 @@ i915-y += i915_vgpu.o
 # legacy horrors
 i915-y += i915_dma.o
 
+# Unit tests
+i915-$(CONFIG_DRM_I915_UNIT_TESTS) += \
+	tests/tests.o \
+	tests/test_intel_dp_link_training.o
+
 obj-$(CONFIG_DRM_I915)  += i915.o
 
 CFLAGS_i915_trace_points.o := -I$(src)
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index bdec64c..2494667 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -1707,6 +1707,8 @@ static int __init i915_init(void)
 {
 	driver.num_ioctls = i915_max_ioctl;
 
+	i915_run_unit_tests();
+
 	/*
 	 * Enable KMS by default, unless explicitly overriden by
 	 * either the i915.modeset prarameter or by the
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 3bf8a9b..39c58d1 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -3502,4 +3502,12 @@ static inline void i915_trace_irq_get(struct intel_engine_cs *ring,
 		i915_gem_request_assign(&ring->trace_irq_req, req);
 }
 
+#ifdef CONFIG_DRM_I915_UNIT_TESTS
+void i915_run_unit_tests(void);
+#else
+static inline void i915_run_unit_tests(void)
+{
+}
+#endif
+
 #endif
diff --git a/drivers/gpu/drm/i915/tests/test_intel_dp_link_training.c b/drivers/gpu/drm/i915/tests/test_intel_dp_link_training.c
new file mode 100644
index 0000000..ab5de74
--- /dev/null
+++ b/drivers/gpu/drm/i915/tests/test_intel_dp_link_training.c
@@ -0,0 +1,467 @@
+/*
+ * Copyright © 2015 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Authors:
+ *    Ander Conselvan de Oliveira <ander.conselvan.de.oliveira@intel.com>
+ *
+ */
+
+#include "../intel_drv.h"
+
+#define assert(x)	WARN_ON(!(x))
+
+struct sink_device {
+	ssize_t (*dpcd_write)(struct sink_device *sink, unsigned int offset,
+			      void *buffer, size_t size);
+	bool (*get_link_status)(struct sink_device *sink,
+				uint8_t link_status[DP_LINK_STATUS_SIZE]);
+
+	struct {
+		bool lane_count_and_bw_set;
+		bool training_pattern_1_set;
+		bool started_with_non_zero_levels;
+		bool cr_done;
+		bool channel_eq_done;
+
+		uint8_t dpcd[0x3000];
+	} data;
+};
+
+/* Fake sink device implementation */
+
+static uint8_t
+sink_device_lane_count(struct sink_device *sink)
+{
+	return sink->data.dpcd[DP_LANE_COUNT_SET];
+}
+
+static uint8_t
+sink_device_get_training_pattern(struct sink_device *sink)
+{
+	return sink->data.dpcd[DP_TRAINING_PATTERN_SET] & DP_TRAINING_PATTERN_MASK;
+}
+
+static uint8_t
+sink_device_get_voltage_swing(struct sink_device *sink, int lane)
+{
+	return sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] &
+		DP_TRAIN_VOLTAGE_SWING_MASK;
+}
+
+static uint8_t
+sink_device_get_pre_emphasis_level(struct sink_device *sink, int lane)
+{
+	return (sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] &
+		 DP_TRAIN_PRE_EMPHASIS_MASK) >> DP_TRAIN_PRE_EMPHASIS_SHIFT;
+}
+
+static void
+sink_device_check_lane_count_and_bw(struct sink_device *sink)
+{
+	if (sink->data.lane_count_and_bw_set)
+		return;
+
+	assert(sink->data.dpcd[DP_TRAINING_PATTERN_SET] == 0);
+
+	if (sink->data.dpcd[DP_LINK_BW_SET] != 0 &&
+	    sink->data.dpcd[DP_LANE_COUNT_SET] != 0)
+		sink->data.lane_count_and_bw_set = true;
+}
+
+static void
+sink_device_check_pattern_1_set(struct sink_device *sink)
+{
+	int lane;
+
+	if (!sink->data.lane_count_and_bw_set ||
+	    sink->data.training_pattern_1_set)
+		return;
+
+	assert(sink_device_get_training_pattern(sink) <= DP_TRAINING_PATTERN_1);
+
+	if (sink_device_get_training_pattern(sink) != DP_TRAINING_PATTERN_1)
+		return;
+
+	assert(sink->data.dpcd[DP_LINK_BW_SET] == DP_LINK_BW_1_62 ||
+	       sink->data.dpcd[DP_LINK_BW_SET] == DP_LINK_BW_2_7);
+
+	assert(sink->data.dpcd[DP_LANE_COUNT_SET] == 1 ||
+		   sink->data.dpcd[DP_LANE_COUNT_SET] == 2 ||
+		   sink->data.dpcd[DP_LANE_COUNT_SET] == 4);
+
+	for (lane = 0; lane < sink_device_lane_count(sink); lane++) {
+		if (sink_device_get_voltage_swing(sink, lane) != DP_TRAIN_VOLTAGE_SWING_LEVEL_0 ||
+		    sink_device_get_pre_emphasis_level(sink, lane) != DP_TRAIN_PRE_EMPH_LEVEL_0)
+			sink->data.started_with_non_zero_levels = true;
+	}
+
+	sink->data.training_pattern_1_set = true;
+}
+
+static void
+sink_device_check_pattern_2_set(struct sink_device *sink)
+{
+	if (!sink->data.cr_done)
+		return;
+
+	assert(sink_device_get_training_pattern(sink) == DP_TRAINING_PATTERN_2);
+}
+
+static void
+sink_device_check_pattern_disable(struct sink_device *sink)
+{
+	if (!sink->data.cr_done || ! sink->data.channel_eq_done)
+		return;
+
+	assert(sink_device_get_training_pattern(sink) == DP_TRAINING_PATTERN_DISABLE);
+}
+
+static ssize_t
+sink_device_dpcd_write(struct sink_device *sink, unsigned int offset,
+		       void *buffer, size_t size)
+{
+	memcpy(sink->data.dpcd + offset, buffer, size);
+
+	sink_device_check_lane_count_and_bw(sink);
+
+	if (!sink->data.cr_done)
+		sink_device_check_pattern_1_set(sink);
+	else if (!sink->data.channel_eq_done)
+		sink_device_check_pattern_2_set(sink);
+	else
+		sink_device_check_pattern_disable(sink);
+
+	return size;
+}
+
+static bool
+sink_device_max_voltage_reached(struct sink_device *sink, int lane)
+{
+	return (sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] & DP_TRAIN_MAX_SWING_REACHED) ==
+		DP_TRAIN_MAX_SWING_REACHED;
+}
+
+static bool
+sink_device_max_pre_emphasis_reached(struct sink_device *sink, int lane)
+{
+	return (sink->data.dpcd[DP_TRAINING_LANE0_SET + lane] & DP_TRAIN_MAX_PRE_EMPHASIS_REACHED) ==
+		DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
+}
+
+static void
+sink_device_set_adjust_voltage(struct sink_device *sink,
+			       int lane, uint8_t level)
+{
+	int shift = DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT * (lane & 1);
+
+	sink->data.dpcd[DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1)] &=
+		~(DP_ADJUST_VOLTAGE_SWING_LANE0_MASK << shift);
+	sink->data.dpcd[DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1)] |=
+		level << shift;
+}
+
+static void
+sink_device_set_adjust_pre_emphasis(struct sink_device *sink,
+				    int lane, uint8_t level)
+{
+	int shift = DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT * (lane & 1);
+
+	sink->data.dpcd[DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1)] &=
+		~(DP_ADJUST_PRE_EMPHASIS_LANE0_MASK << shift);
+	sink->data.dpcd[DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1)] |=
+		level << (DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT + shift);
+}
+
+static bool
+sink_device_request_higher_voltage_swing(struct sink_device *sink)
+{
+	bool max_reached = false;
+	int lane;
+
+	for (lane = 0; lane < sink_device_lane_count(sink); lane++) {
+		if (sink_device_max_voltage_reached(sink, lane)) {
+			max_reached = true;
+			break;
+		}
+	}
+
+	if (max_reached)
+		return false;
+
+	for (lane = 0; lane < sink_device_lane_count(sink); lane++) {
+		uint8_t new_voltage =
+			sink_device_get_voltage_swing(sink, lane) + 1;
+
+		sink_device_set_adjust_voltage(sink, lane, new_voltage);
+	}
+
+	return true;
+}
+
+static bool
+sink_device_request_higher_pre_emphasis(struct sink_device *sink)
+{
+	bool max_reached = false;
+	int lane;
+
+	for (lane = 0; lane < sink_device_lane_count(sink); lane++) {
+		if (sink_device_max_pre_emphasis_reached(sink, lane)) {
+			max_reached = true;
+			break;
+		}
+	}
+
+	if (max_reached)
+		return false;
+
+	for (lane = 0; lane < sink_device_lane_count(sink); lane++) {
+		uint8_t new_pre_emphasis =
+			sink_device_get_pre_emphasis_level(sink, lane) + 1;
+
+		sink_device_set_adjust_pre_emphasis(sink, lane, new_pre_emphasis);
+	}
+
+	return true;
+}
+
+static void
+sink_device_mark_cr_done(struct sink_device *sink)
+{
+	int lane;
+
+	for (lane = 0; lane < sink_device_lane_count(sink); lane++)
+		sink->data.dpcd[DP_LANE0_1_STATUS + (lane >> 1)] |=
+			DP_LANE_CR_DONE << (4 * (lane & 1));
+
+	sink->data.cr_done = true;
+}
+
+static void
+sink_device_mark_channel_eq_done(struct sink_device *sink)
+{
+	int lane;
+
+	for (lane = 0; lane < sink_device_lane_count(sink); lane++) {
+		uint8_t mask = (DP_LANE_CHANNEL_EQ_DONE | DP_LANE_SYMBOL_LOCKED);
+		sink->data.dpcd[DP_LANE0_1_STATUS + (lane >> 1)] |=
+			mask << (4 * (lane & 1));
+	}
+
+	sink->data.dpcd[DP_LANE_ALIGN_STATUS_UPDATED] |= DP_INTERLANE_ALIGN_DONE;
+
+	sink->data.channel_eq_done = true;
+}
+
+static bool
+sink_device_get_link_status(struct sink_device *sink,
+			    uint8_t link_status[DP_LINK_STATUS_SIZE])
+{
+	if (!sink->data.cr_done) {
+		if (!sink_device_request_higher_voltage_swing(sink))
+			sink_device_mark_cr_done(sink);
+	} else if (!sink->data.channel_eq_done) {
+		if (!sink_device_request_higher_pre_emphasis(sink))
+			sink_device_mark_channel_eq_done(sink);
+	}
+
+	memcpy(link_status, sink->data.dpcd + DP_LANE0_1_STATUS,
+	       DP_LINK_STATUS_SIZE);
+
+	return true;
+}
+
+static void
+sink_device_reset(struct sink_device *sink, int lanes, uint8_t link_bw)
+{
+	memset(&sink->data, 0, sizeof sink->data);
+	sink->data.dpcd[DP_MAX_LINK_RATE] = link_bw;
+	sink->data.dpcd[DP_MAX_LANE_COUNT] = lanes;
+}
+
+static struct sink_device simple_sink = {
+	.get_link_status = sink_device_get_link_status,
+	.dpcd_write = sink_device_dpcd_write,
+};
+
+/* Glue code */
+
+struct test_intel_dp {
+	struct intel_dp dp;
+	struct sink_device *sink;
+	uint8_t link_bw;
+
+	uint8_t max_voltage;
+	uint8_t max_pre_emphasis;
+};
+
+static struct test_intel_dp *
+to_test_intel_dp(struct intel_dp *dp)
+{
+	return container_of(dp, struct test_intel_dp, dp);
+}
+
+void test_dp_set_idle_link_train(struct intel_dp *intel_dp)
+{
+}
+
+bool
+test_dp_get_link_status(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_STATUS_SIZE])
+{
+	struct sink_device *sink = to_test_intel_dp(intel_dp)->sink;
+	return sink->get_link_status(sink, link_status);
+}
+
+void
+test_dp_update_signal_levels(struct intel_dp *intel_dp)
+{
+}
+
+void test_dp_compute_rate(struct intel_dp *intel_dp, int port_clock,
+			   uint8_t *link_bw, uint8_t *rate_select)
+{
+	*link_bw = to_test_intel_dp(intel_dp)->link_bw;
+	*rate_select = 0;
+}
+
+void
+test_dp_program_link_training_pattern(struct intel_dp *intel_dp,
+				       uint8_t dp_train_pat)
+{
+}
+
+uint8_t
+test_dp_voltage_max(struct intel_dp *intel_dp)
+{
+	return to_test_intel_dp(intel_dp)->max_voltage <<
+		DP_TRAIN_VOLTAGE_SWING_SHIFT;
+}
+
+uint8_t
+test_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t voltage_swing)
+{
+	return to_test_intel_dp(intel_dp)->max_pre_emphasis <<
+		DP_TRAIN_PRE_EMPHASIS_SHIFT;
+}
+
+ssize_t test_dp_dpcd_write(struct intel_dp *intel_dp, unsigned int offset,
+			   void *buffer, size_t size)
+{
+	struct sink_device *sink = to_test_intel_dp(intel_dp)->sink;
+	return sink->dpcd_write(sink, offset, buffer, size);
+}
+
+static bool test_dp_source_supports_hbr2(struct intel_dp *dp)
+{
+	return false;
+}
+
+/* --- */
+
+static struct test_intel_dp test_dp;
+
+static void
+do_test(struct sink_device *sink, int lanes, uint8_t link_bw,
+	     uint8_t max_voltage, uint8_t max_pre_emphasis)
+{
+	int lane;
+
+	memset(&test_dp, 0, sizeof test_dp);
+	test_dp.dp.lane_count = lanes;
+	test_dp.link_bw = link_bw;
+	test_dp.sink = sink;
+	test_dp.max_voltage =
+		max_voltage >> DP_TRAIN_VOLTAGE_SWING_SHIFT;
+	test_dp.max_pre_emphasis =
+		max_pre_emphasis >> DP_TRAIN_PRE_EMPHASIS_SHIFT;
+
+	test_dp.dp.get_link_status = test_dp_get_link_status;
+	test_dp.dp.set_idle_link_train = test_dp_set_idle_link_train;
+	test_dp.dp.update_signal_levels = test_dp_update_signal_levels;
+	test_dp.dp.compute_rate = test_dp_compute_rate;
+	test_dp.dp.program_link_training_pattern =
+		test_dp_program_link_training_pattern;
+	test_dp.dp.voltage_max = test_dp_voltage_max;
+	test_dp.dp.pre_emphasis_max = test_dp_pre_emphasis_max;
+	test_dp.dp.dpcd_write = test_dp_dpcd_write;
+	test_dp.dp.source_supports_hbr2 = test_dp_source_supports_hbr2;
+
+	sink_device_reset(sink, lanes, link_bw);
+
+	intel_dp_start_link_train(&test_dp.dp);
+	intel_dp_stop_link_train(&test_dp.dp);
+
+	assert(sink->data.cr_done);
+	assert(sink->data.channel_eq_done);
+
+	for (lane = 0; lane < test_dp.dp.lane_count; lane++) {
+		uint8_t cur_v = sink_device_get_voltage_swing(sink, lane);
+		uint8_t cur_p = sink_device_get_pre_emphasis_level(sink, lane);
+
+		assert(cur_v == test_dp.max_voltage);
+		assert(cur_p == test_dp.max_pre_emphasis);
+	}
+}
+
+int test_lanes[] = {
+	1, 2, 4,
+};
+
+uint8_t test_bw[] = {
+	DP_LINK_BW_1_62,
+	DP_LINK_BW_2_7,
+};
+
+uint8_t test_max_voltage[] = {
+	DP_TRAIN_VOLTAGE_SWING_LEVEL_0,
+	DP_TRAIN_VOLTAGE_SWING_LEVEL_1,
+	DP_TRAIN_VOLTAGE_SWING_LEVEL_2,
+	DP_TRAIN_VOLTAGE_SWING_LEVEL_3,
+};
+
+uint8_t test_max_pre_emphasis[] = {
+	DP_TRAIN_PRE_EMPH_LEVEL_0,
+	DP_TRAIN_PRE_EMPH_LEVEL_1,
+	DP_TRAIN_PRE_EMPH_LEVEL_2,
+	DP_TRAIN_PRE_EMPH_LEVEL_3,
+};
+
+void
+run_link_training_tests(void)
+{
+	int lane, bw, voltage, emph;
+
+	for (lane = 0; lane < ARRAY_SIZE(test_lanes); lane++)
+	for (bw = 0; bw < ARRAY_SIZE(test_bw); bw++)
+	for (voltage = 0; voltage < ARRAY_SIZE(test_max_voltage); voltage++)
+	for (emph = 0; emph < ARRAY_SIZE(test_max_pre_emphasis); emph++) {
+		DRM_DEBUG_KMS("l%d-bw%d-v%d-pe%d",
+			      test_lanes[lane],
+			      test_bw[bw],
+			      test_max_voltage[voltage],
+			      test_max_pre_emphasis[emph]);
+		do_test(&simple_sink,
+			test_lanes[lane],
+			test_bw[bw],
+			test_max_voltage[voltage],
+			test_max_pre_emphasis[emph]);
+	}
+}
diff --git a/drivers/gpu/drm/i915/tests/tests.c b/drivers/gpu/drm/i915/tests/tests.c
new file mode 100644
index 0000000..8e4344a
--- /dev/null
+++ b/drivers/gpu/drm/i915/tests/tests.c
@@ -0,0 +1,7 @@
+#include "tests.h"
+
+void
+i915_run_unit_tests(void)
+{
+	run_link_training_tests();
+}
diff --git a/drivers/gpu/drm/i915/tests/tests.h b/drivers/gpu/drm/i915/tests/tests.h
new file mode 100644
index 0000000..400b523
--- /dev/null
+++ b/drivers/gpu/drm/i915/tests/tests.h
@@ -0,0 +1,6 @@
+#ifndef _I915_TESTS_H_
+#define _I915_TESTS_H_
+
+void run_link_training_tests(void);
+
+#endif /* _I915_TESTS_H_ */
-- 
2.4.3

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply related	[flat|nested] 31+ messages in thread

* Re: [PATCH 0/8] [RFC] Link training test
  2015-09-08 12:27 [PATCH 0/8] [RFC] Link training test Ander Conselvan de Oliveira
                   ` (8 preceding siblings ...)
  2015-09-08 12:28 ` [PATCH i-g-t] Add a link training test Ander Conselvan de Oliveira
@ 2015-09-22 15:12 ` Daniel Vetter
  9 siblings, 0 replies; 31+ messages in thread
From: Daniel Vetter @ 2015-09-22 15:12 UTC (permalink / raw)
  To: Ander Conselvan de Oliveira
  Cc: Jani Nikula, intel-gfx, Thierry Reding, dri-devel

Thierry Redding is working on some improved dp helper libraries for link
training. Would make lots of sense to have all that aligned. Adding him
and dri-devel.
-Daniel

On Tue, Sep 8, 2015 at 2:27 PM, Ander Conselvan de Oliveira <ander.conselvan.de.oliveira@intel.com> wrote:
> Hi,
>
> There's been some discussion on improving our link training code, with
> one of the ideas being a complete rewrite of the state machine. However,
> a concern was raised over the risk of regressions. The code we have has
> seen a lot of real world testing, and it would take a long time for any
> new code to get that same exposure. On top of that, there is no explicit
> igt coverage for link training since it is very dependent on the hw
> setup.
>
> Since gathering an extensive set of hardware combinations for getting the
> appropriate test coverage is really difficult, I believe that we should
> aim at testing the link training code in isolation. That's what this RFC
> is about.
>
> Most of the kernel patches in this series are small changes to the hw
> independent part of the link training code that make it possible to move
> it to a separate file.  Then, in the igt patch, a test is created the
> wraps a fake DP sink device around the link training code. The behavior
> of that sink device is pretty simple for now, it just tries to get the
> maximum voltage swing and pre-emphasis level supported. We could do some
> git archeology of the link training code to try to find some more tests
> that would make sense.
>
> I had to make a lot of hacks to get that thing to compile, so that's
> one of the things I'd like to hear comments on. And also on the general
> approach.
>
> Thanks,
> Ander
>
> --
> 2.4.3
>
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/intel-gfx



-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH] drm/i915: Add link training test
  2015-09-15 13:08             ` Ander Conselvan De Oliveira
@ 2015-09-23  8:24               ` Daniel Vetter
  2015-09-23  9:18                 ` Ander Conselvan De Oliveira
  0 siblings, 1 reply; 31+ messages in thread
From: Daniel Vetter @ 2015-09-23  8:24 UTC (permalink / raw)
  To: Ander Conselvan De Oliveira; +Cc: intel-gfx, thomas.wood

On Tue, Sep 15, 2015 at 04:08:53PM +0300, Ander Conselvan De Oliveira wrote:
> On Mon, 2015-09-14 at 16:38 +0300, Ander Conselvan De Oliveira wrote:
> > On Mon, 2015-09-14 at 15:11 +0200, Daniel Vetter wrote:
> > > On Mon, Sep 14, 2015 at 02:51:51PM +0300, Ander Conselvan de Oliveira wrote:
> > > > ---
> > > > 
> > > > On Fri, 2015-09-11 at 17:11 +0300, Ander Conselvan de Oliveira wrote:
> > > > > On Wed, 2015-09-09 at 11:33 +0100, Thomas Wood wrote:
> > > > > > On 8 September 2015 at 13:28, Ander Conselvan de Oliveira
> > > > > > <ander.conselvan.de.oliveira@intel.com> wrote:
> > > > > > > 
> > > > > 
> > > > > > > diff --git a/link-training-test/Makefile b/link-training-test/Makefile
> > > > > > 
> > > > > > If this is meant to be part of the test suite, then it needs to be in
> > > > > > the tests directory and use the igt test infrastructure. Otherwise it
> > > > > > should be placed in tools or tools/link-training-test.
> > > > > 
> > > > > I made the test use the igt infrastructure, but I'm not sure if this is
> > > > > a good fit for it. The dependency on the kernel is on build time, but
> > > > > once compiled this can be run on any machine. This can also introduce
> > > > > build failures if the test is not kept in sync with the driver source.
> > > > > Ideally that a failure to build this would be reported as the test
> > > > > failing, but I have no idea of how to achieve that.
> > > > 
> > > > Alternatively, this could be in the kernel source tree directly. This
> > > > patch adds a test subdir to the i915 source dir, containing the link
> > > > training test. The test is compiled as part of the normal build using
> > > > the extra-y variable so that it doesn't get linked to the final kernel.
> > > > 
> > > > When make is run from the tests directory, a thin wrapper around the
> > > > tests is built and linked to the object file compiled as part of the
> > > > kernel build. Running make run_tests from the test dir runs the test
> > > > and reports success or failure.
> > > > 
> > > > Any thoughts?
> > > 
> > > I think there's some precedence in other subsystems to integrate unit
> > > tests directly in the kernel, e.g. locking selftest or similar things.
> > > Usual approach is to either have a special module (but that often means
> > > piles of EXPORT_SYMBOL only for that selftest module). Or just a y/n
> > > Kconfig option which enables that code and runs all the self/unit tests
> > > when the module loads.
> > > 
> > > I'd go with that approach since it's simpler. And we'd only need to tell
> > > QA to enable that Kconfig option for more testing.
> > 
> > I'll have a look into that Kconfig approach, but there's a couple of things
> > I like about having the unit test as user space binaries:
> > 
> >   - there's no need to boot the newly compiled kernel, so doing a test run
> >     is super fast;
> >   - the binaries can be debugged with gdb just like other user space stuff.
> 
> I implemented the test using the Kconfig approach, and it seems to work well
> without impacting the points above. I added the call to run the test as the
> first thing in i915_init(), and with the driver compiled built-in, running
> the kernel under qemu will run the tests. And qemu can also provide a gdb
> remote target.
> 
> One thing might be a problem though. With the previous approach, the
> functions overriden by the test where simply reimplemented in the new binary.
> But now the test is linked to the entire driver, so that's not possible. To
> work around that, I had to add function pointers to all the functions called
> by the link training state machine to intel_dp. I don't think that method
> scales well.
> 
> I'll send update patches for reference as replies to this mail.

I had a few discussions about this at XDC and I know think doing this is
userspace is better:
- Faster to run tests (since no module reloading required).
- Nouveau is developed in userspace and would like to reuse shared link
  training code too if possible from the dp helpers.

So I'm leaning towards scaffolding in userspace now. Might be good to
check out how the nouveau userspace runtime works just to steal a few
tricks.

Also I think longer-term we should move the link-training code into the
shared dp helpers, but that's another step later on.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
http://blog.ffwll.ch
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH] drm/i915: Add link training test
  2015-09-23  8:24               ` Daniel Vetter
@ 2015-09-23  9:18                 ` Ander Conselvan De Oliveira
  2015-09-23  9:38                     ` Daniel Vetter
  0 siblings, 1 reply; 31+ messages in thread
From: Ander Conselvan De Oliveira @ 2015-09-23  9:18 UTC (permalink / raw)
  To: Daniel Vetter; +Cc: intel-gfx, thomas.wood, martin.peres

On Wed, 2015-09-23 at 10:24 +0200, Daniel Vetter wrote:
> On Tue, Sep 15, 2015 at 04:08:53PM +0300, Ander Conselvan De Oliveira wrote:
> > On Mon, 2015-09-14 at 16:38 +0300, Ander Conselvan De Oliveira wrote:
> > > On Mon, 2015-09-14 at 15:11 +0200, Daniel Vetter wrote:
> > > > On Mon, Sep 14, 2015 at 02:51:51PM +0300, Ander Conselvan de Oliveira wrote:
> > > > > ---
> > > > > 
> > > > > On Fri, 2015-09-11 at 17:11 +0300, Ander Conselvan de Oliveira wrote:
> > > > > > On Wed, 2015-09-09 at 11:33 +0100, Thomas Wood wrote:
> > > > > > > On 8 September 2015 at 13:28, Ander Conselvan de Oliveira
> > > > > > > <ander.conselvan.de.oliveira@intel.com> wrote:
> > > > > > > > 
> > > > > > 
> > > > > > > > diff --git a/link-training-test/Makefile b/link-training-test/Makefile
> > > > > > > 
> > > > > > > If this is meant to be part of the test suite, then it needs to be in
> > > > > > > the tests directory and use the igt test infrastructure. Otherwise it
> > > > > > > should be placed in tools or tools/link-training-test.
> > > > > > 
> > > > > > I made the test use the igt infrastructure, but I'm not sure if this is
> > > > > > a good fit for it. The dependency on the kernel is on build time, but
> > > > > > once compiled this can be run on any machine. This can also introduce
> > > > > > build failures if the test is not kept in sync with the driver source.
> > > > > > Ideally that a failure to build this would be reported as the test
> > > > > > failing, but I have no idea of how to achieve that.
> > > > > 
> > > > > Alternatively, this could be in the kernel source tree directly. This
> > > > > patch adds a test subdir to the i915 source dir, containing the link
> > > > > training test. The test is compiled as part of the normal build using
> > > > > the extra-y variable so that it doesn't get linked to the final kernel.
> > > > > 
> > > > > When make is run from the tests directory, a thin wrapper around the
> > > > > tests is built and linked to the object file compiled as part of the
> > > > > kernel build. Running make run_tests from the test dir runs the test
> > > > > and reports success or failure.
> > > > > 
> > > > > Any thoughts?
> > > > 
> > > > I think there's some precedence in other subsystems to integrate unit
> > > > tests directly in the kernel, e.g. locking selftest or similar things.
> > > > Usual approach is to either have a special module (but that often means
> > > > piles of EXPORT_SYMBOL only for that selftest module). Or just a y/n
> > > > Kconfig option which enables that code and runs all the self/unit tests
> > > > when the module loads.
> > > > 
> > > > I'd go with that approach since it's simpler. And we'd only need to tell
> > > > QA to enable that Kconfig option for more testing.
> > > 
> > > I'll have a look into that Kconfig approach, but there's a couple of things
> > > I like about having the unit test as user space binaries:
> > > 
> > >   - there's no need to boot the newly compiled kernel, so doing a test run
> > >     is super fast;
> > >   - the binaries can be debugged with gdb just like other user space stuff.
> > 
> > I implemented the test using the Kconfig approach, and it seems to work well
> > without impacting the points above. I added the call to run the test as the
> > first thing in i915_init(), and with the driver compiled built-in, running
> > the kernel under qemu will run the tests. And qemu can also provide a gdb
> > remote target.
> > 
> > One thing might be a problem though. With the previous approach, the
> > functions overriden by the test where simply reimplemented in the new binary.
> > But now the test is linked to the entire driver, so that's not possible. To
> > work around that, I had to add function pointers to all the functions called
> > by the link training state machine to intel_dp. I don't think that method
> > scales well.
> > 
> > I'll send update patches for reference as replies to this mail.
> 
> I had a few discussions about this at XDC and I know think doing this is
> userspace is better:
> - Faster to run tests (since no module reloading required).
> - Nouveau is developed in userspace and would like to reuse shared link
>   training code too if possible from the dp helpers.
> 
> So I'm leaning towards scaffolding in userspace now. Might be good to
> check out how the nouveau userspace runtime works just to steal a few
> tricks.

I chatted with Martin Peres about this and had a look at the code. There are a few interesting
tricks in Nouveau indeed. They have user space implementation for some kernel internals such as
mutexes, ioremap on top of libpciaccess, etc. and an extensive set of stubs.

The one thing that wouldn't be easy to take advantage of is the build integration. Their upstream
repository is not actually a kernel tree. It contains their drm code, the same that ends up in the
kernel plus all the user space stuff. There's a script that converts the commits from that
repository for inclusion in the kernel tree.

Ander


> Also I think longer-term we should move the link-training code into the
> shared dp helpers, but that's another step later on.
> -Daniel
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Shared scaffolding to unit-test kernel code in userspace (was Re: [Intel-gfx] [PATCH] drm/i915: Add link training test)
@ 2015-09-23  9:38                     ` Daniel Vetter
  0 siblings, 0 replies; 31+ messages in thread
From: Daniel Vetter @ 2015-09-23  9:38 UTC (permalink / raw)
  To: Ander Conselvan De Oliveira
  Cc: intel-gfx, Thomas Wood, Martin Peres, Ben Skeggs, Nouveau Dev,
	Shuah Khan, Greg KH, Linux Kernel Mailing List

On Wed, Sep 23, 2015 at 11:18 AM, Ander Conselvan De Oliveira <conselvan2@gmail.com> wrote:
> On Wed, 2015-09-23 at 10:24 +0200, Daniel Vetter wrote:
>> On Tue, Sep 15, 2015 at 04:08:53PM +0300, Ander Conselvan De Oliveira wrote:
>> > On Mon, 2015-09-14 at 16:38 +0300, Ander Conselvan De Oliveira wrote:
>> > > On Mon, 2015-09-14 at 15:11 +0200, Daniel Vetter wrote:
>> > > > On Mon, Sep 14, 2015 at 02:51:51PM +0300, Ander Conselvan de Oliveira wrote:
>> > > > > ---
>> > > > >
>> > > > > On Fri, 2015-09-11 at 17:11 +0300, Ander Conselvan de Oliveira wrote:
>> > > > > > On Wed, 2015-09-09 at 11:33 +0100, Thomas Wood wrote:
>> > > > > > > On 8 September 2015 at 13:28, Ander Conselvan de Oliveira
>> > > > > > > <ander.conselvan.de.oliveira@intel.com> wrote:
>> > > > > > > >
>> > > > > >
>> > > > > > > > diff --git a/link-training-test/Makefile b/link-training-test/Makefile
>> > > > > > >
>> > > > > > > If this is meant to be part of the test suite, then it needs to be in
>> > > > > > > the tests directory and use the igt test infrastructure. Otherwise it
>> > > > > > > should be placed in tools or tools/link-training-test.
>> > > > > >
>> > > > > > I made the test use the igt infrastructure, but I'm not sure if this is
>> > > > > > a good fit for it. The dependency on the kernel is on build time, but
>> > > > > > once compiled this can be run on any machine. This can also introduce
>> > > > > > build failures if the test is not kept in sync with the driver source.
>> > > > > > Ideally that a failure to build this would be reported as the test
>> > > > > > failing, but I have no idea of how to achieve that.
>> > > > >
>> > > > > Alternatively, this could be in the kernel source tree directly. This
>> > > > > patch adds a test subdir to the i915 source dir, containing the link
>> > > > > training test. The test is compiled as part of the normal build using
>> > > > > the extra-y variable so that it doesn't get linked to the final kernel.
>> > > > >
>> > > > > When make is run from the tests directory, a thin wrapper around the
>> > > > > tests is built and linked to the object file compiled as part of the
>> > > > > kernel build. Running make run_tests from the test dir runs the test
>> > > > > and reports success or failure.
>> > > > >
>> > > > > Any thoughts?
>> > > >
>> > > > I think there's some precedence in other subsystems to integrate unit
>> > > > tests directly in the kernel, e.g. locking selftest or similar things.
>> > > > Usual approach is to either have a special module (but that often means
>> > > > piles of EXPORT_SYMBOL only for that selftest module). Or just a y/n
>> > > > Kconfig option which enables that code and runs all the self/unit tests
>> > > > when the module loads.
>> > > >
>> > > > I'd go with that approach since it's simpler. And we'd only need to tell
>> > > > QA to enable that Kconfig option for more testing.
>> > >
>> > > I'll have a look into that Kconfig approach, but there's a couple of things
>> > > I like about having the unit test as user space binaries:
>> > >
>> > >   - there's no need to boot the newly compiled kernel, so doing a test run
>> > >     is super fast;
>> > >   - the binaries can be debugged with gdb just like other user space stuff.
>> >
>> > I implemented the test using the Kconfig approach, and it seems to work well
>> > without impacting the points above. I added the call to run the test as the
>> > first thing in i915_init(), and with the driver compiled built-in, running
>> > the kernel under qemu will run the tests. And qemu can also provide a gdb
>> > remote target.
>> >
>> > One thing might be a problem though. With the previous approach, the
>> > functions overriden by the test where simply reimplemented in the new binary.
>> > But now the test is linked to the entire driver, so that's not possible. To
>> > work around that, I had to add function pointers to all the functions called
>> > by the link training state machine to intel_dp. I don't think that method
>> > scales well.
>> >
>> > I'll send update patches for reference as replies to this mail.
>>
>> I had a few discussions about this at XDC and I know think doing this is
>> userspace is better:
>> - Faster to run tests (since no module reloading required).
>> - Nouveau is developed in userspace and would like to reuse shared link
>>   training code too if possible from the dp helpers.
>>
>> So I'm leaning towards scaffolding in userspace now. Might be good to
>> check out how the nouveau userspace runtime works just to steal a few
>> tricks.
>
> I chatted with Martin Peres about this and had a look at the code. There are a few interesting
> tricks in Nouveau indeed. They have user space implementation for some kernel internals such as
> mutexes, ioremap on top of libpciaccess, etc. and an extensive set of stubs.
>
> The one thing that wouldn't be easy to take advantage of is the build integration. Their upstream
> repository is not actually a kernel tree. It contains their drm code, the same that ends up in the
> kernel plus all the user space stuff. There's a script that converts the commits from that
> repository for inclusion in the kernel tree.

Could we perhaps move at least some of the scafffolding for kernel
functions to upstream? I guess nouveau has some means to pull changes from
upstream too, so maybe we could push that some place nice. And I'm pretty
sure we're not the only ones thinking about unit-testing specific
kernelcode in a userspace environment.

Adding noveau and kselftest folks.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Shared scaffolding to unit-test kernel code in userspace (was Re: [Intel-gfx] [PATCH] drm/i915: Add link training test)
@ 2015-09-23  9:38                     ` Daniel Vetter
  0 siblings, 0 replies; 31+ messages in thread
From: Daniel Vetter @ 2015-09-23  9:38 UTC (permalink / raw)
  To: Ander Conselvan De Oliveira
  Cc: Nouveau Dev, intel-gfx, Shuah Khan, Linux Kernel Mailing List,
	Ben Skeggs, Greg KH, Thomas Wood

On Wed, Sep 23, 2015 at 11:18 AM, Ander Conselvan De Oliveira <conselvan2@gmail.com> wrote:
> On Wed, 2015-09-23 at 10:24 +0200, Daniel Vetter wrote:
>> On Tue, Sep 15, 2015 at 04:08:53PM +0300, Ander Conselvan De Oliveira wrote:
>> > On Mon, 2015-09-14 at 16:38 +0300, Ander Conselvan De Oliveira wrote:
>> > > On Mon, 2015-09-14 at 15:11 +0200, Daniel Vetter wrote:
>> > > > On Mon, Sep 14, 2015 at 02:51:51PM +0300, Ander Conselvan de Oliveira wrote:
>> > > > > ---
>> > > > >
>> > > > > On Fri, 2015-09-11 at 17:11 +0300, Ander Conselvan de Oliveira wrote:
>> > > > > > On Wed, 2015-09-09 at 11:33 +0100, Thomas Wood wrote:
>> > > > > > > On 8 September 2015 at 13:28, Ander Conselvan de Oliveira
>> > > > > > > <ander.conselvan.de.oliveira@intel.com> wrote:
>> > > > > > > >
>> > > > > >
>> > > > > > > > diff --git a/link-training-test/Makefile b/link-training-test/Makefile
>> > > > > > >
>> > > > > > > If this is meant to be part of the test suite, then it needs to be in
>> > > > > > > the tests directory and use the igt test infrastructure. Otherwise it
>> > > > > > > should be placed in tools or tools/link-training-test.
>> > > > > >
>> > > > > > I made the test use the igt infrastructure, but I'm not sure if this is
>> > > > > > a good fit for it. The dependency on the kernel is on build time, but
>> > > > > > once compiled this can be run on any machine. This can also introduce
>> > > > > > build failures if the test is not kept in sync with the driver source.
>> > > > > > Ideally that a failure to build this would be reported as the test
>> > > > > > failing, but I have no idea of how to achieve that.
>> > > > >
>> > > > > Alternatively, this could be in the kernel source tree directly. This
>> > > > > patch adds a test subdir to the i915 source dir, containing the link
>> > > > > training test. The test is compiled as part of the normal build using
>> > > > > the extra-y variable so that it doesn't get linked to the final kernel.
>> > > > >
>> > > > > When make is run from the tests directory, a thin wrapper around the
>> > > > > tests is built and linked to the object file compiled as part of the
>> > > > > kernel build. Running make run_tests from the test dir runs the test
>> > > > > and reports success or failure.
>> > > > >
>> > > > > Any thoughts?
>> > > >
>> > > > I think there's some precedence in other subsystems to integrate unit
>> > > > tests directly in the kernel, e.g. locking selftest or similar things.
>> > > > Usual approach is to either have a special module (but that often means
>> > > > piles of EXPORT_SYMBOL only for that selftest module). Or just a y/n
>> > > > Kconfig option which enables that code and runs all the self/unit tests
>> > > > when the module loads.
>> > > >
>> > > > I'd go with that approach since it's simpler. And we'd only need to tell
>> > > > QA to enable that Kconfig option for more testing.
>> > >
>> > > I'll have a look into that Kconfig approach, but there's a couple of things
>> > > I like about having the unit test as user space binaries:
>> > >
>> > >   - there's no need to boot the newly compiled kernel, so doing a test run
>> > >     is super fast;
>> > >   - the binaries can be debugged with gdb just like other user space stuff.
>> >
>> > I implemented the test using the Kconfig approach, and it seems to work well
>> > without impacting the points above. I added the call to run the test as the
>> > first thing in i915_init(), and with the driver compiled built-in, running
>> > the kernel under qemu will run the tests. And qemu can also provide a gdb
>> > remote target.
>> >
>> > One thing might be a problem though. With the previous approach, the
>> > functions overriden by the test where simply reimplemented in the new binary.
>> > But now the test is linked to the entire driver, so that's not possible. To
>> > work around that, I had to add function pointers to all the functions called
>> > by the link training state machine to intel_dp. I don't think that method
>> > scales well.
>> >
>> > I'll send update patches for reference as replies to this mail.
>>
>> I had a few discussions about this at XDC and I know think doing this is
>> userspace is better:
>> - Faster to run tests (since no module reloading required).
>> - Nouveau is developed in userspace and would like to reuse shared link
>>   training code too if possible from the dp helpers.
>>
>> So I'm leaning towards scaffolding in userspace now. Might be good to
>> check out how the nouveau userspace runtime works just to steal a few
>> tricks.
>
> I chatted with Martin Peres about this and had a look at the code. There are a few interesting
> tricks in Nouveau indeed. They have user space implementation for some kernel internals such as
> mutexes, ioremap on top of libpciaccess, etc. and an extensive set of stubs.
>
> The one thing that wouldn't be easy to take advantage of is the build integration. Their upstream
> repository is not actually a kernel tree. It contains their drm code, the same that ends up in the
> kernel plus all the user space stuff. There's a script that converts the commits from that
> repository for inclusion in the kernel tree.

Could we perhaps move at least some of the scafffolding for kernel
functions to upstream? I guess nouveau has some means to pull changes from
upstream too, so maybe we could push that some place nice. And I'm pretty
sure we're not the only ones thinking about unit-testing specific
kernelcode in a userspace environment.

Adding noveau and kselftest folks.
-Daniel
-- 
Daniel Vetter
Software Engineer, Intel Corporation
+41 (0) 79 365 57 48 - http://blog.ffwll.ch
_______________________________________________
Nouveau mailing list
Nouveau@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/nouveau

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: Shared scaffolding to unit-test kernel code in userspace (was Re: [Intel-gfx] [PATCH] drm/i915: Add link training test)
  2015-09-23  9:38                     ` Daniel Vetter
@ 2015-09-29  8:47                       ` Ander Conselvan De Oliveira
  -1 siblings, 0 replies; 31+ messages in thread
From: Ander Conselvan De Oliveira @ 2015-09-29  8:47 UTC (permalink / raw)
  To: Daniel Vetter
  Cc: intel-gfx, Thomas Wood, Martin Peres, Ben Skeggs, Nouveau Dev,
	Shuah Khan, Greg KH, Linux Kernel Mailing List

On Wed, 2015-09-23 at 11:38 +0200, Daniel Vetter wrote:
> On Wed, Sep 23, 2015 at 11:18 AM, Ander Conselvan De Oliveira <conselvan2@gmail.com> wrote:
> > On Wed, 2015-09-23 at 10:24 +0200, Daniel Vetter wrote:
> > > On Tue, Sep 15, 2015 at 04:08:53PM +0300, Ander Conselvan De Oliveira wrote:
> > > > On Mon, 2015-09-14 at 16:38 +0300, Ander Conselvan De Oliveira wrote:
> > > > > On Mon, 2015-09-14 at 15:11 +0200, Daniel Vetter wrote:
> > > > > > On Mon, Sep 14, 2015 at 02:51:51PM +0300, Ander Conselvan de Oliveira wrote:
> > > > > > > ---
> > > > > > > 
> > > > > > > On Fri, 2015-09-11 at 17:11 +0300, Ander Conselvan de Oliveira wrote:
> > > > > > > > On Wed, 2015-09-09 at 11:33 +0100, Thomas Wood wrote:
> > > > > > > > > On 8 September 2015 at 13:28, Ander Conselvan de Oliveira
> > > > > > > > > <ander.conselvan.de.oliveira@intel.com> wrote:
> > > > > > > > > > 
> > > > > > > > 
> > > > > > > > > > diff --git a/link-training-test/Makefile b/link-training-test/Makefile
> > > > > > > > > 
> > > > > > > > > If this is meant to be part of the test suite, then it needs to be in
> > > > > > > > > the tests directory and use the igt test infrastructure. Otherwise it
> > > > > > > > > should be placed in tools or tools/link-training-test.
> > > > > > > > 
> > > > > > > > I made the test use the igt infrastructure, but I'm not sure if this is
> > > > > > > > a good fit for it. The dependency on the kernel is on build time, but
> > > > > > > > once compiled this can be run on any machine. This can also introduce
> > > > > > > > build failures if the test is not kept in sync with the driver source.
> > > > > > > > Ideally that a failure to build this would be reported as the test
> > > > > > > > failing, but I have no idea of how to achieve that.
> > > > > > > 
> > > > > > > Alternatively, this could be in the kernel source tree directly. This
> > > > > > > patch adds a test subdir to the i915 source dir, containing the link
> > > > > > > training test. The test is compiled as part of the normal build using
> > > > > > > the extra-y variable so that it doesn't get linked to the final kernel.
> > > > > > > 
> > > > > > > When make is run from the tests directory, a thin wrapper around the
> > > > > > > tests is built and linked to the object file compiled as part of the
> > > > > > > kernel build. Running make run_tests from the test dir runs the test
> > > > > > > and reports success or failure.
> > > > > > > 
> > > > > > > Any thoughts?
> > > > > > 
> > > > > > I think there's some precedence in other subsystems to integrate unit
> > > > > > tests directly in the kernel, e.g. locking selftest or similar things.
> > > > > > Usual approach is to either have a special module (but that often means
> > > > > > piles of EXPORT_SYMBOL only for that selftest module). Or just a y/n
> > > > > > Kconfig option which enables that code and runs all the self/unit tests
> > > > > > when the module loads.
> > > > > > 
> > > > > > I'd go with that approach since it's simpler. And we'd only need to tell
> > > > > > QA to enable that Kconfig option for more testing.
> > > > > 
> > > > > I'll have a look into that Kconfig approach, but there's a couple of things
> > > > > I like about having the unit test as user space binaries:
> > > > > 
> > > > >   - there's no need to boot the newly compiled kernel, so doing a test run
> > > > >     is super fast;
> > > > >   - the binaries can be debugged with gdb just like other user space stuff.
> > > > 
> > > > I implemented the test using the Kconfig approach, and it seems to work well
> > > > without impacting the points above. I added the call to run the test as the
> > > > first thing in i915_init(), and with the driver compiled built-in, running
> > > > the kernel under qemu will run the tests. And qemu can also provide a gdb
> > > > remote target.
> > > > 
> > > > One thing might be a problem though. With the previous approach, the
> > > > functions overriden by the test where simply reimplemented in the new binary.
> > > > But now the test is linked to the entire driver, so that's not possible. To
> > > > work around that, I had to add function pointers to all the functions called
> > > > by the link training state machine to intel_dp. I don't think that method
> > > > scales well.
> > > > 
> > > > I'll send update patches for reference as replies to this mail.
> > > 
> > > I had a few discussions about this at XDC and I know think doing this is
> > > userspace is better:
> > > - Faster to run tests (since no module reloading required).
> > > - Nouveau is developed in userspace and would like to reuse shared link
> > >   training code too if possible from the dp helpers.
> > > 
> > > So I'm leaning towards scaffolding in userspace now. Might be good to
> > > check out how the nouveau userspace runtime works just to steal a few
> > > tricks.
> > 
> > I chatted with Martin Peres about this and had a look at the code. There are a few interesting
> > tricks in Nouveau indeed. They have user space implementation for some kernel internals such as
> > mutexes, ioremap on top of libpciaccess, etc. and an extensive set of stubs.
> > 
> > The one thing that wouldn't be easy to take advantage of is the build integration. Their 
> > upstream
> > repository is not actually a kernel tree. It contains their drm code, the same that ends up in 
> > the
> > kernel plus all the user space stuff. There's a script that converts the commits from that
> > repository for inclusion in the kernel tree.
> 
> Could we perhaps move at least some of the scafffolding for kernel
> functions to upstream? I guess nouveau has some means to pull changes from
> upstream too, so maybe we could push that some place nice. And I'm pretty
> sure we're not the only ones thinking about unit-testing specific
> kernelcode in a userspace environment.

Maybe this would add a burden on nouveau development without any added benefit? Since we need only a
small subset of what they already have, perhaps it would make more sense to first replicate that
small part for our consumption. I believe what need to be re-implemented and/or stubbed will vary
significantly with the code being tested, so it might make more sense to just let it grow
organically.

I think the more important question is how to include the right code when compiling the user space
binaries. The trick nouveau uses is to have only one header that includes code from the kernel. That
header is replaced with a version intended for user space if compiling user space binaries. We would
have to use the same trick for every subsystem under test, including some include path mangling. At
least drm and i915 would be affected for the tests I'm proposing.

Ander



^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: Shared scaffolding to unit-test kernel code in userspace (was Re: [PATCH] drm/i915: Add link training test)
@ 2015-09-29  8:47                       ` Ander Conselvan De Oliveira
  0 siblings, 0 replies; 31+ messages in thread
From: Ander Conselvan De Oliveira @ 2015-09-29  8:47 UTC (permalink / raw)
  To: Daniel Vetter
  Cc: Nouveau Dev, intel-gfx, Shuah Khan, Linux Kernel Mailing List,
	Martin Peres, Ben Skeggs, Greg KH, Thomas Wood

On Wed, 2015-09-23 at 11:38 +0200, Daniel Vetter wrote:
> On Wed, Sep 23, 2015 at 11:18 AM, Ander Conselvan De Oliveira <conselvan2@gmail.com> wrote:
> > On Wed, 2015-09-23 at 10:24 +0200, Daniel Vetter wrote:
> > > On Tue, Sep 15, 2015 at 04:08:53PM +0300, Ander Conselvan De Oliveira wrote:
> > > > On Mon, 2015-09-14 at 16:38 +0300, Ander Conselvan De Oliveira wrote:
> > > > > On Mon, 2015-09-14 at 15:11 +0200, Daniel Vetter wrote:
> > > > > > On Mon, Sep 14, 2015 at 02:51:51PM +0300, Ander Conselvan de Oliveira wrote:
> > > > > > > ---
> > > > > > > 
> > > > > > > On Fri, 2015-09-11 at 17:11 +0300, Ander Conselvan de Oliveira wrote:
> > > > > > > > On Wed, 2015-09-09 at 11:33 +0100, Thomas Wood wrote:
> > > > > > > > > On 8 September 2015 at 13:28, Ander Conselvan de Oliveira
> > > > > > > > > <ander.conselvan.de.oliveira@intel.com> wrote:
> > > > > > > > > > 
> > > > > > > > 
> > > > > > > > > > diff --git a/link-training-test/Makefile b/link-training-test/Makefile
> > > > > > > > > 
> > > > > > > > > If this is meant to be part of the test suite, then it needs to be in
> > > > > > > > > the tests directory and use the igt test infrastructure. Otherwise it
> > > > > > > > > should be placed in tools or tools/link-training-test.
> > > > > > > > 
> > > > > > > > I made the test use the igt infrastructure, but I'm not sure if this is
> > > > > > > > a good fit for it. The dependency on the kernel is on build time, but
> > > > > > > > once compiled this can be run on any machine. This can also introduce
> > > > > > > > build failures if the test is not kept in sync with the driver source.
> > > > > > > > Ideally that a failure to build this would be reported as the test
> > > > > > > > failing, but I have no idea of how to achieve that.
> > > > > > > 
> > > > > > > Alternatively, this could be in the kernel source tree directly. This
> > > > > > > patch adds a test subdir to the i915 source dir, containing the link
> > > > > > > training test. The test is compiled as part of the normal build using
> > > > > > > the extra-y variable so that it doesn't get linked to the final kernel.
> > > > > > > 
> > > > > > > When make is run from the tests directory, a thin wrapper around the
> > > > > > > tests is built and linked to the object file compiled as part of the
> > > > > > > kernel build. Running make run_tests from the test dir runs the test
> > > > > > > and reports success or failure.
> > > > > > > 
> > > > > > > Any thoughts?
> > > > > > 
> > > > > > I think there's some precedence in other subsystems to integrate unit
> > > > > > tests directly in the kernel, e.g. locking selftest or similar things.
> > > > > > Usual approach is to either have a special module (but that often means
> > > > > > piles of EXPORT_SYMBOL only for that selftest module). Or just a y/n
> > > > > > Kconfig option which enables that code and runs all the self/unit tests
> > > > > > when the module loads.
> > > > > > 
> > > > > > I'd go with that approach since it's simpler. And we'd only need to tell
> > > > > > QA to enable that Kconfig option for more testing.
> > > > > 
> > > > > I'll have a look into that Kconfig approach, but there's a couple of things
> > > > > I like about having the unit test as user space binaries:
> > > > > 
> > > > >   - there's no need to boot the newly compiled kernel, so doing a test run
> > > > >     is super fast;
> > > > >   - the binaries can be debugged with gdb just like other user space stuff.
> > > > 
> > > > I implemented the test using the Kconfig approach, and it seems to work well
> > > > without impacting the points above. I added the call to run the test as the
> > > > first thing in i915_init(), and with the driver compiled built-in, running
> > > > the kernel under qemu will run the tests. And qemu can also provide a gdb
> > > > remote target.
> > > > 
> > > > One thing might be a problem though. With the previous approach, the
> > > > functions overriden by the test where simply reimplemented in the new binary.
> > > > But now the test is linked to the entire driver, so that's not possible. To
> > > > work around that, I had to add function pointers to all the functions called
> > > > by the link training state machine to intel_dp. I don't think that method
> > > > scales well.
> > > > 
> > > > I'll send update patches for reference as replies to this mail.
> > > 
> > > I had a few discussions about this at XDC and I know think doing this is
> > > userspace is better:
> > > - Faster to run tests (since no module reloading required).
> > > - Nouveau is developed in userspace and would like to reuse shared link
> > >   training code too if possible from the dp helpers.
> > > 
> > > So I'm leaning towards scaffolding in userspace now. Might be good to
> > > check out how the nouveau userspace runtime works just to steal a few
> > > tricks.
> > 
> > I chatted with Martin Peres about this and had a look at the code. There are a few interesting
> > tricks in Nouveau indeed. They have user space implementation for some kernel internals such as
> > mutexes, ioremap on top of libpciaccess, etc. and an extensive set of stubs.
> > 
> > The one thing that wouldn't be easy to take advantage of is the build integration. Their 
> > upstream
> > repository is not actually a kernel tree. It contains their drm code, the same that ends up in 
> > the
> > kernel plus all the user space stuff. There's a script that converts the commits from that
> > repository for inclusion in the kernel tree.
> 
> Could we perhaps move at least some of the scafffolding for kernel
> functions to upstream? I guess nouveau has some means to pull changes from
> upstream too, so maybe we could push that some place nice. And I'm pretty
> sure we're not the only ones thinking about unit-testing specific
> kernelcode in a userspace environment.

Maybe this would add a burden on nouveau development without any added benefit? Since we need only a
small subset of what they already have, perhaps it would make more sense to first replicate that
small part for our consumption. I believe what need to be re-implemented and/or stubbed will vary
significantly with the code being tested, so it might make more sense to just let it grow
organically.

I think the more important question is how to include the right code when compiling the user space
binaries. The trick nouveau uses is to have only one header that includes code from the kernel. That
header is replaced with a version intended for user space if compiling user space binaries. We would
have to use the same trick for every subsystem under test, including some include path mangling. At
least drm and i915 would be affected for the tests I'm proposing.

Ander


_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH 7/8] drm/i915: Move max voltage and pre emphasis to intel_dp_link_training.c
  2015-09-08 13:01   ` Ville Syrjälä
@ 2015-10-19  5:14     ` Thulasimani, Sivakumar
  2015-10-19  5:59       ` Ander Conselvan De Oliveira
  0 siblings, 1 reply; 31+ messages in thread
From: Thulasimani, Sivakumar @ 2015-10-19  5:14 UTC (permalink / raw)
  To: Ville Syrjälä, Ander Conselvan de Oliveira
  Cc: jani.nikula, intel-gfx

As an FYI, both of these functions need to be rewritten when we want the 
code to be
compliant to DP spec.
We should read the pre-emphasis given by the panel and if vwing exeeds 
max value
we should use the max vswing supported for that pre-emphasis but current 
code
is opposite of that.  you can read more detailed logic in section 
"3.5.1.2 Link Training"
in DP Spec.

by the way same is true for much of the link training logic and related 
functions
i don't know if such rewriting should be done after fixing those or 
before :(.

regards,
Sivakumar.

On 9/8/2015 6:31 PM, Ville Syrjälä wrote:
> On Tue, Sep 08, 2015 at 03:27:58PM +0300, Ander Conselvan de Oliveira wrote:
>> So link training tests can use real hardware limits.
> These need to be kept in sync with the _signal_levels() functions, so
> moving them to a separate file is a bit questionable.
>
> I suggest that we should attempt to restructure this information as
> some kind of tables, from which we can look up the max values as
> well as the hardware specific values for each setting.
>
> That of course won't solve your problem. So far I don't know what your
> test even does and why does it need this information, so I guess I'll
> need to read on...
>
>> ---
>>   drivers/gpu/drm/i915/intel_dp.c               | 99 ---------------------------
>>   drivers/gpu/drm/i915/intel_dp_link_training.c | 92 +++++++++++++++++++++++++
>>   drivers/gpu/drm/i915/intel_drv.h              | 11 +--
>>   3 files changed, 99 insertions(+), 103 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
>> index 5778059..da87aef 100644
>> --- a/drivers/gpu/drm/i915/intel_dp.c
>> +++ b/drivers/gpu/drm/i915/intel_dp.c
>> @@ -111,13 +111,6 @@ static bool is_edp(struct intel_dp *intel_dp)
>>   	return intel_dig_port->base.type == INTEL_OUTPUT_EDP;
>>   }
>>   
>> -static struct drm_device *intel_dp_to_dev(struct intel_dp *intel_dp)
>> -{
>> -	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
>> -
>> -	return intel_dig_port->base.base.dev;
>> -}
>> -
>>   static struct intel_dp *intel_attached_dp(struct drm_connector *connector)
>>   {
>>   	return enc_to_intel_dp(&intel_attached_encoder(connector)->base);
>> @@ -3054,98 +3047,6 @@ intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_
>>   				       DP_LINK_STATUS_SIZE) == DP_LINK_STATUS_SIZE;
>>   }
>>   
>> -/* These are source-specific values. */
>> -uint8_t
>> -intel_dp_voltage_max(struct intel_dp *intel_dp)
>> -{
>> -	struct drm_device *dev = intel_dp_to_dev(intel_dp);
>> -	struct drm_i915_private *dev_priv = dev->dev_private;
>> -	enum port port = dp_to_dig_port(intel_dp)->port;
>> -
>> -	if (IS_BROXTON(dev))
>> -		return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
>> -	else if (INTEL_INFO(dev)->gen >= 9) {
>> -		if (dev_priv->edp_low_vswing && port == PORT_A)
>> -			return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
>> -		return DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
>> -	} else if (IS_VALLEYVIEW(dev))
>> -		return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
>> -	else if (IS_GEN7(dev) && port == PORT_A)
>> -		return DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
>> -	else if (HAS_PCH_CPT(dev) && port != PORT_A)
>> -		return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
>> -	else
>> -		return DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
>> -}
>> -
>> -uint8_t
>> -intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t voltage_swing)
>> -{
>> -	struct drm_device *dev = intel_dp_to_dev(intel_dp);
>> -	enum port port = dp_to_dig_port(intel_dp)->port;
>> -
>> -	if (INTEL_INFO(dev)->gen >= 9) {
>> -		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
>> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
>> -			return DP_TRAIN_PRE_EMPH_LEVEL_3;
>> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
>> -			return DP_TRAIN_PRE_EMPH_LEVEL_2;
>> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
>> -			return DP_TRAIN_PRE_EMPH_LEVEL_1;
>> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
>> -			return DP_TRAIN_PRE_EMPH_LEVEL_0;
>> -		default:
>> -			return DP_TRAIN_PRE_EMPH_LEVEL_0;
>> -		}
>> -	} else if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
>> -		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
>> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
>> -			return DP_TRAIN_PRE_EMPH_LEVEL_3;
>> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
>> -			return DP_TRAIN_PRE_EMPH_LEVEL_2;
>> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
>> -			return DP_TRAIN_PRE_EMPH_LEVEL_1;
>> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
>> -		default:
>> -			return DP_TRAIN_PRE_EMPH_LEVEL_0;
>> -		}
>> -	} else if (IS_VALLEYVIEW(dev)) {
>> -		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
>> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
>> -			return DP_TRAIN_PRE_EMPH_LEVEL_3;
>> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
>> -			return DP_TRAIN_PRE_EMPH_LEVEL_2;
>> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
>> -			return DP_TRAIN_PRE_EMPH_LEVEL_1;
>> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
>> -		default:
>> -			return DP_TRAIN_PRE_EMPH_LEVEL_0;
>> -		}
>> -	} else if (IS_GEN7(dev) && port == PORT_A) {
>> -		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
>> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
>> -			return DP_TRAIN_PRE_EMPH_LEVEL_2;
>> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
>> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
>> -			return DP_TRAIN_PRE_EMPH_LEVEL_1;
>> -		default:
>> -			return DP_TRAIN_PRE_EMPH_LEVEL_0;
>> -		}
>> -	} else {
>> -		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
>> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
>> -			return DP_TRAIN_PRE_EMPH_LEVEL_2;
>> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
>> -			return DP_TRAIN_PRE_EMPH_LEVEL_2;
>> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
>> -			return DP_TRAIN_PRE_EMPH_LEVEL_1;
>> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
>> -		default:
>> -			return DP_TRAIN_PRE_EMPH_LEVEL_0;
>> -		}
>> -	}
>> -}
>> -
>>   static uint32_t vlv_signal_levels(struct intel_dp *intel_dp)
>>   {
>>   	struct drm_device *dev = intel_dp_to_dev(intel_dp);
>> diff --git a/drivers/gpu/drm/i915/intel_dp_link_training.c b/drivers/gpu/drm/i915/intel_dp_link_training.c
>> index f33cbbb..8d27dce 100644
>> --- a/drivers/gpu/drm/i915/intel_dp_link_training.c
>> +++ b/drivers/gpu/drm/i915/intel_dp_link_training.c
>> @@ -23,6 +23,98 @@
>>   
>>   #include "intel_drv.h"
>>   
>> +/* These are source-specific values. */
>> +static uint8_t
>> +intel_dp_voltage_max(struct intel_dp *intel_dp)
>> +{
>> +	struct drm_device *dev = intel_dp_to_dev(intel_dp);
>> +	struct drm_i915_private *dev_priv = dev->dev_private;
>> +	enum port port = dp_to_dig_port(intel_dp)->port;
>> +
>> +	if (IS_BROXTON(dev))
>> +		return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
>> +	else if (INTEL_INFO(dev)->gen >= 9) {
>> +		if (dev_priv->edp_low_vswing && port == PORT_A)
>> +			return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
>> +		return DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
>> +	} else if (IS_VALLEYVIEW(dev))
>> +		return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
>> +	else if (IS_GEN7(dev) && port == PORT_A)
>> +		return DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
>> +	else if (HAS_PCH_CPT(dev) && port != PORT_A)
>> +		return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
>> +	else
>> +		return DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
>> +}
>> +
>> +static uint8_t
>> +intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t voltage_swing)
>> +{
>> +	struct drm_device *dev = intel_dp_to_dev(intel_dp);
>> +	enum port port = dp_to_dig_port(intel_dp)->port;
>> +
>> +	if (INTEL_INFO(dev)->gen >= 9) {
>> +		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
>> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
>> +			return DP_TRAIN_PRE_EMPH_LEVEL_3;
>> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
>> +			return DP_TRAIN_PRE_EMPH_LEVEL_2;
>> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
>> +			return DP_TRAIN_PRE_EMPH_LEVEL_1;
>> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
>> +			return DP_TRAIN_PRE_EMPH_LEVEL_0;
>> +		default:
>> +			return DP_TRAIN_PRE_EMPH_LEVEL_0;
>> +		}
>> +	} else if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
>> +		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
>> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
>> +			return DP_TRAIN_PRE_EMPH_LEVEL_3;
>> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
>> +			return DP_TRAIN_PRE_EMPH_LEVEL_2;
>> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
>> +			return DP_TRAIN_PRE_EMPH_LEVEL_1;
>> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
>> +		default:
>> +			return DP_TRAIN_PRE_EMPH_LEVEL_0;
>> +		}
>> +	} else if (IS_VALLEYVIEW(dev)) {
>> +		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
>> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
>> +			return DP_TRAIN_PRE_EMPH_LEVEL_3;
>> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
>> +			return DP_TRAIN_PRE_EMPH_LEVEL_2;
>> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
>> +			return DP_TRAIN_PRE_EMPH_LEVEL_1;
>> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
>> +		default:
>> +			return DP_TRAIN_PRE_EMPH_LEVEL_0;
>> +		}
>> +	} else if (IS_GEN7(dev) && port == PORT_A) {
>> +		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
>> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
>> +			return DP_TRAIN_PRE_EMPH_LEVEL_2;
>> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
>> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
>> +			return DP_TRAIN_PRE_EMPH_LEVEL_1;
>> +		default:
>> +			return DP_TRAIN_PRE_EMPH_LEVEL_0;
>> +		}
>> +	} else {
>> +		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
>> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
>> +			return DP_TRAIN_PRE_EMPH_LEVEL_2;
>> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
>> +			return DP_TRAIN_PRE_EMPH_LEVEL_2;
>> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
>> +			return DP_TRAIN_PRE_EMPH_LEVEL_1;
>> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
>> +		default:
>> +			return DP_TRAIN_PRE_EMPH_LEVEL_0;
>> +		}
>> +	}
>> +}
>> +
>>   static void
>>   intel_get_adjust_train(struct intel_dp *intel_dp,
>>   		       const uint8_t link_status[DP_LINK_STATUS_SIZE])
>> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
>> index 29ae4bb..671a20f 100644
>> --- a/drivers/gpu/drm/i915/intel_drv.h
>> +++ b/drivers/gpu/drm/i915/intel_drv.h
>> @@ -1216,15 +1216,18 @@ intel_dp_program_link_training_pattern(struct intel_dp *intel_dp,
>>   void
>>   intel_dp_update_signal_levels(struct intel_dp *intel_dp);
>>   void intel_dp_set_idle_link_train(struct intel_dp *intel_dp);
>> -uint8_t
>> -intel_dp_voltage_max(struct intel_dp *intel_dp);
>> -uint8_t
>> -intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t voltage_swing);
>>   void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock,
>>   			   uint8_t *link_bw, uint8_t *rate_select);
>>   bool intel_dp_source_supports_hbr2(struct drm_device *dev);
>>   bool
>>   intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t link_status[DP_LINK_STATUS_SIZE]);
>> +static inline struct drm_device *intel_dp_to_dev(struct intel_dp *intel_dp)
>> +{
>> +	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
>> +
>> +	return intel_dig_port->base.base.dev;
>> +}
>> +
>>   
>>   /* intel_dp_mst.c */
>>   int intel_dp_mst_encoder_init(struct intel_digital_port *intel_dig_port, int conn_id);
>> -- 
>> 2.4.3

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH 7/8] drm/i915: Move max voltage and pre emphasis to intel_dp_link_training.c
  2015-10-19  5:14     ` Thulasimani, Sivakumar
@ 2015-10-19  5:59       ` Ander Conselvan De Oliveira
  2015-10-19  6:13         ` Thulasimani, Sivakumar
  0 siblings, 1 reply; 31+ messages in thread
From: Ander Conselvan De Oliveira @ 2015-10-19  5:59 UTC (permalink / raw)
  To: Thulasimani, Sivakumar, Ville Syrjälä
  Cc: jani.nikula, intel-gfx, Bride, Jim

On Mon, 2015-10-19 at 10:44 +0530, Thulasimani, Sivakumar wrote:
> As an FYI, both of these functions need to be rewritten when we want the 
> code to be
> compliant to DP spec.
> We should read the pre-emphasis given by the panel and if vwing exeeds 
> max value
> we should use the max vswing supported for that pre-emphasis but current 
> code
> is opposite of that.  you can read more detailed logic in section 
> "3.5.1.2 Link Training"
> in DP Spec.

Yeah, the spec is pretty clear on that.


> by the way same is true for much of the link training logic and related 
> functions

Do you have a list of other spec violations? I'm aware that we don't fallback to
lower bandwidth when link training fails, but that problem can't be solved with
the driver alone. Since we already pick the lowest bandwidth that can support
the mode chosen by userspace, the fallback needs to allow it to choose a new
mode.

There's been some discussion on how to solve this, but I don't know if there was
any conclusions. One of the possibilities was to send an uevent to userspace to
tell it to to choose a smaller mode.

> i don't know if such rewriting should be done after fixing those or 
> before :(.

I have posted a new version of the refactoring patches which I believed are
ready to go. So I'd go for getting those changes in and fix the max voltage vs.
pre-emphasis problem on top of that.

Thanks,
Ander

> 
> regards,
> Sivakumar.
> 
> On 9/8/2015 6:31 PM, Ville Syrjälä wrote:
> > On Tue, Sep 08, 2015 at 03:27:58PM +0300, Ander Conselvan de Oliveira wrote:
> > > So link training tests can use real hardware limits.
> > These need to be kept in sync with the _signal_levels() functions, so
> > moving them to a separate file is a bit questionable.
> > 
> > I suggest that we should attempt to restructure this information as
> > some kind of tables, from which we can look up the max values as
> > well as the hardware specific values for each setting.
> > 
> > That of course won't solve your problem. So far I don't know what your
> > test even does and why does it need this information, so I guess I'll
> > need to read on...
> > 
> > > ---
> > >   drivers/gpu/drm/i915/intel_dp.c               | 99 ---------------------
> > > ------
> > >   drivers/gpu/drm/i915/intel_dp_link_training.c | 92
> > > +++++++++++++++++++++++++
> > >   drivers/gpu/drm/i915/intel_drv.h              | 11 +--
> > >   3 files changed, 99 insertions(+), 103 deletions(-)
> > > 
> > > diff --git a/drivers/gpu/drm/i915/intel_dp.c
> > > b/drivers/gpu/drm/i915/intel_dp.c
> > > index 5778059..da87aef 100644
> > > --- a/drivers/gpu/drm/i915/intel_dp.c
> > > +++ b/drivers/gpu/drm/i915/intel_dp.c
> > > @@ -111,13 +111,6 @@ static bool is_edp(struct intel_dp *intel_dp)
> > >   	return intel_dig_port->base.type == INTEL_OUTPUT_EDP;
> > >   }
> > >   
> > > -static struct drm_device *intel_dp_to_dev(struct intel_dp *intel_dp)
> > > -{
> > > -	struct intel_digital_port *intel_dig_port =
> > > dp_to_dig_port(intel_dp);
> > > -
> > > -	return intel_dig_port->base.base.dev;
> > > -}
> > > -
> > >   static struct intel_dp *intel_attached_dp(struct drm_connector
> > > *connector)
> > >   {
> > >   	return enc_to_intel_dp(&intel_attached_encoder(connector)
> > > ->base);
> > > @@ -3054,98 +3047,6 @@ intel_dp_get_link_status(struct intel_dp *intel_dp,
> > > uint8_t link_status[DP_LINK_
> > >   				       DP_LINK_STATUS_SIZE) ==
> > > DP_LINK_STATUS_SIZE;
> > >   }
> > >   
> > > -/* These are source-specific values. */
> > > -uint8_t
> > > -intel_dp_voltage_max(struct intel_dp *intel_dp)
> > > -{
> > > -	struct drm_device *dev = intel_dp_to_dev(intel_dp);
> > > -	struct drm_i915_private *dev_priv = dev->dev_private;
> > > -	enum port port = dp_to_dig_port(intel_dp)->port;
> > > -
> > > -	if (IS_BROXTON(dev))
> > > -		return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
> > > -	else if (INTEL_INFO(dev)->gen >= 9) {
> > > -		if (dev_priv->edp_low_vswing && port == PORT_A)
> > > -			return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
> > > -		return DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
> > > -	} else if (IS_VALLEYVIEW(dev))
> > > -		return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
> > > -	else if (IS_GEN7(dev) && port == PORT_A)
> > > -		return DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
> > > -	else if (HAS_PCH_CPT(dev) && port != PORT_A)
> > > -		return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
> > > -	else
> > > -		return DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
> > > -}
> > > -
> > > -uint8_t
> > > -intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t
> > > voltage_swing)
> > > -{
> > > -	struct drm_device *dev = intel_dp_to_dev(intel_dp);
> > > -	enum port port = dp_to_dig_port(intel_dp)->port;
> > > -
> > > -	if (INTEL_INFO(dev)->gen >= 9) {
> > > -		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
> > > -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
> > > -			return DP_TRAIN_PRE_EMPH_LEVEL_3;
> > > -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
> > > -			return DP_TRAIN_PRE_EMPH_LEVEL_2;
> > > -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
> > > -			return DP_TRAIN_PRE_EMPH_LEVEL_1;
> > > -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
> > > -			return DP_TRAIN_PRE_EMPH_LEVEL_0;
> > > -		default:
> > > -			return DP_TRAIN_PRE_EMPH_LEVEL_0;
> > > -		}
> > > -	} else if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
> > > -		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
> > > -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
> > > -			return DP_TRAIN_PRE_EMPH_LEVEL_3;
> > > -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
> > > -			return DP_TRAIN_PRE_EMPH_LEVEL_2;
> > > -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
> > > -			return DP_TRAIN_PRE_EMPH_LEVEL_1;
> > > -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
> > > -		default:
> > > -			return DP_TRAIN_PRE_EMPH_LEVEL_0;
> > > -		}
> > > -	} else if (IS_VALLEYVIEW(dev)) {
> > > -		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
> > > -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
> > > -			return DP_TRAIN_PRE_EMPH_LEVEL_3;
> > > -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
> > > -			return DP_TRAIN_PRE_EMPH_LEVEL_2;
> > > -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
> > > -			return DP_TRAIN_PRE_EMPH_LEVEL_1;
> > > -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
> > > -		default:
> > > -			return DP_TRAIN_PRE_EMPH_LEVEL_0;
> > > -		}
> > > -	} else if (IS_GEN7(dev) && port == PORT_A) {
> > > -		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
> > > -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
> > > -			return DP_TRAIN_PRE_EMPH_LEVEL_2;
> > > -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
> > > -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
> > > -			return DP_TRAIN_PRE_EMPH_LEVEL_1;
> > > -		default:
> > > -			return DP_TRAIN_PRE_EMPH_LEVEL_0;
> > > -		}
> > > -	} else {
> > > -		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
> > > -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
> > > -			return DP_TRAIN_PRE_EMPH_LEVEL_2;
> > > -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
> > > -			return DP_TRAIN_PRE_EMPH_LEVEL_2;
> > > -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
> > > -			return DP_TRAIN_PRE_EMPH_LEVEL_1;
> > > -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
> > > -		default:
> > > -			return DP_TRAIN_PRE_EMPH_LEVEL_0;
> > > -		}
> > > -	}
> > > -}
> > > -
> > >   static uint32_t vlv_signal_levels(struct intel_dp *intel_dp)
> > >   {
> > >   	struct drm_device *dev = intel_dp_to_dev(intel_dp);
> > > diff --git a/drivers/gpu/drm/i915/intel_dp_link_training.c
> > > b/drivers/gpu/drm/i915/intel_dp_link_training.c
> > > index f33cbbb..8d27dce 100644
> > > --- a/drivers/gpu/drm/i915/intel_dp_link_training.c
> > > +++ b/drivers/gpu/drm/i915/intel_dp_link_training.c
> > > @@ -23,6 +23,98 @@
> > >   
> > >   #include "intel_drv.h"
> > >   
> > > +/* These are source-specific values. */
> > > +static uint8_t
> > > +intel_dp_voltage_max(struct intel_dp *intel_dp)
> > > +{
> > > +	struct drm_device *dev = intel_dp_to_dev(intel_dp);
> > > +	struct drm_i915_private *dev_priv = dev->dev_private;
> > > +	enum port port = dp_to_dig_port(intel_dp)->port;
> > > +
> > > +	if (IS_BROXTON(dev))
> > > +		return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
> > > +	else if (INTEL_INFO(dev)->gen >= 9) {
> > > +		if (dev_priv->edp_low_vswing && port == PORT_A)
> > > +			return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
> > > +		return DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
> > > +	} else if (IS_VALLEYVIEW(dev))
> > > +		return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
> > > +	else if (IS_GEN7(dev) && port == PORT_A)
> > > +		return DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
> > > +	else if (HAS_PCH_CPT(dev) && port != PORT_A)
> > > +		return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
> > > +	else
> > > +		return DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
> > > +}
> > > +
> > > +static uint8_t
> > > +intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t
> > > voltage_swing)
> > > +{
> > > +	struct drm_device *dev = intel_dp_to_dev(intel_dp);
> > > +	enum port port = dp_to_dig_port(intel_dp)->port;
> > > +
> > > +	if (INTEL_INFO(dev)->gen >= 9) {
> > > +		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
> > > +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
> > > +			return DP_TRAIN_PRE_EMPH_LEVEL_3;
> > > +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
> > > +			return DP_TRAIN_PRE_EMPH_LEVEL_2;
> > > +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
> > > +			return DP_TRAIN_PRE_EMPH_LEVEL_1;
> > > +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
> > > +			return DP_TRAIN_PRE_EMPH_LEVEL_0;
> > > +		default:
> > > +			return DP_TRAIN_PRE_EMPH_LEVEL_0;
> > > +		}
> > > +	} else if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
> > > +		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
> > > +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
> > > +			return DP_TRAIN_PRE_EMPH_LEVEL_3;
> > > +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
> > > +			return DP_TRAIN_PRE_EMPH_LEVEL_2;
> > > +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
> > > +			return DP_TRAIN_PRE_EMPH_LEVEL_1;
> > > +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
> > > +		default:
> > > +			return DP_TRAIN_PRE_EMPH_LEVEL_0;
> > > +		}
> > > +	} else if (IS_VALLEYVIEW(dev)) {
> > > +		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
> > > +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
> > > +			return DP_TRAIN_PRE_EMPH_LEVEL_3;
> > > +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
> > > +			return DP_TRAIN_PRE_EMPH_LEVEL_2;
> > > +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
> > > +			return DP_TRAIN_PRE_EMPH_LEVEL_1;
> > > +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
> > > +		default:
> > > +			return DP_TRAIN_PRE_EMPH_LEVEL_0;
> > > +		}
> > > +	} else if (IS_GEN7(dev) && port == PORT_A) {
> > > +		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
> > > +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
> > > +			return DP_TRAIN_PRE_EMPH_LEVEL_2;
> > > +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
> > > +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
> > > +			return DP_TRAIN_PRE_EMPH_LEVEL_1;
> > > +		default:
> > > +			return DP_TRAIN_PRE_EMPH_LEVEL_0;
> > > +		}
> > > +	} else {
> > > +		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
> > > +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
> > > +			return DP_TRAIN_PRE_EMPH_LEVEL_2;
> > > +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
> > > +			return DP_TRAIN_PRE_EMPH_LEVEL_2;
> > > +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
> > > +			return DP_TRAIN_PRE_EMPH_LEVEL_1;
> > > +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
> > > +		default:
> > > +			return DP_TRAIN_PRE_EMPH_LEVEL_0;
> > > +		}
> > > +	}
> > > +}
> > > +
> > >   static void
> > >   intel_get_adjust_train(struct intel_dp *intel_dp,
> > >   		       const uint8_t link_status[DP_LINK_STATUS_SIZE])
> > > diff --git a/drivers/gpu/drm/i915/intel_drv.h
> > > b/drivers/gpu/drm/i915/intel_drv.h
> > > index 29ae4bb..671a20f 100644
> > > --- a/drivers/gpu/drm/i915/intel_drv.h
> > > +++ b/drivers/gpu/drm/i915/intel_drv.h
> > > @@ -1216,15 +1216,18 @@ intel_dp_program_link_training_pattern(struct
> > > intel_dp *intel_dp,
> > >   void
> > >   intel_dp_update_signal_levels(struct intel_dp *intel_dp);
> > >   void intel_dp_set_idle_link_train(struct intel_dp *intel_dp);
> > > -uint8_t
> > > -intel_dp_voltage_max(struct intel_dp *intel_dp);
> > > -uint8_t
> > > -intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t
> > > voltage_swing);
> > >   void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock,
> > >   			   uint8_t *link_bw, uint8_t *rate_select);
> > >   bool intel_dp_source_supports_hbr2(struct drm_device *dev);
> > >   bool
> > >   intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t
> > > link_status[DP_LINK_STATUS_SIZE]);
> > > +static inline struct drm_device *intel_dp_to_dev(struct intel_dp
> > > *intel_dp)
> > > +{
> > > +	struct intel_digital_port *intel_dig_port =
> > > dp_to_dig_port(intel_dp);
> > > +
> > > +	return intel_dig_port->base.base.dev;
> > > +}
> > > +
> > >   
> > >   /* intel_dp_mst.c */
> > >   int intel_dp_mst_encoder_init(struct intel_digital_port *intel_dig_port,
> > > int conn_id);
_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [PATCH 7/8] drm/i915: Move max voltage and pre emphasis to intel_dp_link_training.c
  2015-10-19  5:59       ` Ander Conselvan De Oliveira
@ 2015-10-19  6:13         ` Thulasimani, Sivakumar
  0 siblings, 0 replies; 31+ messages in thread
From: Thulasimani, Sivakumar @ 2015-10-19  6:13 UTC (permalink / raw)
  To: Ander Conselvan De Oliveira, Ville Syrjälä
  Cc: jani.nikula, intel-gfx, Bride, Jim



On 10/19/2015 11:29 AM, Ander Conselvan De Oliveira wrote:
> On Mon, 2015-10-19 at 10:44 +0530, Thulasimani, Sivakumar wrote:
>> As an FYI, both of these functions need to be rewritten when we want the
>> code to be
>> compliant to DP spec.
>> We should read the pre-emphasis given by the panel and if vwing exeeds
>> max value
>> we should use the max vswing supported for that pre-emphasis but current
>> code
>> is opposite of that.  you can read more detailed logic in section
>> "3.5.1.2 Link Training"
>> in DP Spec.
> Yeah, the spec is pretty clear on that.
>
>
>> by the way same is true for much of the link training logic and related
>> functions
> Do you have a list of other spec violations? I'm aware that we don't fallback to
> lower bandwidth when link training fails, but that problem can't be solved with
> the driver alone. Since we already pick the lowest bandwidth that can support
> the mode chosen by userspace, the fallback needs to allow it to choose a new
> mode.
>
> There's been some discussion on how to solve this, but I don't know if there was
> any conclusions. One of the possibilities was to send an uevent to userspace to
> tell it to to choose a smaller mode.
here is a list of changes i had to do for local branch, that i should be 
upstreaming in the future

drm/i915: Fix swing & emphasis usage (this is the patch for issue i 
talked about above)
drm/i915: EQ retry must be 5 times only
drm/i915: Start link training from max link rate
drm/i915: Fix Clock recovery sequence ( fixes the whole clock recovery 
to be per spec)
drm/i915: Fix EQ sequence in Link training (fixes the whole EQ to be per 
spec)
drm/i915: Check EQ only if CR passed  (This seems to be in place in your 
changes)

All these are just related to link training, i have been asked for list 
of issues recently
as well, so i will probably file each of them in bugzilla/freedesktop so 
someone
will track it.

regards,
Sivakumar
>
>> i don't know if such rewriting should be done after fixing those or
>> before :(.
> I have posted a new version of the refactoring patches which I believed are
> ready to go. So I'd go for getting those changes in and fix the max voltage vs.
> pre-emphasis problem on top of that.
>
> Thanks,
> Ander
>
>> regards,
>> Sivakumar.
>>
>> On 9/8/2015 6:31 PM, Ville Syrjälä wrote:
>>> On Tue, Sep 08, 2015 at 03:27:58PM +0300, Ander Conselvan de Oliveira wrote:
>>>> So link training tests can use real hardware limits.
>>> These need to be kept in sync with the _signal_levels() functions, so
>>> moving them to a separate file is a bit questionable.
>>>
>>> I suggest that we should attempt to restructure this information as
>>> some kind of tables, from which we can look up the max values as
>>> well as the hardware specific values for each setting.
>>>
>>> That of course won't solve your problem. So far I don't know what your
>>> test even does and why does it need this information, so I guess I'll
>>> need to read on...
>>>
>>>> ---
>>>>    drivers/gpu/drm/i915/intel_dp.c               | 99 ---------------------
>>>> ------
>>>>    drivers/gpu/drm/i915/intel_dp_link_training.c | 92
>>>> +++++++++++++++++++++++++
>>>>    drivers/gpu/drm/i915/intel_drv.h              | 11 +--
>>>>    3 files changed, 99 insertions(+), 103 deletions(-)
>>>>
>>>> diff --git a/drivers/gpu/drm/i915/intel_dp.c
>>>> b/drivers/gpu/drm/i915/intel_dp.c
>>>> index 5778059..da87aef 100644
>>>> --- a/drivers/gpu/drm/i915/intel_dp.c
>>>> +++ b/drivers/gpu/drm/i915/intel_dp.c
>>>> @@ -111,13 +111,6 @@ static bool is_edp(struct intel_dp *intel_dp)
>>>>    	return intel_dig_port->base.type == INTEL_OUTPUT_EDP;
>>>>    }
>>>>    
>>>> -static struct drm_device *intel_dp_to_dev(struct intel_dp *intel_dp)
>>>> -{
>>>> -	struct intel_digital_port *intel_dig_port =
>>>> dp_to_dig_port(intel_dp);
>>>> -
>>>> -	return intel_dig_port->base.base.dev;
>>>> -}
>>>> -
>>>>    static struct intel_dp *intel_attached_dp(struct drm_connector
>>>> *connector)
>>>>    {
>>>>    	return enc_to_intel_dp(&intel_attached_encoder(connector)
>>>> ->base);
>>>> @@ -3054,98 +3047,6 @@ intel_dp_get_link_status(struct intel_dp *intel_dp,
>>>> uint8_t link_status[DP_LINK_
>>>>    				       DP_LINK_STATUS_SIZE) ==
>>>> DP_LINK_STATUS_SIZE;
>>>>    }
>>>>    
>>>> -/* These are source-specific values. */
>>>> -uint8_t
>>>> -intel_dp_voltage_max(struct intel_dp *intel_dp)
>>>> -{
>>>> -	struct drm_device *dev = intel_dp_to_dev(intel_dp);
>>>> -	struct drm_i915_private *dev_priv = dev->dev_private;
>>>> -	enum port port = dp_to_dig_port(intel_dp)->port;
>>>> -
>>>> -	if (IS_BROXTON(dev))
>>>> -		return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
>>>> -	else if (INTEL_INFO(dev)->gen >= 9) {
>>>> -		if (dev_priv->edp_low_vswing && port == PORT_A)
>>>> -			return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
>>>> -		return DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
>>>> -	} else if (IS_VALLEYVIEW(dev))
>>>> -		return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
>>>> -	else if (IS_GEN7(dev) && port == PORT_A)
>>>> -		return DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
>>>> -	else if (HAS_PCH_CPT(dev) && port != PORT_A)
>>>> -		return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
>>>> -	else
>>>> -		return DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
>>>> -}
>>>> -
>>>> -uint8_t
>>>> -intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t
>>>> voltage_swing)
>>>> -{
>>>> -	struct drm_device *dev = intel_dp_to_dev(intel_dp);
>>>> -	enum port port = dp_to_dig_port(intel_dp)->port;
>>>> -
>>>> -	if (INTEL_INFO(dev)->gen >= 9) {
>>>> -		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
>>>> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
>>>> -			return DP_TRAIN_PRE_EMPH_LEVEL_3;
>>>> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
>>>> -			return DP_TRAIN_PRE_EMPH_LEVEL_2;
>>>> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
>>>> -			return DP_TRAIN_PRE_EMPH_LEVEL_1;
>>>> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
>>>> -			return DP_TRAIN_PRE_EMPH_LEVEL_0;
>>>> -		default:
>>>> -			return DP_TRAIN_PRE_EMPH_LEVEL_0;
>>>> -		}
>>>> -	} else if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
>>>> -		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
>>>> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
>>>> -			return DP_TRAIN_PRE_EMPH_LEVEL_3;
>>>> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
>>>> -			return DP_TRAIN_PRE_EMPH_LEVEL_2;
>>>> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
>>>> -			return DP_TRAIN_PRE_EMPH_LEVEL_1;
>>>> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
>>>> -		default:
>>>> -			return DP_TRAIN_PRE_EMPH_LEVEL_0;
>>>> -		}
>>>> -	} else if (IS_VALLEYVIEW(dev)) {
>>>> -		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
>>>> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
>>>> -			return DP_TRAIN_PRE_EMPH_LEVEL_3;
>>>> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
>>>> -			return DP_TRAIN_PRE_EMPH_LEVEL_2;
>>>> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
>>>> -			return DP_TRAIN_PRE_EMPH_LEVEL_1;
>>>> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
>>>> -		default:
>>>> -			return DP_TRAIN_PRE_EMPH_LEVEL_0;
>>>> -		}
>>>> -	} else if (IS_GEN7(dev) && port == PORT_A) {
>>>> -		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
>>>> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
>>>> -			return DP_TRAIN_PRE_EMPH_LEVEL_2;
>>>> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
>>>> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
>>>> -			return DP_TRAIN_PRE_EMPH_LEVEL_1;
>>>> -		default:
>>>> -			return DP_TRAIN_PRE_EMPH_LEVEL_0;
>>>> -		}
>>>> -	} else {
>>>> -		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
>>>> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
>>>> -			return DP_TRAIN_PRE_EMPH_LEVEL_2;
>>>> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
>>>> -			return DP_TRAIN_PRE_EMPH_LEVEL_2;
>>>> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
>>>> -			return DP_TRAIN_PRE_EMPH_LEVEL_1;
>>>> -		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
>>>> -		default:
>>>> -			return DP_TRAIN_PRE_EMPH_LEVEL_0;
>>>> -		}
>>>> -	}
>>>> -}
>>>> -
>>>>    static uint32_t vlv_signal_levels(struct intel_dp *intel_dp)
>>>>    {
>>>>    	struct drm_device *dev = intel_dp_to_dev(intel_dp);
>>>> diff --git a/drivers/gpu/drm/i915/intel_dp_link_training.c
>>>> b/drivers/gpu/drm/i915/intel_dp_link_training.c
>>>> index f33cbbb..8d27dce 100644
>>>> --- a/drivers/gpu/drm/i915/intel_dp_link_training.c
>>>> +++ b/drivers/gpu/drm/i915/intel_dp_link_training.c
>>>> @@ -23,6 +23,98 @@
>>>>    
>>>>    #include "intel_drv.h"
>>>>    
>>>> +/* These are source-specific values. */
>>>> +static uint8_t
>>>> +intel_dp_voltage_max(struct intel_dp *intel_dp)
>>>> +{
>>>> +	struct drm_device *dev = intel_dp_to_dev(intel_dp);
>>>> +	struct drm_i915_private *dev_priv = dev->dev_private;
>>>> +	enum port port = dp_to_dig_port(intel_dp)->port;
>>>> +
>>>> +	if (IS_BROXTON(dev))
>>>> +		return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
>>>> +	else if (INTEL_INFO(dev)->gen >= 9) {
>>>> +		if (dev_priv->edp_low_vswing && port == PORT_A)
>>>> +			return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
>>>> +		return DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
>>>> +	} else if (IS_VALLEYVIEW(dev))
>>>> +		return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
>>>> +	else if (IS_GEN7(dev) && port == PORT_A)
>>>> +		return DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
>>>> +	else if (HAS_PCH_CPT(dev) && port != PORT_A)
>>>> +		return DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
>>>> +	else
>>>> +		return DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
>>>> +}
>>>> +
>>>> +static uint8_t
>>>> +intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t
>>>> voltage_swing)
>>>> +{
>>>> +	struct drm_device *dev = intel_dp_to_dev(intel_dp);
>>>> +	enum port port = dp_to_dig_port(intel_dp)->port;
>>>> +
>>>> +	if (INTEL_INFO(dev)->gen >= 9) {
>>>> +		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
>>>> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
>>>> +			return DP_TRAIN_PRE_EMPH_LEVEL_3;
>>>> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
>>>> +			return DP_TRAIN_PRE_EMPH_LEVEL_2;
>>>> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
>>>> +			return DP_TRAIN_PRE_EMPH_LEVEL_1;
>>>> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
>>>> +			return DP_TRAIN_PRE_EMPH_LEVEL_0;
>>>> +		default:
>>>> +			return DP_TRAIN_PRE_EMPH_LEVEL_0;
>>>> +		}
>>>> +	} else if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
>>>> +		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
>>>> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
>>>> +			return DP_TRAIN_PRE_EMPH_LEVEL_3;
>>>> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
>>>> +			return DP_TRAIN_PRE_EMPH_LEVEL_2;
>>>> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
>>>> +			return DP_TRAIN_PRE_EMPH_LEVEL_1;
>>>> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
>>>> +		default:
>>>> +			return DP_TRAIN_PRE_EMPH_LEVEL_0;
>>>> +		}
>>>> +	} else if (IS_VALLEYVIEW(dev)) {
>>>> +		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
>>>> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
>>>> +			return DP_TRAIN_PRE_EMPH_LEVEL_3;
>>>> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
>>>> +			return DP_TRAIN_PRE_EMPH_LEVEL_2;
>>>> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
>>>> +			return DP_TRAIN_PRE_EMPH_LEVEL_1;
>>>> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
>>>> +		default:
>>>> +			return DP_TRAIN_PRE_EMPH_LEVEL_0;
>>>> +		}
>>>> +	} else if (IS_GEN7(dev) && port == PORT_A) {
>>>> +		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
>>>> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
>>>> +			return DP_TRAIN_PRE_EMPH_LEVEL_2;
>>>> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
>>>> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
>>>> +			return DP_TRAIN_PRE_EMPH_LEVEL_1;
>>>> +		default:
>>>> +			return DP_TRAIN_PRE_EMPH_LEVEL_0;
>>>> +		}
>>>> +	} else {
>>>> +		switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
>>>> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_0:
>>>> +			return DP_TRAIN_PRE_EMPH_LEVEL_2;
>>>> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_1:
>>>> +			return DP_TRAIN_PRE_EMPH_LEVEL_2;
>>>> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_2:
>>>> +			return DP_TRAIN_PRE_EMPH_LEVEL_1;
>>>> +		case DP_TRAIN_VOLTAGE_SWING_LEVEL_3:
>>>> +		default:
>>>> +			return DP_TRAIN_PRE_EMPH_LEVEL_0;
>>>> +		}
>>>> +	}
>>>> +}
>>>> +
>>>>    static void
>>>>    intel_get_adjust_train(struct intel_dp *intel_dp,
>>>>    		       const uint8_t link_status[DP_LINK_STATUS_SIZE])
>>>> diff --git a/drivers/gpu/drm/i915/intel_drv.h
>>>> b/drivers/gpu/drm/i915/intel_drv.h
>>>> index 29ae4bb..671a20f 100644
>>>> --- a/drivers/gpu/drm/i915/intel_drv.h
>>>> +++ b/drivers/gpu/drm/i915/intel_drv.h
>>>> @@ -1216,15 +1216,18 @@ intel_dp_program_link_training_pattern(struct
>>>> intel_dp *intel_dp,
>>>>    void
>>>>    intel_dp_update_signal_levels(struct intel_dp *intel_dp);
>>>>    void intel_dp_set_idle_link_train(struct intel_dp *intel_dp);
>>>> -uint8_t
>>>> -intel_dp_voltage_max(struct intel_dp *intel_dp);
>>>> -uint8_t
>>>> -intel_dp_pre_emphasis_max(struct intel_dp *intel_dp, uint8_t
>>>> voltage_swing);
>>>>    void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock,
>>>>    			   uint8_t *link_bw, uint8_t *rate_select);
>>>>    bool intel_dp_source_supports_hbr2(struct drm_device *dev);
>>>>    bool
>>>>    intel_dp_get_link_status(struct intel_dp *intel_dp, uint8_t
>>>> link_status[DP_LINK_STATUS_SIZE]);
>>>> +static inline struct drm_device *intel_dp_to_dev(struct intel_dp
>>>> *intel_dp)
>>>> +{
>>>> +	struct intel_digital_port *intel_dig_port =
>>>> dp_to_dig_port(intel_dp);
>>>> +
>>>> +	return intel_dig_port->base.base.dev;
>>>> +}
>>>> +
>>>>    
>>>>    /* intel_dp_mst.c */
>>>>    int intel_dp_mst_encoder_init(struct intel_digital_port *intel_dig_port,
>>>> int conn_id);

_______________________________________________
Intel-gfx mailing list
Intel-gfx@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/intel-gfx

^ permalink raw reply	[flat|nested] 31+ messages in thread

end of thread, other threads:[~2015-10-19  6:13 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-09-08 12:27 [PATCH 0/8] [RFC] Link training test Ander Conselvan de Oliveira
2015-09-08 12:27 ` [PATCH 1/8] drm/i915: Rename DP link training functions Ander Conselvan de Oliveira
2015-09-08 12:27 ` [PATCH 2/8] drm/i915: Don't pass *DP around to " Ander Conselvan de Oliveira
2015-09-08 12:27 ` [PATCH 3/8] drm/i915: Split intel_dp_update_link_train() Ander Conselvan de Oliveira
2015-09-08 12:27 ` [PATCH 4/8] drm/i915: Split write of pattern to DP reg from intel_dp_set_link_train Ander Conselvan de Oliveira
2015-09-08 12:27 ` [PATCH 5/8] drm/i915: Don't call intel_dp_set_signal_levels() on link train reset Ander Conselvan de Oliveira
2015-09-08 12:27 ` [PATCH 6/8] drm/i915: Move generic link training code to a separate file Ander Conselvan de Oliveira
2015-09-08 12:27 ` [PATCH 7/8] drm/i915: Move max voltage and pre emphasis to intel_dp_link_training.c Ander Conselvan de Oliveira
2015-09-08 13:01   ` Ville Syrjälä
2015-10-19  5:14     ` Thulasimani, Sivakumar
2015-10-19  5:59       ` Ander Conselvan De Oliveira
2015-10-19  6:13         ` Thulasimani, Sivakumar
2015-09-08 12:27 ` [PATCH 8/8] drm/i915: Extract intel_dev_info.[ch] Ander Conselvan de Oliveira
2015-09-08 12:28 ` [PATCH i-g-t] Add a link training test Ander Conselvan de Oliveira
2015-09-08 13:11   ` Ville Syrjälä
2015-09-08 13:26     ` Ander Conselvan De Oliveira
2015-09-09 10:33   ` Thomas Wood
2015-09-11 14:11     ` [PATCH] Add a link training test (v2) Ander Conselvan de Oliveira
2015-09-14 11:51       ` [PATCH] drm/i915: Add link training test Ander Conselvan de Oliveira
2015-09-14 13:11         ` Daniel Vetter
2015-09-14 13:38           ` Ander Conselvan De Oliveira
2015-09-15 13:08             ` Ander Conselvan De Oliveira
2015-09-23  8:24               ` Daniel Vetter
2015-09-23  9:18                 ` Ander Conselvan De Oliveira
2015-09-23  9:38                   ` Shared scaffolding to unit-test kernel code in userspace (was Re: [Intel-gfx] [PATCH] drm/i915: Add link training test) Daniel Vetter
2015-09-23  9:38                     ` Daniel Vetter
2015-09-29  8:47                     ` Ander Conselvan De Oliveira
2015-09-29  8:47                       ` Shared scaffolding to unit-test kernel code in userspace (was " Ander Conselvan De Oliveira
2015-09-15 13:11             ` [PATCH 1/2] drm/i915: Make link training state machine code use function pointers Ander Conselvan de Oliveira
2015-09-15 13:11               ` [PATCH 2/2] drm/i915: Add a link training test Ander Conselvan de Oliveira
2015-09-22 15:12 ` [PATCH 0/8] [RFC] Link " Daniel Vetter

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.