PAF becomes saf/device (history kept), STATE.md dissolves into saf/state.md with the dated era archived, the substrate SAF moves up from souveraine, and every agreement points at saf/INDEX.md and nowhere else. one map, nothing to remember
225 lines
12 KiB
Markdown
225 lines
12 KiB
Markdown
# 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.md` — **supersedes 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.qml` — `blueline_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 = true` — **UCM 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.
|