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 int hda_ipc3_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai, 281 struct snd_pcm_substream *substream, int cmd) 282 { 283 struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream); 284 285 switch (cmd) { 286 case SNDRV_PCM_TRIGGER_SUSPEND: 287 case SNDRV_PCM_TRIGGER_STOP: 288 { 289 struct snd_sof_dai_config_data data = { 0 }; 290 291 data.dai_data = DMA_CHAN_INVALID; 292 return hda_dai_config(w, SOF_DAI_CONFIG_FLAGS_HW_FREE, &data); 293 } 294 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 295 return hda_dai_config(w, SOF_DAI_CONFIG_FLAGS_PAUSE, NULL); 296 default: 297 break; 298 } 299 300 return 0; 301 } 302 303 static const struct hda_dai_widget_dma_ops hda_ipc3_dma_ops = { 304 .get_hext_stream = hda_get_hext_stream, 305 .assign_hext_stream = hda_assign_hext_stream, 306 .release_hext_stream = hda_release_hext_stream, 307 .setup_hext_stream = hda_setup_hext_stream, 308 .reset_hext_stream = hda_reset_hext_stream, 309 .trigger = hda_trigger, 310 .post_trigger = hda_ipc3_post_trigger, 311 }; 312 313 #endif 314 315 const struct hda_dai_widget_dma_ops * 316 hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget) 317 { 318 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) 319 struct snd_sof_dai *sdai = swidget->private; 320 321 switch (sdev->pdata->ipc_type) { 322 case SOF_IPC: 323 { 324 struct sof_dai_private_data *private = sdai->private; 325 326 if (private->dai_config->type == SOF_DAI_INTEL_HDA) 327 return &hda_ipc3_dma_ops; 328 break; 329 } 330 case SOF_INTEL_IPC4: 331 { 332 struct sof_ipc4_copier *ipc4_copier = sdai->private; 333 334 if (ipc4_copier->dai_type == SOF_DAI_INTEL_HDA) 335 return &hda_ipc4_dma_ops; 336 break; 337 } 338 default: 339 break; 340 } 341 #endif 342 return NULL; 343 } 344