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

ASoC: SOF: Intel: hda: make sure DAI widget is set up before IPC

With the implementation of the dynamic pipeline feature, widgets
will only be setup when a PCM is opened during the
hw_params ioctl. The BE hw_params callback is responsible for
sending the DAI_CONFIG for the DAI widgets in the DSP.
With dynamic pipelines, the DAI widgets will need to set up
first before sending the DAI_CONFIG IPC in the BE hw_params.

Update the BE hw_params/hw_free callbacks for all ALH, HDA and SSP
DAIs to set up/free the DAI widget before/after DAI_CONFIG IPC.

Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Reviewed-by: Guennadi Liakhovetski <guennadi.liakhovetski@linux.intel.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Reviewed-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Signed-off-by: Daniel Baluta <daniel.baluta@nxp.com>
Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com>
Link: https://lore.kernel.org/r/20210927120517.20505-11-peter.ujfalusi@linux.intel.com
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

Ranjani Sridharan and committed by
Mark Brown
0acb48dd 8b001416

+244 -120
+109 -71
sound/soc/sof/intel/hda-dai.c
··· 155 155 return 0; 156 156 } 157 157 158 - /* Send DAI_CONFIG IPC to the DAI that matches the dai_name and direction */ 159 - static int hda_link_config_ipc(struct sof_intel_hda_stream *hda_stream, 160 - const char *dai_name, int channel, int dir) 158 + /* Update config for the DAI widget */ 159 + static struct sof_ipc_dai_config *hda_dai_update_config(struct snd_soc_dapm_widget *w, 160 + int channel) 161 161 { 162 + struct snd_sof_widget *swidget = w->dobj.private; 162 163 struct sof_ipc_dai_config *config; 163 164 struct snd_sof_dai *sof_dai; 164 - struct sof_ipc_reply reply; 165 - int ret = 0; 166 165 167 - list_for_each_entry(sof_dai, &hda_stream->sdev->dai_list, list) { 168 - if (!sof_dai->cpu_dai_name) 169 - continue; 166 + if (!swidget) 167 + return NULL; 170 168 171 - if (!strcmp(dai_name, sof_dai->cpu_dai_name) && 172 - dir == sof_dai->comp_dai.direction) { 173 - config = sof_dai->dai_config; 169 + sof_dai = swidget->private; 174 170 175 - if (!config) { 176 - dev_err(hda_stream->sdev->dev, 177 - "error: no config for DAI %s\n", 178 - sof_dai->name); 179 - return -EINVAL; 180 - } 181 - 182 - /* update config with stream tag */ 183 - config->hda.link_dma_ch = channel; 184 - 185 - /* send IPC */ 186 - ret = sof_ipc_tx_message(hda_stream->sdev->ipc, 187 - config->hdr.cmd, 188 - config, 189 - config->hdr.size, 190 - &reply, sizeof(reply)); 191 - 192 - if (ret < 0) 193 - dev_err(hda_stream->sdev->dev, 194 - "error: failed to set dai config for %s\n", 195 - sof_dai->name); 196 - return ret; 197 - } 171 + if (!sof_dai || !sof_dai->dai_config) { 172 + dev_err(swidget->scomp->dev, "error: No config for DAI %s\n", w->name); 173 + return NULL; 198 174 } 199 175 200 - return -EINVAL; 176 + config = &sof_dai->dai_config[sof_dai->current_config]; 177 + 178 + /* update config with stream tag */ 179 + config->hda.link_dma_ch = channel; 180 + 181 + return config; 182 + } 183 + 184 + static int hda_link_config_ipc(struct sof_intel_hda_stream *hda_stream, 185 + struct snd_soc_dapm_widget *w, int channel) 186 + { 187 + struct snd_sof_dev *sdev = hda_stream->sdev; 188 + struct sof_ipc_dai_config *config; 189 + struct sof_ipc_reply reply; 190 + 191 + config = hda_dai_update_config(w, channel); 192 + if (!config) { 193 + dev_err(sdev->dev, "error: no config for DAI %s\n", w->name); 194 + return -ENOENT; 195 + } 196 + 197 + /* send DAI_CONFIG IPC */ 198 + return sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size, 199 + &reply, sizeof(reply)); 200 + } 201 + 202 + static int hda_link_dai_widget_update(struct sof_intel_hda_stream *hda_stream, 203 + struct snd_soc_dapm_widget *w, 204 + int channel, bool widget_setup) 205 + { 206 + struct snd_sof_dev *sdev = hda_stream->sdev; 207 + struct sof_ipc_dai_config *config; 208 + 209 + config = hda_dai_update_config(w, channel); 210 + if (!config) { 211 + dev_err(sdev->dev, "error: no config for DAI %s\n", w->name); 212 + return -ENOENT; 213 + } 214 + 215 + /* set up/free DAI widget and send DAI_CONFIG IPC */ 216 + if (widget_setup) 217 + return hda_ctrl_dai_widget_setup(w); 218 + 219 + return hda_ctrl_dai_widget_free(w); 201 220 } 202 221 203 222 static int hda_link_hw_params(struct snd_pcm_substream *substream, ··· 230 211 struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); 231 212 struct sof_intel_hda_stream *hda_stream; 232 213 struct hda_pipe_params p_params = {0}; 214 + struct snd_soc_dapm_widget *w; 233 215 struct hdac_ext_link *link; 234 216 int stream_tag; 235 217 int ret; ··· 249 229 250 230 hda_stream = hstream_to_sof_hda_stream(link_dev); 251 231 252 - /* update the DSP with the new tag */ 253 - ret = hda_link_config_ipc(hda_stream, dai->name, stream_tag - 1, 254 - substream->stream); 232 + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 233 + w = dai->playback_widget; 234 + else 235 + w = dai->capture_widget; 236 + 237 + /* set up the DAI widget and send the DAI_CONFIG with the new tag */ 238 + ret = hda_link_dai_widget_update(hda_stream, w, stream_tag - 1, true); 255 239 if (ret < 0) 256 240 return ret; 257 241 ··· 311 287 snd_soc_dai_get_dma_data(dai, substream); 312 288 struct sof_intel_hda_stream *hda_stream; 313 289 struct snd_soc_pcm_runtime *rtd; 290 + struct snd_soc_dapm_widget *w; 314 291 struct hdac_ext_link *link; 315 292 struct hdac_stream *hstream; 316 293 struct hdac_bus *bus; ··· 346 321 break; 347 322 case SNDRV_PCM_TRIGGER_SUSPEND: 348 323 case SNDRV_PCM_TRIGGER_STOP: 324 + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 325 + w = dai->playback_widget; 326 + else 327 + w = dai->capture_widget; 328 + 349 329 /* 350 330 * clear link DMA channel. It will be assigned when 351 331 * hw_params is set up again after resume. 352 332 */ 353 - ret = hda_link_config_ipc(hda_stream, dai->name, 354 - DMA_CHAN_INVALID, substream->stream); 333 + ret = hda_link_config_ipc(hda_stream, w, DMA_CHAN_INVALID); 355 334 if (ret < 0) 356 335 return ret; 357 336 ··· 386 357 struct hdac_stream *hstream; 387 358 struct snd_soc_pcm_runtime *rtd; 388 359 struct hdac_ext_stream *link_dev; 360 + struct snd_soc_dapm_widget *w; 389 361 int ret; 390 362 391 363 hstream = substream->runtime->private_data; ··· 402 372 403 373 hda_stream = hstream_to_sof_hda_stream(link_dev); 404 374 405 - /* free the link DMA channel in the FW */ 406 - ret = hda_link_config_ipc(hda_stream, dai->name, DMA_CHAN_INVALID, 407 - substream->stream); 375 + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 376 + w = dai->playback_widget; 377 + else 378 + w = dai->capture_widget; 379 + 380 + /* free the link DMA channel in the FW and the DAI widget */ 381 + ret = hda_link_dai_widget_update(hda_stream, w, DMA_CHAN_INVALID, false); 408 382 if (ret < 0) 409 383 return ret; 410 384 ··· 440 406 441 407 #endif 442 408 443 - static int ssp_dai_hw_params(struct snd_pcm_substream *substream, 444 - struct snd_pcm_hw_params *params, 445 - struct snd_soc_dai *dai) 409 + static int ssp_dai_setup_or_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai, 410 + bool setup) 446 411 { 447 - struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 448 - struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, SOF_AUDIO_PCM_DRV_NAME); 449 - struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); 450 - struct sof_ipc_fw_version *v = &sdev->fw_ready.version; 451 - struct sof_ipc_dai_config *config; 452 - struct snd_sof_dai *sof_dai; 453 - struct sof_ipc_reply reply; 454 - int ret; 412 + struct snd_soc_component *component; 413 + struct snd_sof_widget *swidget; 414 + struct snd_soc_dapm_widget *w; 415 + struct sof_ipc_fw_version *v; 416 + struct snd_sof_dev *sdev; 417 + 418 + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 419 + w = dai->playback_widget; 420 + else 421 + w = dai->capture_widget; 422 + 423 + swidget = w->dobj.private; 424 + component = swidget->scomp; 425 + sdev = snd_soc_component_get_drvdata(component); 426 + v = &sdev->fw_ready.version; 455 427 456 428 /* DAI_CONFIG IPC during hw_params is not supported in older firmware */ 457 429 if (v->abi_version < SOF_ABI_VER(3, 18, 0)) 458 430 return 0; 459 431 460 - list_for_each_entry(sof_dai, &sdev->dai_list, list) { 461 - if (!sof_dai->cpu_dai_name || !sof_dai->dai_config) 462 - continue; 432 + if (setup) 433 + return hda_ctrl_dai_widget_setup(w); 463 434 464 - if (!strcmp(dai->name, sof_dai->cpu_dai_name) && 465 - substream->stream == sof_dai->comp_dai.direction) { 466 - config = &sof_dai->dai_config[sof_dai->current_config]; 435 + return hda_ctrl_dai_widget_free(w); 436 + } 467 437 468 - /* send IPC */ 469 - ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, 470 - config->hdr.size, &reply, sizeof(reply)); 438 + static int ssp_dai_hw_params(struct snd_pcm_substream *substream, 439 + struct snd_pcm_hw_params *params, 440 + struct snd_soc_dai *dai) 441 + { 442 + return ssp_dai_setup_or_free(substream, dai, true); 443 + } 471 444 472 - if (ret < 0) 473 - dev_err(sdev->dev, "error: failed to set DAI config for %s\n", 474 - sof_dai->name); 475 - return ret; 476 - } 477 - } 478 - 479 - return 0; 445 + static int ssp_dai_hw_free(struct snd_pcm_substream *substream, 446 + struct snd_soc_dai *dai) 447 + { 448 + return ssp_dai_setup_or_free(substream, dai, false); 480 449 } 481 450 482 451 static const struct snd_soc_dai_ops ssp_dai_ops = { 483 452 .hw_params = ssp_dai_hw_params, 453 + .hw_free = ssp_dai_hw_free, 484 454 }; 485 455 486 456 /*
+128 -45
sound/soc/sof/intel/hda.c
··· 52 52 return chip_info; 53 53 } 54 54 55 + int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w) 56 + { 57 + struct snd_sof_widget *swidget = w->dobj.private; 58 + struct snd_soc_component *component = swidget->scomp; 59 + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); 60 + struct sof_ipc_dai_config *config; 61 + struct snd_sof_dai *sof_dai; 62 + struct sof_ipc_reply reply; 63 + int ret; 64 + 65 + sof_dai = swidget->private; 66 + 67 + if (!sof_dai || !sof_dai->dai_config) { 68 + dev_err(sdev->dev, "No config for DAI %s\n", w->name); 69 + return -EINVAL; 70 + } 71 + 72 + config = &sof_dai->dai_config[sof_dai->current_config]; 73 + 74 + /* 75 + * For static pipelines, the DAI widget would already be set up and calling 76 + * sof_widget_setup() simply returns without doing anything. 77 + * For dynamic pipelines, the DAI widget will be set up now. 78 + */ 79 + ret = sof_widget_setup(sdev, swidget); 80 + if (ret < 0) { 81 + dev_err(sdev->dev, "error: failed setting up DAI widget %s\n", w->name); 82 + return ret; 83 + } 84 + 85 + /* send DAI_CONFIG IPC */ 86 + ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size, 87 + &reply, sizeof(reply)); 88 + if (ret < 0) { 89 + dev_err(sdev->dev, "error: failed setting DAI config for %s\n", w->name); 90 + return ret; 91 + } 92 + 93 + sof_dai->configured = true; 94 + 95 + return 0; 96 + } 97 + 98 + int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w) 99 + { 100 + struct snd_sof_widget *swidget = w->dobj.private; 101 + struct snd_soc_component *component = swidget->scomp; 102 + struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component); 103 + struct sof_ipc_dai_config *config; 104 + struct snd_sof_dai *sof_dai; 105 + struct sof_ipc_reply reply; 106 + int ret; 107 + 108 + sof_dai = swidget->private; 109 + 110 + if (!sof_dai || !sof_dai->dai_config) { 111 + dev_err(sdev->dev, "error: No config to free DAI %s\n", w->name); 112 + return -EINVAL; 113 + } 114 + 115 + /* nothing to do if hw_free() is called without restarting the stream after resume. */ 116 + if (!sof_dai->configured) 117 + return 0; 118 + 119 + config = &sof_dai->dai_config[sof_dai->current_config]; 120 + 121 + ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size, 122 + &reply, sizeof(reply)); 123 + if (ret < 0) 124 + dev_err(sdev->dev, "error: failed resetting DAI config for %s\n", w->name); 125 + 126 + /* 127 + * Reset the configured_flag and free the widget even if the IPC fails to keep 128 + * the widget use_count balanced 129 + */ 130 + sof_dai->configured = false; 131 + 132 + return sof_widget_free(sdev, swidget); 133 + } 134 + 55 135 #if IS_ENABLED(CONFIG_SND_SOC_SOF_INTEL_SOUNDWIRE) 56 136 57 137 /* ··· 144 64 module_param(sdw_clock_stop_quirks, int, 0444); 145 65 MODULE_PARM_DESC(sdw_clock_stop_quirks, "SOF SoundWire clock stop quirks"); 146 66 67 + static int sdw_dai_config_ipc(struct snd_sof_dev *sdev, 68 + struct snd_soc_dapm_widget *w, 69 + int link_id, int alh_stream_id, int dai_id, bool setup) 70 + { 71 + struct snd_sof_widget *swidget = w->dobj.private; 72 + struct sof_ipc_dai_config *config; 73 + struct snd_sof_dai *sof_dai; 74 + 75 + if (!swidget) { 76 + dev_err(sdev->dev, "error: No private data for widget %s\n", w->name); 77 + return -EINVAL; 78 + } 79 + 80 + sof_dai = swidget->private; 81 + 82 + if (!sof_dai || !sof_dai->dai_config) { 83 + dev_err(sdev->dev, "error: No config for DAI %s\n", w->name); 84 + return -EINVAL; 85 + } 86 + 87 + config = &sof_dai->dai_config[sof_dai->current_config]; 88 + 89 + /* update config with link and stream ID */ 90 + config->dai_index = (link_id << 8) | dai_id; 91 + config->alh.stream_id = alh_stream_id; 92 + 93 + if (setup) 94 + return hda_ctrl_dai_widget_setup(w); 95 + 96 + return hda_ctrl_dai_widget_free(w); 97 + } 98 + 147 99 static int sdw_params_stream(struct device *dev, 148 100 struct sdw_intel_stream_params_data *params_data) 149 101 { 102 + struct snd_pcm_substream *substream = params_data->substream; 150 103 struct snd_sof_dev *sdev = dev_get_drvdata(dev); 151 104 struct snd_soc_dai *d = params_data->dai; 152 - struct sof_ipc_dai_config config; 153 - struct sof_ipc_reply reply; 154 - int link_id = params_data->link_id; 155 - int alh_stream_id = params_data->alh_stream_id; 156 - int ret; 157 - u32 size = sizeof(config); 105 + struct snd_soc_dapm_widget *w; 158 106 159 - memset(&config, 0, size); 160 - config.hdr.size = size; 161 - config.hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG; 162 - config.type = SOF_DAI_INTEL_ALH; 163 - config.dai_index = (link_id << 8) | (d->id); 164 - config.alh.stream_id = alh_stream_id; 107 + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 108 + w = d->playback_widget; 109 + else 110 + w = d->capture_widget; 165 111 166 - /* send message to DSP */ 167 - ret = sof_ipc_tx_message(sdev->ipc, 168 - config.hdr.cmd, &config, size, &reply, 169 - sizeof(reply)); 170 - if (ret < 0) { 171 - dev_err(sdev->dev, 172 - "error: failed to set DAI hw_params for link %d dai->id %d ALH %d\n", 173 - link_id, d->id, alh_stream_id); 174 - } 175 - 176 - return ret; 112 + return sdw_dai_config_ipc(sdev, w, params_data->link_id, params_data->alh_stream_id, 113 + d->id, true); 177 114 } 178 115 179 116 static int sdw_free_stream(struct device *dev, 180 117 struct sdw_intel_stream_free_data *free_data) 181 118 { 119 + struct snd_pcm_substream *substream = free_data->substream; 182 120 struct snd_sof_dev *sdev = dev_get_drvdata(dev); 183 121 struct snd_soc_dai *d = free_data->dai; 184 - struct sof_ipc_dai_config config; 185 - struct sof_ipc_reply reply; 186 - int link_id = free_data->link_id; 187 - int ret; 188 - u32 size = sizeof(config); 122 + struct snd_soc_dapm_widget *w; 189 123 190 - memset(&config, 0, size); 191 - config.hdr.size = size; 192 - config.hdr.cmd = SOF_IPC_GLB_DAI_MSG | SOF_IPC_DAI_CONFIG; 193 - config.type = SOF_DAI_INTEL_ALH; 194 - config.dai_index = (link_id << 8) | d->id; 195 - config.alh.stream_id = 0xFFFF; /* invalid value on purpose */ 124 + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 125 + w = d->playback_widget; 126 + else 127 + w = d->capture_widget; 196 128 197 - /* send message to DSP */ 198 - ret = sof_ipc_tx_message(sdev->ipc, 199 - config.hdr.cmd, &config, size, &reply, 200 - sizeof(reply)); 201 - if (ret < 0) { 202 - dev_err(sdev->dev, 203 - "error: failed to free stream for link %d dai->id %d\n", 204 - link_id, d->id); 205 - } 206 - 207 - return ret; 129 + /* send invalid stream_id */ 130 + return sdw_dai_config_ipc(sdev, w, free_data->link_id, 0xFFFF, d->id, false); 208 131 } 209 132 210 133 static const struct sdw_intel_ops sdw_callback = {
+5
sound/soc/sof/intel/hda.h
··· 733 733 /* PCI driver selection and probe */ 734 734 int hda_pci_intel_probe(struct pci_dev *pci, const struct pci_device_id *pci_id); 735 735 736 + struct snd_sof_dai; 737 + struct sof_ipc_dai_config; 738 + int hda_ctrl_dai_widget_setup(struct snd_soc_dapm_widget *w); 739 + int hda_ctrl_dai_widget_free(struct snd_soc_dapm_widget *w); 740 + 736 741 #endif
+1
sound/soc/sof/sof-audio.c
··· 163 163 return -ENOMEM; 164 164 165 165 dai = swidget->private; 166 + dai->configured = false; 166 167 memcpy(comp, &dai->comp_dai, sizeof(struct sof_ipc_comp_dai)); 167 168 168 169 /* append extended data to the end of the component */
+1 -1
sound/soc/sof/sof-audio.h
··· 130 130 struct snd_sof_dai { 131 131 struct snd_soc_component *scomp; 132 132 const char *name; 133 - const char *cpu_dai_name; 134 133 135 134 struct sof_ipc_comp_dai comp_dai; 136 135 int number_configs; 137 136 int current_config; 137 + bool configured; /* DAI configured during BE hw_params */ 138 138 struct sof_ipc_dai_config *dai_config; 139 139 struct list_head list; /* list in sdev dai list */ 140 140 };
-3
sound/soc/sof/topology.c
··· 2756 2756 if (!dai->dai_config) 2757 2757 return -ENOMEM; 2758 2758 2759 - /* set cpu_dai_name */ 2760 - dai->cpu_dai_name = link->cpus->dai_name; 2761 - 2762 2759 found = 1; 2763 2760 } 2764 2761 }