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