xref: /linux/sound/soc/sof/intel/hda-dai-ops.c (revision e5c91aac491def6ab3f90c4cc246e3fcb0f8f058)
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
7 
8 #include <sound/pcm_params.h>
9 #include <sound/hdaudio_ext.h>
10 #include <sound/hda_register.h>
11 #include <sound/hda-mlink.h>
12 #include <sound/sof/ipc4/header.h>
13 #include <uapi/sound/sof/header.h>
14 #include "../ipc4-priv.h"
15 #include "../ipc4-topology.h"
16 #include "../sof-priv.h"
17 #include "../sof-audio.h"
18 #include "hda.h"
19 
20 /* These ops are only applicable for the HDA DAI's in their current form */
21 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK)
22 /*
23  * This function checks if the host DMA stream corresponding
24  * to the link DMA stream_tag argument is assigned to one
25  * of the FEs connected to the BE DAI.
26  */
27 static bool hda_check_fes(struct snd_soc_pcm_runtime *rtd,
28 			  int dir, int stream_tag)
29 {
30 	struct snd_pcm_substream *fe_substream;
31 	struct hdac_stream *fe_hstream;
32 	struct snd_soc_dpcm *dpcm;
33 
34 	for_each_dpcm_fe(rtd, dir, dpcm) {
35 		fe_substream = snd_soc_dpcm_get_substream(dpcm->fe, dir);
36 		fe_hstream = fe_substream->runtime->private_data;
37 		if (fe_hstream->stream_tag == stream_tag)
38 			return true;
39 	}
40 
41 	return false;
42 }
43 
44 static struct hdac_ext_stream *
45 hda_link_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream,
46 		       enum hda_bus_ml_link_type link_type)
47 {
48 	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
49 	struct sof_intel_hda_dev *sof_hda = bus_to_sof_hda(bus);
50 	struct sof_intel_hda_stream *hda_stream;
51 	const struct sof_intel_dsp_desc *chip;
52 	struct snd_sof_dev *sdev;
53 	struct hdac_ext_stream *res = NULL;
54 	struct hdac_stream *hstream = NULL;
55 	int stream_dir = substream->stream;
56 	bool is_multi = link_type == HDA_BUS_ML_LINK_HDA || link_type == HDA_BUS_ML_LINK_UAOL;
57 	bool is_play = stream_dir == SNDRV_PCM_STREAM_PLAYBACK;
58 	bool is_sdw = link_type == HDA_BUS_ML_LINK_SDW;
59 	bool is_hda = link_type == HDA_BUS_ML_LINK_HDA;
60 	u32 concur_block_mask = 0;
61 	u32 seq_block_mask = 0;
62 	unsigned int stream_idx;
63 
64 	if (!bus->ppcap) {
65 		dev_err(bus->dev, "stream type not supported\n");
66 		return NULL;
67 	}
68 
69 	/*
70 	 * On ACE2+ the link DMA stream allocator must avoid two HW errata,
71 	 * see the comment on struct sof_intel_hda_dev.
72 	 *
73 	 * - Concurrent cross-direction: SoundWire conflicts with HDA, iDisp
74 	 *   and UAOL on the same physical stream index; SSP and DMIC are safe.
75 	 * - Sequential playback: a stream index previously used by an HDA/iDisp
76 	 *   link cannot drive any non-HDA/iDisp link in the same direction
77 	 *   until the next controller reset.
78 	 *
79 	 * The masks are protected by bus->reg_lock; sample them inside the
80 	 * lock together with the stream walk to keep the decision atomic
81 	 * with concurrent allocations and releases.
82 	 */
83 	guard(spinlock_irq)(&bus->reg_lock);
84 
85 	if (is_sdw)
86 		concur_block_mask = sof_hda->link_dma_active_multi_mask[!stream_dir];
87 	else if (is_multi)
88 		concur_block_mask = sof_hda->link_dma_active_sdw_mask[!stream_dir];
89 	if (is_play && !is_hda)
90 		seq_block_mask = sof_hda->link_dma_out_hda_used_mask;
91 
92 	list_for_each_entry(hstream, &bus->stream_list, list) {
93 		struct hdac_ext_stream *hext_stream =
94 			stream_to_hdac_ext_stream(hstream);
95 		if (hstream->direction != substream->stream)
96 			continue;
97 
98 		hda_stream = hstream_to_sof_hda_stream(hext_stream);
99 		sdev = hda_stream->sdev;
100 		chip = get_chip_info(sdev->pdata);
101 
102 		stream_idx = hstream->stream_tag - 1;
103 
104 		/* skip streams blocked by the ACE2+ allocator constraints */
105 		if ((concur_block_mask | seq_block_mask) & BIT(stream_idx))
106 			continue;
107 
108 		/* check if link is available */
109 		if (!hext_stream->link_locked) {
110 			/*
111 			 * choose the first available link for platforms that do not have the
112 			 * PROCEN_FMT_QUIRK set.
113 			 */
114 			if (!(chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK)) {
115 				res = hext_stream;
116 				break;
117 			}
118 
119 			if (hstream->opened) {
120 				/*
121 				 * check if the stream tag matches the stream
122 				 * tag of one of the connected FEs
123 				 */
124 				if (hda_check_fes(rtd, stream_dir,
125 						  hstream->stream_tag)) {
126 					res = hext_stream;
127 					break;
128 				}
129 			} else {
130 				res = hext_stream;
131 
132 				/*
133 				 * This must be a hostless stream.
134 				 * So reserve the host DMA stream.
135 				 */
136 				hda_stream->host_reserved = 1;
137 				break;
138 			}
139 		}
140 	}
141 
142 	if (res) {
143 		/* Make sure that host and link DMA is decoupled. */
144 		snd_hdac_ext_stream_decouple_locked(bus, res, true);
145 
146 		res->link_locked = 1;
147 		res->link_substream = substream;
148 
149 		stream_idx = res->hstream.stream_tag - 1;
150 		if (is_sdw)
151 			sof_hda->link_dma_active_sdw_mask[stream_dir] |= BIT(stream_idx);
152 		else if (is_multi)
153 			sof_hda->link_dma_active_multi_mask[stream_dir] |= BIT(stream_idx);
154 
155 		/* persistent OUT HDA/iDisp shadow, cleared only on CRST# */
156 		if (is_hda && is_play)
157 			sof_hda->link_dma_out_hda_used_mask |= BIT(stream_idx);
158 	}
159 
160 	return res;
161 }
162 
163 static struct hdac_ext_stream *hda_get_hext_stream(struct snd_sof_dev *sdev,
164 						   struct snd_soc_dai *cpu_dai,
165 						   struct snd_pcm_substream *substream)
166 {
167 	return snd_soc_dai_get_dma_data(cpu_dai, substream);
168 }
169 
170 static struct hdac_ext_stream *hda_ipc4_get_hext_stream(struct snd_sof_dev *sdev,
171 							struct snd_soc_dai *cpu_dai,
172 							struct snd_pcm_substream *substream)
173 {
174 	struct snd_sof_widget *pipe_widget;
175 	struct sof_ipc4_pipeline *pipeline;
176 	struct snd_sof_widget *swidget;
177 	struct snd_soc_dapm_widget *w;
178 
179 	w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
180 	swidget = w->dobj.private;
181 	pipe_widget = swidget->spipe->pipe_widget;
182 	pipeline = pipe_widget->private;
183 
184 	/* mark pipeline so that it can be skipped during FE trigger */
185 	pipeline->skip_during_fe_trigger = true;
186 
187 	return snd_soc_dai_get_dma_data(cpu_dai, substream);
188 }
189 
190 static struct hdac_ext_stream *hda_assign_hext_stream(struct snd_sof_dev *sdev,
191 						      struct snd_soc_dai *cpu_dai,
192 						      struct snd_pcm_substream *substream,
193 						      struct hdac_ext_link *hlink)
194 {
195 	struct hdac_ext_stream *hext_stream;
196 	enum hda_bus_ml_link_type link_type = hda_bus_ml_link_get_type(hlink);
197 
198 	hext_stream = hda_link_stream_assign(sof_to_bus(sdev), substream, link_type);
199 	if (!hext_stream)
200 		return NULL;
201 
202 	snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)hext_stream);
203 
204 	return hext_stream;
205 }
206 
207 static void hda_release_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
208 				    struct snd_pcm_substream *substream)
209 {
210 	struct hdac_ext_stream *hext_stream = hda_get_hext_stream(sdev, cpu_dai, substream);
211 	struct sof_intel_hda_dev *sof_hda = sdev->pdata->hw_pdata;
212 	struct hdac_bus *bus = sof_to_bus(sdev);
213 	int dir = substream->stream;
214 	unsigned int stream_idx = hext_stream->hstream.stream_tag - 1;
215 
216 	/*
217 	 * Drop the stream index from the per-direction active concurrency masks.
218 	 * The two masks are mutually exclusive for a given stream/direction
219 	 * (and a stream of the SSP/DMIC kind appears in neither), so a blind
220 	 * clear of both is safe and lets us avoid having to remember the
221 	 * link type at allocation time.
222 	 */
223 	scoped_guard(spinlock_irq, &bus->reg_lock) {
224 		sof_hda->link_dma_active_sdw_mask[dir] &= ~BIT(stream_idx);
225 		sof_hda->link_dma_active_multi_mask[dir] &= ~BIT(stream_idx);
226 	}
227 
228 	snd_soc_dai_set_dma_data(cpu_dai, substream, NULL);
229 	snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK);
230 }
231 
232 static void hda_setup_hext_stream(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream,
233 				  unsigned int format_val)
234 {
235 	snd_hdac_ext_stream_setup(hext_stream, format_val);
236 }
237 
238 static void hda_reset_hext_stream(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream)
239 {
240 	snd_hdac_ext_stream_reset(hext_stream);
241 }
242 
243 static void hda_codec_dai_set_stream(struct snd_sof_dev *sdev,
244 				     struct snd_pcm_substream *substream,
245 				     struct hdac_stream *hstream)
246 {
247 	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
248 	struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
249 
250 	/* set the hdac_stream in the codec dai */
251 	snd_soc_dai_set_stream(codec_dai, hstream, substream->stream);
252 }
253 
254 static unsigned int hda_calc_stream_format(struct snd_sof_dev *sdev,
255 					   struct snd_pcm_substream *substream,
256 					   struct snd_pcm_hw_params *params)
257 {
258 	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
259 	struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
260 	unsigned int link_bps;
261 	unsigned int format_val;
262 	unsigned int bits;
263 
264 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
265 		link_bps = codec_dai->driver->playback.sig_bits;
266 	else
267 		link_bps = codec_dai->driver->capture.sig_bits;
268 
269 	bits = snd_hdac_stream_format_bits(params_format(params), SNDRV_PCM_SUBFORMAT_STD,
270 					   link_bps);
271 	format_val = snd_hdac_stream_format(params_channels(params), bits, params_rate(params));
272 
273 	dev_dbg(sdev->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val,
274 		params_rate(params), params_channels(params), params_format(params));
275 
276 	return format_val;
277 }
278 
279 static struct hdac_ext_link *hda_get_hlink(struct snd_sof_dev *sdev,
280 					   struct snd_pcm_substream *substream)
281 {
282 	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
283 	struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
284 	struct hdac_bus *bus = sof_to_bus(sdev);
285 
286 	return snd_hdac_ext_bus_get_hlink_by_name(bus, codec_dai->component->name);
287 }
288 
289 static unsigned int generic_calc_stream_format(struct snd_sof_dev *sdev,
290 					       struct snd_pcm_substream *substream,
291 					       struct snd_pcm_hw_params *params)
292 {
293 	unsigned int format_val;
294 	unsigned int bits;
295 
296 	bits = snd_hdac_stream_format_bits(params_format(params), SNDRV_PCM_SUBFORMAT_STD,
297 					   params_physical_width(params));
298 	format_val = snd_hdac_stream_format(params_channels(params), bits, params_rate(params));
299 
300 	dev_dbg(sdev->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val,
301 		params_rate(params), params_channels(params), params_format(params));
302 
303 	return format_val;
304 }
305 
306 static unsigned int dmic_calc_stream_format(struct snd_sof_dev *sdev,
307 					    struct snd_pcm_substream *substream,
308 					    struct snd_pcm_hw_params *params)
309 {
310 	unsigned int format_val;
311 	snd_pcm_format_t format;
312 	unsigned int channels;
313 	unsigned int width;
314 	unsigned int bits;
315 
316 	channels = params_channels(params);
317 	format = params_format(params);
318 	width = params_physical_width(params);
319 
320 	if (format == SNDRV_PCM_FORMAT_S16_LE) {
321 		format = SNDRV_PCM_FORMAT_S32_LE;
322 		channels /= 2;
323 		width = 32;
324 	}
325 
326 	bits = snd_hdac_stream_format_bits(format, SNDRV_PCM_SUBFORMAT_STD, width);
327 	format_val = snd_hdac_stream_format(channels, bits, params_rate(params));
328 
329 	dev_dbg(sdev->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val,
330 		params_rate(params), channels, format);
331 
332 	return format_val;
333 }
334 
335 static struct hdac_ext_link *ssp_get_hlink(struct snd_sof_dev *sdev,
336 					   struct snd_pcm_substream *substream)
337 {
338 	struct hdac_bus *bus = sof_to_bus(sdev);
339 
340 	return hdac_bus_eml_ssp_get_hlink(bus);
341 }
342 
343 static struct hdac_ext_link *dmic_get_hlink(struct snd_sof_dev *sdev,
344 					    struct snd_pcm_substream *substream)
345 {
346 	struct hdac_bus *bus = sof_to_bus(sdev);
347 
348 	return hdac_bus_eml_dmic_get_hlink(bus);
349 }
350 
351 static struct hdac_ext_link *sdw_get_hlink(struct snd_sof_dev *sdev,
352 					   struct snd_pcm_substream *substream)
353 {
354 	struct hdac_bus *bus = sof_to_bus(sdev);
355 
356 	return hdac_bus_eml_sdw_get_hlink(bus);
357 }
358 
359 static int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
360 				struct snd_pcm_substream *substream, int cmd)
361 {
362 	struct sof_ipc4_fw_data *ipc4_data = sdev->private;
363 	struct snd_sof_widget *pipe_widget;
364 	struct sof_ipc4_pipeline *pipeline;
365 	struct snd_sof_widget *swidget;
366 	struct snd_soc_dapm_widget *w;
367 	int ret = 0;
368 
369 	w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
370 	swidget = w->dobj.private;
371 	pipe_widget = swidget->spipe->pipe_widget;
372 	pipeline = pipe_widget->private;
373 
374 	if (pipe_widget->instance_id < 0)
375 		return 0;
376 
377 	guard(mutex)(&ipc4_data->pipeline_state_mutex);
378 
379 	switch (cmd) {
380 	case SNDRV_PCM_TRIGGER_START:
381 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
382 		break;
383 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
384 	case SNDRV_PCM_TRIGGER_SUSPEND:
385 	case SNDRV_PCM_TRIGGER_STOP:
386 		ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
387 						  SOF_IPC4_PIPE_PAUSED);
388 		if (ret < 0)
389 			return ret;
390 
391 		pipeline->state = SOF_IPC4_PIPE_PAUSED;
392 
393 		break;
394 	default:
395 		dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
396 		ret = -EINVAL;
397 	}
398 
399 	return ret;
400 }
401 
402 static int hda_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
403 		       struct snd_pcm_substream *substream, int cmd)
404 {
405 	struct hdac_ext_stream *hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
406 
407 	switch (cmd) {
408 	case SNDRV_PCM_TRIGGER_START:
409 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
410 		snd_hdac_ext_stream_start(hext_stream);
411 		break;
412 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
413 		/*
414 		 * Save the LLP registers since in case of PAUSE the LLP
415 		 * register are not reset to 0, the delay calculation will use
416 		 * the saved offsets for compensating the delay calculation.
417 		 */
418 		hext_stream->pplcllpl = readl(hext_stream->pplc_addr + AZX_REG_PPLCLLPL);
419 		hext_stream->pplcllpu = readl(hext_stream->pplc_addr + AZX_REG_PPLCLLPU);
420 		snd_hdac_ext_stream_clear(hext_stream);
421 		break;
422 	case SNDRV_PCM_TRIGGER_SUSPEND:
423 	case SNDRV_PCM_TRIGGER_STOP:
424 		hext_stream->pplcllpl = 0;
425 		hext_stream->pplcllpu = 0;
426 		snd_hdac_ext_stream_clear(hext_stream);
427 		break;
428 	default:
429 		dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
430 		return -EINVAL;
431 	}
432 
433 	return 0;
434 }
435 
436 static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
437 				 struct snd_pcm_substream *substream, int cmd)
438 {
439 	struct sof_ipc4_fw_data *ipc4_data = sdev->private;
440 	struct snd_sof_widget *pipe_widget;
441 	struct sof_ipc4_pipeline *pipeline;
442 	struct snd_sof_widget *swidget;
443 	struct snd_soc_dapm_widget *w;
444 	int ret = 0;
445 
446 	w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
447 	swidget = w->dobj.private;
448 	pipe_widget = swidget->spipe->pipe_widget;
449 	pipeline = pipe_widget->private;
450 
451 	if (pipe_widget->instance_id < 0)
452 		return 0;
453 
454 	guard(mutex)(&ipc4_data->pipeline_state_mutex);
455 
456 	switch (cmd) {
457 	case SNDRV_PCM_TRIGGER_START:
458 		if (pipeline->state != SOF_IPC4_PIPE_PAUSED) {
459 			ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
460 							  SOF_IPC4_PIPE_PAUSED);
461 			if (ret < 0)
462 				return ret;
463 
464 			pipeline->state = SOF_IPC4_PIPE_PAUSED;
465 		}
466 
467 		ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
468 						  SOF_IPC4_PIPE_RUNNING);
469 		if (ret < 0)
470 			return ret;
471 
472 		pipeline->state = SOF_IPC4_PIPE_RUNNING;
473 		swidget->spipe->started_count++;
474 		break;
475 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
476 		ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
477 						  SOF_IPC4_PIPE_RUNNING);
478 		if (ret < 0)
479 			return ret;
480 
481 		pipeline->state = SOF_IPC4_PIPE_RUNNING;
482 		break;
483 	case SNDRV_PCM_TRIGGER_SUSPEND:
484 	case SNDRV_PCM_TRIGGER_STOP:
485 		/*
486 		 * STOP/SUSPEND trigger is invoked only once when all users of this pipeline have
487 		 * been stopped. So, clear the started_count so that the pipeline can be reset
488 		 */
489 		swidget->spipe->started_count = 0;
490 		break;
491 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
492 		break;
493 	default:
494 		dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
495 		ret = -EINVAL;
496 		break;
497 	}
498 
499 	return ret;
500 }
501 
502 static const struct hda_dai_widget_dma_ops hda_ipc4_dma_ops = {
503 	.get_hext_stream = hda_ipc4_get_hext_stream,
504 	.assign_hext_stream = hda_assign_hext_stream,
505 	.release_hext_stream = hda_release_hext_stream,
506 	.setup_hext_stream = hda_setup_hext_stream,
507 	.reset_hext_stream = hda_reset_hext_stream,
508 	.pre_trigger = hda_ipc4_pre_trigger,
509 	.trigger = hda_trigger,
510 	.post_trigger = hda_ipc4_post_trigger,
511 	.codec_dai_set_stream = hda_codec_dai_set_stream,
512 	.calc_stream_format = hda_calc_stream_format,
513 	.get_hlink = hda_get_hlink,
514 };
515 
516 static const struct hda_dai_widget_dma_ops ssp_ipc4_dma_ops = {
517 	.get_hext_stream = hda_ipc4_get_hext_stream,
518 	.assign_hext_stream = hda_assign_hext_stream,
519 	.release_hext_stream = hda_release_hext_stream,
520 	.setup_hext_stream = hda_setup_hext_stream,
521 	.reset_hext_stream = hda_reset_hext_stream,
522 	.pre_trigger = hda_ipc4_pre_trigger,
523 	.trigger = hda_trigger,
524 	.post_trigger = hda_ipc4_post_trigger,
525 	.calc_stream_format = generic_calc_stream_format,
526 	.get_hlink = ssp_get_hlink,
527 };
528 
529 static const struct hda_dai_widget_dma_ops dmic_ipc4_dma_ops = {
530 	.get_hext_stream = hda_ipc4_get_hext_stream,
531 	.assign_hext_stream = hda_assign_hext_stream,
532 	.release_hext_stream = hda_release_hext_stream,
533 	.setup_hext_stream = hda_setup_hext_stream,
534 	.reset_hext_stream = hda_reset_hext_stream,
535 	.pre_trigger = hda_ipc4_pre_trigger,
536 	.trigger = hda_trigger,
537 	.post_trigger = hda_ipc4_post_trigger,
538 	.calc_stream_format = dmic_calc_stream_format,
539 	.get_hlink = dmic_get_hlink,
540 };
541 
542 static const struct hda_dai_widget_dma_ops sdw_ipc4_dma_ops = {
543 	.get_hext_stream = hda_ipc4_get_hext_stream,
544 	.assign_hext_stream = hda_assign_hext_stream,
545 	.release_hext_stream = hda_release_hext_stream,
546 	.setup_hext_stream = hda_setup_hext_stream,
547 	.reset_hext_stream = hda_reset_hext_stream,
548 	.pre_trigger = hda_ipc4_pre_trigger,
549 	.trigger = hda_trigger,
550 	.post_trigger = hda_ipc4_post_trigger,
551 	.calc_stream_format = generic_calc_stream_format,
552 	.get_hlink = sdw_get_hlink,
553 };
554 
555 static const struct hda_dai_widget_dma_ops hda_ipc4_chain_dma_ops = {
556 	.get_hext_stream = hda_get_hext_stream,
557 	.assign_hext_stream = hda_assign_hext_stream,
558 	.release_hext_stream = hda_release_hext_stream,
559 	.setup_hext_stream = hda_setup_hext_stream,
560 	.reset_hext_stream = hda_reset_hext_stream,
561 	.trigger = hda_trigger,
562 	.codec_dai_set_stream = hda_codec_dai_set_stream,
563 	.calc_stream_format = hda_calc_stream_format,
564 	.get_hlink = hda_get_hlink,
565 };
566 
567 static const struct hda_dai_widget_dma_ops sdw_ipc4_chain_dma_ops = {
568 	.get_hext_stream = hda_get_hext_stream,
569 	.assign_hext_stream = hda_assign_hext_stream,
570 	.release_hext_stream = hda_release_hext_stream,
571 	.setup_hext_stream = hda_setup_hext_stream,
572 	.reset_hext_stream = hda_reset_hext_stream,
573 	.trigger = hda_trigger,
574 	.calc_stream_format = generic_calc_stream_format,
575 	.get_hlink = sdw_get_hlink,
576 };
577 
578 static int hda_ipc3_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
579 				 struct snd_pcm_substream *substream, int cmd)
580 {
581 	struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
582 
583 	switch (cmd) {
584 	case SNDRV_PCM_TRIGGER_SUSPEND:
585 	case SNDRV_PCM_TRIGGER_STOP:
586 	{
587 		struct snd_sof_dai_config_data data = { 0 };
588 		int ret;
589 
590 		data.dai_data = DMA_CHAN_INVALID;
591 		ret = hda_dai_config(w, SOF_DAI_CONFIG_FLAGS_HW_FREE, &data);
592 		if (ret < 0)
593 			return ret;
594 
595 		break;
596 	}
597 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
598 		return hda_dai_config(w, SOF_DAI_CONFIG_FLAGS_PAUSE, NULL);
599 	default:
600 		break;
601 	}
602 
603 	return 0;
604 }
605 
606 static const struct hda_dai_widget_dma_ops hda_ipc3_dma_ops = {
607 	.get_hext_stream = hda_get_hext_stream,
608 	.assign_hext_stream = hda_assign_hext_stream,
609 	.release_hext_stream = hda_release_hext_stream,
610 	.setup_hext_stream = hda_setup_hext_stream,
611 	.reset_hext_stream = hda_reset_hext_stream,
612 	.trigger = hda_trigger,
613 	.post_trigger = hda_ipc3_post_trigger,
614 	.codec_dai_set_stream = hda_codec_dai_set_stream,
615 	.calc_stream_format = hda_calc_stream_format,
616 	.get_hlink = hda_get_hlink,
617 };
618 
619 static struct hdac_ext_stream *
620 hda_dspless_get_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
621 			    struct snd_pcm_substream *substream)
622 {
623 	struct hdac_stream *hstream = substream->runtime->private_data;
624 
625 	return stream_to_hdac_ext_stream(hstream);
626 }
627 
628 static void hda_dspless_setup_hext_stream(struct snd_sof_dev *sdev,
629 					  struct hdac_ext_stream *hext_stream,
630 					  unsigned int format_val)
631 {
632 	/*
633 	 * Save the format_val which was adjusted by the maxbps of the codec.
634 	 * This information is not available on the FE side since there we are
635 	 * using dummy_codec.
636 	 */
637 	hext_stream->hstream.format_val = format_val;
638 }
639 
640 static const struct hda_dai_widget_dma_ops hda_dspless_dma_ops = {
641 	.get_hext_stream = hda_dspless_get_hext_stream,
642 	.setup_hext_stream = hda_dspless_setup_hext_stream,
643 	.codec_dai_set_stream = hda_codec_dai_set_stream,
644 	.calc_stream_format = hda_calc_stream_format,
645 	.get_hlink = hda_get_hlink,
646 };
647 
648 static const struct hda_dai_widget_dma_ops sdw_dspless_dma_ops = {
649 	.get_hext_stream = hda_dspless_get_hext_stream,
650 	.setup_hext_stream = hda_dspless_setup_hext_stream,
651 	.calc_stream_format = generic_calc_stream_format,
652 	.get_hlink = sdw_get_hlink,
653 };
654 
655 #endif
656 
657 const struct hda_dai_widget_dma_ops *
658 hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
659 {
660 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK)
661 	struct snd_sof_dai *sdai;
662 	const struct sof_intel_dsp_desc *chip;
663 
664 	chip = get_chip_info(sdev->pdata);
665 	sdai = swidget->private;
666 
667 	if (sdev->dspless_mode_selected) {
668 		switch (sdai->type) {
669 		case SOF_DAI_INTEL_HDA:
670 			return &hda_dspless_dma_ops;
671 		case SOF_DAI_INTEL_ALH:
672 			if (chip->hw_ip_version < SOF_INTEL_ACE_2_0)
673 				return NULL;
674 			return &sdw_dspless_dma_ops;
675 		default:
676 			return NULL;
677 		}
678 	}
679 
680 	switch (sdev->pdata->ipc_type) {
681 	case SOF_IPC_TYPE_3:
682 	{
683 		struct sof_dai_private_data *private = sdai->private;
684 
685 		if (private->dai_config->type == SOF_DAI_INTEL_HDA)
686 			return &hda_ipc3_dma_ops;
687 		break;
688 	}
689 	case SOF_IPC_TYPE_4:
690 	{
691 		struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
692 		struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
693 
694 		switch (sdai->type) {
695 		case SOF_DAI_INTEL_HDA:
696 			if (pipeline->use_chain_dma)
697 				return &hda_ipc4_chain_dma_ops;
698 
699 			return &hda_ipc4_dma_ops;
700 		case SOF_DAI_INTEL_SSP:
701 			if (chip->hw_ip_version < SOF_INTEL_ACE_2_0)
702 				return NULL;
703 			return &ssp_ipc4_dma_ops;
704 		case SOF_DAI_INTEL_DMIC:
705 			if (chip->hw_ip_version < SOF_INTEL_ACE_2_0)
706 				return NULL;
707 			return &dmic_ipc4_dma_ops;
708 		case SOF_DAI_INTEL_ALH:
709 			if (chip->hw_ip_version < SOF_INTEL_ACE_2_0)
710 				return NULL;
711 			if (pipeline->use_chain_dma)
712 				return &sdw_ipc4_chain_dma_ops;
713 			return &sdw_ipc4_dma_ops;
714 
715 		default:
716 			break;
717 		}
718 		break;
719 	}
720 	default:
721 		break;
722 	}
723 #endif
724 	return NULL;
725 }
726