* ASoC: tas2783-sdw: no stereo channel split for two mono amps -> mono output (AMD ACP SoundWire, ASUS ProArt PX13)
@ 2026-07-19 0:53 Antoine Monnet
2026-07-27 8:48 ` Andrey Golovko
0 siblings, 1 reply; 5+ messages in thread
From: Antoine Monnet @ 2026-07-19 0:53 UTC (permalink / raw)
To: linux-sound
Cc: Shenghao Ding, Kevin Lu, Baojun Xu, Mark Brown, Liam Girdwood,
Vijendar Mukunda, Vinod Koul, Bard Liao, Pierre-Louis Bossart,
linux-kernel
Hi,
On an ASUS ProArt PX13 (HN7306) with two mono TAS2783 SoundWire
smart-amps on a
single AMD ACP SoundWire link, playback is effectively mono: only one amp
produces sound and it plays the *left* channel out of the *right*
speaker; the
right channel and the other (left) speaker are silent. Both amps enumerate,
load firmware, and stream without any error.
Environment
-----------
Machine : ASUS ProArt PX13, HN7306 (Ryzen AI Max, "Strix Halo")
Distro : Debian 13 (trixie)
Kernel : 7.0.13+deb13-amd64 (Debian 7.0.13-1~bpo13+1); code identical in
7.1.3
ACP : 0000:c4:00.5 [1022:15e2] rev 70, subsystem ASUSTeK [1043:1714],
driver snd_pci_ps
Amps : 2x TAS2783, SoundWire link 1, slaves 0x8 and 0xB
(sdw:0:1:0102:0000:01:8, sdw:0:1:0102:0000:01:b)
Note on the build: Debian ships sound/soc/codecs/tas2783-sdw.c but does
not set
CONFIG_SND_SOC_TAS2783_SDW, so this is the unmodified in-tree driver
built as an
out-of-tree module (DKMS). The relevant code is unchanged in current
mainline
(verified against 7.1.3).
Analysis
--------
In tas_sdw_hw_params() the driver does:
snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
port_config.num = 1;
else
port_config.num = 2;
sdw_stream_add_slave(sdw_peripheral, &stream_config, &port_config, 1,
sdw_stream);
snd_sdw_params_to_config() sets port_config.ch_mask to cover all stream
channels
(0x3 for a 2-channel stream). This runs per-amp, so *both* mono amps are
added
to the stream with ch_mask = 0x3 and neither is told which channel to
reproduce.
There is no per-amp channel assignment anywhere in the driver (the only
other
ch_mask use is the port-prep write of whatever mask was handed in). The
result
on this board is mono out of a single amp.
Diagnosis on the hardware (per-amp mute + isolated-channel tests) confirmed:
only one amp (tas2783-2, the right speaker) outputs, playing the left
channel;
the other amp (left speaker) is silent regardless of content; no
"without fw"
or port/prepare errors on either slave.
Fix
---
Assign a single SoundWire channel per amp, e.g. right after
snd_sdw_params_to_config():
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
params_channels(params) == 2) {
switch (tas_dev->sdw_peripheral->id.unique_id) {
case 0x8: port_config.ch_mask = 0x1; break; /* left */
case 0xB: port_config.ch_mask = 0x2; break; /* right */
}
}
With this the two speakers are correctly driven:
speaker-test -Dpipewire -c2 -s1 -> left speaker
speaker-test -Dpipewire -c2 -s2 -> right speaker
The unique_id -> L/R mapping is board-specific, so a proper fix should
derive
the per-amp channel assignment from firmware/ACPI (or a machine quirk table)
rather than hard-coded SoundWire addresses. Filing this so the driver
grows a
real mechanism for multi-amp channel mapping.
Reproduce
---------
1. Boot; internal speakers play but only one speaker is audible.
2. speaker-test -Dpipewire -c2 -s1 -> wrong/only-one speaker
speaker-test -Dpipewire -c2 -s2 -> silent
3. With the per-amp ch_mask patch above, both speakers work.
Happy to test patches on this hardware.
Thanks,
Antoine
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: ASoC: tas2783-sdw: no stereo channel split for two mono amps -> mono output (AMD ACP SoundWire, ASUS ProArt PX13)
2026-07-19 0:53 ASoC: tas2783-sdw: no stereo channel split for two mono amps -> mono output (AMD ACP SoundWire, ASUS ProArt PX13) Antoine Monnet
@ 2026-07-27 8:48 ` Andrey Golovko
2026-07-27 11:21 ` Antoine Monnet
0 siblings, 1 reply; 5+ messages in thread
From: Andrey Golovko @ 2026-07-27 8:48 UTC (permalink / raw)
To: Antoine Monnet, linux-sound
Cc: Shenghao Ding, Kevin Lu, Baojun Xu, Sen Wang, Mark Brown,
Liam Girdwood, Vijendar Mukunda, Vinod Koul, Bard Liao,
Pierre-Louis Bossart, linux-kernel
Hi Antoine,
Confirmed on a second machine: ASUS ProArt PX13 HN7306EAC (Ryzen AI MAX+
395), Ubuntu 26.04, 7.2-rc4 based kernel, same two TAS2783 at unique_id
0x8 / 0xB plus RT721 on SoundWire link 1.
Rather than judging by ear, I used the per-amp mixer controls, which
makes the result unambiguous. Both amps at full scale ('tas2783-1/-2 Amp
Volume' = 20, 'Speaker Volume' = 200), all four 'Left/Right Spk[2]
Switch' on:
speaker-test -Dpipewire -c2 -s1 (left channel only)
- audible
- 'tas2783-2 Speaker Volume' = 0 -> complete silence
- 'tas2783-1 Speaker Volume' = 0 -> no audible change
speaker-test -Dpipewire -c2 -s2 (right channel only)
- silent, with both amps unmuted at full scale
So exactly your picture: one amp (tas2783-2) renders audio and it renders
the *left* channel, the other amp contributes nothing, and the right
channel is never reproduced. Both amps do load their own per-address
blob here (1714-1-8.bin / 1714-1-B.bin, via the fallback naming path
after the 0x-prefixed names miss), so this is not a case of the wrong
configuration being downloaded.
On the "proper mechanism" question: a good part of the plumbing already
exists, and it does not need unique_id at all.
- The machine layer already knows which amp is which. In
sound/soc/sdw_utils/soc_sdw_ti_amp.c, asoc_sdw_ti_spk_rtd_init() maps
the component name prefix to a speaker widget: tas2783-1 -> "Left
Spk", tas2783-2 -> "Right Spk", tas2783-3/-4 -> "Left/Right Spk2".
That is where the four 'Left/Right Spk[2] Switch' controls on this
board come from.
- asoc_sdw_hw_params() (sound/soc/sdw_utils/soc_sdw_utils.c) fills
dai_link->ch_maps, but for playback it deliberately hands every codec
the full mask ("Identical data will be sent to all codecs in
playback"), leaving the per-amp channel selection to the amp itself.
acp-sdw-legacy-mach, which drives this board, uses both.
So the driver could derive the channel from the same prefix index the
DAPM routing already uses, or from its entry in dai_link->ch_maps,
instead of hard-coding SoundWire addresses.
Which raises the question for TI: on TAS2783 is the channel selection
meant to come from the per-device .bin (in which case it is evidently
not taking effect on this board), or is the driver expected to program a
per-amp channel mask? Depending on the answer, either the firmware
description or tas_sdw_hw_params() needs fixing - and in the latter case
the amp's channel should come from the machine-level mapping rather than
from unique_id, which as you say is board-specific.
Happy to test patches on this hardware; I can also collect register
dumps from both amps if that helps.
Thanks,
Andrey
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: ASoC: tas2783-sdw: no stereo channel split for two mono amps -> mono output (AMD ACP SoundWire, ASUS ProArt PX13)
2026-07-27 8:48 ` Andrey Golovko
@ 2026-07-27 11:21 ` Antoine Monnet
2026-08-07 5:21 ` Andrey Golovko
0 siblings, 1 reply; 5+ messages in thread
From: Antoine Monnet @ 2026-07-27 11:21 UTC (permalink / raw)
To: Andrey Golovko, linux-sound
Cc: Shenghao Ding, Kevin Lu, Baojun Xu, Sen Wang, Mark Brown,
Liam Girdwood, Vijendar Mukunda, Vinod Koul, Bard Liao,
Pierre-Louis Bossart, linux-kernel
(Apologies — I sent that from the wrong identity.
monnet.antoine@gmail.com and antoine@montane.tech are the same person)
Hi Andrey,
Thanks for the second-machine confirmation and for spelling out the
mechanism - I went with exactly your suggestion and keyed the channel off
the machine-assigned component prefix rather than unique_id.
I logged name_prefix against the resulting ch_mask and got tas2783-1 = 0x8
= left, tas2783-2 = 0xb = right; the split is correct by ear and by per-amp
mixer mute. Since your unit is the same HN7306EAC the prefix ordering is
identical, so this should give correct L/R for you too - a Tested-by from
the second machine would be welcome if you get a chance, but nothing needs
re-deriving.
On the ch_maps alternative you raised: since asoc_sdw_hw_params() hands
every codec the full mask for playback, dai_link->ch_maps doesn't carry a
per-amp selection there, so I took the prefix-index route. Happy to switch
to ch_maps if the machine layer is taught to fill per-amp maps first -
that's really the TI question below.
Patch below, against broonie/sound for-next.
sound/soc/codecs/tas2783-sdw.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c
--- a/sound/soc/codecs/tas2783-sdw.c
+++ b/sound/soc/codecs/tas2783-sdw.c
@@ -216,6 +216,24 @@ static s32 tas_sdw_hw_params(struct
snd_pcm_substream *substream,
/* SoundWire specific configuration */
snd_sdw_params_to_config(substream, params,
&stream_config, &port_config);
+
+ /*
+ * The two mono amps each render one channel of the stereo stream:
+ * snd_sdw_params_to_config() hands every codec the full mask for
+ * playback, so without a per-amp channel selection only one amp would
+ * output. Derive the channel from the machine-assigned component
+ * prefix rather than the SoundWire address (which is board-specific):
+ * soc_sdw_ti_amp.c maps tas2783-1/-3 to the Left speaker and
+ * tas2783-2/-4 to the Right, so odd-indexed amps take the left
+ * channel and even-indexed the right.
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+ params_channels(params) == 2 && component->name_prefix) {
+ const char *idx_str = strrchr(component->name_prefix, '-');
+ unsigned long idx;
+
+ if (idx_str && !kstrtoul(idx_str + 1, 10, &idx) && idx)
+ port_config.ch_mask = (idx & 1) ? BIT(0) : BIT(1);
+ }
+
/* port 1 for playback */
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
port_config.num = 1;
--
2.53.0
On 7/27/26 10:48, Andrey Golovko wrote:
> Hi Antoine,
>
> Confirmed on a second machine: ASUS ProArt PX13 HN7306EAC (Ryzen AI MAX+
> 395), Ubuntu 26.04, 7.2-rc4 based kernel, same two TAS2783 at unique_id
> 0x8 / 0xB plus RT721 on SoundWire link 1.
>
> Rather than judging by ear, I used the per-amp mixer controls, which
> makes the result unambiguous. Both amps at full scale ('tas2783-1/-2 Amp
> Volume' = 20, 'Speaker Volume' = 200), all four 'Left/Right Spk[2]
> Switch' on:
>
> speaker-test -Dpipewire -c2 -s1 (left channel only)
> - audible
> - 'tas2783-2 Speaker Volume' = 0 -> complete silence
> - 'tas2783-1 Speaker Volume' = 0 -> no audible change
>
> speaker-test -Dpipewire -c2 -s2 (right channel only)
> - silent, with both amps unmuted at full scale
>
> So exactly your picture: one amp (tas2783-2) renders audio and it renders
> the *left* channel, the other amp contributes nothing, and the right
> channel is never reproduced. Both amps do load their own per-address
> blob here (1714-1-8.bin / 1714-1-B.bin, via the fallback naming path
> after the 0x-prefixed names miss), so this is not a case of the wrong
> configuration being downloaded.
>
> On the "proper mechanism" question: a good part of the plumbing already
> exists, and it does not need unique_id at all.
>
> - The machine layer already knows which amp is which. In
> sound/soc/sdw_utils/soc_sdw_ti_amp.c, asoc_sdw_ti_spk_rtd_init() maps
> the component name prefix to a speaker widget: tas2783-1 -> "Left
> Spk", tas2783-2 -> "Right Spk", tas2783-3/-4 -> "Left/Right Spk2".
> That is where the four 'Left/Right Spk[2] Switch' controls on this
> board come from.
>
> - asoc_sdw_hw_params() (sound/soc/sdw_utils/soc_sdw_utils.c) fills
> dai_link->ch_maps, but for playback it deliberately hands every codec
> the full mask ("Identical data will be sent to all codecs in
> playback"), leaving the per-amp channel selection to the amp itself.
> acp-sdw-legacy-mach, which drives this board, uses both.
>
> So the driver could derive the channel from the same prefix index the
> DAPM routing already uses, or from its entry in dai_link->ch_maps,
> instead of hard-coding SoundWire addresses.
>
> Which raises the question for TI: on TAS2783 is the channel selection
> meant to come from the per-device .bin (in which case it is evidently
> not taking effect on this board), or is the driver expected to program a
> per-amp channel mask? Depending on the answer, either the firmware
> description or tas_sdw_hw_params() needs fixing - and in the latter case
> the amp's channel should come from the machine-level mapping rather than
> from unique_id, which as you say is board-specific.
>
> Happy to test patches on this hardware; I can also collect register
> dumps from both amps if that helps.
>
> Thanks,
> Andrey
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: ASoC: tas2783-sdw: no stereo channel split for two mono amps -> mono output (AMD ACP SoundWire, ASUS ProArt PX13)
2026-07-27 11:21 ` Antoine Monnet
@ 2026-08-07 5:21 ` Andrey Golovko
2026-08-07 9:19 ` Pierre-Louis Bossart
0 siblings, 1 reply; 5+ messages in thread
From: Andrey Golovko @ 2026-08-07 5:21 UTC (permalink / raw)
To: Antoine Monnet, linux-sound
Cc: Shenghao Ding, Kevin Lu, Baojun Xu, Sen Wang, Mark Brown,
Liam Girdwood, Vijendar Mukunda, Vinod Koul, Bard Liao,
Pierre-Louis Bossart, Robin Everaars, linux-kernel
On 7/27/26 13:21, Antoine Monnet wrote:
> I logged name_prefix against the resulting ch_mask and got tas2783-1 = 0x8
> = left, tas2783-2 = 0xb = right; the split is correct by ear and by per-amp
> mixer mute. Since your unit is the same HN7306EAC the prefix ordering is
> identical, so this should give correct L/R for you too - a Tested-by from
> the second machine would be welcome if you get a chance, but nothing needs
> re-deriving.
Tested-by: Andrey Golovko <andrey@golovko.me>
Confirmed on the second machine: ASUS ProArt PX13 HN7306EAC, v7.2-rc4 based
kernel, same two TAS2783 at unique_id 0x8 / 0xB plus RT721 on SoundWire
link 1.
With your patch applied both amps render, and the assignment is correct by
physical placement, not merely by control name:
speaker-test -Dpipewire -c2 -s1 -> physically left speaker only
speaker-test -Dpipewire -c2 -s2 -> physically right speaker only
Both amps at full scale, all four Left/Right Spk[2] switches on. Before the
patch, -s1 was audible and -s2 was silent, with only one amp contributing at
all - so this is exactly the behaviour you predicted for an identical
HN7306EAC: the prefix ordering matches, tas2783-1 takes the left channel and
tas2783-2 the right, and that lines up with the physical speakers.
One caveat, measured since by Robin Everaars, who reported the same
issue on yet another HN7306EAC (added to Cc):
https://lore.kernel.org/all/20260805183517.8665-1-robineveraars@pm.me/
The ch_mask *value* is not what selects the channel:
sdw_compute_slave_ports() assigns payload offsets positionally along
slave_rt_list, and Robin showed that inverting the two masks between the
amps does not move the audio. A one-channel mask fixes mono because it
stops the mirror-mode offset reset; L/R then follows the codec order in
the DAI link, which on this platform happens to match the speakers. The
Tested-by stands - the split the patch produces is correct on both
machines - but the name_prefix -> BIT(n) mapping reads as if the bit
picks the channel, which is worth a comment or a reword before a formal
submission. Where the binding should really come from is being discussed
in Robin's thread.
Thanks,
Andrey
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: ASoC: tas2783-sdw: no stereo channel split for two mono amps -> mono output (AMD ACP SoundWire, ASUS ProArt PX13)
2026-08-07 5:21 ` Andrey Golovko
@ 2026-08-07 9:19 ` Pierre-Louis Bossart
0 siblings, 0 replies; 5+ messages in thread
From: Pierre-Louis Bossart @ 2026-08-07 9:19 UTC (permalink / raw)
To: Andrey Golovko, Antoine Monnet, linux-sound
Cc: Shenghao Ding, Kevin Lu, Baojun Xu, Sen Wang, Mark Brown,
Liam Girdwood, Vijendar Mukunda, Vinod Koul, Bard Liao,
Robin Everaars, linux-kernel
On 8/7/26 07:21, Andrey Golovko wrote:
> On 7/27/26 13:21, Antoine Monnet wrote:
>> I logged name_prefix against the resulting ch_mask and got tas2783-1 = 0x8
>> = left, tas2783-2 = 0xb = right; the split is correct by ear and by per-amp
>> mixer mute. Since your unit is the same HN7306EAC the prefix ordering is
>> identical, so this should give correct L/R for you too - a Tested-by from
>> the second machine would be welcome if you get a chance, but nothing needs
>> re-deriving.
>
> Tested-by: Andrey Golovko <andrey@golovko.me>
>
> Confirmed on the second machine: ASUS ProArt PX13 HN7306EAC, v7.2-rc4 based
> kernel, same two TAS2783 at unique_id 0x8 / 0xB plus RT721 on SoundWire
> link 1.
>
> With your patch applied both amps render, and the assignment is correct by
> physical placement, not merely by control name:
>
> speaker-test -Dpipewire -c2 -s1 -> physically left speaker only
> speaker-test -Dpipewire -c2 -s2 -> physically right speaker only
>
> Both amps at full scale, all four Left/Right Spk[2] switches on. Before the
> patch, -s1 was audible and -s2 was silent, with only one amp contributing at
> all - so this is exactly the behaviour you predicted for an identical
> HN7306EAC: the prefix ordering matches, tas2783-1 takes the left channel and
> tas2783-2 the right, and that lines up with the physical speakers.
>
> One caveat, measured since by Robin Everaars, who reported the same
> issue on yet another HN7306EAC (added to Cc):
>
> https://lore.kernel.org/all/20260805183517.8665-1-robineveraars@pm.me/
>
> The ch_mask *value* is not what selects the channel:
> sdw_compute_slave_ports() assigns payload offsets positionally along
> slave_rt_list, and Robin showed that inverting the two masks between the
> amps does not move the audio. A one-channel mask fixes mono because it
> stops the mirror-mode offset reset; L/R then follows the codec order in
> the DAI link, which on this platform happens to match the speakers. The
> Tested-by stands - the split the patch produces is correct on both
> machines - but the name_prefix -> BIT(n) mapping reads as if the bit
> picks the channel, which is worth a comment or a reword before a formal
> submission. Where the binding should really come from is being discussed
> in Robin's thread.
Selecting a mono channel at the port level is indeed a solution but not
quite what SDCA designs are supposed to implement. As discussed in the
other thread, all amps are supposed to receive *all* channels and use
mapping/processing to generate the per-amp desired output. That
generation can be dynamic depending on 'posture'.
Looking at the SmartAmp topology in the SDCA spec, my guess would be
that the mapping is really handled in the UDMPU23 and FU 23 entities.
The topology has a 'Amp Transducer Mapping' label which seems to be the
right location to make the left/right speaker selection.
FU23 looks like a dead-end, it is a 'platform' FU not supposed to be
modified by the class driver. The Channel Gain is a platform-specific
value set with OEM defaults, the drivers shouldn't muck with those values.
The UDMPU23 has a 'ClusterIndex' control at offset 0x10, it's not
exposed to userspace but is part of regmap
case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_UDMPU23,
TAS2783_SDCA_CTL_UDMPU_CLUSTER, 0):
It might be worth experimenting with different non-zero cluster indices
for each amp. If the index is zero then the channel selection is
bypassed, whatever is provided to the UDMPU input pin will flow on the
output.
Unfortunately the cluster index is not a direct representation of the
channel selection, it points to a table in which the channel selection
is defined. Nothing requires that the indices be continuous, and that
table can in theory be platform-specific...
One would hope that the codec vendor has guidelines on which cluster
uses what index... Someone at TI should really comment on whether this
UDMPU cluster selection is the correct direction, as usual the caveat
that such experiments might blow the speakers...
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-08-07 9:21 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-19 0:53 ASoC: tas2783-sdw: no stereo channel split for two mono amps -> mono output (AMD ACP SoundWire, ASUS ProArt PX13) Antoine Monnet
2026-07-27 8:48 ` Andrey Golovko
2026-07-27 11:21 ` Antoine Monnet
2026-08-07 5:21 ` Andrey Golovko
2026-08-07 9:19 ` Pierre-Louis Bossart
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox