Skip to content

Commit b74560d

Browse files
committed
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 <areofyl@users.noreply.github.com>
1 parent b94beb5 commit b74560d

1 file changed

Lines changed: 60 additions & 3 deletions

File tree

drivers/gpu/drm/apple/apple_drv.c

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,11 @@
3838
#include <drm/drm_vblank.h>
3939
#include <drm/drm_fixed.h>
4040

41+
#include <linux/soc/apple/rtkit.h>
42+
4143
#include "dcp.h"
44+
#include "dcp-internal.h"
45+
#include "connector.h"
4246
#include "plane.h"
4347

4448
#define DRIVER_NAME "apple"
@@ -627,9 +631,55 @@ MODULE_DEVICE_TABLE(of, of_match);
627631
static int apple_platform_suspend(struct device *dev)
628632
{
629633
struct apple_drm_private *apple = dev_get_drvdata(dev);
634+
int ret;
635+
636+
if (apple) {
637+
ret = drm_mode_config_helper_suspend(&apple->drm);
638+
if (ret)
639+
dev_warn(dev, "drm suspend helper failed: %d, will hotplug on resume\n", ret);
640+
}
641+
642+
return 0;
643+
}
644+
645+
static int apple_platform_suspend_noirq(struct device *dev)
646+
{
647+
struct apple_drm_private *apple = dev_get_drvdata(dev);
648+
struct drm_crtc *crtc;
649+
650+
if (!apple || apple->drm.mode_config.suspend_state)
651+
return 0;
652+
653+
drm_for_each_crtc(crtc, &apple->drm) {
654+
struct apple_crtc *acrtc = to_apple_crtc(crtc);
655+
struct apple_dcp *dcp = platform_get_drvdata(acrtc->dcp);
656+
657+
if (dcp && dcp->connector && !dcp->connector->connected && dcp->rtk) {
658+
dev_info(dev, "quiescing disconnected DCP %d\n", dcp->index);
659+
apple_rtkit_quiesce(dcp->rtk);
660+
}
661+
}
662+
663+
return 0;
664+
}
665+
666+
static int apple_platform_resume_noirq(struct device *dev)
667+
{
668+
struct apple_drm_private *apple = dev_get_drvdata(dev);
669+
struct drm_crtc *crtc;
670+
671+
if (!apple || apple->drm.mode_config.suspend_state)
672+
return 0;
630673

631-
if (apple)
632-
return drm_mode_config_helper_suspend(&apple->drm);
674+
drm_for_each_crtc(crtc, &apple->drm) {
675+
struct apple_crtc *acrtc = to_apple_crtc(crtc);
676+
struct apple_dcp *dcp = platform_get_drvdata(acrtc->dcp);
677+
678+
if (dcp && dcp->connector && !dcp->connector->connected && dcp->rtk) {
679+
dev_info(dev, "re-booting DCP %d after quiesce\n", dcp->index);
680+
apple_rtkit_boot(dcp->rtk);
681+
}
682+
}
633683

634684
return 0;
635685
}
@@ -638,14 +688,21 @@ static int apple_platform_resume(struct device *dev)
638688
{
639689
struct apple_drm_private *apple = dev_get_drvdata(dev);
640690

641-
if (apple)
691+
if (!apple)
692+
return 0;
693+
694+
if (apple->drm.mode_config.suspend_state)
642695
drm_mode_config_helper_resume(&apple->drm);
696+
else
697+
drm_kms_helper_hotplug_event(&apple->drm);
643698

644699
return 0;
645700
}
646701

647702
static const struct dev_pm_ops apple_platform_pm_ops = {
648703
.suspend = apple_platform_suspend,
704+
.suspend_noirq = apple_platform_suspend_noirq,
705+
.resume_noirq = apple_platform_resume_noirq,
649706
.resume = apple_platform_resume,
650707
};
651708
#endif

0 commit comments

Comments
 (0)