mirror of
https://github.com/torvalds/linux.git
synced 2024-11-19 10:31:48 +00:00
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>
This commit is contained in:
parent
a1b3b5eeee
commit
219d8df868
@ -19,6 +19,7 @@
|
|||||||
#include <linux/pm.h>
|
#include <linux/pm.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/spi/spi.h>
|
#include <linux/spi/spi.h>
|
||||||
|
#include <linux/regulator/consumer.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <sound/core.h>
|
#include <sound/core.h>
|
||||||
#include <sound/pcm.h>
|
#include <sound/pcm.h>
|
||||||
@ -30,6 +31,18 @@
|
|||||||
|
|
||||||
#include "wm8995.h"
|
#include "wm8995.h"
|
||||||
|
|
||||||
|
#define WM8995_NUM_SUPPLIES 8
|
||||||
|
static const char *wm8995_supply_names[WM8995_NUM_SUPPLIES] = {
|
||||||
|
"DCVDD",
|
||||||
|
"DBVDD1",
|
||||||
|
"DBVDD2",
|
||||||
|
"DBVDD3",
|
||||||
|
"AVDD1",
|
||||||
|
"AVDD2",
|
||||||
|
"CPVDD",
|
||||||
|
"MICVDD"
|
||||||
|
};
|
||||||
|
|
||||||
static const u16 wm8995_reg_defs[WM8995_MAX_REGISTER + 1] = {
|
static const u16 wm8995_reg_defs[WM8995_MAX_REGISTER + 1] = {
|
||||||
[0] = 0x8995, [5] = 0x0100, [16] = 0x000b, [17] = 0x000b,
|
[0] = 0x8995, [5] = 0x0100, [16] = 0x000b, [17] = 0x000b,
|
||||||
[24] = 0x02c0, [25] = 0x02c0, [26] = 0x02c0, [27] = 0x02c0,
|
[24] = 0x02c0, [25] = 0x02c0, [26] = 0x02c0, [27] = 0x02c0,
|
||||||
@ -126,8 +139,37 @@ struct wm8995_priv {
|
|||||||
int mclk[2];
|
int mclk[2];
|
||||||
int aifclk[2];
|
int aifclk[2];
|
||||||
struct fll_config fll[2], fll_suspend[2];
|
struct fll_config fll[2], fll_suspend[2];
|
||||||
|
struct regulator_bulk_data supplies[WM8995_NUM_SUPPLIES];
|
||||||
|
struct notifier_block disable_nb[WM8995_NUM_SUPPLIES];
|
||||||
|
struct snd_soc_codec *codec;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We can't use the same notifier block for more than one supply and
|
||||||
|
* there's no way I can see to get from a callback to the caller
|
||||||
|
* except container_of().
|
||||||
|
*/
|
||||||
|
#define WM8995_REGULATOR_EVENT(n) \
|
||||||
|
static int wm8995_regulator_event_##n(struct notifier_block *nb, \
|
||||||
|
unsigned long event, void *data) \
|
||||||
|
{ \
|
||||||
|
struct wm8995_priv *wm8995 = container_of(nb, struct wm8995_priv, \
|
||||||
|
disable_nb[n]); \
|
||||||
|
if (event & REGULATOR_EVENT_DISABLE) { \
|
||||||
|
wm8995->codec->cache_sync = 1; \
|
||||||
|
} \
|
||||||
|
return 0; \
|
||||||
|
}
|
||||||
|
|
||||||
|
WM8995_REGULATOR_EVENT(0)
|
||||||
|
WM8995_REGULATOR_EVENT(1)
|
||||||
|
WM8995_REGULATOR_EVENT(2)
|
||||||
|
WM8995_REGULATOR_EVENT(3)
|
||||||
|
WM8995_REGULATOR_EVENT(4)
|
||||||
|
WM8995_REGULATOR_EVENT(5)
|
||||||
|
WM8995_REGULATOR_EVENT(6)
|
||||||
|
WM8995_REGULATOR_EVENT(7)
|
||||||
|
|
||||||
static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1);
|
static const DECLARE_TLV_DB_SCALE(digital_tlv, -7200, 75, 1);
|
||||||
static const DECLARE_TLV_DB_SCALE(in1lr_pga_tlv, -1650, 150, 0);
|
static const DECLARE_TLV_DB_SCALE(in1lr_pga_tlv, -1650, 150, 0);
|
||||||
static const DECLARE_TLV_DB_SCALE(in1l_boost_tlv, 0, 600, 0);
|
static 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,
|
|||||||
break;
|
break;
|
||||||
case SND_SOC_BIAS_STANDBY:
|
case SND_SOC_BIAS_STANDBY:
|
||||||
if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
|
if (codec->dapm.bias_level == SND_SOC_BIAS_OFF) {
|
||||||
|
ret = regulator_bulk_enable(ARRAY_SIZE(wm8995->supplies),
|
||||||
|
wm8995->supplies);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
ret = snd_soc_cache_sync(codec);
|
ret = snd_soc_cache_sync(codec);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(codec->dev,
|
dev_err(codec->dev,
|
||||||
@ -1492,13 +1539,13 @@ static int wm8995_set_bias_level(struct snd_soc_codec *codec,
|
|||||||
|
|
||||||
snd_soc_update_bits(codec, WM8995_POWER_MANAGEMENT_1,
|
snd_soc_update_bits(codec, WM8995_POWER_MANAGEMENT_1,
|
||||||
WM8995_BG_ENA_MASK, WM8995_BG_ENA);
|
WM8995_BG_ENA_MASK, WM8995_BG_ENA);
|
||||||
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SND_SOC_BIAS_OFF:
|
case SND_SOC_BIAS_OFF:
|
||||||
snd_soc_update_bits(codec, WM8995_POWER_MANAGEMENT_1,
|
snd_soc_update_bits(codec, WM8995_POWER_MANAGEMENT_1,
|
||||||
WM8995_BG_ENA_MASK, 0);
|
WM8995_BG_ENA_MASK, 0);
|
||||||
codec->cache_sync = 1;
|
regulator_bulk_disable(ARRAY_SIZE(wm8995->supplies),
|
||||||
|
wm8995->supplies);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1537,10 +1584,12 @@ static int wm8995_remove(struct snd_soc_codec *codec)
|
|||||||
static int wm8995_probe(struct snd_soc_codec *codec)
|
static int wm8995_probe(struct snd_soc_codec *codec)
|
||||||
{
|
{
|
||||||
struct wm8995_priv *wm8995;
|
struct wm8995_priv *wm8995;
|
||||||
|
int i;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
codec->dapm.idle_bias_off = 1;
|
codec->dapm.idle_bias_off = 1;
|
||||||
wm8995 = snd_soc_codec_get_drvdata(codec);
|
wm8995 = snd_soc_codec_get_drvdata(codec);
|
||||||
|
wm8995->codec = codec;
|
||||||
|
|
||||||
ret = snd_soc_codec_set_cache_io(codec, 16, 16, wm8995->control_type);
|
ret = snd_soc_codec_set_cache_io(codec, 16, 16, wm8995->control_type);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
@ -1548,21 +1597,58 @@ static int wm8995_probe(struct snd_soc_codec *codec)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(wm8995->supplies); i++)
|
||||||
|
wm8995->supplies[i].supply = wm8995_supply_names[i];
|
||||||
|
|
||||||
|
ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(wm8995->supplies),
|
||||||
|
wm8995->supplies);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(codec->dev, "Failed to request supplies: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
wm8995->disable_nb[0].notifier_call = wm8995_regulator_event_0;
|
||||||
|
wm8995->disable_nb[1].notifier_call = wm8995_regulator_event_1;
|
||||||
|
wm8995->disable_nb[2].notifier_call = wm8995_regulator_event_2;
|
||||||
|
wm8995->disable_nb[3].notifier_call = wm8995_regulator_event_3;
|
||||||
|
wm8995->disable_nb[4].notifier_call = wm8995_regulator_event_4;
|
||||||
|
wm8995->disable_nb[5].notifier_call = wm8995_regulator_event_5;
|
||||||
|
wm8995->disable_nb[6].notifier_call = wm8995_regulator_event_6;
|
||||||
|
wm8995->disable_nb[7].notifier_call = wm8995_regulator_event_7;
|
||||||
|
|
||||||
|
/* This should really be moved into the regulator core */
|
||||||
|
for (i = 0; i < ARRAY_SIZE(wm8995->supplies); i++) {
|
||||||
|
ret = regulator_register_notifier(wm8995->supplies[i].consumer,
|
||||||
|
&wm8995->disable_nb[i]);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(codec->dev,
|
||||||
|
"Failed to register regulator notifier: %d\n",
|
||||||
|
ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = regulator_bulk_enable(ARRAY_SIZE(wm8995->supplies),
|
||||||
|
wm8995->supplies);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(codec->dev, "Failed to enable supplies: %d\n", ret);
|
||||||
|
goto err_reg_get;
|
||||||
|
}
|
||||||
|
|
||||||
ret = snd_soc_read(codec, WM8995_SOFTWARE_RESET);
|
ret = snd_soc_read(codec, WM8995_SOFTWARE_RESET);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(codec->dev, "Failed to read device ID: %d\n", ret);
|
dev_err(codec->dev, "Failed to read device ID: %d\n", ret);
|
||||||
return ret;
|
goto err_reg_enable;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret != 0x8995) {
|
if (ret != 0x8995) {
|
||||||
dev_err(codec->dev, "Invalid device ID: %#x\n", ret);
|
dev_err(codec->dev, "Invalid device ID: %#x\n", ret);
|
||||||
return -EINVAL;
|
goto err_reg_enable;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = snd_soc_write(codec, WM8995_SOFTWARE_RESET, 0);
|
ret = snd_soc_write(codec, WM8995_SOFTWARE_RESET, 0);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
|
dev_err(codec->dev, "Failed to issue reset: %d\n", ret);
|
||||||
return ret;
|
goto err_reg_enable;
|
||||||
}
|
}
|
||||||
|
|
||||||
wm8995_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
wm8995_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
|
||||||
@ -1597,6 +1683,12 @@ static int wm8995_probe(struct snd_soc_codec *codec)
|
|||||||
ARRAY_SIZE(wm8995_intercon));
|
ARRAY_SIZE(wm8995_intercon));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
err_reg_enable:
|
||||||
|
regulator_bulk_disable(ARRAY_SIZE(wm8995->supplies), wm8995->supplies);
|
||||||
|
err_reg_get:
|
||||||
|
regulator_bulk_free(ARRAY_SIZE(wm8995->supplies), wm8995->supplies);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define WM8995_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
|
#define WM8995_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
|
||||||
|
Loading…
Reference in New Issue
Block a user