[PATCH 072/108] drm/i915/dp_link_caps: Add helper to find position of matching config

Imre Deak
2026-04-28T12:55:28Z

Add a helper to find the iteration position of a given link
configuration within a specified iteration order over the currently
allowed configurations. The rate being looked up may differ from the
nominal rate tracked for allowed configurations due to platform-specific
PLL divider restrictions. Fuzzy matching allows selecting the closest
supported rate.

This is needed by a follow-up change replacing the exact-rate look up in
the fallback code (intel_dp_link_config_index()) with the helper added
here.

Signed-off-by: Imre Deak <imre.deak@intel.com>
---
.../gpu/drm/i915/display/intel_dp_link_caps.c | 88 +++++++++++++++++++
.../gpu/drm/i915/display/intel_dp_link_caps.h | 24 +++++
2 files changed, 112 insertions(+)

diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c
index 48b57aea557ca..284d28f9d98d7 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c
+++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c
@@ -6,6 +6,7 @@
#include <linux/bitops.h>
#include <linux/debugfs.h>
#include <linux/log2.h>
+#include <linux/math.h>
#include <linux/slab.h>
#include <linux/sort.h>
#include <linux/string.h>
@@ -443,6 +444,93 @@ bool intel_dp_link_caps_get_config_by_pos(struct intel_dp_link_caps *link_caps,
config, config_idx);
}

+static bool is_within_percent(int actual, int nominal, int percent)
+{
+ int diff = abs(actual - nominal);
+
+ if (WARN_ON(percent == 0 ||
+ diff > INT_MAX / 100 || nominal > INT_MAX / percent))
+ return false;
+
+ return diff * 100 <= nominal * percent;
+}
+
+static int
+find_config_table_entry_pos(const struct intel_dp_link_caps_config_table *config_table,
+ struct intel_dp_link_caps_config_order config_order, u32 config_mask,
+ enum intel_dp_link_caps_config_match_type match_type,
+ const struct intel_dp_link_config *link_config)
+{
+ struct intel_dp_link_config iter_config;
+ int iter_config_idx;
+ int iter_pos;
+
+ for (iter_pos = 0;
+ get_table_config_by_pos(config_table, config_order, iter_pos,
+ &iter_config, &iter_config_idx);
+ iter_pos++) {
+ if (!(BIT(iter_config_idx) & config_mask))
+ continue;
+
+ if (iter_config.lane_count != link_config->lane_count)
+ continue;
+
+ /*
+ * link_config->rate may be platform-derived rather than the nominal
+ * supported link rate.
+ *
+ * When the caller requests fuzzy rate matching, accept a nominal rate
+ * within 1 percent of the requested rate.
+ *
+ * The DP spec seems to allow at most 300 ppm of symbol clock tolerance,
+ * excluding SSC. However, at least on g4x, the 2.7 Gbps rate exceeds
+ * that (~5000ppm); see intel_dp_compute_rate(). So allow 10000 ppm, or
+ * a 1 percent difference.
+ *
+ * The first match is also the best one, since nominal rates are guaranteed
+ * to be spaced much farther apart than 1 percent.
+ *
+ * TODO: Track the nominal link rate separately, pass it here, and require
+ * an exact match.
+ */
+ if (iter_config.rate != link_config->rate &&
+ (match_type == INTEL_DP_LINK_CAPS_CONFIG_MATCH_EXACT ||
+ !is_within_percent(link_config->rate, iter_config.rate, 1)))
+ continue;
+
+ return iter_pos;
+ }
+
+ return -1;
+}
+
+/**
+ * intel_dp_link_caps_find_allowed_config_pos - find matching allowed config position
+ * @link_caps: link capabilities state
+ * @config_order: iteration order
+ * @match_type: requested match type
+ * @link_config: link configuration to match
+ *
+ * Search the currently allowed link configurations for a match to
+ * @link_config.
+ *
+ * Return:
+ * - The position of the first matching allowed configuration in the
+ * @config_order iteration.
+ * - %-1 if no allowed configuration matches.
+ */
+int intel_dp_link_caps_find_allowed_config_pos(struct intel_dp_link_caps *link_caps,
+ struct intel_dp_link_caps_config_order config_order,
+ enum intel_dp_link_caps_config_match_type match_type,
+ const struct intel_dp_link_config *link_config)
+{
+ u32 allowed_config_mask = intel_dp_link_caps_get_allowed_config_mask(link_caps);
+
+ return find_config_table_entry_pos(&link_caps->config_table, config_order,
+ allowed_config_mask, match_type,
+ link_config);
+}
+
static void set_max_link_limits_no_update(struct intel_dp_link_caps *link_caps,
const struct intel_dp_link_config *max_link_limits)
{
diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h
index 79cd50db90ba6..cacdda15a36af 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h
+++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h
@@ -69,6 +69,26 @@ struct intel_dp_link_caps_config_order {
enum intel_dp_link_caps_config_order_direction dir;
};

+/**
+ * enum intel_dp_link_caps_config_match_type - configuration match semantics
+ * @INTEL_DP_LINK_CAPS_CONFIG_MATCH_EXACT:
+ * Require an exact nominal link rate match and an
+ * exact lane count match.
+ * @INTEL_DP_LINK_CAPS_CONFIG_MATCH_FUZZY_RATE:
+ * Require an exact lane count match, but allow the
+ * requested link rate to match approximately to a
+ * supported nominal link rate.
+ *
+ * Selects how
+ * intel_dp_link_caps_find_allowed_config_pos() matches
+ * the requested &struct intel_dp_link_config against the currently
+ * allowed configurations.
+ */
+enum intel_dp_link_caps_config_match_type {
+ INTEL_DP_LINK_CAPS_CONFIG_MATCH_EXACT,
+ INTEL_DP_LINK_CAPS_CONFIG_MATCH_FUZZY_RATE,
+};
+
int intel_dp_link_caps_common_rate(struct intel_dp_link_caps *link_caps, int index);
int intel_dp_link_caps_common_rate_idx(struct intel_dp_link_caps *link_caps, int rate);
int intel_dp_link_caps_max_common_rate(struct intel_dp_link_caps *link_caps);
@@ -89,6 +109,10 @@ intel_dp_link_caps_get_config_by_pos(struct intel_dp_link_caps *link_caps,
struct intel_dp_link_config *config, int *config_idx);
void intel_dp_link_config_get(struct intel_dp_link_caps *link_caps,
int idx, int *link_rate, int *lane_count);
+int intel_dp_link_caps_find_allowed_config_pos(struct intel_dp_link_caps *link_caps,
+ struct intel_dp_link_caps_config_order order,
+ enum intel_dp_link_caps_config_match_type match_type,
+ const struct intel_dp_link_config *config);

void intel_dp_link_caps_get_max_limits(struct intel_dp_link_caps *link_caps,
struct intel_dp_link_config *max_link_limits);
--
2.49.1