Igt-dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Ray Wu <ray.wu@amd.com>
To: <igt-dev@lists.freedesktop.org>
Cc: <alex.hung@amd.com>, <sunpeng.li@amd.com>,
	<chiahsuan.chung@amd.com>, <ray.wu@amd.com>
Subject: [PATCH i-g-t, v2 2/2] tests/amdgpu/amd_replay: add Replay Rate Control IGT test
Date: Wed, 13 May 2026 15:03:22 +0800	[thread overview]
Message-ID: <20260513070631.3723466-3-ray.wu@amd.com> (raw)
In-Reply-To: <20260513070631.3723466-1-ray.wu@amd.com>

[Why]
Panel Replay rate control lowers the panel refresh rate while the
screen is static by extending coasting vtotal, and restores it when
the screen becomes live again. There is currently no IGT coverage for
this behaviour.

[How]
Add two helpers in lib/igt_amd:

  - igt_amd_replay_support_rate_control(): true when the connector's
    replay_capability advertises "Rate control support: yes".
  - igt_amd_read_replay_coasting_vtotal(): reads replay_coasting_vtotal;
    returns 0 on success or -errno on failure.

Add a "replay_rate_control" subtest that engages Panel Replay via page
flips, samples the coasting vtotal in static and live modes, and
asserts that the static value is strictly greater than the live value.
Skips cleanly when eDP, Panel Replay, or rate control support is
missing.

Signed-off-by: Ray Wu <ray.wu@amd.com>
---
v2:
- Move close(fd) before igt_assert_f() to avoid fd leak. (Alex)
- Correct the rate control comment. (Alex)
- Check drmModePageFlip() return value. (Alex)
---
 lib/igt_amd.c             | 94 +++++++++++++++++++++++++++++++++++++++
 lib/igt_amd.h             |  3 ++
 tests/amdgpu/amd_replay.c | 88 ++++++++++++++++++++++++++++++++++++
 3 files changed, 185 insertions(+)

diff --git a/lib/igt_amd.c b/lib/igt_amd.c
index a97adad43..8ad3be7a7 100644
--- a/lib/igt_amd.c
+++ b/lib/igt_amd.c
@@ -20,7 +20,9 @@
  * OTHER DEALINGS IN THE SOFTWARE.
  */
 
+#include <errno.h>
 #include <fcntl.h>
+#include <string.h>
 #include <sys/stat.h>
 
 #include "igt_amd.h"
@@ -1082,6 +1084,40 @@ bool igt_amd_replay_support_drv(int drm_fd, char *connector_name)
 	return strstr(buf, "Driver support: yes") && strstr(buf, "Config support: yes");
 }
 
+/**
+ * igt_amd_replay_support_rate_control: check if Panel Replay rate control is supported
+ * @drm_fd: DRM file descriptor
+ * @connector_name: The connector's name, on which we're reading the status
+ *
+ * Return:
+ *   true  - "Rate control support: yes" found in replay_capability
+ *   false - rate control not supported, field not present (older kernel),
+ *           or any read/debugfs error
+ */
+bool igt_amd_replay_support_rate_control(int drm_fd, char *connector_name)
+{
+	char buf[128];
+	int ret;
+	int fd;
+
+	fd = igt_debugfs_connector_dir(drm_fd, connector_name, O_RDONLY);
+	if (fd < 0) {
+		igt_info("output %s: debugfs not found\n", connector_name);
+
+		return false;
+	}
+
+	ret = igt_debugfs_simple_read(fd, DEBUGFS_EDP_REPLAY_CAP, buf, sizeof(buf));
+	close(fd);
+	igt_assert_f(ret >= 0, "Reading %s for connector %s failed.\n",
+		     DEBUGFS_EDP_REPLAY_CAP, connector_name);
+
+	if (ret < 1)
+		return false;
+
+	return strstr(buf, "Rate control support: yes");
+}
+
 /**
  * igt_amd_output_has_replay_state: check if eDP connector has replay_state debugfs entry
  * @drm_fd: DRM file descriptor
@@ -1177,6 +1213,64 @@ enum replay_state igt_amd_read_replay_state(int drm_fd, char *connector_name)
 	return convert_replay_state(raw_state);
 }
 
+/**
+ * @brief Read Panel Replay current coasting vtotal from debugfs interface
+ * @param drm_fd DRM file descriptor
+ * @param connector_name The connector's name, on which we're reading the status
+ * @param vtotal Out: parsed vtotal value (only valid when return value is 0)
+ *
+ * Reads /sys/kernel/debug/dri/<N>/<connector>/replay_current_coasting_vtotal,
+ * which contains a single decimal number (e.g. "4938").
+ *
+ * Return:
+ *   0         on success, *vtotal contains the parsed value
+ *  -EINVAL    invalid arguments, or the file content cannot be parsed
+ *  -ENODATA   the debugfs file is empty
+ *  -errno     other errors passed through from the underlying igt
+ *             debugfs helpers (e.g. -ENOENT, -EACCES on debugfs open)
+ */
+int igt_amd_read_replay_coasting_vtotal(int drm_fd, char *connector_name,
+					uint32_t *vtotal)
+{
+	char buf[32] = {0};
+	int fd, ret;
+	unsigned long parsed;
+	char *endp;
+
+	if (!vtotal)
+		return -EINVAL;
+
+	fd = igt_debugfs_connector_dir(drm_fd, connector_name, O_RDONLY);
+	if (fd < 0) {
+		ret = -errno;
+		igt_info("Couldn't open connector %s debugfs directory (%s)\n",
+			 connector_name, strerror(-ret));
+		return ret;
+	}
+
+	ret = igt_debugfs_simple_read(fd, DEBUGFS_EDP_REPLAY_COASTING_VTOTAL,
+				      buf, sizeof(buf) - 1);
+	close(fd);
+
+	if (ret < 1) {
+		igt_info("Reading %s for connector %s failed (%s)\n",
+			 DEBUGFS_EDP_REPLAY_COASTING_VTOTAL, connector_name,
+			 ret < 0 ? strerror(-ret) : "empty");
+		return ret < 0 ? ret : -ENODATA;
+	}
+
+	parsed = strtoul(buf, &endp, 10);
+	if (endp == buf) {
+		igt_info("%s for connector %s: unparseable value '%s'\n",
+			 DEBUGFS_EDP_REPLAY_COASTING_VTOTAL, connector_name,
+			 buf);
+		return -EINVAL;
+	}
+
+	*vtotal = (uint32_t)parsed;
+	return 0;
+}
+
 /**
  * igt_amd_output_has_psr_cap: check if eDP connector has psr_capability debugfs entry
  * @drm_fd: DRM file descriptor
diff --git a/lib/igt_amd.h b/lib/igt_amd.h
index a45122b68..7f11e1d38 100644
--- a/lib/igt_amd.h
+++ b/lib/igt_amd.h
@@ -49,6 +49,7 @@
 #define MULTIPLIER_TO_LR 270000
 #define DEBUGFS_EDP_REPLAY_CAP "replay_capability"
 #define DEBUGFS_EDP_REPLAY_STATE "replay_state"
+#define DEBUGFS_EDP_REPLAY_COASTING_VTOTAL "replay_coasting_vtotal"
 #define DEBUGFS_EDP_PSR_CAP	"psr_capability"
 #define DEBUGFS_EDP_PSR_STATE	"psr_state"
 #define DEBUGFS_ALLOW_EDP_HOTPLUG_DETECT "allow_edp_hotplug_detection"
@@ -227,6 +228,8 @@ bool igt_amd_output_has_ilr_setting(int drm_fd, char *connector_name);
 bool igt_amd_output_has_replay_cap(int drm_fd, char *connector_name);
 bool igt_amd_replay_support_sink(int drm_fd, char *connector_name);
 bool igt_amd_replay_support_drv(int drm_fd, char *connector_name);
+bool igt_amd_replay_support_rate_control(int drm_fd, char *connector_name);
+int igt_amd_read_replay_coasting_vtotal(int drm_fd, char *connector_name, uint32_t *vtotal);
 bool igt_amd_output_has_replay_state(int drm_fd, char *connector_name);
 enum replay_state igt_amd_read_replay_state(int drm_fd, char *connector_name);
 bool igt_amd_output_has_psr_cap(int drm_fd, char *connector_name);
diff --git a/tests/amdgpu/amd_replay.c b/tests/amdgpu/amd_replay.c
index 4b795f5ba..db2b565b5 100644
--- a/tests/amdgpu/amd_replay.c
+++ b/tests/amdgpu/amd_replay.c
@@ -5,6 +5,7 @@
 
 #include <dirent.h>
 #include <fcntl.h>
+#include <string.h>
 
 #include "igt_amd.h"
 
@@ -397,6 +398,90 @@ static void run_check_replay_suspend(struct test_data *data)
 	test_fini(data);
 }
 
+/*
+ * Verify replay rate control: when the screen is static, coasting vtotal should
+ * be extended to lower the refresh rate; under live mode, coasting vtotal should
+ * drop back. Equal values mean rate control did not engage, which is a failure.
+ *
+ *     static_coasting_vtotal > live_coasting_vtotal
+ */
+static void run_check_replay_rate_control(struct test_data *data)
+{
+	int edp_idx;
+	igt_output_t *output;
+	uint32_t static_coasting_vtotal = 0;
+	uint32_t live_coasting_vtotal = 0;
+	int ret;
+
+	test_init(data);
+
+	edp_idx = check_conn_type(data, DRM_MODE_CONNECTOR_eDP);
+	igt_skip_on_f(edp_idx == -1, "no eDP connector found\n");
+
+	/* check if eDP supports Panel Replay. */
+	igt_skip_on(!replay_mode_supported(data));
+
+	igt_skip_on_f(!igt_amd_replay_support_rate_control(data->fd,
+							   data->output->name),
+		      "Replay rate control not supported; skip test\n");
+
+	for_each_connected_output(&data->display, output) {
+		if (output->config.connector->connector_type != DRM_MODE_CONNECTOR_eDP)
+			continue;
+
+		igt_create_color_fb(data->fd, data->mode->hdisplay,
+				    data->mode->vdisplay, DRM_FORMAT_XRGB8888, 0,
+				    0.6, 0.6, 0.6, &data->ref_fb);
+		igt_create_color_fb(data->fd, data->mode->hdisplay,
+				    data->mode->vdisplay, DRM_FORMAT_XRGB8888, 0,
+				    0.0, 0.4, 0.14, &data->ref_fb2);
+
+		igt_plane_set_fb(data->primary, &data->ref_fb);
+		igt_display_commit_atomic(&data->display,
+					  DRM_MODE_ATOMIC_ALLOW_MODESET, 0);
+		data->flip_fb = &data->ref_fb;
+
+		ret = drmModePageFlip(data->fd, output->config.crtc->crtc_id,
+				      data->flip_fb->fb_id,
+				      DRM_MODE_PAGE_FLIP_EVENT, NULL);
+		igt_assert_eq(ret, 0);
+		kmstest_wait_for_pageflip(data->fd);
+
+		/* Do some page flips and let replay enable */
+		page_flip_test(data, output, TEST_MODE_FLIP_ONLY,
+			       FLIP_FRAME_BEFORE_TEST);
+
+		/* Panel Replay state takes time to settle on static screen */
+		sleep(1);
+		ret = igt_amd_read_replay_coasting_vtotal(data->fd,
+							  output->name,
+							  &static_coasting_vtotal);
+		igt_assert_f(ret == 0,
+			     "failed to read static-mode coasting vtotal: %s\n",
+			     strerror(-ret));
+
+		/* Drive page flips to put replay into live mode */
+		page_flip_test(data, output, TEST_MODE_FLIP_ONLY, 20);
+		ret = igt_amd_read_replay_coasting_vtotal(data->fd,
+							  output->name,
+							  &live_coasting_vtotal);
+		igt_assert_f(ret == 0,
+			     "failed to read live-mode coasting vtotal: %s\n",
+			     strerror(-ret));
+
+		igt_fail_on_f(static_coasting_vtotal <= live_coasting_vtotal,
+			      "coasting vtotal in static (%u) must be > live (%u)\n",
+			      static_coasting_vtotal, live_coasting_vtotal);
+
+		igt_remove_fb(data->fd, &data->ref_fb);
+		igt_remove_fb(data->fd, &data->ref_fb2);
+		data->ref_fb.fb_id = 0;
+		data->ref_fb2.fb_id = 0;
+	}
+
+	test_fini(data);
+}
+
 static int opt_handler(int option, int option_index, void *data)
 {
 	switch (option) {
@@ -454,6 +539,9 @@ int igt_main_args("", long_options, help_str, opt_handler, NULL)
 	igt_describe("Test whether Panel Replay can be enabled after resume from suspend");
 	igt_subtest("replay_suspend") run_check_replay_suspend(&data);
 
+	igt_describe("Test whether Panel Replay can be enabled with rate control mode");
+	igt_subtest("replay_rate_control") run_check_replay_rate_control(&data);
+
 	igt_fixture()
 	{
 		if (opt.visual_confirm) {
-- 
2.43.0


  parent reply	other threads:[~2026-05-13  7:07 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-05-13  7:03 [PATCH i-g-t, v2 0/2] tests/amdgpu/amd_replay: Add Replay Rate Control test Ray Wu
2026-05-13  7:03 ` [PATCH i-g-t, v2 1/2] tests/amdgpu/amd_replay: fix operator precedence and typos Ray Wu
2026-05-13  7:03 ` Ray Wu [this message]
2026-05-13 16:43 ` ✓ i915.CI.BAT: success for tests/amdgpu/amd_replay: Add Replay Rate Control test (rev2) Patchwork
2026-05-13 17:50 ` ✓ Xe.CI.BAT: " Patchwork
2026-05-14 15:45 ` ✗ Xe.CI.FULL: failure " Patchwork
2026-05-14 17:13 ` ✗ i915.CI.Full: " Patchwork

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260513070631.3723466-3-ray.wu@amd.com \
    --to=ray.wu@amd.com \
    --cc=alex.hung@amd.com \
    --cc=chiahsuan.chung@amd.com \
    --cc=igt-dev@lists.freedesktop.org \
    --cc=sunpeng.li@amd.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox