Skip to content

Commit 1862e84

Browse files
ujfalusibroonie
authored andcommitted
ASoC: SOF: Intel: hda: Add support for persistent Code Loader DMA buffers
It has been reported that the DMA memory allocation for firmware download can fail after extended period of uptime on systems with relatively small amount of RAM when the system memory becomes fragmented. The issue primarily happens when the system is waking up from system suspend, swap might not be available and the MM system cannot move things around to allow for successful allocation. If the IMR boot is not supported then for each DSP boot we would need to allocate the DMA buffer for firmware transfer, which can increase the chances of the issue to be hit. This patch adds support for allocating the DMA buffers once at first boot time and keep it until the system is shut down, rebooted or the drivers re-loaded and makes this as the default operation. With persistent_cl_buffer module parameter the persistent Code Loader DMA buffer can be disabled to fall back to on demand allocation. Signed-off-by: Peter Ujfalusi <[email protected]> Reviewed-by: Bard Liao <[email protected]> Reviewed-by: Liam Girdwood <[email protected]> Reviewed-by: Kai Vehmanen <[email protected]> Link: https://patch.msgid.link/[email protected] Signed-off-by: Mark Brown <[email protected]>
1 parent 1b1f491 commit 1862e84

File tree

3 files changed

+90
-30
lines changed

3 files changed

+90
-30
lines changed

sound/soc/sof/intel/hda-loader.c

Lines changed: 72 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@
2626
#include "../sof-priv.h"
2727
#include "hda.h"
2828

29+
static bool persistent_cl_buffer = true;
30+
module_param(persistent_cl_buffer, bool, 0444);
31+
MODULE_PARM_DESC(persistent_cl_buffer, "Persistent Code Loader DMA buffer "
32+
"(default = Y, use N to force buffer re-allocation)");
33+
2934
static void hda_ssp_set_cbp_cfp(struct snd_sof_dev *sdev)
3035
{
3136
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
@@ -43,9 +48,10 @@ static void hda_ssp_set_cbp_cfp(struct snd_sof_dev *sdev)
4348
}
4449
}
4550

46-
struct hdac_ext_stream *hda_cl_prepare(struct device *dev, unsigned int format,
47-
unsigned int size, struct snd_dma_buffer *dmab,
48-
int direction, bool is_iccmax)
51+
struct hdac_ext_stream*
52+
hda_cl_prepare(struct device *dev, unsigned int format, unsigned int size,
53+
struct snd_dma_buffer *dmab, bool persistent_buffer, int direction,
54+
bool is_iccmax)
4955
{
5056
struct snd_sof_dev *sdev = dev_get_drvdata(dev);
5157
struct hdac_ext_stream *hext_stream;
@@ -61,11 +67,19 @@ struct hdac_ext_stream *hda_cl_prepare(struct device *dev, unsigned int format,
6167
hstream = &hext_stream->hstream;
6268
hstream->substream = NULL;
6369

64-
/* allocate DMA buffer */
65-
ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, dev, size, dmab);
66-
if (ret < 0) {
67-
dev_err(sdev->dev, "error: memory alloc failed: %d\n", ret);
68-
goto out_put;
70+
/*
71+
* Allocate DMA buffer if it is temporary or if the buffer is intended
72+
* to be persistent but not yet allocated.
73+
* We cannot rely solely on !dmab->area as caller might use a struct on
74+
* stack (when it is temporary) without clearing it to 0.
75+
*/
76+
if (!persistent_buffer || !dmab->area) {
77+
ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, dev, size, dmab);
78+
if (ret < 0) {
79+
dev_err(sdev->dev, "%s: memory alloc failed: %d\n",
80+
__func__, ret);
81+
goto out_put;
82+
}
6983
}
7084

7185
hstream->period_bytes = 0;/* initialize period_bytes */
@@ -91,6 +105,10 @@ struct hdac_ext_stream *hda_cl_prepare(struct device *dev, unsigned int format,
91105

92106
out_free:
93107
snd_dma_free_pages(dmab);
108+
dmab->area = NULL;
109+
dmab->bytes = 0;
110+
hstream->bufsize = 0;
111+
hstream->format_val = 0;
94112
out_put:
95113
hda_dsp_stream_put(sdev, direction, hstream->stream_tag);
96114
return ERR_PTR(ret);
@@ -255,7 +273,7 @@ int hda_cl_trigger(struct device *dev, struct hdac_ext_stream *hext_stream, int
255273
EXPORT_SYMBOL_NS(hda_cl_trigger, SND_SOC_SOF_INTEL_HDA_COMMON);
256274

257275
int hda_cl_cleanup(struct device *dev, struct snd_dma_buffer *dmab,
258-
struct hdac_ext_stream *hext_stream)
276+
bool persistent_buffer, struct hdac_ext_stream *hext_stream)
259277
{
260278
struct snd_sof_dev *sdev = dev_get_drvdata(dev);
261279
struct hdac_stream *hstream = &hext_stream->hstream;
@@ -279,10 +297,14 @@ int hda_cl_cleanup(struct device *dev, struct snd_dma_buffer *dmab,
279297
sd_offset + SOF_HDA_ADSP_REG_SD_BDLPU, 0);
280298

281299
snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, sd_offset, 0);
282-
snd_dma_free_pages(dmab);
283-
dmab->area = NULL;
284-
hstream->bufsize = 0;
285-
hstream->format_val = 0;
300+
301+
if (!persistent_buffer) {
302+
snd_dma_free_pages(dmab);
303+
dmab->area = NULL;
304+
dmab->bytes = 0;
305+
hstream->bufsize = 0;
306+
hstream->format_val = 0;
307+
}
286308

287309
return ret;
288310
}
@@ -340,8 +362,8 @@ int hda_cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream
340362

341363
int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev)
342364
{
365+
struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata;
343366
struct hdac_ext_stream *iccmax_stream;
344-
struct snd_dma_buffer dmab_bdl;
345367
int ret, ret1;
346368
u8 original_gb;
347369

@@ -354,7 +376,8 @@ int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev)
354376
* the data, so use a buffer of PAGE_SIZE for receiving.
355377
*/
356378
iccmax_stream = hda_cl_prepare(sdev->dev, HDA_CL_STREAM_FORMAT, PAGE_SIZE,
357-
&dmab_bdl, SNDRV_PCM_STREAM_CAPTURE, true);
379+
&hda->iccmax_dmab, persistent_cl_buffer,
380+
SNDRV_PCM_STREAM_CAPTURE, true);
358381
if (IS_ERR(iccmax_stream)) {
359382
dev_err(sdev->dev, "error: dma prepare for ICCMAX stream failed\n");
360383
return PTR_ERR(iccmax_stream);
@@ -366,7 +389,8 @@ int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev)
366389
* Perform iccmax stream cleanup. This should be done even if firmware loading fails.
367390
* If the cleanup also fails, we return the initial error
368391
*/
369-
ret1 = hda_cl_cleanup(sdev->dev, &dmab_bdl, iccmax_stream);
392+
ret1 = hda_cl_cleanup(sdev->dev, &hda->iccmax_dmab,
393+
persistent_cl_buffer, iccmax_stream);
370394
if (ret1 < 0) {
371395
dev_err(sdev->dev, "error: ICCMAX stream cleanup failed\n");
372396

@@ -408,7 +432,6 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev)
408432
const struct sof_intel_dsp_desc *chip_info;
409433
struct hdac_ext_stream *hext_stream;
410434
struct firmware stripped_firmware;
411-
struct snd_dma_buffer dmab;
412435
int ret, ret1, i;
413436

414437
if (hda->imrboot_supported && !sdev->first_boot && !hda->skip_imr_boot) {
@@ -432,23 +455,31 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev)
432455
return -EINVAL;
433456
}
434457

435-
stripped_firmware.data = sdev->basefw.fw->data + sdev->basefw.payload_offset;
436-
stripped_firmware.size = sdev->basefw.fw->size - sdev->basefw.payload_offset;
437-
438458
/* init for booting wait */
439459
init_waitqueue_head(&sdev->boot_wait);
440460

441461
/* prepare DMA for code loader stream */
462+
stripped_firmware.size = sdev->basefw.fw->size - sdev->basefw.payload_offset;
442463
hext_stream = hda_cl_prepare(sdev->dev, HDA_CL_STREAM_FORMAT,
443464
stripped_firmware.size,
444-
&dmab, SNDRV_PCM_STREAM_PLAYBACK, false);
465+
&hda->cl_dmab, persistent_cl_buffer,
466+
SNDRV_PCM_STREAM_PLAYBACK, false);
445467
if (IS_ERR(hext_stream)) {
446468
dev_err(sdev->dev, "error: dma prepare for fw loading failed\n");
447469
return PTR_ERR(hext_stream);
448470
}
449471

450-
memcpy(dmab.area, stripped_firmware.data,
451-
stripped_firmware.size);
472+
/*
473+
* Copy the payload to the DMA buffer if it is temporary or if the
474+
* buffer is persistent but it does not have the basefw payload either
475+
* because this is the first boot and the buffer needs to be initialized,
476+
* or a library got loaded and it replaced the basefw.
477+
*/
478+
if (!persistent_cl_buffer || !hda->cl_dmab_contains_basefw) {
479+
stripped_firmware.data = sdev->basefw.fw->data + sdev->basefw.payload_offset;
480+
memcpy(hda->cl_dmab.area, stripped_firmware.data, stripped_firmware.size);
481+
hda->cl_dmab_contains_basefw = true;
482+
}
452483

453484
/* try ROM init a few times before giving up */
454485
for (i = 0; i < HDA_FW_BOOT_ATTEMPTS; i++) {
@@ -514,7 +545,8 @@ int hda_dsp_cl_boot_firmware(struct snd_sof_dev *sdev)
514545
* This should be done even if firmware loading fails.
515546
* If the cleanup also fails, we return the initial error
516547
*/
517-
ret1 = hda_cl_cleanup(sdev->dev, &dmab, hext_stream);
548+
ret1 = hda_cl_cleanup(sdev->dev, &hda->cl_dmab,
549+
persistent_cl_buffer, hext_stream);
518550
if (ret1 < 0) {
519551
dev_err(sdev->dev, "error: Code loader DSP cleanup failed\n");
520552

@@ -545,7 +577,6 @@ int hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev,
545577
struct hdac_ext_stream *hext_stream;
546578
struct firmware stripped_firmware;
547579
struct sof_ipc4_msg msg = {};
548-
struct snd_dma_buffer dmab;
549580
int ret, ret1;
550581

551582
/* if IMR booting is enabled and fw context is saved for D3 state, skip the loading */
@@ -556,16 +587,28 @@ int hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev,
556587
stripped_firmware.data = fw_lib->sof_fw.fw->data + fw_lib->sof_fw.payload_offset;
557588
stripped_firmware.size = fw_lib->sof_fw.fw->size - fw_lib->sof_fw.payload_offset;
558589

590+
/*
591+
* force re-allocation of the cl_dmab if the preserved DMA buffer is
592+
* smaller than what is needed for the library
593+
*/
594+
if (persistent_cl_buffer && stripped_firmware.size > hda->cl_dmab.bytes) {
595+
snd_dma_free_pages(&hda->cl_dmab);
596+
hda->cl_dmab.area = NULL;
597+
hda->cl_dmab.bytes = 0;
598+
}
599+
559600
/* prepare DMA for code loader stream */
560601
hext_stream = hda_cl_prepare(sdev->dev, HDA_CL_STREAM_FORMAT,
561602
stripped_firmware.size,
562-
&dmab, SNDRV_PCM_STREAM_PLAYBACK, false);
603+
&hda->cl_dmab, persistent_cl_buffer,
604+
SNDRV_PCM_STREAM_PLAYBACK, false);
563605
if (IS_ERR(hext_stream)) {
564606
dev_err(sdev->dev, "%s: DMA prepare failed\n", __func__);
565607
return PTR_ERR(hext_stream);
566608
}
567609

568-
memcpy(dmab.area, stripped_firmware.data, stripped_firmware.size);
610+
memcpy(hda->cl_dmab.area, stripped_firmware.data, stripped_firmware.size);
611+
hda->cl_dmab_contains_basefw = false;
569612

570613
/*
571614
* 1st stage: SOF_IPC4_GLB_LOAD_LIBRARY_PREPARE
@@ -628,7 +671,8 @@ int hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev,
628671

629672
cleanup:
630673
/* clean up even in case of error and return the first error */
631-
ret1 = hda_cl_cleanup(sdev->dev, &dmab, hext_stream);
674+
ret1 = hda_cl_cleanup(sdev->dev, &hda->cl_dmab, persistent_cl_buffer,
675+
hext_stream);
632676
if (ret1 < 0) {
633677
dev_err(sdev->dev, "%s: Code loader DSP cleanup failed\n", __func__);
634678

sound/soc/sof/intel/hda.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -936,6 +936,12 @@ void hda_dsp_remove(struct snd_sof_dev *sdev)
936936
/* disable DSP */
937937
hda_dsp_ctrl_ppcap_enable(sdev, false);
938938

939+
/* Free the persistent DMA buffers used for base firmware download */
940+
if (hda->cl_dmab.area)
941+
snd_dma_free_pages(&hda->cl_dmab);
942+
if (hda->iccmax_dmab.area)
943+
snd_dma_free_pages(&hda->iccmax_dmab);
944+
939945
skip_disable_dsp:
940946
free_irq(sdev->ipc_irq, sdev);
941947
if (sdev->msi_enabled)

sound/soc/sof/intel/hda.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,15 @@ struct sof_intel_hda_dev {
495495

496496
int boot_iteration;
497497

498+
/*
499+
* DMA buffers for base firmware download. By default the buffers are
500+
* allocated once and kept through the lifetime of the driver.
501+
* See module parameter: persistent_cl_buffer
502+
*/
503+
struct snd_dma_buffer cl_dmab;
504+
bool cl_dmab_contains_basefw;
505+
struct snd_dma_buffer iccmax_dmab;
506+
498507
struct hda_bus hbus;
499508

500509
/* hw config */
@@ -714,11 +723,12 @@ int hda_cl_copy_fw(struct snd_sof_dev *sdev, struct hdac_ext_stream *hext_stream
714723

715724
struct hdac_ext_stream *hda_cl_prepare(struct device *dev, unsigned int format,
716725
unsigned int size, struct snd_dma_buffer *dmab,
717-
int direction, bool is_iccmax);
726+
bool persistent_buffer, int direction,
727+
bool is_iccmax);
718728
int hda_cl_trigger(struct device *dev, struct hdac_ext_stream *hext_stream, int cmd);
719729

720730
int hda_cl_cleanup(struct device *dev, struct snd_dma_buffer *dmab,
721-
struct hdac_ext_stream *hext_stream);
731+
bool persistent_buffer, struct hdac_ext_stream *hext_stream);
722732
int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot);
723733
#define HDA_CL_STREAM_FORMAT 0x40
724734

0 commit comments

Comments
 (0)