ASoC: aw8896: add logic to handle regulator for DVDD

aw8896's DVDD is not powered by default, add logic to read regulator info
from device tree, and enable power for DVDD in the driver probe function.

Change-Id: I544ef3e8e7f273f868cefc6a3ff27b6e7f9ed768
Signed-off-by: Xiao Li <lixiao@codeaurora.org>
diff --git a/Documentation/devicetree/bindings/sound/aw8896.txt b/Documentation/devicetree/bindings/sound/aw8896.txt
index e74ce67..c96b367 100644
--- a/Documentation/devicetree/bindings/sound/aw8896.txt
+++ b/Documentation/devicetree/bindings/sound/aw8896.txt
@@ -6,7 +6,13 @@
 
   - reg : I2C address of the device
 
-  - reset-gpio: gpio used for HW reset
+  - reset-gpio : gpio used for HW reset
+
+  - dvdd-supply : Power supply for PA's dvdd
+
+  - dvdd-voltage : Minimum and maximum voltage in uV to set for power supply
+
+  - dvdd-current : dvdd's max current in uA
 
 Optional properties:
 
@@ -18,4 +24,7 @@
 		compatible = "awinic,i2c_smartpa";
 		reg = <0x34>;
 		reset-gpio = <&tlmm 68 0>;
+		dvdd-supply = <&pm660_l9>;
+		dvdd-voltage = <1800000 1800000>;
+		dvdd-current = <15000>;
 	};
diff --git a/sound/soc/codecs/aw8896.c b/sound/soc/codecs/aw8896.c
index dbc7cd8..2592b48 100644
--- a/sound/soc/codecs/aw8896.c
+++ b/sound/soc/codecs/aw8896.c
@@ -43,6 +43,7 @@
 #define AW_READ_CHIPID_RETRIES 5
 #define AW_READ_CHIPID_RETRY_DELAY 5
 #define AW8896_MAX_DSP_START_TRY_COUNT    10
+#define DT_MAX_PROP_SIZE 80
 
 static int aw8896_spk_control;
 static int aw8896_rcv_control;
@@ -1068,6 +1069,14 @@
 
 static int aw8896_parse_dt(struct device *dev, struct aw8896 *aw8896,
 		struct device_node *np) {
+	int prop_val = 0;
+	int ret = 0;
+	int len = 0;
+	const __be32 *prop = NULL;
+	struct device_node *regnode = NULL;
+	char *dvdd_supply = "dvdd";
+	char prop_name[DT_MAX_PROP_SIZE] = {0};
+
 	aw8896->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
 	if (aw8896->reset_gpio < 0) {
 		dev_err(dev,
@@ -1082,7 +1091,47 @@
 	if (aw8896->irq_gpio < 0)
 		dev_info(dev, "%s: no irq gpio provided.\n", __func__);
 
+	snprintf(prop_name, DT_MAX_PROP_SIZE, "%s-supply", dvdd_supply);
+	regnode = of_parse_phandle(np, prop_name, 0);
+	if (!regnode) {
+		dev_err(dev, "%s: no %s provided\n", __func__, prop_name);
+		goto err_get_regulator;
+	}
+
+	aw8896->supply.regulator = devm_regulator_get(dev, dvdd_supply);
+	if (IS_ERR(aw8896->supply.regulator)) {
+		dev_err(dev, "%s: failed to get supply for %s\n", __func__,
+			dvdd_supply);
+		goto err_get_regulator;
+	}
+
+	snprintf(prop_name, DT_MAX_PROP_SIZE, "%s-voltage", dvdd_supply);
+	prop = of_get_property(np, prop_name, &len);
+	if (!prop || (len != (2 * sizeof(__be32)))) {
+		dev_err(dev, "%s: no %s provided or format invalid\n",
+			__func__, prop_name);
+		goto err_get_voltage;
+	}
+
+	aw8896->supply.min_uv = be32_to_cpup(&prop[0]);
+	aw8896->supply.max_uv = be32_to_cpup(&prop[1]);
+
+	snprintf(prop_name, DT_MAX_PROP_SIZE, "%s-current", dvdd_supply);
+	ret = of_property_read_u32(np, prop_name, &prop_val);
+	if (ret) {
+		dev_err(dev, "%s: no %s provided\n", __func__, prop_name);
+		goto err_get_current;
+	}
+	aw8896->supply.ua = prop_val;
+
 	return 0;
+
+err_get_current:
+err_get_voltage:
+	devm_regulator_put(aw8896->supply.regulator);
+	aw8896->supply.regulator = NULL;
+err_get_regulator:
+	return -EINVAL;
 }
 
 int aw8896_hw_reset(struct aw8896 *aw8896)
@@ -1354,7 +1403,7 @@
 
 	i2c_set_clientdata(i2c, aw8896);
 	mutex_init(&aw8896->lock);
-	/* aw8896 rst & int */
+
 	if (np) {
 		ret = aw8896_parse_dt(&i2c->dev, aw8896, np);
 		if (ret) {
@@ -1388,6 +1437,30 @@
 		}
 	}
 
+	ret = regulator_set_voltage(aw8896->supply.regulator,
+				    aw8896->supply.max_uv,
+				    aw8896->supply.min_uv);
+	if (ret) {
+		dev_err(&i2c->dev, "%s: set voltage %d ~ %d failed\n",
+				__func__,
+				aw8896->supply.min_uv,
+				aw8896->supply.max_uv);
+		goto err_supply_set;
+	}
+
+	ret = regulator_set_load(aw8896->supply.regulator, aw8896->supply.ua);
+	if (ret < 0) {
+		dev_err(&i2c->dev, "%s: set current %d failed\n", __func__,
+				aw8896->supply.ua);
+		goto err_supply_set;
+	}
+
+	ret = regulator_enable(aw8896->supply.regulator);
+	if (ret < 0) {
+		dev_err(&i2c->dev, "%s: regulator enable failed\n", __func__);
+		goto err_supply_set;
+	}
+
 	ret = aw8896_hw_reset(aw8896);
 	if (ret < 0) {
 		dev_err(&i2c->dev, "%s: aw8896_hw_reset failed\n", __func__);
@@ -1461,6 +1534,11 @@
 	if (gpio_is_valid(aw8896->irq_gpio))
 		devm_gpio_free(&i2c->dev, aw8896->irq_gpio);
 err_hw_rst:
+	if (aw8896->supply.regulator)
+		regulator_disable(aw8896->supply.regulator);
+err_supply_set:
+	if (aw8896->supply.regulator)
+		devm_regulator_put(aw8896->supply.regulator);
 err_irq_gpio_request:
 	if (gpio_is_valid(aw8896->reset_gpio))
 		devm_gpio_free(&i2c->dev, aw8896->reset_gpio);
@@ -1486,6 +1564,11 @@
 	if (gpio_is_valid(aw8896->reset_gpio))
 		devm_gpio_free(&i2c->dev, aw8896->reset_gpio);
 
+	if (aw8896->supply.regulator) {
+		regulator_disable(aw8896->supply.regulator);
+		devm_regulator_put(aw8896->supply.regulator);
+	}
+
 	devm_kfree(&i2c->dev, aw8896);
 	aw8896 = NULL;
 
diff --git a/sound/soc/codecs/aw8896.h b/sound/soc/codecs/aw8896.h
index 48e01ca..5663cb2 100644
--- a/sound/soc/codecs/aw8896.h
+++ b/sound/soc/codecs/aw8896.h
@@ -15,6 +15,7 @@
 
 #ifndef _AW8896_H_
 #define _AW8896_H_
+#include <linux/regulator/driver.h>
 
 /*
  * i2c transaction on Linux limited to 64k
@@ -81,10 +82,18 @@
 	AW8896_DSP_CFG_OK,
 };
 
+struct dvdd_supply {
+	struct regulator *regulator;
+	int min_uv;
+	int max_uv;
+	int ua;
+};
+
 struct aw8896 {
 	struct regmap *regmap;
 	struct i2c_client *i2c;
 	struct snd_soc_codec *codec;
+	struct dvdd_supply supply;
 	struct mutex lock;
 	int dsp_init;
 	int dsp_fw_state;