diff options
Diffstat (limited to 'sound/soc')
-rw-r--r-- | sound/soc/codecs/wm8996.c | 137 |
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 | } |
2357 | EXPORT_SYMBOL_GPL(wm8996_detect); | 2357 | EXPORT_SYMBOL_GPL(wm8996_detect); |
2358 | 2358 | ||
2359 | static 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 | |||
2392 | out: | ||
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 | |||
2418 | static 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 | |||
2359 | static void wm8996_micd(struct snd_soc_codec *codec) | 2441 | static 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 | ||