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(alsa_stream: 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(lock: &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(lock: &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 | |
122 | static int snd_bcm2835_spdif_default_info(struct snd_kcontrol *kcontrol, |
123 | struct snd_ctl_elem_info *uinfo) |
124 | { |
125 | uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; |
126 | uinfo->count = 1; |
127 | return 0; |
128 | } |
129 | |
130 | static int snd_bcm2835_spdif_default_get(struct snd_kcontrol *kcontrol, |
131 | struct snd_ctl_elem_value *ucontrol) |
132 | { |
133 | struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); |
134 | int i; |
135 | |
136 | mutex_lock(&chip->audio_mutex); |
137 | |
138 | for (i = 0; i < 4; i++) |
139 | ucontrol->value.iec958.status[i] = |
140 | (chip->spdif_status >> (i * 8)) & 0xff; |
141 | |
142 | mutex_unlock(lock: &chip->audio_mutex); |
143 | return 0; |
144 | } |
145 | |
146 | static int snd_bcm2835_spdif_default_put(struct snd_kcontrol *kcontrol, |
147 | struct snd_ctl_elem_value *ucontrol) |
148 | { |
149 | struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); |
150 | unsigned int val = 0; |
151 | int i, change; |
152 | |
153 | mutex_lock(&chip->audio_mutex); |
154 | |
155 | for (i = 0; i < 4; i++) |
156 | val |= (unsigned int)ucontrol->value.iec958.status[i] << (i * 8); |
157 | |
158 | change = val != chip->spdif_status; |
159 | chip->spdif_status = val; |
160 | |
161 | mutex_unlock(lock: &chip->audio_mutex); |
162 | return change; |
163 | } |
164 | |
165 | static int snd_bcm2835_spdif_mask_info(struct snd_kcontrol *kcontrol, |
166 | struct snd_ctl_elem_info *uinfo) |
167 | { |
168 | uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958; |
169 | uinfo->count = 1; |
170 | return 0; |
171 | } |
172 | |
173 | static int snd_bcm2835_spdif_mask_get(struct snd_kcontrol *kcontrol, |
174 | struct snd_ctl_elem_value *ucontrol) |
175 | { |
176 | /* |
177 | * bcm2835 supports only consumer mode and sets all other format flags |
178 | * automatically. So the only thing left is signalling non-audio content |
179 | */ |
180 | ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO; |
181 | return 0; |
182 | } |
183 | |
184 | static const struct snd_kcontrol_new snd_bcm2835_spdif[] = { |
185 | { |
186 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, |
187 | .name = SNDRV_CTL_NAME_IEC958("" , PLAYBACK, DEFAULT), |
188 | .info = snd_bcm2835_spdif_default_info, |
189 | .get = snd_bcm2835_spdif_default_get, |
190 | .put = snd_bcm2835_spdif_default_put |
191 | }, |
192 | { |
193 | .access = SNDRV_CTL_ELEM_ACCESS_READ, |
194 | .iface = SNDRV_CTL_ELEM_IFACE_PCM, |
195 | .name = SNDRV_CTL_NAME_IEC958("" , PLAYBACK, CON_MASK), |
196 | .info = snd_bcm2835_spdif_mask_info, |
197 | .get = snd_bcm2835_spdif_mask_get, |
198 | }, |
199 | }; |
200 | |
201 | static int create_ctls(struct bcm2835_chip *chip, size_t size, |
202 | const struct snd_kcontrol_new *kctls) |
203 | { |
204 | int i, err; |
205 | |
206 | for (i = 0; i < size; i++) { |
207 | err = snd_ctl_add(card: chip->card, kcontrol: snd_ctl_new1(kcontrolnew: &kctls[i], private_data: chip)); |
208 | if (err < 0) |
209 | return err; |
210 | } |
211 | return 0; |
212 | } |
213 | |
214 | int snd_bcm2835_new_headphones_ctl(struct bcm2835_chip *chip) |
215 | { |
216 | strscpy(chip->card->mixername, "Broadcom Mixer" , sizeof(chip->card->mixername)); |
217 | return create_ctls(chip, ARRAY_SIZE(snd_bcm2835_ctl), |
218 | kctls: snd_bcm2835_ctl); |
219 | } |
220 | |
221 | int snd_bcm2835_new_hdmi_ctl(struct bcm2835_chip *chip) |
222 | { |
223 | int err; |
224 | |
225 | strscpy(chip->card->mixername, "Broadcom Mixer" , sizeof(chip->card->mixername)); |
226 | err = create_ctls(chip, ARRAY_SIZE(snd_bcm2835_ctl), kctls: snd_bcm2835_ctl); |
227 | if (err < 0) |
228 | return err; |
229 | return create_ctls(chip, ARRAY_SIZE(snd_bcm2835_spdif), |
230 | kctls: snd_bcm2835_spdif); |
231 | } |
232 | |
233 | |