[PATCH i-g-t 1/2] tests/kms_vrr: New subtest for CMRR

Bhanuprakash Modem bhanuprakash.modem at intel.com
Fri Jun 7 12:48:39 UTC 2024


CMRR is an variation of VRR where it varies Vtotal slightly
(between additional 0 and 1 Vtotal scanlines) to match content
rate exactly without frame drops using the adaptive sync framework.

This patch creates a new subtest to validate the CMRR as below:
- Request flips with the refresh_rate * 1.001
- Flips should happen at the frequency of refresh_rate.

V2:
 - Tweak the clock if CMRR supported mode not found (Ankit)
 - Rebase
V3:
 - Read flip completion events instead of Vblank evants

Signed-off-by: Bhanuprakash Modem <bhanuprakash.modem at intel.com>
---
 tests/kms_vrr.c | 130 +++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 128 insertions(+), 2 deletions(-)

diff --git a/tests/kms_vrr.c b/tests/kms_vrr.c
index 7f64d6806..f3daa55f5 100644
--- a/tests/kms_vrr.c
+++ b/tests/kms_vrr.c
@@ -37,6 +37,11 @@
 #include <signal.h>
 
 /**
+ * SUBTEST: cmrr
+ * Description: Test to validate the content rate to exactly match with the
+ * 		requested rate without any frame drops.
+ * Functionality: CMRR
+ *
  * SUBTEST: flip-basic
  * Description: Tests that VRR is enabled and that the difference between flip
  *              timestamps converges to the requested rate
@@ -87,6 +92,9 @@
  */
 #define TEST_DURATION_NS (5000000000ull)
 
+#define CMRR_PRECISION_TOLERANCE	10
+#define CMRR_MULTIPLIER	1.001
+
 enum {
 	TEST_BASIC = 1 << 0,
 	TEST_DPMS = 1 << 1,
@@ -97,7 +105,8 @@ enum {
 	TEST_SEAMLESS_VIRTUAL_RR = 1 << 6,
 	TEST_FASTSET = 1 << 7,
 	TEST_MAXMIN = 1 << 8,
-	TEST_NEGATIVE = 1 << 9,
+	TEST_CMRR = 1 << 9,
+	TEST_NEGATIVE = 1 << 10,
 };
 
 enum {
@@ -232,6 +241,21 @@ virtual_rr_vrr_range_mode(igt_output_t *output, unsigned int virtual_refresh_rat
 	return mode;
 }
 
+static bool
+is_cmrr_mode(drmModeModeInfoPtr mode)
+{
+	int calculated_refresh, actual_refresh, pixel_clock_per_line;
+
+	actual_refresh = mode->vrefresh * 100;
+	pixel_clock_per_line = mode->clock * 1000 / mode->htotal;
+	calculated_refresh = pixel_clock_per_line * 100 / mode->vtotal;
+
+	if ((actual_refresh - calculated_refresh) < CMRR_PRECISION_TOLERANCE)
+		return false;
+
+	return true;
+}
+
 /* Read min and max vrr range from the connector debugfs. */
 static range_t
 get_vrr_range(data_t *data, igt_output_t *output)
@@ -496,6 +520,57 @@ flip_and_measure(data_t *data, igt_output_t *output, enum pipe pipe,
 	return total_flip ? ((total_pass * 100) / total_flip) : 0;
 }
 
+static uint32_t
+flip_and_measure_cmrr(data_t *data, igt_output_t *output, enum pipe pipe,
+		      uint64_t duration_ns)
+{
+	uint64_t start_ns, last_event_ns, event_ns;
+	uint32_t total_flip = 0, total_pass = 0;
+	bool front = false;
+	drmModeModeInfoPtr mode = igt_output_get_mode(output);
+	uint64_t req_rate_ns = rate_from_refresh(mode->vrefresh * CMRR_MULTIPLIER);
+	uint64_t exp_rate_ns = rate_from_refresh(mode->vrefresh);
+	uint64_t threshold_ns = exp_rate_ns / mode->vdisplay; /* Upto 1 scan line. */
+
+	igt_info("CMRR on: requested rate: %"PRIu64" ns (%f Hz) "
+		 "expected rate: %"PRIu64" ns - %"PRIu64" ns (%f-%f Hz)\n",
+		 req_rate_ns, mode->vrefresh * CMRR_MULTIPLIER,
+		 (exp_rate_ns - threshold_ns), (exp_rate_ns + threshold_ns),
+		 (float)NSECS_PER_SEC / (exp_rate_ns + threshold_ns),
+		 (float)NSECS_PER_SEC / (exp_rate_ns - threshold_ns));
+
+	do_flip(data, &data->fb[0]);
+	start_ns = last_event_ns = get_kernel_event_ns(data, DRM_EVENT_FLIP_COMPLETE);
+	do {
+		int64_t target_ns, wait_ns, diff_ns = exp_rate_ns;
+
+		front = !front;
+		do_flip(data, front ? &data->fb[1] : &data->fb[0]);
+
+		event_ns = get_kernel_event_ns(data, DRM_EVENT_FLIP_COMPLETE);
+		igt_debug("event_ns - last_event_ns: %"PRIu64" ns (%f Hz)\n",
+			  event_ns - last_event_ns, (float)NSECS_PER_SEC / (event_ns - last_event_ns));
+
+		diff_ns -= event_ns - last_event_ns;
+		if (llabs(diff_ns) <= threshold_ns)
+			total_pass += 1;
+
+		last_event_ns = event_ns;
+		total_flip += 1;
+
+		diff_ns = event_ns - start_ns;
+		wait_ns = ((diff_ns + req_rate_ns - 1) / req_rate_ns) * req_rate_ns;
+		wait_ns -= diff_ns;
+		target_ns = event_ns + wait_ns;
+		while (get_time_ns() < target_ns - 10);
+	} while (event_ns - start_ns <= duration_ns);
+
+	igt_info("Completed %u flips, %u vblanks were in threshold for (%u Hz) %"PRIu64"ns.\n",
+		 total_flip, total_pass, mode->vrefresh, req_rate_ns);
+
+	return total_flip ? ((total_pass * 100) / total_flip) : 0;
+}
+
 /* Basic VRR flip functionality test - enable, measure, disable, measure */
 static void
 test_basic(data_t *data, enum pipe pipe, igt_output_t *output, uint32_t flags)
@@ -702,6 +777,49 @@ test_seamless_virtual_rr_basic(data_t *data, enum pipe pipe, igt_output_t *outpu
 	}
 }
 
+static void
+test_cmrr(data_t *data, enum pipe pipe, igt_output_t *output, uint32_t flags)
+{
+	uint32_t result;
+	int i;
+	bool found = false;
+	drmModeConnectorPtr connector = output->config.connector;
+	drmModeModeInfo mode = *igt_output_get_mode(output);
+
+	igt_info("CMRR test execution on %s, PIPE_%s with VRR range: (%u-%u) Hz\n",
+		 output->name, kmstest_pipe_name(pipe), data->range.min, data->range.max);
+
+	for (i = 0; i < connector->count_modes; i++) {
+		if (is_cmrr_mode(&connector->modes[i])) {
+			mode = connector->modes[i];
+
+			found = true;
+			break;
+		}
+	}
+
+	igt_info("Selected mode: ");
+	kmstest_dump_mode(&mode);
+
+	if (!found) {
+		igt_info("No CMRR mode found on %s, try to tweak the clock.\n", output->name);
+
+		mode.clock = (mode.htotal * mode.vtotal * (mode.vrefresh * CMRR_MULTIPLIER)) / 1000;
+
+		igt_info("Tweaked mode: ");
+		kmstest_dump_mode(&mode);
+	}
+
+	igt_output_override_mode(output, &mode);
+	igt_display_commit2(&data->display, COMMIT_ATOMIC);
+
+	prepare_test(data, output, pipe);
+	result = flip_and_measure_cmrr(data, output, pipe, TEST_DURATION_NS * 2);
+	igt_assert_f(result > 75,
+		     "Refresh rate (%u Hz) %"PRIu64"ns: Target CMRR on threshold not reached, result was %u%%\n",
+		     mode.vrefresh, rate_from_refresh(mode.vrefresh), result);
+}
+
 static void test_cleanup(data_t *data, enum pipe pipe, igt_output_t *output)
 {
 	if (vrr_capable(output))
@@ -718,7 +836,7 @@ static void test_cleanup(data_t *data, enum pipe pipe, igt_output_t *output)
 
 static bool output_constraint(data_t *data, igt_output_t *output, uint32_t flags)
 {
-	if ((flags & (TEST_SEAMLESS_VRR | TEST_SEAMLESS_DRRS)) &&
+	if ((flags & (TEST_SEAMLESS_VRR | TEST_SEAMLESS_DRRS | TEST_CMRR)) &&
 	    output->config.connector->connector_type != DRM_MODE_CONNECTOR_eDP)
 		return false;
 
@@ -906,6 +1024,14 @@ igt_main_args("drs:", long_opts, help_str, opt_handler, &data)
 		igt_describe("Test to switch to any custom virtual mode in VRR range without modeset.");
 		igt_subtest_with_dynamic("seamless-rr-switch-virtual")
 			run_vrr_test(&data, test_seamless_virtual_rr_basic, TEST_SEAMLESS_VIRTUAL_RR);
+
+		igt_describe("Test to validate the the content rate exactly match with the "
+			     "requested rate without any frame drops.");
+		igt_subtest_with_dynamic("cmrr") {
+			igt_require(intel_display_ver(intel_get_drm_devid(data.drm_fd)) >= 20);
+
+			run_vrr_test(&data, test_cmrr, TEST_CMRR);
+		}
 	}
 
 	igt_fixture {
-- 
2.43.2



More information about the igt-dev mailing list