diff mbox series

[09/13] ASoC: tegra: Add Tegra210 based ADX driver

Message ID 1630056839-6562-10-git-send-email-spujar@nvidia.com
State Accepted
Commit a99ab6f395a9e45ca3f9047e9b88d6e02737419f
Headers show
Series Extend AHUB audio support for Tegra210 and later | expand

Commit Message

Sameer Pujar Aug. 27, 2021, 9:33 a.m. UTC
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
diff mbox series

Patch

diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig
index 54d8342..fd4a8d6 100644
--- a/sound/soc/tegra/Kconfig
+++ b/sound/soc/tegra/Kconfig
@@ -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
diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile
index 549162b..8eb17ad 100644
--- a/sound/soc/tegra/Makefile
+++ b/sound/soc/tegra/Makefile
@@ -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
diff --git a/sound/soc/tegra/tegra210_adx.c b/sound/soc/tegra/tegra210_adx.c
new file mode 100644
index 0000000..5effafc
--- /dev/null
+++ b/sound/soc/tegra/tegra210_adx.c
@@ -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");
diff --git a/sound/soc/tegra/tegra210_adx.h b/sound/soc/tegra/tegra210_adx.h
new file mode 100644
index 0000000..d7dcb64
--- /dev/null
+++ b/sound/soc/tegra/tegra210_adx.h
@@ -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