* [PATCH i-g-t v9 01/49] lib/igt_kms: Add a detect timeout value
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
@ 2026-03-16 16:17 ` Louis Chauvet
2026-03-16 18:50 ` Kamil Konieczny
2026-03-16 16:17 ` [PATCH i-g-t v9 02/49] lib/igt_kms: Add helper to wait for a specific status on a connector Louis Chauvet
` (48 subsequent siblings)
49 siblings, 1 reply; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:17 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
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 | 33 +++++++++++++++++++++++++++++++++
lib/igt_core.h | 9 +++++++++
lib/igt_kms.c | 1 +
3 files changed, 43 insertions(+)
diff --git a/lib/igt_core.c b/lib/igt_core.c
index 3ee670a41a91..e8f31d573512 100644
--- a/lib/igt_core.c
+++ b/lib/igt_core.c
@@ -26,6 +26,7 @@
*
*/
+#include "igt_kms.h"
#ifdef HAVE_LIBGEN_H
#include <libgen.h>
#endif
@@ -271,6 +272,10 @@
* # It is not mandatory and allows overriding default values.
* [DUT]
* SuspendResumeDelay=10
+ *
+ * # The following option define the timeout (in seconds) for detection feature
+ * # (waiting for a connector status)
+ * DisplayDetectTimeout=10.0
* ]|
*
* Some specific configuration options may be used by specific parts of IGT,
@@ -379,6 +384,21 @@ static bool stderr_needs_sentinel = false;
static int _igt_dynamic_tests_executed = -1;
+/**
+ * default_display_detect_timeout: Timeout for display detection, in seconds
+ */
+static double default_display_detect_timeout;
+
+static void igt_set_default_display_detect_timeout(double timeout)
+{
+ default_display_detect_timeout = timeout;
+}
+
+double igt_default_display_detect_timeout(void)
+{
+ return default_display_detect_timeout;
+}
+
static void print_backtrace(void)
{
#ifndef HAVE_LIBUNWIND
@@ -1003,6 +1023,7 @@ static void common_init_config(void)
{
GError *error = NULL;
int ret = 0;
+ static double timeout = 0.0;
igt_key_file = igt_load_igtrc();
@@ -1023,6 +1044,18 @@ static void common_init_config(void)
if (ret != 0)
igt_set_autoresume_delay(ret);
+ 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;
+ }
+ g_clear_error(&error);
+ igt_set_default_display_detect_timeout(timeout);
+
/* Adding filters, order .igtrc, IGT_DEVICE, --device filter */
if (igt_device_filter_count() > 0)
igt_debug("Notice: using --device filters:\n");
diff --git a/lib/igt_core.h b/lib/igt_core.h
index 6845f853c980..daf83cb5380d 100644
--- a/lib/igt_core.h
+++ b/lib/igt_core.h
@@ -143,6 +143,13 @@ struct _GKeyFile *igt_load_igtrc(void);
*/
#define IGT_EXIT_ABORT 112
+/**
+ * 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
void __igt_assert_in_outer_scope(void);
bool __igt_fixture(void);
@@ -446,6 +453,8 @@ void __igt_subtest_group_restore(int, int);
*/
#define igt_main() igt_main_args(NULL, NULL, NULL, NULL, NULL)
+double igt_default_display_detect_timeout(void);
+
const char *igt_test_name(void);
void igt_simple_init_parse_opts(int *argc, char **argv,
const char *extra_short_opts,
diff --git a/lib/igt_kms.c b/lib/igt_kms.c
index 0b05ddd72977..692d75b70ecc 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
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* Re: [PATCH i-g-t v9 01/49] lib/igt_kms: Add a detect timeout value
2026-03-16 16:17 ` [PATCH i-g-t v9 01/49] lib/igt_kms: Add a detect timeout value Louis Chauvet
@ 2026-03-16 18:50 ` Kamil Konieczny
0 siblings, 0 replies; 83+ messages in thread
From: Kamil Konieczny @ 2026-03-16 18:50 UTC (permalink / raw)
To: Louis Chauvet
Cc: igt-dev, thomas.petazzoni, luca.ceresoli, kory.maincent,
markyacoub, khaled.almahallawy
Hi Louis,
On 2026-03-16 at 17:17:22 +0100, Louis Chauvet wrote:
> 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 | 33 +++++++++++++++++++++++++++++++++
> lib/igt_core.h | 9 +++++++++
> lib/igt_kms.c | 1 +
> 3 files changed, 43 insertions(+)
>
> diff --git a/lib/igt_core.c b/lib/igt_core.c
> index 3ee670a41a91..e8f31d573512 100644
> --- a/lib/igt_core.c
> +++ b/lib/igt_core.c
> @@ -26,6 +26,7 @@
> *
> */
>
> +#include "igt_kms.h"
Why do you need this include here? lib/igt_kms.c already
has inlcuded igt_core.h
Regards,
Kamil
> #ifdef HAVE_LIBGEN_H
> #include <libgen.h>
> #endif
> @@ -271,6 +272,10 @@
> * # It is not mandatory and allows overriding default values.
> * [DUT]
> * SuspendResumeDelay=10
> + *
> + * # The following option define the timeout (in seconds) for detection feature
> + * # (waiting for a connector status)
> + * DisplayDetectTimeout=10.0
> * ]|
> *
> * Some specific configuration options may be used by specific parts of IGT,
> @@ -379,6 +384,21 @@ static bool stderr_needs_sentinel = false;
>
> static int _igt_dynamic_tests_executed = -1;
>
> +/**
> + * default_display_detect_timeout: Timeout for display detection, in seconds
> + */
> +static double default_display_detect_timeout;
> +
> +static void igt_set_default_display_detect_timeout(double timeout)
> +{
> + default_display_detect_timeout = timeout;
> +}
> +
> +double igt_default_display_detect_timeout(void)
> +{
> + return default_display_detect_timeout;
> +}
> +
> static void print_backtrace(void)
> {
> #ifndef HAVE_LIBUNWIND
> @@ -1003,6 +1023,7 @@ static void common_init_config(void)
> {
> GError *error = NULL;
> int ret = 0;
> + static double timeout = 0.0;
>
> igt_key_file = igt_load_igtrc();
>
> @@ -1023,6 +1044,18 @@ static void common_init_config(void)
> if (ret != 0)
> igt_set_autoresume_delay(ret);
>
> + 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;
> + }
> + g_clear_error(&error);
> + igt_set_default_display_detect_timeout(timeout);
> +
> /* Adding filters, order .igtrc, IGT_DEVICE, --device filter */
> if (igt_device_filter_count() > 0)
> igt_debug("Notice: using --device filters:\n");
> diff --git a/lib/igt_core.h b/lib/igt_core.h
> index 6845f853c980..daf83cb5380d 100644
> --- a/lib/igt_core.h
> +++ b/lib/igt_core.h
> @@ -143,6 +143,13 @@ struct _GKeyFile *igt_load_igtrc(void);
> */
> #define IGT_EXIT_ABORT 112
>
> +/**
> + * 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
> void __igt_assert_in_outer_scope(void);
>
> bool __igt_fixture(void);
> @@ -446,6 +453,8 @@ void __igt_subtest_group_restore(int, int);
> */
> #define igt_main() igt_main_args(NULL, NULL, NULL, NULL, NULL)
>
> +double igt_default_display_detect_timeout(void);
> +
> const char *igt_test_name(void);
> void igt_simple_init_parse_opts(int *argc, char **argv,
> const char *extra_short_opts,
> diff --git a/lib/igt_kms.c b/lib/igt_kms.c
> index 0b05ddd72977..692d75b70ecc 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
>
> --
> 2.52.0
>
^ permalink raw reply [flat|nested] 83+ messages in thread
* [PATCH i-g-t v9 02/49] lib/igt_kms: Add helper to wait for a specific status on a connector
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
2026-03-16 16:17 ` [PATCH i-g-t v9 01/49] lib/igt_kms: Add a detect timeout value Louis Chauvet
@ 2026-03-16 16:17 ` Louis Chauvet
2026-03-24 10:22 ` Kamil Konieczny
2026-03-16 16:17 ` [PATCH i-g-t v9 03/49] lib/igt_kms: Add function to list connected connectors Louis Chauvet
` (47 subsequent siblings)
49 siblings, 1 reply; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:17 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
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 | 41 +++++++++++++++++++++++++++++++++++++++++
lib/igt_kms.h | 3 +++
2 files changed, 44 insertions(+)
diff --git a/lib/igt_kms.c b/lib/igt_kms.c
index 692d75b70ecc..4a496ab256f0 100644
--- a/lib/igt_kms.c
+++ b/lib/igt_kms.c
@@ -8093,3 +8093,44 @@ igt_crtc_t *igt_random_crtc(igt_display_t *display)
return crtcs[rand() % n];
}
+
+/**
+ * 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();
+
+ igt_assert_eq(igt_gettime(&start), 0);
+ end = start;
+
+ while (igt_time_elapsed(&start, &end) <= timeout) {
+ connector = drmModeGetConnector(drm_fd, connector_id);
+ if (connector && connector->connection == drm_mode) {
+ drmModeFreeConnector(connector);
+ return true;
+ }
+ drmModeFreeConnector(connector);
+ usleep(10000);
+ igt_assert_eq(igt_gettime(&end), 0);
+ }
+
+ 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 081613a4b949..934f24d5915b 100644
--- a/lib/igt_kms.h
+++ b/lib/igt_kms.h
@@ -1301,4 +1301,7 @@ void igt_get_and_wait_out_fence(igt_output_t *output);
igt_colorop_t *igt_find_colorop(igt_display_t *display, uint32_t id);
+bool igt_wait_for_connector_status(int drm_fd, unsigned int connector_id, double timeout,
+ int drm_mode);
+
#endif /* __IGT_KMS_H__ */
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* Re: [PATCH i-g-t v9 02/49] lib/igt_kms: Add helper to wait for a specific status on a connector
2026-03-16 16:17 ` [PATCH i-g-t v9 02/49] lib/igt_kms: Add helper to wait for a specific status on a connector Louis Chauvet
@ 2026-03-24 10:22 ` Kamil Konieczny
0 siblings, 0 replies; 83+ messages in thread
From: Kamil Konieczny @ 2026-03-24 10:22 UTC (permalink / raw)
To: Louis Chauvet
Cc: igt-dev, thomas.petazzoni, luca.ceresoli, kory.maincent,
markyacoub, khaled.almahallawy
Hi Louis,
On 2026-03-16 at 17:17:23 +0100, Louis Chauvet wrote:
> 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 | 41 +++++++++++++++++++++++++++++++++++++++++
> lib/igt_kms.h | 3 +++
> 2 files changed, 44 insertions(+)
>
> diff --git a/lib/igt_kms.c b/lib/igt_kms.c
> index 692d75b70ecc..4a496ab256f0 100644
> --- a/lib/igt_kms.c
> +++ b/lib/igt_kms.c
> @@ -8093,3 +8093,44 @@ igt_crtc_t *igt_random_crtc(igt_display_t *display)
>
> return crtcs[rand() % n];
> }
> +
> +/**
> + * 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)
Checks for equality are risky for floating numbers, instead use
inequality:
if (timeout < 0.0)
> + timeout = igt_default_display_detect_timeout();
> +
> + igt_assert_eq(igt_gettime(&start), 0);
> + end = start;
> +
> + while (igt_time_elapsed(&start, &end) <= timeout) {
> + connector = drmModeGetConnector(drm_fd, connector_id);
> + if (connector && connector->connection == drm_mode) {
> + drmModeFreeConnector(connector);
> + return true;
> + }
> + drmModeFreeConnector(connector);
> + usleep(10000);
imho this should be defined as constant or with #define and used
here.
Regards,
Kamil
> + igt_assert_eq(igt_gettime(&end), 0);
> + }
> +
> + 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 081613a4b949..934f24d5915b 100644
> --- a/lib/igt_kms.h
> +++ b/lib/igt_kms.h
> @@ -1301,4 +1301,7 @@ void igt_get_and_wait_out_fence(igt_output_t *output);
>
> igt_colorop_t *igt_find_colorop(igt_display_t *display, uint32_t id);
>
> +bool igt_wait_for_connector_status(int drm_fd, unsigned int connector_id, double timeout,
> + int drm_mode);
> +
> #endif /* __IGT_KMS_H__ */
>
> --
> 2.52.0
>
^ permalink raw reply [flat|nested] 83+ messages in thread
* [PATCH i-g-t v9 03/49] lib/igt_kms: Add function to list connected connectors
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
2026-03-16 16:17 ` [PATCH i-g-t v9 01/49] lib/igt_kms: Add a detect timeout value Louis Chauvet
2026-03-16 16:17 ` [PATCH i-g-t v9 02/49] lib/igt_kms: Add helper to wait for a specific status on a connector Louis Chauvet
@ 2026-03-16 16:17 ` Louis Chauvet
2026-03-24 10:34 ` Kamil Konieczny
2026-03-16 16:17 ` [PATCH i-g-t v9 04/49] lib/igt_kms: Add helper to obtain a connector by its name or MST path Louis Chauvet
` (46 subsequent siblings)
49 siblings, 1 reply; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:17 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
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 | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
lib/igt_kms.h | 1 +
2 files changed, 81 insertions(+)
diff --git a/lib/igt_kms.c b/lib/igt_kms.c
index 4a496ab256f0..72a38334f921 100644
--- a/lib/igt_kms.c
+++ b/lib/igt_kms.c
@@ -8094,6 +8094,31 @@ igt_crtc_t *igt_random_crtc(igt_display_t *display)
return crtcs[rand() % n];
}
+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
+ */
+ igt_assert_eq(igt_gettime(&start), 0);
+ end = start;
+
+ while (!connector &&
+ igt_time_elapsed(&start, &end) <= timeout) {
+ drmModeFreeConnector(connector);
+ connector = drmModeGetConnector(drm_fd, connector_id);
+ usleep(10000);
+ igt_assert_eq(igt_gettime(&end), 0);
+ }
+
+ return connector;
+}
+
/**
* igt_wait_for_connector_status:
* @drm_fd: drm file descriptor
@@ -8134,3 +8159,58 @@ bool igt_wait_for_connector_status(int drm_fd, unsigned int connector_id, double
return false;
}
+
+/**
+ * igt_get_connected_connectors:
+ *
+ * @drm_fd: DRM file descriptor
+ * @connector_ids: Out pointer for the array of connector ids connected. The function
+ * will allocate and store the pointer in this out pointer. The caller is
+ * responsible to free the inner pointer using free(*connector_ids). 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)
+{
+ uint32_t *tmp_alloc;
+ int connected_count = 0;
+ double timeout = igt_default_display_detect_timeout();
+ drmModeResPtr resources;
+
+ igt_assert(drm_fd);
+ *connector_ids = NULL;
+
+ resources = drmModeGetResources(drm_fd);
+ igt_assert(resources);
+ for (int j = 0; j < resources->count_connectors; j++) {
+ int status = DRM_MODE_UNKNOWNCONNECTION;
+
+ drmModeConnectorPtr connector = igt_wait_for_connector(drm_fd,
+ resources->connectors[j],
+ timeout);
+
+ if (connector)
+ status = connector->connection;
+ drmModeFreeConnector(connector);
+
+ if (status == DRM_MODE_CONNECTED) {
+ tmp_alloc = reallocarray(*connector_ids,
+ connected_count
+ + 1, sizeof(**connector_ids));
+ if (tmp_alloc) {
+ *connector_ids = tmp_alloc;
+ } else {
+ free(*connector_ids);
+ drmModeFreeResources(resources);
+ igt_assert(false);
+ }
+ (*connector_ids)[connected_count] = resources->connectors[j];
+ connected_count++;
+ }
+ }
+ drmModeFreeResources(resources);
+
+ return connected_count;
+}
diff --git a/lib/igt_kms.h b/lib/igt_kms.h
index 934f24d5915b..4efd40a3ad08 100644
--- a/lib/igt_kms.h
+++ b/lib/igt_kms.h
@@ -1303,5 +1303,6 @@ igt_colorop_t *igt_find_colorop(igt_display_t *display, uint32_t id);
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.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* Re: [PATCH i-g-t v9 03/49] lib/igt_kms: Add function to list connected connectors
2026-03-16 16:17 ` [PATCH i-g-t v9 03/49] lib/igt_kms: Add function to list connected connectors Louis Chauvet
@ 2026-03-24 10:34 ` Kamil Konieczny
2026-03-24 13:56 ` Louis Chauvet
0 siblings, 1 reply; 83+ messages in thread
From: Kamil Konieczny @ 2026-03-24 10:34 UTC (permalink / raw)
To: Louis Chauvet
Cc: igt-dev, thomas.petazzoni, luca.ceresoli, kory.maincent,
markyacoub, khaled.almahallawy
Hi Louis,
On 2026-03-16 at 17:17:24 +0100, Louis Chauvet wrote:
> 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 | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> lib/igt_kms.h | 1 +
> 2 files changed, 81 insertions(+)
>
> diff --git a/lib/igt_kms.c b/lib/igt_kms.c
> index 4a496ab256f0..72a38334f921 100644
> --- a/lib/igt_kms.c
> +++ b/lib/igt_kms.c
> @@ -8094,6 +8094,31 @@ igt_crtc_t *igt_random_crtc(igt_display_t *display)
> return crtcs[rand() % n];
> }
>
> +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
> + */
> + igt_assert_eq(igt_gettime(&start), 0);
> + end = start;
> +
> + while (!connector &&
> + igt_time_elapsed(&start, &end) <= timeout) {
> + drmModeFreeConnector(connector);
> + connector = drmModeGetConnector(drm_fd, connector_id);
> + usleep(10000);
Same here, use defined constant.
> + igt_assert_eq(igt_gettime(&end), 0);
> + }
> +
> + return connector;
> +}
> +
> /**
> * igt_wait_for_connector_status:
> * @drm_fd: drm file descriptor
> @@ -8134,3 +8159,58 @@ bool igt_wait_for_connector_status(int drm_fd, unsigned int connector_id, double
>
> return false;
> }
> +
> +/**
> + * igt_get_connected_connectors:
> + *
> + * @drm_fd: DRM file descriptor
> + * @connector_ids: Out pointer for the array of connector ids connected. The function
> + * will allocate and store the pointer in this out pointer. The caller is
> + * responsible to free the inner pointer using free(*connector_ids). caller.
s/ caller.//
> + *
> + * This will probe all the connectors and return the list of all
> + * connected connectors.
Document that this could assert. Also add newline here.
> + * Returns: The number of connectors in @connector_ids
> + */
> +int igt_get_connected_connectors(int drm_fd, uint32_t **connector_ids)
> +{
> + uint32_t *tmp_alloc;
> + int connected_count = 0;
> + double timeout = igt_default_display_detect_timeout();
> + drmModeResPtr resources;
> +
> + igt_assert(drm_fd);
-1 is invalid but will not assert.
> + *connector_ids = NULL;
> +
> + resources = drmModeGetResources(drm_fd);
> + igt_assert(resources);
Cannot you just return 0 instead of asserting?
Or at least use igt_assert_f() with explanation message.
> + for (int j = 0; j < resources->count_connectors; j++) {
> + int status = DRM_MODE_UNKNOWNCONNECTION;
> +
> + drmModeConnectorPtr connector = igt_wait_for_connector(drm_fd,
> + resources->connectors[j],
> + timeout);
> +
> + if (connector)
> + status = connector->connection;
> + drmModeFreeConnector(connector);
> +
> + if (status == DRM_MODE_CONNECTED) {
> + tmp_alloc = reallocarray(*connector_ids,
> + connected_count
> + + 1, sizeof(**connector_ids));
Why this line breaks? imho better:
tmp_alloc = reallocarray(*connector_ids,
connected_count + 1,
sizeof(**connector_ids));
> + if (tmp_alloc) {
> + *connector_ids = tmp_alloc;
> + } else {
> + free(*connector_ids);
> + drmModeFreeResources(resources);
> + igt_assert(false);
Use igt_assert_f() with explanation message.
> + }
Btw better flow is with error check first:
if (!tmp_alloc) {
...handle error here with assert
}
*connector_ids = tmp_alloc;
Regards,
Kamil
> + (*connector_ids)[connected_count] = resources->connectors[j];
> + connected_count++;
> + }
> + }
> + drmModeFreeResources(resources);
> +
> + return connected_count;
> +}
> diff --git a/lib/igt_kms.h b/lib/igt_kms.h
> index 934f24d5915b..4efd40a3ad08 100644
> --- a/lib/igt_kms.h
> +++ b/lib/igt_kms.h
> @@ -1303,5 +1303,6 @@ igt_colorop_t *igt_find_colorop(igt_display_t *display, uint32_t id);
>
> 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.52.0
>
^ permalink raw reply [flat|nested] 83+ messages in thread* Re: [PATCH i-g-t v9 03/49] lib/igt_kms: Add function to list connected connectors
2026-03-24 10:34 ` Kamil Konieczny
@ 2026-03-24 13:56 ` Louis Chauvet
0 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-24 13:56 UTC (permalink / raw)
To: Kamil Konieczny, igt-dev, thomas.petazzoni, luca.ceresoli,
kory.maincent, markyacoub, khaled.almahallawy
On 3/24/26 11:34, Kamil Konieczny wrote:
> Hi Louis,
> On 2026-03-16 at 17:17:24 +0100, Louis Chauvet wrote:
>> 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 | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>> lib/igt_kms.h | 1 +
>> 2 files changed, 81 insertions(+)
>>
>> diff --git a/lib/igt_kms.c b/lib/igt_kms.c
>> index 4a496ab256f0..72a38334f921 100644
>> --- a/lib/igt_kms.c
>> +++ b/lib/igt_kms.c
>> @@ -8094,6 +8094,31 @@ igt_crtc_t *igt_random_crtc(igt_display_t *display)
>> return crtcs[rand() % n];
>> }
>>
>> +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
>> + */
>> + igt_assert_eq(igt_gettime(&start), 0);
>> + end = start;
>> +
>> + while (!connector &&
>> + igt_time_elapsed(&start, &end) <= timeout) {
>> + drmModeFreeConnector(connector);
>> + connector = drmModeGetConnector(drm_fd, connector_id);
>> + usleep(10000);
>
> Same here, use defined constant.
>
>> + igt_assert_eq(igt_gettime(&end), 0);
>> + }
>> +
>> + return connector;
>> +}
>> +
>> /**
>> * igt_wait_for_connector_status:
>> * @drm_fd: drm file descriptor
>> @@ -8134,3 +8159,58 @@ bool igt_wait_for_connector_status(int drm_fd, unsigned int connector_id, double
>>
>> return false;
>> }
>> +
>> +/**
>> + * igt_get_connected_connectors:
>> + *
>> + * @drm_fd: DRM file descriptor
>> + * @connector_ids: Out pointer for the array of connector ids connected. The function
>> + * will allocate and store the pointer in this out pointer. The caller is
>> + * responsible to free the inner pointer using free(*connector_ids). caller.
>
> s/ caller.//
>
>> + *
>> + * This will probe all the connectors and return the list of all
>> + * connected connectors.
>
> Document that this could assert. Also add newline here.
>
>> + * Returns: The number of connectors in @connector_ids
>> + */
>> +int igt_get_connected_connectors(int drm_fd, uint32_t **connector_ids)
>> +{
>> + uint32_t *tmp_alloc;
>> + int connected_count = 0;
>> + double timeout = igt_default_display_detect_timeout();
>> + drmModeResPtr resources;
>> +
>> + igt_assert(drm_fd);
>
> -1 is invalid but will not assert.
>
>> + *connector_ids = NULL;
>> +
>> + resources = drmModeGetResources(drm_fd);
>> + igt_assert(resources);
>
> Cannot you just return 0 instead of asserting?
> Or at least use igt_assert_f() with explanation message.
If the drmModeGetResources fails, it means that there is a big kernel
issue or that the drm_fd is not a DRM file descriptor. In either case I
think it is useless to continue or try to handle the error in the test case.
>> + for (int j = 0; j < resources->count_connectors; j++) {
>> + int status = DRM_MODE_UNKNOWNCONNECTION;
>> +
>> + drmModeConnectorPtr connector = igt_wait_for_connector(drm_fd,
>> + resources->connectors[j],
>> + timeout);
>> +
>> + if (connector)
>> + status = connector->connection;
>> + drmModeFreeConnector(connector);
>> +
>> + if (status == DRM_MODE_CONNECTED) {
>> + tmp_alloc = reallocarray(*connector_ids,
>> + connected_count
>> + + 1, sizeof(**connector_ids));
>
> Why this line breaks? imho better:
> tmp_alloc = reallocarray(*connector_ids,
> connected_count + 1,
> sizeof(**connector_ids));
I agree, it was probably the autowrap of my editor.
>> + if (tmp_alloc) {
>> + *connector_ids = tmp_alloc;
>> + } else {
>> + free(*connector_ids);
>> + drmModeFreeResources(resources);
>> + igt_assert(false);
>
> Use igt_assert_f() with explanation message.
>
>> + }
>
> Btw better flow is with error check first:
> if (!tmp_alloc) {
> ...handle error here with assert
> }
>
> *connector_ids = tmp_alloc;
Right, done for the next iteration.
> Regards,
> Kamil
>
>> + (*connector_ids)[connected_count] = resources->connectors[j];
>> + connected_count++;
>> + }
>> + }
>> + drmModeFreeResources(resources);
>> +
>> + return connected_count;
>> +}
>> diff --git a/lib/igt_kms.h b/lib/igt_kms.h
>> index 934f24d5915b..4efd40a3ad08 100644
>> --- a/lib/igt_kms.h
>> +++ b/lib/igt_kms.h
>> @@ -1303,5 +1303,6 @@ igt_colorop_t *igt_find_colorop(igt_display_t *display, uint32_t id);
>>
>> 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.52.0
>>
^ permalink raw reply [flat|nested] 83+ messages in thread
* [PATCH i-g-t v9 04/49] lib/igt_kms: Add helper to obtain a connector by its name or MST path
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (2 preceding siblings ...)
2026-03-16 16:17 ` [PATCH i-g-t v9 03/49] lib/igt_kms: Add function to list connected connectors Louis Chauvet
@ 2026-03-16 16:17 ` Louis Chauvet
2026-03-16 16:17 ` [PATCH i-g-t v9 05/49] lib/igt_kms: Add helper to wait for new connectors Louis Chauvet
` (45 subsequent siblings)
49 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:17 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
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 72a38334f921..f0f27ccda9d8 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
+/**
+ * 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);
@@ -8214,3 +8218,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)
+{
+ double timeout = igt_default_display_detect_timeout();
+ drmModeResPtr res = drmModeGetResources(drm_fd);
+ int i;
+
+ if (!res)
+ return NULL;
+
+ for (i = 0; i < res->count_connectors; i++) {
+ char name[IGT_KMS_CONNECTOR_NAME_SIZE];
+ int len;
+
+ 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, sizeof(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 4efd40a3ad08..0095d9610e42 100644
--- a/lib/igt_kms.h
+++ b/lib/igt_kms.h
@@ -1304,5 +1304,8 @@ igt_colorop_t *igt_find_colorop(igt_display_t *display, uint32_t id);
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.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* [PATCH i-g-t v9 05/49] lib/igt_kms: Add helper to wait for new connectors
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (3 preceding siblings ...)
2026-03-16 16:17 ` [PATCH i-g-t v9 04/49] lib/igt_kms: Add helper to obtain a connector by its name or MST path Louis Chauvet
@ 2026-03-16 16:17 ` Louis Chauvet
2026-03-17 14:56 ` Kory Maincent
2026-03-24 10:11 ` Kamil Konieczny
2026-03-16 16:17 ` [PATCH i-g-t v9 06/49] lib/tests: Add tests for array manipulations Louis Chauvet
` (44 subsequent siblings)
49 siblings, 2 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:17 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
Add list comparison utilities and a new function to wait for new
connectors to appear on the system.
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
Reviewed-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
lib/igt_kms.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
lib/igt_kms.h | 6 ++++
2 files changed, 103 insertions(+)
diff --git a/lib/igt_kms.c b/lib/igt_kms.c
index f0f27ccda9d8..b220de419a70 100644
--- a/lib/igt_kms.c
+++ b/lib/igt_kms.c
@@ -8322,3 +8322,100 @@ uint32_t igt_get_connector_id_from_mst_path(int drm_fd, const void *mst_path)
return 0;
}
+
+/**
+ * array_contains() - Search an element in the array
+ *
+ * @array: Pointer to the array to search into
+ * @array_len: Length of the array
+ * @value: Value to search in the array
+ *
+ * Returns true if @array contains @value
+ */
+static bool array_contains(const uint32_t *array, int array_len, uint32_t value)
+{
+ igt_assert(array_len == 0 || array);
+
+ for (int i = 0; i < array_len; i++)
+ if (array[i] == value)
+ return true;
+
+ return false;
+}
+
+/**
+ * get_array_diff() - Compute and return the set difference between two arrays
+ *
+ * @array_a: Pointer to the first array to compare
+ * @array_a_len: Length of the first array
+ * @array_b: Pointer to the second array to compare
+ * @array_b_len: Length of the second array
+ * @diff: Out pointer returning a array of items in @array_a but not
+ * in @array_b. Can be NULL to only count the elements.
+ *
+ * Returns the number of element which are in @array_a but not in @array_b.
+ */
+int
+get_array_diff(const uint32_t *array_a, int array_a_len, const uint32_t *array_b, int array_b_len,
+ uint32_t **diff)
+{
+ int diff_len = 0;
+
+ igt_assert(array_a_len == 0 || array_a);
+ igt_assert(array_b_len == 0 || array_b);
+
+ if (diff)
+ *diff = NULL;
+
+ for (int i = 0; i < array_a_len; i++) {
+ if (!array_contains(array_b, array_b_len, array_a[i])) {
+ if (diff) {
+ *diff = reallocarray(*diff, diff_len + 1, sizeof(**diff));
+ igt_assert(*diff);
+ (*diff)[diff_len] = array_a[i];
+ }
+
+ diff_len++;
+ }
+ }
+
+ return diff_len;
+}
+
+/**
+ * kms_wait_for_new_connectors() - Wait for new connector to appear
+ *
+ * @newly_connected: Out pointer returning the array of connectors currently
+ * found connected
+ * @already_connected: Input pointer to array of connectors previously
+ * connected
+ * @already_connected_count: Length of @already_connected
+ * @drm_fd: DRM file descriptor
+ *
+ * Returns the number of connectors not in @already_connected and currently
+ * found connected.
+ */
+int kms_wait_for_new_connectors(uint32_t **newly_connected,
+ const uint32_t *already_connected,
+ int already_connected_count, int drm_fd)
+{
+ int newly_connected_count;
+ struct timespec start, end;
+
+ igt_assert(newly_connected);
+ igt_assert(drm_fd);
+
+ igt_assert_eq(igt_gettime(&start), 0);
+ igt_assert_eq(igt_gettime(&end), 0);
+ do {
+ if (*newly_connected)
+ free(*newly_connected);
+ newly_connected_count = igt_get_connected_connectors(drm_fd, newly_connected);
+ igt_assert_eq(igt_gettime(&end), 0);
+ } while (!get_array_diff(*newly_connected, newly_connected_count,
+ already_connected, already_connected_count,
+ NULL) &&
+ igt_time_elapsed(&start, &end) <= igt_default_display_detect_timeout());
+
+ return newly_connected_count;
+}
diff --git a/lib/igt_kms.h b/lib/igt_kms.h
index 0095d9610e42..c6aae034b5b9 100644
--- a/lib/igt_kms.h
+++ b/lib/igt_kms.h
@@ -1308,4 +1308,10 @@ drmModeConnectorPtr igt_get_connector_from_name(int drm_fd, const char *port_nam
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);
+int kms_wait_for_new_connectors(uint32_t **newly_connected,
+ const uint32_t *already_connected,
+ int already_connected_count, int drm_fd);
+int
+get_array_diff(const uint32_t *array_a, int array_a_len, const uint32_t *array_b, int array_b_len,
+ uint32_t **diff);
#endif /* __IGT_KMS_H__ */
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* Re: [PATCH i-g-t v9 05/49] lib/igt_kms: Add helper to wait for new connectors
2026-03-16 16:17 ` [PATCH i-g-t v9 05/49] lib/igt_kms: Add helper to wait for new connectors Louis Chauvet
@ 2026-03-17 14:56 ` Kory Maincent
2026-03-23 14:40 ` Louis Chauvet
2026-03-24 10:11 ` Kamil Konieczny
1 sibling, 1 reply; 83+ messages in thread
From: Kory Maincent @ 2026-03-17 14:56 UTC (permalink / raw)
To: Louis Chauvet
Cc: igt-dev, thomas.petazzoni, luca.ceresoli, markyacoub,
khaled.almahallawy
On Mon, 16 Mar 2026 17:17:26 +0100
Louis Chauvet <louis.chauvet@bootlin.com> wrote:
> Add list comparison utilities and a new function to wait for new
> connectors to appear on the system.
>
> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> Reviewed-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
> ---
> lib/igt_kms.c | 97
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/igt_kms.h |
> 6 ++++ 2 files changed, 103 insertions(+)
>
> diff --git a/lib/igt_kms.c b/lib/igt_kms.c
> index f0f27ccda9d8..b220de419a70 100644
> --- a/lib/igt_kms.c
> +++ b/lib/igt_kms.c
> @@ -8322,3 +8322,100 @@ uint32_t igt_get_connector_id_from_mst_path(int
> drm_fd, const void *mst_path)
> return 0;
> }
> +
> +/**
> + * array_contains() - Search an element in the array
> + *
> + * @array: Pointer to the array to search into
> + * @array_len: Length of the array
> + * @value: Value to search in the array
> + *
> + * Returns true if @array contains @value
> + */
> +static bool array_contains(const uint32_t *array, int array_len, uint32_t
> value) +{
> + igt_assert(array_len == 0 || array);
> +
> + for (int i = 0; i < array_len; i++)
> + if (array[i] == value)
> + return true;
> +
> + return false;
> +}
> +
> +/**
> + * get_array_diff() - Compute and return the set difference between two
> arrays
> + *
> + * @array_a: Pointer to the first array to compare
> + * @array_a_len: Length of the first array
> + * @array_b: Pointer to the second array to compare
> + * @array_b_len: Length of the second array
> + * @diff: Out pointer returning a array of items in @array_a but not
> + * in @array_b. Can be NULL to only count the elements.
> + *
> + * Returns the number of element which are in @array_a but not in @array_b.
> + */
> +int
> +get_array_diff(const uint32_t *array_a, int array_a_len, const uint32_t
> *array_b, int array_b_len,
> + uint32_t **diff)
> +{
> + int diff_len = 0;
> +
> + igt_assert(array_a_len == 0 || array_a);
> + igt_assert(array_b_len == 0 || array_b);
> +
> + if (diff)
> + *diff = NULL;
> +
> + for (int i = 0; i < array_a_len; i++) {
> + if (!array_contains(array_b, array_b_len, array_a[i])) {
> + if (diff) {
> + *diff = reallocarray(*diff, diff_len + 1,
> sizeof(**diff));
> + igt_assert(*diff);
> + (*diff)[diff_len] = array_a[i];
> + }
> +
> + diff_len++;
> + }
> + }
> +
> + return diff_len;
> +}
> +
> +/**
> + * kms_wait_for_new_connectors() - Wait for new connector to appear
> + *
> + * @newly_connected: Out pointer returning the array of connectors currently
> + * found connected
> + * @already_connected: Input pointer to array of connectors previously
> + * connected
> + * @already_connected_count: Length of @already_connected
> + * @drm_fd: DRM file descriptor
> + *
> + * Returns the number of connectors not in @already_connected and currently
> + * found connected.
> + */
> +int kms_wait_for_new_connectors(uint32_t **newly_connected,
> + const uint32_t *already_connected,
> + int already_connected_count, int drm_fd)
> +{
> + int newly_connected_count;
> + struct timespec start, end;
> +
> + igt_assert(newly_connected);
> + igt_assert(drm_fd);
Are you sure 0 can't be set as DRM file descriptor?
With that fixed you can add my R-by.
--
Köry Maincent, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 83+ messages in thread* Re: [PATCH i-g-t v9 05/49] lib/igt_kms: Add helper to wait for new connectors
2026-03-17 14:56 ` Kory Maincent
@ 2026-03-23 14:40 ` Louis Chauvet
0 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-23 14:40 UTC (permalink / raw)
To: Kory Maincent
Cc: igt-dev, thomas.petazzoni, luca.ceresoli, markyacoub,
khaled.almahallawy
On 3/17/26 15:56, Kory Maincent wrote:
> On Mon, 16 Mar 2026 17:17:26 +0100
> Louis Chauvet <louis.chauvet@bootlin.com> wrote:
>
>> Add list comparison utilities and a new function to wait for new
>> connectors to appear on the system.
>>
>> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
>> Reviewed-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
>> ---
>> lib/igt_kms.c | 97
>> +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/igt_kms.h |
>> 6 ++++ 2 files changed, 103 insertions(+)
>>
>> diff --git a/lib/igt_kms.c b/lib/igt_kms.c
>> index f0f27ccda9d8..b220de419a70 100644
>> --- a/lib/igt_kms.c
>> +++ b/lib/igt_kms.c
>> @@ -8322,3 +8322,100 @@ uint32_t igt_get_connector_id_from_mst_path(int
>> drm_fd, const void *mst_path)
>> return 0;
>> }
>> +
>> +/**
>> + * array_contains() - Search an element in the array
>> + *
>> + * @array: Pointer to the array to search into
>> + * @array_len: Length of the array
>> + * @value: Value to search in the array
>> + *
>> + * Returns true if @array contains @value
>> + */
>> +static bool array_contains(const uint32_t *array, int array_len, uint32_t
>> value) +{
>> + igt_assert(array_len == 0 || array);
>> +
>> + for (int i = 0; i < array_len; i++)
>> + if (array[i] == value)
>> + return true;
>> +
>> + return false;
>> +}
>> +
>> +/**
>> + * get_array_diff() - Compute and return the set difference between two
>> arrays
>> + *
>> + * @array_a: Pointer to the first array to compare
>> + * @array_a_len: Length of the first array
>> + * @array_b: Pointer to the second array to compare
>> + * @array_b_len: Length of the second array
>> + * @diff: Out pointer returning a array of items in @array_a but not
>> + * in @array_b. Can be NULL to only count the elements.
>> + *
>> + * Returns the number of element which are in @array_a but not in @array_b.
>> + */
>> +int
>> +get_array_diff(const uint32_t *array_a, int array_a_len, const uint32_t
>> *array_b, int array_b_len,
>> + uint32_t **diff)
>> +{
>> + int diff_len = 0;
>> +
>> + igt_assert(array_a_len == 0 || array_a);
>> + igt_assert(array_b_len == 0 || array_b);
>> +
>> + if (diff)
>> + *diff = NULL;
>> +
>> + for (int i = 0; i < array_a_len; i++) {
>> + if (!array_contains(array_b, array_b_len, array_a[i])) {
>> + if (diff) {
>> + *diff = reallocarray(*diff, diff_len + 1,
>> sizeof(**diff));
>> + igt_assert(*diff);
>> + (*diff)[diff_len] = array_a[i];
>> + }
>> +
>> + diff_len++;
>> + }
>> + }
>> +
>> + return diff_len;
>> +}
>> +
>> +/**
>> + * kms_wait_for_new_connectors() - Wait for new connector to appear
>> + *
>> + * @newly_connected: Out pointer returning the array of connectors currently
>> + * found connected
>> + * @already_connected: Input pointer to array of connectors previously
>> + * connected
>> + * @already_connected_count: Length of @already_connected
>> + * @drm_fd: DRM file descriptor
>> + *
>> + * Returns the number of connectors not in @already_connected and currently
>> + * found connected.
>> + */
>> +int kms_wait_for_new_connectors(uint32_t **newly_connected,
>> + const uint32_t *already_connected,
>> + int already_connected_count, int drm_fd)
>> +{
>> + int newly_connected_count;
>> + struct timespec start, end;
>> +
>> + igt_assert(newly_connected);
>> + igt_assert(drm_fd);
>
> Are you sure 0 can't be set as DRM file descriptor?
> With that fixed you can add my R-by.
>
No, replaced by igt_assert_fd, thanks!
^ permalink raw reply [flat|nested] 83+ messages in thread
* Re: [PATCH i-g-t v9 05/49] lib/igt_kms: Add helper to wait for new connectors
2026-03-16 16:17 ` [PATCH i-g-t v9 05/49] lib/igt_kms: Add helper to wait for new connectors Louis Chauvet
2026-03-17 14:56 ` Kory Maincent
@ 2026-03-24 10:11 ` Kamil Konieczny
1 sibling, 0 replies; 83+ messages in thread
From: Kamil Konieczny @ 2026-03-24 10:11 UTC (permalink / raw)
To: Louis Chauvet
Cc: igt-dev, thomas.petazzoni, luca.ceresoli, kory.maincent,
markyacoub, khaled.almahallawy
Hi Louis,
On 2026-03-16 at 17:17:26 +0100, Louis Chauvet wrote:
> Add list comparison utilities and a new function to wait for new
> connectors to appear on the system.
>
> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> Reviewed-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
> ---
> lib/igt_kms.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> lib/igt_kms.h | 6 ++++
> 2 files changed, 103 insertions(+)
>
> diff --git a/lib/igt_kms.c b/lib/igt_kms.c
> index f0f27ccda9d8..b220de419a70 100644
> --- a/lib/igt_kms.c
> +++ b/lib/igt_kms.c
> @@ -8322,3 +8322,100 @@ uint32_t igt_get_connector_id_from_mst_path(int drm_fd, const void *mst_path)
>
> return 0;
> }
> +
> +/**
> + * array_contains() - Search an element in the array
> + *
> + * @array: Pointer to the array to search into
> + * @array_len: Length of the array
> + * @value: Value to search in the array
> + *
> + * Returns true if @array contains @value
> + */
> +static bool array_contains(const uint32_t *array, int array_len, uint32_t value)
> +{
> + igt_assert(array_len == 0 || array);
> +
> + for (int i = 0; i < array_len; i++)
> + if (array[i] == value)
> + return true;
> +
> + return false;
> +}
> +
> +/**
> + * get_array_diff() - Compute and return the set difference between two arrays
> + *
> + * @array_a: Pointer to the first array to compare
> + * @array_a_len: Length of the first array
> + * @array_b: Pointer to the second array to compare
> + * @array_b_len: Length of the second array
> + * @diff: Out pointer returning a array of items in @array_a but not
> + * in @array_b. Can be NULL to only count the elements.
> + *
> + * Returns the number of element which are in @array_a but not in @array_b.
> + */
> +int
> +get_array_diff(const uint32_t *array_a, int array_a_len, const uint32_t *array_b, int array_b_len,
> + uint32_t **diff)
> +{
> + int diff_len = 0;
> +
> + igt_assert(array_a_len == 0 || array_a);
> + igt_assert(array_b_len == 0 || array_b);
> +
> + if (diff)
> + *diff = NULL;
> +
> + for (int i = 0; i < array_a_len; i++) {
> + if (!array_contains(array_b, array_b_len, array_a[i])) {
> + if (diff) {
> + *diff = reallocarray(*diff, diff_len + 1, sizeof(**diff));
> + igt_assert(*diff);
> + (*diff)[diff_len] = array_a[i];
> + }
> +
> + diff_len++;
> + }
> + }
> +
> + return diff_len;
> +}
> +
> +/**
> + * kms_wait_for_new_connectors() - Wait for new connector to appear
> + *
> + * @newly_connected: Out pointer returning the array of connectors currently
> + * found connected
> + * @already_connected: Input pointer to array of connectors previously
> + * connected
> + * @already_connected_count: Length of @already_connected
> + * @drm_fd: DRM file descriptor
> + *
> + * Returns the number of connectors not in @already_connected and currently
> + * found connected.
Document when this function will assert.
> + */
> +int kms_wait_for_new_connectors(uint32_t **newly_connected,
> + const uint32_t *already_connected,
> + int already_connected_count, int drm_fd)
> +{
> + int newly_connected_count;
> + struct timespec start, end;
> +
> + igt_assert(newly_connected);
> + igt_assert(drm_fd);
This will fail only for 0, but invalid drm_fd could be -1.
There is a separate assert macro for fd.
> +
> + igt_assert_eq(igt_gettime(&start), 0);
> + igt_assert_eq(igt_gettime(&end), 0);
You do not need it here, it will be filled just before 'while'.
> + do {
> + if (*newly_connected)
> + free(*newly_connected);
Also document that it will free anything passed to it in newly_connected
and refill it.
Regards,
Kamil
> + newly_connected_count = igt_get_connected_connectors(drm_fd, newly_connected);
> + igt_assert_eq(igt_gettime(&end), 0);
> + } while (!get_array_diff(*newly_connected, newly_connected_count,
> + already_connected, already_connected_count,
> + NULL) &&
> + igt_time_elapsed(&start, &end) <= igt_default_display_detect_timeout());
> +
> + return newly_connected_count;
> +}
> diff --git a/lib/igt_kms.h b/lib/igt_kms.h
> index 0095d9610e42..c6aae034b5b9 100644
> --- a/lib/igt_kms.h
> +++ b/lib/igt_kms.h
> @@ -1308,4 +1308,10 @@ drmModeConnectorPtr igt_get_connector_from_name(int drm_fd, const char *port_nam
> 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);
>
> +int kms_wait_for_new_connectors(uint32_t **newly_connected,
> + const uint32_t *already_connected,
> + int already_connected_count, int drm_fd);
> +int
> +get_array_diff(const uint32_t *array_a, int array_a_len, const uint32_t *array_b, int array_b_len,
> + uint32_t **diff);
> #endif /* __IGT_KMS_H__ */
>
> --
> 2.52.0
>
^ permalink raw reply [flat|nested] 83+ messages in thread
* [PATCH i-g-t v9 06/49] lib/tests: Add tests for array manipulations
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (4 preceding siblings ...)
2026-03-16 16:17 ` [PATCH i-g-t v9 05/49] lib/igt_kms: Add helper to wait for new connectors Louis Chauvet
@ 2026-03-16 16:17 ` Louis Chauvet
2026-03-17 15:00 ` Kory Maincent
2026-03-16 16:17 ` [PATCH i-g-t v9 07/49] lib/igt_kms: Add helper to get a pipe from a connector Louis Chauvet
` (43 subsequent siblings)
49 siblings, 1 reply; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:17 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
As array manipulation functions are not trivial, add few tests to ensure
they work properly.
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
lib/tests/igt_kms_array_diff.c | 159 +++++++++++++++++++++++++++++++++++++++++
lib/tests/meson.build | 1 +
2 files changed, 160 insertions(+)
diff --git a/lib/tests/igt_kms_array_diff.c b/lib/tests/igt_kms_array_diff.c
new file mode 100644
index 000000000000..ebdc3b62bc31
--- /dev/null
+++ b/lib/tests/igt_kms_array_diff.c
@@ -0,0 +1,159 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2026 Google
+ *
+ * Authors:
+ * Louis Chauvet <louis.chauvet@bootlin.com> (assisted by MistralAI)
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "drmtest.h"
+#include "igt_core.h"
+#include "igt_kms.h"
+
+/**
+ * TEST: get_array_diff
+ * Category: Core
+ * Description: Test the get_array_diff() function
+ *
+ * SUBTEST: get_array_diff-empty-both
+ * Description: Test with both arrays empty
+ *
+ * SUBTEST: get_array_diff-empty-a
+ * Description: Test with array_a empty
+ *
+ * SUBTEST: get_array_diff-empty-b
+ * Description: Test with array_b empty
+ *
+ * SUBTEST: get_array_diff-no-diff
+ * Description: Test with identical arrays (no difference)
+ *
+ * SUBTEST: get_array_diff-full-diff
+ * Description: Test with completely different arrays
+ *
+ * SUBTEST: get_array_diff-partial-diff
+ * Description: Test with partial overlap between arrays
+ *
+ * SUBTEST: get_array_diff-null-diff
+ * Description: Test with diff parameter set to NULL (count only)
+ */
+
+static void test_get_array_diff_empty_both(void)
+{
+ uint32_t *diff = NULL;
+ int diff_len;
+
+ diff_len = get_array_diff(NULL, 0, NULL, 0, &diff);
+ igt_assert_eq(diff_len, 0);
+ free(diff);
+}
+
+static void test_get_array_diff_empty_a(void)
+{
+ uint32_t array_b[] = {1, 2, 3};
+ uint32_t *diff = NULL;
+ int diff_len;
+
+ diff_len = get_array_diff(NULL, 0, array_b, ARRAY_SIZE(array_b), &diff);
+ igt_assert_eq(diff_len, 0);
+ free(diff);
+}
+
+static void test_get_array_diff_empty_b(void)
+{
+ uint32_t array_a[] = {1, 2, 3};
+ uint32_t *diff = NULL;
+ int diff_len;
+
+ diff_len = get_array_diff(array_a, ARRAY_SIZE(array_a), NULL, 0, &diff);
+ igt_assert_eq(diff_len, 3);
+ igt_assert(diff);
+ igt_assert_eq(diff[0], 1);
+ igt_assert_eq(diff[1], 2);
+ igt_assert_eq(diff[2], 3);
+ free(diff);
+}
+
+static void test_get_array_diff_no_diff(void)
+{
+ uint32_t array_a[] = {1, 2, 3};
+ uint32_t array_b[] = {1, 2, 3};
+ uint32_t *diff = NULL;
+ int diff_len;
+
+ diff_len = get_array_diff(array_a, ARRAY_SIZE(array_a), array_b, ARRAY_SIZE(array_b),
+ &diff);
+ igt_assert_eq(diff_len, 0);
+ free(diff);
+}
+
+static void test_get_array_diff_full_diff(void)
+{
+ uint32_t array_a[] = {1, 2, 3};
+ uint32_t array_b[] = {4, 5, 6};
+ uint32_t *diff = NULL;
+ int diff_len;
+
+ diff_len = get_array_diff(array_a, ARRAY_SIZE(array_a), array_b, ARRAY_SIZE(array_b),
+ &diff);
+ igt_assert_eq(diff_len, 3);
+ igt_assert(diff);
+ igt_assert_eq(diff[0], 1);
+ igt_assert_eq(diff[1], 2);
+ igt_assert_eq(diff[2], 3);
+ free(diff);
+}
+
+static void test_get_array_diff_partial_diff(void)
+{
+ uint32_t array_a[] = {1, 2, 3, 4, 5};
+ uint32_t array_b[] = {2, 3, 6};
+ uint32_t *diff = NULL;
+ int diff_len;
+
+ diff_len = get_array_diff(array_a, ARRAY_SIZE(array_a), array_b, ARRAY_SIZE(array_b),
+ &diff);
+ igt_assert_eq(diff_len, 3);
+ igt_assert(diff);
+ igt_assert_eq(diff[0], 1);
+ igt_assert_eq(diff[1], 4);
+ igt_assert_eq(diff[2], 5);
+ free(diff);
+}
+
+static void test_get_array_diff_null_diff(void)
+{
+ uint32_t array_a[] = {1, 2, 3};
+ uint32_t array_b[] = {2, 3};
+ int diff_len;
+
+ diff_len = get_array_diff(array_a, ARRAY_SIZE(array_a), array_b, ARRAY_SIZE(array_b), NULL);
+ igt_assert_eq(diff_len, 1);
+}
+
+IGT_TEST_DESCRIPTION("Test get_array_diff() function");
+int igt_main()
+{
+ igt_subtest("get_array_diff-empty-both")
+ test_get_array_diff_empty_both();
+
+ igt_subtest("get_array_diff-empty-a")
+ test_get_array_diff_empty_a();
+
+ igt_subtest("get_array_diff-empty-b")
+ test_get_array_diff_empty_b();
+
+ igt_subtest("get_array_diff-no-diff")
+ test_get_array_diff_no_diff();
+
+ igt_subtest("get_array_diff-full-diff")
+ test_get_array_diff_full_diff();
+
+ igt_subtest("get_array_diff-partial-diff")
+ test_get_array_diff_partial_diff();
+
+ igt_subtest("get_array_diff-null-diff")
+ test_get_array_diff_null_diff();
+}
diff --git a/lib/tests/meson.build b/lib/tests/meson.build
index 124a9ecae84d..f1db66d07c28 100644
--- a/lib/tests/meson.build
+++ b/lib/tests/meson.build
@@ -13,6 +13,7 @@ lib_tests = [
'igt_fork_helper',
'igt_hook',
'igt_hook_integration',
+ 'igt_kms_array_diff',
'igt_ktap_parser',
'igt_list_only',
'igt_invalid_subtest_name',
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* [PATCH i-g-t v9 07/49] lib/igt_kms: Add helper to get a pipe from a connector
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (5 preceding siblings ...)
2026-03-16 16:17 ` [PATCH i-g-t v9 06/49] lib/tests: Add tests for array manipulations Louis Chauvet
@ 2026-03-16 16:17 ` Louis Chauvet
2026-03-24 7:58 ` Jani Nikula
2026-03-24 10:02 ` Kamil Konieczny
2026-03-16 16:17 ` [PATCH i-g-t v9 08/49] lib/igt_kms: Expose dump_connector_attrs Louis Chauvet
` (42 subsequent siblings)
49 siblings, 2 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:17 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
Currently there is no way to get a pipe from the connector. Add this tiny
helper that will try to get the currently used pipe for a specific
connector.
Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
lib/igt_kms.c | 41 +++++++++++++++++++++++++++++++++++++++++
lib/igt_kms.h | 3 +++
2 files changed, 44 insertions(+)
diff --git a/lib/igt_kms.c b/lib/igt_kms.c
index b220de419a70..1b34f5eb7ee7 100644
--- a/lib/igt_kms.c
+++ b/lib/igt_kms.c
@@ -8419,3 +8419,44 @@ int kms_wait_for_new_connectors(uint32_t **newly_connected,
return newly_connected_count;
}
+
+/**
+ * igt_get_crtc_index_from_connector_id() - Get a pipe from the connector id
+ * @drm_fd: drm file descriptor to which the connector belongs
+ * @connector_id: connector to inspect
+ *
+ * Returns 0 if the connector is not connected to any pipe
+ */
+int igt_get_crtc_index_from_connector_id(int drm_fd, int connector_id)
+{
+ drmModeObjectPropertiesPtr proplist;
+ drmModePropertyPtr crtc_id_prop;
+ drmModePropertyPtr prop;
+ enum pipe crtc_index;
+ uint32_t crtc_id;
+ int i;
+
+ proplist = drmModeObjectGetProperties(drm_fd, connector_id, DRM_MODE_OBJECT_CONNECTOR);
+ crtc_id_prop = NULL;
+
+ for (i = 0; i < proplist->count_props; i++) {
+ prop = drmModeGetProperty(drm_fd, proplist->props[i]);
+
+ if (strcmp(prop->name, "CRTC_ID") == 0) {
+ crtc_id_prop = prop;
+ break;
+ }
+ drmModeFreeProperty(prop);
+ }
+
+ igt_assert(crtc_id_prop);
+ igt_assert((crtc_id_prop->flags & DRM_MODE_PROP_EXTENDED_TYPE) == DRM_MODE_PROP_OBJECT);
+ crtc_id = proplist->prop_values[i];
+ drmModeFreeProperty(crtc_id_prop);
+ drmModeFreeObjectProperties(proplist);
+ if (crtc_id != 0) {
+ crtc_index = kmstest_get_crtc_index_from_id(drm_fd, crtc_id);
+ return crtc_index;
+ }
+ return 0;
+}
diff --git a/lib/igt_kms.h b/lib/igt_kms.h
index c6aae034b5b9..d346801e7915 100644
--- a/lib/igt_kms.h
+++ b/lib/igt_kms.h
@@ -1314,4 +1314,7 @@ int kms_wait_for_new_connectors(uint32_t **newly_connected,
int
get_array_diff(const uint32_t *array_a, int array_a_len, const uint32_t *array_b, int array_b_len,
uint32_t **diff);
+
+int igt_get_crtc_index_from_connector_id(int drm_fd, int connector_id);
+
#endif /* __IGT_KMS_H__ */
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* Re: [PATCH i-g-t v9 07/49] lib/igt_kms: Add helper to get a pipe from a connector
2026-03-16 16:17 ` [PATCH i-g-t v9 07/49] lib/igt_kms: Add helper to get a pipe from a connector Louis Chauvet
@ 2026-03-24 7:58 ` Jani Nikula
2026-03-24 10:02 ` Kamil Konieczny
1 sibling, 0 replies; 83+ messages in thread
From: Jani Nikula @ 2026-03-24 7:58 UTC (permalink / raw)
To: Louis Chauvet, igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
On Mon, 16 Mar 2026, Louis Chauvet <louis.chauvet@bootlin.com> wrote:
> Currently there is no way to get a pipe from the connector. Add this tiny
> helper that will try to get the currently used pipe for a specific
> connector.
>
> Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> ---
> lib/igt_kms.c | 41 +++++++++++++++++++++++++++++++++++++++++
> lib/igt_kms.h | 3 +++
> 2 files changed, 44 insertions(+)
>
> diff --git a/lib/igt_kms.c b/lib/igt_kms.c
> index b220de419a70..1b34f5eb7ee7 100644
> --- a/lib/igt_kms.c
> +++ b/lib/igt_kms.c
> @@ -8419,3 +8419,44 @@ int kms_wait_for_new_connectors(uint32_t **newly_connected,
>
> return newly_connected_count;
> }
> +
> +/**
> + * igt_get_crtc_index_from_connector_id() - Get a pipe from the connector id
> + * @drm_fd: drm file descriptor to which the connector belongs
> + * @connector_id: connector to inspect
> + *
> + * Returns 0 if the connector is not connected to any pipe
> + */
> +int igt_get_crtc_index_from_connector_id(int drm_fd, int connector_id)
> +{
> + drmModeObjectPropertiesPtr proplist;
> + drmModePropertyPtr crtc_id_prop;
> + drmModePropertyPtr prop;
> + enum pipe crtc_index;
Pipes are pipes, CRTC indexes are indexes, and should not be conflated.
The CRTC index should just be an int.
BR,
Jani.
> + uint32_t crtc_id;
> + int i;
> +
> + proplist = drmModeObjectGetProperties(drm_fd, connector_id, DRM_MODE_OBJECT_CONNECTOR);
> + crtc_id_prop = NULL;
> +
> + for (i = 0; i < proplist->count_props; i++) {
> + prop = drmModeGetProperty(drm_fd, proplist->props[i]);
> +
> + if (strcmp(prop->name, "CRTC_ID") == 0) {
> + crtc_id_prop = prop;
> + break;
> + }
> + drmModeFreeProperty(prop);
> + }
> +
> + igt_assert(crtc_id_prop);
> + igt_assert((crtc_id_prop->flags & DRM_MODE_PROP_EXTENDED_TYPE) == DRM_MODE_PROP_OBJECT);
> + crtc_id = proplist->prop_values[i];
> + drmModeFreeProperty(crtc_id_prop);
> + drmModeFreeObjectProperties(proplist);
> + if (crtc_id != 0) {
> + crtc_index = kmstest_get_crtc_index_from_id(drm_fd, crtc_id);
> + return crtc_index;
> + }
> + return 0;
> +}
> diff --git a/lib/igt_kms.h b/lib/igt_kms.h
> index c6aae034b5b9..d346801e7915 100644
> --- a/lib/igt_kms.h
> +++ b/lib/igt_kms.h
> @@ -1314,4 +1314,7 @@ int kms_wait_for_new_connectors(uint32_t **newly_connected,
> int
> get_array_diff(const uint32_t *array_a, int array_a_len, const uint32_t *array_b, int array_b_len,
> uint32_t **diff);
> +
> +int igt_get_crtc_index_from_connector_id(int drm_fd, int connector_id);
> +
> #endif /* __IGT_KMS_H__ */
--
Jani Nikula, Intel
^ permalink raw reply [flat|nested] 83+ messages in thread* Re: [PATCH i-g-t v9 07/49] lib/igt_kms: Add helper to get a pipe from a connector
2026-03-16 16:17 ` [PATCH i-g-t v9 07/49] lib/igt_kms: Add helper to get a pipe from a connector Louis Chauvet
2026-03-24 7:58 ` Jani Nikula
@ 2026-03-24 10:02 ` Kamil Konieczny
1 sibling, 0 replies; 83+ messages in thread
From: Kamil Konieczny @ 2026-03-24 10:02 UTC (permalink / raw)
To: Louis Chauvet
Cc: Juha-Pekka Heikkila, Karthik B S, Swati Sharma, igt-dev,
thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy
Hi Louis,
On 2026-03-16 at 17:17:28 +0100, Louis Chauvet wrote:
> Currently there is no way to get a pipe from the connector. Add this tiny
> helper that will try to get the currently used pipe for a specific
> connector.
>
> Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> ---
> lib/igt_kms.c | 41 +++++++++++++++++++++++++++++++++++++++++
> lib/igt_kms.h | 3 +++
> 2 files changed, 44 insertions(+)
>
> diff --git a/lib/igt_kms.c b/lib/igt_kms.c
> index b220de419a70..1b34f5eb7ee7 100644
> --- a/lib/igt_kms.c
> +++ b/lib/igt_kms.c
> @@ -8419,3 +8419,44 @@ int kms_wait_for_new_connectors(uint32_t **newly_connected,
>
> return newly_connected_count;
> }
> +
> +/**
> + * igt_get_crtc_index_from_connector_id() - Get a pipe from the connector id
> + * @drm_fd: drm file descriptor to which the connector belongs
> + * @connector_id: connector to inspect
> + *
> + * Returns 0 if the connector is not connected to any pipe
Please also write that this function will assert on errors.
Can this be written without those asserts? +cc Swati, Karthik and J-P
Regards,
Kamil
> + */
> +int igt_get_crtc_index_from_connector_id(int drm_fd, int connector_id)
> +{
> + drmModeObjectPropertiesPtr proplist;
> + drmModePropertyPtr crtc_id_prop;
> + drmModePropertyPtr prop;
> + enum pipe crtc_index;
> + uint32_t crtc_id;
> + int i;
> +
> + proplist = drmModeObjectGetProperties(drm_fd, connector_id, DRM_MODE_OBJECT_CONNECTOR);
> + crtc_id_prop = NULL;
> +
> + for (i = 0; i < proplist->count_props; i++) {
> + prop = drmModeGetProperty(drm_fd, proplist->props[i]);
> +
> + if (strcmp(prop->name, "CRTC_ID") == 0) {
> + crtc_id_prop = prop;
> + break;
> + }
> + drmModeFreeProperty(prop);
> + }
> +
> + igt_assert(crtc_id_prop);
> + igt_assert((crtc_id_prop->flags & DRM_MODE_PROP_EXTENDED_TYPE) == DRM_MODE_PROP_OBJECT);
> + crtc_id = proplist->prop_values[i];
> + drmModeFreeProperty(crtc_id_prop);
> + drmModeFreeObjectProperties(proplist);
> + if (crtc_id != 0) {
> + crtc_index = kmstest_get_crtc_index_from_id(drm_fd, crtc_id);
> + return crtc_index;
> + }
> + return 0;
> +}
> diff --git a/lib/igt_kms.h b/lib/igt_kms.h
> index c6aae034b5b9..d346801e7915 100644
> --- a/lib/igt_kms.h
> +++ b/lib/igt_kms.h
> @@ -1314,4 +1314,7 @@ int kms_wait_for_new_connectors(uint32_t **newly_connected,
> int
> get_array_diff(const uint32_t *array_a, int array_a_len, const uint32_t *array_b, int array_b_len,
> uint32_t **diff);
> +
> +int igt_get_crtc_index_from_connector_id(int drm_fd, int connector_id);
> +
> #endif /* __IGT_KMS_H__ */
>
> --
> 2.52.0
>
^ permalink raw reply [flat|nested] 83+ messages in thread
* [PATCH i-g-t v9 08/49] lib/igt_kms: Expose dump_connector_attrs
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (6 preceding siblings ...)
2026-03-16 16:17 ` [PATCH i-g-t v9 07/49] lib/igt_kms: Add helper to get a pipe from a connector Louis Chauvet
@ 2026-03-16 16:17 ` Louis Chauvet
2026-03-16 16:17 ` [PATCH i-g-t v9 09/49] lib/igt_kms: Expose reset_connectors_at_exit Louis Chauvet
` (41 subsequent siblings)
49 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:17 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
dump_connector_attrs is used by i915 DP helpers, so expose it from igt_kms
to be able to reuse them.
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
---
lib/igt_kms.c | 5 ++++-
lib/igt_kms.h | 1 +
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/lib/igt_kms.c b/lib/igt_kms.c
index 1b34f5eb7ee7..a0d12800404b 100644
--- a/lib/igt_kms.c
+++ b/lib/igt_kms.c
@@ -1766,7 +1766,10 @@ static bool connector_attr_set_debugfs(int drm_fd,
return true;
}
-static void dump_connector_attrs(void)
+/**
+ * dump_connector_attrs: Display on debug log all the connector attributes
+ */
+void dump_connector_attrs(void)
{
char name[80];
diff --git a/lib/igt_kms.h b/lib/igt_kms.h
index d346801e7915..d071ca1800f8 100644
--- a/lib/igt_kms.h
+++ b/lib/igt_kms.h
@@ -258,6 +258,7 @@ enum joined_pipes {
uint64_t igt_kms_frame_time_from_vrefresh(uint32_t vrefresh);
+void dump_connector_attrs(void);
bool kmstest_force_connector(int fd, drmModeConnector *connector,
enum kmstest_force_connector_state state);
bool kmstest_force_connector_joiner(int drm_fd, drmModeConnector *connector, int joined_pipes);
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* [PATCH i-g-t v9 09/49] lib/igt_kms: Expose reset_connectors_at_exit
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (7 preceding siblings ...)
2026-03-16 16:17 ` [PATCH i-g-t v9 08/49] lib/igt_kms: Expose dump_connector_attrs Louis Chauvet
@ 2026-03-16 16:17 ` Louis Chauvet
2026-03-16 16:17 ` [PATCH i-g-t v9 10/49] lib/igt_kms: Expose connector_attr_set and igt_connector_attr_set Louis Chauvet
` (40 subsequent siblings)
49 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:17 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
reset_connectors_at_exit is used by i915 DP helpers, expose it so it can
be reused in a different file.
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
---
lib/igt_kms.c | 7 ++++++-
lib/igt_kms.h | 1 +
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/lib/igt_kms.c b/lib/igt_kms.c
index a0d12800404b..451b273999f0 100644
--- a/lib/igt_kms.c
+++ b/lib/igt_kms.c
@@ -1577,7 +1577,12 @@ void kmstest_set_vt_text_mode(void)
igt_assert(set_vt_mode(KD_TEXT) >= 0);
}
-static void reset_connectors_at_exit(int sig)
+/**
+ * reset_connectors_at_exit:
+ *
+ * Exit handler used to remove any forced state from the connectors.
+ */
+void reset_connectors_at_exit(int sig)
{
igt_reset_connectors();
}
diff --git a/lib/igt_kms.h b/lib/igt_kms.h
index d071ca1800f8..5a9efc8cee48 100644
--- a/lib/igt_kms.h
+++ b/lib/igt_kms.h
@@ -152,6 +152,7 @@ int kmstest_get_crtc_index_from_id(int fd, int crtc_id);
void kmstest_set_vt_graphics_mode(void);
void kmstest_restore_vt_mode(void);
void kmstest_set_vt_text_mode(void);
+void reset_connectors_at_exit(int sig);
enum igt_atomic_crtc_properties {
IGT_CRTC_CTM = 0,
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* [PATCH i-g-t v9 10/49] lib/igt_kms: Expose connector_attr_set and igt_connector_attr_set
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (8 preceding siblings ...)
2026-03-16 16:17 ` [PATCH i-g-t v9 09/49] lib/igt_kms: Expose reset_connectors_at_exit Louis Chauvet
@ 2026-03-16 16:17 ` Louis Chauvet
2026-03-16 16:17 ` [PATCH i-g-t v9 11/49] lib/igt_debugfs: Move debugfs helpers to the proper location Louis Chauvet
` (39 subsequent siblings)
49 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:17 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
Some debugfs helpers need to use connector attributes. Expose those helpers
to allow using them in igt_debugfs.
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
---
lib/igt_kms.c | 30 +++++++++++++++++++++++-------
lib/igt_kms.h | 6 ++++++
2 files changed, 29 insertions(+), 7 deletions(-)
diff --git a/lib/igt_kms.c b/lib/igt_kms.c
index 451b273999f0..ce9f70f31f64 100644
--- a/lib/igt_kms.c
+++ b/lib/igt_kms.c
@@ -100,8 +100,6 @@
*/
#define IGT_KMS_CONNECTOR_NAME_SIZE 50
-typedef bool (*igt_connector_attr_set)(int dir, const char *attr, const char *value);
-
struct igt_connector_attr {
uint32_t connector_type;
uint32_t connector_type_id;
@@ -1682,11 +1680,29 @@ static void connector_attr_free(struct igt_connector_attr *c)
memset(c, 0, sizeof(*c));
}
-static bool connector_attr_set(int idx, drmModeConnector *connector,
- int dir, igt_connector_attr_set set,
- const char *attr, const char *value,
- const char *reset_value,
- bool force_reset)
+/**
+ * connector_attr_set:
+ * @idx: card index
+ * @connector: libdrm connector
+ * @dir: directory file descriptor
+ * @set: function to set the attribute
+ * @attr: attribute name
+ * @value: attribute value
+ * @reset_value: value to reset the attribute to
+ * @force_reset: whether to force reset the attribute
+ *
+ * Configure a new attribute for @connector on card @idx that can be set
+ * using @set function.
+ * connector_attr_set register this attribute to reset it to the @reset_value
+ * during igt_reset_connector.
+ *
+ * Returns: True on success, false on failure.
+ */
+bool connector_attr_set(int idx, drmModeConnector *connector,
+ int dir, igt_connector_attr_set set,
+ const char *attr, const char *value,
+ const char *reset_value,
+ bool force_reset)
{
struct igt_connector_attr *c;
diff --git a/lib/igt_kms.h b/lib/igt_kms.h
index 5a9efc8cee48..6298738b452f 100644
--- a/lib/igt_kms.h
+++ b/lib/igt_kms.h
@@ -1231,6 +1231,12 @@ void igt_parse_connector_tile_blob(drmModePropertyBlobPtr blob,
int igt_connector_sysfs_open(int drm_fd,
drmModeConnector *connector);
+typedef bool (*igt_connector_attr_set)(int dir, const char *attr, const char *value);
+bool connector_attr_set(int idx, drmModeConnector *connector,
+ int dir, igt_connector_attr_set set,
+ const char *attr, const char *value,
+ const char *reset_value,
+ bool force_reset);
uint32_t igt_reduce_format(uint32_t format);
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* [PATCH i-g-t v9 11/49] lib/igt_debugfs: Move debugfs helpers to the proper location
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (9 preceding siblings ...)
2026-03-16 16:17 ` [PATCH i-g-t v9 10/49] lib/igt_kms: Expose connector_attr_set and igt_connector_attr_set Louis Chauvet
@ 2026-03-16 16:17 ` Louis Chauvet
2026-03-16 16:17 ` [PATCH i-g-t v9 12/49] lib/igt_debugfs: Add const when make sense Louis Chauvet
` (38 subsequent siblings)
49 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:17 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
igt_debugfs_read_connector_file and igt_debugfs_write_connector_file are
debugfs related helpers. To avoid mixing everything in igt_kms, move them
to igt_debugfs.
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_debugfs.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
lib/igt_debugfs.h | 13 ++++++++
lib/igt_kms.c | 93 ------------------------------------------------------
3 files changed, 107 insertions(+), 93 deletions(-)
diff --git a/lib/igt_debugfs.c b/lib/igt_debugfs.c
index 11f5ee9984ee..6072d7d68025 100644
--- a/lib/igt_debugfs.c
+++ b/lib/igt_debugfs.c
@@ -43,6 +43,7 @@
#include "igt_aux.h"
#include "igt_kms.h"
#include "igt_debugfs.h"
+#include "igt_device.h"
#include "igt_sysfs.h"
/**
@@ -776,3 +777,96 @@ void __igt_debugfs_dump(int device, const char *filename, int level)
igt_log(IGT_LOG_DOMAIN, level, "%s:\n%s\n", filename, contents);
free(contents);
}
+
+/**
+ * igt_debugfs_read_connector_file:
+ * @drm_fd: A drm file descriptor
+ * @conn_name: Name of the output connector
+ * @filename: The file to read from in the connector's directory
+ * @buf: Buffer to store the read content
+ * @buf_size: Size of the buffer
+ *
+ * Reads from a specific file in the connector's debugfs directory.
+ *
+ * Returns: 0 on success, -1 on failure.
+ */
+int igt_debugfs_read_connector_file(int drm_fd, char *conn_name,
+ const char *filename, char *buf,
+ size_t buf_size)
+{
+ int dir, res;
+
+ dir = igt_debugfs_connector_dir(drm_fd, conn_name, O_RDONLY);
+ igt_assert_f(dir >= 0, "Failed to open debugfs dir for connector %s\n", conn_name);
+
+ res = igt_debugfs_simple_read(dir, filename, buf, buf_size);
+ close(dir);
+
+ if (res < 0)
+ return -1;
+
+ return 0;
+}
+
+/**
+ * igt_debugfs_write_connector_file:
+ * @drm_fd: A drm file descriptor
+ * @conn_name: Name of the output connector
+ * @filename: The file to write to in the connector's directory
+ * @data: Data to write to the file
+ * @data_size: Size of the data to write
+ *
+ * Writes to a specific file in the connector's debugfs directory.
+ *
+ * Returns: 0 on success, -1 on failure.
+ */
+int igt_debugfs_write_connector_file(int drm_fd, char *conn_name,
+ const char *filename, const char *data,
+ size_t data_size)
+{
+ int dir, res;
+
+ dir = igt_debugfs_connector_dir(drm_fd, conn_name, O_RDONLY);
+ igt_assert_f(dir >= 0, "Failed to open debugfs dir for connector %s\n",
+ conn_name);
+
+ res = igt_sysfs_write(dir, filename, data, data_size);
+ close(dir);
+
+ if (res < 0)
+ return -1;
+
+ return 0;
+}
+
+bool connector_attr_set_debugfs(int drm_fd,
+ drmModeConnector *connector,
+ const char *attr, const char *value,
+ const char *reset_value,
+ bool force_reset)
+{
+ char name[80];
+ int idx, dir;
+
+ idx = igt_device_get_card_index(drm_fd);
+ if (idx < 0 || idx > 63)
+ return false;
+
+ snprintf(name, sizeof(name), "%s-%d",
+ kmstest_connector_type_str(connector->connector_type),
+ connector->connector_type_id);
+
+ dir = igt_debugfs_connector_dir(drm_fd, name, O_DIRECTORY);
+ if (dir < 0)
+ return false;
+
+ if (!connector_attr_set(idx, connector, dir,
+ igt_sysfs_set, attr,
+ value, reset_value,
+ force_reset))
+ return false;
+
+ igt_info("Connector %s/%s is now %s\n", name, attr, value);
+
+ return true;
+}
diff --git a/lib/igt_debugfs.h b/lib/igt_debugfs.h
index 2c546f299a43..49fbadd2a74c 100644
--- a/lib/igt_debugfs.h
+++ b/lib/igt_debugfs.h
@@ -28,6 +28,7 @@
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
+#include <xf86drmMode.h>
const char *igt_debugfs_mount(void);
char *igt_debugfs_path(int device, char *path, int pathlen);
@@ -176,4 +177,16 @@ int igt_get_stable_obj_count(int driver);
void __igt_debugfs_dump(int device, const char *filename, int level);
#define igt_debugfs_dump(d, f) __igt_debugfs_dump(d, f, IGT_LOG_DEBUG)
+int igt_debugfs_read_connector_file(int drm_fd, char *conn_name,
+ const char *filename, char *buf,
+ size_t buf_size);
+int igt_debugfs_write_connector_file(int drm_fd, char *conn_name,
+ const char *filename, const char *data,
+ size_t data_size);
+bool connector_attr_set_debugfs(int drm_fd,
+ drmModeConnector *connector,
+ const char *attr, const char *value,
+ const char *reset_value,
+ bool force_reset);
+
#endif /* __IGT_DEBUGFS_H__ */
diff --git a/lib/igt_kms.c b/lib/igt_kms.c
index ce9f70f31f64..97292c5c3dfa 100644
--- a/lib/igt_kms.c
+++ b/lib/igt_kms.c
@@ -1755,38 +1755,6 @@ static bool connector_attr_set_sysfs(int drm_fd,
return true;
}
-static bool connector_attr_set_debugfs(int drm_fd,
- drmModeConnector *connector,
- const char *attr, const char *value,
- const char *reset_value,
- bool force_reset)
-{
- char name[80];
- int idx, dir;
-
- idx = igt_device_get_card_index(drm_fd);
- if (idx < 0 || idx > 63)
- return false;
-
- snprintf(name, sizeof(name), "%s-%d",
- kmstest_connector_type_str(connector->connector_type),
- connector->connector_type_id);
-
- dir = igt_debugfs_connector_dir(drm_fd, name, O_DIRECTORY);
- if (dir < 0)
- return false;
-
- if (!connector_attr_set(idx, connector, dir,
- igt_sysfs_set, attr,
- value, reset_value,
- force_reset))
- return false;
-
- igt_info("Connector %s/%s is now %s\n", name, attr, value);
-
- return true;
-}
-
/**
* dump_connector_attrs: Display on debug log all the connector attributes
*/
@@ -7516,67 +7484,6 @@ static int igt_parse_marked_value(const char *buf, char marked_char, int *result
return -1;
}
-/**
- * igt_debugfs_read_connector_file:
- * @drm_fd: A drm file descriptor
- * @conn_name: Name of the output connector
- * @filename: The file to read from in the connector's directory
- * @buf: Buffer to store the read content
- * @buf_size: Size of the buffer
- *
- * Reads from a specific file in the connector's debugfs directory.
- *
- * Returns: 0 on success, -1 on failure.
- */
-static int igt_debugfs_read_connector_file(int drm_fd, char *conn_name,
- const char *filename, char *buf,
- size_t buf_size)
-{
- int dir, res;
-
- dir = igt_debugfs_connector_dir(drm_fd, conn_name, O_RDONLY);
- igt_assert_f(dir >= 0, "Failed to open debugfs dir for connector %s\n", conn_name);
-
- res = igt_debugfs_simple_read(dir, filename, buf, buf_size);
- close(dir);
-
- if (res < 0)
- return -1;
-
- return 0;
-}
-
-/**
- * igt_debugfs_write_connector_file:
- * @drm_fd: A drm file descriptor
- * @conn_name: Name of the output connector
- * @filename: The file to write to in the connector's directory
- * @data: Data to write to the file
- * @data_size: Size of the data to write
- *
- * Writes to a specific file in the connector's debugfs directory.
- *
- * Returns: 0 on success, -1 on failure.
- */
-static int igt_debugfs_write_connector_file(int drm_fd, char *conn_name,
- const char *filename, const char *data,
- size_t data_size)
-{
- int dir, res;
-
- dir = igt_debugfs_connector_dir(drm_fd, conn_name, O_RDONLY);
- igt_assert_f(dir >= 0, "Failed to open debugfs dir for connector %s\n",
- conn_name);
-
- res = igt_sysfs_write(dir, filename, data, data_size);
- close(dir);
-
- if (res < 0)
- return -1;
-
- return 0;
-}
-
/**
* igt_get_current_link_rate:
* @drm_fd: A drm file descriptor
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* [PATCH i-g-t v9 12/49] lib/igt_debugfs: Add const when make sense
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (10 preceding siblings ...)
2026-03-16 16:17 ` [PATCH i-g-t v9 11/49] lib/igt_debugfs: Move debugfs helpers to the proper location Louis Chauvet
@ 2026-03-16 16:17 ` Louis Chauvet
2026-03-16 16:17 ` [PATCH i-g-t v9 13/49] lib/igt_amd: " Louis Chauvet
` (37 subsequent siblings)
49 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:17 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
Const qualifier avoid unwanted modifications. Add const at place where
it make sense.
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/igt_debugfs.c | 6 +++---
lib/igt_debugfs.h | 6 +++---
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/lib/igt_debugfs.c b/lib/igt_debugfs.c
index 6072d7d68025..b9f1e425c822 100644
--- a/lib/igt_debugfs.c
+++ b/lib/igt_debugfs.c
@@ -270,7 +270,7 @@ int igt_debugfs_tile_dir(int device, uint8_t tile)
* Returns:
* The directory fd, or -1 on failure.
*/
-int igt_debugfs_connector_dir(int device, char *conn_name, int mode)
+int igt_debugfs_connector_dir(int device, const char *conn_name, int mode)
{
int dir, ret;
@@ -790,7 +790,7 @@ void __igt_debugfs_dump(int device, const char *filename, int level)
*
* Returns: 0 on success, -1 on failure.
*/
-int igt_debugfs_read_connector_file(int drm_fd, char *conn_name,
+int igt_debugfs_read_connector_file(int drm_fd, const char *conn_name,
const char *filename, char *buf,
size_t buf_size)
{
@@ -820,7 +820,7 @@ int igt_debugfs_read_connector_file(int drm_fd, char *conn_name,
*
* Returns: 0 on success, -1 on failure.
*/
-int igt_debugfs_write_connector_file(int drm_fd, char *conn_name,
+int igt_debugfs_write_connector_file(int drm_fd, const char *conn_name,
const char *filename, const char *data,
size_t data_size)
{
diff --git a/lib/igt_debugfs.h b/lib/igt_debugfs.h
index 49fbadd2a74c..08f9152fec95 100644
--- a/lib/igt_debugfs.h
+++ b/lib/igt_debugfs.h
@@ -34,7 +34,7 @@ const char *igt_debugfs_mount(void);
char *igt_debugfs_path(int device, char *path, int pathlen);
int igt_debugfs_dir(int device);
-int igt_debugfs_connector_dir(int device, char *conn_name, int mode);
+int igt_debugfs_connector_dir(int device, const char *conn_name, int mode);
int igt_debugfs_crtc_dir(int device, int crtc_index, int mode);
int igt_debugfs_open(int fd, const char *filename, int mode);
@@ -177,10 +177,10 @@ int igt_get_stable_obj_count(int driver);
void __igt_debugfs_dump(int device, const char *filename, int level);
#define igt_debugfs_dump(d, f) __igt_debugfs_dump(d, f, IGT_LOG_DEBUG)
-int igt_debugfs_read_connector_file(int drm_fd, char *conn_name,
+int igt_debugfs_read_connector_file(int drm_fd, const char *conn_name,
const char *filename, char *buf,
size_t buf_size);
-int igt_debugfs_write_connector_file(int drm_fd, char *conn_name,
+int igt_debugfs_write_connector_file(int drm_fd, const char *conn_name,
const char *filename, const char *data,
size_t data_size);
bool connector_attr_set_debugfs(int drm_fd,
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* [PATCH i-g-t v9 13/49] lib/igt_amd: Add const when make sense
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (11 preceding siblings ...)
2026-03-16 16:17 ` [PATCH i-g-t v9 12/49] lib/igt_debugfs: Add const when make sense Louis Chauvet
@ 2026-03-16 16:17 ` Louis Chauvet
2026-03-16 16:17 ` [PATCH i-g-t v9 14/49] lib/igt_kms: " Louis Chauvet
` (36 subsequent siblings)
49 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:17 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
Const qualifier helps to avoid unwanted modification of a pointer.
To help avoiding those errors, modify the function signature to
use const.
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/igt_amd.c | 2 +-
lib/igt_amd.h | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/lib/igt_amd.c b/lib/igt_amd.c
index 3ddb5f40374e..081bad411355 100644
--- a/lib/igt_amd.c
+++ b/lib/igt_amd.c
@@ -831,7 +831,7 @@ int igt_amd_trigger_hotplug(int drm_fd, char *connector_name)
* values of "Current", "Verified", "Reported", and "Preferred", respectively.
*/
void igt_amd_read_link_settings(
- int drm_fd, char *connector_name, int *lane_count, int *link_rate, int *link_spread)
+ int drm_fd, const char *connector_name, int *lane_count, int *link_rate, int *link_spread)
{
int fd, ret;
char buf[101];
diff --git a/lib/igt_amd.h b/lib/igt_amd.h
index bce4657cbcfd..217ee6dcbd02 100644
--- a/lib/igt_amd.h
+++ b/lib/igt_amd.h
@@ -210,7 +210,7 @@ int igt_amd_trigger_hotplug(int drm_fd, char *connector_name);
/* IGT link helper functions */
void igt_amd_read_link_settings(
- int drm_fd, char *connector_name, int *lane_count, int *link_rate, int *link_spread);
+ int drm_fd, const char *connector_name, int *lane_count, int *link_rate, int *link_spread);
void igt_amd_write_link_settings(
int drm_fd, char *connector_name, enum dc_lane_count lane_count,
enum dc_link_rate link_rate, enum dc_link_training_type training_type);
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* [PATCH i-g-t v9 14/49] lib/igt_kms: Add const when make sense
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (12 preceding siblings ...)
2026-03-16 16:17 ` [PATCH i-g-t v9 13/49] lib/igt_amd: " Louis Chauvet
@ 2026-03-16 16:17 ` Louis Chauvet
2026-03-16 16:17 ` [PATCH i-g-t v9 15/49] lib/monitor_edids: Add helper functions for using monitor_edid objects Louis Chauvet
` (35 subsequent siblings)
49 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:17 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
Const qualifier helps to avoid unwanted modification of a pointer.
To help avoiding those errors, modify the function signature to
use const.
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/igt_kms.c | 2 +-
lib/igt_kms.h | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/lib/igt_kms.c b/lib/igt_kms.c
index 97292c5c3dfa..f6ddf25756f1 100644
--- a/lib/igt_kms.c
+++ b/lib/igt_kms.c
@@ -5125,7 +5125,7 @@ int igt_display_drop_events(igt_display_t *display)
*
* Returns: String representing a connector's name, e.g. "DP-1".
*/
-const char *igt_output_name(igt_output_t *output)
+const char *igt_output_name(const igt_output_t *output)
{
return output->name;
}
diff --git a/lib/igt_kms.h b/lib/igt_kms.h
index 6298738b452f..aa1599592e68 100644
--- a/lib/igt_kms.h
+++ b/lib/igt_kms.h
@@ -598,7 +598,7 @@ igt_pipe_crc_t *igt_crtc_crc_new(igt_crtc_t *crtc, const char *source);
igt_pipe_crc_t *igt_crtc_crc_new_nonblock(igt_crtc_t *crtc, const char *source);
igt_crtc_t *igt_output_get_driving_crtc(igt_output_t *output);
-const char *igt_output_name(igt_output_t *output);
+const char *igt_output_name(const igt_output_t *output);
bool kmstest_mode_is_valid(const drmModeModeInfo *mode);
drmModeModeInfo *igt_output_get_mode(igt_output_t *output);
drmModeModeInfo *igt_output_get_highres_mode(igt_output_t *output);
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* [PATCH i-g-t v9 15/49] lib/monitor_edids: Add helper functions for using monitor_edid objects
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (13 preceding siblings ...)
2026-03-16 16:17 ` [PATCH i-g-t v9 14/49] lib/igt_kms: " Louis Chauvet
@ 2026-03-16 16:17 ` Louis Chauvet
2026-03-17 15:09 ` Kory Maincent
2026-03-24 10:15 ` Kamil Konieczny
2026-03-16 16:17 ` [PATCH i-g-t v9 16/49] lib/monitor_edids: Add helper to get an EDID by its name Louis Chauvet
` (34 subsequent siblings)
49 siblings, 2 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:17 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
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>
---
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 144907558be1..07793381257e 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 f6cfe82ff6e1..3984241775c3 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 1cbf1c22f0bb..6043e7a084b3 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 05679f0897f3..e5069868683d 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.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* Re: [PATCH i-g-t v9 15/49] lib/monitor_edids: Add helper functions for using monitor_edid objects
2026-03-16 16:17 ` [PATCH i-g-t v9 15/49] lib/monitor_edids: Add helper functions for using monitor_edid objects Louis Chauvet
@ 2026-03-17 15:09 ` Kory Maincent
2026-03-24 10:15 ` Kamil Konieczny
1 sibling, 0 replies; 83+ messages in thread
From: Kory Maincent @ 2026-03-17 15:09 UTC (permalink / raw)
To: Louis Chauvet
Cc: igt-dev, thomas.petazzoni, luca.ceresoli, markyacoub,
khaled.almahallawy
On Mon, 16 Mar 2026 17:17:36 +0100
Louis Chauvet <louis.chauvet@bootlin.com> wrote:
> 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>
...
> +
> +/**
> + * 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)
Return a const struct monitor_edid to prevent any modification as it is a
static list.
With this fixed you can add my R-by.
Regards,
--
Köry Maincent, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 83+ messages in thread
* Re: [PATCH i-g-t v9 15/49] lib/monitor_edids: Add helper functions for using monitor_edid objects
2026-03-16 16:17 ` [PATCH i-g-t v9 15/49] lib/monitor_edids: Add helper functions for using monitor_edid objects Louis Chauvet
2026-03-17 15:09 ` Kory Maincent
@ 2026-03-24 10:15 ` Kamil Konieczny
2026-03-24 14:09 ` Louis Chauvet
1 sibling, 1 reply; 83+ messages in thread
From: Kamil Konieczny @ 2026-03-24 10:15 UTC (permalink / raw)
To: Louis Chauvet
Cc: igt-dev, thomas.petazzoni, luca.ceresoli, kory.maincent,
markyacoub, khaled.almahallawy
Hi Louis,
On 2026-03-16 at 17:17:36 +0100, Louis Chauvet wrote:
> 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>
> ---
> 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 144907558be1..07793381257e 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 f6cfe82ff6e1..3984241775c3 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 1cbf1c22f0bb..6043e7a084b3 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"
Why do you need drmtest.h header?
> +#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 05679f0897f3..e5069868683d 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>
Place them before stdint.h
Regards,
Kamil
>
> #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.52.0
>
^ permalink raw reply [flat|nested] 83+ messages in thread* Re: [PATCH i-g-t v9 15/49] lib/monitor_edids: Add helper functions for using monitor_edid objects
2026-03-24 10:15 ` Kamil Konieczny
@ 2026-03-24 14:09 ` Louis Chauvet
0 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-24 14:09 UTC (permalink / raw)
To: Kamil Konieczny, igt-dev, thomas.petazzoni, luca.ceresoli,
kory.maincent, markyacoub, khaled.almahallawy
On 3/24/26 11:15, Kamil Konieczny wrote:
> Hi Louis,
> On 2026-03-16 at 17:17:36 +0100, Louis Chauvet wrote:
>> 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>
>> ---
>> 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 144907558be1..07793381257e 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 f6cfe82ff6e1..3984241775c3 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 1cbf1c22f0bb..6043e7a084b3 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"
>
> Why do you need drmtest.h header?
For ARRAY_SIZE, is there a better definition ARRAY_SIZE somewhere else?
>> +#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 05679f0897f3..e5069868683d 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>
>
> Place them before stdint.h
>
> Regards,
> Kamil
>
>>
>> #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.52.0
>>
^ permalink raw reply [flat|nested] 83+ messages in thread
* [PATCH i-g-t v9 16/49] lib/monitor_edids: Add helper to get an EDID by its name
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (14 preceding siblings ...)
2026-03-16 16:17 ` [PATCH i-g-t v9 15/49] lib/monitor_edids: Add helper functions for using monitor_edid objects Louis Chauvet
@ 2026-03-16 16:17 ` Louis Chauvet
2026-03-16 16:17 ` [PATCH i-g-t v9 17/49] lib/monitor_edids: Add helper to print all available EDID names Louis Chauvet
` (33 subsequent siblings)
49 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:17 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
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 6043e7a084b3..34446066346e 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 (strncmp(ALL_EDIDS[i].edid_list[j].name, name, EDID_NAME_MAX_LEN) == 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 e5069868683d..6d9e3fc89592 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.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* [PATCH i-g-t v9 17/49] lib/monitor_edids: Add helper to print all available EDID names
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (15 preceding siblings ...)
2026-03-16 16:17 ` [PATCH i-g-t v9 16/49] lib/monitor_edids: Add helper to get an EDID by its name Louis Chauvet
@ 2026-03-16 16:17 ` Louis Chauvet
2026-03-16 16:17 ` [PATCH i-g-t v9 18/49] lib/unigraf: Add used defines for TSI_Types Louis Chauvet
` (32 subsequent siblings)
49 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:17 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
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 | 16 ++++++++++++++++
lib/monitor_edids/monitor_edids_helper.h | 2 ++
2 files changed, 18 insertions(+)
diff --git a/lib/monitor_edids/monitor_edids_helper.c b/lib/monitor_edids/monitor_edids_helper.c
index 34446066346e..091549ac7a49 100644
--- a/lib/monitor_edids/monitor_edids_helper.c
+++ b/lib/monitor_edids/monitor_edids_helper.c
@@ -200,3 +200,19 @@ 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 6d9e3fc89592..7ae0fd40c041 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.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* [PATCH i-g-t v9 18/49] lib/unigraf: Add used defines for TSI_Types
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (16 preceding siblings ...)
2026-03-16 16:17 ` [PATCH i-g-t v9 17/49] lib/monitor_edids: Add helper to print all available EDID names Louis Chauvet
@ 2026-03-16 16:17 ` Louis Chauvet
2026-03-16 16:17 ` [PATCH i-g-t v9 19/49] lib/unigraf: Add TSI.h Louis Chauvet
` (31 subsequent siblings)
49 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:17 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
Current unigraf public release are not c-compatible, this file hardcode
some values. One future release of libTSI may include a c-compatible
TSI_types.h file with full structure definition, but until then.
Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
lib/unigraf/TSI_types.h | 126 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 126 insertions(+)
diff --git a/lib/unigraf/TSI_types.h b/lib/unigraf/TSI_types.h
new file mode 100644
index 000000000000..c387ab4e1941
--- /dev/null
+++ b/lib/unigraf/TSI_types.h
@@ -0,0 +1,126 @@
+/* SPDX-License-Identifier: MIT */
+
+// Current unigraf public release are not c-compatible, this file hardcode some values.
+// The next release of libTSI should include a c-compatible TSI_types.h file, that will
+// be directly used in place of this file.
+
+#ifndef TSI_REG_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#define TSI_VERSION_TEXT 0x80000001
+#define TSI_DEVCAP_VIDEO_CAPTURE 0x00000001
+#define TSI_SEARCHOPTIONS_SHOW_DEVICES_IN_USE 0x00000001
+
+#define TSI_EDID_TE_INPUT 0x1100
+#define TSI_EDID_SELECT_STREAM 0x1102
+
+#define TSI_BASE_LEGACY_GENERIC(offset) (0x210 + (offset))
+#define TSI_FORCE_HOT_PLUG_STATE_W TSI_BASE_LEGACY_GENERIC(0x2)
+
+#define TSI_BASE_LEGACY_DPRX_MSA(offset) (0x260 + (offset))
+#define TSI_DPRX_MSA_COMMAND_W TSI_BASE_LEGACY_DPRX_MSA(0x0)
+#define TSI_DPRX_MSA_STREAM_COUNT_R TSI_BASE_LEGACY_DPRX_MSA(0x1)
+#define TSI_DPRX_MSA_STREAM_SELECT TSI_BASE_LEGACY_DPRX_MSA(0x3)
+#define TSI_DPRX_MSA_HTOTAL_R TSI_BASE_LEGACY_DPRX_MSA(0x6)
+#define TSI_DPRX_MSA_VTOTAL_R TSI_BASE_LEGACY_DPRX_MSA(0x7)
+#define TSI_DPRX_MSA_HACTIVE_R TSI_BASE_LEGACY_DPRX_MSA(0x8)
+#define TSI_DPRX_MSA_VACTIVE_R TSI_BASE_LEGACY_DPRX_MSA(0x9)
+#define TSI_DPRX_MSA_HSYNC_WIDTH_R TSI_BASE_LEGACY_DPRX_MSA(0xa)
+#define TSI_DPRX_MSA_VSYNC_WIDTH_R TSI_BASE_LEGACY_DPRX_MSA(0xb)
+#define TSI_DPRX_MSA_HSTART_R TSI_BASE_LEGACY_DPRX_MSA(0xc)
+#define TSI_DPRX_MSA_VSTART_R TSI_BASE_LEGACY_DPRX_MSA(0xd)
+
+#define TSI_DPRX_LINK_FLAGS_MST 0x01
+#define TSI_DPRX_LINK_FLAGS_TPS3 0x02
+#define TSI_DPRX_LINK_FLAGS_TPS4 0x03
+#define TSI_DPRX_LINK_FLAGS_EDP 0x04
+#define TSI_DPRX_NOT_DOCUMENTED_DP_128_132_SUPPORTED 0x10
+#define TSI_DPRX_NOT_DOCUMENTED_SIDEBAND_MSG_SUPPORT 0x20
+
+#define TSI_BASE_DPRX(offset) (0x50000000u + 0x21000 + (offset))
+#define TSI_DPRX_HW_CAPS_R TSI_BASE_DPRX(0x4)
+
+/**
+ * struct TSI_DPRX_HW_CAPS_R_s - Structure representing the hardware capabilities of the DP RX.
+ *
+ * This structure defines the bitfields and fields that describe the hardware
+ * capabilities of the DP RX (DisplayPort Receiver) interface. Each field
+ * corresponds to a specific capability or feature supported by the hardware.
+ *
+ * This structure is used to interpret the value read from the
+ * TSI_DPRX_HW_CAPS_R register.
+ *
+ * @mst: MST support
+ * @hdcp_1_x: HDCP 1.x support.
+ * @hdcp_2_x: HDCP 2.x support.
+ * @fec_8_10_b: FEC for 8/10 link support.
+ * @dsc_8_10_b: DSC for 8/10 link support.
+ * @three_lanes: Three lane link configuration support.
+ * @edp_link_rate: eDP link rates are supported.
+ * @mst_stream_count: Number of MST streams supported.
+ * @max_link_rate: Maximum link rate supported. The unit is not specified in documentation,
+ * it is probably like other config rate = 0.27GHz * value
+ * @force_link_config: Forced link configuration support.
+ * @power_provision: Power provision support on DP_PWR pin of receptacle connector.
+ * @aux_swing_voltage_control: AUX output voltage swing control support.
+ * @custom_dp_rate: Custom DP 2.0 rate support.
+ * @custom_bit_rate: Custom bit rate support.
+ * @fec_128_132_b: FEC for 128/132 link support.
+ * @dsc_128_132_b: DSC for 128/132 link support.
+ */
+struct TSI_DPRX_HW_CAPS_R_s {
+ bool mst:1;
+ bool hdcp_1_x:1;
+ bool hdcp_2_x:1;
+ bool fec_8_10_b:1;
+ bool dsc_8_10_b:1;
+ bool reserved_1:1;
+ bool three_lanes:1;
+ bool edp_link_rates_supported:1;
+ uint8_t mst_stream_count:3;
+ uint8_t reserved_2:5;
+ uint8_t max_link_rate;
+ bool force_link_config:1;
+ bool reserved_3:1;
+ bool power_provision:1;
+ bool aux_swing_voltage_control:1;
+ bool custom_dp_rate:1;
+ bool custom_bit_rate:1;
+ bool fec_128_132_b:1;
+ bool dsc_128_132_b:1;
+};
+
+#define TSI_DPRX_LT_LANE_COUNT_R TSI_BASE_DPRX(0x0B)
+#define TSI_DPRX_LT_RATE_R TSI_BASE_DPRX(0x0C)
+#define TSI_DPRX_HPD_FORCE TSI_BASE_DPRX(0x12)
+#define TSI_DPRX_MST_SINK_COUNT TSI_BASE_DPRX(0x9D)
+
+#define TSI_BASE_DP_RX(offset) (0x00010100 + (offset))
+#define TSI_DP_RX_DUT_MAX_LANE_COUNT TSI_BASE_DP_RX(0xf)
+
+#define TSI_BASE_DP_LTT(offset) (0x00010700 + (offset))
+#define TSI_DP_LTT_MAX_LANE_COUNT TSI_BASE_DP_LTT(0x01)
+
+#define TSI_BASE_LEGACY_DPRX(offset) (0x2b0 + (offset))
+#define TSI_DPRX_DPCD_BASE_W TSI_BASE_LEGACY_DPRX(0x9)
+#define TSI_DPRX_DPCD_DATA TSI_BASE_LEGACY_DPRX(0xA)
+#define TSI_DPRX_MAX_LANES TSI_BASE_LEGACY_DPRX(0x10)
+#define TSI_DPRX_MAX_LINK_RATE TSI_BASE_LEGACY_DPRX(0x11)
+#define TSI_DPRX_LINK_FLAGS TSI_BASE_LEGACY_DPRX(0x12)
+#define TSI_DPRX_STREAM_SELECT TSI_BASE_LEGACY_DPRX(0x13)
+#define TSI_DPRX_CRC_R_R TSI_BASE_LEGACY_DPRX(0x14)
+#define TSI_DPRX_CRC_G_R TSI_BASE_LEGACY_DPRX(0x15)
+#define TSI_DPRX_CRC_B_R TSI_BASE_LEGACY_DPRX(0x16)
+#define TSI_DPRX_HPD_PULSE_W TSI_BASE_LEGACY_DPRX(0x1B)
+
+#define H2_SINK_LOAD_PROD_KEYS 0x002
+#define H2_SINK_UNLOAD_KEYS 0x003
+#define H2_SINK_SET_CAPABLE 0x004
+#define H2_SINK_CLEAR_CAPABLE 0x005
+
+#define TSI_BASE_LEGACY_HDCP2(offset) (0x290 + (offset))
+#define TSI_HDCP_2X_COMMAND_W TSI_BASE_LEGACY_HDCP2(0x1)
+
+#endif /* TSI_REG_H */
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* [PATCH i-g-t v9 19/49] lib/unigraf: Add TSI.h
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (17 preceding siblings ...)
2026-03-16 16:17 ` [PATCH i-g-t v9 18/49] lib/unigraf: Add used defines for TSI_Types Louis Chauvet
@ 2026-03-16 16:17 ` Louis Chauvet
2026-03-16 16:17 ` [PATCH i-g-t v9 20/49] lib/unigraf: Initial Unigraf support Louis Chauvet
` (30 subsequent siblings)
49 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:17 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
Unigraf does not provide header for the libTSI.so file, only dynamic
library loading helpers for windows.
In order to link against this library and use unigraf devices, add the
function declaration used in the dynamic library loading wrappers.
Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
lib/unigraf/TSI.h | 228 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 228 insertions(+)
diff --git a/lib/unigraf/TSI.h b/lib/unigraf/TSI.h
new file mode 100644
index 000000000000..ee822c19a055
--- /dev/null
+++ b/lib/unigraf/TSI.h
@@ -0,0 +1,228 @@
+/* SPDX-License-Identifier: MIT */
+
+/*
+ * tsi.h - Header for libTSI.so
+ * Documentation here is taken from official documentation and developer observation.
+ */
+
+#ifndef TSI_H
+#define TSI_H
+
+#define TSI_CURRENT_VERSION 12
+#define MAX_EDID_SIZE 4096
+
+#define TSI_SUCCESS 0
+
+typedef unsigned int TSI_VERSION_ID;
+typedef unsigned int TSI_SEARCH_OPTIONS;
+typedef unsigned int TSI_DEVICE_CAPS;
+typedef unsigned int TSI_CONFIG_ID;
+typedef unsigned int TSI_DEVICE_ID;
+typedef unsigned int TSI_INPUT_ID;
+typedef int TSI_RESULT;
+typedef void *TSI_HANDLE;
+typedef int TSI_FLAGS;
+
+/**
+ * TSI_MISC_GetErrorDescription() - Get a human readable error message
+ * @ErrorCode: Error code for which you want the message
+ * @ErrorString: Pointer where to copy the message
+ * @StringMaxLen: Size of the allocated string @ErrorString
+ *
+ * The official documentation states: If the function succeeds, the
+ * return value is the number of characters required for the complete
+ * error description string.
+ * In reality, this function always returns 0 or < 0 (error), so there is no way to
+ * tell if the allocated memory was big enough
+ *
+ * Returns:
+ * - >= 0 on success, theorically the required string len to store the message
+ * - < 0 on failure
+ */
+TSI_RESULT TSI_MISC_GetErrorDescription(TSI_RESULT ErrorCode,
+ char *ErrorString,
+ unsigned int StringMaxLen);
+
+/**
+ * TSI_Init() - Initialize the TSI library
+ * @ClientVersion: Indicates the version used to call the libTSI.so functions.
+ *
+ * Initialize libTSI for use and sets up internal state. It can be called
+ * multiple times, but TSI_Clean must be called the exact same number of time.
+ *
+ * Returns:
+ * - In case of success: Reference count to the API (number of times to call TSI_Clean)
+ * - TSI_ERROR_NOT_COMPATIBLE if the requested client version is not supported
+ * by the library
+ * - TSI_ERROR_COMPATIBILITY_MISMATCH if TSI_Init is called twice with
+ * different client version
+ */
+TSI_RESULT TSI_Init(TSI_VERSION_ID ClientVersion);
+
+/**
+ * TSI_Clean() - Cleans and closes the TSI library
+ *
+ * When TSI_Clean is called for the last time, cleanup the internal state. It
+ * should be called exactly the same number of times as TSI_Init
+ */
+TSI_RESULT TSI_Clean(void);
+
+/**
+ * TSIX_DEV_RescanDevices() - Refresh the internal list of devices for libTSI
+ * @SearchOptions: Options to filter the list of devices (e.g.,
+ * TSI_SEARCHOPTIONS_SHOW_DEVICES_IN_USE to include
+ * devices already in use).
+ * @RequiredCaps: Filter to list only devices with specific capabilities.
+ * @UnallowedCaps: Filter to list only devices without specific capabilities.
+ *
+ * Returns: >=0 in case of success, <0 on error. The value is not the number of devices,
+ * you need to use TSIX_DEV_GetDeviceCount to get the discovered devices count.
+ *
+ * This function should be called every time you need to update the list of connected devices,
+ * and it must be called at least once before calling TSI_DEV_GetDeviceCount.
+ */
+TSI_RESULT TSIX_DEV_RescanDevices(TSI_SEARCH_OPTIONS SearchOptions,
+ TSI_DEVICE_CAPS RequiredCaps,
+ TSI_DEVICE_CAPS UnallowedCaps);
+
+/**
+ * TSIX_DEV_GetDeviceCount() - Get the count of scanned devices
+ * Returns: the number of devices that the previous call to TSIX_DEV_RescanDevices() detected
+ *
+ * Must be called after a TSIX_DEV_RescanDevices.
+ */
+TSI_RESULT TSIX_DEV_GetDeviceCount(void);
+
+/**
+ * TSIX_DEV_GetDeviceName() - Get the name of a device from the scanned list
+ * @DeviceID: Index in the TSI_DEV_RescanDevices list
+ * @DevNameString: Pointer to store the device name
+ * @NameStringMaxLength: Size of the allocated memory for @DevNameString
+ *
+ * Returns: >=0 in case of success, the length of the device name string
+ * Note: If the return value is larger than NameStringMaxLength, the string may be truncated
+ */
+TSI_RESULT TSIX_DEV_GetDeviceName(TSI_DEVICE_ID DeviceID, char *DevNameString,
+ unsigned int NameStringMaxLength);
+
+/**
+ * TSIX_DEV_OpenDevice() - Open a device from the scanned list
+ * @DeviceID: index in the TSI_DEV_RescanDevices list
+ * @Result: Pointer to store the error code returned while opening the device
+ * Returns: if the device is found, an opaque pointer that can be used for other
+ * API calls, or NULL on error.
+ */
+TSI_HANDLE TSIX_DEV_OpenDevice(TSI_DEVICE_ID DeviceID, TSI_RESULT *Result);
+
+/**
+ * TSIX_DEV_CloseDevice() - Close the device handle when finished
+ * @Device: Device handle to close
+ * Returns: >=0 in case of success
+ */
+TSI_RESULT TSIX_DEV_CloseDevice(TSI_HANDLE Device);
+
+/**
+ * TSIX_DEV_GetDeviceRoleCount() - Get the number of roles available for a device
+ * @Device: Device handle to query
+ *
+ * Returns: >=0 in case of success, the number of roles available for the device
+ */
+TSI_RESULT TSIX_DEV_GetDeviceRoleCount(TSI_HANDLE Device);
+
+/**
+ * TSIX_DEV_GetDeviceRoleName() - Get the name of a specific role for a device
+ * @Device: Device handle to query
+ * @RoleIndex: Index of the role to get the name for
+ * @RoleNameString: Pointer to store the role name
+ * @RoleStringMaxLength: Size of the allocated memory for @RoleNameString
+ *
+ * Returns: >=0 in case of success, the length of the role name string
+ * Note: If the return value is larger than RoleStringMaxLength, the string may be truncated
+ */
+TSI_RESULT TSIX_DEV_GetDeviceRoleName(TSI_HANDLE Device, int RoleIndex,
+ char *RoleNameString, unsigned int RoleStringMaxLength);
+
+/**
+ * TSIX_DEV_SelectRole() - Select a specific role for the specified device
+ * @Device: Device handle on which to assign the role
+ * @RoleIndex: Index of the role to assign
+ * Returns: >=0 in case of success
+ */
+TSI_RESULT TSIX_DEV_SelectRole(TSI_HANDLE Device, int RoleIndex);
+
+/**
+ * TSIX_VIN_GetInputCount() - Get the number of video inputs available on a device
+ * @Device: Device handle to query
+ *
+ * Returns: >=0 in case of success, the number of video inputs available on the device
+ */
+TSI_RESULT TSIX_VIN_GetInputCount(TSI_HANDLE Device);
+
+/**
+ * TSIX_VIN_GetInputName() - Get the name of a specific video input on a device
+ * @Device: Device handle to query
+ * @InputID: Identifier of the input to get the name for
+ * @InputNameString: Pointer to store the input name
+ * @NameStringMaxLen: Size of the allocated memory for @InputNameString
+ *
+ * Returns: >=0 in case of success, the length of the input name string
+ * Note: If the return value is larger than NameStringMaxLen, the string may be truncated
+ */
+TSI_RESULT TSIX_VIN_GetInputName(TSI_HANDLE Device, TSI_INPUT_ID InputID,
+ char *InputNameString, unsigned int NameStringMaxLen);
+
+/**
+ * TSIX_VIN_Disable() - Disable video input on the specified device
+ * @Device: Device handle to disable video input on
+ * Returns: >=0 in case of success
+ */
+TSI_RESULT TSIX_VIN_Disable(TSI_HANDLE Device);
+
+/**
+ * TSIX_VIN_Select() - Select a specific video input on the specified device
+ * @Device: Device handle on which to select the input
+ * @InputID: Identifier of the input to select
+ * Returns: >=0 in case of success
+ */
+
+TSI_RESULT TSIX_VIN_Select(TSI_HANDLE Device, TSI_INPUT_ID InputID);
+/**
+ * TSIX_VIN_Enable() - Enable video input on the specified device
+ * @Device: Device handle to enable video input on
+ * @Flags: Flags to specify the options for enabling the video input
+ * Returns: >=0 in case of success
+ */
+TSI_RESULT TSIX_VIN_Enable(TSI_HANDLE Device, TSI_FLAGS Flags);
+
+/**
+ * TSIX_TS_GetConfigItem() - Read a configuration item from the UCD device
+ * @Device: Device handle to read config from. Can be NULL for certain configuration items,
+ * for example TSI_VERSION_TEXT.
+ * @ConfigItemID: Identifier of the requested configuration item.
+ * @ConfigItemData: Pointer to store the read value.
+ * @ItemMaxSize: Size of the allocated memory for @ConfigItemData.
+ *
+ * Returns: The size of the raw data. If the return value is larger than ItemMaxSize, no data
+ * is copied to ConfigItemData.
+ * Note:
+ * - Some configurations require specific size and alignment for the allocated buffer. Refer to
+ * the specific item configuration documentation for details.
+ * - Data may still be written to @ConfigItemData even if the return value is larger than
+ * @ItemMaxSize, potentially causing buffer overflow.
+ */
+TSI_RESULT TSIX_TS_GetConfigItem(TSI_HANDLE Device, TSI_CONFIG_ID ConfigItemID,
+ void *ConfigItemData,
+ unsigned int ItemMaxSize);
+
+/**
+ * TSIX_TS_SetConfigItem() - Set a configuration item on the specified device
+ * @Device: Device handle to set the configuration on
+ * @ConfigItemID: Identifier of the configuration item to set
+ * @ItemData: Pointer to the data to set
+ * @ItemSize: Size of the data to set
+ * Returns: >=0 in case of success
+ */
+TSI_RESULT TSIX_TS_SetConfigItem(TSI_HANDLE Device, TSI_CONFIG_ID ConfigItemID,
+ const void *ItemData, unsigned int ItemSize);
+
+#endif
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* [PATCH i-g-t v9 20/49] lib/unigraf: Initial Unigraf support
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (18 preceding siblings ...)
2026-03-16 16:17 ` [PATCH i-g-t v9 19/49] lib/unigraf: Add TSI.h Louis Chauvet
@ 2026-03-16 16:17 ` Louis Chauvet
2026-03-24 8:19 ` Naladala, Ramanaidu
2026-03-24 9:58 ` Kamil Konieczny
2026-03-16 16:17 ` [PATCH i-g-t v9 21/49] lib/igt_kms: Automatically connect unigraf on display require Louis Chauvet
` (29 subsequent siblings)
49 siblings, 2 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:17 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
This introduce the basic boilerplate to connect to a unigraf device.
This integration currently only supports one device opened to simplify
its usage and cleanup.
The functions unigraf_open_device and unigraf_require_device will register
a handler to do proper cleanup on IGT exit.
Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
lib/meson.build | 10 +++++
lib/unigraf/unigraf.c | 120 ++++++++++++++++++++++++++++++++++++++++++++++++++
lib/unigraf/unigraf.h | 43 ++++++++++++++++++
meson.build | 14 ++++++
4 files changed, 187 insertions(+)
diff --git a/lib/meson.build b/lib/meson.build
index cd03e8f634e4..268f3238cbfa 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -151,6 +151,13 @@ lib_deps = [
zlib
]
+if libtsi.found()
+ lib_deps += libtsi
+ lib_sources += [
+ 'unigraf/unigraf.c'
+ ]
+endif
+
if libdrm_nouveau.found()
lib_deps += libdrm_nouveau
lib_sources += [
@@ -232,6 +239,9 @@ endif
if chamelium.found()
lib_deps += chamelium
lib_sources += [ 'igt_chamelium.c', 'igt_chamelium_stream.c' ]
+endif
+
+if chamelium.found() or libtsi.found()
lib_sources += 'monitor_edids/monitor_edids_helper.c'
endif
diff --git a/lib/unigraf/unigraf.c b/lib/unigraf/unigraf.c
new file mode 100644
index 000000000000..df18ab178e16
--- /dev/null
+++ b/lib/unigraf/unigraf.c
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2026 Google
+ *
+ * Authors:
+ * Louis Chauvet <louis.chauvet@bootlin.com>
+ */
+
+#include "igt_core.h"
+#include <stdint.h>
+
+#include "unigraf.h"
+#include "TSI.h"
+#include "TSI_types.h"
+
+#define unigraf_debug(fmt, ...) igt_debug("TSI:%p: " fmt, unigraf_device, ##__VA_ARGS__)
+
+static TSI_HANDLE unigraf_device;
+static char *unigraf_default_edid;
+static char *unigraf_connector_name;
+
+static void unigraf_close_device(void)
+{
+ if (!unigraf_device)
+ return;
+
+ unigraf_debug("Closing...\n");
+ unigraf_assert(TSIX_DEV_CloseDevice(unigraf_device));
+ TSI_Clean();
+ unigraf_device = NULL;
+ free(unigraf_default_edid);
+ free(unigraf_connector_name);
+}
+
+/**
+ * unigraf_exit_handler - Handle the exit signal and clean up unigraf resources.
+ * @sig: The signal number received.
+ *
+ * This function is called when the program receives an exit signal. It ensures
+ * that all unigraf resources are properly cleaned up by calling unigraf_deinit
+ * for each open instance.
+ */
+static void unigraf_exit_handler(int sig)
+{
+ unigraf_close_device();
+}
+
+static void unigraf_init(void)
+{
+ int ret;
+
+ unigraf_debug("Initialize unigraf...\n");
+ ret = TSI_Init(TSI_CURRENT_VERSION);
+ unigraf_assert(ret);
+ igt_install_exit_handler(unigraf_exit_handler);
+}
+
+/**
+ * unigraf_device_count() - Return the number of scanned devices
+ *
+ * Must be called after a unigraf_rescan_devices().
+ */
+static unsigned int unigraf_device_count(void)
+{
+ return unigraf_assert(TSIX_DEV_GetDeviceCount());
+}
+
+/**
+ * unigraf_open_device() - Search and open a device.
+ * @drm_fd: File descriptor of the currently used drm device
+ *
+ * Returns: true if a device was found and initialized, otherwise false.
+ *
+ * This function searches for a compatible device and opens it.
+ */
+bool unigraf_open_device(int drm_fd)
+{
+ TSI_RESULT r;
+ int device_count;
+ int chosen_device = 0;
+ int chosen_role = 0;
+ int chosen_input = 0;
+
+ assert(igt_can_fail());
+
+ if (unigraf_device)
+ return true;
+
+ unigraf_init();
+
+ unigraf_assert(TSIX_DEV_RescanDevices(0, TSI_DEVCAP_VIDEO_CAPTURE, 0));
+
+ device_count = unigraf_device_count();
+ if (device_count < 1) {
+ unigraf_debug("No device found.\n");
+ return false;
+ }
+
+ unigraf_device = TSIX_DEV_OpenDevice(chosen_device, &r);
+ unigraf_assert(r);
+ igt_assert(unigraf_device);
+ unigraf_debug("Successfully opened the unigraf device %d.\n", chosen_device);
+
+ unigraf_assert(TSIX_DEV_SelectRole(unigraf_device, chosen_role));
+ unigraf_assert(TSIX_VIN_Select(unigraf_device, chosen_input));
+ unigraf_assert(TSIX_VIN_Enable(unigraf_device, chosen_input));
+
+ return true;
+}
+
+/**
+ * unigraf_require_device() - Search and open a device.
+ * @drm_fd: File descriptor of the currently used drm device
+ *
+ * This is a shorthand to reduce test boilerplate when a unigraf device must be present.
+ */
+void unigraf_require_device(int drm_fd)
+{
+ igt_require(unigraf_open_device(drm_fd));
+}
diff --git a/lib/unigraf/unigraf.h b/lib/unigraf/unigraf.h
new file mode 100644
index 000000000000..c08ce62894c5
--- /dev/null
+++ b/lib/unigraf/unigraf.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2026 Google
+ *
+ * Authors:
+ * Louis Chauvet <louis.chauvet@bootlin.com>
+ */
+
+#ifndef UNIGRAF_H
+#define UNIGRAF_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+/**
+ * unigraf_assert: Helper macro to assert a TSI return value and retrieve a detailed error message.
+ * @result: libTSI return value to check
+ *
+ * This macro checks the return value of a libTSI function call. If the return value indicates an
+ * error, it retrieves a detailed error message and asserts with that message.
+ * If retrieving the error description fails, it asserts with a generic error message.
+ */
+#define unigraf_assert(result) \
+({ \
+ char msg[256]; \
+ TSI_RESULT __r = (result); \
+ if (__r < TSI_SUCCESS) { \
+ TSI_RESULT __r2 = TSI_MISC_GetErrorDescription(__r, msg, sizeof(msg)); \
+ if (__r2 < TSI_SUCCESS) \
+ igt_assert_f(false, \
+ "unigraf error: %d (get error description failed: %d)\n", \
+ __r, __r2); \
+ else \
+ igt_assert_f(false, "unigraf error: %d (%s)\n", __r, msg); \
+ } \
+ (__r); \
+})
+
+bool unigraf_open_device(int drm_fd);
+
+void unigraf_require_device(int drm_fd);
+
+#endif // UNIGRAF_H
diff --git a/meson.build b/meson.build
index 57849648a377..127abbf62024 100644
--- a/meson.build
+++ b/meson.build
@@ -166,6 +166,12 @@ libpci = dependency('libpci', required : true)
libudev = dependency('libudev', required : true)
glib = dependency('glib-2.0', required : true)
+libtsi = cc.find_library('TSI', required : false)
+
+if libtsi.found()
+ config.set('HAVE_UNIGRAF', 1)
+endif
+
xmlrpc = dependency('xmlrpc', required : false)
xmlrpc_util = dependency('xmlrpc_util', required : false)
xmlrpc_client = dependency('xmlrpc_client', required : false)
@@ -290,6 +296,7 @@ amdgpudir = join_paths(libexecdir, 'amdgpu')
msmdir = join_paths(libexecdir, 'msm')
panfrostdir = join_paths(libexecdir, 'panfrost')
panthordir = join_paths(libexecdir, 'panthor')
+unigrafdir = join_paths(libexecdir, 'unigraf')
v3ddir = join_paths(libexecdir, 'v3d')
vc4dir = join_paths(libexecdir, 'vc4')
vkmsdir = join_paths(libexecdir, 'vkms')
@@ -372,6 +379,12 @@ if get_option('use_rpath')
vmwgfx_rpathdir = join_paths(vmwgfx_rpathdir, '..')
endforeach
vmwgfx_rpathdir = join_paths(vmwgfx_rpathdir, libdir)
+
+ unigraf_rpathdir = '$ORIGIN'
+ foreach p : unigrafdir.split('/')
+ unigraf_rpathdir = join_paths(unigraf_rpathdir, '..')
+ endforeach
+ unigraf_rpathdir = join_paths(unigraf_rpathdir, libdir)
else
bindir_rpathdir = ''
libexecdir_rpathdir = ''
@@ -383,6 +396,7 @@ else
vc4_rpathdir = ''
vkms_rpathdir = ''
vmwgfx_rpathdir = ''
+ unigraf_rpathdir = ''
endif
build_testplan = get_option('testplan')
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* Re: [PATCH i-g-t v9 20/49] lib/unigraf: Initial Unigraf support
2026-03-16 16:17 ` [PATCH i-g-t v9 20/49] lib/unigraf: Initial Unigraf support Louis Chauvet
@ 2026-03-24 8:19 ` Naladala, Ramanaidu
2026-03-24 9:37 ` Louis Chauvet
2026-03-24 9:58 ` Kamil Konieczny
1 sibling, 1 reply; 83+ messages in thread
From: Naladala, Ramanaidu @ 2026-03-24 8:19 UTC (permalink / raw)
To: igt-dev
Hi Louis Chauvet,
On 3/16/2026 9:47 PM, Louis Chauvet wrote:
> This introduce the basic boilerplate to connect to a unigraf device.
>
> This integration currently only supports one device opened to simplify
> its usage and cleanup.
>
> The functions unigraf_open_device and unigraf_require_device will register
> a handler to do proper cleanup on IGT exit.
>
> Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> ---
> lib/meson.build | 10 +++++
> lib/unigraf/unigraf.c | 120 ++++++++++++++++++++++++++++++++++++++++++++++++++
> lib/unigraf/unigraf.h | 43 ++++++++++++++++++
> meson.build | 14 ++++++
> 4 files changed, 187 insertions(+)
>
> diff --git a/lib/meson.build b/lib/meson.build
> index cd03e8f634e4..268f3238cbfa 100644
> --- a/lib/meson.build
> +++ b/lib/meson.build
> @@ -151,6 +151,13 @@ lib_deps = [
> zlib
> ]
>
> +if libtsi.found()
> + lib_deps += libtsi
> + lib_sources += [
> + 'unigraf/unigraf.c'
> + ]
> +endif
> +
> if libdrm_nouveau.found()
> lib_deps += libdrm_nouveau
> lib_sources += [
> @@ -232,6 +239,9 @@ endif
> if chamelium.found()
> lib_deps += chamelium
> lib_sources += [ 'igt_chamelium.c', 'igt_chamelium_stream.c' ]
> +endif
> +
> +if chamelium.found() or libtsi.found()
> lib_sources += 'monitor_edids/monitor_edids_helper.c'
> endif
>
> diff --git a/lib/unigraf/unigraf.c b/lib/unigraf/unigraf.c
> new file mode 100644
> index 000000000000..df18ab178e16
> --- /dev/null
> +++ b/lib/unigraf/unigraf.c
> @@ -0,0 +1,120 @@
> +// SPDX-License-Identifier: MIT
> +/*
> + * Copyright © 2026 Google
> + *
> + * Authors:
> + * Louis Chauvet <louis.chauvet@bootlin.com>
> + */
> +
> +#include "igt_core.h"
> +#include <stdint.h>
> +
> +#include "unigraf.h"
> +#include "TSI.h"
> +#include "TSI_types.h"
> +
> +#define unigraf_debug(fmt, ...) igt_debug("TSI:%p: " fmt, unigraf_device, ##__VA_ARGS__)
> +
> +static TSI_HANDLE unigraf_device;
> +static char *unigraf_default_edid;
> +static char *unigraf_connector_name;
> +
> +static void unigraf_close_device(void)
> +{
> + if (!unigraf_device)
> + return;
> +
> + unigraf_debug("Closing...\n");
> + unigraf_assert(TSIX_DEV_CloseDevice(unigraf_device));
> + TSI_Clean();
> + unigraf_device = NULL;
> + free(unigraf_default_edid);
> + free(unigraf_connector_name);
> +}
> +
> +/**
> + * unigraf_exit_handler - Handle the exit signal and clean up unigraf resources.
> + * @sig: The signal number received.
> + *
> + * This function is called when the program receives an exit signal. It ensures
> + * that all unigraf resources are properly cleaned up by calling unigraf_deinit
> + * for each open instance.
> + */
> +static void unigraf_exit_handler(int sig)
> +{
> + unigraf_close_device();
> +}
Unigraf sub test is Success. But the exit handler still asserts due to
invalid argument during unigraf_close_device().
Subtest unigraf-connect-edid: SUCCESS (43.200s)
(unigraf_connectivity:5625) igt_core-DEBUG: Exiting with status code 0
(unigraf_connectivity:5625) unigraf/unigraf-DEBUG: TSI:0x6010b136c010:
Closing...
(unigraf_connectivity:5625) unigraf/unigraf-CRITICAL: Test assertion
failure function unigraf_close_device, file ../lib/unigraf/unigraf.c:97:
(unigraf_connectivity:5625) unigraf/unigraf-CRITICAL: Failed assertion:
false
(unigraf_connectivity:5625) unigraf/unigraf-CRITICAL: Last errno: 22,
Invalid argument
(unigraf_connectivity:5625) unigraf/unigraf-CRITICAL: unigraf error: -50
(Invalid device handle.)
Stack trace:
#0 ../lib/igt_core.c:2108 __igt_fail_assert()
#1 ../lib/unigraf/unigraf.c:97 unigraf_exit_handler()
#2 ../lib/igt_core.c:2994 igt_atexit_handler()
#3 ./stdlib/exit.c:119 __run_exit_handlers()
#4 [exit+0x1e]
#5 ../lib/igt_core.c:2419 igt_exit()
#6 [main+0x32]
#7 ../sysdeps/nptl/libc_start_call_main.h:74 __libc_start_call_main()
#8 ../csu/libc-start.c:128 __libc_start_main@@GLIBC_2.34()
#9 [_start+0x25]
> +
> +static void unigraf_init(void)
> +{
> + int ret;
> +
> + unigraf_debug("Initialize unigraf...\n");
> + ret = TSI_Init(TSI_CURRENT_VERSION);
> + unigraf_assert(ret);
> + igt_install_exit_handler(unigraf_exit_handler);
> +}
> +
> +/**
> + * unigraf_device_count() - Return the number of scanned devices
> + *
> + * Must be called after a unigraf_rescan_devices().
> + */
> +static unsigned int unigraf_device_count(void)
> +{
> + return unigraf_assert(TSIX_DEV_GetDeviceCount());
> +}
> +
> +/**
> + * unigraf_open_device() - Search and open a device.
> + * @drm_fd: File descriptor of the currently used drm device
> + *
> + * Returns: true if a device was found and initialized, otherwise false.
> + *
> + * This function searches for a compatible device and opens it.
> + */
> +bool unigraf_open_device(int drm_fd)
> +{
> + TSI_RESULT r;
> + int device_count;
> + int chosen_device = 0;
> + int chosen_role = 0;
> + int chosen_input = 0;
> +
> + assert(igt_can_fail());
> +
> + if (unigraf_device)
> + return true;
> +
> + unigraf_init();
> +
> + unigraf_assert(TSIX_DEV_RescanDevices(0, TSI_DEVCAP_VIDEO_CAPTURE, 0));
> +
> + device_count = unigraf_device_count();
> + if (device_count < 1) {
> + unigraf_debug("No device found.\n");
> + return false;
> + }
> +
> + unigraf_device = TSIX_DEV_OpenDevice(chosen_device, &r);
> + unigraf_assert(r);
> + igt_assert(unigraf_device);
> + unigraf_debug("Successfully opened the unigraf device %d.\n", chosen_device);
> +
> + unigraf_assert(TSIX_DEV_SelectRole(unigraf_device, chosen_role));
> + unigraf_assert(TSIX_VIN_Select(unigraf_device, chosen_input));
> + unigraf_assert(TSIX_VIN_Enable(unigraf_device, chosen_input));
> +
> + return true;
> +}
> +
> +/**
> + * unigraf_require_device() - Search and open a device.
> + * @drm_fd: File descriptor of the currently used drm device
> + *
> + * This is a shorthand to reduce test boilerplate when a unigraf device must be present.
> + */
> +void unigraf_require_device(int drm_fd)
> +{
> + igt_require(unigraf_open_device(drm_fd));
> +}
> diff --git a/lib/unigraf/unigraf.h b/lib/unigraf/unigraf.h
> new file mode 100644
> index 000000000000..c08ce62894c5
> --- /dev/null
> +++ b/lib/unigraf/unigraf.h
> @@ -0,0 +1,43 @@
> +/* SPDX-License-Identifier: MIT */
> +/*
> + * Copyright © 2026 Google
> + *
> + * Authors:
> + * Louis Chauvet <louis.chauvet@bootlin.com>
> + */
> +
> +#ifndef UNIGRAF_H
> +#define UNIGRAF_H
> +
> +#include <stdbool.h>
> +#include <stdint.h>
> +
> +/**
> + * unigraf_assert: Helper macro to assert a TSI return value and retrieve a detailed error message.
> + * @result: libTSI return value to check
> + *
> + * This macro checks the return value of a libTSI function call. If the return value indicates an
> + * error, it retrieves a detailed error message and asserts with that message.
> + * If retrieving the error description fails, it asserts with a generic error message.
> + */
> +#define unigraf_assert(result) \
> +({ \
> + char msg[256]; \
> + TSI_RESULT __r = (result); \
> + if (__r < TSI_SUCCESS) { \
> + TSI_RESULT __r2 = TSI_MISC_GetErrorDescription(__r, msg, sizeof(msg)); \
> + if (__r2 < TSI_SUCCESS) \
> + igt_assert_f(false, \
> + "unigraf error: %d (get error description failed: %d)\n", \
> + __r, __r2); \
> + else \
> + igt_assert_f(false, "unigraf error: %d (%s)\n", __r, msg); \
> + } \
> + (__r); \
> +})
> +
> +bool unigraf_open_device(int drm_fd);
> +
> +void unigraf_require_device(int drm_fd);
> +
> +#endif // UNIGRAF_H
> diff --git a/meson.build b/meson.build
> index 57849648a377..127abbf62024 100644
> --- a/meson.build
> +++ b/meson.build
> @@ -166,6 +166,12 @@ libpci = dependency('libpci', required : true)
> libudev = dependency('libudev', required : true)
> glib = dependency('glib-2.0', required : true)
>
> +libtsi = cc.find_library('TSI', required : false)
> +
> +if libtsi.found()
> + config.set('HAVE_UNIGRAF', 1)
> +endif
> +
> xmlrpc = dependency('xmlrpc', required : false)
> xmlrpc_util = dependency('xmlrpc_util', required : false)
> xmlrpc_client = dependency('xmlrpc_client', required : false)
> @@ -290,6 +296,7 @@ amdgpudir = join_paths(libexecdir, 'amdgpu')
> msmdir = join_paths(libexecdir, 'msm')
> panfrostdir = join_paths(libexecdir, 'panfrost')
> panthordir = join_paths(libexecdir, 'panthor')
> +unigrafdir = join_paths(libexecdir, 'unigraf')
> v3ddir = join_paths(libexecdir, 'v3d')
> vc4dir = join_paths(libexecdir, 'vc4')
> vkmsdir = join_paths(libexecdir, 'vkms')
> @@ -372,6 +379,12 @@ if get_option('use_rpath')
> vmwgfx_rpathdir = join_paths(vmwgfx_rpathdir, '..')
> endforeach
> vmwgfx_rpathdir = join_paths(vmwgfx_rpathdir, libdir)
> +
> + unigraf_rpathdir = '$ORIGIN'
> + foreach p : unigrafdir.split('/')
> + unigraf_rpathdir = join_paths(unigraf_rpathdir, '..')
> + endforeach
> + unigraf_rpathdir = join_paths(unigraf_rpathdir, libdir)
> else
> bindir_rpathdir = ''
> libexecdir_rpathdir = ''
> @@ -383,6 +396,7 @@ else
> vc4_rpathdir = ''
> vkms_rpathdir = ''
> vmwgfx_rpathdir = ''
> + unigraf_rpathdir = ''
> endif
>
> build_testplan = get_option('testplan')
>
^ permalink raw reply [flat|nested] 83+ messages in thread* Re: [PATCH i-g-t v9 20/49] lib/unigraf: Initial Unigraf support
2026-03-24 8:19 ` Naladala, Ramanaidu
@ 2026-03-24 9:37 ` Louis Chauvet
2026-03-26 9:59 ` Louis Chauvet
0 siblings, 1 reply; 83+ messages in thread
From: Louis Chauvet @ 2026-03-24 9:37 UTC (permalink / raw)
To: Naladala, Ramanaidu, igt-dev
On 3/24/26 09:19, Naladala, Ramanaidu wrote:
> Hi Louis Chauvet,
>
> On 3/16/2026 9:47 PM, Louis Chauvet wrote:
>> This introduce the basic boilerplate to connect to a unigraf device.
>>
>> This integration currently only supports one device opened to simplify
>> its usage and cleanup.
>>
>> The functions unigraf_open_device and unigraf_require_device will
>> register
>> a handler to do proper cleanup on IGT exit.
>>
>> Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
>> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
>> ---
>> lib/meson.build | 10 +++++
>> lib/unigraf/unigraf.c | 120 ++++++++++++++++++++++++++++++++++++++++
>> ++++++++++
>> lib/unigraf/unigraf.h | 43 ++++++++++++++++++
>> meson.build | 14 ++++++
>> 4 files changed, 187 insertions(+)
>>
>> diff --git a/lib/meson.build b/lib/meson.build
>> index cd03e8f634e4..268f3238cbfa 100644
>> --- a/lib/meson.build
>> +++ b/lib/meson.build
>> @@ -151,6 +151,13 @@ lib_deps = [
>> zlib
>> ]
>> +if libtsi.found()
>> + lib_deps += libtsi
>> + lib_sources += [
>> + 'unigraf/unigraf.c'
>> + ]
>> +endif
>> +
>> if libdrm_nouveau.found()
>> lib_deps += libdrm_nouveau
>> lib_sources += [
>> @@ -232,6 +239,9 @@ endif
>> if chamelium.found()
>> lib_deps += chamelium
>> lib_sources += [ 'igt_chamelium.c', 'igt_chamelium_stream.c' ]
>> +endif
>> +
>> +if chamelium.found() or libtsi.found()
>> lib_sources += 'monitor_edids/monitor_edids_helper.c'
>> endif
>> diff --git a/lib/unigraf/unigraf.c b/lib/unigraf/unigraf.c
>> new file mode 100644
>> index 000000000000..df18ab178e16
>> --- /dev/null
>> +++ b/lib/unigraf/unigraf.c
>> @@ -0,0 +1,120 @@
>> +// SPDX-License-Identifier: MIT
>> +/*
>> + * Copyright © 2026 Google
>> + *
>> + * Authors:
>> + * Louis Chauvet <louis.chauvet@bootlin.com>
>> + */
>> +
>> +#include "igt_core.h"
>> +#include <stdint.h>
>> +
>> +#include "unigraf.h"
>> +#include "TSI.h"
>> +#include "TSI_types.h"
>> +
>> +#define unigraf_debug(fmt, ...) igt_debug("TSI:%p: " fmt,
>> unigraf_device, ##__VA_ARGS__)
>> +
>> +static TSI_HANDLE unigraf_device;
>> +static char *unigraf_default_edid;
>> +static char *unigraf_connector_name;
>> +
>> +static void unigraf_close_device(void)
>> +{
>> + if (!unigraf_device)
>> + return;
>> +
>> + unigraf_debug("Closing...\n");
>> + unigraf_assert(TSIX_DEV_CloseDevice(unigraf_device));
>> + TSI_Clean();
>> + unigraf_device = NULL;
>> + free(unigraf_default_edid);
>> + free(unigraf_connector_name);
>> +}
>> +
>> +/**
>> + * unigraf_exit_handler - Handle the exit signal and clean up unigraf
>> resources.
>> + * @sig: The signal number received.
>> + *
>> + * This function is called when the program receives an exit signal.
>> It ensures
>> + * that all unigraf resources are properly cleaned up by calling
>> unigraf_deinit
>> + * for each open instance.
>> + */
>> +static void unigraf_exit_handler(int sig)
>> +{
>> + unigraf_close_device();
>> +}
>
> Unigraf sub test is Success. But the exit handler still asserts due to
> invalid argument during unigraf_close_device().
>
> Subtest unigraf-connect-edid: SUCCESS (43.200s)
> (unigraf_connectivity:5625) igt_core-DEBUG: Exiting with status code 0
> (unigraf_connectivity:5625) unigraf/unigraf-DEBUG: TSI:0x6010b136c010:
> Closing...
> (unigraf_connectivity:5625) unigraf/unigraf-CRITICAL: Test assertion
> failure function unigraf_close_device, file ../lib/unigraf/unigraf.c:97:
> (unigraf_connectivity:5625) unigraf/unigraf-CRITICAL: Failed assertion:
> false
> (unigraf_connectivity:5625) unigraf/unigraf-CRITICAL: Last errno: 22,
> Invalid argument
> (unigraf_connectivity:5625) unigraf/unigraf-CRITICAL: unigraf error: -50
> (Invalid device handle.)
> Stack trace:
> #0 ../lib/igt_core.c:2108 __igt_fail_assert()
> #1 ../lib/unigraf/unigraf.c:97 unigraf_exit_handler()
> #2 ../lib/igt_core.c:2994 igt_atexit_handler()
> #3 ./stdlib/exit.c:119 __run_exit_handlers()
> #4 [exit+0x1e]
> #5 ../lib/igt_core.c:2419 igt_exit()
> #6 [main+0x32]
> #7 ../sysdeps/nptl/libc_start_call_main.h:74 __libc_start_call_main()
> #8 ../csu/libc-start.c:128 __libc_start_main@@GLIBC_2.34()
> #9 [_start+0x25]
I saw it yesterday while checking the default_dectect_timeout values, it
was not the case in the few first iterations of the series. I am
currently looking to understand what happen.
My current guess is that unigraf_exit_handler is maybe called twice,
because the function itself works well in my tiny C programs.
>> +
>> +static void unigraf_init(void)
>> +{
>> + int ret;
>> +
>> + unigraf_debug("Initialize unigraf...\n");
>> + ret = TSI_Init(TSI_CURRENT_VERSION);
>> + unigraf_assert(ret);
>> + igt_install_exit_handler(unigraf_exit_handler);
>> +}
>> +
>> +/**
>> + * unigraf_device_count() - Return the number of scanned devices
>> + *
>> + * Must be called after a unigraf_rescan_devices().
>> + */
>> +static unsigned int unigraf_device_count(void)
>> +{
>> + return unigraf_assert(TSIX_DEV_GetDeviceCount());
>> +}
>> +
>> +/**
>> + * unigraf_open_device() - Search and open a device.
>> + * @drm_fd: File descriptor of the currently used drm device
>> + *
>> + * Returns: true if a device was found and initialized, otherwise false.
>> + *
>> + * This function searches for a compatible device and opens it.
>> + */
>> +bool unigraf_open_device(int drm_fd)
>> +{
>> + TSI_RESULT r;
>> + int device_count;
>> + int chosen_device = 0;
>> + int chosen_role = 0;
>> + int chosen_input = 0;
>> +
>> + assert(igt_can_fail());
>> +
>> + if (unigraf_device)
>> + return true;
>> +
>> + unigraf_init();
>> +
>> + unigraf_assert(TSIX_DEV_RescanDevices(0,
>> TSI_DEVCAP_VIDEO_CAPTURE, 0));
>> +
>> + device_count = unigraf_device_count();
>> + if (device_count < 1) {
>> + unigraf_debug("No device found.\n");
>> + return false;
>> + }
>> +
>> + unigraf_device = TSIX_DEV_OpenDevice(chosen_device, &r);
>> + unigraf_assert(r);
>> + igt_assert(unigraf_device);
>> + unigraf_debug("Successfully opened the unigraf device %d.\n",
>> chosen_device);
>> +
>> + unigraf_assert(TSIX_DEV_SelectRole(unigraf_device, chosen_role));
>> + unigraf_assert(TSIX_VIN_Select(unigraf_device, chosen_input));
>> + unigraf_assert(TSIX_VIN_Enable(unigraf_device, chosen_input));
>> +
>> + return true;
>> +}
>> +
>> +/**
>> + * unigraf_require_device() - Search and open a device.
>> + * @drm_fd: File descriptor of the currently used drm device
>> + *
>> + * This is a shorthand to reduce test boilerplate when a unigraf
>> device must be present.
>> + */
>> +void unigraf_require_device(int drm_fd)
>> +{
>> + igt_require(unigraf_open_device(drm_fd));
>> +}
>> diff --git a/lib/unigraf/unigraf.h b/lib/unigraf/unigraf.h
>> new file mode 100644
>> index 000000000000..c08ce62894c5
>> --- /dev/null
>> +++ b/lib/unigraf/unigraf.h
>> @@ -0,0 +1,43 @@
>> +/* SPDX-License-Identifier: MIT */
>> +/*
>> + * Copyright © 2026 Google
>> + *
>> + * Authors:
>> + * Louis Chauvet <louis.chauvet@bootlin.com>
>> + */
>> +
>> +#ifndef UNIGRAF_H
>> +#define UNIGRAF_H
>> +
>> +#include <stdbool.h>
>> +#include <stdint.h>
>> +
>> +/**
>> + * unigraf_assert: Helper macro to assert a TSI return value and
>> retrieve a detailed error message.
>> + * @result: libTSI return value to check
>> + *
>> + * This macro checks the return value of a libTSI function call. If
>> the return value indicates an
>> + * error, it retrieves a detailed error message and asserts with that
>> message.
>> + * If retrieving the error description fails, it asserts with a
>> generic error message.
>> + */
>> +#define unigraf_assert(result) \
>> +({ \
>> + char msg[256]; \
>> + TSI_RESULT __r = (result); \
>> + if (__r < TSI_SUCCESS) { \
>> + TSI_RESULT __r2 = TSI_MISC_GetErrorDescription(__r, msg,
>> sizeof(msg)); \
>> + if (__r2 < TSI_SUCCESS) \
>> + igt_assert_f(false, \
>> + "unigraf error: %d (get error description
>> failed: %d)\n", \
>> + __r, __r2); \
>> + else \
>> + igt_assert_f(false, "unigraf error: %d (%s)\n", __r,
>> msg); \
>> + } \
>> + (__r); \
>> +})
>> +
>> +bool unigraf_open_device(int drm_fd);
>> +
>> +void unigraf_require_device(int drm_fd);
>> +
>> +#endif // UNIGRAF_H
>> diff --git a/meson.build b/meson.build
>> index 57849648a377..127abbf62024 100644
>> --- a/meson.build
>> +++ b/meson.build
>> @@ -166,6 +166,12 @@ libpci = dependency('libpci', required : true)
>> libudev = dependency('libudev', required : true)
>> glib = dependency('glib-2.0', required : true)
>> +libtsi = cc.find_library('TSI', required : false)
>> +
>> +if libtsi.found()
>> + config.set('HAVE_UNIGRAF', 1)
>> +endif
>> +
>> xmlrpc = dependency('xmlrpc', required : false)
>> xmlrpc_util = dependency('xmlrpc_util', required : false)
>> xmlrpc_client = dependency('xmlrpc_client', required : false)
>> @@ -290,6 +296,7 @@ amdgpudir = join_paths(libexecdir, 'amdgpu')
>> msmdir = join_paths(libexecdir, 'msm')
>> panfrostdir = join_paths(libexecdir, 'panfrost')
>> panthordir = join_paths(libexecdir, 'panthor')
>> +unigrafdir = join_paths(libexecdir, 'unigraf')
>> v3ddir = join_paths(libexecdir, 'v3d')
>> vc4dir = join_paths(libexecdir, 'vc4')
>> vkmsdir = join_paths(libexecdir, 'vkms')
>> @@ -372,6 +379,12 @@ if get_option('use_rpath')
>> vmwgfx_rpathdir = join_paths(vmwgfx_rpathdir, '..')
>> endforeach
>> vmwgfx_rpathdir = join_paths(vmwgfx_rpathdir, libdir)
>> +
>> + unigraf_rpathdir = '$ORIGIN'
>> + foreach p : unigrafdir.split('/')
>> + unigraf_rpathdir = join_paths(unigraf_rpathdir, '..')
>> + endforeach
>> + unigraf_rpathdir = join_paths(unigraf_rpathdir, libdir)
>> else
>> bindir_rpathdir = ''
>> libexecdir_rpathdir = ''
>> @@ -383,6 +396,7 @@ else
>> vc4_rpathdir = ''
>> vkms_rpathdir = ''
>> vmwgfx_rpathdir = ''
>> + unigraf_rpathdir = ''
>> endif
>> build_testplan = get_option('testplan')
>>
^ permalink raw reply [flat|nested] 83+ messages in thread* Re: [PATCH i-g-t v9 20/49] lib/unigraf: Initial Unigraf support
2026-03-24 9:37 ` Louis Chauvet
@ 2026-03-26 9:59 ` Louis Chauvet
0 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-26 9:59 UTC (permalink / raw)
To: Naladala, Ramanaidu, igt-dev
On 3/24/26 10:37, Louis Chauvet wrote:
>
>
> On 3/24/26 09:19, Naladala, Ramanaidu wrote:
>> Hi Louis Chauvet,
>>
>> On 3/16/2026 9:47 PM, Louis Chauvet wrote:
>>> This introduce the basic boilerplate to connect to a unigraf device.
>>>
>>> This integration currently only supports one device opened to simplify
>>> its usage and cleanup.
>>>
>>> The functions unigraf_open_device and unigraf_require_device will
>>> register
>>> a handler to do proper cleanup on IGT exit.
>>>
>>> Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
>>> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
>>> ---
>>> lib/meson.build | 10 +++++
>>> lib/unigraf/unigraf.c | 120 +++++++++++++++++++++++++++++++++++++++
>>> + ++++++++++
>>> lib/unigraf/unigraf.h | 43 ++++++++++++++++++
>>> meson.build | 14 ++++++
>>> 4 files changed, 187 insertions(+)
>>>
>>> diff --git a/lib/meson.build b/lib/meson.build
>>> index cd03e8f634e4..268f3238cbfa 100644
>>> --- a/lib/meson.build
>>> +++ b/lib/meson.build
>>> @@ -151,6 +151,13 @@ lib_deps = [
>>> zlib
>>> ]
>>> +if libtsi.found()
>>> + lib_deps += libtsi
>>> + lib_sources += [
>>> + 'unigraf/unigraf.c'
>>> + ]
>>> +endif
>>> +
>>> if libdrm_nouveau.found()
>>> lib_deps += libdrm_nouveau
>>> lib_sources += [
>>> @@ -232,6 +239,9 @@ endif
>>> if chamelium.found()
>>> lib_deps += chamelium
>>> lib_sources += [ 'igt_chamelium.c', 'igt_chamelium_stream.c' ]
>>> +endif
>>> +
>>> +if chamelium.found() or libtsi.found()
>>> lib_sources += 'monitor_edids/monitor_edids_helper.c'
>>> endif
>>> diff --git a/lib/unigraf/unigraf.c b/lib/unigraf/unigraf.c
>>> new file mode 100644
>>> index 000000000000..df18ab178e16
>>> --- /dev/null
>>> +++ b/lib/unigraf/unigraf.c
>>> @@ -0,0 +1,120 @@
>>> +// SPDX-License-Identifier: MIT
>>> +/*
>>> + * Copyright © 2026 Google
>>> + *
>>> + * Authors:
>>> + * Louis Chauvet <louis.chauvet@bootlin.com>
>>> + */
>>> +
>>> +#include "igt_core.h"
>>> +#include <stdint.h>
>>> +
>>> +#include "unigraf.h"
>>> +#include "TSI.h"
>>> +#include "TSI_types.h"
>>> +
>>> +#define unigraf_debug(fmt, ...) igt_debug("TSI:%p: " fmt,
>>> unigraf_device, ##__VA_ARGS__)
>>> +
>>> +static TSI_HANDLE unigraf_device;
>>> +static char *unigraf_default_edid;
>>> +static char *unigraf_connector_name;
>>> +
>>> +static void unigraf_close_device(void)
>>> +{
>>> + if (!unigraf_device)
>>> + return;
>>> +
>>> + unigraf_debug("Closing...\n");
>>> + unigraf_assert(TSIX_DEV_CloseDevice(unigraf_device));
>>> + TSI_Clean();
>>> + unigraf_device = NULL;
>>> + free(unigraf_default_edid);
>>> + free(unigraf_connector_name);
>>> +}
>>> +
>>> +/**
>>> + * unigraf_exit_handler - Handle the exit signal and clean up
>>> unigraf resources.
>>> + * @sig: The signal number received.
>>> + *
>>> + * This function is called when the program receives an exit signal.
>>> It ensures
>>> + * that all unigraf resources are properly cleaned up by calling
>>> unigraf_deinit
>>> + * for each open instance.
>>> + */
>>> +static void unigraf_exit_handler(int sig)
>>> +{
>>> + unigraf_close_device();
>>> +}
>>
>> Unigraf sub test is Success. But the exit handler still asserts due to
>> invalid argument during unigraf_close_device().
>>
>> Subtest unigraf-connect-edid: SUCCESS (43.200s)
>> (unigraf_connectivity:5625) igt_core-DEBUG: Exiting with status code 0
>> (unigraf_connectivity:5625) unigraf/unigraf-DEBUG: TSI:0x6010b136c010:
For information, the TSI:0x60.... is the current "unigraf device handle"
(probably a pointer), as you can see, the exact same pointer is used
just before in the tests, and the only place where I call
unigraf_close_device is the exit handler.
>> Closing...
>> (unigraf_connectivity:5625) unigraf/unigraf-CRITICAL: Test assertion
>> failure function unigraf_close_device, file ../lib/unigraf/unigraf.c:97:
>> (unigraf_connectivity:5625) unigraf/unigraf-CRITICAL: Failed
>> assertion: false
>> (unigraf_connectivity:5625) unigraf/unigraf-CRITICAL: Last errno: 22,
>> Invalid argument
>> (unigraf_connectivity:5625) unigraf/unigraf-CRITICAL: unigraf error:
>> -50 (Invalid device handle.)
>> Stack trace:
>> #0 ../lib/igt_core.c:2108 __igt_fail_assert()
>> #1 ../lib/unigraf/unigraf.c:97 unigraf_exit_handler()
>> #2 ../lib/igt_core.c:2994 igt_atexit_handler()
>> #3 ./stdlib/exit.c:119 __run_exit_handlers()
>> #4 [exit+0x1e]
>> #5 ../lib/igt_core.c:2419 igt_exit()
>> #6 [main+0x32]
>> #7 ../sysdeps/nptl/libc_start_call_main.h:74 __libc_start_call_main()
>> #8 ../csu/libc-start.c:128 __libc_start_main@@GLIBC_2.34()
>> #9 [_start+0x25]
>
> I saw it yesterday while checking the default_dectect_timeout values, it
> was not the case in the few first iterations of the series. I am
> currently looking to understand what happen.
>
> My current guess is that unigraf_exit_handler is maybe called twice,
> because the function itself works well in my tiny C programs.
I investigated, but I did not find a solution.
If I do this, it works fine:
[1] igt_main {
igt_subtest {
unigraf_open_device();
...
unigraf_close_device();
}
}
If I do this, test is working, but unigraf_close_device fails:
[2] igt_main {
igt_fixup {
unigraf_open_device();
}
igt_subtest {
...
}
igt_fixup { /* or keep the exit handler */
unigraf_close_device(); /* fails */
}
}
I think [1] is not an acceptable solution, espicially since the exit
handler will not be called if all test fails (that can be problematic:
the unigraf device sometimes need to restart it manually if you forgot
to close).
I don't understand why [2] doesn't work, is there an explaination
somewhere to understand how the whole igt_main/fixup/subtest works
internally (different thread? separate processes? which kind of cleanup
is done?)
Thanks,
Louis Chauvet
>>> +
>>> +static void unigraf_init(void)
>>> +{
>>> + int ret;
>>> +
>>> + unigraf_debug("Initialize unigraf...\n");
>>> + ret = TSI_Init(TSI_CURRENT_VERSION);
>>> + unigraf_assert(ret);
>>> + igt_install_exit_handler(unigraf_exit_handler);
>>> +}
>>> +
>>> +/**
>>> + * unigraf_device_count() - Return the number of scanned devices
>>> + *
>>> + * Must be called after a unigraf_rescan_devices().
>>> + */
>>> +static unsigned int unigraf_device_count(void)
>>> +{
>>> + return unigraf_assert(TSIX_DEV_GetDeviceCount());
>>> +}
>>> +
>>> +/**
>>> + * unigraf_open_device() - Search and open a device.
>>> + * @drm_fd: File descriptor of the currently used drm device
>>> + *
>>> + * Returns: true if a device was found and initialized, otherwise
>>> false.
>>> + *
>>> + * This function searches for a compatible device and opens it.
>>> + */
>>> +bool unigraf_open_device(int drm_fd)
>>> +{
>>> + TSI_RESULT r;
>>> + int device_count;
>>> + int chosen_device = 0;
>>> + int chosen_role = 0;
>>> + int chosen_input = 0;
>>> +
>>> + assert(igt_can_fail());
>>> +
>>> + if (unigraf_device)
>>> + return true;
>>> +
>>> + unigraf_init();
>>> +
>>> + unigraf_assert(TSIX_DEV_RescanDevices(0,
>>> TSI_DEVCAP_VIDEO_CAPTURE, 0));
>>> +
>>> + device_count = unigraf_device_count();
>>> + if (device_count < 1) {
>>> + unigraf_debug("No device found.\n");
>>> + return false;
>>> + }
>>> +
>>> + unigraf_device = TSIX_DEV_OpenDevice(chosen_device, &r);
>>> + unigraf_assert(r);
>>> + igt_assert(unigraf_device);
>>> + unigraf_debug("Successfully opened the unigraf device %d.\n",
>>> chosen_device);
>>> +
>>> + unigraf_assert(TSIX_DEV_SelectRole(unigraf_device, chosen_role));
>>> + unigraf_assert(TSIX_VIN_Select(unigraf_device, chosen_input));
>>> + unigraf_assert(TSIX_VIN_Enable(unigraf_device, chosen_input));
>>> +
>>> + return true;
>>> +}
>>> +
>>> +/**
>>> + * unigraf_require_device() - Search and open a device.
>>> + * @drm_fd: File descriptor of the currently used drm device
>>> + *
>>> + * This is a shorthand to reduce test boilerplate when a unigraf
>>> device must be present.
>>> + */
>>> +void unigraf_require_device(int drm_fd)
>>> +{
>>> + igt_require(unigraf_open_device(drm_fd));
>>> +}
>>> diff --git a/lib/unigraf/unigraf.h b/lib/unigraf/unigraf.h
>>> new file mode 100644
>>> index 000000000000..c08ce62894c5
>>> --- /dev/null
>>> +++ b/lib/unigraf/unigraf.h
>>> @@ -0,0 +1,43 @@
>>> +/* SPDX-License-Identifier: MIT */
>>> +/*
>>> + * Copyright © 2026 Google
>>> + *
>>> + * Authors:
>>> + * Louis Chauvet <louis.chauvet@bootlin.com>
>>> + */
>>> +
>>> +#ifndef UNIGRAF_H
>>> +#define UNIGRAF_H
>>> +
>>> +#include <stdbool.h>
>>> +#include <stdint.h>
>>> +
>>> +/**
>>> + * unigraf_assert: Helper macro to assert a TSI return value and
>>> retrieve a detailed error message.
>>> + * @result: libTSI return value to check
>>> + *
>>> + * This macro checks the return value of a libTSI function call. If
>>> the return value indicates an
>>> + * error, it retrieves a detailed error message and asserts with
>>> that message.
>>> + * If retrieving the error description fails, it asserts with a
>>> generic error message.
>>> + */
>>> +#define unigraf_assert(result) \
>>> +({ \
>>> + char msg[256]; \
>>> + TSI_RESULT __r = (result); \
>>> + if (__r < TSI_SUCCESS) { \
>>> + TSI_RESULT __r2 = TSI_MISC_GetErrorDescription(__r, msg,
>>> sizeof(msg)); \
>>> + if (__r2 < TSI_SUCCESS) \
>>> + igt_assert_f(false, \
>>> + "unigraf error: %d (get error description
>>> failed: %d)\n", \
>>> + __r, __r2); \
>>> + else \
>>> + igt_assert_f(false, "unigraf error: %d (%s)\n", __r,
>>> msg); \
>>> + } \
>>> + (__r); \
>>> +})
>>> +
>>> +bool unigraf_open_device(int drm_fd);
>>> +
>>> +void unigraf_require_device(int drm_fd);
>>> +
>>> +#endif // UNIGRAF_H
>>> diff --git a/meson.build b/meson.build
>>> index 57849648a377..127abbf62024 100644
>>> --- a/meson.build
>>> +++ b/meson.build
>>> @@ -166,6 +166,12 @@ libpci = dependency('libpci', required : true)
>>> libudev = dependency('libudev', required : true)
>>> glib = dependency('glib-2.0', required : true)
>>> +libtsi = cc.find_library('TSI', required : false)
>>> +
>>> +if libtsi.found()
>>> + config.set('HAVE_UNIGRAF', 1)
>>> +endif
>>> +
>>> xmlrpc = dependency('xmlrpc', required : false)
>>> xmlrpc_util = dependency('xmlrpc_util', required : false)
>>> xmlrpc_client = dependency('xmlrpc_client', required : false)
>>> @@ -290,6 +296,7 @@ amdgpudir = join_paths(libexecdir, 'amdgpu')
>>> msmdir = join_paths(libexecdir, 'msm')
>>> panfrostdir = join_paths(libexecdir, 'panfrost')
>>> panthordir = join_paths(libexecdir, 'panthor')
>>> +unigrafdir = join_paths(libexecdir, 'unigraf')
>>> v3ddir = join_paths(libexecdir, 'v3d')
>>> vc4dir = join_paths(libexecdir, 'vc4')
>>> vkmsdir = join_paths(libexecdir, 'vkms')
>>> @@ -372,6 +379,12 @@ if get_option('use_rpath')
>>> vmwgfx_rpathdir = join_paths(vmwgfx_rpathdir, '..')
>>> endforeach
>>> vmwgfx_rpathdir = join_paths(vmwgfx_rpathdir, libdir)
>>> +
>>> + unigraf_rpathdir = '$ORIGIN'
>>> + foreach p : unigrafdir.split('/')
>>> + unigraf_rpathdir = join_paths(unigraf_rpathdir, '..')
>>> + endforeach
>>> + unigraf_rpathdir = join_paths(unigraf_rpathdir, libdir)
>>> else
>>> bindir_rpathdir = ''
>>> libexecdir_rpathdir = ''
>>> @@ -383,6 +396,7 @@ else
>>> vc4_rpathdir = ''
>>> vkms_rpathdir = ''
>>> vmwgfx_rpathdir = ''
>>> + unigraf_rpathdir = ''
>>> endif
>>> build_testplan = get_option('testplan')
>>>
>
^ permalink raw reply [flat|nested] 83+ messages in thread
* Re: [PATCH i-g-t v9 20/49] lib/unigraf: Initial Unigraf support
2026-03-16 16:17 ` [PATCH i-g-t v9 20/49] lib/unigraf: Initial Unigraf support Louis Chauvet
2026-03-24 8:19 ` Naladala, Ramanaidu
@ 2026-03-24 9:58 ` Kamil Konieczny
1 sibling, 0 replies; 83+ messages in thread
From: Kamil Konieczny @ 2026-03-24 9:58 UTC (permalink / raw)
To: Louis Chauvet
Cc: igt-dev, thomas.petazzoni, luca.ceresoli, kory.maincent,
markyacoub, khaled.almahallawy
Hi Louis,
On 2026-03-16 at 17:17:41 +0100, Louis Chauvet wrote:
> This introduce the basic boilerplate to connect to a unigraf device.
>
> This integration currently only supports one device opened to simplify
> its usage and cleanup.
>
> The functions unigraf_open_device and unigraf_require_device will register
> a handler to do proper cleanup on IGT exit.
>
> Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> ---
> lib/meson.build | 10 +++++
> lib/unigraf/unigraf.c | 120 ++++++++++++++++++++++++++++++++++++++++++++++++++
> lib/unigraf/unigraf.h | 43 ++++++++++++++++++
> meson.build | 14 ++++++
> 4 files changed, 187 insertions(+)
>
> diff --git a/lib/meson.build b/lib/meson.build
> index cd03e8f634e4..268f3238cbfa 100644
> --- a/lib/meson.build
> +++ b/lib/meson.build
> @@ -151,6 +151,13 @@ lib_deps = [
> zlib
> ]
>
> +if libtsi.found()
> + lib_deps += libtsi
> + lib_sources += [
> + 'unigraf/unigraf.c'
> + ]
> +endif
> +
> if libdrm_nouveau.found()
> lib_deps += libdrm_nouveau
> lib_sources += [
> @@ -232,6 +239,9 @@ endif
> if chamelium.found()
> lib_deps += chamelium
> lib_sources += [ 'igt_chamelium.c', 'igt_chamelium_stream.c' ]
> +endif
> +
> +if chamelium.found() or libtsi.found()
> lib_sources += 'monitor_edids/monitor_edids_helper.c'
> endif
>
> diff --git a/lib/unigraf/unigraf.c b/lib/unigraf/unigraf.c
> new file mode 100644
> index 000000000000..df18ab178e16
> --- /dev/null
> +++ b/lib/unigraf/unigraf.c
> @@ -0,0 +1,120 @@
> +// SPDX-License-Identifier: MIT
> +/*
> + * Copyright © 2026 Google
> + *
> + * Authors:
> + * Louis Chauvet <louis.chauvet@bootlin.com>
> + */
> +
> +#include "igt_core.h"
> +#include <stdint.h>
Please include first system headers, then igt ones,
all in alphabetical order. Also separate them with
one empty line, so here it should look:
#include <stdint.h>
#include "igt_core.h"
> +
> +#include "unigraf.h"
> +#include "TSI.h"
> +#include "TSI_types.h"
Up to you if you prefer to keep all unigraf includes
in one section or mixed in igt ones, just keep it
consistent in all other files.
Few more nits below.
> +
> +#define unigraf_debug(fmt, ...) igt_debug("TSI:%p: " fmt, unigraf_device, ##__VA_ARGS__)
> +
> +static TSI_HANDLE unigraf_device;
> +static char *unigraf_default_edid;
> +static char *unigraf_connector_name;
> +
> +static void unigraf_close_device(void)
> +{
> + if (!unigraf_device)
> + return;
> +
> + unigraf_debug("Closing...\n");
> + unigraf_assert(TSIX_DEV_CloseDevice(unigraf_device));
> + TSI_Clean();
> + unigraf_device = NULL;
> + free(unigraf_default_edid);
> + free(unigraf_connector_name);
> +}
> +
> +/**
> + * unigraf_exit_handler - Handle the exit signal and clean up unigraf resources.
> + * @sig: The signal number received.
> + *
> + * This function is called when the program receives an exit signal. It ensures
> + * that all unigraf resources are properly cleaned up by calling unigraf_deinit
> + * for each open instance.
> + */
> +static void unigraf_exit_handler(int sig)
> +{
> + unigraf_close_device();
> +}
> +
> +static void unigraf_init(void)
> +{
> + int ret;
> +
> + unigraf_debug("Initialize unigraf...\n");
> + ret = TSI_Init(TSI_CURRENT_VERSION);
> + unigraf_assert(ret);
> + igt_install_exit_handler(unigraf_exit_handler);
> +}
> +
> +/**
> + * unigraf_device_count() - Return the number of scanned devices
> + *
> + * Must be called after a unigraf_rescan_devices().
> + */
> +static unsigned int unigraf_device_count(void)
> +{
> + return unigraf_assert(TSIX_DEV_GetDeviceCount());
> +}
> +
> +/**
> + * unigraf_open_device() - Search and open a device.
> + * @drm_fd: File descriptor of the currently used drm device
> + *
> + * Returns: true if a device was found and initialized, otherwise false.
> + *
> + * This function searches for a compatible device and opens it.
I see asserts in code, please document that behaviour.
> + */
> +bool unigraf_open_device(int drm_fd)
> +{
> + TSI_RESULT r;
> + int device_count;
> + int chosen_device = 0;
> + int chosen_role = 0;
> + int chosen_input = 0;
> +
> + assert(igt_can_fail());
> +
> + if (unigraf_device)
> + return true;
> +
> + unigraf_init();
> +
> + unigraf_assert(TSIX_DEV_RescanDevices(0, TSI_DEVCAP_VIDEO_CAPTURE, 0));
> +
> + device_count = unigraf_device_count();
> + if (device_count < 1) {
> + unigraf_debug("No device found.\n");
> + return false;
> + }
> +
> + unigraf_device = TSIX_DEV_OpenDevice(chosen_device, &r);
> + unigraf_assert(r);
> + igt_assert(unigraf_device);
> + unigraf_debug("Successfully opened the unigraf device %d.\n", chosen_device);
> +
> + unigraf_assert(TSIX_DEV_SelectRole(unigraf_device, chosen_role));
> + unigraf_assert(TSIX_VIN_Select(unigraf_device, chosen_input));
> + unigraf_assert(TSIX_VIN_Enable(unigraf_device, chosen_input));
> +
> + return true;
> +}
> +
> +/**
> + * unigraf_require_device() - Search and open a device.
> + * @drm_fd: File descriptor of the currently used drm device
> + *
> + * This is a shorthand to reduce test boilerplate when a unigraf device must be present.
> + */
> +void unigraf_require_device(int drm_fd)
> +{
> + igt_require(unigraf_open_device(drm_fd));
> +}
> diff --git a/lib/unigraf/unigraf.h b/lib/unigraf/unigraf.h
> new file mode 100644
> index 000000000000..c08ce62894c5
> --- /dev/null
> +++ b/lib/unigraf/unigraf.h
> @@ -0,0 +1,43 @@
> +/* SPDX-License-Identifier: MIT */
> +/*
> + * Copyright © 2026 Google
> + *
> + * Authors:
> + * Louis Chauvet <louis.chauvet@bootlin.com>
> + */
> +
> +#ifndef UNIGRAF_H
> +#define UNIGRAF_H
> +
> +#include <stdbool.h>
> +#include <stdint.h>
> +
> +/**
> + * unigraf_assert: Helper macro to assert a TSI return value and retrieve a detailed error message.
> + * @result: libTSI return value to check
> + *
> + * This macro checks the return value of a libTSI function call. If the return value indicates an
> + * error, it retrieves a detailed error message and asserts with that message.
> + * If retrieving the error description fails, it asserts with a generic error message.
> + */
> +#define unigraf_assert(result) \
> +({ \
> + char msg[256]; \
> + TSI_RESULT __r = (result); \
> + if (__r < TSI_SUCCESS) { \
> + TSI_RESULT __r2 = TSI_MISC_GetErrorDescription(__r, msg, sizeof(msg)); \
> + if (__r2 < TSI_SUCCESS) \
> + igt_assert_f(false, \
> + "unigraf error: %d (get error description failed: %d)\n", \
> + __r, __r2); \
> + else \
> + igt_assert_f(false, "unigraf error: %d (%s)\n", __r, msg); \
> + } \
> + (__r); \
> +})
> +
> +bool unigraf_open_device(int drm_fd);
> +
> +void unigraf_require_device(int drm_fd);
> +
> +#endif // UNIGRAF_H
> diff --git a/meson.build b/meson.build
> index 57849648a377..127abbf62024 100644
> --- a/meson.build
> +++ b/meson.build
> @@ -166,6 +166,12 @@ libpci = dependency('libpci', required : true)
> libudev = dependency('libudev', required : true)
> glib = dependency('glib-2.0', required : true)
>
> +libtsi = cc.find_library('TSI', required : false)
> +
> +if libtsi.found()
> + config.set('HAVE_UNIGRAF', 1)
> +endif
> +
> xmlrpc = dependency('xmlrpc', required : false)
> xmlrpc_util = dependency('xmlrpc_util', required : false)
> xmlrpc_client = dependency('xmlrpc_client', required : false)
> @@ -290,6 +296,7 @@ amdgpudir = join_paths(libexecdir, 'amdgpu')
> msmdir = join_paths(libexecdir, 'msm')
> panfrostdir = join_paths(libexecdir, 'panfrost')
> panthordir = join_paths(libexecdir, 'panthor')
> +unigrafdir = join_paths(libexecdir, 'unigraf')
> v3ddir = join_paths(libexecdir, 'v3d')
> vc4dir = join_paths(libexecdir, 'vc4')
> vkmsdir = join_paths(libexecdir, 'vkms')
> @@ -372,6 +379,12 @@ if get_option('use_rpath')
> vmwgfx_rpathdir = join_paths(vmwgfx_rpathdir, '..')
> endforeach
> vmwgfx_rpathdir = join_paths(vmwgfx_rpathdir, libdir)
> +
> + unigraf_rpathdir = '$ORIGIN'
> + foreach p : unigrafdir.split('/')
> + unigraf_rpathdir = join_paths(unigraf_rpathdir, '..')
> + endforeach
> + unigraf_rpathdir = join_paths(unigraf_rpathdir, libdir)
> else
> bindir_rpathdir = ''
> libexecdir_rpathdir = ''
> @@ -383,6 +396,7 @@ else
> vc4_rpathdir = ''
> vkms_rpathdir = ''
> vmwgfx_rpathdir = ''
> + unigraf_rpathdir = ''
Move this to proper alphabetical place before v3d_rpathdir.
Regards,
Kamil
> endif
>
> build_testplan = get_option('testplan')
>
> --
> 2.52.0
>
^ permalink raw reply [flat|nested] 83+ messages in thread
* [PATCH i-g-t v9 21/49] lib/igt_kms: Automatically connect unigraf on display require
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (19 preceding siblings ...)
2026-03-16 16:17 ` [PATCH i-g-t v9 20/49] lib/unigraf: Initial Unigraf support Louis Chauvet
@ 2026-03-16 16:17 ` Louis Chauvet
2026-03-16 16:17 ` [PATCH i-g-t v9 22/49] lib/unigraf: Introduce device configuration Louis Chauvet
` (28 subsequent siblings)
49 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:17 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
As Unigraf can emulate a display, automatically connect it for all
devices. If a test is already using unigraf, this have no effect.
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/igt_kms.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/lib/igt_kms.c b/lib/igt_kms.c
index f6ddf25756f1..064d5b35aa54 100644
--- a/lib/igt_kms.c
+++ b/lib/igt_kms.c
@@ -66,6 +66,9 @@
#ifdef HAVE_CHAMELIUM
#include "igt_chamelium.h"
#endif
+#ifdef HAVE_UNIGRAF
+#include "unigraf/unigraf.h"
+#endif
/**
* SECTION:igt_kms
@@ -3123,6 +3126,10 @@ void igt_display_require(igt_display_t *display, int drm_fd)
int i, crtc_index;
bool is_intel_dev;
+#ifdef HAVE_UNIGRAF
+ unigraf_open_device(drm_fd);
+#endif
+
memset(display, 0, sizeof(igt_display_t));
LOG_INDENT(display, "init");
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* [PATCH i-g-t v9 22/49] lib/unigraf: Introduce device configuration
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (20 preceding siblings ...)
2026-03-16 16:17 ` [PATCH i-g-t v9 21/49] lib/igt_kms: Automatically connect unigraf on display require Louis Chauvet
@ 2026-03-16 16:17 ` Louis Chauvet
2026-03-17 15:16 ` Kory Maincent
2026-03-16 16:17 ` [PATCH i-g-t v9 23/49] lib/unigraf: Introduce role configuration Louis Chauvet
` (27 subsequent siblings)
49 siblings, 1 reply; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:17 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
As there could be multiple devices detected by libTSI, add a configuration
field to ensure that igt will use the proper unigraf device.
The unigraf integration will search for a [Unigraf] entry containing a
Device=
For example, it can look like:
[Unigraf]
Device=UCD-500 [2434C620]
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
lib/unigraf/unigraf.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 50 insertions(+)
diff --git a/lib/unigraf/unigraf.c b/lib/unigraf/unigraf.c
index df18ab178e16..9d5f0a488bed 100644
--- a/lib/unigraf/unigraf.c
+++ b/lib/unigraf/unigraf.c
@@ -12,6 +12,7 @@
#include "unigraf.h"
#include "TSI.h"
#include "TSI_types.h"
+#include "igt_rc.h"
#define unigraf_debug(fmt, ...) igt_debug("TSI:%p: " fmt, unigraf_device, ##__VA_ARGS__)
@@ -19,6 +20,21 @@ static TSI_HANDLE unigraf_device;
static char *unigraf_default_edid;
static char *unigraf_connector_name;
+/**
+ * UNIGRAF_NAME_MAX - Maximum name length to be used for TSI functions
+ */
+#define UNIGRAF_NAME_MAX 1024
+
+/**
+ * UNIGRAF_CONFIG_GROUP - Name of the unigraf group in the configuration file
+ */
+#define UNIGRAF_CONFIG_GROUP "Unigraf"
+
+/**
+ * UNIGRAF_CONFIG_DEVICE_NAME - Key of the device name in the configuration file
+ */
+#define UNIGRAF_CONFIG_DEVICE_NAME "Device"
+
static void unigraf_close_device(void)
{
if (!unigraf_device)
@@ -65,6 +81,21 @@ static unsigned int unigraf_device_count(void)
return unigraf_assert(TSIX_DEV_GetDeviceCount());
}
+static int unigraf_find_device(char *request)
+{
+ int device_count = unigraf_device_count();
+
+ for (int i = 0; i < device_count; i++) {
+ char dev_name[UNIGRAF_NAME_MAX] = "";
+
+ unigraf_assert(TSIX_DEV_GetDeviceName(i, dev_name, UNIGRAF_NAME_MAX));
+ unigraf_debug("Detected unigraf device %d: %s\n", i, dev_name);
+ if (!strncmp(dev_name, request, UNIGRAF_NAME_MAX))
+ return i;
+ }
+ return -ENODEV;
+}
+
/**
* unigraf_open_device() - Search and open a device.
* @drm_fd: File descriptor of the currently used drm device
@@ -76,6 +107,8 @@ static unsigned int unigraf_device_count(void)
bool unigraf_open_device(int drm_fd)
{
TSI_RESULT r;
+ GError *cfg_error = NULL;
+ char *cfg_device = NULL;
int device_count;
int chosen_device = 0;
int chosen_role = 0;
@@ -88,6 +121,15 @@ bool unigraf_open_device(int drm_fd)
unigraf_init();
+ if (igt_key_file) {
+ cfg_device = g_key_file_get_string(igt_key_file, UNIGRAF_CONFIG_GROUP,
+ UNIGRAF_CONFIG_DEVICE_NAME, &cfg_error);
+ if (cfg_error) {
+ unigraf_debug("No device name configured, uses first device available.\n");
+ cfg_device = NULL;
+ }
+ }
+
unigraf_assert(TSIX_DEV_RescanDevices(0, TSI_DEVCAP_VIDEO_CAPTURE, 0));
device_count = unigraf_device_count();
@@ -96,6 +138,14 @@ bool unigraf_open_device(int drm_fd)
return false;
}
+ if (cfg_device) {
+ chosen_device = unigraf_find_device(cfg_device);
+ if (chosen_device < 0) {
+ igt_warn("The requested unigraf device %s is not found, err: %d.\n", cfg_device, chosen_device);
+ return false;
+ }
+ }
+
unigraf_device = TSIX_DEV_OpenDevice(chosen_device, &r);
unigraf_assert(r);
igt_assert(unigraf_device);
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* Re: [PATCH i-g-t v9 22/49] lib/unigraf: Introduce device configuration
2026-03-16 16:17 ` [PATCH i-g-t v9 22/49] lib/unigraf: Introduce device configuration Louis Chauvet
@ 2026-03-17 15:16 ` Kory Maincent
0 siblings, 0 replies; 83+ messages in thread
From: Kory Maincent @ 2026-03-17 15:16 UTC (permalink / raw)
To: Louis Chauvet
Cc: igt-dev, thomas.petazzoni, luca.ceresoli, markyacoub,
khaled.almahallawy
On Mon, 16 Mar 2026 17:17:43 +0100
Louis Chauvet <louis.chauvet@bootlin.com> wrote:
> As there could be multiple devices detected by libTSI, add a configuration
> field to ensure that igt will use the proper unigraf device.
>
> The unigraf integration will search for a [Unigraf] entry containing a
> Device=
>
> For example, it can look like:
>
> [Unigraf]
> Device=UCD-500 [2434C620]
>
> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
Thank you!
--
Köry Maincent, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 83+ messages in thread
* [PATCH i-g-t v9 23/49] lib/unigraf: Introduce role configuration
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (21 preceding siblings ...)
2026-03-16 16:17 ` [PATCH i-g-t v9 22/49] lib/unigraf: Introduce device configuration Louis Chauvet
@ 2026-03-16 16:17 ` Louis Chauvet
2026-03-17 15:19 ` Kory Maincent
2026-03-16 16:17 ` [PATCH i-g-t v9 24/49] lib/unigraf: Introduce input configuration Louis Chauvet
` (26 subsequent siblings)
49 siblings, 1 reply; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:17 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
Some unigraf devices, like the UCD-500 can have multiple hardware
configurations: displayport input/output, usbc input/output.
The device configuration can look like:
[Unigraf]
Device=UCD-500 [2434C620]
Role=DisplayPort Source and Sink
Reviewed-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
lib/unigraf/unigraf.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 56 insertions(+), 1 deletion(-)
diff --git a/lib/unigraf/unigraf.c b/lib/unigraf/unigraf.c
index 9d5f0a488bed..cf760357c6be 100644
--- a/lib/unigraf/unigraf.c
+++ b/lib/unigraf/unigraf.c
@@ -7,6 +7,7 @@
*/
#include "igt_core.h"
+#include <asm-generic/errno-base.h>
#include <stdint.h>
#include "unigraf.h"
@@ -35,6 +36,16 @@ static char *unigraf_connector_name;
*/
#define UNIGRAF_CONFIG_DEVICE_NAME "Device"
+/**
+ * UNIGRAF_CONFIG_DEVICE_ROLE - Key of the device role in the configuration file
+ */
+#define UNIGRAF_CONFIG_DEVICE_ROLE "Role"
+
+/**
+ * UNIGRAF_DEFAULT_ROLE_NAME - Default role name to search on the unigraf device
+ */
+#define UNIGRAF_DEFAULT_ROLE_NAME "USB-C, DP Alt Mode Source and Sink"
+
static void unigraf_close_device(void)
{
if (!unigraf_device)
@@ -96,6 +107,23 @@ static int unigraf_find_device(char *request)
return -ENODEV;
}
+static int unigraf_find_role(const char *request)
+{
+ int role_count = unigraf_assert(TSIX_DEV_GetDeviceRoleCount(unigraf_device));
+
+ for (int i = 0; i < role_count; i++) {
+ char role_name[UNIGRAF_NAME_MAX] = "";
+
+ unigraf_assert(TSIX_DEV_GetDeviceRoleName(unigraf_device, i,
+ role_name,
+ UNIGRAF_NAME_MAX));
+ unigraf_debug("Role %d: %s\n", i, role_name);
+ if (!strncmp(role_name, request, UNIGRAF_NAME_MAX))
+ return i;
+ }
+ return -ENODEV;
+}
+
/**
* unigraf_open_device() - Search and open a device.
* @drm_fd: File descriptor of the currently used drm device
@@ -109,9 +137,10 @@ bool unigraf_open_device(int drm_fd)
TSI_RESULT r;
GError *cfg_error = NULL;
char *cfg_device = NULL;
+ char *cfg_role = NULL;
int device_count;
int chosen_device = 0;
- int chosen_role = 0;
+ int chosen_role;
int chosen_input = 0;
assert(igt_can_fail());
@@ -128,6 +157,14 @@ bool unigraf_open_device(int drm_fd)
unigraf_debug("No device name configured, uses first device available.\n");
cfg_device = NULL;
}
+
+ cfg_error = NULL;
+ cfg_role = g_key_file_get_string(igt_key_file, UNIGRAF_CONFIG_GROUP,
+ UNIGRAF_CONFIG_DEVICE_ROLE, &cfg_error);
+ if (cfg_error) {
+ unigraf_debug("No device role configured.\n");
+ cfg_role = NULL;
+ }
}
unigraf_assert(TSIX_DEV_RescanDevices(0, TSI_DEVCAP_VIDEO_CAPTURE, 0));
@@ -151,6 +188,24 @@ bool unigraf_open_device(int drm_fd)
igt_assert(unigraf_device);
unigraf_debug("Successfully opened the unigraf device %d.\n", chosen_device);
+ if (!cfg_role) {
+ unigraf_debug("No role configured, trying " UNIGRAF_DEFAULT_ROLE_NAME "\n");
+ chosen_role = unigraf_find_role(UNIGRAF_DEFAULT_ROLE_NAME);
+ if (chosen_role < 0) {
+ char role_name[UNIGRAF_NAME_MAX];
+
+ chosen_role = 0;
+ unigraf_assert(TSIX_DEV_GetDeviceRoleName(unigraf_device, chosen_role,
+ role_name, UNIGRAF_NAME_MAX));
+ unigraf_debug("Role " UNIGRAF_DEFAULT_ROLE_NAME " not found, using role 0 (%s)\n",
+ role_name);
+ }
+ } else {
+ chosen_role = unigraf_find_role(cfg_role);
+ igt_assert_f(chosen_role >= 0, "TSI:%p: Role %s not found.",
+ unigraf_device, cfg_role);
+ }
+
unigraf_assert(TSIX_DEV_SelectRole(unigraf_device, chosen_role));
unigraf_assert(TSIX_VIN_Select(unigraf_device, chosen_input));
unigraf_assert(TSIX_VIN_Enable(unigraf_device, chosen_input));
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* Re: [PATCH i-g-t v9 23/49] lib/unigraf: Introduce role configuration
2026-03-16 16:17 ` [PATCH i-g-t v9 23/49] lib/unigraf: Introduce role configuration Louis Chauvet
@ 2026-03-17 15:19 ` Kory Maincent
0 siblings, 0 replies; 83+ messages in thread
From: Kory Maincent @ 2026-03-17 15:19 UTC (permalink / raw)
To: Louis Chauvet
Cc: igt-dev, thomas.petazzoni, luca.ceresoli, markyacoub,
khaled.almahallawy
On Mon, 16 Mar 2026 17:17:44 +0100
Louis Chauvet <louis.chauvet@bootlin.com> wrote:
> Some unigraf devices, like the UCD-500 can have multiple hardware
> configurations: displayport input/output, usbc input/output.
>
> The device configuration can look like:
>
> [Unigraf]
> Device=UCD-500 [2434C620]
> Role=DisplayPort Source and Sink
>
> Reviewed-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
Thank you!
--
Köry Maincent, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 83+ messages in thread
* [PATCH i-g-t v9 24/49] lib/unigraf: Introduce input configuration
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (22 preceding siblings ...)
2026-03-16 16:17 ` [PATCH i-g-t v9 23/49] lib/unigraf: Introduce role configuration Louis Chauvet
@ 2026-03-16 16:17 ` Louis Chauvet
2026-03-16 16:17 ` [PATCH i-g-t v9 25/49] lib/unigraf: Add reset function Louis Chauvet
` (25 subsequent siblings)
49 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:17 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
As multiple input can be present on unigraf devices, add a configuration
field to select the proper input. By default it will take the first
available.
The configuration can look like:
[Unigraf]
Device=UCD-500 [2434C620]
Role=DisplayPort Source and Sink
Input=DP RX
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/unigraf/unigraf.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 55 insertions(+), 1 deletion(-)
diff --git a/lib/unigraf/unigraf.c b/lib/unigraf/unigraf.c
index cf760357c6be..43179d5fb600 100644
--- a/lib/unigraf/unigraf.c
+++ b/lib/unigraf/unigraf.c
@@ -41,11 +41,21 @@ static char *unigraf_connector_name;
*/
#define UNIGRAF_CONFIG_DEVICE_ROLE "Role"
+/**
+ * UNIGRAF_CONFIG_INPUT_NAME - Key of the input name in the configuration file
+ */
+#define UNIGRAF_CONFIG_INPUT_NAME "Input"
+
/**
* UNIGRAF_DEFAULT_ROLE_NAME - Default role name to search on the unigraf device
*/
#define UNIGRAF_DEFAULT_ROLE_NAME "USB-C, DP Alt Mode Source and Sink"
+/**
+ * UNIGRAF_DEFAULT_INPUT_NAME - Default input name to search on the unigraf device
+ */
+#define UNIGRAF_DEFAULT_INPUT_NAME "DP RX"
+
static void unigraf_close_device(void)
{
if (!unigraf_device)
@@ -124,6 +134,22 @@ static int unigraf_find_role(const char *request)
return -ENODEV;
}
+static int unigraf_find_input(const char *request)
+{
+ int role_count = unigraf_assert(TSIX_VIN_GetInputCount(unigraf_device));
+
+ for (int i = 0; i < role_count; i++) {
+ char input_name[UNIGRAF_NAME_MAX] = "";
+
+ unigraf_assert(TSIX_VIN_GetInputName(unigraf_device, i,
+ input_name, UNIGRAF_NAME_MAX));
+ unigraf_debug("Input %d: %s\n", i, input_name);
+ if (!strncmp(input_name, request, UNIGRAF_NAME_MAX))
+ return i;
+ }
+ return -ENODEV;
+}
+
/**
* unigraf_open_device() - Search and open a device.
* @drm_fd: File descriptor of the currently used drm device
@@ -138,10 +164,11 @@ bool unigraf_open_device(int drm_fd)
GError *cfg_error = NULL;
char *cfg_device = NULL;
char *cfg_role = NULL;
+ char *cfg_input = NULL;
int device_count;
int chosen_device = 0;
int chosen_role;
- int chosen_input = 0;
+ int chosen_input;
assert(igt_can_fail());
@@ -165,6 +192,14 @@ bool unigraf_open_device(int drm_fd)
unigraf_debug("No device role configured.\n");
cfg_role = NULL;
}
+
+ cfg_error = NULL;
+ cfg_input = g_key_file_get_string(igt_key_file, UNIGRAF_CONFIG_GROUP,
+ UNIGRAF_CONFIG_INPUT_NAME, &cfg_error);
+ if (cfg_error) {
+ unigraf_debug("No input name configured.\n");
+ cfg_input = NULL;
+ }
}
unigraf_assert(TSIX_DEV_RescanDevices(0, TSI_DEVCAP_VIDEO_CAPTURE, 0));
@@ -207,6 +242,25 @@ bool unigraf_open_device(int drm_fd)
}
unigraf_assert(TSIX_DEV_SelectRole(unigraf_device, chosen_role));
+
+ if (!cfg_input) {
+ unigraf_debug("No input configured, trying " UNIGRAF_DEFAULT_INPUT_NAME "\n");
+ chosen_input = unigraf_find_input(UNIGRAF_DEFAULT_INPUT_NAME);
+ if (chosen_input < 0) {
+ char input_name[UNIGRAF_NAME_MAX];
+
+ chosen_input = 0;
+ unigraf_assert(TSIX_VIN_GetInputName(unigraf_device, chosen_input,
+ input_name, UNIGRAF_NAME_MAX));
+ unigraf_debug("Input " UNIGRAF_DEFAULT_INPUT_NAME " not found, using input 0 (%s).\n",
+ input_name);
+ }
+ } else {
+ chosen_input = unigraf_find_input(cfg_input);
+ igt_assert_f(chosen_input >= 0, "TSI:%p: Input %s not found.",
+ unigraf_device, cfg_input);
+ }
+
unigraf_assert(TSIX_VIN_Select(unigraf_device, chosen_input));
unigraf_assert(TSIX_VIN_Enable(unigraf_device, chosen_input));
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* [PATCH i-g-t v9 25/49] lib/unigraf: Add reset function
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (23 preceding siblings ...)
2026-03-16 16:17 ` [PATCH i-g-t v9 24/49] lib/unigraf: Introduce input configuration Louis Chauvet
@ 2026-03-16 16:17 ` Louis Chauvet
2026-03-16 16:17 ` [PATCH i-g-t v9 26/49] lib/unigraf: Add unigraf assert and deassert helpers Louis Chauvet
` (24 subsequent siblings)
49 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:17 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
To have a consistent state at the beginning of tests, add a unigraf_reset
function that will restore a known state.
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/unigraf/unigraf.c | 12 ++++++++++++
lib/unigraf/unigraf.h | 2 ++
2 files changed, 14 insertions(+)
diff --git a/lib/unigraf/unigraf.c b/lib/unigraf/unigraf.c
index 43179d5fb600..9cfff8254009 100644
--- a/lib/unigraf/unigraf.c
+++ b/lib/unigraf/unigraf.c
@@ -264,6 +264,8 @@ bool unigraf_open_device(int drm_fd)
unigraf_assert(TSIX_VIN_Select(unigraf_device, chosen_input));
unigraf_assert(TSIX_VIN_Enable(unigraf_device, chosen_input));
+ unigraf_reset();
+
return true;
}
@@ -277,3 +279,13 @@ void unigraf_require_device(int drm_fd)
{
igt_require(unigraf_open_device(drm_fd));
}
+
+/**
+ * unigraf_reset() - Reset the Unigraf device
+ *
+ * This function performs a hardware reset of the Unigraf device, restoring it to a
+ * default state. This includes resetting all configuration parameters, stream settings,
+ * and link parameters to default values.
+ */
+void unigraf_reset(void)
+{}
diff --git a/lib/unigraf/unigraf.h b/lib/unigraf/unigraf.h
index c08ce62894c5..4c2cbd6b1aa5 100644
--- a/lib/unigraf/unigraf.h
+++ b/lib/unigraf/unigraf.h
@@ -40,4 +40,6 @@ bool unigraf_open_device(int drm_fd);
void unigraf_require_device(int drm_fd);
+void unigraf_reset(void);
+
#endif // UNIGRAF_H
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* [PATCH i-g-t v9 26/49] lib/unigraf: Add unigraf assert and deassert helpers
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (24 preceding siblings ...)
2026-03-16 16:17 ` [PATCH i-g-t v9 25/49] lib/unigraf: Add reset function Louis Chauvet
@ 2026-03-16 16:17 ` Louis Chauvet
2026-03-16 16:17 ` [PATCH i-g-t v9 27/49] lib/unigraf: Add plug/unplug helpers Louis Chauvet
` (23 subsequent siblings)
49 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:17 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
Unigraf is able to manipulate the HPD line of the connector, add few
helpers to manipulate them.
Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
lib/unigraf/unigraf.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++
lib/unigraf/unigraf.h | 6 ++++++
2 files changed, 59 insertions(+)
diff --git a/lib/unigraf/unigraf.c b/lib/unigraf/unigraf.c
index 9cfff8254009..34f3e83e4358 100644
--- a/lib/unigraf/unigraf.c
+++ b/lib/unigraf/unigraf.c
@@ -92,6 +92,22 @@ static void unigraf_init(void)
igt_install_exit_handler(unigraf_exit_handler);
}
+/**
+ * unigraf_write_u32 - Write a 32-bit value to a TSI configuration item
+ * @config_id: The configuration item ID to write to
+ * @value: The 32-bit value to write
+ *
+ * This macro writes a 32-bit value to the specified TSI configuration item.
+ * This is a macro to have the proper line information when using unigraf_debug.
+ */
+#define unigraf_write_u32(config_id, value) \
+ ({ \
+ uint32_t v = (value); \
+ igt_assert(unigraf_device); \
+ unigraf_debug("Value write: " #config_id "=%d...\n", v); \
+ unigraf_assert(TSIX_TS_SetConfigItem(unigraf_device, config_id, &v, sizeof(value))); \
+ })
+
/**
* unigraf_device_count() - Return the number of scanned devices
*
@@ -289,3 +305,40 @@ void unigraf_require_device(int drm_fd)
*/
void unigraf_reset(void)
{}
+
+/**
+ * unigraf_hpd_assert() - Assert Hot Plug Detect signal
+ *
+ * This function asserts the HPD signal, simulating a device connection.
+ */
+void unigraf_hpd_assert(void)
+{
+ unigraf_write_u32(TSI_FORCE_HOT_PLUG_STATE_W, 1);
+}
+
+/**
+ * unigraf_hpd_pulse() - Pulse the Hot Plug Detect signal
+ * @duration: The duration in milliseconds for which the HPD signal should be pulsed
+ *
+ * This function pulses the HPD signal for the specified duration.
+ */
+void unigraf_hpd_pulse(int duration)
+{
+ /* In theory this should work:
+ * unigraf_write_u32(TSI_DPRX_HPD_PULSE_W, duration);
+ * But this seems to be broken and this works:
+ */
+ unigraf_hpd_deassert();
+ usleep(duration);
+ unigraf_hpd_assert();
+}
+
+/**
+ * unigraf_hpd_deassert() - Deassert Hot Plug Detect signal
+ *
+ * This function deasserts the HPD signal, simulating a device disconnection.
+ */
+void unigraf_hpd_deassert(void)
+{
+ unigraf_write_u32(TSI_FORCE_HOT_PLUG_STATE_W, 0);
+}
diff --git a/lib/unigraf/unigraf.h b/lib/unigraf/unigraf.h
index 4c2cbd6b1aa5..77fba4e53912 100644
--- a/lib/unigraf/unigraf.h
+++ b/lib/unigraf/unigraf.h
@@ -42,4 +42,10 @@ void unigraf_require_device(int drm_fd);
void unigraf_reset(void);
+void unigraf_hpd_deassert(void);
+
+void unigraf_hpd_pulse(int duration);
+
+void unigraf_hpd_assert(void);
+
#endif // UNIGRAF_H
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* [PATCH i-g-t v9 27/49] lib/unigraf: Add plug/unplug helpers
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (25 preceding siblings ...)
2026-03-16 16:17 ` [PATCH i-g-t v9 26/49] lib/unigraf: Add unigraf assert and deassert helpers Louis Chauvet
@ 2026-03-16 16:17 ` Louis Chauvet
2026-03-16 16:17 ` [PATCH i-g-t v9 28/49] lib/unigraf: Allows sst/mst configuration Louis Chauvet
` (22 subsequent siblings)
49 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:17 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
The unigraf can emulate a full cable plug/unplug, so add helper to emulate
this.
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/unigraf/unigraf.c | 28 +++++++++++++++++++++++++++-
lib/unigraf/unigraf.h | 4 ++++
2 files changed, 31 insertions(+), 1 deletion(-)
diff --git a/lib/unigraf/unigraf.c b/lib/unigraf/unigraf.c
index 34f3e83e4358..29283b7751b0 100644
--- a/lib/unigraf/unigraf.c
+++ b/lib/unigraf/unigraf.c
@@ -304,7 +304,9 @@ void unigraf_require_device(int drm_fd)
* and link parameters to default values.
*/
void unigraf_reset(void)
-{}
+{
+ unigraf_plug();
+}
/**
* unigraf_hpd_assert() - Assert Hot Plug Detect signal
@@ -342,3 +344,27 @@ void unigraf_hpd_deassert(void)
{
unigraf_write_u32(TSI_FORCE_HOT_PLUG_STATE_W, 0);
}
+
+/**
+ * unigraf_plug() - Emulate a cable unplug
+ *
+ * This function will emulate a full cable unplug (not a simple HPD line change)
+ */
+void unigraf_unplug(void)
+{
+ int d = 2 << 2;
+
+ unigraf_write_u32(TSI_DPRX_HPD_FORCE, d);
+}
+
+/**
+ * unigraf_plug() - Emulate a cable plug
+ *
+ * This function will emulate a full cable plug (not a simple HPD line change)
+ */
+void unigraf_plug(void)
+{
+ int d = 3 << 2;
+
+ unigraf_write_u32(TSI_DPRX_HPD_FORCE, d);
+}
diff --git a/lib/unigraf/unigraf.h b/lib/unigraf/unigraf.h
index 77fba4e53912..ae1f0d8955ab 100644
--- a/lib/unigraf/unigraf.h
+++ b/lib/unigraf/unigraf.h
@@ -48,4 +48,8 @@ void unigraf_hpd_pulse(int duration);
void unigraf_hpd_assert(void);
+void unigraf_plug(void);
+
+void unigraf_unplug(void);
+
#endif // UNIGRAF_H
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* [PATCH i-g-t v9 28/49] lib/unigraf: Allows sst/mst configuration
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (26 preceding siblings ...)
2026-03-16 16:17 ` [PATCH i-g-t v9 27/49] lib/unigraf: Add plug/unplug helpers Louis Chauvet
@ 2026-03-16 16:17 ` Louis Chauvet
2026-03-16 16:17 ` [PATCH i-g-t v9 29/49] lib/unigraf: Add helpers to read and write edid Louis Chauvet
` (21 subsequent siblings)
49 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:17 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
Unigraf devices can emulate both SST and MST display port. In order to
write tests using both, add fews helper to manipulate them.
The define TSI_DPRX_NOT_DOCUMENTED_SIDEBAND_MSG_SUPPORT is taken from the
python SDK, as it was not defined by the C sdk nor documented.
Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
lib/unigraf/unigraf.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++
lib/unigraf/unigraf.h | 12 ++++++
2 files changed, 123 insertions(+)
diff --git a/lib/unigraf/unigraf.c b/lib/unigraf/unigraf.c
index 29283b7751b0..b752ff573ace 100644
--- a/lib/unigraf/unigraf.c
+++ b/lib/unigraf/unigraf.c
@@ -108,6 +108,25 @@ static void unigraf_init(void)
unigraf_assert(TSIX_TS_SetConfigItem(unigraf_device, config_id, &v, sizeof(value))); \
})
+/**
+ * unigraf_read_u32 - Read a 32-bit value from a TSI configuration item with error handling
+ * @config_id: The configuration item ID to read from
+ *
+ * This macro reads a 32-bit value from the specified TSI configuration item.
+ * It includes error handling and debug output. This is a macro to have the proper
+ * line information when using unigraf_debug.
+ *
+ * Returns: The 32-bit value read from the configuration item
+ */
+#define unigraf_read_u32(config_id) \
+ ({ \
+ uint32_t value; \
+ igt_assert(unigraf_device); \
+ unigraf_assert(TSIX_TS_GetConfigItem(unigraf_device, config_id, &value, sizeof(value))); \
+ unigraf_debug("Value read: " #config_id "=%d\n", value); \
+ value; \
+ })
+
/**
* unigraf_device_count() - Return the number of scanned devices
*
@@ -306,6 +325,8 @@ void unigraf_require_device(int drm_fd)
void unigraf_reset(void)
{
unigraf_plug();
+ unigraf_set_mst_stream_count(1);
+ unigraf_set_sst();
}
/**
@@ -368,3 +389,93 @@ void unigraf_plug(void)
unigraf_write_u32(TSI_DPRX_HPD_FORCE, d);
}
+
+/**
+ * unigraf_set_sst() - Configure the device for Single Stream Transport mode
+ *
+ * This function sets the device to operate in Single Stream Transport (SST) mode.
+ */
+void unigraf_set_sst(void)
+{
+ int link_flags = 0xFFFFFFFF;
+
+ unigraf_assert(TSIX_TS_GetConfigItem(unigraf_device, TSI_DPRX_LINK_FLAGS,
+ &link_flags, sizeof(link_flags)));
+ link_flags &= ~(TSI_DPRX_LINK_FLAGS_MST | TSI_DPRX_NOT_DOCUMENTED_SIDEBAND_MSG_SUPPORT);
+ unigraf_write_u32(TSI_DPRX_LINK_FLAGS, link_flags);
+}
+
+/**
+ * unigraf_set_mst() - Configure the device for Multi Stream Transport mode
+ *
+ * This function sets the device to operate in Multi Stream Transport (MST) mode.
+ */
+void unigraf_set_mst(void)
+{
+ int link_flags = 0xFFFFFFFF;
+
+ unigraf_assert(TSIX_TS_GetConfigItem(unigraf_device, TSI_DPRX_LINK_FLAGS,
+ &link_flags, sizeof(link_flags)));
+ link_flags |= TSI_DPRX_LINK_FLAGS_MST | TSI_DPRX_NOT_DOCUMENTED_SIDEBAND_MSG_SUPPORT;
+ unigraf_write_u32(TSI_DPRX_LINK_FLAGS, link_flags);
+}
+
+/**
+ * unigraf_get_mst_stream_max_count() - Get the maximum number of stream count accepted by the
+ * device
+ * Caution: This function can be destructive to some configuration: the only way to get the
+ * information is to try and read the new value.
+ */
+int unigraf_get_mst_stream_max_count(void)
+{
+ struct TSI_DPRX_HW_CAPS_R_s caps;
+
+ unigraf_assert(TSIX_TS_GetConfigItem(unigraf_device, TSI_DPRX_HW_CAPS_R,
+ &caps, sizeof(caps)));
+
+ return caps.mst_stream_count;
+}
+
+/**
+ * unigraf_get_mst_stream_count() - Get the current number of MST streams
+ *
+ * Returns: The current number of MST streams configured on the device.
+ */
+int unigraf_get_mst_stream_count(void)
+{
+ return unigraf_read_u32(TSI_DPRX_MST_SINK_COUNT);
+}
+
+/**
+ * unigraf_set_mst_stream_count() - Set the number of accepted stream count
+ *
+ * Returns true when the stream count was properly applied, false if the final stream count
+ * is not the one requested
+ */
+bool unigraf_set_mst_stream_count(int count)
+{
+ int new_count;
+
+ igt_assert_lte(count, unigraf_get_mst_stream_max_count());
+
+ unigraf_write_u32(TSI_DPRX_MST_SINK_COUNT, count);
+ new_count = unigraf_get_mst_stream_count();
+
+ igt_warn_on_f(count != new_count,
+ "IGT:%p: Requested MST stream count (%d) differs from what was applied by the device (%d)\n",
+ unigraf_device, count, new_count);
+
+ return count == new_count;
+}
+
+/**
+ * unigraf_select_stream() - Select the active stream for the device
+ * @stream: The stream index to select
+ *
+ * This function selects the active stream for the device. The stream index
+ * should be a valid stream number that the device supports.
+ */
+void unigraf_select_stream(int stream)
+{
+ unigraf_write_u32(TSI_DPRX_STREAM_SELECT, stream);
+}
diff --git a/lib/unigraf/unigraf.h b/lib/unigraf/unigraf.h
index ae1f0d8955ab..c2caa9c2c54c 100644
--- a/lib/unigraf/unigraf.h
+++ b/lib/unigraf/unigraf.h
@@ -52,4 +52,16 @@ void unigraf_plug(void);
void unigraf_unplug(void);
+void unigraf_set_sst(void);
+
+void unigraf_set_mst(void);
+
+int unigraf_get_mst_stream_count(void);
+
+bool unigraf_set_mst_stream_count(int count);
+
+int unigraf_get_mst_stream_max_count(void);
+
+void unigraf_select_stream(int stream);
+
#endif // UNIGRAF_H
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* [PATCH i-g-t v9 29/49] lib/unigraf: Add helpers to read and write edid
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (27 preceding siblings ...)
2026-03-16 16:17 ` [PATCH i-g-t v9 28/49] lib/unigraf: Allows sst/mst configuration Louis Chauvet
@ 2026-03-16 16:17 ` Louis Chauvet
2026-03-17 15:32 ` Kory Maincent
2026-03-23 16:26 ` Kamil Konieczny
2026-03-16 16:17 ` [PATCH i-g-t v9 30/49] lib/unigraf: Add connector and EDID configuration Louis Chauvet
` (20 subsequent siblings)
49 siblings, 2 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:17 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
Read and writing EDID on unigraf can be useful to emulate different
screens, add helpers to read and write them.
There is a limitation on the libTSI.so, it is only possible to read EDID
using buffers of size multiple of 128 bytes, and there is no way to query
the actual EDID size. The current implementation can only read EDID up to
2048 bytes.
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
Reviewed-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
---
lib/unigraf/unigraf.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++
lib/unigraf/unigraf.h | 4 ++++
2 files changed, 63 insertions(+)
diff --git a/lib/unigraf/unigraf.c b/lib/unigraf/unigraf.c
index b752ff573ace..d06da6742e95 100644
--- a/lib/unigraf/unigraf.c
+++ b/lib/unigraf/unigraf.c
@@ -56,6 +56,11 @@ static char *unigraf_connector_name;
*/
#define UNIGRAF_DEFAULT_INPUT_NAME "DP RX"
+/**
+ * EDID_MAX_SIZE - Max EDID size that can be read from the unigraf. The
+ */
+#define EDID_MAX_SIZE 2048
+
static void unigraf_close_device(void)
{
if (!unigraf_device)
@@ -127,6 +132,20 @@ static void unigraf_init(void)
value; \
})
+/**
+ * unigraf_write() - Helper to write a value to unigraf
+ * @config: config id to write
+ * @data: data to write
+ * @data_len: length of the data
+ * Returns
+ */
+static void unigraf_write(TSI_CONFIG_ID config, const void *data, size_t data_len)
+{
+ unigraf_debug("Writing %zu bytes to 0x%x\n", data_len, config);
+
+ unigraf_assert(TSIX_TS_SetConfigItem(unigraf_device, config, data, data_len));
+}
+
/**
* unigraf_device_count() - Return the number of scanned devices
*
@@ -329,6 +348,46 @@ void unigraf_reset(void)
unigraf_set_sst();
}
+/**
+ * unigraf_read_edid() - Read the EDID from the specified stream
+ * @stream: The stream ID to read the EDID from
+ * @edid_size: Pointer to an integer where the size of the EDID will be stored
+ *
+ * Returns: A pointer to the EDID structure, or NULL if the operation failed. The caller
+ * is responsible to free this pointer.
+ */
+struct edid *unigraf_read_edid(uint32_t stream, uint32_t *edid_size)
+{
+ void *edid;
+
+ unigraf_debug("Read EDID for stream %d...\n", stream);
+
+ edid = calloc(EDID_MAX_SIZE, sizeof(char));
+
+ unigraf_write_u32(TSI_EDID_SELECT_STREAM, stream);
+ *edid_size = unigraf_assert(TSIX_TS_GetConfigItem(unigraf_device,
+ TSI_EDID_TE_INPUT,
+ edid, EDID_MAX_SIZE));
+
+ return edid;
+}
+
+/**
+ * unigraf_write_edid() - Write EDID data to the specified stream
+ * @stream: The stream ID to write the EDID to
+ * @edid: Pointer to the EDID structure to write
+ * @edid_size: Size of the EDID data in bytes
+ *
+ * This function writes the provided EDID data to the specified stream.
+ */
+void unigraf_write_edid(uint32_t stream, const struct edid *edid, uint32_t edid_size)
+{
+ unigraf_debug("Write EDID for stream %d...\n", stream);
+
+ unigraf_write_u32(TSI_EDID_SELECT_STREAM, stream);
+ unigraf_write(TSI_EDID_TE_INPUT, edid, 2048);
+}
+
/**
* unigraf_hpd_assert() - Assert Hot Plug Detect signal
*
diff --git a/lib/unigraf/unigraf.h b/lib/unigraf/unigraf.h
index c2caa9c2c54c..40f706ebd483 100644
--- a/lib/unigraf/unigraf.h
+++ b/lib/unigraf/unigraf.h
@@ -42,6 +42,10 @@ void unigraf_require_device(int drm_fd);
void unigraf_reset(void);
+struct edid *unigraf_read_edid(uint32_t stream, uint32_t *edid_size);
+
+void unigraf_write_edid(uint32_t stream, const struct edid *edid, uint32_t edid_size);
+
void unigraf_hpd_deassert(void);
void unigraf_hpd_pulse(int duration);
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* Re: [PATCH i-g-t v9 29/49] lib/unigraf: Add helpers to read and write edid
2026-03-16 16:17 ` [PATCH i-g-t v9 29/49] lib/unigraf: Add helpers to read and write edid Louis Chauvet
@ 2026-03-17 15:32 ` Kory Maincent
2026-03-23 14:46 ` Louis Chauvet
2026-03-23 16:26 ` Kamil Konieczny
1 sibling, 1 reply; 83+ messages in thread
From: Kory Maincent @ 2026-03-17 15:32 UTC (permalink / raw)
To: Louis Chauvet
Cc: igt-dev, thomas.petazzoni, luca.ceresoli, markyacoub,
khaled.almahallawy
On Mon, 16 Mar 2026 17:17:50 +0100
Louis Chauvet <louis.chauvet@bootlin.com> wrote:
> Read and writing EDID on unigraf can be useful to emulate different
> screens, add helpers to read and write them.
>
> There is a limitation on the libTSI.so, it is only possible to read EDID
> using buffers of size multiple of 128 bytes, and there is no way to query
> the actual EDID size. The current implementation can only read EDID up to
> 2048 bytes.
>
> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> Reviewed-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
> ---
> lib/unigraf/unigraf.c | 59
> +++++++++++++++++++++++++++++++++++++++++++++++++++ lib/unigraf/unigraf.h |
> 4 ++++ 2 files changed, 63 insertions(+)
>
> diff --git a/lib/unigraf/unigraf.c b/lib/unigraf/unigraf.c
> index b752ff573ace..d06da6742e95 100644
> --- a/lib/unigraf/unigraf.c
> +++ b/lib/unigraf/unigraf.c
> @@ -56,6 +56,11 @@ static char *unigraf_connector_name;
> */
> #define UNIGRAF_DEFAULT_INPUT_NAME "DP RX"
>
> +/**
> + * EDID_MAX_SIZE - Max EDID size that can be read from the unigraf. The
> + */
The ? Missing a sentence?
> +#define EDID_MAX_SIZE 2048
> +
> static void unigraf_close_device(void)
> {
> if (!unigraf_device)
> @@ -127,6 +132,20 @@ static void unigraf_init(void)
> value;
> \ })
>
...
> +
> +/**
> + * unigraf_write_edid() - Write EDID data to the specified stream
> + * @stream: The stream ID to write the EDID to
> + * @edid: Pointer to the EDID structure to write
> + * @edid_size: Size of the EDID data in bytes
> + *
> + * This function writes the provided EDID data to the specified stream.
> + */
> +void unigraf_write_edid(uint32_t stream, const struct edid *edid, uint32_t
> edid_size) +{
edid_size not used in the function.
> + unigraf_debug("Write EDID for stream %d...\n", stream);
> +
> + unigraf_write_u32(TSI_EDID_SELECT_STREAM, stream);
> + unigraf_write(TSI_EDID_TE_INPUT, edid, 2048);
Use EDID_MAX_SIZE here or the edid_size parameter?
Regards,
--
Köry Maincent, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 83+ messages in thread* Re: [PATCH i-g-t v9 29/49] lib/unigraf: Add helpers to read and write edid
2026-03-17 15:32 ` Kory Maincent
@ 2026-03-23 14:46 ` Louis Chauvet
0 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-23 14:46 UTC (permalink / raw)
To: Kory Maincent
Cc: igt-dev, thomas.petazzoni, luca.ceresoli, markyacoub,
khaled.almahallawy
On 3/17/26 16:32, Kory Maincent wrote:
> On Mon, 16 Mar 2026 17:17:50 +0100
> Louis Chauvet <louis.chauvet@bootlin.com> wrote:
>
>> Read and writing EDID on unigraf can be useful to emulate different
>> screens, add helpers to read and write them.
>>
>> There is a limitation on the libTSI.so, it is only possible to read EDID
>> using buffers of size multiple of 128 bytes, and there is no way to query
>> the actual EDID size. The current implementation can only read EDID up to
>> 2048 bytes.
>>
>> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
>> Reviewed-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
>> ---
>> lib/unigraf/unigraf.c | 59
>> +++++++++++++++++++++++++++++++++++++++++++++++++++ lib/unigraf/unigraf.h |
>> 4 ++++ 2 files changed, 63 insertions(+)
>>
>> diff --git a/lib/unigraf/unigraf.c b/lib/unigraf/unigraf.c
>> index b752ff573ace..d06da6742e95 100644
>> --- a/lib/unigraf/unigraf.c
>> +++ b/lib/unigraf/unigraf.c
>> @@ -56,6 +56,11 @@ static char *unigraf_connector_name;
>> */
>> #define UNIGRAF_DEFAULT_INPUT_NAME "DP RX"
>>
>> +/**
>> + * EDID_MAX_SIZE - Max EDID size that can be read from the unigraf. The
>> + */
>
> The ? Missing a sentence?
>
>> +#define EDID_MAX_SIZE 2048
>> +
>> static void unigraf_close_device(void)
>> {
>> if (!unigraf_device)
>> @@ -127,6 +132,20 @@ static void unigraf_init(void)
>> value;
>> \ })
>>
>
> ...
>
>> +
>> +/**
>> + * unigraf_write_edid() - Write EDID data to the specified stream
>> + * @stream: The stream ID to write the EDID to
>> + * @edid: Pointer to the EDID structure to write
>> + * @edid_size: Size of the EDID data in bytes
>> + *
>> + * This function writes the provided EDID data to the specified stream.
>> + */
>> +void unigraf_write_edid(uint32_t stream, const struct edid *edid, uint32_t
>> edid_size) +{
>
> edid_size not used in the function.
True, I added a min(edid_size, EDID_MAX_SIZE) to avoid out of bound
accesses.
>> + unigraf_debug("Write EDID for stream %d...\n", stream);
>> +
>> + unigraf_write_u32(TSI_EDID_SELECT_STREAM, stream);
>> + unigraf_write(TSI_EDID_TE_INPUT, edid, 2048);
>
> Use EDID_MAX_SIZE here or the edid_size parameter?
Thanks!
> Regards,
^ permalink raw reply [flat|nested] 83+ messages in thread
* Re: [PATCH i-g-t v9 29/49] lib/unigraf: Add helpers to read and write edid
2026-03-16 16:17 ` [PATCH i-g-t v9 29/49] lib/unigraf: Add helpers to read and write edid Louis Chauvet
2026-03-17 15:32 ` Kory Maincent
@ 2026-03-23 16:26 ` Kamil Konieczny
2026-03-23 17:34 ` Louis Chauvet
1 sibling, 1 reply; 83+ messages in thread
From: Kamil Konieczny @ 2026-03-23 16:26 UTC (permalink / raw)
To: Louis Chauvet
Cc: igt-dev, thomas.petazzoni, luca.ceresoli, kory.maincent,
markyacoub, khaled.almahallawy
Hi Louis,
On 2026-03-16 at 17:17:50 +0100, Louis Chauvet wrote:
> Read and writing EDID on unigraf can be useful to emulate different
> screens, add helpers to read and write them.
>
> There is a limitation on the libTSI.so, it is only possible to read EDID
> using buffers of size multiple of 128 bytes, and there is no way to query
> the actual EDID size. The current implementation can only read EDID up to
> 2048 bytes.
>
> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> Reviewed-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
> ---
> lib/unigraf/unigraf.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++
> lib/unigraf/unigraf.h | 4 ++++
Please move all unigraf libs into lib/vendors/unigraf/
similar to uwildmat use case.
> 2 files changed, 63 insertions(+)
>
> diff --git a/lib/unigraf/unigraf.c b/lib/unigraf/unigraf.c
> index b752ff573ace..d06da6742e95 100644
> --- a/lib/unigraf/unigraf.c
> +++ b/lib/unigraf/unigraf.c
> @@ -56,6 +56,11 @@ static char *unigraf_connector_name;
> */
> #define UNIGRAF_DEFAULT_INPUT_NAME "DP RX"
>
> +/**
> + * EDID_MAX_SIZE - Max EDID size that can be read from the unigraf. The
> + */
> +#define EDID_MAX_SIZE 2048
> +
Is this unigraf specific? For local def maybe UNIGRAF_EDID_MAX_SIZE?
Regards,
Kamil
> static void unigraf_close_device(void)
> {
> if (!unigraf_device)
> @@ -127,6 +132,20 @@ static void unigraf_init(void)
> value; \
> })
>
> +/**
> + * unigraf_write() - Helper to write a value to unigraf
> + * @config: config id to write
> + * @data: data to write
> + * @data_len: length of the data
> + * Returns
> + */
> +static void unigraf_write(TSI_CONFIG_ID config, const void *data, size_t data_len)
> +{
> + unigraf_debug("Writing %zu bytes to 0x%x\n", data_len, config);
> +
> + unigraf_assert(TSIX_TS_SetConfigItem(unigraf_device, config, data, data_len));
> +}
> +
> /**
> * unigraf_device_count() - Return the number of scanned devices
> *
> @@ -329,6 +348,46 @@ void unigraf_reset(void)
> unigraf_set_sst();
> }
>
> +/**
> + * unigraf_read_edid() - Read the EDID from the specified stream
> + * @stream: The stream ID to read the EDID from
> + * @edid_size: Pointer to an integer where the size of the EDID will be stored
> + *
> + * Returns: A pointer to the EDID structure, or NULL if the operation failed. The caller
> + * is responsible to free this pointer.
> + */
> +struct edid *unigraf_read_edid(uint32_t stream, uint32_t *edid_size)
> +{
> + void *edid;
> +
> + unigraf_debug("Read EDID for stream %d...\n", stream);
> +
> + edid = calloc(EDID_MAX_SIZE, sizeof(char));
> +
> + unigraf_write_u32(TSI_EDID_SELECT_STREAM, stream);
> + *edid_size = unigraf_assert(TSIX_TS_GetConfigItem(unigraf_device,
> + TSI_EDID_TE_INPUT,
> + edid, EDID_MAX_SIZE));
> +
> + return edid;
> +}
> +
> +/**
> + * unigraf_write_edid() - Write EDID data to the specified stream
> + * @stream: The stream ID to write the EDID to
> + * @edid: Pointer to the EDID structure to write
> + * @edid_size: Size of the EDID data in bytes
> + *
> + * This function writes the provided EDID data to the specified stream.
> + */
> +void unigraf_write_edid(uint32_t stream, const struct edid *edid, uint32_t edid_size)
> +{
> + unigraf_debug("Write EDID for stream %d...\n", stream);
> +
> + unigraf_write_u32(TSI_EDID_SELECT_STREAM, stream);
> + unigraf_write(TSI_EDID_TE_INPUT, edid, 2048);
> +}
> +
> /**
> * unigraf_hpd_assert() - Assert Hot Plug Detect signal
> *
> diff --git a/lib/unigraf/unigraf.h b/lib/unigraf/unigraf.h
> index c2caa9c2c54c..40f706ebd483 100644
> --- a/lib/unigraf/unigraf.h
> +++ b/lib/unigraf/unigraf.h
> @@ -42,6 +42,10 @@ void unigraf_require_device(int drm_fd);
>
> void unigraf_reset(void);
>
> +struct edid *unigraf_read_edid(uint32_t stream, uint32_t *edid_size);
> +
> +void unigraf_write_edid(uint32_t stream, const struct edid *edid, uint32_t edid_size);
> +
> void unigraf_hpd_deassert(void);
>
> void unigraf_hpd_pulse(int duration);
>
> --
> 2.52.0
>
^ permalink raw reply [flat|nested] 83+ messages in thread* Re: [PATCH i-g-t v9 29/49] lib/unigraf: Add helpers to read and write edid
2026-03-23 16:26 ` Kamil Konieczny
@ 2026-03-23 17:34 ` Louis Chauvet
2026-03-24 9:35 ` Louis Chauvet
0 siblings, 1 reply; 83+ messages in thread
From: Louis Chauvet @ 2026-03-23 17:34 UTC (permalink / raw)
To: Kamil Konieczny, igt-dev, thomas.petazzoni, luca.ceresoli,
kory.maincent, markyacoub, khaled.almahallawy
On 3/23/26 17:26, Kamil Konieczny wrote:
> Hi Louis,
> On 2026-03-16 at 17:17:50 +0100, Louis Chauvet wrote:
>> Read and writing EDID on unigraf can be useful to emulate different
>> screens, add helpers to read and write them.
>>
>> There is a limitation on the libTSI.so, it is only possible to read EDID
>> using buffers of size multiple of 128 bytes, and there is no way to query
>> the actual EDID size. The current implementation can only read EDID up to
>> 2048 bytes.
>>
>> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
>> Reviewed-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
>> ---
>> lib/unigraf/unigraf.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++
>> lib/unigraf/unigraf.h | 4 ++++
>
> Please move all unigraf libs into lib/vendors/unigraf/
> similar to uwildmat use case.
Ack, I will do it for the next iteration.
>> 2 files changed, 63 insertions(+)
>>
>> diff --git a/lib/unigraf/unigraf.c b/lib/unigraf/unigraf.c
>> index b752ff573ace..d06da6742e95 100644
>> --- a/lib/unigraf/unigraf.c
>> +++ b/lib/unigraf/unigraf.c
>> @@ -56,6 +56,11 @@ static char *unigraf_connector_name;
>> */
>> #define UNIGRAF_DEFAULT_INPUT_NAME "DP RX"
>>
>> +/**
>> + * EDID_MAX_SIZE - Max EDID size that can be read from the unigraf. The
>> + */
>> +#define EDID_MAX_SIZE 2048
>> +
>
> Is this unigraf specific? For local def maybe UNIGRAF_EDID_MAX_SIZE?
This is a limitation of my unigraf hardware. My current implementation was:
size = TSIX_TS_GetConfigItem(dev,EDID_TE_INPUT,0,0);
buf = malloc(size)
TSIX_TS_GetConfigItem(dev,EDID_TE_INPUT,buf,size);
but TSIX_TS_GetConfigItem don't give the real size but min(buf_size,
real_edid_size) (and sometimes worse: if size is not % 128, it can do
buffer overflow...)
That why I always allocate a "big" buffer to ensure the %128 and to get
the real_size.
I will rename the constant, thanks!
> Regards,
> Kamil
>
>> static void unigraf_close_device(void)
>> {
>> if (!unigraf_device)
>> @@ -127,6 +132,20 @@ static void unigraf_init(void)
>> value; \
>> })
>>
>> +/**
>> + * unigraf_write() - Helper to write a value to unigraf
>> + * @config: config id to write
>> + * @data: data to write
>> + * @data_len: length of the data
>> + * Returns
>> + */
>> +static void unigraf_write(TSI_CONFIG_ID config, const void *data, size_t data_len)
>> +{
>> + unigraf_debug("Writing %zu bytes to 0x%x\n", data_len, config);
>> +
>> + unigraf_assert(TSIX_TS_SetConfigItem(unigraf_device, config, data, data_len));
>> +}
>> +
>> /**
>> * unigraf_device_count() - Return the number of scanned devices
>> *
>> @@ -329,6 +348,46 @@ void unigraf_reset(void)
>> unigraf_set_sst();
>> }
>>
>> +/**
>> + * unigraf_read_edid() - Read the EDID from the specified stream
>> + * @stream: The stream ID to read the EDID from
>> + * @edid_size: Pointer to an integer where the size of the EDID will be stored
>> + *
>> + * Returns: A pointer to the EDID structure, or NULL if the operation failed. The caller
>> + * is responsible to free this pointer.
>> + */
>> +struct edid *unigraf_read_edid(uint32_t stream, uint32_t *edid_size)
>> +{
>> + void *edid;
>> +
>> + unigraf_debug("Read EDID for stream %d...\n", stream);
>> +
>> + edid = calloc(EDID_MAX_SIZE, sizeof(char));
>> +
>> + unigraf_write_u32(TSI_EDID_SELECT_STREAM, stream);
>> + *edid_size = unigraf_assert(TSIX_TS_GetConfigItem(unigraf_device,
>> + TSI_EDID_TE_INPUT,
>> + edid, EDID_MAX_SIZE));
>> +
>> + return edid;
>> +}
>> +
>> +/**
>> + * unigraf_write_edid() - Write EDID data to the specified stream
>> + * @stream: The stream ID to write the EDID to
>> + * @edid: Pointer to the EDID structure to write
>> + * @edid_size: Size of the EDID data in bytes
>> + *
>> + * This function writes the provided EDID data to the specified stream.
>> + */
>> +void unigraf_write_edid(uint32_t stream, const struct edid *edid, uint32_t edid_size)
>> +{
>> + unigraf_debug("Write EDID for stream %d...\n", stream);
>> +
>> + unigraf_write_u32(TSI_EDID_SELECT_STREAM, stream);
>> + unigraf_write(TSI_EDID_TE_INPUT, edid, 2048);
>> +}
>> +
>> /**
>> * unigraf_hpd_assert() - Assert Hot Plug Detect signal
>> *
>> diff --git a/lib/unigraf/unigraf.h b/lib/unigraf/unigraf.h
>> index c2caa9c2c54c..40f706ebd483 100644
>> --- a/lib/unigraf/unigraf.h
>> +++ b/lib/unigraf/unigraf.h
>> @@ -42,6 +42,10 @@ void unigraf_require_device(int drm_fd);
>>
>> void unigraf_reset(void);
>>
>> +struct edid *unigraf_read_edid(uint32_t stream, uint32_t *edid_size);
>> +
>> +void unigraf_write_edid(uint32_t stream, const struct edid *edid, uint32_t edid_size);
>> +
>> void unigraf_hpd_deassert(void);
>>
>> void unigraf_hpd_pulse(int duration);
>>
>> --
>> 2.52.0
>>
^ permalink raw reply [flat|nested] 83+ messages in thread* Re: [PATCH i-g-t v9 29/49] lib/unigraf: Add helpers to read and write edid
2026-03-23 17:34 ` Louis Chauvet
@ 2026-03-24 9:35 ` Louis Chauvet
0 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-24 9:35 UTC (permalink / raw)
To: Kamil Konieczny, igt-dev, thomas.petazzoni, luca.ceresoli,
kory.maincent, markyacoub, khaled.almahallawy
On 3/23/26 18:34, Louis Chauvet wrote:
>
>
> On 3/23/26 17:26, Kamil Konieczny wrote:
>> Hi Louis,
>> On 2026-03-16 at 17:17:50 +0100, Louis Chauvet wrote:
>>> Read and writing EDID on unigraf can be useful to emulate different
>>> screens, add helpers to read and write them.
>>>
>>> There is a limitation on the libTSI.so, it is only possible to read EDID
>>> using buffers of size multiple of 128 bytes, and there is no way to
>>> query
>>> the actual EDID size. The current implementation can only read EDID
>>> up to
>>> 2048 bytes.
>>>
>>> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
>>> Reviewed-by: Luca Ceresoli <luca.ceresoli@bootlin.com>
>>> ---
>>> lib/unigraf/unigraf.c | 59 ++++++++++++++++++++++++++++++++++++++++
>>> +++++++++++
>>> lib/unigraf/unigraf.h | 4 ++++
>>
>> Please move all unigraf libs into lib/vendors/unigraf/
>> similar to uwildmat use case.
>
> Ack, I will do it for the next iteration.
>
>>> 2 files changed, 63 insertions(+)
>>>
>>> diff --git a/lib/unigraf/unigraf.c b/lib/unigraf/unigraf.c
>>> index b752ff573ace..d06da6742e95 100644
>>> --- a/lib/unigraf/unigraf.c
>>> +++ b/lib/unigraf/unigraf.c
>>> @@ -56,6 +56,11 @@ static char *unigraf_connector_name;
>>> */
>>> #define UNIGRAF_DEFAULT_INPUT_NAME "DP RX"
>>> +/**
>>> + * EDID_MAX_SIZE - Max EDID size that can be read from the unigraf. The
>>> + */
>>> +#define EDID_MAX_SIZE 2048
>>> +
>>
>> Is this unigraf specific? For local def maybe UNIGRAF_EDID_MAX_SIZE?
>
> This is a limitation of my unigraf hardware. My current implementation was:
s/current/first/ sorry...
> size = TSIX_TS_GetConfigItem(dev,EDID_TE_INPUT,0,0);
> buf = malloc(size)
> TSIX_TS_GetConfigItem(dev,EDID_TE_INPUT,buf,size);
>
> but TSIX_TS_GetConfigItem don't give the real size but min(buf_size,
> real_edid_size) (and sometimes worse: if size is not % 128, it can do
> buffer overflow...)
>
> That why I always allocate a "big" buffer to ensure the %128 and to get
> the real_size.
>
> I will rename the constant, thanks!
>
>> Regards,
>> Kamil
>>
>>> static void unigraf_close_device(void)
>>> {
>>> if (!unigraf_device)
>>> @@ -127,6 +132,20 @@ static void unigraf_init(void)
>>> value; \
>>> })
>>> +/**
>>> + * unigraf_write() - Helper to write a value to unigraf
>>> + * @config: config id to write
>>> + * @data: data to write
>>> + * @data_len: length of the data
>>> + * Returns
>>> + */
>>> +static void unigraf_write(TSI_CONFIG_ID config, const void *data,
>>> size_t data_len)
>>> +{
>>> + unigraf_debug("Writing %zu bytes to 0x%x\n", data_len, config);
>>> +
>>> + unigraf_assert(TSIX_TS_SetConfigItem(unigraf_device, config,
>>> data, data_len));
>>> +}
>>> +
>>> /**
>>> * unigraf_device_count() - Return the number of scanned devices
>>> *
>>> @@ -329,6 +348,46 @@ void unigraf_reset(void)
>>> unigraf_set_sst();
>>> }
>>> +/**
>>> + * unigraf_read_edid() - Read the EDID from the specified stream
>>> + * @stream: The stream ID to read the EDID from
>>> + * @edid_size: Pointer to an integer where the size of the EDID will
>>> be stored
>>> + *
>>> + * Returns: A pointer to the EDID structure, or NULL if the
>>> operation failed. The caller
>>> + * is responsible to free this pointer.
>>> + */
>>> +struct edid *unigraf_read_edid(uint32_t stream, uint32_t *edid_size)
>>> +{
>>> + void *edid;
>>> +
>>> + unigraf_debug("Read EDID for stream %d...\n", stream);
>>> +
>>> + edid = calloc(EDID_MAX_SIZE, sizeof(char));
>>> +
>>> + unigraf_write_u32(TSI_EDID_SELECT_STREAM, stream);
>>> + *edid_size = unigraf_assert(TSIX_TS_GetConfigItem(unigraf_device,
>>> + TSI_EDID_TE_INPUT,
>>> + edid, EDID_MAX_SIZE));
>>> +
>>> + return edid;
>>> +}
>>> +
>>> +/**
>>> + * unigraf_write_edid() - Write EDID data to the specified stream
>>> + * @stream: The stream ID to write the EDID to
>>> + * @edid: Pointer to the EDID structure to write
>>> + * @edid_size: Size of the EDID data in bytes
>>> + *
>>> + * This function writes the provided EDID data to the specified stream.
>>> + */
>>> +void unigraf_write_edid(uint32_t stream, const struct edid *edid,
>>> uint32_t edid_size)
>>> +{
>>> + unigraf_debug("Write EDID for stream %d...\n", stream);
>>> +
>>> + unigraf_write_u32(TSI_EDID_SELECT_STREAM, stream);
>>> + unigraf_write(TSI_EDID_TE_INPUT, edid, 2048);
>>> +}
>>> +
>>> /**
>>> * unigraf_hpd_assert() - Assert Hot Plug Detect signal
>>> *
>>> diff --git a/lib/unigraf/unigraf.h b/lib/unigraf/unigraf.h
>>> index c2caa9c2c54c..40f706ebd483 100644
>>> --- a/lib/unigraf/unigraf.h
>>> +++ b/lib/unigraf/unigraf.h
>>> @@ -42,6 +42,10 @@ void unigraf_require_device(int drm_fd);
>>> void unigraf_reset(void);
>>> +struct edid *unigraf_read_edid(uint32_t stream, uint32_t *edid_size);
>>> +
>>> +void unigraf_write_edid(uint32_t stream, const struct edid *edid,
>>> uint32_t edid_size);
>>> +
>>> void unigraf_hpd_deassert(void);
>>> void unigraf_hpd_pulse(int duration);
>>>
>>> --
>>> 2.52.0
>>>
>
^ permalink raw reply [flat|nested] 83+ messages in thread
* [PATCH i-g-t v9 30/49] lib/unigraf: Add connector and EDID configuration
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (28 preceding siblings ...)
2026-03-16 16:17 ` [PATCH i-g-t v9 29/49] lib/unigraf: Add helpers to read and write edid Louis Chauvet
@ 2026-03-16 16:17 ` Louis Chauvet
2026-03-16 16:17 ` [PATCH i-g-t v9 31/49] tests/unigraf: Add basic unigraf tests Louis Chauvet
` (19 subsequent siblings)
49 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:17 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
As multiple connectors can be present on the DUT, add a configuration to
tell which connector is used by the Unigraf and which EDID to use on the
Unigraf.
Complete list of EDID name supported can be found in
lib/monitor_edids/{dp,hdmi}_edids.h
The configuration will look like:
[Unigraf]
Device=UCD-500 [2434C620]
Role=DisplayPort Source and Sink
Input=DP RX
Connector=DP-4
EDID=DEL_16543_DELL_P2314T_DP
Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
lib/unigraf/unigraf.c | 126 +++++++++++++++++++++++++++++++++++++++++++++++++-
lib/unigraf/unigraf.h | 3 ++
2 files changed, 127 insertions(+), 2 deletions(-)
diff --git a/lib/unigraf/unigraf.c b/lib/unigraf/unigraf.c
index d06da6742e95..6e70df288cbe 100644
--- a/lib/unigraf/unigraf.c
+++ b/lib/unigraf/unigraf.c
@@ -13,7 +13,10 @@
#include "unigraf.h"
#include "TSI.h"
#include "TSI_types.h"
+#include "igt_edid.h"
+#include "igt_kms.h"
#include "igt_rc.h"
+#include "monitor_edids/monitor_edids_helper.h"
#define unigraf_debug(fmt, ...) igt_debug("TSI:%p: " fmt, unigraf_device, ##__VA_ARGS__)
@@ -46,6 +49,16 @@ static char *unigraf_connector_name;
*/
#define UNIGRAF_CONFIG_INPUT_NAME "Input"
+/**
+ * UNIGRAF_CONFIG_CONNECTOR_NAME - Key of the connector name in the configuration file
+ */
+#define UNIGRAF_CONFIG_CONNECTOR_NAME "Connector"
+
+/**
+ * UNIGRAF_CONFIG_EDID_NAME - Key of the EDID name in the configuration file
+ */
+#define UNIGRAF_CONFIG_EDID_NAME "EDID"
+
/**
* UNIGRAF_DEFAULT_ROLE_NAME - Default role name to search on the unigraf device
*/
@@ -204,6 +217,83 @@ static int unigraf_find_input(const char *request)
return -ENODEV;
}
+static void unigraf_load_default_edid(void)
+{
+ struct edid *edid = get_edid_by_name(unigraf_default_edid);
+
+ if (!edid) {
+ igt_warn("Impossible to find an edid named \"%s\"", unigraf_default_edid);
+ list_edid_names(IGT_LOG_WARN);
+ return;
+ }
+
+ for (int i = unigraf_get_mst_stream_max_count(); i > 0; i--)
+ unigraf_write_edid(i - 1, edid, edid_get_size(edid));
+}
+
+static void unigraf_autodetect_connector(int drm_fd)
+{
+ int newly_connected_count, already_connected_count, diff_len;
+ uint32_t *newly_connected = NULL, *already_connected = NULL;
+ drmModeConnectorPtr connector;
+ uint32_t *diff = NULL;
+
+ igt_assert(drm_fd);
+ unigraf_set_sst();
+ unigraf_hpd_deassert();
+
+ /*
+ * Hard sleep is required here as we don't know how long it will take for the device under
+ * test to properly detect the port disconnection.
+ */
+ sleep(igt_default_display_detect_timeout());
+
+ already_connected_count = igt_get_connected_connectors(drm_fd, &already_connected);
+
+ unigraf_hpd_assert();
+
+ newly_connected_count = kms_wait_for_new_connectors(&newly_connected,
+ already_connected,
+ already_connected_count,
+ drm_fd);
+
+ diff_len = get_array_diff(newly_connected, newly_connected_count,
+ already_connected, already_connected_count, &diff);
+
+ if (!diff_len) {
+ unigraf_debug("No newly connected connector, assuming that the unigraf is not connected.\n");
+ } else if (diff_len > 1) {
+ unigraf_debug("More than one new connectors connected, this is not supported by autodetection.\n");
+ } else {
+ unigraf_debug("Found one connector (%d) connected to the Unigraf\n", diff[0]);
+ connector = drmModeGetConnector(drm_fd, diff[0]);
+ igt_assert(connector);
+ igt_assert(asprintf(&unigraf_connector_name, "%s-%u",
+ kmstest_connector_type_str(connector->connector_type),
+ connector->connector_type_id) != -1);
+ drmModeFreeConnector(connector);
+ }
+
+ free(already_connected);
+ free(newly_connected);
+ free(diff);
+}
+
+/**
+ * unigraf_get_connector() - Get a DRM connector for the Unigraf device
+ * @drm_fd: DRM file descriptor
+ *
+ * Returns: A pointer to the drmModeConnector connected to the unigraf device,
+ * or NULL if the operation failed. The caller is responsible for freeing this
+ * pointer with drmModeFreeConnector().
+ */
+drmModeConnectorPtr unigraf_get_connector(int drm_fd)
+{
+ if (!unigraf_connector_name)
+ unigraf_autodetect_connector(drm_fd);
+ return igt_get_connector_from_name(drm_fd, unigraf_connector_name);
+}
+
/**
* unigraf_open_device() - Search and open a device.
* @drm_fd: File descriptor of the currently used drm device
@@ -219,6 +309,7 @@ bool unigraf_open_device(int drm_fd)
char *cfg_device = NULL;
char *cfg_role = NULL;
char *cfg_input = NULL;
+ char *cfg_edid_name = NULL;
int device_count;
int chosen_device = 0;
int chosen_role;
@@ -227,7 +318,7 @@ bool unigraf_open_device(int drm_fd)
assert(igt_can_fail());
if (unigraf_device)
- return true;
+ return unigraf_connector_name != NULL;
unigraf_init();
@@ -254,6 +345,23 @@ bool unigraf_open_device(int drm_fd)
unigraf_debug("No input name configured.\n");
cfg_input = NULL;
}
+
+ cfg_error = NULL;
+ unigraf_connector_name = g_key_file_get_string(igt_key_file, UNIGRAF_CONFIG_GROUP,
+ UNIGRAF_CONFIG_CONNECTOR_NAME,
+ &cfg_error);
+ if (cfg_error) {
+ unigraf_debug("No connector name configured, will autodetect.\n");
+ unigraf_connector_name = NULL;
+ }
+
+ cfg_error = NULL;
+ cfg_edid_name = g_key_file_get_string(igt_key_file, UNIGRAF_CONFIG_GROUP,
+ UNIGRAF_CONFIG_EDID_NAME, &cfg_error);
+ if (cfg_error) {
+ unigraf_debug("No default EDID set, use IGT default.\n");
+ cfg_edid_name = NULL;
+ }
}
unigraf_assert(TSIX_DEV_RescanDevices(0, TSI_DEVCAP_VIDEO_CAPTURE, 0));
@@ -318,9 +426,21 @@ bool unigraf_open_device(int drm_fd)
unigraf_assert(TSIX_VIN_Select(unigraf_device, chosen_input));
unigraf_assert(TSIX_VIN_Enable(unigraf_device, chosen_input));
+ if (!cfg_edid_name)
+ cfg_edid_name = strdup("DEL_16543_DELL_P2314T_DP");
+
+ unigraf_default_edid = cfg_edid_name;
+
+ if (!unigraf_connector_name) {
+ unigraf_hpd_deassert();
+ unigraf_set_sst();
+ unigraf_hpd_assert();
+ unigraf_autodetect_connector(drm_fd);
+ }
+
unigraf_reset();
- return true;
+ return unigraf_connector_name != NULL;
}
/**
@@ -346,6 +466,8 @@ void unigraf_reset(void)
unigraf_plug();
unigraf_set_mst_stream_count(1);
unigraf_set_sst();
+ unigraf_load_default_edid();
+ unigraf_hpd_assert();
}
/**
diff --git a/lib/unigraf/unigraf.h b/lib/unigraf/unigraf.h
index 40f706ebd483..fdc4441740fa 100644
--- a/lib/unigraf/unigraf.h
+++ b/lib/unigraf/unigraf.h
@@ -11,6 +11,7 @@
#include <stdbool.h>
#include <stdint.h>
+#include <xf86drmMode.h>
/**
* unigraf_assert: Helper macro to assert a TSI return value and retrieve a detailed error message.
@@ -42,6 +43,8 @@ void unigraf_require_device(int drm_fd);
void unigraf_reset(void);
+drmModeConnectorPtr unigraf_get_connector(int drm_fd);
+
struct edid *unigraf_read_edid(uint32_t stream, uint32_t *edid_size);
void unigraf_write_edid(uint32_t stream, const struct edid *edid, uint32_t edid_size);
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* [PATCH i-g-t v9 31/49] tests/unigraf: Add basic unigraf tests
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (29 preceding siblings ...)
2026-03-16 16:17 ` [PATCH i-g-t v9 30/49] lib/unigraf: Add connector and EDID configuration Louis Chauvet
@ 2026-03-16 16:17 ` Louis Chauvet
2026-03-17 15:46 ` Kory Maincent
2026-03-24 19:25 ` Naladala, Ramanaidu
2026-03-16 16:17 ` [PATCH i-g-t v9 32/49] lib/unigraf: Add unigraf CRC capture Louis Chauvet
` (18 subsequent siblings)
49 siblings, 2 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:17 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
Adds two tests to validate unigraf communication.
unigraf-connect checks if the device is connected and if the EDID is
properly applied.
ungiraf-reconnect checks if the device can be connected/reconnected
using SST and MST configurations.
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
tests/meson.build | 4 +
tests/unigraf/meson.build | 12 +++
tests/unigraf/unigraf_connectivity.c | 138 +++++++++++++++++++++++++++++++++++
3 files changed, 154 insertions(+)
diff --git a/tests/meson.build b/tests/meson.build
index cecb4a8ae34a..4d3399d0eff4 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -475,6 +475,10 @@ foreach prog : intel_progs
endif
endforeach
+if libtsi.found()
+ subdir('unigraf')
+endif
+
if chamelium.found()
foreach prog : chamelium_progs
testexe = executable(prog,
diff --git a/tests/unigraf/meson.build b/tests/unigraf/meson.build
new file mode 100644
index 000000000000..4ef8c32151e4
--- /dev/null
+++ b/tests/unigraf/meson.build
@@ -0,0 +1,12 @@
+unigraf_progs = [
+ 'unigraf_connectivity',
+]
+
+foreach prog : unigraf_progs
+ test_executables += executable(prog, prog + '.c',
+ dependencies : test_deps,
+ install_dir : unigrafdir,
+ install_rpath : unigraf_rpathdir,
+ install : true)
+ test_list += join_paths('unigraf', prog)
+endforeach
diff --git a/tests/unigraf/unigraf_connectivity.c b/tests/unigraf/unigraf_connectivity.c
new file mode 100644
index 000000000000..b35149a4de9a
--- /dev/null
+++ b/tests/unigraf/unigraf_connectivity.c
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2026 Google
+ *
+ * Authors:
+ * Louis Chauvet <louis.chauvet@bootlin.com>
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <xf86drmMode.h>
+
+#include "drmtest.h"
+#include "igt_aux.h"
+#include "igt_core.h"
+#include "igt_kms.h"
+#include "unigraf/unigraf.h"
+
+/**
+ * TEST: unigraf connectivity
+ * Category: Core
+ * Description: Testing connectivity with a unigraf device
+ *
+ * SUBTEST: unigraf-connect-edid
+ * Description: Verify that the unigraf device is properly connected to the DUT
+ * and that the correct EDID is detected and read.
+ *
+ * SUBTEST: unigraf-connect-mst
+ * Description: Ensure that the DUT can correctly detect and handle the unigraf device
+ * when it is configured to operate in MST mode.
+ */
+
+IGT_TEST_DESCRIPTION("Test basic unigraf connectivity");
+int igt_main()
+{
+ int drm_fd;
+
+ igt_fixture() {
+ drm_fd = drm_open_driver_master(DRIVER_ANY);
+ }
+
+ igt_describe("Make sure that the unigraf device is connected to the DUT and EDID is properly detected");
+ igt_subtest("unigraf-connect-edid") {
+ drmModePropertyBlobPtr edid_blob = NULL;
+ struct igt_display display;
+ uint64_t edid_blob_id;
+ igt_output_t *output;
+ uint32_t unigraf_edid_len;
+ void *unigraf_edid;
+ bool found = false;
+
+ /*
+ * Sleep are required to allow hardware to configure/detect the current
+ * configuration
+ */
+ unigraf_require_device(drm_fd);
+ unigraf_hpd_deassert();
+ sleep(igt_default_display_detect_timeout());
+ unigraf_set_sst();
+ unigraf_hpd_assert();
+ sleep(igt_default_display_detect_timeout());
+ igt_display_require(&display, drm_fd);
+ sleep(igt_default_display_detect_timeout());
+
+ unigraf_edid = unigraf_read_edid(0, &unigraf_edid_len);
+
+ for_each_connected_output(&display, output) {
+ if (output->config.connector->connector_type ==
+ DRM_MODE_CONNECTOR_DisplayPort) {
+ igt_assert(kmstest_get_property(drm_fd,
+ output->config.connector->connector_id,
+ DRM_MODE_OBJECT_CONNECTOR, "EDID",
+ NULL, &edid_blob_id, NULL));
+ edid_blob = drmModeGetPropertyBlob(drm_fd, edid_blob_id);
+ if (!edid_blob)
+ continue;
+
+ if (!memcmp(unigraf_edid, edid_blob->data,
+ min(edid_blob->length, unigraf_edid_len)))
+ found = true;
+
+ drmModeFreePropertyBlob(edid_blob);
+
+ if (found)
+ break;
+ }
+ }
+ igt_assert_f(found, "No output with the correct EDID was found\n");
+
+ free(unigraf_edid);
+ }
+
+ igt_describe("Make sure that the unigraf device can be used as a MST device");
+ igt_subtest("unigraf-connect-mst") {
+ int newly_connected_count, already_connected_count, diff_len;
+ uint32_t *newly_connected = NULL, *already_connected = NULL, *diff = NULL;
+ int max_count;
+
+ unigraf_require_device(drm_fd);
+ max_count = unigraf_get_mst_stream_max_count();
+
+ unigraf_hpd_deassert();
+
+ already_connected_count = igt_get_connected_connectors(drm_fd, &already_connected);
+
+ igt_debug("Already connected count: %d\n", already_connected_count);
+
+ // i = 0 is SST so we need to process max_count + 1 streams
+ for (int i = 0; i <= max_count; i++) {
+ unigraf_hpd_deassert();
+ // Let the hardware detect the new state
+ sleep(igt_default_display_detect_timeout());
+
+ unigraf_set_mst_stream_count(max(i, 1));
+ if (!i)
+ unigraf_set_sst();
+ else
+ unigraf_set_mst();
+
+ unigraf_hpd_assert();
+ // Let the hardware detect the new state
+ sleep(igt_default_display_detect_timeout());
+
+ newly_connected_count = kms_wait_for_new_connectors(&newly_connected,
+ already_connected,
+ already_connected_count,
+ drm_fd);
+
+ diff_len = get_array_diff(newly_connected, newly_connected_count,
+ already_connected, already_connected_count, &diff);
+
+ igt_assert_f(diff_len == max(i, 1),
+ "Invalid connected connector count, expected %d found %d\n",
+ max(i, 1), diff_len);
+ }
+ }
+}
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* Re: [PATCH i-g-t v9 31/49] tests/unigraf: Add basic unigraf tests
2026-03-16 16:17 ` [PATCH i-g-t v9 31/49] tests/unigraf: Add basic unigraf tests Louis Chauvet
@ 2026-03-17 15:46 ` Kory Maincent
2026-03-23 14:49 ` Louis Chauvet
2026-03-24 19:25 ` Naladala, Ramanaidu
1 sibling, 1 reply; 83+ messages in thread
From: Kory Maincent @ 2026-03-17 15:46 UTC (permalink / raw)
To: Louis Chauvet
Cc: igt-dev, thomas.petazzoni, luca.ceresoli, markyacoub,
khaled.almahallawy
On Mon, 16 Mar 2026 17:17:52 +0100
Louis Chauvet <louis.chauvet@bootlin.com> wrote:
> Adds two tests to validate unigraf communication.
>
> unigraf-connect checks if the device is connected and if the EDID is
> properly applied.
>
> ungiraf-reconnect checks if the device can be connected/reconnected
> using SST and MST configurations.
>
> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> ---
> tests/meson.build | 4 +
> tests/unigraf/meson.build | 12 +++
> tests/unigraf/unigraf_connectivity.c | 138
> +++++++++++++++++++++++++++++++++++ 3 files changed, 154 insertions(+)
>
> diff --git a/tests/meson.build b/tests/meson.build
> index cecb4a8ae34a..4d3399d0eff4 100644
> --- a/tests/meson.build
> +++ b/tests/meson.build
> @@ -475,6 +475,10 @@ foreach prog : intel_progs
> endif
> endforeach
>
> +if libtsi.found()
> + subdir('unigraf')
> +endif
> +
> if chamelium.found()
> foreach prog : chamelium_progs
> testexe = executable(prog,
> diff --git a/tests/unigraf/meson.build b/tests/unigraf/meson.build
> new file mode 100644
> index 000000000000..4ef8c32151e4
> --- /dev/null
> +++ b/tests/unigraf/meson.build
> @@ -0,0 +1,12 @@
> +unigraf_progs = [
> + 'unigraf_connectivity',
> +]
> +
> +foreach prog : unigraf_progs
> + test_executables += executable(prog, prog + '.c',
> + dependencies : test_deps,
> + install_dir : unigrafdir,
> + install_rpath : unigraf_rpathdir,
> + install : true)
> + test_list += join_paths('unigraf', prog)
> +endforeach
> diff --git a/tests/unigraf/unigraf_connectivity.c
> b/tests/unigraf/unigraf_connectivity.c new file mode 100644
> index 000000000000..b35149a4de9a
> --- /dev/null
> +++ b/tests/unigraf/unigraf_connectivity.c
> @@ -0,0 +1,138 @@
> +// SPDX-License-Identifier: MIT
> +/*
> + * Copyright © 2026 Google
> + *
> + * Authors:
> + * Louis Chauvet <louis.chauvet@bootlin.com>
> + */
> +
> +#include <stdint.h>
> +#include <stdlib.h>
> +#include <unistd.h>
> +#include <xf86drmMode.h>
> +
> +#include "drmtest.h"
> +#include "igt_aux.h"
> +#include "igt_core.h"
> +#include "igt_kms.h"
> +#include "unigraf/unigraf.h"
> +
> +/**
> + * TEST: unigraf connectivity
> + * Category: Core
> + * Description: Testing connectivity with a unigraf device
> + *
> + * SUBTEST: unigraf-connect-edid
> + * Description: Verify that the unigraf device is properly connected to the
> DUT
> + * and that the correct EDID is detected and read.
> + *
> + * SUBTEST: unigraf-connect-mst
> + * Description: Ensure that the DUT can correctly detect and handle the
> unigraf device
> + * when it is configured to operate in MST mode.
> + */
> +
> +IGT_TEST_DESCRIPTION("Test basic unigraf connectivity");
> +int igt_main()
> +{
> + int drm_fd;
> +
> + igt_fixture() {
> + drm_fd = drm_open_driver_master(DRIVER_ANY);
> + }
> +
> + igt_describe("Make sure that the unigraf device is connected to the
> DUT and EDID is properly detected");
> + igt_subtest("unigraf-connect-edid") {
> + drmModePropertyBlobPtr edid_blob = NULL;
> + struct igt_display display;
> + uint64_t edid_blob_id;
> + igt_output_t *output;
> + uint32_t unigraf_edid_len;
> + void *unigraf_edid;
> + bool found = false;
> +
> + /*
> + * Sleep are required to allow hardware to configure/detect
> the current
> + * configuration
> + */
> + unigraf_require_device(drm_fd);
> + unigraf_hpd_deassert();
> + sleep(igt_default_display_detect_timeout());
The default display detect timeout is 10 second so you are waiting 10s here?
> + unigraf_set_sst();
> + unigraf_hpd_assert();
> + sleep(igt_default_display_detect_timeout());
10 more seconds?
> + igt_display_require(&display, drm_fd);
> + sleep(igt_default_display_detect_timeout());
again!
Does the hardware so slow that it need 30s to configure and detect the
configuration?
> +
> + unigraf_edid = unigraf_read_edid(0, &unigraf_edid_len);
> +
> + for_each_connected_output(&display, output) {
> + if (output->config.connector->connector_type ==
> + DRM_MODE_CONNECTOR_DisplayPort) {
> + igt_assert(kmstest_get_property(drm_fd,
> +
> output->config.connector->connector_id,
> +
> DRM_MODE_OBJECT_CONNECTOR, "EDID",
> + NULL,
> &edid_blob_id, NULL));
> + edid_blob = drmModeGetPropertyBlob(drm_fd,
> edid_blob_id);
> + if (!edid_blob)
> + continue;
> +
> + if (!memcmp(unigraf_edid, edid_blob->data,
> + min(edid_blob->length,
> unigraf_edid_len)))
> + found = true;
> +
> + drmModeFreePropertyBlob(edid_blob);
> +
> + if (found)
> + break;
> + }
> + }
> + igt_assert_f(found, "No output with the correct EDID was
> found\n"); +
> + free(unigraf_edid);
> + }
> +
> + igt_describe("Make sure that the unigraf device can be used as a MST
> device");
> + igt_subtest("unigraf-connect-mst") {
> + int newly_connected_count, already_connected_count, diff_len;
> + uint32_t *newly_connected = NULL, *already_connected = NULL,
> *diff = NULL;
> + int max_count;
> +
> + unigraf_require_device(drm_fd);
> + max_count = unigraf_get_mst_stream_max_count();
> +
> + unigraf_hpd_deassert();
> +
> + already_connected_count =
> igt_get_connected_connectors(drm_fd, &already_connected); +
> + igt_debug("Already connected count: %d\n",
> already_connected_count); +
> + // i = 0 is SST so we need to process max_count + 1 streams
> + for (int i = 0; i <= max_count; i++) {
> + unigraf_hpd_deassert();
> + // Let the hardware detect the new state
> + sleep(igt_default_display_detect_timeout());
10s?
> +
> + unigraf_set_mst_stream_count(max(i, 1));
> + if (!i)
> + unigraf_set_sst();
> + else
> + unigraf_set_mst();
> +
> + unigraf_hpd_assert();
> + // Let the hardware detect the new state
> + sleep(igt_default_display_detect_timeout());
10s?
> +
> + newly_connected_count =
> kms_wait_for_new_connectors(&newly_connected,
> +
> already_connected,
> +
> already_connected_count,
> +
> drm_fd); +
> + diff_len = get_array_diff(newly_connected,
> newly_connected_count,
> + already_connected,
> already_connected_count, &diff); +
> + igt_assert_f(diff_len == max(i, 1),
> + "Invalid connected connector count,
> expected %d found %d\n",
> + max(i, 1), diff_len);
> + }
> + }
> +}
>
--
Köry Maincent, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 83+ messages in thread* Re: [PATCH i-g-t v9 31/49] tests/unigraf: Add basic unigraf tests
2026-03-17 15:46 ` Kory Maincent
@ 2026-03-23 14:49 ` Louis Chauvet
0 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-23 14:49 UTC (permalink / raw)
To: Kory Maincent
Cc: igt-dev, thomas.petazzoni, luca.ceresoli, markyacoub,
khaled.almahallawy
On 3/17/26 16:46, Kory Maincent wrote:
> On Mon, 16 Mar 2026 17:17:52 +0100
> Louis Chauvet <louis.chauvet@bootlin.com> wrote:
>
>> Adds two tests to validate unigraf communication.
>>
>> unigraf-connect checks if the device is connected and if the EDID is
>> properly applied.
>>
>> ungiraf-reconnect checks if the device can be connected/reconnected
>> using SST and MST configurations.
>>
>> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
>> ---
>> tests/meson.build | 4 +
>> tests/unigraf/meson.build | 12 +++
>> tests/unigraf/unigraf_connectivity.c | 138
>> +++++++++++++++++++++++++++++++++++ 3 files changed, 154 insertions(+)
>>
>> diff --git a/tests/meson.build b/tests/meson.build
>> index cecb4a8ae34a..4d3399d0eff4 100644
>> --- a/tests/meson.build
>> +++ b/tests/meson.build
>> @@ -475,6 +475,10 @@ foreach prog : intel_progs
>> endif
>> endforeach
>>
>> +if libtsi.found()
>> + subdir('unigraf')
>> +endif
>> +
>> if chamelium.found()
>> foreach prog : chamelium_progs
>> testexe = executable(prog,
>> diff --git a/tests/unigraf/meson.build b/tests/unigraf/meson.build
>> new file mode 100644
>> index 000000000000..4ef8c32151e4
>> --- /dev/null
>> +++ b/tests/unigraf/meson.build
>> @@ -0,0 +1,12 @@
>> +unigraf_progs = [
>> + 'unigraf_connectivity',
>> +]
>> +
>> +foreach prog : unigraf_progs
>> + test_executables += executable(prog, prog + '.c',
>> + dependencies : test_deps,
>> + install_dir : unigrafdir,
>> + install_rpath : unigraf_rpathdir,
>> + install : true)
>> + test_list += join_paths('unigraf', prog)
>> +endforeach
>> diff --git a/tests/unigraf/unigraf_connectivity.c
>> b/tests/unigraf/unigraf_connectivity.c new file mode 100644
>> index 000000000000..b35149a4de9a
>> --- /dev/null
>> +++ b/tests/unigraf/unigraf_connectivity.c
>> @@ -0,0 +1,138 @@
>> +// SPDX-License-Identifier: MIT
>> +/*
>> + * Copyright © 2026 Google
>> + *
>> + * Authors:
>> + * Louis Chauvet <louis.chauvet@bootlin.com>
>> + */
>> +
>> +#include <stdint.h>
>> +#include <stdlib.h>
>> +#include <unistd.h>
>> +#include <xf86drmMode.h>
>> +
>> +#include "drmtest.h"
>> +#include "igt_aux.h"
>> +#include "igt_core.h"
>> +#include "igt_kms.h"
>> +#include "unigraf/unigraf.h"
>> +
>> +/**
>> + * TEST: unigraf connectivity
>> + * Category: Core
>> + * Description: Testing connectivity with a unigraf device
>> + *
>> + * SUBTEST: unigraf-connect-edid
>> + * Description: Verify that the unigraf device is properly connected to the
>> DUT
>> + * and that the correct EDID is detected and read.
>> + *
>> + * SUBTEST: unigraf-connect-mst
>> + * Description: Ensure that the DUT can correctly detect and handle the
>> unigraf device
>> + * when it is configured to operate in MST mode.
>> + */
>> +
>> +IGT_TEST_DESCRIPTION("Test basic unigraf connectivity");
>> +int igt_main()
>> +{
>> + int drm_fd;
>> +
>> + igt_fixture() {
>> + drm_fd = drm_open_driver_master(DRIVER_ANY);
>> + }
>> +
>> + igt_describe("Make sure that the unigraf device is connected to the
>> DUT and EDID is properly detected");
>> + igt_subtest("unigraf-connect-edid") {
>> + drmModePropertyBlobPtr edid_blob = NULL;
>> + struct igt_display display;
>> + uint64_t edid_blob_id;
>> + igt_output_t *output;
>> + uint32_t unigraf_edid_len;
>> + void *unigraf_edid;
>> + bool found = false;
>> +
>> + /*
>> + * Sleep are required to allow hardware to configure/detect
>> the current
>> + * configuration
>> + */
>> + unigraf_require_device(drm_fd);
>> + unigraf_hpd_deassert();
>> + sleep(igt_default_display_detect_timeout());
>
> The default display detect timeout is 10 second so you are waiting 10s here?
>
>> + unigraf_set_sst();
>> + unigraf_hpd_assert();
>> + sleep(igt_default_display_detect_timeout());
>
> 10 more seconds?
>
>> + igt_display_require(&display, drm_fd);
>> + sleep(igt_default_display_detect_timeout());
>
> again!
>
> Does the hardware so slow that it need 30s to configure and detect the
> configuration?
The delays are needed, but you are right, I will try with shorter
default to see if the test is flacky.
>> +
>> + unigraf_edid = unigraf_read_edid(0, &unigraf_edid_len);
>> +
>> + for_each_connected_output(&display, output) {
>> + if (output->config.connector->connector_type ==
>> + DRM_MODE_CONNECTOR_DisplayPort) {
>> + igt_assert(kmstest_get_property(drm_fd,
>> +
>> output->config.connector->connector_id,
>> +
>> DRM_MODE_OBJECT_CONNECTOR, "EDID",
>> + NULL,
>> &edid_blob_id, NULL));
>> + edid_blob = drmModeGetPropertyBlob(drm_fd,
>> edid_blob_id);
>> + if (!edid_blob)
>> + continue;
>> +
>> + if (!memcmp(unigraf_edid, edid_blob->data,
>> + min(edid_blob->length,
>> unigraf_edid_len)))
>> + found = true;
>> +
>> + drmModeFreePropertyBlob(edid_blob);
>> +
>> + if (found)
>> + break;
>> + }
>> + }
>> + igt_assert_f(found, "No output with the correct EDID was
>> found\n"); +
>> + free(unigraf_edid);
>> + }
>> +
>> + igt_describe("Make sure that the unigraf device can be used as a MST
>> device");
>> + igt_subtest("unigraf-connect-mst") {
>> + int newly_connected_count, already_connected_count, diff_len;
>> + uint32_t *newly_connected = NULL, *already_connected = NULL,
>> *diff = NULL;
>> + int max_count;
>> +
>> + unigraf_require_device(drm_fd);
>> + max_count = unigraf_get_mst_stream_max_count();
>> +
>> + unigraf_hpd_deassert();
>> +
>> + already_connected_count =
>> igt_get_connected_connectors(drm_fd, &already_connected); +
>> + igt_debug("Already connected count: %d\n",
>> already_connected_count); +
>> + // i = 0 is SST so we need to process max_count + 1 streams
>> + for (int i = 0; i <= max_count; i++) {
>> + unigraf_hpd_deassert();
>> + // Let the hardware detect the new state
>> + sleep(igt_default_display_detect_timeout());
>
> 10s?
>
>> +
>> + unigraf_set_mst_stream_count(max(i, 1));
>> + if (!i)
>> + unigraf_set_sst();
>> + else
>> + unigraf_set_mst();
>> +
>> + unigraf_hpd_assert();
>> + // Let the hardware detect the new state
>> + sleep(igt_default_display_detect_timeout());
>
> 10s?
>
>> +
>> + newly_connected_count =
>> kms_wait_for_new_connectors(&newly_connected,
>> +
>> already_connected,
>> +
>> already_connected_count,
>> +
>> drm_fd); +
>> + diff_len = get_array_diff(newly_connected,
>> newly_connected_count,
>> + already_connected,
>> already_connected_count, &diff); +
>> + igt_assert_f(diff_len == max(i, 1),
>> + "Invalid connected connector count,
>> expected %d found %d\n",
>> + max(i, 1), diff_len);
>> + }
>> + }
>> +}
>>
>
>
>
^ permalink raw reply [flat|nested] 83+ messages in thread
* Re: [PATCH i-g-t v9 31/49] tests/unigraf: Add basic unigraf tests
2026-03-16 16:17 ` [PATCH i-g-t v9 31/49] tests/unigraf: Add basic unigraf tests Louis Chauvet
2026-03-17 15:46 ` Kory Maincent
@ 2026-03-24 19:25 ` Naladala, Ramanaidu
1 sibling, 0 replies; 83+ messages in thread
From: Naladala, Ramanaidu @ 2026-03-24 19:25 UTC (permalink / raw)
To: igt-dev
[-- Attachment #1: Type: text/plain, Size: 10435 bytes --]
Hi Louis,
On 3/16/2026 9:47 PM, Louis Chauvet wrote:
> Adds two tests to validate unigraf communication.
>
> unigraf-connect checks if the device is connected and if the EDID is
> properly applied.
>
> ungiraf-reconnect checks if the device can be connected/reconnected
> using SST and MST configurations.
>
> Signed-off-by: Louis Chauvet<louis.chauvet@bootlin.com>
> ---
> tests/meson.build | 4 +
> tests/unigraf/meson.build | 12 +++
> tests/unigraf/unigraf_connectivity.c | 138 +++++++++++++++++++++++++++++++++++
> 3 files changed, 154 insertions(+)
>
> diff --git a/tests/meson.build b/tests/meson.build
> index cecb4a8ae34a..4d3399d0eff4 100644
> --- a/tests/meson.build
> +++ b/tests/meson.build
> @@ -475,6 +475,10 @@ foreach prog : intel_progs
> endif
> endforeach
>
> +if libtsi.found()
> + subdir('unigraf')
> +endif
> +
> if chamelium.found()
> foreach prog : chamelium_progs
> testexe = executable(prog,
> diff --git a/tests/unigraf/meson.build b/tests/unigraf/meson.build
> new file mode 100644
> index 000000000000..4ef8c32151e4
> --- /dev/null
> +++ b/tests/unigraf/meson.build
> @@ -0,0 +1,12 @@
> +unigraf_progs = [
> + 'unigraf_connectivity',
> +]
> +
> +foreach prog : unigraf_progs
> + test_executables += executable(prog, prog + '.c',
> + dependencies : test_deps,
> + install_dir : unigrafdir,
> + install_rpath : unigraf_rpathdir,
> + install : true)
> + test_list += join_paths('unigraf', prog)
> +endforeach
> diff --git a/tests/unigraf/unigraf_connectivity.c b/tests/unigraf/unigraf_connectivity.c
> new file mode 100644
> index 000000000000..b35149a4de9a
> --- /dev/null
> +++ b/tests/unigraf/unigraf_connectivity.c
> @@ -0,0 +1,138 @@
> +// SPDX-License-Identifier: MIT
> +/*
> + * Copyright © 2026 Google
> + *
> + * Authors:
> + * Louis Chauvet<louis.chauvet@bootlin.com>
> + */
> +
> +#include <stdint.h>
> +#include <stdlib.h>
> +#include <unistd.h>
> +#include <xf86drmMode.h>
> +
> +#include "drmtest.h"
> +#include "igt_aux.h"
> +#include "igt_core.h"
> +#include "igt_kms.h"
> +#include "unigraf/unigraf.h"
> +
> +/**
> + * TEST: unigraf connectivity
> + * Category: Core
> + * Description: Testing connectivity with a unigraf device
> + *
> + * SUBTEST: unigraf-connect-edid
> + * Description: Verify that the unigraf device is properly connected to the DUT
> + * and that the correct EDID is detected and read.
> + *
> + * SUBTEST: unigraf-connect-mst
> + * Description: Ensure that the DUT can correctly detect and handle the unigraf device
> + * when it is configured to operate in MST mode.
> + */
> +
> +IGT_TEST_DESCRIPTION("Test basic unigraf connectivity");
> +int igt_main()
> +{
> + int drm_fd;
> +
> + igt_fixture() {
> + drm_fd = drm_open_driver_master(DRIVER_ANY);
> + }
> +
> + igt_describe("Make sure that the unigraf device is connected to the DUT and EDID is properly detected");
> + igt_subtest("unigraf-connect-edid") {
> + drmModePropertyBlobPtr edid_blob = NULL;
> + struct igt_display display;
> + uint64_t edid_blob_id;
> + igt_output_t *output;
> + uint32_t unigraf_edid_len;
> + void *unigraf_edid;
> + bool found = false;
> +
> + /*
> + * Sleep are required to allow hardware to configure/detect the current
> + * configuration
> + */
> + unigraf_require_device(drm_fd);
I think delays side need to check again. If i ran subtest alone observed
new connector timeouts.
Starting subtest: unigraf-connect-mst
(unigraf_connectivity:7675) unigraf/unigraf-DEBUG: TSI:(nil): Initialize
unigraf...
(unigraf_connectivity:7675) unigraf/unigraf-DEBUG: TSI:(nil): No
connector name configured, will autodetect.
(unigraf_connectivity:7675) unigraf/unigraf-DEBUG: TSI:(nil): MST usage
not configured, using SST.
(unigraf_connectivity:7675) unigraf/unigraf-DEBUG: TSI:(nil): Detected
unigraf device 0: UCD-500 [2607C673]
(unigraf_connectivity:7675) unigraf/unigraf-DEBUG: TSI:0x630f991906c0:
Successfully opened the unigraf device 0.
(unigraf_connectivity:7675) unigraf/unigraf-DEBUG: TSI:0x630f991906c0:
Role 0: DisplayPort Source and Sink
(unigraf_connectivity:7675) unigraf/unigraf-DEBUG: TSI:0x630f991906c0:
Role 1: DisplayPort Source and USB-C, DP Alt Mode Sink
(unigraf_connectivity:7675) unigraf/unigraf-DEBUG: TSI:0x630f991906c0:
Role 2: DisplayPort Sink and USB-C, DP Alt Mode Source
(unigraf_connectivity:7675) unigraf/unigraf-DEBUG: TSI:0x630f991906c0:
Role 3: USB-C, DP Alt Mode Source and Sink
(unigraf_connectivity:7675) unigraf/unigraf-DEBUG: TSI:0x630f991906c0:
Input 0: USB-C RX
(unigraf_connectivity:7675) unigraf/unigraf-DEBUG: TSI:0x630f991906c0:
Value write: TSI_FORCE_HOT_PLUG_STATE_W=0...
(unigraf_connectivity:7675) unigraf/unigraf-DEBUG: TSI:0x630f991906c0:
Value write: TSI_DPRX_LINK_FLAGS=86...
(unigraf_connectivity:7675) unigraf/unigraf-DEBUG: TSI:0x630f991906c0:
Value write: TSI_FORCE_HOT_PLUG_STATE_W=1...
(unigraf_connectivity:7675) unigraf/unigraf-DEBUG: TSI:0x630f991906c0:
Value write: TSI_DPRX_LINK_FLAGS=86...
(unigraf_connectivity:7675) unigraf/unigraf-DEBUG: TSI:0x630f991906c0:
Value write: TSI_FORCE_HOT_PLUG_STATE_W=0...
(unigraf_connectivity:7675) unigraf/unigraf-DEBUG: TSI:0x630f991906c0:
Value write: TSI_FORCE_HOT_PLUG_STATE_W=1...
*(unigraf_connectivity:7675) unigraf/unigraf-DEBUG: TSI:0x630f991906c0:
No newly connected connector, assuming that the unigraf is not connected.*
(unigraf_connectivity:7675) unigraf/unigraf-DEBUG: TSI:0x630f991906c0:
Value write: TSI_DPRX_HPD_FORCE=12...
(unigraf_connectivity:7675) unigraf/unigraf-DEBUG: TSI:0x630f991906c0:
Value write: TSI_DPRX_MST_SINK_COUNT=1...
(unigraf_connectivity:7675) unigraf/unigraf-DEBUG: TSI:0x630f991906c0:
Value read: TSI_DPRX_MST_SINK_COUNT=1
(unigraf_connectivity:7675) unigraf/unigraf-DEBUG: TSI:0x630f991906c0:
Value write: TSI_DPRX_LINK_FLAGS=86...
(unigraf_connectivity:7675) unigraf/unigraf-DEBUG: TSI:0x630f991906c0:
Write EDID for stream 3...
(unigraf_connectivity:7675) unigraf/unigraf-DEBUG: TSI:0x630f991906c0:
Value write: TSI_EDID_SELECT_STREAM=3...
(unigraf_connectivity:7675) unigraf/unigraf-DEBUG: TSI:0x630f991906c0:
Writing 2048 bytes to 0x1100
(unigraf_connectivity:7675) unigraf/unigraf-DEBUG: TSI:0x630f991906c0:
Write EDID for stream 2...
(unigraf_connectivity:7675) unigraf/unigraf-DEBUG: TSI:0x630f991906c0:
Value write: TSI_EDID_SELECT_STREAM=2...
(unigraf_connectivity:7675) unigraf/unigraf-DEBUG: TSI:0x630f991906c0:
Writing 2048 bytes to 0x1100
(unigraf_connectivity:7675) unigraf/unigraf-DEBUG: TSI:0x630f991906c0:
Write EDID for stream 1...
(unigraf_connectivity:7675) unigraf/unigraf-DEBUG: TSI:0x630f991906c0:
Value write: TSI_EDID_SELECT_STREAM=1...
(unigraf_connectivity:7675) unigraf/unigraf-DEBUG: TSI:0x630f991906c0:
Writing 2048 bytes to 0x1100
(unigraf_connectivity:7675) unigraf/unigraf-DEBUG: TSI:0x630f991906c0:
Write EDID for stream 0...
(unigraf_connectivity:7675) unigraf/unigraf-DEBUG: TSI:0x630f991906c0:
Value write: TSI_EDID_SELECT_STREAM=0...
(unigraf_connectivity:7675) unigraf/unigraf-DEBUG: TSI:0x630f991906c0:
Writing 2048 bytes to 0x1100
(unigraf_connectivity:7675) unigraf/unigraf-DEBUG: TSI:0x630f991906c0:
Value write: TSI_FORCE_HOT_PLUG_STATE_W=1...
(unigraf_connectivity:7675) unigraf/unigraf-DEBUG: TSI:0x630f991906c0:
Value write: TSI_DPRX_MAX_LANES=4...
(unigraf_connectivity:7675) unigraf/unigraf-DEBUG: TSI:0x630f991906c0:
Value write: TSI_DPRX_MAX_LINK_RATE=30...
(unigraf_connectivity:7675) unigraf/unigraf-DEBUG: TSI:0x630f991906c0:
Value write: TSI_DPRX_LINK_FLAGS=86...
Test requirement not met in function unigraf_require_device, file
../lib/unigraf/unigraf.c:534:
Test requirement: unigraf_open_device(drm_fd)
> + unigraf_hpd_deassert();
> + sleep(igt_default_display_detect_timeout());
> + unigraf_set_sst();
> + unigraf_hpd_assert();
> + sleep(igt_default_display_detect_timeout());
> + igt_display_require(&display, drm_fd);
> + sleep(igt_default_display_detect_timeout());
> +
> + unigraf_edid = unigraf_read_edid(0, &unigraf_edid_len);
> +
> + for_each_connected_output(&display, output) {
> + if (output->config.connector->connector_type ==
> + DRM_MODE_CONNECTOR_DisplayPort) {
> + igt_assert(kmstest_get_property(drm_fd,
> + output->config.connector->connector_id,
> + DRM_MODE_OBJECT_CONNECTOR, "EDID",
> + NULL, &edid_blob_id, NULL));
> + edid_blob = drmModeGetPropertyBlob(drm_fd, edid_blob_id);
> + if (!edid_blob)
> + continue;
> +
> + if (!memcmp(unigraf_edid, edid_blob->data,
> + min(edid_blob->length, unigraf_edid_len)))
> + found = true;
> +
> + drmModeFreePropertyBlob(edid_blob);
> +
> + if (found)
> + break;
> + }
> + }
> + igt_assert_f(found, "No output with the correct EDID was found\n");
> +
> + free(unigraf_edid);
> + }
> +
> + igt_describe("Make sure that the unigraf device can be used as a MST device");
> + igt_subtest("unigraf-connect-mst") {
> + int newly_connected_count, already_connected_count, diff_len;
> + uint32_t *newly_connected = NULL, *already_connected = NULL, *diff = NULL;
> + int max_count;
> +
> + unigraf_require_device(drm_fd);
> + max_count = unigraf_get_mst_stream_max_count();
> +
> + unigraf_hpd_deassert();
> +
> + already_connected_count = igt_get_connected_connectors(drm_fd, &already_connected);
> +
> + igt_debug("Already connected count: %d\n", already_connected_count);
> +
> + // i = 0 is SST so we need to process max_count + 1 streams
> + for (int i = 0; i <= max_count; i++) {
> + unigraf_hpd_deassert();
> + // Let the hardware detect the new state
> + sleep(igt_default_display_detect_timeout());
> +
> + unigraf_set_mst_stream_count(max(i, 1));
> + if (!i)
> + unigraf_set_sst();
> + else
> + unigraf_set_mst();
> +
> + unigraf_hpd_assert();
> + // Let the hardware detect the new state
> + sleep(igt_default_display_detect_timeout());
> +
> + newly_connected_count = kms_wait_for_new_connectors(&newly_connected,
> + already_connected,
> + already_connected_count,
> + drm_fd);
> +
> + diff_len = get_array_diff(newly_connected, newly_connected_count,
> + already_connected, already_connected_count, &diff);
> +
> + igt_assert_f(diff_len == max(i, 1),
> + "Invalid connected connector count, expected %d found %d\n",
> + max(i, 1), diff_len);
> + }
> + }
> +}
>
[-- Attachment #2: Type: text/html, Size: 11586 bytes --]
^ permalink raw reply [flat|nested] 83+ messages in thread
* [PATCH i-g-t v9 32/49] lib/unigraf: Add unigraf CRC capture
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (30 preceding siblings ...)
2026-03-16 16:17 ` [PATCH i-g-t v9 31/49] tests/unigraf: Add basic unigraf tests Louis Chauvet
@ 2026-03-16 16:17 ` Louis Chauvet
2026-03-16 16:17 ` [PATCH i-g-t v9 33/49] lib/unigraf: Add configuration for CRC usage Louis Chauvet
` (17 subsequent siblings)
49 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:17 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
As unigraf devices are able to grab a CRC for the current video stream,
add an helper to read the CRC.
Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
lib/unigraf/unigraf.c | 22 ++++++++++++++++++++++
lib/unigraf/unigraf.h | 3 +++
2 files changed, 25 insertions(+)
diff --git a/lib/unigraf/unigraf.c b/lib/unigraf/unigraf.c
index 6e70df288cbe..0f56ad998c94 100644
--- a/lib/unigraf/unigraf.c
+++ b/lib/unigraf/unigraf.c
@@ -15,6 +15,7 @@
#include "TSI_types.h"
#include "igt_edid.h"
#include "igt_kms.h"
+#include "igt_pipe_crc.h"
#include "igt_rc.h"
#include "monitor_edids/monitor_edids_helper.h"
@@ -660,3 +661,24 @@ void unigraf_select_stream(int stream)
{
unigraf_write_u32(TSI_DPRX_STREAM_SELECT, stream);
}
+
+/**
+ * unigraf_read_crc() - Read the CRC values from the Unigraf device
+ * @stream: Stream to grab the CRC from
+ * @out: Pointer to an igt_crc_t structure where the CRC values will be stored
+ *
+ * This function reads the CRC values from the Unigraf device and stores them in the
+ * provided igt_crc_t structure.
+ */
+void unigraf_read_crc(int stream, igt_crc_t *out)
+{
+ unigraf_select_stream(stream);
+ unigraf_assert(TSIX_TS_GetConfigItem(unigraf_device, TSI_DPRX_CRC_R_R,
+ &out->crc[0], sizeof(out->crc[0])));
+ unigraf_assert(TSIX_TS_GetConfigItem(unigraf_device, TSI_DPRX_CRC_G_R,
+ &out->crc[1], sizeof(out->crc[1])));
+ unigraf_assert(TSIX_TS_GetConfigItem(unigraf_device, TSI_DPRX_CRC_B_R,
+ &out->crc[2], sizeof(out->crc[2])));
+ out->n_words = 3;
+ out->has_valid_frame = false;
+}
diff --git a/lib/unigraf/unigraf.h b/lib/unigraf/unigraf.h
index fdc4441740fa..8b38261dca09 100644
--- a/lib/unigraf/unigraf.h
+++ b/lib/unigraf/unigraf.h
@@ -9,6 +9,7 @@
#ifndef UNIGRAF_H
#define UNIGRAF_H
+#include "igt_fb.h"
#include <stdbool.h>
#include <stdint.h>
#include <xf86drmMode.h>
@@ -71,4 +72,6 @@ int unigraf_get_mst_stream_max_count(void);
void unigraf_select_stream(int stream);
+void unigraf_read_crc(int stream, igt_crc_t *out);
+
#endif // UNIGRAF_H
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* [PATCH i-g-t v9 33/49] lib/unigraf: Add configuration for CRC usage
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (31 preceding siblings ...)
2026-03-16 16:17 ` [PATCH i-g-t v9 32/49] lib/unigraf: Add unigraf CRC capture Louis Chauvet
@ 2026-03-16 16:17 ` Louis Chauvet
2026-03-16 16:17 ` [PATCH i-g-t v9 34/49] lib/unigraf: add unigraf_get_connector_by_stream Louis Chauvet
` (16 subsequent siblings)
49 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:17 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
As Unigraf devices are able to compute a CRC for the input image, add a
configuration file option to use them.
Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
lib/unigraf/unigraf.c | 25 +++++++++++++++++++++++++
lib/unigraf/unigraf.h | 2 ++
2 files changed, 27 insertions(+)
diff --git a/lib/unigraf/unigraf.c b/lib/unigraf/unigraf.c
index 0f56ad998c94..e14f1fd5b050 100644
--- a/lib/unigraf/unigraf.c
+++ b/lib/unigraf/unigraf.c
@@ -24,6 +24,7 @@
static TSI_HANDLE unigraf_device;
static char *unigraf_default_edid;
static char *unigraf_connector_name;
+static bool unigraf_crc;
/**
* UNIGRAF_NAME_MAX - Maximum name length to be used for TSI functions
@@ -60,6 +61,11 @@ static char *unigraf_connector_name;
*/
#define UNIGRAF_CONFIG_EDID_NAME "EDID"
+/**
+ * UNIGRAF_CONFIG_USE_CRC_NAME - Key of the CRC selection in the configuration file
+ */
+#define UNIGRAF_CONFIG_USE_CRC_NAME "UseCRC"
+
/**
* UNIGRAF_DEFAULT_ROLE_NAME - Default role name to search on the unigraf device
*/
@@ -363,6 +369,14 @@ bool unigraf_open_device(int drm_fd)
unigraf_debug("No default EDID set, use IGT default.\n");
cfg_edid_name = NULL;
}
+
+ cfg_error = NULL;
+ unigraf_crc = g_key_file_get_boolean(igt_key_file, UNIGRAF_CONFIG_GROUP,
+ UNIGRAF_CONFIG_USE_CRC_NAME, &cfg_error);
+ if (cfg_error) {
+ unigraf_debug("CRC usage not configured, using unigraf CRC.\n");
+ unigraf_crc = true;
+ }
}
unigraf_assert(TSIX_DEV_RescanDevices(0, TSI_DEVCAP_VIDEO_CAPTURE, 0));
@@ -682,3 +696,14 @@ void unigraf_read_crc(int stream, igt_crc_t *out)
out->n_words = 3;
out->has_valid_frame = false;
}
+
+/**
+ * unigraf_use_crc() - Check if Unigraf device should be used for CRC computation
+ *
+ * Returns: true if the Unigraf device should be used for CRC computation,
+ * false otherwise.
+ */
+bool unigraf_use_crc(void)
+{
+ return unigraf_crc;
+}
diff --git a/lib/unigraf/unigraf.h b/lib/unigraf/unigraf.h
index 8b38261dca09..f22f4c993622 100644
--- a/lib/unigraf/unigraf.h
+++ b/lib/unigraf/unigraf.h
@@ -74,4 +74,6 @@ void unigraf_select_stream(int stream);
void unigraf_read_crc(int stream, igt_crc_t *out);
+bool unigraf_use_crc(void);
+
#endif // UNIGRAF_H
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* [PATCH i-g-t v9 34/49] lib/unigraf: add unigraf_get_connector_by_stream
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (32 preceding siblings ...)
2026-03-16 16:17 ` [PATCH i-g-t v9 33/49] lib/unigraf: Add configuration for CRC usage Louis Chauvet
@ 2026-03-16 16:17 ` Louis Chauvet
2026-03-16 16:17 ` [PATCH i-g-t v9 35/49] lib/unigraf: Add helper to check timings received by unigraf Louis Chauvet
` (15 subsequent siblings)
49 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:17 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
As unigraf can emulate MST, we need a way to grab the corresponding DRM
connector from the stream id.
Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
lib/unigraf/unigraf.c | 41 +++++++++++++++++++++++++++++++++++++++++
lib/unigraf/unigraf.h | 2 ++
2 files changed, 43 insertions(+)
diff --git a/lib/unigraf/unigraf.c b/lib/unigraf/unigraf.c
index e14f1fd5b050..6d192ffe36b5 100644
--- a/lib/unigraf/unigraf.c
+++ b/lib/unigraf/unigraf.c
@@ -301,6 +301,47 @@ drmModeConnectorPtr unigraf_get_connector(int drm_fd)
return igt_get_connector_from_name(drm_fd, unigraf_connector_name);
}
+/**
+ * unigraf_get_connector_id_by_stream() - Get a connector id from a stream id
+ * @drm_fd: DRM device where the unigraf is attached
+ * @stream_id: Stream id in the MST streams
+ */
+int unigraf_get_connector_id_by_stream(int drm_fd, int stream_id)
+{
+ drmModeConnectorPtr main_connector = unigraf_get_connector(drm_fd);
+ drmModeResPtr res = drmModeGetResources(drm_fd);
+
+ char *mst_path;
+ int mst_path_len = asprintf(&mst_path, "mst:%d-", main_connector->connector_id);
+
+ igt_assert(mst_path_len >= 0);
+
+ for (int i = 0; i < res->count_connectors; i++) {
+ drmModePropertyBlobPtr path_blob = kmstest_get_path_blob(drm_fd,
+ res->connectors[i]);
+
+ if (!path_blob || path_blob->length < mst_path_len)
+ continue;
+
+ if (!memcmp(path_blob->data, mst_path, mst_path_len)) {
+ if (!stream_id) {
+ drmModeFreePropertyBlob(path_blob);
+ drmModeFreeResources(res);
+ drmModeFreeConnector(main_connector);
+ return res->connectors[i];
+ }
+ stream_id--;
+ }
+
+ drmModeFreePropertyBlob(path_blob);
+ }
+
+ drmModeFreeResources(res);
+ drmModeFreeConnector(main_connector);
+
+ return 0;
+}
+
/**
* unigraf_open_device() - Search and open a device.
* @drm_fd: File descriptor of the currently used drm device
diff --git a/lib/unigraf/unigraf.h b/lib/unigraf/unigraf.h
index f22f4c993622..0ed6d1eb2713 100644
--- a/lib/unigraf/unigraf.h
+++ b/lib/unigraf/unigraf.h
@@ -76,4 +76,6 @@ void unigraf_read_crc(int stream, igt_crc_t *out);
bool unigraf_use_crc(void);
+int unigraf_get_connector_id_by_stream(int drm_fd, int stream_id);
+
#endif // UNIGRAF_H
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* [PATCH i-g-t v9 35/49] lib/unigraf: Add helper to check timings received by unigraf
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (33 preceding siblings ...)
2026-03-16 16:17 ` [PATCH i-g-t v9 34/49] lib/unigraf: add unigraf_get_connector_by_stream Louis Chauvet
@ 2026-03-16 16:17 ` Louis Chauvet
2026-03-16 16:17 ` [PATCH i-g-t v9 36/49] lib/igt_pipe_crc: Add unigraf crc calculation Louis Chauvet
` (14 subsequent siblings)
49 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:17 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
Unigraf device can check that some input video parameters.
Add a function to compare a mode with the actual signal
received by the unigraf.
Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
lib/unigraf/unigraf.c | 35 +++++++++++++++++++++++++++++++++++
lib/unigraf/unigraf.h | 2 ++
2 files changed, 37 insertions(+)
diff --git a/lib/unigraf/unigraf.c b/lib/unigraf/unigraf.c
index 6d192ffe36b5..3970c1071cc8 100644
--- a/lib/unigraf/unigraf.c
+++ b/lib/unigraf/unigraf.c
@@ -748,3 +748,38 @@ bool unigraf_use_crc(void)
{
return unigraf_crc;
}
+
+static void unigraf_read_msa(void)
+{
+ uint32_t data = 1;
+
+ unigraf_write_u32(TSI_DPRX_MSA_COMMAND_W, data);
+}
+
+/**
+ * unigraf_assert_stream_timings() - Assert that the received stream on unigraf
+ * matches the mode_info
+ * @stream: Stream id on the unigraf
+ * @mode_info: Mode to compare with
+ */
+void unigraf_assert_stream_timings(int stream, drmModeModeInfoPtr mode_info)
+{
+ uint32_t stream_count;
+
+ igt_assert(mode_info);
+
+ unigraf_read_msa();
+ stream_count = unigraf_read_u32(TSI_DPRX_MSA_STREAM_COUNT_R);
+ igt_assert_lt(stream, stream_count);
+ unigraf_write_u32(TSI_DPRX_MSA_STREAM_SELECT, stream);
+ igt_assert_eq(mode_info->htotal, unigraf_read_u32(TSI_DPRX_MSA_HTOTAL_R));
+ igt_assert_eq(mode_info->vtotal, unigraf_read_u32(TSI_DPRX_MSA_VTOTAL_R));
+ igt_assert_eq(mode_info->hdisplay, unigraf_read_u32(TSI_DPRX_MSA_HACTIVE_R));
+ igt_assert_eq(mode_info->vdisplay, unigraf_read_u32(TSI_DPRX_MSA_VACTIVE_R));
+ igt_assert_eq(mode_info->hsync_end - mode_info->hsync_start,
+ unigraf_read_u32(TSI_DPRX_MSA_HSYNC_WIDTH_R));
+ igt_assert_eq(mode_info->vsync_end - mode_info->vsync_start,
+ unigraf_read_u32(TSI_DPRX_MSA_VSYNC_WIDTH_R));
+ igt_assert_eq(mode_info->vsync_start, unigraf_read_u32(TSI_DPRX_MSA_VSTART_R));
+ igt_assert_eq(mode_info->hsync_start, unigraf_read_u32(TSI_DPRX_MSA_HSTART_R));
+}
diff --git a/lib/unigraf/unigraf.h b/lib/unigraf/unigraf.h
index 0ed6d1eb2713..65f00c8d4948 100644
--- a/lib/unigraf/unigraf.h
+++ b/lib/unigraf/unigraf.h
@@ -78,4 +78,6 @@ bool unigraf_use_crc(void);
int unigraf_get_connector_id_by_stream(int drm_fd, int stream_id);
+void unigraf_assert_stream_timings(int stream, drmModeModeInfoPtr mode_info);
+
#endif // UNIGRAF_H
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* [PATCH i-g-t v9 36/49] lib/igt_pipe_crc: Add unigraf crc calculation
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (34 preceding siblings ...)
2026-03-16 16:17 ` [PATCH i-g-t v9 35/49] lib/unigraf: Add helper to check timings received by unigraf Louis Chauvet
@ 2026-03-16 16:17 ` Louis Chauvet
2026-03-16 16:17 ` [PATCH i-g-t v9 37/49] lib/i915/dp: Move DP-related function for i915 to proper folder Louis Chauvet
` (13 subsequent siblings)
49 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:17 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
Unigraf can read crc from the physical output, use this CRC instead of
internal CRC when configured. This allows to also test the hardware signal
generation.
This small addition allows to use the unigraf device for most of the
kms_* tests.
Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
lib/igt_pipe_crc.c | 35 +++++++++++++++++++++++++++++++++++
lib/unigraf/unigraf.c | 22 ++++++++++++++++++++++
2 files changed, 57 insertions(+)
diff --git a/lib/igt_pipe_crc.c b/lib/igt_pipe_crc.c
index 9a79edd51531..ad76e5ca37f7 100644
--- a/lib/igt_pipe_crc.c
+++ b/lib/igt_pipe_crc.c
@@ -3,9 +3,12 @@
* Copyright © 2013 Intel Corporation
*/
+#include "igt_core.h"
+#include "unigraf/unigraf.h"
#include <inttypes.h>
#include <sys/stat.h>
#include <sys/mount.h>
+#include <xf86drmMode.h>
#ifdef __linux__
#include <sys/sysmacros.h>
#endif
@@ -359,6 +362,38 @@ static void read_one_crc(igt_pipe_crc_t *pipe_crc, igt_crc_t *out)
} while (ret == -EINTR);
fcntl(pipe_crc->crc_fd, F_SETFL, pipe_crc->flags);
+
+#if HAVE_UNIGRAF
+ if (unigraf_open_device(pipe_crc->fd) && unigraf_use_crc()) {
+ int crtc_index;
+ int stream_id;
+ drmModeConnectorPtr connector = unigraf_get_connector(pipe_crc->fd);
+
+ if (!connector)
+ return;
+
+ crtc_index = igt_get_crtc_index_from_connector_id(pipe_crc->fd,
+ connector->connector_id);
+ drmModeFreeConnector(connector);
+
+ if (crtc_index && crtc_index == pipe_crc->crtc_index) {
+ unigraf_read_crc(0, out);
+ return;
+ }
+
+ for (stream_id = 0; stream_id < unigraf_get_mst_stream_count(); stream_id++) {
+ int connector_id = unigraf_get_connector_id_by_stream(pipe_crc->fd,
+ stream_id);
+ crtc_index = igt_get_crtc_index_from_connector_id(pipe_crc->fd,
+ connector_id);
+
+ if (crtc_index && crtc_index == pipe_crc->crtc_index) {
+ unigraf_read_crc(stream_id, out);
+ return;
+ }
+ }
+ }
+#endif
}
/**
diff --git a/lib/unigraf/unigraf.c b/lib/unigraf/unigraf.c
index 3970c1071cc8..83ea1eb1f91c 100644
--- a/lib/unigraf/unigraf.c
+++ b/lib/unigraf/unigraf.c
@@ -66,6 +66,13 @@ static bool unigraf_crc;
*/
#define UNIGRAF_CONFIG_USE_CRC_NAME "UseCRC"
+/**
+ * UNIGRAF_CONFIG_MST_STREAM_COUNT - Key for the stream count configuration
+ *
+ * Set to 0 to use SST, 1..4 for MST
+ */
+#define UNIGRAF_CONFIG_MST_STREAM_COUNT "MSTStreams"
+
/**
* UNIGRAF_DEFAULT_ROLE_NAME - Default role name to search on the unigraf device
*/
@@ -362,6 +369,7 @@ bool unigraf_open_device(int drm_fd)
int chosen_device = 0;
int chosen_role;
int chosen_input;
+ int unigraf_stream_count;
assert(igt_can_fail());
@@ -418,6 +426,15 @@ bool unigraf_open_device(int drm_fd)
unigraf_debug("CRC usage not configured, using unigraf CRC.\n");
unigraf_crc = true;
}
+
+ cfg_error = NULL;
+ unigraf_stream_count = g_key_file_get_boolean(igt_key_file, UNIGRAF_CONFIG_GROUP,
+ UNIGRAF_CONFIG_MST_STREAM_COUNT,
+ &cfg_error);
+ if (cfg_error) {
+ unigraf_debug("MST usage not configured, using SST.\n");
+ unigraf_stream_count = 0;
+ }
}
unigraf_assert(TSIX_DEV_RescanDevices(0, TSI_DEVCAP_VIDEO_CAPTURE, 0));
@@ -496,6 +513,11 @@ bool unigraf_open_device(int drm_fd)
unigraf_reset();
+ if (!unigraf_stream_count)
+ unigraf_set_sst();
+ else
+ unigraf_set_mst_stream_count(unigraf_stream_count);
+
return unigraf_connector_name != NULL;
}
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* [PATCH i-g-t v9 37/49] lib/i915/dp: Move DP-related function for i915 to proper folder
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (35 preceding siblings ...)
2026-03-16 16:17 ` [PATCH i-g-t v9 36/49] lib/igt_pipe_crc: Add unigraf crc calculation Louis Chauvet
@ 2026-03-16 16:17 ` Louis Chauvet
2026-03-16 16:17 ` [PATCH i-g-t v9 38/49] lib/i915/dp: Rename functions to avoid confusion Louis Chauvet
` (12 subsequent siblings)
49 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:17 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
Some functions, prefixed with igt_ are not generic but specific to the
i915 intel driver. To avoid confusion and allows the introduction of
generic helpers, move those to a dedicated file.
Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
lib/i915/i915_dp.c | 345 ++++++++++++++++++++++++++++++++
lib/i915/i915_dp.h | 22 ++
lib/igt_kms.c | 310 ----------------------------
lib/igt_kms.h | 13 --
lib/meson.build | 1 +
tests/intel/kms_dp_link_training.c | 1 +
tests/intel/kms_dp_linktrain_fallback.c | 1 +
7 files changed, 370 insertions(+), 323 deletions(-)
diff --git a/lib/i915/i915_dp.c b/lib/i915/i915_dp.c
new file mode 100644
index 000000000000..024fe3b00210
--- /dev/null
+++ b/lib/i915/i915_dp.c
@@ -0,0 +1,345 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ * Copyright © 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ * Authors:
+ * Daniel Vetter <daniel.vetter@ffwll.ch>
+ * Damien Lespiau <damien.lespiau@intel.com>
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "i915_dp.h"
+#include "igt_core.h"
+
+/**
+ * igt_parse_marked_value:
+ * @buf: Buffer containing the content to parse
+ * @marked_char: The character marking the value to parse
+ * @result: Pointer to store the parsed value
+ *
+ * Finds the integer value in the buffer that is marked by the given character.
+ *
+ * Returns: 0 on success, -1 on failure
+ */
+static int igt_parse_marked_value(const char *buf, char marked_char, int *result)
+{
+ char *marked_ptr, *val_ptr;
+
+ /*
+ * Look for the marked character
+ */
+ marked_ptr = strchr(buf, marked_char);
+
+ if (marked_ptr) {
+ val_ptr = marked_ptr - 1;
+ while (val_ptr > buf && isdigit(*val_ptr))
+ val_ptr--;
+ val_ptr++;
+ if (sscanf(val_ptr, "%d", result) == 1)
+ return 0;
+ }
+ return -1;
+}
+
+ /**
+ * igt_get_current_link_rate:
+ * @drm_fd: A drm file descriptor
+ * @output: Target output
+ *
+ * Returns: link_rate if set for output else -1
+ */
+int igt_get_current_link_rate(int drm_fd, igt_output_t *output)
+{
+ char buf[512];
+ int res, ret;
+
+ res = igt_debugfs_read_connector_file(drm_fd, output->name,
+ "i915_dp_force_link_rate",
+ buf, sizeof(buf));
+ igt_assert_f(res == 0, "Unable to read %s/i915_dp_force_link_rate\n",
+ output->name);
+ res = igt_parse_marked_value(buf, '*', &ret);
+ igt_assert_f(res == 0, "Output %s not enabled\n", output->name);
+ return ret;
+}
+
+/**
+ * igt_get_current_lane_count:
+ * @drm_fd: A drm file descriptor
+ * @output: Target output
+ *
+ * Returns: lane_count if set for output else -1
+ */
+int igt_get_current_lane_count(int drm_fd, igt_output_t *output)
+{
+ char buf[512];
+ int res, ret;
+
+ res = igt_debugfs_read_connector_file(drm_fd, output->name,
+ "i915_dp_force_lane_count",
+ buf, sizeof(buf));
+ igt_assert_f(res == 0, "Unable to read %s/i915_dp_force_lane_count\n",
+ output->name);
+ res = igt_parse_marked_value(buf, '*', &ret);
+ igt_assert_f(res == 0, "Output %s not enabled\n", output->name);
+ return ret;
+}
+
+/**
+ * igt_get_max_link_rate:
+ * @drm_fd: A drm file descriptor
+ * @output: Target output
+ *
+ * Returns: max_link_rate
+ */
+int igt_get_max_link_rate(int drm_fd, igt_output_t *output)
+{
+ char buf[512];
+ int res, ret;
+
+ res = igt_debugfs_read_connector_file(drm_fd, output->name,
+ "i915_dp_max_link_rate",
+ buf, sizeof(buf));
+ igt_assert_f(res == 0, "Unable to read %s/i915_dp_max_link_rate\n",
+ output->name);
+
+ sscanf(buf, "%d", &ret);
+ return ret;
+}
+
+/**
+ * igt_get_max_link_rate:
+ * @drm_fd: A drm file descriptor
+ * @output: Target output
+ *
+ * Returns: max_link_rate
+ */
+int igt_get_max_lane_count(int drm_fd, igt_output_t *output)
+{
+ char buf[512];
+ int res, ret;
+
+ res = igt_debugfs_read_connector_file(drm_fd, output->name,
+ "i915_dp_max_lane_count",
+ buf, sizeof(buf));
+ igt_assert_f(res == 0, "Unable to read %s/i915_dp_max_lane_count\n",
+ output->name);
+
+ sscanf(buf, "%d", &ret);
+ return ret;
+}
+
+/**
+ * igt_force_link_retrain:
+ * @drm_fd: A drm file descriptor
+ * @output: Target output
+ * @retrain_count: number of retraining required
+ *
+ * Force link retrain on the output.
+ */
+void igt_force_link_retrain(int drm_fd, igt_output_t *output, int retrain_count)
+{
+ char value[2];
+ int res;
+
+ snprintf(value, sizeof(value), "%d", retrain_count);
+ res = igt_debugfs_write_connector_file(drm_fd, output->name,
+ "i915_dp_force_link_retrain",
+ value, strlen(value));
+ igt_assert_f(res == 0, "Unable to write to %s/i915_dp_force_link_retrain\n",
+ output->name);
+}
+
+/**
+ * igt_force_lt_failure:
+ * @drm_fd: A drm file descriptor
+ * @output: Target output
+ * @failure_count: 1 for same link param and
+ * 2 for reduced link params
+ *
+ * Force link training failure on the output.
+ * @failure_count: 1 for retraining with same link params
+ * 2 for retraining with reduced link params
+ */
+void igt_force_lt_failure(int drm_fd, igt_output_t *output, int failure_count)
+{
+ char value[2];
+ int res;
+
+ snprintf(value, sizeof(value), "%d", failure_count);
+ res = igt_debugfs_write_connector_file(drm_fd, output->name,
+ "i915_dp_force_link_training_failure",
+ value, strlen(value));
+ igt_assert_f(res == 0, "Unable to write to %s/i915_dp_force_link_training_failure\n",
+ output->name);
+}
+
+/**
+ * igt_get_dp_link_retrain_disabled:
+ * @drm_fd: A drm file descriptor
+ * @output: Target output
+ *
+ * Returns: True if link retrain disabled, false otherwise
+ */
+bool igt_get_dp_link_retrain_disabled(int drm_fd, igt_output_t *output)
+{
+ char buf[512];
+ int res;
+
+ res = igt_debugfs_read_connector_file(drm_fd, output->name,
+ "i915_dp_link_retrain_disabled",
+ buf, sizeof(buf));
+ igt_assert_f(res == 0, "Unable to read %s/i915_dp_link_retrain_disabled\n",
+ output->name);
+ return strstr(buf, "yes");
+}
+
+/**
+ * Checks if the force link training failure debugfs
+ * is available for a specific output.
+ *
+ * @drmfd: file descriptor of the DRM device.
+ * @output: output to check.
+ * Returns:
+ * true if the debugfs is available, false otherwise.
+ */
+bool igt_has_force_link_training_failure_debugfs(int drmfd, igt_output_t *output)
+{
+ char buf[512];
+ int res;
+
+ res = igt_debugfs_read_connector_file(drmfd, output->name,
+ "i915_dp_link_retrain_disabled",
+ buf, sizeof(buf));
+ return res == 0;
+}
+
+/**
+ * igt_get_dp_pending_lt_failures:
+ * @drm_fd: A drm file descriptor
+ * @output: Target output
+ *
+ * Returns: Number of pending link training failures.
+ */
+int igt_get_dp_pending_lt_failures(int drm_fd, igt_output_t *output)
+{
+ char buf[512];
+ int res, ret;
+
+ res = igt_debugfs_read_connector_file(drm_fd, output->name,
+ "i915_dp_force_link_training_failure",
+ buf, sizeof(buf));
+ igt_assert_f(res == 0, "Unable to read %s/i915_dp_force_link_training_failure\n",
+ output->name);
+ sscanf(buf, "%d", &ret);
+ return ret;
+}
+
+/**
+ * igt_dp_pending_retrain:
+ * @drm_fd: A drm file descriptor
+ * @output: Target output
+ *
+ * Returns: Number of pending link retrains.
+ */
+int igt_get_dp_pending_retrain(int drm_fd, igt_output_t *output)
+{
+ char buf[512];
+ int res, ret;
+
+ res = igt_debugfs_read_connector_file(drm_fd, output->name,
+ "i915_dp_force_link_retrain",
+ buf, sizeof(buf));
+ igt_assert_f(res == 0, "Unable to read %s/i915_dp_force_link_retrain\n",
+ output->name);
+ sscanf(buf, "%d", &ret);
+ return ret;
+}
+
+/**
+ * igt_reset_link_params:
+ * @drm_fd: A drm file descriptor
+ * @output: Target output
+ *
+ * Reset link rate and lane count to auto, also installs exit handler
+ * to set link rate and lane count to auto on exit
+ */
+void igt_reset_link_params(int drm_fd, igt_output_t *output)
+{
+ bool valid;
+ drmModeConnector *temp;
+
+ valid = true;
+ valid = valid && connector_attr_set_debugfs(drm_fd, output->config.connector,
+ "i915_dp_force_link_rate",
+ "auto", "auto", true);
+ valid = valid && connector_attr_set_debugfs(drm_fd, output->config.connector,
+ "i915_dp_force_lane_count",
+ "auto", "auto", true);
+ igt_assert_f(valid, "Unable to set attr or install exit handler\n");
+ dump_connector_attrs();
+ igt_install_exit_handler(reset_connectors_at_exit);
+
+ /*
+ * To allow callers to always use GetConnectorCurrent we need to force a
+ * redetection here.
+ */
+ temp = drmModeGetConnector(drm_fd, output->config.connector->connector_id);
+ drmModeFreeConnector(temp);
+}
+
+/**
+ * igt_set_link_params:
+ * @drm_fd: A drm file descriptor
+ * @output: Target output
+ *
+ * set link rate and lane count to given value, also installs exit handler
+ * to set link rate and lane count to auto on exit
+ */
+void igt_set_link_params(int drm_fd, igt_output_t *output,
+ char *link_rate, char *lane_count)
+{
+ bool valid;
+ drmModeConnector *temp;
+
+ valid = true;
+ valid = valid && connector_attr_set_debugfs(drm_fd, output->config.connector,
+ "i915_dp_force_link_rate",
+ link_rate, "auto", true);
+ valid = valid && connector_attr_set_debugfs(drm_fd, output->config.connector,
+ "i915_dp_force_lane_count",
+ lane_count, "auto", true);
+ igt_assert_f(valid, "Unable to set attr or install exit handler\n");
+ dump_connector_attrs();
+ igt_install_exit_handler(reset_connectors_at_exit);
+
+ /*
+ * To allow callers to always use GetConnectorCurrent we need to force a
+ * redetection here.
+ */
+ temp = drmModeGetConnector(drm_fd, output->config.connector->connector_id);
+ drmModeFreeConnector(temp);
+}
diff --git a/lib/i915/i915_dp.h b/lib/i915/i915_dp.h
new file mode 100644
index 000000000000..2bcc9e2cb031
--- /dev/null
+++ b/lib/i915/i915_dp.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: MIT */
+
+#ifndef _I915_DP_H_
+#define _I915_DP_H_
+
+#include "igt_kms.h"
+
+int igt_get_current_link_rate(int drm_fd, igt_output_t *output);
+int igt_get_current_lane_count(int drm_fd, igt_output_t *output);
+int igt_get_max_link_rate(int drm_fd, igt_output_t *output);
+int igt_get_max_lane_count(int drm_fd, igt_output_t *output);
+void igt_force_link_retrain(int drm_fd, igt_output_t *output, int retrain_count);
+void igt_force_lt_failure(int drm_fd, igt_output_t *output, int failure_count);
+bool igt_get_dp_link_retrain_disabled(int drm_fd, igt_output_t *output);
+bool igt_has_force_link_training_failure_debugfs(int drmfd, igt_output_t *output);
+int igt_get_dp_pending_lt_failures(int drm_fd, igt_output_t *output);
+int igt_get_dp_pending_retrain(int drm_fd, igt_output_t *output);
+void igt_reset_link_params(int drm_fd, igt_output_t *output);
+void igt_set_link_params(int drm_fd, igt_output_t *output,
+ char *link_rate, char *lane_count);
+
+#endif
diff --git a/lib/igt_kms.c b/lib/igt_kms.c
index 064d5b35aa54..2f1f5cf4a5f3 100644
--- a/lib/igt_kms.c
+++ b/lib/igt_kms.c
@@ -7461,316 +7461,6 @@ int igt_crtc_num_scalers(igt_crtc_t *crtc)
return num_scalers;
}
-/**
- * igt_parse_marked_value:
- * @buf: Buffer containing the content to parse
- * @marked_char: The character marking the value to parse
- * @result: Pointer to store the parsed value
- *
- * Finds the integer value in the buffer that is marked by the given character.
- *
- * Returns: 0 on success, -1 on failure
- */
-static int igt_parse_marked_value(const char *buf, char marked_char, int *result)
-{
- char *marked_ptr, *val_ptr;
-
- /*
- * Look for the marked character
- */
- marked_ptr = strchr(buf, marked_char);
-
- if (marked_ptr) {
- val_ptr = marked_ptr - 1;
- while (val_ptr > buf && isdigit(*val_ptr))
- val_ptr--;
- val_ptr++;
- if (sscanf(val_ptr, "%d", result) == 1)
- return 0;
- }
- return -1;
-}
-
-/**
- * igt_get_current_link_rate:
- * @drm_fd: A drm file descriptor
- * @output: Target output
- *
- * Returns: link_rate if set for output else -1
- */
-int igt_get_current_link_rate(int drm_fd, igt_output_t *output)
-{
- char buf[512];
- int res, ret;
-
- res = igt_debugfs_read_connector_file(drm_fd, output->name,
- "i915_dp_force_link_rate",
- buf, sizeof(buf));
- igt_assert_f(res == 0, "Unable to read %s/i915_dp_force_link_rate\n",
- output->name);
- res = igt_parse_marked_value(buf, '*', &ret);
- igt_assert_f(res == 0, "Output %s not enabled\n", output->name);
- return ret;
-}
-
-/**
- * igt_get_current_lane_count:
- * @drm_fd: A drm file descriptor
- * @output: Target output
- *
- * Returns: lane_count if set for output else -1
- */
-int igt_get_current_lane_count(int drm_fd, igt_output_t *output)
-{
- char buf[512];
- int res, ret;
-
- res = igt_debugfs_read_connector_file(drm_fd, output->name,
- "i915_dp_force_lane_count",
- buf, sizeof(buf));
- igt_assert_f(res == 0, "Unable to read %s/i915_dp_force_lane_count\n",
- output->name);
- res = igt_parse_marked_value(buf, '*', &ret);
- igt_assert_f(res == 0, "Output %s not enabled\n", output->name);
- return ret;
-}
-
-/**
- * igt_get_max_link_rate:
- * @drm_fd: A drm file descriptor
- * @output: Target output
- *
- * Returns: max_link_rate
- */
-int igt_get_max_link_rate(int drm_fd, igt_output_t *output)
-{
- char buf[512];
- int res, ret;
-
- res = igt_debugfs_read_connector_file(drm_fd, output->name,
- "i915_dp_max_link_rate",
- buf, sizeof(buf));
- igt_assert_f(res == 0, "Unable to read %s/i915_dp_max_link_rate\n",
- output->name);
-
- sscanf(buf, "%d", &ret);
- return ret;
-}
-
-/**
- * igt_get_max_link_rate:
- * @drm_fd: A drm file descriptor
- * @output: Target output
- *
- * Returns: max_link_rate
- */
-int igt_get_max_lane_count(int drm_fd, igt_output_t *output)
-{
- char buf[512];
- int res, ret;
-
- res = igt_debugfs_read_connector_file(drm_fd, output->name,
- "i915_dp_max_lane_count",
- buf, sizeof(buf));
- igt_assert_f(res == 0, "Unable to read %s/i915_dp_max_lane_count\n",
- output->name);
-
- sscanf(buf, "%d", &ret);
- return ret;
-}
-
-/**
- * igt_force_link_retrain:
- * @drm_fd: A drm file descriptor
- * @output: Target output
- * @retrain_count: number of retraining required
- *
- * Force link retrain on the output.
- */
-void igt_force_link_retrain(int drm_fd, igt_output_t *output, int retrain_count)
-{
- char value[2];
- int res;
-
- snprintf(value, sizeof(value), "%d", retrain_count);
- res = igt_debugfs_write_connector_file(drm_fd, output->name,
- "i915_dp_force_link_retrain",
- value, strlen(value));
- igt_assert_f(res == 0, "Unable to write to %s/i915_dp_force_link_retrain\n",
- output->name);
-}
-
-/**
- * igt_force_lt_failure:
- * @drm_fd: A drm file descriptor
- * @output: Target output
- * @failure_count: 1 for same link param and
- * 2 for reduced link params
- *
- * Force link training failure on the output.
- * @failure_count: 1 for retraining with same link params
- * 2 for retraining with reduced link params
- */
-void igt_force_lt_failure(int drm_fd, igt_output_t *output, int failure_count)
-{
- char value[2];
- int res;
-
- snprintf(value, sizeof(value), "%d", failure_count);
- res = igt_debugfs_write_connector_file(drm_fd, output->name,
- "i915_dp_force_link_training_failure",
- value, strlen(value));
- igt_assert_f(res == 0, "Unable to write to %s/i915_dp_force_link_training_failure\n",
- output->name);
-}
-
-/**
- * igt_get_dp_link_retrain_disabled:
- * @drm_fd: A drm file descriptor
- * @output: Target output
- *
- * Returns: True if link retrain disabled, false otherwise
- */
-bool igt_get_dp_link_retrain_disabled(int drm_fd, igt_output_t *output)
-{
- char buf[512];
- int res;
-
- res = igt_debugfs_read_connector_file(drm_fd, output->name,
- "i915_dp_link_retrain_disabled",
- buf, sizeof(buf));
- igt_assert_f(res == 0, "Unable to read %s/i915_dp_link_retrain_disabled\n",
- output->name);
- return strstr(buf, "yes");
-}
-
-/**
- * Checks if the force link training failure debugfs
- * is available for a specific output.
- *
- * @drmfd: file descriptor of the DRM device.
- * @output: output to check.
- * Returns:
- * true if the debugfs is available, false otherwise.
- */
-bool igt_has_force_link_training_failure_debugfs(int drmfd, igt_output_t *output)
-{
- char buf[512];
- int res;
-
- res = igt_debugfs_read_connector_file(drmfd, output->name,
- "i915_dp_link_retrain_disabled",
- buf, sizeof(buf));
- return res == 0;
-}
-
-/**
- * igt_get_dp_pending_lt_failures:
- * @drm_fd: A drm file descriptor
- * @output: Target output
- *
- * Returns: Number of pending link training failures.
- */
-int igt_get_dp_pending_lt_failures(int drm_fd, igt_output_t *output)
-{
- char buf[512];
- int res, ret;
-
- res = igt_debugfs_read_connector_file(drm_fd, output->name,
- "i915_dp_force_link_training_failure",
- buf, sizeof(buf));
- igt_assert_f(res == 0, "Unable to read %s/i915_dp_force_link_training_failure\n",
- output->name);
- sscanf(buf, "%d", &ret);
- return ret;
-}
-
-/**
- * igt_dp_pending_retrain:
- * @drm_fd: A drm file descriptor
- * @output: Target output
- *
- * Returns: Number of pending link retrains.
- */
-int igt_get_dp_pending_retrain(int drm_fd, igt_output_t *output)
-{
- char buf[512];
- int res, ret;
-
- res = igt_debugfs_read_connector_file(drm_fd, output->name,
- "i915_dp_force_link_retrain",
- buf, sizeof(buf));
- igt_assert_f(res == 0, "Unable to read %s/i915_dp_force_link_retrain\n",
- output->name);
- sscanf(buf, "%d", &ret);
- return ret;
-}
-
-/**
- * igt_reset_link_params:
- * @drm_fd: A drm file descriptor
- * @output: Target output
- *
- * Reset link rate and lane count to auto, also installs exit handler
- * to set link rate and lane count to auto on exit
- */
-void igt_reset_link_params(int drm_fd, igt_output_t *output)
-{
- bool valid;
- drmModeConnector *temp;
-
- valid = true;
- valid = valid && connector_attr_set_debugfs(drm_fd, output->config.connector,
- "i915_dp_force_link_rate",
- "auto", "auto", true);
- valid = valid && connector_attr_set_debugfs(drm_fd, output->config.connector,
- "i915_dp_force_lane_count",
- "auto", "auto", true);
- igt_assert_f(valid, "Unable to set attr or install exit handler\n");
- dump_connector_attrs();
- igt_install_exit_handler(reset_connectors_at_exit);
-
- /*
- * To allow callers to always use GetConnectorCurrent we need to force a
- * redetection here.
- */
- temp = drmModeGetConnector(drm_fd, output->config.connector->connector_id);
- drmModeFreeConnector(temp);
-}
-
-/**
- * igt_set_link_params:
- * @drm_fd: A drm file descriptor
- * @output: Target output
- *
- * set link rate and lane count to given value, also installs exit handler
- * to set link rate and lane count to auto on exit
- */
-void igt_set_link_params(int drm_fd, igt_output_t *output,
- char *link_rate, char *lane_count)
-{
- bool valid;
- drmModeConnector *temp;
-
- valid = true;
- valid = valid && connector_attr_set_debugfs(drm_fd, output->config.connector,
- "i915_dp_force_link_rate",
- link_rate, "auto", true);
- valid = valid && connector_attr_set_debugfs(drm_fd, output->config.connector,
- "i915_dp_force_lane_count",
- lane_count, "auto", true);
- igt_assert_f(valid, "Unable to set attr or install exit handler\n");
- dump_connector_attrs();
- igt_install_exit_handler(reset_connectors_at_exit);
-
- /*
- * To allow callers to always use GetConnectorCurrent we need to force a
- * redetection here.
- */
- temp = drmModeGetConnector(drm_fd, output->config.connector->connector_id);
- drmModeFreeConnector(temp);
-}
-
/**
* igt_backlight_read:
* @result: Pointer to store the result
diff --git a/lib/igt_kms.h b/lib/igt_kms.h
index aa1599592e68..042fac6b21b0 100644
--- a/lib/igt_kms.h
+++ b/lib/igt_kms.h
@@ -1284,19 +1284,6 @@ bool intel_pipe_output_combo_valid(igt_display_t *display);
bool igt_check_output_is_dp_mst(igt_output_t *output);
int igt_get_dp_mst_connector_id(igt_output_t *output);
int igt_crtc_num_scalers(igt_crtc_t *crtc);
-int igt_get_current_lane_count(int drm_fd, igt_output_t *output);
-int igt_get_current_link_rate(int drm_fd, igt_output_t *output);
-int igt_get_max_link_rate(int drm_fd, igt_output_t *output);
-int igt_get_max_lane_count(int drm_fd, igt_output_t *output);
-void igt_force_link_retrain(int drm_fd, igt_output_t *output, int retrain_count);
-void igt_force_lt_failure(int drm_fd, igt_output_t *output, int failure_count);
-bool igt_get_dp_link_retrain_disabled(int drm_fd, igt_output_t *output);
-bool igt_has_force_link_training_failure_debugfs(int drmfd, igt_output_t *output);
-int igt_get_dp_pending_lt_failures(int drm_fd, igt_output_t *output);
-int igt_get_dp_pending_retrain(int drm_fd, igt_output_t *output);
-void igt_reset_link_params(int drm_fd, igt_output_t *output);
-void igt_set_link_params(int drm_fd, igt_output_t *output,
- char *link_rate, char *lane_count);
int igt_backlight_read(int *result, const char *fname, igt_backlight_context_t *context);
int igt_backlight_write(int value, const char *fname, igt_backlight_context_t *context);
uint32_t igt_get_connected_output_count(igt_display_t *display);
diff --git a/lib/meson.build b/lib/meson.build
index 268f3238cbfa..17cbed2a0faa 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -16,6 +16,7 @@ lib_sources = [
'i915/intel_fbc.c',
'i915/intel_memory_region.c',
'i915/i915_crc.c',
+ 'i915/i915_dp.c',
'igt_collection.c',
'igt_color_encoding.c',
'igt_configfs.c',
diff --git a/tests/intel/kms_dp_link_training.c b/tests/intel/kms_dp_link_training.c
index d859db64b4ff..57d31934a7ac 100644
--- a/tests/intel/kms_dp_link_training.c
+++ b/tests/intel/kms_dp_link_training.c
@@ -21,6 +21,7 @@
* Description: Test we can drive non-UHBR rates over MST.
*/
+#include "i915/i915_dp.h"
#include "igt.h"
#include "igt_kms.h"
#include "intel/kms_joiner_helper.h"
diff --git a/tests/intel/kms_dp_linktrain_fallback.c b/tests/intel/kms_dp_linktrain_fallback.c
index 91dde2a7dcee..bb779b52564b 100644
--- a/tests/intel/kms_dp_linktrain_fallback.c
+++ b/tests/intel/kms_dp_linktrain_fallback.c
@@ -12,6 +12,7 @@
*/
#include <sys/types.h>
+#include "i915/i915_dp.h"
#include "igt_sysfs.h"
#include "igt.h"
#include "kms_mst_helper.h"
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* [PATCH i-g-t v9 38/49] lib/i915/dp: Rename functions to avoid confusion
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (36 preceding siblings ...)
2026-03-16 16:17 ` [PATCH i-g-t v9 37/49] lib/i915/dp: Move DP-related function for i915 to proper folder Louis Chauvet
@ 2026-03-16 16:17 ` Louis Chauvet
2026-03-16 16:18 ` [PATCH i-g-t v9 39/49] lib/i915/dp: Add helper to get maximum supported rate Louis Chauvet
` (11 subsequent siblings)
49 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:17 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
Add the proper prefix for i915 dp-related functions.
This will avoid confusion when using them.
Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
lib/i915/i915_dp.c | 57 +++++++++++++++++----------------
lib/i915/i915_dp.h | 26 +++++++--------
tests/intel/kms_dp_link_training.c | 18 +++++------
tests/intel/kms_dp_linktrain_fallback.c | 56 ++++++++++++++++----------------
4 files changed, 79 insertions(+), 78 deletions(-)
diff --git a/lib/i915/i915_dp.c b/lib/i915/i915_dp.c
index 024fe3b00210..48a69e9ff283 100644
--- a/lib/i915/i915_dp.c
+++ b/lib/i915/i915_dp.c
@@ -35,7 +35,7 @@
#include "igt_core.h"
/**
- * igt_parse_marked_value:
+ * i915_dp_parse_marked_value:
* @buf: Buffer containing the content to parse
* @marked_char: The character marking the value to parse
* @result: Pointer to store the parsed value
@@ -44,7 +44,7 @@
*
* Returns: 0 on success, -1 on failure
*/
-static int igt_parse_marked_value(const char *buf, char marked_char, int *result)
+static int i915_dp_parse_marked_value(const char *buf, char marked_char, int *result)
{
char *marked_ptr, *val_ptr;
@@ -65,13 +65,13 @@ static int igt_parse_marked_value(const char *buf, char marked_char, int *result
}
/**
- * igt_get_current_link_rate:
+ * i915_dp_get_current_link_rate:
* @drm_fd: A drm file descriptor
* @output: Target output
*
* Returns: link_rate if set for output else -1
*/
-int igt_get_current_link_rate(int drm_fd, igt_output_t *output)
+int i915_dp_get_current_link_rate(int drm_fd, igt_output_t *output)
{
char buf[512];
int res, ret;
@@ -81,19 +81,19 @@ int igt_get_current_link_rate(int drm_fd, igt_output_t *output)
buf, sizeof(buf));
igt_assert_f(res == 0, "Unable to read %s/i915_dp_force_link_rate\n",
output->name);
- res = igt_parse_marked_value(buf, '*', &ret);
+ res = i915_dp_parse_marked_value(buf, '*', &ret);
igt_assert_f(res == 0, "Output %s not enabled\n", output->name);
return ret;
}
/**
- * igt_get_current_lane_count:
+ * i915_dp_get_current_lane_count:
* @drm_fd: A drm file descriptor
* @output: Target output
*
* Returns: lane_count if set for output else -1
*/
-int igt_get_current_lane_count(int drm_fd, igt_output_t *output)
+int i915_dp_get_current_lane_count(int drm_fd, igt_output_t *output)
{
char buf[512];
int res, ret;
@@ -103,19 +103,19 @@ int igt_get_current_lane_count(int drm_fd, igt_output_t *output)
buf, sizeof(buf));
igt_assert_f(res == 0, "Unable to read %s/i915_dp_force_lane_count\n",
output->name);
- res = igt_parse_marked_value(buf, '*', &ret);
+ res = i915_dp_parse_marked_value(buf, '*', &ret);
igt_assert_f(res == 0, "Output %s not enabled\n", output->name);
return ret;
}
/**
- * igt_get_max_link_rate:
+ * i915_dp_get_max_link_rate:
* @drm_fd: A drm file descriptor
* @output: Target output
*
* Returns: max_link_rate
*/
-int igt_get_max_link_rate(int drm_fd, igt_output_t *output)
+int i915_dp_get_max_link_rate(int drm_fd, igt_output_t *output)
{
char buf[512];
int res, ret;
@@ -131,13 +131,13 @@ int igt_get_max_link_rate(int drm_fd, igt_output_t *output)
}
/**
- * igt_get_max_link_rate:
+ * i915_dp_get_max_lane_count:
* @drm_fd: A drm file descriptor
* @output: Target output
*
* Returns: max_link_rate
*/
-int igt_get_max_lane_count(int drm_fd, igt_output_t *output)
+int i915_dp_get_max_lane_count(int drm_fd, igt_output_t *output)
{
char buf[512];
int res, ret;
@@ -153,14 +153,14 @@ int igt_get_max_lane_count(int drm_fd, igt_output_t *output)
}
/**
- * igt_force_link_retrain:
+ * i915_dp_force_link_retrain:
* @drm_fd: A drm file descriptor
* @output: Target output
* @retrain_count: number of retraining required
*
* Force link retrain on the output.
*/
-void igt_force_link_retrain(int drm_fd, igt_output_t *output, int retrain_count)
+void i915_dp_force_link_retrain(int drm_fd, igt_output_t *output, int retrain_count)
{
char value[2];
int res;
@@ -174,7 +174,7 @@ void igt_force_link_retrain(int drm_fd, igt_output_t *output, int retrain_count)
}
/**
- * igt_force_lt_failure:
+ * i915_dp_force_lt_failure:
* @drm_fd: A drm file descriptor
* @output: Target output
* @failure_count: 1 for same link param and
@@ -184,7 +184,7 @@ void igt_force_link_retrain(int drm_fd, igt_output_t *output, int retrain_count)
* @failure_count: 1 for retraining with same link params
* 2 for retraining with reduced link params
*/
-void igt_force_lt_failure(int drm_fd, igt_output_t *output, int failure_count)
+void i915_dp_force_lt_failure(int drm_fd, igt_output_t *output, int failure_count)
{
char value[2];
int res;
@@ -198,13 +198,13 @@ void igt_force_lt_failure(int drm_fd, igt_output_t *output, int failure_count)
}
/**
- * igt_get_dp_link_retrain_disabled:
+ * i915_dp_get_link_retrain_disabled:
* @drm_fd: A drm file descriptor
* @output: Target output
*
* Returns: True if link retrain disabled, false otherwise
*/
-bool igt_get_dp_link_retrain_disabled(int drm_fd, igt_output_t *output)
+bool i915_dp_get_link_retrain_disabled(int drm_fd, igt_output_t *output)
{
char buf[512];
int res;
@@ -218,6 +218,7 @@ bool igt_get_dp_link_retrain_disabled(int drm_fd, igt_output_t *output)
}
/**
+ * i915_dp_has_force_link_training_failure_debugfs:
* Checks if the force link training failure debugfs
* is available for a specific output.
*
@@ -226,7 +227,7 @@ bool igt_get_dp_link_retrain_disabled(int drm_fd, igt_output_t *output)
* Returns:
* true if the debugfs is available, false otherwise.
*/
-bool igt_has_force_link_training_failure_debugfs(int drmfd, igt_output_t *output)
+bool i915_dp_has_force_link_training_failure_debugfs(int drmfd, igt_output_t *output)
{
char buf[512];
int res;
@@ -238,13 +239,13 @@ bool igt_has_force_link_training_failure_debugfs(int drmfd, igt_output_t *output
}
/**
- * igt_get_dp_pending_lt_failures:
+ * i915_dp_get_pending_lt_failures:
* @drm_fd: A drm file descriptor
* @output: Target output
*
* Returns: Number of pending link training failures.
*/
-int igt_get_dp_pending_lt_failures(int drm_fd, igt_output_t *output)
+int i915_dp_get_pending_lt_failures(int drm_fd, igt_output_t *output)
{
char buf[512];
int res, ret;
@@ -259,13 +260,13 @@ int igt_get_dp_pending_lt_failures(int drm_fd, igt_output_t *output)
}
/**
- * igt_dp_pending_retrain:
+ * i915_dp_get_pending_retrain:
* @drm_fd: A drm file descriptor
* @output: Target output
*
* Returns: Number of pending link retrains.
*/
-int igt_get_dp_pending_retrain(int drm_fd, igt_output_t *output)
+int i915_dp_get_pending_retrain(int drm_fd, igt_output_t *output)
{
char buf[512];
int res, ret;
@@ -280,14 +281,14 @@ int igt_get_dp_pending_retrain(int drm_fd, igt_output_t *output)
}
/**
- * igt_reset_link_params:
+ * i915_dp_reset_link_params:
* @drm_fd: A drm file descriptor
* @output: Target output
*
* Reset link rate and lane count to auto, also installs exit handler
* to set link rate and lane count to auto on exit
*/
-void igt_reset_link_params(int drm_fd, igt_output_t *output)
+void i915_dp_reset_link_params(int drm_fd, igt_output_t *output)
{
bool valid;
drmModeConnector *temp;
@@ -312,15 +313,15 @@ void igt_reset_link_params(int drm_fd, igt_output_t *output)
}
/**
- * igt_set_link_params:
+ * i915_dp_set_link_params:
* @drm_fd: A drm file descriptor
* @output: Target output
*
* set link rate and lane count to given value, also installs exit handler
* to set link rate and lane count to auto on exit
*/
-void igt_set_link_params(int drm_fd, igt_output_t *output,
- char *link_rate, char *lane_count)
+void i915_dp_set_link_params(int drm_fd, igt_output_t *output,
+ char *link_rate, char *lane_count)
{
bool valid;
drmModeConnector *temp;
diff --git a/lib/i915/i915_dp.h b/lib/i915/i915_dp.h
index 2bcc9e2cb031..e4249d179543 100644
--- a/lib/i915/i915_dp.h
+++ b/lib/i915/i915_dp.h
@@ -5,18 +5,18 @@
#include "igt_kms.h"
-int igt_get_current_link_rate(int drm_fd, igt_output_t *output);
-int igt_get_current_lane_count(int drm_fd, igt_output_t *output);
-int igt_get_max_link_rate(int drm_fd, igt_output_t *output);
-int igt_get_max_lane_count(int drm_fd, igt_output_t *output);
-void igt_force_link_retrain(int drm_fd, igt_output_t *output, int retrain_count);
-void igt_force_lt_failure(int drm_fd, igt_output_t *output, int failure_count);
-bool igt_get_dp_link_retrain_disabled(int drm_fd, igt_output_t *output);
-bool igt_has_force_link_training_failure_debugfs(int drmfd, igt_output_t *output);
-int igt_get_dp_pending_lt_failures(int drm_fd, igt_output_t *output);
-int igt_get_dp_pending_retrain(int drm_fd, igt_output_t *output);
-void igt_reset_link_params(int drm_fd, igt_output_t *output);
-void igt_set_link_params(int drm_fd, igt_output_t *output,
- char *link_rate, char *lane_count);
+int i915_dp_get_current_link_rate(int drm_fd, igt_output_t *output);
+int i915_dp_get_current_lane_count(int drm_fd, igt_output_t *output);
+int i915_dp_get_max_link_rate(int drm_fd, igt_output_t *output);
+int i915_dp_get_max_lane_count(int drm_fd, igt_output_t *output);
+void i915_dp_force_link_retrain(int drm_fd, igt_output_t *output, int retrain_count);
+void i915_dp_force_lt_failure(int drm_fd, igt_output_t *output, int failure_count);
+bool i915_dp_get_link_retrain_disabled(int drm_fd, igt_output_t *output);
+bool i915_dp_has_force_link_training_failure_debugfs(int drmfd, igt_output_t *output);
+int i915_dp_get_pending_lt_failures(int drm_fd, igt_output_t *output);
+int i915_dp_get_pending_retrain(int drm_fd, igt_output_t *output);
+void i915_dp_reset_link_params(int drm_fd, igt_output_t *output);
+void i915_dp_set_link_params(int drm_fd, igt_output_t *output,
+ char *link_rate, char *lane_count);
#endif
diff --git a/tests/intel/kms_dp_link_training.c b/tests/intel/kms_dp_link_training.c
index 57d31934a7ac..2e4bed9c4626 100644
--- a/tests/intel/kms_dp_link_training.c
+++ b/tests/intel/kms_dp_link_training.c
@@ -201,20 +201,20 @@ static bool run_link_rate_test(data_t *data, bool mst, bool uhbr)
char lane_str[32];
igt_display_reset(&data->display);
- igt_reset_link_params(data->drm_fd, data->output);
+ i915_dp_reset_link_params(data->drm_fd, data->output);
do_modeset(data, mst);
/* Retrain at default/driver parameters */
- igt_force_link_retrain(data->drm_fd, data->output, RETRAIN_COUNT);
+ i915_dp_force_link_retrain(data->drm_fd, data->output, RETRAIN_COUNT);
igt_assert_eq(check_condition_with_timeout(data->drm_fd, data->output,
- igt_get_dp_pending_retrain,
+ i915_dp_get_pending_retrain,
1.0, 20.0), 0);
assert_link_status_good(data, mst);
/* FIXME : Driver may lie max link rate or max lane count */
/* Read max_link_rate and max_lane_count */
- max_link_rate = igt_get_max_link_rate(data->drm_fd, data->output);
- max_lane_count = igt_get_max_lane_count(data->drm_fd, data->output);
+ max_link_rate = i915_dp_get_max_link_rate(data->drm_fd, data->output);
+ max_lane_count = i915_dp_get_max_lane_count(data->drm_fd, data->output);
/* Check sink supports uhbr or not */
is_uhbr_output = (max_link_rate >= UHBR_LINK_RATE);
@@ -233,14 +233,14 @@ static bool run_link_rate_test(data_t *data, bool mst, bool uhbr)
data->output->name, rate_str, max_lane_count);
/* Force retrain at max link params */
- igt_set_link_params(data->drm_fd, data->output, rate_str, lane_str);
- igt_force_link_retrain(data->drm_fd, data->output, RETRAIN_COUNT);
+ i915_dp_set_link_params(data->drm_fd, data->output, rate_str, lane_str);
+ i915_dp_force_link_retrain(data->drm_fd, data->output, RETRAIN_COUNT);
igt_assert_eq(check_condition_with_timeout(data->drm_fd, data->output,
- igt_get_dp_pending_retrain,
+ i915_dp_get_pending_retrain,
1.0, 20.0), 0);
assert_link_status_good(data, mst);
- current_link_rate = igt_get_current_link_rate(data->drm_fd, data->output);
+ current_link_rate = i915_dp_get_current_link_rate(data->drm_fd, data->output);
igt_info("Current link rate is %d\n", current_link_rate);
igt_assert_f(current_link_rate == max_link_rate,
"Link training did not succeed at max link rate.\n");
diff --git a/tests/intel/kms_dp_linktrain_fallback.c b/tests/intel/kms_dp_linktrain_fallback.c
index bb779b52564b..68a79b117cd2 100644
--- a/tests/intel/kms_dp_linktrain_fallback.c
+++ b/tests/intel/kms_dp_linktrain_fallback.c
@@ -242,12 +242,12 @@ static bool force_failure_and_wait(data_t *data,
double interval,
double timeout)
{
- igt_force_lt_failure(data->drm_fd, output, failure_type);
- igt_force_link_retrain(data->drm_fd, output, retrain_count);
+ i915_dp_force_lt_failure(data->drm_fd, output, failure_type);
+ i915_dp_force_link_retrain(data->drm_fd, output, retrain_count);
/* Wait until there's no pending retrain */
if (check_condition_with_timeout(data->drm_fd, output,
- igt_get_dp_pending_retrain,
+ i915_dp_get_pending_retrain,
interval, timeout)) {
igt_info("Timed out waiting for pending retrain\n");
return false;
@@ -255,7 +255,7 @@ static bool force_failure_and_wait(data_t *data,
/* Wait until there's no pending LT failures */
if (check_condition_with_timeout(data->drm_fd, output,
- igt_get_dp_pending_lt_failures,
+ i915_dp_get_pending_lt_failures,
interval, timeout)) {
igt_info("Timed out waiting for pending LT failures\n");
return false;
@@ -355,7 +355,7 @@ static void test_fallback(data_t *data, bool is_mst)
retries = SPURIOUS_HPD_RETRY;
igt_display_reset(&data->display);
- igt_reset_link_params(data->drm_fd, data->output);
+ i915_dp_reset_link_params(data->drm_fd, data->output);
if (!setup_outputs(data, is_mst, outputs,
&output_count, modes, fbs,
primaries))
@@ -363,13 +363,13 @@ static void test_fallback(data_t *data, bool is_mst)
igt_info("Testing link training fallback on %s\n",
igt_output_name(data->output));
- max_link_rate = igt_get_max_link_rate(data->drm_fd, data->output);
- max_lane_count = igt_get_max_lane_count(data->drm_fd, data->output);
- prev_link_rate = igt_get_current_link_rate(data->drm_fd, data->output);
- prev_lane_count = igt_get_current_lane_count(data->drm_fd, data->output);
+ max_link_rate = i915_dp_get_max_link_rate(data->drm_fd, data->output);
+ max_lane_count = i915_dp_get_max_lane_count(data->drm_fd, data->output);
+ prev_link_rate = i915_dp_get_current_link_rate(data->drm_fd, data->output);
+ prev_lane_count = i915_dp_get_current_lane_count(data->drm_fd, data->output);
- while (!igt_get_dp_link_retrain_disabled(data->drm_fd,
- data->output)) {
+ while (!i915_dp_get_link_retrain_disabled(data->drm_fd,
+ data->output)) {
igt_info("Current link rate: %d, Current lane count: %d\n",
prev_link_rate,
prev_lane_count);
@@ -381,8 +381,8 @@ static void test_fallback(data_t *data, bool is_mst)
1.0, 20.0),
"Link training failure steps timed out\n");
- if (igt_get_dp_link_retrain_disabled(data->drm_fd,
- data->output)) {
+ if (i915_dp_get_link_retrain_disabled(data->drm_fd,
+ data->output)) {
igt_reset_connectors();
return;
}
@@ -402,8 +402,8 @@ static void test_fallback(data_t *data, bool is_mst)
fbs,
primaries), "modeset failed\n");
igt_assert_eq(data->output->values[IGT_CONNECTOR_LINK_STATUS], DRM_MODE_LINK_STATUS_GOOD);
- curr_link_rate = igt_get_current_link_rate(data->drm_fd, data->output);
- curr_lane_count = igt_get_current_lane_count(data->drm_fd, data->output);
+ curr_link_rate = i915_dp_get_current_link_rate(data->drm_fd, data->output);
+ curr_lane_count = i915_dp_get_current_lane_count(data->drm_fd, data->output);
igt_debug("Fallback state: prev %dx%d, curr %dx%d, max %dx%d, retries=%u\n",
prev_link_rate, prev_lane_count,
@@ -428,8 +428,8 @@ static bool run_lt_fallback_test(data_t *data)
for_each_connected_output(&data->display, output) {
data->output = output;
- if (!igt_has_force_link_training_failure_debugfs(data->drm_fd,
- data->output)) {
+ if (!i915_dp_has_force_link_training_failure_debugfs(data->drm_fd,
+ data->output)) {
igt_info("Output %s doesn't support forcing link training failure\n",
igt_output_name(data->output));
continue;
@@ -472,8 +472,8 @@ static void test_dsc_sst_fallback(data_t *data)
data->crtc = igt_first_crtc(&data->display);
igt_display_reset(&data->display);
- igt_reset_link_params(data->drm_fd, data->output);
- igt_force_link_retrain(data->drm_fd, data->output, RETRAIN_COUNT);
+ i915_dp_reset_link_params(data->drm_fd, data->output);
+ i915_dp_force_link_retrain(data->drm_fd, data->output, RETRAIN_COUNT);
/* Find a mode that doesn't require DSC initially */
for_each_connector_mode(data->output) {
@@ -509,8 +509,8 @@ static void test_dsc_sst_fallback(data_t *data)
igt_info("Found mode %dx%d@%d %s that doesn't need DSC with link rate %d and lane count %d\n",
non_dsc_mode->hdisplay, non_dsc_mode->vdisplay,
non_dsc_mode->vrefresh, non_dsc_mode->name,
- igt_get_current_link_rate(data->drm_fd, data->output),
- igt_get_current_lane_count(data->drm_fd, data->output));
+ i915_dp_get_current_link_rate(data->drm_fd, data->output),
+ i915_dp_get_current_lane_count(data->drm_fd, data->output));
non_dsc_mode_found = true;
break;
}
@@ -520,7 +520,7 @@ static void test_dsc_sst_fallback(data_t *data)
igt_output_name(data->output));
/* Repeatedly force link failure until DSC is required (or link is disabled) */
- while (!igt_get_dp_link_retrain_disabled(data->drm_fd, data->output)) {
+ while (!i915_dp_get_link_retrain_disabled(data->drm_fd, data->output)) {
mon = igt_watch_uevents();
igt_assert_f(force_failure_and_wait(data, data->output,
@@ -528,8 +528,8 @@ static void test_dsc_sst_fallback(data_t *data)
RETRAIN_COUNT, 1.0, 20.0),
"Forcing DSC fallback timed out\n");
- if (igt_get_dp_link_retrain_disabled(data->drm_fd,
- data->output)) {
+ if (i915_dp_get_link_retrain_disabled(data->drm_fd,
+ data->output)) {
igt_reset_connectors();
igt_flush_uevents(mon);
return;
@@ -553,8 +553,8 @@ static void test_dsc_sst_fallback(data_t *data)
igt_info("mode %dx%d@%d now requires DSC with link rate %d and lane count %d\n",
mode_to_check->hdisplay, mode_to_check->vdisplay,
mode_to_check->vrefresh,
- igt_get_current_link_rate(data->drm_fd, data->output),
- igt_get_current_lane_count(data->drm_fd, data->output));
+ i915_dp_get_current_link_rate(data->drm_fd, data->output),
+ i915_dp_get_current_lane_count(data->drm_fd, data->output));
igt_info("DSC fallback successful on %s\n",
igt_output_name(data->output));
dsc_fallback_successful = true;
@@ -581,8 +581,8 @@ static bool run_dsc_sst_fallaback_test(data_t *data)
for_each_connected_output(&data->display, output) {
data->output = output;
- if (!igt_has_force_link_training_failure_debugfs(data->drm_fd,
- data->output)) {
+ if (!i915_dp_has_force_link_training_failure_debugfs(data->drm_fd,
+ data->output)) {
igt_info("Output %s doesn't support forcing link training.\n",
igt_output_name(data->output));
continue;
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* [PATCH i-g-t v9 39/49] lib/i915/dp: Add helper to get maximum supported rate
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (37 preceding siblings ...)
2026-03-16 16:17 ` [PATCH i-g-t v9 38/49] lib/i915/dp: Rename functions to avoid confusion Louis Chauvet
@ 2026-03-16 16:18 ` Louis Chauvet
2026-03-16 16:18 ` [PATCH i-g-t v9 40/49] lib/i915/dp: Properly check sscanf results Louis Chauvet
` (10 subsequent siblings)
49 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:18 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
Some GPU can't reach all the DP rates. To avoid false positive during
testing with unigraf, add an helper to filter tests based on the maximum
supported rate of the current GPU.
Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
lib/i915/i915_dp.c | 35 +++++++++++++++++++++++++++++++++++
lib/i915/i915_dp.h | 1 +
2 files changed, 36 insertions(+)
diff --git a/lib/i915/i915_dp.c b/lib/i915/i915_dp.c
index 48a69e9ff283..da3dd05c3293 100644
--- a/lib/i915/i915_dp.c
+++ b/lib/i915/i915_dp.c
@@ -28,7 +28,9 @@
*/
#include <ctype.h>
+#include <errno.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include "i915_dp.h"
@@ -344,3 +346,36 @@ void i915_dp_set_link_params(int drm_fd, igt_output_t *output,
temp = drmModeGetConnector(drm_fd, output->config.connector->connector_id);
drmModeFreeConnector(temp);
}
+
+/**
+ * i915_dp_get_max_supported_rate:
+ * @drm_fd: A drm file descriptor
+ * @output: Target output
+ *
+ * Returns: Max supported link rate available for output, else -1
+ */
+int i915_dp_get_max_supported_rate(int drm_fd, const igt_output_t *output)
+{
+ char buf[512];
+ int res, max_rate = -EINVAL;
+ char *token;
+
+ res = igt_debugfs_read_connector_file(drm_fd, igt_output_name(output),
+ "i915_dp_force_link_rate",
+ buf, sizeof(buf));
+ igt_assert_f(!res, "Unable to read %s/i915_dp_force_link_rate\n",
+ igt_output_name(output));
+
+ token = strtok(buf, " ");
+ while (token) {
+ int rate;
+
+ errno = 0;
+ rate = strtol(token, NULL, 0);
+ if (!errno && rate > max_rate)
+ max_rate = rate;
+ token = strtok(NULL, " ");
+ }
+
+ return max_rate;
+}
diff --git a/lib/i915/i915_dp.h b/lib/i915/i915_dp.h
index e4249d179543..b13629147804 100644
--- a/lib/i915/i915_dp.h
+++ b/lib/i915/i915_dp.h
@@ -18,5 +18,6 @@ int i915_dp_get_pending_retrain(int drm_fd, igt_output_t *output);
void i915_dp_reset_link_params(int drm_fd, igt_output_t *output);
void i915_dp_set_link_params(int drm_fd, igt_output_t *output,
char *link_rate, char *lane_count);
+int i915_dp_get_max_supported_rate(int drm_fd, const igt_output_t *output);
#endif
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* [PATCH i-g-t v9 40/49] lib/i915/dp: Properly check sscanf results
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (38 preceding siblings ...)
2026-03-16 16:18 ` [PATCH i-g-t v9 39/49] lib/i915/dp: Add helper to get maximum supported rate Louis Chauvet
@ 2026-03-16 16:18 ` Louis Chauvet
2026-03-16 16:18 ` [PATCH i-g-t v9 41/49] lib/i915/dp: Use igt_output_name instead of private field Louis Chauvet
` (9 subsequent siblings)
49 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:18 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
Current sscanf usage can fail silently, add assert to ensure that the
returned value is correct.
Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
lib/i915/i915_dp.c | 18 ++++++++++++++----
1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/lib/i915/i915_dp.c b/lib/i915/i915_dp.c
index da3dd05c3293..c2cddb955d75 100644
--- a/lib/i915/i915_dp.c
+++ b/lib/i915/i915_dp.c
@@ -128,7 +128,9 @@ int i915_dp_get_max_link_rate(int drm_fd, igt_output_t *output)
igt_assert_f(res == 0, "Unable to read %s/i915_dp_max_link_rate\n",
output->name);
- sscanf(buf, "%d", &ret);
+ igt_assert_f(sscanf(buf, "%d", &ret) == 1,
+ "Failed to parse max link rate from %s\n", buf);
+
return ret;
}
@@ -150,7 +152,9 @@ int i915_dp_get_max_lane_count(int drm_fd, igt_output_t *output)
igt_assert_f(res == 0, "Unable to read %s/i915_dp_max_lane_count\n",
output->name);
- sscanf(buf, "%d", &ret);
+ igt_assert_f(sscanf(buf, "%d", &ret) == 1,
+ "Failed to parse max lane count from %s\n", buf);
+
return ret;
}
@@ -257,7 +261,10 @@ int i915_dp_get_pending_lt_failures(int drm_fd, igt_output_t *output)
buf, sizeof(buf));
igt_assert_f(res == 0, "Unable to read %s/i915_dp_force_link_training_failure\n",
output->name);
- sscanf(buf, "%d", &ret);
+
+ igt_assert_f(sscanf(buf, "%d", &ret) == 1,
+ "Failed to parse pending link training failures from %s\n", buf);
+
return ret;
}
@@ -278,7 +285,10 @@ int i915_dp_get_pending_retrain(int drm_fd, igt_output_t *output)
buf, sizeof(buf));
igt_assert_f(res == 0, "Unable to read %s/i915_dp_force_link_retrain\n",
output->name);
- sscanf(buf, "%d", &ret);
+
+ igt_assert_f(sscanf(buf, "%d", &ret) == 1,
+ "Failed to parse pending link retrains from %s\n", buf);
+
return ret;
}
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* [PATCH i-g-t v9 41/49] lib/i915/dp: Use igt_output_name instead of private field
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (39 preceding siblings ...)
2026-03-16 16:18 ` [PATCH i-g-t v9 40/49] lib/i915/dp: Properly check sscanf results Louis Chauvet
@ 2026-03-16 16:18 ` Louis Chauvet
2026-03-16 16:18 ` [PATCH i-g-t v9 42/49] lib/igt_dp: Create generic helpers for DP information Louis Chauvet
` (8 subsequent siblings)
49 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:18 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
output->name is a private field, use the common helper igt_output_name
instead of the field directly.
Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
lib/i915/i915_dp.c | 43 ++++++++++++++++++++++---------------------
1 file changed, 22 insertions(+), 21 deletions(-)
diff --git a/lib/i915/i915_dp.c b/lib/i915/i915_dp.c
index c2cddb955d75..e54058580ff2 100644
--- a/lib/i915/i915_dp.c
+++ b/lib/i915/i915_dp.c
@@ -35,6 +35,7 @@
#include "i915_dp.h"
#include "igt_core.h"
+#include "igt_kms.h"
/**
* i915_dp_parse_marked_value:
@@ -78,13 +79,13 @@ int i915_dp_get_current_link_rate(int drm_fd, igt_output_t *output)
char buf[512];
int res, ret;
- res = igt_debugfs_read_connector_file(drm_fd, output->name,
+ res = igt_debugfs_read_connector_file(drm_fd, igt_output_name(output),
"i915_dp_force_link_rate",
buf, sizeof(buf));
igt_assert_f(res == 0, "Unable to read %s/i915_dp_force_link_rate\n",
- output->name);
+ igt_output_name(output));
res = i915_dp_parse_marked_value(buf, '*', &ret);
- igt_assert_f(res == 0, "Output %s not enabled\n", output->name);
+ igt_assert_f(res == 0, "Output %s not enabled\n", igt_output_name(output));
return ret;
}
@@ -100,13 +101,13 @@ int i915_dp_get_current_lane_count(int drm_fd, igt_output_t *output)
char buf[512];
int res, ret;
- res = igt_debugfs_read_connector_file(drm_fd, output->name,
+ res = igt_debugfs_read_connector_file(drm_fd, igt_output_name(output),
"i915_dp_force_lane_count",
buf, sizeof(buf));
igt_assert_f(res == 0, "Unable to read %s/i915_dp_force_lane_count\n",
- output->name);
+ igt_output_name(output));
res = i915_dp_parse_marked_value(buf, '*', &ret);
- igt_assert_f(res == 0, "Output %s not enabled\n", output->name);
+ igt_assert_f(res == 0, "Output %s not enabled\n", igt_output_name(output));
return ret;
}
@@ -122,11 +123,11 @@ int i915_dp_get_max_link_rate(int drm_fd, igt_output_t *output)
char buf[512];
int res, ret;
- res = igt_debugfs_read_connector_file(drm_fd, output->name,
+ res = igt_debugfs_read_connector_file(drm_fd, igt_output_name(output),
"i915_dp_max_link_rate",
buf, sizeof(buf));
igt_assert_f(res == 0, "Unable to read %s/i915_dp_max_link_rate\n",
- output->name);
+ igt_output_name(output));
igt_assert_f(sscanf(buf, "%d", &ret) == 1,
"Failed to parse max link rate from %s\n", buf);
@@ -146,11 +147,11 @@ int i915_dp_get_max_lane_count(int drm_fd, igt_output_t *output)
char buf[512];
int res, ret;
- res = igt_debugfs_read_connector_file(drm_fd, output->name,
+ res = igt_debugfs_read_connector_file(drm_fd, igt_output_name(output),
"i915_dp_max_lane_count",
buf, sizeof(buf));
igt_assert_f(res == 0, "Unable to read %s/i915_dp_max_lane_count\n",
- output->name);
+ igt_output_name(output));
igt_assert_f(sscanf(buf, "%d", &ret) == 1,
"Failed to parse max lane count from %s\n", buf);
@@ -172,11 +173,11 @@ void i915_dp_force_link_retrain(int drm_fd, igt_output_t *output, int retrain_co
int res;
snprintf(value, sizeof(value), "%d", retrain_count);
- res = igt_debugfs_write_connector_file(drm_fd, output->name,
+ res = igt_debugfs_write_connector_file(drm_fd, igt_output_name(output),
"i915_dp_force_link_retrain",
value, strlen(value));
igt_assert_f(res == 0, "Unable to write to %s/i915_dp_force_link_retrain\n",
- output->name);
+ igt_output_name(output));
}
/**
@@ -196,11 +197,11 @@ void i915_dp_force_lt_failure(int drm_fd, igt_output_t *output, int failure_coun
int res;
snprintf(value, sizeof(value), "%d", failure_count);
- res = igt_debugfs_write_connector_file(drm_fd, output->name,
+ res = igt_debugfs_write_connector_file(drm_fd, igt_output_name(output),
"i915_dp_force_link_training_failure",
value, strlen(value));
igt_assert_f(res == 0, "Unable to write to %s/i915_dp_force_link_training_failure\n",
- output->name);
+ igt_output_name(output));
}
/**
@@ -215,11 +216,11 @@ bool i915_dp_get_link_retrain_disabled(int drm_fd, igt_output_t *output)
char buf[512];
int res;
- res = igt_debugfs_read_connector_file(drm_fd, output->name,
+ res = igt_debugfs_read_connector_file(drm_fd, igt_output_name(output),
"i915_dp_link_retrain_disabled",
buf, sizeof(buf));
igt_assert_f(res == 0, "Unable to read %s/i915_dp_link_retrain_disabled\n",
- output->name);
+ igt_output_name(output));
return strstr(buf, "yes");
}
@@ -238,7 +239,7 @@ bool i915_dp_has_force_link_training_failure_debugfs(int drmfd, igt_output_t *ou
char buf[512];
int res;
- res = igt_debugfs_read_connector_file(drmfd, output->name,
+ res = igt_debugfs_read_connector_file(drmfd, igt_output_name(output),
"i915_dp_link_retrain_disabled",
buf, sizeof(buf));
return res == 0;
@@ -256,11 +257,11 @@ int i915_dp_get_pending_lt_failures(int drm_fd, igt_output_t *output)
char buf[512];
int res, ret;
- res = igt_debugfs_read_connector_file(drm_fd, output->name,
+ res = igt_debugfs_read_connector_file(drm_fd, igt_output_name(output),
"i915_dp_force_link_training_failure",
buf, sizeof(buf));
igt_assert_f(res == 0, "Unable to read %s/i915_dp_force_link_training_failure\n",
- output->name);
+ igt_output_name(output));
igt_assert_f(sscanf(buf, "%d", &ret) == 1,
"Failed to parse pending link training failures from %s\n", buf);
@@ -280,11 +281,11 @@ int i915_dp_get_pending_retrain(int drm_fd, igt_output_t *output)
char buf[512];
int res, ret;
- res = igt_debugfs_read_connector_file(drm_fd, output->name,
+ res = igt_debugfs_read_connector_file(drm_fd, igt_output_name(output),
"i915_dp_force_link_retrain",
buf, sizeof(buf));
igt_assert_f(res == 0, "Unable to read %s/i915_dp_force_link_retrain\n",
- output->name);
+ igt_output_name(output));
igt_assert_f(sscanf(buf, "%d", &ret) == 1,
"Failed to parse pending link retrains from %s\n", buf);
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* [PATCH i-g-t v9 42/49] lib/igt_dp: Create generic helpers for DP information
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (40 preceding siblings ...)
2026-03-16 16:18 ` [PATCH i-g-t v9 41/49] lib/i915/dp: Use igt_output_name instead of private field Louis Chauvet
@ 2026-03-16 16:18 ` Louis Chauvet
2026-03-17 15:55 ` Kory Maincent
2026-03-16 16:18 ` [PATCH i-g-t v9 43/49] lib/igt_kms: Add asserts to avoid null pointer dereference Louis Chauvet
` (7 subsequent siblings)
49 siblings, 1 reply; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:18 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
With the introduction of unigraf tests, there is no reason to limit the
implementation to intel. Create easly extendable generic functions to
help supporting multiple GPU.
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
lib/igt_dp.c | 177 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
lib/igt_dp.h | 30 ++++++++++
lib/meson.build | 1 +
3 files changed, 208 insertions(+)
diff --git a/lib/igt_dp.c b/lib/igt_dp.c
new file mode 100644
index 000000000000..a9c9eb3efede
--- /dev/null
+++ b/lib/igt_dp.c
@@ -0,0 +1,177 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2026 Google
+ *
+ * Authors:
+ * Louis Chauvet <louis.chauvet@bootlin.com>
+ */
+
+#include <stdint.h>
+
+#include "drmtest.h"
+#include "i915/i915_dp.h"
+#include "igt_core.h"
+#include "igt_kms.h"
+#include "igt_dp.h"
+
+/**
+ * igt_dp_get_current_link_rate: Get current link rate on a display port
+ * @drm_fd: DRM file descriptor
+ * @output: igt_output_t object representing the display port
+ *
+ * Returns:
+ * The current link rate in kb/s, or a negative error code on failure.
+ */
+int igt_dp_get_current_link_rate(int drm_fd, igt_output_t *output)
+{
+ if (is_intel_device(drm_fd))
+ /*
+ * i915_dp_get_current_link_rate returns the value in tens of kb/s because
+ * that what the kernel uses. Convert it to kb/s to have a sane unit...
+ */
+ return i915_dp_get_current_link_rate(drm_fd, output) * 10;
+
+ igt_assert_f(false, "Current drm device is not able to report used link rate\n");
+ return -EINVAL;
+}
+
+/**
+ * igt_dp_get_current_lane_count: Get current lane count on a display port
+ * @drm_fd: DRM file descriptor
+ * @output: igt_output_t object representing the display port
+ *
+ * Returns:
+ * The number of active lanes, or a negative error code on failure.
+ */
+int igt_dp_get_current_lane_count(int drm_fd, igt_output_t *output)
+{
+ if (is_intel_device(drm_fd))
+ return i915_dp_get_current_lane_count(drm_fd, output);
+
+ igt_assert_f(false, "Current drm device is not able to report used lane count\n");
+ return -EINVAL;
+}
+
+/**
+ * igt_dp_get_max_link_rate: Get maximum link rate on a display port
+ * @drm_fd: DRM file descriptor
+ * @output: igt_output_t object representing the display port
+ *
+ * Returns:
+ * The maximum link rate in kb/s, or a negative error code on failure.
+ */
+int igt_dp_get_max_link_rate(int drm_fd, igt_output_t *output)
+{
+ if (is_intel_device(drm_fd))
+ /*
+ * i915_dp_get_max_link_rate returns the value in tens of kb/s because
+ * that what the kernel uses. Convert it to kb/s to have a sane unit...
+ */
+ return i915_dp_get_max_link_rate(drm_fd, output) * 10;
+
+ igt_assert_f(false, "Current drm device is not able to report max link rate\n");
+ return -EINVAL;
+}
+
+/**
+ * igt_dp_get_max_supported_rate: Get maximum supported link rate on a display port
+ * @drm_fd: DRM file descriptor
+ * @output: igt_output_t object representing the display port
+ *
+ * Returns:
+ * The maximum supported link rate in kb/s, or a negative error code on failure.
+ */
+int igt_dp_get_max_supported_rate(int drm_fd, igt_output_t *output)
+{
+ if (is_intel_device(drm_fd))
+ /*
+ * i915_dp_get_max_supported_rate returns the value in tens of kb/s because
+ * that what the kernel uses. Convert it to kb/s to have a sane unit...
+ */
+ return i915_dp_get_max_supported_rate(drm_fd, output) * 10;
+
+ igt_assert_f(false, "Current drm device is not able to report max link rate\n");
+ return -EINVAL;
+}
+
+/**
+ * igt_dp_get_max_lane_count: Get maximum lane count on a display port
+ * @drm_fd: DRM file descriptor
+ * @output: igt_output_t object representing the display port
+ *
+ * Returns:
+ * The maximum number of lanes, or a negative error code on failure.
+ */
+int igt_dp_get_max_lane_count(int drm_fd, igt_output_t *output)
+{
+ if (is_intel_device(drm_fd))
+ return i915_dp_get_max_lane_count(drm_fd, output);
+
+ igt_assert_f(false, "Current drm device is not able to report max lane count\n");
+ return -EINVAL;
+}
+
+/**
+ * igt_dp_force_link_retrain: Force link retraining on a display port
+ * @drm_fd: DRM file descriptor
+ * @output: igt_output_t object representing the display port
+ * @retrain_count: Number of times to retrain the link
+ */
+void igt_dp_force_link_retrain(int drm_fd, igt_output_t *output, int retrain_count)
+{
+ if (is_intel_device(drm_fd)) {
+ i915_dp_force_link_retrain(drm_fd, output, retrain_count);
+ return;
+ }
+
+ igt_assert_f(false, "Current drm device does not support link retraining\n");
+}
+
+/**
+ * igt_dp_get_pending_retrain: Get pending link retrain count
+ * @drm_fd: DRM file descriptor
+ * @output: igt_output_t object representing the display port
+ *
+ * Returns:
+ * The number of pending retrain operations, or a negative error code on failure.
+ */
+int igt_dp_get_pending_retrain(int drm_fd, igt_output_t *output)
+{
+ if (is_intel_device(drm_fd))
+ return i915_dp_get_pending_retrain(drm_fd, output);
+
+ igt_assert_f(false, "Current drm device does not support pending retrain count checking\n");
+ return -EINVAL;
+}
+
+/**
+ * igt_dp_wait_pending_retrain: Wait for pending link retrain operations to complete
+ * @drm_fd: DRM file descriptor
+ * @output: igt_output_t object representing the display port
+ *
+ * This function waits for any pending link retrain operations to complete on the
+ * specified display port. It polls the debugfs interface for the pending retrain
+ * count until it reaches zero, indicating all retrain operations have completed.
+ */
+void igt_dp_wait_pending_retrain(int drm_fd, igt_output_t *output)
+{
+ double timeout = igt_default_display_detect_timeout();
+ struct timespec start, now;
+ double elapsed;
+
+ igt_assert_eq(igt_gettime(&start), 0);
+
+ while (1) {
+ if (!igt_dp_get_pending_retrain(drm_fd, output))
+ return;
+
+ igt_assert_eq(igt_gettime(&now), 0);
+ elapsed = (now.tv_sec - start.tv_sec) +
+ (now.tv_nsec - start.tv_nsec) / 1e9;
+
+ if (elapsed >= timeout)
+ break;
+ usleep(10000);
+ }
+ igt_assert_f(false, "Timeout waiting for pending retrain to complete\n");
+}
diff --git a/lib/igt_dp.h b/lib/igt_dp.h
new file mode 100644
index 000000000000..0c56ad2ec8b1
--- /dev/null
+++ b/lib/igt_dp.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Copyright © 2026 Google
+ *
+ * Authors:
+ * Louis Chauvet <louis.chauvet@bootlin.com>
+ */
+
+#ifndef _IGT_DP_H_
+#define _IGT_DP_H_
+
+#include "igt_kms.h"
+
+int igt_dp_get_current_link_rate(int drm_fd, igt_output_t *output);
+
+int igt_dp_get_current_lane_count(int drm_fd, igt_output_t *output);
+
+int igt_dp_get_max_link_rate(int drm_fd, igt_output_t *output);
+
+int igt_dp_get_max_supported_rate(int drm_fd, igt_output_t *output);
+
+int igt_dp_get_max_lane_count(int drm_fd, igt_output_t *output);
+
+void igt_dp_force_link_retrain(int drm_fd, igt_output_t *output, int retrain_count);
+
+int igt_dp_get_pending_retrain(int drm_fd, igt_output_t *output);
+
+void igt_dp_wait_pending_retrain(int drm_fd, igt_output_t *output);
+
+#endif
diff --git a/lib/meson.build b/lib/meson.build
index 17cbed2a0faa..dd8e60397434 100644
--- a/lib/meson.build
+++ b/lib/meson.build
@@ -29,6 +29,7 @@ lib_sources = [
'igt_drm_fdinfo.c',
'igt_fs.c',
'igt_aux.c',
+ 'igt_dp.c',
'igt_gt.c',
'igt_halffloat.c',
'igt_hwmon.c',
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* Re: [PATCH i-g-t v9 42/49] lib/igt_dp: Create generic helpers for DP information
2026-03-16 16:18 ` [PATCH i-g-t v9 42/49] lib/igt_dp: Create generic helpers for DP information Louis Chauvet
@ 2026-03-17 15:55 ` Kory Maincent
2026-03-23 14:52 ` Louis Chauvet
0 siblings, 1 reply; 83+ messages in thread
From: Kory Maincent @ 2026-03-17 15:55 UTC (permalink / raw)
To: Louis Chauvet
Cc: igt-dev, thomas.petazzoni, luca.ceresoli, markyacoub,
khaled.almahallawy
On Mon, 16 Mar 2026 17:18:03 +0100
Louis Chauvet <louis.chauvet@bootlin.com> wrote:
> With the introduction of unigraf tests, there is no reason to limit the
> implementation to intel. Create easly extendable generic functions to
> help supporting multiple GPU.
>
> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
> ---
> lib/igt_dp.c | 177
> ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/igt_dp.h |
> 30 ++++++++++ lib/meson.build | 1 +
> 3 files changed, 208 insertions(+)
>
> diff --git a/lib/igt_dp.c b/lib/igt_dp.c
> new file mode 100644
> index 000000000000..a9c9eb3efede
> --- /dev/null
> +++ b/lib/igt_dp.c
> @@ -0,0 +1,177 @@
> +// SPDX-License-Identifier: MIT
> +/*
> + * Copyright © 2026 Google
> + *
> + * Authors:
> + * Louis Chauvet <louis.chauvet@bootlin.com>
> + */
> +
> +#include <stdint.h>
> +
> +#include "drmtest.h"
> +#include "i915/i915_dp.h"
> +#include "igt_core.h"
> +#include "igt_kms.h"
> +#include "igt_dp.h"
> +
> +/**
> + * igt_dp_get_current_link_rate: Get current link rate on a display port
> + * @drm_fd: DRM file descriptor
> + * @output: igt_output_t object representing the display port
> + *
> + * Returns:
> + * The current link rate in kb/s, or a negative error code on failure.
> + */
> +int igt_dp_get_current_link_rate(int drm_fd, igt_output_t *output)
> +{
> + if (is_intel_device(drm_fd))
> + /*
> + * i915_dp_get_current_link_rate returns the value in tens
> of kb/s because
> + * that what the kernel uses. Convert it to kb/s to have a
> sane unit...
> + */
> + return i915_dp_get_current_link_rate(drm_fd, output) * 10;
> +
> + igt_assert_f(false, "Current drm device is not able to report used
> link rate\n");
> + return -EINVAL;
Just wondering, shouldn't you use igt_skip() here, to skip the specific test
without exiting the test process?
Same question for the other igt_assert of this patch.
> +}
> +
> +/**
> + * igt_dp_get_current_lane_count: Get current lane count on a display port
> + * @drm_fd: DRM file descriptor
> + * @output: igt_output_t object representing the display port
> + *
> + * Returns:
> + * The number of active lanes, or a negative error code on failure.
> + */
> +int igt_dp_get_current_lane_count(int drm_fd, igt_output_t *output)
> +{
> + if (is_intel_device(drm_fd))
> + return i915_dp_get_current_lane_count(drm_fd, output);
> +
> + igt_assert_f(false, "Current drm device is not able to report used
> lane count\n");
> + return -EINVAL;
> +}
> +
> +/**
> + * igt_dp_get_max_link_rate: Get maximum link rate on a display port
> + * @drm_fd: DRM file descriptor
> + * @output: igt_output_t object representing the display port
> + *
> + * Returns:
> + * The maximum link rate in kb/s, or a negative error code on failure.
> + */
> +int igt_dp_get_max_link_rate(int drm_fd, igt_output_t *output)
> +{
> + if (is_intel_device(drm_fd))
> + /*
> + * i915_dp_get_max_link_rate returns the value in tens of
> kb/s because
> + * that what the kernel uses. Convert it to kb/s to have a
> sane unit...
> + */
> + return i915_dp_get_max_link_rate(drm_fd, output) * 10;
> +
> + igt_assert_f(false, "Current drm device is not able to report max
> link rate\n");
> + return -EINVAL;
> +}
> +
> +/**
> + * igt_dp_get_max_supported_rate: Get maximum supported link rate on a
> display port
> + * @drm_fd: DRM file descriptor
> + * @output: igt_output_t object representing the display port
> + *
> + * Returns:
> + * The maximum supported link rate in kb/s, or a negative error code on
> failure.
> + */
> +int igt_dp_get_max_supported_rate(int drm_fd, igt_output_t *output)
> +{
> + if (is_intel_device(drm_fd))
> + /*
> + * i915_dp_get_max_supported_rate returns the value in tens
> of kb/s because
> + * that what the kernel uses. Convert it to kb/s to have a
> sane unit...
> + */
> + return i915_dp_get_max_supported_rate(drm_fd, output) * 10;
> +
> + igt_assert_f(false, "Current drm device is not able to report max
> link rate\n");
> + return -EINVAL;
> +}
> +
> +/**
> + * igt_dp_get_max_lane_count: Get maximum lane count on a display port
> + * @drm_fd: DRM file descriptor
> + * @output: igt_output_t object representing the display port
> + *
> + * Returns:
> + * The maximum number of lanes, or a negative error code on failure.
> + */
> +int igt_dp_get_max_lane_count(int drm_fd, igt_output_t *output)
> +{
> + if (is_intel_device(drm_fd))
> + return i915_dp_get_max_lane_count(drm_fd, output);
> +
> + igt_assert_f(false, "Current drm device is not able to report max
> lane count\n");
> + return -EINVAL;
> +}
> +
> +/**
> + * igt_dp_force_link_retrain: Force link retraining on a display port
> + * @drm_fd: DRM file descriptor
> + * @output: igt_output_t object representing the display port
> + * @retrain_count: Number of times to retrain the link
> + */
> +void igt_dp_force_link_retrain(int drm_fd, igt_output_t *output, int
> retrain_count) +{
> + if (is_intel_device(drm_fd)) {
> + i915_dp_force_link_retrain(drm_fd, output, retrain_count);
> + return;
> + }
> +
> + igt_assert_f(false, "Current drm device does not support link
> retraining\n"); +}
> +
> +/**
> + * igt_dp_get_pending_retrain: Get pending link retrain count
> + * @drm_fd: DRM file descriptor
> + * @output: igt_output_t object representing the display port
> + *
> + * Returns:
> + * The number of pending retrain operations, or a negative error code on
> failure.
> + */
> +int igt_dp_get_pending_retrain(int drm_fd, igt_output_t *output)
> +{
> + if (is_intel_device(drm_fd))
> + return i915_dp_get_pending_retrain(drm_fd, output);
> +
> + igt_assert_f(false, "Current drm device does not support pending
> retrain count checking\n");
> + return -EINVAL;
> +}
> +
> +/**
> + * igt_dp_wait_pending_retrain: Wait for pending link retrain operations to
> complete
> + * @drm_fd: DRM file descriptor
> + * @output: igt_output_t object representing the display port
> + *
> + * This function waits for any pending link retrain operations to complete
> on the
> + * specified display port. It polls the debugfs interface for the pending
> retrain
> + * count until it reaches zero, indicating all retrain operations have
> completed.
> + */
> +void igt_dp_wait_pending_retrain(int drm_fd, igt_output_t *output)
> +{
> + double timeout = igt_default_display_detect_timeout();
> + struct timespec start, now;
> + double elapsed;
> +
> + igt_assert_eq(igt_gettime(&start), 0);
> +
> + while (1) {
> + if (!igt_dp_get_pending_retrain(drm_fd, output))
> + return;
> +
> + igt_assert_eq(igt_gettime(&now), 0);
> + elapsed = (now.tv_sec - start.tv_sec) +
> + (now.tv_nsec - start.tv_nsec) / 1e9;
> +
> + if (elapsed >= timeout)
> + break;
igt_time_elapsed() ?
Regards,
--
Köry Maincent, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 83+ messages in thread* Re: [PATCH i-g-t v9 42/49] lib/igt_dp: Create generic helpers for DP information
2026-03-17 15:55 ` Kory Maincent
@ 2026-03-23 14:52 ` Louis Chauvet
0 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-23 14:52 UTC (permalink / raw)
To: Kory Maincent
Cc: igt-dev, thomas.petazzoni, luca.ceresoli, markyacoub,
khaled.almahallawy
On 3/17/26 16:55, Kory Maincent wrote:
> On Mon, 16 Mar 2026 17:18:03 +0100
> Louis Chauvet <louis.chauvet@bootlin.com> wrote:
>
>> With the introduction of unigraf tests, there is no reason to limit the
>> implementation to intel. Create easly extendable generic functions to
>> help supporting multiple GPU.
>>
>> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
>> ---
>> lib/igt_dp.c | 177
>> ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/igt_dp.h |
>> 30 ++++++++++ lib/meson.build | 1 +
>> 3 files changed, 208 insertions(+)
>>
>> diff --git a/lib/igt_dp.c b/lib/igt_dp.c
>> new file mode 100644
>> index 000000000000..a9c9eb3efede
>> --- /dev/null
>> +++ b/lib/igt_dp.c
>> @@ -0,0 +1,177 @@
>> +// SPDX-License-Identifier: MIT
>> +/*
>> + * Copyright © 2026 Google
>> + *
>> + * Authors:
>> + * Louis Chauvet <louis.chauvet@bootlin.com>
>> + */
>> +
>> +#include <stdint.h>
>> +
>> +#include "drmtest.h"
>> +#include "i915/i915_dp.h"
>> +#include "igt_core.h"
>> +#include "igt_kms.h"
>> +#include "igt_dp.h"
>> +
>> +/**
>> + * igt_dp_get_current_link_rate: Get current link rate on a display port
>> + * @drm_fd: DRM file descriptor
>> + * @output: igt_output_t object representing the display port
>> + *
>> + * Returns:
>> + * The current link rate in kb/s, or a negative error code on failure.
>> + */
>> +int igt_dp_get_current_link_rate(int drm_fd, igt_output_t *output)
>> +{
>> + if (is_intel_device(drm_fd))
>> + /*
>> + * i915_dp_get_current_link_rate returns the value in tens
>> of kb/s because
>> + * that what the kernel uses. Convert it to kb/s to have a
>> sane unit...
>> + */
>> + return i915_dp_get_current_link_rate(drm_fd, output) * 10;
>> +
>> + igt_assert_f(false, "Current drm device is not able to report used
>> link rate\n");
>> + return -EINVAL;
>
> Just wondering, shouldn't you use igt_skip() here, to skip the specific test
> without exiting the test process?
> Same question for the other igt_assert of this patch.
>
That a good point. I will change to igt_skip.
>> +}
>> +
>> +/**
>> + * igt_dp_get_current_lane_count: Get current lane count on a display port
>> + * @drm_fd: DRM file descriptor
>> + * @output: igt_output_t object representing the display port
>> + *
>> + * Returns:
>> + * The number of active lanes, or a negative error code on failure.
>> + */
>> +int igt_dp_get_current_lane_count(int drm_fd, igt_output_t *output)
>> +{
>> + if (is_intel_device(drm_fd))
>> + return i915_dp_get_current_lane_count(drm_fd, output);
>> +
>> + igt_assert_f(false, "Current drm device is not able to report used
>> lane count\n");
>> + return -EINVAL;
>> +}
>> +
>> +/**
>> + * igt_dp_get_max_link_rate: Get maximum link rate on a display port
>> + * @drm_fd: DRM file descriptor
>> + * @output: igt_output_t object representing the display port
>> + *
>> + * Returns:
>> + * The maximum link rate in kb/s, or a negative error code on failure.
>> + */
>> +int igt_dp_get_max_link_rate(int drm_fd, igt_output_t *output)
>> +{
>> + if (is_intel_device(drm_fd))
>> + /*
>> + * i915_dp_get_max_link_rate returns the value in tens of
>> kb/s because
>> + * that what the kernel uses. Convert it to kb/s to have a
>> sane unit...
>> + */
>> + return i915_dp_get_max_link_rate(drm_fd, output) * 10;
>> +
>> + igt_assert_f(false, "Current drm device is not able to report max
>> link rate\n");
>> + return -EINVAL;
>> +}
>> +
>> +/**
>> + * igt_dp_get_max_supported_rate: Get maximum supported link rate on a
>> display port
>> + * @drm_fd: DRM file descriptor
>> + * @output: igt_output_t object representing the display port
>> + *
>> + * Returns:
>> + * The maximum supported link rate in kb/s, or a negative error code on
>> failure.
>> + */
>> +int igt_dp_get_max_supported_rate(int drm_fd, igt_output_t *output)
>> +{
>> + if (is_intel_device(drm_fd))
>> + /*
>> + * i915_dp_get_max_supported_rate returns the value in tens
>> of kb/s because
>> + * that what the kernel uses. Convert it to kb/s to have a
>> sane unit...
>> + */
>> + return i915_dp_get_max_supported_rate(drm_fd, output) * 10;
>> +
>> + igt_assert_f(false, "Current drm device is not able to report max
>> link rate\n");
>> + return -EINVAL;
>> +}
>> +
>> +/**
>> + * igt_dp_get_max_lane_count: Get maximum lane count on a display port
>> + * @drm_fd: DRM file descriptor
>> + * @output: igt_output_t object representing the display port
>> + *
>> + * Returns:
>> + * The maximum number of lanes, or a negative error code on failure.
>> + */
>> +int igt_dp_get_max_lane_count(int drm_fd, igt_output_t *output)
>> +{
>> + if (is_intel_device(drm_fd))
>> + return i915_dp_get_max_lane_count(drm_fd, output);
>> +
>> + igt_assert_f(false, "Current drm device is not able to report max
>> lane count\n");
>> + return -EINVAL;
>> +}
>> +
>> +/**
>> + * igt_dp_force_link_retrain: Force link retraining on a display port
>> + * @drm_fd: DRM file descriptor
>> + * @output: igt_output_t object representing the display port
>> + * @retrain_count: Number of times to retrain the link
>> + */
>> +void igt_dp_force_link_retrain(int drm_fd, igt_output_t *output, int
>> retrain_count) +{
>> + if (is_intel_device(drm_fd)) {
>> + i915_dp_force_link_retrain(drm_fd, output, retrain_count);
>> + return;
>> + }
>> +
>> + igt_assert_f(false, "Current drm device does not support link
>> retraining\n"); +}
>> +
>> +/**
>> + * igt_dp_get_pending_retrain: Get pending link retrain count
>> + * @drm_fd: DRM file descriptor
>> + * @output: igt_output_t object representing the display port
>> + *
>> + * Returns:
>> + * The number of pending retrain operations, or a negative error code on
>> failure.
>> + */
>> +int igt_dp_get_pending_retrain(int drm_fd, igt_output_t *output)
>> +{
>> + if (is_intel_device(drm_fd))
>> + return i915_dp_get_pending_retrain(drm_fd, output);
>> +
>> + igt_assert_f(false, "Current drm device does not support pending
>> retrain count checking\n");
>> + return -EINVAL;
>> +}
>> +
>> +/**
>> + * igt_dp_wait_pending_retrain: Wait for pending link retrain operations to
>> complete
>> + * @drm_fd: DRM file descriptor
>> + * @output: igt_output_t object representing the display port
>> + *
>> + * This function waits for any pending link retrain operations to complete
>> on the
>> + * specified display port. It polls the debugfs interface for the pending
>> retrain
>> + * count until it reaches zero, indicating all retrain operations have
>> completed.
>> + */
>> +void igt_dp_wait_pending_retrain(int drm_fd, igt_output_t *output)
>> +{
>> + double timeout = igt_default_display_detect_timeout();
>> + struct timespec start, now;
>> + double elapsed;
>> +
>> + igt_assert_eq(igt_gettime(&start), 0);
>> +
>> + while (1) {
>> + if (!igt_dp_get_pending_retrain(drm_fd, output))
>> + return;
>> +
>> + igt_assert_eq(igt_gettime(&now), 0);
>> + elapsed = (now.tv_sec - start.tv_sec) +
>> + (now.tv_nsec - start.tv_nsec) / 1e9;
>> +
>> + if (elapsed >= timeout)
>> + break;
>
> igt_time_elapsed() ?
Good catch, I missed it, thanks!
> Regards,
^ permalink raw reply [flat|nested] 83+ messages in thread
* [PATCH i-g-t v9 43/49] lib/igt_kms: Add asserts to avoid null pointer dereference
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (41 preceding siblings ...)
2026-03-16 16:18 ` [PATCH i-g-t v9 42/49] lib/igt_dp: Create generic helpers for DP information Louis Chauvet
@ 2026-03-16 16:18 ` Louis Chauvet
2026-03-16 16:18 ` [PATCH i-g-t v9 44/49] lib/igt_kms: Add helper to get a CRTC from an output Louis Chauvet
` (6 subsequent siblings)
49 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:18 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
To help debugging and avoid null pointer dereference, add some asserts in
igt_output_from_connector.
Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
lib/igt_kms.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/lib/igt_kms.c b/lib/igt_kms.c
index 2f1f5cf4a5f3..81d5077b3acc 100644
--- a/lib/igt_kms.c
+++ b/lib/igt_kms.c
@@ -3293,6 +3293,8 @@ igt_output_t *igt_output_from_connector(igt_display_t *display,
{
int i;
igt_output_t *found = NULL;
+ igt_assert(display);
+ igt_assert(connector);
for (i = 0; i < display->n_outputs; i++) {
igt_output_t *output = &display->outputs[i];
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* [PATCH i-g-t v9 44/49] lib/igt_kms: Add helper to get a CRTC from an output
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (42 preceding siblings ...)
2026-03-16 16:18 ` [PATCH i-g-t v9 43/49] lib/igt_kms: Add asserts to avoid null pointer dereference Louis Chauvet
@ 2026-03-16 16:18 ` Louis Chauvet
2026-03-17 15:56 ` Kory Maincent
2026-03-16 16:18 ` [PATCH i-g-t v9 45/49] lib/unigraf: Add lane count configuration Louis Chauvet
` (5 subsequent siblings)
49 siblings, 1 reply; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:18 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
Grabbing a valid CRTC from a specific output is useful when testing
the output.
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
lib/igt_kms.c | 20 ++++++++++++++++++++
lib/igt_kms.h | 2 ++
2 files changed, 22 insertions(+)
diff --git a/lib/igt_kms.c b/lib/igt_kms.c
index 81d5077b3acc..d3ab26576313 100644
--- a/lib/igt_kms.c
+++ b/lib/igt_kms.c
@@ -8090,3 +8090,23 @@ int igt_get_crtc_index_from_connector_id(int drm_fd, int connector_id)
}
return 0;
}
+
+/**
+ * igt_get_crtc_for_output:
+ * @display: display to fetch the CRTC
+ * @output: output to use
+ *
+ * Get a valid CRTC for a specific output. The return value is the first valid CRTC for a
+ * specific output.
+ */
+igt_crtc_t *igt_get_crtc_for_output(igt_display_t *display,
+ igt_output_t *output)
+{
+ igt_crtc_t *crtc;
+
+ for_each_crtc(display, crtc)
+ if (igt_crtc_connector_valid(crtc, output))
+ return crtc;
+
+ return NULL;
+}
diff --git a/lib/igt_kms.h b/lib/igt_kms.h
index 042fac6b21b0..af27e8fcad4e 100644
--- a/lib/igt_kms.h
+++ b/lib/igt_kms.h
@@ -1311,5 +1311,7 @@ get_array_diff(const uint32_t *array_a, int array_a_len, const uint32_t *array_b
uint32_t **diff);
int igt_get_crtc_index_from_connector_id(int drm_fd, int connector_id);
+igt_crtc_t *igt_get_crtc_for_output(igt_display_t *display,
+ igt_output_t *output);
#endif /* __IGT_KMS_H__ */
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* [PATCH i-g-t v9 45/49] lib/unigraf: Add lane count configuration
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (43 preceding siblings ...)
2026-03-16 16:18 ` [PATCH i-g-t v9 44/49] lib/igt_kms: Add helper to get a CRTC from an output Louis Chauvet
@ 2026-03-16 16:18 ` Louis Chauvet
2026-03-16 16:18 ` [PATCH i-g-t v9 46/49] docs/unigraf: Add unigraf documentation Louis Chauvet
` (4 subsequent siblings)
49 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:18 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
As unigraf can emulate 1, 2 or 4 lanes display port connections, add
helpers to configure the number of lanes.
Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
lib/unigraf/unigraf.c | 29 +++++++++++++++++++++++++++++
lib/unigraf/unigraf.h | 4 ++++
2 files changed, 33 insertions(+)
diff --git a/lib/unigraf/unigraf.c b/lib/unigraf/unigraf.c
index 83ea1eb1f91c..e046fa5bc4fe 100644
--- a/lib/unigraf/unigraf.c
+++ b/lib/unigraf/unigraf.c
@@ -546,6 +546,7 @@ void unigraf_reset(void)
unigraf_set_sst();
unigraf_load_default_edid();
unigraf_hpd_assert();
+ unigraf_set_max_lane_count(4);
}
/**
@@ -805,3 +806,31 @@ void unigraf_assert_stream_timings(int stream, drmModeModeInfoPtr mode_info)
igt_assert_eq(mode_info->vsync_start, unigraf_read_u32(TSI_DPRX_MSA_VSTART_R));
igt_assert_eq(mode_info->hsync_start, unigraf_read_u32(TSI_DPRX_MSA_HSTART_R));
}
+
+/**
+ * unigraf_set_max_lane_count() - Set the maximum number of lanes advertised to the DUT
+ * @count: The maximum number of lanes to configure on the device
+ *
+ * This function sets the maximum number of lanes that the device will advertise on the DP link.
+ * The actual number of lanes used may be less than the requested count if the
+ * DUT does not support/use it.
+ */
+void unigraf_set_max_lane_count(uint32_t count)
+{
+ unigraf_write_u32(TSI_DPRX_MAX_LANES, count);
+}
+
+/**
+ * unigraf_get_max_lane_count() - Get the maximum number of lanes supported by the device
+ *
+ * Returns: The maximum number of lanes supported by the device.
+ */
+int unigraf_get_max_lane_count(void)
+{
+ int max_lanes;
+
+ igt_assert(unigraf_device);
+ unigraf_assert(TSIX_TS_GetConfigItem(unigraf_device, TSI_DPRX_MAX_LANES,
+ &max_lanes, sizeof(max_lanes)));
+ return max_lanes;
+}
diff --git a/lib/unigraf/unigraf.h b/lib/unigraf/unigraf.h
index 65f00c8d4948..e926855dd059 100644
--- a/lib/unigraf/unigraf.h
+++ b/lib/unigraf/unigraf.h
@@ -80,4 +80,8 @@ int unigraf_get_connector_id_by_stream(int drm_fd, int stream_id);
void unigraf_assert_stream_timings(int stream, drmModeModeInfoPtr mode_info);
+int unigraf_get_max_lane_count(void);
+
+void unigraf_set_max_lane_count(uint32_t count);
+
#endif // UNIGRAF_H
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* [PATCH i-g-t v9 46/49] docs/unigraf: Add unigraf documentation
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (44 preceding siblings ...)
2026-03-16 16:18 ` [PATCH i-g-t v9 45/49] lib/unigraf: Add lane count configuration Louis Chauvet
@ 2026-03-16 16:18 ` Louis Chauvet
2026-03-17 15:58 ` Kory Maincent
2026-03-24 8:07 ` Naladala, Ramanaidu
2026-03-16 16:18 ` [PATCH i-g-t v9 47/49] lib/unigraf: Add helpers to set maximum link rate Louis Chauvet
` (3 subsequent siblings)
49 siblings, 2 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:18 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
Add unigraf documentation to explain how to compile and use it with IGT.
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
docs/unigraf.txt | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 81 insertions(+)
diff --git a/docs/unigraf.txt b/docs/unigraf.txt
new file mode 100644
index 000000000000..37996a2ef98b
--- /dev/null
+++ b/docs/unigraf.txt
@@ -0,0 +1,81 @@
+Unigraf Support in IGT
+========================
+
+This document provides information and instructions for Unigraf support in IGT.
+
+Introduction
+------------
+
+Unigraf devices are a set of devices that can be used to test all sort of graphical
+interface and signals. See Unigraf website for more information about all the
+capabilities of each devices: https://unigraf.fi
+
+Unigraf documentation
+---------------------
+
+All the documentation for libTSI, the library used to communicate with the unigraf,
+can be found on unigraf website: https://www.unigraf.fi/downloads/
+
+Setting up Unigraf
+------------------
+
+For most of the unigraf devices, this should be as simple as "plugging the
+unigraf and plug the video cable". If you need to install some license, upgrade
+the firmware... please take a look at the official unigraf documentation.
+
+Compiling IGT with Unigraf support
+----------------------------------
+
+Unfortunately, the communication with the device use a proprietary library and the
+underlying protocol is not open. To use the unigraf you need to install the unigraf
+SDK, grab the libTSI.so and make it available on the build machine and the DUT.
+
+Deploying the Unigraf with IGT
+------------------------------
+
+Unigraf may work out of the box: by default if IGT find a unigraf device, it will
+try to connect and configure it to use USB-C input. This allows running most of
+the existing tests without an actual screens.
+
+Here is a complete example configuration:
+
+ [Unigraf]
+ # Choose a device using his name, you can find it in UCDConsole (installed with the SDK)
+ Device=UCD-500 [2434C620]
+ # Choose a role for the device, you can find it UCDConsole
+ Role=USB-C, DP Alt Mode Source and Sink
+ # Choose active input for the device
+ Input=DP RX
+ # Set the connector name used by the unigraf device. If not set, IGT will try to
+ # autodetect which connector is used.
+ Connector=DP-2
+ # Set the EDID used by Unigraf
+ EDID=DEL_16543_DELL_P2314T_DP
+ # Enable or disable the use of Unigraf's CRCs instead of the CRTC CRCs
+ UseCRC=1
+ # Set the number of emulated MST streams (0 = Single Stream Transport, >=1 = MST)
+ MSTStreams=0
+
+Current Support in IGT
+----------------------
+
+Support for the Unigraf devices in IGT is found in the following places:
+* lib/unigraf/unigraf.[ch] -> IGT API
+* lib/unigraf/TSI.h -> Header for libTSI.so, not provided by Unigraf
+* lib/unigraf/TSI_types.h -> Header containing all the relevant values from unigraf SDK. The unigraf SDK can't be used directly because it is only compatible with windows and c++.
+
+Current unigraf.c features are:
+* Plug, unplug, hotplug pulse
+* EDID management
+* MST/SST stream management
+* CRC calculation
+
+Current testing are:
+* All igt tests that require a display and use a CRC (kms_plane, kms_cursor_crc, ...)
+* Basic EDID and MST tests
+
+Planned tests:
+* Link training scenarios (link rate, lane count, scrambling, ...)
+* Bandwidth constraint management
+* HDCP content protection
+* Display stream compression
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* Re: [PATCH i-g-t v9 46/49] docs/unigraf: Add unigraf documentation
2026-03-16 16:18 ` [PATCH i-g-t v9 46/49] docs/unigraf: Add unigraf documentation Louis Chauvet
@ 2026-03-17 15:58 ` Kory Maincent
2026-03-24 8:07 ` Naladala, Ramanaidu
1 sibling, 0 replies; 83+ messages in thread
From: Kory Maincent @ 2026-03-17 15:58 UTC (permalink / raw)
To: Louis Chauvet
Cc: igt-dev, thomas.petazzoni, luca.ceresoli, markyacoub,
khaled.almahallawy
On Mon, 16 Mar 2026 17:18:07 +0100
Louis Chauvet <louis.chauvet@bootlin.com> wrote:
> Add unigraf documentation to explain how to compile and use it with IGT.
>
> Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
Thank you!
--
Köry Maincent, Bootlin
Embedded Linux and kernel engineering
https://bootlin.com
^ permalink raw reply [flat|nested] 83+ messages in thread
* Re: [PATCH i-g-t v9 46/49] docs/unigraf: Add unigraf documentation
2026-03-16 16:18 ` [PATCH i-g-t v9 46/49] docs/unigraf: Add unigraf documentation Louis Chauvet
2026-03-17 15:58 ` Kory Maincent
@ 2026-03-24 8:07 ` Naladala, Ramanaidu
1 sibling, 0 replies; 83+ messages in thread
From: Naladala, Ramanaidu @ 2026-03-24 8:07 UTC (permalink / raw)
To: igt-dev
[-- Attachment #1: Type: text/plain, Size: 6833 bytes --]
Hi Louis Chauvet,
On 3/16/2026 9:48 PM, Louis Chauvet wrote:
> Add unigraf documentation to explain how to compile and use it with IGT.
>
> Signed-off-by: Louis Chauvet<louis.chauvet@bootlin.com>
> ---
> docs/unigraf.txt | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 81 insertions(+)
>
> diff --git a/docs/unigraf.txt b/docs/unigraf.txt
> new file mode 100644
> index 000000000000..37996a2ef98b
> --- /dev/null
> +++ b/docs/unigraf.txt
> @@ -0,0 +1,81 @@
> +Unigraf Support in IGT
> +========================
> +
> +This document provides information and instructions for Unigraf support in IGT.
> +
> +Introduction
> +------------
> +
> +Unigraf devices are a set of devices that can be used to test all sort of graphical
> +interface and signals. See Unigraf website for more information about all the
> +capabilities of each devices:https://unigraf.fi
> +
> +Unigraf documentation
> +---------------------
> +
> +All the documentation for libTSI, the library used to communicate with the unigraf,
> +can be found on unigraf website:https://www.unigraf.fi/downloads/
> +
> +Setting up Unigraf
> +------------------
> +
> +For most of the unigraf devices, this should be as simple as "plugging the
> +unigraf and plug the video cable". If you need to install some license, upgrade
> +the firmware... please take a look at the official unigraf documentation.
> +
> +Compiling IGT with Unigraf support
> +----------------------------------
> +
> +Unfortunately, the communication with the device use a proprietary library and the
> +underlying protocol is not open. To use the unigraf you need to install the unigraf
> +SDK, grab the libTSI.so and make it available on the build machine and the DUT.
> +
> +Deploying the Unigraf with IGT
> +------------------------------
> +
> +Unigraf may work out of the box: by default if IGT find a unigraf device, it will
> +try to connect and configure it to use USB-C input. This allows running most of
> +the existing tests without an actual screens.
> +
> +Here is a complete example configuration:
> +
> + [Unigraf]
> + # Choose a device using his name, you can find it in UCDConsole (installed with the SDK)
> + Device=UCD-500 [2434C620]
> + # Choose a role for the device, you can find it UCDConsole
> + Role=USB-C, DP Alt Mode Source and Sink
> + # Choose active input for the device
> + Input=DP RX
Please document the required Input parameters(DP RX, USB RX) in the
|igtrc| file for each supported *Role* type. When an input name is
missing, misspelled, or incompatible with the selected Role, the
function |unigraf_open_device()| triggers an assertion.
more log info:
Starting subtest: unigraf-connect-edid
(unigraf_connectivity:22870) unigraf/unigraf-CRITICAL: Test assertion failure function unigraf_open_device, file ../lib/unigraf/unigraf.c:495:
(unigraf_connectivity:22870) unigraf/unigraf-CRITICAL: Failed assertion: chosen_input >= 0
(unigraf_connectivity:22870) unigraf/unigraf-CRITICAL: Last errno: 2, No such file or directory
(unigraf_connectivity:22870) unigraf/unigraf-CRITICAL: TSI:0x5dad11372b90: Input DP RX not found.Subtest unigraf-connect-edid failed.
**** DEBUG ****
(unigraf_connectivity:22870) unigraf/unigraf-DEBUG: TSI:(nil): Initialize unigraf...
(unigraf_connectivity:22870) unigraf/unigraf-DEBUG: TSI:(nil): No connector name configured, will autodetect.
(unigraf_connectivity:22870) unigraf/unigraf-DEBUG: TSI:(nil): Detected unigraf device 0: UCD-500 [2607C673]
(unigraf_connectivity:22870) unigraf/unigraf-DEBUG: TSI:0x5dad11372b90: Successfully opened the unigraf device 0.
(unigraf_connectivity:22870) unigraf/unigraf-DEBUG: TSI:0x5dad11372b90: Role 0: DisplayPort Source and Sink
(unigraf_connectivity:22870) unigraf/unigraf-DEBUG: TSI:0x5dad11372b90: Role 1: DisplayPort Source and USB-C, DP Alt Mode Sink
(unigraf_connectivity:22870) unigraf/unigraf-DEBUG: TSI:0x5dad11372b90: Role 2: DisplayPort Sink and USB-C, DP Alt Mode Source
(unigraf_connectivity:22870) unigraf/unigraf-DEBUG: TSI:0x5dad11372b90: Role 3: USB-C, DP Alt Mode Source and Sink
(unigraf_connectivity:22870) unigraf/unigraf-DEBUG: TSI:0x5dad11372b90: Input 0: USB-C RX
(unigraf_connectivity:22870) unigraf/unigraf-CRITICAL: Test assertion failure function unigraf_open_device, file ../lib/unigraf/unigraf.c:495:
(unigraf_connectivity:22870) unigraf/unigraf-CRITICAL: Failed assertion: chosen_input >= 0
(unigraf_connectivity:22870) unigraf/unigraf-CRITICAL: Last errno: 2, No such file or directory
(unigraf_connectivity:22870) unigraf/unigraf-CRITICAL: TSI:0x5dad11372b90: Input DP RX not found.Stack trace:
(unigraf_connectivity:22870) igt_core-INFO: #0 ../lib/igt_core.c:2108 __igt_fail_assert()
(unigraf_connectivity:22870) igt_core-INFO: #1 [unigraf_open_device+0xb8e]
(unigraf_connectivity:22870) igt_core-INFO: #2 ../lib/unigraf/unigraf.c:532 unigraf_require_device()
(unigraf_connectivity:22870) igt_core-INFO: #3 ../tests/unigraf/unigraf_connectivity.c:58 __igt_unique____real_main35()
(unigraf_connectivity:22870) igt_core-INFO: #4 ../tests/unigraf/unigraf_connectivity.c:35 main()
(unigraf_connectivity:22870) igt_core-INFO: #5 ../sysdeps/nptl/libc_start_call_main.h:74 __libc_start_call_main()
(unigraf_connectivity:22870) igt_core-INFO: #6 ../csu/libc-start.c:128 __libc_start_main@@GLIBC_2.34()
(unigraf_connectivity:22870) igt_core-INFO: #7 [_start+0x25]
**** END ****
> + # Set the connector name used by the unigraf device. If not set, IGT will try to
> + # autodetect which connector is used.
> + Connector=DP-2
> + # Set the EDID used by Unigraf
> + EDID=DEL_16543_DELL_P2314T_DP
> + # Enable or disable the use of Unigraf's CRCs instead of the CRTC CRCs
> + UseCRC=1
> + # Set the number of emulated MST streams (0 = Single Stream Transport, >=1 = MST)
> + MSTStreams=0
> +
> +Current Support in IGT
> +----------------------
> +
> +Support for the Unigraf devices in IGT is found in the following places:
> +* lib/unigraf/unigraf.[ch] -> IGT API
> +* lib/unigraf/TSI.h -> Header for libTSI.so, not provided by Unigraf
> +* lib/unigraf/TSI_types.h -> Header containing all the relevant values from unigraf SDK. The unigraf SDK can't be used directly because it is only compatible with windows and c++.
> +
> +Current unigraf.c features are:
> +* Plug, unplug, hotplug pulse
> +* EDID management
> +* MST/SST stream management
> +* CRC calculation
> +
> +Current testing are:
> +* All igt tests that require a display and use a CRC (kms_plane, kms_cursor_crc, ...)
> +* Basic EDID and MST tests
> +
> +Planned tests:
> +* Link training scenarios (link rate, lane count, scrambling, ...)
> +* Bandwidth constraint management
> +* HDCP content protection
> +* Display stream compression
>
[-- Attachment #2: Type: text/html, Size: 8412 bytes --]
^ permalink raw reply [flat|nested] 83+ messages in thread
* [PATCH i-g-t v9 47/49] lib/unigraf: Add helpers to set maximum link rate
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (45 preceding siblings ...)
2026-03-16 16:18 ` [PATCH i-g-t v9 46/49] docs/unigraf: Add unigraf documentation Louis Chauvet
@ 2026-03-16 16:18 ` Louis Chauvet
2026-03-16 16:18 ` [PATCH i-g-t v9 48/49] lib/unigraf: Add helpers to get the current LT status Louis Chauvet
` (2 subsequent siblings)
49 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:18 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
As link rate can vary on a display port link, add a way
to change advertised link rate from unigraf.
Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
lib/unigraf/unigraf.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++
lib/unigraf/unigraf.h | 14 ++++++++++++++
2 files changed, 67 insertions(+)
diff --git a/lib/unigraf/unigraf.c b/lib/unigraf/unigraf.c
index e046fa5bc4fe..74957b6b3f5d 100644
--- a/lib/unigraf/unigraf.c
+++ b/lib/unigraf/unigraf.c
@@ -547,6 +547,7 @@ void unigraf_reset(void)
unigraf_load_default_edid();
unigraf_hpd_assert();
unigraf_set_max_lane_count(4);
+ unigraf_set_max_link_rate(UNIGRAF_RATE_8_10_GHZ);
}
/**
@@ -834,3 +835,55 @@ int unigraf_get_max_lane_count(void)
&max_lanes, sizeof(max_lanes)));
return max_lanes;
}
+
+/**
+ * unigraf_set_max_link_rate() - Set the maximum link rate advertised to the DUT
+ * @bandwidth: The maximum link rate to configure on the device. Actual value is
+ * @bandwidth * 270000 kHz. Common values can be found in enum unigraf_rate
+ *
+ * This function sets the maximum link rate that the device will advertise on the DP link.
+ * The actual link rate used may be less than the requested value if the DUT does not
+ * support/use it.
+ */
+void unigraf_set_max_link_rate(int bandwidth)
+{
+ unigraf_write_u32(TSI_DPRX_MAX_LINK_RATE, bandwidth);
+}
+
+/**
+ * unigraf_get_max_link_rate() - Get the maximum link rate supported by the device
+ *
+ * Returns: The maximum link rate supported by the device. The value returned must be multiplied by
+ * 270000 to get the value in kHz.
+ */
+int unigraf_get_max_link_rate(void)
+{
+ return unigraf_read_u32(TSI_DPRX_MAX_LINK_RATE);
+}
+
+/**
+ * unigraf_rate_to_kbs - Convert a rate value to kilobits per second
+ * @rate: The rate value to convert
+ *
+ * This function converts a rate value to kilobits per second (kbps).
+ *
+ * Returns: The converted rate in kilobits per second
+ */
+int unigraf_rate_to_kbs(enum unigraf_rate rate)
+{
+ switch (rate) {
+ case UNIGRAF_RATE_1_62_GHZ:
+ return 1620000;
+ case UNIGRAF_RATE_2_7_GHZ:
+ return 2700000;
+ case UNIGRAF_RATE_5_4_GHZ:
+ return 5400000;
+ case UNIGRAF_RATE_6_75_GHZ:
+ return 6750000;
+ case UNIGRAF_RATE_8_10_GHZ:
+ return 8100000;
+ default:
+ igt_assert_f(0, "Unknown rate %d\n", rate);
+ return 0;
+ }
+}
diff --git a/lib/unigraf/unigraf.h b/lib/unigraf/unigraf.h
index e926855dd059..3cba15732daa 100644
--- a/lib/unigraf/unigraf.h
+++ b/lib/unigraf/unigraf.h
@@ -84,4 +84,18 @@ int unigraf_get_max_lane_count(void);
void unigraf_set_max_lane_count(uint32_t count);
+enum unigraf_rate {
+ UNIGRAF_RATE_1_62_GHZ = 6,
+ UNIGRAF_RATE_2_7_GHZ = 10,
+ UNIGRAF_RATE_5_4_GHZ = 20,
+ UNIGRAF_RATE_6_75_GHZ = 25,
+ UNIGRAF_RATE_8_10_GHZ = 30,
+};
+
+void unigraf_set_max_link_rate(int rate);
+
+int unigraf_get_max_link_rate(void);
+
+int unigraf_rate_to_kbs(enum unigraf_rate rate);
+
#endif // UNIGRAF_H
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* [PATCH i-g-t v9 48/49] lib/unigraf: Add helpers to get the current LT status
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (46 preceding siblings ...)
2026-03-16 16:18 ` [PATCH i-g-t v9 47/49] lib/unigraf: Add helpers to set maximum link rate Louis Chauvet
@ 2026-03-16 16:18 ` Louis Chauvet
2026-03-16 16:18 ` [PATCH i-g-t v9 49/49] tests/unigraf/unigraf_lt: Add test for link training Louis Chauvet
2026-03-17 12:02 ` ✗ Fi.CI.BUILD: failure for Unigraf integration (rev8) Patchwork
49 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:18 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
To ensure that the link training was correct, grab actual link
training status from unigraf.
Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
lib/unigraf/unigraf.c | 20 ++++++++++++++++++++
lib/unigraf/unigraf.h | 4 ++++
2 files changed, 24 insertions(+)
diff --git a/lib/unigraf/unigraf.c b/lib/unigraf/unigraf.c
index 74957b6b3f5d..2e38dbfc9f1d 100644
--- a/lib/unigraf/unigraf.c
+++ b/lib/unigraf/unigraf.c
@@ -836,6 +836,26 @@ int unigraf_get_max_lane_count(void)
return max_lanes;
}
+/**
+ * unigraf_get_lt_rate() - Get the current link training rate
+ *
+ * Returns: The current link training rate in units of 270 MHz.
+ */
+uint32_t unigraf_get_lt_rate(void)
+{
+ return unigraf_read_u32(TSI_DPRX_LT_RATE_R);
+}
+
+/**
+ * unigraf_get_lt_lane_count() - Get the current link training lane count
+ *
+ * Returns: The current number of lanes being used in link training.
+ */
+uint32_t unigraf_get_lt_lane_count(void)
+{
+ return unigraf_read_u32(TSI_DPRX_LT_LANE_COUNT_R);
+}
+
/**
* unigraf_set_max_link_rate() - Set the maximum link rate advertised to the DUT
* @bandwidth: The maximum link rate to configure on the device. Actual value is
diff --git a/lib/unigraf/unigraf.h b/lib/unigraf/unigraf.h
index 3cba15732daa..8032ef8e7fe6 100644
--- a/lib/unigraf/unigraf.h
+++ b/lib/unigraf/unigraf.h
@@ -98,4 +98,8 @@ int unigraf_get_max_link_rate(void);
int unigraf_rate_to_kbs(enum unigraf_rate rate);
+uint32_t unigraf_get_lt_rate(void);
+
+uint32_t unigraf_get_lt_lane_count(void);
+
#endif // UNIGRAF_H
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* [PATCH i-g-t v9 49/49] tests/unigraf/unigraf_lt: Add test for link training
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (47 preceding siblings ...)
2026-03-16 16:18 ` [PATCH i-g-t v9 48/49] lib/unigraf: Add helpers to get the current LT status Louis Chauvet
@ 2026-03-16 16:18 ` Louis Chauvet
2026-03-17 12:02 ` ✗ Fi.CI.BUILD: failure for Unigraf integration (rev8) Patchwork
49 siblings, 0 replies; 83+ messages in thread
From: Louis Chauvet @ 2026-03-16 16:18 UTC (permalink / raw)
To: igt-dev
Cc: thomas.petazzoni, luca.ceresoli, kory.maincent, markyacoub,
khaled.almahallawy, Louis Chauvet
To test link training on various situation, add multiple link
training scenarios.
Reviewed-by: Kory Maincent <kory.maincent@bootlin.com>
Signed-off-by: Louis Chauvet <louis.chauvet@bootlin.com>
---
tests/unigraf/meson.build | 1 +
tests/unigraf/unigraf_lt.c | 183 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 184 insertions(+)
diff --git a/tests/unigraf/meson.build b/tests/unigraf/meson.build
index 4ef8c32151e4..22e64c0f389d 100644
--- a/tests/unigraf/meson.build
+++ b/tests/unigraf/meson.build
@@ -1,5 +1,6 @@
unigraf_progs = [
'unigraf_connectivity',
+ 'unigraf_lt',
]
foreach prog : unigraf_progs
diff --git a/tests/unigraf/unigraf_lt.c b/tests/unigraf/unigraf_lt.c
new file mode 100644
index 000000000000..81e6ecd3fae4
--- /dev/null
+++ b/tests/unigraf/unigraf_lt.c
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright © 2026 Google
+ *
+ * Authors:
+ * Louis Chauvet <louis.chauvet@bootlin.com>
+ */
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <xf86drm.h>
+#include <xf86drmMode.h>
+
+#include "drm_fourcc.h"
+#include "drmtest.h"
+#include "igt_aux.h"
+#include "igt_core.h"
+#include "igt_dp.h"
+#include "igt_edid.h"
+#include "igt_kms.h"
+#include "unigraf/unigraf.h"
+
+/**
+ * TEST: unigraf link training
+ * Category: Core
+ * Description: Testing DP link training with a unigraf device
+ *
+ * SUBTEST: unigraf-dp-lane-count
+ * Description: Make sure that the link training goes well for different lane count
+ *
+ * SUBTEST: unigraf-dp-link-rate
+ * Description: Make sure that the link training goes well for different link rates
+ */
+
+static void init_output_and_display_pattern(igt_display_t *display, igt_output_t *output)
+{
+ igt_crtc_t *crtc;
+ struct igt_fb fb;
+ igt_plane_t *primary;
+ drmModeModeInfo *mode;
+ int fb_id;
+
+ igt_modeset_disable_all_outputs(display);
+ igt_display_reset(display);
+
+ igt_output_set_crtc(output, 0);
+ crtc = igt_get_crtc_for_output(display, output);
+ igt_output_set_crtc(output, crtc);
+
+ /* Get the current mode */
+ mode = igt_output_get_mode(output);
+ igt_assert(mode);
+
+ /* Create a framebuffer with a solid color pattern */
+ fb_id = igt_create_color_pattern_fb(display->drm_fd, mode->hdisplay,
+ mode->vdisplay, DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_MOD_LINEAR, 0, 0, 0, &fb);
+ igt_assert(fb_id > 0);
+
+ primary = igt_output_get_plane_type(output, DRM_PLANE_TYPE_PRIMARY);
+
+ igt_assert(primary);
+
+ igt_plane_set_size(primary, mode->hdisplay, mode->vdisplay);
+ igt_plane_set_fb(primary, &fb);
+ igt_output_override_mode(output, mode);
+
+ igt_display_commit2(display, COMMIT_ATOMIC);
+
+ /* Set the framebuffer to the plane */
+ igt_plane_set_fb(primary, &fb);
+
+ /* Set the plane properties atomically */
+ igt_plane_set_prop_value(primary, IGT_PLANE_FB_ID, fb.fb_id);
+ igt_plane_set_prop_value(primary, IGT_PLANE_CRTC_X, 0);
+ igt_plane_set_prop_value(primary, IGT_PLANE_CRTC_Y, 0);
+ igt_plane_set_prop_value(primary, IGT_PLANE_CRTC_W, mode->hdisplay);
+ igt_plane_set_prop_value(primary, IGT_PLANE_CRTC_H, mode->vdisplay);
+
+ /* Commit the changes atomically */
+ igt_display_commit_atomic(display, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
+}
+
+IGT_TEST_DESCRIPTION("Test unigraf device functionality");
+int igt_main()
+{
+ int drm_fd = -1;
+ igt_display_t display;
+ igt_output_t *output;
+ drmModeConnectorPtr connector;
+ int i;
+
+ igt_fixture() {
+ drm_fd = drm_open_driver(DRIVER_ANY);
+ igt_assert(drm_fd >= 0);
+
+ igt_display_require(&display, drm_fd);
+ unigraf_require_device(drm_fd);
+
+ connector = unigraf_get_connector(drm_fd);
+ }
+
+ igt_subtest_with_dynamic("unigraf-dp-lane-count") {
+ int lane_counts[3] = {1, 2, 4};
+ int current_lanes;
+
+ for (i = 0; i < ARRAY_SIZE(lane_counts); i++) {
+ igt_dynamic_f("unigraf-dp-lane-count-%d", lane_counts[i]) {
+ unigraf_reset();
+ unigraf_set_max_lane_count(lane_counts[i]);
+ unigraf_hpd_pulse(500000);
+
+ igt_display_require_output(&display);
+ output = igt_output_from_connector(&display, connector);
+ igt_assert(output);
+ igt_dp_force_link_retrain(drm_fd, output, 2);
+ init_output_and_display_pattern(&display, output);
+
+ current_lanes = igt_dp_get_current_lane_count(drm_fd, output);
+ igt_assert_eq(current_lanes, lane_counts[i]);
+ }
+ }
+ }
+
+ igt_subtest_with_dynamic("unigraf-dp-link-rate") {
+ int rates[] = {UNIGRAF_RATE_1_62_GHZ,
+ UNIGRAF_RATE_2_7_GHZ,
+ UNIGRAF_RATE_5_4_GHZ,
+ UNIGRAF_RATE_6_75_GHZ,
+ UNIGRAF_RATE_8_10_GHZ};
+ int current_rate;
+ int max_supported_rate;
+
+ for (i = 0; i < ARRAY_SIZE(rates); i++) {
+ igt_dynamic_f("unigraf-dp-link-rate-%d", rates[i]) {
+ unigraf_reset();
+ unigraf_set_max_link_rate(rates[i]);
+ unigraf_hpd_pulse(1000000);
+ igt_display_require_output(&display);
+ igt_display_reset(&display);
+ igt_display_require_output(&display);
+ output = igt_output_from_connector(&display, connector);
+ igt_assert(output);
+ igt_dp_force_link_retrain(drm_fd, output, 2);
+
+ init_output_and_display_pattern(&display, output);
+
+ current_rate = igt_dp_get_max_link_rate(drm_fd, output);
+ max_supported_rate = igt_dp_get_max_supported_rate(drm_fd, output);
+ igt_require(max_supported_rate >= unigraf_rate_to_kbs(rates[i]));
+ igt_assert_eq(current_rate, unigraf_rate_to_kbs(rates[i]));
+ }
+ }
+ }
+
+ igt_subtest("unigraf-dp-link-suspend-resume") {
+ int current_rate, current_lanes;
+
+ unigraf_reset();
+ igt_display_require_output(&display);
+ igt_display_reset(&display);
+ igt_display_require_output(&display);
+ output = igt_output_from_connector(&display, connector);
+ igt_assert(output);
+
+ /* Get initial link parameters */
+ current_rate = igt_dp_get_current_link_rate(drm_fd, output);
+ current_lanes = igt_dp_get_current_lane_count(drm_fd, output);
+
+ /* Suspend the system */
+ igt_system_suspend_autoresume(SUSPEND_STATE_MEM, SUSPEND_TEST_NONE);
+
+ /* Verify link parameters are maintained */
+ igt_assert_eq(igt_dp_get_current_link_rate(drm_fd, output), current_rate);
+ igt_assert_eq(igt_dp_get_current_lane_count(drm_fd, output), current_lanes);
+ }
+
+ igt_fixture() {
+ igt_display_fini(&display);
+ close(drm_fd);
+ }
+}
--
2.52.0
^ permalink raw reply related [flat|nested] 83+ messages in thread* ✗ Fi.CI.BUILD: failure for Unigraf integration (rev8)
2026-03-16 16:17 [PATCH i-g-t v9 00/49] Unigraf integration Louis Chauvet
` (48 preceding siblings ...)
2026-03-16 16:18 ` [PATCH i-g-t v9 49/49] tests/unigraf/unigraf_lt: Add test for link training Louis Chauvet
@ 2026-03-17 12:02 ` Patchwork
49 siblings, 0 replies; 83+ messages in thread
From: Patchwork @ 2026-03-17 12:02 UTC (permalink / raw)
To: Louis Chauvet; +Cc: igt-dev
== Series Details ==
Series: Unigraf integration (rev8)
URL : https://patchwork.freedesktop.org/series/151772/
State : failure
== Summary ==
Applying: lib/igt_kms: Add a detect timeout value
Applying: lib/igt_kms: Add helper to wait for a specific status on a connector
Applying: lib/igt_kms: Add function to list connected connectors
Applying: lib/igt_kms: Add helper to obtain a connector by its name or MST path
Applying: lib/igt_kms: Add helper to wait for new connectors
Applying: lib/tests: Add tests for array manipulations
Applying: lib/igt_kms: Add helper to get a pipe from a connector
Applying: lib/igt_kms: Expose dump_connector_attrs
Applying: lib/igt_kms: Expose reset_connectors_at_exit
Applying: lib/igt_kms: Expose connector_attr_set and igt_connector_attr_set
Applying: lib/igt_debugfs: Move debugfs helpers to the proper location
Applying: lib/igt_debugfs: Add const when make sense
Applying: lib/igt_amd: Add const when make sense
Applying: lib/igt_kms: Add const when make sense
Patch failed at 0014 lib/igt_kms: Add const when make sense
When you have resolved this problem, run "git am --continue".
If you prefer to skip this patch, run "git am --skip" instead.
To restore the original branch and stop patching, run "git am --abort".
^ permalink raw reply [flat|nested] 83+ messages in thread