summaryrefslogtreecommitdiffstats
path: root/sound/soc
diff options
context:
space:
mode:
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 */