File Index
Files and directories
Sign in
FILE

hda-dai.c

File preview with syntax highlighting.

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