1 /* $NetBSD: wss.c,v 1.67 2006/11/16 01:33:00 christos Exp $ */
4 * Copyright (c) 1994 John Brezak
5 * Copyright (c) 1991-1993 Regents of the University of California.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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
38 #include <sys/cdefs.h>
39 __KERNEL_RCSID(0, "$NetBSD: wss.c,v 1.67 2006/11/16 01:33:00 christos Exp $");
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/device.h>
44 #include <sys/errno.h>
50 #include <sys/audioio.h>
51 #include <dev/audio_if.h>
53 #include <dev/isa/isavar.h>
54 #include <dev/isa/isadmavar.h>
56 #include <dev/ic/ad1848reg.h>
57 #include <dev/ic/cs4231reg.h>
58 #include <dev/isa/ad1848var.h>
59 #include <dev/isa/cs4231var.h>
60 #include <dev/isa/wssreg.h>
61 #include <dev/isa/wssvar.h>
62 #include <dev/isa/madreg.h>
65 #define DPRINTF(x) if (wssdebug) printf x
71 struct audio_device wss_device
= {
77 int wss_getdev(void *, struct audio_device
*);
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
*);
84 * Define our interface to the higher level audio driver.
87 const struct audio_hw_if wss_hw_if
= {
91 ad1848_query_encoding
,
93 ad1848_round_blocksize
,
94 ad1848_commit_settings
,
99 ad1848_isa_halt_output
,
100 ad1848_isa_halt_input
,
109 ad1848_isa_round_buffersize
,
111 ad1848_isa_get_props
,
112 ad1848_isa_trigger_output
,
113 ad1848_isa_trigger_input
,
115 NULL
, /* powerstate */
119 * Attach hardware to driver, attach hardware driver to audio
120 * pseudo-device driver .
123 wssattach(struct wss_softc
*sc
)
125 struct ad1848_softc
*ac
;
126 #if 0 /* loses on CS423X chips */
130 ac
= &sc
->sc_ad1848
.sc_ad1848
;
133 sc
->sc_ad1848
.sc_ih
= isa_intr_establish(sc
->wss_ic
, sc
->wss_irq
,
134 IST_EDGE
, IPL_AUDIO
, ad1848_isa_intr
, &sc
->sc_ad1848
);
136 ad1848_isa_attach(&sc
->sc_ad1848
);
138 #if 0 /* loses on CS423X chips */
139 version
= bus_space_read_1(sc
->sc_iot
, sc
->sc_ioh
, WSS_STATUS
)
141 printf(" (vers %d)", version
);
144 switch(sc
->mad_chip_type
) {
149 printf(", OTI-601D");
164 audio_attach_mi(&wss_hw_if
, &sc
->sc_ad1848
, &ac
->sc_dev
);
166 if (sc
->mad_chip_type
!= MAD_NONE
) {
167 struct audio_attach_args arg
;
168 arg
.type
= AUDIODEV_TYPE_OPL
;
171 (void)config_found(&ac
->sc_dev
, &arg
, audioprint
);
176 wss_getdev(void *addr
, struct audio_device
*retp
)
183 static ad1848_devmap_t mappings
[] = {
184 { WSS_MIC_IN_LVL
, AD1848_KIND_LVL
, AD1848_AUX2_CHANNEL
},
185 { WSS_LINE_IN_LVL
, AD1848_KIND_LVL
, AD1848_AUX1_CHANNEL
},
186 { WSS_DAC_LVL
, AD1848_KIND_LVL
, AD1848_DAC_CHANNEL
},
187 { WSS_MONITOR_LVL
, AD1848_KIND_LVL
, AD1848_MONO_CHANNEL
},
188 { WSS_MIC_IN_MUTE
, AD1848_KIND_MUTE
, AD1848_AUX2_CHANNEL
},
189 { WSS_LINE_IN_MUTE
, AD1848_KIND_MUTE
, AD1848_AUX1_CHANNEL
},
190 { WSS_DAC_MUTE
, AD1848_KIND_MUTE
, AD1848_DAC_CHANNEL
},
191 { WSS_MONITOR_MUTE
, AD1848_KIND_MUTE
, AD1848_MONO_CHANNEL
},
192 { WSS_REC_LVL
, AD1848_KIND_RECORDGAIN
, -1 },
193 { WSS_RECORD_SOURCE
, AD1848_KIND_RECORDSOURCE
, -1}
196 static int nummap
= sizeof(mappings
) / sizeof(mappings
[0]);
199 wss_mixer_set_port(void *addr
, mixer_ctrl_t
*cp
)
201 struct ad1848_softc
*ac
;
204 return ad1848_mixer_set_port(ac
, mappings
, nummap
, cp
);
208 wss_mixer_get_port(void *addr
, mixer_ctrl_t
*cp
)
210 struct ad1848_softc
*ac
;
213 return ad1848_mixer_get_port(ac
, mappings
, nummap
, cp
);
217 wss_query_devinfo(void *addr
, mixer_devinfo_t
*dip
)
220 DPRINTF(("wss_query_devinfo: index=%d\n", dip
->index
));
222 case WSS_MIC_IN_LVL
: /* Microphone */
223 dip
->type
= AUDIO_MIXER_VALUE
;
224 dip
->mixer_class
= WSS_INPUT_CLASS
;
225 dip
->prev
= AUDIO_MIXER_LAST
;
226 dip
->next
= WSS_MIC_IN_MUTE
;
227 strcpy(dip
->label
.name
, AudioNmicrophone
);
228 dip
->un
.v
.num_channels
= 2;
229 strcpy(dip
->un
.v
.units
.name
, AudioNvolume
);
232 case WSS_LINE_IN_LVL
: /* line/CD */
233 dip
->type
= AUDIO_MIXER_VALUE
;
234 dip
->mixer_class
= WSS_INPUT_CLASS
;
235 dip
->prev
= AUDIO_MIXER_LAST
;
236 dip
->next
= WSS_LINE_IN_MUTE
;
237 strcpy(dip
->label
.name
, AudioNcd
);
238 dip
->un
.v
.num_channels
= 2;
239 strcpy(dip
->un
.v
.units
.name
, AudioNvolume
);
242 case WSS_DAC_LVL
: /* dacout */
243 dip
->type
= AUDIO_MIXER_VALUE
;
244 dip
->mixer_class
= WSS_INPUT_CLASS
;
245 dip
->prev
= AUDIO_MIXER_LAST
;
246 dip
->next
= WSS_DAC_MUTE
;
247 strcpy(dip
->label
.name
, AudioNdac
);
248 dip
->un
.v
.num_channels
= 2;
249 strcpy(dip
->un
.v
.units
.name
, AudioNvolume
);
252 case WSS_REC_LVL
: /* record level */
253 dip
->type
= AUDIO_MIXER_VALUE
;
254 dip
->mixer_class
= WSS_RECORD_CLASS
;
255 dip
->prev
= AUDIO_MIXER_LAST
;
256 dip
->next
= WSS_RECORD_SOURCE
;
257 strcpy(dip
->label
.name
, AudioNrecord
);
258 dip
->un
.v
.num_channels
= 2;
259 strcpy(dip
->un
.v
.units
.name
, AudioNvolume
);
262 case WSS_MONITOR_LVL
: /* monitor level */
263 dip
->type
= AUDIO_MIXER_VALUE
;
264 dip
->mixer_class
= WSS_MONITOR_CLASS
;
265 dip
->prev
= AUDIO_MIXER_LAST
;
266 dip
->next
= WSS_MONITOR_MUTE
;
267 strcpy(dip
->label
.name
, AudioNmonitor
);
268 dip
->un
.v
.num_channels
= 1;
269 strcpy(dip
->un
.v
.units
.name
, AudioNvolume
);
272 case WSS_INPUT_CLASS
: /* input class descriptor */
273 dip
->type
= AUDIO_MIXER_CLASS
;
274 dip
->mixer_class
= WSS_INPUT_CLASS
;
275 dip
->next
= dip
->prev
= AUDIO_MIXER_LAST
;
276 strcpy(dip
->label
.name
, AudioCinputs
);
279 case WSS_MONITOR_CLASS
: /* monitor class descriptor */
280 dip
->type
= AUDIO_MIXER_CLASS
;
281 dip
->mixer_class
= WSS_MONITOR_CLASS
;
282 dip
->next
= dip
->prev
= AUDIO_MIXER_LAST
;
283 strcpy(dip
->label
.name
, AudioCmonitor
);
286 case WSS_RECORD_CLASS
: /* record source class */
287 dip
->type
= AUDIO_MIXER_CLASS
;
288 dip
->mixer_class
= WSS_RECORD_CLASS
;
289 dip
->next
= dip
->prev
= AUDIO_MIXER_LAST
;
290 strcpy(dip
->label
.name
, AudioCrecord
);
293 case WSS_MIC_IN_MUTE
:
294 dip
->mixer_class
= WSS_INPUT_CLASS
;
295 dip
->type
= AUDIO_MIXER_ENUM
;
296 dip
->prev
= WSS_MIC_IN_LVL
;
297 dip
->next
= AUDIO_MIXER_LAST
;
300 case WSS_LINE_IN_MUTE
:
301 dip
->mixer_class
= WSS_INPUT_CLASS
;
302 dip
->type
= AUDIO_MIXER_ENUM
;
303 dip
->prev
= WSS_LINE_IN_LVL
;
304 dip
->next
= AUDIO_MIXER_LAST
;
308 dip
->mixer_class
= WSS_INPUT_CLASS
;
309 dip
->type
= AUDIO_MIXER_ENUM
;
310 dip
->prev
= WSS_DAC_LVL
;
311 dip
->next
= AUDIO_MIXER_LAST
;
314 case WSS_MONITOR_MUTE
:
315 dip
->mixer_class
= WSS_MONITOR_CLASS
;
316 dip
->type
= AUDIO_MIXER_ENUM
;
317 dip
->prev
= WSS_MONITOR_LVL
;
318 dip
->next
= AUDIO_MIXER_LAST
;
320 strcpy(dip
->label
.name
, AudioNmute
);
321 dip
->un
.e
.num_mem
= 2;
322 strcpy(dip
->un
.e
.member
[0].label
.name
, AudioNoff
);
323 dip
->un
.e
.member
[0].ord
= 0;
324 strcpy(dip
->un
.e
.member
[1].label
.name
, AudioNon
);
325 dip
->un
.e
.member
[1].ord
= 1;
328 case WSS_RECORD_SOURCE
:
329 dip
->mixer_class
= WSS_RECORD_CLASS
;
330 dip
->type
= AUDIO_MIXER_ENUM
;
331 dip
->prev
= WSS_REC_LVL
;
332 dip
->next
= AUDIO_MIXER_LAST
;
333 strcpy(dip
->label
.name
, AudioNsource
);
334 dip
->un
.e
.num_mem
= 3;
335 strcpy(dip
->un
.e
.member
[0].label
.name
, AudioNmicrophone
);
336 dip
->un
.e
.member
[0].ord
= WSS_MIC_IN_LVL
;
337 strcpy(dip
->un
.e
.member
[1].label
.name
, AudioNcd
);
338 dip
->un
.e
.member
[1].ord
= WSS_LINE_IN_LVL
;
339 strcpy(dip
->un
.e
.member
[2].label
.name
, AudioNdac
);
340 dip
->un
.e
.member
[2].ord
= WSS_DAC_LVL
;
347 DPRINTF(("AUDIO_MIXER_DEVINFO: name=%s\n", dip
->label
.name
));
354 * Copyright by Hannu Savolainen 1994
356 * Redistribution and use in source and binary forms, with or without
357 * modification, are permitted provided that the following conditions are
358 * met: 1. Redistributions of source code must retain the above copyright
359 * notice, this list of conditions and the following disclaimer. 2.
360 * Redistributions in binary form must reproduce the above copyright notice,
361 * this list of conditions and the following disclaimer in the documentation
362 * and/or other materials provided with the distribution.
364 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
365 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
366 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
367 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
368 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
369 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
370 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
371 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
372 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
373 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
378 * Initialization code for OPTi MAD16 compatible audio chips. Including
380 * OPTi 82C928 MAD16 (replaced by C929)
381 * OAK OTI-601D Mozart
382 * OPTi 82C929 MAD16 Pro
387 mad_read(struct wss_softc
*sc
, int port
)
393 switch (sc
->mad_chip_type
) { /* Output password */
405 panic("mad_read: Bad chip type=%d", sc
->mad_chip_type
);
407 s
= splaudio(); /* don't want an interrupt between outb&inb */
408 bus_space_write_1(sc
->sc_iot
, sc
->mad_ioh
, MC_PASSWD_REG
, pwd
);
409 tmp
= bus_space_read_1(sc
->sc_iot
, sc
->mad_ioh
, port
);
415 mad_write(struct wss_softc
*sc
, int port
, int value
)
420 switch (sc
->mad_chip_type
) { /* Output password */
432 panic("mad_write: Bad chip type=%d", sc
->mad_chip_type
);
435 bus_space_write_1(sc
->sc_iot
, sc
->mad_ioh
, MC_PASSWD_REG
, pwd
);
436 bus_space_write_1(sc
->sc_iot
, sc
->mad_ioh
, port
, value
& 0xff);
441 madattach(struct wss_softc
*sc
)
443 struct ad1848_softc
*ac
;
444 unsigned char cs4231_mode
;
447 ac
= (struct ad1848_softc
*)&sc
->sc_ad1848
;
448 if (sc
->mad_chip_type
== MAD_NONE
)
451 /* Do we want the joystick disabled? */
452 joy
= device_cfdata(&ac
->sc_dev
)->cf_flags
& 2 ? MC1_JOYDISABLE
: 0;
454 /* enable WSS emulation at the I/O port */
455 mad_write(sc
, MC1_PORT
, M_WSS_PORT_SELECT(sc
->mad_ioindex
) | joy
);
456 mad_write(sc
, MC2_PORT
, MC2_NO_CD_DRQ
); /* disable CD */
457 mad_write(sc
, MC3_PORT
, 0xf0); /* Disable SB */
460 strncmp(ac
->chip_name
, "CS4248", 6) == 0 ||
461 strncmp(ac
->chip_name
, "CS4231", 6) == 0 ? 0x02 : 0;
463 if (sc
->mad_chip_type
== MAD_82C929
) {
464 mad_write(sc
, MC4_PORT
, 0x92);
465 mad_write(sc
, MC5_PORT
, 0xA5 | cs4231_mode
);
466 mad_write(sc
, MC6_PORT
, 0x03); /* Disable MPU401 */
468 mad_write(sc
, MC4_PORT
, 0x02);
469 mad_write(sc
, MC5_PORT
, 0x30 | cs4231_mode
);
475 for (i
= MC1_PORT
; i
<= MC7_PORT
; i
++)
476 DPRINTF(("port %03x after init = %02x\n",
477 i
, mad_read(sc
, i
)));