~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/soc/sof/intel/hda-dai.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php