xref: /openbmc/linux/sound/soc/sof/intel/hda-dai.c (revision 8c29e78b)
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;
34e6ffb0d5SRanjani Sridharan 	struct snd_soc_component *component = swidget->scomp;
35e6ffb0d5SRanjani Sridharan 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(component);
36e6ffb0d5SRanjani Sridharan 	const struct sof_ipc_tplg_ops *tplg_ops = sof_ipc_get_ops(sdev, tplg);
37e6ffb0d5SRanjani Sridharan 	int ret;
38e6ffb0d5SRanjani Sridharan 
39e6ffb0d5SRanjani Sridharan 	if (tplg_ops && tplg_ops->dai_config) {
40e6ffb0d5SRanjani Sridharan 		ret = tplg_ops->dai_config(sdev, swidget, flags, data);
41e6ffb0d5SRanjani Sridharan 		if (ret < 0) {
42e6ffb0d5SRanjani Sridharan 			dev_err(sdev->dev, "DAI config with flags %x failed for widget %s\n",
43e6ffb0d5SRanjani Sridharan 				flags, w->name);
44e6ffb0d5SRanjani Sridharan 			return ret;
45e6ffb0d5SRanjani Sridharan 		}
46e6ffb0d5SRanjani Sridharan 	}
47e6ffb0d5SRanjani Sridharan 
48e6ffb0d5SRanjani Sridharan 	return 0;
49e6ffb0d5SRanjani Sridharan }
50e6ffb0d5SRanjani Sridharan 
512b009fa0SRanjani Sridharan #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
522b009fa0SRanjani Sridharan 
5380afde34SRanjani Sridharan static const struct hda_dai_widget_dma_ops *
5480afde34SRanjani Sridharan hda_dai_get_ops(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai)
55fdd961e3SKeyon Jie {
5680afde34SRanjani Sridharan 	struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream);
5780afde34SRanjani Sridharan 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(cpu_dai->component);
5880afde34SRanjani Sridharan 	struct snd_sof_widget *swidget = w->dobj.private;
5980afde34SRanjani Sridharan 	struct snd_sof_dai *sdai = swidget->private;
60fdd961e3SKeyon Jie 
6180afde34SRanjani Sridharan 	/* select and set the DAI widget ops if not set already */
6280afde34SRanjani Sridharan 	if (!sdai->platform_private) {
6380afde34SRanjani Sridharan 		const struct hda_dai_widget_dma_ops *ops =
6480afde34SRanjani Sridharan 			hda_select_dai_widget_ops(sdev, swidget);
65fdd961e3SKeyon Jie 
6680afde34SRanjani Sridharan 		if (!ops)
67bdf4ad3fSRanjani Sridharan 			return NULL;
6880afde34SRanjani Sridharan 
6980afde34SRanjani Sridharan 		/* check if mandatory ops are set */
7080afde34SRanjani Sridharan 		if (!ops || !ops->get_hext_stream)
7180afde34SRanjani Sridharan 			return NULL;
7280afde34SRanjani Sridharan 
7380afde34SRanjani Sridharan 		sdai->platform_private = ops;
74fdd961e3SKeyon Jie 	}
75fdd961e3SKeyon Jie 
7680afde34SRanjani Sridharan 	return sdai->platform_private;
77fdd961e3SKeyon Jie }
78fdd961e3SKeyon Jie 
79880924caSPierre-Louis Bossart static int hda_link_dma_cleanup(struct snd_pcm_substream *substream,
800351a9b8SPierre-Louis Bossart 				struct hdac_ext_stream *hext_stream,
81880924caSPierre-Louis Bossart 				struct snd_soc_dai *cpu_dai,
822be2caf4SRanjani Sridharan 				struct snd_soc_dai *codec_dai)
83880924caSPierre-Louis Bossart {
8480afde34SRanjani Sridharan 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(cpu_dai->component);
8580afde34SRanjani Sridharan 	const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai);
860351a9b8SPierre-Louis Bossart 	struct hdac_stream *hstream = &hext_stream->hstream;
87880924caSPierre-Louis Bossart 	struct hdac_bus *bus = hstream->bus;
88880924caSPierre-Louis Bossart 	struct sof_intel_hda_stream *hda_stream;
897f1e16aeSPierre-Louis Bossart 	struct hdac_ext_link *hlink;
90880924caSPierre-Louis Bossart 	int stream_tag;
91880924caSPierre-Louis Bossart 
92b0cd60f3SPierre-Louis Bossart 	hlink = snd_hdac_ext_bus_get_hlink_by_name(bus, codec_dai->component->name);
937f1e16aeSPierre-Louis Bossart 	if (!hlink)
94880924caSPierre-Louis Bossart 		return -EINVAL;
95880924caSPierre-Louis Bossart 
96880924caSPierre-Louis Bossart 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
97880924caSPierre-Louis Bossart 		stream_tag = hdac_stream(hext_stream)->stream_tag;
987fa403f2SPierre-Louis Bossart 		snd_hdac_ext_bus_link_clear_stream_id(hlink, stream_tag);
99880924caSPierre-Louis Bossart 	}
10080afde34SRanjani Sridharan 
10180afde34SRanjani Sridharan 	if (ops->release_hext_stream)
10280afde34SRanjani Sridharan 		ops->release_hext_stream(sdev, cpu_dai, substream);
10380afde34SRanjani Sridharan 
104880924caSPierre-Louis Bossart 	hext_stream->link_prepared = 0;
105880924caSPierre-Louis Bossart 
106880924caSPierre-Louis Bossart 	/* free the host DMA channel reserved by hostless streams */
107880924caSPierre-Louis Bossart 	hda_stream = hstream_to_sof_hda_stream(hext_stream);
108880924caSPierre-Louis Bossart 	hda_stream->host_reserved = 0;
109880924caSPierre-Louis Bossart 
110880924caSPierre-Louis Bossart 	return 0;
111880924caSPierre-Louis Bossart }
112880924caSPierre-Louis Bossart 
113f321ffc8SPierre-Louis Bossart static int hda_link_dma_hw_params(struct snd_pcm_substream *substream,
114b436ed8dSRanjani Sridharan 				  struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai)
115fdd961e3SKeyon Jie {
11680afde34SRanjani Sridharan 	const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai);
1171205300aSKuninori Morimoto 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
118be3e8de7SKuninori Morimoto 	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
1194842f79fSPierre-Louis Bossart 	struct hdac_ext_stream *hext_stream;
120a8310c00SRanjani Sridharan 	struct hdac_stream *hstream;
1217f1e16aeSPierre-Louis Bossart 	struct hdac_ext_link *hlink;
1224842f79fSPierre-Louis Bossart 	struct snd_sof_dev *sdev;
1234842f79fSPierre-Louis Bossart 	struct hdac_bus *bus;
124a8310c00SRanjani Sridharan 	unsigned int format_val;
1252a6afac2SRanjani Sridharan 	unsigned int link_bps;
12680afde34SRanjani Sridharan 	int stream_tag;
1274842f79fSPierre-Louis Bossart 
1284842f79fSPierre-Louis Bossart 	sdev = snd_soc_component_get_drvdata(cpu_dai->component);
1294842f79fSPierre-Louis Bossart 	bus = sof_to_bus(sdev);
130fdd961e3SKeyon Jie 
1311f810d2bSPierre-Louis Bossart 	hlink = snd_hdac_ext_bus_get_hlink_by_name(bus, codec_dai->component->name);
1321f810d2bSPierre-Louis Bossart 	if (!hlink)
1331f810d2bSPierre-Louis Bossart 		return -EINVAL;
1341f810d2bSPierre-Louis Bossart 
13580afde34SRanjani Sridharan 	hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream);
13680afde34SRanjani Sridharan 
1377d88b960SPierre-Louis Bossart 	if (!hext_stream) {
13880afde34SRanjani Sridharan 		if (ops->assign_hext_stream)
13980afde34SRanjani Sridharan 			hext_stream = ops->assign_hext_stream(sdev, cpu_dai, substream);
14080afde34SRanjani Sridharan 	}
14180afde34SRanjani Sridharan 
1427d88b960SPierre-Louis Bossart 	if (!hext_stream)
143bdf4ad3fSRanjani Sridharan 		return -EBUSY;
144921162c8SPierre-Louis Bossart 
145a8310c00SRanjani Sridharan 	hstream = &hext_stream->hstream;
146a8310c00SRanjani Sridharan 	stream_tag = hstream->stream_tag;
147a8310c00SRanjani Sridharan 
1482a6afac2SRanjani Sridharan 	if (hext_stream->hstream.direction == SNDRV_PCM_STREAM_PLAYBACK)
1492a6afac2SRanjani Sridharan 		snd_hdac_ext_bus_link_set_stream_id(hlink, stream_tag);
1502a6afac2SRanjani Sridharan 
15163611041SPierre-Louis Bossart 	/* set the hdac_stream in the codec dai */
1527d88b960SPierre-Louis Bossart 	snd_soc_dai_set_stream(codec_dai, hdac_stream(hext_stream), substream->stream);
153fdd961e3SKeyon Jie 
154fdd961e3SKeyon Jie 	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
1552a6afac2SRanjani Sridharan 		link_bps = codec_dai->driver->playback.sig_bits;
156fdd961e3SKeyon Jie 	else
1572a6afac2SRanjani Sridharan 		link_bps = codec_dai->driver->capture.sig_bits;
158fdd961e3SKeyon Jie 
159e2d6569aSRanjani Sridharan 	if (ops->reset_hext_stream)
160e2d6569aSRanjani Sridharan 		ops->reset_hext_stream(sdev, hext_stream);
161a8310c00SRanjani Sridharan 
1622a6afac2SRanjani Sridharan 	format_val = snd_hdac_calc_stream_format(params_rate(params), params_channels(params),
1632a6afac2SRanjani Sridharan 						 params_format(params), link_bps, 0);
164a8310c00SRanjani Sridharan 
165a8310c00SRanjani Sridharan 	dev_dbg(bus->dev, "format_val=%d, rate=%d, ch=%d, format=%d\n",
1662a6afac2SRanjani Sridharan 		format_val, params_rate(params), params_channels(params), params_format(params));
167a8310c00SRanjani Sridharan 
168e2d6569aSRanjani Sridharan 	if (ops->setup_hext_stream)
169e2d6569aSRanjani Sridharan 		ops->setup_hext_stream(sdev, hext_stream, format_val);
170a8310c00SRanjani Sridharan 
171a8310c00SRanjani Sridharan 	hext_stream->link_prepared = 1;
172a8310c00SRanjani Sridharan 
173a8310c00SRanjani Sridharan 	return 0;
174fdd961e3SKeyon Jie }
175fdd961e3SKeyon Jie 
176b436ed8dSRanjani Sridharan static int hda_link_dma_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai)
177f321ffc8SPierre-Louis Bossart {
1781205300aSKuninori Morimoto 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
179fdd961e3SKeyon Jie 	int stream = substream->stream;
180fdd961e3SKeyon Jie 
181b436ed8dSRanjani Sridharan 	return hda_link_dma_hw_params(substream, &rtd->dpcm[stream].hw_params, cpu_dai);
182fdd961e3SKeyon Jie }
183fdd961e3SKeyon Jie 
184*8c29e78bSRanjani Sridharan static int hda_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai)
1859272d6c2SPierre-Louis Bossart {
18680afde34SRanjani Sridharan 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(cpu_dai->component);
18780afde34SRanjani Sridharan 	const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai);
1889272d6c2SPierre-Louis Bossart 	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
1899272d6c2SPierre-Louis Bossart 	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
1909272d6c2SPierre-Louis Bossart 	struct hdac_ext_stream *hext_stream;
1919272d6c2SPierre-Louis Bossart 
19280afde34SRanjani Sridharan 	if (!ops) {
19380afde34SRanjani Sridharan 		dev_err(sdev->dev, "DAI widget ops not set\n");
19480afde34SRanjani Sridharan 		return -EINVAL;
19580afde34SRanjani Sridharan 	}
19680afde34SRanjani Sridharan 
19780afde34SRanjani Sridharan 	hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream);
198880924caSPierre-Louis Bossart 	if (!hext_stream)
1999272d6c2SPierre-Louis Bossart 		return 0;
200880924caSPierre-Louis Bossart 
2012be2caf4SRanjani Sridharan 	return hda_link_dma_cleanup(substream, hext_stream, cpu_dai, codec_dai);
2029272d6c2SPierre-Louis Bossart }
2039272d6c2SPierre-Louis Bossart 
2049272d6c2SPierre-Louis Bossart static int hda_dai_hw_params(struct snd_pcm_substream *substream,
2059272d6c2SPierre-Louis Bossart 			     struct snd_pcm_hw_params *params,
2069272d6c2SPierre-Louis Bossart 			     struct snd_soc_dai *dai)
2079272d6c2SPierre-Louis Bossart {
208e6ffb0d5SRanjani Sridharan 	struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, substream->stream);
20980afde34SRanjani Sridharan 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
21080afde34SRanjani Sridharan 	const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai);
21180afde34SRanjani Sridharan 	struct hdac_ext_stream *hext_stream;
212e6ffb0d5SRanjani Sridharan 	struct snd_sof_dai_config_data data = { 0 };
213e6ffb0d5SRanjani Sridharan 	unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS;
2149272d6c2SPierre-Louis Bossart 	int ret;
2159272d6c2SPierre-Louis Bossart 
21680afde34SRanjani Sridharan 	if (!ops) {
21780afde34SRanjani Sridharan 		dev_err(sdev->dev, "DAI widget ops not set\n");
21880afde34SRanjani Sridharan 		return -EINVAL;
21980afde34SRanjani Sridharan 	}
22080afde34SRanjani Sridharan 
22180afde34SRanjani Sridharan 	hext_stream = ops->get_hext_stream(sdev, dai, substream);
222c4eb48f7SPierre-Louis Bossart 	if (hext_stream && hext_stream->link_prepared)
223c4eb48f7SPierre-Louis Bossart 		return 0;
224c4eb48f7SPierre-Louis Bossart 
225b436ed8dSRanjani Sridharan 	ret = hda_link_dma_hw_params(substream, params, dai);
2269272d6c2SPierre-Louis Bossart 	if (ret < 0)
2279272d6c2SPierre-Louis Bossart 		return ret;
2289272d6c2SPierre-Louis Bossart 
229e6ffb0d5SRanjani Sridharan 	hext_stream = ops->get_hext_stream(sdev, dai, substream);
2309272d6c2SPierre-Louis Bossart 
231e6ffb0d5SRanjani Sridharan 	flags |= SOF_DAI_CONFIG_FLAGS_2_STEP_STOP << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT;
232e6ffb0d5SRanjani Sridharan 	data.dai_data = hdac_stream(hext_stream)->stream_tag - 1;
2339272d6c2SPierre-Louis Bossart 
234e6ffb0d5SRanjani Sridharan 	return hda_dai_config(w, flags, &data);
2359272d6c2SPierre-Louis Bossart }
2369272d6c2SPierre-Louis Bossart 
2374c30004aSRanjani Sridharan static int hda_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
2389272d6c2SPierre-Louis Bossart {
239e6ffb0d5SRanjani Sridharan 	struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, substream->stream);
2409272d6c2SPierre-Louis Bossart 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
24180afde34SRanjani Sridharan 	const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai);
24280afde34SRanjani Sridharan 	struct hdac_ext_stream *hext_stream;
243e6ffb0d5SRanjani Sridharan 	struct snd_sof_dai_config_data data = { 0 };
244e6ffb0d5SRanjani Sridharan 	unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS;
2459272d6c2SPierre-Louis Bossart 	int ret;
2469272d6c2SPierre-Louis Bossart 
24780afde34SRanjani Sridharan 	hext_stream = ops->get_hext_stream(sdev, dai, substream);
248722cbbfaSPierre-Louis Bossart 	if (hext_stream && hext_stream->link_prepared)
2499272d6c2SPierre-Louis Bossart 		return 0;
2509272d6c2SPierre-Louis Bossart 
25118701bb1SPierre-Louis Bossart 	dev_dbg(sdev->dev, "prepare stream dir %d\n", substream->stream);
2529272d6c2SPierre-Louis Bossart 
253b436ed8dSRanjani Sridharan 	ret = hda_link_dma_prepare(substream, dai);
2549272d6c2SPierre-Louis Bossart 	if (ret < 0)
2559272d6c2SPierre-Louis Bossart 		return ret;
2569272d6c2SPierre-Louis Bossart 
257e6ffb0d5SRanjani Sridharan 	hext_stream = ops->get_hext_stream(sdev, dai, substream);
258e6ffb0d5SRanjani Sridharan 
259e6ffb0d5SRanjani Sridharan 	flags |= SOF_DAI_CONFIG_FLAGS_2_STEP_STOP << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT;
260e6ffb0d5SRanjani Sridharan 	data.dai_data = hdac_stream(hext_stream)->stream_tag - 1;
261e6ffb0d5SRanjani Sridharan 
262e6ffb0d5SRanjani Sridharan 	return hda_dai_config(w, flags, &data);
2639272d6c2SPierre-Louis Bossart }
2649272d6c2SPierre-Louis Bossart 
2654c30004aSRanjani Sridharan /*
2664c30004aSRanjani Sridharan  * In contrast to IPC3, the dai trigger in IPC4 mixes pipeline state changes
2674c30004aSRanjani Sridharan  * (over IPC channel) and DMA state change (direct host register changes).
2684c30004aSRanjani Sridharan  */
2692b009fa0SRanjani Sridharan static int hda_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
2704c30004aSRanjani Sridharan {
2714c30004aSRanjani Sridharan 	struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
27280afde34SRanjani Sridharan 	const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai);
27380afde34SRanjani Sridharan 	struct hdac_ext_stream *hext_stream;
2744c30004aSRanjani Sridharan 	struct snd_soc_pcm_runtime *rtd;
2754c30004aSRanjani Sridharan 	struct snd_soc_dai *codec_dai;
2764c30004aSRanjani Sridharan 	int ret;
2774c30004aSRanjani Sridharan 
27818701bb1SPierre-Louis Bossart 	dev_dbg(dai->dev, "cmd=%d dai %s direction %d\n", cmd,
2794c30004aSRanjani Sridharan 		dai->name, substream->stream);
2804c30004aSRanjani Sridharan 
28180afde34SRanjani Sridharan 	hext_stream = ops->get_hext_stream(sdev, dai, substream);
28280afde34SRanjani Sridharan 	if (!hext_stream)
28380afde34SRanjani Sridharan 		return -EINVAL;
28480afde34SRanjani Sridharan 
2854c30004aSRanjani Sridharan 	rtd = asoc_substream_to_rtd(substream);
2864c30004aSRanjani Sridharan 	codec_dai = asoc_rtd_to_codec(rtd, 0);
2874c30004aSRanjani Sridharan 
2884b2ee4cdSRanjani Sridharan 	if (ops->pre_trigger) {
2894b2ee4cdSRanjani Sridharan 		ret = ops->pre_trigger(sdev, dai, substream, cmd);
29037a26eecSRanjani Sridharan 		if (ret < 0)
29137a26eecSRanjani Sridharan 			return ret;
29237a26eecSRanjani Sridharan 	}
29337a26eecSRanjani Sridharan 
2944b2ee4cdSRanjani Sridharan 	if (ops->trigger) {
2954b2ee4cdSRanjani Sridharan 		ret = ops->trigger(sdev, dai, substream, cmd);
29637a26eecSRanjani Sridharan 		if (ret < 0)
29737a26eecSRanjani Sridharan 			return ret;
2984b2ee4cdSRanjani Sridharan 	}
2994b2ee4cdSRanjani Sridharan 
3004b2ee4cdSRanjani Sridharan 	if (ops->post_trigger) {
3014b2ee4cdSRanjani Sridharan 		ret = ops->post_trigger(sdev, dai, substream, cmd);
3024b2ee4cdSRanjani Sridharan 		if (ret < 0)
3034b2ee4cdSRanjani Sridharan 			return ret;
3044b2ee4cdSRanjani Sridharan 	}
3054b2ee4cdSRanjani Sridharan 
3064b2ee4cdSRanjani Sridharan 	switch (cmd) {
3074c30004aSRanjani Sridharan 	case SNDRV_PCM_TRIGGER_SUSPEND:
3084c30004aSRanjani Sridharan 	case SNDRV_PCM_TRIGGER_STOP:
309be7f4f8dSRanjani Sridharan 		ret = hda_link_dma_cleanup(substream, hext_stream, dai, codec_dai);
3104c30004aSRanjani Sridharan 		if (ret < 0) {
3114c30004aSRanjani Sridharan 			dev_err(sdev->dev, "%s: failed to clean up link DMA\n", __func__);
3124c30004aSRanjani Sridharan 			return ret;
3134c30004aSRanjani Sridharan 		}
3144c30004aSRanjani Sridharan 		break;
3154c30004aSRanjani Sridharan 	default:
3164b2ee4cdSRanjani Sridharan 		break;
3174c30004aSRanjani Sridharan 	}
3184c30004aSRanjani Sridharan 
3194c30004aSRanjani Sridharan 	return 0;
3204c30004aSRanjani Sridharan }
3214c30004aSRanjani Sridharan 
3222b009fa0SRanjani Sridharan static const struct snd_soc_dai_ops hda_dai_ops = {
323309e6e55SPierre-Louis Bossart 	.hw_params = hda_dai_hw_params,
324309e6e55SPierre-Louis Bossart 	.hw_free = hda_dai_hw_free,
3252b009fa0SRanjani Sridharan 	.trigger = hda_dai_trigger,
3264c30004aSRanjani Sridharan 	.prepare = hda_dai_prepare,
327fdd961e3SKeyon Jie };
32870368106SCezary Rojewski 
329f09e9284SPierre-Louis Bossart static int hda_dai_suspend(struct hdac_bus *bus)
330f09e9284SPierre-Louis Bossart {
331f09e9284SPierre-Louis Bossart 	struct snd_soc_pcm_runtime *rtd;
332f09e9284SPierre-Louis Bossart 	struct hdac_ext_stream *hext_stream;
333f09e9284SPierre-Louis Bossart 	struct hdac_stream *s;
33423b1944eSPierre-Louis Bossart 	int ret;
335f09e9284SPierre-Louis Bossart 
336f09e9284SPierre-Louis Bossart 	/* set internal flag for BE */
337f09e9284SPierre-Louis Bossart 	list_for_each_entry(s, &bus->stream_list, list) {
338722cbbfaSPierre-Louis Bossart 
339f09e9284SPierre-Louis Bossart 		hext_stream = stream_to_hdac_ext_stream(s);
340f09e9284SPierre-Louis Bossart 
341f09e9284SPierre-Louis Bossart 		/*
342f09e9284SPierre-Louis Bossart 		 * clear stream. This should already be taken care for running
343f09e9284SPierre-Louis Bossart 		 * streams when the SUSPEND trigger is called. But paused
344f09e9284SPierre-Louis Bossart 		 * streams do not get suspended, so this needs to be done
345f09e9284SPierre-Louis Bossart 		 * explicitly during suspend.
346f09e9284SPierre-Louis Bossart 		 */
347f09e9284SPierre-Louis Bossart 		if (hext_stream->link_substream) {
3482b009fa0SRanjani Sridharan 			const struct hda_dai_widget_dma_ops *ops;
3492b009fa0SRanjani Sridharan 			struct snd_sof_widget *swidget;
3502b009fa0SRanjani Sridharan 			struct snd_soc_dapm_widget *w;
35123b1944eSPierre-Louis Bossart 			struct snd_soc_dai *codec_dai;
3522b009fa0SRanjani Sridharan 			struct snd_soc_dai *cpu_dai;
3532b009fa0SRanjani Sridharan 			struct snd_sof_dev *sdev;
3542b009fa0SRanjani Sridharan 			struct snd_sof_dai *sdai;
35523b1944eSPierre-Louis Bossart 
356f09e9284SPierre-Louis Bossart 			rtd = asoc_substream_to_rtd(hext_stream->link_substream);
35723b1944eSPierre-Louis Bossart 			cpu_dai = asoc_rtd_to_cpu(rtd, 0);
35823b1944eSPierre-Louis Bossart 			codec_dai = asoc_rtd_to_codec(rtd, 0);
3592b009fa0SRanjani Sridharan 			w = snd_soc_dai_get_widget(cpu_dai, hdac_stream(hext_stream)->direction);
3602b009fa0SRanjani Sridharan 			swidget = w->dobj.private;
3612b009fa0SRanjani Sridharan 			sdev = snd_soc_component_get_drvdata(swidget->scomp);
3622b009fa0SRanjani Sridharan 			sdai = swidget->private;
3632b009fa0SRanjani Sridharan 			ops = sdai->platform_private;
364f09e9284SPierre-Louis Bossart 
3650351a9b8SPierre-Louis Bossart 			ret = hda_link_dma_cleanup(hext_stream->link_substream,
3660351a9b8SPierre-Louis Bossart 						   hext_stream,
3672be2caf4SRanjani Sridharan 						   cpu_dai, codec_dai);
368880924caSPierre-Louis Bossart 			if (ret < 0)
369880924caSPierre-Louis Bossart 				return ret;
370722cbbfaSPierre-Louis Bossart 
3712b009fa0SRanjani Sridharan 			/* for consistency with TRIGGER_SUSPEND  */
3722b009fa0SRanjani Sridharan 			if (ops->post_trigger) {
3732b009fa0SRanjani Sridharan 				ret = ops->post_trigger(sdev, cpu_dai,
3742b009fa0SRanjani Sridharan 							hext_stream->link_substream,
3752b009fa0SRanjani Sridharan 							SNDRV_PCM_TRIGGER_SUSPEND);
37623b1944eSPierre-Louis Bossart 				if (ret < 0)
37723b1944eSPierre-Louis Bossart 					return ret;
37823b1944eSPierre-Louis Bossart 			}
379f09e9284SPierre-Louis Bossart 		}
3802b009fa0SRanjani Sridharan 	}
381f09e9284SPierre-Louis Bossart 
382f09e9284SPierre-Louis Bossart 	return 0;
383f09e9284SPierre-Louis Bossart }
3844c30004aSRanjani Sridharan 
385fdd961e3SKeyon Jie #endif
386fdd961e3SKeyon Jie 
38751ec71dcSRanjani Sridharan void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops)
38851ec71dcSRanjani Sridharan {
38951ec71dcSRanjani Sridharan 	int i;
39051ec71dcSRanjani Sridharan 
39151ec71dcSRanjani Sridharan 	for (i = 0; i < ops->num_drv; i++) {
392a4203256SPierre-Louis Bossart #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
39351ec71dcSRanjani Sridharan 		if (strstr(ops->drv[i].name, "iDisp") ||
39451ec71dcSRanjani Sridharan 		    strstr(ops->drv[i].name, "Analog") ||
39551ec71dcSRanjani Sridharan 		    strstr(ops->drv[i].name, "Digital"))
3962b009fa0SRanjani Sridharan 			ops->drv[i].ops = &hda_dai_ops;
39751ec71dcSRanjani Sridharan #endif
39851ec71dcSRanjani Sridharan 	}
3992b009fa0SRanjani Sridharan 
4002b009fa0SRanjani Sridharan 	if (sdev->pdata->ipc_type == SOF_INTEL_IPC4 && !hda_use_tplg_nhlt) {
4011da51943SRanjani Sridharan 		struct sof_ipc4_fw_data *ipc4_data = sdev->private;
4021da51943SRanjani Sridharan 
4031da51943SRanjani Sridharan 		ipc4_data->nhlt = intel_nhlt_init(sdev->dev);
40451ec71dcSRanjani Sridharan 	}
40551ec71dcSRanjani Sridharan }
40651ec71dcSRanjani Sridharan 
4071da51943SRanjani Sridharan void hda_ops_free(struct snd_sof_dev *sdev)
4081da51943SRanjani Sridharan {
4091da51943SRanjani Sridharan 	if (sdev->pdata->ipc_type == SOF_INTEL_IPC4) {
4101da51943SRanjani Sridharan 		struct sof_ipc4_fw_data *ipc4_data = sdev->private;
4111da51943SRanjani Sridharan 
4121da51943SRanjani Sridharan 		if (!hda_use_tplg_nhlt)
4131da51943SRanjani Sridharan 			intel_nhlt_free(ipc4_data->nhlt);
4141da51943SRanjani Sridharan 	}
4151da51943SRanjani Sridharan }
4161da51943SRanjani Sridharan EXPORT_SYMBOL_NS(hda_ops_free, SND_SOC_SOF_INTEL_HDA_COMMON);
4171da51943SRanjani Sridharan 
418fdd961e3SKeyon Jie /*
419fdd961e3SKeyon Jie  * common dai driver for skl+ platforms.
420fdd961e3SKeyon Jie  * some products who use this DAI array only physically have a subset of
421fdd961e3SKeyon Jie  * the DAIs, but no harm is done here by adding the whole set.
422fdd961e3SKeyon Jie  */
423fdd961e3SKeyon Jie struct snd_soc_dai_driver skl_dai[] = {
424fdd961e3SKeyon Jie {
425fdd961e3SKeyon Jie 	.name = "SSP0 Pin",
426e81d47e9SBard Liao 	.playback = {
427e81d47e9SBard Liao 		.channels_min = 1,
428e81d47e9SBard Liao 		.channels_max = 8,
429e81d47e9SBard Liao 	},
430e81d47e9SBard Liao 	.capture = {
431e81d47e9SBard Liao 		.channels_min = 1,
432e81d47e9SBard Liao 		.channels_max = 8,
433e81d47e9SBard Liao 	},
434fdd961e3SKeyon Jie },
435fdd961e3SKeyon Jie {
436fdd961e3SKeyon Jie 	.name = "SSP1 Pin",
437e81d47e9SBard Liao 	.playback = {
438e81d47e9SBard Liao 		.channels_min = 1,
439e81d47e9SBard Liao 		.channels_max = 8,
440e81d47e9SBard Liao 	},
441e81d47e9SBard Liao 	.capture = {
442e81d47e9SBard Liao 		.channels_min = 1,
443e81d47e9SBard Liao 		.channels_max = 8,
444e81d47e9SBard Liao 	},
445fdd961e3SKeyon Jie },
446fdd961e3SKeyon Jie {
447fdd961e3SKeyon Jie 	.name = "SSP2 Pin",
448e81d47e9SBard Liao 	.playback = {
449e81d47e9SBard Liao 		.channels_min = 1,
450e81d47e9SBard Liao 		.channels_max = 8,
451e81d47e9SBard Liao 	},
452e81d47e9SBard Liao 	.capture = {
453e81d47e9SBard Liao 		.channels_min = 1,
454e81d47e9SBard Liao 		.channels_max = 8,
455e81d47e9SBard Liao 	},
456fdd961e3SKeyon Jie },
457fdd961e3SKeyon Jie {
458fdd961e3SKeyon Jie 	.name = "SSP3 Pin",
459e81d47e9SBard Liao 	.playback = {
460e81d47e9SBard Liao 		.channels_min = 1,
461e81d47e9SBard Liao 		.channels_max = 8,
462e81d47e9SBard Liao 	},
463e81d47e9SBard Liao 	.capture = {
464e81d47e9SBard Liao 		.channels_min = 1,
465e81d47e9SBard Liao 		.channels_max = 8,
466e81d47e9SBard Liao 	},
467fdd961e3SKeyon Jie },
468fdd961e3SKeyon Jie {
469fdd961e3SKeyon Jie 	.name = "SSP4 Pin",
470e81d47e9SBard Liao 	.playback = {
471e81d47e9SBard Liao 		.channels_min = 1,
472e81d47e9SBard Liao 		.channels_max = 8,
473e81d47e9SBard Liao 	},
474e81d47e9SBard Liao 	.capture = {
475e81d47e9SBard Liao 		.channels_min = 1,
476e81d47e9SBard Liao 		.channels_max = 8,
477e81d47e9SBard Liao 	},
478fdd961e3SKeyon Jie },
479fdd961e3SKeyon Jie {
480fdd961e3SKeyon Jie 	.name = "SSP5 Pin",
481e81d47e9SBard Liao 	.playback = {
482e81d47e9SBard Liao 		.channels_min = 1,
483e81d47e9SBard Liao 		.channels_max = 8,
484e81d47e9SBard Liao 	},
485e81d47e9SBard Liao 	.capture = {
486e81d47e9SBard Liao 		.channels_min = 1,
487e81d47e9SBard Liao 		.channels_max = 8,
488e81d47e9SBard Liao 	},
489fdd961e3SKeyon Jie },
490fdd961e3SKeyon Jie {
491fdd961e3SKeyon Jie 	.name = "DMIC01 Pin",
492e81d47e9SBard Liao 	.capture = {
493e81d47e9SBard Liao 		.channels_min = 1,
494e81d47e9SBard Liao 		.channels_max = 4,
495e81d47e9SBard Liao 	},
496fdd961e3SKeyon Jie },
497fdd961e3SKeyon Jie {
498fdd961e3SKeyon Jie 	.name = "DMIC16k Pin",
499e81d47e9SBard Liao 	.capture = {
500e81d47e9SBard Liao 		.channels_min = 1,
501e81d47e9SBard Liao 		.channels_max = 4,
502e81d47e9SBard Liao 	},
503fdd961e3SKeyon Jie },
504a4203256SPierre-Louis Bossart #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
505fdd961e3SKeyon Jie {
506fdd961e3SKeyon Jie 	.name = "iDisp1 Pin",
507e81d47e9SBard Liao 	.playback = {
508e81d47e9SBard Liao 		.channels_min = 1,
509e81d47e9SBard Liao 		.channels_max = 8,
510e81d47e9SBard Liao 	},
511fdd961e3SKeyon Jie },
512fdd961e3SKeyon Jie {
513fdd961e3SKeyon Jie 	.name = "iDisp2 Pin",
514e81d47e9SBard Liao 	.playback = {
515e81d47e9SBard Liao 		.channels_min = 1,
516e81d47e9SBard Liao 		.channels_max = 8,
517e81d47e9SBard Liao 	},
518fdd961e3SKeyon Jie },
519fdd961e3SKeyon Jie {
520fdd961e3SKeyon Jie 	.name = "iDisp3 Pin",
521e81d47e9SBard Liao 	.playback = {
522e81d47e9SBard Liao 		.channels_min = 1,
523e81d47e9SBard Liao 		.channels_max = 8,
524e81d47e9SBard Liao 	},
525fdd961e3SKeyon Jie },
526fdd961e3SKeyon Jie {
527e68d6696SSathyanarayana Nujella 	.name = "iDisp4 Pin",
528e81d47e9SBard Liao 	.playback = {
529e81d47e9SBard Liao 		.channels_min = 1,
530e81d47e9SBard Liao 		.channels_max = 8,
531e81d47e9SBard Liao 	},
532e68d6696SSathyanarayana Nujella },
533e68d6696SSathyanarayana Nujella {
534fdd961e3SKeyon Jie 	.name = "Analog CPU DAI",
535e81d47e9SBard Liao 	.playback = {
536e81d47e9SBard Liao 		.channels_min = 1,
537e81d47e9SBard Liao 		.channels_max = 16,
538e81d47e9SBard Liao 	},
539e81d47e9SBard Liao 	.capture = {
540e81d47e9SBard Liao 		.channels_min = 1,
541e81d47e9SBard Liao 		.channels_max = 16,
542e81d47e9SBard Liao 	},
543fdd961e3SKeyon Jie },
544fdd961e3SKeyon Jie {
545fdd961e3SKeyon Jie 	.name = "Digital CPU DAI",
546e81d47e9SBard Liao 	.playback = {
547e81d47e9SBard Liao 		.channels_min = 1,
548e81d47e9SBard Liao 		.channels_max = 16,
549e81d47e9SBard Liao 	},
550e81d47e9SBard Liao 	.capture = {
551e81d47e9SBard Liao 		.channels_min = 1,
552e81d47e9SBard Liao 		.channels_max = 16,
553e81d47e9SBard Liao 	},
554fdd961e3SKeyon Jie },
555fdd961e3SKeyon Jie {
556fdd961e3SKeyon Jie 	.name = "Alt Analog CPU DAI",
557e81d47e9SBard Liao 	.playback = {
558e81d47e9SBard Liao 		.channels_min = 1,
559e81d47e9SBard Liao 		.channels_max = 16,
560e81d47e9SBard Liao 	},
561e81d47e9SBard Liao 	.capture = {
562e81d47e9SBard Liao 		.channels_min = 1,
563e81d47e9SBard Liao 		.channels_max = 16,
564e81d47e9SBard Liao 	},
565fdd961e3SKeyon Jie },
566fdd961e3SKeyon Jie #endif
567fdd961e3SKeyon Jie };
568f09e9284SPierre-Louis Bossart 
569f09e9284SPierre-Louis Bossart int hda_dsp_dais_suspend(struct snd_sof_dev *sdev)
570f09e9284SPierre-Louis Bossart {
571f09e9284SPierre-Louis Bossart 	/*
572f09e9284SPierre-Louis Bossart 	 * In the corner case where a SUSPEND happens during a PAUSE, the ALSA core
573f09e9284SPierre-Louis Bossart 	 * does not throw the TRIGGER_SUSPEND. This leaves the DAIs in an unbalanced state.
574f09e9284SPierre-Louis Bossart 	 * Since the component suspend is called last, we can trap this corner case
575f09e9284SPierre-Louis Bossart 	 * and force the DAIs to release their resources.
576f09e9284SPierre-Louis Bossart 	 */
577a4203256SPierre-Louis Bossart #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
578f09e9284SPierre-Louis Bossart 	int ret;
579f09e9284SPierre-Louis Bossart 
580f09e9284SPierre-Louis Bossart 	ret = hda_dai_suspend(sof_to_bus(sdev));
581f09e9284SPierre-Louis Bossart 	if (ret < 0)
582f09e9284SPierre-Louis Bossart 		return ret;
583f09e9284SPierre-Louis Bossart #endif
584f09e9284SPierre-Louis Bossart 
585f09e9284SPierre-Louis Bossart 	return 0;
586f09e9284SPierre-Louis Bossart }
587