Linux-libre 5.4.49-gnu
[librecmc/linux-libre.git] / 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
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