mirror of https://gitee.com/openkylin/linux.git
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: Jesse Barnes <jbarnes@virtuousgeek.org> [ickle: removed first ironlake_edp_backlight_on() on advice of jbarnes] Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
This commit is contained in:
parent
b2094bbad4
commit
33a34e4e59
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue