[PATCH] ALSA: ASoc: Add regulator support to CS4270 codec driver
Daniel Mack
daniel at caiaq.de
Wed Nov 25 15:31:25 CET 2009
Signed-off-by: Daniel Mack <daniel at caiaq.de>
Cc: Mark Brown <broonie at opensource.wolfsonmicro.com>
Cc: Timur Tabi <timur at freescale.com>
Cc: Liam Girdwood <lrg at slimlogic.co.uk>
---
sound/soc/codecs/cs4270.c | 114 ++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 112 insertions(+), 2 deletions(-)
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
index 8069023..34cd97d 100644
--- a/sound/soc/codecs/cs4270.c
+++ b/sound/soc/codecs/cs4270.c
@@ -28,6 +28,7 @@
#include <sound/initval.h>
#include <linux/i2c.h>
#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
#include "cs4270.h"
@@ -114,6 +115,12 @@ struct cs4270_private {
unsigned int mode; /* The mode (I2S or left-justified) */
unsigned int slave_mode;
unsigned int manual_mute;
+
+ /* power domain regulators */
+ struct regulator *va_reg;
+ struct regulator *vd_reg;
+ struct regulator *vlc_reg;
+ bool va_reg_enabled;
};
/**
@@ -458,6 +465,50 @@ static int cs4270_hw_params(struct snd_pcm_substream *substream,
}
/**
+ * cs4270_set_bias_level - catches BIAS level changes
+ * @dai: the SOC DAI
+ * @level: the new BIAS level
+ *
+ * This function controls the voltage regulators to properly
+ * power up/down the voltage regulator domains
+ */
+
+static int cs4270_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ int ret;
+ struct cs4270_private *cs4270 = codec->private_data;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ if (!cs4270->va_reg_enabled) {
+ ret = regulator_enable(cs4270->va_reg);
+ if (ret < 0)
+ return ret;
+
+ cs4270->va_reg_enabled = true;
+ }
+
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ case SND_SOC_BIAS_OFF:
+ if (cs4270->va_reg_enabled) {
+ ret = regulator_disable(cs4270->va_reg);
+ if (ret < 0)
+ return ret;
+
+ cs4270->va_reg_enabled = false;
+ }
+
+ break;
+ }
+
+ codec->bias_level = level;
+ return 0;
+}
+
+/**
* cs4270_dai_mute - enable/disable the CS4270 external mute
* @dai: the SOC DAI
* @mute: 0 = disable mute, 1 = enable mute
@@ -579,6 +630,7 @@ static int cs4270_probe(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
struct snd_soc_codec *codec = cs4270_codec;
+ struct cs4270_private *cs4270 = codec->private_data;
int ret;
/* Connect the codec to the socdev. snd_soc_new_pcms() needs this. */
@@ -599,6 +651,25 @@ static int cs4270_probe(struct platform_device *pdev)
goto error_free_pcms;
}
+ /* get the power supply regulators */
+ cs4270->va_reg = regulator_get(codec->dev, "va");
+ cs4270->vd_reg = regulator_get(codec->dev, "vd");
+ cs4270->vlc_reg = regulator_get(codec->dev, "vlc");
+
+ if (IS_ERR(cs4270->va_reg) ||
+ IS_ERR(cs4270->vd_reg) ||
+ IS_ERR(cs4270->vlc_reg)) {
+ dev_err(codec->dev,
+ "failed to get supplies for device %s\n",
+ dev_name(codec->dev));
+ goto error_free_pcms;
+ }
+
+ regulator_enable(cs4270->vd_reg);
+ regulator_enable(cs4270->vlc_reg);
+
+ cs4270_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
/* And finally, register the socdev */
ret = snd_soc_init_card(socdev);
if (ret < 0) {
@@ -609,6 +680,10 @@ static int cs4270_probe(struct platform_device *pdev)
return 0;
error_free_pcms:
+ regulator_put(cs4270->va_reg);
+ regulator_put(cs4270->vd_reg);
+ regulator_put(cs4270->vlc_reg);
+
snd_soc_free_pcms(socdev);
return ret;
@@ -623,9 +698,21 @@ error_free_pcms:
static int cs4270_remove(struct platform_device *pdev)
{
struct snd_soc_device *socdev = platform_get_drvdata(pdev);
+ struct snd_soc_codec *codec = cs4270_codec;
+ struct cs4270_private *cs4270 = codec->private_data;
snd_soc_free_pcms(socdev);
+ if (cs4270->va_reg_enabled)
+ regulator_disable(cs4270->va_reg);
+
+ regulator_disable(cs4270->vd_reg);
+ regulator_disable(cs4270->vlc_reg);
+
+ regulator_put(cs4270->va_reg);
+ regulator_put(cs4270->vd_reg);
+ regulator_put(cs4270->vlc_reg);
+
return 0;
};
@@ -699,6 +786,8 @@ static int cs4270_i2c_probe(struct i2c_client *i2c_client,
codec->write = cs4270_i2c_write;
codec->reg_cache = cs4270->reg_cache;
codec->reg_cache_size = CS4270_NUMREGS;
+ codec->bias_level = SND_SOC_BIAS_OFF;
+ codec->set_bias_level = cs4270_set_bias_level;
/* The I2C interface is set up, so pre-fill our register cache */
@@ -822,17 +911,38 @@ static int cs4270_i2c_resume(struct i2c_client *client)
static int cs4270_soc_suspend(struct platform_device *pdev, pm_message_t mesg)
{
struct snd_soc_codec *codec = cs4270_codec;
- int reg = snd_soc_read(codec, CS4270_PWRCTL) | CS4270_PWRCTL_PDN_ALL;
+ struct cs4270_private *cs4270 = codec->private_data;
+ int reg, ret;
- return snd_soc_write(codec, CS4270_PWRCTL, reg);
+ reg = snd_soc_read(codec, CS4270_PWRCTL) | CS4270_PWRCTL_PDN_ALL;
+ if (reg < 0)
+ return reg;
+
+ ret = snd_soc_write(codec, CS4270_PWRCTL, reg);
+ if (ret < 0)
+ return ret;
+
+ ret = cs4270_set_bias_level(codec, SND_SOC_BIAS_OFF);
+ if (ret < 0)
+ return ret;
+
+ regulator_disable(cs4270->vd_reg);
+ regulator_disable(cs4270->vlc_reg);
+
+ return 0;
}
static int cs4270_soc_resume(struct platform_device *pdev)
{
struct snd_soc_codec *codec = cs4270_codec;
+ struct cs4270_private *cs4270 = codec->private_data;
struct i2c_client *i2c_client = codec->control_data;
int reg;
+ regulator_enable(cs4270->vd_reg);
+ regulator_enable(cs4270->vlc_reg);
+ cs4270_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
/* In case the device was put to hard reset during sleep, we need to
* wait 500ns here before any I2C communication. */
ndelay(500);
--
1.6.5.2
More information about the Alsa-devel
mailing list