Unverified Commit 0a142f53 authored by Mark Brown's avatar Mark Brown
Browse files

Merge series "Audio Graph Updates" from Sameer Pujar <spujar@nvidia.com>:

This series is a prepraration for using generic graph driver for Tegra210
audio. Tegra audio graph series will be sent in a separate series because
it has some dependency over other series for documentation work. This
series can focus on the generic ASoC driver updates. Below are the summary
of changes done.

 * Support multiple instances of a component. For example there can be
   multiple I2S devices which can use the same component driver.

 * Support open platforms with empty Codec endpoint. Customers can plug
   their own HW and can populate codec endpoint.

 * In a component model there can be many components which can be
   connected together. In such cases Identify no-pcm DPCM DAI links which
   can be used in BE<->BE connections.

 * Expose structures or helpers to be re-used by other similar graph
   drivers.

The series is based on following references where DPCM usgae for Tegra
Audio and simple-card driver proposal were discussed.

 * https://lkml.org/lkml/2020/4/30/519 (DPCM for Tegra)
 * https://lkml.org/lkml/2020/6/27/4 (simple-card driver)

Changelog
=========

v4 -> v5
--------
 * No changes in the core/audio-graph driver patches which are
   part of the current series.
 * Dropped Tegra audio patches and doc patches. This will be
   sent separately once the doc depdendencies are resolved.

v3 -> v4
--------
 * Added new patches to convert graph.txt and audio-graph-card.txt
   to corresponding json-schema files. Later these references
   are used in Tegra audio graph schema.

 * AHUB component binding docs are updated to reflect the usage
   of ports/port/endpoint

 * More common stuff is moved into graph_parse_of() and this is
   used by both generic and Tegra audio graph.

 * DT binding for Tegra audio graph is updated to included "ports { }"

 * As per the suggestion 'void *data' member is dropped from
   'asoc_simple_priv' and instead container method is used to
   maintain required custom data internal to Tegra audio graph.

v2 -> v3
--------
 * Dropped new compatible addition in generic graph driver
   after reviewing it with Morimoto-san. Instead added Tegra
   audio graph driver and new compatibles are added in the same.
 * Added new patches to expose new members for customization
   in audio graph driver.
 * Added new patch for Tegra audio graph driver and related
   documentation.
 * Minor change in below commit where mutex version of helper is used
   "ASoC: audio-graph: Identify 'no_pcm' DAI links for DPCM"
 * DT binding is updated to use the newly exposed compatibles
 * No changes in other patches

v1 -> v2
--------
 * Re-organized ports/endpoints description for ADMAIF and XBAR.
   Updated DT patches accordingly.
 * After above change, multiple Codec endpoint support is not
   required and hence dropped for now. This will be considered
   separately if at all required in future.
 * Re-ordered patches in the series.

Sameer Pujar (7):
  ASoC: soc-core: Fix component name_prefix parsing
  ASoC: soc-pcm: Get all BEs along DAPM path
  ASoC: audio-graph: Use of_node and DAI for DPCM DAI link names
  ASoC: audio-graph: Identify 'no_pcm' DAI links for DPCM
  ASoC: audio-graph: Support empty Codec endpoint
  ASoC: audio-graph: Expose new members for asoc_simple_priv
  ASoC: audio-graph: Expose helpers from audio graph

 include/sound/graph_card.h           |  16 ++++
 include/sound/simple_card_utils.h    |   3 +
 include/sound/soc.h                  |   1 +
 sound/soc/generic/audio-graph-card.c | 175 ++++++++++++++++++++++++-----------
 sound/soc/soc-core.c                 |   3 +-
 sound/soc/soc-pcm.c                  |   3 +-
 6 files changed, 143 insertions(+), 58 deletions(-)
 create mode 100644 include/sound/graph_card.h

--
2.7.4
parents 4e59dd24 e32b100b
Loading
Loading
Loading
Loading
+16 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0
 *
 * ASoC audio graph card support
 *
 */

#ifndef __GRAPH_CARD_H
#define __GRAPH_CARD_H

#include <sound/simple_card_utils.h>

int graph_card_probe(struct snd_soc_card *card);

int graph_parse_of(struct asoc_simple_priv *priv, struct device *dev);

#endif /* __GRAPH_CARD_H */
+3 −0
Original line number Diff line number Diff line
@@ -56,6 +56,9 @@ struct asoc_simple_priv {
	struct asoc_simple_dai *dais;
	struct snd_soc_codec_conf *codec_conf;
	struct gpio_desc *pa_gpio;
	const struct snd_soc_ops *ops;
	unsigned int dpcm_selectable:1;
	unsigned int force_dpcm:1;
};
#define simple_priv_to_card(priv)	(&(priv)->snd_card)
#define simple_priv_to_props(priv, i)	((priv)->dai_props + (i))
+1 −0
Original line number Diff line number Diff line
@@ -1084,6 +1084,7 @@ struct snd_soc_card {
	unsigned int fully_routed:1;
	unsigned int disable_route_checks:1;
	unsigned int probed:1;
	unsigned int component_chaining:1;

	void *drvdata;
};
+119 −56
Original line number Diff line number Diff line
@@ -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;
	if (of_device_get_match_data(dev))
		priv->dpcm_selectable = 1;

	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 = 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)
+2 −1
Original line number Diff line number Diff line
@@ -1124,7 +1124,8 @@ static void soc_set_name_prefix(struct snd_soc_card *card,
	for (i = 0; i < card->num_configs; i++) {
		struct snd_soc_codec_conf *map = &card->codec_conf[i];

		if (snd_soc_is_matching_component(&map->dlc, component)) {
		if (snd_soc_is_matching_component(&map->dlc, component) &&
		    map->name_prefix) {
			component->name_prefix = map->name_prefix;
			return;
		}
Loading