[PATCH i-g-t 09/10] tests/intel/kms_dp_fallback: add test for validating fallback

Kunal Joshi kunal1.joshi at intel.com
Tue Jul 30 21:13:40 UTC 2024


add fallback test which tries to validate fallback by
reducing link rate / lane count, until retrain is disabled
or the lowest mode bw requirements are met.

v2: add test for mst (imre)
    refresh mode list (imre)
    monitor got hotplugs (imre)
    check link parameter are reduced (imre)

Signed-off-by: Kunal Joshi <kunal1.joshi at intel.com>
---
 tests/intel/kms_dp_fallback.c | 361 ++++++++++++++++++++++++++++++++++
 tests/meson.build             |   1 +
 2 files changed, 362 insertions(+)
 create mode 100644 tests/intel/kms_dp_fallback.c

diff --git a/tests/intel/kms_dp_fallback.c b/tests/intel/kms_dp_fallback.c
new file mode 100644
index 000000000..0a74bfc58
--- /dev/null
+++ b/tests/intel/kms_dp_fallback.c
@@ -0,0 +1,361 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2024 Intel Corporation
+ */
+
+/**
+ * TEST: kms dp fallback
+ * Category: Display
+ * Description: Test link training fallback for DP connectors
+ * Driver requirement: i915, xe
+ * Functionality: link training
+ * Mega feature: General Display Features
+ * Test category: functionality test
+ */
+
+#include <sys/types.h>
+
+#include "igt.h"
+#include "igt_psr.h"
+
+/**
+ * SUBTEST: dp-fallback
+ * Description: Test fallback on DP connectors
+ *
+ * SUBTEST: mst-fallback
+ * Description: Test fallback on DP MST connectors
+ *
+ * SUBTEST: uhbr-fallback
+ * Description: Test fallback on DP connectors with uhbr link rate
+ */
+
+enum tests {
+	TEST_EDP_FALLBACK,
+	TEST_DP_FALLBACK,
+	TEST_MST_FALLBACK,
+	TEST_UHBR_FALLBACK,
+	MAX_TESTS,
+};
+
+typedef struct {
+	int drm_fd;
+	igt_display_t display;
+	drmModeModeInfo *mode;
+	igt_output_t *output;
+	enum pipe pipe;
+	struct igt_fb fb;
+	struct igt_plane *primary;
+	enum tests test;
+	int n_pipes;
+} data_t;
+
+IGT_TEST_DESCRIPTION("Test link training fallback for DP connectors");
+
+#define BITS_PER_PIXEL 24
+#define ENCODING_OVERHEAD_8b_10b 0.8
+#define ENCODING_OVERHEAD_128b_132b 0.9723
+
+static const char *str_link_rate(enum dp_link_rate link_rate)
+{
+	switch (link_rate) {
+	case DP_LINK_RATE_162000:
+		return "1.62 Gbps";
+	case DP_LINK_RATE_216000:
+		return "2.16 Gbps";
+	case DP_LINK_RATE_243000:
+		return "2.43 Gbps";
+	case DP_LINK_RATE_270000:
+		return "2.70 Gbps";
+	case DP_LINK_RATE_324000:
+		return "3.24 Gbps";
+	case DP_LINK_RATE_432000:
+		return "4.32 Gbps";
+	case DP_LINK_RATE_540000:
+		return "5.40 Gbps";
+	case DP_LINK_RATE_675000:
+		return "6.75 Gbps";
+	case DP_LINK_RATE_810000:
+		return "8.10 Gbps";
+	case DP_LINK_RATE_1000000:
+		return "10.00 Gbps";
+	case DP_LINK_RATE_1350000:
+		return "13.50 Gbps";
+	case DP_LINK_RATE_2000000:
+		return "20.00 Gbps";
+	default:
+		igt_assert_f(0, "Invalid link rate %d\n", link_rate);
+	}
+}
+
+static const char *str_lane_count(enum dp_lane_count lane_count)
+{
+	switch (lane_count) {
+	case DP_LANE_COUNT_1:
+		return "1";
+	case DP_LANE_COUNT_2:
+		return "2";
+	case DP_LANE_COUNT_4:
+		return "4";
+	default:
+		igt_assert_f(0, "Invalid lane count %d\n", lane_count);
+	}
+}
+
+static void find_mst_outputs(int drm_fd, data_t *data, igt_output_t *output,
+			     igt_output_t **mst_outputs, int *num_mst_outputs)
+{
+	bool is_output_mst;
+	uint64_t path_blob_id;
+	igt_output_t *connector_output;
+	drmModePropertyPtr path_prop = NULL;
+	drmModePropertyPtr connector_path_prop = NULL;
+
+	igt_assert_f(output, "Invalid output\n");
+
+	/*
+	 * Check if given output is MST by checking if it has PATH property
+	 */
+	is_output_mst = kmstest_get_property(drm_fd,
+					     output->config.connector->connector_id,
+					     DRM_MODE_OBJECT_CONNECTOR, "PATH", NULL,
+					     &path_blob_id, &path_prop);
+
+	if (!is_output_mst)
+		return;
+
+	/*
+	 * If output is MST check all other connected output which shares same path
+	 * and fill mst_outputs and num_mst_outputs
+	 */
+	for_each_connected_output(&data->display, connector_output) {
+
+		if (connector_output->config.connector == output->config.connector)
+			continue;
+
+		connector_path_prop = NULL;
+
+		kmstest_get_property(drm_fd,
+				     connector_output->config.connector->connector_id,
+				     DRM_MODE_OBJECT_CONNECTOR, "PATH", NULL,
+				     &path_blob_id, &connector_path_prop);
+
+		if (connector_path_prop && path_prop &&
+		    connector_path_prop->prop_id == path_prop->prop_id)
+			mst_outputs[(*num_mst_outputs)++] = connector_output;
+
+		if (connector_path_prop)
+			drmModeFreeProperty(connector_path_prop);
+	}
+	if (path_prop)
+		drmModeFreeProperty(path_prop);
+}
+
+static bool is_retrain_disabled(data_t *data)
+{
+	return igt_get_dp_link_retrain_disabled(data->drm_fd, data->output);
+}
+
+static void test_mst_fallback(data_t *data, igt_output_t **mst_outputs, int *no_of_mst_outputs)
+{
+	int i;
+	enum dp_link_rate link_rate, prev_link_rate, curr_link_rate;
+	enum dp_lane_count lane_count, prev_lane_count, curr_lane_count;
+	struct udev_monitor *mon;
+	igt_output_t *output[*no_of_mst_outputs];
+	drmModeModeInfo *mode[*no_of_mst_outputs];
+	igt_plane_t *primary[*no_of_mst_outputs];
+	struct igt_fb fb[*no_of_mst_outputs];
+
+	link_rate = igt_get_dp_max_link_rate(data->drm_fd, data->output);
+	lane_count = igt_get_dp_max_lane_count(data->drm_fd, data->output);
+	prev_link_rate = link_rate;
+	prev_lane_count = lane_count;
+
+
+	while (!is_retrain_disabled(data)) {
+
+		for (i = 0; i < *no_of_mst_outputs; i++) {
+			if (i > data->n_pipes)
+				break;
+
+			output[i] = mst_outputs[i];
+			output[i]->force_reprobe = true;
+			igt_output_refresh(output[i]);
+			mode[i] = &output[i]->config.connector->modes[0];
+			igt_debug("updated output %s mode: %dx%d@%d\n", output[i]->name, mode[i]->hdisplay, mode[i]->vdisplay, mode[i]->vrefresh);
+			igt_create_fb(data->drm_fd, mode[i]->hdisplay, mode[i]->vdisplay, DRM_FORMAT_XRGB8888, DRM_FORMAT_MOD_LINEAR, &fb[i]);
+			igt_output_set_pipe(output[i], i);
+			primary[i] = igt_output_get_plane_type(output[i], DRM_PLANE_TYPE_PRIMARY);
+			igt_plane_set_fb(primary[i], &fb[i]);
+		}
+
+		igt_display_commit2(&data->display, COMMIT_ATOMIC);
+		mon = igt_watch_uevents();
+
+		igt_debug("forcing link training failure and retraining\n");
+		kmstest_force_connector_link_training_failure(data->drm_fd, data->output->config.connector, 2);
+
+		while (igt_get_dp_pending_lt_failures(data->drm_fd, data->output) > 0)
+			kmstest_force_connector_retrain(data->drm_fd, data->output->config.connector, 1);
+
+		igt_assert_f(igt_hotplug_detected(mon, 20) || is_retrain_disabled(data),
+			     "Didn't get hotplug for force link training failure\n");
+		igt_flush_uevents(mon);
+
+		curr_link_rate = igt_get_dp_link_rate_set_for_output(data->drm_fd, data->output);
+		curr_lane_count = igt_get_dp_lane_count_set_for_output(data->drm_fd, data->output);
+		igt_info("Current link rate : %s, lane count : %s\n",
+			 str_link_rate(curr_link_rate), str_lane_count(curr_lane_count));
+		igt_assert_f(curr_link_rate < prev_link_rate || curr_lane_count < prev_lane_count,
+			     "Fallback failed\n");
+	}
+
+	kmstest_force_connector_link_rate(data->drm_fd, data->output->config.connector, link_rate);
+	kmstest_force_connector_lane_count(data->drm_fd, data->output->config.connector, lane_count);
+	kmstest_force_connector_retrain(data->drm_fd, data->output->config.connector, 1);
+	igt_reset_connectors();
+}
+
+static void test_dp_fallback(data_t *data)
+{
+	struct udev_monitor *mon;
+	enum dp_link_rate link_rate, prev_link_rate, curr_link_rate;
+	enum dp_lane_count lane_count, prev_lane_count, curr_lane_count;
+
+	link_rate = igt_get_dp_max_link_rate(data->drm_fd, data->output);
+	lane_count = igt_get_dp_max_lane_count(data->drm_fd, data->output);
+	prev_link_rate = link_rate;
+	prev_lane_count = lane_count;
+
+	for_each_pipe(&data->display, data->pipe) {
+
+		igt_dynamic_f("%s-pipe-%s", igt_output_name(data->output),
+			      kmstest_pipe_name(data->pipe)) {
+
+			if (data->test == TEST_UHBR_FALLBACK)
+				igt_require_f(link_rate > DP_LINK_RATE_810000,
+					      "%s is doesn't have UHBR rate\n", data->output->name);
+
+			igt_display_reset(&data->display);
+
+			while (!is_retrain_disabled(data)) {
+
+				/*
+				 * Start with default mode and fallback till we reach minimum link rate and lane count
+				 */
+				data->output->force_reprobe = true;
+				igt_output_refresh(data->output);
+				data->mode = &data->output->config.connector->modes[0];
+				igt_debug("mode: %dx%d@%d\n",
+					 data->mode->hdisplay, data->mode->vdisplay,
+					 data->mode->vrefresh);
+				igt_create_pattern_fb(data->drm_fd, data->mode->hdisplay,
+						      data->mode->vdisplay, DRM_FORMAT_XRGB8888,
+						      DRM_FORMAT_MOD_LINEAR, &data->fb);
+				igt_output_set_pipe(data->output, data->pipe);
+				data->primary = igt_output_get_plane_type(data->output, DRM_PLANE_TYPE_PRIMARY);
+				igt_plane_set_fb(data->primary, &data->fb);
+				igt_display_commit2(&data->display, COMMIT_ATOMIC);
+
+				mon = igt_watch_uevents();
+
+				igt_debug("forcing link training failure and retraining\n");
+				kmstest_force_connector_link_training_failure(data->drm_fd, data->output->config.connector, 2);
+
+				while (igt_get_dp_pending_lt_failures(data->drm_fd, data->output) > 0)
+					kmstest_force_connector_retrain(data->drm_fd, data->output->config.connector, 1);
+
+				igt_assert_f(igt_hotplug_detected(mon, 20) || is_retrain_disabled(data),
+					     "Didn't get hotplug for force link training failure\n");
+				igt_flush_uevents(mon);
+
+				curr_link_rate = igt_get_dp_link_rate_set_for_output(data->drm_fd, data->output);
+				curr_lane_count = igt_get_dp_lane_count_set_for_output(data->drm_fd, data->output);
+				igt_info("Current link rate : %s, lane count : %s\n",
+					 str_link_rate(curr_link_rate), str_lane_count(curr_lane_count));
+				igt_assert_f(curr_link_rate < prev_link_rate || curr_lane_count < prev_lane_count,
+					     "Fallback failed\n");
+			}
+			kmstest_force_connector_link_rate(data->drm_fd, data->output->config.connector, link_rate);
+			kmstest_force_connector_lane_count(data->drm_fd, data->output->config.connector, lane_count);
+			kmstest_force_connector_retrain(data->drm_fd, data->output->config.connector, 1);
+			igt_reset_connectors();
+		}
+	}
+}
+
+igt_main
+{
+	data_t data = {};
+	igt_output_t *output;
+	enum pipe pipe;
+
+	igt_fixture {
+		data.drm_fd = drm_open_driver_master(DRIVER_INTEL);
+		data.n_pipes = 0;
+		kmstest_set_vt_graphics_mode();
+		igt_display_require(&data.display, data.drm_fd);
+
+		for_each_pipe(&data.display, pipe)
+			data.n_pipes++;
+
+	}
+
+	igt_describe_f("Test DP fallback");
+	igt_subtest_with_dynamic("dp-fallback")
+	{
+		data.test = TEST_DP_FALLBACK;
+		for_each_connected_output(&data.display, output) {
+			/* Skip if output is not DP or if it's mst*/
+			if (output->config.connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort) {
+				igt_info("Skipping output %s as it's not DP\n", output->name);
+				continue;
+			}
+			data.output = output;
+			test_dp_fallback(&data);
+		}
+	}
+
+	igt_describe_f("Test UHBR fallback");
+	igt_subtest_with_dynamic_f("uhbr-fallback")
+	{
+		data.test = TEST_UHBR_FALLBACK;
+		for_each_connected_output(&data.display, output) {
+			/* Skip if output is not DP or if it's mst*/
+			if (output->config.connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort) {
+				igt_info("Skipping output %s as it's not DP\n", output->name);
+				continue;
+			}
+			data.output = output;
+			test_dp_fallback(&data);
+		}
+	}
+
+	igt_describe_f("MST Fallback");
+	igt_subtest_with_dynamic("mst-fallback")
+	{
+		int num_mst_outputs;
+		igt_output_t **mst_outputs;
+
+		for_each_connected_output(&data.display, output) {
+
+			if (!igt_check_output_is_dp_mst(output)) {
+				igt_info("Skipping output %s as its non mst\n", output->name);
+				continue;
+			}
+
+			num_mst_outputs = 0;
+			mst_outputs = malloc(sizeof(igt_output_t *) * data.n_pipes);
+			data.output = output;
+			mst_outputs[num_mst_outputs++] = output;
+			find_mst_outputs(data.drm_fd, &data, data.output, mst_outputs, &num_mst_outputs);
+			igt_dynamic_f("%s", igt_output_name(data.output))
+				test_mst_fallback(&data, mst_outputs, &num_mst_outputs);
+		}
+	}
+
+	igt_fixture {
+		igt_display_fini(&data.display);
+		drm_close_driver(data.drm_fd);
+	}
+}
diff --git a/tests/meson.build b/tests/meson.build
index e649466be..23c453170 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -247,6 +247,7 @@ intel_kms_progs = [
 	'kms_ccs',
 	'kms_cdclk',
 	'kms_dirtyfb',
+        'kms_dp_fallback',
 	'kms_draw_crc',
 	'kms_dsc',
 	'kms_fb_coherency',
-- 
2.43.0



More information about the igt-dev mailing list