Merge remote-tracking branches 'regulator/topic/act8865', 'regulator/topic/act8945a...
authorMark Brown <broonie@kernel.org>
Sun, 13 Mar 2016 08:19:24 +0000 (15:19 +0700)
committerMark Brown <broonie@kernel.org>
Sun, 13 Mar 2016 08:19:24 +0000 (15:19 +0700)
Documentation/devicetree/bindings/regulator/act8945a-regulator.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/cs4271.txt
drivers/regulator/Kconfig
drivers/regulator/Makefile
drivers/regulator/act8865-regulator.c
drivers/regulator/act8945a-regulator.c [new file with mode: 0644]
drivers/regulator/axp20x-regulator.c
include/linux/regulator/act8865.h
sound/soc/codecs/cs4271.c

diff --git a/Documentation/devicetree/bindings/regulator/act8945a-regulator.txt b/Documentation/devicetree/bindings/regulator/act8945a-regulator.txt
new file mode 100644 (file)
index 0000000..5c80a77
--- /dev/null
@@ -0,0 +1,80 @@
+Device-Tree bindings for regulators of Active-semi ACT8945A Multi-Function Device
+
+Required properties:
+ - compatible: "active-semi,act8945a", please refer to ../mfd/act8945a.txt.
+
+Optional properties:
+- active-semi,vsel-high: Indicates if the VSEL pin is set to logic-high.
+  If this property is missing, assume the VSEL pin is set to logic-low.
+
+Optional input supply properties:
+  - vp1-supply: The input supply for REG_DCDC1
+  - vp2-supply: The input supply for REG_DCDC2
+  - vp3-supply: The input supply for REG_DCDC3
+  - inl45-supply: The input supply for REG_LDO1 and REG_LDO2
+  - inl67-supply: The input supply for REG_LDO3 and REG_LDO4
+
+Any standard regulator properties can be used to configure the single regulator.
+
+The valid names for regulators are:
+       REG_DCDC1, REG_DCDC2, REG_DCDC3, REG_LDO1, REG_LDO2, REG_LDO3, REG_LDO4.
+
+Example:
+       pmic@5b {
+               compatible = "active-semi,act8945a";
+               reg = <0x5b>;
+               status = "okay";
+
+               active-semi,vsel-high;
+
+               regulators {
+                       vdd_1v35_reg: REG_DCDC1 {
+                               regulator-name = "VDD_1V35";
+                               regulator-min-microvolt = <1350000>;
+                               regulator-max-microvolt = <1350000>;
+                               regulator-always-on;
+                       };
+
+                       vdd_1v2_reg: REG_DCDC2 {
+                               regulator-name = "VDD_1V2";
+                               regulator-min-microvolt = <1100000>;
+                               regulator-max-microvolt = <1300000>;
+                               regulator-always-on;
+                       };
+
+                       vdd_3v3_reg: REG_DCDC3 {
+                               regulator-name = "VDD_3V3";
+                               regulator-min-microvolt = <3300000>;
+                               regulator-max-microvolt = <3300000>;
+                               regulator-always-on;
+                       };
+
+                       vdd_fuse_reg: REG_LDO1 {
+                               regulator-name = "VDD_FUSE";
+                               regulator-min-microvolt = <2500000>;
+                               regulator-max-microvolt = <2500000>;
+                               regulator-always-on;
+                       };
+
+                       vdd_3v3_lp_reg: REG_LDO2 {
+                               regulator-name = "VDD_3V3_LP";
+                               regulator-min-microvolt = <3300000>;
+                               regulator-max-microvolt = <3300000>;
+                               regulator-always-on;
+                       };
+
+                       vdd_led_reg: REG_LDO3 {
+                               regulator-name = "VDD_LED";
+                               regulator-min-microvolt = <3300000>;
+                               regulator-max-microvolt = <3300000>;
+                               regulator-always-on;
+                       };
+
+                       vdd_sdhc_1v8_reg: REG_LDO4 {
+                               regulator-name = "VDD_SDHC_1V8";
+                               regulator-min-microvolt = <1800000>;
+                               regulator-max-microvolt = <1800000>;
+                               regulator-always-on;
+                       };
+               };
+       };
index e2cd1d7..6e699ce 100644 (file)
@@ -33,12 +33,19 @@ Optional properties:
        Note that this is not needed in case the clocks are stable
        throughout the entire runtime of the codec.
 
+ - vd-supply:  Digital power
+ - vl-supply:  Logic power
+ - va-supply:  Analog Power
+
 Examples:
 
        codec_i2c: cs4271@10 {
                compatible = "cirrus,cs4271";
                reg = <0x10>;
                reset-gpio = <&gpio 23 0>;
+               vd-supply = <&vdd_3v3_reg>;
+               vl-supply = <&vdd_3v3_reg>;
+               va-supply = <&vdd_3v3_reg>;
        };
 
        codec_spi: cs4271@0 {
index 8155e80..74a6354 100644 (file)
@@ -78,6 +78,15 @@ config REGULATOR_ACT8865
          This driver controls a active-semi act8865 voltage output
          regulator via I2C bus.
 
+config REGULATOR_ACT8945A
+       tristate "Active-semi ACT8945A voltage regulator"
+       depends on MFD_ACT8945A
+       help
+         This driver controls a active-semi ACT8945A voltage regulator
+         via I2C bus. The ACT8945A features three step-down DC/DC converters
+         and four low-dropout linear regulators, along with a ActivePath
+         battery charger.
+
 config REGULATOR_AD5398
        tristate "Analog Devices AD5398/AD5821 regulators"
        depends on I2C
index 980b194..348cfd7 100644 (file)
@@ -15,6 +15,7 @@ obj-$(CONFIG_REGULATOR_AAT2870) += aat2870-regulator.o
 obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o
 obj-$(CONFIG_REGULATOR_AB8500) += ab8500-ext.o ab8500.o
 obj-$(CONFIG_REGULATOR_ACT8865) += act8865-regulator.o
+obj-$(CONFIG_REGULATOR_ACT8945A) += act8945a-regulator.o
 obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o
 obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o
 obj-$(CONFIG_REGULATOR_ARIZONA) += arizona-micsupp.o arizona-ldo1.o
index f8d4cd3..000d566 100644 (file)
@@ -218,7 +218,7 @@ static const struct regulator_desc act8600_regulators[] = {
                .ops = &act8865_ldo_ops,
                .type = REGULATOR_VOLTAGE,
                .n_voltages = 1,
-               .fixed_uV = 1800000,
+               .fixed_uV = 3300000,
                .enable_reg = ACT8600_LDO910_CTRL,
                .enable_mask = ACT8865_ENA,
                .owner = THIS_MODULE,
@@ -369,7 +369,7 @@ static int act8865_pdata_from_dt(struct device *dev,
        for (i = 0; i < num_matches; i++) {
                regulator->id = i;
                regulator->name = matches[i].name;
-               regulator->platform_data = matches[i].init_data;
+               regulator->init_data = matches[i].init_data;
                of_node[i] = matches[i].of_node;
                regulator++;
        }
@@ -396,7 +396,7 @@ static struct regulator_init_data
 
        for (i = 0; i < pdata->num_regulators; i++) {
                if (pdata->regulators[i].id == id)
-                       return pdata->regulators[i].platform_data;
+                       return pdata->regulators[i].init_data;
        }
 
        return NULL;
@@ -415,7 +415,7 @@ static void act8865_power_off(void)
 static int act8865_pmic_probe(struct i2c_client *client,
                              const struct i2c_device_id *i2c_id)
 {
-       static const struct regulator_desc *regulators;
+       const struct regulator_desc *regulators;
        struct act8865_platform_data pdata_of, *pdata;
        struct device *dev = &client->dev;
        struct device_node **of_node;
diff --git a/drivers/regulator/act8945a-regulator.c b/drivers/regulator/act8945a-regulator.c
new file mode 100644 (file)
index 0000000..441864b
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * Voltage regulation driver for active-semi ACT8945A PMIC
+ *
+ * Copyright (C) 2015 Atmel Corporation
+ *
+ * Author: Wenyou Yang <wenyou.yang@atmel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+
+/**
+ * ACT8945A Global Register Map.
+ */
+#define ACT8945A_SYS_MODE      0x00
+#define ACT8945A_SYS_CTRL      0x01
+#define ACT8945A_DCDC1_VSET1   0x20
+#define ACT8945A_DCDC1_VSET2   0x21
+#define ACT8945A_DCDC1_CTRL    0x22
+#define ACT8945A_DCDC2_VSET1   0x30
+#define ACT8945A_DCDC2_VSET2   0x31
+#define ACT8945A_DCDC2_CTRL    0x32
+#define ACT8945A_DCDC3_VSET1   0x40
+#define ACT8945A_DCDC3_VSET2   0x41
+#define ACT8945A_DCDC3_CTRL    0x42
+#define ACT8945A_LDO1_VSET     0x50
+#define ACT8945A_LDO1_CTRL     0x51
+#define ACT8945A_LDO2_VSET     0x54
+#define ACT8945A_LDO2_CTRL     0x55
+#define ACT8945A_LDO3_VSET     0x60
+#define ACT8945A_LDO3_CTRL     0x61
+#define ACT8945A_LDO4_VSET     0x64
+#define ACT8945A_LDO4_CTRL     0x65
+
+/**
+ * Field Definitions.
+ */
+#define ACT8945A_ENA           0x80    /* ON - [7] */
+#define ACT8945A_VSEL_MASK     0x3F    /* VSET - [5:0] */
+
+/**
+ * ACT8945A Voltage Number
+ */
+#define ACT8945A_VOLTAGE_NUM   64
+
+enum {
+       ACT8945A_ID_DCDC1,
+       ACT8945A_ID_DCDC2,
+       ACT8945A_ID_DCDC3,
+       ACT8945A_ID_LDO1,
+       ACT8945A_ID_LDO2,
+       ACT8945A_ID_LDO3,
+       ACT8945A_ID_LDO4,
+       ACT8945A_REG_NUM,
+};
+
+static const struct regulator_linear_range act8945a_voltage_ranges[] = {
+       REGULATOR_LINEAR_RANGE(600000, 0, 23, 25000),
+       REGULATOR_LINEAR_RANGE(1200000, 24, 47, 50000),
+       REGULATOR_LINEAR_RANGE(2400000, 48, 63, 100000),
+};
+
+static struct regulator_ops act8945a_ops = {
+       .list_voltage           = regulator_list_voltage_linear_range,
+       .map_voltage            = regulator_map_voltage_linear_range,
+       .get_voltage_sel        = regulator_get_voltage_sel_regmap,
+       .set_voltage_sel        = regulator_set_voltage_sel_regmap,
+       .enable                 = regulator_enable_regmap,
+       .disable                = regulator_disable_regmap,
+       .is_enabled             = regulator_is_enabled_regmap,
+};
+
+#define ACT89xx_REG(_name, _family, _id, _vsel_reg, _supply)           \
+       [_family##_ID_##_id] = {                                        \
+               .name                   = _name,                        \
+               .supply_name            = _supply,                      \
+               .of_match               = of_match_ptr("REG_"#_id),     \
+               .regulators_node        = of_match_ptr("regulators"),   \
+               .id                     = _family##_ID_##_id,           \
+               .type                   = REGULATOR_VOLTAGE,            \
+               .ops                    = &act8945a_ops,                \
+               .n_voltages             = ACT8945A_VOLTAGE_NUM,         \
+               .linear_ranges          = act8945a_voltage_ranges,      \
+               .n_linear_ranges        = ARRAY_SIZE(act8945a_voltage_ranges), \
+               .vsel_reg               = _family##_##_id##_##_vsel_reg, \
+               .vsel_mask              = ACT8945A_VSEL_MASK,           \
+               .enable_reg             = _family##_##_id##_CTRL,       \
+               .enable_mask            = ACT8945A_ENA,                 \
+               .owner                  = THIS_MODULE,                  \
+       }
+
+static const struct regulator_desc act8945a_regulators[] = {
+       ACT89xx_REG("DCDC_REG1", ACT8945A, DCDC1, VSET1, "vp1"),
+       ACT89xx_REG("DCDC_REG2", ACT8945A, DCDC2, VSET1, "vp2"),
+       ACT89xx_REG("DCDC_REG3", ACT8945A, DCDC3, VSET1, "vp3"),
+       ACT89xx_REG("LDO_REG1", ACT8945A, LDO1, VSET, "inl45"),
+       ACT89xx_REG("LDO_REG2", ACT8945A, LDO2, VSET, "inl45"),
+       ACT89xx_REG("LDO_REG3", ACT8945A, LDO3, VSET, "inl67"),
+       ACT89xx_REG("LDO_REG4", ACT8945A, LDO4, VSET, "inl67"),
+};
+
+static const struct regulator_desc act8945a_alt_regulators[] = {
+       ACT89xx_REG("DCDC_REG1", ACT8945A, DCDC1, VSET2, "vp1"),
+       ACT89xx_REG("DCDC_REG2", ACT8945A, DCDC2, VSET2, "vp2"),
+       ACT89xx_REG("DCDC_REG3", ACT8945A, DCDC3, VSET2, "vp3"),
+       ACT89xx_REG("LDO_REG1", ACT8945A, LDO1, VSET, "inl45"),
+       ACT89xx_REG("LDO_REG2", ACT8945A, LDO2, VSET, "inl45"),
+       ACT89xx_REG("LDO_REG3", ACT8945A, LDO3, VSET, "inl67"),
+       ACT89xx_REG("LDO_REG4", ACT8945A, LDO4, VSET, "inl67"),
+};
+
+static int act8945a_pmic_probe(struct platform_device *pdev)
+{
+       struct regulator_config config = { };
+       const struct regulator_desc *regulators;
+       struct regulator_dev *rdev;
+       int i, num_regulators;
+       bool voltage_select;
+
+       voltage_select = of_property_read_bool(pdev->dev.parent->of_node,
+                                              "active-semi,vsel-high");
+
+       if (voltage_select) {
+               regulators = act8945a_alt_regulators;
+               num_regulators = ARRAY_SIZE(act8945a_alt_regulators);
+       } else {
+               regulators = act8945a_regulators;
+               num_regulators = ARRAY_SIZE(act8945a_regulators);
+       }
+
+       config.dev = &pdev->dev;
+       config.dev->of_node = pdev->dev.parent->of_node;
+       for (i = 0; i < num_regulators; i++) {
+               rdev = devm_regulator_register(&pdev->dev, &regulators[i], &config);
+               if (IS_ERR(rdev)) {
+                       dev_err(&pdev->dev,
+                               "failed to register %s regulator\n",
+                               regulators[i].name);
+                       return PTR_ERR(rdev);
+               }
+       }
+
+       return 0;
+}
+
+static struct platform_driver act8945a_pmic_driver = {
+       .driver = {
+               .name = "act8945a-regulator",
+       },
+       .probe = act8945a_pmic_probe,
+};
+module_platform_driver(act8945a_pmic_driver);
+
+MODULE_DESCRIPTION("Active-semi ACT8945A voltage regulator driver");
+MODULE_AUTHOR("Wenyou Yang <wenyou.yang@atmel.com>");
+MODULE_LICENSE("GPL");
index f2e1a39..214e815 100644 (file)
@@ -39,7 +39,7 @@
 #define AXP_DESC_IO(_family, _id, _match, _supply, _min, _max, _step, _vreg,   \
                    _vmask, _ereg, _emask, _enable_val, _disable_val)           \
        [_family##_##_id] = {                                                   \
-               .name           = #_id,                                         \
+               .name           = (_match),                                     \
                .supply_name    = (_supply),                                    \
                .of_match       = of_match_ptr(_match),                         \
                .regulators_node = of_match_ptr("regulators"),                  \
@@ -61,7 +61,7 @@
 #define AXP_DESC(_family, _id, _match, _supply, _min, _max, _step, _vreg,      \
                 _vmask, _ereg, _emask)                                         \
        [_family##_##_id] = {                                                   \
-               .name           = #_id,                                         \
+               .name           = (_match),                                     \
                .supply_name    = (_supply),                                    \
                .of_match       = of_match_ptr(_match),                         \
                .regulators_node = of_match_ptr("regulators"),                  \
                .ops            = &axp20x_ops,                                  \
        }
 
-#define AXP_DESC_SW(_family, _id, _match, _supply, _min, _max, _step, _vreg,   \
-                   _vmask, _ereg, _emask)                                      \
+#define AXP_DESC_SW(_family, _id, _match, _supply, _ereg, _emask)              \
        [_family##_##_id] = {                                                   \
-               .name           = #_id,                                         \
+               .name           = (_match),                                     \
                .supply_name    = (_supply),                                    \
                .of_match       = of_match_ptr(_match),                         \
                .regulators_node = of_match_ptr("regulators"),                  \
                .type           = REGULATOR_VOLTAGE,                            \
                .id             = _family##_##_id,                              \
-               .n_voltages     = (((_max) - (_min)) / (_step) + 1),            \
                .owner          = THIS_MODULE,                                  \
-               .min_uV         = (_min) * 1000,                                \
-               .uV_step        = (_step) * 1000,                               \
-               .vsel_reg       = (_vreg),                                      \
-               .vsel_mask      = (_vmask),                                     \
                .enable_reg     = (_ereg),                                      \
                .enable_mask    = (_emask),                                     \
                .ops            = &axp20x_ops_sw,                               \
 
 #define AXP_DESC_FIXED(_family, _id, _match, _supply, _volt)                   \
        [_family##_##_id] = {                                                   \
-               .name           = #_id,                                         \
+               .name           = (_match),                                     \
                .supply_name    = (_supply),                                    \
                .of_match       = of_match_ptr(_match),                         \
                .regulators_node = of_match_ptr("regulators"),                  \
                .ops            = &axp20x_ops_fixed                             \
        }
 
-#define AXP_DESC_TABLE(_family, _id, _match, _supply, _table, _vreg, _vmask,   \
-                      _ereg, _emask)                                           \
+#define AXP_DESC_RANGES(_family, _id, _match, _supply, _ranges, _n_voltages,   \
+                       _vreg, _vmask, _ereg, _emask)                           \
        [_family##_##_id] = {                                                   \
-               .name           = #_id,                                         \
+               .name           = (_match),                                     \
                .supply_name    = (_supply),                                    \
                .of_match       = of_match_ptr(_match),                         \
                .regulators_node = of_match_ptr("regulators"),                  \
                .type           = REGULATOR_VOLTAGE,                            \
                .id             = _family##_##_id,                              \
-               .n_voltages     = ARRAY_SIZE(_table),                           \
+               .n_voltages     = (_n_voltages),                                \
                .owner          = THIS_MODULE,                                  \
                .vsel_reg       = (_vreg),                                      \
                .vsel_mask      = (_vmask),                                     \
                .enable_reg     = (_ereg),                                      \
                .enable_mask    = (_emask),                                     \
-               .volt_table     = (_table),                                     \
-               .ops            = &axp20x_ops_table,                            \
+               .linear_ranges  = (_ranges),                                    \
+               .n_linear_ranges = ARRAY_SIZE(_ranges),                         \
+               .ops            = &axp20x_ops_range,                            \
        }
 
-static const int axp20x_ldo4_data[] = { 1250000, 1300000, 1400000, 1500000, 1600000,
-                                       1700000, 1800000, 1900000, 2000000, 2500000,
-                                       2700000, 2800000, 3000000, 3100000, 3200000,
-                                       3300000 };
-
 static struct regulator_ops axp20x_ops_fixed = {
        .list_voltage           = regulator_list_voltage_linear,
 };
 
-static struct regulator_ops axp20x_ops_table = {
+static struct regulator_ops axp20x_ops_range = {
        .set_voltage_sel        = regulator_set_voltage_sel_regmap,
        .get_voltage_sel        = regulator_get_voltage_sel_regmap,
-       .list_voltage           = regulator_list_voltage_table,
-       .map_voltage            = regulator_map_voltage_ascend,
+       .list_voltage           = regulator_list_voltage_linear_range,
        .enable                 = regulator_enable_regmap,
        .disable                = regulator_disable_regmap,
        .is_enabled             = regulator_is_enabled_regmap,
@@ -160,13 +149,17 @@ static struct regulator_ops axp20x_ops = {
 };
 
 static struct regulator_ops axp20x_ops_sw = {
-       .get_voltage_sel        = regulator_get_voltage_sel_regmap,
-       .list_voltage           = regulator_list_voltage_linear,
        .enable                 = regulator_enable_regmap,
        .disable                = regulator_disable_regmap,
        .is_enabled             = regulator_is_enabled_regmap,
 };
 
+static const struct regulator_linear_range axp20x_ldo4_ranges[] = {
+       REGULATOR_LINEAR_RANGE(1250000, 0x0, 0x0, 0),
+       REGULATOR_LINEAR_RANGE(1300000, 0x1, 0x8, 100000),
+       REGULATOR_LINEAR_RANGE(2500000, 0x9, 0xf, 100000),
+};
+
 static const struct regulator_desc axp20x_regulators[] = {
        AXP_DESC(AXP20X, DCDC2, "dcdc2", "vin2", 700, 2275, 25,
                 AXP20X_DCDC2_V_OUT, 0x3f, AXP20X_PWR_OUT_CTRL, 0x10),
@@ -177,8 +170,9 @@ static const struct regulator_desc axp20x_regulators[] = {
                 AXP20X_LDO24_V_OUT, 0xf0, AXP20X_PWR_OUT_CTRL, 0x04),
        AXP_DESC(AXP20X, LDO3, "ldo3", "ldo3in", 700, 3500, 25,
                 AXP20X_LDO3_V_OUT, 0x7f, AXP20X_PWR_OUT_CTRL, 0x40),
-       AXP_DESC_TABLE(AXP20X, LDO4, "ldo4", "ldo24in", axp20x_ldo4_data,
-                      AXP20X_LDO24_V_OUT, 0x0f, AXP20X_PWR_OUT_CTRL, 0x08),
+       AXP_DESC_RANGES(AXP20X, LDO4, "ldo4", "ldo24in", axp20x_ldo4_ranges,
+                       16, AXP20X_LDO24_V_OUT, 0x0f, AXP20X_PWR_OUT_CTRL,
+                       0x08),
        AXP_DESC_IO(AXP20X, LDO5, "ldo5", "ldo5in", 1800, 3300, 100,
                    AXP20X_LDO5_V_OUT, 0xf0, AXP20X_GPIO0_CTRL, 0x07,
                    AXP20X_IO_ENABLED, AXP20X_IO_DISABLED),
@@ -196,8 +190,8 @@ static const struct regulator_desc axp22x_regulators[] = {
        AXP_DESC(AXP22X, DCDC5, "dcdc5", "vin5", 1000, 2550, 50,
                 AXP22X_DCDC5_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(5)),
        /* secondary switchable output of DCDC1 */
-       AXP_DESC_SW(AXP22X, DC1SW, "dc1sw", NULL, 1600, 3400, 100,
-                   AXP22X_DCDC1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(7)),
+       AXP_DESC_SW(AXP22X, DC1SW, "dc1sw", NULL, AXP22X_PWR_OUT_CTRL2,
+                   BIT(7)),
        /* LDO regulator internally chained to DCDC5 */
        AXP_DESC(AXP22X, DC5LDO, "dc5ldo", NULL, 700, 1400, 100,
                 AXP22X_DC5LDO_V_OUT, 0x7, AXP22X_PWR_OUT_CTRL1, BIT(0)),
index 15fa8f2..2eb3860 100644 (file)
@@ -68,12 +68,12 @@ enum {
  * act8865_regulator_data - regulator data
  * @id: regulator id
  * @name: regulator name
- * @platform_data: regulator init data
+ * @init_data: regulator init data
  */
 struct act8865_regulator_data {
        int id;
        const char *name;
-       struct regulator_init_data *platform_data;
+       struct regulator_init_data *init_data;
 };
 
 /**
index e770ee6..0c0010b 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/of_gpio.h>
+#include <linux/regulator/consumer.h>
 #include <sound/pcm.h>
 #include <sound/soc.h>
 #include <sound/tlv.h>
@@ -157,6 +158,10 @@ static bool cs4271_volatile_reg(struct device *dev, unsigned int reg)
        return reg == CS4271_CHIPID;
 }
 
+static const char * const supply_names[] = {
+       "vd", "vl", "va"
+};
+
 struct cs4271_private {
        unsigned int                    mclk;
        bool                            master;
@@ -170,6 +175,7 @@ struct cs4271_private {
        int                             gpio_disable;
        /* enable soft reset workaround */
        bool                            enable_soft_reset;
+       struct regulator_bulk_data      supplies[ARRAY_SIZE(supply_names)];
 };
 
 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,
 };
 
+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
 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)
                return ret;
 
+       regcache_mark_dirty(cs4271->regmap);
+       regulator_bulk_disable(ARRAY_SIZE(cs4271->supplies), cs4271->supplies);
+
        return 0;
 }
 
@@ -507,6 +530,16 @@ static int cs4271_soc_resume(struct snd_soc_codec *codec)
        int ret;
        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 */
        ret = regcache_sync(cs4271->regmap);
        if (ret < 0)
@@ -553,19 +586,24 @@ static int cs4271_codec_probe(struct snd_soc_codec *codec)
        }
 #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) {
                amutec_eq_bmutec = cs4271plat->amutec_eq_bmutec;
                cs4271->enable_soft_reset = cs4271plat->enable_soft_reset;
        }
 
-       if (gpio_is_valid(cs4271->gpio_nreset)) {
-               /* Reset codec */
-               gpio_direction_output(cs4271->gpio_nreset, 0);
-               mdelay(1);
-               gpio_set_value(cs4271->gpio_nreset, 1);
-               /* Give the codec time to wake up */
-               mdelay(1);
-       }
+       /* Reset codec */
+       cs4271_reset(codec);
+
+       ret = regcache_sync(cs4271->regmap);
+       if (ret < 0)
+               return ret;
 
        ret = regmap_update_bits(cs4271->regmap, CS4271_MODE2,
                                 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 */
                gpio_set_value(cs4271->gpio_nreset, 0);
 
+       regcache_mark_dirty(cs4271->regmap);
+       regulator_bulk_disable(ARRAY_SIZE(cs4271->supplies), cs4271->supplies);
+
        return 0;
 };
 
@@ -617,6 +658,7 @@ static int cs4271_common_probe(struct device *dev,
 {
        struct cs4271_platform_data *cs4271plat = dev->platform_data;
        struct cs4271_private *cs4271;
+       int i, ret;
 
        cs4271 = devm_kzalloc(dev, sizeof(*cs4271), GFP_KERNEL);
        if (!cs4271)
@@ -638,6 +680,17 @@ static int cs4271_common_probe(struct device *dev,
                        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;
        return 0;
 }