| Newsgroups |
org.alsa-project.alsa-devel |
| Message-ID |
<[email protected]> |
Hi,
Both CS35L41 amps driving the internal speakers on an ASUS Zenbook 14
UX3405CA fail every power-up with:
cs35l41-hda spi0-CSC3551:00-cs35l41-hda.0: Failed waiting for
CS35L41_PUP_DONE_MASK: -110
cs35l41-hda spi0-CSC3551:00-cs35l41-hda.1: Failed waiting for
CS35L41_PUP_DONE_MASK: -110
The internal speakers are completely silent. HDMI and Bluetooth are
unaffected.
Reading the amp registers during a failing playback shows that PUP_DONE
*does* eventually assert - the amps are not faulting, they are just
slower to bring up external boost than the 100 ms the driver allows.
Details and evidence below.
ROOT CAUSE EVIDENCE
===================
Registers captured during a failing playback. Playback started, the
PUP_DONE timeout was logged at 11:15:26, registers were read at
11:15:28:
Register amp .0 (L) amp .1 (R)
-------------------- ----------- -----------
PWR_CTRL1 00000001 00000001
IRQ1_STATUS1 01400001 01400000
IRQ1_RAW_STATUS1 41406001 40406000
IRQ1_MASK1 7ffd7e3f 7ffd7e3f
GPIO_STATUS1 00000001 00000000
GPIO_PAD_CONTROL 02010000 02000000
AMP_GAIN_CTRL 00000273 00000273
AMP_ERR_VOL 00000000 00000000
VPBR_STATUS 00000000 00000000
VBBR_STATUS 00000000 00000000
PROTECT_REL_ERR_IGN 00000000 00000000
IRQ1_STATUS1 has bit 24 set on both amps, and CS35L41_PUP_DONE_MASK is
0x01000000 - bit 24. Power-up completed.
That can only be a late assertion. In the CS35L41_EXT_BOOST branch of
cs35l41_global_enable() the failure path returns without clearing the
status; only the success path clears it:
ret = regmap_read_poll_timeout(regmap, CS35L41_IRQ1_STATUS1,
int_status,
int_status & CS35L41_PUP_DONE_MASK,
1000, 100000);
if (ret) {
dev_err(dev, "Failed waiting for CS35L41_PUP_DONE_MASK:
%d\n", ret);
cs35l41_test_key_lock(dev, regmap);
return ret; /* <-- status not cleared */
}
regmap_write(regmap, CS35L41_IRQ1_STATUS1, CS35L41_PUP_DONE_MASK);
Every attempt on this machine takes the failure path, so nothing ever
clears bit 24. The only way it can be set is for the amp to have
asserted PUP_DONE some time after the 100 ms poll expired.
No fault bit is set - not bit 6 (BST_OVP_ERR), 7 (BST_DCM_UVP_ERR), 8
(BST_SHORT_ERR), 15 (TEMP_WARN), 17 (TEMP_ERR) or 31 (AMP_SHORT_ERR).
PWR_CTRL1 = 1 confirms GLOBAL_EN is set, and GPIO_STATUS1 = 1 on amp .0
confirms the VSPK switch is actually being driven.
I should be clear that this is an inference from the code path that
never clears the bit, not a direct observation of the bit changing. I
could not time the assertion: because the failure path never clears it,
it cannot be re-armed without rebooting for a clean baseline.
NO FAULT INTERRUPT HAS EVER FIRED
=================================
Consistent with the above, the amps have never raised an interrupt.
Setup is correct on both sides: all 12 handlers (6 per amp) are
registered, cs35l41_configure_interrupt() does not emit its "IRQ Config
Failed" warning, and IRQ1_MASK1 = 0x7ffd7e3f leaves all six fault
interrupts unmasked at the chip. The parent GPIO interrupt is
level-triggered on hwirq 207.
Yet every counter is zero after hours of uptime:
177: 0 ... 0 intel-gpio 207 cs35l41 IRQ1 Controller, cs35l41 IRQ1
Controller
197: 0 ... 0 cs35l41 IRQ1 Controller 0 Boost Overvoltage Error
198: 0 ... 0 cs35l41 IRQ1 Controller 1 Boost Undervoltage Error
199: 0 ... 0 cs35l41 IRQ1 Controller 2 Boost Inductor Short
Error
200: 0 ... 0 cs35l41 IRQ1 Controller 3 Temperature Warning
201: 0 ... 0 cs35l41 IRQ1 Controller 4 Temperature Error
202: 0 ... 0 cs35l41 IRQ1 Controller 5 Amp Short
(identical block for the second amp, 203-208)
I initially read this as a misrouted interrupt line. I no longer think
so - the register evidence says the part simply does not consider
itself
faulted, so there is nothing for it to report.
HARDWARE
========
Machine: ASUS Zenbook 14 UX3405CA (DMI board UX3405CA)
BIOS: UX3405CA.314, 11/20/2025 (latest from ASUS)
SoC: Intel Core Ultra 200H
HDA codec: Realtek ALC294
Amps: 2x Cirrus Logic CS35L41 (35a40) Revision B2, on SPI
SSID: 10431A63
ALSA components: HDA:8086281d,80860101,00100000
HDA:10ec0294,10431a63,00100004
cfg-dmics:2 iec61937-pcm:5,4,3
SOFTWARE
========
Distribution: Fedora 44
Kernel: 7.1.5-201.fc44.x86_64
linux-firmware: 20260622-1.fc44
alsa-lib: 1.2.16.1
PipeWire: 1.6.8 / WirePlumber 0.5.14
Driver stack: snd_soc_skl_hda_dsp (SOF) + cs35l41-hda over SPI
Earlier 7.1.x kernels showed a milder symptom (see HISTORY); I cannot
confirm whether the PUP_DONE error was present there.
REPRODUCES ON A CLEAN COLD BOOT
===============================
Firmware loads correctly and both amps bind, then power-up fails two
seconds later:
10:48:46 ...hda.0: Calibration applied: R0=10390
10:48:46 ...hda.0: Firmware Loaded - Type: spk-prot, Gain: 19
10:48:46 ...hda.0: CS35L41 Bound - SSID: 10431A63, BST: 1, VSPK: 1,
CH: L, FW EN: 1, SPKID: 1
10:48:46 ...hda.1: Calibration applied: R0=10440
10:48:46 ...hda.1: Firmware Loaded - Type: spk-prot, Gain: 19
10:48:46 ...hda.1: CS35L41 Bound - SSID: 10431A63, BST: 1, VSPK: 0,
CH: R, FW EN: 1, SPKID: 1
10:48:47 ...hda.0: Failed waiting for CS35L41_PUP_DONE_MASK: -110
10:48:47 ...hda.1: Failed waiting for CS35L41_PUP_DONE_MASK: -110
Fully reproducible and permanent from boot. On an earlier boot the
first
failure came 11 s after boot and the only suspend/resume cycle happened
four hours later, so suspend is not the trigger.
BEHAVIOUR WITH THE DSP FIRMWARE ABSENT
======================================
At one point the amps ended up running with no DSP firmware (see the
secondary bug below). In that state the behaviour changed
informatively:
- PUP_DONE no longer timed out at all, across ~20 minutes of repeated
playback.
- The speakers did produce sound, for roughly the first 4 seconds.
- After that they went silent and stayed silent, with playback still
running and no kernel message of any kind.
- Reproduced deliberately with six 2-second pink-noise bursts, 1 s
apart, at 100% volume: only the first two were audible.
"Forced Mute Status" was polled every 2 s across a 20 s playback and
stayed off on both amps.
PLATFORM _DSD IS COMPLETE AND CORRECTLY APPLIED
===============================================
I dumped and decompiled the platform ACPI to check whether the _DSD was
at fault. It is not. Device (SPK1) in SSDT17 declares:
Name (_HID, "CSC3551")
Name (_SUB, "10431A63")
Name (_DSD, Package (0x02)
{
ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
Package (0x06)
{
Package (0x02) { "cirrus,dev-index", Package (0x02)
{ Zero, One } },
Package (0x02) { "reset-gpios", Package (0x08)
{ ^SPK1, , One, Zero, Zero,
^SPK1, , One, Zero, Zero } },
Package (0x02) { "cirrus,speaker-position", Package (0x02)
{ Zero, One } },
Package (0x02) { "cirrus,boost-type", Package (0x02)
{ One, One } },
Package (0x02) { "cirrus,gpio1-func", Package (0x02)
{ One, Zero } },
Package (0x02) { "cirrus,gpio2-func", Package (0x02)
{ 0x02, 0x02 } }
}
})
Every value matches the driver's bind messages and the chip registers:
Property Value Meaning
Confirmed by
----------------------- ------- ------------------------------- --
--------------------
cirrus,boost-type {1, 1} CS35L41_EXT_BOOST
BST: 1 in bind log
cirrus,gpio1-func {1, 0} CS35L41_GPIO1_GPIO / HIZ
VSPK: 1 / VSPK: 0
cirrus,gpio2-func {2, 2} CS35L41_GPIO2_INT_OPEN_DRAIN
IRQ registered, level
cirrus,speaker-position {0, 1} Left / Right
CH: L / CH: R
reset-gpios idx 1 shared reset
"Reset line busy..."
GPIO_PAD_CONTROL reads 0x02010000 on amp .0 (GP2 = 2, GP1 = 1) and
0x02000000 on amp .1 (GP2 = 2, GP1 = 0), exactly as declared.
So unlike 103C89C6 and 103C8B63 - where cs35l41_hda_property.c carries
comments about ACPI declaring the wrong boost type - there is no
evident
mis-declaration here to override.
NOTE ON THE DRIVER PROPERTY TABLE
=================================
In sound/hda/codecs/side-codecs/cs35l41_hda_property.c (formerly
sound/pci/hda/), the entry is:
{ "CSC3551", "10431A63", missing_speaker_id_gpio2 },
identical in v7.0, v7.1 and master. Of the 40 ASUS 1043* SSIDs in that
file, 10431A63 is the only one not handled by generic_dsd_config, and
the only one with no cs35l41_config_table entry.
There is no cirrus,speaker-id-gpios in the platform _DSD, which is
presumably why the quirk exists - it supplies one from GPIO resource
index 2. Decoding the raw _CRS gives five GPIO resources:
idx 0 GpioIo pin 72
idx 1 GpioIo pin 208 reset line referenced by reset-gpios
idx 2 GpioIo pin 206 claimed by the quirk as speaker-id
idx 3 GpioIo pin 207
idx 4 GpioInt pin 207 level-triggered; matches intel-gpio 207
Pin 207 appears twice, as a GpioIo at index 3 and as the GpioInt at
index 4. I cannot verify from here whether index 2 is the correct
speaker-id resource on the CA variant; given the quirk appears to have
been authored for the closely related UX3405MA, that may be worth
confirming.
Also worth noting: "Using extra _DSD properties, bypassing _DSD in
ACPI"
is printed whenever cs35l41_add_dsd_properties() succeeds, but on the
missing_speaker_id_gpio2 path the platform _DSD is still parsed by
cs35l41_hda_parse_acpi(). The message overstates what happened and
initially misled my own diagnosis.
HISTORY
=======
This machine previously showed a milder form of the same problem:
playback cut out for a fraction of a second roughly every 1 s, and a
reboot restored clean audio for a while. No kernel error was logged at
all then. If the cause is a marginal boost ramp against a fixed 100 ms
bound, that earlier intermittent behaviour fits: sometimes boost made
the deadline, sometimes not. It has since degraded to failing every
time.
RULED OUT
=========
None of these changed the behaviour:
- CPU governor (powersave -> performance)
- snd_hda_intel power_save = 0
- PipeWire quantum locked to 1024/2048
- WirePlumber node suspension disabled
- PipeWire RT scheduling (verified: SCHED_RR prio 60 via rtkit)
- SMI storms (0 per turbostat)
- Scheduler latency (no gaps > 30 ms over 15 s)
- CS35L41 runtime PM (forced power/control=on on both SPI devices)
- BIOS and linux-firmware both at latest available versions
Mixer state during failure is correct throughout: ALSA Master 100% on,
Speaker 90% on, SOF Pre/Post Mixer Analog 100% at 0.00 dB, PipeWire
sink
routed to Speaker and unmuted.
SUGGESTED DIRECTION
===================
The 100 ms bound in cs35l41_global_enable() looks too short for this
platform's external boost. A longer timeout, or a per-platform bound,
would likely fix this machine.
If you can supply a debug patch that timestamps the assertion - or
simply raises the timeout so I can report whether audio returns - I am
glad to build and test it. I can also read back further registers or
provide any other debug output on request.
SECONDARY BUG: FIRMWARE LOOKUP FAILS AFTER A PCI REBIND
=======================================================
Probably unrelated, but found along the way. Unbinding and rebinding
the
audio controller:
echo 0000:00:1f.3 > /sys/bus/pci/drivers/sof-audio-pci-intel-
mtl/unbind
echo 0000:00:1f.3 > /sys/bus/pci/drivers/sof-audio-pci-intel-mtl/bind
rebuilds the sound card successfully, but the amps then cannot find
their firmware:
cs35l41-hda ...hda.0: Falling back to default firmware.
cs35l41-hda ...hda.0: Unable to find firmware and tuning
cs35l41-hda ...hda.0: Cannot Initialize Firmware. Error: -2
With firmware_loader dynamic debug enabled, the requested names are:
fw-cirrus/cs35l41--spk-prot-10431a63-spkid1-r0.wmfw
fw-cirrus/cs35l41--spk-prot-10431a63-r0.wmfw
fw-cirrus/cs35l41--spk-prot-10431a63-spkid1.wmfw
fw-cirrus/cs35l41--spk-prot-10431a63.wmfw
fw-cirrus/cs35l41--spk-prot.wmfw
Note the doubled hyphen. SSID (10431a63), speaker id (spkid1) and amp
name (r0) are all still correct - what is missing is dsp_name, i.e.
cs35l41->cs_dsp.name, which should be dsp1. It is empty after the
rebind, so cs35l41_request_firmware_file() builds cs35l41--spk-prot-...
instead of cs35l41-dsp1-spk-prot-..., and every candidate misses,
including the generic fallback (cirrus/cs35l41-dsp1-spk-prot.wmfw.xz is
present on disk).
A runtime reload via the "L0/R0 DSP1 Firmware Load" ALSA controls fails
the same way, so the amps cannot recover their firmware without a
reboot.
Separately, unbinding the HDA codec alone:
echo ehdaudio0D0 >
/sys/bus/hdaudio/drivers/snd_hda_codec_alc269/unbind
tears down the entire SOF card, and the subsequent bind fails with
"No such device".
Happy to provide the full regmap dumps, the decompiled SSDT, or
anything
else useful.
Thanks,
Luqman