for (i = 0; i < NUM_DURATION_PARMS; i++) {
int val;
- if (instant_gain)
+ if (instant_gain) {
val = 1;
- else
- val = gain_params.duration[i];
+ } else {
+ if (i == DURATION_N3_ID)
+ val = mixer->duration[id];
+ else if (i == DURATION_INV_N3_ID)
+ val = (u32)(BIT_ULL(31 + TEGRA210_MIXER_PRESCALAR) /
+ mixer->duration[id]);
+ else
+ val = gain_params.duration[i];
+ }
err = tegra210_mixer_write_ram(mixer,
REG_DURATION_PARAM(reg, i),
return err;
}
+static int tegra210_mixer_get_fade_duration(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_kcontrol_chip(kcontrol);
+ struct tegra210_mixer *mixer = snd_soc_component_get_drvdata(cmpnt);
+
+ ucontrol->value.integer.value[0] = mixer->duration[mc->reg];
+
+ return 0;
+}
+
+static int tegra210_mixer_put_fade_duration(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_kcontrol_chip(kcontrol);
+ struct tegra210_mixer *mixer = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int id = mc->reg;
+ u32 duration = ucontrol->value.integer.value[0];
+
+ if (duration == 0 || duration > TEGRA210_MIXER_FADE_DURATION_MAX)
+ return -EINVAL;
+
+ if (mixer->duration[id] == duration)
+ return 0;
+
+ mixer->duration[id] = duration;
+ mixer->fade_pending[id] = true;
+
+ return 1;
+}
+
+static int tegra210_mixer_get_fade_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_kcontrol_chip(kcontrol);
+ struct tegra210_mixer *mixer = snd_soc_component_get_drvdata(cmpnt);
+
+ ucontrol->value.integer.value[0] = mixer->fade_gain[mc->reg];
+
+ return 0;
+}
+
+static int tegra210_mixer_put_fade_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_kcontrol_chip(kcontrol);
+ struct tegra210_mixer *mixer = snd_soc_component_get_drvdata(cmpnt);
+ unsigned int id = mc->reg;
+
+ if (ucontrol->value.integer.value[0] < 0 ||
+ ucontrol->value.integer.value[0] > TEGRA210_MIXER_GAIN_MAX)
+ return -EINVAL;
+
+ if (mixer->fade_gain[id] == ucontrol->value.integer.value[0])
+ return 0;
+
+ mixer->fade_gain[id] = ucontrol->value.integer.value[0];
+ mixer->fade_pending[id] = true;
+
+ return 1;
+}
+
+static int tegra210_mixer_get_fade_switch(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = 0;
+
+ return 0;
+}
+
+static int tegra210_mixer_put_fade_switch(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct tegra210_mixer *mixer = snd_soc_component_get_drvdata(cmpnt);
+ int id, err, changed = 0;
+
+ err = pm_runtime_resume_and_get(cmpnt->dev);
+ if (err < 0)
+ return err;
+
+ /* Switch off: disable sample count for all active fades */
+ if (!ucontrol->value.integer.value[0]) {
+ for (id = 0; id < TEGRA210_MIXER_RX_MAX; id++) {
+ if (!mixer->in_fade[id])
+ continue;
+
+ regmap_update_bits(mixer->regmap,
+ MIXER_REG(TEGRA210_MIXER_RX1_CTRL,
+ id),
+ TEGRA210_MIXER_SAMPLE_COUNT_ENABLE,
+ 0);
+ mixer->in_fade[id] = false;
+ changed = 1;
+ }
+
+ pm_runtime_put(cmpnt->dev);
+ return changed;
+ }
+
+ /* Stop active fades on pending streams before reconfiguring */
+ for (id = 0; id < TEGRA210_MIXER_RX_MAX; id++) {
+ if (!mixer->fade_pending[id])
+ continue;
+
+ if (mixer->in_fade[id]) {
+ regmap_update_bits(mixer->regmap,
+ MIXER_REG(TEGRA210_MIXER_RX1_CTRL,
+ id),
+ TEGRA210_MIXER_SAMPLE_COUNT_ENABLE,
+ 0);
+ mixer->in_fade[id] = false;
+ }
+
+ mixer->gain_value[id] = mixer->fade_gain[id];
+ err = tegra210_mixer_configure_gain(cmpnt, id, false);
+ if (err) {
+ dev_err(cmpnt->dev,
+ "Failed to configure fade for RX%d\n", id + 1);
+ pm_runtime_put(cmpnt->dev);
+ return err;
+ }
+
+ changed = 1;
+ }
+
+ if (!changed) {
+ pm_runtime_put(cmpnt->dev);
+ return 0;
+ }
+
+ /* Enable sample count for all pending streams */
+ for (id = 0; id < TEGRA210_MIXER_RX_MAX; id++) {
+ if (!mixer->fade_pending[id])
+ continue;
+
+ err = regmap_update_bits(mixer->regmap,
+ MIXER_REG(TEGRA210_MIXER_RX1_CTRL, id),
+ TEGRA210_MIXER_SAMPLE_COUNT_ENABLE,
+ TEGRA210_MIXER_SAMPLE_COUNT_ENABLE);
+ if (err) {
+ dev_err(cmpnt->dev,
+ "Failed to enable sample count for RX%d\n",
+ id + 1);
+ pm_runtime_put(cmpnt->dev);
+ return err;
+ }
+
+ mixer->in_fade[id] = true;
+ mixer->fade_pending[id] = false;
+ }
+
+ pm_runtime_put(cmpnt->dev);
+
+ return 1;
+}
+
+static int tegra210_mixer_get_fade_status(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cmpnt = snd_kcontrol_chip(kcontrol);
+ struct tegra210_mixer *mixer = snd_soc_component_get_drvdata(cmpnt);
+ u32 count;
+ int id, err;
+
+ err = pm_runtime_resume_and_get(cmpnt->dev);
+ if (err < 0)
+ return err;
+
+ for (id = 0; id < TEGRA210_MIXER_RX_MAX; id++) {
+ if (!mixer->in_fade[id]) {
+ ucontrol->value.integer.value[id] = TEGRA210_MIXER_FADE_IDLE;
+ continue;
+ }
+
+ regmap_read(mixer->regmap,
+ MIXER_REG(TEGRA210_MIXER_RX1_SAMPLE_COUNT, id),
+ &count);
+
+ if (count >= mixer->duration[id])
+ ucontrol->value.integer.value[id] = TEGRA210_MIXER_FADE_IDLE;
+ else
+ ucontrol->value.integer.value[id] = TEGRA210_MIXER_FADE_ACTIVE;
+ }
+
+ pm_runtime_put(cmpnt->dev);
+
+ return 0;
+}
+
static int tegra210_mixer_get_gain(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
ADDER_CTRL_DECL(adder4, TEGRA210_MIXER_TX4_ADDER_CONFIG);
ADDER_CTRL_DECL(adder5, TEGRA210_MIXER_TX5_ADDER_CONFIG);
+static int tegra210_mixer_fade_status_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = TEGRA210_MIXER_RX_MAX;
+ uinfo->value.integer.min = TEGRA210_MIXER_FADE_IDLE;
+ uinfo->value.integer.max = TEGRA210_MIXER_FADE_ACTIVE;
+
+ return 0;
+}
+
+#define FADE_CTRL(id) \
+ SOC_SINGLE_EXT("RX" #id " Fade Duration", (id) - 1, 0, \
+ TEGRA210_MIXER_FADE_DURATION_MAX, 0, \
+ tegra210_mixer_get_fade_duration, \
+ tegra210_mixer_put_fade_duration), \
+ SOC_SINGLE_EXT("RX" #id " Fade Gain", (id) - 1, 0, \
+ TEGRA210_MIXER_GAIN_MAX, 0, \
+ tegra210_mixer_get_fade_gain, \
+ tegra210_mixer_put_fade_gain),
+
#define GAIN_CTRL(id) \
SOC_SINGLE_EXT("RX" #id " Gain Volume", \
MIXER_GAIN_CFG_RAM_ADDR((id) - 1), 0, \
- 0x20000, 0, tegra210_mixer_get_gain, \
+ TEGRA210_MIXER_GAIN_MAX, 0, \
+ tegra210_mixer_get_gain, \
tegra210_mixer_put_gain), \
SOC_SINGLE_EXT("RX" #id " Instant Gain Volume", \
MIXER_GAIN_CFG_RAM_ADDR((id) - 1), 0, \
- 0x20000, 0, tegra210_mixer_get_gain, \
+ TEGRA210_MIXER_GAIN_MAX, 0, \
+ tegra210_mixer_get_gain, \
tegra210_mixer_put_instant_gain),
/* Volume controls for all MIXER inputs */
GAIN_CTRL(8)
GAIN_CTRL(9)
GAIN_CTRL(10)
+
+ FADE_CTRL(1)
+ FADE_CTRL(2)
+ FADE_CTRL(3)
+ FADE_CTRL(4)
+ FADE_CTRL(5)
+ FADE_CTRL(6)
+ FADE_CTRL(7)
+ FADE_CTRL(8)
+ FADE_CTRL(9)
+ FADE_CTRL(10)
+ SOC_SINGLE_EXT("Fade Switch", SND_SOC_NOPM, 0, 1, 0,
+ tegra210_mixer_get_fade_switch,
+ tegra210_mixer_put_fade_switch),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Fade Status",
+ .info = tegra210_mixer_fade_status_info,
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .get = tegra210_mixer_get_fade_status,
+ },
};
static const struct snd_soc_dapm_widget tegra210_mixer_widgets[] = {
case TEGRA210_MIXER_GAIN_CFG_RAM_DATA:
case TEGRA210_MIXER_PEAKM_RAM_CTRL:
case TEGRA210_MIXER_PEAKM_RAM_DATA:
+ case TEGRA210_MIXER_RX1_SAMPLE_COUNT:
return true;
default:
return false;
dev_set_drvdata(dev, mixer);
/* Use default gain value for all MIXER inputs */
- for (i = 0; i < TEGRA210_MIXER_RX_MAX; i++)
+ for (i = 0; i < TEGRA210_MIXER_RX_MAX; i++) {
mixer->gain_value[i] = gain_params.gain_value;
+ mixer->fade_gain[i] = gain_params.gain_value;
+ mixer->duration[i] = gain_params.duration[DURATION_N3_ID];
+ }
regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(regs))