[PATCH i-g-t 4/5] tests/intel/kms_dp_linktraining: add tests for UHBR/NON-UHBR over SST/MST
Sharma, Swati2
swati2.sharma at intel.com
Tue Feb 4 14:01:46 UTC 2025
Hi Kunal,
Please find my review comments below.
On 03-02-2025 01:42 pm, Kunal Joshi wrote:
> Add new test subtests kms_dp_linktraining
typo : remove subtests
> 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 | 372 ++++++++++++++++++++++++++++++
> tests/meson.build | 4 +
> 2 files changed, 376 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..6cedc47db
> --- /dev/null
> +++ b/tests/intel/kms_dp_linktraining.c
> @@ -0,0 +1,372 @@
> +// 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
Should this be link_training ?
> + * Mega feature: General Display Features
> + * Test category: functionality test
> + */
> +
> +/**
> + * SUBTEST: uhbr-sst
> + * Description: Test we can drive UHBR rates over SST.
> + * Functionality: feature_discovery, uhbr, sst
This is not feature_discovery test anymore. Please remove.
Same implies to remaining subtests.
> + * Test category: functionality test
> + *
> + * SUBTEST: uhbr-mst
> + * Description: Test we can drive UHBR rates over MST.
> + * Functionality: feature_discovery, uhbr, mst
> + * Test category: functionality test
> + *
> + * SUBTEST: non-uhbr-sst
> + * Description: Test we can drive non-UHBR rates over SST.
> + * Functionality: feature_discovery, sst
> + * Test category: functionality test
> + *
> + * SUBTEST: non-uhbr-mst
> + * Description: Test we can drive non-UHBR rates over MST.
> + * Functionality: feature_discovery, mst
> + * 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.
> + */
Shall we have some gen check since which intel platform UHBR is
supported so that test can exit early?
> +#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)
Fix indentation.
> +{
> + 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, ¤t_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[])
Fix indentation.
> +{
> + 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);
> +
> + /* FIXME : Driver may lie max link rate or max lane count */
> + /* 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);
> + }
> +
> + 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);
> + }
> +}
> diff --git a/tests/meson.build b/tests/meson.build
> index b9dd162cc..926c5825f 100644
> --- a/tests/meson.build
> +++ b/tests/meson.build
> @@ -246,6 +246,7 @@ intel_kms_progs = [
> 'kms_ccs',
> 'kms_cdclk',
> 'kms_dirtyfb',
> + 'kms_dp_linktraining',
> 'kms_dp_linktrain_fallback',
> 'kms_draw_crc',
> 'kms_dsc',
> @@ -367,6 +368,9 @@ extra_sources = {
> 'kms_chamelium_frames': [ join_paths ('chamelium', 'kms_chamelium_helper.c') ],
> 'kms_chamelium_hpd': [ join_paths ('chamelium', 'kms_chamelium_helper.c') ],
> 'kms_dsc': [ join_paths ('intel', 'kms_dsc_helper.c') ],
> + 'kms_dp_linktraining': [
> + join_paths ('intel', 'kms_joiner_helper.c'),
> + join_paths ('intel', 'kms_mst_helper.c') ],
> 'kms_dp_linktrain_fallback': [ join_paths ('intel', 'kms_mst_helper.c') ],
> 'kms_joiner': [ join_paths ('intel', 'kms_joiner_helper.c') ],
> 'kms_psr2_sf': [ join_paths ('intel', 'kms_dsc_helper.c') ],
More information about the igt-dev
mailing list