summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2025-02-06 20:44:46 +0000
committerMark Brown <broonie@kernel.org>2025-02-06 20:44:46 +0000
commita23ff143804d3b8c27157ffa19e48b4e22939115 (patch)
treee61744e089fc440efdcd41a991b3f8dc15e77655
parent4c7518062d638837cea915e0ffe30f846780639a (diff)
parent856366dc924a9561dae39f252b45dfd6cc6895ce (diff)
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.
-rw-r--r--include/uapi/sound/intel/avs/tokens.h7
-rw-r--r--sound/hda/intel-dsp-config.c4
-rw-r--r--sound/soc/codecs/pcm3168a-i2c.c9
-rw-r--r--sound/soc/codecs/pcm3168a.c11
-rw-r--r--sound/soc/intel/avs/avs.h1
-rw-r--r--sound/soc/intel/avs/board_selection.c17
-rw-r--r--sound/soc/intel/avs/boards/Kconfig10
-rw-r--r--sound/soc/intel/avs/boards/Makefile2
-rw-r--r--sound/soc/intel/avs/boards/pcm3168a.c143
-rw-r--r--sound/soc/intel/avs/loader.c64
-rw-r--r--sound/soc/intel/avs/messages.c38
-rw-r--r--sound/soc/intel/avs/messages.h22
-rw-r--r--sound/soc/intel/avs/path.c267
-rw-r--r--sound/soc/intel/avs/tgl.c33
-rw-r--r--sound/soc/intel/avs/topology.c42
-rw-r--r--sound/soc/intel/avs/topology.h11
16 files changed, 542 insertions, 139 deletions
diff --git a/include/uapi/sound/intel/avs/tokens.h b/include/uapi/sound/intel/avs/tokens.h
index 3e3fb258dd54..06ff30537f47 100644
--- a/include/uapi/sound/intel/avs/tokens.h
+++ b/include/uapi/sound/intel/avs/tokens.h
@@ -77,6 +77,13 @@ enum avs_tplg_token {
AVS_TKN_MODCFG_UPDOWN_MIX_CHAN_MAP_U32 = 430,
AVS_TKN_MODCFG_EXT_NUM_INPUT_PINS_U16 = 431,
AVS_TKN_MODCFG_EXT_NUM_OUTPUT_PINS_U16 = 432,
+ AVS_TKN_MODCFG_WHM_REF_AFMT_ID_U32 = 433,
+ AVS_TKN_MODCFG_WHM_OUT_AFMT_ID_U32 = 434,
+ AVS_TKN_MODCFG_WHM_WAKE_TICK_PERIOD_U32 = 435,
+ AVS_TKN_MODCFG_WHM_VINDEX_U8 = 436,
+ AVS_TKN_MODCFG_WHM_DMA_TYPE_U32 = 437,
+ AVS_TKN_MODCFG_WHM_DMABUFF_SIZE_U32 = 438,
+ AVS_TKN_MODCFG_WHM_BLOB_AFMT_ID_U32 = 439,
/* struct avs_tplg_pplcfg */
AVS_TKN_PPLCFG_ID_U32 = 1401,
diff --git a/sound/hda/intel-dsp-config.c b/sound/hda/intel-dsp-config.c
index f564ec7af194..9a8ead75be17 100644
--- a/sound/hda/intel-dsp-config.c
+++ b/sound/hda/intel-dsp-config.c
@@ -108,6 +108,10 @@ static const struct config_entry config_table[] = {
{}
}
},
+ {
+ .flags = FLAG_SST,
+ .device = PCI_DEVICE_ID_INTEL_HDA_RPL_M,
+ },
#endif
#if IS_ENABLED(CONFIG_SND_SOC_SOF_APOLLOLAKE)
{
diff --git a/sound/soc/codecs/pcm3168a-i2c.c b/sound/soc/codecs/pcm3168a-i2c.c
index 7052cc0c97d1..4da608ba514d 100644
--- a/sound/soc/codecs/pcm3168a-i2c.c
+++ b/sound/soc/codecs/pcm3168a-i2c.c
@@ -10,6 +10,7 @@
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/module.h>
+#include <linux/mod_devicetable.h>
#include <sound/soc.h>
@@ -37,6 +38,13 @@ static const struct i2c_device_id pcm3168a_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, pcm3168a_i2c_id);
+static const struct acpi_device_id pcm3168a_acpi_match[] = {
+ { "PCM3168A" },
+ { "104C3168" },
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, pcm3168a_acpi_match);
+
static const struct of_device_id pcm3168a_of_match[] = {
{ .compatible = "ti,pcm3168a", },
{ }
@@ -49,6 +57,7 @@ static struct i2c_driver pcm3168a_i2c_driver = {
.id_table = pcm3168a_i2c_id,
.driver = {
.name = "pcm3168a",
+ .acpi_match_table = pcm3168a_acpi_match,
.of_match_table = pcm3168a_of_match,
.pm = &pcm3168a_pm_ops,
},
diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c
index fac0617ab95b..df6836a652ef 100644
--- a/sound/soc/codecs/pcm3168a.c
+++ b/sound/soc/codecs/pcm3168a.c
@@ -493,9 +493,9 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
}
break;
case 24:
- if (provider_mode || (format == SND_SOC_DAIFMT_DSP_A) ||
- (format == SND_SOC_DAIFMT_DSP_B)) {
- dev_err(component->dev, "24-bit slots not supported in provider mode, or consumer mode using DSP\n");
+ if (!provider_mode && ((format == SND_SOC_DAIFMT_DSP_A) ||
+ (format == SND_SOC_DAIFMT_DSP_B))) {
+ dev_err(component->dev, "24-bit slots not supported in consumer mode using DSP\n");
return -EINVAL;
}
break;
@@ -743,7 +743,7 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap)
return dev_err_probe(dev, PTR_ERR(pcm3168a->gpio_rst),
"failed to acquire RST gpio\n");
- pcm3168a->scki = devm_clk_get(dev, "scki");
+ pcm3168a->scki = devm_clk_get_optional(dev, "scki");
if (IS_ERR(pcm3168a->scki))
return dev_err_probe(dev, PTR_ERR(pcm3168a->scki),
"failed to acquire clock 'scki'\n");
@@ -755,6 +755,9 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap)
}
pcm3168a->sysclk = clk_get_rate(pcm3168a->scki);
+ /* Fallback to the default if no clk entry available. */
+ if (!pcm3168a->sysclk)
+ pcm3168a->sysclk = 24576000;
for (i = 0; i < ARRAY_SIZE(pcm3168a->supplies); i++)
pcm3168a->supplies[i].supply = pcm3168a_supply_names[i];
diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h
index eca6ec0428bb..585543f872fc 100644
--- a/sound/soc/intel/avs/avs.h
+++ b/sound/soc/intel/avs/avs.h
@@ -51,6 +51,7 @@ struct avs_dsp_ops {
int (* const load_basefw)(struct avs_dev *, struct firmware *);
int (* const load_lib)(struct avs_dev *, struct firmware *, u32);
int (* const transfer_mods)(struct avs_dev *, bool, struct avs_module_entry *, u32);
+ int (* const config_basefw)(struct avs_dev *);
int (* const enable_logs)(struct avs_dev *, enum avs_log_enable, u32, u32, unsigned long,
u32 *);
int (* const log_buffer_offset)(struct avs_dev *, u32);
diff --git a/sound/soc/intel/avs/board_selection.c b/sound/soc/intel/avs/board_selection.c
index 0266edeafc19..2d706edcbf92 100644
--- a/sound/soc/intel/avs/board_selection.c
+++ b/sound/soc/intel/avs/board_selection.c
@@ -312,6 +312,18 @@ static struct snd_soc_acpi_mach avs_tgl_i2s_machines[] = {
{},
};
+static struct snd_soc_acpi_mach avs_mbl_i2s_machines[] = {
+ {
+ .id = "PCM3168A",
+ .drv_name = "avs_pcm3168a",
+ .mach_params = {
+ .i2s_link_mask = AVS_SSP(0) | AVS_SSP(2),
+ },
+ .tplg_filename = "pcm3168a-tplg.bin",
+ },
+ {}
+};
+
static struct snd_soc_acpi_mach avs_test_i2s_machines[] = {
{
.drv_name = "avs_i2s_test",
@@ -378,10 +390,11 @@ static const struct avs_acpi_boards i2s_boards[] = {
AVS_MACH_ENTRY(HDA_ICL_LP, avs_icl_i2s_machines),
AVS_MACH_ENTRY(HDA_TGL_LP, avs_tgl_i2s_machines),
AVS_MACH_ENTRY(HDA_EHL_0, avs_tgl_i2s_machines),
+ AVS_MACH_ENTRY(HDA_ADL_N, avs_mbl_i2s_machines),
AVS_MACH_ENTRY(HDA_ADL_P, avs_tgl_i2s_machines),
AVS_MACH_ENTRY(HDA_RPL_P_0, avs_tgl_i2s_machines),
- AVS_MACH_ENTRY(HDA_RPL_M, avs_tgl_i2s_machines),
- {},
+ AVS_MACH_ENTRY(HDA_RPL_M, avs_mbl_i2s_machines),
+ {}
};
static const struct avs_acpi_boards *avs_get_i2s_boards(struct avs_dev *adev)
diff --git a/sound/soc/intel/avs/boards/Kconfig b/sound/soc/intel/avs/boards/Kconfig
index 00b0f6c176d6..ba4bee42124c 100644
--- a/sound/soc/intel/avs/boards/Kconfig
+++ b/sound/soc/intel/avs/boards/Kconfig
@@ -87,6 +87,16 @@ config SND_SOC_INTEL_AVS_MACH_NAU8825
Say Y or m if you have such a device. This is a recommended option.
If unsure select "N".
+config SND_SOC_INTEL_AVS_MACH_PCM3168A
+ tristate "pcm3168a I2S board"
+ depends on I2C
+ depends on MFD_INTEL_LPSS || COMPILE_TEST
+ select SND_SOC_PCM3168A_I2C
+ help
+ This adds support for AVS with PCM3168A I2C codec configuration.
+ Say Y or m if you have such a device. This is a recommended option.
+ If unsure select "N".
+
config SND_SOC_INTEL_AVS_MACH_PROBE
tristate "Probing (data) board"
depends on DEBUG_FS
diff --git a/sound/soc/intel/avs/boards/Makefile b/sound/soc/intel/avs/boards/Makefile
index 4fbd936ffb3e..a95256b94dc8 100644
--- a/sound/soc/intel/avs/boards/Makefile
+++ b/sound/soc/intel/avs/boards/Makefile
@@ -9,6 +9,7 @@ snd-soc-avs-max98927-y := max98927.o
snd-soc-avs-max98357a-y := max98357a.o
snd-soc-avs-max98373-y := max98373.o
snd-soc-avs-nau8825-y := nau8825.o
+snd-soc-avs-pcm3168a-y := pcm3168a.o
snd-soc-avs-probe-y := probe.o
snd-soc-avs-rt274-y := rt274.o
snd-soc-avs-rt286-y := rt286.o
@@ -27,6 +28,7 @@ obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_MAX98927) += snd-soc-avs-max98927.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_MAX98357A) += snd-soc-avs-max98357a.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_MAX98373) += snd-soc-avs-max98373.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_NAU8825) += snd-soc-avs-nau8825.o
+obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_PCM3168A) += snd-soc-avs-pcm3168a.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_PROBE) += snd-soc-avs-probe.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT274) += snd-soc-avs-rt274.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT286) += snd-soc-avs-rt286.o
diff --git a/sound/soc/intel/avs/boards/pcm3168a.c b/sound/soc/intel/avs/boards/pcm3168a.c
new file mode 100644
index 000000000000..5d0e7a5bdc74
--- /dev/null
+++ b/sound/soc/intel/avs/boards/pcm3168a.c
@@ -0,0 +1,143 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2024-2025 Intel Corporation
+//
+// Author: Cezary Rojewski <cezary.rojewski@intel.com>
+//
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+static const struct snd_soc_dapm_widget card_widgets[] = {
+ SND_SOC_DAPM_HP("CPB Stereo HP 1", NULL),
+ SND_SOC_DAPM_HP("CPB Stereo HP 2", NULL),
+ SND_SOC_DAPM_HP("CPB Stereo HP 3", NULL),
+ SND_SOC_DAPM_LINE("CPB Line Out", NULL),
+ SND_SOC_DAPM_MIC("CPB Stereo Mic 1", NULL),
+ SND_SOC_DAPM_MIC("CPB Stereo Mic 2", NULL),
+ SND_SOC_DAPM_LINE("CPB Line In", NULL),
+};
+
+static const struct snd_soc_dapm_route card_routes[] = {
+ { "CPB Stereo HP 1", NULL, "AOUT1L" },
+ { "CPB Stereo HP 1", NULL, "AOUT1R" },
+ { "CPB Stereo HP 2", NULL, "AOUT2L" },
+ { "CPB Stereo HP 2", NULL, "AOUT2R" },
+ { "CPB Stereo HP 3", NULL, "AOUT3L" },
+ { "CPB Stereo HP 3", NULL, "AOUT3R" },
+ { "CPB Line Out", NULL, "AOUT4L" },
+ { "CPB Line Out", NULL, "AOUT4R" },
+
+ { "AIN1L", NULL, "CPB Stereo Mic 1" },
+ { "AIN1R", NULL, "CPB Stereo Mic 1" },
+ { "AIN2L", NULL, "CPB Stereo Mic 2" },
+ { "AIN2R", NULL, "CPB Stereo Mic 2" },
+ { "AIN3L", NULL, "CPB Line In" },
+ { "AIN3R", NULL, "CPB Line In" },
+};
+
+static int avs_pcm3168a_be_fixup(struct snd_soc_pcm_runtime *runtime,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+
+ /* Set SSP to 24 bit. */
+ snd_mask_none(fmt);
+ snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S24_LE);
+
+ return 0;
+}
+
+SND_SOC_DAILINK_DEF(pcm3168a_dac,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-PCM3168A:00", "pcm3168a-dac")));
+SND_SOC_DAILINK_DEF(pcm3168a_adc,
+ DAILINK_COMP_ARRAY(COMP_CODEC("i2c-PCM3168A:00", "pcm3168a-adc")));
+SND_SOC_DAILINK_DEF(cpu_ssp0, DAILINK_COMP_ARRAY(COMP_CPU("SSP0 Pin")));
+SND_SOC_DAILINK_DEF(cpu_ssp2, DAILINK_COMP_ARRAY(COMP_CPU("SSP2 Pin")));
+
+static int avs_create_dai_links(struct device *dev, struct snd_soc_dai_link **links, int *num_links)
+{
+ struct snd_soc_dai_link_component *platform;
+ struct snd_soc_dai_link *dl;
+ const int num_dl = 2;
+
+ dl = devm_kcalloc(dev, num_dl, sizeof(*dl), GFP_KERNEL);
+ platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
+ if (!dl || !platform)
+ return -ENOMEM;
+
+ platform->name = dev_name(dev);
+ dl[0].num_cpus = 1;
+ dl[0].num_codecs = 1;
+ dl[0].platforms = platform;
+ dl[0].num_platforms = 1;
+ dl[0].dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBP_CFP;
+ dl[0].be_hw_params_fixup = avs_pcm3168a_be_fixup;
+ dl[0].nonatomic = 1;
+ dl[0].no_pcm = 1;
+ memcpy(&dl[1], &dl[0], sizeof(*dl));
+
+ dl[0].name = "SSP0-Codec-dac";
+ dl[0].cpus = cpu_ssp0;
+ dl[0].codecs = pcm3168a_dac;
+ dl[1].name = "SSP2-Codec-adc";
+ dl[1].cpus = cpu_ssp2;
+ dl[1].codecs = pcm3168a_adc;
+
+ *links = dl;
+ *num_links = num_dl;
+ return 0;
+}
+
+static int avs_pcm3168a_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct snd_soc_card *card;
+ int ret;
+
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!card)
+ return -ENOMEM;
+
+ ret = avs_create_dai_links(dev, &card->dai_link, &card->num_links);
+ if (ret)
+ return ret;
+
+ card->name = "avs_pcm3168a";
+ card->dev = dev;
+ card->owner = THIS_MODULE;
+ card->dapm_widgets = card_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
+ card->dapm_routes = card_routes;
+ card->num_dapm_routes = ARRAY_SIZE(card_routes);
+ card->fully_routed = true;
+
+ return devm_snd_soc_register_card(dev, card);
+}
+
+static const struct platform_device_id avs_pcm3168a_driver_ids[] = {
+ {
+ .name = "avs_pcm3168a",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_pcm3168a_driver_ids);
+
+static struct platform_driver avs_pcm3168a_driver = {
+ .probe = avs_pcm3168a_probe,
+ .driver = {
+ .name = "avs_pcm3168a",
+ .pm = &snd_soc_pm_ops,
+ },
+ .id_table = avs_pcm3168a_driver_ids,
+};
+
+module_platform_driver(avs_pcm3168a_driver);
+
+MODULE_DESCRIPTION("Intel pcm3168a machine driver");
+MODULE_AUTHOR("Cezary Rojewski <cezary.rojewski@intel.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/avs/loader.c b/sound/soc/intel/avs/loader.c
index 9ff7818395cd..0b29941feb0e 100644
--- a/sound/soc/intel/avs/loader.c
+++ b/sound/soc/intel/avs/loader.c
@@ -603,7 +603,7 @@ release_fw:
return ret;
}
-int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge)
+static int avs_load_firmware(struct avs_dev *adev, bool purge)
{
struct avs_soc_component *acomp;
int ret, i;
@@ -657,28 +657,33 @@ reenable_gating:
return 0;
}
-int avs_dsp_first_boot_firmware(struct avs_dev *adev)
+static int avs_config_basefw(struct avs_dev *adev)
{
- int ret, i;
+ int ret;
- if (avs_platattr_test(adev, CLDMA)) {
- ret = hda_cldma_init(&code_loader, &adev->base.core,
- adev->dsp_ba, AVS_CL_DEFAULT_BUFFER_SIZE);
- if (ret < 0) {
- dev_err(adev->dev, "cldma init failed: %d\n", ret);
+ if (adev->spec->dsp_ops->config_basefw) {
+ ret = avs_dsp_op(adev, config_basefw);
+ if (ret)
return ret;
- }
}
- ret = avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK);
- if (ret < 0)
- return ret;
+ return 0;
+}
- ret = avs_dsp_boot_firmware(adev, true);
- if (ret < 0) {
- dev_err(adev->dev, "firmware boot failed: %d\n", ret);
+int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge)
+{
+ int ret;
+
+ ret = avs_load_firmware(adev, purge);
+ if (ret)
return ret;
- }
+
+ return avs_config_basefw(adev);
+}
+
+static int avs_dsp_alloc_resources(struct avs_dev *adev)
+{
+ int ret, i;
ret = avs_ipc_get_hw_config(adev, &adev->hw_cfg);
if (ret)
@@ -705,6 +710,31 @@ int avs_dsp_first_boot_firmware(struct avs_dev *adev)
strscpy(adev->lib_names[0], "BASEFW", AVS_LIB_NAME_SIZE);
ida_init(&adev->ppl_ida);
-
return 0;
}
+
+int avs_dsp_first_boot_firmware(struct avs_dev *adev)
+{
+ int ret;
+
+ if (avs_platattr_test(adev, CLDMA)) {
+ ret = hda_cldma_init(&code_loader, &adev->base.core,
+ adev->dsp_ba, AVS_CL_DEFAULT_BUFFER_SIZE);
+ if (ret < 0) {
+ dev_err(adev->dev, "cldma init failed: %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = avs_dsp_core_disable(adev, AVS_MAIN_CORE_MASK);
+ if (ret < 0)
+ return ret;
+
+ ret = avs_dsp_boot_firmware(adev, true);
+ if (ret < 0) {
+ dev_err(adev->dev, "firmware boot failed: %d\n", ret);
+ return ret;
+ }
+
+ return avs_dsp_alloc_resources(adev);
+}
diff --git a/sound/soc/intel/avs/messages.c b/sound/soc/intel/avs/messages.c
index 30b666f8909b..242a175381c2 100644
--- a/sound/soc/intel/avs/messages.c
+++ b/sound/soc/intel/avs/messages.c
@@ -510,6 +510,44 @@ err:
return ret;
}
+int avs_ipc_set_fw_config(struct avs_dev *adev, size_t num_tlvs, ...)
+{
+ struct avs_tlv *tlv;
+ void *payload;
+ size_t offset;
+ va_list args;
+ int ret, i;
+
+ payload = kzalloc(AVS_MAILBOX_SIZE, GFP_KERNEL);
+ if (!payload)
+ return -ENOMEM;
+
+ va_start(args, num_tlvs);
+ for (offset = i = 0; i < num_tlvs && offset < AVS_MAILBOX_SIZE - sizeof(*tlv); i++) {
+ tlv = (struct avs_tlv *)(payload + offset);
+ tlv->type = va_arg(args, u32);
+ tlv->length = va_arg(args, u32);
+
+ offset += sizeof(*tlv) + tlv->length;
+ if (offset > AVS_MAILBOX_SIZE)
+ break;
+
+ memcpy(tlv->value, va_arg(args, u8*), tlv->length);
+ }
+
+ if (i == num_tlvs)
+ ret = avs_ipc_set_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID,
+ AVS_BASEFW_FIRMWARE_CONFIG, payload, offset);
+ else
+ ret = -ERANGE;
+
+ va_end(args);
+ kfree(payload);
+ if (ret)
+ dev_err(adev->dev, "set fw cfg failed: %d\n", ret);
+ return ret;
+}
+
int avs_ipc_get_hw_config(struct avs_dev *adev, struct avs_hw_cfg *cfg)
{
struct avs_tlv *tlv;
diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h
index 0378633c7f96..f44fcfc81de7 100644
--- a/sound/soc/intel/avs/messages.h
+++ b/sound/soc/intel/avs/messages.h
@@ -451,6 +451,8 @@ enum avs_fw_cfg_params {
AVS_FW_CFG_RESERVED,
AVS_FW_CFG_POWER_GATING_POLICY,
AVS_FW_CFG_ASSERT_MODE,
+ AVS_FW_CFG_RESERVED2,
+ AVS_FW_CFG_BUS_HARDWARE_ID,
};
struct avs_fw_cfg {
@@ -475,7 +477,14 @@ struct avs_fw_cfg {
u32 power_gating_policy;
};
+struct avs_bus_hwid {
+ u32 device;
+ u32 subsystem;
+ u8 revision;
+};
+
int avs_ipc_get_fw_config(struct avs_dev *adev, struct avs_fw_cfg *cfg);
+int avs_ipc_set_fw_config(struct avs_dev *adev, size_t num_tlvs, ...);
enum avs_hw_cfg_params {
AVS_HW_CFG_AVS_VER,
@@ -643,6 +652,9 @@ int avs_ipc_set_system_time(struct avs_dev *adev);
#define AVS_INTELWOV_MOD_UUID \
GUID_INIT(0xEC774FA9, 0x28D3, 0x424A, 0x90, 0xE4, 0x69, 0xF9, 0x84, 0xF1, 0xEE, 0xB7)
+#define AVS_WOVHOSTM_MOD_UUID \
+ GUID_INIT(0xF9ED62B7, 0x092E, 0x4A90, 0x8F, 0x4D, 0x82, 0xDA, 0xA8, 0xB3, 0x8F, 0x3B)
+
/* channel map */
enum avs_channel_index {
AVS_CHANNEL_LEFT = 0,
@@ -872,6 +884,16 @@ struct avs_wov_cfg {
} __packed;
static_assert(sizeof(struct avs_wov_cfg) == 44);
+struct avs_whm_cfg {
+ struct avs_modcfg_base base;
+ /* Audio format for output pin 0 */
+ struct avs_audio_format ref_fmt;
+ struct avs_audio_format out_fmt;
+ u32 wake_tick_period;
+ struct avs_copier_gtw_cfg gtw_cfg;
+} __packed;
+static_assert(sizeof(struct avs_whm_cfg) == 108);
+
/* Module runtime parameters */
enum avs_copier_runtime_param {
diff --git a/sound/soc/intel/avs/path.c b/sound/soc/intel/avs/path.c
index f31d5e2caa7b..dfb85bd2b665 100644
--- a/sound/soc/intel/avs/path.c
+++ b/sound/soc/intel/avs/path.c
@@ -115,150 +115,188 @@ avs_path_find_variant(struct avs_dev *adev,
return NULL;
}
-__maybe_unused
-static bool avs_dma_type_is_host(u32 dma_type)
+static void avs_init_node_id(union avs_connector_node_id *node_id,
+ struct avs_tplg_modcfg_ext *te, u32 dma_id)
{
- return dma_type == AVS_DMA_HDA_HOST_OUTPUT ||
- dma_type == AVS_DMA_HDA_HOST_INPUT;
-}
+ node_id->val = 0;
+ node_id->dma_type = te->copier.dma_type;
-__maybe_unused
-static bool avs_dma_type_is_link(u32 dma_type)
-{
- return !avs_dma_type_is_host(dma_type);
-}
+ switch (node_id->dma_type) {
+ case AVS_DMA_DMIC_LINK_INPUT:
+ case AVS_DMA_I2S_LINK_OUTPUT:
+ case AVS_DMA_I2S_LINK_INPUT:
+ /* Gateway's virtual index is statically assigned in the topology. */
+ node_id->vindex = te->copier.vindex.val;
+ break;
-__maybe_unused
-static bool avs_dma_type_is_output(u32 dma_type)
-{
- return dma_type == AVS_DMA_HDA_HOST_OUTPUT ||
- dma_type == AVS_DMA_HDA_LINK_OUTPUT ||
- dma_type == AVS_DMA_I2S_LINK_OUTPUT;
-}
+ case AVS_DMA_HDA_HOST_OUTPUT:
+ case AVS_DMA_HDA_HOST_INPUT:
+ /* Gateway's virtual index is dynamically assigned with DMA ID */
+ node_id->vindex = dma_id;
+ break;
-__maybe_unused
-static bool avs_dma_type_is_input(u32 dma_type)
-{
- return !avs_dma_type_is_output(dma_type);
+ case AVS_DMA_HDA_LINK_OUTPUT:
+ case AVS_DMA_HDA_LINK_INPUT:
+ node_id->vindex = te->copier.vindex.val | dma_id;
+ break;
+
+ default:
+ *node_id = INVALID_NODE_ID;
+ break;
+ }
}
-static int avs_copier_create(struct avs_dev *adev, struct avs_path_module *mod)
-{
- struct avs_tplg_module *t = mod->template;
- struct avs_copier_cfg *cfg;
- struct acpi_nhlt_format_config *ep_blob;
- struct acpi_nhlt_endpoint *ep;
- union avs_connector_node_id node_id = {0};
- size_t cfg_size, data_size;
- void *data = NULL;
- u32 dma_type;
- int ret;
+/* Every BLOB contains at least gateway attributes. */
+static struct acpi_nhlt_config *default_blob = (struct acpi_nhlt_config *)&(u32[2]) {4};
- data_size = sizeof(cfg->gtw_cfg.config);
- dma_type = t->cfg_ext->copier.dma_type;
- node_id.dma_type = dma_type;
+static struct acpi_nhlt_config *
+avs_nhlt_config_or_default(struct avs_dev *adev, struct avs_tplg_module *t)
+{
+ struct acpi_nhlt_format_config *fmtcfg;
+ struct avs_tplg_modcfg_ext *te;
+ struct avs_audio_format *fmt;
+ int link_type, dev_type;
+ int bus_id, dir;
- switch (dma_type) {
- struct avs_audio_format *fmt;
- int direction;
+ te = t->cfg_ext;
+ switch (te->copier.dma_type) {
case AVS_DMA_I2S_LINK_OUTPUT:
- case AVS_DMA_I2S_LINK_INPUT:
- if (avs_dma_type_is_input(dma_type))
- direction = SNDRV_PCM_STREAM_CAPTURE;
- else
- direction = SNDRV_PCM_STREAM_PLAYBACK;
-
- if (t->cfg_ext->copier.blob_fmt)
- fmt = t->cfg_ext->copier.blob_fmt;
- else if (direction == SNDRV_PCM_STREAM_CAPTURE)
- fmt = t->in_fmt;
- else
- fmt = t->cfg_ext->copier.out_fmt;
-
- ep = acpi_nhlt_find_endpoint(ACPI_NHLT_LINKTYPE_SSP,
- ACPI_NHLT_DEVICETYPE_CODEC, direction,
- t->cfg_ext->copier.vindex.i2s.instance);
- ep_blob = acpi_nhlt_endpoint_find_fmtcfg(ep, fmt->num_channels, fmt->sampling_freq,
- fmt->valid_bit_depth, fmt->bit_depth);
- if (!ep_blob) {
- dev_err(adev->dev, "no I2S ep_blob found\n");
- return -ENOENT;
- }
-
- data = ep_blob->config.capabilities;
- data_size = ep_blob->config.capabilities_size;
- /* I2S gateway's vindex is statically assigned in topology */
- node_id.vindex = t->cfg_ext->copier.vindex.val;
+ link_type = ACPI_NHLT_LINKTYPE_SSP;
+ dev_type = ACPI_NHLT_DEVICETYPE_CODEC;
+ bus_id = te->copier.vindex.i2s.instance;
+ dir = SNDRV_PCM_STREAM_PLAYBACK;
+ fmt = te->copier.out_fmt;
+ break;
+ case AVS_DMA_I2S_LINK_INPUT:
+ link_type = ACPI_NHLT_LINKTYPE_SSP;
+ dev_type = ACPI_NHLT_DEVICETYPE_CODEC;
+ bus_id = te->copier.vindex.i2s.instance;
+ dir = SNDRV_PCM_STREAM_CAPTURE;
+ fmt = t->in_fmt;
break;
case AVS_DMA_DMIC_LINK_INPUT:
- direction = SNDRV_PCM_STREAM_CAPTURE;
-
- if (t->cfg_ext->copier.blob_fmt)
- fmt = t->cfg_ext->copier.blob_fmt;
- else
- fmt = t->in_fmt;
-
- ep = acpi_nhlt_find_endpoint(ACPI_NHLT_LINKTYPE_PDM, -1, direction, 0);
- ep_blob = acpi_nhlt_endpoint_find_fmtcfg(ep, fmt->num_channels, fmt->sampling_freq,
- fmt->valid_bit_depth, fmt->bit_depth);
- if (!ep_blob) {
- dev_err(adev->dev, "no DMIC ep_blob found\n");
- return -ENOENT;
- }
-
- data = ep_blob->config.capabilities;
- data_size = ep_blob->config.capabilities_size;
- /* DMIC gateway's vindex is statically assigned in topology */
- node_id.vindex = t->cfg_ext->copier.vindex.val;
-
+ link_type = ACPI_NHLT_LINKTYPE_PDM;
+ dev_type = -1; /* ignored */
+ bus_id = 0;
+ dir = SNDRV_PCM_STREAM_CAPTURE;
+ fmt = t->in_fmt;
break;
- case AVS_DMA_HDA_HOST_OUTPUT:
- case AVS_DMA_HDA_HOST_INPUT:
- /* HOST gateway's vindex is dynamically assigned with DMA id */
- node_id.vindex = mod->owner->owner->dma_id;
- break;
+ default:
+ return default_blob;
+ }
- case AVS_DMA_HDA_LINK_OUTPUT:
- case AVS_DMA_HDA_LINK_INPUT:
- node_id.vindex = t->cfg_ext->copier.vindex.val |
- mod->owner->owner->dma_id;
- break;
+ /* Override format selection if necessary. */
+ if (te->copier.blob_fmt)
+ fmt = te->copier.blob_fmt;
- case INVALID_OBJECT_ID:
- default:
- node_id = INVALID_NODE_ID;
- break;
+ fmtcfg = acpi_nhlt_find_fmtcfg(link_type, dev_type, dir, bus_id,
+ fmt->num_channels, fmt->sampling_freq, fmt->valid_bit_depth,
+ fmt->bit_depth);
+ if (!fmtcfg) {
+ dev_warn(adev->dev, "Endpoint format configuration not found.\n");
+ return ERR_PTR(-ENOENT);
}
- cfg_size = offsetof(struct avs_copier_cfg, gtw_cfg.config) + data_size;
- if (cfg_size > AVS_MAILBOX_SIZE)
- return -EINVAL;
+ if (fmtcfg->config.capabilities_size < default_blob->capabilities_size)
+ return ERR_PTR(-ETOOSMALL);
+ /* The firmware expects the payload to be DWORD-aligned. */
+ if (fmtcfg->config.capabilities_size % sizeof(u32))
+ return ERR_PTR(-EINVAL);
+
+ return &fmtcfg->config;
+}
+
+static int avs_fill_gtw_config(struct avs_dev *adev, struct avs_copier_gtw_cfg *gtw,
+ struct avs_tplg_module *t, size_t *cfg_size)
+{
+ struct acpi_nhlt_config *blob;
+ size_t gtw_size;
+
+ blob = avs_nhlt_config_or_default(adev, t);
+ if (IS_ERR(blob))
+ return PTR_ERR(blob);
+ gtw_size = blob->capabilities_size;
+ if (*cfg_size + gtw_size > AVS_MAILBOX_SIZE)
+ return -E2BIG;
+
+ gtw->config_length = gtw_size / sizeof(u32);
+ memcpy(gtw->config.blob, blob->capabilities, blob->capabilities_size);
+ *cfg_size += gtw_size;
+
+ return 0;
+}
+
+static int avs_copier_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+ struct avs_tplg_module *t = mod->template;
+ struct avs_tplg_modcfg_ext *te;
+ struct avs_copier_cfg *cfg;
+ size_t cfg_size;
+ u32 dma_id;
+ int ret;
+
+ te = t->cfg_ext;
cfg = adev->modcfg_buf;
- memset(cfg, 0, cfg_size);
+ dma_id = mod->owner->owner->dma_id;
+ cfg_size = offsetof(struct avs_copier_cfg, gtw_cfg.config);
+
+ ret = avs_fill_gtw_config(adev, &cfg->gtw_cfg, t, &cfg_size);
+ if (ret)
+ return ret;
+
cfg->base.cpc = t->cfg_base->cpc;
cfg->base.ibs = t->cfg_base->ibs;
cfg->base.obs = t->cfg_base->obs;
cfg->base.is_pages = t->cfg_base->is_pages;
cfg->base.audio_fmt = *t->in_fmt;
- cfg->out_fmt = *t->cfg_ext->copier.out_fmt;
- cfg->feature_mask = t->cfg_ext->copier.feature_mask;
- cfg->gtw_cfg.node_id = node_id;
- cfg->gtw_cfg.dma_buffer_size = t->cfg_ext->copier.dma_buffer_size;
- /* config_length in DWORDs */
- cfg->gtw_cfg.config_length = DIV_ROUND_UP(data_size, 4);
- if (data)
- memcpy(&cfg->gtw_cfg.config.blob, data, data_size);
+ cfg->out_fmt = *te->copier.out_fmt;
+ cfg->feature_mask = te->copier.feature_mask;
+ avs_init_node_id(&cfg->gtw_cfg.node_id, te, dma_id);
+ cfg->gtw_cfg.dma_buffer_size = te->copier.dma_buffer_size;
+ mod->gtw_attrs = cfg->gtw_cfg.config.attrs;
+ ret = avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id, t->core_id,
+ t->domain, cfg, cfg_size, &mod->instance_id);
+ return ret;
+}
+
+static int avs_whm_create(struct avs_dev *adev, struct avs_path_module *mod)
+{
+ struct avs_tplg_module *t = mod->template;
+ struct avs_tplg_modcfg_ext *te;
+ struct avs_whm_cfg *cfg;
+ size_t cfg_size;
+ u32 dma_id;
+ int ret;
+
+ te = t->cfg_ext;
+ cfg = adev->modcfg_buf;
+ dma_id = mod->owner->owner->dma_id;
+ cfg_size = offsetof(struct avs_whm_cfg, gtw_cfg.config);
+
+ ret = avs_fill_gtw_config(adev, &cfg->gtw_cfg, t, &cfg_size);
+ if (ret)
+ return ret;
+
+ cfg->base.cpc = t->cfg_base->cpc;
+ cfg->base.ibs = t->cfg_base->ibs;
+ cfg->base.obs = t->cfg_base->obs;
+ cfg->base.is_pages = t->cfg_base->is_pages;
+ cfg->base.audio_fmt = *t->in_fmt;
+ cfg->ref_fmt = *te->whm.ref_fmt;
+ cfg->out_fmt = *te->whm.out_fmt;
+ cfg->wake_tick_period = te->whm.wake_tick_period;
+ avs_init_node_id(&cfg->gtw_cfg.node_id, te, dma_id);
+ cfg->gtw_cfg.dma_buffer_size = te->whm.dma_buffer_size;
mod->gtw_attrs = cfg->gtw_cfg.config.attrs;
- ret = avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id,
- t->core_id, t->domain, cfg, cfg_size,
- &mod->instance_id);
+ ret = avs_dsp_init_module(adev, mod->module_id, mod->owner->instance_id, t->core_id,
+ t->domain, cfg, cfg_size, &mod->instance_id);
return ret;
}
@@ -533,6 +571,7 @@ static struct avs_module_create avs_module_create[] = {
{ &AVS_ASRC_MOD_UUID, avs_asrc_create },
{ &AVS_INTELWOV_MOD_UUID, avs_wov_create },
{ &AVS_PROBE_MOD_UUID, avs_probe_create },
+ { &AVS_WOVHOSTM_MOD_UUID, avs_whm_create },
};
static int avs_path_module_type_create(struct avs_dev *adev, struct avs_path_module *mod)
diff --git a/sound/soc/intel/avs/tgl.c b/sound/soc/intel/avs/tgl.c
index a9019ff5e3af..56905f2b9eb2 100644
--- a/sound/soc/intel/avs/tgl.c
+++ b/sound/soc/intel/avs/tgl.c
@@ -6,7 +6,11 @@
// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
//
+#include <linux/pci.h>
#include "avs.h"
+#include "messages.h"
+
+#define CPUID_TSC_LEAF 0x15
static int avs_tgl_dsp_core_power(struct avs_dev *adev, u32 core_mask, bool power)
{
@@ -35,6 +39,34 @@ static int avs_tgl_dsp_core_stall(struct avs_dev *adev, u32 core_mask, bool stal
return avs_dsp_core_stall(adev, core_mask, stall);
}
+static int avs_tgl_config_basefw(struct avs_dev *adev)
+{
+ struct pci_dev *pci = adev->base.pci;
+ struct avs_bus_hwid hwid;
+ int ret;
+#ifdef CONFIG_X86
+ unsigned int ecx;
+
+#include <asm/cpuid.h>
+ ecx = cpuid_ecx(CPUID_TSC_LEAF);
+ if (ecx) {
+ ret = avs_ipc_set_fw_config(adev, 1, AVS_FW_CFG_XTAL_FREQ_HZ, sizeof(ecx), &ecx);
+ if (ret)
+ return AVS_IPC_RET(ret);
+ }
+#endif
+
+ hwid.device = pci->device;
+ hwid.subsystem = pci->subsystem_vendor | (pci->subsystem_device << 16);
+ hwid.revision = pci->revision;
+
+ ret = avs_ipc_set_fw_config(adev, 1, AVS_FW_CFG_BUS_HARDWARE_ID, sizeof(hwid), &hwid);
+ if (ret)
+ return AVS_IPC_RET(ret);
+
+ return 0;
+}
+
const struct avs_dsp_ops avs_tgl_dsp_ops = {
.power = avs_tgl_dsp_core_power,
.reset = avs_tgl_dsp_core_reset,
@@ -44,6 +76,7 @@ const struct avs_dsp_ops avs_tgl_dsp_ops = {
.load_basefw = avs_icl_load_basefw,
.load_lib = avs_hda_load_library,
.transfer_mods = avs_hda_transfer_modules,
+ .config_basefw = avs_tgl_config_basefw,
.log_buffer_offset = avs_icl_log_buffer_offset,
.log_buffer_status = avs_apl_log_buffer_status,
.coredump = avs_apl_coredump,
diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c
index d612f20ed989..471b00b9a149 100644
--- a/sound/soc/intel/avs/topology.c
+++ b/sound/soc/intel/avs/topology.c
@@ -815,6 +815,48 @@ static const struct avs_tplg_token_parser modcfg_ext_parsers[] = {
.offset = offsetof(struct avs_tplg_modcfg_ext, generic.num_output_pins),
.parse = avs_parse_short_token,
},
+ {
+ .token = AVS_TKN_MODCFG_WHM_REF_AFMT_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, whm.ref_fmt),
+ .parse = avs_parse_audio_format_ptr,
+ },
+ {
+ .token = AVS_TKN_MODCFG_WHM_OUT_AFMT_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, whm.out_fmt),
+ .parse = avs_parse_audio_format_ptr,
+ },
+ {
+ .token = AVS_TKN_MODCFG_WHM_WAKE_TICK_PERIOD_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, whm.wake_tick_period),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_WHM_VINDEX_U8,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, whm.vindex),
+ .parse = avs_parse_byte_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_WHM_DMA_TYPE_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, whm.dma_type),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_WHM_DMABUFF_SIZE_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, whm.dma_buffer_size),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_MODCFG_WHM_BLOB_AFMT_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_modcfg_ext, whm.blob_fmt),
+ .parse = avs_parse_audio_format_ptr,
+ },
};
static const struct avs_tplg_token_parser pin_format_parsers[] = {
diff --git a/sound/soc/intel/avs/topology.h b/sound/soc/intel/avs/topology.h
index 7892e3797f63..23d5ccd19959 100644
--- a/sound/soc/intel/avs/topology.h
+++ b/sound/soc/intel/avs/topology.h
@@ -74,10 +74,17 @@ struct avs_tplg_modcfg_ext {
union avs_virtual_index vindex;
u32 dma_type;
u32 dma_buffer_size;
- u32 config_length;
- /* config_data part of priv data */
} copier;
struct {
+ struct avs_audio_format *ref_fmt;
+ struct avs_audio_format *out_fmt;
+ u32 wake_tick_period;
+ union avs_virtual_index vindex;
+ u32 dma_type;
+ u32 dma_buffer_size;
+ struct avs_audio_format *blob_fmt; /* optional override */
+ } whm;
+ struct {
u32 out_channel_config;
u32 coefficients_select;
s32 coefficients[AVS_CHANNELS_MAX];