aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2011-08-16 21:03:51 -0400
committerMark Brown <broonie@opensource.wolfsonmicro.com>2011-08-22 07:41:56 -0400
commit6f88a4e5785fbf4db9a2c7e16670e1f19e6566d2 (patch)
tree6d7c3d25dbb9ffa237012ed62c6d31fffc428662
parent1ab63da7212d4422cbc40d4ead5cff97f6050a50 (diff)
ASoC: Initial WM8962 DSP2 support
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@opensource.wolfsonmicro.com> Acked-by: Liam Girdwood <lrg@ti.com>
-rw-r--r--sound/soc/codecs/wm8962.c157
1 files changed, 156 insertions, 1 deletions
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index 5538737c88ce..75e784053603 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -63,6 +63,8 @@ struct wm8962_priv {
63 int fll_fref; 63 int fll_fref;
64 int fll_fout; 64 int fll_fout;
65 65
66 u16 dsp2_ena;
67
66 struct delayed_work mic_work; 68 struct delayed_work mic_work;
67 struct snd_soc_jack *jack; 69 struct snd_soc_jack *jack;
68 70
@@ -965,7 +967,7 @@ static const struct wm8962_reg_access {
965 [584] = { 0x002D, 0x002D, 0x0000 }, /* R584 - IRQ Debounce */ 967 [584] = { 0x002D, 0x002D, 0x0000 }, /* R584 - IRQ Debounce */
966 [586] = { 0xC000, 0xC000, 0x0000 }, /* R586 - MICINT Source Pol */ 968 [586] = { 0xC000, 0xC000, 0x0000 }, /* R586 - MICINT Source Pol */
967 [768] = { 0x0001, 0x0001, 0x0000 }, /* R768 - DSP2 Power Management */ 969 [768] = { 0x0001, 0x0001, 0x0000 }, /* R768 - DSP2 Power Management */
968 [1037] = { 0x0000, 0x003F, 0x0000 }, /* R1037 - DSP2_ExecControl */ 970 [1037] = { 0x0000, 0x003F, 0xFFFF }, /* R1037 - DSP2_ExecControl */
969 [4096] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4096 - Write Sequencer 0 */ 971 [4096] = { 0x3FFF, 0x3FFF, 0x0000 }, /* R4096 - Write Sequencer 0 */
970 [4097] = { 0x00FF, 0x00FF, 0x0000 }, /* R4097 - Write Sequencer 1 */ 972 [4097] = { 0x00FF, 0x00FF, 0x0000 }, /* R4097 - Write Sequencer 1 */
971 [4098] = { 0x070F, 0x070F, 0x0000 }, /* R4098 - Write Sequencer 2 */ 973 [4098] = { 0x070F, 0x070F, 0x0000 }, /* R4098 - Write Sequencer 2 */
@@ -1986,6 +1988,122 @@ static const unsigned int classd_tlv[] = {
1986}; 1988};
1987static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0); 1989static const DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
1988 1990
1991static int wm8962_dsp2_write_config(struct snd_soc_codec *codec)
1992{
1993 return 0;
1994}
1995
1996static int wm8962_dsp2_set_enable(struct snd_soc_codec *codec, u16 val)
1997{
1998 u16 adcl = snd_soc_read(codec, WM8962_LEFT_ADC_VOLUME);
1999 u16 adcr = snd_soc_read(codec, WM8962_RIGHT_ADC_VOLUME);
2000 u16 dac = snd_soc_read(codec, WM8962_ADC_DAC_CONTROL_1);
2001
2002 /* Mute the ADCs and DACs */
2003 snd_soc_write(codec, WM8962_LEFT_ADC_VOLUME, 0);
2004 snd_soc_write(codec, WM8962_RIGHT_ADC_VOLUME, WM8962_ADC_VU);
2005 snd_soc_update_bits(codec, WM8962_ADC_DAC_CONTROL_1,
2006 WM8962_DAC_MUTE, WM8962_DAC_MUTE);
2007
2008 snd_soc_write(codec, WM8962_SOUNDSTAGE_ENABLES_0, val);
2009
2010 /* Restore the ADCs and DACs */
2011 snd_soc_write(codec, WM8962_LEFT_ADC_VOLUME, adcl);
2012 snd_soc_write(codec, WM8962_RIGHT_ADC_VOLUME, adcr);
2013 snd_soc_update_bits(codec, WM8962_ADC_DAC_CONTROL_1,
2014 WM8962_DAC_MUTE, dac);
2015
2016 return 0;
2017}
2018
2019static int wm8962_dsp2_start(struct snd_soc_codec *codec)
2020{
2021 struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
2022
2023 wm8962_dsp2_write_config(codec);
2024
2025 snd_soc_write(codec, WM8962_DSP2_EXECCONTROL, WM8962_DSP2_RUNR);
2026
2027 wm8962_dsp2_set_enable(codec, wm8962->dsp2_ena);
2028
2029 return 0;
2030}
2031
2032static int wm8962_dsp2_stop(struct snd_soc_codec *codec)
2033{
2034 wm8962_dsp2_set_enable(codec, 0);
2035
2036 snd_soc_write(codec, WM8962_DSP2_EXECCONTROL, WM8962_DSP2_STOP);
2037
2038 return 0;
2039}
2040
2041#define WM8962_DSP2_ENABLE(xname, xshift) \
2042{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
2043 .info = wm8962_dsp2_ena_info, \
2044 .get = wm8962_dsp2_ena_get, .put = wm8962_dsp2_ena_put, \
2045 .private_value = xshift }
2046
2047static int wm8962_dsp2_ena_info(struct snd_kcontrol *kcontrol,
2048 struct snd_ctl_elem_info *uinfo)
2049{
2050 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
2051
2052 uinfo->count = 1;
2053 uinfo->value.integer.min = 0;
2054 uinfo->value.integer.max = 1;
2055
2056 return 0;
2057}
2058
2059static int wm8962_dsp2_ena_get(struct snd_kcontrol *kcontrol,
2060 struct snd_ctl_elem_value *ucontrol)
2061{
2062 int shift = kcontrol->private_value;
2063 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
2064 struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
2065
2066 ucontrol->value.integer.value[0] = !!(wm8962->dsp2_ena & 1 << shift);
2067
2068 return 0;
2069}
2070
2071static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol,
2072 struct snd_ctl_elem_value *ucontrol)
2073{
2074 int shift = kcontrol->private_value;
2075 struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
2076 struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
2077 int old = wm8962->dsp2_ena;
2078 int ret = 0;
2079 int dsp2_running = snd_soc_read(codec, WM8962_DSP2_POWER_MANAGEMENT) &
2080 WM8962_DSP2_ENA;
2081
2082 mutex_lock(&codec->mutex);
2083
2084 if (ucontrol->value.integer.value[0])
2085 wm8962->dsp2_ena |= 1 << shift;
2086 else
2087 wm8962->dsp2_ena &= ~(1 << shift);
2088
2089 if (wm8962->dsp2_ena == old)
2090 goto out;
2091
2092 ret = 1;
2093
2094 if (dsp2_running) {
2095 if (wm8962->dsp2_ena)
2096 wm8962_dsp2_set_enable(codec, wm8962->dsp2_ena);
2097 else
2098 wm8962_dsp2_stop(codec);
2099 }
2100
2101out:
2102 mutex_unlock(&codec->mutex);
2103
2104 return ret;
2105}
2106
1989/* The VU bits for the headphones are in a different register to the mute 2107/* The VU bits for the headphones are in a different register to the mute
1990 * bits and only take effect on the PGA if it is actually powered. 2108 * bits and only take effect on the PGA if it is actually powered.
1991 */ 2109 */
@@ -2144,6 +2262,11 @@ SOC_DOUBLE_R_TLV("EQ4 Volume", WM8962_EQ3, WM8962_EQ23,
2144 WM8962_EQL_B4_GAIN_SHIFT, 31, 0, eq_tlv), 2262 WM8962_EQL_B4_GAIN_SHIFT, 31, 0, eq_tlv),
2145SOC_DOUBLE_R_TLV("EQ5 Volume", WM8962_EQ3, WM8962_EQ23, 2263SOC_DOUBLE_R_TLV("EQ5 Volume", WM8962_EQ3, WM8962_EQ23,
2146 WM8962_EQL_B5_GAIN_SHIFT, 31, 0, eq_tlv), 2264 WM8962_EQL_B5_GAIN_SHIFT, 31, 0, eq_tlv),
2265
2266WM8962_DSP2_ENABLE("VSS Switch", WM8962_VSS_ENA_SHIFT),
2267WM8962_DSP2_ENABLE("HPF1 Switch", WM8962_HPF1_ENA_SHIFT),
2268WM8962_DSP2_ENABLE("HPF2 Switch", WM8962_HPF2_ENA_SHIFT),
2269WM8962_DSP2_ENABLE("HD Bass Switch", WM8962_HDBASS_ENA_SHIFT),
2147}; 2270};
2148 2271
2149static const struct snd_kcontrol_new wm8962_spk_mono_controls[] = { 2272static const struct snd_kcontrol_new wm8962_spk_mono_controls[] = {
@@ -2403,6 +2526,31 @@ static int out_pga_event(struct snd_soc_dapm_widget *w,
2403 } 2526 }
2404} 2527}
2405 2528
2529static int dsp2_event(struct snd_soc_dapm_widget *w,
2530 struct snd_kcontrol *kcontrol, int event)
2531{
2532 struct snd_soc_codec *codec = w->codec;
2533 struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec);
2534
2535 switch (event) {
2536 case SND_SOC_DAPM_POST_PMU:
2537 if (wm8962->dsp2_ena)
2538 wm8962_dsp2_start(codec);
2539 break;
2540
2541 case SND_SOC_DAPM_PRE_PMD:
2542 if (wm8962->dsp2_ena)
2543 wm8962_dsp2_stop(codec);
2544 break;
2545
2546 default:
2547 BUG();
2548 return -EINVAL;
2549 }
2550
2551 return 0;
2552}
2553
2406static const char *st_text[] = { "None", "Right", "Left" }; 2554static const char *st_text[] = { "None", "Right", "Left" };
2407 2555
2408static const struct soc_enum str_enum = 2556static const struct soc_enum str_enum =
@@ -2525,6 +2673,9 @@ SND_SOC_DAPM_SUPPLY("SYSCLK", WM8962_CLOCKING2, 5, 0, sysclk_event,
2525SND_SOC_DAPM_SUPPLY("Charge Pump", WM8962_CHARGE_PUMP_1, 0, 0, cp_event, 2673SND_SOC_DAPM_SUPPLY("Charge Pump", WM8962_CHARGE_PUMP_1, 0, 0, cp_event,
2526 SND_SOC_DAPM_POST_PMU), 2674 SND_SOC_DAPM_POST_PMU),
2527SND_SOC_DAPM_SUPPLY("TOCLK", WM8962_ADDITIONAL_CONTROL_1, 0, 0, NULL, 0), 2675SND_SOC_DAPM_SUPPLY("TOCLK", WM8962_ADDITIONAL_CONTROL_1, 0, 0, NULL, 0),
2676SND_SOC_DAPM_SUPPLY_S("DSP2", 1, WM8962_DSP2_POWER_MANAGEMENT,
2677 WM8962_DSP2_ENA_SHIFT, 0, dsp2_event,
2678 SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
2528 2679
2529SND_SOC_DAPM_MIXER("INPGAL", WM8962_LEFT_INPUT_PGA_CONTROL, 4, 0, 2680SND_SOC_DAPM_MIXER("INPGAL", WM8962_LEFT_INPUT_PGA_CONTROL, 4, 0,
2530 inpgal, ARRAY_SIZE(inpgal)), 2681 inpgal, ARRAY_SIZE(inpgal)),
@@ -2620,11 +2771,13 @@ static const struct snd_soc_dapm_route wm8962_intercon[] = {
2620 { "ADCL", NULL, "TOCLK" }, 2771 { "ADCL", NULL, "TOCLK" },
2621 { "ADCL", NULL, "MIXINL" }, 2772 { "ADCL", NULL, "MIXINL" },
2622 { "ADCL", NULL, "DMIC" }, 2773 { "ADCL", NULL, "DMIC" },
2774 { "ADCL", NULL, "DSP2" },
2623 2775
2624 { "ADCR", NULL, "SYSCLK" }, 2776 { "ADCR", NULL, "SYSCLK" },
2625 { "ADCR", NULL, "TOCLK" }, 2777 { "ADCR", NULL, "TOCLK" },
2626 { "ADCR", NULL, "MIXINR" }, 2778 { "ADCR", NULL, "MIXINR" },
2627 { "ADCR", NULL, "DMIC" }, 2779 { "ADCR", NULL, "DMIC" },
2780 { "ADCR", NULL, "DSP2" },
2628 2781
2629 { "STL", "Left", "ADCL" }, 2782 { "STL", "Left", "ADCL" },
2630 { "STL", "Right", "ADCR" }, 2783 { "STL", "Right", "ADCR" },
@@ -2636,11 +2789,13 @@ static const struct snd_soc_dapm_route wm8962_intercon[] = {
2636 { "DACL", NULL, "TOCLK" }, 2789 { "DACL", NULL, "TOCLK" },
2637 { "DACL", NULL, "Beep" }, 2790 { "DACL", NULL, "Beep" },
2638 { "DACL", NULL, "STL" }, 2791 { "DACL", NULL, "STL" },
2792 { "DACL", NULL, "DSP2" },
2639 2793
2640 { "DACR", NULL, "SYSCLK" }, 2794 { "DACR", NULL, "SYSCLK" },
2641 { "DACR", NULL, "TOCLK" }, 2795 { "DACR", NULL, "TOCLK" },
2642 { "DACR", NULL, "Beep" }, 2796 { "DACR", NULL, "Beep" },
2643 { "DACR", NULL, "STR" }, 2797 { "DACR", NULL, "STR" },
2798 { "DACR", NULL, "DSP2" },
2644 2799
2645 { "HPMIXL", "IN4L Switch", "IN4L" }, 2800 { "HPMIXL", "IN4L Switch", "IN4L" },
2646 { "HPMIXL", "IN4R Switch", "IN4R" }, 2801 { "HPMIXL", "IN4R Switch", "IN4R" },