Commit d5263dbb authored by Pierre-Louis Bossart's avatar Pierre-Louis Bossart Committed by Mark Brown

ASoC: SOF: Intel: don't ignore IOC interrupts for non-audio transfers

The HDaudio stream interrupts are ignored unless the stream is PCM or
compressed audio. For alternate non-audio usages, such as code loader
or SoundWire BPT case, the IOC interrupt on the last buffer
transferred is silently ignored.

This patch adds a 'struct completion' for each HDaudio stream. This
capability helps detect if the non-audio data transfers
completed. There is no performance impact for audio streams.

In the code loader case, the code currently starts the DMA and
directly checks if the firmware status changes, without checking if
the DMA succeeded. With a first pass waiting for the DMA to complete,
system validation engineers can gather more precise timing information
on firmware boot time or root-cause boot failures more accurately.

A timeout of 500ms was selected for the code loader DMA. This is an
experimental value which should be more than enough - higher values
would certainly be problematic from a usage/latency perspective.
Signed-off-by: default avatarPierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Reviewed-by: default avatarPéter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: default avatarRander Wang <rander.wang@intel.com>
Reviewed-by: default avatarRanjani Sridharan <ranjani.sridharan@linux.intel.com>
Link: https://msgid.link/r/20240404185448.136157-4-pierre-louis.bossart@linux.intel.comSigned-off-by: default avatarMark Brown <broonie@kernel.org>
parent 6cbf0861
...@@ -226,10 +226,15 @@ int hda_cl_trigger(struct device *dev, struct hdac_ext_stream *hext_stream, int ...@@ -226,10 +226,15 @@ int hda_cl_trigger(struct device *dev, struct hdac_ext_stream *hext_stream, int
struct snd_sof_dev *sdev = dev_get_drvdata(dev); struct snd_sof_dev *sdev = dev_get_drvdata(dev);
struct hdac_stream *hstream = &hext_stream->hstream; struct hdac_stream *hstream = &hext_stream->hstream;
int sd_offset = SOF_STREAM_SD_OFFSET(hstream); int sd_offset = SOF_STREAM_SD_OFFSET(hstream);
struct sof_intel_hda_stream *hda_stream;
/* code loader is special case that reuses stream ops */ /* code loader is special case that reuses stream ops */
switch (cmd) { switch (cmd) {
case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_START:
hda_stream = container_of(hext_stream, struct sof_intel_hda_stream,
hext_stream);
reinit_completion(&hda_stream->ioc);
snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL,
1 << hstream->index, 1 << hstream->index,
1 << hstream->index); 1 << hstream->index);
...@@ -283,19 +288,38 @@ int hda_cl_cleanup(struct device *dev, struct snd_dma_buffer *dmab, ...@@ -283,19 +288,38 @@ int hda_cl_cleanup(struct device *dev, struct snd_dma_buffer *dmab,
} }
EXPORT_SYMBOL_NS(hda_cl_cleanup, SND_SOC_SOF_INTEL_HDA_COMMON); EXPORT_SYMBOL_NS(hda_cl_cleanup, SND_SOC_SOF_INTEL_HDA_COMMON);
#define HDA_CL_DMA_IOC_TIMEOUT_MS 500
int hda_cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream) int hda_cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream)
{ {
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
const struct sof_intel_dsp_desc *chip = hda->desc; const struct sof_intel_dsp_desc *chip = hda->desc;
struct sof_intel_hda_stream *hda_stream;
unsigned long time_left;
unsigned int reg; unsigned int reg;
int ret, status; int ret, status;
hda_stream = container_of(hext_stream, struct sof_intel_hda_stream,
hext_stream);
dev_dbg(sdev->dev, "Code loader DMA starting\n");
ret = hda_cl_trigger(sdev->dev, hext_stream, SNDRV_PCM_TRIGGER_START); ret = hda_cl_trigger(sdev->dev, hext_stream, SNDRV_PCM_TRIGGER_START);
if (ret < 0) { if (ret < 0) {
dev_err(sdev->dev, "error: DMA trigger start failed\n"); dev_err(sdev->dev, "error: DMA trigger start failed\n");
return ret; return ret;
} }
/* Wait for completion of transfer */
time_left = wait_for_completion_timeout(&hda_stream->ioc,
msecs_to_jiffies(HDA_CL_DMA_IOC_TIMEOUT_MS));
if (!time_left) {
dev_err(sdev->dev, "Code loader DMA did not complete\n");
return -ETIMEDOUT;
}
dev_dbg(sdev->dev, "Code loader DMA done, waiting for FW_ENTERED status\n");
status = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR, status = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR,
chip->rom_status_reg, reg, chip->rom_status_reg, reg,
(FSR_TO_STATE_CODE(reg) == FSR_STATE_FW_ENTERED), (FSR_TO_STATE_CODE(reg) == FSR_STATE_FW_ENTERED),
...@@ -311,6 +335,8 @@ int hda_cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream ...@@ -311,6 +335,8 @@ int hda_cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream
dev_err(sdev->dev, dev_err(sdev->dev,
"%s: timeout with rom_status_reg (%#x) read\n", "%s: timeout with rom_status_reg (%#x) read\n",
__func__, chip->rom_status_reg); __func__, chip->rom_status_reg);
} else {
dev_dbg(sdev->dev, "Code loader FW_ENTERED status\n");
} }
ret = hda_cl_trigger(sdev->dev, hext_stream, SNDRV_PCM_TRIGGER_STOP); ret = hda_cl_trigger(sdev->dev, hext_stream, SNDRV_PCM_TRIGGER_STOP);
...@@ -318,6 +344,8 @@ int hda_cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream ...@@ -318,6 +344,8 @@ int hda_cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream
dev_err(sdev->dev, "error: DMA trigger stop failed\n"); dev_err(sdev->dev, "error: DMA trigger stop failed\n");
if (!status) if (!status)
status = ret; status = ret;
} else {
dev_dbg(sdev->dev, "Code loader DMA stopped\n");
} }
return status; return status;
......
...@@ -765,10 +765,25 @@ static bool hda_dsp_stream_check(struct hdac_bus *bus, u32 status) ...@@ -765,10 +765,25 @@ static bool hda_dsp_stream_check(struct hdac_bus *bus, u32 status)
writeb(sd_status, s->sd_addr + SOF_HDA_ADSP_REG_SD_STS); writeb(sd_status, s->sd_addr + SOF_HDA_ADSP_REG_SD_STS);
active = true; active = true;
if ((!s->substream && !s->cstream) || if (!s->running)
!s->running ||
(sd_status & SOF_HDA_CL_DMA_SD_INT_COMPLETE) == 0)
continue; continue;
if ((sd_status & SOF_HDA_CL_DMA_SD_INT_COMPLETE) == 0)
continue;
if (!s->substream && !s->cstream) {
/*
* when no substream is found, the DMA may used for code loading
* or data transfers which can rely on wait_for_completion()
*/
struct sof_intel_hda_stream *hda_stream;
struct hdac_ext_stream *hext_stream;
hext_stream = stream_to_hdac_ext_stream(s);
hda_stream = container_of(hext_stream, struct sof_intel_hda_stream,
hext_stream);
complete(&hda_stream->ioc);
continue;
}
/* Inform ALSA only in case not do that with IPC */ /* Inform ALSA only in case not do that with IPC */
if (s->substream && sof_hda->no_ipc_position) { if (s->substream && sof_hda->no_ipc_position) {
...@@ -880,6 +895,7 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev) ...@@ -880,6 +895,7 @@ int hda_dsp_stream_init(struct snd_sof_dev *sdev)
return -ENOMEM; return -ENOMEM;
hda_stream->sdev = sdev; hda_stream->sdev = sdev;
init_completion(&hda_stream->ioc);
hext_stream = &hda_stream->hext_stream; hext_stream = &hda_stream->hext_stream;
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#ifndef __SOF_INTEL_HDA_H #ifndef __SOF_INTEL_HDA_H
#define __SOF_INTEL_HDA_H #define __SOF_INTEL_HDA_H
#include <linux/completion.h>
#include <linux/soundwire/sdw.h> #include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_intel.h> #include <linux/soundwire/sdw_intel.h>
#include <sound/compress_driver.h> #include <sound/compress_driver.h>
...@@ -559,6 +560,7 @@ struct sof_intel_hda_stream { ...@@ -559,6 +560,7 @@ struct sof_intel_hda_stream {
struct sof_intel_stream sof_intel_stream; struct sof_intel_stream sof_intel_stream;
int host_reserved; /* reserve host DMA channel */ int host_reserved; /* reserve host DMA channel */
u32 flags; u32 flags;
struct completion ioc;
}; };
#define hstream_to_sof_hda_stream(hstream) \ #define hstream_to_sof_hda_stream(hstream) \
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment