@@ -1676,7 +1676,8 @@ static void intel_ddi_pre_enable_dp(struct intel_encoder *encoder,
pll->config.crtc_mask = 0;
/* If Link Training fails, send a uevent to generate a hotplug */
- if (!intel_ddi_link_train(intel_dp, link_rate, lane_count, link_mst))
+ if (!intel_ddi_link_train(intel_dp, link_rate, lane_count, link_mst,
+ false))
drm_kms_helper_hotplug_event(encoder->base.dev);
pll->config = tmp_pll_config;
}
@@ -2464,7 +2465,7 @@ intel_ddi_get_link_dpll(struct intel_dp *intel_dp, int clock)
bool
intel_ddi_link_train(struct intel_dp *intel_dp, int max_link_rate,
- uint8_t max_lane_count, bool link_mst)
+ uint8_t max_lane_count, bool link_mst, bool is_upfront)
{
struct intel_connector *connector = intel_dp->attached_connector;
struct intel_encoder *encoder = connector->encoder;
@@ -2512,6 +2513,7 @@ intel_ddi_link_train(struct intel_dp *intel_dp, int max_link_rate,
pll->funcs.disable(dev_priv, pll);
pll->config = tmp_pll_config;
}
+
if (ret) {
DRM_DEBUG_KMS("Link Training successful at link rate: %d lane: %d\n",
link_rate, lane_count);
@@ -2520,6 +2522,21 @@ intel_ddi_link_train(struct intel_dp *intel_dp, int max_link_rate,
}
intel_dp_stop_link_train(intel_dp);
+ if (is_upfront) {
+ DRM_DEBUG_KMS("Upfront link train %s: link_clock:%d lanes:%d\n",
+ ret ? "Passed" : "Failed",
+ link_rate, lane_count);
+ /* Disable port followed by PLL for next retry/clean up */
+ intel_ddi_post_disable(encoder, NULL, NULL);
+ pll->funcs.disable(dev_priv, pll);
+ pll->config = tmp_pll_config;
+ if (ret) {
+ /* Save the upfront values */
+ intel_dp->max_lanes_upfront = lane_count;
+ intel_dp->max_link_rate_upfront = link_rate;
+ }
+ }
+
if (!lane_count)
DRM_ERROR("Link Training Failed\n");
@@ -153,12 +153,21 @@ intel_dp_max_link_bw(struct intel_dp *intel_dp)
static u8 intel_dp_max_lane_count(struct intel_dp *intel_dp)
{
struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
- u8 source_max, sink_max;
+ u8 temp, source_max, sink_max;
source_max = intel_dig_port->max_lanes;
sink_max = drm_dp_max_lane_count(intel_dp->dpcd);
- return min(source_max, sink_max);
+ temp = min(source_max, sink_max);
+
+ /*
+ * Limit max lanes w.r.t to the max value found
+ * using Upfront link training also.
+ */
+ if (intel_dp->max_lanes_upfront)
+ return min(temp, intel_dp->max_lanes_upfront);
+ else
+ return temp;
}
/*
@@ -190,6 +199,42 @@ intel_dp_max_data_rate(int max_link_clock, int max_lanes)
return (max_link_clock * max_lanes * 8) / 10;
}
+static int intel_dp_upfront_crtc_disable(struct intel_crtc *crtc,
+ struct drm_modeset_acquire_ctx *ctx,
+ bool enable)
+{
+ int ret;
+ struct drm_atomic_state *state;
+ struct intel_crtc_state *crtc_state;
+ struct drm_device *dev = crtc->base.dev;
+ enum pipe pipe = crtc->pipe;
+
+ state = drm_atomic_state_alloc(dev);
+ if (!state)
+ return -ENOMEM;
+
+ state->acquire_ctx = ctx;
+
+ crtc_state = intel_atomic_get_crtc_state(state, crtc);
+ if (IS_ERR(crtc_state)) {
+ ret = PTR_ERR(crtc_state);
+ drm_atomic_state_free(state);
+ return ret;
+ }
+
+ DRM_DEBUG_KMS("%sabling crtc %c %s upfront link train\n",
+ enable ? "En" : "Dis",
+ pipe_name(pipe),
+ enable ? "after" : "before");
+
+ crtc_state->base.active = enable;
+ ret = drm_atomic_commit(state);
+ if (ret)
+ drm_atomic_state_free(state);
+
+ return ret;
+}
+
static int
intel_dp_downstream_max_dotclock(struct intel_dp *intel_dp)
{
@@ -281,6 +326,17 @@ static int intel_dp_common_rates(struct intel_dp *intel_dp,
int source_len, sink_len;
sink_len = intel_dp_sink_rates(intel_dp, &sink_rates);
+
+ /* Cap sink rates w.r.t upfront values */
+ if (intel_dp->max_link_rate_upfront) {
+ int len = sink_len - 1;
+
+ while (len > 0 && sink_rates[len] >
+ intel_dp->max_link_rate_upfront)
+ len--;
+ sink_len = len + 1;
+ }
+
source_len = intel_dp_source_rates(intel_dp, &source_rates);
return intersect_rates(source_rates, source_len,
@@ -288,6 +344,92 @@ static int intel_dp_common_rates(struct intel_dp *intel_dp,
common_rates);
}
+static bool intel_dp_upfront_link_train(struct intel_dp *intel_dp)
+{
+ struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
+ struct intel_encoder *intel_encoder = &intel_dig_port->base;
+ struct drm_device *dev = intel_encoder->base.dev;
+ struct drm_i915_private *dev_priv = to_i915(dev);
+ struct drm_mode_config *config = &dev->mode_config;
+ struct drm_modeset_acquire_ctx ctx;
+ struct intel_crtc *intel_crtc;
+ struct drm_crtc *crtc = NULL;
+ struct intel_shared_dpll *pll;
+ struct intel_shared_dpll_config tmp_pll_config;
+ bool disable_dpll = false;
+ int ret;
+ bool done = false, has_mst = false;
+ uint8_t max_lanes;
+ int common_rates[DP_MAX_SUPPORTED_RATES] = {};
+ int common_len;
+ enum intel_display_power_domain power_domain;
+
+ power_domain = intel_display_port_power_domain(intel_encoder);
+ intel_display_power_get(dev_priv, power_domain);
+
+ common_len = intel_dp_common_rates(intel_dp, common_rates);
+ max_lanes = intel_dp_max_lane_count(intel_dp);
+ if (WARN_ON(common_len <= 0))
+ return true;
+
+ drm_modeset_acquire_init(&ctx, 0);
+retry:
+ ret = drm_modeset_lock(&config->connection_mutex, &ctx);
+ if (ret)
+ goto exit_fail;
+
+ if (intel_encoder->base.crtc) {
+ crtc = intel_encoder->base.crtc;
+
+ ret = drm_modeset_lock(&crtc->mutex, &ctx);
+ if (ret)
+ goto exit_fail;
+
+ ret = drm_modeset_lock(&crtc->primary->mutex, &ctx);
+ if (ret)
+ goto exit_fail;
+
+ intel_crtc = to_intel_crtc(crtc);
+ pll = intel_crtc->config->shared_dpll;
+ disable_dpll = true;
+ has_mst = intel_crtc_has_type(intel_crtc->config,
+ INTEL_OUTPUT_DP_MST);
+ ret = intel_dp_upfront_crtc_disable(intel_crtc, &ctx, false);
+ if (ret)
+ goto exit_fail;
+ }
+
+ mutex_lock(&dev_priv->dpll_lock);
+ if (disable_dpll) {
+ /* Clear the PLL config state */
+ tmp_pll_config = pll->config;
+ pll->config.crtc_mask = 0;
+ }
+
+ done = intel_dp->upfront_link_train(intel_dp,
+ common_rates[common_len-1],
+ max_lanes,
+ has_mst,
+ true);
+ if (disable_dpll)
+ pll->config = tmp_pll_config;
+
+ mutex_unlock(&dev_priv->dpll_lock);
+
+ if (crtc)
+ ret = intel_dp_upfront_crtc_disable(intel_crtc, &ctx, true);
+
+exit_fail:
+ if (ret == -EDEADLK) {
+ drm_modeset_backoff(&ctx);
+ goto retry;
+ }
+ drm_modeset_drop_locks(&ctx);
+ drm_modeset_acquire_fini(&ctx);
+ intel_display_power_put(dev_priv, power_domain);
+ return done;
+}
+
static enum drm_mode_status
intel_dp_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
@@ -311,6 +453,19 @@ intel_dp_mode_valid(struct drm_connector *connector,
target_clock = fixed_mode->clock;
}
+ if (intel_dp->upfront_link_train && !intel_dp->upfront_done) {
+ bool do_upfront_link_train;
+ /* Do not do upfront link train, if it is a compliance
+ * request
+ */
+ do_upfront_link_train = !intel_dp->upfront_done &&
+ (intel_dp->compliance_test_type !=
+ DP_TEST_LINK_TRAINING);
+
+ if (do_upfront_link_train)
+ intel_dp->upfront_done = intel_dp_upfront_link_train(intel_dp);
+ }
+
max_link_clock = intel_dp_max_link_rate(intel_dp);
max_lanes = intel_dp_max_lane_count(intel_dp);
@@ -1499,6 +1654,9 @@ intel_dp_max_link_rate(struct intel_dp *intel_dp)
int rates[DP_MAX_SUPPORTED_RATES] = {};
int len;
+ if (intel_dp->max_link_rate_upfront)
+ return intel_dp->max_link_rate_upfront;
+
len = intel_dp_common_rates(intel_dp, rates);
if (WARN_ON(len <= 0))
return 162000;
@@ -1644,6 +1802,21 @@ intel_dp_compute_config(struct intel_encoder *encoder,
for (; bpp >= 6*3; bpp -= 2*3) {
mode_rate = intel_dp_link_required(adjusted_mode->crtc_clock,
bpp);
+
+ if (!is_edp(intel_dp) && intel_dp->upfront_done) {
+ clock = max_clock;
+ lane_count = intel_dp->max_lanes_upfront;
+ link_clock = intel_dp->max_link_rate_upfront;
+ link_avail = intel_dp_max_data_rate(link_clock,
+ lane_count);
+ mode_rate = intel_dp_link_required(adjusted_mode->crtc_clock,
+ bpp);
+ if (mode_rate <= link_avail)
+ goto found;
+ else
+ continue;
+ }
+
clock = max_clock;
lane_count = max_lane_count;
link_clock = common_rates[clock];
@@ -1672,7 +1845,6 @@ found:
}
pipe_config->lane_count = lane_count;
-
pipe_config->pipe_bpp = bpp;
pipe_config->port_clock = common_rates[clock];
@@ -4453,8 +4625,12 @@ intel_dp_long_pulse(struct intel_connector *intel_connector)
out:
if ((status != connector_status_connected) &&
- (intel_dp->is_mst == false))
+ (intel_dp->is_mst == false)) {
intel_dp_unset_edid(intel_dp);
+ intel_dp->upfront_done = false;
+ intel_dp->max_lanes_upfront = 0;
+ intel_dp->max_link_rate_upfront = 0;
+ }
intel_display_power_put(to_i915(dev), power_domain);
return;
@@ -5698,6 +5874,12 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
if (type == DRM_MODE_CONNECTOR_eDP)
intel_encoder->type = INTEL_OUTPUT_EDP;
+ /* Initialize upfront link training vfunc for DP */
+ if (intel_encoder->type != INTEL_OUTPUT_EDP) {
+ if (HAS_DDI(dev_priv))
+ intel_dp->upfront_link_train = intel_ddi_link_train;
+ }
+
/* eDP only on port B and/or C on vlv/chv */
if (WARN_ON((IS_VALLEYVIEW(dev) || IS_CHERRYVIEW(dev)) &&
is_edp(intel_dp) && port != PORT_B && port != PORT_C))
@@ -304,7 +304,6 @@ intel_dp_link_training_channel_equalization(struct intel_dp *intel_dp)
intel_dp_set_idle_link_train(intel_dp);
return intel_dp->channel_eq_status;
-
}
void intel_dp_stop_link_train(struct intel_dp *intel_dp)
@@ -886,6 +886,12 @@ struct intel_dp {
enum hdmi_force_audio force_audio;
bool limited_color_range;
bool color_range_auto;
+
+ /* Upfront link train parameters */
+ int max_link_rate_upfront;
+ uint8_t max_lanes_upfront;
+ bool upfront_done;
+
uint8_t dpcd[DP_RECEIVER_CAP_SIZE];
uint8_t psr_dpcd[EDP_PSR_RECEIVER_CAP_SIZE];
uint8_t downstream_ports[DP_MAX_DOWNSTREAM_PORTS];
@@ -943,6 +949,11 @@ struct intel_dp {
/* This is called before a link training is starterd */
void (*prepare_link_retrain)(struct intel_dp *intel_dp);
+ /* For Upfront link training */
+ bool (*upfront_link_train)(struct intel_dp *intel_dp, int clock,
+ uint8_t lane_count, bool link_mst,
+ bool is_upfront);
+
/* Displayport compliance testing */
unsigned long compliance_test_type;
unsigned long compliance_test_data;
@@ -1165,7 +1176,8 @@ void intel_ddi_clock_get(struct intel_encoder *encoder,
void intel_ddi_set_vc_payload_alloc(struct drm_crtc *crtc, bool state);
uint32_t ddi_signal_levels(struct intel_dp *intel_dp);
bool intel_ddi_link_train(struct intel_dp *intel_dp, int max_link_rate,
- uint8_t max_lane_count, bool link_mst);
+ uint8_t max_lane_count, bool link_mst,
+ bool is_upfront);
struct intel_shared_dpll *intel_ddi_get_link_dpll(struct intel_dp *intel_dp,
int clock);
unsigned int intel_fb_align_height(struct drm_device *dev,