ASoC: apple: mca: Add locking
authorMartin Povišer <povik+lin@cutebit.org>
Wed, 24 Aug 2022 16:07:15 +0000 (18:07 +0200)
committerMark Brown <broonie@kernel.org>
Thu, 25 Aug 2022 12:51:35 +0000 (13:51 +0100)
In DAI ops, accesses to the native cluster (of the DAI), and to data of
clusters related to it by a DPCM frontend-backend link, should have
been synchronized by the 'pcm_mutex' lock at ASoC level.

What is not covered are the 'port_driver' accesses on foreign clusters
to which the current cluster has no a priori relation, so fill in
locking for that. (This should only matter in bizarre configurations of
sharing one MCA peripheral between ASoC cards.)

Signed-off-by: Martin Povišer <povik+lin@cutebit.org>
Link: https://lore.kernel.org/r/20220824160715.95779-5-povik+lin@cutebit.org
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/apple/mca.c

index 807b854..aa67d57 100644 (file)
@@ -158,6 +158,9 @@ struct mca_data {
        struct reset_control *rstc;
        struct device_link *pd_link;
 
+       /* Mutex for accessing port_driver of foreign clusters */
+       struct mutex port_mutex;
+
        int nclusters;
        struct mca_cluster clusters[];
 };
@@ -296,16 +299,21 @@ static bool mca_fe_clocks_in_use(struct mca_cluster *cl)
        struct mca_cluster *be_cl;
        int stream, i;
 
+       mutex_lock(&mca->port_mutex);
        for (i = 0; i < mca->nclusters; i++) {
                be_cl = &mca->clusters[i];
 
                if (be_cl->port_driver != cl->no)
                        continue;
 
-               for_each_pcm_streams(stream)
-                       if (be_cl->clocks_in_use[stream])
+               for_each_pcm_streams(stream) {
+                       if (be_cl->clocks_in_use[stream]) {
+                               mutex_unlock(&mca->port_mutex);
                                return true;
+                       }
+               }
        }
+       mutex_unlock(&mca->port_mutex);
        return false;
 }
 
@@ -349,6 +357,11 @@ static int mca_be_hw_free(struct snd_pcm_substream *substream,
        if (cl->port_driver < 0)
                return -EINVAL;
 
+       /*
+        * We are operating on a foreign cluster here, but since we
+        * belong to the same PCM, accesses should have been
+        * synchronized at ASoC level.
+        */
        fe_cl = &mca->clusters[cl->port_driver];
        if (!mca_fe_clocks_in_use(fe_cl))
                return 0; /* Nothing to do */
@@ -721,7 +734,9 @@ static int mca_be_startup(struct snd_pcm_substream *substream,
                       cl->base + REG_PORT_CLOCK_SEL);
        writel_relaxed(PORT_DATA_SEL_TXA(fe_cl->no),
                       cl->base + REG_PORT_DATA_SEL);
+       mutex_lock(&mca->port_mutex);
        cl->port_driver = fe_cl->no;
+       mutex_unlock(&mca->port_mutex);
        cl->port_started[substream->stream] = true;
 
        return 0;
@@ -731,6 +746,7 @@ static void mca_be_shutdown(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;
 
        cl->port_started[substream->stream] = false;
 
@@ -741,7 +757,9 @@ static void mca_be_shutdown(struct snd_pcm_substream *substream,
                 */
                writel_relaxed(0, cl->base + REG_PORT_ENABLES);
                writel_relaxed(0, cl->base + REG_PORT_DATA_SEL);
+               mutex_lock(&mca->port_mutex);
                cl->port_driver = -1;
+               mutex_unlock(&mca->port_mutex);
        }
 }
 
@@ -962,6 +980,7 @@ static int apple_mca_probe(struct platform_device *pdev)
                return -ENOMEM;
        mca->dev = &pdev->dev;
        mca->nclusters = nclusters;
+       mutex_init(&mca->port_mutex);
        platform_set_drvdata(pdev, mca);
        clusters = mca->clusters;