int fll_fref;
int fll_fout;
+ u16 dsp2_ena;
+
struct delayed_work mic_work;
struct snd_soc_jack *jack;
[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 */
};
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.
*/
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[] = {
}
}
+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 =
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)),
{ "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" },
{ "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" },