]> git.proxmox.com Git - mirror_ubuntu-focal-kernel.git/commitdiff
staging: bcm2835-audio: Move driver under vc04_services
authorMichael Zoran <mzoran@crowfest.net>
Mon, 27 Feb 2017 23:35:12 +0000 (15:35 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 6 Mar 2017 08:17:06 +0000 (09:17 +0100)
The bcm2835-audio driver is part of v04_services, so it makes
sense for it to be located under vc04_services to make
configuration clearer.

Signed-off-by: Michael Zoran <mzoran@crowfest.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
22 files changed:
drivers/staging/Kconfig
drivers/staging/Makefile
drivers/staging/bcm2835-audio/Kconfig [deleted file]
drivers/staging/bcm2835-audio/Makefile [deleted file]
drivers/staging/bcm2835-audio/TODO [deleted file]
drivers/staging/bcm2835-audio/bcm2835-ctl.c [deleted file]
drivers/staging/bcm2835-audio/bcm2835-pcm.c [deleted file]
drivers/staging/bcm2835-audio/bcm2835-vchiq.c [deleted file]
drivers/staging/bcm2835-audio/bcm2835.c [deleted file]
drivers/staging/bcm2835-audio/bcm2835.h [deleted file]
drivers/staging/bcm2835-audio/vc_vchi_audioserv_defs.h [deleted file]
drivers/staging/vc04_services/Kconfig
drivers/staging/vc04_services/Makefile
drivers/staging/vc04_services/bcm2835-audio/Kconfig [new file with mode: 0644]
drivers/staging/vc04_services/bcm2835-audio/Makefile [new file with mode: 0644]
drivers/staging/vc04_services/bcm2835-audio/TODO [new file with mode: 0644]
drivers/staging/vc04_services/bcm2835-audio/bcm2835-ctl.c [new file with mode: 0644]
drivers/staging/vc04_services/bcm2835-audio/bcm2835-pcm.c [new file with mode: 0644]
drivers/staging/vc04_services/bcm2835-audio/bcm2835-vchiq.c [new file with mode: 0644]
drivers/staging/vc04_services/bcm2835-audio/bcm2835.c [new file with mode: 0644]
drivers/staging/vc04_services/bcm2835-audio/bcm2835.h [new file with mode: 0644]
drivers/staging/vc04_services/bcm2835-audio/vc_vchi_audioserv_defs.h [new file with mode: 0644]

index 4c360f8071a8e1d43f2047dc41d18e9b6091949c..8cec648e639d88dacb12a5793aa1968af59047d8 100644 (file)
@@ -102,6 +102,4 @@ source "drivers/staging/greybus/Kconfig"
 
 source "drivers/staging/vc04_services/Kconfig"
 
-source "drivers/staging/bcm2835-audio/Kconfig"
-
 endif # STAGING
index 29cec5aa2945e767bc70bf535ce5f7b745023286..3671dc59579ea0848a07ab106426ed76a79eea8a 100644 (file)
@@ -40,5 +40,4 @@ obj-$(CONFIG_MOST)            += most/
 obj-$(CONFIG_KS7010)           += ks7010/
 obj-$(CONFIG_GREYBUS)          += greybus/
 obj-$(CONFIG_BCM2835_VCHIQ)    += vc04_services/
-obj-$(CONFIG_SND_BCM2835)      += bcm2835-audio/
 
diff --git a/drivers/staging/bcm2835-audio/Kconfig b/drivers/staging/bcm2835-audio/Kconfig
deleted file mode 100644 (file)
index 32a2ff9..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-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
-
diff --git a/drivers/staging/bcm2835-audio/Makefile b/drivers/staging/bcm2835-audio/Makefile
deleted file mode 100644 (file)
index d7b88d1..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-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 -D__VCCOREVER__=0x04000000
-
diff --git a/drivers/staging/bcm2835-audio/TODO b/drivers/staging/bcm2835-audio/TODO
deleted file mode 100644 (file)
index 73d41fa..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-*****************************************************************************
-*                                                                           *
-*                           TODO: BCM2835-AUDIO                             *
-*                                                                           *
-*****************************************************************************
-
-
-1) Document the device tree node
-
-The downstream tree(the tree that the driver was imported from) at
-http://www.github.com/raspberrypi/linux uses this node:
-
-audio: audio {
-       compatible = "brcm,bcm2835-audio";
-       brcm,pwm-channels = <8>;
-};
-
-Since the driver requires the use of VCHIQ, it may be useful to have a link
-in the device tree to the VCHIQ driver.
-
-2) Gracefully handle the case where VCHIQ is missing from the device tree or
-it has not been initialized yet.
-
-3) Review error handling and remove duplicate code.
-
-4) Cleanup the logging mechanism.  The driver should probably be using the
-standard kernel logging mechanisms such as dev_info, dev_dbg, and friends.
-
-5) Fix the remaining checkpatch.pl errors and warnings.
diff --git a/drivers/staging/bcm2835-audio/bcm2835-ctl.c b/drivers/staging/bcm2835-audio/bcm2835-ctl.c
deleted file mode 100644 (file)
index a4ffa1b..0000000
+++ /dev/null
@@ -1,345 +0,0 @@
-/*****************************************************************************
- * 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;
-}
diff --git a/drivers/staging/bcm2835-audio/bcm2835-pcm.c b/drivers/staging/bcm2835-audio/bcm2835-pcm.c
deleted file mode 100644 (file)
index 9e79236..0000000
+++ /dev/null
@@ -1,554 +0,0 @@
-/*****************************************************************************
- * 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/interrupt.h>
-#include <linux/slab.h>
-
-#include <sound/asoundef.h>
-
-#include "bcm2835.h"
-
-/* hardware definition */
-static struct snd_pcm_hardware snd_bcm2835_playback_hw = {
-       .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
-       SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
-       .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
-       .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
-       .rate_min = 8000,
-       .rate_max = 48000,
-       .channels_min = 1,
-       .channels_max = 2,
-       .buffer_bytes_max = 128 * 1024,
-       .period_bytes_min = 1 * 1024,
-       .period_bytes_max = 128 * 1024,
-       .periods_min = 1,
-       .periods_max = 128,
-};
-
-static struct snd_pcm_hardware snd_bcm2835_playback_spdif_hw = {
-       .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
-       SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
-       .formats = SNDRV_PCM_FMTBIT_S16_LE,
-       .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_44100 |
-       SNDRV_PCM_RATE_48000,
-       .rate_min = 44100,
-       .rate_max = 48000,
-       .channels_min = 2,
-       .channels_max = 2,
-       .buffer_bytes_max = 128 * 1024,
-       .period_bytes_min = 1 * 1024,
-       .period_bytes_max = 128 * 1024,
-       .periods_min = 1,
-       .periods_max = 128,
-};
-
-static void snd_bcm2835_playback_free(struct snd_pcm_runtime *runtime)
-{
-       audio_info("Freeing up alsa stream here ..\n");
-       if (runtime->private_data)
-               kfree(runtime->private_data);
-       runtime->private_data = NULL;
-}
-
-void bcm2835_playback_fifo(struct bcm2835_alsa_stream *alsa_stream)
-{
-       unsigned int consumed = 0;
-       int new_period = 0;
-
-       audio_info(" .. IN\n");
-
-       audio_info("alsa_stream=%p substream=%p\n", alsa_stream,
-               alsa_stream ? alsa_stream->substream : 0);
-
-       if (alsa_stream->open)
-               consumed = bcm2835_audio_retrieve_buffers(alsa_stream);
-
-       /* We get called only if playback was triggered, So, the number of buffers we retrieve in
-        * each iteration are the buffers that have been played out already
-        */
-
-       if (alsa_stream->period_size) {
-               if ((alsa_stream->pos / alsa_stream->period_size) !=
-                       ((alsa_stream->pos + consumed) / alsa_stream->period_size))
-                       new_period = 1;
-       }
-       audio_debug("updating pos cur: %d + %d max:%d period_bytes:%d, hw_ptr: %d new_period:%d\n",
-               alsa_stream->pos,
-               consumed,
-               alsa_stream->buffer_size,
-               (int) (alsa_stream->period_size * alsa_stream->substream->runtime->periods),
-               frames_to_bytes(alsa_stream->substream->runtime, alsa_stream->substream->runtime->status->hw_ptr),
-               new_period);
-       if (alsa_stream->buffer_size) {
-               alsa_stream->pos += consumed &~(1 << 30);
-               alsa_stream->pos %= alsa_stream->buffer_size;
-       }
-
-       if (alsa_stream->substream) {
-               if (new_period)
-                       snd_pcm_period_elapsed(alsa_stream->substream);
-       } else {
-               audio_warning(" unexpected NULL substream\n");
-       }
-       audio_info(" .. OUT\n");
-}
-
-/* open callback */
-static int snd_bcm2835_playback_open_generic(
-       struct snd_pcm_substream *substream, int spdif)
-{
-       struct bcm2835_chip *chip = snd_pcm_substream_chip(substream);
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       struct bcm2835_alsa_stream *alsa_stream;
-       int idx;
-       int err;
-
-       audio_info(" .. IN (%d)\n", substream->number);
-
-       if (mutex_lock_interruptible(&chip->audio_mutex)) {
-               audio_error("Interrupted whilst waiting for lock\n");
-               return -EINTR;
-       }
-       audio_info("Alsa open (%d)\n", substream->number);
-       idx = substream->number;
-
-       if (spdif && chip->opened) {
-               err = -EBUSY;
-               goto out;
-       } else if (!spdif && (chip->opened & (1 << idx))) {
-               err = -EBUSY;
-               goto out;
-       }
-       if (idx >= MAX_SUBSTREAMS) {
-               audio_error
-                       ("substream(%d) device doesn't exist max(%d) substreams allowed\n",
-                       idx, MAX_SUBSTREAMS);
-               err = -ENODEV;
-               goto out;
-       }
-
-       /* Check if we are ready */
-       if (!(chip->avail_substreams & (1 << idx))) {
-               /* We are not ready yet */
-               audio_error("substream(%d) device is not ready yet\n", idx);
-               err = -EAGAIN;
-               goto out;
-       }
-
-       alsa_stream = kzalloc(sizeof(*alsa_stream), GFP_KERNEL);
-       if (!alsa_stream) {
-               err = -ENOMEM;
-               goto out;
-       }
-
-       /* Initialise alsa_stream */
-       alsa_stream->chip = chip;
-       alsa_stream->substream = substream;
-       alsa_stream->idx = idx;
-
-       sema_init(&alsa_stream->buffers_update_sem, 0);
-       sema_init(&alsa_stream->control_sem, 0);
-       spin_lock_init(&alsa_stream->lock);
-
-       err = bcm2835_audio_open(alsa_stream);
-       if (err) {
-               kfree(alsa_stream);
-               goto out;
-       }
-       runtime->private_data = alsa_stream;
-       runtime->private_free = snd_bcm2835_playback_free;
-       if (spdif) {
-               runtime->hw = snd_bcm2835_playback_spdif_hw;
-       } else {
-               /* clear spdif status, as we are not in spdif mode */
-               chip->spdif_status = 0;
-               runtime->hw = snd_bcm2835_playback_hw;
-       }
-       /* minimum 16 bytes alignment (for vchiq bulk transfers) */
-       snd_pcm_hw_constraint_step(runtime,
-                                  0,
-                                  SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
-                                  16);
-
-       chip->alsa_stream[idx] = alsa_stream;
-
-       chip->opened |= (1 << idx);
-       alsa_stream->open = 1;
-       alsa_stream->draining = 1;
-
-out:
-       mutex_unlock(&chip->audio_mutex);
-
-       audio_info(" .. OUT =%d\n", err);
-
-       return err;
-}
-
-static int snd_bcm2835_playback_open(struct snd_pcm_substream *substream)
-{
-       return snd_bcm2835_playback_open_generic(substream, 0);
-}
-
-static int snd_bcm2835_playback_spdif_open(struct snd_pcm_substream *substream)
-{
-       return snd_bcm2835_playback_open_generic(substream, 1);
-}
-
-/* close callback */
-static int snd_bcm2835_playback_close(struct snd_pcm_substream *substream)
-{
-       /* the hardware-specific codes will be here */
-
-       struct bcm2835_chip *chip;
-       struct snd_pcm_runtime *runtime;
-       struct bcm2835_alsa_stream *alsa_stream;
-
-       audio_info(" .. IN\n");
-
-       chip = snd_pcm_substream_chip(substream);
-       if (mutex_lock_interruptible(&chip->audio_mutex)) {
-               audio_error("Interrupted whilst waiting for lock\n");
-               return -EINTR;
-       }
-       runtime = substream->runtime;
-       alsa_stream = runtime->private_data;
-
-       audio_info("Alsa close\n");
-
-       /*
-        * Call stop if it's still running. This happens when app
-        * is force killed and we don't get a stop trigger.
-        */
-       if (alsa_stream->running) {
-               int err;
-               err = bcm2835_audio_stop(alsa_stream);
-               alsa_stream->running = 0;
-               if (err)
-                       audio_error(" Failed to STOP alsa device\n");
-       }
-
-       alsa_stream->period_size = 0;
-       alsa_stream->buffer_size = 0;
-
-       if (alsa_stream->open) {
-               alsa_stream->open = 0;
-               bcm2835_audio_close(alsa_stream);
-       }
-       if (alsa_stream->chip)
-               alsa_stream->chip->alsa_stream[alsa_stream->idx] = NULL;
-       /*
-        * Do not free up alsa_stream here, it will be freed up by
-        * runtime->private_free callback we registered in *_open above
-        */
-
-       chip->opened &= ~(1 << substream->number);
-
-       mutex_unlock(&chip->audio_mutex);
-       audio_info(" .. OUT\n");
-
-       return 0;
-}
-
-/* hw_params callback */
-static int snd_bcm2835_pcm_hw_params(struct snd_pcm_substream *substream,
-       struct snd_pcm_hw_params *params)
-{
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       struct bcm2835_alsa_stream *alsa_stream = runtime->private_data;
-       int err;
-
-       audio_info(" .. IN\n");
-
-       err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
-       if (err < 0) {
-               audio_error
-                       (" pcm_lib_malloc failed to allocated pages for buffers\n");
-               return err;
-       }
-
-       alsa_stream->channels = params_channels(params);
-       alsa_stream->params_rate = params_rate(params);
-       alsa_stream->pcm_format_width = snd_pcm_format_width(params_format(params));
-       audio_info(" .. OUT\n");
-
-       return err;
-}
-
-/* hw_free callback */
-static int snd_bcm2835_pcm_hw_free(struct snd_pcm_substream *substream)
-{
-       audio_info(" .. IN\n");
-       return snd_pcm_lib_free_pages(substream);
-}
-
-/* prepare callback */
-static int snd_bcm2835_pcm_prepare(struct snd_pcm_substream *substream)
-{
-       struct bcm2835_chip *chip = snd_pcm_substream_chip(substream);
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       struct bcm2835_alsa_stream *alsa_stream = runtime->private_data;
-       int channels;
-       int err;
-
-       audio_info(" .. IN\n");
-
-       if (mutex_lock_interruptible(&chip->audio_mutex))
-               return -EINTR;
-
-       /* notify the vchiq that it should enter spdif passthrough mode by
-        * setting channels=0 (see
-        * https://github.com/raspberrypi/linux/issues/528) */
-       if (chip->spdif_status & IEC958_AES0_NONAUDIO)
-               channels = 0;
-       else
-               channels = alsa_stream->channels;
-
-       err = bcm2835_audio_set_params(alsa_stream, channels,
-               alsa_stream->params_rate,
-               alsa_stream->pcm_format_width);
-       if (err < 0)
-               audio_error(" error setting hw params\n");
-
-
-       bcm2835_audio_setup(alsa_stream);
-
-       /* in preparation of the stream, set the controls (volume level) of the stream */
-       bcm2835_audio_set_ctls(alsa_stream->chip);
-
-
-       memset(&alsa_stream->pcm_indirect, 0, sizeof(alsa_stream->pcm_indirect));
-
-       alsa_stream->pcm_indirect.hw_buffer_size =
-               alsa_stream->pcm_indirect.sw_buffer_size =
-               snd_pcm_lib_buffer_bytes(substream);
-
-       alsa_stream->buffer_size = snd_pcm_lib_buffer_bytes(substream);
-       alsa_stream->period_size = snd_pcm_lib_period_bytes(substream);
-       alsa_stream->pos = 0;
-
-       audio_debug("buffer_size=%d, period_size=%d pos=%d frame_bits=%d\n",
-               alsa_stream->buffer_size, alsa_stream->period_size,
-               alsa_stream->pos, runtime->frame_bits);
-
-       mutex_unlock(&chip->audio_mutex);
-       audio_info(" .. OUT\n");
-       return 0;
-}
-
-static void snd_bcm2835_pcm_transfer(struct snd_pcm_substream *substream,
-       struct snd_pcm_indirect *rec, size_t bytes)
-{
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       struct bcm2835_alsa_stream *alsa_stream = runtime->private_data;
-       void *src = (void *) (substream->runtime->dma_area + rec->sw_data);
-       int err;
-
-       err = bcm2835_audio_write(alsa_stream, bytes, src);
-       if (err)
-               audio_error(" Failed to transfer to alsa device (%d)\n", err);
-
-}
-
-static int snd_bcm2835_pcm_ack(struct snd_pcm_substream *substream)
-{
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       struct bcm2835_alsa_stream *alsa_stream = runtime->private_data;
-       struct snd_pcm_indirect *pcm_indirect = &alsa_stream->pcm_indirect;
-
-       pcm_indirect->hw_queue_size = runtime->hw.buffer_bytes_max;
-       snd_pcm_indirect_playback_transfer(substream, pcm_indirect,
-                                          snd_bcm2835_pcm_transfer);
-       return 0;
-}
-
-/* trigger callback */
-static int snd_bcm2835_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
-{
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       struct bcm2835_alsa_stream *alsa_stream = runtime->private_data;
-       int err = 0;
-
-       audio_info(" .. IN\n");
-
-       switch (cmd) {
-       case SNDRV_PCM_TRIGGER_START:
-               audio_debug("bcm2835_AUDIO_TRIGGER_START running=%d\n",
-                       alsa_stream->running);
-               if (!alsa_stream->running) {
-                       err = bcm2835_audio_start(alsa_stream);
-                       if (!err) {
-                               alsa_stream->pcm_indirect.hw_io =
-                                       alsa_stream->pcm_indirect.hw_data =
-                                       bytes_to_frames(runtime,
-                                       alsa_stream->pos);
-                               substream->ops->ack(substream);
-                               alsa_stream->running = 1;
-                               alsa_stream->draining = 1;
-                       } else {
-                               audio_error(" Failed to START alsa device (%d)\n", err);
-                       }
-               }
-               break;
-       case SNDRV_PCM_TRIGGER_STOP:
-               audio_debug
-                       ("bcm2835_AUDIO_TRIGGER_STOP running=%d draining=%d\n",
-                       alsa_stream->running, runtime->status->state == SNDRV_PCM_STATE_DRAINING);
-               if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
-                       audio_info("DRAINING\n");
-                       alsa_stream->draining = 1;
-               } else {
-                       audio_info("DROPPING\n");
-                       alsa_stream->draining = 0;
-               }
-               if (alsa_stream->running) {
-                       err = bcm2835_audio_stop(alsa_stream);
-                       if (err != 0)
-                               audio_error(" Failed to STOP alsa device (%d)\n", err);
-                       alsa_stream->running = 0;
-               }
-               break;
-       default:
-               err = -EINVAL;
-       }
-
-       audio_info(" .. OUT\n");
-       return err;
-}
-
-/* pointer callback */
-static snd_pcm_uframes_t
-snd_bcm2835_pcm_pointer(struct snd_pcm_substream *substream)
-{
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       struct bcm2835_alsa_stream *alsa_stream = runtime->private_data;
-
-       audio_info(" .. IN\n");
-
-       audio_debug("pcm_pointer... (%d) hwptr=%d appl=%d pos=%d\n", 0,
-               frames_to_bytes(runtime, runtime->status->hw_ptr),
-               frames_to_bytes(runtime, runtime->control->appl_ptr),
-               alsa_stream->pos);
-
-       audio_info(" .. OUT\n");
-       return snd_pcm_indirect_playback_pointer(substream,
-               &alsa_stream->pcm_indirect,
-               alsa_stream->pos);
-}
-
-static int snd_bcm2835_pcm_lib_ioctl(struct snd_pcm_substream *substream,
-       unsigned int cmd, void *arg)
-{
-       int ret = snd_pcm_lib_ioctl(substream, cmd, arg);
-
-       audio_info(" .. substream=%p, cmd=%d, arg=%p (%x) ret=%d\n", substream,
-               cmd, arg, arg ? *(unsigned *) arg : 0, ret);
-       return ret;
-}
-
-/* operators */
-static struct snd_pcm_ops snd_bcm2835_playback_ops = {
-       .open = snd_bcm2835_playback_open,
-       .close = snd_bcm2835_playback_close,
-       .ioctl = snd_bcm2835_pcm_lib_ioctl,
-       .hw_params = snd_bcm2835_pcm_hw_params,
-       .hw_free = snd_bcm2835_pcm_hw_free,
-       .prepare = snd_bcm2835_pcm_prepare,
-       .trigger = snd_bcm2835_pcm_trigger,
-       .pointer = snd_bcm2835_pcm_pointer,
-       .ack = snd_bcm2835_pcm_ack,
-};
-
-static struct snd_pcm_ops snd_bcm2835_playback_spdif_ops = {
-       .open = snd_bcm2835_playback_spdif_open,
-       .close = snd_bcm2835_playback_close,
-       .ioctl = snd_bcm2835_pcm_lib_ioctl,
-       .hw_params = snd_bcm2835_pcm_hw_params,
-       .hw_free = snd_bcm2835_pcm_hw_free,
-       .prepare = snd_bcm2835_pcm_prepare,
-       .trigger = snd_bcm2835_pcm_trigger,
-       .pointer = snd_bcm2835_pcm_pointer,
-       .ack = snd_bcm2835_pcm_ack,
-};
-
-/* create a pcm device */
-int snd_bcm2835_new_pcm(struct bcm2835_chip *chip)
-{
-       struct snd_pcm *pcm;
-       int err;
-
-       audio_info(" .. IN\n");
-       mutex_init(&chip->audio_mutex);
-       if (mutex_lock_interruptible(&chip->audio_mutex)) {
-               audio_error("Interrupted whilst waiting for lock\n");
-               return -EINTR;
-       }
-       err = snd_pcm_new(chip->card, "bcm2835 ALSA", 0, MAX_SUBSTREAMS, 0, &pcm);
-       if (err < 0)
-               goto out;
-       pcm->private_data = chip;
-       strcpy(pcm->name, "bcm2835 ALSA");
-       chip->pcm = pcm;
-       chip->dest = AUDIO_DEST_AUTO;
-       chip->volume = alsa2chip(0);
-       chip->mute = CTRL_VOL_UNMUTE; /*disable mute on startup */
-       /* set operators */
-       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
-                       &snd_bcm2835_playback_ops);
-
-       /* pre-allocation of buffers */
-       /* NOTE: this may fail */
-       snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
-                                             snd_dma_continuous_data(GFP_KERNEL),
-                                             snd_bcm2835_playback_hw.buffer_bytes_max,
-                                             snd_bcm2835_playback_hw.buffer_bytes_max);
-
-
-out:
-       mutex_unlock(&chip->audio_mutex);
-       audio_info(" .. OUT\n");
-
-       return 0;
-}
-
-int snd_bcm2835_new_spdif_pcm(struct bcm2835_chip *chip)
-{
-       struct snd_pcm *pcm;
-       int err;
-
-       audio_info(" .. IN\n");
-       if (mutex_lock_interruptible(&chip->audio_mutex)) {
-               audio_error("Interrupted whilst waiting for lock\n");
-               return -EINTR;
-       }
-       err = snd_pcm_new(chip->card, "bcm2835 ALSA", 1, 1, 0, &pcm);
-       if (err < 0)
-               goto out;
-
-       pcm->private_data = chip;
-       strcpy(pcm->name, "bcm2835 IEC958/HDMI");
-       chip->pcm_spdif = pcm;
-       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
-                       &snd_bcm2835_playback_spdif_ops);
-
-       /* pre-allocation of buffers */
-       /* NOTE: this may fail */
-       snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
-               snd_dma_continuous_data(GFP_KERNEL),
-               snd_bcm2835_playback_spdif_hw.buffer_bytes_max, snd_bcm2835_playback_spdif_hw.buffer_bytes_max);
-out:
-       mutex_unlock(&chip->audio_mutex);
-       audio_info(" .. OUT\n");
-
-       return 0;
-}
diff --git a/drivers/staging/bcm2835-audio/bcm2835-vchiq.c b/drivers/staging/bcm2835-audio/bcm2835-vchiq.c
deleted file mode 100644 (file)
index df1f3dd..0000000
+++ /dev/null
@@ -1,910 +0,0 @@
-/*****************************************************************************
- * 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/device.h>
-#include <sound/core.h>
-#include <sound/initval.h>
-#include <sound/pcm.h>
-#include <linux/io.h>
-#include <linux/interrupt.h>
-#include <linux/fs.h>
-#include <linux/file.h>
-#include <linux/mm.h>
-#include <linux/syscalls.h>
-#include <linux/uaccess.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/atomic.h>
-#include <linux/module.h>
-#include <linux/completion.h>
-
-#include "bcm2835.h"
-
-/* ---- Include Files -------------------------------------------------------- */
-
-#include "interface/vchi/vchi.h"
-#include "vc_vchi_audioserv_defs.h"
-
-/* ---- Private Constants and Types ------------------------------------------ */
-
-#define BCM2835_AUDIO_STOP           0
-#define BCM2835_AUDIO_START          1
-#define BCM2835_AUDIO_WRITE          2
-
-/* Logging macros (for remapping to other logging mechanisms, i.e., printf) */
-#ifdef AUDIO_DEBUG_ENABLE
-#define LOG_ERR(fmt, arg...)   pr_err("%s:%d " fmt, __func__, __LINE__, ##arg)
-#define LOG_WARN(fmt, arg...)  pr_info("%s:%d " fmt, __func__, __LINE__, ##arg)
-#define LOG_INFO(fmt, arg...)  pr_info("%s:%d " fmt, __func__, __LINE__, ##arg)
-#define LOG_DBG(fmt, arg...)   pr_info("%s:%d " fmt, __func__, __LINE__, ##arg)
-#else
-#define LOG_ERR(fmt, arg...)   pr_err("%s:%d " fmt, __func__, __LINE__, ##arg)
-#define LOG_WARN(fmt, arg...)   no_printk(fmt, ##arg)
-#define LOG_INFO(fmt, arg...)   no_printk(fmt, ##arg)
-#define LOG_DBG(fmt, arg...)    no_printk(fmt, ##arg)
-#endif
-
-struct bcm2835_audio_instance {
-       unsigned int num_connections;
-       VCHI_SERVICE_HANDLE_T vchi_handle[VCHI_MAX_NUM_CONNECTIONS];
-       struct completion msg_avail_comp;
-       struct mutex vchi_mutex;
-       struct bcm2835_alsa_stream *alsa_stream;
-       int result;
-       short peer_version;
-};
-
-static bool force_bulk;
-
-/* ---- Private Variables ---------------------------------------------------- */
-
-/* ---- Private Function Prototypes ------------------------------------------ */
-
-/* ---- Private Functions ---------------------------------------------------- */
-
-static int bcm2835_audio_stop_worker(struct bcm2835_alsa_stream *alsa_stream);
-static int bcm2835_audio_start_worker(struct bcm2835_alsa_stream *alsa_stream);
-static int bcm2835_audio_write_worker(struct bcm2835_alsa_stream *alsa_stream,
-                                     unsigned int count, void *src);
-
-// Routine to send a message across a service
-
-static int
-bcm2835_vchi_msg_queue(VCHI_SERVICE_HANDLE_T handle,
-                      void *data,
-                      unsigned int size)
-{
-       return vchi_queue_kernel_message(handle,
-                                        data,
-                                        size);
-}
-
-static const u32 BCM2835_AUDIO_WRITE_COOKIE1 = ('B' << 24 | 'C' << 16 |
-                                               'M' << 8  | 'A');
-static const u32 BCM2835_AUDIO_WRITE_COOKIE2 = ('D' << 24 | 'A' << 16 |
-                                               'T' << 8  | 'A');
-
-struct bcm2835_audio_work {
-       struct work_struct my_work;
-       struct bcm2835_alsa_stream *alsa_stream;
-       int cmd;
-       void *src;
-       unsigned int count;
-};
-
-static void my_wq_function(struct work_struct *work)
-{
-       struct bcm2835_audio_work *w =
-               container_of(work, struct bcm2835_audio_work, my_work);
-       int ret = -9;
-
-       LOG_DBG(" .. IN %p:%d\n", w->alsa_stream, w->cmd);
-       switch (w->cmd) {
-       case BCM2835_AUDIO_START:
-               ret = bcm2835_audio_start_worker(w->alsa_stream);
-               break;
-       case BCM2835_AUDIO_STOP:
-               ret = bcm2835_audio_stop_worker(w->alsa_stream);
-               break;
-       case BCM2835_AUDIO_WRITE:
-               ret = bcm2835_audio_write_worker(w->alsa_stream, w->count,
-                                                w->src);
-               break;
-       default:
-               LOG_ERR(" Unexpected work: %p:%d\n", w->alsa_stream, w->cmd);
-               break;
-       }
-       kfree((void *) work);
-       LOG_DBG(" .. OUT %d\n", ret);
-}
-
-int bcm2835_audio_start(struct bcm2835_alsa_stream *alsa_stream)
-{
-       int ret = -1;
-
-       LOG_DBG(" .. IN\n");
-       if (alsa_stream->my_wq) {
-               struct bcm2835_audio_work *work;
-
-               work = kmalloc(sizeof(*work), GFP_ATOMIC);
-               /*--- Queue some work (item 1) ---*/
-               if (work) {
-                       INIT_WORK(&work->my_work, my_wq_function);
-                       work->alsa_stream = alsa_stream;
-                       work->cmd = BCM2835_AUDIO_START;
-                       if (queue_work(alsa_stream->my_wq, &work->my_work))
-                               ret = 0;
-               } else
-                       LOG_ERR(" .. Error: NULL work kmalloc\n");
-       }
-       LOG_DBG(" .. OUT %d\n", ret);
-       return ret;
-}
-
-int bcm2835_audio_stop(struct bcm2835_alsa_stream *alsa_stream)
-{
-       int ret = -1;
-
-       LOG_DBG(" .. IN\n");
-       if (alsa_stream->my_wq) {
-               struct bcm2835_audio_work *work;
-
-               work = kmalloc(sizeof(*work), GFP_ATOMIC);
-               /*--- Queue some work (item 1) ---*/
-               if (work) {
-                       INIT_WORK(&work->my_work, my_wq_function);
-                       work->alsa_stream = alsa_stream;
-                       work->cmd = BCM2835_AUDIO_STOP;
-                       if (queue_work(alsa_stream->my_wq, &work->my_work))
-                               ret = 0;
-               } else
-                       LOG_ERR(" .. Error: NULL work kmalloc\n");
-       }
-       LOG_DBG(" .. OUT %d\n", ret);
-       return ret;
-}
-
-int bcm2835_audio_write(struct bcm2835_alsa_stream *alsa_stream,
-                       unsigned int count, void *src)
-{
-       int ret = -1;
-
-       LOG_DBG(" .. IN\n");
-       if (alsa_stream->my_wq) {
-               struct bcm2835_audio_work *work;
-
-               work = kmalloc(sizeof(*work), GFP_ATOMIC);
-               /*--- Queue some work (item 1) ---*/
-               if (work) {
-                       INIT_WORK(&work->my_work, my_wq_function);
-                       work->alsa_stream = alsa_stream;
-                       work->cmd = BCM2835_AUDIO_WRITE;
-                       work->src = src;
-                       work->count = count;
-                       if (queue_work(alsa_stream->my_wq, &work->my_work))
-                               ret = 0;
-               } else
-                       LOG_ERR(" .. Error: NULL work kmalloc\n");
-       }
-       LOG_DBG(" .. OUT %d\n", ret);
-       return ret;
-}
-
-static void my_workqueue_init(struct bcm2835_alsa_stream *alsa_stream)
-{
-       alsa_stream->my_wq = alloc_workqueue("my_queue", WQ_HIGHPRI, 1);
-}
-
-static void my_workqueue_quit(struct bcm2835_alsa_stream *alsa_stream)
-{
-       if (alsa_stream->my_wq) {
-               flush_workqueue(alsa_stream->my_wq);
-               destroy_workqueue(alsa_stream->my_wq);
-               alsa_stream->my_wq = NULL;
-       }
-}
-
-static void audio_vchi_callback(void *param,
-                               const VCHI_CALLBACK_REASON_T reason,
-                               void *msg_handle)
-{
-       struct bcm2835_audio_instance *instance = param;
-       int status;
-       int msg_len;
-       struct vc_audio_msg m;
-
-       LOG_DBG(" .. IN instance=%p, handle=%p, alsa=%p, reason=%d, handle=%p\n",
-               instance, instance ? instance->vchi_handle[0] : NULL, instance ? instance->alsa_stream : NULL, reason, msg_handle);
-
-       if (reason != VCHI_CALLBACK_MSG_AVAILABLE)
-               return;
-
-       if (!instance) {
-               LOG_ERR(" .. instance is null\n");
-               BUG();
-               return;
-       }
-       if (!instance->vchi_handle[0]) {
-               LOG_ERR(" .. instance->vchi_handle[0] is null\n");
-               BUG();
-               return;
-       }
-       status = vchi_msg_dequeue(instance->vchi_handle[0],
-                                 &m, sizeof(m), &msg_len, VCHI_FLAGS_NONE);
-       if (m.type == VC_AUDIO_MSG_TYPE_RESULT) {
-               LOG_DBG(" .. instance=%p, m.type=VC_AUDIO_MSG_TYPE_RESULT, success=%d\n",
-                       instance, m.u.result.success);
-               instance->result = m.u.result.success;
-               complete(&instance->msg_avail_comp);
-       } else if (m.type == VC_AUDIO_MSG_TYPE_COMPLETE) {
-               struct bcm2835_alsa_stream *alsa_stream = instance->alsa_stream;
-
-               LOG_DBG(" .. instance=%p, m.type=VC_AUDIO_MSG_TYPE_COMPLETE, complete=%d\n",
-                       instance, m.u.complete.count);
-               if (m.u.complete.cookie1 != BCM2835_AUDIO_WRITE_COOKIE1 ||
-                   m.u.complete.cookie2 != BCM2835_AUDIO_WRITE_COOKIE2)
-                       LOG_ERR(" .. response is corrupt\n");
-               else if (alsa_stream) {
-                       atomic_add(m.u.complete.count,
-                                  &alsa_stream->retrieved);
-                       bcm2835_playback_fifo(alsa_stream);
-               } else {
-                       LOG_ERR(" .. unexpected alsa_stream=%p\n",
-                               alsa_stream);
-               }
-       } else {
-               LOG_ERR(" .. unexpected m.type=%d\n", m.type);
-       }
-       LOG_DBG(" .. OUT\n");
-}
-
-static struct bcm2835_audio_instance *
-vc_vchi_audio_init(VCHI_INSTANCE_T vchi_instance,
-                  VCHI_CONNECTION_T **vchi_connections,
-                  unsigned int num_connections)
-{
-       unsigned int i;
-       struct bcm2835_audio_instance *instance;
-       int status;
-
-       LOG_DBG("%s: start", __func__);
-
-       if (num_connections > VCHI_MAX_NUM_CONNECTIONS) {
-               LOG_ERR("%s: unsupported number of connections %u (max=%u)\n",
-                       __func__, num_connections, VCHI_MAX_NUM_CONNECTIONS);
-
-               return NULL;
-       }
-       /* Allocate memory for this instance */
-       instance = kmalloc(sizeof(*instance), GFP_KERNEL);
-       if (!instance)
-               return NULL;
-
-       memset(instance, 0, sizeof(*instance));
-       instance->num_connections = num_connections;
-
-       /* Create a lock for exclusive, serialized VCHI connection access */
-       mutex_init(&instance->vchi_mutex);
-       /* Open the VCHI service connections */
-       for (i = 0; i < num_connections; i++) {
-               SERVICE_CREATION_T params = {
-                       VCHI_VERSION_EX(VC_AUDIOSERV_VER, VC_AUDIOSERV_MIN_VER),
-                       VC_AUDIO_SERVER_NAME, // 4cc service code
-                       vchi_connections[i], // passed in fn pointers
-                       0, // rx fifo size (unused)
-                       0, // tx fifo size (unused)
-                       audio_vchi_callback, // service callback
-                       instance, // service callback parameter
-                       1, //TODO: remove VCOS_FALSE,   // unaligned bulk recieves
-                       1, //TODO: remove VCOS_FALSE,   // unaligned bulk transmits
-                       0 // want crc check on bulk transfers
-               };
-
-               LOG_DBG("%s: about to open %i\n", __func__, i);
-               status = vchi_service_open(vchi_instance, &params,
-                       &instance->vchi_handle[i]);
-               LOG_DBG("%s: opened %i: %p=%d\n", __func__, i, instance->vchi_handle[i], status);
-               if (status) {
-                       LOG_ERR("%s: failed to open VCHI service connection (status=%d)\n",
-                               __func__, status);
-
-                       goto err_close_services;
-               }
-               /* Finished with the service for now */
-               vchi_service_release(instance->vchi_handle[i]);
-       }
-
-       LOG_DBG("%s: okay\n", __func__);
-       return instance;
-
-err_close_services:
-       for (i = 0; i < instance->num_connections; i++) {
-               LOG_ERR("%s: closing %i: %p\n", __func__, i, instance->vchi_handle[i]);
-               if (instance->vchi_handle[i])
-                       vchi_service_close(instance->vchi_handle[i]);
-       }
-
-       kfree(instance);
-       LOG_ERR("%s: error\n", __func__);
-
-       return NULL;
-}
-
-static int vc_vchi_audio_deinit(struct bcm2835_audio_instance *instance)
-{
-       unsigned int i;
-
-       LOG_DBG(" .. IN\n");
-
-       if (!instance) {
-               LOG_ERR("%s: invalid handle %p\n", __func__, instance);
-
-               return -1;
-       }
-
-       LOG_DBG(" .. about to lock (%d)\n", instance->num_connections);
-       if (mutex_lock_interruptible(&instance->vchi_mutex)) {
-               LOG_DBG("Interrupted whilst waiting for lock on (%d)\n",
-                       instance->num_connections);
-               return -EINTR;
-       }
-
-       /* Close all VCHI service connections */
-       for (i = 0; i < instance->num_connections; i++) {
-               int status;
-
-               LOG_DBG(" .. %i:closing %p\n", i, instance->vchi_handle[i]);
-               vchi_service_use(instance->vchi_handle[i]);
-
-               status = vchi_service_close(instance->vchi_handle[i]);
-               if (status) {
-                       LOG_DBG("%s: failed to close VCHI service connection (status=%d)\n",
-                               __func__, status);
-               }
-       }
-
-       mutex_unlock(&instance->vchi_mutex);
-
-       kfree(instance);
-
-       LOG_DBG(" .. OUT\n");
-
-       return 0;
-}
-
-static int bcm2835_audio_open_connection(struct bcm2835_alsa_stream *alsa_stream)
-{
-       static VCHI_INSTANCE_T vchi_instance;
-       static VCHI_CONNECTION_T *vchi_connection;
-       static int initted;
-       struct bcm2835_audio_instance *instance =
-               (struct bcm2835_audio_instance *)alsa_stream->instance;
-       int ret;
-
-       LOG_DBG(" .. IN\n");
-
-       LOG_INFO("%s: start\n", __func__);
-       BUG_ON(instance);
-       if (instance) {
-               LOG_ERR("%s: VCHI instance already open (%p)\n",
-                       __func__, instance);
-               instance->alsa_stream = alsa_stream;
-               alsa_stream->instance = instance;
-               ret = 0; // xxx todo -1;
-               goto err_free_mem;
-       }
-
-       /* Initialize and create a VCHI connection */
-       if (!initted) {
-               ret = vchi_initialise(&vchi_instance);
-               if (ret) {
-                       LOG_ERR("%s: failed to initialise VCHI instance (ret=%d)\n",
-                               __func__, ret);
-
-                       ret = -EIO;
-                       goto err_free_mem;
-               }
-               ret = vchi_connect(NULL, 0, vchi_instance);
-               if (ret) {
-                       LOG_ERR("%s: failed to connect VCHI instance (ret=%d)\n",
-                               __func__, ret);
-
-                       ret = -EIO;
-                       goto err_free_mem;
-               }
-               initted = 1;
-       }
-
-       /* Initialize an instance of the audio service */
-       instance = vc_vchi_audio_init(vchi_instance, &vchi_connection, 1);
-
-       if (!instance) {
-               LOG_ERR("%s: failed to initialize audio service\n", __func__);
-
-               ret = -EPERM;
-               goto err_free_mem;
-       }
-
-       instance->alsa_stream = alsa_stream;
-       alsa_stream->instance = instance;
-
-       LOG_DBG(" success !\n");
-       ret = 0;
-err_free_mem:
-       LOG_DBG(" .. OUT\n");
-
-       return ret;
-}
-
-int bcm2835_audio_open(struct bcm2835_alsa_stream *alsa_stream)
-{
-       struct bcm2835_audio_instance *instance;
-       struct vc_audio_msg m;
-       int status;
-       int ret;
-
-       LOG_DBG(" .. IN\n");
-
-       my_workqueue_init(alsa_stream);
-
-       ret = bcm2835_audio_open_connection(alsa_stream);
-       if (ret) {
-               ret = -1;
-               goto exit;
-       }
-       instance = alsa_stream->instance;
-       LOG_DBG(" instance (%p)\n", instance);
-
-       if (mutex_lock_interruptible(&instance->vchi_mutex)) {
-               LOG_DBG("Interrupted whilst waiting for lock on (%d)\n", instance->num_connections);
-               return -EINTR;
-       }
-       vchi_service_use(instance->vchi_handle[0]);
-
-       m.type = VC_AUDIO_MSG_TYPE_OPEN;
-
-       /* Send the message to the videocore */
-       status = bcm2835_vchi_msg_queue(instance->vchi_handle[0],
-                                       &m, sizeof(m));
-
-       if (status) {
-               LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n",
-                       __func__, status);
-
-               ret = -1;
-               goto unlock;
-       }
-
-       ret = 0;
-
-unlock:
-       vchi_service_release(instance->vchi_handle[0]);
-       mutex_unlock(&instance->vchi_mutex);
-exit:
-       LOG_DBG(" .. OUT\n");
-       return ret;
-}
-
-static int bcm2835_audio_set_ctls_chan(struct bcm2835_alsa_stream *alsa_stream,
-                                      struct bcm2835_chip *chip)
-{
-       struct vc_audio_msg m;
-       struct bcm2835_audio_instance *instance = alsa_stream->instance;
-       int status;
-       int ret;
-
-       LOG_DBG(" .. IN\n");
-
-       LOG_INFO(" Setting ALSA dest(%d), volume(%d)\n",
-                chip->dest, chip->volume);
-
-       if (mutex_lock_interruptible(&instance->vchi_mutex)) {
-               LOG_DBG("Interrupted whilst waiting for lock on (%d)\n",
-                       instance->num_connections);
-               return -EINTR;
-       }
-       vchi_service_use(instance->vchi_handle[0]);
-
-       instance->result = -1;
-
-       m.type = VC_AUDIO_MSG_TYPE_CONTROL;
-       m.u.control.dest = chip->dest;
-       m.u.control.volume = chip->volume;
-
-       /* Create the message available completion */
-       init_completion(&instance->msg_avail_comp);
-
-       /* Send the message to the videocore */
-       status = bcm2835_vchi_msg_queue(instance->vchi_handle[0],
-                                       &m, sizeof(m));
-
-       if (status) {
-               LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n",
-                       __func__, status);
-
-               ret = -1;
-               goto unlock;
-       }
-
-       /* We are expecting a reply from the videocore */
-       wait_for_completion(&instance->msg_avail_comp);
-
-       if (instance->result) {
-               LOG_ERR("%s: result=%d\n", __func__, instance->result);
-
-               ret = -1;
-               goto unlock;
-       }
-
-       ret = 0;
-
-unlock:
-       vchi_service_release(instance->vchi_handle[0]);
-       mutex_unlock(&instance->vchi_mutex);
-
-       LOG_DBG(" .. OUT\n");
-       return ret;
-}
-
-int bcm2835_audio_set_ctls(struct bcm2835_chip *chip)
-{
-       int i;
-       int ret = 0;
-
-       LOG_DBG(" .. IN\n");
-       LOG_DBG(" Setting ALSA dest(%d), volume(%d)\n", chip->dest, chip->volume);
-
-       /* change ctls for all substreams */
-       for (i = 0; i < MAX_SUBSTREAMS; i++) {
-               if (chip->avail_substreams & (1 << i)) {
-                       if (!chip->alsa_stream[i]) {
-                               LOG_DBG(" No ALSA stream available?! %i:%p (%x)\n", i, chip->alsa_stream[i], chip->avail_substreams);
-                               ret = 0;
-                       } else if (bcm2835_audio_set_ctls_chan(chip->alsa_stream[i], chip) != 0) {
-                               LOG_ERR("Couldn't set the controls for stream %d\n", i);
-                               ret = -1;
-                       } else {
-                               LOG_DBG(" Controls set for stream %d\n", i);
-                       }
-               }
-       }
-       LOG_DBG(" .. OUT ret=%d\n", ret);
-       return ret;
-}
-
-int bcm2835_audio_set_params(struct bcm2835_alsa_stream *alsa_stream,
-                            unsigned int channels, unsigned int samplerate,
-                            unsigned int bps)
-{
-       struct vc_audio_msg m;
-       struct bcm2835_audio_instance *instance = alsa_stream->instance;
-       int status;
-       int ret;
-
-       LOG_DBG(" .. IN\n");
-
-       LOG_INFO(" Setting ALSA channels(%d), samplerate(%d), bits-per-sample(%d)\n",
-               channels, samplerate, bps);
-
-       /* resend ctls - alsa_stream may not have been open when first send */
-       ret = bcm2835_audio_set_ctls_chan(alsa_stream, alsa_stream->chip);
-       if (ret) {
-               LOG_ERR(" Alsa controls not supported\n");
-               return -EINVAL;
-       }
-
-       if (mutex_lock_interruptible(&instance->vchi_mutex)) {
-               LOG_DBG("Interrupted whilst waiting for lock on (%d)\n", instance->num_connections);
-               return -EINTR;
-       }
-       vchi_service_use(instance->vchi_handle[0]);
-
-       instance->result = -1;
-
-       m.type = VC_AUDIO_MSG_TYPE_CONFIG;
-       m.u.config.channels = channels;
-       m.u.config.samplerate = samplerate;
-       m.u.config.bps = bps;
-
-       /* Create the message available completion */
-       init_completion(&instance->msg_avail_comp);
-
-       /* Send the message to the videocore */
-       status = bcm2835_vchi_msg_queue(instance->vchi_handle[0],
-                                       &m, sizeof(m));
-
-       if (status) {
-               LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n",
-                       __func__, status);
-
-               ret = -1;
-               goto unlock;
-       }
-
-       /* We are expecting a reply from the videocore */
-       wait_for_completion(&instance->msg_avail_comp);
-
-       if (instance->result) {
-               LOG_ERR("%s: result=%d", __func__, instance->result);
-
-               ret = -1;
-               goto unlock;
-       }
-
-       ret = 0;
-
-unlock:
-       vchi_service_release(instance->vchi_handle[0]);
-       mutex_unlock(&instance->vchi_mutex);
-
-       LOG_DBG(" .. OUT\n");
-       return ret;
-}
-
-int bcm2835_audio_setup(struct bcm2835_alsa_stream *alsa_stream)
-{
-       LOG_DBG(" .. IN\n");
-
-       LOG_DBG(" .. OUT\n");
-
-       return 0;
-}
-
-static int bcm2835_audio_start_worker(struct bcm2835_alsa_stream *alsa_stream)
-{
-       struct vc_audio_msg m;
-       struct bcm2835_audio_instance *instance = alsa_stream->instance;
-       int status;
-       int ret;
-
-       LOG_DBG(" .. IN\n");
-
-       if (mutex_lock_interruptible(&instance->vchi_mutex)) {
-               LOG_DBG("Interrupted whilst waiting for lock on (%d)\n",
-                       instance->num_connections);
-               return -EINTR;
-       }
-       vchi_service_use(instance->vchi_handle[0]);
-
-       m.type = VC_AUDIO_MSG_TYPE_START;
-
-       /* Send the message to the videocore */
-       status = bcm2835_vchi_msg_queue(instance->vchi_handle[0],
-                                       &m, sizeof(m));
-
-       if (status) {
-               LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n",
-                       __func__, status);
-
-               ret = -1;
-               goto unlock;
-       }
-
-       ret = 0;
-
-unlock:
-       vchi_service_release(instance->vchi_handle[0]);
-       mutex_unlock(&instance->vchi_mutex);
-       LOG_DBG(" .. OUT\n");
-       return ret;
-}
-
-static int bcm2835_audio_stop_worker(struct bcm2835_alsa_stream *alsa_stream)
-{
-       struct vc_audio_msg m;
-       struct bcm2835_audio_instance *instance = alsa_stream->instance;
-       int status;
-       int ret;
-
-       LOG_DBG(" .. IN\n");
-
-       if (mutex_lock_interruptible(&instance->vchi_mutex)) {
-               LOG_DBG("Interrupted whilst waiting for lock on (%d)\n",
-                       instance->num_connections);
-               return -EINTR;
-       }
-       vchi_service_use(instance->vchi_handle[0]);
-
-       m.type = VC_AUDIO_MSG_TYPE_STOP;
-       m.u.stop.draining = alsa_stream->draining;
-
-       /* Send the message to the videocore */
-       status = bcm2835_vchi_msg_queue(instance->vchi_handle[0],
-                                       &m, sizeof(m));
-
-       if (status) {
-               LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n",
-                       __func__, status);
-
-               ret = -1;
-               goto unlock;
-       }
-
-       ret = 0;
-
-unlock:
-       vchi_service_release(instance->vchi_handle[0]);
-       mutex_unlock(&instance->vchi_mutex);
-       LOG_DBG(" .. OUT\n");
-       return ret;
-}
-
-int bcm2835_audio_close(struct bcm2835_alsa_stream *alsa_stream)
-{
-       struct vc_audio_msg m;
-       struct bcm2835_audio_instance *instance = alsa_stream->instance;
-       int status;
-       int ret;
-
-       LOG_DBG(" .. IN\n");
-
-       my_workqueue_quit(alsa_stream);
-
-       if (mutex_lock_interruptible(&instance->vchi_mutex)) {
-               LOG_DBG("Interrupted whilst waiting for lock on (%d)\n",
-                       instance->num_connections);
-               return -EINTR;
-       }
-       vchi_service_use(instance->vchi_handle[0]);
-
-       m.type = VC_AUDIO_MSG_TYPE_CLOSE;
-
-       /* Create the message available completion */
-       init_completion(&instance->msg_avail_comp);
-
-       /* Send the message to the videocore */
-       status = bcm2835_vchi_msg_queue(instance->vchi_handle[0],
-                                       &m, sizeof(m));
-
-       if (status) {
-               LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n",
-                       __func__, status);
-               ret = -1;
-               goto unlock;
-       }
-
-       /* We are expecting a reply from the videocore */
-       wait_for_completion(&instance->msg_avail_comp);
-
-       if (instance->result) {
-               LOG_ERR("%s: failed result (result=%d)\n",
-                       __func__, instance->result);
-
-               ret = -1;
-               goto unlock;
-       }
-
-       ret = 0;
-
-unlock:
-       vchi_service_release(instance->vchi_handle[0]);
-       mutex_unlock(&instance->vchi_mutex);
-
-       /* Stop the audio service */
-       vc_vchi_audio_deinit(instance);
-       alsa_stream->instance = NULL;
-
-       LOG_DBG(" .. OUT\n");
-       return ret;
-}
-
-static int bcm2835_audio_write_worker(struct bcm2835_alsa_stream *alsa_stream,
-                                     unsigned int count, void *src)
-{
-       struct vc_audio_msg m;
-       struct bcm2835_audio_instance *instance = alsa_stream->instance;
-       int status;
-       int ret;
-
-       LOG_DBG(" .. IN\n");
-
-       LOG_INFO(" Writing %d bytes from %p\n", count, src);
-
-       if (mutex_lock_interruptible(&instance->vchi_mutex)) {
-               LOG_DBG("Interrupted whilst waiting for lock on (%d)\n",
-                       instance->num_connections);
-               return -EINTR;
-       }
-       vchi_service_use(instance->vchi_handle[0]);
-
-       if (instance->peer_version == 0 &&
-                       vchi_get_peer_version(instance->vchi_handle[0], &instance->peer_version) == 0)
-               LOG_DBG("%s: client version %d connected\n", __func__, instance->peer_version);
-
-       m.type = VC_AUDIO_MSG_TYPE_WRITE;
-       m.u.write.count = count;
-       // old version uses bulk, new version uses control
-       m.u.write.max_packet = instance->peer_version < 2 || force_bulk ? 0 : 4000;
-       m.u.write.cookie1 = BCM2835_AUDIO_WRITE_COOKIE1;
-       m.u.write.cookie2 = BCM2835_AUDIO_WRITE_COOKIE2;
-       m.u.write.silence = src == NULL;
-
-       /* Send the message to the videocore */
-       status = bcm2835_vchi_msg_queue(instance->vchi_handle[0],
-                                       &m, sizeof(m));
-
-       if (status) {
-               LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n",
-                       __func__, status);
-
-               ret = -1;
-               goto unlock;
-       }
-       if (!m.u.write.silence) {
-               if (!m.u.write.max_packet) {
-                       /* Send the message to the videocore */
-                       status = vchi_bulk_queue_transmit(instance->vchi_handle[0],
-                               src, count,
-                               0 *
-                               VCHI_FLAGS_BLOCK_UNTIL_QUEUED
-                               +
-                               1 *
-                               VCHI_FLAGS_BLOCK_UNTIL_DATA_READ,
-                               NULL);
-               } else {
-                       while (count > 0) {
-                               int bytes = min((int) m.u.write.max_packet, (int) count);
-
-                               status = bcm2835_vchi_msg_queue(instance->vchi_handle[0],
-                                                               src, bytes);
-                               src = (char *)src + bytes;
-                               count -= bytes;
-                       }
-               }
-               if (status) {
-                       LOG_ERR("%s: failed on vchi_bulk_queue_transmit (status=%d)\n",
-                               __func__, status);
-
-                       ret = -1;
-                       goto unlock;
-               }
-       }
-       ret = 0;
-
-unlock:
-       vchi_service_release(instance->vchi_handle[0]);
-       mutex_unlock(&instance->vchi_mutex);
-       LOG_DBG(" .. OUT\n");
-       return ret;
-}
-
-/**
- * Returns all buffers from arm->vc
- */
-void bcm2835_audio_flush_buffers(struct bcm2835_alsa_stream *alsa_stream)
-{
-       LOG_DBG(" .. IN\n");
-       LOG_DBG(" .. OUT\n");
-}
-
-/**
- * Forces VC to flush(drop) its filled playback buffers and
- * return them the us. (VC->ARM)
- */
-void bcm2835_audio_flush_playback_buffers(struct bcm2835_alsa_stream *alsa_stream)
-{
-       LOG_DBG(" .. IN\n");
-       LOG_DBG(" .. OUT\n");
-}
-
-unsigned int bcm2835_audio_retrieve_buffers(struct bcm2835_alsa_stream *alsa_stream)
-{
-       unsigned int count = atomic_read(&alsa_stream->retrieved);
-
-       atomic_sub(count, &alsa_stream->retrieved);
-       return count;
-}
-
-module_param(force_bulk, bool, 0444);
-MODULE_PARM_DESC(force_bulk, "Force use of vchiq bulk for audio");
diff --git a/drivers/staging/bcm2835-audio/bcm2835.c b/drivers/staging/bcm2835-audio/bcm2835.c
deleted file mode 100644 (file)
index 3a5e528..0000000
+++ /dev/null
@@ -1,250 +0,0 @@
-/*****************************************************************************
- * 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/slab.h>
-#include <linux/module.h>
-#include <linux/of.h>
-
-#include "bcm2835.h"
-
-/* HACKY global pointers needed for successive probes to work : ssp
- * But compared against the changes we will have to do in VC audio_ipc code
- * to export 8 audio_ipc devices as a single IPC device and then monitor all
- * four devices in a thread, this gets things done quickly and should be easier
- * to debug if we run into issues
- */
-
-static struct snd_card *g_card;
-static struct bcm2835_chip *g_chip;
-
-static int snd_bcm2835_free(struct bcm2835_chip *chip)
-{
-       kfree(chip);
-       return 0;
-}
-
-/* component-destructor
- * (see "Management of Cards and Components")
- */
-static int snd_bcm2835_dev_free(struct snd_device *device)
-{
-       return snd_bcm2835_free(device->device_data);
-}
-
-/* chip-specific constructor
- * (see "Management of Cards and Components")
- */
-static int snd_bcm2835_create(struct snd_card *card,
-                             struct platform_device *pdev,
-                             struct bcm2835_chip **rchip)
-{
-       struct bcm2835_chip *chip;
-       int err;
-       static struct snd_device_ops ops = {
-               .dev_free = snd_bcm2835_dev_free,
-       };
-
-       *rchip = NULL;
-
-       chip = kzalloc(sizeof(*chip), GFP_KERNEL);
-       if (!chip)
-               return -ENOMEM;
-
-       chip->card = card;
-
-       err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
-       if (err < 0) {
-               snd_bcm2835_free(chip);
-               return err;
-       }
-
-       *rchip = chip;
-       return 0;
-}
-
-static int snd_bcm2835_alsa_probe_dt(struct platform_device *pdev)
-{
-       struct device *dev = &pdev->dev;
-       struct bcm2835_chip *chip;
-       struct snd_card *card;
-       u32 numchans;
-       int err, i;
-
-       err = of_property_read_u32(dev->of_node, "brcm,pwm-channels",
-                                  &numchans);
-       if (err) {
-               dev_err(dev, "Failed to get DT property 'brcm,pwm-channels'");
-               return err;
-       }
-
-       if (numchans == 0 || numchans > MAX_SUBSTREAMS) {
-               numchans = MAX_SUBSTREAMS;
-               dev_warn(dev, "Illegal 'brcm,pwm-channels' value, will use %u\n",
-                        numchans);
-       }
-
-       err = snd_card_new(&pdev->dev, -1, NULL, THIS_MODULE, 0, &card);
-       if (err) {
-               dev_err(dev, "Failed to create soundcard structure\n");
-               return err;
-       }
-
-       snd_card_set_dev(card, dev);
-       strcpy(card->driver, "bcm2835");
-       strcpy(card->shortname, "bcm2835 ALSA");
-       sprintf(card->longname, "%s", card->shortname);
-
-       err = snd_bcm2835_create(card, pdev, &chip);
-       if (err < 0) {
-               dev_err(dev, "Failed to create bcm2835 chip\n");
-               goto err_free;
-       }
-
-       err = snd_bcm2835_new_pcm(chip);
-       if (err < 0) {
-               dev_err(dev, "Failed to create new bcm2835 pcm device\n");
-               goto err_free;
-       }
-
-       err = snd_bcm2835_new_spdif_pcm(chip);
-       if (err < 0) {
-               dev_err(dev, "Failed to create new bcm2835 spdif pcm device\n");
-               goto err_free;
-       }
-
-       err = snd_bcm2835_new_ctl(chip);
-       if (err < 0) {
-               dev_err(dev, "Failed to create new bcm2835 ctl\n");
-               goto err_free;
-       }
-
-       for (i = 0; i < numchans; i++) {
-               chip->avail_substreams |= (1 << i);
-               chip->pdev[i] = pdev;
-       }
-
-       err = snd_card_register(card);
-       if (err) {
-               dev_err(dev, "Failed to register bcm2835 ALSA card\n");
-               goto err_free;
-       }
-
-       g_card = card;
-       g_chip = chip;
-       platform_set_drvdata(pdev, card);
-       audio_info("bcm2835 ALSA card created with %u channels\n", numchans);
-
-       return 0;
-
-err_free:
-       snd_card_free(card);
-
-       return err;
-}
-
-static int snd_bcm2835_alsa_remove(struct platform_device *pdev)
-{
-       int idx;
-       void *drv_data;
-
-       drv_data = platform_get_drvdata(pdev);
-
-       if (drv_data == (void *)g_card) {
-               /* This is the card device */
-               snd_card_free((struct snd_card *)drv_data);
-               g_card = NULL;
-               g_chip = NULL;
-       } else {
-               idx = (int)(long)drv_data;
-               if (g_card) {
-                       BUG_ON(!g_chip);
-                       /* We pass chip device numbers in audio ipc devices
-                        * other than the one we registered our card with
-                        */
-                       idx = (int)(long)drv_data;
-                       BUG_ON(!idx || idx > MAX_SUBSTREAMS);
-                       g_chip->avail_substreams &= ~(1 << idx);
-                       /* There should be atleast one substream registered
-                        * after we are done here, as it wil be removed when
-                        * the *remove* is called for the card device
-                        */
-                       BUG_ON(!g_chip->avail_substreams);
-               }
-       }
-
-       platform_set_drvdata(pdev, NULL);
-
-       return 0;
-}
-
-#ifdef CONFIG_PM
-
-static int snd_bcm2835_alsa_suspend(struct platform_device *pdev,
-                                   pm_message_t state)
-{
-       return 0;
-}
-
-static int snd_bcm2835_alsa_resume(struct platform_device *pdev)
-{
-       return 0;
-}
-
-#endif
-
-static const struct of_device_id snd_bcm2835_of_match_table[] = {
-       { .compatible = "brcm,bcm2835-audio",},
-       {},
-};
-MODULE_DEVICE_TABLE(of, snd_bcm2835_of_match_table);
-
-static struct platform_driver bcm2835_alsa0_driver = {
-       .probe = snd_bcm2835_alsa_probe_dt,
-       .remove = snd_bcm2835_alsa_remove,
-#ifdef CONFIG_PM
-       .suspend = snd_bcm2835_alsa_suspend,
-       .resume = snd_bcm2835_alsa_resume,
-#endif
-       .driver = {
-               .name = "bcm2835_AUD0",
-               .owner = THIS_MODULE,
-               .of_match_table = snd_bcm2835_of_match_table,
-       },
-};
-
-static int bcm2835_alsa_device_init(void)
-{
-       int retval;
-
-       retval = platform_driver_register(&bcm2835_alsa0_driver);
-       if (retval)
-               pr_err("Error registering bcm2835_alsa0_driver %d .\n", retval);
-
-       return retval;
-}
-
-static void bcm2835_alsa_device_exit(void)
-{
-       platform_driver_unregister(&bcm2835_alsa0_driver);
-}
-
-late_initcall(bcm2835_alsa_device_init);
-module_exit(bcm2835_alsa_device_exit);
-
-MODULE_AUTHOR("Dom Cobley");
-MODULE_DESCRIPTION("Alsa driver for BCM2835 chip");
-MODULE_LICENSE("GPL");
diff --git a/drivers/staging/bcm2835-audio/bcm2835.h b/drivers/staging/bcm2835-audio/bcm2835.h
deleted file mode 100644 (file)
index 2f9d1c9..0000000
+++ /dev/null
@@ -1,170 +0,0 @@
-/*****************************************************************************
- * 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.
- *****************************************************************************/
-
-#ifndef __SOUND_ARM_BCM2835_H
-#define __SOUND_ARM_BCM2835_H
-
-#include <linux/device.h>
-#include <linux/list.h>
-#include <linux/interrupt.h>
-#include <linux/wait.h>
-#include <sound/core.h>
-#include <sound/initval.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/pcm-indirect.h>
-#include <linux/workqueue.h>
-
-/*
- * #define AUDIO_DEBUG_ENABLE
- * #define AUDIO_VERBOSE_DEBUG_ENABLE
- */
-
-/* Debug macros */
-
-#ifdef AUDIO_DEBUG_ENABLE
-#ifdef AUDIO_VERBOSE_DEBUG_ENABLE
-
-#define audio_debug(fmt, arg...) \
-       pr_info("%s:%d " fmt, __func__, __LINE__, ##arg)
-
-#define audio_info(fmt, arg...) \
-       pr_info("%s:%d " fmt, __func__, __LINE__, ##arg)
-
-#else
-
-#define audio_debug(fmt, arg...)
-
-#define audio_info(fmt, arg...)
-
-#endif /* AUDIO_VERBOSE_DEBUG_ENABLE */
-
-#else
-
-#define audio_debug(fmt, arg...)
-
-#define audio_info(fmt, arg...)
-
-#endif /* AUDIO_DEBUG_ENABLE */
-
-#define audio_error(fmt, arg...) \
-       pr_err("%s:%d " fmt, __func__, __LINE__, ##arg)
-
-#define audio_warning(fmt, arg...) \
-       pr_warn("%s:%d " fmt, __func__, __LINE__, ##arg)
-
-#define audio_alert(fmt, arg...) \
-       pr_alert("%s:%d " fmt, __func__, __LINE__, ##arg)
-
-#define MAX_SUBSTREAMS   (8)
-#define AVAIL_SUBSTREAMS_MASK  (0xff)
-
-enum {
-       CTRL_VOL_MUTE,
-       CTRL_VOL_UNMUTE
-};
-
-/* macros for alsa2chip and chip2alsa, instead of functions */
-
-// convert alsa to chip volume (defined as macro rather than function call)
-#define alsa2chip(vol) (uint)(-(((vol) << 8) / 100))
-
-// convert chip to alsa volume
-#define chip2alsa(vol) -(((vol) * 100) >> 8)
-
-/* Some constants for values .. */
-enum snd_bcm2835_route {
-       AUDIO_DEST_AUTO = 0,
-       AUDIO_DEST_HEADPHONES = 1,
-       AUDIO_DEST_HDMI = 2,
-       AUDIO_DEST_MAX,
-};
-
-enum snd_bcm2835_ctrl {
-       PCM_PLAYBACK_VOLUME,
-       PCM_PLAYBACK_MUTE,
-       PCM_PLAYBACK_DEVICE,
-};
-
-/* definition of the chip-specific record */
-struct bcm2835_chip {
-       struct snd_card *card;
-       struct snd_pcm *pcm;
-       struct snd_pcm *pcm_spdif;
-       /* Bitmat for valid reg_base and irq numbers */
-       unsigned int avail_substreams;
-       struct platform_device *pdev[MAX_SUBSTREAMS];
-       struct bcm2835_alsa_stream *alsa_stream[MAX_SUBSTREAMS];
-
-       int volume;
-       int old_volume; /* stores the volume value whist muted */
-       int dest;
-       int mute;
-
-       unsigned int opened;
-       unsigned int spdif_status;
-       struct mutex audio_mutex;
-};
-
-struct bcm2835_alsa_stream {
-       struct bcm2835_chip *chip;
-       struct snd_pcm_substream *substream;
-       struct snd_pcm_indirect pcm_indirect;
-
-       struct semaphore buffers_update_sem;
-       struct semaphore control_sem;
-       spinlock_t lock;
-       volatile unsigned int control;
-       volatile unsigned int status;
-
-       int open;
-       int running;
-       int draining;
-
-       int channels;
-       int params_rate;
-       int pcm_format_width;
-
-       unsigned int pos;
-       unsigned int buffer_size;
-       unsigned int period_size;
-
-       atomic_t retrieved;
-       struct bcm2835_audio_instance *instance;
-       struct workqueue_struct *my_wq;
-       int idx;
-};
-
-int snd_bcm2835_new_ctl(struct bcm2835_chip *chip);
-int snd_bcm2835_new_pcm(struct bcm2835_chip *chip);
-int snd_bcm2835_new_spdif_pcm(struct bcm2835_chip *chip);
-
-int bcm2835_audio_open(struct bcm2835_alsa_stream *alsa_stream);
-int bcm2835_audio_close(struct bcm2835_alsa_stream *alsa_stream);
-int bcm2835_audio_set_params(struct bcm2835_alsa_stream *alsa_stream,
-                            unsigned int channels, unsigned int samplerate,
-                            unsigned int bps);
-int bcm2835_audio_setup(struct bcm2835_alsa_stream *alsa_stream);
-int bcm2835_audio_start(struct bcm2835_alsa_stream *alsa_stream);
-int bcm2835_audio_stop(struct bcm2835_alsa_stream *alsa_stream);
-int bcm2835_audio_set_ctls(struct bcm2835_chip *chip);
-int bcm2835_audio_write(struct bcm2835_alsa_stream *alsa_stream,
-                       unsigned int count,
-                       void *src);
-void bcm2835_playback_fifo(struct bcm2835_alsa_stream *alsa_stream);
-unsigned int bcm2835_audio_retrieve_buffers(struct bcm2835_alsa_stream *alsa_stream);
-void bcm2835_audio_flush_buffers(struct bcm2835_alsa_stream *alsa_stream);
-void bcm2835_audio_flush_playback_buffers(struct bcm2835_alsa_stream *alsa_stream);
-
-#endif /* __SOUND_ARM_BCM2835_H */
diff --git a/drivers/staging/bcm2835-audio/vc_vchi_audioserv_defs.h b/drivers/staging/bcm2835-audio/vc_vchi_audioserv_defs.h
deleted file mode 100644 (file)
index da96f1b..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-/*****************************************************************************
- * 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.
- *****************************************************************************/
-
-#ifndef _VC_AUDIO_DEFS_H_
-#define _VC_AUDIO_DEFS_H_
-
-#define VC_AUDIOSERV_MIN_VER 1
-#define VC_AUDIOSERV_VER 2
-
-/* FourCC code used for VCHI connection */
-#define VC_AUDIO_SERVER_NAME  MAKE_FOURCC("AUDS")
-
-/*
- *  List of screens that are currently supported
- *  All message types supported for HOST->VC direction
- */
-
-enum vc_audio_msg_type {
-       VC_AUDIO_MSG_TYPE_RESULT, // Generic result
-       VC_AUDIO_MSG_TYPE_COMPLETE, // Generic result
-       VC_AUDIO_MSG_TYPE_CONFIG, // Configure audio
-       VC_AUDIO_MSG_TYPE_CONTROL, // Configure audio
-       VC_AUDIO_MSG_TYPE_OPEN, // Configure audio
-       VC_AUDIO_MSG_TYPE_CLOSE, // Configure audio
-       VC_AUDIO_MSG_TYPE_START, // Configure audio
-       VC_AUDIO_MSG_TYPE_STOP, // Configure audio
-       VC_AUDIO_MSG_TYPE_WRITE, // Configure audio
-       VC_AUDIO_MSG_TYPE_MAX
-};
-
-/* configure the audio */
-
-struct vc_audio_config {
-       u32 channels;
-       u32 samplerate;
-       u32 bps;
-};
-
-struct vc_audio_control {
-       u32 volume;
-       u32 dest;
-};
-
-struct vc_audio_open {
-       u32 dummy;
-};
-
-struct vc_audio_close {
-       u32 dummy;
-};
-
-struct vc_audio_start {
-       u32 dummy;
-};
-
-struct vc_audio_stop {
-       u32 draining;
-};
-
-/* configure the write audio samples */
-struct vc_audio_write {
-       u32 count; // in bytes
-       u32 cookie1;
-       u32 cookie2;
-       s16 silence;
-       s16 max_packet;
-};
-
-/* Generic result for a request (VC->HOST) */
-struct vc_audio_result {
-       s32 success; // Success value
-};
-
-/* Generic result for a request (VC->HOST) */
-struct vc_audio_complete {
-       s32 count; // Success value
-       u32 cookie1;
-       u32 cookie2;
-};
-
-/* Message header for all messages in HOST->VC direction */
-struct vc_audio_msg {
-       s32 type; /* Message type (VC_AUDIO_MSG_TYPE) */
-       union {
-               struct vc_audio_config config;
-               struct vc_audio_control control;
-               struct vc_audio_open open;
-               struct vc_audio_close close;
-               struct vc_audio_start start;
-               struct vc_audio_stop stop;
-               struct vc_audio_write write;
-               struct vc_audio_result result;
-               struct vc_audio_complete complete;
-       } u;
-};
-
-#endif /* _VC_AUDIO_DEFS_H_ */
index e61e4ca064a8ab43b4dc4954b22fa3a52310379f..ff44897d30d225fc2865af371c70f14bb25a5cd3 100644 (file)
@@ -1,4 +1,4 @@
-config BCM2835_VCHIQ
+menuconfig BCM2835_VCHIQ
        tristate "Videocore VCHIQ"
        depends on HAS_DMA
        depends on RASPBERRYPI_FIRMWARE || (COMPILE_TEST && !RASPBERRYPI_FIRMWARE)
@@ -8,3 +8,10 @@ config BCM2835_VCHIQ
                BCM2835 family of products.
                Defaults to Y when the Broadcom Videocore services
                are included in the build, N otherwise.
+
+if BCM2835_VCHIQ
+
+source "drivers/staging/vc04_services/bcm2835-audio/Kconfig"
+
+endif
+
index 1a9e742ee40d2ef152817999ad1c931357e793df..d8058d30550f96b7d123898a9f6ac49bad56ce63 100644 (file)
@@ -10,5 +10,7 @@ vchiq-objs := \
    interface/vchiq_arm/vchiq_util.o \
    interface/vchiq_arm/vchiq_connected.o \
 
+obj-$(CONFIG_SND_BCM2835)      += bcm2835-audio/
+
 ccflags-y += -DVCOS_VERIFY_BKPTS=1 -Idrivers/staging/vc04_services -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000
 
diff --git a/drivers/staging/vc04_services/bcm2835-audio/Kconfig b/drivers/staging/vc04_services/bcm2835-audio/Kconfig
new file mode 100644 (file)
index 0000000..32a2ff9
--- /dev/null
@@ -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
+
diff --git a/drivers/staging/vc04_services/bcm2835-audio/Makefile b/drivers/staging/vc04_services/bcm2835-audio/Makefile
new file mode 100644 (file)
index 0000000..d7b88d1
--- /dev/null
@@ -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 -D__VCCOREVER__=0x04000000
+
diff --git a/drivers/staging/vc04_services/bcm2835-audio/TODO b/drivers/staging/vc04_services/bcm2835-audio/TODO
new file mode 100644 (file)
index 0000000..73d41fa
--- /dev/null
@@ -0,0 +1,29 @@
+*****************************************************************************
+*                                                                           *
+*                           TODO: BCM2835-AUDIO                             *
+*                                                                           *
+*****************************************************************************
+
+
+1) Document the device tree node
+
+The downstream tree(the tree that the driver was imported from) at
+http://www.github.com/raspberrypi/linux uses this node:
+
+audio: audio {
+       compatible = "brcm,bcm2835-audio";
+       brcm,pwm-channels = <8>;
+};
+
+Since the driver requires the use of VCHIQ, it may be useful to have a link
+in the device tree to the VCHIQ driver.
+
+2) Gracefully handle the case where VCHIQ is missing from the device tree or
+it has not been initialized yet.
+
+3) Review error handling and remove duplicate code.
+
+4) Cleanup the logging mechanism.  The driver should probably be using the
+standard kernel logging mechanisms such as dev_info, dev_dbg, and friends.
+
+5) Fix the remaining checkpatch.pl errors and warnings.
diff --git a/drivers/staging/vc04_services/bcm2835-audio/bcm2835-ctl.c b/drivers/staging/vc04_services/bcm2835-audio/bcm2835-ctl.c
new file mode 100644 (file)
index 0000000..a4ffa1b
--- /dev/null
@@ -0,0 +1,345 @@
+/*****************************************************************************
+ * 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;
+}
diff --git a/drivers/staging/vc04_services/bcm2835-audio/bcm2835-pcm.c b/drivers/staging/vc04_services/bcm2835-audio/bcm2835-pcm.c
new file mode 100644 (file)
index 0000000..9e79236
--- /dev/null
@@ -0,0 +1,554 @@
+/*****************************************************************************
+ * 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/interrupt.h>
+#include <linux/slab.h>
+
+#include <sound/asoundef.h>
+
+#include "bcm2835.h"
+
+/* hardware definition */
+static struct snd_pcm_hardware snd_bcm2835_playback_hw = {
+       .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+       SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
+       .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+       .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+       .rate_min = 8000,
+       .rate_max = 48000,
+       .channels_min = 1,
+       .channels_max = 2,
+       .buffer_bytes_max = 128 * 1024,
+       .period_bytes_min = 1 * 1024,
+       .period_bytes_max = 128 * 1024,
+       .periods_min = 1,
+       .periods_max = 128,
+};
+
+static struct snd_pcm_hardware snd_bcm2835_playback_spdif_hw = {
+       .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER |
+       SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
+       .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_44100 |
+       SNDRV_PCM_RATE_48000,
+       .rate_min = 44100,
+       .rate_max = 48000,
+       .channels_min = 2,
+       .channels_max = 2,
+       .buffer_bytes_max = 128 * 1024,
+       .period_bytes_min = 1 * 1024,
+       .period_bytes_max = 128 * 1024,
+       .periods_min = 1,
+       .periods_max = 128,
+};
+
+static void snd_bcm2835_playback_free(struct snd_pcm_runtime *runtime)
+{
+       audio_info("Freeing up alsa stream here ..\n");
+       if (runtime->private_data)
+               kfree(runtime->private_data);
+       runtime->private_data = NULL;
+}
+
+void bcm2835_playback_fifo(struct bcm2835_alsa_stream *alsa_stream)
+{
+       unsigned int consumed = 0;
+       int new_period = 0;
+
+       audio_info(" .. IN\n");
+
+       audio_info("alsa_stream=%p substream=%p\n", alsa_stream,
+               alsa_stream ? alsa_stream->substream : 0);
+
+       if (alsa_stream->open)
+               consumed = bcm2835_audio_retrieve_buffers(alsa_stream);
+
+       /* We get called only if playback was triggered, So, the number of buffers we retrieve in
+        * each iteration are the buffers that have been played out already
+        */
+
+       if (alsa_stream->period_size) {
+               if ((alsa_stream->pos / alsa_stream->period_size) !=
+                       ((alsa_stream->pos + consumed) / alsa_stream->period_size))
+                       new_period = 1;
+       }
+       audio_debug("updating pos cur: %d + %d max:%d period_bytes:%d, hw_ptr: %d new_period:%d\n",
+               alsa_stream->pos,
+               consumed,
+               alsa_stream->buffer_size,
+               (int) (alsa_stream->period_size * alsa_stream->substream->runtime->periods),
+               frames_to_bytes(alsa_stream->substream->runtime, alsa_stream->substream->runtime->status->hw_ptr),
+               new_period);
+       if (alsa_stream->buffer_size) {
+               alsa_stream->pos += consumed &~(1 << 30);
+               alsa_stream->pos %= alsa_stream->buffer_size;
+       }
+
+       if (alsa_stream->substream) {
+               if (new_period)
+                       snd_pcm_period_elapsed(alsa_stream->substream);
+       } else {
+               audio_warning(" unexpected NULL substream\n");
+       }
+       audio_info(" .. OUT\n");
+}
+
+/* open callback */
+static int snd_bcm2835_playback_open_generic(
+       struct snd_pcm_substream *substream, int spdif)
+{
+       struct bcm2835_chip *chip = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct bcm2835_alsa_stream *alsa_stream;
+       int idx;
+       int err;
+
+       audio_info(" .. IN (%d)\n", substream->number);
+
+       if (mutex_lock_interruptible(&chip->audio_mutex)) {
+               audio_error("Interrupted whilst waiting for lock\n");
+               return -EINTR;
+       }
+       audio_info("Alsa open (%d)\n", substream->number);
+       idx = substream->number;
+
+       if (spdif && chip->opened) {
+               err = -EBUSY;
+               goto out;
+       } else if (!spdif && (chip->opened & (1 << idx))) {
+               err = -EBUSY;
+               goto out;
+       }
+       if (idx >= MAX_SUBSTREAMS) {
+               audio_error
+                       ("substream(%d) device doesn't exist max(%d) substreams allowed\n",
+                       idx, MAX_SUBSTREAMS);
+               err = -ENODEV;
+               goto out;
+       }
+
+       /* Check if we are ready */
+       if (!(chip->avail_substreams & (1 << idx))) {
+               /* We are not ready yet */
+               audio_error("substream(%d) device is not ready yet\n", idx);
+               err = -EAGAIN;
+               goto out;
+       }
+
+       alsa_stream = kzalloc(sizeof(*alsa_stream), GFP_KERNEL);
+       if (!alsa_stream) {
+               err = -ENOMEM;
+               goto out;
+       }
+
+       /* Initialise alsa_stream */
+       alsa_stream->chip = chip;
+       alsa_stream->substream = substream;
+       alsa_stream->idx = idx;
+
+       sema_init(&alsa_stream->buffers_update_sem, 0);
+       sema_init(&alsa_stream->control_sem, 0);
+       spin_lock_init(&alsa_stream->lock);
+
+       err = bcm2835_audio_open(alsa_stream);
+       if (err) {
+               kfree(alsa_stream);
+               goto out;
+       }
+       runtime->private_data = alsa_stream;
+       runtime->private_free = snd_bcm2835_playback_free;
+       if (spdif) {
+               runtime->hw = snd_bcm2835_playback_spdif_hw;
+       } else {
+               /* clear spdif status, as we are not in spdif mode */
+               chip->spdif_status = 0;
+               runtime->hw = snd_bcm2835_playback_hw;
+       }
+       /* minimum 16 bytes alignment (for vchiq bulk transfers) */
+       snd_pcm_hw_constraint_step(runtime,
+                                  0,
+                                  SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
+                                  16);
+
+       chip->alsa_stream[idx] = alsa_stream;
+
+       chip->opened |= (1 << idx);
+       alsa_stream->open = 1;
+       alsa_stream->draining = 1;
+
+out:
+       mutex_unlock(&chip->audio_mutex);
+
+       audio_info(" .. OUT =%d\n", err);
+
+       return err;
+}
+
+static int snd_bcm2835_playback_open(struct snd_pcm_substream *substream)
+{
+       return snd_bcm2835_playback_open_generic(substream, 0);
+}
+
+static int snd_bcm2835_playback_spdif_open(struct snd_pcm_substream *substream)
+{
+       return snd_bcm2835_playback_open_generic(substream, 1);
+}
+
+/* close callback */
+static int snd_bcm2835_playback_close(struct snd_pcm_substream *substream)
+{
+       /* the hardware-specific codes will be here */
+
+       struct bcm2835_chip *chip;
+       struct snd_pcm_runtime *runtime;
+       struct bcm2835_alsa_stream *alsa_stream;
+
+       audio_info(" .. IN\n");
+
+       chip = snd_pcm_substream_chip(substream);
+       if (mutex_lock_interruptible(&chip->audio_mutex)) {
+               audio_error("Interrupted whilst waiting for lock\n");
+               return -EINTR;
+       }
+       runtime = substream->runtime;
+       alsa_stream = runtime->private_data;
+
+       audio_info("Alsa close\n");
+
+       /*
+        * Call stop if it's still running. This happens when app
+        * is force killed and we don't get a stop trigger.
+        */
+       if (alsa_stream->running) {
+               int err;
+               err = bcm2835_audio_stop(alsa_stream);
+               alsa_stream->running = 0;
+               if (err)
+                       audio_error(" Failed to STOP alsa device\n");
+       }
+
+       alsa_stream->period_size = 0;
+       alsa_stream->buffer_size = 0;
+
+       if (alsa_stream->open) {
+               alsa_stream->open = 0;
+               bcm2835_audio_close(alsa_stream);
+       }
+       if (alsa_stream->chip)
+               alsa_stream->chip->alsa_stream[alsa_stream->idx] = NULL;
+       /*
+        * Do not free up alsa_stream here, it will be freed up by
+        * runtime->private_free callback we registered in *_open above
+        */
+
+       chip->opened &= ~(1 << substream->number);
+
+       mutex_unlock(&chip->audio_mutex);
+       audio_info(" .. OUT\n");
+
+       return 0;
+}
+
+/* hw_params callback */
+static int snd_bcm2835_pcm_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct bcm2835_alsa_stream *alsa_stream = runtime->private_data;
+       int err;
+
+       audio_info(" .. IN\n");
+
+       err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+       if (err < 0) {
+               audio_error
+                       (" pcm_lib_malloc failed to allocated pages for buffers\n");
+               return err;
+       }
+
+       alsa_stream->channels = params_channels(params);
+       alsa_stream->params_rate = params_rate(params);
+       alsa_stream->pcm_format_width = snd_pcm_format_width(params_format(params));
+       audio_info(" .. OUT\n");
+
+       return err;
+}
+
+/* hw_free callback */
+static int snd_bcm2835_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+       audio_info(" .. IN\n");
+       return snd_pcm_lib_free_pages(substream);
+}
+
+/* prepare callback */
+static int snd_bcm2835_pcm_prepare(struct snd_pcm_substream *substream)
+{
+       struct bcm2835_chip *chip = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct bcm2835_alsa_stream *alsa_stream = runtime->private_data;
+       int channels;
+       int err;
+
+       audio_info(" .. IN\n");
+
+       if (mutex_lock_interruptible(&chip->audio_mutex))
+               return -EINTR;
+
+       /* notify the vchiq that it should enter spdif passthrough mode by
+        * setting channels=0 (see
+        * https://github.com/raspberrypi/linux/issues/528) */
+       if (chip->spdif_status & IEC958_AES0_NONAUDIO)
+               channels = 0;
+       else
+               channels = alsa_stream->channels;
+
+       err = bcm2835_audio_set_params(alsa_stream, channels,
+               alsa_stream->params_rate,
+               alsa_stream->pcm_format_width);
+       if (err < 0)
+               audio_error(" error setting hw params\n");
+
+
+       bcm2835_audio_setup(alsa_stream);
+
+       /* in preparation of the stream, set the controls (volume level) of the stream */
+       bcm2835_audio_set_ctls(alsa_stream->chip);
+
+
+       memset(&alsa_stream->pcm_indirect, 0, sizeof(alsa_stream->pcm_indirect));
+
+       alsa_stream->pcm_indirect.hw_buffer_size =
+               alsa_stream->pcm_indirect.sw_buffer_size =
+               snd_pcm_lib_buffer_bytes(substream);
+
+       alsa_stream->buffer_size = snd_pcm_lib_buffer_bytes(substream);
+       alsa_stream->period_size = snd_pcm_lib_period_bytes(substream);
+       alsa_stream->pos = 0;
+
+       audio_debug("buffer_size=%d, period_size=%d pos=%d frame_bits=%d\n",
+               alsa_stream->buffer_size, alsa_stream->period_size,
+               alsa_stream->pos, runtime->frame_bits);
+
+       mutex_unlock(&chip->audio_mutex);
+       audio_info(" .. OUT\n");
+       return 0;
+}
+
+static void snd_bcm2835_pcm_transfer(struct snd_pcm_substream *substream,
+       struct snd_pcm_indirect *rec, size_t bytes)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct bcm2835_alsa_stream *alsa_stream = runtime->private_data;
+       void *src = (void *) (substream->runtime->dma_area + rec->sw_data);
+       int err;
+
+       err = bcm2835_audio_write(alsa_stream, bytes, src);
+       if (err)
+               audio_error(" Failed to transfer to alsa device (%d)\n", err);
+
+}
+
+static int snd_bcm2835_pcm_ack(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct bcm2835_alsa_stream *alsa_stream = runtime->private_data;
+       struct snd_pcm_indirect *pcm_indirect = &alsa_stream->pcm_indirect;
+
+       pcm_indirect->hw_queue_size = runtime->hw.buffer_bytes_max;
+       snd_pcm_indirect_playback_transfer(substream, pcm_indirect,
+                                          snd_bcm2835_pcm_transfer);
+       return 0;
+}
+
+/* trigger callback */
+static int snd_bcm2835_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct bcm2835_alsa_stream *alsa_stream = runtime->private_data;
+       int err = 0;
+
+       audio_info(" .. IN\n");
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               audio_debug("bcm2835_AUDIO_TRIGGER_START running=%d\n",
+                       alsa_stream->running);
+               if (!alsa_stream->running) {
+                       err = bcm2835_audio_start(alsa_stream);
+                       if (!err) {
+                               alsa_stream->pcm_indirect.hw_io =
+                                       alsa_stream->pcm_indirect.hw_data =
+                                       bytes_to_frames(runtime,
+                                       alsa_stream->pos);
+                               substream->ops->ack(substream);
+                               alsa_stream->running = 1;
+                               alsa_stream->draining = 1;
+                       } else {
+                               audio_error(" Failed to START alsa device (%d)\n", err);
+                       }
+               }
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               audio_debug
+                       ("bcm2835_AUDIO_TRIGGER_STOP running=%d draining=%d\n",
+                       alsa_stream->running, runtime->status->state == SNDRV_PCM_STATE_DRAINING);
+               if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) {
+                       audio_info("DRAINING\n");
+                       alsa_stream->draining = 1;
+               } else {
+                       audio_info("DROPPING\n");
+                       alsa_stream->draining = 0;
+               }
+               if (alsa_stream->running) {
+                       err = bcm2835_audio_stop(alsa_stream);
+                       if (err != 0)
+                               audio_error(" Failed to STOP alsa device (%d)\n", err);
+                       alsa_stream->running = 0;
+               }
+               break;
+       default:
+               err = -EINVAL;
+       }
+
+       audio_info(" .. OUT\n");
+       return err;
+}
+
+/* pointer callback */
+static snd_pcm_uframes_t
+snd_bcm2835_pcm_pointer(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct bcm2835_alsa_stream *alsa_stream = runtime->private_data;
+
+       audio_info(" .. IN\n");
+
+       audio_debug("pcm_pointer... (%d) hwptr=%d appl=%d pos=%d\n", 0,
+               frames_to_bytes(runtime, runtime->status->hw_ptr),
+               frames_to_bytes(runtime, runtime->control->appl_ptr),
+               alsa_stream->pos);
+
+       audio_info(" .. OUT\n");
+       return snd_pcm_indirect_playback_pointer(substream,
+               &alsa_stream->pcm_indirect,
+               alsa_stream->pos);
+}
+
+static int snd_bcm2835_pcm_lib_ioctl(struct snd_pcm_substream *substream,
+       unsigned int cmd, void *arg)
+{
+       int ret = snd_pcm_lib_ioctl(substream, cmd, arg);
+
+       audio_info(" .. substream=%p, cmd=%d, arg=%p (%x) ret=%d\n", substream,
+               cmd, arg, arg ? *(unsigned *) arg : 0, ret);
+       return ret;
+}
+
+/* operators */
+static struct snd_pcm_ops snd_bcm2835_playback_ops = {
+       .open = snd_bcm2835_playback_open,
+       .close = snd_bcm2835_playback_close,
+       .ioctl = snd_bcm2835_pcm_lib_ioctl,
+       .hw_params = snd_bcm2835_pcm_hw_params,
+       .hw_free = snd_bcm2835_pcm_hw_free,
+       .prepare = snd_bcm2835_pcm_prepare,
+       .trigger = snd_bcm2835_pcm_trigger,
+       .pointer = snd_bcm2835_pcm_pointer,
+       .ack = snd_bcm2835_pcm_ack,
+};
+
+static struct snd_pcm_ops snd_bcm2835_playback_spdif_ops = {
+       .open = snd_bcm2835_playback_spdif_open,
+       .close = snd_bcm2835_playback_close,
+       .ioctl = snd_bcm2835_pcm_lib_ioctl,
+       .hw_params = snd_bcm2835_pcm_hw_params,
+       .hw_free = snd_bcm2835_pcm_hw_free,
+       .prepare = snd_bcm2835_pcm_prepare,
+       .trigger = snd_bcm2835_pcm_trigger,
+       .pointer = snd_bcm2835_pcm_pointer,
+       .ack = snd_bcm2835_pcm_ack,
+};
+
+/* create a pcm device */
+int snd_bcm2835_new_pcm(struct bcm2835_chip *chip)
+{
+       struct snd_pcm *pcm;
+       int err;
+
+       audio_info(" .. IN\n");
+       mutex_init(&chip->audio_mutex);
+       if (mutex_lock_interruptible(&chip->audio_mutex)) {
+               audio_error("Interrupted whilst waiting for lock\n");
+               return -EINTR;
+       }
+       err = snd_pcm_new(chip->card, "bcm2835 ALSA", 0, MAX_SUBSTREAMS, 0, &pcm);
+       if (err < 0)
+               goto out;
+       pcm->private_data = chip;
+       strcpy(pcm->name, "bcm2835 ALSA");
+       chip->pcm = pcm;
+       chip->dest = AUDIO_DEST_AUTO;
+       chip->volume = alsa2chip(0);
+       chip->mute = CTRL_VOL_UNMUTE; /*disable mute on startup */
+       /* set operators */
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+                       &snd_bcm2835_playback_ops);
+
+       /* pre-allocation of buffers */
+       /* NOTE: this may fail */
+       snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+                                             snd_dma_continuous_data(GFP_KERNEL),
+                                             snd_bcm2835_playback_hw.buffer_bytes_max,
+                                             snd_bcm2835_playback_hw.buffer_bytes_max);
+
+
+out:
+       mutex_unlock(&chip->audio_mutex);
+       audio_info(" .. OUT\n");
+
+       return 0;
+}
+
+int snd_bcm2835_new_spdif_pcm(struct bcm2835_chip *chip)
+{
+       struct snd_pcm *pcm;
+       int err;
+
+       audio_info(" .. IN\n");
+       if (mutex_lock_interruptible(&chip->audio_mutex)) {
+               audio_error("Interrupted whilst waiting for lock\n");
+               return -EINTR;
+       }
+       err = snd_pcm_new(chip->card, "bcm2835 ALSA", 1, 1, 0, &pcm);
+       if (err < 0)
+               goto out;
+
+       pcm->private_data = chip;
+       strcpy(pcm->name, "bcm2835 IEC958/HDMI");
+       chip->pcm_spdif = pcm;
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+                       &snd_bcm2835_playback_spdif_ops);
+
+       /* pre-allocation of buffers */
+       /* NOTE: this may fail */
+       snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+               snd_dma_continuous_data(GFP_KERNEL),
+               snd_bcm2835_playback_spdif_hw.buffer_bytes_max, snd_bcm2835_playback_spdif_hw.buffer_bytes_max);
+out:
+       mutex_unlock(&chip->audio_mutex);
+       audio_info(" .. OUT\n");
+
+       return 0;
+}
diff --git a/drivers/staging/vc04_services/bcm2835-audio/bcm2835-vchiq.c b/drivers/staging/vc04_services/bcm2835-audio/bcm2835-vchiq.c
new file mode 100644 (file)
index 0000000..df1f3dd
--- /dev/null
@@ -0,0 +1,910 @@
+/*****************************************************************************
+ * 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/device.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/mm.h>
+#include <linux/syscalls.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/atomic.h>
+#include <linux/module.h>
+#include <linux/completion.h>
+
+#include "bcm2835.h"
+
+/* ---- Include Files -------------------------------------------------------- */
+
+#include "interface/vchi/vchi.h"
+#include "vc_vchi_audioserv_defs.h"
+
+/* ---- Private Constants and Types ------------------------------------------ */
+
+#define BCM2835_AUDIO_STOP           0
+#define BCM2835_AUDIO_START          1
+#define BCM2835_AUDIO_WRITE          2
+
+/* Logging macros (for remapping to other logging mechanisms, i.e., printf) */
+#ifdef AUDIO_DEBUG_ENABLE
+#define LOG_ERR(fmt, arg...)   pr_err("%s:%d " fmt, __func__, __LINE__, ##arg)
+#define LOG_WARN(fmt, arg...)  pr_info("%s:%d " fmt, __func__, __LINE__, ##arg)
+#define LOG_INFO(fmt, arg...)  pr_info("%s:%d " fmt, __func__, __LINE__, ##arg)
+#define LOG_DBG(fmt, arg...)   pr_info("%s:%d " fmt, __func__, __LINE__, ##arg)
+#else
+#define LOG_ERR(fmt, arg...)   pr_err("%s:%d " fmt, __func__, __LINE__, ##arg)
+#define LOG_WARN(fmt, arg...)   no_printk(fmt, ##arg)
+#define LOG_INFO(fmt, arg...)   no_printk(fmt, ##arg)
+#define LOG_DBG(fmt, arg...)    no_printk(fmt, ##arg)
+#endif
+
+struct bcm2835_audio_instance {
+       unsigned int num_connections;
+       VCHI_SERVICE_HANDLE_T vchi_handle[VCHI_MAX_NUM_CONNECTIONS];
+       struct completion msg_avail_comp;
+       struct mutex vchi_mutex;
+       struct bcm2835_alsa_stream *alsa_stream;
+       int result;
+       short peer_version;
+};
+
+static bool force_bulk;
+
+/* ---- Private Variables ---------------------------------------------------- */
+
+/* ---- Private Function Prototypes ------------------------------------------ */
+
+/* ---- Private Functions ---------------------------------------------------- */
+
+static int bcm2835_audio_stop_worker(struct bcm2835_alsa_stream *alsa_stream);
+static int bcm2835_audio_start_worker(struct bcm2835_alsa_stream *alsa_stream);
+static int bcm2835_audio_write_worker(struct bcm2835_alsa_stream *alsa_stream,
+                                     unsigned int count, void *src);
+
+// Routine to send a message across a service
+
+static int
+bcm2835_vchi_msg_queue(VCHI_SERVICE_HANDLE_T handle,
+                      void *data,
+                      unsigned int size)
+{
+       return vchi_queue_kernel_message(handle,
+                                        data,
+                                        size);
+}
+
+static const u32 BCM2835_AUDIO_WRITE_COOKIE1 = ('B' << 24 | 'C' << 16 |
+                                               'M' << 8  | 'A');
+static const u32 BCM2835_AUDIO_WRITE_COOKIE2 = ('D' << 24 | 'A' << 16 |
+                                               'T' << 8  | 'A');
+
+struct bcm2835_audio_work {
+       struct work_struct my_work;
+       struct bcm2835_alsa_stream *alsa_stream;
+       int cmd;
+       void *src;
+       unsigned int count;
+};
+
+static void my_wq_function(struct work_struct *work)
+{
+       struct bcm2835_audio_work *w =
+               container_of(work, struct bcm2835_audio_work, my_work);
+       int ret = -9;
+
+       LOG_DBG(" .. IN %p:%d\n", w->alsa_stream, w->cmd);
+       switch (w->cmd) {
+       case BCM2835_AUDIO_START:
+               ret = bcm2835_audio_start_worker(w->alsa_stream);
+               break;
+       case BCM2835_AUDIO_STOP:
+               ret = bcm2835_audio_stop_worker(w->alsa_stream);
+               break;
+       case BCM2835_AUDIO_WRITE:
+               ret = bcm2835_audio_write_worker(w->alsa_stream, w->count,
+                                                w->src);
+               break;
+       default:
+               LOG_ERR(" Unexpected work: %p:%d\n", w->alsa_stream, w->cmd);
+               break;
+       }
+       kfree((void *) work);
+       LOG_DBG(" .. OUT %d\n", ret);
+}
+
+int bcm2835_audio_start(struct bcm2835_alsa_stream *alsa_stream)
+{
+       int ret = -1;
+
+       LOG_DBG(" .. IN\n");
+       if (alsa_stream->my_wq) {
+               struct bcm2835_audio_work *work;
+
+               work = kmalloc(sizeof(*work), GFP_ATOMIC);
+               /*--- Queue some work (item 1) ---*/
+               if (work) {
+                       INIT_WORK(&work->my_work, my_wq_function);
+                       work->alsa_stream = alsa_stream;
+                       work->cmd = BCM2835_AUDIO_START;
+                       if (queue_work(alsa_stream->my_wq, &work->my_work))
+                               ret = 0;
+               } else
+                       LOG_ERR(" .. Error: NULL work kmalloc\n");
+       }
+       LOG_DBG(" .. OUT %d\n", ret);
+       return ret;
+}
+
+int bcm2835_audio_stop(struct bcm2835_alsa_stream *alsa_stream)
+{
+       int ret = -1;
+
+       LOG_DBG(" .. IN\n");
+       if (alsa_stream->my_wq) {
+               struct bcm2835_audio_work *work;
+
+               work = kmalloc(sizeof(*work), GFP_ATOMIC);
+               /*--- Queue some work (item 1) ---*/
+               if (work) {
+                       INIT_WORK(&work->my_work, my_wq_function);
+                       work->alsa_stream = alsa_stream;
+                       work->cmd = BCM2835_AUDIO_STOP;
+                       if (queue_work(alsa_stream->my_wq, &work->my_work))
+                               ret = 0;
+               } else
+                       LOG_ERR(" .. Error: NULL work kmalloc\n");
+       }
+       LOG_DBG(" .. OUT %d\n", ret);
+       return ret;
+}
+
+int bcm2835_audio_write(struct bcm2835_alsa_stream *alsa_stream,
+                       unsigned int count, void *src)
+{
+       int ret = -1;
+
+       LOG_DBG(" .. IN\n");
+       if (alsa_stream->my_wq) {
+               struct bcm2835_audio_work *work;
+
+               work = kmalloc(sizeof(*work), GFP_ATOMIC);
+               /*--- Queue some work (item 1) ---*/
+               if (work) {
+                       INIT_WORK(&work->my_work, my_wq_function);
+                       work->alsa_stream = alsa_stream;
+                       work->cmd = BCM2835_AUDIO_WRITE;
+                       work->src = src;
+                       work->count = count;
+                       if (queue_work(alsa_stream->my_wq, &work->my_work))
+                               ret = 0;
+               } else
+                       LOG_ERR(" .. Error: NULL work kmalloc\n");
+       }
+       LOG_DBG(" .. OUT %d\n", ret);
+       return ret;
+}
+
+static void my_workqueue_init(struct bcm2835_alsa_stream *alsa_stream)
+{
+       alsa_stream->my_wq = alloc_workqueue("my_queue", WQ_HIGHPRI, 1);
+}
+
+static void my_workqueue_quit(struct bcm2835_alsa_stream *alsa_stream)
+{
+       if (alsa_stream->my_wq) {
+               flush_workqueue(alsa_stream->my_wq);
+               destroy_workqueue(alsa_stream->my_wq);
+               alsa_stream->my_wq = NULL;
+       }
+}
+
+static void audio_vchi_callback(void *param,
+                               const VCHI_CALLBACK_REASON_T reason,
+                               void *msg_handle)
+{
+       struct bcm2835_audio_instance *instance = param;
+       int status;
+       int msg_len;
+       struct vc_audio_msg m;
+
+       LOG_DBG(" .. IN instance=%p, handle=%p, alsa=%p, reason=%d, handle=%p\n",
+               instance, instance ? instance->vchi_handle[0] : NULL, instance ? instance->alsa_stream : NULL, reason, msg_handle);
+
+       if (reason != VCHI_CALLBACK_MSG_AVAILABLE)
+               return;
+
+       if (!instance) {
+               LOG_ERR(" .. instance is null\n");
+               BUG();
+               return;
+       }
+       if (!instance->vchi_handle[0]) {
+               LOG_ERR(" .. instance->vchi_handle[0] is null\n");
+               BUG();
+               return;
+       }
+       status = vchi_msg_dequeue(instance->vchi_handle[0],
+                                 &m, sizeof(m), &msg_len, VCHI_FLAGS_NONE);
+       if (m.type == VC_AUDIO_MSG_TYPE_RESULT) {
+               LOG_DBG(" .. instance=%p, m.type=VC_AUDIO_MSG_TYPE_RESULT, success=%d\n",
+                       instance, m.u.result.success);
+               instance->result = m.u.result.success;
+               complete(&instance->msg_avail_comp);
+       } else if (m.type == VC_AUDIO_MSG_TYPE_COMPLETE) {
+               struct bcm2835_alsa_stream *alsa_stream = instance->alsa_stream;
+
+               LOG_DBG(" .. instance=%p, m.type=VC_AUDIO_MSG_TYPE_COMPLETE, complete=%d\n",
+                       instance, m.u.complete.count);
+               if (m.u.complete.cookie1 != BCM2835_AUDIO_WRITE_COOKIE1 ||
+                   m.u.complete.cookie2 != BCM2835_AUDIO_WRITE_COOKIE2)
+                       LOG_ERR(" .. response is corrupt\n");
+               else if (alsa_stream) {
+                       atomic_add(m.u.complete.count,
+                                  &alsa_stream->retrieved);
+                       bcm2835_playback_fifo(alsa_stream);
+               } else {
+                       LOG_ERR(" .. unexpected alsa_stream=%p\n",
+                               alsa_stream);
+               }
+       } else {
+               LOG_ERR(" .. unexpected m.type=%d\n", m.type);
+       }
+       LOG_DBG(" .. OUT\n");
+}
+
+static struct bcm2835_audio_instance *
+vc_vchi_audio_init(VCHI_INSTANCE_T vchi_instance,
+                  VCHI_CONNECTION_T **vchi_connections,
+                  unsigned int num_connections)
+{
+       unsigned int i;
+       struct bcm2835_audio_instance *instance;
+       int status;
+
+       LOG_DBG("%s: start", __func__);
+
+       if (num_connections > VCHI_MAX_NUM_CONNECTIONS) {
+               LOG_ERR("%s: unsupported number of connections %u (max=%u)\n",
+                       __func__, num_connections, VCHI_MAX_NUM_CONNECTIONS);
+
+               return NULL;
+       }
+       /* Allocate memory for this instance */
+       instance = kmalloc(sizeof(*instance), GFP_KERNEL);
+       if (!instance)
+               return NULL;
+
+       memset(instance, 0, sizeof(*instance));
+       instance->num_connections = num_connections;
+
+       /* Create a lock for exclusive, serialized VCHI connection access */
+       mutex_init(&instance->vchi_mutex);
+       /* Open the VCHI service connections */
+       for (i = 0; i < num_connections; i++) {
+               SERVICE_CREATION_T params = {
+                       VCHI_VERSION_EX(VC_AUDIOSERV_VER, VC_AUDIOSERV_MIN_VER),
+                       VC_AUDIO_SERVER_NAME, // 4cc service code
+                       vchi_connections[i], // passed in fn pointers
+                       0, // rx fifo size (unused)
+                       0, // tx fifo size (unused)
+                       audio_vchi_callback, // service callback
+                       instance, // service callback parameter
+                       1, //TODO: remove VCOS_FALSE,   // unaligned bulk recieves
+                       1, //TODO: remove VCOS_FALSE,   // unaligned bulk transmits
+                       0 // want crc check on bulk transfers
+               };
+
+               LOG_DBG("%s: about to open %i\n", __func__, i);
+               status = vchi_service_open(vchi_instance, &params,
+                       &instance->vchi_handle[i]);
+               LOG_DBG("%s: opened %i: %p=%d\n", __func__, i, instance->vchi_handle[i], status);
+               if (status) {
+                       LOG_ERR("%s: failed to open VCHI service connection (status=%d)\n",
+                               __func__, status);
+
+                       goto err_close_services;
+               }
+               /* Finished with the service for now */
+               vchi_service_release(instance->vchi_handle[i]);
+       }
+
+       LOG_DBG("%s: okay\n", __func__);
+       return instance;
+
+err_close_services:
+       for (i = 0; i < instance->num_connections; i++) {
+               LOG_ERR("%s: closing %i: %p\n", __func__, i, instance->vchi_handle[i]);
+               if (instance->vchi_handle[i])
+                       vchi_service_close(instance->vchi_handle[i]);
+       }
+
+       kfree(instance);
+       LOG_ERR("%s: error\n", __func__);
+
+       return NULL;
+}
+
+static int vc_vchi_audio_deinit(struct bcm2835_audio_instance *instance)
+{
+       unsigned int i;
+
+       LOG_DBG(" .. IN\n");
+
+       if (!instance) {
+               LOG_ERR("%s: invalid handle %p\n", __func__, instance);
+
+               return -1;
+       }
+
+       LOG_DBG(" .. about to lock (%d)\n", instance->num_connections);
+       if (mutex_lock_interruptible(&instance->vchi_mutex)) {
+               LOG_DBG("Interrupted whilst waiting for lock on (%d)\n",
+                       instance->num_connections);
+               return -EINTR;
+       }
+
+       /* Close all VCHI service connections */
+       for (i = 0; i < instance->num_connections; i++) {
+               int status;
+
+               LOG_DBG(" .. %i:closing %p\n", i, instance->vchi_handle[i]);
+               vchi_service_use(instance->vchi_handle[i]);
+
+               status = vchi_service_close(instance->vchi_handle[i]);
+               if (status) {
+                       LOG_DBG("%s: failed to close VCHI service connection (status=%d)\n",
+                               __func__, status);
+               }
+       }
+
+       mutex_unlock(&instance->vchi_mutex);
+
+       kfree(instance);
+
+       LOG_DBG(" .. OUT\n");
+
+       return 0;
+}
+
+static int bcm2835_audio_open_connection(struct bcm2835_alsa_stream *alsa_stream)
+{
+       static VCHI_INSTANCE_T vchi_instance;
+       static VCHI_CONNECTION_T *vchi_connection;
+       static int initted;
+       struct bcm2835_audio_instance *instance =
+               (struct bcm2835_audio_instance *)alsa_stream->instance;
+       int ret;
+
+       LOG_DBG(" .. IN\n");
+
+       LOG_INFO("%s: start\n", __func__);
+       BUG_ON(instance);
+       if (instance) {
+               LOG_ERR("%s: VCHI instance already open (%p)\n",
+                       __func__, instance);
+               instance->alsa_stream = alsa_stream;
+               alsa_stream->instance = instance;
+               ret = 0; // xxx todo -1;
+               goto err_free_mem;
+       }
+
+       /* Initialize and create a VCHI connection */
+       if (!initted) {
+               ret = vchi_initialise(&vchi_instance);
+               if (ret) {
+                       LOG_ERR("%s: failed to initialise VCHI instance (ret=%d)\n",
+                               __func__, ret);
+
+                       ret = -EIO;
+                       goto err_free_mem;
+               }
+               ret = vchi_connect(NULL, 0, vchi_instance);
+               if (ret) {
+                       LOG_ERR("%s: failed to connect VCHI instance (ret=%d)\n",
+                               __func__, ret);
+
+                       ret = -EIO;
+                       goto err_free_mem;
+               }
+               initted = 1;
+       }
+
+       /* Initialize an instance of the audio service */
+       instance = vc_vchi_audio_init(vchi_instance, &vchi_connection, 1);
+
+       if (!instance) {
+               LOG_ERR("%s: failed to initialize audio service\n", __func__);
+
+               ret = -EPERM;
+               goto err_free_mem;
+       }
+
+       instance->alsa_stream = alsa_stream;
+       alsa_stream->instance = instance;
+
+       LOG_DBG(" success !\n");
+       ret = 0;
+err_free_mem:
+       LOG_DBG(" .. OUT\n");
+
+       return ret;
+}
+
+int bcm2835_audio_open(struct bcm2835_alsa_stream *alsa_stream)
+{
+       struct bcm2835_audio_instance *instance;
+       struct vc_audio_msg m;
+       int status;
+       int ret;
+
+       LOG_DBG(" .. IN\n");
+
+       my_workqueue_init(alsa_stream);
+
+       ret = bcm2835_audio_open_connection(alsa_stream);
+       if (ret) {
+               ret = -1;
+               goto exit;
+       }
+       instance = alsa_stream->instance;
+       LOG_DBG(" instance (%p)\n", instance);
+
+       if (mutex_lock_interruptible(&instance->vchi_mutex)) {
+               LOG_DBG("Interrupted whilst waiting for lock on (%d)\n", instance->num_connections);
+               return -EINTR;
+       }
+       vchi_service_use(instance->vchi_handle[0]);
+
+       m.type = VC_AUDIO_MSG_TYPE_OPEN;
+
+       /* Send the message to the videocore */
+       status = bcm2835_vchi_msg_queue(instance->vchi_handle[0],
+                                       &m, sizeof(m));
+
+       if (status) {
+               LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n",
+                       __func__, status);
+
+               ret = -1;
+               goto unlock;
+       }
+
+       ret = 0;
+
+unlock:
+       vchi_service_release(instance->vchi_handle[0]);
+       mutex_unlock(&instance->vchi_mutex);
+exit:
+       LOG_DBG(" .. OUT\n");
+       return ret;
+}
+
+static int bcm2835_audio_set_ctls_chan(struct bcm2835_alsa_stream *alsa_stream,
+                                      struct bcm2835_chip *chip)
+{
+       struct vc_audio_msg m;
+       struct bcm2835_audio_instance *instance = alsa_stream->instance;
+       int status;
+       int ret;
+
+       LOG_DBG(" .. IN\n");
+
+       LOG_INFO(" Setting ALSA dest(%d), volume(%d)\n",
+                chip->dest, chip->volume);
+
+       if (mutex_lock_interruptible(&instance->vchi_mutex)) {
+               LOG_DBG("Interrupted whilst waiting for lock on (%d)\n",
+                       instance->num_connections);
+               return -EINTR;
+       }
+       vchi_service_use(instance->vchi_handle[0]);
+
+       instance->result = -1;
+
+       m.type = VC_AUDIO_MSG_TYPE_CONTROL;
+       m.u.control.dest = chip->dest;
+       m.u.control.volume = chip->volume;
+
+       /* Create the message available completion */
+       init_completion(&instance->msg_avail_comp);
+
+       /* Send the message to the videocore */
+       status = bcm2835_vchi_msg_queue(instance->vchi_handle[0],
+                                       &m, sizeof(m));
+
+       if (status) {
+               LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n",
+                       __func__, status);
+
+               ret = -1;
+               goto unlock;
+       }
+
+       /* We are expecting a reply from the videocore */
+       wait_for_completion(&instance->msg_avail_comp);
+
+       if (instance->result) {
+               LOG_ERR("%s: result=%d\n", __func__, instance->result);
+
+               ret = -1;
+               goto unlock;
+       }
+
+       ret = 0;
+
+unlock:
+       vchi_service_release(instance->vchi_handle[0]);
+       mutex_unlock(&instance->vchi_mutex);
+
+       LOG_DBG(" .. OUT\n");
+       return ret;
+}
+
+int bcm2835_audio_set_ctls(struct bcm2835_chip *chip)
+{
+       int i;
+       int ret = 0;
+
+       LOG_DBG(" .. IN\n");
+       LOG_DBG(" Setting ALSA dest(%d), volume(%d)\n", chip->dest, chip->volume);
+
+       /* change ctls for all substreams */
+       for (i = 0; i < MAX_SUBSTREAMS; i++) {
+               if (chip->avail_substreams & (1 << i)) {
+                       if (!chip->alsa_stream[i]) {
+                               LOG_DBG(" No ALSA stream available?! %i:%p (%x)\n", i, chip->alsa_stream[i], chip->avail_substreams);
+                               ret = 0;
+                       } else if (bcm2835_audio_set_ctls_chan(chip->alsa_stream[i], chip) != 0) {
+                               LOG_ERR("Couldn't set the controls for stream %d\n", i);
+                               ret = -1;
+                       } else {
+                               LOG_DBG(" Controls set for stream %d\n", i);
+                       }
+               }
+       }
+       LOG_DBG(" .. OUT ret=%d\n", ret);
+       return ret;
+}
+
+int bcm2835_audio_set_params(struct bcm2835_alsa_stream *alsa_stream,
+                            unsigned int channels, unsigned int samplerate,
+                            unsigned int bps)
+{
+       struct vc_audio_msg m;
+       struct bcm2835_audio_instance *instance = alsa_stream->instance;
+       int status;
+       int ret;
+
+       LOG_DBG(" .. IN\n");
+
+       LOG_INFO(" Setting ALSA channels(%d), samplerate(%d), bits-per-sample(%d)\n",
+               channels, samplerate, bps);
+
+       /* resend ctls - alsa_stream may not have been open when first send */
+       ret = bcm2835_audio_set_ctls_chan(alsa_stream, alsa_stream->chip);
+       if (ret) {
+               LOG_ERR(" Alsa controls not supported\n");
+               return -EINVAL;
+       }
+
+       if (mutex_lock_interruptible(&instance->vchi_mutex)) {
+               LOG_DBG("Interrupted whilst waiting for lock on (%d)\n", instance->num_connections);
+               return -EINTR;
+       }
+       vchi_service_use(instance->vchi_handle[0]);
+
+       instance->result = -1;
+
+       m.type = VC_AUDIO_MSG_TYPE_CONFIG;
+       m.u.config.channels = channels;
+       m.u.config.samplerate = samplerate;
+       m.u.config.bps = bps;
+
+       /* Create the message available completion */
+       init_completion(&instance->msg_avail_comp);
+
+       /* Send the message to the videocore */
+       status = bcm2835_vchi_msg_queue(instance->vchi_handle[0],
+                                       &m, sizeof(m));
+
+       if (status) {
+               LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n",
+                       __func__, status);
+
+               ret = -1;
+               goto unlock;
+       }
+
+       /* We are expecting a reply from the videocore */
+       wait_for_completion(&instance->msg_avail_comp);
+
+       if (instance->result) {
+               LOG_ERR("%s: result=%d", __func__, instance->result);
+
+               ret = -1;
+               goto unlock;
+       }
+
+       ret = 0;
+
+unlock:
+       vchi_service_release(instance->vchi_handle[0]);
+       mutex_unlock(&instance->vchi_mutex);
+
+       LOG_DBG(" .. OUT\n");
+       return ret;
+}
+
+int bcm2835_audio_setup(struct bcm2835_alsa_stream *alsa_stream)
+{
+       LOG_DBG(" .. IN\n");
+
+       LOG_DBG(" .. OUT\n");
+
+       return 0;
+}
+
+static int bcm2835_audio_start_worker(struct bcm2835_alsa_stream *alsa_stream)
+{
+       struct vc_audio_msg m;
+       struct bcm2835_audio_instance *instance = alsa_stream->instance;
+       int status;
+       int ret;
+
+       LOG_DBG(" .. IN\n");
+
+       if (mutex_lock_interruptible(&instance->vchi_mutex)) {
+               LOG_DBG("Interrupted whilst waiting for lock on (%d)\n",
+                       instance->num_connections);
+               return -EINTR;
+       }
+       vchi_service_use(instance->vchi_handle[0]);
+
+       m.type = VC_AUDIO_MSG_TYPE_START;
+
+       /* Send the message to the videocore */
+       status = bcm2835_vchi_msg_queue(instance->vchi_handle[0],
+                                       &m, sizeof(m));
+
+       if (status) {
+               LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n",
+                       __func__, status);
+
+               ret = -1;
+               goto unlock;
+       }
+
+       ret = 0;
+
+unlock:
+       vchi_service_release(instance->vchi_handle[0]);
+       mutex_unlock(&instance->vchi_mutex);
+       LOG_DBG(" .. OUT\n");
+       return ret;
+}
+
+static int bcm2835_audio_stop_worker(struct bcm2835_alsa_stream *alsa_stream)
+{
+       struct vc_audio_msg m;
+       struct bcm2835_audio_instance *instance = alsa_stream->instance;
+       int status;
+       int ret;
+
+       LOG_DBG(" .. IN\n");
+
+       if (mutex_lock_interruptible(&instance->vchi_mutex)) {
+               LOG_DBG("Interrupted whilst waiting for lock on (%d)\n",
+                       instance->num_connections);
+               return -EINTR;
+       }
+       vchi_service_use(instance->vchi_handle[0]);
+
+       m.type = VC_AUDIO_MSG_TYPE_STOP;
+       m.u.stop.draining = alsa_stream->draining;
+
+       /* Send the message to the videocore */
+       status = bcm2835_vchi_msg_queue(instance->vchi_handle[0],
+                                       &m, sizeof(m));
+
+       if (status) {
+               LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n",
+                       __func__, status);
+
+               ret = -1;
+               goto unlock;
+       }
+
+       ret = 0;
+
+unlock:
+       vchi_service_release(instance->vchi_handle[0]);
+       mutex_unlock(&instance->vchi_mutex);
+       LOG_DBG(" .. OUT\n");
+       return ret;
+}
+
+int bcm2835_audio_close(struct bcm2835_alsa_stream *alsa_stream)
+{
+       struct vc_audio_msg m;
+       struct bcm2835_audio_instance *instance = alsa_stream->instance;
+       int status;
+       int ret;
+
+       LOG_DBG(" .. IN\n");
+
+       my_workqueue_quit(alsa_stream);
+
+       if (mutex_lock_interruptible(&instance->vchi_mutex)) {
+               LOG_DBG("Interrupted whilst waiting for lock on (%d)\n",
+                       instance->num_connections);
+               return -EINTR;
+       }
+       vchi_service_use(instance->vchi_handle[0]);
+
+       m.type = VC_AUDIO_MSG_TYPE_CLOSE;
+
+       /* Create the message available completion */
+       init_completion(&instance->msg_avail_comp);
+
+       /* Send the message to the videocore */
+       status = bcm2835_vchi_msg_queue(instance->vchi_handle[0],
+                                       &m, sizeof(m));
+
+       if (status) {
+               LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n",
+                       __func__, status);
+               ret = -1;
+               goto unlock;
+       }
+
+       /* We are expecting a reply from the videocore */
+       wait_for_completion(&instance->msg_avail_comp);
+
+       if (instance->result) {
+               LOG_ERR("%s: failed result (result=%d)\n",
+                       __func__, instance->result);
+
+               ret = -1;
+               goto unlock;
+       }
+
+       ret = 0;
+
+unlock:
+       vchi_service_release(instance->vchi_handle[0]);
+       mutex_unlock(&instance->vchi_mutex);
+
+       /* Stop the audio service */
+       vc_vchi_audio_deinit(instance);
+       alsa_stream->instance = NULL;
+
+       LOG_DBG(" .. OUT\n");
+       return ret;
+}
+
+static int bcm2835_audio_write_worker(struct bcm2835_alsa_stream *alsa_stream,
+                                     unsigned int count, void *src)
+{
+       struct vc_audio_msg m;
+       struct bcm2835_audio_instance *instance = alsa_stream->instance;
+       int status;
+       int ret;
+
+       LOG_DBG(" .. IN\n");
+
+       LOG_INFO(" Writing %d bytes from %p\n", count, src);
+
+       if (mutex_lock_interruptible(&instance->vchi_mutex)) {
+               LOG_DBG("Interrupted whilst waiting for lock on (%d)\n",
+                       instance->num_connections);
+               return -EINTR;
+       }
+       vchi_service_use(instance->vchi_handle[0]);
+
+       if (instance->peer_version == 0 &&
+                       vchi_get_peer_version(instance->vchi_handle[0], &instance->peer_version) == 0)
+               LOG_DBG("%s: client version %d connected\n", __func__, instance->peer_version);
+
+       m.type = VC_AUDIO_MSG_TYPE_WRITE;
+       m.u.write.count = count;
+       // old version uses bulk, new version uses control
+       m.u.write.max_packet = instance->peer_version < 2 || force_bulk ? 0 : 4000;
+       m.u.write.cookie1 = BCM2835_AUDIO_WRITE_COOKIE1;
+       m.u.write.cookie2 = BCM2835_AUDIO_WRITE_COOKIE2;
+       m.u.write.silence = src == NULL;
+
+       /* Send the message to the videocore */
+       status = bcm2835_vchi_msg_queue(instance->vchi_handle[0],
+                                       &m, sizeof(m));
+
+       if (status) {
+               LOG_ERR("%s: failed on vchi_msg_queue (status=%d)\n",
+                       __func__, status);
+
+               ret = -1;
+               goto unlock;
+       }
+       if (!m.u.write.silence) {
+               if (!m.u.write.max_packet) {
+                       /* Send the message to the videocore */
+                       status = vchi_bulk_queue_transmit(instance->vchi_handle[0],
+                               src, count,
+                               0 *
+                               VCHI_FLAGS_BLOCK_UNTIL_QUEUED
+                               +
+                               1 *
+                               VCHI_FLAGS_BLOCK_UNTIL_DATA_READ,
+                               NULL);
+               } else {
+                       while (count > 0) {
+                               int bytes = min((int) m.u.write.max_packet, (int) count);
+
+                               status = bcm2835_vchi_msg_queue(instance->vchi_handle[0],
+                                                               src, bytes);
+                               src = (char *)src + bytes;
+                               count -= bytes;
+                       }
+               }
+               if (status) {
+                       LOG_ERR("%s: failed on vchi_bulk_queue_transmit (status=%d)\n",
+                               __func__, status);
+
+                       ret = -1;
+                       goto unlock;
+               }
+       }
+       ret = 0;
+
+unlock:
+       vchi_service_release(instance->vchi_handle[0]);
+       mutex_unlock(&instance->vchi_mutex);
+       LOG_DBG(" .. OUT\n");
+       return ret;
+}
+
+/**
+ * Returns all buffers from arm->vc
+ */
+void bcm2835_audio_flush_buffers(struct bcm2835_alsa_stream *alsa_stream)
+{
+       LOG_DBG(" .. IN\n");
+       LOG_DBG(" .. OUT\n");
+}
+
+/**
+ * Forces VC to flush(drop) its filled playback buffers and
+ * return them the us. (VC->ARM)
+ */
+void bcm2835_audio_flush_playback_buffers(struct bcm2835_alsa_stream *alsa_stream)
+{
+       LOG_DBG(" .. IN\n");
+       LOG_DBG(" .. OUT\n");
+}
+
+unsigned int bcm2835_audio_retrieve_buffers(struct bcm2835_alsa_stream *alsa_stream)
+{
+       unsigned int count = atomic_read(&alsa_stream->retrieved);
+
+       atomic_sub(count, &alsa_stream->retrieved);
+       return count;
+}
+
+module_param(force_bulk, bool, 0444);
+MODULE_PARM_DESC(force_bulk, "Force use of vchiq bulk for audio");
diff --git a/drivers/staging/vc04_services/bcm2835-audio/bcm2835.c b/drivers/staging/vc04_services/bcm2835-audio/bcm2835.c
new file mode 100644 (file)
index 0000000..3a5e528
--- /dev/null
@@ -0,0 +1,250 @@
+/*****************************************************************************
+ * 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/slab.h>
+#include <linux/module.h>
+#include <linux/of.h>
+
+#include "bcm2835.h"
+
+/* HACKY global pointers needed for successive probes to work : ssp
+ * But compared against the changes we will have to do in VC audio_ipc code
+ * to export 8 audio_ipc devices as a single IPC device and then monitor all
+ * four devices in a thread, this gets things done quickly and should be easier
+ * to debug if we run into issues
+ */
+
+static struct snd_card *g_card;
+static struct bcm2835_chip *g_chip;
+
+static int snd_bcm2835_free(struct bcm2835_chip *chip)
+{
+       kfree(chip);
+       return 0;
+}
+
+/* component-destructor
+ * (see "Management of Cards and Components")
+ */
+static int snd_bcm2835_dev_free(struct snd_device *device)
+{
+       return snd_bcm2835_free(device->device_data);
+}
+
+/* chip-specific constructor
+ * (see "Management of Cards and Components")
+ */
+static int snd_bcm2835_create(struct snd_card *card,
+                             struct platform_device *pdev,
+                             struct bcm2835_chip **rchip)
+{
+       struct bcm2835_chip *chip;
+       int err;
+       static struct snd_device_ops ops = {
+               .dev_free = snd_bcm2835_dev_free,
+       };
+
+       *rchip = NULL;
+
+       chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+       if (!chip)
+               return -ENOMEM;
+
+       chip->card = card;
+
+       err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+       if (err < 0) {
+               snd_bcm2835_free(chip);
+               return err;
+       }
+
+       *rchip = chip;
+       return 0;
+}
+
+static int snd_bcm2835_alsa_probe_dt(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct bcm2835_chip *chip;
+       struct snd_card *card;
+       u32 numchans;
+       int err, i;
+
+       err = of_property_read_u32(dev->of_node, "brcm,pwm-channels",
+                                  &numchans);
+       if (err) {
+               dev_err(dev, "Failed to get DT property 'brcm,pwm-channels'");
+               return err;
+       }
+
+       if (numchans == 0 || numchans > MAX_SUBSTREAMS) {
+               numchans = MAX_SUBSTREAMS;
+               dev_warn(dev, "Illegal 'brcm,pwm-channels' value, will use %u\n",
+                        numchans);
+       }
+
+       err = snd_card_new(&pdev->dev, -1, NULL, THIS_MODULE, 0, &card);
+       if (err) {
+               dev_err(dev, "Failed to create soundcard structure\n");
+               return err;
+       }
+
+       snd_card_set_dev(card, dev);
+       strcpy(card->driver, "bcm2835");
+       strcpy(card->shortname, "bcm2835 ALSA");
+       sprintf(card->longname, "%s", card->shortname);
+
+       err = snd_bcm2835_create(card, pdev, &chip);
+       if (err < 0) {
+               dev_err(dev, "Failed to create bcm2835 chip\n");
+               goto err_free;
+       }
+
+       err = snd_bcm2835_new_pcm(chip);
+       if (err < 0) {
+               dev_err(dev, "Failed to create new bcm2835 pcm device\n");
+               goto err_free;
+       }
+
+       err = snd_bcm2835_new_spdif_pcm(chip);
+       if (err < 0) {
+               dev_err(dev, "Failed to create new bcm2835 spdif pcm device\n");
+               goto err_free;
+       }
+
+       err = snd_bcm2835_new_ctl(chip);
+       if (err < 0) {
+               dev_err(dev, "Failed to create new bcm2835 ctl\n");
+               goto err_free;
+       }
+
+       for (i = 0; i < numchans; i++) {
+               chip->avail_substreams |= (1 << i);
+               chip->pdev[i] = pdev;
+       }
+
+       err = snd_card_register(card);
+       if (err) {
+               dev_err(dev, "Failed to register bcm2835 ALSA card\n");
+               goto err_free;
+       }
+
+       g_card = card;
+       g_chip = chip;
+       platform_set_drvdata(pdev, card);
+       audio_info("bcm2835 ALSA card created with %u channels\n", numchans);
+
+       return 0;
+
+err_free:
+       snd_card_free(card);
+
+       return err;
+}
+
+static int snd_bcm2835_alsa_remove(struct platform_device *pdev)
+{
+       int idx;
+       void *drv_data;
+
+       drv_data = platform_get_drvdata(pdev);
+
+       if (drv_data == (void *)g_card) {
+               /* This is the card device */
+               snd_card_free((struct snd_card *)drv_data);
+               g_card = NULL;
+               g_chip = NULL;
+       } else {
+               idx = (int)(long)drv_data;
+               if (g_card) {
+                       BUG_ON(!g_chip);
+                       /* We pass chip device numbers in audio ipc devices
+                        * other than the one we registered our card with
+                        */
+                       idx = (int)(long)drv_data;
+                       BUG_ON(!idx || idx > MAX_SUBSTREAMS);
+                       g_chip->avail_substreams &= ~(1 << idx);
+                       /* There should be atleast one substream registered
+                        * after we are done here, as it wil be removed when
+                        * the *remove* is called for the card device
+                        */
+                       BUG_ON(!g_chip->avail_substreams);
+               }
+       }
+
+       platform_set_drvdata(pdev, NULL);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int snd_bcm2835_alsa_suspend(struct platform_device *pdev,
+                                   pm_message_t state)
+{
+       return 0;
+}
+
+static int snd_bcm2835_alsa_resume(struct platform_device *pdev)
+{
+       return 0;
+}
+
+#endif
+
+static const struct of_device_id snd_bcm2835_of_match_table[] = {
+       { .compatible = "brcm,bcm2835-audio",},
+       {},
+};
+MODULE_DEVICE_TABLE(of, snd_bcm2835_of_match_table);
+
+static struct platform_driver bcm2835_alsa0_driver = {
+       .probe = snd_bcm2835_alsa_probe_dt,
+       .remove = snd_bcm2835_alsa_remove,
+#ifdef CONFIG_PM
+       .suspend = snd_bcm2835_alsa_suspend,
+       .resume = snd_bcm2835_alsa_resume,
+#endif
+       .driver = {
+               .name = "bcm2835_AUD0",
+               .owner = THIS_MODULE,
+               .of_match_table = snd_bcm2835_of_match_table,
+       },
+};
+
+static int bcm2835_alsa_device_init(void)
+{
+       int retval;
+
+       retval = platform_driver_register(&bcm2835_alsa0_driver);
+       if (retval)
+               pr_err("Error registering bcm2835_alsa0_driver %d .\n", retval);
+
+       return retval;
+}
+
+static void bcm2835_alsa_device_exit(void)
+{
+       platform_driver_unregister(&bcm2835_alsa0_driver);
+}
+
+late_initcall(bcm2835_alsa_device_init);
+module_exit(bcm2835_alsa_device_exit);
+
+MODULE_AUTHOR("Dom Cobley");
+MODULE_DESCRIPTION("Alsa driver for BCM2835 chip");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/vc04_services/bcm2835-audio/bcm2835.h b/drivers/staging/vc04_services/bcm2835-audio/bcm2835.h
new file mode 100644 (file)
index 0000000..2f9d1c9
--- /dev/null
@@ -0,0 +1,170 @@
+/*****************************************************************************
+ * 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.
+ *****************************************************************************/
+
+#ifndef __SOUND_ARM_BCM2835_H
+#define __SOUND_ARM_BCM2835_H
+
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/pcm-indirect.h>
+#include <linux/workqueue.h>
+
+/*
+ * #define AUDIO_DEBUG_ENABLE
+ * #define AUDIO_VERBOSE_DEBUG_ENABLE
+ */
+
+/* Debug macros */
+
+#ifdef AUDIO_DEBUG_ENABLE
+#ifdef AUDIO_VERBOSE_DEBUG_ENABLE
+
+#define audio_debug(fmt, arg...) \
+       pr_info("%s:%d " fmt, __func__, __LINE__, ##arg)
+
+#define audio_info(fmt, arg...) \
+       pr_info("%s:%d " fmt, __func__, __LINE__, ##arg)
+
+#else
+
+#define audio_debug(fmt, arg...)
+
+#define audio_info(fmt, arg...)
+
+#endif /* AUDIO_VERBOSE_DEBUG_ENABLE */
+
+#else
+
+#define audio_debug(fmt, arg...)
+
+#define audio_info(fmt, arg...)
+
+#endif /* AUDIO_DEBUG_ENABLE */
+
+#define audio_error(fmt, arg...) \
+       pr_err("%s:%d " fmt, __func__, __LINE__, ##arg)
+
+#define audio_warning(fmt, arg...) \
+       pr_warn("%s:%d " fmt, __func__, __LINE__, ##arg)
+
+#define audio_alert(fmt, arg...) \
+       pr_alert("%s:%d " fmt, __func__, __LINE__, ##arg)
+
+#define MAX_SUBSTREAMS   (8)
+#define AVAIL_SUBSTREAMS_MASK  (0xff)
+
+enum {
+       CTRL_VOL_MUTE,
+       CTRL_VOL_UNMUTE
+};
+
+/* macros for alsa2chip and chip2alsa, instead of functions */
+
+// convert alsa to chip volume (defined as macro rather than function call)
+#define alsa2chip(vol) (uint)(-(((vol) << 8) / 100))
+
+// convert chip to alsa volume
+#define chip2alsa(vol) -(((vol) * 100) >> 8)
+
+/* Some constants for values .. */
+enum snd_bcm2835_route {
+       AUDIO_DEST_AUTO = 0,
+       AUDIO_DEST_HEADPHONES = 1,
+       AUDIO_DEST_HDMI = 2,
+       AUDIO_DEST_MAX,
+};
+
+enum snd_bcm2835_ctrl {
+       PCM_PLAYBACK_VOLUME,
+       PCM_PLAYBACK_MUTE,
+       PCM_PLAYBACK_DEVICE,
+};
+
+/* definition of the chip-specific record */
+struct bcm2835_chip {
+       struct snd_card *card;
+       struct snd_pcm *pcm;
+       struct snd_pcm *pcm_spdif;
+       /* Bitmat for valid reg_base and irq numbers */
+       unsigned int avail_substreams;
+       struct platform_device *pdev[MAX_SUBSTREAMS];
+       struct bcm2835_alsa_stream *alsa_stream[MAX_SUBSTREAMS];
+
+       int volume;
+       int old_volume; /* stores the volume value whist muted */
+       int dest;
+       int mute;
+
+       unsigned int opened;
+       unsigned int spdif_status;
+       struct mutex audio_mutex;
+};
+
+struct bcm2835_alsa_stream {
+       struct bcm2835_chip *chip;
+       struct snd_pcm_substream *substream;
+       struct snd_pcm_indirect pcm_indirect;
+
+       struct semaphore buffers_update_sem;
+       struct semaphore control_sem;
+       spinlock_t lock;
+       volatile unsigned int control;
+       volatile unsigned int status;
+
+       int open;
+       int running;
+       int draining;
+
+       int channels;
+       int params_rate;
+       int pcm_format_width;
+
+       unsigned int pos;
+       unsigned int buffer_size;
+       unsigned int period_size;
+
+       atomic_t retrieved;
+       struct bcm2835_audio_instance *instance;
+       struct workqueue_struct *my_wq;
+       int idx;
+};
+
+int snd_bcm2835_new_ctl(struct bcm2835_chip *chip);
+int snd_bcm2835_new_pcm(struct bcm2835_chip *chip);
+int snd_bcm2835_new_spdif_pcm(struct bcm2835_chip *chip);
+
+int bcm2835_audio_open(struct bcm2835_alsa_stream *alsa_stream);
+int bcm2835_audio_close(struct bcm2835_alsa_stream *alsa_stream);
+int bcm2835_audio_set_params(struct bcm2835_alsa_stream *alsa_stream,
+                            unsigned int channels, unsigned int samplerate,
+                            unsigned int bps);
+int bcm2835_audio_setup(struct bcm2835_alsa_stream *alsa_stream);
+int bcm2835_audio_start(struct bcm2835_alsa_stream *alsa_stream);
+int bcm2835_audio_stop(struct bcm2835_alsa_stream *alsa_stream);
+int bcm2835_audio_set_ctls(struct bcm2835_chip *chip);
+int bcm2835_audio_write(struct bcm2835_alsa_stream *alsa_stream,
+                       unsigned int count,
+                       void *src);
+void bcm2835_playback_fifo(struct bcm2835_alsa_stream *alsa_stream);
+unsigned int bcm2835_audio_retrieve_buffers(struct bcm2835_alsa_stream *alsa_stream);
+void bcm2835_audio_flush_buffers(struct bcm2835_alsa_stream *alsa_stream);
+void bcm2835_audio_flush_playback_buffers(struct bcm2835_alsa_stream *alsa_stream);
+
+#endif /* __SOUND_ARM_BCM2835_H */
diff --git a/drivers/staging/vc04_services/bcm2835-audio/vc_vchi_audioserv_defs.h b/drivers/staging/vc04_services/bcm2835-audio/vc_vchi_audioserv_defs.h
new file mode 100644 (file)
index 0000000..da96f1b
--- /dev/null
@@ -0,0 +1,108 @@
+/*****************************************************************************
+ * 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.
+ *****************************************************************************/
+
+#ifndef _VC_AUDIO_DEFS_H_
+#define _VC_AUDIO_DEFS_H_
+
+#define VC_AUDIOSERV_MIN_VER 1
+#define VC_AUDIOSERV_VER 2
+
+/* FourCC code used for VCHI connection */
+#define VC_AUDIO_SERVER_NAME  MAKE_FOURCC("AUDS")
+
+/*
+ *  List of screens that are currently supported
+ *  All message types supported for HOST->VC direction
+ */
+
+enum vc_audio_msg_type {
+       VC_AUDIO_MSG_TYPE_RESULT, // Generic result
+       VC_AUDIO_MSG_TYPE_COMPLETE, // Generic result
+       VC_AUDIO_MSG_TYPE_CONFIG, // Configure audio
+       VC_AUDIO_MSG_TYPE_CONTROL, // Configure audio
+       VC_AUDIO_MSG_TYPE_OPEN, // Configure audio
+       VC_AUDIO_MSG_TYPE_CLOSE, // Configure audio
+       VC_AUDIO_MSG_TYPE_START, // Configure audio
+       VC_AUDIO_MSG_TYPE_STOP, // Configure audio
+       VC_AUDIO_MSG_TYPE_WRITE, // Configure audio
+       VC_AUDIO_MSG_TYPE_MAX
+};
+
+/* configure the audio */
+
+struct vc_audio_config {
+       u32 channels;
+       u32 samplerate;
+       u32 bps;
+};
+
+struct vc_audio_control {
+       u32 volume;
+       u32 dest;
+};
+
+struct vc_audio_open {
+       u32 dummy;
+};
+
+struct vc_audio_close {
+       u32 dummy;
+};
+
+struct vc_audio_start {
+       u32 dummy;
+};
+
+struct vc_audio_stop {
+       u32 draining;
+};
+
+/* configure the write audio samples */
+struct vc_audio_write {
+       u32 count; // in bytes
+       u32 cookie1;
+       u32 cookie2;
+       s16 silence;
+       s16 max_packet;
+};
+
+/* Generic result for a request (VC->HOST) */
+struct vc_audio_result {
+       s32 success; // Success value
+};
+
+/* Generic result for a request (VC->HOST) */
+struct vc_audio_complete {
+       s32 count; // Success value
+       u32 cookie1;
+       u32 cookie2;
+};
+
+/* Message header for all messages in HOST->VC direction */
+struct vc_audio_msg {
+       s32 type; /* Message type (VC_AUDIO_MSG_TYPE) */
+       union {
+               struct vc_audio_config config;
+               struct vc_audio_control control;
+               struct vc_audio_open open;
+               struct vc_audio_close close;
+               struct vc_audio_start start;
+               struct vc_audio_stop stop;
+               struct vc_audio_write write;
+               struct vc_audio_result result;
+               struct vc_audio_complete complete;
+       } u;
+};
+
+#endif /* _VC_AUDIO_DEFS_H_ */