ASoC: Intel: avs: Update stream status in a separate thread
authorAmadeusz Sławiński <amadeuszx.slawinski@linux.intel.com>
Tue, 8 Oct 2024 08:37:58 +0000 (10:37 +0200)
committerMark Brown <broonie@kernel.org>
Tue, 8 Oct 2024 09:49:11 +0000 (10:49 +0100)
Function snd_pcm_period_elapsed() is part of sequence servicing HDAudio
stream IRQs. It's called under Global Interrupt Enable (GIE) disabled -
no HDAudio interrupts will be raised. At the same time, the function may
end up calling __snd_pcm_xrun() or snd_pcm_drain_done(). On the
avs-driver side, this translates to IPCs and as GIE is disabled, these
will never complete successfully.

Improve system stability by scheduling stream-IRQ handling in a separate
thread.

Signed-off-by: Amadeusz Sławiński <amadeuszx.slawinski@linux.intel.com>
Reviewed-by: Cezary Rojewski <cezary.rojewski@intel.com>
Link: https://patch.msgid.link/20241008083758.756578-1-amadeuszx.slawinski@linux.intel.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/intel/avs/core.c
sound/soc/intel/avs/pcm.c
sound/soc/intel/avs/pcm.h [new file with mode: 0644]

index da7bac09acb4eba67e563f48593a34f633a87d95..73d4bde9b2f78839ad220c822be39bd968e6f1ac 100644 (file)
@@ -28,6 +28,7 @@
 #include "avs.h"
 #include "cldma.h"
 #include "messages.h"
+#include "pcm.h"
 
 static u32 pgctl_mask = AZX_PGCTL_LSRMD_MASK;
 module_param(pgctl_mask, uint, 0444);
@@ -247,7 +248,7 @@ static void hdac_stream_update_pos(struct hdac_stream *stream, u64 buffer_size)
 static void hdac_update_stream(struct hdac_bus *bus, struct hdac_stream *stream)
 {
        if (stream->substream) {
-               snd_pcm_period_elapsed(stream->substream);
+               avs_period_elapsed(stream->substream);
        } else if (stream->cstream) {
                u64 buffer_size = stream->cstream->runtime->buffer_size;
 
index afc0fc74cf9419f973c40314e6f302f23fcb9c1b..4af81158035681b5b8ecfd7efef041efcc719f36 100644 (file)
@@ -16,6 +16,7 @@
 #include <sound/soc-component.h>
 #include "avs.h"
 #include "path.h"
+#include "pcm.h"
 #include "topology.h"
 #include "../../codecs/hda.h"
 
@@ -30,6 +31,7 @@ struct avs_dma_data {
                struct hdac_ext_stream *host_stream;
        };
 
+       struct work_struct period_elapsed_work;
        struct snd_pcm_substream *substream;
 };
 
@@ -56,6 +58,22 @@ avs_dai_find_path_template(struct snd_soc_dai *dai, bool is_fe, int direction)
        return dw->priv;
 }
 
+static void avs_period_elapsed_work(struct work_struct *work)
+{
+       struct avs_dma_data *data = container_of(work, struct avs_dma_data, period_elapsed_work);
+
+       snd_pcm_period_elapsed(data->substream);
+}
+
+void avs_period_elapsed(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+       struct snd_soc_dai *dai = snd_soc_rtd_to_cpu(rtd, 0);
+       struct avs_dma_data *data = snd_soc_dai_get_dma_data(dai, substream);
+
+       schedule_work(&data->period_elapsed_work);
+}
+
 static int avs_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
@@ -77,6 +95,7 @@ static int avs_dai_startup(struct snd_pcm_substream *substream, struct snd_soc_d
        data->substream = substream;
        data->template = template;
        data->adev = adev;
+       INIT_WORK(&data->period_elapsed_work, avs_period_elapsed_work);
        snd_soc_dai_set_dma_data(dai, substream, data);
 
        if (rtd->dai_link->ignore_suspend)
diff --git a/sound/soc/intel/avs/pcm.h b/sound/soc/intel/avs/pcm.h
new file mode 100644 (file)
index 0000000..0f3615c
--- /dev/null
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright(c) 2024 Intel Corporation
+ *
+ * Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+ *          Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+ */
+
+#ifndef __SOUND_SOC_INTEL_AVS_PCM_H
+#define __SOUND_SOC_INTEL_AVS_PCM_H
+
+#include <sound/pcm.h>
+
+void avs_period_elapsed(struct snd_pcm_substream *substream);
+
+#endif