docs: audio root causes found; WiFi narrowed to the modem's own RF path

Audio: Fedora has never built CONFIG_SND_SOC_WSA881X, the speaker-amp
driver the in-tree DTS requires; with it built out of tree the card
registers and plays. The din-ports message is a harmless warning. Also
record the S24_LE format defect and the sink-stealing traps (AirPlay,
Bluetooth, phantom headphone jack) that masqueraded as broken audio.

WiFi: every published modem firmware build (five distinct across nine
WOA versions) and kernel 6.12 all fail identically at QLINK, the link to
the SDR845 RF transceiver. This unit is an LTE SKU with working modem
history under Windows, fsg/fsc are blank, and the modem asks rmtfs for
unknown fsg_oem partitions. Fedora's tqftpserv also serves different
paths than the old aarch64-laptops one — the earlier instructions here
were wrong for it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011XgGF5wfxLDAybVnNz6eNQ
This commit is contained in:
2026-07-28 12:02:19 +03:00
parent 9b28150c86
commit 7585c5f147
2 changed files with 163 additions and 46 deletions

View File

@@ -33,9 +33,10 @@ login. Root is locked.
| Works | Works once you supply firmware | Does not work | | Works | Works once you supply firmware | Does not work |
|---|---|---| |---|---|---|
| UFS storage, USB, keyboard, touchpad, touchscreen | Adreno 630 initialises — see mesa note below | Audio — kernel driver, not firmware | | UFS storage, USB, keyboard, touchpad, touchscreen | Adreno 630 initialises — see mesa note below | Onboard WiFi — loaded by the modem, which does not stay up |
| Display (unaccelerated), battery, charging | Sensor hub — accelerometer, auto-rotate | Onboard WiFi — loaded by the modem, which does not stay up | | Display (unaccelerated), battery, charging | Sensor hub — accelerometer, auto-rotate | LTE modem — crashes at RF init; see docs/firmware.md for what has been ruled out |
| USB WiFi dongles, USB phone tethering | Hardware video decode (venus, `/dev/video0-3`) | LTE modem — crashes at RF init, cause unknown | | USB WiFi dongles, USB phone tethering | Hardware video decode (venus, `/dev/video0-3`) | |
| Bluetooth — the controller has no burned-in address; `c630-bt-addr.service` reads the factory one off the DPP partition at boot | Audio — needs `qcadsp850.mbn` plus one kernel module Fedora does not build; see docs/firmware.md | |
Verified on hardware. `docs/firmware.md` has the evidence for each, including Verified on hardware. `docs/firmware.md` has the evidence for each, including
the things that look like they should help and do not. the things that look like they should help and do not.
@@ -153,9 +154,13 @@ installed packages as missing — see [docs/install.md](docs/install.md).
With the per-model firmware supplied, the GPU initialises, the ADSP, CDSP and With the per-model firmware supplied, the GPU initialises, the ADSP, CDSP and
SLPI all run, sensors appear as IIO devices and venus registers `/dev/video0-3`. SLPI all run, sensors appear as IIO devices and venus registers `/dev/video0-3`.
Audio and onboard WiFi remain broken for reasons unrelated to firmware — see Audio additionally needs one kernel module Fedora does not build
[docs/firmware.md](docs/firmware.md), which records both the evidence and the (`CONFIG_SND_SOC_WSA881X`); with it compiled out of tree, the sound card
several plausible fixes that turn out not to work. registers and the speakers play. Onboard WiFi remains broken: the modem that
hosts the WLAN firmware dies at RF init, and firmware versions, userspace
services and kernel 6.12-vs-7.1 have all been eliminated as the cause — see
[docs/firmware.md](docs/firmware.md), which records the evidence and the many
plausible fixes that turn out not to work.
The internal drive needs `build/install-to-disk.sh` rather than `dd`: its UFS The internal drive needs `build/install-to-disk.sh` rather than `dd`: its UFS
uses 4096-byte logical sectors, which the 512-byte image geometry cannot be uses 4096-byte logical sectors, which the 512-byte image geometry cannot be

View File

@@ -217,8 +217,10 @@ Whichever route, drop the result into `firmware/local/` and rebuild — see
## What the firmware does not fix ## What the firmware does not fix
Installing all of it still leaves two things broken, both for reasons that are Installing all of it still leaves two things not working out of the box, for
not about firmware. Recorded here so nobody repeats the search. reasons that are not about firmware. Audio turns out to be a missing Fedora
kernel module and is fixable today; onboard WiFi remains genuinely broken.
Recorded here so nobody repeats the search.
### Onboard WiFi: everything is in place except a working modem ### Onboard WiFi: everything is in place except a working modem
@@ -261,12 +263,14 @@ said to show around 40.
#### The modem fetches wlanmdsp over TFTP #### The modem fetches wlanmdsp over TFTP
This is the step that is easy to miss, and the reason `tqftpserv` exists. This is the step that is easy to miss, and the reason `tqftpserv` exists.
`tqftpserv` serves from `/lib/firmware/`, and the modem asks it for the WLAN The modem asks it for the WLAN firmware by path — but where tqftpserv serves
firmware by path: from depends on the version, and the layout below (from aarch64-laptops) does
**not** work with Fedora's current build; see [The tqftpserv paths above are
version-dependent](#the-tqftpserv-paths-above-are-version-dependent). On
current Fedora:
```sh ```sh
sudo mkdir -p /lib/firmware/readonly/firmware/image sudo ln -sf updates/qcom/sdm850/LENOVO/81JL/wlanmdsp.mbn /lib/firmware/wlanmdsp.mbn
sudo cp wlanmdsp.mbn /lib/firmware/readonly/firmware/image/
``` ```
The modem also wants a writable area, and says so in the `tqftpserv` journal: The modem also wants a writable area, and says so in the `tqftpserv` journal:
@@ -276,10 +280,13 @@ The modem also wants a writable area, and says so in the `tqftpserv` journal:
[TQFTP] WRQ: /readwrite/server_check.txt [TQFTP] WRQ: /readwrite/server_check.txt
``` ```
On current Fedora that lives in `/tmp/tqftpserv` (recreate after each boot, or
add a tmpfiles.d entry):
```sh ```sh
sudo mkdir -p /lib/firmware/readwrite/ota_firewall sudo mkdir -p /tmp/tqftpserv/ota_firewall
sudo touch /lib/firmware/readwrite/ota_firewall/ruleset sudo touch /tmp/tqftpserv/ota_firewall/ruleset
sudo chmod -R 0777 /lib/firmware/readwrite sudo chmod -R 0777 /tmp/tqftpserv
``` ```
Watching `journalctl -u tqftpserv` while starting the modem is the best Watching `journalctl -u tqftpserv` while starting the modem is the best
@@ -315,26 +322,67 @@ made to load.
#### Firmware has been eliminated as the variable #### Firmware has been eliminated as the variable
The test that narrows it: both projects publish the modem trio, and their The nine driver versions under `Lenovo/YogaC630/` contain five distinct
copies differ — the modem pair matches on size but not checksum. Both were modem-firmware builds — `200.0.1.0` through `200.0.4.0` share one 2018-vintage
installed on this machine, with `rmtfs` and `tqftpserv` running and `wlanmdsp` pair (a different mpss size from all later builds, and the closest match to
in the TFTP path. Both produce the **identical** QLINK failure. this machine's 2018-10 manufacture date), `200.0.10.0`, `200.0.11.0`,
`200.0.14.0`/`200.0.16.0`, and `200.0.19.0` are the others, and
aarch64-laptops' copies differ again by checksum. Every one of them was booted
on this machine, with `rmtfs` and `tqftpserv` running. Every one produces the
**identical** QLINK failure, crashing about 0.4 s after mpss comes up.
That is worth stating carefully. It does not show either set to be wrong, and So neither the firmware nor its vintage is the variable. Nor is the kernel
it does not make one more trustworthy than the other — the aarch64-laptops version, at least not recently: Fedora's 6.12.15 (`kernel-core` from koji,
copies come from a setup whose support table ticks WiFi, but on a 2019 kernel, booted with the same device tree) produces the identical crash. The
and neither set has been shown to work here. What it does show is that changing aarch64-laptops setup that ticked WiFi ran a 2019 5.x branch, so a very old
firmware does not change the outcome. regression is not excluded — but their issue #51 records modem crash/recovery
lockups on that setup too, so "WiFi worked" and "the modem crashed" may well
have coexisted, with `wlanmdsp` loading in the window before the first crash.
An earlier note in this project's history has the crash loop at roughly 42 s;
today it is ~3.4 s. If those cadences are both real, what changed — and
whether a long first window is enough for `wlfw` to appear — is an open
question.
So the difference is not the firmware, and not the userspace services. What #### What QLINK is, and what the modem reads before dying
remains is the kernel: that setup ran `aarch64-laptops/linux` branch `gpu`,
circa 2019 on 5.x, against 7.1.5 here. Both ath10k patches it applied are long
upstream — the device tree carries `qcom,snoc-host-cap-8bit-quirk` from the
first of them. Six years of `qcom-q6v5-mss` churn sits between the two.
That points at a kernel regression rather than anything missing on disk. QLINK is the SerDes link between the SoC and the SDR845 RF transceiver chip;
Confirming it means bisecting or booting an older kernel, which nothing here `rflm_qlnk.cpp` is the modem firmware's manager for it. "Stuck in start state
does yet. 0x0" means the transceiver never answered — the signature of a chip that is
absent, unpowered, or misconfigured. This unit is an LTE SKU (SIM slot,
factory-label IMEI, modem worked under Windows), so the chip is present and
functional; under Windows, ACPI and the Qualcomm PEP handle subsystem power,
and under device-tree boot whatever they did for the RF front end has no
equivalent. That is a hypothesis, not a finding — the DSDT keeps its power
management in opaque PEP tables, so it could not be confirmed from the tables
alone.
`rmtfs -v` shows the modem's storage dialogue in the half-second before the
crash: it opens `modem_fs1`, `modem_fs2`, `modem_fsg` and `modem_fsc`
successfully, requests two partitions Linux's rmtfs has never heard of —
`modem_fsg_oem_1` and `modem_fsg_oem_2`, names that appear inside the mpss
image itself, with no provider found in any WOA-Project cabinet — allocates
its 2 MB buffer, reads a superblock's worth, and dies. On this machine
`modemst1`/`modemst2` carry valid `IMGEFS1`/`IMGEFS2` filesystems, while
**`fsg` and `fsc` are entirely blank**. Byte copies of all four live in
`firmware/local/nv-backup/` (gitignored), taken before any experimentation.
#### The tqftpserv paths above are version-dependent
The `/lib/firmware/readonly/firmware/image/` and `/lib/firmware/readwrite/`
layout described earlier matches the old aarch64-laptops tqftpserv. Fedora's
current build is different: `/readwrite/*` is served from `/tmp/tqftpserv`
(tmpfs — anything created there is gone on reboot), and
`/readonly/firmware/image/<file>` resolves through the normal firmware search
path, i.e. `/lib/firmware/<file>`. So `wlanmdsp.mbn` needs to be reachable as
`/lib/firmware/wlanmdsp.mbn` (a symlink to the copy under `updates/` works),
and the writable tree belongs under `/tmp/tqftpserv/`.
The full Windows-side TFTP tree can be reconstructed, and was: the driver
INFs are TFTP-mapping manifests, and `mcfg_subsys_ext850.inf` maps 159 modem
configuration files — carrier configs and, notably, `mcfg_hw` hardware RF
configs for the SDX20 — into `readonly/firmware/image/modem_pr/mcfg/...`.
Serving all of it changes nothing: the modem crashes before requesting any of
it.
Things that sound like they should help and do not: Things that sound like they should help and do not:
@@ -343,14 +391,24 @@ Things that sound like they should help and do not:
- **`rmtfs` alone.** Necessary, not sufficient. Its "failed to update start - **`rmtfs` alone.** Necessary, not sufficient. Its "failed to update start
state" warnings are its optional remoteproc-control helper and are harmless. state" warnings are its optional remoteproc-control helper and are harmless.
- **The `_nm` variants.** See above. - **The `_nm` variants.** See above.
- **Any published firmware version.** All five distinct builds fail
identically; see above.
- **The MCFG configuration tree.** Served in full over TFTP; the modem never
gets far enough to ask for it.
- **`clk_ignore_unused pd_ignore_unused`.** Already on the command line
(postmarketOS ships the same for this machine).
Untried, in rough order of promise: the other eight driver versions under Untried, in rough order of promise: reporting upstream — the error string
`Lenovo/YogaC630/` (this machine's UEFI is `9UCN33WW(V2.06)` from 2019, and the appears nowhere public, and linux-arm-msm (Bjorn Andersson, Dmitry Baryshkov,
newest package may not be the right vintage); `mcfg_subsys_ext850.cab`, which Steev Klimaszewski all have C630 history) is where the RF-power hypothesis
holds modem configuration data; and building `board-2.bin` from the C630's own could be confirmed or killed; Lenovo's Digital Download Recovery Service image
`bdwlan.*` files rather than relying on Fedora's. for this serial, which would show the exact original driver set and whatever
provides `modem_fsg_oem_*` under Windows; building `board-2.bin` from the
C630's own `bdwlan.*` files (only relevant once the modem stays up); and the
5.x aarch64-laptops kernel itself, to test whether its crash cadence leaves a
usable window.
### Audio is a kernel driver problem ### Audio: Fedora does not build the speaker-amp driver
The ADSP runs, the audio services register, and the codec answers: The ADSP runs, the audio services register, and the codec answers:
@@ -358,23 +416,77 @@ The ADSP runs, the audio services register, and the codec answers:
wcd934x-slim 217:250:1:0: WCD934x chip id major 0x108, minor 0x1 wcd934x-slim 217:250:1:0: WCD934x chip id major 0x108, minor 0x1
``` ```
So SLIMbus, the ADSP and the hardware are all fine. What fails is one sub-device So SLIMbus, the ADSP and the hardware are all fine. The trail of suspicion
of the codec: used to end at this SoundWire message:
``` ```
qcom-soundwire wcd934x-soundwire.12.auto: din-ports (2) mismatch with controller (6) qcom-soundwire wcd934x-soundwire.12.auto: din-ports (2) mismatch with controller (6)
platform sound: deferred probe pending: msm-snd-sdm845: SLIM Playback: codec dai not found platform sound: deferred probe pending: msm-snd-sdm845: SLIM Playback: codec dai not found
``` ```
The codec's SoundWire block declares two data-in ports where the controller The first line is a red herring. Since kernel 6.18 (commit `9e53a66a2f2f`,
expects six, so it never probes, so the sound card has no codec DAI and never "soundwire: qcom: deprecate qcom,din/out-ports") it is a warning: the
appears. That is a mismatch inside the kernel, and needs a kernel or device-tree value overrides the register value afterwards and the probe
device-tree change rather than any blob. continues.
The real blocker is the second line. The device tree's `SLIM Playback` link
names four codec DAIs: the WCD9340, the SoundWire master inside it, and the
two WSA881x speaker amplifiers hanging off that master. Fedora's aarch64
kernel has never set `CONFIG_SND_SOC_WSA881X` — while the newer siblings
WSA883X and WSA884X, which the ThinkPad X13s needs, are both `=m`. With no
amplifier driver the two SoundWire slaves never bind, the machine driver
cannot find its DAIs, and the card sits in deferred probe forever.
Building the one missing module proves it. `sound/soc/codecs/wsa881x.c` is a
single self-contained file; compiled against the running kernel
(`kernel-devel`, `gcc`, `make`, then `make -C /lib/modules/$(uname -r)/build
M=$PWD modules` with `obj-m += snd-soc-wsa881x.o` and `snd-soc-wsa881x-objs :=
wsa881x.o`), installed under `/lib/modules/$(uname -r)/extra/` and picked up
by `depmod -a`, it autoloads on the SoundWire modalias at boot and the
`Lenovo-YOGA-C630-13Q50` card registers, headphone-jack detection included.
The UCM profile has been in alsa-ucm-conf since 2020; `alsaucm` itself is in
`alsa-ucm-utils`, so on the minimal image:
```sh
alsaucm -c Lenovo-YOGA-C630-13Q50 set _verb HiFi set _enadev Speaker
speaker-test -D plughw:0,0 -c2 -t sine -f 440 -l 1
```
The `overflow error on RX port` and register-read `-16` messages logged when a
stream stops are noise, seen on working sdm845 devices too.
**PipeWire needs one more thing.** The ADSP front-end advertises S24_LE
(24-bit samples in 32-bit containers) and then garbles it: playback runs
without any error, consumes data at the right rate, and powers every DAPM
widget — a byte-for-byte identical `/sys/kernel/debug/asoc` widget dump
against a working stream — while producing ~-48 dB of near-silence,
consistent with the samples being bit-shifted inside the container. PipeWire
prefers the widest format a device offers, so every PipeWire client (mpv,
GNOME, `pw-play`) is inaudible while `aplay` and `speaker-test`, which
default to S16_LE, work perfectly. This cost most of a day: the failure is
per-format, invisible in every log, and easy to misattribute to routing,
suspend/resume or per-stream volumes, all of which look guilty and are not.
`overlay/etc/wireplumber/wireplumber.conf.d/51-c630-alsa.conf` pins the
built-in card to S16_LE, which fixes every client. The underlying q6asm
format bug is worth a report to alsa-devel.
Unrelated to the hardware, two pieces of routing politics are worth knowing
on a machine like this: PipeWire's RAOP discovery will happily make a
neighbour's AirPlay speaker the default sink, and a newly connected
Bluetooth device also takes over the default. If sound "does not work",
check `wpctl status` for where the default actually points before blaming
the drivers.
The durable fix is one line of Fedora kernel config — flipping
`CONFIG_SND_SOC_WSA881X` to `=m` for aarch64, an easy case given the in-tree
device tree requires it and both newer WSA drivers are already enabled. Until
that lands, the module has to be built out of tree per kernel.
One thing worth knowing if you go looking: `slim_qcom_ngd_ctrl` can probe before One thing worth knowing if you go looking: `slim_qcom_ngd_ctrl` can probe before
the ADSP is ready and log `QMI wait timeout`. Reloading the module afterwards the ADSP is ready and log `QMI wait timeout`. Reloading the module afterwards
gets the codec detected, which is how the chip ID above was obtained — but it gets the codec detected, which is how the chip ID above was obtained. A series
does not get past the SoundWire mismatch. of NGD probe-ordering fixes (Bjorn Andersson, mid-2026, Cc: stable) targets
exactly this class of race and should reach Fedora with the 7.2 rebase.
## Extracting them ## Extracting them