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 .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 131 static 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 139 static 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 155 static 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 174 static 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 182 static 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 193 static 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 210 static 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 223 int 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 235 static 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 262 int 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 269 static 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 296 int 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