[PATCH v2 6/7] ASoC: apple: mca: Do not mark clocks in use for non-providers

From: James Calligeros

Date: Sun Jun 28 2026 - 07:46:10 EST


From: Hector Martin <marcan@xxxxxxxxx>

On the speakers PCM, this sequence:

1. Open playback
2. Open sense
3. Close playback
4. Close sense

would result in the sense FE being marked as clocks in use at (2), since
there is a clock provider (playback FE). Then at (4) this would WARN since
there is no driver any more when closing the in use clocks.

If (1) and (2) are reversed this does not happen, since the sense PCM is
not marked as using the clocks when there is no provider yet. So, check
explicitly whether the substream FE is a clock provider in be_prepare,
and skip everything if it isn't.

Signed-off-by: Hector Martin <marcan@xxxxxxxxx>
Signed-off-by: James Calligeros <jcalligeros99@xxxxxxxxx>
---
sound/soc/apple/mca.c | 67 ++++++++++++++-----------
1 file changed, 37 insertions(+), 30 deletions(-)

diff --git a/sound/soc/apple/mca.c b/sound/soc/apple/mca.c
index 5b5f2f27b152..a1da01b0be32 100644
--- a/sound/soc/apple/mca.c
+++ b/sound/soc/apple/mca.c
@@ -346,36 +346,6 @@ static bool mca_fe_clocks_in_use(struct mca_cluster *cl)
return false;
}

-static int mca_be_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct mca_cluster *cl = mca_dai_to_cluster(dai);
- struct mca_data *mca = cl->host;
- struct mca_cluster *fe_cl;
- int ret;
-
- if (cl->port_clk_driver < 0)
- return 0;
-
- fe_cl = &mca->clusters[cl->port_clk_driver];
-
- /*
- * Typically the CODECs we are paired with will require clocks
- * to be present at time of unmute with the 'mute_stream' op
- * or at time of DAPM widget power-up. We need to enable clocks
- * here at the latest (frontend prepare would be too late).
- */
- if (!mca_fe_clocks_in_use(fe_cl)) {
- ret = mca_fe_enable_clocks(fe_cl);
- if (ret < 0)
- return ret;
- }
-
- cl->clocks_in_use[substream->stream] = true;
-
- return 0;
-}
-
static int mca_be_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
@@ -806,6 +776,43 @@ static struct snd_soc_pcm_runtime *mca_be_get_fe(struct snd_soc_pcm_runtime *be,
return fe;
}

+static int mca_be_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *be = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_pcm_runtime *fe = mca_be_get_fe(be, substream->stream);
+ struct mca_cluster *cl = mca_dai_to_cluster(dai);
+ struct mca_data *mca = cl->host;
+ struct mca_cluster *fe_cl, *fe_clk_cl;
+ int ret;
+
+ fe_cl = mca_dai_to_cluster(snd_soc_rtd_to_cpu(fe, 0));
+
+ if (!fe_cl->clk_provider)
+ return 0;
+
+ if (cl->port_clk_driver < 0)
+ return 0;
+
+ fe_clk_cl = &mca->clusters[cl->port_clk_driver];
+
+ /*
+ * Typically the CODECs we are paired with will require clocks
+ * to be present at time of unmute with the 'mute_stream' op
+ * or at time of DAPM widget power-up. We need to enable clocks
+ * here at the latest (frontend prepare would be too late).
+ */
+ if (!mca_fe_clocks_in_use(fe_clk_cl)) {
+ ret = mca_fe_enable_clocks(fe_clk_cl);
+ if (ret < 0)
+ return ret;
+ }
+
+ cl->clocks_in_use[substream->stream] = true;
+
+ return 0;
+}
+
static int mca_be_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{

--
2.54.0