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 15 static 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 30 static 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 52 static 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 70 static 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 98 static DECLARE_TLV_DB_SCALE(snd_bcm2835_db_scale, CTRL_VOL_MIN, 1, 1); 99 100 static 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 122 static int snd_bcm2835_spdif_default_info(struct snd_kcontrol *kcontrol, 123 struct snd_ctl_elem_info *uinfo) 124 { 125 uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; 126 uinfo->count = 1; 127 return 0; 128 } 129 130 static int snd_bcm2835_spdif_default_get(struct snd_kcontrol *kcontrol, 131 struct snd_ctl_elem_value *ucontrol) 132 { 133 struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); 134 int i; 135 136 mutex_lock(&chip->audio_mutex); 137 138 for (i = 0; i < 4; i++) 139 ucontrol->value.iec958.status[i] = 140 (chip->spdif_status >> (i * 8)) & 0xff; 141 142 mutex_unlock(&chip->audio_mutex); 143 return 0; 144 } 145 146 static int snd_bcm2835_spdif_default_put(struct snd_kcontrol *kcontrol, 147 struct snd_ctl_elem_value *ucontrol) 148 { 149 struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); 150 unsigned int val = 0; 151 int i, change; 152 153 mutex_lock(&chip->audio_mutex); 154 155 for (i = 0; i < 4; i++) 156 val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8); 157 158 change = val != chip->spdif_status; 159 chip->spdif_status = val; 160 161 mutex_unlock(&chip->audio_mutex); 162 return change; 163 } 164 165 static int snd_bcm2835_spdif_mask_info(struct snd_kcontrol *kcontrol, 166 struct snd_ctl_elem_info *uinfo) 167 { 168 uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; 169 uinfo->count = 1; 170 return 0; 171 } 172 173 static int snd_bcm2835_spdif_mask_get(struct snd_kcontrol *kcontrol, 174 struct snd_ctl_elem_value *ucontrol) 175 { 176 /* 177 * bcm2835 supports only consumer mode and sets all other format flags 178 * automatically. So the only thing left is signalling non-audio content 179 */ 180 ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO; 181 return 0; 182 } 183 184 static const struct snd_kcontrol_new snd_bcm2835_spdif[] = { 185 { 186 .iface = SNDRV_CTL_ELEM_IFACE_PCM, 187 .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), 188 .info = snd_bcm2835_spdif_default_info, 189 .get = snd_bcm2835_spdif_default_get, 190 .put = snd_bcm2835_spdif_default_put 191 }, 192 { 193 .access = SNDRV_CTL_ELEM_ACCESS_READ, 194 .iface = SNDRV_CTL_ELEM_IFACE_PCM, 195 .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK), 196 .info = snd_bcm2835_spdif_mask_info, 197 .get = snd_bcm2835_spdif_mask_get, 198 }, 199 }; 200 201 static int create_ctls(struct bcm2835_chip *chip, size_t size, 202 const struct snd_kcontrol_new *kctls) 203 { 204 int i, err; 205 206 for (i = 0; i < size; i++) { 207 err = snd_ctl_add(chip->card, snd_ctl_new1(&kctls[i], chip)); 208 if (err < 0) 209 return err; 210 } 211 return 0; 212 } 213 214 int snd_bcm2835_new_headphones_ctl(struct bcm2835_chip *chip) 215 { 216 strscpy(chip->card->mixername, "Broadcom Mixer", sizeof(chip->card->mixername)); 217 return create_ctls(chip, ARRAY_SIZE(snd_bcm2835_ctl), 218 snd_bcm2835_ctl); 219 } 220 221 int snd_bcm2835_new_hdmi_ctl(struct bcm2835_chip *chip) 222 { 223 int err; 224 225 strscpy(chip->card->mixername, "Broadcom Mixer", sizeof(chip->card->mixername)); 226 err = create_ctls(chip, ARRAY_SIZE(snd_bcm2835_ctl), snd_bcm2835_ctl); 227 if (err < 0) 228 return err; 229 return create_ctls(chip, ARRAY_SIZE(snd_bcm2835_spdif), 230 snd_bcm2835_spdif); 231 } 232 233