From 9a0b5c38f95b33f0e82d6cfd7d25bbc1045efe0e Mon Sep 17 00:00:00 2001 From: areofyl Date: Sat, 6 Jun 2026 16:29:56 -0700 Subject: [PATCH] apple-drm: quiesce disconnected DCPs on suspend, fix resume crash drm_mode_config_helper_suspend returns -EINVAL when a secondary DCP (external display) is disconnected, which blocks the entire PM suspend path. Even if suspend returns 0 anyway, the DCP RTKit firmware keeps running and generates mailbox IRQs that immediately wake from s2idle. In suspend_noirq, iterate CRTCs and call apple_rtkit_quiesce on disconnected DCPs so they stop generating IRQs right before s2idle entry. In resume_noirq, re-boot them with apple_rtkit_boot. Also fix the resume path: drm_mode_config_helper_resume crashes on NULL suspend_state when suspend failed, so check for that and fall back to a hotplug event instead. Tested on M1 MacBook Air (J313) with USB-C display, fairydust 6.18.10. Signed-off-by: areofyl --- drivers/gpu/drm/apple/apple_drv.c | 63 +++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/apple/apple_drv.c b/drivers/gpu/drm/apple/apple_drv.c index 0f36dad6f96351..010eb156dc7eb6 100644 --- a/drivers/gpu/drm/apple/apple_drv.c +++ b/drivers/gpu/drm/apple/apple_drv.c @@ -38,7 +38,11 @@ #include #include +#include + #include "dcp.h" +#include "dcp-internal.h" +#include "connector.h" #include "plane.h" #define DRIVER_NAME "apple" @@ -627,9 +631,55 @@ MODULE_DEVICE_TABLE(of, of_match); static int apple_platform_suspend(struct device *dev) { struct apple_drm_private *apple = dev_get_drvdata(dev); + int ret; + + if (apple) { + ret = drm_mode_config_helper_suspend(&apple->drm); + if (ret) + dev_warn(dev, "drm suspend helper failed: %d, will hotplug on resume\n", ret); + } + + return 0; +} + +static int apple_platform_suspend_noirq(struct device *dev) +{ + struct apple_drm_private *apple = dev_get_drvdata(dev); + struct drm_crtc *crtc; + + if (!apple || apple->drm.mode_config.suspend_state) + return 0; + + drm_for_each_crtc(crtc, &apple->drm) { + struct apple_crtc *acrtc = to_apple_crtc(crtc); + struct apple_dcp *dcp = platform_get_drvdata(acrtc->dcp); + + if (dcp && dcp->connector && !dcp->connector->connected && dcp->rtk) { + dev_info(dev, "quiescing disconnected DCP %d\n", dcp->index); + apple_rtkit_quiesce(dcp->rtk); + } + } + + return 0; +} + +static int apple_platform_resume_noirq(struct device *dev) +{ + struct apple_drm_private *apple = dev_get_drvdata(dev); + struct drm_crtc *crtc; + + if (!apple || apple->drm.mode_config.suspend_state) + return 0; - if (apple) - return drm_mode_config_helper_suspend(&apple->drm); + drm_for_each_crtc(crtc, &apple->drm) { + struct apple_crtc *acrtc = to_apple_crtc(crtc); + struct apple_dcp *dcp = platform_get_drvdata(acrtc->dcp); + + if (dcp && dcp->connector && !dcp->connector->connected && dcp->rtk) { + dev_info(dev, "re-booting DCP %d after quiesce\n", dcp->index); + apple_rtkit_boot(dcp->rtk); + } + } return 0; } @@ -638,14 +688,21 @@ static int apple_platform_resume(struct device *dev) { struct apple_drm_private *apple = dev_get_drvdata(dev); - if (apple) + if (!apple) + return 0; + + if (apple->drm.mode_config.suspend_state) drm_mode_config_helper_resume(&apple->drm); + else + drm_kms_helper_hotplug_event(&apple->drm); return 0; } static const struct dev_pm_ops apple_platform_pm_ops = { .suspend = apple_platform_suspend, + .suspend_noirq = apple_platform_suspend_noirq, + .resume_noirq = apple_platform_resume_noirq, .resume = apple_platform_resume, }; #endif