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