xref: /linux/drivers/staging/vc04_services/bcm2835-audio/bcm2835-ctl.c (revision e5a52fd2b8cdb700b3c07b030e050a49ef3156b9)
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 
303