[PATCH 0/3] ASoC: SOF: ipc4/intel: Support for ChainDMA
Hi,
On a platform when the DSP is in use, we cannot select individual links to use or not use the DSP, it is either all or none. On some audio endpoint, like HDMI/DP, it is preferred to not use any processing in DSP to reduce the latency and to allow bytestream pass-through (DTS, DD, etc)
IPC4 introduces a new type of end-to-end connection within the DSP which is using the host DMA and link DMA in a single buffer, working back-to-back, passing the received data without looking at it or trying to understand the format, content.
This mode reduces the latency and allows non PCM streams to be sent from userspace.
The feature is enabled per PCM bases, signalled in topology.
Regards, Peter --- Jyri Sarha (2): ASoC: SOF: ipc4: Add macros for chain-dma message bits ASoC: SOF: ipc4/intel: Add support for chained DMA
Ranjani Sridharan (1): ASoC: SOF: topology: Set pipeline widget before updating IPC structures
include/sound/sof/ipc4/header.h | 29 +++++++ include/uapi/sound/sof/tokens.h | 1 + sound/soc/sof/intel/hda-dai-ops.c | 18 ++++- sound/soc/sof/ipc4-pcm.c | 122 +++++++++++++++++++++++++++++- sound/soc/sof/ipc4-topology.c | 120 ++++++++++++++++++++++++++++- sound/soc/sof/ipc4-topology.h | 2 + sound/soc/sof/topology.c | 48 ++++++------ 7 files changed, 308 insertions(+), 32 deletions(-)
From: Ranjani Sridharan ranjani.sridharan@linux.intel.com
Set up the IPC structure for scheduler widgets and set the pipeline widget before updating the IPC structures for all widgets. This will be needed to look up pipeline information during IPC structure set up.
Signed-off-by: Ranjani Sridharan ranjani.sridharan@linux.intel.com Signed-off-by: Jyri Sarha jyri.sarha@intel.com Reviewed-by: Rander Wang rander.wang@intel.com Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com --- sound/soc/sof/topology.c | 48 ++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 24 deletions(-)
diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c index b642835e14df..bc8ca1e05b83 100644 --- a/sound/soc/sof/topology.c +++ b/sound/soc/sof/topology.c @@ -2126,7 +2126,6 @@ static int sof_complete(struct snd_soc_component *scomp) { struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); - struct snd_sof_widget *swidget, *comp_swidget; const struct sof_ipc_tplg_widget_ops *widget_ops; struct snd_sof_control *scontrol; struct snd_sof_pipeline *spipe; @@ -2145,37 +2144,38 @@ static int sof_complete(struct snd_soc_component *scomp) } }
- /* - * then update all widget IPC structures. If any of the ipc_setup callbacks fail, the - * topology will be removed and all widgets will be unloaded resulting in freeing all - * associated memories. - */ - list_for_each_entry(swidget, &sdev->widget_list, list) { - if (widget_ops && widget_ops[swidget->id].ipc_setup) { - ret = widget_ops[swidget->id].ipc_setup(swidget); + /* set up the IPC structures for the pipeline widgets */ + list_for_each_entry(spipe, &sdev->pipeline_list, list) { + struct snd_sof_widget *pipe_widget = spipe->pipe_widget; + struct snd_sof_widget *swidget; + + /* Update the scheduler widget's IPC structure */ + if (widget_ops && widget_ops[pipe_widget->id].ipc_setup) { + ret = widget_ops[pipe_widget->id].ipc_setup(pipe_widget); if (ret < 0) { dev_err(sdev->dev, "failed updating IPC struct for %s\n", - swidget->widget->name); + pipe_widget->widget->name); return ret; } } - }
- /* set the pipe_widget and apply the dynamic_pipeline_widget_flag */ - list_for_each_entry(spipe, &sdev->pipeline_list, list) { - struct snd_sof_widget *pipe_widget = spipe->pipe_widget; - - /* - * Apply the dynamic_pipeline_widget flag and set the pipe_widget field - * for all widgets that have the same pipeline ID as the scheduler widget. - * Skip the scheduler widgets as they have their pipeline set during widget_ready - */ - list_for_each_entry(comp_swidget, &sdev->widget_list, list) - if (comp_swidget->widget->id != snd_soc_dapm_scheduler && - comp_swidget->pipeline_id == pipe_widget->pipeline_id) { - ret = sof_set_widget_pipeline(sdev, spipe, comp_swidget); + /* set the pipeline and update the IPC structure for the non scheduler widgets */ + list_for_each_entry(swidget, &sdev->widget_list, list) + if (swidget->widget->id != snd_soc_dapm_scheduler && + swidget->pipeline_id == pipe_widget->pipeline_id) { + ret = sof_set_widget_pipeline(sdev, spipe, swidget); if (ret < 0) return ret; + + if (widget_ops && widget_ops[swidget->id].ipc_setup) { + ret = widget_ops[swidget->id].ipc_setup(swidget); + if (ret < 0) { + dev_err(sdev->dev, + "failed updating IPC struct for %s\n", + swidget->widget->name); + return ret; + } + } } }
From: Jyri Sarha jyri.sarha@intel.com
In the chained DMA mode, the firmware allocates buffers for the host and link DMA, and takes care of copying data between host- and link-DMA buffers in a low-latency thread. This is different to a regular pipeline, no processing is allowed, and the connection between host- and link DMA is handled with a dedicated IPC.
This patch exposes the macros needed to create the required IPC messages.
Signed-off-by: Jyri Sarha jyri.sarha@intel.com Reviewed-by: Rander Wang rander.wang@intel.com Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com --- include/sound/sof/ipc4/header.h | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+)
diff --git a/include/sound/sof/ipc4/header.h b/include/sound/sof/ipc4/header.h index 49ff1558a171..78568abe2673 100644 --- a/include/sound/sof/ipc4/header.h +++ b/include/sound/sof/ipc4/header.h @@ -196,6 +196,35 @@ enum sof_ipc4_pipeline_state { #define SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID_SHIFT 16 #define SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID(x) ((x) << SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID_SHIFT)
+/* chain dma ipc message */ +#define SOF_IPC4_GLB_CHAIN_DMA_HOST_ID_SHIFT 0 +#define SOF_IPC4_GLB_CHAIN_DMA_HOST_ID_MASK GENMASK(4, 0) +#define SOF_IPC4_GLB_CHAIN_DMA_HOST_ID(x) (((x) << SOF_IPC4_GLB_CHAIN_DMA_HOST_ID_SHIFT) & \ + SOF_IPC4_GLB_CHAIN_DMA_HOST_ID_MASK) + +#define SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_SHIFT 8 +#define SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_MASK GENMASK(12, 8) +#define SOF_IPC4_GLB_CHAIN_DMA_LINK_ID(x) (((x) << SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_SHIFT) & \ + SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_MASK) + +#define SOF_IPC4_GLB_CHAIN_DMA_ALLOCATE_SHIFT 16 +#define SOF_IPC4_GLB_CHAIN_DMA_ALLOCATE_MASK BIT(16) +#define SOF_IPC4_GLB_CHAIN_DMA_ALLOCATE(x) (((x) & 1) << SOF_IPC4_GLB_CHAIN_DMA_ALLOCATE_SHIFT) + +#define SOF_IPC4_GLB_CHAIN_DMA_ENABLE_SHIFT 17 +#define SOF_IPC4_GLB_CHAIN_DMA_ENABLE_MASK BIT(17) +#define SOF_IPC4_GLB_CHAIN_DMA_ENABLE(x) (((x) & 1) << SOF_IPC4_GLB_CHAIN_DMA_ENABLE_SHIFT) + +#define SOF_IPC4_GLB_CHAIN_DMA_SCS_SHIFT 18 +#define SOF_IPC4_GLB_CHAIN_DMA_SCS_MASK BIT(18) +#define SOF_IPC4_GLB_CHAIN_DMA_SCS(x) (((x) & 1) << SOF_IPC4_GLB_CHAIN_DMA_SCS_SHIFT) + +#define SOF_IPC4_GLB_EXT_CHAIN_DMA_FIFO_SIZE_SHIFT 0 +#define SOF_IPC4_GLB_EXT_CHAIN_DMA_FIFO_SIZE_MASK GENMASK(24, 0) +#define SOF_IPC4_GLB_EXT_CHAIN_DMA_FIFO_SIZE(x) (((x) << \ + SOF_IPC4_GLB_EXT_CHAIN_DMA_FIFO_SIZE_SHIFT) & \ + SOF_IPC4_GLB_EXT_CHAIN_DMA_FIFO_SIZE_MASK) + enum sof_ipc4_channel_config { /* one channel only. */ SOF_IPC4_CHANNEL_CONFIG_MONO,
From: Jyri Sarha jyri.sarha@intel.com
Add logic for setting up and tearing down chained DMA connections.
Since pipelines are not used, all the logic to set the pipeline states can be bypassed, with only the DMA programming sequences remaining. In addition the same format needs to be used for host- and link-DMA, without the usual fixup to use the S32_LE format on the link.
Note however that for convenience and compatibility with existing definitions, the topology relies on the concept of pipelines with a 'USE_CHAIN_DMA' token indicating that all the logic shall be bypassed.
Unlike 'normal' ALSA sequences, the chain DMA is not programmed in hw_params/hw_free. The IPC message to set-up and tear-down chained DMA are sent in sof_ipc4_trigger_pipelines(), but the contents prepared earlier.
Chained DMA is only supported by the Intel HDA DAI for now, and only S16_LE and S32_LE formats are supported for now.
Signed-off-by: Jyri Sarha jyri.sarha@intel.com Reviewed-by: Rander Wang rander.wang@intel.com Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com --- include/uapi/sound/sof/tokens.h | 1 + sound/soc/sof/intel/hda-dai-ops.c | 18 ++++- sound/soc/sof/ipc4-pcm.c | 122 +++++++++++++++++++++++++++++- sound/soc/sof/ipc4-topology.c | 120 ++++++++++++++++++++++++++++- sound/soc/sof/ipc4-topology.h | 2 + 5 files changed, 255 insertions(+), 8 deletions(-)
diff --git a/include/uapi/sound/sof/tokens.h b/include/uapi/sound/sof/tokens.h index bd02842124f9..bbc37877aaff 100644 --- a/include/uapi/sound/sof/tokens.h +++ b/include/uapi/sound/sof/tokens.h @@ -54,6 +54,7 @@ #define SOF_TKN_SCHED_DYNAMIC_PIPELINE 206 #define SOF_TKN_SCHED_LP_MODE 207 #define SOF_TKN_SCHED_MEM_USAGE 208 +#define SOF_TKN_SCHED_USE_CHAIN_DMA 209
/* volume */ #define SOF_TKN_VOLUME_RAMP_STEP_TYPE 250 diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c index be109f33715f..de48f13259f1 100644 --- a/sound/soc/sof/intel/hda-dai-ops.c +++ b/sound/soc/sof/intel/hda-dai-ops.c @@ -277,6 +277,15 @@ static const struct hda_dai_widget_dma_ops hda_ipc4_dma_ops = { .post_trigger = hda_ipc4_post_trigger };
+static const struct hda_dai_widget_dma_ops hda_ipc4_chain_dma_ops = { + .get_hext_stream = hda_get_hext_stream, + .assign_hext_stream = hda_assign_hext_stream, + .release_hext_stream = hda_release_hext_stream, + .setup_hext_stream = hda_setup_hext_stream, + .reset_hext_stream = hda_reset_hext_stream, + .trigger = hda_trigger, +}; + static int hda_ipc3_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, struct snd_pcm_substream *substream, int cmd) { @@ -331,8 +340,15 @@ hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidg { struct sof_ipc4_copier *ipc4_copier = sdai->private;
- if (ipc4_copier->dai_type == SOF_DAI_INTEL_HDA) + if (ipc4_copier->dai_type == SOF_DAI_INTEL_HDA) { + struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; + struct sof_ipc4_pipeline *pipeline = pipe_widget->private; + + if (pipeline->use_chain_dma) + return &hda_ipc4_chain_dma_ops; + return &hda_ipc4_dma_ops; + } break; } default: diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index 4598057b7f28..db64200ba1e5 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -193,6 +193,88 @@ sof_ipc4_update_pipeline_state(struct snd_sof_dev *sdev, int state, int cmd, * prepare ioctl before the START trigger. */
+/* + * Chained DMA is a special case where there is no processing on + * DSP. The samples are just moved over by host side DMA to a single + * buffer on DSP and directly from there to link DMA. However, the + * model on SOF driver has two notional pipelines, one at host DAI, + * and another at link DAI. They both shall have the use_chain_dma + * attribute. + */ + +static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev, + struct snd_sof_pcm_stream_pipeline_list *pipeline_list, + int state, int cmd) +{ + bool allocate, enable, set_fifo_size; + struct sof_ipc4_msg msg = {{ 0 }}; + int i; + + switch (state) { + case SOF_IPC4_PIPE_RUNNING: /* Allocate and start chained dma */ + allocate = true; + enable = true; + /* + * SOF assumes creation of a new stream from the presence of fifo_size + * in the message, so we must leave it out in pause release case. + */ + if (cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE) + set_fifo_size = false; + else + set_fifo_size = true; + break; + case SOF_IPC4_PIPE_PAUSED: /* Disable chained DMA. */ + allocate = true; + enable = false; + set_fifo_size = false; + break; + case SOF_IPC4_PIPE_RESET: /* Disable and free chained DMA. */ + allocate = false; + enable = false; + set_fifo_size = false; + break; + default: + dev_err(sdev->dev, "Unexpected state %d", state); + return -EINVAL; + } + + msg.primary = SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_CHAIN_DMA); + msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); + msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG); + + /* + * To set-up the DMA chain, the host DMA ID and SCS setting + * are retrieved from the host pipeline configuration. Likewise + * the link DMA ID and fifo_size are retrieved from the link + * pipeline configuration. + */ + for (i = 0; i < pipeline_list->count; i++) { + struct snd_sof_pipeline *spipe = pipeline_list->pipelines[i]; + struct snd_sof_widget *pipe_widget = spipe->pipe_widget; + struct sof_ipc4_pipeline *pipeline = pipe_widget->private; + + if (!pipeline->use_chain_dma) { + dev_err(sdev->dev, + "All pipelines in chained DMA stream should have use_chain_dma attribute set."); + return -EINVAL; + } + + msg.primary |= pipeline->msg.primary; + + /* Add fifo_size (actually DMA buffer size) field to the message */ + if (set_fifo_size) + msg.extension |= pipeline->msg.extension; + } + + if (allocate) + msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_ALLOCATE_MASK; + + if (enable) + msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_ENABLE_MASK; + + return sof_ipc_tx_message(sdev->ipc, &msg, 0, NULL, 0); +} + static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, struct snd_pcm_substream *substream, int state, int cmd) { @@ -201,6 +283,8 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, struct snd_sof_pcm_stream_pipeline_list *pipeline_list; struct sof_ipc4_fw_data *ipc4_data = sdev->private; struct ipc4_pipeline_set_state_data *trigger_list; + struct snd_sof_widget *pipe_widget; + struct sof_ipc4_pipeline *pipeline; struct snd_sof_pipeline *spipe; struct snd_sof_pcm *spcm; int ret; @@ -218,6 +302,17 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, if (!pipeline_list->pipelines || !pipeline_list->count) return 0;
+ spipe = pipeline_list->pipelines[0]; + pipe_widget = spipe->pipe_widget; + pipeline = pipe_widget->private; + + /* + * If use_chain_dma attribute is set we proceed to chained DMA + * trigger function that handles the rest for the substream. + */ + if (pipeline->use_chain_dma) + return sof_ipc4_chain_dma_trigger(sdev, pipeline_list, state, cmd); + /* allocate memory for the pipeline data */ trigger_list = kzalloc(struct_size(trigger_list, pipeline_ids, pipeline_list->count), GFP_KERNEL); @@ -422,8 +517,10 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, 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, rtd->dai_link->name); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); struct sof_ipc4_copier *ipc4_copier; - int ret; + bool use_chain_dma = false; + int dir;
if (!dai) { dev_err(component->dev, "%s: No DAI found with name %s\n", __func__, @@ -438,9 +535,26 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, return -EINVAL; }
- ret = sof_ipc4_pcm_dai_link_fixup_rate(sdev, params, ipc4_copier); - if (ret) - return ret; + for_each_pcm_streams(dir) { + struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, dir); + + if (w) { + struct snd_sof_widget *swidget = w->dobj.private; + struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; + struct sof_ipc4_pipeline *pipeline = pipe_widget->private; + + if (pipeline->use_chain_dma) + use_chain_dma = true; + } + } + + /* Chain DMA does not use copiers, so no fixup needed */ + if (!use_chain_dma) { + int ret = sof_ipc4_pcm_dai_link_fixup_rate(sdev, params, ipc4_copier); + + if (ret) + return ret; + }
switch (ipc4_copier->dai_type) { case SOF_DAI_INTEL_SSP: diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 3a4a3267017b..f1e1aed94da4 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -19,6 +19,7 @@
#define SOF_IPC4_GAIN_PARAM_ID 0 #define SOF_IPC4_TPLG_ABI_SIZE 6 +#define SOF_IPC4_CHAIN_DMA_BUF_SIZE_MS 2
static DEFINE_IDA(alh_group_ida); static DEFINE_IDA(pipeline_ida); @@ -26,6 +27,8 @@ static DEFINE_IDA(pipeline_ida); static const struct sof_topology_token ipc4_sched_tokens[] = { {SOF_TKN_SCHED_LP_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, offsetof(struct sof_ipc4_pipeline, lp_mode)}, + {SOF_TKN_SCHED_USE_CHAIN_DMA, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16, + offsetof(struct sof_ipc4_pipeline, use_chain_dma)}, {SOF_TKN_SCHED_CORE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, offsetof(struct sof_ipc4_pipeline, core_id)}, }; @@ -475,6 +478,8 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget) struct snd_soc_component *scomp = swidget->scomp; struct snd_sof_dai *dai = swidget->private; struct sof_ipc4_copier *ipc4_copier; + struct snd_sof_widget *pipe_widget; + struct sof_ipc4_pipeline *pipeline; int node_type = 0; int ret;
@@ -512,6 +517,16 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget)
ipc4_copier->data.gtw_cfg.node_id = SOF_IPC4_NODE_TYPE(node_type);
+ pipe_widget = swidget->spipe->pipe_widget; + pipeline = pipe_widget->private; + if (pipeline->use_chain_dma && ipc4_copier->dai_type != SOF_DAI_INTEL_HDA) { + dev_err(scomp->dev, + "Bad DAI type '%d', Chained DMA is only supported by HDA DAIs (%d).\n", + ipc4_copier->dai_type, SOF_DAI_INTEL_HDA); + ret = -ENODEV; + goto free_available_fmt; + } + switch (ipc4_copier->dai_type) { case SOF_DAI_INTEL_ALH: { @@ -643,6 +658,12 @@ static int sof_ipc4_widget_setup_comp_pipeline(struct snd_sof_widget *swidget)
swidget->core = pipeline->core_id;
+ if (pipeline->use_chain_dma) { + dev_dbg(scomp->dev, "Set up chain DMA for %s\n", swidget->widget->name); + swidget->private = pipeline; + return 0; + } + /* parse one set of pipeline tokens */ ret = sof_update_ipc_object(scomp, swidget, SOF_PIPELINE_TOKENS, swidget->tuples, swidget->num_tuples, sizeof(*swidget), 1); @@ -1103,11 +1124,21 @@ static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget) pipeline->mem_usage = 0;
if (WIDGET_IS_AIF(swidget->id) || swidget->id == snd_soc_dapm_buffer) { + if (pipeline->use_chain_dma) { + pipeline->msg.primary = 0; + pipeline->msg.extension = 0; + } ipc4_copier = swidget->private; } else if (WIDGET_IS_DAI(swidget->id)) { struct snd_sof_dai *dai = swidget->private;
ipc4_copier = dai->private; + + if (pipeline->use_chain_dma) { + pipeline->msg.primary = 0; + pipeline->msg.extension = 0; + } + if (ipc4_copier->dai_type == SOF_DAI_INTEL_ALH) { struct sof_ipc4_copier_data *copier_data = &ipc4_copier->data; struct sof_ipc4_alh_configuration_blob *blob; @@ -1344,13 +1375,44 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, return ret; }
- pipe_widget = swidget->spipe->pipe_widget; - pipeline = pipe_widget->private; ipc4_copier = (struct sof_ipc4_copier *)swidget->private; gtw_attr = ipc4_copier->gtw_attr; copier_data = &ipc4_copier->data; available_fmt = &ipc4_copier->available_fmt;
+ pipe_widget = swidget->spipe->pipe_widget; + pipeline = pipe_widget->private; + + if (pipeline->use_chain_dma) { + u32 host_dma_id; + u32 fifo_size; + + host_dma_id = platform_params->stream_tag - 1; + pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_HOST_ID(host_dma_id); + + /* Set SCS bit for S16_LE format only */ + if (params_format(fe_params) == SNDRV_PCM_FORMAT_S16_LE) + pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_SCS_MASK; + + /* + * Despite its name the bitfield 'fifo_size' is used to define DMA buffer + * size. The expression calculates 2ms buffer size. + */ + fifo_size = DIV_ROUND_UP((SOF_IPC4_CHAIN_DMA_BUF_SIZE_MS * + params_rate(fe_params) * + params_channels(fe_params) * + params_physical_width(fe_params)), 8000); + pipeline->msg.extension |= SOF_IPC4_GLB_EXT_CHAIN_DMA_FIFO_SIZE(fifo_size); + + /* + * Chain DMA does not support stream timestamping, set node_id to invalid + * to skip the code in sof_ipc4_get_stream_start_offset(). + */ + copier_data->gtw_cfg.node_id = SOF_IPC4_INVALID_NODE_ID; + + return 0; + } + /* * Use the input_pin_fmts to match pcm params for playback and the output_pin_fmts * for capture. @@ -1375,6 +1437,12 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, case snd_soc_dapm_dai_in: case snd_soc_dapm_dai_out: { + struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; + struct sof_ipc4_pipeline *pipeline = pipe_widget->private; + + if (pipeline->use_chain_dma) + return 0; + dai = swidget->private;
ipc4_copier = (struct sof_ipc4_copier *)dai->private; @@ -1921,6 +1989,9 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget case snd_soc_dapm_scheduler: pipeline = swidget->private;
+ if (pipeline->use_chain_dma) + return 0; + dev_dbg(sdev->dev, "pipeline: %d memory pages: %d\n", swidget->pipeline_id, pipeline->mem_usage);
@@ -1943,6 +2014,10 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget { struct sof_ipc4_copier *ipc4_copier = swidget->private;
+ pipeline = pipe_widget->private; + if (pipeline->use_chain_dma) + return 0; + ipc_size = ipc4_copier->ipc_config_size; ipc_data = ipc4_copier->ipc_config_data;
@@ -1955,6 +2030,10 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget struct snd_sof_dai *dai = swidget->private; struct sof_ipc4_copier *ipc4_copier = dai->private;
+ pipeline = pipe_widget->private; + if (pipeline->use_chain_dma) + return 0; + ipc_size = ipc4_copier->ipc_config_size; ipc_data = ipc4_copier->ipc_config_data;
@@ -2066,6 +2145,9 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget struct sof_ipc4_msg msg = {{ 0 }}; u32 header;
+ if (pipeline->use_chain_dma) + return 0; + header = SOF_IPC4_GLB_PIPE_INSTANCE_ID(swidget->instance_id); header |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_DELETE_PIPELINE); header |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); @@ -2082,7 +2164,11 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget pipeline->state = SOF_IPC4_PIPE_UNINITIALIZED; ida_free(&pipeline_ida, swidget->instance_id); } else { - ida_free(&fw_module->m_ida, swidget->instance_id); + struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; + struct sof_ipc4_pipeline *pipeline = pipe_widget->private; + + if (!pipeline->use_chain_dma) + ida_free(&fw_module->m_ida, swidget->instance_id); }
mutex_unlock(&ipc4_data->pipeline_state_mutex); @@ -2234,12 +2320,27 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route * { struct snd_sof_widget *src_widget = sroute->src_widget; struct snd_sof_widget *sink_widget = sroute->sink_widget; + struct snd_sof_widget *src_pipe_widget = src_widget->spipe->pipe_widget; + struct snd_sof_widget *sink_pipe_widget = sink_widget->spipe->pipe_widget; struct sof_ipc4_fw_module *src_fw_module = src_widget->module_info; struct sof_ipc4_fw_module *sink_fw_module = sink_widget->module_info; + struct sof_ipc4_pipeline *src_pipeline = src_pipe_widget->private; + struct sof_ipc4_pipeline *sink_pipeline = sink_pipe_widget->private; struct sof_ipc4_msg msg = {{ 0 }}; u32 header, extension; int ret;
+ /* no route set up if chain DMA is used */ + if (src_pipeline->use_chain_dma || sink_pipeline->use_chain_dma) { + if (!src_pipeline->use_chain_dma || !sink_pipeline->use_chain_dma) { + dev_err(sdev->dev, + "use_chain_dma must be set for both src %s and sink %s pipelines\n", + src_widget->widget->name, sink_widget->widget->name); + return -EINVAL; + } + return 0; + } + sroute->src_queue_id = sof_ipc4_get_queue_id(src_widget, sink_widget, SOF_PIN_TYPE_OUTPUT); if (sroute->src_queue_id < 0) { @@ -2310,9 +2411,17 @@ static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *s struct sof_ipc4_fw_module *src_fw_module = src_widget->module_info; struct sof_ipc4_fw_module *sink_fw_module = sink_widget->module_info; struct sof_ipc4_msg msg = {{ 0 }}; + struct snd_sof_widget *src_pipe_widget = src_widget->spipe->pipe_widget; + struct snd_sof_widget *sink_pipe_widget = sink_widget->spipe->pipe_widget; + struct sof_ipc4_pipeline *src_pipeline = src_pipe_widget->private; + struct sof_ipc4_pipeline *sink_pipeline = sink_pipe_widget->private; u32 header, extension; int ret = 0;
+ /* no route is set up if chain DMA is used */ + if (src_pipeline->use_chain_dma || sink_pipeline->use_chain_dma) + return 0; + dev_dbg(sdev->dev, "unbind modules %s:%d -> %s:%d\n", src_widget->widget->name, sroute->src_queue_id, sink_widget->widget->name, sroute->dst_queue_id); @@ -2374,6 +2483,11 @@ static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *
switch (ipc4_copier->dai_type) { case SOF_DAI_INTEL_HDA: + if (pipeline->use_chain_dma) { + pipeline->msg.primary &= ~SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_MASK; + pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_LINK_ID(data->dai_data); + break; + } gtw_attr = ipc4_copier->gtw_attr; gtw_attr->lp_buffer_alloc = pipeline->lp_mode; pipeline->skip_during_fe_trigger = true; diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h index 015027b23588..cf007282867b 100644 --- a/sound/soc/sof/ipc4-topology.h +++ b/sound/soc/sof/ipc4-topology.h @@ -126,6 +126,7 @@ struct sof_ipc4_copier_config_set_sink_format { * @mem_usage: Memory usage * @core_id: Target core for the pipeline * @state: Pipeline state + * @use_chain_dma: flag to indicate if the firmware shall use chained DMA * @msg: message structure for pipeline * @skip_during_fe_trigger: skip triggering this pipeline during the FE DAI trigger */ @@ -135,6 +136,7 @@ struct sof_ipc4_pipeline { uint32_t mem_usage; uint32_t core_id; int state; + bool use_chain_dma; struct sof_ipc4_msg msg; bool skip_during_fe_trigger; };
On 21/03/2023 11:26, Peter Ujfalusi wrote:
From: Jyri Sarha jyri.sarha@intel.com
Add logic for setting up and tearing down chained DMA connections.
Since pipelines are not used, all the logic to set the pipeline states can be bypassed, with only the DMA programming sequences remaining. In addition the same format needs to be used for host- and link-DMA, without the usual fixup to use the S32_LE format on the link.
Note however that for convenience and compatibility with existing definitions, the topology relies on the concept of pipelines with a 'USE_CHAIN_DMA' token indicating that all the logic shall be bypassed.
Unlike 'normal' ALSA sequences, the chain DMA is not programmed in hw_params/hw_free. The IPC message to set-up and tear-down chained DMA are sent in sof_ipc4_trigger_pipelines(), but the contents prepared earlier.
Chained DMA is only supported by the Intel HDA DAI for now, and only S16_LE and S32_LE formats are supported for now.
Signed-off-by: Jyri Sarha jyri.sarha@intel.com Reviewed-by: Rander Wang rander.wang@intel.com Signed-off-by: Peter Ujfalusi peter.ujfalusi@linux.intel.com
include/uapi/sound/sof/tokens.h | 1 + sound/soc/sof/intel/hda-dai-ops.c | 18 ++++- sound/soc/sof/ipc4-pcm.c | 122 +++++++++++++++++++++++++++++- sound/soc/sof/ipc4-topology.c | 120 ++++++++++++++++++++++++++++- sound/soc/sof/ipc4-topology.h | 2 + 5 files changed, 255 insertions(+), 8 deletions(-)
diff --git a/include/uapi/sound/sof/tokens.h b/include/uapi/sound/sof/tokens.h index bd02842124f9..bbc37877aaff 100644 --- a/include/uapi/sound/sof/tokens.h +++ b/include/uapi/sound/sof/tokens.h @@ -54,6 +54,7 @@ #define SOF_TKN_SCHED_DYNAMIC_PIPELINE 206 #define SOF_TKN_SCHED_LP_MODE 207 #define SOF_TKN_SCHED_MEM_USAGE 208 +#define SOF_TKN_SCHED_USE_CHAIN_DMA 209
/* volume */ #define SOF_TKN_VOLUME_RAMP_STEP_TYPE 250 diff --git a/sound/soc/sof/intel/hda-dai-ops.c b/sound/soc/sof/intel/hda-dai-ops.c index be109f33715f..de48f13259f1 100644 --- a/sound/soc/sof/intel/hda-dai-ops.c +++ b/sound/soc/sof/intel/hda-dai-ops.c @@ -277,6 +277,15 @@ static const struct hda_dai_widget_dma_ops hda_ipc4_dma_ops = { .post_trigger = hda_ipc4_post_trigger };
+static const struct hda_dai_widget_dma_ops hda_ipc4_chain_dma_ops = {
- .get_hext_stream = hda_get_hext_stream,
- .assign_hext_stream = hda_assign_hext_stream,
- .release_hext_stream = hda_release_hext_stream,
- .setup_hext_stream = hda_setup_hext_stream,
- .reset_hext_stream = hda_reset_hext_stream,
- .trigger = hda_trigger,
+};
static int hda_ipc3_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, struct snd_pcm_substream *substream, int cmd) { @@ -331,8 +340,15 @@ hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidg { struct sof_ipc4_copier *ipc4_copier = sdai->private;
if (ipc4_copier->dai_type == SOF_DAI_INTEL_HDA)
if (ipc4_copier->dai_type == SOF_DAI_INTEL_HDA) {
struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
if (pipeline->use_chain_dma)
return &hda_ipc4_chain_dma_ops;
return &hda_ipc4_dma_ops;
break; } default:}
diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c index 4598057b7f28..db64200ba1e5 100644 --- a/sound/soc/sof/ipc4-pcm.c +++ b/sound/soc/sof/ipc4-pcm.c @@ -193,6 +193,88 @@ sof_ipc4_update_pipeline_state(struct snd_sof_dev *sdev, int state, int cmd,
- prepare ioctl before the START trigger.
*/
+/*
- Chained DMA is a special case where there is no processing on
- DSP. The samples are just moved over by host side DMA to a single
- buffer on DSP and directly from there to link DMA. However, the
- model on SOF driver has two notional pipelines, one at host DAI,
- and another at link DAI. They both shall have the use_chain_dma
- attribute.
- */
+static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev,
struct snd_sof_pcm_stream_pipeline_list *pipeline_list,
int state, int cmd)
+{
- bool allocate, enable, set_fifo_size;
- struct sof_ipc4_msg msg = {{ 0 }};
- int i;
- switch (state) {
- case SOF_IPC4_PIPE_RUNNING: /* Allocate and start chained dma */
allocate = true;
enable = true;
/*
* SOF assumes creation of a new stream from the presence of fifo_size
* in the message, so we must leave it out in pause release case.
*/
if (cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE)
set_fifo_size = false;
else
set_fifo_size = true;
break;
- case SOF_IPC4_PIPE_PAUSED: /* Disable chained DMA. */
allocate = true;
enable = false;
set_fifo_size = false;
break;
- case SOF_IPC4_PIPE_RESET: /* Disable and free chained DMA. */
allocate = false;
enable = false;
set_fifo_size = false;
break;
- default:
dev_err(sdev->dev, "Unexpected state %d", state);
return -EINVAL;
- }
- msg.primary = SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_CHAIN_DMA);
- msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
- msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG);
- /*
* To set-up the DMA chain, the host DMA ID and SCS setting
* are retrieved from the host pipeline configuration. Likewise
* the link DMA ID and fifo_size are retrieved from the link
* pipeline configuration.
*/
- for (i = 0; i < pipeline_list->count; i++) {
struct snd_sof_pipeline *spipe = pipeline_list->pipelines[i];
struct snd_sof_widget *pipe_widget = spipe->pipe_widget;
struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
if (!pipeline->use_chain_dma) {
dev_err(sdev->dev,
"All pipelines in chained DMA stream should have use_chain_dma attribute set.");
return -EINVAL;
}
msg.primary |= pipeline->msg.primary;
/* Add fifo_size (actually DMA buffer size) field to the message */
if (set_fifo_size)
msg.extension |= pipeline->msg.extension;
- }
- if (allocate)
msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_ALLOCATE_MASK;
- if (enable)
msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_ENABLE_MASK;
- return sof_ipc_tx_message(sdev->ipc, &msg, 0, NULL, 0);
+}
static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, struct snd_pcm_substream *substream, int state, int cmd) { @@ -201,6 +283,8 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, struct snd_sof_pcm_stream_pipeline_list *pipeline_list; struct sof_ipc4_fw_data *ipc4_data = sdev->private; struct ipc4_pipeline_set_state_data *trigger_list;
- struct snd_sof_widget *pipe_widget;
- struct sof_ipc4_pipeline *pipeline; struct snd_sof_pipeline *spipe; struct snd_sof_pcm *spcm; int ret;
@@ -218,6 +302,17 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, if (!pipeline_list->pipelines || !pipeline_list->count) return 0;
- spipe = pipeline_list->pipelines[0];
- pipe_widget = spipe->pipe_widget;
- pipeline = pipe_widget->private;
- /*
* If use_chain_dma attribute is set we proceed to chained DMA
* trigger function that handles the rest for the substream.
*/
- if (pipeline->use_chain_dma)
return sof_ipc4_chain_dma_trigger(sdev, pipeline_list, state, cmd);
- /* allocate memory for the pipeline data */ trigger_list = kzalloc(struct_size(trigger_list, pipeline_ids, pipeline_list->count), GFP_KERNEL);
@@ -422,8 +517,10 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, 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, rtd->dai_link->name); struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
- struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); struct sof_ipc4_copier *ipc4_copier;
- int ret;
bool use_chain_dma = false;
int dir;
if (!dai) { dev_err(component->dev, "%s: No DAI found with name %s\n", __func__,
@@ -438,9 +535,26 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd, return -EINVAL; }
- ret = sof_ipc4_pcm_dai_link_fixup_rate(sdev, params, ipc4_copier);
- if (ret)
return ret;
for_each_pcm_streams(dir) {
struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, dir);
if (w) {
struct snd_sof_widget *swidget = w->dobj.private;
struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
if (pipeline->use_chain_dma)
use_chain_dma = true;
}
}
/* Chain DMA does not use copiers, so no fixup needed */
if (!use_chain_dma) {
int ret = sof_ipc4_pcm_dai_link_fixup_rate(sdev, params, ipc4_copier);
if (ret)
return ret;
}
switch (ipc4_copier->dai_type) { case SOF_DAI_INTEL_SSP:
diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c index 3a4a3267017b..f1e1aed94da4 100644 --- a/sound/soc/sof/ipc4-topology.c +++ b/sound/soc/sof/ipc4-topology.c @@ -19,6 +19,7 @@
#define SOF_IPC4_GAIN_PARAM_ID 0 #define SOF_IPC4_TPLG_ABI_SIZE 6 +#define SOF_IPC4_CHAIN_DMA_BUF_SIZE_MS 2
static DEFINE_IDA(alh_group_ida); static DEFINE_IDA(pipeline_ida); @@ -26,6 +27,8 @@ static DEFINE_IDA(pipeline_ida); static const struct sof_topology_token ipc4_sched_tokens[] = { {SOF_TKN_SCHED_LP_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, offsetof(struct sof_ipc4_pipeline, lp_mode)},
- {SOF_TKN_SCHED_USE_CHAIN_DMA, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16,
{SOF_TKN_SCHED_CORE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, offsetof(struct sof_ipc4_pipeline, core_id)},offsetof(struct sof_ipc4_pipeline, use_chain_dma)},
}; @@ -475,6 +478,8 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget) struct snd_soc_component *scomp = swidget->scomp; struct snd_sof_dai *dai = swidget->private; struct sof_ipc4_copier *ipc4_copier;
- struct snd_sof_widget *pipe_widget;
- struct sof_ipc4_pipeline *pipeline; int node_type = 0; int ret;
@@ -512,6 +517,16 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget)
ipc4_copier->data.gtw_cfg.node_id = SOF_IPC4_NODE_TYPE(node_type);
- pipe_widget = swidget->spipe->pipe_widget;
- pipeline = pipe_widget->private;
- if (pipeline->use_chain_dma && ipc4_copier->dai_type != SOF_DAI_INTEL_HDA) {
dev_err(scomp->dev,
"Bad DAI type '%d', Chained DMA is only supported by HDA DAIs (%d).\n",
ipc4_copier->dai_type, SOF_DAI_INTEL_HDA);
ret = -ENODEV;
goto free_available_fmt;
- }
- switch (ipc4_copier->dai_type) { case SOF_DAI_INTEL_ALH: {
@@ -643,6 +658,12 @@ static int sof_ipc4_widget_setup_comp_pipeline(struct snd_sof_widget *swidget)
swidget->core = pipeline->core_id;
- if (pipeline->use_chain_dma) {
dev_dbg(scomp->dev, "Set up chain DMA for %s\n", swidget->widget->name);
swidget->private = pipeline;
return 0;
- }
- /* parse one set of pipeline tokens */ ret = sof_update_ipc_object(scomp, swidget, SOF_PIPELINE_TOKENS, swidget->tuples, swidget->num_tuples, sizeof(*swidget), 1);
@@ -1103,11 +1124,21 @@ static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget) pipeline->mem_usage = 0;
if (WIDGET_IS_AIF(swidget->id) || swidget->id == snd_soc_dapm_buffer) {
if (pipeline->use_chain_dma) {
pipeline->msg.primary = 0;
pipeline->msg.extension = 0;
}
ipc4_copier = swidget->private; } else if (WIDGET_IS_DAI(swidget->id)) { struct snd_sof_dai *dai = swidget->private;
ipc4_copier = dai->private;
if (pipeline->use_chain_dma) {
pipeline->msg.primary = 0;
pipeline->msg.extension = 0;
}
if (ipc4_copier->dai_type == SOF_DAI_INTEL_ALH) { struct sof_ipc4_copier_data *copier_data = &ipc4_copier->data; struct sof_ipc4_alh_configuration_blob *blob;
@@ -1344,13 +1375,44 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, return ret; }
pipe_widget = swidget->spipe->pipe_widget;
ipc4_copier = (struct sof_ipc4_copier *)swidget->private; gtw_attr = ipc4_copier->gtw_attr; copier_data = &ipc4_copier->data; available_fmt = &ipc4_copier->available_fmt;pipeline = pipe_widget->private;
pipe_widget = swidget->spipe->pipe_widget;
pipeline = pipe_widget->private;
if (pipeline->use_chain_dma) {
u32 host_dma_id;
u32 fifo_size;
host_dma_id = platform_params->stream_tag - 1;
pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_HOST_ID(host_dma_id);
/* Set SCS bit for S16_LE format only */
if (params_format(fe_params) == SNDRV_PCM_FORMAT_S16_LE)
pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_SCS_MASK;
/*
* Despite its name the bitfield 'fifo_size' is used to define DMA buffer
* size. The expression calculates 2ms buffer size.
*/
fifo_size = DIV_ROUND_UP((SOF_IPC4_CHAIN_DMA_BUF_SIZE_MS *
params_rate(fe_params) *
params_channels(fe_params) *
params_physical_width(fe_params)), 8000);
pipeline->msg.extension |= SOF_IPC4_GLB_EXT_CHAIN_DMA_FIFO_SIZE(fifo_size);
/*
* Chain DMA does not support stream timestamping, set node_id to invalid
* to skip the code in sof_ipc4_get_stream_start_offset().
*/
copier_data->gtw_cfg.node_id = SOF_IPC4_INVALID_NODE_ID;
return 0;
}
- /*
- Use the input_pin_fmts to match pcm params for playback and the output_pin_fmts
- for capture.
@@ -1375,6 +1437,12 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, case snd_soc_dapm_dai_in: case snd_soc_dapm_dai_out: {
struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
if (pipeline->use_chain_dma)
return 0;
dai = swidget->private;
ipc4_copier = (struct sof_ipc4_copier *)dai->private;
@@ -1921,6 +1989,9 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget case snd_soc_dapm_scheduler: pipeline = swidget->private;
if (pipeline->use_chain_dma)
return 0;
- dev_dbg(sdev->dev, "pipeline: %d memory pages: %d\n", swidget->pipeline_id, pipeline->mem_usage);
@@ -1943,6 +2014,10 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget { struct sof_ipc4_copier *ipc4_copier = swidget->private;
pipeline = pipe_widget->private;
if (pipeline->use_chain_dma)
return 0;
- ipc_size = ipc4_copier->ipc_config_size; ipc_data = ipc4_copier->ipc_config_data;
@@ -1955,6 +2030,10 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget struct snd_sof_dai *dai = swidget->private; struct sof_ipc4_copier *ipc4_copier = dai->private;
pipeline = pipe_widget->private;
if (pipeline->use_chain_dma)
return 0;
- ipc_size = ipc4_copier->ipc_config_size; ipc_data = ipc4_copier->ipc_config_data;
@@ -2066,6 +2145,9 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget struct sof_ipc4_msg msg = {{ 0 }}; u32 header;
if (pipeline->use_chain_dma)
return 0;
there should be a mutex_unlock(&ipc4_data->pipeline_state_mutex); before the return.
I will send a v2 asap.
- header = SOF_IPC4_GLB_PIPE_INSTANCE_ID(swidget->instance_id); header |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_DELETE_PIPELINE); header |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
@@ -2082,7 +2164,11 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget pipeline->state = SOF_IPC4_PIPE_UNINITIALIZED; ida_free(&pipeline_ida, swidget->instance_id); } else {
ida_free(&fw_module->m_ida, swidget->instance_id);
struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
if (!pipeline->use_chain_dma)
ida_free(&fw_module->m_ida, swidget->instance_id);
}
mutex_unlock(&ipc4_data->pipeline_state_mutex);
@@ -2234,12 +2320,27 @@ static int sof_ipc4_route_setup(struct snd_sof_dev *sdev, struct snd_sof_route * { struct snd_sof_widget *src_widget = sroute->src_widget; struct snd_sof_widget *sink_widget = sroute->sink_widget;
struct snd_sof_widget *src_pipe_widget = src_widget->spipe->pipe_widget;
struct snd_sof_widget *sink_pipe_widget = sink_widget->spipe->pipe_widget; struct sof_ipc4_fw_module *src_fw_module = src_widget->module_info; struct sof_ipc4_fw_module *sink_fw_module = sink_widget->module_info;
struct sof_ipc4_pipeline *src_pipeline = src_pipe_widget->private;
struct sof_ipc4_pipeline *sink_pipeline = sink_pipe_widget->private; struct sof_ipc4_msg msg = {{ 0 }}; u32 header, extension; int ret;
/* no route set up if chain DMA is used */
if (src_pipeline->use_chain_dma || sink_pipeline->use_chain_dma) {
if (!src_pipeline->use_chain_dma || !sink_pipeline->use_chain_dma) {
dev_err(sdev->dev,
"use_chain_dma must be set for both src %s and sink %s pipelines\n",
src_widget->widget->name, sink_widget->widget->name);
return -EINVAL;
}
return 0;
}
sroute->src_queue_id = sof_ipc4_get_queue_id(src_widget, sink_widget, SOF_PIN_TYPE_OUTPUT); if (sroute->src_queue_id < 0) {
@@ -2310,9 +2411,17 @@ static int sof_ipc4_route_free(struct snd_sof_dev *sdev, struct snd_sof_route *s struct sof_ipc4_fw_module *src_fw_module = src_widget->module_info; struct sof_ipc4_fw_module *sink_fw_module = sink_widget->module_info; struct sof_ipc4_msg msg = {{ 0 }};
struct snd_sof_widget *src_pipe_widget = src_widget->spipe->pipe_widget;
struct snd_sof_widget *sink_pipe_widget = sink_widget->spipe->pipe_widget;
struct sof_ipc4_pipeline *src_pipeline = src_pipe_widget->private;
struct sof_ipc4_pipeline *sink_pipeline = sink_pipe_widget->private; u32 header, extension; int ret = 0;
/* no route is set up if chain DMA is used */
if (src_pipeline->use_chain_dma || sink_pipeline->use_chain_dma)
return 0;
dev_dbg(sdev->dev, "unbind modules %s:%d -> %s:%d\n", src_widget->widget->name, sroute->src_queue_id, sink_widget->widget->name, sroute->dst_queue_id);
@@ -2374,6 +2483,11 @@ static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *
switch (ipc4_copier->dai_type) { case SOF_DAI_INTEL_HDA:
if (pipeline->use_chain_dma) {
pipeline->msg.primary &= ~SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_MASK;
pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_LINK_ID(data->dai_data);
break;
gtw_attr = ipc4_copier->gtw_attr; gtw_attr->lp_buffer_alloc = pipeline->lp_mode; pipeline->skip_during_fe_trigger = true;}
diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h index 015027b23588..cf007282867b 100644 --- a/sound/soc/sof/ipc4-topology.h +++ b/sound/soc/sof/ipc4-topology.h @@ -126,6 +126,7 @@ struct sof_ipc4_copier_config_set_sink_format {
- @mem_usage: Memory usage
- @core_id: Target core for the pipeline
- @state: Pipeline state
*/
- @use_chain_dma: flag to indicate if the firmware shall use chained DMA
- @msg: message structure for pipeline
- @skip_during_fe_trigger: skip triggering this pipeline during the FE DAI trigger
@@ -135,6 +136,7 @@ struct sof_ipc4_pipeline { uint32_t mem_usage; uint32_t core_id; int state;
- bool use_chain_dma; struct sof_ipc4_msg msg; bool skip_during_fe_trigger;
};
On Tue, 21 Mar 2023 11:26:51 +0200, Peter Ujfalusi wrote:
On a platform when the DSP is in use, we cannot select individual links to use or not use the DSP, it is either all or none. On some audio endpoint, like HDMI/DP, it is preferred to not use any processing in DSP to reduce the latency and to allow bytestream pass-through (DTS, DD, etc)
IPC4 introduces a new type of end-to-end connection within the DSP which is using the host DMA and link DMA in a single buffer, working back-to-back, passing the received data without looking at it or trying to understand the format, content.
[...]
Applied to
broonie/sound.git for-next
Thanks!
[1/3] ASoC: SOF: topology: Set pipeline widget before updating IPC structures commit: 3d3e223f09ed59f7a47d27cf4301b4d0d5c7fc3d [2/3] ASoC: SOF: ipc4: Add macros for chain-dma message bits commit: cb3cdef33136baceada86ba2a21ba30cd53a9087 [3/3] ASoC: SOF: ipc4/intel: Add support for chained DMA commit: ca5ce0caa67fa9eeecaa29d895c2e4c3151c159e
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
On Tue, 21 Mar 2023 11:26:51 +0200, Peter Ujfalusi wrote:
On a platform when the DSP is in use, we cannot select individual links to use or not use the DSP, it is either all or none. On some audio endpoint, like HDMI/DP, it is preferred to not use any processing in DSP to reduce the latency and to allow bytestream pass-through (DTS, DD, etc)
IPC4 introduces a new type of end-to-end connection within the DSP which is using the host DMA and link DMA in a single buffer, working back-to-back, passing the received data without looking at it or trying to understand the format, content.
[...]
Applied to
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
Thanks!
[1/3] ASoC: SOF: topology: Set pipeline widget before updating IPC structures commit: 3d3e223f09ed59f7a47d27cf4301b4d0d5c7fc3d [2/3] ASoC: SOF: ipc4: Add macros for chain-dma message bits commit: cb3cdef33136baceada86ba2a21ba30cd53a9087 [3/3] ASoC: SOF: ipc4/intel: Add support for chained DMA commit: ca5ce0caa67fa9eeecaa29d895c2e4c3151c159e
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
-
Peter Ujfalusi