Files
linux-nv-oot/sound/soc/tegra/tegra210_mixer.c
Sameer Pujar 66a89e5506 ASoC: tegra: Add 'oot' suffix in compatibles
The audio drivers are getting upstreamed continuously with incremental
feature support. However, the OOT version of driver will be used till
all features are available from upstream driver.

Add the "-oot" prefix in the driver's name so that OOT version of driver
can be selected from the device tree file.

Bug 3583581

Change-Id: Iacc55c05bf07d6cc4a0d9745903b0fa92e60d9b3
Signed-off-by: Sameer Pujar <spujar@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/c/linux-nv-oot/+/2866070
Reviewed-by: svcacv <svcacv@nvidia.com>
Reviewed-by: svc-mobile-coverity <svc-mobile-coverity@nvidia.com>
Reviewed-by: svc-mobile-cert <svc-mobile-cert@nvidia.com>
Reviewed-by: Mohan Kumar D <mkumard@nvidia.com>
Reviewed-by: Laxman Dewangan <ldewangan@nvidia.com>
Reviewed-by: Sheetal . <sheetal@nvidia.com>
Tested-by: Mohan Kumar D <mkumard@nvidia.com>
GVS: Gerrit_Virtual_Submit <buildbot_gerritrpt@nvidia.com>
2023-03-31 13:27:13 -07:00

715 lines
22 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
//
// tegra210_mixer.c - Tegra210 MIXER driver
//
// Copyright (c) 2014-2023 NVIDIA CORPORATION. All rights reserved.
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/version.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <drivers-private/sound/soc/tegra/tegra_cif.h>
#include "tegra210_ahub.h"
#include "tegra210_mixer.h"
#define MIXER_RX_REG(reg, id) (reg + (id * TEGRA210_MIXER_RX_STRIDE))
#define MIXER_TX_REG(reg, id) (reg + (id * TEGRA210_MIXER_TX_STRIDE))
#define MIXER_GAIN_CFG_RAM_ADDR(id) \
(TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_0 + \
id*TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_STRIDE)
#define MIXER_RX_REG_DEFAULTS(id) \
{ MIXER_RX_REG(TEGRA210_MIXER_RX1_CIF_CTRL, id), 0x00007700}, \
{ MIXER_RX_REG(TEGRA210_MIXER_RX1_CTRL, id), 0x00010823}, \
{ MIXER_RX_REG(TEGRA210_MIXER_RX1_PEAK_CTRL, id), 0x000012c0}
#define MIXER_TX_REG_DEFAULTS(id) \
{ MIXER_TX_REG(TEGRA210_MIXER_TX1_INT_MASK, id), 0x00000001}, \
{ MIXER_TX_REG(TEGRA210_MIXER_TX1_CIF_CTRL, id), 0x00007700}
static const struct reg_default tegra210_mixer_reg_defaults[] = {
MIXER_RX_REG_DEFAULTS(0),
MIXER_RX_REG_DEFAULTS(1),
MIXER_RX_REG_DEFAULTS(2),
MIXER_RX_REG_DEFAULTS(3),
MIXER_RX_REG_DEFAULTS(4),
MIXER_RX_REG_DEFAULTS(5),
MIXER_RX_REG_DEFAULTS(6),
MIXER_RX_REG_DEFAULTS(7),
MIXER_RX_REG_DEFAULTS(8),
MIXER_RX_REG_DEFAULTS(9),
MIXER_TX_REG_DEFAULTS(0),
MIXER_TX_REG_DEFAULTS(1),
MIXER_TX_REG_DEFAULTS(2),
MIXER_TX_REG_DEFAULTS(3),
MIXER_TX_REG_DEFAULTS(4),
{ TEGRA210_MIXER_CG, 0x00000001},
{ TEGRA210_MIXER_GAIN_CFG_RAM_CTRL, 0x00004000},
{ TEGRA210_MIXER_PEAKM_RAM_CTRL, 0x00004000},
};
static int tegra210_mixer_runtime_suspend(struct device *dev)
{
struct tegra210_mixer *mixer = dev_get_drvdata(dev);
regcache_cache_only(mixer->regmap, true);
regcache_mark_dirty(mixer->regmap);
return 0;
}
static int tegra210_mixer_runtime_resume(struct device *dev)
{
struct tegra210_mixer *mixer = dev_get_drvdata(dev);
regcache_cache_only(mixer->regmap, false);
regcache_sync(mixer->regmap);
return 0;
}
static int tegra210_mixer_write_ram(struct tegra210_mixer *mixer,
unsigned int addr,
unsigned int coef)
{
unsigned int reg, val;
int err;
/* check if busy */
err = regmap_read_poll_timeout(mixer->regmap,
TEGRA210_MIXER_GAIN_CFG_RAM_CTRL,
val, !(val & 0x80000000), 10, 10000);
if (err < 0)
return err;
reg = (addr << TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_SHIFT) &
TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_MASK;
reg |= TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_INIT_EN;
reg |= TEGRA210_MIXER_GAIN_CFG_RAM_RW_WRITE;
reg |= TEGRA210_MIXER_GAIN_CFG_RAM_SEQ_ACCESS_EN;
regmap_write(mixer->regmap,
TEGRA210_MIXER_GAIN_CFG_RAM_CTRL,
reg);
regmap_write(mixer->regmap,
TEGRA210_MIXER_GAIN_CFG_RAM_DATA,
coef);
return 0;
}
static int tegra210_mixer_put_format(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_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
struct tegra210_mixer *mixer = snd_soc_component_get_drvdata(cmpnt);
int value = ucontrol->value.integer.value[0];
if (strstr(kcontrol->id.name, "Audio Channels")) {
if (value >= 0 && value <= 8)
mixer->channels_via_control[mc->reg - 1] = value;
else
return -EINVAL;
}
return 0;
}
static int tegra210_mixer_get_format(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_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
struct tegra210_mixer *mixer = snd_soc_component_get_drvdata(cmpnt);
if (strstr(kcontrol->id.name, "Audio Channels"))
ucontrol->value.integer.value[0] =
mixer->channels_via_control[mc->reg - 1];
return 0;
}
static int tegra210_mixer_get_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_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
struct tegra210_mixer *mixer = snd_soc_component_get_drvdata(cmpnt);
unsigned int reg = mc->reg;
unsigned int i;
i = (reg - TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_0) /
TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_STRIDE;
ucontrol->value.integer.value[0] = mixer->gain_value[i];
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_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
struct tegra210_mixer *mixer = snd_soc_component_get_drvdata(cmpnt);
unsigned int reg = mc->reg, i;
int err;
pm_runtime_get_sync(cmpnt->dev);
/* write default gain config poly coefficients */
for (i = 0; i < 10; i++)
tegra210_mixer_write_ram(mixer, reg + i, mixer->gain_coeff[i]);
/* set duration parameter */
if (strstr(kcontrol->id.name, "Instant")) {
for (; i < 14; i++)
tegra210_mixer_write_ram(mixer, reg + i, 1);
} else {
for (; i < 14; i++)
tegra210_mixer_write_ram(mixer, reg + i,
mixer->gain_coeff[i]);
}
/* write new gain and trigger config */
err = tegra210_mixer_write_ram(mixer, reg + 0x09,
ucontrol->value.integer.value[0]);
err |= tegra210_mixer_write_ram(mixer, reg + 0x0f,
ucontrol->value.integer.value[0]);
pm_runtime_put(cmpnt->dev);
/* save gain */
i = (reg - TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_0) /
TEGRA210_MIXER_GAIN_CFG_RAM_ADDR_STRIDE;
mixer->gain_value[i] = ucontrol->value.integer.value[0];
return err;
}
static int tegra210_mixer_set_audio_cif(struct tegra210_mixer *mixer,
struct snd_pcm_hw_params *params,
unsigned int reg,
unsigned int id)
{
int channels, audio_bits;
struct tegra_cif_conf cif_conf;
memset(&cif_conf, 0, sizeof(struct tegra_cif_conf));
channels = params_channels(params);
if (mixer->channels_via_control[id])
channels = mixer->channels_via_control[id];
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
audio_bits = TEGRA_ACIF_BITS_16;
break;
case SNDRV_PCM_FORMAT_S24_LE:
case SNDRV_PCM_FORMAT_S32_LE:
audio_bits = TEGRA_ACIF_BITS_32;
break;
default:
return -EINVAL;
}
cif_conf.audio_ch = channels;
cif_conf.client_ch = channels;
cif_conf.audio_bits = audio_bits;
cif_conf.client_bits = audio_bits;
tegra_set_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 err, i;
err = tegra210_mixer_set_audio_cif(mixer, params,
TEGRA210_MIXER_RX1_CIF_CTRL +
(dai->id * TEGRA210_MIXER_RX_STRIDE),
dai->id);
/* write the gain config poly coefficients */
for (i = 0; i < 14; i++) {
tegra210_mixer_write_ram(mixer,
MIXER_GAIN_CFG_RAM_ADDR(dai->id) + i,
mixer->gain_coeff[i]);
}
/* write saved gain */
err = tegra210_mixer_write_ram(mixer,
MIXER_GAIN_CFG_RAM_ADDR(dai->id) + 0x09,
mixer->gain_value[dai->id]);
/* trigger the polynomial configuration */
tegra210_mixer_write_ram(mixer,
MIXER_GAIN_CFG_RAM_ADDR(dai->id) + 0xf,
0x01);
return err;
}
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 err;
err = tegra210_mixer_set_audio_cif(mixer, params,
TEGRA210_MIXER_TX1_CIF_CTRL +
((dai->id-10) * TEGRA210_MIXER_TX_STRIDE),
dai->id);
return err;
}
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 = 8, \
.rates = SNDRV_PCM_RATE_8000_192000, \
.formats = SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_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 = 8, \
.rates = SNDRV_PCM_RATE_8000_192000, \
.formats = SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_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_TX1_ADDER_CONFIG);
ADDER_CTRL_DECL(adder2, TEGRA210_MIXER_TX2_ADDER_CONFIG);
ADDER_CTRL_DECL(adder3, TEGRA210_MIXER_TX3_ADDER_CONFIG);
ADDER_CTRL_DECL(adder4, TEGRA210_MIXER_TX4_ADDER_CONFIG);
ADDER_CTRL_DECL(adder5, TEGRA210_MIXER_TX5_ADDER_CONFIG);
static const struct snd_kcontrol_new tegra210_mixer_gain_ctls[] = { \
SOC_SINGLE_EXT("RX1 Gain", MIXER_GAIN_CFG_RAM_ADDR(0), 0, 0x20000, 0,
tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX2 Gain", MIXER_GAIN_CFG_RAM_ADDR(1), 0, 0x20000, 0,
tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX3 Gain", MIXER_GAIN_CFG_RAM_ADDR(2), 0, 0x20000, 0,
tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX4 Gain", MIXER_GAIN_CFG_RAM_ADDR(3), 0, 0x20000, 0,
tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX5 Gain", MIXER_GAIN_CFG_RAM_ADDR(4), 0, 0x20000, 0,
tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX6 Gain", MIXER_GAIN_CFG_RAM_ADDR(5), 0, 0x20000, 0,
tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX7 Gain", MIXER_GAIN_CFG_RAM_ADDR(6), 0, 0x20000, 0,
tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX8 Gain", MIXER_GAIN_CFG_RAM_ADDR(7), 0, 0x20000, 0,
tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX9 Gain", MIXER_GAIN_CFG_RAM_ADDR(8), 0, 0x20000, 0,
tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX10 Gain", MIXER_GAIN_CFG_RAM_ADDR(9), 0, 0x20000, 0,
tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX1 Gain Instant", MIXER_GAIN_CFG_RAM_ADDR(0), 0,
0x20000, 0, tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX2 Gain Instant", MIXER_GAIN_CFG_RAM_ADDR(1), 0,
0x20000, 0, tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX3 Gain Instant", MIXER_GAIN_CFG_RAM_ADDR(2), 0,
0x20000, 0, tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX4 Gain Instant", MIXER_GAIN_CFG_RAM_ADDR(3), 0,
0x20000, 0, tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX5 Gain Instant", MIXER_GAIN_CFG_RAM_ADDR(4), 0,
0x20000, 0, tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX6 Gain Instant", MIXER_GAIN_CFG_RAM_ADDR(5), 0,
0x20000, 0, tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX7 Gain Instant", MIXER_GAIN_CFG_RAM_ADDR(6), 0,
0x20000, 0, tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX8 Gain Instant", MIXER_GAIN_CFG_RAM_ADDR(7), 0,
0x20000, 0, tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX9 Gain Instant", MIXER_GAIN_CFG_RAM_ADDR(8), 0,
0x20000, 0, tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX10 Gain Instant", MIXER_GAIN_CFG_RAM_ADDR(9), 0,
0x20000, 0, tegra210_mixer_get_gain, tegra210_mixer_put_gain),
SOC_SINGLE_EXT("RX1 Audio Channels", 1, 0, 8, 0,
tegra210_mixer_get_format, tegra210_mixer_put_format),
SOC_SINGLE_EXT("RX2 Audio Channels", 2, 0, 8, 0,
tegra210_mixer_get_format, tegra210_mixer_put_format),
SOC_SINGLE_EXT("RX3 Audio Channels", 3, 0, 8, 0,
tegra210_mixer_get_format, tegra210_mixer_put_format),
SOC_SINGLE_EXT("RX4 Audio Channels", 4, 0, 8, 0,
tegra210_mixer_get_format, tegra210_mixer_put_format),
SOC_SINGLE_EXT("RX5 Audio Channels", 5, 0, 8, 0,
tegra210_mixer_get_format, tegra210_mixer_put_format),
SOC_SINGLE_EXT("RX6 Audio Channels", 6, 0, 8, 0,
tegra210_mixer_get_format, tegra210_mixer_put_format),
SOC_SINGLE_EXT("RX7 Audio Channels", 7, 0, 8, 0,
tegra210_mixer_get_format, tegra210_mixer_put_format),
SOC_SINGLE_EXT("RX8 Audio Channels", 8, 0, 8, 0,
tegra210_mixer_get_format, tegra210_mixer_put_format),
SOC_SINGLE_EXT("RX9 Audio Channels", 9, 0, 8, 0,
tegra210_mixer_get_format, tegra210_mixer_put_format),
SOC_SINGLE_EXT("RX10 Audio Channels", 10, 0, 8, 0,
tegra210_mixer_get_format, tegra210_mixer_put_format),
SOC_SINGLE_EXT("TX1 Audio Channels", 11, 0, 8, 0,
tegra210_mixer_get_format, tegra210_mixer_put_format),
SOC_SINGLE_EXT("TX2 Audio Channels", 12, 0, 8, 0,
tegra210_mixer_get_format, tegra210_mixer_put_format),
SOC_SINGLE_EXT("TX3 Audio Channels", 13, 0, 8, 0,
tegra210_mixer_get_format, tegra210_mixer_put_format),
SOC_SINGLE_EXT("TX4 Audio Channels", 14, 0, 8, 0,
tegra210_mixer_get_format, tegra210_mixer_put_format),
SOC_SINGLE_EXT("TX5 Audio Channels", 15, 0, 8, 0,
tegra210_mixer_get_format, tegra210_mixer_put_format),
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_TX1_ENABLE, 0, 0),
SND_SOC_DAPM_AIF_OUT("TX2", NULL, 0,
TEGRA210_MIXER_TX2_ENABLE, 0, 0),
SND_SOC_DAPM_AIF_OUT("TX3", NULL, 0,
TEGRA210_MIXER_TX3_ENABLE, 0, 0),
SND_SOC_DAPM_AIF_OUT("TX4", NULL, 0,
TEGRA210_MIXER_TX4_ENABLE, 0, 0),
SND_SOC_DAPM_AIF_OUT("TX5", NULL, 0,
TEGRA210_MIXER_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_component_driver tegra210_mixer_cmpnt = {
.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),
#if (KERNEL_VERSION(6, 0, 0) > LINUX_VERSION_CODE)
.non_legacy_dai_naming = 1,
#endif
};
static bool tegra210_mixer_wr_reg(struct device *dev,
unsigned int reg)
{
if (reg < TEGRA210_MIXER_RX_LIMIT)
reg %= TEGRA210_MIXER_RX_STRIDE;
else if (reg < TEGRA210_MIXER_TX_LIMIT)
reg = (reg % TEGRA210_MIXER_TX_STRIDE) +
TEGRA210_MIXER_TX1_ENABLE;
switch (reg) {
case TEGRA210_MIXER_RX1_SOFT_RESET:
case TEGRA210_MIXER_RX1_CIF_CTRL ... TEGRA210_MIXER_RX1_PEAK_CTRL:
case TEGRA210_MIXER_TX1_ENABLE:
case TEGRA210_MIXER_TX1_SOFT_RESET:
case TEGRA210_MIXER_TX1_INT_MASK ... TEGRA210_MIXER_TX1_ADDER_CONFIG:
case TEGRA210_MIXER_ENABLE ... TEGRA210_MIXER_CG:
case TEGRA210_MIXER_GAIN_CFG_RAM_CTRL ... TEGRA210_MIXER_CTRL:
return true;
default:
return false;
}
}
static bool tegra210_mixer_rd_reg(struct device *dev,
unsigned int reg)
{
if (reg < TEGRA210_MIXER_RX_LIMIT)
reg %= TEGRA210_MIXER_RX_STRIDE;
else if (reg < TEGRA210_MIXER_TX_LIMIT)
reg = (reg % TEGRA210_MIXER_TX_STRIDE) +
TEGRA210_MIXER_TX1_ENABLE;
switch (reg) {
case TEGRA210_MIXER_RX1_SOFT_RESET ... TEGRA210_MIXER_RX1_SAMPLE_COUNT:
case TEGRA210_MIXER_TX1_ENABLE ... TEGRA210_MIXER_TX1_ADDER_CONFIG:
case TEGRA210_MIXER_ENABLE ... TEGRA210_MIXER_CTRL:
return true;
default:
return false;
}
}
static bool tegra210_mixer_volatile_reg(struct device *dev,
unsigned int reg)
{
if (reg < TEGRA210_MIXER_RX_LIMIT)
reg %= TEGRA210_MIXER_RX_STRIDE;
else if (reg < TEGRA210_MIXER_TX_LIMIT)
reg = (reg % TEGRA210_MIXER_TX_STRIDE) +
TEGRA210_MIXER_TX1_ENABLE;
switch (reg) {
case TEGRA210_MIXER_RX1_SOFT_RESET:
case TEGRA210_MIXER_RX1_STATUS:
case TEGRA210_MIXER_TX1_SOFT_RESET:
case TEGRA210_MIXER_TX1_STATUS:
case TEGRA210_MIXER_TX1_INT_STATUS:
case TEGRA210_MIXER_TX1_INT_SET:
case TEGRA210_MIXER_SOFT_RESET:
case TEGRA210_MIXER_STATUS:
case TEGRA210_MIXER_INT_STATUS:
case TEGRA210_MIXER_GAIN_CFG_RAM_CTRL:
case TEGRA210_MIXER_GAIN_CFG_RAM_DATA:
case TEGRA210_MIXER_PEAKM_RAM_CTRL:
case TEGRA210_MIXER_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_GAIN_CFG_RAM_DATA:
case TEGRA210_MIXER_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,
.reg_defaults = tegra210_mixer_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tegra210_mixer_reg_defaults),
.cache_type = REGCACHE_FLAT,
};
static const struct of_device_id tegra210_mixer_of_match[] = {
{ .compatible = "nvidia,tegra210-amixer-oot" },
{},
};
MODULE_DEVICE_TABLE(of, tegra210_mixer_of_match);
static int tegra210_mixer_platform_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct tegra210_mixer *mixer;
void __iomem *regs;
int err, i;
mixer = devm_kzalloc(dev, sizeof(*mixer), GFP_KERNEL);
if (!mixer)
return -ENOMEM;
mixer->gain_coeff[0] = 0;
mixer->gain_coeff[1] = 0;
mixer->gain_coeff[2] = 0;
mixer->gain_coeff[3] = 0;
mixer->gain_coeff[4] = 0;
mixer->gain_coeff[5] = 0;
mixer->gain_coeff[6] = 0;
mixer->gain_coeff[7] = 0x1000000;
mixer->gain_coeff[8] = 0;
mixer->gain_coeff[9] = 0x10000;
mixer->gain_coeff[10] = 0;
mixer->gain_coeff[11] = 0;
mixer->gain_coeff[12] = 0x400;
mixer->gain_coeff[13] = 0x8000000;
dev_set_drvdata(dev, mixer);
for (i = 0; i < TEGRA210_MIXER_RX_MAX; i++)
mixer->gain_value[i] = 0x10000;
regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(regs))
return PTR_ERR(regs);
mixer->regmap = devm_regmap_init_mmio(dev, regs,
&tegra210_mixer_regmap_config);
if (IS_ERR(mixer->regmap)) {
dev_err(dev, "regmap init failed\n");
return PTR_ERR(mixer->regmap);
}
regcache_cache_only(mixer->regmap, true);
err = devm_snd_soc_register_component(dev, &tegra210_mixer_cmpnt,
tegra210_mixer_dais,
ARRAY_SIZE(tegra210_mixer_dais));
if (err) {
dev_err(dev, "can't register MIXER component, err: %d\n", err);
return err;
}
pm_runtime_enable(dev);
return 0;
}
static int tegra210_mixer_platform_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
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)
SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
};
static struct platform_driver tegra210_mixer_driver = {
.driver = {
.name = "tegra210_mixer",
.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");