From 4c78e25bb83f26bd8fbcc3a8db00123866af4f44 Mon Sep 17 00:00:00 2001 From: Alexey Makhov Date: Mon, 21 Sep 2026 22:57:19 +0300 Subject: [PATCH 1/2] drm/apple: don't treat a slow poweroff clear swap as a DCP crash On DP unplug the DCP firmware first tears down the link on its own (HPD removal, M3 power down, set_device_enabled 1 -> 0) and only then processes the clear swap queued by iomfb_poweroff(). That teardown was measured at ~53 ms, just above the 50 ms the driver waited. The timeout silently set dcp->crashed, after which dcp_crtc_atomic_check() rejects every commit on the CRTC with -EINVAL until reboot: the monitor is detected on the next plug but never gets a modeset (black screen), and the compositor cannot even disable the output. Wait up to 1000 ms, matching the setPowerState(0) wait below. Even then, a clear swap queued behind a slow operation (unplug during a modeset, or right after resume where powering on took about a second) can miss the deadline, so don't treat a timeout as a crash at all: warn and continue powering off, like the setPowerState(0) wait does. Real firmware crashes are reported via dcp_rtk_crashed(). A late reply is safe since the swap cookie is refcounted. Link: https://github.com/AsahiLinux/linux/issues/634 Signed-off-by: Alexey Makhov --- drivers/gpu/drm/apple/iomfb_template.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index cf40e273a2f43c..bb83d18f10c5d4 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -926,15 +926,25 @@ void DCP_FW_NAME(iomfb_poweroff)(struct apple_dcp *dcp) dcp_swap_start(dcp, false, &swap_req, dcp_swap_clear_started, cookie); - ret = wait_for_completion_timeout(&cookie->done, msecs_to_jiffies(50)); + /* + * On DP unplug DCP firmware first tears down the link itself (HPD + * removal, M3 power down, set_device_enabled 1 -> 0), which can take + * more than 50 ms. Only processes the clear swap afterwards. + * + * A timeout here is not a crash: firmware crashes are reported via + * dcp_rtk_crashed(), and dcp->crashed permanently rejects every atomic + * check on this CRTC. Warn and power off anyway, like the + * setPowerState(0) wait below does. A late reply is safe, the cookie is + * refcounted. + */ + ret = wait_for_completion_timeout(&cookie->done, msecs_to_jiffies(1000)); swap_id = cookie->swap_id; kref_put(&cookie->refcount, release_swap_cookie); - if (ret <= 0) { - dcp->crashed = true; - return; - } - - dev_dbg(dcp->dev, "%s: clear swap submitted: %u\n", __func__, swap_id); + if (ret == 0) + dev_warn(dcp->dev, "%s: clear swap timeout %u ms\n", __func__, 1000); + else + dev_dbg(dcp->dev, "%s: clear swap submitted: %u after %u ms\n", + __func__, swap_id, 1000 - jiffies_to_msecs(ret)); poff_cookie = kzalloc(sizeof(*poff_cookie), GFP_KERNEL); if (!poff_cookie) From 1a853aa8a1e6fda3483b2d209aaca19bb5ce5836 Mon Sep 17 00:00:00 2001 From: Alexey Makhov Date: Mon, 21 Sep 2026 10:42:31 +0300 Subject: [PATCH 2/2] drm/apple: stop poweroff early if DCP crashed during the clear swap If the RTKit crashed callback fires while iomfb_poweroff() waits for the clear swap, the wait runs into its timeout and the function then queues abort_swaps, which RTKit refuses for a crashed co-processor, and waits another second for a reply that cannot arrive. Return right after the timeout when dcp->crashed is set. This is the same exit the function took on any clear swap timeout before, so callers already handle it. The unlocked read of dcp->crashed matches dcp_crtc_atomic_check(); a stale value only means taking the previous, slower but safe path. Signed-off-by: Alexey Makhov --- drivers/gpu/drm/apple/iomfb_template.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/apple/iomfb_template.c b/drivers/gpu/drm/apple/iomfb_template.c index bb83d18f10c5d4..77eb2dbf42f693 100644 --- a/drivers/gpu/drm/apple/iomfb_template.c +++ b/drivers/gpu/drm/apple/iomfb_template.c @@ -940,11 +940,18 @@ void DCP_FW_NAME(iomfb_poweroff)(struct apple_dcp *dcp) ret = wait_for_completion_timeout(&cookie->done, msecs_to_jiffies(1000)); swap_id = cookie->swap_id; kref_put(&cookie->refcount, release_swap_cookie); - if (ret == 0) + if (ret == 0) { + /* crashed while we waited, RTKit refuses any further messages */ + if (dcp->crashed) { + dev_warn(dcp->dev, "%s: DCP crashed during clear swap\n", + __func__); + return; + } dev_warn(dcp->dev, "%s: clear swap timeout %u ms\n", __func__, 1000); - else + } else { dev_dbg(dcp->dev, "%s: clear swap submitted: %u after %u ms\n", __func__, swap_id, 1000 - jiffies_to_msecs(ret)); + } poff_cookie = kzalloc(sizeof(*poff_cookie), GFP_KERNEL); if (!poff_cookie)