summaryrefslogtreecommitdiffstats
path: root/sound/soc/generic/audio-graph-card.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/generic/audio-graph-card.c')
-rw-r--r--sound/soc/generic/audio-graph-card.c175
1 files changed, 119 insertions, 56 deletions
diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c
index 97b4f5480a31..16a04a678828 100644
--- a/sound/soc/generic/audio-graph-card.c
+++ b/sound/soc/generic/audio-graph-card.c
@@ -18,7 +18,7 @@
#include <linux/of_graph.h>
#include <linux/platform_device.h>
#include <linux/string.h>
-#include <sound/simple_card_utils.h>
+#include <sound/graph_card.h>
#define DPCM_SELECTABLE 1
@@ -111,6 +111,17 @@ static int graph_get_dai_id(struct device_node *ep)
return id;
}
+static bool soc_component_is_pcm(struct snd_soc_dai_link_component *dlc)
+{
+ struct snd_soc_dai *dai = snd_soc_find_dai_with_mutex(dlc);
+
+ if (dai && (dai->component->driver->pcm_construct ||
+ dai->driver->pcm_new))
+ return true;
+
+ return false;
+}
+
static int asoc_simple_parse_dai(struct device_node *ep,
struct snd_soc_dai_link_component *dlc,
int *is_single_link)
@@ -205,6 +216,7 @@ static int graph_dai_link_of_dpcm(struct asoc_simple_priv *priv,
int dup_codec)
{
struct device *dev = simple_priv_to_dev(priv);
+ struct snd_soc_card *card = simple_priv_to_card(priv);
struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
struct simple_dai_props *dai_props = simple_priv_to_props(priv, li->link);
struct device_node *top = dev->of_node;
@@ -217,6 +229,14 @@ static int graph_dai_link_of_dpcm(struct asoc_simple_priv *priv,
struct snd_soc_dai_link_component *codecs = dai_link->codecs;
int ret;
+ /*
+ * Codec endpoint can be NULL for pluggable audio HW.
+ * Platform DT can populate the Codec endpoint depending on the
+ * plugged HW.
+ */
+ if (!li->cpu && !codec_ep)
+ return 0;
+
/* Do it all CPU endpoint, and 1st Codec endpoint */
if (!li->cpu && dup_codec)
return 0;
@@ -253,11 +273,25 @@ static int graph_dai_link_of_dpcm(struct asoc_simple_priv *priv,
goto out_put_node;
ret = asoc_simple_set_dailink_name(dev, dai_link,
- "fe.%s",
+ "fe.%pOFP.%s",
+ cpus->of_node,
cpus->dai_name);
if (ret < 0)
goto out_put_node;
+ /*
+ * In BE<->BE connections it is not required to create
+ * PCM devices at CPU end of the dai link and thus 'no_pcm'
+ * flag needs to be set. It is useful when there are many
+ * BE components and some of these have to be connected to
+ * form a valid audio path.
+ *
+ * For example: FE <-> BE1 <-> BE2 <-> ... <-> BEn where
+ * there are 'n' BE components in the path.
+ */
+ if (card->component_chaining && !soc_component_is_pcm(cpus))
+ dai_link->no_pcm = 1;
+
/* card->num_links includes Codec */
asoc_simple_canonicalize_cpu(dai_link, is_single_links);
} else {
@@ -287,7 +321,8 @@ static int graph_dai_link_of_dpcm(struct asoc_simple_priv *priv,
goto out_put_node;
ret = asoc_simple_set_dailink_name(dev, dai_link,
- "be.%s",
+ "be.%pOFP.%s",
+ codecs->of_node,
codecs->dai_name);
if (ret < 0)
goto out_put_node;
@@ -320,6 +355,11 @@ static int graph_dai_link_of_dpcm(struct asoc_simple_priv *priv,
snd_soc_dai_link_set_capabilities(dai_link);
dai_link->ops = &graph_ops;
+
+ /* Use custom snd_soc_ops callbacks if available */
+ if (priv->ops)
+ dai_link->ops = priv->ops;
+
dai_link->init = asoc_simple_dai_init;
out_put_node:
@@ -404,6 +444,28 @@ static int graph_dai_link_of(struct asoc_simple_priv *priv,
return 0;
}
+static inline bool parse_as_dpcm_link(struct asoc_simple_priv *priv,
+ struct device_node *codec_port,
+ struct asoc_simple_data *adata)
+{
+ if (priv->force_dpcm)
+ return true;
+
+ if (!priv->dpcm_selectable)
+ return false;
+
+ /*
+ * It is DPCM
+ * if Codec port has many endpoints,
+ * or has convert-xxx property
+ */
+ if ((of_get_child_count(codec_port) > 1) ||
+ (adata->convert_rate || adata->convert_channels))
+ return true;
+
+ return false;
+}
+
static int graph_for_each_link(struct asoc_simple_priv *priv,
struct link_info *li,
int (*func_noml)(struct asoc_simple_priv *priv,
@@ -424,7 +486,6 @@ static int graph_for_each_link(struct asoc_simple_priv *priv,
struct device_node *codec_port;
struct device_node *codec_port_old = NULL;
struct asoc_simple_data adata;
- uintptr_t dpcm_selectable = (uintptr_t)of_device_get_match_data(dev);
int rc, ret;
/* loop for all listed CPU port */
@@ -447,14 +508,8 @@ static int graph_for_each_link(struct asoc_simple_priv *priv,
graph_parse_convert(dev, codec_ep, &adata);
graph_parse_convert(dev, cpu_ep, &adata);
- /*
- * It is DPCM
- * if Codec port has many endpoints,
- * or has convert-xxx property
- */
- if (dpcm_selectable &&
- ((of_get_child_count(codec_port) > 1) ||
- adata.convert_rate || adata.convert_channels))
+ /* check if link requires DPCM parsing */
+ if (parse_as_dpcm_link(priv, codec_port, &adata))
ret = func_dpcm(priv, cpu_ep, codec_ep, li,
(codec_port_old == codec_port));
/* else normal sound */
@@ -474,12 +529,34 @@ static int graph_for_each_link(struct asoc_simple_priv *priv,
return 0;
}
-static int graph_parse_of(struct asoc_simple_priv *priv)
+static void graph_get_dais_count(struct asoc_simple_priv *priv,
+ struct link_info *li);
+
+int graph_parse_of(struct asoc_simple_priv *priv, struct device *dev)
{
struct snd_soc_card *card = simple_priv_to_card(priv);
struct link_info li;
int ret;
+ card->owner = THIS_MODULE;
+ card->dev = dev;
+
+ memset(&li, 0, sizeof(li));
+ graph_get_dais_count(priv, &li);
+ if (!li.link || !li.dais)
+ return -EINVAL;
+
+ ret = asoc_simple_init_priv(priv, &li);
+ if (ret < 0)
+ return ret;
+
+ priv->pa_gpio = devm_gpiod_get_optional(dev, "pa", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->pa_gpio)) {
+ ret = PTR_ERR(priv->pa_gpio);
+ dev_err(dev, "failed to get amplifier gpio: %d\n", ret);
+ return ret;
+ }
+
ret = asoc_simple_parse_widgets(card, NULL);
if (ret < 0)
return ret;
@@ -506,11 +583,32 @@ static int graph_parse_of(struct asoc_simple_priv *priv)
graph_dai_link_of,
graph_dai_link_of_dpcm);
if (ret < 0)
- return ret;
+ goto err;
}
- return asoc_simple_parse_card_name(card, NULL);
+ ret = asoc_simple_parse_card_name(card, NULL);
+ if (ret < 0)
+ goto err;
+
+ snd_soc_card_set_drvdata(card, priv);
+
+ asoc_simple_debug_info(priv);
+
+ ret = devm_snd_soc_register_card(dev, card);
+ if (ret < 0)
+ goto err;
+
+ return 0;
+
+err:
+ asoc_simple_clean_reference(card);
+
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "parse error %d\n", ret);
+
+ return ret;
}
+EXPORT_SYMBOL_GPL(graph_parse_of);
static int graph_count_noml(struct asoc_simple_priv *priv,
struct device_node *cpu_ep,
@@ -538,7 +636,7 @@ static int graph_count_dpcm(struct asoc_simple_priv *priv,
li->link++; /* 1xCPU-dummy */
li->dais++; /* 1xCPU */
- if (!dup_codec) {
+ if (!dup_codec && codec_ep) {
li->link++; /* 1xdummy-Codec */
li->conf++; /* 1xdummy-Codec */
li->dais++; /* 1xCodec */
@@ -607,7 +705,7 @@ static void graph_get_dais_count(struct asoc_simple_priv *priv,
li->link, li->dais, li->conf);
}
-static int graph_card_probe(struct snd_soc_card *card)
+int graph_card_probe(struct snd_soc_card *card)
{
struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(card);
int ret;
@@ -622,14 +720,13 @@ static int graph_card_probe(struct snd_soc_card *card)
return 0;
}
+EXPORT_SYMBOL_GPL(graph_card_probe);
static int graph_probe(struct platform_device *pdev)
{
struct asoc_simple_priv *priv;
struct device *dev = &pdev->dev;
struct snd_soc_card *card;
- struct link_info li;
- int ret;
/* Allocate the private data and the DAI link array */
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
@@ -637,48 +734,14 @@ static int graph_probe(struct platform_device *pdev)
return -ENOMEM;
card = simple_priv_to_card(priv);
- card->owner = THIS_MODULE;
- card->dev = dev;
card->dapm_widgets = graph_dapm_widgets;
card->num_dapm_widgets = ARRAY_SIZE(graph_dapm_widgets);
card->probe = graph_card_probe;
- memset(&li, 0, sizeof(li));
- graph_get_dais_count(priv, &li);
- if (!li.link || !li.dais)
- return -EINVAL;
-
- ret = asoc_simple_init_priv(priv, &li);
- if (ret < 0)
- return ret;
+ if (of_device_get_match_data(dev))
+ priv->dpcm_selectable = 1;
- priv->pa_gpio = devm_gpiod_get_optional(dev, "pa", GPIOD_OUT_LOW);
- if (IS_ERR(priv->pa_gpio)) {
- ret = PTR_ERR(priv->pa_gpio);
- dev_err(dev, "failed to get amplifier gpio: %d\n", ret);
- return ret;
- }
-
- ret = graph_parse_of(priv);
- if (ret < 0) {
- if (ret != -EPROBE_DEFER)
- dev_err(dev, "parse error %d\n", ret);
- goto err;
- }
-
- snd_soc_card_set_drvdata(card, priv);
-
- asoc_simple_debug_info(priv);
-
- ret = devm_snd_soc_register_card(dev, card);
- if (ret < 0)
- goto err;
-
- return 0;
-err:
- asoc_simple_clean_reference(card);
-
- return ret;
+ return graph_parse_of(priv, dev);
}
static int graph_remove(struct platform_device *pdev)