summaryrefslogtreecommitdiff
path: root/sound/soc/codecs/max98396.c
diff options
context:
space:
mode:
authorDaniel Mack <daniel@zonque.org>2022-06-24 13:47:08 +0300
committerMark Brown <broonie@kernel.org>2022-06-27 15:16:09 +0300
commit703ee0557f8921c96e8c42f832b3bd69b7bfb262 (patch)
treef99ee03fcd129ac5d64a03a2398ca4d3c216d5ee /sound/soc/codecs/max98396.c
parent0ce44afd29768e893ff9112ba208271e0d558780 (diff)
downloadlinux-703ee0557f8921c96e8c42f832b3bd69b7bfb262.tar.xz
ASoC: max98396: add voltage regulators
The device has up to 5 potentially independent power supplies: AVDD, DVDD, DVVDIO, VBAT and PVDD. The former 3 are mandatory for the device to function. One of VBAT and PVDD should also be made available. Regulators are enabled during probe time and will stay active except when in suspend mode. Futher, the chip needs to be informed about the presence of VBAT through a bit in register 0x20a0. Signed-off-by: Daniel Mack <daniel@zonque.org> Link: https://lore.kernel.org/r/20220624104712.1934484-5-daniel@zonque.org Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'sound/soc/codecs/max98396.c')
-rw-r--r--sound/soc/codecs/max98396.c117
1 files changed, 116 insertions, 1 deletions
diff --git a/sound/soc/codecs/max98396.c b/sound/soc/codecs/max98396.c
index 56eb62bb041f..06ac637f2696 100644
--- a/sound/soc/codecs/max98396.c
+++ b/sound/soc/codecs/max98396.c
@@ -5,11 +5,18 @@
#include <linux/i2c.h>
#include <linux/module.h>
#include <sound/pcm_params.h>
+#include <linux/regulator/consumer.h>
#include <sound/soc.h>
#include <linux/gpio.h>
#include <sound/tlv.h>
#include "max98396.h"
+static const char * const max98396_core_supplies[MAX98396_NUM_CORE_SUPPLIES] = {
+ "avdd",
+ "dvdd",
+ "dvddio",
+};
+
static struct reg_default max98396_reg[] = {
{MAX98396_R2000_SW_RESET, 0x00},
{MAX98396_R2001_INT_RAW1, 0x00},
@@ -1329,6 +1336,12 @@ static int max98396_probe(struct snd_soc_component *component)
regmap_write(max98396->regmap,
MAX98397_R2057_PCM_RX_SRC2, 0x10);
}
+ /* Supply control */
+ regmap_update_bits(max98396->regmap,
+ MAX98396_R20A0_AMP_SUPPLY_CTL,
+ MAX98396_AMP_SUPPLY_NOVBAT,
+ (max98396->vbat == NULL) ?
+ MAX98396_AMP_SUPPLY_NOVBAT : 0);
/* Enable DC blocker */
regmap_update_bits(max98396->regmap,
MAX98396_R2092_AMP_DSP_CFG, 1, 1);
@@ -1424,12 +1437,38 @@ static int max98396_suspend(struct device *dev)
regcache_cache_only(max98396->regmap, true);
regcache_mark_dirty(max98396->regmap);
+ regulator_bulk_disable(MAX98396_NUM_CORE_SUPPLIES,
+ max98396->core_supplies);
+ if (max98396->pvdd)
+ regulator_disable(max98396->pvdd);
+
+ if (max98396->vbat)
+ regulator_disable(max98396->vbat);
+
return 0;
}
static int max98396_resume(struct device *dev)
{
struct max98396_priv *max98396 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regulator_bulk_enable(MAX98396_NUM_CORE_SUPPLIES,
+ max98396->core_supplies);
+ if (ret < 0)
+ return ret;
+
+ if (max98396->pvdd) {
+ ret = regulator_enable(max98396->pvdd);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (max98396->vbat) {
+ ret = regulator_enable(max98396->vbat);
+ if (ret < 0)
+ return ret;
+ }
regcache_cache_only(max98396->regmap, false);
max98396_reset(max98396, dev);
@@ -1513,11 +1552,24 @@ static void max98396_read_device_property(struct device *dev,
max98396->bypass_slot = 0;
}
+static void max98396_core_supplies_disable(void *priv)
+{
+ struct max98396_priv *max98396 = priv;
+
+ regulator_bulk_disable(MAX98396_NUM_CORE_SUPPLIES,
+ max98396->core_supplies);
+}
+
+static void max98396_supply_disable(void *r)
+{
+ regulator_disable((struct regulator *) r);
+}
+
static int max98396_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct max98396_priv *max98396 = NULL;
- int ret, reg;
+ int i, ret, reg;
max98396 = devm_kzalloc(&i2c->dev, sizeof(*max98396), GFP_KERNEL);
@@ -1543,6 +1595,69 @@ static int max98396_i2c_probe(struct i2c_client *i2c,
return ret;
}
+ /* Obtain regulator supplies */
+ for (i = 0; i < MAX98396_NUM_CORE_SUPPLIES; i++)
+ max98396->core_supplies[i].supply = max98396_core_supplies[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev, MAX98396_NUM_CORE_SUPPLIES,
+ max98396->core_supplies);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to request core supplies: %d\n", ret);
+ return ret;
+ }
+
+ max98396->vbat = devm_regulator_get_optional(&i2c->dev, "vbat");
+ if (IS_ERR(max98396->vbat)) {
+ if (PTR_ERR(max98396->vbat) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ max98396->vbat = NULL;
+ }
+
+ max98396->pvdd = devm_regulator_get_optional(&i2c->dev, "pvdd");
+ if (IS_ERR(max98396->pvdd)) {
+ if (PTR_ERR(max98396->pvdd) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ max98396->pvdd = NULL;
+ }
+
+ ret = regulator_bulk_enable(MAX98396_NUM_CORE_SUPPLIES,
+ max98396->core_supplies);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Unable to enable core supplies: %d", ret);
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(&i2c->dev, max98396_core_supplies_disable,
+ max98396);
+ if (ret < 0)
+ return ret;
+
+ if (max98396->pvdd) {
+ ret = regulator_enable(max98396->pvdd);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_add_action_or_reset(&i2c->dev,
+ max98396_supply_disable,
+ max98396->pvdd);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (max98396->vbat) {
+ ret = regulator_enable(max98396->vbat);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_add_action_or_reset(&i2c->dev,
+ max98396_supply_disable,
+ max98396->vbat);
+ if (ret < 0)
+ return ret;
+ }
+
/* update interleave mode info */
if (device_property_read_bool(&i2c->dev, "adi,interleave_mode"))
max98396->interleave_mode = true;