1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) 2 // 3 // This file is provided under a dual BSD/GPLv2 license. When using or 4 // redistributing this file, you may do so under either license. 5 // 6 // Copyright(c) 2022 Intel Corporation. All rights reserved. 7 8 #include <sound/pcm_params.h> 9 #include <sound/hdaudio_ext.h> 10 #include <sound/sof/ipc4/header.h> 11 #include <uapi/sound/sof/header.h> 12 #include "../ipc4-priv.h" 13 #include "../ipc4-topology.h" 14 #include "../sof-priv.h" 15 #include "../sof-audio.h" 16 #include "hda.h" 17 18 /* These ops are only applicable for the HDA DAI's in their current form */ 19 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) 20 /* 21 * This function checks if the host dma channel corresponding 22 * to the link DMA stream_tag argument is assigned to one 23 * of the FEs connected to the BE DAI. 24 */ 25 static bool hda_check_fes(struct snd_soc_pcm_runtime *rtd, 26 int dir, int stream_tag) 27 { 28 struct snd_pcm_substream *fe_substream; 29 struct hdac_stream *fe_hstream; 30 struct snd_soc_dpcm *dpcm; 31 32 for_each_dpcm_fe(rtd, dir, dpcm) { 33 fe_substream = snd_soc_dpcm_get_substream(dpcm->fe, dir); 34 fe_hstream = fe_substream->runtime->private_data; 35 if (fe_hstream->stream_tag == stream_tag) 36 return true; 37 } 38 39 return false; 40 } 41 42 static struct hdac_ext_stream * 43 hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream) 44 { 45 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 46 struct sof_intel_hda_stream *hda_stream; 47 const struct sof_intel_dsp_desc *chip; 48 struct snd_sof_dev *sdev; 49 struct hdac_ext_stream *res = NULL; 50 struct hdac_stream *hstream = NULL; 51 52 int stream_dir = substream->stream; 53 54 if (!bus->ppcap) { 55 dev_err(bus->dev, "stream type not supported\n"); 56 return NULL; 57 } 58 59 spin_lock_irq(&bus->reg_lock); 60 list_for_each_entry(hstream, &bus->stream_list, list) { 61 struct hdac_ext_stream *hext_stream = 62 stream_to_hdac_ext_stream(hstream); 63 if (hstream->direction != substream->stream) 64 continue; 65 66 hda_stream = hstream_to_sof_hda_stream(hext_stream); 67 sdev = hda_stream->sdev; 68 chip = get_chip_info(sdev->pdata); 69 70 /* check if link is available */ 71 if (!hext_stream->link_locked) { 72 /* 73 * choose the first available link for platforms that do not have the 74 * PROCEN_FMT_QUIRK set. 75 */ 76 if (!(chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK)) { 77 res = hext_stream; 78 break; 79 } 80 81 if (hstream->opened) { 82 /* 83 * check if the stream tag matches the stream 84 * tag of one of the connected FEs 85 */ 86 if (hda_check_fes(rtd, stream_dir, 87 hstream->stream_tag)) { 88 res = hext_stream; 89 break; 90 } 91 } else { 92 res = hext_stream; 93 94 /* 95 * This must be a hostless stream. 96 * So reserve the host DMA channel. 97 */ 98 hda_stream->host_reserved = 1; 99 break; 100 } 101 } 102 } 103 104 if (res) { 105 /* Make sure that host and link DMA is decoupled. */ 106 snd_hdac_ext_stream_decouple_locked(bus, res, true); 107 108 res->link_locked = 1; 109 res->link_substream = substream; 110 } 111 spin_unlock_irq(&bus->reg_lock); 112 113 return res; 114 } 115 116 static struct hdac_ext_stream *hda_get_hext_stream(struct snd_sof_dev *sdev, 117 struct snd_soc_dai *cpu_dai, 118 struct snd_pcm_substream *substream) 119 { 120 return snd_soc_dai_get_dma_data(cpu_dai, substream); 121 } 122 123 static struct hdac_ext_stream *hda_assign_hext_stream(struct snd_sof_dev *sdev, 124 struct snd_soc_dai *cpu_dai, 125 struct snd_pcm_substream *substream) 126 { 127 struct hdac_ext_stream *hext_stream; 128 129 hext_stream = hda_link_stream_assign(sof_to_bus(sdev), substream); 130 if (!hext_stream) 131 return NULL; 132 133 snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)hext_stream); 134 135 return hext_stream; 136 } 137 138 static void hda_release_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, 139 struct snd_pcm_substream *substream) 140 { 141 struct hdac_ext_stream *hext_stream = hda_get_hext_stream(sdev, cpu_dai, substream); 142 143 snd_soc_dai_set_dma_data(cpu_dai, substream, NULL); 144 snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK); 145 } 146 147 static void hda_setup_hext_stream(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream, 148 unsigned int format_val) 149 { 150 snd_hdac_ext_stream_setup(hext_stream, format_val); 151 } 152 153 static void hda_reset_hext_stream(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream) 154 { 155 snd_hdac_ext_stream_reset(hext_stream); 156 } 157 158 static int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, 159 struct snd_pcm_substream *substream, int cmd) 160 { 161 struct snd_sof_widget *pipe_widget; 162 struct sof_ipc4_pipeline *pipeline; 163 struct snd_sof_widget *swidget; 164 struct snd_soc_dapm_widget *w; 165 int ret; 166 167 w = snd_soc_dai_get_widget(cpu_dai, substream->stream); 168 swidget = w->dobj.private; 169 pipe_widget = swidget->spipe->pipe_widget; 170 pipeline = pipe_widget->private; 171 172 switch (cmd) { 173 case SNDRV_PCM_TRIGGER_START: 174 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 175 break; 176 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 177 case SNDRV_PCM_TRIGGER_SUSPEND: 178 case SNDRV_PCM_TRIGGER_STOP: 179 ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, 180 SOF_IPC4_PIPE_PAUSED); 181 if (ret < 0) 182 return ret; 183 184 pipeline->state = SOF_IPC4_PIPE_PAUSED; 185 break; 186 default: 187 dev_err(sdev->dev, "unknown trigger command %d\n", cmd); 188 return -EINVAL; 189 } 190 191 return 0; 192 } 193 194 static int hda_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, 195 struct snd_pcm_substream *substream, int cmd) 196 { 197 struct hdac_ext_stream *hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream); 198 199 switch (cmd) { 200 case SNDRV_PCM_TRIGGER_START: 201 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 202 snd_hdac_ext_stream_start(hext_stream); 203 break; 204 case SNDRV_PCM_TRIGGER_SUSPEND: 205 case SNDRV_PCM_TRIGGER_STOP: 206 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 207 snd_hdac_ext_stream_clear(hext_stream); 208 break; 209 default: 210 dev_err(sdev->dev, "unknown trigger command %d\n", cmd); 211 return -EINVAL; 212 } 213 214 return 0; 215 } 216 217 static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, 218 struct snd_pcm_substream *substream, int cmd) 219 { 220 struct snd_sof_widget *pipe_widget; 221 struct sof_ipc4_pipeline *pipeline; 222 struct snd_sof_widget *swidget; 223 struct snd_soc_dapm_widget *w; 224 int ret; 225 226 w = snd_soc_dai_get_widget(cpu_dai, substream->stream); 227 swidget = w->dobj.private; 228 pipe_widget = swidget->spipe->pipe_widget; 229 pipeline = pipe_widget->private; 230 231 switch (cmd) { 232 case SNDRV_PCM_TRIGGER_START: 233 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 234 if (pipeline->state != SOF_IPC4_PIPE_PAUSED) { 235 ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, 236 SOF_IPC4_PIPE_PAUSED); 237 if (ret < 0) 238 return ret; 239 pipeline->state = SOF_IPC4_PIPE_PAUSED; 240 } 241 242 ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, 243 SOF_IPC4_PIPE_RUNNING); 244 if (ret < 0) 245 return ret; 246 pipeline->state = SOF_IPC4_PIPE_RUNNING; 247 break; 248 case SNDRV_PCM_TRIGGER_SUSPEND: 249 case SNDRV_PCM_TRIGGER_STOP: 250 { 251 ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id, 252 SOF_IPC4_PIPE_RESET); 253 if (ret < 0) 254 return ret; 255 256 pipeline->state = SOF_IPC4_PIPE_RESET; 257 break; 258 } 259 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 260 break; 261 default: 262 dev_err(sdev->dev, "unknown trigger command %d\n", cmd); 263 return -EINVAL; 264 } 265 266 return 0; 267 } 268 269 static const struct hda_dai_widget_dma_ops hda_ipc4_dma_ops = { 270 .get_hext_stream = hda_get_hext_stream, 271 .assign_hext_stream = hda_assign_hext_stream, 272 .release_hext_stream = hda_release_hext_stream, 273 .setup_hext_stream = hda_setup_hext_stream, 274 .reset_hext_stream = hda_reset_hext_stream, 275 .pre_trigger = hda_ipc4_pre_trigger, 276 .trigger = hda_trigger, 277 .post_trigger = hda_ipc4_post_trigger 278 }; 279 280 static const struct hda_dai_widget_dma_ops hda_ipc4_chain_dma_ops = { 281 .get_hext_stream = hda_get_hext_stream, 282 .assign_hext_stream = hda_assign_hext_stream, 283 .release_hext_stream = hda_release_hext_stream, 284 .setup_hext_stream = hda_setup_hext_stream, 285 .reset_hext_stream = hda_reset_hext_stream, 286 .trigger = hda_trigger, 287 }; 288 289 static int hda_ipc3_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, 290 struct snd_pcm_substream *substream, int cmd) 291 { 292 struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream); 293 294 switch (cmd) { 295 case SNDRV_PCM_TRIGGER_SUSPEND: 296 case SNDRV_PCM_TRIGGER_STOP: 297 { 298 struct snd_sof_dai_config_data data = { 0 }; 299 300 data.dai_data = DMA_CHAN_INVALID; 301 return hda_dai_config(w, SOF_DAI_CONFIG_FLAGS_HW_FREE, &data); 302 } 303 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 304 return hda_dai_config(w, SOF_DAI_CONFIG_FLAGS_PAUSE, NULL); 305 default: 306 break; 307 } 308 309 return 0; 310 } 311 312 static const struct hda_dai_widget_dma_ops hda_ipc3_dma_ops = { 313 .get_hext_stream = hda_get_hext_stream, 314 .assign_hext_stream = hda_assign_hext_stream, 315 .release_hext_stream = hda_release_hext_stream, 316 .setup_hext_stream = hda_setup_hext_stream, 317 .reset_hext_stream = hda_reset_hext_stream, 318 .trigger = hda_trigger, 319 .post_trigger = hda_ipc3_post_trigger, 320 }; 321 322 static struct hdac_ext_stream * 323 hda_dspless_get_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, 324 struct snd_pcm_substream *substream) 325 { 326 struct hdac_stream *hstream = substream->runtime->private_data; 327 328 return stream_to_hdac_ext_stream(hstream); 329 } 330 331 static void hda_dspless_setup_hext_stream(struct snd_sof_dev *sdev, 332 struct hdac_ext_stream *hext_stream, 333 unsigned int format_val) 334 { 335 /* 336 * Save the format_val which was adjusted by the maxbps of the codec. 337 * This information is not available on the FE side since there we are 338 * using dummy_codec. 339 */ 340 hext_stream->hstream.format_val = format_val; 341 } 342 343 static const struct hda_dai_widget_dma_ops hda_dspless_dma_ops = { 344 .get_hext_stream = hda_dspless_get_hext_stream, 345 .setup_hext_stream = hda_dspless_setup_hext_stream, 346 }; 347 348 #endif 349 350 const struct hda_dai_widget_dma_ops * 351 hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) 352 { 353 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) 354 struct snd_sof_dai *sdai; 355 356 if (sdev->dspless_mode_selected) 357 return &hda_dspless_dma_ops; 358 359 sdai = swidget->private; 360 361 switch (sdev->pdata->ipc_type) { 362 case SOF_IPC: 363 { 364 struct sof_dai_private_data *private = sdai->private; 365 366 if (private->dai_config->type == SOF_DAI_INTEL_HDA) 367 return &hda_ipc3_dma_ops; 368 break; 369 } 370 case SOF_INTEL_IPC4: 371 { 372 struct sof_ipc4_copier *ipc4_copier = sdai->private; 373 374 if (ipc4_copier->dai_type == SOF_DAI_INTEL_HDA) { 375 struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget; 376 struct sof_ipc4_pipeline *pipeline = pipe_widget->private; 377 378 if (pipeline->use_chain_dma) 379 return &hda_ipc4_chain_dma_ops; 380 381 return &hda_ipc4_dma_ops; 382 } 383 break; 384 } 385 default: 386 break; 387 } 388 #endif 389 return NULL; 390 } 391