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: Intel: avs: Add support for MalibouLake

Merge series from Cezary Rojewski <cezary.rojewski@intel.com>:

The avs-driver is the go-to driver for Intel Automotive. MalibouLake
(MBL) and RedondoLake (RDL) are representatives of the project. These
inherit majority of the featureset from RaptorLake-M (RPL-M) and
AlderLake-N (ADL-N) respectively. The onboard codec for these is TI's
pcm3168a.

In summary, the patchset:

- modifies existing pcm3168a.c to be x86/ACPI friendly
- updates the DSP firmware booting sequence for cAVS 2.5 platforms to
improve its behaviour on some specific revisions/steppings of the
hardware
- adds new machine board driver, avs_pcm3168a
- adds selector entry for RPL-M devices in intel-dspcfg

While there 'ALSA: hda:' patch within the list, I'd prefer the patchset
to go through Mark's tree to avoid conflicts with follow ups to this
one.

Longer version:

Currently the pcm3168a is supported on ARM/DT (ti/j721e-evm.c being the
only user). To make it x86/ACPI friendly, add relevant ACPI-match table
and relax driver's probing conditions.
The default format is 2ch, 24-bits, 48000kHz. As per specification,
24-bits are supported by the chip and it works in production in contrary
to what the existing code suggests. A fix is provided to align the code
with the spec.

Now, a single DSP firmware binary covers a wide range of platforms - a
single one covers AlderLake, RaptorLake and all their derevatires except
for AlderLake-N based due to MEU differences. While most of the hardware
capabilities are read by the firmware during runtime, some information is
not accessible from the DSP level. Provide the HDAudio controller
revision/stepping information to the firmware to address that.

With that done, expand number of modules supported with WovHostModule
(WHM). WHM is a processing module which is tailored for ultra-low-power
scenarios. From software perspective, as most of its config is similar
to the Copier module, code reuse is advised. To make the reuse possible,
existing gateway configuration code is refactor - not only to add
support for WHM but also make it easier to understand. Multiple smaller
functions instead of all-in-one one.

+553 -150
+7
include/uapi/sound/intel/avs/tokens.h
··· 77 77 AVS_TKN_MODCFG_UPDOWN_MIX_CHAN_MAP_U32 = 430, 78 78 AVS_TKN_MODCFG_EXT_NUM_INPUT_PINS_U16 = 431, 79 79 AVS_TKN_MODCFG_EXT_NUM_OUTPUT_PINS_U16 = 432, 80 + AVS_TKN_MODCFG_WHM_REF_AFMT_ID_U32 = 433, 81 + AVS_TKN_MODCFG_WHM_OUT_AFMT_ID_U32 = 434, 82 + AVS_TKN_MODCFG_WHM_WAKE_TICK_PERIOD_U32 = 435, 83 + AVS_TKN_MODCFG_WHM_VINDEX_U8 = 436, 84 + AVS_TKN_MODCFG_WHM_DMA_TYPE_U32 = 437, 85 + AVS_TKN_MODCFG_WHM_DMABUFF_SIZE_U32 = 438, 86 + AVS_TKN_MODCFG_WHM_BLOB_AFMT_ID_U32 = 439, 80 87 81 88 /* struct avs_tplg_pplcfg */ 82 89 AVS_TKN_PPLCFG_ID_U32 = 1401,
+4
sound/hda/intel-dsp-config.c
··· 108 108 {} 109 109 } 110 110 }, 111 + { 112 + .flags = FLAG_SST, 113 + .device = PCI_DEVICE_ID_INTEL_HDA_RPL_M, 114 + }, 111 115 #endif 112 116 #if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE) 113 117 {
+9
sound/soc/codecs/pcm3168a-i2c.c
··· 10 10 #include <linux/i2c.h> 11 11 #include <linux/init.h> 12 12 #include <linux/module.h> 13 + #include <linux/mod_devicetable.h> 13 14 14 15 #include <sound/soc.h> 15 16 ··· 38 37 }; 39 38 MODULE_DEVICE_TABLE(i2c, pcm3168a_i2c_id); 40 39 40 + static const struct acpi_device_id pcm3168a_acpi_match[] = { 41 + { "PCM3168A" }, 42 + { "104C3168" }, 43 + {} 44 + }; 45 + MODULE_DEVICE_TABLE(acpi, pcm3168a_acpi_match); 46 + 41 47 static const struct of_device_id pcm3168a_of_match[] = { 42 48 { .compatible = "ti,pcm3168a", }, 43 49 { } ··· 57 49 .id_table = pcm3168a_i2c_id, 58 50 .driver = { 59 51 .name = "pcm3168a", 52 + .acpi_match_table = pcm3168a_acpi_match, 60 53 .of_match_table = pcm3168a_of_match, 61 54 .pm = &pcm3168a_pm_ops, 62 55 },
+7 -4
sound/soc/codecs/pcm3168a.c
··· 493 493 } 494 494 break; 495 495 case 24: 496 - if (provider_mode || (format == SND_SOC_DAIFMT_DSP_A) || 497 - (format == SND_SOC_DAIFMT_DSP_B)) { 498 - dev_err(component->dev, "24-bit slots not supported in provider mode, or consumer mode using DSP\n"); 496 + if (!provider_mode && ((format == SND_SOC_DAIFMT_DSP_A) || 497 + (format == SND_SOC_DAIFMT_DSP_B))) { 498 + dev_err(component->dev, "24-bit slots not supported in consumer mode using DSP\n"); 499 499 return -EINVAL; 500 500 } 501 501 break; ··· 743 743 return dev_err_probe(dev, PTR_ERR(pcm3168a->gpio_rst), 744 744 "failed to acquire RST gpio\n"); 745 745 746 - pcm3168a->scki = devm_clk_get(dev, "scki"); 746 + pcm3168a->scki = devm_clk_get_optional(dev, "scki"); 747 747 if (IS_ERR(pcm3168a->scki)) 748 748 return dev_err_probe(dev, PTR_ERR(pcm3168a->scki), 749 749 "failed to acquire clock 'scki'\n"); ··· 755 755 } 756 756 757 757 pcm3168a->sysclk = clk_get_rate(pcm3168a->scki); 758 + /* Fallback to the default if no clk entry available. */ 759 + if (!pcm3168a->sysclk) 760 + pcm3168a->sysclk = 24576000; 758 761 759 762 for (i = 0; i < ARRAY_SIZE(pcm3168a->supplies); i++) 760 763 pcm3168a->supplies[i].supply = pcm3168a_supply_names[i];
+1
sound/soc/intel/avs/avs.h
··· 51 51 int (* const load_basefw)(struct avs_dev *, struct firmware *); 52 52 int (* const load_lib)(struct avs_dev *, struct firmware *, u32); 53 53 int (* const transfer_mods)(struct avs_dev *, bool, struct avs_module_entry *, u32); 54 + int (* const config_basefw)(struct avs_dev *); 54 55 int (* const enable_logs)(struct avs_dev *, enum avs_log_enable, u32, u32, unsigned long, 55 56 u32 *); 56 57 int (* const log_buffer_offset)(struct avs_dev *, u32);
+15 -2
sound/soc/intel/avs/board_selection.c
··· 312 312 {}, 313 313 }; 314 314 315 + static struct snd_soc_acpi_mach avs_mbl_i2s_machines[] = { 316 + { 317 + .id = "PCM3168A", 318 + .drv_name = "avs_pcm3168a", 319 + .mach_params = { 320 + .i2s_link_mask = AVS_SSP(0) | AVS_SSP(2), 321 + }, 322 + .tplg_filename = "pcm3168a-tplg.bin", 323 + }, 324 + {} 325 + }; 326 + 315 327 static struct snd_soc_acpi_mach avs_test_i2s_machines[] = { 316 328 { 317 329 .drv_name = "avs_i2s_test", ··· 390 378 AVS_MACH_ENTRY(HDA_ICL_LP, avs_icl_i2s_machines), 391 379 AVS_MACH_ENTRY(HDA_TGL_LP, avs_tgl_i2s_machines), 392 380 AVS_MACH_ENTRY(HDA_EHL_0, avs_tgl_i2s_machines), 381 + AVS_MACH_ENTRY(HDA_ADL_N, avs_mbl_i2s_machines), 393 382 AVS_MACH_ENTRY(HDA_ADL_P, avs_tgl_i2s_machines), 394 383 AVS_MACH_ENTRY(HDA_RPL_P_0, avs_tgl_i2s_machines), 395 - AVS_MACH_ENTRY(HDA_RPL_M, avs_tgl_i2s_machines), 396 - {}, 384 + AVS_MACH_ENTRY(HDA_RPL_M, avs_mbl_i2s_machines), 385 + {} 397 386 }; 398 387 399 388 static const struct avs_acpi_boards *avs_get_i2s_boards(struct avs_dev *adev)
+10
sound/soc/intel/avs/boards/Kconfig
··· 87 87 Say Y or m if you have such a device. This is a recommended option. 88 88 If unsure select "N". 89 89 90 + config SND_SOC_INTEL_AVS_MACH_PCM3168A 91 + tristate "pcm3168a I2S board" 92 + depends on I2C 93 + depends on MFD_INTEL_LPSS || COMPILE_TEST 94 + select SND_SOC_PCM3168A_I2C 95 + help 96 + This adds support for AVS with PCM3168A I2C codec configuration. 97 + Say Y or m if you have such a device. This is a recommended option. 98 + If unsure select "N". 99 + 90 100 config SND_SOC_INTEL_AVS_MACH_PROBE 91 101 tristate "Probing (data) board" 92 102 depends on DEBUG_FS
+2
sound/soc/intel/avs/boards/Makefile
··· 9 9 snd-soc-avs-max98357a-y := max98357a.o 10 10 snd-soc-avs-max98373-y := max98373.o 11 11 snd-soc-avs-nau8825-y := nau8825.o 12 + snd-soc-avs-pcm3168a-y := pcm3168a.o 12 13 snd-soc-avs-probe-y := probe.o 13 14 snd-soc-avs-rt274-y := rt274.o 14 15 snd-soc-avs-rt286-y := rt286.o ··· 28 27 obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_MAX98357A) += snd-soc-avs-max98357a.o 29 28 obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_MAX98373) += snd-soc-avs-max98373.o 30 29 obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_NAU8825) += snd-soc-avs-nau8825.o 30 + obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_PCM3168A) += snd-soc-avs-pcm3168a.o 31 31 obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_PROBE) += snd-soc-avs-probe.o 32 32 obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT274) += snd-soc-avs-rt274.o 33 33 obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT286) += snd-soc-avs-rt286.o
+143
sound/soc/intel/avs/boards/pcm3168a.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + // 3 + // Copyright(c) 2024-2025 Intel Corporation 4 + // 5 + // Author: Cezary Rojewski <cezary.rojewski@intel.com> 6 + // 7 + 8 + #include <linux/module.h> 9 + #include <linux/platform_device.h> 10 + #include <sound/core.h> 11 + #include <sound/pcm.h> 12 + #include <sound/pcm_params.h> 13 + #include <sound/soc.h> 14 + 15 + static const struct snd_soc_dapm_widget card_widgets[] = { 16 + SND_SOC_DAPM_HP("CPB Stereo HP 1", NULL), 17 + SND_SOC_DAPM_HP("CPB Stereo HP 2", NULL), 18 + SND_SOC_DAPM_HP("CPB Stereo HP 3", NULL), 19 + SND_SOC_DAPM_LINE("CPB Line Out", NULL), 20 + SND_SOC_DAPM_MIC("CPB Stereo Mic 1", NULL), 21 + SND_SOC_DAPM_MIC("CPB Stereo Mic 2", NULL), 22 + SND_SOC_DAPM_LINE("CPB Line In", NULL), 23 + }; 24 + 25 + static const struct snd_soc_dapm_route card_routes[] = { 26 + { "CPB Stereo HP 1", NULL, "AOUT1L" }, 27 + { "CPB Stereo HP 1", NULL, "AOUT1R" }, 28 + { "CPB Stereo HP 2", NULL, "AOUT2L" }, 29 + { "CPB Stereo HP 2", NULL, "AOUT2R" }, 30 + { "CPB Stereo HP 3", NULL, "AOUT3L" }, 31 + { "CPB Stereo HP 3", NULL, "AOUT3R" }, 32 + { "CPB Line Out", NULL, "AOUT4L" }, 33 + { "CPB Line Out", NULL, "AOUT4R" }, 34 + 35 + { "AIN1L", NULL, "CPB Stereo Mic 1" }, 36 + { "AIN1R", NULL, "CPB Stereo Mic 1" }, 37 + { "AIN2L", NULL, "CPB Stereo Mic 2" }, 38 + { "AIN2R", NULL, "CPB Stereo Mic 2" }, 39 + { "AIN3L", NULL, "CPB Line In" }, 40 + { "AIN3R", NULL, "CPB Line In" }, 41 + }; 42 + 43 + static int avs_pcm3168a_be_fixup(struct snd_soc_pcm_runtime *runtime, 44 + struct snd_pcm_hw_params *params) 45 + { 46 + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); 47 + 48 + /* Set SSP to 24 bit. */ 49 + snd_mask_none(fmt); 50 + snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE); 51 + 52 + return 0; 53 + } 54 + 55 + SND_SOC_DAILINK_DEF(pcm3168a_dac, 56 + DAILINK_COMP_ARRAY(COMP_CODEC("i2c-PCM3168A:00", "pcm3168a-dac"))); 57 + SND_SOC_DAILINK_DEF(pcm3168a_adc, 58 + DAILINK_COMP_ARRAY(COMP_CODEC("i2c-PCM3168A:00", "pcm3168a-adc"))); 59 + SND_SOC_DAILINK_DEF(cpu_ssp0, DAILINK_COMP_ARRAY(COMP_CPU("SSP0 Pin"))); 60 + SND_SOC_DAILINK_DEF(cpu_ssp2, DAILINK_COMP_ARRAY(COMP_CPU("SSP2 Pin"))); 61 + 62 + static int avs_create_dai_links(struct device *dev, struct snd_soc_dai_link **links, int *num_links) 63 + { 64 + struct snd_soc_dai_link_component *platform; 65 + struct snd_soc_dai_link *dl; 66 + const int num_dl = 2; 67 + 68 + dl = devm_kcalloc(dev, num_dl, sizeof(*dl), GFP_KERNEL); 69 + platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL); 70 + if (!dl || !platform) 71 + return -ENOMEM; 72 + 73 + platform->name = dev_name(dev); 74 + dl[0].num_cpus = 1; 75 + dl[0].num_codecs = 1; 76 + dl[0].platforms = platform; 77 + dl[0].num_platforms = 1; 78 + dl[0].dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP; 79 + dl[0].be_hw_params_fixup = avs_pcm3168a_be_fixup; 80 + dl[0].nonatomic = 1; 81 + dl[0].no_pcm = 1; 82 + memcpy(&dl[1], &dl[0], sizeof(*dl)); 83 + 84 + dl[0].name = "SSP0-Codec-dac"; 85 + dl[0].cpus = cpu_ssp0; 86 + dl[0].codecs = pcm3168a_dac; 87 + dl[1].name = "SSP2-Codec-adc"; 88 + dl[1].cpus = cpu_ssp2; 89 + dl[1].codecs = pcm3168a_adc; 90 + 91 + *links = dl; 92 + *num_links = num_dl; 93 + return 0; 94 + } 95 + 96 + static int avs_pcm3168a_probe(struct platform_device *pdev) 97 + { 98 + struct device *dev = &pdev->dev; 99 + struct snd_soc_card *card; 100 + int ret; 101 + 102 + card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); 103 + if (!card) 104 + return -ENOMEM; 105 + 106 + ret = avs_create_dai_links(dev, &card->dai_link, &card->num_links); 107 + if (ret) 108 + return ret; 109 + 110 + card->name = "avs_pcm3168a"; 111 + card->dev = dev; 112 + card->owner = THIS_MODULE; 113 + card->dapm_widgets = card_widgets; 114 + card->num_dapm_widgets = ARRAY_SIZE(card_widgets); 115 + card->dapm_routes = card_routes; 116 + card->num_dapm_routes = ARRAY_SIZE(card_routes); 117 + card->fully_routed = true; 118 + 119 + return devm_snd_soc_register_card(dev, card); 120 + } 121 + 122 + static const struct platform_device_id avs_pcm3168a_driver_ids[] = { 123 + { 124 + .name = "avs_pcm3168a", 125 + }, 126 + {}, 127 + }; 128 + MODULE_DEVICE_TABLE(platform, avs_pcm3168a_driver_ids); 129 + 130 + static struct platform_driver avs_pcm3168a_driver = { 131 + .probe = avs_pcm3168a_probe, 132 + .driver = { 133 + .name = "avs_pcm3168a", 134 + .pm = &snd_soc_pm_ops, 135 + }, 136 + .id_table = avs_pcm3168a_driver_ids, 137 + }; 138 + 139 + module_platform_driver(avs_pcm3168a_driver); 140 + 141 + MODULE_DESCRIPTION("Intel pcm3168a machine driver"); 142 + MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>"); 143 + MODULE_LICENSE("GPL");
+52 -22
sound/soc/intel/avs/loader.c
··· 603 603 return ret; 604 604 } 605 605 606 - int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge) 606 + static int avs_load_firmware(struct avs_dev *adev, bool purge) 607 607 { 608 608 struct avs_soc_component *acomp; 609 609 int ret, i; ··· 657 657 return 0; 658 658 } 659 659 660 - int avs_dsp_first_boot_firmware(struct avs_dev *adev) 660 + static int avs_config_basefw(struct avs_dev *adev) 661 + { 662 + int ret; 663 + 664 + if (adev->spec->dsp_ops->config_basefw) { 665 + ret = avs_dsp_op(adev, config_basefw); 666 + if (ret) 667 + return ret; 668 + } 669 + 670 + return 0; 671 + } 672 + 673 + int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge) 674 + { 675 + int ret; 676 + 677 + ret = avs_load_firmware(adev, purge); 678 + if (ret) 679 + return ret; 680 + 681 + return avs_config_basefw(adev); 682 + } 683 + 684 + static int avs_dsp_alloc_resources(struct avs_dev *adev) 661 685 { 662 686 int ret, i; 663 - 664 - if (avs_platattr_test(adev, CLDMA)) { 665 - ret = hda_cldma_init(&code_loader, &adev->base.core, 666 - adev->dsp_ba, AVS_CL_DEFAULT_BUFFER_SIZE); 667 - if (ret < 0) { 668 - dev_err(adev->dev, "cldma init failed: %d\n", ret); 669 - return ret; 670 - } 671 - } 672 - 673 - ret = avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK); 674 - if (ret < 0) 675 - return ret; 676 - 677 - ret = avs_dsp_boot_firmware(adev, true); 678 - if (ret < 0) { 679 - dev_err(adev->dev, "firmware boot failed: %d\n", ret); 680 - return ret; 681 - } 682 687 683 688 ret = avs_ipc_get_hw_config(adev, &adev->hw_cfg); 684 689 if (ret) ··· 710 705 strscpy(adev->lib_names[0], "BASEFW", AVS_LIB_NAME_SIZE); 711 706 712 707 ida_init(&adev->ppl_ida); 713 - 714 708 return 0; 709 + } 710 + 711 + int avs_dsp_first_boot_firmware(struct avs_dev *adev) 712 + { 713 + int ret; 714 + 715 + if (avs_platattr_test(adev, CLDMA)) { 716 + ret = hda_cldma_init(&code_loader, &adev->base.core, 717 + adev->dsp_ba, AVS_CL_DEFAULT_BUFFER_SIZE); 718 + if (ret < 0) { 719 + dev_err(adev->dev, "cldma init failed: %d\n", ret); 720 + return ret; 721 + } 722 + } 723 + 724 + ret = avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK); 725 + if (ret < 0) 726 + return ret; 727 + 728 + ret = avs_dsp_boot_firmware(adev, true); 729 + if (ret < 0) { 730 + dev_err(adev->dev, "firmware boot failed: %d\n", ret); 731 + return ret; 732 + } 733 + 734 + return avs_dsp_alloc_resources(adev); 715 735 }
+38
sound/soc/intel/avs/messages.c
··· 510 510 return ret; 511 511 } 512 512 513 + int avs_ipc_set_fw_config(struct avs_dev *adev, size_t num_tlvs, ...) 514 + { 515 + struct avs_tlv *tlv; 516 + void *payload; 517 + size_t offset; 518 + va_list args; 519 + int ret, i; 520 + 521 + payload = kzalloc(AVS_MAILBOX_SIZE, GFP_KERNEL); 522 + if (!payload) 523 + return -ENOMEM; 524 + 525 + va_start(args, num_tlvs); 526 + for (offset = i = 0; i < num_tlvs && offset < AVS_MAILBOX_SIZE - sizeof(*tlv); i++) { 527 + tlv = (struct avs_tlv *)(payload + offset); 528 + tlv->type = va_arg(args, u32); 529 + tlv->length = va_arg(args, u32); 530 + 531 + offset += sizeof(*tlv) + tlv->length; 532 + if (offset > AVS_MAILBOX_SIZE) 533 + break; 534 + 535 + memcpy(tlv->value, va_arg(args, u8*), tlv->length); 536 + } 537 + 538 + if (i == num_tlvs) 539 + ret = avs_ipc_set_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID, 540 + AVS_BASEFW_FIRMWARE_CONFIG, payload, offset); 541 + else 542 + ret = -ERANGE; 543 + 544 + va_end(args); 545 + kfree(payload); 546 + if (ret) 547 + dev_err(adev->dev, "set fw cfg failed: %d\n", ret); 548 + return ret; 549 + } 550 + 513 551 int avs_ipc_get_hw_config(struct avs_dev *adev, struct avs_hw_cfg *cfg) 514 552 { 515 553 struct avs_tlv *tlv;
+22
sound/soc/intel/avs/messages.h
··· 451 451 AVS_FW_CFG_RESERVED, 452 452 AVS_FW_CFG_POWER_GATING_POLICY, 453 453 AVS_FW_CFG_ASSERT_MODE, 454 + AVS_FW_CFG_RESERVED2, 455 + AVS_FW_CFG_BUS_HARDWARE_ID, 454 456 }; 455 457 456 458 struct avs_fw_cfg { ··· 477 475 u32 power_gating_policy; 478 476 }; 479 477 478 + struct avs_bus_hwid { 479 + u32 device; 480 + u32 subsystem; 481 + u8 revision; 482 + }; 483 + 480 484 int avs_ipc_get_fw_config(struct avs_dev *adev, struct avs_fw_cfg *cfg); 485 + int avs_ipc_set_fw_config(struct avs_dev *adev, size_t num_tlvs, ...); 481 486 482 487 enum avs_hw_cfg_params { 483 488 AVS_HW_CFG_AVS_VER, ··· 651 642 652 643 #define AVS_INTELWOV_MOD_UUID \ 653 644 GUID_INIT(0xEC774FA9, 0x28D3, 0x424A, 0x90, 0xE4, 0x69, 0xF9, 0x84, 0xF1, 0xEE, 0xB7) 645 + 646 + #define AVS_WOVHOSTM_MOD_UUID \ 647 + GUID_INIT(0xF9ED62B7, 0x092E, 0x4A90, 0x8F, 0x4D, 0x82, 0xDA, 0xA8, 0xB3, 0x8F, 0x3B) 654 648 655 649 /* channel map */ 656 650 enum avs_channel_index { ··· 883 871 u32 cpc_lp_mode; 884 872 } __packed; 885 873 static_assert(sizeof(struct avs_wov_cfg) == 44); 874 + 875 + struct avs_whm_cfg { 876 + struct avs_modcfg_base base; 877 + /* Audio format for output pin 0 */ 878 + struct avs_audio_format ref_fmt; 879 + struct avs_audio_format out_fmt; 880 + u32 wake_tick_period; 881 + struct avs_copier_gtw_cfg gtw_cfg; 882 + } __packed; 883 + static_assert(sizeof(struct avs_whm_cfg) == 108); 886 884 887 885 /* Module runtime parameters */ 888 886
+159 -120
sound/soc/intel/avs/path.c
··· 115 115 return NULL; 116 116 } 117 117 118 - __maybe_unused 119 - static bool avs_dma_type_is_host(u32 dma_type) 118 + static void avs_init_node_id(union avs_connector_node_id *node_id, 119 + struct avs_tplg_modcfg_ext *te, u32 dma_id) 120 120 { 121 - return dma_type == AVS_DMA_HDA_HOST_OUTPUT || 122 - dma_type == AVS_DMA_HDA_HOST_INPUT; 121 + node_id->val = 0; 122 + node_id->dma_type = te->copier.dma_type; 123 + 124 + switch (node_id->dma_type) { 125 + case AVS_DMA_DMIC_LINK_INPUT: 126 + case AVS_DMA_I2S_LINK_OUTPUT: 127 + case AVS_DMA_I2S_LINK_INPUT: 128 + /* Gateway's virtual index is statically assigned in the topology. */ 129 + node_id->vindex = te->copier.vindex.val; 130 + break; 131 + 132 + case AVS_DMA_HDA_HOST_OUTPUT: 133 + case AVS_DMA_HDA_HOST_INPUT: 134 + /* Gateway's virtual index is dynamically assigned with DMA ID */ 135 + node_id->vindex = dma_id; 136 + break; 137 + 138 + case AVS_DMA_HDA_LINK_OUTPUT: 139 + case AVS_DMA_HDA_LINK_INPUT: 140 + node_id->vindex = te->copier.vindex.val | dma_id; 141 + break; 142 + 143 + default: 144 + *node_id = INVALID_NODE_ID; 145 + break; 146 + } 123 147 } 124 148 125 - __maybe_unused 126 - static bool avs_dma_type_is_link(u32 dma_type) 149 + /* Every BLOB contains at least gateway attributes. */ 150 + static struct acpi_nhlt_config *default_blob = (struct acpi_nhlt_config *)&(u32[2]) {4}; 151 + 152 + static struct acpi_nhlt_config * 153 + avs_nhlt_config_or_default(struct avs_dev *adev, struct avs_tplg_module *t) 127 154 { 128 - return !avs_dma_type_is_host(dma_type); 155 + struct acpi_nhlt_format_config *fmtcfg; 156 + struct avs_tplg_modcfg_ext *te; 157 + struct avs_audio_format *fmt; 158 + int link_type, dev_type; 159 + int bus_id, dir; 160 + 161 + te = t->cfg_ext; 162 + 163 + switch (te->copier.dma_type) { 164 + case AVS_DMA_I2S_LINK_OUTPUT: 165 + link_type = ACPI_NHLT_LINKTYPE_SSP; 166 + dev_type = ACPI_NHLT_DEVICETYPE_CODEC; 167 + bus_id = te->copier.vindex.i2s.instance; 168 + dir = SNDRV_PCM_STREAM_PLAYBACK; 169 + fmt = te->copier.out_fmt; 170 + break; 171 + 172 + case AVS_DMA_I2S_LINK_INPUT: 173 + link_type = ACPI_NHLT_LINKTYPE_SSP; 174 + dev_type = ACPI_NHLT_DEVICETYPE_CODEC; 175 + bus_id = te->copier.vindex.i2s.instance; 176 + dir = SNDRV_PCM_STREAM_CAPTURE; 177 + fmt = t->in_fmt; 178 + break; 179 + 180 + case AVS_DMA_DMIC_LINK_INPUT: 181 + link_type = ACPI_NHLT_LINKTYPE_PDM; 182 + dev_type = -1; /* ignored */ 183 + bus_id = 0; 184 + dir = SNDRV_PCM_STREAM_CAPTURE; 185 + fmt = t->in_fmt; 186 + break; 187 + 188 + default: 189 + return default_blob; 190 + } 191 + 192 + /* Override format selection if necessary. */ 193 + if (te->copier.blob_fmt) 194 + fmt = te->copier.blob_fmt; 195 + 196 + fmtcfg = acpi_nhlt_find_fmtcfg(link_type, dev_type, dir, bus_id, 197 + fmt->num_channels, fmt->sampling_freq, fmt->valid_bit_depth, 198 + fmt->bit_depth); 199 + if (!fmtcfg) { 200 + dev_warn(adev->dev, "Endpoint format configuration not found.\n"); 201 + return ERR_PTR(-ENOENT); 202 + } 203 + 204 + if (fmtcfg->config.capabilities_size < default_blob->capabilities_size) 205 + return ERR_PTR(-ETOOSMALL); 206 + /* The firmware expects the payload to be DWORD-aligned. */ 207 + if (fmtcfg->config.capabilities_size % sizeof(u32)) 208 + return ERR_PTR(-EINVAL); 209 + 210 + return &fmtcfg->config; 129 211 } 130 212 131 - __maybe_unused 132 - static bool avs_dma_type_is_output(u32 dma_type) 213 + static int avs_fill_gtw_config(struct avs_dev *adev, struct avs_copier_gtw_cfg *gtw, 214 + struct avs_tplg_module *t, size_t *cfg_size) 133 215 { 134 - return dma_type == AVS_DMA_HDA_HOST_OUTPUT || 135 - dma_type == AVS_DMA_HDA_LINK_OUTPUT || 136 - dma_type == AVS_DMA_I2S_LINK_OUTPUT; 137 - } 216 + struct acpi_nhlt_config *blob; 217 + size_t gtw_size; 138 218 139 - __maybe_unused 140 - static bool avs_dma_type_is_input(u32 dma_type) 141 - { 142 - return !avs_dma_type_is_output(dma_type); 219 + blob = avs_nhlt_config_or_default(adev, t); 220 + if (IS_ERR(blob)) 221 + return PTR_ERR(blob); 222 + 223 + gtw_size = blob->capabilities_size; 224 + if (*cfg_size + gtw_size > AVS_MAILBOX_SIZE) 225 + return -E2BIG; 226 + 227 + gtw->config_length = gtw_size / sizeof(u32); 228 + memcpy(gtw->config.blob, blob->capabilities, blob->capabilities_size); 229 + *cfg_size += gtw_size; 230 + 231 + return 0; 143 232 } 144 233 145 234 static int avs_copier_create(struct avs_dev *adev, struct avs_path_module *mod) 146 235 { 147 236 struct avs_tplg_module *t = mod->template; 237 + struct avs_tplg_modcfg_ext *te; 148 238 struct avs_copier_cfg *cfg; 149 - struct acpi_nhlt_format_config *ep_blob; 150 - struct acpi_nhlt_endpoint *ep; 151 - union avs_connector_node_id node_id = {0}; 152 - size_t cfg_size, data_size; 153 - void *data = NULL; 154 - u32 dma_type; 239 + size_t cfg_size; 240 + u32 dma_id; 155 241 int ret; 156 242 157 - data_size = sizeof(cfg->gtw_cfg.config); 158 - dma_type = t->cfg_ext->copier.dma_type; 159 - node_id.dma_type = dma_type; 160 - 161 - switch (dma_type) { 162 - struct avs_audio_format *fmt; 163 - int direction; 164 - 165 - case AVS_DMA_I2S_LINK_OUTPUT: 166 - case AVS_DMA_I2S_LINK_INPUT: 167 - if (avs_dma_type_is_input(dma_type)) 168 - direction = SNDRV_PCM_STREAM_CAPTURE; 169 - else 170 - direction = SNDRV_PCM_STREAM_PLAYBACK; 171 - 172 - if (t->cfg_ext->copier.blob_fmt) 173 - fmt = t->cfg_ext->copier.blob_fmt; 174 - else if (direction == SNDRV_PCM_STREAM_CAPTURE) 175 - fmt = t->in_fmt; 176 - else 177 - fmt = t->cfg_ext->copier.out_fmt; 178 - 179 - ep = acpi_nhlt_find_endpoint(ACPI_NHLT_LINKTYPE_SSP, 180 - ACPI_NHLT_DEVICETYPE_CODEC, direction, 181 - t->cfg_ext->copier.vindex.i2s.instance); 182 - ep_blob = acpi_nhlt_endpoint_find_fmtcfg(ep, fmt->num_channels, fmt->sampling_freq, 183 - fmt->valid_bit_depth, fmt->bit_depth); 184 - if (!ep_blob) { 185 - dev_err(adev->dev, "no I2S ep_blob found\n"); 186 - return -ENOENT; 187 - } 188 - 189 - data = ep_blob->config.capabilities; 190 - data_size = ep_blob->config.capabilities_size; 191 - /* I2S gateway's vindex is statically assigned in topology */ 192 - node_id.vindex = t->cfg_ext->copier.vindex.val; 193 - 194 - break; 195 - 196 - case AVS_DMA_DMIC_LINK_INPUT: 197 - direction = SNDRV_PCM_STREAM_CAPTURE; 198 - 199 - if (t->cfg_ext->copier.blob_fmt) 200 - fmt = t->cfg_ext->copier.blob_fmt; 201 - else 202 - fmt = t->in_fmt; 203 - 204 - ep = acpi_nhlt_find_endpoint(ACPI_NHLT_LINKTYPE_PDM, -1, direction, 0); 205 - ep_blob = acpi_nhlt_endpoint_find_fmtcfg(ep, fmt->num_channels, fmt->sampling_freq, 206 - fmt->valid_bit_depth, fmt->bit_depth); 207 - if (!ep_blob) { 208 - dev_err(adev->dev, "no DMIC ep_blob found\n"); 209 - return -ENOENT; 210 - } 211 - 212 - data = ep_blob->config.capabilities; 213 - data_size = ep_blob->config.capabilities_size; 214 - /* DMIC gateway's vindex is statically assigned in topology */ 215 - node_id.vindex = t->cfg_ext->copier.vindex.val; 216 - 217 - break; 218 - 219 - case AVS_DMA_HDA_HOST_OUTPUT: 220 - case AVS_DMA_HDA_HOST_INPUT: 221 - /* HOST gateway's vindex is dynamically assigned with DMA id */ 222 - node_id.vindex = mod->owner->owner->dma_id; 223 - break; 224 - 225 - case AVS_DMA_HDA_LINK_OUTPUT: 226 - case AVS_DMA_HDA_LINK_INPUT: 227 - node_id.vindex = t->cfg_ext->copier.vindex.val | 228 - mod->owner->owner->dma_id; 229 - break; 230 - 231 - case INVALID_OBJECT_ID: 232 - default: 233 - node_id = INVALID_NODE_ID; 234 - break; 235 - } 236 - 237 - cfg_size = offsetof(struct avs_copier_cfg, gtw_cfg.config) + data_size; 238 - if (cfg_size > AVS_MAILBOX_SIZE) 239 - return -EINVAL; 240 - 243 + te = t->cfg_ext; 241 244 cfg = adev->modcfg_buf; 242 - memset(cfg, 0, cfg_size); 245 + dma_id = mod->owner->owner->dma_id; 246 + cfg_size = offsetof(struct avs_copier_cfg, gtw_cfg.config); 247 + 248 + ret = avs_fill_gtw_config(adev, &cfg->gtw_cfg, t, &cfg_size); 249 + if (ret) 250 + return ret; 251 + 243 252 cfg->base.cpc = t->cfg_base->cpc; 244 253 cfg->base.ibs = t->cfg_base->ibs; 245 254 cfg->base.obs = t->cfg_base->obs; 246 255 cfg->base.is_pages = t->cfg_base->is_pages; 247 256 cfg->base.audio_fmt = *t->in_fmt; 248 - cfg->out_fmt = *t->cfg_ext->copier.out_fmt; 249 - cfg->feature_mask = t->cfg_ext->copier.feature_mask; 250 - cfg->gtw_cfg.node_id = node_id; 251 - cfg->gtw_cfg.dma_buffer_size = t->cfg_ext->copier.dma_buffer_size; 252 - /* config_length in DWORDs */ 253 - cfg->gtw_cfg.config_length = DIV_ROUND_UP(data_size, 4); 254 - if (data) 255 - memcpy(&cfg->gtw_cfg.config.blob, data, data_size); 256 - 257 + cfg->out_fmt = *te->copier.out_fmt; 258 + cfg->feature_mask = te->copier.feature_mask; 259 + avs_init_node_id(&cfg->gtw_cfg.node_id, te, dma_id); 260 + cfg->gtw_cfg.dma_buffer_size = te->copier.dma_buffer_size; 257 261 mod->gtw_attrs = cfg->gtw_cfg.config.attrs; 258 262 259 - ret = avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id, 260 - t->core_id, t->domain, cfg, cfg_size, 261 - &mod->instance_id); 263 + ret = avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id, t->core_id, 264 + t->domain, cfg, cfg_size, &mod->instance_id); 265 + return ret; 266 + } 267 + 268 + static int avs_whm_create(struct avs_dev *adev, struct avs_path_module *mod) 269 + { 270 + struct avs_tplg_module *t = mod->template; 271 + struct avs_tplg_modcfg_ext *te; 272 + struct avs_whm_cfg *cfg; 273 + size_t cfg_size; 274 + u32 dma_id; 275 + int ret; 276 + 277 + te = t->cfg_ext; 278 + cfg = adev->modcfg_buf; 279 + dma_id = mod->owner->owner->dma_id; 280 + cfg_size = offsetof(struct avs_whm_cfg, gtw_cfg.config); 281 + 282 + ret = avs_fill_gtw_config(adev, &cfg->gtw_cfg, t, &cfg_size); 283 + if (ret) 284 + return ret; 285 + 286 + cfg->base.cpc = t->cfg_base->cpc; 287 + cfg->base.ibs = t->cfg_base->ibs; 288 + cfg->base.obs = t->cfg_base->obs; 289 + cfg->base.is_pages = t->cfg_base->is_pages; 290 + cfg->base.audio_fmt = *t->in_fmt; 291 + cfg->ref_fmt = *te->whm.ref_fmt; 292 + cfg->out_fmt = *te->whm.out_fmt; 293 + cfg->wake_tick_period = te->whm.wake_tick_period; 294 + avs_init_node_id(&cfg->gtw_cfg.node_id, te, dma_id); 295 + cfg->gtw_cfg.dma_buffer_size = te->whm.dma_buffer_size; 296 + mod->gtw_attrs = cfg->gtw_cfg.config.attrs; 297 + 298 + ret = avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id, t->core_id, 299 + t->domain, cfg, cfg_size, &mod->instance_id); 262 300 return ret; 263 301 } 264 302 ··· 571 533 { &AVS_ASRC_MOD_UUID, avs_asrc_create }, 572 534 { &AVS_INTELWOV_MOD_UUID, avs_wov_create }, 573 535 { &AVS_PROBE_MOD_UUID, avs_probe_create }, 536 + { &AVS_WOVHOSTM_MOD_UUID, avs_whm_create }, 574 537 }; 575 538 576 539 static int avs_path_module_type_create(struct avs_dev *adev, struct avs_path_module *mod)
+33
sound/soc/intel/avs/tgl.c
··· 6 6 // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> 7 7 // 8 8 9 + #include <linux/pci.h> 9 10 #include "avs.h" 11 + #include "messages.h" 12 + 13 + #define CPUID_TSC_LEAF 0x15 10 14 11 15 static int avs_tgl_dsp_core_power(struct avs_dev *adev, u32 core_mask, bool power) 12 16 { ··· 39 35 return avs_dsp_core_stall(adev, core_mask, stall); 40 36 } 41 37 38 + static int avs_tgl_config_basefw(struct avs_dev *adev) 39 + { 40 + struct pci_dev *pci = adev->base.pci; 41 + struct avs_bus_hwid hwid; 42 + int ret; 43 + #ifdef CONFIG_X86 44 + unsigned int ecx; 45 + 46 + #include <asm/cpuid.h> 47 + ecx = cpuid_ecx(CPUID_TSC_LEAF); 48 + if (ecx) { 49 + ret = avs_ipc_set_fw_config(adev, 1, AVS_FW_CFG_XTAL_FREQ_HZ, sizeof(ecx), &ecx); 50 + if (ret) 51 + return AVS_IPC_RET(ret); 52 + } 53 + #endif 54 + 55 + hwid.device = pci->device; 56 + hwid.subsystem = pci->subsystem_vendor | (pci->subsystem_device << 16); 57 + hwid.revision = pci->revision; 58 + 59 + ret = avs_ipc_set_fw_config(adev, 1, AVS_FW_CFG_BUS_HARDWARE_ID, sizeof(hwid), &hwid); 60 + if (ret) 61 + return AVS_IPC_RET(ret); 62 + 63 + return 0; 64 + } 65 + 42 66 const struct avs_dsp_ops avs_tgl_dsp_ops = { 43 67 .power = avs_tgl_dsp_core_power, 44 68 .reset = avs_tgl_dsp_core_reset, ··· 76 44 .load_basefw = avs_icl_load_basefw, 77 45 .load_lib = avs_hda_load_library, 78 46 .transfer_mods = avs_hda_transfer_modules, 47 + .config_basefw = avs_tgl_config_basefw, 79 48 .log_buffer_offset = avs_icl_log_buffer_offset, 80 49 .log_buffer_status = avs_apl_log_buffer_status, 81 50 .coredump = avs_apl_coredump,
+42
sound/soc/intel/avs/topology.c
··· 815 815 .offset = offsetof(struct avs_tplg_modcfg_ext, generic.num_output_pins), 816 816 .parse = avs_parse_short_token, 817 817 }, 818 + { 819 + .token = AVS_TKN_MODCFG_WHM_REF_AFMT_ID_U32, 820 + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, 821 + .offset = offsetof(struct avs_tplg_modcfg_ext, whm.ref_fmt), 822 + .parse = avs_parse_audio_format_ptr, 823 + }, 824 + { 825 + .token = AVS_TKN_MODCFG_WHM_OUT_AFMT_ID_U32, 826 + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, 827 + .offset = offsetof(struct avs_tplg_modcfg_ext, whm.out_fmt), 828 + .parse = avs_parse_audio_format_ptr, 829 + }, 830 + { 831 + .token = AVS_TKN_MODCFG_WHM_WAKE_TICK_PERIOD_U32, 832 + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, 833 + .offset = offsetof(struct avs_tplg_modcfg_ext, whm.wake_tick_period), 834 + .parse = avs_parse_word_token, 835 + }, 836 + { 837 + .token = AVS_TKN_MODCFG_WHM_VINDEX_U8, 838 + .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE, 839 + .offset = offsetof(struct avs_tplg_modcfg_ext, whm.vindex), 840 + .parse = avs_parse_byte_token, 841 + }, 842 + { 843 + .token = AVS_TKN_MODCFG_WHM_DMA_TYPE_U32, 844 + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, 845 + .offset = offsetof(struct avs_tplg_modcfg_ext, whm.dma_type), 846 + .parse = avs_parse_word_token, 847 + }, 848 + { 849 + .token = AVS_TKN_MODCFG_WHM_DMABUFF_SIZE_U32, 850 + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, 851 + .offset = offsetof(struct avs_tplg_modcfg_ext, whm.dma_buffer_size), 852 + .parse = avs_parse_word_token, 853 + }, 854 + { 855 + .token = AVS_TKN_MODCFG_WHM_BLOB_AFMT_ID_U32, 856 + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, 857 + .offset = offsetof(struct avs_tplg_modcfg_ext, whm.blob_fmt), 858 + .parse = avs_parse_audio_format_ptr, 859 + }, 818 860 }; 819 861 820 862 static const struct avs_tplg_token_parser pin_format_parsers[] = {
+9 -2
sound/soc/intel/avs/topology.h
··· 74 74 union avs_virtual_index vindex; 75 75 u32 dma_type; 76 76 u32 dma_buffer_size; 77 - u32 config_length; 78 - /* config_data part of priv data */ 79 77 } copier; 78 + struct { 79 + struct avs_audio_format *ref_fmt; 80 + struct avs_audio_format *out_fmt; 81 + u32 wake_tick_period; 82 + union avs_virtual_index vindex; 83 + u32 dma_type; 84 + u32 dma_buffer_size; 85 + struct avs_audio_format *blob_fmt; /* optional override */ 86 + } whm; 80 87 struct { 81 88 u32 out_channel_config; 82 89 u32 coefficients_select;