summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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