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> 13fdd961e3SKeyon Jie #include "../sof-priv.h" 14ee1e79b7SRanjani Sridharan #include "../sof-audio.h" 15fdd961e3SKeyon Jie #include "hda.h" 16fdd961e3SKeyon Jie 17fdd961e3SKeyon Jie #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) 18fdd961e3SKeyon Jie 19fdd961e3SKeyon Jie struct hda_pipe_params { 20fdd961e3SKeyon Jie u8 host_dma_id; 21fdd961e3SKeyon Jie u8 link_dma_id; 22fdd961e3SKeyon Jie u32 ch; 23fdd961e3SKeyon Jie u32 s_freq; 24fdd961e3SKeyon Jie u32 s_fmt; 25fdd961e3SKeyon Jie u8 linktype; 26fdd961e3SKeyon Jie snd_pcm_format_t format; 27fdd961e3SKeyon Jie int link_index; 28fdd961e3SKeyon Jie int stream; 29fdd961e3SKeyon Jie unsigned int host_bps; 30fdd961e3SKeyon Jie unsigned int link_bps; 31fdd961e3SKeyon Jie }; 32fdd961e3SKeyon Jie 33fdd961e3SKeyon Jie /* 34bdf4ad3fSRanjani Sridharan * This function checks if the host dma channel corresponding 35bdf4ad3fSRanjani Sridharan * to the link DMA stream_tag argument is assigned to one 36bdf4ad3fSRanjani Sridharan * of the FEs connected to the BE DAI. 37fdd961e3SKeyon Jie */ 38bdf4ad3fSRanjani Sridharan static bool hda_check_fes(struct snd_soc_pcm_runtime *rtd, 39bdf4ad3fSRanjani Sridharan int dir, int stream_tag) 40fdd961e3SKeyon Jie { 41bdf4ad3fSRanjani Sridharan struct snd_pcm_substream *fe_substream; 42bdf4ad3fSRanjani Sridharan struct hdac_stream *fe_hstream; 43bdf4ad3fSRanjani Sridharan struct snd_soc_dpcm *dpcm; 44fdd961e3SKeyon Jie 45bdf4ad3fSRanjani Sridharan for_each_dpcm_fe(rtd, dir, dpcm) { 46bdf4ad3fSRanjani Sridharan fe_substream = snd_soc_dpcm_get_substream(dpcm->fe, dir); 47bdf4ad3fSRanjani Sridharan fe_hstream = fe_substream->runtime->private_data; 48bdf4ad3fSRanjani Sridharan if (fe_hstream->stream_tag == stream_tag) 49bdf4ad3fSRanjani Sridharan return true; 50fdd961e3SKeyon Jie } 51fdd961e3SKeyon Jie 52bdf4ad3fSRanjani Sridharan return false; 53fdd961e3SKeyon Jie } 54fdd961e3SKeyon Jie 55bdf4ad3fSRanjani Sridharan static struct hdac_ext_stream * 56bdf4ad3fSRanjani Sridharan hda_link_stream_assign(struct hdac_bus *bus, 57bdf4ad3fSRanjani Sridharan struct snd_pcm_substream *substream) 58bdf4ad3fSRanjani Sridharan { 59*1205300aSKuninori Morimoto struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 60bdf4ad3fSRanjani Sridharan struct sof_intel_hda_stream *hda_stream; 61bdf4ad3fSRanjani Sridharan struct hdac_ext_stream *res = NULL; 62bdf4ad3fSRanjani Sridharan struct hdac_stream *stream = NULL; 63bdf4ad3fSRanjani Sridharan 64bdf4ad3fSRanjani Sridharan int stream_dir = substream->stream; 65bdf4ad3fSRanjani Sridharan 66bdf4ad3fSRanjani Sridharan if (!bus->ppcap) { 67bdf4ad3fSRanjani Sridharan dev_err(bus->dev, "stream type not supported\n"); 68bdf4ad3fSRanjani Sridharan return NULL; 69fdd961e3SKeyon Jie } 70fdd961e3SKeyon Jie 71bdf4ad3fSRanjani Sridharan list_for_each_entry(stream, &bus->stream_list, list) { 72bdf4ad3fSRanjani Sridharan struct hdac_ext_stream *hstream = 73bdf4ad3fSRanjani Sridharan stream_to_hdac_ext_stream(stream); 74bdf4ad3fSRanjani Sridharan if (stream->direction != substream->stream) 75bdf4ad3fSRanjani Sridharan continue; 76fdd961e3SKeyon Jie 77bdf4ad3fSRanjani Sridharan hda_stream = hstream_to_sof_hda_stream(hstream); 78bdf4ad3fSRanjani Sridharan 796b2239e3SRanjani Sridharan /* check if link is available */ 80bdf4ad3fSRanjani Sridharan if (!hstream->link_locked) { 81bdf4ad3fSRanjani Sridharan if (stream->opened) { 82bdf4ad3fSRanjani Sridharan /* 83bdf4ad3fSRanjani Sridharan * check if the stream tag matches the stream 84bdf4ad3fSRanjani Sridharan * tag of one of the connected FEs 85bdf4ad3fSRanjani Sridharan */ 86bdf4ad3fSRanjani Sridharan if (hda_check_fes(rtd, stream_dir, 87bdf4ad3fSRanjani Sridharan stream->stream_tag)) { 88bdf4ad3fSRanjani Sridharan res = hstream; 89bdf4ad3fSRanjani Sridharan break; 90bdf4ad3fSRanjani Sridharan } 91bdf4ad3fSRanjani Sridharan } else { 92bdf4ad3fSRanjani Sridharan res = hstream; 936b2239e3SRanjani Sridharan 946b2239e3SRanjani Sridharan /* 956b2239e3SRanjani Sridharan * This must be a hostless stream. 966b2239e3SRanjani Sridharan * So reserve the host DMA channel. 976b2239e3SRanjani Sridharan */ 986b2239e3SRanjani Sridharan hda_stream->host_reserved = 1; 99bdf4ad3fSRanjani Sridharan break; 100bdf4ad3fSRanjani Sridharan } 101bdf4ad3fSRanjani Sridharan } 102fdd961e3SKeyon Jie } 103fdd961e3SKeyon Jie 104bdf4ad3fSRanjani Sridharan if (res) { 105bdf4ad3fSRanjani Sridharan /* 106bdf4ad3fSRanjani Sridharan * Decouple host and link DMA. The decoupled flag 107bdf4ad3fSRanjani Sridharan * is updated in snd_hdac_ext_stream_decouple(). 108bdf4ad3fSRanjani Sridharan */ 109bdf4ad3fSRanjani Sridharan if (!res->decoupled) 110bdf4ad3fSRanjani Sridharan snd_hdac_ext_stream_decouple(bus, res, true); 111bdf4ad3fSRanjani Sridharan spin_lock_irq(&bus->reg_lock); 112bdf4ad3fSRanjani Sridharan res->link_locked = 1; 113bdf4ad3fSRanjani Sridharan res->link_substream = substream; 114bdf4ad3fSRanjani Sridharan spin_unlock_irq(&bus->reg_lock); 115bdf4ad3fSRanjani Sridharan } 116bdf4ad3fSRanjani Sridharan 117bdf4ad3fSRanjani Sridharan return res; 118fdd961e3SKeyon Jie } 119fdd961e3SKeyon Jie 120fdd961e3SKeyon Jie static int hda_link_dma_params(struct hdac_ext_stream *stream, 121fdd961e3SKeyon Jie struct hda_pipe_params *params) 122fdd961e3SKeyon Jie { 123fdd961e3SKeyon Jie struct hdac_stream *hstream = &stream->hstream; 124fdd961e3SKeyon Jie unsigned char stream_tag = hstream->stream_tag; 125fdd961e3SKeyon Jie struct hdac_bus *bus = hstream->bus; 126fdd961e3SKeyon Jie struct hdac_ext_link *link; 127fdd961e3SKeyon Jie unsigned int format_val; 128fdd961e3SKeyon Jie 129fdd961e3SKeyon Jie snd_hdac_ext_stream_decouple(bus, stream, true); 130fdd961e3SKeyon Jie snd_hdac_ext_link_stream_reset(stream); 131fdd961e3SKeyon Jie 132fdd961e3SKeyon Jie format_val = snd_hdac_calc_stream_format(params->s_freq, params->ch, 133fdd961e3SKeyon Jie params->format, 134fdd961e3SKeyon Jie params->link_bps, 0); 135fdd961e3SKeyon Jie 136fdd961e3SKeyon Jie dev_dbg(bus->dev, "format_val=%d, rate=%d, ch=%d, format=%d\n", 137fdd961e3SKeyon Jie format_val, params->s_freq, params->ch, params->format); 138fdd961e3SKeyon Jie 139fdd961e3SKeyon Jie snd_hdac_ext_link_stream_setup(stream, format_val); 140fdd961e3SKeyon Jie 141fdd961e3SKeyon Jie if (stream->hstream.direction == SNDRV_PCM_STREAM_PLAYBACK) { 142fdd961e3SKeyon Jie list_for_each_entry(link, &bus->hlink_list, list) { 143fdd961e3SKeyon Jie if (link->index == params->link_index) 144fdd961e3SKeyon Jie snd_hdac_ext_link_set_stream_id(link, 145fdd961e3SKeyon Jie stream_tag); 146fdd961e3SKeyon Jie } 147fdd961e3SKeyon Jie } 148fdd961e3SKeyon Jie 149fdd961e3SKeyon Jie stream->link_prepared = 1; 150fdd961e3SKeyon Jie 151fdd961e3SKeyon Jie return 0; 152fdd961e3SKeyon Jie } 153fdd961e3SKeyon Jie 154bdf4ad3fSRanjani Sridharan /* Send DAI_CONFIG IPC to the DAI that matches the dai_name and direction */ 155bdf4ad3fSRanjani Sridharan static int hda_link_config_ipc(struct sof_intel_hda_stream *hda_stream, 156bdf4ad3fSRanjani Sridharan const char *dai_name, int channel, int dir) 157bdf4ad3fSRanjani Sridharan { 158bdf4ad3fSRanjani Sridharan struct sof_ipc_dai_config *config; 159bdf4ad3fSRanjani Sridharan struct snd_sof_dai *sof_dai; 160bdf4ad3fSRanjani Sridharan struct sof_ipc_reply reply; 161bdf4ad3fSRanjani Sridharan int ret = 0; 162bdf4ad3fSRanjani Sridharan 163bdf4ad3fSRanjani Sridharan list_for_each_entry(sof_dai, &hda_stream->sdev->dai_list, list) { 164bdf4ad3fSRanjani Sridharan if (!sof_dai->cpu_dai_name) 165bdf4ad3fSRanjani Sridharan continue; 166bdf4ad3fSRanjani Sridharan 167bdf4ad3fSRanjani Sridharan if (!strcmp(dai_name, sof_dai->cpu_dai_name) && 168bdf4ad3fSRanjani Sridharan dir == sof_dai->comp_dai.direction) { 169bdf4ad3fSRanjani Sridharan config = sof_dai->dai_config; 170bdf4ad3fSRanjani Sridharan 171bdf4ad3fSRanjani Sridharan if (!config) { 172bdf4ad3fSRanjani Sridharan dev_err(hda_stream->sdev->dev, 173bdf4ad3fSRanjani Sridharan "error: no config for DAI %s\n", 174bdf4ad3fSRanjani Sridharan sof_dai->name); 175bdf4ad3fSRanjani Sridharan return -EINVAL; 176bdf4ad3fSRanjani Sridharan } 177bdf4ad3fSRanjani Sridharan 178bdf4ad3fSRanjani Sridharan /* update config with stream tag */ 179bdf4ad3fSRanjani Sridharan config->hda.link_dma_ch = channel; 180bdf4ad3fSRanjani Sridharan 181bdf4ad3fSRanjani Sridharan /* send IPC */ 182bdf4ad3fSRanjani Sridharan ret = sof_ipc_tx_message(hda_stream->sdev->ipc, 183bdf4ad3fSRanjani Sridharan config->hdr.cmd, 184bdf4ad3fSRanjani Sridharan config, 185bdf4ad3fSRanjani Sridharan config->hdr.size, 186bdf4ad3fSRanjani Sridharan &reply, sizeof(reply)); 187bdf4ad3fSRanjani Sridharan 188bdf4ad3fSRanjani Sridharan if (ret < 0) 189bdf4ad3fSRanjani Sridharan dev_err(hda_stream->sdev->dev, 190bdf4ad3fSRanjani Sridharan "error: failed to set dai config for %s\n", 191bdf4ad3fSRanjani Sridharan sof_dai->name); 192bdf4ad3fSRanjani Sridharan return ret; 193bdf4ad3fSRanjani Sridharan } 194bdf4ad3fSRanjani Sridharan } 195bdf4ad3fSRanjani Sridharan 196bdf4ad3fSRanjani Sridharan return -EINVAL; 197bdf4ad3fSRanjani Sridharan } 198bdf4ad3fSRanjani Sridharan 199fdd961e3SKeyon Jie static int hda_link_hw_params(struct snd_pcm_substream *substream, 200fdd961e3SKeyon Jie struct snd_pcm_hw_params *params, 201fdd961e3SKeyon Jie struct snd_soc_dai *dai) 202fdd961e3SKeyon Jie { 203fdd961e3SKeyon Jie struct hdac_stream *hstream = substream->runtime->private_data; 204fdd961e3SKeyon Jie struct hdac_bus *bus = hstream->bus; 205fdd961e3SKeyon Jie struct hdac_ext_stream *link_dev; 206*1205300aSKuninori Morimoto struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 207be3e8de7SKuninori Morimoto struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); 208ed3baacdSRanjani Sridharan struct sof_intel_hda_stream *hda_stream; 209fdd961e3SKeyon Jie struct hda_pipe_params p_params = {0}; 210fdd961e3SKeyon Jie struct hdac_ext_link *link; 211fdd961e3SKeyon Jie int stream_tag; 212bdf4ad3fSRanjani Sridharan int ret; 213fdd961e3SKeyon Jie 214934bf822SRander Wang /* get stored dma data if resuming from system suspend */ 215934bf822SRander Wang link_dev = snd_soc_dai_get_dma_data(dai, substream); 216934bf822SRander Wang if (!link_dev) { 217bdf4ad3fSRanjani Sridharan link_dev = hda_link_stream_assign(bus, substream); 218bdf4ad3fSRanjani Sridharan if (!link_dev) 219bdf4ad3fSRanjani Sridharan return -EBUSY; 220921162c8SPierre-Louis Bossart 221921162c8SPierre-Louis Bossart snd_soc_dai_set_dma_data(dai, substream, (void *)link_dev); 222934bf822SRander Wang } 223fdd961e3SKeyon Jie 224bdf4ad3fSRanjani Sridharan stream_tag = hdac_stream(link_dev)->stream_tag; 225bdf4ad3fSRanjani Sridharan 226bdf4ad3fSRanjani Sridharan hda_stream = hstream_to_sof_hda_stream(link_dev); 227bdf4ad3fSRanjani Sridharan 228bdf4ad3fSRanjani Sridharan /* update the DSP with the new tag */ 229bdf4ad3fSRanjani Sridharan ret = hda_link_config_ipc(hda_stream, dai->name, stream_tag - 1, 230bdf4ad3fSRanjani Sridharan substream->stream); 231bdf4ad3fSRanjani Sridharan if (ret < 0) 232bdf4ad3fSRanjani Sridharan return ret; 233bdf4ad3fSRanjani Sridharan 234fdd961e3SKeyon Jie link = snd_hdac_ext_bus_get_link(bus, codec_dai->component->name); 235fdd961e3SKeyon Jie if (!link) 236fdd961e3SKeyon Jie return -EINVAL; 237fdd961e3SKeyon Jie 238fdd961e3SKeyon Jie /* set the stream tag in the codec dai dma params */ 239fdd961e3SKeyon Jie if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 240fdd961e3SKeyon Jie snd_soc_dai_set_tdm_slot(codec_dai, stream_tag, 0, 0, 0); 241fdd961e3SKeyon Jie else 242fdd961e3SKeyon Jie snd_soc_dai_set_tdm_slot(codec_dai, 0, stream_tag, 0, 0); 243fdd961e3SKeyon Jie 244fdd961e3SKeyon Jie p_params.s_fmt = snd_pcm_format_width(params_format(params)); 245fdd961e3SKeyon Jie p_params.ch = params_channels(params); 246fdd961e3SKeyon Jie p_params.s_freq = params_rate(params); 247fdd961e3SKeyon Jie p_params.stream = substream->stream; 248fdd961e3SKeyon Jie p_params.link_dma_id = stream_tag - 1; 249fdd961e3SKeyon Jie p_params.link_index = link->index; 250fdd961e3SKeyon Jie p_params.format = params_format(params); 251fdd961e3SKeyon Jie 252fdd961e3SKeyon Jie if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 253fdd961e3SKeyon Jie p_params.link_bps = codec_dai->driver->playback.sig_bits; 254fdd961e3SKeyon Jie else 255fdd961e3SKeyon Jie p_params.link_bps = codec_dai->driver->capture.sig_bits; 256fdd961e3SKeyon Jie 257fdd961e3SKeyon Jie return hda_link_dma_params(link_dev, &p_params); 258fdd961e3SKeyon Jie } 259fdd961e3SKeyon Jie 260fdd961e3SKeyon Jie static int hda_link_pcm_prepare(struct snd_pcm_substream *substream, 261fdd961e3SKeyon Jie struct snd_soc_dai *dai) 262fdd961e3SKeyon Jie { 263ed3baacdSRanjani Sridharan struct hdac_ext_stream *link_dev = 264ed3baacdSRanjani Sridharan snd_soc_dai_get_dma_data(dai, substream); 265fdd961e3SKeyon Jie struct snd_sof_dev *sdev = 266fdd961e3SKeyon Jie snd_soc_component_get_drvdata(dai->component); 267*1205300aSKuninori Morimoto struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 268fdd961e3SKeyon Jie int stream = substream->stream; 269fdd961e3SKeyon Jie 270a3ebccb5SKai Vehmanen if (link_dev->link_prepared) 271fdd961e3SKeyon Jie return 0; 272fdd961e3SKeyon Jie 273ed3baacdSRanjani Sridharan dev_dbg(sdev->dev, "hda: prepare stream dir %d\n", substream->stream); 274fdd961e3SKeyon Jie 275fdd961e3SKeyon Jie return hda_link_hw_params(substream, &rtd->dpcm[stream].hw_params, 276fdd961e3SKeyon Jie dai); 277fdd961e3SKeyon Jie } 278fdd961e3SKeyon Jie 279fdd961e3SKeyon Jie static int hda_link_pcm_trigger(struct snd_pcm_substream *substream, 280fdd961e3SKeyon Jie int cmd, struct snd_soc_dai *dai) 281fdd961e3SKeyon Jie { 282fdd961e3SKeyon Jie struct hdac_ext_stream *link_dev = 283fdd961e3SKeyon Jie snd_soc_dai_get_dma_data(dai, substream); 284bdf4ad3fSRanjani Sridharan struct sof_intel_hda_stream *hda_stream; 285bdf4ad3fSRanjani Sridharan struct snd_soc_pcm_runtime *rtd; 286bdf4ad3fSRanjani Sridharan struct hdac_ext_link *link; 287bdf4ad3fSRanjani Sridharan struct hdac_stream *hstream; 288bdf4ad3fSRanjani Sridharan struct hdac_bus *bus; 289bdf4ad3fSRanjani Sridharan int stream_tag; 290fdd961e3SKeyon Jie int ret; 291fdd961e3SKeyon Jie 292bdf4ad3fSRanjani Sridharan hstream = substream->runtime->private_data; 293bdf4ad3fSRanjani Sridharan bus = hstream->bus; 294*1205300aSKuninori Morimoto rtd = asoc_substream_to_rtd(substream); 295bdf4ad3fSRanjani Sridharan 296be3e8de7SKuninori Morimoto link = snd_hdac_ext_bus_get_link(bus, asoc_rtd_to_codec(rtd, 0)->component->name); 297bdf4ad3fSRanjani Sridharan if (!link) 298bdf4ad3fSRanjani Sridharan return -EINVAL; 299bdf4ad3fSRanjani Sridharan 300bdf4ad3fSRanjani Sridharan hda_stream = hstream_to_sof_hda_stream(link_dev); 301bdf4ad3fSRanjani Sridharan 302fdd961e3SKeyon Jie dev_dbg(dai->dev, "In %s cmd=%d\n", __func__, cmd); 303fdd961e3SKeyon Jie switch (cmd) { 304fdd961e3SKeyon Jie case SNDRV_PCM_TRIGGER_RESUME: 305fdd961e3SKeyon Jie /* set up hw_params */ 306fdd961e3SKeyon Jie ret = hda_link_pcm_prepare(substream, dai); 307fdd961e3SKeyon Jie if (ret < 0) { 308fdd961e3SKeyon Jie dev_err(dai->dev, 309fdd961e3SKeyon Jie "error: setting up hw_params during resume\n"); 310fdd961e3SKeyon Jie return ret; 311fdd961e3SKeyon Jie } 312fdd961e3SKeyon Jie 313fdd961e3SKeyon Jie /* fallthrough */ 314fdd961e3SKeyon Jie case SNDRV_PCM_TRIGGER_START: 315fdd961e3SKeyon Jie case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 316fdd961e3SKeyon Jie snd_hdac_ext_link_stream_start(link_dev); 317fdd961e3SKeyon Jie break; 318fdd961e3SKeyon Jie case SNDRV_PCM_TRIGGER_SUSPEND: 319a3ebccb5SKai Vehmanen case SNDRV_PCM_TRIGGER_STOP: 320bdf4ad3fSRanjani Sridharan /* 321934bf822SRander Wang * clear link DMA channel. It will be assigned when 322bdf4ad3fSRanjani Sridharan * hw_params is set up again after resume. 323bdf4ad3fSRanjani Sridharan */ 324bdf4ad3fSRanjani Sridharan ret = hda_link_config_ipc(hda_stream, dai->name, 325bdf4ad3fSRanjani Sridharan DMA_CHAN_INVALID, substream->stream); 326bdf4ad3fSRanjani Sridharan if (ret < 0) 327bdf4ad3fSRanjani Sridharan return ret; 328810dbea3SRander Wang 329810dbea3SRander Wang if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 330bdf4ad3fSRanjani Sridharan stream_tag = hdac_stream(link_dev)->stream_tag; 331bdf4ad3fSRanjani Sridharan snd_hdac_ext_link_clear_stream_id(link, stream_tag); 332810dbea3SRander Wang } 333810dbea3SRander Wang 334a3ebccb5SKai Vehmanen link_dev->link_prepared = 0; 335bdf4ad3fSRanjani Sridharan 336bdf4ad3fSRanjani Sridharan /* fallthrough */ 337bdf4ad3fSRanjani Sridharan case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 338fdd961e3SKeyon Jie snd_hdac_ext_link_stream_clear(link_dev); 339fdd961e3SKeyon Jie break; 340fdd961e3SKeyon Jie default: 341fdd961e3SKeyon Jie return -EINVAL; 342fdd961e3SKeyon Jie } 343fdd961e3SKeyon Jie return 0; 344fdd961e3SKeyon Jie } 345fdd961e3SKeyon Jie 346fdd961e3SKeyon Jie static int hda_link_hw_free(struct snd_pcm_substream *substream, 347fdd961e3SKeyon Jie struct snd_soc_dai *dai) 348fdd961e3SKeyon Jie { 349fdd961e3SKeyon Jie unsigned int stream_tag; 350bdf4ad3fSRanjani Sridharan struct sof_intel_hda_stream *hda_stream; 351fdd961e3SKeyon Jie struct hdac_bus *bus; 352fdd961e3SKeyon Jie struct hdac_ext_link *link; 353fdd961e3SKeyon Jie struct hdac_stream *hstream; 354fdd961e3SKeyon Jie struct snd_soc_pcm_runtime *rtd; 355fdd961e3SKeyon Jie struct hdac_ext_stream *link_dev; 356bdf4ad3fSRanjani Sridharan int ret; 357fdd961e3SKeyon Jie 358fdd961e3SKeyon Jie hstream = substream->runtime->private_data; 359fdd961e3SKeyon Jie bus = hstream->bus; 360*1205300aSKuninori Morimoto rtd = asoc_substream_to_rtd(substream); 361fdd961e3SKeyon Jie link_dev = snd_soc_dai_get_dma_data(dai, substream); 362921162c8SPierre-Louis Bossart 363921162c8SPierre-Louis Bossart if (!link_dev) { 364921162c8SPierre-Louis Bossart dev_dbg(dai->dev, 365921162c8SPierre-Louis Bossart "%s: link_dev is not assigned\n", __func__); 366921162c8SPierre-Louis Bossart return -EINVAL; 367921162c8SPierre-Louis Bossart } 368921162c8SPierre-Louis Bossart 369bdf4ad3fSRanjani Sridharan hda_stream = hstream_to_sof_hda_stream(link_dev); 370bdf4ad3fSRanjani Sridharan 371bdf4ad3fSRanjani Sridharan /* free the link DMA channel in the FW */ 372bdf4ad3fSRanjani Sridharan ret = hda_link_config_ipc(hda_stream, dai->name, DMA_CHAN_INVALID, 373bdf4ad3fSRanjani Sridharan substream->stream); 374bdf4ad3fSRanjani Sridharan if (ret < 0) 375bdf4ad3fSRanjani Sridharan return ret; 376bdf4ad3fSRanjani Sridharan 377be3e8de7SKuninori Morimoto link = snd_hdac_ext_bus_get_link(bus, asoc_rtd_to_codec(rtd, 0)->component->name); 378fdd961e3SKeyon Jie if (!link) 379fdd961e3SKeyon Jie return -EINVAL; 380fdd961e3SKeyon Jie 381810dbea3SRander Wang if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 382fdd961e3SKeyon Jie stream_tag = hdac_stream(link_dev)->stream_tag; 383fdd961e3SKeyon Jie snd_hdac_ext_link_clear_stream_id(link, stream_tag); 384810dbea3SRander Wang } 385810dbea3SRander Wang 386934bf822SRander Wang snd_soc_dai_set_dma_data(dai, substream, NULL); 387bdf4ad3fSRanjani Sridharan snd_hdac_ext_stream_release(link_dev, HDAC_EXT_STREAM_TYPE_LINK); 388fdd961e3SKeyon Jie link_dev->link_prepared = 0; 389fdd961e3SKeyon Jie 3906b2239e3SRanjani Sridharan /* free the host DMA channel reserved by hostless streams */ 3916b2239e3SRanjani Sridharan hda_stream->host_reserved = 0; 3926b2239e3SRanjani Sridharan 393fdd961e3SKeyon Jie return 0; 394fdd961e3SKeyon Jie } 395fdd961e3SKeyon Jie 396fdd961e3SKeyon Jie static const struct snd_soc_dai_ops hda_link_dai_ops = { 397fdd961e3SKeyon Jie .hw_params = hda_link_hw_params, 398fdd961e3SKeyon Jie .hw_free = hda_link_hw_free, 399fdd961e3SKeyon Jie .trigger = hda_link_pcm_trigger, 400fdd961e3SKeyon Jie .prepare = hda_link_pcm_prepare, 401fdd961e3SKeyon Jie }; 40270368106SCezary Rojewski 40370368106SCezary Rojewski #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) 40470368106SCezary Rojewski #include "../compress.h" 40570368106SCezary Rojewski 40670368106SCezary Rojewski static struct snd_soc_cdai_ops sof_probe_compr_ops = { 40770368106SCezary Rojewski .startup = sof_probe_compr_open, 40870368106SCezary Rojewski .shutdown = sof_probe_compr_free, 40970368106SCezary Rojewski .set_params = sof_probe_compr_set_params, 41070368106SCezary Rojewski .trigger = sof_probe_compr_trigger, 41170368106SCezary Rojewski .pointer = sof_probe_compr_pointer, 41270368106SCezary Rojewski }; 41370368106SCezary Rojewski 41470368106SCezary Rojewski #endif 415fdd961e3SKeyon Jie #endif 416fdd961e3SKeyon Jie 417fdd961e3SKeyon Jie /* 418fdd961e3SKeyon Jie * common dai driver for skl+ platforms. 419fdd961e3SKeyon Jie * some products who use this DAI array only physically have a subset of 420fdd961e3SKeyon Jie * the DAIs, but no harm is done here by adding the whole set. 421fdd961e3SKeyon Jie */ 422fdd961e3SKeyon Jie struct snd_soc_dai_driver skl_dai[] = { 423fdd961e3SKeyon Jie { 424fdd961e3SKeyon Jie .name = "SSP0 Pin", 425e81d47e9SBard Liao .playback = { 426e81d47e9SBard Liao .channels_min = 1, 427e81d47e9SBard Liao .channels_max = 8, 428e81d47e9SBard Liao }, 429e81d47e9SBard Liao .capture = { 430e81d47e9SBard Liao .channels_min = 1, 431e81d47e9SBard Liao .channels_max = 8, 432e81d47e9SBard Liao }, 433fdd961e3SKeyon Jie }, 434fdd961e3SKeyon Jie { 435fdd961e3SKeyon Jie .name = "SSP1 Pin", 436e81d47e9SBard Liao .playback = { 437e81d47e9SBard Liao .channels_min = 1, 438e81d47e9SBard Liao .channels_max = 8, 439e81d47e9SBard Liao }, 440e81d47e9SBard Liao .capture = { 441e81d47e9SBard Liao .channels_min = 1, 442e81d47e9SBard Liao .channels_max = 8, 443e81d47e9SBard Liao }, 444fdd961e3SKeyon Jie }, 445fdd961e3SKeyon Jie { 446fdd961e3SKeyon Jie .name = "SSP2 Pin", 447e81d47e9SBard Liao .playback = { 448e81d47e9SBard Liao .channels_min = 1, 449e81d47e9SBard Liao .channels_max = 8, 450e81d47e9SBard Liao }, 451e81d47e9SBard Liao .capture = { 452e81d47e9SBard Liao .channels_min = 1, 453e81d47e9SBard Liao .channels_max = 8, 454e81d47e9SBard Liao }, 455fdd961e3SKeyon Jie }, 456fdd961e3SKeyon Jie { 457fdd961e3SKeyon Jie .name = "SSP3 Pin", 458e81d47e9SBard Liao .playback = { 459e81d47e9SBard Liao .channels_min = 1, 460e81d47e9SBard Liao .channels_max = 8, 461e81d47e9SBard Liao }, 462e81d47e9SBard Liao .capture = { 463e81d47e9SBard Liao .channels_min = 1, 464e81d47e9SBard Liao .channels_max = 8, 465e81d47e9SBard Liao }, 466fdd961e3SKeyon Jie }, 467fdd961e3SKeyon Jie { 468fdd961e3SKeyon Jie .name = "SSP4 Pin", 469e81d47e9SBard Liao .playback = { 470e81d47e9SBard Liao .channels_min = 1, 471e81d47e9SBard Liao .channels_max = 8, 472e81d47e9SBard Liao }, 473e81d47e9SBard Liao .capture = { 474e81d47e9SBard Liao .channels_min = 1, 475e81d47e9SBard Liao .channels_max = 8, 476e81d47e9SBard Liao }, 477fdd961e3SKeyon Jie }, 478fdd961e3SKeyon Jie { 479fdd961e3SKeyon Jie .name = "SSP5 Pin", 480e81d47e9SBard Liao .playback = { 481e81d47e9SBard Liao .channels_min = 1, 482e81d47e9SBard Liao .channels_max = 8, 483e81d47e9SBard Liao }, 484e81d47e9SBard Liao .capture = { 485e81d47e9SBard Liao .channels_min = 1, 486e81d47e9SBard Liao .channels_max = 8, 487e81d47e9SBard Liao }, 488fdd961e3SKeyon Jie }, 489fdd961e3SKeyon Jie { 490fdd961e3SKeyon Jie .name = "DMIC01 Pin", 491e81d47e9SBard Liao .capture = { 492e81d47e9SBard Liao .channels_min = 1, 493e81d47e9SBard Liao .channels_max = 4, 494e81d47e9SBard Liao }, 495fdd961e3SKeyon Jie }, 496fdd961e3SKeyon Jie { 497fdd961e3SKeyon Jie .name = "DMIC16k Pin", 498e81d47e9SBard Liao .capture = { 499e81d47e9SBard Liao .channels_min = 1, 500e81d47e9SBard Liao .channels_max = 4, 501e81d47e9SBard Liao }, 502fdd961e3SKeyon Jie }, 503fdd961e3SKeyon Jie #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) 504fdd961e3SKeyon Jie { 505fdd961e3SKeyon Jie .name = "iDisp1 Pin", 506fdd961e3SKeyon Jie .ops = &hda_link_dai_ops, 507e81d47e9SBard Liao .playback = { 508e81d47e9SBard Liao .channels_min = 1, 509e81d47e9SBard Liao .channels_max = 8, 510e81d47e9SBard Liao }, 511fdd961e3SKeyon Jie }, 512fdd961e3SKeyon Jie { 513fdd961e3SKeyon Jie .name = "iDisp2 Pin", 514fdd961e3SKeyon Jie .ops = &hda_link_dai_ops, 515e81d47e9SBard Liao .playback = { 516e81d47e9SBard Liao .channels_min = 1, 517e81d47e9SBard Liao .channels_max = 8, 518e81d47e9SBard Liao }, 519fdd961e3SKeyon Jie }, 520fdd961e3SKeyon Jie { 521fdd961e3SKeyon Jie .name = "iDisp3 Pin", 522fdd961e3SKeyon Jie .ops = &hda_link_dai_ops, 523e81d47e9SBard Liao .playback = { 524e81d47e9SBard Liao .channels_min = 1, 525e81d47e9SBard Liao .channels_max = 8, 526e81d47e9SBard Liao }, 527fdd961e3SKeyon Jie }, 528fdd961e3SKeyon Jie { 529e68d6696SSathyanarayana Nujella .name = "iDisp4 Pin", 530e68d6696SSathyanarayana Nujella .ops = &hda_link_dai_ops, 531e81d47e9SBard Liao .playback = { 532e81d47e9SBard Liao .channels_min = 1, 533e81d47e9SBard Liao .channels_max = 8, 534e81d47e9SBard Liao }, 535e68d6696SSathyanarayana Nujella }, 536e68d6696SSathyanarayana Nujella { 537fdd961e3SKeyon Jie .name = "Analog CPU DAI", 538fdd961e3SKeyon Jie .ops = &hda_link_dai_ops, 539e81d47e9SBard Liao .playback = { 540e81d47e9SBard Liao .channels_min = 1, 541e81d47e9SBard Liao .channels_max = 16, 542e81d47e9SBard Liao }, 543e81d47e9SBard Liao .capture = { 544e81d47e9SBard Liao .channels_min = 1, 545e81d47e9SBard Liao .channels_max = 16, 546e81d47e9SBard Liao }, 547fdd961e3SKeyon Jie }, 548fdd961e3SKeyon Jie { 549fdd961e3SKeyon Jie .name = "Digital CPU DAI", 550fdd961e3SKeyon Jie .ops = &hda_link_dai_ops, 551e81d47e9SBard Liao .playback = { 552e81d47e9SBard Liao .channels_min = 1, 553e81d47e9SBard Liao .channels_max = 16, 554e81d47e9SBard Liao }, 555e81d47e9SBard Liao .capture = { 556e81d47e9SBard Liao .channels_min = 1, 557e81d47e9SBard Liao .channels_max = 16, 558e81d47e9SBard Liao }, 559fdd961e3SKeyon Jie }, 560fdd961e3SKeyon Jie { 561fdd961e3SKeyon Jie .name = "Alt Analog CPU DAI", 562fdd961e3SKeyon Jie .ops = &hda_link_dai_ops, 563e81d47e9SBard Liao .playback = { 564e81d47e9SBard Liao .channels_min = 1, 565e81d47e9SBard Liao .channels_max = 16, 566e81d47e9SBard Liao }, 567e81d47e9SBard Liao .capture = { 568e81d47e9SBard Liao .channels_min = 1, 569e81d47e9SBard Liao .channels_max = 16, 570e81d47e9SBard Liao }, 571fdd961e3SKeyon Jie }, 57270368106SCezary Rojewski #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES) 57370368106SCezary Rojewski { 57470368106SCezary Rojewski .name = "Probe Extraction CPU DAI", 57570368106SCezary Rojewski .compress_new = snd_soc_new_compress, 57670368106SCezary Rojewski .cops = &sof_probe_compr_ops, 57770368106SCezary Rojewski .capture = { 57870368106SCezary Rojewski .stream_name = "Probe Extraction", 57970368106SCezary Rojewski .channels_min = 1, 58070368106SCezary Rojewski .channels_max = 8, 58170368106SCezary Rojewski .rates = SNDRV_PCM_RATE_48000, 58270368106SCezary Rojewski .rate_min = 48000, 58370368106SCezary Rojewski .rate_max = 48000, 58470368106SCezary Rojewski }, 58570368106SCezary Rojewski }, 58670368106SCezary Rojewski #endif 587fdd961e3SKeyon Jie #endif 588fdd961e3SKeyon Jie }; 589