FILE
bcm2835-ctl.c
File preview with syntax highlighting.
linux-6.19.14/drivers/staging/vc04_services/bcm2835-audio/bcm2835-ctl.c
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 struct snd_ctl_elem_info info; 75 int val, *valp; 76 int changed = 0; 77 78 if (kcontrol->private_value == PCM_PLAYBACK_VOLUME) 79 valp = &chip->volume; 80 else if (kcontrol->private_value == PCM_PLAYBACK_MUTE) 81 valp = &chip->mute; 82 else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE) 83 valp = &chip->dest; 84 else 85 return -EINVAL; 86 87 val = ucontrol->value.integer.value[0]; 88 89 snd_bcm2835_ctl_info(kcontrol, &info); 90 if (val < info.value.integer.min || val > info.value.integer.max) 91 return -EINVAL; 92 93 mutex_lock(&chip->audio_mutex); 94 if (val != *valp) { 95 *valp = val; 96 changed = 1; 97 if (bcm2835_audio_set_chip_ctls(chip)) 98 dev_err(chip->card->dev, "Failed to set ALSA controls..\n"); 99 } 100 mutex_unlock(&chip->audio_mutex); 101 return changed; 102} 103 104static DECLARE_TLV_DB_SCALE(snd_bcm2835_db_scale, CTRL_VOL_MIN, 1, 1); 105 106static const struct snd_kcontrol_new snd_bcm2835_ctl[] = { 107 { 108 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 109 .name = "PCM Playback Volume", 110 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, 111 .private_value = PCM_PLAYBACK_VOLUME, 112 .info = snd_bcm2835_ctl_info, 113 .get = snd_bcm2835_ctl_get, 114 .put = snd_bcm2835_ctl_put, 115 .tlv = {.p = snd_bcm2835_db_scale} 116 }, 117 { 118 .iface = SNDRV_CTL_ELEM_IFACE_MIXER, 119 .name = "PCM Playback Switch", 120 .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, 121 .private_value = PCM_PLAYBACK_MUTE, 122 .info = snd_bcm2835_ctl_info, 123 .get = snd_bcm2835_ctl_get, 124 .put = snd_bcm2835_ctl_put, 125 }, 126}; 127 128static int snd_bcm2835_spdif_default_info(struct snd_kcontrol *kcontrol, 129 struct snd_ctl_elem_info *uinfo) 130{ 131 uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; 132 uinfo->count = 1; 133 return 0; 134} 135 136static int snd_bcm2835_spdif_default_get(struct snd_kcontrol *kcontrol, 137 struct snd_ctl_elem_value *ucontrol) 138{ 139 struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); 140 int i; 141 142 mutex_lock(&chip->audio_mutex); 143 144 for (i = 0; i < 4; i++) 145 ucontrol->value.iec958.status[i] = 146 (chip->spdif_status >> (i * 8)) & 0xff; 147 148 mutex_unlock(&chip->audio_mutex); 149 return 0; 150} 151 152static int snd_bcm2835_spdif_default_put(struct snd_kcontrol *kcontrol, 153 struct snd_ctl_elem_value *ucontrol) 154{ 155 struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); 156 unsigned int val = 0; 157 int i, change; 158 159 mutex_lock(&chip->audio_mutex); 160 161 for (i = 0; i < 4; i++) 162 val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8); 163 164 change = val != chip->spdif_status; 165 chip->spdif_status = val; 166 167 mutex_unlock(&chip->audio_mutex); 168 return change; 169} 170 171static int snd_bcm2835_spdif_mask_info(struct snd_kcontrol *kcontrol, 172 struct snd_ctl_elem_info *uinfo) 173{ 174 uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; 175 uinfo->count = 1; 176 return 0; 177} 178 179static int snd_bcm2835_spdif_mask_get(struct snd_kcontrol *kcontrol, 180 struct snd_ctl_elem_value *ucontrol) 181{ 182 /* 183 * bcm2835 supports only consumer mode and sets all other format flags 184 * automatically. So the only thing left is signalling non-audio content 185 */ 186 ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO; 187 return 0; 188} 189 190static const struct snd_kcontrol_new snd_bcm2835_spdif[] = { 191 { 192 .iface = SNDRV_CTL_ELEM_IFACE_PCM, 193 .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT), 194 .info = snd_bcm2835_spdif_default_info, 195 .get = snd_bcm2835_spdif_default_get, 196 .put = snd_bcm2835_spdif_default_put 197 }, 198 { 199 .access = SNDRV_CTL_ELEM_ACCESS_READ, 200 .iface = SNDRV_CTL_ELEM_IFACE_PCM, 201 .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, CON_MASK), 202 .info = snd_bcm2835_spdif_mask_info, 203 .get = snd_bcm2835_spdif_mask_get, 204 }, 205}; 206 207static int create_ctls(struct bcm2835_chip *chip, size_t size, 208 const struct snd_kcontrol_new *kctls) 209{ 210 int i, err; 211 212 for (i = 0; i < size; i++) { 213 err = snd_ctl_add(chip->card, snd_ctl_new1(&kctls[i], chip)); 214 if (err < 0) 215 return err; 216 } 217 return 0; 218} 219 220int snd_bcm2835_new_headphones_ctl(struct bcm2835_chip *chip) 221{ 222 strscpy(chip->card->mixername, "Broadcom Mixer", sizeof(chip->card->mixername)); 223 return create_ctls(chip, ARRAY_SIZE(snd_bcm2835_ctl), 224 snd_bcm2835_ctl); 225} 226 227int snd_bcm2835_new_hdmi_ctl(struct bcm2835_chip *chip) 228{ 229 int err; 230 231 strscpy(chip->card->mixername, "Broadcom Mixer", sizeof(chip->card->mixername)); 232 err = create_ctls(chip, ARRAY_SIZE(snd_bcm2835_ctl), snd_bcm2835_ctl); 233 if (err < 0) 234 return err; 235 return create_ctls(chip, ARRAY_SIZE(snd_bcm2835_spdif), 236 snd_bcm2835_spdif); 237} 238
~/
Read-only file preview.