[igt-dev] [PATCH] Chamelium: Simplify the chamelium test files.

Mark Yacoub markyacoub at chromium.org
Mon Nov 21 19:17:48 UTC 2022


[Why]
kms_chamelium tests file has grown so much and became a bit big to
manage.
Splitting specific tests like we do for kms_ tests into separate files
puts logically related functionalities into the same place so tests are
more clear.

[How]
1. Rename kms_chamelium_color to chamelium_color to standarize starting
   chamelium test file names which are inside the chamelium/ directory with chamelium_
2. Remove the HPD related tests from the super large kms_chamelium.c
   into into own new files "chamelium_hpd.c"
3. Remove all unused code from kms_chamelium.c
4. Create a chamelium_helper that has all common code between
   kms_chamelium.c and kms_hpd.c

TODO: remove other related tests from kms_chamelium into their own files
and deprecate kms_chamelium completely.

Signed-off-by: Mark Yacoub <markyacoub at chromium.org>
---
 lib/monitor_edids/monitor_edids_helper.c      |   2 +-
 ...ms_color_chamelium.c => chamelium_color.c} |   0
 tests/chamelium/chamelium_helper.c            | 239 ++++++
 tests/chamelium/chamelium_helper.h            |  64 ++
 tests/chamelium/chamelium_hpd.c               | 506 ++++++++++++
 tests/chamelium/kms_chamelium.c               | 721 +-----------------
 tests/kms_color_helper.h                      |   2 +-
 tests/meson.build                             |   9 +-
 8 files changed, 819 insertions(+), 724 deletions(-)
 rename tests/chamelium/{kms_color_chamelium.c => chamelium_color.c} (100%)
 create mode 100644 tests/chamelium/chamelium_helper.c
 create mode 100644 tests/chamelium/chamelium_helper.h
 create mode 100644 tests/chamelium/chamelium_hpd.c

diff --git a/lib/monitor_edids/monitor_edids_helper.c b/lib/monitor_edids/monitor_edids_helper.c
index 41f199bd..1cbf1c22 100644
--- a/lib/monitor_edids/monitor_edids_helper.c
+++ b/lib/monitor_edids/monitor_edids_helper.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: MIT
 /*
  * A helper library for parsing and making use of real EDID data from monitors
  * and make them compatible with IGT and Chamelium.
diff --git a/tests/chamelium/kms_color_chamelium.c b/tests/chamelium/chamelium_color.c
similarity index 100%
rename from tests/chamelium/kms_color_chamelium.c
rename to tests/chamelium/chamelium_color.c
diff --git a/tests/chamelium/chamelium_helper.c b/tests/chamelium/chamelium_helper.c
new file mode 100644
index 00000000..59d089fc
--- /dev/null
+++ b/tests/chamelium/chamelium_helper.c
@@ -0,0 +1,239 @@
+// SPDX-License-Identifier: MIT
+/*
+ * A helper library for all Chamelium tests.
+ *
+ * Copyright 2022 Google LLC.
+ *
+ * Authors: Mark Yacoub <markyacoub at chromium.org>
+ */
+
+#include "chamelium_helper.h"
+
+void init_chamelium(data_t *data)
+{
+	int i;
+
+	/* So fbcon doesn't try to reprobe things itself */
+	kmstest_set_vt_graphics_mode();
+
+	data->drm_fd = drm_open_driver_master(DRIVER_ANY);
+	igt_display_require(&data->display, data->drm_fd);
+	igt_require(data->display.is_atomic);
+
+	/*
+	 * XXX: disabling modeset, can be removed when
+	 * igt_display_require will start doing this for us
+	 */
+	igt_display_commit2(&data->display, COMMIT_ATOMIC);
+
+	/* we need to initalize chamelium after igt_display_require */
+	data->chamelium = chamelium_init(data->drm_fd, &data->display);
+	igt_require(data->chamelium);
+
+	data->ports = chamelium_get_ports(data->chamelium, &data->port_count);
+
+	for (i = 0; i < IGT_CUSTOM_EDID_COUNT; ++i) {
+		data->edids[i] = chamelium_new_edid(data->chamelium,
+						    igt_kms_get_custom_edid(i));
+	}
+}
+
+/* Wait for hotplug and return the remaining time left from timeout */
+bool wait_for_hotplug(struct udev_monitor *mon, int *timeout)
+{
+	struct timespec start, end;
+	int elapsed;
+	bool detected;
+
+	igt_assert_eq(igt_gettime(&start), 0);
+	detected = igt_hotplug_detected(mon, *timeout);
+	igt_assert_eq(igt_gettime(&end), 0);
+
+	elapsed = igt_time_elapsed(&start, &end);
+	igt_assert_lte(0, elapsed);
+	*timeout = max(0, *timeout - elapsed);
+
+	return detected;
+}
+
+/**
+ * wait_for_connector_after_hotplug:
+ *
+ * Waits for the connector attached to @port to have a status of @status after
+ * it's plugged/unplugged.
+ *
+ */
+void wait_for_connector_after_hotplug(data_t *data, struct udev_monitor *mon,
+				      struct chamelium_port *port,
+				      drmModeConnection status)
+{
+	int timeout = CHAMELIUM_HOTPLUG_TIMEOUT;
+	int hotplug_count = 0;
+
+	igt_debug("Waiting for %s to get %s after a hotplug event...\n",
+		  chamelium_port_get_name(port),
+		  kmstest_connector_status_str(status));
+
+	while (timeout > 0) {
+		if (!wait_for_hotplug(mon, &timeout))
+			break;
+
+		hotplug_count++;
+
+		if (chamelium_reprobe_connector(&data->display, data->chamelium,
+						port) == status)
+			return;
+	}
+
+	igt_assert_f(
+		false,
+		"Timed out waiting for %s to get %s after a hotplug. Current state %s hotplug_count %d\n",
+		chamelium_port_get_name(port),
+		kmstest_connector_status_str(status),
+		kmstest_connector_status_str(chamelium_reprobe_connector(
+			&data->display, data->chamelium, port)),
+		hotplug_count);
+}
+
+/**
+ * enable_output:
+ *
+ * Modesets the connector attached to @port for the assigned @mode and draws the
+ * @fb.
+ *
+ */
+void enable_output(data_t *data, struct chamelium_port *port,
+		   igt_output_t *output, drmModeModeInfo *mode,
+		   struct igt_fb *fb)
+{
+	igt_display_t *display = output->display;
+	igt_plane_t *primary =
+		igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY);
+	drmModeConnector *connector =
+		chamelium_port_get_connector(data->chamelium, port, false);
+
+	igt_assert(primary);
+
+	igt_plane_set_size(primary, mode->hdisplay, mode->vdisplay);
+	igt_plane_set_fb(primary, fb);
+	igt_output_override_mode(output, mode);
+
+	/* Clear any color correction values that might be enabled */
+	if (igt_pipe_obj_has_prop(primary->pipe, IGT_CRTC_DEGAMMA_LUT))
+		igt_pipe_obj_replace_prop_blob(primary->pipe,
+					       IGT_CRTC_DEGAMMA_LUT, NULL, 0);
+	if (igt_pipe_obj_has_prop(primary->pipe, IGT_CRTC_GAMMA_LUT))
+		igt_pipe_obj_replace_prop_blob(primary->pipe,
+					       IGT_CRTC_GAMMA_LUT, NULL, 0);
+	if (igt_pipe_obj_has_prop(primary->pipe, IGT_CRTC_CTM))
+		igt_pipe_obj_replace_prop_blob(primary->pipe, IGT_CRTC_CTM,
+					       NULL, 0);
+
+	igt_display_commit2(display, COMMIT_ATOMIC);
+
+	if (chamelium_port_get_type(port) == DRM_MODE_CONNECTOR_VGA)
+		usleep(250000);
+
+	drmModeFreeConnector(connector);
+}
+
+/* Return pipe attached to @outpu.t */
+enum pipe get_pipe_for_output(igt_display_t *display, igt_output_t *output)
+{
+	enum pipe pipe;
+
+	for_each_pipe(display, pipe) {
+		if (igt_pipe_connector_valid(pipe, output)) {
+			return pipe;
+		}
+	}
+
+	igt_assert_f(false, "No pipe found for output %s\n",
+		     igt_output_name(output));
+}
+
+static void chamelium_paint_xr24_pattern(uint32_t *data, size_t width,
+					 size_t height, size_t stride,
+					 size_t block_size)
+{
+	uint32_t colors[] = { 0xff000000, 0xffff0000, 0xff00ff00, 0xff0000ff,
+			      0xffffffff };
+	unsigned i, j;
+
+	for (i = 0; i < height; i++)
+		for (j = 0; j < width; j++)
+			*(data + i * stride / 4 +
+			  j) = colors[((j / block_size) + (i / block_size)) % 5];
+}
+
+/**
+ * chamelium_get_pattern_fb:
+ *
+ * Creates an @fb with an xr24 pattern and returns the fb_id.
+ *
+ */
+int chamelium_get_pattern_fb(data_t *data, size_t width, size_t height,
+			     uint32_t fourcc, size_t block_size,
+			     struct igt_fb *fb)
+{
+	int fb_id;
+	void *ptr;
+
+	igt_assert(fourcc == DRM_FORMAT_XRGB8888);
+
+	fb_id = igt_create_fb(data->drm_fd, width, height, fourcc,
+			      DRM_FORMAT_MOD_LINEAR, fb);
+	igt_assert(fb_id > 0);
+
+	ptr = igt_fb_map_buffer(fb->fd, fb);
+	igt_assert(ptr);
+
+	chamelium_paint_xr24_pattern(ptr, width, height, fb->strides[0],
+				     block_size);
+	igt_fb_unmap_buffer(fb, ptr);
+
+	return fb_id;
+}
+
+/* Generate a simple @fb for the size of @mode. */
+void create_fb_for_mode(data_t *data, struct igt_fb *fb, drmModeModeInfo *mode)
+{
+	int fb_id;
+
+	fb_id = chamelium_get_pattern_fb(data, mode->hdisplay, mode->vdisplay,
+					 DRM_FORMAT_XRGB8888, 64, fb);
+
+	igt_assert(fb_id > 0);
+}
+
+/* Returns the first preferred mode for the connector attached to @port. */
+drmModeModeInfo get_mode_for_port(struct chamelium *chamelium,
+				  struct chamelium_port *port)
+{
+	drmModeConnector *connector =
+		chamelium_port_get_connector(chamelium, port, false);
+	drmModeModeInfo mode;
+	igt_assert(&connector->modes[0] != NULL);
+	memcpy(&mode, &connector->modes[0], sizeof(mode));
+	drmModeFreeConnector(connector);
+	return mode;
+}
+
+/* Returns the igt display output for the connector attached to @port. */
+igt_output_t *get_output_for_port(data_t *data, struct chamelium_port *port)
+{
+	drmModeConnector *connector =
+		chamelium_port_get_connector(data->chamelium, port, true);
+	igt_output_t *output =
+		igt_output_from_connector(&data->display, connector);
+	drmModeFreeConnector(connector);
+	igt_assert(output != NULL);
+	return output;
+}
+
+/* Set the EDID of index @edid to Chamelium's @port. */
+void set_edid(data_t *data, struct chamelium_port *port,
+	      enum igt_custom_edid_type edid)
+{
+	chamelium_port_set_edid(data->chamelium, port, data->edids[edid]);
+}
\ No newline at end of file
diff --git a/tests/chamelium/chamelium_helper.h b/tests/chamelium/chamelium_helper.h
new file mode 100644
index 00000000..d9a91bf7
--- /dev/null
+++ b/tests/chamelium/chamelium_helper.h
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * A helper library for all Chamelium tests.
+ *
+ * Copyright 2022 Google LLC.
+ *
+ * Authors: Mark Yacoub <markyacoub at chromium.org>
+ */
+
+#ifndef TESTS_CHAMELIUM_CHAMELIUM_HELPER_H
+#define TESTS_CHAMELIUM_CHAMELIUM_HELPER_H
+
+#include "igt.h"
+
+#define ONLINE_TIMEOUT 20 /* seconds */
+
+#define for_each_port(p, port)                                 \
+	for (p = 0, port = data.ports[p]; p < data.port_count; \
+	     p++, port = data.ports[p])
+
+#define connector_subtest(name__, type__)                           \
+	igt_subtest(name__)                                         \
+	for_each_port(p, port) if (chamelium_port_get_type(port) == \
+				   DRM_MODE_CONNECTOR_##type__)
+
+/*
+ * The chamelium data structure is used to store all the information known about
+ * chamelium to run the tests.
+ */
+typedef struct {
+	struct chamelium *chamelium;
+	struct chamelium_port **ports;
+	igt_display_t display;
+	int port_count;
+
+	int drm_fd;
+
+	struct chamelium_edid *edids[IGT_CUSTOM_EDID_COUNT];
+} data_t;
+
+void init_chamelium(data_t *data);
+
+bool wait_for_hotplug(struct udev_monitor *mon, int *timeout);
+void wait_for_connector_after_hotplug(data_t *data, struct udev_monitor *mon,
+				      struct chamelium_port *port,
+				      drmModeConnection status);
+
+void enable_output(data_t *data, struct chamelium_port *port,
+		   igt_output_t *output, drmModeModeInfo *mode,
+		   struct igt_fb *fb);
+enum pipe get_pipe_for_output(igt_display_t *display, igt_output_t *output);
+
+int chamelium_get_pattern_fb(data_t *data, size_t width, size_t height,
+			     uint32_t fourcc, size_t block_size,
+			     struct igt_fb *fb);
+void create_fb_for_mode(data_t *data, struct igt_fb *fb, drmModeModeInfo *mode);
+drmModeModeInfo get_mode_for_port(struct chamelium *chamelium,
+				  struct chamelium_port *port);
+igt_output_t *get_output_for_port(data_t *data, struct chamelium_port *port);
+
+void set_edid(data_t *data, struct chamelium_port *port,
+	      enum igt_custom_edid_type edid);
+
+#endif /* TESTS_CHAMELIUM_CHAMELIUM_HELPER_H */
diff --git a/tests/chamelium/chamelium_hpd.c b/tests/chamelium/chamelium_hpd.c
new file mode 100644
index 00000000..c8f5afe4
--- /dev/null
+++ b/tests/chamelium/chamelium_hpd.c
@@ -0,0 +1,506 @@
+// SPDX-License-Identifier: MIT
+/*
+ * A Chamelium test for testing the HPD functionality.
+ *
+ * Copyright 2022 Google LLC.
+ *
+ * Authors: Mark Yacoub <markyacoub at chromium.org>
+ */
+
+#include "chamelium_helper.h"
+
+#define HPD_STORM_PULSE_INTERVAL_DP 100 /* ms */
+#define HPD_STORM_PULSE_INTERVAL_HDMI 200 /* ms */
+
+#define HPD_TOGGLE_COUNT_VGA 5
+#define HPD_TOGGLE_COUNT_DP_HDMI 15
+#define HPD_TOGGLE_COUNT_FAST 3
+
+enum test_modeset_mode {
+	TEST_MODESET_ON,
+	TEST_MODESET_ON_OFF,
+	TEST_MODESET_OFF,
+};
+
+static void try_suspend_resume_hpd(data_t *data, struct chamelium_port *port,
+				   enum igt_suspend_state state,
+				   enum igt_suspend_test test,
+				   struct udev_monitor *mon, bool connected)
+{
+	drmModeConnection target_state = connected ? DRM_MODE_DISCONNECTED :
+						     DRM_MODE_CONNECTED;
+	int timeout = CHAMELIUM_HOTPLUG_TIMEOUT;
+	int delay;
+	int p;
+
+	igt_flush_uevents(mon);
+
+	delay = igt_get_autoresume_delay(state) * 1000 / 2;
+
+	if (port) {
+		chamelium_schedule_hpd_toggle(data->chamelium, port, delay,
+					      !connected);
+	} else {
+		for (p = 0; p < data->port_count; p++) {
+			port = data->ports[p];
+			chamelium_schedule_hpd_toggle(data->chamelium, port,
+						      delay, !connected);
+		}
+
+		port = NULL;
+	}
+
+	igt_system_suspend_autoresume(state, test);
+	igt_assert(wait_for_hotplug(mon, &timeout));
+	chamelium_assert_reachable(data->chamelium, ONLINE_TIMEOUT);
+
+	if (port) {
+		igt_assert_eq(chamelium_reprobe_connector(
+				      &data->display, data->chamelium, port),
+			      target_state);
+	} else {
+		for (p = 0; p < data->port_count; p++) {
+			drmModeConnection current_state;
+
+			port = data->ports[p];
+			/*
+			 * There could be as many hotplug events sent by
+			 * driver as connectors we scheduled an HPD toggle on
+			 * above, depending on timing. So if we're not seeing
+			 * the expected connector state try to wait for an HPD
+			 * event for each connector/port.
+			 */
+			current_state = chamelium_reprobe_connector(
+				&data->display, data->chamelium, port);
+			if (p > 0 && current_state != target_state) {
+				igt_assert(wait_for_hotplug(mon, &timeout));
+				current_state = chamelium_reprobe_connector(
+					&data->display, data->chamelium, port);
+			}
+
+			igt_assert_eq(current_state, target_state);
+		}
+
+		port = NULL;
+	}
+}
+
+static const char test_basic_hotplug_desc[] =
+	"Check that we get uevents and updated connector status on "
+	"hotplug and unplug";
+static void test_hotplug(data_t *data, struct chamelium_port *port,
+			 int toggle_count, enum test_modeset_mode modeset_mode)
+{
+	int i;
+	enum pipe pipe;
+	struct igt_fb fb = { 0 };
+	drmModeModeInfo mode;
+	struct udev_monitor *mon = igt_watch_uevents();
+	igt_output_t *output = get_output_for_port(data, port);
+
+	igt_modeset_disable_all_outputs(&data->display);
+	chamelium_reset_state(&data->display, data->chamelium, NULL,
+			      data->ports, data->port_count);
+
+	igt_hpd_storm_set_threshold(data->drm_fd, 0);
+
+	for (i = 0; i < toggle_count; i++) {
+		igt_flush_uevents(mon);
+
+		/* Check if we get a sysfs hotplug event */
+		chamelium_plug(data->chamelium, port);
+
+		wait_for_connector_after_hotplug(data, mon, port,
+						 DRM_MODE_CONNECTED);
+		igt_flush_uevents(mon);
+
+		if (modeset_mode == TEST_MODESET_ON_OFF ||
+		    (modeset_mode == TEST_MODESET_ON && i == 0)) {
+			if (i == 0) {
+				/* We can only get mode and pipe once we are
+				 * connected */
+				output = get_output_for_port(data, port);
+				pipe = get_pipe_for_output(&data->display,
+							   output);
+				mode = get_mode_for_port(data->chamelium, port);
+				create_fb_for_mode(data, &fb, &mode);
+			}
+
+			igt_output_set_pipe(output, pipe);
+			enable_output(data, port, output, &mode, &fb);
+		}
+
+		/* Now check if we get a hotplug from disconnection */
+		chamelium_unplug(data->chamelium, port);
+
+		wait_for_connector_after_hotplug(data, mon, port,
+						 DRM_MODE_DISCONNECTED);
+
+		igt_flush_uevents(mon);
+
+		if (modeset_mode == TEST_MODESET_ON_OFF) {
+			igt_output_set_pipe(output, PIPE_NONE);
+			igt_display_commit2(&data->display, COMMIT_ATOMIC);
+		}
+	}
+
+	igt_cleanup_uevents(mon);
+	igt_hpd_storm_reset(data->drm_fd);
+	igt_remove_fb(data->drm_fd, &fb);
+}
+
+static const char test_hotplug_for_each_pipe_desc[] =
+	"Check that we get uevents and updated connector status on "
+	"hotplug and unplug for each pipe with valid output";
+static void test_hotplug_for_each_pipe(data_t *data,
+				       struct chamelium_port *port)
+{
+	igt_output_t *output;
+	enum pipe pipe;
+	struct udev_monitor *mon = igt_watch_uevents();
+
+	chamelium_reset_state(&data->display, data->chamelium, port,
+			      data->ports, data->port_count);
+
+	igt_hpd_storm_set_threshold(data->drm_fd, 0);
+	/* Disconnect if any port got connected */
+	chamelium_unplug(data->chamelium, port);
+	wait_for_connector_after_hotplug(data, mon, port,
+					 DRM_MODE_DISCONNECTED);
+
+	for_each_pipe(&data->display, pipe) {
+		igt_flush_uevents(mon);
+		/* Check if we get a sysfs hotplug event */
+		chamelium_plug(data->chamelium, port);
+		wait_for_connector_after_hotplug(data, mon, port,
+						 DRM_MODE_CONNECTED);
+		igt_flush_uevents(mon);
+		output = get_output_for_port(data, port);
+
+		/* If pipe is valid for output then set it */
+		if (igt_pipe_connector_valid(pipe, output)) {
+			igt_output_set_pipe(output, pipe);
+			igt_display_commit2(&data->display, COMMIT_ATOMIC);
+		}
+
+		chamelium_unplug(data->chamelium, port);
+		wait_for_connector_after_hotplug(data, mon, port,
+						 DRM_MODE_DISCONNECTED);
+		igt_flush_uevents(mon);
+	}
+
+	igt_cleanup_uevents(mon);
+	igt_hpd_storm_reset(data->drm_fd);
+}
+
+static const char test_suspend_resume_hpd_desc[] =
+	"Toggle HPD during suspend, check that uevents are sent and connector "
+	"status is updated";
+static void test_suspend_resume_hpd(data_t *data, struct chamelium_port *port,
+				    enum igt_suspend_state state,
+				    enum igt_suspend_test test)
+{
+	struct udev_monitor *mon = igt_watch_uevents();
+
+	igt_modeset_disable_all_outputs(&data->display);
+	chamelium_reset_state(&data->display, data->chamelium, port,
+			      data->ports, data->port_count);
+
+	/* Make sure we notice new connectors after resuming */
+	try_suspend_resume_hpd(data, port, state, test, mon, false);
+
+	/* Now make sure we notice disconnected connectors after resuming */
+	try_suspend_resume_hpd(data, port, state, test, mon, true);
+
+	igt_cleanup_uevents(mon);
+}
+
+static const char test_suspend_resume_hpd_common_desc[] =
+	"Toggle HPD during suspend on all connectors, check that uevents are "
+	"sent and connector status is updated";
+static void test_suspend_resume_hpd_common(data_t *data,
+					   enum igt_suspend_state state,
+					   enum igt_suspend_test test)
+{
+	struct udev_monitor *mon = igt_watch_uevents();
+	struct chamelium_port *port;
+	int p;
+
+	for (p = 0; p < data->port_count; p++) {
+		port = data->ports[p];
+		igt_debug("Testing port %s\n", chamelium_port_get_name(port));
+	}
+
+	igt_modeset_disable_all_outputs(&data->display);
+	chamelium_reset_state(&data->display, data->chamelium, NULL,
+			      data->ports, data->port_count);
+
+	/* Make sure we notice new connectors after resuming */
+	try_suspend_resume_hpd(data, NULL, state, test, mon, false);
+
+	/* Now make sure we notice disconnected connectors after resuming */
+	try_suspend_resume_hpd(data, NULL, state, test, mon, true);
+
+	igt_cleanup_uevents(mon);
+}
+
+static const char test_hpd_without_ddc_desc[] =
+	"Disable DDC on a VGA connector, check we still get a uevent on hotplug";
+static void test_hpd_without_ddc(data_t *data, struct chamelium_port *port)
+{
+	struct udev_monitor *mon = igt_watch_uevents();
+
+	igt_modeset_disable_all_outputs(&data->display);
+	chamelium_reset_state(&data->display, data->chamelium, port,
+			      data->ports, data->port_count);
+	igt_flush_uevents(mon);
+
+	/* Disable the DDC on the connector and make sure we still get a
+	 * hotplug
+	 */
+	chamelium_port_set_ddc_state(data->chamelium, port, false);
+	chamelium_plug(data->chamelium, port);
+
+	igt_assert(igt_hotplug_detected(mon, CHAMELIUM_HOTPLUG_TIMEOUT));
+	igt_assert_eq(chamelium_reprobe_connector(&data->display,
+						  data->chamelium, port),
+		      DRM_MODE_CONNECTED);
+
+	igt_cleanup_uevents(mon);
+}
+
+static const char test_hpd_storm_detect_desc[] =
+	"Trigger a series of hotplugs in a very small timeframe to simulate a"
+	"bad cable, check the kernel falls back to polling to avoid a hotplug "
+	"storm";
+static void test_hpd_storm_detect(data_t *data, struct chamelium_port *port,
+				  int width)
+{
+	struct udev_monitor *mon;
+	int count = 0;
+
+	igt_require_hpd_storm_ctl(data->drm_fd);
+	igt_modeset_disable_all_outputs(&data->display);
+	chamelium_reset_state(&data->display, data->chamelium, port,
+			      data->ports, data->port_count);
+
+	igt_hpd_storm_set_threshold(data->drm_fd, 1);
+	chamelium_fire_hpd_pulses(data->chamelium, port, width, 10);
+	igt_assert(igt_hpd_storm_detected(data->drm_fd));
+
+	mon = igt_watch_uevents();
+	chamelium_fire_hpd_pulses(data->chamelium, port, width, 10);
+
+	/*
+	 * Polling should have been enabled by the HPD storm at this point,
+	 * so we should only get at most 1 hotplug event
+	 */
+	igt_until_timeout(5)
+		count += igt_hotplug_detected(mon, 1);
+	igt_assert_lt(count, 2);
+
+	igt_cleanup_uevents(mon);
+	igt_hpd_storm_reset(data->drm_fd);
+}
+
+static const char test_hpd_storm_disable_desc[] =
+	"Disable HPD storm detection, trigger a storm and check the kernel "
+	"doesn't detect one";
+static void test_hpd_storm_disable(data_t *data, struct chamelium_port *port,
+				   int width)
+{
+	igt_require_hpd_storm_ctl(data->drm_fd);
+	igt_modeset_disable_all_outputs(&data->display);
+	chamelium_reset_state(&data->display, data->chamelium, port,
+			      data->ports, data->port_count);
+
+	igt_hpd_storm_set_threshold(data->drm_fd, 0);
+	chamelium_fire_hpd_pulses(data->chamelium, port, width, 10);
+	igt_assert(!igt_hpd_storm_detected(data->drm_fd));
+
+	igt_hpd_storm_reset(data->drm_fd);
+}
+
+IGT_TEST_DESCRIPTION("Testing HPD with a Chamelium board");
+igt_main
+{
+	data_t data;
+	struct chamelium_port *port;
+	int p;
+
+	igt_fixture {
+		init_chamelium(&data);
+	}
+
+	igt_describe("DisplayPort tests");
+	igt_subtest_group {
+		igt_fixture {
+			chamelium_require_connector_present(
+				data.ports, DRM_MODE_CONNECTOR_DisplayPort,
+				data.port_count, 1);
+		}
+
+		igt_describe(test_basic_hotplug_desc);
+		connector_subtest("dp-hpd", DisplayPort)
+			test_hotplug(&data, port, HPD_TOGGLE_COUNT_DP_HDMI,
+				     TEST_MODESET_OFF);
+
+		igt_describe(test_basic_hotplug_desc);
+		connector_subtest("dp-hpd-fast", DisplayPort) test_hotplug(
+			&data, port, HPD_TOGGLE_COUNT_FAST, TEST_MODESET_OFF);
+
+		igt_describe(test_basic_hotplug_desc);
+		connector_subtest("dp-hpd-enable-disable-mode", DisplayPort)
+			test_hotplug(&data, port, HPD_TOGGLE_COUNT_FAST,
+				     TEST_MODESET_ON_OFF);
+
+		igt_describe(test_basic_hotplug_desc);
+		connector_subtest("dp-hpd-with-enabled-mode", DisplayPort)
+			test_hotplug(&data, port, HPD_TOGGLE_COUNT_FAST,
+				     TEST_MODESET_ON);
+
+		igt_describe(test_hotplug_for_each_pipe_desc);
+		connector_subtest("dp-hpd-for-each-pipe", DisplayPort)
+			test_hotplug_for_each_pipe(&data, port);
+
+		igt_describe(test_suspend_resume_hpd_desc);
+		connector_subtest("dp-hpd-after-suspend", DisplayPort)
+			test_suspend_resume_hpd(&data, port, SUSPEND_STATE_MEM,
+						SUSPEND_TEST_NONE);
+
+		igt_describe(test_suspend_resume_hpd_desc);
+		connector_subtest("dp-hpd-after-hibernate", DisplayPort)
+			test_suspend_resume_hpd(&data, port, SUSPEND_STATE_DISK,
+						SUSPEND_TEST_DEVICES);
+
+		igt_describe(test_hpd_storm_detect_desc);
+		connector_subtest("dp-hpd-storm", DisplayPort)
+			test_hpd_storm_detect(&data, port,
+					      HPD_STORM_PULSE_INTERVAL_DP);
+
+		igt_describe(test_hpd_storm_disable_desc);
+		connector_subtest("dp-hpd-storm-disable", DisplayPort)
+			test_hpd_storm_disable(&data, port,
+					       HPD_STORM_PULSE_INTERVAL_DP);
+	}
+
+	igt_describe("HDMI tests");
+	igt_subtest_group {
+		igt_fixture {
+			chamelium_require_connector_present(
+				data.ports, DRM_MODE_CONNECTOR_HDMIA,
+				data.port_count, 1);
+		}
+
+		igt_describe(test_basic_hotplug_desc);
+		connector_subtest("hdmi-hpd", HDMIA)
+			test_hotplug(&data, port, HPD_TOGGLE_COUNT_DP_HDMI,
+				     TEST_MODESET_OFF);
+
+		igt_describe(test_basic_hotplug_desc);
+		connector_subtest("hdmi-hpd-fast", HDMIA) test_hotplug(
+			&data, port, HPD_TOGGLE_COUNT_FAST, TEST_MODESET_OFF);
+
+		igt_describe(test_basic_hotplug_desc);
+		connector_subtest("hdmi-hpd-enable-disable-mode", HDMIA)
+			test_hotplug(&data, port, HPD_TOGGLE_COUNT_FAST,
+				     TEST_MODESET_ON_OFF);
+
+		igt_describe(test_basic_hotplug_desc);
+		connector_subtest("hdmi-hpd-with-enabled-mode", HDMIA)
+			test_hotplug(&data, port, HPD_TOGGLE_COUNT_FAST,
+				     TEST_MODESET_ON);
+
+		igt_describe(test_hotplug_for_each_pipe_desc);
+		connector_subtest("hdmi-hpd-for-each-pipe", HDMIA)
+			test_hotplug_for_each_pipe(&data, port);
+
+		igt_describe(test_suspend_resume_hpd_desc);
+		connector_subtest("hdmi-hpd-after-suspend", HDMIA)
+			test_suspend_resume_hpd(&data, port, SUSPEND_STATE_MEM,
+						SUSPEND_TEST_NONE);
+
+		igt_describe(test_suspend_resume_hpd_desc);
+		connector_subtest("hdmi-hpd-after-hibernate", HDMIA)
+			test_suspend_resume_hpd(&data, port, SUSPEND_STATE_DISK,
+						SUSPEND_TEST_DEVICES);
+
+		igt_describe(test_hpd_storm_detect_desc);
+		connector_subtest("hdmi-hpd-storm", HDMIA)
+			test_hpd_storm_detect(&data, port,
+					      HPD_STORM_PULSE_INTERVAL_HDMI);
+
+		igt_describe(test_hpd_storm_disable_desc);
+		connector_subtest("hdmi-hpd-storm-disable", HDMIA)
+			test_hpd_storm_disable(&data, port,
+					       HPD_STORM_PULSE_INTERVAL_HDMI);
+	}
+
+	igt_describe("VGA tests");
+	igt_subtest_group {
+		igt_fixture {
+			chamelium_require_connector_present(
+				data.ports, DRM_MODE_CONNECTOR_VGA,
+				data.port_count, 1);
+		}
+
+		igt_describe(test_basic_hotplug_desc);
+		connector_subtest("vga-hpd", VGA) test_hotplug(
+			&data, port, HPD_TOGGLE_COUNT_VGA, TEST_MODESET_OFF);
+
+		igt_describe(test_basic_hotplug_desc);
+		connector_subtest("vga-hpd-fast", VGA) test_hotplug(
+			&data, port, HPD_TOGGLE_COUNT_FAST, TEST_MODESET_OFF);
+
+		igt_describe(test_basic_hotplug_desc);
+		connector_subtest("vga-hpd-enable-disable-mode", VGA)
+			test_hotplug(&data, port, HPD_TOGGLE_COUNT_FAST,
+				     TEST_MODESET_ON_OFF);
+
+		igt_describe(test_basic_hotplug_desc);
+		connector_subtest("vga-hpd-with-enabled-mode", VGA)
+			test_hotplug(&data, port, HPD_TOGGLE_COUNT_FAST,
+				     TEST_MODESET_ON);
+
+		igt_describe(test_suspend_resume_hpd_desc);
+		connector_subtest("vga-hpd-after-suspend", VGA)
+			test_suspend_resume_hpd(&data, port, SUSPEND_STATE_MEM,
+						SUSPEND_TEST_NONE);
+
+		igt_describe(test_suspend_resume_hpd_desc);
+		connector_subtest("vga-hpd-after-hibernate", VGA)
+			test_suspend_resume_hpd(&data, port, SUSPEND_STATE_DISK,
+						SUSPEND_TEST_DEVICES);
+
+		igt_describe(test_hpd_without_ddc_desc);
+		connector_subtest("vga-hpd-without-ddc", VGA)
+			test_hpd_without_ddc(&data, port);
+	}
+
+	igt_describe("Tests that operate on all connectors");
+	igt_subtest_group {
+		igt_fixture {
+			igt_require(data.port_count);
+		}
+
+		igt_describe(test_suspend_resume_hpd_common_desc);
+		igt_subtest("common-hpd-after-suspend")
+			test_suspend_resume_hpd_common(&data, SUSPEND_STATE_MEM,
+						       SUSPEND_TEST_NONE);
+
+		igt_describe(test_suspend_resume_hpd_common_desc);
+		igt_subtest("common-hpd-after-hibernate")
+			test_suspend_resume_hpd_common(&data,
+						       SUSPEND_STATE_DISK,
+						       SUSPEND_TEST_DEVICES);
+	}
+
+	igt_describe(test_hotplug_for_each_pipe_desc);
+	connector_subtest("vga-hpd-for-each-pipe", VGA)
+		test_hotplug_for_each_pipe(&data, port);
+
+	igt_fixture {
+		igt_display_fini(&data.display);
+		close(data.drm_fd);
+	}
+}
diff --git a/tests/chamelium/kms_chamelium.c b/tests/chamelium/kms_chamelium.c
index 3c4b4d75..d11f0271 100644
--- a/tests/chamelium/kms_chamelium.c
+++ b/tests/chamelium/kms_chamelium.c
@@ -24,50 +24,13 @@
  *    Lyude Paul <lyude at redhat.com>
  */
 
-#include "config.h"
-#include "igt.h"
-#include "igt_vc4.h"
-#include "igt_edid.h"
+#include "chamelium_helper.h"
 #include "igt_eld.h"
 #include "igt_infoframe.h"
 #include "monitor_edids/dp_edids.h"
 #include "monitor_edids/hdmi_edids.h"
 #include "monitor_edids/monitor_edids_helper.h"
 
-#include <fcntl.h>
-#include <pthread.h>
-#include <string.h>
-#include <stdatomic.h>
-// #include <stdio.h>
-
-// struct chamelium_edid;
-
-enum test_modeset_mode {
-	TEST_MODESET_ON,
-	TEST_MODESET_ON_OFF,
-	TEST_MODESET_OFF,
-};
-
-typedef struct {
-	struct chamelium *chamelium;
-	struct chamelium_port **ports;
-	igt_display_t display;
-	int port_count;
-
-	int drm_fd;
-
-	struct chamelium_edid *edids[IGT_CUSTOM_EDID_COUNT];
-} data_t;
-
-#define ONLINE_TIMEOUT 20 /* seconds */
-
-#define HPD_STORM_PULSE_INTERVAL_DP 100 /* ms */
-#define HPD_STORM_PULSE_INTERVAL_HDMI 200 /* ms */
-
-#define HPD_TOGGLE_COUNT_VGA 5
-#define HPD_TOGGLE_COUNT_DP_HDMI 15
-#define HPD_TOGGLE_COUNT_FAST 3
-
 static void
 get_connectors_link_status_failed(data_t *data, bool *link_status_failed)
 {
@@ -93,54 +56,6 @@ get_connectors_link_status_failed(data_t *data, bool *link_status_failed)
 	}
 }
 
-/* Wait for hotplug and return the remaining time left from timeout */
-static bool wait_for_hotplug(struct udev_monitor *mon, int *timeout)
-{
-	struct timespec start, end;
-	int elapsed;
-	bool detected;
-
-	igt_assert_eq(igt_gettime(&start), 0);
-	detected = igt_hotplug_detected(mon, *timeout);
-	igt_assert_eq(igt_gettime(&end), 0);
-
-	elapsed = igt_time_elapsed(&start, &end);
-	igt_assert_lte(0, elapsed);
-	*timeout = max(0, *timeout - elapsed);
-
-	return detected;
-}
-
-static void
-wait_for_connector_after_hotplug(data_t *data, struct udev_monitor *mon,
-				 struct chamelium_port *port,
-				 drmModeConnection status)
-{
-	int timeout = CHAMELIUM_HOTPLUG_TIMEOUT;
-	int hotplug_count = 0;
-
-	igt_debug("Waiting for %s to get %s after a hotplug event...\n",
-			  chamelium_port_get_name(port),
-			  kmstest_connector_status_str(status));
-
-	while (timeout > 0) {
-		if (!wait_for_hotplug(mon, &timeout))
-			break;
-
-		hotplug_count++;
-
-		if (chamelium_reprobe_connector(&data->display, data->chamelium,
-						port) == status)
-			return;
-	}
-
-	igt_assert_f(false, "Timed out waiting for %s to get %s after a hotplug. Current state %s hotplug_count %d\n",
-			    chamelium_port_get_name(port),
-			    kmstest_connector_status_str(status),
-			    kmstest_connector_status_str(chamelium_reprobe_connector(&data->display, data->chamelium, port)), hotplug_count);
-}
-
-
 static int chamelium_vga_modes[][2] = {
 	{ 1600, 1200 },
 	{ 1920, 1200 },
@@ -209,244 +124,6 @@ check_analog_bridge(data_t *data, struct chamelium_port *port)
 	return false;
 }
 
-static void chamelium_paint_xr24_pattern(uint32_t *data,
-					 size_t width, size_t height,
-					 size_t stride, size_t block_size)
-{
-	uint32_t colors[] = { 0xff000000,
-			      0xffff0000,
-			      0xff00ff00,
-			      0xff0000ff,
-			      0xffffffff };
-	unsigned i, j;
-
-	for (i = 0; i < height; i++)
-		for (j = 0; j < width; j++)
-			*(data + i * stride / 4 + j) = colors[((j / block_size) + (i / block_size)) % 5];
-}
-
-static int chamelium_get_pattern_fb(data_t *data, size_t width, size_t height,
-				    uint32_t fourcc, size_t block_size,
-				    struct igt_fb *fb)
-{
-	int fb_id;
-	void *ptr;
-
-	igt_assert(fourcc == DRM_FORMAT_XRGB8888);
-
-	fb_id = igt_create_fb(data->drm_fd, width, height, fourcc,
-			      DRM_FORMAT_MOD_LINEAR, fb);
-	igt_assert(fb_id > 0);
-
-	ptr = igt_fb_map_buffer(fb->fd, fb);
-	igt_assert(ptr);
-
-	chamelium_paint_xr24_pattern(ptr, width, height, fb->strides[0],
-				     block_size);
-	igt_fb_unmap_buffer(fb, ptr);
-
-	return fb_id;
-}
-
-static void
-enable_output(data_t *data,
-	      struct chamelium_port *port,
-	      igt_output_t *output,
-	      drmModeModeInfo *mode,
-	      struct igt_fb *fb)
-{
-	igt_display_t *display = output->display;
-	igt_plane_t *primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY);
-	drmModeConnector *connector = chamelium_port_get_connector(
-	    data->chamelium, port, false);
-
-	igt_assert(primary);
-
-	igt_plane_set_size(primary, mode->hdisplay, mode->vdisplay);
-	igt_plane_set_fb(primary, fb);
-	igt_output_override_mode(output, mode);
-
-	/* Clear any color correction values that might be enabled */
-	if (igt_pipe_obj_has_prop(primary->pipe, IGT_CRTC_DEGAMMA_LUT))
-		igt_pipe_obj_replace_prop_blob(primary->pipe, IGT_CRTC_DEGAMMA_LUT, NULL, 0);
-	if (igt_pipe_obj_has_prop(primary->pipe, IGT_CRTC_GAMMA_LUT))
-		igt_pipe_obj_replace_prop_blob(primary->pipe, IGT_CRTC_GAMMA_LUT, NULL, 0);
-	if (igt_pipe_obj_has_prop(primary->pipe, IGT_CRTC_CTM))
-		igt_pipe_obj_replace_prop_blob(primary->pipe, IGT_CRTC_CTM, NULL, 0);
-
-	igt_display_commit2(display, COMMIT_ATOMIC);
-
-	if (chamelium_port_get_type(port) == DRM_MODE_CONNECTOR_VGA)
-		usleep(250000);
-
-	drmModeFreeConnector(connector);
-}
-
-static enum pipe get_pipe_for_output(igt_display_t *display, igt_output_t *output)
-{
-	enum pipe pipe;
-
-	for_each_pipe(display, pipe) {
-		if (igt_pipe_connector_valid(pipe, output)) {
-			return pipe;
-		}
-	}
-
-	igt_assert_f(false, "No pipe found for output %s\n",
-		     igt_output_name(output));
-}
-
-static void create_fb_for_mode(data_t *data, struct igt_fb *fb, drmModeModeInfo *mode)
-{
-	int fb_id;
-
-	fb_id = chamelium_get_pattern_fb(data, mode->hdisplay, mode->vdisplay,
-					 DRM_FORMAT_XRGB8888, 64, fb);
-
-	igt_assert(fb_id > 0);
-}
-
-static drmModeModeInfo get_mode_for_port(struct chamelium *chamelium,
-					 struct chamelium_port *port)
-{
-	drmModeConnector *connector = chamelium_port_get_connector(chamelium,
-								   port, false);
-	drmModeModeInfo mode;
-	igt_assert(&connector->modes[0] != NULL);
-	memcpy(&mode, &connector->modes[0], sizeof(mode));
-	drmModeFreeConnector(connector);
-	return mode;
-}
-
-static igt_output_t *get_output_for_port(data_t *data,
-					 struct chamelium_port *port)
-{
-	drmModeConnector *connector =
-		chamelium_port_get_connector(data->chamelium, port, true);
-	igt_output_t *output = igt_output_from_connector(&data->display,
-							 connector);
-	drmModeFreeConnector(connector);
-	igt_assert(output != NULL);
-	return output;
-}
-
-static const char test_hotplug_for_each_pipe_desc[] =
-	"Check that we get uevents and updated connector status on "
-	"hotplug and unplug for each pipe with valid output";
-static void
-test_hotplug_for_each_pipe(data_t *data, struct chamelium_port *port)
-{
-	igt_output_t *output;
-	enum pipe pipe;
-	struct udev_monitor *mon = igt_watch_uevents();
-
-	chamelium_reset_state(&data->display,
-			      data->chamelium,
-			      port,
-			      data->ports,
-			      data->port_count);
-
-	igt_hpd_storm_set_threshold(data->drm_fd, 0);
-	/* Disconnect if any port got connected */
-	chamelium_unplug(data->chamelium, port);
-	wait_for_connector_after_hotplug(data, mon, port,
-			DRM_MODE_DISCONNECTED);
-
-	for_each_pipe(&data->display, pipe) {
-		igt_flush_uevents(mon);
-		/* Check if we get a sysfs hotplug event */
-		chamelium_plug(data->chamelium, port);
-		wait_for_connector_after_hotplug(data, mon, port,
-				DRM_MODE_CONNECTED);
-		igt_flush_uevents(mon);
-		output = get_output_for_port(data, port);
-
-		/* If pipe is valid for output then set it */
-		if (igt_pipe_connector_valid(pipe, output)) {
-			igt_output_set_pipe(output, pipe);
-			igt_display_commit2(&data->display, COMMIT_ATOMIC);
-		}
-
-		chamelium_unplug(data->chamelium, port);
-		wait_for_connector_after_hotplug(data, mon, port,
-				DRM_MODE_DISCONNECTED);
-		igt_flush_uevents(mon);
-	}
-
-	igt_cleanup_uevents(mon);
-	igt_hpd_storm_reset(data->drm_fd);
-}
-
-static const char test_basic_hotplug_desc[] =
-	"Check that we get uevents and updated connector status on "
-	"hotplug and unplug";
-static void
-test_hotplug(data_t *data, struct chamelium_port *port, int toggle_count,
-	     enum test_modeset_mode modeset_mode)
-{
-	int i;
-	enum pipe pipe;
-	struct igt_fb fb = {0};
-	drmModeModeInfo mode;
-	struct udev_monitor *mon = igt_watch_uevents();
-	igt_output_t *output = get_output_for_port(data, port);
-
-	igt_modeset_disable_all_outputs(&data->display);
-	chamelium_reset_state(&data->display, data->chamelium, NULL,
-			      data->ports, data->port_count);
-
-
-	igt_hpd_storm_set_threshold(data->drm_fd, 0);
-
-	for (i = 0; i < toggle_count; i++) {
-		igt_flush_uevents(mon);
-
-		/* Check if we get a sysfs hotplug event */
-		chamelium_plug(data->chamelium, port);
-
-		wait_for_connector_after_hotplug(data, mon, port,
-						 DRM_MODE_CONNECTED);
-		igt_flush_uevents(mon);
-
-		if (modeset_mode == TEST_MODESET_ON_OFF ||
-		    (modeset_mode == TEST_MODESET_ON && i == 0 )) {
-			if (i == 0) {
-				/* We can only get mode and pipe once we are connected */
-				output = get_output_for_port(data, port);
-				pipe = get_pipe_for_output(&data->display, output);
-				mode = get_mode_for_port(data->chamelium, port);
-				create_fb_for_mode(data, &fb, &mode);
-			}
-
-			igt_output_set_pipe(output, pipe);
-			enable_output(data, port, output, &mode, &fb);
-		}
-
-		/* Now check if we get a hotplug from disconnection */
-		chamelium_unplug(data->chamelium, port);
-
-		wait_for_connector_after_hotplug(data, mon, port,
-						 DRM_MODE_DISCONNECTED);
-
-		igt_flush_uevents(mon);
-
-		if (modeset_mode == TEST_MODESET_ON_OFF) {
-			igt_output_set_pipe(output, PIPE_NONE);
-			igt_display_commit2(&data->display, COMMIT_ATOMIC);
-		}
-	}
-
-	igt_cleanup_uevents(mon);
-	igt_hpd_storm_reset(data->drm_fd);
-	igt_remove_fb(data->drm_fd, &fb);
-}
-
-static void set_edid(data_t *data, struct chamelium_port *port,
-		     enum igt_custom_edid_type edid)
-{
-	chamelium_port_set_edid(data->chamelium, port, data->edids[edid]);
-}
-
 static const char igt_custom_edid_type_read_desc[] =
 	"Make sure the EDID exposed by KMS is the same as the screen's";
 static void
@@ -485,120 +162,6 @@ igt_custom_edid_type_read(data_t *data, struct chamelium_port *port, enum igt_cu
 	drmModeFreeConnector(connector);
 }
 
-static void
-try_suspend_resume_hpd(data_t *data, struct chamelium_port *port,
-		       enum igt_suspend_state state, enum igt_suspend_test test,
-		       struct udev_monitor *mon, bool connected)
-{
-	drmModeConnection target_state = connected ? DRM_MODE_DISCONNECTED :
-						     DRM_MODE_CONNECTED;
-	int timeout = CHAMELIUM_HOTPLUG_TIMEOUT;
-	int delay;
-	int p;
-
-	igt_flush_uevents(mon);
-
-	delay = igt_get_autoresume_delay(state) * 1000 / 2;
-
-	if (port) {
-		chamelium_schedule_hpd_toggle(data->chamelium, port, delay,
-					      !connected);
-	} else {
-		for (p = 0; p < data->port_count; p++) {
-			port = data->ports[p];
-			chamelium_schedule_hpd_toggle(data->chamelium, port,
-						      delay, !connected);
-		}
-
-		port = NULL;
-	}
-
-	igt_system_suspend_autoresume(state, test);
-	igt_assert(wait_for_hotplug(mon, &timeout));
-	chamelium_assert_reachable(data->chamelium, ONLINE_TIMEOUT);
-
-	if (port) {
-		igt_assert_eq(chamelium_reprobe_connector(&data->display,
-							  data->chamelium,
-							  port),
-							  target_state);
-	} else {
-		for (p = 0; p < data->port_count; p++) {
-			drmModeConnection current_state;
-
-			port = data->ports[p];
-			/*
-			 * There could be as many hotplug events sent by
-			 * driver as connectors we scheduled an HPD toggle on
-			 * above, depending on timing. So if we're not seeing
-			 * the expected connector state try to wait for an HPD
-			 * event for each connector/port.
-			 */
-			current_state = chamelium_reprobe_connector(&data->display, data->chamelium, port);
-			if (p > 0 && current_state != target_state) {
-				igt_assert(wait_for_hotplug(mon, &timeout));
-				current_state = chamelium_reprobe_connector(&data->display, data->chamelium, port);
-			}
-
-			igt_assert_eq(current_state, target_state);
-		}
-
-		port = NULL;
-	}
-}
-
-static const char test_suspend_resume_hpd_desc[] =
-	"Toggle HPD during suspend, check that uevents are sent and connector "
-	"status is updated";
-static void
-test_suspend_resume_hpd(data_t *data, struct chamelium_port *port,
-			enum igt_suspend_state state,
-			enum igt_suspend_test test)
-{
-	struct udev_monitor *mon = igt_watch_uevents();
-
-	igt_modeset_disable_all_outputs(&data->display);
-	chamelium_reset_state(&data->display, data->chamelium,
-			      port, data->ports, data->port_count);
-
-	/* Make sure we notice new connectors after resuming */
-	try_suspend_resume_hpd(data, port, state, test, mon, false);
-
-	/* Now make sure we notice disconnected connectors after resuming */
-	try_suspend_resume_hpd(data, port, state, test, mon, true);
-
-	igt_cleanup_uevents(mon);
-}
-
-static const char test_suspend_resume_hpd_common_desc[] =
-	"Toggle HPD during suspend on all connectors, check that uevents are "
-	"sent and connector status is updated";
-static void
-test_suspend_resume_hpd_common(data_t *data, enum igt_suspend_state state,
-			       enum igt_suspend_test test)
-{
-	struct udev_monitor *mon = igt_watch_uevents();
-	struct chamelium_port *port;
-	int p;
-
-	for (p = 0; p < data->port_count; p++) {
-		port = data->ports[p];
-		igt_debug("Testing port %s\n", chamelium_port_get_name(port));
-	}
-
-	igt_modeset_disable_all_outputs(&data->display);
-	chamelium_reset_state(&data->display, data->chamelium, NULL,
-			      data->ports, data->port_count);
-
-	/* Make sure we notice new connectors after resuming */
-	try_suspend_resume_hpd(data, NULL, state, test, mon, false);
-
-	/* Now make sure we notice disconnected connectors after resuming */
-	try_suspend_resume_hpd(data, NULL, state, test, mon, true);
-
-	igt_cleanup_uevents(mon);
-}
-
 static const char test_suspend_resume_edid_change_desc[] =
 	"Simulate a screen being unplugged and another screen being plugged "
 	"during suspend, check that a uevent is sent and connector status is "
@@ -2468,85 +2031,6 @@ static void test_display_planes_random(data_t *data,
 	igt_remove_fb(data->drm_fd, &result_fb);
 }
 
-static const char test_hpd_without_ddc_desc[] =
-	"Disable DDC on a VGA connector, check we still get a uevent on hotplug";
-static void
-test_hpd_without_ddc(data_t *data, struct chamelium_port *port)
-{
-	struct udev_monitor *mon = igt_watch_uevents();
-
-	igt_modeset_disable_all_outputs(&data->display);
-	chamelium_reset_state(&data->display, data->chamelium,
-			      port, data->ports, data->port_count);
-	igt_flush_uevents(mon);
-
-	/* Disable the DDC on the connector and make sure we still get a
-	 * hotplug
-	 */
-	chamelium_port_set_ddc_state(data->chamelium, port, false);
-	chamelium_plug(data->chamelium, port);
-
-	igt_assert(igt_hotplug_detected(mon, CHAMELIUM_HOTPLUG_TIMEOUT));
-	igt_assert_eq(chamelium_reprobe_connector(&data->display,
-						  data->chamelium, port),
-						  DRM_MODE_CONNECTED);
-
-	igt_cleanup_uevents(mon);
-}
-
-static const char test_hpd_storm_detect_desc[] =
-	"Trigger a series of hotplugs in a very small timeframe to simulate a"
-	"bad cable, check the kernel falls back to polling to avoid a hotplug "
-	"storm";
-static void
-test_hpd_storm_detect(data_t *data, struct chamelium_port *port, int width)
-{
-	struct udev_monitor *mon;
-	int count = 0;
-
-	igt_require_hpd_storm_ctl(data->drm_fd);
-	igt_modeset_disable_all_outputs(&data->display);
-	chamelium_reset_state(&data->display, data->chamelium,
-			      port, data->ports, data->port_count);
-
-	igt_hpd_storm_set_threshold(data->drm_fd, 1);
-	chamelium_fire_hpd_pulses(data->chamelium, port, width, 10);
-	igt_assert(igt_hpd_storm_detected(data->drm_fd));
-
-	mon = igt_watch_uevents();
-	chamelium_fire_hpd_pulses(data->chamelium, port, width, 10);
-
-	/*
-	 * Polling should have been enabled by the HPD storm at this point,
-	 * so we should only get at most 1 hotplug event
-	 */
-	igt_until_timeout(5)
-		count += igt_hotplug_detected(mon, 1);
-	igt_assert_lt(count, 2);
-
-	igt_cleanup_uevents(mon);
-	igt_hpd_storm_reset(data->drm_fd);
-}
-
-static const char test_hpd_storm_disable_desc[] =
-	"Disable HPD storm detection, trigger a storm and check the kernel "
-	"doesn't detect one";
-static void
-test_hpd_storm_disable(data_t *data, struct chamelium_port *port, int width)
-{
-	igt_require_hpd_storm_ctl(data->drm_fd);
-	igt_modeset_disable_all_outputs(&data->display);
-	chamelium_reset_state(&data->display, data->chamelium,
-			      port, data->ports, data->port_count);
-
-	igt_hpd_storm_set_threshold(data->drm_fd, 0);
-	chamelium_fire_hpd_pulses(data->chamelium, port,
-				  width, 10);
-	igt_assert(!igt_hpd_storm_detected(data->drm_fd));
-
-	igt_hpd_storm_reset(data->drm_fd);
-}
-
 static const char igt_edid_stress_resolution_desc[] =
 	"Stress test the DUT by testing multiple EDIDs, one right after the other,"
 	"and ensure their validity by check the real screen resolution vs the"
@@ -2673,58 +2157,21 @@ static void edid_resolution_list(data_t *data, struct chamelium_port *port)
 	drmModeFreeConnector(connector);
 }
 
-#define for_each_port(p, port)            \
-	for (p = 0, port = data.ports[p]; \
-	     p < data.port_count;         \
-	     p++, port = data.ports[p])
-
-#define connector_subtest(name__, type__)                    \
-	igt_subtest(name__)                                  \
-		for_each_port(p, port)                       \
-			if (chamelium_port_get_type(port) == \
-			    DRM_MODE_CONNECTOR_ ## type__)
-
 #define connector_dynamic_subtest(name__, type__)            \
 	igt_subtest_with_dynamic(name__)                     \
 		for_each_port(p, port)                       \
 			if (chamelium_port_get_type(port) == \
 			    DRM_MODE_CONNECTOR_ ## type__)
 
-
-static data_t data;
-
 IGT_TEST_DESCRIPTION("Tests requiring a Chamelium board");
 igt_main
 {
+	data_t data;
 	struct chamelium_port *port;
 	int p;
-	size_t i;
 
 	igt_fixture {
-		/* So fbcon doesn't try to reprobe things itself */
-		kmstest_set_vt_graphics_mode();
-
-		data.drm_fd = drm_open_driver_master(DRIVER_ANY);
-		igt_display_require(&data.display, data.drm_fd);
-		igt_require(data.display.is_atomic);
-
-		/*
-		 * XXX: disabling modeset, can be removed when
-		 * igt_display_require will start doing this for us
-		 */
-		igt_display_commit2(&data.display, COMMIT_ATOMIC);
-
-		/* we need to initalize chamelium after igt_display_require */
-		data.chamelium = chamelium_init(data.drm_fd, &data.display);
-		igt_require(data.chamelium);
-
-		data.ports = chamelium_get_ports(data.chamelium,
-						 &data.port_count);
-
-		for (i = 0; i < IGT_CUSTOM_EDID_COUNT; ++i) {
-			data.edids[i] = chamelium_new_edid(data.chamelium,
-							   igt_kms_get_custom_edid(i));
-		}
+		init_chamelium(&data);
 	}
 
 	igt_describe("DisplayPort tests");
@@ -2734,30 +2181,6 @@ igt_main
 							    data.port_count, 1);
 		}
 
-		igt_describe(test_basic_hotplug_desc);
-		connector_subtest("dp-hpd", DisplayPort)
-			test_hotplug(&data, port,
-				     HPD_TOGGLE_COUNT_DP_HDMI,
-				     TEST_MODESET_OFF);
-
-		igt_describe(test_basic_hotplug_desc);
-		connector_subtest("dp-hpd-fast", DisplayPort)
-			test_hotplug(&data, port,
-				     HPD_TOGGLE_COUNT_FAST,
-				     TEST_MODESET_OFF);
-
-		igt_describe(test_basic_hotplug_desc);
-		connector_subtest("dp-hpd-enable-disable-mode", DisplayPort)
-			test_hotplug(&data, port,
-				     HPD_TOGGLE_COUNT_FAST,
-				     TEST_MODESET_ON_OFF);
-
-		igt_describe(test_basic_hotplug_desc);
-		connector_subtest("dp-hpd-with-enabled-mode", DisplayPort)
-			test_hotplug(&data, port,
-				     HPD_TOGGLE_COUNT_FAST,
-				     TEST_MODESET_ON);
-
 		igt_describe(igt_custom_edid_type_read_desc);
 		connector_subtest("dp-edid-read", DisplayPort) {
 			igt_custom_edid_type_read(&data, port, IGT_CUSTOM_EDID_BASE);
@@ -2779,28 +2202,6 @@ igt_main
 		connector_subtest("dp-edid-resolution-list", DisplayPort)
 			edid_resolution_list(&data, port);
 
-		igt_describe(test_suspend_resume_hpd_desc);
-		connector_subtest("dp-hpd-after-suspend", DisplayPort)
-			test_suspend_resume_hpd(&data, port,
-						SUSPEND_STATE_MEM,
-						SUSPEND_TEST_NONE);
-
-		igt_describe(test_suspend_resume_hpd_desc);
-		connector_subtest("dp-hpd-after-hibernate", DisplayPort)
-			test_suspend_resume_hpd(&data, port,
-						SUSPEND_STATE_DISK,
-						SUSPEND_TEST_DEVICES);
-
-		igt_describe(test_hpd_storm_detect_desc);
-		connector_subtest("dp-hpd-storm", DisplayPort)
-			test_hpd_storm_detect(&data, port,
-					      HPD_STORM_PULSE_INTERVAL_DP);
-
-		igt_describe(test_hpd_storm_disable_desc);
-		connector_subtest("dp-hpd-storm-disable", DisplayPort)
-			test_hpd_storm_disable(&data, port,
-					       HPD_STORM_PULSE_INTERVAL_DP);
-
 		igt_describe(test_suspend_resume_edid_change_desc);
 		connector_subtest("dp-edid-change-during-suspend", DisplayPort)
 			test_suspend_resume_edid_change(&data, port,
@@ -2849,10 +2250,6 @@ igt_main
 		connector_subtest("dp-audio-edid", DisplayPort)
 			test_display_audio_edid(&data, port,
 						IGT_CUSTOM_EDID_DP_AUDIO);
-
-		igt_describe(test_hotplug_for_each_pipe_desc);
-		connector_subtest("dp-hpd-for-each-pipe", DisplayPort)
-			test_hotplug_for_each_pipe(&data, port);
 	}
 
 	igt_describe("HDMI tests");
@@ -2862,30 +2259,6 @@ igt_main
 							    data.port_count, 1);
 		}
 
-		igt_describe(test_basic_hotplug_desc);
-		connector_subtest("hdmi-hpd", HDMIA)
-			test_hotplug(&data, port,
-				     HPD_TOGGLE_COUNT_DP_HDMI,
-				     TEST_MODESET_OFF);
-
-		igt_describe(test_basic_hotplug_desc);
-		connector_subtest("hdmi-hpd-fast", HDMIA)
-			test_hotplug(&data, port,
-				     HPD_TOGGLE_COUNT_FAST,
-				     TEST_MODESET_OFF);
-
-		igt_describe(test_basic_hotplug_desc);
-		connector_subtest("hdmi-hpd-enable-disable-mode", HDMIA)
-			test_hotplug(&data, port,
-				     HPD_TOGGLE_COUNT_FAST,
-				     TEST_MODESET_ON_OFF);
-
-		igt_describe(test_basic_hotplug_desc);
-		connector_subtest("hdmi-hpd-with-enabled-mode", HDMIA)
-			test_hotplug(&data, port,
-				     HPD_TOGGLE_COUNT_FAST,
-				     TEST_MODESET_ON);
-
 		igt_describe(igt_custom_edid_type_read_desc);
 		connector_subtest("hdmi-edid-read", HDMIA) {
 			igt_custom_edid_type_read(&data, port, IGT_CUSTOM_EDID_BASE);
@@ -2902,28 +2275,6 @@ igt_main
 			edid_stress_resolution(&data, port, HDMI_EDIDS_NON_4K,
 					       ARRAY_SIZE(HDMI_EDIDS_NON_4K));
 
-		igt_describe(test_suspend_resume_hpd_desc);
-		connector_subtest("hdmi-hpd-after-suspend", HDMIA)
-			test_suspend_resume_hpd(&data, port,
-						SUSPEND_STATE_MEM,
-						SUSPEND_TEST_NONE);
-
-		igt_describe(test_suspend_resume_hpd_desc);
-		connector_subtest("hdmi-hpd-after-hibernate", HDMIA)
-			test_suspend_resume_hpd(&data, port,
-						SUSPEND_STATE_DISK,
-						SUSPEND_TEST_DEVICES);
-
-		igt_describe(test_hpd_storm_detect_desc);
-		connector_subtest("hdmi-hpd-storm", HDMIA)
-			test_hpd_storm_detect(&data, port,
-					      HPD_STORM_PULSE_INTERVAL_HDMI);
-
-		igt_describe(test_hpd_storm_disable_desc);
-		connector_subtest("hdmi-hpd-storm-disable", HDMIA)
-			test_hpd_storm_disable(&data, port,
-					       HPD_STORM_PULSE_INTERVAL_HDMI);
-
 		igt_describe(test_suspend_resume_edid_change_desc);
 		connector_subtest("hdmi-edid-change-during-suspend", HDMIA)
 			test_suspend_resume_edid_change(&data, port,
@@ -3038,10 +2389,6 @@ igt_main
 		igt_describe(test_display_aspect_ratio_desc);
 		connector_subtest("hdmi-aspect-ratio", HDMIA)
 			test_display_aspect_ratio(&data, port);
-
-		igt_describe(test_hotplug_for_each_pipe_desc);
-		connector_subtest("hdmi-hpd-for-each-pipe", HDMIA)
-			test_hotplug_for_each_pipe(&data, port);
 	}
 
 	igt_describe("VGA tests");
@@ -3051,80 +2398,18 @@ igt_main
 							    data.port_count, 1);
 		}
 
-		igt_describe(test_basic_hotplug_desc);
-		connector_subtest("vga-hpd", VGA)
-			test_hotplug(&data, port, HPD_TOGGLE_COUNT_VGA,
-				     TEST_MODESET_OFF);
-
-		igt_describe(test_basic_hotplug_desc);
-		connector_subtest("vga-hpd-fast", VGA)
-			test_hotplug(&data, port, HPD_TOGGLE_COUNT_FAST,
-				     TEST_MODESET_OFF);
-
-		igt_describe(test_basic_hotplug_desc);
-		connector_subtest("vga-hpd-enable-disable-mode", VGA)
-			test_hotplug(&data, port,
-				     HPD_TOGGLE_COUNT_FAST,
-				     TEST_MODESET_ON_OFF);
-
-		igt_describe(test_basic_hotplug_desc);
-		connector_subtest("vga-hpd-with-enabled-mode", VGA)
-			test_hotplug(&data, port,
-				     HPD_TOGGLE_COUNT_FAST,
-				     TEST_MODESET_ON);
-
 		igt_describe(igt_custom_edid_type_read_desc);
 		connector_subtest("vga-edid-read", VGA) {
 			igt_custom_edid_type_read(&data, port, IGT_CUSTOM_EDID_BASE);
 			igt_custom_edid_type_read(&data, port, IGT_CUSTOM_EDID_ALT);
 		}
 
-		igt_describe(test_suspend_resume_hpd_desc);
-		connector_subtest("vga-hpd-after-suspend", VGA)
-			test_suspend_resume_hpd(&data, port,
-						SUSPEND_STATE_MEM,
-						SUSPEND_TEST_NONE);
-
-		igt_describe(test_suspend_resume_hpd_desc);
-		connector_subtest("vga-hpd-after-hibernate", VGA)
-			test_suspend_resume_hpd(&data, port,
-						SUSPEND_STATE_DISK,
-						SUSPEND_TEST_DEVICES);
-
-		igt_describe(test_hpd_without_ddc_desc);
-		connector_subtest("vga-hpd-without-ddc", VGA)
-			test_hpd_without_ddc(&data, port);
-
 		igt_describe(test_display_all_modes_desc);
 		connector_subtest("vga-frame-dump", VGA)
 			test_display_all_modes(&data, port, DRM_FORMAT_XRGB8888,
 					       CHAMELIUM_CHECK_ANALOG, 1);
 	}
 
-	igt_describe("Tests that operate on all connectors");
-	igt_subtest_group {
-
-		igt_fixture {
-			igt_require(data.port_count);
-		}
-
-		igt_describe(test_suspend_resume_hpd_common_desc);
-		igt_subtest("common-hpd-after-suspend")
-			test_suspend_resume_hpd_common(&data,
-						       SUSPEND_STATE_MEM,
-						       SUSPEND_TEST_NONE);
-
-		igt_describe(test_suspend_resume_hpd_common_desc);
-		igt_subtest("common-hpd-after-hibernate")
-			test_suspend_resume_hpd_common(&data,
-						       SUSPEND_STATE_DISK,
-						       SUSPEND_TEST_DEVICES);
-	}
-
-	igt_describe(test_hotplug_for_each_pipe_desc);
-	connector_subtest("vga-hpd-for-each-pipe", VGA)
-		test_hotplug_for_each_pipe(&data, port);
-
 	igt_fixture {
 		igt_display_fini(&data.display);
 		close(data.drm_fd);
diff --git a/tests/kms_color_helper.h b/tests/kms_color_helper.h
index f0ae30e3..9d3b5cf6 100644
--- a/tests/kms_color_helper.h
+++ b/tests/kms_color_helper.h
@@ -27,7 +27,7 @@
 
 /*
  * This header is for code that is shared between kms_color.c and
- * kms_color_chamelium.c. Reusability elsewhere can be questionable.
+ * chamelium_color.c. Reusability elsewhere can be questionable.
  */
 
 #include <math.h>
diff --git a/tests/meson.build b/tests/meson.build
index 12e53e0b..83a1bc39 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -260,6 +260,7 @@ msm_progs = [
 
 chamelium_progs = [
 	'kms_chamelium',
+	'chamelium_hpd',
 ]
 
 test_deps = [ igt_deps ]
@@ -308,7 +309,7 @@ endforeach
 if chamelium.found()
 	foreach prog : chamelium_progs
 		test_executables += executable(prog,
-				 join_paths('chamelium', prog + '.c'),
+				 [join_paths('chamelium', prog + '.c'), join_paths('chamelium', 'chamelium_helper.c')],
 				 dependencies : test_deps,
 				 install_dir : libexecdir,
 				 install_rpath : libexecdir_rpathdir,
@@ -435,13 +436,13 @@ test_executables += executable('kms_color',
 test_list += 'kms_color'
 
 if chamelium.found()
-       test_executables += executable('kms_color_chamelium',
-                             [ 'chamelium/kms_color_chamelium.c', 'kms_color_helper.c' ],
+       test_executables += executable('chamelium_color',
+                             [ 'chamelium/chamelium_color.c', 'kms_color_helper.c' ],
                              dependencies : test_deps + [ chamelium ],
                              install_dir : libexecdir,
                              install_rpath : libexecdir_rpathdir,
                              install : true)
-       test_list += 'kms_color_chamelium'
+       test_list += 'chamelium_color'
 endif
 
 test_executables += executable('sw_sync', 'sw_sync.c',
-- 
2.38.1.584.g0f3c55d4c2-goog



More information about the igt-dev mailing list