1 /* $OpenBSD: wss.c,v 1.22 2003/04/27 11:22:53 ho Exp $ */
2 /* $NetBSD: wss.c,v 1.42 1998/01/19 22:18:23 augustss Exp $ */
3
4 /*
5 * Copyright (c) 1994 John Brezak
6 * Copyright (c) 1991-1993 Regents of the University of California.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed by the Computer Systems
20 * Engineering Group at Lawrence Berkeley Laboratory.
21 * 4. Neither the name of the University nor of the Laboratory may be used
22 * to endorse or promote products derived from this software without
23 * specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 *
37 */
38
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/errno.h>
42 #include <sys/ioctl.h>
43 #include <sys/syslog.h>
44 #include <sys/device.h>
45 #include <sys/proc.h>
46 #include <sys/buf.h>
47
48 #include <machine/cpu.h>
49 #include <machine/intr.h>
50 #include <machine/bus.h>
51
52 #include <sys/audioio.h>
53 #include <dev/audio_if.h>
54
55 #include <dev/isa/isavar.h>
56 #include <dev/isa/isadmavar.h>
57
58 #include <dev/ic/ad1848reg.h>
59 #include <dev/isa/ad1848var.h>
60 #include <dev/isa/wssreg.h>
61 #include <dev/isa/wssvar.h>
62 #include <dev/isa/madreg.h>
63
64 #ifdef AUDIO_DEBUG
65 #define DPRINTF(x) if (wssdebug) printf x
66 int wssdebug = 0;
67 #else
68 #define DPRINTF(x)
69 #endif
70
71 struct audio_device wss_device = {
72 "wss,ad1848",
73 "",
74 "WSS"
75 };
76
77 int wss_getdev(void *, struct audio_device *);
78
79 int wss_mixer_set_port(void *, mixer_ctrl_t *);
80 int wss_mixer_get_port(void *, mixer_ctrl_t *);
81 int wss_query_devinfo(void *, mixer_devinfo_t *);
82
83 /*
84 * Define our interface to the higher level audio driver.
85 */
86
87 struct audio_hw_if wss_hw_if = {
88 ad1848_open,
89 ad1848_close,
90 NULL,
91 ad1848_query_encoding,
92 ad1848_set_params,
93 ad1848_round_blocksize,
94 ad1848_commit_settings,
95 ad1848_dma_init_output,
96 ad1848_dma_init_input,
97 ad1848_dma_output,
98 ad1848_dma_input,
99 ad1848_halt_out_dma,
100 ad1848_halt_in_dma,
101 NULL,
102 wss_getdev,
103 NULL,
104 wss_mixer_set_port,
105 wss_mixer_get_port,
106 wss_query_devinfo,
107 ad1848_malloc,
108 ad1848_free,
109 ad1848_round,
110 ad1848_mappage,
111 ad1848_get_props,
112 NULL,
113 NULL
114 };
115
116 /*
117 * Attach hardware to driver, attach hardware driver to audio
118 * pseudo-device driver .
119 */
120 void
wssattach(sc)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, sc->sc_dev.dv_xname);
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, &sc->sc_ad1848, &sc->sc_dev);
156 }
157
158 int
wss_getdev(addr,retp)159 wss_getdev(addr, retp)
160 void *addr;
161 struct audio_device *retp;
162 {
163 *retp = wss_device;
164 return 0;
165 }
166
167 static ad1848_devmap_t mappings[] = {
168 { WSS_MIC_IN_LVL, AD1848_KIND_LVL, AD1848_AUX2_CHANNEL },
169 { WSS_LINE_IN_LVL, AD1848_KIND_LVL, AD1848_AUX1_CHANNEL },
170 { WSS_DAC_LVL, AD1848_KIND_LVL, AD1848_DAC_CHANNEL },
171 { WSS_MON_LVL, AD1848_KIND_LVL, AD1848_MONO_CHANNEL },
172 { WSS_MIC_IN_MUTE, AD1848_KIND_MUTE, AD1848_AUX2_CHANNEL },
173 { WSS_LINE_IN_MUTE, AD1848_KIND_MUTE, AD1848_AUX1_CHANNEL },
174 { WSS_DAC_MUTE, AD1848_KIND_MUTE, AD1848_DAC_CHANNEL },
175 { WSS_REC_LVL, AD1848_KIND_RECORDGAIN, -1 },
176 { WSS_RECORD_SOURCE, AD1848_KIND_RECORDSOURCE, -1}
177 };
178
179 static int nummap = sizeof(mappings) / sizeof(mappings[0]);
180
181 int
wss_mixer_set_port(addr,cp)182 wss_mixer_set_port(addr, cp)
183 void *addr;
184 mixer_ctrl_t *cp;
185 {
186 struct ad1848_softc *ac = addr;
187
188 return (ad1848_mixer_set_port(ac, mappings, nummap, cp));
189 }
190
191 int
wss_mixer_get_port(addr,cp)192 wss_mixer_get_port(addr, cp)
193 void *addr;
194 mixer_ctrl_t *cp;
195 {
196 struct ad1848_softc *ac = addr;
197
198 return (ad1848_mixer_get_port(ac, mappings, nummap, cp));
199 }
200
201 int
wss_query_devinfo(addr,dip)202 wss_query_devinfo(addr, dip)
203 void *addr;
204 mixer_devinfo_t *dip;
205 {
206 DPRINTF(("wss_query_devinfo: index=%d\n", dip->index));
207
208 switch(dip->index) {
209 case WSS_MIC_IN_LVL: /* Microphone */
210 dip->type = AUDIO_MIXER_VALUE;
211 dip->mixer_class = WSS_INPUT_CLASS;
212 dip->prev = AUDIO_MIXER_LAST;
213 dip->next = WSS_MIC_IN_MUTE;
214 strlcpy(dip->label.name, AudioNmicrophone, sizeof dip->label.name);
215 dip->un.v.num_channels = 2;
216 strlcpy(dip->un.v.units.name, AudioNvolume, sizeof dip->un.v.units.name);
217 break;
218
219 case WSS_LINE_IN_LVL: /* line/CD */
220 dip->type = AUDIO_MIXER_VALUE;
221 dip->mixer_class = WSS_INPUT_CLASS;
222 dip->prev = AUDIO_MIXER_LAST;
223 dip->next = WSS_LINE_IN_MUTE;
224 strlcpy(dip->label.name, AudioNcd, sizeof dip->label.name);
225 dip->un.v.num_channels = 2;
226 strlcpy(dip->un.v.units.name, AudioNvolume, sizeof dip->un.v.units.name);
227 break;
228
229 case WSS_DAC_LVL: /* dacout */
230 dip->type = AUDIO_MIXER_VALUE;
231 dip->mixer_class = WSS_INPUT_CLASS;
232 dip->prev = AUDIO_MIXER_LAST;
233 dip->next = WSS_DAC_MUTE;
234 strlcpy(dip->label.name, AudioNdac, sizeof dip->label.name);
235 dip->un.v.num_channels = 2;
236 strlcpy(dip->un.v.units.name, AudioNvolume, sizeof dip->un.v.units.name);
237 break;
238
239 case WSS_REC_LVL: /* record level */
240 dip->type = AUDIO_MIXER_VALUE;
241 dip->mixer_class = WSS_RECORD_CLASS;
242 dip->prev = AUDIO_MIXER_LAST;
243 dip->next = WSS_RECORD_SOURCE;
244 strlcpy(dip->label.name, AudioNrecord, sizeof dip->label.name);
245 dip->un.v.num_channels = 2;
246 strlcpy(dip->un.v.units.name, AudioNvolume, sizeof dip->un.v.units.name);
247 break;
248
249 case WSS_MON_LVL: /* monitor level */
250 dip->type = AUDIO_MIXER_VALUE;
251 dip->mixer_class = WSS_MONITOR_CLASS;
252 dip->next = dip->prev = AUDIO_MIXER_LAST;
253 strlcpy(dip->label.name, AudioNmonitor, sizeof dip->label.name);
254 dip->un.v.num_channels = 1;
255 strlcpy(dip->un.v.units.name, AudioNvolume, sizeof dip->un.v.units.name);
256 break;
257
258 case WSS_INPUT_CLASS: /* input class descriptor */
259 dip->type = AUDIO_MIXER_CLASS;
260 dip->mixer_class = WSS_INPUT_CLASS;
261 dip->next = dip->prev = AUDIO_MIXER_LAST;
262 strlcpy(dip->label.name, AudioCinputs, sizeof dip->label.name);
263 break;
264
265 case WSS_MONITOR_CLASS: /* monitor class descriptor */
266 dip->type = AUDIO_MIXER_CLASS;
267 dip->mixer_class = WSS_MONITOR_CLASS;
268 dip->next = dip->prev = AUDIO_MIXER_LAST;
269 strlcpy(dip->label.name, AudioCmonitor, sizeof dip->label.name);
270 break;
271
272 case WSS_RECORD_CLASS: /* record source class */
273 dip->type = AUDIO_MIXER_CLASS;
274 dip->mixer_class = WSS_RECORD_CLASS;
275 dip->next = dip->prev = AUDIO_MIXER_LAST;
276 strlcpy(dip->label.name, AudioCrecord, sizeof dip->label.name);
277 break;
278
279 case WSS_MIC_IN_MUTE:
280 dip->mixer_class = WSS_INPUT_CLASS;
281 dip->type = AUDIO_MIXER_ENUM;
282 dip->prev = WSS_MIC_IN_LVL;
283 dip->next = AUDIO_MIXER_LAST;
284 goto mute;
285
286 case WSS_LINE_IN_MUTE:
287 dip->mixer_class = WSS_INPUT_CLASS;
288 dip->type = AUDIO_MIXER_ENUM;
289 dip->prev = WSS_LINE_IN_LVL;
290 dip->next = AUDIO_MIXER_LAST;
291 goto mute;
292
293 case WSS_DAC_MUTE:
294 dip->mixer_class = WSS_INPUT_CLASS;
295 dip->type = AUDIO_MIXER_ENUM;
296 dip->prev = WSS_DAC_LVL;
297 dip->next = AUDIO_MIXER_LAST;
298 mute:
299 strlcpy(dip->label.name, AudioNmute, sizeof dip->label.name);
300 dip->un.e.num_mem = 2;
301 strlcpy(dip->un.e.member[0].label.name, AudioNoff,
302 sizeof dip->un.e.member[0].label.name);
303 dip->un.e.member[0].ord = 0;
304 strlcpy(dip->un.e.member[1].label.name, AudioNon,
305 sizeof dip->un.e.member[1].label.name);
306 dip->un.e.member[1].ord = 1;
307 break;
308
309 case WSS_RECORD_SOURCE:
310 dip->mixer_class = WSS_RECORD_CLASS;
311 dip->type = AUDIO_MIXER_ENUM;
312 dip->prev = WSS_REC_LVL;
313 dip->next = AUDIO_MIXER_LAST;
314 strlcpy(dip->label.name, AudioNsource, sizeof dip->label.name);
315 dip->un.e.num_mem = 3;
316 strlcpy(dip->un.e.member[0].label.name, AudioNmicrophone,
317 sizeof dip->un.e.member[0].label.name);
318 dip->un.e.member[0].ord = WSS_MIC_IN_LVL;
319 strlcpy(dip->un.e.member[1].label.name, AudioNcd,
320 sizeof dip->un.e.member[1].label.name);
321 dip->un.e.member[1].ord = WSS_LINE_IN_LVL;
322 strlcpy(dip->un.e.member[2].label.name, AudioNdac,
323 sizeof dip->un.e.member[2].label.name);
324 dip->un.e.member[2].ord = WSS_DAC_LVL;
325 break;
326
327 default:
328 return ENXIO;
329 /*NOTREACHED*/
330 }
331 DPRINTF(("AUDIO_MIXER_DEVINFO: name=%s\n", dip->label.name));
332
333 return 0;
334 }
335
336
337 /*
338 * Copyright by Hannu Savolainen 1994
339 *
340 * Redistribution and use in source and binary forms, with or without
341 * modification, are permitted provided that the following conditions are
342 * met: 1. Redistributions of source code must retain the above copyright
343 * notice, this list of conditions and the following disclaimer. 2.
344 * Redistributions in binary form must reproduce the above copyright notice,
345 * this list of conditions and the following disclaimer in the documentation
346 * and/or other materials provided with the distribution.
347 *
348 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
349 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
350 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
351 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
352 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
353 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
354 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
355 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
356 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
357 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
358 * SUCH DAMAGE.
359 *
360 */
361 /*
362 * Initialization code for OPTi MAD16 compatible audio chips. Including
363 *
364 * OPTi 82C928 MAD16 (replaced by C929)
365 * OAK OTI-601D Mozart
366 * OPTi 82C929 MAD16 Pro
367 *
368 */
369
370 u_int
mad_read(sc,port)371 mad_read(sc, port)
372 struct wss_softc *sc;
373 int port;
374 {
375 u_int tmp;
376 int pwd;
377 int s;
378
379 switch (sc->mad_chip_type) { /* Output password */
380 case MAD_82C928:
381 case MAD_OTI601D:
382 pwd = M_PASSWD_928;
383 break;
384 case MAD_82C929:
385 pwd = M_PASSWD_929;
386 break;
387 case MAD_82C931:
388 pwd = M_PASSWD_931;
389 break;
390 default:
391 panic("mad_read: Bad chip type=%d", sc->mad_chip_type);
392 }
393 s = splaudio(); /* don't want an interrupt between outb&inb */
394 bus_space_write_1(sc->sc_iot, sc->mad_ioh, MC_PASSWD_REG, pwd);
395 tmp = bus_space_read_1(sc->sc_iot, sc->mad_ioh, port);
396 splx(s);
397 return tmp;
398 }
399
400 void
mad_write(sc,port,value)401 mad_write(sc, port, value)
402 struct wss_softc *sc;
403 int port;
404 int value;
405 {
406 int pwd;
407 int s;
408
409 switch (sc->mad_chip_type) { /* Output password */
410 case MAD_82C928:
411 case MAD_OTI601D:
412 pwd = M_PASSWD_928;
413 break;
414 case MAD_82C929:
415 pwd = M_PASSWD_929;
416 break;
417 case MAD_82C931:
418 pwd = M_PASSWD_931;
419 break;
420 default:
421 panic("mad_write: Bad chip type=%d", sc->mad_chip_type);
422 }
423 s = splaudio();
424 bus_space_write_1(sc->sc_iot, sc->mad_ioh, MC_PASSWD_REG, pwd);
425 bus_space_write_1(sc->sc_iot, sc->mad_ioh, port, value & 0xff);
426 splx(s);
427 }
428
429 void
madattach(sc)430 madattach(sc)
431 struct wss_softc *sc;
432 {
433 unsigned char cs4231_mode;
434 int joy;
435
436 if (sc->mad_chip_type == MAD_NONE)
437 return;
438
439 /* Do we want the joystick disabled? */
440 joy = sc->sc_dev.dv_cfdata->cf_flags & 2 ? MC1_JOYDISABLE : 0;
441
442 /* enable WSS emulation at the I/O port */
443 mad_write(sc, MC1_PORT, M_WSS_PORT_SELECT(sc->mad_ioindex) | joy);
444 mad_write(sc, MC2_PORT, 0x03); /* ? */
445 mad_write(sc, MC3_PORT, 0xf0); /* Disable SB */
446
447 cs4231_mode =
448 strncmp(sc->sc_ad1848.chip_name, "CS4248", 6) == 0 ||
449 strncmp(sc->sc_ad1848.chip_name, "CS4231", 6) == 0 ? 0x02 : 0;
450
451 if (sc->mad_chip_type == MAD_82C929) {
452 mad_write(sc, MC4_PORT, 0x92);
453 mad_write(sc, MC5_PORT, 0xA5 | cs4231_mode);
454 mad_write(sc, MC6_PORT, 0x03); /* Disable MPU401 */
455 } else {
456 mad_write(sc, MC4_PORT, 0x02);
457 mad_write(sc, MC5_PORT, 0x30 | cs4231_mode);
458 }
459
460 #ifdef AUDIO_DEBUG
461 if (wssdebug) {
462 int i;
463 for (i = MC1_PORT; i <= MC7_PORT; i++)
464 DPRINTF(("port %03x after init = %02x\n", i, mad_read(sc, i)));
465 }
466 #endif
467 }
468