soc: soundwire: Avoid wakeup after clock pause

Unlock mutex for soundwire bank switch to process the
interrupt before clock pause and request for clock switch
to switch the soundwire clock source.

Change-Id: Idea4306a0e326864431e7cf956044383bf1790db
Signed-off-by: Sudheer Papothi <spapothi@codeaurora.org>
diff --git a/include/soc/swr-wcd.h b/include/soc/swr-wcd.h
index ad129c8..25c5339 100644
--- a/include/soc/swr-wcd.h
+++ b/include/soc/swr-wcd.h
@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Copyright (c) 2015, 2017-2018 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015, 2017-2019 The Linux Foundation. All rights reserved.
  */
 
 #ifndef _LINUX_SWR_WCD_H
@@ -21,6 +21,7 @@
 	SWR_DEVICE_SSR_UP,
 	SWR_REGISTER_WAKE_IRQ,
 	SWR_SET_PORT_MAP,
+	SWR_REQ_CLK_SWITCH,
 };
 
 struct swr_mstr_port {
diff --git a/soc/swr-mstr-ctrl.c b/soc/swr-mstr-ctrl.c
index 30c1f14..acc42a5 100644
--- a/soc/swr-mstr-ctrl.c
+++ b/soc/swr-mstr-ctrl.c
@@ -1527,13 +1527,15 @@
 	}
 	if (swrm_request_hw_vote(swrm, LPASS_AUDIO_CORE, true)) {
 		ret = IRQ_NONE;
-		goto exit;
+		goto err_audio_hw_vote;
 	}
 	swrm_clk_request(swrm, true);
 	mutex_unlock(&swrm->reslock);
 
 	intr_sts = swr_master_read(swrm, SWRM_INTERRUPT_STATUS);
 	intr_sts_masked = intr_sts & swrm->intr_mask;
+
+	dev_dbg(swrm->dev, "%s: status: 0x%x \n", __func__, intr_sts_masked);
 handle_irq:
 	for (i = 0; i < SWRM_INTERRUPT_MAX; i++) {
 		value = intr_sts_masked & (1 << i);
@@ -1690,13 +1692,16 @@
 	intr_sts_masked = intr_sts & swrm->intr_mask;
 
 	if (intr_sts_masked) {
-		dev_dbg(swrm->dev, "%s: new interrupt received\n", __func__);
+		dev_dbg(swrm->dev, "%s: new interrupt received 0x%x\n",
+			__func__, intr_sts_masked);
 		goto handle_irq;
 	}
 
 	mutex_lock(&swrm->reslock);
 	swrm_clk_request(swrm, false);
 	swrm_request_hw_vote(swrm, LPASS_AUDIO_CORE, false);
+
+err_audio_hw_vote:
 	swrm_request_hw_vote(swrm, LPASS_HW_CORE, false);
 exit:
 	mutex_unlock(&swrm->reslock);
@@ -2203,6 +2208,28 @@
 	for (i = 0 ; i < SWR_MSTR_PORT_LEN; i++)
 		INIT_LIST_HEAD(&swrm->mport_cfg[i].port_req_list);
 
+	/* Register LPASS core hw vote */
+	lpass_core_hw_vote = devm_clk_get(&pdev->dev, "lpass_core_hw_vote");
+	if (IS_ERR(lpass_core_hw_vote)) {
+		ret = PTR_ERR(lpass_core_hw_vote);
+		dev_dbg(&pdev->dev, "%s: clk get %s failed %d\n",
+			__func__, "lpass_core_hw_vote", ret);
+		lpass_core_hw_vote = NULL;
+		ret = 0;
+	}
+	swrm->lpass_core_hw_vote = lpass_core_hw_vote;
+
+	/* Register LPASS audio core vote */
+	lpass_core_audio = devm_clk_get(&pdev->dev, "lpass_audio_hw_vote");
+	if (IS_ERR(lpass_core_audio)) {
+		ret = PTR_ERR(lpass_core_audio);
+		dev_dbg(&pdev->dev, "%s: clk get %s failed %d\n",
+			__func__, "lpass_core_audio", ret);
+		lpass_core_audio = NULL;
+		ret = 0;
+	}
+	swrm->lpass_core_audio = lpass_core_audio;
+
 	if (swrm->reg_irq) {
 		ret = swrm->reg_irq(swrm->handle, swr_mstr_interrupt, swrm,
 			    SWR_IRQ_REGISTER);
@@ -2267,28 +2294,6 @@
 	if (pdev->dev.of_node)
 		of_register_swr_devices(&swrm->master);
 
-	/* Register LPASS core hw vote */
-	lpass_core_hw_vote = devm_clk_get(&pdev->dev, "lpass_core_hw_vote");
-	if (IS_ERR(lpass_core_hw_vote)) {
-		ret = PTR_ERR(lpass_core_hw_vote);
-		dev_dbg(&pdev->dev, "%s: clk get %s failed %d\n",
-			__func__, "lpass_core_hw_vote", ret);
-		lpass_core_hw_vote = NULL;
-		ret = 0;
-	}
-	swrm->lpass_core_hw_vote = lpass_core_hw_vote;
-
-	/* Register LPASS audio core vote */
-	lpass_core_audio = devm_clk_get(&pdev->dev, "lpass_audio_hw_vote");
-	if (IS_ERR(lpass_core_audio)) {
-		ret = PTR_ERR(lpass_core_audio);
-		dev_dbg(&pdev->dev, "%s: clk get %s failed %d\n",
-			__func__, "lpass_core_audio", ret);
-		lpass_core_audio = NULL;
-		ret = 0;
-	}
-	swrm->lpass_core_audio = lpass_core_audio;
-
 	dbgswrm = swrm;
 	debugfs_swrm_dent = debugfs_create_dir(dev_name(&pdev->dev), 0);
 	if (!IS_ERR(debugfs_swrm_dent)) {
@@ -2507,7 +2512,9 @@
 			goto exit;
 		}
 		if (!swrm->clk_stop_mode0_supp || swrm->state == SWR_MSTR_SSR) {
+			mutex_unlock(&swrm->reslock);
 			enable_bank_switch(swrm, 0, SWR_ROW_50, SWR_MIN_COL);
+			mutex_lock(&swrm->reslock);
 			swrm_clk_pause(swrm);
 			swr_master_write(swrm, SWRM_COMP_CFG_ADDR, 0x00);
 			list_for_each_entry(swr_dev, &mstr->devices, dev_list) {
@@ -2558,6 +2565,25 @@
 }
 #endif /* CONFIG_PM */
 
+static int swrm_device_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct swr_mstr_ctrl *swrm = platform_get_drvdata(pdev);
+	int ret = 0;
+
+	dev_dbg(dev, "%s: swrm state: %d\n", __func__, swrm->state);
+	if (!pm_runtime_enabled(dev) || !pm_runtime_suspended(dev)) {
+		ret = swrm_runtime_suspend(dev);
+		if (!ret) {
+			pm_runtime_disable(dev);
+			pm_runtime_set_suspended(dev);
+			pm_runtime_enable(dev);
+		}
+	}
+
+	return 0;
+}
+
 static int swrm_device_down(struct device *dev)
 {
 	struct platform_device *pdev = to_platform_device(dev);
@@ -2694,6 +2720,17 @@
 	mstr = &swrm->master;
 
 	switch (id) {
+	case SWR_REQ_CLK_SWITCH:
+		/* This will put soundwire in clock stop mode and disable the
+		 * clocks, if there is no active usecase running, so that the
+		 * next activity on soundwire will request clock from new clock
+		 * source.
+		 */
+		mutex_lock(&swrm->mlock);
+		if (swrm->state == SWR_MSTR_UP)
+			swrm_device_suspend(&pdev->dev);
+		mutex_unlock(&swrm->mlock);
+		break;
 	case SWR_CLK_FREQ:
 		if (!data) {
 			dev_err(swrm->dev, "%s: data is NULL\n", __func__);