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

Harry Wentland <[email protected]> Wed, 5 Aug 2026 15:15:45 -0400
Newsgroups org.freedesktop.lists.igt-dev
Message-ID <[email protected]>

On 2026-08-04 15:47, Fangzhi Zuo wrote:
> 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]>

Reviewed-by: Harry Wentland <[email protected]>

Harry

> ---
>   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