[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20250518-mca-fixes-v1-6-ee1015a695f6@gmail.com>
Date: Sun, 18 May 2025 20:50:51 +1000
From: James Calligeros <jcalligeros99@...il.com>
To: Martin Povišer <povik+lin@...ebit.org>,
Liam Girdwood <lgirdwood@...il.com>, Mark Brown <broonie@...nel.org>,
Jaroslav Kysela <perex@...ex.cz>, Takashi Iwai <tiwai@...e.com>
Cc: asahi@...ts.linux.dev, linux-sound@...r.kernel.org,
linux-kernel@...r.kernel.org, James Calligeros <jcalligeros99@...il.com>
Subject: [PATCH 6/9] ASoC: apple: mca: Support FEs being clock consumers
From: Martin Povišer <povik+lin@...ebit.org>
Support FEs being I2S clock consumers. This does not mean we support
accepting clocks from outside the SoC (although it paves the way for
that support in the future), but it means multiple FEs can attach to one
BE, one being clock producer and the rest clock consumers.
This is useful for grabbing I/V sense data on some machines, since in
such a scenario the format of the sense data on the I2S bus differs
from that of the audio data (the two formats differing in slot width).
With two FEs attached to the bus, we can split the responsibilities and
command different slot widths to the two.
Signed-off-by: Martin Povišer <povik+lin@...ebit.org>
Signed-off-by: James Calligeros <jcalligeros99@...il.com>
---
sound/soc/apple/mca.c | 125 ++++++++++++++++++++-----
1 file changed, 104 insertions(+), 21 deletions(-)
diff --git a/sound/soc/apple/mca.c b/sound/soc/apple/mca.c
index 7c3e1eae8b963ff593fdcf54e4a1f2e5f64a63c1..be6ff55203121808463846bebda682cdd97fc42d 100644
--- a/sound/soc/apple/mca.c
+++ b/sound/soc/apple/mca.c
@@ -134,12 +134,17 @@ struct mca_cluster {
struct clk *clk_parent;
struct dma_chan *dma_chans[SNDRV_PCM_STREAM_LAST + 1];
+ bool clk_provider;
+
bool port_clk_started[SNDRV_PCM_STREAM_LAST + 1];
int port_clk_driver; /* The cluster driving this cluster's port */
bool clocks_in_use[SNDRV_PCM_STREAM_LAST + 1];
struct device_link *pd_link;
+ /* In case of clock consumer FE */
+ int syncgen_in_use;
+
unsigned int bclk_ratio;
/* Masks etc. picked up via the set_tdm_slot method */
@@ -262,11 +267,32 @@ static int mca_fe_trigger(struct snd_pcm_substream *substream, int cmd,
return 0;
}
+static int mca_fe_get_port(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_pcm_runtime *be;
+ struct snd_soc_dpcm *dpcm;
+
+ be = NULL;
+ for_each_dpcm_be(fe, substream->stream, dpcm) {
+ be = dpcm->be;
+ break;
+ }
+
+ if (!be)
+ return -EINVAL;
+
+ return mca_dai_to_cluster(snd_soc_rtd_to_cpu(be, 0))->no;
+}
+
static int mca_fe_enable_clocks(struct mca_cluster *cl)
{
struct mca_data *mca = cl->host;
int ret;
+ if (!cl->clk_provider)
+ return -EINVAL;
+
ret = clk_prepare_enable(cl->clk_parent);
if (ret) {
dev_err(mca->dev,
@@ -340,7 +366,7 @@ static int mca_be_prepare(struct snd_pcm_substream *substream,
int ret;
if (cl->port_clk_driver < 0)
- return -EINVAL;
+ return 0;
fe_cl = &mca->clusters[cl->port_clk_driver];
@@ -361,6 +387,57 @@ static int mca_be_prepare(struct snd_pcm_substream *substream,
return 0;
}
+static int mca_fe_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;
+
+ if (cl->clk_provider)
+ return 0;
+
+ /* Turn on the cluster power domain if not already in use */
+ if (!cl->syncgen_in_use) {
+ int port = mca_fe_get_port(substream);
+
+ cl->pd_link = device_link_add(mca->dev, cl->pd_dev,
+ DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME |
+ DL_FLAG_RPM_ACTIVE);
+ if (!cl->pd_link) {
+ dev_err(mca->dev,
+ "cluster %d: unable to prop-up power domain\n", cl->no);
+ return -EINVAL;
+ }
+
+ writel_relaxed(port + 6 + 1,
+ cl->base + REG_SYNCGEN_MCLK_SEL);
+ mca_modify(cl, REG_SYNCGEN_STATUS, SYNCGEN_STATUS_EN,
+ SYNCGEN_STATUS_EN);
+ }
+ cl->syncgen_in_use |= 1 << substream->stream;
+
+ return 0;
+}
+
+static int mca_fe_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mca_cluster *cl = mca_dai_to_cluster(dai);
+
+ if (cl->clk_provider)
+ return 0;
+
+ cl->syncgen_in_use &= ~(1 << substream->stream);
+ if (cl->syncgen_in_use)
+ return 0;
+
+ mca_modify(cl, REG_SYNCGEN_STATUS, SYNCGEN_STATUS_EN, 0);
+ if (cl->pd_link)
+ device_link_del(cl->pd_link);
+
+ return 0;
+}
+
static unsigned int mca_crop_mask(unsigned int mask, int nchans)
{
while (hweight32(mask) > nchans)
@@ -486,9 +563,18 @@ static int mca_fe_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
u32 serdes_conf = 0;
u32 bitstart;
- if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) !=
- SND_SOC_DAIFMT_BP_FP)
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BP_FP:
+ cl->clk_provider = true;
+ break;
+
+ case SND_SOC_DAIFMT_BC_FC:
+ cl->clk_provider = false;
+ break;
+
+ default:
goto err;
+ }
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
@@ -545,24 +631,6 @@ static int mca_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
return 0;
}
-static int mca_fe_get_port(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *fe = snd_soc_substream_to_rtd(substream);
- struct snd_soc_pcm_runtime *be;
- struct snd_soc_dpcm *dpcm;
-
- be = NULL;
- for_each_dpcm_be(fe, substream->stream, dpcm) {
- be = dpcm->be;
- break;
- }
-
- if (!be)
- return -EINVAL;
-
- return mca_dai_to_cluster(snd_soc_rtd_to_cpu(be, 0))->no;
-}
-
static int mca_fe_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@@ -687,6 +755,8 @@ static const struct snd_soc_dai_ops mca_fe_ops = {
.set_tdm_slot = mca_fe_set_tdm_slot,
.hw_params = mca_fe_hw_params,
.trigger = mca_fe_trigger,
+ .prepare = mca_fe_prepare,
+ .hw_free = mca_fe_hw_free,
};
/*
@@ -740,6 +810,9 @@ static int mca_be_startup(struct snd_pcm_substream *substream,
PORT_ENABLES_TX_DATA);
}
+ if (!fe_cl->clk_provider)
+ return 0;
+
if (mca_be_clk_started(cl)) {
/*
* Port is already started in the other direction.
@@ -769,7 +842,10 @@ static int mca_be_startup(struct snd_pcm_substream *substream,
static void mca_be_shutdown(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_cluster *fe_cl;
struct mca_data *mca = cl->host;
if (cl->clocks_in_use[substream->stream] &&
@@ -792,11 +868,18 @@ static void mca_be_shutdown(struct snd_pcm_substream *substream,
mca_fe_disable_clocks(fe_cl);
}
+ if (!fe)
+ return;
+ fe_cl = mca_dai_to_cluster(snd_soc_rtd_to_cpu(fe, 0));
+
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
mca_modify(cl, REG_PORT_ENABLES, PORT_ENABLES_TX_DATA, 0);
writel_relaxed(0, cl->base + REG_PORT_DATA_SEL);
}
+ if (!fe_cl->clk_provider)
+ return;
+
cl->port_clk_started[substream->stream] = false;
if (!mca_be_clk_started(cl)) {
/*
--
2.49.0
Powered by blists - more mailing lists