* [BUG] ALSA: hda: snd_hda_scodec_tas2781_i2c: DSP firmware silently fails to load at cold boot (Lenovo Yoga Pro 9 16IMH9)
@ 2026-05-01 17:56 Rámon van Raaij
0 siblings, 0 replies; only message in thread
From: Rámon van Raaij @ 2026-05-01 17:56 UTC (permalink / raw)
To: linux-sound; +Cc: alsa-devel, Takashi Iwai, Shenghao Ding
Machine: Lenovo Yoga Pro 9 16IMH9 (board SSID 83DN)
Kernel: 7.0.3-zen1-1-zen (linux-zen)
Codec: Realtek ALC287, subsystem ID 17aa:38d6
Amps: 4x TI TAS2781, ACPI HID TIAS2781, I2C bus i2c-4
(I2C controller: PCI 0000:00:15.2, Intel Designware)
Fixup: ALC287_FIXUP_TAS2781_I2C (applied correctly via HDA_CODEC_QUIRK 17aa:38d6)
PROBLEM
-------
At cold boot the built-in speakers produce ~10-15% of normal volume.
The TAS2781 amplifiers are powered and the HDA component bind succeeds:
snd_hda_codec_alc269 ehdaudio0D0: bound i2c-TIAS2781:00
(ops tas2781_hda_comp_ops [snd_hda_scodec_tas2781_i2c])
However, the DSP firmware (TAS2XXX38D6.bin) is not loaded. The ALSA
control "Speaker Force Firmware Load" -- which is only registered after
a successful firmware load inside tas2781_hda_comp_bind() -- is absent:
$ amixer -c sofhdadsp cget name="Speaker Force Firmware Load"
amixer: Cannot find the given element from control sysdefault:0
No error is logged to dmesg. The driver continues without DSP calibration
data, leaving the amplifiers at uncalibrated default gain.
The issue does NOT occur after S3 suspend/resume. After resume, a
modprobe -r / modprobe cycle restores full speaker volume.
ROOT CAUSE
----------
The BIOS initialises the TAS2781 amps during POST and leaves them in a
hardware state incompatible with the kernel firmware load sequence. The
request_firmware_nowait() call in the bind path silently fails when called
against hardware in this BIOS-initialised state.
Critical observation: a PCI-level power cycle of the I2C controller
(0000:00:15.2) reliably resolves the issue:
echo 1 > /sys/bus/pci/devices/0000:00:15.2/remove
sleep 2
echo 1 > /sys/bus/pci/rescan
After this sequence ACPI gates the I2C controller power rail (D3cold),
resetting the TAS2781 amps to factory state. On rescan, i2c_designware
and snd_hda_scodec_tas2781_i2c re-probe, DSP firmware loads, and
"Speaker Force Firmware Load" appears in amixer.
A plain modprobe -r / modprobe cycle WITHOUT the PCI power cycle does
NOT fix the issue, confirming this is a hardware state problem, not a
module loading or firmware subsystem timing race.
WORKAROUND
----------
The following systemd service (After=sound.target,
Before=display-manager.service) reliably restores correct volume on
every boot. It must run before PipeWire opens the device; hot-removing
the PCI device while WirePlumber has an active ALSA handle causes a
SIGSEGV in snd_hctl_elem_get_interface (libasound).
[Service]
Type=oneshot
ExecStartPre=/bin/sleep 2
ExecStart=/bin/bash -c 'echo 1 > /sys/bus/pci/devices/0000:00:15.2/remove'
ExecStart=/bin/sleep 2
ExecStart=/bin/bash -c 'echo 1 > /sys/bus/pci/rescan'
ExecStart=/bin/bash -c 'modprobe snd_hda_scodec_tas2781_i2c || true'
ExecStart=/bin/sleep 5
RemainAfterExit=yes
SUGGESTED FIX
-------------
The driver should detect a failed firmware load in tas2781_hda_comp_bind()
and either:
a) Perform a hardware reset sequence on the TAS2781 amps (e.g. via an
ACPI power state transition on the I2C controller) before retrying, or
b) Schedule a deferred retry after the firmware subsystem has fully
settled, with a fallback hardware reset if the retry also fails.
The fix should live in tas2781_hda_comp_bind() or the
request_firmware_nowait() completion callback in
snd_hda_scodec_tas2781_i2c.
Signed-off-by: Rámon van Raaij <ramon@vanraaij.eu>
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2026-05-01 17:56 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-01 17:56 [BUG] ALSA: hda: snd_hda_scodec_tas2781_i2c: DSP firmware silently fails to load at cold boot (Lenovo Yoga Pro 9 16IMH9) Rámon van Raaij
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox