xref: /linux/sound/soc/sof/intel/hda-dai-ops.c (revision 4b132aacb0768ac1e652cf517097ea6f237214b9)
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 channel 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 {
47 	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
48 	struct sof_intel_hda_stream *hda_stream;
49 	const struct sof_intel_dsp_desc *chip;
50 	struct snd_sof_dev *sdev;
51 	struct hdac_ext_stream *res = NULL;
52 	struct hdac_stream *hstream = NULL;
53 
54 	int stream_dir = substream->stream;
55 
56 	if (!bus->ppcap) {
57 		dev_err(bus->dev, "stream type not supported\n");
58 		return NULL;
59 	}
60 
61 	spin_lock_irq(&bus->reg_lock);
62 	list_for_each_entry(hstream, &bus->stream_list, list) {
63 		struct hdac_ext_stream *hext_stream =
64 			stream_to_hdac_ext_stream(hstream);
65 		if (hstream->direction != substream->stream)
66 			continue;
67 
68 		hda_stream = hstream_to_sof_hda_stream(hext_stream);
69 		sdev = hda_stream->sdev;
70 		chip = get_chip_info(sdev->pdata);
71 
72 		/* check if link is available */
73 		if (!hext_stream->link_locked) {
74 			/*
75 			 * choose the first available link for platforms that do not have the
76 			 * PROCEN_FMT_QUIRK set.
77 			 */
78 			if (!(chip->quirks & SOF_INTEL_PROCEN_FMT_QUIRK)) {
79 				res = hext_stream;
80 				break;
81 			}
82 
83 			if (hstream->opened) {
84 				/*
85 				 * check if the stream tag matches the stream
86 				 * tag of one of the connected FEs
87 				 */
88 				if (hda_check_fes(rtd, stream_dir,
89 						  hstream->stream_tag)) {
90 					res = hext_stream;
91 					break;
92 				}
93 			} else {
94 				res = hext_stream;
95 
96 				/*
97 				 * This must be a hostless stream.
98 				 * So reserve the host DMA channel.
99 				 */
100 				hda_stream->host_reserved = 1;
101 				break;
102 			}
103 		}
104 	}
105 
106 	if (res) {
107 		/* Make sure that host and link DMA is decoupled. */
108 		snd_hdac_ext_stream_decouple_locked(bus, res, true);
109 
110 		res->link_locked = 1;
111 		res->link_substream = substream;
112 	}
113 	spin_unlock_irq(&bus->reg_lock);
114 
115 	return res;
116 }
117 
118 static struct hdac_ext_stream *hda_get_hext_stream(struct snd_sof_dev *sdev,
119 						   struct snd_soc_dai *cpu_dai,
120 						   struct snd_pcm_substream *substream)
121 {
122 	return snd_soc_dai_get_dma_data(cpu_dai, substream);
123 }
124 
125 static struct hdac_ext_stream *hda_ipc4_get_hext_stream(struct snd_sof_dev *sdev,
126 							struct snd_soc_dai *cpu_dai,
127 							struct snd_pcm_substream *substream)
128 {
129 	struct snd_sof_widget *pipe_widget;
130 	struct sof_ipc4_pipeline *pipeline;
131 	struct snd_sof_widget *swidget;
132 	struct snd_soc_dapm_widget *w;
133 
134 	w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
135 	swidget = w->dobj.private;
136 	pipe_widget = swidget->spipe->pipe_widget;
137 	pipeline = pipe_widget->private;
138 
139 	/* mark pipeline so that it can be skipped during FE trigger */
140 	pipeline->skip_during_fe_trigger = true;
141 
142 	return snd_soc_dai_get_dma_data(cpu_dai, substream);
143 }
144 
145 static struct hdac_ext_stream *hda_assign_hext_stream(struct snd_sof_dev *sdev,
146 						      struct snd_soc_dai *cpu_dai,
147 						      struct snd_pcm_substream *substream)
148 {
149 	struct hdac_ext_stream *hext_stream;
150 
151 	hext_stream = hda_link_stream_assign(sof_to_bus(sdev), substream);
152 	if (!hext_stream)
153 		return NULL;
154 
155 	snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)hext_stream);
156 
157 	return hext_stream;
158 }
159 
160 static void hda_release_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
161 				    struct snd_pcm_substream *substream)
162 {
163 	struct hdac_ext_stream *hext_stream = hda_get_hext_stream(sdev, cpu_dai, substream);
164 
165 	snd_soc_dai_set_dma_data(cpu_dai, substream, NULL);
166 	snd_hdac_ext_stream_release(hext_stream, HDAC_EXT_STREAM_TYPE_LINK);
167 }
168 
169 static void hda_setup_hext_stream(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream,
170 				  unsigned int format_val)
171 {
172 	snd_hdac_ext_stream_setup(hext_stream, format_val);
173 }
174 
175 static void hda_reset_hext_stream(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream)
176 {
177 	snd_hdac_ext_stream_reset(hext_stream);
178 }
179 
180 static void hda_codec_dai_set_stream(struct snd_sof_dev *sdev,
181 				     struct snd_pcm_substream *substream,
182 				     struct hdac_stream *hstream)
183 {
184 	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
185 	struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
186 
187 	/* set the hdac_stream in the codec dai */
188 	snd_soc_dai_set_stream(codec_dai, hstream, substream->stream);
189 }
190 
191 static unsigned int hda_calc_stream_format(struct snd_sof_dev *sdev,
192 					   struct snd_pcm_substream *substream,
193 					   struct snd_pcm_hw_params *params)
194 {
195 	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
196 	struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
197 	unsigned int link_bps;
198 	unsigned int format_val;
199 	unsigned int bits;
200 
201 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
202 		link_bps = codec_dai->driver->playback.sig_bits;
203 	else
204 		link_bps = codec_dai->driver->capture.sig_bits;
205 
206 	bits = snd_hdac_stream_format_bits(params_format(params), SNDRV_PCM_SUBFORMAT_STD,
207 					   link_bps);
208 	format_val = snd_hdac_stream_format(params_channels(params), bits, params_rate(params));
209 
210 	dev_dbg(sdev->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val,
211 		params_rate(params), params_channels(params), params_format(params));
212 
213 	return format_val;
214 }
215 
216 static struct hdac_ext_link *hda_get_hlink(struct snd_sof_dev *sdev,
217 					   struct snd_pcm_substream *substream)
218 {
219 	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
220 	struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
221 	struct hdac_bus *bus = sof_to_bus(sdev);
222 
223 	return snd_hdac_ext_bus_get_hlink_by_name(bus, codec_dai->component->name);
224 }
225 
226 static unsigned int generic_calc_stream_format(struct snd_sof_dev *sdev,
227 					       struct snd_pcm_substream *substream,
228 					       struct snd_pcm_hw_params *params)
229 {
230 	unsigned int format_val;
231 	unsigned int bits;
232 
233 	bits = snd_hdac_stream_format_bits(params_format(params), SNDRV_PCM_SUBFORMAT_STD,
234 					   params_physical_width(params));
235 	format_val = snd_hdac_stream_format(params_channels(params), bits, params_rate(params));
236 
237 	dev_dbg(sdev->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val,
238 		params_rate(params), params_channels(params), params_format(params));
239 
240 	return format_val;
241 }
242 
243 static unsigned int dmic_calc_stream_format(struct snd_sof_dev *sdev,
244 					    struct snd_pcm_substream *substream,
245 					    struct snd_pcm_hw_params *params)
246 {
247 	unsigned int format_val;
248 	snd_pcm_format_t format;
249 	unsigned int channels;
250 	unsigned int width;
251 	unsigned int bits;
252 
253 	channels = params_channels(params);
254 	format = params_format(params);
255 	width = params_physical_width(params);
256 
257 	if (format == SNDRV_PCM_FORMAT_S16_LE) {
258 		format = SNDRV_PCM_FORMAT_S32_LE;
259 		channels /= 2;
260 		width = 32;
261 	}
262 
263 	bits = snd_hdac_stream_format_bits(format, SNDRV_PCM_SUBFORMAT_STD, width);
264 	format_val = snd_hdac_stream_format(channels, bits, params_rate(params));
265 
266 	dev_dbg(sdev->dev, "format_val=%#x, rate=%d, ch=%d, format=%d\n", format_val,
267 		params_rate(params), channels, format);
268 
269 	return format_val;
270 }
271 
272 static struct hdac_ext_link *ssp_get_hlink(struct snd_sof_dev *sdev,
273 					   struct snd_pcm_substream *substream)
274 {
275 	struct hdac_bus *bus = sof_to_bus(sdev);
276 
277 	return hdac_bus_eml_ssp_get_hlink(bus);
278 }
279 
280 static struct hdac_ext_link *dmic_get_hlink(struct snd_sof_dev *sdev,
281 					    struct snd_pcm_substream *substream)
282 {
283 	struct hdac_bus *bus = sof_to_bus(sdev);
284 
285 	return hdac_bus_eml_dmic_get_hlink(bus);
286 }
287 
288 static struct hdac_ext_link *sdw_get_hlink(struct snd_sof_dev *sdev,
289 					   struct snd_pcm_substream *substream)
290 {
291 	struct hdac_bus *bus = sof_to_bus(sdev);
292 
293 	return hdac_bus_eml_sdw_get_hlink(bus);
294 }
295 
296 static int hda_ipc4_pre_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
297 				struct snd_pcm_substream *substream, int cmd)
298 {
299 	struct sof_ipc4_fw_data *ipc4_data = sdev->private;
300 	struct snd_sof_widget *pipe_widget;
301 	struct sof_ipc4_pipeline *pipeline;
302 	struct snd_sof_widget *swidget;
303 	struct snd_soc_dapm_widget *w;
304 	int ret = 0;
305 
306 	w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
307 	swidget = w->dobj.private;
308 	pipe_widget = swidget->spipe->pipe_widget;
309 	pipeline = pipe_widget->private;
310 
311 	if (pipe_widget->instance_id < 0)
312 		return 0;
313 
314 	mutex_lock(&ipc4_data->pipeline_state_mutex);
315 
316 	switch (cmd) {
317 	case SNDRV_PCM_TRIGGER_START:
318 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
319 		break;
320 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
321 	case SNDRV_PCM_TRIGGER_SUSPEND:
322 	case SNDRV_PCM_TRIGGER_STOP:
323 		ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
324 						  SOF_IPC4_PIPE_PAUSED);
325 		if (ret < 0)
326 			goto out;
327 
328 		pipeline->state = SOF_IPC4_PIPE_PAUSED;
329 		break;
330 	default:
331 		dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
332 		ret = -EINVAL;
333 	}
334 out:
335 	mutex_unlock(&ipc4_data->pipeline_state_mutex);
336 	return ret;
337 }
338 
339 static int hda_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
340 		       struct snd_pcm_substream *substream, int cmd)
341 {
342 	struct hdac_ext_stream *hext_stream = snd_soc_dai_get_dma_data(cpu_dai, substream);
343 
344 	switch (cmd) {
345 	case SNDRV_PCM_TRIGGER_START:
346 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
347 		snd_hdac_ext_stream_start(hext_stream);
348 		break;
349 	case SNDRV_PCM_TRIGGER_SUSPEND:
350 	case SNDRV_PCM_TRIGGER_STOP:
351 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
352 		snd_hdac_ext_stream_clear(hext_stream);
353 
354 		/*
355 		 * Save the LLP registers in case the stream is
356 		 * restarting due PAUSE_RELEASE, or START without a pcm
357 		 * close/open since in this case the LLP register is not reset
358 		 * to 0 and the delay calculation will return with invalid
359 		 * results.
360 		 */
361 		hext_stream->pplcllpl = readl(hext_stream->pplc_addr + AZX_REG_PPLCLLPL);
362 		hext_stream->pplcllpu = readl(hext_stream->pplc_addr + AZX_REG_PPLCLLPU);
363 		break;
364 	default:
365 		dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
366 		return -EINVAL;
367 	}
368 
369 	return 0;
370 }
371 
372 static int hda_ipc4_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
373 				 struct snd_pcm_substream *substream, int cmd)
374 {
375 	struct sof_ipc4_fw_data *ipc4_data = sdev->private;
376 	struct snd_sof_widget *pipe_widget;
377 	struct sof_ipc4_pipeline *pipeline;
378 	struct snd_sof_widget *swidget;
379 	struct snd_soc_dapm_widget *w;
380 	int ret = 0;
381 
382 	w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
383 	swidget = w->dobj.private;
384 	pipe_widget = swidget->spipe->pipe_widget;
385 	pipeline = pipe_widget->private;
386 
387 	if (pipe_widget->instance_id < 0)
388 		return 0;
389 
390 	mutex_lock(&ipc4_data->pipeline_state_mutex);
391 
392 	switch (cmd) {
393 	case SNDRV_PCM_TRIGGER_START:
394 		if (pipeline->state != SOF_IPC4_PIPE_PAUSED) {
395 			ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
396 							  SOF_IPC4_PIPE_PAUSED);
397 			if (ret < 0)
398 				goto out;
399 			pipeline->state = SOF_IPC4_PIPE_PAUSED;
400 		}
401 
402 		ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
403 						  SOF_IPC4_PIPE_RUNNING);
404 		if (ret < 0)
405 			goto out;
406 		pipeline->state = SOF_IPC4_PIPE_RUNNING;
407 		swidget->spipe->started_count++;
408 		break;
409 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
410 		ret = sof_ipc4_set_pipeline_state(sdev, pipe_widget->instance_id,
411 						  SOF_IPC4_PIPE_RUNNING);
412 		if (ret < 0)
413 			goto out;
414 		pipeline->state = SOF_IPC4_PIPE_RUNNING;
415 		break;
416 	case SNDRV_PCM_TRIGGER_SUSPEND:
417 	case SNDRV_PCM_TRIGGER_STOP:
418 		/*
419 		 * STOP/SUSPEND trigger is invoked only once when all users of this pipeline have
420 		 * been stopped. So, clear the started_count so that the pipeline can be reset
421 		 */
422 		swidget->spipe->started_count = 0;
423 		break;
424 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
425 		break;
426 	default:
427 		dev_err(sdev->dev, "unknown trigger command %d\n", cmd);
428 		ret = -EINVAL;
429 		break;
430 	}
431 out:
432 	mutex_unlock(&ipc4_data->pipeline_state_mutex);
433 	return ret;
434 }
435 
436 static const struct hda_dai_widget_dma_ops hda_ipc4_dma_ops = {
437 	.get_hext_stream = hda_ipc4_get_hext_stream,
438 	.assign_hext_stream = hda_assign_hext_stream,
439 	.release_hext_stream = hda_release_hext_stream,
440 	.setup_hext_stream = hda_setup_hext_stream,
441 	.reset_hext_stream = hda_reset_hext_stream,
442 	.pre_trigger = hda_ipc4_pre_trigger,
443 	.trigger = hda_trigger,
444 	.post_trigger = hda_ipc4_post_trigger,
445 	.codec_dai_set_stream = hda_codec_dai_set_stream,
446 	.calc_stream_format = hda_calc_stream_format,
447 	.get_hlink = hda_get_hlink,
448 };
449 
450 static const struct hda_dai_widget_dma_ops ssp_ipc4_dma_ops = {
451 	.get_hext_stream = hda_ipc4_get_hext_stream,
452 	.assign_hext_stream = hda_assign_hext_stream,
453 	.release_hext_stream = hda_release_hext_stream,
454 	.setup_hext_stream = hda_setup_hext_stream,
455 	.reset_hext_stream = hda_reset_hext_stream,
456 	.pre_trigger = hda_ipc4_pre_trigger,
457 	.trigger = hda_trigger,
458 	.post_trigger = hda_ipc4_post_trigger,
459 	.calc_stream_format = generic_calc_stream_format,
460 	.get_hlink = ssp_get_hlink,
461 };
462 
463 static const struct hda_dai_widget_dma_ops dmic_ipc4_dma_ops = {
464 	.get_hext_stream = hda_ipc4_get_hext_stream,
465 	.assign_hext_stream = hda_assign_hext_stream,
466 	.release_hext_stream = hda_release_hext_stream,
467 	.setup_hext_stream = hda_setup_hext_stream,
468 	.reset_hext_stream = hda_reset_hext_stream,
469 	.pre_trigger = hda_ipc4_pre_trigger,
470 	.trigger = hda_trigger,
471 	.post_trigger = hda_ipc4_post_trigger,
472 	.calc_stream_format = dmic_calc_stream_format,
473 	.get_hlink = dmic_get_hlink,
474 };
475 
476 static const struct hda_dai_widget_dma_ops sdw_ipc4_dma_ops = {
477 	.get_hext_stream = hda_ipc4_get_hext_stream,
478 	.assign_hext_stream = hda_assign_hext_stream,
479 	.release_hext_stream = hda_release_hext_stream,
480 	.setup_hext_stream = hda_setup_hext_stream,
481 	.reset_hext_stream = hda_reset_hext_stream,
482 	.pre_trigger = hda_ipc4_pre_trigger,
483 	.trigger = hda_trigger,
484 	.post_trigger = hda_ipc4_post_trigger,
485 	.calc_stream_format = generic_calc_stream_format,
486 	.get_hlink = sdw_get_hlink,
487 };
488 
489 static const struct hda_dai_widget_dma_ops hda_ipc4_chain_dma_ops = {
490 	.get_hext_stream = hda_get_hext_stream,
491 	.assign_hext_stream = hda_assign_hext_stream,
492 	.release_hext_stream = hda_release_hext_stream,
493 	.setup_hext_stream = hda_setup_hext_stream,
494 	.reset_hext_stream = hda_reset_hext_stream,
495 	.trigger = hda_trigger,
496 	.codec_dai_set_stream = hda_codec_dai_set_stream,
497 	.calc_stream_format = hda_calc_stream_format,
498 	.get_hlink = hda_get_hlink,
499 };
500 
501 static const struct hda_dai_widget_dma_ops sdw_ipc4_chain_dma_ops = {
502 	.get_hext_stream = hda_get_hext_stream,
503 	.assign_hext_stream = hda_assign_hext_stream,
504 	.release_hext_stream = hda_release_hext_stream,
505 	.setup_hext_stream = hda_setup_hext_stream,
506 	.reset_hext_stream = hda_reset_hext_stream,
507 	.trigger = hda_trigger,
508 	.calc_stream_format = generic_calc_stream_format,
509 	.get_hlink = sdw_get_hlink,
510 };
511 
512 static int hda_ipc3_post_trigger(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
513 				 struct snd_pcm_substream *substream, int cmd)
514 {
515 	struct hdac_ext_stream *hext_stream = hda_get_hext_stream(sdev, cpu_dai, substream);
516 	struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
517 
518 	switch (cmd) {
519 	case SNDRV_PCM_TRIGGER_SUSPEND:
520 	case SNDRV_PCM_TRIGGER_STOP:
521 	{
522 		struct snd_sof_dai_config_data data = { 0 };
523 		int ret;
524 
525 		data.dai_data = DMA_CHAN_INVALID;
526 		ret = hda_dai_config(w, SOF_DAI_CONFIG_FLAGS_HW_FREE, &data);
527 		if (ret < 0)
528 			return ret;
529 
530 		if (cmd == SNDRV_PCM_TRIGGER_STOP)
531 			return hda_link_dma_cleanup(substream, hext_stream, cpu_dai);
532 
533 		break;
534 	}
535 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
536 		return hda_dai_config(w, SOF_DAI_CONFIG_FLAGS_PAUSE, NULL);
537 	default:
538 		break;
539 	}
540 
541 	return 0;
542 }
543 
544 static const struct hda_dai_widget_dma_ops hda_ipc3_dma_ops = {
545 	.get_hext_stream = hda_get_hext_stream,
546 	.assign_hext_stream = hda_assign_hext_stream,
547 	.release_hext_stream = hda_release_hext_stream,
548 	.setup_hext_stream = hda_setup_hext_stream,
549 	.reset_hext_stream = hda_reset_hext_stream,
550 	.trigger = hda_trigger,
551 	.post_trigger = hda_ipc3_post_trigger,
552 	.codec_dai_set_stream = hda_codec_dai_set_stream,
553 	.calc_stream_format = hda_calc_stream_format,
554 	.get_hlink = hda_get_hlink,
555 };
556 
557 static struct hdac_ext_stream *
558 hda_dspless_get_hext_stream(struct snd_sof_dev *sdev, struct snd_soc_dai *cpu_dai,
559 			    struct snd_pcm_substream *substream)
560 {
561 	struct hdac_stream *hstream = substream->runtime->private_data;
562 
563 	return stream_to_hdac_ext_stream(hstream);
564 }
565 
566 static void hda_dspless_setup_hext_stream(struct snd_sof_dev *sdev,
567 					  struct hdac_ext_stream *hext_stream,
568 					  unsigned int format_val)
569 {
570 	/*
571 	 * Save the format_val which was adjusted by the maxbps of the codec.
572 	 * This information is not available on the FE side since there we are
573 	 * using dummy_codec.
574 	 */
575 	hext_stream->hstream.format_val = format_val;
576 }
577 
578 static const struct hda_dai_widget_dma_ops hda_dspless_dma_ops = {
579 	.get_hext_stream = hda_dspless_get_hext_stream,
580 	.setup_hext_stream = hda_dspless_setup_hext_stream,
581 	.codec_dai_set_stream = hda_codec_dai_set_stream,
582 	.calc_stream_format = hda_calc_stream_format,
583 	.get_hlink = hda_get_hlink,
584 };
585 
586 static const struct hda_dai_widget_dma_ops sdw_dspless_dma_ops = {
587 	.get_hext_stream = hda_dspless_get_hext_stream,
588 	.setup_hext_stream = hda_dspless_setup_hext_stream,
589 	.calc_stream_format = generic_calc_stream_format,
590 	.get_hlink = sdw_get_hlink,
591 };
592 
593 #endif
594 
595 const struct hda_dai_widget_dma_ops *
596 hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
597 {
598 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK)
599 	struct snd_sof_dai *sdai;
600 	const struct sof_intel_dsp_desc *chip;
601 
602 	chip = get_chip_info(sdev->pdata);
603 	sdai = swidget->private;
604 
605 	if (sdev->dspless_mode_selected) {
606 		switch (sdai->type) {
607 		case SOF_DAI_INTEL_HDA:
608 			return &hda_dspless_dma_ops;
609 		case SOF_DAI_INTEL_ALH:
610 			if (chip->hw_ip_version < SOF_INTEL_ACE_2_0)
611 				return NULL;
612 			return &sdw_dspless_dma_ops;
613 		default:
614 			return NULL;
615 		}
616 	}
617 
618 	switch (sdev->pdata->ipc_type) {
619 	case SOF_IPC_TYPE_3:
620 	{
621 		struct sof_dai_private_data *private = sdai->private;
622 
623 		if (private->dai_config->type == SOF_DAI_INTEL_HDA)
624 			return &hda_ipc3_dma_ops;
625 		break;
626 	}
627 	case SOF_IPC_TYPE_4:
628 	{
629 		struct snd_sof_widget *pipe_widget = swidget->spipe->pipe_widget;
630 		struct sof_ipc4_pipeline *pipeline = pipe_widget->private;
631 
632 		switch (sdai->type) {
633 		case SOF_DAI_INTEL_HDA:
634 			if (pipeline->use_chain_dma)
635 				return &hda_ipc4_chain_dma_ops;
636 
637 			return &hda_ipc4_dma_ops;
638 		case SOF_DAI_INTEL_SSP:
639 			if (chip->hw_ip_version < SOF_INTEL_ACE_2_0)
640 				return NULL;
641 			return &ssp_ipc4_dma_ops;
642 		case SOF_DAI_INTEL_DMIC:
643 			if (chip->hw_ip_version < SOF_INTEL_ACE_2_0)
644 				return NULL;
645 			return &dmic_ipc4_dma_ops;
646 		case SOF_DAI_INTEL_ALH:
647 			if (chip->hw_ip_version < SOF_INTEL_ACE_2_0)
648 				return NULL;
649 			if (pipeline->use_chain_dma)
650 				return &sdw_ipc4_chain_dma_ops;
651 			return &sdw_ipc4_dma_ops;
652 
653 		default:
654 			break;
655 		}
656 		break;
657 	}
658 	default:
659 		break;
660 	}
661 #endif
662 	return NULL;
663 }
664