Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ASoC: SOF: ipc4/intel: Support for ChainDMA

Merge series from Peter Ujfalusi <peter.ujfalusi@linux.intel.com>:

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.

+308 -32
+29
include/sound/sof/ipc4/header.h
··· 196 196 #define SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID_SHIFT 16 197 197 #define SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID(x) ((x) << SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID_SHIFT) 198 198 199 + /* chain dma ipc message */ 200 + #define SOF_IPC4_GLB_CHAIN_DMA_HOST_ID_SHIFT 0 201 + #define SOF_IPC4_GLB_CHAIN_DMA_HOST_ID_MASK GENMASK(4, 0) 202 + #define SOF_IPC4_GLB_CHAIN_DMA_HOST_ID(x) (((x) << SOF_IPC4_GLB_CHAIN_DMA_HOST_ID_SHIFT) & \ 203 + SOF_IPC4_GLB_CHAIN_DMA_HOST_ID_MASK) 204 + 205 + #define SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_SHIFT 8 206 + #define SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_MASK GENMASK(12, 8) 207 + #define SOF_IPC4_GLB_CHAIN_DMA_LINK_ID(x) (((x) << SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_SHIFT) & \ 208 + SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_MASK) 209 + 210 + #define SOF_IPC4_GLB_CHAIN_DMA_ALLOCATE_SHIFT 16 211 + #define SOF_IPC4_GLB_CHAIN_DMA_ALLOCATE_MASK BIT(16) 212 + #define SOF_IPC4_GLB_CHAIN_DMA_ALLOCATE(x) (((x) & 1) << SOF_IPC4_GLB_CHAIN_DMA_ALLOCATE_SHIFT) 213 + 214 + #define SOF_IPC4_GLB_CHAIN_DMA_ENABLE_SHIFT 17 215 + #define SOF_IPC4_GLB_CHAIN_DMA_ENABLE_MASK BIT(17) 216 + #define SOF_IPC4_GLB_CHAIN_DMA_ENABLE(x) (((x) & 1) << SOF_IPC4_GLB_CHAIN_DMA_ENABLE_SHIFT) 217 + 218 + #define SOF_IPC4_GLB_CHAIN_DMA_SCS_SHIFT 18 219 + #define SOF_IPC4_GLB_CHAIN_DMA_SCS_MASK BIT(18) 220 + #define SOF_IPC4_GLB_CHAIN_DMA_SCS(x) (((x) & 1) << SOF_IPC4_GLB_CHAIN_DMA_SCS_SHIFT) 221 + 222 + #define SOF_IPC4_GLB_EXT_CHAIN_DMA_FIFO_SIZE_SHIFT 0 223 + #define SOF_IPC4_GLB_EXT_CHAIN_DMA_FIFO_SIZE_MASK GENMASK(24, 0) 224 + #define SOF_IPC4_GLB_EXT_CHAIN_DMA_FIFO_SIZE(x) (((x) << \ 225 + SOF_IPC4_GLB_EXT_CHAIN_DMA_FIFO_SIZE_SHIFT) & \ 226 + SOF_IPC4_GLB_EXT_CHAIN_DMA_FIFO_SIZE_MASK) 227 + 199 228 enum sof_ipc4_channel_config { 200 229 /* one channel only. */ 201 230 SOF_IPC4_CHANNEL_CONFIG_MONO,
+1
include/uapi/sound/sof/tokens.h
··· 54 54 #define SOF_TKN_SCHED_DYNAMIC_PIPELINE 206 55 55 #define SOF_TKN_SCHED_LP_MODE 207 56 56 #define SOF_TKN_SCHED_MEM_USAGE 208 57 + #define SOF_TKN_SCHED_USE_CHAIN_DMA 209 57 58 58 59 /* volume */ 59 60 #define SOF_TKN_VOLUME_RAMP_STEP_TYPE 250
+17 -1
sound/soc/sof/intel/hda-dai-ops.c
··· 277 277 .post_trigger = hda_ipc4_post_trigger 278 278 }; 279 279 280 + static const struct hda_dai_widget_dma_ops hda_ipc4_chain_dma_ops = { 281 + .get_hext_stream = hda_get_hext_stream, 282 + .assign_hext_stream = hda_assign_hext_stream, 283 + .release_hext_stream = hda_release_hext_stream, 284 + .setup_hext_stream = hda_setup_hext_stream, 285 + .reset_hext_stream = hda_reset_hext_stream, 286 + .trigger = hda_trigger, 287 + }; 288 + 280 289 static int hda_ipc3_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, 281 290 struct snd_pcm_substream *substream, int cmd) 282 291 { ··· 340 331 { 341 332 struct sof_ipc4_copier *ipc4_copier = sdai->private; 342 333 343 - if (ipc4_copier->dai_type == SOF_DAI_INTEL_HDA) 334 + if (ipc4_copier->dai_type == SOF_DAI_INTEL_HDA) { 335 + struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; 336 + struct sof_ipc4_pipeline *pipeline = pipe_widget->private; 337 + 338 + if (pipeline->use_chain_dma) 339 + return &hda_ipc4_chain_dma_ops; 340 + 344 341 return &hda_ipc4_dma_ops; 342 + } 345 343 break; 346 344 } 347 345 default:
+118 -4
sound/soc/sof/ipc4-pcm.c
··· 193 193 * prepare ioctl before the START trigger. 194 194 */ 195 195 196 + /* 197 + * Chained DMA is a special case where there is no processing on 198 + * DSP. The samples are just moved over by host side DMA to a single 199 + * buffer on DSP and directly from there to link DMA. However, the 200 + * model on SOF driver has two notional pipelines, one at host DAI, 201 + * and another at link DAI. They both shall have the use_chain_dma 202 + * attribute. 203 + */ 204 + 205 + static int sof_ipc4_chain_dma_trigger(struct snd_sof_dev *sdev, 206 + struct snd_sof_pcm_stream_pipeline_list *pipeline_list, 207 + int state, int cmd) 208 + { 209 + bool allocate, enable, set_fifo_size; 210 + struct sof_ipc4_msg msg = {{ 0 }}; 211 + int i; 212 + 213 + switch (state) { 214 + case SOF_IPC4_PIPE_RUNNING: /* Allocate and start chained dma */ 215 + allocate = true; 216 + enable = true; 217 + /* 218 + * SOF assumes creation of a new stream from the presence of fifo_size 219 + * in the message, so we must leave it out in pause release case. 220 + */ 221 + if (cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE) 222 + set_fifo_size = false; 223 + else 224 + set_fifo_size = true; 225 + break; 226 + case SOF_IPC4_PIPE_PAUSED: /* Disable chained DMA. */ 227 + allocate = true; 228 + enable = false; 229 + set_fifo_size = false; 230 + break; 231 + case SOF_IPC4_PIPE_RESET: /* Disable and free chained DMA. */ 232 + allocate = false; 233 + enable = false; 234 + set_fifo_size = false; 235 + break; 236 + default: 237 + dev_err(sdev->dev, "Unexpected state %d", state); 238 + return -EINVAL; 239 + } 240 + 241 + msg.primary = SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_CHAIN_DMA); 242 + msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); 243 + msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG); 244 + 245 + /* 246 + * To set-up the DMA chain, the host DMA ID and SCS setting 247 + * are retrieved from the host pipeline configuration. Likewise 248 + * the link DMA ID and fifo_size are retrieved from the link 249 + * pipeline configuration. 250 + */ 251 + for (i = 0; i < pipeline_list->count; i++) { 252 + struct snd_sof_pipeline *spipe = pipeline_list->pipelines[i]; 253 + struct snd_sof_widget *pipe_widget = spipe->pipe_widget; 254 + struct sof_ipc4_pipeline *pipeline = pipe_widget->private; 255 + 256 + if (!pipeline->use_chain_dma) { 257 + dev_err(sdev->dev, 258 + "All pipelines in chained DMA stream should have use_chain_dma attribute set."); 259 + return -EINVAL; 260 + } 261 + 262 + msg.primary |= pipeline->msg.primary; 263 + 264 + /* Add fifo_size (actually DMA buffer size) field to the message */ 265 + if (set_fifo_size) 266 + msg.extension |= pipeline->msg.extension; 267 + } 268 + 269 + if (allocate) 270 + msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_ALLOCATE_MASK; 271 + 272 + if (enable) 273 + msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_ENABLE_MASK; 274 + 275 + return sof_ipc_tx_message(sdev->ipc, &msg, 0, NULL, 0); 276 + } 277 + 196 278 static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, 197 279 struct snd_pcm_substream *substream, int state, int cmd) 198 280 { ··· 283 201 struct snd_sof_pcm_stream_pipeline_list *pipeline_list; 284 202 struct sof_ipc4_fw_data *ipc4_data = sdev->private; 285 203 struct ipc4_pipeline_set_state_data *trigger_list; 204 + struct snd_sof_widget *pipe_widget; 205 + struct sof_ipc4_pipeline *pipeline; 286 206 struct snd_sof_pipeline *spipe; 287 207 struct snd_sof_pcm *spcm; 288 208 int ret; ··· 301 217 /* nothing to trigger if the list is empty */ 302 218 if (!pipeline_list->pipelines || !pipeline_list->count) 303 219 return 0; 220 + 221 + spipe = pipeline_list->pipelines[0]; 222 + pipe_widget = spipe->pipe_widget; 223 + pipeline = pipe_widget->private; 224 + 225 + /* 226 + * If use_chain_dma attribute is set we proceed to chained DMA 227 + * trigger function that handles the rest for the substream. 228 + */ 229 + if (pipeline->use_chain_dma) 230 + return sof_ipc4_chain_dma_trigger(sdev, pipeline_list, state, cmd); 304 231 305 232 /* allocate memory for the pipeline data */ 306 233 trigger_list = kzalloc(struct_size(trigger_list, pipeline_ids, pipeline_list->count), ··· 517 422 struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME); 518 423 struct snd_sof_dai *dai = snd_sof_find_dai(component, rtd->dai_link->name); 519 424 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); 425 + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); 520 426 struct sof_ipc4_copier *ipc4_copier; 521 - int ret; 427 + bool use_chain_dma = false; 428 + int dir; 522 429 523 430 if (!dai) { 524 431 dev_err(component->dev, "%s: No DAI found with name %s\n", __func__, ··· 535 438 return -EINVAL; 536 439 } 537 440 538 - ret = sof_ipc4_pcm_dai_link_fixup_rate(sdev, params, ipc4_copier); 539 - if (ret) 540 - return ret; 441 + for_each_pcm_streams(dir) { 442 + struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, dir); 443 + 444 + if (w) { 445 + struct snd_sof_widget *swidget = w->dobj.private; 446 + struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; 447 + struct sof_ipc4_pipeline *pipeline = pipe_widget->private; 448 + 449 + if (pipeline->use_chain_dma) 450 + use_chain_dma = true; 451 + } 452 + } 453 + 454 + /* Chain DMA does not use copiers, so no fixup needed */ 455 + if (!use_chain_dma) { 456 + int ret = sof_ipc4_pcm_dai_link_fixup_rate(sdev, params, ipc4_copier); 457 + 458 + if (ret) 459 + return ret; 460 + } 541 461 542 462 switch (ipc4_copier->dai_type) { 543 463 case SOF_DAI_INTEL_SSP:
+117 -3
sound/soc/sof/ipc4-topology.c
··· 19 19 20 20 #define SOF_IPC4_GAIN_PARAM_ID 0 21 21 #define SOF_IPC4_TPLG_ABI_SIZE 6 22 + #define SOF_IPC4_CHAIN_DMA_BUF_SIZE_MS 2 22 23 23 24 static DEFINE_IDA(alh_group_ida); 24 25 static DEFINE_IDA(pipeline_ida); ··· 27 26 static const struct sof_topology_token ipc4_sched_tokens[] = { 28 27 {SOF_TKN_SCHED_LP_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 29 28 offsetof(struct sof_ipc4_pipeline, lp_mode)}, 29 + {SOF_TKN_SCHED_USE_CHAIN_DMA, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16, 30 + offsetof(struct sof_ipc4_pipeline, use_chain_dma)}, 30 31 {SOF_TKN_SCHED_CORE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 31 32 offsetof(struct sof_ipc4_pipeline, core_id)}, 32 33 }; ··· 478 475 struct snd_soc_component *scomp = swidget->scomp; 479 476 struct snd_sof_dai *dai = swidget->private; 480 477 struct sof_ipc4_copier *ipc4_copier; 478 + struct snd_sof_widget *pipe_widget; 479 + struct sof_ipc4_pipeline *pipeline; 481 480 int node_type = 0; 482 481 int ret; 483 482 ··· 516 511 node_type, ipc4_copier->dai_type, ipc4_copier->dai_index); 517 512 518 513 ipc4_copier->data.gtw_cfg.node_id = SOF_IPC4_NODE_TYPE(node_type); 514 + 515 + pipe_widget = swidget->spipe->pipe_widget; 516 + pipeline = pipe_widget->private; 517 + if (pipeline->use_chain_dma && ipc4_copier->dai_type != SOF_DAI_INTEL_HDA) { 518 + dev_err(scomp->dev, 519 + "Bad DAI type '%d', Chained DMA is only supported by HDA DAIs (%d).\n", 520 + ipc4_copier->dai_type, SOF_DAI_INTEL_HDA); 521 + ret = -ENODEV; 522 + goto free_available_fmt; 523 + } 519 524 520 525 switch (ipc4_copier->dai_type) { 521 526 case SOF_DAI_INTEL_ALH: ··· 657 642 } 658 643 659 644 swidget->core = pipeline->core_id; 645 + 646 + if (pipeline->use_chain_dma) { 647 + dev_dbg(scomp->dev, "Set up chain DMA for %s\n", swidget->widget->name); 648 + swidget->private = pipeline; 649 + return 0; 650 + } 660 651 661 652 /* parse one set of pipeline tokens */ 662 653 ret = sof_update_ipc_object(scomp, swidget, SOF_PIPELINE_TOKENS, swidget->tuples, ··· 1124 1103 pipeline->mem_usage = 0; 1125 1104 1126 1105 if (WIDGET_IS_AIF(swidget->id) || swidget->id == snd_soc_dapm_buffer) { 1106 + if (pipeline->use_chain_dma) { 1107 + pipeline->msg.primary = 0; 1108 + pipeline->msg.extension = 0; 1109 + } 1127 1110 ipc4_copier = swidget->private; 1128 1111 } else if (WIDGET_IS_DAI(swidget->id)) { 1129 1112 struct snd_sof_dai *dai = swidget->private; 1130 1113 1131 1114 ipc4_copier = dai->private; 1115 + 1116 + if (pipeline->use_chain_dma) { 1117 + pipeline->msg.primary = 0; 1118 + pipeline->msg.extension = 0; 1119 + } 1120 + 1132 1121 if (ipc4_copier->dai_type == SOF_DAI_INTEL_ALH) { 1133 1122 struct sof_ipc4_copier_data *copier_data = &ipc4_copier->data; 1134 1123 struct sof_ipc4_alh_configuration_blob *blob; ··· 1375 1344 return ret; 1376 1345 } 1377 1346 1378 - pipe_widget = swidget->spipe->pipe_widget; 1379 - pipeline = pipe_widget->private; 1380 1347 ipc4_copier = (struct sof_ipc4_copier *)swidget->private; 1381 1348 gtw_attr = ipc4_copier->gtw_attr; 1382 1349 copier_data = &ipc4_copier->data; 1383 1350 available_fmt = &ipc4_copier->available_fmt; 1351 + 1352 + pipe_widget = swidget->spipe->pipe_widget; 1353 + pipeline = pipe_widget->private; 1354 + 1355 + if (pipeline->use_chain_dma) { 1356 + u32 host_dma_id; 1357 + u32 fifo_size; 1358 + 1359 + host_dma_id = platform_params->stream_tag - 1; 1360 + pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_HOST_ID(host_dma_id); 1361 + 1362 + /* Set SCS bit for S16_LE format only */ 1363 + if (params_format(fe_params) == SNDRV_PCM_FORMAT_S16_LE) 1364 + pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_SCS_MASK; 1365 + 1366 + /* 1367 + * Despite its name the bitfield 'fifo_size' is used to define DMA buffer 1368 + * size. The expression calculates 2ms buffer size. 1369 + */ 1370 + fifo_size = DIV_ROUND_UP((SOF_IPC4_CHAIN_DMA_BUF_SIZE_MS * 1371 + params_rate(fe_params) * 1372 + params_channels(fe_params) * 1373 + params_physical_width(fe_params)), 8000); 1374 + pipeline->msg.extension |= SOF_IPC4_GLB_EXT_CHAIN_DMA_FIFO_SIZE(fifo_size); 1375 + 1376 + /* 1377 + * Chain DMA does not support stream timestamping, set node_id to invalid 1378 + * to skip the code in sof_ipc4_get_stream_start_offset(). 1379 + */ 1380 + copier_data->gtw_cfg.node_id = SOF_IPC4_INVALID_NODE_ID; 1381 + 1382 + return 0; 1383 + } 1384 1384 1385 1385 /* 1386 1386 * Use the input_pin_fmts to match pcm params for playback and the output_pin_fmts ··· 1437 1375 case snd_soc_dapm_dai_in: 1438 1376 case snd_soc_dapm_dai_out: 1439 1377 { 1378 + struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; 1379 + struct sof_ipc4_pipeline *pipeline = pipe_widget->private; 1380 + 1381 + if (pipeline->use_chain_dma) 1382 + return 0; 1383 + 1440 1384 dai = swidget->private; 1441 1385 1442 1386 ipc4_copier = (struct sof_ipc4_copier *)dai->private; ··· 1989 1921 case snd_soc_dapm_scheduler: 1990 1922 pipeline = swidget->private; 1991 1923 1924 + if (pipeline->use_chain_dma) 1925 + return 0; 1926 + 1992 1927 dev_dbg(sdev->dev, "pipeline: %d memory pages: %d\n", swidget->pipeline_id, 1993 1928 pipeline->mem_usage); 1994 1929 ··· 2014 1943 { 2015 1944 struct sof_ipc4_copier *ipc4_copier = swidget->private; 2016 1945 1946 + pipeline = pipe_widget->private; 1947 + if (pipeline->use_chain_dma) 1948 + return 0; 1949 + 2017 1950 ipc_size = ipc4_copier->ipc_config_size; 2018 1951 ipc_data = ipc4_copier->ipc_config_data; 2019 1952 ··· 2029 1954 { 2030 1955 struct snd_sof_dai *dai = swidget->private; 2031 1956 struct sof_ipc4_copier *ipc4_copier = dai->private; 1957 + 1958 + pipeline = pipe_widget->private; 1959 + if (pipeline->use_chain_dma) 1960 + return 0; 2032 1961 2033 1962 ipc_size = ipc4_copier->ipc_config_size; 2034 1963 ipc_data = ipc4_copier->ipc_config_data; ··· 2145 2066 struct sof_ipc4_msg msg = {{ 0 }}; 2146 2067 u32 header; 2147 2068 2069 + if (pipeline->use_chain_dma) 2070 + return 0; 2071 + 2148 2072 header = SOF_IPC4_GLB_PIPE_INSTANCE_ID(swidget->instance_id); 2149 2073 header |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_DELETE_PIPELINE); 2150 2074 header |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); ··· 2164 2082 pipeline->state = SOF_IPC4_PIPE_UNINITIALIZED; 2165 2083 ida_free(&pipeline_ida, swidget->instance_id); 2166 2084 } else { 2167 - ida_free(&fw_module->m_ida, swidget->instance_id); 2085 + struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; 2086 + struct sof_ipc4_pipeline *pipeline = pipe_widget->private; 2087 + 2088 + if (!pipeline->use_chain_dma) 2089 + ida_free(&fw_module->m_ida, swidget->instance_id); 2168 2090 } 2169 2091 2170 2092 mutex_unlock(&ipc4_data->pipeline_state_mutex); ··· 2320 2234 { 2321 2235 struct snd_sof_widget *src_widget = sroute->src_widget; 2322 2236 struct snd_sof_widget *sink_widget = sroute->sink_widget; 2237 + struct snd_sof_widget *src_pipe_widget = src_widget->spipe->pipe_widget; 2238 + struct snd_sof_widget *sink_pipe_widget = sink_widget->spipe->pipe_widget; 2323 2239 struct sof_ipc4_fw_module *src_fw_module = src_widget->module_info; 2324 2240 struct sof_ipc4_fw_module *sink_fw_module = sink_widget->module_info; 2241 + struct sof_ipc4_pipeline *src_pipeline = src_pipe_widget->private; 2242 + struct sof_ipc4_pipeline *sink_pipeline = sink_pipe_widget->private; 2325 2243 struct sof_ipc4_msg msg = {{ 0 }}; 2326 2244 u32 header, extension; 2327 2245 int ret; 2246 + 2247 + /* no route set up if chain DMA is used */ 2248 + if (src_pipeline->use_chain_dma || sink_pipeline->use_chain_dma) { 2249 + if (!src_pipeline->use_chain_dma || !sink_pipeline->use_chain_dma) { 2250 + dev_err(sdev->dev, 2251 + "use_chain_dma must be set for both src %s and sink %s pipelines\n", 2252 + src_widget->widget->name, sink_widget->widget->name); 2253 + return -EINVAL; 2254 + } 2255 + return 0; 2256 + } 2328 2257 2329 2258 sroute->src_queue_id = sof_ipc4_get_queue_id(src_widget, sink_widget, 2330 2259 SOF_PIN_TYPE_OUTPUT); ··· 2411 2310 struct sof_ipc4_fw_module *src_fw_module = src_widget->module_info; 2412 2311 struct sof_ipc4_fw_module *sink_fw_module = sink_widget->module_info; 2413 2312 struct sof_ipc4_msg msg = {{ 0 }}; 2313 + struct snd_sof_widget *src_pipe_widget = src_widget->spipe->pipe_widget; 2314 + struct snd_sof_widget *sink_pipe_widget = sink_widget->spipe->pipe_widget; 2315 + struct sof_ipc4_pipeline *src_pipeline = src_pipe_widget->private; 2316 + struct sof_ipc4_pipeline *sink_pipeline = sink_pipe_widget->private; 2414 2317 u32 header, extension; 2415 2318 int ret = 0; 2319 + 2320 + /* no route is set up if chain DMA is used */ 2321 + if (src_pipeline->use_chain_dma || sink_pipeline->use_chain_dma) 2322 + return 0; 2416 2323 2417 2324 dev_dbg(sdev->dev, "unbind modules %s:%d -> %s:%d\n", 2418 2325 src_widget->widget->name, sroute->src_queue_id, ··· 2483 2374 2484 2375 switch (ipc4_copier->dai_type) { 2485 2376 case SOF_DAI_INTEL_HDA: 2377 + if (pipeline->use_chain_dma) { 2378 + pipeline->msg.primary &= ~SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_MASK; 2379 + pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_LINK_ID(data->dai_data); 2380 + break; 2381 + } 2486 2382 gtw_attr = ipc4_copier->gtw_attr; 2487 2383 gtw_attr->lp_buffer_alloc = pipeline->lp_mode; 2488 2384 pipeline->skip_during_fe_trigger = true;
+2
sound/soc/sof/ipc4-topology.h
··· 126 126 * @mem_usage: Memory usage 127 127 * @core_id: Target core for the pipeline 128 128 * @state: Pipeline state 129 + * @use_chain_dma: flag to indicate if the firmware shall use chained DMA 129 130 * @msg: message structure for pipeline 130 131 * @skip_during_fe_trigger: skip triggering this pipeline during the FE DAI trigger 131 132 */ ··· 136 135 uint32_t mem_usage; 137 136 uint32_t core_id; 138 137 int state; 138 + bool use_chain_dma; 139 139 struct sof_ipc4_msg msg; 140 140 bool skip_during_fe_trigger; 141 141 };
+24 -24
sound/soc/sof/topology.c
··· 2126 2126 { 2127 2127 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 2128 2128 const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); 2129 - struct snd_sof_widget *swidget, *comp_swidget; 2130 2129 const struct sof_ipc_tplg_widget_ops *widget_ops; 2131 2130 struct snd_sof_control *scontrol; 2132 2131 struct snd_sof_pipeline *spipe; ··· 2144 2145 } 2145 2146 } 2146 2147 2147 - /* 2148 - * then update all widget IPC structures. If any of the ipc_setup callbacks fail, the 2149 - * topology will be removed and all widgets will be unloaded resulting in freeing all 2150 - * associated memories. 2151 - */ 2152 - list_for_each_entry(swidget, &sdev->widget_list, list) { 2153 - if (widget_ops && widget_ops[swidget->id].ipc_setup) { 2154 - ret = widget_ops[swidget->id].ipc_setup(swidget); 2148 + /* set up the IPC structures for the pipeline widgets */ 2149 + list_for_each_entry(spipe, &sdev->pipeline_list, list) { 2150 + struct snd_sof_widget *pipe_widget = spipe->pipe_widget; 2151 + struct snd_sof_widget *swidget; 2152 + 2153 + /* Update the scheduler widget's IPC structure */ 2154 + if (widget_ops && widget_ops[pipe_widget->id].ipc_setup) { 2155 + ret = widget_ops[pipe_widget->id].ipc_setup(pipe_widget); 2155 2156 if (ret < 0) { 2156 2157 dev_err(sdev->dev, "failed updating IPC struct for %s\n", 2157 - swidget->widget->name); 2158 + pipe_widget->widget->name); 2158 2159 return ret; 2159 2160 } 2160 2161 } 2161 - } 2162 2162 2163 - /* set the pipe_widget and apply the dynamic_pipeline_widget_flag */ 2164 - list_for_each_entry(spipe, &sdev->pipeline_list, list) { 2165 - struct snd_sof_widget *pipe_widget = spipe->pipe_widget; 2166 - 2167 - /* 2168 - * Apply the dynamic_pipeline_widget flag and set the pipe_widget field 2169 - * for all widgets that have the same pipeline ID as the scheduler widget. 2170 - * Skip the scheduler widgets as they have their pipeline set during widget_ready 2171 - */ 2172 - list_for_each_entry(comp_swidget, &sdev->widget_list, list) 2173 - if (comp_swidget->widget->id != snd_soc_dapm_scheduler && 2174 - comp_swidget->pipeline_id == pipe_widget->pipeline_id) { 2175 - ret = sof_set_widget_pipeline(sdev, spipe, comp_swidget); 2163 + /* set the pipeline and update the IPC structure for the non scheduler widgets */ 2164 + list_for_each_entry(swidget, &sdev->widget_list, list) 2165 + if (swidget->widget->id != snd_soc_dapm_scheduler && 2166 + swidget->pipeline_id == pipe_widget->pipeline_id) { 2167 + ret = sof_set_widget_pipeline(sdev, spipe, swidget); 2176 2168 if (ret < 0) 2177 2169 return ret; 2170 + 2171 + if (widget_ops && widget_ops[swidget->id].ipc_setup) { 2172 + ret = widget_ops[swidget->id].ipc_setup(swidget); 2173 + if (ret < 0) { 2174 + dev_err(sdev->dev, 2175 + "failed updating IPC struct for %s\n", 2176 + swidget->widget->name); 2177 + return ret; 2178 + } 2179 + } 2178 2180 } 2179 2181 } 2180 2182