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