* [PATCH] ASoC: qcom: q6asm: Correct the 24-bit capture format
@ 2026-11-01 19:05 Michael McAllister
2026-11-04 20:47 ` Demeter Barna
0 siblings, 1 reply; 2+ messages in thread
From: Michael McAllister @ 2026-11-01 19:05 UTC (permalink / raw)
To: Srinivas Kandagatla, Mark Brown
Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, linux-sound,
linux-arm-msm, linux-kernel, Michael McAllister
Recording in S24_LE on my Moto G10 produces badly scaled samples that
wrap at full scale. The PCM V2 DSP interface places 24-bit samples in
bits 31:8 of each 32-bit word, but ALSA S24_LE expects bits 23:0.
Replace S24_LE with S32_LE for capture and constrain it to 24
significant bits. Keep the DSP configured for 24-bit samples. This
matches its native layout without converting samples in the kernel.
Playback is unchanged.
Translate S32_LE back to S24_LE in the APQ8096 and SC7180 backend
fixups so that routing and the physical audio ports keep their 24-bit
width. The other Q6ASM machine drivers already set the backend format.
Tested on the Moto G10 with direct ALSA and PipeWire capture at 48 kHz,
using 16-bit and 32-bit containers, reopening the stream, and recording
after suspend. ALSA reports 24 significant bits for S32_LE. S24_LE
capture through ALSA's plug conversion also works.
Signed-off-by: Michael McAllister <me@michaelmcallister.org>
---
Hi,
I found this while getting the microphone working on my Moto G10
(SM4250). This fixes the incorrect sample format for 24-bit capture;
I've tested it with ALSA and PipeWire, including after suspend.
The APQ8096 and SC7180 changes have only been build-tested, so testing
on those boards would be appreciated.
Thanks,
Michael
sound/soc/qcom/apq8096.c | 8 ++++++++
sound/soc/qcom/qdsp6/q6asm-dai.c | 12 ++++++++++--
sound/soc/qcom/sc7180.c | 8 ++++++++
3 files changed, 26 insertions(+), 2 deletions(-)
diff --git a/sound/soc/qcom/apq8096.c b/sound/soc/qcom/apq8096.c
index cfd6438db..25f2f0c0e 100644
--- a/sound/soc/qcom/apq8096.c
+++ b/sound/soc/qcom/apq8096.c
@@ -6,6 +6,7 @@
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/pcm.h>
+#include <sound/pcm_params.h>
#include "common.h"
#define SLIM_MAX_TX_PORTS 16
@@ -19,10 +20,17 @@ static int apq8096_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
SNDRV_PCM_HW_PARAM_RATE);
struct snd_interval *channels = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
rate->min = rate->max = 48000;
channels->min = channels->max = 2;
+ /* Keep the backend at 24 bits despite the 32-bit capture container. */
+ if (params_format(params) == SNDRV_PCM_FORMAT_S32_LE) {
+ snd_mask_none(fmt);
+ snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
+ }
+
return 0;
}
diff --git a/sound/soc/qcom/qdsp6/q6asm-dai.c b/sound/soc/qcom/qdsp6/q6asm-dai.c
index 4261c3048..4bed05f0d 100644
--- a/sound/soc/qcom/qdsp6/q6asm-dai.c
+++ b/sound/soc/qcom/qdsp6/q6asm-dai.c
@@ -90,7 +90,7 @@ static const struct snd_pcm_hardware q6asm_dai_hardware_capture = {
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
- SNDRV_PCM_FMTBIT_S24_LE),
+ SNDRV_PCM_FMTBIT_S32_LE),
.rates = SNDRV_PCM_RATE_8000_48000,
.rate_min = 8000,
.rate_max = 48000,
@@ -151,7 +151,7 @@ static const struct snd_pcm_hardware q6asm_dai_hardware_playback = {
SNDRV_PCM_RATE_12000 | \
SNDRV_PCM_RATE_24000), \
.formats = (SNDRV_PCM_FMTBIT_S16_LE | \
- SNDRV_PCM_FMTBIT_S24_LE), \
+ SNDRV_PCM_FMTBIT_S32_LE), \
.channels_min = 1, \
.channels_max = 4, \
.rate_min = 8000, \
@@ -377,6 +377,13 @@ static int q6asm_dai_open(struct snd_soc_component *component,
stream_id = cpu_dai->driver->id;
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ /* The DSP stores 24-bit samples in the high bits of 32-bit words. */
+ ret = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+ if (ret < 0)
+ return ret;
+ }
+
pdata = snd_soc_component_get_drvdata(component);
if (!pdata) {
dev_err(dev, "Drv data not found ..\n");
@@ -506,6 +513,7 @@ static int q6asm_dai_hw_params(struct snd_soc_component *component,
prtd->bits_per_sample = 16;
break;
case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S32_LE:
prtd->bits_per_sample = 24;
break;
}
diff --git a/sound/soc/qcom/sc7180.c b/sound/soc/qcom/sc7180.c
index bc5a18883..9e4bd9855 100644
--- a/sound/soc/qcom/sc7180.c
+++ b/sound/soc/qcom/sc7180.c
@@ -13,6 +13,7 @@
#include <sound/core.h>
#include <sound/jack.h>
#include <sound/pcm.h>
+#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <uapi/linux/input-event-codes.h>
@@ -425,10 +426,17 @@ static int sc7180_qdsp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
SNDRV_PCM_HW_PARAM_RATE);
struct snd_interval *channels = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
rate->min = rate->max = 48000;
channels->min = channels->max = 2;
+ /* Keep the backend at 24 bits despite the 32-bit capture container. */
+ if (params_format(params) == SNDRV_PCM_FORMAT_S32_LE) {
+ snd_mask_none(fmt);
+ snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
+ }
+
return 0;
}
base-commit: 46fec1c3029b552ec63b2d01af9d43a386f043ea
--
2.39.5
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [PATCH] ASoC: qcom: q6asm: Correct the 24-bit capture format
2026-11-01 19:05 [PATCH] ASoC: qcom: q6asm: Correct the 24-bit capture format Michael McAllister
@ 2026-11-04 20:47 ` Demeter Barna
0 siblings, 0 replies; 2+ messages in thread
From: Demeter Barna @ 2026-11-04 20:47 UTC (permalink / raw)
To: Michael McAllister
Cc: Srinivas Kandagatla, Mark Brown, Liam Girdwood, Jaroslav Kysela,
Takashi Iwai, linux-sound, linux-arm-msm, linux-kernel
On 2026-11-01 19:05, Michael McAllister wrote:
> Recording in S24_LE on my Moto G10 produces badly scaled samples that
> wrap at full scale. The PCM V2 DSP interface places 24-bit samples in
> bits 31:8 of each 32-bit word, but ALSA S24_LE expects bits 23:0.
I see the same on SM8350 with v7.4-rc1. Without the patch, S24_LE
recordings from the DMICs wrap at full scale, while the actual signal
sits in the upper 24 bits. With it, capture offers S32_LE with 24
significant bits, the recording matches the S16_LE one, and both
PipeWire and plughw work.
Playback has the same problem: the DSP also takes 24-bit samples from
the upper 24 bits, so S24_LE playback comes out barely audible. Offering
S32_LE with 24 significant bits for playback as well brings it to the
expected level. I suspect this has gone unnoticed because userspace
hides it: distributions for these devices tend to force S16LE in their
PipeWire configuration to work around quiet audio.
Would you like to cover playback in a v2, or should I send a playback
patch on top of yours?
Tested-by: Demeter Barna <me@fromdisposition.org> # SM8350
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-11-04 20:47 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-01 19:05 [PATCH] ASoC: qcom: q6asm: Correct the 24-bit capture format Michael McAllister
2026-11-04 20:47 ` Demeter Barna
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®