wss.c revision 1.24 1 /* $NetBSD: wss.c,v 1.24 1997/05/07 18:51:49 augustss Exp $ */
2
3 /*
4 * Copyright (c) 1994 John Brezak
5 * Copyright (c) 1991-1993 Regents of the University of California.
6 * All rights reserved.
7 *
8 * MAD support:
9 * Copyright (c) 1996 Lennart Augustsson
10 * Based on code which is
11 * Copyright (c) 1995 Hannu Savolainen
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. All advertising materials mentioning features or use of this software
22 * must display the following acknowledgement:
23 * This product includes software developed by the Computer Systems
24 * Engineering Group at Lawrence Berkeley Laboratory.
25 * 4. Neither the name of the University nor of the Laboratory may be used
26 * to endorse or promote products derived from this software without
27 * specific prior written permission.
28 *
29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 * SUCH DAMAGE.
40 *
41 */
42 /*
43 * Copyright by Hannu Savolainen 1994
44 *
45 * Redistribution and use in source and binary forms, with or without
46 * modification, are permitted provided that the following conditions are
47 * met: 1. Redistributions of source code must retain the above copyright
48 * notice, this list of conditions and the following disclaimer. 2.
49 * Redistributions in binary form must reproduce the above copyright notice,
50 * this list of conditions and the following disclaimer in the documentation
51 * and/or other materials provided with the distribution.
52 *
53 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
54 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
55 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
56 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
57 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
58 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
59 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
60 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
61 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
62 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
63 * SUCH DAMAGE.
64 *
65 */
66
67 #include <sys/param.h>
68 #include <sys/systm.h>
69 #include <sys/errno.h>
70 #include <sys/ioctl.h>
71 #include <sys/syslog.h>
72 #include <sys/device.h>
73 #include <sys/proc.h>
74 #include <sys/buf.h>
75
76 #include <machine/cpu.h>
77 #include <machine/intr.h>
78 #include <machine/pio.h>
79
80 #include <sys/audioio.h>
81 #include <dev/audio_if.h>
82
83 #include <dev/isa/isavar.h>
84 #include <dev/isa/isadmavar.h>
85
86 #include <dev/ic/ad1848reg.h>
87 #include <dev/isa/ad1848var.h>
88 #include <dev/isa/wssreg.h>
89 #include <dev/isa/madreg.h>
90
91 /*
92 * Mixer devices
93 */
94 #define WSS_MIC_IN_LVL 0
95 #define WSS_LINE_IN_LVL 1
96 #define WSS_DAC_LVL 2
97 #define WSS_REC_LVL 3
98 #define WSS_MON_LVL 4
99 #define WSS_MIC_IN_MUTE 5
100 #define WSS_LINE_IN_MUTE 6
101 #define WSS_DAC_MUTE 7
102
103 #define WSS_RECORD_SOURCE 8
104
105 /* Classes */
106 #define WSS_INPUT_CLASS 9
107 #define WSS_RECORD_CLASS 10
108 #define WSS_MONITOR_CLASS 11
109
110 #ifdef AUDIO_DEBUG
111 #define DPRINTF(x) if (wssdebug) printf x
112 int wssdebug = 0;
113 #else
114 #define DPRINTF(x)
115 #endif
116
117 struct wss_softc {
118 struct device sc_dev; /* base device */
119 struct isadev sc_id; /* ISA device */
120 void *sc_ih; /* interrupt vectoring */
121 bus_space_tag_t sc_iot; /* tag */
122 bus_space_handle_t sc_ioh; /* handle */
123
124 struct ad1848_softc sc_ad1848;
125 #define wss_irq sc_ad1848.sc_irq
126 #define wss_drq sc_ad1848.sc_drq
127
128 int mic_mute, cd_mute, dac_mute;
129 int mad_chip_type; /* chip type if MAD emulation of WSS */
130 bus_space_handle_t sc_mad_ioh; /* handle */
131 };
132
133 struct audio_device wss_device = {
134 "wss,ad1848",
135 "",
136 "WSS"
137 };
138
139 int wssopen __P((dev_t, int));
140 int wss_getdev __P((void *, struct audio_device *));
141 int wss_setfd __P((void *, int));
142
143 int wss_set_out_port __P((void *, int));
144 int wss_get_out_port __P((void *));
145 int wss_set_in_port __P((void *, int));
146 int wss_get_in_port __P((void *));
147 int wss_mixer_set_port __P((void *, mixer_ctrl_t *));
148 int wss_mixer_get_port __P((void *, mixer_ctrl_t *));
149 int wss_query_devinfo __P((void *, mixer_devinfo_t *));
150
151 static int wss_to_vol __P((mixer_ctrl_t *, struct ad1848_volume *));
152 static int wss_from_vol __P((mixer_ctrl_t *, struct ad1848_volume *));
153
154 static int madprobe __P((struct wss_softc *, int));
155 static void madprobedone __P((struct wss_softc *));
156
157 /*
158 * Define our interface to the higher level audio driver.
159 */
160
161 struct audio_hw_if wss_hw_if = {
162 wssopen,
163 ad1848_close,
164 NULL,
165 ad1848_query_encoding,
166 ad1848_set_out_params,
167 ad1848_set_in_params,
168 ad1848_round_blocksize,
169 wss_set_out_port,
170 wss_get_out_port,
171 wss_set_in_port,
172 wss_get_in_port,
173 ad1848_commit_settings,
174 NULL,
175 NULL,
176 ad1848_dma_output,
177 ad1848_dma_input,
178 ad1848_halt_out_dma,
179 ad1848_halt_in_dma,
180 ad1848_cont_out_dma,
181 ad1848_cont_in_dma,
182 NULL,
183 wss_getdev,
184 wss_setfd,
185 wss_mixer_set_port,
186 wss_mixer_get_port,
187 wss_query_devinfo,
188 0, /* not full-duplex */
189 0
190 };
191
192 int wssprobe __P((struct device *, void *, void *));
193 void wssattach __P((struct device *, struct device *, void *));
194
195 struct cfattach wss_ca = {
196 sizeof(struct wss_softc), wssprobe, wssattach
197 };
198
199 struct cfdriver wss_cd = {
200 NULL, "wss", DV_DULL
201 };
202
203 /*
204 * Probe for the Microsoft Sound System hardware.
205 */
206 int
207 wssprobe(parent, match, aux)
208 struct device *parent;
209 void *match, *aux;
210 {
211 register struct wss_softc *sc = match;
212 register struct isa_attach_args *ia = aux;
213 static u_char interrupt_bits[12] = {
214 -1, -1, -1, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20
215 };
216 static u_char dma_bits[4] = {1, 2, 0, 3};
217
218 sc->sc_iot = ia->ia_iot;
219 if (sc->sc_dev.dv_cfdata->cf_flags & 1)
220 sc->mad_chip_type = madprobe(sc, ia->ia_iobase);
221 else
222 sc->mad_chip_type = MAD_NONE;
223
224 if (!WSS_BASE_VALID(ia->ia_iobase)) {
225 DPRINTF(("wss: configured iobase %x invalid\n", ia->ia_iobase));
226 return 0;
227 }
228
229 /* map the ports upto the AD1488 port */
230 if (bus_space_map(sc->sc_iot, ia->ia_iobase, WSS_CODEC, 0, &sc->sc_ioh))
231 return 0;
232
233 sc->sc_ad1848.sc_iot = sc->sc_iot;
234 sc->sc_ad1848.sc_iobase = ia->ia_iobase + WSS_CODEC;
235
236 /* Is there an ad1848 chip at (WSS iobase + WSS_CODEC)? */
237 if (ad1848_probe(&sc->sc_ad1848) == 0)
238 goto bad;
239
240 ia->ia_iosize = WSS_NPORT;
241
242 /* Setup WSS interrupt and DMA */
243 if (!WSS_DRQ_VALID(ia->ia_drq)) {
244 DPRINTF(("wss: configured dma chan %d invalid\n", ia->ia_drq));
245 goto bad;
246 }
247 sc->wss_drq = ia->ia_drq;
248
249 #ifdef NEWCONFIG
250 /*
251 * If the IRQ wasn't compiled in, auto-detect it.
252 */
253 if (ia->ia_irq == IRQUNK) {
254 ia->ia_irq = isa_discoverintr(ad1848_forceintr, &sc->sc_ad1848);
255 if (!WSS_IRQ_VALID(ia->ia_irq)) {
256 printf("wss: couldn't auto-detect interrupt\n");
257 goto bad;
258 }
259 }
260 else
261 #endif
262 if (!WSS_IRQ_VALID(ia->ia_irq)) {
263 DPRINTF(("wss: configured interrupt %d invalid\n", ia->ia_irq));
264 goto bad;
265 }
266
267 sc->wss_irq = ia->ia_irq;
268
269 bus_space_write_1(sc->sc_iot, sc->sc_ioh, WSS_CONFIG,
270 (interrupt_bits[ia->ia_irq] | dma_bits[ia->ia_drq]));
271
272 if (sc->mad_chip_type != MAD_NONE)
273 madprobedone(sc);
274
275 return 1;
276
277 bad:
278 bus_space_unmap(sc->sc_iot, sc->sc_ioh, WSS_CODEC);
279 if (sc->mad_chip_type != MAD_NONE)
280 bus_space_unmap(sc->sc_iot, sc->sc_mad_ioh, MAD_NPORT);
281 return 0;
282 }
283
284 /*
285 * Attach hardware to driver, attach hardware driver to audio
286 * pseudo-device driver .
287 */
288 void
289 wssattach(parent, self, aux)
290 struct device *parent, *self;
291 void *aux;
292 {
293 register struct wss_softc *sc = (struct wss_softc *)self;
294 struct isa_attach_args *ia = (struct isa_attach_args *)aux;
295 int version;
296 int err;
297
298 sc->sc_ad1848.sc_recdrq = ia->ia_drq;
299
300 #ifdef NEWCONFIG
301 isa_establish(&sc->sc_id, &sc->sc_dev);
302 #endif
303 sc->sc_ih = isa_intr_establish(ia->ia_ic, ia->ia_irq, IST_EDGE, IPL_AUDIO,
304 ad1848_intr, &sc->sc_ad1848);
305
306 ad1848_attach(&sc->sc_ad1848);
307
308 version = bus_space_read_1(sc->sc_iot, sc->sc_ioh, WSS_STATUS) & WSS_VERSMASK;
309 printf(" (vers %d)", version);
310 if (sc->mad_chip_type != MAD_NONE)
311 printf(", %s",
312 sc->mad_chip_type == MAD_82C929 ? "82C929" :
313 sc->mad_chip_type == MAD_82C928 ? "82C928" :
314 "OTI-601D");
315 printf("\n");
316
317 sc->sc_ad1848.parent = sc;
318
319 if ((err = audio_hardware_attach(&wss_hw_if, &sc->sc_ad1848)) != 0)
320 printf("wss: could not attach to audio pseudo-device driver (%d)\n", err);
321 }
322
323 static int
324 wss_to_vol(cp, vol)
325 mixer_ctrl_t *cp;
326 struct ad1848_volume *vol;
327 {
328 if (cp->un.value.num_channels == 1) {
329 vol->left = vol->right = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
330 return(1);
331 }
332 else if (cp->un.value.num_channels == 2) {
333 vol->left = cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
334 vol->right = cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
335 return(1);
336 }
337 return(0);
338 }
339
340 static int
341 wss_from_vol(cp, vol)
342 mixer_ctrl_t *cp;
343 struct ad1848_volume *vol;
344 {
345 if (cp->un.value.num_channels == 1) {
346 cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = vol->left;
347 return(1);
348 }
349 else if (cp->un.value.num_channels == 2) {
350 cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = vol->left;
351 cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = vol->right;
352 return(1);
353 }
354 return(0);
355 }
356
357 int
358 wssopen(dev, flags)
359 dev_t dev;
360 int flags;
361 {
362 struct wss_softc *sc;
363 int unit = AUDIOUNIT(dev);
364
365 if (unit >= wss_cd.cd_ndevs)
366 return ENODEV;
367
368 sc = wss_cd.cd_devs[unit];
369 if (!sc)
370 return ENXIO;
371
372 return ad1848_open(&sc->sc_ad1848, dev, flags);
373 }
374
375 int
376 wss_getdev(addr, retp)
377 void *addr;
378 struct audio_device *retp;
379 {
380 *retp = wss_device;
381 return 0;
382 }
383
384 int
385 wss_setfd(addr, flag)
386 void *addr;
387 int flag;
388 {
389 /* Can't do full-duplex */
390 return(ENOTTY);
391 }
392
393
394 int
395 wss_set_out_port(addr, port)
396 void *addr;
397 int port;
398 {
399 DPRINTF(("wss_set_out_port:\n"));
400 return(EINVAL);
401 }
402
403 int
404 wss_get_out_port(addr)
405 void *addr;
406 {
407 DPRINTF(("wss_get_out_port:\n"));
408 return(WSS_DAC_LVL);
409 }
410
411 int
412 wss_set_in_port(addr, port)
413 void *addr;
414 int port;
415 {
416 register struct ad1848_softc *ac = addr;
417
418 DPRINTF(("wss_set_in_port: %d\n", port));
419
420 switch(port) {
421 case WSS_MIC_IN_LVL:
422 port = MIC_IN_PORT;
423 break;
424 case WSS_LINE_IN_LVL:
425 port = LINE_IN_PORT;
426 break;
427 case WSS_DAC_LVL:
428 port = DAC_IN_PORT;
429 break;
430 default:
431 return(EINVAL);
432 /*NOTREACHED*/
433 }
434
435 return(ad1848_set_rec_port(ac, port));
436 }
437
438 int
439 wss_get_in_port(addr)
440 void *addr;
441 {
442 register struct ad1848_softc *ac = addr;
443 int port = WSS_MIC_IN_LVL;
444
445 switch(ad1848_get_rec_port(ac)) {
446 case MIC_IN_PORT:
447 port = WSS_MIC_IN_LVL;
448 break;
449 case LINE_IN_PORT:
450 port = WSS_LINE_IN_LVL;
451 break;
452 case DAC_IN_PORT:
453 port = WSS_DAC_LVL;
454 break;
455 }
456
457 DPRINTF(("wss_get_in_port: %d\n", port));
458
459 return(port);
460 }
461
462 int
463 wss_mixer_set_port(addr, cp)
464 void *addr;
465 mixer_ctrl_t *cp;
466 {
467 register struct ad1848_softc *ac = addr;
468 register struct wss_softc *sc = ac->parent;
469 struct ad1848_volume vol;
470 int error = EINVAL;
471
472 DPRINTF(("wss_mixer_set_port: dev=%d type=%d\n", cp->dev, cp->type));
473
474 switch (cp->dev) {
475 case WSS_MIC_IN_LVL: /* Microphone */
476 if (cp->type == AUDIO_MIXER_VALUE) {
477 if (wss_to_vol(cp, &vol))
478 error = ad1848_set_aux2_gain(ac, &vol);
479 }
480 break;
481
482 case WSS_MIC_IN_MUTE: /* Microphone */
483 if (cp->type == AUDIO_MIXER_ENUM) {
484 sc->mic_mute = cp->un.ord;
485 DPRINTF(("mic mute %d\n", cp->un.ord));
486 error = 0;
487 }
488 break;
489
490 case WSS_LINE_IN_LVL: /* linein/CD */
491 if (cp->type == AUDIO_MIXER_VALUE) {
492 if (wss_to_vol(cp, &vol))
493 error = ad1848_set_aux1_gain(ac, &vol);
494 }
495 break;
496
497 case WSS_LINE_IN_MUTE: /* linein/CD */
498 if (cp->type == AUDIO_MIXER_ENUM) {
499 sc->cd_mute = cp->un.ord;
500 DPRINTF(("CD mute %d\n", cp->un.ord));
501 error = 0;
502 }
503 break;
504
505 case WSS_DAC_LVL: /* dac out */
506 if (cp->type == AUDIO_MIXER_VALUE) {
507 if (wss_to_vol(cp, &vol))
508 error = ad1848_set_out_gain(ac, &vol);
509 }
510 break;
511
512 case WSS_DAC_MUTE: /* dac out */
513 if (cp->type == AUDIO_MIXER_ENUM) {
514 sc->dac_mute = cp->un.ord;
515 DPRINTF(("DAC mute %d\n", cp->un.ord));
516 error = 0;
517 }
518 break;
519
520 case WSS_REC_LVL: /* record level */
521 if (cp->type == AUDIO_MIXER_VALUE) {
522 if (wss_to_vol(cp, &vol))
523 error = ad1848_set_rec_gain(ac, &vol);
524 }
525 break;
526
527 case WSS_RECORD_SOURCE:
528 if (cp->type == AUDIO_MIXER_ENUM) {
529 error = ad1848_set_rec_port(ac, cp->un.ord);
530 }
531 break;
532
533 case WSS_MON_LVL:
534 if (cp->type == AUDIO_MIXER_VALUE && cp->un.value.num_channels == 1) {
535 vol.left = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
536 error = ad1848_set_mon_gain(ac, &vol);
537 }
538 break;
539
540 default:
541 return ENXIO;
542 /*NOTREACHED*/
543 }
544
545 return 0;
546 }
547
548 int
549 wss_mixer_get_port(addr, cp)
550 void *addr;
551 mixer_ctrl_t *cp;
552 {
553 register struct ad1848_softc *ac = addr;
554 register struct wss_softc *sc = ac->parent;
555 struct ad1848_volume vol;
556 int error = EINVAL;
557
558 DPRINTF(("wss_mixer_get_port: port=%d\n", cp->dev));
559
560 switch (cp->dev) {
561 case WSS_MIC_IN_LVL: /* Microphone */
562 if (cp->type == AUDIO_MIXER_VALUE) {
563 error = ad1848_get_aux2_gain(ac, &vol);
564 if (!error)
565 wss_from_vol(cp, &vol);
566 }
567 break;
568
569 case WSS_MIC_IN_MUTE:
570 if (cp->type == AUDIO_MIXER_ENUM) {
571 cp->un.ord = sc->mic_mute;
572 error = 0;
573 }
574 break;
575
576 case WSS_LINE_IN_LVL: /* linein/CD */
577 if (cp->type == AUDIO_MIXER_VALUE) {
578 error = ad1848_get_aux1_gain(ac, &vol);
579 if (!error)
580 wss_from_vol(cp, &vol);
581 }
582 break;
583
584 case WSS_LINE_IN_MUTE:
585 if (cp->type == AUDIO_MIXER_ENUM) {
586 cp->un.ord = sc->cd_mute;
587 error = 0;
588 }
589 break;
590
591 case WSS_DAC_LVL: /* dac out */
592 if (cp->type == AUDIO_MIXER_VALUE) {
593 error = ad1848_get_out_gain(ac, &vol);
594 if (!error)
595 wss_from_vol(cp, &vol);
596 }
597 break;
598
599 case WSS_DAC_MUTE:
600 if (cp->type == AUDIO_MIXER_ENUM) {
601 cp->un.ord = sc->dac_mute;
602 error = 0;
603 }
604 break;
605
606 case WSS_REC_LVL: /* record level */
607 if (cp->type == AUDIO_MIXER_VALUE) {
608 error = ad1848_get_rec_gain(ac, &vol);
609 if (!error)
610 wss_from_vol(cp, &vol);
611 }
612 break;
613
614 case WSS_RECORD_SOURCE:
615 if (cp->type == AUDIO_MIXER_ENUM) {
616 cp->un.ord = ad1848_get_rec_port(ac);
617 error = 0;
618 }
619 break;
620
621 case WSS_MON_LVL: /* monitor level */
622 if (cp->type == AUDIO_MIXER_VALUE && cp->un.value.num_channels == 1) {
623 error = ad1848_get_mon_gain(ac, &vol);
624 if (!error)
625 cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = vol.left;
626 }
627 break;
628
629 default:
630 error = ENXIO;
631 break;
632 }
633
634 return(error);
635 }
636
637 int
638 wss_query_devinfo(addr, dip)
639 void *addr;
640 register mixer_devinfo_t *dip;
641 {
642 DPRINTF(("wss_query_devinfo: index=%d\n", dip->index));
643
644 switch(dip->index) {
645 case WSS_MIC_IN_LVL: /* Microphone */
646 dip->type = AUDIO_MIXER_VALUE;
647 dip->mixer_class = WSS_INPUT_CLASS;
648 dip->prev = AUDIO_MIXER_LAST;
649 dip->next = WSS_MIC_IN_MUTE;
650 strcpy(dip->label.name, AudioNmicrophone);
651 dip->un.v.num_channels = 2;
652 strcpy(dip->un.v.units.name, AudioNvolume);
653 break;
654
655 case WSS_LINE_IN_LVL: /* line/CD */
656 dip->type = AUDIO_MIXER_VALUE;
657 dip->mixer_class = WSS_INPUT_CLASS;
658 dip->prev = AUDIO_MIXER_LAST;
659 dip->next = WSS_LINE_IN_MUTE;
660 strcpy(dip->label.name, AudioNcd);
661 dip->un.v.num_channels = 2;
662 strcpy(dip->un.v.units.name, AudioNvolume);
663 break;
664
665 case WSS_DAC_LVL: /* dacout */
666 dip->type = AUDIO_MIXER_VALUE;
667 dip->mixer_class = WSS_INPUT_CLASS;
668 dip->prev = AUDIO_MIXER_LAST;
669 dip->next = WSS_DAC_MUTE;
670 strcpy(dip->label.name, AudioNdac);
671 dip->un.v.num_channels = 2;
672 strcpy(dip->un.v.units.name, AudioNvolume);
673 break;
674
675 case WSS_REC_LVL: /* record level */
676 dip->type = AUDIO_MIXER_VALUE;
677 dip->mixer_class = WSS_RECORD_CLASS;
678 dip->prev = AUDIO_MIXER_LAST;
679 dip->next = WSS_RECORD_SOURCE;
680 strcpy(dip->label.name, AudioNrecord);
681 dip->un.v.num_channels = 2;
682 strcpy(dip->un.v.units.name, AudioNvolume);
683 break;
684
685 case WSS_MON_LVL: /* monitor level */
686 dip->type = AUDIO_MIXER_VALUE;
687 dip->mixer_class = WSS_MONITOR_CLASS;
688 dip->next = dip->prev = AUDIO_MIXER_LAST;
689 strcpy(dip->label.name, AudioNmonitor);
690 dip->un.v.num_channels = 1;
691 strcpy(dip->un.v.units.name, AudioNvolume);
692 break;
693
694 case WSS_INPUT_CLASS: /* input class descriptor */
695 dip->type = AUDIO_MIXER_CLASS;
696 dip->mixer_class = WSS_INPUT_CLASS;
697 dip->next = dip->prev = AUDIO_MIXER_LAST;
698 strcpy(dip->label.name, AudioCInputs);
699 break;
700
701 case WSS_MONITOR_CLASS: /* monitor class descriptor */
702 dip->type = AUDIO_MIXER_CLASS;
703 dip->mixer_class = WSS_MONITOR_CLASS;
704 dip->next = dip->prev = AUDIO_MIXER_LAST;
705 strcpy(dip->label.name, AudioCMonitor);
706 break;
707
708 case WSS_RECORD_CLASS: /* record source class */
709 dip->type = AUDIO_MIXER_CLASS;
710 dip->mixer_class = WSS_RECORD_CLASS;
711 dip->next = dip->prev = AUDIO_MIXER_LAST;
712 strcpy(dip->label.name, AudioCRecord);
713 break;
714
715 case WSS_MIC_IN_MUTE:
716 dip->mixer_class = WSS_INPUT_CLASS;
717 dip->type = AUDIO_MIXER_ENUM;
718 dip->prev = WSS_MIC_IN_LVL;
719 dip->next = AUDIO_MIXER_LAST;
720 goto mute;
721
722 case WSS_LINE_IN_MUTE:
723 dip->mixer_class = WSS_INPUT_CLASS;
724 dip->type = AUDIO_MIXER_ENUM;
725 dip->prev = WSS_LINE_IN_LVL;
726 dip->next = AUDIO_MIXER_LAST;
727 goto mute;
728
729 case WSS_DAC_MUTE:
730 dip->mixer_class = WSS_INPUT_CLASS;
731 dip->type = AUDIO_MIXER_ENUM;
732 dip->prev = WSS_DAC_LVL;
733 dip->next = AUDIO_MIXER_LAST;
734 mute:
735 strcpy(dip->label.name, AudioNmute);
736 dip->un.e.num_mem = 2;
737 strcpy(dip->un.e.member[0].label.name, AudioNoff);
738 dip->un.e.member[0].ord = 0;
739 strcpy(dip->un.e.member[1].label.name, AudioNon);
740 dip->un.e.member[1].ord = 1;
741 break;
742
743 case WSS_RECORD_SOURCE:
744 dip->mixer_class = WSS_RECORD_CLASS;
745 dip->type = AUDIO_MIXER_ENUM;
746 dip->prev = WSS_REC_LVL;
747 dip->next = AUDIO_MIXER_LAST;
748 strcpy(dip->label.name, AudioNsource);
749 dip->un.e.num_mem = 3;
750 strcpy(dip->un.e.member[0].label.name, AudioNmicrophone);
751 dip->un.e.member[0].ord = WSS_MIC_IN_LVL;
752 strcpy(dip->un.e.member[1].label.name, AudioNcd);
753 dip->un.e.member[1].ord = WSS_LINE_IN_LVL;
754 strcpy(dip->un.e.member[2].label.name, AudioNdac);
755 dip->un.e.member[2].ord = WSS_DAC_LVL;
756 break;
757
758 default:
759 return ENXIO;
760 /*NOTREACHED*/
761 }
762 DPRINTF(("AUDIO_MIXER_DEVINFO: name=%s\n", dip->label.name));
763
764 return 0;
765 }
766
767 /*
768 * Initialization code for OPTi MAD16 compatible audio chips. Including
769 *
770 * OPTi 82C928 MAD16 (replaced by C929)
771 * OAK OTI-601D Mozart
772 * OPTi 82C929 MAD16 Pro
773 *
774 */
775 static unsigned int mad_read __P((struct wss_softc *, int, int));
776 static void mad_write __P((struct wss_softc *, int, int, int));
777 static int detect_mad16 __P((struct wss_softc *, int));
778
779 static unsigned int
780 mad_read(sc, chip_type, port)
781 struct wss_softc *sc;
782 int chip_type;
783 int port;
784 {
785 unsigned int tmp;
786 int s = splaudio(); /* don't want an interrupt between outb&inb */
787
788 switch (chip_type) { /* Output password */
789 case MAD_82C928:
790 case MAD_OTI601D:
791 bus_space_write_1(sc->sc_iot, sc->sc_mad_ioh, MC_PASSWD_REG, M_PASSWD_928);
792 break;
793 case MAD_82C929:
794 bus_space_write_1(sc->sc_iot, sc->sc_mad_ioh, MC_PASSWD_REG, M_PASSWD_929);
795 break;
796 }
797 tmp = bus_space_read_1(sc->sc_iot, sc->sc_mad_ioh, port);
798 splx(s);
799 return tmp;
800 }
801
802 static void
803 mad_write(sc, chip_type, port, value)
804 struct wss_softc *sc;
805 int chip_type;
806 int port;
807 int value;
808 {
809 int s = splaudio(); /* don't want an interrupt between outb&outb */
810
811 switch (chip_type) { /* Output password */
812 case MAD_82C928:
813 case MAD_OTI601D:
814 bus_space_write_1(sc->sc_iot, sc->sc_mad_ioh, MC_PASSWD_REG, M_PASSWD_928);
815 break;
816 case MAD_82C929:
817 bus_space_write_1(sc->sc_iot, sc->sc_mad_ioh, MC_PASSWD_REG, M_PASSWD_929);
818 break;
819 }
820 bus_space_write_1(sc->sc_iot, sc->sc_mad_ioh, port, value & 0xff);
821 splx(s);
822 }
823
824 static int
825 detect_mad16(sc, chip_type)
826 struct wss_softc *sc;
827 int chip_type;
828 {
829 unsigned char tmp, tmp2;
830
831 /*
832 * Check that reading a register doesn't return bus float (0xff)
833 * when the card is accessed using password. This may fail in case
834 * the card is in low power mode. Normally at least the power saving mode
835 * bit should be 0.
836 */
837 if ((tmp = mad_read(sc, chip_type, MC1_PORT)) == 0xff) {
838 DPRINTF(("MC1_PORT returned 0xff\n"));
839 return 0;
840 }
841
842 /*
843 * Now check that the gate is closed on first I/O after writing
844 * the password. (This is how a MAD16 compatible card works).
845 */
846 if ((tmp2 = bus_space_read_1(sc->sc_iot, sc->sc_mad_ioh, MC1_PORT)) == tmp) { /* It didn't close */
847 DPRINTF(("MC1_PORT didn't close after read (0x%02x)\n", tmp2));
848 return 0;
849 }
850
851 mad_write(sc, chip_type, MC1_PORT, tmp ^ 0x80); /* Toggle a bit */
852
853 /* Compare the bit */
854 if ((tmp2 = mad_read(sc, chip_type, MC1_PORT)) != (tmp ^ 0x80)) {
855 mad_write(sc, chip_type, MC1_PORT, tmp); /* Restore */
856 DPRINTF(("Bit revert test failed (0x%02x, 0x%02x)\n", tmp, tmp2));
857 return 0;
858 }
859
860 mad_write(sc, chip_type, MC1_PORT, tmp); /* Restore */
861 return 1;
862 }
863
864 static int
865 madprobe(sc, iobase)
866 struct wss_softc *sc;
867 int iobase;
868 {
869 static int valid_ports[M_WSS_NPORTS] =
870 { M_WSS_PORT0, M_WSS_PORT1, M_WSS_PORT2, M_WSS_PORT3 };
871 int i;
872 int chip_type;
873
874 if (bus_space_map(sc->sc_iot, MAD_BASE, MAD_NPORT, 0, &sc->sc_mad_ioh))
875 return MAD_NONE;
876
877 DPRINTF(("mad: Detect using password = 0xE2\n"));
878 if (!detect_mad16(sc, MAD_82C928)) {
879 /* No luck. Try different model */
880 DPRINTF(("mad: Detect using password = 0xE3\n"));
881 if (!detect_mad16(sc, MAD_82C929))
882 goto bad;
883 chip_type = MAD_82C929;
884 DPRINTF(("mad: 82C929 detected\n"));
885 } else {
886 if ((mad_read(sc, MAD_82C928, MC3_PORT) & 0x03) == 0x03) {
887 DPRINTF(("mad: Mozart detected\n"));
888 chip_type = MAD_OTI601D;
889 } else {
890 DPRINTF(("mad: 82C928 detected?\n"));
891 chip_type = MAD_82C928;
892 }
893 }
894
895 #ifdef AUDIO_DEBUG
896 if (wssdebug)
897 for (i = MC1_PORT; i <= MC7_PORT; i++)
898 printf("mad: port %03x = %02x\n", i, mad_read(sc, chip_type, i));
899 #endif
900
901 /* Set the WSS address. */
902 for (i = 0; i < 4; i++)
903 if (valid_ports[i] == iobase)
904 break;
905 if (i > 3) { /* Not a valid port */
906 printf("mad: Bad WSS base address 0x%x\n", iobase);
907 goto bad;
908 }
909 /* enable WSS emulation at the I/O port, keep joystck */
910 mad_write(sc, chip_type, MC1_PORT, M_WSS_PORT_SELECT(i));
911
912 mad_write(sc, chip_type, MC2_PORT, 0x03); /* ? */
913 mad_write(sc, chip_type, MC3_PORT, 0xf0); /* Disable SB */
914
915 return chip_type;
916 bad:
917 bus_space_unmap(sc->sc_iot, sc->sc_mad_ioh, MAD_NPORT);
918 return MAD_NONE;
919 }
920
921 static void
922 madprobedone(sc)
923 struct wss_softc *sc;
924 {
925 int chip_type = sc->mad_chip_type;
926 unsigned char cs4231_mode;
927
928 cs4231_mode =
929 strncmp(sc->sc_ad1848.chip_name, "CS4248", 6) == 0 ||
930 strncmp(sc->sc_ad1848.chip_name, "CS4231", 6) == 0 ? 0x02 : 0;
931
932 if (chip_type == MAD_82C929) {
933 mad_write(sc, chip_type, MC4_PORT, 0xa2);
934 mad_write(sc, chip_type, MC5_PORT, 0xA5 | cs4231_mode);
935 mad_write(sc, chip_type, MC6_PORT, 0x03); /* Disable MPU401 */
936 } else {
937 mad_write(sc, chip_type, MC4_PORT, 0x02);
938 mad_write(sc, chip_type, MC5_PORT, 0x30 | cs4231_mode);
939 }
940
941 #ifdef AUDIO_DEBUG
942 if (wssdebug) {
943 int i;
944 for (i = MC1_PORT; i <= MC7_PORT; i++)
945 DPRINTF(("port %03x after init = %02x\n", i, mad_read(sc, chip_type, i)));
946 }
947 #endif
948 }
949