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

ASoC: Intel: avs: Allow the topology to carry NHLT data

Typically the hardware configuration for I2S and DMIC devices resides
in the Non-HDAudio Link Table (NHLT) that is part of the ACPI tree. As
the NHLTs existing in the field are not always perfect, workaround
mechanisms are provided to patch them.

Currently the avs-driver is utilizing the ->blob_fmt override (see
topology.h and struct avs_tplg_modcfg_ext) when there is a valid entry
within a NHLT to configure the hardware for specific format but its
descriptor (header) is invalid.

A separate case is when there is no correct hardware configuration at
all within the NHLT available in the system. Patching the header won't
help and forcing ad-hoc BIOS updates for dated system is not feasible.
Allowing the topology to carry the data is the solution of choice as
replacing a userspace file that is part of /lib/firmware/intel/ is less
invasive than BIOS update and solves the problem.

Co-developed-by: Amadeusz Sławiński <amade@asmblr.net>
Signed-off-by: Amadeusz Sławiński <amade@asmblr.net>
Signed-off-by: Cezary Rojewski <cezary.rojewski@intel.com>
Link: https://patch.msgid.link/20251115180627.3589520-2-cezary.rojewski@intel.com
Signed-off-by: Mark Brown <broonie@kernel.org>

authored by

Cezary Rojewski and committed by
Mark Brown
dd9896d4 6a23ae0a

+115 -3
+5
include/uapi/sound/intel/avs/tokens.h
··· 21 21 AVS_TKN_MANIFEST_NUM_BINDINGS_U32 = 8, 22 22 AVS_TKN_MANIFEST_NUM_CONDPATH_TMPLS_U32 = 9, 23 23 AVS_TKN_MANIFEST_NUM_INIT_CONFIGS_U32 = 10, 24 + AVS_TKN_MANIFEST_NUM_NHLT_CONFIGS_U32 = 11, 24 25 25 26 /* struct avs_tplg_library */ 26 27 AVS_TKN_LIBRARY_ID_U32 = 101, ··· 161 160 AVS_TKN_INIT_CONFIG_ID_U32 = 2401, 162 161 AVS_TKN_INIT_CONFIG_PARAM_U8 = 2402, 163 162 AVS_TKN_INIT_CONFIG_LENGTH_U32 = 2403, 163 + 164 + /* struct avs_tplg_nhlt_config */ 165 + AVS_TKN_NHLT_CONFIG_ID_U32 = 2501, 166 + AVS_TKN_NHLT_CONFIG_SIZE_U32 = 2502, 164 167 }; 165 168 166 169 #endif
+103 -3
sound/soc/intel/avs/topology.c
··· 420 420 return 0; 421 421 } 422 422 423 + static int avs_parse_nhlt_config_size(struct snd_soc_component *comp, void *elem, void *object, 424 + u32 offset) 425 + { 426 + struct snd_soc_tplg_vendor_value_elem *tuple = elem; 427 + struct acpi_nhlt_config **blob = (struct acpi_nhlt_config **)((u8 *)object + offset); 428 + u32 size; 429 + 430 + size = le32_to_cpu(tuple->value); 431 + *blob = devm_kzalloc(comp->card->dev, struct_size(*blob, capabilities, size), GFP_KERNEL); 432 + if (!*blob) 433 + return -ENOMEM; 434 + 435 + (*blob)->capabilities_size = size; 436 + return 0; 437 + } 438 + 423 439 static int 424 440 parse_dictionary_header(struct snd_soc_component *comp, 425 441 struct snd_soc_tplg_vendor_array *tuples, ··· 1667 1651 1668 1652 static int avs_tplg_parse_initial_configs(struct snd_soc_component *comp, 1669 1653 struct snd_soc_tplg_vendor_array *tuples, 1670 - u32 block_size) 1654 + u32 block_size, u32 *offset) 1671 1655 { 1672 1656 struct avs_soc_component *acomp = to_avs_soc_component(comp); 1673 1657 struct avs_tplg *tplg = acomp->tplg; 1674 1658 int ret, i; 1659 + 1660 + *offset = 0; 1675 1661 1676 1662 /* Parse tuple section telling how many init configs there are. */ 1677 1663 ret = parse_dictionary_header(comp, tuples, (void **)&tplg->init_configs, ··· 1684 1666 return ret; 1685 1667 1686 1668 block_size -= le32_to_cpu(tuples->size); 1669 + *offset += le32_to_cpu(tuples->size); 1687 1670 /* With header parsed, move on to parsing entries. */ 1688 1671 tuples = avs_tplg_vendor_array_next(tuples); 1689 1672 ··· 1700 1681 */ 1701 1682 tmp = avs_tplg_vendor_array_next(tuples); 1702 1683 esize = le32_to_cpu(tuples->size) + le32_to_cpu(tmp->size); 1684 + *offset += esize; 1703 1685 1704 1686 ret = parse_dictionary_entries(comp, tuples, esize, config, 1, sizeof(*config), 1705 1687 AVS_TKN_INIT_CONFIG_ID_U32, ··· 1712 1692 /* handle raw data section */ 1713 1693 init_config_data = (void *)tuples + esize; 1714 1694 esize = config->length; 1695 + *offset += esize; 1715 1696 1716 1697 config->data = devm_kmemdup(comp->card->dev, init_config_data, esize, GFP_KERNEL); 1717 1698 if (!config->data) ··· 1720 1699 1721 1700 tuples = init_config_data + esize; 1722 1701 block_size -= esize; 1702 + } 1703 + 1704 + return 0; 1705 + } 1706 + 1707 + static const struct avs_tplg_token_parser mod_nhlt_config_parsers[] = { 1708 + { 1709 + .token = AVS_TKN_NHLT_CONFIG_ID_U32, 1710 + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, 1711 + .offset = offsetof(struct avs_tplg_nhlt_config, id), 1712 + .parse = avs_parse_word_token, 1713 + }, 1714 + { 1715 + .token = AVS_TKN_NHLT_CONFIG_SIZE_U32, 1716 + .type = SND_SOC_TPLG_TUPLE_TYPE_WORD, 1717 + .offset = offsetof(struct avs_tplg_nhlt_config, blob), 1718 + .parse = avs_parse_nhlt_config_size, 1719 + }, 1720 + }; 1721 + 1722 + static int avs_tplg_parse_nhlt_configs(struct snd_soc_component *comp, 1723 + struct snd_soc_tplg_vendor_array *tuples, 1724 + u32 block_size) 1725 + { 1726 + struct avs_soc_component *acomp = to_avs_soc_component(comp); 1727 + struct avs_tplg *tplg = acomp->tplg; 1728 + int ret, i; 1729 + 1730 + /* Parse the header section to know how many entries there are. */ 1731 + ret = parse_dictionary_header(comp, tuples, (void **)&tplg->nhlt_configs, 1732 + &tplg->num_nhlt_configs, 1733 + sizeof(*tplg->nhlt_configs), 1734 + AVS_TKN_MANIFEST_NUM_NHLT_CONFIGS_U32); 1735 + if (ret) 1736 + return ret; 1737 + 1738 + block_size -= le32_to_cpu(tuples->size); 1739 + /* With the header parsed, move on to parsing entries. */ 1740 + tuples = avs_tplg_vendor_array_next(tuples); 1741 + 1742 + for (i = 0; i < tplg->num_nhlt_configs && block_size > 0; i++) { 1743 + struct avs_tplg_nhlt_config *config; 1744 + u32 esize; 1745 + 1746 + config = &tplg->nhlt_configs[i]; 1747 + esize = le32_to_cpu(tuples->size); 1748 + 1749 + ret = parse_dictionary_entries(comp, tuples, esize, config, 1, sizeof(*config), 1750 + AVS_TKN_NHLT_CONFIG_ID_U32, 1751 + mod_nhlt_config_parsers, 1752 + ARRAY_SIZE(mod_nhlt_config_parsers)); 1753 + if (ret) 1754 + return ret; 1755 + /* With tuples parsed, the blob shall be allocated. */ 1756 + if (!config->blob) 1757 + return -EINVAL; 1758 + 1759 + /* Consume the raw data and move to the next entry. */ 1760 + memcpy(config->blob->capabilities, (u8 *)tuples + esize, 1761 + config->blob->capabilities_size); 1762 + esize += config->blob->capabilities_size; 1763 + 1764 + block_size -= esize; 1765 + tuples = avs_tplg_vendor_array_at(tuples, esize); 1723 1766 } 1724 1767 1725 1768 return 0; ··· 2093 2008 tuples = avs_tplg_vendor_array_at(tuples, offset); 2094 2009 2095 2010 /* Initial configs dictionary. */ 2096 - ret = avs_tplg_parse_initial_configs(comp, tuples, remaining); 2011 + ret = avs_tplg_parse_initial_configs(comp, tuples, remaining, &offset); 2097 2012 if (ret < 0) 2098 2013 return ret; 2099 2014 2100 - return 0; 2015 + remaining -= offset; 2016 + tuples = avs_tplg_vendor_array_at(tuples, offset); 2017 + 2018 + ret = avs_tplg_vendor_array_lookup(tuples, remaining, 2019 + AVS_TKN_MANIFEST_NUM_NHLT_CONFIGS_U32, &offset); 2020 + if (ret == -ENOENT) 2021 + return 0; 2022 + if (ret) { 2023 + dev_err(comp->dev, "NHLT config lookup failed: %d\n", ret); 2024 + return ret; 2025 + } 2026 + 2027 + tuples = avs_tplg_vendor_array_at(tuples, offset); 2028 + 2029 + /* NHLT configs dictionary. */ 2030 + return avs_tplg_parse_nhlt_configs(comp, tuples, remaining); 2101 2031 } 2102 2032 2103 2033 enum {
+7
sound/soc/intel/avs/topology.h
··· 37 37 u32 num_condpath_tmpls; 38 38 struct avs_tplg_init_config *init_configs; 39 39 u32 num_init_configs; 40 + struct avs_tplg_nhlt_config *nhlt_configs; 41 + u32 num_nhlt_configs; 40 42 41 43 struct list_head path_tmpl_list; 42 44 }; ··· 175 173 u8 param; 176 174 size_t length; 177 175 void *data; 176 + }; 177 + 178 + struct avs_tplg_nhlt_config { 179 + u32 id; 180 + struct acpi_nhlt_config *blob; 178 181 }; 179 182 180 183 struct avs_tplg_path {