drm/i915/dp_mst: Reduce the link parameters in BW order after LT failures

On MST links - at least for some MST branch devices - the list of modes
returned to users on an enabled link depends on the current link
rate/lane count parameters (besides the DPRX link capabilities, any MST
branch BW limit and the maximum link parameters reduced after LT
failures). In particular the MST branch BW limit may depend on the link
rate/lane count parameters programmed to DPCD. After an LT failure and
limiting the maximum link parameters accordingly, users should see a
mode list reflecting these new limits. However with the current fallback
order this isn't ensured, as the new limit could allow for modes
requiring a higher link BW, but these modes will be filtered out due to
the enabled link's lower link BW.

Ensure that the mode list changes in a consistent way after a link
training failure and reducing the link parameters by changing the
fallback order on MST links to happen in BW order.

v2:
- s/INTEL_DP_MAX_SUPPORTED_LANE_COUNTS/INTEL_DP_MAX_SUPPORTED_LANE_CONFIGS
  and s/num_common_lane_counts/num_common_lane_configs to make the
  difference wrt. max lane counts clearer. (Suraj)
- Add a TODO comment to make the SST fallback logic work the same way as
  MST. (Arun)
- Use sort_r()'s default swap function instead of a custom one.

Cc: Suraj Kandpal <suraj.kandpal@intel.com>
Cc: Arun R Murthy <arun.r.murthy@intel.com>
Reviewed-by: Suraj Kandpal <suraj.kandpal@intel.com>
Reviewed-by: Arun R Murthy <arun.r.murthy@intel.com>
Signed-off-by: Imre Deak <imre.deak@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20240729144458.2763667-1-imre.deak@intel.com
This commit is contained in:
Imre Deak 2024-07-29 17:44:58 +03:00
parent 96c468c366
commit aa705f7ec6
4 changed files with 159 additions and 2 deletions

View File

@ -1778,6 +1778,18 @@ struct intel_dp {
int common_rates[DP_MAX_SUPPORTED_RATES];
struct {
/* TODO: move the rest of link specific fields to here */
/* common rate,lane_count configs in bw order */
int num_configs;
#define INTEL_DP_MAX_LANE_COUNT 4
#define INTEL_DP_MAX_SUPPORTED_LANE_CONFIGS (ilog2(INTEL_DP_MAX_LANE_COUNT) + 1)
#define INTEL_DP_LANE_COUNT_EXP_BITS order_base_2(INTEL_DP_MAX_SUPPORTED_LANE_CONFIGS)
#define INTEL_DP_LINK_RATE_IDX_BITS (BITS_PER_TYPE(u8) - INTEL_DP_LANE_COUNT_EXP_BITS)
#define INTEL_DP_MAX_LINK_CONFIGS (DP_MAX_SUPPORTED_RATES * \
INTEL_DP_MAX_SUPPORTED_LANE_CONFIGS)
struct intel_dp_link_config {
u8 link_rate_idx:INTEL_DP_LINK_RATE_IDX_BITS;
u8 lane_count_exp:INTEL_DP_LANE_COUNT_EXP_BITS;
} configs[INTEL_DP_MAX_LINK_CONFIGS];
/* Max lane count for the current link */
int max_lane_count;
/* Max rate for the current link */

View File

@ -29,6 +29,7 @@
#include <linux/i2c.h>
#include <linux/notifier.h>
#include <linux/slab.h>
#include <linux/sort.h>
#include <linux/string_helpers.h>
#include <linux/timekeeping.h>
#include <linux/types.h>
@ -634,6 +635,106 @@ int intel_dp_rate_index(const int *rates, int len, int rate)
return -1;
}
static int intel_dp_link_config_rate(struct intel_dp *intel_dp,
const struct intel_dp_link_config *lc)
{
return intel_dp_common_rate(intel_dp, lc->link_rate_idx);
}
static int intel_dp_link_config_lane_count(const struct intel_dp_link_config *lc)
{
return 1 << lc->lane_count_exp;
}
static int intel_dp_link_config_bw(struct intel_dp *intel_dp,
const struct intel_dp_link_config *lc)
{
return drm_dp_max_dprx_data_rate(intel_dp_link_config_rate(intel_dp, lc),
intel_dp_link_config_lane_count(lc));
}
static int link_config_cmp_by_bw(const void *a, const void *b, const void *p)
{
struct intel_dp *intel_dp = (struct intel_dp *)p; /* remove const */
const struct intel_dp_link_config *lc_a = a;
const struct intel_dp_link_config *lc_b = b;
int bw_a = intel_dp_link_config_bw(intel_dp, lc_a);
int bw_b = intel_dp_link_config_bw(intel_dp, lc_b);
if (bw_a != bw_b)
return bw_a - bw_b;
return intel_dp_link_config_rate(intel_dp, lc_a) -
intel_dp_link_config_rate(intel_dp, lc_b);
}
static void intel_dp_link_config_init(struct intel_dp *intel_dp)
{
struct drm_i915_private *i915 = dp_to_i915(intel_dp);
struct intel_dp_link_config *lc;
int num_common_lane_configs;
int i;
int j;
if (drm_WARN_ON(&i915->drm, !is_power_of_2(intel_dp_max_common_lane_count(intel_dp))))
return;
num_common_lane_configs = ilog2(intel_dp_max_common_lane_count(intel_dp)) + 1;
if (drm_WARN_ON(&i915->drm, intel_dp->num_common_rates * num_common_lane_configs >
ARRAY_SIZE(intel_dp->link.configs)))
return;
intel_dp->link.num_configs = intel_dp->num_common_rates * num_common_lane_configs;
lc = &intel_dp->link.configs[0];
for (i = 0; i < intel_dp->num_common_rates; i++) {
for (j = 0; j < num_common_lane_configs; j++) {
lc->lane_count_exp = j;
lc->link_rate_idx = i;
lc++;
}
}
sort_r(intel_dp->link.configs, intel_dp->link.num_configs,
sizeof(intel_dp->link.configs[0]),
link_config_cmp_by_bw, NULL,
intel_dp);
}
void intel_dp_link_config_get(struct intel_dp *intel_dp, int idx, int *link_rate, int *lane_count)
{
struct drm_i915_private *i915 = dp_to_i915(intel_dp);
const struct intel_dp_link_config *lc;
if (drm_WARN_ON(&i915->drm, idx < 0 || idx >= intel_dp->link.num_configs))
idx = 0;
lc = &intel_dp->link.configs[idx];
*link_rate = intel_dp_link_config_rate(intel_dp, lc);
*lane_count = intel_dp_link_config_lane_count(lc);
}
int intel_dp_link_config_index(struct intel_dp *intel_dp, int link_rate, int lane_count)
{
int link_rate_idx = intel_dp_rate_index(intel_dp->common_rates, intel_dp->num_common_rates,
link_rate);
int lane_count_exp = ilog2(lane_count);
int i;
for (i = 0; i < intel_dp->link.num_configs; i++) {
const struct intel_dp_link_config *lc = &intel_dp->link.configs[i];
if (lc->lane_count_exp == lane_count_exp &&
lc->link_rate_idx == link_rate_idx)
return i;
}
return -1;
}
static void intel_dp_set_common_rates(struct intel_dp *intel_dp)
{
struct drm_i915_private *i915 = dp_to_i915(intel_dp);
@ -652,6 +753,8 @@ static void intel_dp_set_common_rates(struct intel_dp *intel_dp)
intel_dp->common_rates[0] = 162000;
intel_dp->num_common_rates = 1;
}
intel_dp_link_config_init(intel_dp);
}
static bool intel_dp_link_params_valid(struct intel_dp *intel_dp, int link_rate,

View File

@ -107,6 +107,8 @@ int intel_dp_max_common_rate(struct intel_dp *intel_dp);
int intel_dp_max_common_lane_count(struct intel_dp *intel_dp);
int intel_dp_common_rate(struct intel_dp *intel_dp, int index);
int intel_dp_rate_index(const int *rates, int len, int rate);
int intel_dp_link_config_index(struct intel_dp *intel_dp, int link_rate, int lane_count);
void intel_dp_link_config_get(struct intel_dp *intel_dp, int idx, int *link_rate, int *lane_count);
void intel_dp_update_sink_caps(struct intel_dp *intel_dp);
void intel_dp_reset_link_params(struct intel_dp *intel_dp);

View File

@ -1170,6 +1170,41 @@ static bool intel_dp_can_link_train_fallback_for_edp(struct intel_dp *intel_dp,
return true;
}
static bool reduce_link_params_in_bw_order(struct intel_dp *intel_dp,
const struct intel_crtc_state *crtc_state,
int *new_link_rate, int *new_lane_count)
{
int link_rate;
int lane_count;
int i;
i = intel_dp_link_config_index(intel_dp, crtc_state->port_clock, crtc_state->lane_count);
for (i--; i >= 0; i--) {
intel_dp_link_config_get(intel_dp, i, &link_rate, &lane_count);
if ((intel_dp->link.force_rate &&
intel_dp->link.force_rate != link_rate) ||
(intel_dp->link.force_lane_count &&
intel_dp->link.force_lane_count != lane_count))
continue;
/* TODO: Make switching from UHBR to non-UHBR rates work. */
if (drm_dp_is_uhbr_rate(crtc_state->port_clock) !=
drm_dp_is_uhbr_rate(link_rate))
continue;
break;
}
if (i < 0)
return false;
*new_link_rate = link_rate;
*new_lane_count = lane_count;
return true;
}
static int reduce_link_rate(struct intel_dp *intel_dp, int current_rate)
{
int rate_index;
@ -1231,8 +1266,13 @@ static bool reduce_link_params_in_rate_lane_order(struct intel_dp *intel_dp,
static bool reduce_link_params(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state,
int *new_link_rate, int *new_lane_count)
{
return reduce_link_params_in_rate_lane_order(intel_dp, crtc_state,
new_link_rate, new_lane_count);
/* TODO: Use the same fallback logic on SST as on MST. */
if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DP_MST))
return reduce_link_params_in_bw_order(intel_dp, crtc_state,
new_link_rate, new_lane_count);
else
return reduce_link_params_in_rate_lane_order(intel_dp, crtc_state,
new_link_rate, new_lane_count);
}
static int intel_dp_get_link_train_fallback_values(struct intel_dp *intel_dp,