ASoC: amd: acp-pdm: full-scale burst on every DMIC capture start
Robin Everaars <[email protected]> Wed, 05 Aug 2026 18:11:35 +0000
| Newsgroups | org.kernel.vger.linux-sound,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
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.
publickey - [email protected] - 0x8B6BA132.asc
(application/pgp-keys, 889 B)
-----BEGIN PGP PUBLIC KEY BLOCK----- Comment: https://gopenpgp.org Version: GopenPGP 2.10.0 xjMEah8lZRYJKwYBBAHaRw8BAQdAaRkzve49rBEJMKJH746RXHY+2fT77oxROi5d 8JpL+67NKXJvYmluZXZlcmFhcnNAcG0ubWUgPHJvYmluZXZlcmFhcnNAcG0ubWU+ wsARBBMWCgCDBYJqHyVlAwsJBwkQHcV/a8sGGopFFAAAAAAAHAAgc2FsdEBub3Rh dGlvbnMub3BlbnBncGpzLm9yZx6V5qS89w7CDYSIsc5NxuPJI1rgoEyY/v8dbP3H qkoNAxUKCAQWAAIBAhkBApsDAh4BFiEEi2uhMrb2XujFnFjLHcV/a8sGGooAAAZs AP9zIKWwubClFEs0J6jpQHTKXFTq+99MRkfDKqITbumQzQD/R2OazTp4oCJO2bOD NFliVAm8yXP6A+586zR2YKt0RAbOOARqHyVlEgorBgEEAZdVAQUBAQdA3p5F7b5O FsWKrSWEmEHia/oe7no/+Z1W0OPffYrDPy8DAQgHwr4EGBYKAHAFgmofJWUJEB3F f2vLBhqKRRQAAAAAABwAIHNhbHRAbm90YXRpb25zLm9wZW5wZ3Bqcy5vcmevUdR+ V+3UgvIVjqDLFOWyyGp5h4JXAPfYZsD//RHgvQKbDBYhBItroTK29l7oxZxYyx3F f2vLBhqKAABoFgEA8e4eSLSaLmv8/e2W1L9/VKAbj2Z7JES6KApi9BZ6nQgBANhZ FhMFzsyzu2YYtaB8SYtVthJJ6/eIQTT6UdQEIYsF =YKhM -----END PGP PUBLIC KEY BLOCK-----
signature.asc
(application/pgp-signature, 322 B)
-----BEGIN PGP SIGNATURE----- Version: ProtonMail wqsEARYIAF0FgmpzfNcJEB3Ff2vLBhqKNRQAAAAAABwAEHNhbHRAbm90YXRp b25zLm9wZW5wZ3Bqcy5vcme2wDcuviIR7dgQCxWvKq69FiEEi2uhMrb2XujF nFjLHcV/a8sGGooAALhmAQDJ0oaNvGvT+OhbGD1tpDFTcehtC3B8Ke1ykrV/ MAOMowEA4wBErxNIklxjQ6ODdflDulysRaKsyvjtJtP5wuU3wws= =S6uJ -----END PGP SIGNATURE-----