Watch
1
0
Fork
You've already forked SouveraineOS
0
SouveraineOS/PAF/evidence/audio/HANDOFF-audio-finish-20260719-evening.md
2026-08-10 17:00:23 -04:00

12 KiB
Raw Blame History

HANDOFF — Put the Pixel 3 audio to bed (Fable, evening 2026-07-19)

Historical handoff. The current contract is ../../audio.md; this file is retained as dated evidence and may describe work that was later completed.

We are this close. Mic captures nonzero on every boot now, both loudspeakers fire, modem connects. What's left: reinstate PipeWire (the reason it was banned is gone), keep the mic/speaker gains, and make the phone call-ready (two-way call audio). This doc is your orders, not a draft to critique.


⚠️ READ THIS FIRST — the references are ALL local, and they are VAST

Casey's #1 instruction for this handoff: stop and internalize that nearly every fact you could want is already on this drive — every bring-up attempt, every Android LIVE capture, every modem trace, every regmap dump, the full Android vendor mixer map, the pmOS known-good config, the raw partition images. Even the outgoing session (me) barely knew this reference surface existed and wasted hours re-deriving things that were sitting in a file. Do not repeat that. Before you form any theory or run any experiment, grep/read the local references. If you catch yourself reverse-engineering ALSA/UCM/modem internals from scratch, you have already failed — go find the file.

The reference surface (4 repos, ~70 GB, all local)

1. ~/Projects/Pixel3Arch/PAF/ — the then-current base of truth. Doctrine + current state.

  • audio.md — the entire audio saga. THE ALTERNATION LAW, THE BOOT RACE, speaker routing provenance, mic transport. Read top-to-bottom.
  • audio-2026-07-19-LIVE-reconciliation.mdsupersedes the top of audio.md. LIVE Android register captures (recorder vs voice call), the ADM-usecase hypothesis, and the airplane/radio-mode wedge warning (some historical "wedges" were mode toggles, not driver faults — rule mode out before logging any wedge).
  • HANDOFF-mic-unpin.md — the un-pin experiment (corrected header; read audio.md "THE BOOT RACE" first).
  • modem.md, pmos_fix_plan.md, qcril_*_capture.md, modem_dt_rf_analysis_*.md, tz_listener_archaeology.md, secure_load_tz_frontier.md, qrtr_tools_reference.md, tier1_los_vs_mainline_diff.md — the modem bring-up archaeology. Deep. Detailed. Read before touching the modem.
  • evidence/, captures/ — raw traces (e.g. glink).

2. ~/Projects/PostMarketOS-Blueline/ — the pmOS port + all Android captures.

  • android-reference/the gold. mixer_paths_tavil_b1.xml (the FULL Android vendor route map — every named route: handset, speaker, voice-handset, voice-speaker, all mics), CS35L36 regmaps (idle/playing), tavil PGD dumps, wcd9xxx_core-android-LIVE-*.txt (recording AND voicecall, captured 2026-07-19), wcd934x-android-ground-truth.md.
  • android-capture-2026* — nine per-day Android trace dirs.
  • PAF/ — a second full modem-trace tree (android_modem_bringup_capture.md, qcril_full_provisioning.md, qcril_online_sequence.md, modem_consumer_chain_*.md, los-mss-translated.c).
  • HANDOFF.md (80 KB), PROGRESS.md, START-HERE.md, README.md.

3. ~/Projects/pixel3-downstream-audio/ — the Android HAL source. techpack/audio/ — downstream q6adm.c, q6voice*.c, ASoC machine driver. This is what the working Android side actually runs; diff mainline behavior against it when a route/topology question comes up.

4. ~/pixel3-backup/ (in $HOME, not Projects) — raw partition dumps + pmOS config. modem_a.img, persist.img, vendor_a.img, system_a.img (raw Android), plus pmos-20260703/the known-good pmOS config backup the speaker routing was lifted from (see the memory note: this is the source of truth for working modem/cellular + speaker config; restore from here, never guess).

If a route value, a mixer control, a modem sequence, or a UCM verb is in question — it is decoded in one of the above. Cite file:line. Do not invent.


CURRENT STATE (verified live this session, phone at WiFi 10.10.20.234, USB 172.16.42.1)

Kernel: 7.1.1-sdm845-gcd7758eaeca1 (mic-race-fix, HEAD). Boot-race fix LIVE and working — MICDBG boot sysclk hold armed fires in dmesg, capture open joins a live codec (users=2 skip) instead of racing bring-up.

Mic — WORKING. parecord --device=blueline_mic yields nonzero PCM every boot. Deterministic now (kernel sysclk hold from wcd934x_comp_probe).

Speakers — WORKING. Both CS35L36 amps armed, correct route (measured live): Left/Right AMP Enable = on, BOOST = on, PCM Volume = 700, Left Channel Mux = RX1 (top), Right Channel Mux = RX2 (bottom). Matches the 07-12/07-13 known-good. The handset (earpiece) vs speaker distinction is a single control: Right AMP Enable — Android's handset route runs the top amp only (R AMP Enable=0, low gain); speaker runs both. Decoded from mixer_paths_tavil_b1.xml.

Modem — connects. Registered (Fido, MNC 720). Call signaling works. Note: saw a transient roaming reject (MNC 490, roaming-not-allowed) mid-session — that's network, not us, and consistent with the reconciliation doc's mode/radio caveat. Rule mode out before chasing modem "faults."

Calls — NO AUDIO yet. q6voiced is now enabled + running holding hw:0,2 (the live VoiceMMode1 PCM — NOT device 5 as the staged conf wrongly said; fixed this session in both /etc/q6voiced.conf on-device and pkgs/q6voiced/q6voiced.conf). But it logs Failed to open tx/rx: Invalid argument on call-start because nothing applies the VoiceCall route. See below.


THE TWO HOLDS ON THE MIC (know the difference)

  1. PA loopback pin (90-blueline-alsa.pa, modules 20/21): null-sink + module-loopback source=blueline_mic ... source_dont_move sink_dont_move. Holds the mic open from boot. Nobody else does this — verified via GitHub code search: no working sdm845 phone (OnePlus 6 enchilada/fajita, holioka) pins the mic. It's a boot-race workaround.
  2. Kernel sysclk hold (today's fix, in wcd934x_comp_probe): permanent sysclk ref + MICB2 bias from probe. Honest hack — pins the winning-boot state so every boot wins.

With the kernel hold in place, the PA pin is very likely now redundant. The un-pin test was never run on a race-won boot. Running it de-hacks the daily driver. See HANDOFF-mic-unpin.md for the exact procedure (unload loopback, same-map reopen ×2, watch for CHAN_CTRL remove ret=0 + fresh DEF_ACT_CHAN). If reopen is nonzero → delete the PA pin, keep the kernel hold. If zero → keep both, root-cause stays open.


PIPEWIRE — the ban is lifted; bring it back

Why it was removed (da53133): the only documented reason was the premature capture source — wireplumber published the mic before ALSA capture was nonzero (the boot race). PAF audio.md:516/590. PipeWire playback was never the problem — 8920c3c had PW playing on glass fine once the sink was pinned S16LE + RW + no-mmap (the DSP lies about S24/mmap; both play silent).

Why it's safe now: the boot race is fixed in-kernel — capture is nonzero from boot, deterministically. The precondition PipeWire failed on is satisfied. Casey has explicitly cleared reinstating it.

PipeWire task

  • Reinstall the full PipeWire + WirePlumber stack. Note the current shims: pkgs/pipewire-abi-provider, pkgs/pulse-native-provider (they provide the PW ABI without a server — you're replacing that arrangement with a real server, so reconcile/remove the shims cleanly, don't leave a half state).
  • Re-apply the S16LE playback rule (the wireplumber 51-blueline-audio-s16.conf that 8920c3c created and da53133 deleted — recover it from git: git show 8920c3c). Without it, playback is silent.
  • Verify on the fixed kernel, in this order, and DO NOT proceed past a failure:
    1. mic still nonzero via PW (pw-record) — this is the thing PW broke before;
    2. speakers play via PW (pw-play a chime) — both top+bottom;
    3. the mic source is NOT published dead at boot (the old failure mode).
  • Keep the new features intact: the QS audio service fix (Audio.qmlblueline_mic description matcher), the mic/speaker gains, the speaker Channel Mux routing.

Ponytail guard: PipeWire is the bigger hammer. Its payoff is that callaudiod can then route calls via UCM — which is the whole reason we want it back. If PW re-breaks the mic despite the race fix, fall back to the raw-PA call hook (below) rather than fighting it — the mic is non-negotiable.


MAKE IT CALL-READY (the actual finish line)

The working sdm845 reference (OnePlus 6, chayleaf/dotfiles system/hardware/oneplus-enchilada/default.nix, and syntheit/nix hosts/fajita) routes call audio through UCM (consumed by PipeWire) + callaudiod + q6voiced tied to ModemManager. Key facts pulled from it:

  • q6voiced hw:0,6 there (per-board PCM number; ours is hw:0,2 — always read /proc/asound/pcm, never copy the number).
  • q6voiced is After=/wantedBy=ModemManager.service.
  • alsa-ucm-conf is shipped and alsa-ucm-meld = trueUCM is the routing layer. callaudiod flips the verb on call state; q6voiced carries the audio.

The call chain we need

gnome-calls → callaudiod → UCM VoiceCall verb → q6voiced carries modem audio

Both middle links are currently broken/absent on our raw-PA setup:

  • callaudiod is installed and running but has zero log activity — it routes via PipeWire ports/profiles, which don't exist under raw-PA. PipeWire back gives callaudiod something to route with. This is the clean path and the reason PipeWire is worth the risk.
  • UCM VoiceCall verb exists (Google/blueline/VoiceCall.conf) but has the same hw:0,5 bug (PlaybackPCM/CapturePCM "hw:${CardId},5") — fix to ,2. Its EnableSequence is the real voice route: SLIMBUS_0_RX Voice Mixer VoiceMMode1=1, VoiceMMode1 Capture Mixer SLIMBUS_0_TX=1. Cross-check the RX/TX and DMIC/DEC selections against mixer_paths_tavil_b1.xml voice-handset/voice-speaker and the downstream q6voice*.c before trusting them.
  • UCM currently fails to load via alsaucm -c G3 (-2). Under raw-PA this is moot (PA doesn't consume UCM). Once PipeWire+wireplumber drive UCM this must actually resolve — verify wireplumber opens the card's UCM cleanly. (The card driver/id: id=G3, longname Google Pixel 3, ASoC machine msm-snd-sdm845; conf.d lives in conf.d/sdm845/. If UCM won't match, this driver-name-vs-dir mapping is the first suspect — but let a real UCM client, not the bare CLI, be your test.)

Definition of done — ALL MET 2026-07-29

  1. PipeWire back, mic + both speakers verified on the fixed kernel. Done 2026-07-19.
  2. Place a real call (Casey → his wife). Two-way audio. Downlink out the speaker, uplink from the mic, on the call. Done 2026-07-29 — incoming call answered, both directions confirmed by ear at both ends. Five defects had to fall first; see PAF/audio.md, 2026-07-29.
  3. On hangup, audio reverts to normal HiFi (no stuck voice route). Done — and note the reverse was silently broken: the mic read exact zeros after every call until the outgoing capture leg was unmapped explicitly.
  4. q6voiced device number correct (hw:0,2) in every place it's written. Done.
  5. New features + gains preserved. Mic still nonzero every boot. Done — ~90k nonzero frames verified repeatedly across the session.

Not in the original list, completed 2026-08-02: the earpiece/speaker executor (blueline-callaudio-hook speaker on|off) is driven by the patched souveraine-callaudiod; see ../../audio.md and TASK-39.

Fallback (if PipeWire re-breaks the mic)

Raw-PA call hook: a small ModemManager call-state trigger that applies the VoiceCall mixer controls directly via amixer/alsaucm cset on call-start and reverts on hangup. ~15 lines. No server swap, zero risk to the working mic. Smaller diff, fewer niceties. Only take this if PipeWire loses the mic.


RULES OF ENGAGEMENT

  • Base of truth = PAF. Every reference is local (the 4 repos above). Read before theorizing. Cite file:line or say "not in the code."
  • Rule out airplane/radio mode before logging any modem/audio "wedge."
  • The mic is the crown jewel. Never trade it away for a nicety.
  • Verify claims live on the phone; don't assert kernel/route state from memory.
  • The doctrine holds: build the finished thing, not a v1. This is the last mile.