ASoc: msm: Fix the kernel panic issue in dapm

- Widget list in dapm is getting corrupted during concurrent
use cases where dapm_power_widget is accessed from core,
machine driver, codec driver and soc-dsp. This corruption
is resulting in kernel crash in dapm.
- Fix the issue by adding protection in dapm_power_widgets
API.

CRs-fixed: 351250
Change-Id: Ia977770b2c4081353918a40919aafd468bf3c4ed
Signed-off-by: Jayasena Sangaraboina <jsanga@codeaurora.org>
diff --git a/include/sound/soc.h b/include/sound/soc.h
index f51b006..12a8fd5 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -814,6 +814,7 @@
 	struct list_head list;
 	struct mutex mutex;
 	struct mutex dapm_mutex;
+	struct mutex dapm_power_mutex;
 	struct mutex dsp_mutex;
 	spinlock_t dsp_spinlock;
 
diff --git a/sound/soc/msm/msm8960.c b/sound/soc/msm/msm8960.c
index abfadb8..44fab3f 100644
--- a/sound/soc/msm/msm8960.c
+++ b/sound/soc/msm/msm8960.c
@@ -254,6 +254,8 @@
 {
 	struct snd_soc_dapm_context *dapm = &codec->dapm;
 
+	mutex_lock(&dapm->codec->mutex);
+
 	pr_debug("%s: msm8960_spk_control = %d", __func__, msm8960_spk_control);
 	if (msm8960_spk_control == MSM8960_SPK_ON) {
 		snd_soc_dapm_enable_pin(dapm, "Ext Spk Bottom Pos");
@@ -268,6 +270,7 @@
 	}
 
 	snd_soc_dapm_sync(dapm);
+	mutex_unlock(&dapm->codec->mutex);
 }
 
 static int msm8960_get_spk(struct snd_kcontrol *kcontrol,
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index f47b0d3..4192609 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -3811,6 +3811,7 @@
 	card->instantiated = 0;
 	mutex_init(&card->mutex);
 	mutex_init(&card->dapm_mutex);
+	mutex_init(&card->dapm_power_mutex);
 	mutex_init(&card->dsp_mutex);
 	spin_lock_init(&card->dsp_spinlock);
 
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 265df9e..2284f19 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -1306,15 +1306,24 @@
 	struct snd_soc_dapm_context *cur_dapm = NULL;
 	int ret, i;
 	int *sort;
+	int nWidgets;
 
 	if (power_up)
 		sort = dapm_up_seq;
 	else
 		sort = dapm_down_seq;
 
+	nWidgets = ARRAY_SIZE(dapm_up_seq);
+
 	list_for_each_entry_safe(w, n, list, power_list) {
 		ret = 0;
 
+		if (!w->name)
+			continue;
+
+		if (!((w->id >= 0) && (w->id < nWidgets)))
+			continue;
+
 		/* Do we need to apply any queued changes? */
 		if (sort[w->id] != cur_sort || w->reg != cur_reg ||
 		    w->dapm != cur_dapm || w->subseq != cur_subseq) {
@@ -1500,6 +1509,8 @@
 
 	trace_snd_soc_dapm_start(card);
 
+	mutex_lock(&card->dapm_power_mutex);
+
 	list_for_each_entry(d, &card->dapm_list, list)
 		if (d->n_widgets || d->codec == NULL)
 			d->dev_power = 0;
@@ -1609,6 +1620,8 @@
 		"DAPM sequencing finished, waiting %dms\n", card->pop_time);
 	pop_wait(card->pop_time);
 
+	mutex_unlock(&card->dapm_power_mutex);
+
 	trace_snd_soc_dapm_done(card);
 
 	return 0;