asoc: codecs: wsa881x: turn off PA at ssr

Register for a callback for ssr notification
and turn off PA at pre-ssr to protect speaker
from any turning off damage during ssr.

Change-Id: I286d9bdcbcba9561424a8f0d4a1e99a8492caf08
Signed-off-by: Karthikeyan Mani <kmani@codeaurora.org>
diff --git a/asoc/codecs/wsa881x.c b/asoc/codecs/wsa881x.c
index 2871ad3..daafcdc 100644
--- a/asoc/codecs/wsa881x.c
+++ b/asoc/codecs/wsa881x.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
  */
 
 #include <linux/module.h>
@@ -16,6 +16,7 @@
 #include <linux/kernel.h>
 #include <linux/gpio.h>
 #include <linux/of_gpio.h>
+#include <linux/of_platform.h>
 #include <linux/regmap.h>
 #include <linux/debugfs.h>
 #include <soc/soundwire.h>
@@ -101,6 +102,29 @@
 	struct delayed_work ocp_ctl_work;
 	struct device_node *wsa_rst_np;
 	int pa_mute;
+	struct device_node *bolero_np;
+	struct platform_device* bolero_dev;
+	struct notifier_block bolero_nblock;
+	void *handle;
+	int (*register_notifier)(void *handle,
+				 struct notifier_block *nblock,
+				 bool enable);
+};
+
+/* from bolero to WSA events */
+enum {
+	BOLERO_WSA_EVT_TX_CH_HOLD_CLEAR = 1,
+	BOLERO_WSA_EVT_PA_OFF_PRE_SSR,
+	BOLERO_WSA_EVT_SSR_DOWN,
+	BOLERO_WSA_EVT_SSR_UP,
+};
+
+struct wsa_ctrl_platform_data {
+	void *handle;
+	int (*update_wsa_event)(void *handle, u16 event, u32 data);
+	int (*register_notifier)(void *handle,
+				 struct notifier_block *nblock,
+				 bool enable);
 };
 
 #define SWR_SLV_MAX_REG_ADDR	0x390
@@ -1338,12 +1362,39 @@
 	return ret;
 }
 
+static int wsa881x_event_notify(struct notifier_block *nb,
+				unsigned long val, void *ptr)
+{
+	u16 event = (val & 0xffff);
+	struct wsa881x_priv *wsa881x = container_of(nb, struct wsa881x_priv,
+						    bolero_nblock);
+
+	if (!wsa881x)
+		return -EINVAL;
+
+	switch (event) {
+	case BOLERO_WSA_EVT_PA_OFF_PRE_SSR:
+		snd_soc_component_update_bits(wsa881x->component,
+					      WSA881X_SPKR_DRV_GAIN,
+					      0xF0, 0xC0);
+		snd_soc_component_update_bits(wsa881x->component,
+					      WSA881X_SPKR_DRV_EN,
+					      0x80, 0x00);
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
 static int wsa881x_swr_probe(struct swr_device *pdev)
 {
 	int ret = 0;
 	struct wsa881x_priv *wsa881x;
 	u8 devnum = 0;
 	bool pin_state_current = false;
+	struct wsa_ctrl_platform_data *plat_data = NULL;
 
 	wsa881x = devm_kzalloc(&pdev->dev, sizeof(struct wsa881x_priv),
 			    GFP_KERNEL);
@@ -1435,6 +1486,37 @@
 			__func__);
 		goto dev_err;
 	}
+
+	wsa881x->bolero_np = of_parse_phandle(pdev->dev.of_node,
+					      "qcom,bolero-handle", 0);
+	if (wsa881x->bolero_np) {
+		wsa881x->bolero_dev =
+				of_find_device_by_node(wsa881x->bolero_np);
+		if (wsa881x->bolero_dev) {
+			plat_data = dev_get_platdata(&wsa881x->bolero_dev->dev);
+			if (plat_data) {
+				wsa881x->bolero_nblock.notifier_call =
+							wsa881x_event_notify;
+				if (plat_data->register_notifier)
+					plat_data->register_notifier(
+						plat_data->handle,
+						&wsa881x->bolero_nblock,
+						true);
+				wsa881x->register_notifier =
+						plat_data->register_notifier;
+				wsa881x->handle = plat_data->handle;
+			} else {
+				dev_err(&pdev->dev, "%s: plat data not found\n",
+					__func__);
+			}
+		} else {
+			dev_err(&pdev->dev, "%s: bolero dev not found\n",
+				__func__);
+		}
+	} else {
+		dev_info(&pdev->dev, "%s: bolero node not found\n", __func__);
+	}
+
 	mutex_init(&wsa881x->res_lock);
 	mutex_init(&wsa881x->temp_lock);
 
@@ -1457,6 +1539,10 @@
 		dev_err(&pdev->dev, "%s: wsa881x is NULL\n", __func__);
 		return -EINVAL;
 	}
+
+	if (wsa881x->register_notifier)
+		wsa881x->register_notifier(wsa881x->handle,
+					   &wsa881x->bolero_nblock, false);
 	debugfs_remove_recursive(debugfs_wsa881x_dent);
 	debugfs_wsa881x_dent = NULL;
 	mutex_destroy(&wsa881x->res_lock);