[igt-dev] [PATCH] Chamelium: Make autodiscover discover connectors one at a time.
Mark Yacoub
markyacoub at chromium.org
Wed Sep 21 19:36:29 UTC 2022
[Why]
On chamelium v3, the ITE chip can only hold 1 EDID at a time. We can
only read the last EDID being set after a port is plugged.
[How]
On autodiscover, instead of discovering all ports at once by setting
EDIDs to all at once then read them all, perform the operation on 1 port
at a time:
1. Reset Chamelium to unplug all ports.
-for each port-
2. Create and Apply EDID
3. Plug
4. Wait for connector to update EDID
5. Check if we find the EDID or not.
-
6. Turn on supported ports to bring the system back to the previous
state.
TEST=./kms_chamelium --run-subtest dp-hpd --debug [on intel volteer
board and Chamelium V3]
Signed-off-by: Mark Yacoub <markyacoub at chromium.org>
---
lib/igt_chamelium.c | 209 ++++++++++++++++++++++++--------------------
1 file changed, 114 insertions(+), 95 deletions(-)
diff --git a/lib/igt_chamelium.c b/lib/igt_chamelium.c
index b2ce4174..0fee8a83 100644
--- a/lib/igt_chamelium.c
+++ b/lib/igt_chamelium.c
@@ -2472,6 +2472,10 @@ static int port_id_from_edid(int drm_fd, drmModeConnector *connector)
const struct edid *edid;
char mfg[3];
+ /* MST connectors stop being valid on unplug but would still exist in DRM Resources. */
+ if (!connector)
+ return -1;
+
if (connector->connection != DRM_MODE_CONNECTED) {
igt_debug("Skipping auto-discovery for connector %s-%d: "
"connector status is not connected\n",
@@ -2524,6 +2528,52 @@ out:
return port_id;
}
+static bool get_connector_id_for_port(struct chamelium *chamelium,
+ const int expected_port_id,
+ uint32_t *attached_connector_id)
+{
+ int i;
+
+ int drm_fd = chamelium->drm_fd;
+ drmModeRes *res = drmModeGetResources(drm_fd);
+ if (!res)
+ return false;
+
+ for (i = 0; i < res->count_connectors; i++) {
+ uint32_t conn_id;
+ size_t j;
+
+ /* Read the EDID and parse the Chamelium port ID we stored there. */
+ drmModeConnector *connector =
+ drmModeGetConnector(drm_fd, res->connectors[i]);
+ int port_id = port_id_from_edid(drm_fd, connector);
+ drmModeFreeConnector(connector);
+ if (port_id != expected_port_id)
+ continue;
+
+ /* If we already have a mapping from the config file, check that it's consistent. */
+ conn_id = res->connectors[i];
+ for (j = 0; j < chamelium->port_count; j++) {
+ struct chamelium_port *port = &chamelium->ports[j];
+ if (port->connector_id == conn_id) {
+ igt_assert_f(
+ port->id == port_id,
+ "Inconsistency detected in .igtrc: "
+ "connector %s is configured with "
+ "Chamelium port %d, but is "
+ "connected to port %d\n",
+ port->name, port->id, port_id);
+ return false;
+ }
+ }
+
+ *attached_connector_id = conn_id;
+ return true;
+ }
+
+ return false;
+}
+
/**
* chamelium_autodiscover: automagically discover the Chamelium port mapping
*
@@ -2533,44 +2583,47 @@ out:
* past (see #chamelium_read_port_mappings), but this function provides an
* automatic way to do it.
*
- * We will plug all Chamelium ports with a different EDID on each. Then we'll
- * read the EDID on each DRM connector and infer the Chamelium port ID.
+ * We will plug the Chamelium ports one by one with a different EDID on each.
+ * Then we'll read the EDID on each DRM connector and infer the Chamelium port ID.
*/
-static bool chamelium_autodiscover(struct chamelium *chamelium, int drm_fd)
+static bool chamelium_autodiscover(struct chamelium *chamelium)
{
- int candidate_ports[CHAMELIUM_MAX_PORTS];
- size_t candidate_ports_len;
- drmModeRes *res;
- drmModeConnector *connector;
- struct chamelium_port *port;
- size_t i, j, port_count;
- int port_id;
- uint32_t conn_id;
- struct chamelium_edid *edid;
- bool found;
- uint32_t discovered_conns[CHAMELIUM_MAX_PORTS] = {0};
- char conn_name[64];
struct timespec start;
+ struct chamelium_edid *edid;
+ size_t port_count;
+ size_t i;
+ bool is_any_port_mapped = false;
uint64_t elapsed_ns;
- candidate_ports_len = chamelium_get_video_ports(chamelium,
- candidate_ports);
-
+ int candidate_ports[CHAMELIUM_MAX_PORTS];
+ size_t candidate_ports_len =
+ chamelium_get_video_ports(chamelium, candidate_ports);
igt_assert(candidate_ports_len > 0);
igt_debug("Starting Chamelium port auto-discovery on %zu ports\n",
candidate_ports_len);
igt_gettime(&start);
+ /* Reset Chamelium to turn off all ports and test them one at a time. */
+ chamelium_reset(chamelium);
+
edid = chamelium_new_edid(chamelium, igt_kms_get_base_edid());
/* Set EDID and plug ports we want to auto-discover */
port_count = chamelium->port_count;
+ /* Iterate over every port that Chamelium supports and check if it's connected. */
for (i = 0; i < candidate_ports_len; i++) {
- port_id = candidate_ports[i];
-
- /* Get or add a chamelium_port slot */
- port = NULL;
+ int j;
+ int wait_interval_ms, wait_timeout_ms;
+ bool ret;
+ drmModeConnector *connector;
+ uint32_t conn_id = 0;
+ int drm_fd = chamelium->drm_fd;
+ char conn_name[64];
+
+ int port_id = candidate_ports[i];
+ /* Get or add a chamelium_port slot - The port could have been created earlier. */
+ struct chamelium_port *port = NULL;
for (j = 0; j < chamelium->port_count; j++) {
if (chamelium->ports[j].id == port_id) {
port = &chamelium->ports[j];
@@ -2581,100 +2634,66 @@ static bool chamelium_autodiscover(struct chamelium *chamelium, int drm_fd)
igt_assert(port_count < CHAMELIUM_MAX_PORTS);
port = &chamelium->ports[port_count];
port_count++;
-
+ igt_assert(port);
port->id = port_id;
}
+ /* Chameleon V3 works nicely with EDIDs assigned to ports that are not connected. */
chamelium_port_set_edid(chamelium, port, edid);
chamelium_plug(chamelium, port);
- }
-
- igt_info("Sleeping %d seconds for the hotplug to take effect.\n",
- CHAMELIUM_HOTPLUG_DETECTION_DELAY);
- sleep(CHAMELIUM_HOTPLUG_DETECTION_DELAY);
-
- /* Reprobe connectors and build the mapping */
- res = drmModeGetResources(drm_fd);
- if (!res)
- return false;
-
- for (i = 0; i < res->count_connectors; i++) {
- conn_id = res->connectors[i];
- /* Read the EDID and parse the Chamelium port ID we stored
- * there. */
- connector = drmModeGetConnector(drm_fd, res->connectors[i]);
- port_id = port_id_from_edid(drm_fd, connector);
- drmModeFreeConnector(connector);
- if (port_id < 0)
- continue;
-
- /* If we already have a mapping from the config file, check
- * that it's consistent. */
- found = false;
- for (j = 0; j < chamelium->port_count; j++) {
- port = &chamelium->ports[j];
- if (port->connector_id == conn_id) {
- found = true;
- igt_assert_f(port->id == port_id,
- "Inconsistency detected in .igtrc: "
- "connector %s is configured with "
- "Chamelium port %d, but is "
- "connected to port %d\n",
- port->name, port->id, port_id);
- break;
- }
+ /* The ITE chip on Chamelium V3 can only hold 1 EDID at a time, so let's test each port individually. */
+ wait_interval_ms = 1000;
+ wait_timeout_ms = CHAMELIUM_HOTPLUG_DETECTION_DELAY * 1000 +
+ wait_interval_ms;
+ igt_info(
+ "Polling every %f second(s) for %d seconds for the hotplug to take effect.\n",
+ (float)wait_interval_ms / 1000,
+ CHAMELIUM_HOTPLUG_DETECTION_DELAY);
+
+ ret = igt_wait(get_connector_id_for_port(chamelium, port_id,
+ &conn_id),
+ wait_timeout_ms, wait_interval_ms);
+ if (!ret || conn_id < 1) {
+ igt_info("Failed to auto-discover port %d\n", port_id);
+ goto unplug_port;
}
- if (found)
- continue;
+ is_any_port_mapped = true;
- /* We got a new mapping */
- found = false;
- for (j = 0; j < candidate_ports_len; j++) {
- if (port_id == candidate_ports[j]) {
- found = true;
- discovered_conns[j] = conn_id;
- break;
- }
- }
- igt_assert_f(found, "Auto-discovered a port (%d) we haven't "
- "setup\n", port_id);
- }
-
- drmModeFreeResources(res);
-
- /* We now have a Chamelium port ID ↔ DRM connector ID mapping:
- * candidate_ports contains the Chamelium port IDs and
- * discovered_conns contains the DRM connector IDs. */
- for (i = 0; i < candidate_ports_len; i++) {
- port_id = candidate_ports[i];
- conn_id = discovered_conns[i];
- if (!conn_id) {
- continue;
- }
-
- port = &chamelium->ports[chamelium->port_count];
+ /* If we found a connector for our port, increment the number of valid Chamelium ports. */
chamelium->port_count++;
- port->id = port_id;
+ /* With a valid port, assign all of its properties. */
port->type = chamelium_get_port_type(chamelium, port);
port->connector_id = conn_id;
port->adapter_allowed = false;
+ chamelium_set_port_path(port, conn_id, drm_fd);
+ /* Get and assign Connector name. */
connector = drmModeGetConnectorCurrent(drm_fd, conn_id);
snprintf(conn_name, sizeof(conn_name), "%s-%u",
- kmstest_connector_type_str(connector->connector_type),
- connector->connector_type_id);
+ kmstest_connector_type_str(connector->connector_type),
+ connector->connector_type_id);
drmModeFreeConnector(connector);
port->name = strdup(conn_name);
- chamelium_set_port_path(port, port->connector_id, drm_fd);
+
+unplug_port:
+ /* Unplug the port so we can move on to the next one. */
+ chamelium_unplug(chamelium, port);
}
+ /* After we're all set, turn on all supported ports */
+ for (i = 0; i < chamelium->port_count; i++) {
+ struct chamelium_port *port = &chamelium->ports[i];
+ chamelium_plug(chamelium, port);
+ }
+ sleep(CHAMELIUM_HOTPLUG_DETECTION_DELAY);
+
elapsed_ns = igt_nsec_elapsed(&start);
- igt_debug("Auto-discovery took %fms\n",
- (float) elapsed_ns / (1000 * 1000));
+ igt_debug("Auto-discovery took %fms and found %i connector(s)\n",
+ (float)elapsed_ns / (1000 * 1000), chamelium->port_count);
- return true;
+ return is_any_port_mapped;
}
static bool chamelium_read_config(struct chamelium *chamelium)
@@ -2828,7 +2847,7 @@ struct chamelium *chamelium_init(int drm_fd)
igt_info("Chamelium configured without port mapping, "
"performing autodiscovery\n");
- if (!chamelium_autodiscover(chamelium, drm_fd))
+ if (!chamelium_autodiscover(chamelium))
goto error;
if (chamelium->port_count != 0)
--
2.37.3.998.g577e59143f-goog
More information about the igt-dev
mailing list