ASoC: codecs: Add support for LPASS Audio HW vote

Add support for LPASS Audio HW vote needed for low power AMIC
VA usecase.

Change-Id: If6642d418dbf2cc9773fbd0ec0fe35c30afac140
Signed-off-by: Sudheer Papothi <spapothi@codeaurora.org>
diff --git a/asoc/codecs/audio-ext-clk-up.c b/asoc/codecs/audio-ext-clk-up.c
index 3f622d6..632f674 100644
--- a/asoc/codecs/audio-ext-clk-up.c
+++ b/asoc/codecs/audio-ext-clk-up.c
@@ -28,6 +28,7 @@
 	AUDIO_EXT_CLK_LPASS7,
 	AUDIO_EXT_CLK_LPASS_CORE_HW_VOTE,
 	AUDIO_EXT_CLK_LPASS8,
+	AUDIO_EXT_CLK_LPASS_AUDIO_HW_VOTE,
 	AUDIO_EXT_CLK_LPASS_MAX,
 	AUDIO_EXT_CLK_EXTERNAL_PLL = AUDIO_EXT_CLK_LPASS_MAX,
 	AUDIO_EXT_CLK_MAX,
@@ -52,6 +53,7 @@
 	struct audio_ext_clk audio_clk;
 	const char *clk_name;
 	uint32_t lpass_core_hwvote_client_handle;
+	uint32_t lpass_audio_hwvote_client_handle;
 };
 
 static inline struct audio_ext_clk_priv *to_audio_clk(struct clk_hw *hw)
@@ -154,6 +156,17 @@
 		}
 	}
 
+	if (clk_priv->clk_src == AUDIO_EXT_CLK_LPASS_AUDIO_HW_VOTE)  {
+		ret = afe_vote_lpass_core_hw(AFE_LPASS_CORE_HW_DCODEC_BLOCK,
+			"LPASS_HW_DCODEC",
+			&clk_priv->lpass_audio_hwvote_client_handle);
+		if (ret < 0) {
+			pr_err("%s lpass audio hw vote failed %d\n",
+				__func__, ret);
+			return ret;
+		}
+	}
+
 	return 0;
 }
 
@@ -171,6 +184,16 @@
 				__func__, ret);
 		}
 	}
+
+	if (clk_priv->clk_src == AUDIO_EXT_CLK_LPASS_AUDIO_HW_VOTE) {
+		ret = afe_unvote_lpass_core_hw(
+			AFE_LPASS_CORE_HW_DCODEC_BLOCK,
+			clk_priv->lpass_audio_hwvote_client_handle);
+		if (ret < 0) {
+			pr_err("%s lpass audio hw unvote failed %d\n",
+				__func__, ret);
+		}
+	}
 }
 
 static const struct clk_ops audio_ext_clk_ops = {
@@ -335,6 +358,15 @@
 	{
 		.pnctrl_info = {NULL},
 		.fact = {
+			.hw.init = &(struct clk_init_data){
+				.name = "lpass_audio_hw_vote_clk",
+				.ops = &lpass_hw_vote_ops,
+			},
+		},
+	},
+	{
+		.pnctrl_info = {NULL},
+		.fact = {
 			.mult = 1,
 			.div = 1,
 			.hw.init = &(struct clk_init_data){
diff --git a/asoc/codecs/bolero/bolero-cdc.c b/asoc/codecs/bolero/bolero-cdc.c
index fc940c8..cee08dc 100644
--- a/asoc/codecs/bolero/bolero-cdc.c
+++ b/asoc/codecs/bolero/bolero-cdc.c
@@ -909,6 +909,7 @@
 	u32 num_macros = 0;
 	int ret;
 	struct clk *lpass_core_hw_vote = NULL;
+	struct clk *lpass_audio_hw_vote = NULL;
 
 	priv = devm_kzalloc(&pdev->dev, sizeof(struct bolero_priv),
 			    GFP_KERNEL);
@@ -968,6 +969,17 @@
 	}
 	priv->lpass_core_hw_vote = lpass_core_hw_vote;
 
+	/* Register LPASS audio hw vote */
+	lpass_audio_hw_vote = devm_clk_get(&pdev->dev, "lpass_audio_hw_vote");
+	if (IS_ERR(lpass_audio_hw_vote)) {
+		ret = PTR_ERR(lpass_audio_hw_vote);
+		dev_dbg(&pdev->dev, "%s: clk get %s failed %d\n",
+			__func__, "lpass_audio_hw_vote", ret);
+		lpass_audio_hw_vote = NULL;
+		ret = 0;
+	}
+	priv->lpass_audio_hw_vote = lpass_audio_hw_vote;
+
 	return 0;
 }
 
@@ -999,6 +1011,16 @@
 		dev_err(dev, "%s:lpass core hw enable failed\n",
 			__func__);
 
+	if (priv->lpass_audio_hw_vote == NULL) {
+		dev_dbg(dev, "%s: Invalid lpass audio hw node\n", __func__);
+		return 0;
+	}
+
+	ret = clk_prepare_enable(priv->lpass_audio_hw_vote);
+	if (ret < 0)
+		dev_err(dev, "%s:lpass audio hw enable failed\n",
+			__func__);
+
 	pm_runtime_set_autosuspend_delay(priv->dev, BOLERO_AUTO_SUSPEND_DELAY);
 	return 0;
 }
@@ -1013,6 +1035,12 @@
 	else
 		dev_dbg(dev, "%s: Invalid lpass core hw node\n",
 			__func__);
+
+	if (priv->lpass_audio_hw_vote != NULL)
+		clk_disable_unprepare(priv->lpass_audio_hw_vote);
+	else
+		dev_dbg(dev, "%s: Invalid lpass audio hw node\n",
+			__func__);
 	return 0;
 }
 EXPORT_SYMBOL(bolero_runtime_suspend);
diff --git a/asoc/codecs/bolero/internal.h b/asoc/codecs/bolero/internal.h
index 734afbc..d07c5b1 100644
--- a/asoc/codecs/bolero/internal.h
+++ b/asoc/codecs/bolero/internal.h
@@ -58,6 +58,7 @@
 	struct work_struct bolero_add_child_devices_work;
 	u32 version;
 	struct clk *lpass_core_hw_vote;
+	struct clk *lpass_audio_hw_vote;
 
 	/* Entry for version info */
 	struct snd_info_entry *entry;
diff --git a/asoc/codecs/bolero/va-macro.c b/asoc/codecs/bolero/va-macro.c
index 3bba403..ba9c7f1 100644
--- a/asoc/codecs/bolero/va-macro.c
+++ b/asoc/codecs/bolero/va-macro.c
@@ -40,6 +40,8 @@
 #define VA_MACRO_TX_PATH_OFFSET 0x80
 #define VA_MACRO_TX_DMIC_CLK_DIV_MASK 0x0E
 #define VA_MACRO_TX_DMIC_CLK_DIV_SHFT 0x01
+#define VA_MACRO_SWR_MIC_MUX_SEL_MASK 0xF
+#define VA_MACRO_ADC_MUX_CFG_OFFSET 0x2
 
 #define BOLERO_CDC_VA_TX_UNMUTE_DELAY_MS	40
 #define MAX_RETRY_ATTEMPTS 500
@@ -78,6 +80,11 @@
 	VA_MACRO_CLK_DIV_16,
 };
 
+enum {
+	MSM_DMIC,
+	SWR_MIC,
+};
+
 struct va_mute_work {
 	struct va_macro_priv *va_priv;
 	u32 decimator;
@@ -95,6 +102,7 @@
 	struct device *dev;
 	bool dec_active[VA_MACRO_NUM_DECIMATORS];
 	bool va_without_decimation;
+	struct clk *lpass_audio_hw_vote;
 	struct mutex mclk_lock;
 	struct snd_soc_component *component;
 	struct hpf_work va_hpf_work[VA_MACRO_NUM_DECIMATORS];
@@ -232,6 +240,41 @@
 	return 0;
 }
 
+static int va_macro_swr_pwr_event(struct snd_soc_dapm_widget *w,
+			       struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_component *component =
+			snd_soc_dapm_to_component(w->dapm);
+	int ret = 0;
+	struct device *va_dev = NULL;
+	struct va_macro_priv *va_priv = NULL;
+
+	if (!va_macro_get_data(component, &va_dev, &va_priv, __func__))
+		return -EINVAL;
+
+	dev_dbg(va_dev, "%s: event = %d\n", __func__, event);
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		if (va_priv->lpass_audio_hw_vote) {
+			ret = clk_prepare_enable(va_priv->lpass_audio_hw_vote);
+			if (ret)
+				dev_err(va_dev,
+					"%s: lpass audio hw enable failed\n",
+					__func__);
+		}
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		if (va_priv->lpass_audio_hw_vote)
+			clk_disable_unprepare(va_priv->lpass_audio_hw_vote);
+		break;
+	default:
+		dev_err(va_priv->dev,
+			"%s: invalid DAPM event %d\n", __func__, event);
+		ret = -EINVAL;
+	}
+	return ret;
+}
+
 static int va_macro_mclk_event(struct snd_soc_dapm_widget *w,
 			       struct snd_kcontrol *kcontrol, int event)
 {
@@ -254,6 +297,10 @@
 						   true);
 		break;
 	case SND_SOC_DAPM_POST_PMD:
+		bolero_clk_rsc_request_clock(va_priv->dev,
+					   va_priv->default_clk_id,
+					   TX_CORE_CLK,
+					   false);
 		va_macro_mclk_enable(va_priv, 0, true);
 		break;
 	default:
@@ -272,6 +319,7 @@
 	struct snd_soc_component *component;
 	u16 dec_cfg_reg, hpf_gate_reg;
 	u8 hpf_cut_off_freq;
+	u16 adc_mux_reg = 0, adc_n = 0, adc_reg = 0;
 
 	hpf_delayed_work = to_delayed_work(work);
 	hpf_work = container_of(hpf_delayed_work, struct hpf_work, dwork);
@@ -287,6 +335,19 @@
 	dev_dbg(va_priv->dev, "%s: decimator %u hpf_cut_of_freq 0x%x\n",
 		__func__, hpf_work->decimator, hpf_cut_off_freq);
 
+	adc_mux_reg = BOLERO_CDC_VA_INP_MUX_ADC_MUX0_CFG1 +
+			VA_MACRO_ADC_MUX_CFG_OFFSET * hpf_work->decimator;
+	if (snd_soc_component_read32(component, adc_mux_reg) & SWR_MIC) {
+		adc_reg = BOLERO_CDC_VA_INP_MUX_ADC_MUX0_CFG0 +
+			VA_MACRO_ADC_MUX_CFG_OFFSET * hpf_work->decimator;
+		adc_n = snd_soc_component_read32(component, adc_reg) &
+				VA_MACRO_SWR_MIC_MUX_SEL_MASK;
+		if (adc_n >= BOLERO_ADC_MAX)
+			goto va_hpf_set;
+		/* analog mic clear TX hold */
+		bolero_clear_amic_tx_hold(component->dev, adc_n);
+	}
+va_hpf_set:
 	snd_soc_component_update_bits(component,
 			dec_cfg_reg, TX_HPF_CUT_OFF_FREQ_MASK,
 			hpf_cut_off_freq << 5);
@@ -366,10 +427,23 @@
 			__func__, e->reg);
 		return -EINVAL;
 	}
-	/* DMIC selected */
-	if (val != 0)
-		snd_soc_component_update_bits(component, mic_sel_reg,
-				1 << 7, 1 << 7);
+	if (strnstr(widget->name, "SMIC", strlen(widget->name))) {
+		if (val != 0) {
+			if (val < 5)
+				snd_soc_component_update_bits(component,
+							mic_sel_reg,
+							1 << 7, 0x0 << 7);
+			else
+				snd_soc_component_update_bits(component,
+							mic_sel_reg,
+							1 << 7, 0x1 << 7);
+		}
+	} else {
+		/* DMIC selected */
+		if (val != 0)
+			snd_soc_component_update_bits(component, mic_sel_reg,
+					1 << 7, 1 << 7);
+	}
 
 	return snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
 }
@@ -649,6 +723,12 @@
 						   TX_CORE_CLK,
 						   false);
 		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		ret = bolero_clk_rsc_request_clock(va_priv->dev,
+						   va_priv->default_clk_id,
+						   TX_CORE_CLK,
+						   true);
+		break;
 	default:
 		dev_err(va_priv->dev,
 			"%s: invalid DAPM event %d\n", __func__, event);
@@ -1036,15 +1116,18 @@
 static const struct snd_soc_dapm_widget va_macro_dapm_widgets[] = {
 	SND_SOC_DAPM_AIF_OUT_E("VA_AIF1 CAP", "VA_AIF1 Capture", 0,
 		SND_SOC_NOPM, VA_MACRO_AIF1_CAP, 0,
-		va_macro_enable_tx, SND_SOC_DAPM_POST_PMU),
+		va_macro_enable_tx, SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_PRE_PMD),
 
 	SND_SOC_DAPM_AIF_OUT_E("VA_AIF2 CAP", "VA_AIF2 Capture", 0,
 		SND_SOC_NOPM, VA_MACRO_AIF2_CAP, 0,
-		va_macro_enable_tx, SND_SOC_DAPM_POST_PMU),
+		va_macro_enable_tx, SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_PRE_PMD),
 
 	SND_SOC_DAPM_AIF_OUT_E("VA_AIF3 CAP", "VA_AIF3 Capture", 0,
 		SND_SOC_NOPM, VA_MACRO_AIF3_CAP, 0,
-		va_macro_enable_tx, SND_SOC_DAPM_POST_PMU),
+		va_macro_enable_tx, SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_PRE_PMD),
 
 	SND_SOC_DAPM_MIXER("VA_AIF1_CAP Mixer", SND_SOC_NOPM,
 		VA_MACRO_AIF1_CAP, 0,
@@ -1165,6 +1248,10 @@
 			   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
 			   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
 
+	SND_SOC_DAPM_SUPPLY_S("VA_SWR_PWR", -1, SND_SOC_NOPM, 0, 0,
+			      va_macro_swr_pwr_event,
+			      SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
 	SND_SOC_DAPM_SUPPLY_S("VA_MCLK", -1, SND_SOC_NOPM, 0, 0,
 			      va_macro_mclk_event,
 			      SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
@@ -1403,6 +1490,11 @@
 	{"VA SMIC MUX7", "SWR_DMIC5", "VA SWR_MIC5"},
 	{"VA SMIC MUX7", "SWR_DMIC6", "VA SWR_MIC6"},
 	{"VA SMIC MUX7", "SWR_DMIC7", "VA SWR_MIC7"},
+
+	{"VA SWR_ADC0", NULL, "VA_SWR_PWR"},
+	{"VA SWR_ADC1", NULL, "VA_SWR_PWR"},
+	{"VA SWR_ADC2", NULL, "VA_SWR_PWR"},
+	{"VA SWR_ADC3", NULL, "VA_SWR_PWR"},
 };
 
 static const struct snd_kcontrol_new va_macro_snd_controls[] = {
@@ -1621,6 +1713,7 @@
 	int ret = 0;
 	const char *dmic_sample_rate = "qcom,va-dmic-sample-rate";
 	u32 default_clk_id = 0;
+	struct clk *lpass_audio_hw_vote = NULL;
 
 	va_priv = devm_kzalloc(&pdev->dev, sizeof(struct va_macro_priv),
 			    GFP_KERNEL);
@@ -1659,6 +1752,16 @@
 	}
 	va_priv->va_io_base = va_io_base;
 
+	lpass_audio_hw_vote = devm_clk_get(&pdev->dev, "lpass_audio_hw_vote");
+	if (IS_ERR(lpass_audio_hw_vote)) {
+		ret = PTR_ERR(lpass_audio_hw_vote);
+		dev_dbg(&pdev->dev, "%s: clk get %s failed %d\n",
+			__func__, "lpass_audio_hw_vote", ret);
+		lpass_audio_hw_vote = NULL;
+		ret = 0;
+	}
+	va_priv->lpass_audio_hw_vote = lpass_audio_hw_vote;
+
 	if (of_parse_phandle(pdev->dev.of_node, micb_supply_str, 0)) {
 		va_priv->micb_supply = devm_regulator_get(&pdev->dev,
 						micb_supply_str1);
diff --git a/include/dsp/q6afe-v2.h b/include/dsp/q6afe-v2.h
index 323ca1e..8915501 100644
--- a/include/dsp/q6afe-v2.h
+++ b/include/dsp/q6afe-v2.h
@@ -483,6 +483,7 @@
 #define AFE_LPASS_CORE_HW_BLOCK_ID_NONE                        0
 #define AFE_LPASS_CORE_HW_BLOCK_ID_AVTIMER                     2
 #define AFE_LPASS_CORE_HW_MACRO_BLOCK                          3
+#define AFE_LPASS_CORE_HW_DCODEC_BLOCK                         4
 
 /* Handles audio-video timer (avtimer) and BTSC vote requests from clients.
  */