[alsa-devel] [PATCH v3 0/3] topology: Add external API for building topology data
Currently we can build topology binary data files from topology text files. However it is sometimes necessary for DSP FW vendors to build topology binary data within a toolset and create topology binary data directly from within vendor tools.
This series adds an API to the alsa-lib topology core so that vendor tools can create topology data directly.
Changes since V2:-
o Fixed elem memory leaks on error handling paths o Fixed indentation. o Moved container_of and ARRAY_SIZE to local.h o remove use of unlink() and fixed file permissions.
Changes since V1:-
o Split out refactoring of OBJECT_TYPE to SND_TPLG_TYPE_ o Removed inclusing of type_compat.h from asoc.h
Liam Girdwood (1): core: add convenience macros to local.h
Mengdong Lin (2): topology: Add C templates structure for building topology from C programs topology: A API calls to directly build topology data from templates
include/local.h | 7 ++ include/topology.h | 202 ++++++++++++++++++++++++++++++++ src/topology/ctl.c | 292 ++++++++++++++++++++++++++++++++++++++++++++++ src/topology/dapm.c | 183 ++++++++++++++++++++++++++--- src/topology/elem.c | 17 +++ src/topology/parser.c | 59 +++++++++- src/topology/tplg_local.h | 26 ++--- 7 files changed, 749 insertions(+), 37 deletions(-)
Move ARRAY_SIZE() from tplg_local.h to local.h and add container_of() macro to local.h. Both macros are generic but are initially used by topology.
Signed-off-by: Liam Girdwood liam.r.girdwood@linux.intel.com --- include/local.h | 7 +++++++ src/topology/tplg_local.h | 1 - 2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/include/local.h b/include/local.h index 6600816..b429f5d 100644 --- a/include/local.h +++ b/include/local.h @@ -350,4 +350,11 @@ int snd_config_search_alias_hooks(snd_config_t *config,
int _snd_conf_generic_id(const char *id);
+/* convenience macros */ +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) + +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + #endif diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h index febc177..3982cc7 100644 --- a/src/topology/tplg_local.h +++ b/src/topology/tplg_local.h @@ -32,7 +32,6 @@ #define MAX_FILE 256 #define TPLG_MAX_PRIV_SIZE (1024 * 128) #define ALSA_TPLG_DIR ALSA_CONFIG_DIR "/topology" -#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
/** The name of the environment variable containing the tplg directory */ #define ALSA_CONFIG_TPLG_VAR "ALSA_CONFIG_TPLG"
From: Mengdong Lin mengdong.lin@intel.com
Define structures that can be used by applications to directly build topology data instead of using text files. The application will build up the topology data by populating the template structures for each object type and then registering the template with the topology core.
Signed-off-by: Mengdong Lin mengdong.lin@intel.com Signed-off-by: Liam Girdwood liam.r.girdwood@linux.intel.com --- include/topology.h | 176 ++++++++++++++++++++++++++++++++++++++++++++++ src/topology/tplg_local.h | 18 ----- 2 files changed, 176 insertions(+), 18 deletions(-)
diff --git a/include/topology.h b/include/topology.h index 0cb2d79..aee43de 100644 --- a/include/topology.h +++ b/include/topology.h @@ -456,9 +456,30 @@ extern "C" { * */
+/** Maximum number of channels supported in one control */ +#define SND_TPLG_MAX_CHAN 8 + /** Topology context */ typedef struct snd_tplg snd_tplg_t;
+/** Topology object types */ +enum snd_tplg_type { + SND_TPLG_TYPE_TLV = 0, /*!< TLV Data */ + SND_TPLG_TYPE_MIXER, /*!< Mixer control*/ + SND_TPLG_TYPE_ENUM, /*!< Enumerated control */ + SND_TPLG_TYPE_TEXT, /*!< Text data */ + SND_TPLG_TYPE_DATA, /*!< Private data */ + SND_TPLG_TYPE_BYTES, /*!< Byte control */ + SND_TPLG_TYPE_STREAM_CONFIG, /*!< PCM Stream configuration */ + SND_TPLG_TYPE_STREAM_CAPS, /*!< PCM Stream capabilities */ + SND_TPLG_TYPE_PCM, /*!< PCM stream device */ + SND_TPLG_TYPE_DAPM_WIDGET, /*!< DAPM widget */ + SND_TPLG_TYPE_DAPM_GRAPH, /*!< DAPM graph elements */ + SND_TPLG_TYPE_BE, /*!< BE DAI link */ + SND_TPLG_TYPE_CC, /*!< Hostless codec <-> codec link */ + SND_TPLG_TYPE_MANIFEST, /*!< Topology manifest */ +}; + /** * \brief Create a new topology parser instance. * \return New topology parser instance @@ -488,6 +509,161 @@ int snd_tplg_build_file(snd_tplg_t *tplg, const char *infile, */ void snd_tplg_verbose(snd_tplg_t *tplg, int verbose);
+/** \struct snd_tplg_tlv_template + * \brief Template type for all TLV objects. + */ +struct snd_tplg_tlv_template { + int type; /*!< TLV type SNDRV_CTL_TLVT_ */ +}; + +/** \struct snd_tplg_tlv_dbscale_template + * \brief Template type for TLV Scale objects. + */ +struct snd_tplg_tlv_dbscale_template { + struct snd_tplg_tlv_template hdr; /*!< TLV type header */ + int min; /*!< dB minimum value in 0.1dB */ + int step; /*!< dB step size in 0.1dB */ + int mute; /*!< is min dB value mute ? */ +}; + +/** \struct snd_tplg_channel_template + * \brief Template type for single channel mapping. + */ +struct snd_tplg_channel_elem { + int size; /*!< size in bytes of this structure */ + int reg; /*!< channel control register */ + int shift; /*!< channel shift for control bits */ + int id; /*!< ID maps to Left, Right, LFE etc */ +}; + +/** \struct snd_tplg_channel_map_template + * \brief Template type for channel mapping. + */ +struct snd_tplg_channel_map_template { + int num_channels; /*!< number of channel mappings */ + struct snd_tplg_channel_elem channel[SND_TPLG_MAX_CHAN]; /*!< mapping */ +}; + +/** \struct snd_tplg_pdata_template + * \brief Template type for private data objects. + */ +struct snd_tplg_pdata_template { + unsigned int length; /*!< data length */ + const void *data; /*!< data */ +}; + +/** \struct snd_tplg_io_ops_template + * \brief Template type for object operations mapping. + */ +struct snd_tplg_io_ops_template { + int get; /*!< get callback ID */ + int put; /*!< put callback ID */ + int info; /*!< info callback ID */ +}; + +/** \struct snd_tplg_ctl_template + * \brief Template type for control objects. + */ +struct snd_tplg_ctl_template { + int type; /*!< Control type */ + const char *name; /*!< Control name */ + int access; /*!< Control access */ + struct snd_tplg_io_ops_template ops; /*!< operations */ + struct snd_tplg_tlv_template *tlv; /*!< non NULL means we have TLV data */ +}; + +/** \struct snd_tplg_mixer_template + * \brief Template type for mixer control objects. + */ +struct snd_tplg_mixer_template { + struct snd_tplg_ctl_template hdr; /*!< control type header */ + struct snd_tplg_channel_map_template *map; /*!< channel map */ + int min; /*!< min value for mixer */ + int max; /*!< max value for mixer */ + int platform_max; /*!< max value for platform control */ + int invert; /*!< whether controls bits are inverted */ + struct snd_soc_tplg_private *priv; /*!< control private data */ +}; + +/** \struct snd_tplg_enum_template + * \brief Template type for enumerated control objects. + */ +struct snd_tplg_enum_template { + struct snd_tplg_ctl_template hdr; /*!< control type header */ + struct snd_tplg_channel_map_template *map; /*!< channel map */ + int items; /*!< number of enumerated items in control */ + int mask; /*!< register mask size */ + const char **texts; /*!< control text items */ + const int **values; /*!< control value items */ + struct snd_soc_tplg_private *priv; /*!< control private data */ +}; + +/** \struct snd_tplg_bytes_template + * \brief Template type for TLV Scale objects. + */ +struct snd_tplg_bytes_template { + struct snd_tplg_ctl_template hdr; /*!< control type header */ + int max; /*!< max byte control value */ + int mask; /*!< byte control mask */ + int base; /*!< base register */ + int num_regs; /*!< number of registers */ + struct snd_tplg_io_ops_template ext_ops; /*!< ops mapping */ + struct snd_soc_tplg_private *priv; /*!< control private data */ +}; + +/** \struct snd_tplg_graph_elem + * \brief Template type for single DAPM graph element. + */ +struct snd_tplg_graph_elem { + const char *src; /*!< source widget name */ + const char *ctl; /*!< control name or NULL if no control */ + const char *sink; /*!< sink widget name */ +}; + +/** \struct snd_tplg_graph_template + * \brief Template type for array of DAPM graph elements. + */ +struct snd_tplg_graph_template { + int count; /*!< Number of graph elements */ + struct snd_tplg_graph_elem elem[0]; /*!< graph elements */ +}; + +/** \struct snd_tplg_widget_template + * \brief Template type for DAPM widget objects. + */ +struct snd_tplg_widget_template { + int id; /*!< SND_SOC_DAPM_CTL */ + const char *name; /*!< widget name */ + const char *sname; /*!< stream name (certain widgets only) */ + int reg; /*!< negative reg = no direct dapm */ + int shift; /*!< bits to shift */ + int mask; /*!< non-shifted mask */ + int subseq; /*!< sort within widget type */ + unsigned int invert; /*!< invert the power bit */ + unsigned int ignore_suspend; /*!< kept enabled over suspend */ + unsigned short event_flags; /*!< PM event sequence flags */ + unsigned short event_type; /*!< PM event sequence type */ + struct snd_soc_tplg_private *priv; /*!< widget private data */ + int num_ctls; /*!< Number of controls used by widget */ + struct snd_tplg_ctl_template *ctl[0]; /*!< array of widget controls */ +}; + +/** \struct snd_tplg_obj_template + * \brief Generic Template Object + */ +typedef struct snd_tplg_obj_template { + enum snd_tplg_type type; /*!< template object type */ + int index; /*!< group index for object */ + int version; /*!< optional vendor specific version details */ + int vendor_type; /*!< optional vendor specific type info */ + union { + struct snd_tplg_widget_template *widget; /*!< DAPM widget */ + struct snd_tplg_mixer_template *mixer; /*!< Mixer control */ + struct snd_tplg_bytes_template *bytes_ctl; /*!< Bytes control */ + struct snd_tplg_enum_template *enum_ctl; /*!< Enum control */ + struct snd_tplg_graph_template *graph; /*!< Graph elements */ + }; +} snd_tplg_obj_template_t; /* } */
#ifdef __cplusplus diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h index 3982cc7..ec63045 100644 --- a/src/topology/tplg_local.h +++ b/src/topology/tplg_local.h @@ -39,24 +39,6 @@ struct tplg_ref; struct tplg_elem;
-/** Topology object types */ -enum snd_tplg_type { - SND_TPLG_TYPE_TLV = 0, /*!< TLV Data */ - SND_TPLG_TYPE_MIXER, /*!< Mixer control*/ - SND_TPLG_TYPE_ENUM, /*!< Enumerated control */ - SND_TPLG_TYPE_TEXT, /*!< Text data */ - SND_TPLG_TYPE_DATA, /*!< Private data */ - SND_TPLG_TYPE_BYTES, /*!< Byte control */ - SND_TPLG_TYPE_STREAM_CONFIG, /*!< PCM Stream configuration */ - SND_TPLG_TYPE_STREAM_CAPS, /*!< PCM Stream capabilities */ - SND_TPLG_TYPE_PCM, /*!< PCM stream device */ - SND_TPLG_TYPE_DAPM_WIDGET, /*!< DAPM widget */ - SND_TPLG_TYPE_DAPM_GRAPH, /*!< DAPM graph elements */ - SND_TPLG_TYPE_BE, /*!< BE DAI link */ - SND_TPLG_TYPE_CC, /*!< Hostless codec <-> codec link */ - SND_TPLG_TYPE_MANIFEST, /*!< Topology manifest */ -}; - struct snd_tplg {
/* opaque vendor data */
From: Mengdong Lin mengdong.lin@intel.com
Add some new API calls so that applications can directly build topology data using template structures.
Signed-off-by: Mengdong Lin mengdong.lin@intel.com Signed-off-by: Liam Girdwood liam.r.girdwood@linux.intel.com --- include/topology.h | 26 +++++ src/topology/ctl.c | 292 ++++++++++++++++++++++++++++++++++++++++++++++ src/topology/dapm.c | 183 ++++++++++++++++++++++++++--- src/topology/elem.c | 17 +++ src/topology/parser.c | 59 +++++++++- src/topology/tplg_local.h | 7 ++ 6 files changed, 566 insertions(+), 18 deletions(-)
diff --git a/include/topology.h b/include/topology.h index aee43de..6ff8c5f 100644 --- a/include/topology.h +++ b/include/topology.h @@ -664,6 +664,32 @@ typedef struct snd_tplg_obj_template { struct snd_tplg_graph_template *graph; /*!< Graph elements */ }; } snd_tplg_obj_template_t; + +/** + * \brief Register topology template object. + * \param tplg Topology instance. + * \param t Template object. + * \return Zero on success, otherwise a negative error code + */ +int snd_tplg_add_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t); + +/** + * \brief Build all registered topology data into binary file. + * \param tplg Topology instance. + * \param outfile Binary topology output file. + * \return Zero on success, otherwise a negative error code + */ +int snd_tplg_build(snd_tplg_t *tplg, const char *outfile); + +/** + * \brief Attach private data to topology manifest. + * \param tplg Topology instance. + * \param data Private data. + * \param len Length of data in bytes. + * \return Zero on success, otherwise a negative error code + */ +int snd_tplg_set_manifest_data(snd_tplg_t *tplg, const void *data, int len); + /* } */
#ifdef __cplusplus diff --git a/src/topology/ctl.c b/src/topology/ctl.c index 35f684b..68c4ce5 100644 --- a/src/topology/ctl.c +++ b/src/topology/ctl.c @@ -19,6 +19,8 @@ #include "list.h" #include "tplg_local.h"
+#define ENUM_VAL_SIZE (SNDRV_CTL_ELEM_ID_NAME_MAXLEN >> 2) + /* copy referenced TLV to the mixer control */ static int copy_tlv(struct tplg_elem *elem, struct tplg_elem *ref) { @@ -606,3 +608,293 @@ int tplg_parse_control_mixer(snd_tplg_t *tplg,
return 0; } + +static int init_ctl_hdr(struct snd_soc_tplg_ctl_hdr *hdr, + struct snd_tplg_ctl_template *t) +{ + hdr->size = sizeof(struct snd_soc_tplg_ctl_hdr); + hdr->type = t->type; + + elem_copy_text(hdr->name, t->name, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + + /* clean up access flag */ + if (t->access == 0) + t->access = SNDRV_CTL_ELEM_ACCESS_READWRITE; + t->access &= (SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_VOLATILE | + SNDRV_CTL_ELEM_ACCESS_INACTIVE | + SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND | + SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK); + + hdr->access = t->access; + hdr->ops.get = t->ops.get; + hdr->ops.put = t->ops.put; + hdr->ops.info = t->ops.info; + + /* TLV */ + if (hdr->access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE + && !(hdr->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK)) { + + struct snd_tplg_tlv_template *tlvt = t->tlv; + struct snd_soc_tplg_ctl_tlv *tlv = &hdr->tlv; + struct snd_tplg_tlv_dbscale_template *scalet; + struct snd_soc_tplg_tlv_dbscale *scale; + + if (!tlvt) { + SNDERR("error: missing TLV data\n"); + return -EINVAL; + } + + tlv->size = sizeof(struct snd_soc_tplg_ctl_tlv); + tlv->type = tlvt->type; + + switch (tlvt->type) { + case SNDRV_CTL_TLVT_DB_SCALE: + scalet = container_of(tlvt, + struct snd_tplg_tlv_dbscale_template, hdr); + scale = &tlv->scale; + scale->min = scalet->min; + scale->step = scalet->step; + scale->mute = scalet->mute; + break; + + /* TODO: add support for other TLV types */ + default: + SNDERR("error: unsupported TLV type %d\n", tlv->type); + break; + } + } + + return 0; +} + +int tplg_add_mixer(snd_tplg_t *tplg, struct snd_tplg_mixer_template *mixer, + struct tplg_elem **e) +{ + struct snd_soc_tplg_private *priv = mixer->priv; + struct snd_soc_tplg_mixer_control *mc; + struct tplg_elem *elem; + int ret, i; + + tplg_dbg(" Control Mixer: %s\n", mixer->hdr.name); + + if (mixer->hdr.type != SND_SOC_TPLG_TYPE_MIXER) { + SNDERR("error: invalid mixer type %d\n", mixer->hdr.type); + return -EINVAL; + } + + elem = tplg_elem_new_common(tplg, NULL, mixer->hdr.name, + SND_TPLG_TYPE_MIXER); + if (!elem) + return -ENOMEM; + + /* init new mixer */ + mc = elem->mixer_ctrl; + mc->size = elem->size; + ret = init_ctl_hdr(&mc->hdr, &mixer->hdr); + if (ret < 0) { + tplg_elem_free(elem); + return ret; + } + + mc->min = mixer->min; + mc->max = mixer->max; + mc->platform_max = mixer->platform_max; + mc->invert = mixer->invert; + + /* set channel reg to default state */ + for (i = 0; i < SND_SOC_TPLG_MAX_CHAN; i++) + mc->channel[i].reg = -1; + + if (mixer->map) + mc->num_channels = mixer->map->num_channels; + for (i = 0; i < mc->num_channels; i++) { + struct snd_tplg_channel_elem *channel = &mixer->map->channel[i]; + + mc->channel[i].size = channel->size; + mc->channel[i].reg = channel->reg; + mc->channel[i].shift = channel->shift; + mc->channel[i].id = channel->id; + } + + /* priv data */ + if (priv) { + mc = realloc(mc, elem->size + priv->size); + if (!mc) { + tplg_elem_free(elem); + return -ENOMEM; + } + + elem->mixer_ctrl = mc; + elem->size += priv->size; + mc->priv.size = priv->size; + memcpy(mc->priv.data, priv->data, priv->size); + } + + if (e) + *e = elem; + return 0; +} + +int tplg_add_enum(snd_tplg_t *tplg, struct snd_tplg_enum_template *enum_ctl, + struct tplg_elem **e) +{ + struct snd_soc_tplg_enum_control *ec; + struct tplg_elem *elem; + int ret, i; + + tplg_dbg(" Control Enum: %s\n", enum_ctl->hdr.name); + + if (enum_ctl->hdr.type != SND_SOC_TPLG_TYPE_ENUM) { + SNDERR("error: invalid enum type %d\n", enum_ctl->hdr.type); + return -EINVAL; + } + + elem = tplg_elem_new_common(tplg, NULL, enum_ctl->hdr.name, + SND_TPLG_TYPE_ENUM); + if (!elem) + return -ENOMEM; + + ec = elem->enum_ctrl; + ec->size = elem->size; + ret = init_ctl_hdr(&ec->hdr, &enum_ctl->hdr); + if (ret < 0) { + tplg_elem_free(elem); + return ret; + } + + ec->items = enum_ctl->items; + if (ec->items > SND_SOC_TPLG_NUM_TEXTS) + ec->items = SND_SOC_TPLG_NUM_TEXTS; + + ec->mask = enum_ctl->mask; + ec->count = enum_ctl->items; + + if (enum_ctl->texts != NULL) { + for (i = 0; i < ec->items; i++) { + if (enum_ctl->texts[i] != NULL) + strncpy(ec->texts[i], enum_ctl->texts[i], + SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + } + } + + if (enum_ctl->values != NULL) { + for (i = 0; i < ec->items; i++) { + if (enum_ctl->values[i]) + continue; + + memcpy(&ec->values[i * sizeof(int) * ENUM_VAL_SIZE], + enum_ctl->values[i], + sizeof(int) * ENUM_VAL_SIZE); + } + } + + if (enum_ctl->priv != NULL) { + ec = realloc(ec, + elem->size + enum_ctl->priv->size); + if (!ec) { + tplg_elem_free(elem); + return -ENOMEM; + } + + elem->enum_ctrl = ec; + elem->size += enum_ctl->priv->size; + + memcpy(ec->priv.data, enum_ctl->priv->data, + enum_ctl->priv->size); + + ec->priv.size = enum_ctl->priv->size; + } + + if (e) + *e = elem; + return 0; +} + +int tplg_add_bytes(snd_tplg_t *tplg, struct snd_tplg_bytes_template *bytes_ctl, + struct tplg_elem **e) +{ + struct snd_soc_tplg_bytes_control *be; + struct tplg_elem *elem; + int ret; + + tplg_dbg(" Control Bytes: %s\n", bytes_ctl->hdr.name); + + if (bytes_ctl->hdr.type != SND_SOC_TPLG_TYPE_BYTES) { + SNDERR("error: invalid bytes type %d\n", bytes_ctl->hdr.type); + return -EINVAL; + } + + elem = tplg_elem_new_common(tplg, NULL, bytes_ctl->hdr.name, + SND_TPLG_TYPE_BYTES); + if (!elem) + return -ENOMEM; + + be = elem->bytes_ext; + be->size = elem->size; + ret = init_ctl_hdr(&be->hdr, &bytes_ctl->hdr); + if (ret < 0) { + tplg_elem_free(elem); + return ret; + } + + be->max = bytes_ctl->max; + be->mask = bytes_ctl->mask; + be->base = bytes_ctl->base; + be->num_regs = bytes_ctl->num_regs; + be->ext_ops.put = bytes_ctl->ext_ops.put; + be->ext_ops.get = bytes_ctl->ext_ops.get; + + if (bytes_ctl->priv != NULL) { + be = realloc(be, + elem->size + bytes_ctl->priv->size); + if (!be) { + tplg_elem_free(elem); + return -ENOMEM; + } + elem->bytes_ext = be; + elem->size += bytes_ctl->priv->size; + + memcpy(be->priv.data, bytes_ctl->priv->data, + bytes_ctl->priv->size); + + be->priv.size = bytes_ctl->priv->size; + } + + /* check on TLV bytes control */ + if (be->hdr.access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { + if (be->hdr.access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE + != SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) { + SNDERR("error: Invalid TLV bytes control access 0x%x\n", + be->hdr.access); + tplg_elem_free(elem); + return -EINVAL; + } + + if (!be->max) { + tplg_elem_free(elem); + return -EINVAL; + } + } + + if (e) + *e = elem; + return 0; +} + +int tplg_add_mixer_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) +{ + return tplg_add_mixer(tplg, t->mixer, NULL); +} + +int tplg_add_enum_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) +{ + return tplg_add_enum(tplg, t->enum_ctl, NULL); +} + +int tplg_add_bytes_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) +{ + return tplg_add_bytes(tplg, t->bytes_ctl, NULL); +} diff --git a/src/topology/dapm.c b/src/topology/dapm.c index 3458aa7..14969ee 100644 --- a/src/topology/dapm.c +++ b/src/topology/dapm.c @@ -142,8 +142,6 @@ static int tplg_build_widget(snd_tplg_t *tplg, list_for_each(pos, base) {
ref = list_entry(pos, struct tplg_ref, list); - if (ref->id == NULL || ref->elem) - continue;
switch (ref->type) { case SND_TPLG_TYPE_MIXER: @@ -162,6 +160,14 @@ static int tplg_build_widget(snd_tplg_t *tplg, err = copy_dapm_control(elem, ref->elem); break;
+ case SND_TPLG_TYPE_BYTES: + if (!ref->elem) + ref->elem = tplg_elem_lookup(&tplg->bytes_ext_list, + ref->id, SND_TPLG_TYPE_BYTES); + if (ref->elem) + err = copy_dapm_control(elem, ref->elem); + break; + case SND_TPLG_TYPE_DATA: if (!ref->elem) ref->elem = tplg_elem_lookup(&tplg->pdata_list, @@ -278,6 +284,30 @@ int tplg_build_routes(snd_tplg_t *tplg) return 0; }
+struct tplg_elem* tplg_elem_new_route(snd_tplg_t *tplg) +{ + struct tplg_elem *elem; + struct snd_soc_tplg_dapm_graph_elem *line; + + elem = tplg_elem_new(); + if (!elem) + return NULL; + + list_add_tail(&elem->list, &tplg->route_list); + strcpy(elem->id, "line"); + elem->type = SND_TPLG_TYPE_DAPM_GRAPH; + elem->size = sizeof(*line); + + line = calloc(1, sizeof(*line)); + if (!line) { + tplg_elem_free(elem); + return NULL; + } + elem->route = line; + + return elem; +} + #define LINE_SIZE 1024
/* line is defined as '"source, control, sink"' */ @@ -334,7 +364,7 @@ static int tplg_parse_routes(snd_tplg_t *tplg, snd_config_t *cfg) snd_config_iterator_t i, next; snd_config_t *n; struct tplg_elem *elem; - struct snd_soc_tplg_dapm_graph_elem *line = NULL; + struct snd_soc_tplg_dapm_graph_elem *line; int err;
snd_config_for_each(i, next, cfg) { @@ -344,20 +374,11 @@ static int tplg_parse_routes(snd_tplg_t *tplg, snd_config_t *cfg) if (snd_config_get_string(n, &val) < 0) continue;
- elem = tplg_elem_new(); + elem = tplg_elem_new_route(tplg); if (!elem) return -ENOMEM;
- list_add_tail(&elem->list, &tplg->route_list); - strcpy(elem->id, "line"); - elem->type = SND_TPLG_TYPE_DAPM_GRAPH; - elem->size = sizeof(*line); - - line = calloc(1, sizeof(*line)); - if (!line) - return -ENOMEM; - - elem->route = line; + line = elem->route;
err = tplg_parse_line(val, line); if (err < 0) @@ -558,3 +579,137 @@ int tplg_parse_dapm_widget(snd_tplg_t *tplg,
return 0; } + +int tplg_add_route(snd_tplg_t *tplg, struct snd_tplg_graph_elem *t) +{ + struct tplg_elem *elem; + struct snd_soc_tplg_dapm_graph_elem *line; + + if (!t->src || !t->sink) + return -EINVAL; + + elem = tplg_elem_new_route(tplg); + if (!elem) + return -ENOMEM; + + line = elem->route; + elem_copy_text(line->source, t->src, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + if (t->ctl) + elem_copy_text(line->control, t->ctl, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + elem_copy_text(line->sink, t->sink, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + + return 0; +} + +int tplg_add_graph_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) +{ + struct snd_tplg_graph_template *gt = t->graph; + int i, ret; + + for (i = 0; i < gt->count; i++) { + ret = tplg_add_route(tplg, gt->elem + i); + if (ret < 0) + return ret; + } + + return 0; +} + +int tplg_add_widget_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) +{ + struct snd_tplg_widget_template *wt = t->widget; + struct snd_soc_tplg_dapm_widget *w; + struct tplg_elem *elem; + int i, ret = 0; + + tplg_dbg("Widget: %s\n", wt->name); + + elem = tplg_elem_new_common(tplg, NULL, wt->name, + SND_TPLG_TYPE_DAPM_WIDGET); + if (!elem) + return -ENOMEM; + + /* init new widget */ + w = elem->widget; + w->size = elem->size; + + w->id = wt->id; + elem_copy_text(w->name, wt->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + if (wt->sname) + elem_copy_text(w->sname, wt->sname, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + w->reg = wt->reg; + w->shift = wt->shift; + w->mask = wt->mask; + w->subseq = wt->subseq; + w->invert = wt->invert; + w->ignore_suspend = wt->ignore_suspend; + w->event_flags = wt->event_flags; + w->event_type = wt->event_type; + + if (wt->priv != NULL) { + w = realloc(w, + elem->size + wt->priv->size); + if (!w) { + tplg_elem_free(elem); + return -ENOMEM; + } + + elem->widget = w; + elem->size += wt->priv->size; + + memcpy(w->priv.data, wt->priv->data, + wt->priv->size); + w->priv.size = wt->priv->size; + } + + /* add controls to the widget's reference list */ + for (i = 0 ; i < wt->num_ctls; i++) { + struct snd_tplg_ctl_template *ct = wt->ctl[i]; + struct tplg_elem *elem_ctl; + struct snd_tplg_mixer_template *mt; + struct snd_tplg_bytes_template *bt; + struct snd_tplg_enum_template *et; + + if (!ct) { + tplg_elem_free(elem); + return -EINVAL; + } + + switch (ct->type) { + case SND_SOC_TPLG_TYPE_MIXER: + mt = container_of(ct, struct snd_tplg_mixer_template, hdr); + ret = tplg_add_mixer(tplg, mt, &elem_ctl); + break; + + case SND_SOC_TPLG_TYPE_BYTES: + bt = container_of(ct, struct snd_tplg_bytes_template, hdr); + ret = tplg_add_bytes(tplg, bt, &elem_ctl); + break; + + case SND_SOC_TPLG_TYPE_ENUM: + et = container_of(ct, struct snd_tplg_enum_template, hdr); + ret = tplg_add_enum(tplg, et, &elem_ctl); + break; + + default: + SNDERR("error: widget %s: invalid type %d for ctl %d\n", + wt->name, ct->type, i); + ret = -EINVAL; + break; + } + + if (ret < 0) { + tplg_elem_free(elem); + return ret; + } + + ret = tplg_ref_add_elem(elem, elem_ctl); + if (ret < 0) { + tplg_elem_free(elem); + return ret; + } + } + + return 0; +} diff --git a/src/topology/elem.c b/src/topology/elem.c index daabe75..d784236 100644 --- a/src/topology/elem.c +++ b/src/topology/elem.c @@ -35,6 +35,23 @@ int tplg_ref_add(struct tplg_elem *elem, int type, const char* id) return 0; }
+/* directly add a reference elem */ +int tplg_ref_add_elem(struct tplg_elem *elem, struct tplg_elem *elem_ref) +{ + struct tplg_ref *ref; + + ref = calloc(1, sizeof(*ref)); + if (!ref) + return -ENOMEM; + + ref->type = elem_ref->type; + ref->elem = elem_ref; + elem_copy_text(ref->id, elem_ref->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + + list_add_tail(&ref->list, &elem->ref_list); + return 0; +} + void tplg_ref_free_list(struct list_head *base) { struct list_head *pos, *npos; diff --git a/src/topology/parser.c b/src/topology/parser.c index 3e3e2b3..ca7de06 100644 --- a/src/topology/parser.c +++ b/src/topology/parser.c @@ -266,11 +266,8 @@ int snd_tplg_build_file(snd_tplg_t *tplg, const char *infile, snd_config_t *cfg = NULL; int err = 0;
- /* delete any old output files */ - unlink(outfile); - tplg->out_fd = - open(outfile, O_RDWR | O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO); + open(outfile, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); if (tplg->out_fd < 0) { SNDERR("error: failed to open %s err %d\n", outfile, -errno); @@ -309,6 +306,60 @@ out_close: return err; }
+int snd_tplg_add_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) +{ + switch (t->type) { + case SND_TPLG_TYPE_MIXER: + return tplg_add_mixer_object(tplg, t); + case SND_TPLG_TYPE_ENUM: + return tplg_add_enum_object(tplg, t); + case SND_TPLG_TYPE_BYTES: + return tplg_add_bytes_object(tplg, t); + case SND_TPLG_TYPE_DAPM_WIDGET: + return tplg_add_widget_object(tplg, t); + case SND_TPLG_TYPE_DAPM_GRAPH: + return tplg_add_graph_object(tplg, t); + default: + SNDERR("error: invalid object type %d\n", t->type); + return -EINVAL; + }; +} + +int snd_tplg_build(snd_tplg_t *tplg, const char *outfile) +{ + int err; + + tplg->out_fd = + open(outfile, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); + if (tplg->out_fd < 0) { + SNDERR("error: failed to open %s err %d\n", + outfile, -errno); + return -errno; + } + + err = tplg_build_integ(tplg); + if (err < 0) { + SNDERR("error: failed to check topology integrity\n"); + goto out; + } + + err = tplg_write_data(tplg); + if (err < 0) { + SNDERR("error: failed to write data %d\n", err); + goto out; + } + +out: + close(tplg->out_fd); + return err; +} + +int snd_tplg_set_manifest_data(snd_tplg_t *tplg, const void *data, int len) +{ + tplg->manifest.priv.size = len; + tplg->manifest_pdata = data; +} + void snd_tplg_verbose(snd_tplg_t *tplg, int verbose) { tplg->verbose = verbose; diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h index ec63045..063ff9f 100644 --- a/src/topology/tplg_local.h +++ b/src/topology/tplg_local.h @@ -190,6 +190,7 @@ int tplg_build_pcm_dai(snd_tplg_t *tplg, unsigned int type); int tplg_copy_data(struct tplg_elem *elem, struct tplg_elem *ref);
int tplg_ref_add(struct tplg_elem *elem, int type, const char* id); +int tplg_ref_add_elem(struct tplg_elem *elem, struct tplg_elem *elem_ref);
struct tplg_elem *tplg_elem_new(void); void tplg_elem_free(struct tplg_elem *elem); @@ -214,3 +215,9 @@ int tplg_parse_ops(snd_tplg_t *tplg ATTRIBUTE_UNUSED,
struct tplg_elem *lookup_pcm_dai_stream(struct list_head *base, const char* id); + +int tplg_add_mixer_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t); +int tplg_add_enum_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t); +int tplg_add_bytes_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t); +int tplg_add_widget_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t); +int tplg_add_graph_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t);
On Tue, 11 Aug 2015 19:23:14 +0200, Liam Girdwood wrote:
Currently we can build topology binary data files from topology text files. However it is sometimes necessary for DSP FW vendors to build topology binary data within a toolset and create topology binary data directly from within vendor tools.
This series adds an API to the alsa-lib topology core so that vendor tools can create topology data directly.
Changes since V2:-
o Fixed elem memory leaks on error handling paths o Fixed indentation. o Moved container_of and ARRAY_SIZE to local.h o remove use of unlink() and fixed file permissions.
Changes since V1:-
o Split out refactoring of OBJECT_TYPE to SND_TPLG_TYPE_ o Removed inclusing of type_compat.h from asoc.h
Applied all three patches now. The third patch missed the declaration of tplg_add_mixer() & co, which leaded to compile warnings, so I fixed them manually.
thanks,
Takashi
Liam Girdwood (1): core: add convenience macros to local.h
Mengdong Lin (2): topology: Add C templates structure for building topology from C programs topology: A API calls to directly build topology data from templates
include/local.h | 7 ++ include/topology.h | 202 ++++++++++++++++++++++++++++++++ src/topology/ctl.c | 292 ++++++++++++++++++++++++++++++++++++++++++++++ src/topology/dapm.c | 183 ++++++++++++++++++++++++++--- src/topology/elem.c | 17 +++ src/topology/parser.c | 59 +++++++++- src/topology/tplg_local.h | 26 ++--- 7 files changed, 749 insertions(+), 37 deletions(-)
-- 2.1.4
participants (2)
-
Liam Girdwood
-
Takashi Iwai