xref: /openbmc/linux/sound/soc/sof/intel/hda-dai.c (revision 2cf1c348)
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) 2018 Intel Corporation. All rights reserved.
7 //
8 // Authors: Keyon Jie <yang.jie@linux.intel.com>
9 //
10 
11 #include <sound/pcm_params.h>
12 #include <sound/hdaudio_ext.h>
13 #include "../sof-priv.h"
14 #include "../sof-audio.h"
15 #include "hda.h"
16 
17 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
18 
19 #if IS_ENABLED(CONFIG_SND_SOC_SOF_DEBUG_PROBES)
20 #include "../sof-probes.h"
21 #endif
22 
23 struct hda_pipe_params {
24 	u32 ch;
25 	u32 s_freq;
26 	u32 s_fmt;
27 	u8 linktype;
28 	snd_pcm_format_t format;
29 	int link_index;
30 	int stream;
31 	unsigned int link_bps;
32 };
33 
34 /*
35  * This function checks if the host dma channel corresponding
36  * to the link DMA stream_tag argument is assigned to one
37  * of the FEs connected to the BE DAI.
38  */
39 static bool hda_check_fes(struct snd_soc_pcm_runtime *rtd,
40 			  int dir, int stream_tag)
41 {
42 	struct snd_pcm_substream *fe_substream;
43 	struct hdac_stream *fe_hstream;
44 	struct snd_soc_dpcm *dpcm;
45 
46 	for_each_dpcm_fe(rtd, dir, dpcm) {
47 		fe_substream = snd_soc_dpcm_get_substream(dpcm->fe, dir);
48 		fe_hstream = fe_substream->runtime->private_data;
49 		if (fe_hstream->stream_tag == stream_tag)
50 			return true;
51 	}
52 
53 	return false;
54 }
55 
56 static struct hdac_ext_stream *
57 	hda_link_stream_assign(struct hdac_bus *bus,
58 			       struct snd_pcm_substream *substream)
59 {
60 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
61 	struct sof_intel_hda_stream *hda_stream;
62 	struct hdac_ext_stream *res = NULL;
63 	struct hdac_stream *stream = NULL;
64 
65 	int stream_dir = substream->stream;
66 
67 	if (!bus->ppcap) {
68 		dev_err(bus->dev, "stream type not supported\n");
69 		return NULL;
70 	}
71 
72 	spin_lock_irq(&bus->reg_lock);
73 	list_for_each_entry(stream, &bus->stream_list, list) {
74 		struct hdac_ext_stream *hstream =
75 			stream_to_hdac_ext_stream(stream);
76 		if (stream->direction != substream->stream)
77 			continue;
78 
79 		hda_stream = hstream_to_sof_hda_stream(hstream);
80 
81 		/* check if link is available */
82 		if (!hstream->link_locked) {
83 			if (stream->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 						  stream->stream_tag)) {
90 					res = hstream;
91 					break;
92 				}
93 			} else {
94 				res = hstream;
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 		/*
108 		 * Decouple host and link DMA. The decoupled flag
109 		 * is updated in snd_hdac_ext_stream_decouple().
110 		 */
111 		if (!res->decoupled)
112 			snd_hdac_ext_stream_decouple_locked(bus, res, true);
113 
114 		res->link_locked = 1;
115 		res->link_substream = substream;
116 	}
117 	spin_unlock_irq(&bus->reg_lock);
118 
119 	return res;
120 }
121 
122 static int hda_link_dma_params(struct hdac_ext_stream *stream,
123 			       struct hda_pipe_params *params)
124 {
125 	struct hdac_stream *hstream = &stream->hstream;
126 	unsigned char stream_tag = hstream->stream_tag;
127 	struct hdac_bus *bus = hstream->bus;
128 	struct hdac_ext_link *link;
129 	unsigned int format_val;
130 
131 	snd_hdac_ext_stream_decouple(bus, stream, true);
132 	snd_hdac_ext_link_stream_reset(stream);
133 
134 	format_val = snd_hdac_calc_stream_format(params->s_freq, params->ch,
135 						 params->format,
136 						 params->link_bps, 0);
137 
138 	dev_dbg(bus->dev, "format_val=%d, rate=%d, ch=%d, format=%d\n",
139 		format_val, params->s_freq, params->ch, params->format);
140 
141 	snd_hdac_ext_link_stream_setup(stream, format_val);
142 
143 	if (stream->hstream.direction == SNDRV_PCM_STREAM_PLAYBACK) {
144 		list_for_each_entry(link, &bus->hlink_list, list) {
145 			if (link->index == params->link_index)
146 				snd_hdac_ext_link_set_stream_id(link,
147 								stream_tag);
148 		}
149 	}
150 
151 	stream->link_prepared = 1;
152 
153 	return 0;
154 }
155 
156 /* Update config for the DAI widget */
157 static struct sof_ipc_dai_config *hda_dai_update_config(struct snd_soc_dapm_widget *w,
158 							int channel)
159 {
160 	struct snd_sof_widget *swidget = w->dobj.private;
161 	struct sof_ipc_dai_config *config;
162 	struct snd_sof_dai *sof_dai;
163 
164 	if (!swidget)
165 		return NULL;
166 
167 	sof_dai = swidget->private;
168 
169 	if (!sof_dai || !sof_dai->dai_config) {
170 		dev_err(swidget->scomp->dev, "error: No config for DAI %s\n", w->name);
171 		return NULL;
172 	}
173 
174 	config = &sof_dai->dai_config[sof_dai->current_config];
175 
176 	/* update config with stream tag */
177 	config->hda.link_dma_ch = channel;
178 
179 	return config;
180 }
181 
182 static int hda_link_dai_widget_update(struct sof_intel_hda_stream *hda_stream,
183 				      struct snd_soc_dapm_widget *w,
184 				      int channel, bool widget_setup)
185 {
186 	struct snd_sof_dev *sdev = hda_stream->sdev;
187 	struct sof_ipc_dai_config *config;
188 
189 	config = hda_dai_update_config(w, channel);
190 	if (!config) {
191 		dev_err(sdev->dev, "error: no config for DAI %s\n", w->name);
192 		return -ENOENT;
193 	}
194 
195 	/* set up/free DAI widget and send DAI_CONFIG IPC */
196 	if (widget_setup)
197 		return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_2_STEP_STOP);
198 
199 	return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE);
200 }
201 
202 static int hda_link_hw_params(struct snd_pcm_substream *substream,
203 			      struct snd_pcm_hw_params *params,
204 			      struct snd_soc_dai *dai)
205 {
206 	struct hdac_stream *hstream = substream->runtime->private_data;
207 	struct hdac_bus *bus = hstream->bus;
208 	struct hdac_ext_stream *link_dev;
209 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
210 	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
211 	struct sof_intel_hda_stream *hda_stream;
212 	struct hda_pipe_params p_params = {0};
213 	struct snd_soc_dapm_widget *w;
214 	struct hdac_ext_link *link;
215 	int stream_tag;
216 	int ret;
217 
218 	/* get stored dma data if resuming from system suspend */
219 	link_dev = snd_soc_dai_get_dma_data(dai, substream);
220 	if (!link_dev) {
221 		link_dev = hda_link_stream_assign(bus, substream);
222 		if (!link_dev)
223 			return -EBUSY;
224 
225 		snd_soc_dai_set_dma_data(dai, substream, (void *)link_dev);
226 	}
227 
228 	stream_tag = hdac_stream(link_dev)->stream_tag;
229 
230 	hda_stream = hstream_to_sof_hda_stream(link_dev);
231 
232 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
233 		w = dai->playback_widget;
234 	else
235 		w = dai->capture_widget;
236 
237 	/* set up the DAI widget and send the DAI_CONFIG with the new tag */
238 	ret = hda_link_dai_widget_update(hda_stream, w, stream_tag - 1, true);
239 	if (ret < 0)
240 		return ret;
241 
242 	link = snd_hdac_ext_bus_get_link(bus, codec_dai->component->name);
243 	if (!link)
244 		return -EINVAL;
245 
246 	/* set the hdac_stream in the codec dai */
247 	snd_soc_dai_set_stream(codec_dai, hdac_stream(link_dev), substream->stream);
248 
249 	p_params.s_fmt = snd_pcm_format_width(params_format(params));
250 	p_params.ch = params_channels(params);
251 	p_params.s_freq = params_rate(params);
252 	p_params.stream = substream->stream;
253 	p_params.link_index = link->index;
254 	p_params.format = params_format(params);
255 
256 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
257 		p_params.link_bps = codec_dai->driver->playback.sig_bits;
258 	else
259 		p_params.link_bps = codec_dai->driver->capture.sig_bits;
260 
261 	return hda_link_dma_params(link_dev, &p_params);
262 }
263 
264 static int hda_link_pcm_prepare(struct snd_pcm_substream *substream,
265 				struct snd_soc_dai *dai)
266 {
267 	struct hdac_ext_stream *link_dev =
268 				snd_soc_dai_get_dma_data(dai, substream);
269 	struct snd_sof_dev *sdev =
270 				snd_soc_component_get_drvdata(dai->component);
271 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
272 	int stream = substream->stream;
273 
274 	if (link_dev->link_prepared)
275 		return 0;
276 
277 	dev_dbg(sdev->dev, "hda: prepare stream dir %d\n", substream->stream);
278 
279 	return hda_link_hw_params(substream, &rtd->dpcm[stream].hw_params,
280 				  dai);
281 }
282 
283 static int hda_link_dai_config_pause_push_ipc(struct snd_soc_dapm_widget *w)
284 {
285 	struct snd_sof_widget *swidget = w->dobj.private;
286 	struct snd_soc_component *component = swidget->scomp;
287 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
288 	struct sof_ipc_dai_config *config;
289 	struct snd_sof_dai *sof_dai;
290 	struct sof_ipc_reply reply;
291 	int ret;
292 
293 	sof_dai = swidget->private;
294 
295 	if (!sof_dai || !sof_dai->dai_config) {
296 		dev_err(sdev->dev, "No config for DAI %s\n", w->name);
297 		return -EINVAL;
298 	}
299 
300 	config = &sof_dai->dai_config[sof_dai->current_config];
301 
302 	/* set PAUSE command flag */
303 	config->flags = FIELD_PREP(SOF_DAI_CONFIG_FLAGS_CMD_MASK, SOF_DAI_CONFIG_FLAGS_PAUSE);
304 
305 	ret = sof_ipc_tx_message(sdev->ipc, config->hdr.cmd, config, config->hdr.size,
306 				 &reply, sizeof(reply));
307 	if (ret < 0)
308 		dev_err(sdev->dev, "DAI config for %s failed during pause push\n", w->name);
309 
310 	return ret;
311 }
312 
313 static int hda_link_pcm_trigger(struct snd_pcm_substream *substream,
314 				int cmd, struct snd_soc_dai *dai)
315 {
316 	struct hdac_ext_stream *link_dev =
317 				snd_soc_dai_get_dma_data(dai, substream);
318 	struct sof_intel_hda_stream *hda_stream;
319 	struct snd_soc_pcm_runtime *rtd;
320 	struct snd_soc_dapm_widget *w;
321 	struct hdac_ext_link *link;
322 	struct hdac_stream *hstream;
323 	struct hdac_bus *bus;
324 	int stream_tag;
325 	int ret;
326 
327 	hstream = substream->runtime->private_data;
328 	bus = hstream->bus;
329 	rtd = asoc_substream_to_rtd(substream);
330 
331 	link = snd_hdac_ext_bus_get_link(bus, asoc_rtd_to_codec(rtd, 0)->component->name);
332 	if (!link)
333 		return -EINVAL;
334 
335 	hda_stream = hstream_to_sof_hda_stream(link_dev);
336 
337 	dev_dbg(dai->dev, "In %s cmd=%d\n", __func__, cmd);
338 
339 	w = snd_soc_dai_get_widget(dai, substream->stream);
340 
341 	switch (cmd) {
342 	case SNDRV_PCM_TRIGGER_START:
343 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
344 		snd_hdac_ext_link_stream_start(link_dev);
345 		break;
346 	case SNDRV_PCM_TRIGGER_SUSPEND:
347 	case SNDRV_PCM_TRIGGER_STOP:
348 		snd_hdac_ext_link_stream_clear(link_dev);
349 
350 		/*
351 		 * free DAI widget during stop/suspend to keep widget use_count's balanced.
352 		 */
353 		ret = hda_link_dai_widget_update(hda_stream, w, DMA_CHAN_INVALID, false);
354 		if (ret < 0)
355 			return ret;
356 
357 		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
358 			stream_tag = hdac_stream(link_dev)->stream_tag;
359 			snd_hdac_ext_link_clear_stream_id(link, stream_tag);
360 		}
361 
362 		link_dev->link_prepared = 0;
363 		break;
364 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
365 		snd_hdac_ext_link_stream_clear(link_dev);
366 
367 		ret = hda_link_dai_config_pause_push_ipc(w);
368 		if (ret < 0)
369 			return ret;
370 		break;
371 	default:
372 		return -EINVAL;
373 	}
374 	return 0;
375 }
376 
377 static int hda_link_hw_free(struct snd_pcm_substream *substream,
378 			    struct snd_soc_dai *dai)
379 {
380 	unsigned int stream_tag;
381 	struct sof_intel_hda_stream *hda_stream;
382 	struct hdac_bus *bus;
383 	struct hdac_ext_link *link;
384 	struct hdac_stream *hstream;
385 	struct snd_soc_pcm_runtime *rtd;
386 	struct hdac_ext_stream *link_dev;
387 	struct snd_soc_dapm_widget *w;
388 	int ret;
389 
390 	hstream = substream->runtime->private_data;
391 	bus = hstream->bus;
392 	rtd = asoc_substream_to_rtd(substream);
393 	link_dev = snd_soc_dai_get_dma_data(dai, substream);
394 
395 	if (!link_dev) {
396 		dev_dbg(dai->dev,
397 			"%s: link_dev is not assigned\n", __func__);
398 		return -EINVAL;
399 	}
400 
401 	hda_stream = hstream_to_sof_hda_stream(link_dev);
402 
403 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
404 		w = dai->playback_widget;
405 	else
406 		w = dai->capture_widget;
407 
408 	/* free the link DMA channel in the FW and the DAI widget */
409 	ret = hda_link_dai_widget_update(hda_stream, w, DMA_CHAN_INVALID, false);
410 	if (ret < 0)
411 		return ret;
412 
413 	link = snd_hdac_ext_bus_get_link(bus, asoc_rtd_to_codec(rtd, 0)->component->name);
414 	if (!link)
415 		return -EINVAL;
416 
417 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
418 		stream_tag = hdac_stream(link_dev)->stream_tag;
419 		snd_hdac_ext_link_clear_stream_id(link, stream_tag);
420 	}
421 
422 	snd_soc_dai_set_dma_data(dai, substream, NULL);
423 	snd_hdac_ext_stream_release(link_dev, HDAC_EXT_STREAM_TYPE_LINK);
424 	link_dev->link_prepared = 0;
425 
426 	/* free the host DMA channel reserved by hostless streams */
427 	hda_stream->host_reserved = 0;
428 
429 	return 0;
430 }
431 
432 static const struct snd_soc_dai_ops hda_link_dai_ops = {
433 	.hw_params = hda_link_hw_params,
434 	.hw_free = hda_link_hw_free,
435 	.trigger = hda_link_pcm_trigger,
436 	.prepare = hda_link_pcm_prepare,
437 };
438 
439 #endif
440 
441 /* only one flag used so far to harden hw_params/hw_free/trigger/prepare */
442 struct ssp_dai_dma_data {
443 	bool setup;
444 };
445 
446 static int ssp_dai_setup_or_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai,
447 				 bool setup)
448 {
449 	struct snd_soc_component *component;
450 	struct snd_sof_widget *swidget;
451 	struct snd_soc_dapm_widget *w;
452 	struct sof_ipc_fw_version *v;
453 	struct snd_sof_dev *sdev;
454 
455 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
456 		w = dai->playback_widget;
457 	else
458 		w = dai->capture_widget;
459 
460 	swidget = w->dobj.private;
461 	component = swidget->scomp;
462 	sdev = snd_soc_component_get_drvdata(component);
463 	v = &sdev->fw_ready.version;
464 
465 	/* DAI_CONFIG IPC during hw_params is not supported in older firmware */
466 	if (v->abi_version < SOF_ABI_VER(3, 18, 0))
467 		return 0;
468 
469 	if (setup)
470 		return hda_ctrl_dai_widget_setup(w, SOF_DAI_CONFIG_FLAGS_NONE);
471 
472 	return hda_ctrl_dai_widget_free(w, SOF_DAI_CONFIG_FLAGS_NONE);
473 }
474 
475 static int ssp_dai_startup(struct snd_pcm_substream *substream,
476 			   struct snd_soc_dai *dai)
477 {
478 	struct ssp_dai_dma_data *dma_data;
479 
480 	dma_data = kzalloc(sizeof(*dma_data), GFP_KERNEL);
481 	if (!dma_data)
482 		return -ENOMEM;
483 
484 	snd_soc_dai_set_dma_data(dai, substream, dma_data);
485 
486 	return 0;
487 }
488 
489 static int ssp_dai_setup(struct snd_pcm_substream *substream,
490 			 struct snd_soc_dai *dai,
491 			 bool setup)
492 {
493 	struct ssp_dai_dma_data *dma_data;
494 	int ret = 0;
495 
496 	dma_data = snd_soc_dai_get_dma_data(dai, substream);
497 	if (!dma_data) {
498 		dev_err(dai->dev, "%s: failed to get dma_data\n", __func__);
499 		return -EIO;
500 	}
501 
502 	if (dma_data->setup != setup) {
503 		ret = ssp_dai_setup_or_free(substream, dai, setup);
504 		if (!ret)
505 			dma_data->setup = setup;
506 	}
507 	return ret;
508 }
509 
510 static int ssp_dai_hw_params(struct snd_pcm_substream *substream,
511 			     struct snd_pcm_hw_params *params,
512 			     struct snd_soc_dai *dai)
513 {
514 	/* params are ignored for now */
515 	return ssp_dai_setup(substream, dai, true);
516 }
517 
518 static int ssp_dai_prepare(struct snd_pcm_substream *substream,
519 			   struct snd_soc_dai *dai)
520 {
521 	/*
522 	 * the SSP will only be reconfigured during resume operations and
523 	 * not in case of xruns
524 	 */
525 	return ssp_dai_setup(substream, dai, true);
526 }
527 
528 static int ssp_dai_trigger(struct snd_pcm_substream *substream,
529 			   int cmd, struct snd_soc_dai *dai)
530 {
531 	if (cmd != SNDRV_PCM_TRIGGER_SUSPEND)
532 		return 0;
533 
534 	return ssp_dai_setup(substream, dai, false);
535 }
536 
537 static int ssp_dai_hw_free(struct snd_pcm_substream *substream,
538 			   struct snd_soc_dai *dai)
539 {
540 	return ssp_dai_setup(substream, dai, false);
541 }
542 
543 static void ssp_dai_shutdown(struct snd_pcm_substream *substream,
544 			     struct snd_soc_dai *dai)
545 {
546 	struct ssp_dai_dma_data *dma_data;
547 
548 	dma_data = snd_soc_dai_get_dma_data(dai, substream);
549 	if (!dma_data) {
550 		dev_err(dai->dev, "%s: failed to get dma_data\n", __func__);
551 		return;
552 	}
553 	snd_soc_dai_set_dma_data(dai, substream, NULL);
554 	kfree(dma_data);
555 }
556 
557 static const struct snd_soc_dai_ops ssp_dai_ops = {
558 	.startup = ssp_dai_startup,
559 	.hw_params = ssp_dai_hw_params,
560 	.prepare = ssp_dai_prepare,
561 	.trigger = ssp_dai_trigger,
562 	.hw_free = ssp_dai_hw_free,
563 	.shutdown = ssp_dai_shutdown,
564 };
565 
566 /*
567  * common dai driver for skl+ platforms.
568  * some products who use this DAI array only physically have a subset of
569  * the DAIs, but no harm is done here by adding the whole set.
570  */
571 struct snd_soc_dai_driver skl_dai[] = {
572 {
573 	.name = "SSP0 Pin",
574 	.ops = &ssp_dai_ops,
575 	.playback = {
576 		.channels_min = 1,
577 		.channels_max = 8,
578 	},
579 	.capture = {
580 		.channels_min = 1,
581 		.channels_max = 8,
582 	},
583 },
584 {
585 	.name = "SSP1 Pin",
586 	.ops = &ssp_dai_ops,
587 	.playback = {
588 		.channels_min = 1,
589 		.channels_max = 8,
590 	},
591 	.capture = {
592 		.channels_min = 1,
593 		.channels_max = 8,
594 	},
595 },
596 {
597 	.name = "SSP2 Pin",
598 	.ops = &ssp_dai_ops,
599 	.playback = {
600 		.channels_min = 1,
601 		.channels_max = 8,
602 	},
603 	.capture = {
604 		.channels_min = 1,
605 		.channels_max = 8,
606 	},
607 },
608 {
609 	.name = "SSP3 Pin",
610 	.ops = &ssp_dai_ops,
611 	.playback = {
612 		.channels_min = 1,
613 		.channels_max = 8,
614 	},
615 	.capture = {
616 		.channels_min = 1,
617 		.channels_max = 8,
618 	},
619 },
620 {
621 	.name = "SSP4 Pin",
622 	.ops = &ssp_dai_ops,
623 	.playback = {
624 		.channels_min = 1,
625 		.channels_max = 8,
626 	},
627 	.capture = {
628 		.channels_min = 1,
629 		.channels_max = 8,
630 	},
631 },
632 {
633 	.name = "SSP5 Pin",
634 	.ops = &ssp_dai_ops,
635 	.playback = {
636 		.channels_min = 1,
637 		.channels_max = 8,
638 	},
639 	.capture = {
640 		.channels_min = 1,
641 		.channels_max = 8,
642 	},
643 },
644 {
645 	.name = "DMIC01 Pin",
646 	.capture = {
647 		.channels_min = 1,
648 		.channels_max = 4,
649 	},
650 },
651 {
652 	.name = "DMIC16k Pin",
653 	.capture = {
654 		.channels_min = 1,
655 		.channels_max = 4,
656 	},
657 },
658 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
659 {
660 	.name = "iDisp1 Pin",
661 	.ops = &hda_link_dai_ops,
662 	.playback = {
663 		.channels_min = 1,
664 		.channels_max = 8,
665 	},
666 },
667 {
668 	.name = "iDisp2 Pin",
669 	.ops = &hda_link_dai_ops,
670 	.playback = {
671 		.channels_min = 1,
672 		.channels_max = 8,
673 	},
674 },
675 {
676 	.name = "iDisp3 Pin",
677 	.ops = &hda_link_dai_ops,
678 	.playback = {
679 		.channels_min = 1,
680 		.channels_max = 8,
681 	},
682 },
683 {
684 	.name = "iDisp4 Pin",
685 	.ops = &hda_link_dai_ops,
686 	.playback = {
687 		.channels_min = 1,
688 		.channels_max = 8,
689 	},
690 },
691 {
692 	.name = "Analog CPU DAI",
693 	.ops = &hda_link_dai_ops,
694 	.playback = {
695 		.channels_min = 1,
696 		.channels_max = 16,
697 	},
698 	.capture = {
699 		.channels_min = 1,
700 		.channels_max = 16,
701 	},
702 },
703 {
704 	.name = "Digital CPU DAI",
705 	.ops = &hda_link_dai_ops,
706 	.playback = {
707 		.channels_min = 1,
708 		.channels_max = 16,
709 	},
710 	.capture = {
711 		.channels_min = 1,
712 		.channels_max = 16,
713 	},
714 },
715 {
716 	.name = "Alt Analog CPU DAI",
717 	.ops = &hda_link_dai_ops,
718 	.playback = {
719 		.channels_min = 1,
720 		.channels_max = 16,
721 	},
722 	.capture = {
723 		.channels_min = 1,
724 		.channels_max = 16,
725 	},
726 },
727 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES)
728 {
729 	.name = "Probe Extraction CPU DAI",
730 	.compress_new = snd_soc_new_compress,
731 	.cops = &sof_probe_compr_ops,
732 	.capture = {
733 		.stream_name = "Probe Extraction",
734 		.channels_min = 1,
735 		.channels_max = 8,
736 		.rates = SNDRV_PCM_RATE_48000,
737 		.rate_min = 48000,
738 		.rate_max = 48000,
739 	},
740 },
741 #endif
742 #endif
743 };
744