public inbox for igt-dev@lists.freedesktop.org
 help / color / mirror / Atom feed
* [PATCH i-g-t v1 00/25] Chamelium v3 Integration and Test Execution
@ 2026-04-28  4:46 Mohammed Bilal
  2026-04-28  4:46 ` [PATCH i-g-t v1 01/25] lib/igt_kms: Add a detect timeout value Mohammed Bilal
                   ` (28 more replies)
  0 siblings, 29 replies; 37+ messages in thread
From: Mohammed Bilal @ 2026-04-28  4:46 UTC (permalink / raw)
  To: igt-dev; +Cc: kunal1.joshi, Mohammed Bilal

Integrate Chamelium v3 hardware support for display testing. This includes
the Chamelium v3 library implementation, building with v3 support enabled,
and comprehensive display tests for HPD, EDID, frames, color, and audio.

Changes included:

1. Library helpers (patches 1-10):
   - Add connector detection and management helpers
   - Add monitor EDID management helpers
   - Add HDMI 4K EDID support

2. V2 reorganization (patches 11-14):
   - Move existing V2 code to lib/chamelium/v2/ and tests/chamelium/v2/
   - Rename V2 symbols to allow V2 and V3 to coexist

3. V3 library (patches 15-20):
   - Add Chamelium V3 library implementation
   - Support for HPD, EDID, frame capture, color and audio

4. V3 tests (patches 21-25):
   - kms_chamelium_v3_basic: Basic connectivity test
   - kms_chamelium_v3_hpd: Hot plug detect tests
   - kms_chamelium_v3_edid: EDID verification tests
   - kms_chamelium_v3_frames: Frame capture and CRC tests
   - kms_chamelium_v3_color: Color verification tests
   - kms_chamelium_v3_audio: Audio tests

Louis Chauvet (19):
  lib/igt_kms: Add a detect timeout value
  lib/igt_kms: Add helper to wait for a specific status on a connector
  lib/igt_kms: Add function to list connected connectors
  lib/igt_kms: Add helper to obtain a connector by its name or MST path
  lib/igt_kms: Add function to get valid pipe for specific output
  lib/monitor_edids: Add helper functions for using monitor_edid objects
  lib/monitor_edids: Add helper to get an EDID by its name
  lib/monitor_edids: Add helper to print all available EDID names
  lib/monitor_edids: Fix missing names in some monitor EDID
  lib/monitor_edids: Add new EDID for HDMI 4k
  tests/chamelium: Extract Chamelium v2 tests into a separate directory
  lib/chamelium/v2: Extract chamelium v2 wrapper into its own directory
  lib/chamelium/v2: Rename chamelium to chamelium_v2
  lib/chamelium/v2: Rename HAVE_CHAMELIUM to HAVE_CHAMELIUM_V2
  lib/chamelium/v3: Introduce the foundation for the Chamelium v3
    wrapper
  lib/chamelium/v3: Introduce initialization and cleanup of
    Chamelium-related structures
  lib/chamelium/v3: Add method to discover Chamelium ports
  lib/chamelium/v3: Implement method to retrieve Chamelium port names
  tests/chamelium/v3: Implement a basic Chamelium v3 accessibility test

Mohammed Bilal (6):
  lib/chamelium/v3: Add extended API for Chamelium v3 HPD, EDID, Frames,
    Color & Audio
  tests/chamelium/v3: Add HPD (Hot Plug Detect) tests for Chamelium v3
  tests/chamelium/v3: Add EDID tests for Chamelium v3
  tests/chamelium/v3: Add frame capture and CRC tests for Chamelium v3
  tests/chamelium/v3: Add color verification tests for Chamelium v3
  tests/chamelium/v3: Add audio tests for Chamelium v3

 .gitlab-ci.yml                                |    2 +-
 docs/testplan/meson.build                     |    4 +-
 lib/{ => chamelium/v2}/igt_chamelium.c        |    2 +-
 lib/{ => chamelium/v2}/igt_chamelium.h        |    0
 lib/{ => chamelium/v2}/igt_chamelium_stream.c |    2 +-
 lib/{ => chamelium/v2}/igt_chamelium_stream.h |    0
 lib/chamelium/v3/igt_chamelium.c              | 1267 ++++++++++++++
 lib/chamelium/v3/igt_chamelium.h              |  247 +++
 lib/igt.h                                     |    6 +-
 lib/igt_core.c                                |    4 +
 lib/igt_kms.c                                 |  371 +++-
 lib/igt_kms.h                                 |   18 +
 lib/meson.build                               |   21 +-
 lib/monitor_edids/dp_edids.h                  |    3 +
 lib/monitor_edids/hdmi_edids.h                |   27 +-
 lib/monitor_edids/monitor_edids_helper.c      |  127 ++
 lib/monitor_edids/monitor_edids_helper.h      |   12 +-
 lib/tests/meson.build                         |    8 +-
 meson.build                                   |   31 +-
 meson_options.txt                             |    8 +-
 .../chamelium/{ => v2}/kms_chamelium_audio.c  |    0
 .../chamelium/{ => v2}/kms_chamelium_color.c  |    0
 tests/chamelium/{ => v2}/kms_chamelium_edid.c |    2 +-
 .../chamelium/{ => v2}/kms_chamelium_frames.c |    0
 .../chamelium/{ => v2}/kms_chamelium_helper.c |    0
 .../chamelium/{ => v2}/kms_chamelium_helper.h |    0
 tests/chamelium/{ => v2}/kms_chamelium_hpd.c  |    0
 .../{ => v2}/kms_chamelium_sharpness_filter.c |    0
 tests/chamelium/v3/kms_chamelium_v3_audio.c   |  707 ++++++++
 tests/chamelium/v3/kms_chamelium_v3_basic.c   |   30 +
 tests/chamelium/v3/kms_chamelium_v3_color.c   | 1058 +++++++++++
 tests/chamelium/v3/kms_chamelium_v3_edid.c    | 1357 ++++++++++++++
 tests/chamelium/v3/kms_chamelium_v3_frames.c  | 1551 +++++++++++++++++
 tests/chamelium/v3/kms_chamelium_v3_hpd.c     |  928 ++++++++++
 tests/kms_color_helper.h                      |    2 +-
 tests/kms_feature_discovery.c                 |    6 +-
 tests/kms_tiled_display.c                     |    6 +-
 tests/meson.build                             |   53 +-
 38 files changed, 7762 insertions(+), 98 deletions(-)
 rename lib/{ => chamelium/v2}/igt_chamelium.c (99%)
 rename lib/{ => chamelium/v2}/igt_chamelium.h (100%)
 rename lib/{ => chamelium/v2}/igt_chamelium_stream.c (99%)
 rename lib/{ => chamelium/v2}/igt_chamelium_stream.h (100%)
 create mode 100644 lib/chamelium/v3/igt_chamelium.c
 create mode 100644 lib/chamelium/v3/igt_chamelium.h
 rename tests/chamelium/{ => v2}/kms_chamelium_audio.c (100%)
 rename tests/chamelium/{ => v2}/kms_chamelium_color.c (100%)
 rename tests/chamelium/{ => v2}/kms_chamelium_edid.c (99%)
 rename tests/chamelium/{ => v2}/kms_chamelium_frames.c (100%)
 rename tests/chamelium/{ => v2}/kms_chamelium_helper.c (100%)
 rename tests/chamelium/{ => v2}/kms_chamelium_helper.h (100%)
 rename tests/chamelium/{ => v2}/kms_chamelium_hpd.c (100%)
 rename tests/chamelium/{ => v2}/kms_chamelium_sharpness_filter.c (100%)
 create mode 100644 tests/chamelium/v3/kms_chamelium_v3_audio.c
 create mode 100644 tests/chamelium/v3/kms_chamelium_v3_basic.c
 create mode 100644 tests/chamelium/v3/kms_chamelium_v3_color.c
 create mode 100644 tests/chamelium/v3/kms_chamelium_v3_edid.c
 create mode 100644 tests/chamelium/v3/kms_chamelium_v3_frames.c
 create mode 100644 tests/chamelium/v3/kms_chamelium_v3_hpd.c

-- 
2.48.1


^ permalink raw reply	[flat|nested] 37+ messages in thread

* [PATCH i-g-t v1 01/25] lib/igt_kms: Add a detect timeout value
  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 ` Mohammed Bilal
  2026-04-28  7:11   ` Jani Nikula
                     ` (2 more replies)
  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
                   ` (27 subsequent siblings)
  28 siblings, 3 replies; 37+ messages in thread
From: Mohammed Bilal @ 2026-04-28  4:46 UTC (permalink / raw)
  To: igt-dev; +Cc: kunal1.joshi, Louis Chauvet, Kory Maincent, Luca Ceresoli

From: Louis Chauvet <louis.chauvet@bootlin.com>

Some tests need to wait for a specific connector status. In order to make
the timeout customisable for each target, add an option in the
configuration file.

Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
Reviewed-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
 lib/igt_core.c |   4 ++
 lib/igt_kms.c  | 123 +++++++++++++++++++++++++++++++------------------
 lib/igt_kms.h  |  10 ++++
 3 files changed, 92 insertions(+), 45 deletions(-)

diff --git a/lib/igt_core.c b/lib/igt_core.c
index 4f79c0294..cc835ea4c 100644
--- a/lib/igt_core.c
+++ b/lib/igt_core.c
@@ -271,6 +271,10 @@
  *	&num; It is not mandatory and allows overriding default values.
  *	[DUT]
  *	SuspendResumeDelay=10
+ *
+ *	&num; The following option define the timeout for detection feature
+ *	&num; (waiting for a connector status)
+ *	DisplayDetectTimeout=10.0
  * ]|
  *
  * Some specific configuration options may be used by specific parts of IGT,
diff --git a/lib/igt_kms.c b/lib/igt_kms.c
index 38c28f45f..ece888d1a 100644
--- a/lib/igt_kms.c
+++ b/lib/igt_kms.c
@@ -60,6 +60,7 @@
 #include "igt_debugfs.h"
 #include "igt_device.h"
 #include "igt_pipe_crc.h"
+#include "igt_rc.h"
 #include "igt_sysfs.h"
 #include "sw_sync.h"
 #ifdef HAVE_CHAMELIUM
@@ -8066,38 +8067,38 @@ bool igt_has_lobf_debugfs(int drmfd, igt_output_t *output)
 
 igt_crtc_t *igt_crtc_for_crtc_id(igt_display_t *display, uint32_t crtc_id)
 {
-	igt_crtc_t *crtc;
+        igt_crtc_t *crtc;
 
-	for_each_crtc(display, crtc) {
-		if (crtc->crtc_id == crtc_id)
-			return crtc;
-	}
+        for_each_crtc(display, crtc) {
+                if (crtc->crtc_id == crtc_id)
+                        return crtc;
+        }
 
-	return NULL;
+        return NULL;
 }
 
 igt_crtc_t *igt_crtc_for_crtc_index(igt_display_t *display, int crtc_index)
 {
-	igt_crtc_t *crtc;
+        igt_crtc_t *crtc;
 
-	for_each_crtc(display, crtc) {
-		if (crtc->crtc_index == crtc_index)
-			return crtc;
-	}
+        for_each_crtc(display, crtc) {
+                if (crtc->crtc_index == crtc_index)
+                        return crtc;
+        }
 
-	return NULL;
+        return NULL;
 }
 
 igt_crtc_t *igt_crtc_for_pipe(igt_display_t *display, enum pipe pipe)
 {
-	igt_crtc_t *crtc;
+        igt_crtc_t *crtc;
 
-	for_each_crtc(display, crtc) {
-		if (crtc->pipe == pipe)
-			return crtc;
-	}
+        for_each_crtc(display, crtc) {
+                if (crtc->pipe == pipe)
+                        return crtc;
+        }
 
-	return NULL;
+        return NULL;
 }
 
 /*
@@ -8108,14 +8109,14 @@ igt_crtc_t *igt_crtc_for_pipe(igt_display_t *display, enum pipe pipe)
  */
 igt_crtc_t *igt_first_crtc(igt_display_t *display)
 {
-	igt_crtc_t *crtc;
+        igt_crtc_t *crtc;
 
-	for_each_crtc(display, crtc)
-		return crtc;
+        for_each_crtc(display, crtc)
+                return crtc;
 
-	igt_skip("No CRTCs on device\n");
+        igt_skip("No CRTCs on device\n");
 
-	return NULL;
+        return NULL;
 }
 
 /**
@@ -8128,19 +8129,19 @@ igt_crtc_t *igt_first_crtc(igt_display_t *display)
  */
 igt_crtc_t *igt_first_crtc_with_single_output(igt_display_t *display, igt_output_t **ret_output)
 {
-	igt_output_t *output;
-	igt_crtc_t *crtc;
+        igt_output_t *output;
+        igt_crtc_t *crtc;
 
-	for_each_crtc_with_single_output(display, crtc, output) {
-		*ret_output = output;
-		return crtc;
-	}
+        for_each_crtc_with_single_output(display, crtc, output) {
+                *ret_output = output;
+                return crtc;
+        }
 
-	igt_skip("No valid CRTC/output combinations\n");
+        igt_skip("No valid CRTC/output combinations\n");
 
-	*ret_output = NULL;
+        *ret_output = NULL;
 
-	return NULL;
+        return NULL;
 }
 
 /*
@@ -8152,14 +8153,14 @@ igt_crtc_t *igt_first_crtc_with_single_output(igt_display_t *display, igt_output
  */
 igt_crtc_t *igt_next_crtc(igt_display_t *display, igt_crtc_t *crtc)
 {
-	igt_crtc_t *next;
+        igt_crtc_t *next;
 
-	for_each_crtc(display, next) {
-		if (!crtc || next->pipe > crtc->pipe)
-			return next;
-	}
+        for_each_crtc(display, next) {
+                if (!crtc || next->pipe > crtc->pipe)
+                        return next;
+        }
 
-	return NULL;
+        return NULL;
 }
 
 /*
@@ -8170,14 +8171,46 @@ igt_crtc_t *igt_next_crtc(igt_display_t *display, igt_crtc_t *crtc)
  */
 igt_crtc_t *igt_random_crtc(igt_display_t *display)
 {
-	igt_crtc_t *crtcs[IGT_MAX_PIPES];
-	igt_crtc_t *crtc;
-	int n = 0;
+        igt_crtc_t *crtcs[IGT_MAX_PIPES];
+        igt_crtc_t *crtc;
+        int n = 0;
 
-	for_each_crtc(display, crtc)
-		crtcs[n++] = crtc;
+        for_each_crtc(display, crtc)
+                crtcs[n++] = crtc;
+
+        igt_skip_on_f(!n, "No CRTCs on device\n");
+
+        return crtcs[rand() % n];
+}
+
+/**
+ * igt_default_display_detect_timeout:
+ *
+ * Get the default timeout value for detection feature
+ *
+ * Some tests requires to wait for a specific connector status. This value will determine the
+ * timeout value for this waiting.
+ */
+double igt_default_display_detect_timeout(void)
+{
+        static double timeout = 0.0;
+        static bool first_call = true;
+        GError *error = NULL;
+
+        if (first_call) {
+                if (igt_key_file) {
+                        timeout = g_key_file_get_double(igt_key_file, "DUT", "DisplayDetectTimeout",&error);
+                        if (error) {
+                                igt_debug("Failed to read DisplayDetectTimeout, defaulting to %f\n",DEFAULT_DETECT_TIMEOUT);
+                                g_clear_error(&error);
+                                timeout = DEFAULT_DETECT_TIMEOUT;
+                        }
+                } else {
+                        timeout = DEFAULT_DETECT_TIMEOUT;
+                }
 
-	igt_skip_on_f(!n, "No CRTCs on device\n");
+                first_call = false;
+        }
 
-	return crtcs[rand() % n];
+        return timeout;
 }
diff --git a/lib/igt_kms.h b/lib/igt_kms.h
index fcbb6a5ad..b10df1d8d 100644
--- a/lib/igt_kms.h
+++ b/lib/igt_kms.h
@@ -40,6 +40,14 @@
 #include "igt_fb.h"
 #include "ioctl_wrappers.h"
 
+/**
+ * define DEFAULT_DETECT_TIMEOUT - Default timeout in second used for some screen detection
+ * functions
+ *
+ * It can be overiden by option DetectTimeout in the .igtrc file.
+ */
+#define DEFAULT_DETECT_TIMEOUT 10.0
+
 /* Low-level helpers with kmstest_ prefix */
 
 /**
@@ -1289,4 +1297,6 @@ void igt_get_and_wait_out_fence(igt_output_t *output);
 
 igt_colorop_t *igt_find_colorop(igt_display_t *display, uint32_t id);
 
+double igt_default_display_detect_timeout(void);
+
 #endif /* __IGT_KMS_H__ */
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH i-g-t v1 02/25] lib/igt_kms: Add helper to wait for a specific status on a connector
  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  4:46 ` 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
                   ` (26 subsequent siblings)
  28 siblings, 0 replies; 37+ messages in thread
From: Mohammed Bilal @ 2026-04-28  4:46 UTC (permalink / raw)
  To: igt-dev; +Cc: kunal1.joshi, Louis Chauvet, Kory Maincent, Luca Ceresoli

From: Louis Chauvet <louis.chauvet@bootlin.com>

During testing with chamelium, it is frequent to wait for a specific
connector status. This new helper is polling the DRM API to wait for this
status. This allows detecting it without notifier systems which can fail
if hot plug detection is not working properly on the device under test.

Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
Reviewed-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
 lib/igt_kms.c | 39 +++++++++++++++++++++++++++++++++++++++
 lib/igt_kms.h |  3 +++
 2 files changed, 42 insertions(+)

diff --git a/lib/igt_kms.c b/lib/igt_kms.c
index ece888d1a..25184531b 100644
--- a/lib/igt_kms.c
+++ b/lib/igt_kms.c
@@ -8214,3 +8214,42 @@ double igt_default_display_detect_timeout(void)
 
         return timeout;
 }
+
+/**
+ * igt_wait_for_connector_status:
+ * @drm_fd: drm file descriptor
+ * @connector_id: connector to monitor
+ * @timeout: maximum duration to wait, in second. Use -1.0 to set the timeout
+ *           to igt_default_detect_timeout().
+ * @drm_mode: mode to wait for, see enum drmModeConnection
+ *
+ * Wait for at most @timeout that the connector @connector_id  status
+ * become @drm_mode
+ * Returns: true when the status is reached, false if there is a timeout
+ */
+bool igt_wait_for_connector_status(int drm_fd, unsigned int connector_id, double timeout,
+				   int drm_mode)
+{
+	drmModeConnector *connector;
+	struct timespec start, end;
+
+	if (timeout == -1.0)
+		timeout = igt_default_display_detect_timeout();
+
+	clock_gettime(CLOCK_MONOTONIC, &start);
+	end = start;
+
+	while (igt_time_elapsed(&start, &end) <= timeout) {
+		connector = drmModeGetConnector(drm_fd, connector_id);
+		if (connector && connector->connection == drm_mode) {
+			free(connector);
+			return true;
+		}
+		free(connector);
+		clock_gettime(CLOCK_MONOTONIC, &end);
+	}
+
+	igt_debug("Timeout waiting for connection status %d on connector %d\n", drm_mode,
+		  connector_id);
+	return false;
+}
diff --git a/lib/igt_kms.h b/lib/igt_kms.h
index b10df1d8d..60cfc1763 100644
--- a/lib/igt_kms.h
+++ b/lib/igt_kms.h
@@ -1299,4 +1299,7 @@ igt_colorop_t *igt_find_colorop(igt_display_t *display, uint32_t id);
 
 double igt_default_display_detect_timeout(void);
 
+bool igt_wait_for_connector_status(int drm_fd, unsigned int connector_id, double timeout,
+				   int drm_mode);
+
 #endif /* __IGT_KMS_H__ */
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH i-g-t v1 03/25] lib/igt_kms: Add function to list connected connectors
  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  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 ` 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
                   ` (25 subsequent siblings)
  28 siblings, 0 replies; 37+ messages in thread
From: Mohammed Bilal @ 2026-04-28  4:46 UTC (permalink / raw)
  To: igt-dev; +Cc: kunal1.joshi, Louis Chauvet, Kory Maincent, Luca Ceresoli

From: Louis Chauvet <louis.chauvet@bootlin.com>

Introduce the igt_get_connected_connectors() function, which returns a
list of connector IDs that are currently connected according to DRM.

This function includes a timeout mechanism because connectors can be
unplugged at any time. Also, especially with MST, the connector can be
listed by drmModeGetResources() but not yet accessible with
drmModeGetConnector().

Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
Reviewed-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
 lib/igt_kms.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/igt_kms.h |  1 +
 2 files changed, 66 insertions(+)

diff --git a/lib/igt_kms.c b/lib/igt_kms.c
index 25184531b..2ae283e93 100644
--- a/lib/igt_kms.c
+++ b/lib/igt_kms.c
@@ -8215,6 +8215,29 @@ double igt_default_display_detect_timeout(void)
         return timeout;
 }
 
+static drmModeConnectorPtr igt_wait_for_connector(int drm_fd, unsigned int connector_id,
+						  double timeout)
+{
+	drmModeConnectorPtr connector = NULL;
+	struct timespec start, end;
+
+	connector = drmModeGetConnector(drm_fd, connector_id);
+	/*
+	 * This time is required as sometimes some id in the connector list are not totally
+	 * ready or can disappear
+	 */
+	clock_gettime(CLOCK_MONOTONIC, &start);
+	end = start;
+
+	while (!connector &&
+	       igt_time_elapsed(&start, &end) <= timeout) {
+		connector = drmModeGetConnector(drm_fd, connector_id);
+		clock_gettime(CLOCK_MONOTONIC, &end);
+	}
+
+	return connector;
+}
+
 /**
  * igt_wait_for_connector_status:
  * @drm_fd: drm file descriptor
@@ -8253,3 +8276,45 @@ bool igt_wait_for_connector_status(int drm_fd, unsigned int connector_id, double
 		  connector_id);
 	return false;
 }
+
+/**
+ * igt_get_connected_connectors:
+ *
+ * @drm_fd: DRM file description
+ * @connector_ids: Out pointer for the list of connector ids connected. It must be freed by the
+ * caller.
+ *
+ * This will probe all the connectors and return the list of all
+ * connected connectors.
+ * Returns: The number of connectors in @connector_ids
+ */
+int igt_get_connected_connectors(int drm_fd, uint32_t **connector_ids)
+{
+	int connected_count = 0;
+	double timeout = igt_default_display_detect_timeout();
+	drmModeResPtr resources;
+
+	*connector_ids = NULL;
+
+	resources = drmModeGetResources(drm_fd);
+	for (int j = 0; j < resources->count_connectors; j++) {
+		drmModeConnectorPtr connector = igt_wait_for_connector(drm_fd,
+								       resources->connectors[j],
+								       timeout);
+
+		if (connector) {
+			if (connector->connection == DRM_MODE_CONNECTED) {
+				*connector_ids = reallocarray(*connector_ids,
+							      connected_count
+							      + 1, sizeof(**connector_ids));
+				igt_assert(*connector_ids);
+				(*connector_ids)[connected_count] = resources->connectors[j];
+				connected_count++;
+			}
+			drmModeFreeConnector(connector);
+		}
+	}
+	drmModeFreeResources(resources);
+
+	return connected_count;
+}
diff --git a/lib/igt_kms.h b/lib/igt_kms.h
index 60cfc1763..71d77a3c2 100644
--- a/lib/igt_kms.h
+++ b/lib/igt_kms.h
@@ -1301,5 +1301,6 @@ double igt_default_display_detect_timeout(void);
 
 bool igt_wait_for_connector_status(int drm_fd, unsigned int connector_id, double timeout,
 				   int drm_mode);
+int igt_get_connected_connectors(int drm_fd, uint32_t **connector_ids);
 
 #endif /* __IGT_KMS_H__ */
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH i-g-t v1 04/25] lib/igt_kms: Add helper to obtain a connector by its name or MST path
  2026-04-28  4:46 [PATCH i-g-t v1 00/25] Chamelium v3 Integration and Test Execution Mohammed Bilal
                   ` (2 preceding siblings ...)
  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 ` 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
                   ` (24 subsequent siblings)
  28 siblings, 0 replies; 37+ messages in thread
From: Mohammed Bilal @ 2026-04-28  4:46 UTC (permalink / raw)
  To: igt-dev; +Cc: kunal1.joshi, Louis Chauvet, Luca Ceresoli, Kory Maincent

From: Louis Chauvet <louis.chauvet@bootlin.com>

Introduce the functions igt_get_connector_from_name() and
igt_get_connector_id_from_mst_path(). These functions serve to retrieve
the connector object using a connector name and a connector ID from its
MST path, respectively.

Given that the connector id may not be consistent, especially with MST
connectors, these functions are essential to recognize each connector even
after system reboots and plug/unplug events.

The function igt_get_connector_id_from_name() is a convenient wrapper for
igt_get_connector_from_name() to streamline its usage when the caller only
requires the connector id.

Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
Reviewed-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
---
 lib/igt_kms.c | 108 ++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/igt_kms.h |   3 ++
 2 files changed, 111 insertions(+)

diff --git a/lib/igt_kms.c b/lib/igt_kms.c
index 2ae283e93..aa086e96c 100644
--- a/lib/igt_kms.c
+++ b/lib/igt_kms.c
@@ -95,6 +95,10 @@
 #define MAX_EDID 2
 #define DISPLAY_TILE_BLOCK 0x12
 #define MAX_NUM_COLOROPS 256
+/**
+ * define IGT_KMS_CONNECTOR_NAME_SIZE - Size used when a connector name is needed
+ */
+#define IGT_KMS_CONNECTOR_NAME_SIZE 50
 
 typedef bool (*igt_connector_attr_set)(int dir, const char *attr, const char *value);
 
@@ -8318,3 +8322,107 @@ int igt_get_connected_connectors(int drm_fd, uint32_t **connector_ids)
 
 	return connected_count;
 }
+
+
+/**
+ * igt_get_connector_from_name:
+ * @drm_fd: DRM file descriptor
+ * @port_name: Port name to search
+ *
+ * Returns: The connector if found, NULL otherwise. The pointer must
+ * be freed with drmModeFreeConnector()
+ */
+drmModeConnectorPtr igt_get_connector_from_name(int drm_fd, const char *port_name)
+{
+	drmModeResPtr res = drmModeGetResources(drm_fd);
+	double timeout = igt_default_display_detect_timeout();
+	int i, len;
+
+	if (!res)
+		return NULL;
+
+	for (i = 0; i < res->count_connectors; i++) {
+		char name[IGT_KMS_CONNECTOR_NAME_SIZE];
+
+		drmModeConnectorPtr connector = igt_wait_for_connector(drm_fd, res->connectors[i],timeout);
+
+		if (connector) {
+			/* We have to generate the connector name on our own */
+			len = snprintf(name, ARRAY_SIZE(name), "%s-%u",
+				 kmstest_connector_type_str(connector->connector_type),
+				 connector->connector_type_id);
+			name[len] = 0;
+
+
+			if (strcmp(port_name, name) == 0) {
+				drmModeFreeResources(res);
+
+				return connector;
+			}
+
+			drmModeFreeConnector(connector);
+		}
+	}
+
+	drmModeFreeResources(res);
+
+	return NULL;
+}
+
+/**
+ * igt_get_connector_id_from_name:
+ * @drm_fd: DRM file descriptor
+ * @port_name: Port name to find in the connector
+ *
+ * Returns: The connector id if the port is found, 0 if the port is not found
+ */
+uint32_t igt_get_connector_id_from_name(int drm_fd, const char *port_name)
+{
+	drmModeConnectorPtr connector = igt_get_connector_from_name(drm_fd, port_name);
+
+	if (connector) {
+		uint32_t connector_id = connector->connector_id;
+
+		drmModeFreeConnector(connector);
+
+		return connector_id;
+	}
+
+	return 0;
+}
+
+/**
+ * igt_get_connector_id_from_mst_path:
+ * @drm_fd: DRM file descriptor
+ * @mst_path: MST path to find in the connector
+ *
+ * Returns: 0 when no connector is found with the correct mst path
+ */
+uint32_t igt_get_connector_id_from_mst_path(int drm_fd, const void *mst_path)
+{
+	drmModeResPtr res = drmModeGetResources(drm_fd);
+	int i;
+
+	if (!res)
+		return 0;
+
+	for (i = 0; i < res->count_connectors; i++) {
+		uint32_t connector_id = res->connectors[i];
+
+		drmModePropertyBlobPtr path_blob = kmstest_get_path_blob(drm_fd, connector_id);
+
+		if (path_blob) {
+			if (memcmp(path_blob->data, mst_path, path_blob->length) == 0) {
+				drmModeFreePropertyBlob(path_blob);
+				drmModeFreeResources(res);
+				return connector_id;
+			}
+
+			drmModeFreePropertyBlob(path_blob);
+		}
+	}
+
+	drmModeFreeResources(res);
+
+	return 0;
+}
diff --git a/lib/igt_kms.h b/lib/igt_kms.h
index 71d77a3c2..bc921f4ff 100644
--- a/lib/igt_kms.h
+++ b/lib/igt_kms.h
@@ -1302,5 +1302,8 @@ double igt_default_display_detect_timeout(void);
 bool igt_wait_for_connector_status(int drm_fd, unsigned int connector_id, double timeout,
 				   int drm_mode);
 int igt_get_connected_connectors(int drm_fd, uint32_t **connector_ids);
+drmModeConnectorPtr igt_get_connector_from_name(int drm_fd, const char *port_name);
+uint32_t igt_get_connector_id_from_name(int drm_fd, const char *port_name);
+uint32_t igt_get_connector_id_from_mst_path(int drm_fd, const void *mst_path);
 
 #endif /* __IGT_KMS_H__ */
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH i-g-t v1 05/25] lib/igt_kms: Add function to get valid pipe for specific output
  2026-04-28  4:46 [PATCH i-g-t v1 00/25] Chamelium v3 Integration and Test Execution Mohammed Bilal
                   ` (3 preceding siblings ...)
  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 ` 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
                   ` (23 subsequent siblings)
  28 siblings, 1 reply; 37+ messages in thread
From: Mohammed Bilal @ 2026-04-28  4:46 UTC (permalink / raw)
  To: igt-dev; +Cc: kunal1.joshi, Louis Chauvet

From: Louis Chauvet <louis.chauvet@bootlin.com>

Introduces a new function igt_get_pipe_for_output in igt_kms. The function
is designed to retrieve a valid pipe for a specific output in a display.

Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
 lib/igt_kms.c | 22 ++++++++++++++++++++++
 lib/igt_kms.h |  1 +
 2 files changed, 23 insertions(+)

diff --git a/lib/igt_kms.c b/lib/igt_kms.c
index aa086e96c..73d65f70a 100644
--- a/lib/igt_kms.c
+++ b/lib/igt_kms.c
@@ -8426,3 +8426,25 @@ uint32_t igt_get_connector_id_from_mst_path(int drm_fd, const void *mst_path)
 
 	return 0;
 }
+
+/**
+ * igt_get_pipe_for_output:
+ * @display: display to fetch the pipes
+ * @output: output to use
+ *
+ * Get a valid pipe for a specific output. The return value is the pipe first valid pipe for a
+ * specific output.
+ */
+enum pipe igt_get_pipe_for_output(igt_display_t *display,
+				  igt_output_t *output)
+{
+        igt_crtc_t *crtc;
+
+        for_each_crtc(display, crtc) {
+                if (igt_output_is_connected(output) &&
+                     (output->config.valid_crtc_index_mask & (1 << crtc->crtc_index)))
+                        return crtc->pipe;
+        }
+
+        return PIPE_NONE;
+}
diff --git a/lib/igt_kms.h b/lib/igt_kms.h
index bc921f4ff..70b7d1a58 100644
--- a/lib/igt_kms.h
+++ b/lib/igt_kms.h
@@ -1305,5 +1305,6 @@ int igt_get_connected_connectors(int drm_fd, uint32_t **connector_ids);
 drmModeConnectorPtr igt_get_connector_from_name(int drm_fd, const char *port_name);
 uint32_t igt_get_connector_id_from_name(int drm_fd, const char *port_name);
 uint32_t igt_get_connector_id_from_mst_path(int drm_fd, const void *mst_path);
+enum pipe igt_get_pipe_for_output(igt_display_t *display, igt_output_t *output);
 
 #endif /* __IGT_KMS_H__ */
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH i-g-t v1 06/25] lib/monitor_edids: Add helper functions for using monitor_edid objects
  2026-04-28  4:46 [PATCH i-g-t v1 00/25] Chamelium v3 Integration and Test Execution Mohammed Bilal
                   ` (4 preceding siblings ...)
  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  4:46 ` 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
                   ` (22 subsequent siblings)
  28 siblings, 0 replies; 37+ messages in thread
From: Mohammed Bilal @ 2026-04-28  4:46 UTC (permalink / raw)
  To: igt-dev; +Cc: kunal1.joshi, Louis Chauvet, Luca Ceresoli, Kory Maincent

From: Louis Chauvet <louis.chauvet@bootlin.com>

Introduce the functions edid_from_monitor_edid() and
get_edids_for_connector_type(). The former converts a monitor_edid object
to a struct edid, which can then be utilized by igt_kms helpers. The
latter returns a list of monitor_edid objects for a specific connector
with certain characteristics

Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
Reviewed-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
---
 lib/monitor_edids/dp_edids.h             |  3 +
 lib/monitor_edids/hdmi_edids.h           |  3 +
 lib/monitor_edids/monitor_edids_helper.c | 80 ++++++++++++++++++++++++
 lib/monitor_edids/monitor_edids_helper.h |  7 ++-
 4 files changed, 92 insertions(+), 1 deletion(-)

diff --git a/lib/monitor_edids/dp_edids.h b/lib/monitor_edids/dp_edids.h
index 144907558..077933812 100644
--- a/lib/monitor_edids/dp_edids.h
+++ b/lib/monitor_edids/dp_edids.h
@@ -194,4 +194,7 @@ monitor_edid DP_EDIDS_NON_4K[] = {
 
  };
 
+#define DP_EDIDS_4K_COUNT ARRAY_SIZE(DP_EDIDS_4K)
+#define DP_EDIDS_NON_4K_COUNT ARRAY_SIZE(DP_EDIDS_NON_4K)
+
 #endif /* TESTS_CHAMELIUM_MONITOR_EDIDS_DP_EDIDS_H_ */
diff --git a/lib/monitor_edids/hdmi_edids.h b/lib/monitor_edids/hdmi_edids.h
index f6cfe82ff..398424177 100644
--- a/lib/monitor_edids/hdmi_edids.h
+++ b/lib/monitor_edids/hdmi_edids.h
@@ -604,4 +604,7 @@ monitor_edid HDMI_EDIDS_NON_4K[] = {
 		  "1620582c2500baac4200009e0000006b" },
 };
 
+#define HDMI_EDIDS_4K_COUNT ARRAY_SIZE(HDMI_EDIDS_4K)
+#define HDMI_EDIDS_NON_4K_COUNT ARRAY_SIZE(HDMI_EDIDS_NON_4K)
+
 #endif /* TESTS_CHAMELIUM_MONITOR_EDIDS_HDMI_EDIDS_H_ */
diff --git a/lib/monitor_edids/monitor_edids_helper.c b/lib/monitor_edids/monitor_edids_helper.c
index 1cbf1c22f..6043e7a08 100644
--- a/lib/monitor_edids/monitor_edids_helper.c
+++ b/lib/monitor_edids/monitor_edids_helper.c
@@ -15,6 +15,10 @@
 #include <assert.h>
 
 #include "igt_core.h"
+#include "igt_edid.h"
+#include "dp_edids.h"
+#include "drmtest.h"
+#include "hdmi_edids.h"
 
 static uint8_t convert_hex_char_to_byte(char c)
 {
@@ -90,3 +94,79 @@ void free_chamelium_edid_from_monitor_edid(struct chamelium_edid *edid)
 	free(edid);
 	edid = NULL;
 }
+
+/**
+ * edid_from_monitor_edid:
+ * @mon_edid: Monitor EDID to convert
+ *
+ * Get a struct edid from a monitor_edid. This returns a pointer to a newly allocated struct edid.
+ * The caller is in charge to free this pointer when required.
+ */
+struct edid *edid_from_monitor_edid(const monitor_edid *mon_edid)
+{
+	uint8_t *raw_edid;
+	size_t edid_size;
+	int i;
+
+	edid_size = strlen(mon_edid->edid) / 2; /* each ascii is a nibble. */
+	raw_edid = malloc(edid_size);
+	igt_assert(raw_edid);
+
+	for (i = 0; i < edid_size; i++) {
+		raw_edid[i] = convert_hex_char_to_byte(mon_edid->edid[i * 2]) << 4 |
+			      convert_hex_char_to_byte(mon_edid->edid[i * 2 + 1]);
+	}
+
+	if (edid_get_size((struct edid *)raw_edid) > edid_size) {
+		uint8_t *new_edid;
+
+		igt_debug("The edid size stored in the raw edid is longer than the edid stored in the table.");
+		new_edid = realloc(raw_edid, edid_get_size((struct edid *)raw_edid));
+		igt_assert(new_edid);
+		raw_edid = new_edid;
+	}
+
+	return (struct edid *)raw_edid;
+}
+
+/**
+ * get_edids_for_connector_type:
+ * @type: The connector type to get the EDIDs from
+ * @count: Used to store the number of EDIDs in the returned list
+ * @four_k: Use true to fetch 4k EDIDs, false to fetch non-4k EDIDs
+ *
+ * Get the list of EDIDS for a specific connector type. This returns a pointer to a static list,
+ * so no need to free the pointer.
+ */
+struct monitor_edid *get_edids_for_connector_type(uint32_t type, size_t *count, bool four_k)
+{
+	if (four_k) {
+		switch (type) {
+		case DRM_MODE_CONNECTOR_DisplayPort:
+			*count = DP_EDIDS_4K_COUNT;
+			return DP_EDIDS_4K;
+		case DRM_MODE_CONNECTOR_HDMIA:
+			*count = HDMI_EDIDS_4K_COUNT;
+			return HDMI_EDIDS_4K;
+		default:
+			*count = 0;
+			igt_debug("No 4k EDID for the connector %s\n",
+				  kmstest_connector_type_str(type));
+			return NULL;
+		}
+	} else {
+		switch (type) {
+		case DRM_MODE_CONNECTOR_DisplayPort:
+			*count = DP_EDIDS_NON_4K_COUNT;
+			return DP_EDIDS_NON_4K;
+		case DRM_MODE_CONNECTOR_HDMIA:
+			*count = HDMI_EDIDS_NON_4K_COUNT;
+			return HDMI_EDIDS_NON_4K;
+		default:
+			*count = 0;
+			igt_debug("No EDID for the connector %s\n",
+				  kmstest_connector_type_str(type));
+			return NULL;
+		}
+	}
+}
diff --git a/lib/monitor_edids/monitor_edids_helper.h b/lib/monitor_edids/monitor_edids_helper.h
index 05679f089..e50698686 100644
--- a/lib/monitor_edids/monitor_edids_helper.h
+++ b/lib/monitor_edids/monitor_edids_helper.h
@@ -12,6 +12,8 @@
 #define TESTS_CHAMELIUM_MONITOR_EDIDS_MONITOR_EDIDS_HELPER_H_
 
 #include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
 
 #include "igt_chamelium.h"
 
@@ -30,4 +32,7 @@ get_chameleon_edid_from_monitor_edid(struct chamelium *chamelium,
 				     const monitor_edid *edid);
 void free_chamelium_edid_from_monitor_edid(struct chamelium_edid *edid);
 
-#endif /* TESTS_CHAMELIUM_MONITOR_EDIDS_MONITOR_EDIDS_HELPER_H_ */
\ No newline at end of file
+struct edid *edid_from_monitor_edid(const monitor_edid *monitor_edid);
+struct monitor_edid *get_edids_for_connector_type(uint32_t type, size_t *count, bool four_k);
+
+#endif /* TESTS_CHAMELIUM_MONITOR_EDIDS_MONITOR_EDIDS_HELPER_H_ */
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH i-g-t v1 07/25] lib/monitor_edids: Add helper to get an EDID by its name
  2026-04-28  4:46 [PATCH i-g-t v1 00/25] Chamelium v3 Integration and Test Execution Mohammed Bilal
                   ` (5 preceding siblings ...)
  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 ` 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
                   ` (21 subsequent siblings)
  28 siblings, 1 reply; 37+ messages in thread
From: Mohammed Bilal @ 2026-04-28  4:46 UTC (permalink / raw)
  To: igt-dev; +Cc: kunal1.joshi, Louis Chauvet, Kory Maincent, Luca Ceresoli

From: Louis Chauvet <louis.chauvet@bootlin.com>

For testing specific EDID, it is useful to be able to retrieve an EDID by
a verbose name.

Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
Reviewed-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
 lib/monitor_edids/monitor_edids_helper.c | 30 ++++++++++++++++++++++++
 lib/monitor_edids/monitor_edids_helper.h |  1 +
 2 files changed, 31 insertions(+)

diff --git a/lib/monitor_edids/monitor_edids_helper.c b/lib/monitor_edids/monitor_edids_helper.c
index 6043e7a08..01feb34a5 100644
--- a/lib/monitor_edids/monitor_edids_helper.c
+++ b/lib/monitor_edids/monitor_edids_helper.c
@@ -20,6 +20,16 @@
 #include "drmtest.h"
 #include "hdmi_edids.h"
 
+struct {
+	struct monitor_edid *edid_list;
+	int list_size;
+} ALL_EDIDS[] = {
+	{DP_EDIDS_NON_4K,	DP_EDIDS_NON_4K_COUNT},
+	{DP_EDIDS_4K,		DP_EDIDS_4K_COUNT},
+	{HDMI_EDIDS_NON_4K,	HDMI_EDIDS_NON_4K_COUNT},
+	{HDMI_EDIDS_4K,		HDMI_EDIDS_4K_COUNT},
+};
+
 static uint8_t convert_hex_char_to_byte(char c)
 {
 	if (c >= '0' && c <= '9')
@@ -170,3 +180,23 @@ struct monitor_edid *get_edids_for_connector_type(uint32_t type, size_t *count,
 		}
 	}
 }
+
+/**
+ * get_edid_by_name:
+ * @name: Name to search in available EDIDs
+ *
+ * Return the struct edid associated with a specific name. As with edid_from_monitor_edid, the
+ * caller must ensure to free the EDID after use. If no EDID with the exact name is found, returns
+ * NULL.
+ */
+struct edid *get_edid_by_name(const char *name)
+{
+	for (int i = 0; i < ARRAY_SIZE(ALL_EDIDS); i++) {
+		for (int j = 0; j < ALL_EDIDS[i].list_size; j++) {
+			if (strcmp(ALL_EDIDS[i].edid_list[j].name, name) == 0)
+				return edid_from_monitor_edid(&ALL_EDIDS[i].edid_list[j]);
+		}
+	}
+
+	return NULL;
+}
diff --git a/lib/monitor_edids/monitor_edids_helper.h b/lib/monitor_edids/monitor_edids_helper.h
index e50698686..6d9e3fc89 100644
--- a/lib/monitor_edids/monitor_edids_helper.h
+++ b/lib/monitor_edids/monitor_edids_helper.h
@@ -34,5 +34,6 @@ void free_chamelium_edid_from_monitor_edid(struct chamelium_edid *edid);
 
 struct edid *edid_from_monitor_edid(const monitor_edid *monitor_edid);
 struct monitor_edid *get_edids_for_connector_type(uint32_t type, size_t *count, bool four_k);
+struct edid *get_edid_by_name(const char *name);
 
 #endif /* TESTS_CHAMELIUM_MONITOR_EDIDS_MONITOR_EDIDS_HELPER_H_ */
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH i-g-t v1 08/25] lib/monitor_edids: Add helper to print all available EDID names
  2026-04-28  4:46 [PATCH i-g-t v1 00/25] Chamelium v3 Integration and Test Execution Mohammed Bilal
                   ` (6 preceding siblings ...)
  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  4:46 ` 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
                   ` (20 subsequent siblings)
  28 siblings, 0 replies; 37+ messages in thread
From: Mohammed Bilal @ 2026-04-28  4:46 UTC (permalink / raw)
  To: igt-dev; +Cc: kunal1.joshi, Louis Chauvet, Luca Ceresoli, Kory Maincent

From: Louis Chauvet <louis.chauvet@bootlin.com>

During the unigraf configuration, it may be required to know the list
of supported EDID names, so add an helper to print them.

Reviewed-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
 lib/monitor_edids/monitor_edids_helper.c | 17 +++++++++++++++++
 lib/monitor_edids/monitor_edids_helper.h |  2 ++
 2 files changed, 19 insertions(+)

diff --git a/lib/monitor_edids/monitor_edids_helper.c b/lib/monitor_edids/monitor_edids_helper.c
index 01feb34a5..34ae6be9d 100644
--- a/lib/monitor_edids/monitor_edids_helper.c
+++ b/lib/monitor_edids/monitor_edids_helper.c
@@ -200,3 +200,20 @@ struct edid *get_edid_by_name(const char *name)
 
 	return NULL;
 }
+
+
+/*
+ * list_edid_names:
+ * @level: Log level to write the names on
+ *
+ * Print all the EDID available in igt.
+ */
+void list_edid_names(enum igt_log_level level)
+{
+	for (int i = 0; i < ARRAY_SIZE(ALL_EDIDS); i++) {
+		for (int j = 0; j < ALL_EDIDS[i].list_size; j++) {
+			igt_log(IGT_LOG_DOMAIN, level, " - \"%s\"\n",
+				ALL_EDIDS[i].edid_list[j].name);
+		}
+	}
+}
diff --git a/lib/monitor_edids/monitor_edids_helper.h b/lib/monitor_edids/monitor_edids_helper.h
index 6d9e3fc89..7ae0fd40c 100644
--- a/lib/monitor_edids/monitor_edids_helper.h
+++ b/lib/monitor_edids/monitor_edids_helper.h
@@ -15,6 +15,7 @@
 #include <stddef.h>
 #include <stdbool.h>
 
+#include "igt_core.h"
 #include "igt_chamelium.h"
 
 /* Max Length can be increased as needed, when new EDIDs are added. */
@@ -35,5 +36,6 @@ void free_chamelium_edid_from_monitor_edid(struct chamelium_edid *edid);
 struct edid *edid_from_monitor_edid(const monitor_edid *monitor_edid);
 struct monitor_edid *get_edids_for_connector_type(uint32_t type, size_t *count, bool four_k);
 struct edid *get_edid_by_name(const char *name);
+void list_edid_names(enum igt_log_level level);
 
 #endif /* TESTS_CHAMELIUM_MONITOR_EDIDS_MONITOR_EDIDS_HELPER_H_ */
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH i-g-t v1 09/25] lib/monitor_edids: Fix missing names in some monitor EDID
  2026-04-28  4:46 [PATCH i-g-t v1 00/25] Chamelium v3 Integration and Test Execution Mohammed Bilal
                   ` (7 preceding siblings ...)
  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 ` 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
                   ` (19 subsequent siblings)
  28 siblings, 0 replies; 37+ messages in thread
From: Mohammed Bilal @ 2026-04-28  4:46 UTC (permalink / raw)
  To: igt-dev; +Cc: kunal1.joshi, Louis Chauvet, Kamil Konieczny

From: Louis Chauvet <louis.chauvet@bootlin.com>

Some HDMI EDID did not have a good name identifier. Add one to avoid
confusion.

Acked-by: Kamil Konieczny <kamil.konieczny@linux.intel.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
 lib/monitor_edids/hdmi_edids.h | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/lib/monitor_edids/hdmi_edids.h b/lib/monitor_edids/hdmi_edids.h
index 398424177..26dffb7bf 100644
--- a/lib/monitor_edids/hdmi_edids.h
+++ b/lib/monitor_edids/hdmi_edids.h
@@ -241,7 +241,7 @@ monitor_edid HDMI_EDIDS_NON_4K[] = {
 		  "33333633493056465320000000fc0044"
 		  "454c4c20323430354650570a000000fd"
 		  "00384c1e5111000a20202020202000e9" },
-	{ .name = "`",
+	{ .name = "DEL_DELL_2407WFP_HDMI",
 	  .edid = "00ffffffffffff0010ac17a053575930"
 		  "0511010380342178eeee91a3544c9926"
 		  "0f5054a54b008180a940714fb3000101"
@@ -430,7 +430,7 @@ monitor_edid HDMI_EDIDS_NON_4K[] = {
 		  "60350000003200001c011d8018711c16"
 		  "20582c2500c48e2100009e0000000000"
 		  "0000000000000000000000000000000a" },
-	{ .name = "",
+	{ .name = "GSM_50294_M3704C_HDMI",
 	  .edid = "00ffffffffffff001e6d76c401010101"
 		  "2514010380522e78eaac27a355499b25"
 		  "10474aa1080081807140614045403140"
@@ -482,7 +482,7 @@ monitor_edid HDMI_EDIDS_NON_4K[] = {
 		  "5011000a202020202020000000fc0048"
 		  "50205a5232343430770a2020000000ff"
 		  "00434e34333132303836580a2020007f" },
-	{ .name = "",
+	{ .name = "HWP_10582_HP_ZR2440w_HDMI",
 	  .edid = "00ffffffffffff0022f0562901010101"
 		  "0b170103803420782afc81a4554d9d25"
 		  "125054210800d1c081c0814081809500"
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH i-g-t v1 10/25] lib/monitor_edids: Add new EDID for HDMI 4k
  2026-04-28  4:46 [PATCH i-g-t v1 00/25] Chamelium v3 Integration and Test Execution Mohammed Bilal
                   ` (8 preceding siblings ...)
  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 ` 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
                   ` (18 subsequent siblings)
  28 siblings, 0 replies; 37+ messages in thread
From: Mohammed Bilal @ 2026-04-28  4:46 UTC (permalink / raw)
  To: igt-dev; +Cc: kunal1.joshi, Louis Chauvet, Kamil Konieczny

From: Louis Chauvet <louis.chauvet@bootlin.com>

Add EDID for the HDMI screen LG HDR 4K.

Acked-by: Kamil Konieczny <kamil.konieczny@linux.intel.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
 lib/monitor_edids/hdmi_edids.h | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/lib/monitor_edids/hdmi_edids.h b/lib/monitor_edids/hdmi_edids.h
index 26dffb7bf..589a76a8a 100644
--- a/lib/monitor_edids/hdmi_edids.h
+++ b/lib/monitor_edids/hdmi_edids.h
@@ -49,7 +49,23 @@ monitor_edid HDMI_EDIDS_4K[] = {
 		  "5E00A0A0A0295030203500C48F210000"
 		  "1EEF5100A0F070198030203500C48F21"
 		  "00001E000000000000000000000000A8" },
-
+	{ .name = "4K_GSM_LG_HDR_4K_HDMI",
+	  .edid = "00ffffffffffff001e6d4f77c75f1500"
+		  "0521010380462878ea7ba1ae4f44a926"
+		  "0c5054210800d1c06140454001010101"
+		  "010101010101a0cb0046f0703e802010"
+		  "3500b9882100001a000000fd00283c1e"
+		  "873c000a202020202020000000fc004c"
+		  "472048445220344b0a202020000000ff"
+		  "003330354d414e4a43335a37350a01e4"
+		  "0203427223090707830100004d010304"
+		  "10121f202261605f5e5d6d030c001000"
+		  "b83c20006001020367d85dc401788003"
+		  "e30f0003e2006ae305c000e606050159"
+		  "5952a36600a0f0701f8030203500b988"
+		  "2100001a565e00a0a0a0295030203500"
+		  "b9882100001a023a801871382d40582c"
+		  "4500b9882100001a00000000000000af" },
 };
 
 monitor_edid HDMI_EDIDS_NON_4K[] = {
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH i-g-t v1 11/25] tests/chamelium: Extract Chamelium v2 tests into a separate directory
  2026-04-28  4:46 [PATCH i-g-t v1 00/25] Chamelium v3 Integration and Test Execution Mohammed Bilal
                   ` (9 preceding siblings ...)
  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 ` 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
                   ` (17 subsequent siblings)
  28 siblings, 0 replies; 37+ messages in thread
From: Mohammed Bilal @ 2026-04-28  4:46 UTC (permalink / raw)
  To: igt-dev; +Cc: kunal1.joshi, Louis Chauvet, Mohammed Bilal

From: Louis Chauvet <louis.chauvet@bootlin.com>

This commit moves the existing Chamelium tests to the v2/ subdirectory
to prepare for the introduction of Chamelium v3 tests.

Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
Signed-off-by: Mohammed Bilal <mohammed.bilal@intel.com>
---
 tests/chamelium/{ => v2}/kms_chamelium_audio.c     |  0
 tests/chamelium/{ => v2}/kms_chamelium_color.c     |  0
 tests/chamelium/{ => v2}/kms_chamelium_edid.c      |  0
 tests/chamelium/{ => v2}/kms_chamelium_frames.c    |  0
 tests/chamelium/{ => v2}/kms_chamelium_helper.c    |  0
 tests/chamelium/{ => v2}/kms_chamelium_helper.h    |  0
 tests/chamelium/{ => v2}/kms_chamelium_hpd.c       |  0
 .../{ => v2}/kms_chamelium_sharpness_filter.c      |  0
 tests/meson.build                                  | 14 +++++++-------
 9 files changed, 7 insertions(+), 7 deletions(-)
 rename tests/chamelium/{ => v2}/kms_chamelium_audio.c (100%)
 rename tests/chamelium/{ => v2}/kms_chamelium_color.c (100%)
 rename tests/chamelium/{ => v2}/kms_chamelium_edid.c (100%)
 rename tests/chamelium/{ => v2}/kms_chamelium_frames.c (100%)
 rename tests/chamelium/{ => v2}/kms_chamelium_helper.c (100%)
 rename tests/chamelium/{ => v2}/kms_chamelium_helper.h (100%)
 rename tests/chamelium/{ => v2}/kms_chamelium_hpd.c (100%)
 rename tests/chamelium/{ => v2}/kms_chamelium_sharpness_filter.c (100%)

diff --git a/tests/chamelium/kms_chamelium_audio.c b/tests/chamelium/v2/kms_chamelium_audio.c
similarity index 100%
rename from tests/chamelium/kms_chamelium_audio.c
rename to tests/chamelium/v2/kms_chamelium_audio.c
diff --git a/tests/chamelium/kms_chamelium_color.c b/tests/chamelium/v2/kms_chamelium_color.c
similarity index 100%
rename from tests/chamelium/kms_chamelium_color.c
rename to tests/chamelium/v2/kms_chamelium_color.c
diff --git a/tests/chamelium/kms_chamelium_edid.c b/tests/chamelium/v2/kms_chamelium_edid.c
similarity index 100%
rename from tests/chamelium/kms_chamelium_edid.c
rename to tests/chamelium/v2/kms_chamelium_edid.c
diff --git a/tests/chamelium/kms_chamelium_frames.c b/tests/chamelium/v2/kms_chamelium_frames.c
similarity index 100%
rename from tests/chamelium/kms_chamelium_frames.c
rename to tests/chamelium/v2/kms_chamelium_frames.c
diff --git a/tests/chamelium/kms_chamelium_helper.c b/tests/chamelium/v2/kms_chamelium_helper.c
similarity index 100%
rename from tests/chamelium/kms_chamelium_helper.c
rename to tests/chamelium/v2/kms_chamelium_helper.c
diff --git a/tests/chamelium/kms_chamelium_helper.h b/tests/chamelium/v2/kms_chamelium_helper.h
similarity index 100%
rename from tests/chamelium/kms_chamelium_helper.h
rename to tests/chamelium/v2/kms_chamelium_helper.h
diff --git a/tests/chamelium/kms_chamelium_hpd.c b/tests/chamelium/v2/kms_chamelium_hpd.c
similarity index 100%
rename from tests/chamelium/kms_chamelium_hpd.c
rename to tests/chamelium/v2/kms_chamelium_hpd.c
diff --git a/tests/chamelium/kms_chamelium_sharpness_filter.c b/tests/chamelium/v2/kms_chamelium_sharpness_filter.c
similarity index 100%
rename from tests/chamelium/kms_chamelium_sharpness_filter.c
rename to tests/chamelium/v2/kms_chamelium_sharpness_filter.c
diff --git a/tests/meson.build b/tests/meson.build
index 60cea3aa8..4455b55c0 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -392,12 +392,12 @@ extra_sources = {
 	'kms_color':  [ 'kms_color_helper.c' ],
 	'kms_color_pipeline':  [ 'kms_color_helper.c', 'kms_colorop_helper.c' ],
 	'kms_colorop':  [ 'kms_colorop_helper.c' ],
-	'kms_chamelium_audio': [ join_paths ('chamelium', 'kms_chamelium_helper.c') ],
-	'kms_chamelium_color':  [ 'kms_color_helper.c', join_paths ('chamelium', 'kms_chamelium_helper.c') ],
-	'kms_chamelium_edid': [ join_paths ('chamelium', 'kms_chamelium_helper.c') ],
-	'kms_chamelium_frames': [ join_paths ('chamelium', 'kms_chamelium_helper.c') ],
-	'kms_chamelium_hpd': [ join_paths ('chamelium', 'kms_chamelium_helper.c') ],
-	'kms_chamelium_sharpness_filter': [ join_paths ('chamelium', 'kms_chamelium_helper.c') ],
+	'kms_chamelium_audio': [ join_paths ('chamelium', 'v2', 'kms_chamelium_helper.c') ],
+	'kms_chamelium_color':  [ 'kms_color_helper.c', join_paths ('chamelium', 'v2', 'kms_chamelium_helper.c') ],
+	'kms_chamelium_edid': [ join_paths ('chamelium', 'v2', 'kms_chamelium_helper.c') ],
+	'kms_chamelium_frames': [ join_paths ('chamelium', 'v2', 'kms_chamelium_helper.c') ],
+	'kms_chamelium_hpd': [ join_paths ('chamelium', 'v2', 'kms_chamelium_helper.c') ],
+	'kms_chamelium_sharpness_filter': [ join_paths ('chamelium', 'v2', 'kms_chamelium_helper.c') ],
 	'kms_dp_linktrain_fallback': [
            join_paths ('intel', 'kms_mst_helper.c'),
            join_paths ('intel', 'kms_dsc_helper.c') ],
@@ -481,7 +481,7 @@ endforeach
 if chamelium.found()
 	foreach prog : chamelium_progs
 		testexe = executable(prog,
-				 [join_paths('chamelium', prog + '.c')] + extra_sources.get(prog, []),
+				 [join_paths('chamelium', 'v2', prog + '.c')] + extra_sources.get(prog, []),
 				 dependencies : test_deps + extra_dependencies.get(prog, []),
 				 install_dir : libexecdir,
 				 install_rpath : libexecdir_rpathdir,
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH i-g-t v1 12/25] lib/chamelium/v2: Extract chamelium v2 wrapper into its own directory
  2026-04-28  4:46 [PATCH i-g-t v1 00/25] Chamelium v3 Integration and Test Execution Mohammed Bilal
                   ` (10 preceding siblings ...)
  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 ` Mohammed Bilal
  2026-04-28  4:46 ` [PATCH i-g-t v1 13/25] lib/chamelium/v2: Rename chamelium to chamelium_v2 Mohammed Bilal
                   ` (16 subsequent siblings)
  28 siblings, 0 replies; 37+ messages in thread
From: Mohammed Bilal @ 2026-04-28  4:46 UTC (permalink / raw)
  To: igt-dev; +Cc: kunal1.joshi, Louis Chauvet

From: Louis Chauvet <louis.chauvet@bootlin.com>

In preparation for the Chamelium v3, the support for Chamelium v2 is being
extracted into its own directory.

The two Chamelium versions have different sets of functionality (audio,
MST, CRC computation, etc.). As the current wrapper is already complex,
extracting the v2 part into its own directory allows for the v3 wrapper to
be written without breaking any existing tests or complicating the
existing code.

The current identified complexities that necessitate a thorough rework of
the existing v2 wrapper are:

 - Not all ports are discoverable in v3 (MST ports are not physical
   ports, but they can have EDID, be plugged, etc.), and they can change
   over time.
 - The v3 is unable to perform CRC computation, so many tests must be
   rewritten or adapted to work.
 - Not all ports can be plugged at once, which is a feature used by the
   existing v2 wrapper to set up the Chamelium.

Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
 lib/{ => chamelium/v2}/igt_chamelium.c        | 2 +-
 lib/{ => chamelium/v2}/igt_chamelium.h        | 0
 lib/{ => chamelium/v2}/igt_chamelium_stream.c | 2 +-
 lib/{ => chamelium/v2}/igt_chamelium_stream.h | 0
 lib/igt.h                                     | 4 ++--
 lib/igt_kms.c                                 | 2 +-
 lib/meson.build                               | 5 ++++-
 lib/monitor_edids/monitor_edids_helper.h      | 2 +-
 tests/chamelium/v2/kms_chamelium_edid.c       | 2 +-
 tests/kms_feature_discovery.c                 | 2 +-
 10 files changed, 12 insertions(+), 9 deletions(-)
 rename lib/{ => chamelium/v2}/igt_chamelium.c (99%)
 rename lib/{ => chamelium/v2}/igt_chamelium.h (100%)
 rename lib/{ => chamelium/v2}/igt_chamelium_stream.c (99%)
 rename lib/{ => chamelium/v2}/igt_chamelium_stream.h (100%)

diff --git a/lib/igt_chamelium.c b/lib/chamelium/v2/igt_chamelium.c
similarity index 99%
rename from lib/igt_chamelium.c
rename to lib/chamelium/v2/igt_chamelium.c
index cb240a7df..3a0d60fc4 100644
--- a/lib/igt_chamelium.c
+++ b/lib/chamelium/v2/igt_chamelium.c
@@ -41,7 +41,7 @@
 #include <pixman.h>
 #include <cairo.h>
 
-#include "igt_chamelium.h"
+#include "chamelium/v2/igt_chamelium.h"
 #include "igt_core.h"
 #include "igt_aux.h"
 #include "igt_edid.h"
diff --git a/lib/igt_chamelium.h b/lib/chamelium/v2/igt_chamelium.h
similarity index 100%
rename from lib/igt_chamelium.h
rename to lib/chamelium/v2/igt_chamelium.h
diff --git a/lib/igt_chamelium_stream.c b/lib/chamelium/v2/igt_chamelium_stream.c
similarity index 99%
rename from lib/igt_chamelium_stream.c
rename to lib/chamelium/v2/igt_chamelium_stream.c
index a8cd19e51..8d96e6549 100644
--- a/lib/igt_chamelium_stream.c
+++ b/lib/chamelium/v2/igt_chamelium_stream.c
@@ -33,7 +33,7 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 
-#include "igt_chamelium_stream.h"
+#include "chamelium/v2/igt_chamelium_stream.h"
 #include "igt_core.h"
 #include "igt_rc.h"
 
diff --git a/lib/igt_chamelium_stream.h b/lib/chamelium/v2/igt_chamelium_stream.h
similarity index 100%
rename from lib/igt_chamelium_stream.h
rename to lib/chamelium/v2/igt_chamelium_stream.h
diff --git a/lib/igt.h b/lib/igt.h
index 173ca70bf..9802f44cb 100644
--- a/lib/igt.h
+++ b/lib/igt.h
@@ -45,8 +45,8 @@
 #ifdef HAVE_CHAMELIUM
 #include "igt_alsa.h"
 #include "igt_audio.h"
-#include "igt_chamelium.h"
-#include "igt_chamelium_stream.h"
+#include "chamelium/v2/igt_chamelium.h"
+#include "chamelium/v2/igt_chamelium_stream.h"
 #endif
 #include "instdone.h"
 #include "intel_batchbuffer.h"
diff --git a/lib/igt_kms.c b/lib/igt_kms.c
index 73d65f70a..2d7c40622 100644
--- a/lib/igt_kms.c
+++ b/lib/igt_kms.c
@@ -64,7 +64,7 @@
 #include "igt_sysfs.h"
 #include "sw_sync.h"
 #ifdef HAVE_CHAMELIUM
-#include "igt_chamelium.h"
+#include "chamelium/v2/igt_chamelium.h"
 #endif
 
 /**
diff --git a/lib/meson.build b/lib/meson.build
index 0e7efadf3..0d94db9cd 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -235,7 +235,10 @@ endif
 
 if chamelium.found()
 	lib_deps += chamelium
-	lib_sources += [ 'igt_chamelium.c', 'igt_chamelium_stream.c' ]
+	lib_sources += [
+		'chamelium/v2/igt_chamelium.c',
+		'chamelium/v2/igt_chamelium_stream.c'
+	]
 	lib_sources += 'monitor_edids/monitor_edids_helper.c'
 endif
 
diff --git a/lib/monitor_edids/monitor_edids_helper.h b/lib/monitor_edids/monitor_edids_helper.h
index 7ae0fd40c..c32c0ec47 100644
--- a/lib/monitor_edids/monitor_edids_helper.h
+++ b/lib/monitor_edids/monitor_edids_helper.h
@@ -16,7 +16,7 @@
 #include <stdbool.h>
 
 #include "igt_core.h"
-#include "igt_chamelium.h"
+#include "chamelium/v2/igt_chamelium.h"
 
 /* Max Length can be increased as needed, when new EDIDs are added. */
 #define EDID_NAME_MAX_LEN 28
diff --git a/tests/chamelium/v2/kms_chamelium_edid.c b/tests/chamelium/v2/kms_chamelium_edid.c
index e014ba3aa..6bece0a26 100644
--- a/tests/chamelium/v2/kms_chamelium_edid.c
+++ b/tests/chamelium/v2/kms_chamelium_edid.c
@@ -41,7 +41,7 @@
 
 #include "config.h"
 #include "igt.h"
-#include "igt_chamelium.h"
+#include "chamelium/v2/igt_chamelium.h"
 #include "igt_edid.h"
 #include "igt_eld.h"
 #include "igt_vc4.h"
diff --git a/tests/kms_feature_discovery.c b/tests/kms_feature_discovery.c
index 28ec89c55..ba934abbf 100644
--- a/tests/kms_feature_discovery.c
+++ b/tests/kms_feature_discovery.c
@@ -34,7 +34,7 @@
 
 #include "igt.h"
 #ifdef HAVE_CHAMELIUM
-#include "igt_chamelium.h"
+#include "chamelium/v2/igt_chamelium.h"
 #endif
 #include "igt_kms.h"
 #include "igt_psr.h"
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH i-g-t v1 13/25] lib/chamelium/v2: Rename chamelium to chamelium_v2
  2026-04-28  4:46 [PATCH i-g-t v1 00/25] Chamelium v3 Integration and Test Execution Mohammed Bilal
                   ` (11 preceding siblings ...)
  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 ` 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
                   ` (15 subsequent siblings)
  28 siblings, 0 replies; 37+ messages in thread
From: Mohammed Bilal @ 2026-04-28  4:46 UTC (permalink / raw)
  To: igt-dev; +Cc: kunal1.joshi, Louis Chauvet

From: Louis Chauvet <louis.chauvet@bootlin.com>

To avoid confusion in meson build file, rename the variable chamlium to
chamlium_v2.

Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
 .gitlab-ci.yml            |  2 +-
 docs/testplan/meson.build |  4 ++--
 lib/meson.build           |  4 ++--
 lib/tests/meson.build     |  4 ++--
 meson.build               | 16 ++++++++--------
 meson_options.txt         |  4 ++--
 tests/meson.build         |  8 ++++----
 7 files changed, 21 insertions(+), 21 deletions(-)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 34daba4d5..25f4337d8 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -6,7 +6,7 @@ variables:
   MESON_OPTIONS: >
     -Dlibdrm_drivers=intel,nouveau,amdgpu
     -Doverlay=enabled
-    -Dchamelium=enabled
+    -Dchamelium_v2=enabled
     -Dvalgrind=enabled
     -Dman=enabled
     -Dtests=enabled
diff --git a/docs/testplan/meson.build b/docs/testplan/meson.build
index 5560347f1..f60c82ad1 100644
--- a/docs/testplan/meson.build
+++ b/docs/testplan/meson.build
@@ -21,8 +21,8 @@ if build_tests
 		build_info += 'Will Check if documentation is in sync with testlist'
 		check_testlist = [ '--check-testlist', '--igt-build-path', build_root ]
 
-		if not chamelium.found()
-			warning('WARNING: Will not check if documentation is in sync for KMS as chamelium is disabled')
+		if not chamelium_v2.found()
+			warning('WARNING: Will not check if documentation is in sync for KMS as chamelium v2 is disabled')
 		else
 			kms_check_testlist = check_testlist
 		endif
diff --git a/lib/meson.build b/lib/meson.build
index 0d94db9cd..460b596de 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -233,8 +233,8 @@ if alsa.found()
 	lib_sources += 'igt_alsa.c'
 endif
 
-if chamelium.found()
-	lib_deps += chamelium
+if chamelium_v2.found()
+	lib_deps += chamelium_v2
 	lib_sources += [
 		'chamelium/v2/igt_chamelium.c',
 		'chamelium/v2/igt_chamelium_stream.c'
diff --git a/lib/tests/meson.build b/lib/tests/meson.build
index 124a9ecae..483b7fa3f 100644
--- a/lib/tests/meson.build
+++ b/lib/tests/meson.build
@@ -38,8 +38,8 @@ lib_fail_tests = [
 
 lib_tests_deps = igt_deps
 
-if chamelium.found()
-	lib_deps += chamelium
+if chamelium_v2.found()
+	lib_deps += chamelium_v2
 	lib_tests += 'igt_audio'
 endif
 
diff --git a/meson.build b/meson.build
index 79051be21..6816c2ee1 100644
--- a/meson.build
+++ b/meson.build
@@ -86,7 +86,7 @@ foreach cc_arg : cc_args
   endif
 endforeach
 
-build_chamelium = get_option('chamelium')
+build_chamelium_v2 = get_option('chamelium_v2')
 build_docs = get_option('docs')
 build_tests = not get_option('tests').disabled()
 build_xe = not get_option('xe_driver').disabled()
@@ -183,17 +183,17 @@ if not xmlrpc.found() and xmlrpc_cmd.found()
 	endif
 endif
 
-if build_chamelium.enabled() and not (xmlrpc.found() and xmlrpc_util.found() and xmlrpc_client.found())
+if build_chamelium_v2.enabled() and not (xmlrpc.found() and xmlrpc_util.found() and xmlrpc_client.found())
 	error('Chamelium build forced and required dependency xmlrpc not found')
 endif
 
-gsl = dependency('gsl', required : build_chamelium)
-alsa = dependency('alsa', required : build_chamelium)
-libcurl = dependency('libcurl', required : build_chamelium)
+gsl = dependency('gsl', required : build_chamelium_v2)
+alsa = dependency('alsa', required : build_chamelium_v2)
+libcurl = dependency('libcurl', required : build_chamelium_v2)
 
 if xmlrpc.found() and xmlrpc_util.found() and xmlrpc_client.found() and gsl.found() and alsa.found() and libcurl.found()
 	config.set('HAVE_CHAMELIUM', 1)
-	chamelium = declare_dependency(dependencies : [
+	chamelium_v2 = declare_dependency(dependencies : [
 		xmlrpc,
 		xmlrpc_util,
 		xmlrpc_client,
@@ -201,10 +201,10 @@ if xmlrpc.found() and xmlrpc_util.found() and xmlrpc_client.found() and gsl.foun
 		alsa,
 	])
 else
-	chamelium = disabler()
+	chamelium_v2 = disabler()
 endif
 
-build_info += 'Build Chamelium test: @0@'.format(chamelium.found())
+build_info += 'Build Chamelium v2 test: @0@'.format(chamelium_v2.found())
 
 pthreads = dependency('threads')
 math = cc.find_library('m')
diff --git a/meson_options.txt b/meson_options.txt
index e23402b36..42a1b1294 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -8,9 +8,9 @@ option('overlay_backends',
        choices : [ 'auto', 'x', 'xv' ],
        description : 'Overlay backends to enable')
 
-option('chamelium',
+option('chamelium_v2',
        type : 'feature',
-       description : 'Build Chamelium test')
+       description : 'Build Chamelium v2 test')
 
 option('valgrind',
        type : 'feature',
diff --git a/tests/meson.build b/tests/meson.build
index 4455b55c0..7f7224b38 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -363,7 +363,7 @@ if build_xe_eudebug
 	intel_xe_progs += intel_xe_eudebug_progs
 endif
 
-chamelium_progs = [
+chamelium_v2_progs = [
 	'kms_chamelium_audio',
 	'kms_chamelium_color',
 	'kms_chamelium_edid',
@@ -478,8 +478,8 @@ foreach prog : intel_progs
 	endif
 endforeach
 
-if chamelium.found()
-	foreach prog : chamelium_progs
+if chamelium_v2.found()
+	foreach prog : chamelium_v2_progs
 		testexe = executable(prog,
 				 [join_paths('chamelium', 'v2', prog + '.c')] + extra_sources.get(prog, []),
 				 dependencies : test_deps + extra_dependencies.get(prog, []),
@@ -497,7 +497,7 @@ if chamelium.found()
 				    output : name + '.testlist')
 		endif
 	endforeach
-	test_deps += chamelium
+	test_deps += chamelium_v2
 endif
 
 subdir('amdgpu')
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH i-g-t v1 14/25] lib/chamelium/v2: Rename HAVE_CHAMELIUM to HAVE_CHAMELIUM_V2
  2026-04-28  4:46 [PATCH i-g-t v1 00/25] Chamelium v3 Integration and Test Execution Mohammed Bilal
                   ` (12 preceding siblings ...)
  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 ` 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
                   ` (14 subsequent siblings)
  28 siblings, 0 replies; 37+ messages in thread
From: Mohammed Bilal @ 2026-04-28  4:46 UTC (permalink / raw)
  To: igt-dev; +Cc: kunal1.joshi, Louis Chauvet

From: Louis Chauvet <louis.chauvet@bootlin.com>

To avoid confusion in source file between the two chamelium version,
rename the HAVE_CHAMELIUM define to HAVE_CHAMELIUM_V2.

Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
 lib/igt.h                     | 2 +-
 lib/igt_kms.c                 | 4 ++--
 meson.build                   | 2 +-
 tests/kms_color_helper.h      | 2 +-
 tests/kms_feature_discovery.c | 4 ++--
 tests/kms_tiled_display.c     | 6 +++---
 6 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/lib/igt.h b/lib/igt.h
index 9802f44cb..b0758b610 100644
--- a/lib/igt.h
+++ b/lib/igt.h
@@ -42,7 +42,7 @@
 #include "igt_sizes.h"
 #include "igt_stats.h"
 #include "igt_dsc.h"
-#ifdef HAVE_CHAMELIUM
+#ifdef HAVE_CHAMELIUM_V2
 #include "igt_alsa.h"
 #include "igt_audio.h"
 #include "chamelium/v2/igt_chamelium.h"
diff --git a/lib/igt_kms.c b/lib/igt_kms.c
index 2d7c40622..928de2a10 100644
--- a/lib/igt_kms.c
+++ b/lib/igt_kms.c
@@ -63,7 +63,7 @@
 #include "igt_rc.h"
 #include "igt_sysfs.h"
 #include "sw_sync.h"
-#ifdef HAVE_CHAMELIUM
+#ifdef HAVE_CHAMELIUM_V2
 #include "chamelium/v2/igt_chamelium.h"
 #endif
 
@@ -3155,7 +3155,7 @@ void igt_display_require(igt_display_t *display, int drm_fd)
 	if (!resources)
 		goto out;
 
-#ifdef HAVE_CHAMELIUM
+#ifdef HAVE_CHAMELIUM_V2
 	{
 		struct chamelium *chamelium;
 
diff --git a/meson.build b/meson.build
index 6816c2ee1..1bb5595f8 100644
--- a/meson.build
+++ b/meson.build
@@ -192,7 +192,7 @@ alsa = dependency('alsa', required : build_chamelium_v2)
 libcurl = dependency('libcurl', required : build_chamelium_v2)
 
 if xmlrpc.found() and xmlrpc_util.found() and xmlrpc_client.found() and gsl.found() and alsa.found() and libcurl.found()
-	config.set('HAVE_CHAMELIUM', 1)
+	config.set('HAVE_CHAMELIUM_V2', 1)
 	chamelium_v2 = declare_dependency(dependencies : [
 		xmlrpc,
 		xmlrpc_util,
diff --git a/tests/kms_color_helper.h b/tests/kms_color_helper.h
index e2b3da61e..ec2431ee9 100644
--- a/tests/kms_color_helper.h
+++ b/tests/kms_color_helper.h
@@ -56,7 +56,7 @@ typedef struct {
 	uint32_t color_depth;
 	uint64_t degamma_lut_size;
 	uint64_t gamma_lut_size;
-	#ifdef HAVE_CHAMELIUM
+	#ifdef HAVE_CHAMELIUM_V2
 	struct chamelium *chamelium;
 	struct chamelium_port **ports;
 	int port_count;
diff --git a/tests/kms_feature_discovery.c b/tests/kms_feature_discovery.c
index ba934abbf..0efa6c7b2d 100644
--- a/tests/kms_feature_discovery.c
+++ b/tests/kms_feature_discovery.c
@@ -33,7 +33,7 @@
  */
 
 #include "igt.h"
-#ifdef HAVE_CHAMELIUM
+#ifdef HAVE_CHAMELIUM_V2
 #include "chamelium/v2/igt_chamelium.h"
 #endif
 #include "igt_kms.h"
@@ -137,7 +137,7 @@ int igt_main() {
 			}
 		}
 
-#ifdef HAVE_CHAMELIUM
+#ifdef HAVE_CHAMELIUM_V2
 		igt_describe("Make sure that Chamelium is configured and reachable.");
 		igt_subtest("chamelium") {
 			struct chamelium *chamelium =
diff --git a/tests/kms_tiled_display.c b/tests/kms_tiled_display.c
index 7c1074709..1c7ae83d1 100644
--- a/tests/kms_tiled_display.c
+++ b/tests/kms_tiled_display.c
@@ -79,7 +79,7 @@ typedef struct {
 	struct timeval first_ts;
 	int linetime_us;
 
-#ifdef HAVE_CHAMELIUM
+#ifdef HAVE_CHAMELIUM_V2
 	struct chamelium *chamelium;
 	struct chamelium_port **ports;
 	int port_count;
@@ -390,7 +390,7 @@ static bool got_all_page_flips(data_t *data)
 	return true;
 }
 
-#ifdef HAVE_CHAMELIUM
+#ifdef HAVE_CHAMELIUM_V2
 static void test_with_chamelium(data_t *data)
 {
 	int i, count = 0;
@@ -603,7 +603,7 @@ int igt_main()
 		test_cleanup(&data);
 	}
 
-#ifdef HAVE_CHAMELIUM
+#ifdef HAVE_CHAMELIUM_V2
 	igt_describe("Make sure the Tiled CRTCs are synchronized and we get "
 		     "page flips for all tiled CRTCs in one vblank (executes on chamelium).");
 	igt_subtest_f("basic-test-pattern-with-chamelium") {
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH i-g-t v1 15/25] lib/chamelium/v3: Introduce the foundation for the Chamelium v3 wrapper
  2026-04-28  4:46 [PATCH i-g-t v1 00/25] Chamelium v3 Integration and Test Execution Mohammed Bilal
                   ` (13 preceding siblings ...)
  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 ` 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
                   ` (13 subsequent siblings)
  28 siblings, 0 replies; 37+ messages in thread
From: Mohammed Bilal @ 2026-04-28  4:46 UTC (permalink / raw)
  To: igt-dev; +Cc: kunal1.joshi, Louis Chauvet

From: Louis Chauvet <louis.chauvet@bootlin.com>

Adds a build option to enable building the Chamelium v3 wrapper.

Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
 lib/chamelium/v3/igt_chamelium.c |  9 +++++++++
 lib/chamelium/v3/igt_chamelium.h |  8 ++++++++
 lib/igt_kms.c                    |  8 ++++++++
 lib/meson.build                  | 12 +++++++++++-
 lib/tests/meson.build            |  4 ++++
 meson.build                      | 15 ++++++++++++++-
 meson_options.txt                |  4 ++++
 tests/meson.build                | 25 +++++++++++++++++++++++++
 8 files changed, 83 insertions(+), 2 deletions(-)
 create mode 100644 lib/chamelium/v3/igt_chamelium.c
 create mode 100644 lib/chamelium/v3/igt_chamelium.h

diff --git a/lib/chamelium/v3/igt_chamelium.c b/lib/chamelium/v3/igt_chamelium.c
new file mode 100644
index 000000000..152242998
--- /dev/null
+++ b/lib/chamelium/v3/igt_chamelium.c
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: MIT
+
+#include <igt_core.h>
+#include "igt_chamelium.h"
+
+void chamelium_v3_init(void)
+{
+	igt_info("Using chamelium v3\n");
+}
diff --git a/lib/chamelium/v3/igt_chamelium.h b/lib/chamelium/v3/igt_chamelium.h
new file mode 100644
index 000000000..3195be2bc
--- /dev/null
+++ b/lib/chamelium/v3/igt_chamelium.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef V3_IGT_CHAMELIUM_H
+#define V3_IGT_CHAMELIUM_H
+
+void chamelium_v3_init(void);
+
+#endif //V3_IGT_CHAMELIUM_H
diff --git a/lib/igt_kms.c b/lib/igt_kms.c
index 928de2a10..267978f3d 100644
--- a/lib/igt_kms.c
+++ b/lib/igt_kms.c
@@ -3155,7 +3155,14 @@ void igt_display_require(igt_display_t *display, int drm_fd)
 	if (!resources)
 		goto out;
 
+/*
+ * FIXME: Dirty hack to avoid those lines when chamelium v3 is used
+ *
+ * Must be replaced with a configuration file information, so the end user can choose if the
+ * chamelium must be connected or not.
+ */
 #ifdef HAVE_CHAMELIUM_V2
+#ifndef HAVE_CHAMELIUM_V3
 	{
 		struct chamelium *chamelium;
 
@@ -3170,6 +3177,7 @@ void igt_display_require(igt_display_t *display, int drm_fd)
 			chamelium_deinit_rpc_only(chamelium);
 		}
 	}
+#endif
 #endif
 
 	igt_require_f(resources->count_crtcs <= IGT_MAX_PIPES,
diff --git a/lib/meson.build b/lib/meson.build
index 460b596de..589b2bfdd 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -239,7 +239,6 @@ if chamelium_v2.found()
 		'chamelium/v2/igt_chamelium.c',
 		'chamelium/v2/igt_chamelium_stream.c'
 	]
-	lib_sources += 'monitor_edids/monitor_edids_helper.c'
 endif
 
 if build_xe_eudebug
@@ -247,6 +246,17 @@ if build_xe_eudebug
 	lib_sources += 'xe/xe_eudebug.c'
 endif
 
+if chamelium_v3.found()
+	lib_deps += chamelium_v3
+	lib_sources += [
+		'chamelium/v3/igt_chamelium.c',
+	]
+endif
+
+if chamelium_v2.found() or chamelium_v3.found()
+	lib_sources += 'monitor_edids/monitor_edids_helper.c'
+endif
+
 if libprocps.found()
 	lib_deps += libprocps
 else
diff --git a/lib/tests/meson.build b/lib/tests/meson.build
index 483b7fa3f..9b9f29003 100644
--- a/lib/tests/meson.build
+++ b/lib/tests/meson.build
@@ -43,6 +43,10 @@ if chamelium_v2.found()
 	lib_tests += 'igt_audio'
 endif
 
+if chamelium_v3.found()
+	lib_deps += chamelium_v3
+endif
+
 foreach lib_test : lib_tests
 	exec = executable(lib_test, lib_test + '.c', install : false,
 			dependencies : igt_deps)
diff --git a/meson.build b/meson.build
index 1bb5595f8..6e6a66b5e 100644
--- a/meson.build
+++ b/meson.build
@@ -87,6 +87,7 @@ foreach cc_arg : cc_args
 endforeach
 
 build_chamelium_v2 = get_option('chamelium_v2')
+build_chamelium_v3 = get_option('chamelium_v3')
 build_docs = get_option('docs')
 build_tests = not get_option('tests').disabled()
 build_xe = not get_option('xe_driver').disabled()
@@ -183,7 +184,7 @@ if not xmlrpc.found() and xmlrpc_cmd.found()
 	endif
 endif
 
-if build_chamelium_v2.enabled() and not (xmlrpc.found() and xmlrpc_util.found() and xmlrpc_client.found())
+if (build_chamelium_v2.enabled() or build_chamelium_v3.enabled()) and not (xmlrpc.found() and xmlrpc_util.found() and xmlrpc_client.found())
 	error('Chamelium build forced and required dependency xmlrpc not found')
 endif
 
@@ -206,6 +207,18 @@ endif
 
 build_info += 'Build Chamelium v2 test: @0@'.format(chamelium_v2.found())
 
+
+if xmlrpc.found() and xmlrpc_util.found() and xmlrpc_client.found() and libcurl.found()
+	config.set('HAVE_CHAMELIUM_V3', 1)
+	chamelium_v3 = declare_dependency(dependencies : [
+										  xmlrpc,
+										  xmlrpc_util,
+										  xmlrpc_client,
+									  ])
+else
+	chamelium_v3 = disabler()
+endif
+
 pthreads = dependency('threads')
 math = cc.find_library('m')
 realtime = cc.find_library('rt')
diff --git a/meson_options.txt b/meson_options.txt
index 42a1b1294..9107c49d2 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -12,6 +12,10 @@ option('chamelium_v2',
        type : 'feature',
        description : 'Build Chamelium v2 test')
 
+option('chamelium_v3',
+       type : 'feature',
+       description : 'Build Chamelium v3 tests')
+
 option('valgrind',
        type : 'feature',
        description : 'Build with support for valgrind annotations')
diff --git a/tests/meson.build b/tests/meson.build
index 7f7224b38..17677e4e4 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -372,6 +372,9 @@ chamelium_v2_progs = [
 	'kms_chamelium_sharpness_filter',
 ]
 
+chamelium_v3_progs = [
+]
+
 test_deps = [ igt_deps ]
 
 if libdrm_nouveau.found()
@@ -500,6 +503,28 @@ if chamelium_v2.found()
 	test_deps += chamelium_v2
 endif
 
+if chamelium_v3.found()
+	foreach prog : chamelium_v3_progs
+		testexe = executable(prog,
+				 [join_paths('chamelium', 'v3', prog + '.c')],
+				 dependencies : test_deps,
+				 install_dir : libexecdir,
+				 install_rpath : libexecdir_rpathdir,
+				 install : true)
+		test_list += prog
+		test_executables += testexe
+		name = prog.split('/').get(-1)
+		if not meson.is_cross_build()
+			testlist_files += custom_target(name + '.testlist',
+				    build_by_default : true,
+				    command : [testexe, '--show-testlist'],
+				    capture : true,
+				    output : name + '.testlist')
+		endif
+	endforeach
+	test_deps += chamelium_v3
+endif
+
 subdir('amdgpu')
 
 subdir('msm')
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH i-g-t v1 16/25] lib/chamelium/v3: Introduce initialization and cleanup of Chamelium-related structures
  2026-04-28  4:46 [PATCH i-g-t v1 00/25] Chamelium v3 Integration and Test Execution Mohammed Bilal
                   ` (14 preceding siblings ...)
  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 ` 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
                   ` (12 subsequent siblings)
  28 siblings, 0 replies; 37+ messages in thread
From: Mohammed Bilal @ 2026-04-28  4:46 UTC (permalink / raw)
  To: igt-dev; +Cc: kunal1.joshi, Louis Chauvet

From: Louis Chauvet <louis.chauvet@bootlin.com>

In preparation for utilizing the Chamelium in tests, this commit
introduces a few helper functions to read the Chamelium configuration from
a file and initialize RPC-related structures.

Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
 lib/chamelium/v3/igt_chamelium.c | 148 ++++++++++++++++++++++++++++++-
 lib/chamelium/v3/igt_chamelium.h |  39 +++++++-
 2 files changed, 183 insertions(+), 4 deletions(-)

diff --git a/lib/chamelium/v3/igt_chamelium.c b/lib/chamelium/v3/igt_chamelium.c
index 152242998..663f51aaa 100644
--- a/lib/chamelium/v3/igt_chamelium.c
+++ b/lib/chamelium/v3/igt_chamelium.c
@@ -1,9 +1,151 @@
 // SPDX-License-Identifier: MIT
 
-#include <igt_core.h>
+#include <xmlrpc-c/base.h>
+#include <xmlrpc-c/client.h>
+
 #include "igt_chamelium.h"
+#include "igt_core.h"
+#include "igt_rc.h"
+
+#define CHAMELIUM_CONFIG_SECTION "Chameliumv3"
+#define CHAMELIUM_CONFIG_URL "URL"
+
+struct igt_chamelium_v3 {
+	xmlrpc_env env;
+	xmlrpc_client *client;
+	char *url;
+
+	struct igt_list_head port_mapping;
+};
+
+/**
+ * chamelium_v3_port_mapping_alloc - Allocate a port mapping with some default values
+ *
+ * @port_mapping: port mapping to free. The caller must remove the element from the list.
+ */
+static struct chamelium_v3_port_mapping *chamelium_v3_port_mapping_alloc(void)
+{
+	struct chamelium_v3_port_mapping *port_mapping = malloc(sizeof(*port_mapping));
+
+	igt_assert(port_mapping);
+	port_mapping->connector_name = NULL;
+	port_mapping->mst_path = NULL;
+	port_mapping->port_id = 0;
+	port_mapping->parent_id = 0;
+	port_mapping->is_children = false;
+	return port_mapping;
+}
 
-void chamelium_v3_init(void)
+/**
+ * chamelium_v3_port_mapping_free - Free memory assiciated with a port mapping
+ *
+ * @port_mapping: port mapping to free. The caller must remove the element from the list.
+ */
+static void chamelium_v3_port_mapping_free(struct chamelium_v3_port_mapping *port_mapping)
 {
-	igt_info("Using chamelium v3\n");
+	if (port_mapping->connector_name)
+		free(port_mapping->connector_name);
+	if (port_mapping->mst_path)
+		free(port_mapping->mst_path);
+	free(port_mapping);
 }
+
+/**
+ * chamelium_v3_init() - Initialize the RPC connexion with a chamelium
+ *
+ * @url: URL to connect to the chamelium
+ *
+ * Returns a igt_chamelium_v3 pointer, which must be freed by chamelium_v3_uninit.
+ */
+struct igt_chamelium_v3 *chamelium_v3_init(char *url)
+{
+	struct igt_chamelium_v3 *chamelium = malloc(sizeof(struct igt_chamelium_v3));
+	struct xmlrpc_clientparms clientparms;
+	struct xmlrpc_curl_xportparms curlparms;
+
+	if (!chamelium)
+		return NULL;
+
+	memset(chamelium, 0, sizeof(*chamelium));
+	memset(&clientparms, 0, sizeof(clientparms));
+	memset(&curlparms, 0, sizeof(curlparms));
+
+	/* curl's timeout is in milliseconds */
+	curlparms.timeout = 10 * 1000;
+
+	clientparms.transport = "curl";
+	clientparms.transportparmsP = &curlparms;
+	clientparms.transportparm_size = XMLRPC_CXPSIZE(timeout);
+
+	/* Setup the libxmlrpc context */
+	xmlrpc_env_init(&chamelium->env);
+	xmlrpc_client_setup_global_const(&chamelium->env);
+	xmlrpc_client_create(&chamelium->env, XMLRPC_CLIENT_NO_FLAGS, PACKAGE,
+			     PACKAGE_VERSION, &clientparms, 0, &chamelium->client);
+	if (chamelium->env.fault_occurred) {
+		igt_debug("Failed to init xmlrpc: %s\n",
+			  chamelium->env.fault_string);
+		goto error;
+	}
+
+	chamelium->url = strdup(url);
+	IGT_INIT_LIST_HEAD(&chamelium->port_mapping);
+
+	return chamelium;
+error:
+	chamelium_v3_uninit(chamelium);
+	return NULL;
+}
+
+/**
+ * chamelium_v3_init_from_config() - Initialize the RPC connexion with a chamelium from the config
+ * file
+ *
+ * Returns a igt_chamelium_v3 pointer, which must be freed by chamelium_v3_uninit.
+ */
+struct igt_chamelium_v3 *chamelium_v3_init_from_config(void)
+{
+	struct igt_chamelium_v3 *chamelium;
+	GError *error = NULL;
+	char *url;
+
+	if (!igt_key_file) {
+		igt_debug("No configuration file available for chamelium\n");
+		return NULL;
+	}
+
+	url = g_key_file_get_string(igt_key_file, CHAMELIUM_CONFIG_SECTION, CHAMELIUM_CONFIG_URL,
+				    &error);
+	if (!url) {
+		igt_debug("Couldn't read chamelium URL from config file: %s\n", error->message);
+		return false;
+	}
+
+	chamelium = chamelium_v3_init(url);
+	free(url);
+	return chamelium;
+}
+
+/**
+ * chamelium_v3_uninit() - Free the resources used by a chamelium
+ *
+ * @chamelium: The Chamelium instance to free
+ *
+ * Frees the resources used by a connection to the chamelium that was set up
+ * with chamelium_rpc_init().
+ */
+void chamelium_v3_uninit(struct igt_chamelium_v3 *chamelium)
+{
+	struct chamelium_v3_port_mapping *port_mapping, *tmp_port_mapping;
+
+	/* Destroy any mapping we created to make sure we don't leak them */
+	igt_list_for_each_entry_safe(port_mapping, tmp_port_mapping, &chamelium->port_mapping,
+				     link) {
+		chamelium_v3_port_mapping_free(port_mapping);
+	}
+	xmlrpc_client_destroy(chamelium->client);
+	xmlrpc_env_clean(&chamelium->env);
+	free(chamelium->url);
+	free(chamelium);
+}
+
diff --git a/lib/chamelium/v3/igt_chamelium.h b/lib/chamelium/v3/igt_chamelium.h
index 3195be2bc..929d7d4b0 100644
--- a/lib/chamelium/v3/igt_chamelium.h
+++ b/lib/chamelium/v3/igt_chamelium.h
@@ -3,6 +3,43 @@
 #ifndef V3_IGT_CHAMELIUM_H
 #define V3_IGT_CHAMELIUM_H
 
-void chamelium_v3_init(void);
+#include <stdint.h>
+
+#include "igt_list.h"
+
+struct igt_chamelium_v3;
+typedef uint32_t chamelium_v3_port_id;
+
+/** struct chamelium_v3_port_mapping - Represent a mapping between a DRM connector and a port on
+ * the chamelium
+ *
+ * @port_id: Chamelium port ID.
+ * @connector_name: DRM connector name, used to identify the connector in DRM, mutually exclusive
+ *		    with @mst_path
+ * @mst_path: MST path property, used to identify the connector in DRM, mutually exclusive with
+ *	      @mst_path
+ * @is_children: Used to indicate that this chamelium port is a children and can't be plugged alone
+ * @parent_id: Used when @is_children is true to point to the correct parent in chamelium
+ * @link: Linked list structure
+ */
+struct chamelium_v3_port_mapping {
+	chamelium_v3_port_id port_id;
+
+	/* For normal DRM connector */
+	char *connector_name;
+
+	/* For MST connector  */
+	char *mst_path;
+	bool is_children;
+	chamelium_v3_port_id parent_id;
+
+	/* Implementation details */
+	struct igt_list_head link;
+};
+
+struct igt_chamelium_v3 *chamelium_v3_init(char *url);
+struct igt_chamelium_v3 *chamelium_v3_init_from_config(void);
+
+void chamelium_v3_uninit(struct igt_chamelium_v3 *chamelium);
 
 #endif //V3_IGT_CHAMELIUM_H
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH i-g-t v1 17/25] lib/chamelium/v3: Add method to discover Chamelium ports
  2026-04-28  4:46 [PATCH i-g-t v1 00/25] Chamelium v3 Integration and Test Execution Mohammed Bilal
                   ` (15 preceding siblings ...)
  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 ` 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
                   ` (11 subsequent siblings)
  28 siblings, 0 replies; 37+ messages in thread
From: Mohammed Bilal @ 2026-04-28  4:46 UTC (permalink / raw)
  To: igt-dev; +Cc: kunal1.joshi, Louis Chauvet

From: Louis Chauvet <louis.chauvet@bootlin.com>

Since Chamelium v3 ports can change (recently added MST ports, for
example), they need to be discovered to prevent using an incorrect port.

This commit introduces the RPC calls GetSupportedPorts, IsMst, and
GetChildren to list all ports, check if a port is MST-capable, and fetch
the child ports of MST ports, respectively.

Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
 lib/chamelium/v3/igt_chamelium.c | 133 +++++++++++++++++++++++++++++++
 lib/chamelium/v3/igt_chamelium.h |   6 ++
 2 files changed, 139 insertions(+)

diff --git a/lib/chamelium/v3/igt_chamelium.c b/lib/chamelium/v3/igt_chamelium.c
index 663f51aaa..1122ce102 100644
--- a/lib/chamelium/v3/igt_chamelium.c
+++ b/lib/chamelium/v3/igt_chamelium.c
@@ -149,3 +149,136 @@ void chamelium_v3_uninit(struct igt_chamelium_v3 *chamelium)
 	free(chamelium);
 }
 
+/**
+ * __chamelium_rpc - Call a remote function on the chamelium
+ *
+ * @chamelium: Chamelium to call the function on
+ * @method_name: RPC endpoint name
+ * @format_str: RPC parameters description
+ * @...: RPC parameters
+ *
+ * Returns a xmlrpc_value that contains the call result.
+ */
+static xmlrpc_value *__chamelium_rpc(struct igt_chamelium_v3 *chamelium,
+				     const char *method_name,
+				     const char *format_str,
+				     ...)
+{
+	xmlrpc_value *res;
+	va_list va_args;
+
+	if (chamelium->env.fault_occurred) {
+		xmlrpc_env_clean(&chamelium->env);
+		xmlrpc_env_init(&chamelium->env);
+	}
+	va_start(va_args, format_str);
+	xmlrpc_client_call2f_va(&chamelium->env, chamelium->client,
+				chamelium->url, method_name, format_str, &res,
+				va_args);
+	va_end(va_args);
+	igt_assert_f(!chamelium->env.fault_occurred,
+		     "Chamelium RPC call[%s] failed: %s\n", method_name,
+		     chamelium->env.fault_string);
+	return res;
+}
+
+/*
+ * For the RPC calls, please refer to the python code [1] for documentation.
+ *
+ * [1]: https://chromium.googlesource.com/chromiumos/platform/chameleon/+/refs/heads/main/v3/chameleond/v3.py
+ */
+
+/**
+ * chamelium_v3_get_supported_ports - Get the list of ports on the chamelium
+ *
+ * @chamelium: Chamelium to get the ports from
+ * @port_ids: Out pointer for the list of port ids
+ *
+ * Returns the number of element stored in @port_ids. The caller must free this pointer when not
+ * used anymore.
+ */
+int chamelium_v3_get_supported_ports(struct igt_chamelium_v3 *chamelium,
+				     chamelium_v3_port_id **port_ids)
+{
+	xmlrpc_value *res, *res_port;
+	int port_count, i;
+
+	igt_assert(port_ids);
+
+	igt_debug("RPC GetSupportedPorts()\n");
+	res = __chamelium_rpc(chamelium, "GetSupportedPorts", "()");
+
+	port_count = xmlrpc_array_size(&chamelium->env, res);
+	*port_ids = calloc(port_count, sizeof(**port_ids));
+
+	for (i = 0; i < port_count; i++) {
+		xmlrpc_array_read_item(&chamelium->env, res, i, &res_port);
+		xmlrpc_read_int(&chamelium->env, res_port, (int *)&(*port_ids)[i]);
+		xmlrpc_DECREF(res_port);
+	}
+	xmlrpc_DECREF(res);
+
+	return port_count;
+}
+
+/**
+ * chamelium_v3_get_supported_ports - Get the list of children port for a specific port on the
+ *	chamelium
+ *
+ * @chamelium: Chamelium to get the ports from
+ * @port_id: Parent port id to get the children port from
+ * @port_ids: Out pointer for the list of port ids
+ *
+ * MST ports on the chamelium have children ports that can be plugged independently, but are always
+ * connected with a parent connector.
+ *
+ * Returns the number of element stored in @port_ids. The caller must free this pointer when not
+ * used anymore.
+ */
+int chamelium_v3_get_children(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_id port_id,
+			      chamelium_v3_port_id **port_ids)
+{
+	xmlrpc_value *res, *res_port;
+	int port_count, i;
+
+	igt_debug("RPC GetChildren(%d)\n", port_id);
+	res = __chamelium_rpc(chamelium, "GetChildren", "(i)", port_id);
+
+	port_count = xmlrpc_array_size(&chamelium->env, res);
+	*port_ids = calloc(port_count, sizeof(**port_ids));
+
+	for (i = 0; i < port_count; i++) {
+		xmlrpc_array_read_item(&chamelium->env, res, i, &res_port);
+		xmlrpc_read_int(&chamelium->env, res_port, (int *)&(*port_ids)[i]);
+		xmlrpc_DECREF(res_port);
+	}
+	xmlrpc_DECREF(res);
+
+	return port_count;
+}
+
+/**
+ * chamelium_v3_is_mst - Get the MST support for a specific port
+ *
+ * @chamelium: Chamelium to get the ports from
+ * @port_id: Port to get the support from
+ *
+ * Not all ports on the chamelium support MST. This functions allows checking if a specific port
+ * supports MST.
+ *
+ * Returns true if the @port_id supports MST.
+ */
+bool chamelium_v3_is_mst(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_id port_id)
+{
+	xmlrpc_value *res;
+	xmlrpc_bool is_mst;
+
+	igt_debug("RPC IsMst(%d)\n", port_id);
+	res = __chamelium_rpc(chamelium, "IsMst", "(i)", port_id);
+
+	xmlrpc_read_bool(&chamelium->env, res, &is_mst);
+
+	xmlrpc_DECREF(res);
+
+	return is_mst;
+}
diff --git a/lib/chamelium/v3/igt_chamelium.h b/lib/chamelium/v3/igt_chamelium.h
index 929d7d4b0..af5258068 100644
--- a/lib/chamelium/v3/igt_chamelium.h
+++ b/lib/chamelium/v3/igt_chamelium.h
@@ -42,4 +42,10 @@ struct igt_chamelium_v3 *chamelium_v3_init_from_config(void);
 
 void chamelium_v3_uninit(struct igt_chamelium_v3 *chamelium);
 
+int chamelium_v3_get_supported_ports(struct igt_chamelium_v3 *chamelium,
+				     chamelium_v3_port_id **port_ids);
+int chamelium_v3_get_children(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_id port_id,
+			      chamelium_v3_port_id **port_ids);
+bool chamelium_v3_is_mst(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_id port_id);
+
 #endif //V3_IGT_CHAMELIUM_H
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH i-g-t v1 18/25] lib/chamelium/v3: Implement method to retrieve Chamelium port names
  2026-04-28  4:46 [PATCH i-g-t v1 00/25] Chamelium v3 Integration and Test Execution Mohammed Bilal
                   ` (16 preceding siblings ...)
  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 ` 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
                   ` (10 subsequent siblings)
  28 siblings, 0 replies; 37+ messages in thread
From: Mohammed Bilal @ 2026-04-28  4:46 UTC (permalink / raw)
  To: igt-dev; +Cc: kunal1.joshi, Louis Chauvet

From: Louis Chauvet <louis.chauvet@bootlin.com>

Chamelium ports have descriptive names associated with them. This commit
introduces an RPC call to fetch these names, facilitating easier debugging
and better understanding of the port configuration.

Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
 lib/chamelium/v3/igt_chamelium.c | 25 +++++++++++++++++++++++++
 lib/chamelium/v3/igt_chamelium.h |  1 +
 2 files changed, 26 insertions(+)

diff --git a/lib/chamelium/v3/igt_chamelium.c b/lib/chamelium/v3/igt_chamelium.c
index 1122ce102..8fad88e74 100644
--- a/lib/chamelium/v3/igt_chamelium.c
+++ b/lib/chamelium/v3/igt_chamelium.c
@@ -282,3 +282,28 @@ bool chamelium_v3_is_mst(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_i
 
 	return is_mst;
 }
+
+/**
+ * chamelium_v3_get_port_name - Get the port name from its id
+ *
+ * @chamelium: Chamelium to get the ports from
+ * @port_id: Port to get the name
+ *
+ * Returns a string containing the port name for @port_id. The caller must free this pointer when
+ * not used anymore.
+ */
+char *chamelium_v3_get_port_name(struct igt_chamelium_v3 *chamelium,
+				 chamelium_v3_port_id port_id)
+{
+	xmlrpc_value *res;
+	char *port_name;
+
+	igt_debug("RPC GetPortName(%d)\n", port_id);
+	res = __chamelium_rpc(chamelium, "GetPortName", "(i)", port_id);
+
+	xmlrpc_read_string(&chamelium->env, res, (const char **)&port_name);
+
+	xmlrpc_DECREF(res);
+
+	return port_name;
+}
diff --git a/lib/chamelium/v3/igt_chamelium.h b/lib/chamelium/v3/igt_chamelium.h
index af5258068..71351e579 100644
--- a/lib/chamelium/v3/igt_chamelium.h
+++ b/lib/chamelium/v3/igt_chamelium.h
@@ -47,5 +47,6 @@ int chamelium_v3_get_supported_ports(struct igt_chamelium_v3 *chamelium,
 int chamelium_v3_get_children(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_id port_id,
 			      chamelium_v3_port_id **port_ids);
 bool chamelium_v3_is_mst(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_id port_id);
+char *chamelium_v3_get_port_name(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_id port_id);
 
 #endif //V3_IGT_CHAMELIUM_H
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH i-g-t v1 19/25] tests/chamelium/v3: Implement a basic Chamelium v3 accessibility test
  2026-04-28  4:46 [PATCH i-g-t v1 00/25] Chamelium v3 Integration and Test Execution Mohammed Bilal
                   ` (17 preceding siblings ...)
  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 ` 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
                   ` (9 subsequent siblings)
  28 siblings, 0 replies; 37+ messages in thread
From: Mohammed Bilal @ 2026-04-28  4:46 UTC (permalink / raw)
  To: igt-dev; +Cc: kunal1.joshi, Louis Chauvet

From: Louis Chauvet <louis.chauvet@bootlin.com>

Utilizing the RPC calls introduced earlier, this commit adds a simple test
to verify if the Chamelium v3 device is accessible.

Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
 tests/chamelium/v3/kms_chamelium_v3_basic.c | 30 +++++++++++++++++++++
 tests/meson.build                           |  1 +
 2 files changed, 31 insertions(+)
 create mode 100644 tests/chamelium/v3/kms_chamelium_v3_basic.c

diff --git a/tests/chamelium/v3/kms_chamelium_v3_basic.c b/tests/chamelium/v3/kms_chamelium_v3_basic.c
new file mode 100644
index 000000000..1b806458b
--- /dev/null
+++ b/tests/chamelium/v3/kms_chamelium_v3_basic.c
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: MIT
+
+#include "igt.h"
+#include "chamelium/v3/igt_chamelium.h"
+
+int igt_main()
+{
+	struct igt_chamelium_v3 *chamelium;
+
+	igt_describe("Test if the chamelium can list all the ports.");
+	igt_subtest("chamelium-list-ports") {
+		chamelium_v3_port_id *ports;
+		int port_count;
+
+		chamelium = chamelium_v3_init_from_config();
+
+		port_count = chamelium_v3_get_supported_ports(chamelium, &ports);
+
+		for (int i = 0; i < port_count; i++) {
+			char *name = chamelium_v3_get_port_name(chamelium, ports[i]);
+
+			igt_info("Port %d: name: %s\n", ports[i], name);
+			free(name);
+		}
+
+		free(ports);
+
+		chamelium_v3_uninit(chamelium);
+	}
+}
diff --git a/tests/meson.build b/tests/meson.build
index 17677e4e4..80e88954a 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -373,6 +373,7 @@ chamelium_v2_progs = [
 ]
 
 chamelium_v3_progs = [
+	'kms_chamelium_v3_basic',
 ]
 
 test_deps = [ igt_deps ]
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH i-g-t v1 20/25] lib/chamelium/v3: Add extended API for Chamelium v3 HPD, EDID, Frames, Color & Audio
  2026-04-28  4:46 [PATCH i-g-t v1 00/25] Chamelium v3 Integration and Test Execution Mohammed Bilal
                   ` (18 preceding siblings ...)
  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 ` Mohammed Bilal
  2026-04-29 10:44   ` Louis Chauvet
  2026-04-28  4:46 ` [PATCH i-g-t v1 21/25] tests/chamelium/v3: Add HPD (Hot Plug Detect) tests for Chamelium v3 Mohammed Bilal
                   ` (8 subsequent siblings)
  28 siblings, 1 reply; 37+ messages in thread
From: Mohammed Bilal @ 2026-04-28  4:46 UTC (permalink / raw)
  To: igt-dev; +Cc: kunal1.joshi, Mohammed Bilal

This commit adds the extended Chamelium v3 wrapper API functions needed
for HPD, EDID management, frame capture, color verification, and audio
testing with the Chamelium v3 hardware.

Signed-off-by: Mohammed Bilal <mohammed.bilal@intel.com>
---
 lib/chamelium/v3/igt_chamelium.c | 962 ++++++++++++++++++++++++++++++-
 lib/chamelium/v3/igt_chamelium.h | 195 +++++++
 2 files changed, 1155 insertions(+), 2 deletions(-)

diff --git a/lib/chamelium/v3/igt_chamelium.c b/lib/chamelium/v3/igt_chamelium.c
index 8fad88e74..4d4eef1f7 100644
--- a/lib/chamelium/v3/igt_chamelium.c
+++ b/lib/chamelium/v3/igt_chamelium.c
@@ -1,10 +1,16 @@
 // SPDX-License-Identifier: MIT
 
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
 #include <xmlrpc-c/base.h>
 #include <xmlrpc-c/client.h>
 
 #include "igt_chamelium.h"
 #include "igt_core.h"
+#include "igt_kms.h"
 #include "igt_rc.h"
 
 #define CHAMELIUM_CONFIG_SECTION "Chameliumv3"
@@ -70,13 +76,21 @@ struct igt_chamelium_v3 *chamelium_v3_init(char *url)
 	memset(&clientparms, 0, sizeof(clientparms));
 	memset(&curlparms, 0, sizeof(curlparms));
 
-	/* curl's timeout is in milliseconds */
-	curlparms.timeout = 10 * 1000;
+	/* curl's timeout is in milliseconds - 60s for large frame transfers */
+	curlparms.timeout = 60 * 1000;
 
 	clientparms.transport = "curl";
 	clientparms.transportparmsP = &curlparms;
 	clientparms.transportparm_size = XMLRPC_CXPSIZE(timeout);
 
+	/*
+	 * Increase the XML-RPC response size limit to 128MB to handle
+	 * DumpPixels responses for high-resolution frames (e.g. 4K/8K).
+	 * A 3840x2160 RGB frame is ~24MB raw, ~33MB base64 encoded.
+	 * The default xmlrpc-c limit is 15MB which is too small.
+	 */
+	xmlrpc_limit_set(XMLRPC_XML_SIZE_LIMIT_ID, 128 * 1024 * 1024);
+
 	/* Setup the libxmlrpc context */
 	xmlrpc_env_init(&chamelium->env);
 	xmlrpc_client_setup_global_const(&chamelium->env);
@@ -182,6 +196,44 @@ static xmlrpc_value *__chamelium_rpc(struct igt_chamelium_v3 *chamelium,
 	return res;
 }
 
+/**
+ * __chamelium_rpc_try - Call a remote function, returning NULL on failure
+ *
+ * Unlike __chamelium_rpc, this does NOT assert on RPC failure.
+ * Used for polling functions like GetVideoParams where transient
+ * failures (e.g. "Link has been severed") are expected during
+ * video stabilization.
+ *
+ * Returns a xmlrpc_value on success, or NULL on failure.
+ */
+static xmlrpc_value *__chamelium_rpc_try(struct igt_chamelium_v3 *chamelium,
+					 const char *method_name,
+					 const char *format_str,
+					 ...)
+{
+	xmlrpc_value *res;
+	va_list va_args;
+
+	if (chamelium->env.fault_occurred) {
+		xmlrpc_env_clean(&chamelium->env);
+		xmlrpc_env_init(&chamelium->env);
+	}
+	va_start(va_args, format_str);
+	xmlrpc_client_call2f_va(&chamelium->env, chamelium->client,
+				chamelium->url, method_name, format_str, &res,
+				va_args);
+	va_end(va_args);
+
+	if (chamelium->env.fault_occurred) {
+		igt_debug("Chamelium RPC call[%s] failed (non-fatal): %s\n",
+			  method_name, chamelium->env.fault_string);
+		xmlrpc_env_clean(&chamelium->env);
+		xmlrpc_env_init(&chamelium->env);
+		return NULL;
+	}
+	return res;
+}
+
 /*
  * For the RPC calls, please refer to the python code [1] for documentation.
  *
@@ -307,3 +359,909 @@ char *chamelium_v3_get_port_name(struct igt_chamelium_v3 *chamelium,
 
 	return port_name;
 }
+
+/**
+ * chamelium_v3_reset - Reset the Chamelium device
+ *
+ * @chamelium: Chamelium to reset
+ *
+ * Resets all ports to their default state (unplugged).
+ */
+void chamelium_v3_reset(struct igt_chamelium_v3 *chamelium)
+{
+	xmlrpc_value *res;
+
+	igt_debug("RPC Reset()\n");
+	res = __chamelium_rpc(chamelium, "Reset", "()");
+	xmlrpc_DECREF(res);
+}
+
+/**
+ * chamelium_v3_apply_edid - Apply an EDID to a port
+ *
+ * @chamelium: Chamelium to apply EDID on
+ * @port_id: Port to apply EDID to
+ * @edid_id: EDID ID to apply (0 for default)
+ *
+ * Chamelium V3 requires EDID to be applied before plugging a port.
+ */
+void chamelium_v3_apply_edid(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_id port_id,
+			     int edid_id)
+{
+	xmlrpc_value *res;
+
+	igt_debug("RPC ApplyEdid(%d, %d)\n", port_id, edid_id);
+	res = __chamelium_rpc(chamelium, "ApplyEdid", "(ii)", port_id, edid_id);
+	xmlrpc_DECREF(res);
+}
+
+/**
+ * chamelium_v3_plug - Plug a port on the Chamelium
+ *
+ * @chamelium: Chamelium to plug port on
+ * @port_id: Port to plug
+ *
+ * Note: Chamelium V3 requires ApplyEdid to be called before Plug.
+ */
+void chamelium_v3_plug(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_id port_id)
+{
+	xmlrpc_value *res;
+
+	igt_debug("RPC Plug(%d)\n", port_id);
+	res = __chamelium_rpc(chamelium, "Plug", "(i)", port_id);
+	xmlrpc_DECREF(res);
+}
+
+/**
+ * chamelium_v3_unplug - Unplug a port on the Chamelium
+ *
+ * @chamelium: Chamelium to unplug port on
+ * @port_id: Port to unplug
+ */
+void chamelium_v3_unplug(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_id port_id)
+{
+	xmlrpc_value *res;
+
+	igt_debug("RPC Unplug(%d)\n", port_id);
+	res = __chamelium_rpc(chamelium, "Unplug", "(i)", port_id);
+	xmlrpc_DECREF(res);
+}
+
+/**
+ * chamelium_v3_is_plugged - Check if a port is plugged
+ *
+ * @chamelium: Chamelium to check
+ * @port_id: Port to check
+ *
+ * Returns true if the port is plugged.
+ */
+bool chamelium_v3_is_plugged(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_id port_id)
+{
+	xmlrpc_value *res;
+	xmlrpc_bool is_plugged;
+
+	igt_debug("RPC IsPlugged(%d)\n", port_id);
+	res = __chamelium_rpc(chamelium, "IsPlugged", "(i)", port_id);
+
+	xmlrpc_read_bool(&chamelium->env, res, &is_plugged);
+	xmlrpc_DECREF(res);
+
+	return is_plugged;
+}
+
+/**
+ * chamelium_v3_plug_with_children - Plug an MST port with its children
+ *
+ * @chamelium: Chamelium to plug port on
+ * @port_id: Parent MST port to plug
+ * @children_ids: Array of children port IDs to plug with the parent
+ * @children_count: Number of children ports
+ *
+ * For MST, the parent port (e.g. FPGA_DP2) controls the HPD line.
+ * This function plugs the parent along with the specified children.
+ * Using this with an empty children list is equivalent to Plug().
+ */
+void chamelium_v3_plug_with_children(struct igt_chamelium_v3 *chamelium,
+				    chamelium_v3_port_id port_id,
+				    chamelium_v3_port_id *children_ids,
+				    int children_count)
+{
+	xmlrpc_value *res;
+	xmlrpc_value *children_array;
+	xmlrpc_value *child_val;
+	int i;
+
+	igt_debug("RPC PlugWithChildren(%d, [%d children])\n", port_id, children_count);
+
+	/* Build the children array */
+	children_array = xmlrpc_array_new(&chamelium->env);
+	for (i = 0; i < children_count; i++) {
+		child_val = xmlrpc_int_new(&chamelium->env, children_ids[i]);
+		xmlrpc_array_append_item(&chamelium->env, children_array, child_val);
+		xmlrpc_DECREF(child_val);
+	}
+
+	res = __chamelium_rpc(chamelium, "PlugWithChildren", "(iA)",
+			     port_id, children_array);
+	xmlrpc_DECREF(children_array);
+	xmlrpc_DECREF(res);
+}
+
+/**
+ * chamelium_v3_is_conflict - Check if two ports conflict (can't be plugged simultaneously)
+ *
+ * @chamelium: Chamelium to check
+ * @port_id_a: First port ID
+ * @port_id_b: Second port ID
+ *
+ * Some ports on Chamelium V3 are mutually exclusive (e.g. ITE HDMI1 and ITE HDMI2).
+ * If port A is plugged and conflicts with port B, plugging B will raise an exception.
+ * Port A must be explicitly unplugged first.
+ *
+ * Returns true if the two ports conflict.
+ */
+bool chamelium_v3_is_conflict(struct igt_chamelium_v3 *chamelium,
+			    chamelium_v3_port_id port_id_a,
+			    chamelium_v3_port_id port_id_b)
+{
+	xmlrpc_value *res;
+	xmlrpc_bool conflict;
+
+	igt_debug("RPC IsConflict(%d, %d)\n", port_id_a, port_id_b);
+	res = __chamelium_rpc(chamelium, "IsConflict", "(ii)", port_id_a, port_id_b);
+
+	xmlrpc_read_bool(&chamelium->env, res, &conflict);
+	xmlrpc_DECREF(res);
+
+	return conflict;
+}
+
+/**
+ * chamelium_v3_get_mutually_exclusive_ports - Get all ports that conflict with a given port
+ *
+ * @chamelium: Chamelium to check
+ * @port_id: Port to check conflicts for
+ * @port_ids: Out pointer for the list of conflicting port IDs
+ *
+ * Returns the number of conflicting ports stored in @port_ids.
+ * The caller must free this pointer.
+ */
+int chamelium_v3_get_mutually_exclusive_ports(struct igt_chamelium_v3 *chamelium,
+					     chamelium_v3_port_id port_id,
+					     chamelium_v3_port_id **port_ids)
+{
+	xmlrpc_value *res, *res_port;
+	int port_count, i;
+
+	igt_assert(port_ids);
+
+	igt_debug("RPC GetMutuallyExclusivePorts(%d)\n", port_id);
+	res = __chamelium_rpc(chamelium, "GetMutuallyExclusivePorts", "(i)", port_id);
+
+	port_count = xmlrpc_array_size(&chamelium->env, res);
+	*port_ids = calloc(port_count, sizeof(**port_ids));
+
+	for (i = 0; i < port_count; i++) {
+		xmlrpc_array_read_item(&chamelium->env, res, i, &res_port);
+		xmlrpc_read_int(&chamelium->env, res_port, (int *)&(*port_ids)[i]);
+		xmlrpc_DECREF(res_port);
+	}
+	xmlrpc_DECREF(res);
+
+	return port_count;
+}
+
+/**
+ * chamelium_v3_get_connector_type - Get the connector type string for a port
+ *
+ * @chamelium: Chamelium to check
+ * @port_id: Port to get the connector type for
+ *
+ * Returns a string with the connector type (e.g. "DP", "HDMI").
+ * The caller must free this pointer.
+ */
+char *chamelium_v3_get_connector_type(struct igt_chamelium_v3 *chamelium,
+				     chamelium_v3_port_id port_id)
+{
+	xmlrpc_value *res;
+	char *connector_type;
+
+	igt_debug("RPC GetConnectorType(%d)\n", port_id);
+	res = __chamelium_rpc(chamelium, "GetConnectorType", "(i)", port_id);
+
+	xmlrpc_read_string(&chamelium->env, res, (const char **)&connector_type);
+	xmlrpc_DECREF(res);
+
+	return connector_type;
+}
+
+/**
+ * chamelium_v3_get_port_type - Get the type of a port (DP, HDMI, etc.)
+ *
+ * @chamelium: Chamelium to check
+ * @port_id: Port to check
+ *
+ * Returns string containing port type. Caller must free.
+ */
+char *chamelium_v3_get_port_type(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_id port_id)
+{
+	char *name = chamelium_v3_get_port_name(chamelium, port_id);
+	char *type;
+
+	if (strstr(name, "DP") != NULL)
+		type = strdup("DP");
+	else if (strstr(name, "HDMI") != NULL)
+		type = strdup("HDMI");
+	else if (strstr(name, "VGA") != NULL)
+		type = strdup("VGA");
+	else
+		type = strdup("Unknown");
+
+	free(name);
+	return type;
+}
+
+/**
+ * chamelium_v3_port_is_dp - Check if a port is a DisplayPort
+ *
+ * @chamelium: Chamelium to check
+ * @port_id: Port to check
+ *
+ * Returns true if the port is a DP port.
+ */
+bool chamelium_v3_port_is_dp(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_id port_id)
+{
+	char *name = chamelium_v3_get_port_name(chamelium, port_id);
+	bool is_dp = (strstr(name, "DP") != NULL);
+	free(name);
+	return is_dp;
+}
+
+/**
+ * chamelium_v3_port_is_hdmi - Check if a port is HDMI
+ *
+ * @chamelium: Chamelium to check
+ * @port_id: Port to check
+ *
+ * Returns true if the port is an HDMI port.
+ */
+bool chamelium_v3_port_is_hdmi(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_id port_id)
+{
+	char *name = chamelium_v3_get_port_name(chamelium, port_id);
+	bool is_hdmi = (strstr(name, "HDMI") != NULL);
+	free(name);
+	return is_hdmi;
+}
+
+/**
+ * chamelium_v3_fire_hpd_pulse - Fire HPD pulses on a port
+ *
+ * @chamelium: Chamelium to fire pulses on
+ * @port_id: Port to fire pulses on
+ * @deassert_interval_usec: Time in microseconds for deasserted (unplugged) state
+ * @assert_interval_usec: Time in microseconds for asserted (plugged) state
+ * @repeat_count: Number of pulse cycles
+ * @end_level: Final HPD level (0 = deasserted/unplugged, 1 = asserted/plugged)
+ *
+ * Generates rapid HPD pulses for testing HPD storm detection and
+ * signal integrity.
+ */
+void chamelium_v3_fire_hpd_pulse(struct igt_chamelium_v3 *chamelium,
+				 chamelium_v3_port_id port_id,
+				 int deassert_interval_usec,
+				 int assert_interval_usec,
+				 int repeat_count,
+				 int end_level)
+{
+	xmlrpc_value *res;
+
+	igt_debug("RPC FireHpdPulse(%d, %d, %d, %d, %d)\n",
+		  port_id, deassert_interval_usec, assert_interval_usec,
+		  repeat_count, end_level);
+	res = __chamelium_rpc(chamelium, "FireHpdPulse", "(iiiii)",
+			     port_id, deassert_interval_usec,
+			     assert_interval_usec, repeat_count, end_level);
+	xmlrpc_DECREF(res);
+}
+
+/**
+ * chamelium_v3_create_edid - Upload a custom EDID to the Chamelium
+ *
+ * @chamelium: Chamelium to upload EDID to
+ * @edid_data: Raw EDID binary data
+ * @edid_size: Size of the EDID data in bytes
+ *
+ * Uploads raw EDID binary data to the Chamelium V3 device. The returned
+ * EDID ID can be used with chamelium_v3_apply_edid() to set the EDID
+ * on a specific port before plugging.
+ *
+ * Returns the EDID ID assigned by the Chamelium.
+ */
+int chamelium_v3_create_edid(struct igt_chamelium_v3 *chamelium,
+			     const unsigned char *edid_data,
+			     size_t edid_size)
+{
+	xmlrpc_value *res;
+	int edid_id;
+
+	igt_debug("RPC CreateEdid(%zu bytes)\n", edid_size);
+	res = __chamelium_rpc(chamelium, "CreateEdid", "(6)",
+			     edid_data, edid_size);
+
+	xmlrpc_read_int(&chamelium->env, res, &edid_id);
+	xmlrpc_DECREF(res);
+
+	return edid_id;
+}
+
+/**
+ * chamelium_v3_read_port_mappings - Read port mappings from .igtrc config
+ *
+ * Reads [Chamelium:<connector-name>] sections from the IGT config file
+ * and matches each connector name to a real DRM connector on the system.
+ * This is how V2 maps Chamelium ports to DRM connectors.
+ *
+ * @drm_fd: Open DRM file descriptor
+ * @ports: Output array of mapped ports (caller must free)
+ * @count: Output number of mapped ports
+ *
+ * Returns true on success, false if no mappings found or error.
+ */
+bool chamelium_v3_read_port_mappings(int drm_fd,
+				     struct chamelium_v3_drm_port **ports,
+				     int *count)
+{
+	GError *error = NULL;
+	char **group_list;
+	drmModeRes *res;
+	int mapping_count = 0;
+	int port_i = 0;
+	int i, j;
+
+	*ports = NULL;
+	*count = 0;
+
+	if (!igt_key_file) {
+		igt_debug("No config file for port mappings\n");
+		return false;
+	}
+
+	res = drmModeGetResources(drm_fd);
+	if (!res) {
+		igt_debug("Cannot get DRM resources\n");
+		return false;
+	}
+
+	group_list = g_key_file_get_groups(igt_key_file, NULL);
+
+	/* Count [Chamelium:*] sections */
+	for (i = 0; group_list[i] != NULL; i++) {
+		if (strstr(group_list[i], "Chamelium:"))
+			mapping_count++;
+	}
+
+	if (mapping_count == 0) {
+		igt_debug("No [Chamelium:<connector>] sections in config\n");
+		g_strfreev(group_list);
+		drmModeFreeResources(res);
+		return false;
+	}
+
+	*ports = calloc(mapping_count, sizeof(struct chamelium_v3_drm_port));
+	igt_assert(*ports);
+
+	for (i = 0; group_list[i] != NULL; i++) {
+		char *group = group_list[i];
+		char *map_name;
+		int chamelium_port_id;
+
+		if (!strstr(group, "Chamelium:"))
+			continue;
+
+		/* Extract connector name after "Chamelium:" */
+		map_name = group + strlen("Chamelium:");
+
+		chamelium_port_id = g_key_file_get_integer(igt_key_file, group,
+							   "ChameliumPortID",
+							   &error);
+		if (error) {
+			igt_debug("Failed to read ChameliumPortID for %s: %s\n",
+				  map_name, error->message);
+			g_error_free(error);
+			error = NULL;
+			continue;
+		}
+
+		/* Find the DRM connector matching this name */
+		for (j = 0; j < res->count_connectors; j++) {
+			drmModeConnector *conn;
+			char name[64];
+
+			conn = drmModeGetConnectorCurrent(drm_fd, res->connectors[j]);
+			if (!conn)
+				continue;
+
+			snprintf(name, sizeof(name), "%s-%u",
+				 kmstest_connector_type_str(conn->connector_type),
+				 conn->connector_type_id);
+
+			if (strcmp(name, map_name) == 0) {
+				struct chamelium_v3_drm_port *p = &(*ports)[port_i];
+
+				p->port_id = chamelium_port_id;
+				p->connector_id = conn->connector_id;
+				p->connector_type = conn->connector_type;
+				snprintf(p->connector_name, sizeof(p->connector_name),
+					 "%s", name);
+				port_i++;
+
+				igt_info("Mapped Chamelium port %d -> DRM %s (connector_id=%u)\n",
+					 chamelium_port_id, name, conn->connector_id);
+
+				drmModeFreeConnector(conn);
+				break;
+			}
+
+			drmModeFreeConnector(conn);
+		}
+	}
+
+	*count = port_i;
+	g_strfreev(group_list);
+	drmModeFreeResources(res);
+
+	if (port_i == 0) {
+		free(*ports);
+		*ports = NULL;
+		igt_warn("No connectors matched the Chamelium port mappings\n");
+		return false;
+	}
+
+	return true;
+}
+
+/**
+ * chamelium_v3_reprobe_connector - Force-reprobe a DRM connector
+ *
+ * Uses drmModeGetConnector() which triggers a kernel reprobe of the
+ * actual hardware state. This is how we verify whether a connector
+ * is truly connected or disconnected.
+ *
+ * @drm_fd: Open DRM file descriptor
+ * @connector_id: DRM connector ID to reprobe
+ *
+ * Returns the actual connection status.
+ */
+drmModeConnection chamelium_v3_reprobe_connector(int drm_fd,
+						 uint32_t connector_id)
+{
+	drmModeConnector *conn;
+	drmModeConnection status;
+
+	conn = drmModeGetConnector(drm_fd, connector_id);
+	if (!conn)
+		return DRM_MODE_UNKNOWNCONNECTION;
+
+	status = conn->connection;
+	drmModeFreeConnector(conn);
+
+	return status;
+}
+
+/**
+ * chamelium_v3_wait_for_conn_status_change - Poll connector until expected status
+ *
+ * Mirrors V2 chamelium_wait_for_conn_status_change. Polls the real DRM
+ * connector using drmModeGetConnector() (which triggers kernel reprobe)
+ * with 50ms intervals until the status matches or timeout.
+ *
+ * @drm_fd: Open DRM file descriptor
+ * @connector_id: DRM connector ID
+ * @expected: Expected connection status
+ * @timeout_secs: Max seconds to wait
+ *
+ * Returns true if the expected status was reached.
+ */
+bool chamelium_v3_wait_for_conn_status_change(int drm_fd,
+					      uint32_t connector_id,
+					      drmModeConnection expected,
+					      int timeout_secs)
+{
+	struct timespec start, now;
+
+	clock_gettime(CLOCK_MONOTONIC, &start);
+
+	while (1) {
+		if (chamelium_v3_reprobe_connector(drm_fd, connector_id) == expected)
+			return true;
+
+		clock_gettime(CLOCK_MONOTONIC, &now);
+		if (now.tv_sec - start.tv_sec >= timeout_secs)
+			break;
+
+		usleep(50000); /* 50ms poll, same as V2 */
+	}
+
+	return false;
+}
+
+/**
+ * chamelium_v3_get_video_params - Get video timing parameters from port
+ *
+ * Calls the GetVideoParams RPC to retrieve current video timing info.
+ * Uses non-asserting RPC to allow callers (e.g. polling/wait functions)
+ * to handle transient failures gracefully.
+ *
+ * @chamelium: Chamelium instance
+ * @port_id: Port ID to query
+ * @params: Output video parameters structure
+ *
+ * Returns true on success, false on RPC failure (e.g. link not ready).
+ */
+bool chamelium_v3_get_video_params(struct igt_chamelium_v3 *chamelium,
+				   chamelium_v3_port_id port_id,
+				   struct chamelium_v3_video_params *params)
+{
+	xmlrpc_value *res;
+	xmlrpc_value *val;
+
+	igt_assert(params);
+	memset(params, 0, sizeof(*params));
+
+	igt_debug("RPC GetVideoParams(%d)\n", port_id);
+	res = __chamelium_rpc_try(chamelium, "GetVideoParams", "(i)", port_id);
+	if (!res)
+		return false;
+
+	/* Parse the returned struct/dict */
+	xmlrpc_struct_find_value(&chamelium->env, res, "hactive", &val);
+	if (val) { xmlrpc_read_int(&chamelium->env, val, &params->hactive); xmlrpc_DECREF(val); }
+
+	xmlrpc_struct_find_value(&chamelium->env, res, "vactive", &val);
+	if (val) { xmlrpc_read_int(&chamelium->env, val, &params->vactive); xmlrpc_DECREF(val); }
+
+	xmlrpc_struct_find_value(&chamelium->env, res, "htotal", &val);
+	if (val) { xmlrpc_read_int(&chamelium->env, val, &params->htotal); xmlrpc_DECREF(val); }
+
+	xmlrpc_struct_find_value(&chamelium->env, res, "vtotal", &val);
+	if (val) { xmlrpc_read_int(&chamelium->env, val, &params->vtotal); xmlrpc_DECREF(val); }
+
+	xmlrpc_struct_find_value(&chamelium->env, res, "hsync_width", &val);
+	if (val) { xmlrpc_read_int(&chamelium->env, val, &params->hsync_width); xmlrpc_DECREF(val); }
+
+	xmlrpc_struct_find_value(&chamelium->env, res, "vsync_width", &val);
+	if (val) { xmlrpc_read_int(&chamelium->env, val, &params->vsync_width); xmlrpc_DECREF(val); }
+
+	xmlrpc_struct_find_value(&chamelium->env, res, "hfront_porch", &val);
+	if (val) { xmlrpc_read_int(&chamelium->env, val, &params->hfront_porch); xmlrpc_DECREF(val); }
+
+	xmlrpc_struct_find_value(&chamelium->env, res, "vfront_porch", &val);
+	if (val) { xmlrpc_read_int(&chamelium->env, val, &params->vfront_porch); xmlrpc_DECREF(val); }
+
+	xmlrpc_struct_find_value(&chamelium->env, res, "hback_porch", &val);
+	if (val) { xmlrpc_read_int(&chamelium->env, val, &params->hback_porch); xmlrpc_DECREF(val); }
+
+	xmlrpc_struct_find_value(&chamelium->env, res, "vback_porch", &val);
+	if (val) { xmlrpc_read_int(&chamelium->env, val, &params->vback_porch); xmlrpc_DECREF(val); }
+
+	xmlrpc_struct_find_value(&chamelium->env, res, "bpc", &val);
+	if (val) { xmlrpc_read_int(&chamelium->env, val, &params->bpc); xmlrpc_DECREF(val); }
+
+	xmlrpc_struct_find_value(&chamelium->env, res, "clock", &val);
+	if (val) { xmlrpc_read_double(&chamelium->env, val, &params->clock); xmlrpc_DECREF(val); }
+
+	xmlrpc_struct_find_value(&chamelium->env, res, "frame_rate", &val);
+	if (val) { xmlrpc_read_double(&chamelium->env, val, &params->frame_rate); xmlrpc_DECREF(val); }
+
+	xmlrpc_struct_find_value(&chamelium->env, res, "hsync_polarity", &val);
+	if (val) {
+		xmlrpc_bool b;
+		xmlrpc_read_bool(&chamelium->env, val, &b);
+		params->hsync_polarity = b;
+		xmlrpc_DECREF(val);
+	}
+
+	xmlrpc_struct_find_value(&chamelium->env, res, "vsync_polarity", &val);
+	if (val) {
+		xmlrpc_bool b;
+		xmlrpc_read_bool(&chamelium->env, val, &b);
+		params->vsync_polarity = b;
+		xmlrpc_DECREF(val);
+	}
+
+	xmlrpc_struct_find_value(&chamelium->env, res, "interlaced", &val);
+	if (val) {
+		xmlrpc_bool b;
+		xmlrpc_read_bool(&chamelium->env, val, &b);
+		params->interlaced = b;
+		xmlrpc_DECREF(val);
+	}
+
+	xmlrpc_DECREF(res);
+
+	igt_debug("Video params: %dx%d @ %.2f Hz, %d bpc\n",
+		  params->hactive, params->vactive,
+		  params->frame_rate, params->bpc);
+
+	return true;
+}
+
+/**
+ * chamelium_v3_has_audio_support - Check if a port supports audio
+ *
+ * @chamelium: Chamelium instance
+ * @port_id: Port ID to check
+ *
+ * Returns true if the port has audio support.
+ */
+bool chamelium_v3_has_audio_support(struct igt_chamelium_v3 *chamelium,
+				    chamelium_v3_port_id port_id)
+{
+	xmlrpc_value *res;
+	xmlrpc_bool has_support;
+
+	igt_debug("RPC HasAudioSupport(%d)\n", port_id);
+	res = __chamelium_rpc_try(chamelium, "HasAudioSupport", "(i)", port_id);
+	if (!res)
+		return false;
+
+	xmlrpc_read_bool(&chamelium->env, res, &has_support);
+	xmlrpc_DECREF(res);
+
+	return has_support;
+}
+
+/**
+ * chamelium_v3_start_capturing_audio - Start capturing audio from a port
+ *
+ * @chamelium: Chamelium instance
+ * @port_id: Port to capture audio from
+ * @save_to_file: Whether to save captured audio to a file on the Chamelium
+ */
+bool chamelium_v3_start_capturing_audio(struct igt_chamelium_v3 *chamelium,
+					chamelium_v3_port_id port_id,
+					bool save_to_file)
+{
+	xmlrpc_value *res;
+
+	igt_debug("RPC StartCapturingAudio(%d, %s)\n",
+		  port_id, save_to_file ? "true" : "false");
+	res = __chamelium_rpc_try(chamelium, "StartCapturingAudio", "(ib)",
+				 port_id, save_to_file);
+	if (!res)
+		return false;
+	xmlrpc_DECREF(res);
+	return true;
+}
+
+/**
+ * chamelium_v3_stop_capturing_audio - Stop capturing audio from a port
+ *
+ * @chamelium: Chamelium instance
+ * @port_id: Port to stop capturing audio from
+ */
+void chamelium_v3_stop_capturing_audio(struct igt_chamelium_v3 *chamelium,
+				       chamelium_v3_port_id port_id)
+{
+	xmlrpc_value *res;
+
+	igt_debug("RPC StopCapturingAudio(%d)\n", port_id);
+	res = __chamelium_rpc_try(chamelium, "StopCapturingAudio", "(i)",
+				  port_id);
+	if (res)
+		xmlrpc_DECREF(res);
+}
+
+/**
+ * chamelium_v3_get_audio_format - Get the audio capture format
+ *
+ * @chamelium: Chamelium instance
+ * @port_id: Port to query
+ * @rate: Output sampling rate in Hz (or 0 if unknown)
+ * @channels: Output number of channels
+ */
+void chamelium_v3_get_audio_format(struct igt_chamelium_v3 *chamelium,
+				   chamelium_v3_port_id port_id,
+				   int *rate, int *channels)
+{
+	xmlrpc_value *res, *val;
+
+	igt_debug("RPC GetAudioFormat(%d)\n", port_id);
+	res = __chamelium_rpc(chamelium, "GetAudioFormat", "(i)", port_id);
+
+	if (rate) {
+		xmlrpc_struct_find_value(&chamelium->env, res, "rate", &val);
+		if (val) {
+			xmlrpc_read_int(&chamelium->env, val, rate);
+			xmlrpc_DECREF(val);
+		} else {
+			*rate = 0;
+		}
+	}
+
+	if (channels) {
+		xmlrpc_struct_find_value(&chamelium->env, res, "channel", &val);
+		if (val) {
+			xmlrpc_read_int(&chamelium->env, val, channels);
+			xmlrpc_DECREF(val);
+		} else {
+			*channels = 0;
+		}
+	}
+
+	xmlrpc_DECREF(res);
+}
+
+/**
+ * chamelium_v3_get_audio_channel_mapping - Get audio channel mapping
+ *
+ * @chamelium: Chamelium instance
+ * @port_id: Port to query
+ * @mapping: Array of CHAMELIUM_V3_MAX_AUDIO_CHANNELS ints.
+ *           Each element indicates which input channel the capture channel
+ *           is mapped to. -1 means unmapped.
+ */
+void chamelium_v3_get_audio_channel_mapping(struct igt_chamelium_v3 *chamelium,
+					    chamelium_v3_port_id port_id,
+					    int mapping[static CHAMELIUM_V3_MAX_AUDIO_CHANNELS])
+{
+	xmlrpc_value *res, *res_channel;
+	int res_len, i;
+
+	igt_debug("RPC GetAudioChannelMapping(%d)\n", port_id);
+	res = __chamelium_rpc(chamelium, "GetAudioChannelMapping", "(i)", port_id);
+
+	res_len = xmlrpc_array_size(&chamelium->env, res);
+	igt_assert_f(res_len == CHAMELIUM_V3_MAX_AUDIO_CHANNELS,
+		     "Expected %d audio channels, got %d\n",
+		     CHAMELIUM_V3_MAX_AUDIO_CHANNELS, res_len);
+
+	for (i = 0; i < res_len; i++) {
+		xmlrpc_array_read_item(&chamelium->env, res, i, &res_channel);
+		xmlrpc_read_int(&chamelium->env, res_channel, &mapping[i]);
+		xmlrpc_DECREF(res_channel);
+	}
+	xmlrpc_DECREF(res);
+}
+
+/**
+ * chamelium_v3_get_last_infoframe - Get the last received InfoFrame
+ *
+ * @chamelium: Chamelium instance
+ * @port_id: Port to query
+ * @infoframe_type: Type string: "avi", "audio", "mpeg", "vendor", "spd"
+ *
+ * Returns: InfoFrame structure (caller must free with chamelium_v3_infoframe_destroy),
+ *          or NULL if no InfoFrame was received.
+ */
+struct chamelium_v3_infoframe *chamelium_v3_get_last_infoframe(
+	struct igt_chamelium_v3 *chamelium,
+	chamelium_v3_port_id port_id,
+	const char *infoframe_type)
+{
+	xmlrpc_value *res, *val;
+	struct chamelium_v3_infoframe *infoframe;
+	const unsigned char *payload_data;
+	size_t payload_len;
+
+	igt_debug("RPC GetLastInfoFrame(%d, \"%s\")\n", port_id, infoframe_type);
+	res = __chamelium_rpc_try(chamelium, "GetLastInfoFrame", "(is)",
+				  port_id, infoframe_type);
+	if (!res)
+		return NULL;
+
+	infoframe = calloc(1, sizeof(*infoframe));
+
+	xmlrpc_struct_find_value(&chamelium->env, res, "version", &val);
+	if (val) {
+		xmlrpc_read_int(&chamelium->env, val, &infoframe->version);
+		xmlrpc_DECREF(val);
+	}
+
+	xmlrpc_struct_find_value(&chamelium->env, res, "payload", &val);
+	if (val) {
+		xmlrpc_read_base64(&chamelium->env, val, &payload_len,
+				   &payload_data);
+		infoframe->payload = malloc(payload_len);
+		memcpy(infoframe->payload, payload_data, payload_len);
+		infoframe->payload_size = payload_len;
+		xmlrpc_DECREF(val);
+	}
+
+	xmlrpc_DECREF(res);
+
+	return infoframe;
+}
+
+/**
+ * chamelium_v3_infoframe_destroy - Free an InfoFrame structure
+ */
+void chamelium_v3_infoframe_destroy(struct chamelium_v3_infoframe *infoframe)
+{
+	if (infoframe) {
+		free(infoframe->payload);
+		free(infoframe);
+	}
+}
+
+/**
+ * chamelium_v3_capture_frame - Capture current frame from port
+ *
+ * Captures the current displayed frame using DumpPixels API.
+ * Returns RGB data (3 bytes per pixel).
+ *
+ * @chamelium: Chamelium instance
+ * @port_id: Port ID to capture from
+ *
+ * Returns: Frame structure with captured data (caller must free with chamelium_v3_free_frame)
+ */
+struct chamelium_v3_frame *chamelium_v3_capture_frame(struct igt_chamelium_v3 *chamelium,
+						      chamelium_v3_port_id port_id)
+{
+	struct chamelium_v3_frame *frame;
+	struct chamelium_v3_video_params params;
+	xmlrpc_value *res;
+	const unsigned char *data;
+	size_t data_len;
+
+	/* First get video params to know resolution */
+	if (!chamelium_v3_get_video_params(chamelium, port_id, &params)) {
+		igt_warn("Failed to get video params for port %d\n", port_id);
+		return NULL;
+	}
+
+	if (params.hactive <= 0 || params.vactive <= 0) {
+		igt_warn("Invalid video params: %dx%d\n", params.hactive, params.vactive);
+		return NULL;
+	}
+
+	igt_debug("RPC DumpPixels(%d)\n", port_id);
+	res = __chamelium_rpc_try(chamelium, "DumpPixels", "(i)", port_id);
+	if (!res) {
+		igt_warn("DumpPixels RPC failed for port %d\n", port_id);
+		return NULL;
+	}
+
+	/* Extract binary data from XML-RPC response */
+	xmlrpc_read_base64(&chamelium->env, res, &data_len, &data);
+
+	if (chamelium->env.fault_occurred) {
+		igt_warn("Failed to read DumpPixels data: %s\n",
+			 chamelium->env.fault_string);
+		xmlrpc_DECREF(res);
+		return NULL;
+	}
+
+	/* Allocate frame structure */
+	frame = calloc(1, sizeof(*frame));
+	frame->width = params.hactive;
+	frame->height = params.vactive;
+	frame->size = data_len;
+	frame->data = malloc(data_len);
+	memcpy(frame->data, data, data_len);
+
+	xmlrpc_DECREF(res);
+
+	igt_debug("Captured frame: %dx%d, %zu bytes\n",
+		  frame->width, frame->height, frame->size);
+
+	/* Verify data size matches expected RGB (3 bytes per pixel) */
+	size_t expected = (size_t)frame->width * frame->height * 3;
+	if (frame->size != expected) {
+		igt_warn("Frame size mismatch: got %zu, expected %zu\n",
+			 frame->size, expected);
+	}
+
+	return frame;
+}
+
+/**
+ * chamelium_v3_free_frame - Free captured frame memory
+ */
+void chamelium_v3_free_frame(struct chamelium_v3_frame *frame)
+{
+	if (frame) {
+		free(frame->data);
+		free(frame);
+	}
+}
diff --git a/lib/chamelium/v3/igt_chamelium.h b/lib/chamelium/v3/igt_chamelium.h
index 71351e579..a0da1bb6a 100644
--- a/lib/chamelium/v3/igt_chamelium.h
+++ b/lib/chamelium/v3/igt_chamelium.h
@@ -49,4 +49,199 @@ int chamelium_v3_get_children(struct igt_chamelium_v3 *chamelium, chamelium_v3_p
 bool chamelium_v3_is_mst(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_id port_id);
 char *chamelium_v3_get_port_name(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_id port_id);
 
+/* Port control functions */
+void chamelium_v3_reset(struct igt_chamelium_v3 *chamelium);
+void chamelium_v3_plug(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_id port_id);
+void chamelium_v3_unplug(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_id port_id);
+bool chamelium_v3_is_plugged(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_id port_id);
+void chamelium_v3_apply_edid(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_id port_id,
+			     int edid_id);
+
+/* MST (Multi-Stream Transport) functions */
+void chamelium_v3_plug_with_children(struct igt_chamelium_v3 *chamelium,
+				    chamelium_v3_port_id port_id,
+				    chamelium_v3_port_id *children_ids,
+				    int children_count);
+bool chamelium_v3_is_conflict(struct igt_chamelium_v3 *chamelium,
+			    chamelium_v3_port_id port_id_a,
+			    chamelium_v3_port_id port_id_b);
+int chamelium_v3_get_mutually_exclusive_ports(struct igt_chamelium_v3 *chamelium,
+					     chamelium_v3_port_id port_id,
+					     chamelium_v3_port_id **port_ids);
+char *chamelium_v3_get_connector_type(struct igt_chamelium_v3 *chamelium,
+				     chamelium_v3_port_id port_id);
+
+/* Port type detection */
+char *chamelium_v3_get_port_type(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_id port_id);
+bool chamelium_v3_port_is_dp(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_id port_id);
+bool chamelium_v3_port_is_hdmi(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_id port_id);
+
+/* HPD pulse generation */
+void chamelium_v3_fire_hpd_pulse(struct igt_chamelium_v3 *chamelium,
+				 chamelium_v3_port_id port_id,
+				 int deassert_interval_usec,
+				 int assert_interval_usec,
+				 int repeat_count,
+				 int end_level);
+
+/* EDID creation - returns EDID ID for use with chamelium_v3_apply_edid */
+int chamelium_v3_create_edid(struct igt_chamelium_v3 *chamelium,
+			     const unsigned char *edid_data,
+			     size_t edid_size);
+
+/* Video parameters structure */
+struct chamelium_v3_video_params {
+	int hactive;
+	int vactive;
+	int htotal;
+	int vtotal;
+	int hsync_width;
+	int vsync_width;
+	int hfront_porch;
+	int vfront_porch;
+	int hback_porch;
+	int vback_porch;
+	bool hsync_polarity;
+	bool vsync_polarity;
+	double clock;
+	double frame_rate;
+	int bpc;
+	bool interlaced;
+};
+
+/* Frame capture structure */
+struct chamelium_v3_frame {
+	uint8_t *data;      /* RGB pixel data (3 bytes per pixel) */
+	int width;
+	int height;
+	size_t size;        /* Total data size in bytes */
+};
+
+/**
+ * chamelium_v3_get_video_params - Get video timing parameters from port
+ *
+ * @chamelium: Chamelium instance
+ * @port_id: Port ID to query
+ * @params: Output video parameters structure
+ *
+ * Returns true on success.
+ */
+bool chamelium_v3_get_video_params(struct igt_chamelium_v3 *chamelium,
+				   chamelium_v3_port_id port_id,
+				   struct chamelium_v3_video_params *params);
+
+/**
+ * chamelium_v3_capture_frame - Capture current frame from port
+ *
+ * Captures the current displayed frame using DumpPixels API.
+ * Returns RGB data (3 bytes per pixel).
+ *
+ * @chamelium: Chamelium instance
+ * @port_id: Port ID to capture from
+ *
+ * Returns: Frame structure with captured data (caller must free with chamelium_v3_free_frame)
+ */
+struct chamelium_v3_frame *chamelium_v3_capture_frame(struct igt_chamelium_v3 *chamelium,
+						      chamelium_v3_port_id port_id);
+
+/**
+ * chamelium_v3_free_frame - Free captured frame memory
+ */
+void chamelium_v3_free_frame(struct chamelium_v3_frame *frame);
+
+/*
+ * Port-to-DRM-connector mapping.
+ *
+ * Reads [Chamelium:<connector-name>] sections from .igtrc to map
+ * Chamelium port IDs to DRM connector IDs on the DUT.
+ * This is essential for verifying that plug/unplug actually changes
+ * the real DRM connector state.
+ */
+
+/** struct chamelium_v3_drm_port - A Chamelium port mapped to a DRM connector */
+struct chamelium_v3_drm_port {
+	chamelium_v3_port_id port_id;   /* Chamelium port ID (e.g. 4) */
+	uint32_t connector_id;          /* DRM connector_id from kernel */
+	uint32_t connector_type;        /* DRM_MODE_CONNECTOR_* type */
+	char connector_name[64];        /* DRM connector name e.g. "DP-1" */
+};
+
+/**
+ * chamelium_v3_read_port_mappings - Read port mappings from .igtrc config
+ *
+ * Reads [Chamelium:<name>] sections with ChameliumPortID keys, then finds
+ * the matching DRM connector on the system.
+ *
+ * @drm_fd: Open DRM file descriptor
+ * @ports: Output array of mapped ports (caller must free)
+ * @count: Output number of mapped ports
+ *
+ * Returns true on success.
+ */
+bool chamelium_v3_read_port_mappings(int drm_fd,
+				     struct chamelium_v3_drm_port **ports,
+				     int *count);
+
+/**
+ * chamelium_v3_reprobe_connector - Force-reprobe a DRM connector and return status
+ *
+ * Uses drmModeGetConnector() which triggers a kernel reprobe of the
+ * actual hardware state. This is how V2 verifies real connector status.
+ *
+ * @drm_fd: Open DRM file descriptor
+ * @connector_id: DRM connector ID to reprobe
+ *
+ * Returns: drmModeConnection (DRM_MODE_CONNECTED, DRM_MODE_DISCONNECTED, etc.)
+ */
+drmModeConnection chamelium_v3_reprobe_connector(int drm_fd,
+						 uint32_t connector_id);
+
+/**
+ * chamelium_v3_wait_for_conn_status_change - Poll connector until expected status or timeout
+ *
+ * Mirrors V2 chamelium_wait_for_conn_status_change. Polls with 50ms
+ * interval until the connector reports the expected status.
+ *
+ * @drm_fd: Open DRM file descriptor
+ * @connector_id: DRM connector ID
+ * @expected: Expected connection status
+ * @timeout_secs: Max seconds to wait
+ *
+ * Returns true if the expected status was reached.
+ */
+bool chamelium_v3_wait_for_conn_status_change(int drm_fd,
+					      uint32_t connector_id,
+					      drmModeConnection expected,
+					      int timeout_secs);
+
+/* Audio support */
+#define CHAMELIUM_V3_MAX_AUDIO_CHANNELS 8
+
+bool chamelium_v3_has_audio_support(struct igt_chamelium_v3 *chamelium,
+				    chamelium_v3_port_id port_id);
+bool chamelium_v3_start_capturing_audio(struct igt_chamelium_v3 *chamelium,
+					chamelium_v3_port_id port_id,
+					bool save_to_file);
+void chamelium_v3_stop_capturing_audio(struct igt_chamelium_v3 *chamelium,
+				       chamelium_v3_port_id port_id);
+void chamelium_v3_get_audio_format(struct igt_chamelium_v3 *chamelium,
+				   chamelium_v3_port_id port_id,
+				   int *rate, int *channels);
+void chamelium_v3_get_audio_channel_mapping(struct igt_chamelium_v3 *chamelium,
+					    chamelium_v3_port_id port_id,
+					    int mapping[static CHAMELIUM_V3_MAX_AUDIO_CHANNELS]);
+
+/* InfoFrame support */
+struct chamelium_v3_infoframe {
+	int version;
+	uint8_t *payload;
+	size_t payload_size;
+};
+
+struct chamelium_v3_infoframe *chamelium_v3_get_last_infoframe(
+	struct igt_chamelium_v3 *chamelium,
+	chamelium_v3_port_id port_id,
+	const char *infoframe_type);
+void chamelium_v3_infoframe_destroy(struct chamelium_v3_infoframe *infoframe);
+
 #endif //V3_IGT_CHAMELIUM_H
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH i-g-t v1 21/25] tests/chamelium/v3: Add HPD (Hot Plug Detect) tests for Chamelium v3
  2026-04-28  4:46 [PATCH i-g-t v1 00/25] Chamelium v3 Integration and Test Execution Mohammed Bilal
                   ` (19 preceding siblings ...)
  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-28  4:46 ` Mohammed Bilal
  2026-04-28  4:46 ` [PATCH i-g-t v1 22/25] tests/chamelium/v3: Add EDID " Mohammed Bilal
                   ` (7 subsequent siblings)
  28 siblings, 0 replies; 37+ messages in thread
From: Mohammed Bilal @ 2026-04-28  4:46 UTC (permalink / raw)
  To: igt-dev; +Cc: kunal1.joshi, Mohammed Bilal

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


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH i-g-t v1 22/25] tests/chamelium/v3: Add EDID tests for Chamelium v3
  2026-04-28  4:46 [PATCH i-g-t v1 00/25] Chamelium v3 Integration and Test Execution Mohammed Bilal
                   ` (20 preceding siblings ...)
  2026-04-28  4:46 ` [PATCH i-g-t v1 21/25] tests/chamelium/v3: Add HPD (Hot Plug Detect) tests for Chamelium v3 Mohammed Bilal
@ 2026-04-28  4:46 ` Mohammed Bilal
  2026-04-28  4:46 ` [PATCH i-g-t v1 23/25] tests/chamelium/v3: Add frame capture and CRC " Mohammed Bilal
                   ` (6 subsequent siblings)
  28 siblings, 0 replies; 37+ messages in thread
From: Mohammed Bilal @ 2026-04-28  4:46 UTC (permalink / raw)
  To: igt-dev; +Cc: kunal1.joshi, Mohammed Bilal

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

This commit adds EDID testing capabilities for Chamelium v3 including
EDID read/write, mode detection, and multiple monitor EDID support.

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

diff --git a/tests/chamelium/v3/kms_chamelium_v3_edid.c b/tests/chamelium/v3/kms_chamelium_v3_edid.c
new file mode 100644
index 000000000..68b361bfe
--- /dev/null
+++ b/tests/chamelium/v3/kms_chamelium_v3_edid.c
@@ -0,0 +1,1357 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2026 Intel Corporation
+ *
+ * Chamelium V3 EDID Tests
+ *
+ * Tests EDID reading (strict byte comparison), custom EDID upload,
+ * EDID change detection, mode timing verification, and resolution validation.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <math.h>
+#include <time.h>
+
+#include "igt.h"
+#include "igt_edid.h"
+#include "igt_aux.h"
+#include "chamelium/v3/igt_chamelium.h"
+#include "monitor_edids/dp_edids.h"
+#include "monitor_edids/hdmi_edids.h"
+#include "monitor_edids/monitor_edids_helper.h"
+
+/**
+ * TEST: kms chamelium v3 edid
+ * Category: Display
+ * Description: EDID tests for Chamelium V3
+ * Driver requirement: i915, xe
+ * Mega feature: General Display Features
+ *
+ * SUBTEST: dp-edid-read
+ * Description: Make sure the EDID exposed by KMS is the same as the screen's
+ *
+ * SUBTEST: hdmi-edid-read
+ * Description: Make sure the EDID exposed by KMS is the same as the screen's
+ *
+ * SUBTEST: dp-edid-stress-resolution-%s
+ * Description: 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 advertised mode (%arg[1]) resolution.
+ *
+ * SUBTEST: hdmi-edid-stress-resolution-%s
+ * Description: 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 advertised mode (%arg[1]) resolution.
+ *
+ * arg[1]:
+ *
+ * @4k:      4K
+ * @non-4k:  Non 4K
+ *
+ * SUBTEST: dp-edid-resolution-list
+ * Description: Get an EDID with many modes of different configurations, set
+ *              them on the screen and check the screen resolution matches the
+ *              mode resolution.
+ *
+ * SUBTEST: dp-edid-change-during-%s
+ * Description: Simulate a screen being unplugged and another screen being
+ *              plugged during suspend, check that a uevent is sent and
+ *              connector status is updated during %arg[1]
+ *
+ * SUBTEST: hdmi-edid-change-during-%s
+ * Description: Simulate a screen being unplugged and another screen being
+ *              plugged during suspend, check that a uevent is sent and
+ *              connector status is updated during %arg[1]
+ *
+ * arg[1]:
+ *
+ * @hibernate:    hibernation
+ * @suspend:      suspend
+ *
+ * SUBTEST: dp-mode-timings
+ * Description: For each mode of the IGT base EDID, perform a modeset and check
+ *              the mode detected by the Chamelium receiver matches the mode we
+ *              set
+ *
+ * SUBTEST: hdmi-mode-timings
+ * Description: For each mode of the IGT base EDID, perform a modeset and check
+ *              the mode detected by the Chamelium receiver matches the mode we
+ *              set
+ */
+
+#define HPD_WAIT_TIME_MS 3000
+#define EDID_BLOCK_SIZE 128
+#define EDID_SETTLE_TIME_MS 2000
+#define CONNECTOR_REPROBE_RETRIES 10
+#define MODE_CLOCK_ACCURACY 0.05 /* 5% tolerance */
+#define VIDEO_STABLE_TIMEOUT_S 5
+#define MODESET_SETTLE_TIME_MS 2000
+#define CHAMELIUM_HOTPLUG_TIMEOUT 20
+
+/* FPGA DP ports (recommended) */
+#define FPGA_DP1_PORT 4
+#define FPGA_DP2_PORT 5
+#define ITE_HDMI1_PORT 2
+#define ITE_HDMI2_PORT 3
+
+struct edid_test_data {
+	struct igt_chamelium_v3 *chamelium;
+	chamelium_v3_port_id *ports;
+	int port_count;
+	int drm_fd;
+};
+
+/* Find a DP port, preferring FPGA ports */
+static chamelium_v3_port_id find_dp_port(struct edid_test_data *data)
+{
+	int i;
+
+	/* Prefer FPGA DP ports (4, 5) over ITE DP ports (0, 1) */
+	for (i = 0; i < data->port_count; i++) {
+		chamelium_v3_port_id port = data->ports[i];
+
+		if (chamelium_v3_port_is_dp(data->chamelium, port) &&
+		    (port == FPGA_DP1_PORT || port == FPGA_DP2_PORT))
+			return port;
+	}
+	/* Fall back to any DP port */
+	for (i = 0; i < data->port_count; i++) {
+		chamelium_v3_port_id port = data->ports[i];
+
+		if (chamelium_v3_port_is_dp(data->chamelium, port))
+			return port;
+	}
+	return (chamelium_v3_port_id)-1;
+}
+
+/* Find an HDMI port */
+static chamelium_v3_port_id find_hdmi_port(struct edid_test_data *data)
+{
+	int i;
+
+	for (i = 0; i < data->port_count; i++) {
+		chamelium_v3_port_id port = data->ports[i];
+
+		if (chamelium_v3_port_is_hdmi(data->chamelium, port))
+			return port;
+	}
+	return (chamelium_v3_port_id)-1;
+}
+
+/*
+ * Find a connected DRM connector by type.
+ * Uses drmModeGetConnector() which forces a reprobe.
+ * Caller must free with drmModeFreeConnector().
+ */
+static drmModeConnector *find_connected_connector(int drm_fd, uint32_t connector_type)
+{
+	drmModeRes *res;
+	drmModeConnector *connector;
+	int i;
+
+	res = drmModeGetResources(drm_fd);
+	igt_assert(res);
+
+	for (i = 0; i < res->count_connectors; i++) {
+		connector = drmModeGetConnector(drm_fd, res->connectors[i]);
+		if (!connector)
+			continue;
+
+		if (connector->connector_type == connector_type &&
+		    connector->connection == DRM_MODE_CONNECTED) {
+			drmModeFreeResources(res);
+			return connector;
+		}
+		drmModeFreeConnector(connector);
+	}
+
+	drmModeFreeResources(res);
+	return NULL;
+}
+
+/*
+ * Wait for a connector of the given type to become connected.
+ * Retries with reprobe to handle HPD propagation delay.
+ * Returns the connected connector, or NULL if timeout.
+ */
+static drmModeConnector *wait_for_connected_connector(int drm_fd, uint32_t connector_type)
+{
+	drmModeConnector *connector;
+	int attempt;
+
+	for (attempt = 0; attempt < CONNECTOR_REPROBE_RETRIES; attempt++) {
+		usleep(HPD_WAIT_TIME_MS * 1000 / 2);
+		connector = find_connected_connector(drm_fd, connector_type);
+		if (connector)
+			return connector;
+	}
+	return NULL;
+}
+
+/*
+ * Read the EDID blob from a DRM connector.
+ * Returns allocated buffer with EDID data, or NULL if not found.
+ * Caller must free the returned buffer.
+ */
+static unsigned char *read_edid_from_connector(int drm_fd, drmModeConnector *connector,
+					       int *edid_len)
+{
+	int i;
+	drmModePropertyPtr prop;
+	drmModePropertyBlobPtr blob;
+	unsigned char *edid_data;
+
+	for (i = 0; i < connector->count_props; i++) {
+		prop = drmModeGetProperty(drm_fd, connector->props[i]);
+		if (!prop)
+			continue;
+
+		if (strcmp(prop->name, "EDID") == 0 &&
+		    connector->prop_values[i] != 0) {
+			blob = drmModeGetPropertyBlob(drm_fd,
+						      connector->prop_values[i]);
+			if (blob && blob->length > 0) {
+				edid_data = malloc(blob->length);
+				igt_assert(edid_data);
+				memcpy(edid_data, blob->data, blob->length);
+				*edid_len = blob->length;
+				drmModeFreePropertyBlob(blob);
+				drmModeFreeProperty(prop);
+				return edid_data;
+			}
+			if (blob)
+				drmModeFreePropertyBlob(blob);
+		}
+		drmModeFreeProperty(prop);
+	}
+
+	*edid_len = 0;
+	return NULL;
+}
+
+/* Verify EDID header magic bytes: 00 FF FF FF FF FF FF 00 */
+static bool verify_edid_header(const unsigned char *edid, int len)
+{
+	static const unsigned char edid_header[] = {
+		0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00
+	};
+
+	if (len < EDID_BLOCK_SIZE)
+		return false;
+
+	return memcmp(edid, edid_header, sizeof(edid_header)) == 0;
+}
+
+/* Get connector type constant for DRM */
+static uint32_t get_drm_connector_type(bool is_dp)
+{
+	return is_dp ? DRM_MODE_CONNECTOR_DisplayPort : DRM_MODE_CONNECTOR_HDMIA;
+}
+
+/* Get port for given type */
+static chamelium_v3_port_id get_port_for_type(struct edid_test_data *data, bool is_dp)
+{
+	return is_dp ? find_dp_port(data) : find_hdmi_port(data);
+}
+
+/*
+ * Find a CRTC that can drive the given connector.
+ * Needed for DRM modesetting in mode-timings and resolution tests.
+ */
+static uint32_t find_crtc_for_connector(int drm_fd, drmModeConnector *conn)
+{
+	drmModeRes *res;
+	drmModeEncoder *encoder;
+	uint32_t crtc_id = 0;
+	int i, j;
+
+	res = drmModeGetResources(drm_fd);
+	igt_assert(res);
+
+	for (i = 0; i < conn->count_encoders && !crtc_id; i++) {
+		encoder = drmModeGetEncoder(drm_fd, conn->encoders[i]);
+		if (!encoder)
+			continue;
+
+		for (j = 0; j < res->count_crtcs && !crtc_id; j++) {
+			if (encoder->possible_crtcs & (1 << j))
+				crtc_id = res->crtcs[j];
+		}
+		drmModeFreeEncoder(encoder);
+	}
+
+	drmModeFreeResources(res);
+	return crtc_id;
+}
+
+/* Disable all CRTCs to get a clean slate for modesetting */
+static void disable_all_crtcs(int drm_fd)
+{
+	drmModeRes *res;
+	int i;
+
+	res = drmModeGetResources(drm_fd);
+	igt_assert(res);
+
+	for (i = 0; i < res->count_crtcs; i++)
+		drmModeSetCrtc(drm_fd, res->crtcs[i], 0, 0, 0, NULL, 0, NULL);
+
+	drmModeFreeResources(res);
+}
+
+/*
+ * Ensure a specific Chamelium port is plugged with default EDID.
+ *
+ * Some ports share muxes (e.g., FPGA DP1 and DP2 are mutually exclusive),
+ * so we only plug the target port — not all ports.
+ *
+ * This "primes" the DRM connector state so that subsequent per-port
+ * unplug/replug cycles are fast, especially for FPGA DP ports which
+ * need a long time to recover from a full chamelium_v3_reset().
+ */
+static void ensure_port_plugged(struct edid_test_data *data,
+				chamelium_v3_port_id port)
+{
+	disable_all_crtcs(data->drm_fd);
+
+	chamelium_v3_apply_edid(data->chamelium, port, 0);
+	if (!chamelium_v3_is_plugged(data->chamelium, port))
+		chamelium_v3_plug(data->chamelium, port);
+
+	/* Give port time to complete link training */
+	usleep(HPD_WAIT_TIME_MS * 1000);
+	usleep(EDID_SETTLE_TIME_MS * 1000);
+
+	igt_debug("Port %d plugged with default EDID\n", port);
+}
+
+/*
+ * Wait for video to become stable on a Chamelium port.
+ * Polls GetVideoParams until valid resolution is reported or timeout.
+ */
+static bool wait_for_video_stable(struct igt_chamelium_v3 *chamelium,
+				  chamelium_v3_port_id port,
+				  int timeout_secs)
+{
+	struct chamelium_v3_video_params params;
+	struct timespec start, now;
+
+	clock_gettime(CLOCK_MONOTONIC, &start);
+
+	while (1) {
+		memset(&params, 0, sizeof(params));
+		if (chamelium_v3_get_video_params(chamelium, port, &params) &&
+		    params.hactive > 0 && params.vactive > 0)
+			return true;
+
+		clock_gettime(CLOCK_MONOTONIC, &now);
+		if (now.tv_sec - start.tv_sec >= timeout_secs)
+			break;
+
+		usleep(250000); /* 250ms poll interval */
+	}
+
+	return false;
+}
+
+/*
+ * Wait for video to report a specific expected resolution.
+ *
+ * After a mode change, Chamelium (especially ITE HDMI ports) may
+ * temporarily report stale video params from the previous mode.
+ * This function polls until the EXPECTED resolution is seen, not
+ * just any non-zero resolution.
+ */
+static bool wait_for_video_resolution(struct igt_chamelium_v3 *chamelium,
+				      chamelium_v3_port_id port,
+				      int expected_w, int expected_h,
+				      int timeout_secs)
+{
+	struct chamelium_v3_video_params params;
+	struct timespec start, now;
+
+	clock_gettime(CLOCK_MONOTONIC, &start);
+
+	while (1) {
+		memset(&params, 0, sizeof(params));
+		if (chamelium_v3_get_video_params(chamelium, port, &params) &&
+		    params.hactive == expected_w &&
+		    params.vactive == expected_h)
+			return true;
+
+		clock_gettime(CLOCK_MONOTONIC, &now);
+		if (now.tv_sec - start.tv_sec >= timeout_secs)
+			break;
+
+		igt_debug("  Waiting for %dx%d, currently %dx%d...\n",
+			  expected_w, expected_h,
+			  params.hactive, params.vactive);
+		usleep(500000); /* 500ms poll for mode transition */
+	}
+
+	igt_warn("Timed out waiting for resolution %dx%d (last seen: %dx%d)\n",
+		 expected_w, expected_h,
+		 params.hactive, params.vactive);
+	return false;
+}
+
+/*
+ * check_mode_v3 - Verify mode timing parameters.
+ *
+ * Retrieves video parameters from Chamelium V3 GetVideoParams RPC
+ * and compares them against the DRM mode info. Uses the same 5% clock
+ * tolerance and exact matching for all timing parameters.
+ *
+ */
+static void check_mode_v3(struct igt_chamelium_v3 *chamelium,
+			  chamelium_v3_port_id port, bool is_dp,
+			  drmModeModeInfo *mode)
+{
+	struct chamelium_v3_video_params params = { 0 };
+	double mode_clock;
+	int expected_hfp, expected_vfp;
+	int expected_hsw, expected_vsw;
+	bool expected_hsp, expected_vsp;
+	bool ok;
+
+	ok = chamelium_v3_get_video_params(chamelium, port, &params);
+	igt_assert_f(ok, "Failed to get video params from Chamelium\n");
+
+	mode_clock = (double)mode->clock / 1000;
+
+	/* Standard timing derivation from DRM mode info */
+	expected_hfp = mode->hsync_start - mode->hdisplay;
+	expected_vfp = mode->vsync_start - mode->vdisplay;
+	expected_hsw = mode->hsync_end - mode->hsync_start;
+	expected_vsw = mode->vsync_end - mode->vsync_start;
+	expected_hsp = !!(mode->flags & DRM_MODE_FLAG_PHSYNC);
+	expected_vsp = !!(mode->flags & DRM_MODE_FLAG_PVSYNC);
+
+	igt_debug("Checking video mode (%s):\n", is_dp ? "DP" : "HDMI");
+	igt_debug("  clock: got %.2f, expected %.2f ± %.1f%%\n",
+		  params.clock, mode_clock, MODE_CLOCK_ACCURACY * 100);
+	igt_debug("  hactive: got %d, expected %d\n",
+		  params.hactive, mode->hdisplay);
+	igt_debug("  vactive: got %d, expected %d\n",
+		  params.vactive, mode->vdisplay);
+	igt_debug("  htotal: got %d, expected %d\n",
+		  params.htotal, mode->htotal);
+	igt_debug("  vtotal: got %d, expected %d\n",
+		  params.vtotal, mode->vtotal);
+	igt_debug("  hfront_porch: got %d, expected %d\n",
+		  params.hfront_porch, expected_hfp);
+	igt_debug("  vfront_porch: got %d, expected %d\n",
+		  params.vfront_porch, expected_vfp);
+	igt_debug("  hsync_width: got %d, expected %d\n",
+		  params.hsync_width, expected_hsw);
+	igt_debug("  vsync_width: got %d, expected %d\n",
+		  params.vsync_width, expected_vsw);
+	igt_debug("  hsync_polarity: got %d, expected %d\n",
+		  params.hsync_polarity, expected_hsp);
+	igt_debug("  vsync_polarity: got %d, expected %d\n",
+		  params.vsync_polarity, expected_vsp);
+
+	/* Clock check with tolerance */
+	if (params.clock > 0) {
+		igt_assert_f(params.clock >
+			     mode_clock * (1 - MODE_CLOCK_ACCURACY),
+			     "Clock too low: %.2f < %.2f\n",
+			     params.clock,
+			     mode_clock * (1 - MODE_CLOCK_ACCURACY));
+		igt_assert_f(params.clock <
+			     mode_clock * (1 + MODE_CLOCK_ACCURACY),
+			     "Clock too high: %.2f > %.2f\n",
+			     params.clock,
+			     mode_clock * (1 + MODE_CLOCK_ACCURACY));
+	}
+
+	/* Resolution must match exactly */
+	igt_assert_eq(params.hactive, mode->hdisplay);
+	igt_assert_eq(params.vactive, mode->vdisplay);
+
+	/* Totals must match */
+	igt_assert_eq(params.htotal, mode->htotal);
+	igt_assert_eq(params.vtotal, mode->vtotal);
+
+	/* Sync widths must match */
+	igt_assert_eq(params.hsync_width, expected_hsw);
+	igt_assert_eq(params.vsync_width, expected_vsw);
+
+	/* Front porch must match (V3 reports standard front porch) */
+	igt_assert_eq(params.hfront_porch, expected_hfp);
+	igt_assert_eq(params.vfront_porch, expected_vfp);
+
+	/* Sync polarity must match */
+	igt_assert_eq(params.hsync_polarity, expected_hsp);
+	igt_assert_eq(params.vsync_polarity, expected_vsp);
+}
+
+/*
+ * Perform a modeset on a connector with the given mode.
+ * Returns the CRTC ID used, or 0 on failure.
+ * Caller must clean up the fb with igt_remove_fb().
+ */
+static uint32_t set_mode_on_connector(int drm_fd, drmModeConnector *conn,
+				      drmModeModeInfo *mode, struct igt_fb *fb)
+{
+	uint32_t crtc_id;
+	int fb_id, ret;
+
+	crtc_id = find_crtc_for_connector(drm_fd, conn);
+	if (!crtc_id) {
+		igt_warn("No CRTC found for connector %d\n",
+			 conn->connector_id);
+		return 0;
+	}
+
+	fb_id = igt_create_color_pattern_fb(drm_fd,
+					    mode->hdisplay, mode->vdisplay,
+					    DRM_FORMAT_XRGB8888,
+					    DRM_FORMAT_MOD_LINEAR,
+					    0, 0, 0, fb);
+	igt_assert_f(fb_id > 0, "Failed to create FB %dx%d\n",
+		     mode->hdisplay, mode->vdisplay);
+
+	ret = drmModeSetCrtc(drm_fd, crtc_id, fb_id, 0, 0,
+			     &conn->connector_id, 1, mode);
+	if (ret) {
+		igt_warn("drmModeSetCrtc failed: %d\n", ret);
+		igt_remove_fb(drm_fd, fb);
+		return 0;
+	}
+
+	return crtc_id;
+}
+
+/**
+ * SUBTEST: dp-edid-read
+ * Description: Make sure the EDID exposed by KMS matches the uploaded EDID
+ *              (tests both base and alt EDIDs, strict byte comparison)
+ *
+ * SUBTEST: hdmi-edid-read
+ * Description: Make sure the EDID exposed by KMS matches the uploaded EDID
+ *              (tests both base and alt EDIDs, strict byte comparison)
+ */
+
+/*
+ * edid_read_one - Upload a single EDID, read back from DRM, assert exact match.
+ *
+ * Ported from V2 igt_custom_edid_type_read():
+ * - Uploads EDID via CreateEdid + ApplyEdid
+ * - Plugs port, waits for connection
+ * - Reads EDID blob from DRM connector property
+ * - Asserts exact byte-for-byte match (igt_assert, not just igt_info)
+ */
+static void edid_read_one(struct edid_test_data *data, bool is_dp,
+			  const struct edid *raw_edid, const char *edid_name)
+{
+	chamelium_v3_port_id port;
+	size_t edid_size;
+	int edid_id;
+	drmModeConnector *connector;
+	unsigned char *read_edid;
+	int read_len;
+
+	port = get_port_for_type(data, is_dp);
+	igt_require_f(port != (chamelium_v3_port_id)-1,
+		      "No %s port found on Chamelium\n", is_dp ? "DP" : "HDMI");
+
+	/* Ensure target port is plugged first (primes DRM connector state) */
+	ensure_port_plugged(data, port);
+
+	/* Get EDID size and upload to Chamelium */
+	edid_size = edid_get_size(raw_edid);
+	igt_info("Testing %s EDID read (%s, %zu bytes)\n",
+		 edid_name, is_dp ? "DP" : "HDMI", edid_size);
+
+	edid_id = chamelium_v3_create_edid(data->chamelium,
+					   (const unsigned char *)raw_edid,
+					   edid_size);
+	igt_info("  Created EDID with ID %d\n", edid_id);
+
+	/* Unplug target port, apply custom EDID, then re-plug */
+	chamelium_v3_unplug(data->chamelium, port);
+	usleep(HPD_WAIT_TIME_MS * 1000);
+
+	chamelium_v3_apply_edid(data->chamelium, port, edid_id);
+	chamelium_v3_plug(data->chamelium, port);
+
+	/* Wait for connector to reappear */
+	usleep(HPD_WAIT_TIME_MS * 1000);
+	igt_assert_f(chamelium_v3_is_plugged(data->chamelium, port),
+		     "Port %d should be plugged\n", port);
+
+	usleep(EDID_SETTLE_TIME_MS * 1000);
+	connector = wait_for_connected_connector(data->drm_fd,
+						 get_drm_connector_type(is_dp));
+	if (!connector) {
+		igt_info("  Connector not found, retrying after extra wait...\n");
+		usleep(HPD_WAIT_TIME_MS * 1000);
+		connector = wait_for_connected_connector(data->drm_fd,
+							 get_drm_connector_type(is_dp));
+	}
+	igt_require_f(connector, "No connected %s connector found\n",
+		      is_dp ? "DP" : "HDMI");
+
+	igt_info("  Found connector: %s-%d\n",
+		 kmstest_connector_type_str(connector->connector_type),
+		 connector->connector_type_id);
+
+	/* Read EDID blob from DRM connector */
+	read_edid = read_edid_from_connector(data->drm_fd, connector, &read_len);
+	igt_assert_f(read_edid, "Failed to read EDID from DRM connector\n");
+	igt_assert_f(read_len >= (int)edid_size,
+		     "Read EDID too short: %d bytes (uploaded %zu)\n",
+		     read_len, edid_size);
+
+	/* Verify EDID header */
+	igt_assert_f(verify_edid_header(read_edid, read_len),
+		     "Invalid EDID header in read-back data\n");
+
+	/*
+	 * STRICT byte-for-byte comparison.
+	 */
+	igt_assert_f(memcmp(read_edid, raw_edid, edid_size) == 0,
+		     "%s EDID mismatch: uploaded %zu bytes != read %d bytes\n",
+		     edid_name, edid_size, read_len);
+	igt_info("  %s EDID matches exactly (%zu bytes)\n", edid_name, edid_size);
+
+	/* Verify connector has modes */
+	igt_assert_f(connector->count_modes > 0,
+		     "Connector has no modes after EDID apply\n");
+	igt_info("  Connector reports %d modes\n", connector->count_modes);
+
+	/* Cleanup */
+	free(read_edid);
+	drmModeFreeConnector(connector);
+	chamelium_v3_unplug(data->chamelium, port);
+	usleep(HPD_WAIT_TIME_MS * 1000);
+}
+
+/*
+ * test_edid_read - Test both BASE and ALT EDIDs.
+ */
+static void test_edid_read(struct edid_test_data *data, bool is_dp)
+{
+	/* Test base EDID (1920x1080) - same */
+	edid_read_one(data, is_dp, igt_kms_get_base_edid(), "BASE");
+
+	/* Test alt EDID (1400x1050) - same */
+	edid_read_one(data, is_dp, igt_kms_get_alt_edid(), "ALT");
+
+	igt_info("  EDID read test PASSED (both BASE and ALT)\n");
+}
+
+/**
+ * SUBTEST: dp-mode-timings
+ * Description: For each mode of the IGT base EDID, perform a modeset and check
+ *              the mode detected by the Chamelium receiver matches the mode we
+ *              set
+ *
+ * SUBTEST: hdmi-mode-timings
+ * Description: For each mode of the IGT base EDID, perform a modeset and check
+ *              the mode detected by the Chamelium receiver matches the mode we
+ *              set
+ */
+static void test_mode_timings(struct edid_test_data *data, bool is_dp)
+{
+	chamelium_v3_port_id port;
+	const struct edid *base_edid;
+	size_t edid_size;
+	int edid_id;
+	drmModeConnector *connector;
+	int i, count_modes;
+	char *port_name;
+
+	port = get_port_for_type(data, is_dp);
+	igt_require_f(port != (chamelium_v3_port_id)-1,
+		      "No %s port found\n", is_dp ? "DP" : "HDMI");
+
+	port_name = chamelium_v3_get_port_name(data->chamelium, port);
+	igt_info("Testing mode timings on %s (port %d)\n", port_name, port);
+
+	/* Prevent fbcon from interfering */
+	kmstest_set_vt_graphics_mode();
+
+	/*
+	 * Reset Chamelium and disable CRTCs for a clean start.
+	 * Reset state before test.
+	 */
+	chamelium_v3_reset(data->chamelium);
+	disable_all_crtcs(data->drm_fd);
+	usleep(HPD_WAIT_TIME_MS * 1000);
+
+	/* Upload base EDID and plug */
+	base_edid = igt_kms_get_base_edid();
+	edid_size = edid_get_size(base_edid);
+
+	edid_id = chamelium_v3_create_edid(data->chamelium,
+					   (const unsigned char *)base_edid,
+					   edid_size);
+
+	chamelium_v3_apply_edid(data->chamelium, port, edid_id);
+	chamelium_v3_plug(data->chamelium, port);
+	usleep(HPD_WAIT_TIME_MS * 1000);
+	usleep(EDID_SETTLE_TIME_MS * 1000);
+
+	connector = wait_for_connected_connector(data->drm_fd,
+						 get_drm_connector_type(is_dp));
+	igt_require_f(connector, "No connected %s connector found\n",
+		      is_dp ? "DP" : "HDMI");
+
+	count_modes = connector->count_modes;
+	igt_assert_f(count_modes > 0, "Connector has no modes\n");
+	igt_info("  Found %d modes to test\n", count_modes);
+
+	/* List all modes for debug */
+	for (i = 0; i < count_modes; i++)
+		igt_debug("  #%d %s %uHz\n", i,
+			  connector->modes[i].name,
+			  connector->modes[i].vrefresh);
+
+	/*
+	 * For each mode: modeset, wait for video stable, check all
+	 * timing parameters. Validate timing parameters.
+	 */
+	for (i = 0; i < count_modes; i++) {
+		drmModeModeInfo *mode = &connector->modes[i];
+		struct igt_fb fb = { 0 };
+		uint32_t crtc_id;
+
+		igt_info("  Testing #%d %s %uHz (%dx%d)\n",
+			 i, mode->name, mode->vrefresh,
+			 mode->hdisplay, mode->vdisplay);
+
+		disable_all_crtcs(data->drm_fd);
+
+		crtc_id = set_mode_on_connector(data->drm_fd, connector,
+						mode, &fb);
+		igt_assert_f(crtc_id,
+			     "Failed to modeset %dx%d@%d\n",
+			     mode->hdisplay, mode->vdisplay,
+			     mode->vrefresh);
+
+		/* Wait for video to stabilize */
+		usleep(MODESET_SETTLE_TIME_MS * 1000);
+		igt_assert_f(
+			wait_for_video_stable(data->chamelium, port,
+					      VIDEO_STABLE_TIMEOUT_S),
+			"Video not stable for mode #%d\n", i);
+
+		/* Full timing validation */
+		check_mode_v3(data->chamelium, port, is_dp, mode);
+		igt_info("    Mode #%d timing check PASSED\n", i);
+
+		/* Cleanup FB for this mode */
+		drmModeSetCrtc(data->drm_fd, crtc_id, 0, 0, 0,
+			       NULL, 0, NULL);
+		igt_remove_fb(data->drm_fd, &fb);
+	}
+
+	drmModeFreeConnector(connector);
+	chamelium_v3_unplug(data->chamelium, port);
+	usleep(HPD_WAIT_TIME_MS * 1000);
+	free(port_name);
+
+	igt_info("  Mode timings test PASSED (%d modes verified)\n",
+		 count_modes);
+}
+
+/**
+ * SUBTEST: dp-edid-resolution-list
+ * Description: Set full EDID with many modes, iterate each mode,
+ *              perform modeset and verify screen resolution matches
+ *             
+ */
+static void test_edid_resolution_list(struct edid_test_data *data)
+{
+	chamelium_v3_port_id port;
+	const struct edid *full_edid;
+	size_t edid_size;
+	int edid_id;
+	drmModeConnector *connector;
+	int i, count_modes;
+	char *port_name;
+
+	port = find_dp_port(data);
+	igt_require_f(port != (chamelium_v3_port_id)-1,
+		      "No DP port found on Chamelium\n");
+
+	port_name = chamelium_v3_get_port_name(data->chamelium, port);
+	igt_info("Testing EDID resolution list on %s (port %d)\n",
+		 port_name, port);
+
+	/* Prevent fbcon from interfering */
+	kmstest_set_vt_graphics_mode();
+
+	/* Reset Chamelium and disable CRTCs for clean start */
+	chamelium_v3_reset(data->chamelium);
+	disable_all_crtcs(data->drm_fd);
+	usleep(HPD_WAIT_TIME_MS * 1000);
+
+	/* Upload full EDID (many modes) */
+	full_edid = igt_kms_get_custom_edid(IGT_CUSTOM_EDID_FULL);
+	edid_size = edid_get_size(full_edid);
+
+	edid_id = chamelium_v3_create_edid(data->chamelium,
+					   (const unsigned char *)full_edid,
+					   edid_size);
+
+	chamelium_v3_apply_edid(data->chamelium, port, edid_id);
+	chamelium_v3_plug(data->chamelium, port);
+	usleep(HPD_WAIT_TIME_MS * 1000);
+	usleep(EDID_SETTLE_TIME_MS * 1000);
+
+	connector = wait_for_connected_connector(data->drm_fd,
+						 DRM_MODE_CONNECTOR_DisplayPort);
+	igt_require_f(connector, "No connected DP connector found\n");
+
+	count_modes = connector->count_modes;
+	igt_assert_f(count_modes > 0, "Full EDID should have many modes\n");
+	igt_info("  Found %d modes from full EDID\n", count_modes);
+
+	for (i = 0; i < count_modes; i++)
+		igt_debug("  #%d %s %uHz\n", i,
+			  connector->modes[i].name,
+			  connector->modes[i].vrefresh);
+
+	/*
+	 * For each mode, set it and verify the screen
+	 * resolution via Chamelium matches the mode we set.
+	 */
+	for (i = 0; i < count_modes; i++) {
+		drmModeModeInfo *mode = &connector->modes[i];
+		struct igt_fb fb = { 0 };
+		struct chamelium_v3_video_params params = { 0 };
+		uint32_t crtc_id;
+
+		igt_info("  Testing #%d %s %uHz\n",
+			 i, mode->name, mode->vrefresh);
+
+		disable_all_crtcs(data->drm_fd);
+
+		crtc_id = set_mode_on_connector(data->drm_fd, connector,
+						mode, &fb);
+		if (!crtc_id) {
+			igt_info("    Skipped (no CRTC)\n");
+			continue;
+		}
+
+		usleep(MODESET_SETTLE_TIME_MS * 1000);
+		igt_assert_f(
+			wait_for_video_stable(data->chamelium, port,
+					      VIDEO_STABLE_TIMEOUT_S),
+			"Video not stable for mode #%d %s\n", i, mode->name);
+
+		chamelium_v3_get_video_params(data->chamelium, port, &params);
+
+		/* Screen resolution must match mode */
+		igt_assert_eq(params.hactive, mode->hdisplay);
+		igt_assert_eq(params.vactive, mode->vdisplay);
+		igt_info("    Resolution verified: %dx%d\n",
+			 params.hactive, params.vactive);
+
+		drmModeSetCrtc(data->drm_fd, crtc_id, 0, 0, 0,
+			       NULL, 0, NULL);
+		igt_remove_fb(data->drm_fd, &fb);
+	}
+
+	drmModeFreeConnector(connector);
+	chamelium_v3_unplug(data->chamelium, port);
+	usleep(HPD_WAIT_TIME_MS * 1000);
+	free(port_name);
+
+	igt_info("  EDID resolution list test PASSED (%d modes)\n",
+		 count_modes);
+}
+
+/*
+ * Convert a hex string EDID (from monitor_edid) to raw bytes.
+ * Caller must free returned buffer.
+ */
+static uint8_t *hex_str_to_edid_bytes(const char *hex_str, size_t *out_size)
+{
+	size_t len = strlen(hex_str);
+	size_t byte_len = len / 2;
+	uint8_t *bytes;
+	size_t i;
+
+	igt_assert_f(len % 2 == 0, "EDID hex string has odd length\n");
+
+	bytes = malloc(byte_len);
+	igt_assert(bytes);
+
+	for (i = 0; i < byte_len; i++) {
+		char hi = hex_str[i * 2];
+		char lo = hex_str[i * 2 + 1];
+		uint8_t b = 0;
+
+		if (hi >= '0' && hi <= '9') b = (hi - '0') << 4;
+		else if (hi >= 'A' && hi <= 'F') b = (hi - 'A' + 10) << 4;
+		else if (hi >= 'a' && hi <= 'f') b = (hi - 'a' + 10) << 4;
+
+		if (lo >= '0' && lo <= '9') b |= (lo - '0');
+		else if (lo >= 'A' && lo <= 'F') b |= (lo - 'A' + 10);
+		else if (lo >= 'a' && lo <= 'f') b |= (lo - 'a' + 10);
+
+		bytes[i] = b;
+	}
+
+	*out_size = byte_len;
+	return bytes;
+}
+
+/*
+ * edid_stress_resolution_v3 - Stress test multiple EDIDs.
+ *
+ * Iterates over a list of real monitor EDIDs (4K or non-4K), uploads each
+ * to Chamelium, plugs the port, performs a modeset with the preferred mode,
+ * and verifies the screen resolution matches via GetVideoParams.
+ */
+static void edid_stress_resolution_v3(struct edid_test_data *data,
+				      bool is_dp, bool is_4k)
+{
+	chamelium_v3_port_id port;
+	monitor_edid *edid_list;
+	size_t edid_count;
+	int i;
+	char *port_name;
+
+	port = get_port_for_type(data, is_dp);
+	igt_require_f(port != (chamelium_v3_port_id)-1,
+		      "No %s port found\n", is_dp ? "DP" : "HDMI");
+
+	port_name = chamelium_v3_get_port_name(data->chamelium, port);
+
+	/* Get the appropriate EDID list */
+	if (is_dp) {
+		if (is_4k) {
+			edid_list = DP_EDIDS_4K;
+			edid_count = DP_EDIDS_4K_COUNT;
+		} else {
+			edid_list = DP_EDIDS_NON_4K;
+			edid_count = DP_EDIDS_NON_4K_COUNT;
+		}
+	} else {
+		if (is_4k) {
+			edid_list = HDMI_EDIDS_4K;
+			edid_count = HDMI_EDIDS_4K_COUNT;
+		} else {
+			edid_list = HDMI_EDIDS_NON_4K;
+			edid_count = HDMI_EDIDS_NON_4K_COUNT;
+		}
+	}
+
+	igt_info("Stress testing %s %s EDIDs on %s (port %d, %zu EDIDs)\n",
+		 is_4k ? "4K" : "non-4K", is_dp ? "DP" : "HDMI",
+		 port_name, port, edid_count);
+
+	/* Prevent fbcon from interfering */
+	kmstest_set_vt_graphics_mode();
+
+	/* Reset for clean start */
+	chamelium_v3_reset(data->chamelium);
+	disable_all_crtcs(data->drm_fd);
+	usleep(HPD_WAIT_TIME_MS * 1000);
+
+	for (i = 0; i < (int)edid_count; i++) {
+		uint8_t *edid_bytes;
+		size_t edid_size;
+		int edid_id;
+		drmModeConnector *connector;
+		struct igt_fb fb = { 0 };
+		uint32_t crtc_id;
+		int exp_w, exp_h;
+
+		igt_info("  [%d/%zu] Testing EDID: %s\n",
+			 i + 1, edid_count, edid_list[i].name);
+
+		/* Convert hex EDID to bytes */
+		edid_bytes = hex_str_to_edid_bytes(edid_list[i].edid, &edid_size);
+
+		/* Upload and apply EDID */
+		edid_id = chamelium_v3_create_edid(data->chamelium,
+						   edid_bytes, edid_size);
+		chamelium_v3_apply_edid(data->chamelium, port, edid_id);
+
+		chamelium_v3_plug(data->chamelium, port);
+		usleep(HPD_WAIT_TIME_MS * 1000);
+		usleep(EDID_SETTLE_TIME_MS * 1000);
+
+		connector = wait_for_connected_connector(data->drm_fd,
+							 get_drm_connector_type(is_dp));
+		if (!connector) {
+			igt_info("    Connector not found, retrying...\n");
+			usleep(HPD_WAIT_TIME_MS * 1000);
+			connector = wait_for_connected_connector(data->drm_fd,
+								 get_drm_connector_type(is_dp));
+		}
+		igt_assert_f(connector,
+			     "No connector for EDID %s\n", edid_list[i].name);
+		igt_assert_f(connector->count_modes > 0,
+			     "No modes for EDID %s\n", edid_list[i].name);
+
+		igt_info("    %d modes, preferred %dx%d@%d\n",
+			 connector->count_modes,
+			 connector->modes[0].hdisplay,
+			 connector->modes[0].vdisplay,
+			 connector->modes[0].vrefresh);
+
+		/* Modeset with preferred mode */
+		disable_all_crtcs(data->drm_fd);
+		crtc_id = set_mode_on_connector(data->drm_fd, connector,
+						&connector->modes[0], &fb);
+		igt_assert_f(crtc_id, "Failed to modeset for EDID %s\n",
+			     edid_list[i].name);
+
+		exp_w = connector->modes[0].hdisplay;
+		exp_h = connector->modes[0].vdisplay;
+
+		/* Verify resolution via Chamelium */
+		igt_assert_f(
+			wait_for_video_resolution(data->chamelium, port,
+						  exp_w, exp_h,
+						  VIDEO_STABLE_TIMEOUT_S + 5),
+			"Resolution %dx%d not seen for EDID %s\n",
+			exp_w, exp_h, edid_list[i].name);
+
+		igt_info("    Resolution verified: %dx%d\n", exp_w, exp_h);
+
+		/* Cleanup */
+		drmModeSetCrtc(data->drm_fd, crtc_id, 0, 0, 0,
+			       NULL, 0, NULL);
+		igt_remove_fb(data->drm_fd, &fb);
+		drmModeFreeConnector(connector);
+
+		disable_all_crtcs(data->drm_fd);
+		chamelium_v3_unplug(data->chamelium, port);
+		usleep(HPD_WAIT_TIME_MS * 1000);
+
+		free(edid_bytes);
+	}
+
+	free(port_name);
+	igt_info("  Stress resolution test PASSED (%zu EDIDs)\n", edid_count);
+}
+
+/*
+ * test_suspend_resume_edid_change_v3 - Suspend/resume EDID change test.
+ * test_suspend_resume_edid_change().
+ *
+ * Simulates a screen being unplugged and another screen being plugged
+ * during suspend/hibernate. Checks that the connector sees the new EDID
+ * after resume.
+ */
+static void test_suspend_resume_edid_change_v3(struct edid_test_data *data,
+					       bool is_dp,
+					       enum igt_suspend_state state,
+					       enum igt_suspend_test test)
+{
+	chamelium_v3_port_id port;
+	const struct edid *base_edid, *alt_edid;
+	size_t base_size, alt_size;
+	int base_edid_id, alt_edid_id;
+	drmModeConnector *connector;
+	struct udev_monitor *mon;
+	char *port_name;
+
+	port = get_port_for_type(data, is_dp);
+	igt_require_f(port != (chamelium_v3_port_id)-1,
+		      "No %s port found\n", is_dp ? "DP" : "HDMI");
+
+	port_name = chamelium_v3_get_port_name(data->chamelium, port);
+	igt_info("Testing EDID change during %s on %s (port %d)\n",
+		 state == SUSPEND_STATE_MEM ? "suspend" : "hibernate",
+		 port_name, port);
+
+	/* Reset for clean start */
+	chamelium_v3_reset(data->chamelium);
+	disable_all_crtcs(data->drm_fd);
+	usleep(HPD_WAIT_TIME_MS * 1000);
+
+	/* Create both EDIDs */
+	base_edid = igt_kms_get_base_edid();
+	alt_edid = igt_kms_get_alt_edid();
+	base_size = edid_get_size(base_edid);
+	alt_size = edid_get_size(alt_edid);
+
+	base_edid_id = chamelium_v3_create_edid(data->chamelium,
+						(const unsigned char *)base_edid,
+						base_size);
+	alt_edid_id = chamelium_v3_create_edid(data->chamelium,
+					       (const unsigned char *)alt_edid,
+					       alt_size);
+
+	/* Start watching for uevents */
+	mon = igt_watch_uevents();
+	igt_flush_uevents(mon);
+
+	/* Plug with base EDID first */
+	chamelium_v3_apply_edid(data->chamelium, port, base_edid_id);
+	chamelium_v3_plug(data->chamelium, port);
+	usleep(HPD_WAIT_TIME_MS * 1000);
+	usleep(EDID_SETTLE_TIME_MS * 1000);
+
+	connector = wait_for_connected_connector(data->drm_fd,
+						 get_drm_connector_type(is_dp));
+	igt_assert_f(connector, "No connector after base EDID plug\n");
+	igt_info("  Base EDID: %d modes, preferred %dx%d\n",
+		 connector->count_modes,
+		 connector->modes[0].hdisplay,
+		 connector->modes[0].vdisplay);
+	drmModeFreeConnector(connector);
+
+	/* Change the EDID to alt (this sets up the new EDID for after resume) */
+	chamelium_v3_apply_edid(data->chamelium, port, alt_edid_id);
+	igt_info("  Applied alt EDID, now suspending...\n");
+
+	igt_flush_uevents(mon);
+
+	/* Suspend/hibernate and auto-resume */
+	igt_system_suspend_autoresume(state, test);
+
+	/* After resume, check for hotplug event.
+	 * S4 (hibernate) may not always generate a hotplug uevent on all
+	 * platforms, so treat it as non-fatal — the replug cycle below
+	 * will definitively verify the EDID change.
+	 */
+	if (igt_hotplug_detected(mon, CHAMELIUM_HOTPLUG_TIMEOUT)) {
+		igt_info("  Hotplug detected after resume from %s\n",
+			 state == SUSPEND_STATE_MEM ? "suspend" : "hibernate");
+	} else {
+		igt_info("  No hotplug after resume from %s (expected for some S4 platforms)\n",
+			 state == SUSPEND_STATE_MEM ? "suspend" : "hibernate");
+	}
+
+	/* Verify connector is still connected after resume */
+	usleep(EDID_SETTLE_TIME_MS * 1000);
+	connector = wait_for_connected_connector(data->drm_fd,
+						 get_drm_connector_type(is_dp));
+	igt_assert_f(connector,
+		     "No connector after resume from %s\n",
+		     state == SUSPEND_STATE_MEM ? "suspend" : "hibernate");
+	igt_info("  Connector still connected after resume: %d modes, preferred %dx%d\n",
+		 connector->count_modes,
+		 connector->modes[0].hdisplay,
+		 connector->modes[0].vdisplay);
+	drmModeFreeConnector(connector);
+
+	/*
+	 * The kernel may not reprobe the EDID on an already-connected port
+	 * after resume. Do an unplug/replug cycle to force the connector
+	 * to re-read the EDID (alt EDID is still applied on the Chamelium).
+	 * This simulates: "screen A unplugged, screen B
+	 * plugged during suspend".
+	 */
+	igt_flush_uevents(mon);
+	chamelium_v3_unplug(data->chamelium, port);
+	usleep(HPD_WAIT_TIME_MS * 1000);
+	chamelium_v3_apply_edid(data->chamelium, port, alt_edid_id);
+	chamelium_v3_plug(data->chamelium, port);
+	usleep(HPD_WAIT_TIME_MS * 1000);
+	usleep(EDID_SETTLE_TIME_MS * 1000);
+
+	igt_assert_f(igt_hotplug_detected(mon, CHAMELIUM_HOTPLUG_TIMEOUT),
+		     "No hotplug event after replug with alt EDID\n");
+
+	connector = wait_for_connected_connector(data->drm_fd,
+						 get_drm_connector_type(is_dp));
+	igt_assert_f(connector,
+		     "No connector after replug with alt EDID\n");
+
+	igt_info("  After replug with alt EDID: %d modes, preferred %dx%d\n",
+		 connector->count_modes,
+		 connector->modes[0].hdisplay,
+		 connector->modes[0].vdisplay);
+
+	/* Alt EDID should give different preferred mode (1400x1050 vs 1920x1080) */
+	igt_assert_f(connector->modes[0].hdisplay == 1400 &&
+		     connector->modes[0].vdisplay == 1050,
+		     "Expected alt EDID preferred mode 1400x1050, got %dx%d\n",
+		     connector->modes[0].hdisplay,
+		     connector->modes[0].vdisplay);
+
+	drmModeFreeConnector(connector);
+
+	/* Cleanup */
+	chamelium_v3_unplug(data->chamelium, port);
+	usleep(HPD_WAIT_TIME_MS * 1000);
+	free(port_name);
+
+	igt_info("  EDID change during %s test PASSED\n",
+		 state == SUSPEND_STATE_MEM ? "suspend" : "hibernate");
+}
+
+static void __real_main(void)
+{
+	struct edid_test_data data = { 0 };
+	bool setup_done = false;
+
+	/* Setup */
+	data.chamelium = chamelium_v3_init_from_config();
+	if (!data.chamelium) {
+		igt_info("Chamelium not available, skipping tests\n");
+		return;
+	}
+
+	data.port_count = chamelium_v3_get_supported_ports(data.chamelium, &data.ports);
+	if (data.port_count <= 0) {
+		igt_info("No Chamelium ports found, skipping tests\n");
+		chamelium_v3_uninit(data.chamelium);
+		return;
+	}
+
+	/* Need master for modesetting in mode-timings and resolution tests */
+	data.drm_fd = drm_open_driver_master(DRIVER_ANY);
+	if (data.drm_fd < 0) {
+		igt_info("Cannot open DRM device as master, skipping tests\n");
+		free(data.ports);
+		chamelium_v3_uninit(data.chamelium);
+		return;
+	}
+
+	setup_done = true;
+	igt_info("Found %d Chamelium ports, DRM fd: %d\n",
+		 data.port_count, data.drm_fd);
+
+
+	igt_describe("Make sure the EDID exposed by KMS is the same as the screen's");
+	igt_subtest("dp-edid-read") {
+		igt_require(setup_done);
+		test_edid_read(&data, true);
+	}
+
+	igt_describe("Stress test the DUT by testing multiple 4K EDIDs, one "
+		     "right after the other, and ensure their validity");
+	igt_subtest("dp-edid-stress-resolution-4k") {
+		igt_require(setup_done);
+		edid_stress_resolution_v3(&data, true, true);
+	}
+
+	igt_describe("Stress test the DUT by testing multiple non-4K EDIDs, one "
+		     "right after the other, and ensure their validity");
+	igt_subtest("dp-edid-stress-resolution-non-4k") {
+		igt_require(setup_done);
+		edid_stress_resolution_v3(&data, true, false);
+	}
+
+	igt_describe("Get an EDID with many modes of different configurations, "
+		     "set them on the screen and check the screen resolution "
+		     "matches the mode resolution");
+	igt_subtest("dp-edid-resolution-list") {
+		igt_require(setup_done);
+		test_edid_resolution_list(&data);
+	}
+
+	igt_describe("Simulate a screen being unplugged and another screen "
+		     "being plugged during suspend");
+	igt_subtest("dp-edid-change-during-suspend") {
+		igt_require(setup_done);
+		test_suspend_resume_edid_change_v3(&data, true,
+						   SUSPEND_STATE_MEM,
+						   SUSPEND_TEST_NONE);
+	}
+
+	igt_describe("Simulate a screen being unplugged and another screen "
+		     "being plugged during hibernation");
+	igt_subtest("dp-edid-change-during-hibernate") {
+		igt_require(setup_done);
+		test_suspend_resume_edid_change_v3(&data, true,
+						   SUSPEND_STATE_DISK,
+						   SUSPEND_TEST_DEVICES);
+	}
+
+	igt_describe("For each mode of the IGT base EDID, perform a modeset "
+		     "and check the mode detected by Chamelium matches");
+	igt_subtest("dp-mode-timings") {
+		igt_require(setup_done);
+		test_mode_timings(&data, true);
+	}
+
+
+	igt_describe("Make sure the EDID exposed by KMS is the same as the screen's");
+	igt_subtest("hdmi-edid-read") {
+		igt_require(setup_done);
+		test_edid_read(&data, false);
+	}
+
+	igt_describe("Stress test the DUT by testing multiple 4K EDIDs, one "
+		     "right after the other, and ensure their validity");
+	igt_subtest("hdmi-edid-stress-resolution-4k") {
+		igt_require(setup_done);
+		edid_stress_resolution_v3(&data, false, true);
+	}
+
+	igt_describe("Stress test the DUT by testing multiple non-4K EDIDs, one "
+		     "right after the other, and ensure their validity");
+	igt_subtest("hdmi-edid-stress-resolution-non-4k") {
+		igt_require(setup_done);
+		edid_stress_resolution_v3(&data, false, false);
+	}
+
+	igt_describe("Simulate a screen being unplugged and another screen "
+		     "being plugged during suspend");
+	igt_subtest("hdmi-edid-change-during-suspend") {
+		igt_require(setup_done);
+		test_suspend_resume_edid_change_v3(&data, false,
+						   SUSPEND_STATE_MEM,
+						   SUSPEND_TEST_NONE);
+	}
+
+	igt_describe("Simulate a screen being unplugged and another screen "
+		     "being plugged during hibernation");
+	igt_subtest("hdmi-edid-change-during-hibernate") {
+		igt_require(setup_done);
+		test_suspend_resume_edid_change_v3(&data, false,
+						   SUSPEND_STATE_DISK,
+						   SUSPEND_TEST_DEVICES);
+	}
+
+	igt_describe("For each mode of the IGT base EDID, perform a modeset "
+		     "and check the mode detected by Chamelium matches");
+	igt_subtest("hdmi-mode-timings") {
+		igt_require(setup_done);
+		test_mode_timings(&data, false);
+	}
+
+	/* Cleanup: re-plug tested ports so connectors are not left disconnected */
+	if (setup_done) {
+		chamelium_v3_port_id dp_port, hdmi_port;
+
+		igt_info("Restoring port state...\n");
+
+		dp_port = find_dp_port(&data);
+		hdmi_port = find_hdmi_port(&data);
+
+		if (dp_port != (chamelium_v3_port_id)-1) {
+			chamelium_v3_apply_edid(data.chamelium, dp_port, 0);
+			chamelium_v3_plug(data.chamelium, dp_port);
+			igt_info("  Re-plugged DP port %d with default EDID\n",
+				 dp_port);
+		}
+		if (hdmi_port != (chamelium_v3_port_id)-1) {
+			chamelium_v3_apply_edid(data.chamelium, hdmi_port, 0);
+			chamelium_v3_plug(data.chamelium, hdmi_port);
+			igt_info("  Re-plugged HDMI port %d with default EDID\n",
+				 hdmi_port);
+		}
+
+		/* Give connectors time to come up */
+		usleep(HPD_WAIT_TIME_MS * 1000);
+
+		free(data.ports);
+		chamelium_v3_uninit(data.chamelium);
+		drm_close_driver(data.drm_fd);
+	}
+}
+
+IGT_TEST_DESCRIPTION("Testing EDID with a Chamelium V3 board");
+int main(int argc, char **argv)
+{
+	igt_subtest_init(argc, argv);
+	__real_main();
+	igt_exit();
+}
diff --git a/tests/meson.build b/tests/meson.build
index 31ca2d963..cfb59ecbb 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -375,6 +375,7 @@ chamelium_v2_progs = [
 chamelium_v3_progs = [
 	'kms_chamelium_v3_basic',
 	'kms_chamelium_v3_hpd',
+	'kms_chamelium_v3_edid',
 ]
 
 test_deps = [ igt_deps ]
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH i-g-t v1 23/25] tests/chamelium/v3: Add frame capture and CRC tests for Chamelium v3
  2026-04-28  4:46 [PATCH i-g-t v1 00/25] Chamelium v3 Integration and Test Execution Mohammed Bilal
                   ` (21 preceding siblings ...)
  2026-04-28  4:46 ` [PATCH i-g-t v1 22/25] tests/chamelium/v3: Add EDID " Mohammed Bilal
@ 2026-04-28  4:46 ` Mohammed Bilal
  2026-04-28  4:46 ` [PATCH i-g-t v1 24/25] tests/chamelium/v3: Add color verification " Mohammed Bilal
                   ` (5 subsequent siblings)
  28 siblings, 0 replies; 37+ messages in thread
From: Mohammed Bilal @ 2026-04-28  4:46 UTC (permalink / raw)
  To: igt-dev; +Cc: kunal1.joshi, Mohammed Bilal

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

This commit adds frame capture and CRC verification tests for Chamelium v3
including frame dump, pixel comparison, and display output verification.

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

diff --git a/tests/chamelium/v3/kms_chamelium_v3_frames.c b/tests/chamelium/v3/kms_chamelium_v3_frames.c
new file mode 100644
index 000000000..2132cf944
--- /dev/null
+++ b/tests/chamelium/v3/kms_chamelium_v3_frames.c
@@ -0,0 +1,1551 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2026 Intel Corporation
+ *
+ * Chamelium V3 Frame Capture / CRC Tests
+ *
+ * Tests frame capture, pixel-by-pixel comparison, format validation,
+ * and plane composition using the Chamelium DumpPixels API.
+ * Category: Display
+ * Description: Frame capture and comparison tests for Chamelium V3
+ * Driver requirement: i915, xe
+ * Mega feature: General Display Features
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "igt.h"
+#include "igt_kms.h"
+#include "igt_infoframe.h"
+#include "chamelium/v3/igt_chamelium.h"
+
+IGT_TEST_DESCRIPTION("Frame capture and comparison tests using Chamelium V3 DumpPixels API");
+
+#define HPD_WAIT_TIME_MS 3000
+
+/* Chamelium V3 port IDs */
+#define FPGA_DP1_PORT 4
+#define FPGA_DP2_PORT 5
+#define ITE_HDMI1_PORT 2
+#define ITE_HDMI2_PORT 3
+
+/*
+ * Tolerance for frame comparison.
+ * Digital connections (DP/HDMI) should be near-exact, but allow
+ * small differences for color quantization and rounding.
+ */
+#define FRAME_CRC_TOLERANCE 2
+#define FRAME_DUMP_TOLERANCE 0
+#define FRAME_CHECKERBOARD_TOLERANCE 10
+#define FRAME_FORMAT_TOLERANCE 10
+
+/* Maps Video Identification Codes to a mode */
+struct vic_mode {
+	int hactive, vactive;
+	int vrefresh; /* Hz */
+	uint32_t picture_ar;
+};
+
+static const struct vic_mode vic_modes[] = {
+	[16] = {
+		.hactive = 1920,
+		.vactive = 1080,
+		.vrefresh = 60,
+		.picture_ar = DRM_MODE_PICTURE_ASPECT_16_9,
+	},
+};
+
+struct frames_test_data {
+	/* Chamelium V3 */
+	struct igt_chamelium_v3 *chamelium;
+	chamelium_v3_port_id *ports;
+	int port_count;
+
+	/* DRM display */
+	int drm_fd;
+	igt_display_t display;
+};
+
+
+/* Find the first DP port, preferring FPGA ports */
+static chamelium_v3_port_id find_dp_port(struct frames_test_data *data)
+{
+	int i;
+
+	/* Prefer FPGA DP ports first */
+	for (i = 0; i < data->port_count; i++) {
+		chamelium_v3_port_id port = data->ports[i];
+
+		if (chamelium_v3_port_is_dp(data->chamelium, port) &&
+		    chamelium_v3_is_plugged(data->chamelium, port) &&
+		    (port == FPGA_DP1_PORT || port == FPGA_DP2_PORT))
+			return port;
+	}
+	/* Fallback to any DP port */
+	for (i = 0; i < data->port_count; i++) {
+		chamelium_v3_port_id port = data->ports[i];
+
+		if (chamelium_v3_port_is_dp(data->chamelium, port) &&
+		    chamelium_v3_is_plugged(data->chamelium, port))
+			return port;
+	}
+	return (chamelium_v3_port_id)-1;
+}
+
+/* Find the first HDMI port */
+static chamelium_v3_port_id find_hdmi_port(struct frames_test_data *data)
+{
+	int i;
+
+	for (i = 0; i < data->port_count; i++) {
+		chamelium_v3_port_id port = data->ports[i];
+
+		if (chamelium_v3_port_is_hdmi(data->chamelium, port) &&
+		    chamelium_v3_is_plugged(data->chamelium, port))
+			return port;
+	}
+	return (chamelium_v3_port_id)-1;
+}
+
+
+/*
+ * Find a DRM output matching the Chamelium port type.
+ * For HDMI ports, look for HDMI-A connectors; for DP ports, look for DP connectors.
+ */
+static igt_output_t *find_output_for_port(struct frames_test_data *data,
+					  chamelium_v3_port_id port_id,
+					  bool is_hdmi)
+{
+	igt_output_t *output;
+	igt_crtc_t *crtc;
+
+	for_each_crtc(&data->display, crtc) {
+		for_each_valid_output_on_crtc(&data->display, crtc, output) {
+			uint32_t conn_type = output->config.connector->connector_type;
+			bool is_match = false;
+
+			if (is_hdmi)
+				is_match = (conn_type == DRM_MODE_CONNECTOR_HDMIA ||
+					    conn_type == DRM_MODE_CONNECTOR_HDMIB);
+			else
+				is_match = (conn_type == DRM_MODE_CONNECTOR_DisplayPort);
+
+			if (is_match)
+				return output;
+		}
+	}
+
+	return NULL;
+}
+
+/*
+ * Set up display: attach output to a pipe, get primary plane, set mode.
+ * Returns the primary plane, or NULL on failure.
+ */
+static igt_plane_t *setup_output(struct frames_test_data *data,
+				 igt_output_t *output,
+				 drmModeModeInfo *mode)
+{
+	igt_crtc_t *pipe_obj;
+	igt_plane_t *primary;
+	igt_crtc_t *crtc;
+
+	for_each_crtc(&data->display, crtc) {
+		if (igt_crtc_connector_valid(crtc, output)) {
+			pipe_obj = crtc;
+			primary = igt_crtc_get_plane_type(pipe_obj, DRM_PLANE_TYPE_PRIMARY);
+			if (primary) {
+				igt_output_set_crtc(output, pipe_obj);
+				igt_output_override_mode(output, mode);
+				return primary;
+			}
+		}
+	}
+
+	return NULL;
+}
+
+/*
+ * Enable output with the given framebuffer.
+ * Commits the modeset atomically and waits for signal stabilization.
+ */
+static void enable_output(struct frames_test_data *data,
+			  igt_output_t *output,
+			  igt_plane_t *primary,
+			  struct igt_fb *fb)
+{
+	igt_plane_set_fb(primary, fb);
+	igt_display_commit2(&data->display, COMMIT_ATOMIC);
+
+	/* Wait for the display signal to stabilize on the Chamelium */
+	sleep(5);
+}
+
+/*
+ * Disable output and clean up.
+ */
+static void disable_output(struct frames_test_data *data,
+			   igt_output_t *output,
+			   igt_plane_t *primary)
+{
+	igt_plane_set_fb(primary, NULL);
+	igt_output_set_crtc(output, NULL);
+	igt_display_commit2(&data->display, COMMIT_ATOMIC);
+}
+
+
+/*
+ * Compare a captured V3 frame (RGB, 3 bytes per pixel) against a local
+ * framebuffer (XRGB8888, 4 bytes per pixel in BGRA memory order).
+ *
+ * Returns true if frames match within tolerance.
+ */
+static bool compare_frame_to_fb(struct frames_test_data *data,
+				struct chamelium_v3_frame *frame,
+				struct igt_fb *fb,
+				int tolerance)
+{
+	cairo_t *cr;
+	cairo_surface_t *surface;
+	unsigned char *fb_data;
+	int stride;
+	int x, y;
+	int mismatches = 0;
+	int total_pixels = frame->width * frame->height;
+	/* Allow up to 2% mismatch for edge effects and format conversion */
+	int max_mismatches = total_pixels * 2 / 100;
+
+	cr = igt_get_cairo_ctx(data->drm_fd, fb);
+	surface = cairo_get_target(cr);
+	cairo_surface_flush(surface);
+	fb_data = cairo_image_surface_get_data(surface);
+	stride = cairo_image_surface_get_stride(surface);
+
+	igt_debug("Comparing frame %dx%d against framebuffer (stride=%d, tolerance=%d)...\n",
+		  frame->width, frame->height, stride, tolerance);
+
+	for (y = 0; y < frame->height && mismatches <= max_mismatches; y++) {
+		for (x = 0; x < frame->width && mismatches <= max_mismatches; x++) {
+			int frame_idx = (y * frame->width + x) * 3;
+			uint8_t cap_r = frame->data[frame_idx + 0];
+			uint8_t cap_g = frame->data[frame_idx + 1];
+			uint8_t cap_b = frame->data[frame_idx + 2];
+
+			int fb_idx = y * stride + x * 4;
+			uint8_t fb_b = fb_data[fb_idx + 0];
+			uint8_t fb_g = fb_data[fb_idx + 1];
+			uint8_t fb_r = fb_data[fb_idx + 2];
+
+			int diff_r = abs((int)cap_r - (int)fb_r);
+			int diff_g = abs((int)cap_g - (int)fb_g);
+			int diff_b = abs((int)cap_b - (int)fb_b);
+
+			if (diff_r > tolerance || diff_g > tolerance || diff_b > tolerance) {
+				mismatches++;
+				if (mismatches <= 10)
+					igt_debug("Mismatch at (%d,%d): captured RGB(%d,%d,%d) vs expected RGB(%d,%d,%d)\n",
+						  x, y, cap_r, cap_g, cap_b, fb_r, fb_g, fb_b);
+			}
+		}
+	}
+
+	igt_put_cairo_ctx(cr);
+
+	igt_info("Frame comparison: %d mismatches out of %d pixels (%.2f%%)\n",
+		 mismatches, total_pixels, 100.0 * mismatches / total_pixels);
+
+	return mismatches <= max_mismatches;
+}
+
+/*
+ * Strict pixel-exact comparison for frame dump tests.
+ * Returns true only if every pixel matches exactly.
+ */
+static bool compare_frame_to_fb_exact(struct frames_test_data *data,
+				      struct chamelium_v3_frame *frame,
+				      struct igt_fb *fb)
+{
+	cairo_t *cr;
+	cairo_surface_t *surface;
+	unsigned char *fb_data;
+	int stride;
+	int x, y;
+	int mismatches = 0;
+
+	cr = igt_get_cairo_ctx(data->drm_fd, fb);
+	surface = cairo_get_target(cr);
+	cairo_surface_flush(surface);
+	fb_data = cairo_image_surface_get_data(surface);
+	stride = cairo_image_surface_get_stride(surface);
+
+	igt_debug("Exact frame comparison %dx%d...\n", frame->width, frame->height);
+
+	for (y = 0; y < frame->height; y++) {
+		for (x = 0; x < frame->width; x++) {
+			int frame_idx = (y * frame->width + x) * 3;
+			uint8_t cap_r = frame->data[frame_idx + 0];
+			uint8_t cap_g = frame->data[frame_idx + 1];
+			uint8_t cap_b = frame->data[frame_idx + 2];
+
+			int fb_idx = y * stride + x * 4;
+			uint8_t fb_b = fb_data[fb_idx + 0];
+			uint8_t fb_g = fb_data[fb_idx + 1];
+			uint8_t fb_r = fb_data[fb_idx + 2];
+
+			if (cap_r != fb_r || cap_g != fb_g || cap_b != fb_b) {
+				mismatches++;
+				if (mismatches <= 10)
+					igt_debug("Mismatch at (%d,%d): captured RGB(%d,%d,%d) vs expected RGB(%d,%d,%d)\n",
+						  x, y, cap_r, cap_g, cap_b, fb_r, fb_g, fb_b);
+			}
+		}
+	}
+
+	igt_put_cairo_ctx(cr);
+
+	if (mismatches > 0) {
+		int total = frame->width * frame->height;
+
+		igt_info("Exact comparison FAILED: %d mismatches out of %d pixels (%.2f%%)\n",
+			 mismatches, total, 100.0 * mismatches / total);
+	} else {
+		igt_info("Exact comparison PASSED: all pixels match\n");
+	}
+
+	return mismatches == 0;
+}
+
+#define CAPTURE_MAX_RETRIES 5
+#define CAPTURE_RETRY_DELAY_S 5
+
+/*
+ * Capture a frame with retry logic.
+ * DumpPixels can fail with V4L.Timeout if the signal hasn't stabilized
+ * after a mode switch. We retry with increasing delay.
+ */
+static struct chamelium_v3_frame *capture_frame_with_retry(
+	struct frames_test_data *data,
+	chamelium_v3_port_id port_id)
+{
+	struct chamelium_v3_frame *frame;
+	int retry;
+
+	for (retry = 0; retry < CAPTURE_MAX_RETRIES; retry++) {
+		frame = chamelium_v3_capture_frame(data->chamelium, port_id);
+		if (frame)
+			return frame;
+
+		igt_warn("Frame capture attempt %d/%d failed, retrying in %ds...\n",
+			 retry + 1, CAPTURE_MAX_RETRIES,
+			 CAPTURE_RETRY_DELAY_S + retry);
+		sleep(CAPTURE_RETRY_DELAY_S + retry);
+	}
+
+	return NULL;
+}
+
+/*
+ * Capture a frame from Chamelium and compare against the given framebuffer.
+ * Repeats capture `count` times and verifies all captures match.
+ *
+ * Returns true on success.  When skip_on_capture_fail is set, a
+ * DumpPixels timeout (after all retries) returns false instead of
+ * asserting, so the caller can skip the problematic mode.
+ */
+static bool do_test_display(struct frames_test_data *data,
+			    chamelium_v3_port_id port_id,
+			    igt_output_t *output,
+			    igt_plane_t *primary,
+			    drmModeModeInfo *mode,
+			    uint32_t fourcc,
+			    int tolerance,
+			    int count,
+			    bool skip_on_capture_fail)
+{
+	struct igt_fb pattern_fb, frame_fb;
+	struct chamelium_v3_frame *frame;
+	int i, fb_id, frame_id;
+	bool success = true;
+
+	/* Create a pattern framebuffer in XRGB8888 */
+	fb_id = igt_create_color_pattern_fb(data->drm_fd,
+					    mode->hdisplay, mode->vdisplay,
+					    DRM_FORMAT_XRGB8888,
+					    DRM_FORMAT_MOD_LINEAR,
+					    0, 0, 0, &pattern_fb);
+	igt_assert(fb_id > 0);
+
+	/* Convert to the requested format if different */
+	if (fourcc != DRM_FORMAT_XRGB8888) {
+		frame_id = igt_fb_convert(&frame_fb, &pattern_fb, fourcc,
+					  DRM_FORMAT_MOD_LINEAR);
+		igt_assert(frame_id > 0);
+	} else {
+		frame_fb = pattern_fb;
+	}
+
+	/* Enable output with the display framebuffer */
+	enable_output(data, output, primary, &frame_fb);
+
+	igt_info("Capturing %d frame(s) at %dx%d for format %s...\n",
+		 count, mode->hdisplay, mode->vdisplay, igt_format_str(fourcc));
+
+	for (i = 0; i < count; i++) {
+		frame = capture_frame_with_retry(data, port_id);
+		if (!frame) {
+			if (skip_on_capture_fail) {
+				igt_warn("Frame capture failed for %dx%d@%d – skipping mode (V4L timeout)\n",
+					 mode->hdisplay, mode->vdisplay,
+					 mode->vrefresh);
+				success = false;
+				goto cleanup;
+			}
+			igt_assert_f(frame, "Failed to capture frame %d/%d\n",
+				     i + 1, count);
+		}
+
+		igt_assert_f(frame->width == mode->hdisplay &&
+			     frame->height == mode->vdisplay,
+			     "Frame size mismatch: captured %dx%d, expected %dx%d\n",
+			     frame->width, frame->height,
+			     mode->hdisplay, mode->vdisplay);
+
+		/* Compare against the original XRGB8888 pattern */
+		igt_assert_f(compare_frame_to_fb(data, frame, &pattern_fb, tolerance),
+			     "Frame %d/%d comparison failed\n", i + 1, count);
+
+		chamelium_v3_free_frame(frame);
+	}
+
+cleanup:
+	if (fourcc != DRM_FORMAT_XRGB8888)
+		igt_remove_fb(data->drm_fd, &frame_fb);
+	igt_remove_fb(data->drm_fd, &pattern_fb);
+	return success;
+}
+
+/*
+ * Frame dump: capture and do exact pixel-by-pixel comparison.
+ * Frame dump comparison test.
+ */
+static void do_test_frame_dump(struct frames_test_data *data,
+			       chamelium_v3_port_id port_id,
+			       igt_output_t *output,
+			       igt_plane_t *primary,
+			       drmModeModeInfo *mode,
+			       bool is_hdmi)
+{
+	struct igt_fb fb;
+	struct chamelium_v3_frame *frame;
+	int fb_id, j;
+	int dump_count = 5;
+
+	fb_id = igt_create_color_pattern_fb(data->drm_fd,
+					    mode->hdisplay, mode->vdisplay,
+					    DRM_FORMAT_XRGB8888,
+					    DRM_FORMAT_MOD_LINEAR,
+					    0, 0, 0, &fb);
+	igt_assert(fb_id > 0);
+
+	enable_output(data, output, primary, &fb);
+
+	igt_info("Dumping %d frames at %dx%d (%s)...\n",
+		 dump_count, mode->hdisplay, mode->vdisplay,
+		 is_hdmi ? "HDMI-tolerant" : "DP-exact");
+
+	for (j = 0; j < dump_count; j++) {
+		frame = capture_frame_with_retry(data, port_id);
+		igt_assert_f(frame, "Failed to capture frame dump %d/%d\n",
+			     j + 1, dump_count);
+
+		igt_assert_f(frame->width == mode->hdisplay &&
+			     frame->height == mode->vdisplay,
+			     "Frame size mismatch: captured %dx%d, expected %dx%d\n",
+			     frame->width, frame->height,
+			     mode->hdisplay, mode->vdisplay);
+
+		/*
+		 * DP is pixel-exact since it's purely digital.
+		 * HDMI may have color space conversion artifacts at
+		 * certain modes, so use tolerant comparison.
+		 */
+		if (is_hdmi)
+			igt_assert_f(compare_frame_to_fb(data, frame, &fb,
+							 FRAME_CRC_TOLERANCE),
+				     "Frame dump %d/%d comparison failed\n",
+				     j + 1, dump_count);
+		else
+			igt_assert_f(compare_frame_to_fb_exact(data, frame, &fb),
+				     "Frame dump %d/%d pixel comparison failed\n",
+				     j + 1, dump_count);
+
+		chamelium_v3_free_frame(frame);
+	}
+
+	igt_remove_fb(data->drm_fd, &fb);
+}
+
+
+static void randomize_plane_stride(struct frames_test_data *data,
+				   uint32_t width, uint32_t height,
+				   uint32_t format, uint64_t modifier,
+				   size_t *stride)
+{
+	size_t stride_min;
+	uint32_t max_tile_w = 4, tile_w, tile_h;
+	int i;
+	struct igt_fb dummy;
+
+	stride_min = width * igt_format_plane_bpp(format, 0) / 8;
+
+	/* Randomize the stride to less than twice the minimum */
+	*stride = (rand() % stride_min) + stride_min;
+
+	/* Determine max tile width for alignment */
+	igt_create_fb(data->drm_fd, 64, 64, format, modifier, &dummy);
+	for (i = 0; i < dummy.num_planes; i++) {
+		igt_get_fb_tile_size(data->drm_fd, modifier,
+				     dummy.plane_bpp[i], &tile_w, &tile_h);
+		if (tile_w > max_tile_w)
+			max_tile_w = tile_w;
+	}
+	igt_remove_fb(data->drm_fd, &dummy);
+
+	/* Align to both 32-bit and tile width */
+	*stride = ALIGN(*stride, max_tile_w);
+}
+
+static void randomize_plane_setup(struct frames_test_data *data,
+				  igt_plane_t *plane,
+				  drmModeModeInfo *mode,
+				  uint32_t *width, uint32_t *height,
+				  uint32_t *format, uint64_t *modifier,
+				  bool allow_yuv)
+{
+	int min_dim;
+	uint32_t idx[plane->format_mod_count];
+	unsigned int count = 0;
+	unsigned int i;
+
+	/* Count supported formats */
+	for (i = 0; i < plane->format_mod_count; i++)
+		if (igt_fb_supported_format(plane->formats[i]) &&
+		    (allow_yuv || !igt_format_is_yuv(plane->formats[i])))
+			idx[count++] = i;
+
+	igt_assert(count > 0);
+
+	i = idx[rand() % count];
+	*format = plane->formats[i];
+	*modifier = plane->modifiers[i];
+
+	/* Use minimum dimension of 16 for YUV, 8 for others */
+	min_dim = igt_format_is_yuv(*format) ? 16 : 8;
+
+	*width = max((rand() % mode->hdisplay) + 1, 2 * min_dim);
+	*height = max((rand() % mode->vdisplay) + 1, 2 * min_dim);
+}
+
+static void configure_plane(igt_plane_t *plane,
+			    uint32_t src_w, uint32_t src_h,
+			    uint32_t src_x, uint32_t src_y,
+			    uint32_t crtc_w, uint32_t crtc_h,
+			    int32_t crtc_x, int32_t crtc_y,
+			    struct igt_fb *fb)
+{
+	igt_plane_set_fb(plane, fb);
+	igt_plane_set_position(plane, crtc_x, crtc_y);
+	igt_plane_set_size(plane, crtc_w, crtc_h);
+	igt_fb_set_position(fb, plane, src_x, src_y);
+	igt_fb_set_size(fb, plane, src_w, src_h);
+}
+
+static void randomize_plane_coordinates(struct frames_test_data *data,
+					igt_plane_t *plane,
+					drmModeModeInfo *mode,
+					struct igt_fb *fb,
+					uint32_t *src_w, uint32_t *src_h,
+					uint32_t *src_x, uint32_t *src_y,
+					uint32_t *crtc_w, uint32_t *crtc_h,
+					int32_t *crtc_x, int32_t *crtc_y,
+					bool allow_scaling)
+{
+	bool is_yuv = igt_format_is_yuv(fb->drm_format);
+	uint32_t width = fb->width, height = fb->height;
+	double ratio;
+	int ret;
+
+	/* Randomize source offset in the first half */
+	*src_x = rand() % (width / 2);
+	*src_y = rand() % (height / 2);
+
+	*src_w = width - *src_x;
+	*src_h = height - *src_y;
+
+	if (allow_scaling) {
+		*crtc_w = (rand() % mode->hdisplay) + 1;
+		*crtc_h = (rand() % mode->vdisplay) + 1;
+
+		/* Limit scaling ratios */
+		ratio = ((double)*crtc_w / *src_w);
+		if (ratio < 0.5)
+			*src_w = *crtc_w * 2;
+		else if (ratio > 0.8 && ratio < 1.2)
+			*crtc_w = *src_w;
+		else if (ratio > 3.0)
+			*crtc_w = *src_w * 3;
+
+		ratio = ((double)*crtc_h / *src_h);
+		if (ratio < 0.5)
+			*src_h = *crtc_h * 2;
+		else if (ratio > 0.8 && ratio < 1.2)
+			*crtc_h = *src_h;
+		else if (ratio > 3.0)
+			*crtc_h = *src_h * 3;
+	} else {
+		*crtc_w = *src_w;
+		*crtc_h = *src_h;
+	}
+
+	if (*crtc_w != *src_w || *crtc_h != *src_h) {
+		if (*crtc_w < mode->hdisplay)
+			*crtc_x = rand() % (mode->hdisplay - *crtc_w);
+		else
+			*crtc_x = 0;
+
+		if (*crtc_h < mode->vdisplay)
+			*crtc_y = rand() % (mode->vdisplay - *crtc_h);
+		else
+			*crtc_y = 0;
+	} else {
+		*crtc_x = (rand() % mode->hdisplay) - *crtc_w / 2;
+		*crtc_y = (rand() % mode->vdisplay) - *crtc_h / 2;
+	}
+
+	configure_plane(plane, *src_w, *src_h, *src_x, *src_y,
+			*crtc_w, *crtc_h, *crtc_x, *crtc_y, fb);
+	ret = igt_display_try_commit_atomic(
+		&data->display,
+		DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_ALLOW_MODESET,
+		NULL);
+	if (!ret)
+		return;
+
+	igt_assert_f(ret != -ENOSPC,
+		     "Invalid coordinates on a %ux%u fb\n", width, height);
+
+	/* YUV alignment fixup */
+	if (is_yuv) {
+		*src_x &= ~1;
+		*src_y &= ~1;
+		*src_w &= ~1;
+		*src_h &= ~1;
+		*crtc_w &= ~1;
+		*crtc_h &= ~1;
+
+		if (*crtc_x < 0 && (*crtc_x & 1))
+			(*crtc_x)++;
+		else
+			*crtc_x &= ~1;
+
+		if (*crtc_y < 0 && (*crtc_y & 1))
+			(*crtc_y)++;
+		else
+			*crtc_y &= ~1;
+
+		configure_plane(plane, *src_w, *src_h, *src_x, *src_y,
+				*crtc_w, *crtc_h, *crtc_x, *crtc_y, fb);
+		ret = igt_display_try_commit_atomic(
+			&data->display,
+			DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_ALLOW_MODESET,
+			NULL);
+		if (!ret)
+			return;
+	}
+
+	igt_assert(!ret || allow_scaling);
+	igt_info("Scaling ratio %g / %g failed, trying without scaling.\n",
+		 ((double)*crtc_w / *src_w), ((double)*crtc_h / *src_h));
+
+	*crtc_w = *src_w;
+	*crtc_h = *src_h;
+
+	configure_plane(plane, *src_w, *src_h, *src_x, *src_y,
+			*crtc_w, *crtc_h, *crtc_x, *crtc_y, fb);
+	igt_display_commit_atomic(&data->display,
+				  DRM_MODE_ATOMIC_TEST_ONLY |
+				  DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
+}
+
+static void blit_plane_cairo(struct frames_test_data *data,
+			     cairo_surface_t *result,
+			     uint32_t src_w, uint32_t src_h,
+			     uint32_t src_x, uint32_t src_y,
+			     uint32_t crtc_w, uint32_t crtc_h,
+			     int32_t crtc_x, int32_t crtc_y,
+			     struct igt_fb *fb)
+{
+	cairo_surface_t *surface;
+	cairo_surface_t *clipped_surface;
+	cairo_t *cr;
+
+	surface = igt_get_cairo_surface(data->drm_fd, fb);
+
+	if (src_x || src_y) {
+		clipped_surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24,
+							     src_w, src_h);
+		cr = cairo_create(clipped_surface);
+		cairo_translate(cr, -1. * src_x, -1. * src_y);
+		cairo_set_source_surface(cr, surface, 0, 0);
+		cairo_paint(cr);
+		cairo_surface_flush(clipped_surface);
+		cairo_destroy(cr);
+	} else {
+		clipped_surface = surface;
+	}
+
+	cr = cairo_create(result);
+	cairo_translate(cr, crtc_x, crtc_y);
+
+	if (src_w != crtc_w || src_h != crtc_h)
+		cairo_scale(cr, (double)crtc_w / src_w, (double)crtc_h / src_h);
+
+	cairo_set_source_surface(cr, clipped_surface, 0, 0);
+	cairo_surface_destroy(clipped_surface);
+
+	if (src_w != crtc_w || src_h != crtc_h) {
+		cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_BILINEAR);
+		cairo_pattern_set_extend(cairo_get_source(cr), CAIRO_EXTEND_NONE);
+	}
+
+	cairo_paint(cr);
+	cairo_surface_flush(result);
+	cairo_destroy(cr);
+}
+
+static void prepare_randomized_plane(struct frames_test_data *data,
+				     drmModeModeInfo *mode,
+				     igt_plane_t *plane,
+				     struct igt_fb *overlay_fb,
+				     unsigned int index,
+				     cairo_surface_t *result_surface,
+				     bool allow_scaling,
+				     bool allow_yuv)
+{
+	struct igt_fb pattern_fb;
+	uint32_t overlay_fb_w, overlay_fb_h;
+	uint32_t overlay_src_w, overlay_src_h;
+	uint32_t overlay_src_x, overlay_src_y;
+	int32_t overlay_crtc_x, overlay_crtc_y;
+	uint32_t overlay_crtc_w, overlay_crtc_h;
+	uint32_t format;
+	uint64_t modifier;
+	size_t stride;
+	bool tiled;
+	int fb_id;
+
+	randomize_plane_setup(data, plane, mode, &overlay_fb_w, &overlay_fb_h,
+			      &format, &modifier, allow_yuv);
+
+	tiled = (modifier != DRM_FORMAT_MOD_LINEAR);
+	igt_debug("Plane %d: framebuffer size %dx%d %s format (%s)\n", index,
+		  overlay_fb_w, overlay_fb_h, igt_format_str(format),
+		  tiled ? "tiled" : "linear");
+
+	/* Get a pattern framebuffer for the overlay plane */
+	fb_id = igt_create_color_pattern_fb(data->drm_fd,
+					    overlay_fb_w, overlay_fb_h,
+					    DRM_FORMAT_XRGB8888,
+					    DRM_FORMAT_MOD_LINEAR,
+					    0, 0, 0, &pattern_fb);
+	igt_assert(fb_id > 0);
+
+	randomize_plane_stride(data, overlay_fb_w, overlay_fb_h, format,
+			       modifier, &stride);
+
+	igt_debug("Plane %d: stride %ld\n", index, stride);
+
+	fb_id = igt_fb_convert_with_stride(overlay_fb, &pattern_fb, format,
+					   modifier, stride);
+	igt_assert(fb_id > 0);
+
+	randomize_plane_coordinates(data, plane, mode, overlay_fb,
+				    &overlay_src_w, &overlay_src_h,
+				    &overlay_src_x, &overlay_src_y,
+				    &overlay_crtc_w, &overlay_crtc_h,
+				    &overlay_crtc_x, &overlay_crtc_y,
+				    allow_scaling);
+
+	igt_debug("Plane %d: in-framebuffer size %dx%d\n", index,
+		  overlay_src_w, overlay_src_h);
+	igt_debug("Plane %d: in-framebuffer position %dx%d\n", index,
+		  overlay_src_x, overlay_src_y);
+	igt_debug("Plane %d: on-crtc size %dx%d\n", index,
+		  overlay_crtc_w, overlay_crtc_h);
+	igt_debug("Plane %d: on-crtc position %dx%d\n", index,
+		  overlay_crtc_x, overlay_crtc_y);
+
+	blit_plane_cairo(data, result_surface,
+			 overlay_src_w, overlay_src_h,
+			 overlay_src_x, overlay_src_y,
+			 overlay_crtc_w, overlay_crtc_h,
+			 overlay_crtc_x, overlay_crtc_y,
+			 &pattern_fb);
+
+	igt_remove_fb(data->drm_fd, &pattern_fb);
+}
+
+
+/*
+ * Display a test pattern on the first mode and capture/compare frames.
+ * Display one mode test.
+ */
+static void test_display_one_mode(struct frames_test_data *data,
+				  chamelium_v3_port_id port_id,
+				  bool is_hdmi,
+				  uint32_t fourcc,
+				  int tolerance,
+				  int count)
+__attribute__((unused));
+static void test_display_one_mode(struct frames_test_data *data,
+				  chamelium_v3_port_id port_id,
+				  bool is_hdmi,
+				  uint32_t fourcc,
+				  int tolerance,
+				  int count)
+{
+	igt_output_t *output;
+	igt_plane_t *primary;
+	drmModeModeInfo *mode;
+
+	igt_modeset_disable_all_outputs(&data->display);
+
+	output = find_output_for_port(data, port_id, is_hdmi);
+	igt_require_f(output, "No %s output found on DUT\n",
+		      is_hdmi ? "HDMI" : "DP");
+
+	mode = igt_output_get_mode(output);
+	primary = setup_output(data, output, mode);
+	igt_assert(primary);
+
+	igt_require(igt_plane_has_format_mod(primary, fourcc,
+					     DRM_FORMAT_MOD_LINEAR));
+
+	igt_info("Testing first mode: %dx%d@%d\n",
+		 mode->hdisplay, mode->vdisplay, mode->vrefresh);
+
+	do_test_display(data, port_id, output, primary, mode,
+			fourcc, tolerance, count, false);
+
+	disable_output(data, output, primary);
+}
+
+
+/*
+ * Iterate all modes, display a test pattern and capture/compare frames.
+ * Display all modes test.
+ */
+static void test_display_all_modes(struct frames_test_data *data,
+				   chamelium_v3_port_id port_id,
+				   bool is_hdmi,
+				   uint32_t fourcc,
+				   int tolerance,
+				   int count)
+{
+	igt_output_t *output;
+	igt_plane_t *primary;
+	drmModeConnector *connector;
+	int i, count_modes;
+	int tested = 0, skipped = 0;
+
+	output = find_output_for_port(data, port_id, is_hdmi);
+	igt_require_f(output, "No %s output found on DUT\n",
+		      is_hdmi ? "HDMI" : "DP");
+
+	connector = output->config.connector;
+	igt_require_f(connector->count_modes > 0, "No modes available\n");
+
+	igt_info("Testing modes for %s...\n", output->name);
+
+	i = 0;
+	do {
+		drmModeModeInfo local_mode;
+		bool ok;
+
+		/*
+		 * Modes may change due to mode pruning and link issues,
+		 * so refresh the connector each iteration.
+		 * igt_modeset_disable_all_outputs and igt_display_commit2
+		 * may reprobe the connector, invalidating mode pointers.
+		 */
+		igt_modeset_disable_all_outputs(&data->display);
+
+		output = find_output_for_port(data, port_id, is_hdmi);
+		igt_assert(output);
+
+		connector = output->config.connector;
+		count_modes = connector->count_modes;
+		if (i >= count_modes)
+			break;
+
+		/* Copy mode to stack to avoid stale pointer issues */
+		local_mode = connector->modes[i];
+
+		igt_info("Mode %d/%d: %dx%d@%d\n", i + 1,
+			 count_modes,
+			 local_mode.hdisplay, local_mode.vdisplay,
+			 local_mode.vrefresh);
+
+		primary = setup_output(data, output, &local_mode);
+		igt_assert(primary);
+
+		igt_require(igt_plane_has_format_mod(primary, fourcc,
+						     DRM_FORMAT_MOD_LINEAR));
+
+		ok = do_test_display(data, port_id, output, primary,
+				    &local_mode, fourcc, tolerance,
+				    count, true);
+		if (ok)
+			tested++;
+		else
+			skipped++;
+
+		disable_output(data, output, primary);
+	} while (++i < count_modes);
+
+	igt_info("All-modes summary: %d tested, %d skipped (capture timeout) out of %d\n",
+		 tested, skipped, i);
+	igt_assert_f(tested > 0,
+		     "All modes failed to capture – Chamelium capture broken\n");
+}
+
+
+/*
+ * For each mode, capture frames and do pixel-exact comparison.
+ * Frame dump test.
+ */
+static void test_display_frame_dump(struct frames_test_data *data,
+				    chamelium_v3_port_id port_id,
+				    bool is_hdmi)
+{
+	igt_output_t *output;
+	igt_plane_t *primary;
+	drmModeConnector *connector;
+	int i, count_modes;
+
+	output = find_output_for_port(data, port_id, is_hdmi);
+	igt_require_f(output, "No %s output found on DUT\n",
+		      is_hdmi ? "HDMI" : "DP");
+
+	connector = output->config.connector;
+	igt_require_f(connector->count_modes > 0, "No modes available\n");
+
+	igt_info("Frame dump test for %s...\n", output->name);
+
+	i = 0;
+	do {
+		drmModeModeInfo local_mode;
+
+		igt_modeset_disable_all_outputs(&data->display);
+
+		output = find_output_for_port(data, port_id, is_hdmi);
+		igt_assert(output);
+
+		connector = output->config.connector;
+		count_modes = connector->count_modes;
+		if (i >= count_modes)
+			break;
+
+		local_mode = connector->modes[i];
+
+		igt_info("Frame dump mode %d/%d: %dx%d@%d\n", i + 1,
+			 count_modes,
+			 local_mode.hdisplay, local_mode.vdisplay,
+			 local_mode.vrefresh);
+
+		primary = setup_output(data, output, &local_mode);
+		igt_assert(primary);
+
+		do_test_frame_dump(data, port_id, output, primary, &local_mode, is_hdmi);
+
+		disable_output(data, output, primary);
+	} while (++i < count_modes);
+}
+
+
+/*
+ * Test each supported non-planar (RGB) format on HDMI.
+ */
+static void test_display_nonplanar_formats(struct frames_test_data *data,
+					   chamelium_v3_port_id port_id,
+					   bool is_hdmi)
+{
+	igt_output_t *output;
+	igt_plane_t *primary;
+	drmModeModeInfo *mode;
+	unsigned int k;
+
+	igt_modeset_disable_all_outputs(&data->display);
+
+	output = find_output_for_port(data, port_id, is_hdmi);
+	igt_require_f(output, "No %s output found on DUT\n",
+		      is_hdmi ? "HDMI" : "DP");
+
+	mode = igt_output_get_mode(output);
+	primary = setup_output(data, output, mode);
+	igt_assert(primary);
+
+	for (k = 0; k < primary->format_mod_count; k++) {
+		if (!igt_fb_supported_format(primary->formats[k]))
+			continue;
+		if (igt_format_is_yuv(primary->formats[k]))
+			continue;
+		if (primary->modifiers[k] != DRM_FORMAT_MOD_LINEAR)
+			continue;
+
+		igt_info("Testing non-planar format: %s\n",
+			 igt_format_str(primary->formats[k]));
+
+		do_test_display(data, port_id, output, primary, mode,
+				primary->formats[k], FRAME_FORMAT_TOLERANCE,
+				1, false);
+	}
+
+	disable_output(data, output, primary);
+}
+
+
+/*
+ * Test each supported planar (YUV) format with checkerboard comparison.
+ */
+static void test_display_planar_formats(struct frames_test_data *data,
+					chamelium_v3_port_id port_id,
+					bool is_hdmi)
+{
+	igt_output_t *output;
+	igt_plane_t *primary;
+	drmModeModeInfo *mode;
+	unsigned int k;
+
+	igt_modeset_disable_all_outputs(&data->display);
+
+	output = find_output_for_port(data, port_id, is_hdmi);
+	igt_require_f(output, "No %s output found on DUT\n",
+		      is_hdmi ? "HDMI" : "DP");
+
+	mode = igt_output_get_mode(output);
+	primary = setup_output(data, output, mode);
+	igt_assert(primary);
+
+	for (k = 0; k < primary->format_mod_count; k++) {
+		if (!igt_fb_supported_format(primary->formats[k]))
+			continue;
+		if (!igt_format_is_yuv(primary->formats[k]))
+			continue;
+		if (primary->modifiers[k] != DRM_FORMAT_MOD_LINEAR)
+			continue;
+
+		igt_info("Testing planar format: %s\n",
+			 igt_format_str(primary->formats[k]));
+
+		do_test_display(data, port_id, output, primary, mode,
+				primary->formats[k],
+				FRAME_CHECKERBOARD_TOLERANCE, 1, false);
+	}
+
+	disable_output(data, output, primary);
+}
+
+
+/*
+ * Set up random overlay planes with random parameters, capture the
+ * composed frame and compare against a software-composed reference.
+ * Random planes test.
+ */
+static void test_display_planes_random(struct frames_test_data *data,
+				       chamelium_v3_port_id port_id,
+				       bool is_hdmi,
+				       int tolerance,
+				       bool allow_scaling,
+				       bool allow_yuv)
+{
+	igt_output_t *output;
+	drmModeModeInfo *mode;
+	igt_plane_t *primary_plane;
+	struct igt_fb primary_fb;
+	struct igt_fb result_fb;
+	struct igt_fb *overlay_fbs;
+	struct chamelium_v3_frame *frame;
+	unsigned int overlay_planes_max = 0;
+	unsigned int overlay_planes_count;
+	cairo_surface_t *result_surface;
+	unsigned int i;
+	unsigned int fb_id;
+
+	srand(time(NULL));
+
+	igt_modeset_disable_all_outputs(&data->display);
+
+	output = find_output_for_port(data, port_id, is_hdmi);
+	igt_require_f(output, "No %s output found on DUT\n",
+		      is_hdmi ? "HDMI" : "DP");
+
+	mode = igt_output_get_mode(output);
+
+	/* Get primary plane */
+	primary_plane = setup_output(data, output, mode);
+	igt_assert(primary_plane);
+
+	/* Create primary FB */
+	fb_id = igt_create_color_pattern_fb(data->drm_fd,
+					    mode->hdisplay, mode->vdisplay,
+					    DRM_FORMAT_XRGB8888,
+					    DRM_FORMAT_MOD_LINEAR,
+					    0, 0, 0, &primary_fb);
+	igt_assert(fb_id > 0);
+
+	/* Create result FB for software composition */
+	fb_id = igt_create_fb(data->drm_fd, mode->hdisplay, mode->vdisplay,
+			      DRM_FORMAT_XRGB8888, DRM_FORMAT_MOD_LINEAR,
+			      &result_fb);
+	igt_assert(fb_id > 0);
+
+	result_surface = igt_get_cairo_surface(data->drm_fd, &result_fb);
+
+	/* Blit primary framebuffer onto result surface */
+	blit_plane_cairo(data, result_surface, 0, 0, 0, 0, 0, 0, 0, 0,
+			 &primary_fb);
+
+	/* Set primary plane */
+	igt_plane_set_fb(primary_plane, &primary_fb);
+
+	overlay_planes_max = igt_output_count_plane_type(output,
+							 DRM_PLANE_TYPE_OVERLAY);
+	overlay_planes_max = min(overlay_planes_max, 4u);
+
+	overlay_planes_count = (rand() % overlay_planes_max) + 1;
+	igt_debug("Using %d overlay planes\n", overlay_planes_count);
+
+	overlay_fbs = calloc(overlay_planes_count, sizeof(struct igt_fb));
+
+	for (i = 0; i < overlay_planes_count; i++) {
+		struct igt_fb *overlay_fb = &overlay_fbs[i];
+		igt_plane_t *plane = igt_output_get_plane_type_index(
+			output, DRM_PLANE_TYPE_OVERLAY, i);
+		igt_assert(plane);
+
+		prepare_randomized_plane(data, mode, plane, overlay_fb, i,
+					result_surface, allow_scaling,
+					allow_yuv);
+	}
+
+	cairo_surface_destroy(result_surface);
+
+	/* Commit all planes */
+	igt_display_commit2(&data->display, COMMIT_ATOMIC);
+
+	/* Wait for stabilization */
+	sleep(2);
+
+	/* Capture and compare */
+	frame = capture_frame_with_retry(data, port_id);
+	igt_assert_f(frame, "Failed to capture frame for planes-random test\n");
+
+	igt_assert_f(compare_frame_to_fb(data, frame, &result_fb, tolerance),
+		     "Random planes frame comparison failed\n");
+
+	chamelium_v3_free_frame(frame);
+
+	/* Cleanup */
+	for (i = 0; i < overlay_planes_count; i++)
+		igt_remove_fb(data->drm_fd, &overlay_fbs[i]);
+
+	free(overlay_fbs);
+	igt_remove_fb(data->drm_fd, &primary_fb);
+	igt_remove_fb(data->drm_fd, &result_fb);
+
+	disable_output(data, output, primary_plane);
+}
+
+
+static enum infoframe_avi_picture_aspect_ratio
+get_infoframe_avi_picture_ar(uint32_t aspect_ratio)
+{
+	switch (aspect_ratio) {
+	case DRM_MODE_PICTURE_ASPECT_4_3:
+		return INFOFRAME_AVI_PIC_AR_4_3;
+	case DRM_MODE_PICTURE_ASPECT_16_9:
+		return INFOFRAME_AVI_PIC_AR_16_9;
+	default:
+		return INFOFRAME_AVI_PIC_AR_UNSPECIFIED;
+	}
+}
+
+/*
+ * Verify AVI InfoFrame aspect ratio.
+ * Aspect ratio test.
+ */
+static void test_display_aspect_ratio(struct frames_test_data *data,
+				      chamelium_v3_port_id port_id)
+{
+	igt_output_t *output;
+	igt_plane_t *primary;
+	drmModeConnector *connector;
+	drmModeModeInfo local_mode;
+	struct igt_fb fb;
+	struct chamelium_v3_infoframe *infoframe;
+	struct infoframe_avi infoframe_avi;
+	uint8_t vic = 16;
+	const struct vic_mode *vic_mode;
+	uint32_t aspect_ratio;
+	enum infoframe_avi_picture_aspect_ratio frame_ar;
+	int i, fb_id;
+	bool found, ok;
+
+	igt_modeset_disable_all_outputs(&data->display);
+
+	output = find_output_for_port(data, port_id, true /* HDMI */);
+	igt_require_f(output, "No HDMI output found on DUT\n");
+
+	connector = output->config.connector;
+
+	vic_mode = &vic_modes[vic];
+	aspect_ratio = vic_mode->picture_ar;
+
+	/*
+	 * Find a mode matching the VIC resolution and refresh rate.
+	 * V3 doesn't inject a special aspect-ratio EDID, so we match on
+	 * resolution/refresh only. The HDMI driver should still send
+	 * the correct VIC and aspect ratio in the AVI InfoFrame for
+	 * CEA modes like 1920x1080@60 (VIC 16).
+	 */
+	found = false;
+	igt_assert(connector->count_modes > 0);
+	for (i = 0; i < connector->count_modes; i++) {
+		if (vic_mode->hactive == connector->modes[i].hdisplay &&
+		    vic_mode->vactive == connector->modes[i].vdisplay &&
+		    vic_mode->vrefresh == connector->modes[i].vrefresh) {
+			local_mode = connector->modes[i];
+			found = true;
+			break;
+		}
+	}
+	igt_require_f(found,
+		      "Failed to find mode matching VIC %d (%dx%d@%d)\n",
+		      vic, vic_mode->hactive, vic_mode->vactive,
+		      vic_mode->vrefresh);
+
+	igt_info("Aspect ratio test: using mode %dx%d@%d (VIC %d)\n",
+		 local_mode.hdisplay, local_mode.vdisplay,
+		 local_mode.vrefresh, vic);
+
+	primary = setup_output(data, output, &local_mode);
+	igt_assert(primary);
+
+	fb_id = igt_create_color_pattern_fb(data->drm_fd,
+					    local_mode.hdisplay,
+					    local_mode.vdisplay,
+					    DRM_FORMAT_XRGB8888,
+					    DRM_FORMAT_MOD_LINEAR,
+					    0, 0, 0, &fb);
+	igt_assert(fb_id > 0);
+
+	enable_output(data, output, primary, &fb);
+
+	/* Read AVI InfoFrame from Chamelium */
+	infoframe = chamelium_v3_get_last_infoframe(data->chamelium, port_id, "avi");
+	igt_require_f(infoframe,
+		      "AVI InfoFrame not available (GetLastInfoFrame may not be implemented on this Chamelium V3)\n");
+
+	ok = infoframe_avi_parse(&infoframe_avi, infoframe->version,
+				 infoframe->payload, infoframe->payload_size);
+	igt_assert_f(ok, "Failed to parse AVI InfoFrame\n");
+
+	frame_ar = get_infoframe_avi_picture_ar(aspect_ratio);
+
+	igt_info("Checking AVI InfoFrame\n");
+	igt_info("  Picture aspect ratio: got %d, expected %d\n",
+		 infoframe_avi.picture_aspect_ratio, frame_ar);
+	igt_info("  Video Identification Code (VIC): got %d, expected %d\n",
+		 infoframe_avi.vic, vic);
+
+	igt_assert_f(infoframe_avi.picture_aspect_ratio == frame_ar,
+		     "AVI InfoFrame aspect ratio mismatch: got %d, expected %d\n",
+		     infoframe_avi.picture_aspect_ratio, frame_ar);
+	igt_assert_f(infoframe_avi.vic == vic,
+		     "AVI InfoFrame VIC mismatch: got %d, expected %d\n",
+		     infoframe_avi.vic, vic);
+
+	chamelium_v3_infoframe_destroy(infoframe);
+	igt_remove_fb(data->drm_fd, &fb);
+	disable_output(data, output, primary);
+}
+
+
+/**
+ * SUBTEST: dp-crc-fast
+ * Description: Display a test pattern on the first DP mode, capture a frame
+ *              via Chamelium V3 DumpPixels, and compare against local reference.
+ */
+static void test_dp_crc_fast(struct frames_test_data *data)
+{
+	chamelium_v3_port_id port = find_dp_port(data);
+
+	igt_require_f(port != (chamelium_v3_port_id)-1, "No plugged DP port found\n");
+	test_display_one_mode(data, port, false, DRM_FORMAT_XRGB8888,
+			      FRAME_CRC_TOLERANCE, 1);
+}
+
+/**
+ * SUBTEST: dp-crc-single
+ * Description: For each mode of the base EDID on DP, display a test pattern,
+ *              capture 1 frame, and compare.
+ */
+static void test_dp_crc_single(struct frames_test_data *data)
+{
+	chamelium_v3_port_id port = find_dp_port(data);
+
+	igt_require_f(port != (chamelium_v3_port_id)-1, "No plugged DP port found\n");
+	test_display_all_modes(data, port, false, DRM_FORMAT_XRGB8888,
+			       FRAME_CRC_TOLERANCE, 1);
+}
+
+/**
+ * SUBTEST: dp-crc-multiple
+ * Description: For each mode of the base EDID on DP, display a test pattern,
+ *              capture 3 frames, and compare each.
+ */
+static void test_dp_crc_multiple(struct frames_test_data *data)
+{
+	chamelium_v3_port_id port = find_dp_port(data);
+
+	igt_require_f(port != (chamelium_v3_port_id)-1, "No plugged DP port found\n");
+	test_display_all_modes(data, port, false, DRM_FORMAT_XRGB8888,
+			       FRAME_CRC_TOLERANCE, 3);
+}
+
+/**
+ * SUBTEST: dp-frame-dump
+ * Description: For each mode of the base EDID on DP, display a test pattern,
+ *              capture frames and compare them pixel-by-pixel to the sent ones.
+ */
+static void test_dp_frame_dump(struct frames_test_data *data)
+{
+	chamelium_v3_port_id port = find_dp_port(data);
+
+	igt_require_f(port != (chamelium_v3_port_id)-1, "No plugged DP port found\n");
+	test_display_frame_dump(data, port, false);
+}
+
+/**
+ * SUBTEST: hdmi-crc-fast
+ * Description: Display a test pattern on the first HDMI mode, capture a frame
+ *              via Chamelium V3 DumpPixels, and compare against local reference.
+ */
+static void test_hdmi_crc_fast(struct frames_test_data *data)
+{
+	chamelium_v3_port_id port = find_hdmi_port(data);
+
+	igt_require_f(port != (chamelium_v3_port_id)-1, "No plugged HDMI port found\n");
+	test_display_one_mode(data, port, true, DRM_FORMAT_XRGB8888,
+			      FRAME_CRC_TOLERANCE, 1);
+}
+
+/**
+ * SUBTEST: hdmi-crc-single
+ * Description: For each mode of the base EDID on HDMI, display a test pattern,
+ *              capture 1 frame, and compare.
+ */
+static void test_hdmi_crc_single(struct frames_test_data *data)
+{
+	chamelium_v3_port_id port = find_hdmi_port(data);
+
+	igt_require_f(port != (chamelium_v3_port_id)-1, "No plugged HDMI port found\n");
+	test_display_all_modes(data, port, true, DRM_FORMAT_XRGB8888,
+			       FRAME_CRC_TOLERANCE, 1);
+}
+
+/**
+ * SUBTEST: hdmi-crc-multiple
+ * Description: For each mode of the base EDID on HDMI, display a test pattern,
+ *              capture 3 frames, and compare each.
+ */
+static void test_hdmi_crc_multiple(struct frames_test_data *data)
+{
+	chamelium_v3_port_id port = find_hdmi_port(data);
+
+	igt_require_f(port != (chamelium_v3_port_id)-1, "No plugged HDMI port found\n");
+	test_display_all_modes(data, port, true, DRM_FORMAT_XRGB8888,
+			       FRAME_CRC_TOLERANCE, 3);
+}
+
+/**
+ * SUBTEST: hdmi-crc-nonplanar-formats
+ * Description: For each non-planar pixel format supported by the primary plane,
+ *              display a test pattern and compare captured frame.
+ */
+static void test_hdmi_crc_nonplanar_formats(struct frames_test_data *data)
+{
+	chamelium_v3_port_id port = find_hdmi_port(data);
+
+	igt_require_f(port != (chamelium_v3_port_id)-1, "No plugged HDMI port found\n");
+	test_display_nonplanar_formats(data, port, true);
+}
+
+/**
+ * SUBTEST: hdmi-crc-planes-random
+ * Description: Set up random overlay planes on HDMI, capture the composed
+ *              frame and compare against software-composed reference.
+ */
+static void test_hdmi_crc_planes_random(struct frames_test_data *data)
+{
+	chamelium_v3_port_id port = find_hdmi_port(data);
+
+	igt_require_f(port != (chamelium_v3_port_id)-1, "No plugged HDMI port found\n");
+	test_display_planes_random(data, port, true,
+				   FRAME_CRC_TOLERANCE, false, false);
+}
+
+/**
+ * SUBTEST: hdmi-cmp-planar-formats
+ * Description: For each planar (YUV) pixel format, display a test pattern
+ *              and compare captured frame with checkerboard tolerance.
+ */
+static void test_hdmi_cmp_planar_formats(struct frames_test_data *data)
+{
+	chamelium_v3_port_id port = find_hdmi_port(data);
+
+	igt_require_f(port != (chamelium_v3_port_id)-1, "No plugged HDMI port found\n");
+	test_display_planar_formats(data, port, true);
+}
+
+/**
+ * SUBTEST: hdmi-cmp-planes-random
+ * Description: Set up random overlay planes with YUV formats on HDMI,
+ *              capture the frame and compare with checkerboard tolerance.
+ */
+static void test_hdmi_cmp_planes_random(struct frames_test_data *data)
+{
+	chamelium_v3_port_id port = find_hdmi_port(data);
+
+	igt_require_f(port != (chamelium_v3_port_id)-1, "No plugged HDMI port found\n");
+	test_display_planes_random(data, port, true,
+				   FRAME_CHECKERBOARD_TOLERANCE, true, true);
+}
+
+/**
+ * SUBTEST: hdmi-frame-dump
+ * Description: For each mode of the base EDID on HDMI, display a test pattern,
+ *              capture frames and compare pixel-by-pixel.
+ */
+static void test_hdmi_frame_dump(struct frames_test_data *data)
+{
+	chamelium_v3_port_id port = find_hdmi_port(data);
+
+	igt_require_f(port != (chamelium_v3_port_id)-1, "No plugged HDMI port found\n");
+	test_display_frame_dump(data, port, true);
+}
+
+/**
+ * SUBTEST: hdmi-aspect-ratio
+ * Description: Set a mode with a specific picture aspect ratio, read the AVI
+ *              InfoFrame from Chamelium and verify the aspect ratio field matches.
+ */
+static void test_hdmi_aspect_ratio(struct frames_test_data *data)
+{
+	chamelium_v3_port_id port = find_hdmi_port(data);
+
+	igt_require_f(port != (chamelium_v3_port_id)-1, "No plugged HDMI port found\n");
+	test_display_aspect_ratio(data, port);
+}
+
+
+int igt_main()
+{
+	struct frames_test_data data = { 0 };
+
+	igt_fixture() {
+		/* Set VT to graphics mode so fbcon doesn't interfere */
+		kmstest_set_vt_graphics_mode();
+
+		/* Open DRM */
+		data.drm_fd = drm_open_driver_master(DRIVER_ANY);
+		igt_require(data.drm_fd >= 0);
+		igt_display_require(&data.display, data.drm_fd);
+		igt_require(data.display.is_atomic);
+
+		/* Initialize Chamelium V3 */
+		data.chamelium = chamelium_v3_init_from_config();
+		igt_require(data.chamelium);
+
+		data.port_count = chamelium_v3_get_supported_ports(
+			data.chamelium, &data.ports);
+		igt_require(data.port_count > 0);
+
+		igt_info("Found %d Chamelium ports\n", data.port_count);
+	}
+
+	/* DP frame/CRC tests */
+	igt_describe("Display test pattern on first DP mode, capture and compare frame");
+	igt_subtest("dp-crc-fast")
+		test_dp_crc_fast(&data);
+
+	igt_describe("For each DP mode, capture 1 frame and compare");
+	igt_subtest("dp-crc-single")
+		test_dp_crc_single(&data);
+
+	igt_describe("For each DP mode, capture 3 frames and compare");
+	igt_subtest("dp-crc-multiple")
+		test_dp_crc_multiple(&data);
+
+	igt_describe("For each DP mode, capture frames and compare pixel-by-pixel");
+	igt_subtest("dp-frame-dump")
+		test_dp_frame_dump(&data);
+
+	/* HDMI frame/CRC tests */
+	igt_describe("Display test pattern on first HDMI mode, capture and compare frame");
+	igt_subtest("hdmi-crc-fast")
+		test_hdmi_crc_fast(&data);
+
+	igt_describe("For each HDMI mode, capture 1 frame and compare");
+	igt_subtest("hdmi-crc-single")
+		test_hdmi_crc_single(&data);
+
+	igt_describe("For each HDMI mode, capture 3 frames and compare");
+	igt_subtest("hdmi-crc-multiple")
+		test_hdmi_crc_multiple(&data);
+
+	igt_describe("CRC check per non-planar pixel format for HDMI");
+	igt_subtest("hdmi-crc-nonplanar-formats")
+		test_hdmi_crc_nonplanar_formats(&data);
+
+	igt_describe("Random overlay plane frame comparison for HDMI");
+	igt_subtest("hdmi-crc-planes-random")
+		test_hdmi_crc_planes_random(&data);
+
+	igt_describe("Checkerboard comparison for YUV planar formats on HDMI");
+	igt_subtest("hdmi-cmp-planar-formats")
+		test_hdmi_cmp_planar_formats(&data);
+
+	igt_describe("Random overlay plane checkerboard comparison for HDMI");
+	igt_subtest("hdmi-cmp-planes-random")
+		test_hdmi_cmp_planes_random(&data);
+
+	igt_describe("Pixel-by-pixel frame dump comparison for HDMI");
+	igt_subtest("hdmi-frame-dump")
+		test_hdmi_frame_dump(&data);
+
+	igt_describe("AVI InfoFrame aspect ratio verification for HDMI");
+	igt_subtest("hdmi-aspect-ratio")
+		test_hdmi_aspect_ratio(&data);
+
+	igt_fixture() {
+		igt_display_fini(&data.display);
+		close(data.drm_fd);
+		free(data.ports);
+		chamelium_v3_uninit(data.chamelium);
+	}
+}
diff --git a/tests/meson.build b/tests/meson.build
index cfb59ecbb..c3dbff694 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -376,6 +376,7 @@ chamelium_v3_progs = [
 	'kms_chamelium_v3_basic',
 	'kms_chamelium_v3_hpd',
 	'kms_chamelium_v3_edid',
+	'kms_chamelium_v3_frames',
 ]
 
 test_deps = [ igt_deps ]
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH i-g-t v1 24/25] tests/chamelium/v3: Add color verification tests for Chamelium v3
  2026-04-28  4:46 [PATCH i-g-t v1 00/25] Chamelium v3 Integration and Test Execution Mohammed Bilal
                   ` (22 preceding siblings ...)
  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 ` Mohammed Bilal
  2026-04-28  4:46 ` [PATCH i-g-t v1 25/25] tests/chamelium/v3: Add audio " Mohammed Bilal
                   ` (4 subsequent siblings)
  28 siblings, 0 replies; 37+ messages in thread
From: Mohammed Bilal @ 2026-04-28  4:46 UTC (permalink / raw)
  To: igt-dev; +Cc: kunal1.joshi, Mohammed Bilal

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

This commit adds color testing capabilities for Chamelium v3 including
color depth verification, gamma testing, and color space validation.

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

diff --git a/tests/chamelium/v3/kms_chamelium_v3_color.c b/tests/chamelium/v3/kms_chamelium_v3_color.c
new file mode 100644
index 000000000..1a41a0c10
--- /dev/null
+++ b/tests/chamelium/v3/kms_chamelium_v3_color.c
@@ -0,0 +1,1058 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2026 Intel Corporation
+ *
+ * Chamelium V3 Color / Color Management Tests
+ *
+ * Tests degamma, gamma, CTM (Color Transformation Matrix),
+ * and broadcast RGB range using Chamelium frame capture.
+ *
+ * Connector selection priority:
+ *   1. Prefer HDMI if connected (to avoid DP FSM issues)
+ *   2. Fall back to DP if no HDMI is available
+ */
+
+#include <math.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include "igt.h"
+#include "igt_kms.h"
+#include "chamelium/v3/igt_chamelium.h"
+
+/**
+ * TEST: kms chamelium v3 color
+ * Category: Display
+ * Description: Color management tests for Chamelium V3
+ * Driver requirement: i915, xe
+ * Mega feature: General Display Features
+ */
+
+IGT_TEST_DESCRIPTION("Test Color Features at Pipe level using Chamelium V3 to verify");
+
+#define HPD_WAIT_TIME_MS 3000
+
+/* Chamelium V3 port IDs */
+#define FPGA_DP1_PORT 4
+#define FPGA_DP2_PORT 5
+#define ITE_HDMI1_PORT 2
+#define ITE_HDMI2_PORT 3
+
+/* Color type for painting */
+typedef struct {
+	double r, g, b;
+} color_t;
+
+/* Gamma LUT type */
+typedef struct {
+	int size;
+	color_t coeffs[];
+} gamma_lut_t;
+
+struct color_test_data {
+	/* Chamelium V3 */
+	struct igt_chamelium_v3 *chamelium;
+	chamelium_v3_port_id *ports;
+	int port_count;
+	chamelium_v3_port_id selected_port;
+	bool hdmi_selected;
+
+	/* DRM display */
+	int drm_fd;
+	uint32_t devid;
+	igt_display_t display;
+	igt_output_t *output;
+	igt_plane_t *primary;
+	drmModeModeInfo *mode;
+
+	/* Color properties */
+	uint32_t drm_format;
+	uint32_t color_depth;
+	uint64_t degamma_lut_size;
+	uint64_t gamma_lut_size;
+};
+
+/*
+ * Tolerance for analog frame comparison (analog tolerance).
+ * Analog signals have some noise, so we allow small differences.
+ */
+#define FRAME_TOLERANCE 10
+
+
+static gamma_lut_t *alloc_lut(int lut_size)
+{
+	gamma_lut_t *gamma;
+
+	igt_assert_lt(0, lut_size);
+	gamma = malloc(sizeof(*gamma) + lut_size * sizeof(gamma->coeffs[0]));
+	igt_assert(gamma);
+	gamma->size = lut_size;
+
+	return gamma;
+}
+
+static void free_lut(gamma_lut_t *gamma)
+{
+	free(gamma);
+}
+
+static void set_rgb(color_t *coeff, double value)
+{
+	coeff->r = coeff->g = coeff->b = value;
+}
+
+static gamma_lut_t *generate_table(int lut_size, double exp_val)
+{
+	gamma_lut_t *gamma = alloc_lut(lut_size);
+	int i;
+
+	set_rgb(&gamma->coeffs[0], 0.0);
+	for (i = 1; i < lut_size; i++)
+		set_rgb(&gamma->coeffs[i], pow(i * 1.0 / (lut_size - 1), exp_val));
+
+	return gamma;
+}
+
+static gamma_lut_t *generate_table_max(int lut_size)
+{
+	gamma_lut_t *gamma = alloc_lut(lut_size);
+	int i;
+
+	set_rgb(&gamma->coeffs[0], 0.0);
+	for (i = 1; i < lut_size; i++)
+		set_rgb(&gamma->coeffs[i], 1.0);
+
+	return gamma;
+}
+
+static struct drm_color_lut *coeffs_to_lut(struct color_test_data *data,
+					   const gamma_lut_t *gamma,
+					   uint32_t color_depth)
+{
+	struct drm_color_lut *lut;
+	int i, lut_size = gamma->size;
+	uint32_t max_value = (1 << 16) - 1;
+	uint32_t mask;
+
+	if (is_intel_device(data->drm_fd))
+		mask = ((1 << color_depth) - 1) << (16 - color_depth);
+	else
+		mask = max_value;
+
+	lut = malloc(sizeof(struct drm_color_lut) * lut_size);
+
+	for (i = 0; i < lut_size; i++) {
+		uint32_t r = gamma->coeffs[i].r * max_value;
+		uint32_t g = gamma->coeffs[i].g * max_value;
+		uint32_t b = gamma->coeffs[i].b * max_value;
+
+		r &= mask;
+		g &= mask;
+		b &= mask;
+
+		lut[i].red = r;
+		lut[i].green = g;
+		lut[i].blue = b;
+	}
+
+	return lut;
+}
+
+static void set_degamma(struct color_test_data *data, igt_crtc_t *pipe,
+			const gamma_lut_t *gamma)
+{
+	size_t size = sizeof(struct drm_color_lut) * gamma->size;
+	struct drm_color_lut *lut = coeffs_to_lut(data, gamma, data->color_depth);
+
+	igt_crtc_replace_prop_blob(pipe, IGT_CRTC_DEGAMMA_LUT, lut, size);
+	free(lut);
+}
+
+static void set_gamma(struct color_test_data *data, igt_crtc_t *pipe,
+		      const gamma_lut_t *gamma)
+{
+	size_t size = sizeof(struct drm_color_lut) * gamma->size;
+	struct drm_color_lut *lut = coeffs_to_lut(data, gamma, data->color_depth);
+
+	igt_crtc_replace_prop_blob(pipe, IGT_CRTC_GAMMA_LUT, lut, size);
+	free(lut);
+}
+
+static void set_ctm(igt_crtc_t *pipe, const double *coefficients)
+{
+	struct drm_color_ctm ctm;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ctm.matrix); i++) {
+		if (coefficients[i] < 0) {
+			ctm.matrix[i] = (int64_t)(-coefficients[i] * ((int64_t)1L << 32));
+			ctm.matrix[i] |= 1ULL << 63;
+		} else {
+			ctm.matrix[i] = (int64_t)(coefficients[i] * ((int64_t)1L << 32));
+		}
+	}
+
+	igt_crtc_replace_prop_blob(pipe, IGT_CRTC_CTM, &ctm, sizeof(ctm));
+}
+
+static void disable_prop(igt_crtc_t *pipe, enum igt_atomic_crtc_properties prop)
+{
+	if (igt_crtc_has_prop(pipe, prop))
+		igt_crtc_replace_prop_blob(pipe, prop, NULL, 0);
+}
+
+#define disable_degamma(pipe) disable_prop(pipe, IGT_CRTC_DEGAMMA_LUT)
+#define disable_gamma(pipe) disable_prop(pipe, IGT_CRTC_GAMMA_LUT)
+#define disable_ctm(pipe) disable_prop(pipe, IGT_CRTC_CTM)
+
+
+static void paint_gradient_rectangles(struct color_test_data *data,
+				      drmModeModeInfo *mode,
+				      const color_t *colors,
+				      struct igt_fb *fb)
+{
+	cairo_t *cr = igt_get_cairo_ctx(data->drm_fd, fb);
+	int i, l = mode->hdisplay / 3;
+	int rows_remaining = mode->hdisplay % 3;
+
+	for (i = 0; i < 3; i++) {
+		igt_paint_color_gradient_range(cr, i * l, 0, l, mode->vdisplay,
+					       colors[i].r != 0 ? 0.2 : 0,
+					       colors[i].g != 0 ? 0.2 : 0,
+					       colors[i].b != 0 ? 0.2 : 0,
+					       colors[i].r, colors[i].g, colors[i].b);
+	}
+
+	if (rows_remaining > 0)
+		igt_paint_color_gradient_range(cr, i * l, 0, rows_remaining,
+					       mode->vdisplay,
+					       colors[i-1].r != 0 ? 0.2 : 0,
+					       colors[i-1].g != 0 ? 0.2 : 0,
+					       colors[i-1].b != 0 ? 0.2 : 0,
+					       colors[i-1].r, colors[i-1].g,
+					       colors[i-1].b);
+
+	igt_put_cairo_ctx(cr);
+}
+
+static void paint_rectangles(struct color_test_data *data,
+			     drmModeModeInfo *mode,
+			     const color_t *colors,
+			     struct igt_fb *fb)
+{
+	cairo_t *cr = igt_get_cairo_ctx(data->drm_fd, fb);
+	int i, l = mode->hdisplay / 3;
+	int rows_remaining = mode->hdisplay % 3;
+
+	for (i = 0; i < 3; i++) {
+		igt_paint_color(cr, i * l, 0, l, mode->vdisplay,
+				colors[i].r, colors[i].g, colors[i].b);
+	}
+
+	if (rows_remaining > 0)
+		igt_paint_color(cr, i * l, 0, rows_remaining, mode->vdisplay,
+				colors[i-1].r, colors[i-1].g, colors[i-1].b);
+
+	igt_put_cairo_ctx(cr);
+}
+
+
+/*
+ * Compare captured RGB frame against expected framebuffer.
+ * Returns true if frames match within tolerance.
+ */
+static bool compare_frame_to_fb(struct color_test_data *data,
+				struct chamelium_v3_frame *frame,
+				struct igt_fb *fb,
+				int tolerance)
+{
+	cairo_t *cr;
+	cairo_surface_t *surface;
+	unsigned char *fb_data;
+	int stride;
+	int x, y;
+	int mismatches = 0;
+	int total_pixels = frame->width * frame->height;
+	int max_mismatches = total_pixels / 10;  /* Allow 10% mismatch for analog/edge effects */
+        int mid_x, mid_y, frame_idx, fb_idx;
+        uint8_t cap_r, cap_g, cap_b, fb_b, fb_g, fb_r;
+        int diff_r, diff_g, diff_b;
+
+        /* Get framebuffer data */
+        cr = igt_get_cairo_ctx(data->drm_fd, fb);
+        surface = cairo_get_target(cr);
+        cairo_surface_flush(surface);
+        fb_data = cairo_image_surface_get_data(surface);
+        stride = cairo_image_surface_get_stride(surface);
+
+        igt_info("Comparing frame %dx%d against framebuffer (stride=%d)...\n",
+                 frame->width, frame->height, stride);
+
+        /* Sample some middle pixels for debug */
+        mid_x = frame->width / 2;
+        mid_y = frame->height / 2;
+        frame_idx = (mid_y * frame->width + mid_x) * 3;
+        fb_idx = mid_y * stride + mid_x * 4;
+        igt_info("Sample at (%d,%d): captured RGB(%d,%d,%d), expected RGB(%d,%d,%d)\n",
+                 mid_x, mid_y,
+                 frame->data[frame_idx + 0], frame->data[frame_idx + 1], frame->data[frame_idx + 2],
+                 fb_data[fb_idx + 2], fb_data[fb_idx + 1], fb_data[fb_idx + 0]);
+
+        /* Compare pixel by pixel */
+        for (y = 0; y < frame->height && mismatches <= max_mismatches; y++) {
+                for (x = 0; x < frame->width && mismatches <= max_mismatches; x++) {
+                        /* Captured frame is RGB */
+                        frame_idx = (y * frame->width + x) * 3;
+                        cap_r = frame->data[frame_idx + 0];
+                        cap_g = frame->data[frame_idx + 1];
+                        cap_b = frame->data[frame_idx + 2];
+
+                        /* Framebuffer is XRGB8888 (BGRA in memory on little-endian) */
+                        fb_idx = y * stride + x * 4;
+                        fb_b = fb_data[fb_idx + 0];
+                        fb_g = fb_data[fb_idx + 1];
+                        fb_r = fb_data[fb_idx + 2];
+
+                        diff_r = abs((int)cap_r - (int)fb_r);
+                        diff_g = abs((int)cap_g - (int)fb_g);
+                        diff_b = abs((int)cap_b - (int)fb_b);
+
+                        if (diff_r > tolerance || diff_g > tolerance || diff_b > tolerance) {
+                                mismatches++;
+                                if (mismatches <= 10) {
+                                        igt_debug("Mismatch at (%d,%d): captured RGB(%d,%d,%d) vs expected RGB(%d,%d,%d)\n",
+                                                  x, y, cap_r, cap_g, cap_b, fb_r, fb_g, fb_b);
+                                }
+                        }
+                }
+        }
+
+        igt_put_cairo_ctx(cr);
+
+        igt_info("Frame comparison: %d mismatches out of %d pixels (%.2f%%)\n",
+                 mismatches, total_pixels, 100.0 * mismatches / total_pixels);
+
+        return mismatches <= max_mismatches;
+}
+
+
+/*
+ * Find the DRM output that corresponds to the Chamelium port.
+ * For HDMI port 2, look for HDMI-A-1 connector.
+ * For DP port 4, look for DP-1 connector.
+ */
+static bool find_output_for_port(struct color_test_data *data)
+{
+	igt_output_t *output;
+	const char *expected_type;
+	igt_crtc_t *crtc;
+
+	if (data->hdmi_selected)
+		expected_type = "HDMI";
+	else
+		expected_type = "DP";
+
+	igt_info("Looking for %s connector...\n", expected_type);
+
+	for_each_crtc(&data->display, crtc) {
+		for_each_valid_output_on_crtc(&data->display, crtc, output) {
+			uint32_t conn_type = output->config.connector->connector_type;
+			bool is_match = false;
+
+			if (data->hdmi_selected) {
+				is_match = (conn_type == DRM_MODE_CONNECTOR_HDMIA ||
+					    conn_type == DRM_MODE_CONNECTOR_HDMIB);
+			} else {
+				is_match = (conn_type == DRM_MODE_CONNECTOR_DisplayPort);
+			}
+
+			if (is_match) {
+				data->output = output;
+				igt_info("Found matching output: %s on pipe %s\n",
+					 output->name, kmstest_pipe_name(crtc->pipe));
+				return true;
+			}
+		}
+	}
+
+	igt_warn("No matching output found for %s port\n", expected_type);
+	return false;
+}
+
+static bool setup_display(struct color_test_data *data)
+{
+	igt_crtc_t *pipe_obj;
+	igt_crtc_t *crtc;
+
+	if (!find_output_for_port(data))
+		return false;
+
+	/* Find a valid pipe for this output and get primary plane */
+	for_each_crtc(&data->display, crtc) {
+		if (igt_crtc_connector_valid(crtc, data->output)) {
+			pipe_obj = crtc;
+			data->primary = igt_crtc_get_plane_type(pipe_obj, DRM_PLANE_TYPE_PRIMARY);
+			if (data->primary) {
+				igt_output_set_crtc(data->output, pipe_obj);
+				break;
+			}
+		}
+	}
+
+	if (!data->primary) {
+		igt_warn("Could not get primary plane for output\n");
+		return false;
+	}
+
+	pipe_obj = data->primary->crtc;
+	data->mode = igt_output_get_mode(data->output);
+
+	/* Get LUT sizes */
+	if (igt_crtc_has_prop(pipe_obj, IGT_CRTC_DEGAMMA_LUT_SIZE)) {
+		data->degamma_lut_size = igt_crtc_get_prop(pipe_obj, IGT_CRTC_DEGAMMA_LUT_SIZE);
+	} else {
+		data->degamma_lut_size = 256;  /* Default */
+	}
+
+	if (igt_crtc_has_prop(pipe_obj, IGT_CRTC_GAMMA_LUT_SIZE)) {
+		data->gamma_lut_size = igt_crtc_get_prop(pipe_obj, IGT_CRTC_GAMMA_LUT_SIZE);
+	} else {
+		data->gamma_lut_size = 256;  /* Default */
+	}
+
+	data->color_depth = 8;
+	data->drm_format = DRM_FORMAT_XRGB8888;
+
+	igt_info("Display setup: %s @ %dx%d, degamma_lut=%lu, gamma_lut=%lu\n",
+		 data->output->name, data->mode->hdisplay, data->mode->vdisplay,
+		 data->degamma_lut_size, data->gamma_lut_size);
+
+	return true;
+}
+
+/*
+ * Select the best port for color testing.
+ * Priority: HDMI > DP 
+ */
+static int select_color_test_port(struct color_test_data *data)
+{
+	int i;
+	int dp_port_idx = -1;
+	int hdmi_port_idx = -1;
+
+	igt_info("Selecting port for color tests (prefer HDMI over DP)...\n");
+
+	for (i = 0; i < data->port_count; i++) {
+		chamelium_v3_port_id port_id = data->ports[i];
+		char *port_name = chamelium_v3_get_port_name(data->chamelium, port_id);
+		bool is_plugged = chamelium_v3_is_plugged(data->chamelium, port_id);
+
+		igt_info("  Port %d (%s): %s\n", port_id, port_name,
+			 is_plugged ? "plugged" : "unplugged");
+
+		if (!is_plugged) {
+			free(port_name);
+			continue;
+		}
+
+		if (chamelium_v3_port_is_hdmi(data->chamelium, port_id)) {
+			if (hdmi_port_idx < 0) {
+				hdmi_port_idx = i;
+				igt_info("    -> Found HDMI port (preferred)\n");
+			}
+		} else if (chamelium_v3_port_is_dp(data->chamelium, port_id)) {
+			if (dp_port_idx < 0) {
+				dp_port_idx = i;
+				igt_info("    -> Found DP port (fallback)\n");
+			}
+		}
+
+		free(port_name);
+	}
+
+	if (hdmi_port_idx >= 0) {
+		data->selected_port = data->ports[hdmi_port_idx];
+		data->hdmi_selected = true;
+		igt_info("Selected HDMI port %d for color tests\n", data->selected_port);
+		return hdmi_port_idx;
+	}
+
+	if (dp_port_idx >= 0) {
+		data->selected_port = data->ports[dp_port_idx];
+		data->hdmi_selected = false;
+		igt_info("Selected DP port %d for color tests\n", data->selected_port);
+		return dp_port_idx;
+	}
+
+	igt_info("No suitable port found for color tests\n");
+	return -1;
+}
+
+
+/**
+ * SUBTEST: degamma
+ * Description: Verify degamma LUT with max transform using Chamelium frame capture.
+ */
+static void test_degamma(struct color_test_data *data)
+{
+	gamma_lut_t *degamma_full;
+	igt_crtc_t *pipe_obj;
+	struct igt_fb fb_modeset, fb, fbref;
+	struct chamelium_v3_frame *frame;
+	color_t red_green_blue[] = {
+		{ 1.0, 0.0, 0.0 },
+		{ 0.0, 1.0, 0.0 },
+		{ 0.0, 0.0, 1.0 }
+	};
+	bool result;
+
+	igt_require(setup_display(data));
+	pipe_obj = data->primary->crtc;
+	igt_require(igt_crtc_has_prop(pipe_obj, IGT_CRTC_DEGAMMA_LUT));
+
+	degamma_full = generate_table_max(data->degamma_lut_size);
+
+	/* Create framebuffers */
+	igt_create_fb(data->drm_fd, data->mode->hdisplay, data->mode->vdisplay,
+		      DRM_FORMAT_XRGB8888, DRM_FORMAT_MOD_LINEAR, &fb_modeset);
+	igt_create_fb(data->drm_fd, data->mode->hdisplay, data->mode->vdisplay,
+		      DRM_FORMAT_XRGB8888, DRM_FORMAT_MOD_LINEAR, &fb);
+	igt_create_fb(data->drm_fd, data->mode->hdisplay, data->mode->vdisplay,
+		      DRM_FORMAT_XRGB8888, DRM_FORMAT_MOD_LINEAR, &fbref);
+
+	/* Initial modeset with solid color for video signal stabilization */
+	{
+		cairo_t *cr_init = igt_get_cairo_ctx(data->drm_fd, &fb_modeset);
+		igt_paint_color(cr_init, 0, 0, data->mode->hdisplay, data->mode->vdisplay,
+				0.5, 0.5, 0.5);
+		igt_put_cairo_ctx(cr_init);
+	}
+	igt_plane_set_fb(data->primary, &fb_modeset);
+	disable_ctm(pipe_obj);
+	disable_gamma(pipe_obj);
+	disable_degamma(pipe_obj);
+	igt_display_commit(&data->display);
+
+	/* Wait for video signal to stabilize */
+	sleep(2);
+
+	/* Draw reference: solid colors (what gradient + max degamma should produce) */
+	paint_rectangles(data, data->mode, red_green_blue, &fbref);
+
+	/* Draw test: gradient with max degamma LUT */
+	paint_gradient_rectangles(data, data->mode, red_green_blue, &fb);
+	igt_plane_set_fb(data->primary, &fb);
+	set_degamma(data, pipe_obj, degamma_full);
+	igt_display_commit(&data->display);
+
+	/* Wait for display to stabilize after degamma change */
+	sleep(1);
+
+	/* Capture frame from Chamelium */
+	frame = chamelium_v3_capture_frame(data->chamelium, data->selected_port);
+	igt_assert(frame);
+
+	/* Compare captured frame against reference */
+	result = compare_frame_to_fb(data, frame, &fbref, FRAME_TOLERANCE);
+
+	/* Cleanup */
+	chamelium_v3_free_frame(frame);
+	disable_degamma(pipe_obj);
+	igt_plane_set_fb(data->primary, NULL);
+	igt_output_set_crtc(data->output, NULL);
+	igt_display_commit(&data->display);
+	igt_remove_fb(data->drm_fd, &fb_modeset);
+	igt_remove_fb(data->drm_fd, &fb);
+	igt_remove_fb(data->drm_fd, &fbref);
+	free_lut(degamma_full);
+
+	igt_assert(result);
+}
+
+/**
+ * SUBTEST: gamma
+ * Description: Verify gamma LUT with max transform using Chamelium frame capture.
+ */
+static void test_gamma(struct color_test_data *data)
+{
+	gamma_lut_t *gamma_full;
+	igt_crtc_t *pipe_obj;
+	struct igt_fb fb_modeset, fb, fbref;
+	struct chamelium_v3_frame *frame;
+	color_t red_green_blue[] = {
+		{ 1.0, 0.0, 0.0 },
+		{ 0.0, 1.0, 0.0 },
+		{ 0.0, 0.0, 1.0 }
+	};
+	bool result;
+
+	igt_require(setup_display(data));
+	pipe_obj = data->primary->crtc;
+	igt_require(igt_crtc_has_prop(pipe_obj, IGT_CRTC_GAMMA_LUT));
+
+	gamma_full = generate_table_max(data->gamma_lut_size);
+
+	/* Create framebuffers */
+	igt_create_fb(data->drm_fd, data->mode->hdisplay, data->mode->vdisplay,
+		      DRM_FORMAT_XRGB8888, DRM_FORMAT_MOD_LINEAR, &fb_modeset);
+	igt_create_fb(data->drm_fd, data->mode->hdisplay, data->mode->vdisplay,
+		      DRM_FORMAT_XRGB8888, DRM_FORMAT_MOD_LINEAR, &fb);
+	igt_create_fb(data->drm_fd, data->mode->hdisplay, data->mode->vdisplay,
+		      DRM_FORMAT_XRGB8888, DRM_FORMAT_MOD_LINEAR, &fbref);
+
+	/* Initial modeset with solid color for video signal stabilization */
+	{
+		cairo_t *cr_init = igt_get_cairo_ctx(data->drm_fd, &fb_modeset);
+		igt_paint_color(cr_init, 0, 0, data->mode->hdisplay, data->mode->vdisplay,
+				0.5, 0.5, 0.5);
+		igt_put_cairo_ctx(cr_init);
+	}
+	igt_plane_set_fb(data->primary, &fb_modeset);
+	disable_ctm(pipe_obj);
+	disable_degamma(pipe_obj);
+	igt_display_commit(&data->display);
+
+	/* Wait for video signal to stabilize */
+	sleep(2);
+
+	/* Draw reference: solid colors */
+	paint_rectangles(data, data->mode, red_green_blue, &fbref);
+
+	/* Draw test: gradient with max gamma LUT */
+	paint_gradient_rectangles(data, data->mode, red_green_blue, &fb);
+	igt_plane_set_fb(data->primary, &fb);
+	set_gamma(data, pipe_obj, gamma_full);
+	igt_display_commit(&data->display);
+
+	/* Wait for display to stabilize after gamma change */
+	sleep(1);
+
+	frame = chamelium_v3_capture_frame(data->chamelium, data->selected_port);
+	igt_assert(frame);
+
+	result = compare_frame_to_fb(data, frame, &fbref, FRAME_TOLERANCE);
+
+	chamelium_v3_free_frame(frame);
+	disable_gamma(pipe_obj);
+	igt_plane_set_fb(data->primary, NULL);
+	igt_output_set_crtc(data->output, NULL);
+	igt_display_commit(&data->display);
+	igt_remove_fb(data->drm_fd, &fb_modeset);
+	igt_remove_fb(data->drm_fd, &fb);
+	igt_remove_fb(data->drm_fd, &fbref);
+	free_lut(gamma_full);
+
+	igt_assert(result);
+}
+
+/*
+ * Generic CTM test helper
+ */
+static void test_pipe_ctm(struct color_test_data *data,
+			  color_t *before, color_t *after,
+			  const double *ctm_matrix)
+{
+	gamma_lut_t *degamma_linear, *gamma_linear;
+	igt_crtc_t *pipe_obj;
+	struct igt_fb fb_modeset, fb, fbref;
+	struct chamelium_v3_frame *frame;
+	bool result;
+
+	igt_require(setup_display(data));
+	pipe_obj = data->primary->crtc;
+	igt_require(igt_crtc_has_prop(pipe_obj, IGT_CRTC_CTM));
+
+	degamma_linear = generate_table(data->degamma_lut_size, 1.0);
+	gamma_linear = generate_table(data->gamma_lut_size, 1.0);
+
+	/* Create framebuffers */
+	igt_create_fb(data->drm_fd, data->mode->hdisplay, data->mode->vdisplay,
+		      DRM_FORMAT_XRGB8888, DRM_FORMAT_MOD_LINEAR, &fb_modeset);
+	igt_create_fb(data->drm_fd, data->mode->hdisplay, data->mode->vdisplay,
+		      DRM_FORMAT_XRGB8888, DRM_FORMAT_MOD_LINEAR, &fb);
+	igt_create_fb(data->drm_fd, data->mode->hdisplay, data->mode->vdisplay,
+		      DRM_FORMAT_XRGB8888, DRM_FORMAT_MOD_LINEAR, &fbref);
+
+	/* Initial modeset with solid color for video signal stabilization */
+	{
+		cairo_t *cr_init = igt_get_cairo_ctx(data->drm_fd, &fb_modeset);
+		igt_paint_color(cr_init, 0, 0, data->mode->hdisplay, data->mode->vdisplay,
+				0.5, 0.5, 0.5);
+		igt_put_cairo_ctx(cr_init);
+	}
+	igt_plane_set_fb(data->primary, &fb_modeset);
+
+	if (memcmp(before, after, sizeof(color_t) * 3)) {
+		set_degamma(data, pipe_obj, degamma_linear);
+		set_gamma(data, pipe_obj, gamma_linear);
+	} else {
+		disable_degamma(pipe_obj);
+		disable_gamma(pipe_obj);
+	}
+	disable_ctm(pipe_obj);
+	igt_display_commit(&data->display);
+
+	/* Wait for video signal to stabilize */
+	sleep(2);
+
+	/* Draw reference: expected colors after CTM */
+	paint_rectangles(data, data->mode, after, &fbref);
+
+	/* Draw test: before colors with CTM applied */
+	paint_rectangles(data, data->mode, before, &fb);
+	igt_plane_set_fb(data->primary, &fb);
+	set_ctm(pipe_obj, ctm_matrix);
+	igt_display_commit(&data->display);
+
+	/* Wait for display to stabilize after CTM change */
+	sleep(1);
+
+	frame = chamelium_v3_capture_frame(data->chamelium, data->selected_port);
+	igt_assert(frame);
+
+	result = compare_frame_to_fb(data, frame, &fbref, FRAME_TOLERANCE);
+
+	chamelium_v3_free_frame(frame);
+	disable_degamma(pipe_obj);
+	disable_gamma(pipe_obj);
+	disable_ctm(pipe_obj);
+	igt_plane_set_fb(data->primary, NULL);
+	igt_output_set_crtc(data->output, NULL);
+	igt_display_commit(&data->display);
+	igt_remove_fb(data->drm_fd, &fb_modeset);
+	igt_remove_fb(data->drm_fd, &fb);
+	igt_remove_fb(data->drm_fd, &fbref);
+	free_lut(degamma_linear);
+	free_lut(gamma_linear);
+
+	igt_assert(result);
+}
+
+/**
+ * SUBTEST: ctm-red-to-blue
+ * Description: Verify CTM red-to-blue channel swap.
+ */
+static void test_ctm_red_to_blue(struct color_test_data *data)
+{
+	color_t before[] = {
+		{ 1.0, 0.0, 0.0 },
+		{ 0.0, 1.0, 0.0 },
+		{ 0.0, 0.0, 1.0 }
+	};
+	color_t after[] = {
+		{ 0.0, 0.0, 1.0 },  /* Red -> Blue */
+		{ 0.0, 1.0, 0.0 },  /* Green -> Green */
+		{ 0.0, 0.0, 1.0 }   /* Blue -> Blue */
+	};
+	double ctm[] = {
+		0.0, 0.0, 0.0,
+		0.0, 1.0, 0.0,
+		1.0, 0.0, 1.0
+	};
+
+	test_pipe_ctm(data, before, after, ctm);
+}
+
+/**
+ * SUBTEST: ctm-green-to-red
+ * Description: Verify CTM green-to-red channel swap.
+ */
+static void test_ctm_green_to_red(struct color_test_data *data)
+{
+	color_t before[] = {
+		{ 1.0, 0.0, 0.0 },
+		{ 0.0, 1.0, 0.0 },
+		{ 0.0, 0.0, 1.0 }
+	};
+	color_t after[] = {
+		{ 1.0, 0.0, 0.0 },  /* Red -> Red */
+		{ 1.0, 0.0, 0.0 },  /* Green -> Red */
+		{ 0.0, 0.0, 1.0 }   /* Blue -> Blue */
+	};
+	double ctm[] = {
+		1.0, 1.0, 0.0,
+		0.0, 0.0, 0.0,
+		0.0, 0.0, 1.0
+	};
+
+	test_pipe_ctm(data, before, after, ctm);
+}
+
+/**
+ * SUBTEST: ctm-blue-to-red
+ * Description: Verify CTM blue-to-red channel swap.
+ */
+static void test_ctm_blue_to_red(struct color_test_data *data)
+{
+	color_t before[] = {
+		{ 1.0, 0.0, 0.0 },
+		{ 0.0, 1.0, 0.0 },
+		{ 0.0, 0.0, 1.0 }
+	};
+	color_t after[] = {
+		{ 1.0, 0.0, 0.0 },  /* Red -> Red */
+		{ 0.0, 1.0, 0.0 },  /* Green -> Green */
+		{ 1.0, 0.0, 0.0 }   /* Blue -> Red */
+	};
+	double ctm[] = {
+		1.0, 0.0, 1.0,
+		0.0, 1.0, 0.0,
+		0.0, 0.0, 0.0
+	};
+
+	test_pipe_ctm(data, before, after, ctm);
+}
+
+/**
+ * SUBTEST: ctm-max
+ * Description: Verify CTM with 100x scale (saturation to white).
+ */
+static void test_ctm_max(struct color_test_data *data)
+{
+	color_t before[] = {
+		{ 0.5, 0.0, 0.0 },
+		{ 0.0, 0.5, 0.0 },
+		{ 0.0, 0.0, 0.5 }
+	};
+	color_t after[] = {
+		{ 1.0, 0.0, 0.0 },  /* Saturates to max */
+		{ 0.0, 1.0, 0.0 },
+		{ 0.0, 0.0, 1.0 }
+	};
+	double ctm[] = {
+		100.0, 0.0, 0.0,
+		0.0, 100.0, 0.0,
+		0.0, 0.0, 100.0
+	};
+
+	test_pipe_ctm(data, before, after, ctm);
+}
+
+/**
+ * SUBTEST: ctm-negative
+ * Description: Verify CTM with negative scale (clamp to black).
+ */
+static void test_ctm_negative(struct color_test_data *data)
+{
+	color_t before[] = {
+		{ 1.0, 0.0, 0.0 },
+		{ 0.0, 1.0, 0.0 },
+		{ 0.0, 0.0, 1.0 }
+	};
+	color_t after[] = {
+		{ 0.0, 0.0, 0.0 },  /* Clamps to black */
+		{ 0.0, 0.0, 0.0 },
+		{ 0.0, 0.0, 0.0 }
+	};
+	double ctm[] = {
+		-1.0, 0.0, 0.0,
+		0.0, -1.0, 0.0,
+		0.0, 0.0, -1.0
+	};
+
+	test_pipe_ctm(data, before, after, ctm);
+}
+
+/**
+ * SUBTEST: ctm-0-25
+ * Description: Verify CTM with 0.25 scale factor.
+ */
+static void test_ctm_0_25(struct color_test_data *data)
+{
+	color_t before[] = {
+		{ 1.0, 0.0, 0.0 },
+		{ 0.0, 1.0, 0.0 },
+		{ 0.0, 0.0, 1.0 }
+	};
+	color_t after[] = {
+		{ 0.25, 0.0, 0.0 },
+		{ 0.0, 0.25, 0.0 },
+		{ 0.0, 0.0, 0.25 }
+	};
+	double ctm[] = {
+		0.25, 0.0, 0.0,
+		0.0, 0.25, 0.0,
+		0.0, 0.0, 0.25
+	};
+
+	test_pipe_ctm(data, before, after, ctm);
+}
+
+/**
+ * SUBTEST: ctm-0-50
+ * Description: Verify CTM with 0.50 scale factor.
+ */
+static void test_ctm_0_50(struct color_test_data *data)
+{
+	color_t before[] = {
+		{ 1.0, 0.0, 0.0 },
+		{ 0.0, 1.0, 0.0 },
+		{ 0.0, 0.0, 1.0 }
+	};
+	color_t after[] = {
+		{ 0.5, 0.0, 0.0 },
+		{ 0.0, 0.5, 0.0 },
+		{ 0.0, 0.0, 0.5 }
+	};
+	double ctm[] = {
+		0.5, 0.0, 0.0,
+		0.0, 0.5, 0.0,
+		0.0, 0.0, 0.5
+	};
+
+	test_pipe_ctm(data, before, after, ctm);
+}
+
+/**
+ * SUBTEST: ctm-0-75
+ * Description: Verify CTM with 0.75 scale factor.
+ */
+static void test_ctm_0_75(struct color_test_data *data)
+{
+	color_t before[] = {
+		{ 1.0, 0.0, 0.0 },
+		{ 0.0, 1.0, 0.0 },
+		{ 0.0, 0.0, 1.0 }
+	};
+	color_t after[] = {
+		{ 0.75, 0.0, 0.0 },
+		{ 0.0, 0.75, 0.0 },
+		{ 0.0, 0.0, 0.75 }
+	};
+	double ctm[] = {
+		0.75, 0.0, 0.0,
+		0.0, 0.75, 0.0,
+		0.0, 0.0, 0.75
+	};
+
+	test_pipe_ctm(data, before, after, ctm);
+}
+
+/**
+ * SUBTEST: ctm-limited-range
+ * Description: Verify CTM with limited-range broadcast RGB.
+ */
+static void test_ctm_limited_range(struct color_test_data *data)
+{
+	/*
+	 * In limited range, the GPU maps full range [0-255] to [16-235]
+	 * on the wire. The Chamelium captures raw wire values, so:
+	 *   FB 255 (1.0) -> wire 235
+	 *   FB 0   (0.0) -> wire 16
+	 */
+	double limited_max = 235.0 / 255.0;
+	double limited_min = 16.0 / 255.0;
+	color_t before[] = {
+		{ 1.0, 0.0, 0.0 },
+		{ 0.0, 1.0, 0.0 },
+		{ 0.0, 0.0, 1.0 }
+	};
+	color_t after[] = {
+		{ limited_max, limited_min, limited_min },
+		{ limited_min, limited_max, limited_min },
+		{ limited_min, limited_min, limited_max }
+	};
+	double ctm[] = {
+		1.0, 0.0, 0.0,
+		0.0, 1.0, 0.0,
+		0.0, 0.0, 1.0
+	};
+
+	igt_require(setup_display(data));
+	igt_require(igt_output_has_prop(data->output, IGT_CONNECTOR_BROADCAST_RGB));
+
+	/* Set limited range */
+	igt_output_set_prop_value(data->output, IGT_CONNECTOR_BROADCAST_RGB, BROADCAST_RGB_16_235);
+
+	test_pipe_ctm(data, before, after, ctm);
+
+	/* Reset to full range */
+	igt_output_set_prop_value(data->output, IGT_CONNECTOR_BROADCAST_RGB, BROADCAST_RGB_FULL);
+}
+
+
+int igt_main()
+{
+	struct color_test_data data = { 0 };
+	int selected_port_idx;
+
+	igt_fixture() {
+		/* Setup Chamelium */
+		data.chamelium = chamelium_v3_init_from_config();
+		igt_require(data.chamelium);
+
+		data.port_count = chamelium_v3_get_supported_ports(data.chamelium, &data.ports);
+		igt_require(data.port_count > 0);
+
+		data.drm_fd = drm_open_driver_master(DRIVER_ANY);
+		igt_require(data.drm_fd >= 0);
+
+		data.devid = is_intel_device(data.drm_fd) ?
+			     intel_get_drm_devid(data.drm_fd) : 0;
+
+		kmstest_set_vt_graphics_mode();
+		igt_display_require(&data.display, data.drm_fd);
+
+		igt_info("Found %d Chamelium ports\n", data.port_count);
+
+		selected_port_idx = select_color_test_port(&data);
+		igt_require_f(selected_port_idx >= 0,
+			      "No connected port found for color tests\n");
+
+		igt_info("Using %s port %d for color tests\n",
+			 data.hdmi_selected ? "HDMI" : "DP", data.selected_port);
+	}
+
+	igt_describe("Degamma LUT max transform verification");
+	igt_subtest("degamma")
+		test_degamma(&data);
+
+	igt_describe("Gamma LUT max transform verification");
+	igt_subtest("gamma")
+		test_gamma(&data);
+
+	igt_describe("CTM red-to-blue channel swap verification");
+	igt_subtest("ctm-red-to-blue")
+		test_ctm_red_to_blue(&data);
+
+	igt_describe("CTM green-to-red channel swap verification");
+	igt_subtest("ctm-green-to-red")
+		test_ctm_green_to_red(&data);
+
+	igt_describe("CTM blue-to-red channel swap verification");
+	igt_subtest("ctm-blue-to-red")
+		test_ctm_blue_to_red(&data);
+
+	igt_describe("CTM 100x scale factor (saturation to white)");
+	igt_subtest("ctm-max")
+		test_ctm_max(&data);
+
+	igt_describe("CTM negative scale (clamp to black)");
+	igt_subtest("ctm-negative")
+		test_ctm_negative(&data);
+
+	igt_describe("CTM 0.25 scale factor");
+	igt_subtest("ctm-0-25")
+		test_ctm_0_25(&data);
+
+	igt_describe("CTM 0.50 scale factor");
+	igt_subtest("ctm-0-50")
+		test_ctm_0_50(&data);
+
+	igt_describe("CTM 0.75 scale factor");
+	igt_subtest("ctm-0-75")
+		test_ctm_0_75(&data);
+
+	igt_describe("CTM with limited-range broadcast RGB");
+	igt_subtest("ctm-limited-range")
+		test_ctm_limited_range(&data);
+
+	igt_fixture() {
+		igt_display_fini(&data.display);
+		close(data.drm_fd);
+		free(data.ports);
+		chamelium_v3_uninit(data.chamelium);
+	}
+}
diff --git a/tests/meson.build b/tests/meson.build
index c3dbff694..1c7facccd 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -377,6 +377,7 @@ chamelium_v3_progs = [
 	'kms_chamelium_v3_hpd',
 	'kms_chamelium_v3_edid',
 	'kms_chamelium_v3_frames',
+	'kms_chamelium_v3_color',
 ]
 
 test_deps = [ igt_deps ]
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* [PATCH i-g-t v1 25/25] tests/chamelium/v3: Add audio tests for Chamelium v3
  2026-04-28  4:46 [PATCH i-g-t v1 00/25] Chamelium v3 Integration and Test Execution Mohammed Bilal
                   ` (23 preceding siblings ...)
  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 ` Mohammed Bilal
  2026-04-28  5:52 ` ✓ Xe.CI.BAT: success for Chamelium v3 Integration and Test Execution Patchwork
                   ` (3 subsequent siblings)
  28 siblings, 0 replies; 37+ messages in thread
From: Mohammed Bilal @ 2026-04-28  4:46 UTC (permalink / raw)
  To: igt-dev; +Cc: kunal1.joshi, Mohammed Bilal

This commit adds audio testing capabilities for Chamelium v3 including
HDMI audio capture, audio format verification, and audio playback tests.

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

diff --git a/tests/chamelium/v3/kms_chamelium_v3_audio.c b/tests/chamelium/v3/kms_chamelium_v3_audio.c
new file mode 100644
index 000000000..1e392de15
--- /dev/null
+++ b/tests/chamelium/v3/kms_chamelium_v3_audio.c
@@ -0,0 +1,707 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright (c) 2024 Intel Corporation
+ *
+ * Chamelium V3 Audio Tests
+ *
+ * Tests audio output pipeline verification using ALSA, audio signal,
+ * ELD, and EDID infrastructure from the shared IGT libraries.
+ *
+ * The dp-audio and hdmi-audio subtests verify the GPU audio output pipeline:
+ *   1. Audio EDID is applied and ELD is populated correctly.
+ *   2. ALSA output device opens for the connected display.
+ *   3. Multi-format, multi-rate audio playback completes without error.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include "igt.h"
+#include "igt_edid.h"
+#include "igt_eld.h"
+#include "igt_alsa.h"
+#include "igt_audio.h"
+#include "chamelium/v3/igt_chamelium.h"
+
+/**
+ * TEST: kms chamelium v3 audio
+ * Category: Display
+ * Description: Audio tests for Chamelium V3
+ * Driver requirement: i915, xe
+ * Mega feature: Display Audio
+ */
+
+#define HPD_WAIT_TIME_MS      3000
+#define EDID_SETTLE_TIME_MS   2000
+#define CONNECTOR_WAIT_TIMEOUT 10  /* seconds */
+
+/* Playback parameters */
+#define PLAYBACK_CHANNELS 2
+#define PLAYBACK_SAMPLES  1024
+
+/* Capture parameters (used for frequency step calculation) */
+#define CAPTURE_SAMPLES   2048
+
+/* Duration to play each audio configuration (milliseconds) */
+#define PLAYBACK_DURATION_MS 500
+
+/* Chamelium V3 port IDs */
+#define FPGA_DP1_PORT  4
+#define FPGA_DP2_PORT  5
+#define ITE_HDMI1_PORT 2
+#define ITE_HDMI2_PORT 3
+
+struct audio_test_data {
+	struct igt_chamelium_v3 *chamelium;
+	chamelium_v3_port_id *ports;
+	int port_count;
+	int drm_fd;
+};
+
+/**
+ * struct v3_playback_ctx - Context for audio playback callback
+ *
+ * The dp-audio / hdmi-audio subtests verify the GPU audio output pipeline
+ * by playing test signals and verifying ALSA playback completes without error.
+ */
+struct v3_playback_ctx {
+	struct audio_signal *signal;
+	snd_pcm_format_t format;
+	int channels;
+};
+
+static int test_sampling_rates[] = {
+	32000, 44100, 48000,
+};
+
+static int test_sampling_rates_count =
+	sizeof(test_sampling_rates) / sizeof(int);
+
+static int test_frequencies[] = {
+	300, 600, 1200, 10000, 80000,
+};
+
+static int test_frequencies_count = sizeof(test_frequencies) / sizeof(int);
+
+static const snd_pcm_format_t test_formats[] = {
+	SND_PCM_FORMAT_S16_LE,
+	SND_PCM_FORMAT_S24_LE,
+	SND_PCM_FORMAT_S32_LE,
+};
+
+static const int test_formats_count =
+	sizeof(test_formats) / sizeof(test_formats[0]);
+
+
+static chamelium_v3_port_id find_dp_port(struct audio_test_data *data)
+{
+	int i;
+
+	for (i = 0; i < data->port_count; i++) {
+		chamelium_v3_port_id port = data->ports[i];
+
+		if (chamelium_v3_port_is_dp(data->chamelium, port) &&
+		    (port == FPGA_DP1_PORT || port == FPGA_DP2_PORT))
+			return port;
+	}
+	for (i = 0; i < data->port_count; i++) {
+		if (chamelium_v3_port_is_dp(data->chamelium, data->ports[i]))
+			return data->ports[i];
+	}
+	return (chamelium_v3_port_id)-1;
+}
+
+static chamelium_v3_port_id find_hdmi_port(struct audio_test_data *data)
+{
+	int i;
+
+	for (i = 0; i < data->port_count; i++) {
+		if (chamelium_v3_port_is_hdmi(data->chamelium, data->ports[i]))
+			return data->ports[i];
+	}
+	return (chamelium_v3_port_id)-1;
+}
+
+static chamelium_v3_port_id get_port_for_type(struct audio_test_data *data,
+					      bool is_dp)
+{
+	return is_dp ? find_dp_port(data) : find_hdmi_port(data);
+}
+
+static int get_drm_connector_type(bool is_dp)
+{
+	return is_dp ? DRM_MODE_CONNECTOR_DisplayPort :
+		       DRM_MODE_CONNECTOR_HDMIA;
+}
+
+
+static void disable_all_crtcs(int drm_fd)
+{
+	drmModeRes *res = drmModeGetResources(drm_fd);
+
+	if (!res)
+		return;
+
+	for (int i = 0; i < res->count_crtcs; i++)
+		drmModeSetCrtc(drm_fd, res->crtcs[i], 0, 0, 0, NULL, 0, NULL);
+
+	drmModeFreeResources(res);
+}
+
+static void dump_connector_info(int drm_fd, const char *label)
+{
+	drmModeRes *res = drmModeGetResources(drm_fd);
+	const char *status_str;
+
+	if (!res) {
+		igt_info("%s: cannot get DRM resources\n", label);
+		return;
+	}
+
+	igt_info("%s - Connector info:\n", label);
+	for (int i = 0; i < res->count_connectors; i++) {
+		drmModeConnector *conn =
+			drmModeGetConnector(drm_fd, res->connectors[i]);
+		if (!conn)
+			continue;
+
+		switch (conn->connection) {
+		case DRM_MODE_CONNECTED:
+			status_str = "connected";
+			break;
+		case DRM_MODE_DISCONNECTED:
+			status_str = "disconnected";
+			break;
+		default:
+			status_str = "unknown";
+			break;
+		}
+
+		igt_info("  [CONNECTOR:%u:%s-%u]: status: %s, modes: %d\n",
+			 conn->connector_id,
+			 kmstest_connector_type_str(conn->connector_type),
+			 conn->connector_type_id,
+			 status_str, conn->count_modes);
+
+		drmModeFreeConnector(conn);
+	}
+	drmModeFreeResources(res);
+}
+
+static drmModeConnector *wait_for_connected_connector(int drm_fd, int type)
+{
+	int retries = CONNECTOR_WAIT_TIMEOUT * 20; /* 50ms interval */
+
+	while (retries-- > 0) {
+		drmModeRes *res = drmModeGetResources(drm_fd);
+
+		if (!res) {
+			usleep(50000);
+			continue;
+		}
+
+		for (int i = 0; i < res->count_connectors; i++) {
+			drmModeConnector *conn =
+				drmModeGetConnector(drm_fd, res->connectors[i]);
+			if (!conn)
+				continue;
+			if (conn->connector_type == (uint32_t)type &&
+			    conn->connection == DRM_MODE_CONNECTED &&
+			    conn->count_modes > 0) {
+				drmModeFreeResources(res);
+				return conn;
+			}
+			drmModeFreeConnector(conn);
+		}
+		drmModeFreeResources(res);
+		usleep(50000);
+	}
+	return NULL;
+}
+
+static uint32_t find_crtc_for_connector(int drm_fd, drmModeConnector *conn)
+{
+	drmModeRes *res = drmModeGetResources(drm_fd);
+	drmModeEncoder *enc;
+	uint32_t crtc_id = 0;
+	int i;
+
+	if (!res)
+		return 0;
+
+	for (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; j++) {
+			if (enc->possible_crtcs & (1 << j)) {
+				crtc_id = res->crtcs[j];
+				break;
+			}
+		}
+		drmModeFreeEncoder(enc);
+	}
+	drmModeFreeResources(res);
+	return crtc_id;
+}
+
+static int set_mode_on_connector(int drm_fd, drmModeConnector *conn)
+{
+	drmModeModeInfo *mode;
+	uint32_t crtc_id;
+	struct igt_fb fb;
+	int fb_id;
+
+	igt_assert(conn->count_modes > 0);
+	mode = &conn->modes[0];
+
+	crtc_id = find_crtc_for_connector(drm_fd, conn);
+	igt_assert_f(crtc_id, "No CRTC found for connector\n");
+
+	fb_id = igt_create_color_pattern_fb(drm_fd, mode->hdisplay,
+				    mode->vdisplay,
+				    DRM_FORMAT_XRGB8888,
+				    DRM_FORMAT_MOD_LINEAR,
+				    0, 0, 0, &fb);
+	igt_assert(fb_id > 0);
+
+	igt_assert_eq(drmModeSetCrtc(drm_fd, crtc_id, fb_id, 0, 0,
+			     &conn->connector_id, 1, mode), 0);
+
+	igt_info("  Modeset: %dx%d on CRTC %u\n",
+		 mode->hdisplay, mode->vdisplay, crtc_id);
+
+	return fb_id;
+}
+
+/**
+ * ensure_port_plugged - Plug a port with the given EDID and wait for connector
+ *
+ * Resets Chamelium, disables CRTCs, creates EDID (after reset so it
+ * doesn't get wiped), applies EDID, plugs the port, then polls until
+ * the DRM connector shows as connected.
+ */
+static drmModeConnector *ensure_port_plugged(struct audio_test_data *data,
+					     chamelium_v3_port_id port,
+					     bool is_dp,
+					     const unsigned char *edid_data,
+					     size_t edid_size)
+{
+	drmModeConnector *conn;
+	char *port_name;
+	int edid_id;
+
+	port_name = chamelium_v3_get_port_name(data->chamelium, port);
+	igt_info("Preparing port %d (%s) for plug...\n", port, port_name);
+	free(port_name);
+
+	/* 1. Reset chamelium -- unplugs all ports and clears EDIDs */
+	igt_info("  Resetting Chamelium (unplug all)...\n");
+	chamelium_v3_reset(data->chamelium);
+	usleep(HPD_WAIT_TIME_MS * 1000);
+
+	/* 2. Disable all CRTCs for a clean slate */
+	disable_all_crtcs(data->drm_fd);
+	usleep(500000);
+
+	/* 3. Explicitly unplug the target port if still plugged */
+	if (chamelium_v3_is_plugged(data->chamelium, port)) {
+		igt_info("  Port still plugged after reset, unplugging...\n");
+		chamelium_v3_unplug(data->chamelium, port);
+		usleep(HPD_WAIT_TIME_MS * 1000);
+	}
+
+	dump_connector_info(data->drm_fd, "Before plug");
+
+	/* 4. Create EDID AFTER reset (reset clears all created EDIDs) */
+	edid_id = chamelium_v3_create_edid(data->chamelium, edid_data,
+				   edid_size);
+	igt_info("  Created EDID (id=%d, %zu bytes)\n", edid_id, edid_size);
+
+	/* 5. Apply EDID then plug */
+	chamelium_v3_apply_edid(data->chamelium, port, edid_id);
+	igt_info("  Applied EDID and plugging port %d...\n", port);
+	chamelium_v3_plug(data->chamelium, port);
+
+	/* 6. Wait for HPD and link training */
+	igt_info("  Waiting for HPD + link training...\n");
+	usleep(HPD_WAIT_TIME_MS * 1000);
+	usleep(EDID_SETTLE_TIME_MS * 1000);
+
+	dump_connector_info(data->drm_fd, "After plug");
+
+	/* 7. Poll for connected connector */
+	conn = wait_for_connected_connector(data->drm_fd,
+				    get_drm_connector_type(is_dp));
+	if (conn) {
+		igt_info("  Connector %u now CONNECTED (%d modes)\n",
+			 conn->connector_id, conn->count_modes);
+	} else {
+		igt_warn("  Connector NOT connected after plug!\n");
+		dump_connector_info(data->drm_fd, "Plug failed");
+	}
+
+	return conn;
+}
+
+static int v3_playback_callback(void *data, void *buffer, int samples)
+{
+	struct v3_playback_ctx *ctx = data;
+	double *tmp;
+	size_t len;
+
+	len = samples * ctx->channels;
+	tmp = malloc(len * sizeof(double));
+	audio_signal_fill(ctx->signal, tmp, samples);
+	audio_convert_to(buffer, tmp, len, ctx->format);
+	free(tmp);
+
+	return 0; /* alsa_run() will stop after the requested duration */
+}
+
+/**
+ * test_one_playback - Play a test signal for a fixed duration
+ *
+ * Generates a multi-frequency audio signal, configures the ALSA output
+ * device, plays for @duration_ms and returns true if playback completed
+ * without any ALSA errors.
+ */
+static bool test_one_playback(struct alsa *alsa, snd_pcm_format_t format,
+			      int channels, int rate, int duration_ms)
+{
+	struct v3_playback_ctx ctx;
+	struct audio_signal *signal;
+	int step, i, j, freq, ret;
+
+	signal = audio_signal_init(channels, rate);
+	igt_assert_f(signal, "Failed to init audio signal\n");
+
+	/* Add test frequencies */
+	step = 2 * rate / CAPTURE_SAMPLES;
+	for (i = 0; i < test_frequencies_count; i++) {
+		for (j = 0; j < channels; j++) {
+			freq = test_frequencies[i] + j * step;
+			audio_signal_add_frequency(signal, freq, j);
+		}
+	}
+	audio_signal_synthesize(signal);
+
+	ctx.signal = signal;
+	ctx.format = format;
+	ctx.channels = channels;
+
+	alsa_configure_output(alsa, format, channels, rate);
+	alsa_register_output_callback(alsa, v3_playback_callback, &ctx,
+				      PLAYBACK_SAMPLES);
+
+	igt_info("    Playing: %s, %d Hz, %d ch for %d ms ... ",
+		 snd_pcm_format_name(format), rate, channels, duration_ms);
+
+	ret = alsa_run(alsa, duration_ms);
+
+	audio_signal_fini(signal);
+
+	if (ret < 0) {
+		igt_info("FAIL (ret=%d)\n", ret);
+		return false;
+	}
+
+	igt_info("OK\n");
+	return true;
+}
+
+
+static bool check_audio_configuration(struct alsa *alsa,
+			      snd_pcm_format_t format, int channels,
+			      int sampling_rate)
+{
+	if (!alsa_test_output_configuration(alsa, format, channels,
+				    sampling_rate)) {
+		igt_debug("Skipping format %s, rate %d Hz, %d ch: "
+			  "output device doesn't support this config\n",
+			  snd_pcm_format_name(format), sampling_rate, channels);
+		return false;
+	}
+	return true;
+}
+
+
+/**
+ * SUBTEST: dp-audio
+ * Description: Playback various audio signals with various audio formats/rates
+ *              over DP, verify ALSA ELD and playback complete without error
+ *
+ * SUBTEST: hdmi-audio
+ * Description: Playback various audio signals with various audio formats/rates
+ *              over HDMI, verify ALSA ELD and playback complete without error
+ */
+static void test_display_audio(struct audio_test_data *data, bool is_dp)
+{
+	bool run, success;
+	struct alsa *alsa;
+	int ret;
+	drmModeConnector *connector;
+	int fb_id, i, j;
+	int channels, sampling_rate;
+	snd_pcm_format_t format;
+	chamelium_v3_port_id port;
+	const struct edid *audio_edid;
+	size_t edid_size;
+	char *port_name;
+	struct eld_entry eld;
+
+	port = get_port_for_type(data, is_dp);
+	igt_require_f(port != (chamelium_v3_port_id)-1,
+		      "No %s port found on Chamelium\n",
+		      is_dp ? "DP" : "HDMI");
+
+	port_name = chamelium_v3_get_port_name(data->chamelium, port);
+	igt_info("Testing audio playback on %s (port %d)\n", port_name, port);
+	free(port_name);
+
+	igt_require(alsa_has_exclusive_access());
+
+	alsa = alsa_init();
+	igt_assert(alsa);
+
+	/* Get audio EDID */
+	if (is_dp)
+		audio_edid = igt_kms_get_dp_audio_edid();
+	else
+		audio_edid = igt_kms_get_hdmi_audio_edid();
+	edid_size = edid_get_size(audio_edid);
+
+	/* Plug with audio EDID and modeset */
+	connector = ensure_port_plugged(data, port, is_dp,
+				(const unsigned char *)audio_edid,
+				edid_size);
+	igt_assert_f(connector, "No connector after plug with audio EDID\n");
+
+	fb_id = set_mode_on_connector(data->drm_fd, connector);
+	igt_assert(fb_id > 0);
+
+	/* Give audio subsystem time to initialize after modeset */
+	usleep(2000000);
+
+	/* Verify ELD is populated -- confirms the audio output path is
+	 * active and the display reports correct audio capabilities. */
+	igt_assert_f(eld_get_igt(&eld),
+		     "ELD not available -- audio output path not active\n");
+	igt_info("  ELD verified: audio output path is active "
+		 "(SADs: %zu)\n", eld.sads_len);
+
+	/* Test each format/rate combination by playing through ALSA.
+	 * Verify that the GPU audio output pipeline works: ALSA device
+	 * opens, configures, and plays without error for each supported
+	 * configuration. */
+	run = false;
+	success = true;
+	for (i = 0; i < test_sampling_rates_count; i++) {
+		for (j = 0; j < test_formats_count; j++) {
+			ret = alsa_open_output(alsa, "HDMI");
+			igt_assert_f(ret >= 0, "Failed to open ALSA output\n");
+
+			format = test_formats[j];
+			channels = PLAYBACK_CHANNELS;
+			sampling_rate = test_sampling_rates[i];
+
+			if (!check_audio_configuration(alsa, format, channels,
+						       sampling_rate)) {
+				alsa_close_output(alsa);
+				continue;
+			}
+
+			run = true;
+			success &= test_one_playback(alsa, format, channels,
+						     sampling_rate,
+						     PLAYBACK_DURATION_MS);
+			alsa_close_output(alsa);
+		}
+	}
+
+	igt_assert_f(run, "No audio configuration could be tested\n");
+	igt_assert_f(success, "One or more audio playback configs failed\n");
+
+	igt_info("  Audio playback test PASSED (%s)\n",
+		 is_dp ? "DP" : "HDMI");
+
+	drmModeFreeConnector(connector);
+	free(alsa);
+}
+
+
+/**
+ * SUBTEST: dp-audio-edid
+ * Description: Plug a connector with an EDID suitable for audio, check ALSA's
+ *              EDID-Like Data reports the correct audio parameters
+ *
+ * SUBTEST: hdmi-audio-edid
+ * Description: Plug a connector with an EDID suitable for audio, check ALSA's
+ *              EDID-Like Data reports the correct audio parameters
+ */
+static void test_display_audio_edid(struct audio_test_data *data, bool is_dp)
+{
+	chamelium_v3_port_id port;
+	const struct edid *audio_edid;
+	size_t edid_size;
+	drmModeConnector *connector;
+	int fb_id;
+	struct eld_entry eld;
+	struct eld_sad *sad;
+	char *port_name;
+
+	port = get_port_for_type(data, is_dp);
+	igt_require_f(port != (chamelium_v3_port_id)-1,
+		      "No %s port found on Chamelium\n",
+		      is_dp ? "DP" : "HDMI");
+
+	port_name = chamelium_v3_get_port_name(data->chamelium, port);
+	igt_info("Testing audio EDID on %s (port %d)\n", port_name, port);
+	free(port_name);
+
+	igt_require(eld_is_supported());
+
+	/* Get audio EDID */
+	if (is_dp)
+		audio_edid = igt_kms_get_dp_audio_edid();
+	else
+		audio_edid = igt_kms_get_hdmi_audio_edid();
+	edid_size = edid_get_size(audio_edid);
+
+	/* Plug with audio EDID */
+	connector = ensure_port_plugged(data, port, is_dp,
+				(const unsigned char *)audio_edid,
+				edid_size);
+	igt_assert_f(connector, "No connector after plug with audio EDID\n");
+
+	igt_info("  Connected: %d modes, preferred %dx%d\n",
+		 connector->count_modes,
+		 connector->modes[0].hdisplay,
+		 connector->modes[0].vdisplay);
+
+	/* Enable the output -- audio cannot be played on inactive
+	 * connectors (ELD won't be populated until modeset). */
+	fb_id = set_mode_on_connector(data->drm_fd, connector);
+	igt_assert(fb_id > 0);
+
+	/* Wait for ELD to populate after modeset */
+	usleep(2000000);
+
+	/* Read ELD and verify it matches the audio EDID we applied */
+	igt_assert_f(eld_get_igt(&eld), "Failed to read ELD from ALSA\n");
+	igt_assert_f(eld.sads_len == 1,
+		     "Expected 1 SAD in ELD, got %zu\n", eld.sads_len);
+
+	sad = &eld.sads[0];
+	igt_info("  ELD SAD: coding_type=%d, channels=%d, "
+		 "rates=0x%x, bits=0x%x\n",
+		 sad->coding_type, sad->channels,
+		 sad->rates, sad->bits);
+
+	igt_assert_f(sad->coding_type == CEA_SAD_FORMAT_PCM,
+		     "Expected PCM coding type, got %d\n", sad->coding_type);
+	igt_assert_f(sad->channels == 2,
+		     "Expected 2 channels, got %d\n", sad->channels);
+	igt_assert_f(sad->rates ==
+		     (CEA_SAD_SAMPLING_RATE_32KHZ |
+		      CEA_SAD_SAMPLING_RATE_44KHZ |
+		      CEA_SAD_SAMPLING_RATE_48KHZ),
+		     "Unexpected sampling rates: 0x%x\n", sad->rates);
+	igt_assert_f(sad->bits ==
+		     (CEA_SAD_SAMPLE_SIZE_16 |
+		      CEA_SAD_SAMPLE_SIZE_20 |
+		      CEA_SAD_SAMPLE_SIZE_24),
+		     "Unexpected sample sizes: 0x%x\n", sad->bits);
+
+	igt_info("  Audio EDID verification PASSED\n");
+
+	drmModeFreeConnector(connector);
+}
+
+
+static void __real_main(void)
+{
+	struct audio_test_data data = { 0 };
+	bool setup_done = false;
+
+	data.chamelium = chamelium_v3_init_from_config();
+	if (!data.chamelium) {
+		igt_info("Chamelium not available, skipping tests\n");
+		return;
+	}
+
+	data.port_count = chamelium_v3_get_supported_ports(data.chamelium,
+						   &data.ports);
+	if (data.port_count <= 0) {
+		igt_info("No Chamelium ports found, skipping tests\n");
+		chamelium_v3_uninit(data.chamelium);
+		return;
+	}
+
+	data.drm_fd = drm_open_driver_master(DRIVER_ANY);
+	if (data.drm_fd < 0) {
+		igt_info("Cannot open DRM device as master, skipping tests\n");
+		free(data.ports);
+		chamelium_v3_uninit(data.chamelium);
+		return;
+	}
+
+	setup_done = true;
+	igt_info("Opened DRM device as master, found %d Chamelium ports\n",
+		 data.port_count);
+
+	/* Show initial connector status */
+	dump_connector_info(data.drm_fd, "Initial state");
+
+	/* DP audio tests */
+	igt_describe("Playback various audio signals with various audio "
+		     "formats/rates over DP, verify ELD and playback");
+	igt_subtest("dp-audio") {
+		igt_require(setup_done);
+		test_display_audio(&data, true);
+	}
+
+	igt_describe("Plug a connector with an EDID suitable for audio, "
+		     "check ALSA EDID-Like Data reports the correct "
+		     "audio parameters");
+	igt_subtest("dp-audio-edid") {
+		igt_require(setup_done);
+		test_display_audio_edid(&data, true);
+	}
+
+	/* HDMI audio tests */
+	igt_describe("Playback various audio signals with various audio "
+		     "formats/rates over HDMI, verify ELD and playback");
+	igt_subtest("hdmi-audio") {
+		igt_require(setup_done);
+		test_display_audio(&data, false);
+	}
+
+	igt_describe("Plug a connector with an EDID suitable for audio, "
+		     "check ALSA EDID-Like Data reports the correct "
+		     "audio parameters");
+	igt_subtest("hdmi-audio-edid") {
+		igt_require(setup_done);
+		test_display_audio_edid(&data, false);
+	}
+
+	/* Cleanup */
+	if (setup_done) {
+		igt_info("Cleaning up...\n");
+		chamelium_v3_reset(data.chamelium);
+		usleep(HPD_WAIT_TIME_MS * 1000);
+
+		free(data.ports);
+		chamelium_v3_uninit(data.chamelium);
+		close(data.drm_fd);
+	}
+}
+
+int main(int argc, char **argv)
+{
+	igt_subtest_init(argc, argv);
+	__real_main();
+	igt_exit();
+}
diff --git a/tests/meson.build b/tests/meson.build
index 1c7facccd..8c054fd40 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -378,6 +378,7 @@ chamelium_v3_progs = [
 	'kms_chamelium_v3_edid',
 	'kms_chamelium_v3_frames',
 	'kms_chamelium_v3_color',
+	'kms_chamelium_v3_audio',
 ]
 
 test_deps = [ igt_deps ]
-- 
2.48.1


^ permalink raw reply related	[flat|nested] 37+ messages in thread

* ✓ Xe.CI.BAT: success for Chamelium v3 Integration and Test Execution
  2026-04-28  4:46 [PATCH i-g-t v1 00/25] Chamelium v3 Integration and Test Execution Mohammed Bilal
                   ` (24 preceding siblings ...)
  2026-04-28  4:46 ` [PATCH i-g-t v1 25/25] tests/chamelium/v3: Add audio " Mohammed Bilal
@ 2026-04-28  5:52 ` Patchwork
  2026-04-28  6:09 ` ✗ i915.CI.BAT: failure " Patchwork
                   ` (2 subsequent siblings)
  28 siblings, 0 replies; 37+ messages in thread
From: Patchwork @ 2026-04-28  5:52 UTC (permalink / raw)
  To: Mohammed Bilal; +Cc: igt-dev

[-- Attachment #1: Type: text/plain, Size: 1159 bytes --]

== Series Details ==

Series: Chamelium v3 Integration and Test Execution
URL   : https://patchwork.freedesktop.org/series/165589/
State : success

== Summary ==

CI Bug Log - changes from XEIGT_8874_BAT -> XEIGTPW_15069_BAT
====================================================

Summary
-------

  **SUCCESS**

  No regressions found.

  

Participating hosts (13 -> 13)
------------------------------

  No changes in participating hosts


Changes
-------

  No changes found


Build changes
-------------

  * IGT: IGT_8874 -> IGTPW_15069
  * Linux: xe-4940-b6f6b69b2dffa9ad1c43b2149786b4630d41acbf -> xe-4944-aea2c496abcf55b647c14fe720bfc4ea555aac6a

  IGTPW_15069: 86ce060bb02fc7065ec1bcf1e3cb2878b72ff614 @ https://gitlab.freedesktop.org/drm/igt-gpu-tools.git
  IGT_8874: 4568b2c141ab630c34f8eb2b9afab8cbf8f3ce9e @ https://gitlab.freedesktop.org/drm/igt-gpu-tools.git
  xe-4940-b6f6b69b2dffa9ad1c43b2149786b4630d41acbf: b6f6b69b2dffa9ad1c43b2149786b4630d41acbf
  xe-4944-aea2c496abcf55b647c14fe720bfc4ea555aac6a: aea2c496abcf55b647c14fe720bfc4ea555aac6a

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/index.html

[-- Attachment #2: Type: text/html, Size: 1718 bytes --]

^ permalink raw reply	[flat|nested] 37+ messages in thread

* ✗ i915.CI.BAT: failure for Chamelium v3 Integration and Test Execution
  2026-04-28  4:46 [PATCH i-g-t v1 00/25] Chamelium v3 Integration and Test Execution Mohammed Bilal
                   ` (25 preceding siblings ...)
  2026-04-28  5:52 ` ✓ Xe.CI.BAT: success for Chamelium v3 Integration and Test Execution Patchwork
@ 2026-04-28  6:09 ` Patchwork
  2026-04-28 12:36 ` ✗ Xe.CI.FULL: " Patchwork
  2026-04-28 14:02 ` [PATCH i-g-t v1 00/25] " Louis Chauvet
  28 siblings, 0 replies; 37+ messages in thread
From: Patchwork @ 2026-04-28  6:09 UTC (permalink / raw)
  To: Mohammed Bilal; +Cc: igt-dev

[-- Attachment #1: Type: text/plain, Size: 4359 bytes --]

== Series Details ==

Series: Chamelium v3 Integration and Test Execution
URL   : https://patchwork.freedesktop.org/series/165589/
State : failure

== Summary ==

CI Bug Log - changes from IGT_8874 -> IGTPW_15069
====================================================

Summary
-------

  **FAILURE**

  Serious unknown changes coming with IGTPW_15069 absolutely need to be
  verified manually.
  
  If you think the reported changes have nothing to do with the changes
  introduced in IGTPW_15069, please notify your bug team (I915-ci-infra@lists.freedesktop.org) to allow them
  to document this new failure mode, which will reduce false positives in CI.

  External URL: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_15069/index.html

Participating hosts (42 -> 40)
------------------------------

  Missing    (2): bat-dg2-13 fi-snb-2520m 

Possible new issues
-------------------

  Here are the unknown changes that may have been introduced in IGTPW_15069:

### IGT changes ###

#### Possible regressions ####

  * igt@i915_selftest@live@requests:
    - bat-arlh-2:         [PASS][1] -> [INCOMPLETE][2]
   [1]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_8874/bat-arlh-2/igt@i915_selftest@live@requests.html
   [2]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_15069/bat-arlh-2/igt@i915_selftest@live@requests.html

  
Known issues
------------

  Here are the changes found in IGTPW_15069 that come from known issues:

### IGT changes ###

#### Issues hit ####

  * igt@i915_selftest@live:
    - bat-mtlp-8:         [PASS][3] -> [DMESG-FAIL][4] ([i915#12061]) +1 other test dmesg-fail
   [3]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_8874/bat-mtlp-8/igt@i915_selftest@live.html
   [4]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_15069/bat-mtlp-8/igt@i915_selftest@live.html
    - bat-arlh-2:         [PASS][5] -> [INCOMPLETE][6] ([i915#15978])
   [5]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_8874/bat-arlh-2/igt@i915_selftest@live.html
   [6]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_15069/bat-arlh-2/igt@i915_selftest@live.html

  * igt@i915_selftest@live@workarounds:
    - bat-arls-5:         [PASS][7] -> [DMESG-FAIL][8] ([i915#12061]) +1 other test dmesg-fail
   [7]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_8874/bat-arls-5/igt@i915_selftest@live@workarounds.html
   [8]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_15069/bat-arls-5/igt@i915_selftest@live@workarounds.html
    - bat-dg2-14:         [PASS][9] -> [DMESG-FAIL][10] ([i915#12061]) +1 other test dmesg-fail
   [9]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_8874/bat-dg2-14/igt@i915_selftest@live@workarounds.html
   [10]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_15069/bat-dg2-14/igt@i915_selftest@live@workarounds.html

  * igt@kms_pm_rpm@basic-pci-d3-state:
    - fi-kbl-7567u:       [PASS][11] -> [DMESG-WARN][12] ([i915#15673]) +51 other tests dmesg-warn
   [11]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_8874/fi-kbl-7567u/igt@kms_pm_rpm@basic-pci-d3-state.html
   [12]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_15069/fi-kbl-7567u/igt@kms_pm_rpm@basic-pci-d3-state.html

  
#### Possible fixes ####

  * igt@i915_selftest@live:
    - bat-dg2-8:          [DMESG-FAIL][13] ([i915#12061]) -> [PASS][14] +1 other test pass
   [13]: https://intel-gfx-ci.01.org/tree/drm-tip/IGT_8874/bat-dg2-8/igt@i915_selftest@live.html
   [14]: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_15069/bat-dg2-8/igt@i915_selftest@live.html

  
  [i915#12061]: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/12061
  [i915#15673]: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/15673
  [i915#15978]: https://gitlab.freedesktop.org/drm/i915/kernel/-/issues/15978


Build changes
-------------

  * CI: CI-20190529 -> None
  * IGT: IGT_8874 -> IGTPW_15069
  * Linux: CI_DRM_18369 -> CI_DRM_18373

  CI-20190529: 20190529
  CI_DRM_18369: b6f6b69b2dffa9ad1c43b2149786b4630d41acbf @ git://anongit.freedesktop.org/gfx-ci/linux
  CI_DRM_18373: aea2c496abcf55b647c14fe720bfc4ea555aac6a @ git://anongit.freedesktop.org/gfx-ci/linux
  IGTPW_15069: 86ce060bb02fc7065ec1bcf1e3cb2878b72ff614 @ https://gitlab.freedesktop.org/drm/igt-gpu-tools.git
  IGT_8874: 4568b2c141ab630c34f8eb2b9afab8cbf8f3ce9e @ https://gitlab.freedesktop.org/drm/igt-gpu-tools.git

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_15069/index.html

[-- Attachment #2: Type: text/html, Size: 5322 bytes --]

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH i-g-t v1 01/25] lib/igt_kms: Add a detect timeout value
  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
  2 siblings, 0 replies; 37+ messages in thread
From: Jani Nikula @ 2026-04-28  7:11 UTC (permalink / raw)
  To: Mohammed Bilal, igt-dev
  Cc: kunal1.joshi, Louis Chauvet, Kory Maincent, Luca Ceresoli

On Tue, 28 Apr 2026, Mohammed Bilal <mohammed.bilal@intel.com> wrote:
> From: Louis Chauvet <louis.chauvet@bootlin.com>
>
> Some tests need to wait for a specific connector status. In order to make
> the timeout customisable for each target, add an option in the
> configuration file.
>
> Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
> Reviewed-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> ---
>  lib/igt_core.c |   4 ++
>  lib/igt_kms.c  | 123 +++++++++++++++++++++++++++++++------------------
>  lib/igt_kms.h  |  10 ++++
>  3 files changed, 92 insertions(+), 45 deletions(-)
>
> diff --git a/lib/igt_core.c b/lib/igt_core.c
> index 4f79c0294..cc835ea4c 100644
> --- a/lib/igt_core.c
> +++ b/lib/igt_core.c
> @@ -271,6 +271,10 @@
>   *	&num; It is not mandatory and allows overriding default values.
>   *	[DUT]
>   *	SuspendResumeDelay=10
> + *
> + *	&num; The following option define the timeout for detection feature
> + *	&num; (waiting for a connector status)
> + *	DisplayDetectTimeout=10.0
>   * ]|
>   *
>   * Some specific configuration options may be used by specific parts of IGT,
> diff --git a/lib/igt_kms.c b/lib/igt_kms.c
> index 38c28f45f..ece888d1a 100644
> --- a/lib/igt_kms.c
> +++ b/lib/igt_kms.c
> @@ -60,6 +60,7 @@
>  #include "igt_debugfs.h"
>  #include "igt_device.h"
>  #include "igt_pipe_crc.h"
> +#include "igt_rc.h"
>  #include "igt_sysfs.h"
>  #include "sw_sync.h"
>  #ifdef HAVE_CHAMELIUM
> @@ -8066,38 +8067,38 @@ bool igt_has_lobf_debugfs(int drmfd, igt_output_t *output)
>  
>  igt_crtc_t *igt_crtc_for_crtc_id(igt_display_t *display, uint32_t crtc_id)
>  {
> -	igt_crtc_t *crtc;
> +        igt_crtc_t *crtc;
>  
> -	for_each_crtc(display, crtc) {
> -		if (crtc->crtc_id == crtc_id)
> -			return crtc;
> -	}
> +        for_each_crtc(display, crtc) {
> +                if (crtc->crtc_id == crtc_id)
> +                        return crtc;
> +        }
>  
> -	return NULL;
> +        return NULL;

Please check your patches before sending. There's a bunch of tabs to
spaces changes all over the place.

BR,
Jani.

>  }
>  
>  igt_crtc_t *igt_crtc_for_crtc_index(igt_display_t *display, int crtc_index)
>  {
> -	igt_crtc_t *crtc;
> +        igt_crtc_t *crtc;
>  
> -	for_each_crtc(display, crtc) {
> -		if (crtc->crtc_index == crtc_index)
> -			return crtc;
> -	}
> +        for_each_crtc(display, crtc) {
> +                if (crtc->crtc_index == crtc_index)
> +                        return crtc;
> +        }
>  
> -	return NULL;
> +        return NULL;
>  }
>  
>  igt_crtc_t *igt_crtc_for_pipe(igt_display_t *display, enum pipe pipe)
>  {
> -	igt_crtc_t *crtc;
> +        igt_crtc_t *crtc;
>  
> -	for_each_crtc(display, crtc) {
> -		if (crtc->pipe == pipe)
> -			return crtc;
> -	}
> +        for_each_crtc(display, crtc) {
> +                if (crtc->pipe == pipe)
> +                        return crtc;
> +        }
>  
> -	return NULL;
> +        return NULL;
>  }
>  
>  /*
> @@ -8108,14 +8109,14 @@ igt_crtc_t *igt_crtc_for_pipe(igt_display_t *display, enum pipe pipe)
>   */
>  igt_crtc_t *igt_first_crtc(igt_display_t *display)
>  {
> -	igt_crtc_t *crtc;
> +        igt_crtc_t *crtc;
>  
> -	for_each_crtc(display, crtc)
> -		return crtc;
> +        for_each_crtc(display, crtc)
> +                return crtc;
>  
> -	igt_skip("No CRTCs on device\n");
> +        igt_skip("No CRTCs on device\n");
>  
> -	return NULL;
> +        return NULL;
>  }
>  
>  /**
> @@ -8128,19 +8129,19 @@ igt_crtc_t *igt_first_crtc(igt_display_t *display)
>   */
>  igt_crtc_t *igt_first_crtc_with_single_output(igt_display_t *display, igt_output_t **ret_output)
>  {
> -	igt_output_t *output;
> -	igt_crtc_t *crtc;
> +        igt_output_t *output;
> +        igt_crtc_t *crtc;
>  
> -	for_each_crtc_with_single_output(display, crtc, output) {
> -		*ret_output = output;
> -		return crtc;
> -	}
> +        for_each_crtc_with_single_output(display, crtc, output) {
> +                *ret_output = output;
> +                return crtc;
> +        }
>  
> -	igt_skip("No valid CRTC/output combinations\n");
> +        igt_skip("No valid CRTC/output combinations\n");
>  
> -	*ret_output = NULL;
> +        *ret_output = NULL;
>  
> -	return NULL;
> +        return NULL;
>  }
>  
>  /*
> @@ -8152,14 +8153,14 @@ igt_crtc_t *igt_first_crtc_with_single_output(igt_display_t *display, igt_output
>   */
>  igt_crtc_t *igt_next_crtc(igt_display_t *display, igt_crtc_t *crtc)
>  {
> -	igt_crtc_t *next;
> +        igt_crtc_t *next;
>  
> -	for_each_crtc(display, next) {
> -		if (!crtc || next->pipe > crtc->pipe)
> -			return next;
> -	}
> +        for_each_crtc(display, next) {
> +                if (!crtc || next->pipe > crtc->pipe)
> +                        return next;
> +        }
>  
> -	return NULL;
> +        return NULL;
>  }
>  
>  /*
> @@ -8170,14 +8171,46 @@ igt_crtc_t *igt_next_crtc(igt_display_t *display, igt_crtc_t *crtc)
>   */
>  igt_crtc_t *igt_random_crtc(igt_display_t *display)
>  {
> -	igt_crtc_t *crtcs[IGT_MAX_PIPES];
> -	igt_crtc_t *crtc;
> -	int n = 0;
> +        igt_crtc_t *crtcs[IGT_MAX_PIPES];
> +        igt_crtc_t *crtc;
> +        int n = 0;
>  
> -	for_each_crtc(display, crtc)
> -		crtcs[n++] = crtc;
> +        for_each_crtc(display, crtc)
> +                crtcs[n++] = crtc;
> +
> +        igt_skip_on_f(!n, "No CRTCs on device\n");
> +
> +        return crtcs[rand() % n];
> +}
> +
> +/**
> + * igt_default_display_detect_timeout:
> + *
> + * Get the default timeout value for detection feature
> + *
> + * Some tests requires to wait for a specific connector status. This value will determine the
> + * timeout value for this waiting.
> + */
> +double igt_default_display_detect_timeout(void)
> +{
> +        static double timeout = 0.0;
> +        static bool first_call = true;
> +        GError *error = NULL;
> +
> +        if (first_call) {
> +                if (igt_key_file) {
> +                        timeout = g_key_file_get_double(igt_key_file, "DUT", "DisplayDetectTimeout",&error);
> +                        if (error) {
> +                                igt_debug("Failed to read DisplayDetectTimeout, defaulting to %f\n",DEFAULT_DETECT_TIMEOUT);
> +                                g_clear_error(&error);
> +                                timeout = DEFAULT_DETECT_TIMEOUT;
> +                        }
> +                } else {
> +                        timeout = DEFAULT_DETECT_TIMEOUT;
> +                }
>  
> -	igt_skip_on_f(!n, "No CRTCs on device\n");
> +                first_call = false;
> +        }
>  
> -	return crtcs[rand() % n];
> +        return timeout;
>  }
> diff --git a/lib/igt_kms.h b/lib/igt_kms.h
> index fcbb6a5ad..b10df1d8d 100644
> --- a/lib/igt_kms.h
> +++ b/lib/igt_kms.h
> @@ -40,6 +40,14 @@
>  #include "igt_fb.h"
>  #include "ioctl_wrappers.h"
>  
> +/**
> + * define DEFAULT_DETECT_TIMEOUT - Default timeout in second used for some screen detection
> + * functions
> + *
> + * It can be overiden by option DetectTimeout in the .igtrc file.
> + */
> +#define DEFAULT_DETECT_TIMEOUT 10.0
> +
>  /* Low-level helpers with kmstest_ prefix */
>  
>  /**
> @@ -1289,4 +1297,6 @@ void igt_get_and_wait_out_fence(igt_output_t *output);
>  
>  igt_colorop_t *igt_find_colorop(igt_display_t *display, uint32_t id);
>  
> +double igt_default_display_detect_timeout(void);
> +
>  #endif /* __IGT_KMS_H__ */

-- 
Jani Nikula, Intel

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH i-g-t v1 01/25] lib/igt_kms: Add a detect timeout value
  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
  2 siblings, 0 replies; 37+ messages in thread
From: Jani Nikula @ 2026-04-28  7:16 UTC (permalink / raw)
  To: Mohammed Bilal, igt-dev
  Cc: kunal1.joshi, Louis Chauvet, Kory Maincent, Luca Ceresoli

On Tue, 28 Apr 2026, Mohammed Bilal <mohammed.bilal@intel.com> wrote:
> +/**
> + * igt_default_display_detect_timeout:
> + *
> + * Get the default timeout value for detection feature
> + *
> + * Some tests requires to wait for a specific connector status. This value will determine the
> + * timeout value for this waiting.
> + */
> +double igt_default_display_detect_timeout(void)

What is the unit?

Why is it a double? Makes no sense to me.

Typically you'd make it seconds, and if that's not accurate enough, make
it milliseconds.

BR,
Jani.

> +{
> +        static double timeout = 0.0;
> +        static bool first_call = true;
> +        GError *error = NULL;
> +
> +        if (first_call) {
> +                if (igt_key_file) {
> +                        timeout = g_key_file_get_double(igt_key_file, "DUT", "DisplayDetectTimeout",&error);
> +                        if (error) {
> +                                igt_debug("Failed to read DisplayDetectTimeout, defaulting to %f\n",DEFAULT_DETECT_TIMEOUT);
> +                                g_clear_error(&error);
> +                                timeout = DEFAULT_DETECT_TIMEOUT;
> +                        }
> +                } else {
> +                        timeout = DEFAULT_DETECT_TIMEOUT;
> +                }
>  
> -	igt_skip_on_f(!n, "No CRTCs on device\n");
> +                first_call = false;
> +        }
>  
> -	return crtcs[rand() % n];
> +        return timeout;
>  }
> diff --git a/lib/igt_kms.h b/lib/igt_kms.h
> index fcbb6a5ad..b10df1d8d 100644
> --- a/lib/igt_kms.h
> +++ b/lib/igt_kms.h
> @@ -40,6 +40,14 @@
>  #include "igt_fb.h"
>  #include "ioctl_wrappers.h"
>  
> +/**
> + * define DEFAULT_DETECT_TIMEOUT - Default timeout in second used for some screen detection
> + * functions
> + *
> + * It can be overiden by option DetectTimeout in the .igtrc file.
> + */
> +#define DEFAULT_DETECT_TIMEOUT 10.0
> +
>  /* Low-level helpers with kmstest_ prefix */
>  
>  /**
> @@ -1289,4 +1297,6 @@ void igt_get_and_wait_out_fence(igt_output_t *output);
>  
>  igt_colorop_t *igt_find_colorop(igt_display_t *display, uint32_t id);
>  
> +double igt_default_display_detect_timeout(void);
> +
>  #endif /* __IGT_KMS_H__ */

-- 
Jani Nikula, Intel

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH i-g-t v1 01/25] lib/igt_kms: Add a detect timeout value
  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
  2 siblings, 0 replies; 37+ messages in thread
From: Jani Nikula @ 2026-04-28  7:17 UTC (permalink / raw)
  To: Mohammed Bilal, igt-dev
  Cc: kunal1.joshi, Louis Chauvet, Kory Maincent, Luca Ceresoli

On Tue, 28 Apr 2026, Mohammed Bilal <mohammed.bilal@intel.com> wrote:
> From: Louis Chauvet <louis.chauvet@bootlin.com>
>
> Some tests need to wait for a specific connector status. In order to make
> the timeout customisable for each target, add an option in the
> configuration file.
>
> Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
> Reviewed-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>

Oh, one more thing. You need to add your Signed-off-by too when sending
someone else's patches.

BR,
Jani.


-- 
Jani Nikula, Intel

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH i-g-t v1 05/25] lib/igt_kms: Add function to get valid pipe for specific output
  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
  0 siblings, 0 replies; 37+ messages in thread
From: Jani Nikula @ 2026-04-28  7:21 UTC (permalink / raw)
  To: Mohammed Bilal, igt-dev; +Cc: kunal1.joshi, Louis Chauvet

On Tue, 28 Apr 2026, Mohammed Bilal <mohammed.bilal@intel.com> wrote:
> From: Louis Chauvet <louis.chauvet@bootlin.com>
>
> Introduces a new function igt_get_pipe_for_output in igt_kms. The function
> is designed to retrieve a valid pipe for a specific output in a display.

Please avoid using pipes. Why would you need the pipe here?

The function isn't even used in the entire series.

>
> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> ---
>  lib/igt_kms.c | 22 ++++++++++++++++++++++
>  lib/igt_kms.h |  1 +
>  2 files changed, 23 insertions(+)
>
> diff --git a/lib/igt_kms.c b/lib/igt_kms.c
> index aa086e96c..73d65f70a 100644
> --- a/lib/igt_kms.c
> +++ b/lib/igt_kms.c
> @@ -8426,3 +8426,25 @@ uint32_t igt_get_connector_id_from_mst_path(int drm_fd, const void *mst_path)
>  
>  	return 0;
>  }
> +
> +/**
> + * igt_get_pipe_for_output:
> + * @display: display to fetch the pipes
> + * @output: output to use
> + *
> + * Get a valid pipe for a specific output. The return value is the pipe first valid pipe for a
> + * specific output.
> + */
> +enum pipe igt_get_pipe_for_output(igt_display_t *display,
> +				  igt_output_t *output)
> +{
> +        igt_crtc_t *crtc;
> +
> +        for_each_crtc(display, crtc) {
> +                if (igt_output_is_connected(output) &&
> +                     (output->config.valid_crtc_index_mask & (1 << crtc->crtc_index)))
> +                        return crtc->pipe;
> +        }
> +
> +        return PIPE_NONE;
> +}
> diff --git a/lib/igt_kms.h b/lib/igt_kms.h
> index bc921f4ff..70b7d1a58 100644
> --- a/lib/igt_kms.h
> +++ b/lib/igt_kms.h
> @@ -1305,5 +1305,6 @@ int igt_get_connected_connectors(int drm_fd, uint32_t **connector_ids);
>  drmModeConnectorPtr igt_get_connector_from_name(int drm_fd, const char *port_name);
>  uint32_t igt_get_connector_id_from_name(int drm_fd, const char *port_name);
>  uint32_t igt_get_connector_id_from_mst_path(int drm_fd, const void *mst_path);
> +enum pipe igt_get_pipe_for_output(igt_display_t *display, igt_output_t *output);
>  
>  #endif /* __IGT_KMS_H__ */

-- 
Jani Nikula, Intel

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH i-g-t v1 07/25] lib/monitor_edids: Add helper to get an EDID by its name
  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
  0 siblings, 0 replies; 37+ messages in thread
From: Jani Nikula @ 2026-04-28  7:23 UTC (permalink / raw)
  To: Mohammed Bilal, igt-dev
  Cc: kunal1.joshi, Louis Chauvet, Kory Maincent, Luca Ceresoli

On Tue, 28 Apr 2026, Mohammed Bilal <mohammed.bilal@intel.com> wrote:
> From: Louis Chauvet <louis.chauvet@bootlin.com>
>
> For testing specific EDID, it is useful to be able to retrieve an EDID by
> a verbose name.
>
> Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
> Reviewed-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> ---
>  lib/monitor_edids/monitor_edids_helper.c | 30 ++++++++++++++++++++++++
>  lib/monitor_edids/monitor_edids_helper.h |  1 +
>  2 files changed, 31 insertions(+)
>
> diff --git a/lib/monitor_edids/monitor_edids_helper.c b/lib/monitor_edids/monitor_edids_helper.c
> index 6043e7a08..01feb34a5 100644
> --- a/lib/monitor_edids/monitor_edids_helper.c
> +++ b/lib/monitor_edids/monitor_edids_helper.c
> @@ -20,6 +20,16 @@
>  #include "drmtest.h"
>  #include "hdmi_edids.h"
>  
> +struct {
> +	struct monitor_edid *edid_list;
> +	int list_size;
> +} ALL_EDIDS[] = {
> +	{DP_EDIDS_NON_4K,	DP_EDIDS_NON_4K_COUNT},
> +	{DP_EDIDS_4K,		DP_EDIDS_4K_COUNT},
> +	{HDMI_EDIDS_NON_4K,	HDMI_EDIDS_NON_4K_COUNT},
> +	{HDMI_EDIDS_4K,		HDMI_EDIDS_4K_COUNT},
> +};

static const?

> +
>  static uint8_t convert_hex_char_to_byte(char c)
>  {
>  	if (c >= '0' && c <= '9')
> @@ -170,3 +180,23 @@ struct monitor_edid *get_edids_for_connector_type(uint32_t type, size_t *count,
>  		}
>  	}
>  }
> +
> +/**
> + * get_edid_by_name:
> + * @name: Name to search in available EDIDs
> + *
> + * Return the struct edid associated with a specific name. As with edid_from_monitor_edid, the
> + * caller must ensure to free the EDID after use. If no EDID with the exact name is found, returns
> + * NULL.
> + */
> +struct edid *get_edid_by_name(const char *name)

const?

> +{
> +	for (int i = 0; i < ARRAY_SIZE(ALL_EDIDS); i++) {
> +		for (int j = 0; j < ALL_EDIDS[i].list_size; j++) {
> +			if (strcmp(ALL_EDIDS[i].edid_list[j].name, name) == 0)
> +				return edid_from_monitor_edid(&ALL_EDIDS[i].edid_list[j]);
> +		}
> +	}
> +
> +	return NULL;
> +}
> diff --git a/lib/monitor_edids/monitor_edids_helper.h b/lib/monitor_edids/monitor_edids_helper.h
> index e50698686..6d9e3fc89 100644
> --- a/lib/monitor_edids/monitor_edids_helper.h
> +++ b/lib/monitor_edids/monitor_edids_helper.h
> @@ -34,5 +34,6 @@ void free_chamelium_edid_from_monitor_edid(struct chamelium_edid *edid);
>  
>  struct edid *edid_from_monitor_edid(const monitor_edid *monitor_edid);
>  struct monitor_edid *get_edids_for_connector_type(uint32_t type, size_t *count, bool four_k);
> +struct edid *get_edid_by_name(const char *name);
>  
>  #endif /* TESTS_CHAMELIUM_MONITOR_EDIDS_MONITOR_EDIDS_HELPER_H_ */

-- 
Jani Nikula, Intel

^ permalink raw reply	[flat|nested] 37+ messages in thread

* ✗ Xe.CI.FULL: failure for Chamelium v3 Integration and Test Execution
  2026-04-28  4:46 [PATCH i-g-t v1 00/25] Chamelium v3 Integration and Test Execution Mohammed Bilal
                   ` (26 preceding siblings ...)
  2026-04-28  6:09 ` ✗ i915.CI.BAT: failure " Patchwork
@ 2026-04-28 12:36 ` Patchwork
  2026-04-28 14:02 ` [PATCH i-g-t v1 00/25] " Louis Chauvet
  28 siblings, 0 replies; 37+ messages in thread
From: Patchwork @ 2026-04-28 12:36 UTC (permalink / raw)
  To: Mohammed Bilal; +Cc: igt-dev

[-- Attachment #1: Type: text/plain, Size: 35312 bytes --]

== Series Details ==

Series: Chamelium v3 Integration and Test Execution
URL   : https://patchwork.freedesktop.org/series/165589/
State : failure

== Summary ==

CI Bug Log - changes from XEIGT_8874_FULL -> XEIGTPW_15069_FULL
====================================================

Summary
-------

  **WARNING**

  Minor unknown changes coming with XEIGTPW_15069_FULL need to be verified
  manually.
  
  If you think the reported changes have nothing to do with the changes
  introduced in XEIGTPW_15069_FULL, please notify your bug team (I915-ci-infra@lists.freedesktop.org) to allow them
  to document this new failure mode, which will reduce false positives in CI.

  

Participating hosts (2 -> 2)
------------------------------

  No changes in participating hosts

Possible new issues
-------------------

  Here are the unknown changes that may have been introduced in XEIGTPW_15069_FULL:

### IGT changes ###

#### Possible regressions ####

  * {igt@kms_chamelium_v3_basic@chamelium-list-ports} (NEW):
    - shard-lnl:          NOTRUN -> [CRASH][1]
   [1]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-lnl-7/igt@kms_chamelium_v3_basic@chamelium-list-ports.html
    - shard-bmg:          NOTRUN -> [CRASH][2]
   [2]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-8/igt@kms_chamelium_v3_basic@chamelium-list-ports.html

  * {igt@kms_chamelium_v3_color@ctm-0-25} (NEW):
    - shard-bmg:          NOTRUN -> [SKIP][3] +32 other tests skip
   [3]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-6/igt@kms_chamelium_v3_color@ctm-0-25.html

  * {igt@kms_chamelium_v3_hpd@dp-hpd-fast} (NEW):
    - shard-lnl:          NOTRUN -> [SKIP][4] +41 other tests skip
   [4]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-lnl-3/igt@kms_chamelium_v3_hpd@dp-hpd-fast.html

  
#### Warnings ####

  * igt@kms_chamelium_color@ctm-0-25:
    - shard-bmg:          [SKIP][5] ([Intel XE#2325] / [Intel XE#7358]) -> [SKIP][6] +10 other tests skip
   [5]: https://intel-gfx-ci.01.org/tree/intel-xe/IGT_8874/shard-bmg-2/igt@kms_chamelium_color@ctm-0-25.html
   [6]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-3/igt@kms_chamelium_color@ctm-0-25.html

  * igt@kms_chamelium_sharpness_filter@filter-basic:
    - shard-bmg:          [SKIP][7] ([Intel XE#6507]) -> [SKIP][8]
   [7]: https://intel-gfx-ci.01.org/tree/intel-xe/IGT_8874/shard-bmg-2/igt@kms_chamelium_sharpness_filter@filter-basic.html
   [8]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-9/igt@kms_chamelium_sharpness_filter@filter-basic.html
    - shard-lnl:          [SKIP][9] ([Intel XE#6507]) -> [SKIP][10]
   [9]: https://intel-gfx-ci.01.org/tree/intel-xe/IGT_8874/shard-lnl-1/igt@kms_chamelium_sharpness_filter@filter-basic.html
   [10]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-lnl-7/igt@kms_chamelium_sharpness_filter@filter-basic.html

  
New tests
---------

  New tests have been introduced between XEIGT_8874_FULL and XEIGTPW_15069_FULL:

### New IGT tests (43) ###

  * igt@kms_chamelium_v3_basic@chamelium-list-ports:
    - Statuses : 2 crash(s)
    - Exec time: [0.01] s

  * igt@kms_chamelium_v3_color@ctm-0-25:
    - Statuses : 2 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_color@ctm-0-50:
    - Statuses : 2 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_color@ctm-0-75:
    - Statuses : 2 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_color@ctm-blue-to-red:
    - Statuses : 2 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_color@ctm-green-to-red:
    - Statuses : 1 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_color@ctm-limited-range:
    - Statuses : 2 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_color@ctm-max:
    - Statuses : 2 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_color@ctm-negative:
    - Statuses : 2 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_color@ctm-red-to-blue:
    - Statuses : 2 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_color@degamma:
    - Statuses : 2 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_color@gamma:
    - Statuses : 2 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_frames@dp-crc-fast:
    - Statuses : 2 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_frames@dp-crc-multiple:
    - Statuses : 2 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_frames@dp-crc-single:
    - Statuses : 1 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_frames@dp-frame-dump:
    - Statuses : 2 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_frames@hdmi-aspect-ratio:
    - Statuses : 2 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_frames@hdmi-cmp-planar-formats:
    - Statuses : 1 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_frames@hdmi-cmp-planes-random:
    - Statuses : 2 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_frames@hdmi-crc-fast:
    - Statuses : 1 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_frames@hdmi-crc-multiple:
    - Statuses : 2 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_frames@hdmi-crc-nonplanar-formats:
    - Statuses : 2 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_frames@hdmi-crc-planes-random:
    - Statuses : 2 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_frames@hdmi-crc-single:
    - Statuses : 1 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_frames@hdmi-frame-dump:
    - Statuses : 2 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_hpd@dp-hpd:
    - Statuses : 2 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_hpd@dp-hpd-after-hibernate:
    - Statuses : 2 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_hpd@dp-hpd-after-suspend:
    - Statuses : 2 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_hpd@dp-hpd-enable-disable-mode:
    - Statuses : 2 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_hpd@dp-hpd-fast:
    - Statuses : 2 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_hpd@dp-hpd-for-each-pipe:
    - Statuses : 2 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_hpd@dp-hpd-storm:
    - Statuses : 1 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_hpd@dp-hpd-storm-disable:
    - Statuses : 1 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_hpd@dp-hpd-with-enabled-mode:
    - Statuses : 2 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_hpd@hdmi-hpd:
    - Statuses : 2 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_hpd@hdmi-hpd-after-hibernate:
    - Statuses : 2 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_hpd@hdmi-hpd-after-suspend:
    - Statuses : 2 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_hpd@hdmi-hpd-enable-disable-mode:
    - Statuses : 1 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_hpd@hdmi-hpd-fast:
    - Statuses : 2 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_hpd@hdmi-hpd-for-each-pipe:
    - Statuses : 2 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_hpd@hdmi-hpd-storm:
    - Statuses : 2 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_hpd@hdmi-hpd-storm-disable:
    - Statuses : 1 skip(s)
    - Exec time: [0.0] s

  * igt@kms_chamelium_v3_hpd@hdmi-hpd-with-enabled-mode:
    - Statuses : 2 skip(s)
    - Exec time: [0.0] s

  

Known issues
------------

  Here are the changes found in XEIGTPW_15069_FULL that come from known issues:

### IGT changes ###

#### Issues hit ####

  * igt@kms_atomic_transition@plane-all-modeset-transition-fencing-internal-panels:
    - shard-bmg:          NOTRUN -> [SKIP][11] ([Intel XE#2370])
   [11]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-8/igt@kms_atomic_transition@plane-all-modeset-transition-fencing-internal-panels.html

  * igt@kms_big_fb:
    - shard-bmg:          NOTRUN -> [INCOMPLETE][12] ([Intel XE#2594])
   [12]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-2/igt@kms_big_fb.html

  * igt@kms_big_fb@4-tiled-64bpp-rotate-90:
    - shard-bmg:          NOTRUN -> [SKIP][13] ([Intel XE#2327]) +4 other tests skip
   [13]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-7/igt@kms_big_fb@4-tiled-64bpp-rotate-90.html

  * igt@kms_big_fb@y-tiled-max-hw-stride-64bpp-rotate-0-hflip:
    - shard-bmg:          NOTRUN -> [SKIP][14] ([Intel XE#1124]) +9 other tests skip
   [14]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-10/igt@kms_big_fb@y-tiled-max-hw-stride-64bpp-rotate-0-hflip.html

  * igt@kms_big_fb@yf-tiled-addfb-size-overflow:
    - shard-bmg:          NOTRUN -> [SKIP][15] ([Intel XE#610] / [Intel XE#7387])
   [15]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-6/igt@kms_big_fb@yf-tiled-addfb-size-overflow.html

  * igt@kms_ccs@bad-pixel-format-4-tiled-dg2-mc-ccs:
    - shard-bmg:          NOTRUN -> [SKIP][16] ([Intel XE#2887]) +14 other tests skip
   [16]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-1/igt@kms_ccs@bad-pixel-format-4-tiled-dg2-mc-ccs.html

  * igt@kms_ccs@crc-primary-suspend-4-tiled-lnl-ccs@pipe-d-hdmi-a-3:
    - shard-bmg:          NOTRUN -> [SKIP][17] ([Intel XE#2652]) +17 other tests skip
   [17]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-8/igt@kms_ccs@crc-primary-suspend-4-tiled-lnl-ccs@pipe-d-hdmi-a-3.html

  * igt@kms_ccs@crc-primary-suspend-y-tiled-gen12-rc-ccs-cc:
    - shard-bmg:          NOTRUN -> [SKIP][18] ([Intel XE#3432]) +1 other test skip
   [18]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-7/igt@kms_ccs@crc-primary-suspend-y-tiled-gen12-rc-ccs-cc.html

  * igt@kms_cdclk@mode-transition-all-outputs:
    - shard-bmg:          NOTRUN -> [SKIP][19] ([Intel XE#2724] / [Intel XE#7449]) +1 other test skip
   [19]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-9/igt@kms_cdclk@mode-transition-all-outputs.html

  * igt@kms_chamelium_hpd@hdmi-hpd-after-suspend:
    - shard-bmg:          NOTRUN -> [SKIP][20] ([Intel XE#2252]) +5 other tests skip
   [20]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-3/igt@kms_chamelium_hpd@hdmi-hpd-after-suspend.html

  * igt@kms_content_protection@dp-mst-type-0-hdcp14:
    - shard-bmg:          NOTRUN -> [SKIP][21] ([Intel XE#6974])
   [21]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-1/igt@kms_content_protection@dp-mst-type-0-hdcp14.html

  * igt@kms_content_protection@type1:
    - shard-bmg:          NOTRUN -> [SKIP][22] ([Intel XE#7642])
   [22]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-10/igt@kms_content_protection@type1.html

  * igt@kms_cursor_crc@cursor-offscreen-32x10:
    - shard-bmg:          NOTRUN -> [SKIP][23] ([Intel XE#2320]) +3 other tests skip
   [23]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-9/igt@kms_cursor_crc@cursor-offscreen-32x10.html

  * igt@kms_cursor_crc@cursor-offscreen-512x170:
    - shard-bmg:          NOTRUN -> [SKIP][24] ([Intel XE#2321] / [Intel XE#7355]) +1 other test skip
   [24]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-1/igt@kms_cursor_crc@cursor-offscreen-512x170.html

  * igt@kms_cursor_legacy@cursora-vs-flipb-varying-size:
    - shard-bmg:          NOTRUN -> [DMESG-WARN][25] ([Intel XE#5354])
   [25]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-10/igt@kms_cursor_legacy@cursora-vs-flipb-varying-size.html

  * igt@kms_dirtyfb@psr-dirtyfb-ioctl:
    - shard-bmg:          NOTRUN -> [SKIP][26] ([Intel XE#1508])
   [26]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-8/igt@kms_dirtyfb@psr-dirtyfb-ioctl.html

  * igt@kms_dither@fb-8bpc-vs-panel-6bpc@pipe-a-hdmi-a-3:
    - shard-bmg:          NOTRUN -> [SKIP][27] ([Intel XE#1340] / [Intel XE#7435])
   [27]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-9/igt@kms_dither@fb-8bpc-vs-panel-6bpc@pipe-a-hdmi-a-3.html

  * igt@kms_fbc_dirty_rect@fbc-dirty-rectangle-dirtyfb-tests:
    - shard-bmg:          NOTRUN -> [SKIP][28] ([Intel XE#4422] / [Intel XE#7442])
   [28]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-9/igt@kms_fbc_dirty_rect@fbc-dirty-rectangle-dirtyfb-tests.html

  * igt@kms_fbcon_fbt@psr-suspend:
    - shard-bmg:          NOTRUN -> [SKIP][29] ([Intel XE#6126] / [Intel XE#776])
   [29]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-7/igt@kms_fbcon_fbt@psr-suspend.html

  * igt@kms_feature_discovery@display-3x:
    - shard-bmg:          NOTRUN -> [SKIP][30] ([Intel XE#2373] / [Intel XE#7448])
   [30]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-3/igt@kms_feature_discovery@display-3x.html

  * igt@kms_feature_discovery@psr2:
    - shard-bmg:          NOTRUN -> [SKIP][31] ([Intel XE#2374] / [Intel XE#6128])
   [31]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-3/igt@kms_feature_discovery@psr2.html

  * igt@kms_flip_scaled_crc@flip-32bpp-yftileccs-to-64bpp-yftile-upscaling:
    - shard-bmg:          NOTRUN -> [SKIP][32] ([Intel XE#7178] / [Intel XE#7351]) +2 other tests skip
   [32]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-3/igt@kms_flip_scaled_crc@flip-32bpp-yftileccs-to-64bpp-yftile-upscaling.html

  * igt@kms_flip_scaled_crc@flip-nv12-linear-to-nv12-linear-reflect-x:
    - shard-bmg:          NOTRUN -> [SKIP][33] ([Intel XE#7179]) +1 other test skip
   [33]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-3/igt@kms_flip_scaled_crc@flip-nv12-linear-to-nv12-linear-reflect-x.html

  * igt@kms_frontbuffer_tracking@fbc-2p-scndscrn-spr-indfb-draw-mmap-wc:
    - shard-bmg:          NOTRUN -> [SKIP][34] ([Intel XE#4141]) +16 other tests skip
   [34]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-8/igt@kms_frontbuffer_tracking@fbc-2p-scndscrn-spr-indfb-draw-mmap-wc.html

  * igt@kms_frontbuffer_tracking@fbcdrrs-2p-scndscrn-pri-shrfb-draw-render:
    - shard-bmg:          NOTRUN -> [SKIP][35] ([Intel XE#2311]) +33 other tests skip
   [35]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-3/igt@kms_frontbuffer_tracking@fbcdrrs-2p-scndscrn-pri-shrfb-draw-render.html

  * igt@kms_frontbuffer_tracking@fbcdrrs-argb161616f-draw-render:
    - shard-bmg:          NOTRUN -> [SKIP][36] ([Intel XE#7061] / [Intel XE#7356]) +4 other tests skip
   [36]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-10/igt@kms_frontbuffer_tracking@fbcdrrs-argb161616f-draw-render.html

  * igt@kms_frontbuffer_tracking@plane-fbc-rte:
    - shard-bmg:          NOTRUN -> [SKIP][37] ([Intel XE#2350] / [Intel XE#7503])
   [37]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-7/igt@kms_frontbuffer_tracking@plane-fbc-rte.html

  * igt@kms_frontbuffer_tracking@psr-2p-scndscrn-spr-indfb-draw-mmap-wc:
    - shard-bmg:          NOTRUN -> [SKIP][38] ([Intel XE#2313]) +25 other tests skip
   [38]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-3/igt@kms_frontbuffer_tracking@psr-2p-scndscrn-spr-indfb-draw-mmap-wc.html

  * igt@kms_hdmi_inject@inject-audio:
    - shard-bmg:          NOTRUN -> [SKIP][39] ([Intel XE#7308])
   [39]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-10/igt@kms_hdmi_inject@inject-audio.html

  * igt@kms_joiner@basic-ultra-joiner:
    - shard-bmg:          NOTRUN -> [SKIP][40] ([Intel XE#6911] / [Intel XE#7378])
   [40]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-9/igt@kms_joiner@basic-ultra-joiner.html

  * igt@kms_joiner@invalid-modeset-force-ultra-joiner:
    - shard-bmg:          NOTRUN -> [SKIP][41] ([Intel XE#6911] / [Intel XE#7466])
   [41]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-7/igt@kms_joiner@invalid-modeset-force-ultra-joiner.html

  * igt@kms_panel_fitting@atomic-fastset:
    - shard-bmg:          NOTRUN -> [SKIP][42] ([Intel XE#2486])
   [42]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-9/igt@kms_panel_fitting@atomic-fastset.html

  * igt@kms_pipe_stress@stress-xrgb8888-yftiled:
    - shard-bmg:          NOTRUN -> [SKIP][43] ([Intel XE#6912] / [Intel XE#7375])
   [43]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-1/igt@kms_pipe_stress@stress-xrgb8888-yftiled.html

  * igt@kms_plane@pixel-format-4-tiled-dg2-rc-ccs-cc-modifier-source-clamping:
    - shard-bmg:          NOTRUN -> [SKIP][44] ([Intel XE#7283]) +5 other tests skip
   [44]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-3/igt@kms_plane@pixel-format-4-tiled-dg2-rc-ccs-cc-modifier-source-clamping.html

  * igt@kms_plane_lowres@tiling-y:
    - shard-bmg:          NOTRUN -> [SKIP][45] ([Intel XE#2393])
   [45]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-6/igt@kms_plane_lowres@tiling-y.html

  * igt@kms_plane_scaling@planes-upscale-factor-0-25-downscale-factor-0-75@pipe-b:
    - shard-bmg:          NOTRUN -> [SKIP][46] ([Intel XE#2763] / [Intel XE#6886]) +9 other tests skip
   [46]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-9/igt@kms_plane_scaling@planes-upscale-factor-0-25-downscale-factor-0-75@pipe-b.html

  * igt@kms_pm_backlight@brightness-with-dpms:
    - shard-bmg:          NOTRUN -> [SKIP][47] ([Intel XE#2938] / [Intel XE#7376])
   [47]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-3/igt@kms_pm_backlight@brightness-with-dpms.html

  * igt@kms_pm_dc@dc3co-vpb-simulation:
    - shard-bmg:          NOTRUN -> [SKIP][48] ([Intel XE#7794])
   [48]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-3/igt@kms_pm_dc@dc3co-vpb-simulation.html

  * igt@kms_pm_dc@deep-pkgc:
    - shard-bmg:          NOTRUN -> [SKIP][49] ([Intel XE#2505] / [Intel XE#7447])
   [49]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-1/igt@kms_pm_dc@deep-pkgc.html

  * igt@kms_psr2_sf@psr2-plane-move-sf-dmg-area:
    - shard-bmg:          NOTRUN -> [SKIP][50] ([Intel XE#1489]) +7 other tests skip
   [50]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-1/igt@kms_psr2_sf@psr2-plane-move-sf-dmg-area.html

  * igt@kms_psr2_su@page_flip-xrgb8888:
    - shard-bmg:          NOTRUN -> [SKIP][51] ([Intel XE#2387] / [Intel XE#7429])
   [51]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-10/igt@kms_psr2_su@page_flip-xrgb8888.html

  * igt@kms_psr@fbc-psr-dpms:
    - shard-bmg:          NOTRUN -> [SKIP][52] ([Intel XE#2234] / [Intel XE#2850]) +14 other tests skip
   [52]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-8/igt@kms_psr@fbc-psr-dpms.html

  * igt@kms_psr_stress_test@invalidate-primary-flip-overlay:
    - shard-bmg:          NOTRUN -> [SKIP][53] ([Intel XE#7795])
   [53]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-7/igt@kms_psr_stress_test@invalidate-primary-flip-overlay.html

  * igt@kms_rotation_crc@primary-rotation-270:
    - shard-bmg:          NOTRUN -> [SKIP][54] ([Intel XE#3904] / [Intel XE#7342]) +2 other tests skip
   [54]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-7/igt@kms_rotation_crc@primary-rotation-270.html

  * igt@kms_rotation_crc@primary-y-tiled-reflect-x-180:
    - shard-bmg:          NOTRUN -> [SKIP][55] ([Intel XE#2330] / [Intel XE#5813])
   [55]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-7/igt@kms_rotation_crc@primary-y-tiled-reflect-x-180.html

  * igt@kms_sharpness_filter@filter-tap:
    - shard-bmg:          NOTRUN -> [SKIP][56] ([Intel XE#6503]) +2 other tests skip
   [56]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-7/igt@kms_sharpness_filter@filter-tap.html

  * igt@kms_vrr@max-min:
    - shard-bmg:          NOTRUN -> [SKIP][57] ([Intel XE#1499]) +1 other test skip
   [57]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-9/igt@kms_vrr@max-min.html

  * igt@xe_eudebug@basic-vm-bind-metadata-discovery:
    - shard-bmg:          NOTRUN -> [SKIP][58] ([Intel XE#7636]) +12 other tests skip
   [58]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-3/igt@xe_eudebug@basic-vm-bind-metadata-discovery.html

  * igt@xe_evict@evict-mixed-many-threads-small:
    - shard-bmg:          [PASS][59] -> [INCOMPLETE][60] ([Intel XE#6321])
   [59]: https://intel-gfx-ci.01.org/tree/intel-xe/IGT_8874/shard-bmg-1/igt@xe_evict@evict-mixed-many-threads-small.html
   [60]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-10/igt@xe_evict@evict-mixed-many-threads-small.html

  * igt@xe_evict@evict-small-multi-queue-priority-cm:
    - shard-bmg:          NOTRUN -> [SKIP][61] ([Intel XE#7140])
   [61]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-8/igt@xe_evict@evict-small-multi-queue-priority-cm.html

  * igt@xe_exec_basic@multigpu-once-null-rebind:
    - shard-bmg:          NOTRUN -> [SKIP][62] ([Intel XE#2322] / [Intel XE#7372]) +9 other tests skip
   [62]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-3/igt@xe_exec_basic@multigpu-once-null-rebind.html

  * igt@xe_exec_fault_mode@once-multi-queue-userptr-invalidate-prefetch:
    - shard-bmg:          NOTRUN -> [SKIP][63] ([Intel XE#7136]) +9 other tests skip
   [63]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-10/igt@xe_exec_fault_mode@once-multi-queue-userptr-invalidate-prefetch.html

  * igt@xe_exec_multi_queue@two-queues-preempt-mode-fault-dyn-priority-smem:
    - shard-bmg:          NOTRUN -> [SKIP][64] ([Intel XE#6874]) +34 other tests skip
   [64]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-6/igt@xe_exec_multi_queue@two-queues-preempt-mode-fault-dyn-priority-smem.html

  * igt@xe_exec_threads@threads-multi-queue-mixed-userptr-invalidate-race:
    - shard-bmg:          NOTRUN -> [SKIP][65] ([Intel XE#7138]) +9 other tests skip
   [65]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-7/igt@xe_exec_threads@threads-multi-queue-mixed-userptr-invalidate-race.html

  * igt@xe_multigpu_svm@mgpu-concurrent-access-prefetch:
    - shard-bmg:          NOTRUN -> [SKIP][66] ([Intel XE#6964]) +4 other tests skip
   [66]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-6/igt@xe_multigpu_svm@mgpu-concurrent-access-prefetch.html

  * igt@xe_oa@oa-tlb-invalidate:
    - shard-bmg:          NOTRUN -> [SKIP][67] ([Intel XE#2248] / [Intel XE#7325] / [Intel XE#7393])
   [67]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-3/igt@xe_oa@oa-tlb-invalidate.html

  * igt@xe_page_reclaim@prl-max-entries:
    - shard-bmg:          NOTRUN -> [SKIP][68] ([Intel XE#7793])
   [68]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-3/igt@xe_page_reclaim@prl-max-entries.html

  * igt@xe_pat@xa-app-transient-media-on:
    - shard-bmg:          NOTRUN -> [SKIP][69] ([Intel XE#7590]) +1 other test skip
   [69]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-7/igt@xe_pat@xa-app-transient-media-on.html

  * igt@xe_peer2peer@read:
    - shard-bmg:          NOTRUN -> [SKIP][70] ([Intel XE#2427] / [Intel XE#6953] / [Intel XE#7326] / [Intel XE#7353])
   [70]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-6/igt@xe_peer2peer@read.html

  * igt@xe_pm@d3cold-basic-exec:
    - shard-bmg:          NOTRUN -> [SKIP][71] ([Intel XE#2284] / [Intel XE#7370])
   [71]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-2/igt@xe_pm@d3cold-basic-exec.html

  * igt@xe_pxp@pxp-optout:
    - shard-bmg:          NOTRUN -> [SKIP][72] ([Intel XE#4733] / [Intel XE#7417]) +3 other tests skip
   [72]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-7/igt@xe_pxp@pxp-optout.html

  * igt@xe_query@multigpu-query-invalid-query:
    - shard-bmg:          NOTRUN -> [SKIP][73] ([Intel XE#944]) +1 other test skip
   [73]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-6/igt@xe_query@multigpu-query-invalid-query.html

  * igt@xe_sriov_flr@flr-vfs-parallel:
    - shard-bmg:          [PASS][74] -> [FAIL][75] ([Intel XE#6569])
   [74]: https://intel-gfx-ci.01.org/tree/intel-xe/IGT_8874/shard-bmg-2/igt@xe_sriov_flr@flr-vfs-parallel.html
   [75]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-3/igt@xe_sriov_flr@flr-vfs-parallel.html

  
#### Possible fixes ####

  * igt@kms_flip@flip-vs-expired-vblank-interruptible:
    - shard-lnl:          [FAIL][76] ([Intel XE#301] / [Intel XE#3149]) -> [PASS][77] +1 other test pass
   [76]: https://intel-gfx-ci.01.org/tree/intel-xe/IGT_8874/shard-lnl-5/igt@kms_flip@flip-vs-expired-vblank-interruptible.html
   [77]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-lnl-6/igt@kms_flip@flip-vs-expired-vblank-interruptible.html

  * igt@kms_flip@flip-vs-suspend@a-dp2:
    - shard-bmg:          [INCOMPLETE][78] ([Intel XE#2049] / [Intel XE#2597]) -> [PASS][79] +1 other test pass
   [78]: https://intel-gfx-ci.01.org/tree/intel-xe/IGT_8874/shard-bmg-7/igt@kms_flip@flip-vs-suspend@a-dp2.html
   [79]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-10/igt@kms_flip@flip-vs-suspend@a-dp2.html

  * igt@kms_hdr@invalid-hdr:
    - shard-bmg:          [SKIP][80] ([Intel XE#1503]) -> [PASS][81]
   [80]: https://intel-gfx-ci.01.org/tree/intel-xe/IGT_8874/shard-bmg-10/igt@kms_hdr@invalid-hdr.html
   [81]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-7/igt@kms_hdr@invalid-hdr.html

  * igt@kms_pm_dc@dc5-psr:
    - shard-lnl:          [FAIL][82] ([Intel XE#7340]) -> [PASS][83]
   [82]: https://intel-gfx-ci.01.org/tree/intel-xe/IGT_8874/shard-lnl-6/igt@kms_pm_dc@dc5-psr.html
   [83]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-lnl-7/igt@kms_pm_dc@dc5-psr.html

  * igt@xe_survivability@runtime-survivability:
    - shard-bmg:          [DMESG-WARN][84] ([Intel XE#6627] / [Intel XE#7419]) -> [PASS][85]
   [84]: https://intel-gfx-ci.01.org/tree/intel-xe/IGT_8874/shard-bmg-8/igt@xe_survivability@runtime-survivability.html
   [85]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-bmg-1/igt@xe_survivability@runtime-survivability.html

  
#### Warnings ####

  * igt@kms_chamelium_color@ctm-blue-to-red:
    - shard-lnl:          [SKIP][86] ([Intel XE#306] / [Intel XE#7358]) -> [SKIP][87] ([Intel XE#306]) +10 other tests skip
   [86]: https://intel-gfx-ci.01.org/tree/intel-xe/IGT_8874/shard-lnl-1/igt@kms_chamelium_color@ctm-blue-to-red.html
   [87]: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/shard-lnl-5/igt@kms_chamelium_color@ctm-blue-to-red.html

  
  {name}: This element is suppressed. This means it is ignored when computing
          the status of the difference (SUCCESS, WARNING, or FAILURE).

  [Intel XE#1124]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/1124
  [Intel XE#1340]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/1340
  [Intel XE#1489]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/1489
  [Intel XE#1499]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/1499
  [Intel XE#1503]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/1503
  [Intel XE#1508]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/1508
  [Intel XE#2049]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2049
  [Intel XE#2234]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2234
  [Intel XE#2248]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2248
  [Intel XE#2252]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2252
  [Intel XE#2284]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2284
  [Intel XE#2311]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2311
  [Intel XE#2313]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2313
  [Intel XE#2320]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2320
  [Intel XE#2321]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2321
  [Intel XE#2322]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2322
  [Intel XE#2325]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2325
  [Intel XE#2327]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2327
  [Intel XE#2330]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2330
  [Intel XE#2350]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2350
  [Intel XE#2370]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2370
  [Intel XE#2373]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2373
  [Intel XE#2374]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2374
  [Intel XE#2387]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2387
  [Intel XE#2393]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2393
  [Intel XE#2427]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2427
  [Intel XE#2486]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2486
  [Intel XE#2505]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2505
  [Intel XE#2594]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2594
  [Intel XE#2597]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2597
  [Intel XE#2652]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2652
  [Intel XE#2724]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2724
  [Intel XE#2763]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2763
  [Intel XE#2850]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2850
  [Intel XE#2887]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2887
  [Intel XE#2938]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/2938
  [Intel XE#301]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/301
  [Intel XE#306]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/306
  [Intel XE#3149]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/3149
  [Intel XE#3432]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/3432
  [Intel XE#367]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/367
  [Intel XE#3904]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/3904
  [Intel XE#4141]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/4141
  [Intel XE#4422]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/4422
  [Intel XE#4733]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/4733
  [Intel XE#5354]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/5354
  [Intel XE#5813]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/5813
  [Intel XE#610]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/610
  [Intel XE#6126]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/6126
  [Intel XE#6128]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/6128
  [Intel XE#6321]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/6321
  [Intel XE#6503]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/6503
  [Intel XE#6507]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/6507
  [Intel XE#6569]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/6569
  [Intel XE#6627]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/6627
  [Intel XE#6874]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/6874
  [Intel XE#6886]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/6886
  [Intel XE#6911]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/6911
  [Intel XE#6912]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/6912
  [Intel XE#6953]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/6953
  [Intel XE#6964]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/6964
  [Intel XE#6974]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/6974
  [Intel XE#7061]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7061
  [Intel XE#7136]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7136
  [Intel XE#7138]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7138
  [Intel XE#7140]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7140
  [Intel XE#7178]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7178
  [Intel XE#7179]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7179
  [Intel XE#7283]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7283
  [Intel XE#7308]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7308
  [Intel XE#7325]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7325
  [Intel XE#7326]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7326
  [Intel XE#7340]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7340
  [Intel XE#7342]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7342
  [Intel XE#7351]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7351
  [Intel XE#7353]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7353
  [Intel XE#7355]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7355
  [Intel XE#7356]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7356
  [Intel XE#7358]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7358
  [Intel XE#7370]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7370
  [Intel XE#7372]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7372
  [Intel XE#7375]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7375
  [Intel XE#7376]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7376
  [Intel XE#7378]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7378
  [Intel XE#7387]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7387
  [Intel XE#7393]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7393
  [Intel XE#7417]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7417
  [Intel XE#7419]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7419
  [Intel XE#7429]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7429
  [Intel XE#7435]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7435
  [Intel XE#7442]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7442
  [Intel XE#7447]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7447
  [Intel XE#7448]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7448
  [Intel XE#7449]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7449
  [Intel XE#7466]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7466
  [Intel XE#7503]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7503
  [Intel XE#7590]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7590
  [Intel XE#7636]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7636
  [Intel XE#7642]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7642
  [Intel XE#7679]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7679
  [Intel XE#776]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/776
  [Intel XE#7793]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7793
  [Intel XE#7794]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7794
  [Intel XE#7795]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/7795
  [Intel XE#944]: https://gitlab.freedesktop.org/drm/xe/kernel/issues/944


Build changes
-------------

  * IGT: IGT_8874 -> IGTPW_15069
  * Linux: xe-4940-b6f6b69b2dffa9ad1c43b2149786b4630d41acbf -> xe-4944-aea2c496abcf55b647c14fe720bfc4ea555aac6a

  IGTPW_15069: 86ce060bb02fc7065ec1bcf1e3cb2878b72ff614 @ https://gitlab.freedesktop.org/drm/igt-gpu-tools.git
  IGT_8874: 4568b2c141ab630c34f8eb2b9afab8cbf8f3ce9e @ https://gitlab.freedesktop.org/drm/igt-gpu-tools.git
  xe-4940-b6f6b69b2dffa9ad1c43b2149786b4630d41acbf: b6f6b69b2dffa9ad1c43b2149786b4630d41acbf
  xe-4944-aea2c496abcf55b647c14fe720bfc4ea555aac6a: aea2c496abcf55b647c14fe720bfc4ea555aac6a

== Logs ==

For more details see: https://intel-gfx-ci.01.org/tree/intel-xe/IGTPW_15069/index.html

[-- Attachment #2: Type: text/html, Size: 38780 bytes --]

^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH i-g-t v1 00/25] Chamelium v3 Integration and Test Execution
  2026-04-28  4:46 [PATCH i-g-t v1 00/25] Chamelium v3 Integration and Test Execution Mohammed Bilal
                   ` (27 preceding siblings ...)
  2026-04-28 12:36 ` ✗ Xe.CI.FULL: " Patchwork
@ 2026-04-28 14:02 ` Louis Chauvet
  2026-04-29  3:36   ` Bilal, Mohammed
  28 siblings, 1 reply; 37+ messages in thread
From: Louis Chauvet @ 2026-04-28 14:02 UTC (permalink / raw)
  To: Mohammed Bilal, igt-dev
  Cc: kunal1.joshi, Kory Maincent, Luca Ceresoli, Kamil Konieczny

+CC: Kory, Luca, Jani, Kamil

Hello Mohammed,

I'm happy for you to reuse my series, as I no longer have the time to 
work on it (my priorities have shifted to Unigraf support). I'd be glad 
to review your series.

Before I do, could you double-check the patches you sent? I noticed a 
few issues with the igt_kms patches:
- Indentation and Signed-off-by issues reported by Jani.
- Some patches may not have been reviewed by Köry and Luca. Could you 
verify which version you used and ensure the correct reviews are 
included? For example, in [1], Luca's review appears valid (added in 
[2]), but Köry's review is not (added in [3], which is a different version).

@Jani, @Kamil, @Luca, and @Kory have already reviewed the 
igt_kms/monitor_edids patches [4] (newer revision). To avoid duplicate 
reviews, it would be great if we could streamline this. Did you need to 
make changes for your series? Can you underline them so I can integrate 
them in [4].

@Kamil, @Jani: You’ve already provided feedback in [4]. Would you be 
open to merging those patches so Mohammed can reuse them without 
duplicating the review process? Or would you prefer a separate series?

Thanks a lot,
Louis chauvet

[1]:https://lore.kernel.org/all/20260428044644.257001-2-mohammed.bilal@intel.com/
[2]:https://lore.kernel.org/all/20260128-unigraf-integration-v5-1-fbf2e997a011@bootlin.com/
[3]:https://lore.kernel.org/all/20260316-unigraf-integration-v8-1-4489bf8572be@bootlin.com/




On 4/28/26 06:46, Mohammed Bilal wrote:
> Integrate Chamelium v3 hardware support for display testing. This includes
> the Chamelium v3 library implementation, building with v3 support enabled,
> and comprehensive display tests for HPD, EDID, frames, color, and audio.
> 
> Changes included:
> 
> 1. Library helpers (patches 1-10):
>     - Add connector detection and management helpers
>     - Add monitor EDID management helpers
>     - Add HDMI 4K EDID support
> 
> 2. V2 reorganization (patches 11-14):
>     - Move existing V2 code to lib/chamelium/v2/ and tests/chamelium/v2/
>     - Rename V2 symbols to allow V2 and V3 to coexist
> 
> 3. V3 library (patches 15-20):
>     - Add Chamelium V3 library implementation
>     - Support for HPD, EDID, frame capture, color and audio
> 
> 4. V3 tests (patches 21-25):
>     - kms_chamelium_v3_basic: Basic connectivity test
>     - kms_chamelium_v3_hpd: Hot plug detect tests
>     - kms_chamelium_v3_edid: EDID verification tests
>     - kms_chamelium_v3_frames: Frame capture and CRC tests
>     - kms_chamelium_v3_color: Color verification tests
>     - kms_chamelium_v3_audio: Audio tests
> 
> Louis Chauvet (19):
>    lib/igt_kms: Add a detect timeout value
>    lib/igt_kms: Add helper to wait for a specific status on a connector
>    lib/igt_kms: Add function to list connected connectors
>    lib/igt_kms: Add helper to obtain a connector by its name or MST path
>    lib/igt_kms: Add function to get valid pipe for specific output
>    lib/monitor_edids: Add helper functions for using monitor_edid objects
>    lib/monitor_edids: Add helper to get an EDID by its name
>    lib/monitor_edids: Add helper to print all available EDID names
>    lib/monitor_edids: Fix missing names in some monitor EDID
>    lib/monitor_edids: Add new EDID for HDMI 4k
>    tests/chamelium: Extract Chamelium v2 tests into a separate directory
>    lib/chamelium/v2: Extract chamelium v2 wrapper into its own directory
>    lib/chamelium/v2: Rename chamelium to chamelium_v2
>    lib/chamelium/v2: Rename HAVE_CHAMELIUM to HAVE_CHAMELIUM_V2
>    lib/chamelium/v3: Introduce the foundation for the Chamelium v3
>      wrapper
>    lib/chamelium/v3: Introduce initialization and cleanup of
>      Chamelium-related structures
>    lib/chamelium/v3: Add method to discover Chamelium ports
>    lib/chamelium/v3: Implement method to retrieve Chamelium port names
>    tests/chamelium/v3: Implement a basic Chamelium v3 accessibility test
> 
> Mohammed Bilal (6):
>    lib/chamelium/v3: Add extended API for Chamelium v3 HPD, EDID, Frames,
>      Color & Audio
>    tests/chamelium/v3: Add HPD (Hot Plug Detect) tests for Chamelium v3
>    tests/chamelium/v3: Add EDID tests for Chamelium v3
>    tests/chamelium/v3: Add frame capture and CRC tests for Chamelium v3
>    tests/chamelium/v3: Add color verification tests for Chamelium v3
>    tests/chamelium/v3: Add audio tests for Chamelium v3
> 
>   .gitlab-ci.yml                                |    2 +-
>   docs/testplan/meson.build                     |    4 +-
>   lib/{ => chamelium/v2}/igt_chamelium.c        |    2 +-
>   lib/{ => chamelium/v2}/igt_chamelium.h        |    0
>   lib/{ => chamelium/v2}/igt_chamelium_stream.c |    2 +-
>   lib/{ => chamelium/v2}/igt_chamelium_stream.h |    0
>   lib/chamelium/v3/igt_chamelium.c              | 1267 ++++++++++++++
>   lib/chamelium/v3/igt_chamelium.h              |  247 +++
>   lib/igt.h                                     |    6 +-
>   lib/igt_core.c                                |    4 +
>   lib/igt_kms.c                                 |  371 +++-
>   lib/igt_kms.h                                 |   18 +
>   lib/meson.build                               |   21 +-
>   lib/monitor_edids/dp_edids.h                  |    3 +
>   lib/monitor_edids/hdmi_edids.h                |   27 +-
>   lib/monitor_edids/monitor_edids_helper.c      |  127 ++
>   lib/monitor_edids/monitor_edids_helper.h      |   12 +-
>   lib/tests/meson.build                         |    8 +-
>   meson.build                                   |   31 +-
>   meson_options.txt                             |    8 +-
>   .../chamelium/{ => v2}/kms_chamelium_audio.c  |    0
>   .../chamelium/{ => v2}/kms_chamelium_color.c  |    0
>   tests/chamelium/{ => v2}/kms_chamelium_edid.c |    2 +-
>   .../chamelium/{ => v2}/kms_chamelium_frames.c |    0
>   .../chamelium/{ => v2}/kms_chamelium_helper.c |    0
>   .../chamelium/{ => v2}/kms_chamelium_helper.h |    0
>   tests/chamelium/{ => v2}/kms_chamelium_hpd.c  |    0
>   .../{ => v2}/kms_chamelium_sharpness_filter.c |    0
>   tests/chamelium/v3/kms_chamelium_v3_audio.c   |  707 ++++++++
>   tests/chamelium/v3/kms_chamelium_v3_basic.c   |   30 +
>   tests/chamelium/v3/kms_chamelium_v3_color.c   | 1058 +++++++++++
>   tests/chamelium/v3/kms_chamelium_v3_edid.c    | 1357 ++++++++++++++
>   tests/chamelium/v3/kms_chamelium_v3_frames.c  | 1551 +++++++++++++++++
>   tests/chamelium/v3/kms_chamelium_v3_hpd.c     |  928 ++++++++++
>   tests/kms_color_helper.h                      |    2 +-
>   tests/kms_feature_discovery.c                 |    6 +-
>   tests/kms_tiled_display.c                     |    6 +-
>   tests/meson.build                             |   53 +-
>   38 files changed, 7762 insertions(+), 98 deletions(-)
>   rename lib/{ => chamelium/v2}/igt_chamelium.c (99%)
>   rename lib/{ => chamelium/v2}/igt_chamelium.h (100%)
>   rename lib/{ => chamelium/v2}/igt_chamelium_stream.c (99%)
>   rename lib/{ => chamelium/v2}/igt_chamelium_stream.h (100%)
>   create mode 100644 lib/chamelium/v3/igt_chamelium.c
>   create mode 100644 lib/chamelium/v3/igt_chamelium.h
>   rename tests/chamelium/{ => v2}/kms_chamelium_audio.c (100%)
>   rename tests/chamelium/{ => v2}/kms_chamelium_color.c (100%)
>   rename tests/chamelium/{ => v2}/kms_chamelium_edid.c (99%)
>   rename tests/chamelium/{ => v2}/kms_chamelium_frames.c (100%)
>   rename tests/chamelium/{ => v2}/kms_chamelium_helper.c (100%)
>   rename tests/chamelium/{ => v2}/kms_chamelium_helper.h (100%)
>   rename tests/chamelium/{ => v2}/kms_chamelium_hpd.c (100%)
>   rename tests/chamelium/{ => v2}/kms_chamelium_sharpness_filter.c (100%)
>   create mode 100644 tests/chamelium/v3/kms_chamelium_v3_audio.c
>   create mode 100644 tests/chamelium/v3/kms_chamelium_v3_basic.c
>   create mode 100644 tests/chamelium/v3/kms_chamelium_v3_color.c
>   create mode 100644 tests/chamelium/v3/kms_chamelium_v3_edid.c
>   create mode 100644 tests/chamelium/v3/kms_chamelium_v3_frames.c
>   create mode 100644 tests/chamelium/v3/kms_chamelium_v3_hpd.c
> 


^ permalink raw reply	[flat|nested] 37+ messages in thread

* RE: [PATCH i-g-t v1 00/25] Chamelium v3 Integration and Test Execution
  2026-04-28 14:02 ` [PATCH i-g-t v1 00/25] " Louis Chauvet
@ 2026-04-29  3:36   ` Bilal, Mohammed
  0 siblings, 0 replies; 37+ messages in thread
From: Bilal, Mohammed @ 2026-04-29  3:36 UTC (permalink / raw)
  To: Louis Chauvet, igt-dev@lists.freedesktop.org, Kamil Konieczny,
	Nikula, Jani
  Cc: Joshi, Kunal1, Kory Maincent, Luca Ceresoli

Hi Louis,

Thank you for the feedback and for being open to reviewing the series.

I will wait for the patches in [1] to be merged first and then rebase my Chamelium V3 test patches on top. This will simplify the review process and ensure the correct reviewed patches are used.

@Kamil Konieczny , @Nikula, Jani: Could you please consider merging the patches in [1]? Once merged, I will submit only the Chamelium V3 specific patches (library extensions and tests) as a separate series.

Thanks,
Mohammed Bilal

[1] https://patchwork.freedesktop.org/series/151772/#rev10

> -----Original Message-----
> From: Louis Chauvet <louis.chauvet@bootlin.com>
> Sent: 28 April 2026 19:33
> To: Bilal, Mohammed <mohammed.bilal@intel.com>; igt-
> dev@lists.freedesktop.org
> Cc: Joshi, Kunal1 <kunal1.joshi@intel.com>; Kory Maincent
> <kory.maincent@bootlin.com>; Luca Ceresoli <luca.ceresoli@bootlin.com>; Kamil
> Konieczny <kamil.konieczny@linux.intel.com>
> Subject: Re: [PATCH i-g-t v1 00/25] Chamelium v3 Integration and Test Execution
> 
> +CC: Kory, Luca, Jani, Kamil
> 
> Hello Mohammed,
> 
> I'm happy for you to reuse my series, as I no longer have the time to work on it
> (my priorities have shifted to Unigraf support). I'd be glad to review your series.
> 
> Before I do, could you double-check the patches you sent? I noticed a few issues
> with the igt_kms patches:
> - Indentation and Signed-off-by issues reported by Jani.
> - Some patches may not have been reviewed by Köry and Luca. Could you verify
> which version you used and ensure the correct reviews are included? For
> example, in [1], Luca's review appears valid (added in [2]), but Köry's review is not
> (added in [3], which is a different version).
> 
> @Jani, @Kamil, @Luca, and @Kory have already reviewed the
> igt_kms/monitor_edids patches [4] (newer revision). To avoid duplicate reviews, it
> would be great if we could streamline this. Did you need to make changes for your
> series? Can you underline them so I can integrate them in [4].
> 
> @Kamil, @Jani: You’ve already provided feedback in [4]. Would you be open to
> merging those patches so Mohammed can reuse them without duplicating the
> review process? Or would you prefer a separate series?
> 
> Thanks a lot,
> Louis chauvet
> 
> [1]:https://lore.kernel.org/all/20260428044644.257001-2-
> mohammed.bilal@intel.com/
> [2]:https://lore.kernel.org/all/20260128-unigraf-integration-v5-1-
> fbf2e997a011@bootlin.com/
> [3]:https://lore.kernel.org/all/20260316-unigraf-integration-v8-1-
> 4489bf8572be@bootlin.com/
> 
> 
> 
> 
> On 4/28/26 06:46, Mohammed Bilal wrote:
> > Integrate Chamelium v3 hardware support for display testing. This
> > includes the Chamelium v3 library implementation, building with v3
> > support enabled, and comprehensive display tests for HPD, EDID, frames, color,
> and audio.
> >
> > Changes included:
> >
> > 1. Library helpers (patches 1-10):
> >     - Add connector detection and management helpers
> >     - Add monitor EDID management helpers
> >     - Add HDMI 4K EDID support
> >
> > 2. V2 reorganization (patches 11-14):
> >     - Move existing V2 code to lib/chamelium/v2/ and tests/chamelium/v2/
> >     - Rename V2 symbols to allow V2 and V3 to coexist
> >
> > 3. V3 library (patches 15-20):
> >     - Add Chamelium V3 library implementation
> >     - Support for HPD, EDID, frame capture, color and audio
> >
> > 4. V3 tests (patches 21-25):
> >     - kms_chamelium_v3_basic: Basic connectivity test
> >     - kms_chamelium_v3_hpd: Hot plug detect tests
> >     - kms_chamelium_v3_edid: EDID verification tests
> >     - kms_chamelium_v3_frames: Frame capture and CRC tests
> >     - kms_chamelium_v3_color: Color verification tests
> >     - kms_chamelium_v3_audio: Audio tests
> >
> > Louis Chauvet (19):
> >    lib/igt_kms: Add a detect timeout value
> >    lib/igt_kms: Add helper to wait for a specific status on a connector
> >    lib/igt_kms: Add function to list connected connectors
> >    lib/igt_kms: Add helper to obtain a connector by its name or MST path
> >    lib/igt_kms: Add function to get valid pipe for specific output
> >    lib/monitor_edids: Add helper functions for using monitor_edid objects
> >    lib/monitor_edids: Add helper to get an EDID by its name
> >    lib/monitor_edids: Add helper to print all available EDID names
> >    lib/monitor_edids: Fix missing names in some monitor EDID
> >    lib/monitor_edids: Add new EDID for HDMI 4k
> >    tests/chamelium: Extract Chamelium v2 tests into a separate directory
> >    lib/chamelium/v2: Extract chamelium v2 wrapper into its own directory
> >    lib/chamelium/v2: Rename chamelium to chamelium_v2
> >    lib/chamelium/v2: Rename HAVE_CHAMELIUM to HAVE_CHAMELIUM_V2
> >    lib/chamelium/v3: Introduce the foundation for the Chamelium v3
> >      wrapper
> >    lib/chamelium/v3: Introduce initialization and cleanup of
> >      Chamelium-related structures
> >    lib/chamelium/v3: Add method to discover Chamelium ports
> >    lib/chamelium/v3: Implement method to retrieve Chamelium port names
> >    tests/chamelium/v3: Implement a basic Chamelium v3 accessibility
> > test
> >
> > Mohammed Bilal (6):
> >    lib/chamelium/v3: Add extended API for Chamelium v3 HPD, EDID, Frames,
> >      Color & Audio
> >    tests/chamelium/v3: Add HPD (Hot Plug Detect) tests for Chamelium v3
> >    tests/chamelium/v3: Add EDID tests for Chamelium v3
> >    tests/chamelium/v3: Add frame capture and CRC tests for Chamelium v3
> >    tests/chamelium/v3: Add color verification tests for Chamelium v3
> >    tests/chamelium/v3: Add audio tests for Chamelium v3
> >
> >   .gitlab-ci.yml                                |    2 +-
> >   docs/testplan/meson.build                     |    4 +-
> >   lib/{ => chamelium/v2}/igt_chamelium.c        |    2 +-
> >   lib/{ => chamelium/v2}/igt_chamelium.h        |    0
> >   lib/{ => chamelium/v2}/igt_chamelium_stream.c |    2 +-
> >   lib/{ => chamelium/v2}/igt_chamelium_stream.h |    0
> >   lib/chamelium/v3/igt_chamelium.c              | 1267 ++++++++++++++
> >   lib/chamelium/v3/igt_chamelium.h              |  247 +++
> >   lib/igt.h                                     |    6 +-
> >   lib/igt_core.c                                |    4 +
> >   lib/igt_kms.c                                 |  371 +++-
> >   lib/igt_kms.h                                 |   18 +
> >   lib/meson.build                               |   21 +-
> >   lib/monitor_edids/dp_edids.h                  |    3 +
> >   lib/monitor_edids/hdmi_edids.h                |   27 +-
> >   lib/monitor_edids/monitor_edids_helper.c      |  127 ++
> >   lib/monitor_edids/monitor_edids_helper.h      |   12 +-
> >   lib/tests/meson.build                         |    8 +-
> >   meson.build                                   |   31 +-
> >   meson_options.txt                             |    8 +-
> >   .../chamelium/{ => v2}/kms_chamelium_audio.c  |    0
> >   .../chamelium/{ => v2}/kms_chamelium_color.c  |    0
> >   tests/chamelium/{ => v2}/kms_chamelium_edid.c |    2 +-
> >   .../chamelium/{ => v2}/kms_chamelium_frames.c |    0
> >   .../chamelium/{ => v2}/kms_chamelium_helper.c |    0
> >   .../chamelium/{ => v2}/kms_chamelium_helper.h |    0
> >   tests/chamelium/{ => v2}/kms_chamelium_hpd.c  |    0
> >   .../{ => v2}/kms_chamelium_sharpness_filter.c |    0
> >   tests/chamelium/v3/kms_chamelium_v3_audio.c   |  707 ++++++++
> >   tests/chamelium/v3/kms_chamelium_v3_basic.c   |   30 +
> >   tests/chamelium/v3/kms_chamelium_v3_color.c   | 1058 +++++++++++
> >   tests/chamelium/v3/kms_chamelium_v3_edid.c    | 1357 ++++++++++++++
> >   tests/chamelium/v3/kms_chamelium_v3_frames.c  | 1551
> +++++++++++++++++
> >   tests/chamelium/v3/kms_chamelium_v3_hpd.c     |  928 ++++++++++
> >   tests/kms_color_helper.h                      |    2 +-
> >   tests/kms_feature_discovery.c                 |    6 +-
> >   tests/kms_tiled_display.c                     |    6 +-
> >   tests/meson.build                             |   53 +-
> >   38 files changed, 7762 insertions(+), 98 deletions(-)
> >   rename lib/{ => chamelium/v2}/igt_chamelium.c (99%)
> >   rename lib/{ => chamelium/v2}/igt_chamelium.h (100%)
> >   rename lib/{ => chamelium/v2}/igt_chamelium_stream.c (99%)
> >   rename lib/{ => chamelium/v2}/igt_chamelium_stream.h (100%)
> >   create mode 100644 lib/chamelium/v3/igt_chamelium.c
> >   create mode 100644 lib/chamelium/v3/igt_chamelium.h
> >   rename tests/chamelium/{ => v2}/kms_chamelium_audio.c (100%)
> >   rename tests/chamelium/{ => v2}/kms_chamelium_color.c (100%)
> >   rename tests/chamelium/{ => v2}/kms_chamelium_edid.c (99%)
> >   rename tests/chamelium/{ => v2}/kms_chamelium_frames.c (100%)
> >   rename tests/chamelium/{ => v2}/kms_chamelium_helper.c (100%)
> >   rename tests/chamelium/{ => v2}/kms_chamelium_helper.h (100%)
> >   rename tests/chamelium/{ => v2}/kms_chamelium_hpd.c (100%)
> >   rename tests/chamelium/{ => v2}/kms_chamelium_sharpness_filter.c (100%)
> >   create mode 100644 tests/chamelium/v3/kms_chamelium_v3_audio.c
> >   create mode 100644 tests/chamelium/v3/kms_chamelium_v3_basic.c
> >   create mode 100644 tests/chamelium/v3/kms_chamelium_v3_color.c
> >   create mode 100644 tests/chamelium/v3/kms_chamelium_v3_edid.c
> >   create mode 100644 tests/chamelium/v3/kms_chamelium_v3_frames.c
> >   create mode 100644 tests/chamelium/v3/kms_chamelium_v3_hpd.c
> >


^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [PATCH i-g-t v1 20/25] lib/chamelium/v3: Add extended API for Chamelium v3 HPD, EDID, Frames, Color & Audio
  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
  0 siblings, 0 replies; 37+ messages in thread
From: Louis Chauvet @ 2026-04-29 10:44 UTC (permalink / raw)
  To: Mohammed Bilal, igt-dev; +Cc: kunal1.joshi



On 4/28/26 06:46, Mohammed Bilal wrote:
> This commit adds the extended Chamelium v3 wrapper API functions needed
> for HPD, EDID management, frame capture, color verification, and audio
> testing with the Chamelium v3 hardware.
> 
> Signed-off-by: Mohammed Bilal <mohammed.bilal@intel.com>
> ---
>   lib/chamelium/v3/igt_chamelium.c | 962 ++++++++++++++++++++++++++++++-
>   lib/chamelium/v3/igt_chamelium.h | 195 +++++++
>   2 files changed, 1155 insertions(+), 2 deletions(-)
> 
> diff --git a/lib/chamelium/v3/igt_chamelium.c b/lib/chamelium/v3/igt_chamelium.c
> index 8fad88e74..4d4eef1f7 100644
> --- a/lib/chamelium/v3/igt_chamelium.c
> +++ b/lib/chamelium/v3/igt_chamelium.c
> @@ -1,10 +1,16 @@
>   // SPDX-License-Identifier: MIT
>   
> +#include <string.h>
> +#include <time.h>
> +#include <unistd.h>
> +#include <xf86drm.h>
> +#include <xf86drmMode.h>
>   #include <xmlrpc-c/base.h>
>   #include <xmlrpc-c/client.h>
>   
>   #include "igt_chamelium.h"
>   #include "igt_core.h"
> +#include "igt_kms.h"
>   #include "igt_rc.h"
>   
>   #define CHAMELIUM_CONFIG_SECTION "Chameliumv3"
> @@ -70,13 +76,21 @@ struct igt_chamelium_v3 *chamelium_v3_init(char *url)
>   	memset(&clientparms, 0, sizeof(clientparms));
>   	memset(&curlparms, 0, sizeof(curlparms));
>   
> -	/* curl's timeout is in milliseconds */
> -	curlparms.timeout = 10 * 1000;
> +	/* curl's timeout is in milliseconds - 60s for large frame transfers */
> +	curlparms.timeout = 60 * 1000;
>   
>   	clientparms.transport = "curl";
>   	clientparms.transportparmsP = &curlparms;
>   	clientparms.transportparm_size = XMLRPC_CXPSIZE(timeout);
>   
> +	/*
> +	 * Increase the XML-RPC response size limit to 128MB to handle
> +	 * DumpPixels responses for high-resolution frames (e.g. 4K/8K).
> +	 * A 3840x2160 RGB frame is ~24MB raw, ~33MB base64 encoded.
> +	 * The default xmlrpc-c limit is 15MB which is too small.
> +	 */
> +	xmlrpc_limit_set(XMLRPC_XML_SIZE_LIMIT_ID, 128 * 1024 * 1024);
> +
>   	/* Setup the libxmlrpc context */
>   	xmlrpc_env_init(&chamelium->env);
>   	xmlrpc_client_setup_global_const(&chamelium->env);
> @@ -182,6 +196,44 @@ static xmlrpc_value *__chamelium_rpc(struct igt_chamelium_v3 *chamelium,
>   	return res;
>   }
>   
> +/**
> + * __chamelium_rpc_try - Call a remote function, returning NULL on failure
> + *
> + * Unlike __chamelium_rpc, this does NOT assert on RPC failure.
> + * Used for polling functions like GetVideoParams where transient
> + * failures (e.g. "Link has been severed") are expected during
> + * video stabilization.
> + *
> + * Returns a xmlrpc_value on success, or NULL on failure.
> + */
> +static xmlrpc_value *__chamelium_rpc_try(struct igt_chamelium_v3 *chamelium,
> +					 const char *method_name,
> +					 const char *format_str,
> +					 ...)
> +{
> +	xmlrpc_value *res;
> +	va_list va_args;
> +
> +	if (chamelium->env.fault_occurred) {
> +		xmlrpc_env_clean(&chamelium->env);
> +		xmlrpc_env_init(&chamelium->env);
> +	}
> +	va_start(va_args, format_str);
> +	xmlrpc_client_call2f_va(&chamelium->env, chamelium->client,
> +				chamelium->url, method_name, format_str, &res,
> +				va_args);
> +	va_end(va_args);
> +
> +	if (chamelium->env.fault_occurred) {
> +		igt_debug("Chamelium RPC call[%s] failed (non-fatal): %s\n",
> +			  method_name, chamelium->env.fault_string);
> +		xmlrpc_env_clean(&chamelium->env);
> +		xmlrpc_env_init(&chamelium->env);
> +		return NULL;
> +	}
> +	return res;
> +}
> +

Is it possible to reuse this __chamelium_rpc_try in __chamelium_rpc? It 
could avoid the code repetition.

>   /*
>    * For the RPC calls, please refer to the python code [1] for documentation.
>    *
> @@ -307,3 +359,909 @@ char *chamelium_v3_get_port_name(struct igt_chamelium_v3 *chamelium,
>   
>   	return port_name;
>   }
> +
> +/**
> + * chamelium_v3_reset - Reset the Chamelium device
> + *
> + * @chamelium: Chamelium to reset
> + *
> + * Resets all ports to their default state (unplugged).
> + */
> +void chamelium_v3_reset(struct igt_chamelium_v3 *chamelium)
> +{
> +	xmlrpc_value *res;
> +
> +	igt_debug("RPC Reset()\n");
> +	res = __chamelium_rpc(chamelium, "Reset", "()");
> +	xmlrpc_DECREF(res);
> +}
> +
> +/**
> + * chamelium_v3_apply_edid - Apply an EDID to a port
> + *
> + * @chamelium: Chamelium to apply EDID on
> + * @port_id: Port to apply EDID to
> + * @edid_id: EDID ID to apply (0 for default)
> + *
> + * Chamelium V3 requires EDID to be applied before plugging a port.
> + */
> +void chamelium_v3_apply_edid(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_id port_id,
> +			     int edid_id)
> +{
> +	xmlrpc_value *res;
> +
> +	igt_debug("RPC ApplyEdid(%d, %d)\n", port_id, edid_id);
> +	res = __chamelium_rpc(chamelium, "ApplyEdid", "(ii)", port_id, edid_id);
> +	xmlrpc_DECREF(res);
> +}
> +
> +/**
> + * chamelium_v3_plug - Plug a port on the Chamelium
> + *
> + * @chamelium: Chamelium to plug port on
> + * @port_id: Port to plug
> + *
> + * Note: Chamelium V3 requires ApplyEdid to be called before Plug.
> + */
> +void chamelium_v3_plug(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_id port_id)
> +{
> +	xmlrpc_value *res;
> +
> +	igt_debug("RPC Plug(%d)\n", port_id);
> +	res = __chamelium_rpc(chamelium, "Plug", "(i)", port_id);
> +	xmlrpc_DECREF(res);
> +}
> +
> +/**
> + * chamelium_v3_unplug - Unplug a port on the Chamelium
> + *
> + * @chamelium: Chamelium to unplug port on
> + * @port_id: Port to unplug
> + */
> +void chamelium_v3_unplug(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_id port_id)
> +{
> +	xmlrpc_value *res;
> +
> +	igt_debug("RPC Unplug(%d)\n", port_id);
> +	res = __chamelium_rpc(chamelium, "Unplug", "(i)", port_id);
> +	xmlrpc_DECREF(res);
> +}
> +
> +/**
> + * chamelium_v3_is_plugged - Check if a port is plugged
> + *
> + * @chamelium: Chamelium to check
> + * @port_id: Port to check
> + *
> + * Returns true if the port is plugged.
> + */
> +bool chamelium_v3_is_plugged(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_id port_id)
> +{
> +	xmlrpc_value *res;
> +	xmlrpc_bool is_plugged;
> +
> +	igt_debug("RPC IsPlugged(%d)\n", port_id);
> +	res = __chamelium_rpc(chamelium, "IsPlugged", "(i)", port_id);
> +
> +	xmlrpc_read_bool(&chamelium->env, res, &is_plugged);
> +	xmlrpc_DECREF(res);
> +
> +	return is_plugged;
> +}
> +
> +/**
> + * chamelium_v3_plug_with_children - Plug an MST port with its children
> + *
> + * @chamelium: Chamelium to plug port on
> + * @port_id: Parent MST port to plug
> + * @children_ids: Array of children port IDs to plug with the parent
> + * @children_count: Number of children ports
> + *
> + * For MST, the parent port (e.g. FPGA_DP2) controls the HPD line.
> + * This function plugs the parent along with the specified children.
> + * Using this with an empty children list is equivalent to Plug().
> + */
> +void chamelium_v3_plug_with_children(struct igt_chamelium_v3 *chamelium,
> +				    chamelium_v3_port_id port_id,
> +				    chamelium_v3_port_id *children_ids,
> +				    int children_count)
> +{
> +	xmlrpc_value *res;
> +	xmlrpc_value *children_array;
> +	xmlrpc_value *child_val;
> +	int i;
> +
> +	igt_debug("RPC PlugWithChildren(%d, [%d children])\n", port_id, children_count);
> +
> +	/* Build the children array */
> +	children_array = xmlrpc_array_new(&chamelium->env);
> +	for (i = 0; i < children_count; i++) {
> +		child_val = xmlrpc_int_new(&chamelium->env, children_ids[i]);
> +		xmlrpc_array_append_item(&chamelium->env, children_array, child_val);
> +		xmlrpc_DECREF(child_val);
> +	}
> +
> +	res = __chamelium_rpc(chamelium, "PlugWithChildren", "(iA)",
> +			     port_id, children_array);
> +	xmlrpc_DECREF(children_array);
> +	xmlrpc_DECREF(res);
> +}
> +
> +/**
> + * chamelium_v3_is_conflict - Check if two ports conflict (can't be plugged simultaneously)
> + *
> + * @chamelium: Chamelium to check
> + * @port_id_a: First port ID
> + * @port_id_b: Second port ID
> + *
> + * Some ports on Chamelium V3 are mutually exclusive (e.g. ITE HDMI1 and ITE HDMI2).
> + * If port A is plugged and conflicts with port B, plugging B will raise an exception.
> + * Port A must be explicitly unplugged first.
> + *
> + * Returns true if the two ports conflict.
> + */
> +bool chamelium_v3_is_conflict(struct igt_chamelium_v3 *chamelium,
> +			    chamelium_v3_port_id port_id_a,
> +			    chamelium_v3_port_id port_id_b)
> +{
> +	xmlrpc_value *res;
> +	xmlrpc_bool conflict;
> +
> +	igt_debug("RPC IsConflict(%d, %d)\n", port_id_a, port_id_b);
> +	res = __chamelium_rpc(chamelium, "IsConflict", "(ii)", port_id_a, port_id_b);
> +
> +	xmlrpc_read_bool(&chamelium->env, res, &conflict);
> +	xmlrpc_DECREF(res);
> +
> +	return conflict;
> +}
> +
> +/**
> + * chamelium_v3_get_mutually_exclusive_ports - Get all ports that conflict with a given port
> + *
> + * @chamelium: Chamelium to check
> + * @port_id: Port to check conflicts for
> + * @port_ids: Out pointer for the list of conflicting port IDs
> + *
> + * Returns the number of conflicting ports stored in @port_ids.
> + * The caller must free this pointer.
> + */
> +int chamelium_v3_get_mutually_exclusive_ports(struct igt_chamelium_v3 *chamelium,
> +					     chamelium_v3_port_id port_id,
> +					     chamelium_v3_port_id **port_ids)
> +{
> +	xmlrpc_value *res, *res_port;
> +	int port_count, i;
> +
> +	igt_assert(port_ids);
> +
> +	igt_debug("RPC GetMutuallyExclusivePorts(%d)\n", port_id);
> +	res = __chamelium_rpc(chamelium, "GetMutuallyExclusivePorts", "(i)", port_id);
> +
> +	port_count = xmlrpc_array_size(&chamelium->env, res);
> +	*port_ids = calloc(port_count, sizeof(**port_ids));

To catch memory leaks, can you add igt_assert_eq(*port_ids, NULL)? And 
also update the documentation to explain it require *port_ids to be NULL.

> +
> +	for (i = 0; i < port_count; i++) {
> +		xmlrpc_array_read_item(&chamelium->env, res, i, &res_port);
> +		xmlrpc_read_int(&chamelium->env, res_port, (int *)&(*port_ids)[i]);
> +		xmlrpc_DECREF(res_port);
> +	}
> +	xmlrpc_DECREF(res);
> +
> +	return port_count;
> +}
> +
> +/**
> + * chamelium_v3_get_connector_type - Get the connector type string for a port
> + *
> + * @chamelium: Chamelium to check
> + * @port_id: Port to get the connector type for
> + *
> + * Returns a string with the connector type (e.g. "DP", "HDMI").
> + * The caller must free this pointer.
> + */
> +char *chamelium_v3_get_connector_type(struct igt_chamelium_v3 *chamelium,
> +				     chamelium_v3_port_id port_id)
> +{
> +	xmlrpc_value *res;
> +	char *connector_type;
> +
> +	igt_debug("RPC GetConnectorType(%d)\n", port_id);
> +	res = __chamelium_rpc(chamelium, "GetConnectorType", "(i)", port_id);
> +
> +	xmlrpc_read_string(&chamelium->env, res, (const char **)&connector_type);
> +	xmlrpc_DECREF(res);
> +
> +	return connector_type;
> +}
> +
> +/**
> + * chamelium_v3_get_port_type - Get the type of a port (DP, HDMI, etc.)
> + *
> + * @chamelium: Chamelium to check
> + * @port_id: Port to check
> + *
> + * Returns string containing port type. Caller must free.
> + */
> +char *chamelium_v3_get_port_type(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_id port_id)
> +{
> +	char *name = chamelium_v3_get_port_name(chamelium, port_id);
> +	char *type;
> +
> +	if (strstr(name, "DP") != NULL)
> +		type = strdup("DP");
> +	else if (strstr(name, "HDMI") != NULL)
> +		type = strdup("HDMI");
> +	else if (strstr(name, "VGA") != NULL)
> +		type = strdup("VGA");
> +	else
> +		type = strdup("Unknown");

Can you use the existing drm connector type enum [1]? It can avoid 
string manipulation for users.

[1]: 
https://elixir.bootlin.com/linux/v7.0.1/source/include/uapi/drm/drm_mode.h#L403-L423

> +
> +	free(name);
> +	return type;
> +}
> +
> +/**
> + * chamelium_v3_port_is_dp - Check if a port is a DisplayPort
> + *
> + * @chamelium: Chamelium to check
> + * @port_id: Port to check
> + *
> + * Returns true if the port is a DP port.
> + */
> +bool chamelium_v3_port_is_dp(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_id port_id)
> +{
> +	char *name = chamelium_v3_get_port_name(chamelium, port_id);
> +	bool is_dp = (strstr(name, "DP") != NULL);
> +	free(name);
> +	return is_dp;
> +}
> +
> +/**
> + * chamelium_v3_port_is_hdmi - Check if a port is HDMI
> + *
> + * @chamelium: Chamelium to check
> + * @port_id: Port to check
> + *
> + * Returns true if the port is an HDMI port.
> + */
> +bool chamelium_v3_port_is_hdmi(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_id port_id)
> +{
> +	char *name = chamelium_v3_get_port_name(chamelium, port_id);
> +	bool is_hdmi = (strstr(name, "HDMI") != NULL);
> +	free(name);
> +	return is_hdmi;
> +}

And it will make those function very simple / useless (IDK if it is 
really useful to have `port_is_hdmi(chamelium, port_id)` if you can 
write `get_port_type(chamelium, port_id) == DRM_MODE_CONNECTOR_HDMI')

> +
> +/**
> + * chamelium_v3_fire_hpd_pulse - Fire HPD pulses on a port
> + *
> + * @chamelium: Chamelium to fire pulses on
> + * @port_id: Port to fire pulses on
> + * @deassert_interval_usec: Time in microseconds for deasserted (unplugged) state
> + * @assert_interval_usec: Time in microseconds for asserted (plugged) state
> + * @repeat_count: Number of pulse cycles
> + * @end_level: Final HPD level (0 = deasserted/unplugged, 1 = asserted/plugged)
> + *
> + * Generates rapid HPD pulses for testing HPD storm detection and
> + * signal integrity.
> + */
> +void chamelium_v3_fire_hpd_pulse(struct igt_chamelium_v3 *chamelium,
> +				 chamelium_v3_port_id port_id,
> +				 int deassert_interval_usec,
> +				 int assert_interval_usec,
> +				 int repeat_count,
> +				 int end_level)
> +{
> +	xmlrpc_value *res;
> +
> +	igt_debug("RPC FireHpdPulse(%d, %d, %d, %d, %d)\n",
> +		  port_id, deassert_interval_usec, assert_interval_usec,
> +		  repeat_count, end_level);
> +	res = __chamelium_rpc(chamelium, "FireHpdPulse", "(iiiii)",
> +			     port_id, deassert_interval_usec,
> +			     assert_interval_usec, repeat_count, end_level);
> +	xmlrpc_DECREF(res);
> +}
> +
> +/**
> + * chamelium_v3_create_edid - Upload a custom EDID to the Chamelium
> + *
> + * @chamelium: Chamelium to upload EDID to
> + * @edid_data: Raw EDID binary data
> + * @edid_size: Size of the EDID data in bytes
> + *
> + * Uploads raw EDID binary data to the Chamelium V3 device. The returned
> + * EDID ID can be used with chamelium_v3_apply_edid() to set the EDID
> + * on a specific port before plugging.
> + *
> + * Returns the EDID ID assigned by the Chamelium.
> + */
> +int chamelium_v3_create_edid(struct igt_chamelium_v3 *chamelium,
> +			     const unsigned char *edid_data,
> +			     size_t edid_size)
> +{
> +	xmlrpc_value *res;
> +	int edid_id;
> +
> +	igt_debug("RPC CreateEdid(%zu bytes)\n", edid_size);
> +	res = __chamelium_rpc(chamelium, "CreateEdid", "(6)",
> +			     edid_data, edid_size);
> +
> +	xmlrpc_read_int(&chamelium->env, res, &edid_id);
> +	xmlrpc_DECREF(res);
> +
> +	return edid_id;
> +}
> +
> +/**
> + * chamelium_v3_read_port_mappings - Read port mappings from .igtrc config
> + *
> + * Reads [Chamelium:<connector-name>] sections from the IGT config file
> + * and matches each connector name to a real DRM connector on the system.
> + * This is how V2 maps Chamelium ports to DRM connectors.
> + *
> + * @drm_fd: Open DRM file descriptor
> + * @ports: Output array of mapped ports (caller must free)
> + * @count: Output number of mapped ports
> + *
> + * Returns true on success, false if no mappings found or error.
> + */
> +bool chamelium_v3_read_port_mappings(int drm_fd,
> +				     struct chamelium_v3_drm_port **ports,
> +				     int *count)
> +{
> +	GError *error = NULL;
> +	char **group_list;
> +	drmModeRes *res;
> +	int mapping_count = 0;
> +	int port_i = 0;
> +	int i, j;
> +
> +	*ports = NULL;
> +	*count = 0;
> +
> +	if (!igt_key_file) {
> +		igt_debug("No config file for port mappings\n");
> +		return false;
> +	}
> +
> +	res = drmModeGetResources(drm_fd);
> +	if (!res) {
> +		igt_debug("Cannot get DRM resources\n");
> +		return false;
> +	}
> +
> +	group_list = g_key_file_get_groups(igt_key_file, NULL);
> +
> +	/* Count [Chamelium:*] sections */
> +	for (i = 0; group_list[i] != NULL; i++) {
> +		if (strstr(group_list[i], "Chamelium:"))

Can you use define for Chamelium: and update documentation to explain 
the format?

> +			mapping_count++;
> +	}
> +
> +	if (mapping_count == 0) {
> +		igt_debug("No [Chamelium:<connector>] sections in config\n");
> +		g_strfreev(group_list);
> +		drmModeFreeResources(res);
> +		return false;
> +	}
> +
> +	*ports = calloc(mapping_count, sizeof(struct chamelium_v3_drm_port));
> +	igt_assert(*ports);
> +
> +	for (i = 0; group_list[i] != NULL; i++) {
> +		char *group = group_list[i];
> +		char *map_name;
> +		int chamelium_port_id;
> +
> +		if (!strstr(group, "Chamelium:"))
> +			continue;
> +
> +		/* Extract connector name after "Chamelium:" */
> +		map_name = group + strlen("Chamelium:");
> +
> +		chamelium_port_id = g_key_file_get_integer(igt_key_file, group,
> +							   "ChameliumPortID",

ditto, can you use define for ChameliumPortID?

> +							   &error);
> +		if (error) {
> +			igt_debug("Failed to read ChameliumPortID for %s: %s\n",
> +				  map_name, error->message);

If you have a Chamelium: section, it means you really want to use the 
chamelium, so I think this log should be at least igt_warn, or maybe 
igt_error + crash?

> +			g_error_free(error);
> +			error = NULL;
> +			continue;
> +		}
> +
> +		/* Find the DRM connector matching this name */
> +		for (j = 0; j < res->count_connectors; j++) {
> +			drmModeConnector *conn;
> +			char name[64];
> +
> +			conn = drmModeGetConnectorCurrent(drm_fd, res->connectors[j]);
> +			if (!conn)
> +				continue;
> +
> +			snprintf(name, sizeof(name), "%s-%u",
> +				 kmstest_connector_type_str(conn->connector_type),
> +				 conn->connector_type_id);

This pattern is common in igt, I think it could be useful to add a pair 
of macro:

#define IGT_CONNECTOR_FMT "%s-%s"
#define IGT_CONNECTOR_FMT_ARGS \
	kmstest_connector_type_str((conn)->connector_type), \
	(conn)->connector_type_id

And use it:
	snprintf(name, sizeof(name), IGT_CONNECTOR_FMT, 
IGT_CONNECTOR_FMT_ARGS(conn);

But that not a blocker for this serie, it is just a suggestion if you 
have the time.

> +
> +			if (strcmp(name, map_name) == 0) {
> +				struct chamelium_v3_drm_port *p = &(*ports)[port_i];
> +
> +				p->port_id = chamelium_port_id;
> +				p->connector_id = conn->connector_id;
> +				p->connector_type = conn->connector_type;
> +				snprintf(p->connector_name, sizeof(p->connector_name),
> +					 "%s", name);
> +				port_i++;
> +
> +				igt_info("Mapped Chamelium port %d -> DRM %s (connector_id=%u)\n",
> +					 chamelium_port_id, name, conn->connector_id);
> +
> +				drmModeFreeConnector(conn);
> +				break;
> +			}
> +
> +			drmModeFreeConnector(conn);
> +		}
> +	}
> +
> +	*count = port_i;
> +	g_strfreev(group_list);
> +	drmModeFreeResources(res);
> +
> +	if (port_i == 0) {
> +		free(*ports);
> +		*ports = NULL;
> +		igt_warn("No connectors matched the Chamelium port mappings\n");
> +		return false;
> +	}
> +
> +	return true;
> +}
> +
> +/**
> + * chamelium_v3_reprobe_connector - Force-reprobe a DRM connector
> + *
> + * Uses drmModeGetConnector() which triggers a kernel reprobe of the
> + * actual hardware state. This is how we verify whether a connector
> + * is truly connected or disconnected.
> + *
> + * @drm_fd: Open DRM file descriptor
> + * @connector_id: DRM connector ID to reprobe
> + *
> + * Returns the actual connection status.
> + */
> +drmModeConnection chamelium_v3_reprobe_connector(int drm_fd,
> +						 uint32_t connector_id)
> +{
> +	drmModeConnector *conn;
> +	drmModeConnection status;
> +
> +	conn = drmModeGetConnector(drm_fd, connector_id);
> +	if (!conn)
> +		return DRM_MODE_UNKNOWNCONNECTION;
> +
> +	status = conn->connection;
> +	drmModeFreeConnector(conn);
> +
> +	return status;
> +}

I think this should be in igt_kms, this is a generic function.

> +
> +/**
> + * chamelium_v3_wait_for_conn_status_change - Poll connector until expected status
> + *
> + * Mirrors V2 chamelium_wait_for_conn_status_change. Polls the real DRM
> + * connector using drmModeGetConnector() (which triggers kernel reprobe)
> + * with 50ms intervals until the status matches or timeout.
> + *
> + * @drm_fd: Open DRM file descriptor
> + * @connector_id: DRM connector ID
> + * @expected: Expected connection status
> + * @timeout_secs: Max seconds to wait
> + *
> + * Returns true if the expected status was reached.
> + */
> +bool chamelium_v3_wait_for_conn_status_change(int drm_fd,
> +					      uint32_t connector_id,
> +					      drmModeConnection expected,
> +					      int timeout_secs)
> +{
> +	struct timespec start, now;
> +
> +	clock_gettime(CLOCK_MONOTONIC, &start);
> +
> +	while (1) {
> +		if (chamelium_v3_reprobe_connector(drm_fd, connector_id) == expected)
> +			return true;
> +
> +		clock_gettime(CLOCK_MONOTONIC, &now);
> +		if (now.tv_sec - start.tv_sec >= timeout_secs)
> +			break;
> +
> +		usleep(50000); /* 50ms poll, same as V2 */
> +	}
> +
> +	return false;
> +}

This function is implemented in [2] (your series) and [3] (my series, 
with a 10ms sleep, but it can be changed if you think 10ms is too fast), 
I think you should reuse one of the existing implementations.

[2]:https://lore.kernel.org/all/20260428044644.257001-3-mohammed.bilal@intel.com/
[3]:https://lore.kernel.org/all/20260424-unigraf-integration-v11-2-0c5f5346ae60@bootlin.com/

> +/**
> + * chamelium_v3_get_video_params - Get video timing parameters from port
> + *
> + * Calls the GetVideoParams RPC to retrieve current video timing info.
> + * Uses non-asserting RPC to allow callers (e.g. polling/wait functions)
> + * to handle transient failures gracefully.
> + *
> + * @chamelium: Chamelium instance
> + * @port_id: Port ID to query
> + * @params: Output video parameters structure
> + *
> + * Returns true on success, false on RPC failure (e.g. link not ready).
> + */
> +bool chamelium_v3_get_video_params(struct igt_chamelium_v3 *chamelium,
> +				   chamelium_v3_port_id port_id,
> +				   struct chamelium_v3_video_params *params)
> +{
> +	xmlrpc_value *res;
> +	xmlrpc_value *val;
> +
> +	igt_assert(params);
> +	memset(params, 0, sizeof(*params));
> +
> +	igt_debug("RPC GetVideoParams(%d)\n", port_id);
> +	res = __chamelium_rpc_try(chamelium, "GetVideoParams", "(i)", port_id);
> +	if (!res)
> +		return false;
> +
> +	/* Parse the returned struct/dict */
> +	xmlrpc_struct_find_value(&chamelium->env, res, "hactive", &val);
> +	if (val) { xmlrpc_read_int(&chamelium->env, val, &params->hactive); xmlrpc_DECREF(val); }
> +
> +	xmlrpc_struct_find_value(&chamelium->env, res, "vactive", &val);
> +	if (val) { xmlrpc_read_int(&chamelium->env, val, &params->vactive); xmlrpc_DECREF(val); }
> +
> +	xmlrpc_struct_find_value(&chamelium->env, res, "htotal", &val);
> +	if (val) { xmlrpc_read_int(&chamelium->env, val, &params->htotal); xmlrpc_DECREF(val); }
> +
> +	xmlrpc_struct_find_value(&chamelium->env, res, "vtotal", &val);
> +	if (val) { xmlrpc_read_int(&chamelium->env, val, &params->vtotal); xmlrpc_DECREF(val); }
> +
> +	xmlrpc_struct_find_value(&chamelium->env, res, "hsync_width", &val);
> +	if (val) { xmlrpc_read_int(&chamelium->env, val, &params->hsync_width); xmlrpc_DECREF(val); }
> +
> +	xmlrpc_struct_find_value(&chamelium->env, res, "vsync_width", &val);
> +	if (val) { xmlrpc_read_int(&chamelium->env, val, &params->vsync_width); xmlrpc_DECREF(val); }
> +
> +	xmlrpc_struct_find_value(&chamelium->env, res, "hfront_porch", &val);
> +	if (val) { xmlrpc_read_int(&chamelium->env, val, &params->hfront_porch); xmlrpc_DECREF(val); }
> +
> +	xmlrpc_struct_find_value(&chamelium->env, res, "vfront_porch", &val);
> +	if (val) { xmlrpc_read_int(&chamelium->env, val, &params->vfront_porch); xmlrpc_DECREF(val); }
> +
> +	xmlrpc_struct_find_value(&chamelium->env, res, "hback_porch", &val);
> +	if (val) { xmlrpc_read_int(&chamelium->env, val, &params->hback_porch); xmlrpc_DECREF(val); }
> +
> +	xmlrpc_struct_find_value(&chamelium->env, res, "vback_porch", &val);
> +	if (val) { xmlrpc_read_int(&chamelium->env, val, &params->vback_porch); xmlrpc_DECREF(val); }
> +
> +	xmlrpc_struct_find_value(&chamelium->env, res, "bpc", &val);
> +	if (val) { xmlrpc_read_int(&chamelium->env, val, &params->bpc); xmlrpc_DECREF(val); }
> +
> +	xmlrpc_struct_find_value(&chamelium->env, res, "clock", &val);
> +	if (val) { xmlrpc_read_double(&chamelium->env, val, &params->clock); xmlrpc_DECREF(val); }
> +
> +	xmlrpc_struct_find_value(&chamelium->env, res, "frame_rate", &val);
> +	if (val) { xmlrpc_read_double(&chamelium->env, val, &params->frame_rate); xmlrpc_DECREF(val); }
> +
> +	xmlrpc_struct_find_value(&chamelium->env, res, "hsync_polarity", &val);
> +	if (val) {
> +		xmlrpc_bool b;
> +		xmlrpc_read_bool(&chamelium->env, val, &b);
> +		params->hsync_polarity = b;
> +		xmlrpc_DECREF(val);
> +	}
> +
> +	xmlrpc_struct_find_value(&chamelium->env, res, "vsync_polarity", &val);
> +	if (val) {
> +		xmlrpc_bool b;
> +		xmlrpc_read_bool(&chamelium->env, val, &b);
> +		params->vsync_polarity = b;
> +		xmlrpc_DECREF(val);
> +	}
> +
> +	xmlrpc_struct_find_value(&chamelium->env, res, "interlaced", &val);
> +	if (val) {
> +		xmlrpc_bool b;
> +		xmlrpc_read_bool(&chamelium->env, val, &b);
> +		params->interlaced = b;
> +		xmlrpc_DECREF(val);
> +	}
> +
> +	xmlrpc_DECREF(res);
> +
> +	igt_debug("Video params: %dx%d @ %.2f Hz, %d bpc\n",
> +		  params->hactive, params->vactive,
> +		  params->frame_rate, params->bpc);

Two questions:
- is it possible to only have "partial result" (only frame_rate but not 
interlaced for example)? If not, I think you should add an assert if 
there is a success + failure.
- if "partial result" is possible, how do I know which field is 
populated? In particular booleans, they can only be true/false, so the 
user don't know if interlaced=false means "interlaced not present" or 
"interlaced is false".

> +	return true;
> +}
> +
> +/**
> + * chamelium_v3_has_audio_support - Check if a port supports audio
> + *
> + * @chamelium: Chamelium instance
> + * @port_id: Port ID to check
> + *
> + * Returns true if the port has audio support.
> + */
> +bool chamelium_v3_has_audio_support(struct igt_chamelium_v3 *chamelium,
> +				    chamelium_v3_port_id port_id)
> +{
> +	xmlrpc_value *res;
> +	xmlrpc_bool has_support;
> +
> +	igt_debug("RPC HasAudioSupport(%d)\n", port_id);
> +	res = __chamelium_rpc_try(chamelium, "HasAudioSupport", "(i)", port_id);
> +	if (!res)
> +		return false;
> +
> +	xmlrpc_read_bool(&chamelium->env, res, &has_support);
> +	xmlrpc_DECREF(res);
> +
> +	return has_support;
> +}
> +
> +/**
> + * chamelium_v3_start_capturing_audio - Start capturing audio from a port
> + *
> + * @chamelium: Chamelium instance
> + * @port_id: Port to capture audio from
> + * @save_to_file: Whether to save captured audio to a file on the Chamelium
> + */
> +bool chamelium_v3_start_capturing_audio(struct igt_chamelium_v3 *chamelium,
> +					chamelium_v3_port_id port_id,
> +					bool save_to_file)
> +{
> +	xmlrpc_value *res;
> +
> +	igt_debug("RPC StartCapturingAudio(%d, %s)\n",
> +		  port_id, save_to_file ? "true" : "false");
> +	res = __chamelium_rpc_try(chamelium, "StartCapturingAudio", "(ib)",
> +				 port_id, save_to_file);

I think this can use __chamelium_rpc no? If the audio is supported, 
capturing should never fail?

> +	if (!res)
> +		return false;
> +	xmlrpc_DECREF(res);
> +	return true;
> +}
> +
> +/**
> + * chamelium_v3_stop_capturing_audio - Stop capturing audio from a port
> + *
> + * @chamelium: Chamelium instance
> + * @port_id: Port to stop capturing audio from
> + */
> +void chamelium_v3_stop_capturing_audio(struct igt_chamelium_v3 *chamelium,
> +				       chamelium_v3_port_id port_id)
> +{
> +	xmlrpc_value *res;
> +
> +	igt_debug("RPC StopCapturingAudio(%d)\n", port_id);
> +	res = __chamelium_rpc_try(chamelium, "StopCapturingAudio", "(i)",
> +				  port_id);

ditto?

> +	if (res)
> +		xmlrpc_DECREF(res);
> +}
> +
> +/**
> + * chamelium_v3_get_audio_format - Get the audio capture format
> + *
> + * @chamelium: Chamelium instance
> + * @port_id: Port to query
> + * @rate: Output sampling rate in Hz (or 0 if unknown)
> + * @channels: Output number of channels
> + */
> +void chamelium_v3_get_audio_format(struct igt_chamelium_v3 *chamelium,
> +				   chamelium_v3_port_id port_id,
> +				   int *rate, int *channels)
> +{
> +	xmlrpc_value *res, *val;
> +
> +	igt_debug("RPC GetAudioFormat(%d)\n", port_id);
> +	res = __chamelium_rpc(chamelium, "GetAudioFormat", "(i)", port_id);
> +
> +	if (rate) {
> +		xmlrpc_struct_find_value(&chamelium->env, res, "rate", &val);
> +		if (val) {
> +			xmlrpc_read_int(&chamelium->env, val, rate);
> +			xmlrpc_DECREF(val);
> +		} else {
> +			*rate = 0;
> +		}
> +	}
> +
> +	if (channels) {
> +		xmlrpc_struct_find_value(&chamelium->env, res, "channel", &val);
> +		if (val) {
> +			xmlrpc_read_int(&chamelium->env, val, channels);
> +			xmlrpc_DECREF(val);
> +		} else {
> +			*channels = 0;
> +		}
> +	}
> +
> +	xmlrpc_DECREF(res);
> +}
> +
> +/**
> + * chamelium_v3_get_audio_channel_mapping - Get audio channel mapping
> + *
> + * @chamelium: Chamelium instance
> + * @port_id: Port to query
> + * @mapping: Array of CHAMELIUM_V3_MAX_AUDIO_CHANNELS ints.
> + *           Each element indicates which input channel the capture channel
> + *           is mapped to. -1 means unmapped.
> + */
> +void chamelium_v3_get_audio_channel_mapping(struct igt_chamelium_v3 *chamelium,
> +					    chamelium_v3_port_id port_id,
> +					    int mapping[static CHAMELIUM_V3_MAX_AUDIO_CHANNELS])
> +{
> +	xmlrpc_value *res, *res_channel;
> +	int res_len, i;
> +
> +	igt_debug("RPC GetAudioChannelMapping(%d)\n", port_id);
> +	res = __chamelium_rpc(chamelium, "GetAudioChannelMapping", "(i)", port_id);
> +
> +	res_len = xmlrpc_array_size(&chamelium->env, res);
> +	igt_assert_f(res_len == CHAMELIUM_V3_MAX_AUDIO_CHANNELS,
> +		     "Expected %d audio channels, got %d\n",
> +		     CHAMELIUM_V3_MAX_AUDIO_CHANNELS, res_len);
> +
> +	for (i = 0; i < res_len; i++) {
> +		xmlrpc_array_read_item(&chamelium->env, res, i, &res_channel);
> +		xmlrpc_read_int(&chamelium->env, res_channel, &mapping[i]);
> +		xmlrpc_DECREF(res_channel);
> +	}
> +	xmlrpc_DECREF(res);
> +}
> +
> +/**
> + * chamelium_v3_get_last_infoframe - Get the last received InfoFrame
> + *
> + * @chamelium: Chamelium instance
> + * @port_id: Port to query
> + * @infoframe_type: Type string: "avi", "audio", "mpeg", "vendor", "spd"
> + *
> + * Returns: InfoFrame structure (caller must free with chamelium_v3_infoframe_destroy),
> + *          or NULL if no InfoFrame was received.
> + */
> +struct chamelium_v3_infoframe *chamelium_v3_get_last_infoframe(
> +	struct igt_chamelium_v3 *chamelium,
> +	chamelium_v3_port_id port_id,
> +	const char *infoframe_type)
> +{
> +	xmlrpc_value *res, *val;
> +	struct chamelium_v3_infoframe *infoframe;
> +	const unsigned char *payload_data;
> +	size_t payload_len;
> +
> +	igt_debug("RPC GetLastInfoFrame(%d, \"%s\")\n", port_id, infoframe_type);
> +	res = __chamelium_rpc_try(chamelium, "GetLastInfoFrame", "(is)",
> +				  port_id, infoframe_type);
> +	if (!res)
> +		return NULL;
> +
> +	infoframe = calloc(1, sizeof(*infoframe));
> +
> +	xmlrpc_struct_find_value(&chamelium->env, res, "version", &val);
> +	if (val) {
> +		xmlrpc_read_int(&chamelium->env, val, &infoframe->version);
> +		xmlrpc_DECREF(val);
> +	}
> +
> +	xmlrpc_struct_find_value(&chamelium->env, res, "payload", &val);
> +	if (val) {
> +		xmlrpc_read_base64(&chamelium->env, val, &payload_len,
> +				   &payload_data);
> +		infoframe->payload = malloc(payload_len);
> +		memcpy(infoframe->payload, payload_data, payload_len);
> +		infoframe->payload_size = payload_len;
> +		xmlrpc_DECREF(val);
> +	}
> +
> +	xmlrpc_DECREF(res);
> +
> +	return infoframe;
> +}
> +
> +/**
> + * chamelium_v3_infoframe_destroy - Free an InfoFrame structure
> + */
> +void chamelium_v3_infoframe_destroy(struct chamelium_v3_infoframe *infoframe)
> +{
> +	if (infoframe) {
> +		free(infoframe->payload);
> +		free(infoframe);
> +	}
> +}
> +
> +/**
> + * chamelium_v3_capture_frame - Capture current frame from port
> + *
> + * Captures the current displayed frame using DumpPixels API.
> + * Returns RGB data (3 bytes per pixel).
> + *
> + * @chamelium: Chamelium instance
> + * @port_id: Port ID to capture from
> + *
> + * Returns: Frame structure with captured data (caller must free with chamelium_v3_free_frame)

Add documentation that explains that return can be null if there is no 
signal/invalid video param

> + */
> +struct chamelium_v3_frame *chamelium_v3_capture_frame(struct igt_chamelium_v3 *chamelium,
> +						      chamelium_v3_port_id port_id)
> +{
> +	struct chamelium_v3_frame *frame;
> +	struct chamelium_v3_video_params params;
> +	xmlrpc_value *res;
> +	const unsigned char *data;
> +	size_t data_len;
> +
> +	/* First get video params to know resolution */
> +	if (!chamelium_v3_get_video_params(chamelium, port_id, &params)) {
> +		igt_warn("Failed to get video params for port %d\n", port_id);
> +		return NULL;
> +	}
> +
> +	if (params.hactive <= 0 || params.vactive <= 0) {
> +		igt_warn("Invalid video params: %dx%d\n", params.hactive, params.vactive);
> +		return NULL;
> +	}
> +
> +	igt_debug("RPC DumpPixels(%d)\n", port_id);
> +	res = __chamelium_rpc_try(chamelium, "DumpPixels", "(i)", port_id);
> +	if (!res) {
> +		igt_warn("DumpPixels RPC failed for port %d\n", port_id);
> +		return NULL;
> +	}
> +
> +	/* Extract binary data from XML-RPC response */
> +	xmlrpc_read_base64(&chamelium->env, res, &data_len, &data);
> +
> +	if (chamelium->env.fault_occurred) {
> +		igt_warn("Failed to read DumpPixels data: %s\n",
> +			 chamelium->env.fault_string);
> +		xmlrpc_DECREF(res);
> +		return NULL;
> +	}
> +
> +	/* Allocate frame structure */
> +	frame = calloc(1, sizeof(*frame));
> +	frame->width = params.hactive;
> +	frame->height = params.vactive;
> +	frame->size = data_len;
> +	frame->data = malloc(data_len);
> +	memcpy(frame->data, data, data_len);
> +
> +	xmlrpc_DECREF(res);
> +
> +	igt_debug("Captured frame: %dx%d, %zu bytes\n",
> +		  frame->width, frame->height, frame->size);
> +
> +	/* Verify data size matches expected RGB (3 bytes per pixel) */
> +	size_t expected = (size_t)frame->width * frame->height * 3;
> +	if (frame->size != expected) {
> +		igt_warn("Frame size mismatch: got %zu, expected %zu\n",
> +			 frame->size, expected);
> +	}
> +
> +	return frame;
> +}
> +
> +/**
> + * chamelium_v3_free_frame - Free captured frame memory
> + */
> +void chamelium_v3_free_frame(struct chamelium_v3_frame *frame)
> +{
> +	if (frame) {
> +		free(frame->data);
> +		free(frame);
> +	}
> +}
> diff --git a/lib/chamelium/v3/igt_chamelium.h b/lib/chamelium/v3/igt_chamelium.h
> index 71351e579..a0da1bb6a 100644
> --- a/lib/chamelium/v3/igt_chamelium.h
> +++ b/lib/chamelium/v3/igt_chamelium.h
> @@ -49,4 +49,199 @@ int chamelium_v3_get_children(struct igt_chamelium_v3 *chamelium, chamelium_v3_p
>   bool chamelium_v3_is_mst(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_id port_id);
>   char *chamelium_v3_get_port_name(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_id port_id);
>   
> +/* Port control functions */
> +void chamelium_v3_reset(struct igt_chamelium_v3 *chamelium);
> +void chamelium_v3_plug(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_id port_id);
> +void chamelium_v3_unplug(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_id port_id);
> +bool chamelium_v3_is_plugged(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_id port_id);
> +void chamelium_v3_apply_edid(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_id port_id,
> +			     int edid_id);
> +
> +/* MST (Multi-Stream Transport) functions */
> +void chamelium_v3_plug_with_children(struct igt_chamelium_v3 *chamelium,
> +				    chamelium_v3_port_id port_id,
> +				    chamelium_v3_port_id *children_ids,
> +				    int children_count);
> +bool chamelium_v3_is_conflict(struct igt_chamelium_v3 *chamelium,
> +			    chamelium_v3_port_id port_id_a,
> +			    chamelium_v3_port_id port_id_b);
> +int chamelium_v3_get_mutually_exclusive_ports(struct igt_chamelium_v3 *chamelium,
> +					     chamelium_v3_port_id port_id,
> +					     chamelium_v3_port_id **port_ids);
> +char *chamelium_v3_get_connector_type(struct igt_chamelium_v3 *chamelium,
> +				     chamelium_v3_port_id port_id);
> +
> +/* Port type detection */
> +char *chamelium_v3_get_port_type(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_id port_id);
> +bool chamelium_v3_port_is_dp(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_id port_id);
> +bool chamelium_v3_port_is_hdmi(struct igt_chamelium_v3 *chamelium, chamelium_v3_port_id port_id);
> +
> +/* HPD pulse generation */
> +void chamelium_v3_fire_hpd_pulse(struct igt_chamelium_v3 *chamelium,
> +				 chamelium_v3_port_id port_id,
> +				 int deassert_interval_usec,
> +				 int assert_interval_usec,
> +				 int repeat_count,
> +				 int end_level);
> +
> +/* EDID creation - returns EDID ID for use with chamelium_v3_apply_edid */
> +int chamelium_v3_create_edid(struct igt_chamelium_v3 *chamelium,
> +			     const unsigned char *edid_data,
> +			     size_t edid_size);
> +
> +/* Video parameters structure */
> +struct chamelium_v3_video_params {
> +	int hactive;
> +	int vactive;
> +	int htotal;
> +	int vtotal;
> +	int hsync_width;
> +	int vsync_width;
> +	int hfront_porch;
> +	int vfront_porch;
> +	int hback_porch;
> +	int vback_porch;
> +	bool hsync_polarity;
> +	bool vsync_polarity;
> +	double clock;
> +	double frame_rate;
> +	int bpc;
> +	bool interlaced;
> +};
> +
> +/* Frame capture structure */
> +struct chamelium_v3_frame {
> +	uint8_t *data;      /* RGB pixel data (3 bytes per pixel) */
> +	int width;
> +	int height;
> +	size_t size;        /* Total data size in bytes */

Maybe explains that the size is the data buffer size and may be 
different from width*height*3.

> +};
> +
> +/**
> + * chamelium_v3_get_video_params - Get video timing parameters from port
> + *
> + * @chamelium: Chamelium instance
> + * @port_id: Port ID to query
> + * @params: Output video parameters structure
> + *
> + * Returns true on success.
> + */

I don't think you need to duplicate the documentation

> +bool chamelium_v3_get_video_params(struct igt_chamelium_v3 *chamelium,
> +				   chamelium_v3_port_id port_id,
> +				   struct chamelium_v3_video_params *params);
> +
> +/**
> + * chamelium_v3_capture_frame - Capture current frame from port
> + *
> + * Captures the current displayed frame using DumpPixels API.
> + * Returns RGB data (3 bytes per pixel).
> + *
> + * @chamelium: Chamelium instance
> + * @port_id: Port ID to capture from
> + *
> + * Returns: Frame structure with captured data (caller must free with chamelium_v3_free_frame)
> + */

ditto

> +struct chamelium_v3_frame *chamelium_v3_capture_frame(struct igt_chamelium_v3 *chamelium,
> +						      chamelium_v3_port_id port_id);
> +
> +/**
> + * chamelium_v3_free_frame - Free captured frame memory
> + */

ditto

> +void chamelium_v3_free_frame(struct chamelium_v3_frame *frame);
> +
> +/*
> + * Port-to-DRM-connector mapping.
> + *
> + * Reads [Chamelium:<connector-name>] sections from .igtrc to map
> + * Chamelium port IDs to DRM connector IDs on the DUT.
> + * This is essential for verifying that plug/unplug actually changes
> + * the real DRM connector state.
> + */
> +
> +/** struct chamelium_v3_drm_port - A Chamelium port mapped to a DRM connector */
> +struct chamelium_v3_drm_port {
> +	chamelium_v3_port_id port_id;   /* Chamelium port ID (e.g. 4) */
> +	uint32_t connector_id;          /* DRM connector_id from kernel */
> +	uint32_t connector_type;        /* DRM_MODE_CONNECTOR_* type */

connector_type is from the kernel or from the chamelium? There can be an 
adapter in between.

> +	char connector_name[64];        /* DRM connector name e.g. "DP-1" */
> +};
> +
> +/**
> + * chamelium_v3_read_port_mappings - Read port mappings from .igtrc config
> + *
> + * Reads [Chamelium:<name>] sections with ChameliumPortID keys, then finds
> + * the matching DRM connector on the system.
> + *
> + * @drm_fd: Open DRM file descriptor
> + * @ports: Output array of mapped ports (caller must free)
> + * @count: Output number of mapped ports
> + *
> + * Returns true on success.
> + */
> +bool chamelium_v3_read_port_mappings(int drm_fd,
> +				     struct chamelium_v3_drm_port **ports,
> +				     int *count);
> +
> +/**
> + * chamelium_v3_reprobe_connector - Force-reprobe a DRM connector and return status
> + *
> + * Uses drmModeGetConnector() which triggers a kernel reprobe of the
> + * actual hardware state. This is how V2 verifies real connector status.
> + *
> + * @drm_fd: Open DRM file descriptor
> + * @connector_id: DRM connector ID to reprobe
> + *
> + * Returns: drmModeConnection (DRM_MODE_CONNECTED, DRM_MODE_DISCONNECTED, etc.)
> + */
> +drmModeConnection chamelium_v3_reprobe_connector(int drm_fd,
> +						 uint32_t connector_id);
> +
> +/**
> + * chamelium_v3_wait_for_conn_status_change - Poll connector until expected status or timeout
> + *
> + * Mirrors V2 chamelium_wait_for_conn_status_change. Polls with 50ms
> + * interval until the connector reports the expected status.
> + *
> + * @drm_fd: Open DRM file descriptor
> + * @connector_id: DRM connector ID
> + * @expected: Expected connection status
> + * @timeout_secs: Max seconds to wait
> + *
> + * Returns true if the expected status was reached.
> + */
> +bool chamelium_v3_wait_for_conn_status_change(int drm_fd,
> +					      uint32_t connector_id,
> +					      drmModeConnection expected,
> +					      int timeout_secs);
> +
> +/* Audio support */
> +#define CHAMELIUM_V3_MAX_AUDIO_CHANNELS 8
> +
> +bool chamelium_v3_has_audio_support(struct igt_chamelium_v3 *chamelium,
> +				    chamelium_v3_port_id port_id);
> +bool chamelium_v3_start_capturing_audio(struct igt_chamelium_v3 *chamelium,
> +					chamelium_v3_port_id port_id,
> +					bool save_to_file);
> +void chamelium_v3_stop_capturing_audio(struct igt_chamelium_v3 *chamelium,
> +				       chamelium_v3_port_id port_id);
> +void chamelium_v3_get_audio_format(struct igt_chamelium_v3 *chamelium,
> +				   chamelium_v3_port_id port_id,
> +				   int *rate, int *channels);
> +void chamelium_v3_get_audio_channel_mapping(struct igt_chamelium_v3 *chamelium,
> +					    chamelium_v3_port_id port_id,
> +					    int mapping[static CHAMELIUM_V3_MAX_AUDIO_CHANNELS]);
> +
> +/* InfoFrame support */
> +struct chamelium_v3_infoframe {
> +	int version;
> +	uint8_t *payload;
> +	size_t payload_size;
> +};
> +
> +struct chamelium_v3_infoframe *chamelium_v3_get_last_infoframe(
> +	struct igt_chamelium_v3 *chamelium,
> +	chamelium_v3_port_id port_id,
> +	const char *infoframe_type);
> +void chamelium_v3_infoframe_destroy(struct chamelium_v3_infoframe *infoframe);
> +
>   #endif //V3_IGT_CHAMELIUM_H


^ permalink raw reply	[flat|nested] 37+ messages in thread

end of thread, other threads:[~2026-04-29 10:45 UTC | newest]

Thread overview: 37+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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 ` [PATCH i-g-t v1 21/25] tests/chamelium/v3: Add HPD (Hot Plug Detect) tests for Chamelium v3 Mohammed Bilal
2026-04-28  4:46 ` [PATCH i-g-t v1 22/25] tests/chamelium/v3: Add EDID " 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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox