diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c index 830328af19c5ef..a313c0147295c6 100644 --- a/sound/soc/sof/intel/hda-dai.c +++ b/sound/soc/sof/intel/hda-dai.c @@ -245,12 +245,12 @@ static int hda_link_hw_free(struct snd_pcm_substream *substream, memset(&pcm_substream, 0, sizeof(pcm_substream)); if (substream) { + link_dev = snd_soc_dai_get_dma_data(dai, substream); hstream = substream->runtime->private_data; - bus = hstream->bus; rtd = snd_pcm_substream_chip(substream); - link_dev = snd_soc_dai_get_dma_data(dai, substream); - snd_hdac_ext_stream_decouple(bus, link_dev, false); name = rtd->codec_dai->component->name; + bus = hstream->bus; + link = snd_hdac_ext_bus_get_link(bus, name); if (!link) return -EINVAL; diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c index 6a44bc349e44ce..8d18d0971ccaca 100644 --- a/sound/soc/sof/intel/hda-loader.c +++ b/sound/soc/sof/intel/hda-loader.c @@ -37,7 +37,7 @@ static int cl_stream_prepare(struct snd_sof_dev *sdev, unsigned int format, return -EINVAL; } - dsp_stream = hda_dsp_stream_get(sdev, direction); + dsp_stream = hda_dsp_stream_get(sdev, NULL, direction); if (!dsp_stream) { dev_err(sdev->dev, "error: no stream available\n"); diff --git a/sound/soc/sof/intel/hda-pcm.c b/sound/soc/sof/intel/hda-pcm.c index 5714a79fbe1a16..e91254872e0520 100644 --- a/sound/soc/sof/intel/hda-pcm.c +++ b/sound/soc/sof/intel/hda-pcm.c @@ -208,7 +208,7 @@ int hda_dsp_pcm_open(struct snd_sof_dev *sdev, struct hdac_ext_stream *dsp_stream; int direction = substream->stream; - dsp_stream = hda_dsp_stream_get(sdev, direction); + dsp_stream = hda_dsp_stream_get(sdev, substream, direction); if (!dsp_stream) { dev_err(sdev->dev, "error: no stream available\n"); diff --git a/sound/soc/sof/intel/hda-stream.c b/sound/soc/sof/intel/hda-stream.c index 6290b2df5e621d..22f602623fd657 100644 --- a/sound/soc/sof/intel/hda-stream.c +++ b/sound/soc/sof/intel/hda-stream.c @@ -150,9 +150,61 @@ int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev, return 0; } +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) +/* + * return 1 for two cases + * (1) for a free host stream, check whether the matched + * (with the same stream tag number) link stream is free. + * (2) if the matched link stream is used, check whether + * this is used by one BE of this FE pcm. + */ +static int check_link_stream(struct snd_pcm_substream *substream, + struct hdac_stream *hstream) +{ + struct snd_soc_pcm_runtime *fe = substream->private_data; + int direction = substream->stream; + struct snd_soc_pcm_runtime *be_rtd; + struct hdac_ext_stream *link_dev; + struct hdac_ext_stream *stream; + struct snd_soc_dai *cpu_dai; + struct snd_soc_dpcm *dpcm; + + stream = stream_to_hdac_ext_stream(hstream); + + /* this link stream is free */ + if (!stream->link_locked) + return 1; + + for_each_dpcm_be(fe, direction, dpcm) { + struct snd_soc_pcm_runtime *be = dpcm->be; + struct snd_pcm_substream *be_substream = + snd_soc_dpcm_get_substream(be, direction); + + if (!be_substream) + continue; + + if (be->dpcm[direction].state != SND_SOC_DPCM_STATE_OPEN) + continue; + + be_rtd = be_substream->private_data; + cpu_dai = be_rtd->cpu_dai; + link_dev = snd_soc_dai_get_dma_data(cpu_dai, substream); + + /* one BE of this FE uses the same stream tag as this FE */ + if (link_dev && hstream->stream_tag == + hdac_stream(link_dev)->stream_tag) + return 1; + } + + return 0; +} +#endif + /* get next unused stream */ struct hdac_ext_stream * -hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction) +hda_dsp_stream_get(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + int direction) { struct hdac_bus *bus = sof_to_bus(sdev); struct hdac_ext_stream *stream = NULL; @@ -160,9 +212,13 @@ hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction) spin_lock_irq(&bus->reg_lock); - /* get an unused stream */ list_for_each_entry(s, &bus->stream_list, list) { if (s->direction == direction && !s->opened) { +#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA) + if (substream && + !check_link_stream(substream, s)) + continue; +#endif s->opened = true; stream = stream_to_hdac_ext_stream(s); break; diff --git a/sound/soc/sof/intel/hda-trace.c b/sound/soc/sof/intel/hda-trace.c index 33b23bd6a01e1b..a49dfe61e6c85d 100644 --- a/sound/soc/sof/intel/hda-trace.c +++ b/sound/soc/sof/intel/hda-trace.c @@ -40,17 +40,25 @@ static int hda_dsp_trace_prepare(struct snd_sof_dev *sdev) int hda_dsp_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag) { struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; + struct hdac_bus *bus = sof_to_bus(sdev); + struct hdac_stream *hstream = NULL; int ret; - hda->dtrace_stream = hda_dsp_stream_get(sdev, - SNDRV_PCM_STREAM_CAPTURE); + /* dma trace uses the last stream */ + list_for_each_entry_reverse(hstream, &bus->stream_list, list) { + if (hstream->direction == SNDRV_PCM_STREAM_CAPTURE) { + hstream->opened = true; + break; + } + } - if (!hda->dtrace_stream) { + if (!hstream) { dev_err(sdev->dev, "error: no available capture stream for DMA trace\n"); return -ENODEV; } + hda->dtrace_stream = stream_to_hdac_ext_stream(hstream); *stream_tag = hda->dtrace_stream->hstream.stream_tag; /* diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index e49d9c92dd1c8b..e724d431783e03 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -462,7 +462,9 @@ int hda_dsp_stream_setup_bdl(struct snd_sof_dev *sdev, struct hdac_stream *stream); struct hdac_ext_stream * - hda_dsp_stream_get(struct snd_sof_dev *sdev, int direction); + hda_dsp_stream_get(struct snd_sof_dev *sdev, + struct snd_pcm_substream *substream, + int direction); int hda_dsp_stream_put(struct snd_sof_dev *sdev, int direction, int stream_tag); int hda_dsp_stream_spib_config(struct snd_sof_dev *sdev, struct hdac_ext_stream *stream,