mirror of
https://github.com/torvalds/linux.git
synced 2026-08-02 13:22:02 +02:00
ASoC: SOF: Intel: hda-dai: prepare LNL support
Merge series from Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>: The HDAudio support has not changed much since CometLake/2019: the code was reshuffled for IPC4 support and we used hdac_hda for external and Display Audio codec, but the hda-dai code was only used for HDAudio codecs. In the LunarLake architecture, all endpoints (HDaudio, SSP, DMIC, SoundWire) are handled with the HDaudio DMA, which requires us to revisit the definitions of HDA_LINK, and remove the mutual exclusion between NOCODEC and HDA_LINK: we do want the ability to test SSP/DMIC in NOCODEC mode even with an HDA DMA. This code change exposed a number of issues, with a useless .prepare callback, a DAI number mismatch and the need to support SoundWire which is handled by a different component in drivers/soundwire/intel_ace2.c.
This commit is contained in:
commit
3959cd3dcb
|
|
@ -284,8 +284,6 @@ if SND_SOC_SOF_HDA_COMMON
|
|||
|
||||
config SND_SOC_SOF_HDA_LINK
|
||||
bool "SOF support for HDA Links(HDA/HDMI)"
|
||||
depends on SND_SOC_SOF_NOCODEC_SUPPORT=n
|
||||
select SND_SOC_SOF_PROBE_WORK_QUEUE
|
||||
help
|
||||
This adds support for HDA links(HDA/HDMI) with Sound Open Firmware
|
||||
for Intel(R) platforms.
|
||||
|
|
@ -295,6 +293,7 @@ config SND_SOC_SOF_HDA_LINK
|
|||
config SND_SOC_SOF_HDA_AUDIO_CODEC
|
||||
bool "SOF support for HDAudio codecs"
|
||||
depends on SND_SOC_SOF_HDA_LINK
|
||||
select SND_SOC_SOF_PROBE_WORK_QUEUE
|
||||
help
|
||||
This adds support for HDAudio codecs with Sound Open Firmware
|
||||
for Intel(R) platforms.
|
||||
|
|
|
|||
|
|
@ -70,9 +70,14 @@ void sof_hda_bus_init(struct snd_sof_dev *sdev, struct device *dev)
|
|||
{
|
||||
struct hdac_bus *bus = sof_to_bus(sdev);
|
||||
|
||||
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK)
|
||||
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
|
||||
snd_hdac_ext_bus_init(bus, dev, &bus_core_ops, sof_hda_ext_ops);
|
||||
#else /* CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC */
|
||||
#else
|
||||
snd_hdac_ext_bus_init(bus, dev, NULL, NULL);
|
||||
#endif
|
||||
#else
|
||||
|
||||
memset(bus, 0, sizeof(*bus));
|
||||
bus->dev = dev;
|
||||
|
||||
|
|
@ -87,12 +92,12 @@ void sof_hda_bus_init(struct snd_sof_dev *sdev, struct device *dev)
|
|||
bus->idx = 0;
|
||||
|
||||
spin_lock_init(&bus->reg_lock);
|
||||
#endif /* CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC */
|
||||
#endif /* CONFIG_SND_SOC_SOF_HDA_LINK */
|
||||
}
|
||||
|
||||
void sof_hda_bus_exit(struct snd_sof_dev *sdev)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
|
||||
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK)
|
||||
struct hdac_bus *bus = sof_to_bus(sdev);
|
||||
|
||||
snd_hdac_ext_bus_exit(bus);
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
#include "hda.h"
|
||||
|
||||
/* These ops are only applicable for the HDA DAI's in their current form */
|
||||
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
|
||||
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK)
|
||||
/*
|
||||
* This function checks if the host dma channel corresponding
|
||||
* to the link DMA stream_tag argument is assigned to one
|
||||
|
|
@ -350,7 +350,7 @@ static const struct hda_dai_widget_dma_ops hda_dspless_dma_ops = {
|
|||
const struct hda_dai_widget_dma_ops *
|
||||
hda_select_dai_widget_ops(struct snd_sof_dev *sdev, struct snd_sof_widget *swidget)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
|
||||
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK)
|
||||
struct snd_sof_dai *sdai;
|
||||
|
||||
if (sdev->dspless_mode_selected)
|
||||
|
|
|
|||
|
|
@ -27,20 +27,26 @@ 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");
|
||||
|
||||
static struct snd_sof_dev *widget_to_sdev(struct snd_soc_dapm_widget *w)
|
||||
{
|
||||
struct snd_sof_widget *swidget = w->dobj.private;
|
||||
struct snd_soc_component *component = swidget->scomp;
|
||||
|
||||
return snd_soc_component_get_drvdata(component);
|
||||
}
|
||||
|
||||
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_soc_component *component;
|
||||
struct snd_sof_dev *sdev;
|
||||
int ret;
|
||||
|
||||
if (!swidget)
|
||||
return 0;
|
||||
|
||||
component = swidget->scomp;
|
||||
sdev = snd_soc_component_get_drvdata(component);
|
||||
sdev = widget_to_sdev(w);
|
||||
tplg_ops = sof_ipc_get_ops(sdev, tplg);
|
||||
|
||||
if (tplg_ops && tplg_ops->dai_config) {
|
||||
|
|
@ -55,16 +61,26 @@ int hda_dai_config(struct snd_soc_dapm_widget *w, unsigned int flags,
|
|||
return 0;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
|
||||
#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_dev *sdev = snd_soc_component_get_drvdata(cpu_dai->component);
|
||||
struct snd_sof_widget *swidget = w->dobj.private;
|
||||
struct snd_sof_dev *sdev;
|
||||
struct snd_sof_dai *sdai;
|
||||
|
||||
sdev = widget_to_sdev(w);
|
||||
|
||||
/*
|
||||
* The swidget parameter of hda_select_dai_widget_ops() is ignored in
|
||||
* case of DSPless mode
|
||||
|
|
@ -96,14 +112,16 @@ static int hda_link_dma_cleanup(struct snd_pcm_substream *substream,
|
|||
struct snd_soc_dai *cpu_dai,
|
||||
struct snd_soc_dai *codec_dai)
|
||||
{
|
||||
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(cpu_dai->component);
|
||||
const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai);
|
||||
struct hdac_stream *hstream = &hext_stream->hstream;
|
||||
struct hdac_bus *bus = hstream->bus;
|
||||
struct sof_intel_hda_stream *hda_stream;
|
||||
struct hdac_ext_link *hlink;
|
||||
struct snd_sof_dev *sdev;
|
||||
int stream_tag;
|
||||
|
||||
sdev = dai_to_sdev(substream, cpu_dai);
|
||||
|
||||
hlink = snd_hdac_ext_bus_get_hlink_by_name(bus, codec_dai->component->name);
|
||||
if (!hlink)
|
||||
return -EINVAL;
|
||||
|
|
@ -140,7 +158,7 @@ static int hda_link_dma_hw_params(struct snd_pcm_substream *substream,
|
|||
unsigned int link_bps;
|
||||
int stream_tag;
|
||||
|
||||
sdev = snd_soc_component_get_drvdata(cpu_dai->component);
|
||||
sdev = dai_to_sdev(substream, cpu_dai);
|
||||
bus = sof_to_bus(sdev);
|
||||
|
||||
hlink = snd_hdac_ext_bus_get_hlink_by_name(bus, codec_dai->component->name);
|
||||
|
|
@ -188,21 +206,14 @@ static int hda_link_dma_hw_params(struct snd_pcm_substream *substream,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int hda_link_dma_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai)
|
||||
static int __maybe_unused hda_dai_hw_free(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
|
||||
int stream = substream->stream;
|
||||
|
||||
return hda_link_dma_hw_params(substream, &rtd->dpcm[stream].hw_params, cpu_dai);
|
||||
}
|
||||
|
||||
static int hda_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(cpu_dai->component);
|
||||
const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, cpu_dai);
|
||||
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
|
||||
struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0);
|
||||
struct hdac_ext_stream *hext_stream;
|
||||
struct snd_sof_dev *sdev = dai_to_sdev(substream, cpu_dai);
|
||||
|
||||
if (!ops) {
|
||||
dev_err(sdev->dev, "DAI widget ops not set\n");
|
||||
|
|
@ -216,16 +227,16 @@ static int hda_dai_hw_free(struct snd_pcm_substream *substream, struct snd_soc_d
|
|||
return hda_link_dma_cleanup(substream, hext_stream, cpu_dai, codec_dai);
|
||||
}
|
||||
|
||||
static int hda_dai_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params,
|
||||
struct snd_soc_dai *dai)
|
||||
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_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, substream->stream);
|
||||
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
|
||||
const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai);
|
||||
struct hdac_ext_stream *hext_stream;
|
||||
struct snd_sof_dai_config_data data = { 0 };
|
||||
unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS;
|
||||
struct snd_sof_dev *sdev = widget_to_sdev(w);
|
||||
int ret;
|
||||
|
||||
if (!ops) {
|
||||
|
|
@ -249,50 +260,25 @@ static int hda_dai_hw_params(struct snd_pcm_substream *substream,
|
|||
return hda_dai_config(w, flags, &data);
|
||||
}
|
||||
|
||||
static int hda_dai_prepare(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_soc_dapm_widget *w = snd_soc_dai_get_widget(dai, substream->stream);
|
||||
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
|
||||
const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai);
|
||||
struct hdac_ext_stream *hext_stream;
|
||||
struct snd_sof_dai_config_data data = { 0 };
|
||||
unsigned int flags = SOF_DAI_CONFIG_FLAGS_HW_PARAMS;
|
||||
int ret;
|
||||
|
||||
hext_stream = ops->get_hext_stream(sdev, dai, substream);
|
||||
if (hext_stream && hext_stream->link_prepared)
|
||||
return 0;
|
||||
|
||||
dev_dbg(sdev->dev, "prepare stream dir %d\n", substream->stream);
|
||||
|
||||
ret = hda_link_dma_prepare(substream, 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);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 hda_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai)
|
||||
static int __maybe_unused hda_dai_trigger(struct snd_pcm_substream *substream, int cmd,
|
||||
struct snd_soc_dai *dai)
|
||||
{
|
||||
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(dai->component);
|
||||
const struct hda_dai_widget_dma_ops *ops = hda_dai_get_ops(substream, dai);
|
||||
struct hdac_ext_stream *hext_stream;
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
struct snd_soc_dai *codec_dai;
|
||||
struct snd_sof_dev *sdev;
|
||||
int ret;
|
||||
|
||||
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;
|
||||
|
|
@ -333,6 +319,16 @@ static int hda_dai_trigger(struct snd_pcm_substream *substream, int cmd, struct
|
|||
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 = asoc_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,
|
||||
|
|
@ -340,6 +336,8 @@ static const struct snd_soc_dai_ops hda_dai_ops = {
|
|||
.prepare = hda_dai_prepare,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
static int hda_dai_suspend(struct hdac_bus *bus)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
|
|
@ -372,7 +370,7 @@ static int hda_dai_suspend(struct hdac_bus *bus)
|
|||
codec_dai = asoc_rtd_to_codec(rtd, 0);
|
||||
w = snd_soc_dai_get_widget(cpu_dai, hdac_stream(hext_stream)->direction);
|
||||
swidget = w->dobj.private;
|
||||
sdev = snd_soc_component_get_drvdata(swidget->scomp);
|
||||
sdev = widget_to_sdev(w);
|
||||
sdai = swidget->private;
|
||||
ops = sdai->platform_private;
|
||||
|
||||
|
|
@ -588,7 +586,7 @@ int hda_dsp_dais_suspend(struct snd_sof_dev *sdev)
|
|||
* 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_AUDIO_CODEC)
|
||||
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_LINK)
|
||||
int ret;
|
||||
|
||||
ret = hda_dai_suspend(sof_to_bus(sdev));
|
||||
|
|
|
|||
|
|
@ -414,7 +414,7 @@
|
|||
(HDA_DSP_BDL_SIZE / sizeof(struct sof_intel_dsp_bdl))
|
||||
|
||||
/* Number of DAIs */
|
||||
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA)
|
||||
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_AUDIO_CODEC)
|
||||
#define SOF_SKL_NUM_DAIS 15
|
||||
#else
|
||||
#define SOF_SKL_NUM_DAIS 8
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user