[RFC 02/13] ASoC: Intel: avs: Add topology parsing infrastructure
Pierre-Louis Bossart
pierre-louis.bossart at linux.intel.com
Fri Feb 25 18:20:22 CET 2022
On 2/7/22 07:25, Cezary Rojewski wrote:
> AVS topology is split into two major parts: dictionaries - found within
> ASoC topology manifest - and path templates - found within DAPM widget
what is a "path template"? this is the third time I review your patches
and I have yet to find a description of all this.
If you introduce a new concept you really need to explain what problem
you are trying to solve, why it's important and what other alternatives
could be considered. Consider adding a Documentation file to explain
what you are trying to accomplish.
Jumping to optimizations of memory footprint through dictionaries is too
early.
> private data. Dictionaries job is to reduce the total amount of memory
> occupied by topology elements. Rather than having every pipeline and
> module carry its own information, each refers to specific entry in
> specific dictionary by provided (from topology file) indexes. In
> consequence, most struct avs_tplg_xxx are made out of pointers.
> To support the above, range of parsing helpers for all value-types known
> to ALSA: uuid, bool, byte, short, word and string are added. Additional
> handlers help translate pointer-types and more complex objects such as
> audio formats and module base configs.
>
> Signed-off-by: Amadeusz Sławiński <amadeuszx.slawinski at linux.intel.com>
> Signed-off-by: Cezary Rojewski <cezary.rojewski at intel.com>
> ---
> sound/soc/intel/avs/avs.h | 14 +
> sound/soc/intel/avs/topology.c | 595 +++++++++++++++++++++++++++++++++
> sound/soc/intel/avs/topology.h | 44 +++
> 3 files changed, 653 insertions(+)
> create mode 100644 sound/soc/intel/avs/topology.c
> create mode 100644 sound/soc/intel/avs/topology.h
>
> diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h
> index 20987c7744a3..61842720c894 100644
> --- a/sound/soc/intel/avs/avs.h
> +++ b/sound/soc/intel/avs/avs.h
> @@ -13,10 +13,12 @@
> #include <linux/firmware.h>
> #include <sound/hda_codec.h>
> #include <sound/hda_register.h>
> +#include <sound/soc-component.h>
> #include "messages.h"
> #include "registers.h"
>
> struct avs_dev;
> +struct avs_tplg;
>
> struct avs_dsp_ops {
> int (* const power)(struct avs_dev *, u32, bool);
> @@ -223,4 +225,16 @@ int avs_hda_load_library(struct avs_dev *adev, struct firmware *lib, u32 id);
> int avs_hda_transfer_modules(struct avs_dev *adev, bool load,
> struct avs_module_entry *mods, u32 num_mods);
>
> +/* Soc component members */
> +
> +struct avs_soc_component {
> + struct snd_soc_component base;
> + struct avs_tplg *tplg;
> +
> + struct list_head node;
> +};
> +
> +#define to_avs_soc_component(comp) \
> + container_of(comp, struct avs_soc_component, base)
> +
> #endif /* __SOUND_SOC_INTEL_AVS_H */
> diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c
> new file mode 100644
> index 000000000000..4b8b415ca006
> --- /dev/null
> +++ b/sound/soc/intel/avs/topology.c
> @@ -0,0 +1,595 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +//
> +// Copyright(c) 2021 Intel Corporation. All rights reserved.
> +//
> +// Authors: Cezary Rojewski <cezary.rojewski at intel.com>
> +// Amadeusz Slawinski <amadeuszx.slawinski at linux.intel.com>
> +//
> +
> +#include <linux/uuid.h>
> +#include <sound/soc.h>
> +#include <sound/soc-acpi.h>
> +#include <sound/soc-topology.h>
> +#include <uapi/sound/intel/avs/tokens.h>
> +#include "avs.h"
> +#include "topology.h"
> +
> +/* Get pointer to vendor array at the specified offset. */
> +#define avs_tplg_vendor_array_at(array, offset) \
> + ((struct snd_soc_tplg_vendor_array *)((u8 *)array + offset))
> +
> +/* Get pointer to vendor array that is next in line. */
> +#define avs_tplg_vendor_array_next(array) \
> + (avs_tplg_vendor_array_at(array, le32_to_cpu((array)->size)))
> +
> +/*
> + * Scan provided block of tuples for the specified token. If found,
> + * @offset is updated with position at which first matching token is
> + * located.
> + *
> + * Returns 0 on success, -ENOENT if not found and error code otherwise.
> + */
> +static int
> +avs_tplg_vendor_array_lookup(struct snd_soc_tplg_vendor_array *tuples,
> + u32 block_size, u32 token, u32 *offset)
> +{
> + u32 pos = 0;
> +
> + while (block_size > 0) {
> + struct snd_soc_tplg_vendor_value_elem *tuple;
> + u32 tuples_size = le32_to_cpu(tuples->size);
> +
> + if (tuples_size > block_size)
> + return -EINVAL;
> +
> + tuple = tuples->value;
> + if (le32_to_cpu(tuple->token) == token) {
> + *offset = pos;
> + return 0;
> + }
> +
> + block_size -= tuples_size;
> + pos += tuples_size;
> + tuples = avs_tplg_vendor_array_next(tuples);
> + }
> +
> + return -ENOENT;
> +}
> +
> +/*
> + * See avs_tplg_vendor_array_lookup() for description.
> + *
> + * Behaves exactly like its precursor but starts from the next vendor
> + * array in line. Useful when searching for the finish line of an
> + * arbitrary entry in a list of entries where each is composed of
> + * several vendor tuples and a specific token marks the beginning of
> + * a new entry block.
please try and reword such comments for people who didn't take part in
the development.
I really have no idea what this is about.
> + */
> +static int
> +avs_tplg_vendor_array_lookup_next(struct snd_soc_tplg_vendor_array *tuples,
> + u32 block_size, u32 token, u32 *offset)
> +{
> + u32 tuples_size = le32_to_cpu(tuples->size);
> + int ret;
> +
> + if (tuples_size > block_size)
> + return -EINVAL;
> +
> + tuples = avs_tplg_vendor_array_next(tuples);
> + block_size -= tuples_size;
> +
> + ret = avs_tplg_vendor_array_lookup(tuples, block_size, token, offset);
> + if (!ret)
> + *offset += tuples_size;
> + return ret;
> +}
> +
> +/*
> + * Scan provided block of tuples for the specified token which marks
> + * the boarder of an entry block. Behavior is similar to
boarder looks like a typo. Did you mean border? boundary? position?
location?
> + * avs_tplg_vendor_array_lookup() except 0 is also returned if no
> + * matching token has been found. In such case, returned @size is
> + * assigned to @block_size as the entire block belongs to the current
> + * entry.
> + *
> + * Returns 0 on success, error code otherwise.
> + */
> +static int
> +avs_tplg_vendor_entry_size(struct snd_soc_tplg_vendor_array *tuples,
> + u32 block_size, u32 entry_id_token, u32 *size)
> +{
> + int ret;
> +
> + ret = avs_tplg_vendor_array_lookup_next(tuples, block_size, entry_id_token, size);
> + if (ret == -ENOENT) {
> + *size = block_size;
> + ret = 0;
> + }
> +
> + return ret;
> +}
> +
> +/*
> + * Vendor tuple parsing descriptor.
> + *
> + * @token: vendor specific token that identifies tuple
> + * @type: tuple type, one of SND_SOC_TPLG_TUPLE_TYPE_XXX
> + * @offset: offset of a struct's field to initialize
> + * @parse: parsing function, extracts and assigns value to object's field
> + */
> +struct avs_tplg_token_parser {
> + enum avs_tplg_token token;
> + u32 type;
> + u32 offset;
> + int (*parse)(struct snd_soc_component *comp, void *elem, void *object, u32 offset);
> +};
> +
> +static int
> +avs_parse_uuid_token(struct snd_soc_component *comp, void *elem, void *object, u32 offset)
> +{
> + struct snd_soc_tplg_vendor_value_elem *tuple = elem;
> + guid_t *val = (guid_t *)((u8 *)object + offset);
> +
> + guid_copy((guid_t *)val, (const guid_t *)&tuple->value);
> +
> + return 0;
> +}
> +
> +static int
> +avs_parse_bool_token(struct snd_soc_component *comp, void *elem, void *object, u32 offset)
> +{
> + struct snd_soc_tplg_vendor_value_elem *tuple = elem;
> + bool *val = (bool *)((u8 *)object + offset);
> +
> + *val = le32_to_cpu(tuple->value);
> +
> + return 0;
> +}
> +
> +static int
> +avs_parse_byte_token(struct snd_soc_component *comp, void *elem, void *object, u32 offset)
> +{
> + struct snd_soc_tplg_vendor_value_elem *tuple = elem;
> + u8 *val = ((u8 *)object + offset);
> +
> + *val = le32_to_cpu(tuple->value);
> +
> + return 0;
> +}
> +
> +static int
> +avs_parse_short_token(struct snd_soc_component *comp, void *elem, void *object, u32 offset)
> +{
> + struct snd_soc_tplg_vendor_value_elem *tuple = elem;
> + u16 *val = (u16 *)((u8 *)object + offset);
> +
> + *val = le32_to_cpu(tuple->value);
> +
> + return 0;
> +}
> +
> +static int
> +avs_parse_word_token(struct snd_soc_component *comp, void *elem, void *object, u32 offset)
> +{
> + struct snd_soc_tplg_vendor_value_elem *tuple = elem;
> + u32 *val = (u32 *)((u8 *)object + offset);
> +
> + *val = le32_to_cpu(tuple->value);
> +
> + return 0;
> +}
> +
> +static int
> +avs_parse_string_token(struct snd_soc_component *comp, void *elem, void *object, u32 offset)
> +{
> + struct snd_soc_tplg_vendor_string_elem *tuple = elem;
> + char *val = (char *)((u8 *)object + offset);
> +
> + snprintf(val, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s", tuple->string);
> +
> + return 0;
> +}
> +
> +static int avs_parse_uuid_tokens(struct snd_soc_component *comp, void *object,
> + const struct avs_tplg_token_parser *parsers, int count,
> + struct snd_soc_tplg_vendor_array *tuples)
> +{
> + struct snd_soc_tplg_vendor_uuid_elem *tuple;
> + int ret, i, j;
> +
> + /* Parse element by element. */
> + for (i = 0; i < le32_to_cpu(tuples->num_elems); i++) {
> + tuple = &tuples->uuid[i];
> +
> + for (j = 0; j < count; j++) {
> + /* Ignore non-UUID tokens. */
> + if (parsers[j].type != SND_SOC_TPLG_TUPLE_TYPE_UUID ||
> + parsers[j].token != le32_to_cpu(tuple->token))
> + continue;
> +
> + ret = parsers[j].parse(comp, tuple, object, parsers[j].offset);
> + if (ret)
> + return ret;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int avs_parse_string_tokens(struct snd_soc_component *comp, void *object,
> + const struct avs_tplg_token_parser *parsers, int count,
> + struct snd_soc_tplg_vendor_array *tuples)
> +{
> + struct snd_soc_tplg_vendor_string_elem *tuple;
> + int ret, i, j;
> +
> + /* Parse element by element. */
> + for (i = 0; i < le32_to_cpu(tuples->num_elems); i++) {
> + tuple = &tuples->string[i];
> +
> + for (j = 0; j < count; j++) {
> + /* Ignore non-string tokens. */
> + if (parsers[j].type != SND_SOC_TPLG_TUPLE_TYPE_STRING ||
> + parsers[j].token != le32_to_cpu(tuple->token))
> + continue;
> +
> + ret = parsers[j].parse(comp, tuple, object, parsers[j].offset);
> + if (ret)
> + return ret;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int avs_parse_word_tokens(struct snd_soc_component *comp, void *object,
> + const struct avs_tplg_token_parser *parsers, int count,
> + struct snd_soc_tplg_vendor_array *tuples)
> +{
> + struct snd_soc_tplg_vendor_value_elem *tuple;
> + int ret, i, j;
> +
> + /* Parse element by element. */
> + for (i = 0; i < le32_to_cpu(tuples->num_elems); i++) {
> + tuple = &tuples->value[i];
> +
> + for (j = 0; j < count; j++) {
> + /* Ignore non-integer tokens. */
> + if (!(parsers[j].type == SND_SOC_TPLG_TUPLE_TYPE_WORD ||
> + parsers[j].type == SND_SOC_TPLG_TUPLE_TYPE_SHORT ||
> + parsers[j].type == SND_SOC_TPLG_TUPLE_TYPE_BYTE ||
> + parsers[j].type == SND_SOC_TPLG_TUPLE_TYPE_BOOL))
> + continue;
> +
> + if (parsers[j].token != le32_to_cpu(tuple->token))
> + continue;
> +
> + ret = parsers[j].parse(comp, tuple, object, parsers[j].offset);
> + if (ret)
> + return ret;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int avs_parse_tokens(struct snd_soc_component *comp, void *object,
> + const struct avs_tplg_token_parser *parsers, size_t count,
> + struct snd_soc_tplg_vendor_array *tuples, int priv_size)
> +{
> + int array_size, ret;
> +
> + while (priv_size > 0) {
> + array_size = le32_to_cpu(tuples->size);
> +
> + if (array_size <= 0) {
> + dev_err(comp->dev, "invalid array size 0x%x\n", array_size);
> + return -EINVAL;
> + }
> +
> + /* Make sure there is enough data before parsing. */
> + priv_size -= array_size;
> + if (priv_size < 0) {
> + dev_err(comp->dev, "invalid array size 0x%x\n", array_size);
> + return -EINVAL;
> + }
> +
> + switch (le32_to_cpu(tuples->type)) {
> + case SND_SOC_TPLG_TUPLE_TYPE_UUID:
> + ret = avs_parse_uuid_tokens(comp, object, parsers, count, tuples);
> + break;
> + case SND_SOC_TPLG_TUPLE_TYPE_STRING:
> + ret = avs_parse_string_tokens(comp, object, parsers, count, tuples);
> + break;
> + case SND_SOC_TPLG_TUPLE_TYPE_BOOL:
> + case SND_SOC_TPLG_TUPLE_TYPE_BYTE:
> + case SND_SOC_TPLG_TUPLE_TYPE_SHORT:
> + case SND_SOC_TPLG_TUPLE_TYPE_WORD:
> + ret = avs_parse_word_tokens(comp, object, parsers, count, tuples);
avs_parse_bool_token(struct snd_soc_component *comp, void *elem, void
*object, u32 offset)
avs_parse_byte_token(struct snd_soc_component *comp, void *elem, void
*object, u32 offset)
avs_parse_short_token(struct snd_soc_component *comp, void *elem, void
*object, u32 offset)
why did you introduce such helpers, if you only use word_tokens?
> + break;
> + default:
> + dev_err(comp->dev, "unknown token type %d\n", tuples->type);
> + ret = -EINVAL;
> + }
> +
> + if (ret) {
> + dev_err(comp->dev, "parsing %ld tokens of %d type failed: %d\n",
> + count, tuples->type, ret);
> + return ret;
> + }
> +
> + tuples = avs_tplg_vendor_array_next(tuples);
> + }
> +
> + return 0;
> +}
> +static int parse_link_formatted_string(struct snd_soc_component *comp, void *elem,
> + void *object, u32 offset)
> +{
> + struct snd_soc_tplg_vendor_string_elem *tuple = elem;
> + struct snd_soc_acpi_mach *mach = dev_get_platdata(comp->card->dev);
> + char *val = (char *)((u8 *)object + offset);
> +
> + /*
> + * Dynamic naming - string formats, e.g.: ssp%d - supported only for
> + * topologies describing single device e.g.: an I2S codec on SSP0.
> + */
what are you trying to optimize here? the topology will contain the name
in all cases?
> + if (hweight_long(mach->link_mask) != 1)
> + return avs_parse_string_token(comp, elem, object, offset);
> +
> + snprintf(val, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, tuple->string,
> + __ffs(mach->link_mask));
> +
> + return 0;
> +}
> +struct avs_tplg {
> + char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
> + u32 version;
version of what and where does it come from (manifest)?
does this contain an ABI information? if yes, how is it defined?
> + struct snd_soc_component *comp;
> +
> + struct avs_tplg_library *libs;
> + u32 num_libs;
> + struct avs_audio_format *fmts;
> + u32 num_fmts;
> + struct avs_tplg_modcfg_base *modcfgs_base;
> + u32 num_modcfgs_base;
> +};
More information about the Alsa-devel
mailing list