linux/drivers/staging/vc04_services/bcm2835-audio/bcm2835-ctl.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/* Copyright 2011 Broadcom Corporation.  All rights reserved. */
   3
   4#include <sound/core.h>
   5#include <sound/control.h>
   6#include <sound/tlv.h>
   7#include <sound/asoundef.h>
   8
   9#include "bcm2835.h"
  10
  11/* volume maximum and minimum in terms of 0.01dB */
  12#define CTRL_VOL_MAX 400
  13#define CTRL_VOL_MIN -10239 /* originally -10240 */
  14
  15static int bcm2835_audio_set_chip_ctls(struct bcm2835_chip *chip)
  16{
  17        int i, err = 0;
  18
  19        /* change ctls for all substreams */
  20        for (i = 0; i < MAX_SUBSTREAMS; i++) {
  21                if (chip->alsa_stream[i]) {
  22                        err = bcm2835_audio_set_ctls(chip->alsa_stream[i]);
  23                        if (err < 0)
  24                                break;
  25                }
  26        }
  27        return err;
  28}
  29
  30static int snd_bcm2835_ctl_info(struct snd_kcontrol *kcontrol,
  31                                struct snd_ctl_elem_info *uinfo)
  32{
  33        if (kcontrol->private_value == PCM_PLAYBACK_VOLUME) {
  34                uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
  35                uinfo->count = 1;
  36                uinfo->value.integer.min = CTRL_VOL_MIN;
  37                uinfo->value.integer.max = CTRL_VOL_MAX; /* 2303 */
  38        } else if (kcontrol->private_value == PCM_PLAYBACK_MUTE) {
  39                uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
  40                uinfo->count = 1;
  41                uinfo->value.integer.min = 0;
  42                uinfo->value.integer.max = 1;
  43        } else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE) {
  44                uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
  45                uinfo->count = 1;
  46                uinfo->value.integer.min = 0;
  47                uinfo->value.integer.max = AUDIO_DEST_MAX - 1;
  48        }
  49        return 0;
  50}
  51
  52static int snd_bcm2835_ctl_get(struct snd_kcontrol *kcontrol,
  53                               struct snd_ctl_elem_value *ucontrol)
  54{
  55        struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
  56
  57        mutex_lock(&chip->audio_mutex);
  58
  59        if (kcontrol->private_value == PCM_PLAYBACK_VOLUME)
  60                ucontrol->value.integer.value[0] = chip->volume;
  61        else if (kcontrol->private_value == PCM_PLAYBACK_MUTE)
  62                ucontrol->value.integer.value[0] = chip->mute;
  63        else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE)
  64                ucontrol->value.integer.value[0] = chip->dest;
  65
  66        mutex_unlock(&chip->audio_mutex);
  67        return 0;
  68}
  69
  70static int snd_bcm2835_ctl_put(struct snd_kcontrol *kcontrol,
  71                               struct snd_ctl_elem_value *ucontrol)
  72{
  73        struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
  74        int val, *valp;
  75        int changed = 0;
  76
  77        if (kcontrol->private_value == PCM_PLAYBACK_VOLUME)
  78                valp = &chip->volume;
  79        else if (kcontrol->private_value == PCM_PLAYBACK_MUTE)
  80                valp = &chip->mute;
  81        else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE)
  82                valp = &chip->dest;
  83        else
  84                return -EINVAL;
  85
  86        val = ucontrol->value.integer.value[0];
  87        mutex_lock(&chip->audio_mutex);
  88        if (val != *valp) {
  89                *valp = val;
  90                changed = 1;
  91                if (bcm2835_audio_set_chip_ctls(chip))
  92                        dev_err(chip->card->dev, "Failed to set ALSA controls..\n");
  93        }
  94        mutex_unlock(&chip->audio_mutex);
  95        return changed;
  96}
  97
  98static DECLARE_TLV_DB_SCALE(snd_bcm2835_db_scale, CTRL_VOL_MIN, 1, 1);
  99
 100static const struct snd_kcontrol_new snd_bcm2835_ctl[] = {
 101        {
 102                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 103                .name = "PCM Playback Volume",
 104                .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,
 105                .private_value = PCM_PLAYBACK_VOLUME,
 106                .info = snd_bcm2835_ctl_info,
 107                .get = snd_bcm2835_ctl_get,
 108                .put = snd_bcm2835_ctl_put,
 109                .tlv = {.p = snd_bcm2835_db_scale}
 110        },
 111        {
 112                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 113                .name = "PCM Playback Switch",
 114                .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
 115                .private_value = PCM_PLAYBACK_MUTE,
 116                .info = snd_bcm2835_ctl_info,
 117                .get = snd_bcm2835_ctl_get,
 118                .put = snd_bcm2835_ctl_put,
 119        },
 120        {
 121                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 122                .name = "PCM Playback Route",
 123                .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
 124                .private_value = PCM_PLAYBACK_DEVICE,
 125                .info = snd_bcm2835_ctl_info,
 126                .get = snd_bcm2835_ctl_get,
 127                .put = snd_bcm2835_ctl_put,
 128        },
 129};
 130
 131static int snd_bcm2835_spdif_default_info(struct snd_kcontrol *kcontrol,
 132                                          struct snd_ctl_elem_info *uinfo)
 133{
 134        uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
 135        uinfo->count = 1;
 136        return 0;
 137}
 138
 139static int snd_bcm2835_spdif_default_get(struct snd_kcontrol *kcontrol,
 140                                         struct snd_ctl_elem_value *ucontrol)
 141{
 142        struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
 143        int i;
 144
 145        mutex_lock(&chip->audio_mutex);
 146
 147        for (i = 0; i < 4; i++)
 148                ucontrol->value.iec958.status[i] =
 149                        (chip->spdif_status >> (i * 8)) & 0xff;
 150
 151        mutex_unlock(&chip->audio_mutex);
 152        return 0;
 153}
 154
 155static int snd_bcm2835_spdif_default_put(struct snd_kcontrol *kcontrol,
 156                                         struct snd_ctl_elem_value *ucontrol)
 157{
 158        struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
 159        unsigned int val = 0;
 160        int i, change;
 161
 162        mutex_lock(&chip->audio_mutex);
 163
 164        for (i = 0; i < 4; i++)
 165                val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8);
 166
 167        change = val != chip->spdif_status;
 168        chip->spdif_status = val;
 169
 170        mutex_unlock(&chip->audio_mutex);
 171        return change;
 172}
 173
 174static int snd_bcm2835_spdif_mask_info(struct snd_kcontrol *kcontrol,
 175                                       struct snd_ctl_elem_info *uinfo)
 176{
 177        uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
 178        uinfo->count = 1;
 179        return 0;
 180}
 181
 182static int snd_bcm2835_spdif_mask_get(struct snd_kcontrol *kcontrol,
 183                                      struct snd_ctl_elem_value *ucontrol)
 184{
 185        /*
 186         * bcm2835 supports only consumer mode and sets all other format flags
 187         * automatically. So the only thing left is signalling non-audio content
 188         */
 189        ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO;
 190        return 0;
 191}
 192
 193static const struct snd_kcontrol_new snd_bcm2835_spdif[] = {
 194        {
 195                .iface = SNDRV_CTL_ELEM_IFACE_PCM,
 196                .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
 197                .info = snd_bcm2835_spdif_default_info,
 198                .get = snd_bcm2835_spdif_default_get,
 199                .put = snd_bcm2835_spdif_default_put
 200        },
 201        {
 202                .access = SNDRV_CTL_ELEM_ACCESS_READ,
 203                .iface = SNDRV_CTL_ELEM_IFACE_PCM,
 204                .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK),
 205                .info = snd_bcm2835_spdif_mask_info,
 206                .get = snd_bcm2835_spdif_mask_get,
 207        },
 208};
 209
 210static int create_ctls(struct bcm2835_chip *chip, size_t size,
 211                       const struct snd_kcontrol_new *kctls)
 212{
 213        int i, err;
 214
 215        for (i = 0; i < size; i++) {
 216                err = snd_ctl_add(chip->card, snd_ctl_new1(&kctls[i], chip));
 217                if (err < 0)
 218                        return err;
 219        }
 220        return 0;
 221}
 222
 223int snd_bcm2835_new_ctl(struct bcm2835_chip *chip)
 224{
 225        int err;
 226
 227        strcpy(chip->card->mixername, "Broadcom Mixer");
 228        err = create_ctls(chip, ARRAY_SIZE(snd_bcm2835_ctl), snd_bcm2835_ctl);
 229        if (err < 0)
 230                return err;
 231        return create_ctls(chip, ARRAY_SIZE(snd_bcm2835_spdif),
 232                           snd_bcm2835_spdif);
 233}
 234
 235static const struct snd_kcontrol_new snd_bcm2835_headphones_ctl[] = {
 236        {
 237                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 238                .name = "Headphone Playback Volume",
 239                .index = 0,
 240                .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
 241                          SNDRV_CTL_ELEM_ACCESS_TLV_READ,
 242                .private_value = PCM_PLAYBACK_VOLUME,
 243                .info = snd_bcm2835_ctl_info,
 244                .get = snd_bcm2835_ctl_get,
 245                .put = snd_bcm2835_ctl_put,
 246                .count = 1,
 247                .tlv = {.p = snd_bcm2835_db_scale}
 248        },
 249        {
 250                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 251                .name = "Headphone Playback Switch",
 252                .index = 0,
 253                .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
 254                .private_value = PCM_PLAYBACK_MUTE,
 255                .info = snd_bcm2835_ctl_info,
 256                .get = snd_bcm2835_ctl_get,
 257                .put = snd_bcm2835_ctl_put,
 258                .count = 1,
 259        }
 260};
 261
 262int snd_bcm2835_new_headphones_ctl(struct bcm2835_chip *chip)
 263{
 264        strcpy(chip->card->mixername, "Broadcom Mixer");
 265        return create_ctls(chip, ARRAY_SIZE(snd_bcm2835_headphones_ctl),
 266                           snd_bcm2835_headphones_ctl);
 267}
 268
 269static const struct snd_kcontrol_new snd_bcm2835_hdmi[] = {
 270        {
 271                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 272                .name = "HDMI Playback Volume",
 273                .index = 0,
 274                .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
 275                          SNDRV_CTL_ELEM_ACCESS_TLV_READ,
 276                .private_value = PCM_PLAYBACK_VOLUME,
 277                .info = snd_bcm2835_ctl_info,
 278                .get = snd_bcm2835_ctl_get,
 279                .put = snd_bcm2835_ctl_put,
 280                .count = 1,
 281                .tlv = {.p = snd_bcm2835_db_scale}
 282        },
 283        {
 284                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 285                .name = "HDMI Playback Switch",
 286                .index = 0,
 287                .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
 288                .private_value = PCM_PLAYBACK_MUTE,
 289                .info = snd_bcm2835_ctl_info,
 290                .get = snd_bcm2835_ctl_get,
 291                .put = snd_bcm2835_ctl_put,
 292                .count = 1,
 293        }
 294};
 295
 296int snd_bcm2835_new_hdmi_ctl(struct bcm2835_chip *chip)
 297{
 298        strcpy(chip->card->mixername, "Broadcom Mixer");
 299        return create_ctls(chip, ARRAY_SIZE(snd_bcm2835_hdmi),
 300                           snd_bcm2835_hdmi);
 301}
 302
 303