xref: /openbmc/linux/sound/soc/sof/intel/hda-dai.c (revision 1f7b5d52)
1e149ca29SPierre-Louis Bossart // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
2fdd961e3SKeyon Jie //
3fdd961e3SKeyon Jie // This file is provided under a dual BSD/GPLv2 license.  When using or
4fdd961e3SKeyon Jie // redistributing this file, you may do so under either license.
5fdd961e3SKeyon Jie //
6fdd961e3SKeyon Jie // Copyright(c) 2018 Intel Corporation. All rights reserved.
7fdd961e3SKeyon Jie //
8fdd961e3SKeyon Jie // Authors: Keyon Jie <yang.jie@linux.intel.com>
9fdd961e3SKeyon Jie //
10fdd961e3SKeyon Jie 
11fdd961e3SKeyon Jie #include <sound/pcm_params.h>
12fdd961e3SKeyon Jie #include <sound/hdaudio_ext.h>
131da51943SRanjani Sridharan #include <sound/intel-nhlt.h>
144c30004aSRanjani Sridharan #include <sound/sof/ipc4/header.h>
154c30004aSRanjani Sridharan #include <uapi/sound/sof/header.h>
164c30004aSRanjani Sridharan #include "../ipc4-priv.h"
174c30004aSRanjani Sridharan #include "../ipc4-topology.h"
18fdd961e3SKeyon Jie #include "../sof-priv.h"
19ee1e79b7SRanjani Sridharan #include "../sof-audio.h"
20fdd961e3SKeyon Jie #include "hda.h"
21fdd961e3SKeyon Jie 
221da51943SRanjani Sridharan /*
231da51943SRanjani Sridharan  * The default method is to fetch NHLT from BIOS. With this parameter set
241da51943SRanjani Sridharan  * it is possible to override that with NHLT in the SOF topology manifest.
251da51943SRanjani Sridharan  */
261da51943SRanjani Sridharan static bool hda_use_tplg_nhlt;
271da51943SRanjani Sridharan module_param_named(sof_use_tplg_nhlt, hda_use_tplg_nhlt, bool, 0444);
281da51943SRanjani Sridharan MODULE_PARM_DESC(sof_use_tplg_nhlt, "SOF topology nhlt override");
291da51943SRanjani Sridharan 
302b009fa0SRanjani Sridharan int hda_dai_config(struct snd_soc_dapm_widget *w, unsigned int flags,
31e6ffb0d5SRanjani Sridharan 		   struct snd_sof_dai_config_data *data)
32e6ffb0d5SRanjani Sridharan {
33e6ffb0d5SRanjani Sridharan 	struct snd_sof_widget *swidget = w->dobj.private;
34*1f7b5d52SPeter Ujfalusi 	const struct sof_ipc_tplg_ops *tplg_ops;
35*1f7b5d52SPeter Ujfalusi 	struct snd_soc_component *component;
36*1f7b5d52SPeter Ujfalusi 	struct snd_sof_dev *sdev;
37e6ffb0d5SRanjani Sridharan 	int ret;
38e6ffb0d5SRanjani Sridharan 
39*1f7b5d52SPeter Ujfalusi 	if (!swidget)
40*1f7b5d52SPeter Ujfalusi 		return 0;
41*1f7b5d52SPeter Ujfalusi 
42*1f7b5d52SPeter Ujfalusi 	component = swidget->scomp;
43*1f7b5d52SPeter Ujfalusi 	sdev = snd_soc_component_get_drvdata(component);
44*1f7b5d52SPeter Ujfalusi 	tplg_ops = sof_ipc_get_ops(sdev, tplg);
45*1f7b5d52SPeter Ujfalusi 
46e6ffb0d5SRanjani Sridharan 	if (tplg_ops && tplg_ops->dai_config) {
47e6ffb0d5SRanjani Sridharan 		ret = tplg_ops->dai_config(sdev, swidget, flags, data);
48e6ffb0d5SRanjani Sridharan 		if (ret < 0) {
49e6ffb0d5SRanjani Sridharan 			dev_err(sdev->dev, "DAI config with flags %x failed for widget %s\n",
50e6ffb0d5SRanjani Sridharan 				flags, w->name);
51e6ffb0d5SRanjani Sridharan 			return ret;
52e6ffb0d5SRanjani Sridharan 		}
53e6ffb0d5SRanjani Sridharan 	}
54e6ffb0d5SRanjani Sridharan 
55e6ffb0d5SRanjani Sridharan 	return 0;
56e6ffb0d5SRanjani Sridharan }
57e6ffb0d5SRanjani Sridharan 
582b009fa0SRanjani Sridharan #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
592b009fa0SRanjani Sridharan 
6080afde34SRanjani Sridharan static const struct hda_dai_widget_dma_ops *
6180afde34SRanjani Sridharan hda_dai_get_ops(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai)
62fdd961e3SKeyon Jie {
6380afde34SRanjani Sridharan 	struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
6480afde34SRanjani Sridharan 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(cpu_dai->component);
6580afde34SRanjani Sridharan 	struct snd_sof_widget *swidget = w->dobj.private;
66*1f7b5d52SPeter Ujfalusi 	struct snd_sof_dai *sdai;
67*1f7b5d52SPeter Ujfalusi 
68*1f7b5d52SPeter Ujfalusi 	/*
69*1f7b5d52SPeter Ujfalusi 	 * The swidget parameter of hda_select_dai_widget_ops() is ignored in
70*1f7b5d52SPeter Ujfalusi 	 * case of DSPless mode
71*1f7b5d52SPeter Ujfalusi 	 */
72*1f7b5d52SPeter Ujfalusi 	if (sdev->dspless_mode_selected)
73*1f7b5d52SPeter Ujfalusi 		return hda_select_dai_widget_ops(sdev, NULL);
74*1f7b5d52SPeter Ujfalusi 
75*1f7b5d52SPeter Ujfalusi 	sdai = swidget->private;
76fdd961e3SKeyon Jie 
7780afde34SRanjani Sridharan 	/* select and set the DAI widget ops if not set already */
7880afde34SRanjani Sridharan 	if (!sdai->platform_private) {
7980afde34SRanjani Sridharan 		const struct hda_dai_widget_dma_ops *ops =
8080afde34SRanjani Sridharan 			hda_select_dai_widget_ops(sdev, swidget);
8180afde34SRanjani Sridharan 		if (!ops)
82bdf4ad3fSRanjani Sridharan 			return NULL;
8380afde34SRanjani Sridharan 
8480afde34SRanjani Sridharan 		/* check if mandatory ops are set */
8580afde34SRanjani Sridharan 		if (!ops || !ops->get_hext_stream)
8680afde34SRanjani Sridharan 			return NULL;
8780afde34SRanjani Sridharan 
8880afde34SRanjani Sridharan 		sdai->platform_private = ops;
89fdd961e3SKeyon Jie 	}
90fdd961e3SKeyon Jie 
9180afde34SRanjani Sridharan 	return sdai->platform_private;
92fdd961e3SKeyon Jie }
93fdd961e3SKeyon Jie 
94880924caSPierre-Louis Bossart static int hda_link_dma_cleanup(struct snd_pcm_substream *substream,
950351a9b8SPierre-Louis Bossart 				struct hdac_ext_stream *hext_stream,
96880924caSPierre-Louis Bossart 				struct snd_soc_dai *cpu_dai,
972be2caf4SRanjani Sridharan 				struct snd_soc_dai *codec_dai)
98880924caSPierre-Louis Bossart {
9980afde34SRanjani Sridharan 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(cpu_dai->component);
10080afde34SRanjani Sridharan 	const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai);
1010351a9b8SPierre-Louis Bossart 	struct hdac_stream *hstream = &hext_stream->hstream;
102880924caSPierre-Louis Bossart 	struct hdac_bus *bus = hstream->bus;
103880924caSPierre-Louis Bossart 	struct sof_intel_hda_stream *hda_stream;
1047f1e16aeSPierre-Louis Bossart 	struct hdac_ext_link *hlink;
105880924caSPierre-Louis Bossart 	int stream_tag;
106880924caSPierre-Louis Bossart 
107b0cd60f3SPierre-Louis Bossart 	hlink = snd_hdac_ext_bus_get_hlink_by_name(bus, codec_dai->component->name);
1087f1e16aeSPierre-Louis Bossart 	if (!hlink)
109880924caSPierre-Louis Bossart 		return -EINVAL;
110880924caSPierre-Louis Bossart 
111880924caSPierre-Louis Bossart 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
112880924caSPierre-Louis Bossart 		stream_tag = hdac_stream(hext_stream)->stream_tag;
1137fa403f2SPierre-Louis Bossart 		snd_hdac_ext_bus_link_clear_stream_id(hlink, stream_tag);
114880924caSPierre-Louis Bossart 	}
11580afde34SRanjani Sridharan 
11680afde34SRanjani Sridharan 	if (ops->release_hext_stream)
11780afde34SRanjani Sridharan 		ops->release_hext_stream(sdev, cpu_dai, substream);
11880afde34SRanjani Sridharan 
119880924caSPierre-Louis Bossart 	hext_stream->link_prepared = 0;
120880924caSPierre-Louis Bossart 
121880924caSPierre-Louis Bossart 	/* free the host DMA channel reserved by hostless streams */
122880924caSPierre-Louis Bossart 	hda_stream = hstream_to_sof_hda_stream(hext_stream);
123880924caSPierre-Louis Bossart 	hda_stream->host_reserved = 0;
124880924caSPierre-Louis Bossart 
125880924caSPierre-Louis Bossart 	return 0;
126880924caSPierre-Louis Bossart }
127880924caSPierre-Louis Bossart 
128f321ffc8SPierre-Louis Bossart static int hda_link_dma_hw_params(struct snd_pcm_substream *substream,
129b436ed8dSRanjani Sridharan 				  struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai)
130fdd961e3SKeyon Jie {
13180afde34SRanjani Sridharan 	const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai);
1321205300aSKuninori Morimoto 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
133be3e8de7SKuninori Morimoto 	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
1344842f79fSPierre-Louis Bossart 	struct hdac_ext_stream *hext_stream;
135a8310c00SRanjani Sridharan 	struct hdac_stream *hstream;
1367f1e16aeSPierre-Louis Bossart 	struct hdac_ext_link *hlink;
1374842f79fSPierre-Louis Bossart 	struct snd_sof_dev *sdev;
1384842f79fSPierre-Louis Bossart 	struct hdac_bus *bus;
139a8310c00SRanjani Sridharan 	unsigned int format_val;
1402a6afac2SRanjani Sridharan 	unsigned int link_bps;
14180afde34SRanjani Sridharan 	int stream_tag;
1424842f79fSPierre-Louis Bossart 
1434842f79fSPierre-Louis Bossart 	sdev = snd_soc_component_get_drvdata(cpu_dai->component);
1444842f79fSPierre-Louis Bossart 	bus = sof_to_bus(sdev);
145fdd961e3SKeyon Jie 
1461f810d2bSPierre-Louis Bossart 	hlink = snd_hdac_ext_bus_get_hlink_by_name(bus, codec_dai->component->name);
1471f810d2bSPierre-Louis Bossart 	if (!hlink)
1481f810d2bSPierre-Louis Bossart 		return -EINVAL;
1491f810d2bSPierre-Louis Bossart 
15080afde34SRanjani Sridharan 	hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream);
15180afde34SRanjani Sridharan 
1527d88b960SPierre-Louis Bossart 	if (!hext_stream) {
15380afde34SRanjani Sridharan 		if (ops->assign_hext_stream)
15480afde34SRanjani Sridharan 			hext_stream = ops->assign_hext_stream(sdev, cpu_dai, substream);
15580afde34SRanjani Sridharan 	}
15680afde34SRanjani Sridharan 
1577d88b960SPierre-Louis Bossart 	if (!hext_stream)
158bdf4ad3fSRanjani Sridharan 		return -EBUSY;
159921162c8SPierre-Louis Bossart 
160a8310c00SRanjani Sridharan 	hstream = &hext_stream->hstream;
161a8310c00SRanjani Sridharan 	stream_tag = hstream->stream_tag;
162a8310c00SRanjani Sridharan 
1632a6afac2SRanjani Sridharan 	if (hext_stream->hstream.direction == SNDRV_PCM_STREAM_PLAYBACK)
1642a6afac2SRanjani Sridharan 		snd_hdac_ext_bus_link_set_stream_id(hlink, stream_tag);
1652a6afac2SRanjani Sridharan 
16663611041SPierre-Louis Bossart 	/* set the hdac_stream in the codec dai */
1677d88b960SPierre-Louis Bossart 	snd_soc_dai_set_stream(codec_dai, hdac_stream(hext_stream), substream->stream);
168fdd961e3SKeyon Jie 
169fdd961e3SKeyon Jie 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
1702a6afac2SRanjani Sridharan 		link_bps = codec_dai->driver->playback.sig_bits;
171fdd961e3SKeyon Jie 	else
1722a6afac2SRanjani Sridharan 		link_bps = codec_dai->driver->capture.sig_bits;
173fdd961e3SKeyon Jie 
174e2d6569aSRanjani Sridharan 	if (ops->reset_hext_stream)
175e2d6569aSRanjani Sridharan 		ops->reset_hext_stream(sdev, hext_stream);
176a8310c00SRanjani Sridharan 
1772a6afac2SRanjani Sridharan 	format_val = snd_hdac_calc_stream_format(params_rate(params), params_channels(params),
1782a6afac2SRanjani Sridharan 						 params_format(params), link_bps, 0);
179a8310c00SRanjani Sridharan 
180a8310c00SRanjani Sridharan 	dev_dbg(bus->dev, "format_val=%d, rate=%d, ch=%d, format=%d\n",
1812a6afac2SRanjani Sridharan 		format_val, params_rate(params), params_channels(params), params_format(params));
182a8310c00SRanjani Sridharan 
183e2d6569aSRanjani Sridharan 	if (ops->setup_hext_stream)
184e2d6569aSRanjani Sridharan 		ops->setup_hext_stream(sdev, hext_stream, format_val);
185a8310c00SRanjani Sridharan 
186a8310c00SRanjani Sridharan 	hext_stream->link_prepared = 1;
187a8310c00SRanjani Sridharan 
188a8310c00SRanjani Sridharan 	return 0;
189fdd961e3SKeyon Jie }
190fdd961e3SKeyon Jie 
191b436ed8dSRanjani Sridharan static int hda_link_dma_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai)
192f321ffc8SPierre-Louis Bossart {
1931205300aSKuninori Morimoto 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
194fdd961e3SKeyon Jie 	int stream = substream->stream;
195fdd961e3SKeyon Jie 
196b436ed8dSRanjani Sridharan 	return hda_link_dma_hw_params(substream, &rtd->dpcm[stream].hw_params, cpu_dai);
197fdd961e3SKeyon Jie }
198fdd961e3SKeyon Jie 
1998c29e78bSRanjani Sridharan static int hda_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai)
2009272d6c2SPierre-Louis Bossart {
20180afde34SRanjani Sridharan 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(cpu_dai->component);
20280afde34SRanjani Sridharan 	const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai);
2039272d6c2SPierre-Louis Bossart 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
2049272d6c2SPierre-Louis Bossart 	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
2059272d6c2SPierre-Louis Bossart 	struct hdac_ext_stream *hext_stream;
2069272d6c2SPierre-Louis Bossart 
20780afde34SRanjani Sridharan 	if (!ops) {
20880afde34SRanjani Sridharan 		dev_err(sdev->dev, "DAI widget ops not set\n");
20980afde34SRanjani Sridharan 		return -EINVAL;
21080afde34SRanjani Sridharan 	}
21180afde34SRanjani Sridharan 
21280afde34SRanjani Sridharan 	hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream);
213880924caSPierre-Louis Bossart 	if (!hext_stream)
2149272d6c2SPierre-Louis Bossart 		return 0;
215880924caSPierre-Louis Bossart 
2162be2caf4SRanjani Sridharan 	return hda_link_dma_cleanup(substream, hext_stream, cpu_dai, codec_dai);
2179272d6c2SPierre-Louis Bossart }
2189272d6c2SPierre-Louis Bossart 
2199272d6c2SPierre-Louis Bossart static int hda_dai_hw_params(struct snd_pcm_substream *substream,
2209272d6c2SPierre-Louis Bossart 			     struct snd_pcm_hw_params *params,
2219272d6c2SPierre-Louis Bossart 			     struct snd_soc_dai *dai)
2229272d6c2SPierre-Louis Bossart {
223e6ffb0d5SRanjani Sridharan 	struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, substream->stream);
22480afde34SRanjani Sridharan 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
22580afde34SRanjani Sridharan 	const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai);
22680afde34SRanjani Sridharan 	struct hdac_ext_stream *hext_stream;
227e6ffb0d5SRanjani Sridharan 	struct snd_sof_dai_config_data data = { 0 };
228e6ffb0d5SRanjani Sridharan 	unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS;
2299272d6c2SPierre-Louis Bossart 	int ret;
2309272d6c2SPierre-Louis Bossart 
23180afde34SRanjani Sridharan 	if (!ops) {
23280afde34SRanjani Sridharan 		dev_err(sdev->dev, "DAI widget ops not set\n");
23380afde34SRanjani Sridharan 		return -EINVAL;
23480afde34SRanjani Sridharan 	}
23580afde34SRanjani Sridharan 
23680afde34SRanjani Sridharan 	hext_stream = ops->get_hext_stream(sdev, dai, substream);
237c4eb48f7SPierre-Louis Bossart 	if (hext_stream && hext_stream->link_prepared)
238c4eb48f7SPierre-Louis Bossart 		return 0;
239c4eb48f7SPierre-Louis Bossart 
240b436ed8dSRanjani Sridharan 	ret = hda_link_dma_hw_params(substream, params, dai);
2419272d6c2SPierre-Louis Bossart 	if (ret < 0)
2429272d6c2SPierre-Louis Bossart 		return ret;
2439272d6c2SPierre-Louis Bossart 
244e6ffb0d5SRanjani Sridharan 	hext_stream = ops->get_hext_stream(sdev, dai, substream);
2459272d6c2SPierre-Louis Bossart 
246e6ffb0d5SRanjani Sridharan 	flags |= SOF_DAI_CONFIG_FLAGS_2_STEP_STOP << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT;
247e6ffb0d5SRanjani Sridharan 	data.dai_data = hdac_stream(hext_stream)->stream_tag - 1;
2489272d6c2SPierre-Louis Bossart 
249e6ffb0d5SRanjani Sridharan 	return hda_dai_config(w, flags, &data);
2509272d6c2SPierre-Louis Bossart }
2519272d6c2SPierre-Louis Bossart 
2524c30004aSRanjani Sridharan static int hda_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
2539272d6c2SPierre-Louis Bossart {
254e6ffb0d5SRanjani Sridharan 	struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, substream->stream);
2559272d6c2SPierre-Louis Bossart 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
25680afde34SRanjani Sridharan 	const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai);
25780afde34SRanjani Sridharan 	struct hdac_ext_stream *hext_stream;
258e6ffb0d5SRanjani Sridharan 	struct snd_sof_dai_config_data data = { 0 };
259e6ffb0d5SRanjani Sridharan 	unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS;
2609272d6c2SPierre-Louis Bossart 	int ret;
2619272d6c2SPierre-Louis Bossart 
26280afde34SRanjani Sridharan 	hext_stream = ops->get_hext_stream(sdev, dai, substream);
263722cbbfaSPierre-Louis Bossart 	if (hext_stream && hext_stream->link_prepared)
2649272d6c2SPierre-Louis Bossart 		return 0;
2659272d6c2SPierre-Louis Bossart 
26618701bb1SPierre-Louis Bossart 	dev_dbg(sdev->dev, "prepare stream dir %d\n", substream->stream);
2679272d6c2SPierre-Louis Bossart 
268b436ed8dSRanjani Sridharan 	ret = hda_link_dma_prepare(substream, dai);
2699272d6c2SPierre-Louis Bossart 	if (ret < 0)
2709272d6c2SPierre-Louis Bossart 		return ret;
2719272d6c2SPierre-Louis Bossart 
272e6ffb0d5SRanjani Sridharan 	hext_stream = ops->get_hext_stream(sdev, dai, substream);
273e6ffb0d5SRanjani Sridharan 
274e6ffb0d5SRanjani Sridharan 	flags |= SOF_DAI_CONFIG_FLAGS_2_STEP_STOP << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT;
275e6ffb0d5SRanjani Sridharan 	data.dai_data = hdac_stream(hext_stream)->stream_tag - 1;
276e6ffb0d5SRanjani Sridharan 
277e6ffb0d5SRanjani Sridharan 	return hda_dai_config(w, flags, &data);
2789272d6c2SPierre-Louis Bossart }
2799272d6c2SPierre-Louis Bossart 
2804c30004aSRanjani Sridharan /*
2814c30004aSRanjani Sridharan  * In contrast to IPC3, the dai trigger in IPC4 mixes pipeline state changes
2824c30004aSRanjani Sridharan  * (over IPC channel) and DMA state change (direct host register changes).
2834c30004aSRanjani Sridharan  */
2842b009fa0SRanjani Sridharan static int hda_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
2854c30004aSRanjani Sridharan {
2864c30004aSRanjani Sridharan 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
28780afde34SRanjani Sridharan 	const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai);
28880afde34SRanjani Sridharan 	struct hdac_ext_stream *hext_stream;
2894c30004aSRanjani Sridharan 	struct snd_soc_pcm_runtime *rtd;
2904c30004aSRanjani Sridharan 	struct snd_soc_dai *codec_dai;
2914c30004aSRanjani Sridharan 	int ret;
2924c30004aSRanjani Sridharan 
29318701bb1SPierre-Louis Bossart 	dev_dbg(dai->dev, "cmd=%d dai %s direction %d\n", cmd,
2944c30004aSRanjani Sridharan 		dai->name, substream->stream);
2954c30004aSRanjani Sridharan 
29680afde34SRanjani Sridharan 	hext_stream = ops->get_hext_stream(sdev, dai, substream);
29780afde34SRanjani Sridharan 	if (!hext_stream)
29880afde34SRanjani Sridharan 		return -EINVAL;
29980afde34SRanjani Sridharan 
3004c30004aSRanjani Sridharan 	rtd = asoc_substream_to_rtd(substream);
3014c30004aSRanjani Sridharan 	codec_dai = asoc_rtd_to_codec(rtd, 0);
3024c30004aSRanjani Sridharan 
3034b2ee4cdSRanjani Sridharan 	if (ops->pre_trigger) {
3044b2ee4cdSRanjani Sridharan 		ret = ops->pre_trigger(sdev, dai, substream, cmd);
30537a26eecSRanjani Sridharan 		if (ret < 0)
30637a26eecSRanjani Sridharan 			return ret;
30737a26eecSRanjani Sridharan 	}
30837a26eecSRanjani Sridharan 
3094b2ee4cdSRanjani Sridharan 	if (ops->trigger) {
3104b2ee4cdSRanjani Sridharan 		ret = ops->trigger(sdev, dai, substream, cmd);
31137a26eecSRanjani Sridharan 		if (ret < 0)
31237a26eecSRanjani Sridharan 			return ret;
3134b2ee4cdSRanjani Sridharan 	}
3144b2ee4cdSRanjani Sridharan 
3154b2ee4cdSRanjani Sridharan 	if (ops->post_trigger) {
3164b2ee4cdSRanjani Sridharan 		ret = ops->post_trigger(sdev, dai, substream, cmd);
3174b2ee4cdSRanjani Sridharan 		if (ret < 0)
3184b2ee4cdSRanjani Sridharan 			return ret;
3194b2ee4cdSRanjani Sridharan 	}
3204b2ee4cdSRanjani Sridharan 
3214b2ee4cdSRanjani Sridharan 	switch (cmd) {
3224c30004aSRanjani Sridharan 	case SNDRV_PCM_TRIGGER_SUSPEND:
323be7f4f8dSRanjani Sridharan 		ret = hda_link_dma_cleanup(substream, hext_stream, dai, codec_dai);
3244c30004aSRanjani Sridharan 		if (ret < 0) {
3254c30004aSRanjani Sridharan 			dev_err(sdev->dev, "%s: failed to clean up link DMA\n", __func__);
3264c30004aSRanjani Sridharan 			return ret;
3274c30004aSRanjani Sridharan 		}
3284c30004aSRanjani Sridharan 		break;
3294c30004aSRanjani Sridharan 	default:
3304b2ee4cdSRanjani Sridharan 		break;
3314c30004aSRanjani Sridharan 	}
3324c30004aSRanjani Sridharan 
3334c30004aSRanjani Sridharan 	return 0;
3344c30004aSRanjani Sridharan }
3354c30004aSRanjani Sridharan 
3362b009fa0SRanjani Sridharan static const struct snd_soc_dai_ops hda_dai_ops = {
337309e6e55SPierre-Louis Bossart 	.hw_params = hda_dai_hw_params,
338309e6e55SPierre-Louis Bossart 	.hw_free = hda_dai_hw_free,
3392b009fa0SRanjani Sridharan 	.trigger = hda_dai_trigger,
3404c30004aSRanjani Sridharan 	.prepare = hda_dai_prepare,
341fdd961e3SKeyon Jie };
34270368106SCezary Rojewski 
343f09e9284SPierre-Louis Bossart static int hda_dai_suspend(struct hdac_bus *bus)
344f09e9284SPierre-Louis Bossart {
345f09e9284SPierre-Louis Bossart 	struct snd_soc_pcm_runtime *rtd;
346f09e9284SPierre-Louis Bossart 	struct hdac_ext_stream *hext_stream;
347f09e9284SPierre-Louis Bossart 	struct hdac_stream *s;
34823b1944eSPierre-Louis Bossart 	int ret;
349f09e9284SPierre-Louis Bossart 
350f09e9284SPierre-Louis Bossart 	/* set internal flag for BE */
351f09e9284SPierre-Louis Bossart 	list_for_each_entry(s, &bus->stream_list, list) {
352722cbbfaSPierre-Louis Bossart 
353f09e9284SPierre-Louis Bossart 		hext_stream = stream_to_hdac_ext_stream(s);
354f09e9284SPierre-Louis Bossart 
355f09e9284SPierre-Louis Bossart 		/*
356f09e9284SPierre-Louis Bossart 		 * clear stream. This should already be taken care for running
357f09e9284SPierre-Louis Bossart 		 * streams when the SUSPEND trigger is called. But paused
358f09e9284SPierre-Louis Bossart 		 * streams do not get suspended, so this needs to be done
359f09e9284SPierre-Louis Bossart 		 * explicitly during suspend.
360f09e9284SPierre-Louis Bossart 		 */
361f09e9284SPierre-Louis Bossart 		if (hext_stream->link_substream) {
3622b009fa0SRanjani Sridharan 			const struct hda_dai_widget_dma_ops *ops;
3632b009fa0SRanjani Sridharan 			struct snd_sof_widget *swidget;
3642b009fa0SRanjani Sridharan 			struct snd_soc_dapm_widget *w;
36523b1944eSPierre-Louis Bossart 			struct snd_soc_dai *codec_dai;
3662b009fa0SRanjani Sridharan 			struct snd_soc_dai *cpu_dai;
3672b009fa0SRanjani Sridharan 			struct snd_sof_dev *sdev;
3682b009fa0SRanjani Sridharan 			struct snd_sof_dai *sdai;
36923b1944eSPierre-Louis Bossart 
370f09e9284SPierre-Louis Bossart 			rtd = asoc_substream_to_rtd(hext_stream->link_substream);
37123b1944eSPierre-Louis Bossart 			cpu_dai = asoc_rtd_to_cpu(rtd, 0);
37223b1944eSPierre-Louis Bossart 			codec_dai = asoc_rtd_to_codec(rtd, 0);
3732b009fa0SRanjani Sridharan 			w = snd_soc_dai_get_widget(cpu_dai, hdac_stream(hext_stream)->direction);
3742b009fa0SRanjani Sridharan 			swidget = w->dobj.private;
3752b009fa0SRanjani Sridharan 			sdev = snd_soc_component_get_drvdata(swidget->scomp);
3762b009fa0SRanjani Sridharan 			sdai = swidget->private;
3772b009fa0SRanjani Sridharan 			ops = sdai->platform_private;
378f09e9284SPierre-Louis Bossart 
3790351a9b8SPierre-Louis Bossart 			ret = hda_link_dma_cleanup(hext_stream->link_substream,
3800351a9b8SPierre-Louis Bossart 						   hext_stream,
3812be2caf4SRanjani Sridharan 						   cpu_dai, codec_dai);
382880924caSPierre-Louis Bossart 			if (ret < 0)
383880924caSPierre-Louis Bossart 				return ret;
384722cbbfaSPierre-Louis Bossart 
3852b009fa0SRanjani Sridharan 			/* for consistency with TRIGGER_SUSPEND  */
3862b009fa0SRanjani Sridharan 			if (ops->post_trigger) {
3872b009fa0SRanjani Sridharan 				ret = ops->post_trigger(sdev, cpu_dai,
3882b009fa0SRanjani Sridharan 							hext_stream->link_substream,
3892b009fa0SRanjani Sridharan 							SNDRV_PCM_TRIGGER_SUSPEND);
39023b1944eSPierre-Louis Bossart 				if (ret < 0)
39123b1944eSPierre-Louis Bossart 					return ret;
39223b1944eSPierre-Louis Bossart 			}
393f09e9284SPierre-Louis Bossart 		}
3942b009fa0SRanjani Sridharan 	}
395f09e9284SPierre-Louis Bossart 
396f09e9284SPierre-Louis Bossart 	return 0;
397f09e9284SPierre-Louis Bossart }
3984c30004aSRanjani Sridharan 
399fdd961e3SKeyon Jie #endif
400fdd961e3SKeyon Jie 
40151ec71dcSRanjani Sridharan void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops)
40251ec71dcSRanjani Sridharan {
40351ec71dcSRanjani Sridharan 	int i;
40451ec71dcSRanjani Sridharan 
40551ec71dcSRanjani Sridharan 	for (i = 0; i < ops->num_drv; i++) {
406a4203256SPierre-Louis Bossart #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
40751ec71dcSRanjani Sridharan 		if (strstr(ops->drv[i].name, "iDisp") ||
40851ec71dcSRanjani Sridharan 		    strstr(ops->drv[i].name, "Analog") ||
40951ec71dcSRanjani Sridharan 		    strstr(ops->drv[i].name, "Digital"))
4102b009fa0SRanjani Sridharan 			ops->drv[i].ops = &hda_dai_ops;
41151ec71dcSRanjani Sridharan #endif
41251ec71dcSRanjani Sridharan 	}
4132b009fa0SRanjani Sridharan 
4142b009fa0SRanjani Sridharan 	if (sdev->pdata->ipc_type == SOF_INTEL_IPC4 && !hda_use_tplg_nhlt) {
4151da51943SRanjani Sridharan 		struct sof_ipc4_fw_data *ipc4_data = sdev->private;
4161da51943SRanjani Sridharan 
4171da51943SRanjani Sridharan 		ipc4_data->nhlt = intel_nhlt_init(sdev->dev);
41851ec71dcSRanjani Sridharan 	}
41951ec71dcSRanjani Sridharan }
42051ec71dcSRanjani Sridharan 
4211da51943SRanjani Sridharan void hda_ops_free(struct snd_sof_dev *sdev)
4221da51943SRanjani Sridharan {
4231da51943SRanjani Sridharan 	if (sdev->pdata->ipc_type == SOF_INTEL_IPC4) {
4241da51943SRanjani Sridharan 		struct sof_ipc4_fw_data *ipc4_data = sdev->private;
4251da51943SRanjani Sridharan 
4261da51943SRanjani Sridharan 		if (!hda_use_tplg_nhlt)
4271da51943SRanjani Sridharan 			intel_nhlt_free(ipc4_data->nhlt);
4281da51943SRanjani Sridharan 	}
4291da51943SRanjani Sridharan }
4301da51943SRanjani Sridharan EXPORT_SYMBOL_NS(hda_ops_free, SND_SOC_SOF_INTEL_HDA_COMMON);
4311da51943SRanjani Sridharan 
432fdd961e3SKeyon Jie /*
433fdd961e3SKeyon Jie  * common dai driver for skl+ platforms.
434fdd961e3SKeyon Jie  * some products who use this DAI array only physically have a subset of
435fdd961e3SKeyon Jie  * the DAIs, but no harm is done here by adding the whole set.
436fdd961e3SKeyon Jie  */
437fdd961e3SKeyon Jie struct snd_soc_dai_driver skl_dai[] = {
438fdd961e3SKeyon Jie {
439fdd961e3SKeyon Jie 	.name = "SSP0 Pin",
440e81d47e9SBard Liao 	.playback = {
441e81d47e9SBard Liao 		.channels_min = 1,
442e81d47e9SBard Liao 		.channels_max = 8,
443e81d47e9SBard Liao 	},
444e81d47e9SBard Liao 	.capture = {
445e81d47e9SBard Liao 		.channels_min = 1,
446e81d47e9SBard Liao 		.channels_max = 8,
447e81d47e9SBard Liao 	},
448fdd961e3SKeyon Jie },
449fdd961e3SKeyon Jie {
450fdd961e3SKeyon Jie 	.name = "SSP1 Pin",
451e81d47e9SBard Liao 	.playback = {
452e81d47e9SBard Liao 		.channels_min = 1,
453e81d47e9SBard Liao 		.channels_max = 8,
454e81d47e9SBard Liao 	},
455e81d47e9SBard Liao 	.capture = {
456e81d47e9SBard Liao 		.channels_min = 1,
457e81d47e9SBard Liao 		.channels_max = 8,
458e81d47e9SBard Liao 	},
459fdd961e3SKeyon Jie },
460fdd961e3SKeyon Jie {
461fdd961e3SKeyon Jie 	.name = "SSP2 Pin",
462e81d47e9SBard Liao 	.playback = {
463e81d47e9SBard Liao 		.channels_min = 1,
464e81d47e9SBard Liao 		.channels_max = 8,
465e81d47e9SBard Liao 	},
466e81d47e9SBard Liao 	.capture = {
467e81d47e9SBard Liao 		.channels_min = 1,
468e81d47e9SBard Liao 		.channels_max = 8,
469e81d47e9SBard Liao 	},
470fdd961e3SKeyon Jie },
471fdd961e3SKeyon Jie {
472fdd961e3SKeyon Jie 	.name = "SSP3 Pin",
473e81d47e9SBard Liao 	.playback = {
474e81d47e9SBard Liao 		.channels_min = 1,
475e81d47e9SBard Liao 		.channels_max = 8,
476e81d47e9SBard Liao 	},
477e81d47e9SBard Liao 	.capture = {
478e81d47e9SBard Liao 		.channels_min = 1,
479e81d47e9SBard Liao 		.channels_max = 8,
480e81d47e9SBard Liao 	},
481fdd961e3SKeyon Jie },
482fdd961e3SKeyon Jie {
483fdd961e3SKeyon Jie 	.name = "SSP4 Pin",
484e81d47e9SBard Liao 	.playback = {
485e81d47e9SBard Liao 		.channels_min = 1,
486e81d47e9SBard Liao 		.channels_max = 8,
487e81d47e9SBard Liao 	},
488e81d47e9SBard Liao 	.capture = {
489e81d47e9SBard Liao 		.channels_min = 1,
490e81d47e9SBard Liao 		.channels_max = 8,
491e81d47e9SBard Liao 	},
492fdd961e3SKeyon Jie },
493fdd961e3SKeyon Jie {
494fdd961e3SKeyon Jie 	.name = "SSP5 Pin",
495e81d47e9SBard Liao 	.playback = {
496e81d47e9SBard Liao 		.channels_min = 1,
497e81d47e9SBard Liao 		.channels_max = 8,
498e81d47e9SBard Liao 	},
499e81d47e9SBard Liao 	.capture = {
500e81d47e9SBard Liao 		.channels_min = 1,
501e81d47e9SBard Liao 		.channels_max = 8,
502e81d47e9SBard Liao 	},
503fdd961e3SKeyon Jie },
504fdd961e3SKeyon Jie {
505fdd961e3SKeyon Jie 	.name = "DMIC01 Pin",
506e81d47e9SBard Liao 	.capture = {
507e81d47e9SBard Liao 		.channels_min = 1,
508e81d47e9SBard Liao 		.channels_max = 4,
509e81d47e9SBard Liao 	},
510fdd961e3SKeyon Jie },
511fdd961e3SKeyon Jie {
512fdd961e3SKeyon Jie 	.name = "DMIC16k Pin",
513e81d47e9SBard Liao 	.capture = {
514e81d47e9SBard Liao 		.channels_min = 1,
515e81d47e9SBard Liao 		.channels_max = 4,
516e81d47e9SBard Liao 	},
517fdd961e3SKeyon Jie },
518a4203256SPierre-Louis Bossart #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
519fdd961e3SKeyon Jie {
520fdd961e3SKeyon Jie 	.name = "iDisp1 Pin",
521e81d47e9SBard Liao 	.playback = {
522e81d47e9SBard Liao 		.channels_min = 1,
523e81d47e9SBard Liao 		.channels_max = 8,
524e81d47e9SBard Liao 	},
525fdd961e3SKeyon Jie },
526fdd961e3SKeyon Jie {
527fdd961e3SKeyon Jie 	.name = "iDisp2 Pin",
528e81d47e9SBard Liao 	.playback = {
529e81d47e9SBard Liao 		.channels_min = 1,
530e81d47e9SBard Liao 		.channels_max = 8,
531e81d47e9SBard Liao 	},
532fdd961e3SKeyon Jie },
533fdd961e3SKeyon Jie {
534fdd961e3SKeyon Jie 	.name = "iDisp3 Pin",
535e81d47e9SBard Liao 	.playback = {
536e81d47e9SBard Liao 		.channels_min = 1,
537e81d47e9SBard Liao 		.channels_max = 8,
538e81d47e9SBard Liao 	},
539fdd961e3SKeyon Jie },
540fdd961e3SKeyon Jie {
541e68d6696SSathyanarayana Nujella 	.name = "iDisp4 Pin",
542e81d47e9SBard Liao 	.playback = {
543e81d47e9SBard Liao 		.channels_min = 1,
544e81d47e9SBard Liao 		.channels_max = 8,
545e81d47e9SBard Liao 	},
546e68d6696SSathyanarayana Nujella },
547e68d6696SSathyanarayana Nujella {
548fdd961e3SKeyon Jie 	.name = "Analog CPU DAI",
549e81d47e9SBard Liao 	.playback = {
550e81d47e9SBard Liao 		.channels_min = 1,
551e81d47e9SBard Liao 		.channels_max = 16,
552e81d47e9SBard Liao 	},
553e81d47e9SBard Liao 	.capture = {
554e81d47e9SBard Liao 		.channels_min = 1,
555e81d47e9SBard Liao 		.channels_max = 16,
556e81d47e9SBard Liao 	},
557fdd961e3SKeyon Jie },
558fdd961e3SKeyon Jie {
559fdd961e3SKeyon Jie 	.name = "Digital CPU DAI",
560e81d47e9SBard Liao 	.playback = {
561e81d47e9SBard Liao 		.channels_min = 1,
562e81d47e9SBard Liao 		.channels_max = 16,
563e81d47e9SBard Liao 	},
564e81d47e9SBard Liao 	.capture = {
565e81d47e9SBard Liao 		.channels_min = 1,
566e81d47e9SBard Liao 		.channels_max = 16,
567e81d47e9SBard Liao 	},
568fdd961e3SKeyon Jie },
569fdd961e3SKeyon Jie {
570fdd961e3SKeyon Jie 	.name = "Alt Analog CPU DAI",
571e81d47e9SBard Liao 	.playback = {
572e81d47e9SBard Liao 		.channels_min = 1,
573e81d47e9SBard Liao 		.channels_max = 16,
574e81d47e9SBard Liao 	},
575e81d47e9SBard Liao 	.capture = {
576e81d47e9SBard Liao 		.channels_min = 1,
577e81d47e9SBard Liao 		.channels_max = 16,
578e81d47e9SBard Liao 	},
579fdd961e3SKeyon Jie },
580fdd961e3SKeyon Jie #endif
581fdd961e3SKeyon Jie };
582f09e9284SPierre-Louis Bossart 
583f09e9284SPierre-Louis Bossart int hda_dsp_dais_suspend(struct snd_sof_dev *sdev)
584f09e9284SPierre-Louis Bossart {
585f09e9284SPierre-Louis Bossart 	/*
586f09e9284SPierre-Louis Bossart 	 * In the corner case where a SUSPEND happens during a PAUSE, the ALSA core
587f09e9284SPierre-Louis Bossart 	 * does not throw the TRIGGER_SUSPEND. This leaves the DAIs in an unbalanced state.
588f09e9284SPierre-Louis Bossart 	 * Since the component suspend is called last, we can trap this corner case
589f09e9284SPierre-Louis Bossart 	 * and force the DAIs to release their resources.
590f09e9284SPierre-Louis Bossart 	 */
591a4203256SPierre-Louis Bossart #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
592f09e9284SPierre-Louis Bossart 	int ret;
593f09e9284SPierre-Louis Bossart 
594f09e9284SPierre-Louis Bossart 	ret = hda_dai_suspend(sof_to_bus(sdev));
595f09e9284SPierre-Louis Bossart 	if (ret < 0)
596f09e9284SPierre-Louis Bossart 		return ret;
597f09e9284SPierre-Louis Bossart #endif
598f09e9284SPierre-Louis Bossart 
599f09e9284SPierre-Louis Bossart 	return 0;
600f09e9284SPierre-Louis Bossart }
601