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 DATA_32BIT
}; };
enum {
ADMAIF_RX_PATH,
ADMAIF_TX_PATH,
ADMAIF_PATHS,
};
struct tegra_admaif_soc_data { struct tegra_admaif_soc_data {
unsigned int num_ch; 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 *capture_dma_data;
struct tegra_alt_pcm_dma_params *playback_dma_data; struct tegra_alt_pcm_dma_params *playback_dma_data;
const struct tegra_admaif_soc_data *soc_data; const struct tegra_admaif_soc_data *soc_data;
int *override_channels; unsigned int *audio_ch_override[ADMAIF_PATHS];
int *tx_mono_to_stereo; unsigned int *client_ch_override[ADMAIF_PATHS];
int *rx_stereo_to_mono; unsigned int *mono_to_stereo[ADMAIF_PATHS];
unsigned int *stereo_to_mono[ADMAIF_PATHS];
int reg_dump_flag; int reg_dump_flag;
void __iomem *base_addr; void __iomem *base_addr;
}; };

View File

@@ -206,10 +206,11 @@ struct tegra210_i2s {
struct notifier_block slgc_notifier; struct notifier_block slgc_notifier;
int num_supplies; int num_supplies;
int bclk_ratio; int bclk_ratio;
int format_in; int audio_fmt_override[I2S_PATHS];
int codec_bit_format; int codec_bit_format;
int sample_rate_via_control; 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 stereo_to_mono[I2S_PATHS];
int mono_to_stereo[I2S_PATHS]; int mono_to_stereo[I2S_PATHS];
unsigned int fsync_width; 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 device *dev = dai->dev;
struct tegra_admaif *admaif = snd_soc_dai_get_drvdata(dai); struct tegra_admaif *admaif = snd_soc_dai_get_drvdata(dai);
struct tegra210_xbar_cif_conf cif_conf; struct tegra210_xbar_cif_conf cif_conf;
unsigned int reg; unsigned int reg, path;
int valid_bit, channels; int valid_bit, channels;
memset(&cif_conf, 0, sizeof(struct tegra210_xbar_cif_conf)); 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; cif_conf.audio_channels = channels;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
path = ADMAIF_TX_PATH;
reg = CH_TX_REG(TEGRA_ADMAIF_CHAN_ACIF_TX_CTRL, dai->id); 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 { } else {
path = ADMAIF_RX_PATH;
reg = CH_RX_REG(TEGRA_ADMAIF_CHAN_ACIF_RX_CTRL, dai->id); 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); tegra_admaif_set_pack_mode(admaif->regmap, reg, valid_bit);
tegra210_xbar_set_cif(admaif->regmap, reg, &cif_conf); tegra210_xbar_set_cif(admaif->regmap, reg, &cif_conf);
return 0; 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 snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct soc_mixer_control *mc = struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value; (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); struct tegra_admaif *admaif = snd_soc_codec_get_drvdata(codec);
int i; long *uctl_val = &ucontrol->value.integer.value[0];
char buf[50];
if (strstr(kcontrol->id.name, "Channels")) if (strstr(kcontrol->id.name, "Playback Audio Channels"))
ucontrol->value.integer.value[0] = *uctl_val = admaif->audio_ch_override[ADMAIF_TX_PATH][mc->reg];
admaif->override_channels[mc->reg]; else if (strstr(kcontrol->id.name, "Capture Audio Channels"))
else { *uctl_val = admaif->audio_ch_override[ADMAIF_RX_PATH][mc->reg];
for (i = 0 ; i < admaif->soc_data->num_ch; i++) { else if (strstr(kcontrol->id.name, "Playback Client Channels"))
snprintf(buf, 50, "ADMAIF%d Capture stereo to mono", i+1); *uctl_val = admaif->client_ch_override[ADMAIF_TX_PATH][mc->reg];
if (strstr(kcontrol->id.name, buf)) { else if (strstr(kcontrol->id.name, "Capture Client Channels"))
ucontrol->value.integer.value[0] = *uctl_val = admaif->client_ch_override[ADMAIF_RX_PATH][mc->reg];
admaif->rx_stereo_to_mono[i]; else if (strstr(kcontrol->id.name, "Playback mono to stereo"))
break; *uctl_val = admaif->mono_to_stereo[ADMAIF_TX_PATH][ec->reg];
} else if (strstr(kcontrol->id.name, "Playback stereo to mono"))
snprintf(buf, 50, "ADMAIF%d Playback mono to stereo", i+1); *uctl_val = admaif->stereo_to_mono[ADMAIF_TX_PATH][ec->reg];
if (strstr(kcontrol->id.name, buf)) { else if (strstr(kcontrol->id.name, "Capture mono to stereo"))
ucontrol->value.integer.value[0] = *uctl_val = admaif->mono_to_stereo[ADMAIF_RX_PATH][ec->reg];
admaif->tx_mono_to_stereo[i]; else if (strstr(kcontrol->id.name, "Capture stereo to mono"))
break; *uctl_val = admaif->stereo_to_mono[ADMAIF_RX_PATH][ec->reg];
}
}
}
return 0; 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 *mc =
(struct soc_mixer_control *)kcontrol->private_value; (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 snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct tegra_admaif *admaif = snd_soc_codec_get_drvdata(codec); struct tegra_admaif *admaif = snd_soc_codec_get_drvdata(codec);
int value = ucontrol->value.integer.value[0]; int value = ucontrol->value.integer.value[0];
int i;
char buf[50];
if (strstr(kcontrol->id.name, "Channels")) { if (strstr(kcontrol->id.name, "Playback Audio Channels"))
if (value >= 0 && value <= 16) admaif->audio_ch_override[ADMAIF_TX_PATH][mc->reg] = value;
admaif->override_channels[mc->reg] = value; else if (strstr(kcontrol->id.name, "Capture Audio Channels"))
else admaif->audio_ch_override[ADMAIF_RX_PATH][mc->reg] = value;
return -EINVAL; else if (strstr(kcontrol->id.name, "Playback Client Channels"))
} else { admaif->client_ch_override[ADMAIF_TX_PATH][mc->reg] = value;
for (i = 0 ; i < admaif->soc_data->num_ch; i++) { else if (strstr(kcontrol->id.name, "Capture Client Channels"))
snprintf(buf, 50, "ADMAIF%d Capture stereo to mono", i+1); admaif->client_ch_override[ADMAIF_RX_PATH][mc->reg] = value;
if (strstr(kcontrol->id.name, buf)) { else if (strstr(kcontrol->id.name, "Playback mono to stereo"))
admaif->rx_stereo_to_mono[i] = value; admaif->mono_to_stereo[ADMAIF_TX_PATH][ec->reg] = value;
break; else if (strstr(kcontrol->id.name, "Playback stereo to mono"))
} admaif->stereo_to_mono[ADMAIF_TX_PATH][ec->reg] = value;
snprintf(buf, 50, "ADMAIF%d Playback mono to stereo", i+1); else if (strstr(kcontrol->id.name, "Capture mono to stereo"))
if (strstr(kcontrol->id.name, buf)) { admaif->mono_to_stereo[ADMAIF_RX_PATH][ec->reg] = value;
admaif->tx_mono_to_stereo[i] = value; else if (strstr(kcontrol->id.name, "Capture stereo to mono"))
break; admaif->stereo_to_mono[ADMAIF_RX_PATH][ec->reg] = value;
}
}
}
return 0; 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[] = { 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[] = { static const char * const tegra_admaif_mono_conv_text[] = {
"None", "ZERO", "COPY", "ZERO", "COPY",
}; };
static const struct soc_enum tegra_admaif_mono_conv_enum = 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), ARRAY_SIZE(tegra_admaif_stereo_conv_text),
tegra_admaif_stereo_conv_text); tegra_admaif_stereo_conv_text);
#define TEGRA_ADMAIF_CHANNEL_CTRL(reg) \ #define TEGRA_ADMAIF_CHANNEL_CTRL(reg) \
SOC_SINGLE_EXT("ADMAIF" #reg " Channels", reg - 1, 0, 16, 0, \ SOC_SINGLE_EXT("ADMAIF" #reg " Playback Audio Channels", reg - 1, \
tegra_admaif_get_format, tegra_admaif_put_format) 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", \ * below macro is added to avoid looping over all ADMAIFx controls related
tegra_admaif_mono_conv_enum, tegra_admaif_get_format, \ * to mono/stereo conversions in get()/put() callbacks.
tegra_admaif_put_format) */
#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) \ #define TEGRA_ADMAIF_CIF_CTRL(reg) \
SOC_ENUM_EXT("ADMAIF" #reg " Capture stereo to mono conv", \ NV_SOC_ENUM_EXT("ADMAIF" #reg " Playback mono to stereo conv", reg - 1,\
tegra_admaif_stereo_conv_enum, tegra_admaif_get_format, \ tegra_admaif_get_format, tegra_admaif_put_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[] = { static struct snd_kcontrol_new tegra210_admaif_controls[] = {
TEGRA_ADMAIF_CHANNEL_CTRL(1), 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(8),
TEGRA_ADMAIF_CHANNEL_CTRL(9), TEGRA_ADMAIF_CHANNEL_CTRL(9),
TEGRA_ADMAIF_CHANNEL_CTRL(10), TEGRA_ADMAIF_CHANNEL_CTRL(10),
TEGRA_ADMAIF_RX_CIF_CTRL(1), TEGRA_ADMAIF_CIF_CTRL(1),
TEGRA_ADMAIF_RX_CIF_CTRL(2), TEGRA_ADMAIF_CIF_CTRL(2),
TEGRA_ADMAIF_RX_CIF_CTRL(3), TEGRA_ADMAIF_CIF_CTRL(3),
TEGRA_ADMAIF_RX_CIF_CTRL(4), TEGRA_ADMAIF_CIF_CTRL(4),
TEGRA_ADMAIF_RX_CIF_CTRL(5), TEGRA_ADMAIF_CIF_CTRL(5),
TEGRA_ADMAIF_RX_CIF_CTRL(6), TEGRA_ADMAIF_CIF_CTRL(6),
TEGRA_ADMAIF_RX_CIF_CTRL(7), TEGRA_ADMAIF_CIF_CTRL(7),
TEGRA_ADMAIF_RX_CIF_CTRL(8), TEGRA_ADMAIF_CIF_CTRL(8),
TEGRA_ADMAIF_RX_CIF_CTRL(9), TEGRA_ADMAIF_CIF_CTRL(9),
TEGRA_ADMAIF_RX_CIF_CTRL(10), TEGRA_ADMAIF_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),
SOC_SINGLE_EXT("APE Reg Dump", SND_SOC_NOPM, 0, 1, 0, SOC_SINGLE_EXT("APE Reg Dump", SND_SOC_NOPM, 0, 1, 0,
tegra210_ape_dump_reg_get, tegra210_ape_dump_reg_put), 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(18),
TEGRA_ADMAIF_CHANNEL_CTRL(19), TEGRA_ADMAIF_CHANNEL_CTRL(19),
TEGRA_ADMAIF_CHANNEL_CTRL(20), TEGRA_ADMAIF_CHANNEL_CTRL(20),
TEGRA_ADMAIF_RX_CIF_CTRL(1), TEGRA_ADMAIF_CIF_CTRL(1),
TEGRA_ADMAIF_RX_CIF_CTRL(2), TEGRA_ADMAIF_CIF_CTRL(2),
TEGRA_ADMAIF_RX_CIF_CTRL(3), TEGRA_ADMAIF_CIF_CTRL(3),
TEGRA_ADMAIF_RX_CIF_CTRL(4), TEGRA_ADMAIF_CIF_CTRL(4),
TEGRA_ADMAIF_RX_CIF_CTRL(5), TEGRA_ADMAIF_CIF_CTRL(5),
TEGRA_ADMAIF_RX_CIF_CTRL(6), TEGRA_ADMAIF_CIF_CTRL(6),
TEGRA_ADMAIF_RX_CIF_CTRL(7), TEGRA_ADMAIF_CIF_CTRL(7),
TEGRA_ADMAIF_RX_CIF_CTRL(8), TEGRA_ADMAIF_CIF_CTRL(8),
TEGRA_ADMAIF_RX_CIF_CTRL(9), TEGRA_ADMAIF_CIF_CTRL(9),
TEGRA_ADMAIF_RX_CIF_CTRL(10), TEGRA_ADMAIF_CIF_CTRL(10),
TEGRA_ADMAIF_RX_CIF_CTRL(11), TEGRA_ADMAIF_CIF_CTRL(11),
TEGRA_ADMAIF_RX_CIF_CTRL(12), TEGRA_ADMAIF_CIF_CTRL(12),
TEGRA_ADMAIF_RX_CIF_CTRL(13), TEGRA_ADMAIF_CIF_CTRL(13),
TEGRA_ADMAIF_RX_CIF_CTRL(14), TEGRA_ADMAIF_CIF_CTRL(14),
TEGRA_ADMAIF_RX_CIF_CTRL(15), TEGRA_ADMAIF_CIF_CTRL(15),
TEGRA_ADMAIF_RX_CIF_CTRL(16), TEGRA_ADMAIF_CIF_CTRL(16),
TEGRA_ADMAIF_RX_CIF_CTRL(17), TEGRA_ADMAIF_CIF_CTRL(17),
TEGRA_ADMAIF_RX_CIF_CTRL(18), TEGRA_ADMAIF_CIF_CTRL(18),
TEGRA_ADMAIF_RX_CIF_CTRL(19), TEGRA_ADMAIF_CIF_CTRL(19),
TEGRA_ADMAIF_RX_CIF_CTRL(20), TEGRA_ADMAIF_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),
SOC_SINGLE_EXT("APE Reg Dump", SND_SOC_NOPM, 0, 1, 0, SOC_SINGLE_EXT("APE Reg Dump", SND_SOC_NOPM, 0, 1, 0,
tegra210_ape_dump_reg_get, tegra210_ape_dump_reg_put), 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) if (!admaif->playback_dma_data)
return -ENOMEM; return -ENOMEM;
admaif->override_channels = devm_kzalloc(&pdev->dev, for (i = 0; i < ADMAIF_PATHS; i++) {
sizeof(int) * admaif->soc_data->num_ch, admaif->audio_ch_override[i] =
GFP_KERNEL); devm_kcalloc(&pdev->dev, admaif->soc_data->num_ch,
if (!admaif->override_channels) sizeof(unsigned int), GFP_KERNEL);
return -ENOMEM; if (!admaif->audio_ch_override[i])
return -ENOMEM;
admaif->tx_mono_to_stereo = devm_kzalloc(&pdev->dev, admaif->client_ch_override[i] =
sizeof(int) * admaif->soc_data->num_ch, devm_kcalloc(&pdev->dev, admaif->soc_data->num_ch,
GFP_KERNEL); sizeof(unsigned int), GFP_KERNEL);
if (!admaif->tx_mono_to_stereo) if (!admaif->client_ch_override[i])
return -ENOMEM; return -ENOMEM;
admaif->rx_stereo_to_mono = devm_kzalloc(&pdev->dev, admaif->mono_to_stereo[i] =
sizeof(int) * admaif->soc_data->num_ch, devm_kcalloc(&pdev->dev, admaif->soc_data->num_ch,
GFP_KERNEL); sizeof(unsigned int), GFP_KERNEL);
if (!admaif->rx_stereo_to_mono) if (!admaif->mono_to_stereo[i])
return -ENOMEM; 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); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
regs = devm_ioremap_resource(&pdev->dev, res); 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 snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct tegra210_i2s *i2s = snd_soc_codec_get_drvdata(codec); struct tegra210_i2s *i2s = snd_soc_codec_get_drvdata(codec);
long *uctl_val = &ucontrol->value.integer.value[0];
/* get the format control flag */ /* get the format control flag */
if (strstr(kcontrol->id.name, "input")) if (strstr(kcontrol->id.name, "Playback Audio Bit Format"))
ucontrol->value.integer.value[0] = i2s->format_in; *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")) 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")) else if (strstr(kcontrol->id.name, "Sample Rate"))
ucontrol->value.integer.value[0] = i2s->sample_rate_via_control; *uctl_val = i2s->sample_rate_via_control;
else if (strstr(kcontrol->id.name, "Channels")) else if (strstr(kcontrol->id.name, "Playback Audio Channels"))
ucontrol->value.integer.value[0] = i2s->channels_via_control; *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")) else if (strstr(kcontrol->id.name, "Capture stereo to mono"))
ucontrol->value.integer.value[0] = *uctl_val = i2s->stereo_to_mono[I2S_TX_PATH];
i2s->stereo_to_mono[I2S_TX_PATH];
else if (strstr(kcontrol->id.name, "Capture mono to stereo")) else if (strstr(kcontrol->id.name, "Capture mono to stereo"))
ucontrol->value.integer.value[0] = *uctl_val = i2s->mono_to_stereo[I2S_TX_PATH];
i2s->mono_to_stereo[I2S_TX_PATH];
else if (strstr(kcontrol->id.name, "Playback stereo to mono")) else if (strstr(kcontrol->id.name, "Playback stereo to mono"))
ucontrol->value.integer.value[0] = *uctl_val = i2s->stereo_to_mono[I2S_RX_PATH];
i2s->stereo_to_mono[I2S_RX_PATH];
else if (strstr(kcontrol->id.name, "Playback mono to stereo")) else if (strstr(kcontrol->id.name, "Playback mono to stereo"))
ucontrol->value.integer.value[0] = *uctl_val = i2s->mono_to_stereo[I2S_RX_PATH];
i2s->mono_to_stereo[I2S_RX_PATH];
else if (strstr(kcontrol->id.name, "Playback FIFO threshold")) 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")) else if (strstr(kcontrol->id.name, "BCLK Ratio"))
ucontrol->value.integer.value[0] = i2s->bclk_ratio; *uctl_val = i2s->bclk_ratio;
return 0; return 0;
} }
@@ -408,14 +411,20 @@ static int tegra210_i2s_put_format(struct snd_kcontrol *kcontrol,
int value = ucontrol->value.integer.value[0]; int value = ucontrol->value.integer.value[0];
/* set the format control flag */ /* set the format control flag */
if (strstr(kcontrol->id.name, "input")) if (strstr(kcontrol->id.name, "Playback Audio Bit Format"))
i2s->format_in = value; 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")) else if (strstr(kcontrol->id.name, "codec"))
i2s->codec_bit_format = value; i2s->codec_bit_format = value;
else if (strstr(kcontrol->id.name, "Sample Rate")) else if (strstr(kcontrol->id.name, "Sample Rate"))
i2s->sample_rate_via_control = value; i2s->sample_rate_via_control = value;
else if (strstr(kcontrol->id.name, "Channels")) else if (strstr(kcontrol->id.name, "Playback Audio Channels"))
i2s->channels_via_control = value; 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")) else if (strstr(kcontrol->id.name, "Capture stereo to mono"))
i2s->stereo_to_mono[I2S_TX_PATH] = value; i2s->stereo_to_mono[I2S_TX_PATH] = value;
else if (strstr(kcontrol->id.name, "Capture mono to stereo")) 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 device *dev = dai->dev;
struct tegra210_i2s *i2s = snd_soc_dai_get_drvdata(dai); struct tegra210_i2s *i2s = snd_soc_dai_get_drvdata(dai);
unsigned int mask, val, reg, frame_format, rx_mask, tx_mask; 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; struct tegra210_xbar_cif_conf cif_conf;
memset(&cif_conf, 0, sizeof(struct tegra210_xbar_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; return -EINVAL;
} }
cif_conf.audio_channels = channels;
cif_conf.client_channels = channels;
mask = TEGRA210_I2S_CTRL_BIT_SIZE_MASK; mask = TEGRA210_I2S_CTRL_BIT_SIZE_MASK;
switch (params_format(params)) { switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S8: 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) if (i2s->sample_rate_via_control)
srate = i2s->sample_rate_via_control; srate = i2s->sample_rate_via_control;
if (i2s->channels_via_control) { /*
channels = i2s->channels_via_control; * For playback I2S RX-CIF and for capture TX-CIF is used.
rx_mask = tx_mask = (1 << channels) - 1; * 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 { } else {
rx_mask = i2s->rx_mask; rx_mask = i2s->rx_mask;
tx_mask = i2s->tx_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); regmap_read(i2s->regmap, TEGRA210_I2S_CTRL, &val);
frame_format = val & TEGRA210_I2S_CTRL_FRAME_FORMAT_MASK; frame_format = val & TEGRA210_I2S_CTRL_FRAME_FORMAT_MASK;
if (frame_format == TEGRA210_I2S_CTRL_FRAME_FORMAT_FSYNC_MODE) { if (frame_format == TEGRA210_I2S_CTRL_FRAME_FORMAT_FSYNC_MODE)
tegra210_i2s_set_slot_ctrl(i2s->regmap, channels, tx_mask, tegra210_i2s_set_slot_ctrl(i2s->regmap,
rx_mask); cif_conf.client_channels,
cif_conf.audio_channels = channels; tx_mask, rx_mask);
cif_conf.client_channels = channels;
} else {
cif_conf.audio_channels = channels;
cif_conf.client_channels = (channels == 1) ? 2 : channels;
}
i2sclock = srate * sample_size * cif_conf.client_channels; 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 */ /* As a COCEC DAI, CAPTURE is transmit */
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
unsigned int audio_ch = cif_conf.audio_channels;
reg = TEGRA210_I2S_AXBAR_RX_CIF_CTRL; 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 */ /* RX FIFO threshold interms of frames */
max_th = (TEGRA210_I2S_RX_FIFO_DEPTH / channels) - 1; max_th = (TEGRA210_I2S_RX_FIFO_DEPTH / audio_ch) - 1;
if (i2s->rx_fifo_th > max_th) { /* error handling */ if (max_th < 0)
cif_conf.threshold = max_th; return -EINVAL;
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;
}
if (i2s->format_in) if (i2s->rx_fifo_th > max_th) /* error handling */
cif_conf.audio_bits = i2s->rx_fifo_th = max_th;
tegra210_i2s_fmt_values[i2s->format_in];
cif_conf.threshold = i2s->rx_fifo_th;
} else
reg = TEGRA210_I2S_AXBAR_TX_CIF_CTRL; 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); 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[] = { 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[] = { static const char * const tegra210_i2s_mono_conv_text[] = {
"None", "ZERO", "COPY", "ZERO", "COPY",
}; };
static const struct soc_enum tegra210_i2s_mono_conv_enum = 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[] = { static const struct snd_kcontrol_new tegra210_i2s_controls[] = {
SOC_SINGLE_EXT("Loopback", SND_SOC_NOPM, 0, 1, 0, SOC_SINGLE_EXT("Loopback", SND_SOC_NOPM, 0, 1, 0,
tegra210_i2s_loopback_get, tegra210_i2s_loopback_put), tegra210_i2s_loopback_get, tegra210_i2s_loopback_put),
SOC_ENUM_EXT("input bit format", tegra210_i2s_format_enum, SOC_ENUM_EXT("Playback Audio Bit Format", tegra210_i2s_format_enum,
tegra210_i2s_get_format, tegra210_i2s_put_format), 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, SOC_ENUM_EXT("codec bit format", tegra210_i2s_format_enum,
tegra210_i2s_get_format, tegra210_i2s_put_format), tegra210_i2s_get_format, tegra210_i2s_put_format),
SOC_SINGLE_EXT("fsync width", SND_SOC_NOPM, 0, 255, 0, SOC_SINGLE_EXT("fsync width", SND_SOC_NOPM, 0, 255, 0,
tegra210_i2s_fsync_width_get, tegra210_i2s_fsync_width_put), tegra210_i2s_fsync_width_get, tegra210_i2s_fsync_width_put),
SOC_SINGLE_EXT("Sample Rate", 0, 0, 192000, 0, SOC_SINGLE_EXT("Sample Rate", 0, 0, 192000, 0,
tegra210_i2s_get_format, tegra210_i2s_put_format), 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), tegra210_i2s_get_format, tegra210_i2s_put_format),
SOC_SINGLE_EXT("BCLK Ratio", SND_SOC_NOPM, 0, INT_MAX, 0, SOC_SINGLE_EXT("BCLK Ratio", SND_SOC_NOPM, 0, INT_MAX, 0,
tegra210_i2s_get_bclk_ratio, tegra210_i2s_get_bclk_ratio,