From 6265e3db57b272e038f6a09d3be49769b1357552 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Wed, 3 Apr 2019 17:32:16 +0800 Subject: [PATCH 1/3] ASoC:sof: fix FW loading failure in resuming occasion This issue is found on WHL with pulse audio enabled. Pulse audio would try to capture stream at booting stage, then close the capture stream. This makes HDA controller hang. The root cause is: DMA trace would use host dma channel one, and capture stream would use link dma channel one. DMA trace would set decouple mode for host dma channel one and link dma channel one. Capture would also do as DMA trace in hw_params function, but set couple mode for these two dma when pcm is closed. However, DMA trace needs decouple mode, so HDA controller hangs. And another potential risk is when applying coupled and decopuled mode for stream setup, this would break some working capture streams. This patch refines the host stream allocation function to make sure the allocated host stream would not affect other link streams. Signed-off-by: Rander Wang --- sound/soc/sof/intel/hda-loader.c | 2 +- sound/soc/sof/intel/hda-pcm.c | 2 +- sound/soc/sof/intel/hda-stream.c | 60 ++++++++++++++++++++++++++++++-- sound/soc/sof/intel/hda-trace.c | 1 + sound/soc/sof/intel/hda.h | 4 ++- 5 files changed, 64 insertions(+), 5 deletions(-) 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..ac5c4a61a8433e 100644 --- a/sound/soc/sof/intel/hda-trace.c +++ b/sound/soc/sof/intel/hda-trace.c @@ -43,6 +43,7 @@ int hda_dsp_trace_init(struct snd_sof_dev *sdev, u32 *stream_tag) int ret; hda->dtrace_stream = hda_dsp_stream_get(sdev, + NULL, SNDRV_PCM_STREAM_CAPTURE); if (!hda->dtrace_stream) { 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, From d0036f2196388fe229e4a5af886a18dac0f20df8 Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Tue, 16 Apr 2019 16:32:19 +0800 Subject: [PATCH 2/3] ASoC: SOF: trace: reserve last stream for dma trace If dma trace uses the first stream, it is hard to make host dma and link dma channel matched. Signed-off-by: Rander Wang --- sound/soc/sof/intel/hda-trace.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/sound/soc/sof/intel/hda-trace.c b/sound/soc/sof/intel/hda-trace.c index ac5c4a61a8433e..a49dfe61e6c85d 100644 --- a/sound/soc/sof/intel/hda-trace.c +++ b/sound/soc/sof/intel/hda-trace.c @@ -40,18 +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, - NULL, - 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; /* From fe14bf90a0b6b49da417b652c189846b70c2430a Mon Sep 17 00:00:00 2001 From: Rander Wang Date: Tue, 16 Apr 2019 13:32:29 +0800 Subject: [PATCH 3/3] ASoC: sof: hda: remove coupled mode setting Coupled mode setting may break other running streams. For sof, only decoupled mode is needed, so hda stream will be never set to coupled mode. And the code style is refined in this patch. Signed-off-by: Rander Wang --- sound/soc/sof/intel/hda-dai.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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;