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; 34*1f7b5d52SPeter Ujfalusi const struct sof_ipc_tplg_ops *tplg_ops; 35*1f7b5d52SPeter Ujfalusi struct snd_soc_component *component; 36*1f7b5d52SPeter Ujfalusi struct snd_sof_dev *sdev; 37e6ffb0d5SRanjani Sridharan int ret; 38e6ffb0d5SRanjani Sridharan 39*1f7b5d52SPeter Ujfalusi if (!swidget) 40*1f7b5d52SPeter Ujfalusi return 0; 41*1f7b5d52SPeter Ujfalusi 42*1f7b5d52SPeter Ujfalusi component = swidget->scomp; 43*1f7b5d52SPeter Ujfalusi sdev = snd_soc_component_get_drvdata(component); 44*1f7b5d52SPeter Ujfalusi tplg_ops = sof_ipc_get_ops(sdev, tplg); 45*1f7b5d52SPeter Ujfalusi 46e6ffb0d5SRanjani Sridharan if (tplg_ops && tplg_ops->dai_config) { 47e6ffb0d5SRanjani Sridharan ret = tplg_ops->dai_config(sdev, swidget, flags, data); 48e6ffb0d5SRanjani Sridharan if (ret < 0) { 49e6ffb0d5SRanjani Sridharan dev_err(sdev->dev, "DAI config with flags %x failed for widget %s\n", 50e6ffb0d5SRanjani Sridharan flags, w->name); 51e6ffb0d5SRanjani Sridharan return ret; 52e6ffb0d5SRanjani Sridharan } 53e6ffb0d5SRanjani Sridharan } 54e6ffb0d5SRanjani Sridharan 55e6ffb0d5SRanjani Sridharan return 0; 56e6ffb0d5SRanjani Sridharan } 57e6ffb0d5SRanjani Sridharan 582b009fa0SRanjani Sridharan #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) 592b009fa0SRanjani Sridharan 6080afde34SRanjani Sridharan static const struct hda_dai_widget_dma_ops * 6180afde34SRanjani Sridharan hda_dai_get_ops(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) 62fdd961e3SKeyon Jie { 6380afde34SRanjani Sridharan struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream); 6480afde34SRanjani Sridharan struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(cpu_dai->component); 6580afde34SRanjani Sridharan struct snd_sof_widget *swidget = w->dobj.private; 66*1f7b5d52SPeter Ujfalusi struct snd_sof_dai *sdai; 67*1f7b5d52SPeter Ujfalusi 68*1f7b5d52SPeter Ujfalusi /* 69*1f7b5d52SPeter Ujfalusi * The swidget parameter of hda_select_dai_widget_ops() is ignored in 70*1f7b5d52SPeter Ujfalusi * case of DSPless mode 71*1f7b5d52SPeter Ujfalusi */ 72*1f7b5d52SPeter Ujfalusi if (sdev->dspless_mode_selected) 73*1f7b5d52SPeter Ujfalusi return hda_select_dai_widget_ops(sdev, NULL); 74*1f7b5d52SPeter Ujfalusi 75*1f7b5d52SPeter Ujfalusi sdai = swidget->private; 76fdd961e3SKeyon Jie 7780afde34SRanjani Sridharan /* select and set the DAI widget ops if not set already */ 7880afde34SRanjani Sridharan if (!sdai->platform_private) { 7980afde34SRanjani Sridharan const struct hda_dai_widget_dma_ops *ops = 8080afde34SRanjani Sridharan hda_select_dai_widget_ops(sdev, swidget); 8180afde34SRanjani Sridharan if (!ops) 82bdf4ad3fSRanjani Sridharan return NULL; 8380afde34SRanjani Sridharan 8480afde34SRanjani Sridharan /* check if mandatory ops are set */ 8580afde34SRanjani Sridharan if (!ops || !ops->get_hext_stream) 8680afde34SRanjani Sridharan return NULL; 8780afde34SRanjani Sridharan 8880afde34SRanjani Sridharan sdai->platform_private = ops; 89fdd961e3SKeyon Jie } 90fdd961e3SKeyon Jie 9180afde34SRanjani Sridharan return sdai->platform_private; 92fdd961e3SKeyon Jie } 93fdd961e3SKeyon Jie 94880924caSPierre-Louis Bossart static int hda_link_dma_cleanup(struct snd_pcm_substream *substream, 950351a9b8SPierre-Louis Bossart struct hdac_ext_stream *hext_stream, 96880924caSPierre-Louis Bossart struct snd_soc_dai *cpu_dai, 972be2caf4SRanjani Sridharan struct snd_soc_dai *codec_dai) 98880924caSPierre-Louis Bossart { 9980afde34SRanjani Sridharan struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(cpu_dai->component); 10080afde34SRanjani Sridharan const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai); 1010351a9b8SPierre-Louis Bossart struct hdac_stream *hstream = &hext_stream->hstream; 102880924caSPierre-Louis Bossart struct hdac_bus *bus = hstream->bus; 103880924caSPierre-Louis Bossart struct sof_intel_hda_stream *hda_stream; 1047f1e16aeSPierre-Louis Bossart struct hdac_ext_link *hlink; 105880924caSPierre-Louis Bossart int stream_tag; 106880924caSPierre-Louis Bossart 107b0cd60f3SPierre-Louis Bossart hlink = snd_hdac_ext_bus_get_hlink_by_name(bus, codec_dai->component->name); 1087f1e16aeSPierre-Louis Bossart if (!hlink) 109880924caSPierre-Louis Bossart return -EINVAL; 110880924caSPierre-Louis Bossart 111880924caSPierre-Louis Bossart if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 112880924caSPierre-Louis Bossart stream_tag = hdac_stream(hext_stream)->stream_tag; 1137fa403f2SPierre-Louis Bossart snd_hdac_ext_bus_link_clear_stream_id(hlink, stream_tag); 114880924caSPierre-Louis Bossart } 11580afde34SRanjani Sridharan 11680afde34SRanjani Sridharan if (ops->release_hext_stream) 11780afde34SRanjani Sridharan ops->release_hext_stream(sdev, cpu_dai, substream); 11880afde34SRanjani Sridharan 119880924caSPierre-Louis Bossart hext_stream->link_prepared = 0; 120880924caSPierre-Louis Bossart 121880924caSPierre-Louis Bossart /* free the host DMA channel reserved by hostless streams */ 122880924caSPierre-Louis Bossart hda_stream = hstream_to_sof_hda_stream(hext_stream); 123880924caSPierre-Louis Bossart hda_stream->host_reserved = 0; 124880924caSPierre-Louis Bossart 125880924caSPierre-Louis Bossart return 0; 126880924caSPierre-Louis Bossart } 127880924caSPierre-Louis Bossart 128f321ffc8SPierre-Louis Bossart static int hda_link_dma_hw_params(struct snd_pcm_substream *substream, 129b436ed8dSRanjani Sridharan struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai) 130fdd961e3SKeyon Jie { 13180afde34SRanjani Sridharan const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai); 1321205300aSKuninori Morimoto struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 133be3e8de7SKuninori Morimoto struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); 1344842f79fSPierre-Louis Bossart struct hdac_ext_stream *hext_stream; 135a8310c00SRanjani Sridharan struct hdac_stream *hstream; 1367f1e16aeSPierre-Louis Bossart struct hdac_ext_link *hlink; 1374842f79fSPierre-Louis Bossart struct snd_sof_dev *sdev; 1384842f79fSPierre-Louis Bossart struct hdac_bus *bus; 139a8310c00SRanjani Sridharan unsigned int format_val; 1402a6afac2SRanjani Sridharan unsigned int link_bps; 14180afde34SRanjani Sridharan int stream_tag; 1424842f79fSPierre-Louis Bossart 1434842f79fSPierre-Louis Bossart sdev = snd_soc_component_get_drvdata(cpu_dai->component); 1444842f79fSPierre-Louis Bossart bus = sof_to_bus(sdev); 145fdd961e3SKeyon Jie 1461f810d2bSPierre-Louis Bossart hlink = snd_hdac_ext_bus_get_hlink_by_name(bus, codec_dai->component->name); 1471f810d2bSPierre-Louis Bossart if (!hlink) 1481f810d2bSPierre-Louis Bossart return -EINVAL; 1491f810d2bSPierre-Louis Bossart 15080afde34SRanjani Sridharan hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream); 15180afde34SRanjani Sridharan 1527d88b960SPierre-Louis Bossart if (!hext_stream) { 15380afde34SRanjani Sridharan if (ops->assign_hext_stream) 15480afde34SRanjani Sridharan hext_stream = ops->assign_hext_stream(sdev, cpu_dai, substream); 15580afde34SRanjani Sridharan } 15680afde34SRanjani Sridharan 1577d88b960SPierre-Louis Bossart if (!hext_stream) 158bdf4ad3fSRanjani Sridharan return -EBUSY; 159921162c8SPierre-Louis Bossart 160a8310c00SRanjani Sridharan hstream = &hext_stream->hstream; 161a8310c00SRanjani Sridharan stream_tag = hstream->stream_tag; 162a8310c00SRanjani Sridharan 1632a6afac2SRanjani Sridharan if (hext_stream->hstream.direction == SNDRV_PCM_STREAM_PLAYBACK) 1642a6afac2SRanjani Sridharan snd_hdac_ext_bus_link_set_stream_id(hlink, stream_tag); 1652a6afac2SRanjani Sridharan 16663611041SPierre-Louis Bossart /* set the hdac_stream in the codec dai */ 1677d88b960SPierre-Louis Bossart snd_soc_dai_set_stream(codec_dai, hdac_stream(hext_stream), substream->stream); 168fdd961e3SKeyon Jie 169fdd961e3SKeyon Jie if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 1702a6afac2SRanjani Sridharan link_bps = codec_dai->driver->playback.sig_bits; 171fdd961e3SKeyon Jie else 1722a6afac2SRanjani Sridharan link_bps = codec_dai->driver->capture.sig_bits; 173fdd961e3SKeyon Jie 174e2d6569aSRanjani Sridharan if (ops->reset_hext_stream) 175e2d6569aSRanjani Sridharan ops->reset_hext_stream(sdev, hext_stream); 176a8310c00SRanjani Sridharan 1772a6afac2SRanjani Sridharan format_val = snd_hdac_calc_stream_format(params_rate(params), params_channels(params), 1782a6afac2SRanjani Sridharan params_format(params), link_bps, 0); 179a8310c00SRanjani Sridharan 180a8310c00SRanjani Sridharan dev_dbg(bus->dev, "format_val=%d, rate=%d, ch=%d, format=%d\n", 1812a6afac2SRanjani Sridharan format_val, params_rate(params), params_channels(params), params_format(params)); 182a8310c00SRanjani Sridharan 183e2d6569aSRanjani Sridharan if (ops->setup_hext_stream) 184e2d6569aSRanjani Sridharan ops->setup_hext_stream(sdev, hext_stream, format_val); 185a8310c00SRanjani Sridharan 186a8310c00SRanjani Sridharan hext_stream->link_prepared = 1; 187a8310c00SRanjani Sridharan 188a8310c00SRanjani Sridharan return 0; 189fdd961e3SKeyon Jie } 190fdd961e3SKeyon Jie 191b436ed8dSRanjani Sridharan static int hda_link_dma_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) 192f321ffc8SPierre-Louis Bossart { 1931205300aSKuninori Morimoto struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 194fdd961e3SKeyon Jie int stream = substream->stream; 195fdd961e3SKeyon Jie 196b436ed8dSRanjani Sridharan return hda_link_dma_hw_params(substream, &rtd->dpcm[stream].hw_params, cpu_dai); 197fdd961e3SKeyon Jie } 198fdd961e3SKeyon Jie 1998c29e78bSRanjani Sridharan static int hda_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) 2009272d6c2SPierre-Louis Bossart { 20180afde34SRanjani Sridharan struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(cpu_dai->component); 20280afde34SRanjani Sridharan const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai); 2039272d6c2SPierre-Louis Bossart struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 2049272d6c2SPierre-Louis Bossart struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); 2059272d6c2SPierre-Louis Bossart struct hdac_ext_stream *hext_stream; 2069272d6c2SPierre-Louis Bossart 20780afde34SRanjani Sridharan if (!ops) { 20880afde34SRanjani Sridharan dev_err(sdev->dev, "DAI widget ops not set\n"); 20980afde34SRanjani Sridharan return -EINVAL; 21080afde34SRanjani Sridharan } 21180afde34SRanjani Sridharan 21280afde34SRanjani Sridharan hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream); 213880924caSPierre-Louis Bossart if (!hext_stream) 2149272d6c2SPierre-Louis Bossart return 0; 215880924caSPierre-Louis Bossart 2162be2caf4SRanjani Sridharan return hda_link_dma_cleanup(substream, hext_stream, cpu_dai, codec_dai); 2179272d6c2SPierre-Louis Bossart } 2189272d6c2SPierre-Louis Bossart 2199272d6c2SPierre-Louis Bossart static int hda_dai_hw_params(struct snd_pcm_substream *substream, 2209272d6c2SPierre-Louis Bossart struct snd_pcm_hw_params *params, 2219272d6c2SPierre-Louis Bossart struct snd_soc_dai *dai) 2229272d6c2SPierre-Louis Bossart { 223e6ffb0d5SRanjani Sridharan struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, substream->stream); 22480afde34SRanjani Sridharan struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component); 22580afde34SRanjani Sridharan const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai); 22680afde34SRanjani Sridharan struct hdac_ext_stream *hext_stream; 227e6ffb0d5SRanjani Sridharan struct snd_sof_dai_config_data data = { 0 }; 228e6ffb0d5SRanjani Sridharan unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS; 2299272d6c2SPierre-Louis Bossart int ret; 2309272d6c2SPierre-Louis Bossart 23180afde34SRanjani Sridharan if (!ops) { 23280afde34SRanjani Sridharan dev_err(sdev->dev, "DAI widget ops not set\n"); 23380afde34SRanjani Sridharan return -EINVAL; 23480afde34SRanjani Sridharan } 23580afde34SRanjani Sridharan 23680afde34SRanjani Sridharan hext_stream = ops->get_hext_stream(sdev, dai, substream); 237c4eb48f7SPierre-Louis Bossart if (hext_stream && hext_stream->link_prepared) 238c4eb48f7SPierre-Louis Bossart return 0; 239c4eb48f7SPierre-Louis Bossart 240b436ed8dSRanjani Sridharan ret = hda_link_dma_hw_params(substream, params, dai); 2419272d6c2SPierre-Louis Bossart if (ret < 0) 2429272d6c2SPierre-Louis Bossart return ret; 2439272d6c2SPierre-Louis Bossart 244e6ffb0d5SRanjani Sridharan hext_stream = ops->get_hext_stream(sdev, dai, substream); 2459272d6c2SPierre-Louis Bossart 246e6ffb0d5SRanjani Sridharan flags |= SOF_DAI_CONFIG_FLAGS_2_STEP_STOP << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT; 247e6ffb0d5SRanjani Sridharan data.dai_data = hdac_stream(hext_stream)->stream_tag - 1; 2489272d6c2SPierre-Louis Bossart 249e6ffb0d5SRanjani Sridharan return hda_dai_config(w, flags, &data); 2509272d6c2SPierre-Louis Bossart } 2519272d6c2SPierre-Louis Bossart 2524c30004aSRanjani Sridharan static int hda_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) 2539272d6c2SPierre-Louis Bossart { 254e6ffb0d5SRanjani Sridharan struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, substream->stream); 2559272d6c2SPierre-Louis Bossart struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component); 25680afde34SRanjani Sridharan const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai); 25780afde34SRanjani Sridharan struct hdac_ext_stream *hext_stream; 258e6ffb0d5SRanjani Sridharan struct snd_sof_dai_config_data data = { 0 }; 259e6ffb0d5SRanjani Sridharan unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS; 2609272d6c2SPierre-Louis Bossart int ret; 2619272d6c2SPierre-Louis Bossart 26280afde34SRanjani Sridharan hext_stream = ops->get_hext_stream(sdev, dai, substream); 263722cbbfaSPierre-Louis Bossart if (hext_stream && hext_stream->link_prepared) 2649272d6c2SPierre-Louis Bossart return 0; 2659272d6c2SPierre-Louis Bossart 26618701bb1SPierre-Louis Bossart dev_dbg(sdev->dev, "prepare stream dir %d\n", substream->stream); 2679272d6c2SPierre-Louis Bossart 268b436ed8dSRanjani Sridharan ret = hda_link_dma_prepare(substream, dai); 2699272d6c2SPierre-Louis Bossart if (ret < 0) 2709272d6c2SPierre-Louis Bossart return ret; 2719272d6c2SPierre-Louis Bossart 272e6ffb0d5SRanjani Sridharan hext_stream = ops->get_hext_stream(sdev, dai, substream); 273e6ffb0d5SRanjani Sridharan 274e6ffb0d5SRanjani Sridharan flags |= SOF_DAI_CONFIG_FLAGS_2_STEP_STOP << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT; 275e6ffb0d5SRanjani Sridharan data.dai_data = hdac_stream(hext_stream)->stream_tag - 1; 276e6ffb0d5SRanjani Sridharan 277e6ffb0d5SRanjani Sridharan return hda_dai_config(w, flags, &data); 2789272d6c2SPierre-Louis Bossart } 2799272d6c2SPierre-Louis Bossart 2804c30004aSRanjani Sridharan /* 2814c30004aSRanjani Sridharan * In contrast to IPC3, the dai trigger in IPC4 mixes pipeline state changes 2824c30004aSRanjani Sridharan * (over IPC channel) and DMA state change (direct host register changes). 2834c30004aSRanjani Sridharan */ 2842b009fa0SRanjani Sridharan static int hda_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) 2854c30004aSRanjani Sridharan { 2864c30004aSRanjani Sridharan struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component); 28780afde34SRanjani Sridharan const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai); 28880afde34SRanjani Sridharan struct hdac_ext_stream *hext_stream; 2894c30004aSRanjani Sridharan struct snd_soc_pcm_runtime *rtd; 2904c30004aSRanjani Sridharan struct snd_soc_dai *codec_dai; 2914c30004aSRanjani Sridharan int ret; 2924c30004aSRanjani Sridharan 29318701bb1SPierre-Louis Bossart dev_dbg(dai->dev, "cmd=%d dai %s direction %d\n", cmd, 2944c30004aSRanjani Sridharan dai->name, substream->stream); 2954c30004aSRanjani Sridharan 29680afde34SRanjani Sridharan hext_stream = ops->get_hext_stream(sdev, dai, substream); 29780afde34SRanjani Sridharan if (!hext_stream) 29880afde34SRanjani Sridharan return -EINVAL; 29980afde34SRanjani Sridharan 3004c30004aSRanjani Sridharan rtd = asoc_substream_to_rtd(substream); 3014c30004aSRanjani Sridharan codec_dai = asoc_rtd_to_codec(rtd, 0); 3024c30004aSRanjani Sridharan 3034b2ee4cdSRanjani Sridharan if (ops->pre_trigger) { 3044b2ee4cdSRanjani Sridharan ret = ops->pre_trigger(sdev, dai, substream, cmd); 30537a26eecSRanjani Sridharan if (ret < 0) 30637a26eecSRanjani Sridharan return ret; 30737a26eecSRanjani Sridharan } 30837a26eecSRanjani Sridharan 3094b2ee4cdSRanjani Sridharan if (ops->trigger) { 3104b2ee4cdSRanjani Sridharan ret = ops->trigger(sdev, dai, substream, cmd); 31137a26eecSRanjani Sridharan if (ret < 0) 31237a26eecSRanjani Sridharan return ret; 3134b2ee4cdSRanjani Sridharan } 3144b2ee4cdSRanjani Sridharan 3154b2ee4cdSRanjani Sridharan if (ops->post_trigger) { 3164b2ee4cdSRanjani Sridharan ret = ops->post_trigger(sdev, dai, substream, cmd); 3174b2ee4cdSRanjani Sridharan if (ret < 0) 3184b2ee4cdSRanjani Sridharan return ret; 3194b2ee4cdSRanjani Sridharan } 3204b2ee4cdSRanjani Sridharan 3214b2ee4cdSRanjani Sridharan switch (cmd) { 3224c30004aSRanjani Sridharan case SNDRV_PCM_TRIGGER_SUSPEND: 323be7f4f8dSRanjani Sridharan ret = hda_link_dma_cleanup(substream, hext_stream, dai, codec_dai); 3244c30004aSRanjani Sridharan if (ret < 0) { 3254c30004aSRanjani Sridharan dev_err(sdev->dev, "%s: failed to clean up link DMA\n", __func__); 3264c30004aSRanjani Sridharan return ret; 3274c30004aSRanjani Sridharan } 3284c30004aSRanjani Sridharan break; 3294c30004aSRanjani Sridharan default: 3304b2ee4cdSRanjani Sridharan break; 3314c30004aSRanjani Sridharan } 3324c30004aSRanjani Sridharan 3334c30004aSRanjani Sridharan return 0; 3344c30004aSRanjani Sridharan } 3354c30004aSRanjani Sridharan 3362b009fa0SRanjani Sridharan static const struct snd_soc_dai_ops hda_dai_ops = { 337309e6e55SPierre-Louis Bossart .hw_params = hda_dai_hw_params, 338309e6e55SPierre-Louis Bossart .hw_free = hda_dai_hw_free, 3392b009fa0SRanjani Sridharan .trigger = hda_dai_trigger, 3404c30004aSRanjani Sridharan .prepare = hda_dai_prepare, 341fdd961e3SKeyon Jie }; 34270368106SCezary Rojewski 343f09e9284SPierre-Louis Bossart static int hda_dai_suspend(struct hdac_bus *bus) 344f09e9284SPierre-Louis Bossart { 345f09e9284SPierre-Louis Bossart struct snd_soc_pcm_runtime *rtd; 346f09e9284SPierre-Louis Bossart struct hdac_ext_stream *hext_stream; 347f09e9284SPierre-Louis Bossart struct hdac_stream *s; 34823b1944eSPierre-Louis Bossart int ret; 349f09e9284SPierre-Louis Bossart 350f09e9284SPierre-Louis Bossart /* set internal flag for BE */ 351f09e9284SPierre-Louis Bossart list_for_each_entry(s, &bus->stream_list, list) { 352722cbbfaSPierre-Louis Bossart 353f09e9284SPierre-Louis Bossart hext_stream = stream_to_hdac_ext_stream(s); 354f09e9284SPierre-Louis Bossart 355f09e9284SPierre-Louis Bossart /* 356f09e9284SPierre-Louis Bossart * clear stream. This should already be taken care for running 357f09e9284SPierre-Louis Bossart * streams when the SUSPEND trigger is called. But paused 358f09e9284SPierre-Louis Bossart * streams do not get suspended, so this needs to be done 359f09e9284SPierre-Louis Bossart * explicitly during suspend. 360f09e9284SPierre-Louis Bossart */ 361f09e9284SPierre-Louis Bossart if (hext_stream->link_substream) { 3622b009fa0SRanjani Sridharan const struct hda_dai_widget_dma_ops *ops; 3632b009fa0SRanjani Sridharan struct snd_sof_widget *swidget; 3642b009fa0SRanjani Sridharan struct snd_soc_dapm_widget *w; 36523b1944eSPierre-Louis Bossart struct snd_soc_dai *codec_dai; 3662b009fa0SRanjani Sridharan struct snd_soc_dai *cpu_dai; 3672b009fa0SRanjani Sridharan struct snd_sof_dev *sdev; 3682b009fa0SRanjani Sridharan struct snd_sof_dai *sdai; 36923b1944eSPierre-Louis Bossart 370f09e9284SPierre-Louis Bossart rtd = asoc_substream_to_rtd(hext_stream->link_substream); 37123b1944eSPierre-Louis Bossart cpu_dai = asoc_rtd_to_cpu(rtd, 0); 37223b1944eSPierre-Louis Bossart codec_dai = asoc_rtd_to_codec(rtd, 0); 3732b009fa0SRanjani Sridharan w = snd_soc_dai_get_widget(cpu_dai, hdac_stream(hext_stream)->direction); 3742b009fa0SRanjani Sridharan swidget = w->dobj.private; 3752b009fa0SRanjani Sridharan sdev = snd_soc_component_get_drvdata(swidget->scomp); 3762b009fa0SRanjani Sridharan sdai = swidget->private; 3772b009fa0SRanjani Sridharan ops = sdai->platform_private; 378f09e9284SPierre-Louis Bossart 3790351a9b8SPierre-Louis Bossart ret = hda_link_dma_cleanup(hext_stream->link_substream, 3800351a9b8SPierre-Louis Bossart hext_stream, 3812be2caf4SRanjani Sridharan cpu_dai, codec_dai); 382880924caSPierre-Louis Bossart if (ret < 0) 383880924caSPierre-Louis Bossart return ret; 384722cbbfaSPierre-Louis Bossart 3852b009fa0SRanjani Sridharan /* for consistency with TRIGGER_SUSPEND */ 3862b009fa0SRanjani Sridharan if (ops->post_trigger) { 3872b009fa0SRanjani Sridharan ret = ops->post_trigger(sdev, cpu_dai, 3882b009fa0SRanjani Sridharan hext_stream->link_substream, 3892b009fa0SRanjani Sridharan SNDRV_PCM_TRIGGER_SUSPEND); 39023b1944eSPierre-Louis Bossart if (ret < 0) 39123b1944eSPierre-Louis Bossart return ret; 39223b1944eSPierre-Louis Bossart } 393f09e9284SPierre-Louis Bossart } 3942b009fa0SRanjani Sridharan } 395f09e9284SPierre-Louis Bossart 396f09e9284SPierre-Louis Bossart return 0; 397f09e9284SPierre-Louis Bossart } 3984c30004aSRanjani Sridharan 399fdd961e3SKeyon Jie #endif 400fdd961e3SKeyon Jie 40151ec71dcSRanjani Sridharan void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) 40251ec71dcSRanjani Sridharan { 40351ec71dcSRanjani Sridharan int i; 40451ec71dcSRanjani Sridharan 40551ec71dcSRanjani Sridharan for (i = 0; i < ops->num_drv; i++) { 406a4203256SPierre-Louis Bossart #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) 40751ec71dcSRanjani Sridharan if (strstr(ops->drv[i].name, "iDisp") || 40851ec71dcSRanjani Sridharan strstr(ops->drv[i].name, "Analog") || 40951ec71dcSRanjani Sridharan strstr(ops->drv[i].name, "Digital")) 4102b009fa0SRanjani Sridharan ops->drv[i].ops = &hda_dai_ops; 41151ec71dcSRanjani Sridharan #endif 41251ec71dcSRanjani Sridharan } 4132b009fa0SRanjani Sridharan 4142b009fa0SRanjani Sridharan if (sdev->pdata->ipc_type == SOF_INTEL_IPC4 && !hda_use_tplg_nhlt) { 4151da51943SRanjani Sridharan struct sof_ipc4_fw_data *ipc4_data = sdev->private; 4161da51943SRanjani Sridharan 4171da51943SRanjani Sridharan ipc4_data->nhlt = intel_nhlt_init(sdev->dev); 41851ec71dcSRanjani Sridharan } 41951ec71dcSRanjani Sridharan } 42051ec71dcSRanjani Sridharan 4211da51943SRanjani Sridharan void hda_ops_free(struct snd_sof_dev *sdev) 4221da51943SRanjani Sridharan { 4231da51943SRanjani Sridharan if (sdev->pdata->ipc_type == SOF_INTEL_IPC4) { 4241da51943SRanjani Sridharan struct sof_ipc4_fw_data *ipc4_data = sdev->private; 4251da51943SRanjani Sridharan 4261da51943SRanjani Sridharan if (!hda_use_tplg_nhlt) 4271da51943SRanjani Sridharan intel_nhlt_free(ipc4_data->nhlt); 4281da51943SRanjani Sridharan } 4291da51943SRanjani Sridharan } 4301da51943SRanjani Sridharan EXPORT_SYMBOL_NS(hda_ops_free, SND_SOC_SOF_INTEL_HDA_COMMON); 4311da51943SRanjani Sridharan 432fdd961e3SKeyon Jie /* 433fdd961e3SKeyon Jie * common dai driver for skl+ platforms. 434fdd961e3SKeyon Jie * some products who use this DAI array only physically have a subset of 435fdd961e3SKeyon Jie * the DAIs, but no harm is done here by adding the whole set. 436fdd961e3SKeyon Jie */ 437fdd961e3SKeyon Jie struct snd_soc_dai_driver skl_dai[] = { 438fdd961e3SKeyon Jie { 439fdd961e3SKeyon Jie .name = "SSP0 Pin", 440e81d47e9SBard Liao .playback = { 441e81d47e9SBard Liao .channels_min = 1, 442e81d47e9SBard Liao .channels_max = 8, 443e81d47e9SBard Liao }, 444e81d47e9SBard Liao .capture = { 445e81d47e9SBard Liao .channels_min = 1, 446e81d47e9SBard Liao .channels_max = 8, 447e81d47e9SBard Liao }, 448fdd961e3SKeyon Jie }, 449fdd961e3SKeyon Jie { 450fdd961e3SKeyon Jie .name = "SSP1 Pin", 451e81d47e9SBard Liao .playback = { 452e81d47e9SBard Liao .channels_min = 1, 453e81d47e9SBard Liao .channels_max = 8, 454e81d47e9SBard Liao }, 455e81d47e9SBard Liao .capture = { 456e81d47e9SBard Liao .channels_min = 1, 457e81d47e9SBard Liao .channels_max = 8, 458e81d47e9SBard Liao }, 459fdd961e3SKeyon Jie }, 460fdd961e3SKeyon Jie { 461fdd961e3SKeyon Jie .name = "SSP2 Pin", 462e81d47e9SBard Liao .playback = { 463e81d47e9SBard Liao .channels_min = 1, 464e81d47e9SBard Liao .channels_max = 8, 465e81d47e9SBard Liao }, 466e81d47e9SBard Liao .capture = { 467e81d47e9SBard Liao .channels_min = 1, 468e81d47e9SBard Liao .channels_max = 8, 469e81d47e9SBard Liao }, 470fdd961e3SKeyon Jie }, 471fdd961e3SKeyon Jie { 472fdd961e3SKeyon Jie .name = "SSP3 Pin", 473e81d47e9SBard Liao .playback = { 474e81d47e9SBard Liao .channels_min = 1, 475e81d47e9SBard Liao .channels_max = 8, 476e81d47e9SBard Liao }, 477e81d47e9SBard Liao .capture = { 478e81d47e9SBard Liao .channels_min = 1, 479e81d47e9SBard Liao .channels_max = 8, 480e81d47e9SBard Liao }, 481fdd961e3SKeyon Jie }, 482fdd961e3SKeyon Jie { 483fdd961e3SKeyon Jie .name = "SSP4 Pin", 484e81d47e9SBard Liao .playback = { 485e81d47e9SBard Liao .channels_min = 1, 486e81d47e9SBard Liao .channels_max = 8, 487e81d47e9SBard Liao }, 488e81d47e9SBard Liao .capture = { 489e81d47e9SBard Liao .channels_min = 1, 490e81d47e9SBard Liao .channels_max = 8, 491e81d47e9SBard Liao }, 492fdd961e3SKeyon Jie }, 493fdd961e3SKeyon Jie { 494fdd961e3SKeyon Jie .name = "SSP5 Pin", 495e81d47e9SBard Liao .playback = { 496e81d47e9SBard Liao .channels_min = 1, 497e81d47e9SBard Liao .channels_max = 8, 498e81d47e9SBard Liao }, 499e81d47e9SBard Liao .capture = { 500e81d47e9SBard Liao .channels_min = 1, 501e81d47e9SBard Liao .channels_max = 8, 502e81d47e9SBard Liao }, 503fdd961e3SKeyon Jie }, 504fdd961e3SKeyon Jie { 505fdd961e3SKeyon Jie .name = "DMIC01 Pin", 506e81d47e9SBard Liao .capture = { 507e81d47e9SBard Liao .channels_min = 1, 508e81d47e9SBard Liao .channels_max = 4, 509e81d47e9SBard Liao }, 510fdd961e3SKeyon Jie }, 511fdd961e3SKeyon Jie { 512fdd961e3SKeyon Jie .name = "DMIC16k Pin", 513e81d47e9SBard Liao .capture = { 514e81d47e9SBard Liao .channels_min = 1, 515e81d47e9SBard Liao .channels_max = 4, 516e81d47e9SBard Liao }, 517fdd961e3SKeyon Jie }, 518a4203256SPierre-Louis Bossart #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) 519fdd961e3SKeyon Jie { 520fdd961e3SKeyon Jie .name = "iDisp1 Pin", 521e81d47e9SBard Liao .playback = { 522e81d47e9SBard Liao .channels_min = 1, 523e81d47e9SBard Liao .channels_max = 8, 524e81d47e9SBard Liao }, 525fdd961e3SKeyon Jie }, 526fdd961e3SKeyon Jie { 527fdd961e3SKeyon Jie .name = "iDisp2 Pin", 528e81d47e9SBard Liao .playback = { 529e81d47e9SBard Liao .channels_min = 1, 530e81d47e9SBard Liao .channels_max = 8, 531e81d47e9SBard Liao }, 532fdd961e3SKeyon Jie }, 533fdd961e3SKeyon Jie { 534fdd961e3SKeyon Jie .name = "iDisp3 Pin", 535e81d47e9SBard Liao .playback = { 536e81d47e9SBard Liao .channels_min = 1, 537e81d47e9SBard Liao .channels_max = 8, 538e81d47e9SBard Liao }, 539fdd961e3SKeyon Jie }, 540fdd961e3SKeyon Jie { 541e68d6696SSathyanarayana Nujella .name = "iDisp4 Pin", 542e81d47e9SBard Liao .playback = { 543e81d47e9SBard Liao .channels_min = 1, 544e81d47e9SBard Liao .channels_max = 8, 545e81d47e9SBard Liao }, 546e68d6696SSathyanarayana Nujella }, 547e68d6696SSathyanarayana Nujella { 548fdd961e3SKeyon Jie .name = "Analog CPU DAI", 549e81d47e9SBard Liao .playback = { 550e81d47e9SBard Liao .channels_min = 1, 551e81d47e9SBard Liao .channels_max = 16, 552e81d47e9SBard Liao }, 553e81d47e9SBard Liao .capture = { 554e81d47e9SBard Liao .channels_min = 1, 555e81d47e9SBard Liao .channels_max = 16, 556e81d47e9SBard Liao }, 557fdd961e3SKeyon Jie }, 558fdd961e3SKeyon Jie { 559fdd961e3SKeyon Jie .name = "Digital CPU DAI", 560e81d47e9SBard Liao .playback = { 561e81d47e9SBard Liao .channels_min = 1, 562e81d47e9SBard Liao .channels_max = 16, 563e81d47e9SBard Liao }, 564e81d47e9SBard Liao .capture = { 565e81d47e9SBard Liao .channels_min = 1, 566e81d47e9SBard Liao .channels_max = 16, 567e81d47e9SBard Liao }, 568fdd961e3SKeyon Jie }, 569fdd961e3SKeyon Jie { 570fdd961e3SKeyon Jie .name = "Alt Analog CPU DAI", 571e81d47e9SBard Liao .playback = { 572e81d47e9SBard Liao .channels_min = 1, 573e81d47e9SBard Liao .channels_max = 16, 574e81d47e9SBard Liao }, 575e81d47e9SBard Liao .capture = { 576e81d47e9SBard Liao .channels_min = 1, 577e81d47e9SBard Liao .channels_max = 16, 578e81d47e9SBard Liao }, 579fdd961e3SKeyon Jie }, 580fdd961e3SKeyon Jie #endif 581fdd961e3SKeyon Jie }; 582f09e9284SPierre-Louis Bossart 583f09e9284SPierre-Louis Bossart int hda_dsp_dais_suspend(struct snd_sof_dev *sdev) 584f09e9284SPierre-Louis Bossart { 585f09e9284SPierre-Louis Bossart /* 586f09e9284SPierre-Louis Bossart * In the corner case where a SUSPEND happens during a PAUSE, the ALSA core 587f09e9284SPierre-Louis Bossart * does not throw the TRIGGER_SUSPEND. This leaves the DAIs in an unbalanced state. 588f09e9284SPierre-Louis Bossart * Since the component suspend is called last, we can trap this corner case 589f09e9284SPierre-Louis Bossart * and force the DAIs to release their resources. 590f09e9284SPierre-Louis Bossart */ 591a4203256SPierre-Louis Bossart #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) 592f09e9284SPierre-Louis Bossart int ret; 593f09e9284SPierre-Louis Bossart 594f09e9284SPierre-Louis Bossart ret = hda_dai_suspend(sof_to_bus(sdev)); 595f09e9284SPierre-Louis Bossart if (ret < 0) 596f09e9284SPierre-Louis Bossart return ret; 597f09e9284SPierre-Louis Bossart #endif 598f09e9284SPierre-Louis Bossart 599f09e9284SPierre-Louis Bossart return 0; 600f09e9284SPierre-Louis Bossart } 601