asoc: bolero: add SSR changes support on bolero codec

Add changes for audio SSR and PDR on bolero codec
and respective macro drivers.

Change-Id: I146de15022cebb788ccb52ed6b8ab85b7cba2ba0
Signed-off-by: Vaishnavi Kommaraju <vkommara@codeaurora.org>
Signed-off-by: Rohit kumar <rohitkr@codeaurora.org>
Signed-off-by: Laxminath Kasam <lkasam@codeaurora.org>
diff --git a/asoc/codecs/bolero/bolero-cdc.c b/asoc/codecs/bolero/bolero-cdc.c
index e9ccee4..5001b7f 100644
--- a/asoc/codecs/bolero/bolero-cdc.c
+++ b/asoc/codecs/bolero/bolero-cdc.c
@@ -18,7 +18,7 @@
 #include <linux/printk.h>
 #include <linux/delay.h>
 #include <linux/kernel.h>
-
+#include <soc/snd_event.h>
 #include "bolero-cdc.h"
 #include "internal.h"
 
@@ -62,6 +62,11 @@
 	u16 current_mclk_mux_macro;
 
 	mutex_lock(&priv->clk_lock);
+	if (!priv->dev_up) {
+		dev_dbg_ratelimited(priv->dev,
+			"%s: SSR in progress, exit\n", __func__);
+		goto err;
+	}
 	current_mclk_mux_macro =
 		priv->current_mclk_mux_macro[macro_id];
 	if (!priv->macro_params[current_mclk_mux_macro].mclk_fn) {
@@ -94,6 +99,11 @@
 	u16 current_mclk_mux_macro;
 
 	mutex_lock(&priv->clk_lock);
+	if (!priv->dev_up) {
+		dev_dbg_ratelimited(priv->dev,
+			"%s: SSR in progress, exit\n", __func__);
+		goto err;
+	}
 	current_mclk_mux_macro =
 		priv->current_mclk_mux_macro[macro_id];
 	if (!priv->macro_params[current_mclk_mux_macro].mclk_fn) {
@@ -529,10 +539,64 @@
 	return simple_read_from_buffer(buf, count, &pos, buffer, len);
 }
 
+static int bolero_ssr_enable(struct device *dev, void *data)
+{
+	struct bolero_priv *priv = data;
+	int macro_idx;
+
+	if (priv->initial_boot) {
+		priv->initial_boot = false;
+		return 0;
+	}
+
+	if (priv->macro_params[VA_MACRO].event_handler)
+		priv->macro_params[VA_MACRO].event_handler(priv->codec,
+			BOLERO_MACRO_EVT_WAIT_VA_CLK_RESET, 0x0);
+
+	regcache_cache_only(priv->regmap, false);
+	/* call ssr event for supported macros */
+	for (macro_idx = START_MACRO; macro_idx < MAX_MACRO; macro_idx++) {
+		if (!priv->macro_params[macro_idx].event_handler)
+			continue;
+		priv->macro_params[macro_idx].event_handler(priv->codec,
+			BOLERO_MACRO_EVT_SSR_UP, 0x0);
+	}
+	mutex_lock(&priv->clk_lock);
+	priv->dev_up = true;
+	mutex_unlock(&priv->clk_lock);
+	bolero_cdc_notifier_call(priv, BOLERO_WCD_EVT_SSR_UP);
+	return 0;
+}
+
+static void bolero_ssr_disable(struct device *dev, void *data)
+{
+	struct bolero_priv *priv = data;
+	int macro_idx;
+
+	regcache_cache_only(priv->regmap, true);
+
+	mutex_lock(&priv->clk_lock);
+	priv->dev_up = false;
+	mutex_unlock(&priv->clk_lock);
+	/* call ssr event for supported macros */
+	for (macro_idx = START_MACRO; macro_idx < MAX_MACRO; macro_idx++) {
+		if (!priv->macro_params[macro_idx].event_handler)
+			continue;
+		priv->macro_params[macro_idx].event_handler(priv->codec,
+			BOLERO_MACRO_EVT_SSR_DOWN, 0x0);
+	}
+	bolero_cdc_notifier_call(priv, BOLERO_WCD_EVT_SSR_DOWN);
+}
+
 static struct snd_info_entry_ops bolero_info_ops = {
 	.read = bolero_version_read,
 };
 
+static const struct snd_event_ops bolero_ssr_ops = {
+	.enable = bolero_ssr_enable,
+	.disable = bolero_ssr_disable,
+};
+
 /*
  * bolero_info_create_codec_entry - creates bolero module
  * @codec_root: The parent directory
@@ -623,6 +687,16 @@
 	else if (priv->num_macros_registered > 2)
 		priv->version = BOLERO_VERSION_1_2;
 
+	ret = snd_event_client_register(priv->dev, &bolero_ssr_ops, priv);
+	if (!ret) {
+		snd_event_notify(priv->dev, SND_EVENT_UP);
+	} else {
+		dev_err(codec->dev,
+			"%s: Registration with SND event FWK failed ret = %d\n",
+			__func__, ret);
+		goto err;
+	}
+
 	dev_dbg(codec->dev, "%s: bolero soc codec probe success\n", __func__);
 err:
 	return ret;
@@ -633,6 +707,7 @@
 	struct bolero_priv *priv = dev_get_drvdata(codec->dev);
 	int macro_idx;
 
+	snd_event_client_deregister(priv->dev);
 	/* call exit for supported macros */
 	for (macro_idx = START_MACRO; macro_idx < MAX_MACRO; macro_idx++)
 		if (priv->macro_params[macro_idx].exit)
@@ -756,6 +831,8 @@
 		bolero_reg_access[VA_MACRO] = bolero_va_top_reg_access;
 
 	priv->dev = &pdev->dev;
+	priv->dev_up = true;
+	priv->initial_boot = true;
 	priv->regmap = bolero_regmap_init(priv->dev,
 					  &bolero_regmap_config);
 	if (IS_ERR_OR_NULL((void *)(priv->regmap))) {
diff --git a/asoc/codecs/bolero/bolero-cdc.h b/asoc/codecs/bolero/bolero-cdc.h
index 3e8197d..aa52491 100644
--- a/asoc/codecs/bolero/bolero-cdc.h
+++ b/asoc/codecs/bolero/bolero-cdc.h
@@ -43,6 +43,9 @@
 	BOLERO_MACRO_EVT_RX_MUTE = 1, /* for RX mute/unmute */
 	BOLERO_MACRO_EVT_IMPED_TRUE, /* for imped true */
 	BOLERO_MACRO_EVT_IMPED_FALSE, /* for imped false */
+	BOLERO_MACRO_EVT_SSR_DOWN,
+	BOLERO_MACRO_EVT_SSR_UP,
+	BOLERO_MACRO_EVT_WAIT_VA_CLK_RESET
 };
 
 struct macro_ops {
diff --git a/asoc/codecs/bolero/internal.h b/asoc/codecs/bolero/internal.h
index 9e8a74c..0129c39 100644
--- a/asoc/codecs/bolero/internal.h
+++ b/asoc/codecs/bolero/internal.h
@@ -20,6 +20,8 @@
 /* from bolero to WCD events */
 enum {
 	BOLERO_WCD_EVT_TX_CH_HOLD_CLEAR = 1,
+	BOLERO_WCD_EVT_SSR_DOWN,
+	BOLERO_WCD_EVT_SSR_UP,
 };
 
 enum {
@@ -52,6 +54,8 @@
 	struct mutex clk_lock;
 	bool va_without_decimation;
 	bool macros_supported[MAX_MACRO];
+	bool dev_up;
+	bool initial_boot;
 	struct macro_ops macro_params[MAX_MACRO];
 	struct snd_soc_dai_driver *bolero_dais;
 	u16 num_dais;
diff --git a/asoc/codecs/bolero/rx-macro.c b/asoc/codecs/bolero/rx-macro.c
index a0e189b..6c936cb 100644
--- a/asoc/codecs/bolero/rx-macro.c
+++ b/asoc/codecs/bolero/rx-macro.c
@@ -1130,6 +1130,19 @@
 	case BOLERO_MACRO_EVT_IMPED_FALSE:
 		rx_macro_wcd_clsh_imped_config(codec, data, false);
 		break;
+	case BOLERO_MACRO_EVT_SSR_DOWN:
+		swrm_wcd_notify(
+			rx_priv->swr_ctrl_data[0].rx_swr_pdev,
+			SWR_DEVICE_SSR_DOWN, NULL);
+		swrm_wcd_notify(
+			rx_priv->swr_ctrl_data[0].rx_swr_pdev,
+			SWR_DEVICE_DOWN, NULL);
+		break;
+	case BOLERO_MACRO_EVT_SSR_UP:
+		swrm_wcd_notify(
+			rx_priv->swr_ctrl_data[0].rx_swr_pdev,
+			SWR_DEVICE_SSR_UP, NULL);
+		break;
 	}
 	return 0;
 }
diff --git a/asoc/codecs/bolero/tx-macro.c b/asoc/codecs/bolero/tx-macro.c
index 9cfd94d..44826de 100644
--- a/asoc/codecs/bolero/tx-macro.c
+++ b/asoc/codecs/bolero/tx-macro.c
@@ -19,6 +19,7 @@
 #include <sound/soc.h>
 #include <sound/soc-dapm.h>
 #include <sound/tlv.h>
+#include <soc/swr-wcd.h>
 #include "bolero-cdc.h"
 #include "bolero-cdc-registers.h"
 #include "../msm-cdc-pinctrl.h"
@@ -303,6 +304,33 @@
 	return ret;
 }
 
+static int tx_macro_event_handler(struct snd_soc_codec *codec, u16 event,
+				  u32 data)
+{
+	struct device *tx_dev = NULL;
+	struct tx_macro_priv *tx_priv = NULL;
+
+	if (!tx_macro_get_data(codec, &tx_dev, &tx_priv, __func__))
+		return -EINVAL;
+
+	switch (event) {
+	case BOLERO_MACRO_EVT_SSR_DOWN:
+		swrm_wcd_notify(
+			tx_priv->swr_ctrl_data[0].tx_swr_pdev,
+			SWR_DEVICE_SSR_DOWN, NULL);
+		swrm_wcd_notify(
+			tx_priv->swr_ctrl_data[0].tx_swr_pdev,
+			SWR_DEVICE_DOWN, NULL);
+		break;
+	case BOLERO_MACRO_EVT_SSR_UP:
+		swrm_wcd_notify(
+			tx_priv->swr_ctrl_data[0].tx_swr_pdev,
+			SWR_DEVICE_SSR_UP, NULL);
+		break;
+	}
+	return 0;
+}
+
 static void tx_macro_tx_hpf_corner_freq_callback(struct work_struct *work)
 {
 	struct delayed_work *hpf_delayed_work = NULL;
@@ -1640,6 +1668,7 @@
 	ops->dai_ptr = tx_macro_dai;
 	ops->num_dais = ARRAY_SIZE(tx_macro_dai);
 	ops->mclk_fn = tx_macro_mclk_ctrl;
+	ops->event_handler = tx_macro_event_handler;
 }
 
 static int tx_macro_probe(struct platform_device *pdev)
diff --git a/asoc/codecs/bolero/va-macro.c b/asoc/codecs/bolero/va-macro.c
index eb069fa..1d28e41 100644
--- a/asoc/codecs/bolero/va-macro.c
+++ b/asoc/codecs/bolero/va-macro.c
@@ -46,6 +46,7 @@
 #define VA_MACRO_TX_DMIC_CLK_DIV_SHFT 0x01
 
 #define BOLERO_CDC_VA_TX_UNMUTE_DELAY_MS	40
+#define MAX_RETRY_ATTEMPTS 50
 
 static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0);
 static int va_tx_unmute_delay = BOLERO_CDC_VA_TX_UNMUTE_DELAY_MS;
@@ -178,8 +179,7 @@
 				0x02, 0x02);
 		}
 	} else {
-		va_priv->va_mclk_users--;
-		if (va_priv->va_mclk_users == 0) {
+		if (va_priv->va_mclk_users == 1) {
 			regmap_update_bits(regmap,
 				BOLERO_CDC_VA_TOP_CSR_TOP_CFG0,
 				0x02, 0x00);
@@ -192,12 +192,47 @@
 			bolero_request_clock(va_priv->dev,
 					VA_MACRO, MCLK_MUX0, false);
 		}
+		va_priv->va_mclk_users--;
 	}
 exit:
 	mutex_unlock(&va_priv->mclk_lock);
 	return ret;
 }
 
+static int va_macro_event_handler(struct snd_soc_codec *codec, u16 event,
+				  u32 data)
+{
+	struct device *va_dev = NULL;
+	struct va_macro_priv *va_priv = NULL;
+	int retry_cnt = MAX_RETRY_ATTEMPTS;
+
+	if (!va_macro_get_data(codec, &va_dev, &va_priv, __func__))
+		return -EINVAL;
+
+	switch (event) {
+	case BOLERO_MACRO_EVT_WAIT_VA_CLK_RESET:
+		while ((va_priv->va_mclk_users != 0) && (retry_cnt != 0)) {
+			dev_dbg(va_dev, "%s:retry_cnt: %d\n",
+				__func__, retry_cnt);
+			/*
+			 * loop and check every 20ms for va_mclk user count
+			 * to get reset to 0 which ensures userspace teardown
+			 * is done and SSR powerup seq can proceed.
+			 */
+			msleep(20);
+			retry_cnt--;
+		}
+		if (retry_cnt == 0)
+			dev_err(va_dev,
+				"%s: va_mclk_users is non-zero still, audio SSR fail!!\n",
+				__func__);
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
 static int va_macro_mclk_event(struct snd_soc_dapm_widget *w,
 			       struct snd_kcontrol *kcontrol, int event)
 {
@@ -1054,13 +1089,13 @@
 			   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_MCLK", 0, SND_SOC_NOPM, 0, 0,
+	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),
 };
 
 static const struct snd_soc_dapm_widget va_macro_wod_dapm_widgets[] = {
-	SND_SOC_DAPM_SUPPLY_S("VA_MCLK", 0, SND_SOC_NOPM, 0, 0,
+	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),
 };
@@ -1465,6 +1500,7 @@
 	ops->exit = va_macro_deinit;
 	ops->io_base = va_io_base;
 	ops->mclk_fn = va_macro_mclk_ctrl;
+	ops->event_handler = va_macro_event_handler;
 }
 
 static int va_macro_probe(struct platform_device *pdev)
diff --git a/asoc/codecs/bolero/wsa-macro.c b/asoc/codecs/bolero/wsa-macro.c
index 3fe637e..d527186 100644
--- a/asoc/codecs/bolero/wsa-macro.c
+++ b/asoc/codecs/bolero/wsa-macro.c
@@ -859,6 +859,33 @@
 	return ret;
 }
 
+static int wsa_macro_event_handler(struct snd_soc_codec *codec, u16 event,
+				   u32 data)
+{
+	struct device *wsa_dev = NULL;
+	struct wsa_macro_priv *wsa_priv = NULL;
+
+	if (!wsa_macro_get_data(codec, &wsa_dev, &wsa_priv, __func__))
+		return -EINVAL;
+
+	switch (event) {
+	case BOLERO_MACRO_EVT_SSR_DOWN:
+		swrm_wcd_notify(
+			wsa_priv->swr_ctrl_data[0].wsa_swr_pdev,
+			SWR_DEVICE_SSR_DOWN, NULL);
+		swrm_wcd_notify(
+			wsa_priv->swr_ctrl_data[0].wsa_swr_pdev,
+			SWR_DEVICE_DOWN, NULL);
+		break;
+	case BOLERO_MACRO_EVT_SSR_UP:
+		swrm_wcd_notify(
+			wsa_priv->swr_ctrl_data[0].wsa_swr_pdev,
+			SWR_DEVICE_SSR_UP, NULL);
+		break;
+	}
+	return 0;
+}
+
 static int wsa_macro_enable_vi_feedback(struct snd_soc_dapm_widget *w,
 					struct snd_kcontrol *kcontrol,
 					int event)
@@ -2617,6 +2644,7 @@
 	ops->dai_ptr = wsa_macro_dai;
 	ops->num_dais = ARRAY_SIZE(wsa_macro_dai);
 	ops->mclk_fn = wsa_macro_mclk_ctrl;
+	ops->event_handler = wsa_macro_event_handler;
 }
 
 static int wsa_macro_probe(struct platform_device *pdev)