Commit 84e3cfd1 authored by Pierre-Louis Bossart's avatar Pierre-Louis Bossart Committed by Mark Brown

ASoC: SOF: Intel: hda-dai: improve SSP DAI handling for dynamic pipelines

In order to keep the widget use_count balanced, make sure the DAI
widgets are allocated once in hw_params and released in hw_free. A
'setup' status flag is used to deal with cases where the .hw_params
callback is invoked multiple times, and likewise with cases where
hw_free is invoked without hw_params being called first (which can
happen if the FE hw_params fails).

In addition, this patch frees the widgets in the suspend transition,
and reallocates them in the .prepare callback. The 'setup' flag helps
in this case differentiate between resume (setup needed) and
xruns (setup not needed).

This balanced operation was not needed previously but will be required
when SOF dynamic pipelines are enabled.
Co-developed-by: default avatarRanjani Sridharan <ranjani.sridharan@linux.intel.com>
Signed-off-by: default avatarRanjani Sridharan <ranjani.sridharan@linux.intel.com>
Signed-off-by: default avatarPierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Reviewed-by: default avatarRanjani Sridharan <ranjani.sridharan@linux.intel.com>
Reviewed-by: default avatarBard Liao <bard.liao@intel.com>
Reviewed-by: default avatarGuennadi Liakhovetski <guennadi.liakhovetski@linux.intel.com>
Reviewed-by: default avatarPéter Ujfalusi <peter.ujfalusi@linux.intel.com>
Link: https://lore.kernel.org/r/20211004171430.103674-6-pierre-louis.bossart@linux.intel.comSigned-off-by: default avatarMark Brown <broonie@kernel.org>
parent 68776b2f
...@@ -440,6 +440,11 @@ static const struct snd_soc_dai_ops hda_link_dai_ops = { ...@@ -440,6 +440,11 @@ static const struct snd_soc_dai_ops hda_link_dai_ops = {
#endif #endif
/* only one flag used so far to harden hw_params/hw_free/trigger/prepare */
struct ssp_dai_dma_data {
bool setup;
};
static int ssp_dai_setup_or_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai, static int ssp_dai_setup_or_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai,
bool setup) bool setup)
{ {
...@@ -469,22 +474,95 @@ static int ssp_dai_setup_or_free(struct snd_pcm_substream *substream, struct snd ...@@ -469,22 +474,95 @@ static int ssp_dai_setup_or_free(struct snd_pcm_substream *substream, struct snd
return hda_ctrl_dai_widget_free(w); return hda_ctrl_dai_widget_free(w);
} }
static int ssp_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct ssp_dai_dma_data *dma_data;
dma_data = kzalloc(sizeof(*dma_data), GFP_KERNEL);
if (!dma_data)
return -ENOMEM;
snd_soc_dai_set_dma_data(dai, substream, dma_data);
return 0;
}
static int ssp_dai_setup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai,
bool setup)
{
struct ssp_dai_dma_data *dma_data;
int ret = 0;
dma_data = snd_soc_dai_get_dma_data(dai, substream);
if (!dma_data) {
dev_err(dai->dev, "%s: failed to get dma_data\n", __func__);
return -EIO;
}
if (dma_data->setup != setup) {
ret = ssp_dai_setup_or_free(substream, dai, setup);
if (!ret)
dma_data->setup = setup;
}
return ret;
}
static int ssp_dai_hw_params(struct snd_pcm_substream *substream, static int ssp_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
return ssp_dai_setup_or_free(substream, dai, true); /* params are ignored for now */
return ssp_dai_setup(substream, dai, true);
}
static int ssp_dai_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
/*
* the SSP will only be reconfigured during resume operations and
* not in case of xruns
*/
return ssp_dai_setup(substream, dai, true);
}
static int ssp_dai_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *dai)
{
if (cmd != SNDRV_PCM_TRIGGER_SUSPEND)
return 0;
return ssp_dai_setup(substream, dai, false);
} }
static int ssp_dai_hw_free(struct snd_pcm_substream *substream, static int ssp_dai_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
return ssp_dai_setup_or_free(substream, dai, false); return ssp_dai_setup(substream, dai, false);
}
static void ssp_dai_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct ssp_dai_dma_data *dma_data;
dma_data = snd_soc_dai_get_dma_data(dai, substream);
if (!dma_data) {
dev_err(dai->dev, "%s: failed to get dma_data\n", __func__);
return;
}
snd_soc_dai_set_dma_data(dai, substream, NULL);
kfree(dma_data);
} }
static const struct snd_soc_dai_ops ssp_dai_ops = { static const struct snd_soc_dai_ops ssp_dai_ops = {
.startup = ssp_dai_startup,
.hw_params = ssp_dai_hw_params, .hw_params = ssp_dai_hw_params,
.prepare = ssp_dai_prepare,
.trigger = ssp_dai_trigger,
.hw_free = ssp_dai_hw_free, .hw_free = ssp_dai_hw_free,
.shutdown = ssp_dai_shutdown,
}; };
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment