1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /*************************************************************************** |
3 | msnd_pinnacle_mixer.c - description |
4 | ------------------- |
5 | begin : Fre Jun 7 2002 |
6 | copyright : (C) 2002 by karsten wiese |
7 | email : annabellesgarden@yahoo.de |
8 | ***************************************************************************/ |
9 | |
10 | /*************************************************************************** |
11 | * * |
12 | * * |
13 | ***************************************************************************/ |
14 | |
15 | #include <linux/io.h> |
16 | #include <linux/export.h> |
17 | |
18 | #include <sound/core.h> |
19 | #include <sound/control.h> |
20 | #include "msnd.h" |
21 | #include "msnd_pinnacle.h" |
22 | |
23 | |
24 | #define MSND_MIXER_VOLUME 0 |
25 | #define MSND_MIXER_PCM 1 |
26 | #define MSND_MIXER_AUX 2 /* Input source 1 (aux1) */ |
27 | #define MSND_MIXER_IMIX 3 /* Recording monitor */ |
28 | #define MSND_MIXER_SYNTH 4 |
29 | #define MSND_MIXER_SPEAKER 5 |
30 | #define MSND_MIXER_LINE 6 |
31 | #define MSND_MIXER_MIC 7 |
32 | #define MSND_MIXER_RECLEV 11 /* Recording level */ |
33 | #define MSND_MIXER_IGAIN 12 /* Input gain */ |
34 | #define MSND_MIXER_OGAIN 13 /* Output gain */ |
35 | #define MSND_MIXER_DIGITAL 17 /* Digital (input) 1 */ |
36 | |
37 | /* Device mask bits */ |
38 | |
39 | #define MSND_MASK_VOLUME (1 << MSND_MIXER_VOLUME) |
40 | #define MSND_MASK_SYNTH (1 << MSND_MIXER_SYNTH) |
41 | #define MSND_MASK_PCM (1 << MSND_MIXER_PCM) |
42 | #define MSND_MASK_SPEAKER (1 << MSND_MIXER_SPEAKER) |
43 | #define MSND_MASK_LINE (1 << MSND_MIXER_LINE) |
44 | #define MSND_MASK_MIC (1 << MSND_MIXER_MIC) |
45 | #define MSND_MASK_IMIX (1 << MSND_MIXER_IMIX) |
46 | #define MSND_MASK_RECLEV (1 << MSND_MIXER_RECLEV) |
47 | #define MSND_MASK_IGAIN (1 << MSND_MIXER_IGAIN) |
48 | #define MSND_MASK_OGAIN (1 << MSND_MIXER_OGAIN) |
49 | #define MSND_MASK_AUX (1 << MSND_MIXER_AUX) |
50 | #define MSND_MASK_DIGITAL (1 << MSND_MIXER_DIGITAL) |
51 | |
52 | static int snd_msndmix_info_mux(struct snd_kcontrol *kcontrol, |
53 | struct snd_ctl_elem_info *uinfo) |
54 | { |
55 | static const char * const texts[3] = { |
56 | "Analog" , "MASS" , "SPDIF" , |
57 | }; |
58 | struct snd_msnd *chip = snd_kcontrol_chip(kcontrol); |
59 | unsigned items = test_bit(F_HAVEDIGITAL, &chip->flags) ? 3 : 2; |
60 | |
61 | return snd_ctl_enum_info(info: uinfo, channels: 1, items, names: texts); |
62 | } |
63 | |
64 | static int snd_msndmix_get_mux(struct snd_kcontrol *kcontrol, |
65 | struct snd_ctl_elem_value *ucontrol) |
66 | { |
67 | struct snd_msnd *chip = snd_kcontrol_chip(kcontrol); |
68 | /* MSND_MASK_IMIX is the default */ |
69 | ucontrol->value.enumerated.item[0] = 0; |
70 | |
71 | if (chip->recsrc & MSND_MASK_SYNTH) { |
72 | ucontrol->value.enumerated.item[0] = 1; |
73 | } else if ((chip->recsrc & MSND_MASK_DIGITAL) && |
74 | test_bit(F_HAVEDIGITAL, &chip->flags)) { |
75 | ucontrol->value.enumerated.item[0] = 2; |
76 | } |
77 | |
78 | |
79 | return 0; |
80 | } |
81 | |
82 | static int snd_msndmix_set_mux(struct snd_msnd *chip, int val) |
83 | { |
84 | unsigned newrecsrc; |
85 | int change; |
86 | unsigned char msndbyte; |
87 | |
88 | switch (val) { |
89 | case 0: |
90 | newrecsrc = MSND_MASK_IMIX; |
91 | msndbyte = HDEXAR_SET_ANA_IN; |
92 | break; |
93 | case 1: |
94 | newrecsrc = MSND_MASK_SYNTH; |
95 | msndbyte = HDEXAR_SET_SYNTH_IN; |
96 | break; |
97 | case 2: |
98 | newrecsrc = MSND_MASK_DIGITAL; |
99 | msndbyte = HDEXAR_SET_DAT_IN; |
100 | break; |
101 | default: |
102 | return -EINVAL; |
103 | } |
104 | change = newrecsrc != chip->recsrc; |
105 | if (change) { |
106 | change = 0; |
107 | if (!snd_msnd_send_word(chip, high: 0, mid: 0, low: msndbyte)) |
108 | if (!snd_msnd_send_dsp_cmd(chip, HDEX_AUX_REQ)) { |
109 | chip->recsrc = newrecsrc; |
110 | change = 1; |
111 | } |
112 | } |
113 | return change; |
114 | } |
115 | |
116 | static int snd_msndmix_put_mux(struct snd_kcontrol *kcontrol, |
117 | struct snd_ctl_elem_value *ucontrol) |
118 | { |
119 | struct snd_msnd *msnd = snd_kcontrol_chip(kcontrol); |
120 | return snd_msndmix_set_mux(chip: msnd, val: ucontrol->value.enumerated.item[0]); |
121 | } |
122 | |
123 | |
124 | static int snd_msndmix_volume_info(struct snd_kcontrol *kcontrol, |
125 | struct snd_ctl_elem_info *uinfo) |
126 | { |
127 | uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; |
128 | uinfo->count = 2; |
129 | uinfo->value.integer.min = 0; |
130 | uinfo->value.integer.max = 100; |
131 | return 0; |
132 | } |
133 | |
134 | static int snd_msndmix_volume_get(struct snd_kcontrol *kcontrol, |
135 | struct snd_ctl_elem_value *ucontrol) |
136 | { |
137 | struct snd_msnd *msnd = snd_kcontrol_chip(kcontrol); |
138 | int addr = kcontrol->private_value; |
139 | unsigned long flags; |
140 | |
141 | spin_lock_irqsave(&msnd->mixer_lock, flags); |
142 | ucontrol->value.integer.value[0] = msnd->left_levels[addr] * 100; |
143 | ucontrol->value.integer.value[0] /= 0xFFFF; |
144 | ucontrol->value.integer.value[1] = msnd->right_levels[addr] * 100; |
145 | ucontrol->value.integer.value[1] /= 0xFFFF; |
146 | spin_unlock_irqrestore(lock: &msnd->mixer_lock, flags); |
147 | return 0; |
148 | } |
149 | |
150 | #define update_volm(a, b) \ |
151 | do { \ |
152 | writew((dev->left_levels[a] >> 1) * \ |
153 | readw(dev->SMA + SMA_wCurrMastVolLeft) / 0xffff, \ |
154 | dev->SMA + SMA_##b##Left); \ |
155 | writew((dev->right_levels[a] >> 1) * \ |
156 | readw(dev->SMA + SMA_wCurrMastVolRight) / 0xffff, \ |
157 | dev->SMA + SMA_##b##Right); \ |
158 | } while (0); |
159 | |
160 | #define update_potm(d, s, ar) \ |
161 | do { \ |
162 | writeb((dev->left_levels[d] >> 8) * \ |
163 | readw(dev->SMA + SMA_wCurrMastVolLeft) / 0xffff, \ |
164 | dev->SMA + SMA_##s##Left); \ |
165 | writeb((dev->right_levels[d] >> 8) * \ |
166 | readw(dev->SMA + SMA_wCurrMastVolRight) / 0xffff, \ |
167 | dev->SMA + SMA_##s##Right); \ |
168 | if (snd_msnd_send_word(dev, 0, 0, ar) == 0) \ |
169 | snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ); \ |
170 | } while (0); |
171 | |
172 | #define update_pot(d, s, ar) \ |
173 | do { \ |
174 | writeb(dev->left_levels[d] >> 8, \ |
175 | dev->SMA + SMA_##s##Left); \ |
176 | writeb(dev->right_levels[d] >> 8, \ |
177 | dev->SMA + SMA_##s##Right); \ |
178 | if (snd_msnd_send_word(dev, 0, 0, ar) == 0) \ |
179 | snd_msnd_send_dsp_cmd(dev, HDEX_AUX_REQ); \ |
180 | } while (0); |
181 | |
182 | |
183 | static int snd_msndmix_set(struct snd_msnd *dev, int d, int left, int right) |
184 | { |
185 | int bLeft, bRight; |
186 | int wLeft, wRight; |
187 | int updatemaster = 0; |
188 | |
189 | if (d >= LEVEL_ENTRIES) |
190 | return -EINVAL; |
191 | |
192 | bLeft = left * 0xff / 100; |
193 | wLeft = left * 0xffff / 100; |
194 | |
195 | bRight = right * 0xff / 100; |
196 | wRight = right * 0xffff / 100; |
197 | |
198 | dev->left_levels[d] = wLeft; |
199 | dev->right_levels[d] = wRight; |
200 | |
201 | switch (d) { |
202 | /* master volume unscaled controls */ |
203 | case MSND_MIXER_LINE: /* line pot control */ |
204 | /* scaled by IMIX in digital mix */ |
205 | writeb(val: bLeft, addr: dev->SMA + SMA_bInPotPosLeft); |
206 | writeb(val: bRight, addr: dev->SMA + SMA_bInPotPosRight); |
207 | if (snd_msnd_send_word(chip: dev, high: 0, mid: 0, HDEXAR_IN_SET_POTS) == 0) |
208 | snd_msnd_send_dsp_cmd(chip: dev, HDEX_AUX_REQ); |
209 | break; |
210 | case MSND_MIXER_MIC: /* mic pot control */ |
211 | if (dev->type == msndClassic) |
212 | return -EINVAL; |
213 | /* scaled by IMIX in digital mix */ |
214 | writeb(val: bLeft, addr: dev->SMA + SMA_bMicPotPosLeft); |
215 | writeb(val: bRight, addr: dev->SMA + SMA_bMicPotPosRight); |
216 | if (snd_msnd_send_word(chip: dev, high: 0, mid: 0, HDEXAR_MIC_SET_POTS) == 0) |
217 | snd_msnd_send_dsp_cmd(chip: dev, HDEX_AUX_REQ); |
218 | break; |
219 | case MSND_MIXER_VOLUME: /* master volume */ |
220 | writew(val: wLeft, addr: dev->SMA + SMA_wCurrMastVolLeft); |
221 | writew(val: wRight, addr: dev->SMA + SMA_wCurrMastVolRight); |
222 | fallthrough; |
223 | case MSND_MIXER_AUX: /* aux pot control */ |
224 | /* scaled by master volume */ |
225 | |
226 | /* digital controls */ |
227 | case MSND_MIXER_SYNTH: /* synth vol (dsp mix) */ |
228 | case MSND_MIXER_PCM: /* pcm vol (dsp mix) */ |
229 | case MSND_MIXER_IMIX: /* input monitor (dsp mix) */ |
230 | /* scaled by master volume */ |
231 | updatemaster = 1; |
232 | break; |
233 | |
234 | default: |
235 | return -EINVAL; |
236 | } |
237 | |
238 | if (updatemaster) { |
239 | /* update master volume scaled controls */ |
240 | update_volm(MSND_MIXER_PCM, wCurrPlayVol); |
241 | update_volm(MSND_MIXER_IMIX, wCurrInVol); |
242 | if (dev->type == msndPinnacle) |
243 | update_volm(MSND_MIXER_SYNTH, wCurrMHdrVol); |
244 | update_potm(MSND_MIXER_AUX, bAuxPotPos, HDEXAR_AUX_SET_POTS); |
245 | } |
246 | |
247 | return 0; |
248 | } |
249 | |
250 | static int snd_msndmix_volume_put(struct snd_kcontrol *kcontrol, |
251 | struct snd_ctl_elem_value *ucontrol) |
252 | { |
253 | struct snd_msnd *msnd = snd_kcontrol_chip(kcontrol); |
254 | int change, addr = kcontrol->private_value; |
255 | int left, right; |
256 | unsigned long flags; |
257 | |
258 | left = ucontrol->value.integer.value[0] % 101; |
259 | right = ucontrol->value.integer.value[1] % 101; |
260 | spin_lock_irqsave(&msnd->mixer_lock, flags); |
261 | change = msnd->left_levels[addr] != left |
262 | || msnd->right_levels[addr] != right; |
263 | snd_msndmix_set(dev: msnd, d: addr, left, right); |
264 | spin_unlock_irqrestore(lock: &msnd->mixer_lock, flags); |
265 | return change; |
266 | } |
267 | |
268 | |
269 | #define DUMMY_VOLUME(xname, xindex, addr) \ |
270 | { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ |
271 | .info = snd_msndmix_volume_info, \ |
272 | .get = snd_msndmix_volume_get, .put = snd_msndmix_volume_put, \ |
273 | .private_value = addr } |
274 | |
275 | |
276 | static const struct snd_kcontrol_new snd_msnd_controls[] = { |
277 | DUMMY_VOLUME("Master Volume" , 0, MSND_MIXER_VOLUME), |
278 | DUMMY_VOLUME("PCM Volume" , 0, MSND_MIXER_PCM), |
279 | DUMMY_VOLUME("Aux Volume" , 0, MSND_MIXER_AUX), |
280 | DUMMY_VOLUME("Line Volume" , 0, MSND_MIXER_LINE), |
281 | DUMMY_VOLUME("Mic Volume" , 0, MSND_MIXER_MIC), |
282 | DUMMY_VOLUME("Monitor" , 0, MSND_MIXER_IMIX), |
283 | { |
284 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
285 | .name = "Capture Source" , |
286 | .info = snd_msndmix_info_mux, |
287 | .get = snd_msndmix_get_mux, |
288 | .put = snd_msndmix_put_mux, |
289 | } |
290 | }; |
291 | |
292 | |
293 | int snd_msndmix_new(struct snd_card *card) |
294 | { |
295 | struct snd_msnd *chip = card->private_data; |
296 | unsigned int idx; |
297 | int err; |
298 | |
299 | if (snd_BUG_ON(!chip)) |
300 | return -EINVAL; |
301 | spin_lock_init(&chip->mixer_lock); |
302 | strcpy(p: card->mixername, q: "MSND Pinnacle Mixer" ); |
303 | |
304 | for (idx = 0; idx < ARRAY_SIZE(snd_msnd_controls); idx++) { |
305 | err = snd_ctl_add(card, |
306 | kcontrol: snd_ctl_new1(kcontrolnew: snd_msnd_controls + idx, private_data: chip)); |
307 | if (err < 0) |
308 | return err; |
309 | } |
310 | |
311 | return 0; |
312 | } |
313 | EXPORT_SYMBOL(snd_msndmix_new); |
314 | |
315 | void snd_msndmix_setup(struct snd_msnd *dev) |
316 | { |
317 | update_pot(MSND_MIXER_LINE, bInPotPos, HDEXAR_IN_SET_POTS); |
318 | update_potm(MSND_MIXER_AUX, bAuxPotPos, HDEXAR_AUX_SET_POTS); |
319 | update_volm(MSND_MIXER_PCM, wCurrPlayVol); |
320 | update_volm(MSND_MIXER_IMIX, wCurrInVol); |
321 | if (dev->type == msndPinnacle) { |
322 | update_pot(MSND_MIXER_MIC, bMicPotPos, HDEXAR_MIC_SET_POTS); |
323 | update_volm(MSND_MIXER_SYNTH, wCurrMHdrVol); |
324 | } |
325 | } |
326 | EXPORT_SYMBOL(snd_msndmix_setup); |
327 | |
328 | int snd_msndmix_force_recsrc(struct snd_msnd *dev, int recsrc) |
329 | { |
330 | dev->recsrc = -1; |
331 | return snd_msndmix_set_mux(chip: dev, val: recsrc); |
332 | } |
333 | EXPORT_SYMBOL(snd_msndmix_force_recsrc); |
334 | |