Out of the box the ProArt PX13 plays audio fine over HDMI but the internal speakers are completely silent — PipeWire only ever shows a generic “Dummy Output” sink. Getting real, persistent sound out of this laptop turned out to require fixing three independent bugs stacked on top of each other: a missing calibration firmware blob, a missing ALSA UCM profile (plus a broken UCM control reference), and a SoundWire/ACP controller that doesn’t survive boot or suspend/resume cleanly.
This post walks through the actual root cause of each layer and the fix that was applied for each one. All of the code, configs, systemd units and calibration firmware are published at github.com/brainchillz/asus-proart-px13-linux-speaker-fix.
The starting symptom
aplay -l and wpctl status showed two cards:
card 0: HDMI (Generic HD-Audio) -> works fine
card 1: amd-soundwire (rt721-sdca codec + 2x TAS2783 smart amp) -> speakers, no sound
PipeWire’s default sink was a synthetic “Dummy Output” — the telltale sign that PipeWire’s ALSA card profile (ACP) couldn’t find any usable playback device on card 1 at all.
Bug #1: Missing TAS2783 calibration firmware
Root cause
The internal speakers are driven by two TI TAS2783 SoundWire “smart amp” chips. Smart amps like this don’t just amplify — they run a DSP model of the speaker (impedance, thermal limits, excursion protection) that has to be calibrated per laptop chassis. The kernel’s slave-tas2783 SoundWire driver loads this calibration data as a firmware blob, and it derives the filename from the PCI subsystem ID, the SoundWire link number, and the slave’s bus address:
<subsystem-id>-<sdw-link>-<sdw-addr>.bin
# For this laptop (ASUS subsystem ID 0x1714, SoundWire link 1,
# slave addresses 0x8 and 0xB):
1714-1-8.bin
1714-1-B.bin
linux-firmware (as of mid-2026) only ships TAS2783 calibration blobs for a handful of HP/Dell subsystem IDs (8E86, 8E87, 8EF5, 8EF6, etc.). There is no ASUS 1714 blob upstream at all. The result, visible in journalctl -k -b 0 | grep -i tas2783:
slave-tas2783 sdw:0:1:0102:0000:01:8: Direct firmware load for 1714-1-8.bin failed with error -2
slave-tas2783 sdw:0:1:0102:0000:01:8: Failed to read fw binary 1714-1-8.bin
slave-tas2783 sdw:0:1:0102:0000:01:8: error playback without fw download
slave-tas2783 sdw:0:1:0102:0000:01:8: ASoC error (-22): at snd_soc_dai_hw_params() on tas2783-codec
SDW1-PIN1-PLAYBACK-SmartAmp: ASoC error (-22): at __soc_pcm_hw_params() on SDW1-PIN1-PLAYBACK-SmartAmp
Without the calibration blob, the amp’s DSP never finishes initializing, so hw_params() on the SmartAmp playback DAI fails outright (-22 EINVAL) and the amp never produces audio. This is entirely independent of ALSA mixer levels, mute state, or PipeWire configuration — the amp simply refuses to play anything.
The fix
The calibration blobs do exist, just not in linux-firmware — they ship inside ASUS’s official Windows “TI SmartAMP” driver package for this laptop model. The package is an InstallAware self-extracting EXE (not Inno Setup, so innoextract doesn’t work on it). The blobs were recovered by running the installer under wine64 and snapshotting its self-extraction temp directory:
Firmwares/1714-1-0x8.bin -> 1714-1-8.bin
Firmwares/1714-1-0xB.bin -> 1714-1-B.bin
The fix script installs each blob to both of the paths the kernel driver looks in (the generic firmware search path and the TAS2783-specific subdirectory):
/lib/firmware/1714-1-8.bin
/lib/firmware/1714-1-B.bin
/lib/firmware/ti/audio/tas2783/1714-1-8.bin
/lib/firmware/ti/audio/tas2783/1714-1-B.bin
From fix-px13-audio.sh:
echo "==> Installing TAS2783 calibration firmware..."
sudo install -m 644 "$SCRIPT_DIR/firmware/1714-1-8.bin" /lib/firmware/1714-1-8.bin
sudo install -m 644 "$SCRIPT_DIR/firmware/1714-1-B.bin" /lib/firmware/1714-1-B.bin
sudo install -d -m 755 /lib/firmware/ti/audio/tas2783
sudo install -m 644 "$SCRIPT_DIR/firmware/1714-1-8.bin" /lib/firmware/ti/audio/tas2783/1714-1-8.bin
sudo install -m 644 "$SCRIPT_DIR/firmware/1714-1-B.bin" /lib/firmware/ti/audio/tas2783/1714-1-B.bin
These blobs are TI/ASUS proprietary calibration data extracted from ASUS’s own Windows driver package for this exact model. The fix repo is intentionally private about the binaries and they are not redistributed publicly — only the scripts and configs that consume them.
After installing the firmware and re-binding the slave-tas2783 driver (echo <dev> > /sys/bus/soundwire/drivers/slave-tas2783/{unbind,bind}), the “Direct firmware load … failed” / “error playback without fw download” messages disappeared. Bug #1 fixed — but the speakers still produced no sound, because of bug #2.
Bug #2: No ALSA UCM “Speaker” device, and a broken control reference
Even with the firmware loading cleanly and raw ALSA playback to hw:1,2 (the TAS2783 SmartAmp PCM) working, PipeWire still only exposed “Dummy Output”. This turned out to be two separate UCM (Use Case Manager) bugs.
2a. Root cause: no upstream UCM profile for tas2783
PipeWire’s ALSA Card Profile (ACP) module decides what sinks/sources to expose for a card by reading its UCM2 configuration (/usr/share/alsa/ucm2/...). For SOF SoundWire cards, the generic profile at conf.d/amd-soundwire/amd-soundwire.conf / sof-soundwire/HiFi.conf derives which devices exist by parsing the kernel’s alsa.components string for this card, which for this machine is:
cfg-amp:2 hs:rt721
HiFi.conf only adds a "Speaker" SectionDevice if it finds a spk:<codec> token in that string via its SpeakerCodec DefineRegex. This machine’s components string has cfg-amp:2 (two amps) but no spk: token — and even if it did, there is no tas2783.conf codec profile anywhere in upstream alsa-ucm-conf, for any machine. So SpeakerCodec1 resolves to empty, no Speaker device is ever defined, and PipeWire’s ACP only ever sees the rt721 codec’s “Headphones” and “Headset Microphone” devices — never the internal speaker amp.
2a. Fix: a local tas2783 UCM profile + a card-specific override
Two new UCM files were added (neither is part of any package, so a future alsa-ucm-conf upgrade that overwrites /usr/share/alsa/ucm2 could remove them). First, /usr/share/alsa/ucm2/sof-soundwire/tas2783.conf — a minimal Speaker device pointing at PCM device 2 (the SmartAmp playback PCM, confirmed working via aplay -D hw:1,2):
# Speaker device for the TI TAS2783 SoundWire smart amp, used on
# (at least) the ASUS ProArt PX13 (HN7306, Strix Halo). Upstream
# alsa-ucm-conf has no codec profile for tas2783 yet, so this is a
# minimal local addition. PCM device 2 is the SmartAmp playback
# device exposed by the snd_acp_sdw_legacy_mach machine driver.
SectionDevice."Speaker" {
Comment "Speaker"
Value {
PlaybackPriority 100
PlaybackPCM "hw:${CardId},2"
PlaybackChannels 2
}
}
Second, a card-long-name-specific override at /usr/share/alsa/ucm2/conf.d/amd-soundwire/ASUSTeKCOMPUTERINC.-ProArtPX13HN7306EAC-1.0-HN7306EAC.conf. This file takes priority over the generic amd-soundwire.conf for this exact card long-name. It’s a full copy of the generic file with one line appended after all the existing multi-codec If blocks, so it doesn’t disturb the working rt721 headset/mic logic, but still forces the Speaker device in:
# --- Local addition for this machine ---
# alsa.components for this card ("cfg-amp:2 hs:rt721") does not include
# a " spk:<codec>" token, so SpeakerCodec1 above resolves to "" and the
# generic sof-soundwire HiFi profile never adds a "Speaker" device. This
# machine has a TAS2783 SoundWire smart amp on PCM device 2 (confirmed
# via `aplay -D hw:1,2`). Force-select our local tas2783.conf, which adds
# a minimal "Speaker" SectionDevice. This override is applied after the
# multi-codec If blocks above so it doesn't disturb the rt721 headset
# handling.
Define.SpeakerCodec1 "tas2783"
After installing these and restarting PipeWire/WirePlumber, a new sink “Audio Coprocessor Speaker” appeared… but speaker-test against it produced no audible sound, exit code 0, no errors. That led to bug 2b.
2b. Root cause: disdevall chokes on a “Headphone Switch” control that doesn’t exist
Investigation with strace on alsaucm -c amd-soundwire set _verb HiFi showed it failing outright:
ALSA lib main.c:843:(execute_sequence) [error.ucm] unable to execute cset 'name='Headphone Switch' off'
ALSA lib main.c:2863:(set_verb_user) [error.ucm] failed to initialize new use case: HiFi
alsaucm: error failed to set _verb=HiFi: No such file or directory
The chain of causes here is subtle:
HiFi.conf’sIf.multicodecblock includessof-soundwire/rt721.confbecause, at the point itsDefineRegexis evaluated,${var:HeadsetCodec1}is"rt721"— even thoughIf.multi_headsetlater resetsHeadsetCodec1to"". The include decision is made before that reset takes effect.rt721.confhas an unconditionalSectionDevice."Headphones"whoseEnable/DisableSequenceruncset "name='Headphone Switch' ...".HiFi.conf’sSectionVerbEnableSequencerunsdisdevall ""on every_verb HiFiactivation — i.e. it tries to run theDisableSequenceof every defined device, including Headphones.- But this particular rt721-sdca codec/driver combination doesn’t expose a control literally named “Headphone Switch”.
amixer -c1 controlsshows only 19 controls total — things likert721 FU05/FU06 Playback Volumeand the tas2783 Amp/Speaker Volume controls, but no Headphone mute switch of any kind.
So disdevall tries to run a cset against a non-existent control, that single cset failure aborts the entire set _verb HiFi call, and PipeWire/ACP — unable to activate the HiFi UCM verb at all — silently falls back to its generic, built-in “stereo-fallback” profile. That profile plays to hw:1 (the raw card default, i.e. the rt721 codec path), not hw:1,2 (the TAS2783 SmartAmp). Hence: speaker-test runs cleanly and reports success, but the audio goes to a PCM the smart amp never sees — silence.
Left here because they’re instructive about how UCM resolves includes: Adding a SectionDevice."Headphones" override directly in the card-specific conf.d file → error: "unknown master file field SectionDevice" — SectionDevice is only valid inside a UseCase file, not a master conf.d file. Defining Define.MultiCodec1 "rt721-px13" to redirect HiFi.conf’s multicodec include to a forked rt721-px13.conf → had no effect. ${var:MultiCodec1} is resolved once at DefineRegex-evaluation time and isn’t retroactively changed by a later Define. statement; strace confirmed the original rt721.conf was still the file opened.
2b. Fix: patch rt721.conf directly
The fix that actually worked was to directly patch /usr/share/alsa/ucm2/sof-soundwire/rt721.conf and empty out SectionDevice."Headphones"’s Enable/DisableSequence blocks (leaving its Value — routing, jack control, priority — untouched):
SectionDevice."Headphones" {
Comment "Headphones"
# --- Local change for ASUS ProArt PX13 (HN7306) ---
# This rt721-sdca codec/driver does not expose a "Headphone Switch"
# control (only "rt721 FU05/FU06 Playback Volume", no mute switches -
# see `amixer -c1 controls`). HiFi.conf's SectionVerb EnableSequence
# runs "disdevall" (disable all devices) on verb activation, so the
# missing control made `alsaucm set _verb HiFi` fail entirely, which
# made PipeWire/ACP fall back to "stereo-fallback" instead of using
# the local "Speaker" device (see tas2783.conf). Emptied below;
# Value/routing unchanged. If a future alsa-ucm-conf update overwrites
# this file and speakers go silent again, re-apply this change.
EnableSequence [
]
DisableSequence [
]
Value {
PlaybackPriority 200
PlaybackPCM "hw:${CardId}"
JackControl "Headphone Jack"
}
}
With this patch, alsaucm -c amd-soundwire set _verb HiFi exits 0. After restarting PipeWire/WirePlumber, wpctl status showed all three expected devices for the first time:
Sinks:
"Audio Coprocessor Speaker" (id 58)
"Audio Coprocessor Headphones" (id 59, default - higher PlaybackPriority)
Sources:
"Audio Coprocessor Headset Microphone" (id 60)
speaker-test -D pipewire -c 2 -t sine -f 440 -l 1 against the Speaker sink was audibly confirmed working. Bugs #1 and #2 fixed — but only for this boot.
Bug #3: The ACP/SoundWire controller doesn’t survive boot or resume
Everything above gets the speakers working for the boot they’re applied on. But two persistence problems remained:
- On cold boot, the TAS2783 firmware download for SoundWire slave
01:bintermittently fails with an SDW bus timeout (FW download failed: -110), even though the firmware file is present and correct. - On s2idle suspend/resume, the entire
amd-soundwireALSA card (card 1) disappears — not just the amps — and never comes back on its own.
3a. Cold-boot -110 firmware download timeout
With the firmware files in place, a cold boot’s journalctl -k -b 0 sometimes showed (for slave 01:b only):
soundwire sdw-master-0-1: trf on Slave 1 failed:-110 write addr 8088 count 32632
slave-tas2783 sdw:0:1:0102:0000:01:b: FW download failed: -110
slave-tas2783 sdw:0:1:0102:0000:01:b: fw with no files
slave-tas2783 sdw:0:1:0102:0000:01:b: error playback without fw download
slave-tas2783 sdw:0:1:0102:0000:01:b: ASoC error (-22): at snd_soc_dai_hw_params() on tas2783-codec
-110 is ETIMEDOUT — a SoundWire bus write (the firmware download itself) timed out for slave 01:b during early boot. This is a timing/race issue in the SDW bus initialization, distinct from the missing-file problem in Bug #1 (the file is found and read; the transfer to the device times out). Re-running the same unbind/bind cycle from Bug #1 after boot reliably clears it.
3b. Resume root cause: the whole ACP/SoundWire controller dies on s2idle resume
This was the hardest of the three. After a suspend/resume cycle, audio reverted to “Dummy Output” with zero tas2783/SDW kernel messages on resume at all. /proc/asound/cards showed only the HDMI card — card 1 (amd-soundwire) was gone entirely. An earlier attempt at a fix used a systemd-sleep “post” hook that tried to unbind/rebind the individual slave-tas2783 SoundWire devices (the same trick from Bug #1/3a). That approach hung for ~90 seconds on every resume: unbinding a SoundWire slave blocks indefinitely when the parent bus controller itself is dead, which is exactly the post-resume state.
3b. Fix attempt 1: reset at the PCI level (right idea, wrong hook)
If the whole controller is dead, the fix is to reset the whole controller: unbind/rebind the parent AMD ACP PCI device, 0000:c4:00.5 (driver snd_pci_ps), rather than poking individual SoundWire slaves. This tears down and re-probes both SoundWire managers, both TAS2783 amps (re-downloading their calibration firmware fresh), and the rt721 codec — recreating card 1 from scratch, like a cold boot. Verified live against a genuinely broken post-resume state, this worked: card 1 reappeared and “Audio Coprocessor Speaker” came back as the default sink.
However, when wired up as the same kind of systemd-sleep “post” hook, it still hung — this time the PCI unbind blocked for ~90s before being killed, leaving the ACP device unbound and the card gone entirely. Same failure mode, one level up: any unbind/bind of this hardware done from inside the frozen systemd-sleep “post” hook blocks, because it runs while user.slice is frozen and the bus/PM state is still in its broken just-resumed condition.
3b. Fix attempt 2 (final): a post-resume systemd service, not a sleep hook
The working fix moves the PCI reset out of the systemd-sleep hook entirely and into a normal oneshot service ordered after the sleep targets complete — i.e. it runs once the system has fully resumed and user.slice has thawed, in system.slice, which is never frozen:
[Unit]
Description=PX13 audio fix after resume (reset AMD ACP/SoundWire, refresh PipeWire)
# Run AFTER the system has fully resumed AND thawed user.slice. The reset
# (PCI unbind/bind of the ACP) HANGS if attempted from inside the
# systemd-sleep "post" hook (it runs while the device PM/bus is still in the
# broken just-resumed state and user.slice is frozen - the unbind blocks for
# ~90s and gets killed, leaving the card gone). Ordering after the sleep
# targets means this runs once the resume is complete and the session is
# thawed, where the reset works reliably.
After=suspend.target hibernate.target hybrid-sleep.target suspend-then-hibernate.target
[Service]
Type=oneshot
# Small settle delay so the just-resumed ACP device PM has finished before
# we unbind/bind it.
ExecStartPre=/bin/sleep 3
ExecStart=/usr/local/sbin/px13-audio-fix.sh
[Install]
WantedBy=suspend.target hibernate.target hybrid-sleep.target suspend-then-hibernate.target
The actual reset script, px13-audio-fix.sh, does the PCI unbind/bind (wrapped in timeout 30 as defensive belt-and-braces), waits for the amd-soundwire card to reappear in /proc/asound/cards, then refreshes PipeWire/WirePlumber:
PCI_DEV="0000:c4:00.5"
PCI_DRV="/sys/bus/pci/drivers/snd_pci_ps"
CARD_MATCH="amd-soundwire"
pci_write() { timeout 30 sh -c "echo '$1' > '$2'" 2>/dev/null; }
if [ ! -d "$PCI_DRV" ]; then
log "snd_pci_ps driver not present, cannot reset - skipping"
elif [ ! -e "$PCI_DRV/$PCI_DEV" ]; then
log "PCI $PCI_DEV not currently bound to snd_pci_ps - attempting bind only"
pci_write "$PCI_DEV" "$PCI_DRV/bind" || log "bind failed/timed out"
else
log "unbinding PCI $PCI_DEV"
pci_write "$PCI_DEV" "$PCI_DRV/unbind" || log "unbind failed/timed out"
sleep 2
log "binding PCI $PCI_DEV"
pci_write "$PCI_DEV" "$PCI_DRV/bind" || log "bind failed/timed out"
fi
# --- Wait for the amd-soundwire card to reappear -----------------------------
for _ in $(seq 1 40); do
if grep -q "$CARD_MATCH" /proc/asound/cards 2>/dev/null; then
log "amd-soundwire card is present"
break
fi
sleep 0.25
done
The same script also runs at boot (via px13-audio-rebind.service, After=sound.target), which covers Bug 3a’s intermittent -110 timeout for slave 01:b, since the PCI-level reset re-downloads firmware to both amps from scratch.
Frozen-session safety
One more subtlety: px13-audio-fix.sh is shared between the boot service (runs in a normal, unfrozen session) and the resume service (runs after thaw, also normally unfrozen — but defensively checked anyway). Before calling systemctl --user restart for each logged-in user, it checks that user’s cgroup freezer state and skips the PipeWire restart if the session is still frozen, since a systemctl --user call against a frozen session would itself hang:
for uid in $(loginctl list-users --no-legend | awk '{print $1}'); do
runtime_dir="/run/user/$uid"
user_name="$(id -nu "$uid" 2>/dev/null)" || continue
[ -d "$runtime_dir" ] || continue
events="/sys/fs/cgroup/user.slice/user-${uid}.slice/cgroup.events"
if [ -r "$events" ] && grep -q "^frozen 1$" "$events" 2>/dev/null; then
log "uid $uid ($user_name) session frozen - skipping pipewire restart" \
"(udev will re-enumerate on thaw)"
continue
fi
log "restarting pipewire for uid $uid ($user_name)"
sudo -u "$user_name" XDG_RUNTIME_DIR="$runtime_dir" \
systemctl --user restart wireplumber pipewire pipewire-pulse 2>/dev/null \
|| log "pipewire restart for uid $uid failed"
done
Putting it all together
The final, verified-working setup installs:
| Component | Path | Purpose |
|---|---|---|
| Calibration firmware | /lib/firmware/1714-1-{8,B}.bin (+ ti/audio/tas2783/) | Lets the TAS2783 amps initialize (Bug #1) |
| UCM Speaker profile | /usr/share/alsa/ucm2/sof-soundwire/tas2783.conf + card override | Exposes the internal speaker to PipeWire (Bug #2a) |
rt721.conf patch | /usr/share/alsa/ucm2/sof-soundwire/rt721.conf | Removes the unsupported Headphone Switch cset so the HiFi verb activates (Bug #2b) |
px13-audio-fix.sh | /usr/local/sbin/ | Resets the ACP controller (PCI unbind/bind of 0000:c4:00.5) and refreshes PipeWire (Bug #3) |
px13-audio-rebind.service | /etc/systemd/system/ | Runs the reset at boot, After=sound.target (Bug #3a) |
px13-audio-resume.service | /etc/systemd/system/ | Runs the reset after resume, ordered after the sleep targets (Bug #3b) |
After running the install script, the verification commands are:
journalctl -k -b 0 | grep -i tas2783 # no firmware load errors
wpctl status # shows "Audio Coprocessor Speaker"
speaker-test -D pipewire -c 2 -t sine -f 440
A reboot and a suspend/resume cycle were both confirmed to keep the speakers working with no manual intervention.
What would make this unnecessary upstream
Each of the three bugs has a natural upstream home:
- linux-firmware: add the ASUS
1714-1-8.bin/1714-1-B.binTAS2783 calibration blobs (subject to ASUS/TI licensing — this is why the fix here extracts them from ASUS’s own Windows driver package instead). - alsa-ucm-conf: add a
tas2783.confSoundWire codec profile (modeled onrt1308.conf), plus logic so acfg-amp:Ncomponent (or an explicitspk:tas2783token from thesnd_acp_sdw_legacy_machmachine driver) implies a Speaker device even without a recognizedspk:codec. Separately,rt721.conf’sHeadphonesdevice shouldn’t reference aHeadphone Switchcontrol that this codec/driver combination doesn’t expose — ideally guarded withControlExists. - kernel / amd-sdw / snd_pci_ps: the AMD ACP/SoundWire controller should survive s2idle suspend/resume on its own, and the
slave-tas2783driver’s firmware download to slave01:bshouldn’t intermittently time out (-110) at cold boot.
Until then, the full set of firmware, UCM configs, scripts and systemd units described above is available at github.com/brainchillz/asus-proart-px13-linux-speaker-fix.