[PATCH v3 00/13] Audio graph card updates and usage with Tegra210 audio
Summary of changes: * 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 togethe. In such cases Identify no-pcm DPCM DAI links which can be used in BE<->BE connections.
* Add Tegra audio graph driver which is based on generic audio graph driver and specific customizations are done in Tegra driver.
* This pushes DT support for Tegra210 based platforms which uses audio-graph card and above enhancements.
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 =========
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.
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
Sameer Pujar (13): 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: simple-card-utils: Expose new members for asoc_simple_priv ASoC: audio-graph: Update driver as per new exposed members ASoC: audio-graph: Expose helpers from audio graph ASoC: dt-bindings: tegra: Add schema for audio graph card ASoC: tegra: Add audio graph based card driver arm64: defconfig: Enable Tegra audio graph card driver arm64: tegra: Audio graph header for Tegra210 arm64: tegra: Audio graph sound card for Jetson Nano and TX1
.../sound/nvidia,tegra-audio-graph-card.yaml | 67 +++++ .../boot/dts/nvidia/tegra210-audio-graph.dtsi | 138 ++++++++++ arch/arm64/boot/dts/nvidia/tegra210-p2371-2180.dts | 219 ++++++++++++++++ arch/arm64/boot/dts/nvidia/tegra210-p3450-0000.dts | 124 +++++++++ arch/arm64/configs/defconfig | 1 + include/sound/graph_card.h | 19 ++ include/sound/simple_card_utils.h | 4 + include/sound/soc.h | 1 + sound/soc/generic/audio-graph-card.c | 92 +++++-- sound/soc/soc-core.c | 3 +- sound/soc/soc-pcm.c | 3 +- sound/soc/tegra/Kconfig | 9 + sound/soc/tegra/Makefile | 2 + sound/soc/tegra/tegra_audio_graph_card.c | 291 +++++++++++++++++++++ 14 files changed, 955 insertions(+), 18 deletions(-) create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra-audio-graph-card.yaml create mode 100644 arch/arm64/boot/dts/nvidia/tegra210-audio-graph.dtsi create mode 100644 include/sound/graph_card.h create mode 100644 sound/soc/tegra/tegra_audio_graph_card.c
The "prefix" can be defined in DAI link node or it can be specified as part of the component node itself. Currently "sound-name-prefix" defined in a component is not taking effect. Actually the property is not getting parsed. It can be fixed by parsing "sound-name-prefix" property whenever "prefix" is missing in DAI link Codec node.
Signed-off-by: Sameer Pujar spujar@nvidia.com --- sound/soc/soc-core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 74df224..a784943 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -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; }
On Thu, Oct 01, 2020 at 11:02:55PM +0530, Sameer Pujar wrote:
The "prefix" can be defined in DAI link node or it can be specified as part of the component node itself. Currently "sound-name-prefix" defined in a component is not taking effect. Actually the property is not getting parsed. It can be fixed by parsing "sound-name-prefix" property whenever "prefix" is missing in DAI link Codec node.
[...]
--- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -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;
Hi,
It is not obvious how the patch fixes the problem described. I guess now map->name_prefix is NULL on some level and overrides prefix found earlier?
Best Regards, Michał Mirosław
The "prefix" can be defined in DAI link node or it can be specified as part of the component node itself. Currently "sound-name-prefix" defined in a component is not taking effect. Actually the property is not getting parsed. It can be fixed by parsing "sound-name-prefix" property whenever "prefix" is missing in DAI link Codec node.
[...]
--- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -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; }
Hi,
It is not obvious how the patch fixes the problem described. I guess now map->name_prefix is NULL on some level and overrides prefix found earlier?
Best Regards, Michał Mirosław
If map->name_prefix is NULL (which is the prefix defined for Codec DAI component in a DAI link), then go ahead and check if "sound-name-prefix" is provided under component device node itself.
dpcm_end_walk_at_be() stops the graph walk when first BE is found for the given FE component. In a component model we may want to connect multiple DAIs from different components. A new flag is introduced in 'snd_soc_card', which when set allows DAI/component chaining. Later PCM operations can be called for all these listed components for a valid DAPM path.
Signed-off-by: Sameer Pujar spujar@nvidia.com --- include/sound/soc.h | 1 + sound/soc/soc-pcm.c | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/include/sound/soc.h b/include/sound/soc.h index 3b038c5..9b69d70 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -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; }; diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index 09e8d70..25904b8 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -1283,7 +1283,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");
For multiple instances of components, using DAI name alone for DAI links is causing conflicts. Components can define multiple DAIs and hence using just a device name won't help either. Thus DT device node reference and DAI names are used to uniquely represent DAI link names.
Signed-off-by: Sameer Pujar spujar@nvidia.com Acked-by: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- sound/soc/generic/audio-graph-card.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 97b4f54..1e20562 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -253,7 +253,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, - "fe.%s", + "fe.%pOFP.%s", + cpus->of_node, cpus->dai_name); if (ret < 0) goto out_put_node; @@ -287,7 +288,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;
PCM devices are created for FE dai links with 'no-pcm' flag as '0'. Such DAI links have CPU component which implement either pcm_construct() or pcm_new() at component or dai level respectively. Based on this, current patch exposes a helper function to identify such components and populate 'no_pcm' flag for DPCM DAI link.
This helps to have BE<->BE component links where PCM devices need not be created for CPU component involved in such links.
Signed-off-by: Sameer Pujar spujar@nvidia.com Cc: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- sound/soc/generic/audio-graph-card.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+)
diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 1e20562..9b06841 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -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; @@ -259,6 +271,19 @@ static int graph_dai_link_of_dpcm(struct asoc_simple_priv *priv, 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 {
For open platforms, which can support pluggable audio cards, Codec endpoint is not fixed always. It actually depends on the compatible HW module that is going to be connected. From SoC side the given I/O interface is always available. Hence such links have fixed CPU endpoint but no Codec endpoint. This patch helps to support such links where user can populate Codec endpoint only and its fields in Platform DT depending on the plugged HW.
Signed-off-by: Sameer Pujar spujar@nvidia.com Cc: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- sound/soc/generic/audio-graph-card.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 9b06841..0ba50be9 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -229,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; @@ -565,7 +573,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 */
Add new members in struct 'asoc_simple_priv'. Idea is to leverage simple or graph card driver as much as possible and vendor can maintain a thin driver to control the behavior by populating these newly exposed members.
Following are the members added in 'asoc_simple_priv':
- 'ops' struct: In some cases SoC vendor drivers may want to implement 'snd_soc_ops' callbacks differently. In such cases custom callbacks would be used.
- 'force_dpcm' flag: Right now simple or graph card drivers detect DAI links as DPCM links if:
* The dpcm_selectable is set AND * Codec is connected to multiple CPU endpoints or aconvert property is used for rate/channels.
So there is no way to directly specify usage of DPCM alone. So a flag is exposed to mark all links as DPCM. Vendor driver can set this if required.
- 'dpcm_selectable': Currently simple or audio graph drivers provide a way to enable this for specific compatibles. However vendor driver may want to define some additional info. Thus expose this variable where vendor drivers can set this if required.
- 'data': A void pointer member is provided. This would be useful when vendor driver wants to define its own structure for internal usage and still wants to rely on most of the other members from 'asoc_simple_priv'.
Subsequent patches in the series illustrate usage for above.
Signed-off-by: Sameer Pujar spujar@nvidia.com Cc: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- include/sound/simple_card_utils.h | 4 ++++ 1 file changed, 4 insertions(+)
diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index 86a1e95..9825308 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -56,6 +56,10 @@ 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 force_dpcm:1; + uintptr_t dpcm_selectable; + void *data; }; #define simple_priv_to_card(priv) (&(priv)->snd_card) #define simple_priv_to_props(priv, i) ((priv)->dai_props + (i))
Hi Sameer
Thank you for the patch
Add new members in struct 'asoc_simple_priv'. Idea is to leverage simple or graph card driver as much as possible and vendor can maintain a thin driver to control the behavior by populating these newly exposed members.
re-use simple/audio-graph driver is nice idea. My planning next new audio-graph2 can renuse it.
diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index 86a1e95..9825308 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -56,6 +56,10 @@ 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 force_dpcm:1;
- uintptr_t dpcm_selectable;
- void *data;
};
I have opinions about these.
About dpcm_selectable, indeed current audio-graph is using it as "uintptr_t", but as you know, it checks whether it was non-NULL or not only. This means we can use it as bit-field.
BTW, do we need to have dpcm_selectable at priv ?
One note is that, -scu- user is only me (locally), and it will be removed when audio-graph2 was created. (My plan is keep code for you, but remove compatible)
About *data, I think we can avoid *data if driver side priv includes asoc_simple_priv ?
struct my_priv { struct asoc_simple_priv *simple; ... };
#define simple_to_priv(_simple) container_of((_simple), struct my_priv, simple)
Thank you for your help !!
Best regards --- Kuninori Morimoto
diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h index 86a1e95..9825308 100644 --- a/include/sound/simple_card_utils.h +++ b/include/sound/simple_card_utils.h @@ -56,6 +56,10 @@ 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 force_dpcm:1;
uintptr_t dpcm_selectable;
};void *data;
I have opinions about these.
About dpcm_selectable, indeed current audio-graph is using it as "uintptr_t", but as you know, it checks whether it was non-NULL or not only. This means we can use it as bit-field.
Yes that is true. Something like this would work?
graph_probe(...) { ...
if (of_device_get_match_data(dev)) priv->dpcm_selectable = 1;
... }
BTW, do we need to have dpcm_selectable at priv ?
Tegra audio graph driver does not require this because already it is populating 'force_dpcm' flag and having 'selectable' does not make much sense for it.
One note is that, -scu- user is only me (locally), and it will be removed when audio-graph2 was created. (My plan is keep code for you, but remove compatible)
Right now I am just keeping it to allow current code work. Later you can remove this during graph2.
About *data, I think we can avoid *data if driver side priv includes asoc_simple_priv ?
struct my_priv { struct asoc_simple_priv *simple; ... }; #define simple_to_priv(_simple) container_of((_simple), struct my_priv, simple)
This seems like a better plan, will do this.
As per the members exposed earlier in the series, audio graph driver is updated to make use of these. Functionally there is no change in behavior if these are not populated. So following changes are made as part of this.
- Update 'dai_link->ops' for DPCM links if a custom 'snd_soc_ops' is defined by the vendor driver.
- Consider 'force_dpcm' flag status when deciding if a DAI link needs to be treated as DPCM or not. In doing so the logic is moved to a separate inline function for a better readability.
- Populate 'dpcm_selectable' flag which is then used to detect DPCM DAI links.
Signed-off-by: Sameer Pujar spujar@nvidia.com Cc: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- sound/soc/generic/audio-graph-card.c | 40 ++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 9 deletions(-)
diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index 0ba50be9..fb34e34 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -355,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: @@ -439,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, @@ -459,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 */ @@ -482,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 */ @@ -678,6 +698,8 @@ static int graph_probe(struct platform_device *pdev) card->num_dapm_widgets = ARRAY_SIZE(graph_dapm_widgets); card->probe = graph_card_probe;
+ priv->dpcm_selectable = (uintptr_t)of_device_get_match_data(dev); + memset(&li, 0, sizeof(li)); graph_get_dais_count(priv, &li); if (!li.link || !li.dais)
Hi Sameer
As per the members exposed earlier in the series, audio graph driver is updated to make use of these. Functionally there is no change in behavior if these are not populated. So following changes are made as part of this.
Update 'dai_link->ops' for DPCM links if a custom 'snd_soc_ops' is defined by the vendor driver.
Consider 'force_dpcm' flag status when deciding if a DAI link needs to be treated as DPCM or not. In doing so the logic is moved to a separate inline function for a better readability.
Populate 'dpcm_selectable' flag which is then used to detect DPCM DAI links.
Signed-off-by: Sameer Pujar spujar@nvidia.com Cc: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
Can we merge [06/13] and [07/13] patches ?
Thank you for your help !!
Best regards --- Kuninori Morimoto
As per the members exposed earlier in the series, audio graph driver is updated to make use of these. Functionally there is no change in behavior if these are not populated. So following changes are made as part of this.
Update 'dai_link->ops' for DPCM links if a custom 'snd_soc_ops' is defined by the vendor driver.
Consider 'force_dpcm' flag status when deciding if a DAI link needs to be treated as DPCM or not. In doing so the logic is moved to a separate inline function for a better readability.
Populate 'dpcm_selectable' flag which is then used to detect DPCM DAI links.
Signed-off-by: Sameer Pujar spujar@nvidia.com Cc: Kuninori Morimoto kuninori.morimoto.gx@renesas.com
Can we merge [06/13] and [07/13] patches ?
Yes can be merged.
This commit exposes following functions which can be used by a sound card driver based on audio graph driver. Idea is vendors can have a thin driver and re-use common stuff from audio graph driver.
- graph_card_probe() - graph_get_dais_count() - graph_parse_of()
In doing so a new header file is added for audio graph which can be included by vendor drivers.
Signed-off-by: Sameer Pujar spujar@nvidia.com Cc: Kuninori Morimoto kuninori.morimoto.gx@renesas.com --- include/sound/graph_card.h | 19 +++++++++++++++++++ sound/soc/generic/audio-graph-card.c | 11 +++++++---- 2 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 include/sound/graph_card.h
diff --git a/include/sound/graph_card.h b/include/sound/graph_card.h new file mode 100644 index 0000000..0372c1c --- /dev/null +++ b/include/sound/graph_card.h @@ -0,0 +1,19 @@ +/* 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); + +void graph_get_dais_count(struct asoc_simple_priv *priv, + struct link_info *li); + +int graph_parse_of(struct asoc_simple_priv *priv); + +#endif /* __GRAPH_CARD_H */ diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index fb34e34..ae234bf 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -529,7 +529,7 @@ static int graph_for_each_link(struct asoc_simple_priv *priv, return 0; }
-static int graph_parse_of(struct asoc_simple_priv *priv) +int graph_parse_of(struct asoc_simple_priv *priv) { struct snd_soc_card *card = simple_priv_to_card(priv); struct link_info li; @@ -566,6 +566,7 @@ static int graph_parse_of(struct asoc_simple_priv *priv)
return asoc_simple_parse_card_name(card, NULL); } +EXPORT_SYMBOL_GPL(graph_parse_of);
static int graph_count_noml(struct asoc_simple_priv *priv, struct device_node *cpu_ep, @@ -604,8 +605,8 @@ static int graph_count_dpcm(struct asoc_simple_priv *priv, return 0; }
-static void graph_get_dais_count(struct asoc_simple_priv *priv, - struct link_info *li) +void graph_get_dais_count(struct asoc_simple_priv *priv, + struct link_info *li) { struct device *dev = simple_priv_to_dev(priv);
@@ -661,8 +662,9 @@ static void graph_get_dais_count(struct asoc_simple_priv *priv, dev_dbg(dev, "link %d, dais %d, ccnf %d\n", li->link, li->dais, li->conf); } +EXPORT_SYMBOL_GPL(graph_get_dais_count);
-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; @@ -677,6 +679,7 @@ 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) {
Hi Sameer,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on tegra/for-next] [also build test WARNING on v5.9-rc7 next-20201001] [cannot apply to asoc/for-next robh/for-next] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Sameer-Pujar/Audio-graph-card-updat... base: https://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux.git for-next config: xtensa-allyesconfig (attached as .config) compiler: xtensa-linux-gcc (GCC) 9.3.0 reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # https://github.com/0day-ci/linux/commit/b7b97805bc967aae0ce3009c1bbf17b0f232... git remote add linux-review https://github.com/0day-ci/linux git fetch --no-tags linux-review Sameer-Pujar/Audio-graph-card-updates-and-usage-with-Tegra210-audio/20201002-013648 git checkout b7b97805bc967aae0ce3009c1bbf17b0f232e18b # save the attached .config to linux build tree COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=xtensa
If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot lkp@intel.com
All warnings (new ones prefixed by >>):
sound/soc/generic/audio-graph-card.c: In function 'soc_component_is_pcm': sound/soc/generic/audio-graph-card.c:116:28: error: implicit declaration of function 'snd_soc_find_dai_with_mutex' [-Werror=implicit-function-declaration] 116 | struct snd_soc_dai *dai = snd_soc_find_dai_with_mutex(dlc); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~ sound/soc/generic/audio-graph-card.c:116:28: warning: initialization of 'struct snd_soc_dai *' from 'int' makes pointer from integer without a cast [-Wint-conversion] sound/soc/generic/audio-graph-card.c: At top level:
sound/soc/generic/audio-graph-card.c:532:5: warning: no previous prototype for 'graph_parse_of' [-Wmissing-prototypes]
532 | int graph_parse_of(struct asoc_simple_priv *priv) | ^~~~~~~~~~~~~~
sound/soc/generic/audio-graph-card.c:608:6: warning: no previous prototype for 'graph_get_dais_count' [-Wmissing-prototypes]
608 | void graph_get_dais_count(struct asoc_simple_priv *priv, | ^~~~~~~~~~~~~~~~~~~~
sound/soc/generic/audio-graph-card.c:667:5: warning: no previous prototype for 'graph_card_probe' [-Wmissing-prototypes]
667 | int graph_card_probe(struct snd_soc_card *card) | ^~~~~~~~~~~~~~~~ cc1: some warnings being treated as errors
vim +/graph_parse_of +532 sound/soc/generic/audio-graph-card.c
531
532 int graph_parse_of(struct asoc_simple_priv *priv)
533 { 534 struct snd_soc_card *card = simple_priv_to_card(priv); 535 struct link_info li; 536 int ret; 537 538 ret = asoc_simple_parse_widgets(card, NULL); 539 if (ret < 0) 540 return ret; 541 542 ret = asoc_simple_parse_routing(card, NULL); 543 if (ret < 0) 544 return ret; 545 546 memset(&li, 0, sizeof(li)); 547 for (li.cpu = 1; li.cpu >= 0; li.cpu--) { 548 /* 549 * Detect all CPU first, and Detect all Codec 2nd. 550 * 551 * In Normal sound case, all DAIs are detected 552 * as "CPU-Codec". 553 * 554 * In DPCM sound case, 555 * all CPUs are detected as "CPU-dummy", and 556 * all Codecs are detected as "dummy-Codec". 557 * To avoid random sub-device numbering, 558 * detect "dummy-Codec" in last; 559 */ 560 ret = graph_for_each_link(priv, &li, 561 graph_dai_link_of, 562 graph_dai_link_of_dpcm); 563 if (ret < 0) 564 return ret; 565 } 566 567 return asoc_simple_parse_card_name(card, NULL); 568 } 569 EXPORT_SYMBOL_GPL(graph_parse_of); 570 571 static int graph_count_noml(struct asoc_simple_priv *priv, 572 struct device_node *cpu_ep, 573 struct device_node *codec_ep, 574 struct link_info *li) 575 { 576 struct device *dev = simple_priv_to_dev(priv); 577 578 li->link += 1; /* 1xCPU-Codec */ 579 li->dais += 2; /* 1xCPU + 1xCodec */ 580 581 dev_dbg(dev, "Count As Normal\n"); 582 583 return 0; 584 } 585 586 static int graph_count_dpcm(struct asoc_simple_priv *priv, 587 struct device_node *cpu_ep, 588 struct device_node *codec_ep, 589 struct link_info *li, 590 int dup_codec) 591 { 592 struct device *dev = simple_priv_to_dev(priv); 593 594 li->link++; /* 1xCPU-dummy */ 595 li->dais++; /* 1xCPU */ 596 597 if (!dup_codec && codec_ep) { 598 li->link++; /* 1xdummy-Codec */ 599 li->conf++; /* 1xdummy-Codec */ 600 li->dais++; /* 1xCodec */ 601 } 602 603 dev_dbg(dev, "Count As DPCM\n"); 604 605 return 0; 606 } 607
608 void graph_get_dais_count(struct asoc_simple_priv *priv,
609 struct link_info *li) 610 { 611 struct device *dev = simple_priv_to_dev(priv); 612 613 /* 614 * link_num : number of links. 615 * CPU-Codec / CPU-dummy / dummy-Codec 616 * dais_num : number of DAIs 617 * ccnf_num : number of codec_conf 618 * same number for "dummy-Codec" 619 * 620 * ex1) 621 * CPU0 --- Codec0 link : 5 622 * CPU1 --- Codec1 dais : 7 623 * CPU2 -/ ccnf : 1 624 * CPU3 --- Codec2 625 * 626 * => 5 links = 2xCPU-Codec + 2xCPU-dummy + 1xdummy-Codec 627 * => 7 DAIs = 4xCPU + 3xCodec 628 * => 1 ccnf = 1xdummy-Codec 629 * 630 * ex2) 631 * CPU0 --- Codec0 link : 5 632 * CPU1 --- Codec1 dais : 6 633 * CPU2 -/ ccnf : 1 634 * CPU3 -/ 635 * 636 * => 5 links = 1xCPU-Codec + 3xCPU-dummy + 1xdummy-Codec 637 * => 6 DAIs = 4xCPU + 2xCodec 638 * => 1 ccnf = 1xdummy-Codec 639 * 640 * ex3) 641 * CPU0 --- Codec0 link : 6 642 * CPU1 -/ dais : 6 643 * CPU2 --- Codec1 ccnf : 2 644 * CPU3 -/ 645 * 646 * => 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec 647 * => 6 DAIs = 4xCPU + 2xCodec 648 * => 2 ccnf = 2xdummy-Codec 649 * 650 * ex4) 651 * CPU0 --- Codec0 (convert-rate) link : 3 652 * CPU1 --- Codec1 dais : 4 653 * ccnf : 1 654 * 655 * => 3 links = 1xCPU-Codec + 1xCPU-dummy + 1xdummy-Codec 656 * => 4 DAIs = 2xCPU + 2xCodec 657 * => 1 ccnf = 1xdummy-Codec 658 */ 659 graph_for_each_link(priv, li, 660 graph_count_noml, 661 graph_count_dpcm); 662 dev_dbg(dev, "link %d, dais %d, ccnf %d\n", 663 li->link, li->dais, li->conf); 664 } 665 EXPORT_SYMBOL_GPL(graph_get_dais_count); 666
667 int graph_card_probe(struct snd_soc_card *card)
668 { 669 struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(card); 670 int ret; 671 672 ret = asoc_simple_init_hp(card, &priv->hp_jack, NULL); 673 if (ret < 0) 674 return ret; 675 676 ret = asoc_simple_init_mic(card, &priv->mic_jack, NULL); 677 if (ret < 0) 678 return ret; 679 680 return 0; 681 } 682 EXPORT_SYMBOL_GPL(graph_card_probe); 683
--- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
Hi Sameer,
Thank you for the patch! Perhaps something to improve:
[auto build test WARNING on tegra/for-next] [also build test WARNING on v5.9-rc7 next-20201001] [cannot apply to asoc/for-next robh/for-next] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Sameer-Pujar/Audio-graph-card-updat... base: https://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux.git for-next config: x86_64-randconfig-r034-20200930 (attached as .config) compiler: clang version 12.0.0 (https://github.com/llvm/llvm-project bcd05599d0e53977a963799d6ee4f6e0bc21331b) reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # install x86_64 cross compiling tool for clang build # apt-get install binutils-x86-64-linux-gnu # https://github.com/0day-ci/linux/commit/b7b97805bc967aae0ce3009c1bbf17b0f232... git remote add linux-review https://github.com/0day-ci/linux git fetch --no-tags linux-review Sameer-Pujar/Audio-graph-card-updates-and-usage-with-Tegra210-audio/20201002-013648 git checkout b7b97805bc967aae0ce3009c1bbf17b0f232e18b # save the attached .config to linux build tree COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=x86_64
If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot lkp@intel.com
All warnings (new ones prefixed by >>):
sound/soc/generic/audio-graph-card.c:116:28: error: implicit declaration of function 'snd_soc_find_dai_with_mutex' [-Werror,-Wimplicit-function-declaration] struct snd_soc_dai *dai = snd_soc_find_dai_with_mutex(dlc); ^ sound/soc/generic/audio-graph-card.c:116:22: warning: incompatible integer to pointer conversion initializing 'struct snd_soc_dai *' with an expression of type 'int' [-Wint-conversion] struct snd_soc_dai *dai = snd_soc_find_dai_with_mutex(dlc); ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
sound/soc/generic/audio-graph-card.c:532:5: warning: no previous prototype for function 'graph_parse_of' [-Wmissing-prototypes]
int graph_parse_of(struct asoc_simple_priv *priv) ^ sound/soc/generic/audio-graph-card.c:532:1: note: declare 'static' if the function is not intended to be used outside of this translation unit int graph_parse_of(struct asoc_simple_priv *priv) ^ static
sound/soc/generic/audio-graph-card.c:608:6: warning: no previous prototype for function 'graph_get_dais_count' [-Wmissing-prototypes]
void graph_get_dais_count(struct asoc_simple_priv *priv, ^ sound/soc/generic/audio-graph-card.c:608:1: note: declare 'static' if the function is not intended to be used outside of this translation unit void graph_get_dais_count(struct asoc_simple_priv *priv, ^ static
sound/soc/generic/audio-graph-card.c:667:5: warning: no previous prototype for function 'graph_card_probe' [-Wmissing-prototypes]
int graph_card_probe(struct snd_soc_card *card) ^ sound/soc/generic/audio-graph-card.c:667:1: note: declare 'static' if the function is not intended to be used outside of this translation unit int graph_card_probe(struct snd_soc_card *card) ^ static 4 warnings and 1 error generated.
vim +/graph_parse_of +532 sound/soc/generic/audio-graph-card.c
531
532 int graph_parse_of(struct asoc_simple_priv *priv)
533 { 534 struct snd_soc_card *card = simple_priv_to_card(priv); 535 struct link_info li; 536 int ret; 537 538 ret = asoc_simple_parse_widgets(card, NULL); 539 if (ret < 0) 540 return ret; 541 542 ret = asoc_simple_parse_routing(card, NULL); 543 if (ret < 0) 544 return ret; 545 546 memset(&li, 0, sizeof(li)); 547 for (li.cpu = 1; li.cpu >= 0; li.cpu--) { 548 /* 549 * Detect all CPU first, and Detect all Codec 2nd. 550 * 551 * In Normal sound case, all DAIs are detected 552 * as "CPU-Codec". 553 * 554 * In DPCM sound case, 555 * all CPUs are detected as "CPU-dummy", and 556 * all Codecs are detected as "dummy-Codec". 557 * To avoid random sub-device numbering, 558 * detect "dummy-Codec" in last; 559 */ 560 ret = graph_for_each_link(priv, &li, 561 graph_dai_link_of, 562 graph_dai_link_of_dpcm); 563 if (ret < 0) 564 return ret; 565 } 566 567 return asoc_simple_parse_card_name(card, NULL); 568 } 569 EXPORT_SYMBOL_GPL(graph_parse_of); 570 571 static int graph_count_noml(struct asoc_simple_priv *priv, 572 struct device_node *cpu_ep, 573 struct device_node *codec_ep, 574 struct link_info *li) 575 { 576 struct device *dev = simple_priv_to_dev(priv); 577 578 li->link += 1; /* 1xCPU-Codec */ 579 li->dais += 2; /* 1xCPU + 1xCodec */ 580 581 dev_dbg(dev, "Count As Normal\n"); 582 583 return 0; 584 } 585 586 static int graph_count_dpcm(struct asoc_simple_priv *priv, 587 struct device_node *cpu_ep, 588 struct device_node *codec_ep, 589 struct link_info *li, 590 int dup_codec) 591 { 592 struct device *dev = simple_priv_to_dev(priv); 593 594 li->link++; /* 1xCPU-dummy */ 595 li->dais++; /* 1xCPU */ 596 597 if (!dup_codec && codec_ep) { 598 li->link++; /* 1xdummy-Codec */ 599 li->conf++; /* 1xdummy-Codec */ 600 li->dais++; /* 1xCodec */ 601 } 602 603 dev_dbg(dev, "Count As DPCM\n"); 604 605 return 0; 606 } 607
608 void graph_get_dais_count(struct asoc_simple_priv *priv,
609 struct link_info *li) 610 { 611 struct device *dev = simple_priv_to_dev(priv); 612 613 /* 614 * link_num : number of links. 615 * CPU-Codec / CPU-dummy / dummy-Codec 616 * dais_num : number of DAIs 617 * ccnf_num : number of codec_conf 618 * same number for "dummy-Codec" 619 * 620 * ex1) 621 * CPU0 --- Codec0 link : 5 622 * CPU1 --- Codec1 dais : 7 623 * CPU2 -/ ccnf : 1 624 * CPU3 --- Codec2 625 * 626 * => 5 links = 2xCPU-Codec + 2xCPU-dummy + 1xdummy-Codec 627 * => 7 DAIs = 4xCPU + 3xCodec 628 * => 1 ccnf = 1xdummy-Codec 629 * 630 * ex2) 631 * CPU0 --- Codec0 link : 5 632 * CPU1 --- Codec1 dais : 6 633 * CPU2 -/ ccnf : 1 634 * CPU3 -/ 635 * 636 * => 5 links = 1xCPU-Codec + 3xCPU-dummy + 1xdummy-Codec 637 * => 6 DAIs = 4xCPU + 2xCodec 638 * => 1 ccnf = 1xdummy-Codec 639 * 640 * ex3) 641 * CPU0 --- Codec0 link : 6 642 * CPU1 -/ dais : 6 643 * CPU2 --- Codec1 ccnf : 2 644 * CPU3 -/ 645 * 646 * => 6 links = 0xCPU-Codec + 4xCPU-dummy + 2xdummy-Codec 647 * => 6 DAIs = 4xCPU + 2xCodec 648 * => 2 ccnf = 2xdummy-Codec 649 * 650 * ex4) 651 * CPU0 --- Codec0 (convert-rate) link : 3 652 * CPU1 --- Codec1 dais : 4 653 * ccnf : 1 654 * 655 * => 3 links = 1xCPU-Codec + 1xCPU-dummy + 1xdummy-Codec 656 * => 4 DAIs = 2xCPU + 2xCodec 657 * => 1 ccnf = 1xdummy-Codec 658 */ 659 graph_for_each_link(priv, li, 660 graph_count_noml, 661 graph_count_dpcm); 662 dev_dbg(dev, "link %d, dais %d, ccnf %d\n", 663 li->link, li->dais, li->conf); 664 } 665 EXPORT_SYMBOL_GPL(graph_get_dais_count); 666
667 int graph_card_probe(struct snd_soc_card *card)
668 { 669 struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(card); 670 int ret; 671 672 ret = asoc_simple_init_hp(card, &priv->hp_jack, NULL); 673 if (ret < 0) 674 return ret; 675 676 ret = asoc_simple_init_mic(card, &priv->mic_jack, NULL); 677 if (ret < 0) 678 return ret; 679 680 return 0; 681 } 682 EXPORT_SYMBOL_GPL(graph_card_probe); 683
--- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
Add YAML schema for Tegra audio graph sound card DT bindings. It uses the same DT bindings provided by generic audio graph driver. Along with this few standard clock DT bindings are added which are specifically required for Tegra audio.
Signed-off-by: Sameer Pujar spujar@nvidia.com --- .../sound/nvidia,tegra-audio-graph-card.yaml | 70 ++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra-audio-graph-card.yaml
diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-graph-card.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-graph-card.yaml new file mode 100644 index 0000000..b73fbe5 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-graph-card.yaml @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/nvidia,tegra-audio-graph-card.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Audio Graph based Tegra sound card driver + +description: | + This is based on generic audio graph card driver along with additional + customizations for Tegra platforms. It uses the same bindings with + additional standard clock DT bindings required for Tegra. + + See{LINUX}/Documentation/devicetree/bindings/sound/audio-graph-card.txt + +maintainers: + - Jon Hunter jonathanh@nvidia.com + - Sameer Pujar spujar@nvidia.com + +properties: + compatible: + oneOf: + - const: nvidia,tegra210-audio-graph-card + - const: nvidia,tegra186-audio-graph-card + + clocks: + minItems: 2 + + clock-names: + minItems: 2 + items: + - const: pll_a + - const: plla_out0 + + assigned-clocks: + minItems: 1 + maxItems: 3 + + assigned-clock-parents: + minItems: 1 + maxItems: 3 + + assigned-clock-rates: + minItems: 1 + maxItems: 3 + +required: + - compatible + - clocks + - clock-names + - assigned-clocks + - assigned-clock-parents + +examples: + - | + #include<dt-bindings/clock/tegra210-car.h> + + tegra_sound { + compatible = "nvidia,tegra210-audio-graph-card"; + clocks = <&tegra_car TEGRA210_CLK_PLL_A>, + <&tegra_car TEGRA210_CLK_PLL_A_OUT0>; + clock-names = "pll_a", "plla_out0"; + assigned-clocks = <&tegra_car TEGRA210_CLK_PLL_A>, + <&tegra_car TEGRA210_CLK_PLL_A_OUT0>, + <&tegra_car TEGRA210_CLK_EXTERN1>; + assigned-clock-parents = <0>, <0>, <&tegra_car TEGRA210_CLK_PLL_A_OUT0>; + assigned-clock-rates = <368640000>, <49152000>, <12288000>; + }; + +...
On Thu, Oct 01, 2020 at 11:03:03PM +0530, Sameer Pujar wrote:
Add YAML schema for Tegra audio graph sound card DT bindings. It uses the same DT bindings provided by generic audio graph driver. Along with this few standard clock DT bindings are added which are specifically required for Tegra audio.
Signed-off-by: Sameer Pujar spujar@nvidia.com
.../sound/nvidia,tegra-audio-graph-card.yaml | 70 ++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra-audio-graph-card.yaml
diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-graph-card.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-graph-card.yaml new file mode 100644 index 0000000..b73fbe5 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-graph-card.yaml @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/nvidia,tegra-audio-graph-card.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml#
+title: Audio Graph based Tegra sound card driver
+description: |
- This is based on generic audio graph card driver along with additional
- customizations for Tegra platforms. It uses the same bindings with
- additional standard clock DT bindings required for Tegra.
- See{LINUX}/Documentation/devicetree/bindings/sound/audio-graph-card.txt
+maintainers:
- Jon Hunter jonathanh@nvidia.com
- Sameer Pujar spujar@nvidia.com
+properties:
- compatible:
- oneOf:
- const: nvidia,tegra210-audio-graph-card
- const: nvidia,tegra186-audio-graph-card
- clocks:
- minItems: 2
- clock-names:
- minItems: 2
- items:
- const: pll_a
- const: plla_out0
- assigned-clocks:
- minItems: 1
- maxItems: 3
- assigned-clock-parents:
- minItems: 1
- maxItems: 3
- assigned-clock-rates:
- minItems: 1
- maxItems: 3
+required:
- compatible
- clocks
- clock-names
- assigned-clocks
- assigned-clock-parents
Where's the graph? You need to define the ports and reference the common schema.
+examples:
- |
- #include<dt-bindings/clock/tegra210-car.h>
- tegra_sound {
compatible = "nvidia,tegra210-audio-graph-card";
clocks = <&tegra_car TEGRA210_CLK_PLL_A>,
<&tegra_car TEGRA210_CLK_PLL_A_OUT0>;
clock-names = "pll_a", "plla_out0";
assigned-clocks = <&tegra_car TEGRA210_CLK_PLL_A>,
<&tegra_car TEGRA210_CLK_PLL_A_OUT0>,
<&tegra_car TEGRA210_CLK_EXTERN1>;
assigned-clock-parents = <0>, <0>, <&tegra_car TEGRA210_CLK_PLL_A_OUT0>;
assigned-clock-rates = <368640000>, <49152000>, <12288000>;
- };
+...
2.7.4
Add YAML schema for Tegra audio graph sound card DT bindings. It uses the same DT bindings provided by generic audio graph driver. Along with this few standard clock DT bindings are added which are specifically required for Tegra audio.
Signed-off-by: Sameer Pujar spujar@nvidia.com
.../sound/nvidia,tegra-audio-graph-card.yaml | 70 ++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra-audio-graph-card.yaml
diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-graph-card.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-graph-card.yaml new file mode 100644 index 0000000..b73fbe5 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-graph-card.yaml @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/nvidia,tegra-audio-graph-card.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml#
+title: Audio Graph based Tegra sound card driver
+description: |
- This is based on generic audio graph card driver along with additional
- customizations for Tegra platforms. It uses the same bindings with
- additional standard clock DT bindings required for Tegra.
- See{LINUX}/Documentation/devicetree/bindings/sound/audio-graph-card.txt
+maintainers:
- Jon Hunter jonathanh@nvidia.com
- Sameer Pujar spujar@nvidia.com
+properties:
- compatible:
- oneOf:
- const: nvidia,tegra210-audio-graph-card
- const: nvidia,tegra186-audio-graph-card
- clocks:
- minItems: 2
- clock-names:
- minItems: 2
- items:
- const: pll_a
- const: plla_out0
- assigned-clocks:
- minItems: 1
- maxItems: 3
- assigned-clock-parents:
- minItems: 1
- maxItems: 3
- assigned-clock-rates:
- minItems: 1
- maxItems: 3
+required:
- compatible
- clocks
- clock-names
- assigned-clocks
- assigned-clock-parents
Where's the graph? You need to define the ports and reference the common schema.
I am looking to reference the bindings used in below doc which is not yet in YAML format. Only additional properties I listed here. {LINUX}/Documentation/devicetree/bindings/sound/audio-graph-card.txt
Should I keep this doc to *.txt format as well and later move to YAML or is there a way to reference *.txt doc here?
Add YAML schema for Tegra audio graph sound card DT bindings. It uses the same DT bindings provided by generic audio graph driver. Along with this few standard clock DT bindings are added which are specifically required for Tegra audio.
Signed-off-by: Sameer Pujar spujar@nvidia.com
.../sound/nvidia,tegra-audio-graph-card.yaml | 70 ++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 Documentation/devicetree/bindings/sound/nvidia,tegra-audio-graph-card.yaml
diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-graph-card.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-graph-card.yaml
new file mode 100644 index 0000000..b73fbe5 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-graph-card.yaml @@ -0,0 +1,70 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sound/nvidia,tegra-audio-graph-card.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml#
+title: Audio Graph based Tegra sound card driver
+description: | + This is based on generic audio graph card driver along with additional + customizations for Tegra platforms. It uses the same bindings with + additional standard clock DT bindings required for Tegra.
See{LINUX}/Documentation/devicetree/bindings/sound/audio-graph-card.txt
+maintainers: + - Jon Hunter jonathanh@nvidia.com + - Sameer Pujar spujar@nvidia.com
+properties: + compatible: + oneOf: + - const: nvidia,tegra210-audio-graph-card + - const: nvidia,tegra186-audio-graph-card
+ clocks: + minItems: 2
+ clock-names: + minItems: 2 + items: + - const: pll_a + - const: plla_out0
+ assigned-clocks: + minItems: 1 + maxItems: 3
+ assigned-clock-parents: + minItems: 1 + maxItems: 3
+ assigned-clock-rates: + minItems: 1 + maxItems: 3
+required: + - compatible + - clocks + - clock-names + - assigned-clocks + - assigned-clock-parents
Where's the graph? You need to define the ports and reference the common schema.
I am looking to reference the bindings used in below doc which is not yet in YAML format. Only additional properties I listed here. {LINUX}/Documentation/devicetree/bindings/sound/audio-graph-card.txt
Should I keep this doc to *.txt format as well and later move to YAML or is there a way to reference *.txt doc here?
The dependency here is like below, Tegra audio graph card -> generic audio graph card (audio-graph-card.txt) -> graph (graph.txt)
I plan to convert dependencies to json-schema in next revision and then refer these for Tegra audio graph card.
On Fri, Oct 16, 2020 at 10:44:15AM +0530, Sameer Pujar wrote:
I am looking to reference the bindings used in below doc which is not yet in YAML format. Only additional properties I listed here. {LINUX}/Documentation/devicetree/bindings/sound/audio-graph-card.txt
Should I keep this doc to *.txt format as well and later move to YAML or is there a way to reference *.txt doc here?
The dependency here is like below, Tegra audio graph card -> generic audio graph card (audio-graph-card.txt) -> graph (graph.txt)
I plan to convert dependencies to json-schema in next revision and then refer these for Tegra audio graph card.
Yeah, unfortunately you need to do it that way around - you can't reference text bindings from YAML ones as the tooling can't parse the text bindings for validation.
Add Tegra audio machine driver which is based on generic audio graph card driver. It re-uses most of the common stuff from audio graph driver and uses the same DT binding. Required Tegra specific customizations are done in the driver.
Details on the customizations done:
- Update PLL rates at runtime: Tegra HW supports multiple sample rates (multiples of 8x and 11.025x) and both of these groups require different PLL rates. Hence there is a requirement to update this at runtime. This is achieved by providing a custom 'snd_soc_ops' and in hw_param() callback PLL rate is updated as per the sample rate.
- Internal structure 'tegra_audio_graph_data' is used to maintain clock handles of PLL.
- The 'force_dpcm' flag is set to use DPCM for all DAI links.
- The 'component_chaining' flag is set to use DPCM with component model.
Signed-off-by: Sameer Pujar spujar@nvidia.com --- sound/soc/tegra/Kconfig | 9 + sound/soc/tegra/Makefile | 2 + sound/soc/tegra/tegra_audio_graph_card.c | 291 +++++++++++++++++++++++++++++++ 3 files changed, 302 insertions(+) create mode 100644 sound/soc/tegra/tegra_audio_graph_card.c
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig index 3d91bd3..9959605 100644 --- a/sound/soc/tegra/Kconfig +++ b/sound/soc/tegra/Kconfig @@ -118,6 +118,15 @@ config SND_SOC_TEGRA210_ADMAIF channel. Buffer size is configurable for each ADMAIIF channel. Say Y or M if you want to add support for Tegra210 ADMAIF module.
+config SND_SOC_TEGRA_AUDIO_GRAPH_CARD + tristate "Audio Graph Card based Tegra driver" + depends on SND_AUDIO_GRAPH_CARD + help + Config to enable Tegra audio machine driver based on generic + audio graph driver. It is a thin driver written to customize + few things for Tegra audio. Most of the code is re-used from + audio graph driver and the same DT bindings are used. + config SND_SOC_TEGRA_RT5640 tristate "SoC Audio support for Tegra boards using an RT5640 codec" depends on SND_SOC_TEGRA && I2C && GPIOLIB diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile index 60040a0..b17dd6e 100644 --- a/sound/soc/tegra/Makefile +++ b/sound/soc/tegra/Makefile @@ -38,6 +38,7 @@ snd-soc-tegra-trimslice-objs := trimslice.o snd-soc-tegra-alc5632-objs := tegra_alc5632.o snd-soc-tegra-max98090-objs := tegra_max98090.o snd-soc-tegra-sgtl5000-objs := tegra_sgtl5000.o +snd-soc-tegra-audio-graph-card-objs := tegra_audio_graph_card.o
obj-$(CONFIG_SND_SOC_TEGRA_RT5640) += snd-soc-tegra-rt5640.o obj-$(CONFIG_SND_SOC_TEGRA_RT5677) += snd-soc-tegra-rt5677.o @@ -48,3 +49,4 @@ obj-$(CONFIG_SND_SOC_TEGRA_TRIMSLICE) += snd-soc-tegra-trimslice.o obj-$(CONFIG_SND_SOC_TEGRA_ALC5632) += snd-soc-tegra-alc5632.o obj-$(CONFIG_SND_SOC_TEGRA_MAX98090) += snd-soc-tegra-max98090.o obj-$(CONFIG_SND_SOC_TEGRA_SGTL5000) += snd-soc-tegra-sgtl5000.o +obj-$(CONFIG_SND_SOC_TEGRA_AUDIO_GRAPH_CARD) += snd-soc-tegra-audio-graph-card.o diff --git a/sound/soc/tegra/tegra_audio_graph_card.c b/sound/soc/tegra/tegra_audio_graph_card.c new file mode 100644 index 0000000..cfbd3f0 --- /dev/null +++ b/sound/soc/tegra/tegra_audio_graph_card.c @@ -0,0 +1,291 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// tegra_audio_graph_card.c - Audio Graph based Tegra Machine Driver +// +// Copyright (c) 2020 NVIDIA CORPORATION. All rights reserved. + +#include <linux/math64.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <sound/graph_card.h> +#include <sound/pcm_params.h> + +#define MAX_PLLA_OUT0_DIV 128 + +enum srate_type { + /* + * Sample rates multiple of 8000 Hz and below are supported: + * ( 8000, 16000, 32000, 48000, 96000, 192000 Hz ) + */ + x8_RATE, + + /* + * Sample rates multiple of 11025 Hz and below are supported: + * ( 11025, 22050, 44100, 88200, 176400 Hz ) + */ + x11_RATE, + + NUM_RATE_TYPE, +}; + +struct tegra_audio_graph_data { + struct clk *clk_plla_out0; + struct clk *clk_plla; +}; + +struct tegra_audio_chip_data { + unsigned int plla_out0_rates[NUM_RATE_TYPE]; + unsigned int plla_rates[NUM_RATE_TYPE]; +}; + +/* Setup PLL clock as per the given sample rate */ +static int tegra_audio_graph_update_pll(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card); + struct device *dev = rtd->card->dev; + struct tegra_audio_graph_data *graph_data = + (struct tegra_audio_graph_data *)priv->data; + struct tegra_audio_chip_data *chip_data = + (struct tegra_audio_chip_data *)of_device_get_match_data(dev); + unsigned int srate = params_rate(params); + unsigned int plla_rate, plla_out0_rate, bclk; + int err; + + /* There is nothing to configure */ + if (!chip_data) + return 0; + + switch (srate) { + case 11025: + case 22050: + case 44100: + case 88200: + case 176400: + plla_out0_rate = chip_data->plla_out0_rates[x11_RATE]; + plla_rate = chip_data->plla_rates[x11_RATE]; + break; + case 8000: + case 16000: + case 32000: + case 48000: + case 96000: + case 192000: + plla_out0_rate = chip_data->plla_out0_rates[x8_RATE]; + plla_rate = chip_data->plla_rates[x8_RATE]; + break; + default: + dev_err(rtd->card->dev, "Unsupported sample rate %u\n", + srate); + return -EINVAL; + } + + /* + * Below is the clock relation: + * + * PLLA + * | + * |--> PLLA_OUT0 + * | + * |---> I2S modules + * | + * |---> DMIC modules + * | + * |---> DSPK modules + * + * + * Default PLLA_OUT0 rate might be too high when I/O is running + * at minimum PCM configurations. This may result in incorrect + * clock rates and glitchy audio. The maximum divider is 128 + * and any thing higher than that won't work. Thus reduce PLLA_OUT0 + * to work for lower configurations. + * + * This problem is seen for I2S only, as DMIC and DSPK minimum + * clock requirements are under allowed divider limits. + */ + bclk = srate * params_channels(params) * params_width(params); + if (div_u64(plla_out0_rate, bclk) > MAX_PLLA_OUT0_DIV) + plla_out0_rate >>= 1; + + dev_dbg(rtd->card->dev, + "Update clock rates: PLLA(= %u Hz) and PLLA_OUT0(= %u Hz)\n", + plla_rate, plla_out0_rate); + + /* Set PLLA rate */ + err = clk_set_rate(graph_data->clk_plla, plla_rate); + if (err) { + dev_err(rtd->card->dev, + "Can't set plla rate for %u, err: %d\n", + plla_rate, err); + return err; + } + + /* Set PLLA_OUT0 rate */ + err = clk_set_rate(graph_data->clk_plla_out0, plla_out0_rate); + if (err) { + dev_err(rtd->card->dev, + "Can't set plla_out0 rate %u, err: %d\n", + plla_out0_rate, err); + return err; + } + + return err; +} + +static int tegra_audio_graph_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); + int err; + + /* + * This gets called for each DAI link (FE or BE) when DPCM is used. + * We may not want to update PLLA rate for each call. So PLLA update + * must be restricted to external I/O links (I2S, DMIC or DSPK) since + * they actually depend on it. I/O modules update their clocks in + * hw_param() of their respective component driver and PLLA rate + * update here helps them to derive appropriate rates. + * + * TODO: When more HW accelerators get added (like sample rate + * converter, volume gain controller etc., which don't really + * depend on PLLA) we need a better way to filter here. + */ + if (cpu_dai->driver->ops && rtd->dai_link->no_pcm) { + err = tegra_audio_graph_update_pll(substream, params); + if (err) + return err; + } + + return asoc_simple_hw_params(substream, params); +} + +static const struct snd_soc_ops tegra_audio_graph_ops = { + .startup = asoc_simple_startup, + .shutdown = asoc_simple_shutdown, + .hw_params = tegra_audio_graph_hw_params, +}; + +static int tegra_audio_graph_card_probe(struct snd_soc_card *card) +{ + struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(card); + struct tegra_audio_graph_data *graph_data = + (struct tegra_audio_graph_data *)priv->data; + + graph_data->clk_plla = devm_clk_get(card->dev, "pll_a"); + if (IS_ERR(graph_data->clk_plla)) { + dev_err(card->dev, "Can't retrieve clk pll_a\n"); + return PTR_ERR(graph_data->clk_plla); + } + + graph_data->clk_plla_out0 = devm_clk_get(card->dev, "plla_out0"); + if (IS_ERR(graph_data->clk_plla_out0)) { + dev_err(card->dev, "Can't retrieve clk plla_out0\n"); + return PTR_ERR(graph_data->clk_plla_out0); + } + + return graph_card_probe(card); +} + +static int tegra_audio_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 err; + + /* Allocate the private data and the DAI link array */ + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->data = (struct tegra_audio_graph_data *) + devm_kzalloc(dev, sizeof(*priv->data), GFP_KERNEL); + if (!priv->data) + return -ENOMEM; + + card = simple_priv_to_card(priv); + + card->owner = THIS_MODULE; + card->dev = dev; + card->component_chaining = true; + card->probe = tegra_audio_graph_card_probe; + + priv->ops = &tegra_audio_graph_ops; + priv->force_dpcm = 1; + + memset(&li, 0, sizeof(li)); + graph_get_dais_count(priv, &li); + if (!li.link || !li.dais) + return -EINVAL; + + err = asoc_simple_init_priv(priv, &li); + if (err < 0) + return err; + + err = graph_parse_of(priv); + if (err < 0) { + if (err != -EPROBE_DEFER) + dev_err(dev, "Parse error %d\n", err); + goto cleanup; + } + + snd_soc_card_set_drvdata(card, priv); + + asoc_simple_debug_info(priv); + + err = devm_snd_soc_register_card(dev, card); + if (err < 0) + goto cleanup; + + return 0; + +cleanup: + asoc_simple_clean_reference(card); + + return err; +} + +static const struct tegra_audio_chip_data tegra210_data = { + /* PLLA */ + .plla_rates[x8_RATE] = 368640000, + .plla_rates[x11_RATE] = 338688000, + /* PLLA_OUT0 */ + .plla_out0_rates[x8_RATE] = 49152000, + .plla_out0_rates[x11_RATE] = 45158400, +}; + +static const struct tegra_audio_chip_data tegra186_data = { + /* PLLA */ + .plla_rates[x8_RATE] = 245760000, + .plla_rates[x11_RATE] = 270950400, + /* PLLA_OUT0 */ + .plla_out0_rates[x8_RATE] = 49152000, + .plla_out0_rates[x11_RATE] = 45158400, +}; + +static const struct of_device_id graph_of_tegra_match[] = { + { .compatible = "nvidia,tegra210-audio-graph-card", + .data = &tegra210_data }, + { .compatible = "nvidia,tegra186-audio-graph-card", + .data = &tegra186_data }, + {}, +}; +MODULE_DEVICE_TABLE(of, graph_of_tegra_match); + +static struct platform_driver tegra_audio_graph_card = { + .driver = { + .name = "tegra-audio-graph-card", + .pm = &snd_soc_pm_ops, + .of_match_table = graph_of_tegra_match, + }, + .probe = tegra_audio_graph_probe, +}; +module_platform_driver(tegra_audio_graph_card); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("ASoC Tegra Audio Graph Sound Card"); +MODULE_AUTHOR("Sameer Pujar spujar@nvidia.com");
On Thu, Oct 01, 2020 at 11:03:04PM +0530, Sameer Pujar wrote:
Add Tegra audio machine driver which is based on generic audio graph card driver. It re-uses most of the common stuff from audio graph driver and uses the same DT binding. Required Tegra specific customizations are done in the driver.
[...]
- switch (srate) {
- case 11025:
- case 22050:
- case 44100:
- case 88200:
- case 176400:
plla_out0_rate = chip_data->plla_out0_rates[x11_RATE];
plla_rate = chip_data->plla_rates[x11_RATE];
break;
- case 8000:
- case 16000:
- case 32000:
- case 48000:
- case 96000:
- case 192000:
[...]
Do you really need to enumerate the frequencies? Wouldn't just checking srate % 11025 be enough to divide the set in two? Or just calculating the PLLA base rate by multiplying?
Best Regards, Michał Mirosław
On 01/10/2020 20:07, Michał Mirosław wrote:
On Thu, Oct 01, 2020 at 11:03:04PM +0530, Sameer Pujar wrote:
Add Tegra audio machine driver which is based on generic audio graph card driver. It re-uses most of the common stuff from audio graph driver and uses the same DT binding. Required Tegra specific customizations are done in the driver.
[...]
- switch (srate) {
- case 11025:
- case 22050:
- case 44100:
- case 88200:
- case 176400:
plla_out0_rate = chip_data->plla_out0_rates[x11_RATE];
plla_rate = chip_data->plla_rates[x11_RATE];
break;
- case 8000:
- case 16000:
- case 32000:
- case 48000:
- case 96000:
- case 192000:
[...]
Do you really need to enumerate the frequencies? Wouldn't just checking srate % 11025 be enough to divide the set in two? Or just calculating the PLLA base rate by multiplying?
This is quite common among other ASoC drivers from what I can see. The PLL rate does not scale with the srate, we just use a different PLL rate depending on if the srate is 11025 Hz or 8000 Hz based. I don't see any need to change the above.
Cheers Jon
01.10.2020 20:33, Sameer Pujar пишет:
+/* Setup PLL clock as per the given sample rate */ +static int tegra_audio_graph_update_pll(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
+{
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
- struct device *dev = rtd->card->dev;
- struct tegra_audio_graph_data *graph_data =
(struct tegra_audio_graph_data *)priv->data;
- struct tegra_audio_chip_data *chip_data =
(struct tegra_audio_chip_data *)of_device_get_match_data(dev);
void* doesn't need casting
01.10.2020 23:57, Dmitry Osipenko пишет:
01.10.2020 20:33, Sameer Pujar пишет:
+/* Setup PLL clock as per the given sample rate */ +static int tegra_audio_graph_update_pll(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
+{
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
- struct device *dev = rtd->card->dev;
- struct tegra_audio_graph_data *graph_data =
(struct tegra_audio_graph_data *)priv->data;
- struct tegra_audio_chip_data *chip_data =
(struct tegra_audio_chip_data *)of_device_get_match_data(dev);
void* doesn't need casting
There are several similar places in the code. Not a big deal, but this makes code less readable than it could be.
+/* Setup PLL clock as per the given sample rate */ +static int tegra_audio_graph_update_pll(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
+{
- struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
- struct asoc_simple_priv *priv = snd_soc_card_get_drvdata(rtd->card);
- struct device *dev = rtd->card->dev;
- struct tegra_audio_graph_data *graph_data =
(struct tegra_audio_graph_data *)priv->data;
- struct tegra_audio_chip_data *chip_data =
(struct tegra_audio_chip_data *)of_device_get_match_data(dev);
void* doesn't need casting
There are several similar places in the code. Not a big deal, but this makes code less readable than it could be.
I will drop these in next revision.
Hi Sameer
Add Tegra audio machine driver which is based on generic audio graph card driver. It re-uses most of the common stuff from audio graph driver and uses the same DT binding. Required Tegra specific customizations are done in the driver.
(snip)
+static const struct snd_soc_ops tegra_audio_graph_ops = {
- .startup = asoc_simple_startup,
- .shutdown = asoc_simple_shutdown,
- .hw_params = tegra_audio_graph_hw_params,
+};
This is just an idea, but can we use hooks here somehow ?
.ops_hook_pre .ops_hook_func .ops_hook_post
if (priv->ops_hook_pre->func) priv->ops_hook_pre->func_pre(...);
if (priv->ops_hook_func->func) priv->ops_hook_func->func(...); /* driver's function */ else graph_func(...); /* audio-graph function */ if (priv->ops_hook_post->func) priv->ops_hook_post->func(...);
+static int tegra_audio_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 err;
- /* Allocate the private data and the DAI link array */
- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
- if (!priv)
return -ENOMEM;
- priv->data = (struct tegra_audio_graph_data *)
devm_kzalloc(dev, sizeof(*priv->data), GFP_KERNEL);
- if (!priv->data)
return -ENOMEM;
- card = simple_priv_to_card(priv);
- card->owner = THIS_MODULE;
- card->dev = dev;
- card->component_chaining = true;
- card->probe = tegra_audio_graph_card_probe;
- priv->ops = &tegra_audio_graph_ops;
- priv->force_dpcm = 1;
- memset(&li, 0, sizeof(li));
- graph_get_dais_count(priv, &li);
- if (!li.link || !li.dais)
return -EINVAL;
- err = asoc_simple_init_priv(priv, &li);
- if (err < 0)
return err;
- err = graph_parse_of(priv);
- if (err < 0) {
if (err != -EPROBE_DEFER)
dev_err(dev, "Parse error %d\n", err);
goto cleanup;
- }
- snd_soc_card_set_drvdata(card, priv);
- asoc_simple_debug_info(priv);
- err = devm_snd_soc_register_card(dev, card);
- if (err < 0)
goto cleanup;
- return 0;
+cleanup:
- asoc_simple_clean_reference(card);
- return err;
+}
These are almost same as graph_probe(). Maybe we can separate graph_probe() and export function ?
struct tegra_audio_graph_data { struct asoc_simple_priv simple; ... }; #define simple_to_priv(_simple) container_of((_simple), struct my_priv, simple)
static int tegra_audio_graph_probe(struct platform_device *pdev) { struct tegra_audio_graph_data *data; struct asoc_simple_priv *priv;
/* Allocate the private data */ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM;
/* initial audio-graph */ ret = audio_graph_init(priv, pdev); if (ret < 0) return -xxx;
/* over-write for own settings */ card = simple_priv_to_card(priv); card->component_chaining = true; card->probe = tegra_audio_graph_card_probe;
priv = &data->simple; priv->ops_hook_pre = &tegra_audio_graph_ops; priv->force_dpcm = 1;
/* audio-graph remain */ return audio_graph_prove(priv, pdev); }
Thank you for your help !!
Best regards --- Kuninori Morimoto
Add Tegra audio machine driver which is based on generic audio graph card driver. It re-uses most of the common stuff from audio graph driver and uses the same DT binding. Required Tegra specific customizations are done in the driver.
(snip)
+static const struct snd_soc_ops tegra_audio_graph_ops = {
.startup = asoc_simple_startup,
.shutdown = asoc_simple_shutdown,
.hw_params = tegra_audio_graph_hw_params,
+};
This is just an idea, but can we use hooks here somehow ?
.ops_hook_pre .ops_hook_func .ops_hook_post if (priv->ops_hook_pre->func) priv->ops_hook_pre->func_pre(...); if (priv->ops_hook_func->func) priv->ops_hook_func->func(...); /* driver's function */ else graph_func(...); /* audio-graph function */ if (priv->ops_hook_post->func) priv->ops_hook_post->func(...);
Right now I just required to populate some flags or structures and do not have any specific pre()/post() functions to be called. Can this be reserved for later?
+static int tegra_audio_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 err;
/* Allocate the private data and the DAI link array */
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
priv->data = (struct tegra_audio_graph_data *)
devm_kzalloc(dev, sizeof(*priv->data), GFP_KERNEL);
if (!priv->data)
return -ENOMEM;
card = simple_priv_to_card(priv);
card->owner = THIS_MODULE;
card->dev = dev;
card->component_chaining = true;
card->probe = tegra_audio_graph_card_probe;
priv->ops = &tegra_audio_graph_ops;
priv->force_dpcm = 1;
memset(&li, 0, sizeof(li));
graph_get_dais_count(priv, &li);
if (!li.link || !li.dais)
return -EINVAL;
err = asoc_simple_init_priv(priv, &li);
if (err < 0)
return err;
err = graph_parse_of(priv);
if (err < 0) {
if (err != -EPROBE_DEFER)
dev_err(dev, "Parse error %d\n", err);
goto cleanup;
}
snd_soc_card_set_drvdata(card, priv);
asoc_simple_debug_info(priv);
err = devm_snd_soc_register_card(dev, card);
if (err < 0)
goto cleanup;
return 0;
+cleanup:
asoc_simple_clean_reference(card);
return err;
+}
These are almost same as graph_probe(). Maybe we can separate graph_probe() and export function ?
Yes possible, I can move more stuff into graph_parse_of() which is already an exported function in the current series. This can be utilized by both generic audio graph and Tegra audio graph.
Something like below,
static int tegra_audio_graph_probe(struct platform_device *pdev) { struct tegra_audio_priv *priv; struct device *dev = &pdev->dev; struct snd_soc_card *card;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM;
card = simple_priv_to_card(&priv->simple);
card->owner = THIS_MODULE; card->dev = dev; card->probe = tegra_audio_graph_card_probe;
/* graph_parse_of() depends on below */ card->component_chaining = 1; priv->simple.ops = &tegra_audio_graph_ops; priv->simple.force_dpcm = 1;
return graph_parse_of(&priv->simple); }
Does this sound fine?
Hi Sameer
This is just an idea, but can we use hooks here somehow ?
.ops_hook_pre .ops_hook_func .ops_hook_post if (priv->ops_hook_pre->func) priv->ops_hook_pre->func_pre(...); if (priv->ops_hook_func->func) priv->ops_hook_func->func(...); /* driver's function */ else graph_func(...); /* audio-graph function */ if (priv->ops_hook_post->func) priv->ops_hook_post->func(...);
Right now I just required to populate some flags or structures and do not have any specific pre()/post() functions to be called. Can this be reserved for later?
Yeah, of course :)
These are almost same as graph_probe(). Maybe we can separate graph_probe() and export function ?
Yes possible, I can move more stuff into graph_parse_of() which is already an exported function in the current series. This can be utilized by both generic audio graph and Tegra audio graph.
Something like below,
static int tegra_audio_graph_probe(struct platform_device *pdev) { struct tegra_audio_priv *priv; struct device *dev = &pdev->dev; struct snd_soc_card *card;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM;
card = simple_priv_to_card(&priv->simple);
card->owner = THIS_MODULE; card->dev = dev; card->probe = tegra_audio_graph_card_probe;
/* graph_parse_of() depends on below */ card->component_chaining = 1; priv->simple.ops = &tegra_audio_graph_ops; priv->simple.force_dpcm = 1;
return graph_parse_of(&priv->simple); }
I think graph side can handle card->owner / card->dev, but, it looks good to me
Thank you for your help !!
Best regards --- Kuninori Morimoto
This commit enables Tegra audio graph card driver which is based on the generic audio-graph card driver. This is intended to be used on platforms based on Tegra210 and later chips.
Signed-off-by: Sameer Pujar spujar@nvidia.com --- arch/arm64/configs/defconfig | 1 + 1 file changed, 1 insertion(+)
diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 55f9c35..2841f77 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -712,6 +712,7 @@ CONFIG_SND_SOC_TEGRA210_DMIC=m CONFIG_SND_SOC_TEGRA210_I2S=m CONFIG_SND_SOC_TEGRA186_DSPK=m CONFIG_SND_SOC_TEGRA210_ADMAIF=m +CONFIG_SND_SOC_TEGRA_AUDIO_GRAPH_CARD=m CONFIG_SND_SOC_AK4613=m CONFIG_SND_SOC_ES7134=m CONFIG_SND_SOC_ES7241=m
Expose a header which describes DT bindings required to use audio-graph based sound card. All Tegra210 based platforms can include this header and add platform specific information. Currently, from SoC point of view, all links are exposed for ADMAIF, AHUB, I2S and DMIC components.
Signed-off-by: Sameer Pujar spujar@nvidia.com --- .../boot/dts/nvidia/tegra210-audio-graph.dtsi | 138 +++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 arch/arm64/boot/dts/nvidia/tegra210-audio-graph.dtsi
diff --git a/arch/arm64/boot/dts/nvidia/tegra210-audio-graph.dtsi b/arch/arm64/boot/dts/nvidia/tegra210-audio-graph.dtsi new file mode 100644 index 0000000..6287d28c --- /dev/null +++ b/arch/arm64/boot/dts/nvidia/tegra210-audio-graph.dtsi @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: GPL-2.0 + +/ { + tegra_sound { + status = "disabled"; + + clocks = <&tegra_car TEGRA210_CLK_PLL_A>, + <&tegra_car TEGRA210_CLK_PLL_A_OUT0>; + clock-names = "pll_a", "plla_out0"; + + assigned-clocks = <&tegra_car TEGRA210_CLK_PLL_A>, + <&tegra_car TEGRA210_CLK_PLL_A_OUT0>, + <&tegra_car TEGRA210_CLK_EXTERN1>; + assigned-clock-parents = <0>, <0>, <&tegra_car TEGRA210_CLK_PLL_A_OUT0>; + assigned-clock-rates = <368640000>, <49152000>, <12288000>; + }; +}; + +&tegra_admaif { + admaif1_port: port@0 { + admaif1_ep: endpoint { + remote-endpoint = <&xbar_admaif1_ep>; + }; + }; + admaif2_port: port@1 { + admaif2_ep: endpoint { + remote-endpoint = <&xbar_admaif2_ep>; + }; + }; + admaif3_port: port@2 { + admaif3_ep: endpoint { + remote-endpoint = <&xbar_admaif3_ep>; + }; + }; + admaif4_port: port@3 { + admaif4_ep: endpoint { + remote-endpoint = <&xbar_admaif4_ep>; + }; + }; + admaif5_port: port@4 { + admaif5_ep: endpoint { + remote-endpoint = <&xbar_admaif5_ep>; + }; + }; + admaif6_port: port@5 { + admaif6_ep: endpoint { + remote-endpoint = <&xbar_admaif6_ep>; + }; + }; + admaif7_port: port@6 { + admaif7_ep: endpoint { + remote-endpoint = <&xbar_admaif7_ep>; + }; + }; + admaif8_port: port@7 { + admaif8_ep: endpoint { + remote-endpoint = <&xbar_admaif8_ep>; + }; + }; + admaif9_port: port@8 { + admaif9_ep: endpoint { + remote-endpoint = <&xbar_admaif9_ep>; + }; + }; + admaif10_port: port@9 { + admaif10_ep: endpoint { + remote-endpoint = <&xbar_admaif10_ep>; + }; + }; +}; + +&tegra_ahub { + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0x0>; + xbar_admaif1_ep: endpoint { + remote-endpoint = <&admaif1_ep>; + }; + }; + port@1 { + reg = <0x1>; + xbar_admaif2_ep: endpoint { + remote-endpoint = <&admaif2_ep>; + }; + }; + port@2 { + reg = <0x2>; + xbar_admaif3_ep: endpoint { + remote-endpoint = <&admaif3_ep>; + }; + }; + port@3 { + reg = <0x3>; + xbar_admaif4_ep: endpoint { + remote-endpoint = <&admaif4_ep>; + }; + }; + port@4 { + reg = <0x4>; + xbar_admaif5_ep: endpoint { + remote-endpoint = <&admaif5_ep>; + }; + }; + port@5 { + reg = <0x5>; + xbar_admaif6_ep: endpoint { + remote-endpoint = <&admaif6_ep>; + }; + }; + port@6 { + reg = <0x6>; + xbar_admaif7_ep: endpoint { + remote-endpoint = <&admaif7_ep>; + }; + }; + port@7 { + reg = <0x7>; + xbar_admaif8_ep: endpoint { + remote-endpoint = <&admaif8_ep>; + }; + }; + port@8 { + reg = <0x8>; + xbar_admaif9_ep: endpoint { + remote-endpoint = <&admaif9_ep>; + }; + }; + port@9 { + reg = <0x9>; + xbar_admaif10_ep: endpoint { + remote-endpoint = <&admaif10_ep>; + }; + }; + }; +};
Enable support for audio-graph based sound card on Jetson-Nano and Jetson-TX1. Depending on the platform, required I/O interfaces are enabled.
* Jetson-Nano: Enable I2S3, I2S4, DMIC1 and DMIC2. * Jetson-TX1: Enable all I2S and DMIC interfaces.
Signed-off-by: Sameer Pujar spujar@nvidia.com --- arch/arm64/boot/dts/nvidia/tegra210-p2371-2180.dts | 219 +++++++++++++++++++++ arch/arm64/boot/dts/nvidia/tegra210-p3450-0000.dts | 124 ++++++++++++ 2 files changed, 343 insertions(+)
diff --git a/arch/arm64/boot/dts/nvidia/tegra210-p2371-2180.dts b/arch/arm64/boot/dts/nvidia/tegra210-p2371-2180.dts index 4c9c2a0..1233d67 100644 --- a/arch/arm64/boot/dts/nvidia/tegra210-p2371-2180.dts +++ b/arch/arm64/boot/dts/nvidia/tegra210-p2371-2180.dts @@ -3,6 +3,7 @@
#include "tegra210-p2180.dtsi" #include "tegra210-p2597.dtsi" +#include "tegra210-audio-graph.dtsi"
/ { model = "NVIDIA Jetson TX1 Developer Kit"; @@ -127,4 +128,222 @@ status = "okay"; }; }; + + tegra_sound { + status = "okay"; + + compatible = "nvidia,tegra210-audio-graph-card"; + + dais = /* FE */ + <&admaif1_port>, <&admaif2_port>, <&admaif3_port>, + <&admaif4_port>, <&admaif5_port>, <&admaif6_port>, + <&admaif7_port>, <&admaif8_port>, <&admaif9_port>, + <&admaif10_port>, + /* Router */ + <&xbar_i2s1_port>, <&xbar_i2s2_port>, <&xbar_i2s3_port>, + <&xbar_i2s4_port>, <&xbar_i2s5_port>, <&xbar_dmic1_port>, + <&xbar_dmic2_port>, <&xbar_dmic3_port>, + /* I/O DAP Ports */ + <&i2s1_port>, <&i2s2_port>, <&i2s3_port>, <&i2s4_port>, + <&i2s5_port>, <&dmic1_port>, <&dmic2_port>, <&dmic3_port>; + + label = "jetson-tx1-ape"; + }; +}; + +&tegra_admaif { + status = "okay"; +}; + +&tegra_ahub { + status = "okay"; + + ports { + xbar_i2s1_port: port@a { + reg = <0xa>; + xbar_i2s1_ep: endpoint { + remote-endpoint = <&i2s1_cif_ep>; + }; + }; + xbar_i2s2_port: port@b { + reg = <0xb>; + xbar_i2s2_ep: endpoint { + remote-endpoint = <&i2s2_cif_ep>; + }; + }; + xbar_i2s3_port: port@c { + reg = <0xc>; + xbar_i2s3_ep: endpoint { + remote-endpoint = <&i2s3_cif_ep>; + }; + }; + xbar_i2s4_port: port@d { + reg = <0xd>; + xbar_i2s4_ep: endpoint { + remote-endpoint = <&i2s4_cif_ep>; + }; + }; + xbar_i2s5_port: port@e { + reg = <0xe>; + xbar_i2s5_ep: endpoint { + remote-endpoint = <&i2s5_cif_ep>; + }; + }; + xbar_dmic1_port: port@f { + reg = <0xf>; + xbar_dmic1_ep: endpoint { + remote-endpoint = <&dmic1_cif_ep>; + }; + }; + xbar_dmic2_port: port@10 { + reg = <0x10>; + xbar_dmic2_ep: endpoint { + remote-endpoint = <&dmic2_cif_ep>; + }; + }; + xbar_dmic3_port: port@11 { + reg = <0x11>; + xbar_dmic3_ep: endpoint { + remote-endpoint = <&dmic3_cif_ep>; + }; + }; + }; +}; + +&tegra_i2s1 { + status = "okay"; + + port@0 { + i2s1_cif_ep: endpoint { + remote-endpoint = <&xbar_i2s1_ep>; + }; + }; + + i2s1_port: port@1 { + i2s1_dap: endpoint { + dai-format = "i2s"; + + /* Placeholder for external Codec */ + }; + }; +}; + +&tegra_i2s2 { + status = "okay"; + + port@0 { + i2s2_cif_ep: endpoint { + remote-endpoint = <&xbar_i2s2_ep>; + }; + }; + + i2s2_port: port@1 { + i2s2_dap: endpoint { + dai-format = "i2s"; + + /* Placeholder for external Codec */ + }; + }; +}; + +&tegra_i2s3 { + status = "okay"; + + port@0 { + i2s3_cif_ep: endpoint { + remote-endpoint = <&xbar_i2s3_ep>; + }; + }; + + i2s3_port: port@1 { + i2s3_dap_ep: endpoint { + dai-format = "i2s"; + + /* Placeholder for external Codec */ + }; + }; +}; + +&tegra_i2s4 { + status = "okay"; + + port@0 { + i2s4_cif_ep: endpoint { + remote-endpoint = <&xbar_i2s4_ep>; + }; + }; + + i2s4_port: port@1 { + i2s4_dap: endpoint { + dai-format = "i2s"; + + /* Placeholder for external Codec */ + }; + }; +}; + +&tegra_i2s5 { + status = "okay"; + + port@0 { + i2s5_cif_ep: endpoint { + remote-endpoint = <&xbar_i2s5_ep>; + }; + }; + + i2s5_port: port@1 { + i2s5_dap: endpoint { + dai-format = "i2s"; + + /* Placeholder for external Codec */ + }; + }; +}; + +&tegra_dmic1 { + status = "okay"; + + port@0 { + dmic1_cif_ep: endpoint { + remote-endpoint = <&xbar_dmic1_ep>; + }; + }; + + dmic1_port: port@1 { + dmic1_dap: endpoint { + /* Placeholder for external Codec */ + }; + }; +}; + +&tegra_dmic2 { + status = "okay"; + + port@0 { + dmic2_cif_ep: endpoint { + remote-endpoint = <&xbar_dmic2_ep>; + }; + }; + + dmic2_port: port@1 { + dmic2_dap: endpoint { + /* Placeholder for external Codec */ + }; + }; +}; + +&tegra_dmic3 { + status = "okay"; + + port@0 { + dmic3_cif_ep: endpoint { + remote-endpoint = <&xbar_dmic3_ep>; + }; + }; + + dmic3_port: port@1 { + dmic3_dap: endpoint { + /* Placeholder for external Codec */ + }; + }; }; diff --git a/arch/arm64/boot/dts/nvidia/tegra210-p3450-0000.dts b/arch/arm64/boot/dts/nvidia/tegra210-p3450-0000.dts index 859241d..fcce8eed 100644 --- a/arch/arm64/boot/dts/nvidia/tegra210-p3450-0000.dts +++ b/arch/arm64/boot/dts/nvidia/tegra210-p3450-0000.dts @@ -6,6 +6,7 @@ #include <dt-bindings/mfd/max77620.h>
#include "tegra210.dtsi" +#include "tegra210-audio-graph.dtsi"
/ { model = "NVIDIA Jetson Nano Developer Kit"; @@ -870,4 +871,127 @@
vin-supply = <&vdd_5v0_sys>; }; + + tegra_sound { + status = "okay"; + + compatible = "nvidia,tegra210-audio-graph-card"; + + dais = /* FE */ + <&admaif1_port>, <&admaif2_port>, <&admaif3_port>, + <&admaif4_port>, <&admaif5_port>, <&admaif6_port>, + <&admaif7_port>, <&admaif8_port>, <&admaif9_port>, + <&admaif10_port>, + /* Router */ + <&xbar_i2s3_port>, <&xbar_i2s4_port>, + <&xbar_dmic1_port>, <&xbar_dmic2_port>, + /* I/O DAP Ports */ + <&i2s3_port>, <&i2s4_port>, + <&dmic1_port>, <&dmic2_port>; + + label = "jetson-nano-ape"; + }; +}; + +&tegra_admaif { + status = "okay"; +}; + +&tegra_ahub { + status = "okay"; + + ports { + xbar_i2s3_port: port@c { + reg = <0xc>; + xbar_i2s3_ep: endpoint { + remote-endpoint = <&i2s3_cif_ep>; + }; + }; + xbar_i2s4_port: port@d { + reg = <0xd>; + xbar_i2s4_ep: endpoint { + remote-endpoint = <&i2s4_cif_ep>; + }; + }; + xbar_dmic1_port: port@f { + reg = <0xf>; + xbar_dmic1_ep: endpoint { + remote-endpoint = <&dmic1_cif_ep>; + }; + }; + xbar_dmic2_port: port@10 { + reg = <0x10>; + xbar_dmic2_ep: endpoint { + remote-endpoint = <&dmic2_cif_ep>; + }; + }; + }; +}; + +&tegra_i2s3 { + status = "okay"; + + port@0 { + i2s3_cif_ep: endpoint { + remote-endpoint = <&xbar_i2s3_ep>; + }; + }; + + i2s3_port: port@1 { + i2s3_dap_ep: endpoint { + dai-format = "i2s"; + + /* Placeholder for external Codec */ + }; + }; +}; + +&tegra_i2s4 { + status = "okay"; + + port@0 { + i2s4_cif_ep: endpoint { + remote-endpoint = <&xbar_i2s4_ep>; + }; + }; + + i2s4_port: port@1 { + i2s4_dap: endpoint@0 { + dai-format = "i2s"; + + /* Placeholder for external Codec */ + }; + }; +}; + +&tegra_dmic1 { + status = "okay"; + + port@0 { + dmic1_cif_ep: endpoint@0 { + remote-endpoint = <&xbar_dmic1_ep>; + }; + }; + + dmic1_port: port@1 { + dmic1_dap: endpoint@0 { + /* Placeholder for external Codec */ + }; + }; +}; + +&tegra_dmic2 { + status = "okay"; + + port@0 { + dmic2_cif_ep: endpoint@0 { + remote-endpoint = <&xbar_dmic2_ep>; + }; + }; + + dmic2_port: port@1 { + dmic2_dap: endpoint@0 { + /* Placeholder for external Codec */ + }; + }; };
participants (8)
-
Dmitry Osipenko
-
Jon Hunter
-
kernel test robot
-
Kuninori Morimoto
-
Mark Brown
-
Michał Mirosław
-
Rob Herring
-
Sameer Pujar