ASoC: wcd9xxx: Add device tree support in codec for I2C

Add device tree support for I2C mode in codec driver.
Parse I2C device information for codec from device tree
and enable power-supplies, gpio, MCLK configurations.

Change-Id: I070b7389d01b5cb14f2897e0cb75ebad72d432a7
Signed-off-by: Venkat Sudhir <vsudhir@codeaurora.org>
diff --git a/Documentation/devicetree/bindings/sound/taiko_codec.txt b/Documentation/devicetree/bindings/sound/taiko_codec.txt
index 96e3a61..090d8db 100644
--- a/Documentation/devicetree/bindings/sound/taiko_codec.txt
+++ b/Documentation/devicetree/bindings/sound/taiko_codec.txt
@@ -22,12 +22,14 @@
  - qcom,cdc-micbias-cfilt1-mv - cfilt1 output voltage in milli volts.
  - qcom,cdc-micbias-cfilt2-mv - cfilt2 output voltage in milli volts.
  - qcom,cdc-micbias-cfilt3-mv - cfilt3 output voltage in milli volts.
-   cfilt volatge can be set to max of qcom,cdc-micbias-ldoh-v - 0.15V.
+   cfilt voltage can be set to max of qcom,cdc-micbias-ldoh-v - 0.15V.
 
  - qcom,cdc-micbias1-cfilt-sel = cfilt to use for micbias1 (should be from 1 to 3).
  - qcom,cdc-micbias2-cfilt-sel = cfilt to use for micbias2 (should be from 1 to 3).
  - qcom,cdc-micbias3-cfilt-sel = cfilt to use for micbias3 (should be from 1 to 3).
  - qcom,cdc-micbias4-cfilt-sel = cfilt to use for micbias4 (should be from 1 to 3).
+   This value represents the connected CFLIT to MIC Bias.
+
  - qcom,cdc-micbias1-ext-cap: Boolean. Enable micbias 1 external capacitor mode.
  - qcom,cdc-micbias2-ext-cap: Boolean. Enable micbias 2 external capacitor mode.
  - qcom,cdc-micbias3-ext-cap: Boolean. Enable micbias 3 external capacitor mode.
@@ -88,3 +90,109 @@
 	qcom,cdc-slim-ifd = "taiko-slim-ifd";
 	qcom,cdc-slim-ifd-elemental-addr = [00 00 A0 00 17 02];
 };
+
+Wcd9xxx audio CODEC in I2C mode
+
+  - compatible = "qcom,wcd9xxx-i2c-device";
+  - reg: represents the slave address provided to the I2C driver.
+  - qcom,cdc-reset-gpio: gpio used for codec SOC reset.
+  - <supply-name>-supply: phandle to the regulator device tree node.
+  - qcom,<supply-name>-voltage -  specifies voltage levels for supply. Should be
+       specified in pairs (min, max), units mV.
+  - qcom,<supply-name>-current - specifies max current in mA that can drawn
+       from the <supply-name>.
+
+    above three properties with "supply-name" set to  "qcom,cdc-vdd-buck", "qcom,cdc-vdd-tx-h",
+     "qcom,cdc-vdd-rx-h", "qcom,cdc-vddpx-1", "qcom,cdc-vdd-a-1p2v", "qcom,cdc-vddcx-1",
+     "qcom,cdc-vddcx-2" should be present.
+
+ - qcom,cdc-micbias-ldoh-v - LDOH output in volts ( should be 1.95 V and 3.00 V).
+
+ - qcom,cdc-micbias-cfilt1-mv - cfilt1 output voltage in milli volts.
+ - qcom,cdc-micbias-cfilt2-mv - cfilt2 output voltage in milli volts.
+ - qcom,cdc-micbias-cfilt3-mv - cfilt3 output voltage in milli volts.
+   cfilt voltage can be set to max of qcom,cdc-micbias-ldoh-v - 0.15V.
+
+ - qcom,cdc-micbias1-cfilt-sel = cfilt to use for micbias1 (should be from 1 to 3).
+ - qcom,cdc-micbias2-cfilt-sel = cfilt to use for micbias2 (should be from 1 to 3).
+ - qcom,cdc-micbias3-cfilt-sel = cfilt to use for micbias3 (should be from 1 to 3).
+ - qcom,cdc-micbias4-cfilt-sel = cfilt to use for micbias4 (should be from 1 to 3).
+   This value represents the connected CFLIT to MIC Bias.
+
+ - qcom,cdc-micbias1-ext-cap: Boolean. Enable micbias 1 external capacitor mode.
+ - qcom,cdc-micbias2-ext-cap: Boolean. Enable micbias 2 external capacitor mode.
+ - qcom,cdc-micbias3-ext-cap: Boolean. Enable micbias 3 external capacitor mode.
+ - qcom,cdc-micbias4-ext-cap: Boolean. Enable micbias 4 external capacitor mode.
+
+Example:
+i2c@f9925000 {
+	cell-index = <3>;
+	compatible = "qcom,i2c-qup";
+	reg = <0xf9925000 0x1000>;
+	#address-cells = <1>;
+	#size-cells = <0>;
+	reg-names = "qup_phys_addr";
+	interrupts = <0 97 0>;
+	interrupt-names = "qup_err_intr";
+	qcom,i2c-bus-freq = <100000>;
+	qcom,i2c-src-freq = <24000000>;
+
+	wcd9xxx_codec@0d{
+		compatible = "qcom,wcd9xxx-i2c";
+		reg = <0x0d>;
+		qcom,cdc-reset-gpio = <&msmgpio 22 0>;
+		interrupt-parent = <&wcd9xxx_intc>;
+		interrupts = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28>;
+
+		cdc-vdd-buck-supply = <&pm8019_l11>;
+		qcom,cdc-vdd-buck-voltage = <1800000 1800000>;
+		qcom,cdc-vdd-buck-current = <25000>;
+
+		cdc-vdd-tx-h-supply = <&pm8019_l11>;
+		qcom,cdc-vdd-tx-h-voltage = <1800000 1800000>;
+		qcom,cdc-vdd-tx-h-current = <25000>;
+
+		cdc-vdd-rx-h-supply = <&pm8019_l11>;
+		qcom,cdc-vdd-rx-h-voltage = <1800000 1800000>;
+		qcom,cdc-vdd-rx-h-current = <25000>;
+
+		cdc-vddpx-1-supply = <&pm8019_l11>;
+		qcom,cdc-vddpx-1-voltage = <1800000 1800000>;
+		qcom,cdc-vddpx-1-current = <10000>;
+
+		cdc-vdd-a-1p2v-supply = <&pm8019_l9>;
+		qcom,cdc-vdd-a-1p2v-voltage = <1200000 1200000>;
+		qcom,cdc-vdd-a-1p2v-current = <10000>;
+
+		cdc-vddcx-1-supply = <&pm8019_l9>;
+		qcom,cdc-vddcx-1-voltage = <1200000 1200000>;
+		qcom,cdc-vddcx-1-current = <10000>;
+
+		cdc-vddcx-2-supply = <&pm8019_l9>;
+		qcom,cdc-vddcx-2-voltage = <1200000 1200000>;
+		qcom,cdc-vddcx-2-current = <10000>;
+
+		qcom,cdc-micbias-ldoh-v = <0x3>;
+		qcom,cdc-micbias-cfilt1-mv = <1800>;
+		qcom,cdc-micbias-cfilt2-mv = <2700>;
+		qcom,cdc-micbias-cfilt3-mv = <1800>;
+		qcom,cdc-micbias1-cfilt-sel = <0x0>;
+		qcom,cdc-micbias2-cfilt-sel = <0x1>;
+		qcom,cdc-micbias3-cfilt-sel = <0x2>;
+		qcom,cdc-micbias4-cfilt-sel = <0x2>;
+	};
+
+	wcd9xxx_codec@77{
+		compatible = "qcom,wcd9xxx-i2c";
+		reg = <0x77>;
+	};
+
+	wcd9xxx_codec@66{
+		compatible = "qcom,wcd9xxx-i2c";
+		reg = <0x66>;
+	};
+	wcd9xxx_codec@55{
+		compatible = "qcom,wcd9xxx-i2c";
+		reg = <0x55>;
+	};
+};
diff --git a/drivers/mfd/wcd9xxx-core.c b/drivers/mfd/wcd9xxx-core.c
index 1f7b67a..fa7c116 100644
--- a/drivers/mfd/wcd9xxx-core.c
+++ b/drivers/mfd/wcd9xxx-core.c
@@ -35,6 +35,8 @@
 #define MAX_WCD9XXX_DEVICE	4
 #define TABLA_I2C_MODE	0x03
 #define SITAR_I2C_MODE	0x01
+#define CODEC_DT_MAX_PROP_SIZE   40
+#define WCD9XXX_I2C_GSBI_SLAVE_ID "3-000d"
 
 struct wcd9xxx_i2c {
 	struct i2c_client *client;
@@ -43,6 +45,17 @@
 	int mod_id;
 };
 
+static char *taiko_supplies[] = {
+	"cdc-vdd-buck", "cdc-vdd-tx-h", "cdc-vdd-rx-h", "cdc-vddpx-1",
+	"cdc-vdd-a-1p2v", "cdc-vddcx-1", "cdc-vddcx-2",
+};
+
+static int wcd9xxx_dt_parse_vreg_info(struct device *dev,
+	struct wcd9xxx_regulator *vreg, const char *vreg_name);
+static int wcd9xxx_dt_parse_micbias_info(struct device *dev,
+	struct wcd9xxx_micbias_setting *micbias);
+static struct wcd9xxx_pdata *wcd9xxx_populate_dt_pdata(struct device *dev);
+
 struct wcd9xxx_i2c wcd9xxx_modules[MAX_WCD9XXX_DEVICE];
 static int wcd9xxx_intf = -1;
 
@@ -764,19 +777,31 @@
 	int ret = 0;
 	int i2c_mode = 0;
 	static int device_id;
+	struct device *dev;
 
 	pr_info("%s\n", __func__);
 	if (wcd9xxx_intf == WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
-		pr_info("tabla card is already detected in slimbus mode\n");
+		dev_dbg(&client->dev, "%s:Codec is detected in slimbus mode\n",
+			 __func__);
 		return -ENODEV;
 	}
-	pdata = client->dev.platform_data;
 	if (device_id > 0) {
 		wcd9xxx_modules[device_id++].client = client;
-		pr_info("probe for other slaves devices of tabla\n");
+		dev_dbg(&client->dev, "%s:probe for other slaves\n"
+			"devices of codec\n", __func__);
 		return ret;
 	}
-
+	dev = &client->dev;
+	if (client->dev.of_node) {
+		dev_dbg(&client->dev, "%s:Platform data from device tree\n",
+			__func__);
+		pdata = wcd9xxx_populate_dt_pdata(&client->dev);
+		client->dev.platform_data = pdata;
+	} else {
+		dev_dbg(&client->dev, "%s:Platform data from board file\n",
+			__func__);
+		pdata = client->dev.platform_data;
+	}
 	wcd9xxx = kzalloc(sizeof(struct wcd9xxx), GFP_KERNEL);
 	if (wcd9xxx == NULL) {
 		pr_err("%s: error, allocation failed\n", __func__);
@@ -858,7 +883,6 @@
 	return 0;
 }
 
-#define CODEC_DT_MAX_PROP_SIZE   40
 static int wcd9xxx_dt_parse_vreg_info(struct device *dev,
 	struct wcd9xxx_regulator *vreg, const char *vreg_name)
 {
@@ -1057,11 +1081,6 @@
 	return 0;
 }
 
-static char *taiko_supplies[] = {
-	"cdc-vdd-buck", "cdc-vdd-tx-h", "cdc-vdd-rx-h", "cdc-vddpx-1",
-	"cdc-vdd-a-1p2v", "cdc-vddcx-1", "cdc-vddcx-2",
-};
-
 static struct wcd9xxx_pdata *wcd9xxx_populate_dt_pdata(struct device *dev)
 {
 	struct wcd9xxx_pdata *pdata;
@@ -1071,12 +1090,11 @@
 
 	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
 	if (!pdata) {
-		dev_err(dev,
-			"could not allocate memory for platform data\n");
+		dev_err(dev, "could not allocate memory for platform data\n");
 		return NULL;
 	}
-
-	if (!strcmp(dev_name(dev), "taiko-slim-pgd")) {
+	if (!strcmp(dev_name(dev), "taiko-slim-pgd") ||
+		(!strcmp(dev_name(dev), WCD9XXX_I2C_GSBI_SLAVE_ID))) {
 		codec_supplies = taiko_supplies;
 		num_of_supplies = ARRAY_SIZE(taiko_supplies);
 	} else {
@@ -1111,11 +1129,7 @@
 			pdata->reset_gpio);
 		goto err;
 	}
-
-	ret = wcd9xxx_dt_parse_slim_interface_dev_info(dev,
-			&pdata->slimbus_slave_device);
-	if (ret)
-		goto err;
+	dev_dbg(dev, "%s: reset gpio %d", __func__, pdata->reset_gpio);
 	return pdata;
 err:
 	devm_kfree(dev, pdata);
@@ -1151,6 +1165,14 @@
 	if (slim->dev.of_node) {
 		dev_info(&slim->dev, "Platform data from device tree\n");
 		pdata = wcd9xxx_populate_dt_pdata(&slim->dev);
+		ret = wcd9xxx_dt_parse_slim_interface_dev_info(&slim->dev,
+				&pdata->slimbus_slave_device);
+		if (ret) {
+			dev_err(&slim->dev, "Error, parsing slim interface\n");
+			devm_kfree(&slim->dev, pdata);
+			ret = -EINVAL;
+			goto err;
+		}
 		slim->dev.platform_data = pdata;
 
 	} else {
@@ -1460,6 +1482,14 @@
 #define WCD9XXX_I2C_DIGITAL_1	2
 #define WCD9XXX_I2C_DIGITAL_2	3
 
+static struct i2c_device_id wcd9xxx_id_table[] = {
+	{"wcd9xxx-i2c", WCD9XXX_I2C_TOP_LEVEL},
+	{"wcd9xxx-i2c", WCD9XXX_I2C_ANALOG},
+	{"wcd9xxx-i2c", WCD9XXX_I2C_DIGITAL_1},
+	{"wcd9xxx-i2c", WCD9XXX_I2C_DIGITAL_2},
+	{}
+};
+
 static struct i2c_device_id tabla_id_table[] = {
 	{"tabla top level", WCD9XXX_I2C_TOP_LEVEL},
 	{"tabla analog", WCD9XXX_I2C_ANALOG},
@@ -1481,9 +1511,22 @@
 	.suspend = wcd9xxx_i2c_suspend,
 };
 
+static struct i2c_driver wcd9xxx_i2c_driver = {
+	.driver                 = {
+		.owner          =       THIS_MODULE,
+		.name           =       "wcd9xxx-i2c-core",
+	},
+	.id_table               =       wcd9xxx_id_table,
+	.probe                  =       wcd9xxx_i2c_probe,
+	.remove                 =       __devexit_p(wcd9xxx_i2c_remove),
+	.resume	= wcd9xxx_i2c_resume,
+	.suspend = wcd9xxx_i2c_suspend,
+};
+
+
 static int __init wcd9xxx_init(void)
 {
-	int ret1, ret2, ret3, ret4, ret5, ret6;
+	int ret1, ret2, ret3, ret4, ret5, ret6, ret7;
 
 	ret1 = slim_driver_register(&tabla_slim_driver);
 	if (ret1 != 0)
@@ -1495,7 +1538,7 @@
 
 	ret3 = i2c_add_driver(&tabla_i2c_driver);
 	if (ret3 != 0)
-		pr_err("failed to add the I2C driver\n");
+		pr_err("failed to add the tabla2x I2C driver\n");
 
 	ret4 = slim_driver_register(&sitar_slim_driver);
 	if (ret4 != 0)
@@ -1509,7 +1552,11 @@
 	if (ret6 != 0)
 		pr_err("Failed to register taiko SB driver: %d\n", ret6);
 
-	return (ret1 && ret2 && ret3 && ret4 && ret5 && ret6) ? -1 : 0;
+	ret7 = i2c_add_driver(&wcd9xxx_i2c_driver);
+	if (ret7 != 0)
+		pr_err("failed to add the wcd9xxx I2C driver\n");
+
+	return (ret1 && ret2 && ret3 && ret4 && ret5 && ret6 && ret7) ? -1 : 0;
 }
 module_init(wcd9xxx_init);
 
diff --git a/sound/soc/codecs/wcd9320.c b/sound/soc/codecs/wcd9320.c
index ed414d4..8fdf4f2 100644
--- a/sound/soc/codecs/wcd9320.c
+++ b/sound/soc/codecs/wcd9320.c
@@ -45,7 +45,8 @@
 #define TAIKO_TX_PORT_NUMBER	16
 
 #define TAIKO_I2S_MASTER_MODE_MASK 0x08
-
+#define TAIKO_MCLK_CLK_12P288MHZ 12288000
+#define TAIKO_MCLK_CLK_9P6HZ 9600000
 enum {
 	AIF1_PB = 0,
 	AIF1_CAP,
@@ -1538,45 +1539,50 @@
 			return -EINVAL;
 		}
 	}
-	switch (dai_id) {
-	case AIF1_CAP:
-	case AIF2_CAP:
-	case AIF3_CAP:
-		/* only add to the list if value not set
-		 */
-		if (enable && !(widget->value & 1 << port_id)) {
-			if (wcd9xxx_tx_vport_validation(
+	if (taiko_p->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+		switch (dai_id) {
+		case AIF1_CAP:
+		case AIF2_CAP:
+		case AIF3_CAP:
+			/* only add to the list if value not set
+			 */
+			if (enable && !(widget->value & 1 << port_id)) {
+				if (wcd9xxx_tx_vport_validation(
 						vport_check_table[dai_id],
 						port_id,
 						taiko_p->dai)) {
-				pr_info("%s: TX%u is used by other virtual port\n",
-					__func__, port_id + 1);
-				mutex_unlock(&codec->mutex);
-				return -EINVAL;
-			}
-			widget->value |= 1 << port_id;
-			list_add_tail(&core->tx_chs[port_id].list,
+					pr_debug("%s: TX%u is used by other\n"
+						"virtual port\n",
+						__func__, port_id + 1);
+					mutex_unlock(&codec->mutex);
+					return -EINVAL;
+				}
+				widget->value |= 1 << port_id;
+				list_add_tail(&core->tx_chs[port_id].list,
 				      &taiko_p->dai[dai_id].wcd9xxx_ch_list
-				      );
-		} else if (!enable && (widget->value & 1 << port_id)) {
-			widget->value &= ~(1 << port_id);
-			list_del_init(&core->tx_chs[port_id].list);
-		} else {
-			if (enable)
-				pr_info("%s: TX%u port is used by this virtual port\n",
-					__func__, port_id + 1);
-			else
-				pr_info("%s: TX%u port is not used by this virtual port\n",
-					__func__, port_id + 1);
-			/* avoid update power function */
+					      );
+			} else if (!enable && (widget->value & 1 << port_id)) {
+				widget->value &= ~(1 << port_id);
+				list_del_init(&core->tx_chs[port_id].list);
+			} else {
+				if (enable)
+					pr_debug("%s: TX%u port is used by\n"
+						"this virtual port\n",
+						__func__, port_id + 1);
+				else
+					pr_debug("%s: TX%u port is not used by\n"
+						"this virtual port\n",
+						__func__, port_id + 1);
+				/* avoid update power function */
+				mutex_unlock(&codec->mutex);
+				return 0;
+			}
+			break;
+		default:
+			pr_err("Unknown AIF %d\n", dai_id);
 			mutex_unlock(&codec->mutex);
-			return 0;
+			return -EINVAL;
 		}
-		break;
-	default:
-		pr_err("Unknown AIF %d\n", dai_id);
-		mutex_unlock(&codec->mutex);
-		return -EINVAL;
 	}
 	pr_debug("%s: name %s sname %s updated value %u shift %d\n", __func__,
 		widget->name, widget->sname, widget->value, widget->shift);
@@ -2438,10 +2444,10 @@
 	{"SLIM RX3", NULL, "RX_I2S_CLK"},
 	{"SLIM RX4", NULL, "RX_I2S_CLK"},
 
-	{"SLIM TX7", NULL, "TX_I2S_CLK"},
-	{"SLIM TX8", NULL, "TX_I2S_CLK"},
-	{"SLIM TX9", NULL, "TX_I2S_CLK"},
-	{"SLIM TX10", NULL, "TX_I2S_CLK"},
+	{"SLIM TX7 MUX", NULL, "TX_I2S_CLK"},
+	{"SLIM TX8 MUX", NULL, "TX_I2S_CLK"},
+	{"SLIM TX9 MUX", NULL, "TX_I2S_CLK"},
+	{"SLIM TX10 MUX", NULL, "TX_I2S_CLK"},
 };
 
 static const struct snd_soc_dapm_route audio_map[] = {
@@ -3101,7 +3107,11 @@
 static int taiko_set_dai_sysclk(struct snd_soc_dai *dai,
 		int clk_id, unsigned int freq, int dir)
 {
-	pr_debug("%s\n", __func__);
+	struct snd_soc_codec *codec = dai->codec;
+	if (freq == TAIKO_MCLK_CLK_12P288MHZ)
+		snd_soc_write(codec, TAIKO_A_CHIP_CTL, 0x04);
+	else if (freq == TAIKO_MCLK_CLK_9P6HZ)
+		snd_soc_write(codec, TAIKO_A_CHIP_CTL, 0x0A);
 	return 0;
 }
 
diff --git a/sound/soc/codecs/wcd9320.h b/sound/soc/codecs/wcd9320.h
index 7bc5a57..1fff80c 100644
--- a/sound/soc/codecs/wcd9320.h
+++ b/sound/soc/codecs/wcd9320.h
@@ -23,6 +23,7 @@
 #define TAIKO_CACHE_SIZE TAIKO_NUM_REGISTERS
 
 #define TAIKO_REG_VAL(reg, val)		{reg, 0, val}
+#define TAIKO_MCLK_ID 0
 
 extern const u8 taiko_reg_readable[TAIKO_CACHE_SIZE];
 extern const u8 taiko_reset_reg_defaults[TAIKO_CACHE_SIZE];