]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
ASoC: Intel: avs: Allow the topology to carry NHLT data
authorCezary Rojewski <cezary.rojewski@intel.com>
Sat, 15 Nov 2025 18:06:26 +0000 (19:06 +0100)
committerMark Brown <broonie@kernel.org>
Sun, 16 Nov 2025 23:59:04 +0000 (23:59 +0000)
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>
include/uapi/sound/intel/avs/tokens.h
sound/soc/intel/avs/topology.c
sound/soc/intel/avs/topology.h

index f3ff6aae09a9ec922e1f37a07b60f835304c4ba1..f7cbbfb00227add1362636c6f17c3cba1f49e3ba 100644 (file)
@@ -21,6 +21,7 @@ enum avs_tplg_token {
        AVS_TKN_MANIFEST_NUM_BINDINGS_U32               = 8,
        AVS_TKN_MANIFEST_NUM_CONDPATH_TMPLS_U32         = 9,
        AVS_TKN_MANIFEST_NUM_INIT_CONFIGS_U32           = 10,
+       AVS_TKN_MANIFEST_NUM_NHLT_CONFIGS_U32           = 11,
 
        /* struct avs_tplg_library */
        AVS_TKN_LIBRARY_ID_U32                          = 101,
@@ -160,6 +161,10 @@ enum avs_tplg_token {
        AVS_TKN_INIT_CONFIG_ID_U32                      = 2401,
        AVS_TKN_INIT_CONFIG_PARAM_U8                    = 2402,
        AVS_TKN_INIT_CONFIG_LENGTH_U32                  = 2403,
+
+       /* struct avs_tplg_nhlt_config */
+       AVS_TKN_NHLT_CONFIG_ID_U32                      = 2501,
+       AVS_TKN_NHLT_CONFIG_SIZE_U32                    = 2502,
 };
 
 #endif
index dfe8cf50538187d6d2aa154f100166593f996015..48fdbaef56ddb1da681f01784508a6e2877f7f00 100644 (file)
@@ -420,6 +420,22 @@ static int parse_link_formatted_string(struct snd_soc_component *comp, void *ele
        return 0;
 }
 
+static int avs_parse_nhlt_config_size(struct snd_soc_component *comp, void *elem, void *object,
+                                     u32 offset)
+{
+       struct snd_soc_tplg_vendor_value_elem *tuple = elem;
+       struct acpi_nhlt_config **blob = (struct acpi_nhlt_config **)((u8 *)object + offset);
+       u32 size;
+
+       size = le32_to_cpu(tuple->value);
+       *blob = devm_kzalloc(comp->card->dev, struct_size(*blob, capabilities, size), GFP_KERNEL);
+       if (!*blob)
+               return -ENOMEM;
+
+       (*blob)->capabilities_size = size;
+       return 0;
+}
+
 static int
 parse_dictionary_header(struct snd_soc_component *comp,
                        struct snd_soc_tplg_vendor_array *tuples,
@@ -1651,12 +1667,14 @@ static const struct avs_tplg_token_parser mod_init_config_parsers[] = {
 
 static int avs_tplg_parse_initial_configs(struct snd_soc_component *comp,
                                           struct snd_soc_tplg_vendor_array *tuples,
-                                          u32 block_size)
+                                          u32 block_size, u32 *offset)
 {
        struct avs_soc_component *acomp = to_avs_soc_component(comp);
        struct avs_tplg *tplg = acomp->tplg;
        int ret, i;
 
+       *offset = 0;
+
        /* Parse tuple section telling how many init configs there are. */
        ret = parse_dictionary_header(comp, tuples, (void **)&tplg->init_configs,
                                      &tplg->num_init_configs,
@@ -1666,6 +1684,7 @@ static int avs_tplg_parse_initial_configs(struct snd_soc_component *comp,
                return ret;
 
        block_size -= le32_to_cpu(tuples->size);
+       *offset += le32_to_cpu(tuples->size);
        /* With header parsed, move on to parsing entries. */
        tuples = avs_tplg_vendor_array_next(tuples);
 
@@ -1681,6 +1700,7 @@ static int avs_tplg_parse_initial_configs(struct snd_soc_component *comp,
                 */
                tmp = avs_tplg_vendor_array_next(tuples);
                esize = le32_to_cpu(tuples->size) + le32_to_cpu(tmp->size);
+               *offset += esize;
 
                ret = parse_dictionary_entries(comp, tuples, esize, config, 1, sizeof(*config),
                                               AVS_TKN_INIT_CONFIG_ID_U32,
@@ -1692,6 +1712,7 @@ static int avs_tplg_parse_initial_configs(struct snd_soc_component *comp,
                /* handle raw data section */
                init_config_data = (void *)tuples + esize;
                esize = config->length;
+               *offset += esize;
 
                config->data = devm_kmemdup(comp->card->dev, init_config_data, esize, GFP_KERNEL);
                if (!config->data)
@@ -1704,6 +1725,70 @@ static int avs_tplg_parse_initial_configs(struct snd_soc_component *comp,
        return 0;
 }
 
+static const struct avs_tplg_token_parser mod_nhlt_config_parsers[] = {
+       {
+               .token = AVS_TKN_NHLT_CONFIG_ID_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_nhlt_config, id),
+               .parse = avs_parse_word_token,
+       },
+       {
+               .token = AVS_TKN_NHLT_CONFIG_SIZE_U32,
+               .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+               .offset = offsetof(struct avs_tplg_nhlt_config, blob),
+               .parse = avs_parse_nhlt_config_size,
+       },
+};
+
+static int avs_tplg_parse_nhlt_configs(struct snd_soc_component *comp,
+                                      struct snd_soc_tplg_vendor_array *tuples,
+                                      u32 block_size)
+{
+       struct avs_soc_component *acomp = to_avs_soc_component(comp);
+       struct avs_tplg *tplg = acomp->tplg;
+       int ret, i;
+
+       /* Parse the header section to know how many entries there are. */
+       ret = parse_dictionary_header(comp, tuples, (void **)&tplg->nhlt_configs,
+                                     &tplg->num_nhlt_configs,
+                                     sizeof(*tplg->nhlt_configs),
+                                     AVS_TKN_MANIFEST_NUM_NHLT_CONFIGS_U32);
+       if (ret)
+               return ret;
+
+       block_size -= le32_to_cpu(tuples->size);
+       /* With the header parsed, move on to parsing entries. */
+       tuples = avs_tplg_vendor_array_next(tuples);
+
+       for (i = 0; i < tplg->num_nhlt_configs && block_size > 0; i++) {
+               struct avs_tplg_nhlt_config *config;
+               u32 esize;
+
+               config = &tplg->nhlt_configs[i];
+               esize = le32_to_cpu(tuples->size);
+
+               ret = parse_dictionary_entries(comp, tuples, esize, config, 1, sizeof(*config),
+                                              AVS_TKN_NHLT_CONFIG_ID_U32,
+                                              mod_nhlt_config_parsers,
+                                              ARRAY_SIZE(mod_nhlt_config_parsers));
+               if (ret)
+                       return ret;
+               /* With tuples parsed, the blob shall be allocated. */
+               if (!config->blob)
+                       return -EINVAL;
+
+               /* Consume the raw data and move to the next entry. */
+               memcpy(config->blob->capabilities, (u8 *)tuples + esize,
+                      config->blob->capabilities_size);
+               esize += config->blob->capabilities_size;
+
+               block_size -= esize;
+               tuples = avs_tplg_vendor_array_at(tuples, esize);
+       }
+
+       return 0;
+}
+
 static int avs_route_load(struct snd_soc_component *comp, int index,
                          struct snd_soc_dapm_route *route)
 {
@@ -2008,11 +2093,26 @@ static int avs_manifest(struct snd_soc_component *comp, int index,
        tuples = avs_tplg_vendor_array_at(tuples, offset);
 
        /* Initial configs dictionary. */
-       ret = avs_tplg_parse_initial_configs(comp, tuples, remaining);
+       ret = avs_tplg_parse_initial_configs(comp, tuples, remaining, &offset);
        if (ret < 0)
                return ret;
 
-       return 0;
+       remaining -= offset;
+       tuples = avs_tplg_vendor_array_at(tuples, offset);
+
+       ret = avs_tplg_vendor_array_lookup(tuples, remaining,
+                                          AVS_TKN_MANIFEST_NUM_NHLT_CONFIGS_U32, &offset);
+       if (ret == -ENOENT)
+               return 0;
+       if (ret) {
+               dev_err(comp->dev, "NHLT config lookup failed: %d\n", ret);
+               return ret;
+       }
+
+       tuples = avs_tplg_vendor_array_at(tuples, offset);
+
+       /* NHLT configs dictionary. */
+       return avs_tplg_parse_nhlt_configs(comp, tuples, remaining);
 }
 
 enum {
index 1e83fccf2ea2190147af87f4e03d7857065c2afb..61d50960ef06e2e6a19f7ff05c94b22d2e00459c 100644 (file)
@@ -37,6 +37,8 @@ struct avs_tplg {
        u32 num_condpath_tmpls;
        struct avs_tplg_init_config *init_configs;
        u32 num_init_configs;
+       struct avs_tplg_nhlt_config *nhlt_configs;
+       u32 num_nhlt_configs;
 
        struct list_head path_tmpl_list;
 };
@@ -175,6 +177,11 @@ struct avs_tplg_init_config {
        void *data;
 };
 
+struct avs_tplg_nhlt_config {
+       u32 id;
+       struct acpi_nhlt_config *blob;
+};
+
 struct avs_tplg_path {
        u32 id;