From b9b7b17632ebcbb84ac222ab518e8155ea09d3a1 Mon Sep 17 00:00:00 2001 From: Max Haack Date: Wed, 11 Nov 2026 12:38:54 -0500 Subject: [PATCH] drm: apple: free a DP tunnel route when its HPD release times out dcp_tb_release_locked() keeps the route when dcp_dptx_disconnect_oob() fails, expecting a later tunnel request on the port to release it. On unplug, apple_dpin_release_binding() calls the release once and does not retry. The route then holds its pipeline until reboot. dcp_dptx_disconnect_oob() fails this way when DCP's DPTX service stops responding and dptxport_set_hpd() returns -ETIMEDOUT. T8112 has a single external pipeline, and any other display is then refused: apple-dcp 271c00000.dcp: display on USB-C Left-front waits for a free external display pipeline ... Treat -ETIMEDOUT as a completed release and free the route. Tested on J413 with an LG UltraFine 5K and a faulty Thunderbolt cable. --- drivers/gpu/drm/apple/dcp-fabric.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/apple/dcp-fabric.c b/drivers/gpu/drm/apple/dcp-fabric.c index 29321f8032a490..5e88d61252b002 100644 --- a/drivers/gpu/drm/apple/dcp-fabric.c +++ b/drivers/gpu/drm/apple/dcp-fabric.c @@ -2311,11 +2311,25 @@ static int dcp_tb_release_locked(struct apple_dcp_typec_port *port, return ret; } dcp = (*slot)->dcp; - /* A refused display release keeps the route and its binding owned. */ + /* + * A refused display release keeps the route and its binding owned. + * A timed-out one frees them: apple_dpin_release_binding() does not + * retry after an unplug. + */ if (port->hpd || dcp->typec_cable_connected) { ret = dcp_dptx_disconnect_oob(to_platform_device(dcp->dev), 0); - if (ret) + if (ret == -ETIMEDOUT) { + dev_warn(dcp->dev, + "DP tunnel dpin%u: HPD release timed out, releasing its pipeline anyway\n", + dpin); + ret = dcp_dptx_disconnect(dcp, 0); + if (ret) + dev_warn(dcp->dev, + "DP tunnel dpin%u: DPTX release failed: %d\n", + dpin, ret); + } else if (ret) { return ret; + } } ret = dcp_typec_route_deactivate(*slot); if (ret) {