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: 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 <peter.ujfalusi@linux.intel.com>
Reviewed-by: Bard Liao <yung-chuan.liao@linux.intel.com>
Reviewed-by: Liam Girdwood <liam.r.girdwood@intel.com>
Reviewed-by: Kai Vehmanen <kai.vehmanen@linux.intel.com>
Link: https://patch.msgid.link/20241107121440.1472-1-peter.ujfalusi@linux.intel.com
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

Peter Ujfalusi and committed by
Mark Brown
1862e847 1b1f491d

+90 -30
+72 -28
sound/soc/sof/intel/hda-loader.c
··· 26 26 #include "../sof-priv.h" 27 27 #include "hda.h" 28 28 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 + 29 34 static void hda_ssp_set_cbp_cfp(struct snd_sof_dev *sdev) 30 35 { 31 36 struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; ··· 48 43 } 49 44 } 50 45 51 - struct hdac_ext_stream *hda_cl_prepare(struct device *dev, unsigned int format, 52 - unsigned int size, struct snd_dma_buffer *dmab, 53 - int direction, bool is_iccmax) 46 + struct hdac_ext_stream* 47 + hda_cl_prepare(struct device *dev, unsigned int format, unsigned int size, 48 + struct snd_dma_buffer *dmab, bool persistent_buffer, int direction, 49 + bool is_iccmax) 54 50 { 55 51 struct snd_sof_dev *sdev = dev_get_drvdata(dev); 56 52 struct hdac_ext_stream *hext_stream; ··· 67 61 hstream = &hext_stream->hstream; 68 62 hstream->substream = NULL; 69 63 70 - /* allocate DMA buffer */ 71 - ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, dev, size, dmab); 72 - if (ret < 0) { 73 - dev_err(sdev->dev, "error: memory alloc failed: %d\n", ret); 74 - goto out_put; 64 + /* 65 + * Allocate DMA buffer if it is temporary or if the buffer is intended 66 + * to be persistent but not yet allocated. 67 + * We cannot rely solely on !dmab->area as caller might use a struct on 68 + * stack (when it is temporary) without clearing it to 0. 69 + */ 70 + if (!persistent_buffer || !dmab->area) { 71 + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, dev, size, dmab); 72 + if (ret < 0) { 73 + dev_err(sdev->dev, "%s: memory alloc failed: %d\n", 74 + __func__, ret); 75 + goto out_put; 76 + } 75 77 } 76 78 77 79 hstream->period_bytes = 0;/* initialize period_bytes */ ··· 105 91 106 92 out_free: 107 93 snd_dma_free_pages(dmab); 94 + dmab->area = NULL; 95 + dmab->bytes = 0; 96 + hstream->bufsize = 0; 97 + hstream->format_val = 0; 108 98 out_put: 109 99 hda_dsp_stream_put(sdev, direction, hstream->stream_tag); 110 100 return ERR_PTR(ret); ··· 273 255 EXPORT_SYMBOL_NS(hda_cl_trigger, SND_SOC_SOF_INTEL_HDA_COMMON); 274 256 275 257 int hda_cl_cleanup(struct device *dev, struct snd_dma_buffer *dmab, 276 - struct hdac_ext_stream *hext_stream) 258 + bool persistent_buffer, struct hdac_ext_stream *hext_stream) 277 259 { 278 260 struct snd_sof_dev *sdev = dev_get_drvdata(dev); 279 261 struct hdac_stream *hstream = &hext_stream->hstream; ··· 297 279 sd_offset + SOF_HDA_ADSP_REG_SD_BDLPU, 0); 298 280 299 281 snd_sof_dsp_write(sdev, HDA_DSP_HDA_BAR, sd_offset, 0); 300 - snd_dma_free_pages(dmab); 301 - dmab->area = NULL; 302 - hstream->bufsize = 0; 303 - hstream->format_val = 0; 282 + 283 + if (!persistent_buffer) { 284 + snd_dma_free_pages(dmab); 285 + dmab->area = NULL; 286 + dmab->bytes = 0; 287 + hstream->bufsize = 0; 288 + hstream->format_val = 0; 289 + } 304 290 305 291 return ret; 306 292 } ··· 362 340 363 341 int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev) 364 342 { 343 + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; 365 344 struct hdac_ext_stream *iccmax_stream; 366 - struct snd_dma_buffer dmab_bdl; 367 345 int ret, ret1; 368 346 u8 original_gb; 369 347 ··· 376 354 * the data, so use a buffer of PAGE_SIZE for receiving. 377 355 */ 378 356 iccmax_stream = hda_cl_prepare(sdev->dev, HDA_CL_STREAM_FORMAT, PAGE_SIZE, 379 - &dmab_bdl, SNDRV_PCM_STREAM_CAPTURE, true); 357 + &hda->iccmax_dmab, persistent_cl_buffer, 358 + SNDRV_PCM_STREAM_CAPTURE, true); 380 359 if (IS_ERR(iccmax_stream)) { 381 360 dev_err(sdev->dev, "error: dma prepare for ICCMAX stream failed\n"); 382 361 return PTR_ERR(iccmax_stream); ··· 389 366 * Perform iccmax stream cleanup. This should be done even if firmware loading fails. 390 367 * If the cleanup also fails, we return the initial error 391 368 */ 392 - ret1 = hda_cl_cleanup(sdev->dev, &dmab_bdl, iccmax_stream); 369 + ret1 = hda_cl_cleanup(sdev->dev, &hda->iccmax_dmab, 370 + persistent_cl_buffer, iccmax_stream); 393 371 if (ret1 < 0) { 394 372 dev_err(sdev->dev, "error: ICCMAX stream cleanup failed\n"); 395 373 ··· 432 408 const struct sof_intel_dsp_desc *chip_info; 433 409 struct hdac_ext_stream *hext_stream; 434 410 struct firmware stripped_firmware; 435 - struct snd_dma_buffer dmab; 436 411 int ret, ret1, i; 437 412 438 413 if (hda->imrboot_supported && !sdev->first_boot && !hda->skip_imr_boot) { ··· 455 432 return -EINVAL; 456 433 } 457 434 458 - stripped_firmware.data = sdev->basefw.fw->data + sdev->basefw.payload_offset; 459 - stripped_firmware.size = sdev->basefw.fw->size - sdev->basefw.payload_offset; 460 - 461 435 /* init for booting wait */ 462 436 init_waitqueue_head(&sdev->boot_wait); 463 437 464 438 /* prepare DMA for code loader stream */ 439 + stripped_firmware.size = sdev->basefw.fw->size - sdev->basefw.payload_offset; 465 440 hext_stream = hda_cl_prepare(sdev->dev, HDA_CL_STREAM_FORMAT, 466 441 stripped_firmware.size, 467 - &dmab, SNDRV_PCM_STREAM_PLAYBACK, false); 442 + &hda->cl_dmab, persistent_cl_buffer, 443 + SNDRV_PCM_STREAM_PLAYBACK, false); 468 444 if (IS_ERR(hext_stream)) { 469 445 dev_err(sdev->dev, "error: dma prepare for fw loading failed\n"); 470 446 return PTR_ERR(hext_stream); 471 447 } 472 448 473 - memcpy(dmab.area, stripped_firmware.data, 474 - stripped_firmware.size); 449 + /* 450 + * Copy the payload to the DMA buffer if it is temporary or if the 451 + * buffer is persistent but it does not have the basefw payload either 452 + * because this is the first boot and the buffer needs to be initialized, 453 + * or a library got loaded and it replaced the basefw. 454 + */ 455 + if (!persistent_cl_buffer || !hda->cl_dmab_contains_basefw) { 456 + stripped_firmware.data = sdev->basefw.fw->data + sdev->basefw.payload_offset; 457 + memcpy(hda->cl_dmab.area, stripped_firmware.data, stripped_firmware.size); 458 + hda->cl_dmab_contains_basefw = true; 459 + } 475 460 476 461 /* try ROM init a few times before giving up */ 477 462 for (i = 0; i < HDA_FW_BOOT_ATTEMPTS; i++) { ··· 545 514 * This should be done even if firmware loading fails. 546 515 * If the cleanup also fails, we return the initial error 547 516 */ 548 - ret1 = hda_cl_cleanup(sdev->dev, &dmab, hext_stream); 517 + ret1 = hda_cl_cleanup(sdev->dev, &hda->cl_dmab, 518 + persistent_cl_buffer, hext_stream); 549 519 if (ret1 < 0) { 550 520 dev_err(sdev->dev, "error: Code loader DSP cleanup failed\n"); 551 521 ··· 577 545 struct hdac_ext_stream *hext_stream; 578 546 struct firmware stripped_firmware; 579 547 struct sof_ipc4_msg msg = {}; 580 - struct snd_dma_buffer dmab; 581 548 int ret, ret1; 582 549 583 550 /* if IMR booting is enabled and fw context is saved for D3 state, skip the loading */ ··· 587 556 stripped_firmware.data = fw_lib->sof_fw.fw->data + fw_lib->sof_fw.payload_offset; 588 557 stripped_firmware.size = fw_lib->sof_fw.fw->size - fw_lib->sof_fw.payload_offset; 589 558 559 + /* 560 + * force re-allocation of the cl_dmab if the preserved DMA buffer is 561 + * smaller than what is needed for the library 562 + */ 563 + if (persistent_cl_buffer && stripped_firmware.size > hda->cl_dmab.bytes) { 564 + snd_dma_free_pages(&hda->cl_dmab); 565 + hda->cl_dmab.area = NULL; 566 + hda->cl_dmab.bytes = 0; 567 + } 568 + 590 569 /* prepare DMA for code loader stream */ 591 570 hext_stream = hda_cl_prepare(sdev->dev, HDA_CL_STREAM_FORMAT, 592 571 stripped_firmware.size, 593 - &dmab, SNDRV_PCM_STREAM_PLAYBACK, false); 572 + &hda->cl_dmab, persistent_cl_buffer, 573 + SNDRV_PCM_STREAM_PLAYBACK, false); 594 574 if (IS_ERR(hext_stream)) { 595 575 dev_err(sdev->dev, "%s: DMA prepare failed\n", __func__); 596 576 return PTR_ERR(hext_stream); 597 577 } 598 578 599 - memcpy(dmab.area, stripped_firmware.data, stripped_firmware.size); 579 + memcpy(hda->cl_dmab.area, stripped_firmware.data, stripped_firmware.size); 580 + hda->cl_dmab_contains_basefw = false; 600 581 601 582 /* 602 583 * 1st stage: SOF_IPC4_GLB_LOAD_LIBRARY_PREPARE ··· 671 628 672 629 cleanup: 673 630 /* clean up even in case of error and return the first error */ 674 - ret1 = hda_cl_cleanup(sdev->dev, &dmab, hext_stream); 631 + ret1 = hda_cl_cleanup(sdev->dev, &hda->cl_dmab, persistent_cl_buffer, 632 + hext_stream); 675 633 if (ret1 < 0) { 676 634 dev_err(sdev->dev, "%s: Code loader DSP cleanup failed\n", __func__); 677 635
+6
sound/soc/sof/intel/hda.c
··· 936 936 /* disable DSP */ 937 937 hda_dsp_ctrl_ppcap_enable(sdev, false); 938 938 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 + 939 945 skip_disable_dsp: 940 946 free_irq(sdev->ipc_irq, sdev); 941 947 if (sdev->msi_enabled)
+12 -2
sound/soc/sof/intel/hda.h
··· 495 495 496 496 int boot_iteration; 497 497 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 + 498 507 struct hda_bus hbus; 499 508 500 509 /* hw config */ ··· 723 714 724 715 struct hdac_ext_stream *hda_cl_prepare(struct device *dev, unsigned int format, 725 716 unsigned int size, struct snd_dma_buffer *dmab, 726 - int direction, bool is_iccmax); 717 + bool persistent_buffer, int direction, 718 + bool is_iccmax); 727 719 int hda_cl_trigger(struct device *dev, struct hdac_ext_stream *hext_stream, int cmd); 728 720 729 721 int hda_cl_cleanup(struct device *dev, struct snd_dma_buffer *dmab, 730 - struct hdac_ext_stream *hext_stream); 722 + bool persistent_buffer, struct hdac_ext_stream *hext_stream); 731 723 int cl_dsp_init(struct snd_sof_dev *sdev, int stream_tag, bool imr_boot); 732 724 #define HDA_CL_STREAM_FORMAT 0x40 733 725