aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs
diff options
context:
space:
mode:
authorDimitris Papastamos <dp@opensource.wolfsonmicro.com>2011-01-17 06:00:12 -0500
committerMark Brown <broonie@opensource.wolfsonmicro.com>2011-01-17 08:59:50 -0500
commit219d8df86805b8bb20b375707e9be734100ce89d (patch)
treee3ab763372a7c56657678138cf2373364ab54399 /sound/soc/codecs
parenta1b3b5eeeebac8acfa7838ef90f5a00a6f9188a0 (diff)
ASoC: WM8995: Add regulator handling code
Signed-off-by: Dimitris Papastamos <dp@opensource.wolfsonmicro.com> Acked-by: Liam Girdwood <lrg@slimlogic.co.uk> Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/codecs')
-rw-r--r--sound/soc/codecs/wm8995.c102
1 files changed, 97 insertions, 5 deletions
diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c
index f0f678de489f..7d563413df3e 100644
--- a/sound/soc/codecs/wm8995.c
+++ b/sound/soc/codecs/wm8995.c
@@ -19,6 +19,7 @@
19#include <linux/pm.h> 19#include <linux/pm.h>
20#include <linux/i2c.h> 20#include <linux/i2c.h>
21#include <linux/spi/spi.h> 21#include <linux/spi/spi.h>
22#include <linux/regulator/consumer.h>
22#include <linux/slab.h> 23#include <linux/slab.h>
23#include <sound/core.h> 24#include <sound/core.h>
24#include <sound/pcm.h> 25#include <sound/pcm.h>
@@ -30,6 +31,18 @@
30 31
31#include "wm8995.h" 32#include "wm8995.h"
32 33
34#define WM8995_NUM_SUPPLIES 8
35static const char *wm8995_supply_names[WM8995_NUM_SUPPLIES] = {
36 "DCVDD",
37 "DBVDD1",
38 "DBVDD2",
39 "DBVDD3",
40 "AVDD1",
41 "AVDD2",
42 "CPVDD",
43 "MICVDD"
44};
45
33static const u16 wm8995_reg_defs[WM8995_MAX_REGISTER + 1] = { 46static const u16 wm8995_reg_defs[WM8995_MAX_REGISTER + 1] = {
34 [0] = 0x8995, [5] = 0x0100, [16] = 0x000b, [17] = 0x000b, 47 [0] = 0x8995, [5] = 0x0100, [16] = 0x000b, [17] = 0x000b,
35 [24] = 0x02c0, [25] = 0x02c0, [26] = 0x02c0, [27] = 0x02c0, 48 [24] = 0x02c0, [25] = 0x02c0, [26] = 0x02c0, [27] = 0x02c0,
@@ -126,8 +139,37 @@ struct wm8995_priv {
126 int mclk[2]; 139 int mclk[2];
127 int aifclk[2]; 140 int aifclk[2];
128 struct fll_config fll[2], fll_suspend[2]; 141 struct fll_config fll[2], fll_suspend[2];
142 struct regulator_bulk_data supplies[WM8995_NUM_SUPPLIES];
143 struct notifier_block disable_nb[WM8995_NUM_SUPPLIES];
144 struct snd_soc_codec *codec;
129}; 145};
130 146
147/*
148 * We can't use the same notifier block for more than one supply and
149 * there's no way I can see to get from a callback to the caller
150 * except container_of().
151 */
152#define WM8995_REGULATOR_EVENT(n) \
153static int wm8995_regulator_event_##n(struct notifier_block *nb, \
154 unsigned long event, void *data) \
155{ \
156 struct wm8995_priv *wm8995 = container_of(nb, struct wm8995_priv, \
157 disable_nb[n]); \
158 if (event & REGULATOR_EVENT_DISABLE) { \
159 wm8995->codec->cache_sync = 1; \
160 } \
161 return 0; \
162}
163
164WM8995_REGULATOR_EVENT(0)
165WM8995_REGULATOR_EVENT(1)
166WM8995_REGULATOR_EVENT(2)
167WM8995_REGULATOR_EVENT(3)
168WM8995_REGULATOR_EVENT(4)
169WM8995_REGULATOR_EVENT(5)
170WM8995_REGULATOR_EVENT(6)
171WM8995_REGULATOR_EVENT(7)
172
131static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1); 173static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1);
132static const DECLARE_TLV_DB_SCALE(in1lr_pga_tlv, -1650, 150, 0); 174static const DECLARE_TLV_DB_SCALE(in1lr_pga_tlv, -1650, 150, 0);
133static const DECLARE_TLV_DB_SCALE(in1l_boost_tlv, 0, 600, 0); 175static const DECLARE_TLV_DB_SCALE(in1l_boost_tlv, 0, 600, 0);
@@ -1483,6 +1525,11 @@ static int wm8995_set_bias_level(struct snd_soc_codec *codec,
1483 break; 1525 break;
1484 case SND_SOC_BIAS_STANDBY: 1526 case SND_SOC_BIAS_STANDBY:
1485 if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) { 1527 if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
1528 ret = regulator_bulk_enable(ARRAY_SIZE(wm8995->supplies),
1529 wm8995->supplies);
1530 if (ret)
1531 return ret;
1532
1486 ret = snd_soc_cache_sync(codec); 1533 ret = snd_soc_cache_sync(codec);
1487 if (ret) { 1534 if (ret) {
1488 dev_err(codec->dev, 1535 dev_err(codec->dev,
@@ -1492,13 +1539,13 @@ static int wm8995_set_bias_level(struct snd_soc_codec *codec,
1492 1539
1493 snd_soc_update_bits(codec, WM8995_POWER_MANAGEMENT_1, 1540 snd_soc_update_bits(codec, WM8995_POWER_MANAGEMENT_1,
1494 WM8995_BG_ENA_MASK, WM8995_BG_ENA); 1541 WM8995_BG_ENA_MASK, WM8995_BG_ENA);
1495
1496 } 1542 }
1497 break; 1543 break;
1498 case SND_SOC_BIAS_OFF: 1544 case SND_SOC_BIAS_OFF:
1499 snd_soc_update_bits(codec, WM8995_POWER_MANAGEMENT_1, 1545 snd_soc_update_bits(codec, WM8995_POWER_MANAGEMENT_1,
1500 WM8995_BG_ENA_MASK, 0); 1546 WM8995_BG_ENA_MASK, 0);
1501 codec->cache_sync = 1; 1547 regulator_bulk_disable(ARRAY_SIZE(wm8995->supplies),
1548 wm8995->supplies);
1502 break; 1549 break;
1503 } 1550 }
1504 1551
@@ -1537,10 +1584,12 @@ static int wm8995_remove(struct snd_soc_codec *codec)
1537static int wm8995_probe(struct snd_soc_codec *codec) 1584static int wm8995_probe(struct snd_soc_codec *codec)
1538{ 1585{
1539 struct wm8995_priv *wm8995; 1586 struct wm8995_priv *wm8995;
1587 int i;
1540 int ret; 1588 int ret;
1541 1589
1542 codec->dapm.idle_bias_off = 1; 1590 codec->dapm.idle_bias_off = 1;
1543 wm8995 = snd_soc_codec_get_drvdata(codec); 1591 wm8995 = snd_soc_codec_get_drvdata(codec);
1592 wm8995->codec = codec;
1544 1593
1545 ret = snd_soc_codec_set_cache_io(codec, 16, 16, wm8995->control_type); 1594 ret = snd_soc_codec_set_cache_io(codec, 16, 16, wm8995->control_type);
1546 if (ret < 0) { 1595 if (ret < 0) {
@@ -1548,21 +1597,58 @@ static int wm8995_probe(struct snd_soc_codec *codec)
1548 return ret; 1597 return ret;
1549 } 1598 }
1550 1599
1600 for (i = 0; i < ARRAY_SIZE(wm8995->supplies); i++)
1601 wm8995->supplies[i].supply = wm8995_supply_names[i];
1602
1603 ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8995->supplies),
1604 wm8995->supplies);
1605 if (ret) {
1606 dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
1607 return ret;
1608 }
1609
1610 wm8995->disable_nb[0].notifier_call = wm8995_regulator_event_0;
1611 wm8995->disable_nb[1].notifier_call = wm8995_regulator_event_1;
1612 wm8995->disable_nb[2].notifier_call = wm8995_regulator_event_2;
1613 wm8995->disable_nb[3].notifier_call = wm8995_regulator_event_3;
1614 wm8995->disable_nb[4].notifier_call = wm8995_regulator_event_4;
1615 wm8995->disable_nb[5].notifier_call = wm8995_regulator_event_5;
1616 wm8995->disable_nb[6].notifier_call = wm8995_regulator_event_6;
1617 wm8995->disable_nb[7].notifier_call = wm8995_regulator_event_7;
1618
1619 /* This should really be moved into the regulator core */
1620 for (i = 0; i < ARRAY_SIZE(wm8995->supplies); i++) {
1621 ret = regulator_register_notifier(wm8995->supplies[i].consumer,
1622 &wm8995->disable_nb[i]);
1623 if (ret) {
1624 dev_err(codec->dev,
1625 "Failed to register regulator notifier: %d\n",
1626 ret);
1627 }
1628 }
1629
1630 ret = regulator_bulk_enable(ARRAY_SIZE(wm8995->supplies),
1631 wm8995->supplies);
1632 if (ret) {
1633 dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
1634 goto err_reg_get;
1635 }
1636
1551 ret = snd_soc_read(codec, WM8995_SOFTWARE_RESET); 1637 ret = snd_soc_read(codec, WM8995_SOFTWARE_RESET);
1552 if (ret < 0) { 1638 if (ret < 0) {
1553 dev_err(codec->dev, "Failed to read device ID: %d\n", ret); 1639 dev_err(codec->dev, "Failed to read device ID: %d\n", ret);
1554 return ret; 1640 goto err_reg_enable;
1555 } 1641 }
1556 1642
1557 if (ret != 0x8995) { 1643 if (ret != 0x8995) {
1558 dev_err(codec->dev, "Invalid device ID: %#x\n", ret); 1644 dev_err(codec->dev, "Invalid device ID: %#x\n", ret);
1559 return -EINVAL; 1645 goto err_reg_enable;
1560 } 1646 }
1561 1647
1562 ret = snd_soc_write(codec, WM8995_SOFTWARE_RESET, 0); 1648 ret = snd_soc_write(codec, WM8995_SOFTWARE_RESET, 0);
1563 if (ret < 0) { 1649 if (ret < 0) {
1564 dev_err(codec->dev, "Failed to issue reset: %d\n", ret); 1650 dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
1565 return ret; 1651 goto err_reg_enable;
1566 } 1652 }
1567 1653
1568 wm8995_set_bias_level(codec, SND_SOC_BIAS_STANDBY); 1654 wm8995_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
@@ -1597,6 +1683,12 @@ static int wm8995_probe(struct snd_soc_codec *codec)
1597 ARRAY_SIZE(wm8995_intercon)); 1683 ARRAY_SIZE(wm8995_intercon));
1598 1684
1599 return 0; 1685 return 0;
1686
1687err_reg_enable:
1688 regulator_bulk_disable(ARRAY_SIZE(wm8995->supplies), wm8995->supplies);
1689err_reg_get:
1690 regulator_bulk_free(ARRAY_SIZE(wm8995->supplies), wm8995->supplies);
1691 return ret;
1600} 1692}
1601 1693
1602#define WM8995_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ 1694#define WM8995_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\