ASoC: msm8226: Add support to enable codec external MCLK

Add support to enable the clock and the PMIC GPIO needed for
clock used by WCD9306 codec.

Change-Id: Icd50d7b929821f79ad3eca9a02175dd72c27c1ac
Signed-off-by: Bhalchandra Gajare <gajare@codeaurora.org>
diff --git a/arch/arm/mach-msm/clock-8226.c b/arch/arm/mach-msm/clock-8226.c
index 813b210..1a28189 100644
--- a/arch/arm/mach-msm/clock-8226.c
+++ b/arch/arm/mach-msm/clock-8226.c
@@ -3350,6 +3350,15 @@
 	CLK_LOOKUP("", mmss_mmssnoc_bto_ahb_clk.c, ""),
 	CLK_LOOKUP("", mmss_mmssnoc_axi_clk.c, ""),
 	CLK_LOOKUP("", mmss_s0_axi_clk.c, ""),
+
+	/* Audio clocks */
+	CLK_LOOKUP("osr_clk", div_clk1.c, "msm-dai-q6-dev.224"),
+	CLK_LOOKUP("osr_clk", div_clk1.c, "msm-dai-q6-dev.4106"),
+	CLK_LOOKUP("osr_clk", div_clk1.c, "msm-dai-q6-dev.16384"),
+	CLK_LOOKUP("osr_clk", div_clk1.c, "msm-dai-q6-dev.16386"),
+	CLK_LOOKUP("osr_clk", div_clk1.c, "msm-dai-q6-dev.16390"),
+	CLK_LOOKUP("osr_clk", div_clk1.c, "msm-dai-q6-dev.16391"),
+
 };
 
 static struct clk_lookup msm_clocks_8226_rumi[] = {
diff --git a/sound/soc/msm/msm8226.c b/sound/soc/msm/msm8226.c
index 7766b60..4c92aab 100644
--- a/sound/soc/msm/msm8226.c
+++ b/sound/soc/msm/msm8226.c
@@ -84,6 +84,7 @@
 static int msm_btsco_ch = 1;
 
 static struct mutex cdc_mclk_mutex;
+static struct clk *codec_clk;
 static int clk_users;
 
 static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec, int enable,
@@ -95,21 +96,35 @@
 
 	mutex_lock(&cdc_mclk_mutex);
 	if (enable) {
+		if (!codec_clk) {
+			dev_err(codec->dev, "%s: did not get Taiko MCLK\n",
+					__func__);
+			ret = -EINVAL;
+			goto exit;
+		}
 
 		clk_users++;
 		if (clk_users != 1)
 			goto exit;
-		/* TODO: clk_disable */
-		tapan_mclk_enable(codec, 1, dapm);
+		if (codec_clk) {
+			clk_set_rate(codec_clk, TAPAN_EXT_CLK_RATE);
+			clk_prepare_enable(codec_clk);
+			tapan_mclk_enable(codec, 1, dapm);
+		} else {
+			pr_err("%s: Error setting Tapan MCLK\n", __func__);
+			clk_users--;
+			ret = -EINVAL;
+			goto exit;
+		}
 	} else {
 		if (clk_users > 0) {
 			clk_users--;
 			if (clk_users == 0) {
 				tapan_mclk_enable(codec, 0, dapm);
-				/* TODO: clk_enable */
+				clk_disable_unprepare(codec_clk);
 			}
 		} else {
-			pr_err("%s: Error releasing Tabla MCLK\n", __func__);
+			pr_err("%s: Error releasing Tapan MCLK\n", __func__);
 			ret = -EINVAL;
 			goto exit;
 		}
@@ -349,6 +364,11 @@
 
 	snd_soc_dapm_sync(dapm);
 
+	codec_clk = clk_get(cpu_dai->dev, "osr_clk");
+	if (codec_clk < 0)
+		pr_err("%s() Failed to get clock for %s\n",
+			   __func__, dev_name(cpu_dai->dev));
+
 	snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
 				    tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
 
@@ -957,6 +977,17 @@
 
 static int msm8226_prepare_codec_mclk(struct snd_soc_card *card)
 {
+	struct msm8226_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+	int ret;
+	if (pdata->mclk_gpio) {
+		ret = gpio_request(pdata->mclk_gpio, "TAPAN_CODEC_PMIC_MCLK");
+		if (ret) {
+			dev_err(card->dev,
+				"%s: Failed to request taiko mclk gpio %d\n",
+				__func__, pdata->mclk_gpio);
+			return ret;
+		}
+	}
 	return 0;
 }
 
@@ -1008,7 +1039,16 @@
 		goto err;
 	}
 
-	/* TODO: MCLK GPIO */
+	pdata->mclk_gpio = of_get_named_gpio(pdev->dev.of_node,
+				"qcom,cdc-mclk-gpios", 0);
+	if (pdata->mclk_gpio < 0) {
+		dev_err(&pdev->dev,
+			"Looking up %s property in node %s failed %d\n",
+			"qcom, cdc-mclk-gpios", pdev->dev.of_node->full_name,
+			pdata->mclk_gpio);
+		ret = -ENODEV;
+		goto err;
+	}
 
 	ret = msm8226_prepare_codec_mclk(card);
 	if (ret)
@@ -1024,6 +1064,12 @@
 
 	return 0;
 err:
+	if (pdata->mclk_gpio > 0) {
+		dev_dbg(&pdev->dev, "%s free gpio %d\n",
+			__func__, pdata->mclk_gpio);
+		gpio_free(pdata->mclk_gpio);
+		pdata->mclk_gpio = 0;
+	}
 	devm_kfree(&pdev->dev, pdata);
 	return ret;
 }
@@ -1031,8 +1077,9 @@
 static int __devexit msm8226_asoc_machine_remove(struct platform_device *pdev)
 {
 	struct snd_soc_card *card = platform_get_drvdata(pdev);
+	struct msm8226_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
 
-	/* TODO: GPIO MCLK */
+	gpio_free(pdata->mclk_gpio);
 	snd_soc_unregister_card(card);
 
 	return 0;