aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc')
-rw-r--r--sound/soc/codecs/wm8996.c137
1 files changed, 112 insertions, 25 deletions
diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c
index c584e3e6a6fe..cd1ba9637c01 100644
--- a/sound/soc/codecs/wm8996.c
+++ b/sound/soc/codecs/wm8996.c
@@ -2350,12 +2350,94 @@ int wm8996_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack,
2350 2350
2351 /* Enable interrupts and we're off */ 2351 /* Enable interrupts and we're off */
2352 snd_soc_update_bits(codec, WM8996_INTERRUPT_STATUS_2_MASK, 2352 snd_soc_update_bits(codec, WM8996_INTERRUPT_STATUS_2_MASK,
2353 WM8996_IM_MICD_EINT, 0); 2353 WM8996_IM_MICD_EINT | WM8996_HP_DONE_EINT, 0);
2354 2354
2355 return 0; 2355 return 0;
2356} 2356}
2357EXPORT_SYMBOL_GPL(wm8996_detect); 2357EXPORT_SYMBOL_GPL(wm8996_detect);
2358 2358
2359static void wm8996_hpdet_irq(struct snd_soc_codec *codec)
2360{
2361 struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
2362 int val, reg, report;
2363
2364 /* Assume headphone in error conditions; we need to report
2365 * something or we stall our state machine.
2366 */
2367 report = SND_JACK_HEADPHONE;
2368
2369 reg = snd_soc_read(codec, WM8996_HEADPHONE_DETECT_2);
2370 if (reg < 0) {
2371 dev_err(codec->dev, "Failed to read HPDET status\n");
2372 goto out;
2373 }
2374
2375 if (!(reg & WM8996_HP_DONE)) {
2376 dev_err(codec->dev, "Got HPDET IRQ but HPDET is busy\n");
2377 goto out;
2378 }
2379
2380 val = reg & WM8996_HP_LVL_MASK;
2381
2382 dev_dbg(codec->dev, "HPDET measured %d ohms\n", val);
2383
2384 /* If we've got high enough impedence then report as line,
2385 * otherwise assume headphone.
2386 */
2387 if (val >= 126)
2388 report = SND_JACK_LINEOUT;
2389 else
2390 report = SND_JACK_HEADPHONE;
2391
2392out:
2393 if (wm8996->jack_mic)
2394 report |= SND_JACK_MICROPHONE;
2395
2396 snd_soc_jack_report(wm8996->jack, report,
2397 SND_JACK_LINEOUT | SND_JACK_HEADSET);
2398
2399 wm8996->detecting = false;
2400
2401 /* If the output isn't running re-clamp it */
2402 if (!(snd_soc_read(codec, WM8996_POWER_MANAGEMENT_1) &
2403 (WM8996_HPOUT1L_ENA | WM8996_HPOUT1R_RMV_SHORT)))
2404 snd_soc_update_bits(codec, WM8996_ANALOGUE_HP_1,
2405 WM8996_HPOUT1L_RMV_SHORT |
2406 WM8996_HPOUT1R_RMV_SHORT, 0);
2407
2408 /* Go back to looking at the microphone */
2409 snd_soc_update_bits(codec, WM8996_ACCESSORY_DETECT_MODE_1,
2410 WM8996_JD_MODE_MASK, 0);
2411 snd_soc_update_bits(codec, WM8996_MIC_DETECT_1, WM8996_MICD_ENA,
2412 WM8996_MICD_ENA);
2413
2414 snd_soc_dapm_disable_pin(&codec->dapm, "Bandgap");
2415 snd_soc_dapm_sync(&codec->dapm);
2416}
2417
2418static void wm8996_hpdet_start(struct snd_soc_codec *codec)
2419{
2420 /* Unclamp the output, we can't measure while we're shorting it */
2421 snd_soc_update_bits(codec, WM8996_ANALOGUE_HP_1,
2422 WM8996_HPOUT1L_RMV_SHORT |
2423 WM8996_HPOUT1R_RMV_SHORT,
2424 WM8996_HPOUT1L_RMV_SHORT |
2425 WM8996_HPOUT1R_RMV_SHORT);
2426
2427 /* We need bandgap for HPDET */
2428 snd_soc_dapm_force_enable_pin(&codec->dapm, "Bandgap");
2429 snd_soc_dapm_sync(&codec->dapm);
2430
2431 /* Go into headphone detect left mode */
2432 snd_soc_update_bits(codec, WM8996_MIC_DETECT_1, WM8996_MICD_ENA, 0);
2433 snd_soc_update_bits(codec, WM8996_ACCESSORY_DETECT_MODE_1,
2434 WM8996_JD_MODE_MASK, 1);
2435
2436 /* Trigger a measurement */
2437 snd_soc_update_bits(codec, WM8996_HEADPHONE_DETECT_1,
2438 WM8996_HP_POLL, WM8996_HP_POLL);
2439}
2440
2359static void wm8996_micd(struct snd_soc_codec *codec) 2441static void wm8996_micd(struct snd_soc_codec *codec)
2360{ 2442{
2361 struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec); 2443 struct wm8996_priv *wm8996 = snd_soc_codec_get_drvdata(codec);
@@ -2376,28 +2458,36 @@ static void wm8996_micd(struct snd_soc_codec *codec)
2376 wm8996->jack_mic = false; 2458 wm8996->jack_mic = false;
2377 wm8996->detecting = true; 2459 wm8996->detecting = true;
2378 snd_soc_jack_report(wm8996->jack, 0, 2460 snd_soc_jack_report(wm8996->jack, 0,
2379 SND_JACK_HEADSET | SND_JACK_BTN_0); 2461 SND_JACK_LINEOUT | SND_JACK_HEADSET |
2462 SND_JACK_BTN_0);
2463
2380 snd_soc_update_bits(codec, WM8996_MIC_DETECT_1, 2464 snd_soc_update_bits(codec, WM8996_MIC_DETECT_1,
2381 WM8996_MICD_RATE_MASK, 2465 WM8996_MICD_RATE_MASK,
2382 WM8996_MICD_RATE_MASK); 2466 WM8996_MICD_RATE_MASK);
2383 return; 2467 return;
2384 } 2468 }
2385 2469
2386 /* If the measurement is very high we've got a microphone but 2470 /* If the measurement is very high we've got a microphone,
2387 * do a little debounce to account for mechanical issues. 2471 * either we just detected one or if we already reported then
2472 * we've got a button release event.
2388 */ 2473 */
2389 if (val & 0x400) { 2474 if (val & 0x400) {
2390 dev_dbg(codec->dev, "Microphone detected\n"); 2475 if (wm8996->detecting) {
2391 snd_soc_jack_report(wm8996->jack, SND_JACK_HEADSET, 2476 dev_dbg(codec->dev, "Microphone detected\n");
2392 SND_JACK_HEADSET | SND_JACK_BTN_0); 2477 wm8996->jack_mic = true;
2393 wm8996->jack_mic = true; 2478 wm8996_hpdet_start(codec);
2394 wm8996->detecting = false; 2479
2395 2480 /* Increase poll rate to give better responsiveness
2396 /* Increase poll rate to give better responsiveness 2481 * for buttons */
2397 * for buttons */ 2482 snd_soc_update_bits(codec, WM8996_MIC_DETECT_1,
2398 snd_soc_update_bits(codec, WM8996_MIC_DETECT_1, 2483 WM8996_MICD_RATE_MASK,
2399 WM8996_MICD_RATE_MASK, 2484 5 << WM8996_MICD_RATE_SHIFT);
2400 5 << WM8996_MICD_RATE_SHIFT); 2485 } else {
2486 dev_dbg(codec->dev, "Mic button up\n");
2487 snd_soc_jack_report(wm8996->jack, 0, SND_JACK_BTN_0);
2488 }
2489
2490 return;
2401 } 2491 }
2402 2492
2403 /* If we detected a lower impedence during initial startup 2493 /* If we detected a lower impedence during initial startup
@@ -2429,15 +2519,11 @@ static void wm8996_micd(struct snd_soc_codec *codec)
2429 if (val & 0x3fc) { 2519 if (val & 0x3fc) {
2430 if (wm8996->jack_mic) { 2520 if (wm8996->jack_mic) {
2431 dev_dbg(codec->dev, "Mic button detected\n"); 2521 dev_dbg(codec->dev, "Mic button detected\n");
2432 snd_soc_jack_report(wm8996->jack, 2522 snd_soc_jack_report(wm8996->jack, SND_JACK_BTN_0,
2433 SND_JACK_HEADSET | SND_JACK_BTN_0,
2434 SND_JACK_HEADSET | SND_JACK_BTN_0);
2435 } else {
2436 dev_dbg(codec->dev, "Headphone detected\n");
2437 snd_soc_jack_report(wm8996->jack,
2438 SND_JACK_HEADPHONE,
2439 SND_JACK_HEADSET |
2440 SND_JACK_BTN_0); 2523 SND_JACK_BTN_0);
2524 } else if (wm8996->detecting) {
2525 dev_dbg(codec->dev, "Headphone detected\n");
2526 wm8996_hpdet_start(codec);
2441 2527
2442 /* Increase the detection rate a bit for 2528 /* Increase the detection rate a bit for
2443 * responsiveness. 2529 * responsiveness.
@@ -2445,8 +2531,6 @@ static void wm8996_micd(struct snd_soc_codec *codec)
2445 snd_soc_update_bits(codec, WM8996_MIC_DETECT_1, 2531 snd_soc_update_bits(codec, WM8996_MIC_DETECT_1,
2446 WM8996_MICD_RATE_MASK, 2532 WM8996_MICD_RATE_MASK,
2447 7 << WM8996_MICD_RATE_SHIFT); 2533 7 << WM8996_MICD_RATE_SHIFT);
2448
2449 wm8996->detecting = false;
2450 } 2534 }
2451 } 2535 }
2452} 2536}
@@ -2486,6 +2570,9 @@ static irqreturn_t wm8996_irq(int irq, void *data)
2486 if (irq_val & WM8996_MICD_EINT) 2570 if (irq_val & WM8996_MICD_EINT)
2487 wm8996_micd(codec); 2571 wm8996_micd(codec);
2488 2572
2573 if (irq_val & WM8996_HP_DONE_EINT)
2574 wm8996_hpdet_irq(codec);
2575
2489 return IRQ_HANDLED; 2576 return IRQ_HANDLED;
2490} 2577}
2491 2578