Files
c630/docs/firmware.md
rob thijssen 8fa461a41b
Some checks failed
build image / build (push) Has been cancelled
docs: record what firmware fixed, and what it turns out not to fix
The motd was the worst of it — it told anyone logging in that "WiFi and
Bluetooth work with the firmware Fedora ships", which is precisely backwards for
the onboard adapter, and pointed at a Windows partition this machine no longer
has.

What the blobs did fix, verified: the GPU initialises, ADSP/CDSP/SLPI all run,
sensors appear as IIO devices, venus registers /dev/video0-3.

What they did not, and why, so nobody repeats the search:

Onboard WiFi is loaded by the modem — wlan*mdsp* is the WLAN Modem DSP — so
ath10k_snoc binds and then waits silently on a QMI service that never arrives.
The modem boots and dies at "RF stuck in QLINK start state", the RF front-end,
which is board-specific wiring in a way the SoC-level blobs are not. These are
Qualcomm *reference* drivers; good enough for the GPU and DSPs, evidently not
for this machine's radio. Three plausible fixes that do not work are recorded
too: rmtfs (correct to enable, does not help), the _nm firmware variants
(rejected at PBL), and unplugging the USB dongle (different bus entirely — it
was never competing).

Audio gets further than expected. The codec answers over SLIMbus with its chip
id, so hardware and ADSP are fine; what fails is the codec's SoundWire block
declaring two data-in ports where the controller expects six. That is a kernel
mismatch and needs a kernel change.

Also record the mesa caveat properly: the GPU works, but freedreno's EGL path
segfaults gnome-shell into a login loop, and LIBGL_ALWAYS_SOFTWARE does nothing
because mutter uses EGL. MESA_LOADER_DRIVER_OVERRIDE=kms_swrast is what works.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XWRjNJMistCy6ngXH5aJLS
2026-07-28 08:24:11 +03:00

13 KiB

Firmware

The C630's firmware splits into two groups, and the split is about licensing, not difficulty.

What ships in the image

Qualcomm permits redistribution of these, so Fedora packages them and the build installs them:

Package Files Enables
atheros-firmware ath10k/WCN3990/hw1.0/{firmware-5.bin,board-2.bin,wlanmdsp.mbn} WiFi and Bluetooth
qcom-firmware qcom/a630_gmu.bin, qcom/a630_sqe.fw The generic half of the Adreno 630 — necessary but not sufficient

Networking therefore works on a freshly written image. Graphics does not: see below.

What you have to supply

These are signed against per-model keys and Fedora does not ship them, so a stock install has none of them. Nothing about them is unredistributable in principle — the equivalent files for the ThinkPad X13s are in linux-firmware — it is that nobody has obtained permission for this model. See If you have no backup.

The list is not a guess: it is every firmware-name property in the mainline device tree, arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts:

File Device tree node Lost without it
qcdxkmsuc850.mbn gpu_zap_shader Accelerated graphics
qcadsp850.mbn adsp_pas Audio — speakers, microphone
qcslpi850.mbn slpi_pas Sensor hub — lid switch, accelerometer, auto-rotate
qccdsp850.mbn cdsp_pas Compute DSP offload
qcvss850.mbn venus Hardware video decode
qcdsp1v2850.mbn mss_pil Modem, stage 1
qcdsp2850.mbn mss_pil Modem, stage 2
ipa_fws.elf ipa IPA datapath, needed by the modem

They belong under /lib/firmware/updates/qcom/sdm850/LENOVO/81JL/.

The GPU one is worth calling out, because it is easy to assume otherwise: Fedora does ship an sdm845/a630_zap.mbn, but that is the generic Snapdragon 845 zap shader and not what this device asks for. The C630's device tree names qcom/sdm850/LENOVO/81JL/qcdxkmsuc850.mbn specifically, so until you supply it you get an unaccelerated display.

Each file is independent, so a partial recovery is still worth having — qcdxkmsuc850.mbn on its own buys you working graphics.

Confirmed on the hardware

This is not inferred from the device tree. On a machine with the generic Adreno firmware present and the model-signed blob absent, the GPU gets two files in and then stops:

[drm:adreno_request_fw]   loaded qcom/a630_sqe.fw from new location
[drm:adreno_request_fw]   loaded qcom/a630_gmu.bin from new location
[drm:zap_shader_load_mdt] *ERROR* Unable to load qcom/sdm850/LENOVO/81JL/qcdxkmsuc850.mbn
[drm:adreno_load_gpu]     *ERROR* gpu hw init failed: -2

So the generic pieces are necessary and not sufficient, and the zap shader is a hard requirement rather than an optimisation. Two details follow from that trace:

The driver never falls back to sdm845/a630_zap.mbn, even though the file is installed — the device tree's firmware-name pins the model-specific path. Nor would falling back help: the zap shader is signed against the device's own secure-boot chain, and the Snapdragon 845 development board's copy will not validate here.

The display keeps working throughout. msm_dpu binds the panel and the console lands on msmdrmfb regardless, so a failed GPU costs you acceleration, not a picture. A desktop still runs on llvmpipe — though /dev/dri/renderD128 is present even when hw init has failed, so mesa may try freedreno, fail, and take the display manager down with it. LIBGL_ALWAYS_SOFTWARE=1 in /etc/environment avoids that.

These have to be in the initramfs

msm_dpu probes about six seconds in, while the initramfs is still the root filesystem, and does not retry. Firmware that only exists on the real root is therefore invisible to it:

msm_dpu ae01000.display-controller: Direct firmware load for qcom/a630_sqe.fw failed with error -2

overlay/etc/dracut.conf.d/10-c630.conf puts the Adreno files in the initramfs for this reason, and c630-firmware adds qcdxkmsuc850.mbn once it exists.

Only the GPU needs this. The ADSP, CDSP, SLPI, venus and modem all probe around forty seconds in, well after the real root is mounted, and load from it perfectly happily. That distinction is worth keeping: qcdsp2850.mbn alone is 60 MB, so sweeping the whole firmware directory into the initramfs would inflate it roughly twentyfold for no benefit.

Finding them on a backup

If Windows is long gone from the machine but you kept a copy of the files, one search catches all eight, since seven share a suffix:

find /mnt/backup -type f \
    \( -iname 'qc*850.mbn' -o -iname 'ipa_fws.elf' -o -iname 'bdwlan*.bin' \) \
    -printf '%10s  %p\n'

They originally lived in C:\Windows\System32\DriverStore\FileRepository\, in directories named qcdx850.inf_arm64_* and qcadsp*.inf_arm64_*. If the loose files turn up nothing, look for those directories wholesale:

find /mnt/backup -type d -iname 'qc*.inf_arm64_*'

Sizes range from 14 KB (qcdxkmsuc850.mbn) to 60 MB (qcdsp2850.mbn). Despite the .mbn extension they are ELF images, and file should say so:

qcdxkmsuc850.mbn: ELF 32-bit LSB executable, QUALCOMM DSP6, version 1 (SYSV)

Anything reporting as text or HTML is a download that went wrong.

Lenovo does not appear to publish a driver package for the 81JL containing these. If the backups turn up nothing, see If you have no backup — the files are not tied to your individual machine, so yours is not the only possible source.

If you have no backup

Losing the Windows partition is not the end of it, because these blobs are signed per model, not per machine. A copy from any other Yoga C630 works on yours.

The evidence is in linux-firmware itself, which already ships the exact analogous set for the ThinkPad X13s — one copy serving every unit of that model:

qcom/sc8280xp/LENOVO/21BX/qcdxkmsuc8280.mbn     zap shader
qcom/sc8280xp/LENOVO/21BX/qcadsp8280.mbn        audio
qcom/sc8280xp/LENOVO/21BX/qccdsp8280.mbn        compute DSP
qcom/sc8280xp/LENOVO/21BX/qcslpi8280.mbn        sensors
qcom/sc8280xp/LENOVO/21BX/qcvss8280.mbn         video

A one-to-one match with the C630's list, differing only in the model. So these files are redistributable once Lenovo and Qualcomm permit it — it is a licensing question, not a technical one.

qcom/sdm850/LENOVO/81JL/ has simply never been contributed. Fedora ships no sdm850 model firmware at all, so it will not turn up in a linux-firmware update. Three routes, easiest first:

The WOA-Project reference drivers. https://github.com/WOA-Project/Qualcomm-Reference-Drivers collects the Windows-on-ARM driver packages by SoC. The C630's are three cabinet files, and between them they hold every file this machine asks for:

Cabinet Contains
qcdx850.cab qcdxkmsuc850.mbn (zap shader), qcvss850.mbn (venus)
qcipa850.cab ipa_fws.elf
qcsubsys850.cab qcadsp850.mbn, qccdsp850.mbn, qcslpi850.mbn, qcdsp1v2850.mbn, qcdsp2850.mbn, and WLANMDSP.MBN

That last one is a bonus — the WiFi firmware, which is not in the device tree's firmware-name list but is what the onboard ath10k needs.

cabextract or 7z x will open them:

for f in qcdx850.cab qcipa850.cab qcsubsys850.cab; do 7z x -o./extracted "$f"; done
find ./extracted -iname 'qc*850.mbn' -o -iname 'ipa_fws.elf' -o -iname 'WLANMDSP.MBN'

Ignore the _nm and _CLS variants; the device tree asks for the plain names. Verify what you get with file — they should report as ELF 32-bit LSB executable, QUALCOMM DSP6, not as text or HTML.

Lenovo's Digital Download Recovery Service. Lenovo builds a Windows recovery image against a machine's serial number and lets the owner download it. That image contains Windows/System32/DriverStore/FileRepository, and therefore all eight files. Slower than the cabinets, but it is unambiguously your own machine's firmware.

Another C630 owner. The aarch64-laptops project is where these have circulated before, and any 81JL's copy is as good as your own.

Whichever route, drop the result into firmware/local/ and rebuild — see Baking firmware into the image.

What the firmware does not fix

Installing all of it still leaves two things broken, both for reasons that are not about firmware. Recorded here so nobody repeats the search.

Onboard WiFi needs the modem, and the modem needs firmware nobody publishes

The name gives it away: wlanmdsp is the WLAN Modem DSP. On SDM845 the WiFi firmware is loaded by the modem subsystem, so ath10k_snoc binds to 18800000.wifi and then sits silent, waiting on a QMI service that only appears once the modem is up. It never logs an error, because from its point of view nothing has gone wrong yet.

The modem itself boots and then dies:

qcom-q6v5-mss 4080000.remoteproc: MBA booted without debug policy, loading mpss
qcom-q6v5-mss 4080000.remoteproc: fatal error received:
    RFLM@rflm_qlnk.cpp:1087 [3,3] RF stuck in QLINK start state: 0x0 after maximum
remoteproc remoteproc0: crash detected ... recovering

QLINK is the RF front-end interface, and RF front-end wiring is board-specific in a way the DSP and GPU images are not. The WOA-Project cabinets are Qualcomm reference drivers: good enough for the SoC-level blobs, evidently not for this machine's radio. It restarts about every 42 seconds until stopped.

Things that sound like they should help and do not:

  • rmtfs. The modem stores its NV and calibration on the modemst1, modemst2, fsg and fsc partitions and reaches them through the rmtfs daemon. Fedora packages it, and it is disabled by default. Enabling it is correct and worth doing — sudo systemctl enable --now rmtfs — but the QLINK failure persists with it running. Its "failed to update start state" warnings are its optional remoteproc-control helper and are harmless.
  • The _nm firmware variants. qcdsp1v2850_nm.mbn and qcdsp2850_nm.mbn are the no-modem images, 5.7 MB against 60 MB, analogous to the modem_nm.mbn Fedora ships for generic sdm845. They are rejected before they even run: PBL returned unexpected status.
  • Unplugging the USB WiFi dongle. Different bus, different driver, no interaction. ath10k_snoc is waiting on the modem, not competing for a radio.

A USB dongle or USB tethering from a phone is the practical answer.

Audio is a kernel driver problem

The ADSP runs, the audio services register, and the codec answers:

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 of the codec:

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

The codec's SoundWire block declares two data-in ports where the controller expects six, so it never probes, so the sound card has no codec DAI and never appears. That is a mismatch inside the kernel, and needs a kernel or device-tree change rather than any blob.

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 gets the codec detected, which is how the chip ID above was obtained — but it does not get past the SoundWire mismatch.

Extracting them

The image ships Fedora's qcom-firmware-extract. Run it once:

sudo c630-firmware
sudo reboot

That wraps qcom-firmware-extract and regenerates the initramfs afterwards, which matters — the ADSP firmware has to be available before the root filesystem is mounted, and overlay/etc/dracut.conf.d/10-c630.conf only takes effect on a rebuild.

If it cannot find Windows, mount it yourself and point the tool at it:

lsblk -o NAME,SIZE,FSTYPE,LABEL       # find the NTFS partition
sudo mkdir -p /mnt/windows
sudo mount /dev/sda4 /mnt/windows
sudo qcom-firmware-extract --windows-dir /mnt/windows

qcom-firmware-extract was written for the Snapdragon 8cx and X Elite laptops. If it does not recognise the SDM850, copy the files by hand — they are in Windows/System32/DriverStore/FileRepository/ under a qcdx*.inf_arm64_* directory:

sudo find /mnt/windows/Windows/System32/DriverStore/FileRepository \
    -iname 'qc*850.mbn' -o -iname 'ipa_fws.elf' -o -iname 'qcdxkmsuc850.mbn'

sudo mkdir -p /lib/firmware/updates/qcom/sdm850/LENOVO/81JL
sudo cp <each file> /lib/firmware/updates/qcom/sdm850/LENOVO/81JL/
sudo dracut --force --regenerate-all

Check it took:

dmesg | grep -iE 'remoteproc|adsp|cdsp'

Baking firmware into the image

If you would rather not repeat the extraction on every reinstall, drop the files into firmware/local/ in this repo, mirroring the /usr/lib/firmware/updates/ layout:

firmware/local/qcom/sdm850/LENOVO/81JL/qcadsp850.mbn
firmware/local/qcom/sdm850/LENOVO/81JL/qccdsp850.mbn
...

build/stage2.sh picks them up automatically. They are gitignored, and should stay that way — this repository is public and the blobs are not yours to publish.