diff options
Diffstat (limited to 'sound/soc/codecs/wm5100.c')
-rw-r--r-- | sound/soc/codecs/wm5100.c | 162 |
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 | ||
2122 | static 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 | |||
2142 | static 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 | |||
2230 | int 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 | |||
2116 | static irqreturn_t wm5100_irq(int irq, void *data) | 2275 | static 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", |