diff --git a/sound/soc/tegra/tegra210_mixer.c b/sound/soc/tegra/tegra210_mixer.c index ce44117a0b9c..f05617b5f433 100644 --- a/sound/soc/tegra/tegra210_mixer.c +++ b/sound/soc/tegra/tegra210_mixer.c @@ -151,10 +151,17 @@ static int tegra210_mixer_configure_gain(struct snd_soc_component *cmpnt, 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), @@ -173,6 +180,204 @@ static int tegra210_mixer_configure_gain(struct snd_soc_component *cmpnt, 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) { @@ -396,14 +601,37 @@ 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 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 */ @@ -418,6 +646,28 @@ static const struct snd_kcontrol_new tegra210_mixer_gain_ctls[] = { 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[] = { @@ -579,6 +829,7 @@ static bool tegra210_mixer_volatile_reg(struct device *dev, 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; @@ -632,8 +883,11 @@ static int tegra210_mixer_platform_probe(struct platform_device *pdev) 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)) diff --git a/sound/soc/tegra/tegra210_mixer.h b/sound/soc/tegra/tegra210_mixer.h index a330530fbc61..bcbad08cbb9d 100644 --- a/sound/soc/tegra/tegra210_mixer.h +++ b/sound/soc/tegra/tegra210_mixer.h @@ -79,12 +79,25 @@ #define TEGRA210_MIXER_RX_LIMIT (TEGRA210_MIXER_RX_MAX * TEGRA210_MIXER_REG_STRIDE) #define TEGRA210_MIXER_TX_MAX 5 #define TEGRA210_MIXER_TX_LIMIT (TEGRA210_MIXER_RX_LIMIT + (TEGRA210_MIXER_TX_MAX * TEGRA210_MIXER_REG_STRIDE)) +#define TEGRA210_MIXER_SAMPLE_COUNT_SHIFT 24 +#define TEGRA210_MIXER_SAMPLE_COUNT_ENABLE BIT(TEGRA210_MIXER_SAMPLE_COUNT_SHIFT) #define REG_CFG_DONE_TRIGGER 0xf #define VAL_CFG_DONE_TRIGGER 0x1 #define NUM_GAIN_POLY_COEFFS 9 -#define NUM_DURATION_PARMS 4 +#define TEGRA210_MIXER_GAIN_MAX 0x20000 +#define TEGRA210_MIXER_FADE_DURATION_MAX 0x7fffffff + +#define TEGRA210_MIXER_PRESCALAR 6 +#define TEGRA210_MIXER_FADE_IDLE 0 +#define TEGRA210_MIXER_FADE_ACTIVE 1 + +enum { + DURATION_N3_ID = 2, + DURATION_INV_N3_ID, + NUM_DURATION_PARMS, +}; struct tegra210_mixer_gain_params { int poly_coeff[NUM_GAIN_POLY_COEFFS]; @@ -94,6 +107,10 @@ struct tegra210_mixer_gain_params { struct tegra210_mixer { int gain_value[TEGRA210_MIXER_RX_MAX]; + int fade_gain[TEGRA210_MIXER_RX_MAX]; + u32 duration[TEGRA210_MIXER_RX_MAX]; + bool in_fade[TEGRA210_MIXER_RX_MAX]; + bool fade_pending[TEGRA210_MIXER_RX_MAX]; struct regmap *regmap; };