diff options
author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2010-03-15 14:25:26 -0400 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2010-03-16 11:57:15 -0400 |
commit | 8abd16a65d81756706016720e2cc7eeb81d06a2e (patch) | |
tree | e8031539c57c4add59dd84bbf943584ef58fb907 | |
parent | 37f88e8407f75fc6ced5cefb633c314556de3ad1 (diff) |
ASoC: Add WM8903 interrupt support
Currently used to detect completion of the write sequencer.
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Acked-by: Liam Girdwood <lrg@slimlogic.co.uk>
-rw-r--r-- | include/sound/wm8903.h | 2 | ||||
-rw-r--r-- | sound/soc/codecs/wm8903.c | 66 |
2 files changed, 63 insertions, 5 deletions
diff --git a/include/sound/wm8903.h b/include/sound/wm8903.h index 22b66a76febd..49bb1a844bfc 100644 --- a/include/sound/wm8903.h +++ b/include/sound/wm8903.h | |||
@@ -232,6 +232,8 @@ | |||
232 | #define WM8903_GP5_DB_WIDTH 1 /* GP5_DB */ | 232 | #define WM8903_GP5_DB_WIDTH 1 /* GP5_DB */ |
233 | 233 | ||
234 | struct wm8903_platform_data { | 234 | struct wm8903_platform_data { |
235 | bool irq_active_low; /* Set if IRQ active low, default high */ | ||
236 | |||
235 | /* Default register value for R6 (Mic bias), used to configure | 237 | /* Default register value for R6 (Mic bias), used to configure |
236 | * microphone detection. In conjunction with gpio_cfg this | 238 | * microphone detection. In conjunction with gpio_cfg this |
237 | * can be used to route the microphone status signals out onto | 239 | * can be used to route the microphone status signals out onto |
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index 467e6c335c5a..7aa2adbe6fba 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c | |||
@@ -12,12 +12,12 @@ | |||
12 | * TODO: | 12 | * TODO: |
13 | * - TDM mode configuration. | 13 | * - TDM mode configuration. |
14 | * - Digital microphone support. | 14 | * - Digital microphone support. |
15 | * - Interrupt support (mic detect and sequencer). | ||
16 | */ | 15 | */ |
17 | 16 | ||
18 | #include <linux/module.h> | 17 | #include <linux/module.h> |
19 | #include <linux/moduleparam.h> | 18 | #include <linux/moduleparam.h> |
20 | #include <linux/init.h> | 19 | #include <linux/init.h> |
20 | #include <linux/completion.h> | ||
21 | #include <linux/delay.h> | 21 | #include <linux/delay.h> |
22 | #include <linux/pm.h> | 22 | #include <linux/pm.h> |
23 | #include <linux/i2c.h> | 23 | #include <linux/i2c.h> |
@@ -29,6 +29,7 @@ | |||
29 | #include <sound/soc.h> | 29 | #include <sound/soc.h> |
30 | #include <sound/soc-dapm.h> | 30 | #include <sound/soc-dapm.h> |
31 | #include <sound/initval.h> | 31 | #include <sound/initval.h> |
32 | #include <sound/wm8903.h> | ||
32 | 33 | ||
33 | #include "wm8903.h" | 34 | #include "wm8903.h" |
34 | 35 | ||
@@ -220,6 +221,8 @@ struct wm8903_priv { | |||
220 | int playback_active; | 221 | int playback_active; |
221 | int capture_active; | 222 | int capture_active; |
222 | 223 | ||
224 | struct completion wseq; | ||
225 | |||
223 | struct snd_pcm_substream *master_substream; | 226 | struct snd_pcm_substream *master_substream; |
224 | struct snd_pcm_substream *slave_substream; | 227 | struct snd_pcm_substream *slave_substream; |
225 | }; | 228 | }; |
@@ -242,6 +245,7 @@ static int wm8903_run_sequence(struct snd_soc_codec *codec, unsigned int start) | |||
242 | { | 245 | { |
243 | u16 reg[5]; | 246 | u16 reg[5]; |
244 | struct i2c_client *i2c = codec->control_data; | 247 | struct i2c_client *i2c = codec->control_data; |
248 | struct wm8903_priv *wm8903 = codec->private_data; | ||
245 | 249 | ||
246 | BUG_ON(start > 48); | 250 | BUG_ON(start > 48); |
247 | 251 | ||
@@ -256,11 +260,11 @@ static int wm8903_run_sequence(struct snd_soc_codec *codec, unsigned int start) | |||
256 | start | WM8903_WSEQ_START); | 260 | start | WM8903_WSEQ_START); |
257 | 261 | ||
258 | /* Wait for it to complete. If we have the interrupt wired up then | 262 | /* Wait for it to complete. If we have the interrupt wired up then |
259 | * we could block waiting for an interrupt, though polling may still | 263 | * that will break us out of the poll early. |
260 | * be desirable for diagnostic purposes. | ||
261 | */ | 264 | */ |
262 | do { | 265 | do { |
263 | msleep(10); | 266 | wait_for_completion_timeout(&wm8903->wseq, |
267 | msecs_to_jiffies(10)); | ||
264 | 268 | ||
265 | reg[4] = snd_soc_read(codec, WM8903_WRITE_SEQUENCER_4); | 269 | reg[4] = snd_soc_read(codec, WM8903_WRITE_SEQUENCER_4); |
266 | } while (reg[4] & WM8903_WSEQ_BUSY); | 270 | } while (reg[4] & WM8903_WSEQ_BUSY); |
@@ -1433,6 +1437,22 @@ static int wm8903_hw_params(struct snd_pcm_substream *substream, | |||
1433 | return 0; | 1437 | return 0; |
1434 | } | 1438 | } |
1435 | 1439 | ||
1440 | static irqreturn_t wm8903_irq(int irq, void *data) | ||
1441 | { | ||
1442 | struct wm8903_priv *wm8903 = data; | ||
1443 | struct snd_soc_codec *codec = &wm8903->codec; | ||
1444 | int reg; | ||
1445 | |||
1446 | reg = snd_soc_read(codec, WM8903_INTERRUPT_STATUS_1); | ||
1447 | |||
1448 | if (reg & WM8903_WSEQ_BUSY_EINT) { | ||
1449 | dev_dbg(codec->dev, "Write sequencer done\n"); | ||
1450 | complete(&wm8903->wseq); | ||
1451 | } | ||
1452 | |||
1453 | return IRQ_HANDLED; | ||
1454 | } | ||
1455 | |||
1436 | #define WM8903_PLAYBACK_RATES (SNDRV_PCM_RATE_8000 |\ | 1456 | #define WM8903_PLAYBACK_RATES (SNDRV_PCM_RATE_8000 |\ |
1437 | SNDRV_PCM_RATE_11025 | \ | 1457 | SNDRV_PCM_RATE_11025 | \ |
1438 | SNDRV_PCM_RATE_16000 | \ | 1458 | SNDRV_PCM_RATE_16000 | \ |
@@ -1527,9 +1547,11 @@ static struct snd_soc_codec *wm8903_codec; | |||
1527 | static __devinit int wm8903_i2c_probe(struct i2c_client *i2c, | 1547 | static __devinit int wm8903_i2c_probe(struct i2c_client *i2c, |
1528 | const struct i2c_device_id *id) | 1548 | const struct i2c_device_id *id) |
1529 | { | 1549 | { |
1550 | struct wm8903_platform_data *pdata = dev_get_platdata(&i2c->dev); | ||
1530 | struct wm8903_priv *wm8903; | 1551 | struct wm8903_priv *wm8903; |
1531 | struct snd_soc_codec *codec; | 1552 | struct snd_soc_codec *codec; |
1532 | int ret, i; | 1553 | int ret, i; |
1554 | int trigger, irq_pol; | ||
1533 | u16 val; | 1555 | u16 val; |
1534 | 1556 | ||
1535 | wm8903 = kzalloc(sizeof(struct wm8903_priv), GFP_KERNEL); | 1557 | wm8903 = kzalloc(sizeof(struct wm8903_priv), GFP_KERNEL); |
@@ -1553,6 +1575,7 @@ static __devinit int wm8903_i2c_probe(struct i2c_client *i2c, | |||
1553 | codec->reg_cache = &wm8903->reg_cache[0]; | 1575 | codec->reg_cache = &wm8903->reg_cache[0]; |
1554 | codec->private_data = wm8903; | 1576 | codec->private_data = wm8903; |
1555 | codec->volatile_register = wm8903_volatile_register; | 1577 | codec->volatile_register = wm8903_volatile_register; |
1578 | init_completion(&wm8903->wseq); | ||
1556 | 1579 | ||
1557 | i2c_set_clientdata(i2c, codec); | 1580 | i2c_set_clientdata(i2c, codec); |
1558 | codec->control_data = i2c; | 1581 | codec->control_data = i2c; |
@@ -1596,6 +1619,32 @@ static __devinit int wm8903_i2c_probe(struct i2c_client *i2c, | |||
1596 | 1619 | ||
1597 | wm8903->mic_delay = pdata->micdet_delay; | 1620 | wm8903->mic_delay = pdata->micdet_delay; |
1598 | } | 1621 | } |
1622 | |||
1623 | if (i2c->irq) { | ||
1624 | if (pdata && pdata->irq_active_low) { | ||
1625 | trigger = IRQF_TRIGGER_LOW; | ||
1626 | irq_pol = WM8903_IRQ_POL; | ||
1627 | } else { | ||
1628 | trigger = IRQF_TRIGGER_HIGH; | ||
1629 | irq_pol = 0; | ||
1630 | } | ||
1631 | |||
1632 | snd_soc_update_bits(codec, WM8903_INTERRUPT_CONTROL, | ||
1633 | WM8903_IRQ_POL, irq_pol); | ||
1634 | |||
1635 | ret = request_threaded_irq(i2c->irq, NULL, wm8903_irq, | ||
1636 | trigger | IRQF_ONESHOT, | ||
1637 | "wm8903", wm8903); | ||
1638 | if (ret != 0) { | ||
1639 | dev_err(&i2c->dev, "Failed to request IRQ: %d\n", | ||
1640 | ret); | ||
1641 | goto err; | ||
1642 | } | ||
1643 | |||
1644 | /* Enable write sequencer interrupts */ | ||
1645 | snd_soc_update_bits(codec, WM8903_INTERRUPT_STATUS_1_MASK, | ||
1646 | WM8903_IM_WSEQ_BUSY_EINT, 0); | ||
1647 | } | ||
1599 | 1648 | ||
1600 | /* power on device */ | 1649 | /* power on device */ |
1601 | wm8903_set_bias_level(codec, SND_SOC_BIAS_STANDBY); | 1650 | wm8903_set_bias_level(codec, SND_SOC_BIAS_STANDBY); |
@@ -1637,7 +1686,7 @@ static __devinit int wm8903_i2c_probe(struct i2c_client *i2c, | |||
1637 | ret = snd_soc_register_codec(codec); | 1686 | ret = snd_soc_register_codec(codec); |
1638 | if (ret != 0) { | 1687 | if (ret != 0) { |
1639 | dev_err(&i2c->dev, "Failed to register codec: %d\n", ret); | 1688 | dev_err(&i2c->dev, "Failed to register codec: %d\n", ret); |
1640 | goto err; | 1689 | goto err_irq; |
1641 | } | 1690 | } |
1642 | 1691 | ||
1643 | ret = snd_soc_register_dai(&wm8903_dai); | 1692 | ret = snd_soc_register_dai(&wm8903_dai); |
@@ -1650,6 +1699,9 @@ static __devinit int wm8903_i2c_probe(struct i2c_client *i2c, | |||
1650 | 1699 | ||
1651 | err_codec: | 1700 | err_codec: |
1652 | snd_soc_unregister_codec(codec); | 1701 | snd_soc_unregister_codec(codec); |
1702 | err_irq: | ||
1703 | if (i2c->irq) | ||
1704 | free_irq(i2c->irq, wm8903); | ||
1653 | err: | 1705 | err: |
1654 | wm8903_codec = NULL; | 1706 | wm8903_codec = NULL; |
1655 | kfree(wm8903); | 1707 | kfree(wm8903); |
@@ -1659,12 +1711,16 @@ err: | |||
1659 | static __devexit int wm8903_i2c_remove(struct i2c_client *client) | 1711 | static __devexit int wm8903_i2c_remove(struct i2c_client *client) |
1660 | { | 1712 | { |
1661 | struct snd_soc_codec *codec = i2c_get_clientdata(client); | 1713 | struct snd_soc_codec *codec = i2c_get_clientdata(client); |
1714 | struct wm8903_priv *priv = codec->private_data; | ||
1662 | 1715 | ||
1663 | snd_soc_unregister_dai(&wm8903_dai); | 1716 | snd_soc_unregister_dai(&wm8903_dai); |
1664 | snd_soc_unregister_codec(codec); | 1717 | snd_soc_unregister_codec(codec); |
1665 | 1718 | ||
1666 | wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF); | 1719 | wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF); |
1667 | 1720 | ||
1721 | if (client->irq) | ||
1722 | free_irq(client->irq, priv); | ||
1723 | |||
1668 | kfree(codec->private_data); | 1724 | kfree(codec->private_data); |
1669 | 1725 | ||
1670 | wm8903_codec = NULL; | 1726 | wm8903_codec = NULL; |