Re: [PATCH 2/4] ASoC: qcom: sc8280xp: add Ayaneo Pocket S2 card with special WSA channel mapping

From: Neil Armstrong

Date: Mon Jun 15 2026 - 11:53:39 EST


On 6/15/26 10:42, Srinivas Kandagatla wrote:
Thanks Neil for the patch.

On 6/10/26 8:41 AM, Neil Armstrong wrote:
The WSA Speakers are connected on the WSA2 interface, but the
WSA and WSA2 links are handled as a single dai and DSP interface, so
we need to specify the channel mapping of the Ayaneo Pocket S2 for the
WSA dai in order to have functional playback and avoid DSP errors.

Let's add a special entry for the Ayaneo Pocket S2 adding a prepare
callback in order to set the proper channel mapping.

Signed-off-by: Neil Armstrong <neil.armstrong@xxxxxxxxxx>
---
sound/soc/qcom/sc8280xp.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 56 insertions(+)

diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c
index 1f3afc6d015c..2f1688c9f317 100644
--- a/sound/soc/qcom/sc8280xp.c
+++ b/sound/soc/qcom/sc8280xp.c
@@ -14,6 +14,7 @@
#include "qdsp6/q6afe.h"
#include "qdsp6/q6apm.h"
#include "qdsp6/q6prm.h"
+#include "qdsp6/q6dsp-common.h"
#include "common.h"
#include "sdw.h"
@@ -49,6 +50,7 @@ struct snd_soc_common {
bool codec_sysclk_set;
bool mi2s_mclk_enable;
bool mi2s_bclk_enable;
+ int (*snd_prepare)(struct snd_pcm_substream *substream);
};
struct sc8280xp_snd_data {
@@ -193,12 +195,58 @@ static int sc8280xp_snd_hw_params(struct snd_pcm_substream *substream,
return 0;
}
+/*
+ * WSA and WSA2 are handled as a single interface with the
+ * following channels mask:
It should not be handled as single interface in this case, you could use
WSA2 as it is.

I would prefer, could you help me understand how to use it ?


+ * __________________________________________________
+ * | Bits | 3 | 2 | 1 | 0 |
+ * ---------------------------------------------------
+ * | Line | WSA2 Ch2 | WSA2 Ch1 | WSA Ch2 | WSA Ch1 |
+ * ---------------------------------------------------
+ *
+ * The Ayaneo Pocket S2 speakers are connected only to
+ * the WSA2 interface and the WSA interface is not enabled.
+ *
+ * Set the channel mapping on the WSA2 channels only.
+ */
+static const unsigned int ayaneo_ps2_channels_mapping[] = {
+ 0, /* WSA Ch1 */
+ 0, /* WSA Ch2 */
+ PCM_CHANNEL_FL, /* WSA2 Ch1 */
+ PCM_CHANNEL_FR /* WSA2 Ch2 */
+};

So we are are playing 4 channels on a 2 channel speakers?

No, because the only way we found to play on the WSA2 speakers
is to pass set active_channels_mask 0x1100 with lpaif_type to LPAIF_INTF_TYPE_WSA.

I followed your advice to play with the channel map, here's the result.


There is also a patch by Abel, on allowing userspace/ucm to set the
channel map for WSA at https://lkml.org/lkml/2026/6/10/1002
Its worth having a look.

It's unrelated, I don't need to change the mapping from userspace.

Thanks,
Neil


--srini

+
+static int ayaneo_ps2_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);
+ unsigned int channels = substream->runtime->channels;
+
+ if (cpu_dai->id != WSA_CODEC_DMA_RX_0)
+ return 0;
+
+ if (channels != 2)
+ return -EINVAL;
+
+ return snd_soc_dai_set_channel_map(cpu_dai, 0, NULL,
+ ARRAY_SIZE(ayaneo_ps2_channels_mapping),
+ ayaneo_ps2_channels_mapping);
+}
+
static int sc8280xp_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 sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
+ if (data->snd_soc_common_priv->snd_prepare) {
+ int ret;
+
+ ret = data->snd_soc_common_priv->snd_prepare(substream);
+ if (ret)
+ return ret;
+ }
+
return qcom_snd_sdw_prepare(substream, &data->stream_prepared[cpu_dai->id]);
}
@@ -273,6 +321,13 @@ static int sc8280xp_platform_probe(struct platform_device *pdev)
return devm_snd_soc_register_card(dev, card);
}
+static struct snd_soc_common ayaneo_ps2_priv_data = {
+ .driver_name = "ayaneo-ps2",
+ .dapm_widgets = sc8280xp_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets),
+ .snd_prepare = ayaneo_ps2_snd_prepare,
+};
+
static struct snd_soc_common kaanapali_priv_data = {
.driver_name = "kaanapali",
.dapm_widgets = sc8280xp_dapm_widgets,
@@ -341,6 +396,7 @@ static struct snd_soc_common sm8750_priv_data = {
};
static const struct of_device_id snd_sc8280xp_dt_match[] = {
+ {.compatible = "ayaneo,pocket-s2-sndcard", .data = &ayaneo_ps2_priv_data},
{.compatible = "qcom,kaanapali-sndcard", .data = &kaanapali_priv_data},
{.compatible = "qcom,qcm6490-idp-sndcard", .data = &qcm6490_priv_data},
{.compatible = "qcom,qcs615-sndcard", .data = &qcs615_priv_data},