OpenWrt – Blame information for rev 4
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
4 | office | 1 | From 41a90e24c272c84fb8c6d23ac451f0c725dcded6 Mon Sep 17 00:00:00 2001 |
2 | From: popcornmix <popcornmix@gmail.com> |
||
3 | Date: Mon, 26 Mar 2012 22:15:50 +0100 |
||
4 | Subject: [PATCH] bcm2708: alsa sound driver |
||
5 | MIME-Version: 1.0 |
||
6 | Content-Type: text/plain; charset=UTF-8 |
||
7 | Content-Transfer-Encoding: 8bit |
||
8 | |||
9 | Signed-off-by: popcornmix <popcornmix@gmail.com> |
||
10 | |||
11 | alsa: add mmap support and some cleanups to bcm2835 ALSA driver |
||
12 | |||
13 | snd-bcm2835: Add support for spdif/hdmi passthrough |
||
14 | |||
15 | This adds a dedicated subdevice which can be used for passthrough of non-audio |
||
16 | formats (ie encoded a52) through the hdmi audio link. In addition to this |
||
17 | driver extension an appropriate card config is required to make alsa-lib |
||
18 | support the AES parameters for this device. |
||
19 | |||
20 | snd-bcm2708: Add mutex, improve logging |
||
21 | |||
22 | Fix for ALSA driver crash |
||
23 | |||
24 | Avoids an issue when closing and opening vchiq where a message can arrive before service handle has been written |
||
25 | |||
26 | alsa: reduce severity of expected warning message |
||
27 | |||
28 | snd-bcm2708: Fix dmesg spam for non-error case |
||
29 | |||
30 | alsa: Ensure mutexes are released through error paths |
||
31 | |||
32 | alsa: Make interrupted close paths quieter |
||
33 | |||
34 | BCM270x: Add onboard sound device to Device Tree |
||
35 | |||
36 | Add Device Tree support to alsa driver. |
||
37 | Add device to Device Tree. |
||
38 | Don't add platform devices when booting in DT mode. |
||
39 | |||
40 | Signed-off-by: Noralf Trønnes <noralf@tronnes.org> |
||
41 | |||
42 | bcm2835: access controls under the audio mutex |
||
43 | |||
44 | I don't think the ALSA framework provides any kind of automatic |
||
45 | synchronization within the control callbacks. We most likely need |
||
46 | to ensure this manually, so add locking around all access to shared |
||
47 | mutable data. In particular, bcm2835_audio_set_ctls() should |
||
48 | probably always be called under our own audio lock. |
||
49 | |||
50 | snd-bcm2835: Don't allow responses from VC to be interrupted by user signals |
||
51 | |||
52 | There should always be a response, and retry after a signal interruption is not handled, so don't report |
||
53 | we are interruptible. |
||
54 | |||
55 | See: https://github.com/raspberrypi/linux/issues/1560 |
||
56 | |||
57 | snd-bcm2835: Use bcm2835_hw params in preallocate |
||
58 | --- |
||
59 | sound/arm/Kconfig | 7 + |
||
60 | sound/arm/Makefile | 5 + |
||
61 | sound/arm/bcm2835-ctl.c | 350 +++++++++++++++ |
||
62 | sound/arm/bcm2835-pcm.c | 563 +++++++++++++++++++++++ |
||
63 | sound/arm/bcm2835-vchiq.c | 889 +++++++++++++++++++++++++++++++++++++ |
||
64 | sound/arm/bcm2835.c | 511 +++++++++++++++++++++ |
||
65 | sound/arm/bcm2835.h | 167 +++++++ |
||
66 | sound/arm/vc_vchi_audioserv_defs.h | 116 +++++ |
||
67 | 8 files changed, 2608 insertions(+) |
||
68 | create mode 100755 sound/arm/bcm2835-ctl.c |
||
69 | create mode 100755 sound/arm/bcm2835-pcm.c |
||
70 | create mode 100755 sound/arm/bcm2835-vchiq.c |
||
71 | create mode 100644 sound/arm/bcm2835.c |
||
72 | create mode 100755 sound/arm/bcm2835.h |
||
73 | create mode 100644 sound/arm/vc_vchi_audioserv_defs.h |
||
74 | |||
75 | --- a/sound/arm/Kconfig |
||
76 | +++ b/sound/arm/Kconfig |
||
77 | @@ -32,6 +32,13 @@ config SND_PXA2XX_AC97 |
||
78 | Say Y or M if you want to support any AC97 codec attached to |
||
79 | the PXA2xx AC97 interface. |
||
80 | |||
81 | +config SND_BCM2835 |
||
82 | + tristate "BCM2835 ALSA driver" |
||
83 | + depends on ARCH_BCM2835 && BCM2708_VCHIQ && SND |
||
84 | + select SND_PCM |
||
85 | + help |
||
86 | + Say Y or M if you want to support BCM2835 Alsa pcm card driver |
||
87 | + |
||
88 | endif # SND_ARM |
||
89 | |||
90 | config SND_PXA2XX_LIB |
||
91 | --- a/sound/arm/Makefile |
||
92 | +++ b/sound/arm/Makefile |
||
93 | @@ -14,3 +14,8 @@ snd-pxa2xx-lib-$(CONFIG_SND_PXA2XX_LIB_A |
||
94 | |||
95 | obj-$(CONFIG_SND_PXA2XX_AC97) += snd-pxa2xx-ac97.o |
||
96 | snd-pxa2xx-ac97-objs := pxa2xx-ac97.o |
||
97 | + |
||
98 | +obj-$(CONFIG_SND_BCM2835) += snd-bcm2835.o |
||
99 | +snd-bcm2835-objs := bcm2835.o bcm2835-ctl.o bcm2835-pcm.o bcm2835-vchiq.o |
||
100 | + |
||
101 | +ccflags-y += -Idrivers/staging/vc04_services -Idrivers/staging/vc04_services/interface/vcos/linuxkernel -D__VCCOREVER__=0x04000000 |
||
102 | --- /dev/null |
||
103 | +++ b/sound/arm/bcm2835-ctl.c |
||
104 | @@ -0,0 +1,350 @@ |
||
105 | +/***************************************************************************** |
||
106 | +* Copyright 2011 Broadcom Corporation. All rights reserved. |
||
107 | +* |
||
108 | +* Unless you and Broadcom execute a separate written software license |
||
109 | +* agreement governing use of this software, this software is licensed to you |
||
110 | +* under the terms of the GNU General Public License version 2, available at |
||
111 | +* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). |
||
112 | +* |
||
113 | +* Notwithstanding the above, under no circumstances may you combine this |
||
114 | +* software in any way with any other Broadcom software provided under a |
||
115 | +* license other than the GPL, without Broadcom's express prior written |
||
116 | +* consent. |
||
117 | +*****************************************************************************/ |
||
118 | + |
||
119 | +#include <linux/platform_device.h> |
||
120 | +#include <linux/init.h> |
||
121 | +#include <linux/io.h> |
||
122 | +#include <linux/jiffies.h> |
||
123 | +#include <linux/slab.h> |
||
124 | +#include <linux/time.h> |
||
125 | +#include <linux/wait.h> |
||
126 | +#include <linux/delay.h> |
||
127 | +#include <linux/moduleparam.h> |
||
128 | +#include <linux/sched.h> |
||
129 | + |
||
130 | +#include <sound/core.h> |
||
131 | +#include <sound/control.h> |
||
132 | +#include <sound/pcm.h> |
||
133 | +#include <sound/pcm_params.h> |
||
134 | +#include <sound/rawmidi.h> |
||
135 | +#include <sound/initval.h> |
||
136 | +#include <sound/tlv.h> |
||
137 | +#include <sound/asoundef.h> |
||
138 | + |
||
139 | +#include "bcm2835.h" |
||
140 | + |
||
141 | +/* volume maximum and minimum in terms of 0.01dB */ |
||
142 | +#define CTRL_VOL_MAX 400 |
||
143 | +#define CTRL_VOL_MIN -10239 /* originally -10240 */ |
||
144 | + |
||
145 | + |
||
146 | +static int snd_bcm2835_ctl_info(struct snd_kcontrol *kcontrol, |
||
147 | + struct snd_ctl_elem_info *uinfo) |
||
148 | +{ |
||
149 | + audio_info(" ... IN\n"); |
||
150 | + if (kcontrol->private_value == PCM_PLAYBACK_VOLUME) { |
||
151 | + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; |
||
152 | + uinfo->count = 1; |
||
153 | + uinfo->value.integer.min = CTRL_VOL_MIN; |
||
154 | + uinfo->value.integer.max = CTRL_VOL_MAX; /* 2303 */ |
||
155 | + } else if (kcontrol->private_value == PCM_PLAYBACK_MUTE) { |
||
156 | + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; |
||
157 | + uinfo->count = 1; |
||
158 | + uinfo->value.integer.min = 0; |
||
159 | + uinfo->value.integer.max = 1; |
||
160 | + } else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE) { |
||
161 | + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; |
||
162 | + uinfo->count = 1; |
||
163 | + uinfo->value.integer.min = 0; |
||
164 | + uinfo->value.integer.max = AUDIO_DEST_MAX-1; |
||
165 | + } |
||
166 | + audio_info(" ... OUT\n"); |
||
167 | + return 0; |
||
168 | +} |
||
169 | + |
||
170 | +/* toggles mute on or off depending on the value of nmute, and returns |
||
171 | + * 1 if the mute value was changed, otherwise 0 |
||
172 | + */ |
||
173 | +static int toggle_mute(struct bcm2835_chip *chip, int nmute) |
||
174 | +{ |
||
175 | + /* if settings are ok, just return 0 */ |
||
176 | + if(chip->mute == nmute) |
||
177 | + return 0; |
||
178 | + |
||
179 | + /* if the sound is muted then we need to unmute */ |
||
180 | + if(chip->mute == CTRL_VOL_MUTE) |
||
181 | + { |
||
182 | + chip->volume = chip->old_volume; /* copy the old volume back */ |
||
183 | + audio_info("Unmuting, old_volume = %d, volume = %d ...\n", chip->old_volume, chip->volume); |
||
184 | + } |
||
185 | + else /* otherwise we mute */ |
||
186 | + { |
||
187 | + chip->old_volume = chip->volume; |
||
188 | + chip->volume = 26214; /* set volume to minimum level AKA mute */ |
||
189 | + audio_info("Muting, old_volume = %d, volume = %d ...\n", chip->old_volume, chip->volume); |
||
190 | + } |
||
191 | + |
||
192 | + chip->mute = nmute; |
||
193 | + return 1; |
||
194 | +} |
||
195 | + |
||
196 | +static int snd_bcm2835_ctl_get(struct snd_kcontrol *kcontrol, |
||
197 | + struct snd_ctl_elem_value *ucontrol) |
||
198 | +{ |
||
199 | + struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); |
||
200 | + |
||
201 | + if (mutex_lock_interruptible(&chip->audio_mutex)) |
||
202 | + return -EINTR; |
||
203 | + |
||
204 | + BUG_ON(!chip && !(chip->avail_substreams & AVAIL_SUBSTREAMS_MASK)); |
||
205 | + |
||
206 | + if (kcontrol->private_value == PCM_PLAYBACK_VOLUME) |
||
207 | + ucontrol->value.integer.value[0] = chip2alsa(chip->volume); |
||
208 | + else if (kcontrol->private_value == PCM_PLAYBACK_MUTE) |
||
209 | + ucontrol->value.integer.value[0] = chip->mute; |
||
210 | + else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE) |
||
211 | + ucontrol->value.integer.value[0] = chip->dest; |
||
212 | + |
||
213 | + mutex_unlock(&chip->audio_mutex); |
||
214 | + return 0; |
||
215 | +} |
||
216 | + |
||
217 | +static int snd_bcm2835_ctl_put(struct snd_kcontrol *kcontrol, |
||
218 | + struct snd_ctl_elem_value *ucontrol) |
||
219 | +{ |
||
220 | + struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); |
||
221 | + int changed = 0; |
||
222 | + |
||
223 | + if (mutex_lock_interruptible(&chip->audio_mutex)) |
||
224 | + return -EINTR; |
||
225 | + |
||
226 | + if (kcontrol->private_value == PCM_PLAYBACK_VOLUME) { |
||
227 | + audio_info("Volume change attempted.. volume = %d new_volume = %d\n", chip->volume, (int)ucontrol->value.integer.value[0]); |
||
228 | + if (chip->mute == CTRL_VOL_MUTE) { |
||
229 | + /* changed = toggle_mute(chip, CTRL_VOL_UNMUTE); */ |
||
230 | + changed = 1; /* should return 0 to signify no change but the mixer takes this as the opposite sign (no idea why) */ |
||
231 | + goto unlock; |
||
232 | + } |
||
233 | + if (changed |
||
234 | + || (ucontrol->value.integer.value[0] != chip2alsa(chip->volume))) { |
||
235 | + |
||
236 | + chip->volume = alsa2chip(ucontrol->value.integer.value[0]); |
||
237 | + changed = 1; |
||
238 | + } |
||
239 | + |
||
240 | + } else if (kcontrol->private_value == PCM_PLAYBACK_MUTE) { |
||
241 | + /* Now implemented */ |
||
242 | + audio_info(" Mute attempted\n"); |
||
243 | + changed = toggle_mute(chip, ucontrol->value.integer.value[0]); |
||
244 | + |
||
245 | + } else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE) { |
||
246 | + if (ucontrol->value.integer.value[0] != chip->dest) { |
||
247 | + chip->dest = ucontrol->value.integer.value[0]; |
||
248 | + changed = 1; |
||
249 | + } |
||
250 | + } |
||
251 | + |
||
252 | + if (changed) { |
||
253 | + if (bcm2835_audio_set_ctls(chip)) |
||
254 | + printk(KERN_ERR "Failed to set ALSA controls..\n"); |
||
255 | + } |
||
256 | + |
||
257 | +unlock: |
||
258 | + mutex_unlock(&chip->audio_mutex); |
||
259 | + return changed; |
||
260 | +} |
||
261 | + |
||
262 | +static DECLARE_TLV_DB_SCALE(snd_bcm2835_db_scale, CTRL_VOL_MIN, 1, 1); |
||
263 | + |
||
264 | +static struct snd_kcontrol_new snd_bcm2835_ctl[] = { |
||
265 | + { |
||
266 | + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
||
267 | + .name = "PCM Playback Volume", |
||
268 | + .index = 0, |
||
269 | + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, |
||
270 | + .private_value = PCM_PLAYBACK_VOLUME, |
||
271 | + .info = snd_bcm2835_ctl_info, |
||
272 | + .get = snd_bcm2835_ctl_get, |
||
273 | + .put = snd_bcm2835_ctl_put, |
||
274 | + .count = 1, |
||
275 | + .tlv = {.p = snd_bcm2835_db_scale} |
||
276 | + }, |
||
277 | + { |
||
278 | + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
||
279 | + .name = "PCM Playback Switch", |
||
280 | + .index = 0, |
||
281 | + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, |
||
282 | + .private_value = PCM_PLAYBACK_MUTE, |
||
283 | + .info = snd_bcm2835_ctl_info, |
||
284 | + .get = snd_bcm2835_ctl_get, |
||
285 | + .put = snd_bcm2835_ctl_put, |
||
286 | + .count = 1, |
||
287 | + }, |
||
288 | + { |
||
289 | + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
||
290 | + .name = "PCM Playback Route", |
||
291 | + .index = 0, |
||
292 | + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, |
||
293 | + .private_value = PCM_PLAYBACK_DEVICE, |
||
294 | + .info = snd_bcm2835_ctl_info, |
||
295 | + .get = snd_bcm2835_ctl_get, |
||
296 | + .put = snd_bcm2835_ctl_put, |
||
297 | + .count = 1, |
||
298 | + }, |
||
299 | +}; |
||
300 | + |
||
301 | +static int snd_bcm2835_spdif_default_info(struct snd_kcontrol *kcontrol, |
||
302 | + struct snd_ctl_elem_info *uinfo) |
||
303 | +{ |
||
304 | + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; |
||
305 | + uinfo->count = 1; |
||
306 | + return 0; |
||
307 | +} |
||
308 | + |
||
309 | +static int snd_bcm2835_spdif_default_get(struct snd_kcontrol *kcontrol, |
||
310 | + struct snd_ctl_elem_value *ucontrol) |
||
311 | +{ |
||
312 | + struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); |
||
313 | + int i; |
||
314 | + |
||
315 | + if (mutex_lock_interruptible(&chip->audio_mutex)) |
||
316 | + return -EINTR; |
||
317 | + |
||
318 | + for (i = 0; i < 4; i++) |
||
319 | + ucontrol->value.iec958.status[i] = |
||
320 | + (chip->spdif_status >> (i * 8)) && 0xff; |
||
321 | + |
||
322 | + mutex_unlock(&chip->audio_mutex); |
||
323 | + return 0; |
||
324 | +} |
||
325 | + |
||
326 | +static int snd_bcm2835_spdif_default_put(struct snd_kcontrol *kcontrol, |
||
327 | + struct snd_ctl_elem_value *ucontrol) |
||
328 | +{ |
||
329 | + struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); |
||
330 | + unsigned int val = 0; |
||
331 | + int i, change; |
||
332 | + |
||
333 | + if (mutex_lock_interruptible(&chip->audio_mutex)) |
||
334 | + return -EINTR; |
||
335 | + |
||
336 | + for (i = 0; i < 4; i++) |
||
337 | + val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8); |
||
338 | + |
||
339 | + change = val != chip->spdif_status; |
||
340 | + chip->spdif_status = val; |
||
341 | + |
||
342 | + mutex_unlock(&chip->audio_mutex); |
||
343 | + return change; |
||
344 | +} |
||
345 | + |
||
346 | +static int snd_bcm2835_spdif_mask_info(struct snd_kcontrol *kcontrol, |
||
347 | + struct snd_ctl_elem_info *uinfo) |
||
348 | +{ |
||
349 | + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; |
||
350 | + uinfo->count = 1; |
||
351 | + return 0; |
||
352 | +} |
||
353 | + |
||
354 | +static int snd_bcm2835_spdif_mask_get(struct snd_kcontrol *kcontrol, |
||
355 | + struct snd_ctl_elem_value *ucontrol) |
||
356 | +{ |
||
357 | + /* bcm2835 supports only consumer mode and sets all other format flags |
||
358 | + * automatically. So the only thing left is signalling non-audio |
||
359 | + * content */ |
||
360 | + ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO; |
||
361 | + return 0; |
||
362 | +} |
||
363 | + |
||
364 | +static int snd_bcm2835_spdif_stream_info(struct snd_kcontrol *kcontrol, |
||
365 | + struct snd_ctl_elem_info *uinfo) |
||
366 | +{ |
||
367 | + uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; |
||
368 | + uinfo->count = 1; |
||
369 | + return 0; |
||
370 | +} |
||
371 | + |
||
372 | +static int snd_bcm2835_spdif_stream_get(struct snd_kcontrol *kcontrol, |
||
373 | + struct snd_ctl_elem_value *ucontrol) |
||
374 | +{ |
||
375 | + struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); |
||
376 | + int i; |
||
377 | + |
||
378 | + if (mutex_lock_interruptible(&chip->audio_mutex)) |
||
379 | + return -EINTR; |
||
380 | + |
||
381 | + for (i = 0; i < 4; i++) |
||
382 | + ucontrol->value.iec958.status[i] = |
||
383 | + (chip->spdif_status >> (i * 8)) & 0xff; |
||
384 | + |
||
385 | + mutex_unlock(&chip->audio_mutex); |
||
386 | + return 0; |
||
387 | +} |
||
388 | + |
||
389 | +static int snd_bcm2835_spdif_stream_put(struct snd_kcontrol *kcontrol, |
||
390 | + struct snd_ctl_elem_value *ucontrol) |
||
391 | +{ |
||
392 | + struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); |
||
393 | + unsigned int val = 0; |
||
394 | + int i, change; |
||
395 | + |
||
396 | + if (mutex_lock_interruptible(&chip->audio_mutex)) |
||
397 | + return -EINTR; |
||
398 | + |
||
399 | + for (i = 0; i < 4; i++) |
||
400 | + val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8); |
||
401 | + change = val != chip->spdif_status; |
||
402 | + chip->spdif_status = val; |
||
403 | + |
||
404 | + mutex_unlock(&chip->audio_mutex); |
||
405 | + return change; |
||
406 | +} |
||
407 | + |
||
408 | +static struct snd_kcontrol_new snd_bcm2835_spdif[] = { |
||
409 | + { |
||
410 | + .iface = SNDRV_CTL_ELEM_IFACE_PCM, |
||
411 | + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), |
||
412 | + .info = snd_bcm2835_spdif_default_info, |
||
413 | + .get = snd_bcm2835_spdif_default_get, |
||
414 | + .put = snd_bcm2835_spdif_default_put |
||
415 | + }, |
||
416 | + { |
||
417 | + .access = SNDRV_CTL_ELEM_ACCESS_READ, |
||
418 | + .iface = SNDRV_CTL_ELEM_IFACE_PCM, |
||
419 | + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK), |
||
420 | + .info = snd_bcm2835_spdif_mask_info, |
||
421 | + .get = snd_bcm2835_spdif_mask_get, |
||
422 | + }, |
||
423 | + { |
||
424 | + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | |
||
425 | + SNDRV_CTL_ELEM_ACCESS_INACTIVE, |
||
426 | + .iface = SNDRV_CTL_ELEM_IFACE_PCM, |
||
427 | + .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM), |
||
428 | + .info = snd_bcm2835_spdif_stream_info, |
||
429 | + .get = snd_bcm2835_spdif_stream_get, |
||
430 | + .put = snd_bcm2835_spdif_stream_put, |
||
431 | + }, |
||
432 | +}; |
||
433 | + |
||
434 | +int snd_bcm2835_new_ctl(bcm2835_chip_t * chip) |
||
435 | +{ |
||
436 | + int err; |
||
437 | + unsigned int idx; |
||
438 | + |
||
439 | + strcpy(chip->card->mixername, "Broadcom Mixer"); |
||
440 | + for (idx = 0; idx < ARRAY_SIZE(snd_bcm2835_ctl); idx++) { |
||
441 | + err = |
||
442 | + snd_ctl_add(chip->card, |
||
443 | + snd_ctl_new1(&snd_bcm2835_ctl[idx], chip)); |
||
444 | + if (err < 0) |
||
445 | + return err; |
||
446 | + } |
||
447 | + for (idx = 0; idx < ARRAY_SIZE(snd_bcm2835_spdif); idx++) { |
||
448 | + err = snd_ctl_add(chip->card, |
||
449 | + snd_ctl_new1(&snd_bcm2835_spdif[idx], chip)); |
||
450 | + if (err < 0) |
||
451 | + return err; |
||
452 | + } |
||
453 | + return 0; |
||
454 | +} |
||
455 | --- /dev/null |
||
456 | +++ b/sound/arm/bcm2835-pcm.c |
||
457 | @@ -0,0 +1,563 @@ |
||
458 | +/***************************************************************************** |
||
459 | +* Copyright 2011 Broadcom Corporation. All rights reserved. |
||
460 | +* |
||
461 | +* Unless you and Broadcom execute a separate written software license |
||
462 | +* agreement governing use of this software, this software is licensed to you |
||
463 | +* under the terms of the GNU General Public License version 2, available at |
||
464 | +* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). |
||
465 | +* |
||
466 | +* Notwithstanding the above, under no circumstances may you combine this |
||
467 | +* software in any way with any other Broadcom software provided under a |
||
468 | +* license other than the GPL, without Broadcom's express prior written |
||
469 | +* consent. |
||
470 | +*****************************************************************************/ |
||
471 | + |
||
472 | +#include <linux/interrupt.h> |
||
473 | +#include <linux/slab.h> |
||
474 | + |
||
475 | +#include <sound/asoundef.h> |
||
476 | + |
||
477 | +#include "bcm2835.h" |
||
478 | + |
||
479 | +/* hardware definition */ |
||
480 | +static struct snd_pcm_hardware snd_bcm2835_playback_hw = { |
||
481 | + .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | |
||
482 | + SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID), |
||
483 | + .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, |
||
484 | + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, |
||
485 | + .rate_min = 8000, |
||
486 | + .rate_max = 48000, |
||
487 | + .channels_min = 1, |
||
488 | + .channels_max = 2, |
||
489 | + .buffer_bytes_max = 128 * 1024, |
||
490 | + .period_bytes_min = 1 * 1024, |
||
491 | + .period_bytes_max = 128 * 1024, |
||
492 | + .periods_min = 1, |
||
493 | + .periods_max = 128, |
||
494 | +}; |
||
495 | + |
||
496 | +static struct snd_pcm_hardware snd_bcm2835_playback_spdif_hw = { |
||
497 | + .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | |
||
498 | + SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID), |
||
499 | + .formats = SNDRV_PCM_FMTBIT_S16_LE, |
||
500 | + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_44100 | |
||
501 | + SNDRV_PCM_RATE_48000, |
||
502 | + .rate_min = 44100, |
||
503 | + .rate_max = 48000, |
||
504 | + .channels_min = 2, |
||
505 | + .channels_max = 2, |
||
506 | + .buffer_bytes_max = 128 * 1024, |
||
507 | + .period_bytes_min = 1 * 1024, |
||
508 | + .period_bytes_max = 128 * 1024, |
||
509 | + .periods_min = 1, |
||
510 | + .periods_max = 128, |
||
511 | +}; |
||
512 | + |
||
513 | +static void snd_bcm2835_playback_free(struct snd_pcm_runtime *runtime) |
||
514 | +{ |
||
515 | + audio_info("Freeing up alsa stream here ..\n"); |
||
516 | + if (runtime->private_data) |
||
517 | + kfree(runtime->private_data); |
||
518 | + runtime->private_data = NULL; |
||
519 | +} |
||
520 | + |
||
521 | +static irqreturn_t bcm2835_playback_fifo_irq(int irq, void *dev_id) |
||
522 | +{ |
||
523 | + bcm2835_alsa_stream_t *alsa_stream = (bcm2835_alsa_stream_t *) dev_id; |
||
524 | + uint32_t consumed = 0; |
||
525 | + int new_period = 0; |
||
526 | + |
||
527 | + audio_info(" .. IN\n"); |
||
528 | + |
||
529 | + audio_info("alsa_stream=%p substream=%p\n", alsa_stream, |
||
530 | + alsa_stream ? alsa_stream->substream : 0); |
||
531 | + |
||
532 | + if (alsa_stream->open) |
||
533 | + consumed = bcm2835_audio_retrieve_buffers(alsa_stream); |
||
534 | + |
||
535 | + /* We get called only if playback was triggered, So, the number of buffers we retrieve in |
||
536 | + * each iteration are the buffers that have been played out already |
||
537 | + */ |
||
538 | + |
||
539 | + if (alsa_stream->period_size) { |
||
540 | + if ((alsa_stream->pos / alsa_stream->period_size) != |
||
541 | + ((alsa_stream->pos + consumed) / alsa_stream->period_size)) |
||
542 | + new_period = 1; |
||
543 | + } |
||
544 | + audio_debug("updating pos cur: %d + %d max:%d period_bytes:%d, hw_ptr: %d new_period:%d\n", |
||
545 | + alsa_stream->pos, |
||
546 | + consumed, |
||
547 | + alsa_stream->buffer_size, |
||
548 | + (int)(alsa_stream->period_size*alsa_stream->substream->runtime->periods), |
||
549 | + frames_to_bytes(alsa_stream->substream->runtime, alsa_stream->substream->runtime->status->hw_ptr), |
||
550 | + new_period); |
||
551 | + if (alsa_stream->buffer_size) { |
||
552 | + alsa_stream->pos += consumed &~ (1<<30); |
||
553 | + alsa_stream->pos %= alsa_stream->buffer_size; |
||
554 | + } |
||
555 | + |
||
556 | + if (alsa_stream->substream) { |
||
557 | + if (new_period) |
||
558 | + snd_pcm_period_elapsed(alsa_stream->substream); |
||
559 | + } else { |
||
560 | + audio_warning(" unexpected NULL substream\n"); |
||
561 | + } |
||
562 | + audio_info(" .. OUT\n"); |
||
563 | + |
||
564 | + return IRQ_HANDLED; |
||
565 | +} |
||
566 | + |
||
567 | +/* open callback */ |
||
568 | +static int snd_bcm2835_playback_open_generic( |
||
569 | + struct snd_pcm_substream *substream, int spdif) |
||
570 | +{ |
||
571 | + bcm2835_chip_t *chip = snd_pcm_substream_chip(substream); |
||
572 | + struct snd_pcm_runtime *runtime = substream->runtime; |
||
573 | + bcm2835_alsa_stream_t *alsa_stream; |
||
574 | + int idx; |
||
575 | + int err; |
||
576 | + |
||
577 | + audio_info(" .. IN (%d)\n", substream->number); |
||
578 | + |
||
579 | + if(mutex_lock_interruptible(&chip->audio_mutex)) |
||
580 | + { |
||
581 | + audio_error("Interrupted whilst waiting for lock\n"); |
||
582 | + return -EINTR; |
||
583 | + } |
||
584 | + audio_info("Alsa open (%d)\n", substream->number); |
||
585 | + idx = substream->number; |
||
586 | + |
||
587 | + if (spdif && chip->opened != 0) { |
||
588 | + err = -EBUSY; |
||
589 | + goto out; |
||
590 | + } |
||
591 | + else if (!spdif && (chip->opened & (1 << idx))) { |
||
592 | + err = -EBUSY; |
||
593 | + goto out; |
||
594 | + } |
||
595 | + if (idx > MAX_SUBSTREAMS) { |
||
596 | + audio_error |
||
597 | + ("substream(%d) device doesn't exist max(%d) substreams allowed\n", |
||
598 | + idx, MAX_SUBSTREAMS); |
||
599 | + err = -ENODEV; |
||
600 | + goto out; |
||
601 | + } |
||
602 | + |
||
603 | + /* Check if we are ready */ |
||
604 | + if (!(chip->avail_substreams & (1 << idx))) { |
||
605 | + /* We are not ready yet */ |
||
606 | + audio_error("substream(%d) device is not ready yet\n", idx); |
||
607 | + err = -EAGAIN; |
||
608 | + goto out; |
||
609 | + } |
||
610 | + |
||
611 | + alsa_stream = kzalloc(sizeof(bcm2835_alsa_stream_t), GFP_KERNEL); |
||
612 | + if (alsa_stream == NULL) { |
||
613 | + err = -ENOMEM; |
||
614 | + goto out; |
||
615 | + } |
||
616 | + |
||
617 | + /* Initialise alsa_stream */ |
||
618 | + alsa_stream->chip = chip; |
||
619 | + alsa_stream->substream = substream; |
||
620 | + alsa_stream->idx = idx; |
||
621 | + |
||
622 | + sema_init(&alsa_stream->buffers_update_sem, 0); |
||
623 | + sema_init(&alsa_stream->control_sem, 0); |
||
624 | + spin_lock_init(&alsa_stream->lock); |
||
625 | + |
||
626 | + /* Enabled in start trigger, called on each "fifo irq" after that */ |
||
627 | + alsa_stream->enable_fifo_irq = 0; |
||
628 | + alsa_stream->fifo_irq_handler = bcm2835_playback_fifo_irq; |
||
629 | + |
||
630 | + err = bcm2835_audio_open(alsa_stream); |
||
631 | + if (err != 0) { |
||
632 | + kfree(alsa_stream); |
||
633 | + goto out; |
||
634 | + } |
||
635 | + runtime->private_data = alsa_stream; |
||
636 | + runtime->private_free = snd_bcm2835_playback_free; |
||
637 | + if (spdif) { |
||
638 | + runtime->hw = snd_bcm2835_playback_spdif_hw; |
||
639 | + } else { |
||
640 | + /* clear spdif status, as we are not in spdif mode */ |
||
641 | + chip->spdif_status = 0; |
||
642 | + runtime->hw = snd_bcm2835_playback_hw; |
||
643 | + } |
||
644 | + /* minimum 16 bytes alignment (for vchiq bulk transfers) */ |
||
645 | + snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, |
||
646 | + 16); |
||
647 | + |
||
648 | + chip->alsa_stream[idx] = alsa_stream; |
||
649 | + |
||
650 | + chip->opened |= (1 << idx); |
||
651 | + alsa_stream->open = 1; |
||
652 | + alsa_stream->draining = 1; |
||
653 | + |
||
654 | +out: |
||
655 | + mutex_unlock(&chip->audio_mutex); |
||
656 | + |
||
657 | + audio_info(" .. OUT =%d\n", err); |
||
658 | + |
||
659 | + return err; |
||
660 | +} |
||
661 | + |
||
662 | +static int snd_bcm2835_playback_open(struct snd_pcm_substream *substream) |
||
663 | +{ |
||
664 | + return snd_bcm2835_playback_open_generic(substream, 0); |
||
665 | +} |
||
666 | + |
||
667 | +static int snd_bcm2835_playback_spdif_open(struct snd_pcm_substream *substream) |
||
668 | +{ |
||
669 | + return snd_bcm2835_playback_open_generic(substream, 1); |
||
670 | +} |
||
671 | + |
||
672 | +/* close callback */ |
||
673 | +static int snd_bcm2835_playback_close(struct snd_pcm_substream *substream) |
||
674 | +{ |
||
675 | + /* the hardware-specific codes will be here */ |
||
676 | + |
||
677 | + bcm2835_chip_t *chip; |
||
678 | + struct snd_pcm_runtime *runtime; |
||
679 | + bcm2835_alsa_stream_t *alsa_stream; |
||
680 | + |
||
681 | + audio_info(" .. IN\n"); |
||
682 | + |
||
683 | + chip = snd_pcm_substream_chip(substream); |
||
684 | + if(mutex_lock_interruptible(&chip->audio_mutex)) |
||
685 | + { |
||
686 | + audio_error("Interrupted whilst waiting for lock\n"); |
||
687 | + return -EINTR; |
||
688 | + } |
||
689 | + runtime = substream->runtime; |
||
690 | + alsa_stream = runtime->private_data; |
||
691 | + |
||
692 | + audio_info("Alsa close\n"); |
||
693 | + |
||
694 | + /* |
||
695 | + * Call stop if it's still running. This happens when app |
||
696 | + * is force killed and we don't get a stop trigger. |
||
697 | + */ |
||
698 | + if (alsa_stream->running) { |
||
699 | + int err; |
||
700 | + err = bcm2835_audio_stop(alsa_stream); |
||
701 | + alsa_stream->running = 0; |
||
702 | + if (err != 0) |
||
703 | + audio_error(" Failed to STOP alsa device\n"); |
||
704 | + } |
||
705 | + |
||
706 | + alsa_stream->period_size = 0; |
||
707 | + alsa_stream->buffer_size = 0; |
||
708 | + |
||
709 | + if (alsa_stream->open) { |
||
710 | + alsa_stream->open = 0; |
||
711 | + bcm2835_audio_close(alsa_stream); |
||
712 | + } |
||
713 | + if (alsa_stream->chip) |
||
714 | + alsa_stream->chip->alsa_stream[alsa_stream->idx] = NULL; |
||
715 | + /* |
||
716 | + * Do not free up alsa_stream here, it will be freed up by |
||
717 | + * runtime->private_free callback we registered in *_open above |
||
718 | + */ |
||
719 | + |
||
720 | + chip->opened &= ~(1 << substream->number); |
||
721 | + |
||
722 | + mutex_unlock(&chip->audio_mutex); |
||
723 | + audio_info(" .. OUT\n"); |
||
724 | + |
||
725 | + return 0; |
||
726 | +} |
||
727 | + |
||
728 | +/* hw_params callback */ |
||
729 | +static int snd_bcm2835_pcm_hw_params(struct snd_pcm_substream *substream, |
||
730 | + struct snd_pcm_hw_params *params) |
||
731 | +{ |
||
732 | + struct snd_pcm_runtime *runtime = substream->runtime; |
||
733 | + bcm2835_alsa_stream_t *alsa_stream = runtime->private_data; |
||
734 | + int err; |
||
735 | + |
||
736 | + audio_info(" .. IN\n"); |
||
737 | + |
||
738 | + err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); |
||
739 | + if (err < 0) { |
||
740 | + audio_error |
||
741 | + (" pcm_lib_malloc failed to allocated pages for buffers\n"); |
||
742 | + return err; |
||
743 | + } |
||
744 | + |
||
745 | + alsa_stream->channels = params_channels(params); |
||
746 | + alsa_stream->params_rate = params_rate(params); |
||
747 | + alsa_stream->pcm_format_width = snd_pcm_format_width(params_format (params)); |
||
748 | + audio_info(" .. OUT\n"); |
||
749 | + |
||
750 | + return err; |
||
751 | +} |
||
752 | + |
||
753 | +/* hw_free callback */ |
||
754 | +static int snd_bcm2835_pcm_hw_free(struct snd_pcm_substream *substream) |
||
755 | +{ |
||
756 | + audio_info(" .. IN\n"); |
||
757 | + return snd_pcm_lib_free_pages(substream); |
||
758 | +} |
||
759 | + |
||
760 | +/* prepare callback */ |
||
761 | +static int snd_bcm2835_pcm_prepare(struct snd_pcm_substream *substream) |
||
762 | +{ |
||
763 | + bcm2835_chip_t *chip = snd_pcm_substream_chip(substream); |
||
764 | + struct snd_pcm_runtime *runtime = substream->runtime; |
||
765 | + bcm2835_alsa_stream_t *alsa_stream = runtime->private_data; |
||
766 | + int channels; |
||
767 | + int err; |
||
768 | + |
||
769 | + audio_info(" .. IN\n"); |
||
770 | + |
||
771 | + if (mutex_lock_interruptible(&chip->audio_mutex)) |
||
772 | + return -EINTR; |
||
773 | + |
||
774 | + /* notify the vchiq that it should enter spdif passthrough mode by |
||
775 | + * setting channels=0 (see |
||
776 | + * https://github.com/raspberrypi/linux/issues/528) */ |
||
777 | + if (chip->spdif_status & IEC958_AES0_NONAUDIO) |
||
778 | + channels = 0; |
||
779 | + else |
||
780 | + channels = alsa_stream->channels; |
||
781 | + |
||
782 | + err = bcm2835_audio_set_params(alsa_stream, channels, |
||
783 | + alsa_stream->params_rate, |
||
784 | + alsa_stream->pcm_format_width); |
||
785 | + if (err < 0) { |
||
786 | + audio_error(" error setting hw params\n"); |
||
787 | + } |
||
788 | + |
||
789 | + bcm2835_audio_setup(alsa_stream); |
||
790 | + |
||
791 | + /* in preparation of the stream, set the controls (volume level) of the stream */ |
||
792 | + bcm2835_audio_set_ctls(alsa_stream->chip); |
||
793 | + |
||
794 | + |
||
795 | + memset(&alsa_stream->pcm_indirect, 0, sizeof(alsa_stream->pcm_indirect)); |
||
796 | + |
||
797 | + alsa_stream->pcm_indirect.hw_buffer_size = |
||
798 | + alsa_stream->pcm_indirect.sw_buffer_size = |
||
799 | + snd_pcm_lib_buffer_bytes(substream); |
||
800 | + |
||
801 | + alsa_stream->buffer_size = snd_pcm_lib_buffer_bytes(substream); |
||
802 | + alsa_stream->period_size = snd_pcm_lib_period_bytes(substream); |
||
803 | + alsa_stream->pos = 0; |
||
804 | + |
||
805 | + audio_debug("buffer_size=%d, period_size=%d pos=%d frame_bits=%d\n", |
||
806 | + alsa_stream->buffer_size, alsa_stream->period_size, |
||
807 | + alsa_stream->pos, runtime->frame_bits); |
||
808 | + |
||
809 | + mutex_unlock(&chip->audio_mutex); |
||
810 | + audio_info(" .. OUT\n"); |
||
811 | + return 0; |
||
812 | +} |
||
813 | + |
||
814 | +static void snd_bcm2835_pcm_transfer(struct snd_pcm_substream *substream, |
||
815 | + struct snd_pcm_indirect *rec, size_t bytes) |
||
816 | +{ |
||
817 | + struct snd_pcm_runtime *runtime = substream->runtime; |
||
818 | + bcm2835_alsa_stream_t *alsa_stream = runtime->private_data; |
||
819 | + void *src = (void *)(substream->runtime->dma_area + rec->sw_data); |
||
820 | + int err; |
||
821 | + |
||
822 | + err = bcm2835_audio_write(alsa_stream, bytes, src); |
||
823 | + if (err) |
||
824 | + audio_error(" Failed to transfer to alsa device (%d)\n", err); |
||
825 | + |
||
826 | +} |
||
827 | + |
||
828 | +static int snd_bcm2835_pcm_ack(struct snd_pcm_substream *substream) |
||
829 | +{ |
||
830 | + struct snd_pcm_runtime *runtime = substream->runtime; |
||
831 | + bcm2835_alsa_stream_t *alsa_stream = runtime->private_data; |
||
832 | + struct snd_pcm_indirect *pcm_indirect = &alsa_stream->pcm_indirect; |
||
833 | + |
||
834 | + pcm_indirect->hw_queue_size = runtime->hw.buffer_bytes_max; |
||
835 | + snd_pcm_indirect_playback_transfer(substream, pcm_indirect, |
||
836 | + snd_bcm2835_pcm_transfer); |
||
837 | + return 0; |
||
838 | +} |
||
839 | + |
||
840 | +/* trigger callback */ |
||
841 | +static int snd_bcm2835_pcm_trigger(struct snd_pcm_substream *substream, int cmd) |
||
842 | +{ |
||
843 | + struct snd_pcm_runtime *runtime = substream->runtime; |
||
844 | + bcm2835_alsa_stream_t *alsa_stream = runtime->private_data; |
||
845 | + int err = 0; |
||
846 | + |
||
847 | + audio_info(" .. IN\n"); |
||
848 | + |
||
849 | + switch (cmd) { |
||
850 | + case SNDRV_PCM_TRIGGER_START: |
||
851 | + audio_debug("bcm2835_AUDIO_TRIGGER_START running=%d\n", |
||
852 | + alsa_stream->running); |
||
853 | + if (!alsa_stream->running) { |
||
854 | + err = bcm2835_audio_start(alsa_stream); |
||
855 | + if (err == 0) { |
||
856 | + alsa_stream->pcm_indirect.hw_io = |
||
857 | + alsa_stream->pcm_indirect.hw_data = |
||
858 | + bytes_to_frames(runtime, |
||
859 | + alsa_stream->pos); |
||
860 | + substream->ops->ack(substream); |
||
861 | + alsa_stream->running = 1; |
||
862 | + alsa_stream->draining = 1; |
||
863 | + } else { |
||
864 | + audio_error(" Failed to START alsa device (%d)\n", err); |
||
865 | + } |
||
866 | + } |
||
867 | + break; |
||
868 | + case SNDRV_PCM_TRIGGER_STOP: |
||
869 | + audio_debug |
||
870 | + ("bcm2835_AUDIO_TRIGGER_STOP running=%d draining=%d\n", |
||
871 | + alsa_stream->running, runtime->status->state == SNDRV_PCM_STATE_DRAINING); |
||
872 | + if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) { |
||
873 | + audio_info("DRAINING\n"); |
||
874 | + alsa_stream->draining = 1; |
||
875 | + } else { |
||
876 | + audio_info("DROPPING\n"); |
||
877 | + alsa_stream->draining = 0; |
||
878 | + } |
||
879 | + if (alsa_stream->running) { |
||
880 | + err = bcm2835_audio_stop(alsa_stream); |
||
881 | + if (err != 0) |
||
882 | + audio_error(" Failed to STOP alsa device (%d)\n", err); |
||
883 | + alsa_stream->running = 0; |
||
884 | + } |
||
885 | + break; |
||
886 | + default: |
||
887 | + err = -EINVAL; |
||
888 | + } |
||
889 | + |
||
890 | + audio_info(" .. OUT\n"); |
||
891 | + return err; |
||
892 | +} |
||
893 | + |
||
894 | +/* pointer callback */ |
||
895 | +static snd_pcm_uframes_t |
||
896 | +snd_bcm2835_pcm_pointer(struct snd_pcm_substream *substream) |
||
897 | +{ |
||
898 | + struct snd_pcm_runtime *runtime = substream->runtime; |
||
899 | + bcm2835_alsa_stream_t *alsa_stream = runtime->private_data; |
||
900 | + |
||
901 | + audio_info(" .. IN\n"); |
||
902 | + |
||
903 | + audio_debug("pcm_pointer... (%d) hwptr=%d appl=%d pos=%d\n", 0, |
||
904 | + frames_to_bytes(runtime, runtime->status->hw_ptr), |
||
905 | + frames_to_bytes(runtime, runtime->control->appl_ptr), |
||
906 | + alsa_stream->pos); |
||
907 | + |
||
908 | + audio_info(" .. OUT\n"); |
||
909 | + return snd_pcm_indirect_playback_pointer(substream, |
||
910 | + &alsa_stream->pcm_indirect, |
||
911 | + alsa_stream->pos); |
||
912 | +} |
||
913 | + |
||
914 | +static int snd_bcm2835_pcm_lib_ioctl(struct snd_pcm_substream *substream, |
||
915 | + unsigned int cmd, void *arg) |
||
916 | +{ |
||
917 | + int ret = snd_pcm_lib_ioctl(substream, cmd, arg); |
||
918 | + audio_info(" .. substream=%p, cmd=%d, arg=%p (%x) ret=%d\n", substream, |
||
919 | + cmd, arg, arg ? *(unsigned *)arg : 0, ret); |
||
920 | + return ret; |
||
921 | +} |
||
922 | + |
||
923 | +/* operators */ |
||
924 | +static struct snd_pcm_ops snd_bcm2835_playback_ops = { |
||
925 | + .open = snd_bcm2835_playback_open, |
||
926 | + .close = snd_bcm2835_playback_close, |
||
927 | + .ioctl = snd_bcm2835_pcm_lib_ioctl, |
||
928 | + .hw_params = snd_bcm2835_pcm_hw_params, |
||
929 | + .hw_free = snd_bcm2835_pcm_hw_free, |
||
930 | + .prepare = snd_bcm2835_pcm_prepare, |
||
931 | + .trigger = snd_bcm2835_pcm_trigger, |
||
932 | + .pointer = snd_bcm2835_pcm_pointer, |
||
933 | + .ack = snd_bcm2835_pcm_ack, |
||
934 | +}; |
||
935 | + |
||
936 | +static struct snd_pcm_ops snd_bcm2835_playback_spdif_ops = { |
||
937 | + .open = snd_bcm2835_playback_spdif_open, |
||
938 | + .close = snd_bcm2835_playback_close, |
||
939 | + .ioctl = snd_bcm2835_pcm_lib_ioctl, |
||
940 | + .hw_params = snd_bcm2835_pcm_hw_params, |
||
941 | + .hw_free = snd_bcm2835_pcm_hw_free, |
||
942 | + .prepare = snd_bcm2835_pcm_prepare, |
||
943 | + .trigger = snd_bcm2835_pcm_trigger, |
||
944 | + .pointer = snd_bcm2835_pcm_pointer, |
||
945 | + .ack = snd_bcm2835_pcm_ack, |
||
946 | +}; |
||
947 | + |
||
948 | +/* create a pcm device */ |
||
949 | +int snd_bcm2835_new_pcm(bcm2835_chip_t * chip) |
||
950 | +{ |
||
951 | + struct snd_pcm *pcm; |
||
952 | + int err; |
||
953 | + |
||
954 | + audio_info(" .. IN\n"); |
||
955 | + mutex_init(&chip->audio_mutex); |
||
956 | + if(mutex_lock_interruptible(&chip->audio_mutex)) |
||
957 | + { |
||
958 | + audio_error("Interrupted whilst waiting for lock\n"); |
||
959 | + return -EINTR; |
||
960 | + } |
||
961 | + err = |
||
962 | + snd_pcm_new(chip->card, "bcm2835 ALSA", 0, MAX_SUBSTREAMS, 0, &pcm); |
||
963 | + if (err < 0) |
||
964 | + goto out; |
||
965 | + pcm->private_data = chip; |
||
966 | + strcpy(pcm->name, "bcm2835 ALSA"); |
||
967 | + chip->pcm = pcm; |
||
968 | + chip->dest = AUDIO_DEST_AUTO; |
||
969 | + chip->volume = alsa2chip(0); |
||
970 | + chip->mute = CTRL_VOL_UNMUTE; /*disable mute on startup */ |
||
971 | + /* set operators */ |
||
972 | + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, |
||
973 | + &snd_bcm2835_playback_ops); |
||
974 | + |
||
975 | + /* pre-allocation of buffers */ |
||
976 | + /* NOTE: this may fail */ |
||
977 | + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, |
||
978 | + snd_dma_continuous_data (GFP_KERNEL), |
||
979 | + snd_bcm2835_playback_hw.buffer_bytes_max, snd_bcm2835_playback_hw.buffer_bytes_max); |
||
980 | + |
||
981 | + |
||
982 | +out: |
||
983 | + mutex_unlock(&chip->audio_mutex); |
||
984 | + audio_info(" .. OUT\n"); |
||
985 | + |
||
986 | + return 0; |
||
987 | +} |
||
988 | + |
||
989 | +int snd_bcm2835_new_spdif_pcm(bcm2835_chip_t * chip) |
||
990 | +{ |
||
991 | + struct snd_pcm *pcm; |
||
992 | + int err; |
||
993 | + |
||
994 | + audio_info(" .. IN\n"); |
||
995 | + if(mutex_lock_interruptible(&chip->audio_mutex)) |
||
996 | + { |
||
997 | + audio_error("Interrupted whilst waiting for lock\n"); |
||
998 | + return -EINTR; |
||
999 | + } |
||
1000 | + err = snd_pcm_new(chip->card, "bcm2835 ALSA", 1, 1, 0, &pcm); |
||
1001 | + if (err < 0) |
||
1002 | + goto out; |
||
1003 | + |
||
1004 | + pcm->private_data = chip; |
||
1005 | + strcpy(pcm->name, "bcm2835 IEC958/HDMI"); |
||
1006 | + chip->pcm_spdif = pcm; |
||
1007 | + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, |
||
1008 | + &snd_bcm2835_playback_spdif_ops); |
||
1009 | + |
||
1010 | + /* pre-allocation of buffers */ |
||
1011 | + /* NOTE: this may fail */ |
||
1012 | + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, |
||
1013 | + snd_dma_continuous_data (GFP_KERNEL), |
||
1014 | + snd_bcm2835_playback_spdif_hw.buffer_bytes_max, snd_bcm2835_playback_spdif_hw.buffer_bytes_max); |
||
1015 | +out: |
||
1016 | + mutex_unlock(&chip->audio_mutex); |
||
1017 | + audio_info(" .. OUT\n"); |
||
1018 | + |
||
1019 | + return 0; |
||
1020 | +} |
||
1021 | --- /dev/null |
||
1022 | +++ b/sound/arm/bcm2835-vchiq.c |
||
1023 | @@ -0,0 +1,889 @@ |
||
1024 | +/***************************************************************************** |
||
1025 | +* Copyright 2011 Broadcom Corporation. All rights reserved. |
||
1026 | +* |
||
1027 | +* Unless you and Broadcom execute a separate written software license |
||
1028 | +* agreement governing use of this software, this software is licensed to you |
||
1029 | +* under the terms of the GNU General Public License version 2, available at |
||
1030 | +* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). |
||
1031 | +* |
||
1032 | +* Notwithstanding the above, under no circumstances may you combine this |
||
1033 | +* software in any way with any other Broadcom software provided under a |
||
1034 | +* license other than the GPL, without Broadcom's express prior written |
||
1035 | +* consent. |
||
1036 | +*****************************************************************************/ |
||
1037 | + |
||
1038 | +#include <linux/device.h> |
||
1039 | +#include <sound/core.h> |
||
1040 | +#include <sound/initval.h> |
||
1041 | +#include <sound/pcm.h> |
||
1042 | +#include <linux/io.h> |
||
1043 | +#include <linux/interrupt.h> |
||
1044 | +#include <linux/fs.h> |
||
1045 | +#include <linux/file.h> |
||
1046 | +#include <linux/mm.h> |
||
1047 | +#include <linux/syscalls.h> |
||
1048 | +#include <asm/uaccess.h> |
||
1049 | +#include <linux/slab.h> |
||
1050 | +#include <linux/delay.h> |
||
1051 | +#include <linux/atomic.h> |
||
1052 | +#include <linux/module.h> |
||
1053 | +#include <linux/completion.h> |
||
1054 | + |
||
1055 | +#include "bcm2835.h" |
||
1056 | + |
||
1057 | +/* ---- Include Files -------------------------------------------------------- */ |
||
1058 | + |
||
1059 | +#include "interface/vchi/vchi.h" |
||
1060 | +#include "vc_vchi_audioserv_defs.h" |
||
1061 | + |
||
1062 | +/* ---- Private Constants and Types ------------------------------------------ */ |
||
1063 | + |
||
1064 | +#define BCM2835_AUDIO_STOP 0 |
||
1065 | +#define BCM2835_AUDIO_START 1 |
||
1066 | +#define BCM2835_AUDIO_WRITE 2 |
||
1067 | + |
||
1068 | +/* Logging macros (for remapping to other logging mechanisms, i.e., printf) */ |
||
1069 | +#ifdef AUDIO_DEBUG_ENABLE |
||
1070 | + #define LOG_ERR( fmt, arg... ) pr_err( "%s:%d " fmt, __func__, __LINE__, ##arg) |
||
1071 | + #define LOG_WARN( fmt, arg... ) pr_info( "%s:%d " fmt, __func__, __LINE__, ##arg) |
||
1072 | + #define LOG_INFO( fmt, arg... ) pr_info( "%s:%d " fmt, __func__, __LINE__, ##arg) |
||
1073 | + #define LOG_DBG( fmt, arg... ) pr_info( "%s:%d " fmt, __func__, __LINE__, ##arg) |
||
1074 | +#else |
||
1075 | + #define LOG_ERR( fmt, arg... ) pr_err( "%s:%d " fmt, __func__, __LINE__, ##arg) |
||
1076 | + #define LOG_WARN( fmt, arg... ) |
||
1077 | + #define LOG_INFO( fmt, arg... ) |
||
1078 | + #define LOG_DBG( fmt, arg... ) |
||
1079 | +#endif |
||
1080 | + |
||
1081 | +typedef struct opaque_AUDIO_INSTANCE_T { |
||
1082 | + uint32_t num_connections; |
||
1083 | + VCHI_SERVICE_HANDLE_T vchi_handle[VCHI_MAX_NUM_CONNECTIONS]; |
||
1084 | + struct completion msg_avail_comp; |
||
1085 | + struct mutex vchi_mutex; |
||
1086 | + bcm2835_alsa_stream_t *alsa_stream; |
||
1087 | + int32_t result; |
||
1088 | + short peer_version; |
||
1089 | +} AUDIO_INSTANCE_T; |
||
1090 | + |
||
1091 | +bool force_bulk = false; |
||
1092 | + |
||
1093 | +/* ---- Private Variables ---------------------------------------------------- */ |
||
1094 | + |
||
1095 | +/* ---- Private Function Prototypes ------------------------------------------ */ |
||
1096 | + |
||
1097 | +/* ---- Private Functions ---------------------------------------------------- */ |
||
1098 | + |
||
1099 | +static int bcm2835_audio_stop_worker(bcm2835_alsa_stream_t * alsa_stream); |
||
1100 | +static int bcm2835_audio_start_worker(bcm2835_alsa_stream_t * alsa_stream); |
||
1101 | +static int bcm2835_audio_write_worker(bcm2835_alsa_stream_t *alsa_stream, |
||
1102 | + uint32_t count, void *src); |
||
1103 | + |
||
1104 | +typedef struct { |
||
1105 | + struct work_struct my_work; |
||
1106 | + bcm2835_alsa_stream_t *alsa_stream; |
||
1107 | + int cmd; |
||
1108 | + void *src; |
||
1109 | + uint32_t count; |
||
1110 | +} my_work_t; |
||
1111 | + |
||
1112 | +static void my_wq_function(struct work_struct *work) |
||
1113 | +{ |
||
1114 | + my_work_t *w = (my_work_t *) work; |
||
1115 | + int ret = -9; |
||
1116 | + LOG_DBG(" .. IN %p:%d\n", w->alsa_stream, w->cmd); |
||
1117 | + switch (w->cmd) { |
||
1118 | + case BCM2835_AUDIO_START: |
||
1119 | + ret = bcm2835_audio_start_worker(w->alsa_stream); |
||
1120 | + break; |
||
1121 | + case BCM2835_AUDIO_STOP: |
||
1122 | + ret = bcm2835_audio_stop_worker(w->alsa_stream); |
||
1123 | + break; |
||
1124 | + case BCM2835_AUDIO_WRITE: |
||
1125 | + ret = bcm2835_audio_write_worker(w->alsa_stream, w->count, |
||
1126 | + w->src); |
||
1127 | + break; |
||
1128 | + default: |
||
1129 | + LOG_ERR(" Unexpected work: %p:%d\n", w->alsa_stream, w->cmd); |
||
1130 | + break; |
||
1131 | + } |
||
1132 | + kfree((void *)work); |
||
1133 | + LOG_DBG(" .. OUT %d\n", ret); |
||
1134 | +} |
||
1135 | + |
||
1136 | +int bcm2835_audio_start(bcm2835_alsa_stream_t * alsa_stream) |
||
1137 | +{ |
||
1138 | + int ret = -1; |
||
1139 | + LOG_DBG(" .. IN\n"); |
||
1140 | + if (alsa_stream->my_wq) { |
||
1141 | + my_work_t *work = kmalloc(sizeof(my_work_t), GFP_ATOMIC); |
||
1142 | + /*--- Queue some work (item 1) ---*/ |
||
1143 | + if (work) { |
||
1144 | + INIT_WORK((struct work_struct *)work, my_wq_function); |
||
1145 | + work->alsa_stream = alsa_stream; |
||
1146 | + work->cmd = BCM2835_AUDIO_START; |
||
1147 | + if (queue_work |
||
1148 | + (alsa_stream->my_wq, (struct work_struct *)work)) |
||
1149 | + ret = 0; |
||
1150 | + } else |
||
1151 | + LOG_ERR(" .. Error: NULL work kmalloc\n"); |
||
1152 | + } |
||
1153 | + LOG_DBG(" .. OUT %d\n", ret); |
||
1154 | + return ret; |
||
1155 | +} |
||
1156 | + |
||
1157 | +int bcm2835_audio_stop(bcm2835_alsa_stream_t * alsa_stream) |
||
1158 | +{ |
||
1159 | + int ret = -1; |
||
1160 | + LOG_DBG(" .. IN\n"); |
||
1161 | + if (alsa_stream->my_wq) { |
||
1162 | + my_work_t *work = kmalloc(sizeof(my_work_t), GFP_ATOMIC); |
||
1163 | + /*--- Queue some work (item 1) ---*/ |
||
1164 | + if (work) { |
||
1165 | + INIT_WORK((struct work_struct *)work, my_wq_function); |
||
1166 | + work->alsa_stream = alsa_stream; |
||
1167 | + work->cmd = BCM2835_AUDIO_STOP; |
||
1168 | + if (queue_work |
||
1169 | + (alsa_stream->my_wq, (struct work_struct *)work)) |
||
1170 | + ret = 0; |
||
1171 | + } else |
||
1172 | + LOG_ERR(" .. Error: NULL work kmalloc\n"); |
||
1173 | + } |
||
1174 | + LOG_DBG(" .. OUT %d\n", ret); |
||
1175 | + return ret; |
||
1176 | +} |
||
1177 | + |
||
1178 | +int bcm2835_audio_write(bcm2835_alsa_stream_t *alsa_stream, |
||
1179 | + uint32_t count, void *src) |
||
1180 | +{ |
||
1181 | + int ret = -1; |
||
1182 | + LOG_DBG(" .. IN\n"); |
||
1183 | + if (alsa_stream->my_wq) { |
||
1184 | + my_work_t *work = kmalloc(sizeof(my_work_t), GFP_ATOMIC); |
||
1185 | + /*--- Queue some work (item 1) ---*/ |
||
1186 | + if (work) { |
||
1187 | + INIT_WORK((struct work_struct *)work, my_wq_function); |
||
1188 | + work->alsa_stream = alsa_stream; |
||
1189 | + work->cmd = BCM2835_AUDIO_WRITE; |
||
1190 | + work->src = src; |
||
1191 | + work->count = count; |
||
1192 | + if (queue_work |
||
1193 | + (alsa_stream->my_wq, (struct work_struct *)work)) |
||
1194 | + ret = 0; |
||
1195 | + } else |
||
1196 | + LOG_ERR(" .. Error: NULL work kmalloc\n"); |
||
1197 | + } |
||
1198 | + LOG_DBG(" .. OUT %d\n", ret); |
||
1199 | + return ret; |
||
1200 | +} |
||
1201 | + |
||
1202 | +void my_workqueue_init(bcm2835_alsa_stream_t * alsa_stream) |
||
1203 | +{ |
||
1204 | + alsa_stream->my_wq = alloc_workqueue("my_queue", WQ_HIGHPRI, 1); |
||
1205 | + return; |
||
1206 | +} |
||
1207 | + |
||
1208 | +void my_workqueue_quit(bcm2835_alsa_stream_t * alsa_stream) |
||
1209 | +{ |
||
1210 | + if (alsa_stream->my_wq) { |
||
1211 | + flush_workqueue(alsa_stream->my_wq); |
||
1212 | + destroy_workqueue(alsa_stream->my_wq); |
||
1213 | + alsa_stream->my_wq = NULL; |
||
1214 | + } |
||
1215 | + return; |
||
1216 | +} |
||
1217 | + |
||
1218 | +static void audio_vchi_callback(void *param, |
||
1219 | + const VCHI_CALLBACK_REASON_T reason, |
||
1220 | + void *msg_handle) |
||
1221 | +{ |
||
1222 | + AUDIO_INSTANCE_T *instance = (AUDIO_INSTANCE_T *) param; |
||
1223 | + int32_t status; |
||
1224 | + int32_t msg_len; |
||
1225 | + VC_AUDIO_MSG_T m; |
||
1226 | + LOG_DBG(" .. IN instance=%p, handle=%p, alsa=%p, reason=%d, handle=%p\n", |
||
1227 | + instance, instance ? instance->vchi_handle[0] : NULL, instance ? instance->alsa_stream : NULL, reason, msg_handle); |
||
1228 | + |
||
1229 | + if (reason != VCHI_CALLBACK_MSG_AVAILABLE) { |
||
1230 | + return; |
||
1231 | + } |
||
1232 | + if (!instance) { |
||
1233 | + LOG_ERR(" .. instance is null\n"); |
||
1234 | + BUG(); |
||
1235 | + return; |
||
1236 | + } |
||
1237 | + if (!instance->vchi_handle[0]) { |
||
1238 | + LOG_ERR(" .. instance->vchi_handle[0] is null\n"); |
||
1239 | + BUG(); |
||
1240 | + return; |
||
1241 | + } |
||
1242 | + status = vchi_msg_dequeue(instance->vchi_handle[0], |
||
1243 | + &m, sizeof m, &msg_len, VCHI_FLAGS_NONE); |
||
1244 | + if (m.type == VC_AUDIO_MSG_TYPE_RESULT) { |
||
1245 | + LOG_DBG |
||
1246 | + (" .. instance=%p, m.type=VC_AUDIO_MSG_TYPE_RESULT, success=%d\n", |
||
1247 | + instance, m.u.result.success); |
||
1248 | + instance->result = m.u.result.success; |
||
1249 | + complete(&instance->msg_avail_comp); |
||
1250 | + } else if (m.type == VC_AUDIO_MSG_TYPE_COMPLETE) { |
||
1251 | + bcm2835_alsa_stream_t *alsa_stream = instance->alsa_stream; |
||
1252 | + irq_handler_t callback = (irq_handler_t) m.u.complete.callback; |
||
1253 | + LOG_DBG |
||
1254 | + (" .. instance=%p, m.type=VC_AUDIO_MSG_TYPE_COMPLETE, complete=%d\n", |
||
1255 | + instance, m.u.complete.count); |
||
1256 | + if (alsa_stream && callback) { |
||
1257 | + atomic_add(m.u.complete.count, &alsa_stream->retrieved); |
||
1258 | + callback(0, alsa_stream); |
||
1259 | + } else { |
||
1260 | + LOG_ERR(" .. unexpected alsa_stream=%p, callback=%p\n", |
||
1261 | + alsa_stream, callback); |
||
1262 | + } |
||
1263 | + } else { |
||
1264 | + LOG_ERR(" .. unexpected m.type=%d\n", m.type); |
||
1265 | + } |
||
1266 | + LOG_DBG(" .. OUT\n"); |
||
1267 | +} |
||
1268 | + |
||
1269 | +static AUDIO_INSTANCE_T *vc_vchi_audio_init(VCHI_INSTANCE_T vchi_instance, |
||
1270 | + VCHI_CONNECTION_T ** |
||
1271 | + vchi_connections, |
||
1272 | + uint32_t num_connections) |
||
1273 | +{ |
||
1274 | + uint32_t i; |
||
1275 | + AUDIO_INSTANCE_T *instance; |
||
1276 | + int status; |
||
1277 | + |
||
1278 | + LOG_DBG("%s: start", __func__); |
||
1279 | + |
||
1280 | + if (num_connections > VCHI_MAX_NUM_CONNECTIONS) { |
||
1281 | + LOG_ERR("%s: unsupported number of connections %u (max=%u)\n", |
||
1282 | + __func__, num_connections, VCHI_MAX_NUM_CONNECTIONS); |
||
1283 | + |
||
1284 | + return NULL; |
||
1285 | + } |
||
1286 | + /* Allocate memory for this instance */ |
||
1287 | + instance = kmalloc(sizeof(*instance), GFP_KERNEL); |
||
1288 | + if (!instance) |
||
1289 | + return NULL; |
||
1290 | + |
||
1291 | + memset(instance, 0, sizeof(*instance)); |
||
1292 | + instance->num_connections = num_connections; |
||
1293 | + |
||
1294 | + /* Create a lock for exclusive, serialized VCHI connection access */ |
||
1295 | + mutex_init(&instance->vchi_mutex); |
||
1296 | + /* Open the VCHI service connections */ |
||
1297 | + for (i = 0; i < num_connections; i++) { |
||
1298 | + SERVICE_CREATION_T params = { |
||
1299 | + VCHI_VERSION_EX(VC_AUDIOSERV_VER, VC_AUDIOSERV_MIN_VER), |
||
1300 | + VC_AUDIO_SERVER_NAME, // 4cc service code |
||
1301 | + vchi_connections[i], // passed in fn pointers |
||
1302 | + 0, // rx fifo size (unused) |
||
1303 | + 0, // tx fifo size (unused) |
||
1304 | + audio_vchi_callback, // service callback |
||
1305 | + instance, // service callback parameter |
||
1306 | + 1, //TODO: remove VCOS_FALSE, // unaligned bulk recieves |
||
1307 | + 1, //TODO: remove VCOS_FALSE, // unaligned bulk transmits |
||
1308 | + 0 // want crc check on bulk transfers |
||
1309 | + }; |
||
1310 | + |
||
1311 | + LOG_DBG("%s: about to open %i\n", __func__, i); |
||
1312 | + status = vchi_service_open(vchi_instance, ¶ms, |
||
1313 | + &instance->vchi_handle[i]); |
||
1314 | + LOG_DBG("%s: opened %i: %p=%d\n", __func__, i, instance->vchi_handle[i], status); |
||
1315 | + if (status) { |
||
1316 | + LOG_ERR |
||
1317 | + ("%s: failed to open VCHI service connection (status=%d)\n", |
||
1318 | + __func__, status); |
||
1319 | + |
||
1320 | + goto err_close_services; |
||
1321 | + } |
||
1322 | + /* Finished with the service for now */ |
||
1323 | + vchi_service_release(instance->vchi_handle[i]); |
||
1324 | + } |
||
1325 | + |
||
1326 | + LOG_DBG("%s: okay\n", __func__); |
||
1327 | + return instance; |
||
1328 | + |
||
1329 | +err_close_services: |
||
1330 | + for (i = 0; i < instance->num_connections; i++) { |
||
1331 | + LOG_ERR("%s: closing %i: %p\n", __func__, i, instance->vchi_handle[i]); |
||
1332 | + if (instance->vchi_handle[i]) |
||
1333 | + vchi_service_close(instance->vchi_handle[i]); |
||
1334 | + } |
||
1335 | + |
||
1336 | + kfree(instance); |
||
1337 | + LOG_ERR("%s: error\n", __func__); |
||
1338 | + |
||
1339 | + return NULL; |
||
1340 | +} |
||
1341 | + |
||
1342 | +static int32_t vc_vchi_audio_deinit(AUDIO_INSTANCE_T * instance) |
||
1343 | +{ |
||
1344 | + uint32_t i; |
||
1345 | + |
||
1346 | + LOG_DBG(" .. IN\n"); |
||
1347 | + |
||
1348 | + if (instance == NULL) { |
||
1349 | + LOG_ERR("%s: invalid handle %p\n", __func__, instance); |
||
1350 | + |
||
1351 | + return -1; |
||
1352 | + } |
||
1353 | + |
||
1354 | + LOG_DBG(" .. about to lock (%d)\n", instance->num_connections); |
||
1355 | + if(mutex_lock_interruptible(&instance->vchi_mutex)) |
||
1356 | + { |
||
1357 | + LOG_DBG("Interrupted whilst waiting for lock on (%d)\n",instance->num_connections); |
||
1358 | + return -EINTR; |
||
1359 | + } |
||
1360 | + |
||
1361 | + /* Close all VCHI service connections */ |
||
1362 | + for (i = 0; i < instance->num_connections; i++) { |
||
1363 | + int32_t success; |
||
1364 | + LOG_DBG(" .. %i:closing %p\n", i, instance->vchi_handle[i]); |
||
1365 | + vchi_service_use(instance->vchi_handle[i]); |
||
1366 | + |
||
1367 | + success = vchi_service_close(instance->vchi_handle[i]); |
||
1368 | + if (success != 0) { |
||
1369 | + LOG_DBG |
||
1370 | + ("%s: failed to close VCHI service connection (status=%d)\n", |
||
1371 | + __func__, success); |
||
1372 | + } |
||
1373 | + } |
||
1374 | + |
||
1375 | + mutex_unlock(&instance->vchi_mutex); |
||
1376 | + |
||
1377 | + kfree(instance); |
||
1378 | + |
||
1379 | + LOG_DBG(" .. OUT\n"); |
||
1380 | + |
||
1381 | + return 0; |
||
1382 | +} |
||
1383 | + |
||
1384 | +static int bcm2835_audio_open_connection(bcm2835_alsa_stream_t * alsa_stream) |
||
1385 | +{ |
||
1386 | + static VCHI_INSTANCE_T vchi_instance; |
||
1387 | + static VCHI_CONNECTION_T *vchi_connection; |
||
1388 | + static int initted; |
||
1389 | + AUDIO_INSTANCE_T *instance = alsa_stream->instance; |
||
1390 | + int ret; |
||
1391 | + LOG_DBG(" .. IN\n"); |
||
1392 | + |
||
1393 | + LOG_INFO("%s: start\n", __func__); |
||
1394 | + BUG_ON(instance); |
||
1395 | + if (instance) { |
||
1396 | + LOG_ERR("%s: VCHI instance already open (%p)\n", |
||
1397 | + __func__, instance); |
||
1398 | + instance->alsa_stream = alsa_stream; |
||
1399 | + alsa_stream->instance = instance; |
||
1400 | + ret = 0; // xxx todo -1; |
||
1401 | + goto err_free_mem; |
||
1402 | + } |
||
1403 | + |
||
1404 | + /* Initialize and create a VCHI connection */ |
||
1405 | + if (!initted) { |
||
1406 | + ret = vchi_initialise(&vchi_instance); |
||
1407 | + if (ret != 0) { |
||
1408 | + LOG_ERR("%s: failed to initialise VCHI instance (ret=%d)\n", |
||
1409 | + __func__, ret); |
||
1410 | + |
||
1411 | + ret = -EIO; |
||
1412 | + goto err_free_mem; |
||
1413 | + } |
||
1414 | + ret = vchi_connect(NULL, 0, vchi_instance); |
||
1415 | + if (ret != 0) { |
||
1416 | + LOG_ERR("%s: failed to connect VCHI instance (ret=%d)\n", |
||
1417 | + __func__, ret); |
||
1418 | + |
||
1419 | + ret = -EIO; |
||
1420 | + goto err_free_mem; |
||
1421 | + } |
||
1422 | + initted = 1; |
||
1423 | + } |
||
1424 | + |
||
1425 | + /* Initialize an instance of the audio service */ |
||
1426 | + instance = vc_vchi_audio_init(vchi_instance, &vchi_connection, 1); |
||
1427 | + |
||
1428 | + if (instance == NULL) { |
||
1429 | + LOG_ERR("%s: failed to initialize audio service\n", __func__); |
||
1430 | + |
||
1431 | + ret = -EPERM; |
||
1432 | + goto err_free_mem; |
||
1433 | + } |
||
1434 | + |
||
1435 | + instance->alsa_stream = alsa_stream; |
||
1436 | + alsa_stream->instance = instance; |
||
1437 | + |
||
1438 | + LOG_DBG(" success !\n"); |
||
1439 | +err_free_mem: |
||
1440 | + LOG_DBG(" .. OUT\n"); |
||
1441 | + |
||
1442 | + return ret; |
||
1443 | +} |
||
1444 | + |
||
1445 | +int bcm2835_audio_open(bcm2835_alsa_stream_t * alsa_stream) |
||
1446 | +{ |
||
1447 | + AUDIO_INSTANCE_T *instance; |
||
1448 | + VC_AUDIO_MSG_T m; |
||
1449 | + int32_t success; |
||
1450 | + int ret; |
||
1451 | + LOG_DBG(" .. IN\n"); |
||
1452 | + |
||
1453 | + my_workqueue_init(alsa_stream); |
||
1454 | + |
||
1455 | + ret = bcm2835_audio_open_connection(alsa_stream); |
||
1456 | + if (ret != 0) { |
||
1457 | + ret = -1; |
||
1458 | + goto exit; |
||
1459 | + } |
||
1460 | + instance = alsa_stream->instance; |
||
1461 | + LOG_DBG(" instance (%p)\n", instance); |
||
1462 | + |
||
1463 | + if(mutex_lock_interruptible(&instance->vchi_mutex)) |
||
1464 | + { |
||
1465 | + LOG_DBG("Interrupted whilst waiting for lock on (%d)\n",instance->num_connections); |
||
1466 | + return -EINTR; |
||
1467 | + } |
||
1468 | + vchi_service_use(instance->vchi_handle[0]); |
||
1469 | + |
||
1470 | + m.type = VC_AUDIO_MSG_TYPE_OPEN; |
||
1471 | + |
||
1472 | + /* Send the message to the videocore */ |
||
1473 | + success = vchi_msg_queue(instance->vchi_handle[0], |
||
1474 | + &m, sizeof m, |
||
1475 | + VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); |
||
1476 | + |
||
1477 | + if (success != 0) { |
||
1478 | + LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", |
||
1479 | + __func__, success); |
||
1480 | + |
||
1481 | + ret = -1; |
||
1482 | + goto unlock; |
||
1483 | + } |
||
1484 | + |
||
1485 | + ret = 0; |
||
1486 | + |
||
1487 | +unlock: |
||
1488 | + vchi_service_release(instance->vchi_handle[0]); |
||
1489 | + mutex_unlock(&instance->vchi_mutex); |
||
1490 | +exit: |
||
1491 | + LOG_DBG(" .. OUT\n"); |
||
1492 | + return ret; |
||
1493 | +} |
||
1494 | + |
||
1495 | +static int bcm2835_audio_set_ctls_chan(bcm2835_alsa_stream_t * alsa_stream, |
||
1496 | + bcm2835_chip_t * chip) |
||
1497 | +{ |
||
1498 | + VC_AUDIO_MSG_T m; |
||
1499 | + AUDIO_INSTANCE_T *instance = alsa_stream->instance; |
||
1500 | + int32_t success; |
||
1501 | + int ret; |
||
1502 | + LOG_DBG(" .. IN\n"); |
||
1503 | + |
||
1504 | + LOG_INFO |
||
1505 | + (" Setting ALSA dest(%d), volume(%d)\n", chip->dest, chip->volume); |
||
1506 | + |
||
1507 | + if(mutex_lock_interruptible(&instance->vchi_mutex)) |
||
1508 | + { |
||
1509 | + LOG_DBG("Interrupted whilst waiting for lock on (%d)\n",instance->num_connections); |
||
1510 | + return -EINTR; |
||
1511 | + } |
||
1512 | + vchi_service_use(instance->vchi_handle[0]); |
||
1513 | + |
||
1514 | + instance->result = -1; |
||
1515 | + |
||
1516 | + m.type = VC_AUDIO_MSG_TYPE_CONTROL; |
||
1517 | + m.u.control.dest = chip->dest; |
||
1518 | + m.u.control.volume = chip->volume; |
||
1519 | + |
||
1520 | + /* Create the message available completion */ |
||
1521 | + init_completion(&instance->msg_avail_comp); |
||
1522 | + |
||
1523 | + /* Send the message to the videocore */ |
||
1524 | + success = vchi_msg_queue(instance->vchi_handle[0], |
||
1525 | + &m, sizeof m, |
||
1526 | + VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); |
||
1527 | + |
||
1528 | + if (success != 0) { |
||
1529 | + LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", |
||
1530 | + __func__, success); |
||
1531 | + |
||
1532 | + ret = -1; |
||
1533 | + goto unlock; |
||
1534 | + } |
||
1535 | + |
||
1536 | + /* We are expecting a reply from the videocore */ |
||
1537 | + wait_for_completion(&instance->msg_avail_comp); |
||
1538 | + |
||
1539 | + if (instance->result != 0) { |
||
1540 | + LOG_ERR("%s: result=%d\n", __func__, instance->result); |
||
1541 | + |
||
1542 | + ret = -1; |
||
1543 | + goto unlock; |
||
1544 | + } |
||
1545 | + |
||
1546 | + ret = 0; |
||
1547 | + |
||
1548 | +unlock: |
||
1549 | + vchi_service_release(instance->vchi_handle[0]); |
||
1550 | + mutex_unlock(&instance->vchi_mutex); |
||
1551 | + |
||
1552 | + LOG_DBG(" .. OUT\n"); |
||
1553 | + return ret; |
||
1554 | +} |
||
1555 | + |
||
1556 | +int bcm2835_audio_set_ctls(bcm2835_chip_t * chip) |
||
1557 | +{ |
||
1558 | + int i; |
||
1559 | + int ret = 0; |
||
1560 | + LOG_DBG(" .. IN\n"); |
||
1561 | + LOG_DBG(" Setting ALSA dest(%d), volume(%d)\n", chip->dest, chip->volume); |
||
1562 | + |
||
1563 | + /* change ctls for all substreams */ |
||
1564 | + for (i = 0; i < MAX_SUBSTREAMS; i++) { |
||
1565 | + if (chip->avail_substreams & (1 << i)) { |
||
1566 | + if (!chip->alsa_stream[i]) |
||
1567 | + { |
||
1568 | + LOG_DBG(" No ALSA stream available?! %i:%p (%x)\n", i, chip->alsa_stream[i], chip->avail_substreams); |
||
1569 | + ret = 0; |
||
1570 | + } |
||
1571 | + else if (bcm2835_audio_set_ctls_chan /* returns 0 on success */ |
||
1572 | + (chip->alsa_stream[i], chip) != 0) |
||
1573 | + { |
||
1574 | + LOG_ERR("Couldn't set the controls for stream %d\n", i); |
||
1575 | + ret = -1; |
||
1576 | + } |
||
1577 | + else LOG_DBG(" Controls set for stream %d\n", i); |
||
1578 | + } |
||
1579 | + } |
||
1580 | + LOG_DBG(" .. OUT ret=%d\n", ret); |
||
1581 | + return ret; |
||
1582 | +} |
||
1583 | + |
||
1584 | +int bcm2835_audio_set_params(bcm2835_alsa_stream_t * alsa_stream, |
||
1585 | + uint32_t channels, uint32_t samplerate, |
||
1586 | + uint32_t bps) |
||
1587 | +{ |
||
1588 | + VC_AUDIO_MSG_T m; |
||
1589 | + AUDIO_INSTANCE_T *instance = alsa_stream->instance; |
||
1590 | + int32_t success; |
||
1591 | + int ret; |
||
1592 | + LOG_DBG(" .. IN\n"); |
||
1593 | + |
||
1594 | + LOG_INFO |
||
1595 | + (" Setting ALSA channels(%d), samplerate(%d), bits-per-sample(%d)\n", |
||
1596 | + channels, samplerate, bps); |
||
1597 | + |
||
1598 | + /* resend ctls - alsa_stream may not have been open when first send */ |
||
1599 | + ret = bcm2835_audio_set_ctls_chan(alsa_stream, alsa_stream->chip); |
||
1600 | + if (ret != 0) { |
||
1601 | + LOG_ERR(" Alsa controls not supported\n"); |
||
1602 | + return -EINVAL; |
||
1603 | + } |
||
1604 | + |
||
1605 | + if(mutex_lock_interruptible(&instance->vchi_mutex)) |
||
1606 | + { |
||
1607 | + LOG_DBG("Interrupted whilst waiting for lock on (%d)\n",instance->num_connections); |
||
1608 | + return -EINTR; |
||
1609 | + } |
||
1610 | + vchi_service_use(instance->vchi_handle[0]); |
||
1611 | + |
||
1612 | + instance->result = -1; |
||
1613 | + |
||
1614 | + m.type = VC_AUDIO_MSG_TYPE_CONFIG; |
||
1615 | + m.u.config.channels = channels; |
||
1616 | + m.u.config.samplerate = samplerate; |
||
1617 | + m.u.config.bps = bps; |
||
1618 | + |
||
1619 | + /* Create the message available completion */ |
||
1620 | + init_completion(&instance->msg_avail_comp); |
||
1621 | + |
||
1622 | + /* Send the message to the videocore */ |
||
1623 | + success = vchi_msg_queue(instance->vchi_handle[0], |
||
1624 | + &m, sizeof m, |
||
1625 | + VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); |
||
1626 | + |
||
1627 | + if (success != 0) { |
||
1628 | + LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", |
||
1629 | + __func__, success); |
||
1630 | + |
||
1631 | + ret = -1; |
||
1632 | + goto unlock; |
||
1633 | + } |
||
1634 | + |
||
1635 | + /* We are expecting a reply from the videocore */ |
||
1636 | + wait_for_completion(&instance->msg_avail_comp); |
||
1637 | + |
||
1638 | + if (instance->result != 0) { |
||
1639 | + LOG_ERR("%s: result=%d", __func__, instance->result); |
||
1640 | + |
||
1641 | + ret = -1; |
||
1642 | + goto unlock; |
||
1643 | + } |
||
1644 | + |
||
1645 | + ret = 0; |
||
1646 | + |
||
1647 | +unlock: |
||
1648 | + vchi_service_release(instance->vchi_handle[0]); |
||
1649 | + mutex_unlock(&instance->vchi_mutex); |
||
1650 | + |
||
1651 | + LOG_DBG(" .. OUT\n"); |
||
1652 | + return ret; |
||
1653 | +} |
||
1654 | + |
||
1655 | +int bcm2835_audio_setup(bcm2835_alsa_stream_t * alsa_stream) |
||
1656 | +{ |
||
1657 | + LOG_DBG(" .. IN\n"); |
||
1658 | + |
||
1659 | + LOG_DBG(" .. OUT\n"); |
||
1660 | + |
||
1661 | + return 0; |
||
1662 | +} |
||
1663 | + |
||
1664 | +static int bcm2835_audio_start_worker(bcm2835_alsa_stream_t * alsa_stream) |
||
1665 | +{ |
||
1666 | + VC_AUDIO_MSG_T m; |
||
1667 | + AUDIO_INSTANCE_T *instance = alsa_stream->instance; |
||
1668 | + int32_t success; |
||
1669 | + int ret; |
||
1670 | + LOG_DBG(" .. IN\n"); |
||
1671 | + |
||
1672 | + if(mutex_lock_interruptible(&instance->vchi_mutex)) |
||
1673 | + { |
||
1674 | + LOG_DBG("Interrupted whilst waiting for lock on (%d)\n",instance->num_connections); |
||
1675 | + return -EINTR; |
||
1676 | + } |
||
1677 | + vchi_service_use(instance->vchi_handle[0]); |
||
1678 | + |
||
1679 | + m.type = VC_AUDIO_MSG_TYPE_START; |
||
1680 | + |
||
1681 | + /* Send the message to the videocore */ |
||
1682 | + success = vchi_msg_queue(instance->vchi_handle[0], |
||
1683 | + &m, sizeof m, |
||
1684 | + VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); |
||
1685 | + |
||
1686 | + if (success != 0) { |
||
1687 | + LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", |
||
1688 | + __func__, success); |
||
1689 | + |
||
1690 | + ret = -1; |
||
1691 | + goto unlock; |
||
1692 | + } |
||
1693 | + |
||
1694 | + ret = 0; |
||
1695 | + |
||
1696 | +unlock: |
||
1697 | + vchi_service_release(instance->vchi_handle[0]); |
||
1698 | + mutex_unlock(&instance->vchi_mutex); |
||
1699 | + LOG_DBG(" .. OUT\n"); |
||
1700 | + return ret; |
||
1701 | +} |
||
1702 | + |
||
1703 | +static int bcm2835_audio_stop_worker(bcm2835_alsa_stream_t * alsa_stream) |
||
1704 | +{ |
||
1705 | + VC_AUDIO_MSG_T m; |
||
1706 | + AUDIO_INSTANCE_T *instance = alsa_stream->instance; |
||
1707 | + int32_t success; |
||
1708 | + int ret; |
||
1709 | + LOG_DBG(" .. IN\n"); |
||
1710 | + |
||
1711 | + if(mutex_lock_interruptible(&instance->vchi_mutex)) |
||
1712 | + { |
||
1713 | + LOG_DBG("Interrupted whilst waiting for lock on (%d)\n",instance->num_connections); |
||
1714 | + return -EINTR; |
||
1715 | + } |
||
1716 | + vchi_service_use(instance->vchi_handle[0]); |
||
1717 | + |
||
1718 | + m.type = VC_AUDIO_MSG_TYPE_STOP; |
||
1719 | + m.u.stop.draining = alsa_stream->draining; |
||
1720 | + |
||
1721 | + /* Send the message to the videocore */ |
||
1722 | + success = vchi_msg_queue(instance->vchi_handle[0], |
||
1723 | + &m, sizeof m, |
||
1724 | + VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); |
||
1725 | + |
||
1726 | + if (success != 0) { |
||
1727 | + LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", |
||
1728 | + __func__, success); |
||
1729 | + |
||
1730 | + ret = -1; |
||
1731 | + goto unlock; |
||
1732 | + } |
||
1733 | + |
||
1734 | + ret = 0; |
||
1735 | + |
||
1736 | +unlock: |
||
1737 | + vchi_service_release(instance->vchi_handle[0]); |
||
1738 | + mutex_unlock(&instance->vchi_mutex); |
||
1739 | + LOG_DBG(" .. OUT\n"); |
||
1740 | + return ret; |
||
1741 | +} |
||
1742 | + |
||
1743 | +int bcm2835_audio_close(bcm2835_alsa_stream_t * alsa_stream) |
||
1744 | +{ |
||
1745 | + VC_AUDIO_MSG_T m; |
||
1746 | + AUDIO_INSTANCE_T *instance = alsa_stream->instance; |
||
1747 | + int32_t success; |
||
1748 | + int ret; |
||
1749 | + LOG_DBG(" .. IN\n"); |
||
1750 | + |
||
1751 | + my_workqueue_quit(alsa_stream); |
||
1752 | + |
||
1753 | + if(mutex_lock_interruptible(&instance->vchi_mutex)) |
||
1754 | + { |
||
1755 | + LOG_DBG("Interrupted whilst waiting for lock on (%d)\n",instance->num_connections); |
||
1756 | + return -EINTR; |
||
1757 | + } |
||
1758 | + vchi_service_use(instance->vchi_handle[0]); |
||
1759 | + |
||
1760 | + m.type = VC_AUDIO_MSG_TYPE_CLOSE; |
||
1761 | + |
||
1762 | + /* Create the message available completion */ |
||
1763 | + init_completion(&instance->msg_avail_comp); |
||
1764 | + |
||
1765 | + /* Send the message to the videocore */ |
||
1766 | + success = vchi_msg_queue(instance->vchi_handle[0], |
||
1767 | + &m, sizeof m, |
||
1768 | + VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); |
||
1769 | + |
||
1770 | + if (success != 0) { |
||
1771 | + LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", |
||
1772 | + __func__, success); |
||
1773 | + ret = -1; |
||
1774 | + goto unlock; |
||
1775 | + } |
||
1776 | + |
||
1777 | + /* We are expecting a reply from the videocore */ |
||
1778 | + wait_for_completion(&instance->msg_avail_comp); |
||
1779 | + |
||
1780 | + if (instance->result != 0) { |
||
1781 | + LOG_ERR("%s: failed result (result=%d)\n", |
||
1782 | + __func__, instance->result); |
||
1783 | + |
||
1784 | + ret = -1; |
||
1785 | + goto unlock; |
||
1786 | + } |
||
1787 | + |
||
1788 | + ret = 0; |
||
1789 | + |
||
1790 | +unlock: |
||
1791 | + vchi_service_release(instance->vchi_handle[0]); |
||
1792 | + mutex_unlock(&instance->vchi_mutex); |
||
1793 | + |
||
1794 | + /* Stop the audio service */ |
||
1795 | + if (instance) { |
||
1796 | + vc_vchi_audio_deinit(instance); |
||
1797 | + alsa_stream->instance = NULL; |
||
1798 | + } |
||
1799 | + LOG_DBG(" .. OUT\n"); |
||
1800 | + return ret; |
||
1801 | +} |
||
1802 | + |
||
1803 | +int bcm2835_audio_write_worker(bcm2835_alsa_stream_t *alsa_stream, |
||
1804 | + uint32_t count, void *src) |
||
1805 | +{ |
||
1806 | + VC_AUDIO_MSG_T m; |
||
1807 | + AUDIO_INSTANCE_T *instance = alsa_stream->instance; |
||
1808 | + int32_t success; |
||
1809 | + int ret; |
||
1810 | + |
||
1811 | + LOG_DBG(" .. IN\n"); |
||
1812 | + |
||
1813 | + LOG_INFO(" Writing %d bytes from %p\n", count, src); |
||
1814 | + |
||
1815 | + if(mutex_lock_interruptible(&instance->vchi_mutex)) |
||
1816 | + { |
||
1817 | + LOG_DBG("Interrupted whilst waiting for lock on (%d)\n",instance->num_connections); |
||
1818 | + return -EINTR; |
||
1819 | + } |
||
1820 | + vchi_service_use(instance->vchi_handle[0]); |
||
1821 | + |
||
1822 | + if ( instance->peer_version==0 && vchi_get_peer_version(instance->vchi_handle[0], &instance->peer_version) == 0 ) { |
||
1823 | + LOG_DBG("%s: client version %d connected\n", __func__, instance->peer_version); |
||
1824 | + } |
||
1825 | + m.type = VC_AUDIO_MSG_TYPE_WRITE; |
||
1826 | + m.u.write.count = count; |
||
1827 | + // old version uses bulk, new version uses control |
||
1828 | + m.u.write.max_packet = instance->peer_version < 2 || force_bulk ? 0:4000; |
||
1829 | + m.u.write.callback = alsa_stream->fifo_irq_handler; |
||
1830 | + m.u.write.cookie = alsa_stream; |
||
1831 | + m.u.write.silence = src == NULL; |
||
1832 | + |
||
1833 | + /* Send the message to the videocore */ |
||
1834 | + success = vchi_msg_queue(instance->vchi_handle[0], |
||
1835 | + &m, sizeof m, |
||
1836 | + VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); |
||
1837 | + |
||
1838 | + if (success != 0) { |
||
1839 | + LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n", |
||
1840 | + __func__, success); |
||
1841 | + |
||
1842 | + ret = -1; |
||
1843 | + goto unlock; |
||
1844 | + } |
||
1845 | + if (!m.u.write.silence) { |
||
1846 | + if (m.u.write.max_packet == 0) { |
||
1847 | + /* Send the message to the videocore */ |
||
1848 | + success = vchi_bulk_queue_transmit(instance->vchi_handle[0], |
||
1849 | + src, count, |
||
1850 | + 0 * |
||
1851 | + VCHI_FLAGS_BLOCK_UNTIL_QUEUED |
||
1852 | + + |
||
1853 | + 1 * |
||
1854 | + VCHI_FLAGS_BLOCK_UNTIL_DATA_READ, |
||
1855 | + NULL); |
||
1856 | + } else { |
||
1857 | + while (count > 0) { |
||
1858 | + int bytes = min((int)m.u.write.max_packet, (int)count); |
||
1859 | + success = vchi_msg_queue(instance->vchi_handle[0], |
||
1860 | + src, bytes, |
||
1861 | + VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); |
||
1862 | + src = (char *)src + bytes; |
||
1863 | + count -= bytes; |
||
1864 | + } |
||
1865 | + } |
||
1866 | + if (success != 0) { |
||
1867 | + LOG_ERR |
||
1868 | + ("%s: failed on vchi_bulk_queue_transmit (status=%d)\n", |
||
1869 | + __func__, success); |
||
1870 | + |
||
1871 | + ret = -1; |
||
1872 | + goto unlock; |
||
1873 | + } |
||
1874 | + } |
||
1875 | + ret = 0; |
||
1876 | + |
||
1877 | +unlock: |
||
1878 | + vchi_service_release(instance->vchi_handle[0]); |
||
1879 | + mutex_unlock(&instance->vchi_mutex); |
||
1880 | + LOG_DBG(" .. OUT\n"); |
||
1881 | + return ret; |
||
1882 | +} |
||
1883 | + |
||
1884 | +/** |
||
1885 | + * Returns all buffers from arm->vc |
||
1886 | + */ |
||
1887 | +void bcm2835_audio_flush_buffers(bcm2835_alsa_stream_t * alsa_stream) |
||
1888 | +{ |
||
1889 | + LOG_DBG(" .. IN\n"); |
||
1890 | + LOG_DBG(" .. OUT\n"); |
||
1891 | + return; |
||
1892 | +} |
||
1893 | + |
||
1894 | +/** |
||
1895 | + * Forces VC to flush(drop) its filled playback buffers and |
||
1896 | + * return them the us. (VC->ARM) |
||
1897 | + */ |
||
1898 | +void bcm2835_audio_flush_playback_buffers(bcm2835_alsa_stream_t * alsa_stream) |
||
1899 | +{ |
||
1900 | + LOG_DBG(" .. IN\n"); |
||
1901 | + LOG_DBG(" .. OUT\n"); |
||
1902 | +} |
||
1903 | + |
||
1904 | +uint32_t bcm2835_audio_retrieve_buffers(bcm2835_alsa_stream_t * alsa_stream) |
||
1905 | +{ |
||
1906 | + uint32_t count = atomic_read(&alsa_stream->retrieved); |
||
1907 | + atomic_sub(count, &alsa_stream->retrieved); |
||
1908 | + return count; |
||
1909 | +} |
||
1910 | + |
||
1911 | +module_param(force_bulk, bool, 0444); |
||
1912 | +MODULE_PARM_DESC(force_bulk, "Force use of vchiq bulk for audio"); |
||
1913 | --- /dev/null |
||
1914 | +++ b/sound/arm/bcm2835.c |
||
1915 | @@ -0,0 +1,511 @@ |
||
1916 | +/***************************************************************************** |
||
1917 | +* Copyright 2011 Broadcom Corporation. All rights reserved. |
||
1918 | +* |
||
1919 | +* Unless you and Broadcom execute a separate written software license |
||
1920 | +* agreement governing use of this software, this software is licensed to you |
||
1921 | +* under the terms of the GNU General Public License version 2, available at |
||
1922 | +* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). |
||
1923 | +* |
||
1924 | +* Notwithstanding the above, under no circumstances may you combine this |
||
1925 | +* software in any way with any other Broadcom software provided under a |
||
1926 | +* license other than the GPL, without Broadcom's express prior written |
||
1927 | +* consent. |
||
1928 | +*****************************************************************************/ |
||
1929 | + |
||
1930 | +#include <linux/platform_device.h> |
||
1931 | + |
||
1932 | +#include <linux/init.h> |
||
1933 | +#include <linux/slab.h> |
||
1934 | +#include <linux/module.h> |
||
1935 | +#include <linux/of.h> |
||
1936 | + |
||
1937 | +#include "bcm2835.h" |
||
1938 | + |
||
1939 | +/* module parameters (see "Module Parameters") */ |
||
1940 | +/* SNDRV_CARDS: maximum number of cards supported by this module */ |
||
1941 | +static int index[MAX_SUBSTREAMS] = {[0 ... (MAX_SUBSTREAMS - 1)] = -1 }; |
||
1942 | +static char *id[MAX_SUBSTREAMS] = {[0 ... (MAX_SUBSTREAMS - 1)] = NULL }; |
||
1943 | +static int enable[MAX_SUBSTREAMS] = {[0 ... (MAX_SUBSTREAMS - 1)] = 1 }; |
||
1944 | + |
||
1945 | +/* HACKY global pointers needed for successive probes to work : ssp |
||
1946 | + * But compared against the changes we will have to do in VC audio_ipc code |
||
1947 | + * to export 8 audio_ipc devices as a single IPC device and then monitor all |
||
1948 | + * four devices in a thread, this gets things done quickly and should be easier |
||
1949 | + * to debug if we run into issues |
||
1950 | + */ |
||
1951 | + |
||
1952 | +static struct snd_card *g_card = NULL; |
||
1953 | +static bcm2835_chip_t *g_chip = NULL; |
||
1954 | + |
||
1955 | +static int snd_bcm2835_free(bcm2835_chip_t * chip) |
||
1956 | +{ |
||
1957 | + kfree(chip); |
||
1958 | + return 0; |
||
1959 | +} |
||
1960 | + |
||
1961 | +/* component-destructor |
||
1962 | + * (see "Management of Cards and Components") |
||
1963 | + */ |
||
1964 | +static int snd_bcm2835_dev_free(struct snd_device *device) |
||
1965 | +{ |
||
1966 | + return snd_bcm2835_free(device->device_data); |
||
1967 | +} |
||
1968 | + |
||
1969 | +/* chip-specific constructor |
||
1970 | + * (see "Management of Cards and Components") |
||
1971 | + */ |
||
1972 | +static int snd_bcm2835_create(struct snd_card *card, |
||
1973 | + struct platform_device *pdev, |
||
1974 | + bcm2835_chip_t ** rchip) |
||
1975 | +{ |
||
1976 | + bcm2835_chip_t *chip; |
||
1977 | + int err; |
||
1978 | + static struct snd_device_ops ops = { |
||
1979 | + .dev_free = snd_bcm2835_dev_free, |
||
1980 | + }; |
||
1981 | + |
||
1982 | + *rchip = NULL; |
||
1983 | + |
||
1984 | + chip = kzalloc(sizeof(*chip), GFP_KERNEL); |
||
1985 | + if (chip == NULL) |
||
1986 | + return -ENOMEM; |
||
1987 | + |
||
1988 | + chip->card = card; |
||
1989 | + |
||
1990 | + err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); |
||
1991 | + if (err < 0) { |
||
1992 | + snd_bcm2835_free(chip); |
||
1993 | + return err; |
||
1994 | + } |
||
1995 | + |
||
1996 | + *rchip = chip; |
||
1997 | + return 0; |
||
1998 | +} |
||
1999 | + |
||
2000 | +static int snd_bcm2835_alsa_probe_dt(struct platform_device *pdev) |
||
2001 | +{ |
||
2002 | + struct device *dev = &pdev->dev; |
||
2003 | + bcm2835_chip_t *chip; |
||
2004 | + struct snd_card *card; |
||
2005 | + u32 numchans; |
||
2006 | + int err, i; |
||
2007 | + |
||
2008 | + err = of_property_read_u32(dev->of_node, "brcm,pwm-channels", |
||
2009 | + &numchans); |
||
2010 | + if (err) { |
||
2011 | + dev_err(dev, "Failed to get DT property 'brcm,pwm-channels'"); |
||
2012 | + return err; |
||
2013 | + } |
||
2014 | + |
||
2015 | + if (numchans == 0 || numchans > MAX_SUBSTREAMS) { |
||
2016 | + numchans = MAX_SUBSTREAMS; |
||
2017 | + dev_warn(dev, "Illegal 'brcm,pwm-channels' value, will use %u\n", |
||
2018 | + numchans); |
||
2019 | + } |
||
2020 | + |
||
2021 | + err = snd_card_new(&pdev->dev, -1, NULL, THIS_MODULE, 0, &card); |
||
2022 | + if (err) { |
||
2023 | + dev_err(dev, "Failed to create soundcard structure\n"); |
||
2024 | + return err; |
||
2025 | + } |
||
2026 | + |
||
2027 | + snd_card_set_dev(card, dev); |
||
2028 | + strcpy(card->driver, "bcm2835"); |
||
2029 | + strcpy(card->shortname, "bcm2835 ALSA"); |
||
2030 | + sprintf(card->longname, "%s", card->shortname); |
||
2031 | + |
||
2032 | + err = snd_bcm2835_create(card, pdev, &chip); |
||
2033 | + if (err < 0) { |
||
2034 | + dev_err(dev, "Failed to create bcm2835 chip\n"); |
||
2035 | + goto err_free; |
||
2036 | + } |
||
2037 | + |
||
2038 | + err = snd_bcm2835_new_pcm(chip); |
||
2039 | + if (err < 0) { |
||
2040 | + dev_err(dev, "Failed to create new bcm2835 pcm device\n"); |
||
2041 | + goto err_free; |
||
2042 | + } |
||
2043 | + |
||
2044 | + err = snd_bcm2835_new_spdif_pcm(chip); |
||
2045 | + if (err < 0) { |
||
2046 | + dev_err(dev, "Failed to create new bcm2835 spdif pcm device\n"); |
||
2047 | + goto err_free; |
||
2048 | + } |
||
2049 | + |
||
2050 | + err = snd_bcm2835_new_ctl(chip); |
||
2051 | + if (err < 0) { |
||
2052 | + dev_err(dev, "Failed to create new bcm2835 ctl\n"); |
||
2053 | + goto err_free; |
||
2054 | + } |
||
2055 | + |
||
2056 | + for (i = 0; i < numchans; i++) { |
||
2057 | + chip->avail_substreams |= (1 << i); |
||
2058 | + chip->pdev[i] = pdev; |
||
2059 | + } |
||
2060 | + |
||
2061 | + err = snd_card_register(card); |
||
2062 | + if (err) { |
||
2063 | + dev_err(dev, "Failed to register bcm2835 ALSA card \n"); |
||
2064 | + goto err_free; |
||
2065 | + } |
||
2066 | + |
||
2067 | + g_card = card; |
||
2068 | + g_chip = chip; |
||
2069 | + platform_set_drvdata(pdev, card); |
||
2070 | + audio_info("bcm2835 ALSA card created with %u channels\n", numchans); |
||
2071 | + |
||
2072 | + return 0; |
||
2073 | + |
||
2074 | +err_free: |
||
2075 | + snd_card_free(card); |
||
2076 | + |
||
2077 | + return err; |
||
2078 | +} |
||
2079 | + |
||
2080 | +static int snd_bcm2835_alsa_probe(struct platform_device *pdev) |
||
2081 | +{ |
||
2082 | + static int dev; |
||
2083 | + bcm2835_chip_t *chip; |
||
2084 | + struct snd_card *card; |
||
2085 | + int err; |
||
2086 | + |
||
2087 | + if (pdev->dev.of_node) |
||
2088 | + return snd_bcm2835_alsa_probe_dt(pdev); |
||
2089 | + |
||
2090 | + if (dev >= MAX_SUBSTREAMS) |
||
2091 | + return -ENODEV; |
||
2092 | + |
||
2093 | + if (!enable[dev]) { |
||
2094 | + dev++; |
||
2095 | + return -ENOENT; |
||
2096 | + } |
||
2097 | + |
||
2098 | + if (dev > 0) |
||
2099 | + goto add_register_map; |
||
2100 | + |
||
2101 | + err = snd_card_new(&pdev->dev, index[dev], id[dev], THIS_MODULE, 0, &g_card); |
||
2102 | + if (err < 0) |
||
2103 | + goto out; |
||
2104 | + |
||
2105 | + snd_card_set_dev(g_card, &pdev->dev); |
||
2106 | + strcpy(g_card->driver, "bcm2835"); |
||
2107 | + strcpy(g_card->shortname, "bcm2835 ALSA"); |
||
2108 | + sprintf(g_card->longname, "%s", g_card->shortname); |
||
2109 | + |
||
2110 | + err = snd_bcm2835_create(g_card, pdev, &chip); |
||
2111 | + if (err < 0) { |
||
2112 | + dev_err(&pdev->dev, "Failed to create bcm2835 chip\n"); |
||
2113 | + goto out_bcm2835_create; |
||
2114 | + } |
||
2115 | + |
||
2116 | + g_chip = chip; |
||
2117 | + err = snd_bcm2835_new_pcm(chip); |
||
2118 | + if (err < 0) { |
||
2119 | + dev_err(&pdev->dev, "Failed to create new BCM2835 pcm device\n"); |
||
2120 | + goto out_bcm2835_new_pcm; |
||
2121 | + } |
||
2122 | + |
||
2123 | + err = snd_bcm2835_new_spdif_pcm(chip); |
||
2124 | + if (err < 0) { |
||
2125 | + dev_err(&pdev->dev, "Failed to create new BCM2835 spdif pcm device\n"); |
||
2126 | + goto out_bcm2835_new_spdif; |
||
2127 | + } |
||
2128 | + |
||
2129 | + err = snd_bcm2835_new_ctl(chip); |
||
2130 | + if (err < 0) { |
||
2131 | + dev_err(&pdev->dev, "Failed to create new BCM2835 ctl\n"); |
||
2132 | + goto out_bcm2835_new_ctl; |
||
2133 | + } |
||
2134 | + |
||
2135 | +add_register_map: |
||
2136 | + card = g_card; |
||
2137 | + chip = g_chip; |
||
2138 | + |
||
2139 | + BUG_ON(!(card && chip)); |
||
2140 | + |
||
2141 | + chip->avail_substreams |= (1 << dev); |
||
2142 | + chip->pdev[dev] = pdev; |
||
2143 | + |
||
2144 | + if (dev == 0) { |
||
2145 | + err = snd_card_register(card); |
||
2146 | + if (err < 0) { |
||
2147 | + dev_err(&pdev->dev, |
||
2148 | + "Failed to register bcm2835 ALSA card \n"); |
||
2149 | + goto out_card_register; |
||
2150 | + } |
||
2151 | + platform_set_drvdata(pdev, card); |
||
2152 | + audio_info("bcm2835 ALSA card created!\n"); |
||
2153 | + } else { |
||
2154 | + audio_info("bcm2835 ALSA chip created!\n"); |
||
2155 | + platform_set_drvdata(pdev, (void *)dev); |
||
2156 | + } |
||
2157 | + |
||
2158 | + dev++; |
||
2159 | + |
||
2160 | + return 0; |
||
2161 | + |
||
2162 | +out_card_register: |
||
2163 | +out_bcm2835_new_ctl: |
||
2164 | +out_bcm2835_new_spdif: |
||
2165 | +out_bcm2835_new_pcm: |
||
2166 | +out_bcm2835_create: |
||
2167 | + BUG_ON(!g_card); |
||
2168 | + if (snd_card_free(g_card)) |
||
2169 | + dev_err(&pdev->dev, "Failed to free Registered alsa card\n"); |
||
2170 | + g_card = NULL; |
||
2171 | +out: |
||
2172 | + dev = SNDRV_CARDS; /* stop more avail_substreams from being probed */ |
||
2173 | + dev_err(&pdev->dev, "BCM2835 ALSA Probe failed !!\n"); |
||
2174 | + return err; |
||
2175 | +} |
||
2176 | + |
||
2177 | +static int snd_bcm2835_alsa_remove(struct platform_device *pdev) |
||
2178 | +{ |
||
2179 | + uint32_t idx; |
||
2180 | + void *drv_data; |
||
2181 | + |
||
2182 | + drv_data = platform_get_drvdata(pdev); |
||
2183 | + |
||
2184 | + if (drv_data == (void *)g_card) { |
||
2185 | + /* This is the card device */ |
||
2186 | + snd_card_free((struct snd_card *)drv_data); |
||
2187 | + g_card = NULL; |
||
2188 | + g_chip = NULL; |
||
2189 | + } else { |
||
2190 | + idx = (uint32_t) drv_data; |
||
2191 | + if (g_card != NULL) { |
||
2192 | + BUG_ON(!g_chip); |
||
2193 | + /* We pass chip device numbers in audio ipc devices |
||
2194 | + * other than the one we registered our card with |
||
2195 | + */ |
||
2196 | + idx = (uint32_t) drv_data; |
||
2197 | + BUG_ON(!idx || idx > MAX_SUBSTREAMS); |
||
2198 | + g_chip->avail_substreams &= ~(1 << idx); |
||
2199 | + /* There should be atleast one substream registered |
||
2200 | + * after we are done here, as it wil be removed when |
||
2201 | + * the *remove* is called for the card device |
||
2202 | + */ |
||
2203 | + BUG_ON(!g_chip->avail_substreams); |
||
2204 | + } |
||
2205 | + } |
||
2206 | + |
||
2207 | + platform_set_drvdata(pdev, NULL); |
||
2208 | + |
||
2209 | + return 0; |
||
2210 | +} |
||
2211 | + |
||
2212 | +#ifdef CONFIG_PM |
||
2213 | +static int snd_bcm2835_alsa_suspend(struct platform_device *pdev, |
||
2214 | + pm_message_t state) |
||
2215 | +{ |
||
2216 | + return 0; |
||
2217 | +} |
||
2218 | + |
||
2219 | +static int snd_bcm2835_alsa_resume(struct platform_device *pdev) |
||
2220 | +{ |
||
2221 | + return 0; |
||
2222 | +} |
||
2223 | + |
||
2224 | +#endif |
||
2225 | + |
||
2226 | +static const struct of_device_id snd_bcm2835_of_match_table[] = { |
||
2227 | + { .compatible = "brcm,bcm2835-audio", }, |
||
2228 | + {}, |
||
2229 | +}; |
||
2230 | +MODULE_DEVICE_TABLE(of, snd_bcm2835_of_match_table); |
||
2231 | + |
||
2232 | +static struct platform_driver bcm2835_alsa0_driver = { |
||
2233 | + .probe = snd_bcm2835_alsa_probe, |
||
2234 | + .remove = snd_bcm2835_alsa_remove, |
||
2235 | +#ifdef CONFIG_PM |
||
2236 | + .suspend = snd_bcm2835_alsa_suspend, |
||
2237 | + .resume = snd_bcm2835_alsa_resume, |
||
2238 | +#endif |
||
2239 | + .driver = { |
||
2240 | + .name = "bcm2835_AUD0", |
||
2241 | + .owner = THIS_MODULE, |
||
2242 | + .of_match_table = snd_bcm2835_of_match_table, |
||
2243 | + }, |
||
2244 | +}; |
||
2245 | + |
||
2246 | +static struct platform_driver bcm2835_alsa1_driver = { |
||
2247 | + .probe = snd_bcm2835_alsa_probe, |
||
2248 | + .remove = snd_bcm2835_alsa_remove, |
||
2249 | +#ifdef CONFIG_PM |
||
2250 | + .suspend = snd_bcm2835_alsa_suspend, |
||
2251 | + .resume = snd_bcm2835_alsa_resume, |
||
2252 | +#endif |
||
2253 | + .driver = { |
||
2254 | + .name = "bcm2835_AUD1", |
||
2255 | + .owner = THIS_MODULE, |
||
2256 | + }, |
||
2257 | +}; |
||
2258 | + |
||
2259 | +static struct platform_driver bcm2835_alsa2_driver = { |
||
2260 | + .probe = snd_bcm2835_alsa_probe, |
||
2261 | + .remove = snd_bcm2835_alsa_remove, |
||
2262 | +#ifdef CONFIG_PM |
||
2263 | + .suspend = snd_bcm2835_alsa_suspend, |
||
2264 | + .resume = snd_bcm2835_alsa_resume, |
||
2265 | +#endif |
||
2266 | + .driver = { |
||
2267 | + .name = "bcm2835_AUD2", |
||
2268 | + .owner = THIS_MODULE, |
||
2269 | + }, |
||
2270 | +}; |
||
2271 | + |
||
2272 | +static struct platform_driver bcm2835_alsa3_driver = { |
||
2273 | + .probe = snd_bcm2835_alsa_probe, |
||
2274 | + .remove = snd_bcm2835_alsa_remove, |
||
2275 | +#ifdef CONFIG_PM |
||
2276 | + .suspend = snd_bcm2835_alsa_suspend, |
||
2277 | + .resume = snd_bcm2835_alsa_resume, |
||
2278 | +#endif |
||
2279 | + .driver = { |
||
2280 | + .name = "bcm2835_AUD3", |
||
2281 | + .owner = THIS_MODULE, |
||
2282 | + }, |
||
2283 | +}; |
||
2284 | + |
||
2285 | +static struct platform_driver bcm2835_alsa4_driver = { |
||
2286 | + .probe = snd_bcm2835_alsa_probe, |
||
2287 | + .remove = snd_bcm2835_alsa_remove, |
||
2288 | +#ifdef CONFIG_PM |
||
2289 | + .suspend = snd_bcm2835_alsa_suspend, |
||
2290 | + .resume = snd_bcm2835_alsa_resume, |
||
2291 | +#endif |
||
2292 | + .driver = { |
||
2293 | + .name = "bcm2835_AUD4", |
||
2294 | + .owner = THIS_MODULE, |
||
2295 | + }, |
||
2296 | +}; |
||
2297 | + |
||
2298 | +static struct platform_driver bcm2835_alsa5_driver = { |
||
2299 | + .probe = snd_bcm2835_alsa_probe, |
||
2300 | + .remove = snd_bcm2835_alsa_remove, |
||
2301 | +#ifdef CONFIG_PM |
||
2302 | + .suspend = snd_bcm2835_alsa_suspend, |
||
2303 | + .resume = snd_bcm2835_alsa_resume, |
||
2304 | +#endif |
||
2305 | + .driver = { |
||
2306 | + .name = "bcm2835_AUD5", |
||
2307 | + .owner = THIS_MODULE, |
||
2308 | + }, |
||
2309 | +}; |
||
2310 | + |
||
2311 | +static struct platform_driver bcm2835_alsa6_driver = { |
||
2312 | + .probe = snd_bcm2835_alsa_probe, |
||
2313 | + .remove = snd_bcm2835_alsa_remove, |
||
2314 | +#ifdef CONFIG_PM |
||
2315 | + .suspend = snd_bcm2835_alsa_suspend, |
||
2316 | + .resume = snd_bcm2835_alsa_resume, |
||
2317 | +#endif |
||
2318 | + .driver = { |
||
2319 | + .name = "bcm2835_AUD6", |
||
2320 | + .owner = THIS_MODULE, |
||
2321 | + }, |
||
2322 | +}; |
||
2323 | + |
||
2324 | +static struct platform_driver bcm2835_alsa7_driver = { |
||
2325 | + .probe = snd_bcm2835_alsa_probe, |
||
2326 | + .remove = snd_bcm2835_alsa_remove, |
||
2327 | +#ifdef CONFIG_PM |
||
2328 | + .suspend = snd_bcm2835_alsa_suspend, |
||
2329 | + .resume = snd_bcm2835_alsa_resume, |
||
2330 | +#endif |
||
2331 | + .driver = { |
||
2332 | + .name = "bcm2835_AUD7", |
||
2333 | + .owner = THIS_MODULE, |
||
2334 | + }, |
||
2335 | +}; |
||
2336 | + |
||
2337 | +static int bcm2835_alsa_device_init(void) |
||
2338 | +{ |
||
2339 | + int err; |
||
2340 | + err = platform_driver_register(&bcm2835_alsa0_driver); |
||
2341 | + if (err) { |
||
2342 | + pr_err("Error registering bcm2835_alsa0_driver %d .\n", err); |
||
2343 | + goto out; |
||
2344 | + } |
||
2345 | + |
||
2346 | + err = platform_driver_register(&bcm2835_alsa1_driver); |
||
2347 | + if (err) { |
||
2348 | + pr_err("Error registering bcm2835_alsa0_driver %d .\n", err); |
||
2349 | + goto unregister_0; |
||
2350 | + } |
||
2351 | + |
||
2352 | + err = platform_driver_register(&bcm2835_alsa2_driver); |
||
2353 | + if (err) { |
||
2354 | + pr_err("Error registering bcm2835_alsa0_driver %d .\n", err); |
||
2355 | + goto unregister_1; |
||
2356 | + } |
||
2357 | + |
||
2358 | + err = platform_driver_register(&bcm2835_alsa3_driver); |
||
2359 | + if (err) { |
||
2360 | + pr_err("Error registering bcm2835_alsa0_driver %d .\n", err); |
||
2361 | + goto unregister_2; |
||
2362 | + } |
||
2363 | + |
||
2364 | + err = platform_driver_register(&bcm2835_alsa4_driver); |
||
2365 | + if (err) { |
||
2366 | + pr_err("Error registering bcm2835_alsa0_driver %d .\n", err); |
||
2367 | + goto unregister_3; |
||
2368 | + } |
||
2369 | + |
||
2370 | + err = platform_driver_register(&bcm2835_alsa5_driver); |
||
2371 | + if (err) { |
||
2372 | + pr_err("Error registering bcm2835_alsa0_driver %d .\n", err); |
||
2373 | + goto unregister_4; |
||
2374 | + } |
||
2375 | + |
||
2376 | + err = platform_driver_register(&bcm2835_alsa6_driver); |
||
2377 | + if (err) { |
||
2378 | + pr_err("Error registering bcm2835_alsa0_driver %d .\n", err); |
||
2379 | + goto unregister_5; |
||
2380 | + } |
||
2381 | + |
||
2382 | + err = platform_driver_register(&bcm2835_alsa7_driver); |
||
2383 | + if (err) { |
||
2384 | + pr_err("Error registering bcm2835_alsa0_driver %d .\n", err); |
||
2385 | + goto unregister_6; |
||
2386 | + } |
||
2387 | + |
||
2388 | + return 0; |
||
2389 | + |
||
2390 | +unregister_6: |
||
2391 | + platform_driver_unregister(&bcm2835_alsa6_driver); |
||
2392 | +unregister_5: |
||
2393 | + platform_driver_unregister(&bcm2835_alsa5_driver); |
||
2394 | +unregister_4: |
||
2395 | + platform_driver_unregister(&bcm2835_alsa4_driver); |
||
2396 | +unregister_3: |
||
2397 | + platform_driver_unregister(&bcm2835_alsa3_driver); |
||
2398 | +unregister_2: |
||
2399 | + platform_driver_unregister(&bcm2835_alsa2_driver); |
||
2400 | +unregister_1: |
||
2401 | + platform_driver_unregister(&bcm2835_alsa1_driver); |
||
2402 | +unregister_0: |
||
2403 | + platform_driver_unregister(&bcm2835_alsa0_driver); |
||
2404 | +out: |
||
2405 | + return err; |
||
2406 | +} |
||
2407 | + |
||
2408 | +static void bcm2835_alsa_device_exit(void) |
||
2409 | +{ |
||
2410 | + platform_driver_unregister(&bcm2835_alsa0_driver); |
||
2411 | + platform_driver_unregister(&bcm2835_alsa1_driver); |
||
2412 | + platform_driver_unregister(&bcm2835_alsa2_driver); |
||
2413 | + platform_driver_unregister(&bcm2835_alsa3_driver); |
||
2414 | + platform_driver_unregister(&bcm2835_alsa4_driver); |
||
2415 | + platform_driver_unregister(&bcm2835_alsa5_driver); |
||
2416 | + platform_driver_unregister(&bcm2835_alsa6_driver); |
||
2417 | + platform_driver_unregister(&bcm2835_alsa7_driver); |
||
2418 | +} |
||
2419 | + |
||
2420 | +late_initcall(bcm2835_alsa_device_init); |
||
2421 | +module_exit(bcm2835_alsa_device_exit); |
||
2422 | + |
||
2423 | +MODULE_AUTHOR("Dom Cobley"); |
||
2424 | +MODULE_DESCRIPTION("Alsa driver for BCM2835 chip"); |
||
2425 | +MODULE_LICENSE("GPL"); |
||
2426 | +MODULE_ALIAS("platform:bcm2835_alsa"); |
||
2427 | --- /dev/null |
||
2428 | +++ b/sound/arm/bcm2835.h |
||
2429 | @@ -0,0 +1,167 @@ |
||
2430 | +/***************************************************************************** |
||
2431 | +* Copyright 2011 Broadcom Corporation. All rights reserved. |
||
2432 | +* |
||
2433 | +* Unless you and Broadcom execute a separate written software license |
||
2434 | +* agreement governing use of this software, this software is licensed to you |
||
2435 | +* under the terms of the GNU General Public License version 2, available at |
||
2436 | +* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). |
||
2437 | +* |
||
2438 | +* Notwithstanding the above, under no circumstances may you combine this |
||
2439 | +* software in any way with any other Broadcom software provided under a |
||
2440 | +* license other than the GPL, without Broadcom's express prior written |
||
2441 | +* consent. |
||
2442 | +*****************************************************************************/ |
||
2443 | + |
||
2444 | +#ifndef __SOUND_ARM_BCM2835_H |
||
2445 | +#define __SOUND_ARM_BCM2835_H |
||
2446 | + |
||
2447 | +#include <linux/device.h> |
||
2448 | +#include <linux/list.h> |
||
2449 | +#include <linux/interrupt.h> |
||
2450 | +#include <linux/wait.h> |
||
2451 | +#include <sound/core.h> |
||
2452 | +#include <sound/initval.h> |
||
2453 | +#include <sound/pcm.h> |
||
2454 | +#include <sound/pcm_params.h> |
||
2455 | +#include <sound/pcm-indirect.h> |
||
2456 | +#include <linux/workqueue.h> |
||
2457 | + |
||
2458 | +/* |
||
2459 | +#define AUDIO_DEBUG_ENABLE |
||
2460 | +#define AUDIO_VERBOSE_DEBUG_ENABLE |
||
2461 | +*/ |
||
2462 | + |
||
2463 | +/* Debug macros */ |
||
2464 | + |
||
2465 | +#ifdef AUDIO_DEBUG_ENABLE |
||
2466 | +#ifdef AUDIO_VERBOSE_DEBUG_ENABLE |
||
2467 | + |
||
2468 | +#define audio_debug(fmt, arg...) \ |
||
2469 | + printk(KERN_INFO"%s:%d " fmt, __func__, __LINE__, ##arg) |
||
2470 | + |
||
2471 | +#define audio_info(fmt, arg...) \ |
||
2472 | + printk(KERN_INFO"%s:%d " fmt, __func__, __LINE__, ##arg) |
||
2473 | + |
||
2474 | +#else |
||
2475 | + |
||
2476 | +#define audio_debug(fmt, arg...) |
||
2477 | + |
||
2478 | +#define audio_info(fmt, arg...) |
||
2479 | + |
||
2480 | +#endif /* AUDIO_VERBOSE_DEBUG_ENABLE */ |
||
2481 | + |
||
2482 | +#else |
||
2483 | + |
||
2484 | +#define audio_debug(fmt, arg...) |
||
2485 | + |
||
2486 | +#define audio_info(fmt, arg...) |
||
2487 | + |
||
2488 | +#endif /* AUDIO_DEBUG_ENABLE */ |
||
2489 | + |
||
2490 | +#define audio_error(fmt, arg...) \ |
||
2491 | + printk(KERN_ERR"%s:%d " fmt, __func__, __LINE__, ##arg) |
||
2492 | + |
||
2493 | +#define audio_warning(fmt, arg...) \ |
||
2494 | + printk(KERN_WARNING"%s:%d " fmt, __func__, __LINE__, ##arg) |
||
2495 | + |
||
2496 | +#define audio_alert(fmt, arg...) \ |
||
2497 | + printk(KERN_ALERT"%s:%d " fmt, __func__, __LINE__, ##arg) |
||
2498 | + |
||
2499 | +#define MAX_SUBSTREAMS (8) |
||
2500 | +#define AVAIL_SUBSTREAMS_MASK (0xff) |
||
2501 | +enum { |
||
2502 | + CTRL_VOL_MUTE, |
||
2503 | + CTRL_VOL_UNMUTE |
||
2504 | +}; |
||
2505 | + |
||
2506 | +/* macros for alsa2chip and chip2alsa, instead of functions */ |
||
2507 | + |
||
2508 | +#define alsa2chip(vol) (uint)(-((vol << 8) / 100)) /* convert alsa to chip volume (defined as macro rather than function call) */ |
||
2509 | +#define chip2alsa(vol) -((vol * 100) >> 8) /* convert chip to alsa volume */ |
||
2510 | + |
||
2511 | +/* Some constants for values .. */ |
||
2512 | +typedef enum { |
||
2513 | + AUDIO_DEST_AUTO = 0, |
||
2514 | + AUDIO_DEST_HEADPHONES = 1, |
||
2515 | + AUDIO_DEST_HDMI = 2, |
||
2516 | + AUDIO_DEST_MAX, |
||
2517 | +} SND_BCM2835_ROUTE_T; |
||
2518 | + |
||
2519 | +typedef enum { |
||
2520 | + PCM_PLAYBACK_VOLUME, |
||
2521 | + PCM_PLAYBACK_MUTE, |
||
2522 | + PCM_PLAYBACK_DEVICE, |
||
2523 | +} SND_BCM2835_CTRL_T; |
||
2524 | + |
||
2525 | +/* definition of the chip-specific record */ |
||
2526 | +typedef struct bcm2835_chip { |
||
2527 | + struct snd_card *card; |
||
2528 | + struct snd_pcm *pcm; |
||
2529 | + struct snd_pcm *pcm_spdif; |
||
2530 | + /* Bitmat for valid reg_base and irq numbers */ |
||
2531 | + uint32_t avail_substreams; |
||
2532 | + struct platform_device *pdev[MAX_SUBSTREAMS]; |
||
2533 | + struct bcm2835_alsa_stream *alsa_stream[MAX_SUBSTREAMS]; |
||
2534 | + |
||
2535 | + int volume; |
||
2536 | + int old_volume; /* stores the volume value whist muted */ |
||
2537 | + int dest; |
||
2538 | + int mute; |
||
2539 | + |
||
2540 | + unsigned int opened; |
||
2541 | + unsigned int spdif_status; |
||
2542 | + struct mutex audio_mutex; |
||
2543 | +} bcm2835_chip_t; |
||
2544 | + |
||
2545 | +typedef struct bcm2835_alsa_stream { |
||
2546 | + bcm2835_chip_t *chip; |
||
2547 | + struct snd_pcm_substream *substream; |
||
2548 | + struct snd_pcm_indirect pcm_indirect; |
||
2549 | + |
||
2550 | + struct semaphore buffers_update_sem; |
||
2551 | + struct semaphore control_sem; |
||
2552 | + spinlock_t lock; |
||
2553 | + volatile uint32_t control; |
||
2554 | + volatile uint32_t status; |
||
2555 | + |
||
2556 | + int open; |
||
2557 | + int running; |
||
2558 | + int draining; |
||
2559 | + |
||
2560 | + int channels; |
||
2561 | + int params_rate; |
||
2562 | + int pcm_format_width; |
||
2563 | + |
||
2564 | + unsigned int pos; |
||
2565 | + unsigned int buffer_size; |
||
2566 | + unsigned int period_size; |
||
2567 | + |
||
2568 | + uint32_t enable_fifo_irq; |
||
2569 | + irq_handler_t fifo_irq_handler; |
||
2570 | + |
||
2571 | + atomic_t retrieved; |
||
2572 | + struct opaque_AUDIO_INSTANCE_T *instance; |
||
2573 | + struct workqueue_struct *my_wq; |
||
2574 | + int idx; |
||
2575 | +} bcm2835_alsa_stream_t; |
||
2576 | + |
||
2577 | +int snd_bcm2835_new_ctl(bcm2835_chip_t * chip); |
||
2578 | +int snd_bcm2835_new_pcm(bcm2835_chip_t * chip); |
||
2579 | +int snd_bcm2835_new_spdif_pcm(bcm2835_chip_t * chip); |
||
2580 | + |
||
2581 | +int bcm2835_audio_open(bcm2835_alsa_stream_t * alsa_stream); |
||
2582 | +int bcm2835_audio_close(bcm2835_alsa_stream_t * alsa_stream); |
||
2583 | +int bcm2835_audio_set_params(bcm2835_alsa_stream_t * alsa_stream, |
||
2584 | + uint32_t channels, uint32_t samplerate, |
||
2585 | + uint32_t bps); |
||
2586 | +int bcm2835_audio_setup(bcm2835_alsa_stream_t * alsa_stream); |
||
2587 | +int bcm2835_audio_start(bcm2835_alsa_stream_t * alsa_stream); |
||
2588 | +int bcm2835_audio_stop(bcm2835_alsa_stream_t * alsa_stream); |
||
2589 | +int bcm2835_audio_set_ctls(bcm2835_chip_t * chip); |
||
2590 | +int bcm2835_audio_write(bcm2835_alsa_stream_t * alsa_stream, uint32_t count, |
||
2591 | + void *src); |
||
2592 | +uint32_t bcm2835_audio_retrieve_buffers(bcm2835_alsa_stream_t * alsa_stream); |
||
2593 | +void bcm2835_audio_flush_buffers(bcm2835_alsa_stream_t * alsa_stream); |
||
2594 | +void bcm2835_audio_flush_playback_buffers(bcm2835_alsa_stream_t * alsa_stream); |
||
2595 | + |
||
2596 | +#endif /* __SOUND_ARM_BCM2835_H */ |
||
2597 | --- /dev/null |
||
2598 | +++ b/sound/arm/vc_vchi_audioserv_defs.h |
||
2599 | @@ -0,0 +1,116 @@ |
||
2600 | +/***************************************************************************** |
||
2601 | +* Copyright 2011 Broadcom Corporation. All rights reserved. |
||
2602 | +* |
||
2603 | +* Unless you and Broadcom execute a separate written software license |
||
2604 | +* agreement governing use of this software, this software is licensed to you |
||
2605 | +* under the terms of the GNU General Public License version 2, available at |
||
2606 | +* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). |
||
2607 | +* |
||
2608 | +* Notwithstanding the above, under no circumstances may you combine this |
||
2609 | +* software in any way with any other Broadcom software provided under a |
||
2610 | +* license other than the GPL, without Broadcom's express prior written |
||
2611 | +* consent. |
||
2612 | +*****************************************************************************/ |
||
2613 | + |
||
2614 | +#ifndef _VC_AUDIO_DEFS_H_ |
||
2615 | +#define _VC_AUDIO_DEFS_H_ |
||
2616 | + |
||
2617 | +#define VC_AUDIOSERV_MIN_VER 1 |
||
2618 | +#define VC_AUDIOSERV_VER 2 |
||
2619 | + |
||
2620 | +// FourCC code used for VCHI connection |
||
2621 | +#define VC_AUDIO_SERVER_NAME MAKE_FOURCC("AUDS") |
||
2622 | + |
||
2623 | +// Maximum message length |
||
2624 | +#define VC_AUDIO_MAX_MSG_LEN (sizeof( VC_AUDIO_MSG_T )) |
||
2625 | + |
||
2626 | +// List of screens that are currently supported |
||
2627 | +// All message types supported for HOST->VC direction |
||
2628 | +typedef enum { |
||
2629 | + VC_AUDIO_MSG_TYPE_RESULT, // Generic result |
||
2630 | + VC_AUDIO_MSG_TYPE_COMPLETE, // Generic result |
||
2631 | + VC_AUDIO_MSG_TYPE_CONFIG, // Configure audio |
||
2632 | + VC_AUDIO_MSG_TYPE_CONTROL, // Configure audio |
||
2633 | + VC_AUDIO_MSG_TYPE_OPEN, // Configure audio |
||
2634 | + VC_AUDIO_MSG_TYPE_CLOSE, // Configure audio |
||
2635 | + VC_AUDIO_MSG_TYPE_START, // Configure audio |
||
2636 | + VC_AUDIO_MSG_TYPE_STOP, // Configure audio |
||
2637 | + VC_AUDIO_MSG_TYPE_WRITE, // Configure audio |
||
2638 | + VC_AUDIO_MSG_TYPE_MAX |
||
2639 | +} VC_AUDIO_MSG_TYPE; |
||
2640 | + |
||
2641 | +// configure the audio |
||
2642 | +typedef struct { |
||
2643 | + uint32_t channels; |
||
2644 | + uint32_t samplerate; |
||
2645 | + uint32_t bps; |
||
2646 | + |
||
2647 | +} VC_AUDIO_CONFIG_T; |
||
2648 | + |
||
2649 | +typedef struct { |
||
2650 | + uint32_t volume; |
||
2651 | + uint32_t dest; |
||
2652 | + |
||
2653 | +} VC_AUDIO_CONTROL_T; |
||
2654 | + |
||
2655 | +// audio |
||
2656 | +typedef struct { |
||
2657 | + uint32_t dummy; |
||
2658 | + |
||
2659 | +} VC_AUDIO_OPEN_T; |
||
2660 | + |
||
2661 | +// audio |
||
2662 | +typedef struct { |
||
2663 | + uint32_t dummy; |
||
2664 | + |
||
2665 | +} VC_AUDIO_CLOSE_T; |
||
2666 | +// audio |
||
2667 | +typedef struct { |
||
2668 | + uint32_t dummy; |
||
2669 | + |
||
2670 | +} VC_AUDIO_START_T; |
||
2671 | +// audio |
||
2672 | +typedef struct { |
||
2673 | + uint32_t draining; |
||
2674 | + |
||
2675 | +} VC_AUDIO_STOP_T; |
||
2676 | + |
||
2677 | +// configure the write audio samples |
||
2678 | +typedef struct { |
||
2679 | + uint32_t count; // in bytes |
||
2680 | + void *callback; |
||
2681 | + void *cookie; |
||
2682 | + uint16_t silence; |
||
2683 | + uint16_t max_packet; |
||
2684 | +} VC_AUDIO_WRITE_T; |
||
2685 | + |
||
2686 | +// Generic result for a request (VC->HOST) |
||
2687 | +typedef struct { |
||
2688 | + int32_t success; // Success value |
||
2689 | + |
||
2690 | +} VC_AUDIO_RESULT_T; |
||
2691 | + |
||
2692 | +// Generic result for a request (VC->HOST) |
||
2693 | +typedef struct { |
||
2694 | + int32_t count; // Success value |
||
2695 | + void *callback; |
||
2696 | + void *cookie; |
||
2697 | +} VC_AUDIO_COMPLETE_T; |
||
2698 | + |
||
2699 | +// Message header for all messages in HOST->VC direction |
||
2700 | +typedef struct { |
||
2701 | + int32_t type; // Message type (VC_AUDIO_MSG_TYPE) |
||
2702 | + union { |
||
2703 | + VC_AUDIO_CONFIG_T config; |
||
2704 | + VC_AUDIO_CONTROL_T control; |
||
2705 | + VC_AUDIO_OPEN_T open; |
||
2706 | + VC_AUDIO_CLOSE_T close; |
||
2707 | + VC_AUDIO_START_T start; |
||
2708 | + VC_AUDIO_STOP_T stop; |
||
2709 | + VC_AUDIO_WRITE_T write; |
||
2710 | + VC_AUDIO_RESULT_T result; |
||
2711 | + VC_AUDIO_COMPLETE_T complete; |
||
2712 | + } u; |
||
2713 | +} VC_AUDIO_MSG_T; |
||
2714 | + |
||
2715 | +#endif // _VC_AUDIO_DEFS_H_ |