[PATCH 00/19] Make the SOF control, PCM and PM code IPC agnostic
This series is a continuation to the SOF IPC abstraction work to support the new IPC version introduced in the SOF firmware. It makes the top-level control IO, PCM and PM code IPC-agnostic. Other than the first patch, the rest are purely for abstraction and include no changes in functionality.
Ranjani Sridharan (19): ASoC: SOF: set up scheduler widget before all other widgets in the pipeline ASoC: SOF: Make sof_widget_setup/free IPC agnostic ASoC: SOF: Make sof_suspend/resume IPC agnostic ASoC: SOF: Introduce IPC ops for kcontrol IO ASoC: SOF: Add IPC3 topology control ops ASoC: SOF: Add volume_get/put IPC3 ops ASoC: SOF: Add switch get/put IPC3 ops ASoC: SOF: Add enum_get/put control ops for IPC3 ASoC: SOF: Add bytes_get/put control IPC ops for IPC3 ASoC: SOF: Add bytes_ext control IPC ops for IPC3 ASoC: SOF: Introduce IPC-specific PCM ops ASoC: SOF: pcm: expose the sof_pcm_setup_connected_widgets() function ASoC: SOF: Introduce IPC3 PCM hw_free op ASoC: SOF: Define hw_params PCM op for IPC3 ASoC: SOF: Add trigger PCM op for IPC3 ASoC: SOF: Add dai_link_fixup PCM op for IPC3 ASoC: SOF: expose sof_route_setup() ASoC: SOF: topology: Add ops for setting up and tearing down pipelines ASoC: SOF: Add a new dai_get_clk topology IPC op
sound/soc/sof/Makefile | 2 +- sound/soc/sof/control.c | 545 ++++--------------------------- sound/soc/sof/intel/hda-dai.c | 97 +----- sound/soc/sof/intel/hda.c | 169 +++------- sound/soc/sof/intel/hda.h | 7 +- sound/soc/sof/ipc.c | 10 +- sound/soc/sof/ipc3-control.c | 594 ++++++++++++++++++++++++++++++++++ sound/soc/sof/ipc3-ops.h | 21 ++ sound/soc/sof/ipc3-pcm.c | 372 +++++++++++++++++++++ sound/soc/sof/ipc3-topology.c | 384 +++++++++++++++++++++- sound/soc/sof/ipc3.c | 45 +++ sound/soc/sof/pcm.c | 354 +++----------------- sound/soc/sof/pm.c | 80 ++--- sound/soc/sof/sof-audio.c | 442 +++++-------------------- sound/soc/sof/sof-audio.h | 70 +++- sound/soc/sof/sof-priv.h | 17 +- sound/soc/sof/topology.c | 55 ++-- 17 files changed, 1805 insertions(+), 1459 deletions(-) create mode 100644 sound/soc/sof/ipc3-control.c create mode 100644 sound/soc/sof/ipc3-ops.h create mode 100644 sound/soc/sof/ipc3-pcm.c create mode 100644 sound/soc/sof/ipc3.c
For dynamic pipelines, We set up the DAI widget during BE DAI hw_params and this results in it getting set up before the scheduler widget for the pipeline it belongs to is set up. Move the scheduler widget set up into sof_widget_setup() to ensure that the scheduler widget is always the first widget in a pipeline to be set up and the last one to get freed after all the other widgets have been freed.
Fixes: 5fcdbb2d45df ('ASoC: SOF: Add support for dynamic pipelines')
Signed-off-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Péter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- sound/soc/sof/sof-audio.c | 82 +++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 38 deletions(-)
diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 683c290bb69a..cc0d647a2d0d 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -104,7 +104,7 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) .id = swidget->comp_id, }; struct sof_ipc_reply reply; - int ret, ret1; + int ret, err;
if (!swidget->private) return 0; @@ -136,31 +136,41 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) }
/* continue to disable core even if IPC fails */ - ret = sof_ipc_tx_message(sdev->ipc, ipc_free.hdr.cmd, &ipc_free, sizeof(ipc_free), + err = sof_ipc_tx_message(sdev->ipc, ipc_free.hdr.cmd, &ipc_free, sizeof(ipc_free), &reply, sizeof(reply)); - if (ret < 0) + if (err < 0) dev_err(sdev->dev, "error: failed to free widget %s\n", swidget->widget->name);
/* * disable widget core. continue to route setup status and complete flag * even if this fails and return the appropriate error */ - ret1 = snd_sof_dsp_core_put(sdev, swidget->core); - if (ret1 < 0) { + ret = snd_sof_dsp_core_put(sdev, swidget->core); + if (ret < 0) { dev_err(sdev->dev, "error: failed to disable target core: %d for widget %s\n", swidget->core, swidget->widget->name); - if (!ret) - ret = ret1; + if (!err) + err = ret; }
/* reset route setup status for all routes that contain this widget */ sof_reset_route_setup_status(sdev, swidget); swidget->complete = 0;
- if (!ret) + /* + * free the scheduler widget (same as pipe_widget) associated with the current swidget. + * skip for static pipelines + */ + if (swidget->dynamic_pipeline_widget && swidget->id != snd_soc_dapm_scheduler) { + ret = sof_widget_free(sdev, swidget->pipe_widget); + if (ret < 0 && !err) + err = ret; + } + + if (!err) dev_dbg(sdev->dev, "widget %s freed\n", swidget->widget->name);
- return ret; + return err; } EXPORT_SYMBOL(sof_widget_free);
@@ -181,12 +191,32 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) if (++swidget->use_count > 1) return 0;
+ /* + * 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. Skip setting up scheduler widget for static pipelines. + */ + if (swidget->dynamic_pipeline_widget && swidget->id != snd_soc_dapm_scheduler) { + if (!swidget->pipe_widget) { + dev_err(sdev->dev, "No scheduler widget set for %s\n", + swidget->widget->name); + ret = -EINVAL; + goto use_count_dec; + } + + ret = sof_widget_setup(sdev, swidget->pipe_widget); + if (ret < 0) + goto use_count_dec; + } + /* enable widget core */ ret = snd_sof_dsp_core_get(sdev, swidget->core); if (ret < 0) { dev_err(sdev->dev, "error: failed to enable target core for widget %s\n", swidget->widget->name); - goto use_count_dec; + goto pipe_widget_free; }
switch (swidget->id) { @@ -257,6 +287,9 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
core_put: snd_sof_dsp_core_put(sdev, swidget->core); +pipe_widget_free: + if (swidget->id != snd_soc_dapm_scheduler) + sof_widget_free(sdev, swidget->pipe_widget); use_count_dec: swidget->use_count--; return ret; @@ -374,36 +407,14 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, in /* 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); + if (ret < 0) goto widget_free; - } }
/* @@ -456,7 +467,6 @@ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, in break;
sof_widget_free(sdev, swidget); - sof_widget_free(sdev, swidget->pipe_widget); }
return ret; @@ -490,10 +500,6 @@ int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int 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);
Add 3 new topology IPC ops for widget_setup, widget_free and dai_config in order to make the pipeline management code IPC agnostic and implement the ops for IPC3.
Use the newly introduced tplg dai_config op to configure the DAI during BE DAI hw_params and hw_free.
Also, in preparation for IPC4, modify BE hw_params callback to skip setting up the DAI widget. All widgets will be set up during FW hw_params and the DAI_CONFIG IPC should be sent only if the widget use_count is > 0. With setting up/freeing removed from the BE hw_params, remove the configured flag as it is no longer needed.
Signed-off-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Péter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Bard Liao yung-chuan.liao@linux.intel.com --- sound/soc/sof/intel/hda-dai.c | 97 +++---------------- sound/soc/sof/intel/hda.c | 169 +++++++++------------------------- sound/soc/sof/intel/hda.h | 7 +- sound/soc/sof/ipc3-topology.c | 150 ++++++++++++++++++++++++++++++ sound/soc/sof/sof-audio.c | 136 +++++---------------------- sound/soc/sof/sof-audio.h | 13 ++- 6 files changed, 246 insertions(+), 326 deletions(-)
diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 9b78eea8d76b..f9cb9f1f0237 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -162,58 +162,19 @@ static int hda_link_dma_params(struct hdac_ext_stream *hext_stream, return 0; }
-/* 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_dai_private_data *private; - struct sof_ipc_dai_config *config; - struct snd_sof_dai *sof_dai; - - if (!swidget) - return NULL; - - sof_dai = swidget->private; - - if (!sof_dai || !sof_dai->private) { - dev_err(swidget->scomp->dev, "%s: No private data for DAI %s\n", __func__, - w->name); - return NULL; - } - - private = sof_dai->private; - if (!private->dai_config) { - dev_err(swidget->scomp->dev, "%s: No config for DAI %s\n", __func__, w->name); - return NULL; - } - - config = &private->dai_config[sof_dai->current_config]; - - /* update config with stream tag */ - config->hda.link_dma_ch = channel; - - return config; -} - 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; + struct snd_sof_dai_config_data data;
- config = hda_dai_update_config(w, channel); - if (!config) { - dev_err(sdev->dev, "error: no config for DAI %s\n", w->name); - return -ENOENT; - } + data.dai_data = channel;
/* set up/free DAI widget and send DAI_CONFIG IPC */ if (widget_setup) - return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_2_STEP_STOP); + return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_2_STEP_STOP, &data);
- return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE); + return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE, &data); }
static int hda_link_hw_params(struct snd_pcm_substream *substream, @@ -302,35 +263,16 @@ static int hda_link_dai_config_pause_push_ipc(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_dai_private_data *private; - 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->private) { - dev_err(sdev->dev, "%s: No private data for DAI %s\n", __func__, w->name); - return -EINVAL; - } + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; + int ret = 0;
- private = sof_dai->private; - if (!private->dai_config) { - dev_err(sdev->dev, "%s: No config for DAI %s\n", __func__, w->name); - return -EINVAL; + if (tplg_ops->dai_config) { + ret = tplg_ops->dai_config(sdev, swidget, SOF_DAI_CONFIG_FLAGS_PAUSE, NULL); + if (ret < 0) + dev_err(sdev->dev, "%s: DAI config failed for widget %s\n", __func__, + w->name); }
- config = &private->dai_config[sof_dai->current_config]; - - /* set PAUSE command flag */ - config->flags = FIELD_PREP(SOF_DAI_CONFIG_FLAGS_CMD_MASK, SOF_DAI_CONFIG_FLAGS_PAUSE); - - ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size, - &reply, sizeof(reply)); - if (ret < 0) - dev_err(sdev->dev, "DAI config for %s failed during pause push\n", w->name); - return ret; }
@@ -470,30 +412,17 @@ struct ssp_dai_dma_data { static int ssp_dai_setup_or_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai, bool setup) { - 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; - if (setup) - return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE); + return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE, NULL);
- return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE); + return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE, NULL); }
static int ssp_dai_startup(struct snd_pcm_substream *substream, diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c index 7403b9848a56..019f8d6b91d4 100644 --- a/sound/soc/sof/intel/hda.c +++ b/sound/soc/sof/intel/hda.c @@ -41,114 +41,68 @@ #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, unsigned int quirk_flags) +int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w, unsigned int quirk_flags, + struct snd_sof_dai_config_data *data) { 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 sof_dai_private_data *private; - struct snd_sof_dai *sof_dai; - struct sof_ipc_reply reply; + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; + struct snd_sof_dai *sof_dai = swidget->private; int ret;
- sof_dai = swidget->private; - - if (!sof_dai || !sof_dai->private) { - dev_err(sdev->dev, "%s: No private data for DAI %s\n", __func__, w->name); - return -EINVAL; - } - - private = sof_dai->private; - if (!private->dai_config) { - dev_err(sdev->dev, "%s: No config for DAI %s\n", __func__, w->name); + if (!sof_dai) { + dev_err(sdev->dev, "%s: No DAI for DAI widget %s\n", __func__, w->name); return -EINVAL; }
- /* DAI already configured, reset it before reconfiguring it */ - if (sof_dai->configured) { - ret = hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE); - if (ret < 0) - return ret; - } - - config = &private->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; - } + if (tplg_ops->dai_config) { + unsigned int flags;
- /* set HW_PARAMS flag along with quirks */ - config->flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS | - quirk_flags << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT; + /* set HW_PARAMS flag along with quirks */ + flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS | + quirk_flags << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT;
- - /* 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; + ret = tplg_ops->dai_config(sdev, swidget, flags, data); + if (ret < 0) { + dev_err(sdev->dev, "%s: DAI config failed for widget %s\n", __func__, + w->name); + return ret; + } }
- sof_dai->configured = true; - return 0; }
-int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_flags) +int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_flags, + struct snd_sof_dai_config_data *data) { 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_dai_private_data *private; - struct sof_ipc_dai_config *config; - struct snd_sof_dai *sof_dai; - struct sof_ipc_reply reply; - int ret; + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; + struct snd_sof_dai *sof_dai = swidget->private;
- sof_dai = swidget->private; - - if (!sof_dai || !sof_dai->private) { - dev_err(sdev->dev, "%s: No private data for DAI %s\n", __func__, w->name); - return -EINVAL; - } - - private = sof_dai->private; - if (!private->dai_config) { - dev_err(sdev->dev, "%s: No config for DAI %s\n", __func__, w->name); + if (!sof_dai) { + dev_err(sdev->dev, "%s: No DAI for BE DAI widget %s\n", __func__, 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 = &private->dai_config[sof_dai->current_config]; + if (tplg_ops->dai_config) { + unsigned int flags; + int ret;
- /* set HW_FREE flag along with any quirks */ - config->flags = SOF_DAI_CONFIG_FLAGS_HW_FREE | - quirk_flags << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT; + /* set HW_FREE flag along with any quirks */ + flags = SOF_DAI_CONFIG_FLAGS_HW_FREE | + quirk_flags << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT;
- 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; + ret = tplg_ops->dai_config(sdev, swidget, flags, data); + if (ret < 0) + dev_err(sdev->dev, "%s: DAI config failed for widget '%s'\n", __func__, + w->name); + }
- return sof_widget_free(sdev, swidget); + return 0; }
#if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) @@ -163,69 +117,34 @@ 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_dai_private_data *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->private) { - dev_err(sdev->dev, "%s: No private data for DAI %s\n", __func__, w->name); - return -EINVAL; - } - - private = sof_dai->private; - if (!private->dai_config) { - dev_err(sdev->dev, "%s: No config for DAI %s\n", __func__, w->name); - return -EINVAL; - } - - config = &private->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, SOF_DAI_CONFIG_FLAGS_NONE); - - return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE); -} - static int sdw_params_stream(struct device *dev, struct sdw_intel_stream_params_data *params_data) { - struct snd_sof_dev *sdev = dev_get_drvdata(dev); struct snd_soc_dai *d = params_data->dai; + struct snd_sof_dai_config_data data; struct snd_soc_dapm_widget *w;
w = snd_soc_dai_get_widget(d, params_data->stream); + data.dai_index = (params_data->link_id << 8) | d->id; + data.dai_data = params_data->alh_stream_id;
- return sdw_dai_config_ipc(sdev, w, params_data->link_id, params_data->alh_stream_id, - d->id, true); + return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE, &data); }
static int sdw_free_stream(struct device *dev, struct sdw_intel_stream_free_data *free_data) { - struct snd_sof_dev *sdev = dev_get_drvdata(dev); struct snd_soc_dai *d = free_data->dai; + struct snd_sof_dai_config_data data; struct snd_soc_dapm_widget *w;
w = snd_soc_dai_get_widget(d, free_data->stream); + data.dai_index = (free_data->link_id << 8) | d->id;
/* send invalid stream_id */ - return sdw_dai_config_ipc(sdev, w, free_data->link_id, 0xFFFF, d->id, false); + data.dai_data = 0xFFFF; + + return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE, &data); }
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 13b509c9f481..05e5e158614a 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -17,6 +17,7 @@ #include <sound/hda_codec.h> #include <sound/hdaudio_ext.h> #include "../sof-client-probes.h" +#include "../sof-audio.h" #include "shim.h"
/* PCI registers */ @@ -730,8 +731,10 @@ 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, unsigned int quirk_flags); -int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_flags); +int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w, unsigned int quirk_flags, + struct snd_sof_dai_config_data *data); +int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w, unsigned int quirk_flags, + struct snd_sof_dai_config_data *data);
#define SOF_HDA_POSITION_QUIRK_USE_SKYLAKE_LEGACY (0) /* previous implementation */ #define SOF_HDA_POSITION_QUIRK_USE_DPIB_REGISTERS (1) /* recommended if VC0 only */ diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index fe1d5a56080a..8d08ffb37008 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -1909,6 +1909,153 @@ static int sof_ipc3_complete_pipeline(struct snd_sof_dev *sdev, struct snd_sof_w return 1; }
+static int sof_ipc3_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; + + 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, "failed to free widget %s\n", swidget->widget->name); + + return ret; +} + +static int sof_ipc3_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, + unsigned int flags, struct snd_sof_dai_config_data *data) +{ + struct sof_ipc_fw_version *v = &sdev->fw_ready.version; + struct snd_sof_dai *dai = swidget->private; + struct sof_dai_private_data *private; + struct sof_ipc_dai_config *config; + struct sof_ipc_reply reply; + int ret = 0; + + if (!dai || !dai->private) { + dev_err(sdev->dev, "No private data for DAI %s\n", swidget->widget->name); + return -EINVAL; + } + + private = dai->private; + if (!private->dai_config) { + dev_err(sdev->dev, "No config for DAI %s\n", dai->name); + return -EINVAL; + } + + config = &private->dai_config[dai->current_config]; + if (!config) { + dev_err(sdev->dev, "Invalid current config for DAI %s\n", dai->name); + return -EINVAL; + } + + switch (config->type) { + case SOF_DAI_INTEL_SSP: + /* + * DAI_CONFIG IPC during hw_params/hw_free for SSP DAI's is not supported in older + * firmware + */ + if (v->abi_version < SOF_ABI_VER(3, 18, 0) && + ((flags & SOF_DAI_CONFIG_FLAGS_HW_PARAMS) || + (flags & SOF_DAI_CONFIG_FLAGS_HW_FREE))) + return 0; + break; + case SOF_DAI_INTEL_HDA: + if (data) + config->hda.link_dma_ch = data->dai_data; + break; + case SOF_DAI_INTEL_ALH: + if (data) { + config->dai_index = data->dai_index; + config->alh.stream_id = data->dai_data; + } + break; + default: + break; + } + + config->flags = flags; + + /* only send the IPC if the widget is set up in the DSP */ + if (swidget->use_count > 0) { + ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size, + &reply, sizeof(reply)); + if (ret < 0) + dev_err(sdev->dev, "Failed to set dai config for %s\n", dai->name); + } + + return ret; +} + +static int sof_ipc3_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) +{ + struct sof_ipc_comp_reply reply; + int ret; + + if (!swidget->private) + return 0; + + switch (swidget->id) { + case snd_soc_dapm_dai_in: + case snd_soc_dapm_dai_out: + { + struct snd_sof_dai *dai = swidget->private; + struct sof_dai_private_data *dai_data = dai->private; + struct sof_ipc_comp *comp = &dai_data->comp_dai->comp; + + ret = sof_ipc_tx_message(sdev->ipc, comp->hdr.cmd, dai_data->comp_dai, + comp->hdr.size, &reply, sizeof(reply)); + break; + } + case snd_soc_dapm_scheduler: + { + struct sof_ipc_pipe_new *pipeline; + + pipeline = swidget->private; + ret = sof_ipc_tx_message(sdev->ipc, pipeline->hdr.cmd, pipeline, + sizeof(*pipeline), &reply, sizeof(reply)); + break; + } + default: + { + struct sof_ipc_cmd_hdr *hdr; + + hdr = swidget->private; + ret = sof_ipc_tx_message(sdev->ipc, hdr->cmd, swidget->private, hdr->size, + &reply, sizeof(reply)); + break; + } + } + if (ret < 0) + dev_err(sdev->dev, "Failed to setup widget %s\n", swidget->widget->name); + + return ret; +} + /* token list for each topology object */ static enum sof_tokens host_token_list[] = { SOF_CORE_TOKENS, @@ -2012,6 +2159,9 @@ static const struct sof_ipc_tplg_ops ipc3_tplg_ops = { .control_free = sof_ipc3_control_free, .pipeline_complete = sof_ipc3_complete_pipeline, .token_list = ipc3_token_list, + .widget_free = sof_ipc3_widget_free, + .widget_setup = sof_ipc3_widget_setup, + .dai_config = sof_ipc3_dai_config, };
const struct sof_ipc_ops ipc3_ops = { diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index cc0d647a2d0d..7aa4ac313de3 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -27,31 +27,6 @@ static int sof_kcontrol_setup(struct snd_sof_dev *sdev, struct snd_sof_control * return ret; }
-static int sof_dai_config_setup(struct snd_sof_dev *sdev, struct snd_sof_dai *dai) -{ - struct sof_dai_private_data *private = dai->private; - struct sof_ipc_dai_config *config; - struct sof_ipc_reply reply; - int ret; - - config = &private->dai_config[dai->current_config]; - if (!config) { - dev_err(sdev->dev, "error: no config for DAI %s\n", dai->name); - return -EINVAL; - } - - /* set NONE flag to clear all previous settings */ - config->flags = SOF_DAI_CONFIG_FLAGS_NONE; - - 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; @@ -96,15 +71,9 @@ static void sof_reset_route_setup_status(struct snd_sof_dev *sdev, struct snd_so
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, err; + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; + int err = 0; + int ret;
if (!swidget->private) return 0; @@ -113,33 +82,9 @@ int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) 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; - case snd_soc_dapm_dai_in: - case snd_soc_dapm_dai_out: - { - struct snd_sof_dai *dai = swidget->private; - - dai->configured = false; - fallthrough; - } - default: - ipc_free.hdr.cmd |= SOF_IPC_TPLG_COMP_FREE; - break; - } - /* continue to disable core even if IPC fails */ - err = sof_ipc_tx_message(sdev->ipc, ipc_free.hdr.cmd, &ipc_free, sizeof(ipc_free), - &reply, sizeof(reply)); - if (err < 0) - dev_err(sdev->dev, "error: failed to free widget %s\n", swidget->widget->name); + if (tplg_ops->widget_free) + err = tplg_ops->widget_free(sdev, swidget);
/* * disable widget core. continue to route setup status and complete flag @@ -176,11 +121,7 @@ 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; + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; int ret;
/* skip if there is no private data */ @@ -219,53 +160,22 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) goto pipe_widget_free; }
- switch (swidget->id) { - case snd_soc_dapm_dai_in: - case snd_soc_dapm_dai_out: - { - struct sof_dai_private_data *dai_data; - - dai = swidget->private; - dai_data = dai->private; - comp = &dai_data->comp_dai->comp; - dai->configured = false; - - ret = sof_ipc_tx_message(sdev->ipc, comp->hdr.cmd, dai_data->comp_dai, - comp->hdr.size, &r, sizeof(r)); - if (ret < 0) { - dev_err(sdev->dev, "error: failed to load widget %s\n", - swidget->widget->name); + /* setup widget in the DSP */ + if (tplg_ops->widget_setup) { + ret = tplg_ops->widget_setup(sdev, swidget); + if (ret < 0) goto core_put; - } + }
- 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); + /* send config for DAI components */ + if (WIDGET_IS_DAI(swidget->id)) { + unsigned int flags = SOF_DAI_CONFIG_FLAGS_NONE;
- /* - * widget use_count and core ref_count will both be decremented by - * sof_widget_free() - */ - sof_widget_free(sdev, swidget); - return ret; + if (tplg_ops->dai_config) { + ret = tplg_ops->dai_config(sdev, swidget, flags, NULL); + if (ret < 0) + goto widget_free; } - break; - } - case snd_soc_dapm_scheduler: - pipeline = swidget->private; - ret = sof_ipc_tx_message(sdev->ipc, pipeline->hdr.cmd, pipeline, - sizeof(*pipeline), &r, sizeof(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 core_put; }
/* restore kcontrols for widget */ @@ -273,18 +183,16 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) if (ret < 0) { dev_err(sdev->dev, "error: failed to restore kcontrols for widget %s\n", swidget->widget->name); - /* - * widget use_count and core ref_count will both be decremented by - * sof_widget_free() - */ - sof_widget_free(sdev, swidget); - return ret; + goto widget_free; }
dev_dbg(sdev->dev, "widget %s setup complete\n", swidget->widget->name);
return 0;
+widget_free: + /* widget use_count and core ref_count will both be decremented by sof_widget_free() */ + sof_widget_free(sdev, swidget); core_put: snd_sof_dsp_core_put(sdev, swidget->core); pipe_widget_free: diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 622d43707b27..19386184d8f3 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -40,6 +40,11 @@ struct snd_sof_widget; struct snd_sof_route; struct snd_sof_control;
+struct snd_sof_dai_config_data { + int dai_index; + int dai_data; /* contains DAI-specific information */ +}; + /** * struct sof_ipc_tplg_widget_ops - IPC-specific ops for topology widgets * @ipc_setup: Function pointer for setting up widget IPC params @@ -69,6 +74,9 @@ struct sof_ipc_tplg_widget_ops { * @control_setup: Function pointer for setting up kcontrol IPC-specific data * @control_free: Function pointer for freeing kcontrol IPC-specific data * @pipeline_complete: Function pointer for pipeline complete IPC + * @widget_setup: Function pointer for setting up setup in the DSP + * @widget_free: Function pointer for freeing widget in the DSP + * @dai_config: Function pointer for sending DAI config IPC to the DSP */ struct sof_ipc_tplg_ops { const struct sof_ipc_tplg_widget_ops *widget; @@ -77,6 +85,10 @@ struct sof_ipc_tplg_ops { int (*control_setup)(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol); int (*control_free)(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol); int (*pipeline_complete)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget); + int (*widget_setup)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget); + int (*widget_free)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget); + int (*dai_config)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, + unsigned int flags, struct snd_sof_dai_config_data *data); };
/** struct snd_sof_tuple - Tuple info @@ -276,7 +288,6 @@ struct snd_sof_dai {
int number_configs; int current_config; - bool configured; /* DAI configured during BE hw_params */ struct list_head list; /* list in sdev dai list */ void *private; };
Add a new set of IPC ops for PM with the ctx_save and ctx_restore ops for suspend/resume and implement the ops for IPC3.
Signed-off-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Péter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- sound/soc/sof/Makefile | 2 +- sound/soc/sof/ipc.c | 1 + sound/soc/sof/ipc3-ops.h | 19 +++++++++++ sound/soc/sof/ipc3-topology.c | 7 ++-- sound/soc/sof/ipc3.c | 44 ++++++++++++++++++++++++ sound/soc/sof/pm.c | 63 ++++++++++++++--------------------- sound/soc/sof/sof-priv.h | 14 ++++++-- 7 files changed, 104 insertions(+), 46 deletions(-) create mode 100644 sound/soc/sof/ipc3-ops.h create mode 100644 sound/soc/sof/ipc3.c
diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index e13dab59764c..59482903a243 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -2,7 +2,7 @@
snd-sof-objs := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\ control.o trace.o iomem-utils.o sof-audio.o stream-ipc.o\ - ipc3-topology.o + ipc3-topology.o ipc3.o ifneq ($(CONFIG_SND_SOC_SOF_CLIENT),) snd-sof-objs += sof-client.o endif diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 19a294cbbb8d..46a989be9a82 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -17,6 +17,7 @@ #include "sof-priv.h" #include "sof-audio.h" #include "ops.h" +#include "ipc3-ops.h"
typedef void (*ipc_rx_callback)(struct snd_sof_dev *sdev, void *msg_buf);
diff --git a/sound/soc/sof/ipc3-ops.h b/sound/soc/sof/ipc3-ops.h new file mode 100644 index 000000000000..5d8cab92c1a4 --- /dev/null +++ b/sound/soc/sof/ipc3-ops.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ +/* + * This file is provided under a dual BSD/GPLv2 license. When using or + * redistributing this file, you may do so under either license. + * + * Copyright(c) 2021 Intel Corporation. All rights reserved. + * + * Author: Ranjani Sridharan ranjani.sridharan@linux.intel.com + */ + +#ifndef __SOUND_SOC_SOF_IPC3_OPS_H +#define __SOUND_SOC_SOF_IPC3_OPS_H + +#include "sof-priv.h" + +extern const struct sof_ipc_tplg_ops ipc3_tplg_ops; +extern const struct sof_ipc_ops ipc3_ops; + +#endif diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index 8d08ffb37008..bf0cf38f4524 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -11,6 +11,7 @@ #include <sound/pcm_params.h> #include "sof-priv.h" #include "sof-audio.h" +#include "ipc3-ops.h" #include "ops.h"
/* Full volume for default values */ @@ -2152,7 +2153,7 @@ static const struct sof_ipc_tplg_widget_ops tplg_ipc3_widget_ops[SND_SOC_DAPM_TY sof_ipc3_widget_bind_event}, };
-static const struct sof_ipc_tplg_ops ipc3_tplg_ops = { +const struct sof_ipc_tplg_ops ipc3_tplg_ops = { .widget = tplg_ipc3_widget_ops, .route_setup = sof_ipc3_route_setup, .control_setup = sof_ipc3_control_setup, @@ -2163,7 +2164,3 @@ static const struct sof_ipc_tplg_ops ipc3_tplg_ops = { .widget_setup = sof_ipc3_widget_setup, .dai_config = sof_ipc3_dai_config, }; - -const struct sof_ipc_ops ipc3_ops = { - .tplg = &ipc3_tplg_ops, -}; diff --git a/sound/soc/sof/ipc3.c b/sound/soc/sof/ipc3.c new file mode 100644 index 000000000000..e71cf30908c6 --- /dev/null +++ b/sound/soc/sof/ipc3.c @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2021 Intel Corporation. All rights reserved. +// +// + +#include "sof-priv.h" +#include "ipc3-ops.h" + +static int sof_ipc3_ctx_ipc(struct snd_sof_dev *sdev, int cmd) +{ + struct sof_ipc_pm_ctx pm_ctx = { + .hdr.size = sizeof(pm_ctx), + .hdr.cmd = SOF_IPC_GLB_PM_MSG | cmd, + }; + struct sof_ipc_reply reply; + + /* send ctx save ipc to dsp */ + return sof_ipc_tx_message(sdev->ipc, pm_ctx.hdr.cmd, &pm_ctx, + sizeof(pm_ctx), &reply, sizeof(reply)); +} + +static int sof_ipc3_ctx_save(struct snd_sof_dev *sdev) +{ + return sof_ipc3_ctx_ipc(sdev, SOF_IPC_PM_CTX_SAVE); +} + +static int sof_ipc3_ctx_restore(struct snd_sof_dev *sdev) +{ + return sof_ipc3_ctx_ipc(sdev, SOF_IPC_PM_CTX_RESTORE); +} + +static const struct sof_ipc_pm_ops ipc3_pm_ops = { + .ctx_save = sof_ipc3_ctx_save, + .ctx_restore = sof_ipc3_ctx_restore, +}; + +const struct sof_ipc_ops ipc3_ops = { + .tplg = &ipc3_tplg_ops, + .pm = &ipc3_pm_ops, +}; diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 7300ecadabd9..10adbbd0a9cd 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -48,22 +48,6 @@ static u32 snd_sof_dsp_power_target(struct snd_sof_dev *sdev) return target_dsp_state; }
-static int sof_send_pm_ctx_ipc(struct snd_sof_dev *sdev, int cmd) -{ - struct sof_ipc_pm_ctx pm_ctx; - struct sof_ipc_reply reply; - - memset(&pm_ctx, 0, sizeof(pm_ctx)); - - /* configure ctx save ipc message */ - pm_ctx.hdr.size = sizeof(pm_ctx); - pm_ctx.hdr.cmd = SOF_IPC_GLB_PM_MSG | cmd; - - /* send ctx save ipc to dsp */ - return sof_ipc_tx_message(sdev->ipc, pm_ctx.hdr.cmd, &pm_ctx, - sizeof(pm_ctx), &reply, sizeof(reply)); -} - #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_ENABLE_DEBUGFS_CACHE) static void sof_cache_debugfs(struct snd_sof_dev *sdev) { @@ -86,6 +70,7 @@ static void sof_cache_debugfs(struct snd_sof_dev *sdev) static int sof_resume(struct device *dev, bool runtime_resume) { struct snd_sof_dev *sdev = dev_get_drvdata(dev); + const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm; u32 old_state = sdev->dsp_power_state.state; int ret;
@@ -171,11 +156,11 @@ static int sof_resume(struct device *dev, bool runtime_resume) sof_resume_clients(sdev);
/* notify DSP of system resume */ - ret = sof_send_pm_ctx_ipc(sdev, SOF_IPC_PM_CTX_RESTORE); - if (ret < 0) - dev_err(sdev->dev, - "error: ctx_restore ipc error during resume %d\n", - ret); + if (pm_ops && pm_ops->ctx_restore) { + ret = pm_ops->ctx_restore(sdev); + if (ret < 0) + dev_err(sdev->dev, "ctx_restore IPC error during resume: %d\n", ret); + }
return ret; } @@ -183,6 +168,7 @@ static int sof_resume(struct device *dev, bool runtime_resume) static int sof_suspend(struct device *dev, bool runtime_suspend) { struct snd_sof_dev *sdev = dev_get_drvdata(dev); + const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm; pm_message_t pm_state; u32 target_state = 0; int ret; @@ -232,21 +218,20 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) sof_cache_debugfs(sdev); #endif /* notify DSP of upcoming power down */ - ret = sof_send_pm_ctx_ipc(sdev, SOF_IPC_PM_CTX_SAVE); - if (ret == -EBUSY || ret == -EAGAIN) { - /* - * runtime PM has logic to handle -EBUSY/-EAGAIN so - * pass these errors up - */ - dev_err(sdev->dev, - "error: ctx_save ipc error during suspend %d\n", - ret); - return ret; - } else if (ret < 0) { - /* FW in unexpected state, continue to power down */ - dev_warn(sdev->dev, - "ctx_save ipc error %d, proceeding with suspend\n", - ret); + if (pm_ops && pm_ops->ctx_save) { + ret = pm_ops->ctx_save(sdev); + if (ret == -EBUSY || ret == -EAGAIN) { + /* + * runtime PM has logic to handle -EBUSY/-EAGAIN so + * pass these errors up + */ + dev_err(sdev->dev, "ctx_save IPC error during suspend: %d\n", ret); + return ret; + } else if (ret < 0) { + /* FW in unexpected state, continue to power down */ + dev_warn(sdev->dev, "ctx_save IPC error: %d, proceeding with suspend\n", + ret); + } }
suspend: @@ -278,9 +263,11 @@ static int sof_suspend(struct device *dev, bool runtime_suspend)
int snd_sof_dsp_power_down_notify(struct snd_sof_dev *sdev) { + const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm; + /* Notify DSP of upcoming power down */ - if (sof_ops(sdev)->remove) - return sof_send_pm_ctx_ipc(sdev, SOF_IPC_PM_CTX_SAVE); + if (sof_ops(sdev)->remove && pm_ops && pm_ops->ctx_save) + return pm_ops->ctx_save(sdev);
return 0; } diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 0b89c3e6ef21..3e883044dd0f 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -360,18 +360,28 @@ struct snd_sof_ipc_msg { bool ipc_complete; };
+/** + * struct sof_ipc_pm_ops - IPC-specific PM ops + * @ctx_save: Function pointer for context save + * @ctx_restore: Function pointer for context restore + */ +struct sof_ipc_pm_ops { + int (*ctx_save)(struct snd_sof_dev *sdev); + int (*ctx_restore)(struct snd_sof_dev *sdev); +}; + struct sof_ipc_tplg_ops;
/** * struct sof_ipc_ops - IPC-specific ops * @tplg: Pointer to IPC-specific topology ops + * @pm: Pointer to PM ops */ struct sof_ipc_ops { const struct sof_ipc_tplg_ops *tplg; + const struct sof_ipc_pm_ops *pm; };
-extern const struct sof_ipc_ops ipc3_ops; - /* SOF generic IPC data */ struct snd_sof_ipc { struct snd_sof_dev *sdev;
Introduce IPC-specific ops for kcontrol IO in struct ipc_tplg_ops.
Signed-off-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Péter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- sound/soc/sof/sof-audio.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+)
diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 19386184d8f3..d241dd84e708 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -45,6 +45,28 @@ struct snd_sof_dai_config_data { int dai_data; /* contains DAI-specific information */ };
+/** + * struct ipc_tplg_control_ops - IPC-specific ops for topology kcontrol IO + */ +struct ipc_tplg_control_ops { + bool (*volume_put)(struct snd_sof_control *scontrol, struct snd_ctl_elem_value *ucontrol); + int (*volume_get)(struct snd_sof_control *scontrol, struct snd_ctl_elem_value *ucontrol); + bool (*switch_put)(struct snd_sof_control *scontrol, struct snd_ctl_elem_value *ucontrol); + int (*switch_get)(struct snd_sof_control *scontrol, struct snd_ctl_elem_value *ucontrol); + bool (*enum_put)(struct snd_sof_control *scontrol, struct snd_ctl_elem_value *ucontrol); + int (*enum_get)(struct snd_sof_control *scontrol, struct snd_ctl_elem_value *ucontrol); + int (*bytes_put)(struct snd_sof_control *scontrol, struct snd_ctl_elem_value *ucontrol); + int (*bytes_get)(struct snd_sof_control *scontrol, struct snd_ctl_elem_value *ucontrol); + int (*bytes_ext_get)(struct snd_sof_control *scontrol, + const unsigned int __user *binary_data, unsigned int size); + int (*bytes_ext_volatile_get)(struct snd_sof_control *scontrol, + const unsigned int __user *binary_data, unsigned int size); + int (*bytes_ext_put)(struct snd_sof_control *scontrol, + const unsigned int __user *binary_data, unsigned int size); + /* update control data based on notification from the DSP */ + void (*update)(struct snd_sof_dev *sdev, void *ipc_control_message); +}; + /** * struct sof_ipc_tplg_widget_ops - IPC-specific ops for topology widgets * @ipc_setup: Function pointer for setting up widget IPC params @@ -67,6 +89,7 @@ struct sof_ipc_tplg_widget_ops { * @widget: Array of pointers to IPC-specific ops for widgets. This should always be of size * SND_SOF_DAPM_TYPE_COUNT i.e one per widget type. Unsupported widget types will be * initialized to 0. + * @control: Pointer to the IPC-specific ops for topology kcontrol IO * @route_setup: Function pointer for setting up pipeline connections * @token_list: List of all tokens supported by the IPC version. The size of the token_list * array should be SOF_TOKEN_COUNT. The unused elements in the array will be @@ -80,6 +103,7 @@ struct sof_ipc_tplg_widget_ops { */ struct sof_ipc_tplg_ops { const struct sof_ipc_tplg_widget_ops *widget; + const struct ipc_tplg_control_ops *control; int (*route_setup)(struct snd_sof_dev *sdev, struct snd_sof_route *sroute); const struct sof_token_info *token_list; int (*control_setup)(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol);
Define the topology control IPC ops for IPC3, implement the control_notify op and use it.
Signed-off-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Péter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- sound/soc/sof/Makefile | 2 +- sound/soc/sof/control.c | 144 ------------------------------- sound/soc/sof/ipc.c | 6 +- sound/soc/sof/ipc3-control.c | 156 ++++++++++++++++++++++++++++++++++ sound/soc/sof/ipc3-ops.h | 1 + sound/soc/sof/ipc3-topology.c | 1 + sound/soc/sof/sof-audio.h | 6 +- 7 files changed, 166 insertions(+), 150 deletions(-) create mode 100644 sound/soc/sof/ipc3-control.c
diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index 59482903a243..f6d68a3096d9 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -2,7 +2,7 @@
snd-sof-objs := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\ control.o trace.o iomem-utils.o sof-audio.o stream-ipc.o\ - ipc3-topology.o ipc3.o + ipc3-topology.o ipc3.o ipc3-control.o ifneq ($(CONFIG_SND_SOC_SOF_CLIENT),) snd-sof-objs += sof-client.o endif diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index 21ee0545945d..fb2311d880d3 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -526,147 +526,3 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol,
return 0; } - -static void snd_sof_update_control(struct snd_sof_control *scontrol, - struct sof_ipc_ctrl_data *cdata) -{ - struct snd_soc_component *scomp = scontrol->scomp; - struct sof_ipc_ctrl_data *local_cdata; - int i; - - local_cdata = scontrol->ipc_control_data; - - if (cdata->cmd == SOF_CTRL_CMD_BINARY) { - if (cdata->num_elems != local_cdata->data->size) { - dev_err(scomp->dev, - "error: cdata binary size mismatch %u - %u\n", - cdata->num_elems, local_cdata->data->size); - return; - } - - /* copy the new binary data */ - memcpy(local_cdata->data, cdata->data, cdata->num_elems); - } else if (cdata->num_elems != scontrol->num_channels) { - dev_err(scomp->dev, - "error: cdata channel count mismatch %u - %d\n", - cdata->num_elems, scontrol->num_channels); - } else { - /* copy the new values */ - for (i = 0; i < cdata->num_elems; i++) - local_cdata->chanv[i].value = cdata->chanv[i].value; - } -} - -void snd_sof_control_notify(struct snd_sof_dev *sdev, - struct sof_ipc_ctrl_data *cdata) -{ - struct snd_soc_dapm_widget *widget; - struct snd_sof_control *scontrol; - struct snd_sof_widget *swidget; - struct snd_kcontrol *kc = NULL; - struct soc_mixer_control *sm; - struct soc_bytes_ext *be; - size_t expected_size; - struct soc_enum *se; - bool found = false; - int i, type; - - if (cdata->type == SOF_CTRL_TYPE_VALUE_COMP_GET || - cdata->type == SOF_CTRL_TYPE_VALUE_COMP_SET) { - dev_err(sdev->dev, - "Component data is not supported in control notification\n"); - return; - } - - /* Find the swidget first */ - list_for_each_entry(swidget, &sdev->widget_list, list) { - if (swidget->comp_id == cdata->comp_id) { - found = true; - break; - } - } - - if (!found) - return; - - /* Translate SOF cmd to TPLG type */ - switch (cdata->cmd) { - case SOF_CTRL_CMD_VOLUME: - case SOF_CTRL_CMD_SWITCH: - type = SND_SOC_TPLG_TYPE_MIXER; - break; - case SOF_CTRL_CMD_BINARY: - type = SND_SOC_TPLG_TYPE_BYTES; - break; - case SOF_CTRL_CMD_ENUM: - type = SND_SOC_TPLG_TYPE_ENUM; - break; - default: - dev_err(sdev->dev, "error: unknown cmd %u\n", cdata->cmd); - return; - } - - widget = swidget->widget; - for (i = 0; i < widget->num_kcontrols; i++) { - /* skip non matching types or non matching indexes within type */ - if (widget->dobj.widget.kcontrol_type[i] == type && - widget->kcontrol_news[i].index == cdata->index) { - kc = widget->kcontrols[i]; - break; - } - } - - if (!kc) - return; - - switch (cdata->cmd) { - case SOF_CTRL_CMD_VOLUME: - case SOF_CTRL_CMD_SWITCH: - sm = (struct soc_mixer_control *)kc->private_value; - scontrol = sm->dobj.private; - break; - case SOF_CTRL_CMD_BINARY: - be = (struct soc_bytes_ext *)kc->private_value; - scontrol = be->dobj.private; - break; - case SOF_CTRL_CMD_ENUM: - se = (struct soc_enum *)kc->private_value; - scontrol = se->dobj.private; - break; - default: - return; - } - - expected_size = sizeof(struct sof_ipc_ctrl_data); - switch (cdata->type) { - case SOF_CTRL_TYPE_VALUE_CHAN_GET: - case SOF_CTRL_TYPE_VALUE_CHAN_SET: - expected_size += cdata->num_elems * - sizeof(struct sof_ipc_ctrl_value_chan); - break; - case SOF_CTRL_TYPE_DATA_GET: - case SOF_CTRL_TYPE_DATA_SET: - expected_size += cdata->num_elems + sizeof(struct sof_abi_hdr); - break; - default: - return; - } - - if (cdata->rhdr.hdr.size != expected_size) { - dev_err(sdev->dev, "error: component notification size mismatch\n"); - return; - } - - if (cdata->num_elems) - /* - * The message includes the updated value/data, update the - * control's local cache using the received notification - */ - snd_sof_update_control(scontrol, cdata); - else - /* Mark the scontrol that the value/data is changed in SOF */ - scontrol->comp_data_dirty = true; - - snd_ctl_notify_one(swidget->scomp->card->snd_card, - SNDRV_CTL_EVENT_MASK_VALUE, kc, 0); -} diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index 46a989be9a82..af0ae137842b 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -470,6 +470,7 @@ EXPORT_SYMBOL(snd_sof_ipc_reply);
static void ipc_comp_notification(struct snd_sof_dev *sdev, void *msg_buf) { + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; struct sof_ipc_cmd_hdr *hdr = msg_buf; u32 msg_type = hdr->cmd & SOF_CMD_TYPE_MASK;
@@ -482,7 +483,8 @@ static void ipc_comp_notification(struct snd_sof_dev *sdev, void *msg_buf) return; }
- snd_sof_control_notify(sdev, msg_buf); + if (tplg_ops->control->update) + tplg_ops->control->update(sdev, msg_buf); }
/* DSP firmware has sent host a message */ @@ -1031,7 +1033,7 @@ struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev) ipc->ops = &ipc3_ops;
/* check for mandatory ops */ - if (!ipc->ops->tplg || !ipc->ops->tplg->widget) { + if (!ipc->ops->tplg || !ipc->ops->tplg->widget || !ipc->ops->tplg->control) { dev_err(sdev->dev, "Invalid topology IPC ops\n"); return NULL; } diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c new file mode 100644 index 000000000000..d4086e805c18 --- /dev/null +++ b/sound/soc/sof/ipc3-control.c @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2021 Intel Corporation. All rights reserved. +// +// + +#include "sof-priv.h" +#include "sof-audio.h" +#include "ipc3-ops.h" + +static void snd_sof_update_control(struct snd_sof_control *scontrol, + struct sof_ipc_ctrl_data *cdata) +{ + struct snd_soc_component *scomp = scontrol->scomp; + struct sof_ipc_ctrl_data *local_cdata; + int i; + + local_cdata = scontrol->ipc_control_data; + + if (cdata->cmd == SOF_CTRL_CMD_BINARY) { + if (cdata->num_elems != local_cdata->data->size) { + dev_err(scomp->dev, "cdata binary size mismatch %u - %u\n", + cdata->num_elems, local_cdata->data->size); + return; + } + + /* copy the new binary data */ + memcpy(local_cdata->data, cdata->data, cdata->num_elems); + } else if (cdata->num_elems != scontrol->num_channels) { + dev_err(scomp->dev, "cdata channel count mismatch %u - %d\n", + cdata->num_elems, scontrol->num_channels); + } else { + /* copy the new values */ + for (i = 0; i < cdata->num_elems; i++) + local_cdata->chanv[i].value = cdata->chanv[i].value; + } +} + +static void sof_ipc3_control_update(struct snd_sof_dev *sdev, void *ipc_control_message) +{ + struct sof_ipc_ctrl_data *cdata = ipc_control_message; + struct snd_soc_dapm_widget *widget; + struct snd_sof_control *scontrol; + struct snd_sof_widget *swidget; + struct snd_kcontrol *kc = NULL; + struct soc_mixer_control *sm; + struct soc_bytes_ext *be; + size_t expected_size; + struct soc_enum *se; + bool found = false; + int i, type; + + if (cdata->type == SOF_CTRL_TYPE_VALUE_COMP_GET || + cdata->type == SOF_CTRL_TYPE_VALUE_COMP_SET) { + dev_err(sdev->dev, "Component data is not supported in control notification\n"); + return; + } + + /* Find the swidget first */ + list_for_each_entry(swidget, &sdev->widget_list, list) { + if (swidget->comp_id == cdata->comp_id) { + found = true; + break; + } + } + + if (!found) + return; + + /* Translate SOF cmd to TPLG type */ + switch (cdata->cmd) { + case SOF_CTRL_CMD_VOLUME: + case SOF_CTRL_CMD_SWITCH: + type = SND_SOC_TPLG_TYPE_MIXER; + break; + case SOF_CTRL_CMD_BINARY: + type = SND_SOC_TPLG_TYPE_BYTES; + break; + case SOF_CTRL_CMD_ENUM: + type = SND_SOC_TPLG_TYPE_ENUM; + break; + default: + dev_err(sdev->dev, "Unknown cmd %u in %s\n", cdata->cmd, __func__); + return; + } + + widget = swidget->widget; + for (i = 0; i < widget->num_kcontrols; i++) { + /* skip non matching types or non matching indexes within type */ + if (widget->dobj.widget.kcontrol_type[i] == type && + widget->kcontrol_news[i].index == cdata->index) { + kc = widget->kcontrols[i]; + break; + } + } + + if (!kc) + return; + + switch (cdata->cmd) { + case SOF_CTRL_CMD_VOLUME: + case SOF_CTRL_CMD_SWITCH: + sm = (struct soc_mixer_control *)kc->private_value; + scontrol = sm->dobj.private; + break; + case SOF_CTRL_CMD_BINARY: + be = (struct soc_bytes_ext *)kc->private_value; + scontrol = be->dobj.private; + break; + case SOF_CTRL_CMD_ENUM: + se = (struct soc_enum *)kc->private_value; + scontrol = se->dobj.private; + break; + default: + return; + } + + expected_size = sizeof(struct sof_ipc_ctrl_data); + switch (cdata->type) { + case SOF_CTRL_TYPE_VALUE_CHAN_GET: + case SOF_CTRL_TYPE_VALUE_CHAN_SET: + expected_size += cdata->num_elems * + sizeof(struct sof_ipc_ctrl_value_chan); + break; + case SOF_CTRL_TYPE_DATA_GET: + case SOF_CTRL_TYPE_DATA_SET: + expected_size += cdata->num_elems + sizeof(struct sof_abi_hdr); + break; + default: + return; + } + + if (cdata->rhdr.hdr.size != expected_size) { + dev_err(sdev->dev, "Component notification size mismatch\n"); + return; + } + + if (cdata->num_elems) + /* + * The message includes the updated value/data, update the + * control's local cache using the received notification + */ + snd_sof_update_control(scontrol, cdata); + else + /* Mark the scontrol that the value/data is changed in SOF */ + scontrol->comp_data_dirty = true; + + snd_ctl_notify_one(swidget->scomp->card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, kc, 0); +} + +const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops = { + .update = sof_ipc3_control_update, +}; diff --git a/sound/soc/sof/ipc3-ops.h b/sound/soc/sof/ipc3-ops.h index 5d8cab92c1a4..f3d6010d0b77 100644 --- a/sound/soc/sof/ipc3-ops.h +++ b/sound/soc/sof/ipc3-ops.h @@ -15,5 +15,6 @@
extern const struct sof_ipc_tplg_ops ipc3_tplg_ops; extern const struct sof_ipc_ops ipc3_ops; +extern const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops;
#endif diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index bf0cf38f4524..55be97ee816b 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -2155,6 +2155,7 @@ static const struct sof_ipc_tplg_widget_ops tplg_ipc3_widget_ops[SND_SOC_DAPM_TY
const struct sof_ipc_tplg_ops ipc3_tplg_ops = { .widget = tplg_ipc3_widget_ops, + .control = &tplg_ipc3_control_ops, .route_setup = sof_ipc3_route_setup, .control_setup = sof_ipc3_control_setup, .control_free = sof_ipc3_control_free, diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index d241dd84e708..bcd38c882078 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -46,9 +46,9 @@ struct snd_sof_dai_config_data { };
/** - * struct ipc_tplg_control_ops - IPC-specific ops for topology kcontrol IO + * struct sof_ipc_tplg_control_ops - IPC-specific ops for topology kcontrol IO */ -struct ipc_tplg_control_ops { +struct sof_ipc_tplg_control_ops { bool (*volume_put)(struct snd_sof_control *scontrol, struct snd_ctl_elem_value *ucontrol); int (*volume_get)(struct snd_sof_control *scontrol, struct snd_ctl_elem_value *ucontrol); bool (*switch_put)(struct snd_sof_control *scontrol, struct snd_ctl_elem_value *ucontrol); @@ -103,7 +103,7 @@ struct sof_ipc_tplg_widget_ops { */ struct sof_ipc_tplg_ops { const struct sof_ipc_tplg_widget_ops *widget; - const struct ipc_tplg_control_ops *control; + const struct sof_ipc_tplg_control_ops *control; int (*route_setup)(struct snd_sof_dev *sdev, struct snd_sof_route *sroute); const struct sof_token_info *token_list; int (*control_setup)(struct snd_sof_dev *sdev, struct snd_sof_control *scontrol);
Define and set the volume_get/put control IPC ops for IPC3.
Signed-off-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Péter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- sound/soc/sof/control.c | 64 +++++----------------- sound/soc/sof/ipc3-control.c | 100 +++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+), 50 deletions(-)
diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index fb2311d880d3..9eb54b7024b3 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -45,26 +45,6 @@ static void update_mute_led(struct snd_sof_control *scontrol, #endif }
-static inline u32 mixer_to_ipc(unsigned int value, u32 *volume_map, int size) -{ - if (value >= size) - return volume_map[size - 1]; - - return volume_map[value]; -} - -static inline u32 ipc_to_mixer(u32 value, u32 *volume_map, int size) -{ - int i; - - for (i = 0; i < size; i++) { - if (volume_map[i] >= value) - return i; - } - - return i - 1; -} - static void snd_sof_refresh_control(struct snd_sof_control *scontrol) { struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; @@ -85,7 +65,8 @@ static void snd_sof_refresh_control(struct snd_sof_control *scontrol) scontrol->comp_data_dirty = false; ret = snd_sof_ipc_set_get_comp_data(scontrol, false); if (ret < 0) { - dev_err(scomp->dev, "error: failed to get control data: %d\n", ret); + dev_err(scomp->dev, "Failed to get control data: %d\n", ret); + /* Set the flag to re-try next time to get the data */ scontrol->comp_data_dirty = true; } @@ -94,19 +75,14 @@ static void snd_sof_refresh_control(struct snd_sof_control *scontrol) int snd_sof_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct soc_mixer_control *sm = - (struct soc_mixer_control *)kcontrol->private_value; + struct soc_mixer_control *sm = (struct soc_mixer_control *)kcontrol->private_value; struct snd_sof_control *scontrol = sm->dobj.private; - struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; - unsigned int i, channels = scontrol->num_channels; + struct snd_soc_component *scomp = scontrol->scomp; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
- snd_sof_refresh_control(scontrol); - - /* read back each channel */ - for (i = 0; i < channels; i++) - ucontrol->value.integer.value[i] = - ipc_to_mixer(cdata->chanv[i].value, - scontrol->volume_table, sm->max + 1); + if (tplg_ops->control->volume_get) + return tplg_ops->control->volume_get(scontrol, ucontrol);
return 0; } @@ -114,28 +90,16 @@ int snd_sof_volume_get(struct snd_kcontrol *kcontrol, int snd_sof_volume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct soc_mixer_control *sm = - (struct soc_mixer_control *)kcontrol->private_value; + struct soc_mixer_control *sm = (struct soc_mixer_control *)kcontrol->private_value; struct snd_sof_control *scontrol = sm->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; - unsigned int i, channels = scontrol->num_channels; - bool change = false; - u32 value; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
- /* update each channel */ - for (i = 0; i < channels; i++) { - value = mixer_to_ipc(ucontrol->value.integer.value[i], - scontrol->volume_table, sm->max + 1); - change = change || (value != cdata->chanv[i].value); - cdata->chanv[i].channel = i; - cdata->chanv[i].value = value; - } + if (tplg_ops->control->volume_put) + return tplg_ops->control->volume_put(scontrol, ucontrol);
- /* notify DSP of mixer updates */ - if (pm_runtime_active(scomp->dev)) - snd_sof_ipc_set_get_comp_data(scontrol, true); - return change; + return false; }
int snd_sof_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c index d4086e805c18..12aeb3ae4e52 100644 --- a/sound/soc/sof/ipc3-control.c +++ b/sound/soc/sof/ipc3-control.c @@ -11,6 +11,104 @@ #include "sof-audio.h" #include "ipc3-ops.h"
+static inline u32 mixer_to_ipc(unsigned int value, u32 *volume_map, int size) +{ + if (value >= size) + return volume_map[size - 1]; + + return volume_map[value]; +} + +static inline u32 ipc_to_mixer(u32 value, u32 *volume_map, int size) +{ + int i; + + for (i = 0; i < size; i++) { + if (volume_map[i] >= value) + return i; + } + + return i - 1; +} + +static void snd_sof_refresh_control(struct snd_sof_control *scontrol) +{ + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; + struct snd_soc_component *scomp = scontrol->scomp; + int ret; + + if (!scontrol->comp_data_dirty) + return; + + if (!pm_runtime_active(scomp->dev)) + return; + + /* set the ABI header values */ + cdata->data->magic = SOF_ABI_MAGIC; + cdata->data->abi = SOF_ABI_VERSION; + + /* refresh the component data from DSP */ + scontrol->comp_data_dirty = false; + ret = snd_sof_ipc_set_get_comp_data(scontrol, false); + if (ret < 0) { + dev_err(scomp->dev, "Failed to get control data: %d\n", ret); + + /* Set the flag to re-try next time to get the data */ + scontrol->comp_data_dirty = true; + } +} + +static int sof_ipc3_volume_get(struct snd_sof_control *scontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; + unsigned int channels = scontrol->num_channels; + unsigned int i; + + snd_sof_refresh_control(scontrol); + + /* read back each channel */ + for (i = 0; i < channels; i++) + ucontrol->value.integer.value[i] = ipc_to_mixer(cdata->chanv[i].value, + scontrol->volume_table, + scontrol->max + 1); + + return 0; +} + +static bool sof_ipc3_volume_put(struct snd_sof_control *scontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; + struct snd_soc_component *scomp = scontrol->scomp; + unsigned int channels = scontrol->num_channels; + unsigned int i; + bool change = false; + + /* update each channel */ + for (i = 0; i < channels; i++) { + u32 value = mixer_to_ipc(ucontrol->value.integer.value[i], + scontrol->volume_table, scontrol->max + 1); + + change = change || (value != cdata->chanv[i].value); + cdata->chanv[i].channel = i; + cdata->chanv[i].value = value; + } + + /* notify DSP of mixer updates */ + if (pm_runtime_active(scomp->dev)) { + int ret = snd_sof_ipc_set_get_comp_data(scontrol, true); + + if (ret < 0) { + dev_err(scomp->dev, "Failed to set mixer updates for %s\n", + scontrol->name); + return false; + } + } + + return change; +} + static void snd_sof_update_control(struct snd_sof_control *scontrol, struct sof_ipc_ctrl_data *cdata) { @@ -152,5 +250,7 @@ static void sof_ipc3_control_update(struct snd_sof_dev *sdev, void *ipc_control_ }
const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops = { + .volume_put = sof_ipc3_volume_put, + .volume_get = sof_ipc3_volume_get, .update = sof_ipc3_control_update, };
Add the switch_get/put control IPC ops for IPC3.
Signed-off-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Péter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- sound/soc/sof/control.c | 39 +++++++++------------------- sound/soc/sof/ipc3-control.c | 50 ++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 27 deletions(-)
diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index 9eb54b7024b3..f9f8bd37b480 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -127,17 +127,14 @@ int snd_sof_volume_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info int snd_sof_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct soc_mixer_control *sm = - (struct soc_mixer_control *)kcontrol->private_value; + struct soc_mixer_control *sm = (struct soc_mixer_control *)kcontrol->private_value; struct snd_sof_control *scontrol = sm->dobj.private; - struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; - unsigned int i, channels = scontrol->num_channels; - - snd_sof_refresh_control(scontrol); + struct snd_soc_component *scomp = scontrol->scomp; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
- /* read back each channel */ - for (i = 0; i < channels; i++) - ucontrol->value.integer.value[i] = cdata->chanv[i].value; + if (tplg_ops->control->switch_get) + return tplg_ops->control->switch_get(scontrol, ucontrol);
return 0; } @@ -145,31 +142,19 @@ int snd_sof_switch_get(struct snd_kcontrol *kcontrol, int snd_sof_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct soc_mixer_control *sm = - (struct soc_mixer_control *)kcontrol->private_value; + struct soc_mixer_control *sm = (struct soc_mixer_control *)kcontrol->private_value; struct snd_sof_control *scontrol = sm->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; - unsigned int i, channels = scontrol->num_channels; - bool change = false; - u32 value; - - /* update each channel */ - for (i = 0; i < channels; i++) { - value = ucontrol->value.integer.value[i]; - change = change || (value != cdata->chanv[i].value); - cdata->chanv[i].channel = i; - cdata->chanv[i].value = value; - } + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
if (scontrol->led_ctl.use_led) update_mute_led(scontrol, kcontrol, ucontrol);
- /* notify DSP of mixer updates */ - if (pm_runtime_active(scomp->dev)) - snd_sof_ipc_set_get_comp_data(scontrol, true); + if (tplg_ops->control->switch_put) + return tplg_ops->control->switch_put(scontrol, ucontrol);
- return change; + return false; }
int snd_sof_enum_get(struct snd_kcontrol *kcontrol, diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c index 12aeb3ae4e52..ba9cde011b5a 100644 --- a/sound/soc/sof/ipc3-control.c +++ b/sound/soc/sof/ipc3-control.c @@ -109,6 +109,54 @@ static bool sof_ipc3_volume_put(struct snd_sof_control *scontrol, return change; }
+static int sof_ipc3_switch_get(struct snd_sof_control *scontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; + unsigned int channels = scontrol->num_channels; + unsigned int i; + + snd_sof_refresh_control(scontrol); + + /* read back each channel */ + for (i = 0; i < channels; i++) + ucontrol->value.integer.value[i] = cdata->chanv[i].value; + + return 0; +} + +static bool sof_ipc3_switch_put(struct snd_sof_control *scontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; + struct snd_soc_component *scomp = scontrol->scomp; + unsigned int channels = scontrol->num_channels; + unsigned int i; + bool change = false; + u32 value; + + /* update each channel */ + for (i = 0; i < channels; i++) { + value = ucontrol->value.integer.value[i]; + change = change || (value != cdata->chanv[i].value); + cdata->chanv[i].channel = i; + cdata->chanv[i].value = value; + } + + /* notify DSP of mixer updates */ + if (pm_runtime_active(scomp->dev)) { + int ret = snd_sof_ipc_set_get_comp_data(scontrol, true); + + if (ret < 0) { + dev_err(scomp->dev, "Failed to set mixer updates for %s\n", + scontrol->name); + return false; + } + } + + return change; +} + static void snd_sof_update_control(struct snd_sof_control *scontrol, struct sof_ipc_ctrl_data *cdata) { @@ -252,5 +300,7 @@ static void sof_ipc3_control_update(struct snd_sof_dev *sdev, void *ipc_control_ const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops = { .volume_put = sof_ipc3_volume_put, .volume_get = sof_ipc3_volume_get, + .switch_put = sof_ipc3_switch_put, + .switch_get = sof_ipc3_switch_get, .update = sof_ipc3_control_update, };
Define and set the enum_get/put control IPC ops for IPC3.
Signed-off-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Péter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- sound/soc/sof/control.c | 39 +++++++++------------------- sound/soc/sof/ipc3-control.c | 50 ++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 27 deletions(-)
diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index f9f8bd37b480..499d426c5d38 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -160,17 +160,14 @@ int snd_sof_switch_put(struct snd_kcontrol *kcontrol, int snd_sof_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct soc_enum *se = - (struct soc_enum *)kcontrol->private_value; + struct soc_enum *se = (struct soc_enum *)kcontrol->private_value; struct snd_sof_control *scontrol = se->dobj.private; - struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; - unsigned int i, channels = scontrol->num_channels; - - snd_sof_refresh_control(scontrol); + struct snd_soc_component *scomp = scontrol->scomp; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
- /* read back each channel */ - for (i = 0; i < channels; i++) - ucontrol->value.enumerated.item[i] = cdata->chanv[i].value; + if (tplg_ops->control->enum_get) + return tplg_ops->control->enum_get(scontrol, ucontrol);
return 0; } @@ -178,28 +175,16 @@ int snd_sof_enum_get(struct snd_kcontrol *kcontrol, int snd_sof_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct soc_enum *se = - (struct soc_enum *)kcontrol->private_value; + struct soc_enum *se = (struct soc_enum *)kcontrol->private_value; struct snd_sof_control *scontrol = se->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; - unsigned int i, channels = scontrol->num_channels; - bool change = false; - u32 value; - - /* update each channel */ - for (i = 0; i < channels; i++) { - value = ucontrol->value.enumerated.item[i]; - change = change || (value != cdata->chanv[i].value); - cdata->chanv[i].channel = i; - cdata->chanv[i].value = value; - } + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
- /* notify DSP of enum updates */ - if (pm_runtime_active(scomp->dev)) - snd_sof_ipc_set_get_comp_data(scontrol, true); + if (tplg_ops->control->enum_put) + return tplg_ops->control->enum_put(scontrol, ucontrol);
- return change; + return false; }
int snd_sof_bytes_get(struct snd_kcontrol *kcontrol, diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c index ba9cde011b5a..03948f8f7eb0 100644 --- a/sound/soc/sof/ipc3-control.c +++ b/sound/soc/sof/ipc3-control.c @@ -157,6 +157,54 @@ static bool sof_ipc3_switch_put(struct snd_sof_control *scontrol, return change; }
+static int sof_ipc3_enum_get(struct snd_sof_control *scontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; + unsigned int channels = scontrol->num_channels; + unsigned int i; + + snd_sof_refresh_control(scontrol); + + /* read back each channel */ + for (i = 0; i < channels; i++) + ucontrol->value.enumerated.item[i] = cdata->chanv[i].value; + + return 0; +} + +static bool sof_ipc3_enum_put(struct snd_sof_control *scontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; + struct snd_soc_component *scomp = scontrol->scomp; + unsigned int channels = scontrol->num_channels; + unsigned int i; + bool change = false; + u32 value; + + /* update each channel */ + for (i = 0; i < channels; i++) { + value = ucontrol->value.enumerated.item[i]; + change = change || (value != cdata->chanv[i].value); + cdata->chanv[i].channel = i; + cdata->chanv[i].value = value; + } + + /* notify DSP of enum updates */ + if (pm_runtime_active(scomp->dev)) { + int ret = snd_sof_ipc_set_get_comp_data(scontrol, true); + + if (ret < 0) { + dev_err(scomp->dev, "Failed to set enum updates for %s\n", + scontrol->name); + return false; + } + } + + return change; +} + static void snd_sof_update_control(struct snd_sof_control *scontrol, struct sof_ipc_ctrl_data *cdata) { @@ -302,5 +350,7 @@ const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops = { .volume_get = sof_ipc3_volume_get, .switch_put = sof_ipc3_switch_put, .switch_get = sof_ipc3_switch_get, + .enum_put = sof_ipc3_enum_put, + .enum_get = sof_ipc3_enum_get, .update = sof_ipc3_control_update, };
Define and set the bytes_get/put IPC control ops for IPC3.
Signed-off-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Péter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- sound/soc/sof/control.c | 64 ++++++---------------------------- sound/soc/sof/ipc3-control.c | 67 ++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 54 deletions(-)
diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index 499d426c5d38..2a4997e1cd1e 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -190,35 +190,14 @@ int snd_sof_enum_put(struct snd_kcontrol *kcontrol, int snd_sof_bytes_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct soc_bytes_ext *be = - (struct soc_bytes_ext *)kcontrol->private_value; + struct soc_bytes_ext *be = (struct soc_bytes_ext *)kcontrol->private_value; struct snd_sof_control *scontrol = be->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; - struct sof_abi_hdr *data = cdata->data; - size_t size; - - snd_sof_refresh_control(scontrol); - - if (be->max > sizeof(ucontrol->value.bytes.data)) { - dev_err_ratelimited(scomp->dev, - "error: data max %d exceeds ucontrol data array size\n", - be->max); - return -EINVAL; - } - - /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */ - if (data->size > be->max - sizeof(*data)) { - dev_err_ratelimited(scomp->dev, - "error: %u bytes of control data is invalid, max is %zu\n", - data->size, be->max - sizeof(*data)); - return -EINVAL; - } - - size = data->size + sizeof(*data); + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
- /* copy back to kcontrol */ - memcpy(ucontrol->value.bytes.data, data, size); + if (tplg_ops->control->bytes_get) + return tplg_ops->control->bytes_get(scontrol, ucontrol);
return 0; } @@ -226,37 +205,14 @@ int snd_sof_bytes_get(struct snd_kcontrol *kcontrol, int snd_sof_bytes_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct soc_bytes_ext *be = - (struct soc_bytes_ext *)kcontrol->private_value; + struct soc_bytes_ext *be = (struct soc_bytes_ext *)kcontrol->private_value; struct snd_sof_control *scontrol = be->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; - struct sof_abi_hdr *data = cdata->data; - size_t size; - - if (be->max > sizeof(ucontrol->value.bytes.data)) { - dev_err_ratelimited(scomp->dev, - "error: data max %d exceeds ucontrol data array size\n", - be->max); - return -EINVAL; - } - - /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */ - if (data->size > be->max - sizeof(*data)) { - dev_err_ratelimited(scomp->dev, - "error: data size too big %u bytes max is %zu\n", - data->size, be->max - sizeof(*data)); - return -EINVAL; - } - - size = data->size + sizeof(*data); - - /* copy from kcontrol */ - memcpy(data, ucontrol->value.bytes.data, size); + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
- /* notify DSP of byte control updates */ - if (pm_runtime_active(scomp->dev)) - snd_sof_ipc_set_get_comp_data(scontrol, true); + if (tplg_ops->control->bytes_put) + return tplg_ops->control->bytes_put(scontrol, ucontrol);
return 0; } diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c index 03948f8f7eb0..df8e4df9663d 100644 --- a/sound/soc/sof/ipc3-control.c +++ b/sound/soc/sof/ipc3-control.c @@ -205,6 +205,71 @@ static bool sof_ipc3_enum_put(struct snd_sof_control *scontrol, return change; }
+static int sof_ipc3_bytes_get(struct snd_sof_control *scontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; + struct snd_soc_component *scomp = scontrol->scomp; + struct sof_abi_hdr *data = cdata->data; + size_t size; + + snd_sof_refresh_control(scontrol); + + if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) { + dev_err_ratelimited(scomp->dev, "data max %zu exceeds ucontrol data array size\n", + scontrol->max_size); + return -EINVAL; + } + + /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */ + if (data->size > scontrol->max_size - sizeof(*data)) { + dev_err_ratelimited(scomp->dev, + "%u bytes of control data is invalid, max is %zu\n", + data->size, scontrol->max_size - sizeof(*data)); + return -EINVAL; + } + + size = data->size + sizeof(*data); + + /* copy back to kcontrol */ + memcpy(ucontrol->value.bytes.data, data, size); + + return 0; +} + +static int sof_ipc3_bytes_put(struct snd_sof_control *scontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; + struct snd_soc_component *scomp = scontrol->scomp; + struct sof_abi_hdr *data = cdata->data; + size_t size; + + if (scontrol->max_size > sizeof(ucontrol->value.bytes.data)) { + dev_err_ratelimited(scomp->dev, "data max %zu exceeds ucontrol data array size\n", + scontrol->max_size); + return -EINVAL; + } + + /* scontrol->max_size has been verified to be >= sizeof(struct sof_abi_hdr) */ + if (data->size > scontrol->max_size - sizeof(*data)) { + dev_err_ratelimited(scomp->dev, "data size too big %u bytes max is %zu\n", + data->size, scontrol->max_size - sizeof(*data)); + return -EINVAL; + } + + size = data->size + sizeof(*data); + + /* copy from kcontrol */ + memcpy(data, ucontrol->value.bytes.data, size); + + /* notify DSP of byte control updates */ + if (pm_runtime_active(scomp->dev)) + return snd_sof_ipc_set_get_comp_data(scontrol, true); + + return 0; +} + static void snd_sof_update_control(struct snd_sof_control *scontrol, struct sof_ipc_ctrl_data *cdata) { @@ -352,5 +417,7 @@ const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops = { .switch_get = sof_ipc3_switch_get, .enum_put = sof_ipc3_enum_put, .enum_get = sof_ipc3_enum_get, + .bytes_put = sof_ipc3_bytes_put, + .bytes_get = sof_ipc3_bytes_get, .update = sof_ipc3_control_update, };
Define and set the get/put/volatile_get control IPC ops for byte controls for IPC3.
Signed-off-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Péter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- sound/soc/sof/control.c | 197 +++-------------------------------- sound/soc/sof/ipc3-control.c | 171 ++++++++++++++++++++++++++++++ 2 files changed, 188 insertions(+), 180 deletions(-)
diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c index 2a4997e1cd1e..de1778c4002b 100644 --- a/sound/soc/sof/control.c +++ b/sound/soc/sof/control.c @@ -45,33 +45,6 @@ static void update_mute_led(struct snd_sof_control *scontrol, #endif }
-static void snd_sof_refresh_control(struct snd_sof_control *scontrol) -{ - struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; - struct snd_soc_component *scomp = scontrol->scomp; - int ret; - - if (!scontrol->comp_data_dirty) - return; - - if (!pm_runtime_active(scomp->dev)) - return; - - /* set the ABI header values */ - cdata->data->magic = SOF_ABI_MAGIC; - cdata->data->abi = SOF_ABI_VERSION; - - /* refresh the component data from DSP */ - scontrol->comp_data_dirty = false; - ret = snd_sof_ipc_set_get_comp_data(scontrol, false); - if (ret < 0) { - dev_err(scomp->dev, "Failed to get control data: %d\n", ret); - - /* Set the flag to re-try next time to get the data */ - scontrol->comp_data_dirty = true; - } -} - int snd_sof_volume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -221,74 +194,18 @@ int snd_sof_bytes_ext_put(struct snd_kcontrol *kcontrol, const unsigned int __user *binary_data, unsigned int size) { - struct soc_bytes_ext *be = - (struct soc_bytes_ext *)kcontrol->private_value; + struct soc_bytes_ext *be = (struct soc_bytes_ext *)kcontrol->private_value; struct snd_sof_control *scontrol = be->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; - struct snd_ctl_tlv header; - const struct snd_ctl_tlv __user *tlvd = - (const struct snd_ctl_tlv __user *)binary_data; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
/* make sure we have at least a header */ if (size < sizeof(struct snd_ctl_tlv)) return -EINVAL;
- /* - * The beginning of bytes data contains a header from where - * the length (as bytes) is needed to know the correct copy - * length of data from tlvd->tlv. - */ - if (copy_from_user(&header, tlvd, sizeof(struct snd_ctl_tlv))) - return -EFAULT; - - /* make sure TLV info is consistent */ - if (header.length + sizeof(struct snd_ctl_tlv) > size) { - dev_err_ratelimited(scomp->dev, "error: inconsistent TLV, data %d + header %zu > %d\n", - header.length, sizeof(struct snd_ctl_tlv), size); - return -EINVAL; - } - - /* be->max is coming from topology */ - if (header.length > be->max) { - dev_err_ratelimited(scomp->dev, "error: Bytes data size %d exceeds max %d.\n", - header.length, be->max); - return -EINVAL; - } - - /* Check that header id matches the command */ - if (header.numid != cdata->cmd) { - dev_err_ratelimited(scomp->dev, - "error: incorrect numid %d\n", - header.numid); - return -EINVAL; - } - - if (copy_from_user(cdata->data, tlvd->tlv, header.length)) - return -EFAULT; - - if (cdata->data->magic != SOF_ABI_MAGIC) { - dev_err_ratelimited(scomp->dev, - "error: Wrong ABI magic 0x%08x.\n", - cdata->data->magic); - return -EINVAL; - } - - if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, cdata->data->abi)) { - dev_err_ratelimited(scomp->dev, "error: Incompatible ABI version 0x%08x.\n", - cdata->data->abi); - return -EINVAL; - } - - /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */ - if (cdata->data->size > be->max - sizeof(struct sof_abi_hdr)) { - dev_err_ratelimited(scomp->dev, "error: Mismatch in ABI data size (truncated?).\n"); - return -EINVAL; - } - - /* notify DSP of byte control updates */ - if (pm_runtime_active(scomp->dev)) - snd_sof_ipc_set_get_comp_data(scontrol, true); + if (tplg_ops->control->bytes_ext_put) + return tplg_ops->control->bytes_ext_put(scontrol, binary_data, size);
return 0; } @@ -299,67 +216,24 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _ struct soc_bytes_ext *be = (struct soc_bytes_ext *)kcontrol->private_value; struct snd_sof_control *scontrol = be->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; - struct snd_ctl_tlv header; - struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data; - size_t data_size; - int ret; - int err; - - /* - * Decrement the limit by ext bytes header size to - * ensure the user space buffer is not exceeded. - */ - if (size < sizeof(struct snd_ctl_tlv)) - return -ENOSPC; - size -= sizeof(struct snd_ctl_tlv); + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; + int ret, err;
ret = pm_runtime_get_sync(scomp->dev); if (ret < 0 && ret != -EACCES) { - dev_err_ratelimited(scomp->dev, "error: bytes_ext get failed to resume %d\n", ret); + dev_err_ratelimited(scomp->dev, "%s: failed to resume %d\n", __func__, ret); pm_runtime_put_noidle(scomp->dev); return ret; }
- /* set the ABI header values */ - cdata->data->magic = SOF_ABI_MAGIC; - cdata->data->abi = SOF_ABI_VERSION; - /* get all the component data from DSP */ - ret = snd_sof_ipc_set_get_comp_data(scontrol, false); - if (ret < 0) - goto out; - - /* check data size doesn't exceed max coming from topology */ - if (cdata->data->size > be->max - sizeof(struct sof_abi_hdr)) { - dev_err_ratelimited(scomp->dev, "error: user data size %d exceeds max size %zu.\n", - cdata->data->size, - be->max - sizeof(struct sof_abi_hdr)); - ret = -EINVAL; - goto out; - } - - data_size = cdata->data->size + sizeof(struct sof_abi_hdr); + if (tplg_ops->control->bytes_ext_volatile_get) + ret = tplg_ops->control->bytes_ext_volatile_get(scontrol, binary_data, size);
- /* make sure we don't exceed size provided by user space for data */ - if (data_size > size) { - ret = -ENOSPC; - goto out; - } - - header.numid = cdata->cmd; - header.length = data_size; - if (copy_to_user(tlvd, &header, sizeof(struct snd_ctl_tlv))) { - ret = -EFAULT; - goto out; - } - - if (copy_to_user(tlvd->tlv, cdata->data, data_size)) - ret = -EFAULT; -out: pm_runtime_mark_last_busy(scomp->dev); err = pm_runtime_put_autosuspend(scomp->dev); if (err < 0) - dev_err_ratelimited(scomp->dev, "error: bytes_ext get failed to idle %d\n", err); + dev_err_ratelimited(scomp->dev, "%s: failed to idle %d\n", __func__, err);
return ret; } @@ -368,51 +242,14 @@ int snd_sof_bytes_ext_get(struct snd_kcontrol *kcontrol, unsigned int __user *binary_data, unsigned int size) { - struct soc_bytes_ext *be = - (struct soc_bytes_ext *)kcontrol->private_value; + struct soc_bytes_ext *be = (struct soc_bytes_ext *)kcontrol->private_value; struct snd_sof_control *scontrol = be->dobj.private; struct snd_soc_component *scomp = scontrol->scomp; - struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; - struct snd_ctl_tlv header; - struct snd_ctl_tlv __user *tlvd = - (struct snd_ctl_tlv __user *)binary_data; - size_t data_size; - - snd_sof_refresh_control(scontrol); - - /* - * Decrement the limit by ext bytes header size to - * ensure the user space buffer is not exceeded. - */ - if (size < sizeof(struct snd_ctl_tlv)) - return -ENOSPC; - size -= sizeof(struct snd_ctl_tlv); - - /* set the ABI header values */ - cdata->data->magic = SOF_ABI_MAGIC; - cdata->data->abi = SOF_ABI_VERSION; - - /* check data size doesn't exceed max coming from topology */ - if (cdata->data->size > be->max - sizeof(struct sof_abi_hdr)) { - dev_err_ratelimited(scomp->dev, "error: user data size %d exceeds max size %zu.\n", - cdata->data->size, - be->max - sizeof(struct sof_abi_hdr)); - return -EINVAL; - } - - data_size = cdata->data->size + sizeof(struct sof_abi_hdr); - - /* make sure we don't exceed size provided by user space for data */ - if (data_size > size) - return -ENOSPC; - - header.numid = cdata->cmd; - header.length = data_size; - if (copy_to_user(tlvd, &header, sizeof(struct snd_ctl_tlv))) - return -EFAULT; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
- if (copy_to_user(tlvd->tlv, cdata->data, data_size)) - return -EFAULT; + if (tplg_ops->control->bytes_ext_get) + return tplg_ops->control->bytes_ext_get(scontrol, binary_data, size);
return 0; } diff --git a/sound/soc/sof/ipc3-control.c b/sound/soc/sof/ipc3-control.c index df8e4df9663d..cdd5ad860a94 100644 --- a/sound/soc/sof/ipc3-control.c +++ b/sound/soc/sof/ipc3-control.c @@ -270,6 +270,174 @@ static int sof_ipc3_bytes_put(struct snd_sof_control *scontrol, return 0; }
+static int sof_ipc3_bytes_ext_get(struct snd_sof_control *scontrol, + const unsigned int __user *binary_data, unsigned int size) +{ + struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data; + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; + struct snd_soc_component *scomp = scontrol->scomp; + struct snd_ctl_tlv header; + size_t data_size; + + snd_sof_refresh_control(scontrol); + + /* + * Decrement the limit by ext bytes header size to + * ensure the user space buffer is not exceeded. + */ + if (size < sizeof(struct snd_ctl_tlv)) + return -ENOSPC; + + size -= sizeof(struct snd_ctl_tlv); + + /* set the ABI header values */ + cdata->data->magic = SOF_ABI_MAGIC; + cdata->data->abi = SOF_ABI_VERSION; + + /* check data size doesn't exceed max coming from topology */ + if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) { + dev_err_ratelimited(scomp->dev, "User data size %d exceeds max size %zu\n", + cdata->data->size, + scontrol->max_size - sizeof(struct sof_abi_hdr)); + return -EINVAL; + } + + data_size = cdata->data->size + sizeof(struct sof_abi_hdr); + + /* make sure we don't exceed size provided by user space for data */ + if (data_size > size) + return -ENOSPC; + + header.numid = cdata->cmd; + header.length = data_size; + if (copy_to_user(tlvd, &header, sizeof(struct snd_ctl_tlv))) + return -EFAULT; + + if (copy_to_user(tlvd->tlv, cdata->data, data_size)) + return -EFAULT; + + return 0; +} + +static int sof_ipc3_bytes_ext_put(struct snd_sof_control *scontrol, + const unsigned int __user *binary_data, + unsigned int size) +{ + const struct snd_ctl_tlv __user *tlvd = (const struct snd_ctl_tlv __user *)binary_data; + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; + struct snd_soc_component *scomp = scontrol->scomp; + struct snd_ctl_tlv header; + + /* + * The beginning of bytes data contains a header from where + * the length (as bytes) is needed to know the correct copy + * length of data from tlvd->tlv. + */ + if (copy_from_user(&header, tlvd, sizeof(struct snd_ctl_tlv))) + return -EFAULT; + + /* make sure TLV info is consistent */ + if (header.length + sizeof(struct snd_ctl_tlv) > size) { + dev_err_ratelimited(scomp->dev, "Inconsistent TLV, data %d + header %zu > %d\n", + header.length, sizeof(struct snd_ctl_tlv), size); + return -EINVAL; + } + + /* be->max is coming from topology */ + if (header.length > scontrol->max_size) { + dev_err_ratelimited(scomp->dev, "Bytes data size %d exceeds max %zu\n", + header.length, scontrol->max_size); + return -EINVAL; + } + + /* Check that header id matches the command */ + if (header.numid != cdata->cmd) { + dev_err_ratelimited(scomp->dev, "Incorrect command for bytes put %d\n", + header.numid); + return -EINVAL; + } + + if (copy_from_user(cdata->data, tlvd->tlv, header.length)) + return -EFAULT; + + if (cdata->data->magic != SOF_ABI_MAGIC) { + dev_err_ratelimited(scomp->dev, "Wrong ABI magic 0x%08x\n", cdata->data->magic); + return -EINVAL; + } + + if (SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, cdata->data->abi)) { + dev_err_ratelimited(scomp->dev, "Incompatible ABI version 0x%08x\n", + cdata->data->abi); + return -EINVAL; + } + + /* be->max has been verified to be >= sizeof(struct sof_abi_hdr) */ + if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) { + dev_err_ratelimited(scomp->dev, "Mismatch in ABI data size (truncated?)\n"); + return -EINVAL; + } + + /* notify DSP of byte control updates */ + if (pm_runtime_active(scomp->dev)) + return snd_sof_ipc_set_get_comp_data(scontrol, true); + + return 0; +} + +static int sof_ipc3_bytes_ext_volatile_get(struct snd_sof_control *scontrol, + const unsigned int __user *binary_data, + unsigned int size) +{ + struct snd_ctl_tlv __user *tlvd = (struct snd_ctl_tlv __user *)binary_data; + struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; + struct snd_soc_component *scomp = scontrol->scomp; + struct snd_ctl_tlv header; + size_t data_size; + int ret; + + /* + * Decrement the limit by ext bytes header size to + * ensure the user space buffer is not exceeded. + */ + if (size < sizeof(struct snd_ctl_tlv)) + return -ENOSPC; + + size -= sizeof(struct snd_ctl_tlv); + + /* set the ABI header values */ + cdata->data->magic = SOF_ABI_MAGIC; + cdata->data->abi = SOF_ABI_VERSION; + + /* get all the component data from DSP */ + ret = snd_sof_ipc_set_get_comp_data(scontrol, false); + if (ret < 0) + return ret; + + /* check data size doesn't exceed max coming from topology */ + if (cdata->data->size > scontrol->max_size - sizeof(struct sof_abi_hdr)) { + dev_err_ratelimited(scomp->dev, "User data size %d exceeds max size %zu\n", + cdata->data->size, + scontrol->max_size - sizeof(struct sof_abi_hdr)); + return -EINVAL; + } + + data_size = cdata->data->size + sizeof(struct sof_abi_hdr); + + /* make sure we don't exceed size provided by user space for data */ + if (data_size > size) + return -ENOSPC; + + header.numid = cdata->cmd; + header.length = data_size; + if (copy_to_user(tlvd, &header, sizeof(struct snd_ctl_tlv))) + return -EFAULT; + + if (copy_to_user(tlvd->tlv, cdata->data, data_size)) + return -EFAULT; + + return ret; +} + static void snd_sof_update_control(struct snd_sof_control *scontrol, struct sof_ipc_ctrl_data *cdata) { @@ -419,5 +587,8 @@ const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops = { .enum_get = sof_ipc3_enum_get, .bytes_put = sof_ipc3_bytes_put, .bytes_get = sof_ipc3_bytes_get, + .bytes_ext_put = sof_ipc3_bytes_ext_put, + .bytes_ext_get = sof_ipc3_bytes_ext_get, + .bytes_ext_volatile_get = sof_ipc3_bytes_ext_volatile_get, .update = sof_ipc3_control_update, };
Introduce the IPC-specific PCM ops that will be used to abstract the PCM related IPC's.
Signed-off-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Péter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- sound/soc/sof/sof-audio.h | 17 +++++++++++++++++ sound/soc/sof/sof-priv.h | 3 +++ 2 files changed, 20 insertions(+)
diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index bcd38c882078..4d25e781e5ae 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -45,6 +45,23 @@ struct snd_sof_dai_config_data { int dai_data; /* contains DAI-specific information */ };
+/** + * struct sof_ipc_pcm_ops - IPC-specific PCM ops + * @hw_params: Function pointer for hw_params + * @hw_free: Function pointer for hw_free + * @trigger: Function pointer for trigger + * @dai_link_fixup: Function pointer for DAI link fixup + */ +struct sof_ipc_pcm_ops { + int (*hw_params)(struct snd_soc_component *component, struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_sof_platform_stream_params *platform_params); + int (*hw_free)(struct snd_soc_component *component, struct snd_pcm_substream *substream); + int (*trigger)(struct snd_soc_component *component, struct snd_pcm_substream *substream, + int cmd); + int (*dai_link_fixup)(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params); +}; + /** * struct sof_ipc_tplg_control_ops - IPC-specific ops for topology kcontrol IO */ diff --git a/sound/soc/sof/sof-priv.h b/sound/soc/sof/sof-priv.h index 3e883044dd0f..0d9b640ae24c 100644 --- a/sound/soc/sof/sof-priv.h +++ b/sound/soc/sof/sof-priv.h @@ -371,15 +371,18 @@ struct sof_ipc_pm_ops { };
struct sof_ipc_tplg_ops; +struct sof_ipc_pcm_ops;
/** * struct sof_ipc_ops - IPC-specific ops * @tplg: Pointer to IPC-specific topology ops * @pm: Pointer to PM ops + * @pcm: Pointer to PCM ops */ struct sof_ipc_ops { const struct sof_ipc_tplg_ops *tplg; const struct sof_ipc_pm_ops *pm; + const struct sof_ipc_pcm_ops *pcm; };
/* SOF generic IPC data */
It will be used in IPC-specific code.
Signed-off-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Péter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- sound/soc/sof/pcm.c | 5 ++--- sound/soc/sof/sof-audio.h | 2 ++ 2 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 1661b0bc6f12..47599b57ff19 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -105,9 +105,8 @@ int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, struct snd_sof_dev 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) +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; diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 4d25e781e5ae..3ec3f746663a 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -457,4 +457,6 @@ int get_token_uuid(void *elem, void *object, u32 offset); int sof_update_ipc_object(struct snd_soc_component *scomp, void *object, enum sof_tokens token_id, struct snd_sof_tuple *tuples, int num_tuples, size_t object_size, int token_instance_num); +int sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_runtime *rtd, + struct snd_sof_pcm *spcm, int dir); #endif
Add the IPC3 PCM ops, define the hw_free op and modify all users to use the op.
Signed-off-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Péter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Bard Liao yung-chuan.liao@linux.intel.com --- sound/soc/sof/Makefile | 2 +- sound/soc/sof/ipc.c | 5 +++-- sound/soc/sof/ipc3-ops.h | 1 + sound/soc/sof/ipc3-pcm.c | 43 ++++++++++++++++++++++++++++++++++++++ sound/soc/sof/ipc3.c | 1 + sound/soc/sof/pcm.c | 44 +++++++++++++-------------------------- sound/soc/sof/sof-audio.c | 11 +++++++--- 7 files changed, 72 insertions(+), 35 deletions(-) create mode 100644 sound/soc/sof/ipc3-pcm.c
diff --git a/sound/soc/sof/Makefile b/sound/soc/sof/Makefile index f6d68a3096d9..18acbc001b9a 100644 --- a/sound/soc/sof/Makefile +++ b/sound/soc/sof/Makefile @@ -2,7 +2,7 @@
snd-sof-objs := core.o ops.o loader.o ipc.o pcm.o pm.o debug.o topology.o\ control.o trace.o iomem-utils.o sof-audio.o stream-ipc.o\ - ipc3-topology.o ipc3.o ipc3-control.o + ipc3-topology.o ipc3.o ipc3-control.o ipc3-pcm.o ifneq ($(CONFIG_SND_SOC_SOF_CLIENT),) snd-sof-objs += sof-client.o endif diff --git a/sound/soc/sof/ipc.c b/sound/soc/sof/ipc.c index af0ae137842b..5f5753608c79 100644 --- a/sound/soc/sof/ipc.c +++ b/sound/soc/sof/ipc.c @@ -1033,8 +1033,9 @@ struct snd_sof_ipc *snd_sof_ipc_init(struct snd_sof_dev *sdev) ipc->ops = &ipc3_ops;
/* check for mandatory ops */ - if (!ipc->ops->tplg || !ipc->ops->tplg->widget || !ipc->ops->tplg->control) { - dev_err(sdev->dev, "Invalid topology IPC ops\n"); + if (!ipc->ops->pcm || !ipc->ops->tplg || !ipc->ops->tplg->widget || + !ipc->ops->tplg->control) { + dev_err(sdev->dev, "Invalid IPC ops\n"); return NULL; }
diff --git a/sound/soc/sof/ipc3-ops.h b/sound/soc/sof/ipc3-ops.h index f3d6010d0b77..a4784626a3d7 100644 --- a/sound/soc/sof/ipc3-ops.h +++ b/sound/soc/sof/ipc3-ops.h @@ -16,5 +16,6 @@ extern const struct sof_ipc_tplg_ops ipc3_tplg_ops; extern const struct sof_ipc_ops ipc3_ops; extern const struct sof_ipc_tplg_control_ops tplg_ipc3_control_ops; +extern const struct sof_ipc_pcm_ops ipc3_pcm_ops;
#endif diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c new file mode 100644 index 000000000000..96f498b4b2d6 --- /dev/null +++ b/sound/soc/sof/ipc3-pcm.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) +// +// This file is provided under a dual BSD/GPLv2 license. When using or +// redistributing this file, you may do so under either license. +// +// Copyright(c) 2021 Intel Corporation. All rights reserved. +// +// + +#include <sound/pcm_params.h> +#include "ipc3-ops.h" +#include "ops.h" +#include "sof-priv.h" +#include "sof-audio.h" + +static int sof_ipc3_pcm_hw_free(struct snd_soc_component *component, + struct snd_pcm_substream *substream) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct sof_ipc_stream stream; + struct sof_ipc_reply reply; + struct snd_sof_pcm *spcm; + + spcm = snd_sof_find_spcm_dai(component, rtd); + if (!spcm) + return -EINVAL; + + if (!spcm->prepared[substream->stream]) + return 0; + + stream.hdr.size = sizeof(stream); + stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_FREE; + stream.comp_id = spcm->stream[substream->stream].comp_id; + + /* send IPC to the DSP */ + return sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream, + sizeof(stream), &reply, sizeof(reply)); +} + +const struct sof_ipc_pcm_ops ipc3_pcm_ops = { + .hw_free = sof_ipc3_pcm_hw_free, +}; diff --git a/sound/soc/sof/ipc3.c b/sound/soc/sof/ipc3.c index e71cf30908c6..03e914b62728 100644 --- a/sound/soc/sof/ipc3.c +++ b/sound/soc/sof/ipc3.c @@ -41,4 +41,5 @@ static const struct sof_ipc_pm_ops ipc3_pm_ops = { const struct sof_ipc_ops ipc3_ops = { .tplg = &ipc3_tplg_ops, .pm = &ipc3_pm_ops, + .pcm = &ipc3_pcm_ops, }; diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 47599b57ff19..8ef477aff938 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -82,29 +82,6 @@ void snd_sof_pcm_period_elapsed(struct snd_pcm_substream *substream) } EXPORT_SYMBOL(snd_sof_pcm_period_elapsed);
-int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, struct snd_sof_dev *sdev, - struct snd_sof_pcm *spcm) -{ - struct sof_ipc_stream stream; - struct sof_ipc_reply reply; - int ret; - - if (!spcm->prepared[substream->stream]) - return 0; - - stream.hdr.size = sizeof(stream); - stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_FREE; - stream.comp_id = spcm->stream[substream->stream].comp_id; - - /* send IPC to the DSP */ - ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream, - sizeof(stream), &reply, sizeof(reply)); - if (!ret) - spcm->prepared[substream->stream] = false; - - return ret; -} - int sof_pcm_setup_connected_widgets(struct snd_sof_dev *sdev, struct snd_soc_pcm_runtime *rtd, struct snd_sof_pcm *spcm, int dir) { @@ -145,6 +122,7 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_pcm_runtime *runtime = substream->runtime; struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm; struct snd_sof_platform_stream_params platform_params = { 0 }; struct sof_ipc_fw_version *v = &sdev->fw_ready.version; struct snd_sof_pcm *spcm; @@ -164,9 +142,13 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, * Handle repeated calls to hw_params() without free_pcm() in * between. At least ALSA OSS emulation depends on this. */ - ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); - if (ret < 0) - return ret; + if (pcm_ops->hw_free && spcm->prepared[substream->stream]) { + ret = pcm_ops->hw_free(component, substream); + if (ret < 0) + return ret; + + spcm->prepared[substream->stream] = false; + }
dev_dbg(component->dev, "pcm: hw params stream %d dir %d\n", spcm->pcm.pcm_id, substream->stream); @@ -289,6 +271,7 @@ static int sof_pcm_hw_free(struct snd_soc_component *component, { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm; struct snd_sof_pcm *spcm; int ret, err = 0;
@@ -304,10 +287,13 @@ static int sof_pcm_hw_free(struct snd_soc_component *component, spcm->pcm.pcm_id, substream->stream);
/* free PCM in the DSP */ - ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); - if (ret < 0) - err = ret; + if (pcm_ops->hw_free && spcm->prepared[substream->stream]) { + ret = pcm_ops->hw_free(component, substream); + if (ret < 0) + err = ret;
+ spcm->prepared[substream->stream] = false; + }
/* stop DMA */ ret = snd_sof_pcm_platform_hw_free(sdev, substream); diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 7aa4ac313de3..5088ec7019ed 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -591,12 +591,17 @@ int sof_set_up_pipelines(struct snd_sof_dev *sdev, bool verify) int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, struct snd_sof_pcm *spcm, int dir, bool free_widget_list) { + const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm; int ret;
/* Send PCM_FREE IPC to reset pipeline */ - ret = sof_pcm_dsp_pcm_free(substream, sdev, spcm); - if (ret < 0) - return ret; + if (pcm_ops->hw_free && spcm->prepared[substream->stream]) { + ret = pcm_ops->hw_free(sdev->component, substream); + if (ret < 0) + return ret; + } + + spcm->prepared[substream->stream] = false;
/* stop the DMA */ ret = snd_sof_pcm_platform_hw_free(sdev, substream);
Add the hw_params op for IPC3 and use it.
Signed-off-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Péter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- sound/soc/sof/ipc3-pcm.c | 100 +++++++++++++++++++++++++++++++++++ sound/soc/sof/pcm.c | 110 ++++++--------------------------------- 2 files changed, 115 insertions(+), 95 deletions(-)
diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c index 96f498b4b2d6..b715199a2aaa 100644 --- a/sound/soc/sof/ipc3-pcm.c +++ b/sound/soc/sof/ipc3-pcm.c @@ -38,6 +38,106 @@ static int sof_ipc3_pcm_hw_free(struct snd_soc_component *component, sizeof(stream), &reply, sizeof(reply)); }
+static int sof_ipc3_pcm_hw_params(struct snd_soc_component *component, + struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_sof_platform_stream_params *platform_params) +{ + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct sof_ipc_fw_version *v = &sdev->fw_ready.version; + struct snd_pcm_runtime *runtime = substream->runtime; + struct sof_ipc_pcm_params_reply ipc_params_reply; + struct sof_ipc_pcm_params pcm; + struct snd_sof_pcm *spcm; + int ret; + + spcm = snd_sof_find_spcm_dai(component, rtd); + if (!spcm) + return -EINVAL; + + memset(&pcm, 0, sizeof(pcm)); + + /* number of pages should be rounded up */ + pcm.params.buffer.pages = PFN_UP(runtime->dma_bytes); + + /* set IPC PCM parameters */ + pcm.hdr.size = sizeof(pcm); + pcm.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_PARAMS; + pcm.comp_id = spcm->stream[substream->stream].comp_id; + pcm.params.hdr.size = sizeof(pcm.params); + pcm.params.buffer.phy_addr = spcm->stream[substream->stream].page_table.addr; + pcm.params.buffer.size = runtime->dma_bytes; + pcm.params.direction = substream->stream; + pcm.params.sample_valid_bytes = params_width(params) >> 3; + pcm.params.buffer_fmt = SOF_IPC_BUFFER_INTERLEAVED; + pcm.params.rate = params_rate(params); + pcm.params.channels = params_channels(params); + pcm.params.host_period_bytes = params_period_bytes(params); + + /* container size */ + ret = snd_pcm_format_physical_width(params_format(params)); + if (ret < 0) + return ret; + pcm.params.sample_container_bytes = ret >> 3; + + /* format */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16: + pcm.params.frame_fmt = SOF_IPC_FRAME_S16_LE; + break; + case SNDRV_PCM_FORMAT_S24: + pcm.params.frame_fmt = SOF_IPC_FRAME_S24_4LE; + break; + case SNDRV_PCM_FORMAT_S32: + pcm.params.frame_fmt = SOF_IPC_FRAME_S32_LE; + break; + case SNDRV_PCM_FORMAT_FLOAT: + pcm.params.frame_fmt = SOF_IPC_FRAME_FLOAT; + break; + default: + return -EINVAL; + } + + /* Update the IPC message with information from the platform */ + pcm.params.stream_tag = platform_params->stream_tag; + + if (platform_params->use_phy_address) + pcm.params.buffer.phy_addr = platform_params->phy_addr; + + if (platform_params->no_ipc_position) { + /* For older ABIs set host_period_bytes to zero to inform + * FW we don't want position updates. Newer versions use + * no_stream_position for this purpose. + */ + if (v->abi_version < SOF_ABI_VER(3, 10, 0)) + pcm.params.host_period_bytes = 0; + else + pcm.params.no_stream_position = 1; + } + + dev_dbg(component->dev, "stream_tag %d", pcm.params.stream_tag); + + /* 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) { + dev_err(component->dev, "HW params ipc failed for stream %d\n", + pcm.params.stream_tag); + return ret; + } + + ret = snd_sof_set_stream_data_offset(sdev, substream, ipc_params_reply.posn_offset); + if (ret < 0) { + dev_err(component->dev, "%s: invalid stream data offset for PCM %d\n", + __func__, spcm->pcm.pcm_id); + return ret; + } + + return ret; +} + const struct sof_ipc_pcm_ops ipc3_pcm_ops = { + .hw_params = sof_ipc3_pcm_hw_params, .hw_free = sof_ipc3_pcm_hw_free, }; diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 8ef477aff938..8a194c130981 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -119,15 +119,12 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); - struct snd_pcm_runtime *runtime = substream->runtime; struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm; + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_sof_platform_stream_params platform_params = { 0 }; - struct sof_ipc_fw_version *v = &sdev->fw_ready.version; + const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm; + struct snd_pcm_runtime *runtime = substream->runtime; struct snd_sof_pcm *spcm; - struct sof_ipc_pcm_params pcm; - struct sof_ipc_pcm_params_reply ipc_params_reply; int ret;
/* nothing to do for BE */ @@ -153,117 +150,40 @@ static int sof_pcm_hw_params(struct snd_soc_component *component, dev_dbg(component->dev, "pcm: hw params stream %d dir %d\n", spcm->pcm.pcm_id, substream->stream);
- memset(&pcm, 0, sizeof(pcm)); + /* 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; + }
/* create compressed page table for audio firmware */ if (runtime->buffer_changed) { ret = create_page_table(component, substream, runtime->dma_area, runtime->dma_bytes); + if (ret < 0) return ret; }
- /* number of pages should be rounded up */ - pcm.params.buffer.pages = PFN_UP(runtime->dma_bytes); - - /* set IPC PCM parameters */ - pcm.hdr.size = sizeof(pcm); - pcm.hdr.cmd = SOF_IPC_GLB_STREAM_MSG | SOF_IPC_STREAM_PCM_PARAMS; - pcm.comp_id = spcm->stream[substream->stream].comp_id; - pcm.params.hdr.size = sizeof(pcm.params); - pcm.params.buffer.phy_addr = - spcm->stream[substream->stream].page_table.addr; - pcm.params.buffer.size = runtime->dma_bytes; - pcm.params.direction = substream->stream; - pcm.params.sample_valid_bytes = params_width(params) >> 3; - pcm.params.buffer_fmt = SOF_IPC_BUFFER_INTERLEAVED; - pcm.params.rate = params_rate(params); - pcm.params.channels = params_channels(params); - pcm.params.host_period_bytes = params_period_bytes(params); - - /* container size */ - ret = snd_pcm_format_physical_width(params_format(params)); - if (ret < 0) - return ret; - pcm.params.sample_container_bytes = ret >> 3; - - /* format */ - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16: - pcm.params.frame_fmt = SOF_IPC_FRAME_S16_LE; - break; - case SNDRV_PCM_FORMAT_S24: - pcm.params.frame_fmt = SOF_IPC_FRAME_S24_4LE; - break; - case SNDRV_PCM_FORMAT_S32: - pcm.params.frame_fmt = SOF_IPC_FRAME_S32_LE; - break; - case SNDRV_PCM_FORMAT_FLOAT: - pcm.params.frame_fmt = SOF_IPC_FRAME_FLOAT; - break; - default: - return -EINVAL; - } - - /* firmware already configured host stream */ - ret = snd_sof_pcm_platform_hw_params(sdev, - substream, - params, - &platform_params); + ret = snd_sof_pcm_platform_hw_params(sdev, substream, params, &platform_params); if (ret < 0) { - dev_err(component->dev, "error: platform hw params failed\n"); + dev_err(component->dev, "platform hw params failed\n"); return ret; }
- /* Update the IPC message with information from the platform */ - pcm.params.stream_tag = platform_params.stream_tag; - - if (platform_params.use_phy_address) - pcm.params.buffer.phy_addr = platform_params.phy_addr; - - if (platform_params.no_ipc_position) { - /* For older ABIs set host_period_bytes to zero to inform - * FW we don't want position updates. Newer versions use - * no_stream_position for this purpose. - */ - if (v->abi_version < SOF_ABI_VER(3, 10, 0)) - pcm.params.host_period_bytes = 0; - else - pcm.params.no_stream_position = 1; - } - - dev_dbg(component->dev, "stream_tag %d", pcm.params.stream_tag); - - /* 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 (pcm_ops->hw_params) { + ret = pcm_ops->hw_params(component, substream, params, &platform_params); 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) { - dev_err(component->dev, "error: hw params ipc failed for stream %d\n", - pcm.params.stream_tag); - return ret; - } - - ret = snd_sof_set_stream_data_offset(sdev, substream, - ipc_params_reply.posn_offset); - if (ret < 0) { - dev_err(component->dev, "%s: invalid stream data offset for PCM %d\n", - __func__, spcm->pcm.pcm_id); - return ret; - } - spcm->prepared[substream->stream] = true;
/* save pcm hw_params */ memcpy(&spcm->params[substream->stream], params, sizeof(*params));
- return ret; + return 0; }
static int sof_pcm_hw_free(struct snd_soc_component *component,
Add the trigger PCM op for IPC3 and use it.
Signed-off-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Péter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- sound/soc/sof/ipc3-pcm.c | 43 ++++++++++++++++++++++++++++++++++++++++ sound/soc/sof/pcm.c | 23 ++++++--------------- 2 files changed, 49 insertions(+), 17 deletions(-)
diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c index b715199a2aaa..d83d54540bbe 100644 --- a/sound/soc/sof/ipc3-pcm.c +++ b/sound/soc/sof/ipc3-pcm.c @@ -137,7 +137,50 @@ static int sof_ipc3_pcm_hw_params(struct snd_soc_component *component, return ret; }
+static int sof_ipc3_pcm_trigger(struct snd_soc_component *component, + struct snd_pcm_substream *substream, int cmd) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct sof_ipc_stream stream; + struct sof_ipc_reply reply; + struct snd_sof_pcm *spcm; + + spcm = snd_sof_find_spcm_dai(component, rtd); + if (!spcm) + return -EINVAL; + + stream.hdr.size = sizeof(stream); + stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG; + stream.comp_id = spcm->stream[substream->stream].comp_id; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_PAUSE; + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_RELEASE; + break; + case SNDRV_PCM_TRIGGER_START: + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_START; + break; + case SNDRV_PCM_TRIGGER_SUSPEND: + fallthrough; + case SNDRV_PCM_TRIGGER_STOP: + stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_STOP; + break; + default: + dev_err(component->dev, "Unhandled trigger cmd %d\n", cmd); + return -EINVAL; + } + + /* send IPC to the DSP */ + return sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream, + sizeof(stream), &reply, sizeof(reply)); +} + const struct sof_ipc_pcm_ops ipc3_pcm_ops = { .hw_params = sof_ipc3_pcm_hw_params, .hw_free = sof_ipc3_pcm_hw_free, + .trigger = sof_ipc3_pcm_trigger, }; diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index 8a194c130981..dc55a04a1588 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -274,13 +274,12 @@ static int sof_pcm_trigger(struct snd_soc_component *component, { struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm; struct snd_sof_pcm *spcm; - 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; + int ret = 0;
/* nothing to do for BE */ if (rtd->dai_link->no_pcm) @@ -293,17 +292,11 @@ static int sof_pcm_trigger(struct snd_soc_component *component, dev_dbg(component->dev, "pcm: trigger stream %d dir %d cmd %d\n", spcm->pcm.pcm_id, substream->stream, cmd);
- stream.hdr.size = sizeof(stream); - stream.hdr.cmd = SOF_IPC_GLB_STREAM_MSG; - stream.comp_id = spcm->stream[substream->stream].comp_id; - switch (cmd) { case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_PAUSE; ipc_first = true; break; case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_RELEASE; break; case SNDRV_PCM_TRIGGER_START: if (spcm->stream[substream->stream].suspend_ignored) { @@ -315,7 +308,6 @@ static int sof_pcm_trigger(struct snd_soc_component *component, spcm->stream[substream->stream].suspend_ignored = false; return 0; } - stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_START; break; case SNDRV_PCM_TRIGGER_SUSPEND: if (sdev->system_suspend_target == SOF_SUSPEND_S0IX && @@ -332,13 +324,11 @@ static int sof_pcm_trigger(struct snd_soc_component *component, free_widget_list = true; fallthrough; case SNDRV_PCM_TRIGGER_STOP: - stream.hdr.cmd |= SOF_IPC_STREAM_TRIG_STOP; ipc_first = true; reset_hw_params = true; break; default: - dev_err(component->dev, "error: unhandled trigger cmd %d\n", - cmd); + dev_err(component->dev, "Unhandled trigger cmd %d\n", cmd); return -EINVAL; }
@@ -349,11 +339,10 @@ static int sof_pcm_trigger(struct snd_soc_component *component, if (!ipc_first) snd_sof_pcm_platform_trigger(sdev, substream, cmd);
- /* send IPC to the DSP */ - ret = sof_ipc_tx_message(sdev->ipc, stream.hdr.cmd, &stream, - sizeof(stream), &reply, sizeof(reply)); + if (pcm_ops->trigger) + ret = pcm_ops->trigger(component, substream, cmd);
- /* need to STOP DMA even if STOP IPC failed */ + /* need to STOP DMA even if trigger IPC failed */ if (ipc_first) snd_sof_pcm_platform_trigger(sdev, substream, cmd);
Define the dai_link_fixup PCM op for IPC3 and use it
Signed-off-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Péter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Bard Liao yung-chuan.liao@linux.intel.com --- sound/soc/sof/ipc3-pcm.c | 186 +++++++++++++++++++++++++++++++++++++++ sound/soc/sof/pcm.c | 174 +----------------------------------- 2 files changed, 189 insertions(+), 171 deletions(-)
diff --git a/sound/soc/sof/ipc3-pcm.c b/sound/soc/sof/ipc3-pcm.c index d83d54540bbe..58b75943cf6d 100644 --- a/sound/soc/sof/ipc3-pcm.c +++ b/sound/soc/sof/ipc3-pcm.c @@ -179,8 +179,194 @@ static int sof_ipc3_pcm_trigger(struct snd_soc_component *component, sizeof(stream), &reply, sizeof(reply)); }
+static void ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, const char *link_name, + struct snd_pcm_hw_params *params) +{ + struct sof_ipc_dai_config *config; + struct snd_sof_dai *dai; + int i; + + /* + * Search for all matching DAIs as we can have both playback and capture DAI + * associated with the same link. + */ + list_for_each_entry(dai, &sdev->dai_list, list) { + if (!dai->name || strcmp(link_name, dai->name)) + continue; + for (i = 0; i < dai->number_configs; i++) { + struct sof_dai_private_data *private = dai->private; + + config = &private->dai_config[i]; + if (config->ssp.fsync_rate == params_rate(params)) { + dev_dbg(sdev->dev, "DAI config %d matches pcm hw params\n", i); + dai->current_config = i; + break; + } + } + } +} + +static int sof_ipc3_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME); + struct snd_interval *channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_sof_dai *dai = snd_sof_find_dai(component, (char *)rtd->dai_link->name); + struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct sof_dai_private_data *private; + struct snd_soc_dpcm *dpcm; + + if (!dai) { + dev_err(component->dev, "%s: No DAI found with name %s\n", __func__, + rtd->dai_link->name); + return -EINVAL; + } + + private = dai->private; + if (!private) { + dev_err(component->dev, "%s: No private data found for DAI %s\n", __func__, + rtd->dai_link->name); + return -EINVAL; + } + + /* read format from topology */ + snd_mask_none(fmt); + + switch (private->comp_dai->config.frame_fmt) { + case SOF_IPC_FRAME_S16_LE: + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE); + break; + case SOF_IPC_FRAME_S24_4LE: + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); + break; + case SOF_IPC_FRAME_S32_LE: + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE); + break; + default: + dev_err(component->dev, "No available DAI format!\n"); + return -EINVAL; + } + + /* read rate and channels from topology */ + switch (private->dai_config->type) { + case SOF_DAI_INTEL_SSP: + /* search for config to pcm params match, if not found use default */ + ssp_dai_config_pcm_params_match(sdev, (char *)rtd->dai_link->name, params); + + rate->min = private->dai_config[dai->current_config].ssp.fsync_rate; + rate->max = private->dai_config[dai->current_config].ssp.fsync_rate; + channels->min = private->dai_config[dai->current_config].ssp.tdm_slots; + channels->max = private->dai_config[dai->current_config].ssp.tdm_slots; + + dev_dbg(component->dev, "rate_min: %d rate_max: %d\n", rate->min, rate->max); + dev_dbg(component->dev, "channels_min: %d channels_max: %d\n", + channels->min, channels->max); + + break; + case SOF_DAI_INTEL_DMIC: + /* DMIC only supports 16 or 32 bit formats */ + if (private->comp_dai->config.frame_fmt == SOF_IPC_FRAME_S24_4LE) { + dev_err(component->dev, "Invalid fmt %d for DAI type %d\n", + private->comp_dai->config.frame_fmt, + private->dai_config->type); + } + break; + case SOF_DAI_INTEL_HDA: + /* + * HDAudio does not follow the default trigger + * sequence due to firmware implementation + */ + for_each_dpcm_fe(rtd, SNDRV_PCM_STREAM_PLAYBACK, dpcm) { + struct snd_soc_pcm_runtime *fe = dpcm->fe; + + fe->dai_link->trigger[SNDRV_PCM_STREAM_PLAYBACK] = + SND_SOC_DPCM_TRIGGER_POST; + } + break; + case SOF_DAI_INTEL_ALH: + /* + * Dai could run with different channel count compared with + * front end, so get dai channel count from topology + */ + channels->min = private->dai_config->alh.channels; + channels->max = private->dai_config->alh.channels; + break; + case SOF_DAI_IMX_ESAI: + rate->min = private->dai_config->esai.fsync_rate; + rate->max = private->dai_config->esai.fsync_rate; + channels->min = private->dai_config->esai.tdm_slots; + channels->max = private->dai_config->esai.tdm_slots; + + dev_dbg(component->dev, "rate_min: %d rate_max: %d\n", rate->min, rate->max); + dev_dbg(component->dev, "channels_min: %d channels_max: %d\n", + channels->min, channels->max); + break; + case SOF_DAI_MEDIATEK_AFE: + rate->min = private->dai_config->afe.rate; + rate->max = private->dai_config->afe.rate; + channels->min = private->dai_config->afe.channels; + channels->max = private->dai_config->afe.channels; + + dev_dbg(component->dev, "rate_min: %d rate_max: %d\n", rate->min, rate->max); + dev_dbg(component->dev, "channels_min: %d channels_max: %d\n", + channels->min, channels->max); + break; + case SOF_DAI_IMX_SAI: + rate->min = private->dai_config->sai.fsync_rate; + rate->max = private->dai_config->sai.fsync_rate; + channels->min = private->dai_config->sai.tdm_slots; + channels->max = private->dai_config->sai.tdm_slots; + + dev_dbg(component->dev, "rate_min: %d rate_max: %d\n", rate->min, rate->max); + dev_dbg(component->dev, "channels_min: %d channels_max: %d\n", + channels->min, channels->max); + break; + case SOF_DAI_AMD_BT: + rate->min = private->dai_config->acpbt.fsync_rate; + rate->max = private->dai_config->acpbt.fsync_rate; + channels->min = private->dai_config->acpbt.tdm_slots; + channels->max = private->dai_config->acpbt.tdm_slots; + + dev_dbg(component->dev, + "AMD_BT rate_min: %d rate_max: %d\n", rate->min, rate->max); + dev_dbg(component->dev, "AMD_BT channels_min: %d channels_max: %d\n", + channels->min, channels->max); + break; + case SOF_DAI_AMD_SP: + rate->min = private->dai_config->acpsp.fsync_rate; + rate->max = private->dai_config->acpsp.fsync_rate; + channels->min = private->dai_config->acpsp.tdm_slots; + channels->max = private->dai_config->acpsp.tdm_slots; + + dev_dbg(component->dev, + "AMD_SP rate_min: %d rate_max: %d\n", rate->min, rate->max); + dev_dbg(component->dev, "AMD_SP channels_min: %d channels_max: %d\n", + channels->min, channels->max); + break; + case SOF_DAI_AMD_DMIC: + rate->min = private->dai_config->acpdmic.fsync_rate; + rate->max = private->dai_config->acpdmic.fsync_rate; + channels->min = private->dai_config->acpdmic.tdm_slots; + channels->max = private->dai_config->acpdmic.tdm_slots; + + dev_dbg(component->dev, + "AMD_DMIC rate_min: %d rate_max: %d\n", rate->min, rate->max); + dev_dbg(component->dev, "AMD_DMIC channels_min: %d channels_max: %d\n", + channels->min, channels->max); + break; + default: + dev_err(component->dev, "Invalid DAI type %d\n", private->dai_config->type); + break; + } + + return 0; +} + const struct sof_ipc_pcm_ops ipc3_pcm_ops = { .hw_params = sof_ipc3_pcm_hw_params, .hw_free = sof_ipc3_pcm_hw_free, .trigger = sof_ipc3_pcm_trigger, + .dai_link_fixup = sof_ipc3_pcm_dai_link_fixup, }; diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c index dc55a04a1588..658cd8966c9a 100644 --- a/sound/soc/sof/pcm.c +++ b/sound/soc/sof/pcm.c @@ -556,33 +556,6 @@ static int sof_pcm_new(struct snd_soc_component *component, return 0; }
-static void ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, const char *link_name, - struct snd_pcm_hw_params *params) -{ - struct sof_ipc_dai_config *config; - struct snd_sof_dai *dai; - int i; - - /* - * Search for all matching DAIs as we can have both playback and capture DAI - * associated with the same link. - */ - list_for_each_entry(dai, &sdev->dai_list, list) { - if (!dai->name || strcmp(link_name, dai->name)) - continue; - for (i = 0; i < dai->number_configs; i++) { - struct sof_dai_private_data *private = dai->private; - - config = &private->dai_config[i]; - if (config->ssp.fsync_rate == params_rate(params)) { - dev_dbg(sdev->dev, "DAI config %d matches pcm hw params\n", i); - dai->current_config = i; - break; - } - } - } -} - /* fixup the BE DAI link to match any values from topology */ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params) { @@ -596,8 +569,7 @@ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_pa struct snd_sof_dai *dai = snd_sof_find_dai(component, (char *)rtd->dai_link->name); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); - struct sof_dai_private_data *private = dai->private; - struct snd_soc_dpcm *dpcm; + const struct sof_ipc_pcm_ops *pcm_ops = sdev->ipc->ops->pcm;
/* no topology exists for this BE, try a common configuration */ if (!dai) { @@ -618,148 +590,8 @@ int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_pa return 0; }
- /* read format from topology */ - snd_mask_none(fmt); - - switch (private->comp_dai->config.frame_fmt) { - case SOF_IPC_FRAME_S16_LE: - snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE); - break; - case SOF_IPC_FRAME_S24_4LE: - snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); - break; - case SOF_IPC_FRAME_S32_LE: - snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S32_LE); - break; - default: - dev_err(component->dev, "error: No available DAI format!\n"); - return -EINVAL; - } - - /* read rate and channels from topology */ - switch (private->dai_config->type) { - case SOF_DAI_INTEL_SSP: - /* search for config to pcm params match, if not found use default */ - ssp_dai_config_pcm_params_match(sdev, (char *)rtd->dai_link->name, params); - - rate->min = private->dai_config[dai->current_config].ssp.fsync_rate; - rate->max = private->dai_config[dai->current_config].ssp.fsync_rate; - channels->min = private->dai_config[dai->current_config].ssp.tdm_slots; - channels->max = private->dai_config[dai->current_config].ssp.tdm_slots; - - dev_dbg(component->dev, - "rate_min: %d rate_max: %d\n", rate->min, rate->max); - dev_dbg(component->dev, - "channels_min: %d channels_max: %d\n", - channels->min, channels->max); - - break; - case SOF_DAI_INTEL_DMIC: - /* DMIC only supports 16 or 32 bit formats */ - if (private->comp_dai->config.frame_fmt == SOF_IPC_FRAME_S24_4LE) { - dev_err(component->dev, - "error: invalid fmt %d for DAI type %d\n", - private->comp_dai->config.frame_fmt, - private->dai_config->type); - } - break; - case SOF_DAI_INTEL_HDA: - /* - * HDAudio does not follow the default trigger - * sequence due to firmware implementation - */ - for_each_dpcm_fe(rtd, SNDRV_PCM_STREAM_PLAYBACK, dpcm) { - struct snd_soc_pcm_runtime *fe = dpcm->fe; - - fe->dai_link->trigger[SNDRV_PCM_STREAM_PLAYBACK] = - SND_SOC_DPCM_TRIGGER_POST; - } - break; - case SOF_DAI_INTEL_ALH: - /* - * Dai could run with different channel count compared with - * front end, so get dai channel count from topology - */ - channels->min = private->dai_config->alh.channels; - channels->max = private->dai_config->alh.channels; - break; - case SOF_DAI_IMX_ESAI: - rate->min = private->dai_config->esai.fsync_rate; - rate->max = private->dai_config->esai.fsync_rate; - channels->min = private->dai_config->esai.tdm_slots; - channels->max = private->dai_config->esai.tdm_slots; - - dev_dbg(component->dev, - "rate_min: %d rate_max: %d\n", rate->min, rate->max); - dev_dbg(component->dev, - "channels_min: %d channels_max: %d\n", - channels->min, channels->max); - break; - case SOF_DAI_MEDIATEK_AFE: - rate->min = private->dai_config->afe.rate; - rate->max = private->dai_config->afe.rate; - channels->min = private->dai_config->afe.channels; - channels->max = private->dai_config->afe.channels; - - dev_dbg(component->dev, - "rate_min: %d rate_max: %d\n", rate->min, rate->max); - dev_dbg(component->dev, - "channels_min: %d channels_max: %d\n", - channels->min, channels->max); - break; - case SOF_DAI_IMX_SAI: - rate->min = private->dai_config->sai.fsync_rate; - rate->max = private->dai_config->sai.fsync_rate; - channels->min = private->dai_config->sai.tdm_slots; - channels->max = private->dai_config->sai.tdm_slots; - - dev_dbg(component->dev, - "rate_min: %d rate_max: %d\n", rate->min, rate->max); - dev_dbg(component->dev, - "channels_min: %d channels_max: %d\n", - channels->min, channels->max); - break; - case SOF_DAI_AMD_BT: - rate->min = private->dai_config->acpbt.fsync_rate; - rate->max = private->dai_config->acpbt.fsync_rate; - channels->min = private->dai_config->acpbt.tdm_slots; - channels->max = private->dai_config->acpbt.tdm_slots; - - dev_dbg(component->dev, - "AMD_BT rate_min: %d rate_max: %d\n", rate->min, rate->max); - dev_dbg(component->dev, - "AMD_BT channels_min: %d channels_max: %d\n", - channels->min, channels->max); - break; - case SOF_DAI_AMD_SP: - rate->min = private->dai_config->acpsp.fsync_rate; - rate->max = private->dai_config->acpsp.fsync_rate; - channels->min = private->dai_config->acpsp.tdm_slots; - channels->max = private->dai_config->acpsp.tdm_slots; - - dev_dbg(component->dev, - "AMD_SP rate_min: %d rate_max: %d\n", rate->min, rate->max); - dev_dbg(component->dev, - "AMD_SP channels_min: %d channels_max: %d\n", - channels->min, channels->max); - break; - case SOF_DAI_AMD_DMIC: - rate->min = private->dai_config->acpdmic.fsync_rate; - rate->max = private->dai_config->acpdmic.fsync_rate; - channels->min = private->dai_config->acpdmic.tdm_slots; - channels->max = private->dai_config->acpdmic.tdm_slots; - - dev_dbg(component->dev, - "AMD_DMIC rate_min: %d rate_max: %d\n", rate->min, rate->max); - dev_dbg(component->dev, - "AMD_DMIC channels_min: %d channels_max: %d\n", - channels->min, channels->max); - break; - default: - dev_err(component->dev, "error: invalid DAI type %d\n", - private->dai_config->type); - break; - } + if (pcm_ops->dai_link_fixup) + return pcm_ops->dai_link_fixup(rtd, params);
return 0; }
This will be used in IPC3-specific code.
Signed-off-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Péter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com --- sound/soc/sof/sof-audio.c | 4 ++-- sound/soc/sof/sof-audio.h | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index 5088ec7019ed..8885709f40df 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -204,8 +204,8 @@ int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) } EXPORT_SYMBOL(sof_widget_setup);
-static int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsource, - struct snd_soc_dapm_widget *wsink) +int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsource, + struct snd_soc_dapm_widget *wsink) { const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; struct snd_sof_widget *src_widget = wsource->dobj.private; diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 3ec3f746663a..777da321e2aa 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -441,6 +441,8 @@ 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); +int sof_route_setup(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *wsource, + struct snd_soc_dapm_widget *wsink);
/* PCM */ int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir);
Introduce two new ops, set_up_all_pipelines and tear_down_all_pipelines in struct ipc_tplg_ops and define these for IPC3.
Signed-off-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Péter Ujfalusi peter.ujfalusi@linux.intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Bard Liao yung-chuan.liao@linux.intel.com --- sound/soc/sof/ipc3-topology.c | 197 ++++++++++++++++++++++++++++++++++ sound/soc/sof/pm.c | 17 +-- sound/soc/sof/sof-audio.c | 186 -------------------------------- sound/soc/sof/sof-audio.h | 6 +- sound/soc/sof/topology.c | 55 +++++----- 5 files changed, 235 insertions(+), 226 deletions(-)
diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index 55be97ee816b..3ac65dacc7b9 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -2057,6 +2057,201 @@ static int sof_ipc3_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget return ret; }
+static int sof_ipc3_set_up_all_pipelines(struct snd_sof_dev *sdev, bool verify) +{ + struct sof_ipc_fw_version *v = &sdev->fw_ready.version; + struct snd_sof_widget *swidget; + struct snd_sof_route *sroute; + int ret; + + /* restore pipeline components */ + list_for_each_entry(swidget, &sdev->widget_list, list) { + /* only set up the widgets belonging to static pipelines */ + if (!verify && swidget->dynamic_pipeline_widget) + continue; + + /* + * For older firmware, skip scheduler widgets in this loop, + * sof_widget_setup() will be called in the 'complete pipeline' loop + */ + if (v->abi_version < SOF_ABI_VER(3, 19, 0) && + swidget->id == snd_soc_dapm_scheduler) + continue; + + /* 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_dai_private_data *private; + struct sof_ipc_dai_config *config; + + if (!dai || !dai->private) + continue; + private = dai->private; + if (!private->dai_config) + continue; + + config = private->dai_config; + /* + * 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_widget_setup(sdev, swidget); + if (ret < 0) + return ret; + } + + /* restore pipeline connections */ + list_for_each_entry(sroute, &sdev->route_list, list) { + /* only set up routes belonging to static pipelines */ + if (!verify && (sroute->src_widget->dynamic_pipeline_widget || + sroute->sink_widget->dynamic_pipeline_widget)) + continue; + + /* + * For virtual routes, both sink and source are not buffer. IPC3 only supports + * connections between a buffer and a component. Ignore the rest. + */ + if (sroute->src_widget->id != snd_soc_dapm_buffer && + sroute->sink_widget->id != snd_soc_dapm_buffer) + continue; + + ret = sof_route_setup(sdev, sroute->src_widget->widget, + sroute->sink_widget->widget); + if (ret < 0) { + dev_err(sdev->dev, "%s: route set up failed\n", __func__); + return ret; + } + } + + /* complete pipeline */ + 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; + + if (v->abi_version < SOF_ABI_VER(3, 19, 0)) { + ret = sof_widget_setup(sdev, swidget); + if (ret < 0) + return ret; + } + + swidget->complete = sof_ipc3_complete_pipeline(sdev, swidget); + if (swidget->complete < 0) + return swidget->complete; + break; + default: + break; + } + } + + return 0; +} + +/* + * Free the PCM, its associated widgets and set the prepared flag to false for all PCMs that + * did not get suspended(ex: paused streams) so the widgets can be set up again during resume. + */ +static int sof_tear_down_left_over_pipelines(struct snd_sof_dev *sdev) +{ + struct snd_sof_widget *swidget; + struct snd_sof_pcm *spcm; + int dir, ret; + + /* + * free all PCMs and their associated DAPM widgets if their connected DAPM widget + * list is not NULL. This should only be true for paused streams at this point. + * This is equivalent to the handling of FE DAI suspend trigger for running streams. + */ + list_for_each_entry(spcm, &sdev->pcm_list, list) { + for_each_pcm_streams(dir) { + struct snd_pcm_substream *substream = spcm->stream[dir].substream; + + if (!substream || !substream->runtime) + continue; + + if (spcm->stream[dir].list) { + ret = sof_pcm_stream_free(sdev, substream, spcm, dir, true); + if (ret < 0) + return ret; + } + } + } + + /* + * free any left over DAI widgets. This is equivalent to the handling of suspend trigger + * for the BE DAI for running streams. + */ + list_for_each_entry(swidget, &sdev->widget_list, list) + if (WIDGET_IS_DAI(swidget->id) && swidget->use_count == 1) { + ret = sof_widget_free(sdev, swidget); + if (ret < 0) + return ret; + } + + return 0; +} + +/* + * For older firmware, this function doesn't free widgets for static pipelines during suspend. + * It only resets use_count for all widgets. + */ +static int sof_ipc3_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verify) +{ + struct sof_ipc_fw_version *v = &sdev->fw_ready.version; + 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 running streams are suspended and during + * topology loading the sound card unavailable to open PCMs. + */ + list_for_each_entry(swidget, &sdev->widget_list, list) { + if (swidget->dynamic_pipeline_widget) + continue; + + /* Do not free widgets for static pipelines with FW ABI older than 3.19 */ + if (!verify && !swidget->dynamic_pipeline_widget && + v->abi_version < SOF_ABI_VER(3, 19, 0)) { + swidget->use_count = 0; + swidget->complete = 0; + continue; + } + + ret = sof_widget_free(sdev, swidget); + if (ret < 0) + return ret; + } + + /* + * Tear down all pipelines associated with PCMs that did not get suspended + * and unset the prepare flag so that they can be set up again during resume. + * Skip this step for older firmware. + */ + if (!verify && v->abi_version >= SOF_ABI_VER(3, 19, 0)) { + ret = sof_tear_down_left_over_pipelines(sdev); + if (ret < 0) { + dev_err(sdev->dev, "failed to tear down paused pipelines\n"); + return ret; + } + } + + list_for_each_entry(sroute, &sdev->route_list, list) + sroute->setup = false; + + return 0; +} + /* token list for each topology object */ static enum sof_tokens host_token_list[] = { SOF_CORE_TOKENS, @@ -2164,4 +2359,6 @@ const struct sof_ipc_tplg_ops ipc3_tplg_ops = { .widget_free = sof_ipc3_widget_free, .widget_setup = sof_ipc3_widget_setup, .dai_config = sof_ipc3_dai_config, + .set_up_all_pipelines = sof_ipc3_set_up_all_pipelines, + .tear_down_all_pipelines = sof_ipc3_tear_down_all_pipelines, }; diff --git a/sound/soc/sof/pm.c b/sound/soc/sof/pm.c index 10adbbd0a9cd..1c319582ca6f 100644 --- a/sound/soc/sof/pm.c +++ b/sound/soc/sof/pm.c @@ -71,6 +71,7 @@ static int sof_resume(struct device *dev, bool runtime_resume) { struct snd_sof_dev *sdev = dev_get_drvdata(dev); const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm; + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; u32 old_state = sdev->dsp_power_state.state; int ret;
@@ -144,12 +145,12 @@ static int sof_resume(struct device *dev, bool runtime_resume) }
/* restore pipelines */ - ret = sof_set_up_pipelines(sdev, false); - if (ret < 0) { - dev_err(sdev->dev, - "error: failed to restore pipeline after resume %d\n", - ret); - return ret; + if (tplg_ops->set_up_all_pipelines) { + ret = tplg_ops->set_up_all_pipelines(sdev, false); + if (ret < 0) { + dev_err(sdev->dev, "Failed to restore pipeline after resume %d\n", ret); + return ret; + } }
/* Notify clients not managed by pm framework about core resume */ @@ -169,6 +170,7 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) { struct snd_sof_dev *sdev = dev_get_drvdata(dev); const struct sof_ipc_pm_ops *pm_ops = sdev->ipc->ops->pm; + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg; pm_message_t pm_state; u32 target_state = 0; int ret; @@ -204,7 +206,8 @@ static int sof_suspend(struct device *dev, bool runtime_suspend) goto suspend; }
- sof_tear_down_pipelines(sdev, false); + if (tplg_ops->tear_down_all_pipelines) + tplg_ops->tear_down_all_pipelines(sdev, 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 8885709f40df..b4ee65cf9841 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -498,96 +498,6 @@ int sof_set_hw_params_upon_resume(struct device *dev) return snd_sof_dsp_hw_params_upon_resume(sdev); }
-int sof_set_up_pipelines(struct snd_sof_dev *sdev, bool verify) -{ - const struct sof_ipc_tplg_ops *ipc_tplg_ops = sdev->ipc->ops->tplg; - struct sof_ipc_fw_version *v = &sdev->fw_ready.version; - struct snd_sof_widget *swidget; - struct snd_sof_route *sroute; - int ret; - - /* restore pipeline components */ - list_for_each_entry(swidget, &sdev->widget_list, list) { - /* only set up the widgets belonging to static pipelines */ - if (!verify && swidget->dynamic_pipeline_widget) - continue; - - /* - * For older firmware, skip scheduler widgets in this loop, - * sof_widget_setup() will be called in the 'complete pipeline' loop - */ - if (v->abi_version < SOF_ABI_VER(3, 19, 0) && - swidget->id == snd_soc_dapm_scheduler) - continue; - - /* 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_dai_private_data *private = dai->private; - struct sof_ipc_dai_config *config; - - if (!dai || !private || !private->dai_config) - continue; - - config = private->dai_config; - /* - * 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_widget_setup(sdev, swidget); - if (ret < 0) - return ret; - } - - /* restore pipeline connections */ - list_for_each_entry(sroute, &sdev->route_list, list) { - - /* only set up routes belonging to static pipelines */ - if (!verify && (sroute->src_widget->dynamic_pipeline_widget || - sroute->sink_widget->dynamic_pipeline_widget)) - continue; - - ret = ipc_tplg_ops->route_setup(sdev, sroute); - if (ret < 0) { - dev_err(sdev->dev, "%s: restore pipeline connections failed\n", __func__); - return ret; - } - } - - /* complete pipeline */ - 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; - - if (v->abi_version < SOF_ABI_VER(3, 19, 0)) { - ret = sof_widget_setup(sdev, swidget); - if (ret < 0) - return ret; - } - - if (ipc_tplg_ops->pipeline_complete) { - swidget->complete = ipc_tplg_ops->pipeline_complete(sdev, swidget); - if (swidget->complete < 0) - return swidget->complete; - } - break; - default: - break; - } - } - - return 0; -} - int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *substream, struct snd_sof_pcm *spcm, int dir, bool free_widget_list) { @@ -618,102 +528,6 @@ int sof_pcm_stream_free(struct snd_sof_dev *sdev, struct snd_pcm_substream *subs return ret; }
-/* - * Free the PCM, its associated widgets and set the prepared flag to false for all PCMs that - * did not get suspended(ex: paused streams) so the widgets can be set up again during resume. - */ -static int sof_tear_down_left_over_pipelines(struct snd_sof_dev *sdev) -{ - struct snd_sof_widget *swidget; - struct snd_sof_pcm *spcm; - int dir, ret; - - /* - * free all PCMs and their associated DAPM widgets if their connected DAPM widget - * list is not NULL. This should only be true for paused streams at this point. - * This is equivalent to the handling of FE DAI suspend trigger for running streams. - */ - list_for_each_entry(spcm, &sdev->pcm_list, list) - for_each_pcm_streams(dir) { - struct snd_pcm_substream *substream = spcm->stream[dir].substream; - - if (!substream || !substream->runtime) - continue; - - if (spcm->stream[dir].list) { - ret = sof_pcm_stream_free(sdev, substream, spcm, dir, true); - if (ret < 0) - return ret; - } - } - - /* - * free any left over DAI widgets. This is equivalent to the handling of suspend trigger - * for the BE DAI for running streams. - */ - list_for_each_entry(swidget, &sdev->widget_list, list) - if (WIDGET_IS_DAI(swidget->id) && swidget->use_count == 1) { - ret = sof_widget_free(sdev, swidget); - if (ret < 0) - return ret; - } - - return 0; -} - -/* - * For older firmware, this function doesn't free widgets for static pipelines during suspend. - * It only resets use_count for all widgets. - */ -int sof_tear_down_pipelines(struct snd_sof_dev *sdev, bool verify) -{ - struct sof_ipc_fw_version *v = &sdev->fw_ready.version; - 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 running streams are suspended and during - * topology loading the sound card unavailable to open PCMs. - */ - list_for_each_entry(swidget, &sdev->widget_list, list) { - if (swidget->dynamic_pipeline_widget) - continue; - - /* Do not free widgets for static pipelines with FW ABI older than 3.19 */ - if (!verify && !swidget->dynamic_pipeline_widget && - v->abi_version < SOF_ABI_VER(3, 19, 0)) { - swidget->use_count = 0; - swidget->complete = 0; - continue; - } - - ret = sof_widget_free(sdev, swidget); - if (ret < 0) - return ret; - } - - /* - * Tear down all pipelines associated with PCMs that did not get suspended - * and unset the prepare flag so that they can be set up again during resume. - * Skip this step for older firmware. - */ - if (!verify && v->abi_version >= SOF_ABI_VER(3, 19, 0)) { - ret = sof_tear_down_left_over_pipelines(sdev); - if (ret < 0) { - dev_err(sdev->dev, "failed to tear down paused pipelines\n"); - return ret; - } - } - - list_for_each_entry(sroute, &sdev->route_list, list) - sroute->setup = false; - - return 0; -} - /* * Generic object lookup APIs. */ diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index 777da321e2aa..b9a9956b5baa 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -117,6 +117,8 @@ struct sof_ipc_tplg_widget_ops { * @widget_setup: Function pointer for setting up setup in the DSP * @widget_free: Function pointer for freeing widget in the DSP * @dai_config: Function pointer for sending DAI config IPC to the DSP + * @set_up_all_pipelines: Function pointer for setting up all topology pipelines + * @tear_down_all_pipelines: Function pointer for tearing down all topology pipelines */ struct sof_ipc_tplg_ops { const struct sof_ipc_tplg_widget_ops *widget; @@ -130,6 +132,8 @@ struct sof_ipc_tplg_ops { int (*widget_free)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget); int (*dai_config)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, unsigned int flags, struct snd_sof_dai_config_data *data); + int (*set_up_all_pipelines)(struct snd_sof_dev *sdev, bool verify); + int (*tear_down_all_pipelines)(struct snd_sof_dev *sdev, bool verify); };
/** struct snd_sof_tuple - Tuple info @@ -429,8 +433,6 @@ int snd_sof_ipc_set_get_comp_data(struct snd_sof_control *scontrol, bool set); int sof_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params);
/* PM */ -int sof_set_up_pipelines(struct snd_sof_dev *sdev, bool verify); -int sof_tear_down_pipelines(struct snd_sof_dev *sdev, 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); diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index 367fbe2d5b31..9e6545dd415c 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -1797,29 +1797,15 @@ static int sof_route_load(struct snd_soc_component *scomp, int index, sink_swidget->id == snd_soc_dapm_output) goto err;
- /* - * For virtual routes, both sink and source are not - * buffer. Since only buffer linked to component is supported by - * FW, others are reported as error, add check in route function, - * do not send it to FW when both source and sink are not buffer - */ - if (source_swidget->id != snd_soc_dapm_buffer && - sink_swidget->id != snd_soc_dapm_buffer) { - dev_dbg(scomp->dev, "warning: neither Linked source component %s nor sink component %s is of buffer type, ignoring link\n", - route->source, route->sink); - goto err; - } else { - sroute->route = route; - dobj->private = sroute; - sroute->src_widget = source_swidget; - sroute->sink_widget = sink_swidget; - - /* add route to route list */ - list_add(&sroute->list, &sdev->route_list); + sroute->route = route; + dobj->private = sroute; + sroute->src_widget = source_swidget; + sroute->sink_widget = sink_swidget;
- return 0; - } + /* add route to route list */ + list_add(&sroute->list, &sdev->route_list);
+ return 0; err: kfree(sroute); return ret; @@ -1918,21 +1904,28 @@ static int sof_complete(struct snd_soc_component *scomp)
/* verify topology components loading including dynamic pipelines */ if (sof_debug_check_flag(SOF_DBG_VERIFY_TPLG)) { - ret = sof_set_up_pipelines(sdev, true); - if (ret < 0) { - dev_err(sdev->dev, "error: topology verification failed %d\n", ret); - return ret; - } + if (ipc_tplg_ops->set_up_all_pipelines && ipc_tplg_ops->tear_down_all_pipelines) { + ret = ipc_tplg_ops->set_up_all_pipelines(sdev, true); + if (ret < 0) { + dev_err(sdev->dev, "Failed to set up all topology pipelines: %d\n", + ret); + return ret; + }
- ret = sof_tear_down_pipelines(sdev, true); - if (ret < 0) { - dev_err(sdev->dev, "error: topology tear down pipelines failed %d\n", ret); - return ret; + ret = ipc_tplg_ops->tear_down_all_pipelines(sdev, true); + if (ret < 0) { + dev_err(sdev->dev, "Failed to tear down topology pipelines: %d\n", + ret); + return ret; + } } }
/* set up static pipelines */ - return sof_set_up_pipelines(sdev, false); + if (ipc_tplg_ops->set_up_all_pipelines) + return ipc_tplg_ops->set_up_all_pipelines(sdev, false); + + return 0; }
/* manifest - optional to inform component of manifest */
This will help make the code for getting the mclk and bclk IPC specific. Add the implementation for IPC3 as well.
Signed-off-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Reviewed-by: Rander Wang rander.wang@intel.com Reviewed-by: Pierre-Louis Bossart pierre-louis.bossart@linux.intel.com Reviewed-by: Bard Liao yung-chuan.liao@linux.intel.com --- sound/soc/sof/ipc3-topology.c | 29 +++++++++++++++++++++++++++++ sound/soc/sof/sof-audio.c | 31 +++++++------------------------ sound/soc/sof/sof-audio.h | 6 ++++++ 3 files changed, 42 insertions(+), 24 deletions(-)
diff --git a/sound/soc/sof/ipc3-topology.c b/sound/soc/sof/ipc3-topology.c index 3ac65dacc7b9..2f8450a8c0a1 100644 --- a/sound/soc/sof/ipc3-topology.c +++ b/sound/soc/sof/ipc3-topology.c @@ -2252,6 +2252,34 @@ static int sof_ipc3_tear_down_all_pipelines(struct snd_sof_dev *sdev, bool verif return 0; }
+static int sof_ipc3_dai_get_clk(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, int clk_type) +{ + struct sof_dai_private_data *private = dai->private; + + if (!private || !private->dai_config) + return 0; + + switch (private->dai_config->type) { + case SOF_DAI_INTEL_SSP: + switch (clk_type) { + case SOF_DAI_CLK_INTEL_SSP_MCLK: + return private->dai_config->ssp.mclk_rate; + case SOF_DAI_CLK_INTEL_SSP_BCLK: + return private->dai_config->ssp.bclk_rate; + default: + break; + } + dev_err(sdev->dev, "fail to get SSP clk %d rate\n", clk_type); + break; + default: + /* not yet implemented for platforms other than the above */ + dev_err(sdev->dev, "DAI type %d not supported yet!\n", private->dai_config->type); + break; + } + + return -EINVAL; +} + /* token list for each topology object */ static enum sof_tokens host_token_list[] = { SOF_CORE_TOKENS, @@ -2359,6 +2387,7 @@ const struct sof_ipc_tplg_ops ipc3_tplg_ops = { .widget_free = sof_ipc3_widget_free, .widget_setup = sof_ipc3_widget_setup, .dai_config = sof_ipc3_dai_config, + .dai_get_clk = sof_ipc3_dai_get_clk, .set_up_all_pipelines = sof_ipc3_set_up_all_pipelines, .tear_down_all_pipelines = sof_ipc3_tear_down_all_pipelines, }; diff --git a/sound/soc/sof/sof-audio.c b/sound/soc/sof/sof-audio.c index b4ee65cf9841..b2f009a0c5b7 100644 --- a/sound/soc/sof/sof-audio.c +++ b/sound/soc/sof/sof-audio.c @@ -628,40 +628,23 @@ struct snd_sof_dai *snd_sof_find_dai(struct snd_soc_component *scomp, return NULL; }
-#define SOF_DAI_CLK_INTEL_SSP_MCLK 0 -#define SOF_DAI_CLK_INTEL_SSP_BCLK 1 - static int sof_dai_get_clk(struct snd_soc_pcm_runtime *rtd, int clk_type) { struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME); struct snd_sof_dai *dai = snd_sof_find_dai(component, (char *)rtd->dai_link->name); - struct sof_dai_private_data *private = dai->private; + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + const struct sof_ipc_tplg_ops *tplg_ops = sdev->ipc->ops->tplg;
/* use the tplg configured mclk if existed */ - if (!dai || !private || !private->dai_config) + if (!dai) return 0;
- switch (private->dai_config->type) { - case SOF_DAI_INTEL_SSP: - switch (clk_type) { - case SOF_DAI_CLK_INTEL_SSP_MCLK: - return private->dai_config->ssp.mclk_rate; - case SOF_DAI_CLK_INTEL_SSP_BCLK: - return private->dai_config->ssp.bclk_rate; - default: - dev_err(rtd->dev, "fail to get SSP clk %d rate\n", - clk_type); - return -EINVAL; - } - break; - default: - /* not yet implemented for platforms other than the above */ - dev_err(rtd->dev, "DAI type %d not supported yet!\n", - private->dai_config->type); - return -EINVAL; - } + if (tplg_ops->dai_get_clk) + return tplg_ops->dai_get_clk(sdev, dai, clk_type); + + return 0; }
/* diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h index b9a9956b5baa..7f15b3bc8196 100644 --- a/sound/soc/sof/sof-audio.h +++ b/sound/soc/sof/sof-audio.h @@ -30,6 +30,9 @@
#define WIDGET_IS_DAI(id) ((id) == snd_soc_dapm_dai_in || (id) == snd_soc_dapm_dai_out)
+#define SOF_DAI_CLK_INTEL_SSP_MCLK 0 +#define SOF_DAI_CLK_INTEL_SSP_BCLK 1 + /* * Volume fractional word length define to 16 sets * the volume linear gain value to use Qx.16 format @@ -39,6 +42,7 @@ struct snd_sof_widget; struct snd_sof_route; struct snd_sof_control; +struct snd_sof_dai;
struct snd_sof_dai_config_data { int dai_index; @@ -117,6 +121,7 @@ struct sof_ipc_tplg_widget_ops { * @widget_setup: Function pointer for setting up setup in the DSP * @widget_free: Function pointer for freeing widget in the DSP * @dai_config: Function pointer for sending DAI config IPC to the DSP + * @dai_get_clk: Function pointer for getting the DAI clock setting * @set_up_all_pipelines: Function pointer for setting up all topology pipelines * @tear_down_all_pipelines: Function pointer for tearing down all topology pipelines */ @@ -132,6 +137,7 @@ struct sof_ipc_tplg_ops { int (*widget_free)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget); int (*dai_config)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, unsigned int flags, struct snd_sof_dai_config_data *data); + int (*dai_get_clk)(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, int clk_type); int (*set_up_all_pipelines)(struct snd_sof_dev *sdev, bool verify); int (*tear_down_all_pipelines)(struct snd_sof_dev *sdev, bool verify); };
On Thu, 17 Mar 2022 10:50:25 -0700, Ranjani Sridharan wrote:
This series is a continuation to the SOF IPC abstraction work to support the new IPC version introduced in the SOF firmware. It makes the top-level control IO, PCM and PM code IPC-agnostic. Other than the first patch, the rest are purely for abstraction and include no changes in functionality.
Ranjani Sridharan (19): ASoC: SOF: set up scheduler widget before all other widgets in the pipeline ASoC: SOF: Make sof_widget_setup/free IPC agnostic ASoC: SOF: Make sof_suspend/resume IPC agnostic ASoC: SOF: Introduce IPC ops for kcontrol IO ASoC: SOF: Add IPC3 topology control ops ASoC: SOF: Add volume_get/put IPC3 ops ASoC: SOF: Add switch get/put IPC3 ops ASoC: SOF: Add enum_get/put control ops for IPC3 ASoC: SOF: Add bytes_get/put control IPC ops for IPC3 ASoC: SOF: Add bytes_ext control IPC ops for IPC3 ASoC: SOF: Introduce IPC-specific PCM ops ASoC: SOF: pcm: expose the sof_pcm_setup_connected_widgets() function ASoC: SOF: Introduce IPC3 PCM hw_free op ASoC: SOF: Define hw_params PCM op for IPC3 ASoC: SOF: Add trigger PCM op for IPC3 ASoC: SOF: Add dai_link_fixup PCM op for IPC3 ASoC: SOF: expose sof_route_setup() ASoC: SOF: topology: Add ops for setting up and tearing down pipelines ASoC: SOF: Add a new dai_get_clk topology IPC op
[...]
Applied to
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
Thanks!
[01/19] ASoC: SOF: set up scheduler widget before all other widgets in the pipeline commit: 40c2c63ac40d26bb0b8e17ada32e84541363f1b0 [02/19] ASoC: SOF: Make sof_widget_setup/free IPC agnostic commit: 051744b1bf0b13f63af5de3c296d04ab0cc6117c [03/19] ASoC: SOF: Make sof_suspend/resume IPC agnostic commit: 657774acd00f3d63ebae06e5d15a74e013cee0ed [04/19] ASoC: SOF: Introduce IPC ops for kcontrol IO commit: a0149a6bf0b4969a7f732528b2fb6ce32c309dfc [05/19] ASoC: SOF: Add IPC3 topology control ops commit: 10f461d79c2d1afb22344986cc1b4631169cf25e [06/19] ASoC: SOF: Add volume_get/put IPC3 ops commit: 838d04f3e232c3fb8c421959e8ff09e3a918011e [07/19] ASoC: SOF: Add switch get/put IPC3 ops commit: a6668746436824c46b54b3f7fd72523f05f089eb [08/19] ASoC: SOF: Add enum_get/put control ops for IPC3 commit: 049307aad2a355f7b44736eeb5795d6d4499fd12 [09/19] ASoC: SOF: Add bytes_get/put control IPC ops for IPC3 commit: 544ac8858f249950b4d99c68e538cdc07300528f [10/19] ASoC: SOF: Add bytes_ext control IPC ops for IPC3 commit: 67ec2a091630c28ea8d05db2bd7178a05b04b7e6 [11/19] ASoC: SOF: Introduce IPC-specific PCM ops commit: 967885ee45e48b669e0904a38f6aeb1a09b0d9a1 [12/19] ASoC: SOF: pcm: expose the sof_pcm_setup_connected_widgets() function commit: 442c7128219b1769af5685c5453b13711f6b84e2 [13/19] ASoC: SOF: Introduce IPC3 PCM hw_free op commit: 4123c24bd13caa8ff633d9e17fa2089d53b1f766 [14/19] ASoC: SOF: Define hw_params PCM op for IPC3 commit: 621fd48c8cc8d77101a3ac69f7f058d3f8afdbcc [15/19] ASoC: SOF: Add trigger PCM op for IPC3 commit: beac3f4cb66fa05e902768ae75ea691c4a2c0911 [16/19] ASoC: SOF: Add dai_link_fixup PCM op for IPC3 commit: b243b437f4c46b09ec26fc02bea610ace4b45aa2 [17/19] ASoC: SOF: expose sof_route_setup() commit: 3816bbea644202fd0a8410e54dbc30bd93f3292c [18/19] ASoC: SOF: topology: Add ops for setting up and tearing down pipelines commit: 31cd6e469364c42c9c929750991c51e83a95e80b [19/19] ASoC: SOF: Add a new dai_get_clk topology IPC op commit: 85f7a8b6e1bea0ad494fb786a5dd7d9715a976d2
All being well this means that it will be integrated into the linux-next tree (usually sometime in the next 24 hours) and sent to Linus during the next merge window (or sooner if it is a bug fix), however if problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing and review of the tree, please engage with people reporting problems and send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they should be sent as incremental updates against current git, existing patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying to this mail.
Thanks, Mark
participants (2)
-
Mark Brown
-
Ranjani Sridharan