| @@ -19,6 +19,7 @@ · 18 unmodified lines |
| 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); |
| @@ -26,6 +27,8 @@ static DEFINE_IDA(pipeline_ida); · 1 unmodified line |
| 26 | 27 | static const struct sof_topology_token ipc4_sched_tokens[] = { |
| 27 | 28 | {SOF_TKN_SCHED_LP_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, |
| 28 | 29 | offsetof(struct sof_ipc4_pipeline, lp_mode)}, |
| 30 | + {SOF_TKN_SCHED_USE_CHAIN_DMA, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16, |
| 31 | + offsetof(struct sof_ipc4_pipeline, use_chain_dma)}, |
| 29 | 32 | {SOF_TKN_SCHED_CORE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, |
| 30 | 33 | offsetof(struct sof_ipc4_pipeline, core_id)}, |
| 31 | 34 | }; |
| @@ -475,6 +478,8 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget) · 443 unmodified lines |
| 475 | 478 | struct snd_soc_component *scomp = swidget->scomp; |
| 476 | 479 | struct snd_sof_dai *dai = swidget->private; |
| 477 | 480 | struct sof_ipc4_copier *ipc4_copier; |
| 481 | + struct snd_sof_widget *pipe_widget; |
| 482 | + struct sof_ipc4_pipeline *pipeline; |
| 478 | 483 | int node_type = 0; |
| 479 | 484 | int ret; |
| 480 | 485 | |
| @@ -512,6 +517,16 @@ static int sof_ipc4_widget_setup_comp_dai(struct snd_sof_widget *swidget) · 31 unmodified lines |
| 512 | 517 | |
| 513 | 518 | ipc4_copier->data.gtw_cfg.node_id = SOF_IPC4_NODE_TYPE(node_type); |
| 514 | 519 | |
| 520 | + pipe_widget = swidget->spipe->pipe_widget; |
| 521 | + pipeline = pipe_widget->private; |
| 522 | + if (pipeline->use_chain_dma && ipc4_copier->dai_type != SOF_DAI_INTEL_HDA) { |
| 523 | + dev_err(scomp->dev, |
| 524 | + "Bad DAI type '%d', Chained DMA is only supported by HDA DAIs (%d).\n", |
| 525 | + ipc4_copier->dai_type, SOF_DAI_INTEL_HDA); |
| 526 | + ret = -ENODEV; |
| 527 | + goto free_available_fmt; |
| 528 | + } |
| 529 | + |
| 515 | 530 | switch (ipc4_copier->dai_type) { |
| 516 | 531 | case SOF_DAI_INTEL_ALH: |
| 517 | 532 | { |
| @@ -643,6 +658,12 @@ static int sof_ipc4_widget_setup_comp_pipeline(struct snd_sof_widget *swidget) · 125 unmodified lines |
| 643 | 658 | |
| 644 | 659 | swidget->core = pipeline->core_id; |
| 645 | 660 | |
| 661 | + if (pipeline->use_chain_dma) { |
| 662 | + dev_dbg(scomp->dev, "Set up chain DMA for %s\n", swidget->widget->name); |
| 663 | + swidget->private = pipeline; |
| 664 | + return 0; |
| 665 | + } |
| 666 | + |
| 646 | 667 | /* parse one set of pipeline tokens */ |
| 647 | 668 | ret = sof_update_ipc_object(scomp, swidget, SOF_PIPELINE_TOKENS, swidget->tuples, |
| 648 | 669 | swidget->num_tuples, sizeof(*swidget), 1); |
| @@ -1103,11 +1124,21 @@ static void sof_ipc4_unprepare_copier_module(struct snd_sof_widget *swidget) · 454 unmodified lines |
| 1103 | 1124 | pipeline->mem_usage = 0; |
| 1104 | 1125 | |
| 1105 | 1126 | if (WIDGET_IS_AIF(swidget->id) || swidget->id == snd_soc_dapm_buffer) { |
| 1127 | + if (pipeline->use_chain_dma) { |
| 1128 | + pipeline->msg.primary = 0; |
| 1129 | + pipeline->msg.extension = 0; |
| 1130 | + } |
| 1106 | 1131 | ipc4_copier = swidget->private; |
| 1107 | 1132 | } else if (WIDGET_IS_DAI(swidget->id)) { |
| 1108 | 1133 | struct snd_sof_dai *dai = swidget->private; |
| 1109 | 1134 | |
| 1110 | 1135 | ipc4_copier = dai->private; |
| 1136 | + |
| 1137 | + if (pipeline->use_chain_dma) { |
| 1138 | + pipeline->msg.primary = 0; |
| 1139 | + pipeline->msg.extension = 0; |
| 1140 | + } |
| 1141 | + |
| 1111 | 1142 | if (ipc4_copier->dai_type == SOF_DAI_INTEL_ALH) { |
| 1112 | 1143 | struct sof_ipc4_copier_data *copier_data = &ipc4_copier->data; |
| 1113 | 1144 | struct sof_ipc4_alh_configuration_blob *blob; |
| @@ -1344,13 +1375,44 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, · 230 unmodified lines |
| 1344 | 1375 | return ret; |
| 1345 | 1376 | } |
| 1346 | 1377 | |
| 1347 | | − pipe_widget = swidget->spipe->pipe_widget; |
| 1348 | | − pipeline = pipe_widget->private; |
| 1349 | 1378 | ipc4_copier = (struct sof_ipc4_copier *)swidget->private; |
| 1350 | 1379 | gtw_attr = ipc4_copier->gtw_attr; |
| 1351 | 1380 | copier_data = &ipc4_copier->data; |
| 1352 | 1381 | available_fmt = &ipc4_copier->available_fmt; |
| 1353 | 1382 | |
| 1383 | + pipe_widget = swidget->spipe->pipe_widget; |
| 1384 | + pipeline = pipe_widget->private; |
| 1385 | + |
| 1386 | + if (pipeline->use_chain_dma) { |
| 1387 | + u32 host_dma_id; |
| 1388 | + u32 fifo_size; |
| 1389 | + |
| 1390 | + host_dma_id = platform_params->stream_tag - 1; |
| 1391 | + pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_HOST_ID(host_dma_id); |
| 1392 | + |
| 1393 | + /* Set SCS bit for S16_LE format only */ |
| 1394 | + if (params_format(fe_params) == SNDRV_PCM_FORMAT_S16_LE) |
| 1395 | + pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_SCS_MASK; |
| 1396 | + |
| 1397 | + /* |
| 1398 | + * Despite its name the bitfield 'fifo_size' is used to define DMA buffer |
| 1399 | + * size. The expression calculates 2ms buffer size. |
| 1400 | + */ |
| 1401 | + fifo_size = DIV_ROUND_UP((SOF_IPC4_CHAIN_DMA_BUF_SIZE_MS * |
| 1402 | + params_rate(fe_params) * |
| 1403 | + params_channels(fe_params) * |
| 1404 | + params_physical_width(fe_params)), 8000); |
| 1405 | + pipeline->msg.extension |= SOF_IPC4_GLB_EXT_CHAIN_DMA_FIFO_SIZE(fifo_size); |
| 1406 | + |
| 1407 | + /* |
| 1408 | + * Chain DMA does not support stream timestamping, set node_id to invalid |
| 1409 | + * to skip the code in sof_ipc4_get_stream_start_offset(). |
| 1410 | + */ |
| 1411 | + copier_data->gtw_cfg.node_id = SOF_IPC4_INVALID_NODE_ID; |
| 1412 | + |
| 1413 | + return 0; |
| 1414 | + } |
| 1415 | + |
| 1354 | 1416 | /* |
| 1355 | 1417 | * Use the input_pin_fmts to match pcm params for playback and the output_pin_fmts |
| 1356 | 1418 | * for capture. |
| @@ -1375,6 +1437,12 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, · 18 unmodified lines |
| 1375 | 1437 | case snd_soc_dapm_dai_in: |
| 1376 | 1438 | case snd_soc_dapm_dai_out: |
| 1377 | 1439 | { |
| 1440 | + struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; |
| 1441 | + struct sof_ipc4_pipeline *pipeline = pipe_widget->private; |
| 1442 | + |
| 1443 | + if (pipeline->use_chain_dma) |
| 1444 | + return 0; |
| 1445 | + |
| 1378 | 1446 | dai = swidget->private; |
| 1379 | 1447 | |
| 1380 | 1448 | 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 · 540 unmodified lines |
| 1921 | 1989 | case snd_soc_dapm_scheduler: |
| 1922 | 1990 | pipeline = swidget->private; |
| 1923 | 1991 | |
| 1992 | + if (pipeline->use_chain_dma) |
| 1993 | + return 0; |
| 1994 | + |
| 1924 | 1995 | dev_dbg(sdev->dev, "pipeline: %d memory pages: %d\n", swidget->pipeline_id, |
| 1925 | 1996 | pipeline->mem_usage); |
| 1926 | 1997 | |
| @@ -1943,6 +2014,10 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget · 16 unmodified lines |
| 1943 | 2014 | { |
| 1944 | 2015 | struct sof_ipc4_copier *ipc4_copier = swidget->private; |
| 1945 | 2016 | |
| 2017 | + pipeline = pipe_widget->private; |
| 2018 | + if (pipeline->use_chain_dma) |
| 2019 | + return 0; |
| 2020 | + |
| 1946 | 2021 | ipc_size = ipc4_copier->ipc_config_size; |
| 1947 | 2022 | ipc_data = ipc4_copier->ipc_config_data; |
| 1948 | 2023 | |
| @@ -1955,6 +2030,10 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget · 6 unmodified lines |
| 1955 | 2030 | struct snd_sof_dai *dai = swidget->private; |
| 1956 | 2031 | struct sof_ipc4_copier *ipc4_copier = dai->private; |
| 1957 | 2032 | |
| 2033 | + pipeline = pipe_widget->private; |
| 2034 | + if (pipeline->use_chain_dma) |
| 2035 | + return 0; |
| 2036 | + |
| 1958 | 2037 | ipc_size = ipc4_copier->ipc_config_size; |
| 1959 | 2038 | ipc_data = ipc4_copier->ipc_config_data; |
| 1960 | 2039 | |
| @@ -2066,6 +2145,9 @@ static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget · 105 unmodified lines |
| 2066 | 2145 | struct sof_ipc4_msg msg = {{ 0 }}; |
| 2067 | 2146 | u32 header; |
| 2068 | 2147 | |
| 2148 | + if (pipeline->use_chain_dma) |
| 2149 | + return 0; |
| 2150 | + |
| 2069 | 2151 | header = SOF_IPC4_GLB_PIPE_INSTANCE_ID(swidget->instance_id); |
| 2070 | 2152 | header |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_DELETE_PIPELINE); |
| 2071 | 2153 | 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 · 10 unmodified lines |
| 2082 | 2164 | pipeline->state = SOF_IPC4_PIPE_UNINITIALIZED; |
| 2083 | 2165 | ida_free(&pipeline_ida, swidget->instance_id); |
| 2084 | 2166 | } else { |
| 2085 | | − ida_free(&fw_module->m_ida, swidget->instance_id); |
| 2167 | + struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; |
| 2168 | + struct sof_ipc4_pipeline *pipeline = pipe_widget->private; |
| 2169 | + |
| 2170 | + if (!pipeline->use_chain_dma) |
| 2171 | + ida_free(&fw_module->m_ida, swidget->instance_id); |
| 2086 | 2172 | } |
| 2087 | 2173 | |
| 2088 | 2174 | 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 * · 145 unmodified lines |
| 2234 | 2320 | { |
| 2235 | 2321 | struct snd_sof_widget *src_widget = sroute->src_widget; |
| 2236 | 2322 | struct snd_sof_widget *sink_widget = sroute->sink_widget; |
| 2323 | + struct snd_sof_widget *src_pipe_widget = src_widget->spipe->pipe_widget; |
| 2324 | + struct snd_sof_widget *sink_pipe_widget = sink_widget->spipe->pipe_widget; |
| 2237 | 2325 | struct sof_ipc4_fw_module *src_fw_module = src_widget->module_info; |
| 2238 | 2326 | struct sof_ipc4_fw_module *sink_fw_module = sink_widget->module_info; |
| 2327 | + struct sof_ipc4_pipeline *src_pipeline = src_pipe_widget->private; |
| 2328 | + struct sof_ipc4_pipeline *sink_pipeline = sink_pipe_widget->private; |
| 2239 | 2329 | struct sof_ipc4_msg msg = {{ 0 }}; |
| 2240 | 2330 | u32 header, extension; |
| 2241 | 2331 | int ret; |
| 2242 | 2332 | |
| 2333 | + /* no route set up if chain DMA is used */ |
| 2334 | + if (src_pipeline->use_chain_dma || sink_pipeline->use_chain_dma) { |
| 2335 | + if (!src_pipeline->use_chain_dma || !sink_pipeline->use_chain_dma) { |
| 2336 | + dev_err(sdev->dev, |
| 2337 | + "use_chain_dma must be set for both src %s and sink %s pipelines\n", |
| 2338 | + src_widget->widget->name, sink_widget->widget->name); |
| 2339 | + return -EINVAL; |
| 2340 | + } |
| 2341 | + return 0; |
| 2342 | + } |
| 2343 | + |
| 2243 | 2344 | sroute->src_queue_id = sof_ipc4_get_queue_id(src_widget, sink_widget, |
| 2244 | 2345 | SOF_PIN_TYPE_OUTPUT); |
| 2245 | 2346 | 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 · 64 unmodified lines |
| 2310 | 2411 | struct sof_ipc4_fw_module *src_fw_module = src_widget->module_info; |
| 2311 | 2412 | struct sof_ipc4_fw_module *sink_fw_module = sink_widget->module_info; |
| 2312 | 2413 | struct sof_ipc4_msg msg = {{ 0 }}; |
| 2414 | + struct snd_sof_widget *src_pipe_widget = src_widget->spipe->pipe_widget; |
| 2415 | + struct snd_sof_widget *sink_pipe_widget = sink_widget->spipe->pipe_widget; |
| 2416 | + struct sof_ipc4_pipeline *src_pipeline = src_pipe_widget->private; |
| 2417 | + struct sof_ipc4_pipeline *sink_pipeline = sink_pipe_widget->private; |
| 2313 | 2418 | u32 header, extension; |
| 2314 | 2419 | int ret = 0; |
| 2315 | 2420 | |
| 2421 | + /* no route is set up if chain DMA is used */ |
| 2422 | + if (src_pipeline->use_chain_dma || sink_pipeline->use_chain_dma) |
| 2423 | + return 0; |
| 2424 | + |
| 2316 | 2425 | dev_dbg(sdev->dev, "unbind modules %s:%d -> %s:%d\n", |
| 2317 | 2426 | src_widget->widget->name, sroute->src_queue_id, |
| 2318 | 2427 | 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 * · 55 unmodified lines |
| 2374 | 2483 | |
| 2375 | 2484 | switch (ipc4_copier->dai_type) { |
| 2376 | 2485 | case SOF_DAI_INTEL_HDA: |
| 2486 | + if (pipeline->use_chain_dma) { |
| 2487 | + pipeline->msg.primary &= ~SOF_IPC4_GLB_CHAIN_DMA_LINK_ID_MASK; |
| 2488 | + pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_LINK_ID(data->dai_data); |
| 2489 | + break; |
| 2490 | + } |
| 2377 | 2491 | gtw_attr = ipc4_copier->gtw_attr; |
| 2378 | 2492 | gtw_attr->lp_buffer_alloc = pipeline->lp_mode; |
| 2379 | 2493 | pipeline->skip_during_fe_trigger = true; |