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

Configure Feed

Select the types of activity you want to include in your feed.

ASoC: SOF: Support for echoref (virtual DAI)

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

The series adds support for echo reference functionality by allowing
the capturing of playback audio right before it leaves the DSP.
For this to work correctly we need a virtual DAI that is also connected
to the echo reference capture device and in absence of playback a
signal generator generates silence to allow the capture to run.
When the real playback starts, application will start to receive the
playback audio to be usable for echo reference.

+370 -99
+3
include/uapi/sound/sof/tokens.h
··· 56 56 #define SOF_TKN_SCHED_LP_MODE 207 57 57 #define SOF_TKN_SCHED_MEM_USAGE 208 58 58 #define SOF_TKN_SCHED_USE_CHAIN_DMA 209 59 + #define SOF_TKN_SCHED_KCPS 210 60 + #define SOF_TKN_SCHED_DIRECTION 211 61 + #define SOF_TKN_SCHED_DIRECTION_VALID 212 59 62 60 63 /* volume */ 61 64 #define SOF_TKN_VOLUME_RAMP_STEP_TYPE 250
+41 -2
sound/soc/intel/boards/sof_sdw.c
··· 1186 1186 return 0; 1187 1187 } 1188 1188 1189 + static int create_echoref_dailink(struct snd_soc_card *card, 1190 + struct snd_soc_dai_link **dai_links, int *be_id) 1191 + { 1192 + struct device *dev = card->dev; 1193 + int ret; 1194 + char *name = devm_kasprintf(dev, GFP_KERNEL, "Loopback_Virtual"); 1195 + 1196 + if (!name) 1197 + return -ENOMEM; 1198 + 1199 + /* 1200 + * use dummy DAI names as this won't be connected to an actual DAI but just to establish a 1201 + * fe <-> be connection for loopback capture for echo reference 1202 + */ 1203 + ret = asoc_sdw_init_simple_dai_link(dev, *dai_links, be_id, name, 1204 + 0, 1, "Loopback Virtual Pin", "dummy", 1205 + snd_soc_dummy_dlc.name, snd_soc_dummy_dlc.dai_name, 1206 + 1, NULL, NULL); 1207 + if (ret) 1208 + return ret; 1209 + 1210 + (*dai_links)++; 1211 + 1212 + dev_dbg(dev, "Added echo reference DAI link\n"); 1213 + 1214 + return 0; 1215 + } 1216 + 1189 1217 static int sof_card_dai_links_create(struct snd_soc_card *card) 1190 1218 { 1191 1219 struct device *dev = card->dev; ··· 1322 1294 goto err_end; 1323 1295 } 1324 1296 1325 - /* allocate BE dailinks */ 1326 - num_links = sdw_be_num + ssp_num + dmic_num + hdmi_num + bt_num; 1297 + /* 1298 + * allocate BE dailinks, add an extra DAI link for echo reference capture. 1299 + * This should be the last DAI link and it is expected both for monolithic 1300 + * and functional SOF topologies to support echo reference. 1301 + */ 1302 + num_links = sdw_be_num + ssp_num + dmic_num + hdmi_num + bt_num + 1; 1327 1303 dai_links = devm_kcalloc(dev, num_links, sizeof(*dai_links), GFP_KERNEL); 1328 1304 if (!dai_links) { 1329 1305 ret = -ENOMEM; ··· 1374 1342 ret = create_bt_dailinks(card, &dai_links, &be_id); 1375 1343 if (ret) 1376 1344 goto err_end; 1345 + } 1346 + 1347 + /* dummy echo ref link. keep this as the last DAI link. The DAI link ID does not matter */ 1348 + ret = create_echoref_dailink(card, &dai_links, &be_id); 1349 + if (ret) { 1350 + dev_err(dev, "failed to create echo ref dai link: %d\n", ret); 1351 + goto err_end; 1377 1352 } 1378 1353 1379 1354 WARN_ON(codec_conf != card->codec_conf + card->num_configs);
+20 -2
sound/soc/sof/intel/hda-dai.c
··· 70 70 hda_dai_get_ops(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) 71 71 { 72 72 struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream); 73 - struct snd_sof_widget *swidget = w->dobj.private; 73 + struct snd_sof_widget *swidget; 74 74 struct snd_sof_dev *sdev; 75 75 struct snd_sof_dai *sdai; 76 76 77 - sdev = widget_to_sdev(w); 77 + /* 78 + * this is unlikely if the topology and the machine driver DAI links match. 79 + * But if there's a missing DAI link in topology, this will prevent a NULL pointer 80 + * dereference later on. 81 + */ 82 + if (!w) { 83 + dev_err(cpu_dai->dev, "%s: widget is NULL\n", __func__); 84 + return NULL; 85 + } 78 86 87 + sdev = widget_to_sdev(w); 88 + swidget = w->dobj.private; 79 89 if (!swidget) { 80 90 dev_err(sdev->dev, "%s: swidget is NULL\n", __func__); 81 91 return NULL; ··· 864 854 .capture = { 865 855 .channels_min = 1, 866 856 .channels_max = 4, 857 + }, 858 + }, 859 + { 860 + /* Virtual CPU DAI for Echo reference */ 861 + .name = "Loopback Virtual Pin", 862 + .capture = { 863 + .channels_min = 1, 864 + .channels_max = 2, 867 865 }, 868 866 }, 869 867 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
+2 -2
sound/soc/sof/intel/hda.h
··· 418 418 (HDA_DSP_BDL_SIZE / sizeof(struct sof_intel_dsp_bdl)) 419 419 420 420 /* Number of DAIs */ 421 - #define SOF_SKL_NUM_DAIS_NOCODEC 8 421 + #define SOF_SKL_NUM_DAIS_NOCODEC 9 422 422 423 423 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) 424 - #define SOF_SKL_NUM_DAIS 15 424 + #define SOF_SKL_NUM_DAIS 16 425 425 #else 426 426 #define SOF_SKL_NUM_DAIS SOF_SKL_NUM_DAIS_NOCODEC 427 427 #endif
+69 -13
sound/soc/sof/ipc4-topology.c
··· 76 76 offsetof(struct sof_ipc4_pipeline, core_id)}, 77 77 {SOF_TKN_SCHED_PRIORITY, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 78 78 offsetof(struct sof_ipc4_pipeline, priority)}, 79 + {SOF_TKN_SCHED_DIRECTION, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32, 80 + offsetof(struct sof_ipc4_pipeline, direction)}, 81 + {SOF_TKN_SCHED_DIRECTION, SND_SOC_TPLG_TUPLE_TYPE_BOOL, get_token_u16, 82 + offsetof(struct sof_ipc4_pipeline, direction_valid)}, 79 83 }; 80 84 81 85 static const struct sof_topology_token pipeline_tokens[] = { ··· 943 939 944 940 swidget->core = pipeline->core_id; 945 941 spipe->core_mask |= BIT(pipeline->core_id); 942 + if (pipeline->direction_valid) { 943 + spipe->direction = pipeline->direction; 944 + spipe->direction_valid = true; 945 + } 946 946 947 947 if (pipeline->use_chain_dma) { 948 948 dev_dbg(scomp->dev, "Set up chain DMA for %s\n", swidget->widget->name); ··· 962 954 goto err; 963 955 } 964 956 965 - dev_dbg(scomp->dev, "pipeline '%s': id %d, pri %d, core_id %u, lp mode %d\n", 957 + dev_dbg(scomp->dev, "pipeline '%s': id %d, pri %d, core_id %u, lp mode %d direction %d\n", 966 958 swidget->widget->name, swidget->pipeline_id, 967 - pipeline->priority, pipeline->core_id, pipeline->lp_mode); 959 + pipeline->priority, pipeline->core_id, pipeline->lp_mode, pipeline->direction); 968 960 969 961 swidget->private = pipeline; 970 962 ··· 2012 2004 return ret; 2013 2005 } 2014 2006 2007 + static void sof_ipc4_host_config(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, 2008 + struct snd_sof_platform_stream_params *platform_params) 2009 + { 2010 + struct sof_ipc4_copier *ipc4_copier = (struct sof_ipc4_copier *)swidget->private; 2011 + struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; 2012 + struct sof_ipc4_copier_data *copier_data = &ipc4_copier->data; 2013 + struct sof_ipc4_pipeline *pipeline = pipe_widget->private; 2014 + u32 host_dma_id = platform_params->stream_tag - 1; 2015 + 2016 + if (pipeline->use_chain_dma) { 2017 + pipeline->msg.primary &= ~SOF_IPC4_GLB_CHAIN_DMA_HOST_ID_MASK; 2018 + pipeline->msg.primary |= SOF_IPC4_GLB_CHAIN_DMA_HOST_ID(host_dma_id); 2019 + return; 2020 + } 2021 + 2022 + copier_data->gtw_cfg.node_id &= ~SOF_IPC4_NODE_INDEX_MASK; 2023 + copier_data->gtw_cfg.node_id |= SOF_IPC4_NODE_INDEX(host_dma_id); 2024 + } 2025 + 2015 2026 static int 2016 2027 sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget, 2017 2028 struct snd_pcm_hw_params *fe_params, ··· 2753 2726 int input_fmt_index = 0; 2754 2727 int ret; 2755 2728 2756 - input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, 2757 - &process->base_config, 2758 - pipeline_params, 2759 - available_fmt); 2760 - if (input_fmt_index < 0) 2761 - return input_fmt_index; 2729 + if (available_fmt->num_input_formats) { 2730 + input_fmt_index = sof_ipc4_init_input_audio_fmt(sdev, swidget, 2731 + &process->base_config, 2732 + pipeline_params, 2733 + available_fmt); 2734 + if (input_fmt_index < 0) 2735 + return input_fmt_index; 2736 + } 2762 2737 2763 2738 /* Configure output audio format only if the module supports output */ 2764 2739 if (available_fmt->num_output_formats) { ··· 2769 2740 u32 out_ref_rate, out_ref_channels; 2770 2741 int out_ref_valid_bits, out_ref_type; 2771 2742 2772 - in_fmt = &available_fmt->input_pin_fmts[input_fmt_index].audio_fmt; 2743 + if (available_fmt->num_input_formats) { 2744 + in_fmt = &available_fmt->input_pin_fmts[input_fmt_index].audio_fmt; 2773 2745 2774 - out_ref_rate = in_fmt->sampling_frequency; 2775 - out_ref_channels = SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg); 2776 - out_ref_valid_bits = SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg); 2777 - out_ref_type = sof_ipc4_fmt_cfg_to_type(in_fmt->fmt_cfg); 2746 + out_ref_rate = in_fmt->sampling_frequency; 2747 + out_ref_channels = 2748 + SOF_IPC4_AUDIO_FORMAT_CFG_CHANNELS_COUNT(in_fmt->fmt_cfg); 2749 + out_ref_valid_bits = 2750 + SOF_IPC4_AUDIO_FORMAT_CFG_V_BIT_DEPTH(in_fmt->fmt_cfg); 2751 + out_ref_type = sof_ipc4_fmt_cfg_to_type(in_fmt->fmt_cfg); 2752 + } else { 2753 + /* for modules without input formats, use FE params as reference */ 2754 + out_ref_rate = params_rate(fe_params); 2755 + out_ref_channels = params_channels(fe_params); 2756 + ret = sof_ipc4_get_sample_type(sdev, fe_params); 2757 + if (ret < 0) 2758 + return ret; 2759 + out_ref_type = (u32)ret; 2760 + 2761 + out_ref_valid_bits = sof_ipc4_get_valid_bits(sdev, fe_params); 2762 + if (out_ref_valid_bits < 0) 2763 + return out_ref_valid_bits; 2764 + } 2778 2765 2779 2766 output_fmt_index = sof_ipc4_init_output_audio_fmt(sdev, swidget, 2780 2767 &process->base_config, ··· 2817 2772 BIT(SNDRV_PCM_HW_PARAM_RATE)); 2818 2773 if (ret) 2819 2774 return ret; 2775 + } 2776 + 2777 + /* set base cfg to match the first output format if there are no input formats */ 2778 + if (!available_fmt->num_input_formats) { 2779 + struct sof_ipc4_audio_format *out_fmt; 2780 + 2781 + out_fmt = &available_fmt->output_pin_fmts[0].audio_fmt; 2782 + 2783 + /* copy output format */ 2784 + memcpy(&process->base_config.audio_fmt, out_fmt, sizeof(*out_fmt)); 2820 2785 } 2821 2786 } 2822 2787 ··· 3984 3929 .dai_get_param = sof_ipc4_dai_get_param, 3985 3930 .tear_down_all_pipelines = sof_ipc4_tear_down_all_pipelines, 3986 3931 .link_setup = sof_ipc4_link_setup, 3932 + .host_config = sof_ipc4_host_config, 3987 3933 };
+4
sound/soc/sof/ipc4-topology.h
··· 150 150 * @use_chain_dma: flag to indicate if the firmware shall use chained DMA 151 151 * @msg: message structure for pipeline 152 152 * @skip_during_fe_trigger: skip triggering this pipeline during the FE DAI trigger 153 + * @direction_valid: flag indicating if valid direction is set in topology 154 + * @direction: pipeline direction set in topology if direction_valid is true 153 155 */ 154 156 struct sof_ipc4_pipeline { 155 157 uint32_t priority; ··· 162 160 bool use_chain_dma; 163 161 struct sof_ipc4_msg msg; 164 162 bool skip_during_fe_trigger; 163 + bool direction_valid; 164 + u32 direction; 165 165 }; 166 166 167 167 /**
+65 -16
sound/soc/sof/pcm.c
··· 88 88 89 89 spcm->stream[dir].list = list; 90 90 91 - ret = sof_widget_list_setup(sdev, spcm, params, platform_params, dir); 91 + ret = sof_widget_list_prepare(sdev, spcm, params, platform_params, dir); 92 92 if (ret < 0) { 93 - spcm_err(spcm, dir, "Widget list set up failed\n"); 93 + spcm_err(spcm, dir, "widget list prepare failed\n"); 94 94 spcm->stream[dir].list = NULL; 95 95 snd_soc_dapm_dai_free_widgets(&list); 96 96 return ret; ··· 100 100 return 0; 101 101 } 102 102 103 + static struct snd_sof_widget *snd_sof_find_swidget_by_comp_id(struct snd_sof_dev *sdev, 104 + int comp_id) 105 + { 106 + struct snd_sof_widget *swidget; 107 + 108 + list_for_each_entry(swidget, &sdev->widget_list, list) { 109 + if (comp_id == swidget->comp_id) 110 + return swidget; 111 + } 112 + 113 + return NULL; 114 + } 115 + 103 116 static int sof_pcm_hw_params(struct snd_soc_component *component, 104 117 struct snd_pcm_substream *substream, 105 118 struct snd_pcm_hw_params *params) 106 119 { 107 120 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); 108 121 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 122 + const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); 109 123 const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); 110 - struct snd_sof_platform_stream_params platform_params = { 0 }; 124 + struct snd_sof_platform_stream_params *platform_params; 111 125 struct snd_pcm_runtime *runtime = substream->runtime; 126 + struct snd_sof_widget *host_widget; 112 127 struct snd_sof_pcm *spcm; 113 128 int ret; 114 129 ··· 159 144 spcm->prepared[substream->stream] = false; 160 145 } 161 146 162 - ret = snd_sof_pcm_platform_hw_params(sdev, substream, params, &platform_params); 147 + platform_params = &spcm->platform_params[substream->stream]; 148 + ret = snd_sof_pcm_platform_hw_params(sdev, substream, params, platform_params); 163 149 if (ret < 0) { 164 150 spcm_err(spcm, substream->stream, "platform hw params failed\n"); 165 151 return ret; ··· 168 152 169 153 /* if this is a repeated hw_params without hw_free, skip setting up widgets */ 170 154 if (!spcm->stream[substream->stream].list) { 171 - ret = sof_pcm_setup_connected_widgets(sdev, rtd, spcm, params, &platform_params, 155 + ret = sof_pcm_setup_connected_widgets(sdev, rtd, spcm, params, platform_params, 172 156 substream->stream); 173 157 if (ret < 0) 174 158 return ret; 159 + } 160 + 161 + if (!sdev->dspless_mode_selected) { 162 + int host_comp_id = spcm->stream[substream->stream].comp_id; 163 + 164 + host_widget = snd_sof_find_swidget_by_comp_id(sdev, host_comp_id); 165 + if (!host_widget) { 166 + spcm_err(spcm, substream->stream, 167 + "failed to find host widget with comp_id %d\n", host_comp_id); 168 + return -EINVAL; 169 + } 170 + 171 + /* set the host DMA ID */ 172 + if (tplg_ops && tplg_ops->host_config) 173 + tplg_ops->host_config(sdev, host_widget, platform_params); 175 174 } 176 175 177 176 /* create compressed page table for audio firmware */ ··· 199 168 if (ret < 0) 200 169 return ret; 201 170 } 202 - 203 - if (pcm_ops && pcm_ops->hw_params) { 204 - ret = pcm_ops->hw_params(component, substream, params, &platform_params); 205 - if (ret < 0) 206 - return ret; 207 - } 208 - 209 - spcm->prepared[substream->stream] = true; 210 171 211 172 /* save pcm hw_params */ 212 173 memcpy(&spcm->params[substream->stream], params, sizeof(*params)); ··· 304 281 305 282 ret = sof_pcm_stream_free(sdev, substream, spcm, substream->stream, true); 306 283 284 + /* unprepare and free the list of DAPM widgets */ 285 + sof_widget_list_unprepare(sdev, spcm, substream->stream); 286 + 307 287 cancel_work_sync(&spcm->stream[substream->stream].period_elapsed_work); 308 288 309 289 return ret; ··· 317 291 { 318 292 struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); 319 293 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); 294 + const struct sof_ipc_pcm_ops *pcm_ops = sof_ipc_get_ops(sdev, pcm); 295 + struct snd_sof_platform_stream_params *platform_params; 296 + struct snd_soc_dapm_widget_list *list; 297 + struct snd_pcm_hw_params *params; 320 298 struct snd_sof_pcm *spcm; 299 + int dir = substream->stream; 321 300 int ret; 322 301 323 302 /* nothing to do for BE */ ··· 348 317 return ret; 349 318 } 350 319 351 - /* set hw_params */ 352 - ret = sof_pcm_hw_params(component, 353 - substream, &spcm->params[substream->stream]); 320 + ret = sof_pcm_hw_params(component, substream, &spcm->params[substream->stream]); 354 321 if (ret < 0) { 355 322 spcm_err(spcm, substream->stream, 356 323 "failed to set hw_params after resume\n"); 357 324 return ret; 358 325 } 326 + 327 + list = spcm->stream[dir].list; 328 + params = &spcm->params[substream->stream]; 329 + platform_params = &spcm->platform_params[substream->stream]; 330 + ret = sof_widget_list_setup(sdev, spcm, params, platform_params, dir); 331 + if (ret < 0) { 332 + dev_err(sdev->dev, "failed widget list set up for pcm %d dir %d\n", 333 + spcm->pcm.pcm_id, dir); 334 + spcm->stream[dir].list = NULL; 335 + snd_soc_dapm_dai_free_widgets(&list); 336 + return ret; 337 + } 338 + 339 + if (pcm_ops && pcm_ops->hw_params) { 340 + ret = pcm_ops->hw_params(component, substream, params, platform_params); 341 + if (ret < 0) 342 + return ret; 343 + } 344 + 345 + spcm->prepared[substream->stream] = true; 359 346 360 347 return 0; 361 348 }
+151 -64
sound/soc/sof/sof-audio.c
··· 13 13 #include "sof-audio.h" 14 14 #include "ops.h" 15 15 16 + /* 17 + * Check if a DAI widget is an aggregated DAI. Aggregated DAI's have names ending in numbers 18 + * starting with 0. For example: in the case of a SDW speaker with 2 amps, the topology contains 19 + * 2 DAI's names alh-copier.SDW1.Playback.0 and alh-copier-SDW1.Playback.1. In this case, only the 20 + * DAI alh-copier.SDW1.Playback.0 is set up in the firmware. The other DAI, 21 + * alh-copier.SDW1.Playback.1 in topology is for the sake of completeness to show aggregation for 22 + * the speaker amp and does not need any firmware configuration. 23 + */ 24 + static bool is_aggregated_dai(struct snd_sof_widget *swidget) 25 + { 26 + return (WIDGET_IS_DAI(swidget->id) && 27 + isdigit(swidget->widget->name[strlen(swidget->widget->name) - 1]) && 28 + swidget->widget->name[strlen(swidget->widget->name) - 1] != '0'); 29 + } 30 + 16 31 static bool is_virtual_widget(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget, 17 32 const char *func) 18 33 { ··· 269 254 is_virtual_widget(sdev, sink_widget->widget, __func__)) 270 255 return 0; 271 256 257 + /* skip route if source/sink widget is not set up */ 258 + if (!src_widget->use_count || !sink_widget->use_count) 259 + return 0; 260 + 272 261 /* find route matching source and sink widgets */ 273 262 list_for_each_entry(sroute, &sdev->route_list, list) 274 263 if (sroute->src_widget == src_widget && sroute->sink_widget == sink_widget) { ··· 301 282 return 0; 302 283 } 303 284 285 + static bool sof_widget_in_same_direction(struct snd_sof_widget *swidget, int dir) 286 + { 287 + return swidget->spipe->direction == dir; 288 + } 289 + 290 + static int sof_set_up_same_dir_widget_routes(struct snd_sof_dev *sdev, 291 + struct snd_soc_dapm_widget *wsource, 292 + struct snd_soc_dapm_widget *wsink) 293 + { 294 + struct snd_sof_widget *src_widget = wsource->dobj.private; 295 + struct snd_sof_widget *sink_widget = wsink->dobj.private; 296 + 297 + /* 298 + * skip setting up route if source and sink are in different directions (ex. playback and 299 + * echo ref) if the direction is set in topology. These will be set up later. It is enough 300 + * to check if the direction_valid is set for one of the widgets as all widgets will have 301 + * the direction set in topology if one is set. 302 + */ 303 + if (sink_widget->spipe && sink_widget->spipe->direction_valid && 304 + !sof_widget_in_same_direction(sink_widget, src_widget->spipe->direction)) 305 + return 0; 306 + 307 + return sof_route_setup(sdev, wsource, wsink); 308 + } 309 + 304 310 static int sof_setup_pipeline_connections(struct snd_sof_dev *sdev, 305 311 struct snd_soc_dapm_widget_list *list, int dir) 306 312 { 307 - const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); 308 313 struct snd_soc_dapm_widget *widget; 309 314 struct snd_sof_route *sroute; 310 315 struct snd_soc_dapm_path *p; ··· 351 308 continue; 352 309 353 310 if (p->sink->dobj.private) { 354 - ret = sof_route_setup(sdev, widget, p->sink); 311 + ret = sof_set_up_same_dir_widget_routes(sdev, widget, 312 + p->sink); 355 313 if (ret < 0) 356 314 return ret; 357 315 } ··· 368 324 continue; 369 325 370 326 if (p->source->dobj.private) { 371 - ret = sof_route_setup(sdev, p->source, widget); 327 + ret = sof_set_up_same_dir_widget_routes(sdev, p->source, 328 + widget); 372 329 if (ret < 0) 373 330 return ret; 374 331 } ··· 385 340 */ 386 341 list_for_each_entry(sroute, &sdev->route_list, list) { 387 342 bool src_widget_in_dapm_list, sink_widget_in_dapm_list; 388 - struct snd_sof_widget *swidget; 389 343 390 344 if (sroute->setup) 391 345 continue; ··· 393 349 sink_widget_in_dapm_list = widget_in_list(list, sroute->sink_widget->widget); 394 350 395 351 /* 396 - * if both source and sink are in the DAPM list, the route must already have been 397 - * set up above. And if neither are in the DAPM list, the route shouldn't be 398 - * handled now. 352 + * no need to set up the route if both the source and sink widgets are not in the 353 + * DAPM list 399 354 */ 400 - if (src_widget_in_dapm_list == sink_widget_in_dapm_list) 355 + if (!src_widget_in_dapm_list && !sink_widget_in_dapm_list) 401 356 continue; 402 357 403 358 /* 404 - * At this point either the source widget or the sink widget is in the DAPM list 405 - * with a route that might need to be set up. Check the use_count of the widget 406 - * that is not in the DAPM list to confirm if it is in use currently before setting 407 - * up the route. 359 + * set up the route only if both the source and sink widgets are in the DAPM list 360 + * but are in different directions. The ones in the same direction would already 361 + * have been set up in the previous loop. 408 362 */ 409 - if (src_widget_in_dapm_list) 410 - swidget = sroute->sink_widget; 411 - else 412 - swidget = sroute->src_widget; 363 + if (src_widget_in_dapm_list && sink_widget_in_dapm_list) { 364 + struct snd_sof_widget *src_widget, *sink_widget; 413 365 414 - scoped_guard(mutex, &swidget->setup_mutex) { 415 - if (!swidget->use_count) 366 + src_widget = sroute->src_widget->widget->dobj.private; 367 + sink_widget = sroute->sink_widget->widget->dobj.private; 368 + 369 + /* 370 + * it is enough to check if the direction_valid is set for one of the 371 + * widgets as all widgets will have the direction set in topology if one 372 + * is set. 373 + */ 374 + if (src_widget && sink_widget && 375 + src_widget->spipe && src_widget->spipe->direction_valid && 376 + sof_widget_in_same_direction(sink_widget, src_widget->spipe->direction)) 416 377 continue; 417 - 418 - if (tplg_ops && tplg_ops->route_setup) { 419 - /* 420 - * this route will get freed when either the 421 - * source widget or the sink widget is freed 422 - * during hw_free 423 - */ 424 - ret = tplg_ops->route_setup(sdev, sroute); 425 - if (!ret) 426 - sroute->setup = true; 427 - } 428 378 } 379 + 380 + ret = sof_route_setup(sdev, sroute->src_widget->widget, 381 + sroute->sink_widget->widget); 429 382 430 383 if (ret < 0) 431 384 return ret; ··· 433 392 434 393 static void 435 394 sof_unprepare_widgets_in_path(struct snd_sof_dev *sdev, struct snd_soc_dapm_widget *widget, 436 - struct snd_soc_dapm_widget_list *list) 395 + struct snd_soc_dapm_widget_list *list, int dir) 437 396 { 438 397 const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg); 439 398 struct snd_sof_widget *swidget = widget->dobj.private; ··· 443 402 if (is_virtual_widget(sdev, widget, __func__)) 444 403 return; 445 404 446 - /* skip if the widget is in use or if it is already unprepared */ 447 - if (!swidget || !swidget->prepared || swidget->use_count > 0) 405 + if (!swidget) 406 + goto sink_unprepare; 407 + 408 + if (swidget->spipe && swidget->spipe->direction_valid && 409 + !sof_widget_in_same_direction(swidget, dir)) 410 + return; 411 + 412 + /* skip widgets in use, those already unprepared or aggregated DAIs */ 413 + if (!swidget->prepared || swidget->use_count > 0 || is_aggregated_dai(swidget)) 448 414 goto sink_unprepare; 449 415 450 416 widget_ops = tplg_ops ? tplg_ops->widget : NULL; ··· 466 418 snd_soc_dapm_widget_for_each_sink_path(widget, p) { 467 419 if (!widget_in_list(list, p->sink)) 468 420 continue; 421 + 469 422 if (!p->walking && p->sink->dobj.private) { 470 423 p->walking = true; 471 - sof_unprepare_widgets_in_path(sdev, p->sink, list); 424 + sof_unprepare_widgets_in_path(sdev, p->sink, list, dir); 472 425 p->walking = false; 473 426 } 474 427 } ··· 491 442 if (is_virtual_widget(sdev, widget, __func__)) 492 443 return 0; 493 444 445 + if (!swidget) 446 + goto sink_prepare; 447 + 494 448 widget_ops = tplg_ops ? tplg_ops->widget : NULL; 495 449 if (!widget_ops) 496 450 return 0; 497 451 498 - if (!swidget || !widget_ops[widget->id].ipc_prepare || swidget->prepared) 452 + if (swidget->spipe && swidget->spipe->direction_valid && 453 + !sof_widget_in_same_direction(swidget, dir)) 454 + return 0; 455 + 456 + /* skip widgets already prepared or aggregated DAI widgets*/ 457 + if (!widget_ops[widget->id].ipc_prepare || swidget->prepared || 458 + is_aggregated_dai(swidget)) 499 459 goto sink_prepare; 500 460 501 461 /* prepare the source widget */ ··· 522 464 snd_soc_dapm_widget_for_each_sink_path(widget, p) { 523 465 if (!widget_in_list(list, p->sink)) 524 466 continue; 467 + 525 468 if (!p->walking && p->sink->dobj.private) { 526 469 p->walking = true; 527 470 ret = sof_prepare_widgets_in_path(sdev, p->sink, fe_params, ··· 552 493 int dir, struct snd_sof_pcm *spcm) 553 494 { 554 495 struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list; 496 + struct snd_sof_widget *swidget = widget->dobj.private; 555 497 struct snd_soc_dapm_path *p; 556 498 int err; 557 499 int ret = 0; ··· 560 500 if (is_virtual_widget(sdev, widget, __func__)) 561 501 return 0; 562 502 563 - if (widget->dobj.private) { 564 - err = sof_widget_free(sdev, widget->dobj.private); 565 - if (err < 0) 566 - ret = err; 567 - } 503 + if (!swidget) 504 + goto sink_free; 568 505 506 + if (swidget->spipe && swidget->spipe->direction_valid && 507 + !sof_widget_in_same_direction(swidget, dir)) 508 + return 0; 509 + 510 + /* skip aggregated DAIs */ 511 + if (is_aggregated_dai(swidget)) 512 + goto sink_free; 513 + 514 + err = sof_widget_free(sdev, widget->dobj.private); 515 + if (err < 0) 516 + ret = err; 517 + sink_free: 569 518 /* free all widgets in the sink paths even in case of error to keep use counts balanced */ 570 519 snd_soc_dapm_widget_for_each_sink_path(widget, p) { 571 520 if (!p->walking) { ··· 614 545 if (swidget) { 615 546 int i; 616 547 617 - ret = sof_widget_setup(sdev, widget->dobj.private); 548 + if (swidget->spipe && swidget->spipe->direction_valid && 549 + !sof_widget_in_same_direction(swidget, dir)) 550 + return 0; 551 + 552 + /* skip aggregated DAIs */ 553 + if (is_aggregated_dai(swidget)) 554 + goto sink_setup; 555 + 556 + ret = sof_widget_setup(sdev, swidget); 618 557 if (ret < 0) 619 558 return ret; 620 559 ··· 684 607 return 0; 685 608 686 609 for_each_dapm_widgets(list, i, widget) { 687 - if (is_virtual_widget(sdev, widget, __func__)) 688 - continue; 689 - 690 - /* starting widget for playback is AIF type */ 610 + /* starting widget for playback is of AIF type */ 691 611 if (dir == SNDRV_PCM_STREAM_PLAYBACK && widget->id != snd_soc_dapm_aif_in) 692 612 continue; 693 613 694 614 /* starting widget for capture is DAI type */ 695 - if (dir == SNDRV_PCM_STREAM_CAPTURE && widget->id != snd_soc_dapm_dai_out) 615 + if (dir == SNDRV_PCM_STREAM_CAPTURE && widget->id != snd_soc_dapm_dai_out && 616 + widget->id != snd_soc_dapm_output) 696 617 continue; 697 618 698 619 switch (op) { ··· 720 645 break; 721 646 } 722 647 case SOF_WIDGET_UNPREPARE: 723 - sof_unprepare_widgets_in_path(sdev, widget, list); 648 + sof_unprepare_widgets_in_path(sdev, widget, list, dir); 724 649 break; 725 650 default: 726 651 dev_err(sdev->dev, "Invalid widget op %d\n", op); ··· 735 660 return 0; 736 661 } 737 662 663 + int sof_widget_list_prepare(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, 664 + struct snd_pcm_hw_params *fe_params, 665 + struct snd_sof_platform_stream_params *platform_params, 666 + int dir) 667 + { 668 + /* 669 + * Prepare widgets for set up. The prepare step is used to allocate memory, assign 670 + * instance ID and pick the widget configuration based on the runtime PCM params. 671 + */ 672 + return sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params, 673 + dir, SOF_WIDGET_PREPARE); 674 + } 675 + 676 + void sof_widget_list_unprepare(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir) 677 + { 678 + struct snd_soc_dapm_widget_list *list = spcm->stream[dir].list; 679 + 680 + /* unprepare the widget */ 681 + sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_UNPREPARE); 682 + 683 + snd_soc_dapm_dai_free_widgets(&list); 684 + spcm->stream[dir].list = NULL; 685 + } 686 + 738 687 int sof_widget_list_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, 739 688 struct snd_pcm_hw_params *fe_params, 740 689 struct snd_sof_platform_stream_params *platform_params, ··· 769 670 struct snd_soc_dapm_widget *widget; 770 671 int i, ret; 771 672 772 - /* nothing to set up */ 773 - if (!list) 673 + /* nothing to set up or setup has been already done */ 674 + if (!list || spcm->setup_done[dir]) 774 675 return 0; 775 - 776 - /* 777 - * Prepare widgets for set up. The prepare step is used to allocate memory, assign 778 - * instance ID and pick the widget configuration based on the runtime PCM params. 779 - */ 780 - ret = sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params, 781 - dir, SOF_WIDGET_PREPARE); 782 - if (ret < 0) 783 - return ret; 784 676 785 677 /* Set up is used to send the IPC to the DSP to create the widget */ 786 678 ret = sof_walk_widgets_in_order(sdev, spcm, fe_params, platform_params, ··· 827 737 } 828 738 } 829 739 740 + spcm->setup_done[dir] = true; 741 + 830 742 return 0; 831 743 832 744 widget_free: ··· 846 754 int ret; 847 755 848 756 /* nothing to free */ 849 - if (!list) 757 + if (!list || !spcm->setup_done[dir]) 850 758 return 0; 851 759 852 760 /* send IPC to free widget in the DSP */ 853 761 ret = sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_FREE); 854 762 855 - /* unprepare the widget */ 856 - sof_walk_widgets_in_order(sdev, spcm, NULL, NULL, dir, SOF_WIDGET_UNPREPARE); 857 - 858 - snd_soc_dapm_dai_free_widgets(&list); 859 - spcm->stream[dir].list = NULL; 860 - 763 + spcm->setup_done[dir] = false; 861 764 pipeline_list->count = 0; 862 765 863 766 return ret;
+15
sound/soc/sof/sof-audio.h
··· 209 209 * @widget_setup: Function pointer for setting up setup in the DSP 210 210 * @widget_free: Function pointer for freeing widget in the DSP 211 211 * @dai_config: Function pointer for sending DAI config IPC to the DSP 212 + * @host_config: Function pointer for setting the DMA ID for host widgets 212 213 * @dai_get_param: Function pointer for getting the DAI parameter 213 214 * @set_up_all_pipelines: Function pointer for setting up all topology pipelines 214 215 * @tear_down_all_pipelines: Function pointer for tearing down all topology pipelines ··· 231 230 int (*widget_free)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget); 232 231 int (*dai_config)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, 233 232 unsigned int flags, struct snd_sof_dai_config_data *data); 233 + void (*host_config)(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget, 234 + struct snd_sof_platform_stream_params *platform_params); 234 235 int (*dai_get_param)(struct snd_sof_dev *sdev, struct snd_sof_dai *dai, int param_type); 235 236 int (*set_up_all_pipelines)(struct snd_sof_dev *sdev, bool verify); 236 237 int (*tear_down_all_pipelines)(struct snd_sof_dev *sdev, bool verify); ··· 354 351 struct snd_sof_pcm_stream stream[2]; 355 352 struct list_head list; /* list in sdev pcm list */ 356 353 struct snd_pcm_hw_params params[2]; 354 + struct snd_sof_platform_stream_params platform_params[2]; 357 355 bool prepared[2]; /* PCM_PARAMS set successfully */ 356 + bool setup_done[2]; /* the setup of the SOF PCM device is done */ 358 357 bool pending_stop[2]; /* only used if (!pcm_ops->platform_stop_during_hw_free) */ 359 358 360 359 /* Must be last - ends in a flex-array member. */ ··· 512 507 * @complete: flag used to indicate that pipeline set up is complete. 513 508 * @core_mask: Mask containing target cores for all modules in the pipeline 514 509 * @list: List item in sdev pipeline_list 510 + * @direction_valid: flag indicating if the direction is set in topology 511 + * @direction: pipeline direction set in topology, valid is direction_valid is true 512 + * 515 513 */ 516 514 struct snd_sof_pipeline { 517 515 struct snd_sof_widget *pipe_widget; ··· 523 515 int complete; 524 516 unsigned long core_mask; 525 517 struct list_head list; 518 + bool direction_valid; 519 + u32 direction; 526 520 }; 527 521 528 522 /* ASoC SOF DAPM route */ ··· 683 673 struct snd_pcm_hw_params *fe_params, 684 674 struct snd_sof_platform_stream_params *platform_params, 685 675 int dir); 676 + int sof_widget_list_prepare(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, 677 + struct snd_pcm_hw_params *fe_params, 678 + struct snd_sof_platform_stream_params *platform_params, 679 + int dir); 680 + void sof_widget_list_unprepare(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir); 686 681 int sof_widget_list_free(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm, int dir); 687 682 int sof_pcm_dsp_pcm_free(struct snd_pcm_substream *substream, struct snd_sof_dev *sdev, 688 683 struct snd_sof_pcm *spcm);