* [PATCH i-g-t 0/6] RFC: Add new test for eDP data override
@ 2025-11-24 18:27 Jeevan B
2025-11-24 18:27 ` [PATCH i-g-t 1/6] tests/intel/kms_dp_link_training: rename to tests/intel/kms_link_training Jeevan B
` (5 more replies)
0 siblings, 6 replies; 9+ messages in thread
From: Jeevan B @ 2025-11-24 18:27 UTC (permalink / raw)
To: igt-dev; +Cc: kunal1.joshi, suraj.kandpal, Jeevan B
Ignore the first 4 patches as those are under review in a
different series. Since those patches are dependencies for
the last two patches, I have included them.
Please review patches 5 & 6:
lib/igt_kms: Add helper to get eDP/DP supported link rates
tests/intel/kms_link_training: Add edp-data-override subtest
Jeevan B (2):
lib/igt_kms: Add helper to get eDP/DP supported link rates
RFC: tests/intel/kms_link_training: Add edp-data-override subtest
Kunal Joshi (4):
tests/intel/kms_dp_link_training: rename to
tests/intel/kms_link_training
tests/intel/kms_dp_linktrain_fallback: rename to
tests/intel/kms_linktrain_fallback
lib/igt_kms: add helpers for connector type
tests/intel/kms_link_training: extend test for eDP connector
lib/igt_kms.c | 126 +++-
lib/igt_kms.h | 11 +-
tests/intel/kms_dp_linktrain_fallback.c | 639 ------------------
tests/intel/kms_dsc_helper.c | 2 +-
...dp_link_training.c => kms_link_training.c} | 191 +++++-
tests/intel/kms_pm_backlight.c | 2 +-
tests/kms_atomic_transition.c | 4 +-
tests/kms_hdr.c | 2 +-
tests/meson.build | 16 +-
9 files changed, 323 insertions(+), 670 deletions(-)
delete mode 100644 tests/intel/kms_dp_linktrain_fallback.c
rename tests/intel/{kms_dp_link_training.c => kms_link_training.c} (65%)
--
2.43.0
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH i-g-t 1/6] tests/intel/kms_dp_link_training: rename to tests/intel/kms_link_training 2025-11-24 18:27 [PATCH i-g-t 0/6] RFC: Add new test for eDP data override Jeevan B @ 2025-11-24 18:27 ` Jeevan B 2025-11-24 18:27 ` [PATCH i-g-t 2/6] tests/intel/kms_dp_linktrain_fallback: rename to tests/intel/kms_linktrain_fallback Jeevan B ` (4 subsequent siblings) 5 siblings, 0 replies; 9+ messages in thread From: Jeevan B @ 2025-11-24 18:27 UTC (permalink / raw) To: igt-dev; +Cc: kunal1.joshi, suraj.kandpal, Imre Deak, Arun R Murthy, Jeevan B From: Kunal Joshi <kunal1.joshi@intel.com> Renames kms_dp_link_training -> kms_link_training and update meson build for compilation Cc: Imre Deak <imre.deak@intel.com> Cc: Arun R Murthy <arun.r.murthy@intel.com> Signed-off-by: Kunal Joshi <kunal1.joshi@intel.com> Reviewed-by: Jeevan B <jeevan.b@intel.com> --- .../intel/{kms_dp_link_training.c => kms_link_training.c} | 0 tests/meson.build | 8 ++++---- 2 files changed, 4 insertions(+), 4 deletions(-) rename tests/intel/{kms_dp_link_training.c => kms_link_training.c} (100%) diff --git a/tests/intel/kms_dp_link_training.c b/tests/intel/kms_link_training.c similarity index 100% rename from tests/intel/kms_dp_link_training.c rename to tests/intel/kms_link_training.c diff --git a/tests/meson.build b/tests/meson.build index ecc0f4c7f..24675e61e 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -249,7 +249,6 @@ intel_kms_progs = [ 'kms_ccs', 'kms_cdclk', 'kms_dirtyfb', - 'kms_dp_link_training', 'kms_dp_linktrain_fallback', 'kms_draw_crc', 'kms_dsc', @@ -262,6 +261,7 @@ intel_kms_progs = [ 'kms_frontbuffer_tracking', 'kms_joiner', 'kms_legacy_colorkey', + 'kms_link_training', 'kms_mmap_write_crc', 'kms_pipe_b_c_ivb', 'kms_pipe_stress', @@ -386,11 +386,11 @@ extra_sources = { 'kms_dp_linktrain_fallback': [ join_paths ('intel', 'kms_mst_helper.c'), join_paths ('intel', 'kms_dsc_helper.c') ], - 'kms_dp_link_training': [ - join_paths ('intel', 'kms_mst_helper.c'), - join_paths ('intel', 'kms_joiner_helper.c') ], 'kms_dsc': [ join_paths ('intel', 'kms_dsc_helper.c') ], 'kms_joiner': [ join_paths ('intel', 'kms_joiner_helper.c') ], + 'kms_link_training': [ + join_paths ('intel', 'kms_mst_helper.c'), + join_paths ('intel', 'kms_joiner_helper.c') ], 'kms_psr2_sf': [ join_paths ('intel', 'kms_dsc_helper.c') ], } -- 2.43.0 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH i-g-t 2/6] tests/intel/kms_dp_linktrain_fallback: rename to tests/intel/kms_linktrain_fallback 2025-11-24 18:27 [PATCH i-g-t 0/6] RFC: Add new test for eDP data override Jeevan B 2025-11-24 18:27 ` [PATCH i-g-t 1/6] tests/intel/kms_dp_link_training: rename to tests/intel/kms_link_training Jeevan B @ 2025-11-24 18:27 ` Jeevan B 2025-11-24 18:27 ` [PATCH i-g-t 3/6] lib/igt_kms: add helpers for connector type Jeevan B ` (3 subsequent siblings) 5 siblings, 0 replies; 9+ messages in thread From: Jeevan B @ 2025-11-24 18:27 UTC (permalink / raw) To: igt-dev; +Cc: kunal1.joshi, suraj.kandpal, Imre Deak, Arun R Murthy, Jeevan B From: Kunal Joshi <kunal1.joshi@intel.com> rename kms_dp_linktrain_fallback -> kms_linktrain_fallback and update meson build for compilation Cc: Imre Deak <imre.deak@intel.com> Cc: Arun R Murthy <arun.r.murthy@intel.com> Signed-off-by: Kunal Joshi <kunal1.joshi@intel.com> Reviewed-by: Jeevan B <jeevan.b@intel.com> --- tests/intel/kms_dp_linktrain_fallback.c | 639 ------------------------ tests/meson.build | 8 +- 2 files changed, 4 insertions(+), 643 deletions(-) delete mode 100644 tests/intel/kms_dp_linktrain_fallback.c diff --git a/tests/intel/kms_dp_linktrain_fallback.c b/tests/intel/kms_dp_linktrain_fallback.c deleted file mode 100644 index 4d6740883..000000000 --- a/tests/intel/kms_dp_linktrain_fallback.c +++ /dev/null @@ -1,639 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -/* - * Copyright © 2024 Intel Corporation - */ - -/** - * TEST: kms dp linktrain fallback - * Category: Display - * Description: Test link training fallback for eDP/DP connectors - * Driver requirement: i915, xe - * Mega feature: General Display Features - */ - -#include <sys/types.h> -#include "igt_sysfs.h" -#include "igt.h" -#include "kms_mst_helper.h" -#include "kms_dsc_helper.h" - -/** - * SUBTEST: dp-fallback - * Description: Test fallback on DP connectors - * - * SUBTEST: dsc-fallback - * Description: Test fallback to DSC when BW isn't sufficient - */ - -#define RETRAIN_COUNT 1 -/* - * Two consecutives link training failures - * reduces link params (link rate, lane count) - */ -#define LT_FAILURE_REDUCED_CAPS 2 -#define SPURIOUS_HPD_RETRY 3 - -static int traversed_mst_outputs[IGT_MAX_PIPES]; -static int traversed_mst_output_count; -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; - int n_pipes; -} data_t; - -typedef int (*condition_check_fn)(int drm_fd, igt_output_t *output); - -IGT_TEST_DESCRIPTION("Test link-training / dsc fallback"); - -static bool setup_mst_outputs(data_t *data, igt_output_t *mst_output[], - int *output_count) -{ - int i; - igt_output_t *output; - - /* - * Check if this is already traversed - */ - for (i = 0; i < traversed_mst_output_count; i++) - if (i < IGT_MAX_PIPES && - traversed_mst_outputs[i] == data->output->config.connector->connector_id) - return false; - - igt_assert_f(igt_find_all_mst_output_in_topology(data->drm_fd, &data->display, - data->output, mst_output, - output_count) == 0, - "Unable to find mst outputs or given output is not mst\n"); - - for (i = 0; i < *output_count; i++) { - output = mst_output[i]; - if (traversed_mst_output_count < IGT_MAX_PIPES) { - traversed_mst_outputs[traversed_mst_output_count++] = output->config.connector->connector_id; - igt_info("Output %s is in same topology as %s\n", - igt_output_name(output), - igt_output_name(data->output)); - } else { - igt_assert_f(false, "Unable to save traversed output\n"); - return false; - } - } - return true; -} - -static void setup_pipe_on_outputs(data_t *data, - igt_output_t *outputs[], - int *output_count) -{ - int i = 0; - - igt_require_f(data->n_pipes >= *output_count, - "Need %d pipes to assign to %d outputs\n", - data->n_pipes, *output_count); - - for_each_pipe(&data->display, data->pipe) { - if (i >= *output_count) - break; - /* - * TODO: add support for modes requiring joined pipes - */ - igt_info("Setting pipe %s on output %s\n", - kmstest_pipe_name(data->pipe), - igt_output_name(outputs[i])); - igt_output_set_pipe(outputs[i++], data->pipe); - } -} - -static void setup_modeset_on_outputs(data_t *data, - igt_output_t *outputs[], - int *output_count, - drmModeModeInfo *mode[], - struct igt_fb fb[], - struct igt_plane *primary[]) -{ - int i; - - for (i = 0; i < *output_count; i++) { - mode[i] = igt_output_get_mode(outputs[i]); - igt_info("Mode %dx%d@%d on output %s\n", - mode[i]->hdisplay, mode[i]->vdisplay, - mode[i]->vrefresh, - igt_output_name(outputs[i])); - primary[i] = igt_output_get_plane_type(outputs[i], - DRM_PLANE_TYPE_PRIMARY); - igt_create_color_fb(data->drm_fd, - mode[i]->hdisplay, - mode[i]->vdisplay, - DRM_FORMAT_XRGB8888, - DRM_FORMAT_MOD_LINEAR, 0.0, 1.0, 0.0, - &fb[i]); - igt_plane_set_fb(primary[i], &fb[i]); - } -} - -static void set_connector_link_status_good(data_t *data, igt_output_t *outputs[], - int *output_count) -{ - int i; - igt_output_t *output; - - /* - * update the link status to good for all outputs - */ - for_each_connected_output(&data->display, output) - for(i = 0; i < *output_count; i++) - if (output->id == outputs[i]->id) - igt_output_set_prop_value(output, - IGT_CONNECTOR_LINK_STATUS, - DRM_MODE_LINK_STATUS_GOOD); -} - -static bool validate_modeset_for_outputs(data_t *data, - igt_output_t *outputs[], - int *output_count, - drmModeModeInfo *mode[], - struct igt_fb fb[], - struct igt_plane *primary[]) -{ - igt_require_f(*output_count > 0, "Require at least 1 output\n"); - setup_pipe_on_outputs(data, outputs, output_count); - igt_assert_f(igt_fit_modes_in_bw(&data->display), "Unable to fit modes in bw\n"); - setup_modeset_on_outputs(data, outputs, - output_count, - mode, fb, primary); - return true; -} - -static bool setup_outputs(data_t *data, bool is_mst, - igt_output_t *outputs[], - int *output_count, drmModeModeInfo *mode[], - struct igt_fb fb[], struct igt_plane *primary[]) -{ - bool ret; - - *output_count = 0; - - if (is_mst) { - ret = setup_mst_outputs(data, outputs, output_count); - if (!ret) { - igt_info("Skipping MST output %s as already tested\n", - igt_output_name(data->output)); - return false; - } - } else - if ((*output_count) < IGT_MAX_PIPES) - outputs[(*output_count)++] = data->output; - - ret = validate_modeset_for_outputs(data, outputs, - output_count, mode, - fb, primary); - - if (!ret) { - igt_info("Skipping output %s as valid pipe/output combo not found\n", - igt_output_name(data->output)); - return false; - } - - igt_display_commit2(&data->display, COMMIT_ATOMIC); - return true; -} - -static int check_condition_with_timeout(int drm_fd, igt_output_t *output, - condition_check_fn check_fn, - double interval, double timeout) -{ - struct timespec start_time, current_time; - double elapsed_time; - - clock_gettime(CLOCK_MONOTONIC, &start_time); - - while (1) { - if (check_fn(drm_fd, output) == 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 * 1000000)); - } -} - -/* - * Force a link training failure followed by link retrain, then - * block until the driver has no further pending retrain/failure. - * Returns false if we time out waiting. - */ -static bool force_failure_and_wait(data_t *data, - igt_output_t *output, - int failure_type, - int retrain_count, - double interval, - double timeout) -{ - igt_force_lt_failure(data->drm_fd, output, failure_type); - igt_force_link_retrain(data->drm_fd, output, retrain_count); - - /* Wait until there's no pending retrain */ - if (check_condition_with_timeout(data->drm_fd, output, - igt_get_dp_pending_retrain, - interval, timeout)) { - igt_info("Timed out waiting for pending retrain\n"); - return false; - } - - /* Wait until there's no pending LT failures */ - if (check_condition_with_timeout(data->drm_fd, output, - igt_get_dp_pending_lt_failures, - interval, timeout)) { - igt_info("Timed out waiting for pending LT failures\n"); - return false; - } - - return true; -} - -/* - * Waits for a hotplug event, then checks that the link-status is BAD. - * Returns false if the link-status isn't BAD or no hotplug arrives in time. - */ -static bool wait_for_hotplug_and_check_bad(int drm_fd, - data_t *data, - igt_output_t *output, - struct udev_monitor *mon, - double hotplug_timeout) -{ - uint32_t link_status_prop_id; - uint64_t link_status_value; - drmModePropertyPtr link_status_prop; - - if (!igt_hotplug_detected(mon, hotplug_timeout)) { - igt_info("No hotplug event within %.2f seconds.\n", hotplug_timeout); - return false; - } - - kmstest_get_property(drm_fd, - output->config.connector->connector_id, - DRM_MODE_OBJECT_CONNECTOR, - "link-status", - &link_status_prop_id, &link_status_value, - &link_status_prop); - - if (link_status_value != DRM_MODE_LINK_STATUS_BAD) { - igt_info("Expected link-status=BAD but got %" PRIu64 "\n", - link_status_value); - return false; - } - - return true; -} - -/* - * Sets link status=GOOD for the specified outputs, then calls - * validate_modeset_for_outputs() to re-commit. Returns false - * if the re-commit fails. - */ -static bool fix_link_status_and_recommit(data_t *data, - igt_output_t *outputs[], - int *output_count, - drmModeModeInfo * modes[], - struct igt_fb fbs[], - struct igt_plane *primaries[]) -{ - int i; - igt_output_t *out; - - /* Set link-status=GOOD on each tested output */ - for_each_connected_output(&data->display, out) { - for (i = 0; i < *output_count; i++) { - if (out->id == outputs[i]->id) { - igt_output_set_prop_value( - out, IGT_CONNECTOR_LINK_STATUS, - DRM_MODE_LINK_STATUS_GOOD); - } - } - } - - if (!validate_modeset_for_outputs(data, outputs, output_count, - modes, fbs, primaries)) { - igt_info("Modeset validation failed after forcing link-status=GOOD\n"); - return false; - } - - if (igt_display_try_commit_atomic(&data->display, - DRM_MODE_ATOMIC_ALLOW_MODESET, - NULL) != 0) { - igt_info("Commit failed after restoring link-status=GOOD\n"); - return false; - } - - return true; -} - -static void test_fallback(data_t *data, bool is_mst) -{ - int output_count, retries; - int max_link_rate, curr_link_rate, prev_link_rate; - int max_lane_count, curr_lane_count, prev_lane_count; - igt_output_t *outputs[IGT_MAX_PIPES]; - drmModeModeInfo * modes[IGT_MAX_PIPES]; - struct igt_fb fbs[IGT_MAX_PIPES]; - struct igt_plane *primaries[IGT_MAX_PIPES]; - struct udev_monitor *mon; - - retries = SPURIOUS_HPD_RETRY; - - igt_display_reset(&data->display); - igt_reset_link_params(data->drm_fd, data->output); - if (!setup_outputs(data, is_mst, outputs, - &output_count, modes, fbs, - primaries)) - return; - - igt_info("Testing link training fallback on %s\n", - igt_output_name(data->output)); - 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); - prev_link_rate = igt_get_current_link_rate(data->drm_fd, data->output); - prev_lane_count = igt_get_current_lane_count(data->drm_fd, data->output); - - while (!igt_get_dp_link_retrain_disabled(data->drm_fd, - data->output)) { - igt_info("Current link rate: %d, Current lane count: %d\n", - prev_link_rate, - prev_lane_count); - mon = igt_watch_uevents(); - - igt_assert_f(force_failure_and_wait(data, data->output, - LT_FAILURE_REDUCED_CAPS, - RETRAIN_COUNT, - 1.0, 20.0), - "Link training failure steps timed out\n"); - - if (igt_get_dp_link_retrain_disabled(data->drm_fd, - data->output)) { - igt_reset_connectors(); - return; - } - - igt_assert_f(wait_for_hotplug_and_check_bad(data->drm_fd, - data, - data->output, - mon, - 20.0), "Didn't get hotplug or link status != BAD\n"); - - igt_flush_uevents(mon); - set_connector_link_status_good(data, outputs, &output_count); - igt_assert_f(fix_link_status_and_recommit(data, - outputs, - &output_count, - modes, - fbs, - primaries), "modeset failed\n"); - igt_assert_eq(data->output->values[IGT_CONNECTOR_LINK_STATUS], DRM_MODE_LINK_STATUS_GOOD); - curr_link_rate = igt_get_current_link_rate(data->drm_fd, data->output); - curr_lane_count = igt_get_current_lane_count(data->drm_fd, data->output); - - igt_debug("Fallback state: prev %dx%d, curr %dx%d, max %dx%d, retries=%u\n", - prev_link_rate, prev_lane_count, - curr_link_rate, curr_lane_count, - max_link_rate, max_lane_count, - retries); - igt_assert_f((curr_link_rate < prev_link_rate || - curr_lane_count < prev_lane_count) || - ((curr_link_rate == max_link_rate && curr_lane_count == max_lane_count) && --retries), - "Fallback unsuccessful\n"); - - prev_link_rate = curr_link_rate; - prev_lane_count = curr_lane_count; - } -} - -static bool run_lt_fallback_test(data_t *data) -{ - bool ran = false; - igt_output_t *output; - - for_each_connected_output(&data->display, output) { - data->output = output; - - if (!igt_has_force_link_training_failure_debugfs(data->drm_fd, - data->output)) { - igt_info("Output %s doesn't support forcing link training failure\n", - igt_output_name(data->output)); - continue; - } - - if (output->config.connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort) { - igt_info("Skipping output %s as it's not DP\n", output->name); - continue; - } - - ran = true; - - /* - * Check output is MST - */ - if (igt_check_output_is_dp_mst(data->output)) { - igt_info("Testing MST output %s\n", - igt_output_name(data->output)); - test_fallback(data, true); - } else { - igt_info("Testing DP output %s\n", - igt_output_name(data->output)); - test_fallback(data, false); - } - } - return ran; -} - -static void test_dsc_sst_fallback(data_t *data) -{ - bool non_dsc_mode_found = false; - bool dsc_fallback_successful = false; - int ret; - struct udev_monitor *mon; - drmModeModeInfo *mode_to_check; - igt_output_t *outputs[IGT_MAX_PIPES]; - int output_count = 0; - - igt_info("Checking DSC fallback on %s\n", igt_output_name(data->output)); - data->pipe = PIPE_A; - - igt_display_reset(&data->display); - igt_reset_link_params(data->drm_fd, data->output); - igt_force_link_retrain(data->drm_fd, data->output, RETRAIN_COUNT); - - /* Find a mode that doesn't require DSC initially */ - for_each_connector_mode(data->output) { - data->mode = &data->output->config.connector->modes[j__]; - igt_create_color_fb(data->drm_fd, data->mode->hdisplay, - data->mode->vdisplay, DRM_FORMAT_XRGB8888, - DRM_FORMAT_MOD_LINEAR, 0.0, 1.0, 0.0, - &data->fb); - igt_output_override_mode(data->output, data->mode); - 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); - - ret = igt_display_try_commit_atomic(&data->display, - DRM_MODE_ATOMIC_TEST_ONLY | - DRM_MODE_ATOMIC_ALLOW_MODESET, - NULL); - if (ret != 0) { - igt_debug("Skipping mode %dx%d@%d on %s\n", - data->mode->hdisplay, data->mode->vdisplay, - data->mode->vrefresh, - igt_output_name(data->output)); - continue; - } - igt_display_commit2(&data->display, COMMIT_ATOMIC); - - if (!igt_is_dsc_enabled(data->drm_fd, - data->output->name)) { - drmModeModeInfo *non_dsc_mode - = igt_output_get_mode(data->output); - igt_info("Found mode %dx%d@%d %s that doesn't need DSC with link rate %d and lane count %d\n", - non_dsc_mode->hdisplay, non_dsc_mode->vdisplay, - non_dsc_mode->vrefresh, non_dsc_mode->name, - igt_get_current_link_rate(data->drm_fd, data->output), - igt_get_current_lane_count(data->drm_fd, data->output)); - non_dsc_mode_found = true; - break; - } - } - igt_require_f(non_dsc_mode_found, - "No non-DSC mode found on %s\n", - igt_output_name(data->output)); - - /* Repeatedly force link failure until DSC is required (or link is disabled) */ - while (!igt_get_dp_link_retrain_disabled(data->drm_fd, data->output)) { - mon = igt_watch_uevents(); - - igt_assert_f(force_failure_and_wait(data, data->output, - LT_FAILURE_REDUCED_CAPS, - RETRAIN_COUNT, 1.0, 20.0), - "Forcing DSC fallback timed out\n"); - - if (igt_get_dp_link_retrain_disabled(data->drm_fd, - data->output)) { - igt_reset_connectors(); - igt_flush_uevents(mon); - return; - } - - igt_assert_f(wait_for_hotplug_and_check_bad(data->drm_fd, - data, - data->output, - mon, - 20.0), - "Didn't get hotplug or link-status=BAD for DSC\n"); - igt_flush_uevents(mon); - - outputs[output_count++] = data->output; - set_connector_link_status_good(data, outputs, &output_count); - igt_display_commit2(&data->display, COMMIT_ATOMIC); - - mode_to_check = igt_output_get_mode(data->output); - - if (igt_is_dsc_enabled(data->drm_fd, data->output->name)) { - igt_info("mode %dx%d@%d now requires DSC with link rate %d and lane count %d\n", - mode_to_check->hdisplay, mode_to_check->vdisplay, - mode_to_check->vrefresh, - igt_get_current_link_rate(data->drm_fd, data->output), - igt_get_current_lane_count(data->drm_fd, data->output)); - igt_info("DSC fallback successful on %s\n", - igt_output_name(data->output)); - dsc_fallback_successful = true; - break; - } else { - igt_info("mode %dx%d@%d still doesn't require DSC\n", - mode_to_check->hdisplay, mode_to_check->vdisplay, - mode_to_check->vrefresh); - } - } - igt_assert_f(dsc_fallback_successful, "DSC fallback unsuccessful\n"); -} - -static bool run_dsc_sst_fallaback_test(data_t *data) -{ - bool ran = false; - igt_output_t *output; - - if (!is_dsc_supported_by_source(data->drm_fd)) { - igt_info("DSC not supported by source.\n"); - return ran; - } - - for_each_connected_output(&data->display, output) { - data->output = output; - - if (!igt_has_force_link_training_failure_debugfs(data->drm_fd, - data->output)) { - igt_info("Output %s doesn't support forcing link training.\n", - igt_output_name(data->output)); - continue; - } - - if (output->config.connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort) { - igt_info("Skipping output %s as it's not DP\n", output->name); - continue; - } - - if (!is_dsc_supported_by_sink(data->drm_fd, data->output)) - continue; - - ran = true; - test_dsc_sst_fallback(data); - } - - return ran; -} - -igt_main -{ - data_t data = {}; - - igt_fixture { - unsigned int debug_mask_if_ci = DRM_UT_KMS; - data.drm_fd = drm_open_driver_master(DRIVER_INTEL | - DRIVER_XE); - kmstest_set_vt_graphics_mode(); - igt_display_require(&data.display, data.drm_fd); - igt_display_require_output(&data.display); - for_each_pipe(&data.display, data.pipe) - data.n_pipes++; - igt_install_exit_handler(igt_drm_debug_mask_reset_exit_handler); - update_debug_mask_if_ci(debug_mask_if_ci); - - /* - * Some environments may have environment - * variable set to ignore long hpd, disable it for this test - */ - igt_assert_f(igt_ignore_long_hpd(data.drm_fd, false), - "Unable to disable ignore long hpd\n"); - } - - igt_subtest("dp-fallback") { - igt_require_f(run_lt_fallback_test(&data), - "Skipping test as no output found or none supports fallback\n"); - } - - igt_subtest("dsc-fallback") { - igt_require_f(run_dsc_sst_fallaback_test(&data), - "Skipping test as DSC fallback conditions not met.\n"); - } - - igt_fixture { - igt_remove_fb(data.drm_fd, &data.fb); - igt_display_fini(&data.display); - close(data.drm_fd); - } -} diff --git a/tests/meson.build b/tests/meson.build index 24675e61e..e004036c8 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -249,7 +249,6 @@ intel_kms_progs = [ 'kms_ccs', 'kms_cdclk', 'kms_dirtyfb', - 'kms_dp_linktrain_fallback', 'kms_draw_crc', 'kms_dsc', 'kms_fb_coherency', @@ -262,6 +261,7 @@ intel_kms_progs = [ 'kms_joiner', 'kms_legacy_colorkey', 'kms_link_training', + 'kms_linktrain_fallback', 'kms_mmap_write_crc', 'kms_pipe_b_c_ivb', 'kms_pipe_stress', @@ -383,14 +383,14 @@ extra_sources = { 'kms_chamelium_frames': [ join_paths ('chamelium', 'kms_chamelium_helper.c') ], 'kms_chamelium_hpd': [ join_paths ('chamelium', 'kms_chamelium_helper.c') ], 'kms_chamelium_sharpness_filter': [ join_paths ('chamelium', 'kms_chamelium_helper.c') ], - 'kms_dp_linktrain_fallback': [ - join_paths ('intel', 'kms_mst_helper.c'), - join_paths ('intel', 'kms_dsc_helper.c') ], 'kms_dsc': [ join_paths ('intel', 'kms_dsc_helper.c') ], 'kms_joiner': [ join_paths ('intel', 'kms_joiner_helper.c') ], 'kms_link_training': [ join_paths ('intel', 'kms_mst_helper.c'), join_paths ('intel', 'kms_joiner_helper.c') ], + 'kms_linktrain_fallback': [ + join_paths ('intel', 'kms_mst_helper.c'), + join_paths ('intel', 'kms_dsc_helper.c') ], 'kms_psr2_sf': [ join_paths ('intel', 'kms_dsc_helper.c') ], } -- 2.43.0 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH i-g-t 3/6] lib/igt_kms: add helpers for connector type 2025-11-24 18:27 [PATCH i-g-t 0/6] RFC: Add new test for eDP data override Jeevan B 2025-11-24 18:27 ` [PATCH i-g-t 1/6] tests/intel/kms_dp_link_training: rename to tests/intel/kms_link_training Jeevan B 2025-11-24 18:27 ` [PATCH i-g-t 2/6] tests/intel/kms_dp_linktrain_fallback: rename to tests/intel/kms_linktrain_fallback Jeevan B @ 2025-11-24 18:27 ` Jeevan B 2025-11-24 18:27 ` [PATCH i-g-t 4/6] tests/intel/kms_link_training: extend test for eDP connector Jeevan B ` (2 subsequent siblings) 5 siblings, 0 replies; 9+ messages in thread From: Jeevan B @ 2025-11-24 18:27 UTC (permalink / raw) To: igt-dev; +Cc: kunal1.joshi, suraj.kandpal From: Kunal Joshi <kunal1.joshi@intel.com> Introduce helpers for checking output connector type, reuse same mechanism for output_is_internal_panel. Signed-off-by: Kunal Joshi <kunal1.joshi@intel.com> --- lib/igt_kms.c | 74 ++++++++++++++++++++++++++++------ lib/igt_kms.h | 9 ++++- tests/intel/kms_dsc_helper.c | 2 +- tests/intel/kms_pm_backlight.c | 2 +- tests/kms_atomic_transition.c | 4 +- tests/kms_hdr.c | 2 +- 6 files changed, 74 insertions(+), 19 deletions(-) diff --git a/lib/igt_kms.c b/lib/igt_kms.c index 8973ffdb2..c03cfed09 100644 --- a/lib/igt_kms.c +++ b/lib/igt_kms.c @@ -3501,22 +3501,70 @@ igt_plane_t *igt_pipe_get_plane_type_index(igt_pipe_t *pipe, int plane_type, } /** - * output_is_internal_panel: + * igt_output_type_in_mask - test connector type membership + * @output: output (may be NULL) + * @mask: bitmask where bit N corresponds to DRM_MODE_CONNECTOR_* value N + * + * Returns: true if @output has a connector and its connector_type bit is set + * in @mask. Safe for DRM_MODE_CONNECTOR_Unknown (0). If @output or its + * connector is NULL, returns false. + */ +bool igt_output_type_in_mask(igt_output_t *output, uint64_t mask) +{ + unsigned int type; + + if (!output || !output->config.connector) + return false; + + type = output->config.connector->connector_type; + + return (mask & CONNECTOR_TYPE_BIT(type)) != 0; +} + +/** + * igt_output_is_dp_family - classify DP/eDP as DP family + * @output: output + * + * Returns: true if @output is DisplayPort or eDP. + */ +bool igt_output_is_dp_family(igt_output_t *output) +{ + uint64_t mask = + CONNECTOR_TYPE_BIT(DRM_MODE_CONNECTOR_DisplayPort) | + CONNECTOR_TYPE_BIT(DRM_MODE_CONNECTOR_eDP); + + return igt_output_type_in_mask(output, mask); +} + +/** + * igt_output_is_hdmi - classify HDMI connectors + * @output: output + * + * Returns: true if @output is HDMI (A or B). + */ +bool igt_output_is_hdmi(igt_output_t *output) +{ + uint64_t mask = + CONNECTOR_TYPE_BIT(DRM_MODE_CONNECTOR_HDMIA) | + CONNECTOR_TYPE_BIT(DRM_MODE_CONNECTOR_HDMIB); + + return igt_output_type_in_mask(output, mask); +} + +/** + * igt_output_is_internal_panel: * @output: Target output * * Returns: True if the given @output type is internal else False. */ -bool output_is_internal_panel(igt_output_t *output) +bool igt_output_is_internal_panel(igt_output_t *output) { - switch (output->config.connector->connector_type) { - case DRM_MODE_CONNECTOR_LVDS: - case DRM_MODE_CONNECTOR_eDP: - case DRM_MODE_CONNECTOR_DSI: - case DRM_MODE_CONNECTOR_DPI: - return true; - default: - return false; - } + uint64_t mask = CONNECTOR_TYPE_BIT(DRM_MODE_CONNECTOR_LVDS) | + CONNECTOR_TYPE_BIT(DRM_MODE_CONNECTOR_eDP) | + CONNECTOR_TYPE_BIT(DRM_MODE_CONNECTOR_DSI) | + CONNECTOR_TYPE_BIT(DRM_MODE_CONNECTOR_DPI); + + return igt_output_type_in_mask(output, mask); } igt_output_t **__igt_pipe_populate_outputs(igt_display_t *display, igt_output_t **chosen_outputs) @@ -3543,7 +3591,7 @@ igt_output_t **__igt_pipe_populate_outputs(igt_display_t *display, igt_output_t uint32_t pipe_mask = output->config.valid_crtc_idx_mask & full_pipe_mask; bool found = false; - if (output_is_internal_panel(output)) { + if (igt_output_is_internal_panel(output)) { /* * Internal panel should be assigned to pipe A * if possible, so make sure they're enumerated @@ -3571,7 +3619,7 @@ igt_output_t **__igt_pipe_populate_outputs(igt_display_t *display, igt_output_t * Overwrite internal panel if not assigned, * external outputs are faster to do modesets */ - output_is_internal_panel(chosen_outputs[j])) + igt_output_is_internal_panel(chosen_outputs[j])) chosen_outputs[j] = output; } diff --git a/lib/igt_kms.h b/lib/igt_kms.h index f7ff0b17e..cd9f6840a 100644 --- a/lib/igt_kms.h +++ b/lib/igt_kms.h @@ -578,7 +578,14 @@ igt_plane_t *igt_pipe_get_plane_type(igt_pipe_t *pipe, int plane_type); int igt_pipe_count_plane_type(igt_pipe_t *pipe, int plane_type); igt_plane_t *igt_pipe_get_plane_type_index(igt_pipe_t *pipe, int plane_type, int index); -bool output_is_internal_panel(igt_output_t *output); +#ifndef CONNECTOR_TYPE_BIT +#define CONNECTOR_TYPE_BIT(e) (1ULL << (e)) +#endif + +bool igt_output_type_in_mask(igt_output_t *output, uint64_t mask); +bool igt_output_is_dp_family(igt_output_t *output); +bool igt_output_is_hdmi(igt_output_t *output); +bool igt_output_is_internal_panel(igt_output_t *output); igt_output_t *igt_get_single_output_for_pipe(igt_display_t *display, enum pipe pipe); void igt_pipe_request_out_fence(igt_pipe_t *pipe); diff --git a/tests/intel/kms_dsc_helper.c b/tests/intel/kms_dsc_helper.c index cea4304e4..4f23c39af 100644 --- a/tests/intel/kms_dsc_helper.c +++ b/tests/intel/kms_dsc_helper.c @@ -74,7 +74,7 @@ bool is_dsc_supported_by_sink(int drmfd, igt_output_t *output) return false; } - if (!output_is_internal_panel(output) && + if (!igt_output_is_internal_panel(output) && !igt_is_fec_supported(drmfd, output->name)) { igt_info("DSC cannot be enabled without FEC on %s\n", output->name); diff --git a/tests/intel/kms_pm_backlight.c b/tests/intel/kms_pm_backlight.c index a0ecf2b0e..27c31d695 100644 --- a/tests/intel/kms_pm_backlight.c +++ b/tests/intel/kms_pm_backlight.c @@ -239,7 +239,7 @@ igt_main igt_display_require(&display, drm_open_driver(DRIVER_INTEL | DRIVER_XE)); for_each_connected_output(&display, output) { - if (!output_is_internal_panel(output)) + if (!igt_output_is_internal_panel(output)) continue; if (found) diff --git a/tests/kms_atomic_transition.c b/tests/kms_atomic_transition.c index 419afe4dd..f97fc000d 100644 --- a/tests/kms_atomic_transition.c +++ b/tests/kms_atomic_transition.c @@ -1225,11 +1225,11 @@ igt_main_args("", long_opts, help_str, opt_handler, &data) * panels with long power cycle delays. */ if ((transition_tests[i].type == TRANSITION_MODESET) && - output_is_internal_panel(output)) + igt_output_is_internal_panel(output)) continue; if ((transition_tests[i].type == TRANSITION_MODESET_FAST) && - !output_is_internal_panel(output)) + !igt_output_is_internal_panel(output)) continue; if (pipe_count == 2 * count && !data.extended) diff --git a/tests/kms_hdr.c b/tests/kms_hdr.c index 40187275b..3a931ac9e 100644 --- a/tests/kms_hdr.c +++ b/tests/kms_hdr.c @@ -734,7 +734,7 @@ static void test_hdr(data_t *data, uint32_t flags) continue; } - if ((flags & TEST_BRIGHTNESS) && !output_is_internal_panel(output)) { + if ((flags & TEST_BRIGHTNESS) && !igt_output_is_internal_panel(output)) { igt_info("%s: Can't run brightness test on non-internal panel.\n", igt_output_name(output)); continue; -- 2.43.0 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH i-g-t 4/6] tests/intel/kms_link_training: extend test for eDP connector 2025-11-24 18:27 [PATCH i-g-t 0/6] RFC: Add new test for eDP data override Jeevan B ` (2 preceding siblings ...) 2025-11-24 18:27 ` [PATCH i-g-t 3/6] lib/igt_kms: add helpers for connector type Jeevan B @ 2025-11-24 18:27 ` Jeevan B 2025-11-24 18:27 ` [PATCH i-g-t 5/6] lib/igt_kms: Add helper to get eDP/DP supported link rates Jeevan B 2025-11-24 18:27 ` [PATCH i-g-t 6/6] RFC: tests/intel/kms_link_training: Add edp-data-override subtest Jeevan B 5 siblings, 0 replies; 9+ messages in thread From: Jeevan B @ 2025-11-24 18:27 UTC (permalink / raw) To: igt-dev; +Cc: kunal1.joshi, suraj.kandpal, Imre Deak, Arun R Murthy, Jeevan B From: Kunal Joshi <kunal1.joshi@intel.com> Extend kms_link_training test for eDP connector. non-uhbr-sst subtest expected to and skip expected for rest of the subtest as of now. v2: use helper for eDP/DP check (Jeevan) Cc: Imre Deak <imre.deak@intel.com> Cc: Arun R Murthy <arun.r.murthy@intel.com> Signed-off-by: Kunal Joshi <kunal1.joshi@intel.com> Reviewed-by: Jeevan B <jeevan.b@intel.com> --- tests/intel/kms_link_training.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/intel/kms_link_training.c b/tests/intel/kms_link_training.c index fb9b42608..4ddbd72f8 100644 --- a/tests/intel/kms_link_training.c +++ b/tests/intel/kms_link_training.c @@ -266,10 +266,8 @@ static bool test_link_rate(data_t *data, bool mst, bool uhbr) "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); + if (!igt_output_is_dp_family(tmp_output)) { + igt_info("Skipping non-DisplayPort output %s\n", tmp_output->name); igt_info("----------------------------------------------------\n"); continue; } -- 2.43.0 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH i-g-t 5/6] lib/igt_kms: Add helper to get eDP/DP supported link rates 2025-11-24 18:27 [PATCH i-g-t 0/6] RFC: Add new test for eDP data override Jeevan B ` (3 preceding siblings ...) 2025-11-24 18:27 ` [PATCH i-g-t 4/6] tests/intel/kms_link_training: extend test for eDP connector Jeevan B @ 2025-11-24 18:27 ` Jeevan B 2025-11-24 18:27 ` [PATCH i-g-t 6/6] RFC: tests/intel/kms_link_training: Add edp-data-override subtest Jeevan B 5 siblings, 0 replies; 9+ messages in thread From: Jeevan B @ 2025-11-24 18:27 UTC (permalink / raw) To: igt-dev; +Cc: kunal1.joshi, suraj.kandpal, Jeevan B Add igt_get_supported_link_rates to parse debugfs and return available eDP/DP link rates for a given connector. Signed-off-by: Jeevan B <jeevan.b@intel.com> --- lib/igt_kms.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++ lib/igt_kms.h | 2 ++ 2 files changed, 54 insertions(+) diff --git a/lib/igt_kms.c b/lib/igt_kms.c index c03cfed09..4071aa00a 100644 --- a/lib/igt_kms.c +++ b/lib/igt_kms.c @@ -7632,3 +7632,55 @@ bool igt_has_lobf_debugfs(int drmfd, igt_output_t *output) buf, sizeof(buf)); return res == 0; } + +/** + * igt_get_supported_link_rates: + * @drmfd: A drm file descriptor + * @connector_name: Name of the libdrm connector we're going to use + * + * Returns: All the link rates supported by the connector + */ +int igt_get_supported_link_rates(int drmfd, char *connector_name, + int *rates, int max_rates) +{ + char buf[48]; + int fd, res; + int count = 0; + char *token, *star; + + if (!rates || max_rates <= 0) { + return -1; + } + + fd = igt_debugfs_connector_dir(drmfd, connector_name, O_RDONLY); + igt_assert_lte(0, fd); + + res = igt_debugfs_simple_read(fd, "i915_dp_force_link_rate", buf, sizeof(buf)); + igt_require(res > 0); + + igt_require_f(res > 0, + "Unable to read i915_dp_force_link_rate for %s\n", + connector_name); + + buf[res] = '\0'; /* Null terminate */ + token = strtok(buf, " \t\n"); + + while (token && count < max_rates) { + if (!strcmp(token, "[auto]")) { + token = strtok(NULL, " \t\n"); + continue; + } + + star = strchr(token, '*'); + if (star) + *star = '\0'; + + if (isdigit(token[0])) { + rates[count++] = atoi(token); + } + + token = strtok(NULL, " \t\n"); + } + + return count; +} diff --git a/lib/igt_kms.h b/lib/igt_kms.h index cd9f6840a..474a809ea 100644 --- a/lib/igt_kms.h +++ b/lib/igt_kms.h @@ -1303,6 +1303,8 @@ void igt_set_link_params(int drm_fd, igt_output_t *output, int igt_backlight_read(int *result, const char *fname, igt_backlight_context_t *context); int igt_backlight_write(int value, const char *fname, igt_backlight_context_t *context); uint32_t igt_get_connected_output_count(igt_display_t *display); +int igt_get_supported_link_rates(int drmfd, char *connector_name, int *rates, + int max_rates); drmModePropertyBlobRes *igt_get_writeback_formats_blob(igt_output_t *output); -- 2.43.0 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH i-g-t 6/6] RFC: tests/intel/kms_link_training: Add edp-data-override subtest 2025-11-24 18:27 [PATCH i-g-t 0/6] RFC: Add new test for eDP data override Jeevan B ` (4 preceding siblings ...) 2025-11-24 18:27 ` [PATCH i-g-t 5/6] lib/igt_kms: Add helper to get eDP/DP supported link rates Jeevan B @ 2025-11-24 18:27 ` Jeevan B 5 siblings, 0 replies; 9+ messages in thread From: Jeevan B @ 2025-11-24 18:27 UTC (permalink / raw) To: igt-dev; +Cc: kunal1.joshi, suraj.kandpal, Jeevan B Add test for eDP data override for 2.43 and 6.75 Gbps which has some issues. eDP should overide to 2.7 and 8.1 Gbps when supported by panel. Signed-off-by: Jeevan B <jeevan.b@intel.com> Link: https://lore.kernel.org/r/20250821042653.269227-3-suraj.kandpal@intel.com --- tests/intel/kms_link_training.c | 185 ++++++++++++++++++++++++++++++++ 1 file changed, 185 insertions(+) diff --git a/tests/intel/kms_link_training.c b/tests/intel/kms_link_training.c index 4ddbd72f8..22c4c9fd3 100644 --- a/tests/intel/kms_link_training.c +++ b/tests/intel/kms_link_training.c @@ -19,6 +19,9 @@ * * SUBTEST: non-uhbr-mst * Description: Test we can drive non-UHBR rates over MST. + * + * SUBTEST: edp-data-override + * Description: Test eDP data override rates. */ #include "igt.h" @@ -293,6 +296,180 @@ static bool test_link_rate(data_t *data, bool mst, bool uhbr) return ran_any_output; } +static bool test_link_override(data_t *data, int target_rate, const char *rate_name, + bool expect_success, int timeout_sec) +{ + char rate_str[32]; + char lane_str[32]; + int current_rate; + bool training_completed; + + igt_info("Testing %s (%d kHz) - expect %s...\n", + rate_name, target_rate, expect_success ? "SUCCESS" : "BLOCK/FAIL"); + + igt_display_reset(&data->display); + igt_reset_link_params(data->drm_fd, data->output); + do_modeset(data, false); + + snprintf(rate_str, sizeof(rate_str), "%d", target_rate); + snprintf(lane_str, sizeof(lane_str), "4"); + igt_set_link_params(data->drm_fd, data->output, rate_str, lane_str); + igt_force_link_retrain(data->drm_fd, data->output, RETRAIN_COUNT); + + training_completed = (check_condition_with_timeout(data->drm_fd, data->output, + igt_get_dp_pending_retrain, + 1.0, timeout_sec) == 0); + + if (training_completed) { + current_rate = igt_get_current_link_rate(data->drm_fd, data->output); + assert_link_status_good(data, false); + + if (expect_success) { + if (current_rate == target_rate) { + igt_info("%s succeeded at %d kHz\n", rate_name, current_rate); + return true; + } else { + igt_info("%s failed: requested %d kHz, got %d kHz\n", + rate_name, target_rate, current_rate); + return false; + } + } else { + if (current_rate != target_rate) { + igt_info("%s blocked: requested %d kHz, fell back to %d kHz\n", + rate_name, target_rate, current_rate); + return true; + } else { + igt_info("%s was NOT blocked: got requested %d kHz\n", + rate_name, current_rate); + return false; + } + } + } else { + if (expect_success) { + igt_info("%s failed with timeout\n", rate_name); + return false; + } else { + igt_info("%s completely rejected (timeout)\n", rate_name); + return true; + } + } +} + +static void analyze_supported_rates(int *rates, int num_rates, + bool *found_243, bool *found_675, + bool *found_270, bool *found_810) +{ + int i; + + *found_243 = false; + *found_675 = false; + *found_270 = false; + *found_810 = false; + + for (i = 0; i < num_rates; i++) { + igt_info(" %d kHz (%.2f Gbps)", + rates[i], rates[i] / 100000.0); + + switch (rates[i]) { + case 243000: + *found_243 = true; + igt_info("Unsupported!"); + break; + case 675000: + *found_675 = true; + igt_info("Unsupported!"); + break; + case 270000: + *found_270 = true; + igt_info("Supported"); + break; + case 810000: + *found_810 = true; + igt_info("Supported"); + break; + } + igt_info("\n"); + } +} + +static bool test_edp_data_override(data_t *data) +{ + igt_output_t *edp_output = NULL; + int supported_rates[16]; + int num_rates = 0; + bool found_243, found_675, found_270, found_810; + bool test_results[4] = {false}; + bool data_override_works, safe_rates_available; + bool all_tests_passed; + + igt_skip_on_f(!is_intel_device(data->drm_fd), + "Test supported only on Intel platforms.\n"); + + for_each_connected_output(&data->display, edp_output) { + if (igt_output_is_dp_family(edp_output) && + strstr(edp_output->name, "eDP")) { + data->output = edp_output; + break; + } + } + + igt_require_f(data->output != NULL, "No eDP output found\n"); + igt_info("Testing eDP data override on: %s\n", data->output->name); + + num_rates = igt_get_supported_link_rates(data->drm_fd, data->output->name, + supported_rates, 16); + igt_assert_f(num_rates >= 2 && num_rates <= 10, + "Unexpected number of rates (%d), data override might be broken\n", num_rates); + + analyze_supported_rates(supported_rates, num_rates, + &found_243, &found_675, &found_270, &found_810); + + igt_info("PHASE 1: Testing Unsupported rates (expect override/blocking)\n"); + + if (found_243) + test_results[0] = test_link_override(data, 243000, "2.43 Gbps Override rate", + false, 10); + + if (found_675) + test_results[1] = test_link_override(data, 675000, "6.75 Gbps Override rate", + false, 10); + + igt_info("PHASE 2: Testing Supported rates\n"); + + if (found_270) { + test_results[2] = test_link_override(data, 270000, "2.7 Gbps data rate", + true, 10); + } else { + igt_info("Skipping 2.7 Gbps test - not available\n"); + test_results[2] = true; + } + + if (found_810) { + test_results[3] = test_link_override(data, 810000, "8.1 Gbps data rate", + true, 10); + } else { + igt_info("Skipping 8.1 Gbps test - not available\n"); + test_results[3] = true; + } + + igt_reset_link_params(data->drm_fd, data->output); + + data_override_works = (!found_243 && !found_675); + safe_rates_available = (found_270 || found_810); + all_tests_passed = (test_results[0] && test_results[1] && + test_results[2] && test_results[3]); + + igt_info("====================================================\n"); + igt_info("eDP data override working: %s\n", data_override_works ? "YES" : "NO"); + igt_info("Safe override rates available: %s\n", safe_rates_available ? "YES" : "NO"); + igt_info("All rate tests passed: %s\n", all_tests_passed ? "YES" : "NO"); + igt_info("Overall test result: %s\n", + (data_override_works && safe_rates_available && all_tests_passed) ? + "SUCCESS" : "FAIL"); + + return (data_override_works && safe_rates_available && all_tests_passed); +} + IGT_TEST_DESCRIPTION("Test to validate link training on SST/MST with " "UHBR/NON_UHBR rates"); @@ -342,6 +519,14 @@ igt_main "Didn't find any MST output with NON-UHBR rates.\n"); } + igt_describe("Test eDP data override correctly"); + igt_subtest("edp-data-override") { + igt_require_f(intel_display_ver(data.devid) > 35, + "eDP override not supported on platform\n"); + igt_require_f(test_edp_data_override(&data), + "eDP data override test failed\n"); + } + igt_fixture { igt_reset_connectors(); igt_display_fini(&data.display); -- 2.43.0 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH i-g-t 0/6] extend link training test cases for eDP connector
@ 2025-10-15 10:17 Kunal Joshi
2025-10-15 10:17 ` [PATCH i-g-t 3/6] lib/igt_kms: add helpers for connector type Kunal Joshi
0 siblings, 1 reply; 9+ messages in thread
From: Kunal Joshi @ 2025-10-15 10:17 UTC (permalink / raw)
To: igt-dev; +Cc: Kunal Joshi
Rename kms_dp_linktrain_fallback -> kms_linktrain_fallback
and kms_dp_link_training -> kms_link_training.
Add support for eDP connector in both tests.
Note : On local testing eDP fallback is failing,
Seeing eDP/i915_dp_force_link_retrain as 1, so link isn't retrained
and fallback doesn't happen.
Kunal Joshi (6):
tests/intel/kms_dp_link_training: rename to
tests/intel/kms_link_training
tests/intel/kms_dp_linktrain_fallback: rename to
tests/intel/kms_linktrain_fallback
lib/igt_kms: add helpers for connector type
tests/intel/kms_link_training: extend test for eDP connector
tests/intel/kms_linktrain_fallback: extend test for eDP connector
HAX: Do not merge
lib/igt_kms.c | 74 +++++++++++++++----
lib/igt_kms.h | 9 ++-
tests/intel-ci/i915.fast-feedback.testlist | 7 ++
tests/intel-ci/xe-fast-feedback.testlist | 7 ++
tests/intel/kms_dsc_helper.c | 2 +-
...dp_link_training.c => kms_link_training.c} | 6 +-
...in_fallback.c => kms_linktrain_fallback.c} | 43 +++++++----
tests/intel/kms_pm_backlight.c | 2 +-
tests/kms_atomic_transition.c | 4 +-
tests/kms_hdr.c | 2 +-
tests/meson.build | 16 ++--
11 files changed, 126 insertions(+), 46 deletions(-)
rename tests/intel/{kms_dp_link_training.c => kms_link_training.c} (98%)
rename tests/intel/{kms_dp_linktrain_fallback.c => kms_linktrain_fallback.c} (93%)
--
2.25.1
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH i-g-t 3/6] lib/igt_kms: add helpers for connector type 2025-10-15 10:17 [PATCH i-g-t 0/6] extend link training test cases for eDP connector Kunal Joshi @ 2025-10-15 10:17 ` Kunal Joshi 2025-10-27 5:51 ` B, Jeevan 0 siblings, 1 reply; 9+ messages in thread From: Kunal Joshi @ 2025-10-15 10:17 UTC (permalink / raw) To: igt-dev; +Cc: Kunal Joshi Introduce helpers for checking output connector type, reuse same mechanism for output_is_internal_panel. Signed-off-by: Kunal Joshi <kunal1.joshi@intel.com> --- lib/igt_kms.c | 74 ++++++++++++++++++++++++++++------ lib/igt_kms.h | 9 ++++- tests/intel/kms_dsc_helper.c | 2 +- tests/intel/kms_pm_backlight.c | 2 +- tests/kms_atomic_transition.c | 4 +- tests/kms_hdr.c | 2 +- 6 files changed, 74 insertions(+), 19 deletions(-) diff --git a/lib/igt_kms.c b/lib/igt_kms.c index eab4cb92f..f29778f2d 100644 --- a/lib/igt_kms.c +++ b/lib/igt_kms.c @@ -3479,22 +3479,70 @@ igt_plane_t *igt_pipe_get_plane_type_index(igt_pipe_t *pipe, int plane_type, } /** - * output_is_internal_panel: + * igt_output_type_in_mask - test connector type membership + * @output: output (may be NULL) + * @mask: bitmask where bit N corresponds to DRM_MODE_CONNECTOR_* value N + * + * Returns: true if @output has a connector and its connector_type bit is set + * in @mask. Safe for DRM_MODE_CONNECTOR_Unknown (0). If @output or its + * connector is NULL, returns false. + */ +bool igt_output_type_in_mask(igt_output_t *output, uint64_t mask) +{ + unsigned int type; + + if (!output || !output->config.connector) + return false; + + type = output->config.connector->connector_type; + + return (mask & CONNECTOR_TYPE_BIT(type)) != 0; +} + +/** + * igt_output_is_dp_family - classify DP/eDP as DP family + * @output: output + * + * Returns: true if @output is DisplayPort or eDP. + */ +bool igt_output_is_dp_family(igt_output_t *output) +{ + uint64_t mask = + CONNECTOR_TYPE_BIT(DRM_MODE_CONNECTOR_DisplayPort) | + CONNECTOR_TYPE_BIT(DRM_MODE_CONNECTOR_eDP); + + return igt_output_type_in_mask(output, mask); +} + +/** + * igt_output_is_hdmi - classify HDMI connectors + * @output: output + * + * Returns: true if @output is HDMI (A or B). + */ +bool igt_output_is_hdmi(igt_output_t *output) +{ + uint64_t mask = + CONNECTOR_TYPE_BIT(DRM_MODE_CONNECTOR_HDMIA) | + CONNECTOR_TYPE_BIT(DRM_MODE_CONNECTOR_HDMIB); + + return igt_output_type_in_mask(output, mask); +} + +/** + * igt_output_is_internal_panel: * @output: Target output * * Returns: True if the given @output type is internal else False. */ -bool output_is_internal_panel(igt_output_t *output) +bool igt_output_is_internal_panel(igt_output_t *output) { - switch (output->config.connector->connector_type) { - case DRM_MODE_CONNECTOR_LVDS: - case DRM_MODE_CONNECTOR_eDP: - case DRM_MODE_CONNECTOR_DSI: - case DRM_MODE_CONNECTOR_DPI: - return true; - default: - return false; - } + uint64_t mask = CONNECTOR_TYPE_BIT(DRM_MODE_CONNECTOR_LVDS) | + CONNECTOR_TYPE_BIT(DRM_MODE_CONNECTOR_eDP) | + CONNECTOR_TYPE_BIT(DRM_MODE_CONNECTOR_DSI) | + CONNECTOR_TYPE_BIT(DRM_MODE_CONNECTOR_DPI); + + return igt_output_type_in_mask(output, mask); } igt_output_t **__igt_pipe_populate_outputs(igt_display_t *display, igt_output_t **chosen_outputs) @@ -3521,7 +3569,7 @@ igt_output_t **__igt_pipe_populate_outputs(igt_display_t *display, igt_output_t uint32_t pipe_mask = output->config.valid_crtc_idx_mask & full_pipe_mask; bool found = false; - if (output_is_internal_panel(output)) { + if (igt_output_is_internal_panel(output)) { /* * Internal panel should be assigned to pipe A * if possible, so make sure they're enumerated @@ -3549,7 +3597,7 @@ igt_output_t **__igt_pipe_populate_outputs(igt_display_t *display, igt_output_t * Overwrite internal panel if not assigned, * external outputs are faster to do modesets */ - output_is_internal_panel(chosen_outputs[j])) + igt_output_is_internal_panel(chosen_outputs[j])) chosen_outputs[j] = output; } diff --git a/lib/igt_kms.h b/lib/igt_kms.h index 7121ab985..c8d394ea9 100644 --- a/lib/igt_kms.h +++ b/lib/igt_kms.h @@ -569,7 +569,14 @@ igt_plane_t *igt_pipe_get_plane_type(igt_pipe_t *pipe, int plane_type); int igt_pipe_count_plane_type(igt_pipe_t *pipe, int plane_type); igt_plane_t *igt_pipe_get_plane_type_index(igt_pipe_t *pipe, int plane_type, int index); -bool output_is_internal_panel(igt_output_t *output); +#ifndef CONNECTOR_TYPE_BIT +#define CONNECTOR_TYPE_BIT(e) (1ULL << (e)) +#endif + +bool igt_output_type_in_mask(igt_output_t *output, uint64_t mask); +bool igt_output_is_dp_family(igt_output_t *output); +bool igt_output_is_hdmi(igt_output_t *output); +bool igt_output_is_internal_panel(igt_output_t *output); igt_output_t *igt_get_single_output_for_pipe(igt_display_t *display, enum pipe pipe); void igt_pipe_request_out_fence(igt_pipe_t *pipe); diff --git a/tests/intel/kms_dsc_helper.c b/tests/intel/kms_dsc_helper.c index cea4304e4..4f23c39af 100644 --- a/tests/intel/kms_dsc_helper.c +++ b/tests/intel/kms_dsc_helper.c @@ -74,7 +74,7 @@ bool is_dsc_supported_by_sink(int drmfd, igt_output_t *output) return false; } - if (!output_is_internal_panel(output) && + if (!igt_output_is_internal_panel(output) && !igt_is_fec_supported(drmfd, output->name)) { igt_info("DSC cannot be enabled without FEC on %s\n", output->name); diff --git a/tests/intel/kms_pm_backlight.c b/tests/intel/kms_pm_backlight.c index a0ecf2b0e..27c31d695 100644 --- a/tests/intel/kms_pm_backlight.c +++ b/tests/intel/kms_pm_backlight.c @@ -239,7 +239,7 @@ igt_main igt_display_require(&display, drm_open_driver(DRIVER_INTEL | DRIVER_XE)); for_each_connected_output(&display, output) { - if (!output_is_internal_panel(output)) + if (!igt_output_is_internal_panel(output)) continue; if (found) diff --git a/tests/kms_atomic_transition.c b/tests/kms_atomic_transition.c index a13830965..8e0ed9d08 100644 --- a/tests/kms_atomic_transition.c +++ b/tests/kms_atomic_transition.c @@ -1224,11 +1224,11 @@ igt_main_args("", long_opts, help_str, opt_handler, &data) * panels with long power cycle delays. */ if ((transition_tests[i].type == TRANSITION_MODESET) && - output_is_internal_panel(output)) + igt_output_is_internal_panel(output)) continue; if ((transition_tests[i].type == TRANSITION_MODESET_FAST) && - !output_is_internal_panel(output)) + !igt_output_is_internal_panel(output)) continue; if (pipe_count == 2 * count && !data.extended) diff --git a/tests/kms_hdr.c b/tests/kms_hdr.c index efa5964cf..c1c1663a2 100644 --- a/tests/kms_hdr.c +++ b/tests/kms_hdr.c @@ -730,7 +730,7 @@ static void test_hdr(data_t *data, uint32_t flags) continue; } - if ((flags & TEST_BRIGHTNESS) && !output_is_internal_panel(output)) { + if ((flags & TEST_BRIGHTNESS) && !igt_output_is_internal_panel(output)) { igt_info("%s: Can't run brightness test on non-internal panel.\n", igt_output_name(output)); continue; -- 2.25.1 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* RE: [PATCH i-g-t 3/6] lib/igt_kms: add helpers for connector type 2025-10-15 10:17 ` [PATCH i-g-t 3/6] lib/igt_kms: add helpers for connector type Kunal Joshi @ 2025-10-27 5:51 ` B, Jeevan 0 siblings, 0 replies; 9+ messages in thread From: B, Jeevan @ 2025-10-27 5:51 UTC (permalink / raw) To: Joshi, Kunal1, igt-dev@lists.freedesktop.org; +Cc: Joshi, Kunal1 > -----Original Message----- > From: igt-dev <igt-dev-bounces@lists.freedesktop.org> On Behalf Of Kunal Joshi > Sent: Wednesday, October 15, 2025 3:48 PM > To: igt-dev@lists.freedesktop.org > Cc: Joshi, Kunal1 <kunal1.joshi@intel.com> > Subject: [PATCH i-g-t 3/6] lib/igt_kms: add helpers for connector type > > Introduce helpers for checking output > connector type, reuse same mechanism for output_is_internal_panel. > > Signed-off-by: Kunal Joshi <kunal1.joshi@intel.com> > --- > lib/igt_kms.c | 74 ++++++++++++++++++++++++++++------ > lib/igt_kms.h | 9 ++++- > tests/intel/kms_dsc_helper.c | 2 +- > tests/intel/kms_pm_backlight.c | 2 +- > tests/kms_atomic_transition.c | 4 +- > tests/kms_hdr.c | 2 +- > 6 files changed, 74 insertions(+), 19 deletions(-) > > diff --git a/lib/igt_kms.c b/lib/igt_kms.c index eab4cb92f..f29778f2d 100644 > --- a/lib/igt_kms.c > +++ b/lib/igt_kms.c > @@ -3479,22 +3479,70 @@ igt_plane_t > *igt_pipe_get_plane_type_index(igt_pipe_t *pipe, int plane_type, } > > /** > - * output_is_internal_panel: > + * igt_output_type_in_mask - test connector type membership > + * @output: output (may be NULL) > + * @mask: bitmask where bit N corresponds to DRM_MODE_CONNECTOR_* > value > +N > + * > + * Returns: true if @output has a connector and its connector_type bit > +is set > + * in @mask. Safe for DRM_MODE_CONNECTOR_Unknown (0). If @output or its > + * connector is NULL, returns false. > + */ > +bool igt_output_type_in_mask(igt_output_t *output, uint64_t mask) { > + unsigned int type; > + > + if (!output || !output->config.connector) > + return false; > + > + type = output->config.connector->connector_type; > + > + return (mask & CONNECTOR_TYPE_BIT(type)) != 0; } > + > +/** > + * igt_output_is_dp_family - classify DP/eDP as DP family > + * @output: output > + * > + * Returns: true if @output is DisplayPort or eDP. > + */ > +bool igt_output_is_dp_family(igt_output_t *output) { > + uint64_t mask = > + CONNECTOR_TYPE_BIT(DRM_MODE_CONNECTOR_DisplayPort) > | > + CONNECTOR_TYPE_BIT(DRM_MODE_CONNECTOR_eDP); > + > + return igt_output_type_in_mask(output, mask); } > + > +/** > + * igt_output_is_hdmi - classify HDMI connectors > + * @output: output > + * > + * Returns: true if @output is HDMI (A or B). > + */ > +bool igt_output_is_hdmi(igt_output_t *output) { > + uint64_t mask = > + CONNECTOR_TYPE_BIT(DRM_MODE_CONNECTOR_HDMIA) | > + CONNECTOR_TYPE_BIT(DRM_MODE_CONNECTOR_HDMIB); > + > + return igt_output_type_in_mask(output, mask); } > + > +/** > + * igt_output_is_internal_panel: > * @output: Target output > * > * Returns: True if the given @output type is internal else False. > */ > -bool output_is_internal_panel(igt_output_t *output) > +bool igt_output_is_internal_panel(igt_output_t *output) > { > - switch (output->config.connector->connector_type) { > - case DRM_MODE_CONNECTOR_LVDS: > - case DRM_MODE_CONNECTOR_eDP: > - case DRM_MODE_CONNECTOR_DSI: > - case DRM_MODE_CONNECTOR_DPI: > - return true; > - default: > - return false; > - } > + uint64_t mask = > CONNECTOR_TYPE_BIT(DRM_MODE_CONNECTOR_LVDS) | > + > CONNECTOR_TYPE_BIT(DRM_MODE_CONNECTOR_eDP) | > + > CONNECTOR_TYPE_BIT(DRM_MODE_CONNECTOR_DSI) | > + > CONNECTOR_TYPE_BIT(DRM_MODE_CONNECTOR_DPI); > + > + return igt_output_type_in_mask(output, mask); > } Code looks good, but I have one small suggestion. You could define the bitmask values once and reuse them, instead of manually composing them in each helper. #define CONNECTOR_MASK_DP_FAMILY \ (CONNECTOR_TYPE_BIT(DRM_MODE_CONNECTOR_DisplayPort) | \ CONNECTOR_TYPE_BIT(DRM_MODE_CONNECTOR_eDP)) #define CONNECTOR_MASK_HDMI \ (CONNECTOR_TYPE_BIT(DRM_MODE_CONNECTOR_HDMIA) | \ CONNECTOR_TYPE_BIT(DRM_MODE_CONNECTOR_HDMIB)) #define CONNECTOR_MASK_INTERNAL_PANEL \ (CONNECTOR_TYPE_BIT(DRM_MODE_CONNECTOR_LVDS) | \ CONNECTOR_TYPE_BIT(DRM_MODE_CONNECTOR_eDP) | \ CONNECTOR_TYPE_BIT(DRM_MODE_CONNECTOR_DSI) | \ CONNECTOR_TYPE_BIT(DRM_MODE_CONNECTOR_DPI)) > > igt_output_t **__igt_pipe_populate_outputs(igt_display_t *display, igt_output_t > **chosen_outputs) @@ -3521,7 +3569,7 @@ igt_output_t > **__igt_pipe_populate_outputs(igt_display_t *display, igt_output_t > uint32_t pipe_mask = output- > >config.valid_crtc_idx_mask & full_pipe_mask; > bool found = false; > > - if (output_is_internal_panel(output)) { > + if (igt_output_is_internal_panel(output)) { > /* > * Internal panel should be assigned to pipe A > * if possible, so make sure they're enumerated > @@ -3549,7 +3597,7 @@ igt_output_t > **__igt_pipe_populate_outputs(igt_display_t *display, igt_output_t > * Overwrite internal panel if not > assigned, > * external outputs are faster to do > modesets > */ > - > output_is_internal_panel(chosen_outputs[j])) > + > igt_output_is_internal_panel(chosen_outputs[j])) > chosen_outputs[j] = output; > } > > diff --git a/lib/igt_kms.h b/lib/igt_kms.h index 7121ab985..c8d394ea9 100644 > --- a/lib/igt_kms.h > +++ b/lib/igt_kms.h > @@ -569,7 +569,14 @@ igt_plane_t *igt_pipe_get_plane_type(igt_pipe_t *pipe, > int plane_type); int igt_pipe_count_plane_type(igt_pipe_t *pipe, int plane_type); > igt_plane_t *igt_pipe_get_plane_type_index(igt_pipe_t *pipe, int plane_type, > int index); > -bool output_is_internal_panel(igt_output_t *output); > +#ifndef CONNECTOR_TYPE_BIT > +#define CONNECTOR_TYPE_BIT(e) (1ULL << (e)) #endif > + > +bool igt_output_type_in_mask(igt_output_t *output, uint64_t mask); bool > +igt_output_is_dp_family(igt_output_t *output); bool > +igt_output_is_hdmi(igt_output_t *output); bool > +igt_output_is_internal_panel(igt_output_t *output); > igt_output_t *igt_get_single_output_for_pipe(igt_display_t *display, enum pipe > pipe); > > void igt_pipe_request_out_fence(igt_pipe_t *pipe); diff --git > a/tests/intel/kms_dsc_helper.c b/tests/intel/kms_dsc_helper.c index > cea4304e4..4f23c39af 100644 > --- a/tests/intel/kms_dsc_helper.c > +++ b/tests/intel/kms_dsc_helper.c > @@ -74,7 +74,7 @@ bool is_dsc_supported_by_sink(int drmfd, igt_output_t > *output) > return false; > } > > - if (!output_is_internal_panel(output) && > + if (!igt_output_is_internal_panel(output) && > !igt_is_fec_supported(drmfd, output->name)) { > igt_info("DSC cannot be enabled without FEC on %s\n", > output->name); > diff --git a/tests/intel/kms_pm_backlight.c b/tests/intel/kms_pm_backlight.c > index a0ecf2b0e..27c31d695 100644 > --- a/tests/intel/kms_pm_backlight.c > +++ b/tests/intel/kms_pm_backlight.c > @@ -239,7 +239,7 @@ igt_main > igt_display_require(&display, drm_open_driver(DRIVER_INTEL | > DRIVER_XE)); > > for_each_connected_output(&display, output) { > - if (!output_is_internal_panel(output)) > + if (!igt_output_is_internal_panel(output)) > continue; > > if (found) > diff --git a/tests/kms_atomic_transition.c b/tests/kms_atomic_transition.c index > a13830965..8e0ed9d08 100644 > --- a/tests/kms_atomic_transition.c > +++ b/tests/kms_atomic_transition.c > @@ -1224,11 +1224,11 @@ igt_main_args("", long_opts, help_str, opt_handler, > &data) > * panels with long power cycle delays. > */ > if ((transition_tests[i].type == > TRANSITION_MODESET) && > - output_is_internal_panel(output)) > + igt_output_is_internal_panel(output)) > continue; > > if ((transition_tests[i].type == > TRANSITION_MODESET_FAST) && > - !output_is_internal_panel(output)) > + !igt_output_is_internal_panel(output)) > continue; > > if (pipe_count == 2 * count && !data.extended) > diff --git a/tests/kms_hdr.c b/tests/kms_hdr.c index efa5964cf..c1c1663a2 > 100644 > --- a/tests/kms_hdr.c > +++ b/tests/kms_hdr.c > @@ -730,7 +730,7 @@ static void test_hdr(data_t *data, uint32_t flags) > continue; > } > > - if ((flags & TEST_BRIGHTNESS) && > !output_is_internal_panel(output)) { > + if ((flags & TEST_BRIGHTNESS) && > +!igt_output_is_internal_panel(output)) { > igt_info("%s: Can't run brightness test on non-internal > panel.\n", > igt_output_name(output)); > continue; > -- > 2.25.1 ^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2025-11-24 18:28 UTC | newest] Thread overview: 9+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-11-24 18:27 [PATCH i-g-t 0/6] RFC: Add new test for eDP data override Jeevan B 2025-11-24 18:27 ` [PATCH i-g-t 1/6] tests/intel/kms_dp_link_training: rename to tests/intel/kms_link_training Jeevan B 2025-11-24 18:27 ` [PATCH i-g-t 2/6] tests/intel/kms_dp_linktrain_fallback: rename to tests/intel/kms_linktrain_fallback Jeevan B 2025-11-24 18:27 ` [PATCH i-g-t 3/6] lib/igt_kms: add helpers for connector type Jeevan B 2025-11-24 18:27 ` [PATCH i-g-t 4/6] tests/intel/kms_link_training: extend test for eDP connector Jeevan B 2025-11-24 18:27 ` [PATCH i-g-t 5/6] lib/igt_kms: Add helper to get eDP/DP supported link rates Jeevan B 2025-11-24 18:27 ` [PATCH i-g-t 6/6] RFC: tests/intel/kms_link_training: Add edp-data-override subtest Jeevan B -- strict thread matches above, loose matches on Subject: below -- 2025-10-15 10:17 [PATCH i-g-t 0/6] extend link training test cases for eDP connector Kunal Joshi 2025-10-15 10:17 ` [PATCH i-g-t 3/6] lib/igt_kms: add helpers for connector type Kunal Joshi 2025-10-27 5:51 ` B, Jeevan
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).