aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/brcm2708/patches-4.4/0130-bcm2835-access-controls-under-the-audio-mutex.patch
blob: 56dd7df0157739321798fb055406eaf3fbe6007d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
From 0346d7cfbc9c1f48eada6f27c929eda059298879 Mon Sep 17 00:00:00 2001
From: wm4 <wm4@nowhere>
Date: Wed, 13 Jan 2016 19:43:35 +0100
Subject: [PATCH] bcm2835: access controls under the audio mutex

I don't think the ALSA framework provides any kind of automatic
synchronization within the control callbacks. We most likely need
to ensure this manually, so add locking around all access to shared
mutable data. In particular, bcm2835_audio_set_ctls() should
probably always be called under our own audio lock.
---
 sound/arm/bcm2835-ctl.c | 74 +++++++++++++++++++++++++++++++++++++++++--------
 sound/arm/bcm2835-pcm.c |  4 +++
 2 files changed, 66 insertions(+), 12 deletions(-)

--- a/sound/arm/bcm2835-ctl.c
+++ b/sound/arm/bcm2835-ctl.c
@@ -94,6 +94,9 @@ static int snd_bcm2835_ctl_get(struct sn
 {
 	struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
 
+	if (mutex_lock_interruptible(&chip->audio_mutex))
+		return -EINTR;
+
 	BUG_ON(!chip && !(chip->avail_substreams & AVAIL_SUBSTREAMS_MASK));
 
 	if (kcontrol->private_value == PCM_PLAYBACK_VOLUME)
@@ -103,6 +106,7 @@ static int snd_bcm2835_ctl_get(struct sn
 	else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE)
 		ucontrol->value.integer.value[0] = chip->dest;
 
+	mutex_unlock(&chip->audio_mutex);
 	return 0;
 }
 
@@ -112,11 +116,15 @@ static int snd_bcm2835_ctl_put(struct sn
 	struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
 	int changed = 0;
 
+	if (mutex_lock_interruptible(&chip->audio_mutex))
+		return -EINTR;
+
 	if (kcontrol->private_value == PCM_PLAYBACK_VOLUME) {
 		audio_info("Volume change attempted.. volume = %d new_volume = %d\n", chip->volume, (int)ucontrol->value.integer.value[0]);
 		if (chip->mute == CTRL_VOL_MUTE) {
 			/* changed = toggle_mute(chip, CTRL_VOL_UNMUTE); */
-			return 1; /* should return 0 to signify no change but the mixer takes this as the opposite sign (no idea why) */
+			changed = 1; /* should return 0 to signify no change but the mixer takes this as the opposite sign (no idea why) */
+			goto unlock;
 		}
 		if (changed
 		    || (ucontrol->value.integer.value[0] != chip2alsa(chip->volume))) {
@@ -142,6 +150,8 @@ static int snd_bcm2835_ctl_put(struct sn
 			printk(KERN_ERR "Failed to set ALSA controls..\n");
 	}
 
+unlock:
+	mutex_unlock(&chip->audio_mutex);
 	return changed;
 }
 
@@ -198,10 +208,14 @@ static int snd_bcm2835_spdif_default_get
 	struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
 	int i;
 
+	if (mutex_lock_interruptible(&chip->audio_mutex))
+		return -EINTR;
+
 	for (i = 0; i < 4; i++)
 		ucontrol->value.iec958.status[i] =
 			(chip->spdif_status >> (i * 8)) && 0xff;
 
+	mutex_unlock(&chip->audio_mutex);
 	return 0;
 }
 
@@ -212,12 +226,16 @@ static int snd_bcm2835_spdif_default_put
 	unsigned int val = 0;
 	int i, change;
 
+	if (mutex_lock_interruptible(&chip->audio_mutex))
+		return -EINTR;
+
 	for (i = 0; i < 4; i++)
 		val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8);
 
 	change = val != chip->spdif_status;
 	chip->spdif_status = val;
 
+	mutex_unlock(&chip->audio_mutex);
 	return change;
 }
 
@@ -253,9 +271,14 @@ static int snd_bcm2835_spdif_stream_get(
 	struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
 	int i;
 
+	if (mutex_lock_interruptible(&chip->audio_mutex))
+		return -EINTR;
+
 	for (i = 0; i < 4; i++)
 		ucontrol->value.iec958.status[i] =
 			(chip->spdif_status >> (i * 8)) & 0xff;
+
+	mutex_unlock(&chip->audio_mutex);
 	return 0;
 }
 
@@ -266,11 +289,15 @@ static int snd_bcm2835_spdif_stream_put(
 	unsigned int val = 0;
 	int i, change;
 
+	if (mutex_lock_interruptible(&chip->audio_mutex))
+		return -EINTR;
+
 	for (i = 0; i < 4; i++)
 		val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8);
 	change = val != chip->spdif_status;
 	chip->spdif_status = val;
 
+	mutex_unlock(&chip->audio_mutex);
 	return change;
 }
 
@@ -454,11 +481,17 @@ static int snd_bcm2835_chmap_ctl_get(str
 	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 	struct snd_pcm_substream *substream = snd_pcm_chmap_substream(info, idx);
 	struct cea_channel_speaker_allocation *ch = NULL;
+	int res = 0;
 	int cur = 0;
 	int i;
 
-	if (!substream || !substream->runtime)
-		return -ENODEV;
+	if (mutex_lock_interruptible(&chip->audio_mutex))
+		return -EINTR;
+
+	if (!substream || !substream->runtime) {
+		res = -ENODEV;
+		goto unlock;
+	}
 
 	for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
 		if (channel_allocations[i].ca_index == chip->cea_chmap)
@@ -476,7 +509,10 @@ static int snd_bcm2835_chmap_ctl_get(str
 	}
 	while (cur < 8)
 		ucontrol->value.integer.value[cur++] = SNDRV_CHMAP_NA;
-	return 0;
+
+unlock:
+	mutex_unlock(&chip->audio_mutex);
+	return res;
 }
 
 static int snd_bcm2835_chmap_ctl_put(struct snd_kcontrol *kcontrol,
@@ -487,10 +523,16 @@ static int snd_bcm2835_chmap_ctl_put(str
 	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
 	struct snd_pcm_substream *substream = snd_pcm_chmap_substream(info, idx);
 	int i, prepared = 0, cea_chmap = -1;
+	int res = 0;
 	int remap[8];
 
-	if (!substream || !substream->runtime)
-		return -ENODEV;
+	if (mutex_lock_interruptible(&chip->audio_mutex))
+		return -EINTR;
+
+	if (!substream || !substream->runtime) {
+		res = -ENODEV;
+		goto unlock;
+	}
 
 	switch (substream->runtime->status->state) {
 	case SNDRV_PCM_STATE_OPEN:
@@ -500,7 +542,8 @@ static int snd_bcm2835_chmap_ctl_put(str
 		prepared = 1;
 		break;
 	default:
-		return -EBUSY;
+		res = -EBUSY;
+		goto unlock;
 	}
 
 	for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
@@ -538,19 +581,26 @@ static int snd_bcm2835_chmap_ctl_put(str
 		}
 	}
 
-	if (cea_chmap < 0)
-		return -EINVAL;
+	if (cea_chmap < 0) {
+		res = -EINVAL;
+		goto unlock;
+	}
 
 	/* don't change the layout if another substream is active */
-	if (chip->opened != (1 << substream->number) && chip->cea_chmap != cea_chmap)
-		return -EBUSY; /* unsure whether this is a good error code */
+	if (chip->opened != (1 << substream->number) && chip->cea_chmap != cea_chmap) {
+		res = -EBUSY; /* unsure whether this is a good error code */
+		goto unlock;
+	}
 
 	chip->cea_chmap = cea_chmap;
 	for (i = 0; i < 8; i++)
 		chip->map_channels[i] = remap[i];
 	if (prepared)
 		snd_bcm2835_pcm_prepare_again(substream);
-	return 0;
+
+unlock:
+	mutex_unlock(&chip->audio_mutex);
+	return res;
 }
 
 static int snd_bcm2835_add_chmap_ctl(bcm2835_chip_t * chip)
--- a/sound/arm/bcm2835-pcm.c
+++ b/sound/arm/bcm2835-pcm.c
@@ -379,6 +379,9 @@ static int snd_bcm2835_pcm_prepare(struc
 
 	audio_info(" .. IN\n");
 
+	if (mutex_lock_interruptible(&chip->audio_mutex))
+		return -EINTR;
+
 	snd_bcm2835_pcm_prepare_again(substream);
 
 	bcm2835_audio_setup(alsa_stream);
@@ -401,6 +404,7 @@ static int snd_bcm2835_pcm_prepare(struc
 		      alsa_stream->buffer_size, alsa_stream->period_size,
 		      alsa_stream->pos, runtime->frame_bits);
 
+	mutex_unlock(&chip->audio_mutex);
 	audio_info(" .. OUT\n");
 	return 0;
 }