summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2021-01-19 16:20:13 +0000
committerMark Brown <broonie@kernel.org>2021-01-19 16:20:13 +0000
commit6b050d45a60b3fa08d46eda845ec28d7addfd715 (patch)
treecbe4ee4dfe127099bf1dd55ca8bfea4536ba0421
parent97692432406d40c71933ee9f07c4dbe327af846b (diff)
parent202e2f7745437aa5b694de92cbd30bb43b23fbed (diff)
Merge series "Tegra210 audio graph card" from Sameer Pujar <spujar@nvidia.com>:
This series adds audio graph based sound card support for Tegra210 platforms like Jetson-TX1 an Jetson-Nano. The following preparatory audio graph enhancement series is already merged. * https://patchwork.kernel.org/project/alsa-devel/list/?series=375629&state=* Following are the summary of changes: * Add graph/audio-graph based schemas or schema updates for Tegra210 component and machine drivers. * Add Tegra audio graph machine driver. * Add required DT support for Jetson-TX1/Nano. This work is based on earlier discussion of DPCM usage for Tegra and simple card driver updates. * https://lkml.org/lkml/2020/4/30/519 * https://lkml.org/lkml/2020/6/27/4 Original v6 series was sent about 6-7 weeks back. The dependency commit, https://lore.kernel.org/alsa-devel/1610948585-16286-1-git-send-email-spujar@nvidia.com/ is now merged. Resending this now to appear in the top of the mail list. Changelog ========= v5 -> v6 -------- * Added ports or port description in YAML docs for Tegra AHUB devices and graph card in patch 1/6 and 2/6. Reference of audio-graph-port.yaml is used for AHUB devices. * Dropped redundant NULL check return for of_device_get_match_data() in patch 3/6. * Added 'Reviewed-by' tag from Jon Hunter. * No changes in remaining patches. v4 -> v5 -------- * Audio graph related changes were sent in separate v5 series as mentioned above and are dropped from current series. * Graph and audio graph doc patches are dropped from this series and are sent separately as mentioned above. * Minor change with phandle label for TX1 and Nano platform DT files. * No changes in other patches. 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 (6): ASoC: dt-bindings: tegra: Add graph bindings ASoC: dt-bindings: tegra: Add json-schema for Tegra 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 | 187 +++++++++++++++ .../bindings/sound/nvidia,tegra186-dspk.yaml | 18 +- .../bindings/sound/nvidia,tegra210-admaif.yaml | 13 +- .../bindings/sound/nvidia,tegra210-ahub.yaml | 13 +- .../bindings/sound/nvidia,tegra210-dmic.yaml | 18 +- .../bindings/sound/nvidia,tegra210-i2s.yaml | 18 +- .../boot/dts/nvidia/tegra210-audio-graph.dtsi | 153 ++++++++++++ arch/arm64/boot/dts/nvidia/tegra210-p2371-2180.dts | 262 +++++++++++++++++++++ arch/arm64/boot/dts/nvidia/tegra210-p3450-0000.dts | 146 ++++++++++++ arch/arm64/configs/defconfig | 1 + sound/soc/tegra/Kconfig | 9 + sound/soc/tegra/Makefile | 2 + sound/soc/tegra/tegra_audio_graph_card.c | 251 ++++++++++++++++++++ 13 files changed, 1085 insertions(+), 6 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 sound/soc/tegra/tegra_audio_graph_card.c -- 2.7.4
-rw-r--r--Documentation/devicetree/bindings/sound/nvidia,tegra-audio-graph-card.yaml187
-rw-r--r--Documentation/devicetree/bindings/sound/nvidia,tegra186-dspk.yaml18
-rw-r--r--Documentation/devicetree/bindings/sound/nvidia,tegra210-admaif.yaml13
-rw-r--r--Documentation/devicetree/bindings/sound/nvidia,tegra210-ahub.yaml13
-rw-r--r--Documentation/devicetree/bindings/sound/nvidia,tegra210-dmic.yaml18
-rw-r--r--Documentation/devicetree/bindings/sound/nvidia,tegra210-i2s.yaml18
-rw-r--r--sound/soc/tegra/Kconfig9
-rw-r--r--sound/soc/tegra/Makefile2
-rw-r--r--sound/soc/tegra/tegra_audio_graph_card.c251
9 files changed, 523 insertions, 6 deletions
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 000000000000..fc271f644aaf
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/nvidia,tegra-audio-graph-card.yaml
@@ -0,0 +1,187 @@
+# 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.
+
+maintainers:
+ - Jon Hunter <jonathanh@nvidia.com>
+ - Sameer Pujar <spujar@nvidia.com>
+
+allOf:
+ - $ref: audio-graph.yaml#
+
+properties:
+ compatible:
+ enum:
+ - nvidia,tegra210-audio-graph-card
+ - 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:
+ - clocks
+ - clock-names
+ - assigned-clocks
+ - assigned-clock-parents
+
+unevaluatedProperties: false
+
+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>;
+
+ dais = /* FE */
+ <&admaif1_port>,
+ /* Router */
+ <&xbar_i2s1_port>,
+ /* I/O DAP Ports */
+ <&i2s1_port>;
+
+ label = "jetson-tx1-ape";
+ };
+
+ // The ports are defined for AHUB and its child devices.
+ ahub@702d0800 {
+ compatible = "nvidia,tegra210-ahub";
+ reg = <0x702d0800 0x800>;
+ clocks = <&tegra_car TEGRA210_CLK_D_AUDIO>;
+ clock-names = "ahub";
+ assigned-clocks = <&tegra_car TEGRA210_CLK_D_AUDIO>;
+ assigned-clock-parents = <&tegra_car TEGRA210_CLK_PLL_A_OUT0>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0x702d0000 0x702d0000 0x0000e400>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0x0>;
+ xbar_admaif1_ep: endpoint {
+ remote-endpoint = <&admaif1_ep>;
+ };
+ };
+
+ // ...
+
+ xbar_i2s1_port: port@a {
+ reg = <0xa>;
+ xbar_i2s1_ep: endpoint {
+ remote-endpoint = <&i2s1_cif_ep>;
+ };
+ };
+ };
+
+ admaif@702d0000 {
+ compatible = "nvidia,tegra210-admaif";
+ reg = <0x702d0000 0x800>;
+ dmas = <&adma 1>, <&adma 1>,
+ <&adma 2>, <&adma 2>,
+ <&adma 3>, <&adma 3>,
+ <&adma 4>, <&adma 4>,
+ <&adma 5>, <&adma 5>,
+ <&adma 6>, <&adma 6>,
+ <&adma 7>, <&adma 7>,
+ <&adma 8>, <&adma 8>,
+ <&adma 9>, <&adma 9>,
+ <&adma 10>, <&adma 10>;
+ dma-names = "rx1", "tx1",
+ "rx2", "tx2",
+ "rx3", "tx3",
+ "rx4", "tx4",
+ "rx5", "tx5",
+ "rx6", "tx6",
+ "rx7", "tx7",
+ "rx8", "tx8",
+ "rx9", "tx9",
+ "rx10", "tx10";
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ admaif1_port: port@0 {
+ reg = <0x0>;
+ admaif1_ep: endpoint {
+ remote-endpoint = <&xbar_admaif1_ep>;
+ };
+ };
+
+ // More ADMAIF ports to follow
+ };
+ };
+
+ i2s@702d1000 {
+ compatible = "nvidia,tegra210-i2s";
+ clocks = <&tegra_car TEGRA210_CLK_I2S0>;
+ clock-names = "i2s";
+ assigned-clocks = <&tegra_car TEGRA210_CLK_I2S0>;
+ assigned-clock-parents = <&tegra_car TEGRA210_CLK_PLL_A_OUT0>;
+ assigned-clock-rates = <1536000>;
+ reg = <0x702d1000 0x100>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0x0>;
+
+ i2s1_cif_ep: endpoint {
+ remote-endpoint = <&xbar_i2s1_ep>;
+ };
+ };
+
+ i2s1_port: port@1 {
+ reg = <0x1>;
+
+ i2s1_dap: endpoint {
+ dai-format = "i2s";
+ };
+ };
+ };
+ };
+ };
+
+...
diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra186-dspk.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra186-dspk.yaml
index ed2fb32fcdd4..b8645d9c38ac 100644
--- a/Documentation/devicetree/bindings/sound/nvidia,tegra186-dspk.yaml
+++ b/Documentation/devicetree/bindings/sound/nvidia,tegra186-dspk.yaml
@@ -17,6 +17,9 @@ maintainers:
- Jon Hunter <jonathanh@nvidia.com>
- Sameer Pujar <spujar@nvidia.com>
+allOf:
+ - $ref: audio-graph-port.yaml#
+
properties:
$nodename:
pattern: "^dspk@[0-9a-f]*$"
@@ -55,6 +58,19 @@ properties:
The name can be "DSPK1" or "DSPKx", where x depends on the maximum
available instances on a Tegra SoC.
+ ports:
+ type: object
+ properties:
+ port@0:
+ description: |
+ DSPK ACIF (Audio Client Interface) port connected to the
+ corresponding AHUB (Audio Hub) ACIF port.
+
+ port@1:
+ description: |
+ DSPK DAP (Digital Audio Port) interface which can be connected
+ to external audio codec for playback.
+
required:
- compatible
- reg
@@ -64,7 +80,7 @@ required:
- assigned-clock-parents
- sound-name-prefix
-additionalProperties: false
+unevaluatedProperties: false
examples:
- |
diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra210-admaif.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra210-admaif.yaml
index c028b259e822..7cee7722df41 100644
--- a/Documentation/devicetree/bindings/sound/nvidia,tegra210-admaif.yaml
+++ b/Documentation/devicetree/bindings/sound/nvidia,tegra210-admaif.yaml
@@ -17,6 +17,9 @@ maintainers:
- Jon Hunter <jonathanh@nvidia.com>
- Sameer Pujar <spujar@nvidia.com>
+allOf:
+ - $ref: audio-graph-port.yaml#
+
properties:
$nodename:
pattern: "^admaif@[0-9a-f]*$"
@@ -37,6 +40,14 @@ properties:
dma-names: true
+ ports:
+ description: |
+ Contains list of ACIF (Audio CIF) port nodes for ADMAIF channels.
+ The number of port nodes depends on the number of ADMAIF channels
+ that SoC may have. These are interfaced with respective ACIF ports
+ in AHUB (Audio Hub). Each port is capable of data transfers in
+ both directions.
+
if:
properties:
compatible:
@@ -81,7 +92,7 @@ required:
- dmas
- dma-names
-additionalProperties: false
+unevaluatedProperties: false
examples:
- |
diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra210-ahub.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra210-ahub.yaml
index d77219727768..31f3e51974bb 100644
--- a/Documentation/devicetree/bindings/sound/nvidia,tegra210-ahub.yaml
+++ b/Documentation/devicetree/bindings/sound/nvidia,tegra210-ahub.yaml
@@ -17,6 +17,9 @@ maintainers:
- Jon Hunter <jonathanh@nvidia.com>
- Sameer Pujar <spujar@nvidia.com>
+allOf:
+ - $ref: audio-graph-port.yaml#
+
properties:
$nodename:
pattern: "^ahub@[0-9a-f]*$"
@@ -56,6 +59,13 @@ properties:
ranges: true
+ ports:
+ description: |
+ Contains list of ACIF (Audio CIF) port nodes for AHUB (Audio Hub).
+ These are connected to ACIF interfaces of AHUB clients. Thus the
+ number of port nodes depend on the number of clients that AHUB may
+ have depending on the SoC revision.
+
required:
- compatible
- reg
@@ -67,8 +77,7 @@ required:
- "#size-cells"
- ranges
-additionalProperties:
- type: object
+unevaluatedProperties: false
examples:
- |
diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra210-dmic.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra210-dmic.yaml
index 2a3207b550e7..89f4f471be24 100644
--- a/Documentation/devicetree/bindings/sound/nvidia,tegra210-dmic.yaml
+++ b/Documentation/devicetree/bindings/sound/nvidia,tegra210-dmic.yaml
@@ -16,6 +16,9 @@ maintainers:
- Jon Hunter <jonathanh@nvidia.com>
- Sameer Pujar <spujar@nvidia.com>
+allOf:
+ - $ref: audio-graph-port.yaml#
+
properties:
$nodename:
pattern: "^dmic@[0-9a-f]*$"
@@ -56,6 +59,19 @@ properties:
The name can be "DMIC1" or "DMIC2" ... "DMICx", where x depends
on the maximum available instances on a Tegra SoC.
+ ports:
+ type: object
+ properties:
+ port@0:
+ description: |
+ DMIC ACIF (Audio Client Interface) port connected to the
+ corresponding AHUB (Audio Hub) ACIF port.
+
+ port@1:
+ description: |
+ DMIC DAP (Digital Audio Port) interface which can be connected
+ to external audio codec for capture.
+
required:
- compatible
- reg
@@ -64,7 +80,7 @@ required:
- assigned-clocks
- assigned-clock-parents
-additionalProperties: false
+unevaluatedProperties: false
examples:
- |
diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra210-i2s.yaml b/Documentation/devicetree/bindings/sound/nvidia,tegra210-i2s.yaml
index dfc1bf7b7722..556460332ffb 100644
--- a/Documentation/devicetree/bindings/sound/nvidia,tegra210-i2s.yaml
+++ b/Documentation/devicetree/bindings/sound/nvidia,tegra210-i2s.yaml
@@ -16,6 +16,9 @@ maintainers:
- Jon Hunter <jonathanh@nvidia.com>
- Sameer Pujar <spujar@nvidia.com>
+allOf:
+ - $ref: audio-graph-port.yaml#
+
properties:
$nodename:
pattern: "^i2s@[0-9a-f]*$"
@@ -74,6 +77,19 @@ properties:
The name can be "I2S1" or "I2S2" ... "I2Sx", where x depends
on the maximum available instances on a Tegra SoC.
+ ports:
+ type: object
+ properties:
+ port@0:
+ description: |
+ I2S ACIF (Audio Client Interface) port connected to the
+ corresponding AHUB (Audio Hub) ACIF port.
+
+ port@1:
+ description: |
+ I2S DAP (Digital Audio Port) interface which can be connected
+ to external audio codec for playback or capture.
+
required:
- compatible
- reg
@@ -82,7 +98,7 @@ required:
- assigned-clocks
- assigned-clock-parents
-additionalProperties: false
+unevaluatedProperties: false
examples:
- |
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig
index a62cc87551ac..6dc83ad8607a 100644
--- a/sound/soc/tegra/Kconfig
+++ b/sound/soc/tegra/Kconfig
@@ -117,6 +117,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 60040a06b814..b17dd6eef92a 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 000000000000..9e43f16f9336
--- /dev/null
+++ b/sound/soc/tegra/tegra_audio_graph_card.c
@@ -0,0 +1,251 @@
+// 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
+
+#define simple_to_tegra_priv(simple) \
+ container_of(simple, struct tegra_audio_priv, simple)
+
+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_priv {
+ struct asoc_simple_priv simple;
+ struct clk *clk_plla_out0;
+ struct clk *clk_plla;
+};
+
+/* Tegra audio chip data */
+struct tegra_audio_cdata {
+ unsigned int plla_rates[NUM_RATE_TYPE];
+ unsigned int plla_out0_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 *simple = snd_soc_card_get_drvdata(rtd->card);
+ struct tegra_audio_priv *priv = simple_to_tegra_priv(simple);
+ struct device *dev = rtd->card->dev;
+ const struct tegra_audio_cdata *data = of_device_get_match_data(dev);
+ unsigned int plla_rate, plla_out0_rate, bclk;
+ unsigned int srate = params_rate(params);
+ int err;
+
+ switch (srate) {
+ case 11025:
+ case 22050:
+ case 44100:
+ case 88200:
+ case 176400:
+ plla_out0_rate = data->plla_out0_rates[x11_RATE];
+ plla_rate = data->plla_rates[x11_RATE];
+ break;
+ case 8000:
+ case 16000:
+ case 32000:
+ case 48000:
+ case 96000:
+ case 192000:
+ plla_out0_rate = data->plla_out0_rates[x8_RATE];
+ plla_rate = 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(priv->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(priv->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 *simple = snd_soc_card_get_drvdata(card);
+ struct tegra_audio_priv *priv = simple_to_tegra_priv(simple);
+
+ priv->clk_plla = devm_clk_get(card->dev, "pll_a");
+ if (IS_ERR(priv->clk_plla)) {
+ dev_err(card->dev, "Can't retrieve clk pll_a\n");
+ return PTR_ERR(priv->clk_plla);
+ }
+
+ priv->clk_plla_out0 = devm_clk_get(card->dev, "plla_out0");
+ if (IS_ERR(priv->clk_plla_out0)) {
+ dev_err(card->dev, "Can't retrieve clk plla_out0\n");
+ return PTR_ERR(priv->clk_plla_out0);
+ }
+
+ return graph_card_probe(card);
+}
+
+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->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, dev);
+}
+
+static const struct tegra_audio_cdata 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_cdata 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>");