1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (c) by Jaroslav Kysela <perex@perex.cz> |
4 | * Routines for Sound Blaster mixer control |
5 | */ |
6 | |
7 | #include <linux/io.h> |
8 | #include <linux/delay.h> |
9 | #include <linux/time.h> |
10 | #include <sound/core.h> |
11 | #include <sound/sb.h> |
12 | #include <sound/control.h> |
13 | |
14 | #undef IO_DEBUG |
15 | |
16 | void snd_sbmixer_write(struct snd_sb *chip, unsigned char reg, unsigned char data) |
17 | { |
18 | outb(value: reg, SBP(chip, MIXER_ADDR)); |
19 | udelay(10); |
20 | outb(value: data, SBP(chip, MIXER_DATA)); |
21 | udelay(10); |
22 | #ifdef IO_DEBUG |
23 | snd_printk(KERN_DEBUG "mixer_write 0x%x 0x%x\n" , reg, data); |
24 | #endif |
25 | } |
26 | |
27 | unsigned char snd_sbmixer_read(struct snd_sb *chip, unsigned char reg) |
28 | { |
29 | unsigned char result; |
30 | |
31 | outb(value: reg, SBP(chip, MIXER_ADDR)); |
32 | udelay(10); |
33 | result = inb(SBP(chip, MIXER_DATA)); |
34 | udelay(10); |
35 | #ifdef IO_DEBUG |
36 | snd_printk(KERN_DEBUG "mixer_read 0x%x 0x%x\n" , reg, result); |
37 | #endif |
38 | return result; |
39 | } |
40 | |
41 | /* |
42 | * Single channel mixer element |
43 | */ |
44 | |
45 | static int snd_sbmixer_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) |
46 | { |
47 | int mask = (kcontrol->private_value >> 24) & 0xff; |
48 | |
49 | uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; |
50 | uinfo->count = 1; |
51 | uinfo->value.integer.min = 0; |
52 | uinfo->value.integer.max = mask; |
53 | return 0; |
54 | } |
55 | |
56 | static int snd_sbmixer_get_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) |
57 | { |
58 | struct snd_sb *sb = snd_kcontrol_chip(kcontrol); |
59 | unsigned long flags; |
60 | int reg = kcontrol->private_value & 0xff; |
61 | int shift = (kcontrol->private_value >> 16) & 0xff; |
62 | int mask = (kcontrol->private_value >> 24) & 0xff; |
63 | unsigned char val; |
64 | |
65 | spin_lock_irqsave(&sb->mixer_lock, flags); |
66 | val = (snd_sbmixer_read(chip: sb, reg) >> shift) & mask; |
67 | spin_unlock_irqrestore(lock: &sb->mixer_lock, flags); |
68 | ucontrol->value.integer.value[0] = val; |
69 | return 0; |
70 | } |
71 | |
72 | static int snd_sbmixer_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) |
73 | { |
74 | struct snd_sb *sb = snd_kcontrol_chip(kcontrol); |
75 | unsigned long flags; |
76 | int reg = kcontrol->private_value & 0xff; |
77 | int shift = (kcontrol->private_value >> 16) & 0x07; |
78 | int mask = (kcontrol->private_value >> 24) & 0xff; |
79 | int change; |
80 | unsigned char val, oval; |
81 | |
82 | val = (ucontrol->value.integer.value[0] & mask) << shift; |
83 | spin_lock_irqsave(&sb->mixer_lock, flags); |
84 | oval = snd_sbmixer_read(chip: sb, reg); |
85 | val = (oval & ~(mask << shift)) | val; |
86 | change = val != oval; |
87 | if (change) |
88 | snd_sbmixer_write(chip: sb, reg, data: val); |
89 | spin_unlock_irqrestore(lock: &sb->mixer_lock, flags); |
90 | return change; |
91 | } |
92 | |
93 | /* |
94 | * Double channel mixer element |
95 | */ |
96 | |
97 | static int snd_sbmixer_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) |
98 | { |
99 | int mask = (kcontrol->private_value >> 24) & 0xff; |
100 | |
101 | uinfo->type = mask == 1 ? SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; |
102 | uinfo->count = 2; |
103 | uinfo->value.integer.min = 0; |
104 | uinfo->value.integer.max = mask; |
105 | return 0; |
106 | } |
107 | |
108 | static int snd_sbmixer_get_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) |
109 | { |
110 | struct snd_sb *sb = snd_kcontrol_chip(kcontrol); |
111 | unsigned long flags; |
112 | int left_reg = kcontrol->private_value & 0xff; |
113 | int right_reg = (kcontrol->private_value >> 8) & 0xff; |
114 | int left_shift = (kcontrol->private_value >> 16) & 0x07; |
115 | int right_shift = (kcontrol->private_value >> 19) & 0x07; |
116 | int mask = (kcontrol->private_value >> 24) & 0xff; |
117 | unsigned char left, right; |
118 | |
119 | spin_lock_irqsave(&sb->mixer_lock, flags); |
120 | left = (snd_sbmixer_read(chip: sb, reg: left_reg) >> left_shift) & mask; |
121 | right = (snd_sbmixer_read(chip: sb, reg: right_reg) >> right_shift) & mask; |
122 | spin_unlock_irqrestore(lock: &sb->mixer_lock, flags); |
123 | ucontrol->value.integer.value[0] = left; |
124 | ucontrol->value.integer.value[1] = right; |
125 | return 0; |
126 | } |
127 | |
128 | static int snd_sbmixer_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) |
129 | { |
130 | struct snd_sb *sb = snd_kcontrol_chip(kcontrol); |
131 | unsigned long flags; |
132 | int left_reg = kcontrol->private_value & 0xff; |
133 | int right_reg = (kcontrol->private_value >> 8) & 0xff; |
134 | int left_shift = (kcontrol->private_value >> 16) & 0x07; |
135 | int right_shift = (kcontrol->private_value >> 19) & 0x07; |
136 | int mask = (kcontrol->private_value >> 24) & 0xff; |
137 | int change; |
138 | unsigned char left, right, oleft, oright; |
139 | |
140 | left = (ucontrol->value.integer.value[0] & mask) << left_shift; |
141 | right = (ucontrol->value.integer.value[1] & mask) << right_shift; |
142 | spin_lock_irqsave(&sb->mixer_lock, flags); |
143 | if (left_reg == right_reg) { |
144 | oleft = snd_sbmixer_read(chip: sb, reg: left_reg); |
145 | left = (oleft & ~((mask << left_shift) | (mask << right_shift))) | left | right; |
146 | change = left != oleft; |
147 | if (change) |
148 | snd_sbmixer_write(chip: sb, reg: left_reg, data: left); |
149 | } else { |
150 | oleft = snd_sbmixer_read(chip: sb, reg: left_reg); |
151 | oright = snd_sbmixer_read(chip: sb, reg: right_reg); |
152 | left = (oleft & ~(mask << left_shift)) | left; |
153 | right = (oright & ~(mask << right_shift)) | right; |
154 | change = left != oleft || right != oright; |
155 | if (change) { |
156 | snd_sbmixer_write(chip: sb, reg: left_reg, data: left); |
157 | snd_sbmixer_write(chip: sb, reg: right_reg, data: right); |
158 | } |
159 | } |
160 | spin_unlock_irqrestore(lock: &sb->mixer_lock, flags); |
161 | return change; |
162 | } |
163 | |
164 | /* |
165 | * DT-019x / ALS-007 capture/input switch |
166 | */ |
167 | |
168 | static int snd_dt019x_input_sw_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) |
169 | { |
170 | static const char * const texts[5] = { |
171 | "CD" , "Mic" , "Line" , "Synth" , "Master" |
172 | }; |
173 | |
174 | return snd_ctl_enum_info(info: uinfo, channels: 1, items: 5, names: texts); |
175 | } |
176 | |
177 | static int snd_dt019x_input_sw_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) |
178 | { |
179 | struct snd_sb *sb = snd_kcontrol_chip(kcontrol); |
180 | unsigned long flags; |
181 | unsigned char oval; |
182 | |
183 | spin_lock_irqsave(&sb->mixer_lock, flags); |
184 | oval = snd_sbmixer_read(chip: sb, SB_DT019X_CAPTURE_SW); |
185 | spin_unlock_irqrestore(lock: &sb->mixer_lock, flags); |
186 | switch (oval & 0x07) { |
187 | case SB_DT019X_CAP_CD: |
188 | ucontrol->value.enumerated.item[0] = 0; |
189 | break; |
190 | case SB_DT019X_CAP_MIC: |
191 | ucontrol->value.enumerated.item[0] = 1; |
192 | break; |
193 | case SB_DT019X_CAP_LINE: |
194 | ucontrol->value.enumerated.item[0] = 2; |
195 | break; |
196 | case SB_DT019X_CAP_MAIN: |
197 | ucontrol->value.enumerated.item[0] = 4; |
198 | break; |
199 | /* To record the synth on these cards you must record the main. */ |
200 | /* Thus SB_DT019X_CAP_SYNTH == SB_DT019X_CAP_MAIN and would cause */ |
201 | /* duplicate case labels if left uncommented. */ |
202 | /* case SB_DT019X_CAP_SYNTH: |
203 | * ucontrol->value.enumerated.item[0] = 3; |
204 | * break; |
205 | */ |
206 | default: |
207 | ucontrol->value.enumerated.item[0] = 4; |
208 | break; |
209 | } |
210 | return 0; |
211 | } |
212 | |
213 | static int snd_dt019x_input_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) |
214 | { |
215 | struct snd_sb *sb = snd_kcontrol_chip(kcontrol); |
216 | unsigned long flags; |
217 | int change; |
218 | unsigned char nval, oval; |
219 | |
220 | if (ucontrol->value.enumerated.item[0] > 4) |
221 | return -EINVAL; |
222 | switch (ucontrol->value.enumerated.item[0]) { |
223 | case 0: |
224 | nval = SB_DT019X_CAP_CD; |
225 | break; |
226 | case 1: |
227 | nval = SB_DT019X_CAP_MIC; |
228 | break; |
229 | case 2: |
230 | nval = SB_DT019X_CAP_LINE; |
231 | break; |
232 | case 3: |
233 | nval = SB_DT019X_CAP_SYNTH; |
234 | break; |
235 | case 4: |
236 | nval = SB_DT019X_CAP_MAIN; |
237 | break; |
238 | default: |
239 | nval = SB_DT019X_CAP_MAIN; |
240 | } |
241 | spin_lock_irqsave(&sb->mixer_lock, flags); |
242 | oval = snd_sbmixer_read(chip: sb, SB_DT019X_CAPTURE_SW); |
243 | change = nval != oval; |
244 | if (change) |
245 | snd_sbmixer_write(chip: sb, SB_DT019X_CAPTURE_SW, data: nval); |
246 | spin_unlock_irqrestore(lock: &sb->mixer_lock, flags); |
247 | return change; |
248 | } |
249 | |
250 | /* |
251 | * ALS4000 mono recording control switch |
252 | */ |
253 | |
254 | static int snd_als4k_mono_capture_route_info(struct snd_kcontrol *kcontrol, |
255 | struct snd_ctl_elem_info *uinfo) |
256 | { |
257 | static const char * const texts[3] = { |
258 | "L chan only" , "R chan only" , "L ch/2 + R ch/2" |
259 | }; |
260 | |
261 | return snd_ctl_enum_info(info: uinfo, channels: 1, items: 3, names: texts); |
262 | } |
263 | |
264 | static int snd_als4k_mono_capture_route_get(struct snd_kcontrol *kcontrol, |
265 | struct snd_ctl_elem_value *ucontrol) |
266 | { |
267 | struct snd_sb *sb = snd_kcontrol_chip(kcontrol); |
268 | unsigned long flags; |
269 | unsigned char oval; |
270 | |
271 | spin_lock_irqsave(&sb->mixer_lock, flags); |
272 | oval = snd_sbmixer_read(chip: sb, SB_ALS4000_MONO_IO_CTRL); |
273 | spin_unlock_irqrestore(lock: &sb->mixer_lock, flags); |
274 | oval >>= 6; |
275 | if (oval > 2) |
276 | oval = 2; |
277 | |
278 | ucontrol->value.enumerated.item[0] = oval; |
279 | return 0; |
280 | } |
281 | |
282 | static int snd_als4k_mono_capture_route_put(struct snd_kcontrol *kcontrol, |
283 | struct snd_ctl_elem_value *ucontrol) |
284 | { |
285 | struct snd_sb *sb = snd_kcontrol_chip(kcontrol); |
286 | unsigned long flags; |
287 | int change; |
288 | unsigned char nval, oval; |
289 | |
290 | if (ucontrol->value.enumerated.item[0] > 2) |
291 | return -EINVAL; |
292 | spin_lock_irqsave(&sb->mixer_lock, flags); |
293 | oval = snd_sbmixer_read(chip: sb, SB_ALS4000_MONO_IO_CTRL); |
294 | |
295 | nval = (oval & ~(3 << 6)) |
296 | | (ucontrol->value.enumerated.item[0] << 6); |
297 | change = nval != oval; |
298 | if (change) |
299 | snd_sbmixer_write(chip: sb, SB_ALS4000_MONO_IO_CTRL, data: nval); |
300 | spin_unlock_irqrestore(lock: &sb->mixer_lock, flags); |
301 | return change; |
302 | } |
303 | |
304 | /* |
305 | * SBPRO input multiplexer |
306 | */ |
307 | |
308 | static int snd_sb8mixer_info_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) |
309 | { |
310 | static const char * const texts[3] = { |
311 | "Mic" , "CD" , "Line" |
312 | }; |
313 | |
314 | return snd_ctl_enum_info(info: uinfo, channels: 1, items: 3, names: texts); |
315 | } |
316 | |
317 | |
318 | static int snd_sb8mixer_get_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) |
319 | { |
320 | struct snd_sb *sb = snd_kcontrol_chip(kcontrol); |
321 | unsigned long flags; |
322 | unsigned char oval; |
323 | |
324 | spin_lock_irqsave(&sb->mixer_lock, flags); |
325 | oval = snd_sbmixer_read(chip: sb, SB_DSP_CAPTURE_SOURCE); |
326 | spin_unlock_irqrestore(lock: &sb->mixer_lock, flags); |
327 | switch ((oval >> 0x01) & 0x03) { |
328 | case SB_DSP_MIXS_CD: |
329 | ucontrol->value.enumerated.item[0] = 1; |
330 | break; |
331 | case SB_DSP_MIXS_LINE: |
332 | ucontrol->value.enumerated.item[0] = 2; |
333 | break; |
334 | default: |
335 | ucontrol->value.enumerated.item[0] = 0; |
336 | break; |
337 | } |
338 | return 0; |
339 | } |
340 | |
341 | static int snd_sb8mixer_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) |
342 | { |
343 | struct snd_sb *sb = snd_kcontrol_chip(kcontrol); |
344 | unsigned long flags; |
345 | int change; |
346 | unsigned char nval, oval; |
347 | |
348 | if (ucontrol->value.enumerated.item[0] > 2) |
349 | return -EINVAL; |
350 | switch (ucontrol->value.enumerated.item[0]) { |
351 | case 1: |
352 | nval = SB_DSP_MIXS_CD; |
353 | break; |
354 | case 2: |
355 | nval = SB_DSP_MIXS_LINE; |
356 | break; |
357 | default: |
358 | nval = SB_DSP_MIXS_MIC; |
359 | } |
360 | nval <<= 1; |
361 | spin_lock_irqsave(&sb->mixer_lock, flags); |
362 | oval = snd_sbmixer_read(chip: sb, SB_DSP_CAPTURE_SOURCE); |
363 | nval |= oval & ~0x06; |
364 | change = nval != oval; |
365 | if (change) |
366 | snd_sbmixer_write(chip: sb, SB_DSP_CAPTURE_SOURCE, data: nval); |
367 | spin_unlock_irqrestore(lock: &sb->mixer_lock, flags); |
368 | return change; |
369 | } |
370 | |
371 | /* |
372 | * SB16 input switch |
373 | */ |
374 | |
375 | static int snd_sb16mixer_info_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) |
376 | { |
377 | uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; |
378 | uinfo->count = 4; |
379 | uinfo->value.integer.min = 0; |
380 | uinfo->value.integer.max = 1; |
381 | return 0; |
382 | } |
383 | |
384 | static int snd_sb16mixer_get_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) |
385 | { |
386 | struct snd_sb *sb = snd_kcontrol_chip(kcontrol); |
387 | unsigned long flags; |
388 | int reg1 = kcontrol->private_value & 0xff; |
389 | int reg2 = (kcontrol->private_value >> 8) & 0xff; |
390 | int left_shift = (kcontrol->private_value >> 16) & 0x0f; |
391 | int right_shift = (kcontrol->private_value >> 24) & 0x0f; |
392 | unsigned char val1, val2; |
393 | |
394 | spin_lock_irqsave(&sb->mixer_lock, flags); |
395 | val1 = snd_sbmixer_read(chip: sb, reg: reg1); |
396 | val2 = snd_sbmixer_read(chip: sb, reg: reg2); |
397 | spin_unlock_irqrestore(lock: &sb->mixer_lock, flags); |
398 | ucontrol->value.integer.value[0] = (val1 >> left_shift) & 0x01; |
399 | ucontrol->value.integer.value[1] = (val2 >> left_shift) & 0x01; |
400 | ucontrol->value.integer.value[2] = (val1 >> right_shift) & 0x01; |
401 | ucontrol->value.integer.value[3] = (val2 >> right_shift) & 0x01; |
402 | return 0; |
403 | } |
404 | |
405 | static int snd_sb16mixer_put_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) |
406 | { |
407 | struct snd_sb *sb = snd_kcontrol_chip(kcontrol); |
408 | unsigned long flags; |
409 | int reg1 = kcontrol->private_value & 0xff; |
410 | int reg2 = (kcontrol->private_value >> 8) & 0xff; |
411 | int left_shift = (kcontrol->private_value >> 16) & 0x0f; |
412 | int right_shift = (kcontrol->private_value >> 24) & 0x0f; |
413 | int change; |
414 | unsigned char val1, val2, oval1, oval2; |
415 | |
416 | spin_lock_irqsave(&sb->mixer_lock, flags); |
417 | oval1 = snd_sbmixer_read(chip: sb, reg: reg1); |
418 | oval2 = snd_sbmixer_read(chip: sb, reg: reg2); |
419 | val1 = oval1 & ~((1 << left_shift) | (1 << right_shift)); |
420 | val2 = oval2 & ~((1 << left_shift) | (1 << right_shift)); |
421 | val1 |= (ucontrol->value.integer.value[0] & 1) << left_shift; |
422 | val2 |= (ucontrol->value.integer.value[1] & 1) << left_shift; |
423 | val1 |= (ucontrol->value.integer.value[2] & 1) << right_shift; |
424 | val2 |= (ucontrol->value.integer.value[3] & 1) << right_shift; |
425 | change = val1 != oval1 || val2 != oval2; |
426 | if (change) { |
427 | snd_sbmixer_write(chip: sb, reg: reg1, data: val1); |
428 | snd_sbmixer_write(chip: sb, reg: reg2, data: val2); |
429 | } |
430 | spin_unlock_irqrestore(lock: &sb->mixer_lock, flags); |
431 | return change; |
432 | } |
433 | |
434 | |
435 | /* |
436 | */ |
437 | /* |
438 | */ |
439 | int snd_sbmixer_add_ctl(struct snd_sb *chip, const char *name, int index, int type, unsigned long value) |
440 | { |
441 | static const struct snd_kcontrol_new newctls[] = { |
442 | [SB_MIX_SINGLE] = { |
443 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
444 | .info = snd_sbmixer_info_single, |
445 | .get = snd_sbmixer_get_single, |
446 | .put = snd_sbmixer_put_single, |
447 | }, |
448 | [SB_MIX_DOUBLE] = { |
449 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
450 | .info = snd_sbmixer_info_double, |
451 | .get = snd_sbmixer_get_double, |
452 | .put = snd_sbmixer_put_double, |
453 | }, |
454 | [SB_MIX_INPUT_SW] = { |
455 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
456 | .info = snd_sb16mixer_info_input_sw, |
457 | .get = snd_sb16mixer_get_input_sw, |
458 | .put = snd_sb16mixer_put_input_sw, |
459 | }, |
460 | [SB_MIX_CAPTURE_PRO] = { |
461 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
462 | .info = snd_sb8mixer_info_mux, |
463 | .get = snd_sb8mixer_get_mux, |
464 | .put = snd_sb8mixer_put_mux, |
465 | }, |
466 | [SB_MIX_CAPTURE_DT019X] = { |
467 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
468 | .info = snd_dt019x_input_sw_info, |
469 | .get = snd_dt019x_input_sw_get, |
470 | .put = snd_dt019x_input_sw_put, |
471 | }, |
472 | [SB_MIX_MONO_CAPTURE_ALS4K] = { |
473 | .iface = SNDRV_CTL_ELEM_IFACE_MIXER, |
474 | .info = snd_als4k_mono_capture_route_info, |
475 | .get = snd_als4k_mono_capture_route_get, |
476 | .put = snd_als4k_mono_capture_route_put, |
477 | }, |
478 | }; |
479 | struct snd_kcontrol *ctl; |
480 | int err; |
481 | |
482 | ctl = snd_ctl_new1(kcontrolnew: &newctls[type], private_data: chip); |
483 | if (! ctl) |
484 | return -ENOMEM; |
485 | strscpy(ctl->id.name, name, sizeof(ctl->id.name)); |
486 | ctl->id.index = index; |
487 | ctl->private_value = value; |
488 | err = snd_ctl_add(card: chip->card, kcontrol: ctl); |
489 | if (err < 0) |
490 | return err; |
491 | return 0; |
492 | } |
493 | |
494 | /* |
495 | * SB 2.0 specific mixer elements |
496 | */ |
497 | |
498 | static const struct sbmix_elem snd_sb20_controls[] = { |
499 | SB_SINGLE("Master Playback Volume" , SB_DSP20_MASTER_DEV, 1, 7), |
500 | SB_SINGLE("PCM Playback Volume" , SB_DSP20_PCM_DEV, 1, 3), |
501 | SB_SINGLE("Synth Playback Volume" , SB_DSP20_FM_DEV, 1, 7), |
502 | SB_SINGLE("CD Playback Volume" , SB_DSP20_CD_DEV, 1, 7) |
503 | }; |
504 | |
505 | static const unsigned char snd_sb20_init_values[][2] = { |
506 | { SB_DSP20_MASTER_DEV, 0 }, |
507 | { SB_DSP20_FM_DEV, 0 }, |
508 | }; |
509 | |
510 | /* |
511 | * SB Pro specific mixer elements |
512 | */ |
513 | static const struct sbmix_elem snd_sbpro_controls[] = { |
514 | SB_DOUBLE("Master Playback Volume" , |
515 | SB_DSP_MASTER_DEV, SB_DSP_MASTER_DEV, 5, 1, 7), |
516 | SB_DOUBLE("PCM Playback Volume" , |
517 | SB_DSP_PCM_DEV, SB_DSP_PCM_DEV, 5, 1, 7), |
518 | SB_SINGLE("PCM Playback Filter" , SB_DSP_PLAYBACK_FILT, 5, 1), |
519 | SB_DOUBLE("Synth Playback Volume" , |
520 | SB_DSP_FM_DEV, SB_DSP_FM_DEV, 5, 1, 7), |
521 | SB_DOUBLE("CD Playback Volume" , SB_DSP_CD_DEV, SB_DSP_CD_DEV, 5, 1, 7), |
522 | SB_DOUBLE("Line Playback Volume" , |
523 | SB_DSP_LINE_DEV, SB_DSP_LINE_DEV, 5, 1, 7), |
524 | SB_SINGLE("Mic Playback Volume" , SB_DSP_MIC_DEV, 1, 3), |
525 | { |
526 | .name = "Capture Source" , |
527 | .type = SB_MIX_CAPTURE_PRO |
528 | }, |
529 | SB_SINGLE("Capture Filter" , SB_DSP_CAPTURE_FILT, 5, 1), |
530 | SB_SINGLE("Capture Low-Pass Filter" , SB_DSP_CAPTURE_FILT, 3, 1) |
531 | }; |
532 | |
533 | static const unsigned char snd_sbpro_init_values[][2] = { |
534 | { SB_DSP_MASTER_DEV, 0 }, |
535 | { SB_DSP_PCM_DEV, 0 }, |
536 | { SB_DSP_FM_DEV, 0 }, |
537 | }; |
538 | |
539 | /* |
540 | * SB16 specific mixer elements |
541 | */ |
542 | static const struct sbmix_elem snd_sb16_controls[] = { |
543 | SB_DOUBLE("Master Playback Volume" , |
544 | SB_DSP4_MASTER_DEV, (SB_DSP4_MASTER_DEV + 1), 3, 3, 31), |
545 | SB_DOUBLE("PCM Playback Volume" , |
546 | SB_DSP4_PCM_DEV, (SB_DSP4_PCM_DEV + 1), 3, 3, 31), |
547 | SB16_INPUT_SW("Synth Capture Route" , |
548 | SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 6, 5), |
549 | SB_DOUBLE("Synth Playback Volume" , |
550 | SB_DSP4_SYNTH_DEV, (SB_DSP4_SYNTH_DEV + 1), 3, 3, 31), |
551 | SB16_INPUT_SW("CD Capture Route" , |
552 | SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 2, 1), |
553 | SB_DOUBLE("CD Playback Switch" , |
554 | SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 2, 1, 1), |
555 | SB_DOUBLE("CD Playback Volume" , |
556 | SB_DSP4_CD_DEV, (SB_DSP4_CD_DEV + 1), 3, 3, 31), |
557 | SB16_INPUT_SW("Mic Capture Route" , |
558 | SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 0, 0), |
559 | SB_SINGLE("Mic Playback Switch" , SB_DSP4_OUTPUT_SW, 0, 1), |
560 | SB_SINGLE("Mic Playback Volume" , SB_DSP4_MIC_DEV, 3, 31), |
561 | SB_SINGLE("Beep Volume" , SB_DSP4_SPEAKER_DEV, 6, 3), |
562 | SB_DOUBLE("Capture Volume" , |
563 | SB_DSP4_IGAIN_DEV, (SB_DSP4_IGAIN_DEV + 1), 6, 6, 3), |
564 | SB_DOUBLE("Playback Volume" , |
565 | SB_DSP4_OGAIN_DEV, (SB_DSP4_OGAIN_DEV + 1), 6, 6, 3), |
566 | SB16_INPUT_SW("Line Capture Route" , |
567 | SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 4, 3), |
568 | SB_DOUBLE("Line Playback Switch" , |
569 | SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 4, 3, 1), |
570 | SB_DOUBLE("Line Playback Volume" , |
571 | SB_DSP4_LINE_DEV, (SB_DSP4_LINE_DEV + 1), 3, 3, 31), |
572 | SB_SINGLE("Mic Auto Gain" , SB_DSP4_MIC_AGC, 0, 1), |
573 | SB_SINGLE("3D Enhancement Switch" , SB_DSP4_3DSE, 0, 1), |
574 | SB_DOUBLE("Tone Control - Bass" , |
575 | SB_DSP4_BASS_DEV, (SB_DSP4_BASS_DEV + 1), 4, 4, 15), |
576 | SB_DOUBLE("Tone Control - Treble" , |
577 | SB_DSP4_TREBLE_DEV, (SB_DSP4_TREBLE_DEV + 1), 4, 4, 15) |
578 | }; |
579 | |
580 | static const unsigned char snd_sb16_init_values[][2] = { |
581 | { SB_DSP4_MASTER_DEV + 0, 0 }, |
582 | { SB_DSP4_MASTER_DEV + 1, 0 }, |
583 | { SB_DSP4_PCM_DEV + 0, 0 }, |
584 | { SB_DSP4_PCM_DEV + 1, 0 }, |
585 | { SB_DSP4_SYNTH_DEV + 0, 0 }, |
586 | { SB_DSP4_SYNTH_DEV + 1, 0 }, |
587 | { SB_DSP4_INPUT_LEFT, 0 }, |
588 | { SB_DSP4_INPUT_RIGHT, 0 }, |
589 | { SB_DSP4_OUTPUT_SW, 0 }, |
590 | { SB_DSP4_SPEAKER_DEV, 0 }, |
591 | }; |
592 | |
593 | /* |
594 | * DT019x specific mixer elements |
595 | */ |
596 | static const struct sbmix_elem snd_dt019x_controls[] = { |
597 | /* ALS4000 below has some parts which we might be lacking, |
598 | * e.g. snd_als4000_ctl_mono_playback_switch - check it! */ |
599 | SB_DOUBLE("Master Playback Volume" , |
600 | SB_DT019X_MASTER_DEV, SB_DT019X_MASTER_DEV, 4, 0, 15), |
601 | SB_DOUBLE("PCM Playback Switch" , |
602 | SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 2, 1, 1), |
603 | SB_DOUBLE("PCM Playback Volume" , |
604 | SB_DT019X_PCM_DEV, SB_DT019X_PCM_DEV, 4, 0, 15), |
605 | SB_DOUBLE("Synth Playback Switch" , |
606 | SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 4, 3, 1), |
607 | SB_DOUBLE("Synth Playback Volume" , |
608 | SB_DT019X_SYNTH_DEV, SB_DT019X_SYNTH_DEV, 4, 0, 15), |
609 | SB_DOUBLE("CD Playback Switch" , |
610 | SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 2, 1, 1), |
611 | SB_DOUBLE("CD Playback Volume" , |
612 | SB_DT019X_CD_DEV, SB_DT019X_CD_DEV, 4, 0, 15), |
613 | SB_SINGLE("Mic Playback Switch" , SB_DSP4_OUTPUT_SW, 0, 1), |
614 | SB_SINGLE("Mic Playback Volume" , SB_DT019X_MIC_DEV, 4, 7), |
615 | SB_SINGLE("Beep Volume" , SB_DT019X_SPKR_DEV, 0, 7), |
616 | SB_DOUBLE("Line Playback Switch" , |
617 | SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 4, 3, 1), |
618 | SB_DOUBLE("Line Playback Volume" , |
619 | SB_DT019X_LINE_DEV, SB_DT019X_LINE_DEV, 4, 0, 15), |
620 | { |
621 | .name = "Capture Source" , |
622 | .type = SB_MIX_CAPTURE_DT019X |
623 | } |
624 | }; |
625 | |
626 | static const unsigned char snd_dt019x_init_values[][2] = { |
627 | { SB_DT019X_MASTER_DEV, 0 }, |
628 | { SB_DT019X_PCM_DEV, 0 }, |
629 | { SB_DT019X_SYNTH_DEV, 0 }, |
630 | { SB_DT019X_CD_DEV, 0 }, |
631 | { SB_DT019X_MIC_DEV, 0 }, /* Includes PC-speaker in high nibble */ |
632 | { SB_DT019X_LINE_DEV, 0 }, |
633 | { SB_DSP4_OUTPUT_SW, 0 }, |
634 | { SB_DT019X_OUTPUT_SW2, 0 }, |
635 | { SB_DT019X_CAPTURE_SW, 0x06 }, |
636 | }; |
637 | |
638 | /* |
639 | * ALS4000 specific mixer elements |
640 | */ |
641 | static const struct sbmix_elem snd_als4000_controls[] = { |
642 | SB_DOUBLE("PCM Playback Switch" , |
643 | SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 2, 1, 1), |
644 | SB_DOUBLE("Synth Playback Switch" , |
645 | SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 4, 3, 1), |
646 | SB_SINGLE("Mic Boost (+20dB)" , SB_ALS4000_MIC_IN_GAIN, 0, 0x03), |
647 | SB_SINGLE("Master Mono Playback Switch" , SB_ALS4000_MONO_IO_CTRL, 5, 1), |
648 | { |
649 | .name = "Master Mono Capture Route" , |
650 | .type = SB_MIX_MONO_CAPTURE_ALS4K |
651 | }, |
652 | SB_SINGLE("Mono Playback Switch" , SB_DT019X_OUTPUT_SW2, 0, 1), |
653 | SB_SINGLE("Analog Loopback Switch" , SB_ALS4000_MIC_IN_GAIN, 7, 0x01), |
654 | SB_SINGLE("3D Control - Switch" , SB_ALS4000_3D_SND_FX, 6, 0x01), |
655 | SB_SINGLE("Digital Loopback Switch" , |
656 | SB_ALS4000_CR3_CONFIGURATION, 7, 0x01), |
657 | /* FIXME: functionality of 3D controls might be swapped, I didn't find |
658 | * a description of how to identify what is supposed to be what */ |
659 | SB_SINGLE("3D Control - Level" , SB_ALS4000_3D_SND_FX, 0, 0x07), |
660 | /* FIXME: maybe there's actually some standard 3D ctrl name for it?? */ |
661 | SB_SINGLE("3D Control - Freq" , SB_ALS4000_3D_SND_FX, 4, 0x03), |
662 | /* FIXME: ALS4000a.pdf mentions BBD (Bucket Brigade Device) time delay, |
663 | * but what ALSA 3D attribute is that actually? "Center", "Depth", |
664 | * "Wide" or "Space" or even "Level"? Assuming "Wide" for now... */ |
665 | SB_SINGLE("3D Control - Wide" , SB_ALS4000_3D_TIME_DELAY, 0, 0x0f), |
666 | SB_SINGLE("3D PowerOff Switch" , SB_ALS4000_3D_TIME_DELAY, 4, 0x01), |
667 | SB_SINGLE("Master Playback 8kHz / 20kHz LPF Switch" , |
668 | SB_ALS4000_FMDAC, 5, 0x01), |
669 | #ifdef NOT_AVAILABLE |
670 | SB_SINGLE("FMDAC Switch (Option ?)" , SB_ALS4000_FMDAC, 0, 0x01), |
671 | SB_SINGLE("QSound Mode" , SB_ALS4000_QSOUND, 1, 0x1f), |
672 | #endif |
673 | }; |
674 | |
675 | static const unsigned char snd_als4000_init_values[][2] = { |
676 | { SB_DSP4_MASTER_DEV + 0, 0 }, |
677 | { SB_DSP4_MASTER_DEV + 1, 0 }, |
678 | { SB_DSP4_PCM_DEV + 0, 0 }, |
679 | { SB_DSP4_PCM_DEV + 1, 0 }, |
680 | { SB_DSP4_SYNTH_DEV + 0, 0 }, |
681 | { SB_DSP4_SYNTH_DEV + 1, 0 }, |
682 | { SB_DSP4_SPEAKER_DEV, 0 }, |
683 | { SB_DSP4_OUTPUT_SW, 0 }, |
684 | { SB_DSP4_INPUT_LEFT, 0 }, |
685 | { SB_DSP4_INPUT_RIGHT, 0 }, |
686 | { SB_DT019X_OUTPUT_SW2, 0 }, |
687 | { SB_ALS4000_MIC_IN_GAIN, 0 }, |
688 | }; |
689 | |
690 | /* |
691 | */ |
692 | static int snd_sbmixer_init(struct snd_sb *chip, |
693 | const struct sbmix_elem *controls, |
694 | int controls_count, |
695 | const unsigned char map[][2], |
696 | int map_count, |
697 | char *name) |
698 | { |
699 | unsigned long flags; |
700 | struct snd_card *card = chip->card; |
701 | int idx, err; |
702 | |
703 | /* mixer reset */ |
704 | spin_lock_irqsave(&chip->mixer_lock, flags); |
705 | snd_sbmixer_write(chip, reg: 0x00, data: 0x00); |
706 | spin_unlock_irqrestore(lock: &chip->mixer_lock, flags); |
707 | |
708 | /* mute and zero volume channels */ |
709 | for (idx = 0; idx < map_count; idx++) { |
710 | spin_lock_irqsave(&chip->mixer_lock, flags); |
711 | snd_sbmixer_write(chip, reg: map[idx][0], data: map[idx][1]); |
712 | spin_unlock_irqrestore(lock: &chip->mixer_lock, flags); |
713 | } |
714 | |
715 | for (idx = 0; idx < controls_count; idx++) { |
716 | err = snd_sbmixer_add_ctl_elem(chip, c: &controls[idx]); |
717 | if (err < 0) |
718 | return err; |
719 | } |
720 | snd_component_add(card, component: name); |
721 | strcpy(p: card->mixername, q: name); |
722 | return 0; |
723 | } |
724 | |
725 | int snd_sbmixer_new(struct snd_sb *chip) |
726 | { |
727 | struct snd_card *card; |
728 | int err; |
729 | |
730 | if (snd_BUG_ON(!chip || !chip->card)) |
731 | return -EINVAL; |
732 | |
733 | card = chip->card; |
734 | |
735 | switch (chip->hardware) { |
736 | case SB_HW_10: |
737 | return 0; /* no mixer chip on SB1.x */ |
738 | case SB_HW_20: |
739 | case SB_HW_201: |
740 | err = snd_sbmixer_init(chip, |
741 | controls: snd_sb20_controls, |
742 | ARRAY_SIZE(snd_sb20_controls), |
743 | map: snd_sb20_init_values, |
744 | ARRAY_SIZE(snd_sb20_init_values), |
745 | name: "CTL1335" ); |
746 | if (err < 0) |
747 | return err; |
748 | break; |
749 | case SB_HW_PRO: |
750 | case SB_HW_JAZZ16: |
751 | err = snd_sbmixer_init(chip, |
752 | controls: snd_sbpro_controls, |
753 | ARRAY_SIZE(snd_sbpro_controls), |
754 | map: snd_sbpro_init_values, |
755 | ARRAY_SIZE(snd_sbpro_init_values), |
756 | name: "CTL1345" ); |
757 | if (err < 0) |
758 | return err; |
759 | break; |
760 | case SB_HW_16: |
761 | case SB_HW_ALS100: |
762 | case SB_HW_CS5530: |
763 | err = snd_sbmixer_init(chip, |
764 | controls: snd_sb16_controls, |
765 | ARRAY_SIZE(snd_sb16_controls), |
766 | map: snd_sb16_init_values, |
767 | ARRAY_SIZE(snd_sb16_init_values), |
768 | name: "CTL1745" ); |
769 | if (err < 0) |
770 | return err; |
771 | break; |
772 | case SB_HW_ALS4000: |
773 | /* use only the first 16 controls from SB16 */ |
774 | err = snd_sbmixer_init(chip, |
775 | controls: snd_sb16_controls, |
776 | controls_count: 16, |
777 | map: snd_sb16_init_values, |
778 | ARRAY_SIZE(snd_sb16_init_values), |
779 | name: "ALS4000" ); |
780 | if (err < 0) |
781 | return err; |
782 | err = snd_sbmixer_init(chip, |
783 | controls: snd_als4000_controls, |
784 | ARRAY_SIZE(snd_als4000_controls), |
785 | map: snd_als4000_init_values, |
786 | ARRAY_SIZE(snd_als4000_init_values), |
787 | name: "ALS4000" ); |
788 | if (err < 0) |
789 | return err; |
790 | break; |
791 | case SB_HW_DT019X: |
792 | err = snd_sbmixer_init(chip, |
793 | controls: snd_dt019x_controls, |
794 | ARRAY_SIZE(snd_dt019x_controls), |
795 | map: snd_dt019x_init_values, |
796 | ARRAY_SIZE(snd_dt019x_init_values), |
797 | name: "DT019X" ); |
798 | if (err < 0) |
799 | return err; |
800 | break; |
801 | default: |
802 | strcpy(p: card->mixername, q: "???" ); |
803 | } |
804 | return 0; |
805 | } |
806 | |
807 | #ifdef CONFIG_PM |
808 | static const unsigned char sb20_saved_regs[] = { |
809 | SB_DSP20_MASTER_DEV, |
810 | SB_DSP20_PCM_DEV, |
811 | SB_DSP20_FM_DEV, |
812 | SB_DSP20_CD_DEV, |
813 | }; |
814 | |
815 | static const unsigned char sbpro_saved_regs[] = { |
816 | SB_DSP_MASTER_DEV, |
817 | SB_DSP_PCM_DEV, |
818 | SB_DSP_PLAYBACK_FILT, |
819 | SB_DSP_FM_DEV, |
820 | SB_DSP_CD_DEV, |
821 | SB_DSP_LINE_DEV, |
822 | SB_DSP_MIC_DEV, |
823 | SB_DSP_CAPTURE_SOURCE, |
824 | SB_DSP_CAPTURE_FILT, |
825 | }; |
826 | |
827 | static const unsigned char sb16_saved_regs[] = { |
828 | SB_DSP4_MASTER_DEV, SB_DSP4_MASTER_DEV + 1, |
829 | SB_DSP4_3DSE, |
830 | SB_DSP4_BASS_DEV, SB_DSP4_BASS_DEV + 1, |
831 | SB_DSP4_TREBLE_DEV, SB_DSP4_TREBLE_DEV + 1, |
832 | SB_DSP4_PCM_DEV, SB_DSP4_PCM_DEV + 1, |
833 | SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, |
834 | SB_DSP4_SYNTH_DEV, SB_DSP4_SYNTH_DEV + 1, |
835 | SB_DSP4_OUTPUT_SW, |
836 | SB_DSP4_CD_DEV, SB_DSP4_CD_DEV + 1, |
837 | SB_DSP4_LINE_DEV, SB_DSP4_LINE_DEV + 1, |
838 | SB_DSP4_MIC_DEV, |
839 | SB_DSP4_SPEAKER_DEV, |
840 | SB_DSP4_IGAIN_DEV, SB_DSP4_IGAIN_DEV + 1, |
841 | SB_DSP4_OGAIN_DEV, SB_DSP4_OGAIN_DEV + 1, |
842 | SB_DSP4_MIC_AGC |
843 | }; |
844 | |
845 | static const unsigned char dt019x_saved_regs[] = { |
846 | SB_DT019X_MASTER_DEV, |
847 | SB_DT019X_PCM_DEV, |
848 | SB_DT019X_SYNTH_DEV, |
849 | SB_DT019X_CD_DEV, |
850 | SB_DT019X_MIC_DEV, |
851 | SB_DT019X_SPKR_DEV, |
852 | SB_DT019X_LINE_DEV, |
853 | SB_DSP4_OUTPUT_SW, |
854 | SB_DT019X_OUTPUT_SW2, |
855 | SB_DT019X_CAPTURE_SW, |
856 | }; |
857 | |
858 | static const unsigned char als4000_saved_regs[] = { |
859 | /* please verify in dsheet whether regs to be added |
860 | are actually real H/W or just dummy */ |
861 | SB_DSP4_MASTER_DEV, SB_DSP4_MASTER_DEV + 1, |
862 | SB_DSP4_OUTPUT_SW, |
863 | SB_DSP4_PCM_DEV, SB_DSP4_PCM_DEV + 1, |
864 | SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, |
865 | SB_DSP4_SYNTH_DEV, SB_DSP4_SYNTH_DEV + 1, |
866 | SB_DSP4_CD_DEV, SB_DSP4_CD_DEV + 1, |
867 | SB_DSP4_MIC_DEV, |
868 | SB_DSP4_SPEAKER_DEV, |
869 | SB_DSP4_IGAIN_DEV, SB_DSP4_IGAIN_DEV + 1, |
870 | SB_DSP4_OGAIN_DEV, SB_DSP4_OGAIN_DEV + 1, |
871 | SB_DT019X_OUTPUT_SW2, |
872 | SB_ALS4000_MONO_IO_CTRL, |
873 | SB_ALS4000_MIC_IN_GAIN, |
874 | SB_ALS4000_FMDAC, |
875 | SB_ALS4000_3D_SND_FX, |
876 | SB_ALS4000_3D_TIME_DELAY, |
877 | SB_ALS4000_CR3_CONFIGURATION, |
878 | }; |
879 | |
880 | static void save_mixer(struct snd_sb *chip, const unsigned char *regs, int num_regs) |
881 | { |
882 | unsigned char *val = chip->saved_regs; |
883 | if (snd_BUG_ON(num_regs > ARRAY_SIZE(chip->saved_regs))) |
884 | return; |
885 | for (; num_regs; num_regs--) |
886 | *val++ = snd_sbmixer_read(chip, reg: *regs++); |
887 | } |
888 | |
889 | static void restore_mixer(struct snd_sb *chip, const unsigned char *regs, int num_regs) |
890 | { |
891 | unsigned char *val = chip->saved_regs; |
892 | if (snd_BUG_ON(num_regs > ARRAY_SIZE(chip->saved_regs))) |
893 | return; |
894 | for (; num_regs; num_regs--) |
895 | snd_sbmixer_write(chip, reg: *regs++, data: *val++); |
896 | } |
897 | |
898 | void snd_sbmixer_suspend(struct snd_sb *chip) |
899 | { |
900 | switch (chip->hardware) { |
901 | case SB_HW_20: |
902 | case SB_HW_201: |
903 | save_mixer(chip, regs: sb20_saved_regs, ARRAY_SIZE(sb20_saved_regs)); |
904 | break; |
905 | case SB_HW_PRO: |
906 | case SB_HW_JAZZ16: |
907 | save_mixer(chip, regs: sbpro_saved_regs, ARRAY_SIZE(sbpro_saved_regs)); |
908 | break; |
909 | case SB_HW_16: |
910 | case SB_HW_ALS100: |
911 | case SB_HW_CS5530: |
912 | save_mixer(chip, regs: sb16_saved_regs, ARRAY_SIZE(sb16_saved_regs)); |
913 | break; |
914 | case SB_HW_ALS4000: |
915 | save_mixer(chip, regs: als4000_saved_regs, ARRAY_SIZE(als4000_saved_regs)); |
916 | break; |
917 | case SB_HW_DT019X: |
918 | save_mixer(chip, regs: dt019x_saved_regs, ARRAY_SIZE(dt019x_saved_regs)); |
919 | break; |
920 | default: |
921 | break; |
922 | } |
923 | } |
924 | |
925 | void snd_sbmixer_resume(struct snd_sb *chip) |
926 | { |
927 | switch (chip->hardware) { |
928 | case SB_HW_20: |
929 | case SB_HW_201: |
930 | restore_mixer(chip, regs: sb20_saved_regs, ARRAY_SIZE(sb20_saved_regs)); |
931 | break; |
932 | case SB_HW_PRO: |
933 | case SB_HW_JAZZ16: |
934 | restore_mixer(chip, regs: sbpro_saved_regs, ARRAY_SIZE(sbpro_saved_regs)); |
935 | break; |
936 | case SB_HW_16: |
937 | case SB_HW_ALS100: |
938 | case SB_HW_CS5530: |
939 | restore_mixer(chip, regs: sb16_saved_regs, ARRAY_SIZE(sb16_saved_regs)); |
940 | break; |
941 | case SB_HW_ALS4000: |
942 | restore_mixer(chip, regs: als4000_saved_regs, ARRAY_SIZE(als4000_saved_regs)); |
943 | break; |
944 | case SB_HW_DT019X: |
945 | restore_mixer(chip, regs: dt019x_saved_regs, ARRAY_SIZE(dt019x_saved_regs)); |
946 | break; |
947 | default: |
948 | break; |
949 | } |
950 | } |
951 | #endif |
952 | |