* [PATCH v2 1/2] ASoC: cs43130: Drop only the clock reference hw_params() took
@ 2026-10-23 20:29 Olivier Louvignes
2026-10-23 20:29 ` [PATCH v2 2/2] ASoC: cs43130: Mute the PCM path from hw_free() to prepare() Olivier Louvignes
2026-10-26 15:05 ` [PATCH v2 1/2] ASoC: cs43130: Drop only the clock reference hw_params() took Charles Keepax
0 siblings, 2 replies; 4+ messages in thread
From: Olivier Louvignes @ 2026-10-23 20:29 UTC (permalink / raw)
To: Mark Brown
Cc: Liam Girdwood, David Rhodes, Richard Fitzgerald, Charles Keepax,
linux-sound, patches, Olivier Louvignes
cs43130_hw_params() and cs43130_dsd_hw_params() take a reference on the
internal clock (clk_req) and cs43130_hw_free() drops one, but hw_free()
can run twice for one hw_params(). When a stream's hw_params() fails,
soc_pcm_hw_params() rolls back the DAIs it had set up, the codec DAIs
included since they come before the CPU DAIs, and snd_pcm_hw_params()
then calls hw_free() once more, which reaches every DAI of the link
whether its hw_params() ran or not. Each failed hw_params() so left
clk_req one lower than it should be. An application that tries a format
the CPU DAI refuses before one it accepts then gets clk_req back to 0
after its successful hw_params(), so the next stream skips the switch
from the RC oscillator to the external MCLK.
Seen on a HiBy R1 (CS43131 on an Ingenic X1600 I2S controller): after
an S32_LE attempt the I2S controller refuses, then S24_LE, SYS_CLK_CTL_1
reads 0x06 instead of 0x04 while playing and every signal plays with
loud white noise mixed in.
Keep the DAIs holding a reference in a bitmap: a DAI takes one at most
once in hw_params() and hw_free() drops it only if held. A second
hw_params() without hw_free() no longer takes a second reference either.
Fixes: 8f1e5bf9b440 ("ASoC: cs43130: Add support for CS43130 codec")
Signed-off-by: Olivier Louvignes <olivier@mgcrea.io>
---
Changes in v2: rebased on broonie/sound for-next (8cf9607d2d85), where
v1 did not apply. The conflict was only context: the
.auto_selectable_formats lines now in the DAI ops. No change to the fix.
The double hw_free() comes from the core: soc_pcm_hw_params() rolls back
with rollback = 1, then snd_pcm_hw_params() calls hw_free() again and
soc_pcm_hw_clean() runs with rollback = 0, which reaches every DAI. Other
drivers that count references in hw_params() may see the same. If you
would rather that be fixed in soc-pcm, say so and I will look at it
instead.
Tested on v7.3 on a HiBy R1 (CS43131); build-tested with W=1 on
for-next.
sound/soc/codecs/cs43130.c | 15 +++++++++++++++
sound/soc/codecs/cs43130.h | 2 ++
2 files changed, 17 insertions(+)
diff --git a/sound/soc/codecs/cs43130.c b/sound/soc/codecs/cs43130.c
index 7bbb2e83a1ff..7fb23522f081 100644
--- a/sound/soc/codecs/cs43130.c
+++ b/sound/soc/codecs/cs43130.c
@@ -820,6 +820,9 @@ static int cs43130_dsd_hw_params(struct snd_pcm_substream *substream,
u8 dsd_speed;
scoped_guard(mutex, &cs43130->clk_mutex) {
+ if (test_and_set_bit(dai->id, &cs43130->clk_dais))
+ goto clk_held;
+
if (!cs43130->clk_req) {
/* no DAI is currently using clk */
if (!(CS43130_MCLK_22M % params_rate(params)))
@@ -838,6 +841,7 @@ static int cs43130_dsd_hw_params(struct snd_pcm_substream *substream,
if (cs43130->clk_req == 2)
cs43130_pcm_dsd_mix(true, cs43130->regmap);
}
+clk_held:
switch (params_rate(params)) {
case 176400:
@@ -883,6 +887,9 @@ static int cs43130_hw_params(struct snd_pcm_substream *substream,
u8 dsd_speed;
scoped_guard(mutex, &cs43130->clk_mutex) {
+ if (test_and_set_bit(dai->id, &cs43130->clk_dais))
+ goto clk_held;
+
if (!cs43130->clk_req) {
/* no DAI is currently using clk */
if (!(CS43130_MCLK_22M % params_rate(params)))
@@ -901,6 +908,7 @@ static int cs43130_hw_params(struct snd_pcm_substream *substream,
if (cs43130->clk_req == 2)
cs43130_pcm_dsd_mix(true, cs43130->regmap);
}
+clk_held:
switch (dai->id) {
case CS43130_ASP_DOP_DAI:
@@ -990,6 +998,13 @@ static int cs43130_hw_free(struct snd_pcm_substream *substream,
struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
guard(mutex)(&cs43130->clk_mutex);
+ /*
+ * ALSA calls hw_free() after a failed hw_params(), also when this DAI's
+ * hw_params() never ran: drop only a reference it took.
+ */
+ if (!test_and_clear_bit(dai->id, &cs43130->clk_dais))
+ return 0;
+
cs43130->clk_req--;
if (!cs43130->clk_req) {
/* no DAI is currently using clk */
diff --git a/sound/soc/codecs/cs43130.h b/sound/soc/codecs/cs43130.h
index dbdb5b262f1b..b0dba903403f 100644
--- a/sound/soc/codecs/cs43130.h
+++ b/sound/soc/codecs/cs43130.h
@@ -513,6 +513,8 @@ struct cs43130_private {
/* shared by both DAIs */
struct mutex clk_mutex;
int clk_req;
+ /* DAIs holding one of clk_req's references, by DAI id */
+ unsigned long clk_dais;
bool pll_bypass;
struct completion xtal_rdy;
struct completion pll_rdy;
base-commit: 8cf9607d2d855629260fc78e6b54ea7a98525708
--
2.39.5
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH v2 2/2] ASoC: cs43130: Mute the PCM path from hw_free() to prepare()
2026-10-23 20:29 [PATCH v2 1/2] ASoC: cs43130: Drop only the clock reference hw_params() took Olivier Louvignes
@ 2026-10-23 20:29 ` Olivier Louvignes
2026-10-26 15:02 ` Charles Keepax
2026-10-26 15:05 ` [PATCH v2 1/2] ASoC: cs43130: Drop only the clock reference hw_params() took Charles Keepax
1 sibling, 1 reply; 4+ messages in thread
From: Olivier Louvignes @ 2026-10-23 20:29 UTC (permalink / raw)
To: Mark Brown
Cc: Liam Girdwood, David Rhodes, Richard Fitzgerald, Charles Keepax,
linux-sound, patches, Olivier Louvignes
The PCM path is unmuted when DAPM powers it up and muted again only when
DAPM powers it down, pmdown_time after the stream closes. Meanwhile the
DAC plays whatever its serial port carries: on a HiBy R1 (CS43131 on an
Ingenic X1600 I2S controller), loud white noise while a stream is open
but stopped, as when paused, and until DAPM powers down after it closes.
Add a mute_stream() op, so that ASoC mutes the path at hw_free() and
unmutes it at prepare(). The op is not run at trigger time, since the
codec sits on I2C.
Signed-off-by: Olivier Louvignes <olivier@mgcrea.io>
---
Changes in v2: rebased on broonie/sound for-next, the ops now sit next to
.auto_selectable_formats. No other change.
Tested on v7.3 on a HiBy R1 (CS43131), by ear: no hiss while paused or
after the stream stops. Build-tested with W=1 on for-next.
sound/soc/codecs/cs43130.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/sound/soc/codecs/cs43130.c b/sound/soc/codecs/cs43130.c
index 7fb23522f081..919280711f8d 100644
--- a/sound/soc/codecs/cs43130.c
+++ b/sound/soc/codecs/cs43130.c
@@ -1624,6 +1624,20 @@ static int cs43130_set_sysclk(struct snd_soc_dai *codec_dai,
return 0;
}
+/*
+ * Mutes the PCM path from hw_free() to the next prepare(). Otherwise it stays
+ * unmuted until DAPM powers it down, pmdown_time after the stream closes, and
+ * plays what the serial port carries meanwhile: white noise on the HiBy R1.
+ */
+static int cs43130_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
+
+ return regmap_update_bits(cs43130->regmap, CS43130_PCM_PATH_CTL_1,
+ CS43130_MUTE_MASK, mute ? CS43130_MUTE_EN : 0);
+}
+
static const u64 cs43130_selectable_formats =
SND_SOC_POSSIBLE_DAIFMT_I2S |
SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
@@ -1640,6 +1654,8 @@ static const struct snd_soc_dai_ops cs43130_pcm_ops = {
.hw_free = cs43130_hw_free,
.set_sysclk = cs43130_set_sysclk,
.set_fmt = cs43130_pcm_set_fmt,
+ .mute_stream = cs43130_mute_stream,
+ .no_capture_mute = 1,
.auto_selectable_formats = &cs43130_selectable_formats,
.num_auto_selectable_formats = 1,
};
--
2.39.5
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [PATCH v2 2/2] ASoC: cs43130: Mute the PCM path from hw_free() to prepare()
2026-10-23 20:29 ` [PATCH v2 2/2] ASoC: cs43130: Mute the PCM path from hw_free() to prepare() Olivier Louvignes
@ 2026-10-26 15:02 ` Charles Keepax
0 siblings, 0 replies; 4+ messages in thread
From: Charles Keepax @ 2026-10-26 15:02 UTC (permalink / raw)
To: Olivier Louvignes
Cc: Mark Brown, Liam Girdwood, David Rhodes, Richard Fitzgerald,
linux-sound, patches
On Fri, Oct 23, 2026 at 10:29:41PM +0200, Olivier Louvignes wrote:
> The PCM path is unmuted when DAPM powers it up and muted again only when
> DAPM powers it down, pmdown_time after the stream closes. Meanwhile the
> DAC plays whatever its serial port carries: on a HiBy R1 (CS43131 on an
> Ingenic X1600 I2S controller), loud white noise while a stream is open
> but stopped, as when paused, and until DAPM powers down after it closes.
>
> Add a mute_stream() op, so that ASoC mutes the path at hw_free() and
> unmutes it at prepare(). The op is not run at trigger time, since the
> codec sits on I2C.
>
> Signed-off-by: Olivier Louvignes <olivier@mgcrea.io>
> ---
> Changes in v2: rebased on broonie/sound for-next, the ops now sit next to
> .auto_selectable_formats. No other change.
>
> Tested on v7.3 on a HiBy R1 (CS43131), by ear: no hiss while paused or
> after the stream stops. Build-tested with W=1 on for-next.
>
> sound/soc/codecs/cs43130.c | 16 ++++++++++++++++
> 1 file changed, 16 insertions(+)
>
> diff --git a/sound/soc/codecs/cs43130.c b/sound/soc/codecs/cs43130.c
> index 7fb23522f081..919280711f8d 100644
> --- a/sound/soc/codecs/cs43130.c
> +++ b/sound/soc/codecs/cs43130.c
> @@ -1624,6 +1624,20 @@ static int cs43130_set_sysclk(struct snd_soc_dai *codec_dai,
> return 0;
> }
>
> +/*
> + * Mutes the PCM path from hw_free() to the next prepare(). Otherwise it stays
> + * unmuted until DAPM powers it down, pmdown_time after the stream closes, and
> + * plays what the serial port carries meanwhile: white noise on the HiBy R1.
> + */
> +static int cs43130_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
> +{
> + struct snd_soc_component *component = dai->component;
> + struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
> +
> + return regmap_update_bits(cs43130->regmap, CS43130_PCM_PATH_CTL_1,
> + CS43130_MUTE_MASK, mute ? CS43130_MUTE_EN : 0);
> +}
I am slightly concerned about some variants of the chips
running additional register sequences around the mute, I
was not involved in the work on this driver so little unsure
how critical those are. Would it perhaps be slightly safer to
just remove use_pmdown_time? Thereby forcing the chip to
power down immediately.
Thanks,
Charles
> +
> static const u64 cs43130_selectable_formats =
> SND_SOC_POSSIBLE_DAIFMT_I2S |
> SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
> @@ -1640,6 +1654,8 @@ static const struct snd_soc_dai_ops cs43130_pcm_ops = {
> .hw_free = cs43130_hw_free,
> .set_sysclk = cs43130_set_sysclk,
> .set_fmt = cs43130_pcm_set_fmt,
> + .mute_stream = cs43130_mute_stream,
> + .no_capture_mute = 1,
> .auto_selectable_formats = &cs43130_selectable_formats,
> .num_auto_selectable_formats = 1,
> };
> --
> 2.39.5
>
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH v2 1/2] ASoC: cs43130: Drop only the clock reference hw_params() took
2026-10-23 20:29 [PATCH v2 1/2] ASoC: cs43130: Drop only the clock reference hw_params() took Olivier Louvignes
2026-10-23 20:29 ` [PATCH v2 2/2] ASoC: cs43130: Mute the PCM path from hw_free() to prepare() Olivier Louvignes
@ 2026-10-26 15:05 ` Charles Keepax
1 sibling, 0 replies; 4+ messages in thread
From: Charles Keepax @ 2026-10-26 15:05 UTC (permalink / raw)
To: Olivier Louvignes
Cc: Mark Brown, Liam Girdwood, David Rhodes, Richard Fitzgerald,
linux-sound, patches
On Fri, Oct 23, 2026 at 10:29:40PM +0200, Olivier Louvignes wrote:
> cs43130_hw_params() and cs43130_dsd_hw_params() take a reference on the
> internal clock (clk_req) and cs43130_hw_free() drops one, but hw_free()
> can run twice for one hw_params(). When a stream's hw_params() fails,
> soc_pcm_hw_params() rolls back the DAIs it had set up, the codec DAIs
> included since they come before the CPU DAIs, and snd_pcm_hw_params()
> then calls hw_free() once more, which reaches every DAI of the link
> whether its hw_params() ran or not. Each failed hw_params() so left
> clk_req one lower than it should be. An application that tries a format
> the CPU DAI refuses before one it accepts then gets clk_req back to 0
> after its successful hw_params(), so the next stream skips the switch
> from the RC oscillator to the external MCLK.
>
> Seen on a HiBy R1 (CS43131 on an Ingenic X1600 I2S controller): after
> an S32_LE attempt the I2S controller refuses, then S24_LE, SYS_CLK_CTL_1
> reads 0x06 instead of 0x04 while playing and every signal plays with
> loud white noise mixed in.
>
> Keep the DAIs holding a reference in a bitmap: a DAI takes one at most
> once in hw_params() and hw_free() drops it only if held. A second
> hw_params() without hw_free() no longer takes a second reference either.
>
> Fixes: 8f1e5bf9b440 ("ASoC: cs43130: Add support for CS43130 codec")
> Signed-off-by: Olivier Louvignes <olivier@mgcrea.io>
> ---
Reviewed-by: Charles Keepax <ckeepax@opensource.cirrus.com>
Thanks,
Charles
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-10-26 15:06 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-23 20:29 [PATCH v2 1/2] ASoC: cs43130: Drop only the clock reference hw_params() took Olivier Louvignes
2026-10-23 20:29 ` [PATCH v2 2/2] ASoC: cs43130: Mute the PCM path from hw_free() to prepare() Olivier Louvignes
2026-10-26 15:02 ` Charles Keepax
2026-10-26 15:05 ` [PATCH v2 1/2] ASoC: cs43130: Drop only the clock reference hw_params() took Charles Keepax
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.