public inbox for igt-dev@lists.freedesktop.org
 help / color / mirror / Atom feed
From: Mohammed Bilal <mohammed.bilal@intel.com>
To: igt-dev@lists.freedesktop.org
Cc: kunal1.joshi@intel.com, Mohammed Bilal <mohammed.bilal@intel.com>
Subject: [PATCH i-g-t v1 21/25] tests/chamelium/v3: Add HPD (Hot Plug Detect) tests for Chamelium v3
Date: Tue, 28 Apr 2026 10:16:30 +0530	[thread overview]
Message-ID: <20260428044644.257001-22-mohammed.bilal@intel.com> (raw)
In-Reply-To: <20260428044644.257001-1-mohammed.bilal@intel.com>

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=yes, Size: 29293 bytes --]

This commit adds comprehensive HPD testing for Chamelium v3 including
plug/unplug detection, suspend/resume tests, and connector state
verification.

Signed-off-by: Mohammed Bilal <mohammed.bilal@intel.com>
---
 tests/chamelium/v3/kms_chamelium_v3_hpd.c | 928 ++++++++++++++++++++++
 tests/meson.build                         |   1 +
 2 files changed, 929 insertions(+)
 create mode 100644 tests/chamelium/v3/kms_chamelium_v3_hpd.c

diff --git a/tests/chamelium/v3/kms_chamelium_v3_hpd.c b/tests/chamelium/v3/kms_chamelium_v3_hpd.c
new file mode 100644
index 000000000..f523dd176
--- /dev/null
+++ b/tests/chamelium/v3/kms_chamelium_v3_hpd.c
@@ -0,0 +1,928 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2026 Intel Corporation
+ *
+ * Chamelium V3 HPD (Hot Plug Detect) Tests
+ *
+ * This test reads [Chamelium:<connector>] port mappings from .igtrc,
+ * maps each Chamelium port to a specific DRM connector ID, and verifies
+ * that plug/unplug operations produce real kernel uevents AND real
+ * DRM connector status transitions.
+ */
+
+#include <errno.h>
+#include <poll.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <libudev.h>
+
+#include "igt.h"
+#include "igt_sysfs.h"
+#include "chamelium/v3/igt_chamelium.h"
+
+/**
+ * TEST: kms chamelium v3 hpd
+ * Category: Display
+ * Description: HPD tests for Chamelium V3
+ * Driver requirement: i915, xe
+ * Mega feature: General Display Features
+ */
+
+/**
+ * SUBTEST: dp-hpd
+ * Description: Check that we get uevents and updated connector status on
+ *              hotplug and unplug
+ *
+ * SUBTEST: dp-hpd-fast
+ * Description: Check that we get uevents and updated connector status on
+ *              hotplug and unplug
+ *
+ * SUBTEST: dp-hpd-enable-disable-mode
+ * Description: Check that we get uevents and updated connector status on
+ *              hotplug and unplug with modeset toggle
+ *
+ * SUBTEST: dp-hpd-with-enabled-mode
+ * Description: Check that we get uevents and updated connector status on
+ *              hotplug and unplug with enabled mode
+ *
+ * SUBTEST: dp-hpd-for-each-pipe
+ * Description: Check that we get uevents and updated connector status on
+ *              hotplug and unplug for each pipe with valid output
+ *
+ * SUBTEST: dp-hpd-after-suspend
+ * Description: Toggle HPD during Suspend, check that uevents are sent and
+ *              connector status is updated
+ *
+ * SUBTEST: dp-hpd-after-hibernate
+ * Description: Toggle HPD during Hibernation, check that uevents are sent and
+ *              connector status is updated
+ *
+ * SUBTEST: dp-hpd-storm
+ * Description: Trigger a series of hotplugs in a very small timeframe to
+ *              simulate a bad cable, check the kernel falls back to polling
+ *
+ * SUBTEST: dp-hpd-storm-disable
+ * Description: Disable HPD storm detection, trigger a storm and check the
+ *              kernel doesn't detect one
+ *
+ * SUBTEST: hdmi-hpd
+ * Description: Check that we get uevents and updated connector status on
+ *              hotplug and unplug
+ *
+ * SUBTEST: hdmi-hpd-fast
+ * Description: Check that we get uevents and updated connector status on
+ *              hotplug and unplug
+ *
+ * SUBTEST: hdmi-hpd-enable-disable-mode
+ * Description: Check that we get uevents and updated connector status on
+ *              hotplug and unplug with modeset toggle
+ *
+ * SUBTEST: hdmi-hpd-with-enabled-mode
+ * Description: Check that we get uevents and updated connector status on
+ *              hotplug and unplug with enabled mode
+ *
+ * SUBTEST: hdmi-hpd-for-each-pipe
+ * Description: Check that we get uevents and updated connector status on
+ *              hotplug and unplug for each pipe with valid output
+ *
+ * SUBTEST: hdmi-hpd-after-suspend
+ * Description: Toggle HPD during Suspend, check that uevents are sent and
+ *              connector status is updated
+ *
+ * SUBTEST: hdmi-hpd-after-hibernate
+ * Description: Toggle HPD during Hibernation, check that uevents are sent and
+ *              connector status is updated
+ *
+ * SUBTEST: hdmi-hpd-storm
+ * Description: Trigger a series of hotplugs in a very small timeframe to
+ *              simulate a bad cable, check the kernel falls back to polling
+ *
+ * SUBTEST: hdmi-hpd-storm-disable
+ * Description: Disable HPD storm detection, trigger a storm and check the
+ *              kernel doesn't detect one
+ *
+ */
+
+#define HPD_STORM_PULSE_INTERVAL_DP	100 /* ms */
+#define HPD_STORM_PULSE_INTERVAL_HDMI	200 /* ms */
+
+#define HPD_TOGGLE_COUNT_DP_HDMI	15
+#define HPD_TOGGLE_COUNT_FAST		3
+
+#define CHAMELIUM_HOTPLUG_TIMEOUT	20 /* seconds */
+
+enum test_modeset_mode {
+	TEST_MODESET_ON,
+	TEST_MODESET_ON_OFF,
+	TEST_MODESET_OFF,
+};
+
+/*
+ * Test data structure — holds everything needed for HPD tests.
+ * Includes Chamelium RPC handle, DRM fd, AND proper port-to-connector mappings.
+ */
+struct hpd_test_data {
+	struct igt_chamelium_v3 *chamelium;
+	int drm_fd;
+
+	/* Port-to-connector mappings from .igtrc */
+	struct chamelium_v3_drm_port *mapped_ports;
+	int mapped_port_count;
+};
+
+
+/*
+ * find_dp_port - Find first mapped DP port
+ */
+static struct chamelium_v3_drm_port *find_dp_port(struct hpd_test_data *data)
+{
+	for (int i = 0; i < data->mapped_port_count; i++) {
+		if (data->mapped_ports[i].connector_type ==
+		    DRM_MODE_CONNECTOR_DisplayPort)
+			return &data->mapped_ports[i];
+	}
+	return NULL;
+}
+
+/*
+ * find_hdmi_port - Find first mapped HDMI port
+ */
+static struct chamelium_v3_drm_port *find_hdmi_port(struct hpd_test_data *data)
+{
+	for (int i = 0; i < data->mapped_port_count; i++) {
+		if (data->mapped_ports[i].connector_type ==
+		    DRM_MODE_CONNECTOR_HDMIA)
+			return &data->mapped_ports[i];
+	}
+	return NULL;
+}
+
+
+/*
+ * plug_port - Plug a Chamelium port with ApplyEdid first (required for V3 DP)
+ *
+ * In V3, DP connectors only become connected after ApplyEdid + Plug.
+ */
+static void plug_port(struct hpd_test_data *data, chamelium_v3_port_id port_id)
+{
+	chamelium_v3_apply_edid(data->chamelium, port_id, 0);
+	/* DP link training needs time after EDID is applied */
+	usleep(500000);
+	chamelium_v3_plug(data->chamelium, port_id);
+}
+
+/*
+ * plug_all_mapped_ports - Plug all mapped Chamelium ports and wait for them.
+ *
+ * Called before each subtest to ensure all ports are in a known-good
+ * (plugged) state, regardless of what a prior subtest did.
+ */
+static void plug_all_mapped_ports(struct hpd_test_data *data)
+{
+	for (int i = 0; i < data->mapped_port_count; i++) {
+		plug_port(data, data->mapped_ports[i].port_id);
+	}
+	/* Give connectors time to settle */
+	sleep(2);
+}
+
+
+/*
+ * get_connector - Get a fresh drmModeConnector for a mapped port.
+ * This calls drmModeGetConnector() which forces kernel to reprobe.
+ * Caller must free with drmModeFreeConnector().
+ */
+static drmModeConnector *get_connector(struct hpd_test_data *data,
+				       struct chamelium_v3_drm_port *port)
+{
+	return drmModeGetConnector(data->drm_fd, port->connector_id);
+}
+
+
+/*
+ * wait_hotplug_for_connector - Wait for a HOTPLUG uevent for a specific connector.
+ *
+ * Unlike igt_hotplug_detected() which matches ANY hotplug uevent, this
+ * matches HOTPLUG=1 AND CONNECTOR=<connector_id>. This prevents uevents
+ * from other connectors (e.g. HDMI) being consumed when testing DP.
+ *
+ * Returns true if a matching uevent was received, false on timeout.
+ * Updates *timeout with remaining time.
+ */
+static bool wait_hotplug_for_connector(struct udev_monitor *mon,
+				       uint32_t connector_id,
+				       int *timeout)
+{
+	struct pollfd fd = {
+		.fd = udev_monitor_get_fd(mon),
+		.events = POLLIN
+	};
+	struct timespec start, now;
+
+	igt_assert_eq(igt_gettime(&start), 0);
+
+	while (*timeout > 0) {
+		struct udev_device *dev;
+		const char *hotplug_val, *conn_val;
+		int elapsed;
+
+		if (!poll(&fd, 1, *timeout * 1000))
+			break;
+
+		dev = udev_monitor_receive_device(mon);
+		if (!dev)
+			continue;
+
+		hotplug_val = udev_device_get_property_value(dev, "HOTPLUG");
+		conn_val = udev_device_get_property_value(dev, "CONNECTOR");
+
+		igt_debug("uevent: HOTPLUG=%s CONNECTOR=%s (want %u)\n",
+			  hotplug_val ? hotplug_val : "null",
+			  conn_val ? conn_val : "null",
+			  connector_id);
+
+		if (hotplug_val && atoi(hotplug_val) == 1 &&
+		    conn_val && (uint32_t)atoi(conn_val) == connector_id) {
+			udev_device_unref(dev);
+
+			igt_assert_eq(igt_gettime(&now), 0);
+			elapsed = igt_time_elapsed(&start, &now);
+			*timeout = max(0, *timeout - elapsed);
+
+			return true;
+		}
+
+		udev_device_unref(dev);
+
+		igt_assert_eq(igt_gettime(&now), 0);
+		elapsed = igt_time_elapsed(&start, &now);
+		*timeout = max(0, *timeout - elapsed);
+	}
+
+	return false;
+}
+
+/*
+ * wait_for_hotplug_and_check - Wait for uevent for THIS connector,
+ *                              then verify DRM connector status.
+ */
+static void wait_for_hotplug_and_check(struct hpd_test_data *data,
+				       struct udev_monitor *mon,
+				       struct chamelium_v3_drm_port *port,
+				       drmModeConnection expected)
+{
+	int timeout = CHAMELIUM_HOTPLUG_TIMEOUT;
+	int hotplug_count = 0;
+	drmModeConnection current;
+
+	igt_debug("Waiting for %s (connector_id=%u) to become %s after hotplug...\n",
+		  port->connector_name, port->connector_id,
+		  expected == DRM_MODE_CONNECTED ? "connected" : "disconnected");
+
+	while (timeout > 0) {
+		bool detected;
+
+		detected = wait_hotplug_for_connector(mon, port->connector_id,
+						      &timeout);
+
+		if (!detected)
+			break;
+
+		hotplug_count++;
+
+		/* After uevent for OUR connector, reprobe it */
+		current = chamelium_v3_reprobe_connector(data->drm_fd,
+							 port->connector_id);
+		if (current == expected) {
+			igt_debug("%s is now %s (after %d uevents)\n",
+				  port->connector_name,
+				  expected == DRM_MODE_CONNECTED ?
+				  "connected" : "disconnected",
+				  hotplug_count);
+			return;
+		}
+	}
+
+	/*
+	 * If we timed out waiting for a uevent, check the connector status
+	 * directly. If it's already in the expected state, the uevent was
+	 * likely consumed or coalesced by the kernel — treat as success.
+	 */
+	current = chamelium_v3_reprobe_connector(data->drm_fd,
+						 port->connector_id);
+	if (current == expected) {
+		igt_debug("%s is already %s (uevent missed, got %d uevents) — accepting\n",
+			  port->connector_name,
+			  expected == DRM_MODE_CONNECTED ?
+			  "connected" : "disconnected",
+			  hotplug_count);
+		return;
+	}
+
+	igt_assert_f(false,
+		     "Timed out waiting for %s to become %s after hotplug. "
+		     "Got %d uevents for connector %u, connector is: %s\n",
+		     port->connector_name,
+		     expected == DRM_MODE_CONNECTED ? "connected" : "disconnected",
+		     hotplug_count, port->connector_id,
+		     current == DRM_MODE_CONNECTED ? "connected" :
+		     current == DRM_MODE_DISCONNECTED ? "disconnected" : "unknown");
+}
+
+
+/*
+ * reset_port - Unplug only the specific port being tested and wait for it
+ *              to become disconnected.
+ *
+ * Unlike Reset() which resets ALL Chamelium ports (causing other connectors
+ * like HDMI to disconnect when testing DP), this only unplugs the target port.
+ */
+static void reset_port(struct hpd_test_data *data,
+		       struct chamelium_v3_drm_port *port)
+{
+	chamelium_v3_unplug(data->chamelium, port->port_id);
+
+	igt_debug("Waiting for %s to become disconnected after unplug...\n",
+		  port->connector_name);
+
+	igt_assert_f(
+		chamelium_v3_wait_for_conn_status_change(
+			data->drm_fd, port->connector_id,
+			DRM_MODE_DISCONNECTED, CHAMELIUM_HOTPLUG_TIMEOUT),
+		"Connector %s did not become disconnected after unplug\n",
+		port->connector_name);
+}
+
+
+static uint32_t find_crtc_for_connector(int drm_fd, drmModeConnector *conn)
+{
+	drmModeRes *res;
+	drmModeEncoder *enc;
+	uint32_t crtc_id = 0;
+
+	res = drmModeGetResources(drm_fd);
+	if (!res)
+		return 0;
+
+	for (int i = 0; i < conn->count_encoders && !crtc_id; i++) {
+		enc = drmModeGetEncoder(drm_fd, conn->encoders[i]);
+		if (!enc)
+			continue;
+		for (int j = 0; j < res->count_crtcs && !crtc_id; j++) {
+			if (enc->possible_crtcs & (1 << j))
+				crtc_id = res->crtcs[j];
+		}
+		drmModeFreeEncoder(enc);
+	}
+
+	drmModeFreeResources(res);
+	return crtc_id;
+}
+
+static bool enable_modeset(int drm_fd, drmModeConnector *conn, struct igt_fb *fb)
+{
+	uint32_t crtc_id, conn_id;
+	int ret;
+
+	crtc_id = find_crtc_for_connector(drm_fd, conn);
+	if (!crtc_id)
+		return false;
+
+	conn_id = conn->connector_id;
+
+	if (fb->fb_id == 0) {
+		igt_create_color_fb(drm_fd,
+				    conn->modes[0].hdisplay,
+				    conn->modes[0].vdisplay,
+				    DRM_FORMAT_XRGB8888,
+				    DRM_FORMAT_MOD_LINEAR,
+				    1.0, 0.0, 0.0, fb);
+	}
+
+	ret = drmModeSetCrtc(drm_fd, crtc_id, fb->fb_id, 0, 0,
+			     &conn_id, 1, &conn->modes[0]);
+	return ret == 0;
+}
+
+static void disable_modeset(int drm_fd, drmModeConnector *conn)
+{
+	uint32_t crtc_id = find_crtc_for_connector(drm_fd, conn);
+
+	if (crtc_id)
+		drmModeSetCrtc(drm_fd, crtc_id, 0, 0, 0, NULL, 0, NULL);
+}
+
+
+/*
+ * test_hotplug - Generic hotplug toggle test
+ *
+ * 1. Reset all ports and verify all connectors show disconnected
+ * 2. For each toggle cycle:
+ *    a. Plug → wait for uevent → verify connector is CONNECTED
+ *    b. Optional modeset
+ *    c. Unplug → wait for uevent → verify connector is DISCONNECTED
+ */
+static void test_hotplug(struct hpd_test_data *data,
+			 struct chamelium_v3_drm_port *port,
+			 int toggle_count,
+			 enum test_modeset_mode modeset_mode)
+{
+	struct udev_monitor *mon;
+	struct igt_fb fb = { 0 };
+	drmModeConnector *conn = NULL;
+
+	igt_info("Testing hotplug on %s (chamelium port %d, connector_id %u, "
+		 "%d toggles, modeset=%d)\n",
+		 port->connector_name, port->port_id, port->connector_id,
+		 toggle_count, modeset_mode);
+
+	/* Unplug the port under test and confirm it becomes disconnected */
+	reset_port(data, port);
+
+	igt_hpd_storm_set_threshold(data->drm_fd, 0);
+
+	/* Let any straggling uevents settle before we start.
+	 * DP connectors need extra time after unplug for the link
+	 * to fully de-train before re-plugging.
+	 */
+	usleep(3000000);
+
+	for (int i = 0; i < toggle_count; i++) {
+		/*
+		 * Create a fresh uevent monitor right before each action.
+		 * Flush aggressively — any uevent that arrived before this
+		 * point (from prior operations or other ports) must be drained.
+		 */
+		mon = igt_watch_uevents();
+		usleep(100000);
+		igt_flush_uevents(mon);
+
+		/* Plug */
+		plug_port(data, port->port_id);
+		wait_for_hotplug_and_check(data, mon, port, DRM_MODE_CONNECTED);
+		igt_cleanup_uevents(mon);
+
+		/* Optional modeset */
+		if (modeset_mode == TEST_MODESET_ON_OFF ||
+		    (modeset_mode == TEST_MODESET_ON && i == 0)) {
+			if (conn)
+				drmModeFreeConnector(conn);
+			conn = get_connector(data, port);
+			if (conn && conn->count_modes > 0)
+				enable_modeset(data->drm_fd, conn, &fb);
+		}
+
+		/* Fresh monitor for unplug */
+		mon = igt_watch_uevents();
+		usleep(100000);
+		igt_flush_uevents(mon);
+
+		/* Unplug */
+		chamelium_v3_unplug(data->chamelium, port->port_id);
+		wait_for_hotplug_and_check(data, mon, port, DRM_MODE_DISCONNECTED);
+		igt_cleanup_uevents(mon);
+
+		if (modeset_mode == TEST_MODESET_ON_OFF && conn)
+			disable_modeset(data->drm_fd, conn);
+	}
+
+	/*
+	 * Re-plug the port at the end and verify it becomes connected.
+	 * This restores the original state AND serves as the final
+	 * verification that the connector truly responds to hotplug.
+	 */
+	mon = igt_watch_uevents();
+	usleep(100000);
+	igt_flush_uevents(mon);
+	plug_port(data, port->port_id);
+	wait_for_hotplug_and_check(data, mon, port, DRM_MODE_CONNECTED);
+	igt_cleanup_uevents(mon);
+
+	/* Final explicit check: the DRM connector MUST be connected now.
+	 * After many rapid toggles the connector may need a moment to
+	 * stabilize, so poll for up to 5 seconds.
+	 */
+	igt_assert_f(
+		chamelium_v3_wait_for_conn_status_change(
+			data->drm_fd, port->connector_id,
+			DRM_MODE_CONNECTED, 5),
+		"%s (connector_id=%u) is NOT connected after final re-plug. "
+		"HPD is not working correctly!\n",
+		port->connector_name, port->connector_id);
+
+	igt_info("%s: final state is CONNECTED - test PASSED\n",
+		 port->connector_name);
+
+	igt_hpd_storm_reset(data->drm_fd);
+
+	if (fb.fb_id)
+		igt_remove_fb(data->drm_fd, &fb);
+	if (conn)
+		drmModeFreeConnector(conn);
+}
+
+/*
+ * test_hotplug_for_each_pipe - Test HPD across each CRTC/pipe
+ *
+ * For each pipe, plug → verify connected → modeset → unplug → verify disconnected
+ */
+static void test_hotplug_for_each_pipe(struct hpd_test_data *data,
+				       struct chamelium_v3_drm_port *port)
+{
+	struct udev_monitor *mon;
+	drmModeRes *res;
+
+	igt_info("Testing hotplug for each pipe on %s (port %d)\n",
+		 port->connector_name, port->port_id);
+
+	reset_port(data, port);
+	igt_hpd_storm_set_threshold(data->drm_fd, 0);
+
+	/* Let straggling uevents settle */
+	usleep(1000000);
+
+	res = drmModeGetResources(data->drm_fd);
+	igt_assert(res);
+
+	for (int j = 0; j < res->count_crtcs; j++) {
+		struct igt_fb fb = { 0 };
+		uint32_t crtc_id = res->crtcs[j];
+		drmModeConnector *conn;
+
+		igt_info("  Testing pipe/CRTC %d (id=%u)\n", j, crtc_id);
+
+		/* Fresh monitor for plug */
+		mon = igt_watch_uevents();
+		usleep(100000);
+		igt_flush_uevents(mon);
+
+		/* Plug */
+		plug_port(data, port->port_id);
+		wait_for_hotplug_and_check(data, mon, port, DRM_MODE_CONNECTED);
+		igt_cleanup_uevents(mon);
+
+		/* Modeset on this CRTC */
+		conn = get_connector(data, port);
+		if (conn && conn->count_modes > 0) {
+			drmModeEncoder *enc;
+			bool crtc_valid = false;
+
+			for (int e = 0; e < conn->count_encoders; e++) {
+				enc = drmModeGetEncoder(data->drm_fd,
+							conn->encoders[e]);
+				if (enc && (enc->possible_crtcs & (1 << j))) {
+					crtc_valid = true;
+					drmModeFreeEncoder(enc);
+					break;
+				}
+				if (enc)
+					drmModeFreeEncoder(enc);
+			}
+
+			if (crtc_valid) {
+				uint32_t conn_id = conn->connector_id;
+
+				igt_create_color_fb(data->drm_fd,
+						    conn->modes[0].hdisplay,
+						    conn->modes[0].vdisplay,
+						    DRM_FORMAT_XRGB8888,
+						    DRM_FORMAT_MOD_LINEAR,
+						    0.0, 1.0, 0.0, &fb);
+				drmModeSetCrtc(data->drm_fd, crtc_id,
+					       fb.fb_id, 0, 0,
+					       &conn_id, 1, &conn->modes[0]);
+				igt_info("    Modeset on CRTC %u: OK\n", crtc_id);
+			}
+			drmModeFreeConnector(conn);
+		}
+
+		/* Fresh monitor for unplug */
+		mon = igt_watch_uevents();
+		usleep(100000);
+		igt_flush_uevents(mon);
+
+		/* Unplug */
+		chamelium_v3_unplug(data->chamelium, port->port_id);
+		wait_for_hotplug_and_check(data, mon, port, DRM_MODE_DISCONNECTED);
+		igt_cleanup_uevents(mon);
+
+		if (fb.fb_id)
+			igt_remove_fb(data->drm_fd, &fb);
+	}
+
+	/*
+	 * Re-plug the port and verify connected as final check.
+	 */
+	mon = igt_watch_uevents();
+	usleep(100000);
+	igt_flush_uevents(mon);
+	plug_port(data, port->port_id);
+	wait_for_hotplug_and_check(data, mon, port, DRM_MODE_CONNECTED);
+	igt_cleanup_uevents(mon);
+
+	igt_assert_f(
+		chamelium_v3_reprobe_connector(data->drm_fd, port->connector_id)
+			== DRM_MODE_CONNECTED,
+		"%s (connector_id=%u) is NOT connected after final re-plug. "
+		"HPD is not working correctly!\n",
+		port->connector_name, port->connector_id);
+
+	igt_info("%s: final state is CONNECTED - test PASSED\n",
+		 port->connector_name);
+
+	drmModeFreeResources(res);
+	igt_hpd_storm_reset(data->drm_fd);
+}
+
+/*
+ * test_suspend_resume_hpd - Toggle HPD during suspend/hibernate
+ *
+ * V3 does not yet support schedule_hpd_toggle, so these skip.
+ */
+static void test_suspend_resume_hpd(struct hpd_test_data *data,
+				    struct chamelium_v3_drm_port *port,
+				    const char *state_name)
+{
+	igt_skip("V3 Chamelium does not yet support schedule_hpd_toggle "
+		 "required for %s HPD testing\n", state_name);
+}
+
+/*
+ * test_hpd_storm_detect - Fire rapid HPD pulses, verify kernel detects storm
+ */
+static void test_hpd_storm_detect(struct hpd_test_data *data,
+				  struct chamelium_v3_drm_port *port,
+				  int pulse_interval_ms)
+{
+	igt_info("Testing HPD storm detection on %s (port %d, interval=%dms)\n",
+		 port->connector_name, port->port_id, pulse_interval_ms);
+
+	igt_require_hpd_storm_ctl(data->drm_fd);
+
+	reset_port(data, port);
+
+	/* Plug the port first */
+	plug_port(data, port->port_id);
+	igt_assert_f(
+		chamelium_v3_wait_for_conn_status_change(
+			data->drm_fd, port->connector_id,
+			DRM_MODE_CONNECTED, CHAMELIUM_HOTPLUG_TIMEOUT),
+		"Connector %s did not become connected after plug\n",
+		port->connector_name);
+
+	/* Set threshold and fire pulses */
+	igt_hpd_storm_set_threshold(data->drm_fd, 1);
+	chamelium_v3_fire_hpd_pulse(data->chamelium, port->port_id,
+				    pulse_interval_ms * 1000,
+				    pulse_interval_ms * 1000,
+				    10, 1);
+
+	igt_assert(igt_hpd_storm_detected(data->drm_fd));
+
+	igt_hpd_storm_reset(data->drm_fd);
+}
+
+/*
+ * test_hpd_storm_disable - Disable storm detection, verify no false detection
+ */
+static void test_hpd_storm_disable(struct hpd_test_data *data,
+				   struct chamelium_v3_drm_port *port,
+				   int pulse_interval_ms)
+{
+	igt_info("Testing HPD storm disable on %s (port %d, interval=%dms)\n",
+		 port->connector_name, port->port_id, pulse_interval_ms);
+
+	igt_require_hpd_storm_ctl(data->drm_fd);
+
+	reset_port(data, port);
+
+	/* Disable storm detection */
+	igt_hpd_storm_set_threshold(data->drm_fd, 0);
+
+	/* Plug the port first */
+	plug_port(data, port->port_id);
+	igt_assert_f(
+		chamelium_v3_wait_for_conn_status_change(
+			data->drm_fd, port->connector_id,
+			DRM_MODE_CONNECTED, CHAMELIUM_HOTPLUG_TIMEOUT),
+		"Connector %s did not become connected after plug\n",
+		port->connector_name);
+
+	/* Fire pulses */
+	chamelium_v3_fire_hpd_pulse(data->chamelium, port->port_id,
+				    pulse_interval_ms * 1000,
+				    pulse_interval_ms * 1000,
+				    10, 1);
+
+	/* Storm should NOT be detected */
+	igt_assert(!igt_hpd_storm_detected(data->drm_fd));
+
+	igt_hpd_storm_reset(data->drm_fd);
+}
+
+
+int igt_main()
+{
+	struct hpd_test_data data = { 0 };
+	struct chamelium_v3_drm_port *dp_port = NULL, *hdmi_port = NULL;
+
+	/* Setup */
+	igt_fixture() {
+		data.chamelium = chamelium_v3_init_from_config();
+		igt_require_f(data.chamelium,
+			      "Chamelium V3 not available (check .igtrc [Chameliumv3] section)\n");
+
+		data.drm_fd = drm_open_driver_master(DRIVER_ANY);
+		igt_assert_f(data.drm_fd >= 0, "Cannot open DRM device\n");
+
+		kmstest_set_vt_graphics_mode();
+
+		/* Read port-to-connector mappings from .igtrc */
+		igt_require_f(chamelium_v3_read_port_mappings(data.drm_fd,
+							      &data.mapped_ports,
+							      &data.mapped_port_count),
+			      "No port mappings found in .igtrc [Chamelium:<connector>] sections\n");
+
+		igt_info("Found %d port mapping(s):\n", data.mapped_port_count);
+		for (int i = 0; i < data.mapped_port_count; i++) {
+			igt_info("  %s -> chamelium port %d (connector_id=%u)\n",
+				 data.mapped_ports[i].connector_name,
+				 data.mapped_ports[i].port_id,
+				 data.mapped_ports[i].connector_id);
+		}
+
+		dp_port = find_dp_port(&data);
+		hdmi_port = find_hdmi_port(&data);
+	}
+
+
+	igt_fixture() { plug_all_mapped_ports(&data); }
+
+	igt_describe("Check that we get uevents and updated connector status on "
+		     "hotplug and unplug");
+	igt_subtest("dp-hpd") {
+		igt_require_f(dp_port, "No DP port mapped\n");
+		test_hotplug(&data, dp_port, HPD_TOGGLE_COUNT_DP_HDMI,
+			     TEST_MODESET_OFF);
+	}
+
+	igt_fixture() { plug_all_mapped_ports(&data); }
+
+	igt_describe("Check that we get uevents and updated connector status on "
+		     "hotplug and unplug");
+	igt_subtest("dp-hpd-fast") {
+		igt_require_f(dp_port, "No DP port mapped\n");
+		test_hotplug(&data, dp_port, HPD_TOGGLE_COUNT_FAST,
+			     TEST_MODESET_OFF);
+	}
+
+	igt_fixture() { plug_all_mapped_ports(&data); }
+
+	igt_describe("Check hotplug and unplug with modeset enable/disable toggle");
+	igt_subtest("dp-hpd-enable-disable-mode") {
+		igt_require_f(dp_port, "No DP port mapped\n");
+		test_hotplug(&data, dp_port, HPD_TOGGLE_COUNT_FAST,
+			     TEST_MODESET_ON_OFF);
+	}
+
+	igt_fixture() { plug_all_mapped_ports(&data); }
+
+	igt_describe("Check hotplug and unplug with enabled mode");
+	igt_subtest("dp-hpd-with-enabled-mode") {
+		igt_require_f(dp_port, "No DP port mapped\n");
+		test_hotplug(&data, dp_port, HPD_TOGGLE_COUNT_FAST,
+			     TEST_MODESET_ON);
+	}
+
+	igt_fixture() { plug_all_mapped_ports(&data); }
+
+	igt_describe("Check hotplug for each pipe with valid output");
+	igt_subtest("dp-hpd-for-each-pipe") {
+		igt_require_f(dp_port, "No DP port mapped\n");
+		test_hotplug_for_each_pipe(&data, dp_port);
+	}
+
+	igt_fixture() { plug_all_mapped_ports(&data); }
+
+	igt_describe("Toggle HPD during Suspend, check uevents and connector status");
+	igt_subtest("dp-hpd-after-suspend") {
+		igt_require_f(dp_port, "No DP port mapped\n");
+		test_suspend_resume_hpd(&data, dp_port, "suspend");
+	}
+
+	igt_fixture() { plug_all_mapped_ports(&data); }
+
+	igt_describe("Toggle HPD during Hibernation, check uevents and connector status");
+	igt_subtest("dp-hpd-after-hibernate") {
+		igt_require_f(dp_port, "No DP port mapped\n");
+		test_suspend_resume_hpd(&data, dp_port, "hibernate");
+	}
+
+	igt_fixture() { plug_all_mapped_ports(&data); }
+
+	igt_describe("Trigger HPD storm, check kernel detects it");
+	igt_subtest("dp-hpd-storm") {
+		igt_require_f(dp_port, "No DP port mapped\n");
+		test_hpd_storm_detect(&data, dp_port,
+				      HPD_STORM_PULSE_INTERVAL_DP);
+	}
+
+	igt_fixture() { plug_all_mapped_ports(&data); }
+
+	igt_describe("Disable HPD storm detection, verify no false detection");
+	igt_subtest("dp-hpd-storm-disable") {
+		igt_require_f(dp_port, "No DP port mapped\n");
+		test_hpd_storm_disable(&data, dp_port,
+				       HPD_STORM_PULSE_INTERVAL_DP);
+	}
+
+
+	igt_fixture() { plug_all_mapped_ports(&data); }
+
+	igt_describe("Check that we get uevents and updated connector status on "
+		     "hotplug and unplug");
+	igt_subtest("hdmi-hpd") {
+		igt_require_f(hdmi_port, "No HDMI port mapped\n");
+		test_hotplug(&data, hdmi_port, HPD_TOGGLE_COUNT_DP_HDMI,
+			     TEST_MODESET_OFF);
+	}
+
+	igt_fixture() { plug_all_mapped_ports(&data); }
+
+	igt_describe("Check that we get uevents and updated connector status on "
+		     "hotplug and unplug");
+	igt_subtest("hdmi-hpd-fast") {
+		igt_require_f(hdmi_port, "No HDMI port mapped\n");
+		test_hotplug(&data, hdmi_port, HPD_TOGGLE_COUNT_FAST,
+			     TEST_MODESET_OFF);
+	}
+
+	igt_fixture() { plug_all_mapped_ports(&data); }
+
+	igt_describe("Check hotplug and unplug with modeset enable/disable toggle");
+	igt_subtest("hdmi-hpd-enable-disable-mode") {
+		igt_require_f(hdmi_port, "No HDMI port mapped\n");
+		test_hotplug(&data, hdmi_port, HPD_TOGGLE_COUNT_FAST,
+			     TEST_MODESET_ON_OFF);
+	}
+
+	igt_fixture() { plug_all_mapped_ports(&data); }
+
+	igt_describe("Check hotplug and unplug with enabled mode");
+	igt_subtest("hdmi-hpd-with-enabled-mode") {
+		igt_require_f(hdmi_port, "No HDMI port mapped\n");
+		test_hotplug(&data, hdmi_port, HPD_TOGGLE_COUNT_FAST,
+			     TEST_MODESET_ON);
+	}
+
+	igt_fixture() { plug_all_mapped_ports(&data); }
+
+	igt_describe("Check hotplug for each pipe with valid output");
+	igt_subtest("hdmi-hpd-for-each-pipe") {
+		igt_require_f(hdmi_port, "No HDMI port mapped\n");
+		test_hotplug_for_each_pipe(&data, hdmi_port);
+	}
+
+	igt_fixture() { plug_all_mapped_ports(&data); }
+
+	igt_describe("Toggle HPD during Suspend, check uevents and connector status");
+	igt_subtest("hdmi-hpd-after-suspend") {
+		igt_require_f(hdmi_port, "No HDMI port mapped\n");
+		test_suspend_resume_hpd(&data, hdmi_port, "suspend");
+	}
+
+	igt_fixture() { plug_all_mapped_ports(&data); }
+
+	igt_describe("Toggle HPD during Hibernation, check uevents and connector status");
+	igt_subtest("hdmi-hpd-after-hibernate") {
+		igt_require_f(hdmi_port, "No HDMI port mapped\n");
+		test_suspend_resume_hpd(&data, hdmi_port, "hibernate");
+	}
+
+	igt_fixture() { plug_all_mapped_ports(&data); }
+
+	igt_describe("Trigger HPD storm, check kernel detects it");
+	igt_subtest("hdmi-hpd-storm") {
+		igt_require_f(hdmi_port, "No HDMI port mapped\n");
+		test_hpd_storm_detect(&data, hdmi_port,
+				      HPD_STORM_PULSE_INTERVAL_HDMI);
+	}
+
+	igt_fixture() { plug_all_mapped_ports(&data); }
+
+	igt_describe("Disable HPD storm detection, verify no false detection");
+	igt_subtest("hdmi-hpd-storm-disable") {
+		igt_require_f(hdmi_port, "No HDMI port mapped\n");
+		test_hpd_storm_disable(&data, hdmi_port,
+				       HPD_STORM_PULSE_INTERVAL_HDMI);
+	}
+
+	/* Cleanup */
+	igt_fixture() {
+		free(data.mapped_ports);
+		chamelium_v3_uninit(data.chamelium);
+		close(data.drm_fd);
+	}
+}
diff --git a/tests/meson.build b/tests/meson.build
index 80e88954a..31ca2d963 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -374,6 +374,7 @@ chamelium_v2_progs = [
 
 chamelium_v3_progs = [
 	'kms_chamelium_v3_basic',
+	'kms_chamelium_v3_hpd',
 ]
 
 test_deps = [ igt_deps ]
-- 
2.48.1


  parent reply	other threads:[~2026-04-28  4:48 UTC|newest]

Thread overview: 37+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-28  4:46 [PATCH i-g-t v1 00/25] Chamelium v3 Integration and Test Execution Mohammed Bilal
2026-04-28  4:46 ` [PATCH i-g-t v1 01/25] lib/igt_kms: Add a detect timeout value Mohammed Bilal
2026-04-28  7:11   ` Jani Nikula
2026-04-28  7:16   ` Jani Nikula
2026-04-28  7:17   ` Jani Nikula
2026-04-28  4:46 ` [PATCH i-g-t v1 02/25] lib/igt_kms: Add helper to wait for a specific status on a connector Mohammed Bilal
2026-04-28  4:46 ` [PATCH i-g-t v1 03/25] lib/igt_kms: Add function to list connected connectors Mohammed Bilal
2026-04-28  4:46 ` [PATCH i-g-t v1 04/25] lib/igt_kms: Add helper to obtain a connector by its name or MST path Mohammed Bilal
2026-04-28  4:46 ` [PATCH i-g-t v1 05/25] lib/igt_kms: Add function to get valid pipe for specific output Mohammed Bilal
2026-04-28  7:21   ` Jani Nikula
2026-04-28  4:46 ` [PATCH i-g-t v1 06/25] lib/monitor_edids: Add helper functions for using monitor_edid objects Mohammed Bilal
2026-04-28  4:46 ` [PATCH i-g-t v1 07/25] lib/monitor_edids: Add helper to get an EDID by its name Mohammed Bilal
2026-04-28  7:23   ` Jani Nikula
2026-04-28  4:46 ` [PATCH i-g-t v1 08/25] lib/monitor_edids: Add helper to print all available EDID names Mohammed Bilal
2026-04-28  4:46 ` [PATCH i-g-t v1 09/25] lib/monitor_edids: Fix missing names in some monitor EDID Mohammed Bilal
2026-04-28  4:46 ` [PATCH i-g-t v1 10/25] lib/monitor_edids: Add new EDID for HDMI 4k Mohammed Bilal
2026-04-28  4:46 ` [PATCH i-g-t v1 11/25] tests/chamelium: Extract Chamelium v2 tests into a separate directory Mohammed Bilal
2026-04-28  4:46 ` [PATCH i-g-t v1 12/25] lib/chamelium/v2: Extract chamelium v2 wrapper into its own directory Mohammed Bilal
2026-04-28  4:46 ` [PATCH i-g-t v1 13/25] lib/chamelium/v2: Rename chamelium to chamelium_v2 Mohammed Bilal
2026-04-28  4:46 ` [PATCH i-g-t v1 14/25] lib/chamelium/v2: Rename HAVE_CHAMELIUM to HAVE_CHAMELIUM_V2 Mohammed Bilal
2026-04-28  4:46 ` [PATCH i-g-t v1 15/25] lib/chamelium/v3: Introduce the foundation for the Chamelium v3 wrapper Mohammed Bilal
2026-04-28  4:46 ` [PATCH i-g-t v1 16/25] lib/chamelium/v3: Introduce initialization and cleanup of Chamelium-related structures Mohammed Bilal
2026-04-28  4:46 ` [PATCH i-g-t v1 17/25] lib/chamelium/v3: Add method to discover Chamelium ports Mohammed Bilal
2026-04-28  4:46 ` [PATCH i-g-t v1 18/25] lib/chamelium/v3: Implement method to retrieve Chamelium port names Mohammed Bilal
2026-04-28  4:46 ` [PATCH i-g-t v1 19/25] tests/chamelium/v3: Implement a basic Chamelium v3 accessibility test Mohammed Bilal
2026-04-28  4:46 ` [PATCH i-g-t v1 20/25] lib/chamelium/v3: Add extended API for Chamelium v3 HPD, EDID, Frames, Color & Audio Mohammed Bilal
2026-04-29 10:44   ` Louis Chauvet
2026-04-28  4:46 ` Mohammed Bilal [this message]
2026-04-28  4:46 ` [PATCH i-g-t v1 22/25] tests/chamelium/v3: Add EDID tests for Chamelium v3 Mohammed Bilal
2026-04-28  4:46 ` [PATCH i-g-t v1 23/25] tests/chamelium/v3: Add frame capture and CRC " Mohammed Bilal
2026-04-28  4:46 ` [PATCH i-g-t v1 24/25] tests/chamelium/v3: Add color verification " Mohammed Bilal
2026-04-28  4:46 ` [PATCH i-g-t v1 25/25] tests/chamelium/v3: Add audio " Mohammed Bilal
2026-04-28  5:52 ` ✓ Xe.CI.BAT: success for Chamelium v3 Integration and Test Execution Patchwork
2026-04-28  6:09 ` ✗ i915.CI.BAT: failure " Patchwork
2026-04-28 12:36 ` ✗ Xe.CI.FULL: " Patchwork
2026-04-28 14:02 ` [PATCH i-g-t v1 00/25] " Louis Chauvet
2026-04-29  3:36   ` Bilal, Mohammed

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=20260428044644.257001-22-mohammed.bilal@intel.com \
    --to=mohammed.bilal@intel.com \
    --cc=igt-dev@lists.freedesktop.org \
    --cc=kunal1.joshi@intel.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