ASoC: tegra-alt: remove channel hard coding for I2S and ADMAIF

Currently CIF channels are overridden based on mono/stereo settings.
This can be problematic for multi-channel use cases or the cases where
user is intentionally overriding channels through mixer control. In such
cases hard coding based on CIF mono/stereo settings can result in wrong
behavior.

Following are the changes in current patch,
 * Decouple CIF mono/stereo and CIF channel settings. It means that, user
   can override audio channels and CIF settings independently. Also these
   controls are provided for both playback and capture paths.
 * For ADMAIF, channel overrides are provided for both audio and client
   channels in both playback and capture paths.
 * For I2S, separate channel overrides for client channels is not needed
   in playback and capture paths. Hence a common client channel override
   control is provided. However for audio channels it is similar to the
   controls provided for ADMAIF.
 * Similar to channels, for I2S, audio bit format control is updated and
   provided separate controls in playback and capture paths.
 * "None" option is removed for mono/stereo to match HW register spec.
   Earlier channel was hard coded based on this.
 * For I2S, audio channels are used for CIF configuration and for the slot
   control client channels are used (earlier audio channels were getting
   used for slot control)
 * For upstream, we are standardizing mixer control names. For now, only
   relevant controls are updated. Going forward we need to take similar
   approach for all controls/modules. This needs to be reflected in
   test scripts and documentation as well.

Bug 200503387

Change-Id: I04df9a3bda41e028edc46e86ecd1d80966bc83e1
Signed-off-by: Sameer Pujar <spujar@nvidia.com>
Reviewed-on: https://git-master.nvidia.com/r/2244477
Reviewed-by: mobile promotions <svcmobile_promotions@nvidia.com>
Tested-by: mobile promotions <svcmobile_promotions@nvidia.com>
This commit is contained in:
Sameer Pujar
2019-11-21 12:17:37 +05:30
parent f48397b648
commit c3c5ac4e23
4 changed files with 236 additions and 235 deletions

View File

@@ -158,6 +158,11 @@ enum {
DATA_32BIT
};
enum {
ADMAIF_RX_PATH,
ADMAIF_TX_PATH,
ADMAIF_PATHS,
};
struct tegra_admaif_soc_data {
unsigned int num_ch;
@@ -177,9 +182,10 @@ struct tegra_admaif {
struct tegra_alt_pcm_dma_params *capture_dma_data;
struct tegra_alt_pcm_dma_params *playback_dma_data;
const struct tegra_admaif_soc_data *soc_data;
int *override_channels;
int *tx_mono_to_stereo;
int *rx_stereo_to_mono;
unsigned int *audio_ch_override[ADMAIF_PATHS];
unsigned int *client_ch_override[ADMAIF_PATHS];
unsigned int *mono_to_stereo[ADMAIF_PATHS];
unsigned int *stereo_to_mono[ADMAIF_PATHS];
int reg_dump_flag;
void __iomem *base_addr;
};

View File

@@ -206,10 +206,11 @@ struct tegra210_i2s {
struct notifier_block slgc_notifier;
int num_supplies;
int bclk_ratio;
int format_in;
int audio_fmt_override[I2S_PATHS];
int codec_bit_format;
int sample_rate_via_control;
int channels_via_control;
int audio_ch_override[I2S_PATHS];
int client_ch_override; /* common for both TX and RX */
int stereo_to_mono[I2S_PATHS];
int mono_to_stereo[I2S_PATHS];
unsigned int fsync_width;

View File

@@ -318,7 +318,7 @@ static int tegra_admaif_hw_params(struct snd_pcm_substream *substream,
struct device *dev = dai->dev;
struct tegra_admaif *admaif = snd_soc_dai_get_drvdata(dai);
struct tegra210_xbar_cif_conf cif_conf;
unsigned int reg;
unsigned int reg, path;
int valid_bit, channels;
memset(&cif_conf, 0, sizeof(struct tegra210_xbar_cif_conf));
@@ -354,40 +354,26 @@ static int tegra_admaif_hw_params(struct snd_pcm_substream *substream,
cif_conf.audio_channels = channels;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
path = ADMAIF_TX_PATH;
reg = CH_TX_REG(TEGRA_ADMAIF_CHAN_ACIF_TX_CTRL, dai->id);
/* For playback path, if client channel is 1 and mono to
* stereo control was non-zero, then audio channels will be
* set to 2 for mono to stereo conversion.
*/
if ((admaif->tx_mono_to_stereo[dai->id] > 0) &&
(channels == 1)) {
cif_conf.mono_conv =
admaif->tx_mono_to_stereo[dai->id] - 1;
cif_conf.audio_channels = 2;
}
} else {
path = ADMAIF_RX_PATH;
reg = CH_RX_REG(TEGRA_ADMAIF_CHAN_ACIF_RX_CTRL, dai->id);
/* The override channels are needed only for capture path as
* the audio and client channels can be different on RX path.
*/
if (admaif->override_channels[dai->id] > 0) {
cif_conf.audio_channels =
admaif->override_channels[dai->id];
}
/* For capture path, if client channel is 1 and audio channel
* is 2, only then stereo to mono settings will take effect.
*/
if ((admaif->rx_stereo_to_mono[dai->id] > 0) &&
(cif_conf.audio_channels == 2) && (channels == 1)) {
cif_conf.stereo_conv =
admaif->rx_stereo_to_mono[dai->id] - 1;
}
}
if (admaif->audio_ch_override[path][dai->id])
cif_conf.audio_channels =
admaif->audio_ch_override[path][dai->id];
if (admaif->client_ch_override[path][dai->id])
cif_conf.client_channels =
admaif->client_ch_override[path][dai->id];
cif_conf.mono_conv = admaif->mono_to_stereo[path][dai->id];
cif_conf.stereo_conv = admaif->stereo_to_mono[path][dai->id];
tegra_admaif_set_pack_mode(admaif->regmap, reg, valid_bit);
tegra210_xbar_set_cif(admaif->regmap, reg, &cif_conf);
return 0;
@@ -504,29 +490,26 @@ static int tegra_admaif_get_format(struct snd_kcontrol *kcontrol,
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
struct soc_enum *ec = (struct soc_enum *)kcontrol->private_value;
struct tegra_admaif *admaif = snd_soc_codec_get_drvdata(codec);
int i;
char buf[50];
long *uctl_val = &ucontrol->value.integer.value[0];
if (strstr(kcontrol->id.name, "Channels"))
ucontrol->value.integer.value[0] =
admaif->override_channels[mc->reg];
else {
for (i = 0 ; i < admaif->soc_data->num_ch; i++) {
snprintf(buf, 50, "ADMAIF%d Capture stereo to mono", i+1);
if (strstr(kcontrol->id.name, buf)) {
ucontrol->value.integer.value[0] =
admaif->rx_stereo_to_mono[i];
break;
}
snprintf(buf, 50, "ADMAIF%d Playback mono to stereo", i+1);
if (strstr(kcontrol->id.name, buf)) {
ucontrol->value.integer.value[0] =
admaif->tx_mono_to_stereo[i];
break;
}
}
}
if (strstr(kcontrol->id.name, "Playback Audio Channels"))
*uctl_val = admaif->audio_ch_override[ADMAIF_TX_PATH][mc->reg];
else if (strstr(kcontrol->id.name, "Capture Audio Channels"))
*uctl_val = admaif->audio_ch_override[ADMAIF_RX_PATH][mc->reg];
else if (strstr(kcontrol->id.name, "Playback Client Channels"))
*uctl_val = admaif->client_ch_override[ADMAIF_TX_PATH][mc->reg];
else if (strstr(kcontrol->id.name, "Capture Client Channels"))
*uctl_val = admaif->client_ch_override[ADMAIF_RX_PATH][mc->reg];
else if (strstr(kcontrol->id.name, "Playback mono to stereo"))
*uctl_val = admaif->mono_to_stereo[ADMAIF_TX_PATH][ec->reg];
else if (strstr(kcontrol->id.name, "Playback stereo to mono"))
*uctl_val = admaif->stereo_to_mono[ADMAIF_TX_PATH][ec->reg];
else if (strstr(kcontrol->id.name, "Capture mono to stereo"))
*uctl_val = admaif->mono_to_stereo[ADMAIF_RX_PATH][ec->reg];
else if (strstr(kcontrol->id.name, "Capture stereo to mono"))
*uctl_val = admaif->stereo_to_mono[ADMAIF_RX_PATH][ec->reg];
return 0;
}
@@ -536,31 +519,27 @@ static int tegra_admaif_put_format(struct snd_kcontrol *kcontrol,
{
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
struct soc_enum *ec = (struct soc_enum *)kcontrol->private_value;
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct tegra_admaif *admaif = snd_soc_codec_get_drvdata(codec);
int value = ucontrol->value.integer.value[0];
int i;
char buf[50];
if (strstr(kcontrol->id.name, "Channels")) {
if (value >= 0 && value <= 16)
admaif->override_channels[mc->reg] = value;
else
return -EINVAL;
} else {
for (i = 0 ; i < admaif->soc_data->num_ch; i++) {
snprintf(buf, 50, "ADMAIF%d Capture stereo to mono", i+1);
if (strstr(kcontrol->id.name, buf)) {
admaif->rx_stereo_to_mono[i] = value;
break;
}
snprintf(buf, 50, "ADMAIF%d Playback mono to stereo", i+1);
if (strstr(kcontrol->id.name, buf)) {
admaif->tx_mono_to_stereo[i] = value;
break;
}
}
}
if (strstr(kcontrol->id.name, "Playback Audio Channels"))
admaif->audio_ch_override[ADMAIF_TX_PATH][mc->reg] = value;
else if (strstr(kcontrol->id.name, "Capture Audio Channels"))
admaif->audio_ch_override[ADMAIF_RX_PATH][mc->reg] = value;
else if (strstr(kcontrol->id.name, "Playback Client Channels"))
admaif->client_ch_override[ADMAIF_TX_PATH][mc->reg] = value;
else if (strstr(kcontrol->id.name, "Capture Client Channels"))
admaif->client_ch_override[ADMAIF_RX_PATH][mc->reg] = value;
else if (strstr(kcontrol->id.name, "Playback mono to stereo"))
admaif->mono_to_stereo[ADMAIF_TX_PATH][ec->reg] = value;
else if (strstr(kcontrol->id.name, "Playback stereo to mono"))
admaif->stereo_to_mono[ADMAIF_TX_PATH][ec->reg] = value;
else if (strstr(kcontrol->id.name, "Capture mono to stereo"))
admaif->mono_to_stereo[ADMAIF_RX_PATH][ec->reg] = value;
else if (strstr(kcontrol->id.name, "Capture stereo to mono"))
admaif->stereo_to_mono[ADMAIF_RX_PATH][ec->reg] = value;
return 0;
}
@@ -886,11 +865,11 @@ static const struct snd_soc_dapm_route tegra_admaif_routes[] = {
};
static const char * const tegra_admaif_stereo_conv_text[] = {
"None", "CH0", "CH1", "AVG",
"CH0", "CH1", "AVG",
};
static const char * const tegra_admaif_mono_conv_text[] = {
"None", "ZERO", "COPY",
"ZERO", "COPY",
};
static const struct soc_enum tegra_admaif_mono_conv_enum =
@@ -903,19 +882,48 @@ static const struct soc_enum tegra_admaif_stereo_conv_enum =
ARRAY_SIZE(tegra_admaif_stereo_conv_text),
tegra_admaif_stereo_conv_text);
#define TEGRA_ADMAIF_CHANNEL_CTRL(reg) \
SOC_SINGLE_EXT("ADMAIF" #reg " Channels", reg - 1, 0, 16, 0, \
tegra_admaif_get_format, tegra_admaif_put_format)
#define TEGRA_ADMAIF_CHANNEL_CTRL(reg) \
SOC_SINGLE_EXT("ADMAIF" #reg " Playback Audio Channels", reg - 1, \
0, 16, 0, tegra_admaif_get_format, \
tegra_admaif_put_format), \
SOC_SINGLE_EXT("ADMAIF" #reg " Capture Audio Channels", reg - 1, \
0, 16, 0, tegra_admaif_get_format, \
tegra_admaif_put_format), \
SOC_SINGLE_EXT("ADMAIF" #reg " Playback Client Channels", reg - 1, \
0, 16, 0, tegra_admaif_get_format, \
tegra_admaif_put_format), \
SOC_SINGLE_EXT("ADMAIF" #reg " Capture Client Channels", reg - 1, \
0, 16, 0, tegra_admaif_get_format, \
tegra_admaif_put_format)
#define TEGRA_ADMAIF_TX_CIF_CTRL(reg) \
SOC_ENUM_EXT("ADMAIF" #reg " Playback mono to stereo conv", \
tegra_admaif_mono_conv_enum, tegra_admaif_get_format, \
tegra_admaif_put_format)
/*
* below macro is added to avoid looping over all ADMAIFx controls related
* to mono/stereo conversions in get()/put() callbacks.
*/
#define NV_SOC_ENUM_EXT(xname, xreg, xhandler_get, xhandler_put, xenum_text) \
{ \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
.info = snd_soc_info_enum_double, \
.name = xname, \
.get = xhandler_get, \
.put = xhandler_put, \
.private_value = (unsigned long)&(struct soc_enum) \
SOC_ENUM_SINGLE(xreg, 0, ARRAY_SIZE(xenum_text), xenum_text) \
}
#define TEGRA_ADMAIF_RX_CIF_CTRL(reg) \
SOC_ENUM_EXT("ADMAIF" #reg " Capture stereo to mono conv", \
tegra_admaif_stereo_conv_enum, tegra_admaif_get_format, \
tegra_admaif_put_format)
#define TEGRA_ADMAIF_CIF_CTRL(reg) \
NV_SOC_ENUM_EXT("ADMAIF" #reg " Playback mono to stereo conv", reg - 1,\
tegra_admaif_get_format, tegra_admaif_put_format, \
tegra_admaif_mono_conv_text), \
NV_SOC_ENUM_EXT("ADMAIF" #reg " Playback stereo to mono conv", reg - 1,\
tegra_admaif_get_format, tegra_admaif_put_format, \
tegra_admaif_stereo_conv_text), \
NV_SOC_ENUM_EXT("ADMAIF" #reg " Capture mono to stereo conv", reg - 1, \
tegra_admaif_get_format, tegra_admaif_put_format, \
tegra_admaif_mono_conv_text), \
NV_SOC_ENUM_EXT("ADMAIF" #reg " Capture stereo to mono conv", reg - 1, \
tegra_admaif_get_format, tegra_admaif_put_format, \
tegra_admaif_stereo_conv_text)
static struct snd_kcontrol_new tegra210_admaif_controls[] = {
TEGRA_ADMAIF_CHANNEL_CTRL(1),
@@ -928,26 +936,16 @@ static struct snd_kcontrol_new tegra210_admaif_controls[] = {
TEGRA_ADMAIF_CHANNEL_CTRL(8),
TEGRA_ADMAIF_CHANNEL_CTRL(9),
TEGRA_ADMAIF_CHANNEL_CTRL(10),
TEGRA_ADMAIF_RX_CIF_CTRL(1),
TEGRA_ADMAIF_RX_CIF_CTRL(2),
TEGRA_ADMAIF_RX_CIF_CTRL(3),
TEGRA_ADMAIF_RX_CIF_CTRL(4),
TEGRA_ADMAIF_RX_CIF_CTRL(5),
TEGRA_ADMAIF_RX_CIF_CTRL(6),
TEGRA_ADMAIF_RX_CIF_CTRL(7),
TEGRA_ADMAIF_RX_CIF_CTRL(8),
TEGRA_ADMAIF_RX_CIF_CTRL(9),
TEGRA_ADMAIF_RX_CIF_CTRL(10),
TEGRA_ADMAIF_TX_CIF_CTRL(1),
TEGRA_ADMAIF_TX_CIF_CTRL(2),
TEGRA_ADMAIF_TX_CIF_CTRL(3),
TEGRA_ADMAIF_TX_CIF_CTRL(4),
TEGRA_ADMAIF_TX_CIF_CTRL(5),
TEGRA_ADMAIF_TX_CIF_CTRL(6),
TEGRA_ADMAIF_TX_CIF_CTRL(7),
TEGRA_ADMAIF_TX_CIF_CTRL(8),
TEGRA_ADMAIF_TX_CIF_CTRL(9),
TEGRA_ADMAIF_TX_CIF_CTRL(10),
TEGRA_ADMAIF_CIF_CTRL(1),
TEGRA_ADMAIF_CIF_CTRL(2),
TEGRA_ADMAIF_CIF_CTRL(3),
TEGRA_ADMAIF_CIF_CTRL(4),
TEGRA_ADMAIF_CIF_CTRL(5),
TEGRA_ADMAIF_CIF_CTRL(6),
TEGRA_ADMAIF_CIF_CTRL(7),
TEGRA_ADMAIF_CIF_CTRL(8),
TEGRA_ADMAIF_CIF_CTRL(9),
TEGRA_ADMAIF_CIF_CTRL(10),
SOC_SINGLE_EXT("APE Reg Dump", SND_SOC_NOPM, 0, 1, 0,
tegra210_ape_dump_reg_get, tegra210_ape_dump_reg_put),
};
@@ -973,46 +971,26 @@ static struct snd_kcontrol_new tegra186_admaif_controls[] = {
TEGRA_ADMAIF_CHANNEL_CTRL(18),
TEGRA_ADMAIF_CHANNEL_CTRL(19),
TEGRA_ADMAIF_CHANNEL_CTRL(20),
TEGRA_ADMAIF_RX_CIF_CTRL(1),
TEGRA_ADMAIF_RX_CIF_CTRL(2),
TEGRA_ADMAIF_RX_CIF_CTRL(3),
TEGRA_ADMAIF_RX_CIF_CTRL(4),
TEGRA_ADMAIF_RX_CIF_CTRL(5),
TEGRA_ADMAIF_RX_CIF_CTRL(6),
TEGRA_ADMAIF_RX_CIF_CTRL(7),
TEGRA_ADMAIF_RX_CIF_CTRL(8),
TEGRA_ADMAIF_RX_CIF_CTRL(9),
TEGRA_ADMAIF_RX_CIF_CTRL(10),
TEGRA_ADMAIF_RX_CIF_CTRL(11),
TEGRA_ADMAIF_RX_CIF_CTRL(12),
TEGRA_ADMAIF_RX_CIF_CTRL(13),
TEGRA_ADMAIF_RX_CIF_CTRL(14),
TEGRA_ADMAIF_RX_CIF_CTRL(15),
TEGRA_ADMAIF_RX_CIF_CTRL(16),
TEGRA_ADMAIF_RX_CIF_CTRL(17),
TEGRA_ADMAIF_RX_CIF_CTRL(18),
TEGRA_ADMAIF_RX_CIF_CTRL(19),
TEGRA_ADMAIF_RX_CIF_CTRL(20),
TEGRA_ADMAIF_TX_CIF_CTRL(1),
TEGRA_ADMAIF_TX_CIF_CTRL(2),
TEGRA_ADMAIF_TX_CIF_CTRL(3),
TEGRA_ADMAIF_TX_CIF_CTRL(4),
TEGRA_ADMAIF_TX_CIF_CTRL(5),
TEGRA_ADMAIF_TX_CIF_CTRL(6),
TEGRA_ADMAIF_TX_CIF_CTRL(7),
TEGRA_ADMAIF_TX_CIF_CTRL(8),
TEGRA_ADMAIF_TX_CIF_CTRL(9),
TEGRA_ADMAIF_TX_CIF_CTRL(10),
TEGRA_ADMAIF_TX_CIF_CTRL(11),
TEGRA_ADMAIF_TX_CIF_CTRL(12),
TEGRA_ADMAIF_TX_CIF_CTRL(13),
TEGRA_ADMAIF_TX_CIF_CTRL(14),
TEGRA_ADMAIF_TX_CIF_CTRL(15),
TEGRA_ADMAIF_TX_CIF_CTRL(16),
TEGRA_ADMAIF_TX_CIF_CTRL(17),
TEGRA_ADMAIF_TX_CIF_CTRL(18),
TEGRA_ADMAIF_TX_CIF_CTRL(19),
TEGRA_ADMAIF_TX_CIF_CTRL(20),
TEGRA_ADMAIF_CIF_CTRL(1),
TEGRA_ADMAIF_CIF_CTRL(2),
TEGRA_ADMAIF_CIF_CTRL(3),
TEGRA_ADMAIF_CIF_CTRL(4),
TEGRA_ADMAIF_CIF_CTRL(5),
TEGRA_ADMAIF_CIF_CTRL(6),
TEGRA_ADMAIF_CIF_CTRL(7),
TEGRA_ADMAIF_CIF_CTRL(8),
TEGRA_ADMAIF_CIF_CTRL(9),
TEGRA_ADMAIF_CIF_CTRL(10),
TEGRA_ADMAIF_CIF_CTRL(11),
TEGRA_ADMAIF_CIF_CTRL(12),
TEGRA_ADMAIF_CIF_CTRL(13),
TEGRA_ADMAIF_CIF_CTRL(14),
TEGRA_ADMAIF_CIF_CTRL(15),
TEGRA_ADMAIF_CIF_CTRL(16),
TEGRA_ADMAIF_CIF_CTRL(17),
TEGRA_ADMAIF_CIF_CTRL(18),
TEGRA_ADMAIF_CIF_CTRL(19),
TEGRA_ADMAIF_CIF_CTRL(20),
SOC_SINGLE_EXT("APE Reg Dump", SND_SOC_NOPM, 0, 1, 0,
tegra210_ape_dump_reg_get, tegra210_ape_dump_reg_put),
};
@@ -1107,23 +1085,31 @@ static int tegra_admaif_probe(struct platform_device *pdev)
if (!admaif->playback_dma_data)
return -ENOMEM;
admaif->override_channels = devm_kzalloc(&pdev->dev,
sizeof(int) * admaif->soc_data->num_ch,
GFP_KERNEL);
if (!admaif->override_channels)
return -ENOMEM;
for (i = 0; i < ADMAIF_PATHS; i++) {
admaif->audio_ch_override[i] =
devm_kcalloc(&pdev->dev, admaif->soc_data->num_ch,
sizeof(unsigned int), GFP_KERNEL);
if (!admaif->audio_ch_override[i])
return -ENOMEM;
admaif->tx_mono_to_stereo = devm_kzalloc(&pdev->dev,
sizeof(int) * admaif->soc_data->num_ch,
GFP_KERNEL);
if (!admaif->tx_mono_to_stereo)
return -ENOMEM;
admaif->client_ch_override[i] =
devm_kcalloc(&pdev->dev, admaif->soc_data->num_ch,
sizeof(unsigned int), GFP_KERNEL);
if (!admaif->client_ch_override[i])
return -ENOMEM;
admaif->rx_stereo_to_mono = devm_kzalloc(&pdev->dev,
sizeof(int) * admaif->soc_data->num_ch,
GFP_KERNEL);
if (!admaif->rx_stereo_to_mono)
return -ENOMEM;
admaif->mono_to_stereo[i] =
devm_kcalloc(&pdev->dev, admaif->soc_data->num_ch,
sizeof(unsigned int), GFP_KERNEL);
if (!admaif->mono_to_stereo[i])
return -ENOMEM;
admaif->stereo_to_mono[i] =
devm_kcalloc(&pdev->dev, admaif->soc_data->num_ch,
sizeof(unsigned int), GFP_KERNEL);
if (!admaif->stereo_to_mono[i])
return -ENOMEM;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
regs = devm_ioremap_resource(&pdev->dev, res);

View File

@@ -370,32 +370,35 @@ static int tegra210_i2s_get_format(struct snd_kcontrol *kcontrol,
{
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct tegra210_i2s *i2s = snd_soc_codec_get_drvdata(codec);
long *uctl_val = &ucontrol->value.integer.value[0];
/* get the format control flag */
if (strstr(kcontrol->id.name, "input"))
ucontrol->value.integer.value[0] = i2s->format_in;
if (strstr(kcontrol->id.name, "Playback Audio Bit Format"))
*uctl_val = i2s->audio_fmt_override[I2S_RX_PATH];
else if (strstr(kcontrol->id.name, "Capture Audio Bit Format"))
*uctl_val = i2s->audio_fmt_override[I2S_TX_PATH];
else if (strstr(kcontrol->id.name, "codec"))
ucontrol->value.integer.value[0] = i2s->codec_bit_format;
*uctl_val = i2s->codec_bit_format;
else if (strstr(kcontrol->id.name, "Sample Rate"))
ucontrol->value.integer.value[0] = i2s->sample_rate_via_control;
else if (strstr(kcontrol->id.name, "Channels"))
ucontrol->value.integer.value[0] = i2s->channels_via_control;
*uctl_val = i2s->sample_rate_via_control;
else if (strstr(kcontrol->id.name, "Playback Audio Channels"))
*uctl_val = i2s->audio_ch_override[I2S_RX_PATH];
else if (strstr(kcontrol->id.name, "Capture Audio Channels"))
*uctl_val = i2s->audio_ch_override[I2S_TX_PATH];
else if (strstr(kcontrol->id.name, "Client Channels"))
*uctl_val = i2s->client_ch_override;
else if (strstr(kcontrol->id.name, "Capture stereo to mono"))
ucontrol->value.integer.value[0] =
i2s->stereo_to_mono[I2S_TX_PATH];
*uctl_val = i2s->stereo_to_mono[I2S_TX_PATH];
else if (strstr(kcontrol->id.name, "Capture mono to stereo"))
ucontrol->value.integer.value[0] =
i2s->mono_to_stereo[I2S_TX_PATH];
*uctl_val = i2s->mono_to_stereo[I2S_TX_PATH];
else if (strstr(kcontrol->id.name, "Playback stereo to mono"))
ucontrol->value.integer.value[0] =
i2s->stereo_to_mono[I2S_RX_PATH];
*uctl_val = i2s->stereo_to_mono[I2S_RX_PATH];
else if (strstr(kcontrol->id.name, "Playback mono to stereo"))
ucontrol->value.integer.value[0] =
i2s->mono_to_stereo[I2S_RX_PATH];
*uctl_val = i2s->mono_to_stereo[I2S_RX_PATH];
else if (strstr(kcontrol->id.name, "Playback FIFO threshold"))
ucontrol->value.integer.value[0] = i2s->rx_fifo_th;
*uctl_val = i2s->rx_fifo_th;
else if (strstr(kcontrol->id.name, "BCLK Ratio"))
ucontrol->value.integer.value[0] = i2s->bclk_ratio;
*uctl_val = i2s->bclk_ratio;
return 0;
}
@@ -408,14 +411,20 @@ static int tegra210_i2s_put_format(struct snd_kcontrol *kcontrol,
int value = ucontrol->value.integer.value[0];
/* set the format control flag */
if (strstr(kcontrol->id.name, "input"))
i2s->format_in = value;
if (strstr(kcontrol->id.name, "Playback Audio Bit Format"))
i2s->audio_fmt_override[I2S_RX_PATH] = value;
else if (strstr(kcontrol->id.name, "Capture Audio Bit Format"))
i2s->audio_fmt_override[I2S_TX_PATH] = value;
else if (strstr(kcontrol->id.name, "codec"))
i2s->codec_bit_format = value;
else if (strstr(kcontrol->id.name, "Sample Rate"))
i2s->sample_rate_via_control = value;
else if (strstr(kcontrol->id.name, "Channels"))
i2s->channels_via_control = value;
else if (strstr(kcontrol->id.name, "Playback Audio Channels"))
i2s->audio_ch_override[I2S_RX_PATH] = value;
else if (strstr(kcontrol->id.name, "Capture Audio Channels"))
i2s->audio_ch_override[I2S_TX_PATH] = value;
else if (strstr(kcontrol->id.name, "Client Channels"))
i2s->client_ch_override = value;
else if (strstr(kcontrol->id.name, "Capture stereo to mono"))
i2s->stereo_to_mono[I2S_TX_PATH] = value;
else if (strstr(kcontrol->id.name, "Capture mono to stereo"))
@@ -512,7 +521,7 @@ static int tegra210_i2s_hw_params(struct snd_pcm_substream *substream,
struct device *dev = dai->dev;
struct tegra210_i2s *i2s = snd_soc_dai_get_drvdata(dai);
unsigned int mask, val, reg, frame_format, rx_mask, tx_mask;
int ret, sample_size, channels, srate, i2sclock, bitcnt, max_th;
int ret, sample_size, channels, srate, i2sclock, bitcnt, max_th, path;
struct tegra210_xbar_cif_conf cif_conf;
memset(&cif_conf, 0, sizeof(struct tegra210_xbar_cif_conf));
@@ -523,6 +532,9 @@ static int tegra210_i2s_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
cif_conf.audio_channels = channels;
cif_conf.client_channels = channels;
mask = TEGRA210_I2S_CTRL_BIT_SIZE_MASK;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S8:
@@ -567,27 +579,37 @@ static int tegra210_i2s_hw_params(struct snd_pcm_substream *substream,
if (i2s->sample_rate_via_control)
srate = i2s->sample_rate_via_control;
if (i2s->channels_via_control) {
channels = i2s->channels_via_control;
rx_mask = tx_mask = (1 << channels) - 1;
/*
* For playback I2S RX-CIF and for capture TX-CIF is used.
* With reference to AHUB, for I2S, SNDRV_PCM_STREAM_CAPTURE stream is
* actually for playback.
*/
path = (substream->stream == SNDRV_PCM_STREAM_CAPTURE) ?
I2S_RX_PATH : I2S_TX_PATH;
if (i2s->audio_ch_override[path]) {
cif_conf.audio_channels = i2s->audio_ch_override[path];
rx_mask = tx_mask = (1 << cif_conf.audio_channels) - 1;
} else {
rx_mask = i2s->rx_mask;
tx_mask = i2s->tx_mask;
}
if (i2s->client_ch_override)
cif_conf.client_channels = i2s->client_ch_override;
if (i2s->audio_fmt_override[path])
cif_conf.audio_bits =
tegra210_i2s_fmt_values[i2s->audio_fmt_override[path]];
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) {
tegra210_i2s_set_slot_ctrl(i2s->regmap, channels, tx_mask,
rx_mask);
cif_conf.audio_channels = channels;
cif_conf.client_channels = channels;
} else {
cif_conf.audio_channels = channels;
cif_conf.client_channels = (channels == 1) ? 2 : channels;
}
if (frame_format == TEGRA210_I2S_CTRL_FRAME_FORMAT_FSYNC_MODE)
tegra210_i2s_set_slot_ctrl(i2s->regmap,
cif_conf.client_channels,
tx_mask, rx_mask);
i2sclock = srate * sample_size * cif_conf.client_channels;
@@ -620,44 +642,24 @@ static int tegra210_i2s_hw_params(struct snd_pcm_substream *substream,
/* As a COCEC DAI, CAPTURE is transmit */
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
unsigned int audio_ch = cif_conf.audio_channels;
reg = TEGRA210_I2S_AXBAR_RX_CIF_CTRL;
if (i2s->mono_to_stereo[I2S_RX_PATH] > 0) {
cif_conf.audio_channels = 1;
cif_conf.client_channels = 2;
cif_conf.mono_conv =
i2s->mono_to_stereo[I2S_RX_PATH] - 1;
} else if (i2s->stereo_to_mono[I2S_RX_PATH] > 0) {
cif_conf.audio_channels = 2;
cif_conf.client_channels = 1;
cif_conf.stereo_conv =
i2s->stereo_to_mono[I2S_RX_PATH] - 1;
}
/* RX FIFO threshold interms of frames */
max_th = (TEGRA210_I2S_RX_FIFO_DEPTH / channels) - 1;
if (i2s->rx_fifo_th > max_th) { /* error handling */
cif_conf.threshold = max_th;
i2s->rx_fifo_th = max_th;
} else
cif_conf.threshold = i2s->rx_fifo_th;
} else {
if (i2s->mono_to_stereo[I2S_TX_PATH] > 0) {
cif_conf.audio_channels = 2;
cif_conf.client_channels = 1;
cif_conf.mono_conv =
i2s->mono_to_stereo[I2S_TX_PATH] - 1;
} else if (i2s->stereo_to_mono[I2S_TX_PATH] > 0) {
cif_conf.audio_channels = 1;
cif_conf.client_channels = 2;
cif_conf.stereo_conv =
i2s->stereo_to_mono[I2S_TX_PATH] - 1;
}
max_th = (TEGRA210_I2S_RX_FIFO_DEPTH / audio_ch) - 1;
if (max_th < 0)
return -EINVAL;
if (i2s->format_in)
cif_conf.audio_bits =
tegra210_i2s_fmt_values[i2s->format_in];
if (i2s->rx_fifo_th > max_th) /* error handling */
i2s->rx_fifo_th = max_th;
cif_conf.threshold = i2s->rx_fifo_th;
} else
reg = TEGRA210_I2S_AXBAR_TX_CIF_CTRL;
}
cif_conf.stereo_conv = i2s->stereo_to_mono[path];
cif_conf.mono_conv = i2s->mono_to_stereo[path];
tegra210_xbar_set_cif(i2s->regmap, reg, &cif_conf);
@@ -829,11 +831,11 @@ static int tegra210_i2s_fsync_width_put(struct snd_kcontrol *kcontrol,
}
static const char * const tegra210_i2s_stereo_conv_text[] = {
"None", "CH0", "CH1", "AVG",
"CH0", "CH1", "AVG",
};
static const char * const tegra210_i2s_mono_conv_text[] = {
"None", "ZERO", "COPY",
"ZERO", "COPY",
};
static const struct soc_enum tegra210_i2s_mono_conv_enum =
@@ -856,15 +858,21 @@ static const struct soc_enum tegra210_i2s_stereo_conv_enum =
static const struct snd_kcontrol_new tegra210_i2s_controls[] = {
SOC_SINGLE_EXT("Loopback", SND_SOC_NOPM, 0, 1, 0,
tegra210_i2s_loopback_get, tegra210_i2s_loopback_put),
SOC_ENUM_EXT("input bit format", tegra210_i2s_format_enum,
tegra210_i2s_get_format, tegra210_i2s_put_format),
SOC_ENUM_EXT("Playback Audio Bit Format", tegra210_i2s_format_enum,
tegra210_i2s_get_format, tegra210_i2s_put_format),
SOC_ENUM_EXT("Capture Audio Bit Format", tegra210_i2s_format_enum,
tegra210_i2s_get_format, tegra210_i2s_put_format),
SOC_ENUM_EXT("codec bit format", tegra210_i2s_format_enum,
tegra210_i2s_get_format, tegra210_i2s_put_format),
SOC_SINGLE_EXT("fsync width", SND_SOC_NOPM, 0, 255, 0,
tegra210_i2s_fsync_width_get, tegra210_i2s_fsync_width_put),
SOC_SINGLE_EXT("Sample Rate", 0, 0, 192000, 0,
tegra210_i2s_get_format, tegra210_i2s_put_format),
SOC_SINGLE_EXT("Channels", 0, 0, 16, 0,
SOC_SINGLE_EXT("Playback Audio Channels", 0, 0, 16, 0,
tegra210_i2s_get_format, tegra210_i2s_put_format),
SOC_SINGLE_EXT("Capture Audio Channels", 0, 0, 16, 0,
tegra210_i2s_get_format, tegra210_i2s_put_format),
SOC_SINGLE_EXT("Client Channels", 0, 0, 16, 0,
tegra210_i2s_get_format, tegra210_i2s_put_format),
SOC_SINGLE_EXT("BCLK Ratio", SND_SOC_NOPM, 0, INT_MAX, 0,
tegra210_i2s_get_bclk_ratio,