@@ -19,6 +19,7 @@ enum avs_tplg_token {
AVS_TKN_MANIFEST_NUM_MODCFGS_EXT_U32 = 6,
AVS_TKN_MANIFEST_NUM_PPLCFGS_U32 = 7,
AVS_TKN_MANIFEST_NUM_BINDINGS_U32 = 8,
+ AVS_TKN_MANIFEST_NUM_CONDPATH_TMPLS_U32 = 9,
/* struct avs_tplg_library */
AVS_TKN_LIBRARY_ID_U32 = 101,
@@ -122,6 +123,21 @@ enum avs_tplg_token {
AVS_TKN_PATH_FE_FMT_ID_U32 = 1902,
AVS_TKN_PATH_BE_FMT_ID_U32 = 1903,
+ /* struct avs_tplg_path_template (conditional) */
+ AVS_TKN_CONDPATH_TMPL_ID_U32 = 1801,
+ AVS_TKN_CONDPATH_TMPL_SOURCE_TPLG_NAME_STRING = 2002,
+ AVS_TKN_CONDPATH_TMPL_SOURCE_PATH_TMPL_ID_U32 = 2003,
+ AVS_TKN_CONDPATH_TMPL_SINK_TPLG_NAME_STRING = 2004,
+ AVS_TKN_CONDPATH_TMPL_SINK_PATH_TMPL_ID_U32 = 2005,
+ AVS_TKN_CONDPATH_TMPL_COND_TYPE_U32 = 2006,
+ AVS_TKN_CONDPATH_TMPL_OVERRIDABLE_BOOL = 2007,
+ AVS_TKN_CONDPATH_TMPL_PRIORITY_U8 = 2008,
+
+ /* struct avs_tplg_path (conditional) */
+ AVS_TKN_CONDPATH_ID_U32 = 1901,
+ AVS_TKN_CONDPATH_SOURCE_PATH_ID_U32 = 2102,
+ AVS_TKN_CONDPATH_SINK_PATH_ID_U32 = 2103,
+
/* struct avs_tplg_pin_format */
AVS_TKN_PIN_FMT_INDEX_U32 = 2201,
AVS_TKN_PIN_FMT_IOBS_U32 = 2202,
@@ -113,6 +113,79 @@ avs_path_find_variant(struct avs_dev *adev,
return NULL;
}
+static struct avs_tplg_path *
+avs_condpath_find_variant(struct avs_dev *adev,
+ struct avs_tplg_path_template *template,
+ struct avs_path *source, struct avs_path *sink)
+{
+ struct avs_tplg_path *variant;
+
+ list_for_each_entry(variant, &template->path_list, node) {
+ if (variant->source_path_id == source->template->id &&
+ variant->sink_path_id == sink->template->id)
+ return variant;
+ }
+
+ return NULL;
+}
+
+static bool avs_tplg_path_template_id_equal(struct avs_tplg_path_template_id *id,
+ struct avs_tplg_path_template_id *id2)
+{
+ return id->id == id2->id && !strcmp(id->tplg_name, id2->tplg_name);
+}
+
+static struct avs_path *
+avs_condpath_find_match(struct avs_dev *adev,
+ struct avs_tplg_path_template *template,
+ struct avs_path *path, int dir)
+{
+ struct avs_tplg_path_template_id *id, *id2;
+
+ if (dir) {
+ id = &template->source;
+ id2 = &template->sink;
+ } else {
+ id = &template->sink;
+ id2 = &template->source;
+ }
+
+ /* Check whether this path is either source or sink of condpath template. */
+ if (id->id != path->template->owner->id ||
+ strcmp(id->tplg_name, path->template->owner->owner->name))
+ return NULL;
+
+ /* Unidirectional condpaths are allowed. */
+ if (avs_tplg_path_template_id_equal(id, id2))
+ return path;
+
+ /* Now find the counterpart */
+ return avs_path_find_path(adev, id2->tplg_name, id2->id);
+}
+
+static struct avs_path *
+avs_condpath_find_conflict(struct avs_dev *adev, u32 cond_type,
+ struct avs_path *path, int dir)
+{
+ struct avs_path *pos, *target;
+
+ if (cond_type != AVS_COND_TYPE_NONE) {
+ spin_lock(&adev->path_list_lock);
+ list_for_each_entry(pos, &adev->path_list, node) {
+ if (pos->template->owner->cond_type != cond_type)
+ continue;
+ target = dir ? pos->source : pos->sink;
+ if (path == target) {
+ spin_unlock(&adev->path_list_lock);
+ return pos;
+ }
+ }
+ spin_unlock(&adev->path_list_lock);
+ }
+
+ return NULL;
+}
+
__maybe_unused
static bool avs_dma_type_is_host(u32 dma_type)
{
@@ -724,6 +797,10 @@ static int avs_path_init(struct avs_dev *adev, struct avs_path *path,
path->dma_id = dma_id;
INIT_LIST_HEAD(&path->ppl_list);
INIT_LIST_HEAD(&path->node);
+ INIT_LIST_HEAD(&path->source_list);
+ INIT_LIST_HEAD(&path->sink_list);
+ INIT_LIST_HEAD(&path->source_node);
+ INIT_LIST_HEAD(&path->sink_node);
/* create all the pipelines */
list_for_each_entry(tppl, &template->ppl_list, node) {
@@ -810,6 +887,162 @@ static struct avs_path *avs_path_create_unlocked(struct avs_dev *adev, u32 dma_i
return ERR_PTR(ret);
}
+static void avs_condpath_free(struct avs_dev *adev, struct avs_path *path)
+{
+ int ret;
+
+ list_del(&path->source_node);
+ list_del(&path->sink_node);
+
+ ret = avs_path_reset(path);
+ if (ret < 0)
+ dev_err(adev->dev, "reset condpath failed: %d\n", ret);
+
+ ret = avs_path_unbind(path);
+ if (ret < 0)
+ dev_err(adev->dev, "unbind condpath failed: %d\n", ret);
+
+ avs_path_free_unlocked(path);
+}
+
+static struct avs_path *avs_condpath_create(struct avs_dev *adev, u32 dma_id,
+ struct avs_tplg_path *template,
+ struct avs_path *source,
+ struct avs_path *sink)
+{
+ struct avs_path *path;
+ int ret;
+
+ path = avs_path_create_unlocked(adev, dma_id, template);
+ if (IS_ERR(path))
+ return path;
+
+ ret = avs_path_bind(path);
+ if (ret)
+ goto err;
+
+ ret = avs_path_reset(path);
+ if (ret)
+ goto err;
+
+ path->source = source;
+ path->sink = sink;
+ list_add_tail(&path->source_node, &source->source_list);
+ list_add_tail(&path->sink_node, &sink->sink_list);
+
+ return path;
+err:
+ avs_path_free_unlocked(path);
+ return ERR_PTR(ret);
+}
+
+static int avs_condpath_walk(struct avs_dev *adev, struct avs_path *path, int dir)
+{
+ struct avs_tplg_path_template *template;
+ struct avs_soc_component *acomp;
+ struct avs_tplg_path *variant;
+ struct avs_path **other, *conflict;
+ struct avs_path *source, *sink;
+ struct avs_path *cpath;
+ unsigned long type, types = 0;
+ int max, i;
+
+ if (dir) {
+ source = path;
+ other = &sink;
+ } else {
+ sink = path;
+ other = &source;
+ }
+
+ /* First create all non-conflicting condpaths. */
+ list_for_each_entry(acomp, &adev->comp_list, node) {
+ for (i = 0; i < acomp->tplg->num_condpath_tmpls; i++) {
+ template = &acomp->tplg->condpath_tmpls[i];
+
+ /* Do not create unidirectional condpaths twice */
+ if (avs_tplg_path_template_id_equal(&template->source,
+ &template->sink) && dir)
+ continue;
+
+ if (template->cond_type != AVS_COND_TYPE_NONE) {
+ /* Save conflicting types to check later on. */
+ types |= BIT(template->cond_type);
+ continue;
+ }
+
+ *other = avs_condpath_find_match(adev, template, path, dir);
+ if (!*other)
+ continue;
+ variant = avs_condpath_find_variant(adev, template, source, sink);
+ if (!variant)
+ continue;
+
+ cpath = avs_condpath_create(adev, 0, variant, source, sink);
+ if (IS_ERR(cpath))
+ return PTR_ERR(cpath);
+ }
+ }
+ /* Now deal with exclusive condpaths. */
+ for_each_set_bit(type, &types, 32) {
+ variant = NULL;
+ *other = NULL;
+
+ conflict = avs_condpath_find_conflict(adev, type, path, dir);
+ if (conflict) {
+ /* Does existing conflict allow for override? */
+ if (!conflict->template->owner->overridable)
+ continue;
+ max = conflict->template->owner->priority;
+ } else {
+ max = -1;
+ }
+
+ /* Find best match - with highest priority. */
+ list_for_each_entry(acomp, &adev->comp_list, node) {
+ for (i = 0; i < acomp->tplg->num_condpath_tmpls; i++) {
+ template = &acomp->tplg->condpath_tmpls[i];
+
+ /* Do not create unidirectional condpaths twice */
+ if (avs_tplg_path_template_id_equal(&template->source,
+ &template->sink) && dir)
+ continue;
+
+ if (template->cond_type != type || template->priority <= max)
+ continue;
+
+ *other = avs_condpath_find_match(adev, template, path, dir);
+ if (!*other)
+ continue;
+ variant = avs_condpath_find_variant(adev, template, source,
+ sink);
+ if (variant)
+ max = template->priority;
+ }
+ }
+
+ if (variant) {
+ cpath = avs_condpath_create(adev, 0, variant, source, sink);
+ if (IS_ERR(cpath))
+ return PTR_ERR(cpath);
+ }
+ }
+
+ return 0;
+}
+
+/* caller responsible for holding adev->path_mutex */
+static int avs_condpath_walk_all(struct avs_dev *adev, struct avs_path *path)
+{
+ int ret;
+
+ ret = avs_condpath_walk(adev, path, 1);
+ if (ret)
+ return ret;
+
+ return avs_condpath_walk(adev, path, 0);
+}
+
struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id,
struct avs_tplg_path_template *template,
struct snd_pcm_hw_params *fe_params,
@@ -817,6 +1050,7 @@ struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id,
{
struct avs_tplg_path *variant;
struct avs_path *path;
+ int ret;
variant = avs_path_find_variant(adev, template, fe_params, be_params);
if (!variant) {
@@ -830,7 +1064,16 @@ struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id,
mutex_lock(&adev->comp_list_mutex);
path = avs_path_create_unlocked(adev, dma_id, variant);
+ if (IS_ERR(path))
+ goto exit;
+
+ ret = avs_condpath_walk_all(adev, path);
+ if (ret) {
+ avs_path_free_unlocked(path);
+ path = ERR_PTR(ret);
+ }
+exit:
mutex_unlock(&adev->comp_list_mutex);
mutex_unlock(&adev->path_mutex);
@@ -839,10 +1082,19 @@ struct avs_path *avs_path_create(struct avs_dev *adev, u32 dma_id,
void avs_path_free(struct avs_path *path)
{
+ struct avs_path *cpath, *csave;
struct avs_dev *adev = path->owner;
mutex_lock(&adev->path_mutex);
+
+ /* Free all condpaths this path spawned. */
+ list_for_each_entry_safe(cpath, csave, &path->source_list, source_node)
+ avs_condpath_free(path->owner, cpath);
+ list_for_each_entry_safe(cpath, csave, &path->sink_list, sink_node)
+ avs_condpath_free(path->owner, cpath);
+
avs_path_free_unlocked(path);
+
mutex_unlock(&adev->path_mutex);
}
@@ -965,12 +1217,19 @@ int avs_path_reset(struct avs_path *path)
int avs_path_pause(struct avs_path *path)
{
struct avs_path_pipeline *ppl;
+ struct avs_path *cpath;
struct avs_dev *adev = path->owner;
int ret;
if (path->state == AVS_PPL_STATE_PAUSED)
return 0;
+ /* if either source or sink stops, so do attached conditional paths */
+ list_for_each_entry(cpath, &path->source_list, source_node)
+ avs_path_pause(cpath);
+ list_for_each_entry(cpath, &path->sink_list, sink_node)
+ avs_path_pause(cpath);
+
list_for_each_entry_reverse(ppl, &path->ppl_list, node) {
ret = avs_ipc_set_pipeline_state(adev, ppl->instance_id,
AVS_PPL_STATE_PAUSED);
@@ -988,6 +1247,7 @@ int avs_path_pause(struct avs_path *path)
int avs_path_run(struct avs_path *path, int trigger)
{
struct avs_path_pipeline *ppl;
+ struct avs_path *cpath;
struct avs_dev *adev = path->owner;
int ret;
@@ -1008,5 +1268,20 @@ int avs_path_run(struct avs_path *path, int trigger)
}
path->state = AVS_PPL_STATE_RUNNING;
+
+ /* granular pipeline triggering not intended for conditional paths */
+ if (trigger == AVS_TPLG_TRIGGER_AUTO) {
+ /* run conditional paths only if source and sink are both running */
+ list_for_each_entry(cpath, &path->source_list, source_node)
+ if (cpath->source->state == AVS_PPL_STATE_RUNNING &&
+ cpath->sink->state == AVS_PPL_STATE_RUNNING)
+ avs_path_run(cpath, trigger);
+
+ list_for_each_entry(cpath, &path->sink_list, sink_node)
+ if (cpath->source->state == AVS_PPL_STATE_RUNNING &&
+ cpath->sink->state == AVS_PPL_STATE_RUNNING)
+ avs_path_run(cpath, trigger);
+ }
+
return 0;
}
@@ -13,11 +13,24 @@
#include "avs.h"
#include "topology.h"
+#define AVS_COND_TYPE_NONE 0
+#define AVS_COND_TYPE_AECREF 1
+
struct avs_path {
u32 dma_id;
struct list_head ppl_list;
u32 state;
+ /* condpath navigation for standard paths */
+ struct list_head source_list;
+ struct list_head sink_list;
+
+ /* conditional path fields */
+ struct avs_path *source;
+ struct avs_path *sink;
+ struct list_head source_node;
+ struct list_head sink_node;
+
struct avs_tplg_path *template;
struct avs_dev *owner;
/* device path management */
@@ -1171,6 +1171,27 @@ static const struct avs_tplg_token_parser path_parsers[] = {
},
};
+static const struct avs_tplg_token_parser condpath_parsers[] = {
+ {
+ .token = AVS_TKN_CONDPATH_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_path, id),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_CONDPATH_SOURCE_PATH_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_path, source_path_id),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_CONDPATH_SINK_PATH_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_path, sink_path_id),
+ .parse = avs_parse_word_token,
+ },
+};
+
static struct avs_tplg_path *
avs_tplg_path_create(struct snd_soc_component *comp, struct avs_tplg_path_template *owner,
struct snd_soc_tplg_vendor_array *tuples, u32 block_size,
@@ -1238,6 +1259,57 @@ static const struct avs_tplg_token_parser path_tmpl_parsers[] = {
},
};
+static const struct avs_tplg_token_parser condpath_tmpl_parsers[] = {
+ {
+ .token = AVS_TKN_CONDPATH_TMPL_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_path_template, id),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_CONDPATH_TMPL_SOURCE_TPLG_NAME_STRING,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_STRING,
+ .offset = offsetof(struct avs_tplg_path_template, source.tplg_name),
+ .parse = avs_parse_string_token,
+ },
+ {
+ .token = AVS_TKN_CONDPATH_TMPL_SOURCE_PATH_TMPL_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_path_template, source.id),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_CONDPATH_TMPL_SINK_TPLG_NAME_STRING,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_STRING,
+ .offset = offsetof(struct avs_tplg_path_template, sink.tplg_name),
+ .parse = avs_parse_string_token,
+ },
+ {
+ .token = AVS_TKN_CONDPATH_TMPL_SINK_PATH_TMPL_ID_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_path_template, sink.id),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_CONDPATH_TMPL_COND_TYPE_U32,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_WORD,
+ .offset = offsetof(struct avs_tplg_path_template, cond_type),
+ .parse = avs_parse_word_token,
+ },
+ {
+ .token = AVS_TKN_CONDPATH_TMPL_OVERRIDABLE_BOOL,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_BOOL,
+ .offset = offsetof(struct avs_tplg_path_template, overridable),
+ .parse = avs_parse_bool_token,
+ },
+ {
+ .token = AVS_TKN_CONDPATH_TMPL_PRIORITY_U8,
+ .type = SND_SOC_TPLG_TUPLE_TYPE_BYTE,
+ .offset = offsetof(struct avs_tplg_path_template, priority),
+ .parse = avs_parse_byte_token,
+ },
+};
+
static int parse_path_template(struct snd_soc_component *comp,
struct snd_soc_tplg_vendor_array *tuples, u32 block_size,
struct avs_tplg_path_template *template,
@@ -1308,6 +1380,56 @@ avs_tplg_path_template_create(struct snd_soc_component *comp, struct avs_tplg *o
return template;
}
+static int avs_tplg_parse_condpath_templates(struct snd_soc_component *comp,
+ struct snd_soc_tplg_vendor_array *tuples,
+ u32 block_size)
+{
+ struct avs_soc_component *acomp = to_avs_soc_component(comp);
+ struct avs_tplg *tplg = acomp->tplg;
+ int ret, i;
+
+ ret = parse_dictionary_header(comp, tuples, (void **)&tplg->condpath_tmpls,
+ &tplg->num_condpath_tmpls,
+ sizeof(*tplg->condpath_tmpls),
+ AVS_TKN_MANIFEST_NUM_CONDPATH_TMPLS_U32);
+ if (ret)
+ return ret;
+
+ block_size -= le32_to_cpu(tuples->size);
+ /* With header parsed, move on to parsing entries. */
+ tuples = avs_tplg_vendor_array_next(tuples);
+
+ for (i = 0; i < tplg->num_condpath_tmpls; i++) {
+ struct avs_tplg_path_template *template;
+ u32 esize;
+
+ template = &tplg->condpath_tmpls[i];
+ template->owner = tplg; /* Used when building sysfs hierarchy. */
+ INIT_LIST_HEAD(&template->path_list);
+ INIT_LIST_HEAD(&template->node);
+
+ ret = avs_tplg_vendor_entry_size(tuples, block_size,
+ AVS_TKN_CONDPATH_TMPL_ID_U32, &esize);
+ if (ret)
+ return ret;
+
+ ret = parse_path_template(comp, tuples, esize, template,
+ condpath_tmpl_parsers,
+ ARRAY_SIZE(condpath_tmpl_parsers),
+ condpath_parsers,
+ ARRAY_SIZE(condpath_parsers));
+ if (ret < 0) {
+ dev_err(comp->dev, "parse condpath_tmpl: %d failed: %d\n", i, ret);
+ return ret;
+ }
+
+ block_size -= esize;
+ tuples = avs_tplg_vendor_array_at(tuples, esize);
+ }
+
+ return 0;
+}
+
static int avs_route_load(struct snd_soc_component *comp, int index,
struct snd_soc_dapm_route *route)
{
@@ -1512,8 +1634,23 @@ static int avs_manifest(struct snd_soc_component *comp, int index,
remaining -= offset;
tuples = avs_tplg_vendor_array_at(tuples, offset);
+ ret = avs_tplg_vendor_array_lookup(tuples, remaining,
+ AVS_TKN_MANIFEST_NUM_CONDPATH_TMPLS_U32, &offset);
+ if (ret) {
+ dev_err(comp->dev, "condpath lookup failed: %d\n", ret);
+ return ret;
+ }
+
/* Bindings dictionary. */
- return avs_tplg_parse_bindings(comp, tuples, remaining);
+ ret = avs_tplg_parse_bindings(comp, tuples, offset);
+ if (ret < 0)
+ return ret;
+
+ remaining -= offset;
+ tuples = avs_tplg_vendor_array_at(tuples, offset);
+
+ /* Condpaths dictionary. */
+ return avs_tplg_parse_condpath_templates(comp, tuples, remaining);
}
static struct snd_soc_tplg_ops avs_tplg_ops = {
@@ -33,6 +33,8 @@ struct avs_tplg {
u32 num_pplcfgs;
struct avs_tplg_binding *bindings;
u32 num_bindings;
+ struct avs_tplg_path_template *condpath_tmpls;
+ u32 num_condpath_tmpls;
struct list_head path_tmpl_list;
};
@@ -139,6 +141,13 @@ struct avs_tplg_path_template_id {
struct avs_tplg_path_template {
u32 id;
+ /* Conditional path. */
+ struct avs_tplg_path_template_id source;
+ struct avs_tplg_path_template_id sink;
+ u32 cond_type;
+ bool overridable;
+ u8 priority;
+
struct list_head path_list;
struct avs_tplg *owner;
@@ -152,6 +161,9 @@ struct avs_tplg_path {
/* Path format requirements. */
struct avs_audio_format *fe_fmt;
struct avs_audio_format *be_fmt;
+ /* Condpath path-variant requirements. */
+ u32 source_path_id;
+ u32 sink_path_id;
struct list_head ppl_list;