@@ -135,6 +135,17 @@ config SND_SOC_TEGRA210_AMX
output frame by any combination of bytes from the input frames.
Say Y or M if you want to add support for Tegra210 AMX module.
+config SND_SOC_TEGRA210_ADX
+ tristate "Tegra210 ADX module"
+ help
+ Config to enable the Audio Demultiplexer (ADX) which takes an
+ input stream (up to 16 channels) and demultiplexes it into four
+ output streams (each of up to 16 channels). A byte RAM helps to
+ form output frames by any combination of bytes from the input
+ frame. Its design is identical to that of byte RAM in the AMX
+ except that the data flow direction is reversed.
+ Say Y or M if you want to add support for Tegra210 ADX module.
+
config SND_SOC_TEGRA_AUDIO_GRAPH_CARD
tristate "Audio Graph Card based Tegra driver"
depends on SND_AUDIO_GRAPH_CARD
@@ -16,6 +16,7 @@ snd-soc-tegra210-admaif-objs := tegra210_admaif.o
snd-soc-tegra210-mvc-objs := tegra210_mvc.o
snd-soc-tegra210-sfc-objs := tegra210_sfc.o
snd-soc-tegra210-amx-objs := tegra210_amx.o
+snd-soc-tegra210-adx-objs := tegra210_adx.o
obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-pcm.o
obj-$(CONFIG_SND_SOC_TEGRA20_AC97) += snd-soc-tegra20-ac97.o
@@ -32,6 +33,7 @@ obj-$(CONFIG_SND_SOC_TEGRA210_ADMAIF) += snd-soc-tegra210-admaif.o
obj-$(CONFIG_SND_SOC_TEGRA210_MVC) += snd-soc-tegra210-mvc.o
obj-$(CONFIG_SND_SOC_TEGRA210_SFC) += snd-soc-tegra210-sfc.o
obj-$(CONFIG_SND_SOC_TEGRA210_AMX) += snd-soc-tegra210-amx.o
+obj-$(CONFIG_SND_SOC_TEGRA210_ADX) += snd-soc-tegra210-adx.o
# Tegra machine Support
snd-soc-tegra-wm8903-objs := tegra_wm8903.o
new file mode 100644
@@ -0,0 +1,527 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// tegra210_adx.c - Tegra210 ADX driver
+//
+// Copyright (c) 2021 NVIDIA CORPORATION. All rights reserved.
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "tegra210_adx.h"
+#include "tegra_cif.h"
+
+static const struct reg_default tegra210_adx_reg_defaults[] = {
+ { TEGRA210_ADX_RX_INT_MASK, 0x00000001},
+ { TEGRA210_ADX_RX_CIF_CTRL, 0x00007000},
+ { TEGRA210_ADX_TX_INT_MASK, 0x0000000f },
+ { TEGRA210_ADX_TX1_CIF_CTRL, 0x00007000},
+ { TEGRA210_ADX_TX2_CIF_CTRL, 0x00007000},
+ { TEGRA210_ADX_TX3_CIF_CTRL, 0x00007000},
+ { TEGRA210_ADX_TX4_CIF_CTRL, 0x00007000},
+ { TEGRA210_ADX_CG, 0x1},
+ { TEGRA210_ADX_CFG_RAM_CTRL, 0x00004000},
+};
+
+static void tegra210_adx_write_map_ram(struct tegra210_adx *adx)
+{
+ int i;
+
+ regmap_write(adx->regmap, TEGRA210_ADX_CFG_RAM_CTRL,
+ TEGRA210_ADX_CFG_RAM_CTRL_SEQ_ACCESS_EN |
+ TEGRA210_ADX_CFG_RAM_CTRL_ADDR_INIT_EN |
+ TEGRA210_ADX_CFG_RAM_CTRL_RW_WRITE);
+
+ for (i = 0; i < TEGRA210_ADX_RAM_DEPTH; i++)
+ regmap_write(adx->regmap, TEGRA210_ADX_CFG_RAM_DATA,
+ adx->map[i]);
+
+ regmap_write(adx->regmap, TEGRA210_ADX_IN_BYTE_EN0, adx->byte_mask[0]);
+ regmap_write(adx->regmap, TEGRA210_ADX_IN_BYTE_EN1, adx->byte_mask[1]);
+}
+
+static int tegra210_adx_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct tegra210_adx *adx = snd_soc_dai_get_drvdata(dai);
+ unsigned int val;
+ int err;
+
+ /* Ensure if ADX status is disabled */
+ err = regmap_read_poll_timeout_atomic(adx->regmap, TEGRA210_ADX_STATUS,
+ val, !(val & 0x1), 10, 10000);
+ if (err < 0) {
+ dev_err(dai->dev, "failed to stop ADX, err = %d\n", err);
+ return err;
+ }
+
+ /* SW reset */
+ regmap_update_bits(adx->regmap, TEGRA210_ADX_SOFT_RESET,
+ TEGRA210_ADX_SOFT_RESET_SOFT_RESET_MASK,
+ TEGRA210_ADX_SOFT_RESET_SOFT_EN);
+
+ err = regmap_read_poll_timeout(adx->regmap, TEGRA210_ADX_SOFT_RESET,
+ val, !(val & 0x1), 10, 10000);
+ if (err < 0) {
+ dev_err(dai->dev, "failed to reset ADX, err = %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+static int __maybe_unused tegra210_adx_runtime_suspend(struct device *dev)
+{
+ struct tegra210_adx *adx = dev_get_drvdata(dev);
+
+ regcache_cache_only(adx->regmap, true);
+ regcache_mark_dirty(adx->regmap);
+
+ return 0;
+}
+
+static int __maybe_unused tegra210_adx_runtime_resume(struct device *dev)
+{
+ struct tegra210_adx *adx = dev_get_drvdata(dev);
+
+ regcache_cache_only(adx->regmap, false);
+ regcache_sync(adx->regmap);
+
+ tegra210_adx_write_map_ram(adx);
+
+ return 0;
+}
+
+static int tegra210_adx_set_audio_cif(struct snd_soc_dai *dai,
+ unsigned int channels,
+ unsigned int format,
+ unsigned int reg)
+{
+ struct tegra210_adx *adx = snd_soc_dai_get_drvdata(dai);
+ struct tegra_cif_conf cif_conf;
+ int audio_bits;
+
+ memset(&cif_conf, 0, sizeof(struct tegra_cif_conf));
+
+ if (channels < 1 || channels > 16)
+ return -EINVAL;
+
+ switch (format) {
+ case SNDRV_PCM_FORMAT_S8:
+ audio_bits = TEGRA_ACIF_BITS_8;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ audio_bits = TEGRA_ACIF_BITS_16;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ audio_bits = TEGRA_ACIF_BITS_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ cif_conf.audio_ch = channels;
+ cif_conf.client_ch = channels;
+ cif_conf.audio_bits = audio_bits;
+ cif_conf.client_bits = audio_bits;
+
+ tegra_set_cif(adx->regmap, reg, &cif_conf);
+
+ return 0;
+}
+
+static int tegra210_adx_out_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ return tegra210_adx_set_audio_cif(dai, params_channels(params),
+ params_format(params),
+ TEGRA210_ADX_TX1_CIF_CTRL + ((dai->id - 1) * TEGRA210_ADX_AUDIOCIF_CH_STRIDE));
+}
+
+static int tegra210_adx_in_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ return tegra210_adx_set_audio_cif(dai, params_channels(params),
+ params_format(params),
+ TEGRA210_ADX_RX_CIF_CTRL);
+}
+
+static int tegra210_adx_get_byte_map(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct tegra210_adx *adx = snd_soc_component_get_drvdata(cmpnt);
+ struct soc_mixer_control *mc;
+ unsigned char *bytes_map = (unsigned char *)&adx->map;
+ int enabled;
+
+ mc = (struct soc_mixer_control *)kcontrol->private_value;
+ enabled = adx->byte_mask[mc->reg / 32] & (1 << (mc->reg % 32));
+
+ if (enabled)
+ ucontrol->value.integer.value[0] = bytes_map[mc->reg];
+ else
+ ucontrol->value.integer.value[0] = 0;
+
+ return 0;
+}
+
+static int tegra210_adx_put_byte_map(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+ struct tegra210_adx *adx = snd_soc_component_get_drvdata(cmpnt);
+ struct soc_mixer_control *mc;
+ unsigned char *bytes_map = (unsigned char *)&adx->map;
+ int value = ucontrol->value.integer.value[0];
+
+ mc = (struct soc_mixer_control *)kcontrol->private_value;
+
+ if (value >= 0 && value <= 255) {
+ /* update byte map and enable slot */
+ bytes_map[mc->reg] = value;
+ adx->byte_mask[mc->reg / 32] |= (1 << (mc->reg % 32));
+ } else {
+ /* reset byte map and disable slot */
+ bytes_map[mc->reg] = 0;
+ adx->byte_mask[mc->reg / 32] &= ~(1 << (mc->reg % 32));
+ }
+
+ return 0;
+}
+
+static struct snd_soc_dai_ops tegra210_adx_in_dai_ops = {
+ .hw_params = tegra210_adx_in_hw_params,
+ .startup = tegra210_adx_startup,
+};
+
+static struct snd_soc_dai_ops tegra210_adx_out_dai_ops = {
+ .hw_params = tegra210_adx_out_hw_params,
+};
+
+#define IN_DAI \
+ { \
+ .name = "ADX-RX-CIF", \
+ .playback = { \
+ .stream_name = "RX-CIF-Playback", \
+ .channels_min = 1, \
+ .channels_max = 16, \
+ .rates = SNDRV_PCM_RATE_8000_192000, \
+ .formats = SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE, \
+ }, \
+ .capture = { \
+ .stream_name = "RX-CIF-Capture", \
+ .channels_min = 1, \
+ .channels_max = 16, \
+ .rates = SNDRV_PCM_RATE_8000_192000, \
+ .formats = SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE, \
+ }, \
+ .ops = &tegra210_adx_in_dai_ops, \
+ }
+
+#define OUT_DAI(id) \
+ { \
+ .name = "ADX-TX" #id "-CIF", \
+ .playback = { \
+ .stream_name = "TX" #id "-CIF-Playback",\
+ .channels_min = 1, \
+ .channels_max = 16, \
+ .rates = SNDRV_PCM_RATE_8000_192000, \
+ .formats = SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE, \
+ }, \
+ .capture = { \
+ .stream_name = "TX" #id "-CIF-Capture", \
+ .channels_min = 1, \
+ .channels_max = 16, \
+ .rates = SNDRV_PCM_RATE_8000_192000, \
+ .formats = SNDRV_PCM_FMTBIT_S8 | \
+ SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE, \
+ }, \
+ .ops = &tegra210_adx_out_dai_ops, \
+ }
+
+static struct snd_soc_dai_driver tegra210_adx_dais[] = {
+ IN_DAI,
+ OUT_DAI(1),
+ OUT_DAI(2),
+ OUT_DAI(3),
+ OUT_DAI(4),
+};
+
+static const struct snd_soc_dapm_widget tegra210_adx_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("RX", NULL, 0, TEGRA210_ADX_ENABLE,
+ TEGRA210_ADX_ENABLE_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("TX1", NULL, 0, TEGRA210_ADX_CTRL, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TX2", NULL, 0, TEGRA210_ADX_CTRL, 1, 0),
+ SND_SOC_DAPM_AIF_OUT("TX3", NULL, 0, TEGRA210_ADX_CTRL, 2, 0),
+ SND_SOC_DAPM_AIF_OUT("TX4", NULL, 0, TEGRA210_ADX_CTRL, 3, 0),
+};
+
+#define STREAM_ROUTES(id, sname) \
+ { "XBAR-" sname, NULL, "XBAR-TX" }, \
+ { "RX-CIF-" sname, NULL, "XBAR-" sname }, \
+ { "RX", NULL, "RX-CIF-" sname }, \
+ { "TX" #id, NULL, "RX" }, \
+ { "TX" #id "-CIF-" sname, NULL, "TX" #id }, \
+ { "TX" #id " XBAR-" sname, NULL, "TX" #id "-CIF-" sname }, \
+ { "TX" #id " XBAR-RX", NULL, "TX" #id " XBAR-" sname }
+
+#define ADX_ROUTES(id) \
+ STREAM_ROUTES(id, "Playback"), \
+ STREAM_ROUTES(id, "Capture")
+
+#define STREAM_ROUTES(id, sname) \
+ { "XBAR-" sname, NULL, "XBAR-TX" }, \
+ { "RX-CIF-" sname, NULL, "XBAR-" sname }, \
+ { "RX", NULL, "RX-CIF-" sname }, \
+ { "TX" #id, NULL, "RX" }, \
+ { "TX" #id "-CIF-" sname, NULL, "TX" #id }, \
+ { "TX" #id " XBAR-" sname, NULL, "TX" #id "-CIF-" sname }, \
+ { "TX" #id " XBAR-RX", NULL, "TX" #id " XBAR-" sname }
+
+#define ADX_ROUTES(id) \
+ STREAM_ROUTES(id, "Playback"), \
+ STREAM_ROUTES(id, "Capture")
+
+static const struct snd_soc_dapm_route tegra210_adx_routes[] = {
+ ADX_ROUTES(1),
+ ADX_ROUTES(2),
+ ADX_ROUTES(3),
+ ADX_ROUTES(4),
+};
+
+#define TEGRA210_ADX_BYTE_MAP_CTRL(reg) \
+ SOC_SINGLE_EXT("Byte Map " #reg, reg, 0, 256, 0, \
+ tegra210_adx_get_byte_map, \
+ tegra210_adx_put_byte_map)
+
+static struct snd_kcontrol_new tegra210_adx_controls[] = {
+ TEGRA210_ADX_BYTE_MAP_CTRL(0),
+ TEGRA210_ADX_BYTE_MAP_CTRL(1),
+ TEGRA210_ADX_BYTE_MAP_CTRL(2),
+ TEGRA210_ADX_BYTE_MAP_CTRL(3),
+ TEGRA210_ADX_BYTE_MAP_CTRL(4),
+ TEGRA210_ADX_BYTE_MAP_CTRL(5),
+ TEGRA210_ADX_BYTE_MAP_CTRL(6),
+ TEGRA210_ADX_BYTE_MAP_CTRL(7),
+ TEGRA210_ADX_BYTE_MAP_CTRL(8),
+ TEGRA210_ADX_BYTE_MAP_CTRL(9),
+ TEGRA210_ADX_BYTE_MAP_CTRL(10),
+ TEGRA210_ADX_BYTE_MAP_CTRL(11),
+ TEGRA210_ADX_BYTE_MAP_CTRL(12),
+ TEGRA210_ADX_BYTE_MAP_CTRL(13),
+ TEGRA210_ADX_BYTE_MAP_CTRL(14),
+ TEGRA210_ADX_BYTE_MAP_CTRL(15),
+ TEGRA210_ADX_BYTE_MAP_CTRL(16),
+ TEGRA210_ADX_BYTE_MAP_CTRL(17),
+ TEGRA210_ADX_BYTE_MAP_CTRL(18),
+ TEGRA210_ADX_BYTE_MAP_CTRL(19),
+ TEGRA210_ADX_BYTE_MAP_CTRL(20),
+ TEGRA210_ADX_BYTE_MAP_CTRL(21),
+ TEGRA210_ADX_BYTE_MAP_CTRL(22),
+ TEGRA210_ADX_BYTE_MAP_CTRL(23),
+ TEGRA210_ADX_BYTE_MAP_CTRL(24),
+ TEGRA210_ADX_BYTE_MAP_CTRL(25),
+ TEGRA210_ADX_BYTE_MAP_CTRL(26),
+ TEGRA210_ADX_BYTE_MAP_CTRL(27),
+ TEGRA210_ADX_BYTE_MAP_CTRL(28),
+ TEGRA210_ADX_BYTE_MAP_CTRL(29),
+ TEGRA210_ADX_BYTE_MAP_CTRL(30),
+ TEGRA210_ADX_BYTE_MAP_CTRL(31),
+ TEGRA210_ADX_BYTE_MAP_CTRL(32),
+ TEGRA210_ADX_BYTE_MAP_CTRL(33),
+ TEGRA210_ADX_BYTE_MAP_CTRL(34),
+ TEGRA210_ADX_BYTE_MAP_CTRL(35),
+ TEGRA210_ADX_BYTE_MAP_CTRL(36),
+ TEGRA210_ADX_BYTE_MAP_CTRL(37),
+ TEGRA210_ADX_BYTE_MAP_CTRL(38),
+ TEGRA210_ADX_BYTE_MAP_CTRL(39),
+ TEGRA210_ADX_BYTE_MAP_CTRL(40),
+ TEGRA210_ADX_BYTE_MAP_CTRL(41),
+ TEGRA210_ADX_BYTE_MAP_CTRL(42),
+ TEGRA210_ADX_BYTE_MAP_CTRL(43),
+ TEGRA210_ADX_BYTE_MAP_CTRL(44),
+ TEGRA210_ADX_BYTE_MAP_CTRL(45),
+ TEGRA210_ADX_BYTE_MAP_CTRL(46),
+ TEGRA210_ADX_BYTE_MAP_CTRL(47),
+ TEGRA210_ADX_BYTE_MAP_CTRL(48),
+ TEGRA210_ADX_BYTE_MAP_CTRL(49),
+ TEGRA210_ADX_BYTE_MAP_CTRL(50),
+ TEGRA210_ADX_BYTE_MAP_CTRL(51),
+ TEGRA210_ADX_BYTE_MAP_CTRL(52),
+ TEGRA210_ADX_BYTE_MAP_CTRL(53),
+ TEGRA210_ADX_BYTE_MAP_CTRL(54),
+ TEGRA210_ADX_BYTE_MAP_CTRL(55),
+ TEGRA210_ADX_BYTE_MAP_CTRL(56),
+ TEGRA210_ADX_BYTE_MAP_CTRL(57),
+ TEGRA210_ADX_BYTE_MAP_CTRL(58),
+ TEGRA210_ADX_BYTE_MAP_CTRL(59),
+ TEGRA210_ADX_BYTE_MAP_CTRL(60),
+ TEGRA210_ADX_BYTE_MAP_CTRL(61),
+ TEGRA210_ADX_BYTE_MAP_CTRL(62),
+ TEGRA210_ADX_BYTE_MAP_CTRL(63),
+};
+
+static const struct snd_soc_component_driver tegra210_adx_cmpnt = {
+ .dapm_widgets = tegra210_adx_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tegra210_adx_widgets),
+ .dapm_routes = tegra210_adx_routes,
+ .num_dapm_routes = ARRAY_SIZE(tegra210_adx_routes),
+ .controls = tegra210_adx_controls,
+ .num_controls = ARRAY_SIZE(tegra210_adx_controls),
+};
+
+static bool tegra210_adx_wr_reg(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_ADX_TX_INT_MASK ... TEGRA210_ADX_TX4_CIF_CTRL:
+ case TEGRA210_ADX_RX_INT_MASK ... TEGRA210_ADX_RX_CIF_CTRL:
+ case TEGRA210_ADX_ENABLE ... TEGRA210_ADX_CG:
+ case TEGRA210_ADX_CTRL ... TEGRA210_ADX_IN_BYTE_EN1:
+ case TEGRA210_ADX_CFG_RAM_CTRL ... TEGRA210_ADX_CFG_RAM_DATA:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool tegra210_adx_rd_reg(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_ADX_RX_STATUS ... TEGRA210_ADX_CFG_RAM_DATA:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool tegra210_adx_volatile_reg(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_ADX_RX_STATUS:
+ case TEGRA210_ADX_RX_INT_STATUS:
+ case TEGRA210_ADX_RX_INT_SET:
+ case TEGRA210_ADX_TX_STATUS:
+ case TEGRA210_ADX_TX_INT_STATUS:
+ case TEGRA210_ADX_TX_INT_SET:
+ case TEGRA210_ADX_SOFT_RESET:
+ case TEGRA210_ADX_STATUS:
+ case TEGRA210_ADX_INT_STATUS:
+ case TEGRA210_ADX_CFG_RAM_CTRL:
+ case TEGRA210_ADX_CFG_RAM_DATA:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static const struct regmap_config tegra210_adx_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = TEGRA210_ADX_CFG_RAM_DATA,
+ .writeable_reg = tegra210_adx_wr_reg,
+ .readable_reg = tegra210_adx_rd_reg,
+ .volatile_reg = tegra210_adx_volatile_reg,
+ .reg_defaults = tegra210_adx_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tegra210_adx_reg_defaults),
+ .cache_type = REGCACHE_FLAT,
+};
+
+static const struct of_device_id tegra210_adx_of_match[] = {
+ { .compatible = "nvidia,tegra210-adx" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, tegra210_adx_of_match);
+
+static int tegra210_adx_platform_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct tegra210_adx *adx;
+ void __iomem *regs;
+ int err;
+
+ adx = devm_kzalloc(dev, sizeof(*adx), GFP_KERNEL);
+ if (!adx)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, adx);
+
+ regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ adx->regmap = devm_regmap_init_mmio(dev, regs,
+ &tegra210_adx_regmap_config);
+ if (IS_ERR(adx->regmap)) {
+ dev_err(dev, "regmap init failed\n");
+ return PTR_ERR(adx->regmap);
+ }
+
+ regcache_cache_only(adx->regmap, true);
+
+ err = devm_snd_soc_register_component(dev, &tegra210_adx_cmpnt,
+ tegra210_adx_dais,
+ ARRAY_SIZE(tegra210_adx_dais));
+ if (err) {
+ dev_err(dev, "can't register ADX component, err: %d\n", err);
+ return err;
+ }
+
+ pm_runtime_enable(dev);
+
+ return 0;
+}
+
+static int tegra210_adx_platform_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops tegra210_adx_pm_ops = {
+ SET_RUNTIME_PM_OPS(tegra210_adx_runtime_suspend,
+ tegra210_adx_runtime_resume, NULL)
+ SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+};
+
+static struct platform_driver tegra210_adx_driver = {
+ .driver = {
+ .name = "tegra210-adx",
+ .of_match_table = tegra210_adx_of_match,
+ .pm = &tegra210_adx_pm_ops,
+ },
+ .probe = tegra210_adx_platform_probe,
+ .remove = tegra210_adx_platform_remove,
+};
+module_platform_driver(tegra210_adx_driver);
+
+MODULE_AUTHOR("Arun Shamanna Lakshmi <aruns@nvidia.com>");
+MODULE_DESCRIPTION("Tegra210 ADX ASoC driver");
+MODULE_LICENSE("GPL v2");
new file mode 100644
@@ -0,0 +1,72 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * tegra210_adx.h - Definitions for Tegra210 ADX driver
+ *
+ * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved.
+ *
+ */
+
+#ifndef __TEGRA210_ADX_H__
+#define __TEGRA210_ADX_H__
+
+/* Register offsets from TEGRA210_ADX*_BASE */
+#define TEGRA210_ADX_RX_STATUS 0x0c
+#define TEGRA210_ADX_RX_INT_STATUS 0x10
+#define TEGRA210_ADX_RX_INT_MASK 0x14
+#define TEGRA210_ADX_RX_INT_SET 0x18
+#define TEGRA210_ADX_RX_INT_CLEAR 0x1c
+#define TEGRA210_ADX_RX_CIF_CTRL 0x20
+#define TEGRA210_ADX_TX_STATUS 0x4c
+#define TEGRA210_ADX_TX_INT_STATUS 0x50
+#define TEGRA210_ADX_TX_INT_MASK 0x54
+#define TEGRA210_ADX_TX_INT_SET 0x58
+#define TEGRA210_ADX_TX_INT_CLEAR 0x5c
+#define TEGRA210_ADX_TX1_CIF_CTRL 0x60
+#define TEGRA210_ADX_TX2_CIF_CTRL 0x64
+#define TEGRA210_ADX_TX3_CIF_CTRL 0x68
+#define TEGRA210_ADX_TX4_CIF_CTRL 0x6c
+#define TEGRA210_ADX_ENABLE 0x80
+#define TEGRA210_ADX_SOFT_RESET 0x84
+#define TEGRA210_ADX_CG 0x88
+#define TEGRA210_ADX_STATUS 0x8c
+#define TEGRA210_ADX_INT_STATUS 0x90
+#define TEGRA210_ADX_CTRL 0xa4
+#define TEGRA210_ADX_IN_BYTE_EN0 0xa8
+#define TEGRA210_ADX_IN_BYTE_EN1 0xac
+#define TEGRA210_ADX_CFG_RAM_CTRL 0xb8
+#define TEGRA210_ADX_CFG_RAM_DATA 0xbc
+
+/* Fields in TEGRA210_ADX_ENABLE */
+#define TEGRA210_ADX_ENABLE_SHIFT 0
+
+/* Fields in TEGRA210_ADX_CFG_RAM_CTRL */
+#define TEGRA210_ADX_CFG_RAM_CTRL_RAM_ADDR_SHIFT 0
+
+#define TEGRA210_ADX_CFG_RAM_CTRL_RW_SHIFT 14
+#define TEGRA210_ADX_CFG_RAM_CTRL_RW_WRITE (1 << TEGRA210_ADX_CFG_RAM_CTRL_RW_SHIFT)
+
+#define TEGRA210_ADX_CFG_RAM_CTRL_ADDR_INIT_EN_SHIFT 13
+#define TEGRA210_ADX_CFG_RAM_CTRL_ADDR_INIT_EN (1 << TEGRA210_ADX_CFG_RAM_CTRL_ADDR_INIT_EN_SHIFT)
+
+#define TEGRA210_ADX_CFG_RAM_CTRL_SEQ_ACCESS_EN_SHIFT 12
+#define TEGRA210_ADX_CFG_RAM_CTRL_SEQ_ACCESS_EN (1 << TEGRA210_ADX_CFG_RAM_CTRL_SEQ_ACCESS_EN_SHIFT)
+
+/* Fields in TEGRA210_ADX_SOFT_RESET */
+#define TEGRA210_ADX_SOFT_RESET_SOFT_RESET_SHIFT 0
+#define TEGRA210_ADX_SOFT_RESET_SOFT_RESET_MASK (1 << TEGRA210_ADX_SOFT_RESET_SOFT_RESET_SHIFT)
+#define TEGRA210_ADX_SOFT_RESET_SOFT_EN (1 << TEGRA210_ADX_SOFT_RESET_SOFT_RESET_SHIFT)
+#define TEGRA210_ADX_SOFT_RESET_SOFT_DEFAULT (0 << TEGRA210_ADX_SOFT_RESET_SOFT_RESET_SHIFT)
+
+#define TEGRA210_ADX_AUDIOCIF_CH_STRIDE 4
+#define TEGRA210_ADX_RAM_DEPTH 16
+#define TEGRA210_ADX_MAP_STREAM_NUMBER_SHIFT 6
+#define TEGRA210_ADX_MAP_WORD_NUMBER_SHIFT 2
+#define TEGRA210_ADX_MAP_BYTE_NUMBER_SHIFT 0
+
+struct tegra210_adx {
+ struct regmap *regmap;
+ unsigned int map[TEGRA210_ADX_RAM_DEPTH];
+ unsigned int byte_mask[2];
+};
+
+#endif
The Audio Demultiplexer (ADX) block takes an input stream with up to 16 channels and demultiplexes it into four output streams of up to 16 channels each. A byte RAM helps to form output frames by any combination of bytes from the input frame. Its design is identical to that of byte RAM in the AMX except that the data flow direction is reversed. This patch registers ADX driver with ASoC framework. The component driver exposes DAPM widgets, routes and kcontrols for the device. The DAI driver exposes ADX interfaces, which can be used to connect different components in the ASoC layer. Makefile and Kconfig support is added to allow build the driver. It can be enabled in the DT via "nvidia,tegra210-adx" compatible binding. Signed-off-by: Sameer Pujar <spujar@nvidia.com> --- sound/soc/tegra/Kconfig | 11 + sound/soc/tegra/Makefile | 2 + sound/soc/tegra/tegra210_adx.c | 527 +++++++++++++++++++++++++++++++++++++++++ sound/soc/tegra/tegra210_adx.h | 72 ++++++ 4 files changed, 612 insertions(+) create mode 100644 sound/soc/tegra/tegra210_adx.c create mode 100644 sound/soc/tegra/tegra210_adx.h