Files
linux-nv-oot/sound/soc/tegra-alt/tegra210_xbar_alt.c
Rahul Mittal 831e3f9c2f ASoC: tegra-alt: Add Tegra210 DMIC driver
Add Tegra210 DMIC driver
Add DAI links and DAPM map for dmic
Add unit fpga specific clock programming

Bug 1456155

Change-Id: I80475761716dd2965627596500e77aa3f66e0ac9
Signed-off-by: Rahul Mittal <rmittal@nvidia.com>
Reviewed-on: http://git-master/r/422710
Reviewed-by: Sachin Nikam <snikam@nvidia.com>
2022-09-29 15:30:21 +05:30

975 lines
27 KiB
C

/*
* 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 */
};
int tegra210_xbar_get_value_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
struct snd_soc_dapm_widget *widget = wlist->widgets[0];
struct snd_soc_codec *codec = widget->codec;
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int reg_count, reg_val, val, bit_pos = 0, i;
unsigned int reg[TEGRA210_XBAR_UPDATE_MAX_REG];
reg_count = xbar->soc_data->reg_count;
if (reg_count > TEGRA210_XBAR_UPDATE_MAX_REG)
return -EINVAL;
for (i = 0; i < reg_count; i++) {
reg[i] = (e->reg +
xbar->soc_data->reg_offset * i);
reg_val = snd_soc_read(codec, reg[i]);
val = reg_val & xbar->soc_data->mask[i];
if (val != 0) {
bit_pos = ffs(val) + (8 * codec->val_bytes * i);
break;
}
}
for (i = 0; i < e->max; i++) {
if (bit_pos == e->values[i]) {
ucontrol->value.enumerated.item[0] = i;
break;
}
}
return 0;
}
int tegra210_xbar_put_value_enum(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
struct snd_soc_dapm_widget *widget = wlist->widgets[0];
struct snd_soc_codec *codec = widget->codec;
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
unsigned int *item = ucontrol->value.enumerated.item;
unsigned int change = 0, reg_idx = 0, value, *mask, bit_pos = 0;
unsigned int i, wi, reg_count, reg_val = 0, update_idx = 0;
unsigned int reg[TEGRA210_XBAR_UPDATE_MAX_REG];
struct snd_soc_dapm_update update[TEGRA210_XBAR_UPDATE_MAX_REG];
/* initialize the reg_count and mask from soc_data */
reg_count = xbar->soc_data->reg_count;
mask = (unsigned int *)xbar->soc_data->mask;
if (item[0] >= e->max || reg_count > TEGRA210_XBAR_UPDATE_MAX_REG)
return -EINVAL;
value = e->values[item[0]];
if (value) {
/* get the register index and value to set */
reg_idx = (value - 1) / (8 * codec->val_bytes);
bit_pos = (value - 1) % (8 * codec->val_bytes);
reg_val = BIT(bit_pos);
}
for (i = 0; i < reg_count; i++) {
reg[i] = e->reg + xbar->soc_data->reg_offset * i;
if (i == reg_idx) {
change |= snd_soc_test_bits(codec, reg[i],
mask[i], reg_val);
/* set the selected register */
update[reg_count - 1].reg = reg[reg_idx];
update[reg_count - 1].mask = mask[reg_idx];
update[reg_count - 1].val = reg_val;
} else {
/* accumulate the change to update the DAPM path
when none is selected */
change |= snd_soc_test_bits(codec, reg[i],
mask[i], 0);
/* clear the register when not selected */
update[update_idx].reg = reg[i];
update[update_idx].mask = mask[i];
update[update_idx++].val = 0;
}
}
/* power the widgets */
if (change) {
for (wi = 0; wi < wlist->num_widgets; wi++) {
widget = wlist->widgets[wi];
widget->value = reg_val;
for (i = 0; i < reg_count; i++) {
update[i].kcontrol = kcontrol;
update[i].widget = widget;
widget->dapm->update = &update[i];
snd_soc_dapm_mux_update_power(widget,
kcontrol, item[0], e);
widget->dapm->update = NULL;
}
}
}
return change;
}
#define MUX_REG(id) (TEGRA210_XBAR_RX_STRIDE * (id))
#define SOC_VALUE_ENUM_WIDE(xreg, shift, xmax, xtexts, xvalues) \
{ .reg = xreg, .shift_l = shift, .shift_r = shift, \
.max = xmax, .texts = xtexts, .values = xvalues, \
.mask = xmax ? roundup_pow_of_two(xmax) - 1 : 0}
#define SOC_VALUE_ENUM_WIDE_DECL(name, xreg, shift, \
xtexts, xvalues) \
struct soc_enum name = SOC_VALUE_ENUM_WIDE(xreg, shift, \
ARRAY_SIZE(xtexts), xtexts, xvalues)
#define MUX_ENUM_CTRL_DECL(ename, id) \
SOC_VALUE_ENUM_WIDE_DECL(ename##_enum, MUX_REG(id), 0, \
tegra210_xbar_mux_texts, tegra210_xbar_mux_values); \
static const struct snd_kcontrol_new ename##_control = \
SOC_DAPM_ENUM_EXT("Route", ename##_enum,\
tegra210_xbar_get_value_enum,\
tegra210_xbar_put_value_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", "DMIC1", "DMIC1 RX" }, \
{ name " Mux", "DMIC2", "DMIC2 RX" }, \
{ name " Mux", "DMIC3", "DMIC3 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,
.mask[0] = 0xf1f03ff,
.mask[1] = 0x3f30031f,
.mask[2] = 0xff1cf313,
.reg_count = 3,
.reg_offset = TEGRA210_XBAR_PART1_RX,
};
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-dmic", 0x702d4000, "tegra210-dmic.0", NULL),
OF_DEV_AUXDATA("nvidia,tegra210-dmic", 0x702d4100, "tegra210-dmic.1", NULL),
OF_DEV_AUXDATA("nvidia,tegra210-dmic", 0x702d4200, "tegra210-dmic.2", 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, "ahub");
if (IS_ERR(xbar->clk)) {
dev_err(&pdev->dev, "Can't retrieve ahub 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 for 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);