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

ASoC: SOF: Intel/IPC4: Support for external firmware libraries

Merge series from Peter Ujfalusi <peter.ujfalusi@linux.intel.com>:

In IPC4 all DSP loadable executable is a 'library' containing modules. The main
or basefw is also a library which contains multiple modules.
IPC4 allows to use loadable libraries to extend the functionality of the booted
basefw.

This series adds support for loading external libraries in case they are needed
by the loaded topology file.

The libraries must be placed to a specific firmware directory (fw_lib_prefix),
which is:
intel/avs-lib|sof-ipc4-lib/ followed by the platform name and in case of
community key use a 'community' directory.

For example for upx-i11 (community key): intel/avs-lib/tgl/community is the
default path.

The name of the library should be the UUID of the module it contains since the
library loading is going to look for the file as <module_UUID>.bin
In case there is a need to bundle multiple modules into single library, symlinks
can be used to point to the file:

module_boundle.bin
<UUID1>.bin -> module_boundle.bin
<UUID2>.bin -> module_boundle.bin
<UUID3>.bin -> module_boundle.bin

But note that in this case all modules will be loaded to the DSP since only the
whole library can be loaded, not individual modules.

+675 -114
+1
include/sound/simple_card_utils.h
··· 177 177 struct snd_pcm_hw_params *params); 178 178 void asoc_simple_parse_convert(struct device_node *np, char *prefix, 179 179 struct asoc_simple_data *data); 180 + bool asoc_simple_is_convert_required(const struct asoc_simple_data *data); 180 181 181 182 int asoc_simple_parse_routing(struct snd_soc_card *card, 182 183 char *prefix);
+5 -5
include/sound/sof.h
··· 59 59 * SOF Platform data. 60 60 */ 61 61 struct snd_sof_pdata { 62 - const struct firmware *fw; 63 62 const char *name; 64 63 const char *platform; 65 64 66 65 struct device *dev; 67 - 68 - /* indicate how many first bytes shouldn't be loaded into DSP memory. */ 69 - size_t fw_offset; 70 66 71 67 /* 72 68 * notification callback used if the hardware initialization ··· 81 85 const char *fw_filename; 82 86 const char *tplg_filename_prefix; 83 87 const char *tplg_filename; 88 + 89 + /* loadable external libraries available under this directory */ 90 + const char *fw_lib_prefix; 84 91 85 92 /* machine */ 86 93 struct platform_device *pdev_mach; ··· 130 131 unsigned int ipc_supported_mask; 131 132 enum sof_ipc_type ipc_default; 132 133 133 - /* defaults paths for firmware and topology files */ 134 + /* defaults paths for firmware, library and topology files */ 134 135 const char *default_fw_path[SOF_IPC_TYPE_COUNT]; 136 + const char *default_lib_path[SOF_IPC_TYPE_COUNT]; 135 137 const char *default_tplg_path[SOF_IPC_TYPE_COUNT]; 136 138 137 139 /* default firmware name */
+4
include/sound/sof/ipc4/header.h
··· 185 185 #define SOF_IPC4_GLB_PIPE_STATE_MASK GENMASK(15, 0) 186 186 #define SOF_IPC4_GLB_PIPE_STATE(x) ((x) << SOF_IPC4_GLB_PIPE_STATE_SHIFT) 187 187 188 + /* load library ipc msg */ 189 + #define SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID_SHIFT 16 190 + #define SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID(x) ((x) << SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID_SHIFT) 191 + 188 192 enum sof_ipc4_channel_config { 189 193 /* one channel only. */ 190 194 SOF_IPC4_CHANNEL_CONFIG_MONO,
+14
sound/soc/amd/yc/acp6x-mach.c
··· 49 49 .driver_data = &acp6x_card, 50 50 .matches = { 51 51 DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), 52 + DMI_MATCH(DMI_PRODUCT_NAME, "21D0"), 53 + } 54 + }, 55 + { 56 + .driver_data = &acp6x_card, 57 + .matches = { 58 + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), 59 + DMI_MATCH(DMI_PRODUCT_NAME, "21D1"), 60 + } 61 + }, 62 + { 63 + .driver_data = &acp6x_card, 64 + .matches = { 65 + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), 52 66 DMI_MATCH(DMI_PRODUCT_NAME, "21D2"), 53 67 } 54 68 },
+1 -1
sound/soc/codecs/jz4725b.c
··· 359 359 360 360 {"Mixer to ADC", NULL, "Mixer"}, 361 361 {"ADC Source Capture Route", "Mixer", "Mixer to ADC"}, 362 - {"ADC Sourc Capture Routee", "Line In", "Line In"}, 362 + {"ADC Source Capture Route", "Line In", "Line In"}, 363 363 {"ADC Source Capture Route", "Mic 1", "Mic 1"}, 364 364 {"ADC Source Capture Route", "Mic 2", "Mic 2"}, 365 365 {"ADC", NULL, "ADC Source Capture Route"},
+14 -3
sound/soc/codecs/rt1308-sdw.c
··· 50 50 case 0x3008: 51 51 case 0x300a: 52 52 case 0xc000: 53 + case 0xc710: 53 54 case 0xc860 ... 0xc863: 54 55 case 0xc870 ... 0xc873: 55 56 return true; ··· 201 200 { 202 201 struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(dev); 203 202 int ret = 0; 203 + unsigned int tmp; 204 204 205 205 if (rt1308->hw_init) 206 206 return 0; ··· 233 231 /* sw reset */ 234 232 regmap_write(rt1308->regmap, RT1308_SDW_RESET, 0); 235 233 234 + regmap_read(rt1308->regmap, 0xc710, &tmp); 235 + rt1308->hw_ver = tmp; 236 + dev_dbg(dev, "%s, hw_ver=0x%x\n", __func__, rt1308->hw_ver); 237 + 236 238 /* initial settings */ 237 239 regmap_write(rt1308->regmap, 0xc103, 0xc0); 238 240 regmap_write(rt1308->regmap, 0xc030, 0x17); ··· 252 246 regmap_write(rt1308->regmap, 0xc062, 0x05); 253 247 regmap_write(rt1308->regmap, 0xc171, 0x07); 254 248 regmap_write(rt1308->regmap, 0xc173, 0x0d); 255 - regmap_write(rt1308->regmap, 0xc311, 0x7f); 256 - regmap_write(rt1308->regmap, 0xc900, 0x90); 249 + if (rt1308->hw_ver == RT1308_VER_C) { 250 + regmap_write(rt1308->regmap, 0xc311, 0x7f); 251 + regmap_write(rt1308->regmap, 0xc300, 0x09); 252 + } else { 253 + regmap_write(rt1308->regmap, 0xc311, 0x4f); 254 + regmap_write(rt1308->regmap, 0xc300, 0x0b); 255 + } 256 + regmap_write(rt1308->regmap, 0xc900, 0x5a); 257 257 regmap_write(rt1308->regmap, 0xc1a0, 0x84); 258 258 regmap_write(rt1308->regmap, 0xc1a1, 0x01); 259 259 regmap_write(rt1308->regmap, 0xc360, 0x78); ··· 269 257 regmap_write(rt1308->regmap, 0xc070, 0x00); 270 258 regmap_write(rt1308->regmap, 0xc100, 0xd7); 271 259 regmap_write(rt1308->regmap, 0xc101, 0xd7); 272 - regmap_write(rt1308->regmap, 0xc300, 0x09); 273 260 274 261 if (rt1308->first_hw_init) { 275 262 regcache_cache_bypass(rt1308->regmap, false);
+3
sound/soc/codecs/rt1308-sdw.h
··· 139 139 { 0x3005, 0x23 }, 140 140 { 0x3008, 0x02 }, 141 141 { 0x300a, 0x00 }, 142 + { 0xc000 | (RT1308_DATA_PATH << 4), 0x00 }, 142 143 { 0xc003 | (RT1308_DAC_SET << 4), 0x00 }, 143 144 { 0xc000 | (RT1308_POWER << 4), 0x00 }, 144 145 { 0xc001 | (RT1308_POWER << 4), 0x00 }, 145 146 { 0xc002 | (RT1308_POWER << 4), 0x00 }, 147 + { 0xc000 | (RT1308_POWER_STATUS << 4), 0x00 }, 146 148 }; 147 149 148 150 #define RT1308_SDW_OFFSET 0xc000 ··· 165 163 bool first_hw_init; 166 164 int rx_mask; 167 165 int slots; 166 + int hw_ver; 168 167 }; 169 168 170 169 struct sdw_stream_data {
+5
sound/soc/codecs/rt1308.h
··· 286 286 RT1308_AIFS 287 287 }; 288 288 289 + enum rt1308_hw_ver { 290 + RT1308_VER_C = 2, 291 + RT1308_VER_D 292 + }; 293 + 289 294 #endif /* end of _RT1308_H_ */
+1 -1
sound/soc/codecs/tlv320adc3xxx.c
··· 1449 1449 .of_match_table = tlv320adc3xxx_of_match, 1450 1450 }, 1451 1451 .probe_new = adc3xxx_i2c_probe, 1452 - .remove = adc3xxx_i2c_remove, 1452 + .remove = __exit_p(adc3xxx_i2c_remove), 1453 1453 .id_table = adc3xxx_i2c_id, 1454 1454 }; 1455 1455
+1 -1
sound/soc/generic/audio-graph-card.c
··· 417 417 * or has convert-xxx property 418 418 */ 419 419 if ((of_get_child_count(codec_port) > 1) || 420 - (adata->convert_rate || adata->convert_channels)) 420 + asoc_simple_is_convert_required(adata)) 421 421 return true; 422 422 423 423 return false;
+15
sound/soc/generic/simple-card-utils.c
··· 85 85 } 86 86 EXPORT_SYMBOL_GPL(asoc_simple_parse_convert); 87 87 88 + /** 89 + * asoc_simple_is_convert_required() - Query if HW param conversion was requested 90 + * @data: Link data. 91 + * 92 + * Returns true if any HW param conversion was requested for this DAI link with 93 + * any "convert-xxx" properties. 94 + */ 95 + bool asoc_simple_is_convert_required(const struct asoc_simple_data *data) 96 + { 97 + return data->convert_rate || 98 + data->convert_channels || 99 + data->convert_sample_format; 100 + } 101 + EXPORT_SYMBOL_GPL(asoc_simple_is_convert_required); 102 + 88 103 int asoc_simple_parse_daifmt(struct device *dev, 89 104 struct device_node *node, 90 105 struct device_node *codec,
+1 -2
sound/soc/generic/simple-card.c
··· 393 393 * or has convert-xxx property 394 394 */ 395 395 if (dpcm_selectable && 396 - (num > 2 || 397 - adata.convert_rate || adata.convert_channels)) { 396 + (num > 2 || asoc_simple_is_convert_required(&adata))) { 398 397 /* 399 398 * np 400 399 * |1(CPU)|0(Codec) li->cpu
+12
sound/soc/intel/boards/sof_rt5682.c
··· 223 223 SOF_RT5682_SSP_AMP(2) | 224 224 SOF_RT5682_NUM_HDMIDEV(4)), 225 225 }, 226 + { 227 + .callback = sof_rt5682_quirk_cb, 228 + .matches = { 229 + DMI_MATCH(DMI_PRODUCT_FAMILY, "Google_Rex"), 230 + }, 231 + .driver_data = (void *)(SOF_RT5682_MCLK_EN | 232 + SOF_RT5682_SSP_CODEC(2) | 233 + SOF_SPEAKER_AMP_PRESENT | 234 + SOF_RT5682_SSP_AMP(0) | 235 + SOF_RT5682_NUM_HDMIDEV(4) 236 + ), 237 + }, 226 238 {} 227 239 }; 228 240
+11
sound/soc/intel/boards/sof_sdw.c
··· 202 202 SOF_SDW_PCH_DMIC | 203 203 RT711_JD1), 204 204 }, 205 + { 206 + /* NUC15 LAPBC710 skews */ 207 + .callback = sof_sdw_quirk_cb, 208 + .matches = { 209 + DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"), 210 + DMI_MATCH(DMI_BOARD_NAME, "LAPBC710"), 211 + }, 212 + .driver_data = (void *)(SOF_SDW_TGL_HDMI | 213 + SOF_SDW_PCH_DMIC | 214 + RT711_JD1), 215 + }, 205 216 /* TigerLake-SDCA devices */ 206 217 { 207 218 .callback = sof_sdw_quirk_cb,
+1
sound/soc/qcom/Kconfig
··· 187 187 config SND_SOC_SC7180 188 188 tristate "SoC Machine driver for SC7180 boards" 189 189 depends on I2C && GPIOLIB 190 + depends on SOUNDWIRE || SOUNDWIRE=n 190 191 select SND_SOC_QCOM_COMMON 191 192 select SND_SOC_LPASS_SC7180 192 193 select SND_SOC_MAX98357A
+2
sound/soc/qcom/lpass-cpu.c
··· 784 784 return true; 785 785 if (reg == LPASS_HDMI_TX_VBIT_CTL_ADDR(v)) 786 786 return true; 787 + if (reg == LPASS_HDMI_TX_PARITY_ADDR(v)) 788 + return true; 787 789 788 790 for (i = 0; i < v->hdmi_rdma_channels; ++i) { 789 791 if (reg == LPAIF_HDMI_RDMACURR_REG(v, i))
+2 -4
sound/soc/sof/amd/acp-loader.c
··· 48 48 int acp_dsp_block_write(struct snd_sof_dev *sdev, enum snd_sof_fw_blk_type blk_type, 49 49 u32 offset, void *src, size_t size) 50 50 { 51 - struct snd_sof_pdata *plat_data = sdev->pdata; 52 51 struct pci_dev *pci = to_pci_dev(sdev->dev); 53 52 const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata); 54 53 struct acp_dev_data *adata; ··· 60 61 switch (blk_type) { 61 62 case SOF_FW_BLK_TYPE_IRAM: 62 63 if (!adata->bin_buf) { 63 - size_fw = plat_data->fw->size; 64 + size_fw = sdev->basefw.fw->size; 64 65 page_count = PAGE_ALIGN(size_fw) >> PAGE_SHIFT; 65 66 dma_size = page_count * ACP_PAGE_SIZE; 66 67 adata->bin_buf = dma_alloc_coherent(&pci->dev, dma_size, ··· 151 152 int acp_dsp_pre_fw_run(struct snd_sof_dev *sdev) 152 153 { 153 154 struct pci_dev *pci = to_pci_dev(sdev->dev); 154 - struct snd_sof_pdata *plat_data = sdev->pdata; 155 155 struct acp_dev_data *adata; 156 156 unsigned int src_addr, size_fw; 157 157 u32 page_count, dma_size; ··· 184 186 dev_err(sdev->dev, "acp dma transfer status: %d\n", ret); 185 187 186 188 /* Free memory once DMA is complete */ 187 - dma_size = (PAGE_ALIGN(plat_data->fw->size) >> PAGE_SHIFT) * ACP_PAGE_SIZE; 189 + dma_size = (PAGE_ALIGN(sdev->basefw.fw->size) >> PAGE_SHIFT) * ACP_PAGE_SIZE; 188 190 dma_free_coherent(&pci->dev, dma_size, adata->bin_buf, adata->sha_dma_addr); 189 191 dma_free_coherent(&pci->dev, ACP_DEFAULT_DRAM_LENGTH, adata->data_buf, adata->dma_addr); 190 192 adata->bin_buf = NULL;
+3
sound/soc/sof/intel/apl.c
··· 62 62 63 63 ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_1_5; 64 64 65 + /* External library loading support */ 66 + ipc4_data->load_library = hda_dsp_ipc4_load_library; 67 + 65 68 /* doorbell */ 66 69 sof_apl_ops.irq_thread = hda_dsp_ipc4_irq_thread; 67 70
+3
sound/soc/sof/intel/cnl.c
··· 407 407 408 408 ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_1_8; 409 409 410 + /* External library loading support */ 411 + ipc4_data->load_library = hda_dsp_ipc4_load_library; 412 + 410 413 /* doorbell */ 411 414 sof_cnl_ops.irq_thread = cnl_ipc4_irq_thread; 412 415
+3 -4
sound/soc/sof/intel/hda-loader-skl.c
··· 494 494 struct snd_dma_buffer *dmab) 495 495 496 496 { 497 - struct snd_sof_pdata *plat_data = sdev->pdata; 498 - const struct firmware *fw = plat_data->fw; 497 + const struct firmware *fw = sdev->basefw.fw; 499 498 struct firmware stripped_firmware; 500 499 unsigned int bufsize = HDA_SKL_CLDMA_MAX_BUFFER_SIZE; 501 500 int ret; 502 501 503 - stripped_firmware.data = plat_data->fw->data + plat_data->fw_offset; 504 - stripped_firmware.size = plat_data->fw->size - plat_data->fw_offset; 502 + stripped_firmware.data = fw->data + sdev->basefw.payload_offset; 503 + stripped_firmware.size = fw->size - sdev->basefw.payload_offset; 505 504 506 505 dev_dbg(sdev->dev, "firmware size: %#zx buffer size %#x\n", fw->size, bufsize); 507 506
+76 -7
sound/soc/sof/intel/hda-loader.c
··· 19 19 #include <sound/hdaudio_ext.h> 20 20 #include <sound/hda_register.h> 21 21 #include <sound/sof.h> 22 + #include <sound/sof/ipc4/header.h> 22 23 #include "ext_manifest.h" 24 + #include "../ipc4-priv.h" 23 25 #include "../ops.h" 24 26 #include "../sof-priv.h" 25 27 #include "hda.h" ··· 320 318 321 319 int hda_dsp_cl_boot_firmware_iccmax(struct snd_sof_dev *sdev) 322 320 { 323 - struct snd_sof_pdata *plat_data = sdev->pdata; 324 321 struct hdac_ext_stream *iccmax_stream; 325 322 struct hdac_bus *bus = sof_to_bus(sdev); 326 323 struct firmware stripped_firmware; ··· 330 329 /* save the original LTRP guardband value */ 331 330 original_gb = snd_hdac_chip_readb(bus, VS_LTRP) & HDA_VS_INTEL_LTRP_GB_MASK; 332 331 333 - if (plat_data->fw->size <= plat_data->fw_offset) { 332 + if (sdev->basefw.fw->size <= sdev->basefw.payload_offset) { 334 333 dev_err(sdev->dev, "error: firmware size must be greater than firmware offset\n"); 335 334 return -EINVAL; 336 335 } 337 336 338 - stripped_firmware.size = plat_data->fw->size - plat_data->fw_offset; 337 + stripped_firmware.size = sdev->basefw.fw->size - sdev->basefw.payload_offset; 339 338 340 339 /* prepare capture stream for ICCMAX */ 341 340 iccmax_stream = hda_cl_stream_prepare(sdev, HDA_CL_STREAM_FORMAT, stripped_firmware.size, ··· 398 397 dev_dbg(sdev->dev, "IMR restore supported, booting from IMR directly\n"); 399 398 hda->boot_iteration = 0; 400 399 ret = hda_dsp_boot_imr(sdev); 401 - if (!ret) 400 + if (!ret) { 401 + hda->booted_from_imr = true; 402 402 return 0; 403 + } 403 404 404 405 dev_warn(sdev->dev, "IMR restore failed, trying to cold boot\n"); 405 406 } 406 407 408 + hda->booted_from_imr = false; 409 + 407 410 chip_info = desc->chip_info; 408 411 409 - if (plat_data->fw->size <= plat_data->fw_offset) { 412 + if (sdev->basefw.fw->size <= sdev->basefw.payload_offset) { 410 413 dev_err(sdev->dev, "error: firmware size must be greater than firmware offset\n"); 411 414 return -EINVAL; 412 415 } 413 416 414 - stripped_firmware.data = plat_data->fw->data + plat_data->fw_offset; 415 - stripped_firmware.size = plat_data->fw->size - plat_data->fw_offset; 417 + stripped_firmware.data = sdev->basefw.fw->data + sdev->basefw.payload_offset; 418 + stripped_firmware.size = sdev->basefw.fw->size - sdev->basefw.payload_offset; 416 419 417 420 /* init for booting wait */ 418 421 init_waitqueue_head(&sdev->boot_wait); ··· 517 512 snd_sof_dsp_update_bits(sdev, HDA_DSP_PP_BAR, 518 513 SOF_HDA_REG_PP_PPCTL, 519 514 SOF_HDA_PPCTL_GPROCEN, 0); 515 + return ret; 516 + } 517 + 518 + int hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev, 519 + struct sof_ipc4_fw_library *fw_lib, bool reload) 520 + { 521 + struct sof_intel_hda_dev *hda = sdev->pdata->hw_pdata; 522 + struct hdac_ext_stream *hext_stream; 523 + struct firmware stripped_firmware; 524 + struct sof_ipc4_msg msg = {}; 525 + struct snd_dma_buffer dmab; 526 + int ret, ret1; 527 + 528 + /* IMR booting will restore the libraries as well, skip the loading */ 529 + if (reload && hda->booted_from_imr) 530 + return 0; 531 + 532 + /* the fw_lib has been verified during loading, we can trust the validity here */ 533 + stripped_firmware.data = fw_lib->sof_fw.fw->data + fw_lib->sof_fw.payload_offset; 534 + stripped_firmware.size = fw_lib->sof_fw.fw->size - fw_lib->sof_fw.payload_offset; 535 + 536 + /* prepare DMA for code loader stream */ 537 + hext_stream = hda_cl_stream_prepare(sdev, HDA_CL_STREAM_FORMAT, 538 + stripped_firmware.size, 539 + &dmab, SNDRV_PCM_STREAM_PLAYBACK); 540 + if (IS_ERR(hext_stream)) { 541 + dev_err(sdev->dev, "%s: DMA prepare failed\n", __func__); 542 + return PTR_ERR(hext_stream); 543 + } 544 + 545 + memcpy(dmab.area, stripped_firmware.data, stripped_firmware.size); 546 + 547 + msg.primary = hext_stream->hstream.stream_tag - 1; 548 + msg.primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_LOAD_LIBRARY); 549 + msg.primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST); 550 + msg.primary |= SOF_IPC4_MSG_TARGET(SOF_IPC4_FW_GEN_MSG); 551 + msg.primary |= SOF_IPC4_GLB_LOAD_LIBRARY_LIB_ID(fw_lib->id); 552 + 553 + ret = cl_trigger(sdev, hext_stream, SNDRV_PCM_TRIGGER_START); 554 + if (ret < 0) { 555 + dev_err(sdev->dev, "%s: DMA trigger start failed\n", __func__); 556 + goto cleanup; 557 + } 558 + 559 + ret = sof_ipc_tx_message(sdev->ipc, &msg, 0, NULL, 0); 560 + 561 + ret1 = cl_trigger(sdev, hext_stream, SNDRV_PCM_TRIGGER_STOP); 562 + if (ret1 < 0) { 563 + dev_err(sdev->dev, "%s: DMA trigger stop failed\n", __func__); 564 + if (!ret) 565 + ret = ret1; 566 + } 567 + 568 + cleanup: 569 + /* clean up even in case of error and return the first error */ 570 + ret1 = hda_cl_cleanup(sdev, &dmab, hext_stream); 571 + if (ret1 < 0) { 572 + dev_err(sdev->dev, "%s: Code loader DSP cleanup failed\n", __func__); 573 + 574 + /* set return value to indicate cleanup failure */ 575 + if (!ret) 576 + ret = ret1; 577 + } 578 + 520 579 return ret; 521 580 } 522 581
+4
sound/soc/sof/intel/hda.h
··· 481 481 struct sof_intel_hda_dev { 482 482 bool imrboot_supported; 483 483 bool skip_imr_boot; 484 + bool booted_from_imr; 484 485 485 486 int boot_iteration; 486 487 ··· 866 865 void hda_ipc4_dump(struct snd_sof_dev *sdev); 867 866 extern struct sdw_intel_ops sdw_callback; 868 867 868 + struct sof_ipc4_fw_library; 869 + int hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev, 870 + struct sof_ipc4_fw_library *fw_lib, bool reload); 869 871 #endif
+3
sound/soc/sof/intel/icl.c
··· 130 130 131 131 ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_2; 132 132 133 + /* External library loading support */ 134 + ipc4_data->load_library = hda_dsp_ipc4_load_library; 135 + 133 136 /* doorbell */ 134 137 sof_icl_ops.irq_thread = cnl_ipc4_irq_thread; 135 138
+3
sound/soc/sof/intel/mtl.c
··· 659 659 660 660 ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_2; 661 661 662 + /* External library loading support */ 663 + ipc4_data->load_library = hda_dsp_ipc4_load_library; 664 + 662 665 /* set DAI ops */ 663 666 hda_set_dai_drv_ops(sdev, &sof_mtl_ops); 664 667
+6
sound/soc/sof/intel/pci-apl.c
··· 33 33 [SOF_IPC] = "intel/sof", 34 34 [SOF_INTEL_IPC4] = "intel/avs/apl", 35 35 }, 36 + .default_lib_path = { 37 + [SOF_INTEL_IPC4] = "intel/avs-lib/apl", 38 + }, 36 39 .default_tplg_path = { 37 40 [SOF_IPC] = "intel/sof-tplg", 38 41 [SOF_INTEL_IPC4] = "intel/avs-tplg", ··· 63 60 .default_fw_path = { 64 61 [SOF_IPC] = "intel/sof", 65 62 [SOF_INTEL_IPC4] = "intel/avs/glk", 63 + }, 64 + .default_lib_path = { 65 + [SOF_INTEL_IPC4] = "intel/avs-lib/glk", 66 66 }, 67 67 .default_tplg_path = { 68 68 [SOF_IPC] = "intel/sof-tplg",
+9
sound/soc/sof/intel/pci-cnl.c
··· 34 34 [SOF_IPC] = "intel/sof", 35 35 [SOF_INTEL_IPC4] = "intel/avs/cnl", 36 36 }, 37 + .default_lib_path = { 38 + [SOF_INTEL_IPC4] = "intel/avs-lib/cnl", 39 + }, 37 40 .default_tplg_path = { 38 41 [SOF_IPC] = "intel/sof-tplg", 39 42 [SOF_INTEL_IPC4] = "intel/avs-tplg", ··· 64 61 .default_fw_path = { 65 62 [SOF_IPC] = "intel/sof", 66 63 [SOF_INTEL_IPC4] = "intel/avs/cnl", 64 + }, 65 + .default_lib_path = { 66 + [SOF_INTEL_IPC4] = "intel/avs-lib/cnl", 67 67 }, 68 68 .default_tplg_path = { 69 69 [SOF_IPC] = "intel/sof-tplg", ··· 96 90 .default_fw_path = { 97 91 [SOF_IPC] = "intel/sof", 98 92 [SOF_INTEL_IPC4] = "intel/avs/cnl", 93 + }, 94 + .default_lib_path = { 95 + [SOF_INTEL_IPC4] = "intel/avs-lib/cnl", 99 96 }, 100 97 .default_tplg_path = { 101 98 [SOF_IPC] = "intel/sof-tplg",
+6
sound/soc/sof/intel/pci-icl.c
··· 34 34 [SOF_IPC] = "intel/sof", 35 35 [SOF_INTEL_IPC4] = "intel/avs/icl", 36 36 }, 37 + .default_lib_path = { 38 + [SOF_INTEL_IPC4] = "intel/avs-lib/icl", 39 + }, 37 40 .default_tplg_path = { 38 41 [SOF_IPC] = "intel/sof-tplg", 39 42 [SOF_INTEL_IPC4] = "intel/avs-tplg", ··· 64 61 .default_fw_path = { 65 62 [SOF_IPC] = "intel/sof", 66 63 [SOF_INTEL_IPC4] = "intel/avs/jsl", 64 + }, 65 + .default_lib_path = { 66 + [SOF_INTEL_IPC4] = "intel/avs-lib/jsl", 67 67 }, 68 68 .default_tplg_path = { 69 69 [SOF_IPC] = "intel/sof-tplg",
+4 -1
sound/soc/sof/intel/pci-mtl.c
··· 34 34 .default_fw_path = { 35 35 [SOF_INTEL_IPC4] = "intel/sof-ipc4/mtl", 36 36 }, 37 + .default_lib_path = { 38 + [SOF_INTEL_IPC4] = "intel/sof-ipc4-lib/mtl", 39 + }, 37 40 .default_tplg_path = { 38 41 [SOF_INTEL_IPC4] = "intel/sof-ace-tplg", 39 42 }, 40 43 .default_fw_filename = { 41 - [SOF_INTEL_IPC4] = "dsp_basefw.bin", 44 + [SOF_INTEL_IPC4] = "sof-mtl.ri", 42 45 }, 43 46 .nocodec_tplg_filename = "sof-mtl-nocodec.tplg", 44 47 .ops = &sof_mtl_ops,
+53 -1
sound/soc/sof/intel/pci-tgl.c
··· 34 34 [SOF_IPC] = "intel/sof", 35 35 [SOF_INTEL_IPC4] = "intel/avs/tgl", 36 36 }, 37 + .default_lib_path = { 38 + [SOF_INTEL_IPC4] = "intel/avs-lib/tgl", 39 + }, 37 40 .default_tplg_path = { 38 41 [SOF_IPC] = "intel/sof-tplg", 39 42 [SOF_INTEL_IPC4] = "intel/avs-tplg", ··· 64 61 .default_fw_path = { 65 62 [SOF_IPC] = "intel/sof", 66 63 [SOF_INTEL_IPC4] = "intel/avs/tgl-h", 64 + }, 65 + .default_lib_path = { 66 + [SOF_INTEL_IPC4] = "intel/avs-lib/tgl-h", 67 67 }, 68 68 .default_tplg_path = { 69 69 [SOF_IPC] = "intel/sof-tplg", ··· 96 90 [SOF_IPC] = "intel/sof", 97 91 [SOF_INTEL_IPC4] = "intel/avs/ehl", 98 92 }, 93 + .default_lib_path = { 94 + [SOF_INTEL_IPC4] = "intel/avs-lib/ehl", 95 + }, 99 96 .default_tplg_path = { 100 97 [SOF_IPC] = "intel/sof-tplg", 101 98 [SOF_INTEL_IPC4] = "intel/avs-tplg", ··· 126 117 .default_fw_path = { 127 118 [SOF_IPC] = "intel/sof", 128 119 [SOF_INTEL_IPC4] = "intel/avs/adl-s", 120 + }, 121 + .default_lib_path = { 122 + [SOF_INTEL_IPC4] = "intel/avs-lib/adl-s", 129 123 }, 130 124 .default_tplg_path = { 131 125 [SOF_IPC] = "intel/sof-tplg", ··· 158 146 [SOF_IPC] = "intel/sof", 159 147 [SOF_INTEL_IPC4] = "intel/avs/adl", 160 148 }, 149 + .default_lib_path = { 150 + [SOF_INTEL_IPC4] = "intel/avs-lib/adl", 151 + }, 161 152 .default_tplg_path = { 162 153 [SOF_IPC] = "intel/sof-tplg", 163 154 [SOF_INTEL_IPC4] = "intel/avs-tplg", 164 155 }, 165 156 .default_fw_filename = { 166 157 [SOF_IPC] = "sof-adl.ri", 158 + [SOF_INTEL_IPC4] = "dsp_basefw.bin", 159 + }, 160 + .nocodec_tplg_filename = "sof-adl-nocodec.tplg", 161 + .ops = &sof_tgl_ops, 162 + .ops_init = sof_tgl_ops_init, 163 + }; 164 + 165 + static const struct sof_dev_desc adl_n_desc = { 166 + .machines = snd_soc_acpi_intel_adl_machines, 167 + .alt_machines = snd_soc_acpi_intel_adl_sdw_machines, 168 + .use_acpi_target_states = true, 169 + .resindex_lpe_base = 0, 170 + .resindex_pcicfg_base = -1, 171 + .resindex_imr_base = -1, 172 + .irqindex_host_ipc = -1, 173 + .chip_info = &tgl_chip_info, 174 + .ipc_supported_mask = BIT(SOF_IPC) | BIT(SOF_INTEL_IPC4), 175 + .ipc_default = SOF_IPC, 176 + .default_fw_path = { 177 + [SOF_IPC] = "intel/sof", 178 + [SOF_INTEL_IPC4] = "intel/avs/adl-n", 179 + }, 180 + .default_lib_path = { 181 + [SOF_INTEL_IPC4] = "intel/avs-lib/adl-n", 182 + }, 183 + .default_tplg_path = { 184 + [SOF_IPC] = "intel/sof-tplg", 185 + [SOF_INTEL_IPC4] = "intel/avs-tplg", 186 + }, 187 + .default_fw_filename = { 188 + [SOF_IPC] = "sof-adl-n.ri", 167 189 [SOF_INTEL_IPC4] = "dsp_basefw.bin", 168 190 }, 169 191 .nocodec_tplg_filename = "sof-adl-nocodec.tplg", ··· 219 173 .default_fw_path = { 220 174 [SOF_IPC] = "intel/sof", 221 175 [SOF_INTEL_IPC4] = "intel/avs/rpl-s", 176 + }, 177 + .default_lib_path = { 178 + [SOF_INTEL_IPC4] = "intel/avs-lib/rpl-s", 222 179 }, 223 180 .default_tplg_path = { 224 181 [SOF_IPC] = "intel/sof-tplg", ··· 250 201 .default_fw_path = { 251 202 [SOF_IPC] = "intel/sof", 252 203 [SOF_INTEL_IPC4] = "intel/avs/rpl", 204 + }, 205 + .default_lib_path = { 206 + [SOF_INTEL_IPC4] = "intel/avs-lib/rpl", 253 207 }, 254 208 .default_tplg_path = { 255 209 [SOF_IPC] = "intel/sof-tplg", ··· 298 246 { PCI_DEVICE(0x8086, 0x51cf), /* RPL-PX */ 299 247 .driver_data = (unsigned long)&rpl_desc}, 300 248 { PCI_DEVICE(0x8086, 0x54c8), /* ADL-N */ 301 - .driver_data = (unsigned long)&adl_desc}, 249 + .driver_data = (unsigned long)&adl_n_desc}, 302 250 { 0, } 303 251 }; 304 252 MODULE_DEVICE_TABLE(pci, sof_pci_ids);
+3
sound/soc/sof/intel/tgl.c
··· 85 85 86 86 ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_2; 87 87 88 + /* External library loading support */ 89 + ipc4_data->load_library = hda_dsp_ipc4_load_library; 90 + 88 91 /* doorbell */ 89 92 sof_tgl_ops.irq_thread = cnl_ipc4_irq_thread; 90 93
+6
sound/soc/sof/ipc.c
··· 200 200 return NULL; 201 201 } 202 202 203 + if (ops->init && ops->init(sdev)) 204 + return NULL; 205 + 203 206 ipc->ops = ops; 204 207 205 208 return ipc; ··· 220 217 mutex_lock(&ipc->tx_mutex); 221 218 ipc->disable_ipc_tx = true; 222 219 mutex_unlock(&ipc->tx_mutex); 220 + 221 + if (ipc->ops->exit) 222 + ipc->ops->exit(sdev); 223 223 } 224 224 EXPORT_SYMBOL(snd_sof_ipc_free);
+12 -14
sound/soc/sof/ipc3-loader.c
··· 138 138 139 139 static size_t sof_ipc3_fw_parse_ext_man(struct snd_sof_dev *sdev) 140 140 { 141 - struct snd_sof_pdata *plat_data = sdev->pdata; 142 - const struct firmware *fw = plat_data->fw; 141 + const struct firmware *fw = sdev->basefw.fw; 143 142 const struct sof_ext_man_elem_header *elem_hdr; 144 143 const struct sof_ext_man_header *head; 145 144 ssize_t ext_man_size; ··· 309 310 310 311 static int sof_ipc3_load_fw_to_dsp(struct snd_sof_dev *sdev) 311 312 { 312 - struct snd_sof_pdata *plat_data = sdev->pdata; 313 - const struct firmware *fw = plat_data->fw; 313 + u32 payload_offset = sdev->basefw.payload_offset; 314 + const struct firmware *fw = sdev->basefw.fw; 314 315 struct snd_sof_fw_header *header; 315 316 struct snd_sof_mod_hdr *module; 316 317 int (*load_module)(struct snd_sof_dev *sof_dev, struct snd_sof_mod_hdr *hdr); 317 318 size_t remaining; 318 319 int ret, count; 319 320 320 - if (!plat_data->fw) 321 + if (!fw) 321 322 return -EINVAL; 322 323 323 - header = (struct snd_sof_fw_header *)(fw->data + plat_data->fw_offset); 324 + header = (struct snd_sof_fw_header *)(fw->data + payload_offset); 324 325 load_module = sof_ops(sdev)->load_module; 325 326 if (!load_module) { 326 327 dev_dbg(sdev->dev, "Using generic module loading\n"); ··· 330 331 } 331 332 332 333 /* parse each module */ 333 - module = (struct snd_sof_mod_hdr *)(fw->data + plat_data->fw_offset + 334 - sizeof(*header)); 335 - remaining = fw->size - sizeof(*header) - plat_data->fw_offset; 334 + module = (struct snd_sof_mod_hdr *)(fw->data + payload_offset + sizeof(*header)); 335 + remaining = fw->size - sizeof(*header) - payload_offset; 336 336 /* check for wrap */ 337 337 if (remaining > fw->size) { 338 338 dev_err(sdev->dev, "%s: fw size smaller than header size\n", __func__); ··· 372 374 373 375 static int sof_ipc3_validate_firmware(struct snd_sof_dev *sdev) 374 376 { 375 - struct snd_sof_pdata *plat_data = sdev->pdata; 376 - const struct firmware *fw = plat_data->fw; 377 + u32 payload_offset = sdev->basefw.payload_offset; 378 + const struct firmware *fw = sdev->basefw.fw; 377 379 struct snd_sof_fw_header *header; 378 - size_t fw_size = fw->size - plat_data->fw_offset; 380 + size_t fw_size = fw->size - payload_offset; 379 381 380 - if (fw->size <= plat_data->fw_offset) { 382 + if (fw->size <= payload_offset) { 381 383 dev_err(sdev->dev, 382 384 "firmware size must be greater than firmware offset\n"); 383 385 return -EINVAL; 384 386 } 385 387 386 388 /* Read the header information from the data pointer */ 387 - header = (struct snd_sof_fw_header *)(fw->data + plat_data->fw_offset); 389 + header = (struct snd_sof_fw_header *)(fw->data + payload_offset); 388 390 389 391 /* verify FW sig */ 390 392 if (strncmp(header->sig, SND_SOF_FW_SIG, SND_SOF_FW_SIG_SIZE) != 0) {
+215 -18
sound/soc/sof/ipc4-loader.c
··· 14 14 #include "sof-priv.h" 15 15 #include "ops.h" 16 16 17 - static size_t sof_ipc4_fw_parse_ext_man(struct snd_sof_dev *sdev) 17 + /* The module ID includes the id of the library it is part of at offset 12 */ 18 + #define SOF_IPC4_MOD_LIB_ID_SHIFT 12 19 + 20 + static size_t sof_ipc4_fw_parse_ext_man(struct snd_sof_dev *sdev, 21 + struct sof_ipc4_fw_library *fw_lib) 18 22 { 19 23 struct sof_ipc4_fw_data *ipc4_data = sdev->private; 20 - struct snd_sof_pdata *plat_data = sdev->pdata; 24 + const struct firmware *fw = fw_lib->sof_fw.fw; 21 25 struct sof_man4_fw_binary_header *fw_header; 22 - const struct firmware *fw = plat_data->fw; 23 26 struct sof_ext_manifest4_hdr *ext_man_hdr; 24 27 struct sof_man4_module_config *fm_config; 25 28 struct sof_ipc4_fw_module *fw_module; ··· 74 71 return -EINVAL; 75 72 } 76 73 77 - dev_info(sdev->dev, "Loaded firmware version: %u.%u.%u.%u\n", 78 - fw_header->major_version, fw_header->minor_version, 74 + dev_info(sdev->dev, "Loaded firmware library: %s, version: %u.%u.%u.%u\n", 75 + fw_header->name, fw_header->major_version, fw_header->minor_version, 79 76 fw_header->hotfix_version, fw_header->build_version); 80 - dev_dbg(sdev->dev, "Firmware name: %s, header length: %u, module count: %u\n", 81 - fw_header->name, fw_header->len, fw_header->num_module_entries); 77 + dev_dbg(sdev->dev, "Header length: %u, module count: %u\n", 78 + fw_header->len, fw_header->num_module_entries); 82 79 83 - ipc4_data->fw_modules = devm_kmalloc_array(sdev->dev, 84 - fw_header->num_module_entries, 85 - sizeof(*fw_module), GFP_KERNEL); 86 - if (!ipc4_data->fw_modules) 80 + fw_lib->modules = devm_kmalloc_array(sdev->dev, fw_header->num_module_entries, 81 + sizeof(*fw_module), GFP_KERNEL); 82 + if (!fw_lib->modules) 87 83 return -ENOMEM; 88 84 89 - ipc4_data->num_fw_modules = fw_header->num_module_entries; 90 - fw_module = ipc4_data->fw_modules; 85 + fw_lib->name = fw_header->name; 86 + fw_lib->num_modules = fw_header->num_module_entries; 87 + fw_module = fw_lib->modules; 91 88 92 89 fm_entry = (struct sof_man4_module *)((u8 *)fw_header + fw_header->len); 93 90 remaining -= fw_header->len; ··· 137 134 return ext_man_hdr->len; 138 135 } 139 136 137 + static size_t sof_ipc4_fw_parse_basefw_ext_man(struct snd_sof_dev *sdev) 138 + { 139 + struct sof_ipc4_fw_data *ipc4_data = sdev->private; 140 + struct sof_ipc4_fw_library *fw_lib; 141 + size_t payload_offset; 142 + int ret; 143 + 144 + fw_lib = devm_kzalloc(sdev->dev, sizeof(*fw_lib), GFP_KERNEL); 145 + if (!fw_lib) 146 + return -ENOMEM; 147 + 148 + fw_lib->sof_fw.fw = sdev->basefw.fw; 149 + 150 + payload_offset = sof_ipc4_fw_parse_ext_man(sdev, fw_lib); 151 + if (payload_offset > 0) { 152 + fw_lib->sof_fw.payload_offset = payload_offset; 153 + 154 + /* basefw ID is 0 */ 155 + fw_lib->id = 0; 156 + ret = xa_insert(&ipc4_data->fw_lib_xa, 0, fw_lib, GFP_KERNEL); 157 + if (ret) 158 + return ret; 159 + } 160 + 161 + return payload_offset; 162 + } 163 + 164 + static int sof_ipc4_load_library_by_uuid(struct snd_sof_dev *sdev, 165 + unsigned long lib_id, const guid_t *uuid) 166 + { 167 + struct sof_ipc4_fw_data *ipc4_data = sdev->private; 168 + struct sof_ipc4_fw_library *fw_lib; 169 + const char *fw_filename; 170 + size_t payload_offset; 171 + int ret, i, err; 172 + 173 + if (!sdev->pdata->fw_lib_prefix) { 174 + dev_err(sdev->dev, 175 + "Library loading is not supported due to not set library path\n"); 176 + return -EINVAL; 177 + } 178 + 179 + if (!ipc4_data->load_library) { 180 + dev_err(sdev->dev, "Library loading is not supported on this platform\n"); 181 + return -EOPNOTSUPP; 182 + } 183 + 184 + fw_lib = devm_kzalloc(sdev->dev, sizeof(*fw_lib), GFP_KERNEL); 185 + if (!fw_lib) 186 + return -ENOMEM; 187 + 188 + fw_filename = kasprintf(GFP_KERNEL, "%s/%pUL.bin", 189 + sdev->pdata->fw_lib_prefix, uuid); 190 + if (!fw_filename) { 191 + ret = -ENOMEM; 192 + goto free_fw_lib; 193 + } 194 + 195 + ret = request_firmware(&fw_lib->sof_fw.fw, fw_filename, sdev->dev); 196 + if (ret < 0) { 197 + dev_err(sdev->dev, "Library file '%s' is missing\n", fw_filename); 198 + goto free_filename; 199 + } else { 200 + dev_dbg(sdev->dev, "Library file '%s' loaded\n", fw_filename); 201 + } 202 + 203 + payload_offset = sof_ipc4_fw_parse_ext_man(sdev, fw_lib); 204 + if (payload_offset <= 0) { 205 + if (!payload_offset) 206 + ret = -EINVAL; 207 + else 208 + ret = payload_offset; 209 + 210 + goto release; 211 + } 212 + 213 + fw_lib->sof_fw.payload_offset = payload_offset; 214 + fw_lib->id = lib_id; 215 + 216 + /* Fix up the module ID numbers within the library */ 217 + for (i = 0; i < fw_lib->num_modules; i++) 218 + fw_lib->modules[i].man4_module_entry.id |= (lib_id << SOF_IPC4_MOD_LIB_ID_SHIFT); 219 + 220 + /* 221 + * Make sure that the DSP is booted and stays up while attempting the 222 + * loading the library for the first time 223 + */ 224 + ret = pm_runtime_resume_and_get(sdev->dev); 225 + if (ret < 0 && ret != -EACCES) { 226 + dev_err_ratelimited(sdev->dev, "%s: pm_runtime resume failed: %d\n", 227 + __func__, ret); 228 + goto release; 229 + } 230 + 231 + ret = ipc4_data->load_library(sdev, fw_lib, false); 232 + 233 + pm_runtime_mark_last_busy(sdev->dev); 234 + err = pm_runtime_put_autosuspend(sdev->dev); 235 + if (err < 0) 236 + dev_err_ratelimited(sdev->dev, "%s: pm_runtime idle failed: %d\n", 237 + __func__, err); 238 + 239 + if (ret) 240 + goto release; 241 + 242 + ret = xa_insert(&ipc4_data->fw_lib_xa, lib_id, fw_lib, GFP_KERNEL); 243 + if (unlikely(ret)) 244 + goto release; 245 + 246 + kfree(fw_filename); 247 + 248 + return 0; 249 + 250 + release: 251 + release_firmware(fw_lib->sof_fw.fw); 252 + /* Allocated within sof_ipc4_fw_parse_ext_man() */ 253 + devm_kfree(sdev->dev, fw_lib->modules); 254 + free_filename: 255 + kfree(fw_filename); 256 + free_fw_lib: 257 + devm_kfree(sdev->dev, fw_lib); 258 + 259 + return ret; 260 + } 261 + 262 + struct sof_ipc4_fw_module *sof_ipc4_find_module_by_uuid(struct snd_sof_dev *sdev, 263 + const guid_t *uuid) 264 + { 265 + struct sof_ipc4_fw_data *ipc4_data = sdev->private; 266 + struct sof_ipc4_fw_library *fw_lib; 267 + unsigned long lib_id; 268 + int i, ret; 269 + 270 + if (guid_is_null(uuid)) 271 + return NULL; 272 + 273 + xa_for_each(&ipc4_data->fw_lib_xa, lib_id, fw_lib) { 274 + for (i = 0; i < fw_lib->num_modules; i++) { 275 + if (guid_equal(uuid, &fw_lib->modules[i].man4_module_entry.uuid)) 276 + return &fw_lib->modules[i]; 277 + } 278 + } 279 + 280 + /* 281 + * Do not attempt to load external library in case the maximum number of 282 + * firmware libraries have been already loaded 283 + */ 284 + if ((lib_id + 1) == ipc4_data->max_libs_count) { 285 + dev_err(sdev->dev, 286 + "%s: Maximum allowed number of libraries reached (%u)\n", 287 + __func__, ipc4_data->max_libs_count); 288 + return NULL; 289 + } 290 + 291 + /* The module cannot be found, try to load it as a library */ 292 + ret = sof_ipc4_load_library_by_uuid(sdev, lib_id + 1, uuid); 293 + if (ret) 294 + return NULL; 295 + 296 + /* Look for the module in the newly loaded library, it should be available now */ 297 + xa_for_each_start(&ipc4_data->fw_lib_xa, lib_id, fw_lib, lib_id) { 298 + for (i = 0; i < fw_lib->num_modules; i++) { 299 + if (guid_equal(uuid, &fw_lib->modules[i].man4_module_entry.uuid)) 300 + return &fw_lib->modules[i]; 301 + } 302 + } 303 + 304 + return NULL; 305 + } 306 + 140 307 static int sof_ipc4_validate_firmware(struct snd_sof_dev *sdev) 141 308 { 142 309 struct sof_ipc4_fw_data *ipc4_data = sdev->private; 143 310 u32 fw_hdr_offset = ipc4_data->manifest_fw_hdr_offset; 144 - struct snd_sof_pdata *plat_data = sdev->pdata; 145 311 struct sof_man4_fw_binary_header *fw_header; 146 - const struct firmware *fw = plat_data->fw; 312 + const struct firmware *fw = sdev->basefw.fw; 147 313 struct sof_ext_manifest4_hdr *ext_man_hdr; 148 314 149 315 ext_man_hdr = (struct sof_ext_manifest4_hdr *)fw->data; ··· 328 156 return 0; 329 157 } 330 158 331 - static int sof_ipc4_query_fw_configuration(struct snd_sof_dev *sdev) 159 + int sof_ipc4_query_fw_configuration(struct snd_sof_dev *sdev) 332 160 { 333 161 struct sof_ipc4_fw_data *ipc4_data = sdev->private; 334 162 const struct sof_ipc_ops *iops = sdev->ipc->ops; ··· 376 204 trace_sof_ipc4_fw_config(sdev, "Trace log size", *tuple->value); 377 205 ipc4_data->mtrace_log_bytes = *tuple->value; 378 206 break; 207 + case SOF_IPC4_FW_CFG_MAX_LIBS_COUNT: 208 + trace_sof_ipc4_fw_config(sdev, "maximum number of libraries", 209 + *tuple->value); 210 + ipc4_data->max_libs_count = *tuple->value; 211 + if (!ipc4_data->max_libs_count) 212 + ipc4_data->max_libs_count = 1; 213 + break; 379 214 default: 380 215 break; 381 216 } ··· 396 217 return ret; 397 218 } 398 219 220 + int sof_ipc4_reload_fw_libraries(struct snd_sof_dev *sdev) 221 + { 222 + struct sof_ipc4_fw_data *ipc4_data = sdev->private; 223 + struct sof_ipc4_fw_library *fw_lib; 224 + unsigned long lib_id; 225 + int ret = 0; 226 + 227 + xa_for_each_start(&ipc4_data->fw_lib_xa, lib_id, fw_lib, 1) { 228 + ret = ipc4_data->load_library(sdev, fw_lib, true); 229 + if (ret) { 230 + dev_err(sdev->dev, "%s: Failed to reload library: %s, %d\n", 231 + __func__, fw_lib->name, ret); 232 + break; 233 + } 234 + } 235 + 236 + return ret; 237 + } 238 + 399 239 const struct sof_ipc_fw_loader_ops ipc4_loader_ops = { 400 240 .validate = sof_ipc4_validate_firmware, 401 - .parse_ext_manifest = sof_ipc4_fw_parse_ext_man, 402 - .query_fw_configuration = sof_ipc4_query_fw_configuration, 241 + .parse_ext_manifest = sof_ipc4_fw_parse_basefw_ext_man, 403 242 };
+18 -2
sound/soc/sof/ipc4-mtrace.c
··· 108 108 int id; 109 109 u32 slot_offset; 110 110 void *log_buffer; 111 + struct mutex buffer_lock; /* for log_buffer alloc/free */ 111 112 u32 host_read_ptr; 112 113 u32 dsp_write_ptr; 113 114 /* pos update IPC arrived before the slot offset is known, queried */ ··· 129 128 struct sof_mtrace_core_data *core_data = inode->i_private; 130 129 int ret; 131 130 131 + mutex_lock(&core_data->buffer_lock); 132 + 133 + if (core_data->log_buffer) { 134 + ret = -EBUSY; 135 + goto out; 136 + } 137 + 132 138 ret = debugfs_file_get(file->f_path.dentry); 133 139 if (unlikely(ret)) 134 - return ret; 140 + goto out; 135 141 136 142 core_data->log_buffer = kmalloc(SOF_MTRACE_SLOT_SIZE, GFP_KERNEL); 137 143 if (!core_data->log_buffer) { 138 144 debugfs_file_put(file->f_path.dentry); 139 - return -ENOMEM; 145 + ret = -ENOMEM; 146 + goto out; 140 147 } 141 148 142 149 ret = simple_open(inode, file); ··· 152 143 kfree(core_data->log_buffer); 153 144 debugfs_file_put(file->f_path.dentry); 154 145 } 146 + 147 + out: 148 + mutex_unlock(&core_data->buffer_lock); 155 149 156 150 return ret; 157 151 } ··· 292 280 293 281 debugfs_file_put(file->f_path.dentry); 294 282 283 + mutex_lock(&core_data->buffer_lock); 295 284 kfree(core_data->log_buffer); 285 + core_data->log_buffer = NULL; 286 + mutex_unlock(&core_data->buffer_lock); 296 287 297 288 return 0; 298 289 } ··· 578 563 struct sof_mtrace_core_data *core_data = &priv->cores[i]; 579 564 580 565 init_waitqueue_head(&core_data->trace_sleep); 566 + mutex_init(&core_data->buffer_lock); 581 567 core_data->sdev = sdev; 582 568 core_data->id = i; 583 569 }
+50 -19
sound/soc/sof/ipc4-priv.h
··· 25 25 }; 26 26 27 27 /** 28 - * struct sof_ipc4_fw_data - IPC4-specific data 29 - * @manifest_fw_hdr_offset: FW header offset in the manifest 30 - * @num_fw_modules : Number of modules in base FW 31 - * @fw_modules: Array of base FW modules 32 - * @nhlt: NHLT table either from the BIOS or the topology manifest 33 - * @mtrace_type: mtrace type supported on the booted platform 34 - * @mtrace_log_bytes: log bytes as reported by the firmware via fw_config reply 35 - */ 36 - struct sof_ipc4_fw_data { 37 - u32 manifest_fw_hdr_offset; 38 - int num_fw_modules; 39 - void *fw_modules; 40 - void *nhlt; 41 - enum sof_ipc4_mtrace_type mtrace_type; 42 - u32 mtrace_log_bytes; 43 - }; 44 - 45 - /** 46 28 * struct sof_ipc4_fw_module - IPC4 module info 47 - * @sof_man4_module : Module info 29 + * @sof_man4_module: Module info 48 30 * @m_ida: Module instance identifier 49 31 * @bss_size: Module object size 50 32 * @private: Module private data ··· 38 56 void *private; 39 57 }; 40 58 59 + /** 60 + * struct sof_ipc4_fw_library - IPC4 library information 61 + * @sof_fw: SOF Firmware of the library 62 + * @id: Library ID. 0 is reserved for basefw, external libraries must have unique 63 + * ID number between 1 and (sof_ipc4_fw_data.max_libs_count - 1) 64 + * Note: sof_ipc4_fw_data.max_libs_count == 1 implies that external libraries 65 + * are not supported 66 + * @num_modules : Number of FW modules in the library 67 + * @modules: Array of FW modules 68 + */ 69 + struct sof_ipc4_fw_library { 70 + struct sof_firmware sof_fw; 71 + const char *name; 72 + u32 id; 73 + int num_modules; 74 + struct sof_ipc4_fw_module *modules; 75 + }; 76 + 77 + /** 78 + * struct sof_ipc4_fw_data - IPC4-specific data 79 + * @manifest_fw_hdr_offset: FW header offset in the manifest 80 + * @fw_lib_xa: XArray for firmware libraries, including basefw (ID = 0) 81 + * Used to store the FW libraries and to manage the unique IDs of the 82 + * libraries. 83 + * @nhlt: NHLT table either from the BIOS or the topology manifest 84 + * @mtrace_type: mtrace type supported on the booted platform 85 + * @mtrace_log_bytes: log bytes as reported by the firmware via fw_config reply 86 + * @max_libs_count: Maximum number of libraries support by the FW including the 87 + * base firmware 88 + * 89 + * @load_library: Callback function for platform dependent library loading 90 + */ 91 + struct sof_ipc4_fw_data { 92 + u32 manifest_fw_hdr_offset; 93 + struct xarray fw_lib_xa; 94 + void *nhlt; 95 + enum sof_ipc4_mtrace_type mtrace_type; 96 + u32 mtrace_log_bytes; 97 + u32 max_libs_count; 98 + 99 + int (*load_library)(struct snd_sof_dev *sdev, 100 + struct sof_ipc4_fw_library *fw_lib, bool reload); 101 + }; 102 + 41 103 extern const struct sof_ipc_fw_loader_ops ipc4_loader_ops; 42 104 extern const struct sof_ipc_tplg_ops ipc4_tplg_ops; 43 105 extern const struct sof_ipc_tplg_control_ops tplg_ipc4_control_ops; ··· 90 64 91 65 int sof_ipc4_set_pipeline_state(struct snd_sof_dev *sdev, u32 id, u32 state); 92 66 int sof_ipc4_mtrace_update_pos(struct snd_sof_dev *sdev, int core); 67 + 68 + int sof_ipc4_query_fw_configuration(struct snd_sof_dev *sdev); 69 + int sof_ipc4_reload_fw_libraries(struct snd_sof_dev *sdev); 70 + struct sof_ipc4_fw_module *sof_ipc4_find_module_by_uuid(struct snd_sof_dev *sdev, 71 + const guid_t *uuid); 93 72 #endif
+3 -14
sound/soc/sof/ipc4-topology.c
··· 289 289 { 290 290 struct snd_soc_component *scomp = swidget->scomp; 291 291 struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp); 292 - struct sof_ipc4_fw_data *ipc4_data = sdev->private; 293 - struct sof_ipc4_fw_module *fw_modules = ipc4_data->fw_modules; 294 - int i; 295 292 296 - if (!fw_modules) { 297 - dev_err(sdev->dev, "no fw_module information\n"); 298 - return -EINVAL; 299 - } 293 + swidget->module_info = sof_ipc4_find_module_by_uuid(sdev, &swidget->uuid); 300 294 301 - /* set module info */ 302 - for (i = 0; i < ipc4_data->num_fw_modules; i++) { 303 - if (guid_equal(&swidget->uuid, &fw_modules[i].man4_module_entry.uuid)) { 304 - swidget->module_info = &fw_modules[i]; 305 - return 0; 306 - } 307 - } 295 + if (swidget->module_info) 296 + return 0; 308 297 309 298 dev_err(sdev->dev, "failed to find module info for widget %s with UUID %pUL\n", 310 299 swidget->widget->name, &swidget->uuid);
+41
sound/soc/sof/ipc4.c
··· 8 8 // Authors: Rander Wang <rander.wang@linux.intel.com> 9 9 // Peter Ujfalusi <peter.ujfalusi@linux.intel.com> 10 10 // 11 + #include <linux/firmware.h> 11 12 #include <sound/sof/header.h> 12 13 #include <sound/sof/ipc4/header.h> 13 14 #include "sof-priv.h" ··· 658 657 .set_core_state = sof_ipc4_set_core_state, 659 658 }; 660 659 660 + static int sof_ipc4_init(struct snd_sof_dev *sdev) 661 + { 662 + struct sof_ipc4_fw_data *ipc4_data = sdev->private; 663 + 664 + xa_init_flags(&ipc4_data->fw_lib_xa, XA_FLAGS_ALLOC); 665 + 666 + return 0; 667 + } 668 + 669 + static void sof_ipc4_exit(struct snd_sof_dev *sdev) 670 + { 671 + struct sof_ipc4_fw_data *ipc4_data = sdev->private; 672 + struct sof_ipc4_fw_library *fw_lib; 673 + unsigned long lib_id; 674 + 675 + xa_for_each(&ipc4_data->fw_lib_xa, lib_id, fw_lib) { 676 + /* 677 + * The basefw (ID == 0) is handled by generic code, it is not 678 + * loaded by IPC4 code. 679 + */ 680 + if (lib_id != 0) 681 + release_firmware(fw_lib->sof_fw.fw); 682 + 683 + fw_lib->sof_fw.fw = NULL; 684 + } 685 + 686 + xa_destroy(&ipc4_data->fw_lib_xa); 687 + } 688 + 689 + static int sof_ipc4_post_boot(struct snd_sof_dev *sdev) 690 + { 691 + if (sdev->first_boot) 692 + return sof_ipc4_query_fw_configuration(sdev); 693 + 694 + return sof_ipc4_reload_fw_libraries(sdev); 695 + } 696 + 661 697 const struct sof_ipc_ops ipc4_ops = { 698 + .init = sof_ipc4_init, 699 + .exit = sof_ipc4_exit, 700 + .post_fw_boot = sof_ipc4_post_boot, 662 701 .tx_msg = sof_ipc4_tx_msg, 663 702 .rx_msg = sof_ipc4_rx_msg, 664 703 .set_get_data = sof_ipc4_set_get_data,
+12 -13
sound/soc/sof/loader.c
··· 22 22 int ret; 23 23 24 24 /* Don't request firmware again if firmware is already requested */ 25 - if (plat_data->fw) 25 + if (sdev->basefw.fw) 26 26 return 0; 27 27 28 28 fw_filename = kasprintf(GFP_KERNEL, "%s/%s", ··· 31 31 if (!fw_filename) 32 32 return -ENOMEM; 33 33 34 - ret = request_firmware(&plat_data->fw, fw_filename, sdev->dev); 34 + ret = request_firmware(&sdev->basefw.fw, fw_filename, sdev->dev); 35 35 36 36 if (ret < 0) { 37 37 dev_err(sdev->dev, ··· 48 48 ext_man_size = sdev->ipc->ops->fw_loader->parse_ext_manifest(sdev); 49 49 if (ext_man_size > 0) { 50 50 /* when no error occurred, drop extended manifest */ 51 - plat_data->fw_offset = ext_man_size; 51 + sdev->basefw.payload_offset = ext_man_size; 52 52 } else if (!ext_man_size) { 53 53 /* No extended manifest, so nothing to skip during FW load */ 54 54 dev_dbg(sdev->dev, "firmware doesn't contain extended manifest\n"); ··· 67 67 68 68 int snd_sof_load_firmware_memcpy(struct snd_sof_dev *sdev) 69 69 { 70 - struct snd_sof_pdata *plat_data = sdev->pdata; 71 70 int ret; 72 71 73 72 ret = snd_sof_load_firmware_raw(sdev); ··· 99 100 return 0; 100 101 101 102 error: 102 - release_firmware(plat_data->fw); 103 - plat_data->fw = NULL; 103 + release_firmware(sdev->basefw.fw); 104 + sdev->basefw.fw = NULL; 104 105 return ret; 105 106 106 107 } ··· 164 165 if (sdev->fw_state == SOF_FW_BOOT_READY_FAILED) 165 166 return -EIO; /* FW boots but fw_ready op failed */ 166 167 168 + dev_dbg(sdev->dev, "firmware boot complete\n"); 169 + sof_set_fw_state(sdev, SOF_FW_BOOT_COMPLETE); 170 + 167 171 /* perform post fw run operations */ 168 172 ret = snd_sof_dsp_post_fw_run(sdev); 169 173 if (ret < 0) { ··· 174 172 return ret; 175 173 } 176 174 177 - dev_dbg(sdev->dev, "firmware boot complete\n"); 178 - sof_set_fw_state(sdev, SOF_FW_BOOT_COMPLETE); 179 - 180 - if (sdev->first_boot && sdev->ipc->ops->fw_loader->query_fw_configuration) 181 - return sdev->ipc->ops->fw_loader->query_fw_configuration(sdev); 175 + if (sdev->ipc->ops->post_fw_boot) 176 + return sdev->ipc->ops->post_fw_boot(sdev); 182 177 183 178 return 0; 184 179 } ··· 184 185 void snd_sof_fw_unload(struct snd_sof_dev *sdev) 185 186 { 186 187 /* TODO: support module unloading at runtime */ 187 - release_firmware(sdev->pdata->fw); 188 - sdev->pdata->fw = NULL; 188 + release_firmware(sdev->basefw.fw); 189 + sdev->basefw.fw = NULL; 189 190 } 190 191 EXPORT_SYMBOL(snd_sof_fw_unload);
+26
sound/soc/sof/sof-pci-dev.c
··· 28 28 module_param(fw_filename, charp, 0444); 29 29 MODULE_PARM_DESC(fw_filename, "alternate filename for SOF firmware."); 30 30 31 + static char *lib_path; 32 + module_param(lib_path, charp, 0444); 33 + MODULE_PARM_DESC(lib_path, "alternate path for SOF firmware libraries."); 34 + 31 35 static char *tplg_path; 32 36 module_param(tplg_path, charp, 0444); 33 37 MODULE_PARM_DESC(tplg_path, "alternate path for SOF topology."); ··· 274 270 } else { 275 271 sof_pdata->fw_filename_prefix = 276 272 sof_pdata->desc->default_fw_path[sof_pdata->ipc_type]; 273 + } 274 + 275 + if (lib_path) { 276 + sof_pdata->fw_lib_prefix = lib_path; 277 + 278 + dev_dbg(dev, "Module parameter used, changed fw_lib path to %s\n", 279 + sof_pdata->fw_lib_prefix); 280 + 281 + } else if (sof_pdata->desc->default_lib_path[sof_pdata->ipc_type]) { 282 + if (dmi_check_system(community_key_platforms) && sof_dmi_use_community_key) { 283 + sof_pdata->fw_lib_prefix = 284 + devm_kasprintf(dev, GFP_KERNEL, "%s/%s", 285 + sof_pdata->desc->default_lib_path[sof_pdata->ipc_type], 286 + "community"); 287 + 288 + dev_dbg(dev, 289 + "Platform uses community key, changed fw_lib path to %s\n", 290 + sof_pdata->fw_lib_prefix); 291 + } else { 292 + sof_pdata->fw_lib_prefix = 293 + sof_pdata->desc->default_lib_path[sof_pdata->ipc_type]; 294 + } 277 295 } 278 296 279 297 if (tplg_path)
+23 -4
sound/soc/sof/sof-priv.h
··· 136 136 bool cont_update_posn; 137 137 }; 138 138 139 + /** 140 + * struct sof_firmware - Container struct for SOF firmware 141 + * @fw: Pointer to the firmware 142 + * @payload_offset: Offset of the data within the loaded firmware image to be 143 + * loaded to the DSP (skipping for example ext_manifest section) 144 + */ 145 + struct sof_firmware { 146 + const struct firmware *fw; 147 + u32 payload_offset; 148 + }; 149 + 139 150 /* 140 151 * SOF DSP HW abstraction operations. 141 152 * Used to abstract DSP HW architecture and any IO busses between host CPU ··· 421 410 * DSP. 422 411 * The function implements generic, hardware independent way 423 412 * of loading the initial firmware and its modules (if any). 424 - * @query_fw_configuration: Optional function pointer to query information and 425 - * configuration from the booted firmware. 426 - * Executed after the first successful firmware boot. 427 413 */ 428 414 struct sof_ipc_fw_loader_ops { 429 415 int (*validate)(struct snd_sof_dev *sdev); 430 416 size_t (*parse_ext_manifest)(struct snd_sof_dev *sdev); 431 417 int (*load_fw_to_dsp)(struct snd_sof_dev *sdev); 432 - int (*query_fw_configuration)(struct snd_sof_dev *sdev); 433 418 }; 434 419 435 420 struct sof_ipc_tplg_ops; ··· 438 431 * @pcm: Pointer to PCM ops 439 432 * @fw_loader: Pointer to Firmware Loader ops 440 433 * @fw_tracing: Pointer to Firmware tracing ops 434 + * 435 + * @init: Optional pointer for IPC related initialization 436 + * @exit: Optional pointer for IPC related cleanup 437 + * @post_fw_boot: Optional pointer to execute IPC related tasks after firmware 438 + * boot. 441 439 * 442 440 * @tx_msg: Function pointer for sending a 'short' IPC message 443 441 * @set_get_data: Function pointer for set/get data ('large' IPC message). This ··· 464 452 const struct sof_ipc_pcm_ops *pcm; 465 453 const struct sof_ipc_fw_loader_ops *fw_loader; 466 454 const struct sof_ipc_fw_tracing_ops *fw_tracing; 455 + 456 + int (*init)(struct snd_sof_dev *sdev); 457 + void (*exit)(struct snd_sof_dev *sdev); 458 + int (*post_fw_boot)(struct snd_sof_dev *sdev); 467 459 468 460 int (*tx_msg)(struct snd_sof_dev *sdev, void *msg_data, size_t msg_bytes, 469 461 void *reply_data, size_t reply_bytes, bool no_pm); ··· 502 486 struct device *dev; 503 487 spinlock_t ipc_lock; /* lock for IPC users */ 504 488 spinlock_t hw_lock; /* lock for HW IO access */ 489 + 490 + /* Main, Base firmware image */ 491 + struct sof_firmware basefw; 505 492 506 493 /* 507 494 * ASoC components. plat_drv fields are set dynamically so