aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2011-09-04 10:50:31 -0400
committerMark Brown <broonie@opensource.wolfsonmicro.com>2011-09-19 13:30:59 -0400
commit0b684cc14a791accdd6d97cb68242ab5009ece3e (patch)
tree91c8f001d644f4d0cb12a9e46b154bd90c756d60 /sound/soc
parent8259df12fd3f3429648411bfff37dfbb34a2d9b2 (diff)
ASoC: Initial WM8996 headphone impedance measurement support
The WM8996 can measure the impedance of accessories connected to the headphone output. Implement initial support for this, measuring the left channel impedance when an accessory is detected and using this to distinguish between a line load and a headphone load. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Acked-by: Liam Girdwood <lrg@ti.com>
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