ASoC: msm8930: properly protect external mclk control function

Both audio path and MBHC accessory detection logic need master clock
enabled along with bandgap and clock block of CODEC. Clock control
is managed by the machine driver. Calls to clock control function from
audio and MBHC detection paths can be nested. As a result, reference
counter of master clock is incremen/decrement out of order and cause
master clock not enabled when audio path is enabled. Without master clock
CODEC will not consume data and this leads to SLIMBUS overflow error.

CRs-Fixed: 469683
Change-Id: Ib3a55f5d0b36ebe9ba345e7f18162a9314500df2
Signed-off-by: Laxminath Kasam <lkasam@codeaurora.org>
diff --git a/sound/soc/msm/msm8930.c b/sound/soc/msm/msm8930.c
index 798f2c7..da19ca5 100644
--- a/sound/soc/msm/msm8930.c
+++ b/sound/soc/msm/msm8930.c
@@ -77,6 +77,7 @@
 static int clk_users;
 
 static int msm8930_useuro_gpio_requested;
+static struct mutex cdc_mclk_mutex;
 
 static struct snd_soc_jack hs_jack;
 static struct snd_soc_jack button_jack;
@@ -326,35 +327,42 @@
 		struct snd_soc_codec *codec, int enable,
 		bool dapm)
 {
+	int r = 0;
 	pr_debug("%s: enable = %d\n", __func__, enable);
+
+	mutex_lock(&cdc_mclk_mutex);
 	if (enable) {
 		clk_users++;
 		pr_debug("%s: clk_users = %d\n", __func__, clk_users);
-		if (clk_users != 1)
-			return 0;
-
-		if (codec_clk) {
-			clk_set_rate(codec_clk, SITAR_EXT_CLK_RATE);
-			clk_prepare_enable(codec_clk);
-			sitar_mclk_enable(codec, 1, dapm);
-		} else {
-			pr_err("%s: Error setting Sitar MCLK\n", __func__);
-			clk_users--;
-			return -EINVAL;
+		if (clk_users == 1) {
+			if (codec_clk) {
+				clk_set_rate(codec_clk, SITAR_EXT_CLK_RATE);
+				clk_prepare_enable(codec_clk);
+				sitar_mclk_enable(codec, 1, dapm);
+			} else {
+				pr_err("%s: Error setting Sitar MCLK\n",
+					__func__);
+				clk_users--;
+				r = -EINVAL;
+			}
 		}
 	} else {
-		pr_debug("%s: clk_users = %d\n", __func__, clk_users);
-		if (clk_users == 0)
-			return 0;
-		clk_users--;
-		if (!clk_users) {
-			pr_debug("%s: disabling MCLK. clk_users = %d\n",
+		if (clk_users > 0) {
+			clk_users--;
+			pr_debug("%s: clk_users = %d\n", __func__, clk_users);
+			if (clk_users == 0) {
+				pr_debug("%s: disabling MCLK. clk_users = %d\n",
 					 __func__, clk_users);
-			sitar_mclk_enable(codec, 0, dapm);
-			clk_disable_unprepare(codec_clk);
+				sitar_mclk_enable(codec, 0, dapm);
+				clk_disable_unprepare(codec_clk);
+			}
+		} else {
+			pr_err("%s: Error releasing Sitar MCLK\n", __func__);
+			r = -EINVAL;
 		}
 	}
-	return 0;
+	mutex_unlock(&cdc_mclk_mutex);
+	return r;
 }
 
 static bool msm8930_swap_gnd_mic(struct snd_soc_codec *codec)
@@ -1558,6 +1566,7 @@
 		pr_err("%s Fail to configure headset mic gpios\n", __func__);
 
 	atomic_set(&auxpcm_rsc_ref, 0);
+	mutex_init(&cdc_mclk_mutex);
 	return ret;
 
 }
@@ -1572,6 +1581,7 @@
 	msm8930_free_headset_mic_gpios();
 	platform_device_unregister(msm8930_snd_device);
 	kfree(mbhc_cfg.calibration);
+	mutex_destroy(&cdc_mclk_mutex);
 }
 module_exit(msm8930_audio_exit);