Skip to content

Commit 23b028c

Browse files
Electron752gregkh
authored andcommitted
staging: bcm2835-audio: initial staging submission
Initial cleanup of bcm2835-audio driver for the bcm2535(Raspberry PI) Driver provides HDMI audio through ALSA and is built on top of the vc04_services driver. Original version of the driver is available at: http://www.github.com/raspberry/linux Driver compiles without any build errors or warnings. Tested on a RPI 3 running in ARM64 mode with the vlc player and alsautils. Signed-off-by: Michael Zoran <mzoran@crowfest.net> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent e7b56b1 commit 23b028c

File tree

10 files changed

+2634
-0
lines changed

10 files changed

+2634
-0
lines changed

drivers/staging/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,4 +102,6 @@ source "drivers/staging/greybus/Kconfig"
102102

103103
source "drivers/staging/vc04_services/Kconfig"
104104

105+
source "drivers/staging/bcm2835-audio/Kconfig"
106+
105107
endif # STAGING

drivers/staging/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,5 @@ obj-$(CONFIG_MOST) += most/
4040
obj-$(CONFIG_KS7010) += ks7010/
4141
obj-$(CONFIG_GREYBUS) += greybus/
4242
obj-$(CONFIG_BCM2835_VCHIQ) += vc04_services/
43+
obj-$(CONFIG_SND_BCM2835) += bcm2835-audio/
44+

drivers/staging/bcm2835-audio/Kconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
config SND_BCM2835
2+
tristate "BCM2835 ALSA driver"
3+
depends on ARCH_BCM2835 && BCM2835_VCHIQ && SND
4+
select SND_PCM
5+
help
6+
Say Y or M if you want to support BCM2835 Alsa pcm card driver
7+
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
obj-$(CONFIG_SND_BCM2835) += snd-bcm2835.o
2+
snd-bcm2835-objs := bcm2835.o bcm2835-ctl.o bcm2835-pcm.o bcm2835-vchiq.o
3+
4+
ccflags-y += -Idrivers/staging/vc04_services -Idrivers/staging/vc04_services/interface/vcos/linuxkernel -D__VCCOREVER__=0x04000000
5+
Lines changed: 346 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,346 @@
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+
static int snd_bcm2835_ctl_info(struct snd_kcontrol *kcontrol,
42+
struct snd_ctl_elem_info *uinfo)
43+
{
44+
audio_info(" ... IN\n");
45+
if (kcontrol->private_value == PCM_PLAYBACK_VOLUME) {
46+
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
47+
uinfo->count = 1;
48+
uinfo->value.integer.min = CTRL_VOL_MIN;
49+
uinfo->value.integer.max = CTRL_VOL_MAX; /* 2303 */
50+
} else if (kcontrol->private_value == PCM_PLAYBACK_MUTE) {
51+
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
52+
uinfo->count = 1;
53+
uinfo->value.integer.min = 0;
54+
uinfo->value.integer.max = 1;
55+
} else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE) {
56+
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
57+
uinfo->count = 1;
58+
uinfo->value.integer.min = 0;
59+
uinfo->value.integer.max = AUDIO_DEST_MAX - 1;
60+
}
61+
audio_info(" ... OUT\n");
62+
return 0;
63+
}
64+
65+
/* toggles mute on or off depending on the value of nmute, and returns
66+
* 1 if the mute value was changed, otherwise 0
67+
*/
68+
static int toggle_mute(struct bcm2835_chip *chip, int nmute)
69+
{
70+
/* if settings are ok, just return 0 */
71+
if (chip->mute == nmute)
72+
return 0;
73+
74+
/* if the sound is muted then we need to unmute */
75+
if (chip->mute == CTRL_VOL_MUTE) {
76+
chip->volume = chip->old_volume; /* copy the old volume back */
77+
audio_info("Unmuting, old_volume = %d, volume = %d ...\n", chip->old_volume, chip->volume);
78+
} else /* otherwise we mute */ {
79+
chip->old_volume = chip->volume;
80+
chip->volume = 26214; /* set volume to minimum level AKA mute */
81+
audio_info("Muting, old_volume = %d, volume = %d ...\n", chip->old_volume, chip->volume);
82+
}
83+
84+
chip->mute = nmute;
85+
return 1;
86+
}
87+
88+
static int snd_bcm2835_ctl_get(struct snd_kcontrol *kcontrol,
89+
struct snd_ctl_elem_value *ucontrol)
90+
{
91+
struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
92+
93+
if (mutex_lock_interruptible(&chip->audio_mutex))
94+
return -EINTR;
95+
96+
BUG_ON(!chip && !(chip->avail_substreams & AVAIL_SUBSTREAMS_MASK));
97+
98+
if (kcontrol->private_value == PCM_PLAYBACK_VOLUME)
99+
ucontrol->value.integer.value[0] = chip2alsa(chip->volume);
100+
else if (kcontrol->private_value == PCM_PLAYBACK_MUTE)
101+
ucontrol->value.integer.value[0] = chip->mute;
102+
else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE)
103+
ucontrol->value.integer.value[0] = chip->dest;
104+
105+
mutex_unlock(&chip->audio_mutex);
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 (mutex_lock_interruptible(&chip->audio_mutex))
116+
return -EINTR;
117+
118+
if (kcontrol->private_value == PCM_PLAYBACK_VOLUME) {
119+
audio_info("Volume change attempted.. volume = %d new_volume = %d\n", chip->volume, (int) ucontrol->value.integer.value[0]);
120+
if (chip->mute == CTRL_VOL_MUTE) {
121+
/* changed = toggle_mute(chip, CTRL_VOL_UNMUTE); */
122+
changed = 1; /* should return 0 to signify no change but the mixer takes this as the opposite sign (no idea why) */
123+
goto unlock;
124+
}
125+
if (changed
126+
|| (ucontrol->value.integer.value[0] != chip2alsa(chip->volume))) {
127+
128+
chip->volume = alsa2chip(ucontrol->value.integer.value[0]);
129+
changed = 1;
130+
}
131+
132+
} else if (kcontrol->private_value == PCM_PLAYBACK_MUTE) {
133+
/* Now implemented */
134+
audio_info(" Mute attempted\n");
135+
changed = toggle_mute(chip, ucontrol->value.integer.value[0]);
136+
137+
} else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE) {
138+
if (ucontrol->value.integer.value[0] != chip->dest) {
139+
chip->dest = ucontrol->value.integer.value[0];
140+
changed = 1;
141+
}
142+
}
143+
144+
if (changed) {
145+
if (bcm2835_audio_set_ctls(chip))
146+
printk(KERN_ERR "Failed to set ALSA controls..\n");
147+
}
148+
149+
unlock:
150+
mutex_unlock(&chip->audio_mutex);
151+
return changed;
152+
}
153+
154+
static DECLARE_TLV_DB_SCALE(snd_bcm2835_db_scale, CTRL_VOL_MIN, 1, 1);
155+
156+
static struct snd_kcontrol_new snd_bcm2835_ctl[] = {
157+
{
158+
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
159+
.name = "PCM Playback Volume",
160+
.index = 0,
161+
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,
162+
.private_value = PCM_PLAYBACK_VOLUME,
163+
.info = snd_bcm2835_ctl_info,
164+
.get = snd_bcm2835_ctl_get,
165+
.put = snd_bcm2835_ctl_put,
166+
.count = 1,
167+
.tlv =
168+
{.p = snd_bcm2835_db_scale}
169+
},
170+
{
171+
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
172+
.name = "PCM Playback Switch",
173+
.index = 0,
174+
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
175+
.private_value = PCM_PLAYBACK_MUTE,
176+
.info = snd_bcm2835_ctl_info,
177+
.get = snd_bcm2835_ctl_get,
178+
.put = snd_bcm2835_ctl_put,
179+
.count = 1,
180+
},
181+
{
182+
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
183+
.name = "PCM Playback Route",
184+
.index = 0,
185+
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
186+
.private_value = PCM_PLAYBACK_DEVICE,
187+
.info = snd_bcm2835_ctl_info,
188+
.get = snd_bcm2835_ctl_get,
189+
.put = snd_bcm2835_ctl_put,
190+
.count = 1,
191+
},
192+
};
193+
194+
static int snd_bcm2835_spdif_default_info(struct snd_kcontrol *kcontrol,
195+
struct snd_ctl_elem_info *uinfo)
196+
{
197+
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
198+
uinfo->count = 1;
199+
return 0;
200+
}
201+
202+
static int snd_bcm2835_spdif_default_get(struct snd_kcontrol *kcontrol,
203+
struct snd_ctl_elem_value *ucontrol)
204+
{
205+
struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
206+
int i;
207+
208+
if (mutex_lock_interruptible(&chip->audio_mutex))
209+
return -EINTR;
210+
211+
for (i = 0; i < 4; i++)
212+
ucontrol->value.iec958.status[i] =
213+
(chip->spdif_status >> (i * 8)) && 0xff;
214+
215+
mutex_unlock(&chip->audio_mutex);
216+
return 0;
217+
}
218+
219+
static int snd_bcm2835_spdif_default_put(struct snd_kcontrol *kcontrol,
220+
struct snd_ctl_elem_value *ucontrol)
221+
{
222+
struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
223+
unsigned int val = 0;
224+
int i, change;
225+
226+
if (mutex_lock_interruptible(&chip->audio_mutex))
227+
return -EINTR;
228+
229+
for (i = 0; i < 4; i++)
230+
val |= (unsigned int) ucontrol->value.iec958.status[i] << (i * 8);
231+
232+
change = val != chip->spdif_status;
233+
chip->spdif_status = val;
234+
235+
mutex_unlock(&chip->audio_mutex);
236+
return change;
237+
}
238+
239+
static int snd_bcm2835_spdif_mask_info(struct snd_kcontrol *kcontrol,
240+
struct snd_ctl_elem_info *uinfo)
241+
{
242+
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
243+
uinfo->count = 1;
244+
return 0;
245+
}
246+
247+
static int snd_bcm2835_spdif_mask_get(struct snd_kcontrol *kcontrol,
248+
struct snd_ctl_elem_value *ucontrol)
249+
{
250+
/* bcm2835 supports only consumer mode and sets all other format flags
251+
* automatically. So the only thing left is signalling non-audio
252+
* content */
253+
ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO;
254+
return 0;
255+
}
256+
257+
static int snd_bcm2835_spdif_stream_info(struct snd_kcontrol *kcontrol,
258+
struct snd_ctl_elem_info *uinfo)
259+
{
260+
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
261+
uinfo->count = 1;
262+
return 0;
263+
}
264+
265+
static int snd_bcm2835_spdif_stream_get(struct snd_kcontrol *kcontrol,
266+
struct snd_ctl_elem_value *ucontrol)
267+
{
268+
struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
269+
int i;
270+
271+
if (mutex_lock_interruptible(&chip->audio_mutex))
272+
return -EINTR;
273+
274+
for (i = 0; i < 4; i++)
275+
ucontrol->value.iec958.status[i] =
276+
(chip->spdif_status >> (i * 8)) & 0xff;
277+
278+
mutex_unlock(&chip->audio_mutex);
279+
return 0;
280+
}
281+
282+
static int snd_bcm2835_spdif_stream_put(struct snd_kcontrol *kcontrol,
283+
struct snd_ctl_elem_value *ucontrol)
284+
{
285+
struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
286+
unsigned int val = 0;
287+
int i, change;
288+
289+
if (mutex_lock_interruptible(&chip->audio_mutex))
290+
return -EINTR;
291+
292+
for (i = 0; i < 4; i++)
293+
val |= (unsigned int) ucontrol->value.iec958.status[i] << (i * 8);
294+
change = val != chip->spdif_status;
295+
chip->spdif_status = val;
296+
297+
mutex_unlock(&chip->audio_mutex);
298+
return change;
299+
}
300+
301+
static struct snd_kcontrol_new snd_bcm2835_spdif[] = {
302+
{
303+
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
304+
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
305+
.info = snd_bcm2835_spdif_default_info,
306+
.get = snd_bcm2835_spdif_default_get,
307+
.put = snd_bcm2835_spdif_default_put
308+
},
309+
{
310+
.access = SNDRV_CTL_ELEM_ACCESS_READ,
311+
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
312+
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK),
313+
.info = snd_bcm2835_spdif_mask_info,
314+
.get = snd_bcm2835_spdif_mask_get,
315+
},
316+
{
317+
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
318+
SNDRV_CTL_ELEM_ACCESS_INACTIVE,
319+
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
320+
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM),
321+
.info = snd_bcm2835_spdif_stream_info,
322+
.get = snd_bcm2835_spdif_stream_get,
323+
.put = snd_bcm2835_spdif_stream_put,
324+
},
325+
};
326+
327+
int snd_bcm2835_new_ctl(struct bcm2835_chip *chip)
328+
{
329+
int err;
330+
unsigned int idx;
331+
332+
strcpy(chip->card->mixername, "Broadcom Mixer");
333+
for (idx = 0; idx < ARRAY_SIZE(snd_bcm2835_ctl); idx++) {
334+
err = snd_ctl_add(chip->card,
335+
snd_ctl_new1(&snd_bcm2835_ctl[idx], chip));
336+
if (err < 0)
337+
return err;
338+
}
339+
for (idx = 0; idx < ARRAY_SIZE(snd_bcm2835_spdif); idx++) {
340+
err = snd_ctl_add(chip->card,
341+
snd_ctl_new1(&snd_bcm2835_spdif[idx], chip));
342+
if (err < 0)
343+
return err;
344+
}
345+
return 0;
346+
}

0 commit comments

Comments
 (0)