Add helpers to iterate over the allowed and optionally filtered set of link configurations in a given order.
Taking into account disabled configurations will be added later when adding support for disabling configurations. Use the BW order in descending direction by default. v2: - Keep the iteration state in an iteration object. (Jani) - Use a filter object instead of exposing configuration indices. (Jani) - Move changes adding helpers required to setup the iteration object to this patch. Signed-off-by: Imre Deak <[email protected]> --- .../gpu/drm/i915/display/intel_dp_link_caps.c | 213 ++++++++++++++++++ .../gpu/drm/i915/display/intel_dp_link_caps.h | 106 +++++++++ 2 files changed, 319 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 fe5a11efbe67b..d46b9d505a847 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c @@ -16,6 +16,7 @@ #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" @@ -146,6 +147,18 @@ struct intel_dp_link_caps { */ 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; +} /* Get length of common rates array potentially limited by max_rate. */ int intel_dp_common_len_rate_limit(struct intel_dp_link_caps *link_caps, @@ -252,6 +265,206 @@ to_intel_dp_link_config(struct intel_dp_link_caps *link_caps, 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); + + /* TODO: Get filter for enabled configs. */ + return calc_allowed_config_filter(link_caps, INTEL_DP_LINK_CAPS_FILTER_ALL, + &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)); +} + +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 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 af9028e7cb987..5ea87f112c13a 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h @@ -4,6 +4,7 @@ #ifndef __INTEL_DP_LINK_CAPS_H__ #define __INTEL_DP_LINK_CAPS_H__ +#include <linux/bitops.h> #include <linux/types.h> struct intel_connector; @@ -11,6 +12,107 @@ struct intel_dp; struct intel_dp_link_caps; struct intel_dp_link_config; +/** + * enum intel_dp_link_caps_order_key - key used to order configurations + * @INTEL_DP_LINK_CAPS_ORDER_KEY_BW: + * Order configurations by bandwidth, then by link rate. + * @INTEL_DP_LINK_CAPS_ORDER_KEY_RATE_LANE: + * Order configurations by link rate, then by lane count. + * @INTEL_DP_LINK_CAPS_ORDER_KEY_LANE_RATE: + * Order configurations by lane count, then by link rate. + * @INTEL_DP_LINK_CAPS_ORDER_KEY_NUM: + * Number of ordering keys. + * + * Selects how a caller wants the configuration table to be ordered, + * together with an &enum intel_dp_link_caps_order_direction, for + * iteration queries. + * + * See also: + * - &struct intel_dp_link_caps_order + */ +enum intel_dp_link_caps_order_key { + INTEL_DP_LINK_CAPS_ORDER_KEY_BW, + INTEL_DP_LINK_CAPS_ORDER_KEY_RATE_LANE, + INTEL_DP_LINK_CAPS_ORDER_KEY_LANE_RATE, + + INTEL_DP_LINK_CAPS_ORDER_KEY_NUM +}; + +/** + * enum intel_dp_link_caps_order_direction - iteration direction + * @INTEL_DP_LINK_CAPS_ORDER_DIR_ASC: + * Iterate in ascending order according to the selected ordering key. + * @INTEL_DP_LINK_CAPS_ORDER_DIR_DESC: + * Iterate in descending order according to the selected ordering key. + * @INTEL_DP_LINK_CAPS_ORDER_DIR_NUM: + * Number of ordering directions. + * + * Selects the direction associated with an + * &enum intel_dp_link_caps_order_key for iteration queries. + * + * See also: + * - &struct intel_dp_link_caps_order + */ +enum intel_dp_link_caps_order_direction { + INTEL_DP_LINK_CAPS_ORDER_DIR_ASC, + INTEL_DP_LINK_CAPS_ORDER_DIR_DESC, + + INTEL_DP_LINK_CAPS_ORDER_DIR_NUM +}; + +/** + * struct intel_dp_link_caps_order - configuration ordering + * @key: + * Key used to order configurations. + * @dir: + * Direction of the selected ordering. + * + * Describes an iteration order for link configurations. + * + * See also: + * - for_each_dp_link_config() + */ +struct intel_dp_link_caps_order { + enum intel_dp_link_caps_order_key key; + enum intel_dp_link_caps_order_direction dir; +}; + +struct intel_dp_link_caps_filter { + u32 config_mask; +}; + +#define INTEL_DP_LINK_CAPS_FILTER_NONE \ + ((struct intel_dp_link_caps_filter){ .config_mask = 0 }) +#define INTEL_DP_LINK_CAPS_FILTER_ALL \ + ((struct intel_dp_link_caps_filter){ .config_mask = (u32)-1 }) + +struct intel_dp_link_caps_iter { + struct intel_dp_link_caps *link_caps; + int pos; + struct intel_dp_link_caps_order order; + struct intel_dp_link_caps_filter filter; + + bool (*get_next_config)(struct intel_dp_link_caps_iter *iter, + struct intel_dp_link_config *config); +}; + +/** + * for_each_dp_link_config - iterate allowed link configurations + * @__iter: + * &struct intel_dp_link_caps_iter being iterated + * @__config: + * pointer to &struct intel_dp_link_config filled for each match + */ +#define for_each_dp_link_config(__iter, __config) \ + while ((__iter)->get_next_config((__iter), (__config))) + +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); + +void intel_dp_link_caps_iter_end(struct intel_dp_link_caps_iter *iter); + int intel_dp_common_len_rate_limit(struct intel_dp_link_caps *link_caps, int max_rate); int intel_dp_common_rate(struct intel_dp_link_caps *link_caps, int index); @@ -29,6 +131,10 @@ int intel_dp_link_config_index(struct intel_dp_link_caps *link_caps, void intel_dp_link_config_get(struct intel_dp_link_caps *link_caps, int idx, int *link_rate, int *lane_count); +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); + void intel_dp_link_caps_get_max_limits(struct intel_dp_link_caps *link_caps, struct intel_dp_link_config *max_link_limits); bool intel_dp_link_caps_set_max_limits(struct intel_dp_link_caps *link_caps, -- 2.49.1
