mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [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®