// SPDX-License-Identifier: MIT /* * Copyright © 2026 Intel Corporation */ #include #include #include #include #include #include #include #include #include #include "intel_display_core.h" #include "intel_display_types.h" #include "intel_display_utils.h" #include "intel_dp.h" #include "intel_dp_link_caps.h" /** * DOC: DisplayPort link capabilities * * The Intel DP link caps API tracks the supported and allowed * DisplayPort link configurations for a DP encoder and its attached * connectors, and provides helpers to iterate over the allowed * configurations and constrain them by filtering, disabling, or * limiting them to maximum link parameters. * * Locking * ------- * * All accesses to this API must be serialized. The only exception * is intel_dp_link_caps_get_max_limits(), which allow lockless * lookup. Such lookups may observe an out-of-sync &struct * intel_dp_link_config tuple, i.e. a rate from one state and a lane * count from another. * * The Intel i915/xe drivers ensure the above serialization by holding * &drm_mode_config.connection_mutex and, while holding the lock, * waiting for any pending asynchronous atomic commits. This also allows * use of the API from the tails of asynchronous atomic commits, which * cannot hold the lock. * * Iterating and restricting link configurations * --------------------------------------------- * * The link configuration iterators can iterate the ``allowed * configurations`` during modeset configuration selection or link * training fallback handling in a configurable order. * * The iteration order can depend on connector type (eDP, DP SST, * DP MST) and modeset-specific conditions or driver policies, such * as DSC vs. non-DSC modes, power saving vs. better user experience, * or policy changes after a link training failure. * * The configurations exposed via the iterators can be additionally * constrained in the following ways: * * - Filtered for a given modeset based on modeset-specific conditions. * Examples for such conditions include driver policies preferring * power saving or better user experience, post-link training failure * preference changes, or sink automated test requests limiting the * usable configurations. * * - Disabled permanently for the connected sink. Examples of reasons * to disable a configuration include a link training failure for a * given configuration or a driver workaround preventing the use of * a particular configuration. * * - Limited via a maximum link rate and lane count. For example, after * a link training failure, subsequent modesets may be limited to * configurations at or below the failed parameters. * * This mechanism exists for backward compatibility only. Eventually, * it will be removed in favor of relying solely on individually * disabled configurations, as described above. * * Terminology * ----------- * * ``Common link capabilities`` (or ``common caps``) refer to the link * rates and maximum lane count supported by both the source and the * sink, i.e. the intersection of their respective capabilities. * * ``Supported configurations`` are all configurations defined by the * ``Common link capabilities``' link rates and maximum lane count. * * ``Disabled configurations`` are ``Supported configurations`` disabled * via this API. * * ``Enabled configurations`` are ``Supported configurations`` that are * not disabled. * * ``Forced configurations`` are ``Enabled configurations`` forced via * forced link parameter debugfs entries. * * ``Allowed configurations`` are the ``Enabled configurations``, or if * forcing is in effect the ``Forced configurations``, constrained by a * maximum rate and lane count set via the API. */ struct intel_dp_link_caps { struct intel_dp *dp; /* Rate, lane count caps common to source and sink. */ int num_rates; int rates[DP_MAX_SUPPORTED_RATES]; int max_lane_count; /* 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_entry { /* index into rates[] */ 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]; /* * Indices to intel_dp_link_caps::configs[] in rate/lane count, * lane_count/rate order. */ u8 rate_lane_map[INTEL_DP_MAX_LINK_CONFIGS]; u8 lane_rate_map[INTEL_DP_MAX_LINK_CONFIGS]; /* * Filter of configurations enabled for the current sink * connection. * * Each bit in the filter's configuration mask corresponds to a * configuration index in the intel_dp_link_caps::configs[] array. * * All configurations start out enabled in the filter after a * new sink is connected. Users disable configurations afterwards * via the link caps API. All configurations get re-enabled * internally in the following cases: * - when forcing a link rate or lane count * - when intel_dp_link_caps_update(reset=true) is called after * a new sink is connected * - when intel_dp_link_caps_update(reset=false) with changed * link capabilities is called * - when intel_dp_link_caps_reset() is called after a new sink * is connected */ struct intel_dp_link_caps_filter enabled_configs; /* * Allowed configurations are the supported configurations defined by * config_table.rates and config_table.max_lane_count, constrained by * config_table.enabled_configs and the forced_params and * max_limits values below. * * See get_allowed_config_filter() for the filter of these * configurations. */ /* * Forced parameters requested via debugfs. Remains set across sink * disconnects. */ struct intel_dp_link_config forced_params; /* * User set maximum limits. These limits constrain the currently * allowed set of configurations and are not adjusted when sink * capabilities change. * * max_limits.rate/lane_count may come from different allowed * configurations, i.e. the (max_limits.rate, max_limits.lane_count) * tuple itself may not be an allowed configuration. */ struct intel_dp_link_config max_limits; }; static_assert(BITS_PER_TYPE(((struct intel_dp_link_caps_filter *)NULL)->config_mask) >= ARRAY_SIZE(((struct intel_dp_link_caps *)NULL)->configs)); static struct intel_dp_link_caps_order bw_desc_config_order(void) { struct intel_dp_link_caps_order order = { .key = INTEL_DP_LINK_CAPS_ORDER_KEY_BW, .dir = INTEL_DP_LINK_CAPS_ORDER_DIR_DESC, }; return order; } static enum intel_dp_link_caps_order_key connector_compute_order_key(bool is_mst) { if (is_mst) return INTEL_DP_LINK_CAPS_ORDER_KEY_BW; else return INTEL_DP_LINK_CAPS_ORDER_KEY_RATE_LANE; } static enum intel_dp_link_caps_order_key connector_fallback_order_key(bool is_mst) { if (is_mst) return INTEL_DP_LINK_CAPS_ORDER_KEY_BW; else return INTEL_DP_LINK_CAPS_ORDER_KEY_LANE_RATE; } static enum intel_dp_link_caps_order_direction connector_compute_order_dir(bool is_mst, bool use_max_params) { if (is_mst || use_max_params) return INTEL_DP_LINK_CAPS_ORDER_DIR_DESC; else return INTEL_DP_LINK_CAPS_ORDER_DIR_ASC; } struct intel_dp_link_caps_order intel_dp_link_caps_connector_compute_order(struct intel_connector *connector) { struct intel_dp *intel_dp = intel_attached_dp(connector); struct intel_dp_link_caps_order order = { .key = connector_compute_order_key(connector->mst.dp), .dir = connector_compute_order_dir(connector->mst.dp, intel_dp->use_max_params) }; return order; } struct intel_dp_link_caps_order intel_dp_link_caps_connector_fallback_order(bool is_mst) { struct intel_dp_link_caps_order order = { .key = connector_fallback_order_key(is_mst), .dir = INTEL_DP_LINK_CAPS_ORDER_DIR_DESC, }; return order; } /* Get length of common rates array potentially limited by max_rate. */ static int intel_dp_common_len_rate_limit(struct intel_dp_link_caps *link_caps, int max_rate) { return intel_dp_rate_limit_len(link_caps->rates, link_caps->num_rates, max_rate); } static int intel_dp_common_rate(struct intel_dp_link_caps *link_caps, int index) { struct intel_display *display = to_intel_display(link_caps->dp); if (drm_WARN_ON(display->drm, index < 0 || index >= link_caps->num_rates)) return 162000; return link_caps->rates[index]; } /* Theoretical max between source and sink */ static int intel_dp_max_common_rate(struct intel_dp_link_caps *link_caps) { return intel_dp_common_rate(link_caps, link_caps->num_rates - 1); } void intel_dp_link_caps_print_common_rates(struct intel_dp_link_caps *link_caps) { struct intel_display *display = to_intel_display(link_caps->dp); DECLARE_SEQ_BUF(s, 128); int i; for (i = 0; i < link_caps->num_rates; i++) seq_buf_printf(&s, "%s%d", i ? ", " : "", link_caps->rates[i]); drm_dbg_kms(display->drm, "common rates: %s\n", seq_buf_str(&s)); } static int intel_dp_link_caps_max_common_lane_count(struct intel_dp_link_caps *link_caps) { return link_caps->max_lane_count; } static int forced_lane_count(struct intel_dp_link_caps *link_caps) { if (!link_caps->forced_params.lane_count) return 0; return clamp(link_caps->forced_params.lane_count, 1, intel_dp_link_caps_max_common_lane_count(link_caps)); } static int forced_link_rate(struct intel_dp_link_caps *link_caps) { int len; if (!link_caps->forced_params.rate) return 0; len = intel_dp_common_len_rate_limit(link_caps, link_caps->forced_params.rate); if (len == 0) return intel_dp_common_rate(link_caps, 0); return intel_dp_common_rate(link_caps, len - 1); } void intel_dp_link_caps_get_forced_params(struct intel_dp_link_caps *link_caps, struct intel_dp_link_config *forced_params) { forced_params->rate = forced_link_rate(link_caps); forced_params->lane_count = forced_lane_count(link_caps); } static int intel_dp_link_config_rate(struct intel_dp_link_caps *link_caps, const struct intel_dp_link_config_entry *lce) { return intel_dp_common_rate(link_caps, lce->link_rate_idx); } static int intel_dp_link_config_lane_count(const struct intel_dp_link_config_entry *lce) { return 1 << lce->lane_count_exp; } static void to_intel_dp_link_config(struct intel_dp_link_caps *link_caps, int config_idx, struct intel_dp_link_config *config) { const struct intel_dp_link_config_entry *lce = &link_caps->configs[config_idx]; config->rate = intel_dp_link_config_rate(link_caps, lce); config->lane_count = intel_dp_link_config_lane_count(lce); } static int iter_pos_to_idx(struct intel_dp_link_caps *link_caps, struct intel_dp_link_caps_order config_order, int iter_pos) { int config_idx; if (!in_range(iter_pos, 0, link_caps->num_configs)) return -1; switch (config_order.dir) { case INTEL_DP_LINK_CAPS_ORDER_DIR_ASC: break; case INTEL_DP_LINK_CAPS_ORDER_DIR_DESC: iter_pos = link_caps->num_configs - 1 - iter_pos; break; default: MISSING_CASE(config_order.dir); return -1; } switch (config_order.key) { case INTEL_DP_LINK_CAPS_ORDER_KEY_BW: config_idx = iter_pos; break; case INTEL_DP_LINK_CAPS_ORDER_KEY_RATE_LANE: config_idx = link_caps->rate_lane_map[iter_pos]; break; case INTEL_DP_LINK_CAPS_ORDER_KEY_LANE_RATE: config_idx = link_caps->lane_rate_map[iter_pos]; break; default: MISSING_CASE(config_order.key); return -1; } return config_idx; } static bool iter_get_next_config(struct intel_dp_link_caps_iter *iter, struct intel_dp_link_config *config) { while (true) { int config_idx; iter->pos++; config_idx = iter_pos_to_idx(iter->link_caps, iter->order, iter->pos); if (config_idx < 0) { iter->pos = -1; *config = INTEL_DP_LINK_CONFIG_NULL; break; } if (!(BIT(config_idx) & iter->filter.config_mask)) continue; to_intel_dp_link_config(iter->link_caps, config_idx, config); break; } return iter->pos >= 0; } static void iter_start(struct intel_dp_link_caps_iter *iter, struct intel_dp_link_caps *link_caps, struct intel_dp_link_caps_order order, struct intel_dp_link_caps_filter filter) { iter->link_caps = link_caps; iter->pos = -1; iter->order = order; iter->filter = filter; iter->get_next_config = iter_get_next_config; } static struct intel_dp_link_caps_filter calc_allowed_config_filter(struct intel_dp_link_caps *link_caps, struct intel_dp_link_caps_filter enabled_configs, const struct intel_dp_link_config *max_limits, const struct intel_dp_link_config *forced_params) { struct intel_dp_link_caps_filter allowed_configs = INTEL_DP_LINK_CAPS_FILTER_NONE; struct intel_dp_link_caps_order order = bw_desc_config_order(); struct intel_dp_link_caps_iter iter; struct intel_dp_link_config config; iter_start(&iter, link_caps, order, enabled_configs); for_each_dp_link_config(&iter, &config) { if (forced_params->rate && forced_params->rate != config.rate) continue; if (forced_params->lane_count && forced_params->lane_count != config.lane_count) continue; if (config.rate > max_limits->rate) continue; if (config.lane_count > max_limits->lane_count) continue; allowed_configs.config_mask |= BIT(iter_pos_to_idx(link_caps, order, iter.pos)); } intel_dp_link_caps_iter_end(&iter); return allowed_configs; } /* * get_allowed_config_filter - get filter for the currently allowed configs * @link_caps: link capabilities state * * Return: * Filter of link configurations allowed after applying the current * maximum link limits, and further narrowing them by removing any disabled * configuration and limiting to forced link parameters. * * See also: * - intel_dp_link_caps_get_max_limits() * - intel_dp_link_caps_get_forced_params() */ static struct intel_dp_link_caps_filter get_allowed_config_filter(struct intel_dp_link_caps *link_caps) { struct intel_dp_link_config forced_params; intel_dp_link_caps_get_forced_params(link_caps, &forced_params); return calc_allowed_config_filter(link_caps, link_caps->enabled_configs, &link_caps->max_limits, &forced_params); } void intel_dp_link_caps_iter_start(struct intel_dp_link_caps_iter *iter, struct intel_dp_link_caps *link_caps, struct intel_dp_link_caps_order order, struct intel_dp_link_caps_filter filter) { filter.config_mask &= get_allowed_config_filter(link_caps).config_mask; iter_start(iter, link_caps, order, filter); } void intel_dp_link_caps_iter_end(struct intel_dp_link_caps_iter *iter) { memset(iter, 0, sizeof(*iter)); } /** * intel_dp_link_caps_get_max_config - get the maximum config in a given order * @link_caps: link capabilities state * @order_key: ordering key used to rank candidate configurations * @filter: filter for candidate configurations * @max_config: returned maximum link configuration * * Find the last configuration among the currently allowed * configurations filtered by @filter in the iteration order * selected by @order_key, and store it in @max_config. * * See also: * - &enum intel_dp_link_caps_order_key * * Returns: * %true if a maximum config is returned * %false otherwise. */ bool intel_dp_link_caps_get_max_config(struct intel_dp_link_caps *link_caps, enum intel_dp_link_caps_order_key order_key, struct intel_dp_link_caps_filter filter, struct intel_dp_link_config *max_config) { struct intel_dp_link_caps_order order = { .key = order_key, .dir = INTEL_DP_LINK_CAPS_ORDER_DIR_DESC }; struct intel_dp_link_config iter_config; struct intel_dp_link_caps_iter iter; bool found = false; intel_dp_link_caps_iter_start(&iter, link_caps, order, filter); for_each_dp_link_config(&iter, &iter_config) { found = true; break; } intel_dp_link_caps_iter_end(&iter); if (!found) return false; *max_config = iter_config; return true; } /** * intel_dp_link_caps_get_max_bw_config - get maximum BW link configuration * @link_caps: link capabilities state * @max_config: returned maximum link configuration * * Return the maximum BW link configuration among the currently * allowed configurations. */ void intel_dp_link_caps_get_max_bw_config(struct intel_dp_link_caps *link_caps, struct intel_dp_link_config *max_config) { if (!intel_dp_link_caps_get_max_config(link_caps, bw_desc_config_order().key, INTEL_DP_LINK_CAPS_FILTER_ALL, max_config)) *max_config = INTEL_DP_LINK_CONFIG_NULL; } static int find_config_idx(struct intel_dp_link_caps *link_caps, struct intel_dp_link_caps_filter filter, const struct intel_dp_link_config *link_config) { struct intel_dp_link_caps_order order = bw_desc_config_order(); struct intel_dp_link_config iter_config; struct intel_dp_link_caps_iter iter; int pos = -1; intel_dp_link_caps_iter_start(&iter, link_caps, order, filter); for_each_dp_link_config(&iter, &iter_config) { if (iter_config.rate == link_config->rate && iter_config.lane_count == link_config->lane_count) { pos = iter.pos; break; } } intel_dp_link_caps_iter_end(&iter); if (pos < 0) return pos; return iter_pos_to_idx(link_caps, order, pos); } bool intel_dp_link_caps_filter_add(struct intel_dp_link_caps *link_caps, struct intel_dp_link_caps_filter *filter, const struct intel_dp_link_config *config) { int idx; idx = find_config_idx(link_caps, get_allowed_config_filter(link_caps), config); if (idx < 0) return false; filter->config_mask |= BIT(idx); return true; } static bool intel_dp_link_caps_filter_remove(struct intel_dp_link_caps *link_caps, struct intel_dp_link_caps_filter *filter, const struct intel_dp_link_config *config) { int idx; idx = find_config_idx(link_caps, get_allowed_config_filter(link_caps), config); if (idx < 0) return false; filter->config_mask &= ~BIT(idx); return true; } static void set_max_link_limits(struct intel_dp_link_caps *link_caps, const struct intel_dp_link_config *max_link_limits) { link_caps->max_limits = *max_link_limits; } static void reset_max_link_limits(struct intel_dp_link_caps *link_caps) { struct intel_dp_link_config max_link_limits = { .rate = intel_dp_max_common_rate(link_caps), .lane_count = intel_dp_link_caps_max_common_lane_count(link_caps), }; set_max_link_limits(link_caps, &max_link_limits); } static void reset_max_link_limits_reenable_all(struct intel_dp_link_caps *link_caps) { link_caps->enabled_configs = INTEL_DP_LINK_CAPS_FILTER_ALL; reset_max_link_limits(link_caps); } /** * intel_dp_link_caps_disable_config - disable a configuration * @link_caps: link capabilities state * @config: configuration to disable * * Disable the configuration identified by @config. This removes the * configuration from the set of allowed configurations. The disabling * shouldn't leave the remaining configuration set empty. * * The configuration remains disallowed until intel_dp_link_caps() with * reset=%true or changed sink capabilities is called, or * intel_dp_link_caps_reset() is called. Each of these happens after a * new sink is connected or the currently connected sink changes its * capabilities. * * Return: * - %true if @config was valid and the derived state was updated. * - %false if @config was invalid or the remaining configuration set * would remain empty. */ bool intel_dp_link_caps_disable_config(struct intel_dp_link_caps *link_caps, const struct intel_dp_link_config *config) { struct intel_dp_link_caps_filter enabled_configs = link_caps->enabled_configs; struct intel_dp_link_config forced_params; if (!intel_dp_link_caps_filter_remove(link_caps, &enabled_configs, config)) return false; intel_dp_link_caps_get_forced_params(link_caps, &forced_params); if (!calc_allowed_config_filter(link_caps, enabled_configs, &link_caps->max_limits, &forced_params).config_mask) return false; link_caps->enabled_configs = enabled_configs; return true; } /** * intel_dp_link_caps_get_max_limits - get the current maximum link limits * @link_caps: link capabilities state * @max_link_limits: returned maximum link limits * * Return the current maximum rate and lane count limits in * @max_link_limits. * * These limits constrain the set of allowed configurations. * * The limits are set to the maximum common supported values after * intel_dp_link_caps_reset() is called, and can later be modified by * intel_dp_link_caps_set_max_limits(). The max rate and lane count * parameters are independent limits, so the pair does not necessarily * define a valid configuration. * * This function may be called without serializing against updates to * @link_caps. However, without such serialization the returned value may be * an out-of-sync (link rate, lane count) tuple, i.e. the parameters may * belong to different update snapshots in time. */ void intel_dp_link_caps_get_max_limits(struct intel_dp_link_caps *link_caps, struct intel_dp_link_config *max_link_limits) { *max_link_limits = link_caps->max_limits; } static bool max_link_limits_valid(struct intel_dp_link_caps *link_caps, const struct intel_dp_link_config *max_link_limits) { struct intel_dp_link_caps_filter allowed_configs; struct intel_dp_link_config forced_params; if (max_link_limits->lane_count > INTEL_DP_MAX_LANE_COUNT || !is_power_of_2(max_link_limits->lane_count)) return false; /* TODO: Validate max_link_limits->rate against the source supported rates. */ intel_dp_link_caps_get_forced_params(link_caps, &forced_params); allowed_configs = calc_allowed_config_filter(link_caps, link_caps->enabled_configs, max_link_limits, &forced_params); return allowed_configs.config_mask != 0; } /** * intel_dp_link_caps_set_max_limits - set the current maximum link limits * @link_caps: link capabilities state * @max_link_limits: new maximum link limits * * Set the current maximum rate and lane count limits to @max_link_limits, * constraining the set of allowed configurations. * * The new limits must leave at least one configuration allowed: the limits * must not be below the currently active forced parameters or below all the * configurations that remain after disabled configurations are excluded. * * Unlike intel_dp_link_caps_get_max_limits(), the caller must serialize * this call against concurrent queries and updates to @link_caps, in line * with the rest of the API. * * Return: * - %true if the @link_caps cached max limits value got updated with * @max_link_limits. * - %false if @max_link_limits is invalid. */ bool intel_dp_link_caps_set_max_limits(struct intel_dp_link_caps *link_caps, const struct intel_dp_link_config *max_link_limits) { if (!max_link_limits_valid(link_caps, max_link_limits)) return false; set_max_link_limits(link_caps, max_link_limits); return true; } /** * intel_dp_link_caps_reset_max_limits - reset the current maximum link limits * @link_caps: link capabilities state * * Reset the current maximum link limits to the maximum supported common link * rate and lane count. */ void intel_dp_link_caps_reset_max_limits(struct intel_dp_link_caps *link_caps) { reset_max_link_limits(link_caps); } static int intel_dp_link_config_bw(struct intel_dp_link_caps *link_caps, const struct intel_dp_link_config_entry *lce) { return drm_dp_max_dprx_data_rate(intel_dp_link_config_rate(link_caps, lce), intel_dp_link_config_lane_count(lce)); } 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 */ struct intel_dp_link_caps *link_caps = intel_dp->link.caps; const struct intel_dp_link_config_entry *lce_a = a; const struct intel_dp_link_config_entry *lce_b = b; int bw_a = intel_dp_link_config_bw(link_caps, lce_a); int bw_b = intel_dp_link_config_bw(link_caps, lce_b); if (bw_a != bw_b) return bw_a - bw_b; return intel_dp_link_config_rate(link_caps, lce_a) - intel_dp_link_config_rate(link_caps, lce_b); } static int link_config_cmp_by_rate_lane(const void *a, const void *b, const void *p) { const struct intel_dp_link_caps *link_caps = p; u8 *lce_a_idx = (u8 *)a; u8 *lce_b_idx = (u8 *)b; const struct intel_dp_link_config_entry *lce_a = &link_caps->configs[*lce_a_idx]; const struct intel_dp_link_config_entry *lce_b = &link_caps->configs[*lce_b_idx]; if (lce_a->link_rate_idx != lce_b->link_rate_idx) return lce_a->link_rate_idx - lce_b->link_rate_idx; return lce_a->lane_count_exp - lce_b->lane_count_exp; } static int link_config_cmp_by_lane_rate(const void *a, const void *b, const void *p) { const struct intel_dp_link_caps *link_caps = p; u8 *lce_a_idx = (u8 *)a; u8 *lce_b_idx = (u8 *)b; const struct intel_dp_link_config_entry *lce_a = &link_caps->configs[*lce_a_idx]; const struct intel_dp_link_config_entry *lce_b = &link_caps->configs[*lce_b_idx]; if (lce_a->lane_count_exp != lce_b->lane_count_exp) return lce_a->lane_count_exp - lce_b->lane_count_exp; return lce_a->link_rate_idx - lce_b->link_rate_idx; } /** * intel_dp_link_caps_update - rebuild the supported link configuration state * @link_caps: link capabilities state * @rates: supported common link rates * @num_rates: number of entries in @rates * @max_lane_count: supported maximum lane count * @reset: reset limits and disabled configs * * Rebuild the supported link configuration state from @rates and * @max_lane_count. * * If @reset is %true, reset the maximum link limits to the maximum * supported rate and lane count, and re-enable all configurations. * * This function is called regularly, at least after a sink is connected, * but it may also be called later whenever the sink capabilities may have * changed, for example in response to HPD IRQ / RX_CAP_CHANGED signaling. * * In the Intel driver this function is currently called whenever the * connector detect handler runs, after reading the sink capabilities. This * may change if those capabilities are cached until the sink is * disconnected, or until RX_CAP_CHANGED is signaled. In any case, this * function should be called whenever the sink capabilities were read out * and may have changed. * * Returns: * - %true if the link capabilities have changed, %false otherwise. */ bool intel_dp_link_caps_update(struct intel_dp_link_caps *link_caps, const int *rates, int num_rates, int max_lane_count, bool reset) { struct intel_dp *intel_dp = link_caps->dp; struct intel_display *display = to_intel_display(intel_dp); struct intel_dp_link_config_entry *lce; bool link_params_changed = reset; int num_common_lane_configs; int i; int j; if (drm_WARN_ON(display->drm, !is_power_of_2(max_lane_count))) return false; if (drm_WARN_ON(display->drm, num_rates > ARRAY_SIZE(link_caps->rates))) return false; num_common_lane_configs = ilog2(max_lane_count) + 1; if (drm_WARN_ON(display->drm, num_rates * num_common_lane_configs > ARRAY_SIZE(link_caps->configs))) return false; /* TODO: Add a struct containing both rates and number of rates. */ static_assert(__same_type(rates[0], link_caps->rates[0])); if (num_rates != link_caps->num_rates || memcmp(rates, link_caps->rates, num_rates * sizeof(rates[0]))) link_params_changed = true; if (max_lane_count != link_caps->max_lane_count) link_params_changed = true; memcpy(link_caps->rates, rates, num_rates * sizeof(rates[0])); link_caps->num_rates = num_rates; link_caps->max_lane_count = max_lane_count; link_caps->num_configs = num_rates * num_common_lane_configs; lce = &link_caps->configs[0]; for (i = 0; i < link_caps->num_rates; i++) { for (j = 0; j < num_common_lane_configs; j++) { lce->lane_count_exp = j; lce->link_rate_idx = i; lce++; } } sort_r(link_caps->configs, link_caps->num_configs, sizeof(link_caps->configs[0]), link_config_cmp_by_bw, NULL, intel_dp); for (i = 0; i < link_caps->num_configs; i++) { link_caps->rate_lane_map[i] = i; link_caps->lane_rate_map[i] = i; } sort_r(link_caps->rate_lane_map, link_caps->num_configs, sizeof(link_caps->rate_lane_map[0]), link_config_cmp_by_rate_lane, NULL, link_caps); sort_r(link_caps->lane_rate_map, link_caps->num_configs, sizeof(link_caps->lane_rate_map[0]), link_config_cmp_by_lane_rate, NULL, link_caps); if (link_params_changed) reset_max_link_limits_reenable_all(link_caps); return link_params_changed; } /** * intel_dp_link_caps_reset - reset link capability restrictions * @link_caps: link capabilities state * * Reset all current restrictions except for the user requested forced * parameters, thus updating the set of allowed configurations and the * derived maximum link information accordingly. * * This function is regularly called after a sink is connected, either * for the first time to the connector or after a previous sink was * disconnected from it, and intel_dp_link_caps_update() was called. */ void intel_dp_link_caps_reset(struct intel_dp_link_caps *link_caps) { reset_max_link_limits_reenable_all(link_caps); } static int i915_dp_force_link_rate_show(struct seq_file *m, void *data) { struct intel_connector *connector = to_intel_connector(m->private); struct intel_display *display = to_intel_display(connector); struct intel_dp *intel_dp = intel_attached_dp(connector); struct intel_dp_link_caps *link_caps = intel_dp->link.caps; int current_rate = -1; int force_rate; int err; int i; err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); if (err) return err; intel_dp_flush_connector_commits(connector); if (intel_dp->link.active) current_rate = intel_dp->link_rate; force_rate = link_caps->forced_params.rate; drm_modeset_unlock(&display->drm->mode_config.connection_mutex); seq_printf(m, "%sauto%s", force_rate == 0 ? "[" : "", force_rate == 0 ? "]" : ""); for (i = 0; i < intel_dp->num_source_rates; i++) seq_printf(m, " %s%d%s%s", intel_dp->source_rates[i] == force_rate ? "[" : "", intel_dp->source_rates[i], intel_dp->source_rates[i] == current_rate ? "*" : "", intel_dp->source_rates[i] == force_rate ? "]" : ""); seq_putc(m, '\n'); return 0; } static int parse_link_rate(struct intel_dp_link_caps *link_caps, const char __user *ubuf, size_t len) { struct intel_dp *intel_dp = link_caps->dp; char *kbuf; const char *p; int rate; int ret = 0; kbuf = memdup_user_nul(ubuf, len); if (IS_ERR(kbuf)) return PTR_ERR(kbuf); p = strim(kbuf); if (!strcmp(p, "auto")) { rate = 0; } else { ret = kstrtoint(p, 0, &rate); if (ret < 0) goto out_free; if (intel_dp_rate_index(intel_dp->source_rates, intel_dp->num_source_rates, rate) < 0) ret = -EINVAL; } out_free: kfree(kbuf); return ret < 0 ? ret : rate; } static ssize_t i915_dp_force_link_rate_write(struct file *file, const char __user *ubuf, size_t len, loff_t *offp) { struct seq_file *m = file->private_data; struct intel_connector *connector = to_intel_connector(m->private); struct intel_display *display = to_intel_display(connector); struct intel_dp *intel_dp = intel_attached_dp(connector); struct intel_dp_link_caps *link_caps = intel_dp->link.caps; int rate; int err; rate = parse_link_rate(link_caps, ubuf, len); if (rate < 0) return rate; err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); if (err) return err; intel_dp_flush_connector_commits(connector); intel_dp_reset_link_params(intel_dp); link_caps->forced_params.rate = rate; drm_modeset_unlock(&display->drm->mode_config.connection_mutex); *offp += len; return len; } DEFINE_SHOW_STORE_ATTRIBUTE(i915_dp_force_link_rate); static int i915_dp_force_lane_count_show(struct seq_file *m, void *data) { struct intel_connector *connector = to_intel_connector(m->private); struct intel_display *display = to_intel_display(connector); struct intel_dp *intel_dp = intel_attached_dp(connector); struct intel_dp_link_caps *link_caps = intel_dp->link.caps; int current_lane_count = -1; int force_lane_count; int err; int i; err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); if (err) return err; intel_dp_flush_connector_commits(connector); if (intel_dp->link.active) current_lane_count = intel_dp->lane_count; force_lane_count = link_caps->forced_params.lane_count; drm_modeset_unlock(&display->drm->mode_config.connection_mutex); seq_printf(m, "%sauto%s", force_lane_count == 0 ? "[" : "", force_lane_count == 0 ? "]" : ""); for (i = 1; i <= 4; i <<= 1) seq_printf(m, " %s%d%s%s", i == force_lane_count ? "[" : "", i, i == current_lane_count ? "*" : "", i == force_lane_count ? "]" : ""); seq_putc(m, '\n'); return 0; } static int parse_lane_count(const char __user *ubuf, size_t len) { char *kbuf; const char *p; int lane_count; int ret = 0; kbuf = memdup_user_nul(ubuf, len); if (IS_ERR(kbuf)) return PTR_ERR(kbuf); p = strim(kbuf); if (!strcmp(p, "auto")) { lane_count = 0; } else { ret = kstrtoint(p, 0, &lane_count); if (ret < 0) goto out_free; switch (lane_count) { case 1: case 2: case 4: break; default: ret = -EINVAL; } } out_free: kfree(kbuf); return ret < 0 ? ret : lane_count; } static ssize_t i915_dp_force_lane_count_write(struct file *file, const char __user *ubuf, size_t len, loff_t *offp) { struct seq_file *m = file->private_data; struct intel_connector *connector = to_intel_connector(m->private); struct intel_display *display = to_intel_display(connector); struct intel_dp *intel_dp = intel_attached_dp(connector); struct intel_dp_link_caps *link_caps = intel_dp->link.caps; int lane_count; int err; lane_count = parse_lane_count(ubuf, len); if (lane_count < 0) return lane_count; err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); if (err) return err; intel_dp_flush_connector_commits(connector); intel_dp_reset_link_params(intel_dp); link_caps->forced_params.lane_count = lane_count; drm_modeset_unlock(&display->drm->mode_config.connection_mutex); *offp += len; return len; } DEFINE_SHOW_STORE_ATTRIBUTE(i915_dp_force_lane_count); static int i915_dp_max_link_rate_show(void *data, u64 *val) { struct intel_connector *connector = to_intel_connector(data); struct intel_display *display = to_intel_display(connector); struct intel_dp *intel_dp = intel_attached_dp(connector); struct intel_dp_link_config max_link_limits; int err; err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); if (err) return err; intel_dp_flush_connector_commits(connector); intel_dp_link_caps_get_max_limits(intel_dp->link.caps, &max_link_limits); *val = max_link_limits.rate; drm_modeset_unlock(&display->drm->mode_config.connection_mutex); return 0; } DEFINE_DEBUGFS_ATTRIBUTE(i915_dp_max_link_rate_fops, i915_dp_max_link_rate_show, NULL, "%llu\n"); static int i915_dp_max_lane_count_show(void *data, u64 *val) { struct intel_connector *connector = to_intel_connector(data); struct intel_display *display = to_intel_display(connector); struct intel_dp *intel_dp = intel_attached_dp(connector); struct intel_dp_link_config max_link_limits; int err; err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); if (err) return err; intel_dp_flush_connector_commits(connector); intel_dp_link_caps_get_max_limits(intel_dp->link.caps, &max_link_limits); *val = max_link_limits.lane_count; drm_modeset_unlock(&display->drm->mode_config.connection_mutex); return 0; } DEFINE_DEBUGFS_ATTRIBUTE(i915_dp_max_lane_count_fops, i915_dp_max_lane_count_show, NULL, "%llu\n"); static int intel_dp_allowed_link_configs_show(struct seq_file *m, void *data) { struct intel_connector *connector = to_intel_connector(m->private); struct intel_display *display = to_intel_display(connector); struct intel_dp *intel_dp = intel_attached_dp(connector); struct intel_dp_link_caps *link_caps = intel_dp->link.caps; struct intel_dp_link_config link_config; struct intel_dp_link_caps_iter iter; int err; int i; err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); if (err) return err; intel_dp_flush_connector_commits(connector); i = 0; intel_dp_link_caps_iter_start(&iter, link_caps, intel_dp_link_caps_connector_compute_order(connector), INTEL_DP_LINK_CAPS_FILTER_ALL); for_each_dp_link_config(&iter, &link_config) { seq_printf(m, "%s%dx%d", i ? " " : "", link_config.lane_count, link_config.rate); i++; } intel_dp_link_caps_iter_end(&iter); drm_modeset_unlock(&display->drm->mode_config.connection_mutex); seq_putc(m, '\n'); return 0; } DEFINE_SHOW_ATTRIBUTE(intel_dp_allowed_link_configs); /** * intel_dp_link_caps_debugfs_add - add link caps debugfs files for a connector * @connector: connector to add the debugfs files for * * Add the link-capability debugfs files for a DP @connector. */ void intel_dp_link_caps_debugfs_add(struct intel_connector *connector) { struct dentry *root = connector->base.debugfs_entry; if (connector->base.connector_type != DRM_MODE_CONNECTOR_DisplayPort && connector->base.connector_type != DRM_MODE_CONNECTOR_eDP) return; debugfs_create_file("i915_dp_force_link_rate", 0644, root, connector, &i915_dp_force_link_rate_fops); debugfs_create_file("i915_dp_force_lane_count", 0644, root, connector, &i915_dp_force_lane_count_fops); debugfs_create_file("i915_dp_max_link_rate", 0444, root, connector, &i915_dp_max_link_rate_fops); debugfs_create_file("i915_dp_max_lane_count", 0444, root, connector, &i915_dp_max_lane_count_fops); debugfs_create_file("intel_dp_allowed_link_configs", 0444, root, connector, &intel_dp_allowed_link_configs_fops); } struct intel_dp_link_caps *intel_dp_link_caps_init(struct intel_dp *intel_dp) { struct intel_dp_link_caps *link_caps; link_caps = kzalloc_obj(*link_caps); if (!link_caps) return NULL; link_caps->dp = intel_dp; link_caps->enabled_configs = INTEL_DP_LINK_CAPS_FILTER_ALL; return link_caps; } void intel_dp_link_caps_cleanup(struct intel_dp_link_caps *link_caps) { kfree(link_caps); } #if IS_ENABLED(CONFIG_KUNIT) #define __INIT_MEMBER(__name, __fn) \ .__name = __fn, #define INTEL_DP_LINK_CAPS_TEST_OPS_INIT \ INTEL_DP_LINK_CAPS_TEST_OPS_MEMBERS(__INIT_MEMBER) #ifdef I915 const struct intel_dp_link_caps_test_ops i915_display_dp_link_caps_test_ops = { INTEL_DP_LINK_CAPS_TEST_OPS_INIT }; EXPORT_SYMBOL(i915_display_dp_link_caps_test_ops); #else const struct intel_dp_link_caps_test_ops intel_display_dp_link_caps_test_ops = { INTEL_DP_LINK_CAPS_TEST_OPS_INIT }; EXPORT_SYMBOL(intel_display_dp_link_caps_test_ops); #endif /* I915 */ #undef INTEL_DP_LINK_CAPS_TEST_OPS_INIT #undef __INIT_MEMBER #endif /* CONFIG_KUNIT */