-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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>
- Loading branch information
Michael Zoran
authored and
Greg Kroah-Hartman
committed
Jan 27, 2017
1 parent
e7b56b1
commit 23b028c
Showing
10 changed files
with
2,634 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
config SND_BCM2835 | ||
tristate "BCM2835 ALSA driver" | ||
depends on ARCH_BCM2835 && BCM2835_VCHIQ && SND | ||
select SND_PCM | ||
help | ||
Say Y or M if you want to support BCM2835 Alsa pcm card driver | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
obj-$(CONFIG_SND_BCM2835) += snd-bcm2835.o | ||
snd-bcm2835-objs := bcm2835.o bcm2835-ctl.o bcm2835-pcm.o bcm2835-vchiq.o | ||
|
||
ccflags-y += -Idrivers/staging/vc04_services -Idrivers/staging/vc04_services/interface/vcos/linuxkernel -D__VCCOREVER__=0x04000000 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,346 @@ | ||
/***************************************************************************** | ||
* Copyright 2011 Broadcom Corporation. All rights reserved. | ||
* | ||
* Unless you and Broadcom execute a separate written software license | ||
* agreement governing use of this software, this software is licensed to you | ||
* under the terms of the GNU General Public License version 2, available at | ||
* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). | ||
* | ||
* Notwithstanding the above, under no circumstances may you combine this | ||
* software in any way with any other Broadcom software provided under a | ||
* license other than the GPL, without Broadcom's express prior written | ||
* consent. | ||
*****************************************************************************/ | ||
|
||
#include <linux/platform_device.h> | ||
#include <linux/init.h> | ||
#include <linux/io.h> | ||
#include <linux/jiffies.h> | ||
#include <linux/slab.h> | ||
#include <linux/time.h> | ||
#include <linux/wait.h> | ||
#include <linux/delay.h> | ||
#include <linux/moduleparam.h> | ||
#include <linux/sched.h> | ||
|
||
#include <sound/core.h> | ||
#include <sound/control.h> | ||
#include <sound/pcm.h> | ||
#include <sound/pcm_params.h> | ||
#include <sound/rawmidi.h> | ||
#include <sound/initval.h> | ||
#include <sound/tlv.h> | ||
#include <sound/asoundef.h> | ||
|
||
#include "bcm2835.h" | ||
|
||
/* volume maximum and minimum in terms of 0.01dB */ | ||
#define CTRL_VOL_MAX 400 | ||
#define CTRL_VOL_MIN -10239 /* originally -10240 */ | ||
|
||
static int snd_bcm2835_ctl_info(struct snd_kcontrol *kcontrol, | ||
struct snd_ctl_elem_info *uinfo) | ||
{ | ||
audio_info(" ... IN\n"); | ||
if (kcontrol->private_value == PCM_PLAYBACK_VOLUME) { | ||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
uinfo->count = 1; | ||
uinfo->value.integer.min = CTRL_VOL_MIN; | ||
uinfo->value.integer.max = CTRL_VOL_MAX; /* 2303 */ | ||
} else if (kcontrol->private_value == PCM_PLAYBACK_MUTE) { | ||
uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; | ||
uinfo->count = 1; | ||
uinfo->value.integer.min = 0; | ||
uinfo->value.integer.max = 1; | ||
} else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE) { | ||
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; | ||
uinfo->count = 1; | ||
uinfo->value.integer.min = 0; | ||
uinfo->value.integer.max = AUDIO_DEST_MAX - 1; | ||
} | ||
audio_info(" ... OUT\n"); | ||
return 0; | ||
} | ||
|
||
/* toggles mute on or off depending on the value of nmute, and returns | ||
* 1 if the mute value was changed, otherwise 0 | ||
*/ | ||
static int toggle_mute(struct bcm2835_chip *chip, int nmute) | ||
{ | ||
/* if settings are ok, just return 0 */ | ||
if (chip->mute == nmute) | ||
return 0; | ||
|
||
/* if the sound is muted then we need to unmute */ | ||
if (chip->mute == CTRL_VOL_MUTE) { | ||
chip->volume = chip->old_volume; /* copy the old volume back */ | ||
audio_info("Unmuting, old_volume = %d, volume = %d ...\n", chip->old_volume, chip->volume); | ||
} else /* otherwise we mute */ { | ||
chip->old_volume = chip->volume; | ||
chip->volume = 26214; /* set volume to minimum level AKA mute */ | ||
audio_info("Muting, old_volume = %d, volume = %d ...\n", chip->old_volume, chip->volume); | ||
} | ||
|
||
chip->mute = nmute; | ||
return 1; | ||
} | ||
|
||
static int snd_bcm2835_ctl_get(struct snd_kcontrol *kcontrol, | ||
struct snd_ctl_elem_value *ucontrol) | ||
{ | ||
struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); | ||
|
||
if (mutex_lock_interruptible(&chip->audio_mutex)) | ||
return -EINTR; | ||
|
||
BUG_ON(!chip && !(chip->avail_substreams & AVAIL_SUBSTREAMS_MASK)); | ||
|
||
if (kcontrol->private_value == PCM_PLAYBACK_VOLUME) | ||
ucontrol->value.integer.value[0] = chip2alsa(chip->volume); | ||
else if (kcontrol->private_value == PCM_PLAYBACK_MUTE) | ||
ucontrol->value.integer.value[0] = chip->mute; | ||
else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE) | ||
ucontrol->value.integer.value[0] = chip->dest; | ||
|
||
mutex_unlock(&chip->audio_mutex); | ||
return 0; | ||
} | ||
|
||
static int snd_bcm2835_ctl_put(struct snd_kcontrol *kcontrol, | ||
struct snd_ctl_elem_value *ucontrol) | ||
{ | ||
struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); | ||
int changed = 0; | ||
|
||
if (mutex_lock_interruptible(&chip->audio_mutex)) | ||
return -EINTR; | ||
|
||
if (kcontrol->private_value == PCM_PLAYBACK_VOLUME) { | ||
audio_info("Volume change attempted.. volume = %d new_volume = %d\n", chip->volume, (int) ucontrol->value.integer.value[0]); | ||
if (chip->mute == CTRL_VOL_MUTE) { | ||
/* changed = toggle_mute(chip, CTRL_VOL_UNMUTE); */ | ||
changed = 1; /* should return 0 to signify no change but the mixer takes this as the opposite sign (no idea why) */ | ||
goto unlock; | ||
} | ||
if (changed | ||
|| (ucontrol->value.integer.value[0] != chip2alsa(chip->volume))) { | ||
|
||
chip->volume = alsa2chip(ucontrol->value.integer.value[0]); | ||
changed = 1; | ||
} | ||
|
||
} else if (kcontrol->private_value == PCM_PLAYBACK_MUTE) { | ||
/* Now implemented */ | ||
audio_info(" Mute attempted\n"); | ||
changed = toggle_mute(chip, ucontrol->value.integer.value[0]); | ||
|
||
} else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE) { | ||
if (ucontrol->value.integer.value[0] != chip->dest) { | ||
chip->dest = ucontrol->value.integer.value[0]; | ||
changed = 1; | ||
} | ||
} | ||
|
||
if (changed) { | ||
if (bcm2835_audio_set_ctls(chip)) | ||
printk(KERN_ERR "Failed to set ALSA controls..\n"); | ||
} | ||
|
||
unlock: | ||
mutex_unlock(&chip->audio_mutex); | ||
return changed; | ||
} | ||
|
||
static DECLARE_TLV_DB_SCALE(snd_bcm2835_db_scale, CTRL_VOL_MIN, 1, 1); | ||
|
||
static struct snd_kcontrol_new snd_bcm2835_ctl[] = { | ||
{ | ||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
.name = "PCM Playback Volume", | ||
.index = 0, | ||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, | ||
.private_value = PCM_PLAYBACK_VOLUME, | ||
.info = snd_bcm2835_ctl_info, | ||
.get = snd_bcm2835_ctl_get, | ||
.put = snd_bcm2835_ctl_put, | ||
.count = 1, | ||
.tlv = | ||
{.p = snd_bcm2835_db_scale} | ||
}, | ||
{ | ||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
.name = "PCM Playback Switch", | ||
.index = 0, | ||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
.private_value = PCM_PLAYBACK_MUTE, | ||
.info = snd_bcm2835_ctl_info, | ||
.get = snd_bcm2835_ctl_get, | ||
.put = snd_bcm2835_ctl_put, | ||
.count = 1, | ||
}, | ||
{ | ||
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, | ||
.name = "PCM Playback Route", | ||
.index = 0, | ||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE, | ||
.private_value = PCM_PLAYBACK_DEVICE, | ||
.info = snd_bcm2835_ctl_info, | ||
.get = snd_bcm2835_ctl_get, | ||
.put = snd_bcm2835_ctl_put, | ||
.count = 1, | ||
}, | ||
}; | ||
|
||
static int snd_bcm2835_spdif_default_info(struct snd_kcontrol *kcontrol, | ||
struct snd_ctl_elem_info *uinfo) | ||
{ | ||
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; | ||
uinfo->count = 1; | ||
return 0; | ||
} | ||
|
||
static int snd_bcm2835_spdif_default_get(struct snd_kcontrol *kcontrol, | ||
struct snd_ctl_elem_value *ucontrol) | ||
{ | ||
struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); | ||
int i; | ||
|
||
if (mutex_lock_interruptible(&chip->audio_mutex)) | ||
return -EINTR; | ||
|
||
for (i = 0; i < 4; i++) | ||
ucontrol->value.iec958.status[i] = | ||
(chip->spdif_status >> (i * 8)) && 0xff; | ||
|
||
mutex_unlock(&chip->audio_mutex); | ||
return 0; | ||
} | ||
|
||
static int snd_bcm2835_spdif_default_put(struct snd_kcontrol *kcontrol, | ||
struct snd_ctl_elem_value *ucontrol) | ||
{ | ||
struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); | ||
unsigned int val = 0; | ||
int i, change; | ||
|
||
if (mutex_lock_interruptible(&chip->audio_mutex)) | ||
return -EINTR; | ||
|
||
for (i = 0; i < 4; i++) | ||
val |= (unsigned int) ucontrol->value.iec958.status[i] << (i * 8); | ||
|
||
change = val != chip->spdif_status; | ||
chip->spdif_status = val; | ||
|
||
mutex_unlock(&chip->audio_mutex); | ||
return change; | ||
} | ||
|
||
static int snd_bcm2835_spdif_mask_info(struct snd_kcontrol *kcontrol, | ||
struct snd_ctl_elem_info *uinfo) | ||
{ | ||
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; | ||
uinfo->count = 1; | ||
return 0; | ||
} | ||
|
||
static int snd_bcm2835_spdif_mask_get(struct snd_kcontrol *kcontrol, | ||
struct snd_ctl_elem_value *ucontrol) | ||
{ | ||
/* bcm2835 supports only consumer mode and sets all other format flags | ||
* automatically. So the only thing left is signalling non-audio | ||
* content */ | ||
ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO; | ||
return 0; | ||
} | ||
|
||
static int snd_bcm2835_spdif_stream_info(struct snd_kcontrol *kcontrol, | ||
struct snd_ctl_elem_info *uinfo) | ||
{ | ||
uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; | ||
uinfo->count = 1; | ||
return 0; | ||
} | ||
|
||
static int snd_bcm2835_spdif_stream_get(struct snd_kcontrol *kcontrol, | ||
struct snd_ctl_elem_value *ucontrol) | ||
{ | ||
struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); | ||
int i; | ||
|
||
if (mutex_lock_interruptible(&chip->audio_mutex)) | ||
return -EINTR; | ||
|
||
for (i = 0; i < 4; i++) | ||
ucontrol->value.iec958.status[i] = | ||
(chip->spdif_status >> (i * 8)) & 0xff; | ||
|
||
mutex_unlock(&chip->audio_mutex); | ||
return 0; | ||
} | ||
|
||
static int snd_bcm2835_spdif_stream_put(struct snd_kcontrol *kcontrol, | ||
struct snd_ctl_elem_value *ucontrol) | ||
{ | ||
struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); | ||
unsigned int val = 0; | ||
int i, change; | ||
|
||
if (mutex_lock_interruptible(&chip->audio_mutex)) | ||
return -EINTR; | ||
|
||
for (i = 0; i < 4; i++) | ||
val |= (unsigned int) ucontrol->value.iec958.status[i] << (i * 8); | ||
change = val != chip->spdif_status; | ||
chip->spdif_status = val; | ||
|
||
mutex_unlock(&chip->audio_mutex); | ||
return change; | ||
} | ||
|
||
static struct snd_kcontrol_new snd_bcm2835_spdif[] = { | ||
{ | ||
.iface = SNDRV_CTL_ELEM_IFACE_PCM, | ||
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), | ||
.info = snd_bcm2835_spdif_default_info, | ||
.get = snd_bcm2835_spdif_default_get, | ||
.put = snd_bcm2835_spdif_default_put | ||
}, | ||
{ | ||
.access = SNDRV_CTL_ELEM_ACCESS_READ, | ||
.iface = SNDRV_CTL_ELEM_IFACE_PCM, | ||
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK), | ||
.info = snd_bcm2835_spdif_mask_info, | ||
.get = snd_bcm2835_spdif_mask_get, | ||
}, | ||
{ | ||
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | | ||
SNDRV_CTL_ELEM_ACCESS_INACTIVE, | ||
.iface = SNDRV_CTL_ELEM_IFACE_PCM, | ||
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM), | ||
.info = snd_bcm2835_spdif_stream_info, | ||
.get = snd_bcm2835_spdif_stream_get, | ||
.put = snd_bcm2835_spdif_stream_put, | ||
}, | ||
}; | ||
|
||
int snd_bcm2835_new_ctl(struct bcm2835_chip *chip) | ||
{ | ||
int err; | ||
unsigned int idx; | ||
|
||
strcpy(chip->card->mixername, "Broadcom Mixer"); | ||
for (idx = 0; idx < ARRAY_SIZE(snd_bcm2835_ctl); idx++) { | ||
err = snd_ctl_add(chip->card, | ||
snd_ctl_new1(&snd_bcm2835_ctl[idx], chip)); | ||
if (err < 0) | ||
return err; | ||
} | ||
for (idx = 0; idx < ARRAY_SIZE(snd_bcm2835_spdif); idx++) { | ||
err = snd_ctl_add(chip->card, | ||
snd_ctl_new1(&snd_bcm2835_spdif[idx], chip)); | ||
if (err < 0) | ||
return err; | ||
} | ||
return 0; | ||
} |
Oops, something went wrong.