1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19#include <linux/slab.h>
20
21#include "sound_config.h"
22
23#define __SB_MIXER_C__
24
25#include "sb.h"
26#include "sb_mixer.h"
27
28#include "sb_ess.h"
29
30#define SBPRO_RECORDING_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | SOUND_MASK_CD)
31
32
33#define SGNXPRO_RECORDING_DEVICES SBPRO_RECORDING_DEVICES
34
35#define SBPRO_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_LINE | SOUND_MASK_MIC | \
36 SOUND_MASK_CD | SOUND_MASK_VOLUME)
37
38
39
40
41
42
43#define SGNXPRO_MIXER_DEVICES (SBPRO_MIXER_DEVICES|SOUND_MASK_BASS| \
44 SOUND_MASK_TREBLE|SOUND_MASK_SPEAKER )
45
46#define SB16_RECORDING_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_LINE | SOUND_MASK_MIC | \
47 SOUND_MASK_CD)
48
49#define SB16_OUTFILTER_DEVICES (SOUND_MASK_LINE | SOUND_MASK_MIC | \
50 SOUND_MASK_CD)
51
52#define SB16_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_PCM | SOUND_MASK_SPEAKER | SOUND_MASK_LINE | SOUND_MASK_MIC | \
53 SOUND_MASK_CD | \
54 SOUND_MASK_IGAIN | SOUND_MASK_OGAIN | \
55 SOUND_MASK_VOLUME | SOUND_MASK_BASS | SOUND_MASK_TREBLE | \
56 SOUND_MASK_IMIX)
57
58
59
60
61#define ALS007_MIXER_DEVICES (SOUND_MASK_SYNTH | SOUND_MASK_LINE | \
62 SOUND_MASK_PCM | SOUND_MASK_MIC | \
63 SOUND_MASK_CD | \
64 SOUND_MASK_VOLUME)
65
66static mixer_tab sbpro_mix = {
67MIX_ENT(SOUND_MIXER_VOLUME, 0x22, 7, 4, 0x22, 3, 4),
68MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0),
69MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0),
70MIX_ENT(SOUND_MIXER_SYNTH, 0x26, 7, 4, 0x26, 3, 4),
71MIX_ENT(SOUND_MIXER_PCM, 0x04, 7, 4, 0x04, 3, 4),
72MIX_ENT(SOUND_MIXER_SPEAKER, 0x00, 0, 0, 0x00, 0, 0),
73MIX_ENT(SOUND_MIXER_LINE, 0x2e, 7, 4, 0x2e, 3, 4),
74MIX_ENT(SOUND_MIXER_MIC, 0x0a, 2, 3, 0x00, 0, 0),
75MIX_ENT(SOUND_MIXER_CD, 0x28, 7, 4, 0x28, 3, 4),
76MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
77MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
78MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0)
79};
80
81static mixer_tab sb16_mix = {
82MIX_ENT(SOUND_MIXER_VOLUME, 0x30, 7, 5, 0x31, 7, 5),
83MIX_ENT(SOUND_MIXER_BASS, 0x46, 7, 4, 0x47, 7, 4),
84MIX_ENT(SOUND_MIXER_TREBLE, 0x44, 7, 4, 0x45, 7, 4),
85MIX_ENT(SOUND_MIXER_SYNTH, 0x34, 7, 5, 0x35, 7, 5),
86MIX_ENT(SOUND_MIXER_PCM, 0x32, 7, 5, 0x33, 7, 5),
87MIX_ENT(SOUND_MIXER_SPEAKER, 0x3b, 7, 2, 0x00, 0, 0),
88MIX_ENT(SOUND_MIXER_LINE, 0x38, 7, 5, 0x39, 7, 5),
89MIX_ENT(SOUND_MIXER_MIC, 0x3a, 7, 5, 0x00, 0, 0),
90MIX_ENT(SOUND_MIXER_CD, 0x36, 7, 5, 0x37, 7, 5),
91MIX_ENT(SOUND_MIXER_IMIX, 0x3c, 0, 1, 0x00, 0, 0),
92MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
93MIX_ENT(SOUND_MIXER_RECLEV, 0x3f, 7, 2, 0x40, 7, 2),
94MIX_ENT(SOUND_MIXER_IGAIN, 0x3f, 7, 2, 0x40, 7, 2),
95MIX_ENT(SOUND_MIXER_OGAIN, 0x41, 7, 2, 0x42, 7, 2)
96};
97
98static mixer_tab als007_mix =
99{
100MIX_ENT(SOUND_MIXER_VOLUME, 0x62, 7, 4, 0x62, 3, 4),
101MIX_ENT(SOUND_MIXER_BASS, 0x00, 0, 0, 0x00, 0, 0),
102MIX_ENT(SOUND_MIXER_TREBLE, 0x00, 0, 0, 0x00, 0, 0),
103MIX_ENT(SOUND_MIXER_SYNTH, 0x66, 7, 4, 0x66, 3, 4),
104MIX_ENT(SOUND_MIXER_PCM, 0x64, 7, 4, 0x64, 3, 4),
105MIX_ENT(SOUND_MIXER_SPEAKER, 0x00, 0, 0, 0x00, 0, 0),
106MIX_ENT(SOUND_MIXER_LINE, 0x6e, 7, 4, 0x6e, 3, 4),
107MIX_ENT(SOUND_MIXER_MIC, 0x6a, 2, 3, 0x00, 0, 0),
108MIX_ENT(SOUND_MIXER_CD, 0x68, 7, 4, 0x68, 3, 4),
109MIX_ENT(SOUND_MIXER_IMIX, 0x00, 0, 0, 0x00, 0, 0),
110MIX_ENT(SOUND_MIXER_ALTPCM, 0x00, 0, 0, 0x00, 0, 0),
111MIX_ENT(SOUND_MIXER_RECLEV, 0x00, 0, 0, 0x00, 0, 0),
112MIX_ENT(SOUND_MIXER_IGAIN, 0x00, 0, 0, 0x00, 0, 0),
113MIX_ENT(SOUND_MIXER_OGAIN, 0x00, 0, 0, 0x00, 0, 0)
114};
115
116
117
118
119
120
121static int smg_default_levels[32] =
122{
123 0x2020,
124 0x4b4b,
125 0x4b4b,
126 0x6464,
127 0x6464,
128 0x4b4b,
129 0x4b4b,
130 0x0000,
131 0x4b4b,
132 0x4b4b,
133 0x4b4b,
134 0x4b4b,
135 0x4b4b,
136 0x4b4b,
137 0x4040,
138 0x4040,
139 0x1515
140};
141
142static int sb_default_levels[32] =
143{
144 0x5a5a,
145 0x4b4b,
146 0x4b4b,
147 0x4b4b,
148 0x4b4b,
149 0x4b4b,
150 0x4b4b,
151 0x1010,
152 0x4b4b,
153 0x0000,
154 0x4b4b,
155 0x4b4b,
156 0x4b4b,
157 0x4b4b,
158 0x4040,
159 0x4040,
160 0x1515
161};
162
163static unsigned char sb16_recmasks_L[SOUND_MIXER_NRDEVICES] =
164{
165 0x00,
166 0x00,
167 0x00,
168 0x40,
169 0x00,
170 0x00,
171 0x10,
172 0x01,
173 0x04,
174 0x00,
175 0x00,
176 0x00,
177 0x00,
178 0x00
179};
180
181static unsigned char sb16_recmasks_R[SOUND_MIXER_NRDEVICES] =
182{
183 0x00,
184 0x00,
185 0x00,
186 0x20,
187 0x00,
188 0x00,
189 0x08,
190 0x01,
191 0x02,
192 0x00,
193 0x00,
194 0x00,
195 0x00,
196 0x00
197};
198
199static char smw_mix_regs[] =
200{
201 0x0b,
202 0x0d,
203 0x0d,
204 0x05,
205 0x09,
206 0x00,
207 0x03,
208 0x01,
209 0x07,
210 0x00,
211 0x00,
212 0x00,
213 0x00,
214 0x00,
215 0x00,
216 0x00,
217 0x00
218};
219
220static int sbmixnum = 1;
221
222static void sb_mixer_reset(sb_devc * devc);
223
224void sb_mixer_set_stereo(sb_devc * devc, int mode)
225{
226 sb_chgmixer(devc, OUT_FILTER, STEREO_DAC, (mode ? STEREO_DAC : MONO_DAC));
227}
228
229static int detect_mixer(sb_devc * devc)
230{
231
232 return 1;
233}
234
235static void change_bits(sb_devc * devc, unsigned char *regval, int dev, int chn, int newval)
236{
237 unsigned char mask;
238 int shift;
239
240 mask = (1 << (*devc->iomap)[dev][chn].nbits) - 1;
241 newval = (int) ((newval * mask) + 50) / 100;
242
243 shift = (*devc->iomap)[dev][chn].bitoffs - (*devc->iomap)[dev][LEFT_CHN].nbits + 1;
244
245 *regval &= ~(mask << shift);
246 *regval |= (newval & mask) << shift;
247}
248
249static int sb_mixer_get(sb_devc * devc, int dev)
250{
251 if (!((1 << dev) & devc->supported_devices))
252 return -EINVAL;
253 return devc->levels[dev];
254}
255
256void smw_mixer_init(sb_devc * devc)
257{
258 int i;
259
260 sb_setmixer(devc, 0x00, 0x18);
261 sb_setmixer(devc, 0x10, 0x38);
262
263 devc->supported_devices = 0;
264 for (i = 0; i < sizeof(smw_mix_regs); i++)
265 if (smw_mix_regs[i] != 0)
266 devc->supported_devices |= (1 << i);
267
268 devc->supported_rec_devices = devc->supported_devices &
269 ~(SOUND_MASK_BASS | SOUND_MASK_TREBLE | SOUND_MASK_PCM | SOUND_MASK_VOLUME);
270 sb_mixer_reset(devc);
271}
272
273int sb_common_mixer_set(sb_devc * devc, int dev, int left, int right)
274{
275 int regoffs;
276 unsigned char val;
277
278 if ((dev < 0) || (dev >= devc->iomap_sz))
279 return -EINVAL;
280
281 regoffs = (*devc->iomap)[dev][LEFT_CHN].regno;
282
283 if (regoffs == 0)
284 return -EINVAL;
285
286 val = sb_getmixer(devc, regoffs);
287 change_bits(devc, &val, dev, LEFT_CHN, left);
288
289 if ((*devc->iomap)[dev][RIGHT_CHN].regno != regoffs)
290
291
292 {
293 sb_setmixer(devc, regoffs, val);
294
295
296 regoffs = (*devc->iomap)[dev][RIGHT_CHN].regno;
297
298 if (regoffs == 0)
299 return left | (left << 8);
300
301
302
303 val = sb_getmixer(devc, regoffs);
304
305
306 }
307 change_bits(devc, &val, dev, RIGHT_CHN, right);
308
309 sb_setmixer(devc, regoffs, val);
310
311 return left | (right << 8);
312}
313
314static int smw_mixer_set(sb_devc * devc, int dev, int left, int right)
315{
316 int reg, val;
317
318 switch (dev)
319 {
320 case SOUND_MIXER_VOLUME:
321 sb_setmixer(devc, 0x0b, 96 - (96 * left / 100));
322 sb_setmixer(devc, 0x0c, 96 - (96 * right / 100));
323 break;
324
325 case SOUND_MIXER_BASS:
326 case SOUND_MIXER_TREBLE:
327 devc->levels[dev] = left | (right << 8);
328
329 val = ((devc->levels[SOUND_MIXER_TREBLE] & 0xff) * 16 / (unsigned) 100) << 4;
330 val |= ((devc->levels[SOUND_MIXER_BASS] & 0xff) * 16 / (unsigned) 100) & 0x0f;
331 sb_setmixer(devc, 0x0d, val);
332
333
334 val = (((devc->levels[SOUND_MIXER_TREBLE] >> 8) & 0xff) * 16 / (unsigned) 100) << 4;
335 val |= (((devc->levels[SOUND_MIXER_BASS] >> 8) & 0xff) * 16 / (unsigned) 100) & 0x0f;
336 sb_setmixer(devc, 0x0e, val);
337
338 break;
339
340 default:
341
342 if (dev < 0 || dev >= ARRAY_SIZE(smw_mix_regs))
343 return -EINVAL;
344 reg = smw_mix_regs[dev];
345 if (reg == 0)
346 return -EINVAL;
347 sb_setmixer(devc, reg, (24 - (24 * left / 100)) | 0x20);
348 sb_setmixer(devc, reg + 1, (24 - (24 * right / 100)) | 0x40);
349 }
350
351 devc->levels[dev] = left | (right << 8);
352 return left | (right << 8);
353}
354
355static int sb_mixer_set(sb_devc * devc, int dev, int value)
356{
357 int left = value & 0x000000ff;
358 int right = (value & 0x0000ff00) >> 8;
359 int retval;
360
361 if (left > 100)
362 left = 100;
363 if (right > 100)
364 right = 100;
365
366 if ((dev < 0) || (dev > 31))
367 return -EINVAL;
368
369 if (!(devc->supported_devices & (1 << dev)))
370
371
372 return -EINVAL;
373
374
375 switch (devc->model) {
376 case MDL_SMW:
377 retval = smw_mixer_set(devc, dev, left, right);
378 break;
379 case MDL_ESS:
380 retval = ess_mixer_set(devc, dev, left, right);
381 break;
382 default:
383 retval = sb_common_mixer_set(devc, dev, left, right);
384 }
385 if (retval >= 0) devc->levels[dev] = retval;
386
387 return retval;
388}
389
390
391
392
393static void set_recsrc(sb_devc * devc, int src)
394{
395 sb_setmixer(devc, RECORD_SRC, (sb_getmixer(devc, RECORD_SRC) & ~7) | (src & 0x7));
396}
397
398static int set_recmask(sb_devc * devc, int mask)
399{
400 int devmask, i;
401 unsigned char regimageL, regimageR;
402
403 devmask = mask & devc->supported_rec_devices;
404
405 switch (devc->model)
406 {
407 case MDL_SBPRO:
408 case MDL_ESS:
409 case MDL_JAZZ:
410 case MDL_SMW:
411 if (devc->model == MDL_ESS && ess_set_recmask (devc, &devmask)) {
412 break;
413 };
414 if (devmask != SOUND_MASK_MIC &&
415 devmask != SOUND_MASK_LINE &&
416 devmask != SOUND_MASK_CD)
417 {
418
419
420
421
422 devmask &= ~devc->recmask;
423 }
424 if (devmask != SOUND_MASK_MIC &&
425 devmask != SOUND_MASK_LINE &&
426 devmask != SOUND_MASK_CD)
427 {
428
429
430
431
432 devmask = SOUND_MASK_MIC;
433 }
434 if (devmask ^ devc->recmask)
435
436
437 {
438 switch (devmask)
439 {
440 case SOUND_MASK_MIC:
441 set_recsrc(devc, SRC__MIC);
442 break;
443
444 case SOUND_MASK_LINE:
445 set_recsrc(devc, SRC__LINE);
446 break;
447
448 case SOUND_MASK_CD:
449 set_recsrc(devc, SRC__CD);
450 break;
451
452 default:
453 set_recsrc(devc, SRC__MIC);
454 }
455 }
456 break;
457
458 case MDL_SB16:
459 if (!devmask)
460 devmask = SOUND_MASK_MIC;
461
462 if (devc->submodel == SUBMDL_ALS007)
463 {
464 switch (devmask)
465 {
466 case SOUND_MASK_LINE:
467 sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_LINE);
468 break;
469 case SOUND_MASK_CD:
470 sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_CD);
471 break;
472 case SOUND_MASK_SYNTH:
473 sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_SYNTH);
474 break;
475 default:
476 sb_setmixer(devc, ALS007_RECORD_SRC, ALS007_MIC);
477 break;
478 }
479 }
480 else
481 {
482 regimageL = regimageR = 0;
483 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
484 {
485 if ((1 << i) & devmask)
486 {
487 regimageL |= sb16_recmasks_L[i];
488 regimageR |= sb16_recmasks_R[i];
489 }
490 sb_setmixer (devc, SB16_IMASK_L, regimageL);
491 sb_setmixer (devc, SB16_IMASK_R, regimageR);
492 }
493 }
494 break;
495 }
496 devc->recmask = devmask;
497 return devc->recmask;
498}
499
500static int set_outmask(sb_devc * devc, int mask)
501{
502 int devmask, i;
503 unsigned char regimage;
504
505 devmask = mask & devc->supported_out_devices;
506
507 switch (devc->model)
508 {
509 case MDL_SB16:
510 if (devc->submodel == SUBMDL_ALS007)
511 break;
512 else
513 {
514 regimage = 0;
515 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
516 {
517 if ((1 << i) & devmask)
518 {
519 regimage |= (sb16_recmasks_L[i] | sb16_recmasks_R[i]);
520 }
521 sb_setmixer (devc, SB16_OMASK, regimage);
522 }
523 }
524 break;
525 default:
526 break;
527 }
528
529 devc->outmask = devmask;
530 return devc->outmask;
531}
532
533static int sb_mixer_ioctl(int dev, unsigned int cmd, void __user *arg)
534{
535 sb_devc *devc = mixer_devs[dev]->devc;
536 int val, ret;
537 int __user *p = arg;
538
539
540
541
542
543
544 if (devc->model == MDL_SB16) {
545 if (cmd == SOUND_MIXER_AGC)
546 {
547 if (get_user(val, p))
548 return -EFAULT;
549 sb_setmixer(devc, 0x43, (~val) & 0x01);
550 return 0;
551 }
552 if (cmd == SOUND_MIXER_3DSE)
553 {
554
555
556 if (devc->minor < 15)
557 return -EINVAL;
558 if (get_user(val, p))
559 return -EFAULT;
560 if (val == 0 || val == 1)
561 sb_chgmixer(devc, AWE_3DSE, 0x01, val);
562 else if (val == 2)
563 {
564 ret = sb_getmixer(devc, AWE_3DSE)&0x01;
565 return put_user(ret, p);
566 }
567 else
568 return -EINVAL;
569 return 0;
570 }
571 }
572 if (((cmd >> 8) & 0xff) == 'M')
573 {
574 if (_SIOC_DIR(cmd) & _SIOC_WRITE)
575 {
576 if (get_user(val, p))
577 return -EFAULT;
578 switch (cmd & 0xff)
579 {
580 case SOUND_MIXER_RECSRC:
581 ret = set_recmask(devc, val);
582 break;
583
584 case SOUND_MIXER_OUTSRC:
585 ret = set_outmask(devc, val);
586 break;
587
588 default:
589 ret = sb_mixer_set(devc, cmd & 0xff, val);
590 }
591 }
592 else switch (cmd & 0xff)
593 {
594 case SOUND_MIXER_RECSRC:
595 ret = devc->recmask;
596 break;
597
598 case SOUND_MIXER_OUTSRC:
599 ret = devc->outmask;
600 break;
601
602 case SOUND_MIXER_DEVMASK:
603 ret = devc->supported_devices;
604 break;
605
606 case SOUND_MIXER_STEREODEVS:
607 ret = devc->supported_devices;
608
609 if (devc->model == MDL_ESS)
610 ret &= ~(SOUND_MASK_SPEAKER|SOUND_MASK_IMIX);
611 else if (devc->model != MDL_JAZZ && devc->model != MDL_SMW)
612 ret &= ~(SOUND_MASK_MIC | SOUND_MASK_SPEAKER | SOUND_MASK_IMIX);
613 break;
614
615 case SOUND_MIXER_RECMASK:
616 ret = devc->supported_rec_devices;
617 break;
618
619 case SOUND_MIXER_OUTMASK:
620 ret = devc->supported_out_devices;
621 break;
622
623 case SOUND_MIXER_CAPS:
624 ret = devc->mixer_caps;
625 break;
626
627 default:
628 ret = sb_mixer_get(devc, cmd & 0xff);
629 break;
630 }
631 return put_user(ret, p);
632 } else
633 return -EINVAL;
634}
635
636static struct mixer_operations sb_mixer_operations =
637{
638 .owner = THIS_MODULE,
639 .id = "SB",
640 .name = "Sound Blaster",
641 .ioctl = sb_mixer_ioctl
642};
643
644static struct mixer_operations als007_mixer_operations =
645{
646 .owner = THIS_MODULE,
647 .id = "ALS007",
648 .name = "Avance ALS-007",
649 .ioctl = sb_mixer_ioctl
650};
651
652static void sb_mixer_reset(sb_devc * devc)
653{
654 char name[32];
655 int i;
656
657 sprintf(name, "SB_%d", devc->sbmixnum);
658
659 if (devc->sbmo.sm_games)
660 devc->levels = load_mixer_volumes(name, smg_default_levels, 1);
661 else
662 devc->levels = load_mixer_volumes(name, sb_default_levels, 1);
663
664 for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
665 sb_mixer_set(devc, i, devc->levels[i]);
666
667 if (devc->model != MDL_ESS || !ess_mixer_reset (devc)) {
668 set_recmask(devc, SOUND_MASK_MIC);
669 };
670}
671
672int sb_mixer_init(sb_devc * devc, struct module *owner)
673{
674 int mixer_type = 0;
675 int m;
676
677 devc->sbmixnum = sbmixnum++;
678 devc->levels = NULL;
679
680 sb_setmixer(devc, 0x00, 0);
681
682 if (!(mixer_type = detect_mixer(devc)))
683 return 0;
684
685 switch (devc->model)
686 {
687 case MDL_ESSPCI:
688 case MDL_YMPCI:
689 case MDL_SBPRO:
690 case MDL_AZTECH:
691 case MDL_JAZZ:
692 devc->mixer_caps = SOUND_CAP_EXCL_INPUT;
693 devc->supported_devices = SBPRO_MIXER_DEVICES;
694 devc->supported_rec_devices = SBPRO_RECORDING_DEVICES;
695 devc->iomap = &sbpro_mix;
696 devc->iomap_sz = ARRAY_SIZE(sbpro_mix);
697 break;
698
699 case MDL_ESS:
700 ess_mixer_init (devc);
701 break;
702
703 case MDL_SMW:
704 devc->mixer_caps = SOUND_CAP_EXCL_INPUT;
705 devc->supported_devices = 0;
706 devc->supported_rec_devices = 0;
707 devc->iomap = &sbpro_mix;
708 devc->iomap_sz = ARRAY_SIZE(sbpro_mix);
709 smw_mixer_init(devc);
710 break;
711
712 case MDL_SB16:
713 devc->mixer_caps = 0;
714 devc->supported_rec_devices = SB16_RECORDING_DEVICES;
715 devc->supported_out_devices = SB16_OUTFILTER_DEVICES;
716 if (devc->submodel != SUBMDL_ALS007)
717 {
718 devc->supported_devices = SB16_MIXER_DEVICES;
719 devc->iomap = &sb16_mix;
720 devc->iomap_sz = ARRAY_SIZE(sb16_mix);
721 }
722 else
723 {
724 devc->supported_devices = ALS007_MIXER_DEVICES;
725 devc->iomap = &als007_mix;
726 devc->iomap_sz = ARRAY_SIZE(als007_mix);
727 }
728 break;
729
730 default:
731 printk(KERN_WARNING "sb_mixer: Unsupported mixer type %d\n", devc->model);
732 return 0;
733 }
734
735 m = sound_alloc_mixerdev();
736 if (m == -1)
737 return 0;
738
739 mixer_devs[m] = kmalloc(sizeof(struct mixer_operations), GFP_KERNEL);
740 if (mixer_devs[m] == NULL)
741 {
742 printk(KERN_ERR "sb_mixer: Can't allocate memory\n");
743 sound_unload_mixerdev(m);
744 return 0;
745 }
746
747 if (devc->submodel != SUBMDL_ALS007)
748 memcpy ((char *) mixer_devs[m], (char *) &sb_mixer_operations, sizeof (struct mixer_operations));
749 else
750 memcpy ((char *) mixer_devs[m], (char *) &als007_mixer_operations, sizeof (struct mixer_operations));
751
752 mixer_devs[m]->devc = devc;
753
754 if (owner)
755 mixer_devs[m]->owner = owner;
756
757 devc->my_mixerdev = m;
758 sb_mixer_reset(devc);
759 return 1;
760}
761
762void sb_mixer_unload(sb_devc *devc)
763{
764 if (devc->my_mixerdev == -1)
765 return;
766
767 kfree(mixer_devs[devc->my_mixerdev]);
768 sound_unload_mixerdev(devc->my_mixerdev);
769 sbmixnum--;
770}
771