18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
28c2ecf20Sopenharmony_ci//
38c2ecf20Sopenharmony_ci// This file is provided under a dual BSD/GPLv2 license.  When using or
48c2ecf20Sopenharmony_ci// redistributing this file, you may do so under either license.
58c2ecf20Sopenharmony_ci//
68c2ecf20Sopenharmony_ci// Copyright(c) 2018 Intel Corporation. All rights reserved.
78c2ecf20Sopenharmony_ci//
88c2ecf20Sopenharmony_ci// Authors: Keyon Jie <yang.jie@linux.intel.com>
98c2ecf20Sopenharmony_ci//
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <sound/pcm_params.h>
128c2ecf20Sopenharmony_ci#include <sound/hdaudio_ext.h>
138c2ecf20Sopenharmony_ci#include "../sof-priv.h"
148c2ecf20Sopenharmony_ci#include "../sof-audio.h"
158c2ecf20Sopenharmony_ci#include "hda.h"
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistruct hda_pipe_params {
208c2ecf20Sopenharmony_ci	u8 host_dma_id;
218c2ecf20Sopenharmony_ci	u8 link_dma_id;
228c2ecf20Sopenharmony_ci	u32 ch;
238c2ecf20Sopenharmony_ci	u32 s_freq;
248c2ecf20Sopenharmony_ci	u32 s_fmt;
258c2ecf20Sopenharmony_ci	u8 linktype;
268c2ecf20Sopenharmony_ci	snd_pcm_format_t format;
278c2ecf20Sopenharmony_ci	int link_index;
288c2ecf20Sopenharmony_ci	int stream;
298c2ecf20Sopenharmony_ci	unsigned int host_bps;
308c2ecf20Sopenharmony_ci	unsigned int link_bps;
318c2ecf20Sopenharmony_ci};
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci/*
348c2ecf20Sopenharmony_ci * This function checks if the host dma channel corresponding
358c2ecf20Sopenharmony_ci * to the link DMA stream_tag argument is assigned to one
368c2ecf20Sopenharmony_ci * of the FEs connected to the BE DAI.
378c2ecf20Sopenharmony_ci */
388c2ecf20Sopenharmony_cistatic bool hda_check_fes(struct snd_soc_pcm_runtime *rtd,
398c2ecf20Sopenharmony_ci			  int dir, int stream_tag)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	struct snd_pcm_substream *fe_substream;
428c2ecf20Sopenharmony_ci	struct hdac_stream *fe_hstream;
438c2ecf20Sopenharmony_ci	struct snd_soc_dpcm *dpcm;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	for_each_dpcm_fe(rtd, dir, dpcm) {
468c2ecf20Sopenharmony_ci		fe_substream = snd_soc_dpcm_get_substream(dpcm->fe, dir);
478c2ecf20Sopenharmony_ci		fe_hstream = fe_substream->runtime->private_data;
488c2ecf20Sopenharmony_ci		if (fe_hstream->stream_tag == stream_tag)
498c2ecf20Sopenharmony_ci			return true;
508c2ecf20Sopenharmony_ci	}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	return false;
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic struct hdac_ext_stream *
568c2ecf20Sopenharmony_ci	hda_link_stream_assign(struct hdac_bus *bus,
578c2ecf20Sopenharmony_ci			       struct snd_pcm_substream *substream)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
608c2ecf20Sopenharmony_ci	struct sof_intel_hda_stream *hda_stream;
618c2ecf20Sopenharmony_ci	struct hdac_ext_stream *res = NULL;
628c2ecf20Sopenharmony_ci	struct hdac_stream *stream = NULL;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	int stream_dir = substream->stream;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	if (!bus->ppcap) {
678c2ecf20Sopenharmony_ci		dev_err(bus->dev, "stream type not supported\n");
688c2ecf20Sopenharmony_ci		return NULL;
698c2ecf20Sopenharmony_ci	}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	spin_lock_irq(&bus->reg_lock);
728c2ecf20Sopenharmony_ci	list_for_each_entry(stream, &bus->stream_list, list) {
738c2ecf20Sopenharmony_ci		struct hdac_ext_stream *hstream =
748c2ecf20Sopenharmony_ci			stream_to_hdac_ext_stream(stream);
758c2ecf20Sopenharmony_ci		if (stream->direction != substream->stream)
768c2ecf20Sopenharmony_ci			continue;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci		hda_stream = hstream_to_sof_hda_stream(hstream);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci		/* check if link is available */
818c2ecf20Sopenharmony_ci		if (!hstream->link_locked) {
828c2ecf20Sopenharmony_ci			if (stream->opened) {
838c2ecf20Sopenharmony_ci				/*
848c2ecf20Sopenharmony_ci				 * check if the stream tag matches the stream
858c2ecf20Sopenharmony_ci				 * tag of one of the connected FEs
868c2ecf20Sopenharmony_ci				 */
878c2ecf20Sopenharmony_ci				if (hda_check_fes(rtd, stream_dir,
888c2ecf20Sopenharmony_ci						  stream->stream_tag)) {
898c2ecf20Sopenharmony_ci					res = hstream;
908c2ecf20Sopenharmony_ci					break;
918c2ecf20Sopenharmony_ci				}
928c2ecf20Sopenharmony_ci			} else {
938c2ecf20Sopenharmony_ci				res = hstream;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci				/*
968c2ecf20Sopenharmony_ci				 * This must be a hostless stream.
978c2ecf20Sopenharmony_ci				 * So reserve the host DMA channel.
988c2ecf20Sopenharmony_ci				 */
998c2ecf20Sopenharmony_ci				hda_stream->host_reserved = 1;
1008c2ecf20Sopenharmony_ci				break;
1018c2ecf20Sopenharmony_ci			}
1028c2ecf20Sopenharmony_ci		}
1038c2ecf20Sopenharmony_ci	}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	if (res) {
1068c2ecf20Sopenharmony_ci		/*
1078c2ecf20Sopenharmony_ci		 * Decouple host and link DMA. The decoupled flag
1088c2ecf20Sopenharmony_ci		 * is updated in snd_hdac_ext_stream_decouple().
1098c2ecf20Sopenharmony_ci		 */
1108c2ecf20Sopenharmony_ci		if (!res->decoupled)
1118c2ecf20Sopenharmony_ci			snd_hdac_ext_stream_decouple_locked(bus, res, true);
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci		res->link_locked = 1;
1148c2ecf20Sopenharmony_ci		res->link_substream = substream;
1158c2ecf20Sopenharmony_ci	}
1168c2ecf20Sopenharmony_ci	spin_unlock_irq(&bus->reg_lock);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	return res;
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic int hda_link_dma_params(struct hdac_ext_stream *stream,
1228c2ecf20Sopenharmony_ci			       struct hda_pipe_params *params)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	struct hdac_stream *hstream = &stream->hstream;
1258c2ecf20Sopenharmony_ci	unsigned char stream_tag = hstream->stream_tag;
1268c2ecf20Sopenharmony_ci	struct hdac_bus *bus = hstream->bus;
1278c2ecf20Sopenharmony_ci	struct hdac_ext_link *link;
1288c2ecf20Sopenharmony_ci	unsigned int format_val;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	snd_hdac_ext_stream_decouple(bus, stream, true);
1318c2ecf20Sopenharmony_ci	snd_hdac_ext_link_stream_reset(stream);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	format_val = snd_hdac_calc_stream_format(params->s_freq, params->ch,
1348c2ecf20Sopenharmony_ci						 params->format,
1358c2ecf20Sopenharmony_ci						 params->link_bps, 0);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	dev_dbg(bus->dev, "format_val=%d, rate=%d, ch=%d, format=%d\n",
1388c2ecf20Sopenharmony_ci		format_val, params->s_freq, params->ch, params->format);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	snd_hdac_ext_link_stream_setup(stream, format_val);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	if (stream->hstream.direction == SNDRV_PCM_STREAM_PLAYBACK) {
1438c2ecf20Sopenharmony_ci		list_for_each_entry(link, &bus->hlink_list, list) {
1448c2ecf20Sopenharmony_ci			if (link->index == params->link_index)
1458c2ecf20Sopenharmony_ci				snd_hdac_ext_link_set_stream_id(link,
1468c2ecf20Sopenharmony_ci								stream_tag);
1478c2ecf20Sopenharmony_ci		}
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	stream->link_prepared = 1;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	return 0;
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci/* Send DAI_CONFIG IPC to the DAI that matches the dai_name and direction */
1568c2ecf20Sopenharmony_cistatic int hda_link_config_ipc(struct sof_intel_hda_stream *hda_stream,
1578c2ecf20Sopenharmony_ci			       const char *dai_name, int channel, int dir)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	struct sof_ipc_dai_config *config;
1608c2ecf20Sopenharmony_ci	struct snd_sof_dai *sof_dai;
1618c2ecf20Sopenharmony_ci	struct sof_ipc_reply reply;
1628c2ecf20Sopenharmony_ci	int ret = 0;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	list_for_each_entry(sof_dai, &hda_stream->sdev->dai_list, list) {
1658c2ecf20Sopenharmony_ci		if (!sof_dai->cpu_dai_name)
1668c2ecf20Sopenharmony_ci			continue;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci		if (!strcmp(dai_name, sof_dai->cpu_dai_name) &&
1698c2ecf20Sopenharmony_ci		    dir == sof_dai->comp_dai.direction) {
1708c2ecf20Sopenharmony_ci			config = sof_dai->dai_config;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci			if (!config) {
1738c2ecf20Sopenharmony_ci				dev_err(hda_stream->sdev->dev,
1748c2ecf20Sopenharmony_ci					"error: no config for DAI %s\n",
1758c2ecf20Sopenharmony_ci					sof_dai->name);
1768c2ecf20Sopenharmony_ci				return -EINVAL;
1778c2ecf20Sopenharmony_ci			}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci			/* update config with stream tag */
1808c2ecf20Sopenharmony_ci			config->hda.link_dma_ch = channel;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci			/* send IPC */
1838c2ecf20Sopenharmony_ci			ret = sof_ipc_tx_message(hda_stream->sdev->ipc,
1848c2ecf20Sopenharmony_ci						 config->hdr.cmd,
1858c2ecf20Sopenharmony_ci						 config,
1868c2ecf20Sopenharmony_ci						 config->hdr.size,
1878c2ecf20Sopenharmony_ci						 &reply, sizeof(reply));
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci			if (ret < 0)
1908c2ecf20Sopenharmony_ci				dev_err(hda_stream->sdev->dev,
1918c2ecf20Sopenharmony_ci					"error: failed to set dai config for %s\n",
1928c2ecf20Sopenharmony_ci					sof_dai->name);
1938c2ecf20Sopenharmony_ci			return ret;
1948c2ecf20Sopenharmony_ci		}
1958c2ecf20Sopenharmony_ci	}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	return -EINVAL;
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cistatic int hda_link_hw_params(struct snd_pcm_substream *substream,
2018c2ecf20Sopenharmony_ci			      struct snd_pcm_hw_params *params,
2028c2ecf20Sopenharmony_ci			      struct snd_soc_dai *dai)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	struct hdac_stream *hstream = substream->runtime->private_data;
2058c2ecf20Sopenharmony_ci	struct hdac_bus *bus = hstream->bus;
2068c2ecf20Sopenharmony_ci	struct hdac_ext_stream *link_dev;
2078c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
2088c2ecf20Sopenharmony_ci	struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
2098c2ecf20Sopenharmony_ci	struct sof_intel_hda_stream *hda_stream;
2108c2ecf20Sopenharmony_ci	struct hda_pipe_params p_params = {0};
2118c2ecf20Sopenharmony_ci	struct hdac_ext_link *link;
2128c2ecf20Sopenharmony_ci	int stream_tag;
2138c2ecf20Sopenharmony_ci	int ret;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	link = snd_hdac_ext_bus_get_link(bus, codec_dai->component->name);
2168c2ecf20Sopenharmony_ci	if (!link)
2178c2ecf20Sopenharmony_ci		return -EINVAL;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	/* get stored dma data if resuming from system suspend */
2208c2ecf20Sopenharmony_ci	link_dev = snd_soc_dai_get_dma_data(dai, substream);
2218c2ecf20Sopenharmony_ci	if (!link_dev) {
2228c2ecf20Sopenharmony_ci		link_dev = hda_link_stream_assign(bus, substream);
2238c2ecf20Sopenharmony_ci		if (!link_dev)
2248c2ecf20Sopenharmony_ci			return -EBUSY;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci		snd_soc_dai_set_dma_data(dai, substream, (void *)link_dev);
2278c2ecf20Sopenharmony_ci	}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	stream_tag = hdac_stream(link_dev)->stream_tag;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	hda_stream = hstream_to_sof_hda_stream(link_dev);
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	/* update the DSP with the new tag */
2348c2ecf20Sopenharmony_ci	ret = hda_link_config_ipc(hda_stream, dai->name, stream_tag - 1,
2358c2ecf20Sopenharmony_ci				  substream->stream);
2368c2ecf20Sopenharmony_ci	if (ret < 0)
2378c2ecf20Sopenharmony_ci		return ret;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	/* set the hdac_stream in the codec dai */
2408c2ecf20Sopenharmony_ci	snd_soc_dai_set_stream(codec_dai, hdac_stream(link_dev), substream->stream);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	p_params.s_fmt = snd_pcm_format_width(params_format(params));
2438c2ecf20Sopenharmony_ci	p_params.ch = params_channels(params);
2448c2ecf20Sopenharmony_ci	p_params.s_freq = params_rate(params);
2458c2ecf20Sopenharmony_ci	p_params.stream = substream->stream;
2468c2ecf20Sopenharmony_ci	p_params.link_dma_id = stream_tag - 1;
2478c2ecf20Sopenharmony_ci	p_params.link_index = link->index;
2488c2ecf20Sopenharmony_ci	p_params.format = params_format(params);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
2518c2ecf20Sopenharmony_ci		p_params.link_bps = codec_dai->driver->playback.sig_bits;
2528c2ecf20Sopenharmony_ci	else
2538c2ecf20Sopenharmony_ci		p_params.link_bps = codec_dai->driver->capture.sig_bits;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	return hda_link_dma_params(link_dev, &p_params);
2568c2ecf20Sopenharmony_ci}
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_cistatic int hda_link_pcm_prepare(struct snd_pcm_substream *substream,
2598c2ecf20Sopenharmony_ci				struct snd_soc_dai *dai)
2608c2ecf20Sopenharmony_ci{
2618c2ecf20Sopenharmony_ci	struct hdac_ext_stream *link_dev =
2628c2ecf20Sopenharmony_ci				snd_soc_dai_get_dma_data(dai, substream);
2638c2ecf20Sopenharmony_ci	struct snd_sof_dev *sdev =
2648c2ecf20Sopenharmony_ci				snd_soc_component_get_drvdata(dai->component);
2658c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
2668c2ecf20Sopenharmony_ci	int stream = substream->stream;
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci	if (link_dev->link_prepared)
2698c2ecf20Sopenharmony_ci		return 0;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	dev_dbg(sdev->dev, "hda: prepare stream dir %d\n", substream->stream);
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	return hda_link_hw_params(substream, &rtd->dpcm[stream].hw_params,
2748c2ecf20Sopenharmony_ci				  dai);
2758c2ecf20Sopenharmony_ci}
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_cistatic int hda_link_pcm_trigger(struct snd_pcm_substream *substream,
2788c2ecf20Sopenharmony_ci				int cmd, struct snd_soc_dai *dai)
2798c2ecf20Sopenharmony_ci{
2808c2ecf20Sopenharmony_ci	struct hdac_ext_stream *link_dev =
2818c2ecf20Sopenharmony_ci				snd_soc_dai_get_dma_data(dai, substream);
2828c2ecf20Sopenharmony_ci	struct sof_intel_hda_stream *hda_stream;
2838c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd;
2848c2ecf20Sopenharmony_ci	struct hdac_ext_link *link;
2858c2ecf20Sopenharmony_ci	struct hdac_stream *hstream;
2868c2ecf20Sopenharmony_ci	struct hdac_bus *bus;
2878c2ecf20Sopenharmony_ci	int stream_tag;
2888c2ecf20Sopenharmony_ci	int ret;
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	hstream = substream->runtime->private_data;
2918c2ecf20Sopenharmony_ci	bus = hstream->bus;
2928c2ecf20Sopenharmony_ci	rtd = asoc_substream_to_rtd(substream);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	link = snd_hdac_ext_bus_get_link(bus, asoc_rtd_to_codec(rtd, 0)->component->name);
2958c2ecf20Sopenharmony_ci	if (!link)
2968c2ecf20Sopenharmony_ci		return -EINVAL;
2978c2ecf20Sopenharmony_ci
2988c2ecf20Sopenharmony_ci	hda_stream = hstream_to_sof_hda_stream(link_dev);
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	dev_dbg(dai->dev, "In %s cmd=%d\n", __func__, cmd);
3018c2ecf20Sopenharmony_ci	switch (cmd) {
3028c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_RESUME:
3038c2ecf20Sopenharmony_ci		/* set up hw_params */
3048c2ecf20Sopenharmony_ci		ret = hda_link_pcm_prepare(substream, dai);
3058c2ecf20Sopenharmony_ci		if (ret < 0) {
3068c2ecf20Sopenharmony_ci			dev_err(dai->dev,
3078c2ecf20Sopenharmony_ci				"error: setting up hw_params during resume\n");
3088c2ecf20Sopenharmony_ci			return ret;
3098c2ecf20Sopenharmony_ci		}
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci		fallthrough;
3128c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_START:
3138c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
3148c2ecf20Sopenharmony_ci		snd_hdac_ext_link_stream_start(link_dev);
3158c2ecf20Sopenharmony_ci		break;
3168c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_SUSPEND:
3178c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_STOP:
3188c2ecf20Sopenharmony_ci		/*
3198c2ecf20Sopenharmony_ci		 * clear link DMA channel. It will be assigned when
3208c2ecf20Sopenharmony_ci		 * hw_params is set up again after resume.
3218c2ecf20Sopenharmony_ci		 */
3228c2ecf20Sopenharmony_ci		ret = hda_link_config_ipc(hda_stream, dai->name,
3238c2ecf20Sopenharmony_ci					  DMA_CHAN_INVALID, substream->stream);
3248c2ecf20Sopenharmony_ci		if (ret < 0)
3258c2ecf20Sopenharmony_ci			return ret;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
3288c2ecf20Sopenharmony_ci			stream_tag = hdac_stream(link_dev)->stream_tag;
3298c2ecf20Sopenharmony_ci			snd_hdac_ext_link_clear_stream_id(link, stream_tag);
3308c2ecf20Sopenharmony_ci		}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci		link_dev->link_prepared = 0;
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci		fallthrough;
3358c2ecf20Sopenharmony_ci	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
3368c2ecf20Sopenharmony_ci		snd_hdac_ext_link_stream_clear(link_dev);
3378c2ecf20Sopenharmony_ci		break;
3388c2ecf20Sopenharmony_ci	default:
3398c2ecf20Sopenharmony_ci		return -EINVAL;
3408c2ecf20Sopenharmony_ci	}
3418c2ecf20Sopenharmony_ci	return 0;
3428c2ecf20Sopenharmony_ci}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_cistatic int hda_link_hw_free(struct snd_pcm_substream *substream,
3458c2ecf20Sopenharmony_ci			    struct snd_soc_dai *dai)
3468c2ecf20Sopenharmony_ci{
3478c2ecf20Sopenharmony_ci	unsigned int stream_tag;
3488c2ecf20Sopenharmony_ci	struct sof_intel_hda_stream *hda_stream;
3498c2ecf20Sopenharmony_ci	struct hdac_bus *bus;
3508c2ecf20Sopenharmony_ci	struct hdac_ext_link *link;
3518c2ecf20Sopenharmony_ci	struct hdac_stream *hstream;
3528c2ecf20Sopenharmony_ci	struct snd_soc_pcm_runtime *rtd;
3538c2ecf20Sopenharmony_ci	struct hdac_ext_stream *link_dev;
3548c2ecf20Sopenharmony_ci	int ret;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	hstream = substream->runtime->private_data;
3578c2ecf20Sopenharmony_ci	bus = hstream->bus;
3588c2ecf20Sopenharmony_ci	rtd = asoc_substream_to_rtd(substream);
3598c2ecf20Sopenharmony_ci	link_dev = snd_soc_dai_get_dma_data(dai, substream);
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	if (!link_dev) {
3628c2ecf20Sopenharmony_ci		dev_dbg(dai->dev,
3638c2ecf20Sopenharmony_ci			"%s: link_dev is not assigned\n", __func__);
3648c2ecf20Sopenharmony_ci		return -EINVAL;
3658c2ecf20Sopenharmony_ci	}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	hda_stream = hstream_to_sof_hda_stream(link_dev);
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	/* free the link DMA channel in the FW */
3708c2ecf20Sopenharmony_ci	ret = hda_link_config_ipc(hda_stream, dai->name, DMA_CHAN_INVALID,
3718c2ecf20Sopenharmony_ci				  substream->stream);
3728c2ecf20Sopenharmony_ci	if (ret < 0)
3738c2ecf20Sopenharmony_ci		return ret;
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	link = snd_hdac_ext_bus_get_link(bus, asoc_rtd_to_codec(rtd, 0)->component->name);
3768c2ecf20Sopenharmony_ci	if (!link)
3778c2ecf20Sopenharmony_ci		return -EINVAL;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
3808c2ecf20Sopenharmony_ci		stream_tag = hdac_stream(link_dev)->stream_tag;
3818c2ecf20Sopenharmony_ci		snd_hdac_ext_link_clear_stream_id(link, stream_tag);
3828c2ecf20Sopenharmony_ci	}
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	snd_soc_dai_set_dma_data(dai, substream, NULL);
3858c2ecf20Sopenharmony_ci	snd_hdac_ext_stream_release(link_dev, HDAC_EXT_STREAM_TYPE_LINK);
3868c2ecf20Sopenharmony_ci	link_dev->link_prepared = 0;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	/* free the host DMA channel reserved by hostless streams */
3898c2ecf20Sopenharmony_ci	hda_stream->host_reserved = 0;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	return 0;
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_cistatic const struct snd_soc_dai_ops hda_link_dai_ops = {
3958c2ecf20Sopenharmony_ci	.hw_params = hda_link_hw_params,
3968c2ecf20Sopenharmony_ci	.hw_free = hda_link_hw_free,
3978c2ecf20Sopenharmony_ci	.trigger = hda_link_pcm_trigger,
3988c2ecf20Sopenharmony_ci	.prepare = hda_link_pcm_prepare,
3998c2ecf20Sopenharmony_ci};
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES)
4028c2ecf20Sopenharmony_ci#include "../compress.h"
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_cistatic struct snd_soc_cdai_ops sof_probe_compr_ops = {
4058c2ecf20Sopenharmony_ci	.startup	= sof_probe_compr_open,
4068c2ecf20Sopenharmony_ci	.shutdown	= sof_probe_compr_free,
4078c2ecf20Sopenharmony_ci	.set_params	= sof_probe_compr_set_params,
4088c2ecf20Sopenharmony_ci	.trigger	= sof_probe_compr_trigger,
4098c2ecf20Sopenharmony_ci	.pointer	= sof_probe_compr_pointer,
4108c2ecf20Sopenharmony_ci};
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci#endif
4138c2ecf20Sopenharmony_ci#endif
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci/*
4168c2ecf20Sopenharmony_ci * common dai driver for skl+ platforms.
4178c2ecf20Sopenharmony_ci * some products who use this DAI array only physically have a subset of
4188c2ecf20Sopenharmony_ci * the DAIs, but no harm is done here by adding the whole set.
4198c2ecf20Sopenharmony_ci */
4208c2ecf20Sopenharmony_cistruct snd_soc_dai_driver skl_dai[] = {
4218c2ecf20Sopenharmony_ci{
4228c2ecf20Sopenharmony_ci	.name = "SSP0 Pin",
4238c2ecf20Sopenharmony_ci	.playback = {
4248c2ecf20Sopenharmony_ci		.channels_min = 1,
4258c2ecf20Sopenharmony_ci		.channels_max = 8,
4268c2ecf20Sopenharmony_ci	},
4278c2ecf20Sopenharmony_ci	.capture = {
4288c2ecf20Sopenharmony_ci		.channels_min = 1,
4298c2ecf20Sopenharmony_ci		.channels_max = 8,
4308c2ecf20Sopenharmony_ci	},
4318c2ecf20Sopenharmony_ci},
4328c2ecf20Sopenharmony_ci{
4338c2ecf20Sopenharmony_ci	.name = "SSP1 Pin",
4348c2ecf20Sopenharmony_ci	.playback = {
4358c2ecf20Sopenharmony_ci		.channels_min = 1,
4368c2ecf20Sopenharmony_ci		.channels_max = 8,
4378c2ecf20Sopenharmony_ci	},
4388c2ecf20Sopenharmony_ci	.capture = {
4398c2ecf20Sopenharmony_ci		.channels_min = 1,
4408c2ecf20Sopenharmony_ci		.channels_max = 8,
4418c2ecf20Sopenharmony_ci	},
4428c2ecf20Sopenharmony_ci},
4438c2ecf20Sopenharmony_ci{
4448c2ecf20Sopenharmony_ci	.name = "SSP2 Pin",
4458c2ecf20Sopenharmony_ci	.playback = {
4468c2ecf20Sopenharmony_ci		.channels_min = 1,
4478c2ecf20Sopenharmony_ci		.channels_max = 8,
4488c2ecf20Sopenharmony_ci	},
4498c2ecf20Sopenharmony_ci	.capture = {
4508c2ecf20Sopenharmony_ci		.channels_min = 1,
4518c2ecf20Sopenharmony_ci		.channels_max = 8,
4528c2ecf20Sopenharmony_ci	},
4538c2ecf20Sopenharmony_ci},
4548c2ecf20Sopenharmony_ci{
4558c2ecf20Sopenharmony_ci	.name = "SSP3 Pin",
4568c2ecf20Sopenharmony_ci	.playback = {
4578c2ecf20Sopenharmony_ci		.channels_min = 1,
4588c2ecf20Sopenharmony_ci		.channels_max = 8,
4598c2ecf20Sopenharmony_ci	},
4608c2ecf20Sopenharmony_ci	.capture = {
4618c2ecf20Sopenharmony_ci		.channels_min = 1,
4628c2ecf20Sopenharmony_ci		.channels_max = 8,
4638c2ecf20Sopenharmony_ci	},
4648c2ecf20Sopenharmony_ci},
4658c2ecf20Sopenharmony_ci{
4668c2ecf20Sopenharmony_ci	.name = "SSP4 Pin",
4678c2ecf20Sopenharmony_ci	.playback = {
4688c2ecf20Sopenharmony_ci		.channels_min = 1,
4698c2ecf20Sopenharmony_ci		.channels_max = 8,
4708c2ecf20Sopenharmony_ci	},
4718c2ecf20Sopenharmony_ci	.capture = {
4728c2ecf20Sopenharmony_ci		.channels_min = 1,
4738c2ecf20Sopenharmony_ci		.channels_max = 8,
4748c2ecf20Sopenharmony_ci	},
4758c2ecf20Sopenharmony_ci},
4768c2ecf20Sopenharmony_ci{
4778c2ecf20Sopenharmony_ci	.name = "SSP5 Pin",
4788c2ecf20Sopenharmony_ci	.playback = {
4798c2ecf20Sopenharmony_ci		.channels_min = 1,
4808c2ecf20Sopenharmony_ci		.channels_max = 8,
4818c2ecf20Sopenharmony_ci	},
4828c2ecf20Sopenharmony_ci	.capture = {
4838c2ecf20Sopenharmony_ci		.channels_min = 1,
4848c2ecf20Sopenharmony_ci		.channels_max = 8,
4858c2ecf20Sopenharmony_ci	},
4868c2ecf20Sopenharmony_ci},
4878c2ecf20Sopenharmony_ci{
4888c2ecf20Sopenharmony_ci	.name = "DMIC01 Pin",
4898c2ecf20Sopenharmony_ci	.capture = {
4908c2ecf20Sopenharmony_ci		.channels_min = 1,
4918c2ecf20Sopenharmony_ci		.channels_max = 4,
4928c2ecf20Sopenharmony_ci	},
4938c2ecf20Sopenharmony_ci},
4948c2ecf20Sopenharmony_ci{
4958c2ecf20Sopenharmony_ci	.name = "DMIC16k Pin",
4968c2ecf20Sopenharmony_ci	.capture = {
4978c2ecf20Sopenharmony_ci		.channels_min = 1,
4988c2ecf20Sopenharmony_ci		.channels_max = 4,
4998c2ecf20Sopenharmony_ci	},
5008c2ecf20Sopenharmony_ci},
5018c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
5028c2ecf20Sopenharmony_ci{
5038c2ecf20Sopenharmony_ci	.name = "iDisp1 Pin",
5048c2ecf20Sopenharmony_ci	.ops = &hda_link_dai_ops,
5058c2ecf20Sopenharmony_ci	.playback = {
5068c2ecf20Sopenharmony_ci		.channels_min = 1,
5078c2ecf20Sopenharmony_ci		.channels_max = 8,
5088c2ecf20Sopenharmony_ci	},
5098c2ecf20Sopenharmony_ci},
5108c2ecf20Sopenharmony_ci{
5118c2ecf20Sopenharmony_ci	.name = "iDisp2 Pin",
5128c2ecf20Sopenharmony_ci	.ops = &hda_link_dai_ops,
5138c2ecf20Sopenharmony_ci	.playback = {
5148c2ecf20Sopenharmony_ci		.channels_min = 1,
5158c2ecf20Sopenharmony_ci		.channels_max = 8,
5168c2ecf20Sopenharmony_ci	},
5178c2ecf20Sopenharmony_ci},
5188c2ecf20Sopenharmony_ci{
5198c2ecf20Sopenharmony_ci	.name = "iDisp3 Pin",
5208c2ecf20Sopenharmony_ci	.ops = &hda_link_dai_ops,
5218c2ecf20Sopenharmony_ci	.playback = {
5228c2ecf20Sopenharmony_ci		.channels_min = 1,
5238c2ecf20Sopenharmony_ci		.channels_max = 8,
5248c2ecf20Sopenharmony_ci	},
5258c2ecf20Sopenharmony_ci},
5268c2ecf20Sopenharmony_ci{
5278c2ecf20Sopenharmony_ci	.name = "iDisp4 Pin",
5288c2ecf20Sopenharmony_ci	.ops = &hda_link_dai_ops,
5298c2ecf20Sopenharmony_ci	.playback = {
5308c2ecf20Sopenharmony_ci		.channels_min = 1,
5318c2ecf20Sopenharmony_ci		.channels_max = 8,
5328c2ecf20Sopenharmony_ci	},
5338c2ecf20Sopenharmony_ci},
5348c2ecf20Sopenharmony_ci{
5358c2ecf20Sopenharmony_ci	.name = "Analog CPU DAI",
5368c2ecf20Sopenharmony_ci	.ops = &hda_link_dai_ops,
5378c2ecf20Sopenharmony_ci	.playback = {
5388c2ecf20Sopenharmony_ci		.channels_min = 1,
5398c2ecf20Sopenharmony_ci		.channels_max = 16,
5408c2ecf20Sopenharmony_ci	},
5418c2ecf20Sopenharmony_ci	.capture = {
5428c2ecf20Sopenharmony_ci		.channels_min = 1,
5438c2ecf20Sopenharmony_ci		.channels_max = 16,
5448c2ecf20Sopenharmony_ci	},
5458c2ecf20Sopenharmony_ci},
5468c2ecf20Sopenharmony_ci{
5478c2ecf20Sopenharmony_ci	.name = "Digital CPU DAI",
5488c2ecf20Sopenharmony_ci	.ops = &hda_link_dai_ops,
5498c2ecf20Sopenharmony_ci	.playback = {
5508c2ecf20Sopenharmony_ci		.channels_min = 1,
5518c2ecf20Sopenharmony_ci		.channels_max = 16,
5528c2ecf20Sopenharmony_ci	},
5538c2ecf20Sopenharmony_ci	.capture = {
5548c2ecf20Sopenharmony_ci		.channels_min = 1,
5558c2ecf20Sopenharmony_ci		.channels_max = 16,
5568c2ecf20Sopenharmony_ci	},
5578c2ecf20Sopenharmony_ci},
5588c2ecf20Sopenharmony_ci{
5598c2ecf20Sopenharmony_ci	.name = "Alt Analog CPU DAI",
5608c2ecf20Sopenharmony_ci	.ops = &hda_link_dai_ops,
5618c2ecf20Sopenharmony_ci	.playback = {
5628c2ecf20Sopenharmony_ci		.channels_min = 1,
5638c2ecf20Sopenharmony_ci		.channels_max = 16,
5648c2ecf20Sopenharmony_ci	},
5658c2ecf20Sopenharmony_ci	.capture = {
5668c2ecf20Sopenharmony_ci		.channels_min = 1,
5678c2ecf20Sopenharmony_ci		.channels_max = 16,
5688c2ecf20Sopenharmony_ci	},
5698c2ecf20Sopenharmony_ci},
5708c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES)
5718c2ecf20Sopenharmony_ci{
5728c2ecf20Sopenharmony_ci	.name = "Probe Extraction CPU DAI",
5738c2ecf20Sopenharmony_ci	.compress_new = snd_soc_new_compress,
5748c2ecf20Sopenharmony_ci	.cops = &sof_probe_compr_ops,
5758c2ecf20Sopenharmony_ci	.capture = {
5768c2ecf20Sopenharmony_ci		.stream_name = "Probe Extraction",
5778c2ecf20Sopenharmony_ci		.channels_min = 1,
5788c2ecf20Sopenharmony_ci		.channels_max = 8,
5798c2ecf20Sopenharmony_ci		.rates = SNDRV_PCM_RATE_48000,
5808c2ecf20Sopenharmony_ci		.rate_min = 48000,
5818c2ecf20Sopenharmony_ci		.rate_max = 48000,
5828c2ecf20Sopenharmony_ci	},
5838c2ecf20Sopenharmony_ci},
5848c2ecf20Sopenharmony_ci#endif
5858c2ecf20Sopenharmony_ci#endif
5868c2ecf20Sopenharmony_ci};
587