Skip to content
Open
Show file tree
Hide file tree
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
58 changes: 57 additions & 1 deletion drivers/gpu/drm/apple/dcp-fabric.c
Original file line number Diff line number Diff line change
Expand Up @@ -1378,6 +1378,45 @@ static int dcp_typec_port_busy(struct apple_dcp_typec_port *port,
return -ENOSPC;
}

/*
* PD controllers report a DP state at probe for ports with no display behind
* them, an empty port or one with a dock that has none, and whichever port is
* routed first wins the pipeline. A port whose sink asserts HPD and finds no
* free pipeline takes one over from a direct DP-alt port whose sink never
* did. Tunnels, ports in a SAFE transition and pipelines whose fixed output
* is in use are left alone. The released port drops its applied state, so
* its next update is routed again once a pipeline is free.
*/
static struct apple_dcp_typec_route *
dcp_typec_take_over_without_hpd(struct apple_dcp_typec_port *port)
{
struct apple_dcp_typec_route *candidate;

list_for_each_entry(candidate, &port->routes, port_link) {
struct apple_dcp_typec_route *held =
candidate->dcp->active_typec_route;
int ret;

if (!held || held->tunnel || held->port == port ||
held->port->hpd || held->port->dp_hpd ||
held->port->safe_pending ||
dcp_typec_route_busy(candidate))
continue;

ret = dcp_typec_route_deactivate(held);
if (ret)
return ERR_PTR(ret);
held->port->owner = NULL;
held->port->applied_valid = false;
dev_info(candidate->dcp->dev,
"released Type-C DPTX PHY %u held without HPD\n",
held->dptx_phy);
return candidate;
}

return NULL;
}

static int dcp_typec_route_set(struct typec_mux_dev *mux,
struct typec_mux_state *state)
{
Expand Down Expand Up @@ -1518,10 +1557,27 @@ static int dcp_typec_route_set(struct typec_mux_dev *mux,
}

if (!port->owner) {
/*
* Nothing behind the port asserts HPD yet, a dock without a
* display for one: leave the pipelines to ports with a display.
* The port controller reports HPD as a new status when a sink
* appears, and the port is routed then. A port that already
* owns a pipeline keeps it across an HPD drop, as before.
*/
if (!hpd) {
port->hpd = false;
port->applied_valid = true;
return 0;
}

best = dcp_typec_free_route(port);
if (!best)
best = dcp_typec_take_over_without_hpd(port);
if (IS_ERR(best))
return PTR_ERR(best);
/* Every pipeline is driving another port's display. */
if (!best)
return hpd ? dcp_typec_port_busy(port, route->dcp) : -ENOSPC;
return dcp_typec_port_busy(port, route->dcp);

ret = dcp_typec_route_activate(best, best->xbar);
if (ret)
Expand Down
2 changes: 1 addition & 1 deletion drivers/usb/typec/tipd/cd321x-pm-test.c
Original file line number Diff line number Diff line change
Expand Up @@ -713,7 +713,7 @@ static void cd321x_display_taken_test(struct kunit *test)
KUNIT_EXPECT_FALSE(test, cd321x_display_taken(0, true));
/* Other SoCs keep -EBUSY as the taken display. */
KUNIT_EXPECT_TRUE(test, cd321x_display_taken(-EBUSY, false));
KUNIT_EXPECT_FALSE(test, cd321x_display_taken(-ENOSPC, false));
KUNIT_EXPECT_TRUE(test, cd321x_display_taken(-ENOSPC, false));
KUNIT_EXPECT_FALSE(test, cd321x_display_taken(-EAGAIN, false));
KUNIT_EXPECT_FALSE(test, cd321x_display_taken(0, false));
}
Expand Down
8 changes: 5 additions & 3 deletions drivers/usb/typec/tipd/cd321x-pm.h
Original file line number Diff line number Diff line change
Expand Up @@ -355,12 +355,14 @@ static inline void cd321x_pm_remove(struct cd321x_pm_state *pm)
/*
* A display mux reports -ENOSPC when every display pipeline is driving
* another port. That lasts until the other display goes away, so the port's
* USB path is set up without the display and nothing is retried. Any other
* error, such as -EBUSY from a PHY in the middle of a transition, is retried.
* USB path is set up without the display and nothing is retried. The DCP
* fabric reports it as -ENOSPC on every SoC now; older trees used -EBUSY
* outside H17, which is still accepted there. Any other error, such as
* -EBUSY from an H17 PHY in the middle of a transition, is retried.
*/
static inline bool cd321x_display_taken(int ret, bool enospc)
{
return ret == (enospc ? -ENOSPC : -EBUSY);
return ret == -ENOSPC || (!enospc && ret == -EBUSY);
}

enum cd321x_dp_send {
Expand Down