summaryrefslogtreecommitdiffstats
path: root/sound/soc
diff options
context:
space:
mode:
authorChao Song <chao.song@linux.intel.com>2022-11-07 10:57:05 +0200
committerMark Brown <broonie@kernel.org>2022-11-07 13:58:10 +0000
commit3b3acedbd0f30b822e05e5e51b646a67de0031fc (patch)
tree7cdd31b28d70acf4daf2710252ffe73917dfa093 /sound/soc
parent6327c7297fc86d2203ab31882152a9d0b049f7b2 (diff)
downloadlinux-stable-3b3acedbd0f30b822e05e5e51b646a67de0031fc.tar.gz
linux-stable-3b3acedbd0f30b822e05e5e51b646a67de0031fc.tar.bz2
linux-stable-3b3acedbd0f30b822e05e5e51b646a67de0031fc.zip
ASoC: SOF: Add support to parse pin binding array from topology
Add support for parsing sink/source pin binding array per widget from topology. The pin binding arrays will be used to determine the source and sink queue IDs during widget binding for widget that requires special pin binding. An example of widget that requires special pin binding is the smart amplifier widget, its feedback sink pin has to be connected to a capture DAI copier for codec feedback, while the other sink pin has to be connected to a host DAI copier. Pin ID is required during widget binding for correct route setup. Conversely, the pin ID for 'generic' pins is not defined in the topology and will be allocated by the kernel dynamically. When only one pin is supported, the pin ID shall always be zero. When more than one pin is supported, the pin ID is determined with the ID allocation mechanism in the kernel. Signed-off-by: Chao Song <chao.song@linux.intel.com> Signed-off-by: Bard Liao <yung-chuan.liao@linux.intel.com> Suggested-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com> Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> Reviewed-by: Rander Wang <rander.wang@intel.com> Reviewed-by: Péter Ujfalusi <peter.ujfalusi@linux.intel.com> Signed-off-by: Peter Ujfalusi <peter.ujfalusi@linux.intel.com> Link: https://lore.kernel.org/r/20221107085706.2550-3-peter.ujfalusi@linux.intel.com Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'sound/soc')
-rw-r--r--sound/soc/sof/sof-audio.h21
-rw-r--r--sound/soc/sof/topology.c139
2 files changed, 156 insertions, 4 deletions
diff --git a/sound/soc/sof/sof-audio.h b/sound/soc/sof/sof-audio.h
index dfca713f00df..efc80a5febc3 100644
--- a/sound/soc/sof/sof-audio.h
+++ b/sound/soc/sof/sof-audio.h
@@ -30,6 +30,10 @@
*/
#define SOF_WIDGET_MAX_NUM_PINS 8
+/* The type of a widget pin is either sink or source */
+#define SOF_PIN_TYPE_SINK 0
+#define SOF_PIN_TYPE_SOURCE 1
+
/* max number of FE PCMs before BEs */
#define SOF_BE_PCM_BASE 16
@@ -402,6 +406,22 @@ struct snd_sof_widget {
u32 num_sink_pins;
u32 num_source_pins;
+ /*
+ * The sink/source pin binding array, it takes the form of
+ * [widget_name_connected_to_pin0, widget_name_connected_to_pin1, ...],
+ * with the index as the queue ID.
+ *
+ * The array is used for special pin binding. Note that even if there
+ * is only one sink/source pin requires special pin binding, pin binding
+ * should be defined for all sink/source pins in topology, for pin(s) that
+ * are not used, give the value "NotConnected".
+ *
+ * If pin binding is not defined in topology, nothing to parse in the kernel,
+ * sink_pin_binding and src_pin_binding shall be NULL.
+ */
+ char **sink_pin_binding;
+ char **src_pin_binding;
+
void *private; /* core does not touch this */
};
@@ -546,6 +566,7 @@ int get_token_u16(void *elem, void *object, u32 offset);
int get_token_comp_format(void *elem, void *object, u32 offset);
int get_token_dai_type(void *elem, void *object, u32 offset);
int get_token_uuid(void *elem, void *object, u32 offset);
+int get_token_string(void *elem, void *object, u32 offset);
int sof_update_ipc_object(struct snd_soc_component *scomp, void *object, enum sof_tokens token_id,
struct snd_sof_tuple *tuples, int num_tuples,
size_t object_size, int token_instance_num);
diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c
index e839fcdc938b..3b1290d34131 100644
--- a/sound/soc/sof/topology.c
+++ b/sound/soc/sof/topology.c
@@ -360,6 +360,21 @@ int get_token_uuid(void *elem, void *object, u32 offset)
return 0;
}
+/*
+ * The string gets from topology will be stored in heap, the owner only
+ * holds a char* member point to the heap.
+ */
+int get_token_string(void *elem, void *object, u32 offset)
+{
+ /* "dst" here points to the char* member of the owner */
+ char **dst = (char **)((u8 *)object + offset);
+
+ *dst = kstrdup(elem, GFP_KERNEL);
+ if (!*dst)
+ return -ENOMEM;
+ return 0;
+};
+
int get_token_comp_format(void *elem, void *object, u32 offset)
{
u32 *val = (u32 *)((u8 *)object + offset);
@@ -399,6 +414,16 @@ static const struct sof_topology_token comp_pin_tokens[] = {
offsetof(struct snd_sof_widget, num_source_pins)},
};
+static const struct sof_topology_token comp_sink_pin_binding_tokens[] = {
+ {SOF_TKN_COMP_SINK_PIN_BINDING_WNAME, SND_SOC_TPLG_TUPLE_TYPE_STRING,
+ get_token_string, 0},
+};
+
+static const struct sof_topology_token comp_src_pin_binding_tokens[] = {
+ {SOF_TKN_COMP_SRC_PIN_BINDING_WNAME, SND_SOC_TPLG_TUPLE_TYPE_STRING,
+ get_token_string, 0},
+};
+
/**
* sof_parse_uuid_tokens - Parse multiple sets of UUID tokens
* @scomp: pointer to soc component
@@ -579,7 +604,7 @@ static int sof_parse_string_tokens(struct snd_soc_component *scomp,
{
struct snd_soc_tplg_vendor_string_elem *elem;
int found = 0;
- int i, j;
+ int i, j, ret;
/* parse element by element */
for (i = 0; i < le32_to_cpu(array->num_elems); i++) {
@@ -596,7 +621,9 @@ static int sof_parse_string_tokens(struct snd_soc_component *scomp,
continue;
/* matched - now load token */
- tokens[j].get_token(elem->string, object, offset + tokens[j].offset);
+ ret = tokens[j].get_token(elem->string, object, offset + tokens[j].offset);
+ if (ret < 0)
+ return ret;
found++;
}
@@ -676,6 +703,7 @@ static int sof_parse_token_sets(struct snd_soc_component *scomp,
int found = 0;
int total = 0;
int asize;
+ int ret;
while (array_size > 0 && total < count * token_instance_num) {
asize = le32_to_cpu(array->size);
@@ -702,8 +730,15 @@ static int sof_parse_token_sets(struct snd_soc_component *scomp,
array);
break;
case SND_SOC_TPLG_TUPLE_TYPE_STRING:
- found += sof_parse_string_tokens(scomp, object, offset, tokens, count,
- array);
+
+ ret = sof_parse_string_tokens(scomp, object, offset, tokens, count,
+ array);
+ if (ret < 0) {
+ dev_err(scomp->dev, "error: no memory to copy string token\n");
+ return ret;
+ }
+
+ found += ret;
break;
case SND_SOC_TPLG_TUPLE_TYPE_BOOL:
case SND_SOC_TPLG_TUPLE_TYPE_BYTE:
@@ -1258,6 +1293,79 @@ err:
return ret;
}
+static void sof_free_pin_binding(struct snd_sof_widget *swidget,
+ bool pin_type)
+{
+ char **pin_binding;
+ u32 num_pins;
+ int i;
+
+ if (pin_type == SOF_PIN_TYPE_SINK) {
+ pin_binding = swidget->sink_pin_binding;
+ num_pins = swidget->num_sink_pins;
+ } else {
+ pin_binding = swidget->src_pin_binding;
+ num_pins = swidget->num_source_pins;
+ }
+
+ if (pin_binding) {
+ for (i = 0; i < num_pins; i++)
+ kfree(pin_binding[i]);
+ }
+
+ kfree(pin_binding);
+}
+
+static int sof_parse_pin_binding(struct snd_sof_widget *swidget,
+ struct snd_soc_tplg_private *priv, bool pin_type)
+{
+ const struct sof_topology_token *pin_binding_token;
+ char *pin_binding[SOF_WIDGET_MAX_NUM_PINS];
+ int token_count;
+ u32 num_pins;
+ char **pb;
+ int ret;
+ int i;
+
+ if (pin_type == SOF_PIN_TYPE_SINK) {
+ num_pins = swidget->num_sink_pins;
+ pin_binding_token = comp_sink_pin_binding_tokens;
+ token_count = ARRAY_SIZE(comp_sink_pin_binding_tokens);
+ } else {
+ num_pins = swidget->num_source_pins;
+ pin_binding_token = comp_src_pin_binding_tokens;
+ token_count = ARRAY_SIZE(comp_src_pin_binding_tokens);
+ }
+
+ memset(pin_binding, 0, SOF_WIDGET_MAX_NUM_PINS * sizeof(char *));
+ ret = sof_parse_token_sets(swidget->scomp, pin_binding, pin_binding_token,
+ token_count, priv->array, le32_to_cpu(priv->size),
+ num_pins, sizeof(char *));
+ if (ret < 0)
+ goto err;
+
+ /* copy pin binding array to swidget only if it is defined in topology */
+ if (pin_binding[0]) {
+ pb = kmemdup(pin_binding, num_pins * sizeof(char *), GFP_KERNEL);
+ if (!pb) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ if (pin_type == SOF_PIN_TYPE_SINK)
+ swidget->sink_pin_binding = pb;
+ else
+ swidget->src_pin_binding = pb;
+ }
+
+ return 0;
+
+err:
+ for (i = 0; i < num_pins; i++)
+ kfree(pin_binding[i]);
+
+ return ret;
+}
+
/* external widget init - used for any driver specific init */
static int sof_widget_ready(struct snd_soc_component *scomp, int index,
struct snd_soc_dapm_widget *w,
@@ -1301,6 +1409,26 @@ static int sof_widget_ready(struct snd_soc_component *scomp, int index,
return -EINVAL;
}
+ if (swidget->num_sink_pins > 1) {
+ ret = sof_parse_pin_binding(swidget, priv, SOF_PIN_TYPE_SINK);
+ /* on parsing error, pin binding is not allocated, nothing to free. */
+ if (ret < 0) {
+ dev_err(scomp->dev, "failed to parse sink pin binding for %s\n",
+ w->name);
+ return ret;
+ }
+ }
+
+ if (swidget->num_source_pins > 1) {
+ ret = sof_parse_pin_binding(swidget, priv, SOF_PIN_TYPE_SOURCE);
+ /* on parsing error, pin binding is not allocated, nothing to free. */
+ if (ret < 0) {
+ dev_err(scomp->dev, "failed to parse source pin binding for %s\n",
+ w->name);
+ return ret;
+ }
+ }
+
dev_dbg(scomp->dev,
"tplg: widget %d (%s) is ready [type: %d, pipe: %d, pins: %d / %d, stream: %s]\n",
swidget->comp_id, w->name, swidget->id, index,
@@ -1496,6 +1624,9 @@ out:
if (widget_ops[swidget->id].ipc_free)
widget_ops[swidget->id].ipc_free(swidget);
+ sof_free_pin_binding(swidget, SOF_PIN_TYPE_SINK);
+ sof_free_pin_binding(swidget, SOF_PIN_TYPE_SOURCE);
+
kfree(swidget->tuples);
/* remove and free swidget object */