ASoC: apq8009: add changes to enable lpass_mclk for apq8009

Modify audio_ref_clk_probe and lpass_mclk_prepare functions to read lpass
gpio register and lpass-clock values from dtsi to enable lpass_mclk.

Enable LPASS_CLK_VER_2 and use cdc digital clock support for apq8009
target.

Change-Id: I5532d274b51cb69fbb58e39dd8f48bb3a8d10039
Signed-off-by: Raja Mallik <rmallik@codeaurora.org>
Signed-off-by: Chinkit Kumar,Kirti Kumar Parmar <parma@codeaurora.org>
Signed-off-by: Raja Mallik <rmallik@codeaurora.org>
diff --git a/asoc/codecs/audio-ext-clk.c b/asoc/codecs/audio-ext-clk.c
index a9db07c..df8e73a 100644
--- a/asoc/codecs/audio-ext-clk.c
+++ b/asoc/codecs/audio-ext-clk.c
@@ -25,12 +25,19 @@
 #include <dsp/q6afe-v2.h>
 #include "audio-ext-clk.h"
 
+#define clk_audio_lpass_mclk 0x575ec22b
+
 enum audio_clk_mux {
 	PMI_CLK,
 	AP_CLK2,
 	LPASS_MCLK,
 };
 
+enum clk_enablement {
+	CLK_DISABLE = 0,
+	CLK_ENABLE,
+};
+
 struct pinctrl_info {
 	struct pinctrl *pinctrl;
 	struct pinctrl_state *sleep;
@@ -56,6 +63,13 @@
 	struct clk c;
 };
 
+struct audio_ext_lpass_mclk {
+	struct pinctrl_info pnctrl_info;
+	struct clk c;
+	u32 lpass_clock;
+	void __iomem *lpass_csr_gpio_mux_spkrctl_vaddr;
+};
+
 static struct afe_clk_set clk2_config = {
 	Q6AFE_LPASS_CLK_CONFIG_API_VERSION,
 	Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_OSR,
@@ -65,6 +79,120 @@
 	0,
 };
 
+static struct afe_clk_set digital_cdc_core_clk = {
+	Q6AFE_LPASS_CLK_CONFIG_API_VERSION,
+	Q6AFE_LPASS_CLK_ID_INTERNAL_DIGITAL_CODEC_CORE,
+	Q6AFE_LPASS_OSR_CLK_9_P600_MHZ,
+	Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+	Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+	0,
+};
+
+static int audio_ext_set_lpass_mclk_v2(struct clk *clk,
+				       enum clk_enablement enable)
+{
+	struct audio_ext_lpass_mclk *audio_lpass_mclk;
+	int ret, val;
+
+	pr_debug("%s: Setting clock using v2, enable(%d)\n", __func__, enable);
+
+	audio_lpass_mclk = container_of(clk, struct audio_ext_lpass_mclk, c);
+	if (audio_lpass_mclk == NULL) {
+		pr_err("%s: audio_lpass_mclk is NULL\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (audio_lpass_mclk->lpass_csr_gpio_mux_spkrctl_vaddr &&
+	    enable) {
+		val = ioread32(audio_lpass_mclk->
+				lpass_csr_gpio_mux_spkrctl_vaddr);
+		val = val | 0x00000002;
+		iowrite32(val, audio_lpass_mclk->
+				lpass_csr_gpio_mux_spkrctl_vaddr);
+	}
+
+	digital_cdc_core_clk.enable = enable;
+	ret = afe_set_lpass_clock_v2(AFE_PORT_ID_PRIMARY_MI2S_RX,
+				     &digital_cdc_core_clk);
+	if (ret < 0) {
+		pr_err("%s: afe_set_digital_codec_core_clock failed\n"
+			" with ret %d\n", __func__, ret);
+		goto done;
+	}
+
+done:
+	return ret;
+}
+
+static int audio_ext_lpass_mclk_prepare(struct clk *clk)
+{
+	struct audio_ext_lpass_mclk *audio_lpass_mclk;
+	struct pinctrl_info *pnctrl_info;
+	enum lpass_clk_ver lpass_clk_ver;
+	int ret;
+
+	audio_lpass_mclk = container_of(clk, struct audio_ext_lpass_mclk, c);
+	if (audio_lpass_mclk == NULL) {
+		pr_err("%s: audio_lpass_mclk is NULL\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	pnctrl_info = &audio_lpass_mclk->pnctrl_info;
+	if (pnctrl_info && pnctrl_info->pinctrl) {
+		ret = pinctrl_select_state(pnctrl_info->pinctrl,
+					   pnctrl_info->active);
+		if (ret) {
+			pr_err("%s: pinctrl active state selection failed with %d\n",
+				__func__, ret);
+			ret = -EIO;
+			goto done;
+		}
+	}
+
+	lpass_clk_ver = afe_get_lpass_clk_ver();
+
+	if (lpass_clk_ver >= LPASS_CLK_VER_2)
+		ret = audio_ext_set_lpass_mclk_v2(clk, CLK_ENABLE);
+done:
+	return ret;
+}
+
+static void audio_ext_lpass_mclk_unprepare(struct clk *clk)
+{
+	struct audio_ext_lpass_mclk *audio_lpass_mclk;
+	struct pinctrl_info *pnctrl_info;
+	enum lpass_clk_ver lpass_clk_ver;
+	int ret;
+
+	audio_lpass_mclk = container_of(clk, struct audio_ext_lpass_mclk, c);
+	if (audio_lpass_mclk == NULL) {
+		pr_err("%s: audio_lpass_mclk is NULL\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	pnctrl_info = &audio_lpass_mclk->pnctrl_info;
+	if (pnctrl_info && pnctrl_info->pinctrl) {
+		ret = pinctrl_select_state(pnctrl_info->pinctrl,
+					   pnctrl_info->sleep);
+		if (ret) {
+			pr_err("%s: pinctrl sleep state selection failed with %d\n",
+				__func__, ret);
+			ret = -EIO;
+			goto done;
+		}
+	}
+
+	lpass_clk_ver = afe_get_lpass_clk_ver();
+
+	if (lpass_clk_ver >= LPASS_CLK_VER_2)
+		ret = audio_ext_set_lpass_mclk_v2(clk, CLK_DISABLE);
+done:
+	pr_debug("%s: Unprepare of mclk exiting with %d\n", __func__, ret);
+}
+
 static inline struct audio_ext_ap_clk *to_audio_ap_clk(struct clk *clk)
 {
 	return container_of(clk, struct audio_ext_ap_clk, c);
@@ -201,6 +329,12 @@
 	.prepare = audio_ext_pmi_clk_prepare,
 	.unprepare = audio_ext_pmi_clk_unprepare,
 };
+
+static struct clk_ops audio_ext_lpass_mclk_ops = {
+	.prepare = audio_ext_lpass_mclk_prepare,
+	.unprepare = audio_ext_lpass_mclk_unprepare,
+};
+
 static struct audio_ext_pmi_clk audio_pmi_lnbb_clk = {
 	.gpio = -EINVAL,
 	.c = {
@@ -234,10 +368,19 @@
 	},
 };
 
+static struct audio_ext_lpass_mclk audio_lpass_mclk = {
+	.c = {
+		.dbg_name = "audio_ext_lpass_mclk",
+		.ops = &audio_ext_lpass_mclk_ops,
+		CLK_INIT(audio_lpass_mclk.c),
+	},
+};
+
 static struct clk_lookup audio_ref_clock[] = {
 	CLK_LIST(audio_ap_clk),
 	CLK_LIST(audio_pmi_clk),
 	CLK_LIST(audio_ap_clk2),
+	CLK_LIST(audio_lpass_mclk),
 };
 
 static int audio_get_pinctrl(struct platform_device *pdev,
@@ -255,6 +398,9 @@
 	case AP_CLK2:
 		pnctrl_info = &audio_ap_clk2.pnctrl_info;
 		break;
+	case LPASS_MCLK:
+		pnctrl_info = &audio_lpass_mclk.pnctrl_info;
+		break;
 	default:
 		dev_err(dev, "%s Not a valid MUX ID: %d\n",
 			__func__, mux);
@@ -307,6 +453,40 @@
 	int clk_gpio;
 	int ret;
 	struct clk *div_clk1;
+	u32 lpass_csr_gpio_mux_spkrctl_reg = 0;
+
+	ret = of_property_read_u32(pdev->dev.of_node,
+			"qcom,lpass-clock",
+			&audio_lpass_mclk.lpass_clock);
+	if (ret)
+		dev_dbg(&pdev->dev, "%s: qcom,lpass-clock is undefined\n",
+				__func__);
+
+	if (audio_lpass_mclk.lpass_clock) {
+
+		ret = of_property_read_u32(pdev->dev.of_node, "reg",
+				&lpass_csr_gpio_mux_spkrctl_reg);
+		if (!ret) {
+			audio_lpass_mclk.lpass_csr_gpio_mux_spkrctl_vaddr =
+			devm_ioremap(&pdev->dev, lpass_csr_gpio_mux_spkrctl_reg, 4);
+			if (audio_lpass_mclk.lpass_csr_gpio_mux_spkrctl_vaddr == NULL) {
+				dev_err(&pdev->dev, "%s devm_ioremap failed\n", __func__);
+				return -ENOMEM;
+			}
+		}
+
+		ret = audio_get_pinctrl(pdev, LPASS_MCLK);
+		if (ret)
+			dev_err(&pdev->dev, "%s: Parsing pinctrl %s failed\n",
+				__func__, "LPASS_MCLK");
+
+		ret = of_msm_clock_register(pdev->dev.of_node, audio_ref_clock,
+			      ARRAY_SIZE(audio_ref_clock));
+		if (ret)
+			dev_err(&pdev->dev, "%s: clock register failed\n",
+				__func__);
+		return ret;
+	}
 
 	clk_gpio = of_get_named_gpio(pdev->dev.of_node,
 				     "qcom,audio-ref-clk-gpio", 0);
@@ -348,6 +528,7 @@
 static int audio_ref_clk_remove(struct platform_device *pdev)
 {
 	struct pinctrl_info *pnctrl_info = &audio_ap_clk2.pnctrl_info;
+	struct pinctrl_info *lpass_pnctrl_info = &audio_lpass_mclk.pnctrl_info;
 	struct pinctrl_info *pmi_pnctrl_info = &audio_pmi_clk.pnctrl_info;
 
 	if (audio_pmi_clk.gpio > 0)
@@ -360,11 +541,19 @@
 		pnctrl_info->pinctrl = NULL;
 	}
 
+	if (lpass_pnctrl_info->pinctrl) {
+		devm_pinctrl_put(lpass_pnctrl_info->pinctrl);
+		lpass_pnctrl_info->pinctrl = NULL;
+	}
+
 	if (pmi_pnctrl_info->pinctrl) {
 		devm_pinctrl_put(pmi_pnctrl_info->pinctrl);
 		pmi_pnctrl_info->pinctrl = NULL;
 	}
 
+	if (audio_lpass_mclk.lpass_csr_gpio_mux_spkrctl_vaddr)
+		devm_iounmap(&pdev->dev,
+		 audio_lpass_mclk.lpass_csr_gpio_mux_spkrctl_vaddr);
 	return 0;
 }