Commit 27547a39 authored by Mark Brown's avatar Mark Brown

Merge series "Add support for on demand pipeline setup/destroy" from Peter...

Merge series "Add support for on demand pipeline setup/destroy" from Peter Ujfalusi <peter.ujfalusi@linux.intel.com>:

Hi,

The previous, v2 of this series was sent by Daniel Baluta:
https://lore.kernel.org/alsa-devel/20210917143659.401102-1-daniel.baluta@oss.nxp.com/

We have agreed that it might be better that someone from Intel is going to take it
from here as we already have the infrastructure up to test and verify the
dynamic pipelines support.

Changes since v2 (sent by Daniel Baluta):
- patch 10: Fix NULL point dereference in hda_dai_update_config()
- I have kept Daniel's SoB for the series.

Changes since v1:
- Signed-off-by tag added by Daniel

This series implements initial support for dynamic pipelines to setup/teardown
pipeline as needed when a PCM is open/closed.

Initially dynamic pipelines are only supported with single core setup which will
be expanded with a follow-up series.

Review with SOF community at
https://github.com/thesofproject/linux/pull/2794

The feature has been merged on 1st of April to sof-dev, all issues found since
has been fixed and squashed to this upstream series.

Regards,
Peter
---
Ranjani Sridharan (12):
  ASoC: topology: change the complete op in snd_soc_tplg_ops to return
    int
  ASoC: SOF: control: Add access field in struct snd_sof_control
  ASoC: SOF: topology: Add new token for dynamic pipeline
  ASoC: SOF: sof-audio: add helpers for widgets, kcontrols and dai
    config set up
  AsoC: dapm: export a couple of functions
  ASoC: SOF: Add new fields to snd_sof_route
  ASoC: SOF: restore kcontrols for widget during set up
  ASoC: SOF: Don't set up widgets during topology parsing
  ASoC: SOF: Introduce widget use_count
  ASoC: SOF: Intel: hda: make sure DAI widget is set up before IPC
  ASoC: SOF: Add support for dynamic pipelines
  ASoC: SOF: topology: Add kernel parameter for topology verification

 include/sound/soc-dpcm.h               |   1 +
 include/sound/soc-topology.h           |   2 +-
 include/uapi/sound/sof/tokens.h        |   1 +
 sound/soc/intel/skylake/skl-topology.c |   6 +-
 sound/soc/soc-dapm.c                   |   2 +
 sound/soc/soc-pcm.c                    |   4 +-
 sound/soc/soc-topology.c               |  10 +-
 sound/soc/sof/intel/hda-dai.c          | 174 +++---
 sound/soc/sof/intel/hda.c              | 177 ++++--
 sound/soc/sof/intel/hda.h              |   5 +
 sound/soc/sof/ipc.c                    |  22 +
 sound/soc/sof/pcm.c                    |  58 +-
 sound/soc/sof/pm.c                     |   4 +-
 sound/soc/sof/sof-audio.c              | 709 +++++++++++++++++++------
 sound/soc/sof/sof-audio.h              |  32 +-
 sound/soc/sof/sof-priv.h               |   1 +
 sound/soc/sof/topology.c               | 362 +++++--------
 17 files changed, 1032 insertions(+), 538 deletions(-)

--
2.33.0
parents 83bea088 c0e7969c
......@@ -159,6 +159,7 @@ int dpcm_be_dai_trigger(struct snd_soc_pcm_runtime *fe, int stream, int cmd);
int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream);
int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir,
int event);
bool dpcm_end_walk_at_be(struct snd_soc_dapm_widget *widget, enum snd_soc_dapm_direction dir);
#define dpcm_be_dai_startup_rollback(fe, stream, last) \
dpcm_be_dai_stop(fe, stream, 0, last)
......
......@@ -151,7 +151,7 @@ struct snd_soc_tplg_ops {
struct snd_soc_tplg_hdr *);
/* completion - called at completion of firmware loading */
void (*complete)(struct snd_soc_component *);
int (*complete)(struct snd_soc_component *comp);
/* manifest - optional to inform component of manifest */
int (*manifest)(struct snd_soc_component *, int index,
......
......@@ -51,6 +51,7 @@
#define SOF_TKN_SCHED_CORE 203
#define SOF_TKN_SCHED_FRAMES 204
#define SOF_TKN_SCHED_TIME_DOMAIN 205
#define SOF_TKN_SCHED_DYNAMIC_PIPELINE 206
/* volume */
#define SOF_TKN_VOLUME_RAMP_STEP_TYPE 250
......
......@@ -3637,7 +3637,7 @@ static int skl_manifest_load(struct snd_soc_component *cmpnt, int index,
return 0;
}
static void skl_tplg_complete(struct snd_soc_component *component)
static int skl_tplg_complete(struct snd_soc_component *component)
{
struct snd_soc_dobj *dobj;
struct snd_soc_acpi_mach *mach;
......@@ -3646,7 +3646,7 @@ static void skl_tplg_complete(struct snd_soc_component *component)
val = kmalloc(sizeof(*val), GFP_KERNEL);
if (!val)
return;
return -ENOMEM;
mach = dev_get_platdata(component->card->dev);
list_for_each_entry(dobj, &component->dobj_list, list) {
......@@ -3671,7 +3671,9 @@ static void skl_tplg_complete(struct snd_soc_component *component)
}
}
}
kfree(val);
return 0;
}
static struct snd_soc_tplg_ops skl_tplg_ops = {
......
......@@ -1331,11 +1331,13 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
return paths;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_dai_get_connected_widgets);
void snd_soc_dapm_dai_free_widgets(struct snd_soc_dapm_widget_list **list)
{
dapm_widget_list_free(list);
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_dai_free_widgets);
/*
* Handler for regulator supply widget.
......
......@@ -1262,8 +1262,7 @@ static int widget_in_list(struct snd_soc_dapm_widget_list *list,
return 0;
}
static bool dpcm_end_walk_at_be(struct snd_soc_dapm_widget *widget,
enum snd_soc_dapm_direction dir)
bool dpcm_end_walk_at_be(struct snd_soc_dapm_widget *widget, enum snd_soc_dapm_direction dir)
{
struct snd_soc_card *card = widget->dapm->card;
struct snd_soc_pcm_runtime *rtd;
......@@ -1281,6 +1280,7 @@ static bool dpcm_end_walk_at_be(struct snd_soc_dapm_widget *widget,
return false;
}
EXPORT_SYMBOL_GPL(dpcm_end_walk_at_be);
int dpcm_path_get(struct snd_soc_pcm_runtime *fe,
int stream, struct snd_soc_dapm_widget_list **list)
......
......@@ -78,7 +78,7 @@ struct soc_tplg {
};
static int soc_tplg_process_headers(struct soc_tplg *tplg);
static void soc_tplg_complete(struct soc_tplg *tplg);
static int soc_tplg_complete(struct soc_tplg *tplg);
/* check we dont overflow the data for this control chunk */
static int soc_tplg_check_elem_count(struct soc_tplg *tplg, size_t elem_size,
......@@ -312,10 +312,12 @@ static int soc_tplg_dai_link_load(struct soc_tplg *tplg,
}
/* tell the component driver that all firmware has been loaded in this request */
static void soc_tplg_complete(struct soc_tplg *tplg)
static int soc_tplg_complete(struct soc_tplg *tplg)
{
if (tplg->ops && tplg->ops->complete)
tplg->ops->complete(tplg->comp);
return tplg->ops->complete(tplg->comp);
return 0;
}
/* add a dynamic kcontrol */
......@@ -2625,7 +2627,7 @@ static int soc_tplg_load(struct soc_tplg *tplg)
ret = soc_tplg_process_headers(tplg);
if (ret == 0)
soc_tplg_complete(tplg);
return soc_tplg_complete(tplg);
return ret;
}
......
......@@ -155,49 +155,68 @@ static int hda_link_dma_params(struct hdac_ext_stream *stream,
return 0;
}
/* Send DAI_CONFIG IPC to the DAI that matches the dai_name and direction */
static int hda_link_config_ipc(struct sof_intel_hda_stream *hda_stream,
const char *dai_name, int channel, int dir)
/* Update config for the DAI widget */
static struct sof_ipc_dai_config *hda_dai_update_config(struct snd_soc_dapm_widget *w,
int channel)
{
struct snd_sof_widget *swidget = w->dobj.private;
struct sof_ipc_dai_config *config;
struct snd_sof_dai *sof_dai;
struct sof_ipc_reply reply;
int ret = 0;
list_for_each_entry(sof_dai, &hda_stream->sdev->dai_list, list) {
if (!sof_dai->cpu_dai_name)
continue;
if (!swidget)
return NULL;
if (!strcmp(dai_name, sof_dai->cpu_dai_name) &&
dir == sof_dai->comp_dai.direction) {
config = sof_dai->dai_config;
sof_dai = swidget->private;
if (!config) {
dev_err(hda_stream->sdev->dev,
"error: no config for DAI %s\n",
sof_dai->name);
return -EINVAL;
if (!sof_dai || !sof_dai->dai_config) {
dev_err(swidget->scomp->dev, "error: No config for DAI %s\n", w->name);
return NULL;
}
config = &sof_dai->dai_config[sof_dai->current_config];
/* update config with stream tag */
config->hda.link_dma_ch = channel;
/* send IPC */
ret = sof_ipc_tx_message(hda_stream->sdev->ipc,
config->hdr.cmd,
config,
config->hdr.size,
&reply, sizeof(reply));
return config;
}
if (ret < 0)
dev_err(hda_stream->sdev->dev,
"error: failed to set dai config for %s\n",
sof_dai->name);
return ret;
static int hda_link_config_ipc(struct sof_intel_hda_stream *hda_stream,
struct snd_soc_dapm_widget *w, int channel)
{
struct snd_sof_dev *sdev = hda_stream->sdev;
struct sof_ipc_dai_config *config;
struct sof_ipc_reply reply;
config = hda_dai_update_config(w, channel);
if (!config) {
dev_err(sdev->dev, "error: no config for DAI %s\n", w->name);
return -ENOENT;
}
/* send DAI_CONFIG IPC */
return sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size,
&reply, sizeof(reply));
}
static int hda_link_dai_widget_update(struct sof_intel_hda_stream *hda_stream,
struct snd_soc_dapm_widget *w,
int channel, bool widget_setup)
{
struct snd_sof_dev *sdev = hda_stream->sdev;
struct sof_ipc_dai_config *config;
config = hda_dai_update_config(w, channel);
if (!config) {
dev_err(sdev->dev, "error: no config for DAI %s\n", w->name);
return -ENOENT;
}
return -EINVAL;
/* set up/free DAI widget and send DAI_CONFIG IPC */
if (widget_setup)
return hda_ctrl_dai_widget_setup(w);
return hda_ctrl_dai_widget_free(w);
}
static int hda_link_hw_params(struct snd_pcm_substream *substream,
......@@ -211,6 +230,7 @@ static int hda_link_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
struct sof_intel_hda_stream *hda_stream;
struct hda_pipe_params p_params = {0};
struct snd_soc_dapm_widget *w;
struct hdac_ext_link *link;
int stream_tag;
int ret;
......@@ -229,9 +249,13 @@ static int hda_link_hw_params(struct snd_pcm_substream *substream,
hda_stream = hstream_to_sof_hda_stream(link_dev);
/* update the DSP with the new tag */
ret = hda_link_config_ipc(hda_stream, dai->name, stream_tag - 1,
substream->stream);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
w = dai->playback_widget;
else
w = dai->capture_widget;
/* set up the DAI widget and send the DAI_CONFIG with the new tag */
ret = hda_link_dai_widget_update(hda_stream, w, stream_tag - 1, true);
if (ret < 0)
return ret;
......@@ -287,6 +311,7 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream,
snd_soc_dai_get_dma_data(dai, substream);
struct sof_intel_hda_stream *hda_stream;
struct snd_soc_pcm_runtime *rtd;
struct snd_soc_dapm_widget *w;
struct hdac_ext_link *link;
struct hdac_stream *hstream;
struct hdac_bus *bus;
......@@ -321,12 +346,16 @@ static int hda_link_pcm_trigger(struct snd_pcm_substream *substream,
break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
w = dai->playback_widget;
else
w = dai->capture_widget;
/*
* clear link DMA channel. It will be assigned when
* hw_params is set up again after resume.
*/
ret = hda_link_config_ipc(hda_stream, dai->name,
DMA_CHAN_INVALID, substream->stream);
ret = hda_link_config_ipc(hda_stream, w, DMA_CHAN_INVALID);
if (ret < 0)
return ret;
......@@ -357,6 +386,7 @@ static int hda_link_hw_free(struct snd_pcm_substream *substream,
struct hdac_stream *hstream;
struct snd_soc_pcm_runtime *rtd;
struct hdac_ext_stream *link_dev;
struct snd_soc_dapm_widget *w;
int ret;
hstream = substream->runtime->private_data;
......@@ -372,9 +402,13 @@ static int hda_link_hw_free(struct snd_pcm_substream *substream,
hda_stream = hstream_to_sof_hda_stream(link_dev);
/* free the link DMA channel in the FW */
ret = hda_link_config_ipc(hda_stream, dai->name, DMA_CHAN_INVALID,
substream->stream);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
w = dai->playback_widget;
else
w = dai->capture_widget;
/* free the link DMA channel in the FW and the DAI widget */
ret = hda_link_dai_widget_update(hda_stream, w, DMA_CHAN_INVALID, false);
if (ret < 0)
return ret;
......@@ -406,47 +440,51 @@ static const struct snd_soc_dai_ops hda_link_dai_ops = {
#endif
static int ssp_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
static int ssp_dai_setup_or_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai,
bool setup)
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME);
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
struct sof_ipc_fw_version *v = &sdev->fw_ready.version;
struct sof_ipc_dai_config *config;
struct snd_sof_dai *sof_dai;
struct sof_ipc_reply reply;
int ret;
struct snd_soc_component *component;
struct snd_sof_widget *swidget;
struct snd_soc_dapm_widget *w;
struct sof_ipc_fw_version *v;
struct snd_sof_dev *sdev;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
w = dai->playback_widget;
else
w = dai->capture_widget;
swidget = w->dobj.private;
component = swidget->scomp;
sdev = snd_soc_component_get_drvdata(component);
v = &sdev->fw_ready.version;
/* DAI_CONFIG IPC during hw_params is not supported in older firmware */
if (v->abi_version < SOF_ABI_VER(3, 18, 0))
return 0;
list_for_each_entry(sof_dai, &sdev->dai_list, list) {
if (!sof_dai->cpu_dai_name || !sof_dai->dai_config)
continue;
if (!strcmp(dai->name, sof_dai->cpu_dai_name) &&
substream->stream == sof_dai->comp_dai.direction) {
config = &sof_dai->dai_config[sof_dai->current_config];
if (setup)
return hda_ctrl_dai_widget_setup(w);
/* send IPC */
ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config,
config->hdr.size, &reply, sizeof(reply));
return hda_ctrl_dai_widget_free(w);
}
if (ret < 0)
dev_err(sdev->dev, "error: failed to set DAI config for %s\n",
sof_dai->name);
return ret;
}
}
static int ssp_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
return ssp_dai_setup_or_free(substream, dai, true);
}
return 0;
static int ssp_dai_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
return ssp_dai_setup_or_free(substream, dai, false);
}
static const struct snd_soc_dai_ops ssp_dai_ops = {
.hw_params = ssp_dai_hw_params,
.hw_free = ssp_dai_hw_free,
};
/*
......
......@@ -41,6 +41,86 @@
#define EXCEPT_MAX_HDR_SIZE 0x400
#define HDA_EXT_ROM_STATUS_SIZE 8
int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w)
{
struct snd_sof_widget *swidget = w->dobj.private;
struct snd_soc_component *component = swidget->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
struct sof_ipc_dai_config *config;
struct snd_sof_dai *sof_dai;
struct sof_ipc_reply reply;
int ret;
sof_dai = swidget->private;
if (!sof_dai || !sof_dai->dai_config) {
dev_err(sdev->dev, "No config for DAI %s\n", w->name);
return -EINVAL;
}
config = &sof_dai->dai_config[sof_dai->current_config];
/*
* For static pipelines, the DAI widget would already be set up and calling
* sof_widget_setup() simply returns without doing anything.
* For dynamic pipelines, the DAI widget will be set up now.
*/
ret = sof_widget_setup(sdev, swidget);
if (ret < 0) {
dev_err(sdev->dev, "error: failed setting up DAI widget %s\n", w->name);
return ret;
}
/* send DAI_CONFIG IPC */
ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size,
&reply, sizeof(reply));
if (ret < 0) {
dev_err(sdev->dev, "error: failed setting DAI config for %s\n", w->name);
return ret;
}
sof_dai->configured = true;
return 0;
}
int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w)
{
struct snd_sof_widget *swidget = w->dobj.private;
struct snd_soc_component *component = swidget->scomp;
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
struct sof_ipc_dai_config *config;
struct snd_sof_dai *sof_dai;
struct sof_ipc_reply reply;
int ret;
sof_dai = swidget->private;
if (!sof_dai || !sof_dai->dai_config) {
dev_err(sdev->dev, "error: No config to free DAI %s\n", w->name);
return -EINVAL;
}
/* nothing to do if hw_free() is called without restarting the stream after resume. */
if (!sof_dai->configured)
return 0;
config = &sof_dai->dai_config[sof_dai->current_config];
ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size,
&reply, sizeof(reply));
if (ret < 0)
dev_err(sdev->dev, "error: failed resetting DAI config for %s\n", w->name);
/*
* Reset the configured_flag and free the widget even if the IPC fails to keep
* the widget use_count balanced
*/
sof_dai->configured = false;
return sof_widget_free(sdev, swidget);
}
static const struct sof_intel_dsp_desc
*get_chip_info(struct snd_sof_pdata *pdata)
{
......@@ -64,67 +144,70 @@ static int sdw_clock_stop_quirks = SDW_INTEL_CLK_STOP_BUS_RESET;
module_param(sdw_clock_stop_quirks, int, 0444);
MODULE_PARM_DESC(sdw_clock_stop_quirks, "SOF SoundWire clock stop quirks");
static int sdw_dai_config_ipc(struct snd_sof_dev *sdev,
struct snd_soc_dapm_widget *w,
int link_id, int alh_stream_id, int dai_id, bool setup)
{
struct snd_sof_widget *swidget = w->dobj.private;
struct sof_ipc_dai_config *config;
struct snd_sof_dai *sof_dai;
if (!swidget) {
dev_err(sdev->dev, "error: No private data for widget %s\n", w->name);
return -EINVAL;
}
sof_dai = swidget->private;
if (!sof_dai || !sof_dai->dai_config) {
dev_err(sdev->dev, "error: No config for DAI %s\n", w->name);
return -EINVAL;
}
config = &sof_dai->dai_config[sof_dai->current_config];
/* update config with link and stream ID */
config->dai_index = (link_id << 8) | dai_id;
config->alh.stream_id = alh_stream_id;
if (setup)
return hda_ctrl_dai_widget_setup(w);
return hda_ctrl_dai_widget_free(w);
}
static int sdw_params_stream(struct device *dev,
struct sdw_intel_stream_params_data *params_data)
{
struct snd_pcm_substream *substream = params_data->substream;
struct snd_sof_dev *sdev = dev_get_drvdata(dev);
struct snd_soc_dai *d = params_data->dai;
struct sof_ipc_dai_config config;
struct sof_ipc_reply reply;
int link_id = params_data->link_id;
int alh_stream_id = params_data->alh_stream_id;
int ret;
u32 size = sizeof(config);
memset(&config, 0, size);
config.hdr.size = size;
config.hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG;
config.type = SOF_DAI_INTEL_ALH;
config.dai_index = (link_id << 8) | (d->id);
config.alh.stream_id = alh_stream_id;
/* send message to DSP */
ret = sof_ipc_tx_message(sdev->ipc,
config.hdr.cmd, &config, size, &reply,
sizeof(reply));
if (ret < 0) {
dev_err(sdev->dev,
"error: failed to set DAI hw_params for link %d dai->id %d ALH %d\n",
link_id, d->id, alh_stream_id);
}
struct snd_soc_dapm_widget *w;
return ret;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
w = d->playback_widget;
else
w = d->capture_widget;
return sdw_dai_config_ipc(sdev, w, params_data->link_id, params_data->alh_stream_id,
d->id, true);
}
static int sdw_free_stream(struct device *dev,
struct sdw_intel_stream_free_data *free_data)
{
struct snd_pcm_substream *substream = free_data->substream;
struct snd_sof_dev *sdev = dev_get_drvdata(dev);
struct snd_soc_dai *d = free_data->dai;
struct sof_ipc_dai_config config;
struct sof_ipc_reply reply;
int link_id = free_data->link_id;
int ret;
u32 size = sizeof(config);
memset(&config, 0, size);
config.hdr.size = size;
config.hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG;
config.type = SOF_DAI_INTEL_ALH;
config.dai_index = (link_id << 8) | d->id;
config.alh.stream_id = 0xFFFF; /* invalid value on purpose */
/* send message to DSP */
ret = sof_ipc_tx_message(sdev->ipc,
config.hdr.cmd, &config, size, &reply,
sizeof(reply));
if (ret < 0) {
dev_err(sdev->dev,
"error: failed to free stream for link %d dai->id %d\n",
link_id, d->id);
}
struct snd_soc_dapm_widget *w;
return ret;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
w = d->playback_widget;
else
w = d->capture_widget;
/* send invalid stream_id */
return sdw_dai_config_ipc(sdev, w, free_data->link_id, 0xFFFF, d->id, false);
}
static const struct sdw_intel_ops sdw_callback = {
......
......@@ -733,4 +733,9 @@ void hda_set_mach_params(const struct snd_soc_acpi_mach *mach,
/* PCI driver selection and probe */
int hda_pci_intel_probe(struct pci_dev *pci, const struct pci_device_id *pci_id);
struct snd_sof_dai;
struct sof_ipc_dai_config;
int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w);
int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w);
#endif
......@@ -744,9 +744,31 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol,
struct sof_ipc_fw_ready *ready = &sdev->fw_ready;
struct sof_ipc_fw_version *v = &ready->version;
struct sof_ipc_ctrl_data_params sparams;
struct snd_sof_widget *swidget;
bool widget_found = false;
size_t send_bytes;
int err;
list_for_each_entry(swidget, &sdev->widget_list, list) {
if (swidget->comp_id == scontrol->comp_id) {
widget_found = true;
break;
}
}
if (!widget_found) {
dev_err(sdev->dev, "error: can't find widget with id %d\n", scontrol->comp_id);
return -EINVAL;
}
/*
* Volatile controls should always be part of static pipelines and the widget use_count
* would always be > 0 in this case. For the others, just return the cached value if the
* widget is not set up.
*/
if (!swidget->use_count)
return 0;
/* read or write firmware volume */
if (scontrol->readback_offset != 0) {
/* write/read value header via mmaped region */
......
......@@ -116,6 +116,40 @@ static int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream,
return ret;
}
static int sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev,
struct snd_soc_pcm_runtime *rtd,
struct snd_sof_pcm *spcm, int dir)
{
struct snd_soc_dai *dai;
int ret, j;
/* query DAPM for list of connected widgets and set them up */
for_each_rtd_cpu_dais(rtd, j, dai) {
struct snd_soc_dapm_widget_list *list;
ret = snd_soc_dapm_dai_get_connected_widgets(dai, dir, &list,
dpcm_end_walk_at_be);
if (ret < 0) {
dev_err(sdev->dev, "error: dai %s has no valid %s path\n", dai->name,
dir == SNDRV_PCM_STREAM_PLAYBACK ? "playback" : "capture");
return ret;
}
spcm->stream[dir].list = list;
ret = sof_widget_list_setup(sdev, spcm, dir);
if (ret < 0) {
dev_err(sdev->dev, "error: failed widget list set up for pcm %d dir %d\n",
spcm->pcm.pcm_id, dir);
spcm->stream[dir].list = NULL;
snd_soc_dapm_dai_free_widgets(&list);
return ret;
}
}
return 0;
}
static int sof_pcm_hw_params(struct snd_soc_component *component,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
......@@ -213,7 +247,14 @@ static int sof_pcm_hw_params(struct snd_soc_component *component,
dev_dbg(component->dev, "stream_tag %d", pcm.params.stream_tag);
/* send IPC to the DSP */
/* if this is a repeated hw_params without hw_free, skip setting up widgets */
if (!spcm->stream[substream->stream].list) {
ret = sof_pcm_setup_connected_widgets(sdev, rtd, spcm, substream->stream);
if (ret < 0)
return ret;
}
/* send hw_params IPC to the DSP */
ret = sof_ipc_tx_message(sdev->ipc, pcm.hdr.cmd, &pcm, sizeof(pcm),
&ipc_params_reply, sizeof(ipc_params_reply));
if (ret < 0) {
......@@ -259,6 +300,10 @@ static int sof_pcm_hw_free(struct snd_soc_component *component,
err = ret;
}
ret = sof_widget_list_free(sdev, spcm, substream->stream);
if (ret < 0)
err = ret;
cancel_work_sync(&spcm->stream[substream->stream].period_elapsed_work);
ret = snd_sof_pcm_platform_hw_free(sdev, substream);
......@@ -316,6 +361,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
struct sof_ipc_stream stream;
struct sof_ipc_reply reply;
bool reset_hw_params = false;
bool free_widget_list = false;
bool ipc_first = false;
int ret;
......@@ -386,6 +432,7 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
spcm->stream[substream->stream].suspend_ignored = true;
return 0;
}
free_widget_list = true;
fallthrough;
case SNDRV_PCM_TRIGGER_STOP:
stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_STOP;
......@@ -414,8 +461,15 @@ static int sof_pcm_trigger(struct snd_soc_component *component,
snd_sof_pcm_platform_trigger(sdev, substream, cmd);
/* free PCM if reset_hw_params is set and the STOP IPC is successful */
if (!ret && reset_hw_params)
if (!ret && reset_hw_params) {
ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm);
if (ret < 0)
return ret;
/* free widget list only for SUSPEND trigger */
if (free_widget_list)
ret = sof_widget_list_free(sdev, spcm, substream->stream);
}
return ret;
}
......
......@@ -157,7 +157,7 @@ static int sof_resume(struct device *dev, bool runtime_resume)
}
/* restore pipelines */
ret = sof_restore_pipelines(sdev->dev);
ret = sof_set_up_pipelines(sdev->dev, false);
if (ret < 0) {
dev_err(sdev->dev,
"error: failed to restore pipeline after resume %d\n",
......@@ -208,6 +208,8 @@ static int sof_suspend(struct device *dev, bool runtime_suspend)
if (target_state == SOF_DSP_PM_D0)
goto suspend;
sof_tear_down_pipelines(dev, false);
/* release trace */
snd_sof_release_trace(sdev);
......
This diff is collapsed.
......@@ -28,6 +28,8 @@
#define DMA_CHAN_INVALID 0xFFFFFFFF
#define WIDGET_IS_DAI(id) ((id) == snd_soc_dapm_dai_in || (id) == snd_soc_dapm_dai_out)
/* PCM stream, mapped to FW component */
struct snd_sof_pcm_stream {
u32 comp_id;
......@@ -35,6 +37,7 @@ struct snd_sof_pcm_stream {
struct sof_ipc_stream_posn posn;
struct snd_pcm_substream *substream;
struct work_struct period_elapsed_work;
struct snd_soc_dapm_widget_list *list; /* list of connected DAPM widgets */
bool d0i3_compatible; /* DSP can be in D0I3 when this pcm is opened */
/*
* flag to indicate that the DSP pipelines should be kept
......@@ -66,6 +69,7 @@ struct snd_sof_control {
int min_volume_step; /* min volume step for volume_table */
int max_volume_step; /* max volume step for volume_table */
int num_channels;
unsigned int access;
u32 readback_offset; /* offset to mmapped data if used */
struct sof_ipc_ctrl_data *control_data;
u32 size; /* cdata size */
......@@ -80,17 +84,31 @@ struct snd_sof_control {
bool comp_data_dirty;
};
struct snd_sof_widget;
/* ASoC SOF DAPM widget */
struct snd_sof_widget {
struct snd_soc_component *scomp;
int comp_id;
int pipeline_id;
int complete;
int use_count; /* use_count will be protected by the PCM mutex held by the core */
int core;
int id;
/*
* Flag indicating if the widget should be set up dynamically when a PCM is opened.
* This flag is only set for the scheduler type widget in topology. During topology
* loading, this flag is propagated to all the widgets belonging to the same pipeline.
* When this flag is not set, a widget is set up at the time of topology loading
* and retained until the DSP enters D3. It will need to be set up again when resuming
* from D3.
*/
bool dynamic_pipeline_widget;
struct snd_soc_dapm_widget *widget;
struct list_head list; /* list in sdev widget list */
struct snd_sof_widget *pipe_widget;
/* extended data for UUID components */
struct sof_ipc_comp_ext comp_ext;
......@@ -104,6 +122,9 @@ struct snd_sof_route {
struct snd_soc_dapm_route *route;
struct list_head list; /* list in sdev route list */
struct snd_sof_widget *src_widget;
struct snd_sof_widget *sink_widget;
bool setup;
void *private;
};
......@@ -112,11 +133,11 @@ struct snd_sof_route {
struct snd_sof_dai {
struct snd_soc_component *scomp;
const char *name;
const char *cpu_dai_name;
struct sof_ipc_comp_dai comp_dai;
int number_configs;
int current_config;
bool configured; /* DAI configured during BE hw_params */
struct sof_ipc_dai_config *dai_config;
struct list_head list; /* list in sdev dai list */
};
......@@ -225,7 +246,8 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol,
int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params);
/* PM */
int sof_restore_pipelines(struct device *dev);
int sof_set_up_pipelines(struct device *dev, bool verify);
int sof_tear_down_pipelines(struct device *dev, bool verify);
int sof_set_hw_params_upon_resume(struct device *dev);
bool snd_sof_stream_suspend_ignored(struct snd_sof_dev *sdev);
bool snd_sof_dsp_only_d0i3_compatible_stream_active(struct snd_sof_dev *sdev);
......@@ -234,4 +256,10 @@ bool snd_sof_dsp_only_d0i3_compatible_stream_active(struct snd_sof_dev *sdev);
int sof_machine_register(struct snd_sof_dev *sdev, void *pdata);
void sof_machine_unregister(struct snd_sof_dev *sdev, void *pdata);
int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget);
int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget);
/* PCM */
int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir);
int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir);
#endif
......@@ -23,6 +23,7 @@
/* debug flags */
#define SOF_DBG_ENABLE_TRACE BIT(0)
#define SOF_DBG_RETAIN_CTX BIT(1) /* prevent DSP D3 on FW exception */
#define SOF_DBG_VERIFY_TPLG BIT(2) /* verify topology during load */
#define SOF_DBG_DUMP_REGS BIT(0)
#define SOF_DBG_DUMP_MBOX BIT(1)
......
This diff is collapsed.
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