Commit 33a34e4e authored by Jesse Barnes's avatar Jesse Barnes Committed by Chris Wilson

drm/i915: split DP link training across panel power sequencing

Mode set sequence requires that we start training, then enable the
panel, then complete training.  So split the DP training function into
two parts; the first enables the DP port and sets training pattern 1 and
the second completes the training.

As part of this, remove some redundant function args from the various DP
handling functions and use the intel_dp fields everywhere we can.
Signed-off-by: default avatarJesse Barnes <jbarnes@virtuousgeek.org>
[ickle: removed first ironlake_edp_backlight_on() on advice of jbarnes]
Signed-off-by: default avatarChris Wilson <chris@chris-wilson.co.uk>
parent b2094bba
......@@ -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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment