Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Reviewed-by: Péter Ujfalusi <peter.ujfalusi@linux.intel.com>
Reviewed-by: Rander Wang <rander.wang@intel.com>
Reviewed-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Link: https://msgid.link/r/20240404185448.136157-4-pierre-louis.bossart@linux.intel.com
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

Pierre-Louis Bossart and committed by
Mark Brown
d5263dbb 6cbf0861

+49 -3
+28
sound/soc/sof/intel/hda-loader.c
··· 226 226 struct snd_sof_dev *sdev = dev_get_drvdata(dev); 227 227 struct hdac_stream *hstream = &hext_stream->hstream; 228 228 int sd_offset = SOF_STREAM_SD_OFFSET(hstream); 229 + struct sof_intel_hda_stream *hda_stream; 229 230 230 231 /* code loader is special case that reuses stream ops */ 231 232 switch (cmd) { 232 233 case SNDRV_PCM_TRIGGER_START: 234 + hda_stream = container_of(hext_stream, struct sof_intel_hda_stream, 235 + hext_stream); 236 + reinit_completion(&hda_stream->ioc); 237 + 233 238 snd_sof_dsp_update_bits(sdev, HDA_DSP_HDA_BAR, SOF_HDA_INTCTL, 234 239 1 << hstream->index, 235 240 1 << hstream->index); ··· 288 283 } 289 284 EXPORT_SYMBOL_NS(hda_cl_cleanup, SND_SOC_SOF_INTEL_HDA_COMMON); 290 285 286 + #define HDA_CL_DMA_IOC_TIMEOUT_MS 500 287 + 291 288 int hda_cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream) 292 289 { 293 290 struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; 294 291 const struct sof_intel_dsp_desc *chip = hda->desc; 292 + struct sof_intel_hda_stream *hda_stream; 293 + unsigned long time_left; 295 294 unsigned int reg; 296 295 int ret, status; 296 + 297 + hda_stream = container_of(hext_stream, struct sof_intel_hda_stream, 298 + hext_stream); 299 + 300 + dev_dbg(sdev->dev, "Code loader DMA starting\n"); 297 301 298 302 ret = hda_cl_trigger(sdev->dev, hext_stream, SNDRV_PCM_TRIGGER_START); 299 303 if (ret < 0) { 300 304 dev_err(sdev->dev, "error: DMA trigger start failed\n"); 301 305 return ret; 302 306 } 307 + 308 + /* Wait for completion of transfer */ 309 + time_left = wait_for_completion_timeout(&hda_stream->ioc, 310 + msecs_to_jiffies(HDA_CL_DMA_IOC_TIMEOUT_MS)); 311 + 312 + if (!time_left) { 313 + dev_err(sdev->dev, "Code loader DMA did not complete\n"); 314 + return -ETIMEDOUT; 315 + } 316 + dev_dbg(sdev->dev, "Code loader DMA done, waiting for FW_ENTERED status\n"); 303 317 304 318 status = snd_sof_dsp_read_poll_timeout(sdev, HDA_DSP_BAR, 305 319 chip->rom_status_reg, reg, ··· 335 311 dev_err(sdev->dev, 336 312 "%s: timeout with rom_status_reg (%#x) read\n", 337 313 __func__, chip->rom_status_reg); 314 + } else { 315 + dev_dbg(sdev->dev, "Code loader FW_ENTERED status\n"); 338 316 } 339 317 340 318 ret = hda_cl_trigger(sdev->dev, hext_stream, SNDRV_PCM_TRIGGER_STOP); ··· 344 318 dev_err(sdev->dev, "error: DMA trigger stop failed\n"); 345 319 if (!status) 346 320 status = ret; 321 + } else { 322 + dev_dbg(sdev->dev, "Code loader DMA stopped\n"); 347 323 } 348 324 349 325 return status;
+19 -3
sound/soc/sof/intel/hda-stream.c
··· 765 765 writeb(sd_status, s->sd_addr + SOF_HDA_ADSP_REG_SD_STS); 766 766 767 767 active = true; 768 - if ((!s->substream && !s->cstream) || 769 - !s->running || 770 - (sd_status & SOF_HDA_CL_DMA_SD_INT_COMPLETE) == 0) 768 + if (!s->running) 771 769 continue; 770 + if ((sd_status & SOF_HDA_CL_DMA_SD_INT_COMPLETE) == 0) 771 + continue; 772 + if (!s->substream && !s->cstream) { 773 + /* 774 + * when no substream is found, the DMA may used for code loading 775 + * or data transfers which can rely on wait_for_completion() 776 + */ 777 + struct sof_intel_hda_stream *hda_stream; 778 + struct hdac_ext_stream *hext_stream; 779 + 780 + hext_stream = stream_to_hdac_ext_stream(s); 781 + hda_stream = container_of(hext_stream, struct sof_intel_hda_stream, 782 + hext_stream); 783 + 784 + complete(&hda_stream->ioc); 785 + continue; 786 + } 772 787 773 788 /* Inform ALSA only in case not do that with IPC */ 774 789 if (s->substream && sof_hda->no_ipc_position) { ··· 895 880 return -ENOMEM; 896 881 897 882 hda_stream->sdev = sdev; 883 + init_completion(&hda_stream->ioc); 898 884 899 885 hext_stream = &hda_stream->hext_stream; 900 886
+2
sound/soc/sof/intel/hda.h
··· 11 11 #ifndef __SOF_INTEL_HDA_H 12 12 #define __SOF_INTEL_HDA_H 13 13 14 + #include <linux/completion.h> 14 15 #include <linux/soundwire/sdw.h> 15 16 #include <linux/soundwire/sdw_intel.h> 16 17 #include <sound/compress_driver.h> ··· 560 559 struct sof_intel_stream sof_intel_stream; 561 560 int host_reserved; /* reserve host DMA channel */ 562 561 u32 flags; 562 + struct completion ioc; 563 563 }; 564 564 565 565 #define hstream_to_sof_hda_stream(hstream) \