|
@@ -63,6 +63,8 @@ struct wm8962_priv {
|
|
int fll_fref;
|
|
int fll_fref;
|
|
int fll_fout;
|
|
int fll_fout;
|
|
|
|
|
|
|
|
+ u16 dsp2_ena;
|
|
|
|
+
|
|
struct delayed_work mic_work;
|
|
struct delayed_work mic_work;
|
|
struct snd_soc_jack *jack;
|
|
struct snd_soc_jack *jack;
|
|
|
|
|
|
@@ -965,7 +967,7 @@ static const struct wm8962_reg_access {
|
|
[584] = { 0x002D, 0x002D, 0x0000 }, /* R584 - IRQ Debounce */
|
|
[584] = { 0x002D, 0x002D, 0x0000 }, /* R584 - IRQ Debounce */
|
|
[586] = { 0xC000, 0xC000, 0x0000 }, /* R586 - MICINT Source Pol */
|
|
[586] = { 0xC000, 0xC000, 0x0000 }, /* R586 - MICINT Source Pol */
|
|
[768] = { 0x0001, 0x0001, 0x0000 }, /* R768 - DSP2 Power Management */
|
|
[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 */
|
|
[4096] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4096 - Write Sequencer 0 */
|
|
[4097] = { 0x00FF, 0x00FF, 0x0000 }, /* R4097 - Write Sequencer 1 */
|
|
[4097] = { 0x00FF, 0x00FF, 0x0000 }, /* R4097 - Write Sequencer 1 */
|
|
[4098] = { 0x070F, 0x070F, 0x0000 }, /* R4098 - Write Sequencer 2 */
|
|
[4098] = { 0x070F, 0x070F, 0x0000 }, /* R4098 - Write Sequencer 2 */
|
|
@@ -1986,6 +1988,122 @@ static const unsigned int classd_tlv[] = {
|
|
};
|
|
};
|
|
static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
|
|
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
|
|
/* 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.
|
|
* bits and only take effect on the PGA if it is actually powered.
|
|
*/
|
|
*/
|
|
@@ -2144,6 +2262,11 @@ SOC_DOUBLE_R_TLV("EQ4 Volume", WM8962_EQ3, WM8962_EQ23,
|
|
WM8962_EQL_B4_GAIN_SHIFT, 31, 0, eq_tlv),
|
|
WM8962_EQL_B4_GAIN_SHIFT, 31, 0, eq_tlv),
|
|
SOC_DOUBLE_R_TLV("EQ5 Volume", WM8962_EQ3, WM8962_EQ23,
|
|
SOC_DOUBLE_R_TLV("EQ5 Volume", WM8962_EQ3, WM8962_EQ23,
|
|
WM8962_EQL_B5_GAIN_SHIFT, 31, 0, eq_tlv),
|
|
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 const struct snd_kcontrol_new wm8962_spk_mono_controls[] = {
|
|
@@ -2403,6 +2526,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 char *st_text[] = { "None", "Right", "Left" };
|
|
|
|
|
|
static const struct soc_enum str_enum =
|
|
static const struct soc_enum str_enum =
|
|
@@ -2525,6 +2673,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_SUPPLY("Charge Pump", WM8962_CHARGE_PUMP_1, 0, 0, cp_event,
|
|
SND_SOC_DAPM_POST_PMU),
|
|
SND_SOC_DAPM_POST_PMU),
|
|
SND_SOC_DAPM_SUPPLY("TOCLK", WM8962_ADDITIONAL_CONTROL_1, 0, 0, NULL, 0),
|
|
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,
|
|
SND_SOC_DAPM_MIXER("INPGAL", WM8962_LEFT_INPUT_PGA_CONTROL, 4, 0,
|
|
inpgal, ARRAY_SIZE(inpgal)),
|
|
inpgal, ARRAY_SIZE(inpgal)),
|
|
@@ -2620,11 +2771,13 @@ static const struct snd_soc_dapm_route wm8962_intercon[] = {
|
|
{ "ADCL", NULL, "TOCLK" },
|
|
{ "ADCL", NULL, "TOCLK" },
|
|
{ "ADCL", NULL, "MIXINL" },
|
|
{ "ADCL", NULL, "MIXINL" },
|
|
{ "ADCL", NULL, "DMIC" },
|
|
{ "ADCL", NULL, "DMIC" },
|
|
|
|
+ { "ADCL", NULL, "DSP2" },
|
|
|
|
|
|
{ "ADCR", NULL, "SYSCLK" },
|
|
{ "ADCR", NULL, "SYSCLK" },
|
|
{ "ADCR", NULL, "TOCLK" },
|
|
{ "ADCR", NULL, "TOCLK" },
|
|
{ "ADCR", NULL, "MIXINR" },
|
|
{ "ADCR", NULL, "MIXINR" },
|
|
{ "ADCR", NULL, "DMIC" },
|
|
{ "ADCR", NULL, "DMIC" },
|
|
|
|
+ { "ADCR", NULL, "DSP2" },
|
|
|
|
|
|
{ "STL", "Left", "ADCL" },
|
|
{ "STL", "Left", "ADCL" },
|
|
{ "STL", "Right", "ADCR" },
|
|
{ "STL", "Right", "ADCR" },
|
|
@@ -2636,11 +2789,13 @@ static const struct snd_soc_dapm_route wm8962_intercon[] = {
|
|
{ "DACL", NULL, "TOCLK" },
|
|
{ "DACL", NULL, "TOCLK" },
|
|
{ "DACL", NULL, "Beep" },
|
|
{ "DACL", NULL, "Beep" },
|
|
{ "DACL", NULL, "STL" },
|
|
{ "DACL", NULL, "STL" },
|
|
|
|
+ { "DACL", NULL, "DSP2" },
|
|
|
|
|
|
{ "DACR", NULL, "SYSCLK" },
|
|
{ "DACR", NULL, "SYSCLK" },
|
|
{ "DACR", NULL, "TOCLK" },
|
|
{ "DACR", NULL, "TOCLK" },
|
|
{ "DACR", NULL, "Beep" },
|
|
{ "DACR", NULL, "Beep" },
|
|
{ "DACR", NULL, "STR" },
|
|
{ "DACR", NULL, "STR" },
|
|
|
|
+ { "DACR", NULL, "DSP2" },
|
|
|
|
|
|
{ "HPMIXL", "IN4L Switch", "IN4L" },
|
|
{ "HPMIXL", "IN4L Switch", "IN4L" },
|
|
{ "HPMIXL", "IN4R Switch", "IN4R" },
|
|
{ "HPMIXL", "IN4R Switch", "IN4R" },
|