* [PATCH v3 1/3] ASoC: qcom: audioreach: compute active channel maps from channel_map
2026-07-28 8:46 [PATCH v3 0/3] ASoC: qcom: audioreach: support WSA speakers only on WSA2 Neil Armstrong
@ 2026-07-28 8:46 ` Neil Armstrong
2026-07-28 9:03 ` sashiko-bot
2026-07-28 8:46 ` [PATCH v3 2/3] ASoC: dt-bindings: qcom,sm8250: Add Ayaneo Pocket S2 sound card Neil Armstrong
2026-07-28 8:46 ` [PATCH v3 3/3] ASoC: qcom: sc8280xp: add Ayaneo Pocket S2 card with special WSA channel mapping Neil Armstrong
2 siblings, 1 reply; 7+ messages in thread
From: Neil Armstrong @ 2026-07-28 8:46 UTC (permalink / raw)
To: Srinivas Kandagatla, Liam Girdwood, Mark Brown, Jaroslav Kysela,
Takashi Iwai, Bjorn Andersson, Konrad Dybcio, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: kancy2333, linux-sound, linux-arm-msm, linux-kernel, devicetree,
Neil Armstrong, Srinivas Kandagatla
The Qualcom SM8650 based Ayaneo Pocket S2 gaming device has a set
of 2 WSA speakers connected on the WSA2 lines.
But the Audioreach DSP only handles WSA2 in pair with the WSA
interface by using the upper bits of the active_channels_mask
for WSA2 and the lower bits for WSA:
/-------------------------------------------------\
| Bits | 3 | 2 | 1 | 0 |
|-------------------------------------------------|
| Line | WSA2 Ch2 | WSA2 Ch1 | WSA Ch2 | WSA Ch1 |
\-------------------------------------------------/
Setting only the WSA2 upper bits is perfectly valid and
functional but the current Audioreach code builds the bitmask
from the channels count with:
active_channels_mask = (1 << num_channels) - 1;
In order to enable the WSA2 bits the channel count should be 4,
but the lower WSA bits are then also enabled and the DSP errors
out when trying to play on the disabled WSA interface.
A solution would've been to add a fake WSA2 topology element which
would be translated into the top bits only, but it's not clean and
add some special exceptions in the generic Audioreach code.
The solution suggested by Srinivas is to use the channel mapping to
set this bitmask.
This works but makes all the other calls using the channel mapping fail
because the DSP requires the channel_mapping table to start from index 0
and using num_channel length in order to apply the mapping on the
active_channels_mask bits in order.
So we need to skip the empty channel mapping entries in all other
users of the channel_map to build valid channel_mapping tables.
This should not break any other usecases since the default channel
mapping always start from index 0, and will add flexibilty to allow
some special non linear mapping for other interfaces as well.
Suggested-by: Srinivas Kandagatla <srinivas.kandagatla@oss.qualcomm.com>
Tested-by: Srinivas Kandagatla <srinivas.kandagatla@oss.qualcomm.com>
Reviewed-by: Srinivas Kandagatla <srinivas.kandagatla@oss.qualcomm.com>
Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
sound/soc/qcom/qdsp6/audioreach.c | 47 ++++++++++++++++++++++++++++++---------
1 file changed, 37 insertions(+), 10 deletions(-)
diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c
index e6e9eb2e85aa..0cc840aca69d 100644
--- a/sound/soc/qcom/qdsp6/audioreach.c
+++ b/sound/soc/qcom/qdsp6/audioreach.c
@@ -703,6 +703,7 @@ static int audioreach_codec_dma_set_media_format(struct q6apm_graph *graph,
int pm_sz = APM_HW_EP_PMODE_CFG_PSIZE;
int size = ic_sz + ep_sz + fs_sz + pm_sz;
void *p;
+ int i;
struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(size, APM_CMD_SET_CFG, 0);
if (IS_ERR(pkt))
@@ -741,7 +742,12 @@ static int audioreach_codec_dma_set_media_format(struct q6apm_graph *graph,
intf_cfg->cfg.lpaif_type = module->hw_interface_type;
intf_cfg->cfg.intf_index = module->hw_interface_idx;
- intf_cfg->cfg.active_channels_mask = (1 << cfg->num_channels) - 1;
+ intf_cfg->cfg.active_channels_mask = 0;
+ /* Convert the physical channel mapping into a bit field */
+ for (i = 0; i < AR_PCM_MAX_NUM_CHANNEL; i++)
+ if (cfg->channel_map[i])
+ intf_cfg->cfg.active_channels_mask |= BIT(i);
+
p += ic_sz;
pm_cfg = p;
@@ -840,7 +846,7 @@ static int audioreach_mfc_set_media_format(struct q6apm_graph *graph,
uint32_t num_channels = cfg->num_channels;
int payload_size = APM_MFC_CFG_PSIZE(media_format, num_channels) +
APM_MODULE_PARAM_DATA_SIZE;
- int i;
+ int i, j;
void *p;
struct gpr_pkt *pkt __free(kfree) = audioreach_alloc_apm_cmd_pkt(payload_size, APM_CMD_SET_CFG, 0);
@@ -860,8 +866,12 @@ static int audioreach_mfc_set_media_format(struct q6apm_graph *graph,
media_format->sample_rate = cfg->sample_rate;
media_format->bit_width = cfg->bit_width;
media_format->num_channels = cfg->num_channels;
- for (i = 0; i < num_channels; i++)
- media_format->channel_mapping[i] = cfg->channel_map[i];
+ /* Convert the physical mapping to a logical mapping of the channels */
+ for (i = 0, j = 0; i < AR_PCM_MAX_NUM_CHANNEL && j < cfg->num_channels; i++) {
+ if (!cfg->channel_map[i])
+ continue;
+ media_format->channel_mapping[j++] = cfg->channel_map[i];
+ }
return q6apm_send_cmd_sync(graph->apm, pkt, 0);
}
@@ -1080,6 +1090,7 @@ static int audioreach_pcm_set_media_format(struct q6apm_graph *graph,
struct apm_pcm_module_media_fmt_cmd *cfg;
struct apm_module_param_data *param_data;
int payload_size;
+ int i, j;
if (num_channels > 4) {
dev_err(graph->dev, "Error: Invalid channels (%d)!\n", num_channels);
@@ -1113,7 +1124,12 @@ static int audioreach_pcm_set_media_format(struct q6apm_graph *graph,
media_cfg->num_channels = mcfg->num_channels;
media_cfg->q_factor = mcfg->bit_width - 1;
media_cfg->bits_per_sample = mcfg->bit_width;
- memcpy(media_cfg->channel_mapping, mcfg->channel_map, mcfg->num_channels);
+ /* Convert the physical mapping to a logical mapping of the channels */
+ for (i = 0, j = 0; i < AR_PCM_MAX_NUM_CHANNEL && j < mcfg->num_channels; i++) {
+ if (!mcfg->channel_map[i])
+ continue;
+ media_cfg->channel_mapping[j++] = mcfg->channel_map[i];
+ }
return q6apm_send_cmd_sync(graph->apm, pkt, 0);
}
@@ -1163,6 +1179,7 @@ static int audioreach_shmem_set_media_format(struct q6apm_graph *graph,
struct payload_media_fmt_pcm *cfg;
struct media_format *header;
int rc, payload_size;
+ int i, j;
void *p;
if (num_channels > 4) {
@@ -1202,7 +1219,12 @@ static int audioreach_shmem_set_media_format(struct q6apm_graph *graph,
cfg->q_factor = mcfg->bit_width - 1;
cfg->endianness = PCM_LITTLE_ENDIAN;
cfg->num_channels = mcfg->num_channels;
- memcpy(cfg->channel_mapping, mcfg->channel_map, mcfg->num_channels);
+ /* Convert the physical mapping to a logical mapping of the channels */
+ for (i = 0, j = 0; i < AR_PCM_MAX_NUM_CHANNEL && j < cfg->num_channels; i++) {
+ if (!mcfg->channel_map[i])
+ continue;
+ cfg->channel_mapping[j++] = mcfg->channel_map[i];
+ }
} else {
rc = audioreach_set_compr_media_format(header, p, mcfg);
if (rc)
@@ -1279,7 +1301,7 @@ static int audioreach_speaker_protection_vi(struct q6apm_graph *graph,
struct apm_module_sp_vi_ex_mode_cfg *ex_cfg;
int op_sz, cm_sz, ex_sz;
struct apm_module_param_data *param_data;
- int rc, i, payload_size;
+ int rc, i, payload_size, j;
struct gpr_pkt *pkt;
void *p;
@@ -1320,14 +1342,19 @@ static int audioreach_speaker_protection_vi(struct q6apm_graph *graph,
param_data->param_size = cm_sz - APM_MODULE_PARAM_DATA_SIZE;
cm_cfg->cfg.num_channels = num_channels * 2;
- for (i = 0; i < num_channels; i++) {
+ /* Convert the physical mapping to a logical mapping of the channels */
+ for (i = 0, j = 0; i < AR_PCM_MAX_NUM_CHANNEL && j < num_channels; i++) {
+ if (!mcfg->channel_map[i])
+ continue;
/*
* Map speakers into Vsense and then Isense of each channel.
* E.g. for PCM_CHANNEL_FL and PCM_CHANNEL_FR to:
* [1, 2, 3, 4]
*/
- cm_cfg->cfg.channel_mapping[2 * i] = (mcfg->channel_map[i] - 1) * 2 + 1;
- cm_cfg->cfg.channel_mapping[2 * i + 1] = (mcfg->channel_map[i] - 1) * 2 + 2;
+ cm_cfg->cfg.channel_mapping[2 * j] = (mcfg->channel_map[i] - 1) * 2 + 1;
+ cm_cfg->cfg.channel_mapping[2 * j + 1] = (mcfg->channel_map[i] - 1) * 2 + 2;
+
+ ++j;
}
p += cm_sz;
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread* Re: [PATCH v3 1/3] ASoC: qcom: audioreach: compute active channel maps from channel_map
2026-07-28 8:46 ` [PATCH v3 1/3] ASoC: qcom: audioreach: compute active channel maps from channel_map Neil Armstrong
@ 2026-07-28 9:03 ` sashiko-bot
2026-07-28 9:46 ` Neil Armstrong
0 siblings, 1 reply; 7+ messages in thread
From: sashiko-bot @ 2026-07-28 9:03 UTC (permalink / raw)
To: Neil Armstrong; +Cc: conor+dt, robh, devicetree
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [High] The new `active_channels_mask` computation in `audioreach_codec_dma_set_media_format` introduces regressions by unconditionally iterating over all `AR_PCM_MAX_NUM_CHANNEL` elements.
Pre-existing issues:
- [High] Kernel stack memory is leaked to the DSP and evaluated in control logic because `cfg.channel_map` is not zero-initialized.
--
--- Patch [1]: [PATCH v3 1/3] ASoC: qcom: audioreach: compute active channel maps from channel_map ---
commit 893598480a0705f69c55eefe5bf4e2b969edc6d8
Author: Neil Armstrong <neil.armstrong@linaro.org>
Subject: ASoC: qcom: audioreach: compute active channel maps from channel_map
This patch updates the Audioreach DSP code to compute the active channel
mask and logical channel mappings using the physical channel mapping array
(channel_map) rather than a simple bitmask derived from the channel count.
This enables WSA2 speaker usage on specific hardware by correctly setting
only the upper bits of the channel mask.
> diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c
> index e6e9eb2e85aa1..0cc840aca69dd 100644
> --- a/sound/soc/qcom/qdsp6/audioreach.c
> +++ b/sound/soc/qcom/qdsp6/audioreach.c
> @@ -741,7 +742,12 @@ static int audioreach_codec_dma_set_media_format(struct q6apm_graph *graph,
>
> intf_cfg->cfg.lpaif_type = module->hw_interface_type;
> intf_cfg->cfg.intf_index = module->hw_interface_idx;
> - intf_cfg->cfg.active_channels_mask = (1 << cfg->num_channels) - 1;
> + intf_cfg->cfg.active_channels_mask = 0;
> + /* Convert the physical channel mapping into a bit field */
> + for (i = 0; i < AR_PCM_MAX_NUM_CHANNEL; i++)
> + if (cfg->channel_map[i])
> + intf_cfg->cfg.active_channels_mask |= BIT(i);
> +
[Severity: High]
If a stream is configured with 4 channels and later reconfigured to 2
channels, audioreach_set_default_channel_mapping() in q6dma_hw_params() only
overwrites the first 2 entries, leaving the remaining entries intact.
Since this loop iterates unconditionally up to AR_PCM_MAX_NUM_CHANNEL, could
it pick up those stale entries and activate unintended channels in the DSP?
Additionally, for channel counts greater than 4 (like 6 or 8),
audioreach_set_default_channel_mapping() can leave the map as all zeros. Could
this result in an active_channels_mask of 0 and break the DMA configuration?
[Severity: High]
This is a pre-existing issue, but struct audioreach_module_config cfg is
allocated on the stack in q6apm_dai_prepare() without being initialized:
q6apm_dai_prepare() {
...
struct audioreach_module_config cfg;
...
}
When an unsupported channel count is used,
audioreach_set_default_channel_mapping() leaves the array untouched,
containing stack garbage.
While the previous code copied this memory blindly, the new loop here branches
on this uninitialized stack memory to construct the channel mask. Could this
result in unpredictable DSP configurations and leak stack contents in the IPC
payload?
> p += ic_sz;
>
> pm_cfg = p;
[ ... ]
> @@ -1113,7 +1124,12 @@ static int audioreach_pcm_set_media_format(struct q6apm_graph *graph,
> media_cfg->num_channels = mcfg->num_channels;
> media_cfg->q_factor = mcfg->bit_width - 1;
> media_cfg->bits_per_sample = mcfg->bit_width;
> - memcpy(media_cfg->channel_mapping, mcfg->channel_map, mcfg->num_channels);
> + /* Convert the physical mapping to a logical mapping of the channels */
> + for (i = 0, j = 0; i < AR_PCM_MAX_NUM_CHANNEL && j < mcfg->num_channels; i++) {
> + if (!mcfg->channel_map[i])
> + continue;
> + media_cfg->channel_mapping[j++] = mcfg->channel_map[i];
> + }
[Severity: High]
This is a pre-existing issue, but similar to the above concern, could this
loop branch on uninitialized stack memory if mcfg->channel_map was left
uninitialized in q6apm_dai_prepare()?
This would cause the code to evaluate stack garbage in its control flow and
copy it into the DSP IPC payload.
>
> return q6apm_send_cmd_sync(graph->apm, pkt, 0);
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260728-topic-sm8650-ayaneo-pocket-s2-wsa2-fix-v3-0-b29f44720178@linaro.org?part=1
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: [PATCH v3 1/3] ASoC: qcom: audioreach: compute active channel maps from channel_map
2026-07-28 9:03 ` sashiko-bot
@ 2026-07-28 9:46 ` Neil Armstrong
0 siblings, 0 replies; 7+ messages in thread
From: Neil Armstrong @ 2026-07-28 9:46 UTC (permalink / raw)
To: sashiko-reviews; +Cc: conor+dt, robh, devicetree
On 7/28/26 11:03, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
>
> New issues:
> - [High] The new `active_channels_mask` computation in `audioreach_codec_dma_set_media_format` introduces regressions by unconditionally iterating over all `AR_PCM_MAX_NUM_CHANNEL` elements.
>
> Pre-existing issues:
> - [High] Kernel stack memory is leaked to the DSP and evaluated in control logic because `cfg.channel_map` is not zero-initialized.
> --
>
> --- Patch [1]: [PATCH v3 1/3] ASoC: qcom: audioreach: compute active channel maps from channel_map ---
> commit 893598480a0705f69c55eefe5bf4e2b969edc6d8
> Author: Neil Armstrong <neil.armstrong@linaro.org>
> Subject: ASoC: qcom: audioreach: compute active channel maps from channel_map
>
> This patch updates the Audioreach DSP code to compute the active channel
> mask and logical channel mappings using the physical channel mapping array
> (channel_map) rather than a simple bitmask derived from the channel count.
> This enables WSA2 speaker usage on specific hardware by correctly setting
> only the upper bits of the channel mask.
>
>> diff --git a/sound/soc/qcom/qdsp6/audioreach.c b/sound/soc/qcom/qdsp6/audioreach.c
>> index e6e9eb2e85aa1..0cc840aca69dd 100644
>> --- a/sound/soc/qcom/qdsp6/audioreach.c
>> +++ b/sound/soc/qcom/qdsp6/audioreach.c
>> @@ -741,7 +742,12 @@ static int audioreach_codec_dma_set_media_format(struct q6apm_graph *graph,
>>
>> intf_cfg->cfg.lpaif_type = module->hw_interface_type;
>> intf_cfg->cfg.intf_index = module->hw_interface_idx;
>> - intf_cfg->cfg.active_channels_mask = (1 << cfg->num_channels) - 1;
>> + intf_cfg->cfg.active_channels_mask = 0;
>> + /* Convert the physical channel mapping into a bit field */
>> + for (i = 0; i < AR_PCM_MAX_NUM_CHANNEL; i++)
>> + if (cfg->channel_map[i])
>> + intf_cfg->cfg.active_channels_mask |= BIT(i);
>> +
>
> [Severity: High]
> If a stream is configured with 4 channels and later reconfigured to 2
> channels, audioreach_set_default_channel_mapping() in q6dma_hw_params() only
> overwrites the first 2 entries, leaving the remaining entries intact.
This is a valid issue, will fix.
>
> Since this loop iterates unconditionally up to AR_PCM_MAX_NUM_CHANNEL, could
> it pick up those stale entries and activate unintended channels in the DSP?
>
> Additionally, for channel counts greater than 4 (like 6 or 8),
> audioreach_set_default_channel_mapping() can leave the map as all zeros. Could
> this result in an active_channels_mask of 0 and break the DMA configuration?
Yes, the old shift should be kept if the result mask is 0
>
> [Severity: High]
> This is a pre-existing issue, but struct audioreach_module_config cfg is
> allocated on the stack in q6apm_dai_prepare() without being initialized:
>
> q6apm_dai_prepare() {
> ...
> struct audioreach_module_config cfg;
> ...
> }
>
> When an unsupported channel count is used,
> audioreach_set_default_channel_mapping() leaves the array untouched,
> containing stack garbage.
>
> While the previous code copied this memory blindly, the new loop here branches
> on this uninitialized stack memory to construct the channel mask. Could this
> result in unpredictable DSP configurations and leak stack contents in the IPC
> payload?
Yep the mapping array should be set to 0 in audioreach_set_default_channel_mapping() whatever the channel count
>
>> p += ic_sz;
>>
>> pm_cfg = p;
>
> [ ... ]
>
>> @@ -1113,7 +1124,12 @@ static int audioreach_pcm_set_media_format(struct q6apm_graph *graph,
>> media_cfg->num_channels = mcfg->num_channels;
>> media_cfg->q_factor = mcfg->bit_width - 1;
>> media_cfg->bits_per_sample = mcfg->bit_width;
>> - memcpy(media_cfg->channel_mapping, mcfg->channel_map, mcfg->num_channels);
>> + /* Convert the physical mapping to a logical mapping of the channels */
>> + for (i = 0, j = 0; i < AR_PCM_MAX_NUM_CHANNEL && j < mcfg->num_channels; i++) {
>> + if (!mcfg->channel_map[i])
>> + continue;
>> + media_cfg->channel_mapping[j++] = mcfg->channel_map[i];
>> + }
>
> [Severity: High]
> This is a pre-existing issue, but similar to the above concern, could this
> loop branch on uninitialized stack memory if mcfg->channel_map was left
> uninitialized in q6apm_dai_prepare()?
>
> This would cause the code to evaluate stack garbage in its control flow and
> copy it into the DSP IPC payload.
>
>>
>> return q6apm_send_cmd_sync(graph->apm, pkt, 0);
>> }
>
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v3 2/3] ASoC: dt-bindings: qcom,sm8250: Add Ayaneo Pocket S2 sound card
2026-07-28 8:46 [PATCH v3 0/3] ASoC: qcom: audioreach: support WSA speakers only on WSA2 Neil Armstrong
2026-07-28 8:46 ` [PATCH v3 1/3] ASoC: qcom: audioreach: compute active channel maps from channel_map Neil Armstrong
@ 2026-07-28 8:46 ` Neil Armstrong
2026-07-28 8:46 ` [PATCH v3 3/3] ASoC: qcom: sc8280xp: add Ayaneo Pocket S2 card with special WSA channel mapping Neil Armstrong
2 siblings, 0 replies; 7+ messages in thread
From: Neil Armstrong @ 2026-07-28 8:46 UTC (permalink / raw)
To: Srinivas Kandagatla, Liam Girdwood, Mark Brown, Jaroslav Kysela,
Takashi Iwai, Bjorn Andersson, Konrad Dybcio, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: kancy2333, linux-sound, linux-arm-msm, linux-kernel, devicetree,
Neil Armstrong, Krzysztof Kozlowski
Document the bindings for the sound card on the Ayaneo Pocket S2
which uses the special speaker connection incompatible with
the default SM8650 sound card.
Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
Documentation/devicetree/bindings/sound/qcom,sm8250.yaml | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml
index bd5f5a6268a3..0998b2abd8bb 100644
--- a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml
@@ -31,6 +31,7 @@ properties:
- qcom,sm8750-sndcard
- const: qcom,sm8450-sndcard
- enum:
+ - ayaneo,pocket-s2-sndcard
- fairphone,fp4-sndcard
- fairphone,fp5-sndcard
- qcom,apq8096-sndcard
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH v3 3/3] ASoC: qcom: sc8280xp: add Ayaneo Pocket S2 card with special WSA channel mapping
2026-07-28 8:46 [PATCH v3 0/3] ASoC: qcom: audioreach: support WSA speakers only on WSA2 Neil Armstrong
2026-07-28 8:46 ` [PATCH v3 1/3] ASoC: qcom: audioreach: compute active channel maps from channel_map Neil Armstrong
2026-07-28 8:46 ` [PATCH v3 2/3] ASoC: dt-bindings: qcom,sm8250: Add Ayaneo Pocket S2 sound card Neil Armstrong
@ 2026-07-28 8:46 ` Neil Armstrong
2026-07-28 8:55 ` sashiko-bot
2 siblings, 1 reply; 7+ messages in thread
From: Neil Armstrong @ 2026-07-28 8:46 UTC (permalink / raw)
To: Srinivas Kandagatla, Liam Girdwood, Mark Brown, Jaroslav Kysela,
Takashi Iwai, Bjorn Andersson, Konrad Dybcio, Rob Herring,
Krzysztof Kozlowski, Conor Dooley
Cc: kancy2333, linux-sound, linux-arm-msm, linux-kernel, devicetree,
Neil Armstrong, Srinivas Kandagatla
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.
Reviewed-by: Srinivas Kandagatla <srinivas.kandagatla@oss.qualcomm.com>
Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
sound/soc/qcom/sc8280xp.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 57 insertions(+)
diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c
index a9304784d41e..4ccff32a413b 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"
@@ -50,6 +51,7 @@ struct snd_soc_common {
bool mi2s_mclk_enable;
bool mi2s_bclk_enable;
bool wcd_jack;
+ int (*snd_prepare)(struct snd_pcm_substream *substream);
};
struct sc8280xp_snd_data {
@@ -213,12 +215,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:
+ * __________________________________________________
+ * | 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 */
+};
+
+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]);
}
@@ -293,6 +341,14 @@ 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,
+ .wcd_jack = true,
+};
+
static const struct snd_soc_common eliza_priv_data = {
.driver_name = "eliza",
.dapm_widgets = sc8280xp_dapm_widgets,
@@ -384,6 +440,7 @@ static const 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,eliza-sndcard", .data = &eliza_priv_data },
{ .compatible = "qcom,hawi-sndcard", .data = &hawi_priv_data },
{ .compatible = "qcom,kaanapali-sndcard", .data = &kaanapali_priv_data },
--
2.34.1
^ permalink raw reply related [flat|nested] 7+ messages in thread* Re: [PATCH v3 3/3] ASoC: qcom: sc8280xp: add Ayaneo Pocket S2 card with special WSA channel mapping
2026-07-28 8:46 ` [PATCH v3 3/3] ASoC: qcom: sc8280xp: add Ayaneo Pocket S2 card with special WSA channel mapping Neil Armstrong
@ 2026-07-28 8:55 ` sashiko-bot
0 siblings, 0 replies; 7+ messages in thread
From: sashiko-bot @ 2026-07-28 8:55 UTC (permalink / raw)
To: Neil Armstrong; +Cc: conor+dt, devicetree, robh
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] The `ayaneo_ps2_priv_data` structure is missing the `const` qualifier.
--
--- Patch [3]: [PATCH v3 3/3] ASoC: qcom: sc8280xp: add Ayaneo Pocket S2 card with special WSA channel mapping ---
commit b02e8ff5c253c4eba630e44b1766096e7e732603
Author: Neil Armstrong <neil.armstrong@linaro.org>
ASoC: qcom: sc8280xp: add Ayaneo Pocket S2 card with special WSA channel mapping
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.
> diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c
> index a9304784d41ee..4ccff32a413b6 100644
> --- a/sound/soc/qcom/sc8280xp.c
> +++ b/sound/soc/qcom/sc8280xp.c
[ ... ]
> @@ -293,6 +341,14 @@ 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 = {
[Severity: Low]
Should ayaneo_ps2_priv_data be declared as const?
Other similar structures in this file, such as eliza_priv_data, are already
marked as const.
> + .driver_name = "ayaneo-ps2",
> + .dapm_widgets = sc8280xp_dapm_widgets,
> + .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets),
> + .snd_prepare = ayaneo_ps2_snd_prepare,
> + .wcd_jack = true,
> +};
> +
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260728-topic-sm8650-ayaneo-pocket-s2-wsa2-fix-v3-0-b29f44720178@linaro.org?part=3
^ permalink raw reply [flat|nested] 7+ messages in thread