Skip to content

J713: shut the speaker amps down only after the bus stops (clean I/V sense at stream ends) - #663

Closed
erikwb wants to merge 16 commits into
aurora-silicon:integration/aurorafrom
erikwb:j713/tas2764-deferred-shutdown
Closed

erikwb wants to merge 16 commits into
aurora-silicon:integration/aurorafrom
erikwb:j713/tas2764-deferred-shutdown

Conversation

@erikwb

@erikwb erikwb commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

On the J713 (MacBook Air 13" M4) the speaker amps' current and voltage sense goes bad for a moment at the end of every playback stream. This makes the SN012776 amps wait for the speaker bus to stop before they go to software shutdown. It's opt-in per board through a new device tree property; only the J713 sets it.

Depends on #439 (the J713 speakers). The first commits here are #439's; the last four are new.

What happens

The AOP keeps the speaker TDM bus running for a while after a playback stream stops, and the speaker protection daemon keeps reading the amps' I/V sense words in that time. The TAS2764 driver mutes each amp at the stream's end, waits for the ramp-down, then puts it in software shutdown, one amp after another. Those shutdowns land in the bus's tail.

The right tweeter's voltage sense slot then reads full scale for about 20 samples at every stream stop. It happens for a stream of digital silence played with the speaker volumes locked, so it isn't sound. speakersafetyd and speakerguardd both charge those samples as power.

The change

  1. dt-bindings: apple,deferred-shutdown on ti,tas27xx: the amp's bus outlives its playback stream and its sense slots are still sampled, so it must not enter software shutdown until the bus has stopped.
  2. tas2764: with the property, the amp is muted at the stream's end as before and goes to software shutdown 150 ms later. On the J713 the bus ran on for up to 3520 frames (73 ms) after a stream's end.
    • A stream that starts in between cancels the pending shutdown.
    • Suspend and removal shut the amp down at once.
    • A muted amp sees the bus clock stop while it waits, and can see the clock fault latched again after its shutdown. While the amp is stopped, that clock-only fault is expected and no longer logged. A clock fault during a stream is still reported.
    • Amps without the property take exactly the same paths as before.
  3. t8132-j713: set it on the four amps.
  4. tas2764 (new): drop the clock fault latched while stopped at unmute.
    • The problem: a stream that started soon after the last one ended often logged "bad TDM clock" on the first amp the link unmutes (the left woofer). The fault wasn't the new stream's. The muted amp latches the stopped clock about every 10 ms while it waits. The four amps share one fault line, and each handler reads six latch registers over I2C, so a latch can reach its handler up to about 10 ms late. By then the unmute has already cleared the stopped state.
    • The fix: at unmute, a latch holding only the clock fault is cleared before the amp counts as running. The handler holds a lock while it reads and judges the latches, so the unmute can't fall between the two. Any other latched fault is left for the handler.

Testing (J713)

  • Sense tails: I captured the speaker sense stream directly while playing three silent streams and a chord with the volumes locked.

    • Before: about 90 full-scale words per run, all on the right tweeter's voltage slot, at every stream stop.
    • After: none.
    • No fault logged, and every amp still ends in software shutdown (PWR_CTRL 0x2).
  • Restarts: pairs of short streams 90, 120 and 150 ms apart, so the second starts while the first's shutdown is pending. All four amps play, the sense words stay valid, and there are no pops.

  • Faults at restarts (commit 4): silent streams of 0.3 s on the speakers, with gaps of 40 ms to 1 s before each start. Faults are counted from the journal.

    Starts that logged a fault
    Without commit 4 9 of 64, all on the left woofer: 9 of 32 with gaps up to 140 ms, 0 of 32 with gaps of 170 ms or more
    With commit 4 0 of 96 (and 0 of 96 again with tracing, where 11 stale latches were cleared at unmute)

    An earlier version of this description said the restart pairs logged no fault. That was wrong: they hit this race.

  • Under speakerguardd: a recording of 13 streams, including those restarts, has no full-scale sense words, and its replay certifies.

  • Checks:

Untested elsewhere. The J616s has the same AOP-fed speaker stream and may want the property too.

erikwb added 15 commits October 24, 2026 22:14
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 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>
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>
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>
@erikwb

erikwb commented Nov 1, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #820, where these four commits are 13–16, unchanged.

@erikwb erikwb closed this Nov 1, 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.

1 participant