[RFC PATCH 5/6] ASoC: soc-pcm: Create new snd_pcm_runtime for BE DAIs

Codrin Ciubotariu
2021-05-19T10:50:24Z

The BE DAIs are different than the FE DAIs. They have different HW
capabilities, different HW constraints and different HW parameters. Also,
the buffer used to read/write data from/to user-space to/from FE DAIs has
nothing to do with the BE DAIs. For this reason, this patch creates a new
snd_pcm_runtime for the BE DAIs. The new structure can be used to better
represent the HW capabilities, so the HW parameters are no longer copied
from the FE, but created separately. For BE DAIs that need a buffer to move
the data to/from the FE DAIs (no dev-to-dev DMA capability), the new
snd_pcm_runtime can store the needed parameters to allocate the buffer and
set the DMA transfers.

Signed-off-by: Codrin Ciubotariu <codrin.ciubotariu@microchip.com>
---
sound/soc/soc-pcm.c | 126 +++++++++++++++++++++++++++++++-------------
1 file changed, 90 insertions(+), 36 deletions(-)

diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 8659089a87a0..7d95df20541e 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -1134,7 +1134,6 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe,

dpcm->be = be;
dpcm->fe = fe;
- be->dpcm[stream].runtime = fe->dpcm[stream].runtime;
dpcm->state = SND_SOC_DPCM_LINK_STATE_NEW;
spin_lock_irqsave(&fe->card->dpcm_lock, flags);
list_add(&dpcm->list_be, &fe->dpcm[stream].be_clients);
@@ -1150,34 +1149,6 @@ static int dpcm_be_connect(struct snd_soc_pcm_runtime *fe,
return 1;
}

-/* reparent a BE onto another FE */
-static void dpcm_be_reparent(struct snd_soc_pcm_runtime *fe,
- struct snd_soc_pcm_runtime *be, int stream)
-{
- struct snd_soc_dpcm *dpcm;
- struct snd_pcm_substream *fe_substream, *be_substream;
-
- /* reparent if BE is connected to other FEs */
- if (!be->dpcm[stream].users)
- return;
-
- be_substream = snd_soc_dpcm_get_substream(be, stream);
-
- for_each_dpcm_fe(be, stream, dpcm) {
- if (dpcm->fe == fe)
- continue;
-
- dev_dbg(fe->dev, "reparent %s path %s %s %s\n",
- stream ? "capture" : "playback",
- dpcm->fe->dai_link->name,
- stream ? "<-" : "->", dpcm->be->dai_link->name);
-
- fe_substream = snd_soc_dpcm_get_substream(dpcm->fe, stream);
- be_substream->runtime = fe_substream->runtime;
- break;
- }
-}
-
/* disconnect a BE and FE */
void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream)
{
@@ -1196,9 +1167,6 @@ void dpcm_be_disconnect(struct snd_soc_pcm_runtime *fe, int stream)
stream ? "capture" : "playback", fe->dai_link->name,
stream ? "<-" : "->", dpcm->be->dai_link->name);

- /* BEs still alive need new FE */
- dpcm_be_reparent(fe, dpcm->be, stream);
-
dpcm_remove_debugfs_state(dpcm);

spin_lock_irqsave(&fe->card->dpcm_lock, flags);
@@ -1468,7 +1436,12 @@ void dpcm_be_dai_stop(struct snd_soc_pcm_runtime *fe, int stream,
}

soc_pcm_close(be_substream);
- be_substream->runtime = NULL;
+
+ if (be_substream->runtime) {
+ snd_pcm_runtime_free(be_substream->runtime);
+ be_substream->runtime = NULL;
+ }
+
be->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
}
}
@@ -1482,6 +1455,7 @@ int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream)
/* only startup BE DAIs that are either sinks or sources to this FE DAI */
for_each_dpcm_be(fe, stream, dpcm) {
struct snd_pcm_substream *be_substream;
+ struct snd_pcm_runtime *runtime;

be = dpcm->be;
be_substream = snd_soc_dpcm_get_substream(be, stream);
@@ -1514,7 +1488,23 @@ int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream)
dev_dbg(be->dev, "ASoC: open %s BE %s\n",
stream ? "capture" : "playback", be->dai_link->name);

- be_substream->runtime = be->dpcm[stream].runtime;
+ runtime = snd_pcm_runtime_alloc();
+ if (!runtime) {
+ err = -ENOMEM;
+ goto unwind;
+ }
+
+ be_substream->runtime = runtime;
+
+ /* initialize the BE constraints */
+ err = snd_pcm_hw_constraints_init(be_substream);
+ if (err < 0) {
+ dev_err(be->dev,
+ "dpcm_be_hw_constraints_init failed: %d\n",
+ err);
+ goto unwind;
+ }
+
err = soc_pcm_open(be_substream);
if (err < 0) {
be->dpcm[stream].users--;
@@ -1527,6 +1517,14 @@ int dpcm_be_dai_startup(struct snd_soc_pcm_runtime *fe, int stream)
goto unwind;
}

+ err = snd_pcm_hw_constraints_complete(be_substream);
+ if (err < 0) {
+ dev_err(fe->dev,
+ "snd_pcm_hw_constraints_complete failed: %d\n",
+ err);
+ goto unwind;
+ }
+
be->dpcm[stream].state = SND_SOC_DPCM_STATE_OPEN;
count++;
}
@@ -1897,6 +1895,14 @@ int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream)
if (ret < 0)
goto unwind;

+ /* apply constrains */
+ dpcm->hw_params.rmask = ~0U;
+ ret = snd_pcm_hw_refine(be_substream, &dpcm->hw_params);
+ if (ret < 0) {
+ dev_err(fe->dev, "failed to refine hw parameters: %d\n", ret);
+ goto unwind;
+ }
+
/* copy the fixed-up hw params for BE dai */
memcpy(&be->dpcm[stream].hw_params, &dpcm->hw_params,
sizeof(struct snd_pcm_hw_params));
@@ -1918,6 +1924,7 @@ int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream)
goto unwind;

be->dpcm[stream].state = SND_SOC_DPCM_STATE_HW_PARAMS;
+ snd_pcm_runtime_set(be_substream, &dpcm->hw_params);
}
return 0;

@@ -1949,17 +1956,64 @@ int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream)
return ret;
}

+static int dpcm_be_dai_hw_params_init(struct snd_soc_pcm_runtime *fe, int stream)
+{
+ struct snd_pcm_hw_params *params = &fe->dpcm[stream].hw_params;
+ int k;
+
+ for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK;
+ k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++)
+ snd_mask_any(hw_param_mask(params, k));
+
+ for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL;
+ k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++)
+ snd_interval_any(hw_param_interval(params, k));
+
+ return 0;
+}
+
static int dpcm_fe_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *fe = asoc_substream_to_rtd(substream);
int ret, stream = substream->stream;
+ struct snd_interval *t, *dpcm_t;

mutex_lock_nested(&fe->card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);

- memcpy(&fe->dpcm[stream].hw_params, params,
- sizeof(struct snd_pcm_hw_params));
+ /* initialize the BE HW params */
+ dpcm_be_dai_hw_params_init(fe, stream);
+
+ /* FIXME: a very low period time will make the CPU take too many
+ * interrupts, which might end up not having enough time to actually
+ * fill the buffer(s); for now, the BE min period time will be half of
+ * the FE min period time
+ */
+ t = hw_param_interval(params, SNDRV_PCM_HW_PARAM_PERIOD_TIME);
+ dpcm_t = hw_param_interval(&fe->dpcm[stream].hw_params,
+ SNDRV_PCM_HW_PARAM_PERIOD_TIME);
+ dpcm_t->min = t->min / 2;
+
+ if (fe->dai_link->dpcm_merged_format) {
+ memcpy(hw_param_interval(&fe->dpcm[stream].hw_params,
+ SNDRV_PCM_HW_PARAM_FORMAT),
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_FORMAT),
+ sizeof(struct snd_interval));
+ }
+ if (fe->dai_link->dpcm_merged_chan) {
+ memcpy(hw_param_interval(&fe->dpcm[stream].hw_params,
+ SNDRV_PCM_HW_PARAM_CHANNELS),
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS),
+ sizeof(struct snd_interval));
+ }
+ if (fe->dai_link->dpcm_merged_rate) {
+ memcpy(hw_param_interval(&fe->dpcm[stream].hw_params,
+ SNDRV_PCM_HW_PARAM_RATE),
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE),
+ sizeof(struct snd_interval));
+ }
+
ret = dpcm_be_dai_hw_params(fe, stream);
if (ret < 0)
goto out;
--
2.27.0