Skip to content

arm64: dts: apple: t8132-j713: Hold AOP messages in system sleep (depends on #820, #831) - #450

Open
erikwb wants to merge 22 commits into
aurora-silicon:integration/aurorafrom
erikwb:j713/aop-mbox-sleep
Open

erikwb wants to merge 22 commits into
aurora-silicon:integration/aurorafrom
erikwb:j713/aop-mbox-sleep

Conversation

@erikwb

@erikwb erikwb commented Oct 25, 2026 •

Copy link
Copy Markdown

Holds the J713 AOP's once-a-second report in its mailbox during suspend-to-idle, so the report no longer wakes the CPUs.

Depends on #820 and #831.

This was split out of the suspend-to-idle series (first #449, now #831), because that series doesn't depend on the AOP series. Rebased from the closed #434 onto #820, so the AOP commits are carried only once. The commit itself is unchanged.

Commit

  • arm64: dts: apple: t8132-j713: Hold AOP messages in system sleep

Testing

On the J713, on integration/aurora 41c4055 with #820, the suspend-to-idle series (now #830 and #831), #432 and #429:
over 222 s of s2idle, the AOP mailbox (390608000.mailbox-recv) took 3 interrupts. It took 10 in the first 10 s awake after resume.

Again with the whole J713 PR stack (#830–#833, #819/#820, #531, #825): over 82 s of s2idle, the AOP mailbox took 5 interrupts.

Earlier, with the suspend-to-idle series (then #449) on 26.6.2 firmware: over 112 s of s2idle, the AOP mailbox (390608000.mailbox-recv) took 3 interrupts, against 946 in 938 s without the flag. The AOP audio worked after resume.

This change was developed and tested by Claude (Anthropic), working with me on the J713.

@RyanTheTide

RyanTheTide commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

This head adds the AOP mailbox hold flag only on J713. The reported 112-second s2idle run and working AOP audio after resume are the board evidence. The parent commits from #434 are already part of this branch and must be applied only once.

The stacked kernel at 5b6f5515e2f8 passed both full universal and Fedora-base WERROR builds (Image, modules and DTBs); both preserve all 131 common-config settings. ARM64 16 KiB KUnit passed 275/275. DCP host tests passed 63/63, and the PMGR and radio SID checks passed. The M4 host suite has the same five failing groups as the pinned base (18/23 on each); the AFKv2 HPD harness also fails on both. These are recorded failures, not a full host-suite pass. All six T8132 DTBs were checked: 13 schema diagnostics against 11 on the base, with exactly two new DCP dma-noncoherent binding errors from #429. The seven targeted binding checks passed. These software gates do not replace the required shared-code boots.

No separate issue found in the one-line follow-up. Merge after both #434 and #449. It inherits #449's lost-retention review hold and the combined kernel's shared-code boot requirement.

erikwb added 22 commits November 1, 2026 16:58
The T8132 (M4) AOP runs the same firmware transport as the T6041 one, and
its bootloader hands it over the same way. It also programs the AOP DART's
address filter (DAPF) and reserves the filter's windows, which the firmware
reaches through its DART at their physical addresses. The windows come in
memory-region with the firmware segments, up to 16 of them.

Signed-off-by: Erik Bourget <erik@ebourget.net>
The T8132 (M4) AOP takes the T6040 boot arguments (EC0p 0x100_0000_0000,
nCal 0, alig 64, AOPt 0): iBoot leaves the boot-argument table at its
defaults in the AOP SRAM and the AOP halted, and the ADT gives __ETEXT the
DART address 0x100_0000_0000. The bootloader programs the AOP DART's DAPF
and reserves the firmware segments and the DAPF windows, so the T6040
configuration applies unchanged.

Tested on J713 (MacBook Air 13" M4) with m1n1 handing the AOP over (its
memory-region holds __ETEXT, __OS_LOG and the 15 DAPF windows): the AOP
starts, and the microphones capture (3 channels, 48 kHz) through
aop_audio.

Signed-off-by: Erik Bourget <erik@ebourget.net>
The T8132 AOP audio service is the T6041 one: the same LPAI microphone
flow, and the same ADMAC channel map for the speakers and the jack. Allow
"apple,t8132-aop-audio" in front of the T6041 compatible.

A machine brought up in steps lists only the streams it has so far, so
allow the microphones alone, and the microphones with the jack, besides
the speaker layouts.

Signed-off-by: Erik Bourget <erik@ebourget.net>
Describe the T8132 AOP with its mailbox and DART, and the audio fabric's
two ADMACs (admac-leap-ns on DART stream 10, admac-base-ns on stream 8),
all disabled. Addresses and interrupts are the ADT's; the ADMAC channel
map is the T6041 one: the microphones on LEAP channel 1.

Signed-off-by: Erik Bourget <erik@ebourget.net>
Enable the AOP, its DART and mailbox, and the ADMACs on the MacBook Air
13" M4, with the AOP audio service for the three microphones.

/chosen already asks for the AOP's handoff: m1n1 programs the AOP DART's
DAPF and adds the firmware segments and the DAPF windows to the AOP's
memory-region, or disables the AOP.

Signed-off-by: Erik Bourget <erik@ebourget.net>
The jack component was only registered along with the speaker one, so a
machine whose audio node lists the jack's DMA channels but not yet the
speakers' got no jack. Register the speaker component only where the node
lists "spkr", and the jack's where it lists "cout" and "cin", each on its
own. Creating the services does not reach the firmware; nothing changes
for the J616s, which lists all of them.

Signed-off-by: Erik Bourget <erik@ebourget.net>
The MacBook Air 13" M4 (J713) headphone jack is a card of its own on the
AOP, like the J616s one.

Signed-off-by: Erik Bourget <erik@ebourget.net>
The J713 jack runs on the AOP firmware's MCA cluster 2 with the same
CS42L84 setup as the J616s jack. Use the J616s jack configuration,
including its -6 dB cap on the headphone DAC: this board's analog full
scale is unmeasured too.

Signed-off-by: Erik Bourget <erik@ebourget.net>
Add the CS42L84 on i2c1 (reset on nub GPIO 17, interrupt on AP GPIO 98,
from the ADT), the jack's ADMAC channels on the AOP audio node (base-ns
TX2 and RX2, as on the J616s) and the jack's own card.

The codec is the first consumer of the i2c1 power domain, so the J713
power-domain policy now enables it; it has no parent domains.

Signed-off-by: Erik Bourget <erik@ebourget.net>
The MacBook Air 13" M4 (J713) speakers are a card of their own on the
AOP's speaker stream, like the J616s ones.

Signed-off-by: Erik Bourget <erik@ebourget.net>
The MacBook Air 13" M4 (J713) runs its four SN012776 amps, a woofer and
a tweeter per side, on the AOP's speaker stream exactly like the J616s:
the same 250-bit frame, I2S with an inverted frame clock, six playback
and twelve sense slots, of which the four amps take the first four and
eight. Amp gain 15 and the -20 dB safe volume as on the other SN012776
machines.

The speaker front-end's channel count was one per amp; add
speaker_fe_channels for a CPU stream with more slots than amps, and give
the J713 six. Its last two channels are silent.

Signed-off-by: Erik Bourget <erik@ebourget.net>
Add the four SN012776 amps on i2c3 from the ADT (audio-speaker at 0x38
and its speaker1..3: left woofer 0x38, right woofer 0x3b, left tweeter
0x39, right tweeter 0x3c), each with its sense slots from
speaker-config (imon at RX byte 4i, vmon at 4i + 2), the shared
interrupt on AP GPIO 14 and the shared shutdown line on AP GPIO 15 as
the fail-closed SDZ group's fixed regulator. The speaker card lists the
amps in the ADT provider order, so each takes its native playback slot,
with the amp-tx-zd-config idle masks. The AOP audio node gets the
speaker and sense channels (base-ns TX0 and RX0): the J616s stream, of
which the four amps take the first four playback and eight sense
slots. The amps are the first consumer of the i2c3 power domain.

Signed-off-by: Erik Bourget <erik@ebourget.net>
On an AOP-fed speaker stream the TDM bus keeps running for a while after
the playback stream stops, and the amplifiers' current and voltage sense
slots are still sampled in that time. An amplifier put in software
shutdown then can leave garbage in its sense slots.

Add a property for such amplifiers: they must not enter software
shutdown until the bus has stopped.

Signed-off-by: Erik Bourget <erik@ebourget.net>
On the J713 the AOP keeps the speaker TDM bus running for a while after
a playback stream stops, and the speaker protection still reads the
amplifiers' I/V sense words in that time. Each amp is muted and then
shut down in turn as the stream ends, so the shutdowns land in that
tail. The right tweeter's voltage sense slot then reads full scale for
about 20 samples at every stream stop, even for a stream of digital
silence played with the volume locked; the protection daemons charge
that as power.

With apple,deferred-shutdown, mute the amp at the stream's end as
before, but put it in software shutdown only 150 ms later, after the bus
has stopped (it ran on for up to 73 ms on the J713). A stream that
starts in between cancels it. Suspend and removal shut the amp down at
once. Such an amp sees the bus clock stop while it waits, and may see it
latched again after its shutdown; that clock-only fault is expected then
and no longer logged. Amps without the property keep the current
behaviour.

On a J713 the full-scale words in the stream tails go from about 90 per
test (three silent streams and a chord) to none, with no fault logged,
and every amp still ends in software shutdown.

Signed-off-by: Erik Bourget <erik@ebourget.net>
The J713's AOP keeps the speaker bus running briefly after a stream
stops, and the speaker protection reads the amps' sense words in that
time. Have the four amps wait for the bus clock to stop before they go
to software shutdown.

Signed-off-by: Erik Bourget <erik@ebourget.net>
With apple,deferred-shutdown the muted amp stays powered after a stream
until the bus clock stops, and keeps latching TDM clock faults while it
waits; the fault handler ignores them while stream_stopped is set.  The
amps share one fault line and each handler reads six latches over I2C,
so a latch can reach its handler several milliseconds late.  When the
next stream unmutes the amp in that window, stream_stopped is already
clear and the stale latch is reported as a fault on the new stream.

On the J713 this hit the first amp the link unmutes on 9 of 32 silent
streams that started within 140 ms of the end of the last one, and on
none of 32 that started later.

At unmute, clear the latches before clearing stream_stopped when the
first latch register holds only the clock fault (the clear takes every
latch, so any other fault there is left for the handler), and hold a
lock across the handler's read and judgement of the latches so that
the unmute cannot fall between them.  With this, none of 96 silent
streams logged a fault, 48 of them started within 140 ms of the last.

Signed-off-by: Erik Bourget <erik@ebourget.net>
…leep

Since the speaker volumes are locked from suspend-prepare, the speakers
stay silent after the first system sleep. Locking clips every speaker
volume to the lock's ceiling (1, -100 dB on the SN012776), and unlocking
only lifts the ceiling again. After a lost lease that is fine:
speakersafetyd gets -ETIMEDOUT, restarts and sets the volumes itself. The
system-sleep lock is deliberately not reported as a lost lease, so the
daemon keeps its state and never writes the volumes again. They stay at
-100 dB until the daemon restarts. alsactl then stores them that way at
shutdown and restores them at the next boot, where they stay unless the
daemon's cold start sets the volumes again after that restore.

Keep the speaker volumes at suspend-prepare, before the lock clips them,
and set them again at the first unlock after it. That is the daemon's
first ping after the resume, which in practice comes when playback next
starts. These are the values the daemon set and still assumes.

The daemon may not ping between two sleeps. A suspend-prepare that finds
the volumes locked already keeps what an earlier one saved, so a second
sleep before any playback doesn't lose them. Hold the card's controls
for reading from keeping the volumes until they are clipped, so a volume
the daemon writes meanwhile can't be overwritten by an older, higher one
at the unlock. Hold them as well in the work that can unlock after a
back-end starts or stops, so setting the volumes again there doesn't race
a volume write either. Control writes hold them for writing and take
volume_lock_mutex inside, in the same order.

A new owner of the lock, such as a restarted daemon, sets the volumes
itself: drop what was kept then, and when the card is removed. Nothing
is kept for a card that is shutting down. The card's remove waits for
the unlock work, which takes the card's controls, while the snd_card
still exists.

Tested on a J713: after a suspend-to-idle the four speaker volumes are
back at 0 dB and the speakers play, without restarting speakersafetyd.
The KUnit cases go through the lock control and the PM notifier: one
sleep, two sleeps before the daemon pings, and a new owner after a sleep.

Fixes: 1cbce20 ("ASoC: apple: macaudio: lock the speaker volumes before userspace freezes")
Signed-off-by: Erik Bourget <erik@ebourget.net>
The AOP firmware keeps a device attachment for as long as it runs, and
the driver has no way to drop one at removal.  When the module is
loaded again, the probe's attach calls for the microphone devices get
kIOReturnBusy back and the probe fails.  The machine then has no audio
until it reboots: with the T6040 flow the speaker and jack streams are
registered by the same probe and are lost as well.

The speaker and jack services already treat that return as attached.
Do the same in the probe, and share the constant with aop_spkr.  The
probe's property writes that follow are accepted by the still attached
devices.

Signed-off-by: Erik Bourget <erik@ebourget.net>
The component probe resets the amp and reinitialises the register
cache, which clears the SDOUT idle slot mask and the force-0 enable.
The idle slot setup cached in the driver data survives the reset,
though, so when the card binds again and set_tdm_idle() asks for the
same mask and mode, it returns early and the force-0 enable is never
written.  set_bclk_ratio() writes the mask again but not the enable.

On the J713 every rebind of the speaker card left the two amps that
force their idle slots to zero without the enable, and the other two
amps' voltage sense words then came back with the top bit set in about
a quarter of the frames.  The speaker protection daemon reads those as a
voltage without current and holds the speakers at a safe level.

Clear the cached setup after the reset, so that the next set_tdm_idle()
writes it.

Signed-off-by: Erik Bourget <erik@ebourget.net>
The component probe requests the fault IRQ as a devres of the I2C
device, which stays bound when the sound card is torn down.  Every
rebind of the card probes the component again and adds another handler
for the same amp on the line: after two rebinds the J713's four amps
had twelve handlers on their shared IRQ, each fault interrupt ran every
one of them, and each does six I2C reads.

Free the IRQ in the component remove.  Request it as the probe's last
step, so that a probe that fails later does not leave a handler behind
either.

Signed-off-by: Erik Bourget <erik@ebourget.net>
tas2764_codec_probe() enables the SDZ supply and raises the shutdown GPIO
before it resets the amp and writes its setup. When one of those writes
fails, or an amp in an SDZ group can't get its fault IRQ, the probe
returns with the supply still enabled. ASoC doesn't call the component's
remove after a failed probe, so nothing balances that enable, and every
later probe attempt adds another. The supply then stays on after the
card is removed and, for an amp outside an SDZ group, through system
sleep as well.

Undo the power-up on those failures: lower the shutdown GPIO and drop
the supply enable, unless the group failed and its supply was already
forced off, as the remove does.

Signed-off-by: Erik Bourget <erik@ebourget.net>
The AOP sends a report every second; hold it in the mailbox across
suspend-to-idle instead of waking the system.

Signed-off-by: Erik Bourget <erik@ebourget.net>
@erikwb
erikwb force-pushed the j713/aop-mbox-sleep branch from 5d519c2 to 1bee5ac Compare November 1, 2026 23:57
@erikwb erikwb changed the title arm64: dts: apple: t8132-j713: Hold AOP messages in system sleep (depends on #434, #449) arm64: dts: apple: t8132-j713: Hold AOP messages in system sleep (depends on #820, #449) Nov 1, 2026
@erikwb erikwb changed the title arm64: dts: apple: t8132-j713: Hold AOP messages in system sleep (depends on #820, #449) arm64: dts: apple: t8132-j713: Hold AOP messages in system sleep (depends on #820, #831) Nov 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants