diff options
author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2010-09-30 18:37:53 -0400 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2010-10-02 17:23:26 -0400 |
commit | 7711308ae68900a047782f5fe1bd46196ea8be0e (patch) | |
tree | 09a31bf794d410f94f604afa45dd816c46a4efa6 /sound/soc/codecs/wm8962.c | |
parent | 3367b8d4278d1f8a28995cc5e57a995f7147cb73 (diff) |
ASoC: Implement interrupt based jack detection
Allow microphone detection on WM8962 to be performed using the interrupt
signal, allowing the detection of both microphone presence and button
presses with a signal singal from the CODEC to CPU. Currently a 250ms
debounce time is applied to both short circuit and presence detection,
this has not been optimised.
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Acked-by: Liam Girdwood <lrg@slimlogic.co.uk>
Diffstat (limited to 'sound/soc/codecs/wm8962.c')
-rw-r--r-- | sound/soc/codecs/wm8962.c | 85 |
1 files changed, 85 insertions, 0 deletions
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index eb66c66047b1..3a1db4a76a64 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <linux/slab.h> | 25 | #include <linux/slab.h> |
26 | #include <linux/workqueue.h> | 26 | #include <linux/workqueue.h> |
27 | #include <sound/core.h> | 27 | #include <sound/core.h> |
28 | #include <sound/jack.h> | ||
28 | #include <sound/pcm.h> | 29 | #include <sound/pcm.h> |
29 | #include <sound/pcm_params.h> | 30 | #include <sound/pcm_params.h> |
30 | #include <sound/soc.h> | 31 | #include <sound/soc.h> |
@@ -63,6 +64,9 @@ struct wm8962_priv { | |||
63 | int fll_fref; | 64 | int fll_fref; |
64 | int fll_fout; | 65 | int fll_fout; |
65 | 66 | ||
67 | struct delayed_work mic_work; | ||
68 | struct snd_soc_jack *jack; | ||
69 | |||
66 | struct regulator_bulk_data supplies[WM8962_NUM_SUPPLIES]; | 70 | struct regulator_bulk_data supplies[WM8962_NUM_SUPPLIES]; |
67 | struct notifier_block disable_nb[WM8962_NUM_SUPPLIES]; | 71 | struct notifier_block disable_nb[WM8962_NUM_SUPPLIES]; |
68 | 72 | ||
@@ -1462,9 +1466,40 @@ static struct snd_soc_dai_driver wm8962_dai = { | |||
1462 | .symmetric_rates = 1, | 1466 | .symmetric_rates = 1, |
1463 | }; | 1467 | }; |
1464 | 1468 | ||
1469 | static void wm8962_mic_work(struct work_struct *work) | ||
1470 | { | ||
1471 | struct wm8962_priv *wm8962 = container_of(work, | ||
1472 | struct wm8962_priv, | ||
1473 | mic_work.work); | ||
1474 | struct snd_soc_codec *codec = wm8962->codec; | ||
1475 | int status = 0; | ||
1476 | int irq_pol = 0; | ||
1477 | int reg; | ||
1478 | |||
1479 | reg = snd_soc_read(codec, WM8962_ADDITIONAL_CONTROL_4); | ||
1480 | |||
1481 | if (reg & WM8962_MICDET_STS) { | ||
1482 | status |= SND_JACK_MICROPHONE; | ||
1483 | irq_pol |= WM8962_MICD_IRQ_POL; | ||
1484 | } | ||
1485 | |||
1486 | if (reg & WM8962_MICSHORT_STS) { | ||
1487 | status |= SND_JACK_BTN_0; | ||
1488 | irq_pol |= WM8962_MICSCD_IRQ_POL; | ||
1489 | } | ||
1490 | |||
1491 | snd_soc_jack_report(wm8962->jack, status, | ||
1492 | SND_JACK_MICROPHONE | SND_JACK_BTN_0); | ||
1493 | |||
1494 | snd_soc_update_bits(codec, WM8962_MICINT_SOURCE_POL, | ||
1495 | WM8962_MICSCD_IRQ_POL | | ||
1496 | WM8962_MICD_IRQ_POL, irq_pol); | ||
1497 | } | ||
1498 | |||
1465 | static irqreturn_t wm8962_irq(int irq, void *data) | 1499 | static irqreturn_t wm8962_irq(int irq, void *data) |
1466 | { | 1500 | { |
1467 | struct snd_soc_codec *codec = data; | 1501 | struct snd_soc_codec *codec = data; |
1502 | struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); | ||
1468 | int mask; | 1503 | int mask; |
1469 | int active; | 1504 | int active; |
1470 | 1505 | ||
@@ -1479,12 +1514,59 @@ static irqreturn_t wm8962_irq(int irq, void *data) | |||
1479 | if (active & WM8962_TEMP_SHUT_EINT) | 1514 | if (active & WM8962_TEMP_SHUT_EINT) |
1480 | dev_crit(codec->dev, "Thermal shutdown\n"); | 1515 | dev_crit(codec->dev, "Thermal shutdown\n"); |
1481 | 1516 | ||
1517 | if (active & (WM8962_MICSCD_EINT | WM8962_MICD_EINT)) { | ||
1518 | dev_dbg(codec->dev, "Microphone event detected\n"); | ||
1519 | |||
1520 | schedule_delayed_work(&wm8962->mic_work, | ||
1521 | msecs_to_jiffies(250)); | ||
1522 | } | ||
1523 | |||
1482 | /* Acknowledge the interrupts */ | 1524 | /* Acknowledge the interrupts */ |
1483 | snd_soc_write(codec, WM8962_INTERRUPT_STATUS_2, active); | 1525 | snd_soc_write(codec, WM8962_INTERRUPT_STATUS_2, active); |
1484 | 1526 | ||
1485 | return IRQ_HANDLED; | 1527 | return IRQ_HANDLED; |
1486 | } | 1528 | } |
1487 | 1529 | ||
1530 | /** | ||
1531 | * wm8962_mic_detect - Enable microphone detection via the WM8962 IRQ | ||
1532 | * | ||
1533 | * @codec: WM8962 codec | ||
1534 | * @jack: jack to report detection events on | ||
1535 | * | ||
1536 | * Enable microphone detection via IRQ on the WM8962. If GPIOs are | ||
1537 | * being used to bring out signals to the processor then only platform | ||
1538 | * data configuration is needed for WM8962 and processor GPIOs should | ||
1539 | * be configured using snd_soc_jack_add_gpios() instead. | ||
1540 | * | ||
1541 | * If no jack is supplied detection will be disabled. | ||
1542 | */ | ||
1543 | int wm8962_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack) | ||
1544 | { | ||
1545 | struct wm8962_priv *wm8962 = snd_soc_codec_get_drvdata(codec); | ||
1546 | int irq_mask, enable; | ||
1547 | |||
1548 | wm8962->jack = jack; | ||
1549 | if (jack) { | ||
1550 | irq_mask = 0; | ||
1551 | enable = WM8962_MICDET_ENA; | ||
1552 | } else { | ||
1553 | irq_mask = WM8962_MICD_EINT | WM8962_MICSCD_EINT; | ||
1554 | enable = 0; | ||
1555 | } | ||
1556 | |||
1557 | snd_soc_update_bits(codec, WM8962_INTERRUPT_STATUS_2_MASK, | ||
1558 | WM8962_MICD_EINT | WM8962_MICSCD_EINT, irq_mask); | ||
1559 | snd_soc_update_bits(codec, WM8962_ADDITIONAL_CONTROL_4, | ||
1560 | WM8962_MICDET_ENA, enable); | ||
1561 | |||
1562 | /* Send an initial empty report */ | ||
1563 | snd_soc_jack_report(wm8962->jack, 0, | ||
1564 | SND_JACK_MICROPHONE | SND_JACK_BTN_0); | ||
1565 | |||
1566 | return 0; | ||
1567 | } | ||
1568 | EXPORT_SYMBOL_GPL(wm8962_mic_detect); | ||
1569 | |||
1488 | #ifdef CONFIG_PM | 1570 | #ifdef CONFIG_PM |
1489 | static int wm8962_resume(struct snd_soc_codec *codec) | 1571 | static int wm8962_resume(struct snd_soc_codec *codec) |
1490 | { | 1572 | { |
@@ -1773,6 +1855,7 @@ static int wm8962_probe(struct snd_soc_codec *codec) | |||
1773 | int i, trigger, irq_pol; | 1855 | int i, trigger, irq_pol; |
1774 | 1856 | ||
1775 | wm8962->codec = codec; | 1857 | wm8962->codec = codec; |
1858 | INIT_DELAYED_WORK(&wm8962->mic_work, wm8962_mic_work); | ||
1776 | 1859 | ||
1777 | codec->cache_sync = 1; | 1860 | codec->cache_sync = 1; |
1778 | codec->idle_bias_off = 1; | 1861 | codec->idle_bias_off = 1; |
@@ -1946,6 +2029,8 @@ static int wm8962_remove(struct snd_soc_codec *codec) | |||
1946 | if (i2c->irq) | 2029 | if (i2c->irq) |
1947 | free_irq(i2c->irq, codec); | 2030 | free_irq(i2c->irq, codec); |
1948 | 2031 | ||
2032 | cancel_delayed_work_sync(&wm8962->mic_work); | ||
2033 | |||
1949 | wm8962_free_gpio(codec); | 2034 | wm8962_free_gpio(codec); |
1950 | wm8962_free_beep(codec); | 2035 | wm8962_free_beep(codec); |
1951 | for (i = 0; i < ARRAY_SIZE(wm8962->supplies); i++) | 2036 | for (i = 0; i < ARRAY_SIZE(wm8962->supplies); i++) |