diff --git a/sound/soc/tegra-alt/Kconfig b/sound/soc/tegra-alt/Kconfig
index f2a20096..fb47ff88 100644
--- a/sound/soc/tegra-alt/Kconfig
+++ b/sound/soc/tegra-alt/Kconfig
@@ -1,8 +1,8 @@
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
+ depends on ARCH_TEGRA
select REGMAP_MMIO
- select SND_SOC_DMAENGINE_PCM if TEGRA20_APB_DMA
+ select SND_SOC_DMAENGINE_PCM
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
@@ -18,18 +18,35 @@ config SND_SOC_TEGRA_ALT_114_OR_LATER
depends on SND_SOC_TEGRA_ALT
depends on ARCH_TEGRA_11x_SOC || ARCH_TEGRA_12x_SOC
+config SND_SOC_TEGRA_ALT_210
+ def_bool y
+ depends on SND_SOC_TEGRA_ALT
+ depends on ARCH_TEGRA_APE
+
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_TEGRA210_XBAR_ALT
+ tristate "Tegra210 XBAR driver"
+ depends on SND_SOC_TEGRA_ALT && SND_SOC_TEGRA_ALT_210
+ help
+ Say Y or M if you want to add support for Tegra210 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_TEGRA210_ADMAIF_ALT
+ tristate "Tegra210 ADMIF driver"
+ depends on SND_SOC_TEGRA_ALT && SND_SOC_TEGRA_ALT_210
+ help
+ Say Y or M if you want to add support for Tegra210 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
@@ -54,6 +71,54 @@ config SND_SOC_TEGRA114_ADX_ALT
help
Say Y or M if you want to add support for Tegra114 ADX module.
+config SND_SOC_TEGRA210_I2S_ALT
+ tristate "Tegra210 I2S driver"
+ depends on SND_SOC_TEGRA_ALT && SND_SOC_TEGRA_ALT_210
+ help
+ Say Y or M if you want to add support for Tegra210 I2S module.
+
+config SND_SOC_TEGRA210_AMX_ALT
+ tristate "Tegra210 AMX driver"
+ depends on SND_SOC_TEGRA_ALT && SND_SOC_TEGRA_ALT_210
+ help
+ Say Y or M if you want to add support for Tegra210 AMX module.
+
+config SND_SOC_TEGRA210_ADX_ALT
+ tristate "Tegra210 ADX driver"
+ depends on SND_SOC_TEGRA_ALT && SND_SOC_TEGRA_ALT_210
+ help
+ Say Y or M if you want to add support for Tegra210 ADX module.
+
+config SND_SOC_TEGRA210_MIXER_ALT
+ tristate "Tegra210 MIXER driver"
+ depends on SND_SOC_TEGRA_ALT && SND_SOC_TEGRA_ALT_210
+ help
+ Say Y or M if you want to add support for Tegra210 MIXER module.
+
+config SND_SOC_TEGRA210_SFC_ALT
+ tristate "Tegra210 SFC driver"
+ depends on SND_SOC_TEGRA_ALT && SND_SOC_TEGRA_ALT_210
+ help
+ Say Y or M if you want to add support for Tegra210 SFC module.
+
+config SND_SOC_TEGRA210_AFC_ALT
+ tristate "Tegra210 AFC driver"
+ depends on SND_SOC_TEGRA_ALT && SND_SOC_TEGRA_ALT_210
+ help
+ Say Y or M if you want to add support for Tegra210 AFC module.
+
+config SND_SOC_TEGRA210_MVC_ALT
+ tristate "Tegra210 MVC driver"
+ depends on SND_SOC_TEGRA_ALT && SND_SOC_TEGRA_ALT_210
+ help
+ Say Y or M if you want to add support for Tegra210 MVC module.
+
+config SND_SOC_TEGRA210_SPDIF_ALT
+ tristate "Tegra210 SPDIF driver"
+ depends on SND_SOC_TEGRA_ALT && SND_SOC_TEGRA_ALT_210
+ help
+ Say Y or M if you want to add support for Tegra210 SPDIF module.
+
config SND_SOC_TEGRA_VCM30T124_ALT
tristate "SoC Audio support for VCM30_T124"
depends on SND_SOC_TEGRA_ALT
diff --git a/sound/soc/tegra-alt/Makefile b/sound/soc/tegra-alt/Makefile
index 176b10cd..f4e1ead9 100644
--- a/sound/soc/tegra-alt/Makefile
+++ b/sound/soc/tegra-alt/Makefile
@@ -11,15 +11,36 @@ snd-soc-tegra30-alt-i2s-objs := tegra30_i2s_alt.o
snd-soc-tegra30-alt-spdif-objs := tegra30_spdif_alt.o
snd-soc-tegra114-alt-amx-objs := tegra114_amx_alt.o
snd-soc-tegra114-alt-adx-objs := tegra114_adx_alt.o
+snd-soc-tegra210-alt-admaif-objs := tegra210_admaif_alt.o
+snd-soc-tegra210-alt-xbar-objs := tegra210_xbar_alt.o
+snd-soc-tegra210-alt-i2s-objs := tegra210_i2s_alt.o
+snd-soc-tegra210-alt-amx-objs := tegra210_amx_alt.o
+snd-soc-tegra210-alt-adx-objs := tegra210_adx_alt.o
+snd-soc-tegra210-alt-mixer-objs := tegra210_mixer_alt.o
+snd-soc-tegra210-alt-sfc-objs := tegra210_sfc_alt.o
+snd-soc-tegra210-alt-afc-objs := tegra210_afc_alt.o
+snd-soc-tegra210-alt-mvc-objs := tegra210_mvc_alt.o
+snd-soc-tegra210-alt-spdif-objs := tegra210_spdif_alt.o
+snd-soc-tegra210-alt-fpga-clock-objs := ahub_unit_fpga_clock.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_TEGRA_ALT) += snd-soc-tegra-alt-pcm.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_TEGRA30_SPDIF_ALT) += snd-soc-tegra30-alt-spdif.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
+obj-$(CONFIG_SND_SOC_TEGRA210_ADMAIF_ALT) += snd-soc-tegra210-alt-admaif.o
+obj-$(CONFIG_SND_SOC_TEGRA210_XBAR_ALT) += snd-soc-tegra210-alt-xbar.o
+obj-$(CONFIG_SND_SOC_TEGRA210_I2S_ALT) += snd-soc-tegra210-alt-i2s.o
+obj-$(CONFIG_SND_SOC_TEGRA210_AMX_ALT) += snd-soc-tegra210-alt-amx.o
+obj-$(CONFIG_SND_SOC_TEGRA210_ADX_ALT) += snd-soc-tegra210-alt-adx.o
+obj-$(CONFIG_SND_SOC_TEGRA210_MIXER_ALT) += snd-soc-tegra210-alt-mixer.o
+obj-$(CONFIG_SND_SOC_TEGRA210_SFC_ALT) += snd-soc-tegra210-alt-sfc.o
+obj-$(CONFIG_SND_SOC_TEGRA210_AFC_ALT) += snd-soc-tegra210-alt-afc.o
+obj-$(CONFIG_SND_SOC_TEGRA210_MVC_ALT) += snd-soc-tegra210-alt-mvc.o
+obj-$(CONFIG_SND_SOC_TEGRA210_SPDIF_ALT) += snd-soc-tegra210-alt-spdif.o
# Tegra machine Support
snd-soc-tegra-alt-vcm30t124-objs := tegra_vcm30t124_alt.o
diff --git a/sound/soc/tegra-alt/tegra210_admaif_alt.c b/sound/soc/tegra-alt/tegra210_admaif_alt.c
new file mode 100644
index 00000000..d343ae5f
--- /dev/null
+++ b/sound/soc/tegra-alt/tegra210_admaif_alt.c
@@ -0,0 +1,604 @@
+/*
+ * tegra210_admaif_alt.c - Tegra ADMAIF driver
+ *
+ * Copyright (c) 2014 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 .
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "tegra_pcm_alt.h"
+#include "tegra210_xbar_alt.h"
+#include "tegra210_admaif_alt.h"
+
+#define DRV_NAME "tegra210-ape-admaif"
+
+static bool tegra210_admaif_wr_reg(struct device *dev, unsigned int reg)
+{
+ reg = reg % TEGRA210_ADMAIF_CHANNEL_REG_STRIDE;
+
+ switch (reg) {
+ case TEGRA210_ADMAIF_XBAR_TX_ENABLE:
+ case TEGRA210_ADMAIF_XBAR_TX_STATUS:
+ case TEGRA210_ADMAIF_XBAR_TX_FIFO_CTRL:
+ case TEGRA210_ADMAIF_XBAR_TX_SOFT_RESET:
+ case TEGRA210_ADMAIF_CHAN_ACIF_TX_CTRL:
+ case TEGRA210_ADMAIF_XBAR_RX_ENABLE:
+ case TEGRA210_ADMAIF_XBAR_RX_FIFO_CTRL:
+ case TEGRA210_ADMAIF_XBAR_RX_SOFT_RESET:
+ case TEGRA210_ADMAIF_CHAN_ACIF_RX_CTRL:
+ case TEGRA210_ADMAIF_GLOBAL_ENABLE:
+ return true;
+ default:
+ break;
+ };
+
+ return false;
+}
+
+static bool tegra210_admaif_rd_reg(struct device *dev, unsigned int reg)
+{
+ reg = reg % TEGRA210_ADMAIF_CHANNEL_REG_STRIDE;
+
+ switch (reg) {
+ case TEGRA210_ADMAIF_XBAR_RX_STATUS:
+ case TEGRA210_ADMAIF_XBAR_RX_INT_STATUS:
+ case TEGRA210_ADMAIF_XBAR_RX_ENABLE:
+ case TEGRA210_ADMAIF_XBAR_RX_SOFT_RESET:
+ case TEGRA210_ADMAIF_XBAR_RX_FIFO_CTRL:
+ case TEGRA210_ADMAIF_CHAN_ACIF_RX_CTRL:
+ case TEGRA210_ADMAIF_XBAR_TX_STATUS:
+ case TEGRA210_ADMAIF_XBAR_TX_INT_STATUS:
+ case TEGRA210_ADMAIF_XBAR_TX_ENABLE:
+ case TEGRA210_ADMAIF_XBAR_TX_SOFT_RESET:
+ case TEGRA210_ADMAIF_XBAR_TX_FIFO_CTRL:
+ case TEGRA210_ADMAIF_CHAN_ACIF_TX_CTRL:
+ case TEGRA210_ADMAIF_GLOBAL_ENABLE:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static bool tegra210_admaif_volatile_reg(struct device *dev, unsigned int reg)
+{
+ reg = reg % TEGRA210_ADMAIF_CHANNEL_REG_STRIDE;
+
+ switch (reg) {
+ case TEGRA210_ADMAIF_XBAR_RX_STATUS:
+ case TEGRA210_ADMAIF_XBAR_TX_STATUS:
+ case TEGRA210_ADMAIF_XBAR_RX_INT_STATUS:
+ case TEGRA210_ADMAIF_XBAR_TX_INT_STATUS:
+ case TEGRA210_ADMAIF_XBAR_RX_SOFT_RESET:
+ case TEGRA210_ADMAIF_XBAR_TX_SOFT_RESET:
+ return true;
+ default:
+ break;
+ };
+
+ return false;
+}
+
+static const struct regmap_config tegra210_admaif_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = TEGRA210_ADMAIF_LAST_REG,
+ .writeable_reg = tegra210_admaif_wr_reg,
+ .readable_reg = tegra210_admaif_rd_reg,
+ .volatile_reg = tegra210_admaif_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static void tegra210_admaif_global_enable(struct tegra210_admaif *admaif,
+ int enable)
+{
+ if (enable) {
+ regmap_update_bits(admaif->regmap,
+ TEGRA210_ADMAIF_GLOBAL_ENABLE, 1, 1);
+ admaif->refcnt++;
+ } else {
+ admaif->refcnt--;
+
+ if (!admaif->refcnt)
+ regmap_update_bits(admaif->regmap,
+ TEGRA210_ADMAIF_GLOBAL_ENABLE, 1, 0);
+ }
+}
+
+static int tegra210_admaif_sw_reset(struct snd_soc_dai *dai,
+ int direction, int timeout)
+{
+ struct tegra210_admaif *admaif = snd_soc_dai_get_drvdata(dai);
+ unsigned int sw_reset_reg, val;
+ int wait = timeout;
+
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ sw_reset_reg = TEGRA210_ADMAIF_XBAR_TX_SOFT_RESET +
+ (dai->id * TEGRA210_ADMAIF_CHANNEL_REG_STRIDE);
+ } else {
+ sw_reset_reg = TEGRA210_ADMAIF_XBAR_RX_SOFT_RESET +
+ (dai->id * TEGRA210_ADMAIF_CHANNEL_REG_STRIDE);
+ }
+
+ regmap_update_bits(admaif->regmap, sw_reset_reg, 1, 1);
+
+ do {
+ regmap_read(admaif->regmap, sw_reset_reg, &val);
+ wait--;
+ if (!wait)
+ return -EINVAL;
+ } while (val & 0x00000001);
+
+ return 0;
+}
+
+static int tegra210_admaif_runtime_suspend(struct device *dev)
+{
+ struct tegra210_admaif *admaif = dev_get_drvdata(dev);
+
+ regcache_cache_only(admaif->regmap, true);
+
+ clk_disable(admaif->clk);
+
+ return 0;
+}
+
+static int tegra210_admaif_runtime_resume(struct device *dev)
+{
+ struct tegra210_admaif *admaif = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_enable(admaif->clk);
+ if (ret) {
+ dev_err(dev, "clk_enable failed: %d\n", ret);
+ return ret;
+ }
+
+ regcache_cache_only(admaif->regmap, false);
+
+ return 0;
+}
+
+static int tegra210_admaif_set_pack_mode(struct regmap *map, unsigned int reg,
+ int valid_bit)
+{
+ switch (valid_bit) {
+ case DATA_8BIT:
+ regmap_update_bits(map, reg,
+ TEGRA210_ADMAIF_CHAN_ACIF_CTRL_PACK8_EN_MASK,
+ TEGRA210_ADMAIF_CHAN_ACIF_CTRL_PACK8_EN);
+ regmap_update_bits(map, reg,
+ TEGRA210_ADMAIF_CHAN_ACIF_CTRL_PACK16_EN_MASK,
+ 0);
+ break;
+ case DATA_16BIT:
+ regmap_update_bits(map, reg,
+ TEGRA210_ADMAIF_CHAN_ACIF_CTRL_PACK16_EN_MASK,
+ TEGRA210_ADMAIF_CHAN_ACIF_CTRL_PACK16_EN);
+ regmap_update_bits(map, reg,
+ TEGRA210_ADMAIF_CHAN_ACIF_CTRL_PACK8_EN_MASK,
+ 0);
+ break;
+ case DATA_32BIT:
+ regmap_update_bits(map, reg,
+ TEGRA210_ADMAIF_CHAN_ACIF_CTRL_PACK16_EN_MASK,
+ 0);
+ regmap_update_bits(map, reg,
+ TEGRA210_ADMAIF_CHAN_ACIF_CTRL_PACK8_EN_MASK,
+ 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int tegra210_admaif_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct device *dev = dai->dev;
+ struct tegra210_admaif *admaif = snd_soc_dai_get_drvdata(dai);
+ struct tegra210_xbar_cif_conf cif_conf;
+ unsigned int reg, fifo_ctrl, fifo_size;
+ int valid_bit, ret;
+
+ 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 = TEGRA210_AUDIOCIF_BITS_16;
+ cif_conf.client_bits = TEGRA210_AUDIOCIF_BITS_16;
+ valid_bit = DATA_16BIT;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ cif_conf.audio_bits = TEGRA210_AUDIOCIF_BITS_32;
+ cif_conf.client_bits = TEGRA210_AUDIOCIF_BITS_32;
+ valid_bit = DATA_32BIT;
+ break;
+ default:
+ dev_err(dev, "Wrong format!\n");
+ return -EINVAL;
+ }
+
+ 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) {
+ reg = TEGRA210_ADMAIF_CHAN_ACIF_TX_CTRL +
+ (dai->id * TEGRA210_ADMAIF_CHANNEL_REG_STRIDE);
+ fifo_ctrl = TEGRA210_ADMAIF_XBAR_TX_FIFO_CTRL +
+ (dai->id * TEGRA210_ADMAIF_CHANNEL_REG_STRIDE);
+ fifo_size = 3;
+ } else {
+ reg = TEGRA210_ADMAIF_CHAN_ACIF_RX_CTRL +
+ (dai->id * TEGRA210_ADMAIF_CHANNEL_REG_STRIDE);
+ fifo_ctrl = TEGRA210_ADMAIF_XBAR_RX_FIFO_CTRL +
+ (dai->id * TEGRA210_ADMAIF_CHANNEL_REG_STRIDE);
+ fifo_size = 3;
+ }
+
+ /* HW needs sw reset to make sure previous transaction be clean */
+ ret = tegra210_admaif_sw_reset(dai, substream->stream, 0xffff);
+ if (ret) {
+ dev_err(dev, "Failed at sw reset\n");
+ return ret;
+ }
+
+ tegra210_admaif_set_pack_mode(admaif->regmap, reg, valid_bit);
+ admaif->soc_data->set_audio_cif(admaif->regmap, reg, &cif_conf);
+
+ regmap_update_bits(admaif->regmap, fifo_ctrl,
+ TEGRA210_ADMAIF_XBAR_DMA_FIFO_SIZE_MASK,
+ fifo_size << TEGRA210_ADMAIF_XBAR_DMA_FIFO_SIZE_SHIFT);
+
+ return 0;
+}
+
+static void tegra210_admaif_start_playback(struct snd_soc_dai *dai)
+{
+ struct tegra210_admaif *admaif = snd_soc_dai_get_drvdata(dai);
+ unsigned int reg;
+
+ tegra210_admaif_global_enable(admaif, 1);
+
+ reg = TEGRA210_ADMAIF_XBAR_TX_ENABLE +
+ (dai->id * TEGRA210_ADMAIF_CHANNEL_REG_STRIDE);
+ regmap_update_bits(admaif->regmap, reg,
+ TEGRA210_ADMAIF_XBAR_TX_ENABLE_MASK,
+ TEGRA210_ADMAIF_XBAR_TX_EN);
+}
+
+static void tegra210_admaif_stop_playback(struct snd_soc_dai *dai)
+{
+ struct tegra210_admaif *admaif = snd_soc_dai_get_drvdata(dai);
+ unsigned int reg;
+
+ tegra210_admaif_global_enable(admaif, 0);
+ reg = TEGRA210_ADMAIF_XBAR_TX_ENABLE +
+ (dai->id * TEGRA210_ADMAIF_CHANNEL_REG_STRIDE);
+ regmap_update_bits(admaif->regmap, reg,
+ TEGRA210_ADMAIF_XBAR_TX_ENABLE_MASK,
+ 0);
+}
+
+static void tegra210_admaif_start_capture(struct snd_soc_dai *dai)
+{
+ struct tegra210_admaif *admaif = snd_soc_dai_get_drvdata(dai);
+ unsigned int reg;
+
+ tegra210_admaif_global_enable(admaif, 1);
+ reg = TEGRA210_ADMAIF_XBAR_RX_ENABLE +
+ (dai->id * TEGRA210_ADMAIF_CHANNEL_REG_STRIDE);
+ regmap_update_bits(admaif->regmap, reg,
+ TEGRA210_ADMAIF_XBAR_RX_ENABLE_MASK,
+ TEGRA210_ADMAIF_XBAR_RX_EN);
+}
+
+static void tegra210_admaif_stop_capture(struct snd_soc_dai *dai)
+{
+ struct tegra210_admaif *admaif = snd_soc_dai_get_drvdata(dai);
+ unsigned int reg;
+
+ tegra210_admaif_global_enable(admaif, 0);
+ reg = TEGRA210_ADMAIF_XBAR_RX_ENABLE +
+ (dai->id * TEGRA210_ADMAIF_CHANNEL_REG_STRIDE);
+ regmap_update_bits(admaif->regmap, reg,
+ TEGRA210_ADMAIF_XBAR_RX_ENABLE_MASK,
+ 0);
+}
+
+static int tegra210_admaif_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)
+ tegra210_admaif_start_playback(dai);
+ else
+ tegra210_admaif_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)
+ tegra210_admaif_stop_playback(dai);
+ else
+ tegra210_admaif_stop_capture(dai);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_dai_ops tegra210_admaif_dai_ops = {
+ .hw_params = tegra210_admaif_hw_params,
+ .trigger = tegra210_admaif_trigger,
+};
+
+static int tegra210_admaif_dai_probe(struct snd_soc_dai *dai)
+{
+ struct tegra210_admaif *admaif = snd_soc_dai_get_drvdata(dai);
+
+ dai->capture_dma_data = &admaif->capture_dma_data[dai->id];
+ dai->playback_dma_data = &admaif->playback_dma_data[dai->id];
+
+ return 0;
+}
+
+#define ADMAIF_DAI(id) \
+ { \
+ .name = "ADMAIF" #id, \
+ .probe = tegra210_admaif_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 = &tegra210_admaif_dai_ops, \
+ }
+
+static struct snd_soc_dai_driver tegra210_admaif_dais[10] = {
+ ADMAIF_DAI(1),
+ ADMAIF_DAI(2),
+ ADMAIF_DAI(3),
+ ADMAIF_DAI(4),
+ ADMAIF_DAI(5),
+ ADMAIF_DAI(6),
+ ADMAIF_DAI(7),
+ ADMAIF_DAI(8),
+ ADMAIF_DAI(9),
+ ADMAIF_DAI(10),
+};
+
+static const struct snd_soc_component_driver tegra210_admaif_dai_driver = {
+ .name = DRV_NAME,
+};
+
+static struct tegra210_admaif_soc_data soc_data_tegra210 = {
+ .num_ch = 10,
+ .set_audio_cif = tegra210_xbar_set_cif,
+};
+
+static const struct of_device_id tegra210_admaif_of_match[] = {
+ { .compatible = "nvidia,tegra210-admaif", .data = &soc_data_tegra210 },
+ {},
+};
+
+static int tegra210_admaif_probe(struct platform_device *pdev)
+{
+ int i;
+
+ int ret;
+ struct tegra210_admaif *admaif;
+ void __iomem *regs;
+ struct resource *res;
+ const struct of_device_id *match;
+ struct tegra210_admaif_soc_data *soc_data;
+
+ match = of_match_device(tegra210_admaif_of_match, &pdev->dev);
+ if (!match) {
+ dev_err(&pdev->dev, "Error: No device match found\n");
+ return -ENODEV;
+ }
+ soc_data = (struct tegra210_admaif_soc_data *)match->data;
+
+ admaif = devm_kzalloc(&pdev->dev, sizeof(*admaif), GFP_KERNEL);
+ if (!admaif) {
+ dev_err(&pdev->dev, "Can't allocate tegra210_admaif\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ dev_set_drvdata(&pdev->dev, admaif);
+
+ admaif->refcnt = 0;
+
+ admaif->soc_data = soc_data;
+
+ admaif->capture_dma_data = devm_kzalloc(&pdev->dev,
+ sizeof(struct tegra_alt_pcm_dma_params) *
+ admaif->soc_data->num_ch,
+ GFP_KERNEL);
+ if (!admaif->capture_dma_data) {
+ dev_err(&pdev->dev, "Can't allocate tegra_alt_pcm_dma_params\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ admaif->playback_dma_data = devm_kzalloc(&pdev->dev,
+ sizeof(struct tegra_alt_pcm_dma_params) *
+ admaif->soc_data->num_ch,
+ GFP_KERNEL);
+ if (!admaif->playback_dma_data) {
+ dev_err(&pdev->dev, "Can't allocate tegra_alt_pcm_dma_params\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ admaif->clk = devm_clk_get(&pdev->dev, "admaif");
+ if (IS_ERR(admaif->clk)) {
+ dev_err(&pdev->dev, "Can't retrieve clock\n");
+ ret = PTR_ERR(admaif->clk);
+ goto err;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "No memory resource for admaif\n");
+ ret = -ENODEV;
+ goto err_clk_put;
+ }
+
+ regs = devm_request_and_ioremap(&pdev->dev, res);
+ if (!regs) {
+ dev_err(&pdev->dev, "request/iomap region failed\n");
+ ret = -ENODEV;
+ goto err_clk_put;
+ }
+
+ admaif->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
+ &tegra210_admaif_regmap_config);
+ if (IS_ERR(admaif->regmap)) {
+ dev_err(&pdev->dev, "regmap init failed\n");
+ ret = PTR_ERR(admaif->regmap);
+ goto err_clk_put;
+ }
+ regcache_cache_only(admaif->regmap, true);
+
+ pm_runtime_enable(&pdev->dev);
+ if (!pm_runtime_enabled(&pdev->dev)) {
+ ret = tegra210_admaif_runtime_resume(&pdev->dev);
+ if (ret)
+ goto err_pm_disable;
+ }
+
+ for (i = 0; i < admaif->soc_data->num_ch; i++) {
+ admaif->playback_dma_data[i].addr = res->start +
+ TEGRA210_ADMAIF_XBAR_TX_FIFO_WRITE +
+ (i * TEGRA210_ADMAIF_CHANNEL_REG_STRIDE);
+
+ admaif->capture_dma_data[i].addr = res->start +
+ TEGRA210_ADMAIF_XBAR_RX_FIFO_READ +
+ (i * TEGRA210_ADMAIF_CHANNEL_REG_STRIDE);
+
+ admaif->playback_dma_data[i].wrap = 4;
+ admaif->playback_dma_data[i].width = 32;
+ admaif->playback_dma_data[i].req_sel = i + 1;
+
+ admaif->capture_dma_data[i].wrap = 4;
+ admaif->capture_dma_data[i].width = 32;
+ admaif->capture_dma_data[i].req_sel = i + 1;
+ }
+
+ ret = snd_soc_register_component(&pdev->dev,
+ &tegra210_admaif_dai_driver,
+ tegra210_admaif_dais,
+ ARRAY_SIZE(tegra210_admaif_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;
+ }
+
+ return 0;
+
+err_unregister_dais:
+ snd_soc_unregister_component(&pdev->dev);
+err_suspend:
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ tegra210_admaif_runtime_suspend(&pdev->dev);
+err_pm_disable:
+ pm_runtime_disable(&pdev->dev);
+err_clk_put:
+ devm_clk_put(&pdev->dev, admaif->clk);
+err:
+ return ret;
+}
+
+static int tegra210_admaif_remove(struct platform_device *pdev)
+{
+ struct tegra210_admaif *admaif = 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))
+ tegra210_admaif_runtime_suspend(&pdev->dev);
+
+ devm_clk_put(&pdev->dev, admaif->clk);
+
+ return 0;
+}
+
+static const struct dev_pm_ops tegra210_admaif_pm_ops = {
+ SET_RUNTIME_PM_OPS(tegra210_admaif_runtime_suspend,
+ tegra210_admaif_runtime_resume, NULL)
+};
+
+static struct platform_driver tegra210_admaif_driver = {
+ .probe = tegra210_admaif_probe,
+ .remove = tegra210_admaif_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = tegra210_admaif_of_match,
+ .pm = &tegra210_admaif_pm_ops,
+ },
+};
+module_platform_driver(tegra210_admaif_driver);
+
+MODULE_AUTHOR("Songhee Baek ");
+MODULE_DESCRIPTION("Tegra210 ADMAIF driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/tegra-alt/tegra210_adx_alt.c b/sound/soc/tegra-alt/tegra210_adx_alt.c
new file mode 100644
index 00000000..9ffe3071
--- /dev/null
+++ b/sound/soc/tegra-alt/tegra210_adx_alt.c
@@ -0,0 +1,659 @@
+/*
+ * tegra210_adx_alt.c - Tegra210 ADX driver
+ *
+ * Copyright (c) 2014 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 .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "tegra210_xbar_alt.h"
+#include "tegra210_adx_alt.h"
+
+#define DRV_NAME "tegra210-adx"
+
+/**
+ * tegra210_adx_enable_outstream - enable output stream
+ * @adx: struct of tegra210_adx
+ * @stream_id: adx output stream id for enabling
+ */
+static void tegra210_adx_enable_outstream(struct tegra210_adx *adx,
+ unsigned int stream_id)
+{
+ int reg;
+
+ reg = TEGRA210_ADX_CTRL;
+
+ regmap_update_bits(adx->regmap, reg,
+ TEGRA210_ADX_TX_ENABLE << stream_id,
+ TEGRA210_ADX_TX_ENABLE << stream_id);
+}
+
+/**
+ * tegra210_adx_disable_outstream - disable output stream
+ * @adx: struct of tegra210_adx
+ * @stream_id: adx output stream id for disabling
+ */
+static void tegra210_adx_disable_outstream(struct tegra210_adx *adx,
+ unsigned int stream_id)
+{
+ int reg;
+
+ reg = TEGRA210_ADX_CTRL;
+
+ regmap_update_bits(adx->regmap, reg,
+ TEGRA210_ADX_TX_ENABLE << stream_id,
+ TEGRA210_ADX_TX_DISABLE);
+}
+
+/**
+ * tegra210_adx_set_in_byte_mask - set byte mask for input frame
+ * @adx: struct of tegra210_adx
+ * @mask1: enable for bytes 31 ~ 0 of input frame
+ * @mask2: enable for bytes 63 ~ 32 of input frame
+ */
+static void tegra210_adx_set_in_byte_mask(struct tegra210_adx *adx,
+ unsigned int mask1,
+ unsigned int mask2)
+{
+ regmap_write(adx->regmap, TEGRA210_ADX_IN_BYTE_EN0, mask1);
+ regmap_write(adx->regmap, TEGRA210_ADX_IN_BYTE_EN1, mask2);
+}
+
+/**
+ * tegra210_adx_set_map_table - set map table not RAM
+ * @adx: struct of tegra210_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 tegra210_adx_set_map_table(struct tegra210_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 << TEGRA210_ADX_MAP_STREAM_NUMBER_SHIFT) |
+ (nth_word << TEGRA210_ADX_MAP_WORD_NUMBER_SHIFT) |
+ (nth_byte << TEGRA210_ADX_MAP_BYTE_NUMBER_SHIFT);
+}
+
+/**
+ * tegra210_adx_write_map_ram - write map information in RAM
+ * @adx: struct of tegra210_adx
+ * @addr: n-th word of input stream
+ * @val : bytes mapping information of the word
+ */
+static void tegra210_adx_write_map_ram(struct tegra210_adx *adx,
+ unsigned int addr,
+ unsigned int val)
+{
+ unsigned int reg;
+
+ regmap_write(adx->regmap, TEGRA210_ADX_AHUBRAMCTL_ADX_CTRL,
+ (addr << TEGRA210_ADX_AHUBRAMCTL_ADX_CTRL_RAM_ADDR_SHIFT));
+
+ regmap_write(adx->regmap, TEGRA210_ADX_AHUBRAMCTL_ADX_DATA, val);
+
+ regmap_read(adx->regmap, TEGRA210_ADX_AHUBRAMCTL_ADX_CTRL, ®);
+ reg |= TEGRA210_ADX_AHUBRAMCTL_ADX_CTRL_ADDR_INIT_EN;
+
+ regmap_write(adx->regmap, TEGRA210_ADX_AHUBRAMCTL_ADX_CTRL, reg);
+
+ regmap_read(adx->regmap, TEGRA210_ADX_AHUBRAMCTL_ADX_CTRL, ®);
+ reg |= TEGRA210_ADX_AHUBRAMCTL_ADX_CTRL_RW_WRITE;
+
+ regmap_write(adx->regmap, TEGRA210_ADX_AHUBRAMCTL_ADX_CTRL, reg);
+}
+
+static void tegra210_adx_update_map_ram(struct tegra210_adx *adx)
+{
+ int i;
+
+ for (i = 0; i < TEGRA210_ADX_RAM_DEPTH; i++)
+ tegra210_adx_write_map_ram(adx, i, adx->map[i]);
+}
+
+#ifdef TEGRA210_ADX_MAP_READ
+static unsigned int tegra210_adx_read_map_ram(struct tegra210_adx *adx,
+ unsigned int addr)
+{
+ unsigned int val, wait;
+ wait = 0xffff;
+
+ regmap_write(adx->regmap, TEGRA210_ADX_AHUBRAMCTL_ADX_CTRL,
+ (addr << TEGRA210_ADX_AHUBRAMCTL_ADX_CTRL_RAM_ADDR_SHIFT));
+
+ regmap_read(adx->regmap, TEGRA210_ADX_AHUBRAMCTL_ADX_CTRL, &val);
+ val |= TEGRA210_ADX_AHUBRAMCTL_ADX_CTRL_ADDR_INIT_EN;
+ regmap_write(adx->regmap, TEGRA210_ADX_AHUBRAMCTL_ADX_CTRL, val);
+ regmap_read(adx->regmap, TEGRA210_ADX_AHUBRAMCTL_ADX_CTRL, &val);
+ val &= ~(TEGRA210_ADX_AHUBRAMCTL_ADX_CTRL_RW_WRITE);
+ regmap_write(adx->regmap, TEGRA210_ADX_AHUBRAMCTL_ADX_CTRL, val);
+
+ do {
+ regmap_read(adx->regmap, TEGRA210_ADX_AHUBRAMCTL_ADX_CTRL, &val);
+ wait--;
+ if (!wait)
+ return -EINVAL;
+ } while (val & 0x80000000);
+
+ regmap_read(adx->regmap, TEGRA210_ADX_AHUBRAMCTL_ADX_DATA, &val);
+
+ return val;
+}
+#endif
+
+static int tegra210_adx_runtime_suspend(struct device *dev)
+{
+ struct tegra210_adx *adx = dev_get_drvdata(dev);
+
+ regcache_cache_only(adx->regmap, true);
+
+#ifndef CONFIG_MACH_GRENADA
+ clk_disable_unprepare(adx->clk_adx);
+#endif
+
+ return 0;
+}
+
+static int tegra210_adx_runtime_resume(struct device *dev)
+{
+ struct tegra210_adx *adx = dev_get_drvdata(dev);
+#ifndef CONFIG_MACH_GRENADA
+ int ret;
+
+ ret = clk_prepare_enable(adx->clk_adx);
+ if (ret) {
+ dev_err(dev, "clk_enable failed: %d\n", ret);
+ return ret;
+ }
+#endif
+
+ regcache_cache_only(adx->regmap, false);
+
+ return 0;
+}
+
+static int tegra210_adx_set_audio_cif(struct tegra210_adx *adx,
+ struct snd_pcm_hw_params *params,
+ unsigned int reg)
+{
+ int channels, audio_bits;
+ struct tegra210_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 = TEGRA210_AUDIOCIF_BITS_16;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ audio_bits = TEGRA210_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.truncate = 0;
+ cif_conf.mono_conv = 0;
+
+ adx->soc_data->set_audio_cif(adx->regmap, reg, &cif_conf);
+
+ return 0;
+}
+
+static int tegra210_adx_out_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct tegra210_adx *adx = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ ret = tegra210_adx_set_audio_cif(adx, params,
+ TEGRA210_ADX_AXBAR_TX1_CIF_CTRL +
+ (dai->id * TEGRA210_ADX_AUDIOCIF_CH_STRIDE));
+
+ return ret;
+}
+
+static int tegra210_adx_out_trigger(struct snd_pcm_substream *substream,
+ int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct tegra210_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:
+ tegra210_adx_enable_outstream(adx, dai->id);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ tegra210_adx_disable_outstream(adx, dai->id);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int tegra210_adx_in_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct tegra210_adx *adx = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ ret = tegra210_adx_set_audio_cif(adx, params,
+ TEGRA210_ADX_AXBAR_RX_CIF_CTRL);
+ return ret;
+}
+
+int tegra210_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 tegra210_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;
+ }
+
+ memset(adx->map, 0, sizeof(adx->map));
+ 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;
+ tegra210_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;
+ }
+ }
+
+ tegra210_adx_update_map_ram(adx);
+ tegra210_adx_set_in_byte_mask(adx, byte_mask1, byte_mask2);
+
+ return 0;
+}
+
+static int tegra210_adx_codec_probe(struct snd_soc_codec *codec)
+{
+ struct tegra210_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 tegra210_adx_in_dai_ops = {
+ .hw_params = tegra210_adx_in_hw_params,
+ .set_channel_map = tegra210_adx_set_channel_map,
+};
+
+static struct snd_soc_dai_ops tegra210_adx_out_dai_ops = {
+ .hw_params = tegra210_adx_out_hw_params,
+ .trigger = tegra210_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 = &tegra210_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 tegra210_adx_dais[] = {
+ OUT_DAI(1),
+ OUT_DAI(2),
+ OUT_DAI(3),
+ OUT_DAI(4),
+ IN_DAI(IN, &tegra210_adx_in_dai_ops),
+};
+
+static const struct snd_soc_dapm_widget tegra210_adx_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("IN", NULL, 0, TEGRA210_ADX_ENABLE, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("OUT1", NULL, 0, TEGRA210_ADX_CTRL, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("OUT2", NULL, 0, TEGRA210_ADX_CTRL, 1, 0),
+ SND_SOC_DAPM_AIF_OUT("OUT3", NULL, 0, TEGRA210_ADX_CTRL, 2, 0),
+ SND_SOC_DAPM_AIF_OUT("OUT4", NULL, 0, TEGRA210_ADX_CTRL, 3, 0),
+};
+
+static const struct snd_soc_dapm_route tegra210_adx_routes[] = {
+ { "IN", NULL, "IN Receive" },
+ { "OUT1", NULL, "IN" },
+ { "OUT2", NULL, "IN" },
+ { "OUT3", NULL, "IN" },
+ { "OUT4", NULL, "IN" },
+ { "OUT1 Transmit", NULL, "OUT1" },
+ { "OUT2 Transmit", NULL, "OUT2" },
+ { "OUT3 Transmit", NULL, "OUT3" },
+ { "OUT4 Transmit", NULL, "OUT4" },
+};
+
+static struct snd_soc_codec_driver tegra210_adx_codec = {
+ .probe = tegra210_adx_codec_probe,
+ .dapm_widgets = tegra210_adx_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tegra210_adx_widgets),
+ .dapm_routes = tegra210_adx_routes,
+ .num_dapm_routes = ARRAY_SIZE(tegra210_adx_routes),
+};
+
+static bool tegra210_adx_wr_reg(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_ADX_AXBAR_TX_INT_MASK:
+ case TEGRA210_ADX_AXBAR_TX_INT_SET:
+ case TEGRA210_ADX_AXBAR_TX_INT_CLEAR:
+ case TEGRA210_ADX_AXBAR_TX1_CIF_CTRL:
+ case TEGRA210_ADX_AXBAR_TX2_CIF_CTRL:
+ case TEGRA210_ADX_AXBAR_TX3_CIF_CTRL:
+ case TEGRA210_ADX_AXBAR_TX4_CIF_CTRL:
+ case TEGRA210_ADX_AXBAR_RX_INT_MASK:
+ case TEGRA210_ADX_AXBAR_RX_INT_SET:
+ case TEGRA210_ADX_AXBAR_RX_INT_CLEAR:
+ case TEGRA210_ADX_AXBAR_RX_CIF_CTRL:
+ case TEGRA210_ADX_ENABLE:
+ case TEGRA210_ADX_SOFT_RESET:
+ case TEGRA210_ADX_CG:
+ case TEGRA210_ADX_CTRL:
+ case TEGRA210_ADX_IN_BYTE_EN0:
+ case TEGRA210_ADX_IN_BYTE_EN1:
+ case TEGRA210_ADX_CYA:
+ case TEGRA210_ADX_AHUBRAMCTL_ADX_CTRL:
+ case TEGRA210_ADX_AHUBRAMCTL_ADX_DATA:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static bool tegra210_adx_rd_reg(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_ADX_AXBAR_RX_STATUS:
+ case TEGRA210_ADX_AXBAR_RX_INT_STATUS:
+ case TEGRA210_ADX_AXBAR_RX_INT_MASK:
+ case TEGRA210_ADX_AXBAR_RX_INT_SET:
+ case TEGRA210_ADX_AXBAR_RX_INT_CLEAR:
+ case TEGRA210_ADX_AXBAR_RX_CIF_CTRL:
+ case TEGRA210_ADX_AXBAR_TX_STATUS:
+ case TEGRA210_ADX_AXBAR_TX_INT_STATUS:
+ case TEGRA210_ADX_AXBAR_TX_INT_MASK:
+ case TEGRA210_ADX_AXBAR_TX_INT_SET:
+ case TEGRA210_ADX_AXBAR_TX_INT_CLEAR:
+ case TEGRA210_ADX_AXBAR_TX1_CIF_CTRL:
+ case TEGRA210_ADX_AXBAR_TX2_CIF_CTRL:
+ case TEGRA210_ADX_AXBAR_TX3_CIF_CTRL:
+ case TEGRA210_ADX_AXBAR_TX4_CIF_CTRL:
+ case TEGRA210_ADX_ENABLE:
+ case TEGRA210_ADX_SOFT_RESET:
+ case TEGRA210_ADX_CG:
+ case TEGRA210_ADX_CTRL:
+ case TEGRA210_ADX_IN_BYTE_EN0:
+ case TEGRA210_ADX_IN_BYTE_EN1:
+ case TEGRA210_ADX_CYA:
+ case TEGRA210_ADX_AHUBRAMCTL_ADX_CTRL:
+ case TEGRA210_ADX_AHUBRAMCTL_ADX_DATA:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static const struct regmap_config tegra210_adx_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = TEGRA210_ADX_AHUBRAMCTL_ADX_DATA,
+ .writeable_reg = tegra210_adx_wr_reg,
+ .readable_reg = tegra210_adx_rd_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct tegra210_adx_soc_data soc_data_tegra210 = {
+ .set_audio_cif = tegra210_xbar_set_cif
+};
+
+static const struct of_device_id tegra210_adx_of_match[] = {
+ { .compatible = "nvidia,tegra210-adx", .data = &soc_data_tegra210 },
+ {},
+};
+
+static int tegra210_adx_platform_probe(struct platform_device *pdev)
+{
+ struct tegra210_adx *adx;
+ struct resource *mem, *memregion;
+ void __iomem *regs;
+ int ret = 0;
+ const struct of_device_id *match;
+ struct tegra210_adx_soc_data *soc_data;
+
+ match = of_match_device(tegra210_adx_of_match, &pdev->dev);
+ if (!match) {
+ dev_err(&pdev->dev, "Error: No device match found\n");
+ ret = -ENODEV;
+ goto err;
+ }
+ soc_data = (struct tegra210_adx_soc_data *)match->data;
+
+ adx = devm_kzalloc(&pdev->dev, sizeof(struct tegra210_adx), GFP_KERNEL);
+ if (!adx) {
+ dev_err(&pdev->dev, "Can't allocate tegra210_adx\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+ dev_set_drvdata(&pdev->dev, adx);
+
+ adx->soc_data = soc_data;
+
+#ifndef CONFIG_MACH_GRENADA
+ 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;
+ }
+#endif
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "No memory resource\n");
+ ret = -ENODEV;
+ goto err;
+ }
+
+ 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,
+ &tegra210_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);
+
+ if (of_property_read_u32(pdev->dev.of_node,
+ "nvidia,ahub-adx-id",
+ &pdev->dev.id) < 0) {
+ dev_err(&pdev->dev,
+ "Missing property nvidia,ahub-adx-id\n");
+ ret = -ENODEV;
+ goto err_clk_put;
+ }
+
+ pm_runtime_enable(&pdev->dev);
+ if (!pm_runtime_enabled(&pdev->dev)) {
+ ret = tegra210_adx_runtime_resume(&pdev->dev);
+ if (ret)
+ goto err_pm_disable;
+ }
+
+ ret = snd_soc_register_codec(&pdev->dev, &tegra210_adx_codec,
+ tegra210_adx_dais,
+ ARRAY_SIZE(tegra210_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))
+ tegra210_adx_runtime_suspend(&pdev->dev);
+err_pm_disable:
+ pm_runtime_disable(&pdev->dev);
+err_clk_put:
+#ifndef CONFIG_MACH_GRENADA
+ devm_clk_put(&pdev->dev, adx->clk_adx);
+#else
+ dev_err(&pdev->dev, "err\n");
+#endif
+err:
+ return ret;
+}
+
+static int tegra210_adx_platform_remove(struct platform_device *pdev)
+{
+#ifndef CONFIG_MACH_GRENADA
+ struct tegra210_adx *adx = dev_get_drvdata(&pdev->dev);
+#endif
+ snd_soc_unregister_codec(&pdev->dev);
+
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ tegra210_adx_runtime_suspend(&pdev->dev);
+
+#ifndef CONFIG_MACH_GRENADA
+ devm_clk_put(&pdev->dev, adx->clk_adx);
+#endif
+ return 0;
+}
+
+static const struct dev_pm_ops tegra210_adx_pm_ops = {
+ SET_RUNTIME_PM_OPS(tegra210_adx_runtime_suspend,
+ tegra210_adx_runtime_resume, NULL)
+};
+
+static struct platform_driver tegra210_adx_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = tegra210_adx_of_match,
+ .pm = &tegra210_adx_pm_ops,
+ },
+ .probe = tegra210_adx_platform_probe,
+ .remove = tegra210_adx_platform_remove,
+};
+module_platform_driver(tegra210_adx_driver);
+
+MODULE_AUTHOR("Arun Shamanna Lakshmi ");
+MODULE_DESCRIPTION("Tegra210 ADX ASoC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, tegra210_adx_of_match);
diff --git a/sound/soc/tegra-alt/tegra210_afc_alt.c b/sound/soc/tegra-alt/tegra210_afc_alt.c
new file mode 100644
index 00000000..198b7a02
--- /dev/null
+++ b/sound/soc/tegra-alt/tegra210_afc_alt.c
@@ -0,0 +1,485 @@
+/*
+ * tegra210_afc_alt.c - Tegra210 AFC driver
+ *
+ * Copyright (c) 2014 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 .
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "tegra210_xbar_alt.h"
+#include "tegra210_afc_alt.h"
+
+#define DRV_NAME "tegra210-afc"
+
+static int tegra210_afc_runtime_suspend(struct device *dev)
+{
+ struct tegra210_afc *afc = dev_get_drvdata(dev);
+
+ regcache_cache_only(afc->regmap, true);
+
+#ifndef CONFIG_MACH_GRENADA
+ clk_disable_unprepare(afc->clk_afc);
+#endif
+
+ return 0;
+}
+
+static int tegra210_afc_runtime_resume(struct device *dev)
+{
+ struct tegra210_afc *afc = dev_get_drvdata(dev);
+
+#ifndef CONFIG_MACH_GRENADA
+ int ret;
+
+ ret = clk_prepare_enable(afc->clk_afc);
+ if (ret) {
+ dev_err(dev, "clk_enable failed: %d\n", ret);
+ return ret;
+ }
+#endif
+
+ regcache_cache_only(afc->regmap, false);
+
+ return 0;
+}
+
+/* returns the destination I2S id connected along the AFC path */
+static unsigned int tegra210_afc_get_i2s_id(unsigned int afc_id)
+{
+ unsigned int i2s_reg, i2s_val, amx_reg, amx_val, i, j;
+
+ for (i = 1; i < 6; i++) {
+ i2s_val = 0;
+ i2s_reg = TEGRA210_XBAR_PART1_RX +
+ TEGRA210_XBAR_RX_STRIDE * (i + 0xF);
+ tegra210_xbar_read_reg(i2s_reg, &i2s_val);
+ if ((i2s_val >> 24) & (1 << afc_id)) {
+ return i;
+ } else if (i2s_val & 0x300) {
+ for (j = 1; j < 9; j++) {
+ amx_val = 0;
+ amx_reg = TEGRA210_XBAR_PART1_RX +
+ TEGRA210_XBAR_RX_STRIDE * (j + 0x4F);
+ tegra210_xbar_read_reg(amx_reg, &amx_val);
+ if ((amx_val >> 24) & (1 << afc_id))
+ return i;
+ }
+ }
+ }
+ return 1;
+}
+
+/* returns the id if SFC is connected along the AFC src path */
+static unsigned int tegra210_afc_get_sfc_id(unsigned int afc_id)
+{
+ unsigned int reg, val = 0;
+
+ reg = TEGRA210_XBAR_PART0_RX +
+ TEGRA210_XBAR_RX_STRIDE * (afc_id + 0x34);
+
+ tegra210_xbar_read_reg(reg, &val);
+ val = val >> 24;
+
+ return val;
+}
+
+static int tegra210_afc_set_thresholds(struct tegra210_afc *afc,
+ unsigned int afc_id)
+{
+ unsigned int i2s_id, value;
+
+ if (tegra210_afc_get_sfc_id(afc_id)) {
+ /* TODO program thresholds using SRC_BURST */
+ } else {
+ value = 8 << TEGRA210_AFC_FIFO_HIGH_THRESHOLD_SHIFT;
+ value |= 7 << TEGRA210_AFC_FIFO_START_THRESHOLD_SHIFT;
+ value |= 6;
+ }
+ regmap_write(afc->regmap, TEGRA210_AFC_TXCIF_FIFO_PARAMS, value);
+
+ i2s_id = tegra210_afc_get_i2s_id(afc_id);
+
+ value |= i2s_id << TEGRA210_AFC_DEST_I2S_ID_SHIFT;
+
+ regmap_write(afc->regmap, TEGRA210_AFC_DEST_I2S_PARAMS, value);
+
+ return 0;
+}
+
+static int tegra210_afc_set_audio_cif(struct tegra210_afc *afc,
+ struct snd_pcm_hw_params *params,
+ unsigned int reg)
+{
+ int channels, audio_bits;
+ struct tegra210_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 = TEGRA210_AUDIOCIF_BITS_16;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ audio_bits = TEGRA210_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.truncate = 0;
+ cif_conf.mono_conv = 0;
+
+ afc->soc_data->set_audio_cif(afc->regmap, reg, &cif_conf);
+
+ return 0;
+}
+
+static int tegra210_afc_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct device *dev = dai->dev;
+ struct tegra210_afc *afc = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ /* set RX cif and TX cif */
+ ret = tegra210_afc_set_audio_cif(afc, params,
+ TEGRA210_AFC_AXBAR_RX_CIF_CTRL);
+ if (ret) {
+ dev_err(dev, "Can't set AFC RX CIF: %d\n", ret);
+ return ret;
+ }
+ ret = tegra210_afc_set_audio_cif(afc, params,
+ TEGRA210_AFC_AXBAR_TX_CIF_CTRL);
+ if (ret) {
+ dev_err(dev, "Can't set AFC TX CIF: %d\n", ret);
+ return ret;
+ }
+
+ /* program the thresholds, destn i2s id, PPM values */
+ tegra210_afc_set_thresholds(afc, dev->id);
+
+ return ret;
+
+}
+
+static int tegra210_afc_codec_probe(struct snd_soc_codec *codec)
+{
+ struct tegra210_afc *afc = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ codec->control_data = afc->regmap;
+ ret = snd_soc_codec_set_cache_io(codec, 64, 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 tegra210_afc_dai_ops = {
+ .hw_params = tegra210_afc_hw_params,
+};
+
+static struct snd_soc_dai_driver tegra210_afc_dais[] = {
+ {
+ .name = "AFC IN",
+ .playback = {
+ .stream_name = "AFC Receive",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ },
+ {
+ .name = "AFC OUT",
+ .capture = {
+ .stream_name = "AFC Transmit",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &tegra210_afc_dai_ops,
+ }
+};
+
+static const struct snd_soc_dapm_widget tegra210_afc_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("AFC RX", NULL, 0, SND_SOC_NOPM,
+ 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AFC TX", NULL, 0, TEGRA210_AFC_ENABLE,
+ TEGRA210_AFC_EN_SHIFT, 0),
+};
+
+static const struct snd_soc_dapm_route tegra210_afc_routes[] = {
+ { "AFC RX", NULL, "AFC Receive" },
+ { "AFC TX", NULL, "AFC RX" },
+ { "AFC Transmit", NULL, "AFC TX" },
+};
+
+static struct snd_soc_codec_driver tegra210_afc_codec = {
+ .probe = tegra210_afc_codec_probe,
+ .dapm_widgets = tegra210_afc_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tegra210_afc_widgets),
+ .dapm_routes = tegra210_afc_routes,
+ .num_dapm_routes = ARRAY_SIZE(tegra210_afc_routes),
+};
+
+static bool tegra210_afc_wr_rd_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_AFC_AXBAR_RX_STATUS:
+ case TEGRA210_AFC_AXBAR_RX_CIF_CTRL:
+ case TEGRA210_AFC_AXBAR_RX_CYA:
+ case TEGRA210_AFC_AXBAR_TX_STATUS:
+ case TEGRA210_AFC_AXBAR_TX_INT_STATUS:
+ case TEGRA210_AFC_AXBAR_TX_INT_MASK:
+ case TEGRA210_AFC_AXBAR_TX_INT_SET:
+ case TEGRA210_AFC_AXBAR_TX_INT_CLEAR:
+ case TEGRA210_AFC_AXBAR_TX_CIF_CTRL:
+ case TEGRA210_AFC_AXBAR_TX_CYA:
+ case TEGRA210_AFC_ENABLE:
+ case TEGRA210_AFC_SOFT_RESET:
+ case TEGRA210_AFC_CG:
+ case TEGRA210_AFC_STATUS:
+ case TEGRA210_AFC_INT_STATUS:
+ case TEGRA210_AFC_INT_MASK:
+ case TEGRA210_AFC_INT_SET:
+ case TEGRA210_AFC_INT_CLEAR:
+ case TEGRA210_AFC_DEST_I2S_PARAMS:
+ case TEGRA210_AFC_TXCIF_FIFO_PARAMS:
+ case TEGRA210_AFC_CLK_PPM_DIFF:
+ case TEGRA210_AFC_DBG_CTRL:
+ case TEGRA210_AFC_TOTAL_SAMPLES:
+ case TEGRA210_AFC_DECIMATION_SAMPLES:
+ case TEGRA210_AFC_INTERPOLATION_SAMPLES:
+ case TEGRA210_AFC_DBG_INTERNAL:
+ case TEGRA210_AFC_LCOEF_1_4_0:
+ case TEGRA210_AFC_LCOEF_1_4_1:
+ case TEGRA210_AFC_LCOEF_1_4_2:
+ case TEGRA210_AFC_LCOEF_1_4_3:
+ case TEGRA210_AFC_LCOEF_1_4_4:
+ case TEGRA210_AFC_LCOEF_1_4_5:
+ case TEGRA210_AFC_LCOEF_2_4_0:
+ case TEGRA210_AFC_LCOEF_2_4_1:
+ case TEGRA210_AFC_LCOEF_2_4_2:
+ case TEGRA210_AFC_CYA:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static bool tegra210_afc_volatile_reg(struct device *dev, unsigned int reg)
+{
+ return false;
+}
+
+static const struct regmap_config tegra210_afc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = TEGRA210_AFC_CYA,
+ .writeable_reg = tegra210_afc_wr_rd_reg,
+ .readable_reg = tegra210_afc_wr_rd_reg,
+ .volatile_reg = tegra210_afc_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct tegra210_afc_soc_data soc_data_tegra210 = {
+ .set_audio_cif = tegra210_xbar_set_cif,
+};
+
+static const struct of_device_id tegra210_afc_of_match[] = {
+ { .compatible = "nvidia,tegra210-afc", .data = &soc_data_tegra210 },
+ {},
+};
+
+static int tegra210_afc_platform_probe(struct platform_device *pdev)
+{
+ struct tegra210_afc *afc;
+ struct resource *mem, *memregion;
+ void __iomem *regs;
+ int ret = 0;
+ const struct of_device_id *match;
+ struct tegra210_afc_soc_data *soc_data;
+
+ match = of_match_device(tegra210_afc_of_match, &pdev->dev);
+ if (!match) {
+ dev_err(&pdev->dev, "Error: No device match found\n");
+ ret = -ENODEV;
+ goto err;
+ }
+ soc_data = (struct tegra210_afc_soc_data *)match->data;
+
+ afc = devm_kzalloc(&pdev->dev, sizeof(struct tegra210_afc), GFP_KERNEL);
+ if (!afc) {
+ dev_err(&pdev->dev, "Can't allocate afc\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+ dev_set_drvdata(&pdev->dev, afc);
+
+ afc->soc_data = soc_data;
+
+ /* initialize default destination I2S */
+ afc->destination_i2s = 1;
+
+#ifndef CONFIG_MACH_GRENADA
+ afc->clk_afc = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(afc->clk_afc)) {
+ dev_err(&pdev->dev, "Can't retrieve afc clock\n");
+ ret = PTR_ERR(afc->clk_afc);
+ goto err;
+ }
+#endif
+
+ 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), pdev->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;
+ }
+
+ afc->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
+ &tegra210_afc_regmap_config);
+ if (IS_ERR(afc->regmap)) {
+ dev_err(&pdev->dev, "regmap init failed\n");
+ ret = PTR_ERR(afc->regmap);
+ goto err_clk_put;
+ }
+ regcache_cache_only(afc->regmap, true);
+
+ if (of_property_read_u32(pdev->dev.of_node,
+ "nvidia,ahub-afc-id",
+ &pdev->dev.id) < 0) {
+ dev_err(&pdev->dev,
+ "Missing property nvidia,ahub-afc-id\n");
+ ret = -ENODEV;
+ goto err_clk_put;
+ }
+
+ pm_runtime_enable(&pdev->dev);
+ if (!pm_runtime_enabled(&pdev->dev)) {
+ ret = tegra210_afc_runtime_resume(&pdev->dev);
+ if (ret)
+ goto err_pm_disable;
+ }
+
+ ret = snd_soc_register_codec(&pdev->dev, &tegra210_afc_codec,
+ tegra210_afc_dais,
+ ARRAY_SIZE(tegra210_afc_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))
+ tegra210_afc_runtime_suspend(&pdev->dev);
+err_pm_disable:
+ pm_runtime_disable(&pdev->dev);
+err_clk_put:
+#ifndef CONFIG_MACH_GRENADA
+ devm_clk_put(&pdev->dev, afc->clk_afc);
+#else
+ dev_err(&pdev->dev, "error\n");
+#endif
+err:
+ return ret;
+}
+
+static int tegra210_afc_platform_remove(struct platform_device *pdev)
+{
+#ifndef CONFIG_MACH_GRENADA
+ struct tegra210_afc *afc = dev_get_drvdata(&pdev->dev);
+#endif
+ snd_soc_unregister_codec(&pdev->dev);
+
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ tegra210_afc_runtime_suspend(&pdev->dev);
+
+#ifndef CONFIG_MACH_GRENADA
+ devm_clk_put(&pdev->dev, afc->clk_afc);
+#endif
+
+ return 0;
+}
+
+static const struct dev_pm_ops tegra210_afc_pm_ops = {
+ SET_RUNTIME_PM_OPS(tegra210_afc_runtime_suspend,
+ tegra210_afc_runtime_resume, NULL)
+};
+
+static struct platform_driver tegra210_afc_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = tegra210_afc_of_match,
+ .pm = &tegra210_afc_pm_ops,
+ },
+ .probe = tegra210_afc_platform_probe,
+ .remove = tegra210_afc_platform_remove,
+};
+module_platform_driver(tegra210_afc_driver)
+
+MODULE_AUTHOR("Arun Shamanna Lakshmi ");
+MODULE_DESCRIPTION("Tegra210 AFC ASoC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, tegra210_afc_of_match);
diff --git a/sound/soc/tegra-alt/tegra210_amx_alt.c b/sound/soc/tegra-alt/tegra210_amx_alt.c
new file mode 100644
index 00000000..8f9632ce
--- /dev/null
+++ b/sound/soc/tegra-alt/tegra210_amx_alt.c
@@ -0,0 +1,696 @@
+/*
+ * tegra210_amx_alt.c - Tegra210 AMX driver
+ *
+ * Copyright (c) 2014 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 .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "tegra210_xbar_alt.h"
+#include "tegra210_amx_alt.h"
+
+#define DRV_NAME "tegra210_amx"
+
+/**
+ * tegra210_amx_set_master_stream - set master stream and dependency
+ * @amx: struct of tegra210_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
+ *
+ * This dependency matter on starting point not every frame.
+ * Once amx starts to run, it is work as wait on all.
+ */
+static void tegra210_amx_set_master_stream(struct tegra210_amx *amx,
+ unsigned int stream_id,
+ unsigned int dependency)
+{
+ unsigned int mask, val;
+
+ mask = (TEGRA210_AMX_CTRL_MSTR_RX_NUM_MASK |
+ TEGRA210_AMX_CTRL_RX_DEP_MASK);
+
+ val = (stream_id << TEGRA210_AMX_CTRL_MSTR_RX_NUN_SHIFT) |
+ (dependency << TEGRA210_AMX_CTRL_RX_DEP_SHIFT);
+
+ regmap_update_bits(amx->regmap, TEGRA210_AMX_CTRL, mask, val);
+}
+
+/**
+ * tegra210_amx_enable_instream - enable input stream
+ * @amx: struct of tegra210_amx
+ * @stream_id: amx input stream id for enabling
+ */
+static void tegra210_amx_enable_instream(struct tegra210_amx *amx,
+ unsigned int stream_id)
+{
+ int reg;
+
+ reg = TEGRA210_AMX_CTRL;
+
+ regmap_update_bits(amx->regmap, reg,
+ TEGRA210_AMX_RX_ENABLE << stream_id,
+ TEGRA210_AMX_RX_ENABLE << stream_id);
+}
+
+/**
+ * tegra210_amx_disable_instream - disable input stream
+ * @amx: struct of tegra210_amx
+ * @stream_id: amx input stream id for disabling
+ */
+static void tegra210_amx_disable_instream(struct tegra210_amx *amx,
+ unsigned int stream_id)
+{
+ int reg;
+
+ reg = TEGRA210_AMX_CTRL;
+
+ regmap_update_bits(amx->regmap, reg,
+ TEGRA210_AMX_RX_ENABLE << stream_id,
+ TEGRA210_AMX_RX_DISABLE);
+}
+
+/**
+ * tegra210_amx_set_out_byte_mask - set byte mask for output frame
+ * @amx: struct of tegra210_amx
+ * @mask1: enable for bytes 31 ~ 0
+ * @mask2: enable for bytes 63 ~ 32
+ */
+static void tegra210_amx_set_out_byte_mask(struct tegra210_amx *amx,
+ unsigned int mask1,
+ unsigned int mask2)
+{
+ regmap_write(amx->regmap, TEGRA210_AMX_OUT_BYTE_EN0, mask1);
+ regmap_write(amx->regmap, TEGRA210_AMX_OUT_BYTE_EN1, mask2);
+}
+
+/**
+ * tegra210_amx_set_map_table - set map table not RAM
+ * @amx: struct of tegra210_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 tegra210_amx_set_map_table(struct tegra210_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 << TEGRA210_AMX_MAP_STREAM_NUMBER_SHIFT) |
+ (nth_word << TEGRA210_AMX_MAP_WORD_NUMBER_SHIFT) |
+ (nth_byte << TEGRA210_AMX_MAP_BYTE_NUMBER_SHIFT);
+}
+
+/**
+ * tegra210_amx_write_map_ram - write map information in RAM
+ * @amx: struct of tegra210_amx
+ * @addr: n-th word of input stream
+ * @val : bytes mapping information of the word
+ */
+static void tegra210_amx_write_map_ram(struct tegra210_amx *amx,
+ unsigned int addr,
+ unsigned int val)
+{
+ unsigned int reg;
+
+ regmap_write(amx->regmap, TEGRA210_AMX_AHUBRAMCTL_AMX_CTRL,
+ (addr << TEGRA210_AMX_AHUBRAMCTL_AMX_CTRL_RAM_ADDR_SHIFT));
+
+ regmap_write(amx->regmap, TEGRA210_AMX_AHUBRAMCTL_AMX_DATA, val);
+
+ regmap_read(amx->regmap, TEGRA210_AMX_AHUBRAMCTL_AMX_CTRL, ®);
+ reg |= TEGRA210_AMX_AHUBRAMCTL_AMX_CTRL_ADDR_INIT_EN;
+
+ regmap_write(amx->regmap, TEGRA210_AMX_AHUBRAMCTL_AMX_CTRL, reg);
+
+ regmap_read(amx->regmap, TEGRA210_AMX_AHUBRAMCTL_AMX_CTRL, ®);
+ reg |= TEGRA210_AMX_AHUBRAMCTL_AMX_CTRL_RW_WRITE;
+
+ regmap_write(amx->regmap, TEGRA210_AMX_AHUBRAMCTL_AMX_CTRL, reg);
+}
+
+static void tegra210_amx_update_map_ram(struct tegra210_amx *amx)
+{
+ int i;
+
+ for (i = 0; i < TEGRA210_AMX_RAM_DEPTH; i++)
+ tegra210_amx_write_map_ram(amx, i, amx->map[i]);
+}
+
+static int tegra210_amx_runtime_suspend(struct device *dev)
+{
+ struct tegra210_amx *amx = dev_get_drvdata(dev);
+
+ regcache_cache_only(amx->regmap, true);
+
+#ifndef CONFIG_MACH_GRENADA
+ clk_disable_unprepare(amx->clk_amx);
+#endif
+
+ return 0;
+}
+
+#ifdef TEGRA210_AMX_MAP_READ
+static unsigned int tegra210_amx_read_map_ram(struct tegra210_amx *amx,
+ unsigned int addr)
+{
+ unsigned int val, wait;
+ wait = 0xffff;
+
+ regmap_write(amx->regmap, TEGRA210_AMX_AHUBRAMCTL_AMX_CTRL,
+ (addr << TEGRA210_AMX_AHUBRAMCTL_AMX_CTRL_RAM_ADDR_SHIFT));
+
+ regmap_read(amx->regmap, TEGRA210_AMX_AHUBRAMCTL_AMX_CTRL, &val);
+ val |= TEGRA210_AMX_AHUBRAMCTL_AMX_CTRL_ADDR_INIT_EN;
+ regmap_write(amx->regmap, TEGRA210_AMX_AHUBRAMCTL_AMX_CTRL, val);
+ regmap_read(amx->regmap, TEGRA210_AMX_AHUBRAMCTL_AMX_CTRL, &val);
+ val &= ~(TEGRA210_AMX_AHUBRAMCTL_AMX_CTRL_RW_WRITE);
+ regmap_write(amx->regmap, TEGRA210_AMX_AHUBRAMCTL_AMX_CTRL, val);
+
+ do {
+ regmap_read(amx->regmap,
+ TEGRA210_AMX_AHUBRAMCTL_AMX_CTRL, &val);
+ wait--;
+ if (!wait)
+ return -EINVAL;
+ } while (val & 0x80000000);
+
+ regmap_read(amx->regmap, TEGRA210_AMX_AHUBRAMCTL_AMX_DATA, &val);
+
+ return val;
+}
+#endif
+
+static int tegra210_amx_runtime_resume(struct device *dev)
+{
+ struct tegra210_amx *amx = dev_get_drvdata(dev);
+#ifndef CONFIG_MACH_GRENADA
+ int ret;
+
+ ret = clk_prepare_enable(amx->clk_amx);
+ if (ret) {
+ dev_err(dev, "clk_enable failed: %d\n", ret);
+ return ret;
+ }
+#endif
+
+ regcache_cache_only(amx->regmap, false);
+
+ return 0;
+}
+
+static int tegra210_amx_set_audio_cif(struct tegra210_amx *amx,
+ struct snd_pcm_hw_params *params,
+ unsigned int reg)
+{
+ int channels, audio_bits;
+ struct tegra210_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 = TEGRA210_AUDIOCIF_BITS_16;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ audio_bits = TEGRA210_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.truncate = 0;
+ cif_conf.mono_conv = 0;
+
+ amx->soc_data->set_audio_cif(amx->regmap, reg, &cif_conf);
+
+ return 0;
+}
+
+
+static int tegra210_amx_in_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct tegra210_amx *amx = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ ret = tegra210_amx_set_audio_cif(amx, params,
+ TEGRA210_AMX_AXBAR_RX1_CIF_CTRL +
+ (dai->id * TEGRA210_AMX_AUDIOCIF_CH_STRIDE));
+
+ return ret;
+}
+
+static int tegra210_amx_in_trigger(struct snd_pcm_substream *substream,
+ int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct tegra210_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:
+ tegra210_amx_enable_instream(amx, dai->id);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ tegra210_amx_disable_instream(amx, dai->id);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int tegra210_amx_out_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct tegra210_amx *amx = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ ret = tegra210_amx_set_audio_cif(amx, params,
+ TEGRA210_AMX_AXBAR_TX_CIF_CTRL);
+
+ return ret;
+}
+
+int tegra210_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 tegra210_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;
+ }
+
+ tegra210_amx_set_master_stream(amx, 0,
+ TEGRA210_AMX_WAIT_ON_ALL);
+
+ memset(amx->map, 0, sizeof(amx->map));
+
+ 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;
+ tegra210_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;
+ }
+ }
+
+ tegra210_amx_update_map_ram(amx);
+ tegra210_amx_set_out_byte_mask(amx, byte_mask1, byte_mask2);
+
+ return 0;
+}
+
+static int tegra210_amx_codec_probe(struct snd_soc_codec *codec)
+{
+ struct tegra210_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 tegra210_amx_out_dai_ops = {
+ .hw_params = tegra210_amx_out_hw_params,
+ .set_channel_map = tegra210_amx_set_channel_map,
+};
+
+static struct snd_soc_dai_ops tegra210_amx_in_dai_ops = {
+ .hw_params = tegra210_amx_in_hw_params,
+ .trigger = tegra210_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 = &tegra210_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 tegra210_amx_dais[] = {
+ IN_DAI(1),
+ IN_DAI(2),
+ IN_DAI(3),
+ IN_DAI(4),
+ OUT_DAI(OUT, &tegra210_amx_out_dai_ops),
+};
+
+static const struct snd_soc_dapm_widget tegra210_amx_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("IN1", NULL, 0, TEGRA210_AMX_CTRL, 0, 0),
+ SND_SOC_DAPM_AIF_IN("IN2", NULL, 0, TEGRA210_AMX_CTRL, 1, 0),
+ SND_SOC_DAPM_AIF_IN("IN3", NULL, 0, TEGRA210_AMX_CTRL, 2, 0),
+ SND_SOC_DAPM_AIF_IN("IN4", NULL, 0, TEGRA210_AMX_CTRL, 3, 0),
+ SND_SOC_DAPM_AIF_OUT("OUT", NULL, 0, TEGRA210_AMX_ENABLE, 0, 0),
+};
+
+static const struct snd_soc_dapm_route tegra210_amx_routes[] = {
+ { "IN1", NULL, "IN1 Receive" },
+ { "IN2", NULL, "IN2 Receive" },
+ { "IN3", NULL, "IN3 Receive" },
+ { "IN4", NULL, "IN4 Receive" },
+ { "OUT", NULL, "IN1" },
+ { "OUT", NULL, "IN2" },
+ { "OUT", NULL, "IN3" },
+ { "OUT", NULL, "IN4" },
+ { "OUT Transmit", NULL, "OUT" },
+};
+
+static struct snd_soc_codec_driver tegra210_amx_codec = {
+ .probe = tegra210_amx_codec_probe,
+ .dapm_widgets = tegra210_amx_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tegra210_amx_widgets),
+ .dapm_routes = tegra210_amx_routes,
+ .num_dapm_routes = ARRAY_SIZE(tegra210_amx_routes),
+};
+
+static bool tegra210_amx_wr_reg(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_AMX_AXBAR_RX_INT_MASK:
+ case TEGRA210_AMX_AXBAR_RX_INT_SET:
+ case TEGRA210_AMX_AXBAR_RX_INT_CLEAR:
+ case TEGRA210_AMX_AXBAR_RX1_CIF_CTRL:
+ case TEGRA210_AMX_AXBAR_RX2_CIF_CTRL:
+ case TEGRA210_AMX_AXBAR_RX3_CIF_CTRL:
+ case TEGRA210_AMX_AXBAR_RX4_CIF_CTRL:
+ case TEGRA210_AMX_AXBAR_TX_INT_MASK:
+ case TEGRA210_AMX_AXBAR_TX_INT_SET:
+ case TEGRA210_AMX_AXBAR_TX_INT_CLEAR:
+ case TEGRA210_AMX_AXBAR_TX_CIF_CTRL:
+ case TEGRA210_AMX_ENABLE:
+ case TEGRA210_AMX_SOFT_RESET:
+ case TEGRA210_AMX_CG:
+ case TEGRA210_AMX_CTRL:
+ case TEGRA210_AMX_OUT_BYTE_EN0:
+ case TEGRA210_AMX_OUT_BYTE_EN1:
+ case TEGRA210_AMX_CYA:
+ case TEGRA210_AMX_AHUBRAMCTL_AMX_CTRL:
+ case TEGRA210_AMX_AHUBRAMCTL_AMX_DATA:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static bool tegra210_amx_rd_reg(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_AMX_AXBAR_RX_STATUS:
+ case TEGRA210_AMX_AXBAR_RX_INT_STATUS:
+ case TEGRA210_AMX_AXBAR_RX_INT_MASK:
+ case TEGRA210_AMX_AXBAR_RX_INT_SET:
+ case TEGRA210_AMX_AXBAR_RX_INT_CLEAR:
+ case TEGRA210_AMX_AXBAR_RX1_CIF_CTRL:
+ case TEGRA210_AMX_AXBAR_RX2_CIF_CTRL:
+ case TEGRA210_AMX_AXBAR_RX3_CIF_CTRL:
+ case TEGRA210_AMX_AXBAR_RX4_CIF_CTRL:
+ case TEGRA210_AMX_AXBAR_TX_STATUS:
+ case TEGRA210_AMX_AXBAR_TX_INT_STATUS:
+ case TEGRA210_AMX_AXBAR_TX_INT_MASK:
+ case TEGRA210_AMX_AXBAR_TX_INT_SET:
+ case TEGRA210_AMX_AXBAR_TX_INT_CLEAR:
+ case TEGRA210_AMX_AXBAR_TX_CIF_CTRL:
+ case TEGRA210_AMX_ENABLE:
+ case TEGRA210_AMX_SOFT_RESET:
+ case TEGRA210_AMX_CG:
+ case TEGRA210_AMX_STATUS:
+ case TEGRA210_AMX_INT_STATUS:
+ case TEGRA210_AMX_CTRL:
+ case TEGRA210_AMX_OUT_BYTE_EN0:
+ case TEGRA210_AMX_OUT_BYTE_EN1:
+ case TEGRA210_AMX_CYA:
+ case TEGRA210_AMX_DBG:
+ case TEGRA210_AMX_AHUBRAMCTL_AMX_CTRL:
+ case TEGRA210_AMX_AHUBRAMCTL_AMX_DATA:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static const struct regmap_config tegra210_amx_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = TEGRA210_AMX_AHUBRAMCTL_AMX_DATA,
+ .writeable_reg = tegra210_amx_wr_reg,
+ .readable_reg = tegra210_amx_rd_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct tegra210_amx_soc_data soc_data_tegra210 = {
+ .set_audio_cif = tegra210_xbar_set_cif
+};
+
+static const struct of_device_id tegra210_amx_of_match[] = {
+ { .compatible = "nvidia,tegra210-amx", .data = &soc_data_tegra210 },
+ {},
+};
+
+static int tegra210_amx_platform_probe(struct platform_device *pdev)
+{
+ struct tegra210_amx *amx;
+ struct resource *mem, *memregion;
+ void __iomem *regs;
+ int ret;
+ const struct of_device_id *match;
+ struct tegra210_amx_soc_data *soc_data;
+
+ match = of_match_device(tegra210_amx_of_match, &pdev->dev);
+ if (!match) {
+ dev_err(&pdev->dev, "Error: No device match found\n");
+ ret = -ENODEV;
+ goto err;
+ }
+ soc_data = (struct tegra210_amx_soc_data *)match->data;
+
+ amx = devm_kzalloc(&pdev->dev, sizeof(struct tegra210_amx), GFP_KERNEL);
+ if (!amx) {
+ dev_err(&pdev->dev, "Can't allocate tegra210_amx\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+ dev_set_drvdata(&pdev->dev, amx);
+
+ amx->soc_data = soc_data;
+
+#ifndef CONFIG_MACH_GRENADA
+ amx->clk_amx = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(amx->clk_amx)) {
+ dev_err(&pdev->dev, "Can't retrieve tegra210_amx clock\n");
+ ret = PTR_ERR(amx->clk_amx);
+ goto err;
+ }
+#endif
+
+ 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,
+ &tegra210_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);
+
+ if (of_property_read_u32(pdev->dev.of_node,
+ "nvidia,ahub-amx-id",
+ &pdev->dev.id) < 0) {
+ dev_err(&pdev->dev,
+ "Missing property nvidia,ahub-amx-id\n");
+ ret = -ENODEV;
+ goto err_clk_put;
+ }
+
+ pm_runtime_enable(&pdev->dev);
+ if (!pm_runtime_enabled(&pdev->dev)) {
+ ret = tegra210_amx_runtime_resume(&pdev->dev);
+ if (ret)
+ goto err_pm_disable;
+ }
+
+ ret = snd_soc_register_codec(&pdev->dev, &tegra210_amx_codec,
+ tegra210_amx_dais,
+ ARRAY_SIZE(tegra210_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))
+ tegra210_amx_runtime_suspend(&pdev->dev);
+err_pm_disable:
+ pm_runtime_disable(&pdev->dev);
+err_clk_put:
+#ifndef CONFIG_MACH_GRENADA
+ devm_clk_put(&pdev->dev, amx->clk_amx);
+#else
+ dev_err(&pdev->dev, "error\n");
+#endif
+err:
+ return ret;
+}
+
+static int tegra210_amx_platform_remove(struct platform_device *pdev)
+{
+#ifndef CONFIG_MACH_GRENADA
+ struct tegra210_amx *amx = dev_get_drvdata(&pdev->dev);
+#endif
+
+ snd_soc_unregister_codec(&pdev->dev);
+
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ tegra210_amx_runtime_suspend(&pdev->dev);
+
+#ifndef CONFIG_MACH_GRENADA
+ devm_clk_put(&pdev->dev, amx->clk_amx);
+#endif
+
+ return 0;
+}
+
+static const struct dev_pm_ops tegra210_amx_pm_ops = {
+ SET_RUNTIME_PM_OPS(tegra210_amx_runtime_suspend,
+ tegra210_amx_runtime_resume, NULL)
+};
+
+static struct platform_driver tegra210_amx_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = tegra210_amx_of_match,
+ .pm = &tegra210_amx_pm_ops,
+ },
+ .probe = tegra210_amx_platform_probe,
+ .remove = tegra210_amx_platform_remove,
+};
+module_platform_driver(tegra210_amx_driver);
+
+MODULE_AUTHOR("Songhee Baek ");
+MODULE_DESCRIPTION("Tegra210 AMX ASoC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, tegra210_amx_of_match);
diff --git a/sound/soc/tegra-alt/tegra210_i2s_alt.c b/sound/soc/tegra-alt/tegra210_i2s_alt.c
new file mode 100644
index 00000000..01199e08
--- /dev/null
+++ b/sound/soc/tegra-alt/tegra210_i2s_alt.c
@@ -0,0 +1,664 @@
+/*
+ * tegra210_i2s.c - Tegra210 I2S driver
+ *
+ * Copyright (c) 2014 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 .
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "tegra210_xbar_alt.h"
+#include "tegra210_i2s_alt.h"
+
+#define DRV_NAME "tegra210-i2s"
+
+static int tegra210_i2s_set_clock_rate(struct device *dev, int clock_rate)
+{
+#ifndef CONFIG_MACH_GRENADA
+ unsigned int val;
+ struct tegra210_i2s *i2s = dev_get_drvdata(dev);
+ int ret;
+
+ regmap_read(i2s->regmap, TEGRA210_I2S_CTRL, &val);
+
+ if ((val & TEGRA210_I2S_CTRL_MASTER_EN_MASK) ==
+ TEGRA210_I2S_CTRL_MASTER_EN) {
+ 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(i2s->clk_i2s, i2s->clk_i2s_sync);
+ if (ret) {
+ dev_err(dev, "Can't set parent of i2s clock\n");
+ return ret;
+ }
+ }
+
+ return ret;
+#else
+ return 0;
+#endif
+}
+
+static int tegra210_i2s_runtime_suspend(struct device *dev)
+{
+ struct tegra210_i2s *i2s = dev_get_drvdata(dev);
+
+ regcache_cache_only(i2s->regmap, true);
+ clk_disable_unprepare(i2s->clk_i2s);
+
+ return 0;
+}
+
+static int tegra210_i2s_runtime_resume(struct device *dev)
+{
+ struct tegra210_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 tegra210_i2s_set_fmt(struct snd_soc_dai *dai,
+ unsigned int fmt)
+{
+ struct tegra210_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+ unsigned int mask, val;
+
+ mask = TEGRA210_I2S_CTRL_EDGE_CTRL_MASK;
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ val = TEGRA210_I2S_CTRL_EDGE_CTRL_POS_EDGE;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ val = TEGRA210_I2S_CTRL_EDGE_CTRL_NEG_EDGE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ mask |= TEGRA210_I2S_CTRL_MASTER_EN_MASK;
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ val &= ~(TEGRA210_I2S_CTRL_MASTER_EN);
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ val |= TEGRA210_I2S_CTRL_MASTER_EN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ mask |= TEGRA210_I2S_CTRL_FRAME_FORMAT_MASK |
+ TEGRA210_I2S_CTRL_LRCK_POLARITY_MASK;
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ val |= TEGRA210_I2S_CTRL_FRAME_FORMAT_FSYNC_MODE;
+ val |= TEGRA210_I2S_CTRL_LRCK_POLARITY_LOW;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ val |= TEGRA210_I2S_CTRL_FRAME_FORMAT_FSYNC_MODE;
+ val |= TEGRA210_I2S_CTRL_LRCK_POLARITY_HIGH;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ val |= TEGRA210_I2S_CTRL_FRAME_FORMAT_LRCK_MODE;
+ val |= TEGRA210_I2S_CTRL_LRCK_POLARITY_LOW;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ val |= TEGRA210_I2S_CTRL_FRAME_FORMAT_LRCK_MODE;
+ val |= TEGRA210_I2S_CTRL_LRCK_POLARITY_LOW;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ val |= TEGRA210_I2S_CTRL_FRAME_FORMAT_LRCK_MODE;
+ val |= TEGRA210_I2S_CTRL_LRCK_POLARITY_LOW;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ pm_runtime_get_sync(dai->dev);
+ regmap_update_bits(i2s->regmap, TEGRA210_I2S_CTRL, mask, val);
+ /* FIXME: global enabling */
+ regmap_update_bits(i2s->regmap, TEGRA210_I2S_ENABLE,
+ TEGRA210_I2S_EN_MASK, TEGRA210_I2S_EN);
+
+ pm_runtime_put(dai->dev);
+
+ return 0;
+}
+
+static int tegra210_i2s_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct tegra210_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+ i2s->srate = freq;
+
+ return 0;
+}
+
+static int tegra210_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 tegra210_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+ unsigned int mask, val, reg, frame_format;
+ int ret, sample_size, channels, srate, i2sclock, bitcnt;
+ struct tegra210_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 = TEGRA210_I2S_CTRL_BIT_SIZE_MASK;
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ val = TEGRA210_I2S_CTRL_BIT_SIZE_16;
+ sample_size = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ val = TEGRA210_I2S_CTRL_BIT_SIZE_24;
+ sample_size = 24;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ val = TEGRA210_I2S_CTRL_BIT_SIZE_32;
+ sample_size = 32;
+ break;
+ default:
+ dev_err(dev, "Wrong format!\n");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(i2s->regmap, TEGRA210_I2S_CTRL, mask, val);
+
+ srate = i2s->srate;
+
+ regmap_read(i2s->regmap, TEGRA210_I2S_CTRL, &val);
+
+ frame_format = val & TEGRA210_I2S_CTRL_FRAME_FORMAT_MASK;
+
+ if (frame_format == TEGRA210_I2S_CTRL_FRAME_FORMAT_FSYNC_MODE) {
+ i2sclock = srate * channels * sample_size;
+ regmap_write(i2s->regmap, TEGRA210_I2S_SLOT_CTRL, channels - 1);
+ regmap_write(i2s->regmap, TEGRA210_I2S_AXBAR_TX_SLOT_CTRL,
+ ((1 << channels) - 1));
+ regmap_write(i2s->regmap, TEGRA210_I2S_AXBAR_RX_SLOT_CTRL,
+ ((1 << channels) - 1));
+ } else
+ i2sclock = srate * channels * sample_size * 2;
+
+ bitcnt = (i2sclock / srate) - 1;
+ if ((bitcnt < 0) ||
+ (bitcnt > TEGRA210_I2S_TIMING_CHANNEL_BIT_CNT_MASK)) {
+ dev_err(dev, "Can't set channel bit count\n");
+ return -EINVAL;
+ }
+
+ ret = tegra210_i2s_set_clock_rate(dev, i2sclock);
+ if (ret) {
+ dev_err(dev, "Can't set I2S clock rate: %d\n", ret);
+ return ret;
+ }
+
+ if ((frame_format == TEGRA210_I2S_FRAME_FORMAT_LRCK) && (channels > 1)) {
+ val = (bitcnt >> 1) <<
+ TEGRA210_I2S_TIMING_CHANNEL_BIT_CNT_SHIFT;
+ } else
+ val = bitcnt << TEGRA210_I2S_TIMING_CHANNEL_BIT_CNT_SHIFT;
+
+ if (i2sclock % (2 * srate))
+ val |= TEGRA210_I2S_TIMING_NON_SYM_EN;
+
+ regmap_write(i2s->regmap, TEGRA210_I2S_TIMING, val);
+
+ regmap_update_bits(i2s->regmap, TEGRA210_I2S_CTRL,
+ TEGRA210_I2S_CTRL_FSYNC_WIDTH_MASK,
+ 31 << TEGRA210_I2S_CTRL_FSYNC_WIDTH_SHIFT);
+
+ cif_conf.threshold = 0;
+ cif_conf.audio_channels = channels;
+ cif_conf.client_channels = channels;
+ cif_conf.audio_bits = (sample_size == 16 ? TEGRA210_AUDIOCIF_BITS_16 :
+ TEGRA210_AUDIOCIF_BITS_32);
+
+ cif_conf.client_bits = (sample_size == 16 ? TEGRA210_AUDIOCIF_BITS_16 :
+ TEGRA210_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) {
+ regmap_update_bits(i2s->regmap, TEGRA210_I2S_AXBAR_RX_CTRL,
+ TEGRA210_I2S_AXBAR_RX_CTRL_DATA_OFFSET_MASK,
+ (1 << TEGRA210_I2S_AXBAR_RX_CTRL_DATA_OFFSET_SHIFT));
+
+ reg = TEGRA210_I2S_AXBAR_RX_CIF_CTRL;
+ } else {
+ regmap_update_bits(i2s->regmap, TEGRA210_I2S_AXBAR_TX_CTRL,
+ TEGRA210_I2S_AXBAR_TX_CTRL_DATA_OFFSET_MASK,
+ (1 << TEGRA210_I2S_AXBAR_TX_CTRL_DATA_OFFSET_SHIFT));
+
+ reg = TEGRA210_I2S_AXBAR_TX_CIF_CTRL;
+ }
+
+ i2s->soc_data->set_audio_cif(i2s->regmap, reg, &cif_conf);
+
+ return 0;
+}
+
+static int tegra210_i2s_codec_probe(struct snd_soc_codec *codec)
+{
+ struct tegra210_i2s *i2s = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ codec->control_data = i2s->regmap;
+ ret = snd_soc_codec_set_cache_io(codec, 64, 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 tegra210_i2s_dai_ops = {
+ .set_fmt = tegra210_i2s_set_fmt,
+ .hw_params = tegra210_i2s_hw_params,
+ .set_sysclk = tegra210_i2s_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_driver tegra210_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 = &tegra210_i2s_dai_ops,
+ .symmetric_rates = 1,
+ }
+};
+
+static const struct snd_kcontrol_new tegra210_i2s_controls[] = {
+ SOC_SINGLE("Loopback", TEGRA210_I2S_CTRL, 8, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget tegra210_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, TEGRA210_I2S_AXBAR_TX_ENABLE,
+ TEGRA210_I2S_AXBAR_TX_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("DAP TX", NULL, 0, TEGRA210_I2S_AXBAR_RX_ENABLE,
+ TEGRA210_I2S_AXBAR_RX_EN_SHIFT, 0),
+};
+
+static const struct snd_soc_dapm_route tegra210_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 tegra210_i2s_codec = {
+ .probe = tegra210_i2s_codec_probe,
+ .dapm_widgets = tegra210_i2s_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tegra210_i2s_widgets),
+ .dapm_routes = tegra210_i2s_routes,
+ .num_dapm_routes = ARRAY_SIZE(tegra210_i2s_routes),
+ .controls = tegra210_i2s_controls,
+ .num_controls = ARRAY_SIZE(tegra210_i2s_controls),
+};
+
+static bool tegra210_i2s_wr_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_I2S_AXBAR_RX_ENABLE:
+ case TEGRA210_I2S_AXBAR_RX_INT_MASK:
+ case TEGRA210_I2S_AXBAR_RX_INT_SET:
+ case TEGRA210_I2S_AXBAR_RX_INT_CLEAR:
+ case TEGRA210_I2S_AXBAR_RX_CIF_CTRL:
+ case TEGRA210_I2S_AXBAR_RX_CTRL:
+ case TEGRA210_I2S_AXBAR_RX_SLOT_CTRL:
+ case TEGRA210_I2S_AXBAR_RX_CLK_TRIM:
+ case TEGRA210_I2S_AXBAR_TX_ENABLE:
+ case TEGRA210_I2S_AXBAR_TX_INT_MASK:
+ case TEGRA210_I2S_AXBAR_TX_INT_SET:
+ case TEGRA210_I2S_AXBAR_TX_INT_CLEAR:
+ case TEGRA210_I2S_AXBAR_TX_CIF_CTRL:
+ case TEGRA210_I2S_AXBAR_TX_CTRL:
+ case TEGRA210_I2S_AXBAR_TX_SLOT_CTRL:
+ case TEGRA210_I2S_AXBAR_TX_CLK_TRIM:
+ case TEGRA210_I2S_ENABLE:
+ case TEGRA210_I2S_SOFT_RESET:
+ case TEGRA210_I2S_CG:
+ case TEGRA210_I2S_CTRL:
+ case TEGRA210_I2S_TIMING:
+ case TEGRA210_I2S_SLOT_CTRL:
+ case TEGRA210_I2S_CLK_TRIM:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static bool tegra210_i2s_rd_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_I2S_AXBAR_RX_STATUS:
+ case TEGRA210_I2S_AXBAR_RX_CIF_FIFO_STATUS:
+ case TEGRA210_I2S_AXBAR_RX_ENABLE:
+ case TEGRA210_I2S_AXBAR_RX_INT_MASK:
+ case TEGRA210_I2S_AXBAR_RX_INT_SET:
+ case TEGRA210_I2S_AXBAR_RX_INT_CLEAR:
+ case TEGRA210_I2S_AXBAR_RX_CIF_CTRL:
+ case TEGRA210_I2S_AXBAR_RX_CTRL:
+ case TEGRA210_I2S_AXBAR_RX_SLOT_CTRL:
+ case TEGRA210_I2S_AXBAR_RX_CLK_TRIM:
+ case TEGRA210_I2S_AXBAR_RX_INT_STATUS:
+ case TEGRA210_I2S_AXBAR_RX_SOFT_RESET:
+ case TEGRA210_I2S_AXBAR_TX_STATUS:
+ case TEGRA210_I2S_AXBAR_TX_CIF_FIFO_STATUS:
+ case TEGRA210_I2S_AXBAR_TX_ENABLE:
+ case TEGRA210_I2S_AXBAR_TX_INT_MASK:
+ case TEGRA210_I2S_AXBAR_TX_INT_SET:
+ case TEGRA210_I2S_AXBAR_TX_INT_CLEAR:
+ case TEGRA210_I2S_AXBAR_TX_CIF_CTRL:
+ case TEGRA210_I2S_AXBAR_TX_CTRL:
+ case TEGRA210_I2S_AXBAR_TX_SLOT_CTRL:
+ case TEGRA210_I2S_AXBAR_TX_CLK_TRIM:
+ case TEGRA210_I2S_AXBAR_TX_INT_STATUS:
+ case TEGRA210_I2S_AXBAR_TX_SOFT_RESET:
+ case TEGRA210_I2S_ENABLE:
+ case TEGRA210_I2S_STATUS:
+ case TEGRA210_I2S_SOFT_RESET:
+ case TEGRA210_I2S_CG:
+ case TEGRA210_I2S_CTRL:
+ case TEGRA210_I2S_TIMING:
+ case TEGRA210_I2S_SLOT_CTRL:
+ case TEGRA210_I2S_CLK_TRIM:
+ case TEGRA210_I2S_INT_STATUS:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static bool tegra210_i2s_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_I2S_AXBAR_RX_INT_STATUS:
+ case TEGRA210_I2S_AXBAR_TX_INT_STATUS:
+ case TEGRA210_I2S_INT_STATUS:
+ case TEGRA210_I2S_AXBAR_RX_SOFT_RESET:
+ case TEGRA210_I2S_AXBAR_TX_SOFT_RESET:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static const struct regmap_config tegra210_i2s_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = TEGRA210_I2S_CLK_TRIM,
+ .writeable_reg = tegra210_i2s_wr_reg,
+ .readable_reg = tegra210_i2s_rd_reg,
+ .volatile_reg = tegra210_i2s_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct tegra210_i2s_soc_data soc_data_tegra210 = {
+ .set_audio_cif = tegra210_xbar_set_cif,
+};
+
+static const struct of_device_id tegra210_i2s_of_match[] = {
+ { .compatible = "nvidia,tegra210-i2s", .data = &soc_data_tegra210 },
+ {},
+};
+
+static int tegra210_i2s_platform_probe(struct platform_device *pdev)
+{
+ struct tegra210_i2s *i2s;
+ struct resource *mem, *memregion;
+ void __iomem *regs;
+ int ret = 0;
+ const struct of_device_id *match;
+ struct tegra210_i2s_soc_data *soc_data;
+
+ match = of_match_device(tegra210_i2s_of_match, &pdev->dev);
+ if (!match) {
+ dev_err(&pdev->dev, "Error: No device match found\n");
+ ret = -ENODEV;
+ goto err;
+ }
+ soc_data = (struct tegra210_i2s_soc_data *)match->data;
+
+ i2s = devm_kzalloc(&pdev->dev, sizeof(struct tegra210_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_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_i2s_sync_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), pdev->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,
+ &tegra210_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);
+
+ if (of_property_read_u32(pdev->dev.of_node,
+ "nvidia,ahub-i2s-id",
+ &pdev->dev.id) < 0) {
+ dev_err(&pdev->dev,
+ "Missing property nvidia,ahub-i2s-id\n");
+ ret = -ENODEV;
+ goto err_pll_a_out0_clk_put;
+ }
+
+ pm_runtime_enable(&pdev->dev);
+ if (!pm_runtime_enabled(&pdev->dev)) {
+ ret = tegra210_i2s_runtime_resume(&pdev->dev);
+ if (ret)
+ goto err_pm_disable;
+ }
+
+ ret = snd_soc_register_codec(&pdev->dev, &tegra210_i2s_codec,
+ tegra210_i2s_dais,
+ ARRAY_SIZE(tegra210_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))
+ tegra210_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_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 tegra210_i2s_platform_remove(struct platform_device *pdev)
+{
+ struct tegra210_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))
+ tegra210_i2s_runtime_suspend(&pdev->dev);
+
+ devm_clk_put(&pdev->dev, i2s->clk_i2s);
+ devm_clk_put(&pdev->dev, i2s->clk_i2s_sync);
+ clk_put(i2s->clk_pll_a_out0);
+
+ return 0;
+}
+
+static const struct dev_pm_ops tegra210_i2s_pm_ops = {
+ SET_RUNTIME_PM_OPS(tegra210_i2s_runtime_suspend,
+ tegra210_i2s_runtime_resume, NULL)
+};
+
+static struct platform_driver tegra210_i2s_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = tegra210_i2s_of_match,
+ .pm = &tegra210_i2s_pm_ops,
+ },
+ .probe = tegra210_i2s_platform_probe,
+ .remove = tegra210_i2s_platform_remove,
+};
+module_platform_driver(tegra210_i2s_driver)
+
+MODULE_AUTHOR("Songhee Baek ");
+MODULE_DESCRIPTION("Tegra210 I2S ASoC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, tegra210_i2s_of_match);
diff --git a/sound/soc/tegra-alt/tegra210_mixer_alt.c b/sound/soc/tegra-alt/tegra210_mixer_alt.c
new file mode 100644
index 00000000..be8d8898
--- /dev/null
+++ b/sound/soc/tegra-alt/tegra210_mixer_alt.c
@@ -0,0 +1,706 @@
+/*
+ * tegra210_mixer_alt.c - Tegra210 MIXER driver
+ *
+ * Copyright (c) 2014 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 .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "tegra210_xbar_alt.h"
+#include "tegra210_mixer_alt.h"
+
+#define DRV_NAME "tegra210_mixer"
+
+#define MIXER_GAIN_CONFIG_RAM_ADDR(id) \
+ (TEGRA210_MIXER_AHUBRAMCTL_GAIN_CONFIG_RAM_ADDR_0 + \
+ id*TEGRA210_MIXER_AHUBRAMCTL_GAIN_CONFIG_RAM_ADDR_STRIDE)
+
+static int tegra210_mixer_runtime_suspend(struct device *dev)
+{
+ struct tegra210_mixer *mixer = dev_get_drvdata(dev);
+
+ regcache_cache_only(mixer->regmap, true);
+
+#ifndef CONFIG_MACH_GRENADA
+ clk_disable_unprepare(mixer->clk_mixer);
+#endif
+
+ return 0;
+}
+
+static int tegra210_mixer_runtime_resume(struct device *dev)
+{
+ struct tegra210_mixer *mixer = dev_get_drvdata(dev);
+#ifndef CONFIG_MACH_GRENADA
+ int ret;
+
+ ret = clk_prepare_enable(mixer->clk_mixer);
+ if (ret) {
+ dev_err(dev, "clk_enable failed: %d\n", ret);
+ return ret;
+ }
+#endif
+
+ regcache_cache_only(mixer->regmap, false);
+
+ return 0;
+}
+
+static int tegra210_mixer_write_ram(struct tegra210_mixer *mixer,
+ unsigned int addr,
+ unsigned int val)
+{
+ unsigned int reg, value, wait = 0xffff;
+
+ /* check if busy */
+ do {
+ regmap_read(mixer->regmap,
+ TEGRA210_MIXER_AHUBRAMCTL_GAIN_CONFIG_RAM_CTRL, &value);
+ wait--;
+ if (!wait)
+ return -EINVAL;
+ } while (value & 0x80000000);
+ value = 0;
+
+ reg = (addr << TEGRA210_MIXER_AHUBRAMCTL_GAIN_CONFIG_RAM_CTRL_RAM_ADDR_SHIFT) &
+ TEGRA210_MIXER_AHUBRAMCTL_GAIN_CONFIG_RAM_CTRL_RAM_ADDR_MASK;
+ reg |= TEGRA210_MIXER_AHUBRAMCTL_GAIN_CONFIG_RAM_CTRL_ADDR_INIT_EN;
+ reg |= TEGRA210_MIXER_AHUBRAMCTL_GAIN_CONFIG_RAM_CTRL_RW_WRITE;
+ reg |= TEGRA210_MIXER_AHUBRAMCTL_GAIN_CONFIG_RAM_CTRL_SEQ_ACCESS_EN;
+
+ regmap_write(mixer->regmap,
+ TEGRA210_MIXER_AHUBRAMCTL_GAIN_CONFIG_RAM_CTRL, reg);
+ regmap_write(mixer->regmap,
+ TEGRA210_MIXER_AHUBRAMCTL_GAIN_CONFIG_RAM_DATA, val);
+
+ return 0;
+}
+
+static int tegra210_mixer_get_gain(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return 0;
+}
+
+static int tegra210_mixer_put_gain(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct tegra210_mixer *mixer = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg = mc->reg;
+ unsigned int ret;
+
+ /* set the gain and trigger config */
+ ret = tegra210_mixer_write_ram(mixer, reg + 0x09,
+ ucontrol->value.integer.value[0]);
+ ret |= tegra210_mixer_write_ram(mixer, reg + 0x0f,
+ ucontrol->value.integer.value[0]);
+
+ return ret;
+}
+
+static int tegra210_mixer_set_audio_cif(struct tegra210_mixer *mixer,
+ struct snd_pcm_hw_params *params,
+ unsigned int reg)
+{
+ int channels, audio_bits;
+ struct tegra210_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 = TEGRA210_AUDIOCIF_BITS_16;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ audio_bits = TEGRA210_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.truncate = 0;
+ cif_conf.mono_conv = 0;
+
+ mixer->soc_data->set_audio_cif(mixer->regmap, reg, &cif_conf);
+ return 0;
+}
+
+
+static int tegra210_mixer_in_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct tegra210_mixer *mixer = snd_soc_dai_get_drvdata(dai);
+ int ret, i;
+
+ ret = tegra210_mixer_set_audio_cif(mixer, params,
+ TEGRA210_MIXER_AXBAR_RX1_CIF_CTRL +
+ (dai->id * TEGRA210_MIXER_AXBAR_RX_STRIDE));
+
+ /* write the gain config poly coefficients */
+ for (i = 0; i < 14; i++) {
+ tegra210_mixer_write_ram (mixer,
+ MIXER_GAIN_CONFIG_RAM_ADDR(dai->id) + i,
+ mixer->gain_coeff[i]);
+ }
+
+ /* trigger the polynomial configuration */
+ tegra210_mixer_write_ram (mixer,
+ MIXER_GAIN_CONFIG_RAM_ADDR(dai->id) + 0xf,
+ 0x01);
+
+ return ret;
+}
+
+static int tegra210_mixer_out_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct tegra210_mixer *mixer = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ ret = tegra210_mixer_set_audio_cif(mixer, params,
+ TEGRA210_MIXER_AXBAR_TX1_CIF_CTRL +
+ ((dai->id-10) * TEGRA210_MIXER_AXBAR_TX_STRIDE));
+
+ return ret;
+}
+
+static int tegra210_mixer_codec_probe(struct snd_soc_codec *codec)
+{
+ struct tegra210_mixer *mixer = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ codec->control_data = mixer->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 tegra210_mixer_out_dai_ops = {
+ .hw_params = tegra210_mixer_out_hw_params,
+};
+
+static struct snd_soc_dai_ops tegra210_mixer_in_dai_ops = {
+ .hw_params = tegra210_mixer_in_hw_params,
+};
+
+#define IN_DAI(sname, id, dai_ops) \
+ { \
+ .name = #sname #id, \
+ .playback = { \
+ .stream_name = #sname #id " Receive", \
+ .channels_min = 1, \
+ .channels_max = 2, \
+ .rates = SNDRV_PCM_RATE_8000_96000, \
+ .formats = SNDRV_PCM_FMTBIT_S16_LE, \
+ }, \
+ .ops = dai_ops, \
+ }
+
+#define OUT_DAI(sname, id, dai_ops) \
+ { \
+ .name = #sname #id, \
+ .capture = { \
+ .stream_name = #sname #id " Transmit", \
+ .channels_min = 1, \
+ .channels_max = 2, \
+ .rates = SNDRV_PCM_RATE_8000_96000, \
+ .formats = SNDRV_PCM_FMTBIT_S16_LE, \
+ }, \
+ .ops = dai_ops, \
+ }
+
+static struct snd_soc_dai_driver tegra210_mixer_dais[] = {
+ IN_DAI(RX, 1, &tegra210_mixer_in_dai_ops),
+ IN_DAI(RX, 2, &tegra210_mixer_in_dai_ops),
+ IN_DAI(RX, 3, &tegra210_mixer_in_dai_ops),
+ IN_DAI(RX, 4, &tegra210_mixer_in_dai_ops),
+ IN_DAI(RX, 5, &tegra210_mixer_in_dai_ops),
+ IN_DAI(RX, 6, &tegra210_mixer_in_dai_ops),
+ IN_DAI(RX, 7, &tegra210_mixer_in_dai_ops),
+ IN_DAI(RX, 8, &tegra210_mixer_in_dai_ops),
+ IN_DAI(RX, 9, &tegra210_mixer_in_dai_ops),
+ IN_DAI(RX, 10, &tegra210_mixer_in_dai_ops),
+ OUT_DAI(TX, 1, &tegra210_mixer_out_dai_ops),
+ OUT_DAI(TX, 2, &tegra210_mixer_out_dai_ops),
+ OUT_DAI(TX, 3, &tegra210_mixer_out_dai_ops),
+ OUT_DAI(TX, 4, &tegra210_mixer_out_dai_ops),
+ OUT_DAI(TX, 5, &tegra210_mixer_out_dai_ops),
+};
+
+#define ADDER_CTRL_DECL(name, reg) \
+ static const struct snd_kcontrol_new name[] = { \
+ SOC_DAPM_SINGLE("RX1", reg, 0, 1, 0), \
+ SOC_DAPM_SINGLE("RX2", reg, 1, 1, 0), \
+ SOC_DAPM_SINGLE("RX3", reg, 2, 1, 0), \
+ SOC_DAPM_SINGLE("RX4", reg, 3, 1, 0), \
+ SOC_DAPM_SINGLE("RX5", reg, 4, 1, 0), \
+ SOC_DAPM_SINGLE("RX6", reg, 5, 1, 0), \
+ SOC_DAPM_SINGLE("RX7", reg, 6, 1, 0), \
+ SOC_DAPM_SINGLE("RX8", reg, 7, 1, 0), \
+ SOC_DAPM_SINGLE("RX9", reg, 8, 1, 0), \
+ SOC_DAPM_SINGLE("RX10", reg, 9, 1, 0), \
+ }
+
+ADDER_CTRL_DECL(adder1, TEGRA210_MIXER_AXBAR_TX1_ADDER_CONFIG);
+ADDER_CTRL_DECL(adder2, TEGRA210_MIXER_AXBAR_TX2_ADDER_CONFIG);
+ADDER_CTRL_DECL(adder3, TEGRA210_MIXER_AXBAR_TX3_ADDER_CONFIG);
+ADDER_CTRL_DECL(adder4, TEGRA210_MIXER_AXBAR_TX4_ADDER_CONFIG);
+ADDER_CTRL_DECL(adder5, TEGRA210_MIXER_AXBAR_TX5_ADDER_CONFIG);
+
+static const struct snd_kcontrol_new tegra210_mixer_gain_ctls[] = { \
+ SOC_SINGLE_EXT("RX1 Gain", MIXER_GAIN_CONFIG_RAM_ADDR(0), 0, 0x20000, 0,
+ tegra210_mixer_get_gain, tegra210_mixer_put_gain),
+ SOC_SINGLE_EXT("RX2 Gain", MIXER_GAIN_CONFIG_RAM_ADDR(1), 0, 0x20000, 0,
+ tegra210_mixer_get_gain, tegra210_mixer_put_gain),
+ SOC_SINGLE_EXT("RX3 Gain", MIXER_GAIN_CONFIG_RAM_ADDR(2), 0, 0x20000, 0,
+ tegra210_mixer_get_gain, tegra210_mixer_put_gain),
+ SOC_SINGLE_EXT("RX4 Gain", MIXER_GAIN_CONFIG_RAM_ADDR(3), 0, 0x20000, 0,
+ tegra210_mixer_get_gain, tegra210_mixer_put_gain),
+ SOC_SINGLE_EXT("RX5 Gain", MIXER_GAIN_CONFIG_RAM_ADDR(4), 0, 0x20000, 0,
+ tegra210_mixer_get_gain, tegra210_mixer_put_gain),
+ SOC_SINGLE_EXT("RX6 Gain", MIXER_GAIN_CONFIG_RAM_ADDR(5), 0, 0x20000, 0,
+ tegra210_mixer_get_gain, tegra210_mixer_put_gain),
+ SOC_SINGLE_EXT("RX7 Gain", MIXER_GAIN_CONFIG_RAM_ADDR(6), 0, 0x20000, 0,
+ tegra210_mixer_get_gain, tegra210_mixer_put_gain),
+ SOC_SINGLE_EXT("RX8 Gain", MIXER_GAIN_CONFIG_RAM_ADDR(7), 0, 0x20000, 0,
+ tegra210_mixer_get_gain, tegra210_mixer_put_gain),
+ SOC_SINGLE_EXT("RX9 Gain", MIXER_GAIN_CONFIG_RAM_ADDR(8), 0, 0x20000, 0,
+ tegra210_mixer_get_gain, tegra210_mixer_put_gain),
+ SOC_SINGLE_EXT("RX10 Gain", MIXER_GAIN_CONFIG_RAM_ADDR(9), 0, 0x20000, 0,
+ tegra210_mixer_get_gain, tegra210_mixer_put_gain),
+ SOC_SINGLE("Mixer Enable", TEGRA210_MIXER_ENABLE, 0, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget tegra210_mixer_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("RX1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("RX2", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("RX3", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("RX4", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("RX5", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("RX6", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("RX7", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("RX8", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("RX9", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("RX10", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TX1", NULL, 0,
+ TEGRA210_MIXER_AXBAR_TX1_ENABLE, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TX2", NULL, 0,
+ TEGRA210_MIXER_AXBAR_TX2_ENABLE, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TX3", NULL, 0,
+ TEGRA210_MIXER_AXBAR_TX3_ENABLE, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TX4", NULL, 0,
+ TEGRA210_MIXER_AXBAR_TX4_ENABLE, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TX5", NULL, 0,
+ TEGRA210_MIXER_AXBAR_TX5_ENABLE, 0, 0),
+ SND_SOC_DAPM_MIXER("Adder1", SND_SOC_NOPM, 1, 0,
+ adder1, ARRAY_SIZE(adder1)),
+ SND_SOC_DAPM_MIXER("Adder2", SND_SOC_NOPM, 1, 0,
+ adder2, ARRAY_SIZE(adder2)),
+ SND_SOC_DAPM_MIXER("Adder3", SND_SOC_NOPM, 1, 0,
+ adder3, ARRAY_SIZE(adder3)),
+ SND_SOC_DAPM_MIXER("Adder4", SND_SOC_NOPM, 1, 0,
+ adder4, ARRAY_SIZE(adder4)),
+ SND_SOC_DAPM_MIXER("Adder5", SND_SOC_NOPM, 1, 0,
+ adder5, ARRAY_SIZE(adder5)),
+};
+
+#define MIXER_ROUTES(name, id) \
+ {name, "RX1", "RX1",}, \
+ {name, "RX2", "RX2",}, \
+ {name, "RX3", "RX3",}, \
+ {name, "RX4", "RX4",}, \
+ {name, "RX5", "RX5",}, \
+ {name, "RX6", "RX6",}, \
+ {name, "RX7", "RX7",}, \
+ {name, "RX8", "RX8",}, \
+ {name, "RX9", "RX9",}, \
+ {name, "RX10", "RX10"}, \
+ {"TX"#id, NULL, name}
+
+static const struct snd_soc_dapm_route tegra210_mixer_routes[] = {
+ { "RX1", NULL, "RX1 Receive" },
+ { "RX2", NULL, "RX2 Receive" },
+ { "RX3", NULL, "RX3 Receive" },
+ { "RX4", NULL, "RX4 Receive" },
+ { "RX5", NULL, "RX5 Receive" },
+ { "RX6", NULL, "RX6 Receive" },
+ { "RX7", NULL, "RX7 Receive" },
+ { "RX8", NULL, "RX8 Receive" },
+ { "RX9", NULL, "RX9 Receive" },
+ { "RX10", NULL, "RX10 Receive" },
+ /* route between MIXER RXs and TXs */
+ MIXER_ROUTES("Adder1", 1),
+ MIXER_ROUTES("Adder2", 2),
+ MIXER_ROUTES("Adder3", 3),
+ MIXER_ROUTES("Adder4", 4),
+ MIXER_ROUTES("Adder5", 5),
+ { "TX1 Transmit", NULL, "TX1" },
+ { "TX2 Transmit", NULL, "TX2" },
+ { "TX3 Transmit", NULL, "TX3" },
+ { "TX4 Transmit", NULL, "TX4" },
+ { "TX5 Transmit", NULL, "TX5" },
+};
+
+static struct snd_soc_codec_driver tegra210_mixer_codec = {
+ .probe = tegra210_mixer_codec_probe,
+ .dapm_widgets = tegra210_mixer_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tegra210_mixer_widgets),
+ .dapm_routes = tegra210_mixer_routes,
+ .num_dapm_routes = ARRAY_SIZE(tegra210_mixer_routes),
+ .controls = tegra210_mixer_gain_ctls,
+ .num_controls = ARRAY_SIZE(tegra210_mixer_gain_ctls),
+};
+
+static bool tegra210_mixer_wr_reg(struct device *dev,
+ unsigned int reg)
+{
+ if (reg < TEGRA210_MIXER_AXBAR_RX_LIMIT)
+ reg %= TEGRA210_MIXER_AXBAR_RX_STRIDE;
+ else if (reg < TEGRA210_MIXER_AXBAR_TX_LIMIT)
+ reg = (reg % TEGRA210_MIXER_AXBAR_TX_STRIDE) +
+ TEGRA210_MIXER_AXBAR_TX1_ENABLE;
+
+ switch (reg) {
+ case TEGRA210_MIXER_AXBAR_RX1_SOFT_RESET:
+ case TEGRA210_MIXER_AXBAR_RX1_STATUS:
+ case TEGRA210_MIXER_AXBAR_RX1_CIF_CTRL:
+ case TEGRA210_MIXER_AXBAR_RX1_CTRL:
+ case TEGRA210_MIXER_AXBAR_RX1_PEAK_CTRL:
+
+ case TEGRA210_MIXER_AXBAR_TX1_ENABLE:
+ case TEGRA210_MIXER_AXBAR_TX1_SOFT_RESET:
+ case TEGRA210_MIXER_AXBAR_TX1_INT_MASK:
+ case TEGRA210_MIXER_AXBAR_TX1_INT_SET:
+ case TEGRA210_MIXER_AXBAR_TX1_INT_CLEAR:
+ case TEGRA210_MIXER_AXBAR_TX1_CIF_CTRL:
+ case TEGRA210_MIXER_AXBAR_TX1_ADDER_CONFIG:
+
+ case TEGRA210_MIXER_ENABLE:
+ case TEGRA210_MIXER_SOFT_RESET:
+ case TEGRA210_MIXER_CG:
+ case TEGRA210_MIXER_AHUBRAMCTL_GAIN_CONFIG_RAM_CTRL:
+ case TEGRA210_MIXER_AHUBRAMCTL_GAIN_CONFIG_RAM_DATA:
+ case TEGRA210_MIXER_AHUBRAMCTL_PEAKM_RAM_CTRL:
+ case TEGRA210_MIXER_AHUBRAMCTL_PEAKM_RAM_DATA:
+ case TEGRA210_MIXER_CTRL:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static bool tegra210_mixer_rd_reg(struct device *dev,
+ unsigned int reg)
+{
+ if (reg < TEGRA210_MIXER_AXBAR_RX_LIMIT)
+ reg %= TEGRA210_MIXER_AXBAR_RX_STRIDE;
+ else if (reg < TEGRA210_MIXER_AXBAR_TX_LIMIT)
+ reg = (reg % TEGRA210_MIXER_AXBAR_TX_STRIDE) +
+ TEGRA210_MIXER_AXBAR_TX1_ENABLE;
+
+ switch (reg) {
+ case TEGRA210_MIXER_AXBAR_RX1_SOFT_RESET:
+ case TEGRA210_MIXER_AXBAR_RX1_STATUS:
+ case TEGRA210_MIXER_AXBAR_RX1_CIF_CTRL:
+ case TEGRA210_MIXER_AXBAR_RX1_CTRL:
+ case TEGRA210_MIXER_AXBAR_RX1_PEAK_CTRL:
+ case TEGRA210_MIXER_AXBAR_RX1_SAMPLE_COUNT:
+
+ case TEGRA210_MIXER_AXBAR_TX1_ENABLE:
+ case TEGRA210_MIXER_AXBAR_TX1_SOFT_RESET:
+ case TEGRA210_MIXER_AXBAR_TX1_STATUS:
+ case TEGRA210_MIXER_AXBAR_TX1_INT_STATUS:
+ case TEGRA210_MIXER_AXBAR_TX1_INT_MASK:
+ case TEGRA210_MIXER_AXBAR_TX1_INT_SET:
+ case TEGRA210_MIXER_AXBAR_TX1_INT_CLEAR:
+ case TEGRA210_MIXER_AXBAR_TX1_CIF_CTRL:
+ case TEGRA210_MIXER_AXBAR_TX1_ADDER_CONFIG:
+
+ case TEGRA210_MIXER_ENABLE:
+ case TEGRA210_MIXER_SOFT_RESET:
+ case TEGRA210_MIXER_CG:
+ case TEGRA210_MIXER_STATUS:
+ case TEGRA210_MIXER_INT_STATUS:
+ case TEGRA210_MIXER_AHUBRAMCTL_GAIN_CONFIG_RAM_CTRL:
+ case TEGRA210_MIXER_AHUBRAMCTL_GAIN_CONFIG_RAM_DATA:
+ case TEGRA210_MIXER_AHUBRAMCTL_PEAKM_RAM_CTRL:
+ case TEGRA210_MIXER_AHUBRAMCTL_PEAKM_RAM_DATA:
+ case TEGRA210_MIXER_CTRL:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static bool tegra210_mixer_volatile_reg(struct device *dev,
+ unsigned int reg)
+{
+ if (reg < TEGRA210_MIXER_AXBAR_RX_LIMIT)
+ reg %= TEGRA210_MIXER_AXBAR_RX_STRIDE;
+ else if (reg < TEGRA210_MIXER_AXBAR_TX_LIMIT)
+ reg = (reg % TEGRA210_MIXER_AXBAR_TX_STRIDE) +
+ TEGRA210_MIXER_AXBAR_TX1_ENABLE;
+
+ switch (reg) {
+ case TEGRA210_MIXER_AXBAR_RX1_SOFT_RESET:
+ case TEGRA210_MIXER_AXBAR_RX1_STATUS:
+
+ case TEGRA210_MIXER_AXBAR_TX1_SOFT_RESET:
+ case TEGRA210_MIXER_AXBAR_TX1_STATUS:
+ case TEGRA210_MIXER_AXBAR_TX1_INT_STATUS:
+ case TEGRA210_MIXER_AXBAR_TX1_INT_SET:
+
+ case TEGRA210_MIXER_SOFT_RESET:
+ case TEGRA210_MIXER_STATUS:
+ case TEGRA210_MIXER_INT_STATUS:
+ case TEGRA210_MIXER_AHUBRAMCTL_GAIN_CONFIG_RAM_CTRL:
+ case TEGRA210_MIXER_AHUBRAMCTL_GAIN_CONFIG_RAM_DATA:
+ case TEGRA210_MIXER_AHUBRAMCTL_PEAKM_RAM_CTRL:
+ case TEGRA210_MIXER_AHUBRAMCTL_PEAKM_RAM_DATA:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static bool tegra210_mixer_precious_reg(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_MIXER_AHUBRAMCTL_GAIN_CONFIG_RAM_DATA:
+ case TEGRA210_MIXER_AHUBRAMCTL_PEAKM_RAM_DATA:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static const struct regmap_config tegra210_mixer_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = TEGRA210_MIXER_CTRL,
+ .writeable_reg = tegra210_mixer_wr_reg,
+ .readable_reg = tegra210_mixer_rd_reg,
+ .volatile_reg = tegra210_mixer_volatile_reg,
+ .precious_reg = tegra210_mixer_precious_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct tegra210_mixer_soc_data soc_data_tegra210 = {
+ .set_audio_cif = tegra210_xbar_set_cif
+};
+
+static const struct of_device_id tegra210_mixer_of_match[] = {
+ { .compatible = "nvidia,tegra210-amixer", .data = &soc_data_tegra210 },
+ {},
+};
+
+static int tegra210_mixer_platform_probe(struct platform_device *pdev)
+{
+ struct tegra210_mixer *mixer;
+ struct resource *mem, *memregion;
+ void __iomem *regs;
+ int ret;
+ const struct of_device_id *match;
+ struct tegra210_mixer_soc_data *soc_data;
+
+ match = of_match_device(tegra210_mixer_of_match, &pdev->dev);
+ if (!match) {
+ dev_err(&pdev->dev, "Error: No device match found\n");
+ ret = -ENODEV;
+ goto err;
+ }
+ soc_data = (struct tegra210_mixer_soc_data *)match->data;
+
+ mixer = devm_kzalloc(&pdev->dev,
+ sizeof(struct tegra210_mixer), GFP_KERNEL);
+ if (!mixer) {
+ dev_err(&pdev->dev, "Can't allocate tegra210_mixer\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+ dev_set_drvdata(&pdev->dev, mixer);
+
+ mixer->soc_data = soc_data;
+ mixer->gain_coeff[0] = 0x2003EEC;
+ mixer->gain_coeff[1] = 0x38EBC;
+ mixer->gain_coeff[2] = 0xFFFFFEBB;
+ mixer->gain_coeff[3] = 0x1414253;
+ mixer->gain_coeff[4] = 0xF6CEC;
+ mixer->gain_coeff[5] = 0xFFFFC9DE;
+ mixer->gain_coeff[6] = 0xC4B873;
+ mixer->gain_coeff[7] = 0x422F76;
+ mixer->gain_coeff[8] = 0xFFFA021C;
+ mixer->gain_coeff[9] = 0x10000;
+ mixer->gain_coeff[10] = 0x1A1;
+ mixer->gain_coeff[11] = 0x823;
+ mixer->gain_coeff[12] = 0x3e80;
+ mixer->gain_coeff[13] = 0x83126E;
+
+#ifndef CONFIG_MACH_GRENADA
+ mixer->clk_mixer = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(mixer->clk_mixer)) {
+ dev_err(&pdev->dev, "Can't retrieve tegra210_mixer clock\n");
+ ret = PTR_ERR(mixer->clk_mixer);
+ goto err;
+ }
+#endif
+
+ 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;
+ }
+
+ mixer->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
+ &tegra210_mixer_regmap_config);
+ if (IS_ERR(mixer->regmap)) {
+ dev_err(&pdev->dev, "regmap init failed\n");
+ ret = PTR_ERR(mixer->regmap);
+ goto err_clk_put;
+ }
+ regcache_cache_only(mixer->regmap, true);
+
+ if (of_property_read_u32(pdev->dev.of_node,
+ "nvidia,ahub-amixer-id",
+ &pdev->dev.id) < 0) {
+ dev_err(&pdev->dev,
+ "Missing property nvidia,ahub-amixer-id\n");
+ ret = -ENODEV;
+ goto err_clk_put;
+ }
+
+ pm_runtime_enable(&pdev->dev);
+ if (!pm_runtime_enabled(&pdev->dev)) {
+ ret = tegra210_mixer_runtime_resume(&pdev->dev);
+ if (ret)
+ goto err_pm_disable;
+ }
+
+ ret = snd_soc_register_codec(&pdev->dev, &tegra210_mixer_codec,
+ tegra210_mixer_dais,
+ ARRAY_SIZE(tegra210_mixer_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))
+ tegra210_mixer_runtime_suspend(&pdev->dev);
+err_pm_disable:
+ pm_runtime_disable(&pdev->dev);
+err_clk_put:
+#ifndef CONFIG_MACH_GRENADA
+ devm_clk_put(&pdev->dev, mixer->clk_mixer);
+#else
+ dev_err(&pdev->dev, "error\n");
+#endif
+err:
+ return ret;
+}
+
+static int tegra210_mixer_platform_remove(struct platform_device *pdev)
+{
+#ifndef CONFIG_MACH_GRENADA
+ struct tegra210_mixer *mixer = dev_get_drvdata(&pdev->dev);
+#endif
+
+ snd_soc_unregister_codec(&pdev->dev);
+
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ tegra210_mixer_runtime_suspend(&pdev->dev);
+
+#ifndef CONFIG_MACH_GRENADA
+ devm_clk_put(&pdev->dev, mixer->clk_mixer);
+#endif
+
+ return 0;
+}
+
+static const struct dev_pm_ops tegra210_mixer_pm_ops = {
+ SET_RUNTIME_PM_OPS(tegra210_mixer_runtime_suspend,
+ tegra210_mixer_runtime_resume, NULL)
+};
+
+static struct platform_driver tegra210_mixer_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = tegra210_mixer_of_match,
+ .pm = &tegra210_mixer_pm_ops,
+ },
+ .probe = tegra210_mixer_platform_probe,
+ .remove = tegra210_mixer_platform_remove,
+};
+module_platform_driver(tegra210_mixer_driver);
+
+MODULE_AUTHOR("Arun Shamanna Lakshmi ");
+MODULE_DESCRIPTION("Tegra210 MIXER ASoC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, tegra210_mixer_of_match);
diff --git a/sound/soc/tegra-alt/tegra210_mvc_alt.c b/sound/soc/tegra-alt/tegra210_mvc_alt.c
new file mode 100644
index 00000000..3eaffcf0
--- /dev/null
+++ b/sound/soc/tegra-alt/tegra210_mvc_alt.c
@@ -0,0 +1,549 @@
+/*
+ * tegra210_mvc_alt.c - Tegra210 MVC driver
+ *
+ * Copyright (c) 2014 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 .
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "tegra210_xbar_alt.h"
+#include "tegra210_mvc_alt.h"
+
+#define DRV_NAME "tegra210-mvc"
+
+static int tegra210_mvc_runtime_suspend(struct device *dev)
+{
+ struct tegra210_mvc *mvc = dev_get_drvdata(dev);
+
+ regcache_cache_only(mvc->regmap, true);
+
+ #ifndef CONFIG_MACH_GRENADA
+ clk_disable_unprepare(mvc->clk_mvc);
+ #endif
+
+ return 0;
+}
+
+static int tegra210_mvc_runtime_resume(struct device *dev)
+{
+ struct tegra210_mvc *mvc = dev_get_drvdata(dev);
+
+ #ifndef CONFIG_MACH_GRENADA
+ int ret;
+
+ ret = clk_prepare_enable(mvc->clk_mvc);
+ if (ret) {
+ dev_err(dev, "clk_enable failed: %d\n", ret);
+ return ret;
+ }
+ #endif
+
+ regcache_cache_only(mvc->regmap, false);
+
+ return 0;
+}
+
+static int tegra210_mvc_write_ram(struct tegra210_mvc *mvc,
+ unsigned int addr,
+ unsigned int val)
+{
+ unsigned int reg, value, wait = 0xffff;
+
+ /* check if busy */
+ do {
+ regmap_read(mvc->regmap,
+ TEGRA210_MVC_AHUBRAMCTL_CONFIG_RAM_CTRL, &value);
+ wait--;
+ if (!wait)
+ return -EINVAL;
+ } while (value & 0x80000000);
+ value = 0;
+
+ reg = (addr << TEGRA210_MVC_AHUBRAMCTL_CONFIG_RAM_CTRL_RAM_ADDR_SHIFT) &
+ TEGRA210_MVC_AHUBRAMCTL_CONFIG_RAM_CTRL_RAM_ADDR_MASK;
+ reg |= TEGRA210_MVC_AHUBRAMCTL_CONFIG_RAM_CTRL_ADDR_INIT_EN;
+ reg |= TEGRA210_MVC_AHUBRAMCTL_CONFIG_RAM_CTRL_RW_WRITE;
+ reg |= TEGRA210_MVC_AHUBRAMCTL_CONFIG_RAM_CTRL_SEQ_ACCESS_EN;
+
+ regmap_write(mvc->regmap,
+ TEGRA210_MVC_AHUBRAMCTL_CONFIG_RAM_CTRL, reg);
+ regmap_write(mvc->regmap,
+ TEGRA210_MVC_AHUBRAMCTL_CONFIG_RAM_DATA, val);
+
+ return 0;
+}
+
+static int tegra210_mvc_get_vol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ return 0;
+}
+
+static int tegra210_mvc_put_vol(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct tegra210_mvc *mvc = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg = mc->reg;
+ unsigned int ret, value, wait = 0xffff;
+
+ /* check if VOLUME_SWITCH is triggered*/
+ do {
+ regmap_read(mvc->regmap,
+ TEGRA210_MVC_SWITCH, &value);
+ wait--;
+ if (!wait)
+ return -EINVAL;
+ } while (value & TEGRA210_MVC_VOLUME_SWITCH_MASK);
+
+ if (reg == TEGRA210_MVC_TARGET_VOL) {
+ ret = regmap_write(mvc->regmap, TEGRA210_MVC_TARGET_VOL,
+ ucontrol->value.integer.value[0]*(1<<24));
+ } else {
+ ret = regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL,
+ TEGRA210_MVC_MUTE_MASK, (ucontrol->value.integer.value[0] ?
+ TEGRA210_MVC_MUTE_EN : 0));
+ }
+ ret |= regmap_update_bits(mvc->regmap, TEGRA210_MVC_SWITCH,
+ TEGRA210_MVC_VOLUME_SWITCH_MASK,
+ TEGRA210_MVC_VOLUME_SWITCH_TRIGGER);
+
+ return ret;
+}
+
+static int tegra210_mvc_set_audio_cif(struct tegra210_mvc *mvc,
+ struct snd_pcm_hw_params *params,
+ unsigned int reg)
+{
+ int channels, audio_bits;
+ struct tegra210_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 = TEGRA210_AUDIOCIF_BITS_16;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ audio_bits = TEGRA210_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.truncate = 0;
+ cif_conf.mono_conv = 0;
+
+ mvc->soc_data->set_audio_cif(mvc->regmap, reg, &cif_conf);
+
+ return 0;
+}
+
+static int tegra210_mvc_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct device *dev = dai->dev;
+ struct tegra210_mvc *mvc = snd_soc_dai_get_drvdata(dai);
+ int i, ret;
+
+ /* set RX cif and TX cif */
+ ret = tegra210_mvc_set_audio_cif(mvc, params,
+ TEGRA210_MVC_AXBAR_RX_CIF_CTRL);
+ if (ret) {
+ dev_err(dev, "Can't set MVC RX CIF: %d\n", ret);
+ return ret;
+ }
+ ret = tegra210_mvc_set_audio_cif(mvc, params,
+ TEGRA210_MVC_AXBAR_TX_CIF_CTRL);
+ if (ret) {
+ dev_err(dev, "Can't set MVC TX CIF: %d\n", ret);
+ return ret;
+ }
+
+ /* disable per_channel_cntrl_en */
+ regmap_update_bits(mvc->regmap, TEGRA210_MVC_CTRL,
+ TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK,
+ ~(TEGRA210_MVC_PER_CHAN_CTRL_EN_MASK));
+
+ /* init the default volume=1 for MVC */
+ regmap_write(mvc->regmap, TEGRA210_MVC_INIT_VOL,
+ TEGRA210_MVC_INIT_VOL_DEFAULT);
+
+ /* program the poly coefficients */
+ for (i = 0; i < 9; i++)
+ tegra210_mvc_write_ram(mvc, i, mvc->poly_coeff[i]);
+
+
+ /* program poly_n1, poly_n2, duration */
+ regmap_write(mvc->regmap, TEGRA210_MVC_POLY_N1, mvc->poly_n1);
+ regmap_write(mvc->regmap, TEGRA210_MVC_POLY_N2, mvc->poly_n2);
+ regmap_write(mvc->regmap, TEGRA210_MVC_DURATION, mvc->duration);
+
+ /* program duration_inv */
+ regmap_write(mvc->regmap, TEGRA210_MVC_DURATION_INV, mvc->duration_inv);
+
+ return ret;
+}
+
+static int tegra210_mvc_codec_probe(struct snd_soc_codec *codec)
+{
+ struct tegra210_mvc *mvc = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ codec->control_data = mvc->regmap;
+ ret = snd_soc_codec_set_cache_io(codec, 64, 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 tegra210_mvc_dai_ops = {
+ .hw_params = tegra210_mvc_hw_params,
+};
+
+static const struct snd_kcontrol_new tegra210_mvc_vol_ctrl[] = {
+ SOC_SINGLE_EXT("Vol", TEGRA210_MVC_TARGET_VOL, 0, 100, 0,
+ tegra210_mvc_get_vol, tegra210_mvc_put_vol),
+ SOC_SINGLE_EXT("Mute", TEGRA210_MVC_CTRL, 0, 1, 0,
+ tegra210_mvc_get_vol, tegra210_mvc_put_vol),
+};
+
+static struct snd_soc_dai_driver tegra210_mvc_dais[] = {
+ {
+ .name = "MVC IN",
+ .playback = {
+ .stream_name = "MVC Receive",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ },
+ {
+ .name = "MVC OUT",
+ .capture = {
+ .stream_name = "MVC Transmit",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &tegra210_mvc_dai_ops,
+ }
+};
+
+static const struct snd_soc_dapm_widget tegra210_mvc_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("MVC RX", NULL, 0, SND_SOC_NOPM,
+ 0, 0),
+ SND_SOC_DAPM_AIF_OUT("MVC TX", NULL, 0, TEGRA210_MVC_ENABLE,
+ TEGRA210_MVC_EN_SHIFT, 0),
+};
+
+static const struct snd_soc_dapm_route tegra210_mvc_routes[] = {
+ { "MVC RX", NULL, "MVC Receive" },
+ { "MVC TX", NULL, "MVC RX" },
+ { "MVC Transmit", NULL, "MVC TX" },
+};
+
+static struct snd_soc_codec_driver tegra210_mvc_codec = {
+ .probe = tegra210_mvc_codec_probe,
+ .dapm_widgets = tegra210_mvc_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tegra210_mvc_widgets),
+ .dapm_routes = tegra210_mvc_routes,
+ .num_dapm_routes = ARRAY_SIZE(tegra210_mvc_routes),
+ .controls = tegra210_mvc_vol_ctrl,
+ .num_controls = ARRAY_SIZE(tegra210_mvc_vol_ctrl),
+};
+
+static bool tegra210_mvc_wr_rd_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_MVC_AXBAR_RX_STATUS:
+ case TEGRA210_MVC_AXBAR_RX_INT_STATUS:
+ case TEGRA210_MVC_AXBAR_RX_INT_MASK:
+ case TEGRA210_MVC_AXBAR_RX_INT_SET:
+ case TEGRA210_MVC_AXBAR_RX_INT_CLEAR:
+ case TEGRA210_MVC_AXBAR_RX_CIF_CTRL:
+ case TEGRA210_MVC_AXBAR_RX_CYA:
+ case TEGRA210_MVC_AXBAR_RX_DBG:
+ case TEGRA210_MVC_AXBAR_TX_STATUS:
+ case TEGRA210_MVC_AXBAR_TX_INT_STATUS:
+ case TEGRA210_MVC_AXBAR_TX_INT_MASK:
+ case TEGRA210_MVC_AXBAR_TX_INT_SET:
+ case TEGRA210_MVC_AXBAR_TX_INT_CLEAR:
+ case TEGRA210_MVC_AXBAR_TX_CIF_CTRL:
+ case TEGRA210_MVC_AXBAR_TX_CYA:
+ case TEGRA210_MVC_AXBAR_TX_DBG:
+ case TEGRA210_MVC_ENABLE:
+ case TEGRA210_MVC_SOFT_RESET:
+ case TEGRA210_MVC_CG:
+ case TEGRA210_MVC_STATUS:
+ case TEGRA210_MVC_INT_STATUS:
+ case TEGRA210_MVC_CTRL:
+ case TEGRA210_MVC_SWITCH:
+ case TEGRA210_MVC_INIT_VOL:
+ case TEGRA210_MVC_TARGET_VOL:
+ case TEGRA210_MVC_DURATION:
+ case TEGRA210_MVC_DURATION_INV:
+ case TEGRA210_MVC_POLY_N1:
+ case TEGRA210_MVC_POLY_N2:
+ case TEGRA210_MVC_PEAK_CTRL:
+ case TEGRA210_MVC_AHUBRAMCTL_CONFIG_RAM_CTRL:
+ case TEGRA210_MVC_AHUBRAMCTL_CONFIG_RAM_DATA:
+ case TEGRA210_MVC_PEAK_VALUE:
+ case TEGRA210_MVC_CONFIG_ERR_TYPE:
+ case TEGRA210_MVC_CYA:
+ case TEGRA210_MVC_DBG:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static bool tegra210_mvc_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_MVC_AXBAR_RX_STATUS:
+ case TEGRA210_MVC_AXBAR_RX_INT_STATUS:
+ case TEGRA210_MVC_AXBAR_RX_INT_SET:
+
+ case TEGRA210_MVC_AXBAR_TX_STATUS:
+ case TEGRA210_MVC_AXBAR_TX_INT_STATUS:
+ case TEGRA210_MVC_AXBAR_TX_INT_SET:
+
+ case TEGRA210_MVC_SOFT_RESET:
+ case TEGRA210_MVC_STATUS:
+ case TEGRA210_MVC_INT_STATUS:
+ case TEGRA210_MVC_SWITCH:
+ case TEGRA210_MVC_AHUBRAMCTL_CONFIG_RAM_CTRL:
+ case TEGRA210_MVC_AHUBRAMCTL_CONFIG_RAM_DATA:
+ case TEGRA210_MVC_PEAK_VALUE:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static const struct regmap_config tegra210_mvc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = TEGRA210_MVC_CYA,
+ .writeable_reg = tegra210_mvc_wr_rd_reg,
+ .readable_reg = tegra210_mvc_wr_rd_reg,
+ .volatile_reg = tegra210_mvc_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct tegra210_mvc_soc_data soc_data_tegra210 = {
+ .set_audio_cif = tegra210_xbar_set_cif,
+};
+
+static const struct of_device_id tegra210_mvc_of_match[] = {
+ { .compatible = "nvidia,tegra210-mvc", .data = &soc_data_tegra210 },
+ {},
+};
+
+static int tegra210_mvc_platform_probe(struct platform_device *pdev)
+{
+ struct tegra210_mvc *mvc;
+ struct resource *mem, *memregion;
+ void __iomem *regs;
+ int ret = 0;
+ const struct of_device_id *match;
+ struct tegra210_mvc_soc_data *soc_data;
+
+ match = of_match_device(tegra210_mvc_of_match, &pdev->dev);
+ if (!match) {
+ dev_err(&pdev->dev, "Error: No device match found\n");
+ ret = -ENODEV;
+ goto err;
+ }
+ soc_data = (struct tegra210_mvc_soc_data *)match->data;
+
+ mvc = devm_kzalloc(&pdev->dev, sizeof(struct tegra210_mvc), GFP_KERNEL);
+ if (!mvc) {
+ dev_err(&pdev->dev, "Can't allocate mvc\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+ dev_set_drvdata(&pdev->dev, mvc);
+
+ mvc->soc_data = soc_data;
+
+ mvc->poly_n1 = 16;
+ mvc->poly_n2 = 63;
+ mvc->duration = 150;
+ mvc->duration_inv = 14316558;
+ mvc->poly_coeff[0] = 23738319;
+ mvc->poly_coeff[1] = 659403;
+ mvc->poly_coeff[2] = -3680;
+ mvc->poly_coeff[3] = 15546680;
+ mvc->poly_coeff[4] = 2530732;
+ mvc->poly_coeff[5] = -120985;
+ mvc->poly_coeff[6] = 12048422;
+ mvc->poly_coeff[7] = 5527252;
+ mvc->poly_coeff[8] = -785042;
+
+#ifndef CONFIG_MACH_GRENADA
+ mvc->clk_mvc = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(mvc->clk_mvc)) {
+ dev_err(&pdev->dev, "Can't retrieve mvc clock\n");
+ ret = PTR_ERR(mvc->clk_mvc);
+ goto err;
+ }
+#endif
+
+ 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), pdev->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;
+ }
+
+ mvc->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
+ &tegra210_mvc_regmap_config);
+ if (IS_ERR(mvc->regmap)) {
+ dev_err(&pdev->dev, "regmap init failed\n");
+ ret = PTR_ERR(mvc->regmap);
+ goto err_clk_put;
+ }
+ regcache_cache_only(mvc->regmap, true);
+
+ if (of_property_read_u32(pdev->dev.of_node,
+ "nvidia,ahub-mvc-id",
+ &pdev->dev.id) < 0) {
+ dev_err(&pdev->dev,
+ "Missing property nvidia,ahub-mvc-id\n");
+ ret = -ENODEV;
+ goto err_clk_put;
+ }
+
+ pm_runtime_enable(&pdev->dev);
+ if (!pm_runtime_enabled(&pdev->dev)) {
+ ret = tegra210_mvc_runtime_resume(&pdev->dev);
+ if (ret)
+ goto err_pm_disable;
+ }
+
+ ret = snd_soc_register_codec(&pdev->dev, &tegra210_mvc_codec,
+ tegra210_mvc_dais,
+ ARRAY_SIZE(tegra210_mvc_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))
+ tegra210_mvc_runtime_suspend(&pdev->dev);
+err_pm_disable:
+ pm_runtime_disable(&pdev->dev);
+err_clk_put:
+#ifndef CONFIG_MACH_GRENADA
+ devm_clk_put(&pdev->dev, mvc->clk_mvc);
+#else
+ dev_err(&pdev->dev, "error\n");
+#endif
+err:
+ return ret;
+}
+
+static int tegra210_mvc_platform_remove(struct platform_device *pdev)
+{
+#ifndef CONFIG_MACH_GRENADA
+ struct tegra210_mvc *mvc = dev_get_drvdata(&pdev->dev);
+#endif
+ snd_soc_unregister_codec(&pdev->dev);
+
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ tegra210_mvc_runtime_suspend(&pdev->dev);
+
+#ifndef CONFIG_MACH_GRENADA
+ devm_clk_put(&pdev->dev, mvc->clk_mvc);
+#endif
+
+ return 0;
+}
+
+static const struct dev_pm_ops tegra210_mvc_pm_ops = {
+ SET_RUNTIME_PM_OPS(tegra210_mvc_runtime_suspend,
+ tegra210_mvc_runtime_resume, NULL)
+};
+
+static struct platform_driver tegra210_mvc_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = tegra210_mvc_of_match,
+ .pm = &tegra210_mvc_pm_ops,
+ },
+ .probe = tegra210_mvc_platform_probe,
+ .remove = tegra210_mvc_platform_remove,
+};
+module_platform_driver(tegra210_mvc_driver)
+
+MODULE_AUTHOR("Arun Shamanna Lakshmi ");
+MODULE_DESCRIPTION("Tegra210 MVC ASoC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, tegra210_mvc_of_match);
diff --git a/sound/soc/tegra-alt/tegra210_sfc_alt.c b/sound/soc/tegra-alt/tegra210_sfc_alt.c
new file mode 100644
index 00000000..49a3a2c4
--- /dev/null
+++ b/sound/soc/tegra-alt/tegra210_sfc_alt.c
@@ -0,0 +1,584 @@
+/*
+ * tegra210_sfc_alt.c - Tegra210 SFC driver
+ *
+ * Copyright (c) 2014 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 .
+ */
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "tegra210_xbar_alt.h"
+#include "tegra210_sfc_alt.h"
+
+#define DRV_NAME "tegra210-sfc"
+
+static int tegra210_sfc_runtime_suspend(struct device *dev)
+{
+ struct tegra210_sfc *sfc = dev_get_drvdata(dev);
+
+ regcache_cache_only(sfc->regmap, true);
+
+#ifndef CONFIG_MACH_GRENADA
+ clk_disable_unprepare(sfc->clk_sfc);
+#endif
+
+ return 0;
+}
+
+static int tegra210_sfc_runtime_resume(struct device *dev)
+{
+ struct tegra210_sfc *sfc = dev_get_drvdata(dev);
+
+#ifndef CONFIG_MACH_GRENADA
+ int ret;
+
+ ret = clk_prepare_enable(sfc->clk_sfc);
+ if (ret) {
+ dev_err(dev, "clk_enable failed: %d\n", ret);
+ return ret;
+ }
+#endif
+
+ regcache_cache_only(sfc->regmap, false);
+
+ return 0;
+}
+
+static int tegra210_sfc_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct tegra210_sfc *sfc = snd_soc_dai_get_drvdata(dai);
+
+ if (dir == SND_SOC_CLOCK_OUT)
+ sfc->srate_out = freq;
+ else if (dir == SND_SOC_CLOCK_IN)
+ sfc->srate_in = freq;
+
+ return 0;
+}
+
+static int tegra210_sfc_set_audio_cif(struct tegra210_sfc *sfc,
+ struct snd_pcm_hw_params *params,
+ unsigned int reg)
+{
+ int channels, audio_bits;
+ struct tegra210_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 = TEGRA210_AUDIOCIF_BITS_16;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ audio_bits = TEGRA210_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.truncate = 0;
+ cif_conf.mono_conv = 0;
+
+ sfc->soc_data->set_audio_cif(sfc->regmap, reg, &cif_conf);
+
+ return 0;
+}
+
+static int tegra210_sfc_in_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct device *dev = dai->dev;
+ struct tegra210_sfc *sfc = snd_soc_dai_get_drvdata(dai);
+ unsigned int value;
+ int ret;
+
+ ret = tegra210_sfc_set_audio_cif(sfc, params,
+ TEGRA210_SFC_AXBAR_RX_CIF_CTRL);
+ if (ret) {
+ dev_err(dev, "Can't set SFC RX CIF: %d\n", ret);
+ return ret;
+ }
+
+ switch (sfc->srate_in) {
+ case 8000:
+ value = TEGRA210_SFC_FS8;
+ break;
+ case 11025:
+ value = TEGRA210_SFC_FS11_025;
+ break;
+ case 16000:
+ value = TEGRA210_SFC_FS16;
+ break;
+ case 22050:
+ value = TEGRA210_SFC_FS22_05;
+ break;
+ case 24000:
+ value = TEGRA210_SFC_FS24;
+ break;
+ case 32000:
+ value = TEGRA210_SFC_FS32;
+ break;
+ case 44100:
+ value = TEGRA210_SFC_FS44_1;
+ break;
+ case 48000:
+ value = TEGRA210_SFC_FS48;
+ break;
+ case 64000:
+ value = TEGRA210_SFC_FS64;
+ break;
+ case 88200:
+ value = TEGRA210_SFC_FS88_2;
+ break;
+ case 96000:
+ value = TEGRA210_SFC_FS96;
+ break;
+ case 176400:
+ value = TEGRA210_SFC_FS176_4;
+ break;
+ case 192000:
+ value = TEGRA210_SFC_FS192;
+ break;
+ default:
+ value = TEGRA210_SFC_FS8;
+ break;
+ }
+
+ regmap_write(sfc->regmap, TEGRA210_SFC_AXBAR_RX_FREQ, value);
+ return ret;
+}
+
+static int tegra210_sfc_out_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct device *dev = dai->dev;
+ struct tegra210_sfc *sfc = snd_soc_dai_get_drvdata(dai);
+ unsigned int value;
+ int ret;
+
+ ret = tegra210_sfc_set_audio_cif(sfc, params,
+ TEGRA210_SFC_AXBAR_TX_CIF_CTRL);
+ if (ret) {
+ dev_err(dev, "Can't set SFC TX CIF: %d\n", ret);
+ return ret;
+ }
+ switch (sfc->srate_out) {
+ case 8000:
+ value = TEGRA210_SFC_FS8;
+ break;
+ case 11025:
+ value = TEGRA210_SFC_FS11_025;
+ break;
+ case 16000:
+ value = TEGRA210_SFC_FS16;
+ break;
+ case 22050:
+ value = TEGRA210_SFC_FS22_05;
+ break;
+ case 24000:
+ value = TEGRA210_SFC_FS24;
+ break;
+ case 32000:
+ value = TEGRA210_SFC_FS32;
+ break;
+ case 44100:
+ value = TEGRA210_SFC_FS44_1;
+ break;
+ case 48000:
+ value = TEGRA210_SFC_FS48;
+ break;
+ case 64000:
+ value = TEGRA210_SFC_FS64;
+ break;
+ case 88200:
+ value = TEGRA210_SFC_FS88_2;
+ break;
+ case 96000:
+ value = TEGRA210_SFC_FS96;
+ break;
+ case 176400:
+ value = TEGRA210_SFC_FS176_4;
+ break;
+ case 192000:
+ value = TEGRA210_SFC_FS192;
+ break;
+ default:
+ value = TEGRA210_SFC_FS8;
+ break;
+ }
+
+ regmap_write(sfc->regmap, TEGRA210_SFC_AXBAR_TX_FREQ, value);
+ return ret;
+}
+
+static int tegra210_sfc_codec_probe(struct snd_soc_codec *codec)
+{
+ struct tegra210_sfc *sfc = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ codec->control_data = sfc->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 tegra210_sfc_in_dai_ops = {
+ .hw_params = tegra210_sfc_in_hw_params,
+ .set_sysclk = tegra210_sfc_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_ops tegra210_sfc_out_dai_ops = {
+ .hw_params = tegra210_sfc_out_hw_params,
+};
+
+static struct snd_soc_dai_driver tegra210_sfc_dais[] = {
+ {
+ .name = "CIF",
+ .playback = {
+ .stream_name = "SFC Receive",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &tegra210_sfc_in_dai_ops,
+ },
+ {
+ .name = "DAP",
+ .capture = {
+ .stream_name = "SFC Transmit",
+ .channels_min = 1,
+ .channels_max = 16,
+ .rates = SNDRV_PCM_RATE_8000_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &tegra210_sfc_out_dai_ops,
+ }
+};
+
+static const struct snd_soc_dapm_widget tegra210_sfc_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("SFC RX", NULL, 0, SND_SOC_NOPM,
+ 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SFC TX", NULL, 0, TEGRA210_SFC_ENABLE,
+ TEGRA210_SFC_EN_SHIFT, 0),
+};
+
+static const struct snd_soc_dapm_route tegra210_sfc_routes[] = {
+ { "SFC RX", NULL, "SFC Receive" },
+ { "SFC TX", NULL, "SFC RX" },
+ { "SFC Transmit", NULL, "SFC TX" },
+};
+
+static struct snd_soc_codec_driver tegra210_sfc_codec = {
+ .probe = tegra210_sfc_codec_probe,
+ .dapm_widgets = tegra210_sfc_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tegra210_sfc_widgets),
+ .dapm_routes = tegra210_sfc_routes,
+ .num_dapm_routes = ARRAY_SIZE(tegra210_sfc_routes),
+};
+
+static bool tegra210_sfc_wr_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_SFC_AXBAR_RX_INT_MASK:
+ case TEGRA210_SFC_AXBAR_RX_INT_SET:
+ case TEGRA210_SFC_AXBAR_RX_INT_CLEAR:
+ case TEGRA210_SFC_AXBAR_RX_CIF_CTRL:
+ case TEGRA210_SFC_AXBAR_RX_FREQ:
+
+ case TEGRA210_SFC_AXBAR_TX_INT_MASK:
+ case TEGRA210_SFC_AXBAR_TX_INT_SET:
+ case TEGRA210_SFC_AXBAR_TX_INT_CLEAR:
+ case TEGRA210_SFC_AXBAR_TX_CIF_CTRL:
+ case TEGRA210_SFC_AXBAR_TX_FREQ:
+
+ case TEGRA210_SFC_ENABLE:
+ case TEGRA210_SFC_SOFT_RESET:
+ case TEGRA210_SFC_CG:
+ case TEGRA210_SFC_COEF_RAM:
+ case TEGRA210_SFC_AHUBRAMCTL_SFC_CTRL:
+ case TEGRA210_SFC_AHUBRAMCTL_SFC_DATA:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static bool tegra210_sfc_rd_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_SFC_AXBAR_RX_STATUS:
+ case TEGRA210_SFC_AXBAR_RX_INT_STATUS:
+ case TEGRA210_SFC_AXBAR_RX_INT_MASK:
+ case TEGRA210_SFC_AXBAR_RX_INT_SET:
+ case TEGRA210_SFC_AXBAR_RX_INT_CLEAR:
+ case TEGRA210_SFC_AXBAR_RX_CIF_CTRL:
+ case TEGRA210_SFC_AXBAR_RX_FREQ:
+
+ case TEGRA210_SFC_AXBAR_TX_STATUS:
+ case TEGRA210_SFC_AXBAR_TX_INT_STATUS:
+ case TEGRA210_SFC_AXBAR_TX_INT_MASK:
+ case TEGRA210_SFC_AXBAR_TX_INT_SET:
+ case TEGRA210_SFC_AXBAR_TX_INT_CLEAR:
+ case TEGRA210_SFC_AXBAR_TX_CIF_CTRL:
+ case TEGRA210_SFC_AXBAR_TX_FREQ:
+
+ case TEGRA210_SFC_ENABLE:
+ case TEGRA210_SFC_SOFT_RESET:
+ case TEGRA210_SFC_CG:
+ case TEGRA210_SFC_STATUS:
+ case TEGRA210_SFC_INT_STATUS:
+ case TEGRA210_SFC_COEF_RAM:
+ case TEGRA210_SFC_AHUBRAMCTL_SFC_CTRL:
+ case TEGRA210_SFC_AHUBRAMCTL_SFC_DATA:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static bool tegra210_sfc_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_SFC_AXBAR_RX_STATUS:
+ case TEGRA210_SFC_AXBAR_RX_INT_STATUS:
+ case TEGRA210_SFC_AXBAR_RX_INT_SET:
+
+ case TEGRA210_SFC_AXBAR_TX_STATUS:
+ case TEGRA210_SFC_AXBAR_TX_INT_STATUS:
+ case TEGRA210_SFC_AXBAR_TX_INT_SET:
+
+ case TEGRA210_SFC_SOFT_RESET:
+ case TEGRA210_SFC_STATUS:
+ case TEGRA210_SFC_INT_STATUS:
+ case TEGRA210_SFC_AHUBRAMCTL_SFC_CTRL:
+ case TEGRA210_SFC_AHUBRAMCTL_SFC_DATA:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static bool tegra210_sfc_precious_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_SFC_AHUBRAMCTL_SFC_DATA:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static const struct regmap_config tegra210_sfc_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = TEGRA210_SFC_AHUBRAMCTL_SFC_DATA,
+ .writeable_reg = tegra210_sfc_wr_reg,
+ .readable_reg = tegra210_sfc_rd_reg,
+ .volatile_reg = tegra210_sfc_volatile_reg,
+ .precious_reg = tegra210_sfc_precious_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct tegra210_sfc_soc_data soc_data_tegra210 = {
+ .set_audio_cif = tegra210_xbar_set_cif,
+};
+
+static const struct of_device_id tegra210_sfc_of_match[] = {
+ { .compatible = "nvidia,tegra210-sfc", .data = &soc_data_tegra210 },
+ {},
+};
+
+static int tegra210_sfc_platform_probe(struct platform_device *pdev)
+{
+ struct tegra210_sfc *sfc;
+ struct resource *mem, *memregion;
+ void __iomem *regs;
+ int ret = 0;
+ const struct of_device_id *match;
+ struct tegra210_sfc_soc_data *soc_data;
+
+ match = of_match_device(tegra210_sfc_of_match, &pdev->dev);
+ if (!match) {
+ dev_err(&pdev->dev, "Error: No device match found\n");
+ ret = -ENODEV;
+ goto err;
+ }
+ soc_data = (struct tegra210_sfc_soc_data *)match->data;
+
+ sfc = devm_kzalloc(&pdev->dev, sizeof(struct tegra210_sfc), GFP_KERNEL);
+ if (!sfc) {
+ dev_err(&pdev->dev, "Can't allocate sfc\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+ dev_set_drvdata(&pdev->dev, sfc);
+
+ sfc->soc_data = soc_data;
+
+ /* initialize default output srate */
+ sfc->srate_out = 48000;
+
+#ifndef CONFIG_MACH_GRENADA
+ sfc->clk_sfc = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(sfc->clk_sfc)) {
+ dev_err(&pdev->dev, "Can't retrieve sfc clock\n");
+ ret = PTR_ERR(sfc->clk_sfc);
+ goto err;
+ }
+#endif
+
+ 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), pdev->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;
+ }
+
+ sfc->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
+ &tegra210_sfc_regmap_config);
+ if (IS_ERR(sfc->regmap)) {
+ dev_err(&pdev->dev, "regmap init failed\n");
+ ret = PTR_ERR(sfc->regmap);
+ goto err_clk_put;
+ }
+ regcache_cache_only(sfc->regmap, true);
+
+ if (of_property_read_u32(pdev->dev.of_node,
+ "nvidia,ahub-sfc-id",
+ &pdev->dev.id) < 0) {
+ dev_err(&pdev->dev,
+ "Missing property nvidia,ahub-sfc-id\n");
+ ret = -ENODEV;
+ goto err_clk_put;
+ }
+
+ pm_runtime_enable(&pdev->dev);
+ if (!pm_runtime_enabled(&pdev->dev)) {
+ ret = tegra210_sfc_runtime_resume(&pdev->dev);
+ if (ret)
+ goto err_pm_disable;
+ }
+
+ ret = snd_soc_register_codec(&pdev->dev, &tegra210_sfc_codec,
+ tegra210_sfc_dais,
+ ARRAY_SIZE(tegra210_sfc_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))
+ tegra210_sfc_runtime_suspend(&pdev->dev);
+err_pm_disable:
+ pm_runtime_disable(&pdev->dev);
+err_clk_put:
+#ifndef CONFIG_MACH_GRENADA
+ devm_clk_put(&pdev->dev, sfc->clk_sfc);
+#else
+ dev_err(&pdev->dev, "error\n");
+#endif
+err:
+ return ret;
+}
+
+static int tegra210_sfc_platform_remove(struct platform_device *pdev)
+{
+#ifndef CONFIG_MACH_GRENADA
+ struct tegra210_sfc *sfc = dev_get_drvdata(&pdev->dev);
+#endif
+ snd_soc_unregister_codec(&pdev->dev);
+
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ tegra210_sfc_runtime_suspend(&pdev->dev);
+
+#ifndef CONFIG_MACH_GRENADA
+ devm_clk_put(&pdev->dev, sfc->clk_sfc);
+#endif
+
+ return 0;
+}
+
+static const struct dev_pm_ops tegra210_sfc_pm_ops = {
+ SET_RUNTIME_PM_OPS(tegra210_sfc_runtime_suspend,
+ tegra210_sfc_runtime_resume, NULL)
+};
+
+static struct platform_driver tegra210_sfc_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = tegra210_sfc_of_match,
+ .pm = &tegra210_sfc_pm_ops,
+ },
+ .probe = tegra210_sfc_platform_probe,
+ .remove = tegra210_sfc_platform_remove,
+};
+module_platform_driver(tegra210_sfc_driver)
+
+MODULE_AUTHOR("Arun Shamanna Lakshmi ");
+MODULE_DESCRIPTION("Tegra210 SFC ASoC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, tegra210_sfc_of_match);
diff --git a/sound/soc/tegra-alt/tegra210_spdif_alt.c b/sound/soc/tegra-alt/tegra210_spdif_alt.c
new file mode 100644
index 00000000..9f36efec
--- /dev/null
+++ b/sound/soc/tegra-alt/tegra210_spdif_alt.c
@@ -0,0 +1,501 @@
+/*
+ * tegra210_spdif_alt.c - Tegra210 SPDIF driver
+ *
+ * Copyright (c) 2014 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 .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "tegra210_xbar_alt.h"
+#include "tegra210_spdif_alt.h"
+
+#define DRV_NAME "tegra210-spdif"
+
+static int tegra210_spdif_runtime_suspend(struct device *dev)
+{
+ struct tegra210_spdif *spdif = dev_get_drvdata(dev);
+
+ regcache_cache_only(spdif->regmap, true);
+
+#ifndef CONFIG_MACH_GRENADA
+ clk_disable_unprepare(spdif->clk_spdif_out);
+ clk_disable_unprepare(spdif->clk_spdif_in);
+#endif
+
+ return 0;
+}
+
+static int tegra210_spdif_runtime_resume(struct device *dev)
+{
+ struct tegra210_spdif *spdif = dev_get_drvdata(dev);
+ int ret = 0;
+
+#ifndef CONFIG_MACH_GRENADA
+ ret = clk_prepare_enable(spdif->clk_spdif_out);
+ if (ret) {
+ dev_err(dev, "spdif_out_clk_enable failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(spdif->clk_spdif_in);
+ if (ret) {
+ dev_err(dev, "spdif_in_clk_enable failed: %d\n", ret);
+ return ret;
+ }
+#endif
+
+ regcache_cache_only(spdif->regmap, false);
+
+ return ret;
+}
+
+static int tegra210_spdif_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ #ifndef CONFIG_MACH_GRENADA
+ struct device *dev = dai->dev;
+ struct tegra210_spdif *spdif = snd_soc_dai_get_drvdata(dai);
+ int spdif_out_clock_rate, spdif_in_clock_rate;
+ int ret;
+
+ switch (freq) {
+ case 32000:
+ spdif_out_clock_rate = 4096000;
+ spdif_in_clock_rate = 48000000;
+ break;
+ case 44100:
+ spdif_out_clock_rate = 5644800;
+ spdif_in_clock_rate = 48000000;
+ break;
+ case 48000:
+ spdif_out_clock_rate = 6144000;
+ spdif_in_clock_rate = 48000000;
+ break;
+ case 88200:
+ spdif_out_clock_rate = 11289600;
+ spdif_in_clock_rate = 72000000;
+ break;
+ case 96000:
+ spdif_out_clock_rate = 12288000;
+ spdif_in_clock_rate = 72000000;
+ break;
+ case 176400:
+ spdif_out_clock_rate = 22579200;
+ spdif_in_clock_rate = 108000000;
+ break;
+ case 192000:
+ spdif_out_clock_rate = 24576000;
+ spdif_in_clock_rate = 108000000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (dir == SND_SOC_CLOCK_OUT) {
+ ret = clk_set_rate(spdif->clk_spdif_out, spdif_out_clock_rate);
+ if (ret) {
+ dev_err(dev, "Can't set SPDIF Out clock rate: %d\n",
+ ret);
+ return ret;
+ }
+ } else {
+ ret = clk_set_rate(spdif->clk_spdif_in, spdif_in_clock_rate);
+ if (ret) {
+ dev_err(dev, "Can't set SPDIF In clock rate: %d\n",
+ ret);
+ return ret;
+ }
+ }
+ #endif
+
+ return 0;
+}
+
+static int tegra210_spdif_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+#ifndef CONFIG_MACH_GRENADA
+ struct device *dev = dai->dev;
+ struct tegra210_spdif *spdif = snd_soc_dai_get_drvdata(dai);
+ int channels, audio_bits, bit_mode;
+ struct tegra210_xbar_cif_conf cif_conf;
+
+ channels = params_channels(params);
+
+ if (channels < 2) {
+ dev_err(dev, "Doesn't support %d channels\n", channels);
+ return -EINVAL;
+ }
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ audio_bits = TEGRA210_AUDIOCIF_BITS_16;
+ bit_mode = TEGRA210_SPDIF_BIT_MODE16;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ audio_bits = TEGRA210_AUDIOCIF_BITS_32;
+ bit_mode = TEGRA210_SPDIF_BIT_MODERAW;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ memset(&cif_conf, 0, sizeof(cif_conf));
+ cif_conf.audio_channels = channels;
+ cif_conf.client_channels = channels;
+ cif_conf.audio_bits = audio_bits;
+ cif_conf.client_bits = audio_bits;
+
+ regmap_update_bits(spdif->regmap, TEGRA210_SPDIF_CTRL,
+ TEGRA210_SPDIF_CTRL_BIT_MODE_MASK,
+ bit_mode);
+
+ /* As a CODEC DAI, CAPTURE is transmit */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ spdif->soc_data->set_audio_cif(spdif->regmap,
+ TEGRA210_SPDIF_CIF_TXD_CTRL,
+ &cif_conf);
+ } else {
+ spdif->soc_data->set_audio_cif(spdif->regmap,
+ TEGRA210_SPDIF_CIF_RXD_CTRL,
+ &cif_conf);
+ }
+#endif
+ return 0;
+}
+
+static int tegra210_spdif_codec_probe(struct snd_soc_codec *codec)
+{
+ struct tegra210_spdif *spdif = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ codec->control_data = spdif->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 tegra210_spdif_dai_ops = {
+ .hw_params = tegra210_spdif_hw_params,
+ .set_sysclk = tegra210_spdif_set_dai_sysclk,
+};
+
+static struct snd_soc_dai_driver tegra210_spdif_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 = &tegra210_spdif_dai_ops,
+ }
+};
+
+static const struct snd_soc_dapm_widget tegra210_spdif_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, TEGRA210_SPDIF_CTRL, 29, 0),
+ SND_SOC_DAPM_AIF_OUT("DAP TX", NULL, 0, TEGRA210_SPDIF_CTRL, 28, 0),
+};
+
+static const struct snd_soc_dapm_route tegra210_spdif_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 tegra210_spdif_codec = {
+ .probe = tegra210_spdif_codec_probe,
+ .dapm_widgets = tegra210_spdif_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tegra210_spdif_widgets),
+ .dapm_routes = tegra210_spdif_routes,
+ .num_dapm_routes = ARRAY_SIZE(tegra210_spdif_routes),
+};
+
+static bool tegra210_spdif_wr_rd_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_SPDIF_CTRL:
+ case TEGRA210_SPDIF_STROBE_CTRL:
+ case TEGRA210_SPDIF_CIF_TXD_CTRL:
+ case TEGRA210_SPDIF_CIF_RXD_CTRL:
+ case TEGRA210_SPDIF_CIF_TXU_CTRL:
+ case TEGRA210_SPDIF_CIF_RXU_CTRL:
+ case TEGRA210_SPDIF_CH_STA_RX_A:
+ case TEGRA210_SPDIF_CH_STA_RX_B:
+ case TEGRA210_SPDIF_CH_STA_RX_C:
+ case TEGRA210_SPDIF_CH_STA_RX_D:
+ case TEGRA210_SPDIF_CH_STA_RX_E:
+ case TEGRA210_SPDIF_CH_STA_RX_F:
+ case TEGRA210_SPDIF_CH_STA_TX_A:
+ case TEGRA210_SPDIF_CH_STA_TX_B:
+ case TEGRA210_SPDIF_CH_STA_TX_C:
+ case TEGRA210_SPDIF_CH_STA_TX_D:
+ case TEGRA210_SPDIF_CH_STA_TX_E:
+ case TEGRA210_SPDIF_CH_STA_TX_F:
+ case TEGRA210_SPDIF_FLOWCTL_CTRL:
+ case TEGRA210_SPDIF_TX_STEP:
+ case TEGRA210_SPDIF_FLOW_STATUS:
+ case TEGRA210_SPDIF_FLOW_TOTAL:
+ case TEGRA210_SPDIF_FLOW_OVER:
+ case TEGRA210_SPDIF_FLOW_UNDER:
+ case TEGRA210_SPDIF_LCOEF_1_4_0:
+ case TEGRA210_SPDIF_LCOEF_1_4_1:
+ case TEGRA210_SPDIF_LCOEF_1_4_2:
+ case TEGRA210_SPDIF_LCOEF_1_4_3:
+ case TEGRA210_SPDIF_LCOEF_1_4_4:
+ case TEGRA210_SPDIF_LCOEF_1_4_5:
+ case TEGRA210_SPDIF_LCOEF_2_4_0:
+ case TEGRA210_SPDIF_LCOEF_2_4_1:
+ case TEGRA210_SPDIF_LCOEF_2_4_2:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static const struct regmap_config tegra210_spdif_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = TEGRA210_SPDIF_LCOEF_2_4_2,
+ .writeable_reg = tegra210_spdif_wr_rd_reg,
+ .readable_reg = tegra210_spdif_wr_rd_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct tegra210_spdif_soc_data soc_data_tegra210 = {
+ .set_audio_cif = tegra210_xbar_set_cif,
+};
+
+static const struct of_device_id tegra210_spdif_of_match[] = {
+ { .compatible = "nvidia,tegra210-spdif", .data = &soc_data_tegra210 },
+ {},
+};
+
+static int tegra210_spdif_platform_probe(struct platform_device *pdev)
+{
+ struct tegra210_spdif *spdif;
+ struct resource *mem, *memregion;
+ void __iomem *regs;
+ const struct of_device_id *match;
+ struct tegra210_spdif_soc_data *soc_data;
+ int ret;
+
+ match = of_match_device(tegra210_spdif_of_match, &pdev->dev);
+ if (!match) {
+ dev_err(&pdev->dev, "Error: No device match found\n");
+ ret = -ENODEV;
+ goto err;
+ }
+ soc_data = (struct tegra210_spdif_soc_data *)match->data;
+
+ spdif = devm_kzalloc(&pdev->dev, sizeof(struct tegra210_spdif),
+ GFP_KERNEL);
+ if (!spdif) {
+ dev_err(&pdev->dev, "Can't allocate tegra210_spdif\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+ dev_set_drvdata(&pdev->dev, spdif);
+
+ spdif->soc_data = soc_data;
+
+ #ifndef CONFIG_MACH_GRENADA
+ spdif->clk_spdif_out = devm_clk_get(&pdev->dev, "spdif_out");
+ if (IS_ERR(spdif->clk_spdif_out)) {
+ dev_err(&pdev->dev, "Can't retrieve spdif clock\n");
+ ret = PTR_ERR(spdif->clk_spdif_out);
+ goto err;
+ }
+
+ spdif->clk_spdif_in = devm_clk_get(&pdev->dev, "spdif_in");
+ if (IS_ERR(spdif->clk_spdif_in)) {
+ dev_err(&pdev->dev, "Can't retrieve spdif clock\n");
+ ret = PTR_ERR(spdif->clk_spdif_in);
+ goto err_spdif_out_clk_put;
+ }
+ #endif
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "No memory resource\n");
+ ret = -ENODEV;
+ goto err_spdif_in_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_spdif_in_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_spdif_in_clk_put;
+ }
+
+ spdif->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
+ &tegra210_spdif_regmap_config);
+ if (IS_ERR(spdif->regmap)) {
+ dev_err(&pdev->dev, "regmap init failed\n");
+ ret = PTR_ERR(spdif->regmap);
+ goto err_spdif_in_clk_put;
+ }
+ regcache_cache_only(spdif->regmap, true);
+
+ if (of_property_read_u32(pdev->dev.of_node,
+ "nvidia,ahub-spdif-id",
+ &pdev->dev.id) < 0) {
+ dev_err(&pdev->dev,
+ "Missing property nvidia,ahub-spdif-id\n");
+ ret = -ENODEV;
+ goto err_spdif_in_clk_put;
+ }
+
+ pm_runtime_enable(&pdev->dev);
+ if (!pm_runtime_enabled(&pdev->dev)) {
+ ret = tegra210_spdif_runtime_resume(&pdev->dev);
+ if (ret)
+ goto err_pm_disable;
+ }
+
+ ret = snd_soc_register_codec(&pdev->dev, &tegra210_spdif_codec,
+ tegra210_spdif_dais,
+ ARRAY_SIZE(tegra210_spdif_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))
+ tegra210_spdif_runtime_suspend(&pdev->dev);
+err_pm_disable:
+ pm_runtime_disable(&pdev->dev);
+err_spdif_in_clk_put:
+#ifndef CONFIG_MACH_GRENADA
+ devm_clk_put(&pdev->dev, spdif->clk_spdif_in);
+#else
+ dev_err(&pdev->dev, "error\n");
+#endif
+#ifndef CONFIG_MACH_GRENADA
+err_spdif_out_clk_put:
+ devm_clk_put(&pdev->dev, spdif->clk_spdif_out);
+#else
+ dev_err(&pdev->dev, "error\n");
+#endif
+err:
+ return ret;
+}
+
+static int tegra210_spdif_platform_remove(struct platform_device *pdev)
+{
+ #ifndef CONFIG_MACH_GRENADA
+ struct tegra210_spdif *spdif = dev_get_drvdata(&pdev->dev);
+ #endif
+
+ snd_soc_unregister_codec(&pdev->dev);
+
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ tegra210_spdif_runtime_suspend(&pdev->dev);
+
+ #ifndef CONFIG_MACH_GRENADA
+ devm_clk_put(&pdev->dev, spdif->clk_spdif_out);
+ devm_clk_put(&pdev->dev, spdif->clk_spdif_in);
+ #endif
+
+ return 0;
+}
+
+static const struct dev_pm_ops tegra210_spdif_pm_ops = {
+ SET_RUNTIME_PM_OPS(tegra210_spdif_runtime_suspend,
+ tegra210_spdif_runtime_resume, NULL)
+};
+
+static struct platform_driver tegra210_spdif_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = tegra210_spdif_of_match,
+ .pm = &tegra210_spdif_pm_ops,
+ },
+ .probe = tegra210_spdif_platform_probe,
+ .remove = tegra210_spdif_platform_remove,
+};
+module_platform_driver(tegra210_spdif_driver);
+
+MODULE_AUTHOR("Arun Shamanna Lakshmi ");
+MODULE_AUTHOR("Songhee Baek ");
+MODULE_DESCRIPTION("Tegra210 SPDIF ASoC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, tegra210_spdif_of_match);
diff --git a/sound/soc/tegra-alt/tegra210_xbar_alt.c b/sound/soc/tegra-alt/tegra210_xbar_alt.c
new file mode 100644
index 00000000..a897f61b
--- /dev/null
+++ b/sound/soc/tegra-alt/tegra210_xbar_alt.c
@@ -0,0 +1,854 @@
+/*
+ * tegra210_xbar_alt.c - Tegra210 XBAR driver
+ *
+ * Copyright (c) 2014 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 .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "tegra210_xbar_alt.h"
+
+#define DRV_NAME "tegra210-axbar"
+
+struct tegra210_xbar *xbar;
+
+static const struct regmap_config tegra210_xbar_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = TEGRA210_XBAR_PART2_RX + (TEGRA210_XBAR_RX_STRIDE *
+ (TEGRA210_XBAR_AUDIO_RX_COUNT - 1)),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int tegra210_xbar_runtime_suspend(struct device *dev)
+{
+
+ regcache_cache_only(xbar->regmap, true);
+
+ clk_disable(xbar->clk);
+
+ return 0;
+}
+
+static int tegra210_xbar_runtime_resume(struct device *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 tegra210_xbar_codec_probe(struct snd_soc_codec *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 tegra210_xbar_dais[] = {
+ DAI(ADMAIF1),
+ DAI(ADMAIF2),
+ DAI(ADMAIF3),
+ DAI(ADMAIF4),
+ DAI(ADMAIF5),
+ DAI(ADMAIF6),
+ DAI(ADMAIF7),
+ DAI(ADMAIF8),
+ DAI(ADMAIF9),
+ DAI(ADMAIF10),
+ DAI(I2S1),
+ DAI(I2S2),
+ DAI(I2S3),
+ DAI(I2S4),
+ DAI(I2S5),
+ DAI(SFC1),
+ DAI(SFC2),
+ DAI(SFC3),
+ DAI(SFC4),
+ DAI(MIXER1-1),
+ DAI(MIXER1-2),
+ DAI(MIXER1-3),
+ DAI(MIXER1-4),
+ DAI(MIXER1-5),
+ DAI(MIXER1-6),
+ DAI(MIXER1-7),
+ DAI(MIXER1-8),
+ DAI(MIXER1-9),
+ DAI(MIXER1-10),
+ DAI(SPDIF1-1),
+ DAI(SPDIF1-2),
+ DAI(AFC1),
+ DAI(AFC2),
+ DAI(AFC3),
+ DAI(AFC4),
+ DAI(AFC5),
+ DAI(AFC6),
+ DAI(OPE1),
+ DAI(OPE2),
+ DAI(SPKPROT1),
+ DAI(MVC1),
+ DAI(MVC2),
+ DAI(IQC1-1),
+ DAI(IQC1-2),
+ DAI(IQC2-1),
+ DAI(IQC2-2),
+ DAI(DMIC1),
+ DAI(DMIC2),
+ DAI(DMIC3),
+ DAI(AMX1),
+ DAI(AMX1-1),
+ DAI(AMX1-2),
+ DAI(AMX1-3),
+ DAI(AMX1-4),
+ DAI(AMX2),
+ DAI(AMX2-1),
+ DAI(AMX2-2),
+ DAI(AMX2-3),
+ DAI(AMX2-4),
+ DAI(ADX1-1),
+ DAI(ADX1-2),
+ DAI(ADX1-3),
+ DAI(ADX1-4),
+ DAI(ADX1),
+ DAI(ADX2-1),
+ DAI(ADX2-2),
+ DAI(ADX2-3),
+ DAI(ADX2-4),
+ DAI(ADX2),
+};
+
+static const char * const tegra210_xbar_mux_texts[] = {
+ "None",
+ "ADMAIF1",
+ "ADMAIF2",
+ "ADMAIF3",
+ "ADMAIF4",
+ "ADMAIF5",
+ "ADMAIF6",
+ "ADMAIF7",
+ "ADMAIF8",
+ "ADMAIF9",
+ "ADMAIF10",
+ "I2S1",
+ "I2S2",
+ "I2S3",
+ "I2S4",
+ "I2S5",
+ "SFC1",
+ "SFC2",
+ "SFC3",
+ "SFC4",
+ /* index 0..19 above are inputs of PART0 Mux */
+ "MIXER1-1",
+ "MIXER1-2",
+ "MIXER1-3",
+ "MIXER1-4",
+ "MIXER1-5",
+ "AMX1",
+ "AMX2",
+ "SPDIF1-1",
+ "SPDIF1-2",
+ "AFC1",
+ "AFC2",
+ "AFC3",
+ "AFC4",
+ "AFC5",
+ "AFC6",
+ /* index 20..34 above are inputs of PART1 Mux */
+ "OPE1",
+ "OPE2",
+ "SPKPROT1",
+ "MVC1",
+ "MVC2",
+ "IQC1-1",
+ "IQC1-2",
+ "IQC2-1",
+ "IQC2-2",
+ "DMIC1",
+ "DMIC2",
+ "DMIC3",
+ "ADX1-1",
+ "ADX1-2",
+ "ADX1-3",
+ "ADX1-4",
+ "ADX2-1",
+ "ADX2-2",
+ "ADX2-3",
+ "ADX2-4",
+ /* index 35..53 above are inputs of PART2 Mux */
+};
+
+#define MUX_VALUE(npart, nbit) (1 + nbit + npart * 32)
+static const int tegra210_xbar_mux_values[] = {
+ /* Mux0 input, Mux1 input, Mux2 input */
+ 0,
+ MUX_VALUE(0, 0),
+ MUX_VALUE(0, 1),
+ MUX_VALUE(0, 2),
+ MUX_VALUE(0, 3),
+ MUX_VALUE(0, 4),
+ MUX_VALUE(0, 5),
+ MUX_VALUE(0, 6),
+ MUX_VALUE(0, 7),
+ MUX_VALUE(0, 8),
+ MUX_VALUE(0, 9),
+ MUX_VALUE(0, 16),
+ MUX_VALUE(0, 17),
+ MUX_VALUE(0, 18),
+ MUX_VALUE(0, 19),
+ MUX_VALUE(0, 20),
+ MUX_VALUE(0, 24),
+ MUX_VALUE(0, 25),
+ MUX_VALUE(0, 26),
+ MUX_VALUE(0, 27),
+ /* index 0..19 above are inputs of PART0 Mux */
+ MUX_VALUE(1, 0),
+ MUX_VALUE(1, 1),
+ MUX_VALUE(1, 2),
+ MUX_VALUE(1, 3),
+ MUX_VALUE(1, 4),
+ MUX_VALUE(1, 8),
+ MUX_VALUE(1, 9),
+ MUX_VALUE(1, 20),
+ MUX_VALUE(1, 21),
+ MUX_VALUE(1, 24),
+ MUX_VALUE(1, 25),
+ MUX_VALUE(1, 26),
+ MUX_VALUE(1, 27),
+ MUX_VALUE(1, 28),
+ MUX_VALUE(1, 29),
+ /* index 20..34 above are inputs of PART1 Mux */
+ MUX_VALUE(2, 0),
+ MUX_VALUE(2, 1),
+ MUX_VALUE(2, 4),
+ MUX_VALUE(2, 8),
+ MUX_VALUE(2, 9),
+ MUX_VALUE(2, 12),
+ MUX_VALUE(2, 13),
+ MUX_VALUE(2, 14),
+ MUX_VALUE(2, 15),
+ MUX_VALUE(2, 18),
+ MUX_VALUE(2, 19),
+ MUX_VALUE(2, 20),
+ MUX_VALUE(2, 24),
+ MUX_VALUE(2, 25),
+ MUX_VALUE(2, 26),
+ MUX_VALUE(2, 27),
+ MUX_VALUE(2, 28),
+ MUX_VALUE(2, 29),
+ MUX_VALUE(2, 30),
+ MUX_VALUE(2, 31),
+ /* index 35..53 above are inputs of PART2 Mux */
+};
+
+#define MUX0_REG(id) (TEGRA210_XBAR_PART0_RX + \
+ (TEGRA210_XBAR_RX_STRIDE * (id)))
+
+#define MUX1_REG(id) (TEGRA210_XBAR_PART1_RX + \
+ (TEGRA210_XBAR_RX_STRIDE * (id)))
+
+#define MUX2_REG(id) (TEGRA210_XBAR_PART2_RX + \
+ (TEGRA210_XBAR_RX_STRIDE * (id)))
+
+#define MUX_ENUM_CTRL_DECL(ename, id) \
+ int ename##_regs[3] = { MUX0_REG(id), MUX1_REG(id), MUX2_REG(id) }; \
+ unsigned int ename##_masks[3] = { 0xf1f03ff, 0x3f30031f, 0xff1cf313 }; \
+ SOC_VALUE_ENUM_ONEHOT_DECL(ename##_enum, ename##_regs, ename##_masks, 3, \
+ tegra210_xbar_mux_texts, tegra210_xbar_mux_values); \
+ static const struct snd_kcontrol_new ename##_control = \
+ SOC_DAPM_ENUM_ONEHOT("Route", ename##_enum)
+
+MUX_ENUM_CTRL_DECL(admaif1_tx, 0x00);
+MUX_ENUM_CTRL_DECL(admaif2_tx, 0x01);
+MUX_ENUM_CTRL_DECL(admaif3_tx, 0x02);
+MUX_ENUM_CTRL_DECL(admaif4_tx, 0x03);
+MUX_ENUM_CTRL_DECL(admaif5_tx, 0x04);
+MUX_ENUM_CTRL_DECL(admaif6_tx, 0x05);
+MUX_ENUM_CTRL_DECL(admaif7_tx, 0x06);
+MUX_ENUM_CTRL_DECL(admaif8_tx, 0x07);
+MUX_ENUM_CTRL_DECL(admaif9_tx, 0x08);
+MUX_ENUM_CTRL_DECL(admaif10_tx, 0x09);
+MUX_ENUM_CTRL_DECL(i2s1_tx, 0x10);
+MUX_ENUM_CTRL_DECL(i2s2_tx, 0x11);
+MUX_ENUM_CTRL_DECL(i2s3_tx, 0x12);
+MUX_ENUM_CTRL_DECL(i2s4_tx, 0x13);
+MUX_ENUM_CTRL_DECL(i2s5_tx, 0x14);
+MUX_ENUM_CTRL_DECL(sfc1_tx, 0x18);
+MUX_ENUM_CTRL_DECL(sfc2_tx, 0x19);
+MUX_ENUM_CTRL_DECL(sfc3_tx, 0x1a);
+MUX_ENUM_CTRL_DECL(sfc4_tx, 0x1b);
+MUX_ENUM_CTRL_DECL(mixer11_tx, 0x20);
+MUX_ENUM_CTRL_DECL(mixer12_tx, 0x21);
+MUX_ENUM_CTRL_DECL(mixer13_tx, 0x22);
+MUX_ENUM_CTRL_DECL(mixer14_tx, 0x23);
+MUX_ENUM_CTRL_DECL(mixer15_tx, 0x24);
+MUX_ENUM_CTRL_DECL(mixer16_tx, 0x25);
+MUX_ENUM_CTRL_DECL(mixer17_tx, 0x26);
+MUX_ENUM_CTRL_DECL(mixer18_tx, 0x27);
+MUX_ENUM_CTRL_DECL(mixer19_tx, 0x28);
+MUX_ENUM_CTRL_DECL(mixer110_tx, 0x29);
+MUX_ENUM_CTRL_DECL(spdif11_tx, 0x30);
+MUX_ENUM_CTRL_DECL(spdif12_tx, 0x31);
+MUX_ENUM_CTRL_DECL(afc1_tx, 0x34);
+MUX_ENUM_CTRL_DECL(afc2_tx, 0x35);
+MUX_ENUM_CTRL_DECL(afc3_tx, 0x36);
+MUX_ENUM_CTRL_DECL(afc4_tx, 0x37);
+MUX_ENUM_CTRL_DECL(afc5_tx, 0x38);
+MUX_ENUM_CTRL_DECL(afc6_tx, 0x39);
+MUX_ENUM_CTRL_DECL(ope1_tx, 0x40);
+MUX_ENUM_CTRL_DECL(ope2_tx, 0x41);
+MUX_ENUM_CTRL_DECL(spkprot_tx, 0x44);
+MUX_ENUM_CTRL_DECL(mvc1_tx, 0x48);
+MUX_ENUM_CTRL_DECL(mvc2_tx, 0x49);
+MUX_ENUM_CTRL_DECL(amx11_tx, 0x50);
+MUX_ENUM_CTRL_DECL(amx12_tx, 0x51);
+MUX_ENUM_CTRL_DECL(amx13_tx, 0x52);
+MUX_ENUM_CTRL_DECL(amx14_tx, 0x53);
+MUX_ENUM_CTRL_DECL(amx21_tx, 0x54);
+MUX_ENUM_CTRL_DECL(amx22_tx, 0x55);
+MUX_ENUM_CTRL_DECL(amx23_tx, 0x56);
+MUX_ENUM_CTRL_DECL(amx24_tx, 0x57);
+MUX_ENUM_CTRL_DECL(adx1_tx, 0x58);
+MUX_ENUM_CTRL_DECL(adx2_tx, 0x59);
+
+#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 tegra210_xbar_codec.num_dapm_widgets near the end of
+ * tegra210_xbar_probe()
+ */
+static const struct snd_soc_dapm_widget tegra210_xbar_widgets[] = {
+ WIDGETS("ADMAIF1", admaif1_tx),
+ WIDGETS("ADMAIF2", admaif2_tx),
+ WIDGETS("ADMAIF3", admaif3_tx),
+ WIDGETS("ADMAIF4", admaif4_tx),
+ WIDGETS("ADMAIF5", admaif5_tx),
+ WIDGETS("ADMAIF6", admaif6_tx),
+ WIDGETS("ADMAIF7", admaif7_tx),
+ WIDGETS("ADMAIF8", admaif8_tx),
+ WIDGETS("ADMAIF9", admaif9_tx),
+ WIDGETS("ADMAIF10", admaif10_tx),
+ WIDGETS("I2S1", i2s1_tx),
+ WIDGETS("I2S2", i2s2_tx),
+ WIDGETS("I2S3", i2s3_tx),
+ WIDGETS("I2S4", i2s4_tx),
+ WIDGETS("I2S5", i2s5_tx),
+ WIDGETS("SFC1", sfc1_tx),
+ WIDGETS("SFC2", sfc2_tx),
+ WIDGETS("SFC3", sfc3_tx),
+ WIDGETS("SFC4", sfc4_tx),
+ WIDGETS("MIXER1-1", mixer11_tx),
+ WIDGETS("MIXER1-2", mixer12_tx),
+ WIDGETS("MIXER1-3", mixer13_tx),
+ WIDGETS("MIXER1-4", mixer14_tx),
+ WIDGETS("MIXER1-5", mixer15_tx),
+ WIDGETS("MIXER1-6", mixer16_tx),
+ WIDGETS("MIXER1-7", mixer17_tx),
+ WIDGETS("MIXER1-8", mixer18_tx),
+ WIDGETS("MIXER1-9", mixer19_tx),
+ WIDGETS("MIXER1-10", mixer110_tx),
+ WIDGETS("SPDIF1-1", spdif11_tx),
+ WIDGETS("SPDIF1-2", spdif12_tx),
+ WIDGETS("AFC1", afc1_tx),
+ WIDGETS("AFC2", afc2_tx),
+ WIDGETS("AFC3", afc3_tx),
+ WIDGETS("AFC4", afc4_tx),
+ WIDGETS("AFC5", afc5_tx),
+ WIDGETS("AFC6", afc6_tx),
+ WIDGETS("OPE1", ope1_tx),
+ WIDGETS("OPE2", ope2_tx),
+ WIDGETS("SPKPROT1", spkprot_tx),
+ WIDGETS("MVC1", mvc1_tx),
+ WIDGETS("MVC2", mvc2_tx),
+ WIDGETS("AMX1-1", amx11_tx),
+ WIDGETS("AMX1-2", amx12_tx),
+ WIDGETS("AMX1-3", amx13_tx),
+ WIDGETS("AMX1-4", amx14_tx),
+ WIDGETS("AMX2-1", amx21_tx),
+ WIDGETS("AMX2-2", amx22_tx),
+ WIDGETS("AMX2-3", amx23_tx),
+ WIDGETS("AMX2-4", amx24_tx),
+ WIDGETS("ADX1", adx1_tx),
+ WIDGETS("ADX2", adx2_tx),
+ TX_WIDGETS("IQC1-1"),
+ TX_WIDGETS("IQC1-2"),
+ TX_WIDGETS("IQC2-1"),
+ TX_WIDGETS("IQC2-2"),
+ TX_WIDGETS("DMIC1"),
+ TX_WIDGETS("DMIC2"),
+ TX_WIDGETS("DMIC3"),
+ TX_WIDGETS("AMX1"),
+ TX_WIDGETS("ADX1-1"),
+ TX_WIDGETS("ADX1-2"),
+ TX_WIDGETS("ADX1-3"),
+ TX_WIDGETS("ADX1-4"),
+ TX_WIDGETS("AMX2"),
+ TX_WIDGETS("ADX2-1"),
+ TX_WIDGETS("ADX2-2"),
+ TX_WIDGETS("ADX2-3"),
+ TX_WIDGETS("ADX2-4"),
+};
+
+#define TEGRA210_ROUTES(name) \
+ { name " RX", NULL, name " Receive"}, \
+ { name " Transmit", NULL, name " TX"}, \
+ { name " TX", NULL, name " Mux" }, \
+ { name " Mux", "ADMAIF1", "ADMAIF1 RX" }, \
+ { name " Mux", "ADMAIF2", "ADMAIF2 RX" }, \
+ { name " Mux", "ADMAIF3", "ADMAIF3 RX" }, \
+ { name " Mux", "ADMAIF4", "ADMAIF4 RX" }, \
+ { name " Mux", "ADMAIF5", "ADMAIF5 RX" }, \
+ { name " Mux", "ADMAIF6", "ADMAIF6 RX" }, \
+ { name " Mux", "ADMAIF7", "ADMAIF7 RX" }, \
+ { name " Mux", "ADMAIF8", "ADMAIF8 RX" }, \
+ { name " Mux", "ADMAIF9", "ADMAIF9 RX" }, \
+ { name " Mux", "ADMAIF10", "ADMAIF10 RX" }, \
+ { name " Mux", "I2S1", "I2S1 RX" }, \
+ { name " Mux", "I2S2", "I2S2 RX" }, \
+ { name " Mux", "I2S3", "I2S3 RX" }, \
+ { name " Mux", "I2S4", "I2S4 RX" }, \
+ { name " Mux", "I2S5", "I2S5 RX" }, \
+ { name " Mux", "SFC1", "SFC1 RX" }, \
+ { name " Mux", "SFC2", "SFC2 RX" }, \
+ { name " Mux", "SFC3", "SFC3 RX" }, \
+ { name " Mux", "SFC4", "SFC4 RX" }, \
+ { name " Mux", "MIXER1-1", "MIXER1-1 RX" }, \
+ { name " Mux", "MIXER1-2", "MIXER1-2 RX" }, \
+ { name " Mux", "MIXER1-3", "MIXER1-3 RX" }, \
+ { name " Mux", "MIXER1-4", "MIXER1-4 RX" }, \
+ { name " Mux", "MIXER1-5", "MIXER1-5 RX" }, \
+ { name " Mux", "SPDIF1-1", "SPDIF1-1 RX" }, \
+ { name " Mux", "SPDIF1-2", "SPDIF1-2 RX" }, \
+ { name " Mux", "AFC1", "AFC1 RX" }, \
+ { name " Mux", "AFC2", "AFC2 RX" }, \
+ { name " Mux", "AFC3", "AFC3 RX" }, \
+ { name " Mux", "AFC4", "AFC4 RX" }, \
+ { name " Mux", "AFC5", "AFC5 RX" }, \
+ { name " Mux", "AFC6", "AFC6 RX" }, \
+ { name " Mux", "OPE1", "OPE1 RX" }, \
+ { name " Mux", "OPE2", "OPE2 RX" }, \
+ { name " Mux", "MVC1", "MVC1 RX" }, \
+ { name " Mux", "MVC2", "MVC2 RX" }, \
+ { name " Mux", "IQC1-1", "IQC1-1 RX" }, \
+ { name " Mux", "IQC1-2", "IQC1-2 RX" }, \
+ { name " Mux", "IQC2-1", "IQC2-1 RX" }, \
+ { name " Mux", "IQC2-2", "IQC2-2 RX" }, \
+ { name " Mux", "AMX1", "AMX1 RX" }, \
+ { name " Mux", "ADX1-1", "ADX1-1 RX" }, \
+ { name " Mux", "ADX1-2", "ADX1-2 RX" }, \
+ { name " Mux", "ADX1-3", "ADX1-3 RX" }, \
+ { name " Mux", "ADX1-4", "ADX1-4 RX" }, \
+ { name " Mux", "AMX2", "AMX1 RX" }, \
+ { name " Mux", "ADX2-1", "ADX1-1 RX" }, \
+ { name " Mux", "ADX2-2", "ADX1-2 RX" }, \
+ { name " Mux", "ADX2-3", "ADX1-3 RX" }, \
+ { name " Mux", "ADX2-4", "ADX1-4 RX" },
+
+
+#define IN_OUT_ROUTES(name) \
+ { name " RX", NULL, name " Receive" }, \
+ { name " Transmit", NULL, name " TX" },
+
+/*
+ * The number of entries in, and order of, this array is closely tied to the
+ * calculation of tegra210_xbar_codec.num_dapm_routes near the end of
+ * tegra210_xbar_probe()
+ */
+static const struct snd_soc_dapm_route tegra210_xbar_routes[] = {
+ TEGRA210_ROUTES("ADMAIF1")
+ TEGRA210_ROUTES("ADMAIF2")
+ TEGRA210_ROUTES("ADMAIF3")
+ TEGRA210_ROUTES("ADMAIF4")
+ TEGRA210_ROUTES("ADMAIF5")
+ TEGRA210_ROUTES("ADMAIF6")
+ TEGRA210_ROUTES("ADMAIF7")
+ TEGRA210_ROUTES("ADMAIF8")
+ TEGRA210_ROUTES("ADMAIF9")
+ TEGRA210_ROUTES("ADMAIF10")
+ TEGRA210_ROUTES("I2S1")
+ TEGRA210_ROUTES("I2S2")
+ TEGRA210_ROUTES("I2S3")
+ TEGRA210_ROUTES("I2S4")
+ TEGRA210_ROUTES("I2S5")
+ TEGRA210_ROUTES("SFC1")
+ TEGRA210_ROUTES("SFC2")
+ TEGRA210_ROUTES("SFC3")
+ TEGRA210_ROUTES("SFC4")
+ TEGRA210_ROUTES("MIXER1-1")
+ TEGRA210_ROUTES("MIXER1-2")
+ TEGRA210_ROUTES("MIXER1-3")
+ TEGRA210_ROUTES("MIXER1-4")
+ TEGRA210_ROUTES("MIXER1-5")
+ TEGRA210_ROUTES("MIXER1-6")
+ TEGRA210_ROUTES("MIXER1-7")
+ TEGRA210_ROUTES("MIXER1-8")
+ TEGRA210_ROUTES("MIXER1-9")
+ TEGRA210_ROUTES("MIXER1-10")
+ TEGRA210_ROUTES("SPDIF1-1")
+ TEGRA210_ROUTES("SPDIF1-2")
+ TEGRA210_ROUTES("AFC1")
+ TEGRA210_ROUTES("AFC2")
+ TEGRA210_ROUTES("AFC3")
+ TEGRA210_ROUTES("AFC4")
+ TEGRA210_ROUTES("AFC5")
+ TEGRA210_ROUTES("AFC6")
+ TEGRA210_ROUTES("OPE1")
+ TEGRA210_ROUTES("OPE2")
+ TEGRA210_ROUTES("SPKPROT1")
+ TEGRA210_ROUTES("MVC1")
+ TEGRA210_ROUTES("MVC2")
+ TEGRA210_ROUTES("AMX1-1")
+ TEGRA210_ROUTES("AMX1-2")
+ TEGRA210_ROUTES("AMX1-3")
+ TEGRA210_ROUTES("AMX1-4")
+ TEGRA210_ROUTES("AMX2-1")
+ TEGRA210_ROUTES("AMX2-2")
+ TEGRA210_ROUTES("AMX2-3")
+ TEGRA210_ROUTES("AMX2-4")
+ TEGRA210_ROUTES("ADX1")
+ TEGRA210_ROUTES("ADX2")
+ IN_OUT_ROUTES("IQC1-1")
+ IN_OUT_ROUTES("IQC1-2")
+ IN_OUT_ROUTES("IQC2-1")
+ IN_OUT_ROUTES("IQC2-1")
+ IN_OUT_ROUTES("DMIC1")
+ IN_OUT_ROUTES("DMIC2")
+ IN_OUT_ROUTES("DMIC3")
+ IN_OUT_ROUTES("AMX1")
+ IN_OUT_ROUTES("AMX2")
+ IN_OUT_ROUTES("ADX1-1")
+ IN_OUT_ROUTES("ADX1-2")
+ IN_OUT_ROUTES("ADX1-3")
+ IN_OUT_ROUTES("ADX1-4")
+ IN_OUT_ROUTES("ADX2-1")
+ IN_OUT_ROUTES("ADX2-2")
+ IN_OUT_ROUTES("ADX2-3")
+ IN_OUT_ROUTES("ADX2-4")
+};
+
+static struct snd_soc_codec_driver tegra210_xbar_codec = {
+ .probe = tegra210_xbar_codec_probe,
+ .dapm_widgets = tegra210_xbar_widgets,
+ .dapm_routes = tegra210_xbar_routes,
+ .num_dapm_widgets = ARRAY_SIZE(tegra210_xbar_widgets),
+ .num_dapm_routes = ARRAY_SIZE(tegra210_xbar_routes),
+};
+
+static const struct tegra210_xbar_soc_data soc_data_tegra210 = {
+ .regmap_config = &tegra210_xbar_regmap_config,
+};
+
+static const struct of_device_id tegra210_xbar_of_match[] = {
+ { .compatible = "nvidia,tegra210-axbar", .data = &soc_data_tegra210 },
+ {},
+};
+
+#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;
+} configlink_clocks[] = {
+ { "ape" },
+};
+
+/* FIXME: base address for T210 */
+struct of_dev_auxdata tegra210_xbar_auxdata[] = {
+ OF_DEV_AUXDATA("nvidia,tegra210-admaif", 0x702d0000, "tegra210-admaif", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra210-i2s", 0x702d1000, "tegra210-i2s.0", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra210-i2s", 0x702d1100, "tegra210-i2s.1", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra210-i2s", 0x702d1200, "tegra210-i2s.2", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra210-i2s", 0x702d1300, "tegra210-i2s.3", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra210-i2s", 0x702d1400, "tegra210-i2s.4", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra210-amx", 0x702d3000, "tegra210-amx.0", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra210-amx", 0x702d3100, "tegra210-amx.1", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra210-adx", 0x702d3800, "tegra210-adx.0", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra210-adx", 0x702d3900, "tegra210-adx.1", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra210-afc", 0x702d7000, "tegra210-afc.0", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra210-afc", 0x702d7100, "tegra210-afc.1", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra210-afc", 0x702d7200, "tegra210-afc.2", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra210-afc", 0x702d7300, "tegra210-afc.3", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra210-afc", 0x702d7400, "tegra210-afc.4", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra210-afc", 0x702d7500, "tegra210-afc.5", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra210-sfc", 0x702d2000, "tegra210-sfc.0", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra210-sfc", 0x702d2200, "tegra210-sfc.1", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra210-sfc", 0x702d2400, "tegra210-sfc.2", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra210-sfc", 0x702d2600, "tegra210-sfc.3", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra210-mvc", 0x702da000, "tegra210-mvc.0", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra210-mvc", 0x702da200, "tegra210-mvc.1", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra210-amixer", 0x702dbb00, "tegra210-mixer", NULL),
+ OF_DEV_AUXDATA("nvidia,tegra210-spdif", 0x702d6000, "tegra210-spdif", NULL),
+ {}
+};
+
+static int tegra210_xbar_probe(struct platform_device *pdev)
+{
+ struct clk *clk;
+ void __iomem *regs;
+ int ret, i;
+ const struct of_device_id *match;
+ struct tegra210_xbar_soc_data *soc_data;
+ struct clk *parent_clk;
+
+ match = of_match_device(tegra210_xbar_of_match, &pdev->dev);
+ if (!match) {
+ dev_err(&pdev->dev, "Error: No device match found\n");
+ ret = -ENODEV;
+ goto err;
+ }
+ soc_data = (struct tegra210_xbar_soc_data *)match->data;
+
+ /*
+ * The TEGRA APE XBAR client 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++) {
+ 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);
+ }
+
+ xbar = devm_kzalloc(&pdev->dev, sizeof(*xbar), GFP_KERNEL);
+ if (!xbar) {
+ dev_err(&pdev->dev, "Can't allocate xbar\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ 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_rate(xbar->clk_parent, 24560000);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to set clock rate of pll_a_out0\n");
+ 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 = tegra210_xbar_runtime_resume(&pdev->dev);
+ if (ret)
+ goto err_pm_disable;
+ }
+
+ ret = snd_soc_register_codec(&pdev->dev, &tegra210_xbar_codec,
+ tegra210_xbar_dais, ARRAY_SIZE(tegra210_xbar_dais));
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Could not register CODEC: %d\n", ret);
+ goto err_suspend;
+ }
+
+ of_platform_populate(pdev->dev.of_node, NULL, tegra210_xbar_auxdata,
+ &pdev->dev);
+ return 0;
+
+err_suspend:
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ tegra210_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 tegra210_xbar_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ tegra210_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 tegra210_xbar_pm_ops = {
+ SET_RUNTIME_PM_OPS(tegra210_xbar_runtime_suspend,
+ tegra210_xbar_runtime_resume, NULL)
+};
+
+static struct platform_driver tegra210_xbar_driver = {
+ .probe = tegra210_xbar_probe,
+ .remove = tegra210_xbar_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = tegra210_xbar_of_match,
+ .pm = &tegra210_xbar_pm_ops,
+ },
+};
+module_platform_driver(tegra210_xbar_driver);
+
+void tegra210_xbar_set_cif(struct regmap *regmap, unsigned int reg,
+ struct tegra210_xbar_cif_conf *conf)
+{
+ unsigned int value;
+
+ value = (conf->threshold <<
+ TEGRA210_AUDIOCIF_CTRL_FIFO_THRESHOLD_SHIFT) |
+ ((conf->audio_channels - 1) <<
+ TEGRA210_AUDIOCIF_CTRL_AUDIO_CHANNELS_SHIFT) |
+ ((conf->client_channels - 1) <<
+ TEGRA210_AUDIOCIF_CTRL_CLIENT_CHANNELS_SHIFT) |
+ (conf->audio_bits <<
+ TEGRA210_AUDIOCIF_CTRL_AUDIO_BITS_SHIFT) |
+ (conf->client_bits <<
+ TEGRA210_AUDIOCIF_CTRL_CLIENT_BITS_SHIFT) |
+ (conf->expand <<
+ TEGRA210_AUDIOCIF_CTRL_EXPAND_SHIFT) |
+ (conf->stereo_conv <<
+ TEGRA210_AUDIOCIF_CTRL_STEREO_CONV_SHIFT) |
+ (conf->replicate <<
+ TEGRA210_AUDIOCIF_CTRL_REPLICATE_SHIFT) |
+ (conf->truncate <<
+ TEGRA210_AUDIOCIF_CTRL_TRUNCATE_SHIFT) |
+ (conf->mono_conv <<
+ TEGRA210_AUDIOCIF_CTRL_MONO_CONV_SHIFT);
+
+ regmap_update_bits(regmap, reg, 0x3fffffff, value);
+}
+EXPORT_SYMBOL_GPL(tegra210_xbar_set_cif);
+
+int tegra210_xbar_read_reg (unsigned int reg, unsigned int *val)
+{
+ int ret;
+
+ ret = regmap_read (xbar->regmap, reg, val);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tegra210_xbar_read_reg);
+
+MODULE_AUTHOR("Stephen Warren ");
+MODULE_DESCRIPTION("Tegra210 XBAR driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);