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
This commit is contained in:
Коммит
0a142f5367
|
@ -0,0 +1,16 @@
|
|||
/* 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 */
|
|
@ -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))
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -1259,7 +1259,8 @@ int dpcm_path_get(struct snd_soc_pcm_runtime *fe,
|
|||
|
||||
/* get number of valid DAI paths and their widgets */
|
||||
paths = snd_soc_dapm_dai_get_connected_widgets(cpu_dai, stream, list,
|
||||
dpcm_end_walk_at_be);
|
||||
fe->card->component_chaining ?
|
||||
NULL : dpcm_end_walk_at_be);
|
||||
|
||||
dev_dbg(fe->dev, "ASoC: found %d audio %s paths\n", paths,
|
||||
stream ? "capture" : "playback");
|
||||
|
|
Загрузка…
Ссылка в новой задаче