Merge tag 'f2fs-for-6.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk...
[linux-2.6-block.git] / sound / soc / sof / intel / hda-dai.c
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 <sound/hda-mlink.h>
14 #include <sound/hda_register.h>
15 #include <sound/intel-nhlt.h>
16 #include <sound/sof/ipc4/header.h>
17 #include <uapi/sound/sof/header.h>
18 #include "../ipc4-priv.h"
19 #include "../ipc4-topology.h"
20 #include "../sof-priv.h"
21 #include "../sof-audio.h"
22 #include "hda.h"
23
24 /*
25  * The default method is to fetch NHLT from BIOS. With this parameter set
26  * it is possible to override that with NHLT in the SOF topology manifest.
27  */
28 static bool hda_use_tplg_nhlt;
29 module_param_named(sof_use_tplg_nhlt, hda_use_tplg_nhlt, bool, 0444);
30 MODULE_PARM_DESC(sof_use_tplg_nhlt, "SOF topology nhlt override");
31
32 static struct snd_sof_dev *widget_to_sdev(struct snd_soc_dapm_widget *w)
33 {
34         struct snd_sof_widget *swidget = w->dobj.private;
35         struct snd_soc_component *component = swidget->scomp;
36
37         return snd_soc_component_get_drvdata(component);
38 }
39
40 int hda_dai_config(struct snd_soc_dapm_widget *w, unsigned int flags,
41                    struct snd_sof_dai_config_data *data)
42 {
43         struct snd_sof_widget *swidget = w->dobj.private;
44         const struct sof_ipc_tplg_ops *tplg_ops;
45         struct snd_sof_dev *sdev;
46         int ret;
47
48         if (!swidget)
49                 return 0;
50
51         sdev = widget_to_sdev(w);
52         tplg_ops = sof_ipc_get_ops(sdev, tplg);
53
54         if (tplg_ops && tplg_ops->dai_config) {
55                 ret = tplg_ops->dai_config(sdev, swidget, flags, data);
56                 if (ret < 0) {
57                         dev_err(sdev->dev, "DAI config with flags %x failed for widget %s\n",
58                                 flags, w->name);
59                         return ret;
60                 }
61         }
62
63         return 0;
64 }
65
66 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK)
67
68 static struct snd_sof_dev *dai_to_sdev(struct snd_pcm_substream *substream,
69                                        struct snd_soc_dai *cpu_dai)
70 {
71         struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
72
73         return widget_to_sdev(w);
74 }
75
76 static const struct hda_dai_widget_dma_ops *
77 hda_dai_get_ops(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai)
78 {
79         struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
80         struct snd_sof_widget *swidget = w->dobj.private;
81         struct snd_sof_dev *sdev;
82         struct snd_sof_dai *sdai;
83
84         sdev = widget_to_sdev(w);
85
86         if (!swidget) {
87                 dev_err(sdev->dev, "%s: swidget is NULL\n", __func__);
88                 return NULL;
89         }
90
91         if (sdev->dspless_mode_selected)
92                 return hda_select_dai_widget_ops(sdev, swidget);
93
94         sdai = swidget->private;
95
96         /* select and set the DAI widget ops if not set already */
97         if (!sdai->platform_private) {
98                 const struct hda_dai_widget_dma_ops *ops =
99                         hda_select_dai_widget_ops(sdev, swidget);
100                 if (!ops)
101                         return NULL;
102
103                 /* check if mandatory ops are set */
104                 if (!ops || !ops->get_hext_stream)
105                         return NULL;
106
107                 sdai->platform_private = ops;
108         }
109
110         return sdai->platform_private;
111 }
112
113 int hda_link_dma_cleanup(struct snd_pcm_substream *substream, struct hdac_ext_stream *hext_stream,
114                          struct snd_soc_dai *cpu_dai)
115 {
116         const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai);
117         struct sof_intel_hda_stream *hda_stream;
118         struct hdac_ext_link *hlink;
119         struct snd_sof_dev *sdev;
120         int stream_tag;
121
122         if (!ops) {
123                 dev_err(cpu_dai->dev, "DAI widget ops not set\n");
124                 return -EINVAL;
125         }
126
127         sdev = dai_to_sdev(substream, cpu_dai);
128
129         hlink = ops->get_hlink(sdev, substream);
130         if (!hlink)
131                 return -EINVAL;
132
133         if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
134                 stream_tag = hdac_stream(hext_stream)->stream_tag;
135                 snd_hdac_ext_bus_link_clear_stream_id(hlink, stream_tag);
136         }
137
138         if (ops->release_hext_stream)
139                 ops->release_hext_stream(sdev, cpu_dai, substream);
140
141         hext_stream->link_prepared = 0;
142
143         /* free the host DMA channel reserved by hostless streams */
144         hda_stream = hstream_to_sof_hda_stream(hext_stream);
145         hda_stream->host_reserved = 0;
146
147         return 0;
148 }
149
150 static int hda_link_dma_hw_params(struct snd_pcm_substream *substream,
151                                   struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai)
152 {
153         const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai);
154         struct hdac_ext_stream *hext_stream;
155         struct hdac_stream *hstream;
156         struct hdac_ext_link *hlink;
157         struct snd_sof_dev *sdev;
158         int stream_tag;
159
160         if (!ops) {
161                 dev_err(cpu_dai->dev, "DAI widget ops not set\n");
162                 return -EINVAL;
163         }
164
165         sdev = dai_to_sdev(substream, cpu_dai);
166
167         hlink = ops->get_hlink(sdev, substream);
168         if (!hlink)
169                 return -EINVAL;
170
171         hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream);
172
173         if (!hext_stream) {
174                 if (ops->assign_hext_stream)
175                         hext_stream = ops->assign_hext_stream(sdev, cpu_dai, substream);
176         }
177
178         if (!hext_stream)
179                 return -EBUSY;
180
181         hstream = &hext_stream->hstream;
182         stream_tag = hstream->stream_tag;
183
184         if (hext_stream->hstream.direction == SNDRV_PCM_STREAM_PLAYBACK)
185                 snd_hdac_ext_bus_link_set_stream_id(hlink, stream_tag);
186
187         /* set the hdac_stream in the codec dai */
188         if (ops->codec_dai_set_stream)
189                 ops->codec_dai_set_stream(sdev, substream, hstream);
190
191         if (ops->reset_hext_stream)
192                 ops->reset_hext_stream(sdev, hext_stream);
193
194         if (ops->calc_stream_format && ops->setup_hext_stream) {
195                 unsigned int format_val = ops->calc_stream_format(sdev, substream, params);
196
197                 ops->setup_hext_stream(sdev, hext_stream, format_val);
198         }
199
200         hext_stream->link_prepared = 1;
201
202         return 0;
203 }
204
205 static int __maybe_unused hda_dai_hw_free(struct snd_pcm_substream *substream,
206                                           struct snd_soc_dai *cpu_dai)
207 {
208         const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai);
209         struct hdac_ext_stream *hext_stream;
210         struct snd_sof_dev *sdev = dai_to_sdev(substream, cpu_dai);
211
212         if (!ops) {
213                 dev_err(cpu_dai->dev, "DAI widget ops not set\n");
214                 return -EINVAL;
215         }
216
217         hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream);
218         if (!hext_stream)
219                 return 0;
220
221         return hda_link_dma_cleanup(substream, hext_stream, cpu_dai);
222 }
223
224 static int __maybe_unused hda_dai_hw_params(struct snd_pcm_substream *substream,
225                                             struct snd_pcm_hw_params *params,
226                                             struct snd_soc_dai *dai)
227 {
228         struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, substream->stream);
229         const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai);
230         struct hdac_ext_stream *hext_stream;
231         struct snd_sof_dai_config_data data = { 0 };
232         unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS;
233         struct snd_sof_dev *sdev = widget_to_sdev(w);
234         int ret;
235
236         if (!ops) {
237                 dev_err(sdev->dev, "DAI widget ops not set\n");
238                 return -EINVAL;
239         }
240
241         hext_stream = ops->get_hext_stream(sdev, dai, substream);
242         if (hext_stream && hext_stream->link_prepared)
243                 return 0;
244
245         ret = hda_link_dma_hw_params(substream, params, dai);
246         if (ret < 0)
247                 return ret;
248
249         hext_stream = ops->get_hext_stream(sdev, dai, substream);
250
251         flags |= SOF_DAI_CONFIG_FLAGS_2_STEP_STOP << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT;
252         data.dai_data = hdac_stream(hext_stream)->stream_tag - 1;
253
254         return hda_dai_config(w, flags, &data);
255 }
256
257 /*
258  * In contrast to IPC3, the dai trigger in IPC4 mixes pipeline state changes
259  * (over IPC channel) and DMA state change (direct host register changes).
260  */
261 static int __maybe_unused hda_dai_trigger(struct snd_pcm_substream *substream, int cmd,
262                                           struct snd_soc_dai *dai)
263 {
264         const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai);
265         struct hdac_ext_stream *hext_stream;
266         struct snd_sof_dev *sdev;
267         int ret;
268
269         if (!ops) {
270                 dev_err(dai->dev, "DAI widget ops not set\n");
271                 return -EINVAL;
272         }
273
274         dev_dbg(dai->dev, "cmd=%d dai %s direction %d\n", cmd,
275                 dai->name, substream->stream);
276
277         sdev = dai_to_sdev(substream, dai);
278
279         hext_stream = ops->get_hext_stream(sdev, dai, substream);
280         if (!hext_stream)
281                 return -EINVAL;
282
283         if (ops->pre_trigger) {
284                 ret = ops->pre_trigger(sdev, dai, substream, cmd);
285                 if (ret < 0)
286                         return ret;
287         }
288
289         if (ops->trigger) {
290                 ret = ops->trigger(sdev, dai, substream, cmd);
291                 if (ret < 0)
292                         return ret;
293         }
294
295         if (ops->post_trigger) {
296                 ret = ops->post_trigger(sdev, dai, substream, cmd);
297                 if (ret < 0)
298                         return ret;
299         }
300
301         switch (cmd) {
302         case SNDRV_PCM_TRIGGER_SUSPEND:
303                 ret = hda_link_dma_cleanup(substream, hext_stream, dai);
304                 if (ret < 0) {
305                         dev_err(sdev->dev, "%s: failed to clean up link DMA\n", __func__);
306                         return ret;
307                 }
308                 break;
309         default:
310                 break;
311         }
312
313         return 0;
314 }
315
316 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
317
318 static int hda_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
319 {
320         struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
321         int stream = substream->stream;
322
323         return hda_dai_hw_params(substream, &rtd->dpcm[stream].hw_params, dai);
324 }
325
326 static const struct snd_soc_dai_ops hda_dai_ops = {
327         .hw_params = hda_dai_hw_params,
328         .hw_free = hda_dai_hw_free,
329         .trigger = hda_dai_trigger,
330         .prepare = hda_dai_prepare,
331 };
332
333 #endif
334
335 static struct sof_ipc4_copier *widget_to_copier(struct snd_soc_dapm_widget *w)
336 {
337         struct snd_sof_widget *swidget = w->dobj.private;
338         struct snd_sof_dai *sdai = swidget->private;
339         struct sof_ipc4_copier *ipc4_copier = (struct sof_ipc4_copier *)sdai->private;
340
341         return ipc4_copier;
342 }
343
344 static int non_hda_dai_hw_params(struct snd_pcm_substream *substream,
345                                  struct snd_pcm_hw_params *params,
346                                  struct snd_soc_dai *cpu_dai)
347 {
348         struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
349         struct sof_ipc4_dma_config_tlv *dma_config_tlv;
350         const struct hda_dai_widget_dma_ops *ops;
351         struct sof_ipc4_dma_config *dma_config;
352         struct sof_ipc4_copier *ipc4_copier;
353         struct hdac_ext_stream *hext_stream;
354         struct hdac_stream *hstream;
355         struct snd_sof_dev *sdev;
356         int stream_id;
357         int ret;
358
359         ops = hda_dai_get_ops(substream, cpu_dai);
360         if (!ops) {
361                 dev_err(cpu_dai->dev, "DAI widget ops not set\n");
362                 return -EINVAL;
363         }
364
365         /* use HDaudio stream handling */
366         ret = hda_dai_hw_params(substream, params, cpu_dai);
367         if (ret < 0) {
368                 dev_err(cpu_dai->dev, "%s: hda_dai_hw_params failed: %d\n", __func__, ret);
369                 return ret;
370         }
371
372         sdev = widget_to_sdev(w);
373         if (sdev->dspless_mode_selected)
374                 goto skip_tlv;
375
376         /* get stream_id */
377         hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream);
378
379         if (!hext_stream) {
380                 dev_err(cpu_dai->dev, "%s: no hext_stream found\n", __func__);
381                 return -ENODEV;
382         }
383
384         hstream = &hext_stream->hstream;
385         stream_id = hstream->stream_tag;
386
387         if (!stream_id) {
388                 dev_err(cpu_dai->dev, "%s: no stream_id allocated\n", __func__);
389                 return -ENODEV;
390         }
391
392         /* configure TLV */
393         ipc4_copier = widget_to_copier(w);
394
395         dma_config_tlv = &ipc4_copier->dma_config_tlv;
396         dma_config_tlv->type = SOF_IPC4_GTW_DMA_CONFIG_ID;
397         /* dma_config_priv_size is zero */
398         dma_config_tlv->length = sizeof(dma_config_tlv->dma_config);
399
400         dma_config = &dma_config_tlv->dma_config;
401
402         dma_config->dma_method = SOF_IPC4_DMA_METHOD_HDA;
403         dma_config->pre_allocated_by_host = 1;
404         dma_config->dma_channel_id = stream_id - 1;
405         dma_config->stream_id = stream_id;
406         dma_config->dma_stream_channel_map.device_count = 0; /* mapping not used */
407         dma_config->dma_priv_config_size = 0;
408
409 skip_tlv:
410         return 0;
411 }
412
413 static int non_hda_dai_prepare(struct snd_pcm_substream *substream,
414                                struct snd_soc_dai *cpu_dai)
415 {
416         struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
417         int stream = substream->stream;
418
419         return non_hda_dai_hw_params(substream, &rtd->dpcm[stream].hw_params, cpu_dai);
420 }
421
422 static const struct snd_soc_dai_ops ssp_dai_ops = {
423         .hw_params = non_hda_dai_hw_params,
424         .hw_free = hda_dai_hw_free,
425         .trigger = hda_dai_trigger,
426         .prepare = non_hda_dai_prepare,
427 };
428
429 static const struct snd_soc_dai_ops dmic_dai_ops = {
430         .hw_params = non_hda_dai_hw_params,
431         .hw_free = hda_dai_hw_free,
432         .trigger = hda_dai_trigger,
433         .prepare = non_hda_dai_prepare,
434 };
435
436 int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream,
437                           struct snd_pcm_hw_params *params,
438                           struct snd_soc_dai *cpu_dai,
439                           int link_id)
440 {
441         struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
442         const struct hda_dai_widget_dma_ops *ops;
443         struct hdac_ext_stream *hext_stream;
444         struct snd_sof_dev *sdev;
445         int ret;
446
447         ret = non_hda_dai_hw_params(substream, params, cpu_dai);
448         if (ret < 0) {
449                 dev_err(cpu_dai->dev, "%s: non_hda_dai_hw_params failed %d\n", __func__, ret);
450                 return ret;
451         }
452
453         ops = hda_dai_get_ops(substream, cpu_dai);
454         sdev = widget_to_sdev(w);
455         hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream);
456
457         if (!hext_stream)
458                 return -ENODEV;
459
460         /* in the case of SoundWire we need to program the PCMSyCM registers */
461         ret = hdac_bus_eml_sdw_map_stream_ch(sof_to_bus(sdev), link_id, cpu_dai->id,
462                                              GENMASK(params_channels(params) - 1, 0),
463                                              hdac_stream(hext_stream)->stream_tag,
464                                              substream->stream);
465         if (ret < 0) {
466                 dev_err(cpu_dai->dev, "%s:  hdac_bus_eml_sdw_map_stream_ch failed %d\n",
467                         __func__, ret);
468                 return ret;
469         }
470
471         return 0;
472 }
473
474 int sdw_hda_dai_hw_free(struct snd_pcm_substream *substream,
475                         struct snd_soc_dai *cpu_dai,
476                         int link_id)
477 {
478         struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
479         struct snd_sof_dev *sdev;
480         int ret;
481
482         ret = hda_dai_hw_free(substream, cpu_dai);
483         if (ret < 0) {
484                 dev_err(cpu_dai->dev, "%s: non_hda_dai_hw_free failed %d\n", __func__, ret);
485                 return ret;
486         }
487
488         sdev = widget_to_sdev(w);
489
490         /* in the case of SoundWire we need to reset the PCMSyCM registers */
491         ret = hdac_bus_eml_sdw_map_stream_ch(sof_to_bus(sdev), link_id, cpu_dai->id,
492                                              0, 0, substream->stream);
493         if (ret < 0) {
494                 dev_err(cpu_dai->dev, "%s:  hdac_bus_eml_sdw_map_stream_ch failed %d\n",
495                         __func__, ret);
496                 return ret;
497         }
498
499         return 0;
500 }
501
502 int sdw_hda_dai_trigger(struct snd_pcm_substream *substream, int cmd,
503                         struct snd_soc_dai *cpu_dai)
504 {
505         return hda_dai_trigger(substream, cmd, cpu_dai);
506 }
507
508 static int hda_dai_suspend(struct hdac_bus *bus)
509 {
510         struct snd_soc_pcm_runtime *rtd;
511         struct hdac_ext_stream *hext_stream;
512         struct hdac_stream *s;
513         int ret;
514
515         /* set internal flag for BE */
516         list_for_each_entry(s, &bus->stream_list, list) {
517
518                 hext_stream = stream_to_hdac_ext_stream(s);
519
520                 /*
521                  * clear stream. This should already be taken care for running
522                  * streams when the SUSPEND trigger is called. But paused
523                  * streams do not get suspended, so this needs to be done
524                  * explicitly during suspend.
525                  */
526                 if (hext_stream->link_substream) {
527                         const struct hda_dai_widget_dma_ops *ops;
528                         struct snd_sof_widget *swidget;
529                         struct snd_soc_dapm_widget *w;
530                         struct snd_soc_dai *cpu_dai;
531                         struct snd_sof_dev *sdev;
532                         struct snd_sof_dai *sdai;
533
534                         rtd = snd_soc_substream_to_rtd(hext_stream->link_substream);
535                         cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
536                         w = snd_soc_dai_get_widget(cpu_dai, hdac_stream(hext_stream)->direction);
537                         swidget = w->dobj.private;
538                         sdev = widget_to_sdev(w);
539                         sdai = swidget->private;
540                         ops = sdai->platform_private;
541
542                         ret = hda_link_dma_cleanup(hext_stream->link_substream,
543                                                    hext_stream,
544                                                    cpu_dai);
545                         if (ret < 0)
546                                 return ret;
547
548                         /* for consistency with TRIGGER_SUSPEND  */
549                         if (ops->post_trigger) {
550                                 ret = ops->post_trigger(sdev, cpu_dai,
551                                                         hext_stream->link_substream,
552                                                         SNDRV_PCM_TRIGGER_SUSPEND);
553                                 if (ret < 0)
554                                         return ret;
555                         }
556                 }
557         }
558
559         return 0;
560 }
561
562 static void ssp_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops)
563 {
564         const struct sof_intel_dsp_desc *chip;
565         int i;
566
567         chip = get_chip_info(sdev->pdata);
568
569         if (chip->hw_ip_version >= SOF_INTEL_ACE_2_0) {
570                 for (i = 0; i < ops->num_drv; i++) {
571                         if (strstr(ops->drv[i].name, "SSP"))
572                                 ops->drv[i].ops = &ssp_dai_ops;
573                 }
574         }
575 }
576
577 static void dmic_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops)
578 {
579         const struct sof_intel_dsp_desc *chip;
580         int i;
581
582         chip = get_chip_info(sdev->pdata);
583
584         if (chip->hw_ip_version >= SOF_INTEL_ACE_2_0) {
585                 for (i = 0; i < ops->num_drv; i++) {
586                         if (strstr(ops->drv[i].name, "DMIC"))
587                                 ops->drv[i].ops = &dmic_dai_ops;
588                 }
589         }
590 }
591
592 #else
593
594 static inline void ssp_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) {}
595 static inline void dmic_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) {}
596
597 #endif /* CONFIG_SND_SOC_SOF_HDA_LINK */
598
599 void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops)
600 {
601         int i;
602
603         for (i = 0; i < ops->num_drv; i++) {
604 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
605                 if (strstr(ops->drv[i].name, "iDisp") ||
606                     strstr(ops->drv[i].name, "Analog") ||
607                     strstr(ops->drv[i].name, "Digital"))
608                         ops->drv[i].ops = &hda_dai_ops;
609 #endif
610         }
611
612         ssp_set_dai_drv_ops(sdev, ops);
613         dmic_set_dai_drv_ops(sdev, ops);
614
615         if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4 && !hda_use_tplg_nhlt) {
616                 struct sof_ipc4_fw_data *ipc4_data = sdev->private;
617
618                 ipc4_data->nhlt = intel_nhlt_init(sdev->dev);
619         }
620 }
621
622 void hda_ops_free(struct snd_sof_dev *sdev)
623 {
624         if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
625                 struct sof_ipc4_fw_data *ipc4_data = sdev->private;
626
627                 if (!hda_use_tplg_nhlt)
628                         intel_nhlt_free(ipc4_data->nhlt);
629
630                 kfree(sdev->private);
631                 sdev->private = NULL;
632         }
633 }
634 EXPORT_SYMBOL_NS(hda_ops_free, SND_SOC_SOF_INTEL_HDA_COMMON);
635
636 /*
637  * common dai driver for skl+ platforms.
638  * some products who use this DAI array only physically have a subset of
639  * the DAIs, but no harm is done here by adding the whole set.
640  */
641 struct snd_soc_dai_driver skl_dai[] = {
642 {
643         .name = "SSP0 Pin",
644         .playback = {
645                 .channels_min = 1,
646                 .channels_max = 8,
647         },
648         .capture = {
649                 .channels_min = 1,
650                 .channels_max = 8,
651         },
652 },
653 {
654         .name = "SSP1 Pin",
655         .playback = {
656                 .channels_min = 1,
657                 .channels_max = 8,
658         },
659         .capture = {
660                 .channels_min = 1,
661                 .channels_max = 8,
662         },
663 },
664 {
665         .name = "SSP2 Pin",
666         .playback = {
667                 .channels_min = 1,
668                 .channels_max = 8,
669         },
670         .capture = {
671                 .channels_min = 1,
672                 .channels_max = 8,
673         },
674 },
675 {
676         .name = "SSP3 Pin",
677         .playback = {
678                 .channels_min = 1,
679                 .channels_max = 8,
680         },
681         .capture = {
682                 .channels_min = 1,
683                 .channels_max = 8,
684         },
685 },
686 {
687         .name = "SSP4 Pin",
688         .playback = {
689                 .channels_min = 1,
690                 .channels_max = 8,
691         },
692         .capture = {
693                 .channels_min = 1,
694                 .channels_max = 8,
695         },
696 },
697 {
698         .name = "SSP5 Pin",
699         .playback = {
700                 .channels_min = 1,
701                 .channels_max = 8,
702         },
703         .capture = {
704                 .channels_min = 1,
705                 .channels_max = 8,
706         },
707 },
708 {
709         .name = "DMIC01 Pin",
710         .capture = {
711                 .channels_min = 1,
712                 .channels_max = 4,
713         },
714 },
715 {
716         .name = "DMIC16k Pin",
717         .capture = {
718                 .channels_min = 1,
719                 .channels_max = 4,
720         },
721 },
722 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
723 {
724         .name = "iDisp1 Pin",
725         .playback = {
726                 .channels_min = 1,
727                 .channels_max = 8,
728         },
729 },
730 {
731         .name = "iDisp2 Pin",
732         .playback = {
733                 .channels_min = 1,
734                 .channels_max = 8,
735         },
736 },
737 {
738         .name = "iDisp3 Pin",
739         .playback = {
740                 .channels_min = 1,
741                 .channels_max = 8,
742         },
743 },
744 {
745         .name = "iDisp4 Pin",
746         .playback = {
747                 .channels_min = 1,
748                 .channels_max = 8,
749         },
750 },
751 {
752         .name = "Analog CPU DAI",
753         .playback = {
754                 .channels_min = 1,
755                 .channels_max = 16,
756         },
757         .capture = {
758                 .channels_min = 1,
759                 .channels_max = 16,
760         },
761 },
762 {
763         .name = "Digital CPU DAI",
764         .playback = {
765                 .channels_min = 1,
766                 .channels_max = 16,
767         },
768         .capture = {
769                 .channels_min = 1,
770                 .channels_max = 16,
771         },
772 },
773 {
774         .name = "Alt Analog CPU DAI",
775         .playback = {
776                 .channels_min = 1,
777                 .channels_max = 16,
778         },
779         .capture = {
780                 .channels_min = 1,
781                 .channels_max = 16,
782         },
783 },
784 #endif
785 };
786
787 int hda_dsp_dais_suspend(struct snd_sof_dev *sdev)
788 {
789         /*
790          * In the corner case where a SUSPEND happens during a PAUSE, the ALSA core
791          * does not throw the TRIGGER_SUSPEND. This leaves the DAIs in an unbalanced state.
792          * Since the component suspend is called last, we can trap this corner case
793          * and force the DAIs to release their resources.
794          */
795 #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK)
796         int ret;
797
798         ret = hda_dai_suspend(sof_to_bus(sdev));
799         if (ret < 0)
800                 return ret;
801 #endif
802
803         return 0;
804 }