[PATCH i-g-t 3/4] tests/intel/kms_dp_linktraining: add tests for UHBR/NON-UHBR over SST/MST

Kunal Joshi kunal1.joshi at intel.com
Fri Jan 31 09:30:59 UTC 2025


Add new test subtests kms_dp_linktraining
to validate both UHBR and non-UHBR link rates over SST
and MST configurations.
Add four new subtests (uhbr-sst, uhbr-mst, non-uhbr-sst, non-uhbr-mst)
to check if the link rates match the expected UHBR or NON-UHBR capability
and whether the outputs are MST or SST.

v2: Add definition for UHBR_LINK_RATE
v3: Add failure criteria (Jani Nikula)

Signed-off-by: Kunal Joshi <kunal1.joshi at intel.com>
---
 tests/intel/kms_dp_linktraining.c | 353 ++++++++++++++++++++++++++++++
 1 file changed, 353 insertions(+)
 create mode 100644 tests/intel/kms_dp_linktraining.c

diff --git a/tests/intel/kms_dp_linktraining.c b/tests/intel/kms_dp_linktraining.c
new file mode 100644
index 000000000..bc8167008
--- /dev/null
+++ b/tests/intel/kms_dp_linktraining.c
@@ -0,0 +1,353 @@
+// SPDX-License-Identifier: MIT
+/*
+ * TEST: kms dp linktraining
+ * Category: Display
+ * Description: Test to validate link training on SST/MST with UHBR/NON_UHBR rates
+ * Driver requirement: i915, xe
+ * Functionality: linktraining
+ * Mega feature: General Display Features
+ * Test category: functionality test
+ */
+
+#include "igt.h"
+#include "igt_kms.h"
+#include "intel/kms_joiner_helper.h"
+#include "intel/kms_mst_helper.h"
+
+/*
+ * DP Spec defines 10, 13.5, and 20 Gbps as UHBR.
+ * Anything below that is considered NON-UHBR.
+ */
+#define UHBR_LINK_RATE	1000000
+#define RETRAIN_COUNT	1
+
+typedef struct {
+	int drm_fd;
+	igt_display_t display;
+	igt_output_t *output;
+} data_t;
+
+/*
+ * check_condition_with_timeout - Polls check_fn until it returns 0
+ * or until 'timeout' seconds elapse.
+ */
+static int check_condition_with_timeout(int drm_fd, igt_output_t *output,
+		int (*check_fn)(int, igt_output_t *),
+		double interval, double timeout)
+{
+	struct timespec start_time, current_time;
+	double elapsed_time;
+	int ret;
+
+	clock_gettime(CLOCK_MONOTONIC, &start_time);
+
+	while (true) {
+		ret = check_fn(drm_fd, output);
+		if (ret == 0)
+			return 0;
+
+		clock_gettime(CLOCK_MONOTONIC, &current_time);
+		elapsed_time = (current_time.tv_sec - start_time.tv_sec) +
+			(current_time.tv_nsec - start_time.tv_nsec) / 1e9;
+		if (elapsed_time >= timeout)
+			return -1;
+
+		usleep((useconds_t)(interval * 1e6));
+	}
+}
+
+/*
+ * assert_link_status_good - Verifies link-status == GOOD
+ * for either a single SST output or all MST outputs in the topology.
+ */
+static void assert_link_status_good(data_t *data, bool mst)
+{
+	igt_output_t *outputs[IGT_MAX_PIPES];
+	uint32_t link_status_prop_id;
+	uint64_t link_status_value;
+	drmModePropertyPtr link_status_prop;
+	int count = 0;
+	int i;
+
+	if (mst) {
+		igt_assert_f(igt_find_all_mst_output_in_topology(data->drm_fd,
+					&data->display, data->output,
+					outputs, &count),
+				"Unable to find MST outputs\n");
+
+		for (i = 0; i < count; i++) {
+			kmstest_get_property(data->drm_fd,
+					outputs[i]->config.connector->connector_id,
+					DRM_MODE_OBJECT_CONNECTOR,
+					"link-status",
+					&link_status_prop_id,
+					&link_status_value,
+					&link_status_prop);
+
+			igt_assert_eq(link_status_value,
+					DRM_MODE_LINK_STATUS_GOOD);
+		}
+	} else {
+		kmstest_get_property(data->drm_fd,
+				data->output->config.connector->connector_id,
+				DRM_MODE_OBJECT_CONNECTOR,
+				"link-status",
+				&link_status_prop_id,
+				&link_status_value,
+				&link_status_prop);
+
+		igt_assert_eq(link_status_value, DRM_MODE_LINK_STATUS_GOOD);
+	}
+}
+
+/*
+ * setup_planes_fbs - Create solid-color FBs and attach them to the primary plane.
+ */
+static void setup_planes_fbs(data_t *data, igt_output_t *outs[],
+		int count, drmModeModeInfo *modes[],
+		struct igt_fb fbs[], struct igt_plane *planes[])
+{
+	int i;
+
+	for (i = 0; i < count; i++) {
+		modes[i] = igt_output_get_mode(outs[i]);
+		igt_info("Mode %dx%d@%d on output %s\n",
+				modes[i]->hdisplay, modes[i]->vdisplay,
+				modes[i]->vrefresh, igt_output_name(outs[i]));
+
+		planes[i] = igt_output_get_plane_type(outs[i],
+				DRM_PLANE_TYPE_PRIMARY);
+
+		igt_create_color_fb(data->drm_fd, modes[i]->hdisplay,
+				modes[i]->vdisplay,
+				DRM_FORMAT_XRGB8888,
+				DRM_FORMAT_MOD_LINEAR,
+				0.0, 1.0, 0.0, &fbs[i]);
+
+		igt_plane_set_fb(planes[i], &fbs[i]);
+	}
+}
+
+/*
+ * fit_modes_in_bw - Tries atomic TEST_ONLY commit; if it fails, overrides
+ * output modes to fit bandwidth.
+ */
+static bool fit_modes_in_bw(data_t *data)
+{
+	int ret;
+
+	ret = igt_display_try_commit_atomic(&data->display,
+			DRM_MODE_ATOMIC_TEST_ONLY |
+			DRM_MODE_ATOMIC_ALLOW_MODESET,
+			NULL);
+	if (ret != 0) {
+		bool found;
+
+		found = igt_override_all_active_output_modes_to_fit_bw(&data->display);
+		igt_require_f(found, "No valid mode combo found for modeset\n");
+	}
+
+	return true;
+}
+
+static void do_modeset(data_t *data, bool mst)
+{
+	uint32_t master_pipes_mask = 0;
+	uint32_t valid_pipes_mask = 0;
+	uint32_t used_pipes_mask = 0;
+	igt_output_t *outs[IGT_MAX_PIPES];
+	drmModeModeInfo *modes[IGT_MAX_PIPES];
+	struct igt_fb fbs[IGT_MAX_PIPES];
+	struct igt_plane *planes[IGT_MAX_PIPES];
+	int n_pipes = 0;
+	int out_count = 0;
+	int i;
+
+	for_each_pipe(&data->display, i) {
+		valid_pipes_mask |= BIT(i);
+		n_pipes++;
+	}
+
+	if (mst) {
+		igt_assert_f(igt_find_all_mst_output_in_topology(data->drm_fd,
+					&data->display,
+					data->output, outs,
+					&out_count),
+				"Unable to find MST outputs\n");
+	} else {
+		outs[0] = data->output;
+		out_count = 1;
+	}
+
+	igt_assert_f(out_count > 0, "Require at least one output\n");
+
+	igt_set_all_master_pipes_for_platform(&data->display, &master_pipes_mask);
+
+	igt_assert_f(igt_assign_pipes_for_outputs(data->drm_fd,
+				outs,
+				out_count,
+				n_pipes,
+				&used_pipes_mask,
+				master_pipes_mask,
+				valid_pipes_mask),
+			"Unable to assign pipes for outputs\n");
+
+	setup_planes_fbs(data, outs, out_count, modes, fbs, planes);
+	fit_modes_in_bw(data);
+	igt_display_commit2(&data->display, COMMIT_ATOMIC);
+}
+
+/*
+ * run_link_rate_test - Main link training routine. Expects the MST vs. SST check
+ * to be done beforehand. Returns true if tested at the correct rate.
+ */
+static bool run_link_rate_test(data_t *data, bool mst, bool uhbr)
+{
+	int max_link_rate;
+	int max_lane_count;
+	int current_link_rate;
+	bool is_uhbr_output;
+	char rate_str[32];
+	char lane_str[32];
+
+	igt_display_reset(&data->display);
+	igt_reset_link_params(data->drm_fd, data->output);
+
+	/* Retrain at default/driver parameters */
+	igt_force_link_retrain(data->drm_fd, data->output, RETRAIN_COUNT);
+	igt_assert_eq(check_condition_with_timeout(data->drm_fd, data->output,
+		      igt_get_dp_pending_retrain, 1.0, 20.0), 0);
+	assert_link_status_good(data, mst);
+	do_modeset(data, mst);
+
+	/* Read max_link_rate and max_lane_count */
+	max_link_rate = igt_get_max_link_rate(data->drm_fd, data->output);
+	max_lane_count = igt_get_max_lane_count(data->drm_fd, data->output);
+
+	/* Check sink supports uhbr or not */
+	is_uhbr_output = (max_link_rate >= UHBR_LINK_RATE);
+	if ((uhbr && !is_uhbr_output) || (!uhbr && is_uhbr_output)) {
+		igt_info("Test expects %s, but output %s is %s.\n",
+			 uhbr ? "UHBR" : "NON-UHBR",
+			 data->output->name,
+			 is_uhbr_output ? "UHBR" : "NON-UHBR");
+		igt_info("----------------------------------------------------\n");
+		return false;
+	}
+
+	snprintf(rate_str, sizeof(rate_str), "%d", max_link_rate);
+	snprintf(lane_str, sizeof(lane_str), "%d", max_lane_count);
+	igt_info("Max link rate for %s is %s, lane count = %d\n",
+		 data->output->name, rate_str, max_lane_count);
+
+	/* Force retrain at max link params */
+	igt_set_link_params(data->drm_fd, data->output, rate_str, lane_str);
+	igt_force_link_retrain(data->drm_fd, data->output, RETRAIN_COUNT);
+	igt_assert_eq(check_condition_with_timeout(data->drm_fd, data->output,
+		      igt_get_dp_pending_retrain, 1.0, 20.0), 0);
+	assert_link_status_good(data, mst);
+
+	current_link_rate = igt_get_current_link_rate(data->drm_fd, data->output);
+	igt_info("Current link rate is %d\n", current_link_rate);
+	igt_assert_f(current_link_rate == max_link_rate,
+		     "Link training did not succeed at max link rate.\n");
+	igt_assert_f(is_uhbr_output ?
+		     current_link_rate >= UHBR_LINK_RATE :
+		     current_link_rate < UHBR_LINK_RATE,
+		     is_uhbr_output ? "Link training didn't happen at uhbr rates" :
+		     "Link training didn't happen at non-uhbr rates");
+	igt_info("----------------------------------------------------\n");
+	return true;
+}
+
+/*
+ * test_link_rate - Iterates over connected DP outputs. Checks MST vs. SST
+ * early, then calls run_link_rate_test(). Returns true if it ran on at
+ * least one matching output.
+ */
+static bool test_link_rate(data_t *data, bool mst, bool uhbr)
+{
+	bool ran_any_output = false, is_mst = false;
+	igt_output_t *tmp_output;
+
+	igt_skip_on_f(!is_intel_device(data->drm_fd),
+			"Test supported only on Intel platforms.\n");
+
+	for_each_connected_output(&data->display, tmp_output) {
+		if (tmp_output->config.connector->connector_type !=
+		    DRM_MODE_CONNECTOR_DisplayPort) {
+			igt_info("Skipping non-DisplayPort output %s\n",
+					tmp_output->name);
+			igt_info("----------------------------------------------------\n");
+			continue;
+		}
+
+		/* Early skip if MST vs. SST does not match. */
+		is_mst = igt_check_output_is_dp_mst(tmp_output);
+		if (mst && !is_mst) {
+			igt_info("Skipping %s: MST requested but it's SST.\n",
+					tmp_output->name);
+			igt_info("----------------------------------------------------\n");
+			continue;
+		} else if (!mst && is_mst) {
+			igt_info("Skipping %s: SST requested but it's MST.\n",
+					tmp_output->name);
+			igt_info("----------------------------------------------------\n");
+			continue;
+		}
+		data->output = tmp_output;
+		igt_info("Running link training test for %s\n",
+			 data->output->name);
+		ran_any_output = ran_any_output | run_link_rate_test(data, mst, uhbr);
+	}
+	return ran_any_output;
+}
+
+IGT_TEST_DESCRIPTION("Test to validate link training on SST/MST with "
+		"UHBR/NON_UHBR rates");
+
+igt_main
+{
+	data_t data = {};
+
+	igt_fixture {
+		data.drm_fd = drm_open_driver_master(DRIVER_ANY);
+		kmstest_set_vt_graphics_mode();
+		igt_display_require(&data.display, data.drm_fd);
+		igt_display_require_output(&data.display);
+	}
+
+	/*
+	 * Subtests:
+	 * uhbr-sst, uhbr-mst, non-uhbr-sst, non-uhbr-mst
+	 */
+	igt_describe("Test we can drive UHBR rates over SST");
+	igt_subtest("uhbr-sst") {
+		igt_require_f(test_link_rate(&data, false, true),
+			      "Didn't find any SST output with UHBR rates.\n");
+	}
+
+	igt_describe("Test we can drive UHBR rates over MST");
+	igt_subtest("uhbr-mst") {
+		igt_require_f(test_link_rate(&data, true, true),
+			      "Didn't find any MST output with UHBR rates.\n");
+	}
+
+	igt_describe("Test we can drive NON-UHBR rates over SST");
+	igt_subtest("non-uhbr-sst") {
+		igt_require_f(test_link_rate(&data, false, false),
+			      "Didn't find any SST output with NON-UHBR rates.\n");
+	}
+
+	igt_describe("Test we can drive NON-UHBR rates over MST");
+	igt_subtest("non-uhbr-mst") {
+		igt_require_f(test_link_rate(&data, true, false),
+			      "Didn't find any MST output with NON-UHBR rates.\n");
+	}
+
+	igt_fixture {
+		igt_reset_connectors();
+		igt_display_fini(&data.display);
+		close(data.drm_fd);
+	}
+}
-- 
2.34.1



More information about the Intel-gfx-trybot mailing list