ASoC: cs4271: add regulator consumer support

The cs4271 has three power domains: vd, vl and va.
Enable them all, as long as the codec is in use.

While at it, factored out the reset code into its own function.

Signed-off-by: Pascal Huerst <pascal.huerst@gmail.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
This commit is contained in:
Pascal Huerst 2016-02-16 16:19:06 +01:00 committed by Mark Brown
parent 92e963f50f
commit 9a397f4736
2 changed files with 68 additions and 8 deletions

View File

@ -33,12 +33,19 @@ Optional properties:
Note that this is not needed in case the clocks are stable Note that this is not needed in case the clocks are stable
throughout the entire runtime of the codec. throughout the entire runtime of the codec.
- vd-supply: Digital power
- vl-supply: Logic power
- va-supply: Analog Power
Examples: Examples:
codec_i2c: cs4271@10 { codec_i2c: cs4271@10 {
compatible = "cirrus,cs4271"; compatible = "cirrus,cs4271";
reg = <0x10>; reg = <0x10>;
reset-gpio = <&gpio 23 0>; reset-gpio = <&gpio 23 0>;
vd-supply = <&vdd_3v3_reg>;
vl-supply = <&vdd_3v3_reg>;
va-supply = <&vdd_3v3_reg>;
}; };
codec_spi: cs4271@0 { codec_spi: cs4271@0 {

View File

@ -26,6 +26,7 @@
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/of_gpio.h> #include <linux/of_gpio.h>
#include <linux/regulator/consumer.h>
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/soc.h> #include <sound/soc.h>
#include <sound/tlv.h> #include <sound/tlv.h>
@ -157,6 +158,10 @@ static bool cs4271_volatile_reg(struct device *dev, unsigned int reg)
return reg == CS4271_CHIPID; return reg == CS4271_CHIPID;
} }
static const char * const supply_names[] = {
"vd", "vl", "va"
};
struct cs4271_private { struct cs4271_private {
unsigned int mclk; unsigned int mclk;
bool master; bool master;
@ -170,6 +175,7 @@ struct cs4271_private {
int gpio_disable; int gpio_disable;
/* enable soft reset workaround */ /* enable soft reset workaround */
bool enable_soft_reset; bool enable_soft_reset;
struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
}; };
static const struct snd_soc_dapm_widget cs4271_dapm_widgets[] = { static const struct snd_soc_dapm_widget cs4271_dapm_widgets[] = {
@ -487,6 +493,20 @@ static struct snd_soc_dai_driver cs4271_dai = {
.symmetric_rates = 1, .symmetric_rates = 1,
}; };
static int cs4271_reset(struct snd_soc_codec *codec)
{
struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
if (gpio_is_valid(cs4271->gpio_nreset)) {
gpio_set_value(cs4271->gpio_nreset, 0);
mdelay(1);
gpio_set_value(cs4271->gpio_nreset, 1);
mdelay(1);
}
return 0;
}
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int cs4271_soc_suspend(struct snd_soc_codec *codec) static int cs4271_soc_suspend(struct snd_soc_codec *codec)
{ {
@ -499,6 +519,9 @@ static int cs4271_soc_suspend(struct snd_soc_codec *codec)
if (ret < 0) if (ret < 0)
return ret; return ret;
regcache_mark_dirty(cs4271->regmap);
regulator_bulk_disable(ARRAY_SIZE(cs4271->supplies), cs4271->supplies);
return 0; return 0;
} }
@ -507,6 +530,16 @@ static int cs4271_soc_resume(struct snd_soc_codec *codec)
int ret; int ret;
struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec); struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
ret = regulator_bulk_enable(ARRAY_SIZE(cs4271->supplies),
cs4271->supplies);
if (ret < 0) {
dev_err(codec->dev, "Failed to enable regulators: %d\n", ret);
return ret;
}
/* Do a proper reset after power up */
cs4271_reset(codec);
/* Restore codec state */ /* Restore codec state */
ret = regcache_sync(cs4271->regmap); ret = regcache_sync(cs4271->regmap);
if (ret < 0) if (ret < 0)
@ -553,19 +586,24 @@ static int cs4271_codec_probe(struct snd_soc_codec *codec)
} }
#endif #endif
ret = regulator_bulk_enable(ARRAY_SIZE(cs4271->supplies),
cs4271->supplies);
if (ret < 0) {
dev_err(codec->dev, "Failed to enable regulators: %d\n", ret);
return ret;
}
if (cs4271plat) { if (cs4271plat) {
amutec_eq_bmutec = cs4271plat->amutec_eq_bmutec; amutec_eq_bmutec = cs4271plat->amutec_eq_bmutec;
cs4271->enable_soft_reset = cs4271plat->enable_soft_reset; cs4271->enable_soft_reset = cs4271plat->enable_soft_reset;
} }
if (gpio_is_valid(cs4271->gpio_nreset)) { /* Reset codec */
/* Reset codec */ cs4271_reset(codec);
gpio_direction_output(cs4271->gpio_nreset, 0);
mdelay(1); ret = regcache_sync(cs4271->regmap);
gpio_set_value(cs4271->gpio_nreset, 1); if (ret < 0)
/* Give the codec time to wake up */ return ret;
mdelay(1);
}
ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2, ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2,
CS4271_MODE2_PDN | CS4271_MODE2_CPEN, CS4271_MODE2_PDN | CS4271_MODE2_CPEN,
@ -595,6 +633,9 @@ static int cs4271_codec_remove(struct snd_soc_codec *codec)
/* Set codec to the reset state */ /* Set codec to the reset state */
gpio_set_value(cs4271->gpio_nreset, 0); gpio_set_value(cs4271->gpio_nreset, 0);
regcache_mark_dirty(cs4271->regmap);
regulator_bulk_disable(ARRAY_SIZE(cs4271->supplies), cs4271->supplies);
return 0; return 0;
}; };
@ -617,6 +658,7 @@ static int cs4271_common_probe(struct device *dev,
{ {
struct cs4271_platform_data *cs4271plat = dev->platform_data; struct cs4271_platform_data *cs4271plat = dev->platform_data;
struct cs4271_private *cs4271; struct cs4271_private *cs4271;
int i, ret;
cs4271 = devm_kzalloc(dev, sizeof(*cs4271), GFP_KERNEL); cs4271 = devm_kzalloc(dev, sizeof(*cs4271), GFP_KERNEL);
if (!cs4271) if (!cs4271)
@ -638,6 +680,17 @@ static int cs4271_common_probe(struct device *dev,
return ret; return ret;
} }
for (i = 0; i < ARRAY_SIZE(supply_names); i++)
cs4271->supplies[i].supply = supply_names[i];
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(cs4271->supplies),
cs4271->supplies);
if (ret < 0) {
dev_err(dev, "Failed to get regulators: %d\n", ret);
return ret;
}
*c = cs4271; *c = cs4271;
return 0; return 0;
} }