xref: /linux/drivers/staging/vc04_services/bcm2835-audio/bcm2835-ctl.c (revision a100922a3855eb35ecd465f1d558546b1e144445)
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 
bcm2835_audio_set_chip_ctls(struct bcm2835_chip * chip)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 
snd_bcm2835_ctl_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)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 
snd_bcm2835_ctl_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 
snd_bcm2835_ctl_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)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 	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 
104 static DECLARE_TLV_DB_SCALE(snd_bcm2835_db_scale, CTRL_VOL_MIN, 1, 1);
105 
106 static 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 
snd_bcm2835_spdif_default_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)128 static 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 
snd_bcm2835_spdif_default_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)136 static 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 
snd_bcm2835_spdif_default_put(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)152 static 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 
snd_bcm2835_spdif_mask_info(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_info * uinfo)171 static 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 
snd_bcm2835_spdif_mask_get(struct snd_kcontrol * kcontrol,struct snd_ctl_elem_value * ucontrol)179 static 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 
190 static 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 
create_ctls(struct bcm2835_chip * chip,size_t size,const struct snd_kcontrol_new * kctls)207 static 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 
snd_bcm2835_new_headphones_ctl(struct bcm2835_chip * chip)220 int 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 
snd_bcm2835_new_hdmi_ctl(struct bcm2835_chip * chip)227 int 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 
239