Skip to content

Commit 634cd02

Browse files
committed
bcm2708: alsa sound driver
Signed-off-by: popcornmix <[email protected]> alsa: add mmap support and some cleanups to bcm2835 ALSA driver snd-bcm2835: Add support for spdif/hdmi passthrough This adds a dedicated subdevice which can be used for passthrough of non-audio formats (ie encoded a52) through the hdmi audio link. In addition to this driver extension an appropriate card config is required to make alsa-lib support the AES parameters for this device. snd-bcm2708: Add mutex, improve logging Fix for ALSA driver crash Avoids an issue when closing and opening vchiq where a message can arrive before service handle has been written alsa: reduce severity of expected warning message snd-bcm2708: Fix dmesg spam for non-error case alsa: Ensure mutexes are released through error paths alsa: Make interrupted close paths quieter BCM270x: Add onboard sound device to Device Tree Add Device Tree support to alsa driver. Add device to Device Tree. Don't add platform devices when booting in DT mode. Signed-off-by: Noralf Trønnes <[email protected]>
1 parent b7974fe commit 634cd02

File tree

8 files changed

+2589
-0
lines changed

8 files changed

+2589
-0
lines changed

sound/arm/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,13 @@ config SND_PXA2XX_AC97
4040
Say Y or M if you want to support any AC97 codec attached to
4141
the PXA2xx AC97 interface.
4242

43+
config SND_BCM2835
44+
tristate "BCM2835 ALSA driver"
45+
depends on (ARCH_BCM2708 || ARCH_BCM2709 || ARCH_BCM2835) \
46+
&& BCM2708_VCHIQ && SND
47+
select SND_PCM
48+
help
49+
Say Y or M if you want to support BCM2835 Alsa pcm card driver
50+
4351
endif # SND_ARM
4452

sound/arm/Makefile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,8 @@ snd-pxa2xx-lib-$(CONFIG_SND_PXA2XX_LIB_AC97) += pxa2xx-ac97-lib.o
1414

1515
obj-$(CONFIG_SND_PXA2XX_AC97) += snd-pxa2xx-ac97.o
1616
snd-pxa2xx-ac97-objs := pxa2xx-ac97.o
17+
18+
obj-$(CONFIG_SND_BCM2835) += snd-bcm2835.o
19+
snd-bcm2835-objs := bcm2835.o bcm2835-ctl.o bcm2835-pcm.o bcm2835-vchiq.o
20+
21+
ccflags-y += -Idrivers/misc/vc04_services -Idrivers/misc/vc04_services/interface/vcos/linuxkernel -D__VCCOREVER__=0x04000000

sound/arm/bcm2835-ctl.c

Lines changed: 323 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,323 @@
1+
/*****************************************************************************
2+
* Copyright 2011 Broadcom Corporation. All rights reserved.
3+
*
4+
* Unless you and Broadcom execute a separate written software license
5+
* agreement governing use of this software, this software is licensed to you
6+
* under the terms of the GNU General Public License version 2, available at
7+
* http://www.broadcom.com/licenses/GPLv2.php (the "GPL").
8+
*
9+
* Notwithstanding the above, under no circumstances may you combine this
10+
* software in any way with any other Broadcom software provided under a
11+
* license other than the GPL, without Broadcom's express prior written
12+
* consent.
13+
*****************************************************************************/
14+
15+
#include <linux/platform_device.h>
16+
#include <linux/init.h>
17+
#include <linux/io.h>
18+
#include <linux/jiffies.h>
19+
#include <linux/slab.h>
20+
#include <linux/time.h>
21+
#include <linux/wait.h>
22+
#include <linux/delay.h>
23+
#include <linux/moduleparam.h>
24+
#include <linux/sched.h>
25+
26+
#include <sound/core.h>
27+
#include <sound/control.h>
28+
#include <sound/pcm.h>
29+
#include <sound/pcm_params.h>
30+
#include <sound/rawmidi.h>
31+
#include <sound/initval.h>
32+
#include <sound/tlv.h>
33+
#include <sound/asoundef.h>
34+
35+
#include "bcm2835.h"
36+
37+
/* volume maximum and minimum in terms of 0.01dB */
38+
#define CTRL_VOL_MAX 400
39+
#define CTRL_VOL_MIN -10239 /* originally -10240 */
40+
41+
42+
static int snd_bcm2835_ctl_info(struct snd_kcontrol *kcontrol,
43+
struct snd_ctl_elem_info *uinfo)
44+
{
45+
audio_info(" ... IN\n");
46+
if (kcontrol->private_value == PCM_PLAYBACK_VOLUME) {
47+
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
48+
uinfo->count = 1;
49+
uinfo->value.integer.min = CTRL_VOL_MIN;
50+
uinfo->value.integer.max = CTRL_VOL_MAX; /* 2303 */
51+
} else if (kcontrol->private_value == PCM_PLAYBACK_MUTE) {
52+
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
53+
uinfo->count = 1;
54+
uinfo->value.integer.min = 0;
55+
uinfo->value.integer.max = 1;
56+
} else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE) {
57+
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
58+
uinfo->count = 1;
59+
uinfo->value.integer.min = 0;
60+
uinfo->value.integer.max = AUDIO_DEST_MAX-1;
61+
}
62+
audio_info(" ... OUT\n");
63+
return 0;
64+
}
65+
66+
/* toggles mute on or off depending on the value of nmute, and returns
67+
* 1 if the mute value was changed, otherwise 0
68+
*/
69+
static int toggle_mute(struct bcm2835_chip *chip, int nmute)
70+
{
71+
/* if settings are ok, just return 0 */
72+
if(chip->mute == nmute)
73+
return 0;
74+
75+
/* if the sound is muted then we need to unmute */
76+
if(chip->mute == CTRL_VOL_MUTE)
77+
{
78+
chip->volume = chip->old_volume; /* copy the old volume back */
79+
audio_info("Unmuting, old_volume = %d, volume = %d ...\n", chip->old_volume, chip->volume);
80+
}
81+
else /* otherwise we mute */
82+
{
83+
chip->old_volume = chip->volume;
84+
chip->volume = 26214; /* set volume to minimum level AKA mute */
85+
audio_info("Muting, old_volume = %d, volume = %d ...\n", chip->old_volume, chip->volume);
86+
}
87+
88+
chip->mute = nmute;
89+
return 1;
90+
}
91+
92+
static int snd_bcm2835_ctl_get(struct snd_kcontrol *kcontrol,
93+
struct snd_ctl_elem_value *ucontrol)
94+
{
95+
struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
96+
97+
BUG_ON(!chip && !(chip->avail_substreams & AVAIL_SUBSTREAMS_MASK));
98+
99+
if (kcontrol->private_value == PCM_PLAYBACK_VOLUME)
100+
ucontrol->value.integer.value[0] = chip2alsa(chip->volume);
101+
else if (kcontrol->private_value == PCM_PLAYBACK_MUTE)
102+
ucontrol->value.integer.value[0] = chip->mute;
103+
else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE)
104+
ucontrol->value.integer.value[0] = chip->dest;
105+
106+
return 0;
107+
}
108+
109+
static int snd_bcm2835_ctl_put(struct snd_kcontrol *kcontrol,
110+
struct snd_ctl_elem_value *ucontrol)
111+
{
112+
struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
113+
int changed = 0;
114+
115+
if (kcontrol->private_value == PCM_PLAYBACK_VOLUME) {
116+
audio_info("Volume change attempted.. volume = %d new_volume = %d\n", chip->volume, (int)ucontrol->value.integer.value[0]);
117+
if (chip->mute == CTRL_VOL_MUTE) {
118+
/* changed = toggle_mute(chip, CTRL_VOL_UNMUTE); */
119+
return 1; /* should return 0 to signify no change but the mixer takes this as the opposite sign (no idea why) */
120+
}
121+
if (changed
122+
|| (ucontrol->value.integer.value[0] != chip2alsa(chip->volume))) {
123+
124+
chip->volume = alsa2chip(ucontrol->value.integer.value[0]);
125+
changed = 1;
126+
}
127+
128+
} else if (kcontrol->private_value == PCM_PLAYBACK_MUTE) {
129+
/* Now implemented */
130+
audio_info(" Mute attempted\n");
131+
changed = toggle_mute(chip, ucontrol->value.integer.value[0]);
132+
133+
} else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE) {
134+
if (ucontrol->value.integer.value[0] != chip->dest) {
135+
chip->dest = ucontrol->value.integer.value[0];
136+
changed = 1;
137+
}
138+
}
139+
140+
if (changed) {
141+
if (bcm2835_audio_set_ctls(chip))
142+
printk(KERN_ERR "Failed to set ALSA controls..\n");
143+
}
144+
145+
return changed;
146+
}
147+
148+
static DECLARE_TLV_DB_SCALE(snd_bcm2835_db_scale, CTRL_VOL_MIN, 1, 1);
149+
150+
static struct snd_kcontrol_new snd_bcm2835_ctl[] = {
151+
{
152+
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
153+
.name = "PCM Playback Volume",
154+
.index = 0,
155+
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,
156+
.private_value = PCM_PLAYBACK_VOLUME,
157+
.info = snd_bcm2835_ctl_info,
158+
.get = snd_bcm2835_ctl_get,
159+
.put = snd_bcm2835_ctl_put,
160+
.count = 1,
161+
.tlv = {.p = snd_bcm2835_db_scale}
162+
},
163+
{
164+
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
165+
.name = "PCM Playback Switch",
166+
.index = 0,
167+
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
168+
.private_value = PCM_PLAYBACK_MUTE,
169+
.info = snd_bcm2835_ctl_info,
170+
.get = snd_bcm2835_ctl_get,
171+
.put = snd_bcm2835_ctl_put,
172+
.count = 1,
173+
},
174+
{
175+
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
176+
.name = "PCM Playback Route",
177+
.index = 0,
178+
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
179+
.private_value = PCM_PLAYBACK_DEVICE,
180+
.info = snd_bcm2835_ctl_info,
181+
.get = snd_bcm2835_ctl_get,
182+
.put = snd_bcm2835_ctl_put,
183+
.count = 1,
184+
},
185+
};
186+
187+
static int snd_bcm2835_spdif_default_info(struct snd_kcontrol *kcontrol,
188+
struct snd_ctl_elem_info *uinfo)
189+
{
190+
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
191+
uinfo->count = 1;
192+
return 0;
193+
}
194+
195+
static int snd_bcm2835_spdif_default_get(struct snd_kcontrol *kcontrol,
196+
struct snd_ctl_elem_value *ucontrol)
197+
{
198+
struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
199+
int i;
200+
201+
for (i = 0; i < 4; i++)
202+
ucontrol->value.iec958.status[i] =
203+
(chip->spdif_status >> (i * 8)) && 0xff;
204+
205+
return 0;
206+
}
207+
208+
static int snd_bcm2835_spdif_default_put(struct snd_kcontrol *kcontrol,
209+
struct snd_ctl_elem_value *ucontrol)
210+
{
211+
struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
212+
unsigned int val = 0;
213+
int i, change;
214+
215+
for (i = 0; i < 4; i++)
216+
val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8);
217+
218+
change = val != chip->spdif_status;
219+
chip->spdif_status = val;
220+
221+
return change;
222+
}
223+
224+
static int snd_bcm2835_spdif_mask_info(struct snd_kcontrol *kcontrol,
225+
struct snd_ctl_elem_info *uinfo)
226+
{
227+
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
228+
uinfo->count = 1;
229+
return 0;
230+
}
231+
232+
static int snd_bcm2835_spdif_mask_get(struct snd_kcontrol *kcontrol,
233+
struct snd_ctl_elem_value *ucontrol)
234+
{
235+
/* bcm2835 supports only consumer mode and sets all other format flags
236+
* automatically. So the only thing left is signalling non-audio
237+
* content */
238+
ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO;
239+
return 0;
240+
}
241+
242+
static int snd_bcm2835_spdif_stream_info(struct snd_kcontrol *kcontrol,
243+
struct snd_ctl_elem_info *uinfo)
244+
{
245+
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
246+
uinfo->count = 1;
247+
return 0;
248+
}
249+
250+
static int snd_bcm2835_spdif_stream_get(struct snd_kcontrol *kcontrol,
251+
struct snd_ctl_elem_value *ucontrol)
252+
{
253+
struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
254+
int i;
255+
256+
for (i = 0; i < 4; i++)
257+
ucontrol->value.iec958.status[i] =
258+
(chip->spdif_status >> (i * 8)) & 0xff;
259+
return 0;
260+
}
261+
262+
static int snd_bcm2835_spdif_stream_put(struct snd_kcontrol *kcontrol,
263+
struct snd_ctl_elem_value *ucontrol)
264+
{
265+
struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
266+
unsigned int val = 0;
267+
int i, change;
268+
269+
for (i = 0; i < 4; i++)
270+
val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8);
271+
change = val != chip->spdif_status;
272+
chip->spdif_status = val;
273+
274+
return change;
275+
}
276+
277+
static struct snd_kcontrol_new snd_bcm2835_spdif[] = {
278+
{
279+
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
280+
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
281+
.info = snd_bcm2835_spdif_default_info,
282+
.get = snd_bcm2835_spdif_default_get,
283+
.put = snd_bcm2835_spdif_default_put
284+
},
285+
{
286+
.access = SNDRV_CTL_ELEM_ACCESS_READ,
287+
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
288+
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK),
289+
.info = snd_bcm2835_spdif_mask_info,
290+
.get = snd_bcm2835_spdif_mask_get,
291+
},
292+
{
293+
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
294+
SNDRV_CTL_ELEM_ACCESS_INACTIVE,
295+
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
296+
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM),
297+
.info = snd_bcm2835_spdif_stream_info,
298+
.get = snd_bcm2835_spdif_stream_get,
299+
.put = snd_bcm2835_spdif_stream_put,
300+
},
301+
};
302+
303+
int snd_bcm2835_new_ctl(bcm2835_chip_t * chip)
304+
{
305+
int err;
306+
unsigned int idx;
307+
308+
strcpy(chip->card->mixername, "Broadcom Mixer");
309+
for (idx = 0; idx < ARRAY_SIZE(snd_bcm2835_ctl); idx++) {
310+
err =
311+
snd_ctl_add(chip->card,
312+
snd_ctl_new1(&snd_bcm2835_ctl[idx], chip));
313+
if (err < 0)
314+
return err;
315+
}
316+
for (idx = 0; idx < ARRAY_SIZE(snd_bcm2835_spdif); idx++) {
317+
err = snd_ctl_add(chip->card,
318+
snd_ctl_new1(&snd_bcm2835_spdif[idx], chip));
319+
if (err < 0)
320+
return err;
321+
}
322+
return 0;
323+
}

0 commit comments

Comments
 (0)