diff options
author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2010-03-15 17:22:58 -0400 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2010-03-16 11:57:43 -0400 |
commit | 7245387e36e16ae918467685c34510606fd74b7c (patch) | |
tree | 40ecf5eb8fb9eaa287c37d0dd99645380d3b0b90 /sound/soc/codecs | |
parent | 8abd16a65d81756706016720e2cc7eeb81d06a2e (diff) |
ASoC: Implement interrupt driven microphone detection for WM8903
Support use of the WM8903 IRQ for reporting of microphone presence
and short detection.
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Acked-by: Liam Girdwood <lrg@slimlogic.co.uk>
Diffstat (limited to 'sound/soc/codecs')
-rw-r--r-- | sound/soc/codecs/wm8903.c | 106 | ||||
-rw-r--r-- | sound/soc/codecs/wm8903.h | 4 |
2 files changed, 107 insertions, 3 deletions
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index 7aa2adbe6fba..b5427b47d6fd 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <linux/i2c.h> | 23 | #include <linux/i2c.h> |
24 | #include <linux/platform_device.h> | 24 | #include <linux/platform_device.h> |
25 | #include <sound/core.h> | 25 | #include <sound/core.h> |
26 | #include <sound/jack.h> | ||
26 | #include <sound/pcm.h> | 27 | #include <sound/pcm.h> |
27 | #include <sound/pcm_params.h> | 28 | #include <sound/pcm_params.h> |
28 | #include <sound/tlv.h> | 29 | #include <sound/tlv.h> |
@@ -223,6 +224,12 @@ struct wm8903_priv { | |||
223 | 224 | ||
224 | struct completion wseq; | 225 | struct completion wseq; |
225 | 226 | ||
227 | struct snd_soc_jack *mic_jack; | ||
228 | int mic_det; | ||
229 | int mic_short; | ||
230 | int mic_last_report; | ||
231 | int mic_delay; | ||
232 | |||
226 | struct snd_pcm_substream *master_substream; | 233 | struct snd_pcm_substream *master_substream; |
227 | struct snd_pcm_substream *slave_substream; | 234 | struct snd_pcm_substream *slave_substream; |
228 | }; | 235 | }; |
@@ -1437,19 +1444,112 @@ static int wm8903_hw_params(struct snd_pcm_substream *substream, | |||
1437 | return 0; | 1444 | return 0; |
1438 | } | 1445 | } |
1439 | 1446 | ||
1447 | /** | ||
1448 | * wm8903_mic_detect - Enable microphone detection via the WM8903 IRQ | ||
1449 | * | ||
1450 | * @codec: WM8903 codec | ||
1451 | * @jack: jack to report detection events on | ||
1452 | * @det: value to report for presence detection | ||
1453 | * @shrt: value to report for short detection | ||
1454 | * | ||
1455 | * Enable microphone detection via IRQ on the WM8903. If GPIOs are | ||
1456 | * being used to bring out signals to the processor then only platform | ||
1457 | * data configuration is needed for WM8903 and processor GPIOs should | ||
1458 | * be configured using snd_soc_jack_add_gpios() instead. | ||
1459 | * | ||
1460 | * The current threasholds for detection should be configured using | ||
1461 | * micdet_cfg in the platform data. Using this function will force on | ||
1462 | * the microphone bias for the device. | ||
1463 | */ | ||
1464 | int wm8903_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack, | ||
1465 | int det, int shrt) | ||
1466 | { | ||
1467 | struct wm8903_priv *wm8903 = codec->private_data; | ||
1468 | int irq_mask = 0; | ||
1469 | |||
1470 | dev_dbg(codec->dev, "Enabling microphone detection: %x %x\n", | ||
1471 | det, shrt); | ||
1472 | |||
1473 | /* Store the configuration */ | ||
1474 | wm8903->mic_jack = jack; | ||
1475 | wm8903->mic_det = det; | ||
1476 | wm8903->mic_short = shrt; | ||
1477 | |||
1478 | /* Enable interrupts we've got a report configured for */ | ||
1479 | if (det) | ||
1480 | irq_mask &= ~WM8903_MICDET_EINT; | ||
1481 | if (shrt) | ||
1482 | irq_mask &= ~WM8903_MICSHRT_EINT; | ||
1483 | |||
1484 | snd_soc_update_bits(codec, WM8903_INTERRUPT_STATUS_1_MASK, | ||
1485 | WM8903_MICDET_EINT | WM8903_MICSHRT_EINT, | ||
1486 | irq_mask); | ||
1487 | |||
1488 | /* Enable mic detection, this may not have been set through | ||
1489 | * platform data (eg, if the defaults are OK). */ | ||
1490 | snd_soc_update_bits(codec, WM8903_WRITE_SEQUENCER_0, | ||
1491 | WM8903_WSEQ_ENA, WM8903_WSEQ_ENA); | ||
1492 | snd_soc_update_bits(codec, WM8903_MIC_BIAS_CONTROL_0, | ||
1493 | WM8903_MICDET_ENA, WM8903_MICDET_ENA); | ||
1494 | |||
1495 | /* Force the microphone bias on; this will trigger an initial | ||
1496 | * detection. */ | ||
1497 | snd_soc_dapm_force_enable_pin(codec, "Mic Bias"); | ||
1498 | |||
1499 | return 0; | ||
1500 | } | ||
1501 | EXPORT_SYMBOL_GPL(wm8903_mic_detect); | ||
1502 | |||
1440 | static irqreturn_t wm8903_irq(int irq, void *data) | 1503 | static irqreturn_t wm8903_irq(int irq, void *data) |
1441 | { | 1504 | { |
1442 | struct wm8903_priv *wm8903 = data; | 1505 | struct wm8903_priv *wm8903 = data; |
1443 | struct snd_soc_codec *codec = &wm8903->codec; | 1506 | struct snd_soc_codec *codec = &wm8903->codec; |
1444 | int reg; | 1507 | int mic_report; |
1508 | int int_pol; | ||
1509 | int int_val = 0; | ||
1510 | int mask = ~snd_soc_read(codec, WM8903_INTERRUPT_STATUS_1_MASK); | ||
1445 | 1511 | ||
1446 | reg = snd_soc_read(codec, WM8903_INTERRUPT_STATUS_1); | 1512 | int_val = snd_soc_read(codec, WM8903_INTERRUPT_STATUS_1) & mask; |
1447 | 1513 | ||
1448 | if (reg & WM8903_WSEQ_BUSY_EINT) { | 1514 | if (int_val & WM8903_WSEQ_BUSY_EINT) { |
1449 | dev_dbg(codec->dev, "Write sequencer done\n"); | 1515 | dev_dbg(codec->dev, "Write sequencer done\n"); |
1450 | complete(&wm8903->wseq); | 1516 | complete(&wm8903->wseq); |
1451 | } | 1517 | } |
1452 | 1518 | ||
1519 | /* | ||
1520 | * The rest is microphone jack detection. We need to manually | ||
1521 | * invert the polarity of the interrupt after each event - to | ||
1522 | * simplify the code keep track of the last state we reported | ||
1523 | * and just invert the relevant bits in both the report and | ||
1524 | * the polarity register. | ||
1525 | */ | ||
1526 | mic_report = wm8903->mic_last_report; | ||
1527 | int_pol = snd_soc_read(codec, WM8903_INTERRUPT_POLARITY_1); | ||
1528 | |||
1529 | if (int_val & WM8903_MICSHRT_EINT) { | ||
1530 | dev_dbg(codec->dev, "Microphone short (pol=%x)\n", int_pol); | ||
1531 | |||
1532 | mic_report ^= wm8903->mic_short; | ||
1533 | int_pol ^= WM8903_MICSHRT_INV; | ||
1534 | } | ||
1535 | |||
1536 | if (int_val & WM8903_MICDET_EINT) { | ||
1537 | dev_dbg(codec->dev, "Microphone detect (pol=%x)\n", int_pol); | ||
1538 | |||
1539 | mic_report ^= wm8903->mic_det; | ||
1540 | int_pol ^= WM8903_MICDET_INV; | ||
1541 | |||
1542 | msleep(wm8903->mic_delay); | ||
1543 | } | ||
1544 | |||
1545 | snd_soc_update_bits(codec, WM8903_INTERRUPT_POLARITY_1, | ||
1546 | WM8903_MICSHRT_INV | WM8903_MICDET_INV, int_pol); | ||
1547 | |||
1548 | snd_soc_jack_report(wm8903->mic_jack, mic_report, | ||
1549 | wm8903->mic_short | wm8903->mic_det); | ||
1550 | |||
1551 | wm8903->mic_last_report = mic_report; | ||
1552 | |||
1453 | return IRQ_HANDLED; | 1553 | return IRQ_HANDLED; |
1454 | } | 1554 | } |
1455 | 1555 | ||
diff --git a/sound/soc/codecs/wm8903.h b/sound/soc/codecs/wm8903.h index 8f19a2413785..ce384a2ad820 100644 --- a/sound/soc/codecs/wm8903.h +++ b/sound/soc/codecs/wm8903.h | |||
@@ -18,6 +18,10 @@ | |||
18 | extern struct snd_soc_dai wm8903_dai; | 18 | extern struct snd_soc_dai wm8903_dai; |
19 | extern struct snd_soc_codec_device soc_codec_dev_wm8903; | 19 | extern struct snd_soc_codec_device soc_codec_dev_wm8903; |
20 | 20 | ||
21 | extern int wm8903_mic_detect(struct snd_soc_codec *codec, | ||
22 | struct snd_soc_jack *jack, | ||
23 | int det, int shrt); | ||
24 | |||
21 | #define WM8903_MCLK_DIV_2 1 | 25 | #define WM8903_MCLK_DIV_2 1 |
22 | #define WM8903_CLK_SYS 2 | 26 | #define WM8903_CLK_SYS 2 |
23 | #define WM8903_BCLK 3 | 27 | #define WM8903_BCLK 3 |