1fdd961e3SKeyon Jie // SPDX-License-Identifier: (GPL-2.0 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> 13fdd961e3SKeyon Jie #include "../sof-priv.h" 14fdd961e3SKeyon Jie #include "hda.h" 15fdd961e3SKeyon Jie 16fdd961e3SKeyon Jie #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) 17fdd961e3SKeyon Jie 18fdd961e3SKeyon Jie struct hda_pipe_params { 19fdd961e3SKeyon Jie u8 host_dma_id; 20fdd961e3SKeyon Jie u8 link_dma_id; 21fdd961e3SKeyon Jie u32 ch; 22fdd961e3SKeyon Jie u32 s_freq; 23fdd961e3SKeyon Jie u32 s_fmt; 24fdd961e3SKeyon Jie u8 linktype; 25fdd961e3SKeyon Jie snd_pcm_format_t format; 26fdd961e3SKeyon Jie int link_index; 27fdd961e3SKeyon Jie int stream; 28fdd961e3SKeyon Jie unsigned int host_bps; 29fdd961e3SKeyon Jie unsigned int link_bps; 30fdd961e3SKeyon Jie }; 31fdd961e3SKeyon Jie 32fdd961e3SKeyon Jie /* 33bdf4ad3fSRanjani Sridharan * This function checks if the host dma channel corresponding 34bdf4ad3fSRanjani Sridharan * to the link DMA stream_tag argument is assigned to one 35bdf4ad3fSRanjani Sridharan * of the FEs connected to the BE DAI. 36fdd961e3SKeyon Jie */ 37bdf4ad3fSRanjani Sridharan static bool hda_check_fes(struct snd_soc_pcm_runtime *rtd, 38bdf4ad3fSRanjani Sridharan int dir, int stream_tag) 39fdd961e3SKeyon Jie { 40bdf4ad3fSRanjani Sridharan struct snd_pcm_substream *fe_substream; 41bdf4ad3fSRanjani Sridharan struct hdac_stream *fe_hstream; 42bdf4ad3fSRanjani Sridharan struct snd_soc_dpcm *dpcm; 43fdd961e3SKeyon Jie 44bdf4ad3fSRanjani Sridharan for_each_dpcm_fe(rtd, dir, dpcm) { 45bdf4ad3fSRanjani Sridharan fe_substream = snd_soc_dpcm_get_substream(dpcm->fe, dir); 46bdf4ad3fSRanjani Sridharan fe_hstream = fe_substream->runtime->private_data; 47bdf4ad3fSRanjani Sridharan if (fe_hstream->stream_tag == stream_tag) 48bdf4ad3fSRanjani Sridharan return true; 49fdd961e3SKeyon Jie } 50fdd961e3SKeyon Jie 51bdf4ad3fSRanjani Sridharan return false; 52fdd961e3SKeyon Jie } 53fdd961e3SKeyon Jie 54bdf4ad3fSRanjani Sridharan static struct hdac_ext_stream * 55bdf4ad3fSRanjani Sridharan hda_link_stream_assign(struct hdac_bus *bus, 56bdf4ad3fSRanjani Sridharan struct snd_pcm_substream *substream) 57bdf4ad3fSRanjani Sridharan { 58bdf4ad3fSRanjani Sridharan struct snd_soc_pcm_runtime *rtd = substream->private_data; 59bdf4ad3fSRanjani Sridharan struct sof_intel_hda_stream *hda_stream; 60bdf4ad3fSRanjani Sridharan struct hdac_ext_stream *res = NULL; 61bdf4ad3fSRanjani Sridharan struct hdac_stream *stream = NULL; 62bdf4ad3fSRanjani Sridharan 63bdf4ad3fSRanjani Sridharan int stream_dir = substream->stream; 64bdf4ad3fSRanjani Sridharan 65bdf4ad3fSRanjani Sridharan if (!bus->ppcap) { 66bdf4ad3fSRanjani Sridharan dev_err(bus->dev, "stream type not supported\n"); 67bdf4ad3fSRanjani Sridharan return NULL; 68fdd961e3SKeyon Jie } 69fdd961e3SKeyon Jie 70bdf4ad3fSRanjani Sridharan list_for_each_entry(stream, &bus->stream_list, list) { 71bdf4ad3fSRanjani Sridharan struct hdac_ext_stream *hstream = 72bdf4ad3fSRanjani Sridharan stream_to_hdac_ext_stream(stream); 73bdf4ad3fSRanjani Sridharan if (stream->direction != substream->stream) 74bdf4ad3fSRanjani Sridharan continue; 75fdd961e3SKeyon Jie 76bdf4ad3fSRanjani Sridharan hda_stream = hstream_to_sof_hda_stream(hstream); 77bdf4ad3fSRanjani Sridharan 786b2239e3SRanjani Sridharan /* check if link is available */ 79bdf4ad3fSRanjani Sridharan if (!hstream->link_locked) { 80bdf4ad3fSRanjani Sridharan if (stream->opened) { 81bdf4ad3fSRanjani Sridharan /* 82bdf4ad3fSRanjani Sridharan * check if the stream tag matches the stream 83bdf4ad3fSRanjani Sridharan * tag of one of the connected FEs 84bdf4ad3fSRanjani Sridharan */ 85bdf4ad3fSRanjani Sridharan if (hda_check_fes(rtd, stream_dir, 86bdf4ad3fSRanjani Sridharan stream->stream_tag)) { 87bdf4ad3fSRanjani Sridharan res = hstream; 88bdf4ad3fSRanjani Sridharan break; 89bdf4ad3fSRanjani Sridharan } 90bdf4ad3fSRanjani Sridharan } else { 91bdf4ad3fSRanjani Sridharan res = hstream; 926b2239e3SRanjani Sridharan 936b2239e3SRanjani Sridharan /* 946b2239e3SRanjani Sridharan * This must be a hostless stream. 956b2239e3SRanjani Sridharan * So reserve the host DMA channel. 966b2239e3SRanjani Sridharan */ 976b2239e3SRanjani Sridharan hda_stream->host_reserved = 1; 98bdf4ad3fSRanjani Sridharan break; 99bdf4ad3fSRanjani Sridharan } 100bdf4ad3fSRanjani Sridharan } 101fdd961e3SKeyon Jie } 102fdd961e3SKeyon Jie 103bdf4ad3fSRanjani Sridharan if (res) { 104bdf4ad3fSRanjani Sridharan /* 105bdf4ad3fSRanjani Sridharan * Decouple host and link DMA. The decoupled flag 106bdf4ad3fSRanjani Sridharan * is updated in snd_hdac_ext_stream_decouple(). 107bdf4ad3fSRanjani Sridharan */ 108bdf4ad3fSRanjani Sridharan if (!res->decoupled) 109bdf4ad3fSRanjani Sridharan snd_hdac_ext_stream_decouple(bus, res, true); 110bdf4ad3fSRanjani Sridharan spin_lock_irq(&bus->reg_lock); 111bdf4ad3fSRanjani Sridharan res->link_locked = 1; 112bdf4ad3fSRanjani Sridharan res->link_substream = substream; 113bdf4ad3fSRanjani Sridharan spin_unlock_irq(&bus->reg_lock); 114bdf4ad3fSRanjani Sridharan } 115bdf4ad3fSRanjani Sridharan 116bdf4ad3fSRanjani Sridharan return res; 117fdd961e3SKeyon Jie } 118fdd961e3SKeyon Jie 119fdd961e3SKeyon Jie static int hda_link_dma_params(struct hdac_ext_stream *stream, 120fdd961e3SKeyon Jie struct hda_pipe_params *params) 121fdd961e3SKeyon Jie { 122fdd961e3SKeyon Jie struct hdac_stream *hstream = &stream->hstream; 123fdd961e3SKeyon Jie unsigned char stream_tag = hstream->stream_tag; 124fdd961e3SKeyon Jie struct hdac_bus *bus = hstream->bus; 125fdd961e3SKeyon Jie struct hdac_ext_link *link; 126fdd961e3SKeyon Jie unsigned int format_val; 127fdd961e3SKeyon Jie 128fdd961e3SKeyon Jie snd_hdac_ext_stream_decouple(bus, stream, true); 129fdd961e3SKeyon Jie snd_hdac_ext_link_stream_reset(stream); 130fdd961e3SKeyon Jie 131fdd961e3SKeyon Jie format_val = snd_hdac_calc_stream_format(params->s_freq, params->ch, 132fdd961e3SKeyon Jie params->format, 133fdd961e3SKeyon Jie params->link_bps, 0); 134fdd961e3SKeyon Jie 135fdd961e3SKeyon Jie dev_dbg(bus->dev, "format_val=%d, rate=%d, ch=%d, format=%d\n", 136fdd961e3SKeyon Jie format_val, params->s_freq, params->ch, params->format); 137fdd961e3SKeyon Jie 138fdd961e3SKeyon Jie snd_hdac_ext_link_stream_setup(stream, format_val); 139fdd961e3SKeyon Jie 140fdd961e3SKeyon Jie if (stream->hstream.direction == SNDRV_PCM_STREAM_PLAYBACK) { 141fdd961e3SKeyon Jie list_for_each_entry(link, &bus->hlink_list, list) { 142fdd961e3SKeyon Jie if (link->index == params->link_index) 143fdd961e3SKeyon Jie snd_hdac_ext_link_set_stream_id(link, 144fdd961e3SKeyon Jie stream_tag); 145fdd961e3SKeyon Jie } 146fdd961e3SKeyon Jie } 147fdd961e3SKeyon Jie 148fdd961e3SKeyon Jie stream->link_prepared = 1; 149fdd961e3SKeyon Jie 150fdd961e3SKeyon Jie return 0; 151fdd961e3SKeyon Jie } 152fdd961e3SKeyon Jie 153bdf4ad3fSRanjani Sridharan /* Send DAI_CONFIG IPC to the DAI that matches the dai_name and direction */ 154bdf4ad3fSRanjani Sridharan static int hda_link_config_ipc(struct sof_intel_hda_stream *hda_stream, 155bdf4ad3fSRanjani Sridharan const char *dai_name, int channel, int dir) 156bdf4ad3fSRanjani Sridharan { 157bdf4ad3fSRanjani Sridharan struct sof_ipc_dai_config *config; 158bdf4ad3fSRanjani Sridharan struct snd_sof_dai *sof_dai; 159bdf4ad3fSRanjani Sridharan struct sof_ipc_reply reply; 160bdf4ad3fSRanjani Sridharan int ret = 0; 161bdf4ad3fSRanjani Sridharan 162bdf4ad3fSRanjani Sridharan list_for_each_entry(sof_dai, &hda_stream->sdev->dai_list, list) { 163bdf4ad3fSRanjani Sridharan if (!sof_dai->cpu_dai_name) 164bdf4ad3fSRanjani Sridharan continue; 165bdf4ad3fSRanjani Sridharan 166bdf4ad3fSRanjani Sridharan if (!strcmp(dai_name, sof_dai->cpu_dai_name) && 167bdf4ad3fSRanjani Sridharan dir == sof_dai->comp_dai.direction) { 168bdf4ad3fSRanjani Sridharan config = sof_dai->dai_config; 169bdf4ad3fSRanjani Sridharan 170bdf4ad3fSRanjani Sridharan if (!config) { 171bdf4ad3fSRanjani Sridharan dev_err(hda_stream->sdev->dev, 172bdf4ad3fSRanjani Sridharan "error: no config for DAI %s\n", 173bdf4ad3fSRanjani Sridharan sof_dai->name); 174bdf4ad3fSRanjani Sridharan return -EINVAL; 175bdf4ad3fSRanjani Sridharan } 176bdf4ad3fSRanjani Sridharan 177bdf4ad3fSRanjani Sridharan /* update config with stream tag */ 178bdf4ad3fSRanjani Sridharan config->hda.link_dma_ch = channel; 179bdf4ad3fSRanjani Sridharan 180bdf4ad3fSRanjani Sridharan /* send IPC */ 181bdf4ad3fSRanjani Sridharan ret = sof_ipc_tx_message(hda_stream->sdev->ipc, 182bdf4ad3fSRanjani Sridharan config->hdr.cmd, 183bdf4ad3fSRanjani Sridharan config, 184bdf4ad3fSRanjani Sridharan config->hdr.size, 185bdf4ad3fSRanjani Sridharan &reply, sizeof(reply)); 186bdf4ad3fSRanjani Sridharan 187bdf4ad3fSRanjani Sridharan if (ret < 0) 188bdf4ad3fSRanjani Sridharan dev_err(hda_stream->sdev->dev, 189bdf4ad3fSRanjani Sridharan "error: failed to set dai config for %s\n", 190bdf4ad3fSRanjani Sridharan sof_dai->name); 191bdf4ad3fSRanjani Sridharan return ret; 192bdf4ad3fSRanjani Sridharan } 193bdf4ad3fSRanjani Sridharan } 194bdf4ad3fSRanjani Sridharan 195bdf4ad3fSRanjani Sridharan return -EINVAL; 196bdf4ad3fSRanjani Sridharan } 197bdf4ad3fSRanjani Sridharan 198fdd961e3SKeyon Jie static int hda_link_hw_params(struct snd_pcm_substream *substream, 199fdd961e3SKeyon Jie struct snd_pcm_hw_params *params, 200fdd961e3SKeyon Jie struct snd_soc_dai *dai) 201fdd961e3SKeyon Jie { 202fdd961e3SKeyon Jie struct hdac_stream *hstream = substream->runtime->private_data; 203fdd961e3SKeyon Jie struct hdac_bus *bus = hstream->bus; 204fdd961e3SKeyon Jie struct hdac_ext_stream *link_dev; 205fdd961e3SKeyon Jie struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); 206fdd961e3SKeyon Jie struct snd_soc_dai *codec_dai = rtd->codec_dai; 207ed3baacdSRanjani Sridharan struct sof_intel_hda_stream *hda_stream; 208fdd961e3SKeyon Jie struct hda_pipe_params p_params = {0}; 209fdd961e3SKeyon Jie struct hdac_ext_link *link; 210fdd961e3SKeyon Jie int stream_tag; 211bdf4ad3fSRanjani Sridharan int ret; 212fdd961e3SKeyon Jie 213934bf822SRander Wang /* get stored dma data if resuming from system suspend */ 214934bf822SRander Wang link_dev = snd_soc_dai_get_dma_data(dai, substream); 215934bf822SRander Wang if (!link_dev) { 216bdf4ad3fSRanjani Sridharan link_dev = hda_link_stream_assign(bus, substream); 217bdf4ad3fSRanjani Sridharan if (!link_dev) 218bdf4ad3fSRanjani Sridharan return -EBUSY; 219934bf822SRander Wang } 220fdd961e3SKeyon Jie 221bdf4ad3fSRanjani Sridharan stream_tag = hdac_stream(link_dev)->stream_tag; 222bdf4ad3fSRanjani Sridharan 223bdf4ad3fSRanjani Sridharan hda_stream = hstream_to_sof_hda_stream(link_dev); 224bdf4ad3fSRanjani Sridharan 225bdf4ad3fSRanjani Sridharan /* update the DSP with the new tag */ 226bdf4ad3fSRanjani Sridharan ret = hda_link_config_ipc(hda_stream, dai->name, stream_tag - 1, 227bdf4ad3fSRanjani Sridharan substream->stream); 228bdf4ad3fSRanjani Sridharan if (ret < 0) 229bdf4ad3fSRanjani Sridharan return ret; 230bdf4ad3fSRanjani Sridharan 231bdf4ad3fSRanjani Sridharan snd_soc_dai_set_dma_data(dai, substream, (void *)link_dev); 232bdf4ad3fSRanjani Sridharan 233fdd961e3SKeyon Jie link = snd_hdac_ext_bus_get_link(bus, codec_dai->component->name); 234fdd961e3SKeyon Jie if (!link) 235fdd961e3SKeyon Jie return -EINVAL; 236fdd961e3SKeyon Jie 237fdd961e3SKeyon Jie /* set the stream tag in the codec dai dma params */ 238fdd961e3SKeyon Jie if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 239fdd961e3SKeyon Jie snd_soc_dai_set_tdm_slot(codec_dai, stream_tag, 0, 0, 0); 240fdd961e3SKeyon Jie else 241fdd961e3SKeyon Jie snd_soc_dai_set_tdm_slot(codec_dai, 0, stream_tag, 0, 0); 242fdd961e3SKeyon Jie 243fdd961e3SKeyon Jie p_params.s_fmt = snd_pcm_format_width(params_format(params)); 244fdd961e3SKeyon Jie p_params.ch = params_channels(params); 245fdd961e3SKeyon Jie p_params.s_freq = params_rate(params); 246fdd961e3SKeyon Jie p_params.stream = substream->stream; 247fdd961e3SKeyon Jie p_params.link_dma_id = stream_tag - 1; 248fdd961e3SKeyon Jie p_params.link_index = link->index; 249fdd961e3SKeyon Jie p_params.format = params_format(params); 250fdd961e3SKeyon Jie 251fdd961e3SKeyon Jie if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 252fdd961e3SKeyon Jie p_params.link_bps = codec_dai->driver->playback.sig_bits; 253fdd961e3SKeyon Jie else 254fdd961e3SKeyon Jie p_params.link_bps = codec_dai->driver->capture.sig_bits; 255fdd961e3SKeyon Jie 256fdd961e3SKeyon Jie return hda_link_dma_params(link_dev, &p_params); 257fdd961e3SKeyon Jie } 258fdd961e3SKeyon Jie 259fdd961e3SKeyon Jie static int hda_link_pcm_prepare(struct snd_pcm_substream *substream, 260fdd961e3SKeyon Jie struct snd_soc_dai *dai) 261fdd961e3SKeyon Jie { 262ed3baacdSRanjani Sridharan struct hdac_ext_stream *link_dev = 263ed3baacdSRanjani Sridharan snd_soc_dai_get_dma_data(dai, substream); 264ed3baacdSRanjani Sridharan struct sof_intel_hda_stream *hda_stream; 265fdd961e3SKeyon Jie struct snd_sof_dev *sdev = 266fdd961e3SKeyon Jie snd_soc_component_get_drvdata(dai->component); 267ed3baacdSRanjani Sridharan struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream); 268fdd961e3SKeyon Jie int stream = substream->stream; 269fdd961e3SKeyon Jie 270bdf4ad3fSRanjani Sridharan hda_stream = hstream_to_sof_hda_stream(link_dev); 271fdd961e3SKeyon Jie 272a3ebccb5SKai Vehmanen if (link_dev->link_prepared) 273fdd961e3SKeyon Jie return 0; 274fdd961e3SKeyon Jie 275ed3baacdSRanjani Sridharan dev_dbg(sdev->dev, "hda: prepare stream dir %d\n", substream->stream); 276fdd961e3SKeyon Jie 277fdd961e3SKeyon Jie return hda_link_hw_params(substream, &rtd->dpcm[stream].hw_params, 278fdd961e3SKeyon Jie dai); 279fdd961e3SKeyon Jie } 280fdd961e3SKeyon Jie 281fdd961e3SKeyon Jie static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, 282fdd961e3SKeyon Jie int cmd, struct snd_soc_dai *dai) 283fdd961e3SKeyon Jie { 284fdd961e3SKeyon Jie struct hdac_ext_stream *link_dev = 285fdd961e3SKeyon Jie snd_soc_dai_get_dma_data(dai, substream); 286bdf4ad3fSRanjani Sridharan struct sof_intel_hda_stream *hda_stream; 287bdf4ad3fSRanjani Sridharan struct snd_soc_pcm_runtime *rtd; 288bdf4ad3fSRanjani Sridharan struct hdac_ext_link *link; 289bdf4ad3fSRanjani Sridharan struct hdac_stream *hstream; 290bdf4ad3fSRanjani Sridharan struct hdac_bus *bus; 291bdf4ad3fSRanjani Sridharan int stream_tag; 292fdd961e3SKeyon Jie int ret; 293fdd961e3SKeyon Jie 294bdf4ad3fSRanjani Sridharan hstream = substream->runtime->private_data; 295bdf4ad3fSRanjani Sridharan bus = hstream->bus; 296bdf4ad3fSRanjani Sridharan rtd = snd_pcm_substream_chip(substream); 297bdf4ad3fSRanjani Sridharan 298bdf4ad3fSRanjani Sridharan link = snd_hdac_ext_bus_get_link(bus, rtd->codec_dai->component->name); 299bdf4ad3fSRanjani Sridharan if (!link) 300bdf4ad3fSRanjani Sridharan return -EINVAL; 301bdf4ad3fSRanjani Sridharan 302bdf4ad3fSRanjani Sridharan hda_stream = hstream_to_sof_hda_stream(link_dev); 303bdf4ad3fSRanjani Sridharan 304fdd961e3SKeyon Jie dev_dbg(dai->dev, "In %s cmd=%d\n", __func__, cmd); 305fdd961e3SKeyon Jie switch (cmd) { 306fdd961e3SKeyon Jie case SNDRV_PCM_TRIGGER_RESUME: 307fdd961e3SKeyon Jie /* set up hw_params */ 308fdd961e3SKeyon Jie ret = hda_link_pcm_prepare(substream, dai); 309fdd961e3SKeyon Jie if (ret < 0) { 310fdd961e3SKeyon Jie dev_err(dai->dev, 311fdd961e3SKeyon Jie "error: setting up hw_params during resume\n"); 312fdd961e3SKeyon Jie return ret; 313fdd961e3SKeyon Jie } 314fdd961e3SKeyon Jie 315fdd961e3SKeyon Jie /* fallthrough */ 316fdd961e3SKeyon Jie case SNDRV_PCM_TRIGGER_START: 317fdd961e3SKeyon Jie case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 318fdd961e3SKeyon Jie snd_hdac_ext_link_stream_start(link_dev); 319fdd961e3SKeyon Jie break; 320fdd961e3SKeyon Jie case SNDRV_PCM_TRIGGER_SUSPEND: 321a3ebccb5SKai Vehmanen case SNDRV_PCM_TRIGGER_STOP: 322bdf4ad3fSRanjani Sridharan /* 323934bf822SRander Wang * clear link DMA channel. It will be assigned when 324bdf4ad3fSRanjani Sridharan * hw_params is set up again after resume. 325bdf4ad3fSRanjani Sridharan */ 326bdf4ad3fSRanjani Sridharan ret = hda_link_config_ipc(hda_stream, dai->name, 327bdf4ad3fSRanjani Sridharan DMA_CHAN_INVALID, substream->stream); 328bdf4ad3fSRanjani Sridharan if (ret < 0) 329bdf4ad3fSRanjani Sridharan return ret; 330*810dbea3SRander Wang 331*810dbea3SRander Wang if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 332bdf4ad3fSRanjani Sridharan stream_tag = hdac_stream(link_dev)->stream_tag; 333bdf4ad3fSRanjani Sridharan snd_hdac_ext_link_clear_stream_id(link, stream_tag); 334*810dbea3SRander Wang } 335*810dbea3SRander Wang 336a3ebccb5SKai Vehmanen link_dev->link_prepared = 0; 337bdf4ad3fSRanjani Sridharan 338bdf4ad3fSRanjani Sridharan /* fallthrough */ 339bdf4ad3fSRanjani Sridharan case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 340fdd961e3SKeyon Jie snd_hdac_ext_link_stream_clear(link_dev); 341fdd961e3SKeyon Jie break; 342fdd961e3SKeyon Jie default: 343fdd961e3SKeyon Jie return -EINVAL; 344fdd961e3SKeyon Jie } 345fdd961e3SKeyon Jie return 0; 346fdd961e3SKeyon Jie } 347fdd961e3SKeyon Jie 348fdd961e3SKeyon Jie static int hda_link_hw_free(struct snd_pcm_substream *substream, 349fdd961e3SKeyon Jie struct snd_soc_dai *dai) 350fdd961e3SKeyon Jie { 351fdd961e3SKeyon Jie unsigned int stream_tag; 352bdf4ad3fSRanjani Sridharan struct sof_intel_hda_stream *hda_stream; 353fdd961e3SKeyon Jie struct hdac_bus *bus; 354fdd961e3SKeyon Jie struct hdac_ext_link *link; 355fdd961e3SKeyon Jie struct hdac_stream *hstream; 356fdd961e3SKeyon Jie struct snd_soc_pcm_runtime *rtd; 357fdd961e3SKeyon Jie struct hdac_ext_stream *link_dev; 358bdf4ad3fSRanjani Sridharan int ret; 359fdd961e3SKeyon Jie 360fdd961e3SKeyon Jie hstream = substream->runtime->private_data; 361fdd961e3SKeyon Jie bus = hstream->bus; 362fdd961e3SKeyon Jie rtd = snd_pcm_substream_chip(substream); 363fdd961e3SKeyon Jie link_dev = snd_soc_dai_get_dma_data(dai, substream); 364bdf4ad3fSRanjani Sridharan hda_stream = hstream_to_sof_hda_stream(link_dev); 365bdf4ad3fSRanjani Sridharan 366bdf4ad3fSRanjani Sridharan /* free the link DMA channel in the FW */ 367bdf4ad3fSRanjani Sridharan ret = hda_link_config_ipc(hda_stream, dai->name, DMA_CHAN_INVALID, 368bdf4ad3fSRanjani Sridharan substream->stream); 369bdf4ad3fSRanjani Sridharan if (ret < 0) 370bdf4ad3fSRanjani Sridharan return ret; 371bdf4ad3fSRanjani Sridharan 372bdf4ad3fSRanjani Sridharan link = snd_hdac_ext_bus_get_link(bus, rtd->codec_dai->component->name); 373fdd961e3SKeyon Jie if (!link) 374fdd961e3SKeyon Jie return -EINVAL; 375fdd961e3SKeyon Jie 376*810dbea3SRander Wang if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 377fdd961e3SKeyon Jie stream_tag = hdac_stream(link_dev)->stream_tag; 378fdd961e3SKeyon Jie snd_hdac_ext_link_clear_stream_id(link, stream_tag); 379*810dbea3SRander Wang } 380*810dbea3SRander Wang 381934bf822SRander Wang snd_soc_dai_set_dma_data(dai, substream, NULL); 382bdf4ad3fSRanjani Sridharan snd_hdac_ext_stream_release(link_dev, HDAC_EXT_STREAM_TYPE_LINK); 383fdd961e3SKeyon Jie link_dev->link_prepared = 0; 384fdd961e3SKeyon Jie 3856b2239e3SRanjani Sridharan /* free the host DMA channel reserved by hostless streams */ 3866b2239e3SRanjani Sridharan hda_stream->host_reserved = 0; 3876b2239e3SRanjani Sridharan 388fdd961e3SKeyon Jie return 0; 389fdd961e3SKeyon Jie } 390fdd961e3SKeyon Jie 391fdd961e3SKeyon Jie static const struct snd_soc_dai_ops hda_link_dai_ops = { 392fdd961e3SKeyon Jie .hw_params = hda_link_hw_params, 393fdd961e3SKeyon Jie .hw_free = hda_link_hw_free, 394fdd961e3SKeyon Jie .trigger = hda_link_pcm_trigger, 395fdd961e3SKeyon Jie .prepare = hda_link_pcm_prepare, 396fdd961e3SKeyon Jie }; 397fdd961e3SKeyon Jie #endif 398fdd961e3SKeyon Jie 399fdd961e3SKeyon Jie /* 400fdd961e3SKeyon Jie * common dai driver for skl+ platforms. 401fdd961e3SKeyon Jie * some products who use this DAI array only physically have a subset of 402fdd961e3SKeyon Jie * the DAIs, but no harm is done here by adding the whole set. 403fdd961e3SKeyon Jie */ 404fdd961e3SKeyon Jie struct snd_soc_dai_driver skl_dai[] = { 405fdd961e3SKeyon Jie { 406fdd961e3SKeyon Jie .name = "SSP0 Pin", 407fdd961e3SKeyon Jie }, 408fdd961e3SKeyon Jie { 409fdd961e3SKeyon Jie .name = "SSP1 Pin", 410fdd961e3SKeyon Jie }, 411fdd961e3SKeyon Jie { 412fdd961e3SKeyon Jie .name = "SSP2 Pin", 413fdd961e3SKeyon Jie }, 414fdd961e3SKeyon Jie { 415fdd961e3SKeyon Jie .name = "SSP3 Pin", 416fdd961e3SKeyon Jie }, 417fdd961e3SKeyon Jie { 418fdd961e3SKeyon Jie .name = "SSP4 Pin", 419fdd961e3SKeyon Jie }, 420fdd961e3SKeyon Jie { 421fdd961e3SKeyon Jie .name = "SSP5 Pin", 422fdd961e3SKeyon Jie }, 423fdd961e3SKeyon Jie { 424fdd961e3SKeyon Jie .name = "DMIC01 Pin", 425fdd961e3SKeyon Jie }, 426fdd961e3SKeyon Jie { 427fdd961e3SKeyon Jie .name = "DMIC16k Pin", 428fdd961e3SKeyon Jie }, 429fdd961e3SKeyon Jie #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) 430fdd961e3SKeyon Jie { 431fdd961e3SKeyon Jie .name = "iDisp1 Pin", 432fdd961e3SKeyon Jie .ops = &hda_link_dai_ops, 433fdd961e3SKeyon Jie }, 434fdd961e3SKeyon Jie { 435fdd961e3SKeyon Jie .name = "iDisp2 Pin", 436fdd961e3SKeyon Jie .ops = &hda_link_dai_ops, 437fdd961e3SKeyon Jie }, 438fdd961e3SKeyon Jie { 439fdd961e3SKeyon Jie .name = "iDisp3 Pin", 440fdd961e3SKeyon Jie .ops = &hda_link_dai_ops, 441fdd961e3SKeyon Jie }, 442fdd961e3SKeyon Jie { 443fdd961e3SKeyon Jie .name = "Analog CPU DAI", 444fdd961e3SKeyon Jie .ops = &hda_link_dai_ops, 445fdd961e3SKeyon Jie }, 446fdd961e3SKeyon Jie { 447fdd961e3SKeyon Jie .name = "Digital CPU DAI", 448fdd961e3SKeyon Jie .ops = &hda_link_dai_ops, 449fdd961e3SKeyon Jie }, 450fdd961e3SKeyon Jie { 451fdd961e3SKeyon Jie .name = "Alt Analog CPU DAI", 452fdd961e3SKeyon Jie .ops = &hda_link_dai_ops, 453fdd961e3SKeyon Jie }, 454fdd961e3SKeyon Jie #endif 455fdd961e3SKeyon Jie }; 456