unsigned int reg, fifo_ctrl, fifo_size;
int valid_bit;
- cif_conf.audio_channels = params_channels(params);
- cif_conf.client_channels = params_channels(params);
+ if (admaif->override_channels[dai->id] > 0) {
+ cif_conf.audio_channels = admaif->override_channels[dai->id];
+ cif_conf.client_channels = admaif->override_channels[dai->id];
+ } else {
+ cif_conf.audio_channels = params_channels(params);
+ cif_conf.client_channels = params_channels(params);
+ }
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
.trigger = tegra210_admaif_trigger,
};
+static int tegra210_admaif_get_channels(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct tegra210_admaif *admaif = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = admaif->override_channels[mc->reg];
+ return 0;
+}
+
+static int tegra210_admaif_put_channels(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct tegra210_admaif *admaif = snd_soc_codec_get_drvdata(codec);
+ int value = ucontrol->value.integer.value[0];
+
+ if (value > 0 && value <= 16) {
+ admaif->override_channels[mc->reg] = value;
+ return 0;
+ } else
+ return -EINVAL;
+}
+
static int tegra210_admaif_dai_probe(struct snd_soc_dai *dai)
{
struct tegra210_admaif *admaif = snd_soc_dai_get_drvdata(dai);
ADMAIF_ROUTES(10)
};
+#define TEGRA210_ADMAIF_CHANNEL_CTRL(reg) \
+ SOC_SINGLE_EXT("ADMAIF" #reg " Channels", reg - 1, 0, 16, 0, \
+ tegra210_admaif_get_channels, tegra210_admaif_put_channels)
+
+static struct snd_kcontrol_new tegra210_admaif_controls[] = {
+ TEGRA210_ADMAIF_CHANNEL_CTRL(1),
+ TEGRA210_ADMAIF_CHANNEL_CTRL(2),
+ TEGRA210_ADMAIF_CHANNEL_CTRL(3),
+ TEGRA210_ADMAIF_CHANNEL_CTRL(4),
+ TEGRA210_ADMAIF_CHANNEL_CTRL(5),
+ TEGRA210_ADMAIF_CHANNEL_CTRL(6),
+ TEGRA210_ADMAIF_CHANNEL_CTRL(7),
+ TEGRA210_ADMAIF_CHANNEL_CTRL(8),
+ TEGRA210_ADMAIF_CHANNEL_CTRL(9),
+ TEGRA210_ADMAIF_CHANNEL_CTRL(10)
+};
+
static int tegra210_admaif_codec_probe(struct snd_soc_codec *codec)
{
struct tegra210_admaif *admaif = snd_soc_codec_get_drvdata(codec);
.num_dapm_widgets = ARRAY_SIZE(tegra210_admaif_widgets),
.dapm_routes = tegra210_admaif_routes,
.num_dapm_routes = ARRAY_SIZE(tegra210_admaif_routes),
+ .controls = tegra210_admaif_controls,
+ .num_controls = ARRAY_SIZE(tegra210_admaif_controls),
.idle_bias_off = 1,
};
admaif->soc_data->num_ch,
GFP_KERNEL);
if (!admaif->capture_dma_data) {
- dev_err(&pdev->dev, "Can't allocate tegra_alt_pcm_dma_params\n");
+ dev_err(&pdev->dev, "Can't allocate capture_dma_data\n");
ret = -ENOMEM;
goto err;
}
admaif->soc_data->num_ch,
GFP_KERNEL);
if (!admaif->playback_dma_data) {
- dev_err(&pdev->dev, "Can't allocate tegra_alt_pcm_dma_params\n");
+ dev_err(&pdev->dev, "Can't allocate playback_dma_data\n");
ret = -ENOMEM;
goto err;
}
struct tegra_alt_pcm_dma_params *capture_dma_data;
struct tegra_alt_pcm_dma_params *playback_dma_data;
const struct tegra210_admaif_soc_data *soc_data;
+ int override_channels[TEGRA210_ADMAIF_CHANNEL_COUNT];
};
#endif
}
#endif
-static int tegra210_amx_set_audio_cif(struct tegra210_amx *amx,
+static int tegra210_amx_set_audio_cif(struct snd_soc_dai *dai,
struct snd_pcm_hw_params *params,
unsigned int reg)
{
+ struct tegra210_amx *amx = snd_soc_dai_get_drvdata(dai);
int channels, audio_bits;
struct tegra210_xbar_cif_conf cif_conf;
- channels = params_channels(params);
+ if (strstr(dai->name, "OUT")) {
+ channels = amx->output_channels > 0 ?
+ amx->output_channels : params_channels(params);
+ } else
+ channels = params_channels(params);
+
if (channels < 1 || channels > 16)
return -EINVAL;
return 0;
}
-
static int tegra210_amx_in_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct tegra210_amx *amx = snd_soc_dai_get_drvdata(dai);
int ret;
- ret = tegra210_amx_set_audio_cif(amx, params,
+ ret = tegra210_amx_set_audio_cif(dai, params,
TEGRA210_AMX_AXBAR_RX1_CIF_CTRL +
(dai->id * TEGRA210_AMX_AUDIOCIF_CH_STRIDE));
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
- struct tegra210_amx *amx = snd_soc_dai_get_drvdata(dai);
int ret;
- ret = tegra210_amx_set_audio_cif(amx, params,
+ ret = tegra210_amx_set_audio_cif(dai, params,
TEGRA210_AMX_AXBAR_TX_CIF_CTRL);
return ret;
/* update byte map */
tegra210_amx_set_map_table(amx, reg, stream, channel, byte);
- /* update byte_mask */
- if (value) {
- /* enable slot */
- if (reg > 31)
- amx->byte_mask[1] |= (1 << (reg - 32));
- else
- amx->byte_mask[0] |= (1 << reg);
- } else {
- /* disable slot */
- if (reg > 31)
- amx->byte_mask[1] &= ~(1 << (reg - 32));
- else
- amx->byte_mask[0] &= ~(1 << reg);
- }
+ /* update byte_mask to enable slot */
+ if (reg > 31)
+ amx->byte_mask[1] |= (1 << (reg - 32));
+ else
+ amx->byte_mask[0] |= (1 << reg);
return 0;
}
+static int tegra210_amx_get_output_channels(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct tegra210_amx *amx = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = amx->output_channels;
+ return 0;
+}
+
+static int tegra210_amx_put_output_channels(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct tegra210_amx *amx = snd_soc_codec_get_drvdata(codec);
+ int value = ucontrol->value.integer.value[0];
+
+ if (value > 0 && value <= 16) {
+ amx->output_channels = value;
+ return 0;
+ } else
+ return -EINVAL;
+}
+
static int tegra210_amx_codec_probe(struct snd_soc_codec *codec)
{
struct tegra210_amx *amx = snd_soc_codec_get_drvdata(codec);
SOC_SINGLE_EXT("Byte Map " #reg, reg, 0, 0xFF, 0, \
tegra210_amx_get_byte_map, tegra210_amx_put_byte_map)
-static struct snd_kcontrol_new tegra210_amx_map_controls[] = {
+#define TEGRA210_AMX_OUTPUT_CHANNELS_CTRL(reg) \
+ SOC_SINGLE_EXT("Output Channels", reg, 0, 16, 0, \
+ tegra210_amx_get_output_channels, \
+ tegra210_amx_put_output_channels)
+
+static struct snd_kcontrol_new tegra210_amx_controls[] = {
TEGRA210_AMX_BYTE_MAP_CTRL(0),
TEGRA210_AMX_BYTE_MAP_CTRL(1),
TEGRA210_AMX_BYTE_MAP_CTRL(2),
TEGRA210_AMX_BYTE_MAP_CTRL(61),
TEGRA210_AMX_BYTE_MAP_CTRL(62),
TEGRA210_AMX_BYTE_MAP_CTRL(63),
+
+ TEGRA210_AMX_OUTPUT_CHANNELS_CTRL(1),
};
static struct snd_soc_codec_driver tegra210_amx_codec = {
.num_dapm_widgets = ARRAY_SIZE(tegra210_amx_widgets),
.dapm_routes = tegra210_amx_routes,
.num_dapm_routes = ARRAY_SIZE(tegra210_amx_routes),
- .controls = tegra210_amx_map_controls,
- .num_controls = ARRAY_SIZE(tegra210_amx_map_controls),
+ .controls = tegra210_amx_controls,
+ .num_controls = ARRAY_SIZE(tegra210_amx_controls),
.idle_bias_off = 1,
};
/*
* Those defines are not in register field.
*/
+#define TEGRA210_AMX_NUM_INPUTS 4
#define TEGRA210_AMX_RAM_DEPTH 16
#define TEGRA210_AMX_MAP_STREAM_NUMBER_SHIFT 6
#define TEGRA210_AMX_MAP_STREAM_NUMBER_MASK (0x3 << TEGRA210_AMX_MAP_STREAM_NUMBER_SHIFT)
struct tegra210_amx {
struct regmap *regmap;
- unsigned int map[16];
+ unsigned int map[TEGRA210_AMX_RAM_DEPTH];
unsigned int byte_mask[2];
+ int output_channels;
const struct tegra210_amx_soc_data *soc_data;
};
return -EINVAL;
}
+ if (sfc->stereo_conv_input > 0 && 2 == channels &&
+ (reg == TEGRA210_SFC_AXBAR_RX_CIF_CTRL)) {
+ cif_conf.stereo_conv = sfc->stereo_conv_input - 1;
+ cif_conf.client_channels = 1;
+ } else if (sfc->mono_conv_output > 0 && 2 == channels &&
+ (reg == TEGRA210_SFC_AXBAR_TX_CIF_CTRL)) {
+ cif_conf.mono_conv = sfc->mono_conv_output - 1;
+ cif_conf.client_channels = 1;
+ } else {
+ cif_conf.mono_conv = 0;
+ cif_conf.stereo_conv = 0;
+ cif_conf.client_channels = channels;
+ }
+
cif_conf.threshold = 0;
cif_conf.audio_channels = channels;
- cif_conf.client_channels = channels;
cif_conf.audio_bits = audio_bits;
if (sfc->format_in && (reg == TEGRA210_SFC_AXBAR_RX_CIF_CTRL))
cif_conf.audio_bits = tegra210_sfc_fmt_values[sfc->format_in];
cif_conf.client_bits = TEGRA210_AUDIOCIF_BITS_32;
cif_conf.expand = 0;
- cif_conf.stereo_conv = 0;
cif_conf.replicate = 0;
cif_conf.truncate = 0;
- cif_conf.mono_conv = 0;
sfc->soc_data->set_audio_cif(sfc->regmap, reg, &cif_conf);
return 0;
}
+static int tegra210_sfc_get_stereo_conv(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct tegra210_sfc *sfc = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = sfc->stereo_conv_input;
+ return 0;
+}
+
+static int tegra210_sfc_put_stereo_conv(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct tegra210_sfc *sfc = snd_soc_codec_get_drvdata(codec);
+
+ sfc->stereo_conv_input = ucontrol->value.integer.value[0];
+ return 0;
+}
+
+static int tegra210_sfc_get_mono_conv(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct tegra210_sfc *sfc = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = sfc->mono_conv_output;
+ return 0;
+}
+
+static int tegra210_sfc_put_mono_conv(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct tegra210_sfc *sfc = snd_soc_codec_get_drvdata(codec);
+
+ sfc->mono_conv_output = ucontrol->value.integer.value[0];
+ return 0;
+}
+
static int tegra210_sfc_codec_probe(struct snd_soc_codec *codec)
{
struct tegra210_sfc *sfc = snd_soc_codec_get_drvdata(codec);
"32",
};
+static const char * const tegra210_sfc_stereo_conv_text[] = {
+ "None", "CH0", "CH1", "AVG",
+};
+
+static const char * const tegra210_sfc_mono_conv_text[] = {
+ "None", "ZERO", "COPY",
+};
+
static const struct soc_enum tegra210_sfc_format_enum =
SOC_ENUM_SINGLE(SND_SOC_NOPM, 0,
ARRAY_SIZE(tegra210_sfc_format_text),
tegra210_sfc_format_text);
+static const struct soc_enum tegra210_sfc_stereo_conv_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ ARRAY_SIZE(tegra210_sfc_stereo_conv_text),
+ tegra210_sfc_stereo_conv_text);
+
+static const struct soc_enum tegra210_sfc_mono_conv_enum =
+ SOC_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ ARRAY_SIZE(tegra210_sfc_mono_conv_text),
+ tegra210_sfc_mono_conv_text);
+
static const struct snd_kcontrol_new tegra210_sfc_controls[] = {
SOC_SINGLE_EXT("input rate", 0, 0, 192000, 0,
tegra210_sfc_get_srate, tegra210_sfc_put_srate),
tegra210_sfc_get_format, tegra210_sfc_put_format),
SOC_SINGLE_EXT("init", 0, 0, 1, 0,
tegra210_sfc_init_get, tegra210_sfc_init_put),
+ SOC_ENUM_EXT("input stereo conv", tegra210_sfc_stereo_conv_enum,
+ tegra210_sfc_get_stereo_conv, tegra210_sfc_put_stereo_conv),
+ SOC_ENUM_EXT("output mono conv", tegra210_sfc_mono_conv_enum,
+ tegra210_sfc_get_mono_conv, tegra210_sfc_put_mono_conv),
};
static struct snd_soc_codec_driver tegra210_sfc_codec = {
struct regmap *regmap;
struct snd_pcm_hw_params in_hw_params;
struct snd_pcm_hw_params out_hw_params;
+ int stereo_conv_input;
+ int mono_conv_output;
const struct tegra210_sfc_soc_data *soc_data;
};