* [PATCH v3 0/3] ASoC: qcom: x1e80100: allow WSA channel map override
@ 2026-11-05 16:02 Abel Vesa
2026-11-05 16:02 ` [PATCH v3 1/3] ALSA: pcm: Add snd_pcm_add_named_chmap_ctls() for custom control names Abel Vesa
` (2 more replies)
0 siblings, 3 replies; 5+ messages in thread
From: Abel Vesa @ 2026-11-05 16:02 UTC (permalink / raw)
To: Srinivas Kandagatla, Liam Girdwood, Mark Brown, Jaroslav Kysela,
Takashi Iwai
Cc: Srinivas Kandagatla, linux-sound, linux-arm-msm, linux-kernel,
Abel Vesa
The x1e80100 machine driver currently derives the WSA RX slot map only
from the runtime channel count and programs that map unconditionally in
.prepare(). This means userspace cannot override the speaker channel map
from UCM.
Add a small ALSA core helper to register channel map controls under a
custom name (so the WSA maps are not confused with the DP/HDMI
"Playback Channel Map" controls on the same card), a common helper to
translate SNDRV_CHMAP_* positions into Q6 PCM_CHANNEL_* slots, and
writable "WSA Playback Channel Map" / "WSA2 Playback Channel Map"
controls on the WSA backend PCMs in the x1e80100 machine driver.
Signed-off-by: Abel Vesa <abel.vesa@oss.qualcomm.com>
---
Prepared with help from an LLM-based assistant.
Changes in v3:
- Rebased on next-20261105.
- Switch to the approach suggested by Srini on v1: add
snd_pcm_add_named_chmap_ctls() to the ALSA core and register the WSA
channel map controls on the backend PCMs from late_probe, instead of
card-level mixer controls with a hand-rolled TLV callback. The v2
claim that no_pcm backend links have no PCM instance was wrong; they
get an internal PCM from soc_new_pcm().
- Use a per-WSA-port index for the stored channel map instead of
indexing by AFE port.
- Link to v2: https://patch.msgid.link/20260724-b4-sound-qcom-x1e80100-allow-ucm-channel-map-v2-0-2ca3c1250018@oss.qualcomm.com
Changes in v2:
- Rebased on next-20260723.
- Return 1 from the control put callback when the map changes.
- Allow up to six WSA channels for Glymur.
- Use WSA-specific control names.
- Expose standard ALSA channel maps through TLV data.
- Move the SNDRV_CHMAP_* to Q6 PCM_CHANNEL_* conversion helper into
qcom common code.
- Register card-level controls because the WSA backend links are no_pcm
and have no PCM instance available at card init.
- Link to v1: https://patch.msgid.link/20260610-sound-qcom-x1e80100-allow-ucm-channel-map-v1-1-9339724994f5@oss.qualcomm.com
---
Abel Vesa (2):
ASoC: qcom: common: Add SNDRV channel map to Q6 slot helper
ASoC: qcom: x1e80100: Allow userspace WSA RX chmap override
Srinivas Kandagatla (1):
ALSA: pcm: Add snd_pcm_add_named_chmap_ctls() for custom control names
include/sound/pcm.h | 6 ++
sound/core/pcm_lib.c | 48 +++++++++---
sound/soc/qcom/common.c | 30 +++++++
sound/soc/qcom/common.h | 1 +
sound/soc/qcom/x1e80100.c | 193 +++++++++++++++++++++++++++++++++++++++++++++-
5 files changed, 264 insertions(+), 14 deletions(-)
---
base-commit: ddfe51b0a0f5dc9e415cc2c8ad8db17532dd426b
change-id: 20260724-b4-sound-qcom-x1e80100-allow-ucm-channel-map-5b9ecb27c766
Best regards,
--
Abel
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v3 1/3] ALSA: pcm: Add snd_pcm_add_named_chmap_ctls() for custom control names
2026-11-05 16:02 [PATCH v3 0/3] ASoC: qcom: x1e80100: allow WSA channel map override Abel Vesa
@ 2026-11-05 16:02 ` Abel Vesa
2026-11-05 16:02 ` [PATCH v3 2/3] ASoC: qcom: common: Add SNDRV channel map to Q6 slot helper Abel Vesa
2026-11-05 16:02 ` [PATCH v3 3/3] ASoC: qcom: x1e80100: Allow userspace WSA RX chmap override Abel Vesa
2 siblings, 0 replies; 5+ messages in thread
From: Abel Vesa @ 2026-11-05 16:02 UTC (permalink / raw)
To: Srinivas Kandagatla, Liam Girdwood, Mark Brown, Jaroslav Kysela,
Takashi Iwai
Cc: Srinivas Kandagatla, linux-sound, linux-arm-msm, linux-kernel,
Abel Vesa
From: Srinivas Kandagatla <srinivas.kandagatla@oss.qualcomm.com>
Add snd_pcm_add_named_chmap_ctls() to allow drivers to register channel
map controls with names other than the default "Playback Channel Map"
and "Capture Channel Map".
This is useful for cards exposing several channel map controls, such as
Qualcomm machine drivers with both HDMI/DP and WSA speaker backends,
where a distinct name makes it clear to userspace which backend the
control belongs to.
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@oss.qualcomm.com>
Signed-off-by: Abel Vesa <abel.vesa@oss.qualcomm.com>
---
include/sound/pcm.h | 6 ++++++
sound/core/pcm_lib.c | 48 ++++++++++++++++++++++++++++++++++++++----------
2 files changed, 44 insertions(+), 10 deletions(-)
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index 1cf511f8d880..58f5533a0315 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -1501,6 +1501,12 @@ extern const struct snd_pcm_chmap_elem snd_pcm_alt_chmaps[];
#define SND_PCM_CHMAP_MASK_246 (SND_PCM_CHMAP_MASK_24 | (1U << 6))
#define SND_PCM_CHMAP_MASK_2468 (SND_PCM_CHMAP_MASK_246 | (1U << 8))
+int snd_pcm_add_named_chmap_ctls(struct snd_pcm *pcm, int stream,
+ const char *name,
+ const struct snd_pcm_chmap_elem *chmap,
+ int max_channels,
+ unsigned long private_value,
+ struct snd_pcm_chmap **info_ret);
int snd_pcm_add_chmap_ctls(struct snd_pcm *pcm, int stream,
const struct snd_pcm_chmap_elem *chmap,
int max_channels,
diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
index bb16738bb591..d7d63f5109f4 100644
--- a/sound/core/pcm_lib.c
+++ b/sound/core/pcm_lib.c
@@ -2586,9 +2586,11 @@ static void pcm_chmap_ctl_private_free(struct snd_kcontrol *kcontrol)
}
/**
- * snd_pcm_add_chmap_ctls - create channel-mapping control elements
+ * snd_pcm_add_named_chmap_ctls - create channel-mapping control elements
+ * with a custom name
* @pcm: the assigned PCM instance
* @stream: stream direction
+ * @name: the control element name
* @chmap: channel map elements (for query)
* @max_channels: the max number of channels for the stream
* @private_value: the value passed to each kcontrol's private_value field
@@ -2597,11 +2599,12 @@ static void pcm_chmap_ctl_private_free(struct snd_kcontrol *kcontrol)
* Create channel-mapping control elements assigned to the given PCM stream(s).
* Return: Zero if successful, or a negative error value.
*/
-int snd_pcm_add_chmap_ctls(struct snd_pcm *pcm, int stream,
- const struct snd_pcm_chmap_elem *chmap,
- int max_channels,
- unsigned long private_value,
- struct snd_pcm_chmap **info_ret)
+int snd_pcm_add_named_chmap_ctls(struct snd_pcm *pcm, int stream,
+ const char *name,
+ const struct snd_pcm_chmap_elem *chmap,
+ int max_channels,
+ unsigned long private_value,
+ struct snd_pcm_chmap **info_ret)
{
struct snd_pcm_chmap *info;
struct snd_kcontrol_new knew = {
@@ -2625,10 +2628,7 @@ int snd_pcm_add_chmap_ctls(struct snd_pcm *pcm, int stream,
info->stream = stream;
info->chmap = chmap;
info->max_channels = max_channels;
- if (stream == SNDRV_PCM_STREAM_PLAYBACK)
- knew.name = "Playback Channel Map";
- else
- knew.name = "Capture Channel Map";
+ knew.name = name;
knew.device = pcm->device;
knew.count = pcm->streams[stream].substream_count;
knew.private_value = private_value;
@@ -2646,4 +2646,32 @@ int snd_pcm_add_chmap_ctls(struct snd_pcm *pcm, int stream,
*info_ret = info;
return 0;
}
+EXPORT_SYMBOL_GPL(snd_pcm_add_named_chmap_ctls);
+
+/**
+ * snd_pcm_add_chmap_ctls - create channel-mapping control elements
+ * @pcm: the assigned PCM instance
+ * @stream: stream direction
+ * @chmap: channel map elements (for query)
+ * @max_channels: the max number of channels for the stream
+ * @private_value: the value passed to each kcontrol's private_value field
+ * @info_ret: store struct snd_pcm_chmap instance if non-NULL
+ *
+ * Create channel-mapping control elements assigned to the given PCM stream(s),
+ * using the default "Playback Channel Map" / "Capture Channel Map" names.
+ * Return: Zero if successful, or a negative error value.
+ */
+int snd_pcm_add_chmap_ctls(struct snd_pcm *pcm, int stream,
+ const struct snd_pcm_chmap_elem *chmap,
+ int max_channels,
+ unsigned long private_value,
+ struct snd_pcm_chmap **info_ret)
+{
+ const char *name = stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ "Playback Channel Map" : "Capture Channel Map";
+
+ return snd_pcm_add_named_chmap_ctls(pcm, stream, name, chmap,
+ max_channels, private_value,
+ info_ret);
+}
EXPORT_SYMBOL_GPL(snd_pcm_add_chmap_ctls);
--
2.54.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v3 2/3] ASoC: qcom: common: Add SNDRV channel map to Q6 slot helper
2026-11-05 16:02 [PATCH v3 0/3] ASoC: qcom: x1e80100: allow WSA channel map override Abel Vesa
2026-11-05 16:02 ` [PATCH v3 1/3] ALSA: pcm: Add snd_pcm_add_named_chmap_ctls() for custom control names Abel Vesa
@ 2026-11-05 16:02 ` Abel Vesa
2026-11-05 16:02 ` [PATCH v3 3/3] ASoC: qcom: x1e80100: Allow userspace WSA RX chmap override Abel Vesa
2 siblings, 0 replies; 5+ messages in thread
From: Abel Vesa @ 2026-11-05 16:02 UTC (permalink / raw)
To: Srinivas Kandagatla, Liam Girdwood, Mark Brown, Jaroslav Kysela,
Takashi Iwai
Cc: Srinivas Kandagatla, linux-sound, linux-arm-msm, linux-kernel,
Abel Vesa
Add a small helper that converts ALSA channel map positions to the Q6 PCM
channel identifiers used by Qualcomm machine drivers.
The mapping is useful beyond a single machine driver when exposing
writable channel-map controls.
Signed-off-by: Abel Vesa <abel.vesa@oss.qualcomm.com>
---
sound/soc/qcom/common.c | 30 ++++++++++++++++++++++++++++++
sound/soc/qcom/common.h | 1 +
2 files changed, 31 insertions(+)
diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c
index 83b745f617a8..29b2df21b765 100644
--- a/sound/soc/qcom/common.c
+++ b/sound/soc/qcom/common.c
@@ -6,7 +6,9 @@
#include <linux/module.h>
#include <sound/jack.h>
#include <linux/input-event-codes.h>
+#include <sound/pcm.h>
#include "common.h"
+#include "qdsp6/q6dsp-common.h"
#define NAME_SIZE 32
@@ -327,6 +329,34 @@ int qcom_snd_parse_of(struct snd_soc_card *card)
}
EXPORT_SYMBOL_GPL(qcom_snd_parse_of);
+int qcom_snd_chmap_to_q6(unsigned int pos)
+{
+ switch (pos) {
+ case SNDRV_CHMAP_FL:
+ return PCM_CHANNEL_FL;
+ case SNDRV_CHMAP_FR:
+ return PCM_CHANNEL_FR;
+ case SNDRV_CHMAP_MONO:
+ case SNDRV_CHMAP_FC:
+ return PCM_CHANNEL_FC;
+ case SNDRV_CHMAP_LFE:
+ return PCM_CHANNEL_LFE;
+ case SNDRV_CHMAP_RL:
+ return PCM_CHANNEL_LB;
+ case SNDRV_CHMAP_RR:
+ return PCM_CHANNEL_RB;
+ case SNDRV_CHMAP_SL:
+ return PCM_CHANNEL_LS;
+ case SNDRV_CHMAP_SR:
+ return PCM_CHANNEL_RS;
+ case SNDRV_CHMAP_RC:
+ return PCM_CHANNEL_CS;
+ default:
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL_GPL(qcom_snd_chmap_to_q6);
+
static struct snd_soc_jack_pin qcom_headset_jack_pins[] = {
/* Headset */
{
diff --git a/sound/soc/qcom/common.h b/sound/soc/qcom/common.h
index 37b185a4968f..2b1d30230d77 100644
--- a/sound/soc/qcom/common.h
+++ b/sound/soc/qcom/common.h
@@ -31,6 +31,7 @@ int qcom_snd_wcd_jack_setup(struct snd_soc_pcm_runtime *rtd,
struct snd_soc_jack *jack, bool *jack_setup);
int qcom_snd_dp_jack_setup(struct snd_soc_pcm_runtime *rtd,
struct snd_soc_jack *dp_jack, int id);
+int qcom_snd_chmap_to_q6(unsigned int pos);
#endif
--
2.54.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v3 3/3] ASoC: qcom: x1e80100: Allow userspace WSA RX chmap override
2026-11-05 16:02 [PATCH v3 0/3] ASoC: qcom: x1e80100: allow WSA channel map override Abel Vesa
2026-11-05 16:02 ` [PATCH v3 1/3] ALSA: pcm: Add snd_pcm_add_named_chmap_ctls() for custom control names Abel Vesa
2026-11-05 16:02 ` [PATCH v3 2/3] ASoC: qcom: common: Add SNDRV channel map to Q6 slot helper Abel Vesa
@ 2026-11-05 16:02 ` Abel Vesa
2026-11-06 5:53 ` sashiko-bot
2 siblings, 1 reply; 5+ messages in thread
From: Abel Vesa @ 2026-11-05 16:02 UTC (permalink / raw)
To: Srinivas Kandagatla, Liam Girdwood, Mark Brown, Jaroslav Kysela,
Takashi Iwai
Cc: Srinivas Kandagatla, linux-sound, linux-arm-msm, linux-kernel,
Abel Vesa
The WSA RX slot mapping is currently computed entirely from the channel
count in .prepare() and pushed unconditionally via
snd_soc_dai_set_channel_map(). Userspace has no way to express a
different channel map, so any map defined in UCM is ignored.
Add writable "WSA Playback Channel Map" and "WSA2 Playback Channel Map"
controls on the WSA RX backend PCMs, using the standard ALSA channel map
TLV. Store the userspace-selected SNDRV_CHMAP_* map per WSA backend,
translate it to Q6 PCM_CHANNEL_* slots at .prepare() time, and keep the
previous default mapping when userspace has not provided a complete map.
The controls are added from the card late_probe callback. The backend
PCMs are only created after all DAI links are initialized, and adding
controls from the stream startup callback would take controls_rwsem
under dapm_mutex, deadlocking against DAPM mixer writes.
Allow up to six WSA channels for Glymur.
Signed-off-by: Abel Vesa <abel.vesa@oss.qualcomm.com>
---
sound/soc/qcom/x1e80100.c | 193 +++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 189 insertions(+), 4 deletions(-)
diff --git a/sound/soc/qcom/x1e80100.c b/sound/soc/qcom/x1e80100.c
index c81df41ace88..6a91b3581c75 100644
--- a/sound/soc/qcom/x1e80100.c
+++ b/sound/soc/qcom/x1e80100.c
@@ -15,14 +15,117 @@
#include "qdsp6/q6dsp-common.h"
#include "sdw.h"
+#define X1E80100_WSA_MAX_CHANNELS 6
+#define X1E80100_WSA_PORTS 2
+
struct x1e80100_snd_data {
bool stream_prepared[AFE_PORT_MAX];
struct snd_soc_card *card;
struct snd_soc_jack jack;
struct snd_soc_jack dp_jack[8];
bool jack_setup;
+ /* Userspace-selected SNDRV_CHMAP_* positions, 0 means unset */
+ unsigned int wsa_chmap[X1E80100_WSA_PORTS][X1E80100_WSA_MAX_CHANNELS];
};
+static int x1e80100_wsa_port_index(unsigned int dai_id)
+{
+ switch (dai_id) {
+ case WSA_CODEC_DMA_RX_0:
+ return 0;
+ case WSA_CODEC_DMA_RX_1:
+ return 1;
+ default:
+ return -EINVAL;
+ }
+}
+
+static unsigned int *
+x1e80100_wsa_chmap_from_kcontrol(struct snd_kcontrol *kcontrol)
+{
+ struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_pcm_runtime *rtd = info->pcm->private_data;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct x1e80100_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
+ int idx = x1e80100_wsa_port_index(cpu_dai->id);
+
+ if (idx < 0)
+ return NULL;
+
+ return data->wsa_chmap[idx];
+}
+
+static int x1e80100_wsa_chmap_ctl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ unsigned int *map = x1e80100_wsa_chmap_from_kcontrol(kcontrol);
+ int i;
+
+ if (!map)
+ return -EINVAL;
+
+ for (i = 0; i < X1E80100_WSA_MAX_CHANNELS; i++)
+ ucontrol->value.integer.value[i] = map[i];
+
+ return 0;
+}
+
+static int x1e80100_wsa_chmap_ctl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ unsigned int *map = x1e80100_wsa_chmap_from_kcontrol(kcontrol);
+ bool changed = false;
+ int i;
+
+ if (!map)
+ return -EINVAL;
+
+ for (i = 0; i < X1E80100_WSA_MAX_CHANNELS; i++) {
+ unsigned int pos = ucontrol->value.integer.value[i];
+
+ if (pos && qcom_snd_chmap_to_q6(pos) < 0)
+ return -EINVAL;
+
+ if (map[i] != pos)
+ changed = true;
+ }
+
+ if (!changed)
+ return 0;
+
+ for (i = 0; i < X1E80100_WSA_MAX_CHANNELS; i++)
+ map[i] = ucontrol->value.integer.value[i];
+
+ return 1;
+}
+
+static int x1e80100_add_wsa_chmap_ctls(struct snd_soc_pcm_runtime *rtd,
+ const char *name)
+{
+ struct snd_pcm_chmap *info;
+ unsigned int i;
+ int ret;
+
+ ret = snd_pcm_add_named_chmap_ctls(rtd->pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ name, snd_pcm_std_chmaps,
+ X1E80100_WSA_MAX_CHANNELS, 0, &info);
+ if (ret)
+ return ret;
+
+ /*
+ * The core registers a query-only control. Make it writable so that
+ * userspace (UCM) can select the WSA channel map. The card is not yet
+ * registered at this point, so the control is not visible to
+ * userspace while being modified.
+ */
+ for (i = 0; i < info->kctl->count; i++)
+ info->kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
+ info->kctl->get = x1e80100_wsa_chmap_ctl_get;
+ info->kctl->put = x1e80100_wsa_chmap_ctl_put;
+
+ return 0;
+}
+
static int x1e80100_snd_init(struct snd_soc_pcm_runtime *rtd)
{
struct x1e80100_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
@@ -92,9 +195,11 @@ static int x1e80100_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
return 0;
}
-static int x1e80100_snd_hw_map_channels(unsigned int *ch_map, int num)
+/* Default WSA RX slot mapping when userspace has not provided a channel map */
+static int x1e80100_snd_default_wsa_slots(unsigned int *ch_map,
+ unsigned int channels)
{
- switch (num) {
+ switch (channels) {
case 1:
ch_map[0] = PCM_CHANNEL_FC;
break;
@@ -113,6 +218,14 @@ static int x1e80100_snd_hw_map_channels(unsigned int *ch_map, int num)
ch_map[2] = PCM_CHANNEL_FR;
ch_map[3] = PCM_CHANNEL_RB;
break;
+ case 6:
+ ch_map[0] = PCM_CHANNEL_FL;
+ ch_map[1] = PCM_CHANNEL_FR;
+ ch_map[2] = PCM_CHANNEL_LB;
+ ch_map[3] = PCM_CHANNEL_RB;
+ ch_map[4] = PCM_CHANNEL_FC;
+ ch_map[5] = PCM_CHANNEL_LFE;
+ break;
default:
return -EINVAL;
}
@@ -120,19 +233,56 @@ static int x1e80100_snd_hw_map_channels(unsigned int *ch_map, int num)
return 0;
}
+static int x1e80100_snd_build_wsa_slots(struct x1e80100_snd_data *data,
+ unsigned int dai_id,
+ unsigned int channels,
+ unsigned int *rx_slot)
+{
+ unsigned int i, set = 0;
+ unsigned int *user;
+ int idx;
+
+ idx = x1e80100_wsa_port_index(dai_id);
+ if (idx < 0)
+ return idx;
+
+ if (channels > X1E80100_WSA_MAX_CHANNELS)
+ return -EINVAL;
+
+ user = data->wsa_chmap[idx];
+ for (i = 0; i < channels; i++)
+ if (user[i])
+ set++;
+
+ /* Fall back to the default map unless userspace set all channels */
+ if (set != channels)
+ return x1e80100_snd_default_wsa_slots(rx_slot, channels);
+
+ for (i = 0; i < channels; i++) {
+ int q6 = qcom_snd_chmap_to_q6(user[i]);
+
+ if (q6 < 0)
+ return q6;
+ rx_slot[i] = q6;
+ }
+
+ return 0;
+}
+
static int x1e80100_snd_prepare(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
struct x1e80100_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
unsigned int channels = substream->runtime->channels;
- unsigned int rx_slot[4];
+ unsigned int rx_slot[X1E80100_WSA_MAX_CHANNELS];
int ret;
switch (cpu_dai->id) {
case WSA_CODEC_DMA_RX_0:
case WSA_CODEC_DMA_RX_1:
- ret = x1e80100_snd_hw_map_channels(rx_slot, channels);
+ ret = x1e80100_snd_build_wsa_slots(data, cpu_dai->id,
+ channels, rx_slot);
if (ret)
return ret;
@@ -164,6 +314,40 @@ static const struct snd_soc_ops x1e80100_be_ops = {
.prepare = x1e80100_snd_prepare,
};
+/*
+ * The WSA channel map controls are added here rather than in the DAI link
+ * init or startup callbacks: the backend PCMs are created only after all
+ * links are initialized, and adding controls from startup would take
+ * controls_rwsem under dapm_mutex, deadlocking against DAPM mixer writes.
+ */
+static int x1e80100_late_probe(struct snd_soc_card *card)
+{
+ struct snd_soc_pcm_runtime *rtd;
+ int ret;
+
+ for_each_card_rtds(card, rtd) {
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ const char *name;
+
+ switch (cpu_dai->id) {
+ case WSA_CODEC_DMA_RX_0:
+ name = "WSA Playback Channel Map";
+ break;
+ case WSA_CODEC_DMA_RX_1:
+ name = "WSA2 Playback Channel Map";
+ break;
+ default:
+ continue;
+ }
+
+ ret = x1e80100_add_wsa_chmap_ctls(rtd, name);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
static void x1e80100_add_be_ops(struct snd_soc_card *card)
{
struct snd_soc_dai_link *link;
@@ -203,6 +387,7 @@ static int x1e80100_platform_probe(struct platform_device *pdev)
return ret;
card->driver_name = of_device_get_match_data(dev);
+ card->late_probe = x1e80100_late_probe;
x1e80100_add_be_ops(card);
return devm_snd_soc_register_card(dev, card);
--
2.54.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH v3 3/3] ASoC: qcom: x1e80100: Allow userspace WSA RX chmap override
2026-11-05 16:02 ` [PATCH v3 3/3] ASoC: qcom: x1e80100: Allow userspace WSA RX chmap override Abel Vesa
@ 2026-11-06 5:53 ` sashiko-bot
0 siblings, 0 replies; 5+ messages in thread
From: sashiko-bot @ 2026-11-06 5:53 UTC (permalink / raw)
To: Abel Vesa; +Cc: linux-sound
Thank you for your contribution! Sashiko AI review found 3 potential
issues to consider:
- [Medium] ASoC: qcom: x1e80100: channel map truncation in
x1e80100_snd_build_wsa_slots()
- [Medium] ASoC: qcom: x1e80100: 6-channel WSA playback fails in
AudioReach prepare
- [Medium] ASoC: qcom: x1e80100: TOCTOU race on wsa_chmap in
x1e80100_snd_build_wsa_slots()
--
commit 6a0b312ac836c9b0665e198b3ecffb015e2de776
Author: Abel Vesa <abel.vesa@oss.qualcomm.com>
ASoC: qcom: x1e80100: Allow userspace WSA RX chmap override
This commit adds writable ALSA channel map controls for the WSA RX
backends so userspace can override the default Q6 slot mapping, and
extends the WSA channel count up to six channels.
> diff --git a/sound/soc/qcom/x1e80100.c b/sound/soc/qcom/x1e80100.c
> index c81df41ace883..6a91b3581c756 100644
> --- a/sound/soc/qcom/x1e80100.c
> +++ b/sound/soc/qcom/x1e80100.c
> @@ -15,14 +15,117 @@
> #include "qdsp6/q6dsp-common.h"
> #include "sdw.h"
>
> +#define X1E80100_WSA_MAX_CHANNELS 6
> +#define X1E80100_WSA_PORTS 2
> +
> struct x1e80100_snd_data {
> bool stream_prepared[AFE_PORT_MAX];
> struct snd_soc_card *card;
> struct snd_soc_jack jack;
> struct snd_soc_jack dp_jack[8];
> bool jack_setup;
> + /* Userspace-selected SNDRV_CHMAP_* positions, 0 means unset */
> + unsigned int wsa_chmap[X1E80100_WSA_PORTS][X1E80100_WSA_MAX_CHANNELS];
> };
[ ... ]
> +static int x1e80100_wsa_chmap_ctl_put(struct snd_kcontrol *kcontrol,
> + struct snd_ctl_elem_value *ucontrol)
> +{
> + unsigned int *map = x1e80100_wsa_chmap_from_kcontrol(kcontrol);
> + bool changed = false;
> + int i;
> +
> + if (!map)
> + return -EINVAL;
> +
> + for (i = 0; i < X1E80100_WSA_MAX_CHANNELS; i++) {
> + unsigned int pos = ucontrol->value.integer.value[i];
> +
> + if (pos && qcom_snd_chmap_to_q6(pos) < 0)
> + return -EINVAL;
> +
> + if (map[i] != pos)
> + changed = true;
> + }
> +
> + if (!changed)
> + return 0;
> +
> + for (i = 0; i < X1E80100_WSA_MAX_CHANNELS; i++)
> + map[i] = ucontrol->value.integer.value[i];
> +
> + return 1;
> +}
[ ... ]
> @@ -113,6 +218,14 @@ static int x1e80100_snd_hw_map_channels(unsigned int *ch_map, int num)
> ch_map[2] = PCM_CHANNEL_FR;
> ch_map[3] = PCM_CHANNEL_RB;
> break;
> + case 6:
> + ch_map[0] = PCM_CHANNEL_FL;
> + ch_map[1] = PCM_CHANNEL_FR;
> + ch_map[2] = PCM_CHANNEL_LB;
> + ch_map[3] = PCM_CHANNEL_RB;
> + ch_map[4] = PCM_CHANNEL_FC;
> + ch_map[5] = PCM_CHANNEL_LFE;
> + break;
> default:
> return -EINVAL;
> }
[Severity: Medium]
Can a 6-channel WSA playback stream succeed when prepared through
AudioReach?
x1e80100_be_hw_params_fixup() leaves the channel count unchanged for
WSA_CODEC_DMA_RX_0 and WSA_CODEC_DMA_RX_1, so a 6-channel stream runs with
num_channels == 6 on both the frontend (q6apm_dai_prepare()) and backend
(q6apm_lpass_dai_prepare()). However, the AudioReach media format helpers
reject anything above 4 channels with -EINVAL, and
audioreach_set_default_channel_mapping() only populates 1, 2 and 4
channels:
sound/soc/qcom/qdsp6/audioreach.c:audioreach_pcm_set_media_format() {
...
if (num_channels > 4) {
dev_err(graph->dev, "Error: Invalid channels (%d)!\n", num_channels);
return -EINVAL;
}
...
}
As a result, preparing a 6-channel WSA stream fails with -EINVAL in
audioreach_shmem_set_media_format() and audioreach_pcm_set_media_format().
> @@ -120,19 +233,56 @@ static int x1e80100_snd_hw_map_channels(unsigned int *ch_map, int num)
> return 0;
> }
>
> +static int x1e80100_snd_build_wsa_slots(struct x1e80100_snd_data *data,
> + unsigned int dai_id,
> + unsigned int channels,
> + unsigned int *rx_slot)
> +{
> + unsigned int i, set = 0;
> + unsigned int *user;
> + int idx;
> +
> + idx = x1e80100_wsa_port_index(dai_id);
> + if (idx < 0)
> + return idx;
> +
> + if (channels > X1E80100_WSA_MAX_CHANNELS)
> + return -EINVAL;
> +
> + user = data->wsa_chmap[idx];
> + for (i = 0; i < channels; i++)
> + if (user[i])
> + set++;
> +
> + /* Fall back to the default map unless userspace set all channels */
> + if (set != channels)
> + return x1e80100_snd_default_wsa_slots(rx_slot, channels);
[Severity: Medium]
Does x1e80100_snd_build_wsa_slots() truncate a wider userspace channel map
when a stream with fewer channels is prepared?
The loop only counts non-zero entries in user[0 .. channels - 1] and does
not check whether user[channels .. X1E80100_WSA_MAX_CHANNELS - 1] are also
set. If UCM configures a 4-channel speaker map such as [FL, RL, FR, RR,
0, 0] at card init and a 2-channel stereo stream is later played, set is 2
(matching channels), so x1e80100_snd_build_wsa_slots() applies the first
two entries [FL, RL] instead of falling back to
x1e80100_snd_default_wsa_slots(), routing both stereo channels to the left
speakers. Conversely, x1e80100_wsa_chmap_ctl_put() accepts sparse maps
with zeroed slots below X1E80100_WSA_MAX_CHANNELS that
x1e80100_snd_build_wsa_slots() then silently ignores.
> +
> + for (i = 0; i < channels; i++) {
> + int q6 = qcom_snd_chmap_to_q6(user[i]);
> +
> + if (q6 < 0)
> + return q6;
> + rx_slot[i] = q6;
> + }
> +
> + return 0;
> +}
[Severity: Medium]
Can a concurrent control write race with x1e80100_snd_build_wsa_slots() on
data->wsa_chmap[idx] and cause x1e80100_snd_prepare() to fail with
-EINVAL?
x1e80100_wsa_chmap_ctl_put() updates data->wsa_chmap[idx] under
controls_rwsem, while x1e80100_snd_prepare() reads it under pcm_mutex
with no shared lock:
CPU0 CPU1
x1e80100_snd_prepare()
x1e80100_snd_build_wsa_slots()
for (i = 0; i < channels; i++)
if (user[i])
set++;
/* set == channels */
x1e80100_wsa_chmap_ctl_put()
map[i] = 0;
qcom_snd_chmap_to_q6(user[i])
--> returns -EINVAL
Because user[i] is re-read in the second loop, clearing the channel map to
0 (unset) or updating it concurrently can make qcom_snd_chmap_to_q6(0)
abort PCM prepare with -EINVAL instead of falling back to
x1e80100_snd_default_wsa_slots(), or program a torn mix of old and new
channel positions.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261105-b4-sound-qcom-x1e80100-allow-ucm-channel-map-v3-0-29e06613115f@oss.qualcomm.com?part=3
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-11-06 5:53 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-05 16:02 [PATCH v3 0/3] ASoC: qcom: x1e80100: allow WSA channel map override Abel Vesa
2026-11-05 16:02 ` [PATCH v3 1/3] ALSA: pcm: Add snd_pcm_add_named_chmap_ctls() for custom control names Abel Vesa
2026-11-05 16:02 ` [PATCH v3 2/3] ASoC: qcom: common: Add SNDRV channel map to Q6 slot helper Abel Vesa
2026-11-05 16:02 ` [PATCH v3 3/3] ASoC: qcom: x1e80100: Allow userspace WSA RX chmap override Abel Vesa
2026-11-06 5:53 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox