linux/drivers/staging/vc04_services/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        if (kcontrol->private_value == PCM_PLAYBACK_VOLUME) {
  45                uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
  46                uinfo->count = 1;
  47                uinfo->value.integer.min = CTRL_VOL_MIN;
  48                uinfo->value.integer.max = CTRL_VOL_MAX; /* 2303 */
  49        } else if (kcontrol->private_value == PCM_PLAYBACK_MUTE) {
  50                uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
  51                uinfo->count = 1;
  52                uinfo->value.integer.min = 0;
  53                uinfo->value.integer.max = 1;
  54        } else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE) {
  55                uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
  56                uinfo->count = 1;
  57                uinfo->value.integer.min = 0;
  58                uinfo->value.integer.max = AUDIO_DEST_MAX - 1;
  59        }
  60        return 0;
  61}
  62
  63/* toggles mute on or off depending on the value of nmute, and returns
  64 * 1 if the mute value was changed, otherwise 0
  65 */
  66static int toggle_mute(struct bcm2835_chip *chip, int nmute)
  67{
  68        /* if settings are ok, just return 0 */
  69        if (chip->mute == nmute)
  70                return 0;
  71
  72        /* if the sound is muted then we need to unmute */
  73        if (chip->mute == CTRL_VOL_MUTE) {
  74                chip->volume = chip->old_volume; /* copy the old volume back */
  75                audio_info("Unmuting, old_volume = %d, volume = %d ...\n", chip->old_volume, chip->volume);
  76        } else /* otherwise we mute */ {
  77                chip->old_volume = chip->volume;
  78                chip->volume = 26214; /* set volume to minimum level AKA mute */
  79                audio_info("Muting, old_volume = %d, volume = %d ...\n", chip->old_volume, chip->volume);
  80        }
  81
  82        chip->mute = nmute;
  83        return 1;
  84}
  85
  86static int snd_bcm2835_ctl_get(struct snd_kcontrol *kcontrol,
  87                               struct snd_ctl_elem_value *ucontrol)
  88{
  89        struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
  90
  91        if (mutex_lock_interruptible(&chip->audio_mutex))
  92                return -EINTR;
  93
  94        BUG_ON(!chip && !(chip->avail_substreams & AVAIL_SUBSTREAMS_MASK));
  95
  96        if (kcontrol->private_value == PCM_PLAYBACK_VOLUME)
  97                ucontrol->value.integer.value[0] = chip2alsa(chip->volume);
  98        else if (kcontrol->private_value == PCM_PLAYBACK_MUTE)
  99                ucontrol->value.integer.value[0] = chip->mute;
 100        else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE)
 101                ucontrol->value.integer.value[0] = chip->dest;
 102
 103        mutex_unlock(&chip->audio_mutex);
 104        return 0;
 105}
 106
 107static int snd_bcm2835_ctl_put(struct snd_kcontrol *kcontrol,
 108                                struct snd_ctl_elem_value *ucontrol)
 109{
 110        struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
 111        int changed = 0;
 112
 113        if (mutex_lock_interruptible(&chip->audio_mutex))
 114                return -EINTR;
 115
 116        if (kcontrol->private_value == PCM_PLAYBACK_VOLUME) {
 117                audio_info("Volume change attempted.. volume = %d new_volume = %d\n", chip->volume, (int)ucontrol->value.integer.value[0]);
 118                if (chip->mute == CTRL_VOL_MUTE) {
 119                        /* changed = toggle_mute(chip, CTRL_VOL_UNMUTE); */
 120                        changed = 1; /* should return 0 to signify no change but the mixer takes this as the opposite sign (no idea why) */
 121                        goto unlock;
 122                }
 123                if (changed || (ucontrol->value.integer.value[0] != chip2alsa(chip->volume))) {
 124                        chip->volume = alsa2chip(ucontrol->value.integer.value[0]);
 125                        changed = 1;
 126                }
 127
 128        } else if (kcontrol->private_value == PCM_PLAYBACK_MUTE) {
 129                /* Now implemented */
 130                audio_info(" Mute attempted\n");
 131                changed = toggle_mute(chip, ucontrol->value.integer.value[0]);
 132
 133        } else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE) {
 134                if (ucontrol->value.integer.value[0] != chip->dest) {
 135                        chip->dest = ucontrol->value.integer.value[0];
 136                        changed = 1;
 137                }
 138        }
 139
 140        if (changed && bcm2835_audio_set_ctls(chip))
 141                dev_err(chip->card->dev, "Failed to set ALSA controls..\n");
 142
 143unlock:
 144        mutex_unlock(&chip->audio_mutex);
 145        return changed;
 146}
 147
 148static DECLARE_TLV_DB_SCALE(snd_bcm2835_db_scale, CTRL_VOL_MIN, 1, 1);
 149
 150static struct snd_kcontrol_new snd_bcm2835_ctl[] = {
 151        {
 152                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 153                .name = "PCM Playback Volume",
 154                .index = 0,
 155                .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,
 156                .private_value = PCM_PLAYBACK_VOLUME,
 157                .info = snd_bcm2835_ctl_info,
 158                .get = snd_bcm2835_ctl_get,
 159                .put = snd_bcm2835_ctl_put,
 160                .count = 1,
 161                .tlv = {.p = snd_bcm2835_db_scale}
 162        },
 163        {
 164                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 165                .name = "PCM Playback Switch",
 166                .index = 0,
 167                .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
 168                .private_value = PCM_PLAYBACK_MUTE,
 169                .info = snd_bcm2835_ctl_info,
 170                .get = snd_bcm2835_ctl_get,
 171                .put = snd_bcm2835_ctl_put,
 172                .count = 1,
 173        },
 174        {
 175                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 176                .name = "PCM Playback Route",
 177                .index = 0,
 178                .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
 179                .private_value = PCM_PLAYBACK_DEVICE,
 180                .info = snd_bcm2835_ctl_info,
 181                .get = snd_bcm2835_ctl_get,
 182                .put = snd_bcm2835_ctl_put,
 183                .count = 1,
 184        },
 185};
 186
 187static int snd_bcm2835_spdif_default_info(struct snd_kcontrol *kcontrol,
 188        struct snd_ctl_elem_info *uinfo)
 189{
 190        uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
 191        uinfo->count = 1;
 192        return 0;
 193}
 194
 195static int snd_bcm2835_spdif_default_get(struct snd_kcontrol *kcontrol,
 196        struct snd_ctl_elem_value *ucontrol)
 197{
 198        struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
 199        int i;
 200
 201        if (mutex_lock_interruptible(&chip->audio_mutex))
 202                return -EINTR;
 203
 204        for (i = 0; i < 4; i++)
 205                ucontrol->value.iec958.status[i] =
 206                        (chip->spdif_status >> (i * 8)) & 0xff;
 207
 208        mutex_unlock(&chip->audio_mutex);
 209        return 0;
 210}
 211
 212static int snd_bcm2835_spdif_default_put(struct snd_kcontrol *kcontrol,
 213        struct snd_ctl_elem_value *ucontrol)
 214{
 215        struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
 216        unsigned int val = 0;
 217        int i, change;
 218
 219        if (mutex_lock_interruptible(&chip->audio_mutex))
 220                return -EINTR;
 221
 222        for (i = 0; i < 4; i++)
 223                val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8);
 224
 225        change = val != chip->spdif_status;
 226        chip->spdif_status = val;
 227
 228        mutex_unlock(&chip->audio_mutex);
 229        return change;
 230}
 231
 232static int snd_bcm2835_spdif_mask_info(struct snd_kcontrol *kcontrol,
 233        struct snd_ctl_elem_info *uinfo)
 234{
 235        uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
 236        uinfo->count = 1;
 237        return 0;
 238}
 239
 240static int snd_bcm2835_spdif_mask_get(struct snd_kcontrol *kcontrol,
 241        struct snd_ctl_elem_value *ucontrol)
 242{
 243        /*
 244         * bcm2835 supports only consumer mode and sets all other format flags
 245         * automatically. So the only thing left is signalling non-audio content
 246         */
 247        ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO;
 248        return 0;
 249}
 250
 251static int snd_bcm2835_spdif_stream_info(struct snd_kcontrol *kcontrol,
 252        struct snd_ctl_elem_info *uinfo)
 253{
 254        uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
 255        uinfo->count = 1;
 256        return 0;
 257}
 258
 259static int snd_bcm2835_spdif_stream_get(struct snd_kcontrol *kcontrol,
 260        struct snd_ctl_elem_value *ucontrol)
 261{
 262        struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
 263        int i;
 264
 265        if (mutex_lock_interruptible(&chip->audio_mutex))
 266                return -EINTR;
 267
 268        for (i = 0; i < 4; i++)
 269                ucontrol->value.iec958.status[i] =
 270                (chip->spdif_status >> (i * 8)) & 0xff;
 271
 272        mutex_unlock(&chip->audio_mutex);
 273        return 0;
 274}
 275
 276static int snd_bcm2835_spdif_stream_put(struct snd_kcontrol *kcontrol,
 277        struct snd_ctl_elem_value *ucontrol)
 278{
 279        struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
 280        unsigned int val = 0;
 281        int i, change;
 282
 283        if (mutex_lock_interruptible(&chip->audio_mutex))
 284                return -EINTR;
 285
 286        for (i = 0; i < 4; i++)
 287                val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8);
 288        change = val != chip->spdif_status;
 289        chip->spdif_status = val;
 290
 291        mutex_unlock(&chip->audio_mutex);
 292        return change;
 293}
 294
 295static struct snd_kcontrol_new snd_bcm2835_spdif[] = {
 296        {
 297                .iface = SNDRV_CTL_ELEM_IFACE_PCM,
 298                .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
 299                .info = snd_bcm2835_spdif_default_info,
 300                .get = snd_bcm2835_spdif_default_get,
 301                .put = snd_bcm2835_spdif_default_put
 302        },
 303        {
 304                .access = SNDRV_CTL_ELEM_ACCESS_READ,
 305                .iface = SNDRV_CTL_ELEM_IFACE_PCM,
 306                .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK),
 307                .info = snd_bcm2835_spdif_mask_info,
 308                .get = snd_bcm2835_spdif_mask_get,
 309        },
 310        {
 311                .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
 312                SNDRV_CTL_ELEM_ACCESS_INACTIVE,
 313                .iface = SNDRV_CTL_ELEM_IFACE_PCM,
 314                .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM),
 315                .info = snd_bcm2835_spdif_stream_info,
 316                .get = snd_bcm2835_spdif_stream_get,
 317                .put = snd_bcm2835_spdif_stream_put,
 318        },
 319};
 320
 321int snd_bcm2835_new_ctl(struct bcm2835_chip *chip)
 322{
 323        int err;
 324        unsigned int idx;
 325
 326        strcpy(chip->card->mixername, "Broadcom Mixer");
 327        for (idx = 0; idx < ARRAY_SIZE(snd_bcm2835_ctl); idx++) {
 328                err = snd_ctl_add(chip->card,
 329                                  snd_ctl_new1(&snd_bcm2835_ctl[idx], chip));
 330                if (err < 0)
 331                        return err;
 332        }
 333        for (idx = 0; idx < ARRAY_SIZE(snd_bcm2835_spdif); idx++) {
 334                err = snd_ctl_add(chip->card,
 335                                  snd_ctl_new1(&snd_bcm2835_spdif[idx], chip));
 336                if (err < 0)
 337                        return err;
 338        }
 339        return 0;
 340}
 341
 342static struct snd_kcontrol_new snd_bcm2835_headphones_ctl[] = {
 343        {
 344                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 345                .name = "Headphone Playback Volume",
 346                .index = 0,
 347                .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
 348                          SNDRV_CTL_ELEM_ACCESS_TLV_READ,
 349                .private_value = PCM_PLAYBACK_VOLUME,
 350                .info = snd_bcm2835_ctl_info,
 351                .get = snd_bcm2835_ctl_get,
 352                .put = snd_bcm2835_ctl_put,
 353                .count = 1,
 354                .tlv = {.p = snd_bcm2835_db_scale}
 355        },
 356        {
 357                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 358                .name = "Headphone Playback Switch",
 359                .index = 0,
 360                .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
 361                .private_value = PCM_PLAYBACK_MUTE,
 362                .info = snd_bcm2835_ctl_info,
 363                .get = snd_bcm2835_ctl_get,
 364                .put = snd_bcm2835_ctl_put,
 365                .count = 1,
 366        }
 367};
 368
 369int snd_bcm2835_new_headphones_ctl(struct bcm2835_chip *chip)
 370{
 371        int err;
 372        unsigned int idx;
 373
 374        strcpy(chip->card->mixername, "Broadcom Mixer");
 375        for (idx = 0; idx < ARRAY_SIZE(snd_bcm2835_headphones_ctl); idx++) {
 376                err = snd_ctl_add(chip->card,
 377                                  snd_ctl_new1(&snd_bcm2835_headphones_ctl[idx],
 378                                               chip));
 379                if (err)
 380                        return err;
 381        }
 382        return 0;
 383}
 384
 385static struct snd_kcontrol_new snd_bcm2835_hdmi[] = {
 386        {
 387                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 388                .name = "HDMI Playback Volume",
 389                .index = 0,
 390                .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
 391                          SNDRV_CTL_ELEM_ACCESS_TLV_READ,
 392                .private_value = PCM_PLAYBACK_VOLUME,
 393                .info = snd_bcm2835_ctl_info,
 394                .get = snd_bcm2835_ctl_get,
 395                .put = snd_bcm2835_ctl_put,
 396                .count = 1,
 397                .tlv = {.p = snd_bcm2835_db_scale}
 398        },
 399        {
 400                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 401                .name = "HDMI Playback Switch",
 402                .index = 0,
 403                .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
 404                .private_value = PCM_PLAYBACK_MUTE,
 405                .info = snd_bcm2835_ctl_info,
 406                .get = snd_bcm2835_ctl_get,
 407                .put = snd_bcm2835_ctl_put,
 408                .count = 1,
 409        }
 410};
 411
 412int snd_bcm2835_new_hdmi_ctl(struct bcm2835_chip *chip)
 413{
 414        int err;
 415        unsigned int idx;
 416
 417        strcpy(chip->card->mixername, "Broadcom Mixer");
 418        for (idx = 0; idx < ARRAY_SIZE(snd_bcm2835_hdmi); idx++) {
 419                err = snd_ctl_add(chip->card,
 420                                  snd_ctl_new1(&snd_bcm2835_hdmi[idx], chip));
 421                if (err)
 422                        return err;
 423        }
 424        return 0;
 425}
 426
 427