Branch data Line data Source code
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 : 3 : 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 : 3 : for (i = 0; i < MAX_SUBSTREAMS; i++) {
21 : 3 : if (chip->alsa_stream[i]) {
22 : 0 : err = bcm2835_audio_set_ctls(chip->alsa_stream[i]);
23 : 0 : if (err < 0)
24 : : break;
25 : : }
26 : : }
27 : 3 : return err;
28 : : }
29 : :
30 : 3 : static int snd_bcm2835_ctl_info(struct snd_kcontrol *kcontrol,
31 : : struct snd_ctl_elem_info *uinfo)
32 : : {
33 : 3 : if (kcontrol->private_value == PCM_PLAYBACK_VOLUME) {
34 : 3 : uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
35 : 3 : uinfo->count = 1;
36 : 3 : uinfo->value.integer.min = CTRL_VOL_MIN;
37 : 3 : uinfo->value.integer.max = CTRL_VOL_MAX; /* 2303 */
38 : 3 : } else if (kcontrol->private_value == PCM_PLAYBACK_MUTE) {
39 : 3 : uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
40 : 3 : uinfo->count = 1;
41 : 3 : uinfo->value.integer.min = 0;
42 : 3 : uinfo->value.integer.max = 1;
43 : 0 : } else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE) {
44 : 0 : uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
45 : 0 : uinfo->count = 1;
46 : 0 : uinfo->value.integer.min = 0;
47 : 0 : uinfo->value.integer.max = AUDIO_DEST_MAX - 1;
48 : : }
49 : 3 : return 0;
50 : : }
51 : :
52 : 3 : static int snd_bcm2835_ctl_get(struct snd_kcontrol *kcontrol,
53 : : struct snd_ctl_elem_value *ucontrol)
54 : : {
55 : 3 : struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
56 : :
57 : 3 : mutex_lock(&chip->audio_mutex);
58 : :
59 : 3 : if (kcontrol->private_value == PCM_PLAYBACK_VOLUME)
60 : 3 : ucontrol->value.integer.value[0] = chip->volume;
61 : 3 : else if (kcontrol->private_value == PCM_PLAYBACK_MUTE)
62 : 3 : ucontrol->value.integer.value[0] = chip->mute;
63 : 0 : else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE)
64 : 0 : ucontrol->value.integer.value[0] = chip->dest;
65 : :
66 : 3 : mutex_unlock(&chip->audio_mutex);
67 : 3 : return 0;
68 : : }
69 : :
70 : 3 : static int snd_bcm2835_ctl_put(struct snd_kcontrol *kcontrol,
71 : : struct snd_ctl_elem_value *ucontrol)
72 : : {
73 : 3 : struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
74 : : int val, *valp;
75 : : int changed = 0;
76 : :
77 : 3 : if (kcontrol->private_value == PCM_PLAYBACK_VOLUME)
78 : 3 : valp = &chip->volume;
79 : 3 : else if (kcontrol->private_value == PCM_PLAYBACK_MUTE)
80 : 3 : valp = &chip->mute;
81 : 0 : else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE)
82 : 0 : valp = &chip->dest;
83 : : else
84 : : return -EINVAL;
85 : :
86 : 3 : val = ucontrol->value.integer.value[0];
87 : 3 : mutex_lock(&chip->audio_mutex);
88 : 3 : if (val != *valp) {
89 : 3 : *valp = val;
90 : : changed = 1;
91 : 3 : if (bcm2835_audio_set_chip_ctls(chip))
92 : 0 : dev_err(chip->card->dev, "Failed to set ALSA controls..\n");
93 : : }
94 : 3 : mutex_unlock(&chip->audio_mutex);
95 : 3 : 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 : 0 : static int snd_bcm2835_spdif_default_info(struct snd_kcontrol *kcontrol,
132 : : struct snd_ctl_elem_info *uinfo)
133 : : {
134 : 0 : uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
135 : 0 : uinfo->count = 1;
136 : 0 : return 0;
137 : : }
138 : :
139 : 0 : static int snd_bcm2835_spdif_default_get(struct snd_kcontrol *kcontrol,
140 : : struct snd_ctl_elem_value *ucontrol)
141 : : {
142 : 0 : struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
143 : : int i;
144 : :
145 : 0 : mutex_lock(&chip->audio_mutex);
146 : :
147 : 0 : for (i = 0; i < 4; i++)
148 : 0 : ucontrol->value.iec958.status[i] =
149 : 0 : (chip->spdif_status >> (i * 8)) & 0xff;
150 : :
151 : 0 : mutex_unlock(&chip->audio_mutex);
152 : 0 : return 0;
153 : : }
154 : :
155 : 0 : static int snd_bcm2835_spdif_default_put(struct snd_kcontrol *kcontrol,
156 : : struct snd_ctl_elem_value *ucontrol)
157 : : {
158 : 0 : struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol);
159 : : unsigned int val = 0;
160 : : int i, change;
161 : :
162 : 0 : mutex_lock(&chip->audio_mutex);
163 : :
164 : 0 : for (i = 0; i < 4; i++)
165 : 0 : val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8);
166 : :
167 : 0 : change = val != chip->spdif_status;
168 : 0 : chip->spdif_status = val;
169 : :
170 : 0 : mutex_unlock(&chip->audio_mutex);
171 : 0 : return change;
172 : : }
173 : :
174 : 0 : static int snd_bcm2835_spdif_mask_info(struct snd_kcontrol *kcontrol,
175 : : struct snd_ctl_elem_info *uinfo)
176 : : {
177 : 0 : uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
178 : 0 : uinfo->count = 1;
179 : 0 : return 0;
180 : : }
181 : :
182 : 0 : 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 : 0 : ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO;
190 : 0 : 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 : 3 : 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 : 3 : for (i = 0; i < size; i++) {
216 : 3 : err = snd_ctl_add(chip->card, snd_ctl_new1(&kctls[i], chip));
217 : 3 : if (err < 0)
218 : 0 : return err;
219 : : }
220 : : return 0;
221 : : }
222 : :
223 : 0 : int snd_bcm2835_new_ctl(struct bcm2835_chip *chip)
224 : : {
225 : : int err;
226 : :
227 : 0 : strcpy(chip->card->mixername, "Broadcom Mixer");
228 : 0 : err = create_ctls(chip, ARRAY_SIZE(snd_bcm2835_ctl), snd_bcm2835_ctl);
229 : 0 : if (err < 0)
230 : : return err;
231 : 0 : 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 : 3 : int snd_bcm2835_new_headphones_ctl(struct bcm2835_chip *chip)
263 : : {
264 : 3 : strcpy(chip->card->mixername, "Broadcom Mixer");
265 : 3 : 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 : 3 : int snd_bcm2835_new_hdmi_ctl(struct bcm2835_chip *chip)
297 : : {
298 : 3 : strcpy(chip->card->mixername, "Broadcom Mixer");
299 : 3 : return create_ctls(chip, ARRAY_SIZE(snd_bcm2835_hdmi),
300 : : snd_bcm2835_hdmi);
301 : : }
302 : :
|