diff options
author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2011-08-12 22:57:18 -0400 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2011-11-29 15:22:00 -0500 |
commit | b00adf76a6fa492c39f8225fc42debc01bbbdc1d (patch) | |
tree | ab0f53480c9a0a4dc57a61ce15bade43f5748fd7 | |
parent | 500fa30ed5795a1d8e8539d0cd81f73b34f831a3 (diff) |
ASoC: Enhance default WM8958 microphone detection
Actively manage the detection rate for microphones with WM8958, providing
improved power consumption and maximising the benefit from the hardware
debounce.
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
-rw-r--r-- | sound/soc/codecs/wm8994.c | 120 | ||||
-rw-r--r-- | sound/soc/codecs/wm8994.h | 2 |
2 files changed, 111 insertions, 11 deletions
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index 207bccd156f1..027bf683efce 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c | |||
@@ -53,6 +53,56 @@ static int wm8994_retune_mobile_base[] = { | |||
53 | WM8994_AIF2_EQ_GAINS_1, | 53 | WM8994_AIF2_EQ_GAINS_1, |
54 | }; | 54 | }; |
55 | 55 | ||
56 | static void wm8958_default_micdet(u16 status, void *data); | ||
57 | |||
58 | static const struct { | ||
59 | int sysclk; | ||
60 | bool idle; | ||
61 | int start; | ||
62 | int rate; | ||
63 | } wm8958_micd_rates[] = { | ||
64 | { 32768, true, 1, 4 }, | ||
65 | { 32768, false, 1, 1 }, | ||
66 | { 44100 * 256, true, 7, 6 }, | ||
67 | { 44100 * 256, false, 7, 6 }, | ||
68 | }; | ||
69 | |||
70 | static void wm8958_micd_set_rate(struct snd_soc_codec *codec) | ||
71 | { | ||
72 | struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); | ||
73 | int best, i, sysclk, val; | ||
74 | bool idle; | ||
75 | |||
76 | if (wm8994->jack_cb != wm8958_default_micdet) | ||
77 | return; | ||
78 | |||
79 | idle = !wm8994->jack_mic; | ||
80 | |||
81 | sysclk = snd_soc_read(codec, WM8994_CLOCKING_1); | ||
82 | if (sysclk & WM8994_SYSCLK_SRC) | ||
83 | sysclk = wm8994->aifclk[1]; | ||
84 | else | ||
85 | sysclk = wm8994->aifclk[0]; | ||
86 | |||
87 | best = 0; | ||
88 | for (i = 0; i < ARRAY_SIZE(wm8958_micd_rates); i++) { | ||
89 | if (wm8958_micd_rates[i].idle != idle) | ||
90 | continue; | ||
91 | if (abs(wm8958_micd_rates[i].sysclk - sysclk) < | ||
92 | abs(wm8958_micd_rates[best].sysclk - sysclk)) | ||
93 | best = i; | ||
94 | else if (wm8958_micd_rates[best].idle != idle) | ||
95 | best = i; | ||
96 | } | ||
97 | |||
98 | val = wm8958_micd_rates[best].start << WM8958_MICD_BIAS_STARTTIME_SHIFT | ||
99 | | wm8958_micd_rates[best].rate << WM8958_MICD_RATE_SHIFT; | ||
100 | |||
101 | snd_soc_update_bits(codec, WM8958_MIC_DETECT_1, | ||
102 | WM8958_MICD_BIAS_STARTTIME_MASK | | ||
103 | WM8958_MICD_RATE_MASK, val); | ||
104 | } | ||
105 | |||
56 | static int wm8994_readable(struct snd_soc_codec *codec, unsigned int reg) | 106 | static int wm8994_readable(struct snd_soc_codec *codec, unsigned int reg) |
57 | { | 107 | { |
58 | struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); | 108 | struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); |
@@ -221,8 +271,10 @@ static int configure_clock(struct snd_soc_codec *codec) | |||
221 | */ | 271 | */ |
222 | 272 | ||
223 | /* If they're equal it doesn't matter which is used */ | 273 | /* If they're equal it doesn't matter which is used */ |
224 | if (wm8994->aifclk[0] == wm8994->aifclk[1]) | 274 | if (wm8994->aifclk[0] == wm8994->aifclk[1]) { |
275 | wm8958_micd_set_rate(codec); | ||
225 | return 0; | 276 | return 0; |
277 | } | ||
226 | 278 | ||
227 | if (wm8994->aifclk[0] < wm8994->aifclk[1]) | 279 | if (wm8994->aifclk[0] < wm8994->aifclk[1]) |
228 | new = WM8994_SYSCLK_SRC; | 280 | new = WM8994_SYSCLK_SRC; |
@@ -236,6 +288,8 @@ static int configure_clock(struct snd_soc_codec *codec) | |||
236 | 288 | ||
237 | snd_soc_dapm_sync(&codec->dapm); | 289 | snd_soc_dapm_sync(&codec->dapm); |
238 | 290 | ||
291 | wm8958_micd_set_rate(codec); | ||
292 | |||
239 | return 0; | 293 | return 0; |
240 | } | 294 | } |
241 | 295 | ||
@@ -2987,21 +3041,56 @@ static void wm8958_default_micdet(u16 status, void *data) | |||
2987 | { | 3041 | { |
2988 | struct snd_soc_codec *codec = data; | 3042 | struct snd_soc_codec *codec = data; |
2989 | struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); | 3043 | struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec); |
2990 | int report = 0; | ||
2991 | 3044 | ||
2992 | /* If nothing present then clear our statuses */ | 3045 | /* If nothing present then clear our statuses */ |
2993 | if (!(status & WM8958_MICD_STS)) | 3046 | if (!(status & WM8958_MICD_STS)) { |
2994 | goto done; | 3047 | dev_dbg(codec->dev, "Detected open circuit\n"); |
3048 | wm8994->jack_mic = false; | ||
3049 | wm8994->detecting = true; | ||
3050 | |||
3051 | wm8958_micd_set_rate(codec); | ||
2995 | 3052 | ||
2996 | report = SND_JACK_MICROPHONE; | 3053 | snd_soc_jack_report(wm8994->micdet[0].jack, 0, |
3054 | SND_JACK_BTN_0 | SND_JACK_HEADSET); | ||
3055 | |||
3056 | return; | ||
3057 | } | ||
2997 | 3058 | ||
2998 | /* Everything else is buttons; just assign slots */ | 3059 | /* If the measurement is showing a high impedence we've got a |
2999 | if (status & 0x1c) | 3060 | * microphone. |
3000 | report |= SND_JACK_BTN_0; | 3061 | */ |
3062 | if (wm8994->detecting && (status & 0x600)) { | ||
3063 | dev_dbg(codec->dev, "Detected microphone\n"); | ||
3064 | |||
3065 | wm8994->detecting = false; | ||
3066 | wm8994->jack_mic = true; | ||
3067 | |||
3068 | wm8958_micd_set_rate(codec); | ||
3069 | |||
3070 | snd_soc_jack_report(wm8994->micdet[0].jack, SND_JACK_HEADSET, | ||
3071 | SND_JACK_HEADSET); | ||
3072 | } | ||
3001 | 3073 | ||
3002 | done: | 3074 | |
3003 | snd_soc_jack_report(wm8994->micdet[0].jack, report, | 3075 | if (wm8994->detecting && status & 0x4) { |
3004 | SND_JACK_BTN_0 | SND_JACK_MICROPHONE); | 3076 | dev_dbg(codec->dev, "Detected headphone\n"); |
3077 | wm8994->detecting = false; | ||
3078 | |||
3079 | wm8958_micd_set_rate(codec); | ||
3080 | |||
3081 | snd_soc_jack_report(wm8994->micdet[0].jack, SND_JACK_HEADPHONE, | ||
3082 | SND_JACK_HEADSET); | ||
3083 | } | ||
3084 | |||
3085 | /* Report short circuit as a button */ | ||
3086 | if (wm8994->jack_mic) { | ||
3087 | if (status & 0x4) | ||
3088 | snd_soc_jack_report(wm8994->micdet[0].jack, | ||
3089 | SND_JACK_BTN_0, SND_JACK_BTN_0); | ||
3090 | else | ||
3091 | snd_soc_jack_report(wm8994->micdet[0].jack, | ||
3092 | 0, SND_JACK_BTN_0); | ||
3093 | } | ||
3005 | } | 3094 | } |
3006 | 3095 | ||
3007 | /** | 3096 | /** |
@@ -3047,6 +3136,15 @@ int wm8958_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, | |||
3047 | wm8994->jack_cb = cb; | 3136 | wm8994->jack_cb = cb; |
3048 | wm8994->jack_cb_data = cb_data; | 3137 | wm8994->jack_cb_data = cb_data; |
3049 | 3138 | ||
3139 | wm8994->detecting = true; | ||
3140 | wm8994->jack_mic = false; | ||
3141 | |||
3142 | wm8958_micd_set_rate(codec); | ||
3143 | |||
3144 | /* Detect microphones and short circuits */ | ||
3145 | snd_soc_update_bits(codec, WM8958_MIC_DETECT_2, | ||
3146 | WM8958_MICD_LVL_SEL_MASK, 0x41); | ||
3147 | |||
3050 | snd_soc_update_bits(codec, WM8958_MIC_DETECT_1, | 3148 | snd_soc_update_bits(codec, WM8958_MIC_DETECT_1, |
3051 | WM8958_MICD_ENA, WM8958_MICD_ENA); | 3149 | WM8958_MICD_ENA, WM8958_MICD_ENA); |
3052 | } else { | 3150 | } else { |
diff --git a/sound/soc/codecs/wm8994.h b/sound/soc/codecs/wm8994.h index f4f1355efc82..1087425cbac0 100644 --- a/sound/soc/codecs/wm8994.h +++ b/sound/soc/codecs/wm8994.h | |||
@@ -126,6 +126,8 @@ struct wm8994_priv { | |||
126 | struct soc_enum enh_eq_enum; | 126 | struct soc_enum enh_eq_enum; |
127 | 127 | ||
128 | struct wm8994_micdet micdet[2]; | 128 | struct wm8994_micdet micdet[2]; |
129 | bool detecting; | ||
130 | bool jack_mic; | ||
129 | 131 | ||
130 | wm8958_micdet_cb jack_cb; | 132 | wm8958_micdet_cb jack_cb; |
131 | void *jack_cb_data; | 133 | void *jack_cb_data; |