mirror of
git://nv-tegra.nvidia.com/linux-nv-oot.git
synced 2025-12-22 09:11:26 +03:00
ASoC: tegra-alt: T210 AHUB drivers
This driver is for T210 AHUB components. Bug 1442940 Change-Id: If6b33bc89cfc273f8024b7e4f6d644c170c2fe4f Signed-off-by: Songhee Baek <sbaek@nvidia.com> Signed-off-by: Arun Shamanna Lakshmi <aruns@nvidia.com> Reviewed-on: http://git-master/r/397314 (cherry picked from commit 3972fbeccbec85f74a0ed959212382db5471e65d) Reviewed-on: http://git-master/r/355184 Tested-by: Sumit Bhattacharya <sumitb@nvidia.com> Reviewed-by: Sumit Bhattacharya <sumitb@nvidia.com> Reviewed-by: Rahul Mittal <rmittal@nvidia.com>
This commit is contained in:
committed by
Sameer Pujar
parent
7a66be7f52
commit
f9bb9e84e5
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
604
sound/soc/tegra-alt/tegra210_admaif_alt.c
Normal file
604
sound/soc/tegra-alt/tegra210_admaif_alt.c
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <mach/clk.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/pcm_params.h>
|
||||
|
||||
#include "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 <sbaek@nvidia.com>");
|
||||
MODULE_DESCRIPTION("Tegra210 ADMAIF driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
||||
659
sound/soc/tegra-alt/tegra210_adx_alt.c
Normal file
659
sound/soc/tegra-alt/tegra210_adx_alt.c
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#include "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 <aruns@nvidia.com>");
|
||||
MODULE_DESCRIPTION("Tegra210 ADX ASoC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
||||
MODULE_DEVICE_TABLE(of, tegra210_adx_of_match);
|
||||
485
sound/soc/tegra-alt/tegra210_afc_alt.c
Normal file
485
sound/soc/tegra-alt/tegra210_afc_alt.c
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#include "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 <aruns@nvidia.com>");
|
||||
MODULE_DESCRIPTION("Tegra210 AFC ASoC driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
||||
MODULE_DEVICE_TABLE(of, tegra210_afc_of_match);
|
||||
696
sound/soc/tegra-alt/tegra210_amx_alt.c
Normal file
696
sound/soc/tegra-alt/tegra210_amx_alt.c
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#include "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 <sbaek@nvidia.com>");
|
||||
MODULE_DESCRIPTION("Tegra210 AMX ASoC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
||||
MODULE_DEVICE_TABLE(of, tegra210_amx_of_match);
|
||||
664
sound/soc/tegra-alt/tegra210_i2s_alt.c
Normal file
664
sound/soc/tegra-alt/tegra210_i2s_alt.c
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#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 <sbaek@nvidia.com>");
|
||||
MODULE_DESCRIPTION("Tegra210 I2S ASoC driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
||||
MODULE_DEVICE_TABLE(of, tegra210_i2s_of_match);
|
||||
706
sound/soc/tegra-alt/tegra210_mixer_alt.c
Normal file
706
sound/soc/tegra-alt/tegra210_mixer_alt.c
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#include "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 <aruns@nvidia.com>");
|
||||
MODULE_DESCRIPTION("Tegra210 MIXER ASoC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
||||
MODULE_DEVICE_TABLE(of, tegra210_mixer_of_match);
|
||||
549
sound/soc/tegra-alt/tegra210_mvc_alt.c
Normal file
549
sound/soc/tegra-alt/tegra210_mvc_alt.c
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#include "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 <aruns@nvidia.com>");
|
||||
MODULE_DESCRIPTION("Tegra210 MVC ASoC driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
||||
MODULE_DEVICE_TABLE(of, tegra210_mvc_of_match);
|
||||
584
sound/soc/tegra-alt/tegra210_sfc_alt.c
Normal file
584
sound/soc/tegra-alt/tegra210_sfc_alt.c
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#include "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 <aruns@nvidia.com>");
|
||||
MODULE_DESCRIPTION("Tegra210 SFC ASoC driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
||||
MODULE_DEVICE_TABLE(of, tegra210_sfc_of_match);
|
||||
501
sound/soc/tegra-alt/tegra210_spdif_alt.c
Normal file
501
sound/soc/tegra-alt/tegra210_spdif_alt.c
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#include "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 <aruns@nvidia.com>");
|
||||
MODULE_AUTHOR("Songhee Baek <sbaek@nvidia.com>");
|
||||
MODULE_DESCRIPTION("Tegra210 SPDIF ASoC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
||||
MODULE_DEVICE_TABLE(of, tegra210_spdif_of_match);
|
||||
854
sound/soc/tegra-alt/tegra210_xbar_alt.c
Normal file
854
sound/soc/tegra-alt/tegra210_xbar_alt.c
Normal file
@@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/soc.h>
|
||||
#include <mach/clk.h>
|
||||
|
||||
#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 <swarren@nvidia.com>");
|
||||
MODULE_DESCRIPTION("Tegra210 XBAR driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
||||
Reference in New Issue
Block a user