[PATCH i-g-t v1 1/2] lib/igt_vrr:Add VRR helper library for display refresh rate testing

Naladala Ramanaidu <[email protected]> Wed, 5 Aug 2026 04:46:57 +0530
Newsgroups org.freedesktop.lists.igt-dev
Message-ID <[email protected]>
Introduce a new helper library for Variable Refresh Rate (VRR).

Add helpers to validate targeted refresh-rate testing.

Signed-off-by: Naladala Ramanaidu <[email protected]>
---
 lib/igt_vrr.c   | 150 ++++++++++++++++++++++++++++++++++++++++++++++++
 lib/igt_vrr.h   |  45 +++++++++++++++
 lib/meson.build |   1 +
 3 files changed, 196 insertions(+)
 create mode 100644 lib/igt_vrr.c
 create mode 100644 lib/igt_vrr.h

diff --git a/lib/igt_vrr.c b/lib/igt_vrr.c
new file mode 100644
index 000000000..a11cc8adc
--- /dev/null
+++ b/lib/igt_vrr.c
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2026 Intel Corporation
+ */
+
+#include <inttypes.h>
+
+#include "igt_vrr.h"
+
+/**
+ * igt_cmrr_debugfs_write:
+ * @fd: DRM file descriptor.
+ * @crtc: CRTC whose VRR debugfs node is to be updated.
+ * @vrefresh: Target refresh rate to program.
+ * @cmrr_flag: CMRR mode to configure (video, non-video, or disable).
+ *
+ * Write the target CMRR refresh rate configuration to the per-CRTC
+ * VRR debugfs interface.
+ *
+ * Returns:
+ * None.
+ */
+void
+igt_target_rr_debugfs_write(int fd, igt_crtc_t *crtc,
+			    uint32_t vrefresh,
+			    uint32_t cmrr_flag)
+{
+	char buf[32];
+	char path[64];
+	uint32_t len, ret;
+	uint64_t cmrr_value;
+
+	ret = snprintf(path, sizeof(path),
+		       "crtc-%d/intel_vrr_target_refresh_rate",
+		       crtc->pipe);
+	igt_assert(ret > 0 && ret < sizeof(path));
+
+	switch (cmrr_flag) {
+	case CMRR_VIDEO_MODE:
+		cmrr_value = vrefresh * CMRR_NUMERATOR;
+		len = snprintf(buf, sizeof(buf),
+			       "%" PRIu64 "/%llu",
+			       cmrr_value,
+			       CMRR_VIDEO_MODE_DENOMINATOR);
+		igt_assert(len > 0 && len < sizeof(buf));
+		break;
+
+	case CMRR_NON_VIDEO_MODE:
+		cmrr_value = vrefresh * CMRR_NUMERATOR;
+		len = snprintf(buf, sizeof(buf),
+			       "%" PRIu64 "/%llu",
+			       cmrr_value,
+			       CMRR_DENOMINATOR);
+		igt_assert(len > 0 && len < sizeof(buf));
+		break;
+
+	default:
+		cmrr_value = 0 * CMRR_NUMERATOR;
+		len = snprintf(buf, sizeof(buf),
+			       "%" PRIu64 "/0", cmrr_value);
+		igt_assert(len > 0 && len < sizeof(buf));
+		break;
+	}
+
+	__igt_debugfs_write(fd, path, buf, len);
+}
+
+/**
+ * igt_cmrr_debugfs_read:
+ * @fd: DRM file descriptor.
+ * @crtc: CRTC whose VRR debugfs node is to be read.
+ *
+ * Read the configured CMRR mode from the per-CRTC VRR debugfs node.
+ *
+ * Return: The configured CMRR mode (video, non-video, or disabled).
+ */
+uint32_t
+igt_target_rr_debugfs_read(int fd, igt_crtc_t *crtc)
+{
+	char buf[32];
+	char path[64];
+	uint32_t ret;
+	uint64_t numerator, denominator;
+
+	ret = snprintf(path, sizeof(path),
+		       "crtc-%d/intel_vrr_target_refresh_rate",
+		       crtc->pipe);
+	igt_assert(ret > 0 && ret < sizeof(path));
+
+	igt_debugfs_read(fd, path, buf);
+
+	ret = sscanf(buf, "%" SCNu64 "/%" SCNu64,
+		     &numerator, &denominator);
+	igt_assert(ret == 2);
+
+	if (denominator == CMRR_VIDEO_MODE_DENOMINATOR)
+		return CMRR_VIDEO_MODE;
+
+	if (denominator == CMRR_DENOMINATOR)
+		return CMRR_NON_VIDEO_MODE;
+
+	if (denominator == 0)
+		return CMRR_DISABLE;
+
+	return CMRR_DISABLE;
+}
+
+/**
+ * igt_vrr_mode_line_refresh_hz:
+ * @mode: DRM display mode used for the calculation
+ *
+ * Compute the refresh rate directly from the mode timing parameters.
+ *
+ * Returns: Refresh rate in Hz as a floating-point value.
+ */
+double igt_vrr_mode_line_refresh_hz(const drmModeModeInfo *mode)
+{
+	return (double)mode->clock * 1000.0 / ((double)mode->htotal * (double)mode->vtotal);
+}
+
+/**
+ * igt_vrr_get_mode_with_video_timeing:
+ * @output: Display output containing connector mode list
+ * @fps: Requested integer refresh rate in Hz
+ * @matched_mode: Returned mode that matches @fps
+ *
+ * Find and return a connector mode that matches the requested
+ * video timing refresh rate in Hz.
+ *
+ * Returns: true when a mode is found, false otherwise
+ */
+
+bool igt_vrr_get_mode_with_video_timeing(igt_output_t *output,
+					 uint32_t fps,
+					 drmModeModeInfo *matched_mode)
+{
+	drmModeConnectorPtr connector;
+
+	connector = output->config.connector;
+	if (!connector)
+		return false;
+
+	for (int i = 0; i < connector->count_modes; i++) {
+		if (connector->modes[i].vrefresh == fps) {
+			*matched_mode = connector->modes[i];
+			return true;
+		}
+	}
+	return false;
+}
diff --git a/lib/igt_vrr.h b/lib/igt_vrr.h
new file mode 100644
index 000000000..7724f60a1
--- /dev/null
+++ b/lib/igt_vrr.h
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2026 Intel Corporation
+ */
+
+#ifndef IGT_VRR_H
+#define IGT_VRR_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include "igt.h"
+#include "igt_kms.h"
+
+#define CMRR_NUMERATOR 1000ULL
+#define CMRR_DENOMINATOR 1000ULL
+#define CMRR_VIDEO_MODE_DENOMINATOR 1001ULL
+#define TARGET_RR_SAMP_COUNT 100
+
+enum {
+	CMRR_VIDEO_MODE,
+	CMRR_NON_VIDEO_MODE,
+	CMRR_DISABLE,
+};
+
+const uint32_t igt_vrr_standard_video_timing_fps[] = {
+	24, 25, 30, 48, 50, 60, 75, 90, 96, 100, 120, 144, 165, 180, 200, 240,
+};
+
+const uint32_t igt_vrr_standard_video_timing_fps_count =
+	ARRAY_SIZE(igt_vrr_standard_video_timing_fps);
+
+void
+igt_target_rr_debugfs_write(int fd, igt_crtc_t *crtc,
+			    uint32_t vrefresh,
+			    uint32_t cmrr_flag);
+uint32_t
+igt_target_rr_debugfs_read(int fd, igt_crtc_t *crtc);
+
+double igt_vrr_mode_line_refresh_hz(const drmModeModeInfo *mode);
+
+bool igt_vrr_get_mode_with_video_timeing(igt_output_t *output,
+					 uint32_t fps,
+					 drmModeModeInfo *matched_mode);
+
+#endif
diff --git a/lib/meson.build b/lib/meson.build
index 3001b473e..8675bd4a6 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -22,6 +22,7 @@ lib_sources = [
 	'igt_configfs.c',
 	'igt_facts.c',
 	'igt_crc.c',
+        'igt_vrr.c',
 	'igt_debugfs.c',
 	'igt_device.c',
 	'igt_device_scan.c',
-- 
2.43.0