Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Pierre-Louis Bossart | 1575 | 39.54% | 46 | 47.92% |
Ranjani Sridharan | 1307 | 32.81% | 27 | 28.12% |
Keyon Jie | 462 | 11.60% | 1 | 1.04% |
Bard Liao | 309 | 7.76% | 7 | 7.29% |
Cezary Rojewski | 186 | 4.67% | 1 | 1.04% |
Peter Ujfalusi | 62 | 1.56% | 3 | 3.12% |
Liam Girdwood | 26 | 0.65% | 3 | 3.12% |
Rander Wang | 19 | 0.48% | 2 | 2.08% |
Kuninori Morimoto | 13 | 0.33% | 3 | 3.12% |
Kai Vehmanen | 13 | 0.33% | 1 | 1.04% |
Sathyanarayana Nujella | 9 | 0.23% | 1 | 1.04% |
Jaska Uimonen | 2 | 0.05% | 1 | 1.04% |
Total | 3983 | 96 |
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) // // This file is provided under a dual BSD/GPLv2 license. When using or // redistributing this file, you may do so under either license. // // Copyright(c) 2018 Intel Corporation // // Authors: Keyon Jie <yang.jie@linux.intel.com> // #include <sound/pcm_params.h> #include <sound/hdaudio_ext.h> #include <sound/hda-mlink.h> #include <sound/hda_register.h> #include <sound/intel-nhlt.h> #include <sound/sof/ipc4/header.h> #include <uapi/sound/sof/header.h> #include "../ipc4-priv.h" #include "../ipc4-topology.h" #include "../sof-priv.h" #include "../sof-audio.h" #include "hda.h" /* * The default method is to fetch NHLT from BIOS. With this parameter set * it is possible to override that with NHLT in the SOF topology manifest. */ static bool hda_use_tplg_nhlt; module_param_named(sof_use_tplg_nhlt, hda_use_tplg_nhlt, bool, 0444); MODULE_PARM_DESC(sof_use_tplg_nhlt, "SOF topology nhlt override"); int hda_dai_config(struct snd_soc_dapm_widget *w, unsigned int flags, struct snd_sof_dai_config_data *data) { struct snd_sof_widget *swidget = w->dobj.private; const struct sof_ipc_tplg_ops *tplg_ops; struct snd_sof_dev *sdev; int ret; if (!swidget) return 0; sdev = widget_to_sdev(w); tplg_ops = sof_ipc_get_ops(sdev, tplg); if (tplg_ops && tplg_ops->dai_config) { ret = tplg_ops->dai_config(sdev, swidget, flags, data); if (ret < 0) { dev_err(sdev->dev, "DAI config with flags %x failed for widget %s\n", flags, w->name); return ret; } } return 0; } EXPORT_SYMBOL_NS(hda_dai_config, SND_SOC_SOF_INTEL_HDA_COMMON); #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK) static struct snd_sof_dev *dai_to_sdev(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream); return widget_to_sdev(w); } static const struct hda_dai_widget_dma_ops * hda_dai_get_ops(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream); struct snd_sof_widget *swidget = w->dobj.private; struct snd_sof_dev *sdev; struct snd_sof_dai *sdai; sdev = widget_to_sdev(w); if (!swidget) { dev_err(sdev->dev, "%s: swidget is NULL\n", __func__); return NULL; } if (sdev->dspless_mode_selected) return hda_select_dai_widget_ops(sdev, swidget); sdai = swidget->private; /* select and set the DAI widget ops if not set already */ if (!sdai->platform_private) { const struct hda_dai_widget_dma_ops *ops = hda_select_dai_widget_ops(sdev, swidget); if (!ops) return NULL; /* check if mandatory ops are set */ if (!ops || !ops->get_hext_stream) return NULL; sdai->platform_private = ops; } return sdai->platform_private; } int hda_link_dma_cleanup(struct snd_pcm_substream *substream, struct hdac_ext_stream *hext_stream, struct snd_soc_dai *cpu_dai) { const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai); struct sof_intel_hda_stream *hda_stream; struct hdac_ext_link *hlink; struct snd_sof_dev *sdev; int stream_tag; if (!ops) { dev_err(cpu_dai->dev, "DAI widget ops not set\n"); return -EINVAL; } sdev = dai_to_sdev(substream, cpu_dai); hlink = ops->get_hlink(sdev, substream); if (!hlink) return -EINVAL; if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { stream_tag = hdac_stream(hext_stream)->stream_tag; snd_hdac_ext_bus_link_clear_stream_id(hlink, stream_tag); } if (ops->release_hext_stream) ops->release_hext_stream(sdev, cpu_dai, substream); hext_stream->link_prepared = 0; /* free the host DMA channel reserved by hostless streams */ hda_stream = hstream_to_sof_hda_stream(hext_stream); hda_stream->host_reserved = 0; return 0; } static int hda_link_dma_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai) { const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai); struct hdac_ext_stream *hext_stream; struct hdac_stream *hstream; struct hdac_ext_link *hlink; struct snd_sof_dev *sdev; int stream_tag; if (!ops) { dev_err(cpu_dai->dev, "DAI widget ops not set\n"); return -EINVAL; } sdev = dai_to_sdev(substream, cpu_dai); hlink = ops->get_hlink(sdev, substream); if (!hlink) return -EINVAL; hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream); if (!hext_stream) { if (ops->assign_hext_stream) hext_stream = ops->assign_hext_stream(sdev, cpu_dai, substream); } if (!hext_stream) return -EBUSY; hstream = &hext_stream->hstream; stream_tag = hstream->stream_tag; if (hext_stream->hstream.direction == SNDRV_PCM_STREAM_PLAYBACK) snd_hdac_ext_bus_link_set_stream_id(hlink, stream_tag); /* set the hdac_stream in the codec dai */ if (ops->codec_dai_set_stream) ops->codec_dai_set_stream(sdev, substream, hstream); if (ops->reset_hext_stream) ops->reset_hext_stream(sdev, hext_stream); if (ops->calc_stream_format && ops->setup_hext_stream) { unsigned int format_val = ops->calc_stream_format(sdev, substream, params); ops->setup_hext_stream(sdev, hext_stream, format_val); } hext_stream->link_prepared = 1; return 0; } static int __maybe_unused hda_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai); struct hdac_ext_stream *hext_stream; struct snd_sof_dev *sdev = dai_to_sdev(substream, cpu_dai); if (!ops) { dev_err(cpu_dai->dev, "DAI widget ops not set\n"); return -EINVAL; } hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream); if (!hext_stream) return 0; return hda_link_dma_cleanup(substream, hext_stream, cpu_dai); } static int __maybe_unused hda_dai_hw_params_data(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai, struct snd_sof_dai_config_data *data, unsigned int flags) { struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, substream->stream); const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai); struct hdac_ext_stream *hext_stream; struct snd_sof_dev *sdev = widget_to_sdev(w); int ret; if (!ops) { dev_err(sdev->dev, "DAI widget ops not set\n"); return -EINVAL; } hext_stream = ops->get_hext_stream(sdev, dai, substream); if (hext_stream && hext_stream->link_prepared) return 0; ret = hda_link_dma_hw_params(substream, params, dai); if (ret < 0) return ret; hext_stream = ops->get_hext_stream(sdev, dai, substream); flags |= SOF_DAI_CONFIG_FLAGS_2_STEP_STOP << SOF_DAI_CONFIG_FLAGS_QUIRK_SHIFT; data->dai_data = hdac_stream(hext_stream)->stream_tag - 1; return hda_dai_config(w, flags, data); } static int __maybe_unused hda_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct snd_sof_dai_config_data data = { 0 }; unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS; return hda_dai_hw_params_data(substream, params, dai, &data, flags); } /* * In contrast to IPC3, the dai trigger in IPC4 mixes pipeline state changes * (over IPC channel) and DMA state change (direct host register changes). */ static int __maybe_unused hda_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai); struct hdac_ext_stream *hext_stream; struct snd_sof_dev *sdev; int ret; if (!ops) { dev_err(dai->dev, "DAI widget ops not set\n"); return -EINVAL; } dev_dbg(dai->dev, "cmd=%d dai %s direction %d\n", cmd, dai->name, substream->stream); sdev = dai_to_sdev(substream, dai); hext_stream = ops->get_hext_stream(sdev, dai, substream); if (!hext_stream) return -EINVAL; if (ops->pre_trigger) { ret = ops->pre_trigger(sdev, dai, substream, cmd); if (ret < 0) return ret; } if (ops->trigger) { ret = ops->trigger(sdev, dai, substream, cmd); if (ret < 0) return ret; } if (ops->post_trigger) { ret = ops->post_trigger(sdev, dai, substream, cmd); if (ret < 0) return ret; } switch (cmd) { case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: ret = hda_link_dma_cleanup(substream, hext_stream, dai); if (ret < 0) { dev_err(sdev->dev, "%s: failed to clean up link DMA\n", __func__); return ret; } break; default: break; } return 0; } #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) static int hda_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); int stream = substream->stream; return hda_dai_hw_params(substream, &rtd->dpcm[stream].hw_params, dai); } static const struct snd_soc_dai_ops hda_dai_ops = { .hw_params = hda_dai_hw_params, .hw_free = hda_dai_hw_free, .trigger = hda_dai_trigger, .prepare = hda_dai_prepare, }; #endif static struct sof_ipc4_copier *widget_to_copier(struct snd_soc_dapm_widget *w) { struct snd_sof_widget *swidget = w->dobj.private; struct snd_sof_dai *sdai = swidget->private; struct sof_ipc4_copier *ipc4_copier = (struct sof_ipc4_copier *)sdai->private; return ipc4_copier; } static int non_hda_dai_hw_params_data(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai, struct snd_sof_dai_config_data *data, unsigned int flags) { struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream); struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct sof_ipc4_dma_config_tlv *dma_config_tlv; const struct hda_dai_widget_dma_ops *ops; struct sof_ipc4_dma_config *dma_config; struct sof_ipc4_copier *ipc4_copier; struct hdac_ext_stream *hext_stream; struct hdac_stream *hstream; struct snd_sof_dev *sdev; struct snd_soc_dai *dai; int cpu_dai_id; int stream_id; int ret; ops = hda_dai_get_ops(substream, cpu_dai); if (!ops) { dev_err(cpu_dai->dev, "DAI widget ops not set\n"); return -EINVAL; } sdev = widget_to_sdev(w); hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream); /* nothing more to do if the link is already prepared */ if (hext_stream && hext_stream->link_prepared) return 0; /* use HDaudio stream handling */ ret = hda_dai_hw_params_data(substream, params, cpu_dai, data, flags); if (ret < 0) { dev_err(cpu_dai->dev, "%s: hda_dai_hw_params_data failed: %d\n", __func__, ret); return ret; } if (sdev->dspless_mode_selected) return 0; /* get stream_id */ hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream); if (!hext_stream) { dev_err(cpu_dai->dev, "%s: no hext_stream found\n", __func__); return -ENODEV; } hstream = &hext_stream->hstream; stream_id = hstream->stream_tag; if (!stream_id) { dev_err(cpu_dai->dev, "%s: no stream_id allocated\n", __func__); return -ENODEV; } /* configure TLV */ ipc4_copier = widget_to_copier(w); for_each_rtd_cpu_dais(rtd, cpu_dai_id, dai) { if (dai == cpu_dai) break; } dma_config_tlv = &ipc4_copier->dma_config_tlv[cpu_dai_id]; dma_config_tlv->type = SOF_IPC4_GTW_DMA_CONFIG_ID; /* dma_config_priv_size is zero */ dma_config_tlv->length = sizeof(dma_config_tlv->dma_config); dma_config = &dma_config_tlv->dma_config; dma_config->dma_method = SOF_IPC4_DMA_METHOD_HDA; dma_config->pre_allocated_by_host = 1; dma_config->dma_channel_id = stream_id - 1; dma_config->stream_id = stream_id; /* * Currently we use a DMA for each device in ALH blob. The device will * be copied in sof_ipc4_prepare_copier_module. */ dma_config->dma_stream_channel_map.device_count = 1; dma_config->dma_priv_config_size = 0; return 0; } static int non_hda_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai) { struct snd_sof_dai_config_data data = { 0 }; unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS; return non_hda_dai_hw_params_data(substream, params, cpu_dai, &data, flags); } static int non_hda_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai) { struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); int stream = substream->stream; return non_hda_dai_hw_params(substream, &rtd->dpcm[stream].hw_params, cpu_dai); } static const struct snd_soc_dai_ops ssp_dai_ops = { .hw_params = non_hda_dai_hw_params, .hw_free = hda_dai_hw_free, .trigger = hda_dai_trigger, .prepare = non_hda_dai_prepare, }; static const struct snd_soc_dai_ops dmic_dai_ops = { .hw_params = non_hda_dai_hw_params, .hw_free = hda_dai_hw_free, .trigger = hda_dai_trigger, .prepare = non_hda_dai_prepare, }; int sdw_hda_dai_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai, int link_id, int intel_alh_id) { struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream); struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream); struct sof_ipc4_dma_config_tlv *dma_config_tlv; struct snd_sof_dai_config_data data = { 0 }; unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS; const struct hda_dai_widget_dma_ops *ops; struct sof_ipc4_dma_config *dma_config; struct sof_ipc4_copier *ipc4_copier; struct hdac_ext_stream *hext_stream; struct snd_soc_dai *dai; struct snd_sof_dev *sdev; bool cpu_dai_found = false; int cpu_dai_id; int ch_mask; int ret; int i; ops = hda_dai_get_ops(substream, cpu_dai); if (!ops) { dev_err(cpu_dai->dev, "DAI widget ops not set\n"); return -EINVAL; } sdev = widget_to_sdev(w); hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream); /* nothing more to do if the link is already prepared */ if (hext_stream && hext_stream->link_prepared) return 0; /* * reset the PCMSyCM registers to handle a prepare callback when the PCM is restarted * due to xruns or after a call to snd_pcm_drain/drop() */ ret = hdac_bus_eml_sdw_map_stream_ch(sof_to_bus(sdev), link_id, cpu_dai->id, 0, 0, substream->stream); if (ret < 0) { dev_err(cpu_dai->dev, "%s: hdac_bus_eml_sdw_map_stream_ch failed %d\n", __func__, ret); return ret; } data.dai_index = (link_id << 8) | cpu_dai->id; data.dai_node_id = intel_alh_id; ret = non_hda_dai_hw_params_data(substream, params, cpu_dai, &data, flags); if (ret < 0) { dev_err(cpu_dai->dev, "%s: non_hda_dai_hw_params failed %d\n", __func__, ret); return ret; } hext_stream = ops->get_hext_stream(sdev, cpu_dai, substream); if (!hext_stream) return -ENODEV; /* * in the case of SoundWire we need to program the PCMSyCM registers. In case * of aggregated devices, we need to define the channel mask for each sublink * by reconstructing the split done in soc-pcm.c */ for_each_rtd_cpu_dais(rtd, cpu_dai_id, dai) { if (dai == cpu_dai) { cpu_dai_found = true; break; } } if (!cpu_dai_found) return -ENODEV; ch_mask = GENMASK(params_channels(params) - 1, 0); ret = hdac_bus_eml_sdw_map_stream_ch(sof_to_bus(sdev), link_id, cpu_dai->id, ch_mask, hdac_stream(hext_stream)->stream_tag, substream->stream); if (ret < 0) { dev_err(cpu_dai->dev, "%s: hdac_bus_eml_sdw_map_stream_ch failed %d\n", __func__, ret); return ret; } if (sdev->dspless_mode_selected) return 0; ipc4_copier = widget_to_copier(w); dma_config_tlv = &ipc4_copier->dma_config_tlv[cpu_dai_id]; dma_config = &dma_config_tlv->dma_config; dma_config->dma_stream_channel_map.mapping[0].device = data.dai_index; dma_config->dma_stream_channel_map.mapping[0].channel_mask = ch_mask; /* * copy the dma_config_tlv to all ipc4_copier in the same link. Because only one copier * will be handled in sof_ipc4_prepare_copier_module. */ for_each_rtd_cpu_dais(rtd, i, dai) { w = snd_soc_dai_get_widget(dai, substream->stream); ipc4_copier = widget_to_copier(w); memcpy(&ipc4_copier->dma_config_tlv[cpu_dai_id], dma_config_tlv, sizeof(*dma_config_tlv)); } return 0; } EXPORT_SYMBOL_NS(sdw_hda_dai_hw_params, SND_SOC_SOF_INTEL_HDA_COMMON); int sdw_hda_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai, int link_id) { struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(cpu_dai, substream->stream); struct snd_sof_dev *sdev; int ret; ret = hda_dai_hw_free(substream, cpu_dai); if (ret < 0) { dev_err(cpu_dai->dev, "%s: non_hda_dai_hw_free failed %d\n", __func__, ret); return ret; } sdev = widget_to_sdev(w); /* in the case of SoundWire we need to reset the PCMSyCM registers */ ret = hdac_bus_eml_sdw_map_stream_ch(sof_to_bus(sdev), link_id, cpu_dai->id, 0, 0, substream->stream); if (ret < 0) { dev_err(cpu_dai->dev, "%s: hdac_bus_eml_sdw_map_stream_ch failed %d\n", __func__, ret); return ret; } return 0; } EXPORT_SYMBOL_NS(sdw_hda_dai_hw_free, SND_SOC_SOF_INTEL_HDA_COMMON); int sdw_hda_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *cpu_dai) { return hda_dai_trigger(substream, cmd, cpu_dai); } EXPORT_SYMBOL_NS(sdw_hda_dai_trigger, SND_SOC_SOF_INTEL_HDA_COMMON); static int hda_dai_suspend(struct hdac_bus *bus) { struct snd_soc_pcm_runtime *rtd; struct hdac_ext_stream *hext_stream; struct hdac_stream *s; int ret; /* set internal flag for BE */ list_for_each_entry(s, &bus->stream_list, list) { hext_stream = stream_to_hdac_ext_stream(s); /* * clear stream. This should already be taken care for running * streams when the SUSPEND trigger is called. But paused * streams do not get suspended, so this needs to be done * explicitly during suspend. */ if (hext_stream->link_substream) { const struct hda_dai_widget_dma_ops *ops; struct snd_sof_widget *swidget; struct snd_soc_dapm_widget *w; struct snd_soc_dai *cpu_dai; struct snd_sof_dev *sdev; struct snd_sof_dai *sdai; rtd = snd_soc_substream_to_rtd(hext_stream->link_substream); cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); w = snd_soc_dai_get_widget(cpu_dai, hdac_stream(hext_stream)->direction); swidget = w->dobj.private; sdev = widget_to_sdev(w); sdai = swidget->private; ops = sdai->platform_private; /* for consistency with TRIGGER_SUSPEND */ if (ops->post_trigger) { ret = ops->post_trigger(sdev, cpu_dai, hext_stream->link_substream, SNDRV_PCM_TRIGGER_SUSPEND); if (ret < 0) return ret; } ret = hda_link_dma_cleanup(hext_stream->link_substream, hext_stream, cpu_dai); if (ret < 0) return ret; } } return 0; } static void ssp_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) { const struct sof_intel_dsp_desc *chip; int i; chip = get_chip_info(sdev->pdata); if (chip->hw_ip_version >= SOF_INTEL_ACE_2_0) { for (i = 0; i < ops->num_drv; i++) { if (strstr(ops->drv[i].name, "SSP")) ops->drv[i].ops = &ssp_dai_ops; } } } static void dmic_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) { const struct sof_intel_dsp_desc *chip; int i; chip = get_chip_info(sdev->pdata); if (chip->hw_ip_version >= SOF_INTEL_ACE_2_0) { for (i = 0; i < ops->num_drv; i++) { if (strstr(ops->drv[i].name, "DMIC")) ops->drv[i].ops = &dmic_dai_ops; } } } #else static inline void ssp_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) {} static inline void dmic_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) {} #endif /* CONFIG_SND_SOC_SOF_HDA_LINK */ void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops) { int i; for (i = 0; i < ops->num_drv; i++) { #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) if (strstr(ops->drv[i].name, "iDisp") || strstr(ops->drv[i].name, "Analog") || strstr(ops->drv[i].name, "Digital")) ops->drv[i].ops = &hda_dai_ops; #endif } ssp_set_dai_drv_ops(sdev, ops); dmic_set_dai_drv_ops(sdev, ops); if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4 && !hda_use_tplg_nhlt) { struct sof_ipc4_fw_data *ipc4_data = sdev->private; ipc4_data->nhlt = intel_nhlt_init(sdev->dev); } } EXPORT_SYMBOL_NS(hda_set_dai_drv_ops, SND_SOC_SOF_INTEL_HDA_COMMON); void hda_ops_free(struct snd_sof_dev *sdev) { if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) { struct sof_ipc4_fw_data *ipc4_data = sdev->private; if (!hda_use_tplg_nhlt) intel_nhlt_free(ipc4_data->nhlt); kfree(sdev->private); sdev->private = NULL; } } EXPORT_SYMBOL_NS(hda_ops_free, SND_SOC_SOF_INTEL_HDA_COMMON); /* * common dai driver for skl+ platforms. * some products who use this DAI array only physically have a subset of * the DAIs, but no harm is done here by adding the whole set. */ struct snd_soc_dai_driver skl_dai[] = { { .name = "SSP0 Pin", .playback = { .channels_min = 1, .channels_max = 8, }, .capture = { .channels_min = 1, .channels_max = 8, }, }, { .name = "SSP1 Pin", .playback = { .channels_min = 1, .channels_max = 8, }, .capture = { .channels_min = 1, .channels_max = 8, }, }, { .name = "SSP2 Pin", .playback = { .channels_min = 1, .channels_max = 8, }, .capture = { .channels_min = 1, .channels_max = 8, }, }, { .name = "SSP3 Pin", .playback = { .channels_min = 1, .channels_max = 8, }, .capture = { .channels_min = 1, .channels_max = 8, }, }, { .name = "SSP4 Pin", .playback = { .channels_min = 1, .channels_max = 8, }, .capture = { .channels_min = 1, .channels_max = 8, }, }, { .name = "SSP5 Pin", .playback = { .channels_min = 1, .channels_max = 8, }, .capture = { .channels_min = 1, .channels_max = 8, }, }, { .name = "DMIC01 Pin", .capture = { .channels_min = 1, .channels_max = 4, }, }, { .name = "DMIC16k Pin", .capture = { .channels_min = 1, .channels_max = 4, }, }, #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC) { .name = "iDisp1 Pin", .playback = { .channels_min = 1, .channels_max = 8, }, }, { .name = "iDisp2 Pin", .playback = { .channels_min = 1, .channels_max = 8, }, }, { .name = "iDisp3 Pin", .playback = { .channels_min = 1, .channels_max = 8, }, }, { .name = "iDisp4 Pin", .playback = { .channels_min = 1, .channels_max = 8, }, }, { .name = "Analog CPU DAI", .playback = { .channels_min = 1, .channels_max = 16, }, .capture = { .channels_min = 1, .channels_max = 16, }, }, { .name = "Digital CPU DAI", .playback = { .channels_min = 1, .channels_max = 16, }, .capture = { .channels_min = 1, .channels_max = 16, }, }, { .name = "Alt Analog CPU DAI", .playback = { .channels_min = 1, .channels_max = 16, }, .capture = { .channels_min = 1, .channels_max = 16, }, }, #endif }; EXPORT_SYMBOL_NS(skl_dai, SND_SOC_SOF_INTEL_HDA_COMMON); int hda_dsp_dais_suspend(struct snd_sof_dev *sdev) { /* * In the corner case where a SUSPEND happens during a PAUSE, the ALSA core * does not throw the TRIGGER_SUSPEND. This leaves the DAIs in an unbalanced state. * Since the component suspend is called last, we can trap this corner case * and force the DAIs to release their resources. */ #if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK) int ret; ret = hda_dai_suspend(sof_to_bus(sdev)); if (ret < 0) return ret; #endif return 0; }
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1