Watch
1
0
Fork
You've already forked SouveraineOS
0
SouveraineOS/saf/device/audio.md
Fimeg bde961c6f2 saf: one spine — device, state, and work under the index
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
2026-08-18 09:47:30 -04:00

836 lines
51 KiB
Markdown

# Audio — WCD9340 / SLIMbus
## Current — 2026-08-07
This is a working phone audio stack, not an active microphone bring-up.
- **Runtime:** PipeWire + WirePlumber + pipewire-pulse + pipewire-alsa. Native
PulseAudio and the ABI-provider shims are historical experiments, not the
running authority.
- **HiFi:** both CS35L36 speakers play; the WCD9340 microphone captures clean
mono audio through DMIC4 → DEC5/TX5. Casey uses it regularly for STT.
- **Calls:** the `Voice Call` UCM profile, `souveraine-callaudio`,
`souveraine-q6voiced`, and `souveraine-callaudiod` carry real two-way calls
and the earpiece/speaker path.
- **Persistence:** WirePlumber restores volumes and the selected profile. Do
not add another volume writer; role policy remains TASK-39.
- **One hardware debt remains:** the kernel permanently holds codec sysclk and
MICB2 from probe. Audio works, but the hold costs idle power and blocks a
clean suspend story. TASK-07 owns removing it.
The July **“UN-PIN TEST PASSED”** below removed the userspace always-open mic
pin. It did **not** remove kernel commit `cd7758eae`'s sysclk + MICB2 hold; that
similar noun is the likely source of the remembered “we resolved it.”
The implementation lives in Pixel3Arch under `pkgs/souveraine-ucm-blueline`,
`pkgs/souveraine-wireplumber-blueline` (which packages the canonical
WirePlumber policy from `rootfs-overlay/`), `pkgs/blueline-callaudio` (package
name `souveraine-callaudio`), `pkgs/souveraine-callaudiod`, and
`pkgs/q6voiced` (package name `souveraine-q6voiced`).
Everything below is the evidence ledger. Headings such as “still open” describe
the date beneath them unless the current block above repeats the defect.
## UCM PROFILE GONE AT BOOT — 2026-08-16, fixed same day
Boot-time music died after kernel `7.1.1.r162.g457a667bd3eb` (installed
2026-08-13, first boot 08-16 00:46): the card came up with only `off` and
`pro-audio`, no UCM profiles, so no HiFi sink existed at boot. In-session
audio (calls, STT mic) kept working — the regression only hit the fresh-boot
state, which is why it surfaced as “music at boot”.
Measured chain, all on-device:
- **Kernel made routed capture strictly mono 48k.** The wcd934x
recorder-state work in r162 (`b30e849be` “program all DMIC interface pads”,
`9610ef9cf` “replay complete blueline recorder state”) rejects stereo
capture hw_params on the routed MultiMedia2 (`CHANNELS: [1 4]` advertised,
but `arecord -c 2` → -EINVAL, `-c 1` → fine).
- **ACP probes with declared UCM values.** The Mic devices declared no
`CaptureChannels`/`CaptureRate`, so ACP assumed stereo at the FE-default
rate → capture probe failed -EINVAL → every profile “not supported” → ACP
fell back to `pro-audio` (priority 1) at boot.
- **Secondary lock, not the cause:** `90-blueline.conf` sets
`session.suspend-timeout-seconds = 0` on `~alsa_output.*`, so the pro-audio
sink never released hw:0,0; later probes died “Device or resource busy”.
The persisted `default-profile` state (wireplumber saves the selected
profile, by design) then re-pinned `pro-audio` each boot.
- **Fix:** declared `CaptureChannels 1` + `CaptureRate 48000` in both Mic
devices (HiFi + VoiceCall). ACP probe now succeeds; all three UCM profiles
form; card activates HiFi at boot. Tone verified audible on glass; mic
capture nonzero through the UCM source. Shipped as
`souveraine-ucm-blueline 0.1.0-2` (commit `9f0f103`, CI run 1696);
rootfs-overlay UCM copies kept identical to the package.
- **Not shipped, known hardening:** scope the never-suspend output rule to
`~alsa_output.platform-sound.*__sink` (the UCM sinks) so a stray pro-audio
fallback can never hold hw:0,0 forever.
Probe mechanics learned (canonical, 2026-08-16):
- `spa-acp-tool -v -v hw:0` is the ACP probe; “Probing profile X” with no
failure lines after it = supported. The trailing “unknown command hw:0” is
a harmless arg-parsing quirk.
- `alsaucm -c hw:0 set _verb HiFi` is the real-card UCM branch; it runs the
verb EnableSequence (and enables devices — TX5 mapping included). `-c G3`
and `-c "Google Pixel 3"` hit the *no-hw* branch (conf.virt.d / legacy
paths) and fail ENOENT even when UCM is healthy — red herrings.
- `alsaucm list profiles` / `get _verbs` fail with ENOENT quirks on this
box; they are not valid probes.
- `cat /proc/asound/card0/pcm*p/sub0/status` shows `owner_pid` — the way to
find who holds a PCM.
- Raw `arecord -D hw:0,1` only works while the verb routes are applied;
`spa-acp-tool` runs DisableSequence on exit and tears them. Re-apply the
verb before raw capture tests.
## PIPEWIRE RESTORED — 2026-07-19 late evening
The PA-native arrangement is retired. Live state, all verified on-device:
- **Stack:** pipewire 1.6.8 + wireplumber 0.5.15 + pipewire-pulse +
pipewire-alsa. The shims (`pipewire-abi-provider`, `pulse-native-provider`)
and the patched `pulseaudio-q6` are removed (rollback pkg kept at
`~/build/pulseaudio-q6-r3/`; `/etc/pulse` backed up as
`/etc/pulse.bak-20260719`). The `enable-pipewire` gate drop-ins were
deleted — PipeWire is unconditional now.
- **UCM discovery works under ACP/wireplumber.** HiFi verb → Speaker sink +
Mic source appear named from the UCM comments. The old `alsaucm -c G3`
`-2` failure is an id-vs-longname CLI quirk; `-c "Google Pixel 3"` parses
both verbs fine. Real clients are unaffected.
- **Playback:** `51-blueline-audio-s16.conf` (S16LE + disable-mmap +
generous periods, recovered verbatim from `8920c3c`) reinstated; chime
confirmed audible on glass.
- **Capture:** mic source pinned mono S16LE 48k, suspend disabled,
period 4800 / headroom 9600 (`90-blueline.conf`). Nonzero on every test
capture.
- **UN-PIN TEST PASSED.** With the kernel sysclk hold (`cd7758eae`) and no
loopback hold, two consecutive same-map reopens both captured nonzero
(451k/470k nonzero samples). The userspace mic pin is DELETED — kernel
hold alone is sufficient. Cold-boot confirmation pending.
- **New kernel bug found and fixed:** any xrun → userspace
`snd_pcm_prepare()` re-enters wcd934x prepare without shutdown →
`slim_stream_prepare` -EINVAL "Stream already Prepared" retried ~50/s
forever (PipeWire recover loop starved the whole graph). Fix
`f56b1ae08` on `mic-race-fix`: per-dai `prepared` flag, re-prepare
no-ops, stream stays armed (alternation-law friendly). Wireplumber
headroom masks the common case until the kernel is installed.
- **VoiceCall.conf `hw:0,5``hw:0,2`** fixed on-device + repo.
- **Voice-call discovery:** VoiceMMode1 (`hw:0,2`) open fails -EINVAL until
the voice BE routes (`SLIMBUS_0_RX Voice Mixer VoiceMMode1`,
`VoiceMMode1 Capture Mixer SLIMBUS_0_TX`) are applied — DPCM
no-backend rejection. With routes applied the PCM opens. This is why
q6voiced logged "Failed to open tx/rx: Invalid argument". ~~Because the
PCM is unprobeable, ACP can never synthesize a VoiceCall profile, so
callaudiod's profile-switching path is structurally dead on this card.~~
**Wrong — see 2026-07-29.** ACP never probes `hw:0,2`; it probes the PCMs
the verb *declares*, and the profile was lost for two reasons of our own
making. Shipped `blueline-callaudio-hook` (system service): MM `CallAdded`
→ UCM voice verb, last call terminating → HiFi verb restore. q6voiced then
opens an already-routed PCM.
## PHONE CALLS WORK — TWO-WAY AUDIO ON A REAL BEARER — 2026-07-29
Incoming call, answered, audio both directions, confirmed by ear at both ends.
Five defects had to fall, every one of them ours:
- **Verb named `VoiceCall`.** alsa-lib's `SND_USE_CASE_VERB_VOICECALL` is
`"Voice Call"`, with the space, and callaudiod accepts only a profile
carrying that prefix (`cad-pulse.c:508`). Renamed.
- **The voice verb gave MultiMedia1 no backend.** HiFi routes it through
`QUAT_TDM_RX_0`; the voice verb routed nothing, so opening `hw:0,0` under
the verb returned -EINVAL and ACP dropped the profile — *"Profile 'Voice
Call' mapping 'Voice Call: Speaker: sink': output PCM open failed"*,
`spa-acp-tool -v -v`. That, not an unprobeable `hw:0,2`, is why no voice
profile ever appeared. The card carries one now.
- **Downlink went to `SLIMBUS_0_RX`.** blueline's earpiece and loudspeaker are
both CS35L36 amps on `QUAT_TDM_RX_0`; the WCD9340 is the uplink and the
headset jack. Voice sent to the codec reached nothing — audible as a call
the far end heard perfectly while this end heard silence.
- **A front-end takes exactly one backend.** These mixers are *not* additive:
enabling a second silently clears the first, measured in both directions.
Setting `QUAT` and `SLIMBUS` together is not belt-and-braces, it is a no-op.
- **q6voiced latches a failed open.** tinyalsa's `pcm_open()` returns a
non-NULL handle on failure and `q6voiced_open()` guards on `if (v->tx)`, so
one lost race silences the whole call. Reachable on any outgoing call:
`mm_state_is_active()` counts DIALING, emitted in the same instant as
`CallAdded`. Incoming calls are safe only by accident — RINGING_IN is not
counted active, so their open lands well after the routes. Patched in
`souveraine-q6voiced` to release and retry.
Two further findings:
- **The mic died after every call**, not just occasionally. `alsaucm` holds its
UCM context in-process, so a one-shot `set _verb HiFi` has no previous verb
to dismantle and never runs its `DisableSequence`. TX5 and TX7 both stayed
mapped into `AIF1_CAP`, SLIM_0_TX carried two channels into a mono front-end,
and capture read exact zeros. The hook now unmaps the outgoing leg explicitly.
- **callaudiod cannot own earpiece/speaker on this card.** It switches a sink
*port* (`cad-pulse.c:961`) and needs both port types on one sink, but ACP
models UCM devices as one profile per combination — `Voice Call (Earpiece,
Mic)` and `Voice Call (Mic, Speaker)`. `ConflictingDevices` does not change
it. enchilada sidesteps this by giving each transducer its own PCM; blueline
has both amps behind one. The toggle is therefore
`blueline-callaudio-hook speaker on|off`, writing the amps directly.
## ROOT CAUSE FOUND AND FIXED — MICS CAPTURE REAL AUDIO — 2026-07-19
**The wall was the codec master clock.** Mainline DT fed the WCD9340's `extclk`
from `RPMH_LN_BB_CLK2` (inherited from the reference-board dtsi). On blueline,
Android's own DT gives **LN_BB_CLK2 to the Pixel Visual Core (`mnh-sm`)** and
feeds the codec from **`clock_rpmh` index 4 = LN_BB_CLK3** (verified by
decompiling the phone's `dtbo_b`; `audio_ext_clk_lnbb``<&clock_rpmh 0x04>`).
The RPMh vote on the wrong buffer succeeds, so nothing errors — the codec just
never receives 19.2 MHz. That single fault explained the entire residue:
- eFuse sensing timed out every boot (`-110`, `status=0x0`) — needs MCLK, no
ports involved. After the fix: `eFuse sense complete: status=0x01 out14=0xda
out15=0x01` — first time ever on mainline.
- Analog AND digital capture read zeros (whole audio core unclocked) while
every SLIM control write ACKed (SLIMbus clock is separate).
- IRQ 213 (codec SLIM data line) never fired in the machine's life; after the
fix it fires on every capture.
- MBHC read garbage impedance at probe.
Fix: `&wcd9340 { clocks = <&rpmhcc RPMH_LN_BB_CLK3>; }` in
`sdm845-google-common.dtsi` (committed in the kernel tree). Deployed initially
as a verified one-byte DTB patch (`/boot/dtbs/sdm845-google-blueline.dtb`,
backup `.lnbb2-orig` beside it; kexec chain reads the DTB from `/boot`, NOT
from the boot_a image — see below).
**Live capture results after the fix (kernel `g8cbfbb939327`, same boot):**
- AMIC1 → ADC1 → DEC0 → TX0: 121,724 / 144,000 nonzero samples
- DMIC0 → DEC7 → TX7 (`hw:0,1`): 94,198 nonzero
- DMIC4 → DEC5 → TX5 (Android triple-mic route): 81,545 nonzero
- DMIC3 → DEC6 → TX6 (Android triple-mic route): 92,216 nonzero
- DMIC4 → DEC7 → TX7 (Android handset-mic route): 124,050 nonzero
All three physical mics (DMIC0/3/4 per Android's `triple-mic` path) and the
analog path produce real ambient audio through plain ALSA `hw:0,1`
**no HAL, no ADM usecase, no ACDB OEM tree in the loop**. This falsifies the
"ADM usecase routing is required for samples to flow" hypothesis from the
2026-07-19 live-reconciliation doc, and retroactively confirms every "codec
registers are bit-perfect but silent" audit: the registers were right; the
silicon was unclocked.
### Boot chain discovery (why kernel deploys "never landed")
`boot_a` holds pocketboot (LinuxBoot, a 7.2.0-rc1 first-stage kernel). It
kexecs `/boot/vmlinuz-blueline` + `/boot/dtbs/sdm845-google-blueline.dtb` per
`/boot/extlinux/extlinux.conf` on Arch_root (`linux,booted-from-kexec` in the
live chosen node). Flashing boot_a via fastboot does NOT change the running
kernel/DTB — install to `/boot` on the rootfs instead.
### Then-open capture intermittency — superseded later on 2026-07-19
- Ordering effect observed on the first post-fix boot: DMIC3/4 through
MUX6/MUX7 read zero until the DEC5/DEC6 (Android-native) routes ran once;
after that, the previously-dead MUX7+DMIC4 handset route worked. Some
first-use state sticks. Full codec+IFD regmap snapshots of the known-good
state saved on-phone (`/tmp/codec-good-state.txt`, `/tmp/ifd-good-state.txt`
— copy off before they evaporate; /tmp is tmpfs).
- The last test of the session (speaker-tone loopback) read all-zero again
right after a string of successes — cause unestablished. Per the
live-reconciliation doc's warning, record airplane/radio/mode state before
interpreting ANY zero capture from now on.
- Android runtime parity notes for the diff: Android recording state has
`0xd25 = 0x11` (ADC MUX3/7 mode reg) where ours reads 0x00 even while
working; pads `0x8015-0x8018 = 0x00`, `0x8019/1a = 0x0a` — identical to what
our kernel programs (my in-session debugfs pad pokes don't persist).
- Regmap-cache caveat from the reconciliation doc applies to OUR debugfs reads
of IFD/PGD registers too: prefer live-read paths when auditing those.
### THE ALTERNATION LAW — exact zombie mechanism (2026-07-19, evening)
Every capture result from all three boots today reduces to ONE deterministic
rule: **a SLIM TX capture produces samples only when its channel map differs
from the previous capture session's map. Re-opening with the identical map
always reads zeros.** Verified live: TX5→TX5 = work,zero; TX5→TX6→TX5 =
work,work,work; boot-first TX7 always zero (ch135 is pre-touched by ADSP
boot/cal state, so the first TX7 session counts as a same-map reopen).
Everything earlier read as input-specific (DMIC3/4 dead), power-related
(PA holding sysclk), or "priming" was test-order artifact of this law.
- Codec side is fully correct per-session (DEF_ACT_CHAN wbuf identical
between dead and working sessions except TID; teardown calls
slim_stream_unprepare which sends DEACTIVATE/REMOVE/RECONFIGURE per
drivers/slimbus/stream.c).
- Clock gates during a zombie capture are bit-identical to Android's working
recording (0x711=0x91 0xd41=0x01 0xd42=0x0d).
- Conclusion: the ADSP's SLIM-master/AFE side treats an identical re-DEFINE
as a no-op and never re-arms the data path. Android never hits this because
its HAL/ADM re-arms per session (the reconciliation doc's ADM hypothesis,
refined: not "ADM required for routing" but "ADM masks the stale-restart").
- NEXT INSTRUMENTATION: log outgoing MCs in qcom_slim_ngd xfer path and
diff same-map vs changed-map session teardown/setup on the wire; then
candidate kernel fix = force a map perturbation or explicit port
disconnect+reconnect on every SLIM capture start.
- Userspace mitigation shipped meanwhile (see 90-blueline-alsa.pa): a
null-sink loopback holds blueline_mic open from PA start — the boot-time
open is on virgin TX5 (works), and a held-open source never re-opens, so
the law is never violated. VALIDATED on true cold boot 2026-07-19 12:09:
blueline_mic RUNNING at boot, 262k/263k nonzero samples first client,
two back-to-back client sessions both live (peaks 5256/5590/26044).
Costs continuous capture power (remove once the kernel fix lands).
### THE BOOT RACE — pinned boot capture is a coin flip; kernel + config were red herrings (2026-07-19 evening)
Journal survey of ALL 9 boots of 2026-07-19 (`journalctl -b -8 .. -b 0`,
MICDBG sysclk lines at the pin's boot-time capture open):
| boots | kernel | at pin open | MICB2 | outcome |
|---|---|---|---|---|
| -7 | g323b297 | `users=2 skip` (joined a live hold) | 0x50 | 262k WORKING |
| -3 | gbdf098937 | `users=2 skip` | 0x50 | never tested (!) |
| -8 | g323b297 | `users=1 full bring-up` after `users=0 HW POWER-OFF` | 0x10 | zero |
| -6 -5 -4, 0 | gbdf098937 | same losing pattern | 0x10 | zero |
| -2 -1 | gfdb22e5 | same losing pattern | 0x10 | zero |
An early boot-time actor grabs codec sysclk ~3s after boot, holds ~3s, and has
MICB2's 0x40 bit up while it holds. If PA's pin lands INSIDE that window the
capture joins as user 2 and samples flow; if it lands after the release (codec
did a HW POWER-OFF), capture does its own full bring-up and reads zero on every
route for the rest of the boot — the same persistent zombie the PA-stop
experiment produced. Same kernel both wins and loses (g323b297: won -7, lost
-8; gbdf098937: won -3, lost four). Therefore:
- **QRTR=y (`fdb22e5`/`f245323`) is exonerated.** Every "dead on fdb22e5"
observation was a lost race, sampled. Config stays QRTR=m / PD_MAPPER=m
(Kconfig forces PD_MAPPER ≤ QRTR anyway); it is orthogonal to the mic.
- **The CHAN_CTRL-remove commit (`bdf098937`) is exonerated for "dead at
boot"** — its code path is open-inert (only adds `disable_stream`, verified
by diff) and it won the race on boot -3.
- **The un-pin question is STILL unanswered.** It needs a race-winning boot:
pinned baseline nonzero, `pactl unload-module` the loopback, then same-map
reopen; nonzero reopen = pin can be deleted.
- The early holder is unidentified; MBHC headset-detect is the prime suspect
(boot-time codec user, raises MICB2 for impedance sensing). MICB2
0x50-vs-0x10 tracks outcome 8/8 but is NOT yet proven causal — it may just
fingerprint the hold. Verify before building on it: read Android's ANA_MICB2
during a working slot-B recording, and/or set the 0x40 bit via debugfs during
a fresh lost-race boot's capture.
- Kernel fix direction (pick after causality check): wire the DMIC supply
(micbias) into the capture DAPM path, or hold sysclk from probe until first
capture so the boot window cannot close early.
### Afternoon findings — zombie state + wedge reproduction (2026-07-19)
- Cold-boot experiment (radios all on, recorded): first `arecord` after boot
opened ONLY the FE — zero kernel-side codec/BE activity, zeros. Second and
third captures (any route) worked. The morning "DEC5 unlocks MUX7" story is
partly this: two distinct zero-classes exist — (a) BE-never-opened,
(b) BE-open-but-silent.
- Stopping PulseAudio (releasing the card → last sysclk user drops →
`wcd934x_disable_ana_bias_and_syclk` powers the codec down) put capture into
a persistent all-zero state on EVERY route, and restarting PA did not
recover it. Mixer state is bit-identical PA-up vs PA-down (diff empty), so
it is a power-lifecycle effect, not mixer state.
- In the zombie state, live volatile reads DURING capture show the analog
domain healthy: `ANA_BIAS=0x80 BUCK=0x07 MICB4=0x90`. The bias/buck theory
is dead. Codec-internal clock gates (0xd41/0xd42) were not yet live-readable
— instrumented build `323b29715` makes them volatile and logs
enable/disable/sido lifecycle + per-capture clock-gate reads.
- Manually zeroing `CLK_SYS_MCLK_PRG` (0x711) via debugfs wedged the SLIM bus
into the historical `-110` NACK loop (`Failed to read IRQ status: -110`,
`Error Interrupt 0x82000000`, 1 Hz). One historical "SLIM wedge" class is
therefore reproducible as pure codec-clock-state loss — consistent with the
2026-07-19 warning that not every wedge was a bus/driver fault. Phone needs
a cold boot after this (self-inflicted, cause exactly known).
- Upstream comparison: `enable_ana_bias_and_sysclk`/`disable` and the
sido-cache skip are upstream-inherited structure, not our divergence; our
only body change is the Android-parity buck sequence (captures work with
it). No structural smoking gun found by code audit — hence instrumentation.
## SLIM NACK loop confirmed boot-invariant on a real cold boot — 2026-07-15
- Fixed three misidentified raw-hex register writes in
`wcd934x_apply_blueline_android_tx7_capture_setup()` (kernel `5199244ee0ee`):
`0x0b07-0x0b3f` were RX playback compander `CTL6` registers (not TX
per-path enables), `0x0c96` is `WCD934X_SWR_AHB_BRIDGE_ACCESS_STATUS` (a
status register, not a TX enable — writing it was the suspected cause of a
SLIM wedge seen on the prior build `9610ef9cf45a`), and `0x0d1e`/`0x0d25`
are `ADC_MUX0_CFG1`/`ADC_MUX3_CFG1` (TX0/TX3, not TX7/TX8 as the old
comment claimed). Verified against downstream
`techpack/include/asoc/wcd934x_registers.h`. This fix did NOT resolve the
SLIM wedge — see below.
- The recurring error is a genuine **NACK**, not a bus hang:
`qcom,slim-ngd-ctrl: Error Interrupt received 0x82000000` decodes to
`NGD_INT_TX_MSG_SENT (BIT 31) | NGD_INT_TX_NACKED_2 (BIT 25)` — the
message transmitted and the target actively refused it. The target is
`LA:0xcf` (the WCD934x codec's assigned logical address, confirmed not
`SLIM_LA_MGR`/`0xff`), message code `0x60` =
`SLIM_MSG_MC_REQUEST_VALUE`. The read is `regmap-irq`'s generic
`regmap_irq_thread()` polling the codec's IRQ status registers
(`drivers/base/regmap/regmap-irq.c`) — the codec's IRQ line is
`IRQF_TRIGGER_HIGH` (level), so a failed status read never clears it and
it refires every ~1s, forever, from ~1-2min post-boot onward.
- Tested with a **user-operated true power-off (`shutdown -h now`, not
`fastboot reboot`) followed by manual power-on**, specifically to rule
out the in-place-ADSP-restart degradation `b0b813f` warned about. Result:
the NACK loop reproduces identically on this real cold boot (confirmed
climbing at 110s uptime, same `MC:0x60,LA:0xcf` signature). **This is not
a warm-reboot or ADSP-restart artifact** — it is boot-invariant, matching
the entry below from earlier the same day.
- `blueline-hexagonrpcd-adsp.service` (`b0b813f`, still uncommitted to
gitea as of this entry) attaches cleanly on this same cold boot — no
fluence/ECNS/AVS module errors, only the unrelated `lowi_lp.conf` miss.
So the ADSP audio-PD HexagonFS gap is fixed and not the current blocker;
capture still fails at the ALSA `hw_params` layer (`arecord` "Unable to
install hw params") before the ADSP module question is even reachable,
because the SLIM NACK loop blocks it upstream.
- **Current standing blocker, plainly:** the SLIM NACK loop on `LA:0xcf`.
Not yet root-caused. Two real, separate bugs were found and fixed
tonight (the register-write bug above, and the ADSP HexagonFS gap in
`b0b813f`); this is a third, still-open one.
## Cold-boot SLIM-NGD correction — 2026-07-14
- A user-operated full power-off while unplugged, followed by power-on, still
logs `qcom,slim-ngd-ctrl ... QMI wait timeout` at 12:29:52, immediately
after ADSP boot/WCD934x probe, followed by repeated `SLIM SAT: Rcvd master
capability`. This happened before any ALSA/Pulse microphone client opened.
- Therefore the timeout/capability loop is **not merely a warm-reboot or
long-uptime artifact**. ADSP, CDSP, SLPI, and MSS all reach `running`, so
the fault is specifically in the ADSP↔NGD SLIM initialization exchange and
is now the primary boot-invariant microphone lead. Do not restart ADSP or
NGD while investigating; preserve this state for tracing.
- The adjacent WCD MBHC threshold/impedance messages concern headset/accessory
detection (including the available USB-to-mic adapter), not the target
internal DMIC4 → DEC7 → TX7 capture path.
## Q6 VoiceMMode1 call path — staged 2026-07-14
- This is deliberately separate from the still-silent raw `MultiMedia1/2`
capture path. Android proves the board microphone works; normal PCM remains
an ADM/calibration problem, while modem calls use the hostless Q6Voice path.
- The missing board wiring was real: `sdm845.dtsi` left Q6 MVM/CVS/CVP
disabled and blueline had no `VoiceMMode1` DAI/card link. Kernel commit
`551d3bb1b` enables those services and instantiates the link on the tested
Type-C baseline. Its matching module tree is installed on the phone at
`7.1.1-sdm845-g551d3bb1bd35`; its boot image is staged locally.
- The preserved prior device package, found at
`archive/pmos-20260709/pmaports/device/testing/device-google-blueline/`,
is the authoritative board data: `VoiceMMode1` enumerates as **`hw:0,5`**,
not the raw MultiMedia devices. Its exact `VoiceCall.conf` routing
(`SLIMBUS_0_RX Voice Mixer VoiceMMode1`, `VoiceMMode1 Capture Mixer
SLIMBUS_0_TX`, DMIC4→DEC7→TX7) is installed in the Arch UCM overlay.
- `q6voiced-blueline` was built and installed natively, with that exact PCM-5
configuration. Its system unit is deliberately **disabled until the new
kernel cold-boot confirms `00-05: VoiceMMode1`**; enabling it on the old
kernel would create meaningless restart noise. First test must be a clean
cold boot and an actual ModemManager call *before any HiFi/raw-capture
experiment*, because the historical second call failure was SLIMBUS RX
already claimed by earlier HiFi activity.
## Live state — 2026-07-12 evening (mic)
**Live Android Recorder ground truth — 2026-07-14:** On the rooted Lineage
slot-B reference system (`4.9.337-gb929452c5c49`), an ordinary
`org.lineageos.recorder` recording was observed live, not inferred from XML.
It opens **MultiMedia1 / PCM device 0** (not MultiMedia2): its userspace input
is mono S16_LE at 8 kHz, while the active backend is
`SLIMBUS_0_TX`, mono S16_LE at 48 kHz. The downstream HAL enables
`DMIC4 -> DEC7 -> TX7`; `SLIM_0_TX Channels=One`; and its active capture
app-type control is `[69938, 4, 48000, 3]`: downstream's documented field
order makes that `app_type=69938` (the Qualcomm default TX-path app type),
`acdb_dev_id=4`, `sample_rate=48000`, `backend_id=3`. The capture track was active and
non-silent in AudioFlinger. Crucially, Android's dedicated WCD SLIM-interface
debug dump during that recording read `0x032=0x80`, `0x057=0x05`,
`0x097=0x21`, and `0x11c=0x80`. (`tavil-slim-pgd` regmap is codec register
space, not this interface-device space.) Every earlier Arch microphone
negative test targeted `hw:0,1` / MultiMedia2 at 48 kHz. The exact
**MultiMedia1 `hw:0,0` 8-kHz FE / 48-kHz SLIMBUS backend** retest then ran on
Arch with TX7/DMIC4 and the active speaker RX route preserved: its 8-second
raw payload was 128,000 bytes / **zero non-zero samples**. (An earlier
apparent 19-sample result was immediately identified as `arecord`'s 44-byte
WAV header and corrected with `-t raw`.) This closes the FE/rate mismatch as a
microphone fix. The Android HAL's ADM identity and ACDB path is now the
leading differential: it programs this FE's app type, topology, and device
calibration, whereas mainline q6routing passes zeros.
**DEF_ACT_CHAN client-field test — 2026-07-13:** The Android downstream NGD
driver encodes the first `DEF_ACT_CHAN` payload byte as
`(data_fmt << 5) | (laddr & 0x1f)`; mainline had sent the full WCD9340 logical
address (`0xcf`) and consequently put its high bits into the data-format field.
The one-line correction was built on the current Type-C/G56 test base as
`3ceab71eded0`, its matching module tree was installed before flashing, and
the resulting `7.1.1-sdm845-g3ceab71eded0` boot was verified on glass. A
controlled single-direction `DMIC4 → DEC7 → TX7 → MultiMedia2` 48 kHz mono
capture (TX6 disabled) produced `384000` bytes / `192000` samples, with **0
non-zero bytes** and signed-16 range `0..0`. ALSA state restored (only the
known read-only HPH controls complained); no new SLIM/Q6/NGD timeout or audio
error appeared. This fix is a valid protocol correction and must remain, but
it is **not sufficient to make blueline microphone capture work**.
**Android DMIC-pad + analog split test — 2026-07-13:** Android's WCD9340
initial register table writes `DMIC3_CLK_PINCFG`/`DMIC3_DATA_PINCFG`
(`0x8019`/`0x801a`) to `0x0a`; mainline defined but never programmed those
registers. That exact pair was added to WCD934x component initialization,
built as `959f0d5e6`, and booted as
`7.1.1-sdm845-g959f0d5e65fc`. The normal Android handset route
`DMIC4 -> DEC7 -> TX7 -> SLIMBUS_0_TX -> MultiMedia2` remained exactly zero
(384000 bytes / 192000 signed-16 samples / zero non-zero bytes), without a
capture-time SLIM, Q6, or ADSP error. A separate reversible replay of
Android's analog fallback `AMIC1 -> ADC1 -> DEC6 -> TX6` also produced the
same exact zero stream. Thus neither the DMIC3 pad configuration nor a
bottom-DMIC/flex-only fault can explain the failure: the common remaining
boundary is codec TX channel activation and/or the ADM/ADSP capture path.
The only ALSA restore errors were the known read-only HPH impedance/type
controls.
**ADM/calibration lead — 2026-07-13 (refined 2026-07-14):** Mainline `q6routing_stream_open()`
unconditionally passes `NULL_COPP_TOPOLOGY` and app/acdb values of zero to
`q6adm_open()`. Pixel Android's `msm-pcm-routing-v2.c` starts with that
fallback but looks up topology, app type, and ACDB device ID by FE/BE/session
from audio-calibration tables before opening ADM. Do not replace the topology
with a guessed constant: the live Android `MultiMedia1 + SLIMBUS_0_TX`
recorder has now identified `app_type=69938`, `acdb_dev_id=4`,
`sample_rate=48000` (and backend ID 3). The exact ADM topology was decoded
from the extracted board-matched ACDB on 2026-07-14; see the next entry.
**ADM identity-only test — 2026-07-14 (negative):** Kernel
`7.1.1-sdm845-g3b9cf346af3a` passed Android's observed `app_type=69938` and
`acdb_dev_id=4` only for live `SLIMBUS_0_TX` capture, leaving the topology
`NULL` rather than inventing one. The matching modules were installed before
the pmOS-chain `boot_a` image was flashed; the running kernel and modules
matched. A controlled `MultiMedia1` / `hw:0,0`, mono S16_LE 8 kHz raw capture
with the Android-equivalent `DMIC4 -> DEC7 -> TX7` route yielded 128,000 bytes
with **zero non-zero samples**. ALSA state was restored (only the known
read-only HPH controls refused writes). This eliminates the two ADM identity
numbers alone as a microphone fix. The remaining Android differential is the
calibration-selected ADM topology and/or downstream codec-side programming.
**Board ACDB topology decoded — 2026-07-14:** The local Lineage vendor blob
`blobs/los-vendor/etc/acdbdata/OEM/sdm845-tavil-b1-snd-card/Handset_cal.acdb`
is not merely a future reference: `acdb-ldp` decodes its `HANDSET_MIC` device
4 configuration map as `69938 -> 0x00010315` (at 48 kHz). Downstream's
`apr_audio-v2.h` names `0x00010315` as
`ADM_CMD_COPP_OPENOPOLOGY_ID_MIC_MONO_AUDIO_COPP`; it is the specific Android
mono-microphone COPP, rather than the mainline null passthrough
`0x00010312` or a guessed default. This explains why the identity-only test
was necessarily incomplete. The present 7.1.1 tree also contains q6core's
optional ACDB topology-registration code, but blueline's q6core DT node has
no `qcom,acdb-name` and no `Global_cal.acdb` is installed as firmware, so that
path is currently dormant.
**ADM topology + AudProc test — 2026-07-14 (negative):** The current kernel
opens `SLIMBUS_0_TX` capture with topology `0x10315`, app type 69938, and ACDB
device 4, then parses and sends the board ACDB's complete 524-byte AudProc
table. Live dmesg confirms `SLIMBUS_0_TX applied ACDB AudProc calibration:
device=4 rate=48000 app=69938 (524 bytes)`, but capture remains exactly zero.
This eliminates the selected COPP topology plus AudProc table as a complete
fix; Android also sends the separate AudVol table.
**AFE calibration delivery audit — 2026-07-14:** All three local Pixel
Handset ACDB variants agree. Device 4 has no `AFECLUT0` common-calibration
row, and its `AFE_CMN_TOPID_V2` device property is `0x000112fb`, Qualcomm's
`AFE_TX_TOPOLOGY_ID_NONE`. The only device-4 AFE datum is a 48-kHz AV-sync
delay of 578, which cannot account for an all-zero stream. Qualcomm's SDM670
ACDB loader passes the same device ID 4 to AFE (there is no hidden remap to
the unrelated AFE-common IDs 100/128/130/149), tries topology, common table,
then hardware delay. Therefore adding an AFE common/topology sender here
would deliver no microphone payload. Do not revisit AFE delivery without new
Android evidence.
**ADM AudVol + explicit SLIM-error test — 2026-07-14 (negative):** Device 4,
app type 69938, volume index 0 decodes to a 40-byte table: soft-volume
stepping parameters and mono gain `0x2000` (unity). Kernel
`7.1.1-sdm845-gfb8f067e12f2` parses `AVOLLUT0`/`AVOLCDFT`/`AVOLCDOT` with the
same validated generic table builder and sends AudProc then AudVol, matching
Android's order. The same build propagates previously ignored failures from
SLIM port connection and WCD934x stream prepare/enable. A controlled 5-second
`DMIC4 -> DEC7 -> TX7 -> MultiMedia2` mono 48-kHz capture completed normally
and produced 480,000 bytes / 240,000 samples, all exactly zero. Live dmesg
confirms `SLIMBUS_0_TX applied ACDB AudProc/AudVol calibration: device=4
rate=48000 app=69938 (524/40 bytes)` and contains no capture-time SLIM
connect/prepare/enable, WCD934x, Q6, permission, or missing-file error.
Therefore neither the missing AudVol table nor a silently discarded SLIM
setup failure explains the zero stream.
Do not stream the ADSP-owned `217:250:0:0` debugfs regmap `registers` file
during capture: a filtered read blocked after the capture had completed and
had to be killed. This is consistent with the earlier observation that IFD
`0x097` is controlled by the ADSP SLIM master; it is not a safe codec-style
bulk register snapshot path and yielded no new state evidence.
**True cold-boot re-validation — 2026-07-13:** Kernel
`7.1.1-sdm845-g44d43e6ef336` includes the WCD934x `.prepare`/`.shutdown`
stream-lifecycle experiment. On a physical shutdown/power-on with native WCN
boot healthy, the controlled DMIC4 → DEC7 → TX7 → MultiMedia2 raw capture was
again exactly zero (`192000` bytes, `0` non-zero bytes). The temporary
`mic-ngd` trace proves the entire kernel-visible sequence ran successfully:
`q6afe_port_start ret=0`, `slim_stream_enable` entered,
`qcom_slim_ngd_enable_stream` entered and returned `0`. There was no SLIM/Q6
timeout. ALSA was restored immediately; the three HPH-impedance restore errors
are documented read-only controls. **This falsifies the prepare-order patch as
a microphone fix and makes silent ADSP-side channel activation (not userspace,
Q6 return handling, or NGD error propagation) the current boundary.**
**Stable-SLPI follow-up capture — 2026-07-13:** After the repaired sensor
registry cleared CHRE's five-minute watchdog and the SSC desktop sensor clients
were exercised successfully, the same safe single-direction TX7/DMIC4 route
was captured as 48 kHz mono raw audio for five seconds. It produced 480,000
bytes / 240,000 samples, all exactly zero. ALSA state was restored by trap;
SLPI, hexagonrpcd, and iio-sensor-proxy stayed active, with no `MC:0x2c`,
`-110`, or CHRE fatal. The sensor repair therefore improves platform stability
but does **not** open the microphone path.
**NGD/update audit — 2026-07-13:** The running 7.1.1 source already contains
the complete upstream May-2026 NGD probe/SSR race-fix series that landed for
7.2-rc: deferred IRQ enable, controller-owned workqueue initialization, safe
SSR/PDR cleanup, and the `tx_lock` ABBA deadlock fix. It therefore must not be
re-applied as a speculative microphone patch. This boot still logged one
`QMI wait timeout`; that is the driver's one-second wait for the remote QMI
service, not an `EPERM` or file-access failure. It remains a boot-stability
lead, but the subsequent raw capture's core `CONNECT_SOURCE` transaction
returned `0` and the audio data was still all zero.
- The on-device session's mic doc (collected to
`docs/tasks/mic-slim-tx-20260712.md`, source
phone `~/projects/unfinished/pixel3-microphone/`) found every capture
start at ~23h uptime logging 3x `TX timed out:MC:0x2c` /
`failed:-110` — the SLIMbus `CONNECT_SRC` user message to the ADSP
master never answered — and called that the localized root cause.
- **Re-test after a WARM reboot (same day, laptop-driven): the 0x2c
timeout did not reproduce — but the boot was NOT clean.** That boot was
produced by `sudo reboot` (warm), and its dmesg shows `QMI wait timeout`
at 8.1s plus the repeating "Rcvd master capability" retry loop — the
documented degraded slim-ngd state that warm reboots carry over (same
TZ-poison family as the WiFi MSA issue; only an UNPLUGGED cold power-off
clears it). Within that caveat: a `hw:0,1` capture with the doc's exact
TX7/DMIC4 arming logged 37 slim transfers — all MC `0x60`/`0x68` value
messages, zero errors, zero timeouts — and was still bit-for-bit zero,
with IFD `0097/0320/0330/0970` all `00` mid-capture. **Both datapoints
(0x2c timeout at 23h uptime; silent no-activation here) need
re-validation on a true cold boot before either is trusted.**
- No root-cause conclusion is valid yet. The `-110` may be a degraded-NGD
artifact of long uptime / remoteproc restarts, while the silent all-zero
capture may be a separate normal-state failure; the only attempted contrast
was tainted by a warm reboot. Kernel-visible Q6 and NGD success plus Android's
`IFD 0x097=21` versus mainline `00` remain observations, **not a proven
boot-invariant activation failure**.
- Note: `0320/0330` read `00` here vs `c0/01` in the 07-11 canonical 2ch
trace — arming differed (single TX7 vs TX7+TX8); observation only.
- What the phone doc contributes regardless: OnePlus 6 (enchilada) mic
works on pmOS 6.x kernels with this same driver stack (6.x->7.x
regression candidate in slimbus/ngd/wcd934x, or blueline-specific ADSP
behavior); pmOS's blueline UCM ships with the whole mic section
commented out (nobody has solved this on mainline); operational
hazards — never restart the adsp remoteproc while debugging slim (NGD
stays degraded until reboot, reproducible), never leave MultiMedia2
with both `SLIMBUS_0_RX` and `SLIMBUS_0_TX` routes enabled (q6adm/q6asm
wedge); QUAT_TDM_TX_0 in DT would give a WCD-free CS35L36 VMON/IMON
capture reference.
- Refined next steps: trace the `qcom_slim_ngd_enable_stream` user-message
exchange on a fresh boot (is CONNECT_SRC sent and ACKed, or skipped?);
diff `drivers/slimbus/{qcom-ngd-ctrl.c,stream.c,messaging.c}` +
`wcd934x.c` against pmOS's 6.x sdm845 tree; if an enchilada is handy,
boot this 7.1.1 kernel on it to split regression vs blueline-specific;
ask #sdm845-mainline about silent no-activation with working value
messages.
### Upstream/device-branch archaeology — 2026-07-12 (no phone test)
- The current `sdm845/7.1-dev` upstream line is active (its tip is two months
newer than our 7.1.1 base) but its audio delta is the CS35L36 speaker DTS
support already mined for the working speaker route; it contains no WCD934x
microphone/SLIM-TX fix. Do not hold a cold-boot test for an imagined pending
7.1 microphone update.
- **Real candidate to test after a true cold baseline:** historical
`joel/op6-audio-fixes-wip` commit `1d5776ca` changes WCD934x SLIM lifecycle
from `.trigger` (enable on PCM START) to `.prepare` plus `.shutdown` (enable
before START, disable on close). Our current `wcd934x.c` still has the old
`.trigger` implementation. This is not upstream and not proof of a fix, but
it moves the `slim_stream_enable()` / CONNECT_SRC exchange out of PCM START
into the backend prepare phase. Static inspection confirms the Q6 SLIM DAI
starts its AFE port in `.prepare`, while DPCM prepares BEs before the FE;
that makes this a concrete stream-ordering hypothesis rather than a generic
codec tweak. First make an instrumentation-only patch or trace plan that
establishes the exact per-DAI callback order; only then consider a
rollback-approved kernel test.
- Related branch commits were inspected and are **not** default candidates for
blueline DMIC capture: `75ad5bb0` adds separate AIF2/AIF3 SLIM DAI links for
OnePlus use cases (our AIF1 `SLIMBUS_0_TX` link already matches its AIF1
portion); `4a05bee2` and beryllium `07078b5c` repair MBHC/MIC_BIAS2 headset
handling, not the DMIC4/MIC_BIAS4 path. Keep them as topology references,
not a grab-bag patch series.
- **Candidate build/deploy and cold-boot result (2026-07-13):** the lifecycle
change was ported exactly onto the running Type-C-bisect base
(`56b0a053b`) as `44d43e6ef` / Gitea branch
`mic-slim-prepare-order-g56`, built on ArchDev, module tree installed, and
boot image flashed to slot A. Casey then performed an unplugged shutdown and
power-on; on `7.1.1-sdm845-g44d43e6ef336`, the controlled
`DMIC4 → DEC7 → TX7 → MultiMedia2` `hw:0,1` capture still contained only
zero samples. No `MC:0x2c` / `-110` occurred during that capture. Thus the
prepare-order hypothesis is a valid deployed negative result, not an
untested lead; retain the kernel as the current microphone-test baseline.
## Live state — 2026-07-12 (speakers)
- BOTH speakers now fire: the earlier "speaker playback works" state was in
fact top-speaker-only — both CS35L36 amps had `Channel Mux = RX1`, so the
bottom loudspeaker never received a channel. Fixed with the pmOS backup
image's routing (Left=RX1 top, Right=RX2 bottom, plus LDM select / PDM
ramps / zero-cross / per-amp ASPTX sources) in the overlay UCM
(commit 2f39c06), installed on-device and persisted via `alsactl store`.
Verified by ear: distinct left/right/stereo tones.
- Phone-side handoff doc `~/projects/unfinished/pixel3-speaker-routing/`
(README + baseline state + test tones) documents the diagnosis trail;
its "next step" (pmOS MRs online) was superseded by reading the local
pmos-20260703 rootfs image directly (debugfs, no mount needed).
- **Regression and guardrail — 2026-07-13:** the live TDM split was still
correct (`Left Channel Mux=RX1`, `Right Channel Mux=RX2`), but both amp and
boost switches were off. Re-enabled all four and stored the corrected state
on the phone. `docs/tasks/asound-baseline-20260712.state` is a *pre-fix
diagnostic snapshot* (`Right Channel Mux=RX1`); it must never be passed to
`alsactl restore`, because doing so regresses the bottom speaker. Use the
deployed UCM and current `/var/lib/alsa/asound.state` as the restore source.
## Prior live state — 2026-07-11
- Speaker playback works end-to-end through PipeWire, UCM, QUAT TDM and the
CS35L36 amplifiers (top speaker only — see above).
- `arecord -D hw:0,1 -f S16_LE -r 48000 -c 2` (the canonical MultiMedia2
front-end, whose `SLIMBUS_0_TX` link is enabled) opens and runs, but capture is
bit-for-bit zero. This is a hardware/ASoC transport investigation, not a
PipeWire source-selection failure; no PipeWire source can exist until ALSA
produces samples.
- This work did not start `iio-sensor-proxy`. There is no evidence that the
sensor-client HACK or SLPI wake-lockup mechanism gates this audio path.
## Verified zero-capture path — historical 2026-07-11
The controlled route is Android's documented handset/endfire pair:
- DMIC4 -> DEC7 -> SLIM TX7 and DMIC3 -> DEC8 -> SLIM TX8.
- Q6 function tracing during `arecord` observed, in order:
`q6slim_set_channel_map`, `wcd934x_hw_params`,
`q6afe_slim_port_prepare`, `q6afe_port_start`, and `wcd934x_trigger`.
- A temporary argument probe observed the two expected shared-channel IDs
delivered to Q6 when TX6 is off: `tx_num=2`, `0x87`, `0x88` (135, 136).
The resulting 478,078 signed-16 samples on `hw:0,1` were all zero.
- The WCD9340 interface device (`217:250:0:0`, `WCD9340-IFC-DEV`) receives
the expected driver writes for both ports: TX7 map/config `0x11c=80`,
`0x11d=01`, `0x57=05`; TX8 `0x120=80`, `0x121=01`, `0x58=05`.
Its live interrupt enables are `0x32=c0` and `0x33=01`, so their absence is
**not** the explanation for silence.
- On that same canonical front-end, `q6afe_port_start()` returns `0`, then
`slim_stream_enable()` invokes `qcom_slim_ngd_enable_stream()`, which returns
`0` after the controller's `DEF_ACT_CHAN` and `RECONFIG_NOW` operations.
Kernel-visible Q6 and NGD schedule activation therefore succeeded.
- **Corrected DAPM observation:** a clean live `hw:0,1` capture powers
codec-side `AIF1 CAP` and `AIF1_CAP Mixer`. Its prepare phase calls
`wcd934x_codec_enable_slim` and two `wcd934x_codec_enable_dec` events.
The earlier report that these callbacks never fired was false: the trace
missed the prepare-phase calls. Do not use that earlier result as evidence.
- The *persistent* mixer state leaves `CDC_IF TX7 MUX` and `CDC_IF TX8 MUX`
at `ZERO`, despite the corresponding AIF mixer switches and DMIC muxes
being selected. Consequently the TX7/TX8 branch is off during an ordinary
capture and no `wcd934x_codec_enable_dmic` event occurs. This is a real UCM
route omission, not an ADSP conclusion.
- A controlled, automatically reverted test set those muxes to `DEC7` and
`DEC8` and disabled stray TX6. It powered the entire intended branch and
invoked `wcd934x_codec_enable_dmic` twice (with matching shutdown calls).
The independent two-second PCM capture was nevertheless 384,000 zero bytes.
Thus the codec DAPM path and physical DMIC endpoints are viable once the
muxes are selected, but that route omission is not the final silence cause.
- This is **not** a missing FE-to-BE connection: runtime ASoC state reports
`MultiMedia2 Capture: start` with `SLIM Capture 1: start` as its active
backend, and the codec's AIF/decimator event callbacks run. With the CDC
muxes selected, the DMIC callbacks run too; the unresolved point at that
date was after the fully powered codec capture path.
- Reducing the route to the downstream-style single TX7/DMIC4 channel did not
change the result: a four-second `hw:0,1` mono capture contained 192,000
zero samples. Channel count/configuration is therefore not the activation
trigger.
## What the 2026-07-11 evidence did and did not say
Android's captured recording state has IFD `0x097=21`; mainline reads `00`.
During a five-second live TX7/TX8 capture, writing `0097 21` through the
debugfs regmap returned success but read back `0097: 00`, and all 478,078
samples remained zero. This confirms that a userspace register replay cannot
enable that state on this path. It does **not** establish what `0x097` means
or prove that it is the root cause. Do not turn it into an unconditional
codec write or call it an “ADSP-master fix” without an implementation-level
source or Android-side transition capture.
TX6 is enabled by the current mixer default alongside TX7/TX8. It makes the
unconstrained map three channels (`0x86`, `0x87`, `0x88`), while disabling it
produces the correct TX7/TX8 pair above. That is a real UCM/machine-routing
cleanup item, but it did not restore capture and is not the root cause.
## Proposed next work on 2026-07-11 — superseded
1. Capture the working Android interface-device transition, or find the
downstream owner/meaning of IFD `0x097`; compare the actual write sequence,
not just idle/recording register snapshots.
2. Add the TX7/TX8 `CDC_IF` mux selections to the eventual microphone UCM
enable sequence (and keep TX6 off for this two-channel route). Do not
expose a broken PipeWire source before its capture is non-zero.
3. Repeat the WCD9340 interface/ADSP comparison only with that fully powered
codec path. The still-zero full-path test strengthens, but does not prove,
the relevance of the non-sticking Android IFD `0x097=21` delta. Capture an
Android transition or find the downstream owner before making a firmware
or register claim. Do not flash a kernel test without an explicit
rollback-approved deployment plan.
4. Only after that, compare the remaining post-activation state with Android
at the firmware boundary. Q6 and NGD success alone do not show that the
codec/ADSP hardware emits decimator samples.
5. Fix the stray TX6 default separately in the persistent UCM/machine route,
then verify it with the channel-map probe. Do not claim mic support until a
live capture contains non-zero samples.
## Instrumentation
`/usr/local/sbin/blueline-lockup-trace` is a root helper deployed from
`rootfs-overlay/`. `mic-start` traces the Q6/WCD stream functions and the
three WCD codec DAPM event callbacks; `mic-map-start`, `mic-ifc-start`, and
`mic-ngd-start` collect Q6 channel-map, WCD interface-register-write, and
Q6/NGD return traces respectively. Trace sessions are temporary and must
always be paired with the matching `*-stop` command; the current phone was
checked clean afterwards (`tracing_on=0`, tracer `nop`, no kprobes).
## 2026-07-18 full Android power-state reconciliation
A clean, single-channel PulseAudio/UCM capture on kernel `4c3a94153` proved
that the intended TX7/DMIC4 path is selected and fully ACKed, but every one of
55,680 S16 samples is exactly zero. A simultaneous live codec dump exposed
two remaining Android/mainline power-state differences which earlier tests had
not actually eliminated:
- `ANA_MICB4` is `0x50` on mainline versus `0x90` during Android recording.
The DT opt-in `qcom,blueline-dmic-micbias-pullup` is on the `codec@1,0`
node, but `wcd934x_codec_parse_data()` queried `dev->parent->of_node` (the
SLIM controller). Therefore `micbias_pullup` was always false and the
earlier claim that pull-up mode had been tested is invalid. The code now
queries `dev->of_node`, selecting Qualcomm's downstream
`MICB_PULLUP_ENABLE` path (`0x90`).
- `ANA_BUCK_CTL` is `0x00` on mainline versus `0x07` on Android. Qualcomm's
resource manager sets RCO followed by buck `PRE_EN1`, `PRE_EN2`, and
`HIGH_ACCURACY_EN`, with 100 us between stages. The mainline driver had
dropped this sequence. It is restored as ordered masked writes, including
the reverse transition; this is not the harmful unsequenced Android
end-state replay removed earlier.
The same correction (`a75371b49`) also matches Android's board state by
programming only DMIC interface 3 pads (the real DMIC3/4 inputs) and avoids
leaving TX7 at the transient 150 Hz HPF setting (`CFG0=0xd0`) indefinitely;
Android's steady recording state is `CFG0=0x90`. One boot-time log reports
whether pull-up mode was parsed, and the capture log reports live BUCK and all
MICB registers so the next capture validates the complete state in one pass.
## 2026-07-18 eFuse initialization regression and cache fix
On the clean `3b85e688f` boot, the complete downstream AFE codec-control map
registered successfully and TX7 again reached `port_start`, CONNECT_SOURCE,
DEF_ACT_CHAN, and RECONFIG_NOW with positive ACKs. PulseAudio captured 760,318
raw bytes (380,159 S16 samples), all exactly zero. The journal exposed a real
codec boot failure: `wcd934x_enable_efuse_sensing` read `EFUSE_STATUS=0` and
reported that sensing never completed.
History showed that `1c095f7a7` had added the downstream-required
internal-SIDO start before eFuse sensing, but `c1b6371c6` removed the call while
dropping unrelated experiments. The exact downstream SIDO/buck transition
helper was later restored by `a75371b49` without reconnecting it to eFuse init.
The audit also found that downstream treats `EFUSE_CTL`, all eFuse value
outputs, and status as volatile, while mainline cached them in `REGCACHE_MAPLE`.
Commit `82355eac0` batches the complete correction: internal SIDO before the
sense enable, the required 5 ms delay before RCO/BG, downstream-parity volatile
register handling for the eFuse/clock/buck sequence, live completion polling,
register error propagation, and explicit status/out14/out15 logging. Do not
interpret another zero capture until a cold boot proves the codec prints
`eFuse sense complete`; otherwise the physical codec trim/version state is
still uninitialized even though SLIM management commands ACK normally.