aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/wm5100.c
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2011-09-27 12:39:50 -0400
committerMark Brown <broonie@opensource.wolfsonmicro.com>2011-10-11 13:59:01 -0400
commitba896ede9a9a54a9114ee2a4fe534328078c6b02 (patch)
tree887d21c0965bf7af4a5d711ad697aac45c831e18 /sound/soc/codecs/wm5100.c
parent3a53d827292b657afcb73495cac139371cb157e1 (diff)
ASoC: Implement WM5100 accessory detection support
The WM5100 includes an advanced, low power, accessory detect subsystem capable of detecting both accessory presence and button presses while the device is in an ultra low power mode. Implement initial support for this. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/codecs/wm5100.c')
-rw-r--r--sound/soc/codecs/wm5100.c162
1 files changed, 162 insertions, 0 deletions
diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c
index 8d90ba9c1f5f..02c011d7512e 100644
--- a/sound/soc/codecs/wm5100.c
+++ b/sound/soc/codecs/wm5100.c
@@ -26,6 +26,7 @@
26#include <sound/pcm.h> 26#include <sound/pcm.h>
27#include <sound/pcm_params.h> 27#include <sound/pcm_params.h>
28#include <sound/soc.h> 28#include <sound/soc.h>
29#include <sound/jack.h>
29#include <sound/initval.h> 30#include <sound/initval.h>
30#include <sound/tlv.h> 31#include <sound/tlv.h>
31#include <sound/wm5100.h> 32#include <sound/wm5100.h>
@@ -68,6 +69,11 @@ struct wm5100_priv {
68 69
69 bool out_ena[2]; 70 bool out_ena[2];
70 71
72 struct snd_soc_jack *jack;
73 bool jack_detecting;
74 bool jack_mic;
75 int jack_mode;
76
71 struct wm5100_fll fll[2]; 77 struct wm5100_fll fll[2];
72 78
73 struct wm5100_pdata pdata; 79 struct wm5100_pdata pdata;
@@ -2113,6 +2119,159 @@ static int wm5100_dig_vu[] = {
2113 WM5100_DAC_DIGITAL_VOLUME_6R, 2119 WM5100_DAC_DIGITAL_VOLUME_6R,
2114}; 2120};
2115 2121
2122static void wm5100_set_detect_mode(struct snd_soc_codec *codec, int the_mode)
2123{
2124 struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec);
2125 struct wm5100_jack_mode *mode = &wm5100->pdata.jack_modes[the_mode];
2126
2127 BUG_ON(the_mode >= ARRAY_SIZE(wm5100->pdata.jack_modes));
2128
2129 gpio_set_value_cansleep(wm5100->pdata.hp_pol, mode->hp_pol);
2130 snd_soc_update_bits(codec, WM5100_ACCESSORY_DETECT_MODE_1,
2131 WM5100_ACCDET_BIAS_SRC_MASK |
2132 WM5100_ACCDET_SRC,
2133 (mode->bias << WM5100_ACCDET_BIAS_SRC_SHIFT) |
2134 mode->micd_src << WM5100_ACCDET_SRC_SHIFT);
2135
2136 wm5100->jack_mode = the_mode;
2137
2138 dev_dbg(codec->dev, "Set microphone polarity to %d\n",
2139 wm5100->jack_mode);
2140}
2141
2142static void wm5100_micd_irq(struct snd_soc_codec *codec)
2143{
2144 struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec);
2145 int val;
2146
2147 val = snd_soc_read(codec, WM5100_MIC_DETECT_3);
2148
2149 dev_dbg(codec->dev, "Microphone event: %x\n", val);
2150
2151 if (!(val & WM5100_ACCDET_VALID)) {
2152 dev_warn(codec->dev, "Microphone detection state invalid\n");
2153 return;
2154 }
2155
2156 /* No accessory, reset everything and report removal */
2157 if (!(val & WM5100_ACCDET_STS)) {
2158 dev_dbg(codec->dev, "Jack removal detected\n");
2159 wm5100->jack_mic = false;
2160 wm5100->jack_detecting = true;
2161 snd_soc_jack_report(wm5100->jack, 0,
2162 SND_JACK_LINEOUT | SND_JACK_HEADSET |
2163 SND_JACK_BTN_0);
2164
2165 snd_soc_update_bits(codec, WM5100_MIC_DETECT_1,
2166 WM5100_ACCDET_RATE_MASK,
2167 WM5100_ACCDET_RATE_MASK);
2168 return;
2169 }
2170
2171 /* If the measurement is very high we've got a microphone,
2172 * either we just detected one or if we already reported then
2173 * we've got a button release event.
2174 */
2175 if (val & 0x400) {
2176 if (wm5100->jack_detecting) {
2177 dev_dbg(codec->dev, "Microphone detected\n");
2178 wm5100->jack_mic = true;
2179 snd_soc_jack_report(wm5100->jack,
2180 SND_JACK_HEADSET,
2181 SND_JACK_HEADSET | SND_JACK_BTN_0);
2182
2183 /* Increase poll rate to give better responsiveness
2184 * for buttons */
2185 snd_soc_update_bits(codec, WM5100_MIC_DETECT_1,
2186 WM5100_ACCDET_RATE_MASK,
2187 5 << WM5100_ACCDET_RATE_SHIFT);
2188 } else {
2189 dev_dbg(codec->dev, "Mic button up\n");
2190 snd_soc_jack_report(wm5100->jack, 0, SND_JACK_BTN_0);
2191 }
2192
2193 return;
2194 }
2195
2196 /* If we detected a lower impedence during initial startup
2197 * then we probably have the wrong polarity, flip it. Don't
2198 * do this for the lowest impedences to speed up detection of
2199 * plain headphones.
2200 */
2201 if (wm5100->jack_detecting && (val & 0x3f8)) {
2202 wm5100_set_detect_mode(codec, !wm5100->jack_mode);
2203
2204 return;
2205 }
2206
2207 /* Don't distinguish between buttons, just report any low
2208 * impedence as BTN_0.
2209 */
2210 if (val & 0x3fc) {
2211 if (wm5100->jack_mic) {
2212 dev_dbg(codec->dev, "Mic button detected\n");
2213 snd_soc_jack_report(wm5100->jack, SND_JACK_BTN_0,
2214 SND_JACK_BTN_0);
2215 } else if (wm5100->jack_detecting) {
2216 dev_dbg(codec->dev, "Headphone detected\n");
2217 snd_soc_jack_report(wm5100->jack, SND_JACK_HEADPHONE,
2218 SND_JACK_HEADPHONE);
2219
2220 /* Increase the detection rate a bit for
2221 * responsiveness.
2222 */
2223 snd_soc_update_bits(codec, WM5100_MIC_DETECT_1,
2224 WM5100_ACCDET_RATE_MASK,
2225 7 << WM5100_ACCDET_RATE_SHIFT);
2226 }
2227 }
2228}
2229
2230int wm5100_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
2231{
2232 struct wm5100_priv *wm5100 = snd_soc_codec_get_drvdata(codec);
2233
2234 if (jack) {
2235 wm5100->jack = jack;
2236 wm5100->jack_detecting = true;
2237
2238 wm5100_set_detect_mode(codec, 0);
2239
2240 /* Slowest detection rate, gives debounce for initial
2241 * detection */
2242 snd_soc_update_bits(codec, WM5100_MIC_DETECT_1,
2243 WM5100_ACCDET_BIAS_STARTTIME_MASK |
2244 WM5100_ACCDET_RATE_MASK,
2245 (7 << WM5100_ACCDET_BIAS_STARTTIME_SHIFT) |
2246 WM5100_ACCDET_RATE_MASK);
2247
2248 /* We need the charge pump to power MICBIAS */
2249 snd_soc_dapm_force_enable_pin(&codec->dapm, "CP2");
2250 snd_soc_dapm_force_enable_pin(&codec->dapm, "SYSCLK");
2251 snd_soc_dapm_sync(&codec->dapm);
2252
2253 /* We start off just enabling microphone detection - even a
2254 * plain headphone will trigger detection.
2255 */
2256 snd_soc_update_bits(codec, WM5100_MIC_DETECT_1,
2257 WM5100_ACCDET_ENA, WM5100_ACCDET_ENA);
2258
2259 snd_soc_update_bits(codec, WM5100_INTERRUPT_STATUS_3_MASK,
2260 WM5100_IM_ACCDET_EINT, 0);
2261 } else {
2262 snd_soc_update_bits(codec, WM5100_INTERRUPT_STATUS_3_MASK,
2263 WM5100_IM_HPDET_EINT |
2264 WM5100_IM_ACCDET_EINT,
2265 WM5100_IM_HPDET_EINT |
2266 WM5100_IM_ACCDET_EINT);
2267 snd_soc_update_bits(codec, WM5100_MIC_DETECT_1,
2268 WM5100_ACCDET_ENA, 0);
2269 wm5100->jack = NULL;
2270 }
2271
2272 return 0;
2273}
2274
2116static irqreturn_t wm5100_irq(int irq, void *data) 2275static irqreturn_t wm5100_irq(int irq, void *data)
2117{ 2276{
2118 struct snd_soc_codec *codec = data; 2277 struct snd_soc_codec *codec = data;
@@ -2144,6 +2303,9 @@ static irqreturn_t wm5100_irq(int irq, void *data)
2144 complete(&wm5100->fll[1].lock); 2303 complete(&wm5100->fll[1].lock);
2145 } 2304 }
2146 2305
2306 if (irq_val & WM5100_ACCDET_EINT)
2307 wm5100_micd_irq(codec);
2308
2147 irq_val = snd_soc_read(codec, WM5100_INTERRUPT_STATUS_4); 2309 irq_val = snd_soc_read(codec, WM5100_INTERRUPT_STATUS_4);
2148 if (irq_val < 0) { 2310 if (irq_val < 0) {
2149 dev_err(codec->dev, "Failed to read IRQ status 4: %d\n", 2311 dev_err(codec->dev, "Failed to read IRQ status 4: %d\n",