[PATCH] tools/amd_hdmi_compliance: Add ALLM content type toggle test

Fangzhi Zuo <[email protected]> Tue, 4 Aug 2026 15:47:35 -0400
Newsgroups org.freedesktop.lists.igt-dev
Message-ID <[email protected]>
Add a new '-a' option that exercises HDMI ALLM (HF1-56 steps 14/15) by
toggling the connector 'content type' property between No Data and Game
on a live pipe without tearing down the link.

A full atomic modeset is performed once, then each toggle is first
attempted as a property-only atomic commit. If the kernel rejects it,
the change is retried with DRM_MODE_ATOMIC_ALLOW_MODESET. The path taken
is reported so the operator can tell whether ALLM was toggled in-band
(seamless, link stays up) or required a modeset that blanks/retrains the
link.

Signed-off-by: Fangzhi Zuo <[email protected]>
---
 tools/amd_hdmi_compliance.c | 97 +++++++++++++++++++++++++++++++++++--
 1 file changed, 94 insertions(+), 3 deletions(-)

diff --git a/tools/amd_hdmi_compliance.c b/tools/amd_hdmi_compliance.c
index 0bc77e092..2af4c0bab 100644
--- a/tools/amd_hdmi_compliance.c
+++ b/tools/amd_hdmi_compliance.c
@@ -913,7 +913,91 @@ static void test_vic_mode(data_t *data, int vic, int conn_id)
 	test_fini(data);
 }
 
-const char *optstr = "hvt:i:b:y:e:";
+/*
+ * Toggle HDMI content type (No Data <-> Game) on a live pipe to exercise HDMI
+ * ALLM (HF1-56 steps 14/15) without tearing down the link.
+ *
+ * A full atomic modeset is performed once, then each toggle is first attempted
+ * as a property-only atomic commit (no DRM_MODE_ATOMIC_ALLOW_MODESET). If the
+ * kernel rejects it, the change is retried with ALLOW_MODESET. The path taken
+ * is reported:
+ *   [seamless, no modeset] -> ALLM toggled in-band, link stays up
+ *   [MODESET required]     -> content-type change blanks/retrains the link
+ */
+static void test_allm_toggle(data_t *data, int conn_id)
+{
+	igt_display_t *display = &data->display;
+	drmModeModeInfo *mode;
+	uint32_t connector_id, prop_id;
+	uint64_t cur_ct = 0;
+	igt_fb_t afb;
+	char line[16];
+	int game = 0;
+
+	test_init(data, conn_id);
+
+	connector_id = data->output->config.connector->connector_id;
+	igt_require_f(kmstest_get_property(data->fd, connector_id,
+					   DRM_MODE_OBJECT_CONNECTOR,
+					   "content type", &prop_id, &cur_ct,
+					   NULL),
+		      "connector has no 'content type' property\n");
+
+	/* Initial full atomic modeset with a test pattern. */
+	mode = igt_output_get_mode(data->output);
+	igt_info("Using connector id %d, mode %s\n", connector_id, mode->name);
+	igt_create_pattern_fb(data->fd, mode->hdisplay, mode->vdisplay,
+			      DRM_FORMAT_XRGB8888, 0, &afb);
+	igt_plane_set_fb(data->primary, &afb);
+	igt_display_commit_atomic(display, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
+
+	igt_info("\nMode set. content type = No Data (0).\n");
+	igt_info("Press [Enter] to toggle Game <-> No Data, 'q'+[Enter] to quit.\n\n");
+
+	while (fgets(line, sizeof(line), stdin)) {
+		drmModeAtomicReq *req;
+		uint64_t val;
+		int ret;
+
+		if (line[0] == 'q')
+			break;
+
+		game = !game;
+		val = game ? DRM_MODE_CONTENT_TYPE_GAME :
+			     DRM_MODE_CONTENT_TYPE_NO_DATA;
+
+		req = drmModeAtomicAlloc();
+		igt_assert(req);
+		drmModeAtomicAddProperty(req, connector_id, prop_id, val);
+
+		/* Try a seamless, property-only commit first. */
+		ret = drmModeAtomicCommit(data->fd, req, 0, NULL);
+		if (ret) {
+			int nomodeset_err = errno;
+
+			ret = drmModeAtomicCommit(data->fd, req,
+						  DRM_MODE_ATOMIC_ALLOW_MODESET,
+						  NULL);
+			igt_info("content type = %-7s (%llu)  [MODESET required, link blanked] (nomodeset err: %s)\n",
+				 game ? "Game" : "No Data",
+				 (unsigned long long)val,
+				 strerror(nomodeset_err));
+		} else {
+			igt_info("content type = %-7s (%llu)  [seamless, no modeset]\n",
+				 game ? "Game" : "No Data",
+				 (unsigned long long)val);
+		}
+		if (ret)
+			igt_warn("commit failed: %s\n", strerror(errno));
+
+		drmModeAtomicFree(req);
+	}
+
+	igt_remove_fb(data->fd, &afb);
+	test_fini(data);
+}
+
+const char *optstr = "hvt:i:b:y:e:a";
 static void usage(const char *name)
 {
 	igt_info("Usage: %s options\n", name);
@@ -924,7 +1008,8 @@ static void usage(const char *name)
 	igt_info("-b 6|8|10|12|16	Set 6|8|10|12|16 bpc\n");
 	igt_info("-y 1|2|3|4	Set RGB|YUV422|YUV444|YUV420\n");
 	igt_info("-e seconds    number of seconds to display test pattern and exit\n");
-	igt_info("NOTE: if -i is not specified, first connected HDMI connector will be used for -t, -b and -y\n");
+	igt_info("-a            Toggle HDMI content type (ALLM) Game <-> No Data on a live pipe\n");
+	igt_info("NOTE: if -i is not specified, first connected HDMI connector will be used for -t, -b, -y and -a\n");
 }
 
 int main(int argc, char **argv)
@@ -935,6 +1020,7 @@ int main(int argc, char **argv)
 	int conn_id = 0;
 	int max_bpc = 0;
 	int pixel_format = PIXEL_ENCODING_UNKNOWN;
+	bool allm = false;
 
 	memset(&data, 0, sizeof(data));
 
@@ -958,6 +1044,9 @@ int main(int argc, char **argv)
 		case 'e':
 			data.timeout_seconds = atoi(optarg);
 			break;
+		case 'a':
+			allm = true;
+			break;
 		default:
 		case 'h':
 			usage(argv[0]);
@@ -979,7 +1068,9 @@ int main(int argc, char **argv)
 	if (max_bpc)
 		set_max_bpc(&data, max_bpc, conn_id);
 
-	if (vic >= 0) {
+	if (allm) {
+		test_allm_toggle(&data, conn_id);
+	} else if (vic >= 0) {
 		if (vic > ARRAY_SIZE(test_modes) || !test_modes[vic].name[0])
 			igt_warn("VIC %d is not supported\n", vic);
 		else
-- 
2.53.0