]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ASoC: codecs: nau8822: add support for supply regulators
authorAlexey Charkov <alchark@flipper.net>
Mon, 25 May 2026 09:20:46 +0000 (13:20 +0400)
committerMark Brown <broonie@kernel.org>
Mon, 1 Jun 2026 14:13:56 +0000 (15:13 +0100)
NAU8822 has four power supply pins: VDDA, VDDB, VDDC, and VDDSPK, which
need to be online and stable before communication with the device is
attempted.

Request and enable these regulators at init time, if provided. Also wait
for 100 us after powering up the supply regulators before attempting to
access the device registers, as recommended by the datasheet.

This helps avoid -ENXIO errors when the codec is probed before the
regulators are ready.

Signed-off-by: Alexey Charkov <alchark@flipper.net>
Link: https://patch.msgid.link/20260525-nau8822-reg-v2-2-7d37ae393e46@flipper.net
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/codecs/nau8822.c
sound/soc/codecs/nau8822.h

index a11759f85eaca1065d6fc09d5e20584bef38002d..cd4a7de47939b6a2f6bdb9659ddf5130c0eee89c 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/pm.h>
 #include <linux/i2c.h>
 #include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -108,6 +109,10 @@ static const struct reg_default nau8822_reg_defaults[] = {
        { NAU8822_REG_OUTPUT_TIEOFF, 0x0000 },
 };
 
+static const char * const nau8822_supply_names[NAU8822_NUM_SUPPLIES] = {
+       "vdda", "vddb", "vddc", "vddspk",
+};
+
 static bool nau8822_readable_reg(struct device *dev, unsigned int reg)
 {
        switch (reg) {
@@ -1056,6 +1061,7 @@ static int nau8822_suspend(struct snd_soc_component *component)
        struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
 
        snd_soc_dapm_force_bias_level(dapm, SND_SOC_BIAS_OFF);
+       regulator_bulk_disable(NAU8822_NUM_SUPPLIES, nau8822->supplies);
 
        regcache_mark_dirty(nau8822->regmap);
 
@@ -1066,6 +1072,15 @@ static int nau8822_resume(struct snd_soc_component *component)
 {
        struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component);
        struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+       int ret = regulator_bulk_enable(NAU8822_NUM_SUPPLIES, nau8822->supplies);
+
+       if (ret) {
+               dev_err(component->dev,
+                       "Failed to enable regulators: %d\n", ret);
+               return ret;
+       }
+
+       fsleep(100);
 
        regcache_sync(nau8822->regmap);
 
@@ -1153,7 +1168,7 @@ static int nau8822_i2c_probe(struct i2c_client *i2c)
 {
        struct device *dev = &i2c->dev;
        struct nau8822 *nau8822 = dev_get_platdata(dev);
-       int ret;
+       int ret, i;
 
        if (!nau8822) {
                nau8822 = devm_kzalloc(dev, sizeof(*nau8822), GFP_KERNEL);
@@ -1167,6 +1182,13 @@ static int nau8822_i2c_probe(struct i2c_client *i2c)
                return dev_err_probe(&i2c->dev, PTR_ERR(nau8822->mclk),
                        "Error getting mclk\n");
 
+       for (i = 0; i < NAU8822_NUM_SUPPLIES; i++)
+               nau8822->supplies[i].supply = nau8822_supply_names[i];
+
+       ret = devm_regulator_bulk_get(dev, NAU8822_NUM_SUPPLIES, nau8822->supplies);
+       if (ret)
+               return dev_err_probe(dev, ret, "Failed to get regulators\n");
+
        nau8822->regmap = devm_regmap_init_i2c(i2c, &nau8822_regmap_config);
        if (IS_ERR(nau8822->regmap)) {
                ret = PTR_ERR(nau8822->regmap);
@@ -1175,21 +1197,38 @@ static int nau8822_i2c_probe(struct i2c_client *i2c)
        }
        nau8822->dev = dev;
 
+       ret = regulator_bulk_enable(NAU8822_NUM_SUPPLIES, nau8822->supplies);
+       if (ret)
+               return dev_err_probe(dev, ret, "Failed to enable regulators\n");
+
+       fsleep(100);
+
        /* Reset the codec */
        ret = regmap_write(nau8822->regmap, NAU8822_REG_RESET, 0x00);
        if (ret != 0) {
                dev_err(&i2c->dev, "Failed to issue reset: %d\n", ret);
-               return ret;
+               goto err_reg;
        }
 
        ret = devm_snd_soc_register_component(dev, &soc_component_dev_nau8822,
                                                &nau8822_dai, 1);
        if (ret != 0) {
                dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret);
-               return ret;
+               goto err_reg;
        }
 
        return 0;
+
+err_reg:
+       regulator_bulk_disable(NAU8822_NUM_SUPPLIES, nau8822->supplies);
+       return ret;
+}
+
+static void nau8822_i2c_remove(struct i2c_client *i2c)
+{
+       struct nau8822 *nau8822 = i2c_get_clientdata(i2c);
+
+       regulator_bulk_disable(NAU8822_NUM_SUPPLIES, nau8822->supplies);
 }
 
 static const struct i2c_device_id nau8822_i2c_id[] = {
@@ -1212,6 +1251,7 @@ static struct i2c_driver nau8822_i2c_driver = {
                .of_match_table = of_match_ptr(nau8822_of_match),
        },
        .probe = nau8822_i2c_probe,
+       .remove = nau8822_i2c_remove,
        .id_table = nau8822_i2c_id,
 };
 module_i2c_driver(nau8822_i2c_driver);
index 13fe0a091e9ed4f2f95c4b8ae8a3e26b423b9310..24799c7b5931b8ab745940f6f360073e841b2acc 100644 (file)
@@ -211,6 +211,8 @@ struct nau8822_pll {
        int freq_out;
 };
 
+#define NAU8822_NUM_SUPPLIES   4
+
 /* Codec Private Data */
 struct nau8822 {
        struct device *dev;
@@ -219,6 +221,7 @@ struct nau8822 {
        struct nau8822_pll pll;
        int sysclk;
        int div_id;
+       struct regulator_bulk_data supplies[NAU8822_NUM_SUPPLIES];
 };
 
 #endif /* __NAU8822_H__ */