From 27982e027ab2a4b063cd2d5f2d2c73faf0a670cf Mon Sep 17 00:00:00 2001 From: Ryan Murray Date: Thu, 29 Oct 2026 13:15:09 +1100 Subject: [PATCH] drm/apple: Release a CRTC whose Type-C route moved to another pipeline On the J616s, plugging an HDMI display in while a Type-C display held the hybrid pipeline (dcpext0) left both external displays dark. The fabric moves the Type-C display to dcpext1 so that HDMI gets dcpext0, and the hotplug of the moved display then tries to retrain its active CRTC. That CRTC is still dcpext0's, which no longer drives the port, so the reset is refused (-EINVAL) and link recovery starts. Meanwhile the compositor, which still sees the Type-C connector on dcpext0's CRTC, finds no CRTC for the HDMI display: mutter logs "No available CRTC for monitor" and falls back to the internal panel alone. When the connector's CRTC belongs to a pipeline its port is no longer routed to, release that CRTC instead of resetting it. The hotplug that follows lets the compositor pair the Type-C display with the pipeline that now drives it and the HDMI display with dcpext0. Pipelines whose routes follow their CRTC are unchanged. Assisted-by: LLM Signed-off-by: Ryan Murray --- drivers/gpu/drm/apple/iomfb.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/apple/iomfb.c b/drivers/gpu/drm/apple/iomfb.c index d2c7273e75e24..2004d34a7f942 100644 --- a/drivers/gpu/drm/apple/iomfb.c +++ b/drivers/gpu/drm/apple/iomfb.c @@ -871,6 +871,19 @@ bool dcp_crtc_can_retrain(struct drm_crtc *crtc, struct apple_connector *connect return to_apple_crtc(crtc)->dcp == pdev; } +/* + * A Type-C port whose pipelines do not follow their CRTC is driven by the + * pipeline its route is on; @crtc belonging to another one has lost it. + */ +static bool dcp_crtc_lost_route(struct drm_crtc *crtc, struct apple_connector *connector) +{ + struct platform_device *pdev = READ_ONCE(connector->dcp); + + return connector->port_encoder && pdev && + !dcp_typec_follows_crtc(platform_get_drvdata(pdev)) && + to_apple_crtc(crtc)->dcp != pdev; +} + static int dcp_retrain_active_crtc(struct apple_connector *connector) { struct drm_device *dev = connector->base.dev; @@ -882,10 +895,24 @@ static int dcp_retrain_active_crtc(struct apple_connector *connector) crtc = connector->base.state ? connector->base.state->crtc : NULL; if (crtc && crtc->state && crtc->state->active && - dcp_crtc_can_retrain(crtc, connector)) + dcp_crtc_lost_route(crtc, connector)) { + /* + * The port's route moved to another pipeline (an HDMI display + * took the hybrid): this CRTC cannot drive the display any + * more, and a reset would only be refused. Release it, so the + * hotplug that follows lets the compositor pair the display + * with the pipeline that now drives it, and this pipeline's + * own display with this CRTC. + */ + struct drm_mode_set set = { .crtc = crtc }; + + ret = drm_atomic_helper_set_config(&set, &ctx); + } else if (crtc && crtc->state && crtc->state->active && + dcp_crtc_can_retrain(crtc, connector)) { ret = drm_atomic_helper_reset_crtc(crtc, &ctx); - else + } else { ret = 0; + } DRM_MODESET_LOCK_ALL_END(dev, ctx, ret);