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