[PATCH 107/108] drm/i915/kunit: DP link: add update config tests

Imre Deak
2026-04-28T12:56:12Z

Add KUnit tests for link_caps updates, covering reset and merge updates
when supported rates and lane counts shrink or expand.

The tests also cover updates with disabled configurations, including
random shrink and expand sequences, to verify that disabled state,
allowed configurations, ordering, and max limits stay consistent across
updates.

Signed-off-by: Imre Deak <imre.deak@intel.com>
---
.../i915/display/tests/intel_dp_link_test.c | 763 ++++++++++++++++++
1 file changed, 763 insertions(+)

diff --git a/drivers/gpu/drm/i915/display/tests/intel_dp_link_test.c b/drivers/gpu/drm/i915/display/tests/intel_dp_link_test.c
index e6feb68cb912f..10c4679f31efd 100644
--- a/drivers/gpu/drm/i915/display/tests/intel_dp_link_test.c
+++ b/drivers/gpu/drm/i915/display/tests/intel_dp_link_test.c
@@ -82,12 +82,29 @@ struct link_config_set {
int size;
};

+struct test_config_table {
+ struct kunit *test;
+
+ struct link_rate_set rates;
+ int max_lane_count;
+ struct link_config_set disabled_configs;
+};
+
static const int standard_dp_link_rates[] = {
162000, 270000, 540000, 810000, 1000000, 1350000, 2000000
};

#define LINK_TEST_NUM_STANDARD_RATES (ARRAY_SIZE(standard_dp_link_rates))

+#define INIT_STANDARD_TABLE(__test, __num_rates, __max_lane_count) { \
+ .test = (__test), \
+ .rates = { \
+ .entries = standard_dp_link_rates, \
+ .size = (__num_rates), \
+ }, \
+ .max_lane_count = (__max_lane_count), \
+}
+
static const struct link_config_set standard_dp_link_configs[] = {
[TEST_CONFIG_ORDER_KEY_BW] = { /* MBps PBN */
.entries = {
@@ -180,12 +197,56 @@ static const struct link_config_set standard_dp_link_configs[] = {
},
};

+static int lookup_rate(const struct link_rate_set *rate_set, int rate)
+{
+ int i;
+
+ for (i = 0; i < rate_set->size; i++)
+ if (rate_set->entries[i] == rate)
+ return i;
+
+ return -1;
+}
+
+static bool has_rate(const struct link_rate_set *rate_set, int rate)
+{
+ return lookup_rate(rate_set, rate) >= 0;
+}
+
static bool link_configs_match(const struct intel_dp_link_config *a,
const struct intel_dp_link_config *b)
{
return a->rate == b->rate && a->lane_count == b->lane_count;
}

+static int lookup_config(const struct link_config_set *config_set,
+ const struct intel_dp_link_config *config)
+{
+ int i;
+
+ for (i = 0; i < config_set->size; i++)
+ if (link_configs_match(&config_set->entries[i], config))
+ return i;
+
+ return -1;
+}
+
+static bool has_config(const struct link_config_set *config_set,
+ const struct intel_dp_link_config *config)
+{
+ return lookup_config(config_set, config) >= 0;
+}
+
+static void add_config(struct kunit *test,
+ struct link_config_set *config_set,
+ const struct intel_dp_link_config *config)
+{
+ KUNIT_ASSERT_LT(test, config_set->size, ARRAY_SIZE(config_set->entries));
+
+ config_set->entries[config_set->size] = *config;
+ config_set->size++;
+}
+
static u32 get_all_config_mask(void)
{
return GENMASK_U32(LINK_TEST_MAX_CONFIGS - 1, 0);
@@ -274,9 +335,711 @@ static void intel_dp_link_caps_test_baseline(struct kunit *test)
baseline_test_for_order(test, link_caps, config_orders[i]);
}

+static int get_num_configs(int num_rates, int max_lane_count)
+{
+ return num_rates * LINK_TEST_NUM_LANE_CONFIGS(max_lane_count);
+}
+
+static int rand_in_range(struct test_ctx *ctx, int min, int max)
+{
+ return min + (prandom_u32_state(&ctx->rnd) % (max - min + 1));
+}
+
+/*
+ * TEST: Update reset
+ * ------------------
+ * Verify that resetting link_caps with the DP standard rates/lane
+ * counts updates the configuration table accordingly for all
+ * combinations.
+ */
+static u32 get_link_caps_enabled_config_mask(struct kunit *test,
+ struct intel_dp_link_caps *link_caps)
+{
+ struct test_ctx *ctx = test->priv;
+ const struct intel_dp_link_caps_test_ops *ops =
+ ctx->link_caps_ops;
+ struct intel_dp_link_config old_limits;
+ u32 mask;
+
+ /*
+ * The link_caps allowed config mask is limited by both the disabled
+ * state of the configs and the current max limits parameters. To get
+ * the enabled mask unaffected by the max limit parameters, get the
+ * allowed mask by resetting the max limits temporarily.
+ */
+ ops->get_max_limits(link_caps, &old_limits);
+ ops->reset_max_limits(link_caps);
+
+ mask = ops->get_allowed_config_mask(link_caps);
+
+ ops->set_max_limits(link_caps, &old_limits);
+
+ return mask;
+}
+
+static void verify_bw_asc_config_order(struct kunit *test,
+ const struct intel_dp_link_config *last_config,
+ const struct intel_dp_link_config *config)
+{
+ int config_bw = drm_dp_max_dprx_data_rate(config->rate,
+ config->lane_count);
+ int last_config_bw = drm_dp_max_dprx_data_rate(last_config->rate,
+ last_config->lane_count);
+
+ KUNIT_EXPECT_GE(test, config_bw, last_config_bw);
+ if (config_bw == last_config_bw)
+ KUNIT_EXPECT_GT(test, config->rate, last_config->rate);
+}
+
+static void verify_bw_desc_config_order(struct kunit *test,
+ const struct intel_dp_link_config *last_config,
+ const struct intel_dp_link_config *config)
+{
+ int config_bw = drm_dp_max_dprx_data_rate(config->rate,
+ config->lane_count);
+ int last_config_bw = drm_dp_max_dprx_data_rate(last_config->rate,
+ last_config->lane_count);
+
+ KUNIT_EXPECT_LE(test, config_bw, last_config_bw);
+ if (config_bw == last_config_bw)
+ KUNIT_EXPECT_LT(test, config->rate, last_config->rate);
+}
+
+static void verify_rate_lane_asc_config_order(struct kunit *test,
+ const struct intel_dp_link_config *last_config,
+ const struct intel_dp_link_config *config)
+{
+ KUNIT_EXPECT_GE(test, config->rate, last_config->rate);
+ if (config->rate == last_config->rate)
+ KUNIT_EXPECT_GT(test, config->lane_count, last_config->lane_count);
+}
+
+static void verify_rate_lane_desc_config_order(struct kunit *test,
+ const struct intel_dp_link_config *last_config,
+ const struct intel_dp_link_config *config)
+{
+ KUNIT_EXPECT_LE(test, config->rate, last_config->rate);
+ if (config->rate == last_config->rate)
+ KUNIT_EXPECT_LT(test, config->lane_count, last_config->lane_count);
+}
+
+static void verify_config_order(struct kunit *test,
+ struct intel_dp_link_caps_config_order config_order,
+ const struct intel_dp_link_config *last_config,
+ const struct intel_dp_link_config *config)
+{
+ switch (config_order.key) {
+ case INTEL_DP_LINK_CAPS_CONFIG_ORDER_KEY_BW:
+ if (config_order.dir == INTEL_DP_LINK_CAPS_CONFIG_ORDER_DIR_ASC)
+ verify_bw_asc_config_order(test, last_config, config);
+ else
+ verify_bw_desc_config_order(test, last_config, config);
+ break;
+ case INTEL_DP_LINK_CAPS_CONFIG_ORDER_KEY_RATE_LANE:
+ if (config_order.dir == INTEL_DP_LINK_CAPS_CONFIG_ORDER_DIR_ASC)
+ verify_rate_lane_asc_config_order(test, last_config, config);
+ else
+ verify_rate_lane_desc_config_order(test, last_config, config);
+ break;
+ default:
+ KUNIT_FAIL_AND_ABORT(test, "Missing order key: %d", config_order.key);
+ }
+}
+
+static void
+verify_link_caps_for_order(const struct test_config_table *expected_table,
+ struct intel_dp_link_caps *link_caps,
+ struct intel_dp_link_caps_config_order config_order)
+{
+ struct kunit *test = expected_table->test;
+ struct test_ctx *ctx = test->priv;
+ const struct intel_dp_link_caps_test_ops *ops =
+ ctx->link_caps_ops;
+ struct intel_dp_link_config expected_max_limits = {};
+ struct intel_dp_link_config actual_max_limits;
+ struct intel_dp_link_config last_config = {};
+ struct intel_dp_link_config iter_config;
+ int num_actual_configs = 0;
+ int iter_config_idx;
+
+ test_for_each_dp_link_config_idx(ctx, config_order, get_all_config_mask(),
+ &iter_config, &iter_config_idx) {
+ bool config_disabled;
+
+ num_actual_configs++;
+
+ /*
+ * Verify the config's rate/lane-count values and its ordering relative
+ * to the previous config.
+ */
+ if (last_config.rate)
+ verify_config_order(test, config_order, &last_config, &iter_config);
+ last_config = iter_config;
+
+ KUNIT_EXPECT_TRUE(test, has_rate(&expected_table->rates,
+ iter_config.rate));
+ KUNIT_EXPECT_LE(test, iter_config.lane_count,
+ expected_table->max_lane_count);
+ KUNIT_EXPECT_TRUE(test, is_power_of_2(iter_config.lane_count));
+
+ /* Verify the config's disabled state */
+ config_disabled = !(get_link_caps_enabled_config_mask(test, link_caps) &
+ BIT(iter_config_idx));
+ KUNIT_EXPECT_EQ(test, config_disabled,
+ has_config(&expected_table->disabled_configs,
+ &iter_config));
+
+ /*
+ * Update the max limits for allowed configs, verified at the
+ * end for the whole config table.
+ */
+ if (!(ops->get_allowed_config_mask(link_caps) &
+ BIT(iter_config_idx)))
+ continue;
+
+ expected_max_limits.rate = max(expected_max_limits.rate,
+ iter_config.rate);
+ expected_max_limits.lane_count = max(expected_max_limits.lane_count,
+ iter_config.lane_count);
+ }
+
+ KUNIT_EXPECT_EQ(test, num_actual_configs,
+ get_num_configs(expected_table->rates.size,
+ expected_table->max_lane_count));
+
+ ops->get_max_limits(link_caps, &actual_max_limits);
+ KUNIT_EXPECT_TRUE(test, link_configs_match(&expected_max_limits,
+ &actual_max_limits));
+}
+
+static void verify_link_caps(const struct test_config_table *expected_table,
+ struct intel_dp_link_caps *link_caps)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(config_orders); i++)
+ verify_link_caps_for_order(expected_table, link_caps, config_orders[i]);
+}
+
+/*
+ * Filter out the disabled configs, which got removed after a configuration
+ * shrink step reduced the maximum rate and/or maximum lane count of the
+ * configurations.
+ *
+ * Also make sure that at least one configuration (the minimal config) remains
+ * enabled after a shrink step.
+ */
+static void update_disabled_configs(struct test_config_table *expected_table)
+{
+ struct kunit *test = expected_table->test;
+ struct link_config_set new_disabled_configs = {};
+ const struct intel_dp_link_config min_config = {
+ .rate = expected_table->rates.entries[0],
+ .lane_count = 1,
+ };
+ bool min_config_disabled = false;
+ int num_configs;
+ int i;
+
+ num_configs = get_num_configs(expected_table->rates.size,
+ expected_table->max_lane_count);
+
+ for (i = 0; i < expected_table->disabled_configs.size; i++) {
+ const struct intel_dp_link_config *config =
+ &expected_table->disabled_configs.entries[i];
+
+ if (config->rate >
+ expected_table->rates.entries[expected_table->rates.size - 1] ||
+ config->lane_count >
+ expected_table->max_lane_count)
+ continue;
+
+ /*
+ * Mark the minimum config disabled only at the end, and only if that
+ * would not leave all configs disabled.
+ */
+ if (link_configs_match(config, &min_config)) {
+ min_config_disabled = true;
+
+ continue;
+ }
+
+ add_config(test, &new_disabled_configs, config);
+ }
+
+ expected_table->disabled_configs = new_disabled_configs;
+
+ if (min_config_disabled &&
+ expected_table->disabled_configs.size + 1 < num_configs)
+ add_config(test, &expected_table->disabled_configs, &min_config);
+}
+
+static void update_link_caps_and_verify(struct test_config_table *expected_table,
+ struct intel_dp_link_caps *link_caps,
+ enum intel_dp_link_caps_update_mode update_mode)
+{
+ struct kunit *test = expected_table->test;
+ struct test_ctx *ctx = test->priv;
+ const struct intel_dp_link_caps_test_ops *ops =
+ ctx->link_caps_ops;
+
+ update_disabled_configs(expected_table);
+
+ ops->update(link_caps,
+ expected_table->rates.entries, expected_table->rates.size,
+ expected_table->max_lane_count,
+ update_mode);
+
+ /*
+ * In case of adding new configurations vs. replacing the current
+ * ones with new ones, the max limits tracked by link_caps don't
+ * get updated automatically, do that here as expected by all the
+ * test cases.
+ */
+ if (update_mode == INTEL_DP_LINK_CAPS_UPDATE_MERGE)
+ ops->reset_max_limits(link_caps);
+
+ verify_link_caps(expected_table, link_caps);
+}
+
+static void intel_dp_link_caps_test_update_reset(struct kunit *test)
+{
+ struct test_ctx *ctx = test->priv;
+ struct intel_dp_link_caps *link_caps = ctx->dev.dp.link.caps;
+ int max_lane_count;
+ int num_rates;
+
+ for (max_lane_count = 1;
+ max_lane_count <= LINK_TEST_MAX_LANE_COUNT;
+ max_lane_count <<= 1) {
+ for (num_rates = 1;
+ num_rates <= LINK_TEST_NUM_STANDARD_RATES;
+ num_rates++) {
+ struct test_config_table expected_table =
+ INIT_STANDARD_TABLE(test, num_rates,
+ max_lane_count);
+
+ update_link_caps_and_verify(&expected_table, link_caps,
+ INTEL_DP_LINK_CAPS_UPDATE_RESET);
+ }
+ }
+}
+
+/*
+ * TEST: Update shrink and expand
+ * ------------------------------
+ * Verify that removing or adding supported rates/lane counts updates
+ * the configuration table accordingly.
+ */
+static void disable_configs_and_verify(struct kunit *test,
+ struct intel_dp_link_caps *link_caps,
+ struct test_config_table *expected_table,
+ const struct link_config_set *config_set)
+{
+ struct test_ctx *ctx = test->priv;
+ const struct intel_dp_link_caps_test_ops *ops =
+ ctx->link_caps_ops;
+ int i;
+
+ for (i = 0; i < config_set->size; i++) {
+ int config_idx;
+
+ config_idx = ops->find_allowed_config_idx(link_caps,
+ INTEL_DP_LINK_CAPS_CONFIG_MATCH_EXACT,
+ &config_set->entries[i]);
+ KUNIT_ASSERT_GE(test, config_idx, 0);
+
+ KUNIT_ASSERT_FALSE(test, has_config(&expected_table->disabled_configs,
+ &config_set->entries[i]));
+ add_config(test, &expected_table->disabled_configs, &config_set->entries[i]);
+
+ ops->disable_config(link_caps, config_idx);
+
+ verify_link_caps(expected_table, link_caps);
+ }
+}
+
+static void disable_configs_for_shrink_and_verify(struct test_config_table *expected_table,
+ struct intel_dp_link_caps *link_caps)
+{
+ struct kunit *test = expected_table->test;
+ struct link_config_set config_set = {};
+ struct intel_dp_link_config max_config;
+
+ /*
+ * When configs shrink disable the config with the
+ * second-highest rate,lane params, so the disabled config
+ * stays around after the configs got shrunk.
+ */
+ KUNIT_ASSERT_GE(test, expected_table->rates.size, 2);
+ KUNIT_ASSERT_GE(test, expected_table->max_lane_count, 2);
+
+ max_config.rate = expected_table->rates.entries[expected_table->rates.size - 2];
+ max_config.lane_count = expected_table->max_lane_count >> 1;
+
+ add_config(test, &config_set, &max_config);
+ disable_configs_and_verify(test, link_caps, expected_table,
+ &config_set);
+}
+
+static void disable_configs_for_expand_and_verify(struct test_config_table *expected_table,
+ struct intel_dp_link_caps *link_caps)
+{
+ struct kunit *test = expected_table->test;
+ struct link_config_set config_set = {};
+ struct intel_dp_link_config max_config;
+
+ KUNIT_ASSERT_GE(test, expected_table->rates.size, 1);
+
+ max_config.rate = expected_table->rates.entries[expected_table->rates.size - 1];
+ max_config.lane_count = expected_table->max_lane_count;
+
+ add_config(test, &config_set, &max_config);
+ disable_configs_and_verify(test, link_caps, expected_table,
+ &config_set);
+}
+
+static void get_nth_rate_lane_config(const struct test_config_table *expected_table, int n,
+ struct intel_dp_link_config *config)
+{
+ int num_lane_configs = LINK_TEST_NUM_LANE_CONFIGS(expected_table->max_lane_count);
+ int rate_idx = n / num_lane_configs;
+ int lane_count_exp = n % num_lane_configs;
+
+ config->rate = expected_table->rates.entries[rate_idx];
+ config->lane_count = 1 << lane_count_exp;
+}
+
+static void test_update_rates_shrink(struct kunit *test, bool disable_configs)
+{
+ struct test_ctx *ctx = test->priv;
+ struct intel_dp_link_caps *link_caps = ctx->dev.dp.link.caps;
+ struct test_config_table expected_table =
+ INIT_STANDARD_TABLE(test, LINK_TEST_NUM_STANDARD_RATES,
+ LINK_TEST_MAX_LANE_COUNT);
+
+ update_link_caps_and_verify(&expected_table, link_caps,
+ INTEL_DP_LINK_CAPS_UPDATE_RESET);
+
+ while (expected_table.rates.size > 1) {
+ if (disable_configs)
+ disable_configs_for_shrink_and_verify(&expected_table, link_caps);
+
+ expected_table.rates.size--;
+
+ update_link_caps_and_verify(&expected_table, link_caps,
+ INTEL_DP_LINK_CAPS_UPDATE_MERGE);
+ }
+}
+
+static void intel_dp_link_caps_test_update_rates_shrink(struct kunit *test)
+{
+ test_update_rates_shrink(test, false);
+}
+
+static void intel_dp_link_caps_test_update_rates_shrink_disable(struct kunit *test)
+{
+ test_update_rates_shrink(test, true);
+}
+
+static void test_update_rates_expand(struct kunit *test, bool disable_configs)
+{
+ struct test_ctx *ctx = test->priv;
+ struct intel_dp_link_caps *link_caps = ctx->dev.dp.link.caps;
+ struct test_config_table expected_table =
+ INIT_STANDARD_TABLE(test, 1, LINK_TEST_MAX_LANE_COUNT);
+
+ update_link_caps_and_verify(&expected_table, link_caps,
+ INTEL_DP_LINK_CAPS_UPDATE_RESET);
+
+ while (expected_table.rates.size < LINK_TEST_NUM_STANDARD_RATES) {
+ if (disable_configs)
+ disable_configs_for_expand_and_verify(&expected_table, link_caps);
+
+ expected_table.rates.size++;
+
+ update_link_caps_and_verify(&expected_table, link_caps,
+ INTEL_DP_LINK_CAPS_UPDATE_MERGE);
+ }
+}
+
+static void intel_dp_link_caps_test_update_rates_expand(struct kunit *test)
+{
+ test_update_rates_expand(test, false);
+}
+
+static void intel_dp_link_caps_test_update_rates_expand_disable(struct kunit *test)
+{
+ test_update_rates_expand(test, true);
+}
+
+static void test_update_lanes_shrink(struct kunit *test, bool disable_configs)
+{
+ struct test_ctx *ctx = test->priv;
+ struct intel_dp_link_caps *link_caps = ctx->dev.dp.link.caps;
+ struct test_config_table expected_table =
+ INIT_STANDARD_TABLE(test, LINK_TEST_NUM_STANDARD_RATES,
+ LINK_TEST_MAX_LANE_COUNT);
+
+ update_link_caps_and_verify(&expected_table, link_caps,
+ INTEL_DP_LINK_CAPS_UPDATE_RESET);
+
+ while (expected_table.max_lane_count > 1) {
+ if (disable_configs)
+ disable_configs_for_shrink_and_verify(&expected_table, link_caps);
+
+ expected_table.max_lane_count >>= 1;
+
+ update_link_caps_and_verify(&expected_table, link_caps,
+ INTEL_DP_LINK_CAPS_UPDATE_MERGE);
+ }
+}
+
+static void intel_dp_link_caps_test_update_lanes_shrink(struct kunit *test)
+{
+ test_update_lanes_shrink(test, false);
+}
+
+static void intel_dp_link_caps_test_update_lanes_shrink_disable(struct kunit *test)
+{
+ test_update_lanes_shrink(test, true);
+}
+
+static void test_update_lanes_expand(struct kunit *test, bool disable_configs)
+{
+ struct test_ctx *ctx = test->priv;
+ struct intel_dp_link_caps *link_caps = ctx->dev.dp.link.caps;
+ struct test_config_table expected_table =
+ INIT_STANDARD_TABLE(test, LINK_TEST_NUM_STANDARD_RATES, 1);
+
+ update_link_caps_and_verify(&expected_table, link_caps,
+ INTEL_DP_LINK_CAPS_UPDATE_RESET);
+
+ while (expected_table.max_lane_count < LINK_TEST_MAX_LANE_COUNT) {
+ if (disable_configs)
+ disable_configs_for_expand_and_verify(&expected_table, link_caps);
+
+ expected_table.max_lane_count <<= 1;
+
+ update_link_caps_and_verify(&expected_table, link_caps,
+ INTEL_DP_LINK_CAPS_UPDATE_MERGE);
+ }
+}
+
+static void intel_dp_link_caps_test_update_lanes_expand(struct kunit *test)
+{
+ test_update_lanes_expand(test, false);
+}
+
+static void intel_dp_link_caps_test_update_lanes_expand_disable(struct kunit *test)
+{
+ test_update_lanes_expand(test, true);
+}
+
+static void disable_random_configs_and_verify(struct test_config_table *expected_table,
+ struct intel_dp_link_caps *link_caps)
+{
+ struct kunit *test = expected_table->test;
+ struct test_ctx *ctx = test->priv;
+ struct link_config_set config_set = {};
+ u32 disabled_config_mask;
+ int num_configs;
+ int i;
+
+ num_configs = get_num_configs(expected_table->rates.size,
+ expected_table->max_lane_count);
+ disabled_config_mask = prandom_u32_state(&ctx->rnd) &
+ GENMASK_U32(num_configs - 1, 0);
+
+ for (i = 0; i < num_configs; i++) {
+ struct intel_dp_link_config config;
+
+ /* At least one config must remain enabled. */
+ if (expected_table->disabled_configs.size +
+ config_set.size + 1 >= num_configs)
+ break;
+
+ if (!(BIT(i) & disabled_config_mask))
+ continue;
+
+ get_nth_rate_lane_config(expected_table, i, &config);
+ /* Don't disable a config twice. */
+ if (has_config(&expected_table->disabled_configs, &config))
+ continue;
+
+ add_config(test, &config_set, &config);
+ }
+
+ disable_configs_and_verify(test, link_caps, expected_table,
+ &config_set);
+}
+
+static void get_params_shrink_step(struct test_ctx *ctx,
+ int num_rates, int max_lane_count,
+ int *rates_step, int *lanes_step)
+{
+ int shrink_mask;
+
+ *rates_step = 0;
+ *lanes_step = 0;
+
+ if (num_rates == 1)
+ shrink_mask = BIT(0); /* shrink only lanes */
+ else if (max_lane_count == 1)
+ shrink_mask = BIT(1); /* shrink only rates */
+ else
+ shrink_mask = rand_in_range(ctx,
+ BIT(0),
+ BIT(0) | BIT(1)); /* shrink one or both params */
+
+ if (shrink_mask & BIT(1))
+ *rates_step = rand_in_range(ctx, 1, num_rates - 1);
+
+ if (shrink_mask & BIT(0))
+ *lanes_step = rand_in_range(ctx, 1, ilog2(max_lane_count));
+}
+
+static void get_params_expand_step(struct test_ctx *ctx,
+ int max_num_rates, int num_rates,
+ int max_supported_lane_count, int max_lane_count,
+ int *rates_step, int *lanes_step)
+{
+ int expand_mask;
+
+ *rates_step = 0;
+ *lanes_step = 0;
+
+ if (num_rates == max_num_rates)
+ expand_mask = BIT(0); /* expand only lanes */
+ else if (max_lane_count == max_supported_lane_count)
+ expand_mask = BIT(1); /* expand only rates */
+ else
+ expand_mask = rand_in_range(ctx,
+ BIT(0),
+ BIT(0) | BIT(1)); /* expand one or both params */
+
+ if (expand_mask & BIT(1))
+ *rates_step = rand_in_range(ctx, 1, max_num_rates - num_rates);
+
+ if (expand_mask & BIT(0))
+ *lanes_step = rand_in_range(ctx, 1, ilog2(max_supported_lane_count /
+ max_lane_count));
+}
+
+static void test_update_params_shrink_random(struct kunit *test, bool disable_configs)
+{
+ struct test_ctx *ctx = test->priv;
+ struct intel_dp_link_caps *link_caps = ctx->dev.dp.link.caps;
+ struct test_config_table expected_table =
+ INIT_STANDARD_TABLE(test, LINK_TEST_NUM_STANDARD_RATES,
+ LINK_TEST_MAX_LANE_COUNT);
+
+ update_link_caps_and_verify(&expected_table, link_caps,
+ INTEL_DP_LINK_CAPS_UPDATE_RESET);
+
+ while (expected_table.rates.size > 1 || expected_table.max_lane_count > 1) {
+ int rates_step;
+ int lanes_step;
+
+ if (disable_configs)
+ disable_random_configs_and_verify(&expected_table, link_caps);
+
+ get_params_shrink_step(ctx,
+ expected_table.rates.size,
+ expected_table.max_lane_count,
+ &rates_step, &lanes_step);
+
+ expected_table.rates.size -= rates_step;
+ expected_table.max_lane_count >>= lanes_step;
+
+ update_link_caps_and_verify(&expected_table, link_caps,
+ INTEL_DP_LINK_CAPS_UPDATE_MERGE);
+ }
+}
+
+static void intel_dp_link_caps_test_update_params_shrink_random(struct kunit *test)
+{
+ int i;
+
+ for (i = 0; i < LINK_TEST_NUM_RANDOM_ITERATIONS; i++)
+ test_update_params_shrink_random(test, false);
+}
+
+static void intel_dp_link_caps_test_update_params_shrink_disable_random(struct kunit *test)
+{
+ int i;
+
+ for (i = 0; i < LINK_TEST_NUM_RANDOM_ITERATIONS; i++)
+ test_update_params_shrink_random(test, true);
+}
+
+static void test_update_params_expand_random(struct kunit *test, bool disable_configs)
+{
+ struct test_ctx *ctx = test->priv;
+ struct intel_dp_link_caps *link_caps = ctx->dev.dp.link.caps;
+ struct test_config_table expected_table =
+ INIT_STANDARD_TABLE(test, 1, 1);
+
+ update_link_caps_and_verify(&expected_table, link_caps,
+ INTEL_DP_LINK_CAPS_UPDATE_RESET);
+
+ while (expected_table.rates.size < LINK_TEST_NUM_STANDARD_RATES ||
+ expected_table.max_lane_count < LINK_TEST_MAX_LANE_COUNT) {
+ int rates_step;
+ int lanes_step;
+
+ if (disable_configs)
+ disable_random_configs_and_verify(&expected_table, link_caps);
+
+ get_params_expand_step(ctx,
+ LINK_TEST_NUM_STANDARD_RATES,
+ expected_table.rates.size,
+ LINK_TEST_MAX_LANE_COUNT,
+ expected_table.max_lane_count,
+ &rates_step, &lanes_step);
+
+ expected_table.rates.size += rates_step;
+ expected_table.max_lane_count <<= lanes_step;
+
+ update_link_caps_and_verify(&expected_table, link_caps,
+ INTEL_DP_LINK_CAPS_UPDATE_MERGE);
+ }
+}
+
+static void intel_dp_link_caps_test_update_params_expand_random(struct kunit *test)
+{
+ int i;
+
+ for (i = 0; i < LINK_TEST_NUM_RANDOM_ITERATIONS; i++)
+ test_update_params_expand_random(test, false);
+}
+
+static void intel_dp_link_caps_test_update_params_expand_disable_random(struct kunit *test)
+{
+ int i;
+
+ for (i = 0; i < LINK_TEST_NUM_RANDOM_ITERATIONS; i++)
+ test_update_params_expand_random(test, true);
+}
+
static struct kunit_case intel_dp_link_test_cases[] = {
KUNIT_CASE(intel_dp_link_caps_test_baseline),

+ KUNIT_CASE(intel_dp_link_caps_test_update_reset),
+
+ KUNIT_CASE(intel_dp_link_caps_test_update_rates_shrink),
+ KUNIT_CASE(intel_dp_link_caps_test_update_rates_shrink_disable),
+ KUNIT_CASE(intel_dp_link_caps_test_update_rates_expand),
+ KUNIT_CASE(intel_dp_link_caps_test_update_rates_expand_disable),
+ KUNIT_CASE(intel_dp_link_caps_test_update_lanes_shrink),
+ KUNIT_CASE(intel_dp_link_caps_test_update_lanes_shrink_disable),
+ KUNIT_CASE(intel_dp_link_caps_test_update_lanes_expand),
+ KUNIT_CASE(intel_dp_link_caps_test_update_lanes_expand_disable),
+ KUNIT_CASE(intel_dp_link_caps_test_update_params_shrink_random),
+ KUNIT_CASE(intel_dp_link_caps_test_update_params_shrink_disable_random),
+ KUNIT_CASE(intel_dp_link_caps_test_update_params_expand_random),
+ KUNIT_CASE(intel_dp_link_caps_test_update_params_expand_disable_random),
+
{}
};

--
2.49.1