Skip to content

ASoC: apple: J713 (M4 MacBook Air) speakers on the AOP (depends on #437) - #439

Closed
erikwb wants to merge 12 commits into
aurora-silicon:integration/aurorafrom
erikwb:j713/aop-speakers
Closed

erikwb wants to merge 12 commits into
aurora-silicon:integration/aurorafrom
erikwb:j713/aop-speakers

Conversation

@erikwb

@erikwb erikwb commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

The internal speakers of the J713 (MacBook Air 13" M4): four SN012776 amps on the AOP's speaker stream, set up like the J616s, with the same protections.

Depends on #437 (jack), which depends on #434 (AOP and mics). The first nine commits belong to those two PRs; this one adds the last three. Rebased onto the reworked #434 (bootloader AOP handoff); these three commits are unchanged.

Commits

  1. ASoC: dt-bindings: apple,macaudio: Add the J713 speakers
  2. ASoC: apple: macaudio: Add the J713 speakers
    • The macaudio_j713_cfg uses SN012776 amps, one woofer and one tweeter per side, amp gain 15 and the −20 dB safe volume, with J616s-style AOP front-ends.
    • It adds a new speaker_fe_channels field (6 for the J713). macaudio limited the speaker front-end to one channel per amp, which conflicts with the AOP DAI's six channels and makes the open fail with EINVAL.
  3. arm64: dts: apple: t8132-j713: Add the speakers
    • The four amps on i2c3 (from the ADT), each with its sense slots.
    • The shared SDZ line (AP GPIO 15) as the fail-closed SDZ group's fixed regulator, and the shared interrupt (AP GPIO 14).
    • The speaker card in the ADT provider order, with the amp-tx-zd-config idle masks.
    • The speaker and sense channels on the AOP audio node.
    • ps_i2c3 enabled.

No AOP driver change is needed: the J713 speaker stream is the J616s one.

From the ADT

J713
Stream (iop-audio-controller/audio-speaker) 12 MHz, 250-bit frame, 48 kHz
Amps (/arm-io/i2c3/audio-speaker*, provider order = playback slot) 0x38 left woofer, 0x3b right woofer, 0x39 left tweeter, 0x3c right tweeter
Sense (speaker-config) amp i: imon at RX byte 4i, vmon at 4i+2
Idle masks (amp-tx-zd-config rows 0/1) 0xf0f0 / 0x0f0f: the left woofer zeroes the right amps' sense, the right woofer the left amps'
SDZ / IRQ AP GPIO 15 (function-reset) / AP GPIO 14, level low

The ADT's speaker entry lists a 4-slot playback mask and an 8-slot sense mask. The firmware's stream is nonetheless the J616s one: 6 playback and 12 sense channels, of which the four amps use the first 4 and 8.

I first wired it as 4/8, and the two mistakes cancelled out. Playback ran 1.5× fast and smeared each channel over both amps of its side (ch 0 and ch 2 → slots {0, 2, 4}). Reading 12-channel sense as 8 slowed it by the same 1.5×, so tones still showed up at the right frequencies. Timing a 3 s playback (2.1 s) gave it away. With 6/12, it runs in real time and every check below is clean.

Testing (J713)

All of this ran with speakersafetyd 3.0.0 holding the lease. The profile is the M3 Air's j613.conf with channels = 12: same chassis and speaker set. The ADT's per-unit DC resistances (4.00/4.10 Ω woofers, 3.55/3.68 Ω tweeters) match j613's nominals. The profile will go to speakersafetyd separately.

  • Probe: all four amps bind to tas2764, and the SDZ regulator has 4 users (the fail-closed group).
  • Interlock: without speakersafetyd, the volumes stay at the locked maximum (silent). With it, Speaker volumes unlocked. Stopping it gives Speaker volumes locked: Lock timeout.
  • Silence + sense: 8 live sense channels at a few LSB around zero, frames whole, no stalls.
  • Channel → amp → sense map:
    • Method: a −30 dBFS chord on channels 0–3 (500 / 700 / 1100 / 1300 Hz), with speakersafetyd's raw sense buffer dumped and each tone located by Goertzel.
    • Result: each tone appears in exactly one amp's I/V pair: ch 0 → left woofer, ch 1 → right woofer, ch 2 → left tweeter, ch 3 → right tweeter. Sense channels 8–11 are silent, with no cross-talk.
  • Power: speakersafetyd's per-speaker power is plausible and distinct: 15.0 / 15.8 / 12.8 / 13.9 mW.
  • By ear: left woofer low, right woofer low, left tweeter high, right tweeter high, as expected.
  • No faults, no negative power, no DART or AOP errors.

Not done yet (userspace):

  • A J713 asahi-audio graph. The tweeters have no passive crossover, so the desktop must not reach the raw speakers before one exists.
  • UCM.
  • The speakersafetyd profile.

On my machine the desktop is kept off the speaker card until those exist.

Checks:

  • checkpatch --strict is clean on all three commits.
  • dt_binding_check passes for apple,macaudio.yaml.
  • dtbs_check on t8132-j713.dtb reports nothing for the amps, the card or the AOP.

erikwb added 12 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>
@RyanTheTide

RyanTheTide commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

The J713 setup preserves the four-amp shutdown group and volume lease, uses six playback slots and twelve sense channels, and keeps the first four playback slots in LW/RW/LT/RT order. The contributor's sense mapping and protected playback results are the J713 evidence.

This must not ship without speaker protection for J713, either an independently qualified speakerguardd #10 after #8 or a qualified J713 speakersafetyd profile. #10 is currently unqualified, and the speakersafetyd profile is still described as a separate submission. Pair the protection with UCM #6 and asahi-audio #10 so desktop playback goes through the crossover and the raw speaker path stays hidden. The tweeters have no passive crossover.

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.

Merge after #434 and #437, with the shared audio boot checks completed. Keep the speaker shipping hold until that userspace/protection combination is ready.

erikwb added a commit to erikwb/speakerguardd that referenced this pull request Oct 31, 2026
The J713 shares the J413's chassis, and its ADT's per-unit DC resistances
(4.00 and 4.10 ohm woofers, 3.55 and 3.68 ohm tweeters) are in line with
the J413 constants, so it is taken to have the J413's speakers. Replace
its generic draft with j413.conf on the J713's sense stream, as measured
on a J713 (speakerguardd#8, aurora-silicon#10):

- The M4 AOP's speaker stream has twelve sense channels. The four amps
  fill the first eight, I then V per amp in the J413 order, and channels
  8-11 carry nothing.
- The tweeters take their own DC resistances as z_min, below their 3.72
  ohm z_nominal and r_ff. That raises the worst-case current and power
  bounds and lowers the low-band line-fault threshold slightly (4.6% and
  1.1%), both in the protective direction.

Against the draft this takes the J413's reviewed margins for these
speakers instead of the generic fallbacks: safe_reduction 99.5 dB instead
of 20 (the J713 kernel, aurora-silicon/linux#439, holds a locked SN012776
at raw volume step 1, about -100 dB, as on the J413), t_limit 150 C with
10 K headroom instead of 120 C with 15 K, no extra regulator headroom, no
J700 power caps, and the J413's r_ff instead of the 2.7 ohm placeholder.
The amp gain stays 18.5 dBV: the J713 kernel caps the index at 15. There
is no [Playback] section, since the J713 kernel has no energy grant.

The draft's guesses did not fit the machine: eight channels, and a gain
index above the J713's cap. Run on a J713, the draft never armed. This
profile arms 42 s after a cold boot, and the onset bound then holds the
tweeters about 15 dB and the woofers about 5 dB down without a grant. A
chord and five short blips played with no hand-offs. It stays unqualified.

J713 moves from the draft admission matrix to the machine profiles'
tests: the V/I parse test (the whole profile against j413.conf apart
from the channel count and tweeter z_min), the unqualified refusal, the
recovery requirement of the hot full-scale onset test, and the grant
reserve test.

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

erikwb commented Nov 1, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #820, with the speaker commits unchanged (10–12). The speaker shipping hold you set is listed there, with the current state of speakerguardd#10 and #8, alsa-ucm-conf-asahi#6 and asahi-audio#10.

@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.

2 participants