Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions drivers/gpu/drm/apple/dcp-fabric.c
Original file line number Diff line number Diff line change
Expand Up @@ -1378,6 +1378,52 @@ static int dcp_typec_port_busy(struct apple_dcp_typec_port *port,
return -ENOSPC;
}

/*
* @waiting has a display (HPD up) but no pipeline. A direct DP-alt port that
* holds one of @waiting's pipelines with no display behind it (its sink has
* never asserted HPD, as a USB-C hub with a DP output and nothing plugged in)
* gives it up: a pipeline goes to a display, not to whichever port entered DP
* mode first. The idle port keeps DP mode and is routed again when a pipeline
* is free and its own sink asserts HPD. Returns true if one was released.
*/
static bool dcp_typec_reclaim_idle(struct apple_dcp_typec_port *waiting)
{
struct apple_dcp_typec_route *route;
struct apple_dcp_typec_port *idle;

lockdep_assert_held(&dcp_typec_fabric_lock);

list_for_each_entry(route, &waiting->routes, port_link) {
list_for_each_entry(idle, &dcp_typec_ports, link) {
struct apple_dcp_typec_route *owner = idle->owner;
struct apple_dcp *dcp;
const char *from = NULL, *to = NULL;

if (idle == waiting || !owner || owner->tunnel ||
owner->dcp != route->dcp || idle->safe_pending ||
idle->dp_hpd || idle->hpd)
continue;
dcp = owner->dcp;
if (dcp->typec_cable_connected ||
(dcp->typec_connector &&
READ_ONCE(dcp->typec_connector->connected)))
continue;
if (dcp_typec_route_deactivate(owner))
return false;
idle->preferred_route = owner;
idle->owner = NULL;
idle->busy_noted = false;
of_property_read_string(idle->connector_np, "label", &from);
of_property_read_string(waiting->connector_np, "label", &to);
dev_info(dcp->dev,
"external display pipeline moves from %s (no display) to the display on %s\n",
from ?: "a USB-C port", to ?: "a USB-C port");
return true;
}
}
return false;
}

static int dcp_typec_route_set(struct typec_mux_dev *mux,
struct typec_mux_state *state)
{
Expand Down Expand Up @@ -1519,6 +1565,9 @@ static int dcp_typec_route_set(struct typec_mux_dev *mux,

if (!port->owner) {
best = dcp_typec_free_route(port);
/* A pipeline held by a port with no display goes to this one. */
if (!best && hpd && dcp_typec_reclaim_idle(port))
best = dcp_typec_free_route(port);
/* Every pipeline is driving another port's display. */
if (!best)
return hpd ? dcp_typec_port_busy(port, route->dcp) : -ENOSPC;
Expand Down