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

ASoC: SOF: ipc4: Multi-stream playback and capture support

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

The following series will enable multi-stream support for playback and capture
streams.
Currently only a single PCM can be connected to a DAI, with the multi-stream
support it is possible to connect multiple PCMs to a single DAI.

To achieve this we need to make sure that DAIs/AIF are only set up once since
other stream could be connected to it later.

We also need to introduce reference or use counting for widgets to make sure
that they are not going to be destroyed while other streams are still using
them.

With the multi-stream support we also need to extend our current locking scheme
which worked well for simple paths.

+718 -309
+2
include/sound/soc-dpcm.h
··· 162 162 int dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir, 163 163 int event); 164 164 bool dpcm_end_walk_at_be(struct snd_soc_dapm_widget *widget, enum snd_soc_dapm_direction dir); 165 + int widget_in_list(struct snd_soc_dapm_widget_list *list, 166 + struct snd_soc_dapm_widget *widget); 165 167 166 168 #define dpcm_be_dai_startup_rollback(fe, stream, last) \ 167 169 dpcm_be_dai_stop(fe, stream, 0, last)
+3
include/sound/sof/ipc4/header.h
··· 185 185 #define SOF_IPC4_GLB_PIPE_STATE_MASK GENMASK(15, 0) 186 186 #define SOF_IPC4_GLB_PIPE_STATE(x) ((x) << SOF_IPC4_GLB_PIPE_STATE_SHIFT) 187 187 188 + /* pipeline set state IPC msg extension */ 189 + #define SOF_IPC4_GLB_PIPE_STATE_EXT_MULTI BIT(0) 190 + 188 191 /* load library ipc msg */ 189 192 #define SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID_SHIFT 16 190 193 #define SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID(x) ((x) << SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID_SHIFT)
+2 -1
sound/soc/soc-pcm.c
··· 1337 1337 return NULL; 1338 1338 } 1339 1339 1340 - static int widget_in_list(struct snd_soc_dapm_widget_list *list, 1340 + int widget_in_list(struct snd_soc_dapm_widget_list *list, 1341 1341 struct snd_soc_dapm_widget *widget) 1342 1342 { 1343 1343 struct snd_soc_dapm_widget *w; ··· 1349 1349 1350 1350 return 0; 1351 1351 } 1352 + EXPORT_SYMBOL_GPL(widget_in_list); 1352 1353 1353 1354 bool dpcm_end_walk_at_be(struct snd_soc_dapm_widget *widget, enum snd_soc_dapm_direction dir) 1354 1355 {
+1
sound/soc/sof/core.c
··· 390 390 INIT_LIST_HEAD(&sdev->pcm_list); 391 391 INIT_LIST_HEAD(&sdev->kcontrol_list); 392 392 INIT_LIST_HEAD(&sdev->widget_list); 393 + INIT_LIST_HEAD(&sdev->pipeline_list); 393 394 INIT_LIST_HEAD(&sdev->dai_list); 394 395 INIT_LIST_HEAD(&sdev->dai_link_list); 395 396 INIT_LIST_HEAD(&sdev->route_list);
+17 -75
sound/soc/sof/intel/hda-dai.c
··· 450 450 { 451 451 struct hdac_ext_stream *hext_stream = snd_soc_dai_get_dma_data(dai, substream); 452 452 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component); 453 + struct snd_sof_widget *pipe_widget; 454 + struct sof_ipc4_pipeline *pipeline; 453 455 struct snd_soc_pcm_runtime *rtd; 454 456 struct snd_sof_widget *swidget; 455 457 struct snd_soc_dapm_widget *w; ··· 468 466 469 467 w = snd_soc_dai_get_widget(dai, substream->stream); 470 468 swidget = w->dobj.private; 469 + pipe_widget = swidget->spipe->pipe_widget; 470 + pipeline = pipe_widget->private; 471 471 472 472 switch (cmd) { 473 473 case SNDRV_PCM_TRIGGER_START: 474 474 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 475 475 snd_hdac_ext_stream_start(hext_stream); 476 + if (pipeline->state != SOF_IPC4_PIPE_PAUSED) { 477 + ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, 478 + SOF_IPC4_PIPE_PAUSED); 479 + if (ret < 0) 480 + return ret; 481 + pipeline->state = SOF_IPC4_PIPE_PAUSED; 482 + } 483 + 484 + ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, 485 + SOF_IPC4_PIPE_RUNNING); 486 + if (ret < 0) 487 + return ret; 488 + pipeline->state = SOF_IPC4_PIPE_RUNNING; 476 489 break; 477 490 case SNDRV_PCM_TRIGGER_SUSPEND: 478 491 case SNDRV_PCM_TRIGGER_STOP: 479 492 { 480 - struct snd_sof_widget *pipe_widget = swidget->pipe_widget; 481 - struct sof_ipc4_pipeline *pipeline = pipe_widget->private; 482 - 483 493 ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, 484 494 SOF_IPC4_PIPE_PAUSED); 485 495 if (ret < 0) ··· 517 503 } 518 504 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 519 505 { 520 - struct snd_sof_widget *pipe_widget = swidget->pipe_widget; 521 - struct sof_ipc4_pipeline *pipeline = pipe_widget->private; 522 - 523 506 ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, 524 507 SOF_IPC4_PIPE_PAUSED); 525 508 if (ret < 0) ··· 714 703 .shutdown = ssp_dai_shutdown, 715 704 }; 716 705 717 - static int ipc4_be_dai_common_trigger(struct snd_soc_dai *dai, int cmd, int stream) 718 - { 719 - struct snd_sof_widget *pipe_widget; 720 - struct sof_ipc4_pipeline *pipeline; 721 - struct snd_sof_widget *swidget; 722 - struct snd_soc_dapm_widget *w; 723 - struct snd_sof_dev *sdev; 724 - int ret; 725 - 726 - w = snd_soc_dai_get_widget(dai, stream); 727 - swidget = w->dobj.private; 728 - pipe_widget = swidget->pipe_widget; 729 - pipeline = pipe_widget->private; 730 - sdev = snd_soc_component_get_drvdata(swidget->scomp); 731 - 732 - switch (cmd) { 733 - case SNDRV_PCM_TRIGGER_SUSPEND: 734 - case SNDRV_PCM_TRIGGER_STOP: 735 - ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, 736 - SOF_IPC4_PIPE_PAUSED); 737 - if (ret < 0) 738 - return ret; 739 - pipeline->state = SOF_IPC4_PIPE_PAUSED; 740 - 741 - ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, 742 - SOF_IPC4_PIPE_RESET); 743 - if (ret < 0) 744 - return ret; 745 - pipeline->state = SOF_IPC4_PIPE_RESET; 746 - break; 747 - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 748 - ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, 749 - SOF_IPC4_PIPE_PAUSED); 750 - if (ret < 0) 751 - return ret; 752 - pipeline->state = SOF_IPC4_PIPE_PAUSED; 753 - break; 754 - default: 755 - break; 756 - } 757 - 758 - return 0; 759 - } 760 - 761 - static int ipc4_be_dai_trigger(struct snd_pcm_substream *substream, 762 - int cmd, struct snd_soc_dai *dai) 763 - { 764 - return ipc4_be_dai_common_trigger(dai, cmd, substream->stream); 765 - } 766 - 767 - static const struct snd_soc_dai_ops ipc4_dmic_dai_ops = { 768 - .trigger = ipc4_be_dai_trigger, 769 - }; 770 - 771 - static const struct snd_soc_dai_ops ipc4_ssp_dai_ops = { 772 - .trigger = ipc4_be_dai_trigger, 773 - }; 774 - 775 706 void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) 776 707 { 777 708 int i; ··· 738 785 struct sof_ipc4_fw_data *ipc4_data = sdev->private; 739 786 740 787 for (i = 0; i < ops->num_drv; i++) { 741 - if (strstr(ops->drv[i].name, "DMIC")) { 742 - ops->drv[i].ops = &ipc4_dmic_dai_ops; 743 - continue; 744 - } 745 - if (strstr(ops->drv[i].name, "SSP")) { 746 - ops->drv[i].ops = &ipc4_ssp_dai_ops; 747 - continue; 748 - } 749 788 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) 750 789 if (strstr(ops->drv[i].name, "iDisp") || 751 790 strstr(ops->drv[i].name, "Analog") || ··· 748 803 749 804 if (!hda_use_tplg_nhlt) 750 805 ipc4_data->nhlt = intel_nhlt_init(sdev->dev); 751 - 752 - if (IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE)) 753 - sdw_callback.trigger = ipc4_be_dai_common_trigger; 754 806 755 807 break; 756 808 }
+30 -16
sound/soc/sof/ipc3-control.c
··· 12 12 #include "ipc3-priv.h" 13 13 14 14 /* IPC set()/get() for kcontrols. */ 15 - static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool set) 15 + static int sof_ipc3_set_get_kcontrol_data(struct snd_sof_control *scontrol, 16 + bool set, bool lock) 16 17 { 17 18 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scontrol->scomp); 18 19 struct sof_ipc_ctrl_data *cdata = scontrol->ipc_control_data; ··· 22 21 struct snd_sof_widget *swidget; 23 22 bool widget_found = false; 24 23 u32 ipc_cmd, msg_bytes; 24 + int ret = 0; 25 25 26 26 list_for_each_entry(swidget, &sdev->widget_list, list) { 27 27 if (swidget->comp_id == scontrol->comp_id) { ··· 37 35 return -EINVAL; 38 36 } 39 37 38 + if (lock) 39 + mutex_lock(&swidget->setup_mutex); 40 + else 41 + lockdep_assert_held(&swidget->setup_mutex); 42 + 40 43 /* 41 - * Volatile controls should always be part of static pipelines and the widget use_count 42 - * would always be > 0 in this case. For the others, just return the cached value if the 43 - * widget is not set up. 44 + * Volatile controls should always be part of static pipelines and the 45 + * widget use_count would always be > 0 in this case. For the others, 46 + * just return the cached value if the widget is not set up. 44 47 */ 45 48 if (!swidget->use_count) 46 - return 0; 49 + goto unlock; 47 50 48 51 /* 49 52 * Select the IPC cmd and the ctrl_type based on the ctrl_cmd and the ··· 88 81 sizeof(struct sof_abi_hdr); 89 82 break; 90 83 default: 91 - return -EINVAL; 84 + ret = -EINVAL; 85 + goto unlock; 92 86 } 93 87 94 88 cdata->rhdr.hdr.size = msg_bytes; 95 89 cdata->elems_remaining = 0; 96 90 97 - return iops->set_get_data(sdev, cdata, cdata->rhdr.hdr.size, set); 91 + ret = iops->set_get_data(sdev, cdata, cdata->rhdr.hdr.size, set); 92 + 93 + unlock: 94 + if (lock) 95 + mutex_unlock(&swidget->setup_mutex); 96 + 97 + return ret; 98 98 } 99 99 100 100 static void snd_sof_refresh_control(struct snd_sof_control *scontrol) ··· 122 108 123 109 /* refresh the component data from DSP */ 124 110 scontrol->comp_data_dirty = false; 125 - ret = sof_ipc3_set_get_kcontrol_data(scontrol, false); 111 + ret = sof_ipc3_set_get_kcontrol_data(scontrol, false, true); 126 112 if (ret < 0) { 127 113 dev_err(scomp->dev, "Failed to get control data: %d\n", ret); 128 114 ··· 170 156 171 157 /* notify DSP of mixer updates */ 172 158 if (pm_runtime_active(scomp->dev)) { 173 - int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true); 159 + int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, true); 174 160 175 161 if (ret < 0) { 176 162 dev_err(scomp->dev, "Failed to set mixer updates for %s\n", ··· 218 204 219 205 /* notify DSP of mixer updates */ 220 206 if (pm_runtime_active(scomp->dev)) { 221 - int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true); 207 + int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, true); 222 208 223 209 if (ret < 0) { 224 210 dev_err(scomp->dev, "Failed to set mixer updates for %s\n", ··· 266 252 267 253 /* notify DSP of enum updates */ 268 254 if (pm_runtime_active(scomp->dev)) { 269 - int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true); 255 + int ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, true); 270 256 271 257 if (ret < 0) { 272 258 dev_err(scomp->dev, "Failed to set enum updates for %s\n", ··· 338 324 339 325 /* notify DSP of byte control updates */ 340 326 if (pm_runtime_active(scomp->dev)) 341 - return sof_ipc3_set_get_kcontrol_data(scontrol, true); 327 + return sof_ipc3_set_get_kcontrol_data(scontrol, true, true); 342 328 343 329 return 0; 344 330 } ··· 452 438 453 439 /* notify DSP of byte control updates */ 454 440 if (pm_runtime_active(scomp->dev)) 455 - return sof_ipc3_set_get_kcontrol_data(scontrol, true); 441 + return sof_ipc3_set_get_kcontrol_data(scontrol, true, true); 456 442 457 443 return 0; 458 444 } ··· 482 468 cdata->data->abi = SOF_ABI_VERSION; 483 469 484 470 /* get all the component data from DSP */ 485 - ret = sof_ipc3_set_get_kcontrol_data(scontrol, false); 471 + ret = sof_ipc3_set_get_kcontrol_data(scontrol, false, true); 486 472 if (ret < 0) 487 473 return ret; 488 474 ··· 661 647 list_for_each_entry(scontrol, &sdev->kcontrol_list, list) 662 648 if (scontrol->comp_id == swidget->comp_id) { 663 649 /* set kcontrol data in DSP */ 664 - ret = sof_ipc3_set_get_kcontrol_data(scontrol, true); 650 + ret = sof_ipc3_set_get_kcontrol_data(scontrol, true, false); 665 651 if (ret < 0) { 666 652 dev_err(sdev->dev, 667 653 "kcontrol %d set up failed for widget %s\n", ··· 678 664 if (swidget->dynamic_pipeline_widget) 679 665 continue; 680 666 681 - ret = sof_ipc3_set_get_kcontrol_data(scontrol, false); 667 + ret = sof_ipc3_set_get_kcontrol_data(scontrol, false, false); 682 668 if (ret < 0) 683 669 dev_warn(sdev->dev, 684 670 "kcontrol %d read failed for widget %s\n",
+27 -5
sound/soc/sof/ipc3-topology.c
··· 2233 2233 return ret; 2234 2234 } 2235 2235 2236 - swidget->complete = sof_ipc3_complete_pipeline(sdev, swidget); 2237 - if (swidget->complete < 0) 2238 - return swidget->complete; 2236 + swidget->spipe->complete = sof_ipc3_complete_pipeline(sdev, swidget); 2237 + if (swidget->spipe->complete < 0) 2238 + return swidget->spipe->complete; 2239 2239 break; 2240 2240 default: 2241 2241 break; ··· 2264 2264 for_each_pcm_streams(dir) { 2265 2265 struct snd_pcm_substream *substream = spcm->stream[dir].substream; 2266 2266 2267 - if (!substream || !substream->runtime) 2267 + if (!substream || !substream->runtime || spcm->stream[dir].suspend_ignored) 2268 2268 continue; 2269 2269 2270 2270 if (spcm->stream[dir].list) { ··· 2316 2316 /* Do not free widgets for static pipelines with FW older than SOF2.2 */ 2317 2317 if (!verify && !swidget->dynamic_pipeline_widget && 2318 2318 SOF_FW_VER(v->major, v->minor, v->micro) < SOF_FW_VER(2, 2, 0)) { 2319 + mutex_lock(&swidget->setup_mutex); 2319 2320 swidget->use_count = 0; 2320 - swidget->complete = 0; 2321 + mutex_unlock(&swidget->setup_mutex); 2322 + if (swidget->spipe) 2323 + swidget->spipe->complete = 0; 2321 2324 continue; 2322 2325 } 2323 2326 ··· 2425 2422 dev_err(scomp->dev, "%s: Topology ABI is more recent than kernel\n", __func__); 2426 2423 return -EINVAL; 2427 2424 } 2425 + 2426 + return 0; 2427 + } 2428 + 2429 + static int sof_ipc3_link_setup(struct snd_sof_dev *sdev, struct snd_soc_dai_link *link) 2430 + { 2431 + if (link->no_pcm) 2432 + return 0; 2433 + 2434 + /* 2435 + * set default trigger order for all links. Exceptions to 2436 + * the rule will be handled in sof_pcm_dai_link_fixup() 2437 + * For playback, the sequence is the following: start FE, 2438 + * start BE, stop BE, stop FE; for Capture the sequence is 2439 + * inverted start BE, start FE, stop FE, stop BE 2440 + */ 2441 + link->trigger[SNDRV_PCM_STREAM_PLAYBACK] = SND_SOC_DPCM_TRIGGER_PRE; 2442 + link->trigger[SNDRV_PCM_STREAM_CAPTURE] = SND_SOC_DPCM_TRIGGER_POST; 2428 2443 2429 2444 return 0; 2430 2445 } ··· 2558 2537 .set_up_all_pipelines = sof_ipc3_set_up_all_pipelines, 2559 2538 .tear_down_all_pipelines = sof_ipc3_tear_down_all_pipelines, 2560 2539 .parse_manifest = sof_ipc3_parse_manifest, 2540 + .link_setup = sof_ipc3_link_setup, 2561 2541 };
+23 -10
sound/soc/sof/ipc4-control.c
··· 12 12 #include "ipc4-priv.h" 13 13 #include "ipc4-topology.h" 14 14 15 - static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol, bool set) 15 + static int sof_ipc4_set_get_kcontrol_data(struct snd_sof_control *scontrol, 16 + bool set, bool lock) 16 17 { 17 18 struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; 18 19 struct snd_soc_component *scomp = scontrol->scomp; ··· 22 21 struct sof_ipc4_msg *msg = &cdata->msg; 23 22 struct snd_sof_widget *swidget; 24 23 bool widget_found = false; 24 + int ret = 0; 25 25 26 26 /* find widget associated with the control */ 27 27 list_for_each_entry(swidget, &sdev->widget_list, list) { ··· 37 35 return -ENOENT; 38 36 } 39 37 38 + if (lock) 39 + mutex_lock(&swidget->setup_mutex); 40 + else 41 + lockdep_assert_held(&swidget->setup_mutex); 42 + 40 43 /* 41 - * Volatile controls should always be part of static pipelines and the widget use_count 42 - * would always be > 0 in this case. For the others, just return the cached value if the 43 - * widget is not set up. 44 + * Volatile controls should always be part of static pipelines and the 45 + * widget use_count would always be > 0 in this case. For the others, 46 + * just return the cached value if the widget is not set up. 44 47 */ 45 48 if (!swidget->use_count) 46 - return 0; 49 + goto unlock; 47 50 48 51 msg->primary &= ~SOF_IPC4_MOD_INSTANCE_MASK; 49 52 msg->primary |= SOF_IPC4_MOD_INSTANCE(swidget->instance_id); 50 53 51 - return iops->set_get_data(sdev, msg, msg->data_size, set); 54 + ret = iops->set_get_data(sdev, msg, msg->data_size, set); 55 + 56 + unlock: 57 + if (lock) 58 + mutex_unlock(&swidget->setup_mutex); 59 + 60 + return ret; 52 61 } 53 62 54 63 static int 55 64 sof_ipc4_set_volume_data(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, 56 - struct snd_sof_control *scontrol) 65 + struct snd_sof_control *scontrol, bool lock) 57 66 { 58 67 struct sof_ipc4_control_data *cdata = scontrol->ipc_control_data; 59 68 struct sof_ipc4_gain *gain = swidget->private; ··· 103 90 msg->data_ptr = &data; 104 91 msg->data_size = sizeof(data); 105 92 106 - ret = sof_ipc4_set_get_kcontrol_data(scontrol, true); 93 + ret = sof_ipc4_set_get_kcontrol_data(scontrol, true, lock); 107 94 msg->data_ptr = NULL; 108 95 msg->data_size = 0; 109 96 if (ret < 0) { ··· 158 145 return false; 159 146 } 160 147 161 - ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol); 148 + ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol, true); 162 149 if (ret < 0) 163 150 return false; 164 151 ··· 188 175 189 176 list_for_each_entry(scontrol, &sdev->kcontrol_list, list) 190 177 if (scontrol->comp_id == swidget->comp_id) { 191 - ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol); 178 + ret = sof_ipc4_set_volume_data(sdev, swidget, scontrol, false); 192 179 if (ret < 0) { 193 180 dev_err(sdev->dev, "%s: kcontrol %d set up failed for widget %s\n", 194 181 __func__, scontrol->comp_id, swidget->widget->name);
+280 -71
sound/soc/sof/ipc4-pcm.c
··· 13 13 #include "ipc4-priv.h" 14 14 #include "ipc4-topology.h" 15 15 16 + static int sof_ipc4_set_multi_pipeline_state(struct snd_sof_dev *sdev, u32 state, 17 + struct ipc4_pipeline_set_state_data *trigger_list) 18 + { 19 + struct sof_ipc4_msg msg = {{ 0 }}; 20 + u32 primary, ipc_size; 21 + 22 + /* trigger a single pipeline */ 23 + if (trigger_list->count == 1) 24 + return sof_ipc4_set_pipeline_state(sdev, trigger_list->pipeline_ids[0], state); 25 + 26 + primary = state; 27 + primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_SET_PIPELINE_STATE); 28 + primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); 29 + primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG); 30 + msg.primary = primary; 31 + 32 + /* trigger multiple pipelines with a single IPC */ 33 + msg.extension = SOF_IPC4_GLB_PIPE_STATE_EXT_MULTI; 34 + 35 + /* ipc_size includes the count and the pipeline IDs for the number of pipelines */ 36 + ipc_size = sizeof(u32) * (trigger_list->count + 1); 37 + msg.data_size = ipc_size; 38 + msg.data_ptr = trigger_list; 39 + 40 + return sof_ipc_tx_message(sdev->ipc, &msg, ipc_size, NULL, 0); 41 + } 42 + 16 43 int sof_ipc4_set_pipeline_state(struct snd_sof_dev *sdev, u32 id, u32 state) 17 44 { 18 45 struct sof_ipc4_msg msg = {{ 0 }}; ··· 59 32 } 60 33 EXPORT_SYMBOL(sof_ipc4_set_pipeline_state); 61 34 35 + static void 36 + sof_ipc4_add_pipeline_to_trigger_list(struct snd_sof_dev *sdev, int state, 37 + struct snd_sof_pipeline *spipe, 38 + struct ipc4_pipeline_set_state_data *trigger_list) 39 + { 40 + struct snd_sof_widget *pipe_widget = spipe->pipe_widget; 41 + struct sof_ipc4_pipeline *pipeline = pipe_widget->private; 42 + 43 + if (pipeline->skip_during_fe_trigger) 44 + return; 45 + 46 + switch (state) { 47 + case SOF_IPC4_PIPE_RUNNING: 48 + /* 49 + * Trigger pipeline if all PCMs containing it are paused or if it is RUNNING 50 + * for the first time 51 + */ 52 + if (spipe->started_count == spipe->paused_count) 53 + trigger_list->pipeline_ids[trigger_list->count++] = 54 + pipe_widget->instance_id; 55 + break; 56 + case SOF_IPC4_PIPE_RESET: 57 + /* RESET if the pipeline is neither running nor paused */ 58 + if (!spipe->started_count && !spipe->paused_count) 59 + trigger_list->pipeline_ids[trigger_list->count++] = 60 + pipe_widget->instance_id; 61 + break; 62 + case SOF_IPC4_PIPE_PAUSED: 63 + /* Pause the pipeline only when its started_count is 1 more than paused_count */ 64 + if (spipe->paused_count == (spipe->started_count - 1)) 65 + trigger_list->pipeline_ids[trigger_list->count++] = 66 + pipe_widget->instance_id; 67 + break; 68 + default: 69 + break; 70 + } 71 + } 72 + 73 + static void 74 + sof_ipc4_update_pipeline_state(struct snd_sof_dev *sdev, int state, int cmd, 75 + struct snd_sof_pipeline *spipe, 76 + struct ipc4_pipeline_set_state_data *trigger_list) 77 + { 78 + struct snd_sof_widget *pipe_widget = spipe->pipe_widget; 79 + struct sof_ipc4_pipeline *pipeline = pipe_widget->private; 80 + int i; 81 + 82 + if (pipeline->skip_during_fe_trigger) 83 + return; 84 + 85 + /* set state for pipeline if it was just triggered */ 86 + for (i = 0; i < trigger_list->count; i++) { 87 + if (trigger_list->pipeline_ids[i] == pipe_widget->instance_id) { 88 + pipeline->state = state; 89 + break; 90 + } 91 + } 92 + 93 + switch (state) { 94 + case SOF_IPC4_PIPE_PAUSED: 95 + switch (cmd) { 96 + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 97 + /* 98 + * increment paused_count if the PAUSED is the final state during 99 + * the PAUSE trigger 100 + */ 101 + spipe->paused_count++; 102 + break; 103 + case SNDRV_PCM_TRIGGER_STOP: 104 + case SNDRV_PCM_TRIGGER_SUSPEND: 105 + /* 106 + * decrement started_count if PAUSED is the final state during the 107 + * STOP trigger 108 + */ 109 + spipe->started_count--; 110 + break; 111 + default: 112 + break; 113 + } 114 + break; 115 + case SOF_IPC4_PIPE_RUNNING: 116 + switch (cmd) { 117 + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 118 + /* decrement paused_count for RELEASE */ 119 + spipe->paused_count--; 120 + break; 121 + case SNDRV_PCM_TRIGGER_START: 122 + case SNDRV_PCM_TRIGGER_RESUME: 123 + /* increment started_count for START/RESUME */ 124 + spipe->started_count++; 125 + break; 126 + default: 127 + break; 128 + } 129 + break; 130 + default: 131 + break; 132 + } 133 + } 134 + 135 + /* 136 + * The picture below represents the pipeline state machine wrt PCM actions corresponding to the 137 + * triggers and ioctls 138 + * +---------------+ 139 + * | | 140 + * | INIT | 141 + * | | 142 + * +-------+-------+ 143 + * | 144 + * | 145 + * | START 146 + * | 147 + * | 148 + * +----------------+ +------v-------+ +-------------+ 149 + * | | START | | HW_FREE | | 150 + * | RUNNING <-------------+ PAUSED +--------------> + RESET | 151 + * | | PAUSE | | | | 152 + * +------+---------+ RELEASE +---------+----+ +-------------+ 153 + * | ^ 154 + * | | 155 + * | | 156 + * | | 157 + * | PAUSE | 158 + * +---------------------------------+ 159 + * STOP/SUSPEND 160 + * 161 + * Note that during system suspend, the suspend trigger is followed by a hw_free in 162 + * sof_pcm_trigger(). So, the final state during suspend would be RESET. 163 + * Also, since the SOF driver doesn't support full resume, streams would be restarted with the 164 + * prepare ioctl before the START trigger. 165 + */ 166 + 62 167 static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component, 63 - struct snd_pcm_substream *substream, int state) 168 + struct snd_pcm_substream *substream, int state, int cmd) 64 169 { 65 170 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); 66 171 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 67 - struct snd_sof_widget *pipeline_widget; 68 - struct snd_soc_dapm_widget_list *list; 69 - struct snd_soc_dapm_widget *widget; 70 - struct sof_ipc4_pipeline *pipeline; 71 - struct snd_sof_widget *swidget; 172 + struct snd_sof_pcm_stream_pipeline_list *pipeline_list; 173 + struct sof_ipc4_fw_data *ipc4_data = sdev->private; 174 + struct ipc4_pipeline_set_state_data *trigger_list; 175 + struct snd_sof_pipeline *spipe; 72 176 struct snd_sof_pcm *spcm; 73 - int ret = 0; 74 - int num_widgets; 177 + int ret; 178 + int i; 179 + 180 + dev_dbg(sdev->dev, "trigger cmd: %d state: %d\n", cmd, state); 75 181 76 182 spcm = snd_sof_find_spcm_dai(component, rtd); 77 183 if (!spcm) 78 184 return -EINVAL; 79 185 80 - list = spcm->stream[substream->stream].list; 186 + pipeline_list = &spcm->stream[substream->stream].pipeline_list; 81 187 82 - for_each_dapm_widgets(list, num_widgets, widget) { 83 - swidget = widget->dobj.private; 188 + /* nothing to trigger if the list is empty */ 189 + if (!pipeline_list->pipelines || !pipeline_list->count) 190 + return 0; 84 191 85 - if (!swidget) 86 - continue; 192 + /* allocate memory for the pipeline data */ 193 + trigger_list = kzalloc(struct_size(trigger_list, pipeline_ids, pipeline_list->count), 194 + GFP_KERNEL); 195 + if (!trigger_list) 196 + return -ENOMEM; 87 197 88 - /* 89 - * set pipeline state for both FE and BE pipelines for RUNNING state. 90 - * For PAUSE/RESET, set the pipeline state only for the FE pipeline. 91 - */ 92 - switch (state) { 93 - case SOF_IPC4_PIPE_PAUSED: 94 - case SOF_IPC4_PIPE_RESET: 95 - if (!WIDGET_IS_AIF(swidget->id)) 96 - continue; 97 - break; 98 - default: 99 - break; 198 + mutex_lock(&ipc4_data->pipeline_state_mutex); 199 + 200 + /* 201 + * IPC4 requires pipelines to be triggered in order starting at the sink and 202 + * walking all the way to the source. So traverse the pipeline_list in the order 203 + * sink->source when starting PCM's and in the reverse order to pause/stop PCM's. 204 + * Skip the pipelines that have their skip_during_fe_trigger flag set. If there is a fork 205 + * in the pipeline, the order of triggering between the left/right paths will be 206 + * indeterministic. But the sink->source trigger order sink->source would still be 207 + * guaranteed for each fork independently. 208 + */ 209 + if (state == SOF_IPC4_PIPE_RUNNING || state == SOF_IPC4_PIPE_RESET) 210 + for (i = pipeline_list->count - 1; i >= 0; i--) { 211 + spipe = pipeline_list->pipelines[i]; 212 + sof_ipc4_add_pipeline_to_trigger_list(sdev, state, spipe, trigger_list); 213 + } 214 + else 215 + for (i = 0; i < pipeline_list->count; i++) { 216 + spipe = pipeline_list->pipelines[i]; 217 + sof_ipc4_add_pipeline_to_trigger_list(sdev, state, spipe, trigger_list); 100 218 } 101 219 102 - /* find pipeline widget for the pipeline that this widget belongs to */ 103 - pipeline_widget = swidget->pipe_widget; 104 - pipeline = (struct sof_ipc4_pipeline *)pipeline_widget->private; 105 - 106 - if (pipeline->state == state) 107 - continue; 108 - 109 - /* first set the pipeline to PAUSED state */ 110 - if (pipeline->state != SOF_IPC4_PIPE_PAUSED) { 111 - ret = sof_ipc4_set_pipeline_state(sdev, pipeline_widget->instance_id, 112 - SOF_IPC4_PIPE_PAUSED); 113 - if (ret < 0) { 114 - dev_err(sdev->dev, "failed to pause pipeline %d\n", 115 - swidget->pipeline_id); 116 - return ret; 117 - } 118 - } 119 - 120 - pipeline->state = SOF_IPC4_PIPE_PAUSED; 121 - 122 - if (pipeline->state == state) 123 - continue; 124 - 125 - /* then set the final state */ 126 - ret = sof_ipc4_set_pipeline_state(sdev, pipeline_widget->instance_id, state); 127 - if (ret < 0) { 128 - dev_err(sdev->dev, "failed to set state %d for pipeline %d\n", 129 - state, swidget->pipeline_id); 130 - break; 131 - } 132 - 133 - pipeline->state = state; 220 + /* return if all pipelines are in the requested state already */ 221 + if (!trigger_list->count) { 222 + ret = 0; 223 + goto free; 134 224 } 135 225 226 + /* no need to pause before reset or before pause release */ 227 + if (state == SOF_IPC4_PIPE_RESET || cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE) 228 + goto skip_pause_transition; 229 + 230 + /* 231 + * set paused state for pipelines if the final state is PAUSED or when the pipeline 232 + * is set to RUNNING for the first time after the PCM is started. 233 + */ 234 + ret = sof_ipc4_set_multi_pipeline_state(sdev, SOF_IPC4_PIPE_PAUSED, trigger_list); 235 + if (ret < 0) { 236 + dev_err(sdev->dev, "failed to pause all pipelines\n"); 237 + goto free; 238 + } 239 + 240 + /* update PAUSED state for all pipelines just triggered */ 241 + for (i = 0; i < pipeline_list->count ; i++) { 242 + spipe = pipeline_list->pipelines[i]; 243 + sof_ipc4_update_pipeline_state(sdev, SOF_IPC4_PIPE_PAUSED, cmd, spipe, 244 + trigger_list); 245 + } 246 + 247 + /* return if this is the final state */ 248 + if (state == SOF_IPC4_PIPE_PAUSED) 249 + goto free; 250 + skip_pause_transition: 251 + /* else set the RUNNING/RESET state in the DSP */ 252 + ret = sof_ipc4_set_multi_pipeline_state(sdev, state, trigger_list); 253 + if (ret < 0) { 254 + dev_err(sdev->dev, "failed to set final state %d for all pipelines\n", state); 255 + goto free; 256 + } 257 + 258 + /* update RUNNING/RESET state for all pipelines that were just triggered */ 259 + for (i = 0; i < pipeline_list->count; i++) { 260 + spipe = pipeline_list->pipelines[i]; 261 + sof_ipc4_update_pipeline_state(sdev, state, cmd, spipe, trigger_list); 262 + } 263 + 264 + free: 265 + mutex_unlock(&ipc4_data->pipeline_state_mutex); 266 + kfree(trigger_list); 136 267 return ret; 137 268 } 138 269 ··· 319 134 } 320 135 321 136 /* set the pipeline state */ 322 - return sof_ipc4_trigger_pipelines(component, substream, state); 137 + return sof_ipc4_trigger_pipelines(component, substream, state, cmd); 323 138 } 324 139 325 140 static int sof_ipc4_pcm_hw_free(struct snd_soc_component *component, 326 141 struct snd_pcm_substream *substream) 327 142 { 328 - return sof_ipc4_trigger_pipelines(component, substream, SOF_IPC4_PIPE_RESET); 143 + /* command is not relevant with RESET, so just pass 0 */ 144 + return sof_ipc4_trigger_pipelines(component, substream, SOF_IPC4_PIPE_RESET, 0); 329 145 } 330 146 331 147 static void ipc4_ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, const char *link_name, ··· 369 183 struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); 370 184 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); 371 185 struct sof_ipc4_copier *ipc4_copier; 372 - struct snd_soc_dpcm *dpcm; 373 186 374 187 if (!dai) { 375 188 dev_err(component->dev, "%s: No DAI found with name %s\n", __func__, ··· 390 205 rate->min = ipc4_copier->available_fmt.base_config->audio_fmt.sampling_frequency; 391 206 rate->max = rate->min; 392 207 393 - /* 394 - * Set trigger order for capture to SND_SOC_DPCM_TRIGGER_PRE. This is required 395 - * to ensure that the BE DAI pipeline gets stopped/suspended before the FE DAI 396 - * pipeline gets triggered and the pipeline widgets are freed. 397 - */ 398 - for_each_dpcm_fe(rtd, SNDRV_PCM_STREAM_CAPTURE, dpcm) { 399 - struct snd_soc_pcm_runtime *fe = dpcm->fe; 400 - 401 - fe->dai_link->trigger[SNDRV_PCM_STREAM_CAPTURE] = SND_SOC_DPCM_TRIGGER_PRE; 402 - } 403 - 404 208 switch (ipc4_copier->dai_type) { 405 209 case SOF_DAI_INTEL_SSP: 406 210 ipc4_ssp_dai_config_pcm_params_match(sdev, (char *)rtd->dai_link->name, params); ··· 401 227 return 0; 402 228 } 403 229 230 + static void sof_ipc4_pcm_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm) 231 + { 232 + struct snd_sof_pcm_stream_pipeline_list *pipeline_list; 233 + int stream; 234 + 235 + for_each_pcm_streams(stream) { 236 + pipeline_list = &spcm->stream[stream].pipeline_list; 237 + kfree(pipeline_list->pipelines); 238 + pipeline_list->pipelines = NULL; 239 + } 240 + } 241 + 242 + static int sof_ipc4_pcm_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm) 243 + { 244 + struct snd_sof_pcm_stream_pipeline_list *pipeline_list; 245 + struct sof_ipc4_fw_data *ipc4_data = sdev->private; 246 + int stream; 247 + 248 + for_each_pcm_streams(stream) { 249 + pipeline_list = &spcm->stream[stream].pipeline_list; 250 + 251 + /* allocate memory for max number of pipeline IDs */ 252 + pipeline_list->pipelines = kcalloc(ipc4_data->max_num_pipelines, 253 + sizeof(struct snd_sof_widget *), GFP_KERNEL); 254 + if (!pipeline_list->pipelines) { 255 + sof_ipc4_pcm_free(sdev, spcm); 256 + return -ENOMEM; 257 + } 258 + } 259 + 260 + return 0; 261 + } 262 + 404 263 const struct sof_ipc_pcm_ops ipc4_pcm_ops = { 405 264 .trigger = sof_ipc4_pcm_trigger, 406 265 .hw_free = sof_ipc4_pcm_hw_free, 407 266 .dai_link_fixup = sof_ipc4_pcm_dai_link_fixup, 267 + .pcm_setup = sof_ipc4_pcm_setup, 268 + .pcm_free = sof_ipc4_pcm_free, 408 269 };
+2
sound/soc/sof/ipc4-priv.h
··· 70 70 * base firmware 71 71 * 72 72 * @load_library: Callback function for platform dependent library loading 73 + * @pipeline_state_mutex: Mutex to protect pipeline triggers, ref counts, states and deletion 73 74 */ 74 75 struct sof_ipc4_fw_data { 75 76 u32 manifest_fw_hdr_offset; ··· 83 82 84 83 int (*load_library)(struct snd_sof_dev *sdev, 85 84 struct sof_ipc4_fw_library *fw_lib, bool reload); 85 + struct mutex pipeline_state_mutex; /* protect pipeline triggers, ref counts and states */ 86 86 }; 87 87 88 88 extern const struct sof_ipc_fw_loader_ops ipc4_loader_ops;
+40 -8
sound/soc/sof/ipc4-topology.c
··· 855 855 856 856 total = SOF_IPC4_FW_PAGE(task_mem + queue_mem); 857 857 858 - pipe_widget = swidget->pipe_widget; 858 + pipe_widget = swidget->spipe->pipe_widget; 859 859 pipeline = pipe_widget->private; 860 860 pipeline->mem_usage += total; 861 861 } ··· 969 969 struct sof_ipc4_pipeline *pipeline; 970 970 971 971 /* reset pipeline memory usage */ 972 - pipe_widget = swidget->pipe_widget; 972 + pipe_widget = swidget->spipe->pipe_widget; 973 973 pipeline = pipe_widget->private; 974 974 pipeline->mem_usage = 0; 975 975 ··· 1136 1136 struct snd_sof_widget *pipe_widget; 1137 1137 struct sof_ipc4_pipeline *pipeline; 1138 1138 1139 - pipe_widget = swidget->pipe_widget; 1139 + pipe_widget = swidget->spipe->pipe_widget; 1140 1140 pipeline = pipe_widget->private; 1141 1141 ipc4_copier = (struct sof_ipc4_copier *)swidget->private; 1142 1142 gtw_attr = ipc4_copier->gtw_attr; ··· 1495 1495 1496 1496 static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) 1497 1497 { 1498 - struct snd_sof_widget *pipe_widget = swidget->pipe_widget; 1498 + struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; 1499 1499 struct sof_ipc4_fw_data *ipc4_data = sdev->private; 1500 1500 struct sof_ipc4_pipeline *pipeline; 1501 1501 struct sof_ipc4_msg *msg; ··· 1625 1625 static int sof_ipc4_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) 1626 1626 { 1627 1627 struct sof_ipc4_fw_module *fw_module = swidget->module_info; 1628 + struct sof_ipc4_fw_data *ipc4_data = sdev->private; 1628 1629 int ret = 0; 1630 + 1631 + mutex_lock(&ipc4_data->pipeline_state_mutex); 1629 1632 1630 1633 /* freeing a pipeline frees all the widgets associated with it */ 1631 1634 if (swidget->id == snd_soc_dapm_scheduler) { ··· 1654 1651 } else { 1655 1652 ida_free(&fw_module->m_ida, swidget->instance_id); 1656 1653 } 1654 + 1655 + mutex_unlock(&ipc4_data->pipeline_state_mutex); 1657 1656 1658 1657 return ret; 1659 1658 } ··· 1810 1805 struct sof_ipc4_fw_module *sink_fw_module = sink_widget->module_info; 1811 1806 struct sof_ipc4_msg msg = {{ 0 }}; 1812 1807 u32 header, extension; 1813 - int ret; 1808 + int ret = 0; 1814 1809 1815 1810 dev_dbg(sdev->dev, "unbind modules %s:%d -> %s:%d\n", 1816 1811 src_widget->widget->name, sroute->src_queue_id, 1817 1812 sink_widget->widget->name, sroute->dst_queue_id); 1813 + 1814 + /* 1815 + * routes belonging to the same pipeline will be disconnected by the FW when the pipeline 1816 + * is freed. So avoid sending this IPC which will be ignored by the FW anyway. 1817 + */ 1818 + if (src_widget->spipe->pipe_widget == sink_widget->spipe->pipe_widget) 1819 + goto out; 1818 1820 1819 1821 header = src_fw_module->man4_module_entry.id; 1820 1822 header |= SOF_IPC4_MOD_INSTANCE(src_widget->instance_id); ··· 1841 1829 if (ret < 0) 1842 1830 dev_err(sdev->dev, "failed to unbind modules %s -> %s\n", 1843 1831 src_widget->widget->name, sink_widget->widget->name); 1844 - 1832 + out: 1845 1833 sof_ipc4_put_queue_id(sink_widget, sroute->dst_queue_id, SOF_PIN_TYPE_SINK); 1846 1834 sof_ipc4_put_queue_id(src_widget, sroute->src_queue_id, SOF_PIN_TYPE_SOURCE); 1847 1835 ··· 1851 1839 static int sof_ipc4_dai_config(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, 1852 1840 unsigned int flags, struct snd_sof_dai_config_data *data) 1853 1841 { 1854 - struct snd_sof_widget *pipe_widget = swidget->pipe_widget; 1842 + struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; 1855 1843 struct sof_ipc4_pipeline *pipeline = pipe_widget->private; 1856 1844 struct snd_sof_dai *dai = swidget->private; 1857 1845 struct sof_ipc4_gtw_attributes *gtw_attr; ··· 1874 1862 case SOF_DAI_INTEL_HDA: 1875 1863 gtw_attr = ipc4_copier->gtw_attr; 1876 1864 gtw_attr->lp_buffer_alloc = pipeline->lp_mode; 1865 + pipeline->skip_during_fe_trigger = true; 1877 1866 fallthrough; 1878 1867 case SOF_DAI_INTEL_ALH: 1879 1868 copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK; ··· 2031 2018 for_each_pcm_streams(dir) { 2032 2019 struct snd_pcm_substream *substream = spcm->stream[dir].substream; 2033 2020 2034 - if (!substream || !substream->runtime) 2021 + if (!substream || !substream->runtime || spcm->stream[dir].suspend_ignored) 2035 2022 continue; 2036 2023 2037 2024 if (spcm->stream[dir].list) { ··· 2041 2028 } 2042 2029 } 2043 2030 } 2031 + return 0; 2032 + } 2033 + 2034 + static int sof_ipc4_link_setup(struct snd_sof_dev *sdev, struct snd_soc_dai_link *link) 2035 + { 2036 + if (link->no_pcm) 2037 + return 0; 2038 + 2039 + /* 2040 + * set default trigger order for all links. Exceptions to 2041 + * the rule will be handled in sof_pcm_dai_link_fixup() 2042 + * For playback, the sequence is the following: start BE, 2043 + * start FE, stop FE, stop BE; for Capture the sequence is 2044 + * inverted start FE, start BE, stop BE, stop FE 2045 + */ 2046 + link->trigger[SNDRV_PCM_STREAM_PLAYBACK] = SND_SOC_DPCM_TRIGGER_POST; 2047 + link->trigger[SNDRV_PCM_STREAM_CAPTURE] = SND_SOC_DPCM_TRIGGER_PRE; 2048 + 2044 2049 return 0; 2045 2050 } 2046 2051 ··· 2168 2137 .parse_manifest = sof_ipc4_parse_manifest, 2169 2138 .dai_get_clk = sof_ipc4_dai_get_clk, 2170 2139 .tear_down_all_pipelines = sof_ipc4_tear_down_all_pipelines, 2140 + .link_setup = sof_ipc4_link_setup, 2171 2141 };
+12
sound/soc/sof/ipc4-topology.h
··· 73 73 * @mem_usage: Memory usage 74 74 * @state: Pipeline state 75 75 * @msg: message structure for pipeline 76 + * @skip_during_fe_trigger: skip triggering this pipeline during the FE DAI trigger 76 77 */ 77 78 struct sof_ipc4_pipeline { 78 79 uint32_t priority; ··· 81 80 uint32_t mem_usage; 82 81 int state; 83 82 struct sof_ipc4_msg msg; 83 + bool skip_during_fe_trigger; 84 84 }; 85 + 86 + /** 87 + * struct sof_ipc4_multi_pipeline_data - multi pipeline trigger IPC data 88 + * @count: Number of pipelines to be triggered 89 + * @pipeline_ids: Flexible array of IDs of the pipelines to be triggered 90 + */ 91 + struct ipc4_pipeline_set_state_data { 92 + u32 count; 93 + DECLARE_FLEX_ARRAY(u32, pipeline_ids); 94 + } __packed; 85 95 86 96 /** 87 97 * struct sof_ipc4_available_audio_format - Available audio formats
+2
sound/soc/sof/ipc4.c
··· 662 662 { 663 663 struct sof_ipc4_fw_data *ipc4_data = sdev->private; 664 664 665 + mutex_init(&ipc4_data->pipeline_state_mutex); 666 + 665 667 xa_init_flags(&ipc4_data->fw_lib_xa, XA_FLAGS_ALLOC); 666 668 667 669 return 0;
+1 -4
sound/soc/sof/pcm.c
··· 282 282 const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); 283 283 struct snd_sof_pcm *spcm; 284 284 bool reset_hw_params = false; 285 - bool free_widget_list = false; 286 285 bool ipc_first = false; 287 286 int ret = 0; 288 287 ··· 325 326 spcm->stream[substream->stream].suspend_ignored = true; 326 327 return 0; 327 328 } 328 - free_widget_list = true; 329 329 fallthrough; 330 330 case SNDRV_PCM_TRIGGER_STOP: 331 331 ipc_first = true; ··· 351 353 352 354 /* free PCM if reset_hw_params is set and the STOP IPC is successful */ 353 355 if (!ret && reset_hw_params) 354 - ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, 355 - free_widget_list); 356 + ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, false); 356 357 357 358 return ret; 358 359 }
+156 -70
sound/soc/sof/sof-audio.c
··· 28 28 } 29 29 } 30 30 31 - int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) 31 + static int sof_widget_free_unlocked(struct snd_sof_dev *sdev, 32 + struct snd_sof_widget *swidget) 32 33 { 33 34 const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); 35 + struct snd_sof_widget *pipe_widget; 34 36 int err = 0; 35 37 int ret; 36 38 ··· 44 42 /* only free when use_count is 0 */ 45 43 if (--swidget->use_count) 46 44 return 0; 45 + 46 + pipe_widget = swidget->spipe->pipe_widget; 47 47 48 48 /* reset route setup status for all routes that contain this widget */ 49 49 sof_reset_route_setup_status(sdev, swidget); ··· 71 67 * skip for static pipelines 72 68 */ 73 69 if (swidget->dynamic_pipeline_widget && swidget->id != snd_soc_dapm_scheduler) { 74 - ret = sof_widget_free(sdev, swidget->pipe_widget); 70 + ret = sof_widget_free_unlocked(sdev, pipe_widget); 75 71 if (ret < 0 && !err) 76 72 err = ret; 77 - swidget->pipe_widget->complete = 0; 78 73 } 74 + 75 + /* clear pipeline complete */ 76 + if (swidget->id == snd_soc_dapm_scheduler) 77 + swidget->spipe->complete = 0; 79 78 80 79 if (!err) 81 80 dev_dbg(sdev->dev, "widget %s freed\n", swidget->widget->name); 82 81 83 82 return err; 84 83 } 84 + 85 + int sof_widget_free(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) 86 + { 87 + int ret; 88 + 89 + mutex_lock(&swidget->setup_mutex); 90 + ret = sof_widget_free_unlocked(sdev, swidget); 91 + mutex_unlock(&swidget->setup_mutex); 92 + 93 + return ret; 94 + } 85 95 EXPORT_SYMBOL(sof_widget_free); 86 96 87 - int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) 97 + static int sof_widget_setup_unlocked(struct snd_sof_dev *sdev, 98 + struct snd_sof_widget *swidget) 88 99 { 89 100 const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); 101 + bool use_count_decremented = false; 90 102 int ret; 91 103 92 104 /* skip if there is no private data */ ··· 123 103 * widget in the pipeline is freed. Skip setting up scheduler widget for static pipelines. 124 104 */ 125 105 if (swidget->dynamic_pipeline_widget && swidget->id != snd_soc_dapm_scheduler) { 126 - if (!swidget->pipe_widget) { 127 - dev_err(sdev->dev, "No scheduler widget set for %s\n", 128 - swidget->widget->name); 106 + if (!swidget->spipe || !swidget->spipe->pipe_widget) { 107 + dev_err(sdev->dev, "No pipeline set for %s\n", swidget->widget->name); 129 108 ret = -EINVAL; 130 109 goto use_count_dec; 131 110 } 132 111 133 - ret = sof_widget_setup(sdev, swidget->pipe_widget); 112 + ret = sof_widget_setup_unlocked(sdev, swidget->spipe->pipe_widget); 134 113 if (ret < 0) 135 114 goto use_count_dec; 136 115 } ··· 173 154 174 155 widget_free: 175 156 /* widget use_count and core ref_count will both be decremented by sof_widget_free() */ 176 - sof_widget_free(sdev, swidget); 157 + sof_widget_free_unlocked(sdev, swidget); 158 + use_count_decremented = true; 177 159 core_put: 178 160 snd_sof_dsp_core_put(sdev, swidget->core); 179 161 pipe_widget_free: 180 162 if (swidget->id != snd_soc_dapm_scheduler) 181 - sof_widget_free(sdev, swidget->pipe_widget); 163 + sof_widget_free_unlocked(sdev, swidget->spipe->pipe_widget); 182 164 use_count_dec: 183 - swidget->use_count--; 165 + if (!use_count_decremented) 166 + swidget->use_count--; 167 + 168 + return ret; 169 + } 170 + 171 + int sof_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) 172 + { 173 + int ret; 174 + 175 + mutex_lock(&swidget->setup_mutex); 176 + ret = sof_widget_setup_unlocked(sdev, swidget); 177 + mutex_unlock(&swidget->setup_mutex); 178 + 184 179 return ret; 185 180 } 186 181 EXPORT_SYMBOL(sof_widget_setup); ··· 274 241 if (!widget->dobj.private) 275 242 continue; 276 243 277 - snd_soc_dapm_widget_for_each_sink_path(widget, p) 244 + snd_soc_dapm_widget_for_each_sink_path(widget, p) { 245 + if (!widget_in_list(list, p->sink)) 246 + continue; 247 + 278 248 if (p->sink->dobj.private) { 279 249 ret = sof_route_setup(sdev, widget, p->sink); 280 250 if (ret < 0) 281 251 return ret; 282 252 } 253 + } 283 254 } 284 255 } else { 285 256 for_each_dapm_widgets(list, i, widget) { 286 257 if (!widget->dobj.private) 287 258 continue; 288 259 289 - snd_soc_dapm_widget_for_each_source_path(widget, p) 260 + snd_soc_dapm_widget_for_each_source_path(widget, p) { 261 + if (!widget_in_list(list, p->source)) 262 + continue; 263 + 290 264 if (p->source->dobj.private) { 291 265 ret = sof_route_setup(sdev, p->source, widget); 292 266 if (ret < 0) 293 267 return ret; 294 268 } 269 + } 295 270 } 296 271 } 297 272 ··· 307 266 } 308 267 309 268 static void 310 - sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget) 269 + sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget, 270 + struct snd_soc_dapm_widget_list *list) 311 271 { 312 272 const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); 313 273 struct snd_sof_widget *swidget = widget->dobj.private; ··· 329 287 sink_unprepare: 330 288 /* unprepare all widgets in the sink paths */ 331 289 snd_soc_dapm_widget_for_each_sink_path(widget, p) { 290 + if (!widget_in_list(list, p->sink)) 291 + continue; 332 292 if (!p->walking && p->sink->dobj.private) { 333 293 p->walking = true; 334 - sof_unprepare_widgets_in_path(sdev, p->sink); 294 + sof_unprepare_widgets_in_path(sdev, p->sink, list); 335 295 p->walking = false; 336 296 } 337 297 } ··· 343 299 sof_prepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget, 344 300 struct snd_pcm_hw_params *fe_params, 345 301 struct snd_sof_platform_stream_params *platform_params, 346 - struct snd_pcm_hw_params *pipeline_params, int dir) 302 + struct snd_pcm_hw_params *pipeline_params, int dir, 303 + struct snd_soc_dapm_widget_list *list) 347 304 { 348 305 const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); 349 306 struct snd_sof_widget *swidget = widget->dobj.private; ··· 372 327 sink_prepare: 373 328 /* prepare all widgets in the sink paths */ 374 329 snd_soc_dapm_widget_for_each_sink_path(widget, p) { 330 + if (!widget_in_list(list, p->sink)) 331 + continue; 375 332 if (!p->walking && p->sink->dobj.private) { 376 333 p->walking = true; 377 334 ret = sof_prepare_widgets_in_path(sdev, p->sink, fe_params, 378 - platform_params, pipeline_params, dir); 335 + platform_params, pipeline_params, dir, 336 + list); 379 337 p->walking = false; 380 338 if (ret < 0) { 381 339 /* unprepare the source widget */ ··· 399 351 * (DAI type for capture, AIF type for playback) 400 352 */ 401 353 static int sof_free_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget, 402 - int dir) 354 + int dir, struct snd_sof_pcm *spcm) 403 355 { 356 + struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list; 404 357 struct snd_soc_dapm_path *p; 405 358 int err; 406 359 int ret = 0; 407 360 408 - /* free all widgets even in case of error to keep use counts balanced */ 361 + if (widget->dobj.private) { 362 + err = sof_widget_free(sdev, widget->dobj.private); 363 + if (err < 0) 364 + ret = err; 365 + } 366 + 367 + /* free all widgets in the sink paths even in case of error to keep use counts balanced */ 409 368 snd_soc_dapm_widget_for_each_sink_path(widget, p) { 410 - if (!p->walking && p->sink->dobj.private && widget->dobj.private) { 369 + if (!p->walking) { 370 + if (!widget_in_list(list, p->sink)) 371 + continue; 372 + 411 373 p->walking = true; 412 - if (WIDGET_IS_AIF_OR_DAI(widget->id)) { 413 - err = sof_widget_free(sdev, widget->dobj.private); 414 - if (err < 0) 415 - ret = err; 416 - } 417 374 418 - err = sof_widget_free(sdev, p->sink->dobj.private); 419 - if (err < 0) 420 - ret = err; 421 - 422 - err = sof_free_widgets_in_path(sdev, p->sink, dir); 375 + err = sof_free_widgets_in_path(sdev, p->sink, dir, spcm); 423 376 if (err < 0) 424 377 ret = err; 425 378 p->walking = false; ··· 436 387 * The error path in this function ensures that all successfully set up widgets getting freed. 437 388 */ 438 389 static int sof_set_up_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget, 439 - int dir) 390 + int dir, struct snd_sof_pcm *spcm) 440 391 { 392 + struct snd_sof_pcm_stream_pipeline_list *pipeline_list = &spcm->stream[dir].pipeline_list; 393 + struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list; 394 + struct snd_sof_widget *swidget = widget->dobj.private; 395 + struct snd_sof_pipeline *spipe; 441 396 struct snd_soc_dapm_path *p; 442 397 int ret; 443 398 399 + if (swidget) { 400 + int i; 401 + 402 + ret = sof_widget_setup(sdev, widget->dobj.private); 403 + if (ret < 0) 404 + return ret; 405 + 406 + /* skip populating the pipe_widgets array if it is NULL */ 407 + if (!pipeline_list->pipelines) 408 + goto sink_setup; 409 + 410 + /* 411 + * Add the widget's pipe_widget to the list of pipelines to be triggered if not 412 + * already in the list. This will result in the pipelines getting added in the 413 + * order source to sink. 414 + */ 415 + for (i = 0; i < pipeline_list->count; i++) { 416 + spipe = pipeline_list->pipelines[i]; 417 + if (spipe == swidget->spipe) 418 + break; 419 + } 420 + 421 + if (i == pipeline_list->count) { 422 + pipeline_list->count++; 423 + pipeline_list->pipelines[i] = swidget->spipe; 424 + } 425 + } 426 + 427 + sink_setup: 444 428 snd_soc_dapm_widget_for_each_sink_path(widget, p) { 445 - if (!p->walking && p->sink->dobj.private && widget->dobj.private) { 429 + if (!p->walking) { 430 + if (!widget_in_list(list, p->sink)) 431 + continue; 432 + 446 433 p->walking = true; 447 - if (WIDGET_IS_AIF_OR_DAI(widget->id)) { 448 - ret = sof_widget_setup(sdev, widget->dobj.private); 449 - if (ret < 0) 450 - goto out; 451 - } 452 434 453 - ret = sof_widget_setup(sdev, p->sink->dobj.private); 454 - if (ret < 0) { 455 - if (WIDGET_IS_AIF_OR_DAI(widget->id)) 456 - sof_widget_free(sdev, widget->dobj.private); 457 - goto out; 458 - } 459 - 460 - ret = sof_set_up_widgets_in_path(sdev, p->sink, dir); 461 - if (ret < 0) { 462 - if (WIDGET_IS_AIF_OR_DAI(widget->id)) 463 - sof_widget_free(sdev, widget->dobj.private); 464 - sof_widget_free(sdev, p->sink->dobj.private); 465 - } 466 - out: 435 + ret = sof_set_up_widgets_in_path(sdev, p->sink, dir, spcm); 467 436 p->walking = false; 468 - if (ret < 0) 437 + if (ret < 0) { 438 + if (swidget) 439 + sof_widget_free(sdev, swidget); 469 440 return ret; 441 + } 470 442 } 471 443 } 472 444 ··· 495 425 } 496 426 497 427 static int 498 - sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget_list *list, 428 + sof_walk_widgets_in_order(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, 499 429 struct snd_pcm_hw_params *fe_params, 500 430 struct snd_sof_platform_stream_params *platform_params, int dir, 501 431 enum sof_widget_op op) 502 432 { 433 + struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list; 503 434 struct snd_soc_dapm_widget *widget; 504 435 char *str; 505 436 int ret = 0; 506 437 int i; 438 + 439 + if (!list) 440 + return 0; 507 441 508 442 for_each_dapm_widgets(list, i, widget) { 509 443 /* starting widget for playback is AIF type */ ··· 520 446 521 447 switch (op) { 522 448 case SOF_WIDGET_SETUP: 523 - ret = sof_set_up_widgets_in_path(sdev, widget, dir); 449 + ret = sof_set_up_widgets_in_path(sdev, widget, dir, spcm); 524 450 str = "set up"; 525 451 break; 526 452 case SOF_WIDGET_FREE: 527 - ret = sof_free_widgets_in_path(sdev, widget, dir); 453 + ret = sof_free_widgets_in_path(sdev, widget, dir, spcm); 528 454 str = "free"; 529 455 break; 530 456 case SOF_WIDGET_PREPARE: ··· 540 466 */ 541 467 memcpy(&pipeline_params, fe_params, sizeof(*fe_params)); 542 468 543 - ret = sof_prepare_widgets_in_path(sdev, widget, fe_params, 544 - platform_params, &pipeline_params, dir); 469 + ret = sof_prepare_widgets_in_path(sdev, widget, fe_params, platform_params, 470 + &pipeline_params, dir, list); 545 471 break; 546 472 } 547 473 case SOF_WIDGET_UNPREPARE: 548 - sof_unprepare_widgets_in_path(sdev, widget); 474 + sof_unprepare_widgets_in_path(sdev, widget, list); 549 475 break; 550 476 default: 551 477 dev_err(sdev->dev, "Invalid widget op %d\n", op); ··· 578 504 * Prepare widgets for set up. The prepare step is used to allocate memory, assign 579 505 * instance ID and pick the widget configuration based on the runtime PCM params. 580 506 */ 581 - ret = sof_walk_widgets_in_order(sdev, list, fe_params, platform_params, 507 + ret = sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params, 582 508 dir, SOF_WIDGET_PREPARE); 583 509 if (ret < 0) 584 510 return ret; 585 511 586 512 /* Set up is used to send the IPC to the DSP to create the widget */ 587 - ret = sof_walk_widgets_in_order(sdev, list, fe_params, platform_params, 513 + ret = sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params, 588 514 dir, SOF_WIDGET_SETUP); 589 515 if (ret < 0) { 590 - ret = sof_walk_widgets_in_order(sdev, list, fe_params, platform_params, 516 + ret = sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params, 591 517 dir, SOF_WIDGET_UNPREPARE); 592 518 return ret; 593 519 } ··· 604 530 for_each_dapm_widgets(list, i, widget) { 605 531 struct snd_sof_widget *swidget = widget->dobj.private; 606 532 struct snd_sof_widget *pipe_widget; 533 + struct snd_sof_pipeline *spipe; 607 534 608 535 if (!swidget) 609 536 continue; 610 537 611 - pipe_widget = swidget->pipe_widget; 538 + spipe = swidget->spipe; 539 + if (!spipe) { 540 + dev_err(sdev->dev, "no pipeline found for %s\n", 541 + swidget->widget->name); 542 + ret = -EINVAL; 543 + goto widget_free; 544 + } 545 + 546 + pipe_widget = spipe->pipe_widget; 612 547 if (!pipe_widget) { 613 548 dev_err(sdev->dev, "error: no pipeline widget found for %s\n", 614 549 swidget->widget->name); ··· 625 542 goto widget_free; 626 543 } 627 544 628 - if (pipe_widget->complete) 545 + if (spipe->complete) 629 546 continue; 630 547 631 548 if (tplg_ops && tplg_ops->pipeline_complete) { 632 - pipe_widget->complete = tplg_ops->pipeline_complete(sdev, pipe_widget); 633 - if (pipe_widget->complete < 0) { 634 - ret = pipe_widget->complete; 549 + spipe->complete = tplg_ops->pipeline_complete(sdev, pipe_widget); 550 + if (spipe->complete < 0) { 551 + ret = spipe->complete; 635 552 goto widget_free; 636 553 } 637 554 } ··· 640 557 return 0; 641 558 642 559 widget_free: 643 - sof_walk_widgets_in_order(sdev, list, fe_params, platform_params, dir, 560 + sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params, dir, 644 561 SOF_WIDGET_FREE); 645 - sof_walk_widgets_in_order(sdev, list, NULL, NULL, dir, SOF_WIDGET_UNPREPARE); 562 + sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_UNPREPARE); 646 563 647 564 return ret; 648 565 } 649 566 650 567 int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir) 651 568 { 569 + struct snd_sof_pcm_stream_pipeline_list *pipeline_list = &spcm->stream[dir].pipeline_list; 652 570 struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list; 653 571 int ret; 654 572 ··· 658 574 return 0; 659 575 660 576 /* send IPC to free widget in the DSP */ 661 - ret = sof_walk_widgets_in_order(sdev, list, NULL, NULL, dir, SOF_WIDGET_FREE); 577 + ret = sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_FREE); 662 578 663 579 /* unprepare the widget */ 664 - sof_walk_widgets_in_order(sdev, list, NULL, NULL, dir, SOF_WIDGET_UNPREPARE); 580 + sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_UNPREPARE); 665 581 666 582 snd_soc_dapm_dai_free_widgets(&list); 667 583 spcm->stream[dir].list = NULL; 584 + 585 + pipeline_list->count = 0; 668 586 669 587 return ret; 670 588 }
+47 -7
sound/soc/sof/sof-audio.h
··· 85 85 struct snd_sof_route; 86 86 struct snd_sof_control; 87 87 struct snd_sof_dai; 88 + struct snd_sof_pcm; 88 89 89 90 struct snd_sof_dai_config_data { 90 91 int dai_index; ··· 98 97 * @hw_free: Function pointer for hw_free 99 98 * @trigger: Function pointer for trigger 100 99 * @dai_link_fixup: Function pointer for DAI link fixup 100 + * @pcm_setup: Function pointer for IPC-specific PCM set up that can be used for allocating 101 + * additional memory in the SOF PCM stream structure 102 + * @pcm_free: Function pointer for PCM free that can be used for freeing any 103 + * additional memory in the SOF PCM stream structure 101 104 */ 102 105 struct sof_ipc_pcm_ops { 103 106 int (*hw_params)(struct snd_soc_component *component, struct snd_pcm_substream *substream, ··· 111 106 int (*trigger)(struct snd_soc_component *component, struct snd_pcm_substream *substream, 112 107 int cmd); 113 108 int (*dai_link_fixup)(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params); 109 + int (*pcm_setup)(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm); 110 + void (*pcm_free)(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm); 114 111 }; 115 112 116 113 /** ··· 187 180 * @set_up_all_pipelines: Function pointer for setting up all topology pipelines 188 181 * @tear_down_all_pipelines: Function pointer for tearing down all topology pipelines 189 182 * @parse_manifest: Function pointer for ipc4 specific parsing of topology manifest 183 + * @link_setup: Function pointer for IPC-specific DAI link set up 190 184 * 191 185 * Note: function pointers (ops) are optional 192 186 */ ··· 209 201 int (*tear_down_all_pipelines)(struct snd_sof_dev *sdev, bool verify); 210 202 int (*parse_manifest)(struct snd_soc_component *scomp, int index, 211 203 struct snd_soc_tplg_manifest *man); 204 + int (*link_setup)(struct snd_sof_dev *sdev, struct snd_soc_dai_link *link); 212 205 }; 213 206 214 207 /** struct snd_sof_tuple - Tuple info ··· 285 276 int count; 286 277 }; 287 278 279 + /** 280 + * struct snd_sof_pcm_stream_pipeline_list - List of pipelines associated with a PCM stream 281 + * @count: number of pipeline widgets in the @pipe_widgets array 282 + * @pipelines: array of pipelines 283 + */ 284 + struct snd_sof_pcm_stream_pipeline_list { 285 + u32 count; 286 + struct snd_sof_pipeline **pipelines; 287 + }; 288 + 288 289 /* PCM stream, mapped to FW component */ 289 290 struct snd_sof_pcm_stream { 290 291 u32 comp_id; ··· 310 291 * active or not while suspending the stream 311 292 */ 312 293 bool suspend_ignored; 294 + struct snd_sof_pcm_stream_pipeline_list pipeline_list; 313 295 }; 314 296 315 297 /* ALSA SOF PCM device */ ··· 383 363 int comp_id; 384 364 int pipeline_id; 385 365 /* 386 - * complete flag is used to indicate that pipeline set up is complete for scheduler type 387 - * widgets. It is unused for all other widget types. 388 - */ 389 - int complete; 390 - /* 391 366 * the prepared flag is used to indicate that a widget has been prepared for getting set 392 367 * up in the DSP. 393 368 */ 394 369 bool prepared; 395 - int use_count; /* use_count will be protected by the PCM mutex held by the core */ 370 + 371 + struct mutex setup_mutex; /* to protect the swidget setup and free operations */ 372 + 373 + /* 374 + * use_count is protected by the PCM mutex held by the core and the 375 + * setup_mutex against non stream domain races (kcontrol access for 376 + * example) 377 + */ 378 + int use_count; 379 + 396 380 int core; 397 381 int id; /* id is the DAPM widget type */ 398 382 /* ··· 417 393 418 394 struct snd_soc_dapm_widget *widget; 419 395 struct list_head list; /* list in sdev widget list */ 420 - struct snd_sof_widget *pipe_widget; 396 + struct snd_sof_pipeline *spipe; 421 397 void *module_info; 422 398 423 399 const guid_t uuid; ··· 453 429 struct ida sink_queue_ida; 454 430 455 431 void *private; /* core does not touch this */ 432 + }; 433 + 434 + /** struct snd_sof_pipeline - ASoC SOF pipeline 435 + * @pipe_widget: Pointer to the pipeline widget 436 + * @started_count: Count of number of PCM's that have started this pipeline 437 + * @paused_count: Count of number of PCM's that have started and have currently paused this 438 + pipeline 439 + * @complete: flag used to indicate that pipeline set up is complete. 440 + * @list: List item in sdev pipeline_list 441 + */ 442 + struct snd_sof_pipeline { 443 + struct snd_sof_widget *pipe_widget; 444 + int started_count; 445 + int paused_count; 446 + int complete; 447 + struct list_head list; 456 448 }; 457 449 458 450 /* ASoC SOF DAPM route */
+1
sound/soc/sof/sof-priv.h
··· 578 578 struct list_head pcm_list; 579 579 struct list_head kcontrol_list; 580 580 struct list_head widget_list; 581 + struct list_head pipeline_list; 581 582 struct list_head dai_list; 582 583 struct list_head dai_link_list; 583 584 struct list_head route_list;
+72 -42
sound/soc/sof/topology.c
··· 1402 1402 swidget->scomp = scomp; 1403 1403 swidget->widget = w; 1404 1404 swidget->comp_id = sdev->next_comp_id++; 1405 - swidget->complete = 0; 1406 1405 swidget->id = w->id; 1407 1406 swidget->pipeline_id = index; 1408 1407 swidget->private = NULL; 1408 + mutex_init(&swidget->setup_mutex); 1409 + 1409 1410 ida_init(&swidget->src_queue_ida); 1410 1411 ida_init(&swidget->sink_queue_ida); 1411 1412 ··· 1554 1553 } 1555 1554 } 1556 1555 1556 + /* create and add pipeline for scheduler type widgets */ 1557 + if (w->id == snd_soc_dapm_scheduler) { 1558 + struct snd_sof_pipeline *spipe; 1559 + 1560 + spipe = kzalloc(sizeof(*spipe), GFP_KERNEL); 1561 + if (!spipe) { 1562 + kfree(swidget->private); 1563 + kfree(swidget->tuples); 1564 + kfree(swidget); 1565 + return -ENOMEM; 1566 + } 1567 + 1568 + spipe->pipe_widget = swidget; 1569 + swidget->spipe = spipe; 1570 + list_add(&spipe->list, &sdev->pipeline_list); 1571 + } 1572 + 1557 1573 w->dobj.private = swidget; 1558 1574 list_add(&swidget->list, &sdev->widget_list); 1559 1575 return ret; ··· 1626 1608 sof_disconnect_dai_widget(scomp, widget); 1627 1609 1628 1610 break; 1611 + case snd_soc_dapm_scheduler: 1612 + { 1613 + struct snd_sof_pipeline *spipe = swidget->spipe; 1614 + 1615 + list_del(&spipe->list); 1616 + kfree(spipe); 1617 + swidget->spipe = NULL; 1618 + break; 1619 + } 1629 1620 default: 1630 1621 break; 1631 1622 } ··· 1696 1669 struct snd_soc_tplg_pcm *pcm, struct snd_soc_dai *dai) 1697 1670 { 1698 1671 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 1672 + const struct sof_ipc_pcm_ops *ipc_pcm_ops = sof_ipc_get_ops(sdev, pcm); 1699 1673 struct snd_soc_tplg_stream_caps *caps; 1700 1674 struct snd_soc_tplg_private *private = &pcm->priv; 1701 1675 struct snd_sof_pcm *spcm; ··· 1723 1695 1724 1696 spcm->pcm = *pcm; 1725 1697 dev_dbg(scomp->dev, "tplg: load pcm %s\n", pcm->dai_name); 1698 + 1699 + /* perform pcm set op */ 1700 + if (ipc_pcm_ops && ipc_pcm_ops->pcm_setup) { 1701 + ret = ipc_pcm_ops->pcm_setup(sdev, spcm); 1702 + if (ret < 0) 1703 + return ret; 1704 + } 1726 1705 1727 1706 dai_drv->dobj.private = spcm; 1728 1707 list_add(&spcm->list, &sdev->pcm_list); ··· 1808 1773 static int sof_dai_unload(struct snd_soc_component *scomp, 1809 1774 struct snd_soc_dobj *dobj) 1810 1775 { 1776 + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 1777 + const struct sof_ipc_pcm_ops *ipc_pcm_ops = sof_ipc_get_ops(sdev, pcm); 1811 1778 struct snd_sof_pcm *spcm = dobj->private; 1812 1779 1813 1780 /* free PCM DMA pages */ ··· 1818 1781 1819 1782 if (spcm->pcm.capture) 1820 1783 snd_dma_free_pages(&spcm->stream[SNDRV_PCM_STREAM_CAPTURE].page_table); 1784 + 1785 + /* perform pcm free op */ 1786 + if (ipc_pcm_ops && ipc_pcm_ops->pcm_free) 1787 + ipc_pcm_ops->pcm_free(sdev, spcm); 1821 1788 1822 1789 /* remove from list and free spcm */ 1823 1790 list_del(&spcm->list); ··· 1854 1813 } 1855 1814 link->platforms->name = dev_name(scomp->dev); 1856 1815 1857 - /* 1858 - * Set nonatomic property for FE dai links as their trigger action 1859 - * involves IPC's. 1860 - */ 1816 + if (tplg_ops && tplg_ops->link_setup) { 1817 + ret = tplg_ops->link_setup(sdev, link); 1818 + if (ret < 0) 1819 + return ret; 1820 + } 1821 + 1822 + /* Set nonatomic property for FE dai links as their trigger action involves IPC's */ 1861 1823 if (!link->no_pcm) { 1862 1824 link->nonatomic = true; 1863 - 1864 - /* 1865 - * set default trigger order for all links. Exceptions to 1866 - * the rule will be handled in sof_pcm_dai_link_fixup() 1867 - * For playback, the sequence is the following: start FE, 1868 - * start BE, stop BE, stop FE; for Capture the sequence is 1869 - * inverted start BE, start FE, stop FE, stop BE 1870 - */ 1871 - link->trigger[SNDRV_PCM_STREAM_PLAYBACK] = 1872 - SND_SOC_DPCM_TRIGGER_PRE; 1873 - link->trigger[SNDRV_PCM_STREAM_CAPTURE] = 1874 - SND_SOC_DPCM_TRIGGER_POST; 1875 - 1876 - /* nothing more to do for FE dai links */ 1877 1825 return 0; 1878 1826 } 1879 1827 ··· 2109 2079 } 2110 2080 2111 2081 /** 2112 - * sof_set_pipe_widget - Set pipe_widget for a component 2082 + * sof_set_widget_pipeline - Set pipeline for a component 2113 2083 * @sdev: pointer to struct snd_sof_dev 2114 - * @pipe_widget: pointer to struct snd_sof_widget of type snd_soc_dapm_scheduler 2084 + * @spipe: pointer to struct snd_sof_pipeline 2115 2085 * @swidget: pointer to struct snd_sof_widget that has the same pipeline ID as @pipe_widget 2116 2086 * 2117 2087 * Return: 0 if successful, -EINVAL on error. 2118 2088 * The function checks if @swidget is associated with any volatile controls. If so, setting 2119 2089 * the dynamic_pipeline_widget is disallowed. 2120 2090 */ 2121 - static int sof_set_pipe_widget(struct snd_sof_dev *sdev, struct snd_sof_widget *pipe_widget, 2122 - struct snd_sof_widget *swidget) 2091 + static int sof_set_widget_pipeline(struct snd_sof_dev *sdev, struct snd_sof_pipeline *spipe, 2092 + struct snd_sof_widget *swidget) 2123 2093 { 2094 + struct snd_sof_widget *pipe_widget = spipe->pipe_widget; 2124 2095 struct snd_sof_control *scontrol; 2125 2096 2126 2097 if (pipe_widget->dynamic_pipeline_widget) { ··· 2136 2105 } 2137 2106 } 2138 2107 2139 - /* set the pipe_widget and apply the dynamic_pipeline_widget_flag */ 2140 - swidget->pipe_widget = pipe_widget; 2108 + /* set the pipeline and apply the dynamic_pipeline_widget_flag */ 2109 + swidget->spipe = spipe; 2141 2110 swidget->dynamic_pipeline_widget = pipe_widget->dynamic_pipeline_widget; 2142 2111 2143 2112 return 0; ··· 2151 2120 struct snd_sof_widget *swidget, *comp_swidget; 2152 2121 const struct sof_ipc_tplg_widget_ops *widget_ops; 2153 2122 struct snd_sof_control *scontrol; 2123 + struct snd_sof_pipeline *spipe; 2154 2124 int ret; 2155 2125 2156 2126 widget_ops = tplg_ops ? tplg_ops->widget : NULL; ··· 2184 2152 } 2185 2153 2186 2154 /* set the pipe_widget and apply the dynamic_pipeline_widget_flag */ 2187 - list_for_each_entry(swidget, &sdev->widget_list, list) { 2188 - switch (swidget->id) { 2189 - case snd_soc_dapm_scheduler: 2190 - /* 2191 - * Apply the dynamic_pipeline_widget flag and set the pipe_widget field 2192 - * for all widgets that have the same pipeline ID as the scheduler widget 2193 - */ 2194 - list_for_each_entry(comp_swidget, &sdev->widget_list, list) 2195 - if (comp_swidget->pipeline_id == swidget->pipeline_id) { 2196 - ret = sof_set_pipe_widget(sdev, swidget, comp_swidget); 2197 - if (ret < 0) 2198 - return ret; 2199 - } 2200 - break; 2201 - default: 2202 - break; 2203 - } 2155 + list_for_each_entry(spipe, &sdev->pipeline_list, list) { 2156 + struct snd_sof_widget *pipe_widget = spipe->pipe_widget; 2157 + 2158 + /* 2159 + * Apply the dynamic_pipeline_widget flag and set the pipe_widget field 2160 + * for all widgets that have the same pipeline ID as the scheduler widget. 2161 + * Skip the scheduler widgets as they have their pipeline set during widget_ready 2162 + */ 2163 + list_for_each_entry(comp_swidget, &sdev->widget_list, list) 2164 + if (comp_swidget->widget->id != snd_soc_dapm_scheduler && 2165 + comp_swidget->pipeline_id == pipe_widget->pipeline_id) { 2166 + ret = sof_set_widget_pipeline(sdev, spipe, comp_swidget); 2167 + if (ret < 0) 2168 + return ret; 2169 + } 2204 2170 } 2205 2171 2206 2172 /* verify topology components loading including dynamic pipelines */