@@ -2378,6 +2378,53 @@ intel_ddi_init_hdmi_connector(struct intel_digital_port *intel_dig_port)
return connector;
}
+
+bool intel_bxt_upfront_link_train(struct intel_dp *intel_dp,
+ int clock, uint8_t lane_count,
+ bool link_mst)
+{
+ struct intel_connector *connector = intel_dp->attached_connector;
+ struct intel_encoder *encoder = connector->encoder;
+ struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
+ struct intel_shared_dpll *pll;
+ struct intel_shared_dpll_config tmp_pll_config;
+ struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
+ enum intel_dpll_id dpll_id = (enum intel_dpll_id)dig_port->port;
+
+ /*
+ * FIXME: Works only for BXT.
+ * Select the required PLL. This works for platforms where
+ * there is no shared DPLL.
+ */
+ pll = &dev_priv->shared_dplls[dpll_id];
+ if (WARN_ON(pll->active_mask)) {
+ DRM_ERROR("Shared DPLL already in use. active_mask:%x\n", pll->active_mask);
+ return false;
+ }
+
+ tmp_pll_config = pll->config;
+
+ if (!bxt_ddi_dp_set_dpll_hw_state(clock, &pll->config.hw_state)) {
+ DRM_ERROR("Could not setup DPLL\n");
+ pll->config = tmp_pll_config;
+ return false;
+ }
+
+ /* Enable PLL followed by port */
+ pll->funcs.enable(dev_priv, pll);
+ intel_ddi_pre_enable_dp(encoder, clock, lane_count, pll, link_mst);
+
+ DRM_DEBUG_KMS("Upfront link train %s: link_clock:%d lanes:%d\n",
+ intel_dp->channel_eq_status ? "Passed" : "Failed", clock, lane_count);
+
+ /* Disable port followed by PLL for next retry/clean up */
+ intel_ddi_post_disable(encoder);
+ pll->funcs.disable(dev_priv, pll);
+
+ pll->config = tmp_pll_config;
+ return intel_dp->channel_eq_status;
+}
+
void intel_ddi_init(struct drm_device *dev, enum port port)
{
struct drm_i915_private *dev_priv = to_i915(dev);
@@ -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,224 @@ 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_sink_rates(struct intel_dp *intel_dp, const int **sink_rates)
+{
+ if (intel_dp->num_sink_rates) {
+ *sink_rates = intel_dp->sink_rates;
+ return intel_dp->num_sink_rates;
+ }
+
+ *sink_rates = default_rates;
+
+ return (intel_dp_max_link_bw(intel_dp) >> 3) + 1;
+}
+
+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_i915_private *dev_priv = to_i915(dig_port->base.base.dev);
+
+ /* WaDisableHBR2:skl */
+ if (IS_SKL_REVID(dev_priv, 0, SKL_REVID_B0))
+ return false;
+
+ if ((IS_HASWELL(dev_priv) && !IS_HSW_ULX(dev_priv)) ||
+ IS_BROADWELL(dev_priv) || (INTEL_GEN(dev_priv) >= 9))
+ return true;
+ else
+ return false;
+}
+
+static int
+intel_dp_source_rates(struct intel_dp *intel_dp, const int **source_rates)
+{
+ struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
+ struct drm_i915_private *dev_priv = to_i915(dig_port->base.base.dev);
+ int size;
+
+ if (IS_BROXTON(dev_priv)) {
+ *source_rates = bxt_rates;
+ size = ARRAY_SIZE(bxt_rates);
+ } else if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) {
+ *source_rates = skl_rates;
+ size = ARRAY_SIZE(skl_rates);
+ } else {
+ *source_rates = default_rates;
+ size = ARRAY_SIZE(default_rates);
+ }
+
+ /* This depends on the fact that 5.4 is last value in the array */
+ if (!intel_dp_source_supports_hbr2(intel_dp))
+ size--;
+
+ return size;
+}
+
+static int intersect_rates(const int *source_rates, int source_len,
+ const int *sink_rates, int sink_len,
+ int *common_rates)
+{
+ int i = 0, j = 0, k = 0;
+
+ while (i < source_len && j < sink_len) {
+ if (source_rates[i] == sink_rates[j]) {
+ if (WARN_ON(k >= DP_MAX_SUPPORTED_RATES))
+ return k;
+ common_rates[k] = source_rates[i];
+ ++k;
+ ++i;
+ ++j;
+ } else if (source_rates[i] < sink_rates[j]) {
+ ++i;
+ } else {
+ ++j;
+ }
+ }
+ return k;
+}
+
+static int intel_dp_common_rates(struct intel_dp *intel_dp,
+ int *common_rates)
+{
+ const int *source_rates, *sink_rates;
+ 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,
+ sink_rates, sink_len,
+ 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;
+ int ret;
+ bool done = false, has_mst = false;
+ uint8_t lane_count, max_lanes = 4;
+ int common_rates[DP_MAX_SUPPORTED_RATES] = {};
+ int clock, 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);
+ 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);
+ 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);
+
+ for (lane_count = max_lanes; lane_count >= 1 && !done;
+ lane_count >>= 1) {
+ for (clock = common_len - 1; clock >= 0 && !done; clock--) {
+ done = intel_dp->upfront_link_train(intel_dp,
+ common_rates[clock],
+ lane_count,
+ has_mst);
+ if (done) {
+ intel_dp->max_lanes_upfront = lane_count;
+ intel_dp->max_link_rate_upfront = common_rates[clock];
+ break;
+ }
+ }
+ }
+
+ 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)
@@ -211,6 +438,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);
@@ -1256,60 +1496,6 @@ intel_dp_aux_init(struct intel_dp *intel_dp, struct intel_connector *connector)
intel_dp->aux.transfer = intel_dp_aux_transfer;
}
-static int
-intel_dp_sink_rates(struct intel_dp *intel_dp, const int **sink_rates)
-{
- if (intel_dp->num_sink_rates) {
- *sink_rates = intel_dp->sink_rates;
- return intel_dp->num_sink_rates;
- }
-
- *sink_rates = default_rates;
-
- return (intel_dp_max_link_bw(intel_dp) >> 3) + 1;
-}
-
-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;
-
- /* WaDisableHBR2:skl */
- if (IS_SKL_REVID(dev, 0, SKL_REVID_B0))
- return false;
-
- if ((IS_HASWELL(dev) && !IS_HSW_ULX(dev)) || IS_BROADWELL(dev) ||
- (INTEL_INFO(dev)->gen >= 9))
- return true;
- else
- return false;
-}
-
-static int
-intel_dp_source_rates(struct intel_dp *intel_dp, const int **source_rates)
-{
- struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
- struct drm_device *dev = dig_port->base.base.dev;
- int size;
-
- if (IS_BROXTON(dev)) {
- *source_rates = bxt_rates;
- size = ARRAY_SIZE(bxt_rates);
- } else if (IS_SKYLAKE(dev) || IS_KABYLAKE(dev)) {
- *source_rates = skl_rates;
- size = ARRAY_SIZE(skl_rates);
- } else {
- *source_rates = default_rates;
- size = ARRAY_SIZE(default_rates);
- }
-
- /* This depends on the fact that 5.4 is last value in the array */
- if (!intel_dp_source_supports_hbr2(intel_dp))
- size--;
-
- return size;
-}
-
static void
intel_dp_set_clock(struct intel_encoder *encoder,
struct intel_crtc_state *pipe_config)
@@ -1343,42 +1529,6 @@ intel_dp_set_clock(struct intel_encoder *encoder,
}
}
-static int intersect_rates(const int *source_rates, int source_len,
- const int *sink_rates, int sink_len,
- int *common_rates)
-{
- int i = 0, j = 0, k = 0;
-
- while (i < source_len && j < sink_len) {
- if (source_rates[i] == sink_rates[j]) {
- if (WARN_ON(k >= DP_MAX_SUPPORTED_RATES))
- return k;
- common_rates[k] = source_rates[i];
- ++k;
- ++i;
- ++j;
- } else if (source_rates[i] < sink_rates[j]) {
- ++i;
- } else {
- ++j;
- }
- }
- return k;
-}
-
-static int intel_dp_common_rates(struct intel_dp *intel_dp,
- int *common_rates)
-{
- const int *source_rates, *sink_rates;
- int source_len, sink_len;
-
- sink_len = intel_dp_sink_rates(intel_dp, &sink_rates);
- source_len = intel_dp_source_rates(intel_dp, &source_rates);
-
- return intersect_rates(source_rates, source_len,
- sink_rates, sink_len,
- common_rates);
-}
static void snprintf_int_array(char *str, size_t len,
const int *array, int nelem)
@@ -1436,6 +1586,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;
@@ -1472,7 +1625,7 @@ intel_dp_compute_config(struct intel_encoder *encoder,
enum port port = dp_to_dig_port(intel_dp)->port;
struct intel_crtc *intel_crtc = to_intel_crtc(pipe_config->base.crtc);
struct intel_connector *intel_connector = intel_dp->attached_connector;
- int lane_count, clock;
+ int lane_count, clock = 0;
int min_lane_count = 1;
int max_lane_count = intel_dp_max_lane_count(intel_dp);
/* Conveniently, the link BW constants become indices with a shift...*/
@@ -1551,11 +1704,23 @@ 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;
+ }
for (clock = min_clock; clock <= max_clock; clock++) {
for (lane_count = min_lane_count;
- lane_count <= max_lane_count;
- lane_count <<= 1) {
+ lane_count <= max_lane_count;
+ lane_count <<= 1) {
link_clock = common_rates[clock];
link_avail = intel_dp_max_data_rate(link_clock,
@@ -1565,6 +1730,7 @@ intel_dp_compute_config(struct intel_encoder *encoder,
goto found;
}
}
+
}
}
@@ -1585,7 +1751,6 @@ found:
}
pipe_config->lane_count = lane_count;
-
pipe_config->pipe_bpp = bpp;
pipe_config->port_clock = common_rates[clock];
@@ -4244,7 +4409,7 @@ intel_dp_long_pulse(struct intel_connector *intel_connector)
struct drm_device *dev = connector->dev;
enum drm_connector_status status;
enum intel_display_power_domain power_domain;
- u8 sink_irq_vector = 0;
+ u8 sink_irq_vector;
power_domain = intel_display_port_aux_power_domain(intel_encoder);
intel_display_power_get(to_i915(dev), power_domain);
@@ -4337,9 +4502,12 @@ intel_dp_long_pulse(struct intel_connector *intel_connector)
}
out:
- if ((status != connector_status_connected) &&
- (intel_dp->is_mst == false))
+ if (status != connector_status_connected) {
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;
@@ -5587,6 +5755,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 (IS_BROXTON(dev_priv))
+ intel_dp->upfront_link_train = intel_bxt_upfront_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))
@@ -232,7 +232,6 @@ static u32 intel_dp_training_pattern(struct intel_dp *intel_dp)
static void
intel_dp_link_training_channel_equalization(struct intel_dp *intel_dp)
{
- bool channel_eq = false;
int tries, cr_tries;
u32 training_pattern;
@@ -248,7 +247,7 @@ intel_dp_link_training_channel_equalization(struct intel_dp *intel_dp)
tries = 0;
cr_tries = 0;
- channel_eq = false;
+ intel_dp->channel_eq_status = false;
for (;;) {
uint8_t link_status[DP_LINK_STATUS_SIZE];
@@ -276,7 +275,7 @@ intel_dp_link_training_channel_equalization(struct intel_dp *intel_dp)
if (drm_dp_channel_eq_ok(link_status,
intel_dp->lane_count)) {
- channel_eq = true;
+ intel_dp->channel_eq_status = true;
break;
}
@@ -302,7 +301,7 @@ intel_dp_link_training_channel_equalization(struct intel_dp *intel_dp)
intel_dp_set_idle_link_train(intel_dp);
- if (channel_eq)
+ if (intel_dp->channel_eq_status)
DRM_DEBUG_KMS("Channel EQ done. DP Training successful\n");
}
@@ -869,6 +869,15 @@ struct intel_dp {
enum hdmi_force_audio force_audio;
bool limited_color_range;
bool color_range_auto;
+
+ /* Whether Channel Equalization was achieved during Link training */
+ bool channel_eq_status;
+
+ /* 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];
@@ -926,6 +935,10 @@ 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);
+
/* Displayport compliance testing */
unsigned long compliance_test_type;
unsigned long compliance_test_data;
@@ -1145,6 +1158,9 @@ void intel_ddi_clock_get(struct intel_encoder *encoder,
struct intel_crtc_state *pipe_config);
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_bxt_upfront_link_train(struct intel_dp *intel_dp,
+ int clock, uint8_t lane_count,
+ bool link_mst);
unsigned int intel_fb_align_height(struct drm_device *dev,
unsigned int height,