wss.c revision 1.43 1 /* $NetBSD: wss.c,v 1.43 1998/02/23 14:12:18 drochner 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 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the Computer Systems
19 * Engineering Group at Lawrence Berkeley Laboratory.
20 * 4. Neither the name of the University nor of the Laboratory may be used
21 * to endorse or promote products derived from this software without
22 * specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 */
37
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/errno.h>
41 #include <sys/ioctl.h>
42 #include <sys/syslog.h>
43 #include <sys/device.h>
44 #include <sys/proc.h>
45 #include <sys/buf.h>
46
47 #include <machine/cpu.h>
48 #include <machine/intr.h>
49 #include <machine/bus.h>
50
51 #include <sys/audioio.h>
52 #include <dev/audio_if.h>
53
54 #include <dev/isa/isavar.h>
55 #include <dev/isa/isadmavar.h>
56
57 #include <dev/ic/ad1848reg.h>
58 #include <dev/isa/ad1848var.h>
59 #include <dev/isa/wssreg.h>
60 #include <dev/isa/wssvar.h>
61 #include <dev/isa/madreg.h>
62
63 #ifdef AUDIO_DEBUG
64 #define DPRINTF(x) if (wssdebug) printf x
65 int wssdebug = 0;
66 #else
67 #define DPRINTF(x)
68 #endif
69
70 struct audio_device wss_device = {
71 "wss,ad1848",
72 "",
73 "WSS"
74 };
75
76 int wss_getdev __P((void *, struct audio_device *));
77
78 int wss_mixer_set_port __P((void *, mixer_ctrl_t *));
79 int wss_mixer_get_port __P((void *, mixer_ctrl_t *));
80 int wss_query_devinfo __P((void *, mixer_devinfo_t *));
81
82 static int wss_to_vol __P((mixer_ctrl_t *, struct ad1848_volume *));
83 static int wss_from_vol __P((mixer_ctrl_t *, struct ad1848_volume *));
84
85 /*
86 * Define our interface to the higher level audio driver.
87 */
88
89 struct audio_hw_if wss_hw_if = {
90 ad1848_open,
91 ad1848_close,
92 NULL,
93 ad1848_query_encoding,
94 ad1848_set_params,
95 ad1848_round_blocksize,
96 ad1848_commit_settings,
97 ad1848_dma_init_output,
98 ad1848_dma_init_input,
99 ad1848_dma_output,
100 ad1848_dma_input,
101 ad1848_halt_out_dma,
102 ad1848_halt_in_dma,
103 NULL,
104 wss_getdev,
105 NULL,
106 wss_mixer_set_port,
107 wss_mixer_get_port,
108 wss_query_devinfo,
109 ad1848_malloc,
110 ad1848_free,
111 ad1848_round,
112 ad1848_mappage,
113 ad1848_get_props,
114 };
115
116 /*
117 * Attach hardware to driver, attach hardware driver to audio
118 * pseudo-device driver .
119 */
120 void
121 wssattach(sc)
122 struct wss_softc *sc;
123 {
124 int version;
125
126 madattach(sc);
127
128 sc->sc_ih = isa_intr_establish(sc->sc_ic, sc->wss_irq, IST_EDGE, IPL_AUDIO,
129 ad1848_intr, &sc->sc_ad1848);
130
131 ad1848_attach(&sc->sc_ad1848);
132
133 version = bus_space_read_1(sc->sc_iot, sc->sc_ioh, WSS_STATUS) & WSS_VERSMASK;
134 printf(" (vers %d)", version);
135 switch(sc->mad_chip_type) {
136 case MAD_82C928:
137 printf(", 82C928");
138 break;
139 case MAD_OTI601D:
140 printf(", OTI-601D");
141 break;
142 case MAD_82C929:
143 printf(", 82C929");
144 break;
145 case MAD_82C931:
146 printf(", 82C931");
147 break;
148 default:
149 break;
150 }
151 printf("\n");
152
153 sc->sc_ad1848.parent = sc;
154
155 audio_attach_mi(&wss_hw_if, 0, &sc->sc_ad1848, &sc->sc_dev);
156 }
157
158 static int
159 wss_to_vol(cp, vol)
160 mixer_ctrl_t *cp;
161 struct ad1848_volume *vol;
162 {
163 if (cp->un.value.num_channels == 1) {
164 vol->left = vol->right = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
165 return(1);
166 }
167 else if (cp->un.value.num_channels == 2) {
168 vol->left = cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
169 vol->right = cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
170 return(1);
171 }
172 return(0);
173 }
174
175 static int
176 wss_from_vol(cp, vol)
177 mixer_ctrl_t *cp;
178 struct ad1848_volume *vol;
179 {
180 if (cp->un.value.num_channels == 1) {
181 cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = vol->left;
182 return(1);
183 }
184 else if (cp->un.value.num_channels == 2) {
185 cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = vol->left;
186 cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = vol->right;
187 return(1);
188 }
189 return(0);
190 }
191
192 int
193 wss_getdev(addr, retp)
194 void *addr;
195 struct audio_device *retp;
196 {
197 *retp = wss_device;
198 return 0;
199 }
200
201 int
202 wss_mixer_set_port(addr, cp)
203 void *addr;
204 mixer_ctrl_t *cp;
205 {
206 struct ad1848_softc *ac = addr;
207 struct wss_softc *sc = ac->parent;
208 struct ad1848_volume vol;
209 int error = EINVAL;
210
211 DPRINTF(("wss_mixer_set_port: dev=%d type=%d\n", cp->dev, cp->type));
212
213 switch (cp->dev) {
214 case WSS_MIC_IN_LVL: /* Microphone */
215 if (cp->type == AUDIO_MIXER_VALUE) {
216 if (wss_to_vol(cp, &vol))
217 error = ad1848_set_aux2_gain(ac, &vol);
218 }
219 break;
220
221 case WSS_MIC_IN_MUTE: /* Microphone */
222 if (cp->type == AUDIO_MIXER_ENUM) {
223 sc->mic_mute = cp->un.ord;
224 DPRINTF(("mic mute %d\n", cp->un.ord));
225 error = 0;
226 }
227 break;
228
229 case WSS_LINE_IN_LVL: /* linein/CD */
230 if (cp->type == AUDIO_MIXER_VALUE) {
231 if (wss_to_vol(cp, &vol))
232 error = ad1848_set_aux1_gain(ac, &vol);
233 }
234 break;
235
236 case WSS_LINE_IN_MUTE: /* linein/CD */
237 if (cp->type == AUDIO_MIXER_ENUM) {
238 sc->cd_mute = cp->un.ord;
239 DPRINTF(("CD mute %d\n", cp->un.ord));
240 error = 0;
241 }
242 break;
243
244 case WSS_DAC_LVL: /* dac out */
245 if (cp->type == AUDIO_MIXER_VALUE) {
246 if (wss_to_vol(cp, &vol))
247 error = ad1848_set_out_gain(ac, &vol);
248 }
249 break;
250
251 case WSS_DAC_MUTE: /* dac out */
252 if (cp->type == AUDIO_MIXER_ENUM) {
253 sc->dac_mute = cp->un.ord;
254 DPRINTF(("DAC mute %d\n", cp->un.ord));
255 error = 0;
256 }
257 break;
258
259 case WSS_REC_LVL: /* record level */
260 if (cp->type == AUDIO_MIXER_VALUE) {
261 if (wss_to_vol(cp, &vol))
262 error = ad1848_set_rec_gain(ac, &vol);
263 }
264 break;
265
266 case WSS_RECORD_SOURCE:
267 if (cp->type == AUDIO_MIXER_ENUM) {
268 error = ad1848_set_rec_port(ac, cp->un.ord);
269 }
270 break;
271
272 case WSS_MON_LVL:
273 if (cp->type == AUDIO_MIXER_VALUE && cp->un.value.num_channels == 1) {
274 vol.left = cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
275 error = ad1848_set_mon_gain(ac, &vol);
276 }
277 break;
278
279 default:
280 return ENXIO;
281 /*NOTREACHED*/
282 }
283
284 return 0;
285 }
286
287 int
288 wss_mixer_get_port(addr, cp)
289 void *addr;
290 mixer_ctrl_t *cp;
291 {
292 struct ad1848_softc *ac = addr;
293 struct wss_softc *sc = ac->parent;
294 struct ad1848_volume vol;
295 int error = EINVAL;
296
297 DPRINTF(("wss_mixer_get_port: port=%d\n", cp->dev));
298
299 switch (cp->dev) {
300 case WSS_MIC_IN_LVL: /* Microphone */
301 if (cp->type == AUDIO_MIXER_VALUE) {
302 error = ad1848_get_aux2_gain(ac, &vol);
303 if (!error)
304 wss_from_vol(cp, &vol);
305 }
306 break;
307
308 case WSS_MIC_IN_MUTE:
309 if (cp->type == AUDIO_MIXER_ENUM) {
310 cp->un.ord = sc->mic_mute;
311 error = 0;
312 }
313 break;
314
315 case WSS_LINE_IN_LVL: /* linein/CD */
316 if (cp->type == AUDIO_MIXER_VALUE) {
317 error = ad1848_get_aux1_gain(ac, &vol);
318 if (!error)
319 wss_from_vol(cp, &vol);
320 }
321 break;
322
323 case WSS_LINE_IN_MUTE:
324 if (cp->type == AUDIO_MIXER_ENUM) {
325 cp->un.ord = sc->cd_mute;
326 error = 0;
327 }
328 break;
329
330 case WSS_DAC_LVL: /* dac out */
331 if (cp->type == AUDIO_MIXER_VALUE) {
332 error = ad1848_get_out_gain(ac, &vol);
333 if (!error)
334 wss_from_vol(cp, &vol);
335 }
336 break;
337
338 case WSS_DAC_MUTE:
339 if (cp->type == AUDIO_MIXER_ENUM) {
340 cp->un.ord = sc->dac_mute;
341 error = 0;
342 }
343 break;
344
345 case WSS_REC_LVL: /* record level */
346 if (cp->type == AUDIO_MIXER_VALUE) {
347 error = ad1848_get_rec_gain(ac, &vol);
348 if (!error)
349 wss_from_vol(cp, &vol);
350 }
351 break;
352
353 case WSS_RECORD_SOURCE:
354 if (cp->type == AUDIO_MIXER_ENUM) {
355 cp->un.ord = ad1848_get_rec_port(ac);
356 error = 0;
357 }
358 break;
359
360 case WSS_MON_LVL: /* monitor level */
361 if (cp->type == AUDIO_MIXER_VALUE && cp->un.value.num_channels == 1) {
362 error = ad1848_get_mon_gain(ac, &vol);
363 if (!error)
364 cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] = vol.left;
365 }
366 break;
367
368 default:
369 error = ENXIO;
370 break;
371 }
372
373 return(error);
374 }
375
376 int
377 wss_query_devinfo(addr, dip)
378 void *addr;
379 mixer_devinfo_t *dip;
380 {
381 DPRINTF(("wss_query_devinfo: index=%d\n", dip->index));
382
383 switch(dip->index) {
384 case WSS_MIC_IN_LVL: /* Microphone */
385 dip->type = AUDIO_MIXER_VALUE;
386 dip->mixer_class = WSS_INPUT_CLASS;
387 dip->prev = AUDIO_MIXER_LAST;
388 dip->next = WSS_MIC_IN_MUTE;
389 strcpy(dip->label.name, AudioNmicrophone);
390 dip->un.v.num_channels = 2;
391 strcpy(dip->un.v.units.name, AudioNvolume);
392 break;
393
394 case WSS_LINE_IN_LVL: /* line/CD */
395 dip->type = AUDIO_MIXER_VALUE;
396 dip->mixer_class = WSS_INPUT_CLASS;
397 dip->prev = AUDIO_MIXER_LAST;
398 dip->next = WSS_LINE_IN_MUTE;
399 strcpy(dip->label.name, AudioNcd);
400 dip->un.v.num_channels = 2;
401 strcpy(dip->un.v.units.name, AudioNvolume);
402 break;
403
404 case WSS_DAC_LVL: /* dacout */
405 dip->type = AUDIO_MIXER_VALUE;
406 dip->mixer_class = WSS_INPUT_CLASS;
407 dip->prev = AUDIO_MIXER_LAST;
408 dip->next = WSS_DAC_MUTE;
409 strcpy(dip->label.name, AudioNdac);
410 dip->un.v.num_channels = 2;
411 strcpy(dip->un.v.units.name, AudioNvolume);
412 break;
413
414 case WSS_REC_LVL: /* record level */
415 dip->type = AUDIO_MIXER_VALUE;
416 dip->mixer_class = WSS_RECORD_CLASS;
417 dip->prev = AUDIO_MIXER_LAST;
418 dip->next = WSS_RECORD_SOURCE;
419 strcpy(dip->label.name, AudioNrecord);
420 dip->un.v.num_channels = 2;
421 strcpy(dip->un.v.units.name, AudioNvolume);
422 break;
423
424 case WSS_MON_LVL: /* monitor level */
425 dip->type = AUDIO_MIXER_VALUE;
426 dip->mixer_class = WSS_MONITOR_CLASS;
427 dip->next = dip->prev = AUDIO_MIXER_LAST;
428 strcpy(dip->label.name, AudioNmonitor);
429 dip->un.v.num_channels = 1;
430 strcpy(dip->un.v.units.name, AudioNvolume);
431 break;
432
433 case WSS_INPUT_CLASS: /* input class descriptor */
434 dip->type = AUDIO_MIXER_CLASS;
435 dip->mixer_class = WSS_INPUT_CLASS;
436 dip->next = dip->prev = AUDIO_MIXER_LAST;
437 strcpy(dip->label.name, AudioCinputs);
438 break;
439
440 case WSS_MONITOR_CLASS: /* monitor class descriptor */
441 dip->type = AUDIO_MIXER_CLASS;
442 dip->mixer_class = WSS_MONITOR_CLASS;
443 dip->next = dip->prev = AUDIO_MIXER_LAST;
444 strcpy(dip->label.name, AudioCmonitor);
445 break;
446
447 case WSS_RECORD_CLASS: /* record source class */
448 dip->type = AUDIO_MIXER_CLASS;
449 dip->mixer_class = WSS_RECORD_CLASS;
450 dip->next = dip->prev = AUDIO_MIXER_LAST;
451 strcpy(dip->label.name, AudioCrecord);
452 break;
453
454 case WSS_MIC_IN_MUTE:
455 dip->mixer_class = WSS_INPUT_CLASS;
456 dip->type = AUDIO_MIXER_ENUM;
457 dip->prev = WSS_MIC_IN_LVL;
458 dip->next = AUDIO_MIXER_LAST;
459 goto mute;
460
461 case WSS_LINE_IN_MUTE:
462 dip->mixer_class = WSS_INPUT_CLASS;
463 dip->type = AUDIO_MIXER_ENUM;
464 dip->prev = WSS_LINE_IN_LVL;
465 dip->next = AUDIO_MIXER_LAST;
466 goto mute;
467
468 case WSS_DAC_MUTE:
469 dip->mixer_class = WSS_INPUT_CLASS;
470 dip->type = AUDIO_MIXER_ENUM;
471 dip->prev = WSS_DAC_LVL;
472 dip->next = AUDIO_MIXER_LAST;
473 mute:
474 strcpy(dip->label.name, AudioNmute);
475 dip->un.e.num_mem = 2;
476 strcpy(dip->un.e.member[0].label.name, AudioNoff);
477 dip->un.e.member[0].ord = 0;
478 strcpy(dip->un.e.member[1].label.name, AudioNon);
479 dip->un.e.member[1].ord = 1;
480 break;
481
482 case WSS_RECORD_SOURCE:
483 dip->mixer_class = WSS_RECORD_CLASS;
484 dip->type = AUDIO_MIXER_ENUM;
485 dip->prev = WSS_REC_LVL;
486 dip->next = AUDIO_MIXER_LAST;
487 strcpy(dip->label.name, AudioNsource);
488 dip->un.e.num_mem = 3;
489 strcpy(dip->un.e.member[0].label.name, AudioNmicrophone);
490 dip->un.e.member[0].ord = WSS_MIC_IN_LVL;
491 strcpy(dip->un.e.member[1].label.name, AudioNcd);
492 dip->un.e.member[1].ord = WSS_LINE_IN_LVL;
493 strcpy(dip->un.e.member[2].label.name, AudioNdac);
494 dip->un.e.member[2].ord = WSS_DAC_LVL;
495 break;
496
497 default:
498 return ENXIO;
499 /*NOTREACHED*/
500 }
501 DPRINTF(("AUDIO_MIXER_DEVINFO: name=%s\n", dip->label.name));
502
503 return 0;
504 }
505
506
507 /*
508 * Copyright by Hannu Savolainen 1994
509 *
510 * Redistribution and use in source and binary forms, with or without
511 * modification, are permitted provided that the following conditions are
512 * met: 1. Redistributions of source code must retain the above copyright
513 * notice, this list of conditions and the following disclaimer. 2.
514 * Redistributions in binary form must reproduce the above copyright notice,
515 * this list of conditions and the following disclaimer in the documentation
516 * and/or other materials provided with the distribution.
517 *
518 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
519 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
520 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
521 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
522 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
523 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
524 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
525 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
526 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
527 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
528 * SUCH DAMAGE.
529 *
530 */
531 /*
532 * Initialization code for OPTi MAD16 compatible audio chips. Including
533 *
534 * OPTi 82C928 MAD16 (replaced by C929)
535 * OAK OTI-601D Mozart
536 * OPTi 82C929 MAD16 Pro
537 *
538 */
539
540 u_int
541 mad_read(sc, port)
542 struct wss_softc *sc;
543 int port;
544 {
545 u_int tmp;
546 int pwd;
547 int s;
548
549 switch (sc->mad_chip_type) { /* Output password */
550 case MAD_82C928:
551 case MAD_OTI601D:
552 pwd = M_PASSWD_928;
553 break;
554 case MAD_82C929:
555 pwd = M_PASSWD_929;
556 break;
557 case MAD_82C931:
558 pwd = M_PASSWD_931;
559 break;
560 default:
561 panic("mad_read: Bad chip type=%d\n", sc->mad_chip_type);
562 }
563 s = splaudio(); /* don't want an interrupt between outb&inb */
564 bus_space_write_1(sc->sc_iot, sc->mad_ioh, MC_PASSWD_REG, pwd);
565 tmp = bus_space_read_1(sc->sc_iot, sc->mad_ioh, port);
566 splx(s);
567 return tmp;
568 }
569
570 void
571 mad_write(sc, port, value)
572 struct wss_softc *sc;
573 int port;
574 int value;
575 {
576 int pwd;
577 int s;
578
579 switch (sc->mad_chip_type) { /* Output password */
580 case MAD_82C928:
581 case MAD_OTI601D:
582 pwd = M_PASSWD_928;
583 break;
584 case MAD_82C929:
585 pwd = M_PASSWD_929;
586 break;
587 case MAD_82C931:
588 pwd = M_PASSWD_931;
589 break;
590 default:
591 panic("mad_write: Bad chip type=%d\n", sc->mad_chip_type);
592 }
593 s = splaudio();
594 bus_space_write_1(sc->sc_iot, sc->mad_ioh, MC_PASSWD_REG, pwd);
595 bus_space_write_1(sc->sc_iot, sc->mad_ioh, port, value & 0xff);
596 splx(s);
597 }
598
599 void
600 madattach(sc)
601 struct wss_softc *sc;
602 {
603 unsigned char cs4231_mode;
604 int joy;
605
606 if (sc->mad_chip_type == MAD_NONE)
607 return;
608
609 /* Do we want the joystick disabled? */
610 joy = sc->sc_dev.dv_cfdata->cf_flags & 2 ? MC1_JOYDISABLE : 0;
611
612 /* enable WSS emulation at the I/O port */
613 mad_write(sc, MC1_PORT, M_WSS_PORT_SELECT(sc->mad_ioindex) | joy);
614 mad_write(sc, MC2_PORT, 0x03); /* ? */
615 mad_write(sc, MC3_PORT, 0xf0); /* Disable SB */
616
617 cs4231_mode =
618 strncmp(sc->sc_ad1848.chip_name, "CS4248", 6) == 0 ||
619 strncmp(sc->sc_ad1848.chip_name, "CS4231", 6) == 0 ? 0x02 : 0;
620
621 if (sc->mad_chip_type == MAD_82C929) {
622 mad_write(sc, MC4_PORT, 0x92);
623 mad_write(sc, MC5_PORT, 0xA5 | cs4231_mode);
624 mad_write(sc, MC6_PORT, 0x03); /* Disable MPU401 */
625 } else {
626 mad_write(sc, MC4_PORT, 0x02);
627 mad_write(sc, MC5_PORT, 0x30 | cs4231_mode);
628 }
629
630 #ifdef AUDIO_DEBUG
631 if (wssdebug) {
632 int i;
633 for (i = MC1_PORT; i <= MC7_PORT; i++)
634 DPRINTF(("port %03x after init = %02x\n", i, mad_read(sc, i)));
635 }
636 #endif
637 }
638