diff options
Diffstat (limited to 'sound/soc/sof')
-rw-r--r-- | sound/soc/sof/intel/hda-dai.c | 174 | ||||
-rw-r--r-- | sound/soc/sof/intel/hda.c | 177 | ||||
-rw-r--r-- | sound/soc/sof/intel/hda.h | 5 | ||||
-rw-r--r-- | sound/soc/sof/ipc.c | 22 | ||||
-rw-r--r-- | sound/soc/sof/pcm.c | 58 | ||||
-rw-r--r-- | sound/soc/sof/pm.c | 4 | ||||
-rw-r--r-- | sound/soc/sof/sof-audio.c | 709 | ||||
-rw-r--r-- | sound/soc/sof/sof-audio.h | 32 | ||||
-rw-r--r-- | sound/soc/sof/sof-priv.h | 1 | ||||
-rw-r--r-- | sound/soc/sof/topology.c | 362 |
10 files changed, 1015 insertions, 529 deletions
diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 2f54a659b78a..59d6750c6a20 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -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; + } - /* update config with stream tag */ - config->hda.link_dma_ch = channel; + config = &sof_dai->dai_config[sof_dai->current_config]; - /* send IPC */ - ret = sof_ipc_tx_message(hda_stream->sdev->ipc, - config->hdr.cmd, - config, - config->hdr.size, - &reply, sizeof(reply)); + /* update config with stream tag */ + config->hda.link_dma_ch = channel; - if (ret < 0) - dev_err(hda_stream->sdev->dev, - "error: failed to set dai config for %s\n", - sof_dai->name); - return ret; - } + return config; +} + +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, }; /* diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index c11e4c14d875..066e90506ae6 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -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 = { diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index cb5a1b17f153..1195018a1f4f 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -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 diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 0034e7a34a1d..53593df95155 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -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 */ diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index b4280459e5db..374df2dfa816 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -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; } diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 89b4b3d76539..77a3496d3dbd 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -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); diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 989912f2b739..c4cabe26b157 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -11,6 +11,486 @@ #include "sof-audio.h" #include "ops.h" +static int sof_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol) +{ + int ipc_cmd, ctrl_type; + int ret; + + /* reset readback offset for scontrol */ + scontrol->readback_offset = 0; + + /* notify DSP of kcontrol values */ + switch (scontrol->cmd) { + case SOF_CTRL_CMD_VOLUME: + case SOF_CTRL_CMD_ENUM: + case SOF_CTRL_CMD_SWITCH: + ipc_cmd = SOF_IPC_COMP_SET_VALUE; + ctrl_type = SOF_CTRL_TYPE_VALUE_CHAN_SET; + break; + case SOF_CTRL_CMD_BINARY: + ipc_cmd = SOF_IPC_COMP_SET_DATA; + ctrl_type = SOF_CTRL_TYPE_DATA_SET; + break; + default: + return 0; + } + + ret = snd_sof_ipc_set_get_comp_data(scontrol, ipc_cmd, ctrl_type, scontrol->cmd, true); + if (ret < 0) + dev_err(sdev->dev, "error: failed kcontrol value set for widget: %d\n", + scontrol->comp_id); + + return ret; +} + +static int sof_dai_config_setup(struct snd_sof_dev *sdev, struct snd_sof_dai *dai) +{ + struct sof_ipc_dai_config *config; + struct sof_ipc_reply reply; + int ret; + + config = &dai->dai_config[dai->current_config]; + if (!config) { + dev_err(sdev->dev, "error: no config for DAI %s\n", dai->name); + return -EINVAL; + } + + 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 to set dai config for %s\n", dai->name); + + return ret; +} + +static int sof_widget_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) +{ + struct snd_sof_control *scontrol; + int ret; + + /* set up all controls for the widget */ + list_for_each_entry(scontrol, &sdev->kcontrol_list, list) + if (scontrol->comp_id == swidget->comp_id) { + ret = sof_kcontrol_setup(sdev, scontrol); + if (ret < 0) { + dev_err(sdev->dev, "error: fail to set up kcontrols for widget %s\n", + swidget->widget->name); + return ret; + } + } + + return 0; +} + +static void sof_reset_route_setup_status(struct snd_sof_dev *sdev, struct snd_sof_widget *widget) +{ + struct snd_sof_route *sroute; + + list_for_each_entry(sroute, &sdev->route_list, list) + if (sroute->src_widget == widget || sroute->sink_widget == widget) + sroute->setup = false; +} + +int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) +{ + struct sof_ipc_free ipc_free = { + .hdr = { + .size = sizeof(ipc_free), + .cmd = SOF_IPC_GLB_TPLG_MSG, + }, + .id = swidget->comp_id, + }; + struct sof_ipc_reply reply; + int ret; + + if (!swidget->private) + return 0; + + /* only free when use_count is 0 */ + if (--swidget->use_count) + return 0; + + switch (swidget->id) { + case snd_soc_dapm_scheduler: + ipc_free.hdr.cmd |= SOF_IPC_TPLG_PIPE_FREE; + break; + case snd_soc_dapm_buffer: + ipc_free.hdr.cmd |= SOF_IPC_TPLG_BUFFER_FREE; + break; + default: + ipc_free.hdr.cmd |= SOF_IPC_TPLG_COMP_FREE; + break; + } + + ret = sof_ipc_tx_message(sdev->ipc, ipc_free.hdr.cmd, &ipc_free, sizeof(ipc_free), + &reply, sizeof(reply)); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to free widget %s\n", swidget->widget->name); + swidget->use_count++; + return ret; + } + + /* reset route setup status for all routes that contain this widget */ + sof_reset_route_setup_status(sdev, swidget); + swidget->complete = 0; + dev_dbg(sdev->dev, "widget %s freed\n", swidget->widget->name); + + return 0; +} +EXPORT_SYMBOL(sof_widget_free); + +int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) +{ + struct sof_ipc_pipe_new *pipeline; + struct sof_ipc_comp_reply r; + struct sof_ipc_cmd_hdr *hdr; + struct sof_ipc_comp *comp; + struct snd_sof_dai *dai; + size_t ipc_size; + int ret; + + /* skip if there is no private data */ + if (!swidget->private) + return 0; + + /* widget already set up */ + if (++swidget->use_count > 1) + return 0; + + ret = sof_pipeline_core_enable(sdev, swidget); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to enable target core: %d for widget %s\n", + ret, swidget->widget->name); + goto use_count_dec; + } + + switch (swidget->id) { + case snd_soc_dapm_dai_in: + case snd_soc_dapm_dai_out: + ipc_size = sizeof(struct sof_ipc_comp_dai) + sizeof(struct sof_ipc_comp_ext); + comp = kzalloc(ipc_size, GFP_KERNEL); + if (!comp) + return -ENOMEM; + + dai = swidget->private; + dai->configured = false; + memcpy(comp, &dai->comp_dai, sizeof(struct sof_ipc_comp_dai)); + + /* append extended data to the end of the component */ + memcpy((u8 *)comp + sizeof(struct sof_ipc_comp_dai), &swidget->comp_ext, + sizeof(swidget->comp_ext)); + + ret = sof_ipc_tx_message(sdev->ipc, comp->hdr.cmd, comp, ipc_size, &r, sizeof(r)); + kfree(comp); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to load widget %s\n", + swidget->widget->name); + goto use_count_dec; + } + + ret = sof_dai_config_setup(sdev, dai); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to load dai config for DAI %s\n", + swidget->widget->name); + sof_widget_free(sdev, swidget); + return ret; + } + break; + case snd_soc_dapm_scheduler: + pipeline = swidget->private; + ret = sof_load_pipeline_ipc(sdev->dev, pipeline, &r); + break; + default: + hdr = swidget->private; + ret = sof_ipc_tx_message(sdev->ipc, hdr->cmd, swidget->private, hdr->size, + &r, sizeof(r)); + break; + } + if (ret < 0) { + dev_err(sdev->dev, "error: failed to load widget %s\n", swidget->widget->name); + goto use_count_dec; + } + + /* restore kcontrols for widget */ + ret = sof_widget_kcontrol_setup(sdev, swidget); + if (ret < 0) { + dev_err(sdev->dev, "error: failed to restore kcontrols for widget %s\n", + swidget->widget->name); + sof_widget_free(sdev, swidget); + return ret; + } + + dev_dbg(sdev->dev, "widget %s setup complete\n", swidget->widget->name); + + return 0; + +use_count_dec: + swidget->use_count--; + return ret; +} +EXPORT_SYMBOL(sof_widget_setup); + +static int sof_route_setup_ipc(struct snd_sof_dev *sdev, struct snd_sof_route *sroute) +{ + struct sof_ipc_pipe_comp_connect *connect; + struct sof_ipc_reply reply; + int ret; + + /* skip if there's no private data */ + if (!sroute->private) + return 0; + + /* nothing to do if route is already set up */ + if (sroute->setup) + return 0; + + connect = sroute->private; + + dev_dbg(sdev->dev, "setting up route %s -> %s\n", + sroute->src_widget->widget->name, + sroute->sink_widget->widget->name); + + /* send ipc */ + ret = sof_ipc_tx_message(sdev->ipc, + connect->hdr.cmd, + connect, sizeof(*connect), + &reply, sizeof(reply)); + if (ret < 0) { + dev_err(sdev->dev, "%s: route setup failed %d\n", __func__, ret); + return ret; + } + + sroute->setup = true; + + return 0; +} + +static int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsource, + struct snd_soc_dapm_widget *wsink) +{ + struct snd_sof_widget *src_widget = wsource->dobj.private; + struct snd_sof_widget *sink_widget = wsink->dobj.private; + struct snd_sof_route *sroute; + bool route_found = false; + + /* ignore routes involving virtual widgets in topology */ + switch (src_widget->id) { + case snd_soc_dapm_out_drv: + case snd_soc_dapm_output: + case snd_soc_dapm_input: + return 0; + default: + break; + } + + switch (sink_widget->id) { + case snd_soc_dapm_out_drv: + case snd_soc_dapm_output: + case snd_soc_dapm_input: + return 0; + default: + break; + } + + /* find route matching source and sink widgets */ + list_for_each_entry(sroute, &sdev->route_list, list) + if (sroute->src_widget == src_widget && sroute->sink_widget == sink_widget) { + route_found = true; + break; + } + + if (!route_found) { + dev_err(sdev->dev, "error: cannot find SOF route for source %s -> %s sink\n", + wsource->name, wsink->name); + return -EINVAL; + } + + return sof_route_setup_ipc(sdev, sroute); +} + +static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev, + struct snd_soc_dapm_widget_list *list, int dir) +{ + struct snd_soc_dapm_widget *widget; + struct snd_soc_dapm_path *p; + int ret; + int i; + + /* + * Set up connections between widgets in the sink/source paths based on direction. + * Some non-SOF widgets exist in topology either for compatibility or for the + * purpose of connecting a pipeline from a host to a DAI in order to receive the DAPM + * events. But they are not handled by the firmware. So ignore them. + */ + if (dir == SNDRV_PCM_STREAM_PLAYBACK) { + for_each_dapm_widgets(list, i, widget) { + if (!widget->dobj.private) + continue; + + snd_soc_dapm_widget_for_each_sink_path(widget, p) + if (p->sink->dobj.private) { + ret = sof_route_setup(sdev, widget, p->sink); + if (ret < 0) + return ret; + } + } + } else { + for_each_dapm_widgets(list, i, widget) { + if (!widget->dobj.private) + continue; + + snd_soc_dapm_widget_for_each_source_path(widget, p) + if (p->source->dobj.private) { + ret = sof_route_setup(sdev, p->source, widget); + if (ret < 0) + return ret; + } + } + } + + return 0; +} + +int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir) +{ + struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list; + struct snd_soc_dapm_widget *widget; + int i, ret, num_widgets; + + /* nothing to set up */ + if (!list) + return 0; + + /* set up widgets in the list */ + for_each_dapm_widgets(list, num_widgets, widget) { + struct snd_sof_widget *swidget = widget->dobj.private; + struct snd_sof_widget *pipe_widget; + + if (!swidget) + continue; + + /* + * The scheduler widget for a pipeline is not part of the connected DAPM + * widget list and it needs to be set up before the widgets in the pipeline + * are set up. The use_count for the scheduler widget is incremented for every + * widget in a given pipeline to ensure that it is freed only after the last + * widget in the pipeline is freed. + */ + pipe_widget = swidget->pipe_widget; + if (!pipe_widget) { + dev_err(sdev->dev, "error: no pipeline widget found for %s\n", + swidget->widget->name); + ret = -EINVAL; + goto widget_free; + } + + ret = sof_widget_setup(sdev, pipe_widget); + if (ret < 0) + goto widget_free; + + /* set up the widget */ + ret = sof_widget_setup(sdev, swidget); + if (ret < 0) { + sof_widget_free(sdev, pipe_widget); + goto widget_free; + } + } + + /* + * error in setting pipeline connections will result in route status being reset for + * routes that were successfully set up when the widgets are freed. + */ + ret = sof_setup_pipeline_connections(sdev, list, dir); + if (ret < 0) + goto widget_free; + + /* complete pipelines */ + for_each_dapm_widgets(list, i, widget) { + struct snd_sof_widget *swidget = widget->dobj.private; + struct snd_sof_widget *pipe_widget; + + if (!swidget) + continue; + + pipe_widget = swidget->pipe_widget; + if (!pipe_widget) { + dev_err(sdev->dev, "error: no pipeline widget found for %s\n", + swidget->widget->name); + ret = -EINVAL; + goto widget_free; + } + + if (pipe_widget->complete) + continue; + + pipe_widget->complete = snd_sof_complete_pipeline(sdev->dev, pipe_widget); + if (pipe_widget->complete < 0) { + ret = pipe_widget->complete; + goto widget_free; + } + } + + return 0; + +widget_free: + /* free all widgets that have been set up successfully */ + for_each_dapm_widgets(list, i, widget) { + struct snd_sof_widget *swidget = widget->dobj.private; + + if (!swidget) + continue; + + if (!num_widgets--) + break; + + sof_widget_free(sdev, swidget); + sof_widget_free(sdev, swidget->pipe_widget); + } + + return ret; +} + +int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir) +{ + struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list; + struct snd_soc_dapm_widget *widget; + int i, ret; + int ret1 = 0; + + /* nothing to free */ + if (!list) + return 0; + + /* + * Free widgets in the list. This can fail but continue freeing other widgets to keep + * use_counts balanced. + */ + for_each_dapm_widgets(list, i, widget) { + struct snd_sof_widget *swidget = widget->dobj.private; + + if (!swidget) + continue; + + /* + * free widget and its pipe_widget. Either of these can fail, but free as many as + * possible before freeing the list and returning the error. + */ + ret = sof_widget_free(sdev, swidget); + if (ret < 0) + ret1 = ret; + + ret = sof_widget_free(sdev, swidget->pipe_widget); + if (ret < 0) + ret1 = ret; + } + + snd_soc_dapm_dai_free_widgets(&list); + spcm->stream[dir].list = NULL; + + return ret1; +} + /* * helper to determine if there are only D0i3 compatible * streams active @@ -93,55 +573,6 @@ int sof_set_hw_params_upon_resume(struct device *dev) return snd_sof_dsp_hw_params_upon_resume(sdev); } -static int sof_restore_kcontrols(struct device *dev) -{ - struct snd_sof_dev *sdev = dev_get_drvdata(dev); - struct snd_sof_control *scontrol; - int ipc_cmd, ctrl_type; - int ret = 0; - - /* restore kcontrol values */ - list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { - /* reset readback offset for scontrol after resuming */ - scontrol->readback_offset = 0; - - /* notify DSP of kcontrol values */ - switch (scontrol->cmd) { - case SOF_CTRL_CMD_VOLUME: - case SOF_CTRL_CMD_ENUM: - case SOF_CTRL_CMD_SWITCH: - ipc_cmd = SOF_IPC_COMP_SET_VALUE; - ctrl_type = SOF_CTRL_TYPE_VALUE_CHAN_SET; - ret = snd_sof_ipc_set_get_comp_data(scontrol, - ipc_cmd, ctrl_type, - scontrol->cmd, - true); - break; - case SOF_CTRL_CMD_BINARY: - ipc_cmd = SOF_IPC_COMP_SET_DATA; - ctrl_type = SOF_CTRL_TYPE_DATA_SET; - ret = snd_sof_ipc_set_get_comp_data(scontrol, - ipc_cmd, ctrl_type, - scontrol->cmd, - true); - break; - - default: - break; - } - - if (ret < 0) { - dev_err(dev, - "error: failed kcontrol value set for widget: %d\n", - scontrol->comp_id); - - return ret; - } - } - - return 0; -} - const struct sof_ipc_pipe_new *snd_sof_pipeline_find(struct snd_sof_dev *sdev, int pipeline_id) { @@ -158,142 +589,54 @@ const struct sof_ipc_pipe_new *snd_sof_pipeline_find(struct snd_sof_dev *sdev, return NULL; } -int sof_restore_pipelines(struct device *dev) +int sof_set_up_pipelines(struct device *dev, bool verify) { struct snd_sof_dev *sdev = dev_get_drvdata(dev); struct snd_sof_widget *swidget; struct snd_sof_route *sroute; - struct sof_ipc_pipe_new *pipeline; - struct snd_sof_dai *dai; - struct sof_ipc_cmd_hdr *hdr; - struct sof_ipc_comp *comp; - size_t ipc_size; int ret; /* restore pipeline components */ list_for_each_entry_reverse(swidget, &sdev->widget_list, list) { - struct sof_ipc_comp_reply r; - - /* skip if there is no private data */ - if (!swidget->private) + /* only set up the widgets belonging to static pipelines */ + if (!verify && swidget->dynamic_pipeline_widget) continue; - ret = sof_pipeline_core_enable(sdev, swidget); - if (ret < 0) { - dev_err(dev, - "error: failed to enable target core: %d\n", - ret); + /* update DAI config. The IPC will be sent in sof_widget_setup() */ + if (WIDGET_IS_DAI(swidget->id)) { + struct snd_sof_dai *dai = swidget->private; + struct sof_ipc_dai_config *config; - return ret; - } - - switch (swidget->id) { - case snd_soc_dapm_dai_in: - case snd_soc_dapm_dai_out: - ipc_size = sizeof(struct sof_ipc_comp_dai) + - sizeof(struct sof_ipc_comp_ext); - comp = kzalloc(ipc_size, GFP_KERNEL); - if (!comp) - return -ENOMEM; - - dai = swidget->private; - memcpy(comp, &dai->comp_dai, - sizeof(struct sof_ipc_comp_dai)); - - /* append extended data to the end of the component */ - memcpy((u8 *)comp + sizeof(struct sof_ipc_comp_dai), - &swidget->comp_ext, sizeof(swidget->comp_ext)); - - ret = sof_ipc_tx_message(sdev->ipc, comp->hdr.cmd, - comp, ipc_size, - &r, sizeof(r)); - kfree(comp); - break; - case snd_soc_dapm_scheduler: + if (!dai || !dai->dai_config) + continue; + config = dai->dai_config; /* - * During suspend, all DSP cores are powered off. - * Therefore upon resume, create the pipeline comp - * and power up the core that the pipeline is - * scheduled on. + * The link DMA channel would be invalidated for running + * streams but not for streams that were in the PAUSED + * state during suspend. So invalidate it here before setting + * the dai config in the DSP. */ - pipeline = swidget->private; - ret = sof_load_pipeline_ipc(dev, pipeline, &r); - break; - default: - hdr = swidget->private; - ret = sof_ipc_tx_message(sdev->ipc, hdr->cmd, - swidget->private, hdr->size, - &r, sizeof(r)); - break; + if (config->type == SOF_DAI_INTEL_HDA) + config->hda.link_dma_ch = DMA_CHAN_INVALID; } - if (ret < 0) { - dev_err(dev, - "error: failed to load widget type %d with ID: %d\n", - swidget->widget->id, swidget->comp_id); + ret = sof_widget_setup(sdev, swidget); + if (ret < 0) return ret; - } } /* restore pipeline connections */ - list_for_each_entry_reverse(sroute, &sdev->route_list, list) { - struct sof_ipc_pipe_comp_connect *connect; - struct sof_ipc_reply reply; + list_for_each_entry(sroute, &sdev->route_list, list) { - /* skip if there's no private data */ - if (!sroute->private) + /* only set up routes belonging to static pipelines */ + if (!verify && (sroute->src_widget->dynamic_pipeline_widget || + sroute->sink_widget->dynamic_pipeline_widget)) continue; - connect = sroute->private; - - /* send ipc */ - ret = sof_ipc_tx_message(sdev->ipc, - connect->hdr.cmd, - connect, sizeof(*connect), - &reply, sizeof(reply)); + ret = sof_route_setup_ipc(sdev, sroute); if (ret < 0) { - dev_err(dev, - "error: failed to load route sink %s control %s source %s\n", - sroute->route->sink, - sroute->route->control ? sroute->route->control - : "none", - sroute->route->source); - - return ret; - } - } - - /* restore dai links */ - list_for_each_entry_reverse(dai, &sdev->dai_list, list) { - struct sof_ipc_reply reply; - struct sof_ipc_dai_config *config = &dai->dai_config[dai->current_config]; - - if (!config) { - dev_err(dev, "error: no config for DAI %s\n", - dai->name); - continue; - } - - /* - * The link DMA channel would be invalidated for running - * streams but not for streams that were in the PAUSED - * state during suspend. So invalidate it here before setting - * the dai config in the DSP. - */ - if (config->type == SOF_DAI_INTEL_HDA) - config->hda.link_dma_ch = DMA_CHAN_INVALID; - - ret = sof_ipc_tx_message(sdev->ipc, - config->hdr.cmd, config, - config->hdr.size, - &reply, sizeof(reply)); - - if (ret < 0) { - dev_err(dev, - "error: failed to set dai config for %s\n", - dai->name); - + dev_err(sdev->dev, "%s: restore pipeline connections failed\n", __func__); return ret; } } @@ -302,6 +645,10 @@ int sof_restore_pipelines(struct device *dev) list_for_each_entry(swidget, &sdev->widget_list, list) { switch (swidget->id) { case snd_soc_dapm_scheduler: + /* only complete static pipelines */ + if (!verify && swidget->dynamic_pipeline_widget) + continue; + swidget->complete = snd_sof_complete_pipeline(dev, swidget); break; @@ -310,13 +657,41 @@ int sof_restore_pipelines(struct device *dev) } } - /* restore pipeline kcontrols */ - ret = sof_restore_kcontrols(dev); - if (ret < 0) - dev_err(dev, - "error: restoring kcontrols after resume\n"); + return 0; +} - return ret; +/* + * This function doesn't free widgets during suspend. It only resets the set up status for all + * routes and use_count for all widgets. + */ +int sof_tear_down_pipelines(struct device *dev, bool verify) +{ + struct snd_sof_dev *sdev = dev_get_drvdata(dev); + struct snd_sof_widget *swidget; + struct snd_sof_route *sroute; + int ret; + + /* + * This function is called during suspend and for one-time topology verification during + * first boot. In both cases, there is no need to protect swidget->use_count and + * sroute->setup because during suspend all streams are suspended and during topology + * loading the sound card unavailable to open PCMs. + */ + list_for_each_entry_reverse(swidget, &sdev->widget_list, list) { + if (!verify) { + swidget->use_count = 0; + continue; + } + + ret = sof_widget_free(sdev, swidget); + if (ret < 0) + return ret; + } + + list_for_each_entry(sroute, &sdev->route_list, list) + sroute->setup = false; + + return 0; } /* diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 9a8d005e75a0..149b3dbcddd1 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -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 diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index a6ded5bd0674..1289e2efeb62 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -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) diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index cc9585bfa4e9..44d60081bc26 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -572,6 +572,12 @@ static const struct sof_topology_token sched_tokens[] = { offsetof(struct sof_ipc_pipe_new, time_domain), 0}, }; +static const struct sof_topology_token pipeline_tokens[] = { + {SOF_TKN_SCHED_DYNAMIC_PIPELINE, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16, + offsetof(struct snd_sof_widget, dynamic_pipeline_widget), 0}, + +}; + /* volume */ static const struct sof_topology_token volume_tokens[] = { {SOF_TKN_VOLUME_RAMP_STEP_TYPE, SND_SOC_TPLG_TUPLE_TYPE_WORD, @@ -1250,6 +1256,7 @@ static int sof_control_load(struct snd_soc_component *scomp, int index, return -ENOMEM; scontrol->scomp = scomp; + scontrol->access = kc->access; switch (le32_to_cpu(hdr->ops.info)) { case SND_SOC_TPLG_CTL_VOLSW: @@ -1512,10 +1519,8 @@ static struct sof_ipc_comp *sof_comp_alloc(struct snd_sof_widget *swidget, static int sof_widget_load_dai(struct snd_soc_component *scomp, int index, struct snd_sof_widget *swidget, struct snd_soc_tplg_dapm_widget *tw, - struct sof_ipc_comp_reply *r, struct snd_sof_dai *dai) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; struct sof_ipc_comp_dai *comp_dai; size_t ipc_size = sizeof(*comp_dai); @@ -1552,10 +1557,7 @@ static int sof_widget_load_dai(struct snd_soc_component *scomp, int index, swidget->widget->name, comp_dai->type, comp_dai->dai_index); sof_dbg_comp_config(scomp, &comp_dai->config); - ret = sof_ipc_tx_message(sdev->ipc, comp_dai->comp.hdr.cmd, - comp_dai, ipc_size, r, sizeof(*r)); - - if (ret == 0 && dai) { + if (dai) { dai->scomp = scomp; /* @@ -1577,10 +1579,8 @@ finish: static int sof_widget_load_buffer(struct snd_soc_component *scomp, int index, struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw, - struct sof_ipc_comp_reply *r) + struct snd_soc_tplg_dapm_widget *tw) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; struct sof_ipc_buffer *buffer; int ret; @@ -1612,15 +1612,7 @@ static int sof_widget_load_buffer(struct snd_soc_component *scomp, int index, swidget->private = buffer; - ret = sof_ipc_tx_message(sdev->ipc, buffer->comp.hdr.cmd, buffer, - sizeof(*buffer), r, sizeof(*r)); - if (ret < 0) { - dev_err(scomp->dev, "error: buffer %s load failed\n", - swidget->widget->name); - kfree(buffer); - } - - return ret; + return 0; } /* bind PCM ID to host component ID */ @@ -1649,10 +1641,8 @@ static int spcm_bind(struct snd_soc_component *scomp, struct snd_sof_pcm *spcm, static int sof_widget_load_pcm(struct snd_soc_component *scomp, int index, struct snd_sof_widget *swidget, enum sof_ipc_stream_direction dir, - struct snd_soc_tplg_dapm_widget *tw, - struct sof_ipc_comp_reply *r) + struct snd_soc_tplg_dapm_widget *tw) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; struct sof_ipc_comp_host *host; size_t ipc_size = sizeof(*host); @@ -1691,10 +1681,7 @@ static int sof_widget_load_pcm(struct snd_soc_component *scomp, int index, swidget->private = host; - ret = sof_ipc_tx_message(sdev->ipc, host->comp.hdr.cmd, host, - ipc_size, r, sizeof(*r)); - if (ret >= 0) - return ret; + return 0; err: kfree(host); return ret; @@ -1723,8 +1710,7 @@ int sof_load_pipeline_ipc(struct device *dev, static int sof_widget_load_pipeline(struct snd_soc_component *scomp, int index, struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw, - struct sof_ipc_comp_reply *r) + struct snd_soc_tplg_dapm_widget *tw) { struct snd_soc_tplg_private *private = &tw->priv; struct sof_ipc_pipe_new *pipeline; @@ -1764,16 +1750,22 @@ static int sof_widget_load_pipeline(struct snd_soc_component *scomp, int index, goto err; } + ret = sof_parse_tokens(scomp, swidget, pipeline_tokens, + ARRAY_SIZE(pipeline_tokens), private->array, + le32_to_cpu(private->size)); + if (ret != 0) { + dev_err(scomp->dev, "error: parse dynamic pipeline token failed %d\n", + private->size); + goto err; + } + dev_dbg(scomp->dev, "pipeline %s: period %d pri %d mips %d core %d frames %d\n", swidget->widget->name, pipeline->period, pipeline->priority, pipeline->period_mips, pipeline->core, pipeline->frames_per_sched); swidget->private = pipeline; - /* send ipc's to create pipeline comp and power up schedule core */ - ret = sof_load_pipeline_ipc(scomp->dev, pipeline, r); - if (ret >= 0) - return ret; + return 0; err: kfree(pipeline); return ret; @@ -1785,10 +1777,8 @@ err: static int sof_widget_load_mixer(struct snd_soc_component *scomp, int index, struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw, - struct sof_ipc_comp_reply *r) + struct snd_soc_tplg_dapm_widget *tw) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; struct sof_ipc_comp_mixer *mixer; size_t ipc_size = sizeof(*mixer); @@ -1817,12 +1807,7 @@ static int sof_widget_load_mixer(struct snd_soc_component *scomp, int index, swidget->private = mixer; - ret = sof_ipc_tx_message(sdev->ipc, mixer->comp.hdr.cmd, mixer, - ipc_size, r, sizeof(*r)); - if (ret < 0) - kfree(mixer); - - return ret; + return 0; } /* @@ -1830,10 +1815,8 @@ static int sof_widget_load_mixer(struct snd_soc_component *scomp, int index, */ static int sof_widget_load_mux(struct snd_soc_component *scomp, int index, struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw, - struct sof_ipc_comp_reply *r) + struct snd_soc_tplg_dapm_widget *tw) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; struct sof_ipc_comp_mux *mux; size_t ipc_size = sizeof(*mux); @@ -1862,12 +1845,7 @@ static int sof_widget_load_mux(struct snd_soc_component *scomp, int index, swidget->private = mux; - ret = sof_ipc_tx_message(sdev->ipc, mux->comp.hdr.cmd, mux, - ipc_size, r, sizeof(*r)); - if (ret < 0) - kfree(mux); - - return ret; + return 0; } /* @@ -1876,8 +1854,7 @@ static int sof_widget_load_mux(struct snd_soc_component *scomp, int index, static int sof_widget_load_pga(struct snd_soc_component *scomp, int index, struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw, - struct sof_ipc_comp_reply *r) + struct snd_soc_tplg_dapm_widget *tw) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; @@ -1937,10 +1914,7 @@ static int sof_widget_load_pga(struct snd_soc_component *scomp, int index, } } - ret = sof_ipc_tx_message(sdev->ipc, volume->comp.hdr.cmd, volume, - ipc_size, r, sizeof(*r)); - if (ret >= 0) - return ret; + return 0; err: kfree(volume); return ret; @@ -1952,10 +1926,8 @@ err: static int sof_widget_load_src(struct snd_soc_component *scomp, int index, struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw, - struct sof_ipc_comp_reply *r) + struct snd_soc_tplg_dapm_widget *tw) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; struct sof_ipc_comp_src *src; size_t ipc_size = sizeof(*src); @@ -1994,10 +1966,7 @@ static int sof_widget_load_src(struct snd_soc_component *scomp, int index, swidget->private = src; - ret = sof_ipc_tx_message(sdev->ipc, src->comp.hdr.cmd, src, - ipc_size, r, sizeof(*r)); - if (ret >= 0) - return ret; + return 0; err: kfree(src); return ret; @@ -2009,10 +1978,8 @@ err: static int sof_widget_load_asrc(struct snd_soc_component *scomp, int index, struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw, - struct sof_ipc_comp_reply *r) + struct snd_soc_tplg_dapm_widget *tw) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; struct sof_ipc_comp_asrc *asrc; size_t ipc_size = sizeof(*asrc); @@ -2053,10 +2020,7 @@ static int sof_widget_load_asrc(struct snd_soc_component *scomp, int index, swidget->private = asrc; - ret = sof_ipc_tx_message(sdev->ipc, asrc->comp.hdr.cmd, asrc, - ipc_size, r, sizeof(*r)); - if (ret >= 0) - return ret; + return 0; err: kfree(asrc); return ret; @@ -2068,10 +2032,8 @@ err: static int sof_widget_load_siggen(struct snd_soc_component *scomp, int index, struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw, - struct sof_ipc_comp_reply *r) + struct snd_soc_tplg_dapm_widget *tw) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_tplg_private *private = &tw->priv; struct sof_ipc_comp_tone *tone; size_t ipc_size = sizeof(*tone); @@ -2110,10 +2072,7 @@ static int sof_widget_load_siggen(struct snd_soc_component *scomp, int index, swidget->private = tone; - ret = sof_ipc_tx_message(sdev->ipc, tone->comp.hdr.cmd, tone, - ipc_size, r, sizeof(*r)); - if (ret >= 0) - return ret; + return 0; err: kfree(tone); return ret; @@ -2195,10 +2154,8 @@ static int sof_get_control_data(struct snd_soc_component *scomp, static int sof_process_load(struct snd_soc_component *scomp, int index, struct snd_sof_widget *swidget, struct snd_soc_tplg_dapm_widget *tw, - struct sof_ipc_comp_reply *r, int type) { - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_soc_dapm_widget *widget = swidget->widget; struct snd_soc_tplg_private *private = &tw->priv; struct sof_ipc_comp_process *process; @@ -2272,33 +2229,6 @@ static int sof_process_load(struct snd_soc_component *scomp, int index, process->size = ipc_data_size; swidget->private = process; - - ret = sof_ipc_tx_message(sdev->ipc, process->comp.hdr.cmd, process, - ipc_size, r, sizeof(*r)); - - if (ret < 0) { - dev_err(scomp->dev, "error: create process failed\n"); - goto err; - } - - /* we sent the data in single message so return */ - if (ipc_data_size) - goto out; - - /* send control data with large message supported method */ - for (i = 0; i < widget->num_kcontrols; i++) { - wdata[i].control->readback_offset = 0; - ret = snd_sof_ipc_set_get_comp_data(wdata[i].control, - wdata[i].ipc_cmd, - wdata[i].ctrl_type, - wdata[i].control->cmd, - true); - if (ret != 0) { - dev_err(scomp->dev, "error: send control failed\n"); - break; - } - } - err: if (ret < 0) kfree(process); @@ -2314,8 +2244,7 @@ out: static int sof_widget_load_process(struct snd_soc_component *scomp, int index, struct snd_sof_widget *swidget, - struct snd_soc_tplg_dapm_widget *tw, - struct sof_ipc_comp_reply *r) + struct snd_soc_tplg_dapm_widget *tw) { struct snd_soc_tplg_private *private = &tw->priv; struct sof_ipc_comp_process config; @@ -2341,8 +2270,7 @@ static int sof_widget_load_process(struct snd_soc_component *scomp, int index, } /* now load process specific data and send IPC */ - ret = sof_process_load(scomp, index, swidget, tw, r, - find_process_comp_type(config.type)); + ret = sof_process_load(scomp, index, swidget, tw, find_process_comp_type(config.type)); if (ret < 0) { dev_err(scomp->dev, "error: process loading failed\n"); return ret; @@ -2391,8 +2319,6 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); struct snd_sof_widget *swidget; struct snd_sof_dai *dai; - struct sof_ipc_comp_reply reply; - struct snd_sof_control *scontrol; struct sof_ipc_comp comp = { .core = SOF_DSP_PRIMARY_CORE, }; @@ -2409,7 +2335,6 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, swidget->id = w->id; swidget->pipeline_id = index; swidget->private = NULL; - memset(&reply, 0, sizeof(reply)); dev_dbg(scomp->dev, "tplg: ready widget id %d pipe %d type %d name : %s stream %s\n", swidget->comp_id, index, swidget->id, tw->name, @@ -2428,14 +2353,6 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, swidget->core = comp.core; - /* default is primary core, safe to call for already enabled cores */ - ret = sof_core_enable(sdev, comp.core); - if (ret < 0) { - dev_err(scomp->dev, "error: enable core: %d\n", ret); - kfree(swidget); - return ret; - } - ret = sof_parse_tokens(scomp, &swidget->comp_ext, comp_ext_tokens, ARRAY_SIZE(comp_ext_tokens), tw->priv.array, le32_to_cpu(tw->priv.size)); @@ -2456,7 +2373,7 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, return -ENOMEM; } - ret = sof_widget_load_dai(scomp, index, swidget, tw, &reply, dai); + ret = sof_widget_load_dai(scomp, index, swidget, tw, dai); if (ret == 0) { sof_connect_dai_widget(scomp, w, tw, dai); list_add(&dai->list, &sdev->dai_list); @@ -2466,47 +2383,40 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, } break; case snd_soc_dapm_mixer: - ret = sof_widget_load_mixer(scomp, index, swidget, tw, &reply); + ret = sof_widget_load_mixer(scomp, index, swidget, tw); break; case snd_soc_dapm_pga: - ret = sof_widget_load_pga(scomp, index, swidget, tw, &reply); - /* Find scontrol for this pga and set readback offset*/ - list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { - if (scontrol->comp_id == swidget->comp_id) { - scontrol->readback_offset = reply.offset; - break; - } - } + ret = sof_widget_load_pga(scomp, index, swidget, tw); break; case snd_soc_dapm_buffer: - ret = sof_widget_load_buffer(scomp, index, swidget, tw, &reply); + ret = sof_widget_load_buffer(scomp, index, swidget, tw); break; case snd_soc_dapm_scheduler: - ret = sof_widget_load_pipeline(scomp, index, swidget, tw, &reply); + ret = sof_widget_load_pipeline(scomp, index, swidget, tw); break; case snd_soc_dapm_aif_out: ret = sof_widget_load_pcm(scomp, index, swidget, - SOF_IPC_STREAM_CAPTURE, tw, &reply); + SOF_IPC_STREAM_CAPTURE, tw); break; case snd_soc_dapm_aif_in: ret = sof_widget_load_pcm(scomp, index, swidget, - SOF_IPC_STREAM_PLAYBACK, tw, &reply); + SOF_IPC_STREAM_PLAYBACK, tw); break; case snd_soc_dapm_src: - ret = sof_widget_load_src(scomp, index, swidget, tw, &reply); + ret = sof_widget_load_src(scomp, index, swidget, tw); break; case snd_soc_dapm_asrc: - ret = sof_widget_load_asrc(scomp, index, swidget, tw, &reply); + ret = sof_widget_load_asrc(scomp, index, swidget, tw); break; case snd_soc_dapm_siggen: - ret = sof_widget_load_siggen(scomp, index, swidget, tw, &reply); + ret = sof_widget_load_siggen(scomp, index, swidget, tw); break; case snd_soc_dapm_effect: - ret = sof_widget_load_process(scomp, index, swidget, tw, &reply); + ret = sof_widget_load_process(scomp, index, swidget, tw); break; case snd_soc_dapm_mux: case snd_soc_dapm_demux: - ret = sof_widget_load_mux(scomp, index, swidget, tw, &reply); + ret = sof_widget_load_mux(scomp, index, swidget, tw); break; case snd_soc_dapm_switch: case snd_soc_dapm_dai_link: @@ -2517,12 +2427,12 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index, } /* check IPC reply */ - if (ret < 0 || reply.rhdr.error < 0) { + if (ret < 0) { dev_err(scomp->dev, - "error: DSP failed to add widget id %d type %d name : %s stream %s reply %d\n", + "error: failed to add widget id %d type %d name : %s stream %s\n", tw->shift, swidget->id, tw->name, strnlen(tw->sname, SNDRV_CTL_ELEM_ID_NAME_MAXLEN) > 0 - ? tw->sname : "none", reply.rhdr.error); + ? tw->sname : "none"); kfree(swidget); return ret; } @@ -2828,9 +2738,6 @@ static int sof_set_dai_config_multi(struct snd_sof_dev *sdev, u32 size, continue; if (strcmp(link->name, dai->name) == 0) { - struct sof_ipc_reply reply; - int ret; - /* * the same dai config will be applied to all DAIs in * the same dai link. We have to ensure that the ipc @@ -2842,18 +2749,6 @@ static int sof_set_dai_config_multi(struct snd_sof_dev *sdev, u32 size, dev_dbg(sdev->dev, "set DAI config for %s index %d\n", dai->name, config[curr_conf].dai_index); - /* send message to DSP */ - ret = sof_ipc_tx_message(sdev->ipc, - config[curr_conf].hdr.cmd, - &config[curr_conf], size, - &reply, sizeof(reply)); - - if (ret < 0) { - dev_err(sdev->dev, - "error: failed to set DAI config for %s index %d\n", - dai->name, config[curr_conf].dai_index); - return ret; - } dai->number_configs = num_conf; dai->current_config = curr_conf; @@ -2861,9 +2756,6 @@ static int sof_set_dai_config_multi(struct snd_sof_dev *sdev, u32 size, if (!dai->dai_config) return -ENOMEM; - /* set cpu_dai_name */ - dai->cpu_dai_name = link->cpus->dai_name; - found = 1; } } @@ -3383,7 +3275,6 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, struct snd_sof_widget *source_swidget, *sink_swidget; struct snd_soc_dobj *dobj = &route->dobj; struct snd_sof_route *sroute; - struct sof_ipc_reply reply; int ret = 0; /* allocate memory for sroute and connect */ @@ -3458,33 +3349,11 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, route->source, route->sink); goto err; } else { - ret = sof_ipc_tx_message(sdev->ipc, - connect->hdr.cmd, - connect, sizeof(*connect), - &reply, sizeof(reply)); - - /* check IPC return value */ - if (ret < 0) { - dev_err(scomp->dev, "error: failed to add route sink %s control %s source %s\n", - route->sink, - route->control ? route->control : "none", - route->source); - goto err; - } - - /* check IPC reply */ - if (reply.error < 0) { - dev_err(scomp->dev, "error: DSP failed to add route sink %s control %s source %s result %d\n", - route->sink, - route->control ? route->control : "none", - route->source, reply.error); - ret = reply.error; - goto err; - } - sroute->route = route; dobj->private = sroute; sroute->private = connect; + sroute->src_widget = source_swidget; + sroute->sink_widget = sink_swidget; /* add route to route list */ list_add(&sroute->list, &sdev->route_list); @@ -3498,50 +3367,6 @@ err: return ret; } -/* Function to set the initial value of SOF kcontrols. - * The value will be stored in scontrol->control_data - */ -static int snd_sof_cache_kcontrol_val(struct snd_soc_component *scomp) -{ - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct snd_sof_control *scontrol = NULL; - int ipc_cmd, ctrl_type; - int ret = 0; - - list_for_each_entry(scontrol, &sdev->kcontrol_list, list) { - - /* notify DSP of kcontrol values */ - switch (scontrol->cmd) { - case SOF_CTRL_CMD_VOLUME: - case SOF_CTRL_CMD_ENUM: - case SOF_CTRL_CMD_SWITCH: - ipc_cmd = SOF_IPC_COMP_GET_VALUE; - ctrl_type = SOF_CTRL_TYPE_VALUE_CHAN_GET; - break; - case SOF_CTRL_CMD_BINARY: - ipc_cmd = SOF_IPC_COMP_GET_DATA; - ctrl_type = SOF_CTRL_TYPE_DATA_GET; - break; - default: - dev_err(scomp->dev, - "error: Invalid scontrol->cmd: %d\n", - scontrol->cmd); - return -EINVAL; - } - ret = snd_sof_ipc_set_get_comp_data(scontrol, - ipc_cmd, ctrl_type, - scontrol->cmd, - false); - if (ret < 0) { - dev_warn(scomp->dev, - "error: kcontrol value get for widget: %d\n", - scontrol->comp_id); - } - } - - return ret; -} - int snd_sof_complete_pipeline(struct device *dev, struct snd_sof_widget *swidget) { @@ -3566,31 +3391,84 @@ int snd_sof_complete_pipeline(struct device *dev, return 1; } +/** + * sof_set_pipe_widget - Set pipe_widget for a component + * @sdev: pointer to struct snd_sof_dev + * @pipe_widget: pointer to struct snd_sof_widget of type snd_soc_dapm_scheduler + * @swidget: pointer to struct snd_sof_widget that has the same pipeline ID as @pipe_widget + * + * Return: 0 if successful, -EINVAL on error. + * The function checks if @swidget is associated with any volatile controls. If so, setting + * the dynamic_pipeline_widget is disallowed. + */ +static int sof_set_pipe_widget(struct snd_sof_dev *sdev, struct snd_sof_widget *pipe_widget, + struct snd_sof_widget *swidget) +{ + struct snd_sof_control *scontrol; + + if (pipe_widget->dynamic_pipeline_widget) { + /* dynamic widgets cannot have volatile kcontrols */ + list_for_each_entry(scontrol, &sdev->kcontrol_list, list) + if (scontrol->comp_id == swidget->comp_id && + (scontrol->access & SNDRV_CTL_ELEM_ACCESS_VOLATILE)) { + dev_err(sdev->dev, + "error: volatile control found for dynamic widget %s\n", + swidget->widget->name); + return -EINVAL; + } + } + + /* set the pipe_widget and apply the dynamic_pipeline_widget_flag */ + swidget->pipe_widget = pipe_widget; + swidget->dynamic_pipeline_widget = pipe_widget->dynamic_pipeline_widget; + + return 0; +} + /* completion - called at completion of firmware loading */ -static void sof_complete(struct snd_soc_component *scomp) +static int sof_complete(struct snd_soc_component *scomp) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); - struct snd_sof_widget *swidget; + struct snd_sof_widget *swidget, *comp_swidget; + int ret; - /* some widget types require completion notificattion */ + /* set the pipe_widget and apply the dynamic_pipeline_widget_flag */ list_for_each_entry(swidget, &sdev->widget_list, list) { - if (swidget->complete) - continue; - switch (swidget->id) { case snd_soc_dapm_scheduler: - swidget->complete = - snd_sof_complete_pipeline(scomp->dev, swidget); + /* + * Apply the dynamic_pipeline_widget flag and set the pipe_widget field + * for all widgets that have the same pipeline ID as the scheduler widget + */ + list_for_each_entry_reverse(comp_swidget, &sdev->widget_list, list) + if (comp_swidget->pipeline_id == swidget->pipeline_id) { + ret = sof_set_pipe_widget(sdev, swidget, comp_swidget); + if (ret < 0) + return ret; + } break; default: break; } } - /* - * cache initial values of SOF kcontrols by reading DSP value over - * IPC. It may be overwritten by alsa-mixer after booting up - */ - snd_sof_cache_kcontrol_val(scomp); + + /* verify topology components loading including dynamic pipelines */ + if (sof_core_debug & SOF_DBG_VERIFY_TPLG) { + ret = sof_set_up_pipelines(scomp->dev, true); + if (ret < 0) { + dev_err(sdev->dev, "error: topology verification failed %d\n", ret); + return ret; + } + + ret = sof_tear_down_pipelines(scomp->dev, true); + if (ret < 0) { + dev_err(sdev->dev, "error: topology tear down pipelines failed %d\n", ret); + return ret; + } + } + + /* set up static pipelines */ + return sof_set_up_pipelines(scomp->dev, false); } /* manifest - optional to inform component of manifest */ |