The Linux Kernel Mailing List
 help / color / mirror / Atom feed
* ASoC: amd: acp-pdm: full-scale burst on every DMIC capture start
@ 2026-08-05 18:11 Robin Everaars
  2026-08-10  5:11 ` Mukunda,Vijendar
  0 siblings, 1 reply; 2+ messages in thread
From: Robin Everaars @ 2026-08-05 18:11 UTC (permalink / raw)
  To: Vijendar.Mukunda, linux-sound
  Cc: venkataprasad.potturu, lgirdwood, broonie, linux-kernel


[-- Attachment #1.1: Type: text/plain, Size: 4682 bytes --]

The ACP PDM DMIC emits a full-scale burst at the start of every capture stream.
It is loud and it begins at the same frame every time. On a cold start it lasts
long enough that the far end of a call hears a click on join.

Hardware and software
=====================

ASUS ProArt PX13 HN7306EAC, AMD Strix Halo. Card 1 "amdsoundwire", driver
amd-soundwire, longname
ASUSTeKCOMPUTERINC.-ProArtPX13HN7306EAC-1.0-HN7306EAC. The DMIC array is
/proc/asound/card1 pcm4c, id "acp-dmic-codec dmic-hifi-4", reachable as
hw:1,4 or hw:amdsoundwire,4. Native format S32_LE, 48000 Hz, 2 channels,
single rate. Kernel 7.1.5, alsa-lib and alsa-utils 1.2.16, PipeWire 1.6.8,
NixOS.

What the burst looks like
=========================

Capturing 3 s of a quiet room at 48 kHz gives 304 to 310 samples railed to
+32767 or -32768, and every one of them falls inside frames 21 to 177, i.e.
the first 0.44 to 3.65 ms of the stream. After the first 10 ms there are zero
clipped samples.

The shape 
rules out an analog transient. Printing the first 40 frames shows an
exponentially growing alternating pattern that collapses into a rail, that is
a Nyquist-rate oscillation at 0 dBFS:

  (336, 2529) (311, 2454) ... (4765, 1614) (-14926, -7363) (-32768, -32768)
  (3598, -3283) (-817, 2296) (-32768, 32767) (32767, -32768) (-32768, 32767)
  (32767, -32768) (-32768, 32767) ...

Left and right are in antiphase. That reads as an unflushed decimator rather
than anything acoustic.

Reproducible frame for frame across runs, and across every rate, format and
channel count the PCM accepts.

The cold start is much worse, and it is the common case
=======================================================

The 3.6 ms figure above is the WARM case, where the ACP was already awake. On a
cold start, meaning the ACP had runtime-suspended, a second and far larger
excursion follows: it rails from about 15 ms to about 50 ms and only reaches the
noise floor around 250 ms.

That is the c
ase that matters in practice. Any userspace audio server suspends
an idle capture node after a few seconds (WirePlumber does it after 5 s), so
every real call join is a cold start.

The driver produces it
======================

Identical through plain arecord with PipeWire out of the picture:

  arecord -D hw:1,4 -f S32_LE -r 48000 -c 2 -d 6 -t wav /tmp/x.wav

gives 304 railed samples in frames 21 to 177, against 305 to 307 through
PipeWire. Same start frame, same duration, same shape.

It also survives band-limiting. Low-passing to 8 kHz and to 4 kHz still leaves
an audible click, so a call codec does not remove it.

Where it comes from
===================

sound/soc/amd/acp/acp-pdm.c has no settling delay, no mute and no discard of
the first frames. acp_dmic_dai_trigger() enables the PDM controller and the DMA
in the same breath, so the decimator's first output words go straight to
userspace. A PDM microphone needs time to settle after its clock starts, and a

CIC or decimation filter chain needs to flush its state, and neither is waited
for here.

Other ASoC DMIC paths handle this. snd_soc_dmic has a wakeup_delay module
parameter whose msleep runs at DAPM POST_PMU for exactly this reason.

What would fix it
=================

Any of these, in rough order of preference:

1. Discard or mute the first N frames after PDM_ENABLE inside acp-pdm.c, where N
   covers the decimator's own settling. That is the cheapest fix and it is
   invisible to userspace.
2. Wait for the microphone to settle between enabling the PDM clock and enabling
   the DMA, rather than doing both in acp_dmic_dai_trigger().
3. Expose a settling delay the way snd_soc_dmic does, so a platform that needs
   longer can set it.

A separate, smaller thing on the same device
============================================

There is a standing DC offset of about +0.06 FS on channel 0, roughly -24 dBFS,
present with no acoustic input. It is passed through to user
space, so naive
level meters read about -23 dBFS in a silent room and any AGC treats it as
signal. After an 80 Hz high-pass the real floor is peak -45 dBFS rms -62 dBFS.

If a DC blocker is expected somewhere in this path, it is not running here.

Reproducer
==========

  # cold start: let the ACP runtime-suspend first, then capture
  cat /sys/bus/pci/devices/*/power/runtime_status   # wait for suspended
  arecord -D hw:1,4 -f S32_LE -r 48000 -c 2 -d 3 -t wav /tmp/cold.wav

Then look at the first 300 ms. Any tool will do; the railed samples are obvious.
Repeating the capture immediately afterwards gives the shorter warm-start
version, which is a convenient way to see both.

I have no fix to offer, only the measurements. Happy to test a patch on this
hardware.

[-- Attachment #1.2: publickey - robineveraars@pm.me - 0x8B6BA132.asc --]
[-- Type: application/pgp-keys, Size: 889 bytes --]

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 322 bytes --]

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-08-10  5:11 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-05 18:11 ASoC: amd: acp-pdm: full-scale burst on every DMIC capture start Robin Everaars
2026-08-10  5:11 ` Mukunda,Vijendar

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox