From 121fc9d380226c5c443cbdcb742c732d2bac020f Mon Sep 17 00:00:00 2001 From: Diljit Singh Date: Sun, 8 Nov 2026 17:30:49 -0500 Subject: [PATCH] pmdomain: apple: pmgr-pwrstate: keep domains another owner raised across a sleep On the T8140 the AOP firmware raises audio domains of its own in the always-on PMGR. Once the internal microphone array (HPAI) has run, audio_leap_rx1 is on and audio_leap_rx2-4 and audio_mca3-5_m are clock gated, while genpd, with no consumer for them, has them off. The sleep keeps them. The resume pass then put them where genpd's books say, power-gated, and the AOP never raises them again: after every sleep the array's DMA ran at 48 kHz on all-zero samples, while the low-power microphone kept working (measured). At the noirq suspend, note which domains genpd has off with a target still on. On resume, lower such a domain only if the sleep changed its target. On the J700 the array then records after the first deep sleep and after later sleeps, including a stream held open across the sleep (measured). Signed-off-by: Diljit Singh --- drivers/pmdomain/apple/pmgr-pwrstate.c | 44 ++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/drivers/pmdomain/apple/pmgr-pwrstate.c b/drivers/pmdomain/apple/pmgr-pwrstate.c index 5b19dc9125b64..b2b793e904ec9 100644 --- a/drivers/pmdomain/apple/pmgr-pwrstate.c +++ b/drivers/pmdomain/apple/pmgr-pwrstate.c @@ -71,6 +71,10 @@ struct apple_pmgr_ps { bool strict_state; bool wait_parent; u32 reset_time_us; + /* Off in genpd but on in hardware as the system slept: someone else raised it. */ + bool sleep_raised_by_other; + /* That domain's target as the system slept. */ + u32 sleep_target; }; #define genpd_to_apple_pmgr_ps(_genpd) container_of(_genpd, struct apple_pmgr_ps, genpd) @@ -417,6 +421,32 @@ static int apple_pmgr_ps_resume_early(struct device *dev) return 0; } +/* + * By the noirq phase genpd has powered off what it is going to. A domain it has off + * whose target is still on was raised by a coprocessor's firmware: on T8140 the AOP + * raises audio domains of its own in the always-on PMGR, and the sleep keeps them. + */ +static int apple_pmgr_ps_suspend_noirq(struct device *dev) +{ + struct apple_pmgr_ps *ps = dev_get_drvdata(dev); + unsigned long flags; + u32 reg; + + ps->sleep_raised_by_other = false; + if (ps->firmware_owned || ps->inherited_on) + return 0; + + spin_lock_irqsave(&ps->genpd.slock, flags); + if (!regmap_read(ps->regmap, ps->offset, ®)) { + ps->sleep_target = FIELD_GET(APPLE_PMGR_PS_TARGET, reg); + ps->sleep_raised_by_other = ps->genpd.status == GENPD_STATE_OFF && + ps->sleep_target != APPLE_PMGR_PS_PWRGATE; + } + spin_unlock_irqrestore(&ps->genpd.slock, flags); + + return 0; +} + /* * A firmware-driven suspend powers the SoC down and its sleep engine leaves the * domains in states of its own: AUTO_ENABLE cleared, some at clock gating. genpd @@ -427,7 +457,8 @@ static int apple_pmgr_ps_resume_early(struct device *dev) * 30 others (measured under aura). Which of them Linux needs isn't established, * so this follows genpd's books rather than a list. A domain whose driver took it * down for the sleep, as the USB host's PHY does, is that driver's to raise, in - * its own order. + * its own order. A domain someone else raised before the sleep, and still at that + * target, stays theirs. */ static int apple_pmgr_ps_resume_noirq(struct device *dev) { @@ -444,7 +475,14 @@ static int apple_pmgr_ps_resume_noirq(struct device *dev) ret = regmap_read(ps->regmap, ps->offset, ®); if (!ret) { if (ps->genpd.status == GENPD_STATE_OFF) { - if (FIELD_GET(APPLE_PMGR_PS_TARGET, reg) != APPLE_PMGR_PS_PWRGATE) + u32 target = FIELD_GET(APPLE_PMGR_PS_TARGET, reg); + + /* + * Lowering an AOP-raised audio domain here leaves the internal + * microphone array's data path dark: the AOP never raises it again. + */ + if (target != APPLE_PMGR_PS_PWRGATE && + !(ps->sleep_raised_by_other && target == ps->sleep_target)) ret = apple_pmgr_ps_set(&ps->genpd, APPLE_PMGR_PS_PWRGATE, false); } else { @@ -846,7 +884,7 @@ MODULE_DEVICE_TABLE(of, apple_pmgr_ps_of_match); static const struct dev_pm_ops apple_pmgr_ps_pm_ops = { LATE_SYSTEM_SLEEP_PM_OPS(apple_pmgr_ps_suspend_late, apple_pmgr_ps_resume_early) - NOIRQ_SYSTEM_SLEEP_PM_OPS(NULL, apple_pmgr_ps_resume_noirq) + NOIRQ_SYSTEM_SLEEP_PM_OPS(apple_pmgr_ps_suspend_noirq, apple_pmgr_ps_resume_noirq) }; static struct platform_driver apple_pmgr_ps_driver = {