summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSonghee Baek <sbaek@nvidia.com>2013-08-01 13:50:35 -0700
committerBharat Nihalani <bnihalani@nvidia.com>2013-10-28 02:19:24 -0700
commita8bcdcfac8ec4443159fbd8a0cc810b523303564 (patch)
tree3d138661ce5106a3fbed2e365f7f668bdc51e6fc
parentd01bea42308387395d7569a5253f8936de3dca30 (diff)
ASoC: tegra-alt: Alternative DAPM-based driver
This driver is for the ahub with using DAPM. It supports kcontrols to route within the ahub clients by user space so we can route the memory to amx, adx and other ahub clients to I2S for playback/capture. Bug 1354235 Bug 1373091 Change-Id: Ia43b007de7c1161ce551087428a090ff2bf1e09e Based-on-work-by: Stephen Warren <swarren@nvidia.com> Signed-off-by: Arun Shamanna Lakshmi <aruns@nvidia.com> Signed-off-by: Songhee Baek <sbaek@nvidia.com> Reviewed-on: http://git-master/r/289919 GVS: Gerrit_Virtual_Submit Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
-rw-r--r--sound/soc/Kconfig1
-rw-r--r--sound/soc/Makefile1
-rw-r--r--sound/soc/tegra-alt/Kconfig49
-rw-r--r--sound/soc/tegra-alt/Makefile20
-rw-r--r--sound/soc/tegra-alt/tegra114_adx_alt.c575
-rw-r--r--sound/soc/tegra-alt/tegra114_adx_alt.h112
-rw-r--r--sound/soc/tegra-alt/tegra114_amx_alt.c601
-rw-r--r--sound/soc/tegra-alt/tegra114_amx_alt.h127
-rw-r--r--sound/soc/tegra-alt/tegra30_apbif_alt.c763
-rw-r--r--sound/soc/tegra-alt/tegra30_apbif_alt.h323
-rw-r--r--sound/soc/tegra-alt/tegra30_i2s_alt.c661
-rw-r--r--sound/soc/tegra-alt/tegra30_i2s_alt.h266
-rw-r--r--sound/soc/tegra-alt/tegra30_xbar_alt.c798
-rw-r--r--sound/soc/tegra-alt/tegra30_xbar_alt.h171
-rw-r--r--sound/soc/tegra-alt/tegra_asoc_utils_alt.c441
-rw-r--r--sound/soc/tegra-alt/tegra_asoc_utils_alt.h75
-rw-r--r--sound/soc/tegra-alt/tegra_pcm_alt.c282
-rw-r--r--sound/soc/tegra-alt/tegra_pcm_alt.h52
18 files changed, 5318 insertions, 0 deletions
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 9e675c76436c..423e06d73c5f 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -52,6 +52,7 @@ source "sound/soc/samsung/Kconfig"
source "sound/soc/s6000/Kconfig"
source "sound/soc/sh/Kconfig"
source "sound/soc/tegra/Kconfig"
+source "sound/soc/tegra-alt/Kconfig"
source "sound/soc/txx9/Kconfig"
source "sound/soc/ux500/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 197b6ae54c8d..3d14cb82795d 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -30,5 +30,6 @@ obj-$(CONFIG_SND_SOC) += samsung/
obj-$(CONFIG_SND_SOC) += s6000/
obj-$(CONFIG_SND_SOC) += sh/
obj-$(CONFIG_SND_SOC) += tegra/
+obj-$(CONFIG_SND_SOC) += tegra-alt/
obj-$(CONFIG_SND_SOC) += txx9/
obj-$(CONFIG_SND_SOC) += ux500/
diff --git a/sound/soc/tegra-alt/Kconfig b/sound/soc/tegra-alt/Kconfig
new file mode 100644
index 000000000000..60ea06882af9
--- /dev/null
+++ b/sound/soc/tegra-alt/Kconfig
@@ -0,0 +1,49 @@
+config SND_SOC_TEGRA_ALT
+ tristate "Alternative DAPM-based SoC audio support for the Tegra System-on-Chip"
+ depends on ARCH_TEGRA && TEGRA20_APB_DMA
+ select REGMAP_MMIO
+ select SND_SOC_DMAENGINE_PCM if TEGRA20_APB_DMA
+ help
+ Say Y or M here if you want support for SoC audio on Tegra, using the
+ alternative driver that exposes to user-space the full routing capabilities
+ of the AHUB (Audio HUB) hardware module.
+
+config SND_SOC_TEGRA_ALT_30_OR_LATER
+ def_bool y
+ depends on SND_SOC_TEGRA_ALT
+ depends on ARCH_TEGRA_3x_SOC || ARCH_TEGRA_11x_SOC || ARCH_TEGRA_12x_SOC
+
+config SND_SOC_TEGRA_ALT_114_OR_LATER
+ def_bool y
+ depends on SND_SOC_TEGRA_ALT
+ depends on ARCH_TEGRA_11x_SOC || ARCH_TEGRA_12x_SOC
+
+config SND_SOC_TEGRA30_XBAR_ALT
+ tristate "Tegra30 XBAR driver"
+ depends on SND_SOC_TEGRA_ALT && SND_SOC_TEGRA_ALT_30_OR_LATER
+ help
+ Say Y or M if you want to add support for Tegra30 XBAR module.
+
+config SND_SOC_TEGRA30_APBIF_ALT
+ tristate "Tegra30 APBIF driver"
+ depends on SND_SOC_TEGRA30_XBAR_ALT && SND_SOC_TEGRA_ALT_30_OR_LATER
+ help
+ Say Y or M if you want to add support for Tegra30 APBIF module.
+
+config SND_SOC_TEGRA30_I2S_ALT
+ tristate "Tegra30 I2S driver"
+ depends on SND_SOC_TEGRA_ALT && SND_SOC_TEGRA_ALT_30_OR_LATER
+ help
+ Say Y or M if you want to add support for Tegra30 I2S module.
+
+config SND_SOC_TEGRA114_AMX_ALT
+ tristate "Tegra114 AMX driver"
+ depends on SND_SOC_TEGRA_ALT && SND_SOC_TEGRA_ALT_114_OR_LATER
+ help
+ Say Y or M if you want to add support for Tegra114 AMX module.
+
+config SND_SOC_TEGRA114_ADX_ALT
+ tristate "Tegra114 ADX driver"
+ depends on SND_SOC_TEGRA_ALT && SND_SOC_TEGRA_ALT_114_OR_LATER
+ help
+ Say Y or M if you want to add support for Tegra114 ADX module.
diff --git a/sound/soc/tegra-alt/Makefile b/sound/soc/tegra-alt/Makefile
new file mode 100644
index 000000000000..38c71d618d38
--- /dev/null
+++ b/sound/soc/tegra-alt/Makefile
@@ -0,0 +1,20 @@
+GCOV_PROFILE := y
+
+subdir-ccflags-y := -Werror
+
+# Tegra platform Support
+snd-soc-tegra-alt-pcm-objs := tegra_pcm_alt.o
+snd-soc-tegra-alt-utils-objs := tegra_asoc_utils_alt.o
+snd-soc-tegra30-alt-apbif-objs := tegra30_apbif_alt.o
+snd-soc-tegra30-alt-xbar-objs := tegra30_xbar_alt.o
+snd-soc-tegra30-alt-i2s-objs := tegra30_i2s_alt.o
+snd-soc-tegra114-alt-amx-objs := tegra114_amx_alt.o
+snd-soc-tegra114-alt-adx-objs := tegra114_adx_alt.o
+
+obj-$(CONFIG_SND_SOC_TEGRA_ALT) += snd-soc-tegra-alt-pcm.o
+obj-$(CONFIG_SND_SOC_TEGRA_ALT) += snd-soc-tegra-alt-utils.o
+obj-$(CONFIG_SND_SOC_TEGRA30_APBIF_ALT) += snd-soc-tegra30-alt-apbif.o
+obj-$(CONFIG_SND_SOC_TEGRA30_XBAR_ALT) += snd-soc-tegra30-alt-xbar.o
+obj-$(CONFIG_SND_SOC_TEGRA30_I2S_ALT) += snd-soc-tegra30-alt-i2s.o
+obj-$(CONFIG_SND_SOC_TEGRA114_AMX_ALT) += snd-soc-tegra114-alt-amx.o
+obj-$(CONFIG_SND_SOC_TEGRA114_ADX_ALT) += snd-soc-tegra114-alt-adx.o
diff --git a/sound/soc/tegra-alt/tegra114_adx_alt.c b/sound/soc/tegra-alt/tegra114_adx_alt.c
new file mode 100644
index 000000000000..18572b793028
--- /dev/null
+++ b/sound/soc/tegra-alt/tegra114_adx_alt.c
@@ -0,0 +1,575 @@
+/*
+ * tegra114_adx_alt.c - Tegra114 ADX driver
+ *
+ * Copyright (c) 2013 NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <linux/of_device.h>
+
+#include "tegra30_xbar_alt.h"
+#include "tegra114_adx_alt.h"
+
+#define DRV_NAME "tegra114-adx"
+
+/**
+ * tegra114_adx_enable_outstream - enable output stream
+ * @adx: struct of tegra114_adx
+ * @stream_id: adx output stream id for enabling
+ */
+static void tegra114_adx_enable_outstream(struct tegra114_adx *adx,
+ unsigned int stream_id)
+{
+ int reg;
+
+ reg = TEGRA_ADX_OUT_CH_CTRL;
+
+ regmap_update_bits(adx->regmap, reg,
+ TEGRA_ADX_OUT_CH_ENABLE << stream_id,
+ TEGRA_ADX_OUT_CH_ENABLE << stream_id);
+}
+
+/**
+ * tegra114_adx_disable_outstream - disable output stream
+ * @adx: struct of tegra114_adx
+ * @stream_id: adx output stream id for disabling
+ */
+static void tegra114_adx_disable_outstream(struct tegra114_adx *adx,
+ unsigned int stream_id)
+{
+ int reg;
+
+ reg = TEGRA_ADX_OUT_CH_CTRL;
+
+ regmap_update_bits(adx->regmap, reg,
+ TEGRA_ADX_OUT_CH_ENABLE << stream_id,
+ TEGRA_ADX_OUT_CH_DISABLE << stream_id);
+}
+
+/**
+ * tegra114_adx_set_in_byte_mask - set byte mask for input frame
+ * @adx: struct of tegra114_adx
+ * @mask1: enable for bytes 31 ~ 0 of input frame
+ * @mask2: enable for bytes 63 ~ 32 of input frame
+ */
+static void tegra114_adx_set_in_byte_mask(struct tegra114_adx *adx,
+ unsigned int mask1,
+ unsigned int mask2)
+{
+ regmap_write(adx->regmap, TEGRA_ADX_IN_BYTE_EN0, mask1);
+ regmap_write(adx->regmap, TEGRA_ADX_IN_BYTE_EN1, mask2);
+}
+
+/**
+ * tegra114_adx_set_map_table - set map table not RAM
+ * @adx: struct of tegra114_adx
+ * @out_byte_addr: byte address in one frame
+ * @stream_id: input stream id
+ * @nth_word: n-th word in the input stream
+ * @nth_byte: n-th byte in the word
+ */
+static void tegra114_adx_set_map_table(struct tegra114_adx *adx,
+ unsigned int out_byte_addr,
+ unsigned int stream_id,
+ unsigned int nth_word,
+ unsigned int nth_byte)
+{
+ unsigned char *bytes_map = (unsigned char *)&adx->map;
+
+ bytes_map[out_byte_addr] = (stream_id <<
+ TEGRA_ADX_MAP_STREAM_NUMBER_SHIFT) |
+ (nth_word <<
+ TEGRA_ADX_MAP_WORD_NUMBER_SHIFT) |
+ (nth_byte <<
+ TEGRA_ADX_MAP_BYTE_NUMBER_SHIFT);
+}
+
+/**
+ * tegra114_adx_write_map_ram - write map information in RAM
+ * @adx: struct of tegra114_adx
+ * @addr: n-th word of input stream
+ * @val : bytes mapping information of the word
+ */
+static void tegra114_adx_write_map_ram(struct tegra114_adx *adx,
+ unsigned int addr,
+ unsigned int val)
+{
+ unsigned int reg;
+
+ regmap_write(adx->regmap, TEGRA_ADX_AUDIORAMCTL_ADX_CTRL,
+ (addr <<
+ TEGRA_ADX_AUDIORAMCTL_ADX_CTRL_RAM_ADR_SHIFT));
+
+ regmap_write(adx->regmap, TEGRA_ADX_AUDIORAMCTL_ADX_DATA, val);
+
+ regmap_read(adx->regmap, TEGRA_ADX_AUDIORAMCTL_ADX_CTRL, &reg);
+ reg |= TEGRA_ADX_AUDIORAMCTL_ADX_CTRL_HW_ADR_EN_ENABLE;
+
+ regmap_write(adx->regmap, TEGRA_ADX_AUDIORAMCTL_ADX_CTRL, reg);
+
+ regmap_read(adx->regmap, TEGRA_ADX_AUDIORAMCTL_ADX_CTRL, &reg);
+ reg |= TEGRA_ADX_AUDIORAMCTL_ADX_CTRL_RW_WRITE;
+
+ regmap_write(adx->regmap, TEGRA_ADX_AUDIORAMCTL_ADX_CTRL, reg);
+}
+
+static void tegra114_adx_update_map_ram(struct tegra114_adx *adx)
+{
+ int i;
+
+ for (i = 0; i < TEGRA_ADX_RAM_DEPTH; i++)
+ tegra114_adx_write_map_ram(adx, i, adx->map[i]);
+}
+
+static int tegra114_adx_runtime_suspend(struct device *dev)
+{
+ struct tegra114_adx *adx = dev_get_drvdata(dev);
+
+ regcache_cache_only(adx->regmap, true);
+
+ clk_disable_unprepare(adx->clk_adx);
+
+ return 0;
+}
+
+static int tegra114_adx_runtime_resume(struct device *dev)
+{
+ struct tegra114_adx *adx = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(adx->clk_adx);
+ if (ret) {
+ dev_err(dev, "clk_enable failed: %d\n", ret);
+ return ret;
+ }
+
+ regcache_cache_only(adx->regmap, false);
+
+ return 0;
+}
+
+static int tegra114_adx_set_audio_cif(struct tegra114_adx *adx,
+ struct snd_pcm_hw_params *params,
+ unsigned int reg)
+{
+ int channels, audio_bits;
+ struct tegra30_xbar_cif_conf cif_conf;
+
+ channels = params_channels(params);
+ if (channels < 2)
+ return -EINVAL;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ audio_bits = TEGRA30_AUDIOCIF_BITS_16;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ audio_bits = TEGRA30_AUDIOCIF_BITS_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ cif_conf.threshold = 0;
+ cif_conf.audio_channels = channels;
+ cif_conf.client_channels = channels;
+ cif_conf.audio_bits = audio_bits;
+ cif_conf.client_bits = audio_bits;
+ cif_conf.expand = 0;
+ cif_conf.stereo_conv = 0;
+ cif_conf.replicate = 0;
+ cif_conf.direction = 0;
+ cif_conf.truncate = 0;
+ cif_conf.mono_conv = 0;
+
+ adx->soc_data->set_audio_cif(adx->regmap, reg, &cif_conf);
+
+ return 0;
+}
+
+static int tegra114_adx_out_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct tegra114_adx *adx = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ ret = tegra114_adx_set_audio_cif(adx, params,
+ TEGRA_ADX_AUDIOCIF_CH0_CTRL +
+ (dai->id * TEGRA_ADX_AUDIOCIF_CH_STRIDE));
+
+ return ret;
+}
+
+static int tegra114_adx_out_trigger(struct snd_pcm_substream *substream,
+ int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct tegra114_adx *adx = snd_soc_dai_get_drvdata(dai);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ tegra114_adx_enable_outstream(adx, dai->id);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ tegra114_adx_disable_outstream(adx, dai->id);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int tegra114_adx_in_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct tegra114_adx *adx = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ ret = tegra114_adx_set_audio_cif(adx, params,
+ TEGRA_ADX_AUDIOCIF_IN_CTRL);
+
+ return ret;
+}
+
+int tegra114_adx_set_channel_map(struct snd_soc_dai *dai,
+ unsigned int tx_num, unsigned int *tx_slot,
+ unsigned int rx_num, unsigned int *rx_slot)
+{
+ struct device *dev = dai->dev;
+ struct tegra114_adx *adx = snd_soc_dai_get_drvdata(dai);
+ unsigned int byte_mask1 = 0, byte_mask2 = 0;
+ unsigned int out_stream_idx, out_ch_idx, out_byte_idx;
+ int i;
+
+ if ((rx_num < 1) || (rx_num > 64)) {
+ dev_err(dev, "Doesn't support %d rx_num, need to be 1 to 64\n",
+ rx_num);
+ return -EINVAL;
+ }
+
+ if (!rx_slot) {
+ dev_err(dev, "rx_slot is NULL\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < rx_num; i++) {
+ if (rx_slot[i] != 0) {
+ /* getting mapping information */
+ /* n-th output stream : 0 to 3 */
+ out_stream_idx = (rx_slot[i] >> 16) & 0x3;
+ /* n-th audio channel of output stream : 1 to 16 */
+ out_ch_idx = (rx_slot[i] >> 8) & 0x1f;
+ /* n-th byte of audio channel : 0 to 3 */
+ out_byte_idx = rx_slot[i] & 0x3;
+ tegra114_adx_set_map_table(adx, i, out_stream_idx,
+ out_ch_idx - 1,
+ out_byte_idx);
+
+ /* making byte_mask */
+ if (i > 32)
+ byte_mask2 |= 1 << (32 - i);
+ else
+ byte_mask1 |= 1 << i;
+ }
+ }
+
+ tegra114_adx_update_map_ram(adx);
+
+ tegra114_adx_set_in_byte_mask(adx, byte_mask1, byte_mask2);
+
+ return 0;
+}
+
+static int tegra114_adx_codec_probe(struct snd_soc_codec *codec)
+{
+ struct tegra114_adx *adx = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ codec->control_data = adx->regmap;
+ ret = snd_soc_codec_set_cache_io(codec, 32, 32, SND_SOC_REGMAP);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_dai_ops tegra114_adx_in_dai_ops = {
+ .hw_params = tegra114_adx_in_hw_params,
+ .set_channel_map = tegra114_adx_set_channel_map,
+};
+
+static struct snd_soc_dai_ops tegra114_adx_out_dai_ops = {
+ .hw_params = tegra114_adx_out_hw_params,
+ .trigger = tegra114_adx_out_trigger,
+};
+
+#define OUT_DAI(id) \
+ { \
+ .name = "OUT" #id, \
+ .capture = { \
+ .stream_name = "OUT" #id " Transmit", \
+ .channels_min = 1, \
+ .channels_max = 16, \
+ .rates = SNDRV_PCM_RATE_8000_96000, \
+ .formats = SNDRV_PCM_FMTBIT_S16_LE, \
+ }, \
+ .ops = &tegra114_adx_out_dai_ops, \
+ }
+
+#define IN_DAI(sname, dai_ops) \
+ { \
+ .name = #sname, \
+ .playback = { \
+ .stream_name = #sname " Receive", \
+ .channels_min = 1, \
+ .channels_max = 16, \
+ .rates = SNDRV_PCM_RATE_8000_96000, \
+ .formats = SNDRV_PCM_FMTBIT_S16_LE, \
+ }, \
+ .ops = dai_ops, \
+ }
+
+static struct snd_soc_dai_driver tegra114_adx_dais[] = {
+ OUT_DAI(0),
+ OUT_DAI(1),
+ OUT_DAI(2),
+ OUT_DAI(3),
+ IN_DAI(IN, &tegra114_adx_in_dai_ops),
+};
+
+static const struct snd_soc_dapm_widget tegra114_adx_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("IN", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("OUT0", NULL, 0, TEGRA_ADX_OUT_CH_CTRL, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("OUT1", NULL, 0, TEGRA_ADX_OUT_CH_CTRL, 1, 0),
+ SND_SOC_DAPM_AIF_OUT("OUT2", NULL, 0, TEGRA_ADX_OUT_CH_CTRL, 2, 0),
+ SND_SOC_DAPM_AIF_OUT("OUT3", NULL, 0, TEGRA_ADX_OUT_CH_CTRL, 3, 0),
+};
+
+static const struct snd_soc_dapm_route tegra114_adx_routes[] = {
+ { "IN", NULL, "IN Receive" },
+ { "OUT0", NULL, "IN" },
+ { "OUT1", NULL, "IN" },
+ { "OUT2", NULL, "IN" },
+ { "OUT3", NULL, "IN" },
+ { "OUT0 Transmit", NULL, "OUT0" },
+ { "OUT1 Transmit", NULL, "OUT1" },
+ { "OUT2 Transmit", NULL, "OUT2" },
+ { "OUT3 Transmit", NULL, "OUT3" },
+};
+
+static struct snd_soc_codec_driver tegra114_adx_codec = {
+ .probe = tegra114_adx_codec_probe,
+ .dapm_widgets = tegra114_adx_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tegra114_adx_widgets),
+ .dapm_routes = tegra114_adx_routes,
+ .num_dapm_routes = ARRAY_SIZE(tegra114_adx_routes),
+};
+
+static bool tegra114_adx_wr_rd_reg(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA_ADX_CTRL:
+ case TEGRA_ADX_OUT_CH_CTRL:
+ case TEGRA_ADX_IN_BYTE_EN0:
+ case TEGRA_ADX_IN_BYTE_EN1:
+ case TEGRA_ADX_AUDIORAMCTL_ADX_CTRL:
+ case TEGRA_ADX_AUDIORAMCTL_ADX_DATA:
+ case TEGRA_ADX_AUDIOCIF_IN_CTRL:
+ case TEGRA_ADX_AUDIOCIF_CH0_CTRL:
+ case TEGRA_ADX_AUDIOCIF_CH1_CTRL:
+ case TEGRA_ADX_AUDIOCIF_CH2_CTRL:
+ case TEGRA_ADX_AUDIOCIF_CH3_CTRL:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static const struct regmap_config tegra114_adx_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = TEGRA_ADX_AUDIOCIF_CH3_CTRL,
+ .writeable_reg = tegra114_adx_wr_rd_reg,
+ .readable_reg = tegra114_adx_wr_rd_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct tegra114_adx_soc_data soc_data_tegra114 = {
+ .set_audio_cif = tegra30_xbar_set_cif
+};
+
+static const struct tegra114_adx_soc_data soc_data_tegra124 = {
+ .set_audio_cif = tegra124_xbar_set_cif
+};
+
+static const struct of_device_id tegra114_adx_of_match[] = {
+ { .compatible = "nvidia,tegra114-adx", .data = &soc_data_tegra114 },
+ { .compatible = "nvidia,tegra124-adx", .data = &soc_data_tegra124 },
+ {},
+};
+
+static int tegra114_adx_platform_probe(struct platform_device *pdev)
+{
+ struct tegra114_adx *adx;
+ struct resource *mem, *memregion;
+ void __iomem *regs;
+ int ret = 0;
+ const struct of_device_id *match;
+ struct tegra114_adx_soc_data *soc_data;
+
+ match = of_match_device(tegra114_adx_of_match, &pdev->dev);
+ if (!match) {
+ dev_err(&pdev->dev, "Error: No device match found\n");
+ ret = -ENODEV;
+ goto err;
+ }
+ soc_data = (struct tegra114_adx_soc_data *)match->data;
+
+ adx = devm_kzalloc(&pdev->dev, sizeof(struct tegra114_adx), GFP_KERNEL);
+ if (!adx) {
+ dev_err(&pdev->dev, "Can't allocate tegra114_adx\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+ dev_set_drvdata(&pdev->dev, adx);
+
+ adx->soc_data = soc_data;
+
+ adx->clk_adx = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(adx->clk_adx)) {
+ dev_err(&pdev->dev, "Can't retrieve adx clock\n");
+ ret = PTR_ERR(adx->clk_adx);
+ goto err;
+ }
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "No memory resource\n");
+ ret = -ENODEV;
+ goto err_clk_put;
+ }
+
+ memregion = devm_request_mem_region(&pdev->dev, mem->start,
+ resource_size(mem), DRV_NAME);
+ if (!memregion) {
+ dev_err(&pdev->dev, "Memory region already claimed\n");
+ ret = -EBUSY;
+ goto err_clk_put;
+ }
+
+ regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
+ if (!regs) {
+ dev_err(&pdev->dev, "ioremap failed\n");
+ ret = -ENOMEM;
+ goto err_clk_put;
+ }
+
+ adx->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
+ &tegra114_adx_regmap_config);
+ if (IS_ERR(adx->regmap)) {
+ dev_err(&pdev->dev, "regmap init failed\n");
+ ret = PTR_ERR(adx->regmap);
+ goto err_clk_put;
+ }
+ regcache_cache_only(adx->regmap, true);
+
+ pm_runtime_enable(&pdev->dev);
+ if (!pm_runtime_enabled(&pdev->dev)) {
+ ret = tegra114_adx_runtime_resume(&pdev->dev);
+ if (ret)
+ goto err_pm_disable;
+ }
+
+ ret = snd_soc_register_codec(&pdev->dev, &tegra114_adx_codec,
+ tegra114_adx_dais,
+ ARRAY_SIZE(tegra114_adx_dais));
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Could not register CODEC: %d\n", ret);
+ goto err_suspend;
+ }
+
+ return 0;
+
+err_suspend:
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ tegra114_adx_runtime_suspend(&pdev->dev);
+err_pm_disable:
+ pm_runtime_disable(&pdev->dev);
+err_clk_put:
+ devm_clk_put(&pdev->dev, adx->clk_adx);
+err:
+ return ret;
+}
+
+static int tegra114_adx_platform_remove(struct platform_device *pdev)
+{
+ struct tegra114_adx *adx = dev_get_drvdata(&pdev->dev);
+
+ snd_soc_unregister_codec(&pdev->dev);
+
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ tegra114_adx_runtime_suspend(&pdev->dev);
+
+ devm_clk_put(&pdev->dev, adx->clk_adx);
+
+ return 0;
+}
+
+static const struct dev_pm_ops tegra114_adx_pm_ops = {
+ SET_RUNTIME_PM_OPS(tegra114_adx_runtime_suspend,
+ tegra114_adx_runtime_resume, NULL)
+};
+
+static struct platform_driver tegra114_adx_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = tegra114_adx_of_match,
+ .pm = &tegra114_adx_pm_ops,
+ },
+ .probe = tegra114_adx_platform_probe,
+ .remove = tegra114_adx_platform_remove,
+};
+module_platform_driver(tegra114_adx_driver);
+
+MODULE_AUTHOR("Arun Shamanna Lakshmi <aruns@nvidia.com>");
+MODULE_DESCRIPTION("Tegra114 ADX ASoC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, tegra114_adx_of_match);
diff --git a/sound/soc/tegra-alt/tegra114_adx_alt.h b/sound/soc/tegra-alt/tegra114_adx_alt.h
new file mode 100644
index 000000000000..ea6ba0be2ce1
--- /dev/null
+++ b/sound/soc/tegra-alt/tegra114_adx_alt.h
@@ -0,0 +1,112 @@
+/*
+ * tegra114_adx_alt.h - Definitions for Tegra114 ADX driver
+ *
+ * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __TEGRA114_ADX_ALT_H__
+#define __TEGRA114_ADX_ALT_H__
+
+#define TEGRA_ADX_AUDIOCIF_CH_STRIDE 4
+
+/* Register offsets from ADX*_BASE */
+#define TEGRA_ADX_CTRL 0x00
+#define TEGRA_ADX_OUT_CH_CTRL 0x04
+#define TEGRA_ADX_IN_BYTE_EN0 0x08
+#define TEGRA_ADX_IN_BYTE_EN1 0x0c
+#define TEGRA_ADX_AUDIORAMCTL_ADX_CTRL 0x10
+#define TEGRA_ADX_AUDIORAMCTL_ADX_DATA 0x14
+#define TEGRA_ADX_AUDIOCIF_IN_CTRL 0x18
+#define TEGRA_ADX_AUDIOCIF_CH0_CTRL 0x1c
+#define TEGRA_ADX_AUDIOCIF_CH1_CTRL 0x20
+#define TEGRA_ADX_AUDIOCIF_CH2_CTRL 0x24
+#define TEGRA_ADX_AUDIOCIF_CH3_CTRL 0x28
+
+/* Fields inTEGRA_ADX_CTRL */
+#define TEGRA_ADX_CTRL_SOFT_RESET_SHIFT 31
+#define TEGRA_ADX_CTRL_CG_EN_SHIFT 30
+
+/* Fields inTEGRA_ADX_OUT_CH_CTRL */
+#define TEGRA_ADX_OUT_CH_ENABLE 1
+#define TEGRA_ADX_OUT_CH_DISABLE 0
+#define TEGRA_ADX_OUT_CH_CTRL_CH3_FORCE_DISABLE_SHIFT 11
+#define TEGRA_ADX_OUT_CH_CTRL_CH2_FORCE_DISABLE_SHIFT 10
+#define TEGRA_ADX_OUT_CH_CTRL_CH1_FORCE_DISABLE_SHIFT 9
+#define TEGRA_ADX_OUT_CH_CTRL_CH0_FORCE_DISABLE_SHIFT 8
+#define TEGRA_ADX_OUT_CH_CTRL_CH3_DISABLE_SHIFT 3
+#define TEGRA_ADX_OUT_CH_CTRL_CH2_DISABLE_SHIFT 2
+#define TEGRA_ADX_OUT_CH_CTRL_CH1_DISABLE_SHIFT 1
+#define TEGRA_ADX_OUT_CH_CTRL_CH0_DISABLE_SHIFT 0
+
+/* Fields inTEGRA_ADX_AUDIORAMCTL_ADX_CTRL */
+#define TEGRA_ADX_AUDIORAMCTL_ADX_CTRL_RAM_ADR_SHIFT 0
+#define TEGRA_ADX_AUDIORAMCTL_ADX_CTRL_HW_ADR_EN_SHIFT 12
+#define TEGRA_ADX_AUDIORAMCTL_ADX_CTRL_RESET_HW_ADR_SHIFT 13
+#define TEGRA_ADX_AUDIORAMCTL_ADX_CTRL_RW_SHIFT 14
+#define TEGRA_ADX_AUDIORAMCTL_ADX_CTRL_READ_BUSY_SHIFT 31
+
+#define TEGRA_ADX_AUDIORAMCTL_ADX_CTRL_HW_ADR_EN_ENABLE (1 << TEGRA_ADX_AUDIORAMCTL_ADX_CTRL_HW_ADR_EN_SHIFT)
+#define TEGRA_ADX_AUDIORAMCTL_ADX_CTRL_HW_ADR_EN_DISABLE 0
+#define TEGRA_ADX_AUDIORAMCTL_ADX_CTRL_RW_READ 0
+#define TEGRA_ADX_AUDIORAMCTL_ADX_CTRL_RW_WRITE (1 << TEGRA_ADX_AUDIORAMCTL_ADX_CTRL_RW_SHIFT)
+
+/*
+ * Those defines are not in register field.
+ */
+#define TEGRA_ADX_RAM_DEPTH 16
+#define TEGRA_ADX_MAP_STREAM_NUMBER_SHIFT 6
+#define TEGRA_ADX_MAP_WORD_NUMBER_SHIFT 2
+#define TEGRA_ADX_MAP_BYTE_NUMBER_SHIFT 0
+
+/* Fields in TEGRA_ADX_AUDIOCIF_IN_CTRL */
+/* Uses field from AUDIOCIF_CTRL_* in tegra_cif_utils_alt.h */
+
+/* Fields in TEGRA_ADX_AUDIOCIF_CH0_CTRL */
+/* Uses field from AUDIOCIF_CTRL_* in tegra_cif_utils_alt.h */
+
+/* Fields in TEGRA_ADX_AUDIOCIF_CH1_CTRL */
+/* Uses field from AUDIOCIF_CTRL_* in tegra_cif_utils_alt.h */
+
+/* Fields in TEGRA_ADX_AUDIOCIF_CH2_CTRL */
+/* Uses field from AUDIOCIF_CTRL_* in tegra_cif_utils_alt.h */
+
+/* Fields in TEGRA_ADX_AUDIOCIF_CH3_CTRL */
+/* Uses field from AUDIOCIF_CTRL_* in tegra_cif_utils_alt.h */
+
+enum {
+ /* Code assumes that OUT_STREAM values of ADX start at 0 */
+ /* OUT_STREAM# is equilvant to hw OUT_CH# */
+ TEGRA_ADX_OUT_STREAM0 = 0,
+ TEGRA_ADX_OUT_STREAM1,
+ TEGRA_ADX_OUT_STREAM2,
+ TEGRA_ADX_OUT_STREAM3,
+ TEGRA_ADX_IN_STREAM,
+ TEGRA_ADX_TOTAL_STREAM
+};
+
+struct tegra114_adx_soc_data {
+ void (*set_audio_cif)(struct regmap *map,
+ unsigned int reg,
+ struct tegra30_xbar_cif_conf *conf);
+};
+
+struct tegra114_adx {
+ struct clk *clk_adx;
+ struct regmap *regmap;
+ unsigned int map[16];
+ const struct tegra114_adx_soc_data *soc_data;
+};
+
+#endif
diff --git a/sound/soc/tegra-alt/tegra114_amx_alt.c b/sound/soc/tegra-alt/tegra114_amx_alt.c
new file mode 100644
index 000000000000..6c6dde7f8e01
--- /dev/null
+++ b/sound/soc/tegra-alt/tegra114_amx_alt.c
@@ -0,0 +1,601 @@
+/*
+ * tegra114_amx_alt.c - Tegra114 AMX driver
+ *
+ * Copyright (c) 2013 NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <linux/of_device.h>
+
+#include "tegra30_xbar_alt.h"
+#include "tegra114_amx_alt.h"
+
+#define DRV_NAME "tegra114_amx"
+
+/**
+ * tegra114_amx_set_master_stream - set master stream and dependency
+ * @amx: struct of tegra114_amx
+ * @stream_id: one of input stream id to be a master
+ * @dependency: master dependency for tansferring
+ * 0 - wait on all, 1 - wait on any, 2 - wait on master
+ *
+ * This dependency matter on starting point not every frame.
+ * Once amx starts to run, it is work as wait on all.
+ */
+static void tegra114_amx_set_master_stream(struct tegra114_amx *amx,
+ unsigned int stream_id,
+ unsigned int dependency)
+{
+ unsigned int val;
+
+ regmap_read(amx->regmap, TEGRA_AMX_CTRL, &val);
+
+ val &= ~(TEGRA_AMX_CTRL_MSTR_CH_NUM_MASK | TEGRA_AMX_CTRL_CH_DEP_MASK);
+ val |= ((stream_id << TEGRA_AMX_CTRL_MSTR_CH_NUM_SHIFT) |
+ (dependency << TEGRA_AMX_CTRL_CH_DEP_SHIFT));
+
+ regmap_write(amx->regmap, TEGRA_AMX_CTRL, val);
+}
+
+/**
+ * tegra114_amx_enable_instream - enable input stream
+ * @amx: struct of tegra114_amx
+ * @stream_id: amx input stream id for enabling
+ */
+static void tegra114_amx_enable_instream(struct tegra114_amx *amx,
+ unsigned int stream_id)
+{
+ int reg;
+
+ reg = TEGRA_AMX_IN_CH_CTRL;
+
+ regmap_update_bits(amx->regmap, reg,
+ TEGRA_AMX_IN_CH_ENABLE << stream_id,
+ TEGRA_AMX_IN_CH_ENABLE << stream_id);
+}
+
+/**
+ * tegra114_amx_disable_instream - disable input stream
+ * @amx: struct of tegra114_amx
+ * @stream_id: amx input stream id for disabling
+ */
+static void tegra114_amx_disable_instream(struct tegra114_amx *amx,
+ unsigned int stream_id)
+{
+ int reg;
+
+ reg = TEGRA_AMX_IN_CH_CTRL;
+
+ regmap_update_bits(amx->regmap, reg,
+ TEGRA_AMX_IN_CH_ENABLE << stream_id,
+ TEGRA_AMX_IN_CH_DISABLE << stream_id);
+}
+
+/**
+ * tegra114_amx_set_out_byte_mask - set byte mask for output frame
+ * @amx: struct of tegra114_amx
+ * @mask1: enable for bytes 31 ~ 0
+ * @mask2: enable for bytes 63 ~ 32
+ */
+static void tegra114_amx_set_out_byte_mask(struct tegra114_amx *amx,
+ unsigned int mask1,
+ unsigned int mask2)
+{
+ regmap_write(amx->regmap, TEGRA_AMX_OUT_BYTE_EN0, mask1);
+ regmap_write(amx->regmap, TEGRA_AMX_OUT_BYTE_EN1, mask2);
+}
+
+/**
+ * tegra114_amx_set_map_table - set map table not RAM
+ * @amx: struct of tegra114_amx
+ * @out_byte_addr: byte address in one frame
+ * @stream_id: input stream id
+ * @nth_word: n-th word in the input stream
+ * @nth_byte: n-th byte in the word
+ */
+static void tegra114_amx_set_map_table(struct tegra114_amx *amx,
+ unsigned int out_byte_addr,
+ unsigned int stream_id,
+ unsigned int nth_word,
+ unsigned int nth_byte)
+{
+ unsigned char *bytes_map = (unsigned char *)&amx->map;
+
+ bytes_map[out_byte_addr] =
+ (stream_id << TEGRA_AMX_MAP_STREAM_NUMBER_SHIFT) |
+ (nth_word << TEGRA_AMX_MAP_WORD_NUMBER_SHIFT) |
+ (nth_byte << TEGRA_AMX_MAP_BYTE_NUMBER_SHIFT);
+}
+
+/**
+ * tegra114_amx_write_map_ram - write map information in RAM
+ * @amx: struct of tegra114_amx
+ * @addr: n-th word of input stream
+ * @val : bytes mapping information of the word
+ */
+static void tegra114_amx_write_map_ram(struct tegra114_amx *amx,
+ unsigned int addr,
+ unsigned int val)
+{
+ unsigned int reg;
+
+ regmap_write(amx->regmap, TEGRA_AMX_AUDIORAMCTL_AMX_CTRL,
+ (addr << TEGRA_AMX_AUDIORAMCTL_AMX_CTRL_RAM_ADR_SHIFT));
+
+ regmap_write(amx->regmap, TEGRA_AMX_AUDIORAMCTL_AMX_DATA, val);
+
+ regmap_read(amx->regmap, TEGRA_AMX_AUDIORAMCTL_AMX_CTRL, &reg);
+ reg |= TEGRA_AMX_AUDIORAMCTL_AMX_CTRL_HW_ADR_EN_ENABLE;
+
+ regmap_write(amx->regmap, TEGRA_AMX_AUDIORAMCTL_AMX_CTRL, reg);
+
+ regmap_read(amx->regmap, TEGRA_AMX_AUDIORAMCTL_AMX_CTRL, &reg);
+ reg |= TEGRA_AMX_AUDIORAMCTL_AMX_CTRL_RW_WRITE;
+
+ regmap_write(amx->regmap, TEGRA_AMX_AUDIORAMCTL_AMX_CTRL, reg);
+}
+
+static void tegra114_amx_update_map_ram(struct tegra114_amx *amx)
+{
+ int i;
+
+ for (i = 0; i < TEGRA_AMX_RAM_DEPTH; i++)
+ tegra114_amx_write_map_ram(amx, i, amx->map[i]);
+}
+
+static int tegra114_amx_runtime_suspend(struct device *dev)
+{
+ struct tegra114_amx *amx = dev_get_drvdata(dev);
+
+ regcache_cache_only(amx->regmap, true);
+
+ clk_disable_unprepare(amx->clk_amx);
+
+ return 0;
+}
+
+static int tegra114_amx_runtime_resume(struct device *dev)
+{
+ struct tegra114_amx *amx = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(amx->clk_amx);
+ if (ret) {
+ dev_err(dev, "clk_enable failed: %d\n", ret);
+ return ret;
+ }
+
+ regcache_cache_only(amx->regmap, false);
+
+ return 0;
+}
+
+static int tegra114_amx_set_audio_cif(struct tegra114_amx *amx,
+ struct snd_pcm_hw_params *params,
+ unsigned int reg)
+{
+ int channels, audio_bits;
+ struct tegra30_xbar_cif_conf cif_conf;
+
+ channels = params_channels(params);
+ if (channels < 2)
+ return -EINVAL;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ audio_bits = TEGRA30_AUDIOCIF_BITS_16;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ audio_bits = TEGRA30_AUDIOCIF_BITS_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ cif_conf.threshold = 0;
+ cif_conf.audio_channels = channels;
+ cif_conf.client_channels = channels;
+ cif_conf.audio_bits = audio_bits;
+ cif_conf.client_bits = audio_bits;
+ cif_conf.expand = 0;
+ cif_conf.stereo_conv = 0;
+ cif_conf.replicate = 0;
+ cif_conf.direction = 0;
+ cif_conf.truncate = 0;
+ cif_conf.mono_conv = 0;
+
+ amx->soc_data->set_audio_cif(amx->regmap, reg, &cif_conf);
+
+ return 0;
+}
+
+
+static int tegra114_amx_in_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct tegra114_amx *amx = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ ret = tegra114_amx_set_audio_cif(amx, params,
+ TEGRA_AMX_AUDIOCIF_CH0_CTRL +
+ (dai->id * TEGRA_AMX_AUDIOCIF_CH_STRIDE));
+
+ return ret;
+}
+
+static int tegra114_amx_in_trigger(struct snd_pcm_substream *substream,
+ int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct tegra114_amx *amx = snd_soc_dai_get_drvdata(dai);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ tegra114_amx_enable_instream(amx, dai->id);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ tegra114_amx_disable_instream(amx, dai->id);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int tegra114_amx_out_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct tegra114_amx *amx = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ ret = tegra114_amx_set_audio_cif(amx, params,
+ TEGRA_AMX_AUDIOCIF_OUT_CTRL);
+
+ return ret;
+}
+
+int tegra114_amx_set_channel_map(struct snd_soc_dai *dai,
+ unsigned int tx_num, unsigned int *tx_slot,
+ unsigned int rx_num, unsigned int *rx_slot)
+{
+ struct device *dev = dai->dev;
+ struct tegra114_amx *amx = snd_soc_dai_get_drvdata(dai);
+ unsigned int byte_mask1 = 0, byte_mask2 = 0;
+ unsigned int in_stream_idx, in_ch_idx, in_byte_idx;
+ int i;
+
+ if ((tx_num < 1) || (tx_num > 64)) {
+ dev_err(dev, "Doesn't support %d tx_num, need to be 1 to 64\n",
+ tx_num);
+ return -EINVAL;
+ }
+
+ if (!tx_slot) {
+ dev_err(dev, "tx_slot is NULL\n");
+ return -EINVAL;
+ }
+
+ tegra114_amx_set_master_stream(amx, 0,
+ TEGRA_AMX_WAIT_ON_ANY);
+
+ for (i = 0; i < tx_num; i++) {
+ if (tx_slot[i] != 0) {
+ /* getting mapping information */
+ /* n-th input stream : 0 to 3 */
+ in_stream_idx = (tx_slot[i] >> 16) & 0x3;
+ /* n-th audio channel of input stream : 1 to 16 */
+ in_ch_idx = (tx_slot[i] >> 8) & 0x1f;
+ /* n-th byte of audio channel : 0 to 3 */
+ in_byte_idx = tx_slot[i] & 0x3;
+ tegra114_amx_set_map_table(amx, i, in_stream_idx,
+ in_ch_idx - 1,
+ in_byte_idx);
+
+ /* making byte_mask */
+ if (i > 32)
+ byte_mask2 |= 1 << (32 - i);
+ else
+ byte_mask1 |= 1 << i;
+ }
+ }
+
+ tegra114_amx_update_map_ram(amx);
+
+ tegra114_amx_set_out_byte_mask(amx, byte_mask1, byte_mask2);
+
+ return 0;
+}
+
+static int tegra114_amx_codec_probe(struct snd_soc_codec *codec)
+{
+ struct tegra114_amx *amx = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ codec->control_data = amx->regmap;
+ ret = snd_soc_codec_set_cache_io(codec, 32, 32, SND_SOC_REGMAP);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_dai_ops tegra114_amx_out_dai_ops = {
+ .hw_params = tegra114_amx_out_hw_params,
+ .set_channel_map = tegra114_amx_set_channel_map,
+};
+
+static struct snd_soc_dai_ops tegra114_amx_in_dai_ops = {
+ .hw_params = tegra114_amx_in_hw_params,
+ .trigger = tegra114_amx_in_trigger,
+};
+
+#define IN_DAI(id) \
+ { \
+ .name = "IN" #id, \
+ .playback = { \
+ .stream_name = "IN" #id " Receive", \
+ .channels_min = 1, \
+ .channels_max = 16, \
+ .rates = SNDRV_PCM_RATE_8000_96000, \
+ .formats = SNDRV_PCM_FMTBIT_S16_LE, \
+ }, \
+ .ops = &tegra114_amx_in_dai_ops, \
+ }
+
+#define OUT_DAI(sname, dai_ops) \
+ { \
+ .name = #sname, \
+ .capture = { \
+ .stream_name = #sname " Transmit", \
+ .channels_min = 1, \
+ .channels_max = 16, \
+ .rates = SNDRV_PCM_RATE_8000_96000, \
+ .formats = SNDRV_PCM_FMTBIT_S16_LE, \
+ }, \
+ .ops = dai_ops, \
+ }
+
+static struct snd_soc_dai_driver tegra114_amx_dais[] = {
+ IN_DAI(0),
+ IN_DAI(1),
+ IN_DAI(2),
+ IN_DAI(3),
+ OUT_DAI(OUT, &tegra114_amx_out_dai_ops),
+};
+
+static const struct snd_soc_dapm_widget tegra114_amx_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("IN0", NULL, 0, TEGRA_AMX_IN_CH_CTRL, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN1", NULL, 0, TEGRA_AMX_IN_CH_CTRL, 1, 0),
+ SND_SOC_DAPM_AIF_IN("IN2", NULL, 0, TEGRA_AMX_IN_CH_CTRL, 2, 0),
+ SND_SOC_DAPM_AIF_IN("IN3", NULL, 0, TEGRA_AMX_IN_CH_CTRL, 3, 0),
+ SND_SOC_DAPM_AIF_OUT("OUT", NULL, 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route tegra114_amx_routes[] = {
+ { "IN0", NULL, "IN0 Receive" },
+ { "IN1", NULL, "IN1 Receive" },
+ { "IN2", NULL, "IN2 Receive" },
+ { "IN3", NULL, "IN3 Receive" },
+ { "OUT", NULL, "IN0" },
+ { "OUT", NULL, "IN1" },
+ { "OUT", NULL, "IN2" },
+ { "OUT", NULL, "IN3" },
+ { "OUT Transmit", NULL, "OUT" },
+};
+
+static struct snd_soc_codec_driver tegra114_amx_codec = {
+ .probe = tegra114_amx_codec_probe,
+ .dapm_widgets = tegra114_amx_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tegra114_amx_widgets),
+ .dapm_routes = tegra114_amx_routes,
+ .num_dapm_routes = ARRAY_SIZE(tegra114_amx_routes),
+};
+
+static bool tegra114_amx_wr_rd_reg(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA_AMX_CTRL:
+ case TEGRA_AMX_IN_CH_CTRL:
+ case TEGRA_AMX_OUT_BYTE_EN0:
+ case TEGRA_AMX_OUT_BYTE_EN1:
+ case TEGRA_AMX_AUDIORAMCTL_AMX_CTRL:
+ case TEGRA_AMX_AUDIORAMCTL_AMX_DATA:
+ case TEGRA_AMX_AUDIOCIF_OUT_CTRL:
+ case TEGRA_AMX_AUDIOCIF_CH0_CTRL:
+ case TEGRA_AMX_AUDIOCIF_CH1_CTRL:
+ case TEGRA_AMX_AUDIOCIF_CH2_CTRL:
+ case TEGRA_AMX_AUDIOCIF_CH3_CTRL:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static const struct regmap_config tegra114_amx_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = TEGRA_AMX_AUDIOCIF_CH3_CTRL,
+ .writeable_reg = tegra114_amx_wr_rd_reg,
+ .readable_reg = tegra114_amx_wr_rd_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct tegra114_amx_soc_data soc_data_tegra114 = {
+ .set_audio_cif = tegra30_xbar_set_cif
+};
+
+static const struct tegra114_amx_soc_data soc_data_tegra124 = {
+ .set_audio_cif = tegra124_xbar_set_cif
+};
+
+static const struct of_device_id tegra114_amx_of_match[] = {
+ { .compatible = "nvidia,tegra114-amx", .data = &soc_data_tegra114 },
+ { .compatible = "nvidia,tegra124-amx", .data = &soc_data_tegra124 },
+ {},
+};
+
+static int tegra114_amx_platform_probe(struct platform_device *pdev)
+{
+ struct tegra114_amx *amx;
+ struct resource *mem, *memregion;
+ void __iomem *regs;
+ int ret;
+ const struct of_device_id *match;
+ struct tegra114_amx_soc_data *soc_data;
+
+ match = of_match_device(tegra114_amx_of_match, &pdev->dev);
+ if (!match) {
+ dev_err(&pdev->dev, "Error: No device match found\n");
+ ret = -ENODEV;
+ goto err;
+ }
+ soc_data = (struct tegra114_amx_soc_data *)match->data;
+
+ amx = devm_kzalloc(&pdev->dev, sizeof(struct tegra114_amx), GFP_KERNEL);
+ if (!amx) {
+ dev_err(&pdev->dev, "Can't allocate tegra114_amx\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+ dev_set_drvdata(&pdev->dev, amx);
+
+ amx->soc_data = soc_data;
+
+ amx->clk_amx = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(amx->clk_amx)) {
+ dev_err(&pdev->dev, "Can't retrieve tegra114_amx clock\n");
+ ret = PTR_ERR(amx->clk_amx);
+ goto err;
+ }
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "No memory resource\n");
+ ret = -ENODEV;
+ goto err_clk_put;
+ }
+
+ memregion = devm_request_mem_region(&pdev->dev, mem->start,
+ resource_size(mem), DRV_NAME);
+ if (!memregion) {
+ dev_err(&pdev->dev, "Memory region already claimed\n");
+ ret = -EBUSY;
+ goto err_clk_put;
+ }
+
+ regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
+ if (!regs) {
+ dev_err(&pdev->dev, "ioremap failed\n");
+ ret = -ENOMEM;
+ goto err_clk_put;
+ }
+
+ amx->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
+ &tegra114_amx_regmap_config);
+ if (IS_ERR(amx->regmap)) {
+ dev_err(&pdev->dev, "regmap init failed\n");
+ ret = PTR_ERR(amx->regmap);
+ goto err_clk_put;
+ }
+ regcache_cache_only(amx->regmap, true);
+
+ pm_runtime_enable(&pdev->dev);
+ if (!pm_runtime_enabled(&pdev->dev)) {
+ ret = tegra114_amx_runtime_resume(&pdev->dev);
+ if (ret)
+ goto err_pm_disable;
+ }
+
+ ret = snd_soc_register_codec(&pdev->dev, &tegra114_amx_codec,
+ tegra114_amx_dais,
+ ARRAY_SIZE(tegra114_amx_dais));
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Could not register CODEC: %d\n", ret);
+ goto err_suspend;
+ }
+
+ return 0;
+
+err_suspend:
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ tegra114_amx_runtime_suspend(&pdev->dev);
+err_pm_disable:
+ pm_runtime_disable(&pdev->dev);
+err_clk_put:
+ devm_clk_put(&pdev->dev, amx->clk_amx);
+err:
+ return ret;
+}
+
+static int tegra114_amx_platform_remove(struct platform_device *pdev)
+{
+ struct tegra114_amx *amx = dev_get_drvdata(&pdev->dev);
+
+ snd_soc_unregister_codec(&pdev->dev);
+
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ tegra114_amx_runtime_suspend(&pdev->dev);
+
+ devm_clk_put(&pdev->dev, amx->clk_amx);
+
+ return 0;
+}
+
+static const struct dev_pm_ops tegra114_amx_pm_ops = {
+ SET_RUNTIME_PM_OPS(tegra114_amx_runtime_suspend,
+ tegra114_amx_runtime_resume, NULL)
+};
+
+static struct platform_driver tegra114_amx_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = tegra114_amx_of_match,
+ .pm = &tegra114_amx_pm_ops,
+ },
+ .probe = tegra114_amx_platform_probe,
+ .remove = tegra114_amx_platform_remove,
+};
+module_platform_driver(tegra114_amx_driver);
+
+MODULE_AUTHOR("Songhee Baek <sbaek@nvidia.com>");
+MODULE_DESCRIPTION("Tegra114 AMX ASoC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, tegra114_amx_of_match);
diff --git a/sound/soc/tegra-alt/tegra114_amx_alt.h b/sound/soc/tegra-alt/tegra114_amx_alt.h
new file mode 100644
index 000000000000..25fa4005c1c4
--- /dev/null
+++ b/sound/soc/tegra-alt/tegra114_amx_alt.h
@@ -0,0 +1,127 @@
+/*
+ * tegra114_amx_alt.h - Definitions for Tegra114 AMX driver
+ *
+ * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHIN
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __TEGRA114_AMX_ALT_H__
+#define __TEGRA114_AMX_ALT_H__
+
+#define TEGRA_AMX_AUDIOCIF_CH_STRIDE 4
+
+/* Register offsets from TEGRA_AMX*_BASE */
+#define TEGRA_AMX_CTRL 0x00
+#define TEGRA_AMX_IN_CH_CTRL 0x04
+#define TEGRA_AMX_OUT_BYTE_EN0 0x08
+#define TEGRA_AMX_OUT_BYTE_EN1 0x0c
+#define TEGRA_AMX_AUDIORAMCTL_AMX_CTRL 0x10
+#define TEGRA_AMX_AUDIORAMCTL_AMX_DATA 0x14
+#define TEGRA_AMX_AUDIOCIF_OUT_CTRL 0x18
+#define TEGRA_AMX_AUDIOCIF_CH0_CTRL 0x1c
+#define TEGRA_AMX_AUDIOCIF_CH1_CTRL 0x20
+#define TEGRA_AMX_AUDIOCIF_CH2_CTRL 0x24
+#define TEGRA_AMX_AUDIOCIF_CH3_CTRL 0x28
+
+/* Fields in TEGRA_AMX_CTRL */
+#define TEGRA_AMX_CTRL_SOFT_RESET_SHIFT 31
+#define TEGRA_AMX_CTRL_CG_EN_SHIFT 30
+
+#define TEGRA_AMX_CTRL_MSTR_CH_NUM_SHIFT 10
+#define TEGRA_AMX_CTRL_MSTR_CH_NUM_MASK (3 << TEGRA_AMX_CTRL_MSTR_CH_NUM_SHIFT)
+
+#define TEGRA_AMX_CTRL_CH_DEP_SHIFT 8
+#define TEGRA_AMX_CTRL_CH_DEP_MASK (3 << TEGRA_AMX_CTRL_CH_DEP_SHIFT)
+#define TEGRA_AMX_CTRL_CH_DEP_WT_ON_ALL 0
+#define TEGRA_AMX_CTRL_CH_DEP_WT_ON_ANY (1 << TEGRA_AMX_CTRL_CH_DEP_SHIFT)
+#define TEGRA_AMX_CTRL_CH_DEP_WT_ON_MASTER (2 << TEGRA_AMX_CTRL_CH_DEP_SHIFT)
+#define TEGRA_AMX_CTRL_CH_DEP_RSVD (3 << TEGRA_AMX_CTRL_CH_DEP_SHIFT)
+
+/* Fields in TEGRA_AMX_IN_CH_CTRL */
+#define TEGRA_AMX_IN_CH_ENABLE 1
+#define TEGRA_AMX_IN_CH_DISABLE 0
+#define TEGRA_AMX_IN_CH_CTRL_CH3_FORCE_DISABLE_SHIFT 11
+#define TEGRA_AMX_IN_CH_CTRL_CH2_FORCE_DISABLE_SHIFT 10
+#define TEGRA_AMX_IN_CH_CTRL_CH1_FORCE_DISABLE_SHIFT 9
+#define TEGRA_AMX_IN_CH_CTRL_CH0_FORCE_DISABLE_SHIFT 8
+#define TEGRA_AMX_IN_CH_CTRL_CH3_DISABLE_SHIFT 3
+#define TEGRA_AMX_IN_CH_CTRL_CH2_DISABLE_SHIFT 2
+#define TEGRA_AMX_IN_CH_CTRL_CH1_DISABLE_SHIFT 1
+#define TEGRA_AMX_IN_CH_CTRL_CH0_DISABLE_SHIFT 0
+
+/* Fields in TEGRA_AMX_AUDIORAMCTL_AMX_CTRL */
+#define TEGRA_AMX_AUDIORAMCTL_AMX_CTRL_RAM_ADR_SHIFT 0
+#define TEGRA_AMX_AUDIORAMCTL_AMX_CTRL_HW_ADR_EN_SHIFT 12
+#define TEGRA_AMX_AUDIORAMCTL_AMX_CTRL_RESET_HW_ADR_SHIFT 13
+#define TEGRA_AMX_AUDIORAMCTL_AMX_CTRL_RW_SHIFT 14
+#define TEGRA_AMX_AUDIORAMCTL_AMX_CTRL_READ_BUSY_SHIFT 31
+
+#define TEGRA_AMX_AUDIORAMCTL_AMX_CTRL_HW_ADR_EN_ENABLE (1 << TEGRA_AMX_AUDIORAMCTL_AMX_CTRL_HW_ADR_EN_SHIFT)
+#define TEGRA_AMX_AUDIORAMCTL_AMX_CTRL_HW_ADR_EN_DISABLE 0
+#define TEGRA_AMX_AUDIORAMCTL_AMX_CTRL_RW_READ 0
+#define TEGRA_AMX_AUDIORAMCTL_AMX_CTRL_RW_WRITE (1 << TEGRA_AMX_AUDIORAMCTL_AMX_CTRL_RW_SHIFT)
+
+/*
+ * Those defines are not in register field.
+ */
+#define TEGRA_AMX_RAM_DEPTH 16
+#define TEGRA_AMX_MAP_STREAM_NUMBER_SHIFT 6
+#define TEGRA_AMX_MAP_WORD_NUMBER_SHIFT 2
+#define TEGRA_AMX_MAP_BYTE_NUMBER_SHIFT 0
+
+/* Fields in TEGRA_AMX_AUDIOCIF_IN_CTRL */
+/* Uses field from AUDIOCIF_CTRL_* in tegra_cif_utils_alt.h */
+
+/* Fields in TEGRA_AMX_AUDIOCIF_CH0_CTRL */
+/* Uses field from AUDIOCIF_CTRL_* in tegra_cif_utils_alt.h */
+
+/* Fields in TEGRA_AMX_AUDIOCIF_CH1_CTRL */
+/* Uses field from AUDIOCIF_CTRL_* in tegra_cif_utils_alt.h */
+
+/* Fields in TEGRA_AMX_AUDIOCIF_CH2_CTRL */
+/* Uses field from AUDIOCIF_CTRL_* in tegra_cif_utils_alt.h */
+
+/* Fields in TEGRA_AMX_AUDIOCIF_CH3_CTRL */
+/* Uses field from AUDIOCIF_CTRL_* in tegra_cif_utils_alt.h */
+
+enum {
+ TEGRA_AMX_WAIT_ON_ALL,
+ TEGRA_AMX_WAIT_ON_ANY,
+ TEGRA_AMX_WAIT_ON_MASTER,
+};
+
+enum {
+ /* Code assumes that IN_STREAM values of AMX start at 0 */
+ TEGRA_AMX_IN_STREAM0 = 0,
+ TEGRA_AMX_IN_STREAM1,
+ TEGRA_AMX_IN_STREAM2,
+ TEGRA_AMX_IN_STREAM3,
+ TEGRA_AMX_OUT_STREAM,
+ TEGRA_AMX_TOTAL_STREAM
+};
+
+struct tegra114_amx_soc_data {
+ void (*set_audio_cif)(struct regmap *map,
+ unsigned int reg,
+ struct tegra30_xbar_cif_conf *conf);
+};
+
+struct tegra114_amx {
+ struct clk *clk_amx;
+ struct regmap *regmap;
+ unsigned int map[16];
+ const struct tegra114_amx_soc_data *soc_data;
+};
+
+#endif
diff --git a/sound/soc/tegra-alt/tegra30_apbif_alt.c b/sound/soc/tegra-alt/tegra30_apbif_alt.c
new file mode 100644
index 000000000000..3dbd36a18eb7
--- /dev/null
+++ b/sound/soc/tegra-alt/tegra30_apbif_alt.c
@@ -0,0 +1,763 @@
+/*
+ * tegra30_apbif_alt.c - Tegra APBIF driver
+ *
+ * Copyright (c) 2011-2013 NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <mach/clk.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+
+#include "tegra30_xbar_alt.h"
+#include "tegra30_apbif_alt.h"
+#include "tegra_pcm_alt.h"
+
+#define DRV_NAME "tegra30-ahub-apbif"
+
+#define FIFOS_IN_FIRST_REG_BLOCK 4
+
+#define LAST_REG(name) \
+ (TEGRA_AHUB_##name + \
+ (TEGRA_AHUB_##name##_STRIDE * TEGRA_AHUB_##name##_COUNT) - 4)
+
+#define REG_IN_ARRAY(reg, name) \
+ ((reg >= TEGRA_AHUB_##name) && \
+ (reg <= LAST_REG(name) && \
+ (!((reg - TEGRA_AHUB_##name) % TEGRA_AHUB_##name##_STRIDE))))
+
+static bool tegra30_apbif_wr_rd_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA_AHUB_CONFIG_LINK_CTRL:
+ case TEGRA_AHUB_MISC_CTRL:
+ case TEGRA_AHUB_APBDMA_LIVE_STATUS:
+ case TEGRA_AHUB_I2S_LIVE_STATUS:
+ case TEGRA_AHUB_SPDIF_LIVE_STATUS:
+ case TEGRA_AHUB_I2S_INT_MASK:
+ case TEGRA_AHUB_DAM_INT_MASK:
+ case TEGRA_AHUB_SPDIF_INT_MASK:
+ case TEGRA_AHUB_APBIF_INT_MASK:
+ case TEGRA_AHUB_I2S_INT_STATUS:
+ case TEGRA_AHUB_DAM_INT_STATUS:
+ case TEGRA_AHUB_SPDIF_INT_STATUS:
+ case TEGRA_AHUB_APBIF_INT_STATUS:
+ case TEGRA_AHUB_I2S_INT_SOURCE:
+ case TEGRA_AHUB_DAM_INT_SOURCE:
+ case TEGRA_AHUB_SPDIF_INT_SOURCE:
+ case TEGRA_AHUB_APBIF_INT_SOURCE:
+ case TEGRA_AHUB_I2S_INT_SET:
+ case TEGRA_AHUB_DAM_INT_SET:
+ case TEGRA_AHUB_SPDIF_INT_SET:
+ case TEGRA_AHUB_APBIF_INT_SET:
+ return true;
+ default:
+ break;
+ };
+
+ if (REG_IN_ARRAY(reg, CHANNEL_CTRL) ||
+ REG_IN_ARRAY(reg, CHANNEL_CLEAR) ||
+ REG_IN_ARRAY(reg, CHANNEL_STATUS) ||
+ REG_IN_ARRAY(reg, CHANNEL_TXFIFO) ||
+ REG_IN_ARRAY(reg, CHANNEL_RXFIFO) ||
+ REG_IN_ARRAY(reg, CIF_TX_CTRL) ||
+ REG_IN_ARRAY(reg, CIF_RX_CTRL) ||
+ REG_IN_ARRAY(reg, DAM_LIVE_STATUS))
+ return true;
+
+ return false;
+}
+
+static bool tegra30_apbif_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA_AHUB_CONFIG_LINK_CTRL:
+ case TEGRA_AHUB_MISC_CTRL:
+ case TEGRA_AHUB_APBDMA_LIVE_STATUS:
+ case TEGRA_AHUB_I2S_LIVE_STATUS:
+ case TEGRA_AHUB_SPDIF_LIVE_STATUS:
+ case TEGRA_AHUB_I2S_INT_STATUS:
+ case TEGRA_AHUB_DAM_INT_STATUS:
+ case TEGRA_AHUB_SPDIF_INT_STATUS:
+ case TEGRA_AHUB_APBIF_INT_STATUS:
+ case TEGRA_AHUB_I2S_INT_SET:
+ case TEGRA_AHUB_DAM_INT_SET:
+ case TEGRA_AHUB_SPDIF_INT_SET:
+ case TEGRA_AHUB_APBIF_INT_SET:
+ return true;
+ default:
+ break;
+ };
+
+ if (REG_IN_ARRAY(reg, CHANNEL_CLEAR) ||
+ REG_IN_ARRAY(reg, CHANNEL_STATUS) ||
+ REG_IN_ARRAY(reg, CHANNEL_TXFIFO) ||
+ REG_IN_ARRAY(reg, CHANNEL_RXFIFO) ||
+ REG_IN_ARRAY(reg, DAM_LIVE_STATUS))
+ return true;
+
+ return false;
+}
+
+static bool tegra30_apbif_precious_reg(struct device *dev, unsigned int reg)
+{
+ if (REG_IN_ARRAY(reg, CHANNEL_TXFIFO) ||
+ REG_IN_ARRAY(reg, CHANNEL_RXFIFO))
+ return true;
+
+ return false;
+}
+
+static const struct regmap_config tegra30_apbif_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = TEGRA_AHUB_APBIF_INT_SET,
+ .writeable_reg = tegra30_apbif_wr_rd_reg,
+ .readable_reg = tegra30_apbif_wr_rd_reg,
+ .volatile_reg = tegra30_apbif_volatile_reg,
+ .precious_reg = tegra30_apbif_precious_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct regmap_config tegra30_apbif2_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = TEGRA_AHUB_CIF_RX9_CTRL,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int tegra30_apbif_runtime_suspend(struct device *dev)
+{
+ struct tegra30_apbif *apbif = dev_get_drvdata(dev);
+
+ regcache_cache_only(apbif->regmap[0], true);
+ if (apbif->regmap[1])
+ regcache_cache_only(apbif->regmap[1], true);
+
+ clk_disable(apbif->clk);
+
+ return 0;
+}
+
+static int tegra30_apbif_runtime_resume(struct device *dev)
+{
+ struct tegra30_apbif *apbif = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_enable(apbif->clk);
+ if (ret) {
+ dev_err(dev, "clk_enable failed: %d\n", ret);
+ return ret;
+ }
+
+ regcache_cache_only(apbif->regmap[0], false);
+ if (apbif->regmap[1])
+ regcache_cache_only(apbif->regmap[1], false);
+
+ return 0;
+}
+
+static int tegra30_apbif_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct device *dev = dai->dev;
+ struct tegra30_apbif *apbif = snd_soc_dai_get_drvdata(dai);
+ u32 reg, mask, val, base_ch;
+ struct tegra30_xbar_cif_conf cif_conf;
+ struct regmap *regmap;
+
+ cif_conf.audio_channels = params_channels(params);
+ cif_conf.client_channels = params_channels(params);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ cif_conf.audio_bits = TEGRA30_AUDIOCIF_BITS_16;
+ cif_conf.client_bits = TEGRA30_AUDIOCIF_BITS_16;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ cif_conf.audio_bits = TEGRA30_AUDIOCIF_BITS_32;
+ cif_conf.client_bits = TEGRA30_AUDIOCIF_BITS_32;
+ break;
+ default:
+ dev_err(dev, "Wrong format!\n");
+ return -EINVAL;
+ }
+
+ if (dai->id < FIFOS_IN_FIRST_REG_BLOCK) {
+ base_ch = 0;
+ regmap = apbif->regmap[0];
+ } else {
+ base_ch = FIFOS_IN_FIRST_REG_BLOCK;
+ regmap = apbif->regmap[1];
+ }
+
+ reg = TEGRA_AHUB_CHANNEL_CTRL +
+ ((dai->id - base_ch) * TEGRA_AHUB_CHANNEL_CTRL_STRIDE);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ mask = TEGRA_AHUB_CHANNEL_CTRL_TX_THRESHOLD_MASK |
+ TEGRA_AHUB_CHANNEL_CTRL_TX_PACK_EN |
+ TEGRA_AHUB_CHANNEL_CTRL_TX_PACK_MASK;
+ val = (7 << TEGRA_AHUB_CHANNEL_CTRL_TX_THRESHOLD_SHIFT) |
+ TEGRA_AHUB_CHANNEL_CTRL_TX_PACK_EN |
+ TEGRA_AHUB_CHANNEL_CTRL_TX_PACK_16;
+ regmap_update_bits(regmap, reg, mask, val);
+ } else {
+ mask = TEGRA_AHUB_CHANNEL_CTRL_RX_THRESHOLD_MASK |
+ TEGRA_AHUB_CHANNEL_CTRL_RX_PACK_EN |
+ TEGRA_AHUB_CHANNEL_CTRL_RX_PACK_MASK;
+ val = (7 << TEGRA_AHUB_CHANNEL_CTRL_RX_THRESHOLD_SHIFT) |
+ TEGRA_AHUB_CHANNEL_CTRL_RX_PACK_EN |
+ TEGRA_AHUB_CHANNEL_CTRL_RX_PACK_16;
+ regmap_update_bits(regmap, reg, mask, val);
+ }
+
+ cif_conf.threshold = 0;
+ cif_conf.expand = 0;
+ cif_conf.stereo_conv = 0;
+ cif_conf.replicate = 0;
+ cif_conf.truncate = 0;
+ cif_conf.mono_conv = 0;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ cif_conf.direction = TEGRA30_AUDIOCIF_DIRECTION_TX;
+ reg = TEGRA_AHUB_CIF_TX_CTRL +
+ ((dai->id - base_ch) * TEGRA_AHUB_CIF_TX_CTRL_STRIDE);
+
+ } else {
+ cif_conf.direction = TEGRA30_AUDIOCIF_DIRECTION_RX;
+ reg = TEGRA_AHUB_CIF_RX_CTRL +
+ ((dai->id - base_ch) * TEGRA_AHUB_CIF_RX_CTRL_STRIDE);
+ }
+ apbif->soc_data->set_audio_cif(regmap, reg, &cif_conf);
+
+ return 0;
+}
+
+static void tegra30_apbif_start_playback(struct snd_soc_dai *dai)
+{
+ struct tegra30_apbif *apbif = snd_soc_dai_get_drvdata(dai);
+ unsigned int reg, base_ch;
+ struct regmap *regmap;
+
+ if (dai->id < FIFOS_IN_FIRST_REG_BLOCK) {
+ base_ch = 0;
+ regmap = apbif->regmap[0];
+ } else {
+ base_ch = FIFOS_IN_FIRST_REG_BLOCK;
+ regmap = apbif->regmap[1];
+ }
+
+ reg = TEGRA_AHUB_CHANNEL_CTRL +
+ ((dai->id - base_ch) * TEGRA_AHUB_CHANNEL_CTRL_STRIDE);
+ regmap_update_bits(regmap, reg, TEGRA_AHUB_CHANNEL_CTRL_TX_EN,
+ TEGRA_AHUB_CHANNEL_CTRL_TX_EN);
+}
+
+static void tegra30_apbif_stop_playback(struct snd_soc_dai *dai)
+{
+ struct tegra30_apbif *apbif = snd_soc_dai_get_drvdata(dai);
+ unsigned int reg, base_ch;
+ struct regmap *regmap;
+
+ if (dai->id < FIFOS_IN_FIRST_REG_BLOCK) {
+ base_ch = 0;
+ regmap = apbif->regmap[0];
+ } else {
+ base_ch = FIFOS_IN_FIRST_REG_BLOCK;
+ regmap = apbif->regmap[1];
+ }
+
+ reg = TEGRA_AHUB_CHANNEL_CTRL +
+ ((dai->id - base_ch) * TEGRA_AHUB_CHANNEL_CTRL_STRIDE);
+ regmap_update_bits(regmap, reg, TEGRA_AHUB_CHANNEL_CTRL_TX_EN, 0);
+}
+
+static void tegra30_apbif_start_capture(struct snd_soc_dai *dai)
+{
+ struct tegra30_apbif *apbif = snd_soc_dai_get_drvdata(dai);
+ unsigned int reg, base_ch;
+ struct regmap *regmap;
+
+ if (dai->id < FIFOS_IN_FIRST_REG_BLOCK) {
+ base_ch = 0;
+ regmap = apbif->regmap[0];
+ } else {
+ base_ch = FIFOS_IN_FIRST_REG_BLOCK;
+ regmap = apbif->regmap[1];
+ }
+
+ reg = TEGRA_AHUB_CHANNEL_CTRL +
+ ((dai->id - base_ch) * TEGRA_AHUB_CHANNEL_CTRL_STRIDE);
+ regmap_update_bits(regmap, reg, TEGRA_AHUB_CHANNEL_CTRL_RX_EN,
+ TEGRA_AHUB_CHANNEL_CTRL_RX_EN);
+}
+
+static void tegra30_apbif_stop_capture(struct snd_soc_dai *dai)
+{
+ struct tegra30_apbif *apbif = snd_soc_dai_get_drvdata(dai);
+ unsigned int reg, base_ch;
+ struct regmap *regmap;
+
+ if (dai->id < FIFOS_IN_FIRST_REG_BLOCK) {
+ base_ch = 0;
+ regmap = apbif->regmap[0];
+ } else {
+ base_ch = FIFOS_IN_FIRST_REG_BLOCK;
+ regmap = apbif->regmap[1];
+ }
+
+ reg = TEGRA_AHUB_CHANNEL_CTRL +
+ ((dai->id - base_ch) * TEGRA_AHUB_CHANNEL_CTRL_STRIDE);
+ regmap_update_bits(regmap, reg, TEGRA_AHUB_CHANNEL_CTRL_RX_EN, 0);
+}
+
+static int tegra30_apbif_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ tegra30_apbif_start_playback(dai);
+ else
+ tegra30_apbif_start_capture(dai);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ tegra30_apbif_stop_playback(dai);
+ else
+ tegra30_apbif_stop_capture(dai);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_dai_ops tegra30_apbif_dai_ops = {
+ .hw_params = tegra30_apbif_hw_params,
+ .trigger = tegra30_apbif_trigger,
+};
+
+static int tegra30_apbif_dai_probe(struct snd_soc_dai *dai)
+{
+ struct tegra30_apbif *apbif = snd_soc_dai_get_drvdata(dai);
+
+ dai->capture_dma_data = &apbif->capture_dma_data[dai->id];
+ dai->playback_dma_data = &apbif->playback_dma_data[dai->id];
+
+ return 0;
+}
+
+#define APBIF_DAI(id) \
+ { \
+ .name = "APBIF" #id, \
+ .probe = tegra30_apbif_dai_probe, \
+ .playback = { \
+ .stream_name = "Playback " #id, \
+ .channels_min = 2, \
+ .channels_max = 2, \
+ .rates = SNDRV_PCM_RATE_8000_96000, \
+ .formats = SNDRV_PCM_FMTBIT_S16_LE, \
+ }, \
+ .capture = { \
+ .stream_name = "Capture " #id, \
+ .channels_min = 2, \
+ .channels_max = 2, \
+ .rates = SNDRV_PCM_RATE_8000_96000, \
+ .formats = SNDRV_PCM_FMTBIT_S16_LE, \
+ }, \
+ .ops = &tegra30_apbif_dai_ops, \
+ }
+
+static struct snd_soc_dai_driver tegra30_apbif_dais[10] = {
+ APBIF_DAI(0),
+ APBIF_DAI(1),
+ APBIF_DAI(2),
+ APBIF_DAI(3),
+ APBIF_DAI(4),
+ APBIF_DAI(5),
+ APBIF_DAI(6),
+ APBIF_DAI(7),
+ APBIF_DAI(8),
+ APBIF_DAI(9),
+};
+
+static const struct snd_soc_component_driver tegra30_apbif_dai_driver = {
+ .name = DRV_NAME,
+};
+
+#define CLK_LIST_MASK_TEGRA30 BIT(0)
+#define CLK_LIST_MASK_TEGRA114 BIT(1)
+#define CLK_LIST_MASK_TEGRA124 BIT(2)
+
+#define CLK_LIST_MASK_TEGRA30_OR_LATER \
+ (CLK_LIST_MASK_TEGRA30 | CLK_LIST_MASK_TEGRA114 |\
+ CLK_LIST_MASK_TEGRA124)
+#define CLK_LIST_MASK_TEGRA114_OR_LATER \
+ (CLK_LIST_MASK_TEGRA114 | CLK_LIST_MASK_TEGRA124)
+
+static const struct {
+ const char *clk_name;
+ unsigned int clk_list_mask;
+} configlink_clocks[] = {
+ { "i2s0", CLK_LIST_MASK_TEGRA30_OR_LATER },
+ { "i2s1", CLK_LIST_MASK_TEGRA30_OR_LATER },
+ { "i2s2", CLK_LIST_MASK_TEGRA30_OR_LATER },
+ { "i2s3", CLK_LIST_MASK_TEGRA30_OR_LATER },
+ { "i2s4", CLK_LIST_MASK_TEGRA30_OR_LATER },
+ { "dam0", CLK_LIST_MASK_TEGRA30_OR_LATER },
+ { "dam1", CLK_LIST_MASK_TEGRA30_OR_LATER },
+ { "dam2", CLK_LIST_MASK_TEGRA30_OR_LATER },
+ { "spdif_in", CLK_LIST_MASK_TEGRA30_OR_LATER },
+ { "amx", CLK_LIST_MASK_TEGRA114_OR_LATER },
+ { "adx", CLK_LIST_MASK_TEGRA114_OR_LATER },
+ { "amx1", CLK_LIST_MASK_TEGRA124 },
+ { "adx1", CLK_LIST_MASK_TEGRA124 },
+ { "afc0", CLK_LIST_MASK_TEGRA124 },
+ { "afc1", CLK_LIST_MASK_TEGRA124 },
+ { "afc2", CLK_LIST_MASK_TEGRA124 },
+ { "afc3", CLK_LIST_MASK_TEGRA124 },
+ { "afc4", CLK_LIST_MASK_TEGRA124 },
+ { "afc5", CLK_LIST_MASK_TEGRA124 },
+};
+
+
+struct of_dev_auxdata tegra30_apbif_auxdata[] = {
+ OF_DEV_AUXDATA("nvidia,tegra30-i2s", 0x70080300, "tegra30-i2s.0", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra30-i2s", 0x70080400, "tegra30-i2s.1", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra30-i2s", 0x70080500, "tegra30-i2s.2", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra30-i2s", 0x70080600, "tegra30-i2s.3", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra30-i2s", 0x70080700, "tegra30-i2s.4", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra114-amx", 0x70080c00, "tegra114-amx.0", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra114-adx", 0x70080e00, "tegra114-adx.0", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra124-i2s", 0x70301000, "tegra30-i2s.0", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra124-i2s", 0x70301100, "tegra30-i2s.1", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra124-i2s", 0x70301200, "tegra30-i2s.2", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra124-i2s", 0x70301300, "tegra30-i2s.3", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra124-i2s", 0x70301400, "tegra30-i2s.4", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra124-amx", 0x70303000, "tegra124-amx.0", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra124-amx", 0x70303100, "tegra124-amx.1", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra124-adx", 0x70303800, "tegra124-adx.0", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra124-adx", 0x70303900, "tegra124-adx.1", NULL),
+ {}
+};
+
+static struct tegra30_apbif_soc_data soc_data_tegra30 = {
+ .num_ch = FIFOS_IN_FIRST_REG_BLOCK,
+ .clk_list_mask = CLK_LIST_MASK_TEGRA30,
+ .set_audio_cif = tegra30_xbar_set_cif,
+};
+
+static struct tegra30_apbif_soc_data soc_data_tegra114 = {
+ .num_ch = FIFOS_IN_FIRST_REG_BLOCK + 6,
+ .clk_list_mask = CLK_LIST_MASK_TEGRA114,
+ .set_audio_cif = tegra30_xbar_set_cif,
+};
+
+static struct tegra30_apbif_soc_data soc_data_tegra124 = {
+ .num_ch = FIFOS_IN_FIRST_REG_BLOCK + 6,
+ .clk_list_mask = CLK_LIST_MASK_TEGRA124,
+ .set_audio_cif = tegra124_xbar_set_cif,
+};
+
+static const struct of_device_id tegra30_apbif_of_match[] = {
+ { .compatible = "nvidia,tegra30-ahub", .data = &soc_data_tegra30 },
+ { .compatible = "nvidia,tegra114-ahub", .data = &soc_data_tegra114 },
+ { .compatible = "nvidia,tegra124-ahub", .data = &soc_data_tegra124 },
+ {},
+};
+
+static struct platform_device_info tegra30_xbar_device_info = {
+ .name = "tegra30-ahub-xbar",
+ .id = -1,
+};
+
+static int tegra30_apbif_probe(struct platform_device *pdev)
+{
+ int i;
+ struct clk *clk;
+ int ret;
+ struct tegra30_apbif *apbif;
+ void __iomem *regs;
+ struct resource *res[2];
+ u32 of_dma[10][2];
+ const struct of_device_id *match;
+ struct tegra30_apbif_soc_data *soc_data;
+
+ match = of_match_device(tegra30_apbif_of_match, &pdev->dev);
+ if (!match) {
+ dev_err(&pdev->dev, "Error: No device match found\n");
+ return -ENODEV;
+ }
+ soc_data = (struct tegra30_apbif_soc_data *)match->data;
+
+ /*
+ * The TEGRA_AHUB APBIF hosts a register bus: the "configlink".
+ * For this to operate correctly, all devices on this bus must
+ * be out of reset.
+ * Ensure that here.
+ */
+ for (i = 0; i < ARRAY_SIZE(configlink_clocks); i++) {
+ if (!(configlink_clocks[i].clk_list_mask &
+ soc_data->clk_list_mask))
+ continue;
+ clk = devm_clk_get(&pdev->dev, configlink_clocks[i].clk_name);
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "Can't get clock %s\n",
+ configlink_clocks[i].clk_name);
+ ret = PTR_ERR(clk);
+ goto err;
+ }
+ tegra_periph_reset_deassert(clk);
+ devm_clk_put(&pdev->dev, clk);
+ }
+
+ apbif = devm_kzalloc(&pdev->dev, sizeof(*apbif), GFP_KERNEL);
+ if (!apbif) {
+ dev_err(&pdev->dev, "Can't allocate tegra30_apbif\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ dev_set_drvdata(&pdev->dev, apbif);
+
+ apbif->soc_data = soc_data;
+
+ apbif->capture_dma_data = devm_kzalloc(&pdev->dev,
+ sizeof(struct tegra_alt_pcm_dma_params) *
+ apbif->soc_data->num_ch,
+ GFP_KERNEL);
+ if (!apbif->capture_dma_data) {
+ dev_err(&pdev->dev, "Can't allocate tegra_alt_pcm_dma_params\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ apbif->playback_dma_data = devm_kzalloc(&pdev->dev,
+ sizeof(struct tegra_alt_pcm_dma_params) *
+ apbif->soc_data->num_ch,
+ GFP_KERNEL);
+ if (!apbif->playback_dma_data) {
+ dev_err(&pdev->dev, "Can't allocate tegra_alt_pcm_dma_params\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ apbif->clk = devm_clk_get(&pdev->dev, "apbif");
+ if (IS_ERR(apbif->clk)) {
+ dev_err(&pdev->dev, "Can't retrieve clock\n");
+ ret = PTR_ERR(apbif->clk);
+ goto err;
+ }
+
+ res[0] = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res[0]) {
+ dev_err(&pdev->dev, "No memory resource for apbif\n");
+ ret = -ENODEV;
+ goto err_clk_put;
+ }
+ res[1] = NULL;
+
+ regs = devm_request_and_ioremap(&pdev->dev, res[0]);
+ if (!regs) {
+ dev_err(&pdev->dev, "request/iomap region failed\n");
+ ret = -ENODEV;
+ goto err_clk_put;
+ }
+
+ apbif->regmap[0] = devm_regmap_init_mmio(&pdev->dev, regs,
+ &tegra30_apbif_regmap_config);
+ if (IS_ERR(apbif->regmap[0])) {
+ dev_err(&pdev->dev, "regmap init failed\n");
+ ret = PTR_ERR(apbif->regmap[0]);
+ goto err_clk_put;
+ }
+ regcache_cache_only(apbif->regmap[0], true);
+
+ if (apbif->soc_data->num_ch > FIFOS_IN_FIRST_REG_BLOCK) {
+ res[1] = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ if (!res[1]) {
+ dev_info(&pdev->dev, "No memory resource for apbif2\n");
+ ret = -ENODEV;
+ goto err_clk_put;
+ }
+
+ regs = devm_request_and_ioremap(&pdev->dev, res[1]);
+ if (!regs) {
+ dev_err(&pdev->dev, "request/iomap region failed\n");
+ ret = -ENODEV;
+ goto err_clk_put;
+ }
+
+ apbif->regmap[1] = devm_regmap_init_mmio(&pdev->dev, regs,
+ &tegra30_apbif2_regmap_config);
+ if (IS_ERR(apbif->regmap[1])) {
+ dev_err(&pdev->dev, "regmap init failed\n");
+ ret = PTR_ERR(apbif->regmap[1]);
+ goto err_clk_put;
+ }
+ regcache_cache_only(apbif->regmap[1], true);
+ }
+
+ if (of_property_read_u32_array(pdev->dev.of_node,
+ "nvidia,dma-request-selector",
+ &of_dma[0][0],
+ apbif->soc_data->num_ch * 2) < 0) {
+ dev_err(&pdev->dev,
+ "Missing property nvidia,dma-request-selector\n");
+ ret = -ENODEV;
+ goto err_clk_put;
+ }
+
+ pm_runtime_enable(&pdev->dev);
+ if (!pm_runtime_enabled(&pdev->dev)) {
+ ret = tegra30_apbif_runtime_resume(&pdev->dev);
+ if (ret)
+ goto err_pm_disable;
+ }
+
+ /* default DAI number is 4 */
+ for (i = 0; i < apbif->soc_data->num_ch; i++) {
+ if (i < FIFOS_IN_FIRST_REG_BLOCK) {
+ apbif->playback_dma_data[i].addr = res[0]->start +
+ TEGRA_AHUB_CHANNEL_TXFIFO +
+ (i * TEGRA_AHUB_CHANNEL_TXFIFO_STRIDE);
+
+ apbif->capture_dma_data[i].addr = res[0]->start +
+ TEGRA_AHUB_CHANNEL_RXFIFO +
+ (i * TEGRA_AHUB_CHANNEL_RXFIFO_STRIDE);
+ } else {
+ apbif->playback_dma_data[i].addr = res[1]->start +
+ TEGRA_AHUB_CHANNEL_TXFIFO +
+ ((i - FIFOS_IN_FIRST_REG_BLOCK) *
+ TEGRA_AHUB_CHANNEL_TXFIFO_STRIDE);
+
+ apbif->capture_dma_data[i].addr = res[1]->start +
+ TEGRA_AHUB_CHANNEL_RXFIFO +
+ ((i - FIFOS_IN_FIRST_REG_BLOCK) *
+ TEGRA_AHUB_CHANNEL_RXFIFO_STRIDE);
+ }
+
+ apbif->playback_dma_data[i].wrap = 4;
+ apbif->playback_dma_data[i].width = 32;
+ apbif->playback_dma_data[i].req_sel = of_dma[i][1];
+
+ apbif->capture_dma_data[i].wrap = 4;
+ apbif->capture_dma_data[i].width = 32;
+ apbif->capture_dma_data[i].req_sel = of_dma[i][1];
+ }
+
+
+ ret = snd_soc_register_component(&pdev->dev,
+ &tegra30_apbif_dai_driver,
+ tegra30_apbif_dais,
+ ARRAY_SIZE(tegra30_apbif_dais));
+ if (ret) {
+ dev_err(&pdev->dev, "Could not register DAIs %d: %d\n",
+ i, ret);
+ ret = -ENOMEM;
+ goto err_suspend;
+ }
+
+ ret = tegra_alt_pcm_platform_register(&pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not register PCM: %d\n", ret);
+ goto err_unregister_dais;
+ }
+
+ tegra30_xbar_device_info.res = platform_get_resource(pdev,
+ IORESOURCE_MEM, 1);
+ if (!tegra30_xbar_device_info.res) {
+ dev_err(&pdev->dev, "No memory resource for xbar\n");
+ goto err_unregister_platform;
+ }
+ tegra30_xbar_device_info.num_res = 1;
+ tegra30_xbar_device_info.parent = &pdev->dev;
+ platform_device_register_full(&tegra30_xbar_device_info);
+
+ of_platform_populate(pdev->dev.of_node, NULL, tegra30_apbif_auxdata,
+ &pdev->dev);
+
+ return 0;
+
+err_unregister_platform:
+ tegra_alt_pcm_platform_unregister(&pdev->dev);
+err_unregister_dais:
+ snd_soc_unregister_component(&pdev->dev);
+err_suspend:
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ tegra30_apbif_runtime_suspend(&pdev->dev);
+err_pm_disable:
+ pm_runtime_disable(&pdev->dev);
+err_clk_put:
+ devm_clk_put(&pdev->dev, apbif->clk);
+err:
+ return ret;
+}
+
+static int tegra30_apbif_remove(struct platform_device *pdev)
+{
+ struct tegra30_apbif *apbif = dev_get_drvdata(&pdev->dev);
+
+ snd_soc_unregister_component(&pdev->dev);
+
+ tegra_alt_pcm_platform_unregister(&pdev->dev);
+
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ tegra30_apbif_runtime_suspend(&pdev->dev);
+
+ devm_clk_put(&pdev->dev, apbif->clk);
+
+ return 0;
+}
+
+static const struct dev_pm_ops tegra30_apbif_pm_ops = {
+ SET_RUNTIME_PM_OPS(tegra30_apbif_runtime_suspend,
+ tegra30_apbif_runtime_resume, NULL)
+};
+
+static struct platform_driver tegra30_apbif_driver = {
+ .probe = tegra30_apbif_probe,
+ .remove = tegra30_apbif_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = tegra30_apbif_of_match,
+ .pm = &tegra30_apbif_pm_ops,
+ },
+};
+module_platform_driver(tegra30_apbif_driver);
+
+MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_DESCRIPTION("Tegra30 APBIF driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/tegra-alt/tegra30_apbif_alt.h b/sound/soc/tegra-alt/tegra30_apbif_alt.h
new file mode 100644
index 000000000000..13b189012e84
--- /dev/null
+++ b/sound/soc/tegra-alt/tegra30_apbif_alt.h
@@ -0,0 +1,323 @@
+/*
+ * tegra30_apbif_alt.h - Tegra30 APBIF registers
+ *
+ * Copyright (c) 2011-2013 NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __TEGRA_APBIF_ALT_H__
+#define __TEGRA_APBIF_ALT_H__
+
+#define TEGRA_APBIF_CHANNEL0_STRIDE 0x20
+#define TEGRA_APBIF_CHANNEL0_COUNT 4
+
+#define TEGRA_APBIF_AUDIOCIF_STRIDE 0x20
+#define TEGRA_APBIF_AUDIOCIF_COUNT 4
+
+/* TEGRA_AHUB_CHANNEL_CTRL */
+
+#define TEGRA_AHUB_CHANNEL_CTRL 0x0
+#define TEGRA_AHUB_CHANNEL_CTRL_STRIDE 0x20
+#define TEGRA_AHUB_CHANNEL_CTRL_COUNT 4
+#define TEGRA_AHUB_CHANNEL_CTRL_TX_EN (1 << 31)
+#define TEGRA_AHUB_CHANNEL_CTRL_RX_EN (1 << 30)
+#define TEGRA_AHUB_CHANNEL_CTRL_LOOPBACK (1 << 29)
+
+#define TEGRA_AHUB_CHANNEL_CTRL_TX_THRESHOLD_SHIFT 16
+#define TEGRA_AHUB_CHANNEL_CTRL_TX_THRESHOLD_MASK_US 0xff
+#define TEGRA_AHUB_CHANNEL_CTRL_TX_THRESHOLD_MASK (TEGRA_AHUB_CHANNEL_CTRL_TX_THRESHOLD_MASK_US << TEGRA_AHUB_CHANNEL_CTRL_TX_THRESHOLD_SHIFT)
+
+#define TEGRA_AHUB_CHANNEL_CTRL_RX_THRESHOLD_SHIFT 8
+#define TEGRA_AHUB_CHANNEL_CTRL_RX_THRESHOLD_MASK_US 0xff
+#define TEGRA_AHUB_CHANNEL_CTRL_RX_THRESHOLD_MASK (TEGRA_AHUB_CHANNEL_CTRL_RX_THRESHOLD_MASK_US << TEGRA_AHUB_CHANNEL_CTRL_RX_THRESHOLD_SHIFT)
+
+#define TEGRA_AHUB_CHANNEL_CTRL_TX_PACK_EN (1 << 6)
+
+#define PACK_8_4 2
+#define PACK_16 3
+
+#define TEGRA_AHUB_CHANNEL_CTRL_TX_PACK_SHIFT 4
+#define TEGRA_AHUB_CHANNEL_CTRL_TX_PACK_MASK_US 3
+#define TEGRA_AHUB_CHANNEL_CTRL_TX_PACK_MASK (TEGRA_AHUB_CHANNEL_CTRL_TX_PACK_MASK_US << TEGRA_AHUB_CHANNEL_CTRL_TX_PACK_SHIFT)
+#define TEGRA_AHUB_CHANNEL_CTRL_TX_PACK_8_4 (PACK_8_4 << TEGRA_AHUB_CHANNEL_CTRL_TX_PACK_SHIFT)
+#define TEGRA_AHUB_CHANNEL_CTRL_TX_PACK_16 (PACK_16 << TEGRA_AHUB_CHANNEL_CTRL_TX_PACK_SHIFT)
+
+#define TEGRA_AHUB_CHANNEL_CTRL_RX_PACK_EN (1 << 2)
+
+#define TEGRA_AHUB_CHANNEL_CTRL_RX_PACK_SHIFT 0
+#define TEGRA_AHUB_CHANNEL_CTRL_RX_PACK_MASK_US 3
+#define TEGRA_AHUB_CHANNEL_CTRL_RX_PACK_MASK (TEGRA_AHUB_CHANNEL_CTRL_RX_PACK_MASK_US << TEGRA_AHUB_CHANNEL_CTRL_RX_PACK_SHIFT)
+#define TEGRA_AHUB_CHANNEL_CTRL_RX_PACK_8_4 (PACK_8_4 << TEGRA_AHUB_CHANNEL_CTRL_RX_PACK_SHIFT)
+#define TEGRA_AHUB_CHANNEL_CTRL_RX_PACK_16 (PACK_16 << TEGRA_AHUB_CHANNEL_CTRL_RX_PACK_SHIFT)
+
+/* TEGRA_AHUB_CHANNEL_CLEAR */
+
+#define TEGRA_AHUB_CHANNEL_CLEAR 0x4
+#define TEGRA_AHUB_CHANNEL_CLEAR_STRIDE 0x20
+#define TEGRA_AHUB_CHANNEL_CLEAR_COUNT 4
+#define TEGRA_AHUB_CHANNEL_CLEAR_TX_SOFT_RESET (1 << 31)
+#define TEGRA_AHUB_CHANNEL_CLEAR_RX_SOFT_RESET (1 << 30)
+
+/* TEGRA_AHUB_CHANNEL_STATUS */
+
+#define TEGRA_AHUB_CHANNEL_STATUS 0x8
+#define TEGRA_AHUB_CHANNEL_STATUS_STRIDE 0x20
+#define TEGRA_AHUB_CHANNEL_STATUS_COUNT 4
+#define TEGRA_AHUB_CHANNEL_STATUS_TX_FREE_SHIFT 24
+#define TEGRA_AHUB_CHANNEL_STATUS_TX_FREE_MASK_US 0xff
+#define TEGRA_AHUB_CHANNEL_STATUS_TX_FREE_MASK (TEGRA_AHUB_CHANNEL_STATUS_TX_FREE_MASK_US << TEGRA_AHUB_CHANNEL_STATUS_TX_FREE_SHIFT)
+#define TEGRA_AHUB_CHANNEL_STATUS_RX_FREE_SHIFT 16
+#define TEGRA_AHUB_CHANNEL_STATUS_RX_FREE_MASK_US 0xff
+#define TEGRA_AHUB_CHANNEL_STATUS_RX_FREE_MASK (TEGRA_AHUB_CHANNEL_STATUS_RX_FREE_MASK_US << TEGRA_AHUB_CHANNEL_STATUS_RX_FREE_SHIFT)
+#define TEGRA_AHUB_CHANNEL_STATUS_TX_TRIG (1 << 1)
+#define TEGRA_AHUB_CHANNEL_STATUS_RX_TRIG (1 << 0)
+
+/* TEGRA_AHUB_CHANNEL_TXFIFO */
+
+#define TEGRA_AHUB_CHANNEL_TXFIFO 0xc
+#define TEGRA_AHUB_CHANNEL_TXFIFO_STRIDE 0x20
+#define TEGRA_AHUB_CHANNEL_TXFIFO_COUNT 4
+
+/* TEGRA_AHUB_CHANNEL_RXFIFO */
+
+#define TEGRA_AHUB_CHANNEL_RXFIFO 0x10
+#define TEGRA_AHUB_CHANNEL_RXFIFO_STRIDE 0x20
+#define TEGRA_AHUB_CHANNEL_RXFIFO_COUNT 4
+
+/* TEGRA_AHUB_CIF_TX_CTRL */
+
+#define TEGRA_AHUB_CIF_TX_CTRL 0x14
+#define TEGRA_AHUB_CIF_TX_CTRL_STRIDE 0x20
+#define TEGRA_AHUB_CIF_TX_CTRL_COUNT 4
+/* Uses field from AUDIOCIF_CTRL_* */
+
+/* TEGRA_AHUB_CIF_RX_CTRL */
+
+#define TEGRA_AHUB_CIF_RX_CTRL 0x18
+#define TEGRA_AHUB_CIF_RX_CTRL_STRIDE 0x20
+#define TEGRA_AHUB_CIF_RX_CTRL_COUNT 4
+/* Uses field from AUDIOCIF_CTRL_* */
+
+/* TEGRA_AHUB_CONFIG_LINK_CTRL */
+
+#define TEGRA_AHUB_CONFIG_LINK_CTRL 0x80
+#define TEGRA_AHUB_CONFIG_LINK_CTRL_MASTER_FIFO_FULL_CNT_SHIFT 28
+#define TEGRA_AHUB_CONFIG_LINK_CTRL_MASTER_FIFO_FULL_CNT_MASK_US 0xf
+#define TEGRA_AHUB_CONFIG_LINK_CTRL_MASTER_FIFO_FULL_CNT_MASK (TEGRA_AHUB_CONFIG_LINK_CTRL_MASTER_FIFO_FULL_CNT_MASK_US << TEGRA_AHUB_CONFIG_LINK_CTRL_MASTER_FIFO_FULL_CNT_SHIFT)
+#define TEGRA_AHUB_CONFIG_LINK_CTRL_TIMEOUT_CNT_SHIFT 16
+#define TEGRA_AHUB_CONFIG_LINK_CTRL_TIMEOUT_CNT_MASK_US 0xfff
+#define TEGRA_AHUB_CONFIG_LINK_CTRL_TIMEOUT_CNT_MASK (TEGRA_AHUB_CONFIG_LINK_CTRL_TIMEOUT_CNT_MASK_US << TEGRA_AHUB_CONFIG_LINK_CTRL_TIMEOUT_CNT_SHIFT)
+#define TEGRA_AHUB_CONFIG_LINK_CTRL_IDLE_CNT_SHIFT 4
+#define TEGRA_AHUB_CONFIG_LINK_CTRL_IDLE_CNT_MASK_US 0xfff
+#define TEGRA_AHUB_CONFIG_LINK_CTRL_IDLE_CNT_MASK (TEGRA_AHUB_CONFIG_LINK_CTRL_IDLE_CNT_MASK_US << TEGRA_AHUB_CONFIG_LINK_CTRL_IDLE_CNT_SHIFT)
+#define TEGRA_AHUB_CONFIG_LINK_CTRL_CG_EN (1 << 2)
+#define TEGRA_AHUB_CONFIG_LINK_CTRL_CLEAR_TIMEOUT_CNTR (1 << 1)
+#define TEGRA_AHUB_CONFIG_LINK_CTRL_SOFT_RESET (1 << 0)
+
+/* TEGRA_AHUB_MISC_CTRL */
+
+#define TEGRA_AHUB_MISC_CTRL 0x84
+#define TEGRA_AHUB_MISC_CTRL_AUDIO_ACTIVE (1 << 31)
+#define TEGRA_AHUB_MISC_CTRL_AUDIO_CG_EN (1 << 8)
+#define TEGRA_AHUB_MISC_CTRL_AUDIO_OBS_SEL_SHIFT 0
+#define TEGRA_AHUB_MISC_CTRL_AUDIO_OBS_SEL_MASK (0x1f << TEGRA_AHUB_MISC_CTRL_AUDIO_OBS_SEL_SHIFT)
+
+/* TEGRA_AHUB_APBDMA_LIVE_STATUS */
+
+#define TEGRA_AHUB_APBDMA_LIVE_STATUS 0x88
+#define TEGRA_AHUB_APBDMA_LIVE_STATUS_CH3_RX_CIF_FIFO_FULL (1 << 31)
+#define TEGRA_AHUB_APBDMA_LIVE_STATUS_CH3_TX_CIF_FIFO_FULL (1 << 30)
+#define TEGRA_AHUB_APBDMA_LIVE_STATUS_CH2_RX_CIF_FIFO_FULL (1 << 29)
+#define TEGRA_AHUB_APBDMA_LIVE_STATUS_CH2_TX_CIF_FIFO_FULL (1 << 28)
+#define TEGRA_AHUB_APBDMA_LIVE_STATUS_CH1_RX_CIF_FIFO_FULL (1 << 27)
+#define TEGRA_AHUB_APBDMA_LIVE_STATUS_CH1_TX_CIF_FIFO_FULL (1 << 26)
+#define TEGRA_AHUB_APBDMA_LIVE_STATUS_CH0_RX_CIF_FIFO_FULL (1 << 25)
+#define TEGRA_AHUB_APBDMA_LIVE_STATUS_CH0_TX_CIF_FIFO_FULL (1 << 24)
+#define TEGRA_AHUB_APBDMA_LIVE_STATUS_CH3_RX_CIF_FIFO_EMPTY (1 << 23)
+#define TEGRA_AHUB_APBDMA_LIVE_STATUS_CH3_TX_CIF_FIFO_EMPTY (1 << 22)
+#define TEGRA_AHUB_APBDMA_LIVE_STATUS_CH2_RX_CIF_FIFO_EMPTY (1 << 21)
+#define TEGRA_AHUB_APBDMA_LIVE_STATUS_CH2_TX_CIF_FIFO_EMPTY (1 << 20)
+#define TEGRA_AHUB_APBDMA_LIVE_STATUS_CH1_RX_CIF_FIFO_EMPTY (1 << 19)
+#define TEGRA_AHUB_APBDMA_LIVE_STATUS_CH1_TX_CIF_FIFO_EMPTY (1 << 18)
+#define TEGRA_AHUB_APBDMA_LIVE_STATUS_CH0_RX_CIF_FIFO_EMPTY (1 << 17)
+#define TEGRA_AHUB_APBDMA_LIVE_STATUS_CH0_TX_CIF_FIFO_EMPTY (1 << 16)
+#define TEGRA_AHUB_APBDMA_LIVE_STATUS_CH3_RX_DMA_FIFO_FULL (1 << 15)
+#define TEGRA_AHUB_APBDMA_LIVE_STATUS_CH3_TX_DMA_FIFO_FULL (1 << 14)
+#define TEGRA_AHUB_APBDMA_LIVE_STATUS_CH2_RX_DMA_FIFO_FULL (1 << 13)
+#define TEGRA_AHUB_APBDMA_LIVE_STATUS_CH2_TX_DMA_FIFO_FULL (1 << 12)
+#define TEGRA_AHUB_APBDMA_LIVE_STATUS_CH1_RX_DMA_FIFO_FULL (1 << 11)
+#define TEGRA_AHUB_APBDMA_LIVE_STATUS_CH1_TX_DMA_FIFO_FULL (1 << 10)
+#define TEGRA_AHUB_APBDMA_LIVE_STATUS_CH0_RX_DMA_FIFO_FULL (1 << 9)
+#define TEGRA_AHUB_APBDMA_LIVE_STATUS_CH0_TX_DMA_FIFO_FULL (1 << 8)
+#define TEGRA_AHUB_APBDMA_LIVE_STATUS_CH3_RX_DMA_FIFO_EMPTY (1 << 7)
+#define TEGRA_AHUB_APBDMA_LIVE_STATUS_CH3_TX_DMA_FIFO_EMPTY (1 << 6)
+#define TEGRA_AHUB_APBDMA_LIVE_STATUS_CH2_RX_DMA_FIFO_EMPTY (1 << 5)
+#define TEGRA_AHUB_APBDMA_LIVE_STATUS_CH2_TX_DMA_FIFO_EMPTY (1 << 4)
+#define TEGRA_AHUB_APBDMA_LIVE_STATUS_CH1_RX_DMA_FIFO_EMPTY (1 << 3)
+#define TEGRA_AHUB_APBDMA_LIVE_STATUS_CH1_TX_DMA_FIFO_EMPTY (1 << 2)
+#define TEGRA_AHUB_APBDMA_LIVE_STATUS_CH0_RX_DMA_FIFO_EMPTY (1 << 1)
+#define TEGRA_AHUB_APBDMA_LIVE_STATUS_CH0_TX_DMA_FIFO_EMPTY (1 << 0)
+
+/* TEGRA_AHUB_I2S_LIVE_STATUS */
+
+#define TEGRA_AHUB_I2S_LIVE_STATUS 0x8c
+#define TEGRA_AHUB_I2S_LIVE_STATUS_I2S4_RX_FIFO_FULL (1 << 29)
+#define TEGRA_AHUB_I2S_LIVE_STATUS_I2S4_TX_FIFO_FULL (1 << 28)
+#define TEGRA_AHUB_I2S_LIVE_STATUS_I2S3_RX_FIFO_FULL (1 << 27)
+#define TEGRA_AHUB_I2S_LIVE_STATUS_I2S3_TX_FIFO_FULL (1 << 26)
+#define TEGRA_AHUB_I2S_LIVE_STATUS_I2S2_RX_FIFO_FULL (1 << 25)
+#define TEGRA_AHUB_I2S_LIVE_STATUS_I2S2_TX_FIFO_FULL (1 << 24)
+#define TEGRA_AHUB_I2S_LIVE_STATUS_I2S1_RX_FIFO_FULL (1 << 23)
+#define TEGRA_AHUB_I2S_LIVE_STATUS_I2S1_TX_FIFO_FULL (1 << 22)
+#define TEGRA_AHUB_I2S_LIVE_STATUS_I2S0_RX_FIFO_FULL (1 << 21)
+#define TEGRA_AHUB_I2S_LIVE_STATUS_I2S0_TX_FIFO_FULL (1 << 20)
+#define TEGRA_AHUB_I2S_LIVE_STATUS_I2S4_RX_FIFO_ENABLED (1 << 19)
+#define TEGRA_AHUB_I2S_LIVE_STATUS_I2S4_TX_FIFO_ENABLED (1 << 18)
+#define TEGRA_AHUB_I2S_LIVE_STATUS_I2S3_RX_FIFO_ENABLED (1 << 17)
+#define TEGRA_AHUB_I2S_LIVE_STATUS_I2S3_TX_FIFO_ENABLED (1 << 16)
+#define TEGRA_AHUB_I2S_LIVE_STATUS_I2S2_RX_FIFO_ENABLED (1 << 15)
+#define TEGRA_AHUB_I2S_LIVE_STATUS_I2S2_TX_FIFO_ENABLED (1 << 14)
+#define TEGRA_AHUB_I2S_LIVE_STATUS_I2S1_RX_FIFO_ENABLED (1 << 13)
+#define TEGRA_AHUB_I2S_LIVE_STATUS_I2S1_TX_FIFO_ENABLED (1 << 12)
+#define TEGRA_AHUB_I2S_LIVE_STATUS_I2S0_RX_FIFO_ENABLED (1 << 11)
+#define TEGRA_AHUB_I2S_LIVE_STATUS_I2S0_TX_FIFO_ENABLED (1 << 10)
+#define TEGRA_AHUB_I2S_LIVE_STATUS_I2S4_RX_FIFO_EMPTY (1 << 9)
+#define TEGRA_AHUB_I2S_LIVE_STATUS_I2S4_TX_FIFO_EMPTY (1 << 8)
+#define TEGRA_AHUB_I2S_LIVE_STATUS_I2S3_RX_FIFO_EMPTY (1 << 7)
+#define TEGRA_AHUB_I2S_LIVE_STATUS_I2S3_TX_FIFO_EMPTY (1 << 6)
+#define TEGRA_AHUB_I2S_LIVE_STATUS_I2S2_RX_FIFO_EMPTY (1 << 5)
+#define TEGRA_AHUB_I2S_LIVE_STATUS_I2S2_TX_FIFO_EMPTY (1 << 4)
+#define TEGRA_AHUB_I2S_LIVE_STATUS_I2S1_RX_FIFO_EMPTY (1 << 3)
+#define TEGRA_AHUB_I2S_LIVE_STATUS_I2S1_TX_FIFO_EMPTY (1 << 2)
+#define TEGRA_AHUB_I2S_LIVE_STATUS_I2S0_RX_FIFO_EMPTY (1 << 1)
+#define TEGRA_AHUB_I2S_LIVE_STATUS_I2S0_TX_FIFO_EMPTY (1 << 0)
+
+/* TEGRA_AHUB_DAM0_LIVE_STATUS */
+
+#define TEGRA_AHUB_DAM_LIVE_STATUS 0x90
+#define TEGRA_AHUB_DAM_LIVE_STATUS_STRIDE 0x8
+#define TEGRA_AHUB_DAM_LIVE_STATUS_COUNT 3
+#define TEGRA_AHUB_DAM_LIVE_STATUS_TX_ENABLED (1 << 26)
+#define TEGRA_AHUB_DAM_LIVE_STATUS_RX1_ENABLED (1 << 25)
+#define TEGRA_AHUB_DAM_LIVE_STATUS_RX0_ENABLED (1 << 24)
+#define TEGRA_AHUB_DAM_LIVE_STATUS_TXFIFO_FULL (1 << 15)
+#define TEGRA_AHUB_DAM_LIVE_STATUS_RX1FIFO_FULL (1 << 9)
+#define TEGRA_AHUB_DAM_LIVE_STATUS_RX0FIFO_FULL (1 << 8)
+#define TEGRA_AHUB_DAM_LIVE_STATUS_TXFIFO_EMPTY (1 << 7)
+#define TEGRA_AHUB_DAM_LIVE_STATUS_RX1FIFO_EMPTY (1 << 1)
+#define TEGRA_AHUB_DAM_LIVE_STATUS_RX0FIFO_EMPTY (1 << 0)
+
+/* TEGRA_AHUB_SPDIF_LIVE_STATUS */
+
+#define TEGRA_AHUB_SPDIF_LIVE_STATUS 0xa8
+#define TEGRA_AHUB_SPDIF_LIVE_STATUS_USER_TX_ENABLED (1 << 11)
+#define TEGRA_AHUB_SPDIF_LIVE_STATUS_USER_RX_ENABLED (1 << 10)
+#define TEGRA_AHUB_SPDIF_LIVE_STATUS_DATA_TX_ENABLED (1 << 9)
+#define TEGRA_AHUB_SPDIF_LIVE_STATUS_DATA_RX_ENABLED (1 << 8)
+#define TEGRA_AHUB_SPDIF_LIVE_STATUS_USER_TXFIFO_FULL (1 << 7)
+#define TEGRA_AHUB_SPDIF_LIVE_STATUS_USER_RXFIFO_FULL (1 << 6)
+#define TEGRA_AHUB_SPDIF_LIVE_STATUS_DATA_TXFIFO_FULL (1 << 5)
+#define TEGRA_AHUB_SPDIF_LIVE_STATUS_DATA_RXFIFO_FULL (1 << 4)
+#define TEGRA_AHUB_SPDIF_LIVE_STATUS_USER_TXFIFO_EMPTY (1 << 3)
+#define TEGRA_AHUB_SPDIF_LIVE_STATUS_USER_RXFIFO_EMPTY (1 << 2)
+#define TEGRA_AHUB_SPDIF_LIVE_STATUS_DATA_TXFIFO_EMPTY (1 << 1)
+#define TEGRA_AHUB_SPDIF_LIVE_STATUS_DATA_RXFIFO_EMPTY (1 << 0)
+
+/* TEGRA_AHUB_I2S_INT_MASK */
+
+#define TEGRA_AHUB_I2S_INT_MASK 0xb0
+
+/* TEGRA_AHUB_DAM_INT_MASK */
+
+#define TEGRA_AHUB_DAM_INT_MASK 0xb4
+
+/* TEGRA_AHUB_SPDIF_INT_MASK */
+
+#define TEGRA_AHUB_SPDIF_INT_MASK 0xbc
+
+/* TEGRA_AHUB_APBIF_INT_MASK */
+
+#define TEGRA_AHUB_APBIF_INT_MASK 0xc0
+
+/* TEGRA_AHUB_I2S_INT_STATUS */
+
+#define TEGRA_AHUB_I2S_INT_STATUS 0xc8
+
+/* TEGRA_AHUB_DAM_INT_STATUS */
+
+#define TEGRA_AHUB_DAM_INT_STATUS 0xcc
+
+/* TEGRA_AHUB_SPDIF_INT_STATUS */
+
+#define TEGRA_AHUB_SPDIF_INT_STATUS 0xd4
+
+/* TEGRA_AHUB_APBIF_INT_STATUS */
+
+#define TEGRA_AHUB_APBIF_INT_STATUS 0xd8
+
+/* TEGRA_AHUB_I2S_INT_SOURCE */
+
+#define TEGRA_AHUB_I2S_INT_SOURCE 0xe0
+
+/* TEGRA_AHUB_DAM_INT_SOURCE */
+
+#define TEGRA_AHUB_DAM_INT_SOURCE 0xe4
+
+/* TEGRA_AHUB_SPDIF_INT_SOURCE */
+
+#define TEGRA_AHUB_SPDIF_INT_SOURCE 0xec
+
+/* TEGRA_AHUB_APBIF_INT_SOURCE */
+
+#define TEGRA_AHUB_APBIF_INT_SOURCE 0xf0
+
+/* TEGRA_AHUB_I2S_INT_SET */
+
+#define TEGRA_AHUB_I2S_INT_SET 0xf8
+
+/* TEGRA_AHUB_DAM_INT_SET */
+
+#define TEGRA_AHUB_DAM_INT_SET 0xfc
+
+/* TEGRA_AHUB_SPDIF_INT_SET */
+
+#define TEGRA_AHUB_SPDIF_INT_SET 0x100
+
+/* TEGRA_AHUB_APBIF_INT_SET */
+
+#define TEGRA_AHUB_APBIF_INT_SET 0x104
+
+/* TEGRA_AHUB_CIF_RX9_CTRL */
+#define TEGRA_AHUB_CIF_RX9_CTRL 0xb8
+
+struct tegra30_apbif_soc_data {
+ unsigned int num_ch;
+ unsigned int clk_list_mask;
+ void (*set_audio_cif)(struct regmap *map,
+ unsigned int reg,
+ struct tegra30_xbar_cif_conf *cif_conf);
+};
+
+struct tegra30_apbif {
+ struct clk *clk;
+ /* regmap for APBIF */
+ struct regmap *regmap[2];
+ /* regmap for APBIF2 */
+ struct regmap *regmap1;
+ struct tegra_alt_pcm_dma_params *capture_dma_data;
+ struct tegra_alt_pcm_dma_params *playback_dma_data;
+ const struct tegra30_apbif_soc_data *soc_data;
+};
+
+#endif
diff --git a/sound/soc/tegra-alt/tegra30_i2s_alt.c b/sound/soc/tegra-alt/tegra30_i2s_alt.c
new file mode 100644
index 000000000000..6894f5cec0d4
--- /dev/null
+++ b/sound/soc/tegra-alt/tegra30_i2s_alt.c
@@ -0,0 +1,661 @@
+/*
+ * tegra30_i2s_alt.c - Tegra30 I2S driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (c) 2010-2013 NVIDIA CORPORATION. All rights reserved.
+ *
+ * Based on code copyright/by:
+ *
+ * Copyright (c) 2009-2010, NVIDIA Corporation.
+ * Scott Peterson <speterson@nvidia.com>
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Iliyan Malchev <malchev@google.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/of_device.h>
+
+#include "tegra30_xbar_alt.h"
+#include "tegra30_i2s_alt.h"
+
+#define DRV_NAME "tegra30-i2s"
+
+static void tegra30_i2s_set_slot_ctrl(struct regmap *regmap,
+ unsigned int total_slots,
+ unsigned int tx_slot_mask,
+ unsigned int rx_slot_mask)
+{
+ regmap_write(regmap, TEGRA30_I2S_SLOT_CTRL,
+ ((total_slots - 1) << TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_SHIFT) |
+ (rx_slot_mask << TEGRA30_I2S_SLOT_CTRL_RX_SLOT_ENABLES_SHIFT) |
+ (tx_slot_mask << TEGRA30_I2S_SLOT_CTRL_TX_SLOT_ENABLES_SHIFT));
+}
+
+static void tegra114_i2s_set_slot_ctrl(struct regmap *regmap,
+ unsigned int total_slots,
+ unsigned int tx_slot_mask,
+ unsigned int rx_slot_mask)
+{
+ regmap_write(regmap, TEGRA30_I2S_SLOT_CTRL, total_slots - 1);
+ regmap_write(regmap, TEGRA114_I2S_SLOT_CTRL2,
+ (rx_slot_mask <<
+ TEGRA114_I2S_SLOT_CTRL2_RX_SLOT_ENABLES_SHIFT) |
+ tx_slot_mask);
+}
+
+static int tegra30_i2s_set_clock_rate(struct device *dev, int clock_rate)
+{
+ unsigned int val;
+ struct tegra30_i2s *i2s = dev_get_drvdata(dev);
+ int ret;
+
+ regmap_read(i2s->regmap, TEGRA30_I2S_CTRL, &val);
+
+ if ((val & TEGRA30_I2S_CTRL_MASTER_MASK) ==
+ TEGRA30_I2S_CTRL_MASTER_ENABLE) {
+ ret = clk_set_parent(i2s->clk_i2s, i2s->clk_pll_a_out0);
+ if (ret) {
+ dev_err(dev, "Can't set parent of I2S clock\n");
+ return ret;
+ }
+
+ ret = clk_set_rate(i2s->clk_i2s, clock_rate);
+ if (ret) {
+ dev_err(dev, "Can't set I2S clock rate: %d\n", ret);
+ return ret;
+ }
+ } else {
+ ret = clk_set_rate(i2s->clk_i2s_sync, clock_rate);
+ if (ret) {
+ dev_err(dev, "Can't set I2S sync clock rate\n");
+ return ret;
+ }
+
+ ret = clk_set_parent(clk_get_parent(i2s->clk_audio_2x),
+ i2s->clk_i2s_sync);
+ if (ret) {
+ dev_err(dev, "Can't set parent of audio2x clock\n");
+ return ret;
+ }
+
+ ret = clk_set_rate(i2s->clk_audio_2x, clock_rate);
+ if (ret) {
+ dev_err(dev, "Can't set audio2x clock rate\n");
+ return ret;
+ }
+
+ ret = clk_set_parent(i2s->clk_i2s, i2s->clk_audio_2x);
+ if (ret) {
+ dev_err(dev, "Can't set parent of i2s clock\n");
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int tegra30_i2s_runtime_suspend(struct device *dev)
+{
+ struct tegra30_i2s *i2s = dev_get_drvdata(dev);
+
+ regcache_cache_only(i2s->regmap, true);
+
+ clk_disable_unprepare(i2s->clk_i2s);
+
+ return 0;
+}
+
+static int tegra30_i2s_runtime_resume(struct device *dev)
+{
+ struct tegra30_i2s *i2s = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(i2s->clk_i2s);
+ if (ret) {
+ dev_err(dev, "clk_enable failed: %d\n", ret);
+ return ret;
+ }
+
+ regcache_cache_only(i2s->regmap, false);
+
+ return 0;
+}
+
+static int tegra30_i2s_set_fmt(struct snd_soc_dai *dai,
+ unsigned int fmt)
+{
+ struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+ unsigned int mask, val;
+
+ mask = TEGRA30_I2S_CH_CTRL_EGDE_CTRL_MASK;
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ val = TEGRA30_I2S_CH_CTRL_EGDE_CTRL_POS_EDGE;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ val = TEGRA30_I2S_CH_CTRL_EGDE_CTRL_NEG_EDGE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ pm_runtime_get_sync(dai->dev);
+ regmap_update_bits(i2s->regmap, TEGRA30_I2S_CH_CTRL, mask, val);
+ pm_runtime_put(dai->dev);
+
+ mask = TEGRA30_I2S_CTRL_MASTER_MASK;
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ val = TEGRA30_I2S_CTRL_SLAVE_ENABLE;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ val = TEGRA30_I2S_CTRL_MASTER_ENABLE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ mask |= TEGRA30_I2S_CTRL_FRAME_FORMAT_MASK |
+ TEGRA30_I2S_CTRL_LRCK_MASK;
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ val |= TEGRA30_I2S_CTRL_FRAME_FORMAT_FSYNC;
+ val |= TEGRA30_I2S_LRCK_LEFT_LOW;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ val |= TEGRA30_I2S_CTRL_FRAME_FORMAT_FSYNC;
+ val |= TEGRA30_I2S_LRCK_RIGHT_LOW;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ val |= TEGRA30_I2S_FRAME_FORMAT_LRCK;
+ val |= TEGRA30_I2S_LRCK_LEFT_LOW;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ val |= TEGRA30_I2S_FRAME_FORMAT_LRCK;
+ val |= TEGRA30_I2S_LRCK_LEFT_LOW;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ val |= TEGRA30_I2S_FRAME_FORMAT_LRCK;
+ val |= TEGRA30_I2S_LRCK_LEFT_LOW;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ pm_runtime_get_sync(dai->dev);
+ regmap_update_bits(i2s->regmap, TEGRA30_I2S_CTRL, mask, val);
+ pm_runtime_put(dai->dev);
+
+ return 0;
+}
+
+static int tegra30_i2s_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+ i2s->srate = freq;
+
+ return 0;
+}
+
+static int tegra30_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct device *dev = dai->dev;
+ struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+ unsigned int mask, val, reg;
+ int ret, sample_size, channels, srate, i2sclock, bitcnt;
+ struct tegra30_xbar_cif_conf cif_conf;
+
+ channels = params_channels(params);
+ if (channels < 1) {
+ dev_err(dev, "Doesn't support %d channels\n", channels);
+ return -EINVAL;
+ }
+
+ mask = TEGRA30_I2S_CTRL_BIT_SIZE_MASK;
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ val = TEGRA30_I2S_CTRL_BIT_SIZE_16;
+ sample_size = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ val = TEGRA30_I2S_CTRL_BIT_SIZE_32;
+ sample_size = 32;
+ break;
+ default:
+ dev_err(dev, "Wrong format!\n");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(i2s->regmap, TEGRA30_I2S_CTRL, mask, val);
+
+ srate = i2s->srate;
+
+ regmap_read(i2s->regmap, TEGRA30_I2S_CTRL, &val);
+ if ((val & TEGRA30_I2S_CTRL_FRAME_FORMAT_MASK) ==
+ TEGRA30_I2S_CTRL_FRAME_FORMAT_FSYNC) {
+ i2sclock = srate * channels * sample_size;
+ i2s->soc_data->set_slot_ctrl(i2s->regmap, channels,
+ (1 << channels) - 1,
+ (1 << channels) - 1);
+ } else
+ i2sclock = srate * channels * sample_size * 2;
+
+ bitcnt = (i2sclock / srate) - 1;
+ if ((bitcnt < 0) ||
+ (bitcnt > TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US)) {
+ dev_err(dev, "Can't set channel bit count\n");
+ return -EINVAL;
+ }
+
+ ret = tegra30_i2s_set_clock_rate(dev, i2sclock);
+ if (ret) {
+ dev_err(dev, "Can't set I2S clock rate: %d\n", ret);
+ return ret;
+ }
+
+ if (channels != 2)
+ val = bitcnt << TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT;
+ else
+ val = (bitcnt >> 1) <<
+ TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT;
+
+ if (i2sclock % (2 * srate))
+ val |= TEGRA30_I2S_TIMING_NON_SYM_ENABLE;
+
+ regmap_write(i2s->regmap, TEGRA30_I2S_TIMING, val);
+
+ cif_conf.threshold = 0;
+ cif_conf.audio_channels = channels;
+ cif_conf.client_channels = channels;
+ cif_conf.audio_bits = (sample_size == 16 ? TEGRA30_AUDIOCIF_BITS_16 :
+ TEGRA30_AUDIOCIF_BITS_32);
+
+ cif_conf.client_bits = (sample_size == 16 ? TEGRA30_AUDIOCIF_BITS_16 :
+ TEGRA30_AUDIOCIF_BITS_32);
+ cif_conf.expand = 0;
+ cif_conf.stereo_conv = 0;
+ cif_conf.replicate = 0;
+ cif_conf.truncate = 0;
+ cif_conf.mono_conv = 0;
+
+ /* As a COCEC DAI, CAPTURE is transmit */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ cif_conf.direction = TEGRA30_AUDIOCIF_DIRECTION_RX;
+ reg = TEGRA30_I2S_AUDIOCIF_I2STX_CTRL;
+ } else {
+ cif_conf.direction = TEGRA30_AUDIOCIF_DIRECTION_TX;
+ reg = TEGRA30_I2S_AUDIOCIF_I2SRX_CTRL;
+ }
+ i2s->soc_data->set_audio_cif(i2s->regmap, reg, &cif_conf);
+
+ val = (1 << TEGRA30_I2S_OFFSET_RX_DATA_OFFSET_SHIFT) |
+ (1 << TEGRA30_I2S_OFFSET_TX_DATA_OFFSET_SHIFT);
+ regmap_write(i2s->regmap, TEGRA30_I2S_OFFSET, val);
+
+ regmap_update_bits(i2s->regmap, TEGRA30_I2S_CH_CTRL,
+ TEGRA30_I2S_CH_CTRL_FSYNC_WIDTH_MASK,
+ 31 << TEGRA30_I2S_CH_CTRL_FSYNC_WIDTH_SHIFT);
+
+ return 0;
+}
+
+static int tegra30_i2s_codec_probe(struct snd_soc_codec *codec)
+{
+ struct tegra30_i2s *i2s = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ codec->control_data = i2s->regmap;
+ ret = snd_soc_codec_set_cache_io(codec, 32, 32, SND_SOC_REGMAP);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_dai_ops tegra30_i2s_dai_ops = {
+ .set_fmt = tegra30_i2s_set_fmt,
+ .hw_params = tegra30_i2s_hw_params,
+ .set_sysclk = tegra30_i2s_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_driver tegra30_i2s_dais[] = {
+ {
+ .name = "CIF",
+ .playback = {
+ .stream_name = "CIF Receive",
+ .channels_min = 1,
+ .channels_max = 16,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "CIF Transmit",
+ .channels_min = 1,
+ .channels_max = 16,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ },
+ {
+ .name = "DAP",
+ .playback = {
+ .stream_name = "DAP Receive",
+ .channels_min = 1,
+ .channels_max = 16,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .capture = {
+ .stream_name = "DAP Transmit",
+ .channels_min = 1,
+ .channels_max = 16,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &tegra30_i2s_dai_ops,
+ .symmetric_rates = 1,
+ }
+};
+
+static const struct snd_soc_dapm_widget tegra30_i2s_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("CIF RX", NULL, 0, SND_SOC_NOPM,
+ 0, 0),
+ SND_SOC_DAPM_AIF_OUT("CIF TX", NULL, 0, SND_SOC_NOPM,
+ 0, 0),
+ SND_SOC_DAPM_AIF_IN("DAP RX", NULL, 0, TEGRA30_I2S_CTRL,
+ TEGRA30_I2S_CTRL_XFER_EN_RX_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("DAP TX", NULL, 0, TEGRA30_I2S_CTRL,
+ TEGRA30_I2S_CTRL_XFER_EN_TX_SHIFT, 0),
+};
+
+static const struct snd_soc_dapm_route tegra30_i2s_routes[] = {
+ { "CIF RX", NULL, "CIF Receive" },
+ { "DAP TX", NULL, "CIF RX" },
+ { "DAP Transmit", NULL, "DAP TX" },
+
+ { "DAP RX", NULL, "DAP Receive" },
+ { "CIF TX", NULL, "DAP RX" },
+ { "CIF Transmit", NULL, "CIF TX" },
+};
+
+static struct snd_soc_codec_driver tegra30_i2s_codec = {
+ .probe = tegra30_i2s_codec_probe,
+ .dapm_widgets = tegra30_i2s_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tegra30_i2s_widgets),
+ .dapm_routes = tegra30_i2s_routes,
+ .num_dapm_routes = ARRAY_SIZE(tegra30_i2s_routes),
+};
+
+static bool tegra30_i2s_wr_rd_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA30_I2S_CTRL:
+ case TEGRA30_I2S_TIMING:
+ case TEGRA30_I2S_OFFSET:
+ case TEGRA30_I2S_CH_CTRL:
+ case TEGRA30_I2S_SLOT_CTRL:
+ case TEGRA30_I2S_AUDIOCIF_I2STX_CTRL:
+ case TEGRA30_I2S_AUDIOCIF_I2SRX_CTRL:
+ case TEGRA30_I2S_FLOWCTL:
+ case TEGRA30_I2S_TX_STEP:
+ case TEGRA30_I2S_FLOW_STATUS:
+ case TEGRA30_I2S_FLOW_TOTAL:
+ case TEGRA30_I2S_FLOW_OVER:
+ case TEGRA30_I2S_FLOW_UNDER:
+ case TEGRA30_I2S_LCOEF_1_4_0:
+ case TEGRA30_I2S_LCOEF_1_4_1:
+ case TEGRA30_I2S_LCOEF_1_4_2:
+ case TEGRA30_I2S_LCOEF_1_4_3:
+ case TEGRA30_I2S_LCOEF_1_4_4:
+ case TEGRA30_I2S_LCOEF_1_4_5:
+ case TEGRA30_I2S_LCOEF_2_4_0:
+ case TEGRA30_I2S_LCOEF_2_4_1:
+ case TEGRA30_I2S_LCOEF_2_4_2:
+ case TEGRA114_I2S_SLOT_CTRL2:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static bool tegra30_i2s_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA30_I2S_FLOW_STATUS:
+ case TEGRA30_I2S_FLOW_TOTAL:
+ case TEGRA30_I2S_FLOW_OVER:
+ case TEGRA30_I2S_FLOW_UNDER:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static const struct regmap_config tegra30_i2s_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = TEGRA114_I2S_SLOT_CTRL2,
+ .writeable_reg = tegra30_i2s_wr_rd_reg,
+ .readable_reg = tegra30_i2s_wr_rd_reg,
+ .volatile_reg = tegra30_i2s_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct tegra30_i2s_soc_data soc_data_tegra30 = {
+ .set_audio_cif = tegra30_xbar_set_cif,
+ .set_slot_ctrl = tegra30_i2s_set_slot_ctrl,
+};
+
+static const struct tegra30_i2s_soc_data soc_data_tegra114 = {
+ .set_audio_cif = tegra30_xbar_set_cif,
+ .set_slot_ctrl = tegra114_i2s_set_slot_ctrl,
+};
+
+static const struct tegra30_i2s_soc_data soc_data_tegra124 = {
+ .set_audio_cif = tegra124_xbar_set_cif,
+ .set_slot_ctrl = tegra114_i2s_set_slot_ctrl,
+};
+
+static const struct of_device_id tegra30_i2s_of_match[] = {
+ { .compatible = "nvidia,tegra30-i2s", .data = &soc_data_tegra30 },
+ { .compatible = "nvidia,tegra114-i2s", .data = &soc_data_tegra114 },
+ { .compatible = "nvidia,tegra124-i2s", .data = &soc_data_tegra124 },
+ {},
+};
+
+static int tegra30_i2s_platform_probe(struct platform_device *pdev)
+{
+ struct tegra30_i2s *i2s;
+ struct resource *mem, *memregion;
+ void __iomem *regs;
+ int ret = 0;
+ const struct of_device_id *match;
+ struct tegra30_i2s_soc_data *soc_data;
+
+ match = of_match_device(tegra30_i2s_of_match, &pdev->dev);
+ if (!match) {
+ dev_err(&pdev->dev, "Error: No device match found\n");
+ ret = -ENODEV;
+ goto err;
+ }
+ soc_data = (struct tegra30_i2s_soc_data *)match->data;
+
+ i2s = devm_kzalloc(&pdev->dev, sizeof(struct tegra30_i2s), GFP_KERNEL);
+ if (!i2s) {
+ dev_err(&pdev->dev, "Can't allocate i2s\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+ dev_set_drvdata(&pdev->dev, i2s);
+
+ i2s->soc_data = soc_data;
+
+ /* initialize srate with default sampling rate */
+ i2s->srate = 48000;
+
+ i2s->clk_i2s = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(i2s->clk_i2s)) {
+ dev_err(&pdev->dev, "Can't retrieve i2s clock\n");
+ ret = PTR_ERR(i2s->clk_i2s);
+ goto err;
+ }
+
+ i2s->clk_i2s_sync = devm_clk_get(&pdev->dev, "ext_audio_sync");
+ if (IS_ERR(i2s->clk_i2s_sync)) {
+ dev_err(&pdev->dev, "Can't retrieve i2s_sync clock\n");
+ ret = PTR_ERR(i2s->clk_i2s_sync);
+ goto err_clk_put;
+ }
+
+ i2s->clk_audio_2x = devm_clk_get(&pdev->dev, "audio_sync_2x");
+ if (IS_ERR(i2s->clk_audio_2x)) {
+ dev_err(&pdev->dev, "Can't retrieve audio 2x clock\n");
+ ret = PTR_ERR(i2s->clk_audio_2x);
+ goto err_i2s_sync_clk_put;
+ }
+
+ i2s->clk_pll_a_out0 = clk_get_sys(NULL, "pll_a_out0");
+ if (IS_ERR(i2s->clk_pll_a_out0)) {
+ dev_err(&pdev->dev, "Can't retrieve pll_a_out0 clock\n");
+ ret = PTR_ERR(i2s->clk_pll_a_out0);
+ goto err_audio_2x_clk_put;
+ }
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "No memory resource\n");
+ ret = -ENODEV;
+ goto err_pll_a_out0_clk_put;
+ }
+
+ memregion = devm_request_mem_region(&pdev->dev, mem->start,
+ resource_size(mem), DRV_NAME);
+ if (!memregion) {
+ dev_err(&pdev->dev, "Memory region already claimed\n");
+ ret = -EBUSY;
+ goto err_pll_a_out0_clk_put;
+ }
+
+ regs = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
+ if (!regs) {
+ dev_err(&pdev->dev, "ioremap failed\n");
+ ret = -ENOMEM;
+ goto err_pll_a_out0_clk_put;
+ }
+
+ i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
+ &tegra30_i2s_regmap_config);
+ if (IS_ERR(i2s->regmap)) {
+ dev_err(&pdev->dev, "regmap init failed\n");
+ ret = PTR_ERR(i2s->regmap);
+ goto err_pll_a_out0_clk_put;
+ }
+ regcache_cache_only(i2s->regmap, true);
+
+ pm_runtime_enable(&pdev->dev);
+ if (!pm_runtime_enabled(&pdev->dev)) {
+ ret = tegra30_i2s_runtime_resume(&pdev->dev);
+ if (ret)
+ goto err_pm_disable;
+ }
+
+ ret = snd_soc_register_codec(&pdev->dev, &tegra30_i2s_codec,
+ tegra30_i2s_dais,
+ ARRAY_SIZE(tegra30_i2s_dais));
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Could not register CODEC: %d\n", ret);
+ goto err_suspend;
+ }
+
+ return 0;
+
+err_suspend:
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ tegra30_i2s_runtime_suspend(&pdev->dev);
+err_pm_disable:
+ pm_runtime_disable(&pdev->dev);
+err_pll_a_out0_clk_put:
+ clk_put(i2s->clk_pll_a_out0);
+err_audio_2x_clk_put:
+ devm_clk_put(&pdev->dev, i2s->clk_audio_2x);
+err_i2s_sync_clk_put:
+ devm_clk_put(&pdev->dev, i2s->clk_i2s_sync);
+err_clk_put:
+ devm_clk_put(&pdev->dev, i2s->clk_i2s);
+err:
+ return ret;
+}
+
+static int tegra30_i2s_platform_remove(struct platform_device *pdev)
+{
+ struct tegra30_i2s *i2s = dev_get_drvdata(&pdev->dev);
+
+ snd_soc_unregister_codec(&pdev->dev);
+
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ tegra30_i2s_runtime_suspend(&pdev->dev);
+
+ devm_clk_put(&pdev->dev, i2s->clk_i2s);
+ devm_clk_put(&pdev->dev, i2s->clk_i2s_sync);
+ devm_clk_put(&pdev->dev, i2s->clk_audio_2x);
+ clk_put(i2s->clk_pll_a_out0);
+
+ return 0;
+}
+
+static const struct dev_pm_ops tegra30_i2s_pm_ops = {
+ SET_RUNTIME_PM_OPS(tegra30_i2s_runtime_suspend,
+ tegra30_i2s_runtime_resume, NULL)
+};
+
+static struct platform_driver tegra30_i2s_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = tegra30_i2s_of_match,
+ .pm = &tegra30_i2s_pm_ops,
+ },
+ .probe = tegra30_i2s_platform_probe,
+ .remove = tegra30_i2s_platform_remove,
+};
+module_platform_driver(tegra30_i2s_driver);
+
+MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_DESCRIPTION("Tegra30 I2S ASoC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, tegra30_i2s_of_match);
diff --git a/sound/soc/tegra-alt/tegra30_i2s_alt.h b/sound/soc/tegra-alt/tegra30_i2s_alt.h
new file mode 100644
index 000000000000..51a572e27481
--- /dev/null
+++ b/sound/soc/tegra-alt/tegra30_i2s_alt.h
@@ -0,0 +1,266 @@
+/*
+ * tegra30_i2s_alt.h - Definitions for Tegra30 I2S driver
+ *
+ * Copyright (c) 2011-2013, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __TEGRA30_I2S_ALT_H__
+#define __TEGRA30_I2S_ALT_H__
+
+/* Register offsets from TEGRA30_I2S*_BASE */
+
+#define TEGRA30_I2S_CTRL 0x0
+#define TEGRA30_I2S_TIMING 0x4
+#define TEGRA30_I2S_OFFSET 0x08
+#define TEGRA30_I2S_CH_CTRL 0x0c
+#define TEGRA30_I2S_SLOT_CTRL 0x10
+#define TEGRA30_I2S_AUDIOCIF_I2STX_CTRL 0x14
+#define TEGRA30_I2S_AUDIOCIF_I2SRX_CTRL 0x18
+#define TEGRA30_I2S_FLOWCTL 0x1c
+#define TEGRA30_I2S_TX_STEP 0x20
+#define TEGRA30_I2S_FLOW_STATUS 0x24
+#define TEGRA30_I2S_FLOW_TOTAL 0x28
+#define TEGRA30_I2S_FLOW_OVER 0x2c
+#define TEGRA30_I2S_FLOW_UNDER 0x30
+#define TEGRA30_I2S_LCOEF_1_4_0 0x34
+#define TEGRA30_I2S_LCOEF_1_4_1 0x38
+#define TEGRA30_I2S_LCOEF_1_4_2 0x3c
+#define TEGRA30_I2S_LCOEF_1_4_3 0x40
+#define TEGRA30_I2S_LCOEF_1_4_4 0x44
+#define TEGRA30_I2S_LCOEF_1_4_5 0x48
+#define TEGRA30_I2S_LCOEF_2_4_0 0x4c
+#define TEGRA30_I2S_LCOEF_2_4_1 0x50
+#define TEGRA30_I2S_LCOEF_2_4_2 0x54
+#define TEGRA114_I2S_SLOT_CTRL2 0x64
+
+/* Fields in TEGRA30_I2S_CTRL */
+#define TEGRA30_I2S_CTRL_XFER_EN_TX_SHIFT 31
+#define TEGRA30_I2S_CTRL_XFER_EN_RX_SHIFT 30
+#define TEGRA30_I2S_CTRL_XFER_EN_TX (1 << TEGRA30_I2S_CTRL_XFER_EN_TX_SHIFT)
+#define TEGRA30_I2S_CTRL_XFER_EN_RX (1 << TEGRA30_I2S_CTRL_XFER_EN_RX_SHIFT)
+#define TEGRA30_I2S_CTRL_CG_EN (1 << 29)
+#define TEGRA30_I2S_CTRL_SOFT_RESET (1 << 28)
+#define TEGRA30_I2S_CTRL_TX_FLOWCTL_EN (1 << 27)
+
+#define TEGRA30_I2S_CTRL_OBS_SEL_SHIFT 24
+#define TEGRA30_I2S_CTRL_OBS_SEL_MASK (7 << TEGRA30_I2S_CTRL_OBS_SEL_SHIFT)
+
+#define TEGRA30_I2S_FRAME_FORMAT_LRCK 0
+#define TEGRA30_I2S_FRAME_FORMAT_FSYNC 1
+
+#define TEGRA30_I2S_CTRL_FRAME_FORMAT_SHIFT 12
+#define TEGRA30_I2S_CTRL_FRAME_FORMAT_MASK (7 << TEGRA30_I2S_CTRL_FRAME_FORMAT_SHIFT)
+#define TEGRA30_I2S_CTRL_FRAME_FORMAT_LRCK (TEGRA30_I2S_FRAME_FORMAT_LRCK << TEGRA30_I2S_CTRL_FRAME_FORMAT_SHIFT)
+#define TEGRA30_I2S_CTRL_FRAME_FORMAT_FSYNC (TEGRA30_I2S_FRAME_FORMAT_FSYNC << TEGRA30_I2S_CTRL_FRAME_FORMAT_SHIFT)
+
+#define TEGRA30_I2S_CTRL_MASTER_ENABLE (1 << 10)
+#define TEGRA30_I2S_CTRL_SLAVE_ENABLE 0
+#define TEGRA30_I2S_CTRL_MASTER_MASK (1 << 10)
+
+#define TEGRA30_I2S_LRCK_LEFT_LOW 0
+#define TEGRA30_I2S_LRCK_RIGHT_LOW 1
+
+#define TEGRA30_I2S_CTRL_LRCK_SHIFT 9
+#define TEGRA30_I2S_CTRL_LRCK_MASK (1 << TEGRA30_I2S_CTRL_LRCK_SHIFT)
+#define TEGRA30_I2S_CTRL_LRCK_L_LOW (TEGRA30_I2S_LRCK_LEFT_LOW << TEGRA30_I2S_CTRL_LRCK_SHIFT)
+#define TEGRA30_I2S_CTRL_LRCK_R_LOW (TEGRA30_I2S_LRCK_RIGHT_LOW << TEGRA30_I2S_CTRL_LRCK_SHIFT)
+
+#define TEGRA30_I2S_CTRL_LPBK_ENABLE (1 << 8)
+
+#define TEGRA30_I2S_BIT_CODE_LINEAR 0
+#define TEGRA30_I2S_BIT_CODE_ULAW 1
+#define TEGRA30_I2S_BIT_CODE_ALAW 2
+
+#define TEGRA30_I2S_CTRL_BIT_CODE_SHIFT 4
+#define TEGRA30_I2S_CTRL_BIT_CODE_MASK (3 << TEGRA30_I2S_CTRL_BIT_CODE_SHIFT)
+#define TEGRA30_I2S_CTRL_BIT_CODE_LINEAR (TEGRA30_I2S_BIT_CODE_LINEAR << TEGRA30_I2S_CTRL_BIT_CODE_SHIFT)
+#define TEGRA30_I2S_CTRL_BIT_CODE_ULAW (TEGRA30_I2S_BIT_CODE_ULAW << TEGRA30_I2S_CTRL_BIT_CODE_SHIFT)
+#define TEGRA30_I2S_CTRL_BIT_CODE_ALAW (TEGRA30_I2S_BIT_CODE_ALAW << TEGRA30_I2S_CTRL_BIT_CODE_SHIFT)
+
+#define TEGRA30_I2S_BITS_8 1
+#define TEGRA30_I2S_BITS_12 2
+#define TEGRA30_I2S_BITS_16 3
+#define TEGRA30_I2S_BITS_20 4
+#define TEGRA30_I2S_BITS_24 5
+#define TEGRA30_I2S_BITS_28 6
+#define TEGRA30_I2S_BITS_32 7
+
+/* Sample container size; see {RX,TX}_MASK field in CH_CTRL below */
+#define TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT 0
+#define TEGRA30_I2S_CTRL_BIT_SIZE_MASK (7 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA30_I2S_CTRL_BIT_SIZE_8 (TEGRA30_I2S_BITS_8 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA30_I2S_CTRL_BIT_SIZE_12 (TEGRA30_I2S_BITS_12 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA30_I2S_CTRL_BIT_SIZE_16 (TEGRA30_I2S_BITS_16 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA30_I2S_CTRL_BIT_SIZE_20 (TEGRA30_I2S_BITS_20 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA30_I2S_CTRL_BIT_SIZE_24 (TEGRA30_I2S_BITS_24 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA30_I2S_CTRL_BIT_SIZE_28 (TEGRA30_I2S_BITS_28 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT)
+#define TEGRA30_I2S_CTRL_BIT_SIZE_32 (TEGRA30_I2S_BITS_32 << TEGRA30_I2S_CTRL_BIT_SIZE_SHIFT)
+
+/* Fields in TEGRA30_I2S_TIMING */
+
+#define TEGRA30_I2S_TIMING_NON_SYM_ENABLE (1 << 12)
+#define TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT 0
+#define TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US 0x7fff
+#define TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_MASK (TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US << TEGRA30_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT)
+
+/* Fields in TEGRA30_I2S_OFFSET */
+
+#define TEGRA30_I2S_OFFSET_RX_DATA_OFFSET_SHIFT 16
+#define TEGRA30_I2S_OFFSET_RX_DATA_OFFSET_MASK_US 0x7ff
+#define TEGRA30_I2S_OFFSET_RX_DATA_OFFSET_MASK (TEGRA30_I2S_OFFSET_RX_DATA_OFFSET_MASK_US << TEGRA30_I2S_OFFSET_RX_DATA_OFFSET_SHIFT)
+#define TEGRA30_I2S_OFFSET_TX_DATA_OFFSET_SHIFT 0
+#define TEGRA30_I2S_OFFSET_TX_DATA_OFFSET_MASK_US 0x7ff
+#define TEGRA30_I2S_OFFSET_TX_DATA_OFFSET_MASK (TEGRA30_I2S_OFFSET_TX_DATA_OFFSET_MASK_US << TEGRA30_I2S_OFFSET_TX_DATA_OFFSET_SHIFT)
+
+/* Fields in TEGRA30_I2S_CH_CTRL */
+
+/* (FSYNC width - 1) in bit clocks */
+#define TEGRA30_I2S_CH_CTRL_FSYNC_WIDTH_SHIFT 24
+#define TEGRA30_I2S_CH_CTRL_FSYNC_WIDTH_MASK_US 0xff
+#define TEGRA30_I2S_CH_CTRL_FSYNC_WIDTH_MASK (TEGRA30_I2S_CH_CTRL_FSYNC_WIDTH_MASK_US << TEGRA30_I2S_CH_CTRL_FSYNC_WIDTH_SHIFT)
+
+#define TEGRA30_I2S_HIGHZ_NO 0
+#define TEGRA30_I2S_HIGHZ_YES 1
+#define TEGRA30_I2S_HIGHZ_ON_HALF_BIT_CLK 2
+
+#define TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_SHIFT 12
+#define TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_MASK (3 << TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_SHIFT)
+#define TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_NO (TEGRA30_I2S_HIGHZ_NO << TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_SHIFT)
+#define TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_YES (TEGRA30_I2S_HIGHZ_YES << TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_SHIFT)
+#define TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_ON_HALF_BIT_CLK (TEGRA30_I2S_HIGHZ_ON_HALF_BIT_CLK << TEGRA30_I2S_CH_CTRL_HIGHZ_CTRL_SHIFT)
+
+#define TEGRA30_I2S_MSB_FIRST 0
+#define TEGRA30_I2S_LSB_FIRST 1
+
+#define TEGRA30_I2S_CH_CTRL_RX_BIT_ORDER_SHIFT 10
+#define TEGRA30_I2S_CH_CTRL_RX_BIT_ORDER_MASK (1 << TEGRA30_I2S_CH_CTRL_RX_BIT_ORDER_SHIFT)
+#define TEGRA30_I2S_CH_CTRL_RX_BIT_ORDER_MSB_FIRST (TEGRA30_I2S_MSB_FIRST << TEGRA30_I2S_CH_CTRL_RX_BIT_ORDER_SHIFT)
+#define TEGRA30_I2S_CH_CTRL_RX_BIT_ORDER_LSB_FIRST (TEGRA30_I2S_LSB_FIRST << TEGRA30_I2S_CH_CTRL_RX_BIT_ORDER_SHIFT)
+#define TEGRA30_I2S_CH_CTRL_TX_BIT_ORDER_SHIFT 9
+#define TEGRA30_I2S_CH_CTRL_TX_BIT_ORDER_MASK (1 << TEGRA30_I2S_CH_CTRL_TX_BIT_ORDER_SHIFT)
+#define TEGRA30_I2S_CH_CTRL_TX_BIT_ORDER_MSB_FIRST (TEGRA30_I2S_MSB_FIRST << TEGRA30_I2S_CH_CTRL_TX_BIT_ORDER_SHIFT)
+#define TEGRA30_I2S_CH_CTRL_TX_BIT_ORDER_LSB_FIRST (TEGRA30_I2S_LSB_FIRST << TEGRA30_I2S_CH_CTRL_TX_BIT_ORDER_SHIFT)
+
+#define TEGRA30_I2S_POS_EDGE 0
+#define TEGRA30_I2S_NEG_EDGE 1
+
+#define TEGRA30_I2S_CH_CTRL_EGDE_CTRL_SHIFT 8
+#define TEGRA30_I2S_CH_CTRL_EGDE_CTRL_MASK (1 << TEGRA30_I2S_CH_CTRL_EGDE_CTRL_SHIFT)
+#define TEGRA30_I2S_CH_CTRL_EGDE_CTRL_POS_EDGE (TEGRA30_I2S_POS_EDGE << TEGRA30_I2S_CH_CTRL_EGDE_CTRL_SHIFT)
+#define TEGRA30_I2S_CH_CTRL_EGDE_CTRL_NEG_EDGE (TEGRA30_I2S_NEG_EDGE << TEGRA30_I2S_CH_CTRL_EGDE_CTRL_SHIFT)
+
+/* Sample size is # bits from BIT_SIZE minus this field */
+#define TEGRA30_I2S_CH_CTRL_RX_MASK_BITS_SHIFT 4
+#define TEGRA30_I2S_CH_CTRL_RX_MASK_BITS_MASK_US 7
+#define TEGRA30_I2S_CH_CTRL_RX_MASK_BITS_MASK (TEGRA30_I2S_CH_CTRL_RX_MASK_BITS_MASK_US << TEGRA30_I2S_CH_CTRL_RX_MASK_BITS_SHIFT)
+
+#define TEGRA30_I2S_CH_CTRL_TX_MASK_BITS_SHIFT 0
+#define TEGRA30_I2S_CH_CTRL_TX_MASK_BITS_MASK_US 7
+#define TEGRA30_I2S_CH_CTRL_TX_MASK_BITS_MASK (TEGRA30_I2S_CH_CTRL_TX_MASK_BITS_MASK_US << TEGRA30_I2S_CH_CTRL_TX_MASK_BITS_SHIFT)
+
+/* Fields in TEGRA30_I2S_SLOT_CTRL */
+
+/* Number of slots in frame, minus 1 */
+#define TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_SHIFT 0
+#define TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_MASK_US 7
+#define TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOTS_MASK (TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOT_MASK_US << TEGRA30_I2S_SLOT_CTRL_TOTAL_SLOT_SHIFT)
+/* Rx Slot enables */
+#define TEGRA30_I2S_SLOT_CTRL_RX_SLOT_ENABLES_SHIFT 8
+#define TEGRA30_I2S_SLOT_CTRL_RX_SLOT_ENABLES_MASK_US 0xff
+#define TEGRA30_I2S_SLOT_CTRL_RX_SLOT_ENABLES_MASK (TEGRA30_I2S_SLOT_CTRL_RX_SLOT_ENABLES_MASK_US << TEGRA30_I2S_SLOT_CTRL_RX_SLOT_ENABLES_SHIFT)
+
+/* Tx Slot enables */
+#define TEGRA30_I2S_SLOT_CTRL_TX_SLOT_ENABLES_SHIFT 0
+#define TEGRA30_I2S_SLOT_CTRL_TX_SLOT_ENABLES_MASK_US 0xff
+#define TEGRA30_I2S_SLOT_CTRL_TX_SLOT_ENABLES_MASK (TEGRA30_I2S_SLOT_CTRL_TX_SLOT_ENABLES_MASK_US << TEGRA30_I2S_SLOT_CTRL_TX_SLOT_ENABLES_SHIFT)
+
+
+
+/* Fields in TEGRA30_I2S_CIF_RX_CTRL */
+/* Uses field from AUDIOCIF_CTRL_* in tegra_cif_utils_alt.h */
+
+/* Fields in TEGRA30_I2S_CIF_TX_CTRL */
+/* Uses field from AUDIOCIF_CTRL_* in tegra_cif_utils_alt.h */
+
+/* Fields in TEGRA30_I2S_FLOWCTL */
+
+#define TEGRA30_I2S_FILTER_LINEAR 0
+#define TEGRA30_I2S_FILTER_QUAD 1
+
+#define TEGRA30_I2S_FLOWCTL_FILTER_SHIFT 31
+#define TEGRA30_I2S_FLOWCTL_FILTER_MASK (1 << TEGRA30_I2S_FLOWCTL_FILTER_SHIFT)
+#define TEGRA30_I2S_FLOWCTL_FILTER_LINEAR (TEGRA30_I2S_FILTER_LINEAR << TEGRA30_I2S_FLOWCTL_FILTER_SHIFT)
+#define TEGRA30_I2S_FLOWCTL_FILTER_QUAD (TEGRA30_I2S_FILTER_QUAD << TEGRA30_I2S_FLOWCTL_FILTER_SHIFT)
+
+/* Fields in TEGRA30_I2S_TX_STEP */
+
+#define TEGRA30_I2S_TX_STEP_SHIFT 0
+#define TEGRA30_I2S_TX_STEP_MASK_US 0xffff
+#define TEGRA30_I2S_TX_STEP_MASK (TEGRA30_I2S_TX_STEP_MASK_US << TEGRA30_I2S_TX_STEP_SHIFT)
+
+/* Fields in TEGRA30_I2S_FLOW_STATUS */
+
+#define TEGRA30_I2S_FLOW_STATUS_UNDERFLOW (1 << 31)
+#define TEGRA30_I2S_FLOW_STATUS_OVERFLOW (1 << 30)
+#define TEGRA30_I2S_FLOW_STATUS_MONITOR_INT_EN (1 << 4)
+#define TEGRA30_I2S_FLOW_STATUS_COUNTER_CLR (1 << 3)
+#define TEGRA30_I2S_FLOW_STATUS_MONITOR_CLR (1 << 2)
+#define TEGRA30_I2S_FLOW_STATUS_COUNTER_EN (1 << 1)
+#define TEGRA30_I2S_FLOW_STATUS_MONITOR_EN (1 << 0)
+
+/*
+ * There are no fields in TEGRA30_I2S_FLOW_TOTAL, I2S_FLOW_OVER,
+ * TEGRA30_I2S_FLOW_UNDER; they are counters taking the whole register.
+ */
+
+/* Fields in TEGRA30_I2S_LCOEF_* */
+
+#define TEGRA30_I2S_LCOEF_COEF_SHIFT 0
+#define TEGRA30_I2S_LCOEF_COEF_MASK_US 0xffff
+#define TEGRA30_I2S_LCOEF_COEF_MASK (TEGRA30_I2S_LCOEF_COEF_MASK_US << TEGRA30_I2S_LCOEF_COEF_SHIFT)
+
+/* Fields in TEGRA114_I2S_SLOT_CTRL2 */
+
+/* TDM mode slot enable bitmask */
+#define TEGRA114_I2S_SLOT_CTRL2_RX_SLOT_ENABLES_SHIFT 16
+#define TEGRA114_I2S_SLOT_CTRL2_RX_SLOT_ENABLES_MASK (0xffff << TEGRA114_I2S_SLOT_CTRL_RX_SLOT_ENABLES_SHIFT)
+
+#define TEGRA114_I2S_SLOT_CTRL2_TX_SLOT_ENABLES_SHIFT 0
+#define TEGRA114_I2S_SLOT_CTRL2_TX_SLOT_ENABLES_MASK (0xffff << TEGRA114_I2S_SLOT_CTRL_TX_SLOT_ENABLES_SHIFT)
+
+struct tegra30_i2s_soc_data {
+ void (*set_audio_cif)(struct regmap *map,
+ unsigned int reg,
+ struct tegra30_xbar_cif_conf *conf);
+ void (*set_slot_ctrl)(struct regmap *map,
+ unsigned int total_slots,
+ unsigned int tx_slot_mask,
+ unsigned int rx_slot_mask);
+};
+
+struct tegra30_i2s {
+ struct clk *clk_i2s;
+ struct clk *clk_i2s_sync;
+ struct clk *clk_pll_a_out0;
+ struct clk *clk_audio_2x;
+ struct regmap *regmap;
+ struct pinctrl *pinctrl;
+ struct pinctrl_state *pin_default_state;
+ struct pinctrl_state *pin_idle_state;
+ unsigned int srate;
+ const struct tegra30_i2s_soc_data *soc_data;
+};
+
+#endif
diff --git a/sound/soc/tegra-alt/tegra30_xbar_alt.c b/sound/soc/tegra-alt/tegra30_xbar_alt.c
new file mode 100644
index 000000000000..96054fc5d07b
--- /dev/null
+++ b/sound/soc/tegra-alt/tegra30_xbar_alt.c
@@ -0,0 +1,798 @@
+/*
+ * tegra30_xbar_alt.c - Tegra30 XBAR driver
+ *
+ * Copyright (c) 2011-2013 NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+
+#include "tegra30_xbar_alt.h"
+
+#define DRV_NAME "tegra30-ahub-xbar"
+
+static const struct regmap_config tegra30_xbar_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = TEGRA_AHUB_AUDIO_RX_STRIDE *
+ (TEGRA_AHUB_AUDIO_RX_COUNT - 1),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct regmap_config tegra124_xbar_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = TEGRA_AHUB_AUDIO_RX1 + (TEGRA_AHUB_AUDIO_RX_STRIDE *
+ (TEGRA_AHUB_AUDIO_RX_COUNT - 1)),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int tegra30_xbar_runtime_suspend(struct device *dev)
+{
+ struct tegra30_xbar *xbar = dev_get_drvdata(dev);
+
+ regcache_cache_only(xbar->regmap, true);
+
+ clk_disable(xbar->clk);
+
+ return 0;
+}
+
+static int tegra30_xbar_runtime_resume(struct device *dev)
+{
+ struct tegra30_xbar *xbar = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_enable(xbar->clk);
+ if (ret) {
+ dev_err(dev, "clk_enable failed: %d\n", ret);
+ return ret;
+ }
+
+ regcache_cache_only(xbar->regmap, false);
+
+ return 0;
+}
+
+static int tegra30_xbar_codec_probe(struct snd_soc_codec *codec)
+{
+ struct tegra30_xbar *xbar = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ codec->control_data = xbar->regmap;
+ ret = snd_soc_codec_set_cache_io(codec, 32, 32, SND_SOC_REGMAP);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+#define DAI(sname) \
+ { \
+ .name = #sname, \
+ .playback = { \
+ .stream_name = #sname " Receive", \
+ .channels_min = 2, \
+ .channels_max = 2, \
+ .rates = SNDRV_PCM_RATE_8000_96000, \
+ .formats = SNDRV_PCM_FMTBIT_S16_LE, \
+ }, \
+ .capture = { \
+ .stream_name = #sname " Transmit", \
+ .channels_min = 2, \
+ .channels_max = 2, \
+ .rates = SNDRV_PCM_RATE_8000_96000, \
+ .formats = SNDRV_PCM_FMTBIT_S16_LE, \
+ }, \
+ }
+
+static struct snd_soc_dai_driver tegra30_xbar_dais[] = {
+ DAI(APBIF0),
+ DAI(APBIF1),
+ DAI(APBIF2),
+ DAI(APBIF3),
+ DAI(I2S0),
+ DAI(I2S1),
+ DAI(I2S2),
+ DAI(I2S3),
+ DAI(I2S4),
+ /* index 0..8 above are used on Tegra30 */
+ DAI(APBIF4),
+ DAI(APBIF5),
+ DAI(APBIF6),
+ DAI(APBIF7),
+ DAI(APBIF8),
+ DAI(APBIF9),
+ DAI(AMX0),
+ DAI(AMX0-0),
+ DAI(AMX0-1),
+ DAI(AMX0-2),
+ DAI(AMX0-3),
+ DAI(ADX0-0),
+ DAI(ADX0-1),
+ DAI(ADX0-2),
+ DAI(ADX0-3),
+ DAI(ADX0),
+ /* index 0..24 above are used on Tegra114 */
+ DAI(AMX1),
+ DAI(AMX1-0),
+ DAI(AMX1-1),
+ DAI(AMX1-2),
+ DAI(AMX1-3),
+ DAI(ADX1-0),
+ DAI(ADX1-1),
+ DAI(ADX1-2),
+ DAI(ADX1-3),
+ DAI(ADX1),
+ /* index 0..34 above are used on Tegra124 */
+};
+
+static const char * const tegra30_xbar_mux_texts[] = {
+ "None",
+ "APBIF0",
+ "APBIF1",
+ "APBIF2",
+ "APBIF3",
+ "I2S0",
+ "I2S1",
+ "I2S2",
+ "I2S3",
+ "I2S4",
+ /* index 0..9 above are used on Tegra30 */
+ "APBIF4",
+ "APBIF5",
+ "APBIF6",
+ "APBIF7",
+ "APBIF8",
+ "APBIF9",
+ "AMX0",
+ "ADX0-0",
+ "ADX0-1",
+ "ADX0-2",
+ "ADX0-3",
+ /* index 0..20 above are used on Tegra114 */
+ "AMX1",
+ "ADX1-0",
+ "ADX1-1",
+ "ADX1-2",
+ "ADX1-3",
+ /* index 0..25 above are used on Tegra124 */
+};
+
+static const int tegra30_xbar_mux_values[] = {
+ /* Mux0 input, Mux1 input */
+ 0, 0,
+ BIT(0), 0,
+ BIT(1), 0,
+ BIT(2), 0,
+ BIT(3), 0,
+ BIT(4), 0,
+ BIT(5), 0,
+ BIT(6), 0,
+ BIT(7), 0,
+ BIT(8), 0,
+ /* index 0..9 above are used on Tegra30 */
+ BIT(14), 0,
+ BIT(15), 0,
+ BIT(16), 0,
+ BIT(17), 0,
+ BIT(18), 0,
+ BIT(19), 0,
+ BIT(20), 0,
+ BIT(21), 0,
+ BIT(22), 0,
+ BIT(23), 0,
+ BIT(24), 0,
+ /* index 0..20 above are used on Tegra114 */
+ 0, BIT(0),
+ 0, BIT(1),
+ 0, BIT(2),
+ 0, BIT(3),
+ 0, BIT(4),
+ /* index 0..25 above are used on Tegra124 */
+};
+
+int tegra30_xbar_get_value_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int reg_val[2], mux;
+ struct snd_soc_codec *codec = widget->codec;
+ struct tegra30_xbar *xbar = snd_soc_codec_get_drvdata(codec);
+
+ regmap_read(widget->codec->control_data, e->reg, &reg_val[0]);
+
+ if (xbar->soc_data->num_mux1_input)
+ regmap_read(widget->codec->control_data, e->reg2, &reg_val[1]);
+ else
+ reg_val[1] = 0;
+
+ for (mux = 0; mux < e->max; mux++) {
+ if ((reg_val[0] == e->values[mux * 2]) &&
+ (reg_val[1] == e->values[mux * 2 + 1]))
+ break;
+ }
+ ucontrol->value.enumerated.item[0] = mux;
+
+ return 0;
+}
+
+int tegra30_xbar_put_value_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int value[2], mux, old;
+ struct snd_soc_dapm_update update;
+ int wi;
+ struct snd_soc_codec *codec = widget->codec;
+ struct tegra30_xbar *xbar = snd_soc_codec_get_drvdata(codec);
+
+ if (ucontrol->value.enumerated.item[0] > e->max - 1)
+ return -EINVAL;
+
+ mux = ucontrol->value.enumerated.item[0];
+ value[0] = e->values[ucontrol->value.enumerated.item[0] * 2];
+ value[1] = e->values[(ucontrol->value.enumerated.item[0] * 2) + 1];
+
+ regmap_read(widget->codec->control_data, e->reg, &old);
+ if (value[0] != old) {
+ for (wi = 0; wi < wlist->num_widgets; wi++) {
+ widget = wlist->widgets[wi];
+ widget->value = value[0];
+ update.kcontrol = kcontrol;
+ update.widget = widget;
+ update.reg = e->reg;
+ update.mask = xbar->soc_data->mask[0];
+ update.val = value[0];
+ widget->dapm->update = &update;
+ snd_soc_dapm_mux_update_power(widget, kcontrol, mux, e);
+ widget->dapm->update = NULL;
+ }
+ }
+
+ if (xbar->soc_data->num_mux1_input) {
+ regmap_read(widget->codec->control_data, e->reg2, &old);
+ if (value[1] != old) {
+ for (wi = 0; wi < wlist->num_widgets; wi++) {
+ widget = wlist->widgets[wi];
+ widget->value = value[1];
+ update.kcontrol = kcontrol;
+ update.widget = widget;
+ update.reg = e->reg2;
+ update.mask = xbar->soc_data->mask[1];
+ update.val = value[1];
+ widget->dapm->update = &update;
+ snd_soc_dapm_mux_update_power(widget, kcontrol, mux, e);
+ widget->dapm->update = NULL;
+ }
+ }
+ }
+
+ return 0;
+}
+
+#define MUX0_REG(id) (TEGRA_AHUB_AUDIO_RX + \
+ (TEGRA_AHUB_AUDIO_RX_STRIDE * (id)))
+
+#define MUX1_REG(id) (TEGRA_AHUB_AUDIO_RX1 + \
+ (TEGRA_AHUB_AUDIO_RX_STRIDE * (id)))
+
+#define SOC_ENUM_WIDE(xreg, yreg, shift, xmax, xtexts, xvalues) \
+{ .reg = xreg, .reg2 = yreg, .shift_l = shift, .shift_r = shift, \
+ .max = xmax, .texts = xtexts, .values = xvalues, \
+ .mask = xmax ? roundup_pow_of_two(xmax) - 1 : 0}
+
+#define SOC_ENUM_WIDE_DECL(name, xreg, yreg, shift, xtexts, xvalues) \
+ struct soc_enum name = SOC_ENUM_WIDE(xreg, yreg, shift, \
+ ARRAY_SIZE(xtexts), xtexts, xvalues)
+
+#define MUX_ENUM_CTRL_DECL(ename, id) \
+ SOC_ENUM_WIDE_DECL(ename##_enum, MUX0_REG(id), MUX1_REG(id), \
+ 0, tegra30_xbar_mux_texts, tegra30_xbar_mux_values); \
+ static const struct snd_kcontrol_new ename##_control = \
+ SOC_DAPM_ENUM_EXT("Route", ename##_enum,\
+ tegra30_xbar_get_value_enum,\
+ tegra30_xbar_put_value_enum)
+
+MUX_ENUM_CTRL_DECL(apbif0_tx, 0x00);
+MUX_ENUM_CTRL_DECL(apbif1_tx, 0x01);
+MUX_ENUM_CTRL_DECL(apbif2_tx, 0x02);
+MUX_ENUM_CTRL_DECL(apbif3_tx, 0x03);
+MUX_ENUM_CTRL_DECL(i2s0_tx, 0x04);
+MUX_ENUM_CTRL_DECL(i2s1_tx, 0x05);
+MUX_ENUM_CTRL_DECL(i2s2_tx, 0x06);
+MUX_ENUM_CTRL_DECL(i2s3_tx, 0x07);
+MUX_ENUM_CTRL_DECL(i2s4_tx, 0x08);
+/* above controls are used on Tegra30 */
+MUX_ENUM_CTRL_DECL(apbif4_tx, 0x11);
+MUX_ENUM_CTRL_DECL(apbif5_tx, 0x12);
+MUX_ENUM_CTRL_DECL(apbif6_tx, 0x13);
+MUX_ENUM_CTRL_DECL(apbif7_tx, 0x14);
+MUX_ENUM_CTRL_DECL(apbif8_tx, 0x15);
+MUX_ENUM_CTRL_DECL(apbif9_tx, 0x16);
+MUX_ENUM_CTRL_DECL(amx00_tx, 0x17);
+MUX_ENUM_CTRL_DECL(amx01_tx, 0x18);
+MUX_ENUM_CTRL_DECL(amx02_tx, 0x19);
+MUX_ENUM_CTRL_DECL(amx03_tx, 0x1a);
+MUX_ENUM_CTRL_DECL(adx0_tx, 0x1b);
+/* above controls are used on Tegra114 */
+MUX_ENUM_CTRL_DECL(amx10_tx, 0x1e);
+MUX_ENUM_CTRL_DECL(amx11_tx, 0x1f);
+MUX_ENUM_CTRL_DECL(amx12_tx, 0x20);
+MUX_ENUM_CTRL_DECL(amx13_tx, 0x21);
+MUX_ENUM_CTRL_DECL(adx1_tx, 0x22);
+/* above controls are used on Tegra124 */
+
+#define WIDGETS(sname, ename) \
+ SND_SOC_DAPM_AIF_IN(sname " RX", NULL, 0, SND_SOC_NOPM, 0, 0), \
+ SND_SOC_DAPM_AIF_OUT(sname " TX", NULL, 0, SND_SOC_NOPM, 0, 0), \
+ SND_SOC_DAPM_VALUE_MUX(sname " Mux", SND_SOC_NOPM, 0, 0, &ename##_control)
+
+#define TX_WIDGETS(sname) \
+ SND_SOC_DAPM_AIF_IN(sname " RX", NULL, 0, SND_SOC_NOPM, 0, 0), \
+ SND_SOC_DAPM_AIF_OUT(sname " TX", NULL, 0, SND_SOC_NOPM, 0, 0)
+
+/*
+ * The number of entries in, and order of, this array is closely tied to the
+ * calculation of tegra30_xbar_codec.num_dapm_widgets near the end of
+ * tegra30_xbar_probe()
+ */
+static const struct snd_soc_dapm_widget tegra30_xbar_widgets[] = {
+ WIDGETS("APBIF0", apbif0_tx),
+ WIDGETS("APBIF1", apbif1_tx),
+ WIDGETS("APBIF2", apbif2_tx),
+ WIDGETS("APBIF3", apbif3_tx),
+ WIDGETS("I2S0", i2s0_tx),
+ WIDGETS("I2S1", i2s1_tx),
+ WIDGETS("I2S2", i2s2_tx),
+ WIDGETS("I2S3", i2s3_tx),
+ WIDGETS("I2S4", i2s4_tx),
+ /* index 0..8 above are used on Tegra30 */
+ WIDGETS("APBIF4", apbif4_tx),
+ WIDGETS("APBIF5", apbif5_tx),
+ WIDGETS("APBIF6", apbif6_tx),
+ WIDGETS("APBIF7", apbif7_tx),
+ WIDGETS("APBIF8", apbif8_tx),
+ WIDGETS("APBIF9", apbif9_tx),
+ WIDGETS("AMX0-0", amx00_tx),
+ WIDGETS("AMX0-1", amx01_tx),
+ WIDGETS("AMX0-2", amx02_tx),
+ WIDGETS("AMX0-3", amx03_tx),
+ WIDGETS("ADX0", adx0_tx),
+ TX_WIDGETS("AMX0"),
+ TX_WIDGETS("ADX0-0"),
+ TX_WIDGETS("ADX0-1"),
+ TX_WIDGETS("ADX0-2"),
+ TX_WIDGETS("ADX0-3"),
+ /* index 0..24 above are used on Tegra114 */
+ WIDGETS("AMX1-0", amx10_tx),
+ WIDGETS("AMX1-1", amx11_tx),
+ WIDGETS("AMX1-2", amx12_tx),
+ WIDGETS("AMX1-3", amx13_tx),
+ WIDGETS("ADX1", adx1_tx),
+ TX_WIDGETS("AMX1"),
+ TX_WIDGETS("ADX1-0"),
+ TX_WIDGETS("ADX1-1"),
+ TX_WIDGETS("ADX1-2"),
+ TX_WIDGETS("ADX1-3"),
+ /* index 0..34 above are used on Tegra124 */
+};
+
+/* These routes used on Tegra30, Tegra114, Tegra124 */
+#define TEGRA30_ROUTES(name) \
+ { name " RX", NULL, name " Receive"}, \
+ { name " Transmit", NULL, name " TX"}, \
+ { name " TX", NULL, name " Mux" }, \
+ { name " Mux", "APBIF0", "APBIF0 RX" }, \
+ { name " Mux", "APBIF1", "APBIF1 RX" }, \
+ { name " Mux", "APBIF2", "APBIF2 RX" }, \
+ { name " Mux", "APBIF3", "APBIF3 RX" }, \
+ { name " Mux", "I2S0", "I2S0 RX" }, \
+ { name " Mux", "I2S1", "I2S1 RX" }, \
+ { name " Mux", "I2S2", "I2S2 RX" }, \
+ { name " Mux", "I2S3", "I2S3 RX" }, \
+ { name " Mux", "I2S4", "I2S4 RX" },
+
+/* These routes used on Tegra114 and Tegra124 */
+#define TEGRA114_ROUTES(name) \
+ { name " Mux", "APBIF4", "APBIF4 RX" }, \
+ { name " Mux", "APBIF5", "APBIF5 RX" }, \
+ { name " Mux", "APBIF6", "APBIF6 RX" }, \
+ { name " Mux", "APBIF7", "APBIF7 RX" }, \
+ { name " Mux", "APBIF8", "APBIF8 RX" }, \
+ { name " Mux", "APBIF9", "APBIF9 RX" }, \
+ { name " Mux", "AMX0", "AMX0 RX" }, \
+ { name " Mux", "ADX0-0", "ADX0-0 RX" }, \
+ { name " Mux", "ADX0-1", "ADX0-1 RX" }, \
+ { name " Mux", "ADX0-2", "ADX0-2 RX" }, \
+ { name " Mux", "ADX0-3", "ADX0-3 RX" },
+
+#define AMX_OUT_ADX_IN_ROUTES(name) \
+ { name " RX", NULL, name " Receive"}, \
+ { name " Transmit", NULL, name " TX"},
+
+/* These routes used on Tegra124 only */
+#define TEGRA124_ROUTES(name) \
+ { name " Mux", "AMX1", "AMX1 RX" }, \
+ { name " Mux", "ADX1-0", "ADX1-0 RX" }, \
+ { name " Mux", "ADX1-1", "ADX1-1 RX" }, \
+ { name " Mux", "ADX1-2", "ADX1-2 RX" }, \
+ { name " Mux", "ADX1-3", "ADX1-3 RX" },
+
+/*
+ * The number of entries in, and order of, this array is closely tied to the
+ * calculation of tegra30_xbar_codec.num_dapm_routes near the end of
+ * tegra30_xbar_probe()
+ */
+static const struct snd_soc_dapm_route tegra30_xbar_routes[] = {
+ TEGRA30_ROUTES("APBIF0")
+ TEGRA30_ROUTES("APBIF1")
+ TEGRA30_ROUTES("APBIF2")
+ TEGRA30_ROUTES("APBIF3")
+ TEGRA30_ROUTES("I2S0")
+ TEGRA30_ROUTES("I2S1")
+ TEGRA30_ROUTES("I2S2")
+ TEGRA30_ROUTES("I2S3")
+ TEGRA30_ROUTES("I2S4")
+ /* above routes are used on Tegra30 */
+ TEGRA30_ROUTES("APBIF4")
+ TEGRA30_ROUTES("APBIF5")
+ TEGRA30_ROUTES("APBIF6")
+ TEGRA30_ROUTES("APBIF7")
+ TEGRA30_ROUTES("APBIF8")
+ TEGRA30_ROUTES("APBIF9")
+ TEGRA30_ROUTES("AMX0-0")
+ TEGRA30_ROUTES("AMX0-1")
+ TEGRA30_ROUTES("AMX0-2")
+ TEGRA30_ROUTES("AMX0-3")
+ TEGRA30_ROUTES("ADX0")
+ TEGRA114_ROUTES("APBIF0")
+ TEGRA114_ROUTES("APBIF1")
+ TEGRA114_ROUTES("APBIF2")
+ TEGRA114_ROUTES("APBIF3")
+ TEGRA114_ROUTES("I2S0")
+ TEGRA114_ROUTES("I2S1")
+ TEGRA114_ROUTES("I2S2")
+ TEGRA114_ROUTES("I2S3")
+ TEGRA114_ROUTES("I2S4")
+ TEGRA114_ROUTES("APBIF4")
+ TEGRA114_ROUTES("APBIF5")
+ TEGRA114_ROUTES("APBIF6")
+ TEGRA114_ROUTES("APBIF7")
+ TEGRA114_ROUTES("APBIF8")
+ TEGRA114_ROUTES("APBIF9")
+ TEGRA114_ROUTES("AMX0-0")
+ TEGRA114_ROUTES("AMX0-1")
+ TEGRA114_ROUTES("AMX0-2")
+ TEGRA114_ROUTES("AMX0-3")
+ TEGRA114_ROUTES("ADX0")
+ AMX_OUT_ADX_IN_ROUTES("AMX0")
+ AMX_OUT_ADX_IN_ROUTES("ADX0-0")
+ AMX_OUT_ADX_IN_ROUTES("ADX0-1")
+ AMX_OUT_ADX_IN_ROUTES("ADX0-2")
+ AMX_OUT_ADX_IN_ROUTES("ADX0-3")
+ /* above routes are used on Tegra114 */
+ TEGRA30_ROUTES("AMX1-0")
+ TEGRA30_ROUTES("AMX1-1")
+ TEGRA30_ROUTES("AMX1-2")
+ TEGRA30_ROUTES("AMX1-3")
+ TEGRA30_ROUTES("ADX1")
+ TEGRA114_ROUTES("AMX1-0")
+ TEGRA114_ROUTES("AMX1-1")
+ TEGRA114_ROUTES("AMX1-2")
+ TEGRA114_ROUTES("AMX1-3")
+ TEGRA114_ROUTES("ADX1")
+ TEGRA124_ROUTES("APBIF0")
+ TEGRA124_ROUTES("APBIF1")
+ TEGRA124_ROUTES("APBIF2")
+ TEGRA124_ROUTES("APBIF3")
+ TEGRA124_ROUTES("I2S0")
+ TEGRA124_ROUTES("I2S1")
+ TEGRA124_ROUTES("I2S2")
+ TEGRA124_ROUTES("I2S3")
+ TEGRA124_ROUTES("I2S4")
+ TEGRA124_ROUTES("APBIF4")
+ TEGRA124_ROUTES("APBIF5")
+ TEGRA124_ROUTES("APBIF6")
+ TEGRA124_ROUTES("APBIF7")
+ TEGRA124_ROUTES("APBIF8")
+ TEGRA124_ROUTES("APBIF9")
+ TEGRA124_ROUTES("AMX0-0")
+ TEGRA124_ROUTES("AMX0-1")
+ TEGRA124_ROUTES("AMX0-2")
+ TEGRA124_ROUTES("AMX0-3")
+ TEGRA124_ROUTES("ADX0")
+ TEGRA124_ROUTES("AMX1-0")
+ TEGRA124_ROUTES("AMX1-1")
+ TEGRA124_ROUTES("AMX1-2")
+ TEGRA124_ROUTES("AMX1-3")
+ TEGRA124_ROUTES("ADX1")
+ AMX_OUT_ADX_IN_ROUTES("AMX1")
+ AMX_OUT_ADX_IN_ROUTES("ADX1-0")
+ AMX_OUT_ADX_IN_ROUTES("ADX1-1")
+ AMX_OUT_ADX_IN_ROUTES("ADX1-2")
+ AMX_OUT_ADX_IN_ROUTES("ADX1-3")
+ /* above routes are used on Tegra124 */
+};
+
+static struct snd_soc_codec_driver tegra30_xbar_codec = {
+ .probe = tegra30_xbar_codec_probe,
+ .dapm_widgets = tegra30_xbar_widgets,
+ .dapm_routes = tegra30_xbar_routes,
+};
+
+static const struct tegra30_xbar_soc_data soc_data_tegra30 = {
+ .regmap_config = &tegra30_xbar_regmap_config,
+ .num_dais = 9,
+ .num_mux_widgets = 9,
+ .num_mux0_input = 9,
+ .num_mux1_input = 0,
+ .mask[0] = 0x1ff,
+ .mask[1] = 0,
+};
+
+static const struct tegra30_xbar_soc_data soc_data_tegra114 = {
+ .regmap_config = &tegra30_xbar_regmap_config,
+ .num_dais = 25,
+ .num_mux_widgets = 20,
+ .num_mux0_input = 20,
+ .num_mux1_input = 0,
+ .mask[0] = 0x1ffffff,
+ .mask[1] = 0,
+};
+
+static const struct tegra30_xbar_soc_data soc_data_tegra124 = {
+ .regmap_config = &tegra124_xbar_regmap_config,
+ .num_dais = 35,
+ .num_mux_widgets = 25,
+ .num_mux0_input = 20,
+ .num_mux1_input = 5,
+ .mask[0] = 0x1ffffff,
+ .mask[1] = 0x7ff,
+};
+
+static const struct of_device_id tegra30_xbar_of_match[] = {
+ { .compatible = "nvidia,tegra30-ahub", .data = &soc_data_tegra30 },
+ { .compatible = "nvidia,tegra114-ahub", .data = &soc_data_tegra114 },
+ { .compatible = "nvidia,tegra124-ahub", .data = &soc_data_tegra124 },
+ {},
+};
+
+static int tegra30_xbar_probe(struct platform_device *pdev)
+{
+ struct tegra30_xbar *xbar;
+ void __iomem *regs;
+ int ret;
+ const struct of_device_id *match;
+ struct tegra30_xbar_soc_data *soc_data;
+ struct clk *parent_clk;
+
+ match = of_match_device(tegra30_xbar_of_match, pdev->dev.parent);
+ if (!match) {
+ dev_err(&pdev->dev, "Error: No device match found\n");
+ ret = -ENODEV;
+ goto err;
+ }
+ soc_data = (struct tegra30_xbar_soc_data *)match->data;
+
+ xbar = devm_kzalloc(&pdev->dev, sizeof(*xbar), GFP_KERNEL);
+ if (!xbar) {
+ dev_err(&pdev->dev, "Can't allocate xbar\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+ dev_set_drvdata(&pdev->dev, xbar);
+
+ xbar->soc_data = soc_data;
+
+ xbar->clk = devm_clk_get(&pdev->dev, "d_audio");
+ if (IS_ERR(xbar->clk)) {
+ dev_err(&pdev->dev, "Can't retrieve clock\n");
+ ret = PTR_ERR(xbar->clk);
+ goto err;
+ }
+
+ xbar->clk_parent = clk_get_sys(NULL, "pll_a_out0");
+ if (IS_ERR(xbar->clk)) {
+ dev_err(&pdev->dev, "Can't retrieve pll_a_out0 clock\n");
+ ret = PTR_ERR(xbar->clk_parent);
+ goto err_clk_put;
+ }
+
+ parent_clk = clk_get_parent(xbar->clk);
+ if (IS_ERR(parent_clk)) {
+ dev_err(&pdev->dev, "Can't get parent clock fo xbar\n");
+ ret = PTR_ERR(parent_clk);
+ goto err_clk_put;
+ }
+
+ ret = clk_set_parent(xbar->clk, xbar->clk_parent);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to set parent clock with pll_a_out0\n");
+ goto err_clk_put;
+ }
+
+ regs = devm_request_and_ioremap(&pdev->dev, pdev->resource);
+ if (!regs) {
+ dev_err(&pdev->dev, "request/iomap region failed\n");
+ ret = -ENODEV;
+ goto err_clk_set_parent;
+ }
+
+ xbar->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
+ soc_data->regmap_config);
+ if (IS_ERR(xbar->regmap)) {
+ dev_err(&pdev->dev, "regmap init failed\n");
+ ret = PTR_ERR(xbar->regmap);
+ goto err_clk_put_parent;
+ }
+ regcache_cache_only(xbar->regmap, true);
+
+ pm_runtime_enable(&pdev->dev);
+ if (!pm_runtime_enabled(&pdev->dev)) {
+ ret = tegra30_xbar_runtime_resume(&pdev->dev);
+ if (ret)
+ goto err_pm_disable;
+ }
+
+ tegra30_xbar_codec.num_dapm_widgets = (soc_data->num_dais * 2) +
+ soc_data->num_mux_widgets;
+
+ tegra30_xbar_codec.num_dapm_routes = (soc_data->num_dais * 2) +
+ (soc_data->num_mux_widgets *
+ (soc_data->num_mux0_input +
+ soc_data->num_mux1_input + 1));
+
+ ret = snd_soc_register_codec(&pdev->dev, &tegra30_xbar_codec,
+ tegra30_xbar_dais, soc_data->num_dais);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Could not register CODEC: %d\n", ret);
+ goto err_suspend;
+ }
+
+ return 0;
+
+err_suspend:
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ tegra30_xbar_runtime_suspend(&pdev->dev);
+err_pm_disable:
+ pm_runtime_disable(&pdev->dev);
+err_clk_put_parent:
+ clk_put(xbar->clk_parent);
+err_clk_set_parent:
+ clk_set_parent(xbar->clk, parent_clk);
+err_clk_put:
+ devm_clk_put(&pdev->dev, xbar->clk);
+err:
+ return ret;
+}
+
+static int tegra30_xbar_remove(struct platform_device *pdev)
+{
+ struct tegra30_xbar *xbar = dev_get_drvdata(&pdev->dev);
+
+ snd_soc_unregister_codec(&pdev->dev);
+
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ tegra30_xbar_runtime_suspend(&pdev->dev);
+
+ devm_clk_put(&pdev->dev, xbar->clk);
+ clk_put(xbar->clk_parent);
+
+ return 0;
+}
+
+static const struct dev_pm_ops tegra30_xbar_pm_ops = {
+ SET_RUNTIME_PM_OPS(tegra30_xbar_runtime_suspend,
+ tegra30_xbar_runtime_resume, NULL)
+};
+
+static struct platform_driver tegra30_xbar_driver = {
+ .probe = tegra30_xbar_probe,
+ .remove = tegra30_xbar_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = tegra30_xbar_of_match,
+ .pm = &tegra30_xbar_pm_ops,
+ },
+};
+module_platform_driver(tegra30_xbar_driver);
+
+void tegra30_xbar_set_cif(struct regmap *regmap, unsigned int reg,
+ struct tegra30_xbar_cif_conf *conf)
+{
+ unsigned int value;
+
+ value = (conf->threshold <<
+ TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) |
+ ((conf->audio_channels - 1) <<
+ TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) |
+ ((conf->client_channels - 1) <<
+ TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) |
+ (conf->audio_bits <<
+ TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) |
+ (conf->client_bits <<
+ TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT) |
+ (conf->expand <<
+ TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT) |
+ (conf->stereo_conv <<
+ TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT) |
+ (conf->replicate <<
+ TEGRA30_AUDIOCIF_CTRL_REPLICATE_SHIFT) |
+ (conf->direction <<
+ TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT) |
+ (conf->truncate <<
+ TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT) |
+ (conf->mono_conv <<
+ TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT);
+
+ regmap_write(regmap, reg, value);
+}
+EXPORT_SYMBOL_GPL(tegra30_xbar_set_cif);
+
+void tegra124_xbar_set_cif(struct regmap *regmap, unsigned int reg,
+ struct tegra30_xbar_cif_conf *conf)
+{
+ unsigned int value;
+
+ value = (conf->threshold <<
+ TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) |
+ ((conf->audio_channels - 1) <<
+ TEGRA124_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) |
+ ((conf->client_channels - 1) <<
+ TEGRA124_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) |
+ (conf->audio_bits <<
+ TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) |
+ (conf->client_bits <<
+ TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT) |
+ (conf->expand <<
+ TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT) |
+ (conf->stereo_conv <<
+ TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT) |
+ (conf->replicate <<
+ TEGRA30_AUDIOCIF_CTRL_REPLICATE_SHIFT) |
+ (conf->direction <<
+ TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT) |
+ (conf->truncate <<
+ TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT) |
+ (conf->mono_conv <<
+ TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT);
+
+ regmap_write(regmap, reg, value);
+}
+EXPORT_SYMBOL_GPL(tegra124_xbar_set_cif);
+
+MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_DESCRIPTION("Tegra30 XBAR driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/tegra-alt/tegra30_xbar_alt.h b/sound/soc/tegra-alt/tegra30_xbar_alt.h
new file mode 100644
index 000000000000..4f99e08884b4
--- /dev/null
+++ b/sound/soc/tegra-alt/tegra30_xbar_alt.h
@@ -0,0 +1,171 @@
+/*
+ * tegra30_xbar_alt.h - TEGRA XBAR registers
+ *
+ * Copyright (c) 2011-2013 NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __TEGRA_XBAR_ALT_H__
+#define __TEGRA_XBAR_ALT_H__
+
+#define TEGRA_AHUB_AUDIO_RX 0x0
+#define TEGRA_AHUB_AUDIO_RX1 0x200
+#define TEGRA_AHUB_AUDIO_RX_STRIDE 0x4
+#define TEGRA_AHUB_AUDIO_RX_COUNT 168
+/* This register repeats twice for each XBAR TX CIF */
+/* The fields in this register are 1 bit per XBAR RX CIF */
+
+/* Fields in *_CIF_RX/TX_CTRL; used by AHUB FIFOs, and all other audio modules */
+
+#define TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT 28
+#define TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK_US 0xf
+#define TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK (TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK_US << TEGRA30_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT)
+
+#define TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT 24
+#define TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK_US 0x3f
+#define TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK (TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_MASK_US << TEGRA124_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT)
+
+/* Channel count minus 1 */
+#define TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT 24
+#define TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK_US 7
+#define TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK (TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK_US << TEGRA30_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT)
+
+/* Channel count minus 1 */
+#define TEGRA124_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT 20
+#define TEGRA124_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK_US 0xf
+#define TEGRA124_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK (TEGRA124_AUDIOCIF_CTRL_AUDIO_CHANNELS_MASK_US << TEGRA124_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT)
+
+/* Channel count minus 1 */
+#define TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT 16
+#define TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_MASK_US 7
+#define TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_MASK (TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_MASK_US << TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT)
+
+/* Channel count minus 1 */
+#define TEGRA124_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT 16
+#define TEGRA124_AUDIOCIF_CTRL_CLIENT_CHANNELS_MASK_US 0xf
+#define TEGRA124_AUDIOCIF_CTRL_CLIENT_CHANNELS_MASK (TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_MASK_US << TEGRA30_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT)
+
+#define TEGRA30_AUDIOCIF_BITS_4 0
+#define TEGRA30_AUDIOCIF_BITS_8 1
+#define TEGRA30_AUDIOCIF_BITS_12 2
+#define TEGRA30_AUDIOCIF_BITS_16 3
+#define TEGRA30_AUDIOCIF_BITS_20 4
+#define TEGRA30_AUDIOCIF_BITS_24 5
+#define TEGRA30_AUDIOCIF_BITS_28 6
+#define TEGRA30_AUDIOCIF_BITS_32 7
+
+#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT 12
+#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_MASK (7 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_4 (TEGRA30_AUDIOCIF_BITS_4 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_8 (TEGRA30_AUDIOCIF_BITS_8 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_12 (TEGRA30_AUDIOCIF_BITS_12 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_16 (TEGRA30_AUDIOCIF_BITS_16 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_20 (TEGRA30_AUDIOCIF_BITS_20 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_24 (TEGRA30_AUDIOCIF_BITS_24 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_28 (TEGRA30_AUDIOCIF_BITS_28 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_32 (TEGRA30_AUDIOCIF_BITS_32 << TEGRA30_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT)
+
+#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT 8
+#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_MASK (7 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_4 (TEGRA30_AUDIOCIF_BITS_4 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_8 (TEGRA30_AUDIOCIF_BITS_8 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_12 (TEGRA30_AUDIOCIF_BITS_12 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_16 (TEGRA30_AUDIOCIF_BITS_16 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_20 (TEGRA30_AUDIOCIF_BITS_20 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_24 (TEGRA30_AUDIOCIF_BITS_24 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_28 (TEGRA30_AUDIOCIF_BITS_28 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_32 (TEGRA30_AUDIOCIF_BITS_32 << TEGRA30_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT)
+
+#define TEGRA30_AUDIOCIF_EXPAND_ZERO 0
+#define TEGRA30_AUDIOCIF_EXPAND_ONE 1
+#define TEGRA30_AUDIOCIF_EXPAND_LFSR 2
+
+#define TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT 6
+#define TEGRA30_AUDIOCIF_CTRL_EXPAND_MASK (3 << TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_EXPAND_ZERO (TEGRA30_AUDIOCIF_EXPAND_ZERO << TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_EXPAND_ONE (TEGRA30_AUDIOCIF_EXPAND_ONE << TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_EXPAND_LFSR (TEGRA30_AUDIOCIF_EXPAND_LFSR << TEGRA30_AUDIOCIF_CTRL_EXPAND_SHIFT)
+
+#define TEGRA30_AUDIOCIF_STEREO_CONV_CH0 0
+#define TEGRA30_AUDIOCIF_STEREO_CONV_CH1 1
+#define TEGRA30_AUDIOCIF_STEREO_CONV_AVG 2
+
+#define TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT 4
+#define TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_MASK (3 << TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_CH0 (TEGRA30_AUDIOCIF_STEREO_CONV_CH0 << TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_CH1 (TEGRA30_AUDIOCIF_STEREO_CONV_CH1 << TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_AVG (TEGRA30_AUDIOCIF_STEREO_CONV_AVG << TEGRA30_AUDIOCIF_CTRL_STEREO_CONV_SHIFT)
+
+#define TEGRA30_AUDIOCIF_CTRL_REPLICATE_SHIFT 3
+
+#define TEGRA30_AUDIOCIF_DIRECTION_TX 0
+#define TEGRA30_AUDIOCIF_DIRECTION_RX 1
+
+#define TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT 2
+#define TEGRA30_AUDIOCIF_CTRL_DIRECTION_MASK (1 << TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_DIRECTION_TX (TEGRA30_AUDIOCIF_DIRECTION_TX << TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_DIRECTION_RX (TEGRA30_AUDIOCIF_DIRECTION_RX << TEGRA30_AUDIOCIF_CTRL_DIRECTION_SHIFT)
+
+#define TEGRA30_AUDIOCIF_TRUNCATE_ROUND 0
+#define TEGRA30_AUDIOCIF_TRUNCATE_CHOP 1
+
+#define TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT 1
+#define TEGRA30_AUDIOCIF_CTRL_TRUNCATE_MASK (1 << TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_TRUNCATE_ROUND (TEGRA30_AUDIOCIF_TRUNCATE_ROUND << TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_TRUNCATE_CHOP (TEGRA30_AUDIOCIF_TRUNCATE_CHOP << TEGRA30_AUDIOCIF_CTRL_TRUNCATE_SHIFT)
+
+#define TEGRA30_AUDIOCIF_MONO_CONV_ZERO 0
+#define TEGRA30_AUDIOCIF_MONO_CONV_COPY 1
+
+#define TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT 0
+#define TEGRA30_AUDIOCIF_CTRL_MONO_CONV_MASK (1 << TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_MONO_CONV_ZERO (TEGRA30_AUDIOCIF_MONO_CONV_ZERO << TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT)
+#define TEGRA30_AUDIOCIF_CTRL_MONO_CONV_COPY (TEGRA30_AUDIOCIF_MONO_CONV_COPY << TEGRA30_AUDIOCIF_CTRL_MONO_CONV_SHIFT)
+
+struct tegra30_xbar_cif_conf {
+ unsigned int threshold;
+ unsigned int audio_channels;
+ unsigned int client_channels;
+ unsigned int audio_bits;
+ unsigned int client_bits;
+ unsigned int expand;
+ unsigned int stereo_conv;
+ unsigned int replicate;
+ unsigned int direction;
+ unsigned int truncate;
+ unsigned int mono_conv;
+};
+
+void tegra30_xbar_set_cif(struct regmap *regmap, unsigned int reg,
+ struct tegra30_xbar_cif_conf *conf);
+void tegra124_xbar_set_cif(struct regmap *regmap, unsigned int reg,
+ struct tegra30_xbar_cif_conf *conf);
+
+struct tegra30_xbar_soc_data {
+ const struct regmap_config *regmap_config;
+ unsigned int num_dais;
+ unsigned int num_mux_widgets;
+ unsigned int num_mux0_input;
+ unsigned int num_mux1_input;
+ unsigned int mask[2];
+};
+
+struct tegra30_xbar {
+ struct clk *clk;
+ struct clk *clk_parent;
+ struct regmap *regmap;
+ const struct tegra30_xbar_soc_data *soc_data;
+};
+
+#endif
diff --git a/sound/soc/tegra-alt/tegra_asoc_utils_alt.c b/sound/soc/tegra-alt/tegra_asoc_utils_alt.c
new file mode 100644
index 000000000000..08ce9e3ff77a
--- /dev/null
+++ b/sound/soc/tegra-alt/tegra_asoc_utils_alt.c
@@ -0,0 +1,441 @@
+/*
+ * tegra_asoc_utils_alt.c - MCLK and DAP Utility driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (c) 2010-2013 NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+
+#include <mach/clk.h>
+#include <mach/pinmux.h>
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+#include <mach/pinmux-tegra20.h>
+#endif
+#ifdef CONFIG_ARCH_TEGRA_3x_SOC
+#include <mach/pinmux-tegra30.h>
+#endif
+#ifdef CONFIG_ARCH_TEGRA_11x_SOC
+#include <mach/pinmux-t11.h>
+#endif
+#ifdef CONFIG_ARCH_TEGRA_12x_SOC
+#include <mach/pinmux-t12.h>
+#endif
+#ifdef CONFIG_ARCH_TEGRA_14x_SOC
+#include <mach/pinmux-t14.h>
+#endif
+
+#include <sound/soc.h>
+
+#include "tegra_asoc_utils_alt.h"
+
+static atomic_t dap_ref_count[5];
+
+#define TRISTATE_DAP_PORT(n) \
+static void tristate_dap_##n(bool tristate) \
+{ \
+ enum tegra_pingroup fs, sclk, din, dout; \
+ fs = TEGRA_PINGROUP_DAP##n##_FS; \
+ sclk = TEGRA_PINGROUP_DAP##n##_SCLK; \
+ din = TEGRA_PINGROUP_DAP##n##_DIN; \
+ dout = TEGRA_PINGROUP_DAP##n##_DOUT; \
+ if (tristate) { \
+ if (atomic_dec_return(&dap_ref_count[n-1]) == 0) {\
+ tegra_pinmux_set_tristate(fs, TEGRA_TRI_TRISTATE); \
+ tegra_pinmux_set_tristate(sclk, TEGRA_TRI_TRISTATE); \
+ tegra_pinmux_set_tristate(din, TEGRA_TRI_TRISTATE); \
+ tegra_pinmux_set_tristate(dout, TEGRA_TRI_TRISTATE); \
+ } \
+ } else { \
+ if (atomic_inc_return(&dap_ref_count[n-1]) == 1) {\
+ tegra_pinmux_set_tristate(fs, TEGRA_TRI_NORMAL); \
+ tegra_pinmux_set_tristate(sclk, TEGRA_TRI_NORMAL); \
+ tegra_pinmux_set_tristate(din, TEGRA_TRI_NORMAL); \
+ tegra_pinmux_set_tristate(dout, TEGRA_TRI_NORMAL); \
+ } \
+ } \
+}
+
+TRISTATE_DAP_PORT(1)
+TRISTATE_DAP_PORT(2)
+/*I2S2 and I2S3 for other chips do not map to DAP3 and DAP4 (also
+these pinmux dont exist for other chips), they map to some
+other pinmux*/
+#if defined(CONFIG_ARCH_TEGRA_11x_SOC)\
+ || defined(CONFIG_ARCH_TEGRA_12x_SOC)\
+ || defined(CONFIG_ARCH_TEGRA_3x_SOC)
+ TRISTATE_DAP_PORT(3)
+ TRISTATE_DAP_PORT(4)
+#endif
+
+int tegra_alt_asoc_utils_tristate_dap(int id, bool tristate)
+{
+ switch (id) {
+ case 0:
+ tristate_dap_1(tristate);
+ break;
+ case 1:
+ tristate_dap_2(tristate);
+ break;
+/*I2S2 and I2S3 for other chips do not map to DAP3 and DAP4 (also
+these pinmux dont exist for other chips), they map to some
+other pinmux*/
+#if defined(CONFIG_ARCH_TEGRA_11x_SOC)\
+ || defined(CONFIG_ARCH_TEGRA_12x_SOC)\
+ || defined(CONFIG_ARCH_TEGRA_3x_SOC)
+ case 2:
+ tristate_dap_3(tristate);
+ break;
+ case 3:
+ tristate_dap_4(tristate);
+ break;
+#endif
+ default:
+ pr_warn("Invalid DAP port\n");
+ break;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tegra_alt_asoc_utils_tristate_dap);
+
+int tegra_alt_asoc_utils_set_rate(struct tegra_asoc_audio_clock_info *data,
+ int srate,
+ int mclk,
+ int clk_out_rate)
+{
+ int new_baseclock;
+ bool clk_change;
+ int err;
+
+ switch (srate) {
+ case 11025:
+ case 22050:
+ case 44100:
+ case 88200:
+ if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA20)
+ new_baseclock = 56448000;
+ else if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA30)
+ new_baseclock = 564480000;
+ else
+ new_baseclock = 282240000;
+ break;
+ case 8000:
+ case 16000:
+ case 32000:
+ case 48000:
+ case 64000:
+ case 96000:
+ if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA20)
+ new_baseclock = 73728000;
+ else if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA30)
+ new_baseclock = 552960000;
+ else
+ new_baseclock = 368640000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ clk_change = ((new_baseclock != data->set_baseclock) ||
+ (mclk != data->set_mclk));
+ if (!clk_change)
+ return 0;
+
+ /* Don't change rate if already one dai-link is using it */
+ if (data->lock_count)
+ return -EINVAL;
+
+ data->set_baseclock = 0;
+ data->set_mclk = 0;
+
+ if (data->clk_pll_a_state) {
+ clk_disable_unprepare(data->clk_pll_a);
+ data->clk_pll_a_state = 0;
+ }
+
+ if (data->clk_pll_a_out0_state) {
+ clk_disable_unprepare(data->clk_pll_a_out0);
+ data->clk_pll_a_out0_state = 0;
+ }
+
+ if (data->clk_cdev1_state) {
+ clk_disable_unprepare(data->clk_cdev1);
+ data->clk_cdev1_state = 0;
+ }
+
+ err = clk_set_rate(data->clk_pll_a, new_baseclock);
+ if (err) {
+ dev_err(data->dev, "Can't set pll_a rate: %d\n", err);
+ return err;
+ }
+
+ err = clk_set_rate(data->clk_pll_a_out0, mclk);
+ if (err) {
+ dev_err(data->dev, "Can't set clk_pll_a_out0 rate: %d\n", err);
+ return err;
+ }
+
+ err = clk_set_rate(data->clk_cdev1, clk_out_rate);
+ if (err) {
+ dev_err(data->dev, "Can't set clk_cdev1 rate: %d\n", err);
+ return err;
+ }
+
+ err = clk_prepare_enable(data->clk_pll_a);
+ if (err) {
+ dev_err(data->dev, "Can't enable pll_a: %d\n", err);
+ return err;
+ }
+ data->clk_pll_a_state = 1;
+
+ err = clk_prepare_enable(data->clk_pll_a_out0);
+ if (err) {
+ dev_err(data->dev, "Can't enable pll_a_out0: %d\n", err);
+ return err;
+ }
+ data->clk_pll_a_out0_state = 1;
+
+ err = clk_prepare_enable(data->clk_cdev1);
+ if (err) {
+ dev_err(data->dev, "Can't enable cdev1: %d\n", err);
+ return err;
+ }
+ data->clk_cdev1_state = 1;
+
+ data->set_baseclock = new_baseclock;
+ data->set_mclk = mclk;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tegra_alt_asoc_utils_set_rate);
+
+void tegra_alt_asoc_utils_lock_clk_rate(struct tegra_asoc_audio_clock_info *data,
+ int lock)
+{
+ if (lock)
+ data->lock_count++;
+ else if (data->lock_count)
+ data->lock_count--;
+}
+EXPORT_SYMBOL_GPL(tegra_alt_asoc_utils_lock_clk_rate);
+
+int tegra_alt_asoc_utils_clk_enable(struct tegra_asoc_audio_clock_info *data)
+{
+ int err;
+
+ err = clk_prepare_enable(data->clk_cdev1);
+ if (err) {
+ dev_err(data->dev, "Can't enable cdev1: %d\n", err);
+ return err;
+ }
+ data->clk_cdev1_state = 1;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tegra_alt_asoc_utils_clk_enable);
+
+int tegra_alt_asoc_utils_clk_disable(struct tegra_asoc_audio_clock_info *data)
+{
+ clk_disable_unprepare(data->clk_cdev1);
+ data->clk_cdev1_state = 0;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tegra_alt_asoc_utils_clk_disable);
+
+int tegra_alt_asoc_utils_init(struct tegra_asoc_audio_clock_info *data,
+ struct device *dev, struct snd_soc_card *card)
+{
+ int ret;
+
+ data->dev = dev;
+ data->card = card;
+
+ data->clk_pll_p_out1 = clk_get_sys(NULL, "pll_p_out1");
+ if (IS_ERR(data->clk_pll_p_out1)) {
+ dev_err(data->dev, "Can't retrieve clk pll_p_out1\n");
+ ret = PTR_ERR(data->clk_pll_p_out1);
+ goto err;
+ }
+
+ if (of_machine_is_compatible("nvidia,tegra20"))
+ data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA20;
+ else if (of_machine_is_compatible("nvidia,tegra30"))
+ data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA30;
+ else if (of_machine_is_compatible("nvidia,tegra114"))
+ data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA114;
+ else if (of_machine_is_compatible("nvidia,tegra148"))
+ data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA148;
+ else if (of_machine_is_compatible("nvidia,tegra124"))
+ data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA124;
+ else if (!dev->of_node) {
+ /* non-DT is always Tegra20 */
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+ data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA20;
+#elif defined(CONFIG_ARCH_TEGRA_3x_SOC)
+ data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA30;
+#elif defined(CONFIG_ARCH_TEGRA_11x_SOC)
+ data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA114;
+#elif defined(CONFIG_ARCH_TEGRA_14x_SOC)
+ data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA148;
+#elif defined(CONFIG_ARCH_TEGRA_12x_SOC)
+ data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA124;
+#endif
+ } else
+ /* DT boot, but unknown SoC */
+ return -EINVAL;
+
+ data->clk_pll_a = clk_get_sys(NULL, "pll_a");
+ if (IS_ERR(data->clk_pll_a)) {
+ dev_err(data->dev, "Can't retrieve clk pll_a\n");
+ ret = PTR_ERR(data->clk_pll_a);
+ goto err_put_pll_p_out1;
+ }
+
+ data->clk_pll_a_out0 = clk_get_sys(NULL, "pll_a_out0");
+ if (IS_ERR(data->clk_pll_a_out0)) {
+ dev_err(data->dev, "Can't retrieve clk pll_a_out0\n");
+ ret = PTR_ERR(data->clk_pll_a_out0);
+ goto err_put_pll_a;
+ }
+
+ data->clk_m = clk_get_sys(NULL, "clk_m");
+ if (IS_ERR(data->clk_m)) {
+ dev_err(data->dev, "Can't retrieve clk clk_m\n");
+ ret = PTR_ERR(data->clk_m);
+ goto err;
+ }
+
+ if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA20)
+ data->clk_cdev1 = clk_get_sys(NULL, "cdev1");
+ else
+ data->clk_cdev1 = clk_get_sys("extern1", NULL);
+
+ if (IS_ERR(data->clk_cdev1)) {
+ dev_err(data->dev, "Can't retrieve clk cdev1\n");
+ ret = PTR_ERR(data->clk_cdev1);
+ goto err_put_pll_a_out0;
+ }
+
+ if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA20)
+ data->clk_out1 = ERR_PTR(-ENOENT);
+ else {
+ data->clk_out1 = clk_get_sys("clk_out_1", "extern1");
+ if (IS_ERR(data->clk_out1)) {
+ dev_err(data->dev, "Can't retrieve clk out1\n");
+ ret = PTR_ERR(data->clk_out1);
+ goto err_put_cdev1;
+ }
+ }
+
+ ret = clk_prepare_enable(data->clk_cdev1);
+ if (ret) {
+ dev_err(data->dev, "Can't enable clk cdev1/extern1");
+ goto err_put_out1;
+ }
+ data->clk_cdev1_state = 1;
+
+ if (!IS_ERR(data->clk_out1)) {
+ ret = clk_prepare_enable(data->clk_out1);
+ if (ret) {
+ dev_err(data->dev, "Can't enable clk out1");
+ goto err_put_out1;
+ }
+ }
+
+ ret = tegra_alt_asoc_utils_set_rate(data, 48000, 256 * 48000, 256 * 48000);
+ if (ret)
+ goto err_put_out1;
+
+ return 0;
+
+err_put_out1:
+ if (!IS_ERR(data->clk_out1))
+ clk_put(data->clk_out1);
+err_put_cdev1:
+ clk_put(data->clk_cdev1);
+err_put_pll_a_out0:
+ clk_put(data->clk_pll_a_out0);
+err_put_pll_a:
+ clk_put(data->clk_pll_a);
+err_put_pll_p_out1:
+ clk_put(data->clk_pll_p_out1);
+err:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tegra_alt_asoc_utils_init);
+
+int tegra_alt_asoc_utils_set_parent(struct tegra_asoc_audio_clock_info *data,
+ int is_i2s_master)
+{
+ int ret = -ENODEV;
+
+ if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA20)
+ return ret;
+
+ if (is_i2s_master) {
+ ret = clk_set_parent(data->clk_cdev1, data->clk_pll_a_out0);
+ if (ret) {
+ dev_err(data->dev, "Can't set clk cdev1/extern1 parent");
+ return ret;
+ }
+ } else {
+ ret = clk_set_parent(data->clk_cdev1, data->clk_m);
+ if (ret) {
+ dev_err(data->dev, "Can't set clk cdev1/extern1 parent");
+ return ret;
+ }
+
+ ret = clk_set_rate(data->clk_cdev1, 13000000);
+ if (ret) {
+ dev_err(data->dev, "Can't set clk rate");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tegra_alt_asoc_utils_set_parent);
+
+void tegra_alt_asoc_utils_fini(struct tegra_asoc_audio_clock_info *data)
+{
+ if (data->clk_cdev1_state)
+ clk_disable(data->clk_cdev1);
+
+ if (!IS_ERR(data->clk_out1))
+ clk_put(data->clk_out1);
+
+ if (!IS_ERR(data->clk_pll_a_out0))
+ clk_put(data->clk_pll_a_out0);
+
+ if (!IS_ERR(data->clk_pll_a))
+ clk_put(data->clk_pll_a);
+
+ if (!IS_ERR(data->clk_pll_p_out1))
+ clk_put(data->clk_pll_p_out1);
+}
+EXPORT_SYMBOL_GPL(tegra_alt_asoc_utils_fini);
+
+MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_DESCRIPTION("Tegra ASoC utility code");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/tegra-alt/tegra_asoc_utils_alt.h b/sound/soc/tegra-alt/tegra_asoc_utils_alt.h
new file mode 100644
index 000000000000..877f9b808873
--- /dev/null
+++ b/sound/soc/tegra-alt/tegra_asoc_utils_alt.h
@@ -0,0 +1,75 @@
+/*
+ * tegra_alt_asoc_utils.h - Definitions for MCLK and DAP Utility driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (c) 2011-2013 NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __TEGRA_ASOC_UTILS_ALT_H__
+#define __TEGRA_ASOC_UTILS_ALT_H_
+
+struct clk;
+struct device;
+
+enum tegra_asoc_utils_soc {
+ TEGRA_ASOC_UTILS_SOC_TEGRA20,
+ TEGRA_ASOC_UTILS_SOC_TEGRA30,
+ TEGRA_ASOC_UTILS_SOC_TEGRA114,
+ TEGRA_ASOC_UTILS_SOC_TEGRA148,
+ TEGRA_ASOC_UTILS_SOC_TEGRA124,
+};
+
+struct tegra_asoc_audio_clock_info {
+ struct device *dev;
+ struct snd_soc_card *card;
+ enum tegra_asoc_utils_soc soc;
+ struct clk *clk_pll_a;
+ int clk_pll_a_state;
+ struct clk *clk_pll_a_out0;
+ int clk_pll_a_out0_state;
+ struct clk *clk_cdev1;
+ int clk_cdev1_state;
+ struct clk *clk_out1;
+ struct clk *clk_m;
+ int clk_m_state;
+ struct clk *clk_pll_p_out1;
+ int set_mclk;
+ int lock_count;
+ int set_baseclock;
+};
+
+int tegra_alt_asoc_utils_set_rate(struct tegra_asoc_audio_clock_info *data,
+ int srate,
+ int mclk,
+ int clk_out_rate);
+void tegra_alt_asoc_utils_lock_clk_rate(
+ struct tegra_asoc_audio_clock_info *data,
+ int lock);
+int tegra_alt_asoc_utils_init(struct tegra_asoc_audio_clock_info *data,
+ struct device *dev, struct snd_soc_card *card);
+void tegra_alt_asoc_utils_fini(struct tegra_asoc_audio_clock_info *data);
+
+int tegra_alt_asoc_utils_set_parent(struct tegra_asoc_audio_clock_info *data,
+ int is_i2s_master);
+int tegra_alt_asoc_utils_clk_enable(struct tegra_asoc_audio_clock_info *data);
+int tegra_alt_asoc_utils_clk_disable(struct tegra_asoc_audio_clock_info *data);
+int tegra_alt_asoc_utils_register_ctls(struct tegra_asoc_audio_clock_info *data);
+
+int tegra_alt_asoc_utils_tristate_dap(int id, bool tristate);
+
+#endif
diff --git a/sound/soc/tegra-alt/tegra_pcm_alt.c b/sound/soc/tegra-alt/tegra_pcm_alt.c
new file mode 100644
index 000000000000..b66881ebcb64
--- /dev/null
+++ b/sound/soc/tegra-alt/tegra_pcm_alt.c
@@ -0,0 +1,282 @@
+/*
+ * tegra_alt_pcm.c - Tegra PCM driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (c) 2011-2013 NVIDIA CORPORATION. All rights reserved.
+ *
+ * Based on code copyright/by:
+ *
+ * Copyright (c) 2009-2010, NVIDIA Corporation.
+ * Scott Peterson <speterson@nvidia.com>
+ * Vijay Mali <vmali@nvidia.com>
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Iliyan Malchev <malchev@google.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+
+#include "tegra_pcm_alt.h"
+
+static const struct snd_pcm_hardware tegra_alt_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_PAUSE |
+ SNDRV_PCM_INFO_RESUME |
+ SNDRV_PCM_INFO_INTERLEAVED,
+ .formats = SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S20_3LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .period_bytes_min = 128,
+ .period_bytes_max = PAGE_SIZE * 2,
+ .periods_min = 1,
+ .periods_max = 8,
+ .buffer_bytes_max = PAGE_SIZE * 8,
+ .fifo_size = 4,
+};
+
+static int tegra_alt_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct device *dev = rtd->platform->dev;
+ struct tegra_alt_pcm_runtime_data *prtd;
+ int ret;
+
+ prtd = kzalloc(sizeof(struct tegra_alt_pcm_runtime_data), GFP_KERNEL);
+ if (prtd == NULL)
+ return -ENOMEM;
+
+ /* Set HW params now that initialization is complete */
+ snd_soc_set_runtime_hwparams(substream, &tegra_alt_pcm_hardware);
+
+ /* Ensure period size is multiple of 8 */
+ ret = snd_pcm_hw_constraint_step(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 0x8);
+ if (ret) {
+ dev_err(dev, "failed to set constraint %d\n", ret);
+ kfree(prtd);
+ return ret;
+ }
+
+ ret = snd_dmaengine_pcm_open_request_chan(substream, NULL, NULL);
+ if (ret) {
+ dev_err(dev, "dmaengine pcm open failed with err %d\n", ret);
+ kfree(prtd);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tegra_alt_pcm_close(struct snd_pcm_substream *substream)
+{
+ snd_dmaengine_pcm_close_release_chan(substream);
+ return 0;
+}
+
+int tegra_alt_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct device *dev = rtd->platform->dev;
+ struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
+ struct tegra_alt_pcm_dma_params *dmap;
+ struct dma_slave_config slave_config;
+ int ret;
+
+ dmap = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+
+ ret = snd_hwparams_to_dma_slave_config(substream, params,
+ &slave_config);
+ if (ret) {
+ dev_err(dev, "hw params config failed with err %d\n", ret);
+ return ret;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ slave_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ slave_config.dst_addr = dmap->addr;
+ slave_config.dst_maxburst = 4;
+ } else {
+ slave_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ slave_config.src_addr = dmap->addr;
+ slave_config.src_maxburst = 4;
+ }
+ slave_config.slave_id = dmap->req_sel;
+
+ ret = dmaengine_slave_config(chan, &slave_config);
+ if (ret < 0) {
+ dev_err(dev, "dma slave config failed with err %d\n", ret);
+ return ret;
+ }
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ return 0;
+}
+
+int tegra_alt_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ snd_pcm_set_runtime_buffer(substream, NULL);
+ return 0;
+}
+
+static int tegra_alt_pcm_mmap(struct snd_pcm_substream *substream,
+ struct vm_area_struct *vma)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ return dma_mmap_writecombine(substream->pcm->card->dev, vma,
+ runtime->dma_area,
+ runtime->dma_addr,
+ runtime->dma_bytes);
+}
+
+static struct snd_pcm_ops tegra_alt_pcm_ops = {
+ .open = tegra_alt_pcm_open,
+ .close = tegra_alt_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = tegra_alt_pcm_hw_params,
+ .hw_free = tegra_alt_pcm_hw_free,
+ .trigger = snd_dmaengine_pcm_trigger,
+ .pointer = snd_dmaengine_pcm_pointer,
+ .mmap = tegra_alt_pcm_mmap,
+};
+
+static int tegra_alt_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
+ int stream , size_t size)
+{
+ struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
+ buf->area = dma_alloc_writecombine(pcm->card->dev, size,
+ &buf->addr, GFP_KERNEL);
+ if (!buf->area)
+ return -ENOMEM;
+ buf->private_data = NULL;
+ buf->dev.type = SNDRV_DMA_TYPE_DEV;
+ buf->dev.dev = pcm->card->dev;
+ buf->bytes = size;
+
+ return 0;
+}
+
+void tegra_alt_pcm_deallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_dma_buffer *buf;
+
+ substream = pcm->streams[stream].substream;
+ if (!substream)
+ return;
+
+ buf = &substream->dma_buffer;
+ if (!buf->area)
+ return;
+
+ dma_free_writecombine(pcm->card->dev, buf->bytes,
+ buf->area, buf->addr);
+ buf->area = NULL;
+}
+
+static u64 tegra_dma_mask = DMA_BIT_MASK(32);
+
+int tegra_alt_pcm_dma_allocate(struct snd_soc_pcm_runtime *rtd, size_t size)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_pcm *pcm = rtd->pcm;
+ int ret = 0;
+
+ if (!card->dev->dma_mask)
+ card->dev->dma_mask = &tegra_dma_mask;
+ if (!card->dev->coherent_dma_mask)
+ card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+ if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
+ ret = tegra_alt_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_PLAYBACK,
+ size);
+ if (ret)
+ goto err;
+ }
+
+ if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
+ ret = tegra_alt_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_CAPTURE,
+ size);
+ if (ret)
+ goto err_free_play;
+ }
+
+ return 0;
+
+err_free_play:
+ tegra_alt_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK);
+err:
+ return ret;
+}
+
+int tegra_alt_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ return tegra_alt_pcm_dma_allocate(rtd,
+ tegra_alt_pcm_hardware.buffer_bytes_max);
+}
+
+void tegra_alt_pcm_free(struct snd_pcm *pcm)
+{
+ tegra_alt_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_CAPTURE);
+ tegra_alt_pcm_deallocate_dma_buffer(pcm, SNDRV_PCM_STREAM_PLAYBACK);
+}
+
+static int tegra_alt_pcm_probe(struct snd_soc_platform *platform)
+{
+ platform->dapm.idle_bias_off = 1;
+ return 0;
+}
+
+static struct snd_soc_platform_driver tegra_alt_pcm_platform = {
+ .ops = &tegra_alt_pcm_ops,
+ .pcm_new = tegra_alt_pcm_new,
+ .pcm_free = tegra_alt_pcm_free,
+ .probe = tegra_alt_pcm_probe,
+};
+
+int tegra_alt_pcm_platform_register(struct device *dev)
+{
+ return snd_soc_register_platform(dev, &tegra_alt_pcm_platform);
+}
+EXPORT_SYMBOL_GPL(tegra_alt_pcm_platform_register);
+
+void tegra_alt_pcm_platform_unregister(struct device *dev)
+{
+ snd_soc_unregister_platform(dev);
+}
+EXPORT_SYMBOL_GPL(tegra_alt_pcm_platform_unregister);
+
+MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
+MODULE_DESCRIPTION("Tegra Alt PCM ASoC driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/tegra-alt/tegra_pcm_alt.h b/sound/soc/tegra-alt/tegra_pcm_alt.h
new file mode 100644
index 000000000000..b6d4990e03be
--- /dev/null
+++ b/sound/soc/tegra-alt/tegra_pcm_alt.h
@@ -0,0 +1,52 @@
+/*
+ * tegra_pcm_alt.h - Definitions for Tegra PCM driver
+ *
+ * Author: Stephen Warren <swarren@nvidia.com>
+ * Copyright (c) 2011-2013 NVIDIA CORPORATION. All rights reserved.
+ *
+ * Based on code copyright/by:
+ *
+ * Copyright (c) 2009-2010, NVIDIA Corporation.
+ * Scott Peterson <speterson@nvidia.com>
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Iliyan Malchev <malchev@google.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __TEGRA_PCM_ALT_H__
+#define __TEGRA_PCM_ALT_H__
+
+#define MAX_DMA_REQ_COUNT 2
+
+struct tegra_alt_pcm_dma_params {
+ unsigned long addr;
+ unsigned long wrap;
+ unsigned long width;
+ unsigned long req_sel;
+};
+
+struct tegra_alt_pcm_runtime_data {
+ int running;
+ int disable_intr;
+ dma_addr_t avp_dma_addr;
+};
+
+int tegra_alt_pcm_platform_register(struct device *dev);
+void tegra_alt_pcm_platform_unregister(struct device *dev);
+
+#endif