[alsa-devel] [PATCH] ASoC: Initial WM8962 DSP2 support
Liam Girdwood
lrg at ti.com
Mon Aug 22 12:24:54 CEST 2011
On 21/08/11 12:52, Mark Brown wrote:
> The WM8962 features a DSP providing a number of signal processing
> features including HD Bass and Virtual Surround Sound (VSS). Enable
> initial support for this, allowing users to enable and disable the
> algorithms using the default coefficient sets. Further patches will
> add support for runtime configuration of the DSP coefficients.
>
> Signed-off-by: Mark Brown <broonie at opensource.wolfsonmicro.com>
> ---
> sound/soc/codecs/wm8962.c | 157 ++++++++++++++++++++++++++++++++++++++++++++-
> 1 files changed, 156 insertions(+), 1 deletions(-)
>
> diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
> index 8526383..c9e07aa 100644
> --- a/sound/soc/codecs/wm8962.c
> +++ b/sound/soc/codecs/wm8962.c
> @@ -63,6 +63,8 @@ struct wm8962_priv {
> int fll_fref;
> int fll_fout;
>
> + u16 dsp2_ena;
> +
> struct delayed_work mic_work;
> struct snd_soc_jack *jack;
>
> @@ -967,7 +969,7 @@ static const struct wm8962_reg_access {
> [584] = { 0x002D, 0x002D, 0x0000 }, /* R584 - IRQ Debounce */
> [586] = { 0xC000, 0xC000, 0x0000 }, /* R586 - MICINT Source Pol */
> [768] = { 0x0001, 0x0001, 0x0000 }, /* R768 - DSP2 Power Management */
> - [1037] = { 0x0000, 0x003F, 0x0000 }, /* R1037 - DSP2_ExecControl */
> + [1037] = { 0x0000, 0x003F, 0xFFFF }, /* R1037 - DSP2_ExecControl */
> [4096] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4096 - Write Sequencer 0 */
> [4097] = { 0x00FF, 0x00FF, 0x0000 }, /* R4097 - Write Sequencer 1 */
> [4098] = { 0x070F, 0x070F, 0x0000 }, /* R4098 - Write Sequencer 2 */
> @@ -1988,6 +1990,122 @@ static const unsigned int classd_tlv[] = {
> };
> static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
>
> +static int wm8962_dsp2_write_config(struct snd_soc_codec *codec)
> +{
> + return 0;
> +}
> +
> +static int wm8962_dsp2_set_enable(struct snd_soc_codec *codec, u16 val)
> +{
> + u16 adcl = snd_soc_read(codec, WM8962_LEFT_ADC_VOLUME);
> + u16 adcr = snd_soc_read(codec, WM8962_RIGHT_ADC_VOLUME);
> + u16 dac = snd_soc_read(codec, WM8962_ADC_DAC_CONTROL_1);
> +
> + /* Mute the ADCs and DACs */
> + snd_soc_write(codec, WM8962_LEFT_ADC_VOLUME, 0);
> + snd_soc_write(codec, WM8962_RIGHT_ADC_VOLUME, WM8962_ADC_VU);
> + snd_soc_update_bits(codec, WM8962_ADC_DAC_CONTROL_1,
> + WM8962_DAC_MUTE, WM8962_DAC_MUTE);
> +
> + snd_soc_write(codec, WM8962_SOUNDSTAGE_ENABLES_0, val);
> +
> + /* Restore the ADCs and DACs */
> + snd_soc_write(codec, WM8962_LEFT_ADC_VOLUME, adcl);
> + snd_soc_write(codec, WM8962_RIGHT_ADC_VOLUME, adcr);
> + snd_soc_update_bits(codec, WM8962_ADC_DAC_CONTROL_1,
> + WM8962_DAC_MUTE, dac);
> +
> + return 0;
> +}
> +
> +static int wm8962_dsp2_start(struct snd_soc_codec *codec)
> +{
> + struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
> +
> + wm8962_dsp2_write_config(codec);
> +
> + snd_soc_write(codec, WM8962_DSP2_EXECCONTROL, WM8962_DSP2_RUNR);
> +
> + wm8962_dsp2_set_enable(codec, wm8962->dsp2_ena);
> +
> + return 0;
> +}
> +
> +static int wm8962_dsp2_stop(struct snd_soc_codec *codec)
> +{
> + wm8962_dsp2_set_enable(codec, 0);
> +
> + snd_soc_write(codec, WM8962_DSP2_EXECCONTROL, WM8962_DSP2_STOP);
> +
> + return 0;
> +}
> +
> +#define WM8962_DSP2_ENABLE(xname, xshift) \
> +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
> + .info = wm8962_dsp2_ena_info, \
> + .get = wm8962_dsp2_ena_get, .put = wm8962_dsp2_ena_put, \
> + .private_value = xshift }
> +
> +static int wm8962_dsp2_ena_info(struct snd_kcontrol *kcontrol,
> + struct snd_ctl_elem_info *uinfo)
> +{
> + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
> +
> + uinfo->count = 1;
> + uinfo->value.integer.min = 0;
> + uinfo->value.integer.max = 1;
> +
> + return 0;
> +}
> +
> +static int wm8962_dsp2_ena_get(struct snd_kcontrol *kcontrol,
> + struct snd_ctl_elem_value *ucontrol)
> +{
> + int shift = kcontrol->private_value;
> + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
> + struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
> +
> + ucontrol->value.integer.value[0] = !!(wm8962->dsp2_ena & 1 << shift);
> +
> + return 0;
> +}
> +
> +static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol,
> + struct snd_ctl_elem_value *ucontrol)
> +{
> + int shift = kcontrol->private_value;
> + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
> + struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
> + int old = wm8962->dsp2_ena;
> + int ret = 0;
> + int dsp2_running = snd_soc_read(codec, WM8962_DSP2_POWER_MANAGEMENT) &
> + WM8962_DSP2_ENA;
> +
> + mutex_lock(&codec->mutex);
> +
> + if (ucontrol->value.integer.value[0])
> + wm8962->dsp2_ena |= 1 << shift;
> + else
> + wm8962->dsp2_ena &= ~(1 << shift);
> +
> + if (wm8962->dsp2_ena == old)
> + goto out;
> +
> + ret = 1;
> +
> + if (dsp2_running) {
> + if (wm8962->dsp2_ena)
> + wm8962_dsp2_set_enable(codec, wm8962->dsp2_ena);
> + else
> + wm8962_dsp2_stop(codec);
> + }
> +
> +out:
> + mutex_unlock(&codec->mutex);
> +
> + return ret;
> +}
> +
> /* The VU bits for the headphones are in a different register to the mute
> * bits and only take effect on the PGA if it is actually powered.
> */
> @@ -2146,6 +2264,11 @@ SOC_DOUBLE_R_TLV("EQ4 Volume", WM8962_EQ3, WM8962_EQ23,
> WM8962_EQL_B4_GAIN_SHIFT, 31, 0, eq_tlv),
> SOC_DOUBLE_R_TLV("EQ5 Volume", WM8962_EQ3, WM8962_EQ23,
> WM8962_EQL_B5_GAIN_SHIFT, 31, 0, eq_tlv),
> +
> +WM8962_DSP2_ENABLE("VSS Switch", WM8962_VSS_ENA_SHIFT),
> +WM8962_DSP2_ENABLE("HPF1 Switch", WM8962_HPF1_ENA_SHIFT),
> +WM8962_DSP2_ENABLE("HPF2 Switch", WM8962_HPF2_ENA_SHIFT),
> +WM8962_DSP2_ENABLE("HD Bass Switch", WM8962_HDBASS_ENA_SHIFT),
> };
>
> static const struct snd_kcontrol_new wm8962_spk_mono_controls[] = {
> @@ -2405,6 +2528,31 @@ static int out_pga_event(struct snd_soc_dapm_widget *w,
> }
> }
>
> +static int dsp2_event(struct snd_soc_dapm_widget *w,
> + struct snd_kcontrol *kcontrol, int event)
> +{
> + struct snd_soc_codec *codec = w->codec;
> + struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
> +
> + switch (event) {
> + case SND_SOC_DAPM_POST_PMU:
> + if (wm8962->dsp2_ena)
> + wm8962_dsp2_start(codec);
> + break;
> +
> + case SND_SOC_DAPM_PRE_PMD:
> + if (wm8962->dsp2_ena)
> + wm8962_dsp2_stop(codec);
> + break;
> +
> + default:
> + BUG();
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> static const char *st_text[] = { "None", "Right", "Left" };
>
> static const struct soc_enum str_enum =
> @@ -2527,6 +2675,9 @@ SND_SOC_DAPM_SUPPLY("SYSCLK", WM8962_CLOCKING2, 5, 0, sysclk_event,
> SND_SOC_DAPM_SUPPLY("Charge Pump", WM8962_CHARGE_PUMP_1, 0, 0, cp_event,
> SND_SOC_DAPM_POST_PMU),
> SND_SOC_DAPM_SUPPLY("TOCLK", WM8962_ADDITIONAL_CONTROL_1, 0, 0, NULL, 0),
> +SND_SOC_DAPM_SUPPLY_S("DSP2", 1, WM8962_DSP2_POWER_MANAGEMENT,
> + WM8962_DSP2_ENA_SHIFT, 0, dsp2_event,
> + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
>
> SND_SOC_DAPM_MIXER("INPGAL", WM8962_LEFT_INPUT_PGA_CONTROL, 4, 0,
> inpgal, ARRAY_SIZE(inpgal)),
> @@ -2622,11 +2773,13 @@ static const struct snd_soc_dapm_route wm8962_intercon[] = {
> { "ADCL", NULL, "TOCLK" },
> { "ADCL", NULL, "MIXINL" },
> { "ADCL", NULL, "DMIC" },
> + { "ADCL", NULL, "DSP2" },
>
> { "ADCR", NULL, "SYSCLK" },
> { "ADCR", NULL, "TOCLK" },
> { "ADCR", NULL, "MIXINR" },
> { "ADCR", NULL, "DMIC" },
> + { "ADCR", NULL, "DSP2" },
>
> { "STL", "Left", "ADCL" },
> { "STL", "Right", "ADCR" },
> @@ -2638,11 +2791,13 @@ static const struct snd_soc_dapm_route wm8962_intercon[] = {
> { "DACL", NULL, "TOCLK" },
> { "DACL", NULL, "Beep" },
> { "DACL", NULL, "STL" },
> + { "DACL", NULL, "DSP2" },
>
> { "DACR", NULL, "SYSCLK" },
> { "DACR", NULL, "TOCLK" },
> { "DACR", NULL, "Beep" },
> { "DACR", NULL, "STR" },
> + { "DACR", NULL, "DSP2" },
>
> { "HPMIXL", "IN4L Switch", "IN4L" },
> { "HPMIXL", "IN4R Switch", "IN4R" },
Acked-by: Liam Girdwood <lrg at ti.com>
More information about the Alsa-devel
mailing list