From 81247bb7da726fd4df432c5d1512afe6d08293cc Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Sat, 20 Mar 2021 22:23:15 -0700 Subject: [PATCH 01/33] topology: channel: expose lookup_channel() Expose the lookup_channel() so that it can be reused in topology2.0. Signed-off-by: Ranjani Sridharan --- src/topology/channel.c | 2 +- src/topology/tplg_local.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/topology/channel.c b/src/topology/channel.c index ebdff4696..884f8cce4 100644 --- a/src/topology/channel.c +++ b/src/topology/channel.c @@ -60,7 +60,7 @@ static const struct map_elem channel_map[] = { }; -static int lookup_channel(const char *c) +int lookup_channel(const char *c) { unsigned int i; diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h index 1cb8b694f..50c62cf4b 100644 --- a/src/topology/tplg_local.h +++ b/src/topology/tplg_local.h @@ -458,3 +458,4 @@ int tplg_decode_pcm(snd_tplg_t *tplg, size_t pos, int tplg_decode_dai(snd_tplg_t *tplg, size_t pos, struct snd_soc_tplg_hdr *hdr, void *bin, size_t size); +int lookup_channel(const char *c); From 6b905215ec96fc4f224c89051290a1785cabaa83 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Sat, 20 Mar 2021 22:29:05 -0700 Subject: [PATCH 02/33] topology: pcm: expose functions for parsing hw_config Split the logic for parsing each hw_config parameter into a separate function tplg_set_hw_config_param() and expose it so that they can be reused in topology2.0. Signed-off-by: Ranjani Sridharan --- src/topology/pcm.c | 396 ++++++++++++++++++++------------------ src/topology/tplg_local.h | 1 + 2 files changed, 206 insertions(+), 191 deletions(-) diff --git a/src/topology/pcm.c b/src/topology/pcm.c index a473b59b4..e5c0d8f9b 100644 --- a/src/topology/pcm.c +++ b/src/topology/pcm.c @@ -1401,244 +1401,258 @@ static const char *get_audio_hw_format_name(unsigned int type) return NULL; } -int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg, - void *private ATTRIBUTE_UNUSED) +int tplg_set_hw_config_param(snd_config_t *n, struct snd_soc_tplg_hw_config *hw_cfg) { - - struct snd_soc_tplg_hw_config *hw_cfg; - struct tplg_elem *elem; - snd_config_iterator_t i, next; - snd_config_t *n; const char *id, *val = NULL; int ret, ival; bool provider_legacy; - elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_HW_CONFIG); - if (!elem) - return -ENOMEM; + if (snd_config_get_id(n, &id) < 0) + return 0; - hw_cfg = elem->hw_cfg; - hw_cfg->size = elem->size; + /* skip comments */ + if (strcmp(id, "comment") == 0) + return 0; + if (id[0] == '#') + return 0; - tplg_dbg(" Link HW config: %s", elem->id); + if (strcmp(id, "id") == 0) { + if (parse_unsigned(n, &hw_cfg->id)) + return -EINVAL; + return 0; + } - snd_config_for_each(i, next, cfg) { + if (strcmp(id, "format") == 0 || + strcmp(id, "fmt") == 0) { + if (snd_config_get_string(n, &val) < 0) + return -EINVAL; - n = snd_config_iterator_entry(i); - if (snd_config_get_id(n, &id) < 0) - continue; + ret = get_audio_hw_format(val); + if (ret < 0) + return ret; + hw_cfg->fmt = ret; + return 0; + } - /* skip comments */ - if (strcmp(id, "comment") == 0) - continue; - if (id[0] == '#') - continue; + provider_legacy = false; + if (strcmp(id, "bclk_master") == 0) { + SNDERR("deprecated option %s, please use 'bclk'\n", id); + provider_legacy = true; + } - if (strcmp(id, "id") == 0) { - if (parse_unsigned(n, &hw_cfg->id)) - return -EINVAL; - continue; - } + if (provider_legacy || + strcmp(id, "bclk") == 0) { - if (strcmp(id, "format") == 0 || - strcmp(id, "fmt") == 0) { - if (snd_config_get_string(n, &val) < 0) - return -EINVAL; + if (snd_config_get_string(n, &val) < 0) + return -EINVAL; - ret = get_audio_hw_format(val); - if (ret < 0) - return ret; - hw_cfg->fmt = ret; - continue; - } + if (!strcmp(val, "master")) { + /* For backwards capability, + * "master" == "codec is slave" + */ + SNDERR("deprecated bclk value '%s'", val); + + hw_cfg->bclk_provider = SND_SOC_TPLG_BCLK_CC; + } else if (!strcmp(val, "codec_slave")) { + SNDERR("deprecated bclk value '%s', use 'codec_consumer'", val); - provider_legacy = false; - if (strcmp(id, "bclk_master") == 0) { - SNDERR("deprecated option %s, please use 'bclk'\n", id); - provider_legacy = true; + hw_cfg->bclk_provider = SND_SOC_TPLG_BCLK_CC; + } else if (!strcmp(val, "codec_consumer")) { + hw_cfg->bclk_provider = SND_SOC_TPLG_BCLK_CC; + } else if (!strcmp(val, "codec_master")) { + SNDERR("deprecated bclk value '%s', use 'codec_provider", val); + + hw_cfg->bclk_provider = SND_SOC_TPLG_BCLK_CP; + } else if (!strcmp(val, "codec_provider")) { + hw_cfg->bclk_provider = SND_SOC_TPLG_BCLK_CP; } + return 0; + } - if (provider_legacy || - strcmp(id, "bclk") == 0) { + if (strcmp(id, "bclk_freq") == 0 || + strcmp(id, "bclk_rate") == 0) { + if (parse_unsigned(n, &hw_cfg->bclk_rate)) + return -EINVAL; + return 0; + } - if (snd_config_get_string(n, &val) < 0) - return -EINVAL; + if (strcmp(id, "bclk_invert") == 0 || + strcmp(id, "invert_bclk") == 0) { + ival = snd_config_get_bool(n); + if (ival < 0) + return -EINVAL; - if (!strcmp(val, "master")) { - /* For backwards capability, - * "master" == "codec is slave" - */ - SNDERR("deprecated bclk value '%s'", val); - - hw_cfg->bclk_provider = SND_SOC_TPLG_BCLK_CC; - } else if (!strcmp(val, "codec_slave")) { - SNDERR("deprecated bclk value '%s', use 'codec_consumer'", val); - - hw_cfg->bclk_provider = SND_SOC_TPLG_BCLK_CC; - } else if (!strcmp(val, "codec_consumer")) { - hw_cfg->bclk_provider = SND_SOC_TPLG_BCLK_CC; - } else if (!strcmp(val, "codec_master")) { - SNDERR("deprecated bclk value '%s', use 'codec_provider", val); - - hw_cfg->bclk_provider = SND_SOC_TPLG_BCLK_CP; - } else if (!strcmp(val, "codec_provider")) { - hw_cfg->bclk_provider = SND_SOC_TPLG_BCLK_CP; - } - continue; - } + hw_cfg->invert_bclk = ival; + return 0; + } - if (strcmp(id, "bclk_freq") == 0 || - strcmp(id, "bclk_rate") == 0) { - if (parse_unsigned(n, &hw_cfg->bclk_rate)) - return -EINVAL; - continue; - } + provider_legacy = false; + if (strcmp(id, "fsync_master") == 0) { + SNDERR("deprecated option %s, please use 'fsync'\n", id); + provider_legacy = true; + } - if (strcmp(id, "bclk_invert") == 0 || - strcmp(id, "invert_bclk") == 0) { - ival = snd_config_get_bool(n); - if (ival < 0) - return -EINVAL; + if (provider_legacy || + strcmp(id, "fsync") == 0) { - hw_cfg->invert_bclk = ival; - continue; - } + if (snd_config_get_string(n, &val) < 0) + return -EINVAL; - provider_legacy = false; - if (strcmp(id, "fsync_master") == 0) { - SNDERR("deprecated option %s, please use 'fsync'\n", id); - provider_legacy = true; - } + if (!strcmp(val, "master")) { + /* For backwards capability, + * "master" == "codec is slave" + */ + SNDERR("deprecated fsync value '%s'", val); - if (provider_legacy || - strcmp(id, "fsync") == 0) { + hw_cfg->fsync_provider = SND_SOC_TPLG_FSYNC_CC; + } else if (!strcmp(val, "codec_slave")) { + SNDERR("deprecated fsync value '%s', use 'codec_consumer'", val); - if (snd_config_get_string(n, &val) < 0) - return -EINVAL; + hw_cfg->fsync_provider = SND_SOC_TPLG_FSYNC_CC; + } else if (!strcmp(val, "codec_consumer")) { + hw_cfg->fsync_provider = SND_SOC_TPLG_FSYNC_CC; + } else if (!strcmp(val, "codec_master")) { + SNDERR("deprecated fsync value '%s', use 'codec_provider'", val); - if (!strcmp(val, "master")) { - /* For backwards capability, - * "master" == "codec is slave" - */ - SNDERR("deprecated fsync value '%s'", val); - - hw_cfg->fsync_provider = SND_SOC_TPLG_FSYNC_CC; - } else if (!strcmp(val, "codec_slave")) { - SNDERR("deprecated fsync value '%s', use 'codec_consumer'", val); - - hw_cfg->fsync_provider = SND_SOC_TPLG_FSYNC_CC; - } else if (!strcmp(val, "codec_consumer")) { - hw_cfg->fsync_provider = SND_SOC_TPLG_FSYNC_CC; - } else if (!strcmp(val, "codec_master")) { - SNDERR("deprecated fsync value '%s', use 'codec_provider'", val); - - hw_cfg->fsync_provider = SND_SOC_TPLG_FSYNC_CP; - } else if (!strcmp(val, "codec_provider")) { - hw_cfg->fsync_provider = SND_SOC_TPLG_FSYNC_CP; - } - continue; + hw_cfg->fsync_provider = SND_SOC_TPLG_FSYNC_CP; + } else if (!strcmp(val, "codec_provider")) { + hw_cfg->fsync_provider = SND_SOC_TPLG_FSYNC_CP; } + return 0; + } - if (strcmp(id, "fsync_invert") == 0 || - strcmp(id, "invert_fsync") == 0) { - ival = snd_config_get_bool(n); - if (ival < 0) - return -EINVAL; + if (strcmp(id, "fsync_invert") == 0 || + strcmp(id, "invert_fsync") == 0) { + ival = snd_config_get_bool(n); + if (ival < 0) + return -EINVAL; - hw_cfg->invert_fsync = ival; - continue; - } + hw_cfg->invert_fsync = ival; + return 0; + } - if (strcmp(id, "fsync_freq") == 0 || - strcmp(id, "fsync_rate") == 0) { - if (parse_unsigned(n, &hw_cfg->fsync_rate)) - return -EINVAL; - continue; - } + if (strcmp(id, "fsync_freq") == 0 || + strcmp(id, "fsync_rate") == 0) { + if (parse_unsigned(n, &hw_cfg->fsync_rate)) + return -EINVAL; + return 0; + } - if (strcmp(id, "mclk_freq") == 0 || - strcmp(id, "mclk_rate") == 0) { - if (parse_unsigned(n, &hw_cfg->mclk_rate)) - return -EINVAL; - continue; - } + if (strcmp(id, "mclk_freq") == 0 || + strcmp(id, "mclk_rate") == 0) { + if (parse_unsigned(n, &hw_cfg->mclk_rate)) + return -EINVAL; + return 0; + } - if (strcmp(id, "mclk") == 0 || - strcmp(id, "mclk_direction") == 0) { - if (snd_config_get_string(n, &val) < 0) - return -EINVAL; + if (strcmp(id, "mclk") == 0 || + strcmp(id, "mclk_direction") == 0) { + if (snd_config_get_string(n, &val) < 0) + return -EINVAL; - if (!strcmp(val, "master")) { - /* For backwards capability, - * "master" == "for codec, mclk is input" - */ - SNDERR("deprecated mclk value '%s'", val); - - hw_cfg->mclk_direction = SND_SOC_TPLG_MCLK_CI; - } else if (!strcmp(val, "codec_mclk_in")) { - hw_cfg->mclk_direction = SND_SOC_TPLG_MCLK_CI; - } else if (!strcmp(val, "codec_mclk_out")) { - hw_cfg->mclk_direction = SND_SOC_TPLG_MCLK_CO; - } - continue; + if (!strcmp(val, "master")) { + /* For backwards capability, + * "master" == "for codec, mclk is input" + */ + SNDERR("deprecated mclk value '%s'", val); + + hw_cfg->mclk_direction = SND_SOC_TPLG_MCLK_CI; + } else if (!strcmp(val, "codec_mclk_in")) { + hw_cfg->mclk_direction = SND_SOC_TPLG_MCLK_CI; + } else if (!strcmp(val, "codec_mclk_out")) { + hw_cfg->mclk_direction = SND_SOC_TPLG_MCLK_CO; } + return 0; + } - if (strcmp(id, "pm_gate_clocks") == 0 || - strcmp(id, "clock_gated") == 0) { - ival = snd_config_get_bool(n); - if (ival < 0) - return -EINVAL; + if (strcmp(id, "pm_gate_clocks") == 0 || + strcmp(id, "clock_gated") == 0) { + ival = snd_config_get_bool(n); + if (ival < 0) + return -EINVAL; - if (ival) - hw_cfg->clock_gated = - SND_SOC_TPLG_DAI_CLK_GATE_GATED; - else - hw_cfg->clock_gated = - SND_SOC_TPLG_DAI_CLK_GATE_CONT; - continue; - } + if (ival) + hw_cfg->clock_gated = + SND_SOC_TPLG_DAI_CLK_GATE_GATED; + else + hw_cfg->clock_gated = + SND_SOC_TPLG_DAI_CLK_GATE_CONT; + return 0; + } - if (strcmp(id, "tdm_slots") == 0) { - if (parse_unsigned(n, &hw_cfg->tdm_slots)) - return -EINVAL; - continue; - } + if (strcmp(id, "tdm_slots") == 0) { + if (parse_unsigned(n, &hw_cfg->tdm_slots)) + return -EINVAL; + return 0; + } - if (strcmp(id, "tdm_slot_width") == 0) { - if (parse_unsigned(n, &hw_cfg->tdm_slot_width)) - return -EINVAL; - continue; - } + if (strcmp(id, "tdm_slot_width") == 0) { + if (parse_unsigned(n, &hw_cfg->tdm_slot_width)) + return -EINVAL; + return 0; + } - if (strcmp(id, "tx_slots") == 0) { - if (parse_unsigned(n, &hw_cfg->tx_slots)) - return -EINVAL; - continue; - } + if (strcmp(id, "tx_slots") == 0) { + if (parse_unsigned(n, &hw_cfg->tx_slots)) + return -EINVAL; + return 0; + } - if (strcmp(id, "rx_slots") == 0) { - if (parse_unsigned(n, &hw_cfg->rx_slots)) - return -EINVAL; - continue; - } + if (strcmp(id, "rx_slots") == 0) { + if (parse_unsigned(n, &hw_cfg->rx_slots)) + return -EINVAL; + return 0; + } - if (strcmp(id, "tx_channels") == 0) { - if (parse_unsigned(n, &hw_cfg->tx_channels)) - return -EINVAL; - continue; - } + if (strcmp(id, "tx_channels") == 0) { + if (parse_unsigned(n, &hw_cfg->tx_channels)) + return -EINVAL; + return 0; + } - if (strcmp(id, "rx_channels") == 0) { - if (parse_unsigned(n, &hw_cfg->rx_channels)) - return -EINVAL; - continue; - } + if (strcmp(id, "rx_channels") == 0) { + if (parse_unsigned(n, &hw_cfg->rx_channels)) + return -EINVAL; + return 0; + } + + return 0; +} + +static int tplg_set_hw_config(snd_config_t *cfg, struct snd_soc_tplg_hw_config *hw_cfg) +{ + snd_config_iterator_t i, next; + snd_config_t *n; + int ret; + snd_config_for_each(i, next, cfg) { + n = snd_config_iterator_entry(i); + ret = tplg_set_hw_config_param(n, hw_cfg); + if (ret < 0) + return ret; } return 0; } +int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg, + void *private ATTRIBUTE_UNUSED) +{ + struct snd_soc_tplg_hw_config *hw_cfg; + struct tplg_elem *elem; + + elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_HW_CONFIG); + if (!elem) + return -ENOMEM; + + hw_cfg = elem->hw_cfg; + hw_cfg->size = elem->size; + + tplg_dbg(" Link HW config: %s", elem->id); + + return tplg_set_hw_config(cfg, hw_cfg); +} + /* save hw config */ int tplg_save_hw_config(snd_tplg_t *tplg ATTRIBUTE_UNUSED, struct tplg_elem *elem, diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h index 50c62cf4b..7d78ae523 100644 --- a/src/topology/tplg_local.h +++ b/src/topology/tplg_local.h @@ -459,3 +459,4 @@ int tplg_decode_dai(snd_tplg_t *tplg, size_t pos, struct snd_soc_tplg_hdr *hdr, void *bin, size_t size); int lookup_channel(const char *c); +int tplg_set_hw_config_param(snd_config_t *n, struct snd_soc_tplg_hw_config *hw_cfg); From 4a3f22ca060b5b8ed4701812ed16599df2ecfeeb Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Sat, 20 Mar 2021 22:37:58 -0700 Subject: [PATCH 03/33] topology: pcm: expose functions for parsing PCM params Expose the tplg_parse_pcm_param(), tplg_parse_link_param() tplg_parse_stream_caps_param() functions for parsing the PCM params, link params and PCM capabilities. These will be reused in Topology2.0. Signed-off-by: Ranjani Sridharan --- src/topology/pcm.c | 560 ++++++++++++++++++++------------------ src/topology/tplg_local.h | 4 + 2 files changed, 304 insertions(+), 260 deletions(-) diff --git a/src/topology/pcm.c b/src/topology/pcm.c index e5c0d8f9b..e8b95260d 100644 --- a/src/topology/pcm.c +++ b/src/topology/pcm.c @@ -394,142 +394,153 @@ static int parse_unsigned(snd_config_t *n, void *dst) return 0; } -/* Parse pcm stream capabilities */ -int tplg_parse_stream_caps(snd_tplg_t *tplg, - snd_config_t *cfg, - void *private ATTRIBUTE_UNUSED) +int tplg_parse_stream_caps_param(snd_config_t *n, struct snd_soc_tplg_stream_caps *sc) { - struct snd_soc_tplg_stream_caps *sc; - struct tplg_elem *elem; - snd_config_iterator_t i, next; - snd_config_t *n; const char *id, *val; char *s; int err; - elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_STREAM_CAPS); - if (!elem) - return -ENOMEM; + if (snd_config_get_id(n, &id) < 0) + return 0; - sc = elem->stream_caps; - sc->size = elem->size; - snd_strlcpy(sc->name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + /* skip comments */ + if (strcmp(id, "comment") == 0) + return 0; + if (id[0] == '#') + return 0; - tplg_dbg(" PCM Capabilities: %s", elem->id); + if (strcmp(id, "formats") == 0) { + if (snd_config_get_string(n, &val) < 0) + return -EINVAL; - snd_config_for_each(i, next, cfg) { - n = snd_config_iterator_entry(i); - if (snd_config_get_id(n, &id) < 0) - continue; + s = strdup(val); + if (s == NULL) + return -ENOMEM; - /* skip comments */ - if (strcmp(id, "comment") == 0) - continue; - if (id[0] == '#') - continue; + err = split_format(sc, s); + free(s); - if (strcmp(id, "formats") == 0) { - if (snd_config_get_string(n, &val) < 0) - return -EINVAL; + if (err < 0) + return err; - s = strdup(val); - if (s == NULL) - return -ENOMEM; + tplg_dbg("\t\t%s: %s", id, val); + return 0; + } - err = split_format(sc, s); - free(s); + if (strcmp(id, "rates") == 0) { + if (snd_config_get_string(n, &val) < 0) + return -EINVAL; - if (err < 0) - return err; + s = strdup(val); + if (!s) + return -ENOMEM; - tplg_dbg("\t\t%s: %s", id, val); - continue; - } + err = split_rate(sc, s); + free(s); - if (strcmp(id, "rates") == 0) { - if (snd_config_get_string(n, &val) < 0) - return -EINVAL; + if (err < 0) + return err; - s = strdup(val); - if (!s) - return -ENOMEM; + tplg_dbg("\t\t%s: %s", id, val); + return 0; + } - err = split_rate(sc, s); - free(s); + if (strcmp(id, "rate_min") == 0) { + if (parse_unsigned(n, &sc->rate_min)) + return -EINVAL; + return 0; + } - if (err < 0) - return err; + if (strcmp(id, "rate_max") == 0) { + if (parse_unsigned(n, &sc->rate_max)) + return -EINVAL; + return 0; + } - tplg_dbg("\t\t%s: %s", id, val); - continue; - } + if (strcmp(id, "channels_min") == 0) { + if (parse_unsigned(n, &sc->channels_min)) + return -EINVAL; + return 0; + } - if (strcmp(id, "rate_min") == 0) { - if (parse_unsigned(n, &sc->rate_min)) - return -EINVAL; - continue; - } + if (strcmp(id, "channels_max") == 0) { + if (parse_unsigned(n, &sc->channels_max)) + return -EINVAL; + return 0; + } - if (strcmp(id, "rate_max") == 0) { - if (parse_unsigned(n, &sc->rate_max)) - return -EINVAL; - continue; - } + if (strcmp(id, "periods_min") == 0) { + if (parse_unsigned(n, &sc->periods_min)) + return -EINVAL; + return 0; + } - if (strcmp(id, "channels_min") == 0) { - if (parse_unsigned(n, &sc->channels_min)) - return -EINVAL; - continue; - } + if (strcmp(id, "periods_max") == 0) { + if (parse_unsigned(n, &sc->periods_max)) + return -EINVAL; + return 0; + } - if (strcmp(id, "channels_max") == 0) { - if (parse_unsigned(n, &sc->channels_max)) - return -EINVAL; - continue; - } + if (strcmp(id, "period_size_min") == 0) { + if (parse_unsigned(n, &sc->period_size_min)) + return -EINVAL; + return 0; + } - if (strcmp(id, "periods_min") == 0) { - if (parse_unsigned(n, &sc->periods_min)) - return -EINVAL; - continue; - } + if (strcmp(id, "period_size_max") == 0) { + if (parse_unsigned(n, &sc->period_size_max)) + return -EINVAL; + return 0; + } - if (strcmp(id, "periods_max") == 0) { - if (parse_unsigned(n, &sc->periods_max)) - return -EINVAL; - continue; - } + if (strcmp(id, "buffer_size_min") == 0) { + if (parse_unsigned(n, &sc->buffer_size_min)) + return -EINVAL; + return 0; + } - if (strcmp(id, "period_size_min") == 0) { - if (parse_unsigned(n, &sc->period_size_min)) - return -EINVAL; - continue; - } + if (strcmp(id, "buffer_size_max") == 0) { + if (parse_unsigned(n, &sc->buffer_size_max)) + return -EINVAL; + return 0; + } - if (strcmp(id, "period_size_max") == 0) { - if (parse_unsigned(n, &sc->period_size_max)) - return -EINVAL; - continue; - } + if (strcmp(id, "sig_bits") == 0) { + if (parse_unsigned(n, &sc->sig_bits)) + return -EINVAL; + return 0; + } - if (strcmp(id, "buffer_size_min") == 0) { - if (parse_unsigned(n, &sc->buffer_size_min)) - return -EINVAL; - continue; - } + return 0; +} - if (strcmp(id, "buffer_size_max") == 0) { - if (parse_unsigned(n, &sc->buffer_size_max)) - return -EINVAL; - continue; - } +/* Parse pcm stream capabilities */ +int tplg_parse_stream_caps(snd_tplg_t *tplg, + snd_config_t *cfg, + void *private ATTRIBUTE_UNUSED) +{ + struct snd_soc_tplg_stream_caps *sc; + struct tplg_elem *elem; + snd_config_iterator_t i, next; + snd_config_t *n; + int err; - if (strcmp(id, "sig_bits") == 0) { - if (parse_unsigned(n, &sc->sig_bits)) - return -EINVAL; - continue; - } + elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_STREAM_CAPS); + if (!elem) + return -ENOMEM; + + sc = elem->stream_caps; + sc->size = elem->size; + snd_strlcpy(sc->name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + tplg_dbg(" PCM Capabilities: %s", elem->id); + + snd_config_for_each(i, next, cfg) { + n = snd_config_iterator_entry(i); + + err = tplg_parse_stream_caps_param(n, sc); + if (err < 0) + return err; } return 0; @@ -835,6 +846,94 @@ static int save_flags(unsigned int flags, unsigned int mask, return err; } +int tplg_parse_pcm_param(snd_tplg_t *tplg, snd_config_t *n, struct tplg_elem *elem) +{ + struct snd_soc_tplg_pcm *pcm = elem->pcm; + const char *id; + int err, ival; + + if (snd_config_get_id(n, &id) < 0) + return 0; + + /* skip comments */ + if (strcmp(id, "comment") == 0) + return 0; + + if (id[0] == '#') + return 0; + + if (strcmp(id, "id") == 0) { + if (parse_unsigned(n, &pcm->pcm_id)) + return -EINVAL; + return 0; + } + + if (strcmp(id, "pcm") == 0) { + err = tplg_parse_compound(tplg, n, + tplg_parse_streams, elem); + if (err < 0) + return err; + return 0; + } + + if (strcmp(id, "compress") == 0) { + ival = snd_config_get_bool(n); + if (ival < 0) + return -EINVAL; + + pcm->compress = ival; + + tplg_dbg("\t%s: %d", id, ival); + return 0; + } + + if (strcmp(id, "dai") == 0) { + err = tplg_parse_compound(tplg, n, + tplg_parse_fe_dai, elem); + if (err < 0) + return err; + return 0; + } + + /* flags */ + if (strcmp(id, "symmetric_rates") == 0) { + err = parse_flag(n, + SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_RATES, + &pcm->flag_mask, &pcm->flags); + if (err < 0) + return err; + return 0; + } + + if (strcmp(id, "symmetric_channels") == 0) { + err = parse_flag(n, + SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_CHANNELS, + &pcm->flag_mask, &pcm->flags); + if (err < 0) + return err; + return 0; + } + + if (strcmp(id, "symmetric_sample_bits") == 0) { + err = parse_flag(n, + SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_SAMPLEBITS, + &pcm->flag_mask, &pcm->flags); + if (err < 0) + return err; + return 0; + } + + /* private data */ + if (strcmp(id, "data") == 0) { + err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA); + if (err < 0) + return err; + return 0; + } + + return 0; +} + /* Parse PCM (for front end DAI & DAI link) in text conf file */ int tplg_parse_pcm(snd_tplg_t *tplg, snd_config_t *cfg, void *private ATTRIBUTE_UNUSED) @@ -843,8 +942,7 @@ int tplg_parse_pcm(snd_tplg_t *tplg, snd_config_t *cfg, struct tplg_elem *elem; snd_config_iterator_t i, next; snd_config_t *n; - const char *id; - int err, ival; + int err; elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_PCM); if (!elem) @@ -859,83 +957,9 @@ int tplg_parse_pcm(snd_tplg_t *tplg, snd_config_t *cfg, snd_config_for_each(i, next, cfg) { n = snd_config_iterator_entry(i); - if (snd_config_get_id(n, &id) < 0) - continue; - - /* skip comments */ - if (strcmp(id, "comment") == 0) - continue; - if (id[0] == '#') - continue; - - if (strcmp(id, "id") == 0) { - if (parse_unsigned(n, &pcm->pcm_id)) - return -EINVAL; - continue; - } - - if (strcmp(id, "pcm") == 0) { - err = tplg_parse_compound(tplg, n, - tplg_parse_streams, elem); - if (err < 0) - return err; - continue; - } - - if (strcmp(id, "compress") == 0) { - ival = snd_config_get_bool(n); - if (ival < 0) - return -EINVAL; - - pcm->compress = ival; - - tplg_dbg("\t%s: %d", id, ival); - continue; - } - - if (strcmp(id, "dai") == 0) { - err = tplg_parse_compound(tplg, n, - tplg_parse_fe_dai, elem); - if (err < 0) - return err; - continue; - } - - /* flags */ - if (strcmp(id, "symmetric_rates") == 0) { - err = parse_flag(n, - SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_RATES, - &pcm->flag_mask, &pcm->flags); - if (err < 0) - return err; - continue; - } - - if (strcmp(id, "symmetric_channels") == 0) { - err = parse_flag(n, - SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_CHANNELS, - &pcm->flag_mask, &pcm->flags); - if (err < 0) - return err; - continue; - } - - if (strcmp(id, "symmetric_sample_bits") == 0) { - err = parse_flag(n, - SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_SAMPLEBITS, - &pcm->flag_mask, &pcm->flags); - if (err < 0) - return err; - continue; - } - - /* private data */ - if (strcmp(id, "data") == 0) { - err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA); - if (err < 0) - return err; - continue; - } + err = tplg_parse_pcm_param(tplg, n, elem); + if (err < 0) + return err; } return 0; @@ -1130,6 +1154,94 @@ static int parse_hw_config_refs(snd_tplg_t *tplg ATTRIBUTE_UNUSED, return 0; } +int tplg_parse_link_param(snd_tplg_t *tplg, snd_config_t *n, + struct snd_soc_tplg_link_config *link, struct tplg_elem *elem) +{ + const char *id, *val = NULL; + int err; + + if (snd_config_get_id(n, &id) < 0) + return 0; + + /* skip comments */ + if (strcmp(id, "comment") == 0) + return 0; + if (id[0] == '#') + return 0; + + if (strcmp(id, "id") == 0) { + if (parse_unsigned(n, &link->id)) + return -EINVAL; + return 0; + } + + if (strcmp(id, "stream_name") == 0) { + if (snd_config_get_string(n, &val) < 0) + return -EINVAL; + + snd_strlcpy(link->stream_name, val, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + tplg_dbg("\t%s: %s", id, val); + return 0; + } + + if (strcmp(id, "hw_configs") == 0) { + if (!elem) + return 0; + + err = parse_hw_config_refs(tplg, n, elem); + if (err < 0) + return err; + return 0; + } + + if (strcmp(id, "default_hw_conf_id") == 0) { + if (parse_unsigned(n, &link->default_hw_config_id)) + return -EINVAL; + return 0; + } + + /* flags */ + if (strcmp(id, "symmetric_rates") == 0) { + err = parse_flag(n, + SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_RATES, + &link->flag_mask, &link->flags); + if (err < 0) + return err; + return 0; + } + + if (strcmp(id, "symmetric_channels") == 0) { + err = parse_flag(n, + SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_CHANNELS, + &link->flag_mask, &link->flags); + if (err < 0) + return err; + return 0; + } + + if (strcmp(id, "symmetric_sample_bits") == 0) { + err = parse_flag(n, + SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_SAMPLEBITS, + &link->flag_mask, &link->flags); + if (err < 0) + return err; + return 0; + } + + /* private data */ + if (strcmp(id, "data") == 0) { + if (!elem) + return 0; + err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA); + if (err < 0) + return err; + return 0; + } + + return 0; +} + /* Parse a physical link element in text conf file */ int tplg_parse_link(snd_tplg_t *tplg, snd_config_t *cfg, void *private ATTRIBUTE_UNUSED) @@ -1138,8 +1250,7 @@ int tplg_parse_link(snd_tplg_t *tplg, snd_config_t *cfg, struct tplg_elem *elem; snd_config_iterator_t i, next; snd_config_t *n; - const char *id, *val = NULL; - int err; + int ret; elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_BE); if (!elem) @@ -1152,81 +1263,10 @@ int tplg_parse_link(snd_tplg_t *tplg, snd_config_t *cfg, tplg_dbg(" Link: %s", elem->id); snd_config_for_each(i, next, cfg) { - n = snd_config_iterator_entry(i); - if (snd_config_get_id(n, &id) < 0) - continue; - - /* skip comments */ - if (strcmp(id, "comment") == 0) - continue; - if (id[0] == '#') - continue; - - if (strcmp(id, "id") == 0) { - if (parse_unsigned(n, &link->id)) - return -EINVAL; - continue; - } - - if (strcmp(id, "stream_name") == 0) { - if (snd_config_get_string(n, &val) < 0) - return -EINVAL; - - snd_strlcpy(link->stream_name, val, - SNDRV_CTL_ELEM_ID_NAME_MAXLEN); - tplg_dbg("\t%s: %s", id, val); - continue; - } - - if (strcmp(id, "hw_configs") == 0) { - err = parse_hw_config_refs(tplg, n, elem); - if (err < 0) - return err; - continue; - } - - if (strcmp(id, "default_hw_conf_id") == 0) { - if (parse_unsigned(n, &link->default_hw_config_id)) - return -EINVAL; - continue; - } - - /* flags */ - if (strcmp(id, "symmetric_rates") == 0) { - err = parse_flag(n, - SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_RATES, - &link->flag_mask, &link->flags); - if (err < 0) - return err; - continue; - } - - if (strcmp(id, "symmetric_channels") == 0) { - err = parse_flag(n, - SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_CHANNELS, - &link->flag_mask, &link->flags); - if (err < 0) - return err; - continue; - } - - if (strcmp(id, "symmetric_sample_bits") == 0) { - err = parse_flag(n, - SND_SOC_TPLG_LNK_FLGBIT_SYMMETRIC_SAMPLEBITS, - &link->flag_mask, &link->flags); - if (err < 0) - return err; - continue; - } - - /* private data */ - if (strcmp(id, "data") == 0) { - err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA); - if (err < 0) - return err; - continue; - } + ret = tplg_parse_link_param(tplg, n, link, elem); + if (ret < 0) + return ret; } return 0; diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h index 7d78ae523..f15c37b2c 100644 --- a/src/topology/tplg_local.h +++ b/src/topology/tplg_local.h @@ -460,3 +460,7 @@ int tplg_decode_dai(snd_tplg_t *tplg, size_t pos, void *bin, size_t size); int lookup_channel(const char *c); int tplg_set_hw_config_param(snd_config_t *n, struct snd_soc_tplg_hw_config *hw_cfg); +int tplg_parse_stream_caps_param(snd_config_t *n, struct snd_soc_tplg_stream_caps *sc); +int tplg_parse_pcm_param(snd_tplg_t *tplg, snd_config_t *n, struct tplg_elem *elem); +int tplg_parse_link_param(snd_tplg_t *tplg, snd_config_t *n, + struct snd_soc_tplg_link_config *link, struct tplg_elem *elem); From 4dfb31fa44436b1316ad966a1a1cba3c81cf075a Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Sat, 20 Mar 2021 22:41:43 -0700 Subject: [PATCH 04/33] topology: dapm: expose lookup_widget() This will be reused in topology2.0 Signed-off-by: Ranjani Sridharan --- src/topology/dapm.c | 2 +- src/topology/tplg_local.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/topology/dapm.c b/src/topology/dapm.c index f6a84a603..73d27fcbd 100644 --- a/src/topology/dapm.c +++ b/src/topology/dapm.c @@ -48,7 +48,7 @@ static const struct map_elem widget_map[] = { {"decoder", SND_SOC_TPLG_DAPM_DECODER}, }; -static int lookup_widget(const char *w) +int lookup_widget(const char *w) { unsigned int i; diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h index f15c37b2c..0d4f9302d 100644 --- a/src/topology/tplg_local.h +++ b/src/topology/tplg_local.h @@ -464,3 +464,4 @@ int tplg_parse_stream_caps_param(snd_config_t *n, struct snd_soc_tplg_stream_cap int tplg_parse_pcm_param(snd_tplg_t *tplg, snd_config_t *n, struct tplg_elem *elem); int tplg_parse_link_param(snd_tplg_t *tplg, snd_config_t *n, struct snd_soc_tplg_link_config *link, struct tplg_elem *elem); +int lookup_widget(const char *w); From b49954a33ed397114f01da7516e4d6f7f268e55d Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Sat, 20 Mar 2021 22:55:44 -0700 Subject: [PATCH 05/33] topology: ctl: expose functions for parsing controls Expose some functions needed for parsing mixer and byte controls. These will be reused for topology2.0. Signed-off-by: Ranjani Sridharan --- src/topology/ctl.c | 419 ++++++++++++++++++++++---------------- src/topology/tplg_local.h | 8 + 2 files changed, 251 insertions(+), 176 deletions(-) diff --git a/src/topology/ctl.c b/src/topology/ctl.c index dd05424d3..96a2b66cd 100644 --- a/src/topology/ctl.c +++ b/src/topology/ctl.c @@ -45,16 +45,21 @@ static const struct ctl_access_elem ctl_access[] = { }; /* find CTL access strings and conver to values */ -static int parse_access_values(snd_config_t *cfg, - struct snd_soc_tplg_ctl_hdr *hdr) +int parse_access_values(snd_config_t *cfg, struct snd_soc_tplg_ctl_hdr *hdr) { snd_config_iterator_t i, next; snd_config_t *n; - const char *value = NULL; + const char *id, *value = NULL; unsigned int j; tplg_dbg(" Access:"); + if (snd_config_get_id(cfg, &id) < 0) + return 0; + + if (strcmp(id, "access")) + return 0; + snd_config_for_each(i, next, cfg) { n = snd_config_iterator_entry(i); @@ -67,6 +72,7 @@ static int parse_access_values(snd_config_t *cfg, if (strcmp(value, ctl_access[j].name) == 0) { hdr->access |= ctl_access[j].value; tplg_dbg("\t%s", value); + break; } } @@ -314,6 +320,36 @@ int tplg_build_controls(snd_tplg_t *tplg) return 0; } +int tplg_parse_tlv_dbscale_param(snd_config_t *n, struct snd_soc_tplg_tlv_dbscale *scale) +{ + const char *id = NULL; + int val; + + /* get ID */ + if (snd_config_get_id(n, &id) < 0) + return -EINVAL; + + /* get value */ + if (tplg_get_integer(n, &val, 0)) + return 0; + + tplg_dbg("\t%s = %i", id, val); + + /* get TLV data */ + if (strcmp(id, "min") == 0) + scale->min = val; + else if (strcmp(id, "step") == 0) + scale->step = val; + else if (strcmp(id, "mute") == 0) + scale->mute = val; + else if (strcmp(id, "name")) { + SNDERR("unknown id '%s'", id); + return -EINVAL; + } + + return 0; +} + /* * Parse TLV of DBScale type. @@ -326,10 +362,9 @@ static int tplg_parse_tlv_dbscale(snd_config_t *cfg, struct tplg_elem *elem) snd_config_t *n; struct snd_soc_tplg_ctl_tlv *tplg_tlv = elem->tlv; struct snd_soc_tplg_tlv_dbscale *scale; - const char *id = NULL; - int val; + int ret; - tplg_dbg(" scale: %s", elem->id); + tplg_dbg("scale: %s", elem->id); tplg_tlv->size = sizeof(struct snd_soc_tplg_ctl_tlv); tplg_tlv->type = SNDRV_CTL_TLVT_DB_SCALE; @@ -339,25 +374,9 @@ static int tplg_parse_tlv_dbscale(snd_config_t *cfg, struct tplg_elem *elem) n = snd_config_iterator_entry(i); - /* get ID */ - if (snd_config_get_id(n, &id) < 0) - return -EINVAL; - - /* get value */ - if (tplg_get_integer(n, &val, 0)) - continue; - - tplg_dbg("\t%s = %i", id, val); - - /* get TLV data */ - if (strcmp(id, "min") == 0) - scale->min = val; - else if (strcmp(id, "step") == 0) - scale->step = val; - else if (strcmp(id, "mute") == 0) - scale->mute = val; - else - SNDERR("unknown id '%s'", id); + ret = tplg_parse_tlv_dbscale_param(n, scale); + if (ret < 0) + return ret; } return 0; @@ -427,6 +446,94 @@ int tplg_save_tlv(snd_tplg_t *tplg ATTRIBUTE_UNUSED, return err; } +int tplg_parse_control_bytes_param(snd_tplg_t *tplg, snd_config_t *n, + struct snd_soc_tplg_bytes_control *be, + struct tplg_elem *elem) +{ + + const char *id, *val = NULL; + int err, ival; + + if (snd_config_get_id(n, &id) < 0) + return 0; + + /* skip comments */ + if (strcmp(id, "comment") == 0) + return 0; + if (id[0] == '#') + return 0; + + if (strcmp(id, "base") == 0) { + if (tplg_get_integer(n, &ival, 0)) + return -EINVAL; + + be->base = ival; + tplg_dbg("\t%s: %d", id, be->base); + return 0; + } + + if (strcmp(id, "num_regs") == 0) { + if (tplg_get_integer(n, &ival, 0)) + return -EINVAL; + + be->num_regs = ival; + tplg_dbg("\t%s: %d", id, be->num_regs); + return 0; + } + + if (strcmp(id, "max") == 0) { + if (tplg_get_integer(n, &ival, 0)) + return -EINVAL; + + be->max = ival; + tplg_dbg("\t%s: %d", id, be->max); + return 0; + } + + if (strcmp(id, "mask") == 0) { + if (tplg_get_integer(n, &ival, 16)) + return -EINVAL; + + be->mask = ival; + tplg_dbg("\t%s: %d", id, be->mask); + return 0; + } + + if (strcmp(id, "data") == 0) { + err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA); + if (err < 0) + return err; + return 0; + } + + if (strcmp(id, "tlv") == 0) { + if (snd_config_get_string(n, &val) < 0) + return -EINVAL; + + err = tplg_ref_add(elem, SND_TPLG_TYPE_TLV, val); + if (err < 0) + return err; + + tplg_dbg("\t%s: %s", id, val); + return 0; + } + + if (strcmp(id, "ops") == 0) { + err = tplg_parse_compound(tplg, n, tplg_parse_ops, &be->hdr); + if (err < 0) + return err; + return 0; + } + + if (strcmp(id, "extops") == 0) { + err = tplg_parse_compound(tplg, n, tplg_parse_ext_ops, be); + if (err < 0) + return err; + return 0; + } + + return 0; +} /* Parse Control Bytes */ int tplg_parse_control_bytes(snd_tplg_t *tplg, snd_config_t *cfg, @@ -436,8 +543,8 @@ int tplg_parse_control_bytes(snd_tplg_t *tplg, struct tplg_elem *elem; snd_config_iterator_t i, next; snd_config_t *n; - const char *id, *val = NULL; - int err, ival; + const char *id; + int err; bool access_set = false, tlv_set = false; elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_BYTES); @@ -453,94 +560,29 @@ int tplg_parse_control_bytes(snd_tplg_t *tplg, snd_config_for_each(i, next, cfg) { n = snd_config_iterator_entry(i); - if (snd_config_get_id(n, &id) < 0) - continue; - - /* skip comments */ - if (strcmp(id, "comment") == 0) - continue; - if (id[0] == '#') - continue; - - if (strcmp(id, "base") == 0) { - if (tplg_get_integer(n, &ival, 0)) - return -EINVAL; - - be->base = ival; - tplg_dbg("\t%s: %d", id, be->base); - continue; - } - - if (strcmp(id, "num_regs") == 0) { - if (tplg_get_integer(n, &ival, 0)) - return -EINVAL; - - be->num_regs = ival; - tplg_dbg("\t%s: %d", id, be->num_regs); - continue; - } - - if (strcmp(id, "max") == 0) { - if (tplg_get_integer(n, &ival, 0)) - return -EINVAL; - - be->max = ival; - tplg_dbg("\t%s: %d", id, be->max); - continue; - } - - if (strcmp(id, "mask") == 0) { - if (tplg_get_integer(n, &ival, 16)) - return -EINVAL; - - be->mask = ival; - tplg_dbg("\t%s: %d", id, be->mask); - continue; - } - - if (strcmp(id, "data") == 0) { - err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA); - if (err < 0) - return err; - continue; - } - - if (strcmp(id, "tlv") == 0) { - if (snd_config_get_string(n, &val) < 0) - return -EINVAL; - - err = tplg_ref_add(elem, SND_TPLG_TYPE_TLV, val); - if (err < 0) - return err; - - tlv_set = true; - tplg_dbg("\t%s: %s", id, val); - continue; - } - - if (strcmp(id, "ops") == 0) { - err = tplg_parse_compound(tplg, n, tplg_parse_ops, - &be->hdr); - if (err < 0) - return err; - continue; - } + err = tplg_parse_control_bytes_param(tplg, n, be, elem); + if (err < 0) + return err; + } - if (strcmp(id, "extops") == 0) { - err = tplg_parse_compound(tplg, n, tplg_parse_ext_ops, - be); - if (err < 0) - return err; + /* check if access/tlv are set. No need to check for error or parse these again */ + snd_config_for_each(i, next, cfg) { + n = snd_config_iterator_entry(i); + if (snd_config_get_id(n, &id) < 0) continue; - } if (strcmp(id, "access") == 0) { + if (!cfg) + return 0; err = parse_access(cfg, &be->hdr); if (err < 0) return err; access_set = true; continue; } + + if (!strcmp(id, "tlv")) + tlv_set = true; } /* set CTL access to default values if none are provided */ @@ -730,6 +772,86 @@ int tplg_save_control_enum(snd_tplg_t *tplg ATTRIBUTE_UNUSED, return err; } +int tplg_parse_control_mixer_param(snd_tplg_t *tplg, snd_config_t *n, + struct snd_soc_tplg_mixer_control *mc, + struct tplg_elem *elem) +{ + const char *id, *val = NULL; + int err, ival; + + if (snd_config_get_id(n, &id) < 0) + return 0; + + /* skip comments */ + if (strcmp(id, "comment") == 0) + return 0; + if (id[0] == '#') + return 0; + + if (strcmp(id, "channel") == 0) { + if (mc->num_channels >= SND_SOC_TPLG_MAX_CHAN) { + SNDERR("too many channels %s", elem->id); + return -EINVAL; + } + + err = tplg_parse_compound(tplg, n, tplg_parse_channel, + mc->channel); + if (err < 0) + return err; + + mc->num_channels = tplg->channel_idx; + return 0; + } + + if (strcmp(id, "max") == 0) { + if (tplg_get_integer(n, &ival, 0)) + return -EINVAL; + + mc->max = ival; + tplg_dbg("\t%s: %d", id, mc->max); + return 0; + } + + if (strcmp(id, "invert") == 0) { + ival = snd_config_get_bool(n); + if (ival < 0) + return -EINVAL; + mc->invert = ival; + + tplg_dbg("\t%s: %d", id, mc->invert); + return 0; + } + + if (strcmp(id, "ops") == 0) { + err = tplg_parse_compound(tplg, n, tplg_parse_ops, + &mc->hdr); + if (err < 0) + return err; + return 0; + } + + if (strcmp(id, "tlv") == 0) { + if (snd_config_get_string(n, &val) < 0) + return -EINVAL; + + err = tplg_ref_add(elem, SND_TPLG_TYPE_TLV, val); + if (err < 0) + return err; + + tplg_dbg("\t%s: %s", id, val); + return 0; + } + + if (strcmp(id, "data") == 0) { + err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA); + if (err < 0) + return err; + return 0; + } + + return 0; +} + /* Parse Controls. * * Mixer control. Supports multiple channels. @@ -742,8 +864,8 @@ int tplg_parse_control_mixer(snd_tplg_t *tplg, struct tplg_elem *elem; snd_config_iterator_t i, next; snd_config_t *n; - const char *id, *val = NULL; - int err, j, ival; + const char *id; + int err, j; bool access_set = false, tlv_set = false; elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_MIXER); @@ -763,87 +885,32 @@ int tplg_parse_control_mixer(snd_tplg_t *tplg, tplg_dbg(" Control Mixer: %s", elem->id); - /* giterate trough each mixer elment */ + /* iterate through each mixer element */ snd_config_for_each(i, next, cfg) { n = snd_config_iterator_entry(i); - if (snd_config_get_id(n, &id) < 0) - continue; - - /* skip comments */ - if (strcmp(id, "comment") == 0) - continue; - if (id[0] == '#') - continue; - - if (strcmp(id, "channel") == 0) { - if (mc->num_channels >= SND_SOC_TPLG_MAX_CHAN) { - SNDERR("too many channels %s", elem->id); - return -EINVAL; - } - - err = tplg_parse_compound(tplg, n, tplg_parse_channel, - mc->channel); - if (err < 0) - return err; - - mc->num_channels = tplg->channel_idx; - continue; - } - - if (strcmp(id, "max") == 0) { - if (tplg_get_integer(n, &ival, 0)) - return -EINVAL; - - mc->max = ival; - tplg_dbg("\t%s: %d", id, mc->max); - continue; - } - - if (strcmp(id, "invert") == 0) { - ival = snd_config_get_bool(n); - if (ival < 0) - return -EINVAL; - mc->invert = ival; - - tplg_dbg("\t%s: %d", id, mc->invert); - continue; - } - - if (strcmp(id, "ops") == 0) { - err = tplg_parse_compound(tplg, n, tplg_parse_ops, - &mc->hdr); - if (err < 0) - return err; - continue; - } - - if (strcmp(id, "tlv") == 0) { - if (snd_config_get_string(n, &val) < 0) - return -EINVAL; - - err = tplg_ref_add(elem, SND_TPLG_TYPE_TLV, val); - if (err < 0) - return err; - - tlv_set = true; - tplg_dbg("\t%s: %s", id, val); - continue; - } + err = tplg_parse_control_mixer_param(tplg, n, mc, elem); + if (err < 0) + return err; + } - if (strcmp(id, "data") == 0) { - err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA); - if (err < 0) - return err; + /* check if access/tlv are set. No need to check for error or parse these again */ + snd_config_for_each(i, next, cfg) { + n = snd_config_iterator_entry(i); + if (snd_config_get_id(n, &id) < 0) continue; - } if (strcmp(id, "access") == 0) { + if (!cfg) + return 0; err = parse_access(cfg, &mc->hdr); if (err < 0) return err; access_set = true; continue; } + + if (!strcmp(id, "tlv")) + tlv_set = true; } /* set CTL access to default values if none are provided */ diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h index 0d4f9302d..13d97317e 100644 --- a/src/topology/tplg_local.h +++ b/src/topology/tplg_local.h @@ -465,3 +465,11 @@ int tplg_parse_pcm_param(snd_tplg_t *tplg, snd_config_t *n, struct tplg_elem *el int tplg_parse_link_param(snd_tplg_t *tplg, snd_config_t *n, struct snd_soc_tplg_link_config *link, struct tplg_elem *elem); int lookup_widget(const char *w); +int tplg_parse_control_mixer_param(snd_tplg_t *tplg, snd_config_t *n, + struct snd_soc_tplg_mixer_control *mc, + struct tplg_elem *elem); +int tplg_parse_control_bytes_param(snd_tplg_t *tplg, snd_config_t *n, + struct snd_soc_tplg_bytes_control *be, + struct tplg_elem *elem); +int parse_access_values(snd_config_t *cfg, struct snd_soc_tplg_ctl_hdr *hdr); +int tplg_parse_tlv_dbscale_param(snd_config_t *n, struct snd_soc_tplg_tlv_dbscale *scale); From 06818171b448016577f6e3667bc87d437c13feb0 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Sun, 22 Nov 2020 12:25:53 -0800 Subject: [PATCH 06/33] topology: data: expose some functions to parse private data Expose multiple functions that will be reused to parse tuples and private data in Topology2.0. Signed-off-by: Ranjani Sridharan --- src/topology/data.c | 159 ++++++++++++++++++++------------------ src/topology/tplg_local.h | 7 ++ 2 files changed, 91 insertions(+), 75 deletions(-) diff --git a/src/topology/data.c b/src/topology/data.c index 0546d63e4..a93d920fe 100644 --- a/src/topology/data.c +++ b/src/topology/data.c @@ -18,6 +18,7 @@ */ #include "list.h" +#include "local.h" #include "tplg_local.h" #include @@ -340,7 +341,7 @@ static int get_hex_num(const char *str) } /* get uuid from a string made by 16 characters separated by commas */ -static int get_uuid(const char *str, unsigned char *uuid_le) +int get_uuid(const char *str, unsigned char *uuid_le) { unsigned long int val; char *tmp, *s = NULL; @@ -465,8 +466,7 @@ static int copy_data_hex(char *data, int off, const char *str, int width) return 0; } -static int tplg_parse_data_hex(snd_config_t *cfg, struct tplg_elem *elem, - int width) +int tplg_parse_data_hex(snd_config_t *cfg, struct tplg_elem *elem, int width) { struct snd_soc_tplg_private *priv; const char *value = NULL; @@ -516,8 +516,7 @@ static int tplg_parse_data_hex(snd_config_t *cfg, struct tplg_elem *elem, } /* get the token integer value from its id */ -static int get_token_value(const char *token_id, - struct tplg_vendor_tokens *tokens) +int get_token_value(const char *token_id, struct tplg_vendor_tokens *tokens) { unsigned int i; @@ -588,93 +587,103 @@ unsigned int tplg_get_tuple_size(int type) } } -/* Add a tuples object to the private buffer of its parent data element */ -static int copy_tuples(struct tplg_elem *elem, - struct tplg_vendor_tuples *tuples, - struct tplg_vendor_tokens *tokens) +int scan_tuple_set(struct tplg_elem *elem, struct tplg_tuple_set *tuple_set, + struct tplg_vendor_tokens *tokens, int size) { struct snd_soc_tplg_private *priv = elem->data, *priv2; - struct tplg_tuple_set *tuple_set; - struct tplg_tuple *tuple; - struct snd_soc_tplg_vendor_array *array; - struct snd_soc_tplg_vendor_uuid_elem *uuid; struct snd_soc_tplg_vendor_string_elem *string; struct snd_soc_tplg_vendor_value_elem *value; - int set_size, size, off; - unsigned int i, j; + struct snd_soc_tplg_vendor_uuid_elem *uuid; + struct snd_soc_tplg_vendor_array *array; + struct tplg_tuple *tuple; + int set_size, off; + unsigned int j; int token_val; - size = priv ? priv->size : 0; /* original private data size */ - /* scan each tuples set (one set per type) */ - for (i = 0; i < tuples->num_sets ; i++) { - tuple_set = tuples->set[i]; - set_size = sizeof(struct snd_soc_tplg_vendor_array) - + tplg_get_tuple_size(tuple_set->type) - * tuple_set->num_tuples; - size += set_size; - if (size > TPLG_MAX_PRIV_SIZE) { - SNDERR("data too big %d", size); - return -EINVAL; - } + set_size = sizeof(*array) + tplg_get_tuple_size(tuple_set->type) * tuple_set->num_tuples; + size += set_size; + if (size > TPLG_MAX_PRIV_SIZE) { + SNDERR("data too big %d", size); + return -EINVAL; + } - if (priv != NULL) { - priv2 = realloc(priv, sizeof(*priv) + size); - if (priv2 == NULL) { - free(priv); - priv = NULL; - } else { - priv = priv2; - } + if (priv != NULL) { + priv2 = realloc(priv, sizeof(*priv) + size); + if (priv2 == NULL) { + free(priv); + priv = NULL; } else { - priv = calloc(1, sizeof(*priv) + size); + priv = priv2; } - if (!priv) - return -ENOMEM; + } else { + priv = calloc(1, sizeof(*priv) + size); + } + if (!priv) + return -ENOMEM; - off = priv->size; - priv->size = size; /* update private data size */ - elem->data = priv; - - array = (struct snd_soc_tplg_vendor_array *)(priv->data + off); - memset(array, 0, set_size); - array->size = set_size; - array->type = tuple_set->type; - array->num_elems = tuple_set->num_tuples; - - /* fill the private data buffer */ - for (j = 0; j < tuple_set->num_tuples; j++) { - tuple = &tuple_set->tuple[j]; - token_val = get_token_value(tuple->token, tokens); - if (token_val < 0) - return -EINVAL; + off = priv->size; + priv->size = size; /* update private data size */ + elem->data = priv; - switch (tuple_set->type) { - case SND_SOC_TPLG_TUPLE_TYPE_UUID: - uuid = &array->uuid[j]; - uuid->token = token_val; - memcpy(uuid->uuid, tuple->uuid, 16); - break; - - case SND_SOC_TPLG_TUPLE_TYPE_STRING: - string = &array->string[j]; - string->token = token_val; - snd_strlcpy(string->string, tuple->string, - SNDRV_CTL_ELEM_ID_NAME_MAXLEN); - break; - - default: - value = &array->value[j]; - value->token = token_val; - value->value = tuple->value; - break; - } + array = (struct snd_soc_tplg_vendor_array *)(priv->data + off); + memset(array, 0, set_size); + array->size = set_size; + array->type = tuple_set->type; + array->num_elems = tuple_set->num_tuples; + + /* fill the private data buffer */ + for (j = 0; j < tuple_set->num_tuples; j++) { + tuple = &tuple_set->tuple[j]; + token_val = get_token_value(tuple->token, tokens); + if (token_val < 0) + return -EINVAL; + + switch (tuple_set->type) { + case SND_SOC_TPLG_TUPLE_TYPE_UUID: + uuid = &array->uuid[j]; + uuid->token = token_val; + memcpy(uuid->uuid, tuple->uuid, 16); + break; + + case SND_SOC_TPLG_TUPLE_TYPE_STRING: + string = &array->string[j]; + string->token = token_val; + snd_strlcpy(string->string, tuple->string, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + break; + + default: + value = &array->value[j]; + value->token = token_val; + value->value = tuple->value; + break; } } return 0; } +/* Add a tuples object to the private buffer of its parent data element */ +static int copy_tuples(struct tplg_elem *elem, + struct tplg_vendor_tuples *tuples, + struct tplg_vendor_tokens *tokens) +{ + struct snd_soc_tplg_private *priv = elem->data; + unsigned int i; + int size, ret; + + /* scan each tuples set (one set per type) */ + for (i = 0; i < tuples->num_sets ; i++) { + size = priv ? priv->size : 0; /* original private data size */ + ret = scan_tuple_set(elem, tuples->set[i], tokens, size); + if (ret < 0) + return ret; + priv = elem->data; + } + + return 0; +} + /* build a data element from its tuples */ static int build_tuples(snd_tplg_t *tplg, struct tplg_elem *elem) { @@ -756,7 +765,7 @@ static struct tuple_type tuple_types[] = { }, }; -static int get_tuple_type(const char *name) +int get_tuple_type(const char *name) { struct tuple_type *t; unsigned int i; diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h index 13d97317e..8d3038ab7 100644 --- a/src/topology/tplg_local.h +++ b/src/topology/tplg_local.h @@ -473,3 +473,10 @@ int tplg_parse_control_bytes_param(snd_tplg_t *tplg, snd_config_t *n, struct tplg_elem *elem); int parse_access_values(snd_config_t *cfg, struct snd_soc_tplg_ctl_hdr *hdr); int tplg_parse_tlv_dbscale_param(snd_config_t *n, struct snd_soc_tplg_tlv_dbscale *scale); +int scan_tuple_set(struct tplg_elem *elem, struct tplg_tuple_set *tuple_set, + struct tplg_vendor_tokens *tokens, int size); +int get_uuid(const char *str, unsigned char *uuid_le); +int get_tuple_type(const char *name); +int get_token_value(const char *token_id, struct tplg_vendor_tokens *tokens); +struct tplg_elem *tplg_elem_new_route(snd_tplg_t *tplg, int index); +int tplg_parse_data_hex(snd_config_t *cfg, struct tplg_elem *elem, int width); From a3543e61ed6efaf733a8814280fcb5a46387382c Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Sat, 20 Mar 2021 23:30:54 -0700 Subject: [PATCH 07/33] topology: dapm: expose function to parse widget params Split the widget param parser and expose the function to be reuse for Topology2.0. Signed-off-by: Ranjani Sridharan --- src/topology/dapm.c | 250 ++++++++++++++++++++------------------ src/topology/tplg_local.h | 2 + 2 files changed, 133 insertions(+), 119 deletions(-) diff --git a/src/topology/dapm.c b/src/topology/dapm.c index 73d27fcbd..31f1374b5 100644 --- a/src/topology/dapm.c +++ b/src/topology/dapm.c @@ -507,161 +507,173 @@ int tplg_save_dapm_graph(snd_tplg_t *tplg, int index, return err; } -/* DAPM Widget */ -int tplg_parse_dapm_widget(snd_tplg_t *tplg, - snd_config_t *cfg, void *private ATTRIBUTE_UNUSED) +int tplg_parse_dapm_widget_param(snd_config_t *n, struct snd_soc_tplg_dapm_widget *widget, + struct tplg_elem *elem) { - struct snd_soc_tplg_dapm_widget *widget; - struct tplg_elem *elem; - snd_config_iterator_t i, next; - snd_config_t *n; const char *id, *val = NULL; int widget_type, err, ival; - elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_DAPM_WIDGET); - if (!elem) - return -ENOMEM; + if (snd_config_get_id(n, &id) < 0) + return 0; - tplg_dbg(" Widget: %s", elem->id); + /* skip comments */ + if (strcmp(id, "comment") == 0) + return 0; + if (id[0] == '#') + return 0; - widget = elem->widget; - snd_strlcpy(widget->name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); - widget->size = elem->size; + if (strcmp(id, "type") == 0) { + if (snd_config_get_string(n, &val) < 0) + return -EINVAL; - snd_config_for_each(i, next, cfg) { + widget_type = lookup_widget(val); + if (widget_type < 0){ + SNDERR("widget '%s': Unsupported widget type %s", + elem->id, val); + return -EINVAL; + } - n = snd_config_iterator_entry(i); - if (snd_config_get_id(n, &id) < 0) - continue; + widget->id = widget_type; + tplg_dbg("\t%s: %s", id, val); + return 0; + } - /* skip comments */ - if (strcmp(id, "comment") == 0) - continue; - if (id[0] == '#') - continue; + if (strcmp(id, "stream_name") == 0) { + if (snd_config_get_string(n, &val) < 0) + return -EINVAL; - if (strcmp(id, "type") == 0) { - if (snd_config_get_string(n, &val) < 0) - return -EINVAL; + snd_strlcpy(widget->sname, val, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + tplg_dbg("\t%s: %s", id, val); + return 0; + } - widget_type = lookup_widget(val); - if (widget_type < 0){ - SNDERR("widget '%s': Unsupported widget type %s", - elem->id, val); - return -EINVAL; - } + if (strcmp(id, "no_pm") == 0) { + ival = snd_config_get_bool(n); + if (ival < 0) + return -EINVAL; - widget->id = widget_type; - tplg_dbg("\t%s: %s", id, val); - continue; - } + widget->reg = ival ? -1 : 0; - if (strcmp(id, "stream_name") == 0) { - if (snd_config_get_string(n, &val) < 0) - return -EINVAL; + tplg_dbg("\t%s: %s", id, val); + return 0; + } - snd_strlcpy(widget->sname, val, - SNDRV_CTL_ELEM_ID_NAME_MAXLEN); - tplg_dbg("\t%s: %s", id, val); - continue; - } + if (strcmp(id, "shift") == 0) { + if (tplg_get_integer(n, &ival, 0)) + return -EINVAL; - if (strcmp(id, "no_pm") == 0) { - ival = snd_config_get_bool(n); - if (ival < 0) - return -EINVAL; + widget->shift = ival; + tplg_dbg("\t%s: %d", id, widget->shift); + return 0; + } - widget->reg = ival ? -1 : 0; + if (strcmp(id, "reg") == 0) { + if (tplg_get_integer(n, &ival, 0)) + return -EINVAL; - tplg_dbg("\t%s: %s", id, val); - continue; - } + widget->reg = ival; + tplg_dbg("\t%s: %d", id, widget->reg); + return 0; + } - if (strcmp(id, "shift") == 0) { - if (tplg_get_integer(n, &ival, 0)) - return -EINVAL; + if (strcmp(id, "invert") == 0) { + ival = snd_config_get_bool(n); + if (ival < 0) + return -EINVAL; - widget->shift = ival; - tplg_dbg("\t%s: %d", id, widget->shift); - continue; - } + widget->invert = ival; + tplg_dbg("\t%s: %d", id, widget->invert); + return 0; + } - if (strcmp(id, "reg") == 0) { - if (tplg_get_integer(n, &ival, 0)) - return -EINVAL; + if (strcmp(id, "subseq") == 0) { + if (tplg_get_integer(n, &ival, 0)) + return -EINVAL; - widget->reg = ival; - tplg_dbg("\t%s: %d", id, widget->reg); - continue; - } + widget->subseq = ival; + tplg_dbg("\t%s: %d", id, widget->subseq); + return 0; + } - if (strcmp(id, "invert") == 0) { - ival = snd_config_get_bool(n); - if (ival < 0) - return -EINVAL; + if (strcmp(id, "event_type") == 0) { + if (tplg_get_integer(n, &ival, 0)) + return -EINVAL; - widget->invert = ival; - tplg_dbg("\t%s: %d", id, widget->invert); - continue; - } + widget->event_type = ival; + tplg_dbg("\t%s: %d", id, widget->event_type); + return 0; + } - if (strcmp(id, "subseq") == 0) { - if (tplg_get_integer(n, &ival, 0)) - return -EINVAL; + if (strcmp(id, "event_flags") == 0) { + if (tplg_get_integer(n, &ival, 0)) + return -EINVAL; - widget->subseq = ival; - tplg_dbg("\t%s: %d", id, widget->subseq); - continue; - } + widget->event_flags = ival; + tplg_dbg("\t%s: %d", id, widget->event_flags); + return 0; + } - if (strcmp(id, "event_type") == 0) { - if (tplg_get_integer(n, &ival, 0)) - return -EINVAL; + if (strcmp(id, "enum") == 0) { + err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_ENUM); + if (err < 0) + return err; - widget->event_type = ival; - tplg_dbg("\t%s: %d", id, widget->event_type); - continue; - } + return 0; + } - if (strcmp(id, "event_flags") == 0) { - if (tplg_get_integer(n, &ival, 0)) - return -EINVAL; + if (strcmp(id, "mixer") == 0) { + err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_MIXER); + if (err < 0) + return err; - widget->event_flags = ival; - tplg_dbg("\t%s: %d", id, widget->event_flags); - continue; - } + return 0; + } - if (strcmp(id, "enum") == 0) { - err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_ENUM); - if (err < 0) - return err; + if (strcmp(id, "bytes") == 0) { + err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_BYTES); + if (err < 0) + return err; - continue; - } + return 0; + } - if (strcmp(id, "mixer") == 0) { - err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_MIXER); - if (err < 0) - return err; + if (strcmp(id, "data") == 0) { + err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA); + if (err < 0) + return err; + return 0; + } - continue; - } + return 0; +} - if (strcmp(id, "bytes") == 0) { - err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_BYTES); - if (err < 0) - return err; +/* DAPM Widget */ +int tplg_parse_dapm_widget(snd_tplg_t *tplg, + snd_config_t *cfg, void *private ATTRIBUTE_UNUSED) +{ + struct snd_soc_tplg_dapm_widget *widget; + struct tplg_elem *elem; + snd_config_iterator_t i, next; + snd_config_t *n; + int ret; - continue; - } + elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_DAPM_WIDGET); + if (!elem) + return -ENOMEM; - if (strcmp(id, "data") == 0) { - err = tplg_parse_refs(n, elem, SND_TPLG_TYPE_DATA); - if (err < 0) - return err; - continue; - } + tplg_dbg(" Widget: %s", elem->id); + + widget = elem->widget; + snd_strlcpy(widget->name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + widget->size = elem->size; + + snd_config_for_each(i, next, cfg) { + + n = snd_config_iterator_entry(i); + ret = tplg_parse_dapm_widget_param(n, widget, elem); + if (ret < 0) + return ret; } return 0; diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h index 8d3038ab7..c6f4deddc 100644 --- a/src/topology/tplg_local.h +++ b/src/topology/tplg_local.h @@ -480,3 +480,5 @@ int get_tuple_type(const char *name); int get_token_value(const char *token_id, struct tplg_vendor_tokens *tokens); struct tplg_elem *tplg_elem_new_route(snd_tplg_t *tplg, int index); int tplg_parse_data_hex(snd_config_t *cfg, struct tplg_elem *elem, int width); +int tplg_parse_dapm_widget_param(snd_config_t *n, struct snd_soc_tplg_dapm_widget *widget, + struct tplg_elem *elem); From 6eab4f6ca01b8254fdc12a787b24658137805be9 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Sun, 22 Nov 2020 12:32:11 -0800 Subject: [PATCH 08/33] topology: modify struct tplg_tuple_set Add 2 new fields to struct tplg_tuple_set. The token_ref field will be used to save the reference to the token set name. And the list elem will be used to add the tuple_sets to a list of tuples sets that will be created in Topology2.0. Signed-off-by: Ranjani Sridharan --- src/topology/tplg_local.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h index c6f4deddc..bd1ea943b 100644 --- a/src/topology/tplg_local.h +++ b/src/topology/tplg_local.h @@ -138,6 +138,8 @@ struct tplg_tuple { struct tplg_tuple_set { unsigned int type; /* uuid, bool, byte, short, word, string*/ unsigned int num_tuples; + char token_ref[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + struct list_head list; /* item in tuple_set_list */ struct tplg_tuple tuple[0]; }; @@ -288,7 +290,6 @@ int tplg_parse_dai(snd_tplg_t *tplg, snd_config_t *cfg, void *priv); int tplg_parse_link(snd_tplg_t *tplg, snd_config_t *cfg, void *priv); int tplg_parse_cc(snd_tplg_t *tplg, snd_config_t *cfg, void *priv); int tplg_parse_hw_config(snd_tplg_t *tplg, snd_config_t *cfg, void *priv); - unsigned int tplg_get_tuple_size(int type); void tplg_free_tuples(void *obj); From db68302058d0fffb694b36baf771e71d49ff872b Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Tue, 9 Mar 2021 16:12:26 -0800 Subject: [PATCH 09/33] topology: replace references to object with element Topology 2.0 will introduce a new element named object. In preparation for that, replave the references to object with element where applicable. Signed-off-by: Ranjani Sridharan --- include/topology.h | 6 +++--- src/topology/ctl.c | 12 ++++++------ src/topology/dapm.c | 8 ++++---- src/topology/data.c | 6 +++--- src/topology/elem.c | 2 +- src/topology/parser.c | 20 ++++++++++---------- src/topology/pcm.c | 18 +++++++++--------- src/topology/tplg_local.h | 18 +++++++++--------- 8 files changed, 45 insertions(+), 45 deletions(-) diff --git a/include/topology.h b/include/topology.h index d1feee4d9..0186c79a4 100644 --- a/include/topology.h +++ b/include/topology.h @@ -1102,12 +1102,12 @@ typedef struct snd_tplg_obj_template { } snd_tplg_obj_template_t; /** - * \brief Register topology template object. + * \brief Register topology template element. * \param tplg Topology instance. - * \param t Template object. + * \param t Template element. * \return Zero on success, otherwise a negative error code */ -int snd_tplg_add_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t); +int snd_tplg_add_element(snd_tplg_t *tplg, snd_tplg_obj_template_t *t); /** * \brief Build all registered topology data into binary file. diff --git a/src/topology/ctl.c b/src/topology/ctl.c index 96a2b66cd..dfd42cf5a 100644 --- a/src/topology/ctl.c +++ b/src/topology/ctl.c @@ -1260,17 +1260,17 @@ int tplg_add_bytes(snd_tplg_t *tplg, struct snd_tplg_bytes_template *bytes_ctl, return 0; } -int tplg_add_mixer_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) +int tplg_add_mixer_element(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) +int tplg_add_enum_element(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) +int tplg_add_bytes_element(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) { return tplg_add_bytes(tplg, t->bytes_ctl, NULL); } @@ -1382,7 +1382,7 @@ int tplg_decode_control_mixer(snd_tplg_t *tplg, err = tplg_decode_control_mixer1(tplg, &heap, &mt, pos, bin, size2); if (err >= 0) { t.mixer = &mt; - err = snd_tplg_add_object(tplg, &t); + err = snd_tplg_add_element(tplg, &t); } tplg_free(&heap); if (err < 0) @@ -1488,7 +1488,7 @@ int tplg_decode_control_enum(snd_tplg_t *tplg, err = tplg_decode_control_enum1(tplg, &heap, &et, pos, ec); if (err >= 0) { t.enum_ctl = &et; - err = snd_tplg_add_object(tplg, &t); + err = snd_tplg_add_element(tplg, &t); } tplg_free(&heap); if (err < 0) @@ -1577,7 +1577,7 @@ int tplg_decode_control_bytes(snd_tplg_t *tplg, return err; t.bytes_ctl = &bt; - err = snd_tplg_add_object(tplg, &t); + err = snd_tplg_add_element(tplg, &t); if (err < 0) return err; diff --git a/src/topology/dapm.c b/src/topology/dapm.c index 31f1374b5..e8ad9dd50 100644 --- a/src/topology/dapm.c +++ b/src/topology/dapm.c @@ -761,7 +761,7 @@ int tplg_add_route(snd_tplg_t *tplg, struct snd_tplg_graph_elem *t, int index) return 0; } -int tplg_add_graph_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) +int tplg_add_graph_element(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) { struct snd_tplg_graph_template *gt = t->graph; int i, ret; @@ -775,7 +775,7 @@ int tplg_add_graph_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) return 0; } -int tplg_add_widget_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) +int tplg_add_widget_element(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; @@ -1022,7 +1022,7 @@ int tplg_decode_dapm_widget(snd_tplg_t *tplg, } t.widget = wt; - err = snd_tplg_add_object(tplg, &t); + err = snd_tplg_add_element(tplg, &t); tplg_free(&heap); if (err < 0) return err; @@ -1073,5 +1073,5 @@ int tplg_decode_dapm_graph(snd_tplg_t *tplg, } t.graph = gt; - return snd_tplg_add_object(tplg, &t); + return snd_tplg_add_element(tplg, &t); } diff --git a/src/topology/data.c b/src/topology/data.c index a93d920fe..9c79710c4 100644 --- a/src/topology/data.c +++ b/src/topology/data.c @@ -663,7 +663,7 @@ int scan_tuple_set(struct tplg_elem *elem, struct tplg_tuple_set *tuple_set, return 0; } -/* Add a tuples object to the private buffer of its parent data element */ +/* Add a tuples to the private buffer of its parent data element */ static int copy_tuples(struct tplg_elem *elem, struct tplg_vendor_tuples *tuples, struct tplg_vendor_tokens *tokens) @@ -717,7 +717,7 @@ static int build_tuples(snd_tplg_t *tplg, struct tplg_elem *elem) return -EINVAL; } - /* a data object can have multiple tuples objects */ + /* a data element can have multiple tuples */ err = copy_tuples(elem, tuples->tuples, tokens->tokens); if (err < 0) return err; @@ -1559,7 +1559,7 @@ int tplg_copy_data(snd_tplg_t *tplg, struct tplg_elem *elem, return 0; } -/* check data objects and build those with tuples */ +/* check data elements and build those with tuples */ int tplg_build_data(snd_tplg_t *tplg) { struct list_head *base, *pos; diff --git a/src/topology/elem.c b/src/topology/elem.c index cbd7f4b63..a6de30696 100644 --- a/src/topology/elem.c +++ b/src/topology/elem.c @@ -305,7 +305,7 @@ void tplg_elem_free(struct tplg_elem *elem) tplg_ref_free_list(&elem->ref_list); - /* free struct snd_tplg_ object, + /* free struct snd_tplg_ element, * the union pointers share the same address */ if (elem->obj) { diff --git a/src/topology/parser.c b/src/topology/parser.c index f34de01bd..7ee977687 100644 --- a/src/topology/parser.c +++ b/src/topology/parser.c @@ -323,29 +323,29 @@ int snd_tplg_build_file(snd_tplg_t *tplg, return snd_tplg_build(tplg, outfile); } -int snd_tplg_add_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) +int snd_tplg_add_element(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); + return tplg_add_mixer_element(tplg, t); case SND_TPLG_TYPE_ENUM: - return tplg_add_enum_object(tplg, t); + return tplg_add_enum_element(tplg, t); case SND_TPLG_TYPE_BYTES: - return tplg_add_bytes_object(tplg, t); + return tplg_add_bytes_element(tplg, t); case SND_TPLG_TYPE_DAPM_WIDGET: - return tplg_add_widget_object(tplg, t); + return tplg_add_widget_element(tplg, t); case SND_TPLG_TYPE_DAPM_GRAPH: - return tplg_add_graph_object(tplg, t); + return tplg_add_graph_element(tplg, t); case SND_TPLG_TYPE_PCM: - return tplg_add_pcm_object(tplg, t); + return tplg_add_pcm_element(tplg, t); case SND_TPLG_TYPE_DAI: - return tplg_add_dai_object(tplg, t); + return tplg_add_dai_element(tplg, t); case SND_TPLG_TYPE_LINK: case SND_TPLG_TYPE_BE: case SND_TPLG_TYPE_CC: - return tplg_add_link_object(tplg, t); + return tplg_add_link_element(tplg, t); default: - SNDERR("invalid object type %d", t->type); + SNDERR("invalid element type %d", t->type); return -EINVAL; }; } diff --git a/src/topology/pcm.c b/src/topology/pcm.c index e8b95260d..7e8185ba6 100644 --- a/src/topology/pcm.c +++ b/src/topology/pcm.c @@ -1758,8 +1758,8 @@ int tplg_save_hw_config(snd_tplg_t *tplg ATTRIBUTE_UNUSED, return err; } -/* copy stream object */ -static void tplg_add_stream_object(struct snd_soc_tplg_stream *strm, +/* copy stream config */ +static void tplg_add_stream_config(struct snd_soc_tplg_stream *strm, struct snd_tplg_stream_template *strm_tpl) { snd_strlcpy(strm->name, strm_tpl->name, @@ -1803,7 +1803,7 @@ static int tplg_add_stream_caps(snd_tplg_t *tplg, } /* Add a PCM element (FE DAI & DAI link) from C API */ -int tplg_add_pcm_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) +int tplg_add_pcm_element(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) { struct snd_tplg_pcm_template *pcm_tpl = t->pcm; struct snd_soc_tplg_private *priv; @@ -1849,7 +1849,7 @@ int tplg_add_pcm_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) pcm->num_streams = pcm_tpl->num_streams; for (i = 0; i < pcm_tpl->num_streams; i++) - tplg_add_stream_object(&pcm->stream[i], &pcm_tpl->stream[i]); + tplg_add_stream_config(&pcm->stream[i], &pcm_tpl->stream[i]); /* private data */ priv = pcm_tpl->priv; @@ -1905,7 +1905,7 @@ static int set_link_hw_config(struct snd_soc_tplg_hw_config *cfg, } /* Add a physical DAI link element from C API */ -int tplg_add_link_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) +int tplg_add_link_element(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) { struct snd_tplg_link_template *link_tpl = t->link; struct snd_soc_tplg_link_config *link; @@ -1939,7 +1939,7 @@ int tplg_add_link_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) return -EINVAL; link->num_streams = link_tpl->num_streams; for (i = 0; i < link->num_streams; i++) - tplg_add_stream_object(&link->stream[i], &link_tpl->stream[i]); + tplg_add_stream_config(&link->stream[i], &link_tpl->stream[i]); /* HW configs */ if (link_tpl->num_hw_configs > SND_SOC_TPLG_HW_CONFIG_MAX) @@ -1965,7 +1965,7 @@ int tplg_add_link_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) return 0; } -int tplg_add_dai_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) +int tplg_add_dai_element(snd_tplg_t *tplg, snd_tplg_obj_template_t *t) { struct snd_tplg_dai_template *dai_tpl = t->dai; struct snd_soc_tplg_dai *dai; @@ -2134,7 +2134,7 @@ int tplg_decode_pcm(snd_tplg_t *tplg, pos += sizeof(*pcm) + pcm->priv.size; t.pcm = pt; - err = snd_tplg_add_object(tplg, &t); + err = snd_tplg_add_element(tplg, &t); if (err < 0) return err; @@ -2295,7 +2295,7 @@ int tplg_decode_link(snd_tplg_t *tplg, pos += sizeof(*link) + link->priv.size; t.link = < - err = snd_tplg_add_object(tplg, &t); + err = snd_tplg_add_element(tplg, &t); if (err < 0) return err; diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h index bd1ea943b..462790952 100644 --- a/src/topology/tplg_local.h +++ b/src/topology/tplg_local.h @@ -159,7 +159,7 @@ struct tplg_elem { int index; enum snd_tplg_type type; - int size; /* total size of this object inc pdata and ref objects */ + int size; /* total size of this element inc pdata and ref objects */ int compound_elem; /* dont write this element as individual elem */ int vendor_type; /* vendor type for private data */ @@ -342,11 +342,11 @@ int tplg_add_data(snd_tplg_t *tplg, struct tplg_elem *parent, const void *bin, size_t size); int tplg_add_data_bytes(snd_tplg_t *tplg, struct tplg_elem *parent, const char *suffix, const void *bin, size_t size); -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); +int tplg_add_mixer_element(snd_tplg_t *tplg, snd_tplg_obj_template_t *t); +int tplg_add_enum_element(snd_tplg_t *tplg, snd_tplg_obj_template_t *t); +int tplg_add_bytes_element(snd_tplg_t *tplg, snd_tplg_obj_template_t *t); +int tplg_add_widget_element(snd_tplg_t *tplg, snd_tplg_obj_template_t *t); +int tplg_add_graph_element(snd_tplg_t *tplg, snd_tplg_obj_template_t *t); int tplg_add_mixer(snd_tplg_t *tplg, struct snd_tplg_mixer_template *mixer, struct tplg_elem **e); @@ -358,9 +358,9 @@ int tplg_add_bytes(snd_tplg_t *tplg, struct snd_tplg_bytes_template *bytes_ctl, int tplg_build_pcms(snd_tplg_t *tplg, unsigned int type); int tplg_build_dais(snd_tplg_t *tplg, unsigned int type); int tplg_build_links(snd_tplg_t *tplg, unsigned int type); -int tplg_add_link_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t); -int tplg_add_pcm_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t); -int tplg_add_dai_object(snd_tplg_t *tplg, snd_tplg_obj_template_t *t); +int tplg_add_link_element(snd_tplg_t *tplg, snd_tplg_obj_template_t *t); +int tplg_add_pcm_element(snd_tplg_t *tplg, snd_tplg_obj_template_t *t); +int tplg_add_dai_element(snd_tplg_t *tplg, snd_tplg_obj_template_t *t); int tplg_nice_value_format(char *dst, size_t dst_size, unsigned int value); From ae7c8c3f70351a9049228dfefe985cfdf34de105 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 11 Mar 2021 17:56:15 -0800 Subject: [PATCH 10/33] topology: Introduce new class keyword Topology today has some common classes that are often reused throughout with slightly altered configurations i.e. widgets (components), pipelines, dais and controls. This PR introduces the high level concept of reusable "class" like definitions that can be used to create topology objects e.g. Class.Component - Class for widgets that can be included within Class.Pipeline. Class definitions typically contain arguments and attributes. It also adds support for creating a new class topology element and saving it to the class_list. This will be used as the template when creating objects of this class type. A class typically consists of arguments and attributes. Arguments and attributes both refer to parameters associated with the class or its object. The difference is that the arguments are also used for naming the object. For ex:, a simple class definition would be Class.Base."data" { DefineArgument."name" { type "string" } DefineArgument."index" {} DefineAttribute."bytes" {} } The argument "name" would be used to build the name of the data object as "data..". When parsing the class definition, the arguments and attributes are stored as part of the attribute_list in struct tplg_class. Ad attribute/argument can have values that can be of type integer, long, double or a string. Signed-off-by: Ranjani Sridharan --- include/topology.h | 1 + src/topology/Makefile.am | 5 +- src/topology/class.c | 187 +++++++++++++++++++++++++++++++++++++ src/topology/elem.c | 12 +++ src/topology/parser.c | 2 + src/topology/tplg2_local.h | 55 +++++++++++ src/topology/tplg_local.h | 2 + 7 files changed, 262 insertions(+), 2 deletions(-) create mode 100644 src/topology/class.c create mode 100644 src/topology/tplg2_local.h diff --git a/include/topology.h b/include/topology.h index 0186c79a4..dc014cb41 100644 --- a/include/topology.h +++ b/include/topology.h @@ -766,6 +766,7 @@ enum snd_tplg_type { SND_TPLG_TYPE_LINK, /*!< Physical DAI link */ SND_TPLG_TYPE_HW_CONFIG, /*!< Link HW config */ SND_TPLG_TYPE_DAI, /*!< Physical DAI */ + SND_TPLG_TYPE_CLASS, /*!< Class */ }; /** Fit for all user cases */ diff --git a/src/topology/Makefile.am b/src/topology/Makefile.am index 9f48891f5..bfec57c3e 100644 --- a/src/topology/Makefile.am +++ b/src/topology/Makefile.am @@ -30,8 +30,9 @@ libatopology_la_SOURCES =\ elem.c \ save.c \ decoder.c \ - log.c + log.c \ + class.c -noinst_HEADERS = tplg_local.h +noinst_HEADERS = tplg_local.h tplg2_local.h AM_CPPFLAGS=-I$(top_srcdir)/include diff --git a/src/topology/class.c b/src/topology/class.c new file mode 100644 index 000000000..76ef2a7cb --- /dev/null +++ b/src/topology/class.c @@ -0,0 +1,187 @@ +/* + Copyright(c) 2020-2021 Intel Corporation + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + Author: Ranjani Sridharan +*/ + +#include "list.h" +#include "local.h" +#include "tplg_local.h" +#include "tplg2_local.h" +#include + +/* Parse class attributes/arguments and add to class attribute_list */ +static int tplg_parse_class_attributes(snd_tplg_t *tplg ATTRIBUTE_UNUSED, snd_config_t *cfg, + struct tplg_class *class, int type) +{ + snd_config_iterator_t i, next; + snd_config_t *n; + const char *id; + int j = 0; + + snd_config_for_each(i, next, cfg) { + struct tplg_attribute *attr; + + attr = calloc(1, sizeof(*attr)); + if (!attr) + return -ENOMEM; + attr->param_type = type; + if (type == TPLG_CLASS_PARAM_TYPE_ARGUMENT) + j++; + + n = snd_config_iterator_entry(i); + + if (snd_config_get_id(n, &id) < 0) + continue; + + /* set attribute name */ + snd_strlcpy(attr->name, id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + + /* add to class attribute list */ + list_add_tail(&attr->list, &class->attribute_list); + } + + if (type == TPLG_CLASS_PARAM_TYPE_ARGUMENT) + class->num_args = j; + + return 0; +} + +/* create topology elem of type SND_TPLG_TYPE_CLASS */ +static struct tplg_elem *tplg_class_elem(snd_tplg_t *tplg, snd_config_t *cfg, int type) +{ + struct tplg_class *class; + struct tplg_elem *elem; + const char *id; + + if (snd_config_get_id(cfg, &id) < 0) + return NULL; + + elem = tplg_elem_new_common(tplg, cfg, NULL, SND_TPLG_TYPE_CLASS); + if (!elem) + return NULL; + + /* init class */ + class = calloc(1, sizeof(*class)); + if (!class) + return NULL; + + class->type = type; + snd_strlcpy(class->name, id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + INIT_LIST_HEAD(&class->attribute_list); + elem->class = class; + + return elem; +} + +static int tplg_define_class(snd_tplg_t *tplg, snd_config_t *cfg, int type) +{ + snd_config_iterator_t i, next; + struct tplg_elem *class_elem; + struct tplg_class *class; + struct tplg_elem *elem; + snd_config_t *n; + const char *id; + int ret; + + if (snd_config_get_id(cfg, &id) < 0) { + SNDERR("Invalid name for class\n"); + return -EINVAL; + } + + /* check if the class exists already */ + class_elem = tplg_elem_lookup(&tplg->class_list, id, + SND_TPLG_TYPE_CLASS, SND_TPLG_INDEX_ALL); + if (class_elem) + return 0; + + /* create class topology elem */ + elem = tplg_class_elem(tplg, cfg, type); + if (!elem) + return -ENOMEM; + + class = elem->class; + + /* Parse the class definition */ + snd_config_for_each(i, next, cfg) { + n = snd_config_iterator_entry(i); + if (snd_config_get_id(n, &id) < 0) + continue; + + /* parse arguments */ + if (!strcmp(id, "DefineArgument")) { + ret = tplg_parse_class_attributes(tplg, n, class, + TPLG_CLASS_PARAM_TYPE_ARGUMENT); + if (ret < 0) { + SNDERR("failed to parse args for class %s\n", class->name); + return ret; + } + + continue; + } + + /* parse attributes */ + if (!strcmp(id, "DefineAttribute")) { + ret = tplg_parse_class_attributes(tplg, n, class, + TPLG_CLASS_PARAM_TYPE_ATTRIBUTE); + if (ret < 0) { + SNDERR("failed to parse attributes for class %s\n", class->name); + return ret; + } + } + + } + + return 0; +} + +int tplg_define_classes(snd_tplg_t *tplg, snd_config_t *cfg, void *priv ATTRIBUTE_UNUSED) +{ + snd_config_iterator_t i, next; + snd_config_t *n; + const char *id; + int ret; + + if (snd_config_get_id(cfg, &id) < 0) + return -EINVAL; + + /* create class */ + snd_config_for_each(i, next, cfg) { + n = snd_config_iterator_entry(i); + if (snd_config_get_id(n, &id) < 0) + continue; + + ret = tplg_define_class(tplg, n, SND_TPLG_CLASS_TYPE_BASE); + if (ret < 0) { + SNDERR("Failed to create class %s\n", id); + return ret; + } + } + + return 0; +} + +void tplg2_free_elem_class(struct tplg_elem *elem) +{ + struct tplg_class *class = elem->class; + struct list_head *pos, *npos; + struct tplg_attribute *attr; + + /* free attributes */ + list_for_each_safe(pos, npos, &class->attribute_list) { + attr = list_entry(pos, struct tplg_attribute, list); + list_del(&attr->list); + free(attr); + } +} diff --git a/src/topology/elem.c b/src/topology/elem.c index a6de30696..86a87b035 100644 --- a/src/topology/elem.c +++ b/src/topology/elem.c @@ -19,6 +19,7 @@ #include "list.h" #include "tplg_local.h" +#include "tplg2_local.h" struct tplg_table tplg_table[] = { { @@ -226,6 +227,14 @@ struct tplg_table tplg_table[] = { .enew = 1, .parse = tplg_parse_hw_config, .save = tplg_save_hw_config, + }, + { + .name = "Class Definition", + .id = "Class", + .loff = offsetof(snd_tplg_t, class_list), + .type = SND_TPLG_TYPE_CLASS, + .enew = 1, + .parse = tplg_define_classes, } }; @@ -303,6 +312,9 @@ void tplg_elem_free(struct tplg_elem *elem) { list_del(&elem->list); + if (elem->type == SND_TPLG_TYPE_CLASS) + tplg2_free_elem_class(elem); + tplg_ref_free_list(&elem->ref_list); /* free struct snd_tplg_ element, diff --git a/src/topology/parser.c b/src/topology/parser.c index 7ee977687..507356f51 100644 --- a/src/topology/parser.c +++ b/src/topology/parser.c @@ -469,6 +469,7 @@ snd_tplg_t *snd_tplg_create(int flags) INIT_LIST_HEAD(&tplg->token_list); INIT_LIST_HEAD(&tplg->tuple_list); INIT_LIST_HEAD(&tplg->hw_cfg_list); + INIT_LIST_HEAD(&tplg->class_list); return tplg; } @@ -483,6 +484,7 @@ void snd_tplg_free(snd_tplg_t *tplg) free(tplg->bin); free(tplg->manifest_pdata); + tplg_elem_free_list(&tplg->class_list); tplg_elem_free_list(&tplg->tlv_list); tplg_elem_free_list(&tplg->widget_list); tplg_elem_free_list(&tplg->pcm_list); diff --git a/src/topology/tplg2_local.h b/src/topology/tplg2_local.h new file mode 100644 index 000000000..6b1ee1570 --- /dev/null +++ b/src/topology/tplg2_local.h @@ -0,0 +1,55 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ +#include +#include +#include + +#include "local.h" +#include "list.h" +#include "bswap.h" +#include "topology.h" + +#include +#include +#include +#include + +enum tplg_class_param_type { + TPLG_CLASS_PARAM_TYPE_ARGUMENT, + TPLG_CLASS_PARAM_TYPE_ATTRIBUTE, +}; + +struct tplg_attribute { + char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + snd_config_type_t type; + enum tplg_class_param_type param_type; + struct list_head list; /* item in class/object attribute list */ + union { + long integer; + long long integer64; + double d; + char string[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + }value; +}; + +/* class types */ +#define SND_TPLG_CLASS_TYPE_BASE 0 + +struct tplg_class { + char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + int num_args; + struct list_head attribute_list; /* list of attributes */ + int type; +}; + +int tplg_define_classes(snd_tplg_t *tplg, snd_config_t *cfg, void *priv); +void tplg2_free_elem_class(struct tplg_elem *elem); diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h index 462790952..196696f8c 100644 --- a/src/topology/tplg_local.h +++ b/src/topology/tplg_local.h @@ -94,6 +94,7 @@ struct snd_tplg { struct list_head pcm_config_list; struct list_head pcm_caps_list; struct list_head hw_cfg_list; + struct list_head class_list; /* type-specific control lists */ struct list_head mixer_list; @@ -185,6 +186,7 @@ struct tplg_elem { struct tplg_vendor_tokens *tokens; struct tplg_vendor_tuples *tuples; struct snd_soc_tplg_manifest *manifest; + struct tplg_class *class; }; /* an element may refer to other elements: From 612200d2e2444863a5dfce9b1ccefc1f7ee1d158 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 11 Mar 2021 18:18:59 -0800 Subject: [PATCH 11/33] topology: class: Add token_ref field Attributes/arguments in a class could refer to tuple values that need to be added to the private data. Add a field called token_ref to struct tplg_attribute stores the name of SectionVendorTokens and the tuple type that will be used to build the tuple value for the attribute. For ex: "sof_tkn_dai.word" refers to the SectionVendorTokens with the name "sof_tkn_dai" and "word" refers to the tuple type. Signed-off-by: Ranjani Sridharan --- src/topology/class.c | 41 +++++++++++++++++++++++++++++++++++++- src/topology/tplg2_local.h | 3 ++- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/topology/class.c b/src/topology/class.c index 76ef2a7cb..29d85eea7 100644 --- a/src/topology/class.c +++ b/src/topology/class.c @@ -21,6 +21,41 @@ #include "tplg2_local.h" #include +static int tplg_parse_class_attribute(snd_tplg_t *tplg ATTRIBUTE_UNUSED, snd_config_t *cfg, + struct tplg_attribute *attr) +{ + snd_config_iterator_t i, next; + snd_config_t *n; + const char *id; + + snd_config_for_each(i, next, cfg) { + n = snd_config_iterator_entry(i); + + if (snd_config_get_id(n, &id) < 0) + continue; + + /* + * Parse token reference for class attributes/arguments. The token_ref field stores the + * name of SectionVendorTokens and type that will be used to build the tuple value for the + * attribute. For ex: "sof_tkn_dai.word" refers to the SectionVendorTokens with the name + * "sof_tkn_dai" and "word" refers to the tuple types. + */ + if (!strcmp(id, "token_ref")) { + const char *s; + + if (snd_config_get_string(n, &s) < 0) { + SNDERR("invalid token_ref for attribute %s\n", attr->name); + return -EINVAL; + } + + snd_strlcpy(attr->token_ref, s, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + continue; + } + } + + return 0; +} + /* Parse class attributes/arguments and add to class attribute_list */ static int tplg_parse_class_attributes(snd_tplg_t *tplg ATTRIBUTE_UNUSED, snd_config_t *cfg, struct tplg_class *class, int type) @@ -28,7 +63,7 @@ static int tplg_parse_class_attributes(snd_tplg_t *tplg ATTRIBUTE_UNUSED, snd_co snd_config_iterator_t i, next; snd_config_t *n; const char *id; - int j = 0; + int ret, j = 0; snd_config_for_each(i, next, cfg) { struct tplg_attribute *attr; @@ -48,6 +83,10 @@ static int tplg_parse_class_attributes(snd_tplg_t *tplg ATTRIBUTE_UNUSED, snd_co /* set attribute name */ snd_strlcpy(attr->name, id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + ret = tplg_parse_class_attribute(tplg, n, attr); + if (ret < 0) + return ret; + /* add to class attribute list */ list_add_tail(&attr->list, &class->attribute_list); } diff --git a/src/topology/tplg2_local.h b/src/topology/tplg2_local.h index 6b1ee1570..89c2cf464 100644 --- a/src/topology/tplg2_local.h +++ b/src/topology/tplg2_local.h @@ -32,7 +32,8 @@ struct tplg_attribute { char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; snd_config_type_t type; enum tplg_class_param_type param_type; - struct list_head list; /* item in class/object attribute list */ + char token_ref[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + struct list_head list; /* item in attribute list */ union { long integer; long long integer64; From eae59fa6c52d4440adef724526b01ed5a5f40f39 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 11 Mar 2021 18:32:59 -0800 Subject: [PATCH 12/33] topology: class: add constraints for attributes One of the main advantages with Topology2.0 is the ability to validate the types and values for the arguments and attributes for a class instance. Add the constraint field for struct tplg_attribute that will be used to parse and save the constraints specified for the attribute/argument in the class definition. Typical constraints include min/max value or a set of valid values allowed for the attribute. The set of valid values can also be translated from a string value to an integer value during topology build. An example for attribute constraint would be: DefineAttribute."direction" { token_ref "sof_tkn_dai.word" constraints { value_ref "sof_tkn_direction" values [ "playback" "capture" ] } } where the value_ref "sof_tkn_direction" would translate the values as follows: SectionVendorTokens."sof_tkn_direction" { playback "0" capture "1" } Signed-off-by: Ranjani Sridharan --- src/topology/class.c | 152 ++++++++++++++++++++++++++++++++++++- src/topology/tplg2_local.h | 16 ++++ 2 files changed, 166 insertions(+), 2 deletions(-) diff --git a/src/topology/class.c b/src/topology/class.c index 29d85eea7..3c054c032 100644 --- a/src/topology/class.c +++ b/src/topology/class.c @@ -21,12 +21,143 @@ #include "tplg2_local.h" #include -static int tplg_parse_class_attribute(snd_tplg_t *tplg ATTRIBUTE_UNUSED, snd_config_t *cfg, +/* save valid values for attributes */ +static int tplg_parse_constraint_valid_values(snd_tplg_t *tplg, snd_config_t *cfg, + struct attribute_constraint *c, + char *name) +{ + snd_config_iterator_t i, next; + snd_config_t *n; + int err; + + snd_config_for_each(i, next, cfg) { + struct tplg_attribute_ref *v; + const char *id, *s; + + n = snd_config_iterator_entry(i); + if (snd_config_get_id(n, &id) < 0) { + SNDERR("invalid reference value for '%s'\n", name); + return -EINVAL; + } + + err = snd_config_get_string(n, &s); + if (err < 0) { + SNDERR("Invalid value for '%s'\n", name); + return err; + } + + v = calloc(1, sizeof(*v)); + + /* + * some attributes come with valid string values that translate to integer values + */ + if (c->value_ref) { + struct tplg_elem *token_elem; + + v->string = s; + + /* get reference token elem */ + token_elem = tplg_elem_lookup(&tplg->token_list, + c->value_ref, + SND_TPLG_TYPE_TOKEN, SND_TPLG_INDEX_ALL); + if (!token_elem) { + SNDERR("No valid token elem for ref '%s'\n", + c->value_ref); + free(v); + return -EINVAL; + } + + /* save the value corresponding to the string */ + v->value = get_token_value(s, token_elem->tokens); + } else { + /* others just have valid string values */ + v->string = s; + v->value = -EINVAL; + } + + list_add(&v->list, &c->value_list); + } + + return 0; +} + +/* + * Attributes can be associated with constraints such as min, max values. + * Some attributes could also have pre-defined valid values. + * The pre-defined values are human-readable values that sometimes need to be translated + * to tuple values for provate data. For ex: the value "playback" and "capture" for + * direction attributes need to be translated to 0 and 1 respectively for a DAI widget + */ +static int tplg_parse_class_constraints(snd_tplg_t *tplg, snd_config_t *cfg, + struct attribute_constraint *c, char *name) +{ + snd_config_iterator_t i, next; + snd_config_t *n; + int err; + + snd_config_for_each(i, next, cfg) { + const char *id, *s; + long v; + + n = snd_config_iterator_entry(i); + + if (snd_config_get_id(n, &id) < 0) + continue; + + /* set min value constraint */ + if (!strcmp(id, "min")) { + err = snd_config_get_integer(n, &v); + if (err < 0) { + SNDERR("Invalid min constraint for %s\n", name); + return err; + } + c->min = v; + continue; + } + + /* set max value constraint */ + if (!strcmp(id, "max")) { + err = snd_config_get_integer(n, &v); + if (err < 0) { + SNDERR("Invalid min constraint for %s\n", name); + return err; + } + c->max = v; + continue; + } + + /* parse reference for string values that need to be translated to tuple values */ + if (!strcmp(id, "value_ref")) { + err = snd_config_get_string(n, &s); + if (err < 0) { + SNDERR("Invalid value ref for %s\n", name); + return err; + } + c->value_ref = s; + continue; + } + + /* parse the list of valid values */ + if (!strcmp(id, "values")) { + err = tplg_parse_constraint_valid_values(tplg, n, c, name); + if (err < 0) { + SNDERR("Error parsing valid values for %s\n", name); + return err; + } + continue; + } + } + + return 0; +} + +static int tplg_parse_class_attribute(snd_tplg_t *tplg, snd_config_t *cfg, struct tplg_attribute *attr) { snd_config_iterator_t i, next; snd_config_t *n; const char *id; + int ret; snd_config_for_each(i, next, cfg) { n = snd_config_iterator_entry(i); @@ -34,6 +165,17 @@ static int tplg_parse_class_attribute(snd_tplg_t *tplg ATTRIBUTE_UNUSED, snd_con if (snd_config_get_id(n, &id) < 0) continue; + /* Parse class attribute constraints */ + if (!strcmp(id, "constraints")) { + ret = tplg_parse_class_constraints(tplg, n, &attr->constraint, + attr->name); + if (ret < 0) { + SNDERR("Error parsing constraints for %s\n", attr->name); + return -EINVAL; + } + continue; + } + /* * Parse token reference for class attributes/arguments. The token_ref field stores the * name of SectionVendorTokens and type that will be used to build the tuple value for the @@ -56,8 +198,9 @@ static int tplg_parse_class_attribute(snd_tplg_t *tplg ATTRIBUTE_UNUSED, snd_con return 0; } + /* Parse class attributes/arguments and add to class attribute_list */ -static int tplg_parse_class_attributes(snd_tplg_t *tplg ATTRIBUTE_UNUSED, snd_config_t *cfg, +static int tplg_parse_class_attributes(snd_tplg_t *tplg, snd_config_t *cfg, struct tplg_class *class, int type) { snd_config_iterator_t i, next; @@ -75,6 +218,11 @@ static int tplg_parse_class_attributes(snd_tplg_t *tplg ATTRIBUTE_UNUSED, snd_co if (type == TPLG_CLASS_PARAM_TYPE_ARGUMENT) j++; + /* init attribute */ + INIT_LIST_HEAD(&attr->constraint.value_list); + attr->constraint.min = INT_MIN; + attr->constraint.max = INT_MAX; + n = snd_config_iterator_entry(i); if (snd_config_get_id(n, &id) < 0) diff --git a/src/topology/tplg2_local.h b/src/topology/tplg2_local.h index 89c2cf464..49311b8d0 100644 --- a/src/topology/tplg2_local.h +++ b/src/topology/tplg2_local.h @@ -23,6 +23,20 @@ #include #include +struct tplg_attribute_ref { + const char *string; + int value; + struct list_head list; /* item in attribute constraint value_list */ +}; + +struct attribute_constraint { + struct list_head value_list; /* list of valid values */ + const char *value_ref; + int mask; + int min; + int max; +}; + enum tplg_class_param_type { TPLG_CLASS_PARAM_TYPE_ARGUMENT, TPLG_CLASS_PARAM_TYPE_ATTRIBUTE, @@ -33,6 +47,8 @@ struct tplg_attribute { snd_config_type_t type; enum tplg_class_param_type param_type; char token_ref[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + char value_ref[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + struct attribute_constraint constraint; struct list_head list; /* item in attribute list */ union { long integer; From 2c818c8960583236e24196bd9d8233d63872dd52 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 11 Mar 2021 18:38:55 -0800 Subject: [PATCH 13/33] topology: class: parse default value Class definitions may also provide default values for some attributes. Parse and save these values. Attributes/arguments that have constraints are validated against the set constraints. The following example shows a class definition with default attribute values: Class.Base."hw_config" { # # Argument used to construct hw config (hw config ID) # DefineArgument."id" {} DefineAttribute."format" { constraints { values [ "I2S" "DSP_A" "DSP_B" ] } } DefineAttribute."mclk" {} DefineAttribute."mclk_freq" {} DefineAttribute."bclk" {} DefineAttribute."bclk_freq" {} DefineAttribute."fsync" {} DefineAttribute."fsync_freq" {} DefineAttribute."tdm_slots" {} DefineAttribute."tdm_slot_width" {} DefineAttribute."tx_slots" {} DefineAttribute."rx_slots" {} format "I2S" mclk "codec_mclk_in" bclk "codec_consumer" fsync "codec_consumer" fsync_freq 48000 tdm_slots 2 tx_slots 3 rx_slots 3 } Signed-off-by: Ranjani Sridharan --- src/topology/class.c | 191 +++++++++++++++++++++++++++++++++++++ src/topology/tplg2_local.h | 2 + 2 files changed, 193 insertions(+) diff --git a/src/topology/class.c b/src/topology/class.c index 3c054c032..756e4a4c2 100644 --- a/src/topology/class.c +++ b/src/topology/class.c @@ -151,6 +151,190 @@ static int tplg_parse_class_constraints(snd_tplg_t *tplg, snd_config_t *cfg, return 0; } +/* + * Validate attributes that can have an array of values. Note that the array of values + * is not parsed here and should be handled by the compiler when the object containing + * this attribute is parsed. + */ +static int tplg_parse_attribute_compound_value(snd_config_t *cfg, struct tplg_attribute *attr) +{ + snd_config_iterator_t i, next; + struct list_head *pos; + snd_config_t *n; + + /* every value in the array must be valid */ + snd_config_for_each(i, next, cfg) { + const char *id, *s; + bool found = false; + + n = snd_config_iterator_entry(i); + if (snd_config_get_id(n, &id) < 0) { + SNDERR("invalid cfg id for attribute %s\n", attr->name); + return -EINVAL; + } + + if (snd_config_get_string(n, &s) < 0) { + SNDERR("invalid string for attribute %s\n", attr->name); + return -EINVAL; + } + + if (list_empty(&attr->constraint.value_list)) + continue; + + list_for_each(pos, &attr->constraint.value_list) { + struct tplg_attribute_ref *v; + + v = list_entry(pos, struct tplg_attribute_ref, list); + if (!strcmp(s, v->string)) { + found = true; + break; + } + } + + if (!found) { + SNDERR("Invalid value %s for attribute %s\n", s, attr->name); + return -EINVAL; + } + } + + return 0; +} + +/* + * Parse attribute values and set the attribute's type field. Attributes/arguments with + * constraints are validated against them before saving the value. + */ +int tplg_parse_attribute_value(snd_config_t *cfg, struct list_head *list) +{ + snd_config_type_t type = snd_config_get_type(cfg); + struct tplg_attribute *attr = NULL; + struct list_head *pos; + bool found = false; + int err; + const char *id; + + if (snd_config_get_id(cfg, &id) < 0) { + SNDERR("No name for attribute\n"); + return -EINVAL; + } + + /* ignore non-existent attributes */ + list_for_each(pos, list) { + attr = list_entry(pos, struct tplg_attribute, list); + + if (!strcmp(attr->name, id)) { + found = true; + break; + } + } + + if (!found) + return 0; + + attr->cfg = cfg; + + /* parse value */ + switch (type) { + case SND_CONFIG_TYPE_INTEGER: + { + long v; + + err = snd_config_get_integer(cfg, &v); + assert(err >= 0); + + if (v < attr->constraint.min || v > attr->constraint.max) { + SNDERR("Value %d out of range for attribute %s\n", v, attr->name); + return -EINVAL; + } + attr->value.integer = v; + break; + } + case SND_CONFIG_TYPE_INTEGER64: + { + long long v; + + err = snd_config_get_integer64(cfg, &v); + assert(err >= 0); + if (v < attr->constraint.min || v > attr->constraint.max) { + SNDERR("Value %ld out of range for attribute %s\n", v, attr->name); + return -EINVAL; + } + + attr->value.integer64 = v; + break; + } + case SND_CONFIG_TYPE_STRING: + { + struct list_head *pos; + const char *s; + + err = snd_config_get_string(cfg, &s); + assert(err >= 0); + + /* attributes with no pre-defined valid values */ + if (list_empty(&attr->constraint.value_list)) { + + /* change bool string to integer value */ + if (!strcmp(s, "true")) { + attr->value.integer = 1; + attr->type = SND_CONFIG_TYPE_INTEGER; + attr->found = true; + return 0; + } else if (!strcmp(s, "false")) { + attr->value.integer = 0; + attr->type = SND_CONFIG_TYPE_INTEGER; + attr->found = true; + return 0; + } + + snd_strlcpy(attr->value.string, s, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + break; + } + + /* Check if value is in the accepted valid values list */ + list_for_each(pos, &attr->constraint.value_list) { + struct tplg_attribute_ref *v; + + v = list_entry(pos, struct tplg_attribute_ref, list); + + if (!strcmp(s, v->string)) { + snd_strlcpy(attr->value.string, v->string, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + attr->type = type; + attr->found = true; + return 0; + } + } + + SNDERR("Invalid value %s for attribute %s\n", s, attr->name); + return -EINVAL; + } + case SND_CONFIG_TYPE_REAL: + { + double d; + + err = snd_config_get_real(cfg, &d); + assert(err >= 0); + attr->value.d = d; + break; + } + case SND_CONFIG_TYPE_COMPOUND: + /* for attributes that have an array of values */ + err = tplg_parse_attribute_compound_value(cfg, attr); + if (err < 0) + return err; + break; + default: + SNDERR("Unsupported type %d for attribute %s\n", type, attr->name); + return -EINVAL; + } + + attr->type = type; + attr->found = true; + + return 0; +} + static int tplg_parse_class_attribute(snd_tplg_t *tplg, snd_config_t *cfg, struct tplg_attribute *attr) { @@ -326,8 +510,15 @@ static int tplg_define_class(snd_tplg_t *tplg, snd_config_t *cfg, int type) SNDERR("failed to parse attributes for class %s\n", class->name); return ret; } + continue; } + /* class definitions come with default attribute values, process them too */ + ret = tplg_parse_attribute_value(n, &class->attribute_list); + if (ret < 0) { + SNDERR("failed to parse attribute value for class %s\n", class->name); + return -EINVAL; + } } return 0; diff --git a/src/topology/tplg2_local.h b/src/topology/tplg2_local.h index 49311b8d0..edbe6440e 100644 --- a/src/topology/tplg2_local.h +++ b/src/topology/tplg2_local.h @@ -48,6 +48,8 @@ struct tplg_attribute { enum tplg_class_param_type param_type; char token_ref[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; char value_ref[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + bool found; + snd_config_t *cfg; struct attribute_constraint constraint; struct list_head list; /* item in attribute list */ union { From 33c54b73bbf1a7938ec283701f38b0f30e9cafdb Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 11 Mar 2021 18:44:48 -0800 Subject: [PATCH 14/33] topology: class: categorize attributes Another type of constraint the attributes can have is whether their value is immutable (fixed in class definition), mandatory, automatic or deprecated. Class definitions categorize attributes by specifying the attribute name in the attributes.mandatory/immutable/deprecated array. An example for this is: attributes { mandatory [ "no_pm" "uuid" "widget_type" ] immutable [ "uuid" ] automatic [ "stream_name" ] deprecated [ "preload_count" ] } Signed-off-by: Ranjani Sridharan --- src/topology/class.c | 134 ++++++++++++++++++++++++++++++++++++- src/topology/tplg2_local.h | 6 ++ 2 files changed, 139 insertions(+), 1 deletion(-) diff --git a/src/topology/class.c b/src/topology/class.c index 756e4a4c2..dc8c778b5 100644 --- a/src/topology/class.c +++ b/src/topology/class.c @@ -151,6 +151,26 @@ static int tplg_parse_class_constraints(snd_tplg_t *tplg, snd_config_t *cfg, return 0; } +/* check if mandatory and immutable attributes have been provided a value */ +static bool tplg_class_attribute_sanity_check(struct tplg_class *class) +{ + struct list_head *pos; + + list_for_each(pos, &class->attribute_list) { + struct tplg_attribute *attr = list_entry(pos, struct tplg_attribute, list); + + /* immutable attributes must be provided a value in the class definition */ + if ((attr->constraint.mask & TPLG_CLASS_ATTRIBUTE_MASK_IMMUTABLE) && + !attr->found) { + SNDERR("Missing value for mmutable attribute '%s'in class '%s'", + attr->name, class->name); + return false; + } + } + + return true; +} + /* * Validate attributes that can have an array of values. Note that the array of values * is not parsed here and should be handled by the compiler when the object containing @@ -200,6 +220,107 @@ static int tplg_parse_attribute_compound_value(snd_config_t *cfg, struct tplg_at return 0; } +/* helper function to get an attribute by name */ +static struct tplg_attribute *tplg_get_attribute_by_name(struct list_head *list, const char *name) +{ + struct list_head *pos; + + list_for_each(pos, list) { + struct tplg_attribute *attr = list_entry(pos, struct tplg_attribute, list); + + if (!strcmp(attr->name, name)) + return attr; + } + + return NULL; +} + +/* apply the category mask to the attribute constraint */ +static int tplg_parse_class_attribute_category(snd_config_t *cfg, struct tplg_class *class, + int category) +{ + snd_config_iterator_t i, next; + snd_config_t *n; + + snd_config_for_each(i, next, cfg) { + struct tplg_attribute *attr; + const char *id; + + n = snd_config_iterator_entry(i); + if (snd_config_get_string(n, &id) < 0) { + SNDERR("invalid attribute category name for class %s\n", class->name); + return -EINVAL; + } + + attr = tplg_get_attribute_by_name(&class->attribute_list, id); + if (!attr) + continue; + + attr->constraint.mask |= category; + } + + return 0; +} + +/* + * At the end of class attribute definitions, there could be section categorizing attributes + * as mandatory, immutable or deprecated etc. Parse these and apply them to the attribute + * constraint. + */ +static int tplg_parse_class_attribute_categories(snd_config_t *cfg, struct tplg_class *class) +{ + snd_config_iterator_t i, next; + snd_config_t *n; + int category = 0; + int ret; + + snd_config_for_each(i, next, cfg) { + const char *id; + + n = snd_config_iterator_entry(i); + if (snd_config_get_id(n, &id) < 0) { + SNDERR("invalid attribute category for class %s\n", class->name); + return -EINVAL; + } + + if (!strcmp(id, "mandatory")) + category = TPLG_CLASS_ATTRIBUTE_MASK_MANDATORY; + + if (!strcmp(id, "immutable")) + category = TPLG_CLASS_ATTRIBUTE_MASK_IMMUTABLE; + + if (!strcmp(id, "deprecated")) + category = TPLG_CLASS_ATTRIBUTE_MASK_DEPRECATED; + + if (!strcmp(id, "automatic")) + category = TPLG_CLASS_ATTRIBUTE_MASK_AUTOMATIC; + + if (!strcmp(id, "unique")) { + struct tplg_attribute *unique_attr; + const char *s; + int err = snd_config_get_string(n, &s); + assert(err >= 0); + + unique_attr = tplg_get_attribute_by_name(&class->attribute_list, s); + if (!unique_attr) + continue; + + unique_attr->constraint.mask |= TPLG_CLASS_ATTRIBUTE_MASK_UNIQUE; + continue; + } + + if (!category) + continue; + + /* apply the constraint to the attribute */ + ret = tplg_parse_class_attribute_category(n, class, category); + if (ret < 0) + return ret; + } + + return 0; +} + /* * Parse attribute values and set the attribute's type field. Attributes/arguments with * constraints are validated against them before saving the value. @@ -513,6 +634,16 @@ static int tplg_define_class(snd_tplg_t *tplg, snd_config_t *cfg, int type) continue; } + /* parse attribute constraint category and apply the constraint */ + if (!strcmp(id, "attributes")) { + ret = tplg_parse_class_attribute_categories(n, class); + if (ret < 0) { + SNDERR("failed to parse attributes for class %s\n", class->name); + return ret; + } + continue; + } + /* class definitions come with default attribute values, process them too */ ret = tplg_parse_attribute_value(n, &class->attribute_list); if (ret < 0) { @@ -521,7 +652,8 @@ static int tplg_define_class(snd_tplg_t *tplg, snd_config_t *cfg, int type) } } - return 0; + /* ensure immutable attributes have been provided values */ + return tplg_class_attribute_sanity_check(class); } int tplg_define_classes(snd_tplg_t *tplg, snd_config_t *cfg, void *priv ATTRIBUTE_UNUSED) diff --git a/src/topology/tplg2_local.h b/src/topology/tplg2_local.h index edbe6440e..bf3d5d528 100644 --- a/src/topology/tplg2_local.h +++ b/src/topology/tplg2_local.h @@ -23,6 +23,12 @@ #include #include +#define TPLG_CLASS_ATTRIBUTE_MASK_MANDATORY 1 << 1 +#define TPLG_CLASS_ATTRIBUTE_MASK_IMMUTABLE 1 << 2 +#define TPLG_CLASS_ATTRIBUTE_MASK_DEPRECATED 1 << 3 +#define TPLG_CLASS_ATTRIBUTE_MASK_AUTOMATIC 1 << 4 +#define TPLG_CLASS_ATTRIBUTE_MASK_UNIQUE 1 << 5 + struct tplg_attribute_ref { const char *string; int value; From 1de6037d42a84e6ab0fd154e1508e22af642500f Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 11 Mar 2021 19:55:00 -0800 Subject: [PATCH 15/33] topology: Introduce the new object keyword The Object keyword is used to instantiate a class. When an object is instantiated, it must be provided with arguments and mandatory attributes. Example object instantiation: Object.host."N" { direction "playback" period_sink_count 2 period_source_count 0 widget_type "aif_in" } where 'N' is a unique identifier for this object in the parent alsaconf node. For objects that have an index attribute, this value is copied to the index attribute by the compiler. The default attributes values for a class are copied over to the object when the object is created. But these will be overriden when they are explictly set during object instantation as above. Signed-off-by: Ranjani Sridharan --- include/topology.h | 1 + src/topology/Makefile.am | 3 +- src/topology/class.c | 10 +- src/topology/elem.c | 11 ++ src/topology/object.c | 307 +++++++++++++++++++++++++++++++++++++ src/topology/parser.c | 2 + src/topology/tplg2_local.h | 15 ++ src/topology/tplg_local.h | 2 + 8 files changed, 347 insertions(+), 4 deletions(-) create mode 100644 src/topology/object.c diff --git a/include/topology.h b/include/topology.h index dc014cb41..8cc793314 100644 --- a/include/topology.h +++ b/include/topology.h @@ -767,6 +767,7 @@ enum snd_tplg_type { SND_TPLG_TYPE_HW_CONFIG, /*!< Link HW config */ SND_TPLG_TYPE_DAI, /*!< Physical DAI */ SND_TPLG_TYPE_CLASS, /*!< Class */ + SND_TPLG_TYPE_OBJECT, /*!< Object */ }; /** Fit for all user cases */ diff --git a/src/topology/Makefile.am b/src/topology/Makefile.am index bfec57c3e..4c75543cf 100644 --- a/src/topology/Makefile.am +++ b/src/topology/Makefile.am @@ -31,7 +31,8 @@ libatopology_la_SOURCES =\ save.c \ decoder.c \ log.c \ - class.c + class.c \ + object.c noinst_HEADERS = tplg_local.h tplg2_local.h diff --git a/src/topology/class.c b/src/topology/class.c index dc8c778b5..b18bce03c 100644 --- a/src/topology/class.c +++ b/src/topology/class.c @@ -221,7 +221,7 @@ static int tplg_parse_attribute_compound_value(snd_config_t *cfg, struct tplg_at } /* helper function to get an attribute by name */ -static struct tplg_attribute *tplg_get_attribute_by_name(struct list_head *list, const char *name) +struct tplg_attribute *tplg_get_attribute_by_name(struct list_head *list, const char *name) { struct list_head *pos; @@ -325,7 +325,7 @@ static int tplg_parse_class_attribute_categories(snd_config_t *cfg, struct tplg_ * Parse attribute values and set the attribute's type field. Attributes/arguments with * constraints are validated against them before saving the value. */ -int tplg_parse_attribute_value(snd_config_t *cfg, struct list_head *list) +int tplg_parse_attribute_value(snd_config_t *cfg, struct list_head *list, bool override) { snd_config_type_t type = snd_config_get_type(cfg); struct tplg_attribute *attr = NULL; @@ -352,6 +352,10 @@ int tplg_parse_attribute_value(snd_config_t *cfg, struct list_head *list) if (!found) return 0; + /* do not override previously set value */ + if (!override && attr->found) + return 0; + attr->cfg = cfg; /* parse value */ @@ -645,7 +649,7 @@ static int tplg_define_class(snd_tplg_t *tplg, snd_config_t *cfg, int type) } /* class definitions come with default attribute values, process them too */ - ret = tplg_parse_attribute_value(n, &class->attribute_list); + ret = tplg_parse_attribute_value(n, &class->attribute_list, false); if (ret < 0) { SNDERR("failed to parse attribute value for class %s\n", class->name); return -EINVAL; diff --git a/src/topology/elem.c b/src/topology/elem.c index 86a87b035..3b2d4a3d9 100644 --- a/src/topology/elem.c +++ b/src/topology/elem.c @@ -235,6 +235,14 @@ struct tplg_table tplg_table[] = { .type = SND_TPLG_TYPE_CLASS, .enew = 1, .parse = tplg_define_classes, + }, + { + .name = "New Object", + .id = "Object", + .loff = offsetof(snd_tplg_t, object_list), + .type = SND_TPLG_TYPE_OBJECT, + .enew = 1, + .parse = tplg_create_objects, } }; @@ -315,6 +323,9 @@ void tplg_elem_free(struct tplg_elem *elem) if (elem->type == SND_TPLG_TYPE_CLASS) tplg2_free_elem_class(elem); + if (elem->type == SND_TPLG_TYPE_OBJECT) + tplg2_free_elem_object(elem); + tplg_ref_free_list(&elem->ref_list); /* free struct snd_tplg_ element, diff --git a/src/topology/object.c b/src/topology/object.c new file mode 100644 index 000000000..76443cbfa --- /dev/null +++ b/src/topology/object.c @@ -0,0 +1,307 @@ +/* + Copyright(c) 2020-2021 Intel Corporation + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + Author: Ranjani Sridharan +*/ +#include "list.h" +#include "local.h" +#include "tplg_local.h" +#include "tplg2_local.h" +#include + +/* Process the attribute values provided during object instantiation */ +static int tplg_process_attributes(snd_config_t *cfg, struct tplg_object *object) +{ + snd_config_iterator_t i, next; + struct list_head *pos; + snd_config_t *n; + const char *id; + int ret; + + snd_config_for_each(i, next, cfg) { + n = snd_config_iterator_entry(i); + if (snd_config_get_id(n, &id) < 0) + continue; + + list_for_each(pos, &object->attribute_list) { + struct tplg_attribute *attr = list_entry(pos, struct tplg_attribute, list); + + /* copy new value based on type */ + if (!strcmp(id, attr->name)) { + /* cannot update immutable attributes */ + if (attr->constraint.mask & TPLG_CLASS_ATTRIBUTE_MASK_IMMUTABLE) { + SNDERR("Warning: cannot update immutable attribute: %s for object %s\n", + attr->name, object->name); + continue; + } + + ret = tplg_parse_attribute_value(n, &object->attribute_list, true); + if (ret < 0) { + SNDERR("Error parsing attribute %s value: %d\n", + attr->name, ret); + return ret; + } + + attr->found = true; + break; + } + } + } + + return 0; +} + +/* copy the preset attribute values and constraints */ +static int tplg_copy_attribute(struct tplg_attribute *attr, struct tplg_attribute *ref_attr) +{ + struct list_head *pos1; + + snd_strlcpy(attr->name, ref_attr->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + snd_strlcpy(attr->token_ref, ref_attr->token_ref, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + attr->found = ref_attr->found; + attr->param_type = ref_attr->param_type; + attr->cfg = ref_attr->cfg; + attr->type = ref_attr->type; + + /* copy value */ + if (ref_attr->found) { + switch (ref_attr->type) { + case SND_CONFIG_TYPE_INTEGER: + attr->value.integer = ref_attr->value.integer; + break; + case SND_CONFIG_TYPE_INTEGER64: + attr->value.integer64 = ref_attr->value.integer64; + break; + case SND_CONFIG_TYPE_STRING: + snd_strlcpy(attr->value.string, ref_attr->value.string, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + break; + case SND_CONFIG_TYPE_REAL: + { + attr->value.d = ref_attr->value.d; + break; + } + case SND_CONFIG_TYPE_COMPOUND: + break; + default: + SNDERR("Unsupported type %d for attribute %s\n", attr->type, attr->name); + return -EINVAL; + } + } + + /* copy attribute constraints */ + INIT_LIST_HEAD(&attr->constraint.value_list); + attr->constraint.value_ref = ref_attr->constraint.value_ref; + list_for_each(pos1, &ref_attr->constraint.value_list) { + struct tplg_attribute_ref *ref; + struct tplg_attribute_ref *new_ref = calloc(1, sizeof(*new_ref)); + + ref = list_entry(pos1, struct tplg_attribute_ref, list); + memcpy(new_ref, ref, sizeof(*ref)); + list_add(&new_ref->list, &attr->constraint.value_list); + } + attr->constraint.mask = ref_attr->constraint.mask; + attr->constraint.min = INT_MIN; + attr->constraint.max = INT_MAX; + + return 0; +} + +/* find the attribute with the mask TPLG_CLASS_ATTRIBUTE_MASK_UNIQUE and set the value */ +static int tplg_object_set_unique_attribute(struct tplg_object *object, snd_config_t *cfg) +{ + struct tplg_attribute *attr; + struct list_head *pos; + const char *id; + bool found = false; + + list_for_each(pos, &object->attribute_list) { + attr = list_entry(pos, struct tplg_attribute, list); + + if (attr->constraint.mask & TPLG_CLASS_ATTRIBUTE_MASK_UNIQUE) { + found = true; + break; + } + } + + /* no unique attribute for object */ + if (!found) { + SNDERR("No unique attribute set for object %s\n", object->name); + return -EINVAL; + } + + /* no need to check return value as it is already checked in tplg_create_object() */ + snd_config_get_id(cfg, &id); + + if (id[0] >= '0' && id[0] <= '9') { + attr->value.integer = atoi(id); + attr->type = SND_CONFIG_TYPE_INTEGER; + } else { + snd_strlcpy(attr->value.string, id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + attr->type = SND_CONFIG_TYPE_STRING; + } + + attr->found = true; + + return 0; +} + +/* + * Create an object of class "class" by copying the attribute list, number of arguments + * and default attribute values from the class definition. Objects can also be given + * new values during instantiation and these will override the default values set in + * the class definition + */ +struct tplg_object * +tplg_create_object(snd_tplg_t *tplg, snd_config_t *cfg, struct tplg_class *class) +{ + struct tplg_object *object; + struct tplg_elem *elem; + struct list_head *pos; + const char *id; + char object_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + int ret; + + if (!class) { + SNDERR("Invalid class elem for object\n"); + return NULL; + } + + /* get object index */ + if (snd_config_get_id(cfg, &id) < 0) + return NULL; + + ret = snprintf(object_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s.%s", class->name, id); + if (ret > SNDRV_CTL_ELEM_ID_NAME_MAXLEN) + SNDERR("Warning: object name %s truncated to %d characters\n", object_name, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + + /* create and initialize object type element */ + elem = tplg_elem_new_common(tplg, NULL, object_name, SND_TPLG_TYPE_OBJECT); + if (!elem) { + SNDERR("Failed to create tplg elem for %s\n", object_name); + return NULL; + } + + object = calloc(1, sizeof(*object)); + if (!object) { + return NULL; + } + + object->cfg = cfg; + object->elem = elem; + object->num_args = class->num_args; + snd_strlcpy(object->name, object_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + snd_strlcpy(object->class_name, class->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + object->type = class->type; + INIT_LIST_HEAD(&object->attribute_list); + elem->object = object; + + /* copy attributes from class */ + list_for_each(pos, &class->attribute_list) { + struct tplg_attribute *attr = list_entry(pos, struct tplg_attribute, list); + struct tplg_attribute *new_attr = calloc(1, sizeof(*attr)); + + if (!new_attr) + return NULL; + + ret = tplg_copy_attribute(new_attr, attr); + if (ret < 0) { + SNDERR("Error copying attribute %s\n", attr->name); + free(new_attr); + return NULL; + } + list_add_tail(&new_attr->list, &object->attribute_list); + } + + /* set unique attribute for object */ + ret = tplg_object_set_unique_attribute(object, cfg); + if (ret < 0) + return NULL; + + /* process object attribute values */ + ret = tplg_process_attributes(cfg, object); + if (ret < 0) { + SNDERR("Failed to process attributes for %s\n", object->name); + return NULL; + } + + return object; +} + +int tplg_create_new_object(snd_tplg_t *tplg, snd_config_t *cfg, struct tplg_elem *class_elem) +{ + snd_config_iterator_t i, next; + struct tplg_class *class = class_elem->class; + struct tplg_object *object; + snd_config_t *n; + const char *id; + + /* create all objects of the same class type */ + snd_config_for_each(i, next, cfg) { + + n = snd_config_iterator_entry(i); + if (snd_config_get_id(n, &id) < 0) + continue; + + /* + * Create object by duplicating the attributes and child objects from the class + * definiion + */ + object = tplg_create_object(tplg, n, class_elem->class); + if (!object) { + SNDERR("Error creating object for class %s\n", class->name); + return -EINVAL; + } + } + + return 0; +} + +/* create top-level topology objects */ +int tplg_create_objects(snd_tplg_t *tplg, snd_config_t *cfg, void *private ATTRIBUTE_UNUSED) +{ + struct tplg_elem *class_elem; + const char *id; + + if (snd_config_get_id(cfg, &id) < 0) + return -EINVAL; + + /* look up class elem */ + class_elem = tplg_elem_lookup(&tplg->class_list, id, + SND_TPLG_TYPE_CLASS, SND_TPLG_INDEX_ALL); + if (!class_elem) { + SNDERR("No class elem found for %s\n", id); + return -EINVAL; + } + + return tplg_create_new_object(tplg, cfg, class_elem); +} + +void tplg2_free_elem_object(struct tplg_elem *elem) +{ + struct tplg_object *object = elem->object; + struct list_head *pos, *npos; + struct tplg_attribute *attr; + + /* + * free args, attributes and tuples. Child objects will be freed when + * tplg->object_list is freed + */ + list_for_each_safe(pos, npos, &object->attribute_list) { + attr = list_entry(pos, struct tplg_attribute, list); + list_del(&attr->list); + free(attr); + } +} diff --git a/src/topology/parser.c b/src/topology/parser.c index 507356f51..9732ef588 100644 --- a/src/topology/parser.c +++ b/src/topology/parser.c @@ -470,6 +470,7 @@ snd_tplg_t *snd_tplg_create(int flags) INIT_LIST_HEAD(&tplg->tuple_list); INIT_LIST_HEAD(&tplg->hw_cfg_list); INIT_LIST_HEAD(&tplg->class_list); + INIT_LIST_HEAD(&tplg->object_list); return tplg; } @@ -484,6 +485,7 @@ void snd_tplg_free(snd_tplg_t *tplg) free(tplg->bin); free(tplg->manifest_pdata); + tplg_elem_free_list(&tplg->object_list); tplg_elem_free_list(&tplg->class_list); tplg_elem_free_list(&tplg->tlv_list); tplg_elem_free_list(&tplg->widget_list); diff --git a/src/topology/tplg2_local.h b/src/topology/tplg2_local.h index bf3d5d528..84aa1f802 100644 --- a/src/topology/tplg2_local.h +++ b/src/topology/tplg2_local.h @@ -66,6 +66,17 @@ struct tplg_attribute { }value; }; +struct tplg_object { + char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + char class_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + int num_args; + struct list_head attribute_list; + struct tplg_elem *elem; + snd_config_t *cfg; + int type; + struct list_head list; /* item in parent object list */ +}; + /* class types */ #define SND_TPLG_CLASS_TYPE_BASE 0 @@ -78,3 +89,7 @@ struct tplg_class { int tplg_define_classes(snd_tplg_t *tplg, snd_config_t *cfg, void *priv); void tplg2_free_elem_class(struct tplg_elem *elem); +int tplg_create_objects(snd_tplg_t *tplg, snd_config_t *cfg, void *priv); +int tplg_parse_attribute_value(snd_config_t *cfg, struct list_head *list, bool override); +struct tplg_attribute *tplg_get_attribute_by_name(struct list_head *list, const char *name); +void tplg2_free_elem_object(struct tplg_elem *elem); diff --git a/src/topology/tplg_local.h b/src/topology/tplg_local.h index 196696f8c..fabd3b0bc 100644 --- a/src/topology/tplg_local.h +++ b/src/topology/tplg_local.h @@ -95,6 +95,7 @@ struct snd_tplg { struct list_head pcm_caps_list; struct list_head hw_cfg_list; struct list_head class_list; + struct list_head object_list; /* type-specific control lists */ struct list_head mixer_list; @@ -187,6 +188,7 @@ struct tplg_elem { struct tplg_vendor_tuples *tuples; struct snd_soc_tplg_manifest *manifest; struct tplg_class *class; + struct tplg_object *object; }; /* an element may refer to other elements: From d09eda006c65e181d642122443a9797b5c4e8d50 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 11 Mar 2021 21:18:14 -0800 Subject: [PATCH 16/33] topology: class: parse child objects Apart from attributes, arguments and constraints, a class definition could also include child objects. Parse these child objects and save them in the class' object_list. An example of this would be a PGA widget class with 2 mixer controls as follows: Class.Component."pga" { /* PGA arguments and attribute definitions (not shown) */ Object.mixer."0" { name "Master Playback Volume" } Object.mixer."1" { name "Mute Switch" } /* PGA default values(not shown) */ } Signed-off-by: Ranjani Sridharan --- src/topology/class.c | 69 ++++++++++++++++++++++++++++++++++++++ src/topology/object.c | 8 +++-- src/topology/tplg2_local.h | 4 +++ 3 files changed, 79 insertions(+), 2 deletions(-) diff --git a/src/topology/class.c b/src/topology/class.c index b18bce03c..2c6eb23b3 100644 --- a/src/topology/class.c +++ b/src/topology/class.c @@ -576,11 +576,71 @@ static struct tplg_elem *tplg_class_elem(snd_tplg_t *tplg, snd_config_t *cfg, in class->type = type; snd_strlcpy(class->name, id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); INIT_LIST_HEAD(&class->attribute_list); + INIT_LIST_HEAD(&class->object_list); elem->class = class; return elem; } +static int tplg_create_class_object(snd_tplg_t *tplg, snd_config_t *cfg, struct list_head *list, + struct tplg_elem *class_elem) +{ + snd_config_iterator_t i, next; + struct tplg_object *object; + snd_config_t *n; + const char *id; + + snd_config_for_each(i, next, cfg) { + + n = snd_config_iterator_entry(i); + if (snd_config_get_id(n, &id) < 0) + continue; + + /* create object */ + object = tplg_create_object(tplg, n, class_elem->class, NULL, list); + if (!object) { + SNDERR("Failed to create object for class %s\n", class_elem->id); + return -EINVAL;; + } + } + + return 0; +} + +/* + * Class definition can have pre-defined objects, for ex: a PGA widget can have a mixer object. + * Parse and create these objects. They will be built when then parent object is instantiated. + */ +static int tplg_create_class_objects(snd_tplg_t *tplg, snd_config_t *cfg, struct list_head *list) +{ + snd_config_iterator_t i, next; + struct tplg_elem *class_elem; + snd_config_t *n; + const char *id; + int ret; + + snd_config_for_each(i, next, cfg) { + + n = snd_config_iterator_entry(i); + if (snd_config_get_id(n, &id) < 0) + continue; + + class_elem = tplg_elem_lookup(&tplg->class_list, id, + SND_TPLG_TYPE_CLASS, SND_TPLG_INDEX_ALL); + if (!class_elem) + continue; + + /* create object */ + ret = tplg_create_class_object(tplg, n, list, class_elem); + if (ret < 0) { + SNDERR("Failed to create object for class %s\n", class_elem->id); + return ret; + } + } + + return 0; +} + static int tplg_define_class(snd_tplg_t *tplg, snd_config_t *cfg, int type) { snd_config_iterator_t i, next; @@ -638,6 +698,15 @@ static int tplg_define_class(snd_tplg_t *tplg, snd_config_t *cfg, int type) continue; } + /* parse objects */ + if (!strcmp(id, "Object")) { + ret = tplg_create_class_objects(tplg, n, &class->object_list); + if (ret < 0) { + SNDERR("Cannot create objects for class %s\n", class->name); + return -EINVAL; + } + } + /* parse attribute constraint category and apply the constraint */ if (!strcmp(id, "attributes")) { ret = tplg_parse_class_attribute_categories(n, class); diff --git a/src/topology/object.c b/src/topology/object.c index 76443cbfa..7c2b1fb55 100644 --- a/src/topology/object.c +++ b/src/topology/object.c @@ -164,7 +164,8 @@ static int tplg_object_set_unique_attribute(struct tplg_object *object, snd_conf * the class definition */ struct tplg_object * -tplg_create_object(snd_tplg_t *tplg, snd_config_t *cfg, struct tplg_class *class) +tplg_create_object(snd_tplg_t *tplg, snd_config_t *cfg, struct tplg_class *class, + struct tplg_object *parent ATTRIBUTE_UNUSED, struct list_head *list) { struct tplg_object *object; struct tplg_elem *elem; @@ -237,6 +238,9 @@ tplg_create_object(snd_tplg_t *tplg, snd_config_t *cfg, struct tplg_class *class return NULL; } + if (list) + list_add_tail(&object->list, list); + return object; } @@ -259,7 +263,7 @@ int tplg_create_new_object(snd_tplg_t *tplg, snd_config_t *cfg, struct tplg_elem * Create object by duplicating the attributes and child objects from the class * definiion */ - object = tplg_create_object(tplg, n, class_elem->class); + object = tplg_create_object(tplg, n, class_elem->class, NULL, NULL); if (!object) { SNDERR("Error creating object for class %s\n", class->name); return -EINVAL; diff --git a/src/topology/tplg2_local.h b/src/topology/tplg2_local.h index 84aa1f802..b373411c9 100644 --- a/src/topology/tplg2_local.h +++ b/src/topology/tplg2_local.h @@ -84,6 +84,7 @@ struct tplg_class { char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; int num_args; struct list_head attribute_list; /* list of attributes */ + struct list_head object_list; /* list of child objects */ int type; }; @@ -91,5 +92,8 @@ int tplg_define_classes(snd_tplg_t *tplg, snd_config_t *cfg, void *priv); void tplg2_free_elem_class(struct tplg_elem *elem); int tplg_create_objects(snd_tplg_t *tplg, snd_config_t *cfg, void *priv); int tplg_parse_attribute_value(snd_config_t *cfg, struct list_head *list, bool override); +struct tplg_object * +tplg_create_object(snd_tplg_t *tplg, snd_config_t *cfg, struct tplg_class *class, + struct tplg_object *parent, struct list_head *list); struct tplg_attribute *tplg_get_attribute_by_name(struct list_head *list, const char *name); void tplg2_free_elem_object(struct tplg_elem *elem); From 4a6d170860dd2fba97f15236705eb008d0b9788f Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 11 Mar 2021 21:32:30 -0800 Subject: [PATCH 17/33] topology: object: copy child objects from class When creating an object, also copy all the child objects that the class definitions may include. This has to be done recursively until all the child objects within objects are copied. Signed-off-by: Ranjani Sridharan --- src/topology/object.c | 91 ++++++++++++++++++++++++++++++++++++++ src/topology/tplg2_local.h | 1 + 2 files changed, 92 insertions(+) diff --git a/src/topology/object.c b/src/topology/object.c index 7c2b1fb55..7677019dd 100644 --- a/src/topology/object.c +++ b/src/topology/object.c @@ -157,6 +157,89 @@ static int tplg_object_set_unique_attribute(struct tplg_object *object, snd_conf return 0; } +/* Copy object from class definition and create the topology element for the newly copied object */ +static int tplg_copy_object(snd_tplg_t *tplg, struct tplg_object *src, struct tplg_object *dest, + struct list_head *list) +{ + struct tplg_elem *elem; + struct list_head *pos; + int ret; + + if (!src || !dest) { + SNDERR("Invalid src/dest object\n"); + return -EINVAL; + } + + dest->num_args = src->num_args; + snd_strlcpy(dest->name, src->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + snd_strlcpy(dest->class_name, src->class_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + dest->type = src->type; + dest->cfg = src->cfg; + INIT_LIST_HEAD(&dest->attribute_list); + INIT_LIST_HEAD(&dest->object_list); + + /* copy attributes */ + list_for_each(pos, &src->attribute_list) { + struct tplg_attribute *attr = list_entry(pos, struct tplg_attribute, list); + struct tplg_attribute *new_attr = calloc(1, sizeof(*attr)); + + if (!new_attr) + return -ENOMEM; + + ret = tplg_copy_attribute(new_attr, attr); + if (ret < 0) { + SNDERR("Error copying attribute %s\n", attr->name); + free(new_attr); + return -ENOMEM; + } + list_add_tail(&new_attr->list, &dest->attribute_list); + } + + /* copy its child objects */ + list_for_each(pos, &src->object_list) { + struct tplg_object *child = list_entry(pos, struct tplg_object, list); + struct tplg_object *new_child = calloc(1, sizeof(*new_child)); + + ret = tplg_copy_object(tplg, child, new_child, &dest->object_list); + if (ret < 0) { + SNDERR("error copying child object %s\n", child->name); + return ret; + } + } + + /* create tplg elem of type SND_TPLG_TYPE_OBJECT */ + elem = tplg_elem_new_common(tplg, NULL, dest->name, SND_TPLG_TYPE_OBJECT); + if (!elem) + return -ENOMEM; + elem->object = dest; + dest->elem = elem; + + list_add_tail(&dest->list, list); + return 0; +} + +/* class definitions may have pre-defined objects. Copy these into the object */ +static int tplg_copy_child_objects(snd_tplg_t *tplg, struct tplg_class *class, + struct tplg_object *object) +{ + struct list_head *pos; + int ret; + + /* copy child objects */ + list_for_each(pos, &class->object_list) { + struct tplg_object *obj = list_entry(pos, struct tplg_object, list); + struct tplg_object *new_obj = calloc(1, sizeof(*obj)); + + ret = tplg_copy_object(tplg, obj, new_obj, &object->object_list); + if (ret < 0) { + free(new_obj); + return ret; + } + } + + return 0; +} + /* * Create an object of class "class" by copying the attribute list, number of arguments * and default attribute values from the class definition. Objects can also be given @@ -207,6 +290,7 @@ tplg_create_object(snd_tplg_t *tplg, snd_config_t *cfg, struct tplg_class *class snd_strlcpy(object->class_name, class->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); object->type = class->type; INIT_LIST_HEAD(&object->attribute_list); + INIT_LIST_HEAD(&object->object_list); elem->object = object; /* copy attributes from class */ @@ -238,6 +322,13 @@ tplg_create_object(snd_tplg_t *tplg, snd_config_t *cfg, struct tplg_class *class return NULL; } + /* now copy child objects */ + ret = tplg_copy_child_objects(tplg, class, object); + if (ret < 0) { + SNDERR("Failed to create DAI object for %s\n", object->name); + return NULL; + } + if (list) list_add_tail(&object->list, list); diff --git a/src/topology/tplg2_local.h b/src/topology/tplg2_local.h index b373411c9..e3079e4f3 100644 --- a/src/topology/tplg2_local.h +++ b/src/topology/tplg2_local.h @@ -71,6 +71,7 @@ struct tplg_object { char class_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; int num_args; struct list_head attribute_list; + struct list_head object_list; struct tplg_elem *elem; snd_config_t *cfg; int type; From ebe2a0825cc590012ff497781c64235bf824066a Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 22 Mar 2021 22:07:20 -0700 Subject: [PATCH 18/33] topology: object: create child objects within a parent object Just like classes, objects can have other objects when they are instantiated. Parse these(recursively) and add them to the object's object_list. For ex: mixer objects could have child objects for specifying the ops, channels, tlv etc. Object.mixer."0" { #Channel register and shift for Front Left/Right Object.channel."0" { name "fl" shift 0 } Object.channel."1" { name "fr" } Object.tlv."0" { name "vtlv_m64s2" Object.scale."0" { name "m64s2" mute 1 } } Object.ops."0" { name "ctl" info "volsw" #256 binds the mixer control to volume get/put handlers get 256 put 256 } } Signed-off-by: Ranjani Sridharan --- src/topology/object.c | 93 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/src/topology/object.c b/src/topology/object.c index 7677019dd..9f45067b3 100644 --- a/src/topology/object.c +++ b/src/topology/object.c @@ -62,6 +62,92 @@ static int tplg_process_attributes(snd_config_t *cfg, struct tplg_object *object return 0; } +static int create_child_object_instance(snd_tplg_t *tplg, snd_config_t *cfg, + struct tplg_object *parent, struct list_head *list, + struct tplg_elem *class_elem) +{ + snd_config_iterator_t i, next; + snd_config_t *n; + const char *id; + + snd_config_for_each(i, next, cfg) { + struct tplg_object *object; + + n = snd_config_iterator_entry(i); + if (snd_config_get_id(n, &id) < 0) + continue; + + object = tplg_create_object(tplg, n, class_elem->class, parent, list); + if (!object) { + SNDERR("Error creating child %s for parent %s\n", id, parent->name); + return -EINVAL; + } + } + + return 0; +} + +/* create child object */ +int tplg_create_child_object_type(snd_tplg_t *tplg, snd_config_t *cfg, + struct tplg_object *parent, struct list_head *list) +{ + snd_config_iterator_t i, next; + struct tplg_elem *class_elem; + snd_config_t *n; + const char *id; + int ret; + + snd_config_for_each(i, next, cfg) { + n = snd_config_iterator_entry(i); + if (snd_config_get_id(n, &id) < 0) + continue; + + /* check if it is a valid object */ + class_elem = tplg_elem_lookup(&tplg->class_list, id, + SND_TPLG_TYPE_CLASS, SND_TPLG_INDEX_ALL); + + if (!class_elem) + continue; + + ret = create_child_object_instance(tplg, n, parent, list, class_elem); + if (ret < 0) { + SNDERR("Error creating %s type child object for parent %s\n", + class_elem->id, parent->name); + return ret; + } + } + + return 0; +} + +/* create child objects that are part of the parent object instance */ +static int tplg_create_child_objects(snd_tplg_t *tplg, snd_config_t *cfg, + struct tplg_object *parent) +{ + snd_config_iterator_t i, next; + snd_config_t *n; + const char *id; + int ret; + + snd_config_for_each(i, next, cfg) { + n = snd_config_iterator_entry(i); + if (snd_config_get_id(n, &id) < 0) + continue; + + if (strcmp(id, "Object")) + continue; + + /* create object */ + ret = tplg_create_child_object_type(tplg, n, parent, &parent->object_list); + if (ret < 0) { + SNDERR("Error creating child objects for %s\n", parent->name); + return ret; + } + } + + return 0; +} + /* copy the preset attribute values and constraints */ static int tplg_copy_attribute(struct tplg_attribute *attr, struct tplg_attribute *ref_attr) { @@ -329,6 +415,13 @@ tplg_create_object(snd_tplg_t *tplg, snd_config_t *cfg, struct tplg_class *class return NULL; } + /* create child objects that are part of the object instance */ + ret = tplg_create_child_objects(tplg, cfg, object); + if (ret < 0) { + SNDERR("failed to create child objects for %s\n", object->name); + return NULL; + } + if (list) list_add_tail(&object->list, list); From 360f782739dc29722cb5a0caaaaab26119936177 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 22 Mar 2021 22:08:53 -0700 Subject: [PATCH 19/33] topology: add some helper functions Add a couple of helper functions to look up objects within a prent's object_list or the global topology object list based on class_name and the value for the unique attribute in the class. Signed-off-by: Ranjani Sridharan --- src/topology/object.c | 86 ++++++++++++++++++++++++++++++++++++++ src/topology/tplg2_local.h | 4 ++ 2 files changed, 90 insertions(+) diff --git a/src/topology/object.c b/src/topology/object.c index 9f45067b3..c81ad6a75 100644 --- a/src/topology/object.c +++ b/src/topology/object.c @@ -20,6 +20,92 @@ #include "tplg2_local.h" #include +static bool tplg_object_unique_attribute_match(struct tplg_object *object, char *input) +{ + struct tplg_attribute *attr; + struct list_head *pos; + bool found = false; + + /* find attribute with TPLG_CLASS_ATTRIBUTE_MASK_UNIQUE mask */ + list_for_each(pos, &object->attribute_list) { + attr = list_entry(pos, struct tplg_attribute, list); + + if (attr->constraint.mask & TPLG_CLASS_ATTRIBUTE_MASK_UNIQUE) { + found = true; + break; + } + } + + if (!found) + return false; + + /* check if the value matches based on type */ + switch (attr->type) { + case SND_CONFIG_TYPE_INTEGER: + if (attr->value.integer == atoi(input)) + return true; + break; + case SND_CONFIG_TYPE_STRING: + if (!strcmp(attr->value.string, input)) + return true; + break; + default: + break; + } + + return false; +} + +/* look up object based on class type and input value for the unique attribute */ +struct tplg_object *tplg_object_elem_lookup(snd_tplg_t *tplg, const char *class_name, + char *input) +{ + struct list_head *pos; + struct tplg_elem *elem; + + if (!class_name) + return NULL; + + list_for_each(pos, &tplg->object_list) { + struct tplg_object *object; + + elem = list_entry(pos, struct tplg_elem, list); + object = elem->object; + + /* check if class_name natches */ + if (strcmp(object->class_name, class_name)) + continue; + + if (tplg_object_unique_attribute_match(object, input)) + return elem->object; + } + + return NULL; +} + +/* look up object based on class type and unique attribute value in a list */ +struct tplg_object *tplg_object_lookup_in_list(struct list_head *list, const char *class_name, + char *input) +{ + struct list_head *pos; + + if (!class_name) + return NULL; + + list_for_each(pos, list) { + struct tplg_object *object = list_entry(pos, struct tplg_object, list); + + /* check if class_name natches */ + if (strcmp(object->class_name, class_name)) + continue; + + if (tplg_object_unique_attribute_match(object, input)) + return object; + } + + return NULL; +} + /* Process the attribute values provided during object instantiation */ static int tplg_process_attributes(snd_config_t *cfg, struct tplg_object *object) { diff --git a/src/topology/tplg2_local.h b/src/topology/tplg2_local.h index e3079e4f3..57124afa2 100644 --- a/src/topology/tplg2_local.h +++ b/src/topology/tplg2_local.h @@ -98,3 +98,7 @@ tplg_create_object(snd_tplg_t *tplg, snd_config_t *cfg, struct tplg_class *class struct tplg_object *parent, struct list_head *list); struct tplg_attribute *tplg_get_attribute_by_name(struct list_head *list, const char *name); void tplg2_free_elem_object(struct tplg_elem *elem); +struct tplg_object *tplg_object_elem_lookup(snd_tplg_t *tplg, const char *class_name, + char *input); +struct tplg_object *tplg_object_lookup_in_list(struct list_head *list, const char *class_name, + char *input); From 6a446a72a21dd5bd71fecfd516bac6c9e99e5e0f Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 11 Mar 2021 21:44:42 -0800 Subject: [PATCH 20/33] topology: object: Inherit attributes from parent object One of the key features of Topology2.0 is the concept of inheriting object attribute values from parent attribute values. Inheritance is implicit when an attribute in a child object shares the same name as an attribute in the parent object. When creating an object, make sure to pass down the attribute values to all its child objects recursively. Alternatively, a parent object can also explictly set an attribute for its child object by using the object's class name and index attribute as follows: Object.volume-playback."1" { pipeline_id 1 pcm_id 0 pcm_name "Port0" format "s24le" # Set value for mixer.0 in pga.0 pga.0.mixer.0.name "1 Master Playback Volume" } The last line in the above object instance, sets the mixer name for the mixer object with index 0 in the PGA object with index 0 in the volume-playback object. As a rule of thumb, the attribute value set for an object always overrides inherited values from parent or the ones explicitly set in the parent. Signed-off-by: Ranjani Sridharan --- src/topology/object.c | 225 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 225 insertions(+) diff --git a/src/topology/object.c b/src/topology/object.c index c81ad6a75..4ed468dc0 100644 --- a/src/topology/object.c +++ b/src/topology/object.c @@ -106,6 +106,82 @@ struct tplg_object *tplg_object_lookup_in_list(struct list_head *list, const cha return NULL; } +/* Set child object attributes */ +static int tplg_set_child_attributes(snd_tplg_t *tplg, snd_config_t *cfg, + struct tplg_object *object) +{ + snd_config_iterator_t i, next; + snd_config_t *n; + int ret; + + snd_config_for_each(i, next, cfg) { + snd_config_iterator_t first, second; + snd_config_t *first_cfg, *second_cfg; + struct tplg_elem *class_elem; + struct tplg_object *child; + const char *class_name, *index_str, *attribute_name; + + n = snd_config_iterator_entry(i); + if (snd_config_get_id(n, &class_name) < 0) + continue; + + if (snd_config_get_type(n) != SND_CONFIG_TYPE_COMPOUND) + continue; + + /* check if it is a valid class name */ + class_elem = tplg_elem_lookup(&tplg->class_list, class_name, + SND_TPLG_TYPE_CLASS, SND_TPLG_INDEX_ALL); + if (!class_elem) + continue; + + /* get index */ + first = snd_config_iterator_first(n); + first_cfg = snd_config_iterator_entry(first); + if (snd_config_get_id(first_cfg, &index_str) < 0) + continue; + + if (snd_config_get_type(first_cfg) != SND_CONFIG_TYPE_COMPOUND) { + SNDERR("No attribute name for child %s.%s\n", class_name, index_str); + return -EINVAL; + } + + /* the next node can either be an attribute name or a child class */ + second = snd_config_iterator_first(first_cfg); + second_cfg = snd_config_iterator_entry(second); + if (snd_config_get_id(second_cfg, &attribute_name) < 0) + continue; + + /* get object of type class_name and unique attribute value */ + child = tplg_object_lookup_in_list(&object->object_list, class_name, (char *)index_str); + if (!child) { + SNDERR("No child %s.%s found for object %s\n", + class_name, index_str, object->name); + return -EINVAL; + } + + /* + * if the second conf node is an attribute name, set the value but do not + * override the object value if already set. + */ + if (snd_config_get_type(second_cfg) != SND_CONFIG_TYPE_COMPOUND) { + ret = tplg_parse_attribute_value(second_cfg, &child->attribute_list, false); + + if (ret < 0) { + SNDERR("Failed to set attribute for object %s\n", object->name); + return ret; + } + continue; + } + + /* otherwise pass it down to the child object */ + ret = tplg_set_child_attributes(tplg, first_cfg, child); + if (ret < 0) + return ret; + } + + return 0; +} + /* Process the attribute values provided during object instantiation */ static int tplg_process_attributes(snd_config_t *cfg, struct tplg_object *object) { @@ -234,6 +310,141 @@ static int tplg_create_child_objects(snd_tplg_t *tplg, snd_config_t *cfg, return 0; } +/* + * Child objects could have arguments inherited from the parent. Update the name now that the + * parent has been instantiated and values updated. + */ +static int tplg_update_object_name_from_args(struct tplg_object *object) +{ + struct list_head *pos; + char string[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + int ret; + + snprintf(string, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s", object->class_name); + + list_for_each(pos, &object->attribute_list) { + struct tplg_attribute *attr = list_entry(pos, struct tplg_attribute, list); + char new_str[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + + if (attr->param_type != TPLG_CLASS_PARAM_TYPE_ARGUMENT) + continue; + + switch (attr->type) { + case SND_CONFIG_TYPE_INTEGER: + ret = snprintf(new_str, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s.%ld", + string, attr->value.integer); + if (ret > SNDRV_CTL_ELEM_ID_NAME_MAXLEN) { + SNDERR("Object name too long for %s\n", object->name); + return -EINVAL; + } + snd_strlcpy(string, new_str, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + break; + case SND_CONFIG_TYPE_STRING: + ret = snprintf(new_str, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s.%s", + string, attr->value.string); + if (ret > SNDRV_CTL_ELEM_ID_NAME_MAXLEN) { + SNDERR("Object name too long for %s\n", object->name); + return -EINVAL; + } + snd_strlcpy(string, new_str, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + break; + default: + break; + } + } + + snd_strlcpy(object->name, string, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + + return 0; +} + +/* update attributes inherited from parent */ +static int tplg_update_attributes_from_parent(struct tplg_object *object, + struct tplg_object *ref_object) +{ + struct list_head *pos, *pos1; + + /* update object attribute values from reference object */ + list_for_each(pos, &object->attribute_list) { + struct tplg_attribute *attr = list_entry(pos, struct tplg_attribute, list); + + if (attr->found) + continue; + + list_for_each(pos1, &ref_object->attribute_list) { + struct tplg_attribute *ref_attr; + + ref_attr = list_entry(pos1, struct tplg_attribute, list); + if (!ref_attr->found) + continue; + + if (!strcmp(attr->name, ref_attr->name)) { + switch (ref_attr->type) { + case SND_CONFIG_TYPE_INTEGER: + attr->value.integer = ref_attr->value.integer; + attr->type = ref_attr->type; + break; + case SND_CONFIG_TYPE_INTEGER64: + attr->value.integer64 = ref_attr->value.integer64; + attr->type = ref_attr->type; + break; + case SND_CONFIG_TYPE_STRING: + snd_strlcpy(attr->value.string, + ref_attr->value.string, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + attr->type = ref_attr->type; + break; + case SND_CONFIG_TYPE_REAL: + attr->value.d = ref_attr->value.d; + attr->type = ref_attr->type; + break; + default: + SNDERR("Unsupported type %d for attribute %s\n", + attr->type, attr->name); + return -EINVAL; + } + attr->found = true; + } + } + } + + return 0; +} + +/* Propdagate the updated attribute values to child o bjects */ +static int tplg_process_child_objects(struct tplg_object *parent) +{ + struct list_head *pos; + int ret; + + list_for_each(pos, &parent->object_list) { + struct tplg_object *object = list_entry(pos, struct tplg_object, list); + + /* update attribute values inherited from parent */ + ret = tplg_update_attributes_from_parent(object, parent); + if (ret < 0) { + SNDERR("failed to update arguments for %s\n", object->name); + return ret; + } + + /* update object name after args update */ + ret = tplg_update_object_name_from_args(object); + if (ret < 0) + return ret; + + /* update the object elem ID as well */ + snd_strlcpy(object->elem->id, object->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + + /* now process its child objects */ + ret = tplg_process_child_objects(object); + if (ret < 0) { + SNDERR("Cannot update child object for %s\n", object->name); + } + } + + return 0; +} + /* copy the preset attribute values and constraints */ static int tplg_copy_attribute(struct tplg_attribute *attr, struct tplg_attribute *ref_attr) { @@ -508,6 +719,20 @@ tplg_create_object(snd_tplg_t *tplg, snd_config_t *cfg, struct tplg_class *class return NULL; } + /* pass down the object attributes to its child objects */ + ret = tplg_process_child_objects(object); + if (ret < 0) { + SNDERR("failed to create child objects for %s\n", object->name); + return NULL; + } + + /* process child object attributes set explictly in the parent object */ + ret = tplg_set_child_attributes(tplg, cfg, object); + if (ret < 0) { + SNDERR("failed to set child attributes for %s\n", object->name); + return NULL; + } + if (list) list_add_tail(&object->list, list); From ffe15a213b3e40ad8c6ddc6eed5068a37c398d9a Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 11 Mar 2021 22:09:57 -0800 Subject: [PATCH 21/33] topology: object: build topology elements for all objects Once an object is created, the next step is to create the topology element for the topology builder. This patch adds support for build BASE type objects specifically for manifest and data objects. Before building the topology elements, the compiler needs to ensure that all the mandatory attributes have been provided. Also, if attributes have references for valid values, they need to be translated to the appropriate tuple value. Signed-off-by: Ranjani Sridharan --- src/topology/object.c | 199 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+) diff --git a/src/topology/object.c b/src/topology/object.c index 4ed468dc0..ac5479301 100644 --- a/src/topology/object.c +++ b/src/topology/object.c @@ -540,6 +540,67 @@ static int tplg_object_set_unique_attribute(struct tplg_object *object, snd_conf return 0; } +static int tplg_object_attributes_sanity_check(struct tplg_object *object) +{ + struct list_head *pos; + + /* sanity check for object attributes */ + list_for_each(pos, &object->attribute_list) { + struct tplg_attribute *attr = list_entry(pos, struct tplg_attribute, list); + + /* check if mandatory and value provided */ + if ((attr->constraint.mask & TPLG_CLASS_ATTRIBUTE_MASK_MANDATORY) && + !(attr->constraint.mask & TPLG_CLASS_ATTRIBUTE_MASK_IMMUTABLE) && + !attr->found) { + SNDERR("Mandatory attribute %s not found for object %s\n", attr->name, + object->name); + return -EINVAL; + } + + /* translate string values to integer if needed to be added to private data */ + switch (attr->type) { + case SND_CONFIG_TYPE_STRING: + { + struct list_head *pos1; + + /* skip attributes with no pre-defined valid values */ + if (list_empty(&attr->constraint.value_list)) + continue; + + /* Translate the string value to integer if needed */ + list_for_each(pos1, &attr->constraint.value_list) { + struct tplg_attribute_ref *v; + + v = list_entry(pos1, struct tplg_attribute_ref, list); + + if (!strcmp(attr->value.string, v->string)) { + if (v->value != -EINVAL) { + attr->value.integer = v->value; + attr->type = SND_CONFIG_TYPE_INTEGER; + } + break; + } + } + break; + } + default: + break; + } + } + + /* recursively check all child objects */ + list_for_each(pos, &object->object_list) { + struct tplg_object *child = list_entry(pos, struct tplg_object, list); + int ret; + + ret = tplg_object_attributes_sanity_check(child); + if (ret < 0) + return ret; + } + + return 0; +} + /* Copy object from class definition and create the topology element for the newly copied object */ static int tplg_copy_object(snd_tplg_t *tplg, struct tplg_object *src, struct tplg_object *dest, struct list_head *list) @@ -739,6 +800,129 @@ tplg_create_object(snd_tplg_t *tplg, snd_config_t *cfg, struct tplg_class *class return object; } +static int tplg_build_manifest_object(snd_tplg_t *tplg, struct tplg_object *object) { + struct snd_soc_tplg_manifest *manifest; + struct tplg_elem *m_elem; + struct list_head *pos; + int ret; + + if (!list_empty(&tplg->manifest_list)) { + SNDERR("Manifest data already exists"); + return -EINVAL; + } + + m_elem = tplg_elem_new_common(tplg, NULL, object->name, SND_TPLG_TYPE_MANIFEST); + if (!m_elem) + return -ENOMEM; + + manifest = m_elem->manifest; + manifest->size = m_elem->size; + + list_for_each(pos, &object->object_list) { + struct tplg_object *child = list_entry(pos, struct tplg_object, list); + + if (!object->cfg) + continue; + + if (!strcmp(child->class_name, "data")) { + struct tplg_attribute *name; + + name = tplg_get_attribute_by_name(&object->attribute_list, "name"); + + ret = tplg_ref_add(m_elem, SND_TPLG_TYPE_DATA, name->value.string); + if (ret < 0) { + SNDERR("failed to add data elem %s to manifest elem %s\n", + name->value.string, m_elem->id); + return ret; + } + } + } + + tplg_dbg(" Manifest: %s", m_elem->id); + + return 0; +} + +static int tplg_build_data_object(snd_tplg_t *tplg, struct tplg_object *object) { + struct tplg_attribute *bytes, *name; + struct tplg_elem *data_elem; + int ret; + + name = tplg_get_attribute_by_name(&object->attribute_list, "name"); + if (!name) { + SNDERR("invalid name for data object: %s", object->name); + return -EINVAL; + } + + /* check if data elem exists already */ + data_elem = tplg_elem_lookup(&tplg->widget_list, name->value.string, + SND_TPLG_TYPE_DATA, SND_TPLG_INDEX_ALL); + if (!data_elem) { + /* create data elem */ + data_elem = tplg_elem_new_common(tplg, NULL, name->value.string, + SND_TPLG_TYPE_DATA); + if (!data_elem) { + SNDERR("failed to create data elem for %s\n", object->name); + return -EINVAL; + } + } + + bytes = tplg_get_attribute_by_name(&object->attribute_list, "bytes"); + + if (!bytes || !bytes->cfg) + return 0; + + ret = tplg_parse_data_hex(bytes->cfg, data_elem, 1); + if (ret < 0) + SNDERR("failed to parse byte for data: %s", object->name); + + tplg_dbg("data: %s", name->value.string); + + return ret; +} + + +static int tplg_build_base_object(snd_tplg_t *tplg, struct tplg_object *object) +{ + if (!strcmp(object->class_name, "manifest")) + return tplg_build_manifest_object(tplg, object); + + if (!strcmp(object->class_name, "data")) + return tplg_build_data_object(tplg, object); + + return 0; +} + +static int tplg_build_object(snd_tplg_t *tplg, struct tplg_object *object) +{ + struct list_head *pos; + int ret; + + switch (object->type) { + case SND_TPLG_CLASS_TYPE_BASE: + ret = tplg_build_base_object(tplg, object); + if (ret < 0) { + SNDERR("Failed to build object %s\n", object->name); + return ret; + } + break; + } + + /* build child objects */ + list_for_each(pos, &object->object_list) { + struct tplg_object *child = list_entry(pos, struct tplg_object, list); + + ret = tplg_build_object(tplg, child); + if (ret < 0) { + SNDERR("Failed to build object\n", child->name); + return ret; + } + } + + return 0; +} + + int tplg_create_new_object(snd_tplg_t *tplg, snd_config_t *cfg, struct tplg_elem *class_elem) { snd_config_iterator_t i, next; @@ -746,6 +930,7 @@ int tplg_create_new_object(snd_tplg_t *tplg, snd_config_t *cfg, struct tplg_elem struct tplg_object *object; snd_config_t *n; const char *id; + int ret; /* create all objects of the same class type */ snd_config_for_each(i, next, cfg) { @@ -763,6 +948,20 @@ int tplg_create_new_object(snd_tplg_t *tplg, snd_config_t *cfg, struct tplg_elem SNDERR("Error creating object for class %s\n", class->name); return -EINVAL; } + + /* check if all mandatory values are present and translate string values to integer */ + ret = tplg_object_attributes_sanity_check(object); + if (ret < 0) { + SNDERR("Object %s failed sanity check\n", object->name); + return -EINVAL; + } + + /* Build the object by creating the topology element */ + ret = tplg_build_object(tplg, object); + if (ret < 0) { + SNDERR("Error creating object for class %s\n", class->name); + return -EINVAL; + } } return 0; From 2f6777f54f1875578e14d3b2575a64e551c52e0c Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 11 Mar 2021 22:18:12 -0800 Subject: [PATCH 22/33] topology: object: add support for building private data Many topology elements pack tuple data in their private data. Iterate through the attribute list of an object and build tuple sets for those attributes that have the token_ref field set. Add a new field, tuple_set_list to struct objects and add the tuple sets to the list. Once the tuple_sets are built for an object, the compiler scans the tuple sets against the provided token reference and builds the private data array. Signed-off-by: Ranjani Sridharan --- src/topology/object.c | 269 +++++++++++++++++++++++++++++++++++++ src/topology/tplg2_local.h | 3 + 2 files changed, 272 insertions(+) diff --git a/src/topology/object.c b/src/topology/object.c index ac5479301..4de1729be 100644 --- a/src/topology/object.c +++ b/src/topology/object.c @@ -619,6 +619,7 @@ static int tplg_copy_object(snd_tplg_t *tplg, struct tplg_object *src, struct tp snd_strlcpy(dest->class_name, src->class_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); dest->type = src->type; dest->cfg = src->cfg; + INIT_LIST_HEAD(&dest->tuple_set_list); INIT_LIST_HEAD(&dest->attribute_list); INIT_LIST_HEAD(&dest->object_list); @@ -733,6 +734,7 @@ tplg_create_object(snd_tplg_t *tplg, snd_config_t *cfg, struct tplg_class *class snd_strlcpy(object->name, object_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); snd_strlcpy(object->class_name, class->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); object->type = class->type; + INIT_LIST_HEAD(&object->tuple_set_list); INIT_LIST_HEAD(&object->attribute_list); INIT_LIST_HEAD(&object->object_list); elem->object = object; @@ -800,6 +802,266 @@ tplg_create_object(snd_tplg_t *tplg, snd_config_t *cfg, struct tplg_class *class return object; } +static int tplg2_get_bool(struct tplg_attribute *attr) +{ + if (attr->type != SND_CONFIG_TYPE_INTEGER) + return -EINVAL; + + if (attr->value.integer != 0 && attr->value.integer != 1) + return -EINVAL; + + return attr->value.integer; +} + +static int tplg_get_object_tuple_set(struct tplg_object *object, struct tplg_tuple_set **out, + const char *token_ref) +{ + struct list_head *pos, *_pos; + struct tplg_tuple_set *set; + const char *type; + char *tokenref_str; + int len, set_type; + int size; + + /* get tuple set type */ + type = strchr(token_ref, '.'); + if (!type) { + SNDERR("No type given for tuple set: '%s' in object: '%s'\n", + token_ref, object->name); + return -EINVAL; + } + + set_type = get_tuple_type(type + 1); + if (set_type < 0) { + SNDERR("Invalid type for tuple set: '%s' in object: '%s'\n", + token_ref, object->name); + return -EINVAL; + } + + /* get tuple token ref name */ + len = strlen(token_ref) - strlen(type) + 1; + tokenref_str = calloc(1, len); + snd_strlcpy(tokenref_str, token_ref, len); + + /* realloc set if set is found */ + list_for_each_safe(pos, _pos, &object->tuple_set_list) { + struct tplg_tuple_set *set2; + + set = list_entry(pos, struct tplg_tuple_set, list); + + if (set->type == (unsigned int)set_type && !(strcmp(set->token_ref, tokenref_str))) { + + set->num_tuples++; + size = sizeof(*set) + set->num_tuples * sizeof(struct tplg_tuple); + set2 = realloc(set, size); + if (!set2) + return -ENOMEM; + list_del(&set->list); + + set = set2; + list_add_tail(&set->list, &object->tuple_set_list); + *out = set; + return 0; + } + } + + /* else create a new set and add it to the object's tuple_set_list */ + size = sizeof(*set) + sizeof(struct tplg_tuple); + set = calloc(1, size); + set->num_tuples = 1; + set->type = set_type; + snd_strlcpy(set->token_ref, tokenref_str, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + list_add_tail(&set->list, &object->tuple_set_list); + *out = set; + + return 0; +} + +static int tplg_build_object_tuple_set_from_attributes(struct tplg_object *object, + struct tplg_attribute *attr) +{ + struct tplg_tuple_set *set; + struct tplg_tuple *tuple; + struct list_head *pos; + int ret; + + /* get tuple set if it exists already or create one */ + ret = tplg_get_object_tuple_set(object, &set, attr->token_ref); + if (ret < 0) { + SNDERR("Invalid tuple set for '%s'\n", object->name); + return ret; + } + + /* update set with new tuple */ + tuple = &set->tuple[set->num_tuples - 1]; + snd_strlcpy(tuple->token, attr->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + switch (set->type) { + case SND_SOC_TPLG_TUPLE_TYPE_UUID: + { + const char *value; + + if (snd_config_get_string(attr->cfg, &value) < 0) + break; + if (get_uuid(value, tuple->uuid) < 0) { + SNDERR("failed to get uuid from string %s\n", value); + return -EINVAL; + } + tplg_dbg("\t\tuuid string %s ", value); + tplg_dbg("\t\t%s = 0x%x", tuple->token, tuple->uuid); + break; + } + case SND_SOC_TPLG_TUPLE_TYPE_STRING: + snd_strlcpy(tuple->string, attr->value.string, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + tplg_dbg("\t\t%s = %s", tuple->token, tuple->string); + break; + + case SND_SOC_TPLG_TUPLE_TYPE_BOOL: + { + int ret = tplg2_get_bool(attr); + + if (ret < 0) { + SNDERR("Invalid value for tuple %s\n", tuple->token); + return tuple->value; + } + tuple->value = ret; + tplg_dbg("\t\t%s = %d", tuple->token, tuple->value); + break; + } + case SND_SOC_TPLG_TUPLE_TYPE_BYTE: + case SND_SOC_TPLG_TUPLE_TYPE_SHORT: + case SND_SOC_TPLG_TUPLE_TYPE_WORD: + { + unsigned int tuple_val = 0; + + switch(attr->type) { + case SND_CONFIG_TYPE_STRING: + if (!attr->constraint.value_ref) { + SNDERR("Invalid tuple value type for %s\n", tuple->token); + return -EINVAL; + } + + /* convert attribute string values to corresponding integer value */ + list_for_each(pos, &attr->constraint.value_list) { + struct tplg_attribute_ref *v; + + v = list_entry(pos, struct tplg_attribute_ref, list); + if (!strcmp(attr->value.string, v->string)) + if (v->value != -EINVAL) + tuple_val = v->value; + } + break; + case SND_CONFIG_TYPE_INTEGER: + tuple_val = attr->value.integer; + break; + case SND_CONFIG_TYPE_INTEGER64: + tuple_val = attr->value.integer64; + break; + default: + SNDERR("Invalid value type %d for tuple %s for object %s \n", attr->type, + tuple->token, object->name); + return -EINVAL; + } + + if ((set->type == SND_SOC_TPLG_TUPLE_TYPE_WORD + && tuple_val > UINT_MAX) + || (set->type == SND_SOC_TPLG_TUPLE_TYPE_SHORT + && tuple_val > USHRT_MAX) + || (set->type == SND_SOC_TPLG_TUPLE_TYPE_BYTE + && tuple_val > UCHAR_MAX)) { + SNDERR("tuple %s: invalid value", tuple->token); + return -EINVAL; + } + + tuple->value = tuple_val; + tplg_dbg("\t\t%s = 0x%x", tuple->token, tuple->value); + break; + } + default: + break; + } + + return 0; +} + +/* Build tuple sets from object attributes */ +int tplg_build_object_tuple_sets(struct tplg_object *object) +{ + struct list_head *pos; + int ret = 0; + + list_for_each(pos, &object->attribute_list) { + struct tplg_attribute *attr = list_entry(pos, struct tplg_attribute, list); + + if (attr->constraint.mask & TPLG_CLASS_ATTRIBUTE_MASK_DEPRECATED) { + if (attr->found) + SNDERR("Warning: attibute %s decprecated\n", attr->name); + continue; + } + + if (strcmp(attr->token_ref, "")) { + if (!attr->found) + continue; + + ret = tplg_build_object_tuple_set_from_attributes(object, attr); + if (ret < 0) + return ret; + } + } + + return ret; +} + +int tplg_build_private_data(snd_tplg_t *tplg, struct tplg_object *object) +{ + struct snd_soc_tplg_private *priv; + struct tplg_elem *data_elem; + struct list_head *pos; + int ret; + + /* build tuple sets for object */ + ret = tplg_build_object_tuple_sets(object); + if (ret < 0) + return ret; + + data_elem = tplg_elem_lookup(&tplg->pdata_list, object->name, + SND_TPLG_TYPE_DATA, SND_TPLG_INDEX_ALL); + if(!data_elem) + return 0; + + priv = data_elem->data; + + /* build link private data from tuple sets */ + list_for_each(pos, &object->tuple_set_list) { + struct tplg_tuple_set *set = list_entry(pos, struct tplg_tuple_set, list); + struct tplg_elem *token_elem; + + if (!set->token_ref) { + SNDERR("No valid token ref for tuple set type %d\n", set->type); + return -EINVAL; + } + + /* get reference token elem */ + token_elem = tplg_elem_lookup(&tplg->token_list, set->token_ref, + SND_TPLG_TYPE_TOKEN, SND_TPLG_INDEX_ALL); + if (!token_elem) { + SNDERR("No valid tokens for ref %s\n", set->token_ref); + return -EINVAL; + } + + ret = scan_tuple_set(data_elem, set, token_elem->tokens, priv ? priv->size : 0); + if (ret < 0) + return ret; + + /* priv gets modified while scanning new sets */ + priv = data_elem->data; + } + + tplg_dbg("Object %s built", object->name); + + return 0; +} + static int tplg_build_manifest_object(snd_tplg_t *tplg, struct tplg_object *object) { struct snd_soc_tplg_manifest *manifest; struct tplg_elem *m_elem; @@ -992,6 +1254,7 @@ void tplg2_free_elem_object(struct tplg_elem *elem) struct tplg_object *object = elem->object; struct list_head *pos, *npos; struct tplg_attribute *attr; + struct tplg_tuple_set *set; /* * free args, attributes and tuples. Child objects will be freed when @@ -1002,4 +1265,10 @@ void tplg2_free_elem_object(struct tplg_elem *elem) list_del(&attr->list); free(attr); } + + list_for_each_safe(pos, npos, &object->tuple_set_list) { + set = list_entry(pos, struct tplg_tuple_set, list); + list_del(&set->list); + free(set); + } } diff --git a/src/topology/tplg2_local.h b/src/topology/tplg2_local.h index 57124afa2..b09ab0d3f 100644 --- a/src/topology/tplg2_local.h +++ b/src/topology/tplg2_local.h @@ -70,7 +70,9 @@ struct tplg_object { char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; char class_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; int num_args; + int num_tuple_sets; struct list_head attribute_list; + struct list_head tuple_set_list; struct list_head object_list; struct tplg_elem *elem; snd_config_t *cfg; @@ -97,6 +99,7 @@ struct tplg_object * tplg_create_object(snd_tplg_t *tplg, snd_config_t *cfg, struct tplg_class *class, struct tplg_object *parent, struct list_head *list); struct tplg_attribute *tplg_get_attribute_by_name(struct list_head *list, const char *name); +int tplg_build_private_data(snd_tplg_t *tplg, struct tplg_object *object); void tplg2_free_elem_object(struct tplg_elem *elem); struct tplg_object *tplg_object_elem_lookup(snd_tplg_t *tplg, const char *class_name, char *input); From 67bbc68626120c21847e7c5f5a389282574b6ba0 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 11 Mar 2021 22:32:36 -0800 Subject: [PATCH 23/33] topology: class: Introduce pre-defined types of classes Add some commonly used Classes such as Component, DAI, Pipeline PCM as pre-defined class types. Update the struct tplg_object to include type-specific data based on class type for some classes. Signed-off-by: Ranjani Sridharan --- src/topology/class.c | 34 ++++++++++++++++++++++++++++++++-- src/topology/tplg2_local.h | 24 ++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/src/topology/class.c b/src/topology/class.c index 2c6eb23b3..4e9bc641c 100644 --- a/src/topology/class.c +++ b/src/topology/class.c @@ -21,6 +21,29 @@ #include "tplg2_local.h" #include +/* mapping of widget text names to types */ +static const struct map_elem class_map[] = { + {"Base", SND_TPLG_CLASS_TYPE_BASE}, + {"Pipeline", SND_TPLG_CLASS_TYPE_PIPELINE}, + {"Component", SND_TPLG_CLASS_TYPE_COMPONENT}, + {"Control", SND_TPLG_CLASS_TYPE_CONTROL}, + {"Dai", SND_TPLG_CLASS_TYPE_DAI}, + {"PCM", SND_TPLG_CLASS_TYPE_PCM}, +}; + + +int lookup_class_type(const char *c) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(class_map); i++) { + if (strcmp(class_map[i].name, c) == 0) + return class_map[i].id; + } + + return -EINVAL; +} + /* save valid values for attributes */ static int tplg_parse_constraint_valid_values(snd_tplg_t *tplg, snd_config_t *cfg, struct attribute_constraint *c, @@ -734,18 +757,25 @@ int tplg_define_classes(snd_tplg_t *tplg, snd_config_t *cfg, void *priv ATTRIBUT snd_config_iterator_t i, next; snd_config_t *n; const char *id; - int ret; + int class, ret; if (snd_config_get_id(cfg, &id) < 0) return -EINVAL; + /* classes must belong to one of the pre-defined types */ + class = lookup_class_type(id); + if (class < 0) { + SNDERR("Invalid class type %s\n", id); + return -EINVAL; + } + /* create class */ snd_config_for_each(i, next, cfg) { n = snd_config_iterator_entry(i); if (snd_config_get_id(n, &id) < 0) continue; - ret = tplg_define_class(tplg, n, SND_TPLG_CLASS_TYPE_BASE); + ret = tplg_define_class(tplg, n, class); if (ret < 0) { SNDERR("Failed to create class %s\n", id); return ret; diff --git a/src/topology/tplg2_local.h b/src/topology/tplg2_local.h index b09ab0d3f..59f60362b 100644 --- a/src/topology/tplg2_local.h +++ b/src/topology/tplg2_local.h @@ -66,6 +66,20 @@ struct tplg_attribute { }value; }; +struct tplg_dai_object { + struct tplg_elem *link_elem; + int num_hw_configs; +}; + +struct tplg_pipeline_object { + struct tplg_object *pipe_widget_object; +}; + +struct tplg_comp_object { + struct tplg_elem *widget_elem; + int widget_id; +}; + struct tplg_object { char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; char class_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; @@ -78,10 +92,20 @@ struct tplg_object { snd_config_t *cfg; int type; struct list_head list; /* item in parent object list */ + union { + struct tplg_comp_object component; + struct tplg_dai_object dai; + struct tplg_pipeline_object pipeline; + }object_type; }; /* class types */ #define SND_TPLG_CLASS_TYPE_BASE 0 +#define SND_TPLG_CLASS_TYPE_COMPONENT 1 +#define SND_TPLG_CLASS_TYPE_PIPELINE 2 +#define SND_TPLG_CLASS_TYPE_DAI 3 +#define SND_TPLG_CLASS_TYPE_CONTROL 4 +#define SND_TPLG_CLASS_TYPE_PCM 5 struct tplg_class { char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; From 6d5a2fc95ac759ad4a9dbb58c59ad7b35ffe2895 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 11 Mar 2021 22:40:55 -0800 Subject: [PATCH 24/33] topology: object: Add support for DAPM widget type objects Add support for building DAPM widget/component type class objects by creating the topology element of type SND_TPLG_TYPE_DAPM_WIDGET and setting the widget params based on the object attributes. Signed-off-by: Ranjani Sridharan --- src/topology/Makefile.am | 3 +- src/topology/dapm-object.c | 135 +++++++++++++++++++++++++++++++++++++ src/topology/object.c | 35 ++++++++++ src/topology/tplg2_local.h | 2 + 4 files changed, 174 insertions(+), 1 deletion(-) create mode 100644 src/topology/dapm-object.c diff --git a/src/topology/Makefile.am b/src/topology/Makefile.am index 4c75543cf..adebee8c0 100644 --- a/src/topology/Makefile.am +++ b/src/topology/Makefile.am @@ -32,7 +32,8 @@ libatopology_la_SOURCES =\ decoder.c \ log.c \ class.c \ - object.c + object.c \ + dapm-object.c noinst_HEADERS = tplg_local.h tplg2_local.h diff --git a/src/topology/dapm-object.c b/src/topology/dapm-object.c new file mode 100644 index 000000000..a12af15db --- /dev/null +++ b/src/topology/dapm-object.c @@ -0,0 +1,135 @@ +/* + Copyright(c) 2020-2021 Intel Corporation + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + Author: Ranjani Sridharan +*/ +#include "list.h" +#include "local.h" +#include "tplg_local.h" +#include "tplg2_local.h" +#include + +int tplg_create_component_object(struct tplg_object *object) +{ + struct tplg_comp_object *comp = &object->object_type.component; + struct tplg_attribute *widget_type; + int widget_id; + + widget_type = tplg_get_attribute_by_name(&object->attribute_list, "widget_type"); + if (!widget_type) { + SNDERR("No widget_type given for %s\n", object->name); + return -EINVAL; + } + + widget_id = lookup_widget(widget_type->value.string); + + if (widget_id < 0) { + SNDERR("Invalid widget ID for %s\n", object->name); + return widget_id; + } + + comp->widget_id = widget_id; + return 0; +} + +static int tplg_create_widget_elem(snd_tplg_t *tplg, struct tplg_object *object) +{ + struct tplg_comp_object *widget_object = &object->object_type.component; + struct tplg_elem *widget_elem, *data_elem; + struct snd_soc_tplg_dapm_widget *widget; + char *class_name = object->class_name; + char *elem_name; + int ret; + + if (strcmp(class_name, "virtual_widget")) + elem_name = object->name; + else + elem_name = strchr(object->name, '.') + 1; + + widget_elem = tplg_elem_new_common(tplg, NULL, elem_name, + SND_TPLG_TYPE_DAPM_WIDGET); + if (!widget_elem) + return -ENOMEM; + + /* create data elem for w */ + data_elem = tplg_elem_new_common(tplg, NULL, elem_name, SND_TPLG_TYPE_DATA); + if (!data_elem) + return -ENOMEM; + + ret = tplg_ref_add(widget_elem, SND_TPLG_TYPE_DATA, data_elem->id); + if (ret < 0) { + SNDERR("failed to add data elem %s to widget elem %s\n", data_elem->id, + widget_elem->id); + return ret; + } + + widget_object->widget_elem = widget_elem; + widget = widget_elem->widget; + widget->id = widget_object->widget_id; + widget->size = widget_elem->size; + snd_strlcpy(widget->name, widget_elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + + return 0; +} + +int tplg_build_comp_object(snd_tplg_t *tplg, struct tplg_object *object) +{ + struct tplg_attribute *pipeline_id; + struct snd_soc_tplg_dapm_widget *widget; + struct tplg_comp_object *comp = &object->object_type.component; + struct tplg_elem *w_elem; + struct list_head *pos; + int ret; + + ret = tplg_create_widget_elem(tplg, object); + if (ret < 0) { + SNDERR("Failed to create widget elem for object %s\n", object->name); + return ret; + } + w_elem = comp->widget_elem; + widget = w_elem->widget; + + pipeline_id = tplg_get_attribute_by_name(&object->attribute_list, "pipeline_id"); + if (pipeline_id) + w_elem->index = pipeline_id->value.integer; + + /* parse widget params from attributes */ + list_for_each(pos, &object->attribute_list) { + struct tplg_attribute *attr = list_entry(pos, struct tplg_attribute, list); + + if (!strcmp(attr->name, "stream_name") && attr->found) { + snd_strlcpy(widget->sname, attr->value.string, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + continue; + } + + if (!attr->cfg) + continue; + + /* widget type is already processed */ + if (!strcmp(attr->name, "type")) + continue; + + ret = tplg_parse_dapm_widget_param(attr->cfg, widget, NULL); + if (ret < 0) { + SNDERR("Error parsing widget params for %s\n", object->name); + return ret; + } + } + + tplg_dbg("Widget: %s id: %d stream_name: %s no_pm: %d", + w_elem->id, widget->id, widget->sname, widget->reg); + + return tplg_build_private_data(tplg, object); +} diff --git a/src/topology/object.c b/src/topology/object.c index 4de1729be..e49d9b057 100644 --- a/src/topology/object.c +++ b/src/topology/object.c @@ -640,6 +640,19 @@ static int tplg_copy_object(snd_tplg_t *tplg, struct tplg_object *src, struct tp list_add_tail(&new_attr->list, &dest->attribute_list); } + switch(src->type) { + case SND_TPLG_CLASS_TYPE_COMPONENT: + { + struct tplg_comp_object *dest_comp_object = &dest->object_type.component; + struct tplg_comp_object *src_comp_object = &src->object_type.component; + + memcpy(dest_comp_object, src_comp_object, sizeof(*dest_comp_object)); + break; + } + default: + break; + } + /* copy its child objects */ list_for_each(pos, &src->object_list) { struct tplg_object *child = list_entry(pos, struct tplg_object, list); @@ -768,6 +781,19 @@ tplg_create_object(snd_tplg_t *tplg, snd_config_t *cfg, struct tplg_class *class return NULL; } + /* sanity check */ + switch(object->type) { + case SND_TPLG_CLASS_TYPE_COMPONENT: + ret = tplg_create_component_object(object); + if (ret < 0) { + SNDERR("Failed to create component object for %s\n", object->name); + return NULL; + } + break; + default: + break; + } + /* now copy child objects */ ret = tplg_copy_child_objects(tplg, class, object); if (ret < 0) { @@ -1161,6 +1187,15 @@ static int tplg_build_object(snd_tplg_t *tplg, struct tplg_object *object) int ret; switch (object->type) { + case SND_TPLG_CLASS_TYPE_COMPONENT: + { + ret = tplg_build_comp_object(tplg, object); + if (ret < 0) { + SNDERR("Failed to build comp object %s\n", object->name); + return ret; + } + break; + } case SND_TPLG_CLASS_TYPE_BASE: ret = tplg_build_base_object(tplg, object); if (ret < 0) { diff --git a/src/topology/tplg2_local.h b/src/topology/tplg2_local.h index 59f60362b..6fd6b0c43 100644 --- a/src/topology/tplg2_local.h +++ b/src/topology/tplg2_local.h @@ -124,6 +124,8 @@ tplg_create_object(snd_tplg_t *tplg, snd_config_t *cfg, struct tplg_class *class struct tplg_object *parent, struct list_head *list); struct tplg_attribute *tplg_get_attribute_by_name(struct list_head *list, const char *name); int tplg_build_private_data(snd_tplg_t *tplg, struct tplg_object *object); +int tplg_build_comp_object(snd_tplg_t *tplg, struct tplg_object *object); +int tplg_create_component_object(struct tplg_object *object); void tplg2_free_elem_object(struct tplg_elem *elem); struct tplg_object *tplg_object_elem_lookup(snd_tplg_t *tplg, const char *class_name, char *input); From 7d1f0aa04562a05559ca0933ebb440020f68fe75 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 22 Mar 2021 11:35:23 -0700 Subject: [PATCH 25/33] topology2: dapm-object: add support for controls SND_TPLG_TYPE_DAPM_WIDGET type topology elements can contaim kcontrols associated with them. Add support for building the controls of type mixer/bytes when building the widget. ENum control support will be addded later. Signed-off-by: Ranjani Sridharan --- src/topology/dapm-object.c | 369 +++++++++++++++++++++++++++++++++++++ 1 file changed, 369 insertions(+) diff --git a/src/topology/dapm-object.c b/src/topology/dapm-object.c index a12af15db..820a810d5 100644 --- a/src/topology/dapm-object.c +++ b/src/topology/dapm-object.c @@ -43,6 +43,320 @@ int tplg_create_component_object(struct tplg_object *object) return 0; } +static int tplg2_parse_channel(struct tplg_object *object, struct tplg_elem *mixer_elem) +{ + struct snd_soc_tplg_mixer_control *mc = mixer_elem->mixer_ctrl; + struct snd_soc_tplg_channel *channel = mc->channel; + struct list_head *pos; + char *channel_name = strchr(object->name, '.') + 1; + int channel_id = lookup_channel(channel_name); + + if (channel_id < 0) { + SNDERR("invalid channel %d for mixer %s", channel_id, mixer_elem->id); + return -EINVAL; + } + + channel += mc->num_channels; + + channel->id = channel_id; + channel->size = sizeof(*channel); + list_for_each(pos, &object->attribute_list) { + struct tplg_attribute *attr = list_entry(pos, struct tplg_attribute, list); + + if (!strcmp(attr->name, "reg")) + channel->reg = attr->value.integer; + + + if (!strcmp(attr->name, "shift")) + channel->shift = attr->value.integer; + } + + mc->num_channels++; + if (mc->num_channels >= SND_SOC_TPLG_MAX_CHAN) { + SNDERR("Max channels exceeded for %s\n", mixer_elem->id); + return -EINVAL; + } + + tplg_dbg("channel: %s id: %d reg:%d shift %d", channel_name, channel->id, channel->reg, channel->shift); + + return 0; +} + +static int tplg2_parse_tlv(snd_tplg_t *tplg, struct tplg_object *object, + struct tplg_elem *mixer_elem) +{ + struct snd_soc_tplg_ctl_tlv *tplg_tlv; + struct snd_soc_tplg_tlv_dbscale *scale; + struct tplg_elem *elem; + struct list_head *pos; + int ret; + + /* Just add ref if TLV elem exists already */ + elem = tplg_elem_lookup(&tplg->widget_list, object->name, SND_TPLG_TYPE_TLV, + SND_TPLG_INDEX_ALL); + if (elem) { + tplg_tlv = elem->tlv; + scale = &tplg_tlv->scale; + goto ref; + } + + /* otherwise create new TLV elem */ + elem = tplg_elem_new_common(tplg, NULL, object->name, SND_TPLG_TYPE_TLV); + if (!elem) + return -ENOMEM; + + tplg_tlv = elem->tlv; + tplg_tlv->size = sizeof(struct snd_soc_tplg_ctl_tlv); + tplg_tlv->type = SNDRV_CTL_TLVT_DB_SCALE; + scale = &tplg_tlv->scale; + + list_for_each(pos, &object->object_list) { + struct tplg_object *child = list_entry(pos, struct tplg_object, list); + + if (!strcmp(child->class_name, "scale")) { + list_for_each(pos, &child->attribute_list) { + struct tplg_attribute *attr; + + attr = list_entry(pos, struct tplg_attribute, list); + if (!attr->cfg) + continue; + + ret = tplg_parse_tlv_dbscale_param(attr->cfg, scale); + if (ret < 0) { + SNDERR("failed to DBScale for tlv %s", object->name); + return ret; + } + } + + break; + } + } +ref: + tplg_dbg("TLV: %s scale min: %d step %d mute %d", elem->id, scale->min, scale->step, scale->mute); + + ret = tplg_ref_add(mixer_elem, SND_TPLG_TYPE_TLV, elem->id); + if (ret < 0) { + SNDERR("failed to add tlv elem %s to mixer elem %s\n", + elem->id, mixer_elem->id); + return ret; + } + + return 0; +} + +static struct tplg_elem *tplg_build_comp_mixer(snd_tplg_t *tplg, struct tplg_object *object, + char *name) +{ + struct snd_soc_tplg_mixer_control *mc; + struct snd_soc_tplg_ctl_hdr *hdr; + struct tplg_elem *elem; + struct list_head *pos; + bool access_set = false, tlv_set = false; + int j, ret; + + elem = tplg_elem_new_common(tplg, NULL, name, SND_TPLG_TYPE_MIXER); + if (!elem) + return NULL; + + /* init new mixer */ + mc = elem->mixer_ctrl; + snd_strlcpy(mc->hdr.name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + mc->hdr.type = SND_SOC_TPLG_TYPE_MIXER; + mc->size = elem->size; + hdr = &mc->hdr; + + /* set channel reg to default state */ + for (j = 0; j < SND_SOC_TPLG_MAX_CHAN; j++) + mc->channel[j].reg = -1; + + /* parse some control params from attributes */ + list_for_each(pos, &object->attribute_list) { + struct tplg_attribute *attr; + + attr = list_entry(pos, struct tplg_attribute, list); + + if (!attr->cfg) + continue; + + ret = tplg_parse_control_mixer_param(tplg, attr->cfg, mc, elem); + if (ret < 0) { + SNDERR("Error parsing hw_config for %s\n", object->name); + return NULL; + } + + if (!strcmp(attr->name, "access")) { + ret = parse_access_values(attr->cfg, &mc->hdr); + if (ret < 0) { + SNDERR("Error parsing access attribute for %s\n", object->name); + return NULL; + } + access_set = true; + } + + } + + /* parse the rest from child objects */ + list_for_each(pos, &object->object_list) { + struct tplg_object *child = list_entry(pos, struct tplg_object, list); + + if (!object->cfg) + continue; + + if (!strcmp(child->class_name, "ops")) { + ret = tplg_parse_ops(tplg, child->cfg, hdr); + if (ret < 0) { + SNDERR("Error parsing ops for mixer %s\n", object->name); + return NULL; + } + continue; + } + + if (!strcmp(child->class_name, "tlv")) { + ret = tplg2_parse_tlv(tplg, child, elem); + if (ret < 0) { + SNDERR("Error parsing tlv for mixer %s\n", object->name); + return NULL; + } + tlv_set = true; + continue; + } + + if (!strcmp(child->class_name, "channel")) { + ret = tplg2_parse_channel(child, elem); + if (ret < 0) { + SNDERR("Error parsing channel %d for mixer %s\n", child->name, + object->name); + return NULL; + } + continue; + } + } + tplg_dbg("Mixer: %s, num_channels: %d", elem->id, mc->num_channels); + tplg_dbg("Ops info: %d get: %d put: %d max: %d", hdr->ops.info, hdr->ops.get, hdr->ops.put, mc->max); + + /* set CTL access to default values if none provided */ + if (!access_set) { + mc->hdr.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; + if (tlv_set) + mc->hdr.access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; + } + + return elem; +} + +static struct tplg_elem *tplg_build_comp_bytes(snd_tplg_t *tplg, struct tplg_object *object, + char *name) +{ + struct snd_soc_tplg_bytes_control *be; + struct snd_soc_tplg_ctl_hdr *hdr; + struct tplg_elem *elem; + struct list_head *pos; + bool access_set = false, tlv_set = false; + int ret; + + elem = tplg_elem_new_common(tplg, NULL, name, SND_TPLG_TYPE_BYTES); + if (!elem) + return NULL; + + /* init new byte control */ + be = elem->bytes_ext; + snd_strlcpy(be->hdr.name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + be->hdr.type = SND_SOC_TPLG_TYPE_BYTES; + be->size = elem->size; + hdr = &be->hdr; + + /* parse some control params from attributes */ + list_for_each(pos, &object->attribute_list) { + struct tplg_attribute *attr; + + attr = list_entry(pos, struct tplg_attribute, list); + + if (!attr->cfg) + continue; + + ret = tplg_parse_control_bytes_param(tplg, attr->cfg, be, elem); + if (ret < 0) { + SNDERR("Error parsing control bytes params for %s\n", object->name); + return NULL; + } + + if (!strcmp(attr->name, "access")) { + ret = parse_access_values(attr->cfg, &be->hdr); + if (ret < 0) { + SNDERR("Error parsing access attribute for %s\n", object->name); + return NULL; + } else { + access_set = true; + } + } + + } + + /* parse the rest from child objects */ + list_for_each(pos, &object->object_list) { + struct tplg_object *child = list_entry(pos, struct tplg_object, list); + + if (!object->cfg) + continue; + + if (!strcmp(child->class_name, "ops")) { + ret = tplg_parse_ops(tplg, child->cfg, hdr); + if (ret < 0) { + SNDERR("Error parsing ops for mixer %s\n", object->name); + return NULL; + } + continue; + } + + if (!strcmp(child->class_name, "tlv")) { + ret = tplg2_parse_tlv(tplg, child, elem); + if (ret < 0) { + SNDERR("Error parsing tlv for mixer %s\n", object->name); + return NULL; + } else { + tlv_set = true; + } + continue; + } + + if (!strcmp(child->class_name, "extops")) { + ret = tplg_parse_ext_ops(tplg, child->cfg, &be->hdr); + if (ret < 0) { + SNDERR("Error parsing ext ops for bytes %s\n", object->name); + return NULL; + } + continue; + } + + /* add data reference for byte control by adding a new obect */ + if (!strcmp(child->class_name, "data")) { + struct tplg_attribute *name; + + name = tplg_get_attribute_by_name(&child->attribute_list, "name"); + /* add reference to data elem */ + ret = tplg_ref_add(elem, SND_TPLG_TYPE_DATA, name->value.string); + if (ret < 0) { + SNDERR("failed to add data elem %s to byte control %s\n", + name->value.string, elem->id); + return NULL; + } + } + } + + tplg_dbg("Bytes: %s Ops info: %d get: %d put: %d", elem->id, hdr->ops.info, hdr->ops.get, + hdr->ops.put); + tplg_dbg("Ext Ops info: %d get: %d put: %d", be->ext_ops.info, be->ext_ops.get, be->ext_ops.put); + + /* set CTL access to default values if none provided */ + if (!access_set) { + be->hdr.access = SNDRV_CTL_ELEM_ACCESS_READWRITE; + if (tlv_set) + be->hdr.access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ; + } + + return elem; +} + static int tplg_create_widget_elem(snd_tplg_t *tplg, struct tplg_object *object) { struct tplg_comp_object *widget_object = &object->object_type.component; @@ -128,6 +442,61 @@ int tplg_build_comp_object(snd_tplg_t *tplg, struct tplg_object *object) } } + /* build controls */ + list_for_each(pos, &object->object_list) { + struct tplg_object *child = list_entry(pos, struct tplg_object, list); + struct tplg_elem *elem; + char *class_name = child->class_name; + + if (!strcmp(class_name, "mixer")) { + struct tplg_attribute *name_attr; + + /* skip if no name is provided */ + name_attr = tplg_get_attribute_by_name(&child->attribute_list, + "name"); + + if (!name_attr || !strcmp(name_attr->value.string, "")) + continue; + + elem = tplg_build_comp_mixer(tplg, child, name_attr->value.string); + if (!elem) { + SNDERR("Failed to build mixer control for %s\n", object->name); + return -EINVAL; + } + + ret = tplg_ref_add(w_elem, SND_TPLG_TYPE_MIXER, elem->id); + if (ret < 0) { + SNDERR("failed to add mixer elem %s to widget elem %s\n", + elem->id, w_elem->id); + return ret; + } + } + + if (!strcmp(class_name, "bytes")) { + struct tplg_attribute *name_attr; + + /* skip if no name is provided */ + name_attr = tplg_get_attribute_by_name(&child->attribute_list, + "name"); + + if (!name_attr || !strcmp(name_attr->value.string, "")) + continue; + + elem = tplg_build_comp_bytes(tplg, child, name_attr->value.string); + if (!elem) { + SNDERR("Failed to build bytes control for %s\n", object->name); + return -EINVAL; + } + + ret = tplg_ref_add(w_elem, SND_TPLG_TYPE_BYTES, elem->id); + if (ret < 0) { + SNDERR("failed to add bytes control elem %s to widget elem %s\n", + elem->id, w_elem->id); + return ret; + } + } + } + tplg_dbg("Widget: %s id: %d stream_name: %s no_pm: %d", w_elem->id, widget->id, widget->sname, widget->reg); From 51a628e148ffd33ba8db40d66e1f9bbc12a7a5b2 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 22 Mar 2021 11:37:17 -0700 Subject: [PATCH 26/33] topology2: add support for PCM group objects Add support for SND_TPLG_TYPE_STREAM_CAPS and SND_TPLG_TYPE_PCM type topology elements by parsing the PCM class objects and their attributes. Signed-off-by: Ranjani Sridharan --- src/topology/Makefile.am | 3 +- src/topology/object.c | 7 ++ src/topology/pcm-object.c | 232 +++++++++++++++++++++++++++++++++++++ src/topology/tplg2_local.h | 1 + 4 files changed, 242 insertions(+), 1 deletion(-) create mode 100644 src/topology/pcm-object.c diff --git a/src/topology/Makefile.am b/src/topology/Makefile.am index adebee8c0..f1c7b114b 100644 --- a/src/topology/Makefile.am +++ b/src/topology/Makefile.am @@ -33,7 +33,8 @@ libatopology_la_SOURCES =\ log.c \ class.c \ object.c \ - dapm-object.c + dapm-object.c \ + pcm-object.c noinst_HEADERS = tplg_local.h tplg2_local.h diff --git a/src/topology/object.c b/src/topology/object.c index e49d9b057..96ba1eb3e 100644 --- a/src/topology/object.c +++ b/src/topology/object.c @@ -1196,6 +1196,13 @@ static int tplg_build_object(snd_tplg_t *tplg, struct tplg_object *object) } break; } + case SND_TPLG_CLASS_TYPE_PCM: + ret = tplg_build_pcm_type_object(tplg, object); + if (ret < 0) { + SNDERR("Failed to build PCM class object %s\n", object->name); + return ret; + } + break; case SND_TPLG_CLASS_TYPE_BASE: ret = tplg_build_base_object(tplg, object); if (ret < 0) { diff --git a/src/topology/pcm-object.c b/src/topology/pcm-object.c new file mode 100644 index 000000000..74a26f778 --- /dev/null +++ b/src/topology/pcm-object.c @@ -0,0 +1,232 @@ +/* + Copyright(c) 2020-2021 Intel Corporation + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + Author: Ranjani Sridharan +*/ + +#include "list.h" +#include "local.h" +#include "tplg_local.h" +#include "tplg2_local.h" +#include + +int tplg_build_pcm_caps_object(snd_tplg_t *tplg, struct tplg_object *object) +{ + struct snd_soc_tplg_stream_caps *sc; + struct tplg_elem *elem; + struct list_head *pos; + char *pcm_caps_name; + int ret; + + /* drop the class name from the object name to extract the pcm caps name */ + pcm_caps_name = strchr(object->name, '.') + 1; + elem = tplg_elem_new_common(tplg, NULL, pcm_caps_name, SND_TPLG_TYPE_STREAM_CAPS); + if (!elem) + return -ENOMEM; + + tplg_dbg("PCM caps elem: %s", elem->id); + + sc = elem->stream_caps; + sc->size = elem->size; + snd_strlcpy(sc->name, elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + + list_for_each(pos, &object->attribute_list) { + struct tplg_attribute *attr = list_entry(pos, struct tplg_attribute, list); + + if (!strcmp(attr->name, "rate_min")) { + sc->rate_min = attr->value.integer; + continue; + } + + if (!strcmp(attr->name, "rate_max")) { + sc->rate_max = attr->value.integer; + continue; + } + + if (!strcmp(attr->name, "channels_min")) { + sc->channels_min = attr->value.integer; + continue; + } + + if (!strcmp(attr->name, "channels_max")) { + sc->channels_max = attr->value.integer; + continue; + } + + if (!attr->cfg) + continue; + + + ret = tplg_parse_stream_caps_param(attr->cfg, sc); + if (ret < 0) { + SNDERR("Failed to parse PCM caps %s\n", object->name); + return ret; + } + } + + return 0; +} + +static int tplg2_get_unsigned_attribute(struct tplg_attribute *arg, unsigned int *val, int base) +{ + const char *str; + long lval; + unsigned long uval; + + if (arg->type == SND_CONFIG_TYPE_INTEGER) { + lval = arg->value.integer; + if (lval < 0 && lval >= INT_MIN) + lval = UINT_MAX + lval + 1; + if (lval < 0 || lval > UINT_MAX) + return -ERANGE; + *val = lval; + return 0; + } + + if (arg->type == SND_CONFIG_TYPE_STRING) { + SNDERR("Invalid type for %s\n", arg->name); + return -EINVAL; + } + + str = strdup(arg->value.string); + + uval = strtoul(str, NULL, base); + if (errno == ERANGE && uval == ULONG_MAX) + return -ERANGE; + if (errno && uval == 0) + return -EINVAL; + if (uval > UINT_MAX) + return -ERANGE; + *val = uval; + + return 0; +} + +static struct tplg_elem* tplg2_lookup_pcm_by_name(snd_tplg_t *tplg, char *pcm_name) +{ + struct snd_soc_tplg_pcm *pcm; + struct list_head *pos; + + list_for_each(pos, &tplg->pcm_list) { + struct tplg_elem *elem = list_entry(pos, struct tplg_elem, list); + pcm = elem->pcm; + + if (!strcmp(pcm->pcm_name, pcm_name)) { + return elem; + } + } + + return NULL; +} + +static int tplg_build_pcm_object(snd_tplg_t *tplg, struct tplg_object *object) +{ + struct tplg_attribute *pcm_id; + struct tplg_attribute *name; + struct tplg_attribute *dir; + struct snd_soc_tplg_stream_caps *caps; + struct snd_soc_tplg_pcm *pcm; + struct tplg_elem *elem; + struct list_head *pos; + char *dai_name; + char *caps_name; + unsigned int dai_id; + int ret; + + dir = tplg_get_attribute_by_name(&object->attribute_list, "direction"); + name = tplg_get_attribute_by_name(&object->attribute_list, "pcm_name"); + pcm_id = tplg_get_attribute_by_name(&object->attribute_list, "pcm_id"); + caps_name = strchr(object->name, '.') + 1; + dai_name = strdup(name->value.string); + + /* check if pcm elem exists already */ + elem = tplg2_lookup_pcm_by_name(tplg, name->value.string); + if (!elem) { + elem = tplg_elem_new_common(tplg, NULL, name->value.string, SND_TPLG_TYPE_PCM); + if (!elem) + return -ENOMEM; + + pcm = elem->pcm; + pcm->size = elem->size; + + /* set PCM name */ + snd_strlcpy(pcm->pcm_name, name->value.string, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + } else { + pcm = elem->pcm; + } + + ret = tplg2_get_unsigned_attribute(pcm_id, &dai_id, 0); + if (ret < 0) { + SNDERR("Invalid value for PCM DAI ID"); + return ret; + } + + /*TODO: check if pcm_id and dai_id are always the same */ + pcm->pcm_id = dai_id; + unaligned_put32(&pcm->dai_id, dai_id); + + /* set dai name */ + snprintf(pcm->dai_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s %ld", dai_name, + pcm_id->value.integer); + free(dai_name); + + list_for_each(pos, &object->attribute_list) { + struct tplg_attribute *attr = list_entry(pos, struct tplg_attribute, list); + + if (!attr->cfg) + continue; + + ret = tplg_parse_pcm_param(tplg, attr->cfg, elem); + if (ret < 0) { + SNDERR("Failed to parse PCM %s\n", object->name); + return -EINVAL; + } + } + + caps = pcm->caps; + if (!strcmp(dir->value.string, "playback")) { + if (strcmp(caps[SND_SOC_TPLG_STREAM_PLAYBACK].name, "")) { + SNDERR("PCM Playback capabilities already set for %s\n", object->name); + return -EINVAL; + } + + unaligned_put32(&pcm->playback, 1); + snd_strlcpy(caps[SND_SOC_TPLG_STREAM_PLAYBACK].name, caps_name, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + } else { + if (strcmp(caps[SND_SOC_TPLG_STREAM_CAPTURE].name, "")) { + SNDERR("PCM Capture capabilities already set for %s\n", object->name); + return -EINVAL; + } + + snd_strlcpy(caps[SND_SOC_TPLG_STREAM_CAPTURE].name, caps_name, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + unaligned_put32(&pcm->capture, 1); + } + + tplg_dbg(" PCM: %s ID: %d dai_name: %s", pcm->pcm_name, pcm->dai_id, pcm->dai_name); + + return tplg_build_private_data(tplg, object); +} + +int tplg_build_pcm_type_object(snd_tplg_t *tplg, struct tplg_object *object) +{ + if (!strcmp(object->class_name, "pcm")) + return tplg_build_pcm_object(tplg, object); + + if (!strcmp(object->class_name, "pcm_caps")) + return tplg_build_pcm_caps_object(tplg, object); + + return 0; +} diff --git a/src/topology/tplg2_local.h b/src/topology/tplg2_local.h index 6fd6b0c43..4b715d56a 100644 --- a/src/topology/tplg2_local.h +++ b/src/topology/tplg2_local.h @@ -131,3 +131,4 @@ struct tplg_object *tplg_object_elem_lookup(snd_tplg_t *tplg, const char *class_ char *input); struct tplg_object *tplg_object_lookup_in_list(struct list_head *list, const char *class_name, char *input); +int tplg_build_pcm_type_object(snd_tplg_t *tplg, struct tplg_object *object); From 68d42ca81d02fff35d966a3c510a35a1e4edd2d7 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 22 Mar 2021 11:41:18 -0700 Subject: [PATCH 27/33] topology2: add BE DAI object build support Add support for building SND_TPLG_TYPE_BE type topology elements by parsing the SND_TPLG_CLASS_TYPE_DAI type objects and their attributes. Signed-off-by: Ranjani Sridharan --- src/topology/Makefile.am | 3 +- src/topology/dai-object.c | 180 +++++++++++++++++++++++++++++++++++++ src/topology/object.c | 15 +++- src/topology/tplg2_local.h | 3 + 4 files changed, 199 insertions(+), 2 deletions(-) create mode 100644 src/topology/dai-object.c diff --git a/src/topology/Makefile.am b/src/topology/Makefile.am index f1c7b114b..21b0f95c1 100644 --- a/src/topology/Makefile.am +++ b/src/topology/Makefile.am @@ -34,7 +34,8 @@ libatopology_la_SOURCES =\ class.c \ object.c \ dapm-object.c \ - pcm-object.c + pcm-object.c \ + dai-object.c noinst_HEADERS = tplg_local.h tplg2_local.h diff --git a/src/topology/dai-object.c b/src/topology/dai-object.c new file mode 100644 index 000000000..923453bf4 --- /dev/null +++ b/src/topology/dai-object.c @@ -0,0 +1,180 @@ +/* + Copyright(c) 2020-2021 Intel Corporation + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + Author: Ranjani Sridharan +*/ + +#include "list.h" +#include "local.h" +#include "tplg_local.h" +#include "tplg2_local.h" +#include + +int tplg_create_dai_object(struct tplg_class *class, struct tplg_object *object) +{ + struct list_head *pos; + + /* check if child objects are of the right type */ + list_for_each(pos, &class->object_list) { + struct tplg_object *obj = list_entry(pos, struct tplg_object, list); + + switch (obj->type) { + case SND_TPLG_CLASS_TYPE_BASE: + if (!strcmp(obj->class_name, "endpoint")) + break; + + SNDERR("Unexpected child class %s for dai %s\n", obj->class_name, + object->name); + return -EINVAL; + case SND_TPLG_CLASS_TYPE_COMPONENT: + break; + default: + SNDERR("Unexpected child type %d for %s\n", obj->type, object->name); + return -EINVAL; + } + } + + return 0; +} + +static int tplg_create_link_elem(snd_tplg_t *tplg, struct tplg_object *object) +{ + struct tplg_attribute *stream_name, *id; + struct tplg_attribute *default_hw_cfg; + struct tplg_dai_object *dai = &object->object_type.dai; + struct tplg_elem *link_elem, *data_elem; + struct snd_soc_tplg_link_config *link; + int ret; + + stream_name = tplg_get_attribute_by_name(&object->attribute_list, "stream_name"); + id = tplg_get_attribute_by_name(&object->attribute_list, "id"); + default_hw_cfg = tplg_get_attribute_by_name(&object->attribute_list, "default_hw_config"); + + if (!stream_name || stream_name->type != SND_CONFIG_TYPE_STRING) { + SNDERR("No DAI name for %s\n", object->name); + return -EINVAL; + } + + link_elem = tplg_elem_new_common(tplg, NULL, stream_name->value.string, SND_TPLG_TYPE_BE); + if (!link_elem) + return -ENOMEM; + dai->link_elem = link_elem; + + link = link_elem->link; + link->size = link_elem->size; + snd_strlcpy(link->name, link_elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + link->default_hw_config_id = default_hw_cfg->value.integer; + link->id = id->value.integer; + + /* create data elem for link */ + data_elem = tplg_elem_new_common(tplg, NULL, object->name, SND_TPLG_TYPE_DATA); + if (!data_elem) + return -ENOMEM; + + ret = tplg_ref_add(link_elem, SND_TPLG_TYPE_DATA, data_elem->id); + if (ret < 0) { + SNDERR("failed to add data elem %s to link elem %s\n", data_elem->id, + link_elem->id); + return ret; + } + + return 0; +} + +int tplg_build_dai_object(snd_tplg_t *tplg, struct tplg_object *object) +{ + struct tplg_dai_object *dai = &object->object_type.dai; + struct snd_soc_tplg_link_config *link; + struct tplg_elem *l_elem; + struct list_head *pos, *_pos; + int i = 0; + int ret; + + ret = tplg_create_link_elem(tplg, object); + if (ret < 0) { + SNDERR("Failed to create widget elem for object\n", object->name); + return ret; + } + l_elem = dai->link_elem; + link = l_elem->link; + + list_for_each(pos, &object->object_list) { + struct tplg_object *child = list_entry(pos, struct tplg_object, list); + struct list_head *pos1; + + if (!strcmp(child->class_name, "hw_config")) { + struct tplg_attribute *id; + struct snd_soc_tplg_hw_config *hw_cfg = &link->hw_config[i++]; + + /* set hw_config ID */ + id = tplg_get_attribute_by_name(&child->attribute_list, "id"); + if (!id || id->type != SND_CONFIG_TYPE_INTEGER) { + SNDERR("No ID for hw_config %s\n", child->name); + return -EINVAL; + } + hw_cfg->id = id->value.integer; + + /* parse hw_config params from attributes */ + list_for_each(pos1, &child->attribute_list) { + struct tplg_attribute *attr; + + attr = list_entry(pos1, struct tplg_attribute, list); + if (!attr->cfg) + continue; + + ret = tplg_set_hw_config_param(attr->cfg, hw_cfg); + if (ret < 0) { + SNDERR("Error parsing hw_config for object %s\n", + object->name); + return ret; + } + } + tplg_dbg("HW Config: %d", hw_cfg->id); + } + + if (!strcmp(child->class_name, "pdm_config")) { + /* build tuple sets for pdm_config object */ + ret = tplg_build_object_tuple_sets(child); + if (ret < 0) + return ret; + + list_for_each_safe(pos1, _pos, &child->tuple_set_list) { + struct tplg_tuple_set *set; + set = list_entry(pos1, struct tplg_tuple_set, list); + list_del(&set->list); + list_add_tail(&set->list, &object->tuple_set_list); + } + } + } + + /* parse link params from attributes */ + list_for_each(pos, &object->attribute_list) { + struct tplg_attribute *attr = list_entry(pos, struct tplg_attribute, list); + + if (!attr->cfg) + continue; + + ret = tplg_parse_link_param(tplg, attr->cfg, link, NULL); + if (ret < 0) { + SNDERR("Error parsing hw_config for object %s\n", + object->name); + return ret; + } + } + + link->num_hw_configs = i; + tplg_dbg("Link elem: %s num_hw_configs: %d", l_elem->id, link->num_hw_configs); + + return tplg_build_private_data(tplg, object); +} diff --git a/src/topology/object.c b/src/topology/object.c index 96ba1eb3e..f48ed8ad6 100644 --- a/src/topology/object.c +++ b/src/topology/object.c @@ -15,7 +15,6 @@ Author: Ranjani Sridharan */ #include "list.h" -#include "local.h" #include "tplg_local.h" #include "tplg2_local.h" #include @@ -783,6 +782,13 @@ tplg_create_object(snd_tplg_t *tplg, snd_config_t *cfg, struct tplg_class *class /* sanity check */ switch(object->type) { + case SND_TPLG_CLASS_TYPE_DAI: + ret = tplg_create_dai_object(class, object); + if (ret < 0) { + SNDERR("Failed to create DAI object for %s\n", object->name); + return NULL; + } + break; case SND_TPLG_CLASS_TYPE_COMPONENT: ret = tplg_create_component_object(object); if (ret < 0) { @@ -1196,6 +1202,13 @@ static int tplg_build_object(snd_tplg_t *tplg, struct tplg_object *object) } break; } + case SND_TPLG_CLASS_TYPE_DAI: + ret = tplg_build_dai_object(tplg, object); + if (ret < 0) { + SNDERR("Failed to build DAI object %s\n", object->name); + return ret; + } + break; case SND_TPLG_CLASS_TYPE_PCM: ret = tplg_build_pcm_type_object(tplg, object); if (ret < 0) { diff --git a/src/topology/tplg2_local.h b/src/topology/tplg2_local.h index 4b715d56a..6f2e35df1 100644 --- a/src/topology/tplg2_local.h +++ b/src/topology/tplg2_local.h @@ -125,6 +125,9 @@ tplg_create_object(snd_tplg_t *tplg, snd_config_t *cfg, struct tplg_class *class struct tplg_attribute *tplg_get_attribute_by_name(struct list_head *list, const char *name); int tplg_build_private_data(snd_tplg_t *tplg, struct tplg_object *object); int tplg_build_comp_object(snd_tplg_t *tplg, struct tplg_object *object); +int tplg_build_dai_object(snd_tplg_t *tplg, struct tplg_object *object); +int tplg_build_object_tuple_sets(struct tplg_object *object); +int tplg_create_dai_object(struct tplg_class *class, struct tplg_object *object); int tplg_create_component_object(struct tplg_object *object); void tplg2_free_elem_object(struct tplg_elem *elem); struct tplg_object *tplg_object_elem_lookup(snd_tplg_t *tplg, const char *class_name, From c53b81eda047483420244e5afb34bd2aa882d750 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 11 Mar 2021 23:09:03 -0800 Subject: [PATCH 28/33] topology: object: add support for pipeline type objects Sanity check while creating SND_TPLG_CLASS_TYPE_PIPELINE type objects. Signed-off-by: Ranjani Sridharan --- src/topology/Makefile.am | 3 +- src/topology/custom-object.c | 55 ++++++++++++++++++++++++++++++++++++ src/topology/object.c | 7 +++++ src/topology/tplg2_local.h | 1 + 4 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 src/topology/custom-object.c diff --git a/src/topology/Makefile.am b/src/topology/Makefile.am index 21b0f95c1..cbc2360f9 100644 --- a/src/topology/Makefile.am +++ b/src/topology/Makefile.am @@ -35,7 +35,8 @@ libatopology_la_SOURCES =\ object.c \ dapm-object.c \ pcm-object.c \ - dai-object.c + dai-object.c \ + custom-object.c noinst_HEADERS = tplg_local.h tplg2_local.h diff --git a/src/topology/custom-object.c b/src/topology/custom-object.c new file mode 100644 index 000000000..0d26a20b7 --- /dev/null +++ b/src/topology/custom-object.c @@ -0,0 +1,55 @@ +/* + Copyright(c) 2020-2021 Intel Corporation + All rights reserved. + + This library is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + Author: Ranjani Sridharan +*/ + +/* + * This file contains the create/build routines for custom classes that are not + * DAI, component or PCM type + */ +#include "list.h" +#include "local.h" +#include "tplg_local.h" +#include "tplg2_local.h" +#include + +/* pipeline object customization */ +int tplg_create_pipeline_object(struct tplg_class *class, struct tplg_object *object) +{ + struct list_head *pos; + + /* check if child objects are of the right type */ + list_for_each(pos, &class->object_list) { + struct tplg_object *obj = list_entry(pos, struct tplg_object, list); + + switch (obj->type) { + case SND_TPLG_CLASS_TYPE_BASE: + if (!strcmp(obj->class_name, "connection") || + !strcmp(obj->class_name, "endpoint")) + break; + SNDERR("Unexpected child class %s for pipeline %s\n", obj->class_name, + object->name); + return -EINVAL; + case SND_TPLG_CLASS_TYPE_COMPONENT: + case SND_TPLG_CLASS_TYPE_PCM: + break; + default: + SNDERR("Unexpected child object type %d for %s\n", obj->type, object->name); + return -EINVAL; + } + } + + return 0; +} diff --git a/src/topology/object.c b/src/topology/object.c index f48ed8ad6..c8cd93575 100644 --- a/src/topology/object.c +++ b/src/topology/object.c @@ -782,6 +782,13 @@ tplg_create_object(snd_tplg_t *tplg, snd_config_t *cfg, struct tplg_class *class /* sanity check */ switch(object->type) { + case SND_TPLG_CLASS_TYPE_PIPELINE: + ret = tplg_create_pipeline_object(class, object); + if (ret < 0) { + SNDERR("Failed to create pipeline object for %s\n", object->name); + return NULL; + } + break; case SND_TPLG_CLASS_TYPE_DAI: ret = tplg_create_dai_object(class, object); if (ret < 0) { diff --git a/src/topology/tplg2_local.h b/src/topology/tplg2_local.h index 6f2e35df1..22c97841c 100644 --- a/src/topology/tplg2_local.h +++ b/src/topology/tplg2_local.h @@ -135,3 +135,4 @@ struct tplg_object *tplg_object_elem_lookup(snd_tplg_t *tplg, const char *class_ struct tplg_object *tplg_object_lookup_in_list(struct list_head *list, const char *class_name, char *input); int tplg_build_pcm_type_object(snd_tplg_t *tplg, struct tplg_object *object); +int tplg_create_pipeline_object(struct tplg_class *class, struct tplg_object *object); From d17cf5d7ae94c3c6b72b27e1f9908cddf2ba9ef1 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 22 Mar 2021 12:06:10 -0700 Subject: [PATCH 29/33] topology2: add support for building DAPM routes Add support for building SND_TPLG_TYPE_DAPM_GRAPH type topology elements by pasring the objects of class "connection" and their attributes. Signed-off-by: Ranjani Sridharan --- src/topology/dapm-object.c | 68 ++++++++++++++++++++++++++++++++++++++ src/topology/object.c | 4 +++ src/topology/tplg2_local.h | 1 + 3 files changed, 73 insertions(+) diff --git a/src/topology/dapm-object.c b/src/topology/dapm-object.c index 820a810d5..d21f14c06 100644 --- a/src/topology/dapm-object.c +++ b/src/topology/dapm-object.c @@ -43,6 +43,74 @@ int tplg_create_component_object(struct tplg_object *object) return 0; } +static int tplg_dapm_route_validate_widget(snd_tplg_t *tplg, char *wname, char *dest) +{ + struct tplg_elem *w_elem; + + /* check if it is a valid widget */ + w_elem = tplg_elem_lookup(&tplg->widget_list, wname, + SND_TPLG_TYPE_DAPM_WIDGET, SND_TPLG_INDEX_ALL); + if (!w_elem) { + SNDERR("No widget %s found\n", wname); + return -EINVAL; + } + + snd_strlcpy(dest, w_elem->id, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + + return 0; +} + +int tplg_build_dapm_route(snd_tplg_t *tplg, struct tplg_object *object) +{ + struct snd_soc_tplg_dapm_graph_elem *line; + struct list_head *pos; + struct tplg_elem *elem; + int ret; + + /* create graph elem */ + elem = tplg_elem_new_route(tplg, 0); + if (!elem) + return -ENOMEM; + + line = elem->route; + + /* set graph elem index and control values */ + list_for_each(pos, &object->attribute_list) { + struct tplg_attribute *attr = list_entry(pos, struct tplg_attribute, list); + + if (!strcmp(attr->name, "pipeline_id")) { + elem->index = attr->value.integer; + continue; + } + + if (!strcmp(attr->name, "control")) + snd_strlcpy(line->control, attr->value.string, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + + if (!strcmp(attr->name, "source_widget")) { + ret = tplg_dapm_route_validate_widget(tplg, attr->value.string, + line->source); + if (ret < 0) { + SNDERR("Failed to find source widget for route %s\n", object->name); + return ret; + } + } + + if (!strcmp(attr->name, "sink_widget")) { + ret = tplg_dapm_route_validate_widget(tplg, attr->value.string, + line->sink); + if (ret < 0) { + SNDERR("Failed to find sink widget for route %s\n", object->name); + return ret; + } + } + } + + tplg_dbg("DAPM route: %s -> %s", line->source, line->sink); + + return 0; +} + static int tplg2_parse_channel(struct tplg_object *object, struct tplg_elem *mixer_elem) { struct snd_soc_tplg_mixer_control *mc = mixer_elem->mixer_ctrl; diff --git a/src/topology/object.c b/src/topology/object.c index c8cd93575..f77b561dc 100644 --- a/src/topology/object.c +++ b/src/topology/object.c @@ -1191,6 +1191,10 @@ static int tplg_build_base_object(snd_tplg_t *tplg, struct tplg_object *object) if (!strcmp(object->class_name, "data")) return tplg_build_data_object(tplg, object); + if (!strcmp(object->class_name, "connection")) + return tplg_build_dapm_route(tplg, object); + + return 0; } diff --git a/src/topology/tplg2_local.h b/src/topology/tplg2_local.h index 22c97841c..a6adab3ac 100644 --- a/src/topology/tplg2_local.h +++ b/src/topology/tplg2_local.h @@ -136,3 +136,4 @@ struct tplg_object *tplg_object_lookup_in_list(struct list_head *list, const cha char *input); int tplg_build_pcm_type_object(snd_tplg_t *tplg, struct tplg_object *object); int tplg_create_pipeline_object(struct tplg_class *class, struct tplg_object *object); +int tplg_build_dapm_route(snd_tplg_t *tplg, struct tplg_object *object); From b6a034d02fb5a344c9c92297fa325e0edc9dfd06 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 22 Mar 2021 16:56:11 -0700 Subject: [PATCH 30/33] topology2: update automatic attributes for host class Attributes have a constraint called TPLG_CLASS_ATTRIBUTE_MASK_AUTOMATIC that allows the values to be updated by the alsatplg compiler when the object containing the attribute is built. One example of an automatic attribute is the "stream_name" attribute for "host" and "copier" class objects. Add the logic to update the stream name for such objects. Signed-off-by: Ranjani Sridharan --- src/topology/custom-object.c | 43 ++++++++++++++++++++++++++++++++++++ src/topology/object.c | 7 ++++++ src/topology/tplg2_local.h | 2 ++ 3 files changed, 52 insertions(+) diff --git a/src/topology/custom-object.c b/src/topology/custom-object.c index 0d26a20b7..cc95038a0 100644 --- a/src/topology/custom-object.c +++ b/src/topology/custom-object.c @@ -25,6 +25,26 @@ #include "tplg2_local.h" #include +static void tplg_set_stream_name(struct tplg_object *object) +{ + struct tplg_attribute *pcm_name, *pcm_id, *dir, *stream_name; + int ret; + + pcm_name = tplg_get_attribute_by_name(&object->attribute_list, "pcm_name"); + pcm_id = tplg_get_attribute_by_name(&object->attribute_list, "pcm_id"); + dir = tplg_get_attribute_by_name(&object->attribute_list, "direction"); + stream_name = tplg_get_attribute_by_name(&object->attribute_list, "stream_name"); + + ret = snprintf(stream_name->value.string, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, + "%s.%s.%ld", pcm_name->value.string, + dir->value.string, pcm_id->value.integer); + if (ret > SNDRV_CTL_ELEM_ID_NAME_MAXLEN) + SNDERR("warning: widget stream name truncated \n"); + + stream_name->found = true; + stream_name->type = SND_CONFIG_TYPE_STRING; +} + /* pipeline object customization */ int tplg_create_pipeline_object(struct tplg_class *class, struct tplg_object *object) { @@ -53,3 +73,26 @@ int tplg_create_pipeline_object(struct tplg_class *class, struct tplg_object *ob return 0; } + +int tplg_update_automatic_attributes(snd_tplg_t *tplg, struct tplg_object *object, + struct tplg_object *parent) +{ + struct list_head *pos; + int ret; + + if (!strcmp(object->class_name, "host") || + !strcmp(object->class_name, "copier")) { + tplg_set_stream_name(object); + } + + /* now update all automatic attributes for all child objects */ + list_for_each(pos, &object->object_list) { + struct tplg_object *child = list_entry(pos, struct tplg_object, list); + + ret = tplg_update_automatic_attributes(tplg, child, object); + if (ret < 0) + return ret; + } + + return 0; +} diff --git a/src/topology/object.c b/src/topology/object.c index f77b561dc..67c429988 100644 --- a/src/topology/object.c +++ b/src/topology/object.c @@ -835,6 +835,13 @@ tplg_create_object(snd_tplg_t *tplg, snd_config_t *cfg, struct tplg_class *class return NULL; } + /* update automatic attributes in object */ + ret = tplg_update_automatic_attributes(tplg, object, parent); + if (ret < 0) { + SNDERR("failed to update automatic attributes for %s\n", object->name); + return NULL; + } + if (list) list_add_tail(&object->list, list); diff --git a/src/topology/tplg2_local.h b/src/topology/tplg2_local.h index a6adab3ac..764d2aa96 100644 --- a/src/topology/tplg2_local.h +++ b/src/topology/tplg2_local.h @@ -137,3 +137,5 @@ struct tplg_object *tplg_object_lookup_in_list(struct list_head *list, const cha int tplg_build_pcm_type_object(snd_tplg_t *tplg, struct tplg_object *object); int tplg_create_pipeline_object(struct tplg_class *class, struct tplg_object *object); int tplg_build_dapm_route(snd_tplg_t *tplg, struct tplg_object *object); +int tplg_update_automatic_attributes(snd_tplg_t *tplg, struct tplg_object *object, + struct tplg_object *parent); From 71837152ac5c139198302a0a4cdc407bbbe513e5 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 22 Mar 2021 17:00:55 -0700 Subject: [PATCH 31/33] topology2: update automatic attributes for buffer class The buffer class's "size" attribute is an autmatic attribute. It needs to computed using the pipeline attributes along with some of the buffer object attributes. Add support for updating it. Signed-off-by: Ranjani Sridharan --- src/topology/custom-object.c | 122 +++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) diff --git a/src/topology/custom-object.c b/src/topology/custom-object.c index cc95038a0..ea994de56 100644 --- a/src/topology/custom-object.c +++ b/src/topology/custom-object.c @@ -74,6 +74,120 @@ int tplg_create_pipeline_object(struct tplg_class *class, struct tplg_object *ob return 0; } +static int tplg_get_sample_size_from_format(char *format) +{ + if (!strcmp(format, "s32le") || !strcmp(format, "s24le") || !strcmp(format, "float") ) + return 4; + + if (!strcmp(format, "s16le")) + return 2; + + return -EINVAL; +} + +static int tplg_update_buffer_size(struct tplg_object *buffer_object, + struct tplg_object *pipeline_object) +{ + struct list_head *pos; + struct tplg_attribute *size_attribute = NULL; + char pipeline_format[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + int periods = 0; + int sample_size; + int channels = 0; + int frames = 0; + int rate = 0; + int schedule_period = 0; + + /* get periods and channels from buffer object */ + list_for_each(pos, &buffer_object->attribute_list) { + struct tplg_attribute *attr = list_entry(pos, struct tplg_attribute, list); + + if (!strcmp(attr->name, "periods")) { + if (attr->type == SND_CONFIG_TYPE_INTEGER) { + periods = attr->value.integer; + } else { + SNDERR("Invalid value for periods for object %s \n", + buffer_object->name); + return -EINVAL; + } + } + + if (!strcmp(attr->name, "channels")) { + if (attr->type == SND_CONFIG_TYPE_INTEGER) { + channels = attr->value.integer; + } else { + SNDERR("Invalid value for channels for object %s \n", + buffer_object->name); + return -EINVAL; + } + } + + if (!strcmp(attr->name, "size")) + size_attribute = attr; + } + + if (!size_attribute) { + SNDERR("Can't find size attribute for %s\n", buffer_object->name); + return -EINVAL; + } + + /* get schedule_period, channels, rate and format from pipeline object */ + list_for_each(pos, &pipeline_object->attribute_list) { + struct tplg_attribute *attr = list_entry(pos, struct tplg_attribute, list); + + if (!strcmp(attr->name, "period")) { + if (attr->type == SND_CONFIG_TYPE_INTEGER) { + schedule_period = attr->value.integer; + } else { + SNDERR("Invalid value for period for object %s \n", + pipeline_object->name); + return -EINVAL; + } + } + + if (!strcmp(attr->name, "rate")) { + if (attr->type == SND_CONFIG_TYPE_INTEGER) { + rate = attr->value.integer; + } else { + SNDERR("Invalid value for rate for object %s \n", + pipeline_object->name); + return -EINVAL; + } + } + + if (!strcmp(attr->name, "format")) { + if (attr->type == SND_CONFIG_TYPE_STRING) { + snd_strlcpy(pipeline_format, attr->value.string, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + } else { + SNDERR("Invalid format for pipeline %s \n", + pipeline_object->name); + return -EINVAL; + } + } + } + + sample_size = tplg_get_sample_size_from_format(pipeline_format); + if (sample_size < 0) { + SNDERR("Invalid value for sample size for object %s \n", pipeline_object->name); + return sample_size; + } + + /* compute buffer size */ + frames = (rate * schedule_period)/1000000; + size_attribute->value.integer = periods * sample_size * channels * frames; + if (!size_attribute->value.integer) { + SNDERR("Invalid buffer size %d for %s \n",size_attribute->value.integer, + buffer_object->name); + return -EINVAL; + } + + size_attribute->found = true; + size_attribute->type = SND_CONFIG_TYPE_INTEGER; + + return 0; +} + int tplg_update_automatic_attributes(snd_tplg_t *tplg, struct tplg_object *object, struct tplg_object *parent) { @@ -85,6 +199,14 @@ int tplg_update_automatic_attributes(snd_tplg_t *tplg, struct tplg_object *objec tplg_set_stream_name(object); } + if (!strcmp(object->class_name, "buffer")) { + if (parent) { + ret = tplg_update_buffer_size(object, parent); + if (ret < 0) + return 0; + } + } + /* now update all automatic attributes for all child objects */ list_for_each(pos, &object->object_list) { struct tplg_object *child = list_entry(pos, struct tplg_object, list); From a95ea5e7465b824bb809bb47e49c23d010e953ef Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Mon, 22 Mar 2021 17:05:30 -0700 Subject: [PATCH 32/33] topology: update automatic attribute for endpoint class Endpoint class' "widget_name" attribute is an automatic attribute. It's value should be set after resolving the object reference in the "widget" attribute. If the "widget" attribute value does not contain the "Object." prefix, its value is simply copied to the widget_name attribute. Signed-off-by: Ranjani Sridharan --- src/topology/custom-object.c | 84 ++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/src/topology/custom-object.c b/src/topology/custom-object.c index ea994de56..6ec9a3fed 100644 --- a/src/topology/custom-object.c +++ b/src/topology/custom-object.c @@ -19,6 +19,8 @@ * This file contains the create/build routines for custom classes that are not * DAI, component or PCM type */ +#define TPLG_DEBUG + #include "list.h" #include "local.h" #include "tplg_local.h" @@ -188,12 +190,94 @@ static int tplg_update_buffer_size(struct tplg_object *buffer_object, return 0; } +/* + * Widget names for pipeline endpoints can be of the following type: + * "Object.class.index" which refers to an object of class "class" with index in the + * parent object_list or the global topology object_list + */ +static int tplg_set_widget_name(snd_tplg_t *tplg, struct tplg_object *object, + struct tplg_object *parent, char *string, + struct tplg_attribute *dest_widget) +{ + struct tplg_object *child = NULL; + char *object_str, *last_dot; + char class_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN], *index_str; + + /* strip "Object." from the string */ + object_str = strchr(string, '.'); + if (!object_str) { + SNDERR("Incomplete name for source object in route %s for parent %s\n", + object->name, parent->name); + return -EINVAL; + } + + /* get last occurence of '.' */ + last_dot = strrchr(string, '.'); + + /* get index of object */ + index_str = strchr(object_str + 1, '.'); + if (!index_str) { + SNDERR("No unique attribute for object in route %s for parent %s\n", + object->name, parent->name); + return 0; + } + + /* get class name */ + snd_strlcpy(class_name, object_str + 1, strlen(object_str) - strlen(index_str)); + + + /* look up object from parent object_list */ + if (parent) + child = tplg_object_lookup_in_list(&parent->object_list, class_name, + index_str + 1); + else + /* look up object from global list */ + child = tplg_object_elem_lookup(tplg, class_name, index_str + 1); + + if (!child) { + SNDERR("No object %s.%s found in parent %s\n", + class_name, index_str, object->name, parent->name); + return -EINVAL; + } + + /* end of string? */ + if (last_dot != index_str) { + char *str = strchr(index_str + 1, '.'); + char new_str[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + + snprintf(new_str, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s%s", "Object", str); + + return tplg_set_widget_name(tplg, object, child, new_str, dest_widget); + } + + snd_strlcpy(dest_widget->value.string, child->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + + return 0; +} + + int tplg_update_automatic_attributes(snd_tplg_t *tplg, struct tplg_object *object, struct tplg_object *parent) { struct list_head *pos; int ret; + /* set widget name for pipeline endpoint objects */ + if (!strcmp(object->class_name, "endpoint")) { + struct tplg_attribute *widget_name, *widget; + + widget = tplg_get_attribute_by_name(&object->attribute_list, "widget"); + widget_name = tplg_get_attribute_by_name(&object->attribute_list, "widget_name"); + ret = tplg_set_widget_name(tplg, object, parent, widget->value.string, + widget_name); + if (ret < 0) { + SNDERR("Failed to set source widget name for %s\n", object->name); + return ret; + } + + tplg_dbg("endpoint widget name %s", widget_name->value.string); + } + if (!strcmp(object->class_name, "host") || !strcmp(object->class_name, "copier")) { tplg_set_stream_name(object); From e1091b6351dbb0be49059192f0617bc47fc22152 Mon Sep 17 00:00:00 2001 From: Ranjani Sridharan Date: Thu, 11 Mar 2021 23:14:47 -0800 Subject: [PATCH 33/33] topology: update automatic attributes for connection class The source_widget and sink_widget attributes in the connection class are automatic attributes. Their values need to be updated based on the "source" and "sink" attribute values. Signed-off-by: Ranjani Sridharan --- src/topology/custom-object.c | 51 ++++++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/src/topology/custom-object.c b/src/topology/custom-object.c index 6ec9a3fed..ba4c6584a 100644 --- a/src/topology/custom-object.c +++ b/src/topology/custom-object.c @@ -191,7 +191,7 @@ static int tplg_update_buffer_size(struct tplg_object *buffer_object, } /* - * Widget names for pipeline endpoints can be of the following type: + * Widget names for route source/sink or pipeline endpoints can be of the following type: * "Object.class.index" which refers to an object of class "class" with index in the * parent object_list or the global topology object_list */ @@ -250,11 +250,51 @@ static int tplg_set_widget_name(snd_tplg_t *tplg, struct tplg_object *object, return tplg_set_widget_name(tplg, object, child, new_str, dest_widget); } - snd_strlcpy(dest_widget->value.string, child->name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + /* for endpoint objects, gets the widget name */ + if (!strcmp(child->class_name, "endpoint")) { + struct tplg_attribute *widget_name; + + widget_name = tplg_get_attribute_by_name(&child->attribute_list, "widget_name"); + snd_strlcpy(dest_widget->value.string, widget_name->value.string, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + } else { + snd_strlcpy(dest_widget->value.string, child->name, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN); + } return 0; } +/* Update Object references for source/sink in route objects */ +static int tplg_update_route_object(snd_tplg_t *tplg, struct tplg_object *object, + struct tplg_object *parent) +{ + struct tplg_attribute *widget, *src_widget_name, *sink_widget_name; + int ret; + + /* set source widget name */ + widget = tplg_get_attribute_by_name(&object->attribute_list, "source"); + src_widget_name = tplg_get_attribute_by_name(&object->attribute_list, "source_widget"); + ret = tplg_set_widget_name(tplg, object, parent, widget->value.string, + src_widget_name); + if (ret < 0) { + SNDERR("Failed to set source widget name for %s\n", object->name); + return ret; + } + + /* set sink widget name */ + widget = tplg_get_attribute_by_name(&object->attribute_list, "sink"); + sink_widget_name = tplg_get_attribute_by_name(&object->attribute_list, "sink_widget"); + ret = tplg_set_widget_name(tplg, object, parent, widget->value.string, + sink_widget_name); + if (ret < 0) + SNDERR("Failed to set sink widget name for %s\n", object->name); + + tplg_dbg("route: source: %s -> sink: %s", src_widget_name->value.string, + sink_widget_name->value.string); + + return ret; +} int tplg_update_automatic_attributes(snd_tplg_t *tplg, struct tplg_object *object, struct tplg_object *parent) @@ -262,6 +302,13 @@ int tplg_update_automatic_attributes(snd_tplg_t *tplg, struct tplg_object *objec struct list_head *pos; int ret; + /* set source/sink widget names for routes */ + if (!strcmp(object->class_name, "connection")) { + ret = tplg_update_route_object(tplg, object, parent); + if (ret < 0) + return ret; + } + /* set widget name for pipeline endpoint objects */ if (!strcmp(object->class_name, "endpoint")) { struct tplg_attribute *widget_name, *widget;