wss.c revision 1.22 1 /* $NetBSD: wss.c,v 1.22 1997/04/06 00:54:27 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_set_in_sr,
166 ad1848_get_in_sr,
167 ad1848_set_out_sr,
168 ad1848_get_out_sr,
169 ad1848_query_encoding,
170 ad1848_set_format,
171 ad1848_get_encoding,
172 ad1848_get_precision,
173 ad1848_set_channels,
174 ad1848_get_channels,
175 ad1848_round_blocksize,
176 wss_set_out_port,
177 wss_get_out_port,
178 wss_set_in_port,
179 wss_get_in_port,
180 ad1848_commit_settings,
181 NULL,
182 NULL,
183 ad1848_dma_output,
184 ad1848_dma_input,
185 ad1848_halt_out_dma,
186 ad1848_halt_in_dma,
187 ad1848_cont_out_dma,
188 ad1848_cont_in_dma,
189 NULL,
190 wss_getdev,
191 wss_setfd,
192 wss_mixer_set_port,
193 wss_mixer_get_port,
194 wss_query_devinfo,
195 0, /* not full-duplex */
196 0
197 };
198
199 int wssprobe __P((struct device *, void *, void *));
200 void wssattach __P((struct device *, struct device *, void *));
201
202 struct cfattach wss_ca = {
203 sizeof(struct wss_softc), wssprobe, wssattach
204 };
205
206 struct cfdriver wss_cd = {
207 NULL, "wss", DV_DULL
208 };
209
210 /*
211 * Probe for the Microsoft Sound System hardware.
212 */
213 int
214 wssprobe(parent, match, aux)
215 struct device *parent;
216 void *match, *aux;
217 {
218 register struct wss_softc *sc = match;
219 register struct isa_attach_args *ia = aux;
220 static u_char interrupt_bits[12] = {
221 -1, -1, -1, -1, -1, -1, -1, 0x08, -1, 0x10, 0x18, 0x20
222 };
223 static u_char dma_bits[4] = {1, 2, 0, 3};
224
225 sc->sc_iot = ia->ia_iot;
226 if (sc->sc_dev.dv_cfdata->cf_flags & 1)
227 sc->mad_chip_type = madprobe(sc, ia->ia_iobase);
228 else
229 sc->mad_chip_type = MAD_NONE;
230
231 if (!WSS_BASE_VALID(ia->ia_iobase)) {
232 DPRINTF(("wss: configured iobase %x invalid\n", ia->ia_iobase));
233 return 0;
234 }
235
236 /* map the ports upto the AD1488 port */
237 if (bus_space_map(sc->sc_iot, ia->ia_iobase, WSS_CODEC, 0, &sc->sc_ioh))
238 return 0;
239
240 sc->sc_ad1848.sc_iot = sc->sc_iot;
241 sc->sc_ad1848.sc_iobase = ia->ia_iobase + WSS_CODEC;
242
243 /* Is there an ad1848 chip at (WSS iobase + WSS_CODEC)? */
244 if (ad1848_probe(&sc->sc_ad1848) == 0)
245 goto bad;
246
247 ia->ia_iosize = WSS_NPORT;
248
249 /* Setup WSS interrupt and DMA */
250 if (!WSS_DRQ_VALID(ia->ia_drq)) {
251 DPRINTF(("wss: configured dma chan %d invalid\n", ia->ia_drq));
252 goto bad;
253 }
254 sc->wss_drq = ia->ia_drq;
255
256 #ifdef NEWCONFIG
257 /*
258 * If the IRQ wasn't compiled in, auto-detect it.
259 */
260 if (ia->ia_irq == IRQUNK) {
261 ia->ia_irq = isa_discoverintr(ad1848_forceintr, &sc->sc_ad1848);
262 if (!WSS_IRQ_VALID(ia->ia_irq)) {
263 printf("wss: couldn't auto-detect interrupt\n");
264 goto bad;
265 }
266 }
267 else
268 #endif
269 if (!WSS_IRQ_VALID(ia->ia_irq)) {
270 DPRINTF(("wss: configured interrupt %d invalid\n", ia->ia_irq));
271 goto bad;
272 }
273
274 sc->wss_irq = ia->ia_irq;
275
276 bus_space_write_1(sc->sc_iot, sc->sc_ioh, WSS_CONFIG,
277 (interrupt_bits[ia->ia_irq] | dma_bits[ia->ia_drq]));
278
279 if (sc->mad_chip_type != MAD_NONE)
280 madprobedone(sc);
281
282 return 1;
283
284 bad:
285 bus_space_unmap(sc->sc_iot, sc->sc_ioh, WSS_CODEC);
286 if (sc->mad_chip_type != MAD_NONE)
287 bus_space_unmap(sc->sc_iot, sc->sc_mad_ioh, MAD_NPORT);
288 return 0;
289 }
290
291 /*
292 * Attach hardware to driver, attach hardware driver to audio
293 * pseudo-device driver .
294 */
295 void
296 wssattach(parent, self, aux)
297 struct device *parent, *self;
298 void *aux;
299 {
300 register struct wss_softc *sc = (struct wss_softc *)self;
301 struct isa_attach_args *ia = (struct isa_attach_args *)aux;
302 int version;
303 int err;
304
305 sc->sc_ad1848.sc_recdrq = ia->ia_drq;
306
307 #ifdef NEWCONFIG
308 isa_establish(&sc->sc_id, &sc->sc_dev);
309 #endif
310 sc->sc_ih = isa_intr_establish(ia->ia_ic, ia->ia_irq, IST_EDGE, IPL_AUDIO,
311 ad1848_intr, &sc->sc_ad1848);
312
313 ad1848_attach(&sc->sc_ad1848);
314
315 version = bus_space_read_1(sc->sc_iot, sc->sc_ioh, WSS_STATUS) & WSS_VERSMASK;
316 printf(" (vers %d)", version);
317 if (sc->mad_chip_type != MAD_NONE)
318 printf(", %s",
319 sc->mad_chip_type == MAD_82C929 ? "82C929" :
320 sc->mad_chip_type == MAD_82C928 ? "82C928" :
321 "OTI-601D");
322 printf("\n");
323
324 sc->sc_ad1848.parent = sc;
325
326 if ((err = audio_hardware_attach(&wss_hw_if, &sc->sc_ad1848)) != 0)
327 printf("wss: could not attach to audio pseudo-device driver (%d)\n", err);
328 }
329
330 static int
331 wss_to_vol(cp, vol)
332 mixer_ctrl_t *cp;
333 struct ad1848_volume *vol;
334 {
335 if (cp->un.value.num_channels == 1) {
336 vol->left = vol->right = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
337 return(1);
338 }
339 else if (cp->un.value.num_channels == 2) {
340 vol->left = cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
341 vol->right = cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
342 return(1);
343 }
344 return(0);
345 }
346
347 static int
348 wss_from_vol(cp, vol)
349 mixer_ctrl_t *cp;
350 struct ad1848_volume *vol;
351 {
352 if (cp->un.value.num_channels == 1) {
353 cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = vol->left;
354 return(1);
355 }
356 else if (cp->un.value.num_channels == 2) {
357 cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = vol->left;
358 cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = vol->right;
359 return(1);
360 }
361 return(0);
362 }
363
364 int
365 wssopen(dev, flags)
366 dev_t dev;
367 int flags;
368 {
369 struct wss_softc *sc;
370 int unit = AUDIOUNIT(dev);
371
372 if (unit >= wss_cd.cd_ndevs)
373 return ENODEV;
374
375 sc = wss_cd.cd_devs[unit];
376 if (!sc)
377 return ENXIO;
378
379 return ad1848_open(&sc->sc_ad1848, dev, flags);
380 }
381
382 int
383 wss_getdev(addr, retp)
384 void *addr;
385 struct audio_device *retp;
386 {
387 *retp = wss_device;
388 return 0;
389 }
390
391 int
392 wss_setfd(addr, flag)
393 void *addr;
394 int flag;
395 {
396 /* Can't do full-duplex */
397 return(ENOTTY);
398 }
399
400
401 int
402 wss_set_out_port(addr, port)
403 void *addr;
404 int port;
405 {
406 DPRINTF(("wss_set_out_port:\n"));
407 return(EINVAL);
408 }
409
410 int
411 wss_get_out_port(addr)
412 void *addr;
413 {
414 DPRINTF(("wss_get_out_port:\n"));
415 return(WSS_DAC_LVL);
416 }
417
418 int
419 wss_set_in_port(addr, port)
420 void *addr;
421 int port;
422 {
423 register struct ad1848_softc *ac = addr;
424
425 DPRINTF(("wss_set_in_port: %d\n", port));
426
427 switch(port) {
428 case WSS_MIC_IN_LVL:
429 port = MIC_IN_PORT;
430 break;
431 case WSS_LINE_IN_LVL:
432 port = LINE_IN_PORT;
433 break;
434 case WSS_DAC_LVL:
435 port = DAC_IN_PORT;
436 break;
437 default:
438 return(EINVAL);
439 /*NOTREACHED*/
440 }
441
442 return(ad1848_set_rec_port(ac, port));
443 }
444
445 int
446 wss_get_in_port(addr)
447 void *addr;
448 {
449 register struct ad1848_softc *ac = addr;
450 int port = WSS_MIC_IN_LVL;
451
452 switch(ad1848_get_rec_port(ac)) {
453 case MIC_IN_PORT:
454 port = WSS_MIC_IN_LVL;
455 break;
456 case LINE_IN_PORT:
457 port = WSS_LINE_IN_LVL;
458 break;
459 case DAC_IN_PORT:
460 port = WSS_DAC_LVL;
461 break;
462 }
463
464 DPRINTF(("wss_get_in_port: %d\n", port));
465
466 return(port);
467 }
468
469 int
470 wss_mixer_set_port(addr, cp)
471 void *addr;
472 mixer_ctrl_t *cp;
473 {
474 register struct ad1848_softc *ac = addr;
475 register struct wss_softc *sc = ac->parent;
476 struct ad1848_volume vol;
477 int error = EINVAL;
478
479 DPRINTF(("wss_mixer_set_port: dev=%d type=%d\n", cp->dev, cp->type));
480
481 switch (cp->dev) {
482 case WSS_MIC_IN_LVL: /* Microphone */
483 if (cp->type == AUDIO_MIXER_VALUE) {
484 if (wss_to_vol(cp, &vol))
485 error = ad1848_set_aux2_gain(ac, &vol);
486 }
487 break;
488
489 case WSS_MIC_IN_MUTE: /* Microphone */
490 if (cp->type == AUDIO_MIXER_ENUM) {
491 sc->mic_mute = cp->un.ord;
492 DPRINTF(("mic mute %d\n", cp->un.ord));
493 error = 0;
494 }
495 break;
496
497 case WSS_LINE_IN_LVL: /* linein/CD */
498 if (cp->type == AUDIO_MIXER_VALUE) {
499 if (wss_to_vol(cp, &vol))
500 error = ad1848_set_aux1_gain(ac, &vol);
501 }
502 break;
503
504 case WSS_LINE_IN_MUTE: /* linein/CD */
505 if (cp->type == AUDIO_MIXER_ENUM) {
506 sc->cd_mute = cp->un.ord;
507 DPRINTF(("CD mute %d\n", cp->un.ord));
508 error = 0;
509 }
510 break;
511
512 case WSS_DAC_LVL: /* dac out */
513 if (cp->type == AUDIO_MIXER_VALUE) {
514 if (wss_to_vol(cp, &vol))
515 error = ad1848_set_out_gain(ac, &vol);
516 }
517 break;
518
519 case WSS_DAC_MUTE: /* dac out */
520 if (cp->type == AUDIO_MIXER_ENUM) {
521 sc->dac_mute = cp->un.ord;
522 DPRINTF(("DAC mute %d\n", cp->un.ord));
523 error = 0;
524 }
525 break;
526
527 case WSS_REC_LVL: /* record level */
528 if (cp->type == AUDIO_MIXER_VALUE) {
529 if (wss_to_vol(cp, &vol))
530 error = ad1848_set_rec_gain(ac, &vol);
531 }
532 break;
533
534 case WSS_RECORD_SOURCE:
535 if (cp->type == AUDIO_MIXER_ENUM) {
536 error = ad1848_set_rec_port(ac, cp->un.ord);
537 }
538 break;
539
540 case WSS_MON_LVL:
541 if (cp->type == AUDIO_MIXER_VALUE && cp->un.value.num_channels == 1) {
542 vol.left = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
543 error = ad1848_set_mon_gain(ac, &vol);
544 }
545 break;
546
547 default:
548 return ENXIO;
549 /*NOTREACHED*/
550 }
551
552 return 0;
553 }
554
555 int
556 wss_mixer_get_port(addr, cp)
557 void *addr;
558 mixer_ctrl_t *cp;
559 {
560 register struct ad1848_softc *ac = addr;
561 register struct wss_softc *sc = ac->parent;
562 struct ad1848_volume vol;
563 int error = EINVAL;
564
565 DPRINTF(("wss_mixer_get_port: port=%d\n", cp->dev));
566
567 switch (cp->dev) {
568 case WSS_MIC_IN_LVL: /* Microphone */
569 if (cp->type == AUDIO_MIXER_VALUE) {
570 error = ad1848_get_aux2_gain(ac, &vol);
571 if (!error)
572 wss_from_vol(cp, &vol);
573 }
574 break;
575
576 case WSS_MIC_IN_MUTE:
577 if (cp->type == AUDIO_MIXER_ENUM) {
578 cp->un.ord = sc->mic_mute;
579 error = 0;
580 }
581 break;
582
583 case WSS_LINE_IN_LVL: /* linein/CD */
584 if (cp->type == AUDIO_MIXER_VALUE) {
585 error = ad1848_get_aux1_gain(ac, &vol);
586 if (!error)
587 wss_from_vol(cp, &vol);
588 }
589 break;
590
591 case WSS_LINE_IN_MUTE:
592 if (cp->type == AUDIO_MIXER_ENUM) {
593 cp->un.ord = sc->cd_mute;
594 error = 0;
595 }
596 break;
597
598 case WSS_DAC_LVL: /* dac out */
599 if (cp->type == AUDIO_MIXER_VALUE) {
600 error = ad1848_get_out_gain(ac, &vol);
601 if (!error)
602 wss_from_vol(cp, &vol);
603 }
604 break;
605
606 case WSS_DAC_MUTE:
607 if (cp->type == AUDIO_MIXER_ENUM) {
608 cp->un.ord = sc->dac_mute;
609 error = 0;
610 }
611 break;
612
613 case WSS_REC_LVL: /* record level */
614 if (cp->type == AUDIO_MIXER_VALUE) {
615 error = ad1848_get_rec_gain(ac, &vol);
616 if (!error)
617 wss_from_vol(cp, &vol);
618 }
619 break;
620
621 case WSS_RECORD_SOURCE:
622 if (cp->type == AUDIO_MIXER_ENUM) {
623 cp->un.ord = ad1848_get_rec_port(ac);
624 error = 0;
625 }
626 break;
627
628 case WSS_MON_LVL: /* monitor level */
629 if (cp->type == AUDIO_MIXER_VALUE && cp->un.value.num_channels == 1) {
630 error = ad1848_get_mon_gain(ac, &vol);
631 if (!error)
632 cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = vol.left;
633 }
634 break;
635
636 default:
637 error = ENXIO;
638 break;
639 }
640
641 return(error);
642 }
643
644 int
645 wss_query_devinfo(addr, dip)
646 void *addr;
647 register mixer_devinfo_t *dip;
648 {
649 DPRINTF(("wss_query_devinfo: index=%d\n", dip->index));
650
651 switch(dip->index) {
652 case WSS_MIC_IN_LVL: /* Microphone */
653 dip->type = AUDIO_MIXER_VALUE;
654 dip->mixer_class = WSS_INPUT_CLASS;
655 dip->prev = AUDIO_MIXER_LAST;
656 dip->next = WSS_MIC_IN_MUTE;
657 strcpy(dip->label.name, AudioNmicrophone);
658 dip->un.v.num_channels = 2;
659 strcpy(dip->un.v.units.name, AudioNvolume);
660 break;
661
662 case WSS_LINE_IN_LVL: /* line/CD */
663 dip->type = AUDIO_MIXER_VALUE;
664 dip->mixer_class = WSS_INPUT_CLASS;
665 dip->prev = AUDIO_MIXER_LAST;
666 dip->next = WSS_LINE_IN_MUTE;
667 strcpy(dip->label.name, AudioNcd);
668 dip->un.v.num_channels = 2;
669 strcpy(dip->un.v.units.name, AudioNvolume);
670 break;
671
672 case WSS_DAC_LVL: /* dacout */
673 dip->type = AUDIO_MIXER_VALUE;
674 dip->mixer_class = WSS_INPUT_CLASS;
675 dip->prev = AUDIO_MIXER_LAST;
676 dip->next = WSS_DAC_MUTE;
677 strcpy(dip->label.name, AudioNdac);
678 dip->un.v.num_channels = 2;
679 strcpy(dip->un.v.units.name, AudioNvolume);
680 break;
681
682 case WSS_REC_LVL: /* record level */
683 dip->type = AUDIO_MIXER_VALUE;
684 dip->mixer_class = WSS_RECORD_CLASS;
685 dip->prev = AUDIO_MIXER_LAST;
686 dip->next = WSS_RECORD_SOURCE;
687 strcpy(dip->label.name, AudioNrecord);
688 dip->un.v.num_channels = 2;
689 strcpy(dip->un.v.units.name, AudioNvolume);
690 break;
691
692 case WSS_MON_LVL: /* monitor level */
693 dip->type = AUDIO_MIXER_VALUE;
694 dip->mixer_class = WSS_MONITOR_CLASS;
695 dip->next = dip->prev = AUDIO_MIXER_LAST;
696 strcpy(dip->label.name, AudioNmonitor);
697 dip->un.v.num_channels = 1;
698 strcpy(dip->un.v.units.name, AudioNvolume);
699 break;
700
701 case WSS_INPUT_CLASS: /* input class descriptor */
702 dip->type = AUDIO_MIXER_CLASS;
703 dip->mixer_class = WSS_INPUT_CLASS;
704 dip->next = dip->prev = AUDIO_MIXER_LAST;
705 strcpy(dip->label.name, AudioCInputs);
706 break;
707
708 case WSS_MONITOR_CLASS: /* monitor class descriptor */
709 dip->type = AUDIO_MIXER_CLASS;
710 dip->mixer_class = WSS_MONITOR_CLASS;
711 dip->next = dip->prev = AUDIO_MIXER_LAST;
712 strcpy(dip->label.name, AudioNmonitor);
713 break;
714
715 case WSS_RECORD_CLASS: /* record source class */
716 dip->type = AUDIO_MIXER_CLASS;
717 dip->mixer_class = WSS_RECORD_CLASS;
718 dip->next = dip->prev = AUDIO_MIXER_LAST;
719 strcpy(dip->label.name, AudioNrecord);
720 break;
721
722 case WSS_MIC_IN_MUTE:
723 dip->mixer_class = WSS_INPUT_CLASS;
724 dip->type = AUDIO_MIXER_ENUM;
725 dip->prev = WSS_MIC_IN_LVL;
726 dip->next = AUDIO_MIXER_LAST;
727 goto mute;
728
729 case WSS_LINE_IN_MUTE:
730 dip->mixer_class = WSS_INPUT_CLASS;
731 dip->type = AUDIO_MIXER_ENUM;
732 dip->prev = WSS_LINE_IN_LVL;
733 dip->next = AUDIO_MIXER_LAST;
734 goto mute;
735
736 case WSS_DAC_MUTE:
737 dip->mixer_class = WSS_INPUT_CLASS;
738 dip->type = AUDIO_MIXER_ENUM;
739 dip->prev = WSS_DAC_LVL;
740 dip->next = AUDIO_MIXER_LAST;
741 mute:
742 strcpy(dip->label.name, AudioNmute);
743 dip->un.e.num_mem = 2;
744 strcpy(dip->un.e.member[0].label.name, AudioNoff);
745 dip->un.e.member[0].ord = 0;
746 strcpy(dip->un.e.member[1].label.name, AudioNon);
747 dip->un.e.member[1].ord = 1;
748 break;
749
750 case WSS_RECORD_SOURCE:
751 dip->mixer_class = WSS_RECORD_CLASS;
752 dip->type = AUDIO_MIXER_ENUM;
753 dip->prev = WSS_REC_LVL;
754 dip->next = AUDIO_MIXER_LAST;
755 strcpy(dip->label.name, AudioNsource);
756 dip->un.e.num_mem = 3;
757 strcpy(dip->un.e.member[0].label.name, AudioNmicrophone);
758 dip->un.e.member[0].ord = WSS_MIC_IN_LVL;
759 strcpy(dip->un.e.member[1].label.name, AudioNcd);
760 dip->un.e.member[1].ord = WSS_LINE_IN_LVL;
761 strcpy(dip->un.e.member[2].label.name, AudioNdac);
762 dip->un.e.member[2].ord = WSS_DAC_LVL;
763 break;
764
765 default:
766 return ENXIO;
767 /*NOTREACHED*/
768 }
769 DPRINTF(("AUDIO_MIXER_DEVINFO: name=%s\n", dip->label.name));
770
771 return 0;
772 }
773
774 /*
775 * Initialization code for OPTi MAD16 compatible audio chips. Including
776 *
777 * OPTi 82C928 MAD16 (replaced by C929)
778 * OAK OTI-601D Mozart
779 * OPTi 82C929 MAD16 Pro
780 *
781 */
782 static unsigned int mad_read __P((struct wss_softc *, int, int));
783 static void mad_write __P((struct wss_softc *, int, int, int));
784 static int detect_mad16 __P((struct wss_softc *, int));
785
786 static unsigned int
787 mad_read(sc, chip_type, port)
788 struct wss_softc *sc;
789 int chip_type;
790 int port;
791 {
792 unsigned int tmp;
793 int s = splaudio(); /* don't want an interrupt between outb&inb */
794
795 switch (chip_type) { /* Output password */
796 case MAD_82C928:
797 case MAD_OTI601D:
798 bus_space_write_1(sc->sc_iot, sc->sc_mad_ioh, MC_PASSWD_REG, M_PASSWD_928);
799 break;
800 case MAD_82C929:
801 bus_space_write_1(sc->sc_iot, sc->sc_mad_ioh, MC_PASSWD_REG, M_PASSWD_929);
802 break;
803 }
804 tmp = bus_space_read_1(sc->sc_iot, sc->sc_mad_ioh, port);
805 splx(s);
806 return tmp;
807 }
808
809 static void
810 mad_write(sc, chip_type, port, value)
811 struct wss_softc *sc;
812 int chip_type;
813 int port;
814 int value;
815 {
816 int s = splaudio(); /* don't want an interrupt between outb&outb */
817
818 switch (chip_type) { /* Output password */
819 case MAD_82C928:
820 case MAD_OTI601D:
821 bus_space_write_1(sc->sc_iot, sc->sc_mad_ioh, MC_PASSWD_REG, M_PASSWD_928);
822 break;
823 case MAD_82C929:
824 bus_space_write_1(sc->sc_iot, sc->sc_mad_ioh, MC_PASSWD_REG, M_PASSWD_929);
825 break;
826 }
827 bus_space_write_1(sc->sc_iot, sc->sc_mad_ioh, port, value & 0xff);
828 splx(s);
829 }
830
831 static int
832 detect_mad16(sc, chip_type)
833 struct wss_softc *sc;
834 int chip_type;
835 {
836 unsigned char tmp, tmp2;
837
838 /*
839 * Check that reading a register doesn't return bus float (0xff)
840 * when the card is accessed using password. This may fail in case
841 * the card is in low power mode. Normally at least the power saving mode
842 * bit should be 0.
843 */
844 if ((tmp = mad_read(sc, chip_type, MC1_PORT)) == 0xff) {
845 DPRINTF(("MC1_PORT returned 0xff\n"));
846 return 0;
847 }
848
849 /*
850 * Now check that the gate is closed on first I/O after writing
851 * the password. (This is how a MAD16 compatible card works).
852 */
853 if ((tmp2 = bus_space_read_1(sc->sc_iot, sc->sc_mad_ioh, MC1_PORT)) == tmp) { /* It didn't close */
854 DPRINTF(("MC1_PORT didn't close after read (0x%02x)\n", tmp2));
855 return 0;
856 }
857
858 mad_write(sc, chip_type, MC1_PORT, tmp ^ 0x80); /* Toggle a bit */
859
860 /* Compare the bit */
861 if ((tmp2 = mad_read(sc, chip_type, MC1_PORT)) != (tmp ^ 0x80)) {
862 mad_write(sc, chip_type, MC1_PORT, tmp); /* Restore */
863 DPRINTF(("Bit revert test failed (0x%02x, 0x%02x)\n", tmp, tmp2));
864 return 0;
865 }
866
867 mad_write(sc, chip_type, MC1_PORT, tmp); /* Restore */
868 return 1;
869 }
870
871 static int
872 madprobe(sc, iobase)
873 struct wss_softc *sc;
874 int iobase;
875 {
876 static int valid_ports[M_WSS_NPORTS] =
877 { M_WSS_PORT0, M_WSS_PORT1, M_WSS_PORT2, M_WSS_PORT3 };
878 int i;
879 int chip_type;
880
881 if (bus_space_map(sc->sc_iot, MAD_BASE, MAD_NPORT, 0, &sc->sc_mad_ioh))
882 return MAD_NONE;
883
884 DPRINTF(("mad: Detect using password = 0xE2\n"));
885 if (!detect_mad16(sc, MAD_82C928)) {
886 /* No luck. Try different model */
887 DPRINTF(("mad: Detect using password = 0xE3\n"));
888 if (!detect_mad16(sc, MAD_82C929))
889 goto bad;
890 chip_type = MAD_82C929;
891 DPRINTF(("mad: 82C929 detected\n"));
892 } else {
893 if ((mad_read(sc, MAD_82C928, MC3_PORT) & 0x03) == 0x03) {
894 DPRINTF(("mad: Mozart detected\n"));
895 chip_type = MAD_OTI601D;
896 } else {
897 DPRINTF(("mad: 82C928 detected?\n"));
898 chip_type = MAD_82C928;
899 }
900 }
901
902 #ifdef AUDIO_DEBUG
903 for (i = MC1_PORT; i <= MC7_PORT; i++)
904 printf("mad: port %03x = %02x\n", i, mad_read(sc, chip_type, i));
905 #endif
906
907 /* Set the WSS address. */
908 for (i = 0; i < 4; i++)
909 if (valid_ports[i] == iobase)
910 break;
911 if (i > 3) { /* Not a valid port */
912 printf("mad: Bad WSS base address 0x%x\n", iobase);
913 goto bad;
914 }
915 /* enable WSS emulation at the I/O port, keep joystck */
916 mad_write(sc, chip_type, MC1_PORT, M_WSS_PORT_SELECT(i));
917
918 mad_write(sc, chip_type, MC2_PORT, 0x03); /* ? */
919 mad_write(sc, chip_type, MC3_PORT, 0xf0); /* Disable SB */
920
921 return chip_type;
922 bad:
923 bus_space_unmap(sc->sc_iot, sc->sc_mad_ioh, MAD_NPORT);
924 return MAD_NONE;
925 }
926
927 static void
928 madprobedone(sc)
929 struct wss_softc *sc;
930 {
931 int chip_type = sc->mad_chip_type;
932 unsigned char cs4231_mode;
933
934 cs4231_mode =
935 strncmp(sc->sc_ad1848.chip_name, "CS4248", 6) == 0 ||
936 strncmp(sc->sc_ad1848.chip_name, "CS4231", 6) == 0 ? 0x02 : 0;
937
938 if (chip_type == MAD_82C929) {
939 mad_write(sc, chip_type, MC4_PORT, 0xa2);
940 mad_write(sc, chip_type, MC5_PORT, 0xA5 | cs4231_mode);
941 mad_write(sc, chip_type, MC6_PORT, 0x03); /* Disable MPU401 */
942 } else {
943 mad_write(sc, chip_type, MC4_PORT, 0x02);
944 mad_write(sc, chip_type, MC5_PORT, 0x30 | cs4231_mode);
945 }
946
947 #ifdef AUDIO_DEBUG
948 if (wssdebug) {
949 int i;
950 for (i = MC1_PORT; i <= MC7_PORT; i++)
951 DPRINTF(("port %03x after init = %02x\n", i, mad_read(sc, chip_type, i)));
952 }
953 #endif
954 }
955