@@ -153,12 +153,18 @@ static void vlv_steal_power_sequencer(struct drm_device *dev,
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);
+ temp = min(source_max, sink_max);
+ /* Limit the max value to max fallback value during link train
+ * failure handling.
+ */
+ if (intel_dp->fallback_lane_count)
+ return min(temp, intel_dp->fallback_lane_count);
- return min(source_max, sink_max);
+ return temp;
}
/*
@@ -281,6 +287,15 @@ 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);
+ /* Limit the max sink rate to the max fallback link rate
+ * found during link training failure handling.
+ */
+ if (intel_dp->fallback_link_rate) {
+ int len = sink_len - 1;
+ while (len > 0 && sink_rates[len] > intel_dp->fallback_link_rate)
+ len--;
+ sink_len = len + 1;
+ }
source_len = intel_dp_source_rates(intel_dp, &source_rates);
return intersect_rates(source_rates, source_len,
@@ -288,6 +303,46 @@ static int intel_dp_common_rates(struct intel_dp *intel_dp,
common_rates);
}
+static int intel_dp_link_rate_index(struct intel_dp *intel_dp,
+ int *common_rates, int link_rate)
+{
+ int common_len;
+ int index;
+
+ common_len = intel_dp_common_rates(intel_dp, common_rates);
+ for (index = 0; index < common_len; index++) {
+ if (link_rate == common_rates[common_len - index - 1])
+ return common_len - index - 1;
+ }
+
+ return -1;
+}
+
+int intel_dp_get_link_train_fallback_values(struct intel_dp *intel_dp,
+ int link_rate, uint8_t lane_count)
+{
+ int common_rates[DP_MAX_SUPPORTED_RATES] = {};
+ int common_len;
+ int link_rate_index = -1;
+
+ common_len = intel_dp_common_rates(intel_dp, common_rates);
+ link_rate_index = intel_dp_link_rate_index(intel_dp,
+ common_rates,
+ link_rate);
+ if (link_rate_index > 0) {
+ intel_dp->fallback_link_rate = common_rates[link_rate_index - 1];
+ intel_dp->fallback_lane_count = lane_count;
+ } else if (lane_count > 1) {
+ intel_dp->fallback_link_rate = 0;
+ intel_dp->fallback_lane_count = lane_count >> 1;
+ } else {
+ DRM_ERROR("Link Training Unsuccessful\n");
+ return -1;
+ }
+
+ return 0;
+}
+
static enum drm_mode_status
intel_dp_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
@@ -891,6 +891,8 @@ struct intel_dp {
uint32_t DP;
int link_rate;
uint8_t lane_count;
+ int fallback_link_rate;
+ uint8_t fallback_lane_count;
uint8_t sink_count;
bool link_mst;
bool has_audio;
@@ -1393,6 +1395,8 @@ bool intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
void intel_dp_set_link_params(struct intel_dp *intel_dp,
int link_rate, uint8_t lane_count,
bool link_mst);
+int intel_dp_get_link_train_fallback_values(struct intel_dp *intel_dp,
+ int link_rate, uint8_t lane_count);
void intel_dp_start_link_train(struct intel_dp *intel_dp);
void intel_dp_stop_link_train(struct intel_dp *intel_dp);
void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode);