Watch
1
0
Fork
You've already forked SouveraineOS
0

tasks: mic is functional-pinned (spec'd: sample-dup + unpin test); add 15 idle coordinator

07 rewritten around the working mic-race-fix kernel (f56b1ae08 running):
canonical-tree truth, the sample-duplication defect with measured
evidence, and the cold-boot unpin protocol. 15 is the freeze/doze/
RTC-wakeup design carved from today's idle-burn numbers.
This commit is contained in:
Fimeg 2026-07-21 09:19:50 -04:00
commit 039d73eb3b
3 changed files with 181 additions and 68 deletions

View file

@ -1,70 +1,106 @@
# TASK 07 — WCD9340 mic: SLIM TX channel/port contract
# TASK 07 — WCD9340 mic: functional-pinned; unpin + fidelity remain
**Status:** diagnosed 2026-07-17. Frontier — no mainline SDM845 phone has
mic working. This is the most important open thread.
**Status:** FUNCTIONAL 2026-07-20, re-verified 2026-07-21 (3/3 captures,
real audio, zero SLIM errors). Two defects remain: a sample-duplication
bug that halves audio bandwidth, and the power pin that makes it work.
The 2026-07-17 "diagnosed" text below the fold is history — kept because
the trace method and dead-end ledger stop future sessions from
re-deriving them.
## Goal
## Where the working code actually lives (fixed 2026-07-21)
Microphone input on blueline (WCD9340 codec over SLIMbus). Audio OUTPUT
already works (speaker path is DSP→MI2S→Cirrus cs35l36 amps, never
touches the WCD codec). Mic is the only WCD/SLIMbus customer.
The phone runs `7.1.1-sdm845-gf56b1ae0831c` — that is commit
`f56b1ae08` ("make prepare idempotent across PCM re-prepare",
2026-07-20), tip of branch **`mic-race-fix`**.
## The diagnosis (2026-07-17 regmap trace)
- **Canonical tree: `~/Projects/Pixel3Arch/kernel/linux-blueline-current`**
(worktree on `mic-race-fix`, now synced to f56b1ae08).
- Build tree on archdev: `~/build/pixel3arch-kernel/src/linux-blueline-src`,
branch `makepkg` — this is what actually built the running kernel.
- Stale claimants, do not trust their STATUS.md: `linux-blueline-work`
(detached at 07-18), `linux-blueline-mic-order-g56` (broken worktree,
parent .git moved), and the dirty 07-03 checkout at
`PostMarketOS-Blueline/references/linux-7.1.1-sdm845-CANONICAL`.
**The prior diagnosis was wrong.** Commit `5199244ee` dropped the
`0x0c96` (SWR_AHB_BRIDGE_ACCESS_STATUS) write, blaming it for the SLIM
-110 wedge. A trace taken *with that fix applied* still wedges
identically. 0x0c96 was innocent.
The mic stack (07-14 → 07-20), newest first: prepare idempotency ·
sysclk+MICB2 pin at probe (cd7758eae) · CHAN_CTRL remove on stream
disable · **LN_BB_CLK3 extclk (the MCLK fix — codec was on the wrong
PMIC clock buffer, root of the original eFuse/-110 era)** · RCO SIDO
buck · DMIC DAPM micbias · decimator clk at PRE_PMU · PGA unmute ·
ch pairing + AFE CDC_DEV_CFG.
**The real trigger:** the codec accepts every TX-path setup write
cleanly (microsecond latencies). The bus dies on the first *read* after
arming TX7/TX8:
Audio server truth: the phone runs **PipeWire 1.6.8** (pipewire-pulse;
pulseaudio.service inactive — the old "pulseaudio MASKED" note is
history). UCM exposes `alsa_input.platform-sound.HiFi__Mic__source`,
s16le 1ch 48 kHz. Capture test:
`pw-record --target <mic-source-id> /tmp/x.wav`.
```
2552.829597 reg_write 0x800 val=6 # last healthy write
2552.829598 hw_read_start 0x801 # polls TX port status
2553.875314 hw_read_done 0x801 (1.046s) # first -110 timeout
→ forever: NGD 0x82000000 = TX_NACKED_2
```
## Defect 1 — sample duplication (the "bit buggy")
Every write succeeds. No single write is poisonous. The wedge is the
codec's TX port failing to complete a SLIMbus TX transaction once armed
— a **channel/port mapping contract** problem between NGD and the codec,
not a bad register write. Last writes before death were the TX7/TX8
path-config burst (`CDC_TX7_TX_PATH_CFG0` 0xaa2, `CDC_TX7_TX_VOL_CTL`
0xaa4, `CDC_TX8_TX_PATH_CTL/CFG0/VOL_CTL` 0xab1/0xab2/0xab4). Full
trace + register map: `~/Projects/Pixel3Arch/kernel/linux-blueline-mic-order/MIC-WEDGE-TRACE-20260717.md`.
Every captured sample arrives exactly twice: raw stream begins
`-1224, -1224, -190, -190, 1044, 1044, …` (measured 2026-07-21; RMS
7179, rail fraction 0.03% — dynamics healthy otherwise). The ALSA layer
believes 1ch 48 kHz S16 (`/proc/asound/card0/pcm1c` during capture), so
the doubling happens **below the kernel interface**: the TX7/TX8 SLIM
channel pair both carry the same 24 kHz decimator output and AFE
interleaves the pair into the "48 kHz" stream. Net effect: 12 kHz real
bandwidth — voice sounds muffled/underwater.
## Next step
Fix directions, in order of likelihood:
1. Map mono 48 kHz capture to **one** SLIM channel (downstream 4.9 uses
a single channel for SLIMBUS_0_TX mono; the pair is for stereo).
2. If the pair stays, feed it as a true even/odd sample split, not two
taps of one decimator.
Stop hunting "the misidentified register." Compare our SLIM TX
channel/port setup against **db845c**'s working mainline SLIM capture
path, register-for-register. db845c is the same codec + bus and is the
only known-working reference. The fault is in the NGD→codec TX channel
contract, likely in port enable / channel-map setup in
`sound/soc/codecs/wcd934x.c` or the q6 SLIM DAI config.
Acceptance: no exact duplicate pairs in raw capture; spectrum extends
past 12 kHz (whistle test).
## Context
## Defect 2 — the power pin (the "unpin test")
- Kernel tree: `~/Projects/Pixel3Arch/kernel/linux-blueline-mic-order/`
(CANONICAL per its STATUS.md). Tip `5199244ee`. Build with
`pmbootstrap build --src=<repo>/references/linux-7.1.1-sdm845-CANONICAL
linux-postmarketos-qcom-sdm845 --force`.
- The TX7 setup fn: `wcd934x_apply_blueline_android_tx7_capture_setup()`
in `sound/soc/codecs/wcd934x.c` (~line 1880).
- pulseaudio is MASKED on the phone (symlinks to /dev/null). It must stay
masked while debugging capture — unmask only once mic is understood.
- Repro: cold boot, `arecord -D hw:0,0 -f S16_LE -r 48000 -c 1 -d 2`.
Wedges on stream start. Only cold boot clears (unbind deadlocks D-state).
- Instrumentation: `~/mic-hunt.sh` on the phone (regmap ftrace + slimbus
dynamic debug + arecord, snapshots to `~/mic-hunt-<ts>/`).
`cd7758eae` holds a permanent codec sysclk reference + MICB2 pull-up
from component probe. Why: capture only produces samples when it opens
against an already-powered codec. A capture whose own bring-up starts
from codec power-off reads zeros on every route until the next cold
boot. Working boots had merely won a race with MBHC's ~3 s probe-time
sysclk hold; losing boots lost it by ~1 s.
## Acceptance
Cost: sysclk + SIDO buck + MICB2 pull-up burn from boot, forever —
this is standing idle drain and pocket heat, and it blocks any real
suspend story (TASK-08/15).
`arecord -D hw:0,0 -d 2` captures non-zero samples without wedging the
bus. No -110 storm in dmesg.
The unpin test (cold boots — Casey at the device):
1. Build `mic-race-fix` with cd7758eae reverted plus a candidate
ordering fix (start from what MBHC's probe path powers that the
capture path doesn't: RCO/SIDO buck sequencing — see 3ac06ca49 —
and clock-gate readbacks from 323b29715's instrumentation).
2. Cold boot. **Wait >3 min before first capture** so the MBHC hold has
expired and the race can't be accidentally won.
3. `pw-record` → non-zero samples? Repeat across 3 cold boots.
4. Zeros → the ordering fix is wrong; pin goes back; iterate. The pin
is the fallback, never the end state.
## Acceptance (task-level)
Unpinned kernel: first capture after a >3 min-idle cold boot returns
real samples, three cold boots in a row. No duplicate-pair signature.
No -110/NACK storm in dmesg across repeated open/close.
## History (2026-07-17, superseded)
The bus-wedge diagnosis that got us here: every TX-path setup write
succeeded; the bus died on the first *read* after arming TX7/TX8
(`0x801` poll → 1.046 s timeout → NGD `TX_NACKED_2` forever). Full
trace: `linux-blueline-mic-order/MIC-WEDGE-TRACE-20260717.md`. Dead
ends already falsified — do not re-derive: 0x0c96 was innocent; codec
regs match Android ground truth bit-for-bit; q6afe cdc_codec_config
already fires (ret=0); AFE_SLIMBUS_SLAVE_PORT_CONFIG is SLIMBUS_5_TX
only; ADSP file-serving complete (only adsp_avs_config.acdb requested);
adsp.mbn is stock Google-signed Android-11 era; slot B is wiped, no LOS
image on-device to diff. Post-mortem of the whole hunt:
`linux-blueline-current/2026-07-19-FABLEWORK.txt`.
## Connects to
handoff doc "Microphone bringup leads" (mailing lists show WIP from
others), the 2025-26 upstream fix wave already in our base.
TASK-15 idle coordinator (the pin is its enemy), TASK-08 device state
manager, upstream: LN_BB_CLK3 + CHAN_CTRL-remove + the eventual unpin
ordering fix are mainline candidates once stable.

View file

@ -0,0 +1,76 @@
# TASK 15 — Idle coordinator: freeze, doze, and scheduled wakeups
**Status:** open — design task. Carved 2026-07-21 from Casey's ask
("app freeze state for idle, the timeoffs and wakeups to check for
notifications") plus the measured idle burn below.
## Why (measured 2026-07-21, phone idle, screen locked)
- `culver` 9.8% CPU, 6:11 accumulated — the single biggest idle spinner
- `qs` (shell) 4.5%, `souveraine-player` 3.9%
- cluster1 peaked 54.9 °C; Casey feels it in-pocket
- The mic power pin (TASK-07 defect 2) holds codec sysclk + MICB2 from
boot — invisible to `top`, real on the battery
A phone that is "off" should approach zero. Nothing on the device today
even attempts that.
## Shape of the thing
One coordinator (substrate side, likely a sessiond sibling or sessiond
itself — it already owns lock state, which IS the idle signal) driving
three tiers:
1. **Screen-off, tethered to lock.** Lock → after a grace period,
freeze the app tier: `systemctl --user freeze app-*.scope` (cgroup
freezer; requires app launches to land in per-app scopes — they may
already via uwsm/exec rules, verify). The shell, sessiond, modem
stack, and the notification path stay unfrozen. Unlock → thaw
everything, instantly.
2. **Doze.** After N min locked: stop the polling herd (culver first —
find its idle loop regardless; 10% CPU while doing nothing is a bug
on its own), drop Wi-Fi to power-save, let UPower's percentage
polling coast. The lockscreen glance data (media, agent, notify
cards) must still update on wake — cards read on unlock, they don't
need background freshness.
3. **Suspend + scheduled wakeups — the end state.** s2idle with RTC
alarms (`CLOCK_BOOTTIME_ALARM` timers in the coordinator, not
rtcwake hacks): wake every M min → thaw the network fetchers for a
bounded window (check Matrix/mail/notifications) → banner/lock-card
anything that landed (NotifyEvents seam already exists) → re-freeze,
re-suspend. Calls/SMS never wait for a poll window: modem IRQs are
hardware wake sources. Blocked-by: TASK-09 FTS suspend race
(interim-fixed, kernel-side) and the TASK-07 unpin (a pinned codec
clock across suspend is either a wake veto or a lie).
## Design questions to settle before code
- Freeze unit granularity: per-app scopes vs one `apps.slice`. A slice
freeze is one command and no bookkeeping; per-app lets Chatty stay
live for SMS-adjacent work. Likely: freeze `apps.slice`, keep an
allowlist slice (`apps-wake.slice`) for the few that may run locked.
- Who is the notification fetcher during doze? If everything that polls
is frozen, the wake window must thaw → settle → fetch → freeze; the
coordinator needs a "fetch complete" signal, not a fixed sleep.
- culver: freeze it like any app, but ALSO find the spin — freezing a
busy-loop just defers the heat to unlock time.
- Wi-Fi during suspend: WoWLAN on ath10k_snoc is unproven on this
device; assume packets do NOT wake us, only RTC + modem do. Design
for that and be pleasantly surprised otherwise.
- Interaction with charging: plugged-in can skip tiers 23 (it's the
desk-clock case; also the only time `grim`/remote debugging works).
## Acceptance
Locked overnight on battery: no app-tier CPU time accumulated, wake
windows visible in the coordinator's log at the configured cadence, an
SMS arriving mid-doze rings within seconds, and morning battery drop
measurably better than the current baseline (capture tonight's number
first — it is the before).
## Connects to
TASK-08 device-state-manager (this IS its idle half — 08 keeps the
profile/governor side), TASK-09 (suspend blocker), TASK-07 defect 2
(codec pin), NotifyEvents/LockNotifyCard (the landing surface for
wake-window arrivals).

View file

@ -15,12 +15,13 @@ plus open threads from the 2026-07-17 session.
| 1 | [Favorites — wife's name on her number](01-favorites-contacts.md) | blocked | needs a contacts-store decision (qtpim retired — see archive/06) |
| 2 | [Lockscreen-as-Rust-system: glance + swipe](02-lockscreen-rust-system.md) | in progress | stopgap shipped 2026-07-17 |
| 3 | [Boot timing: fade splash → lock, no extra fixes](03-boot-timing-splash-to-lock.md) | open | paired with TASK-11 (now archived) |
| 7 | [WCD9340 mic: SLIM TX channel/port contract](07-mic-wcd9340-slim-tx.md) | diagnosed | frontier; pairs with kernel series |
| 7 | [WCD9340 mic: functional-pinned; unpin + fidelity](07-mic-wcd9340-slim-tx.md) | functional 07-20 | defects: sample-dup, power pin; kernel `mic-race-fix` |
| 8 | [Device state manager / power profiles](08-device-state-manager.md) | open | blocked-by TASK-09 suspend item |
| 9 | [Suspend-resume FTS calibration race](09-suspend-resume-fts.md) | interim-fix | kernel-side; cold-boot only |
| 12 | [Face auth as a capability factor (Gaze reference)](12-gaze-reference-face-auth.md) | open | per-target: phone needs front-cam bring-up |
| 13 | [Active Edge (squeeze) as a sensor input](13-active-edge-squeeze.md) | open | substrate up; bring-up unit not drafted |
| 14 | [Overview pane → phone app grid](14-overview-appgrid.md) | open | shell-side; appgrid prior art linked in-file |
| 15 | [Idle coordinator: freeze, doze, scheduled wakeups](15-idle-coordinator.md) | open | design; blocked-by 09 for suspend tier; eats half of 08 |
Archived (see `archive/`): 05 crash reporter — done 2026-07-21,
CrashReporter.qml in-shell watcher, kill-test verified on device;