xref: /openbsd-src/sys/dev/isa/sb.c (revision b2ea75c1b17e1a9a339660e7ed45cd24946b230e)
1 /*	$OpenBSD: sb.c,v 1.20 1999/07/20 16:36:05 deraadt Exp $	*/
2 /*	$NetBSD: sb.c,v 1.57 1998/01/12 09:43:46 thorpej Exp $	*/
3 
4 /*
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 "midi.h"
39 
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/errno.h>
43 #include <sys/ioctl.h>
44 #include <sys/syslog.h>
45 #include <sys/device.h>
46 #include <sys/proc.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 #include <dev/midi_if.h>
55 
56 #include <dev/isa/isavar.h>
57 #include <dev/isa/isadmavar.h>
58 
59 #include <dev/isa/sbreg.h>
60 #include <dev/isa/sbvar.h>
61 #include <dev/isa/sbdspvar.h>
62 
63 struct cfdriver sb_cd = {
64 	NULL, "sb", DV_DULL
65 };
66 
67 #if NMIDI > 0
68 int	sb_mpu401_open __P((void *, int,
69 			     void (*iintr)__P((void *, int)),
70 			     void (*ointr)__P((void *)), void *arg));
71 void	sb_mpu401_close __P((void *));
72 int	sb_mpu401_output __P((void *, int));
73 void	sb_mpu401_getinfo __P((void *, struct midi_info *));
74 
75 struct midi_hw_if sb_midi_hw_if = {
76 	sbdsp_midi_open,
77 	sbdsp_midi_close,
78 	sbdsp_midi_output,
79 	sbdsp_midi_getinfo,
80 	0,			/* ioctl */
81 };
82 
83 struct midi_hw_if sb_mpu401_hw_if = {
84 	sb_mpu401_open,
85 	sb_mpu401_close,
86 	sb_mpu401_output,
87 	sb_mpu401_getinfo,
88 	0,			/* ioctl */
89 };
90 #endif
91 
92 struct audio_device sb_device = {
93 	"SoundBlaster",
94 	"x",
95 	"sb"
96 };
97 
98 int	sb_getdev __P((void *, struct audio_device *));
99 
100 /*
101  * Define our interface to the higher level audio driver.
102  */
103 
104 struct audio_hw_if sb_hw_if = {
105 	sbdsp_open,
106 	sbdsp_close,
107 	0,
108 	sbdsp_query_encoding,
109 	sbdsp_set_params,
110 	sbdsp_round_blocksize,
111 	0,
112 	0,
113 	0,
114 	0,
115 	0,
116 	sbdsp_haltdma,
117 	sbdsp_haltdma,
118 	sbdsp_speaker_ctl,
119 	sb_getdev,
120 	0,
121 	sbdsp_mixer_set_port,
122 	sbdsp_mixer_get_port,
123 	sbdsp_mixer_query_devinfo,
124 	sb_malloc,
125 	sb_free,
126 	sb_round,
127         sb_mappage,
128 	sbdsp_get_props,
129 	sbdsp_trigger_output,
130 	sbdsp_trigger_input
131 };
132 
133 #ifdef AUDIO_DEBUG
134 #define DPRINTF(x)	if (sbdebug) printf x
135 int	sbdebug = 0;
136 #else
137 #define DPRINTF(x)
138 #endif
139 
140 /*
141  * Probe / attach routines.
142  */
143 
144 
145 int
146 sbmatch(sc)
147 	struct sbdsp_softc *sc;
148 {
149 	static u_char drq_conf[8] = {
150 		0x01, 0x02, -1, 0x08, -1, 0x20, 0x40, 0x80
151 	};
152 
153 	static u_char irq_conf[11] = {
154 		-1, -1, 0x01, -1, -1, 0x02, -1, 0x04, -1, 0x01, 0x08
155 	};
156 
157 	if (sbdsp_probe(sc) == 0)
158 		return 0;
159 
160 	/*
161 	 * Cannot auto-discover DMA channel.
162 	 */
163 	if (ISSBPROCLASS(sc)) {
164 		if (!SBP_DRQ_VALID(sc->sc_drq8)) {
165 			DPRINTF(("%s: configured dma chan %d invalid\n",
166 			    sc->sc_dev.dv_xname, sc->sc_drq8));
167 			return 0;
168 		}
169 	} else {
170 		if (!SB_DRQ_VALID(sc->sc_drq8)) {
171 			DPRINTF(("%s: configured dma chan %d invalid\n",
172 			    sc->sc_dev.dv_xname, sc->sc_drq8));
173 			return 0;
174 		}
175 	}
176 
177         if (0 <= sc->sc_drq16 && sc->sc_drq16 <= 3)
178         	/*
179                  * XXX Some ViBRA16 cards seem to have two 8 bit DMA
180                  * channels.  I've no clue how to use them, so ignore
181                  * one of them for now.  -- augustss@netbsd.org
182                  */
183         	sc->sc_drq16 = -1;
184 
185 	if (ISSB16CLASS(sc)) {
186 		if (sc->sc_drq16 == -1)
187 			sc->sc_drq16 = sc->sc_drq8;
188 		if (!SB16_DRQ_VALID(sc->sc_drq16)) {
189 			DPRINTF(("%s: configured dma chan %d invalid\n",
190 			    sc->sc_dev.dv_xname, sc->sc_drq16));
191 			return 0;
192 		}
193 	} else
194 		sc->sc_drq16 = sc->sc_drq8;
195 
196 	if (ISSBPROCLASS(sc)) {
197 		if (!SBP_IRQ_VALID(sc->sc_irq)) {
198 			DPRINTF(("%s: configured irq %d invalid\n",
199 			    sc->sc_dev.dv_xname, sc->sc_irq));
200 			return 0;
201 		}
202 	} else {
203 		if (!SB_IRQ_VALID(sc->sc_irq)) {
204 			DPRINTF(("%s: configured irq %d invalid\n",
205 			    sc->sc_dev.dv_xname, sc->sc_irq));
206 			return 0;
207 		}
208 	}
209 
210 	if (ISSB16CLASS(sc)) {
211 		int w, r;
212 #if 0
213 		DPRINTF(("%s: old drq conf %02x\n", sc->sc_dev.dv_xname,
214 		    sbdsp_mix_read(sc, SBP_SET_DRQ)));
215 		DPRINTF(("%s: try drq conf %02x\n", sc->sc_dev.dv_xname,
216 		    drq_conf[sc->sc_drq16] | drq_conf[sc->sc_drq8]));
217 #endif
218 		w = drq_conf[sc->sc_drq16] | drq_conf[sc->sc_drq8];
219 		sbdsp_mix_write(sc, SBP_SET_DRQ, w);
220 		r = sbdsp_mix_read(sc, SBP_SET_DRQ) & 0xeb;
221 		if (r != w) {
222 			DPRINTF(("%s: setting drq mask %02x failed, got %02x\n", sc->sc_dev.dv_xname, w, r));
223 			return 0;
224 		}
225 #if 0
226 		DPRINTF(("%s: new drq conf %02x\n", sc->sc_dev.dv_xname,
227 		    sbdsp_mix_read(sc, SBP_SET_DRQ)));
228 #endif
229 
230 #if 0
231 		DPRINTF(("%s: old irq conf %02x\n", sc->sc_dev.dv_xname,
232 		    sbdsp_mix_read(sc, SBP_SET_IRQ)));
233 		DPRINTF(("%s: try irq conf %02x\n", sc->sc_dev.dv_xname,
234 		    irq_conf[sc->sc_irq]));
235 #endif
236 		w = irq_conf[sc->sc_irq];
237 		sbdsp_mix_write(sc, SBP_SET_IRQ, w);
238 		r = sbdsp_mix_read(sc, SBP_SET_IRQ) & 0x0f;
239 		if (r != w) {
240 			DPRINTF(("%s: setting irq mask %02x failed, got %02x\n",
241 			    sc->sc_dev.dv_xname, w, r));
242 			return 0;
243 		}
244 #if 0
245 		DPRINTF(("%s: new irq conf %02x\n", sc->sc_dev.dv_xname,
246 		    sbdsp_mix_read(sc, SBP_SET_IRQ)));
247 #endif
248 	}
249 
250 	return 1;
251 }
252 
253 
254 void
255 sbattach(sc)
256 	struct sbdsp_softc *sc;
257 {
258 	struct audio_attach_args arg;
259 #if NMIDI > 0
260 	struct midi_hw_if *mhw = &sb_midi_hw_if;
261 #endif
262 
263 	sc->sc_ih = isa_intr_establish(sc->sc_ic, sc->sc_irq, IST_EDGE,
264 	    IPL_AUDIO, sbdsp_intr, sc, sc->sc_dev.dv_xname);
265 
266 	sbdsp_attach(sc);
267 
268 #if NMIDI > 0
269 	sc->sc_hasmpu = 0;
270 	if (ISSB16CLASS(sc) && sc->sc_mpu_sc.iobase != 0) {
271 		sc->sc_mpu_sc.iot = sc->sc_iot;
272 		if (mpu_find(&sc->sc_mpu_sc)) {
273 			sc->sc_hasmpu = 1;
274 			mhw = &sb_mpu401_hw_if;
275 		}
276 	}
277 	midi_attach_mi(mhw, sc, &sc->sc_dev);
278 #endif
279 
280 	audio_attach_mi(&sb_hw_if, sc, &sc->sc_dev);
281 
282 	arg.type = AUDIODEV_TYPE_OPL;
283 	arg.hwif = 0;
284 	arg.hdl = 0;
285 	(void)config_found(&sc->sc_dev, &arg, audioprint);
286 }
287 
288 /*
289  * Various routines to interface to higher level audio driver
290  */
291 
292 int
293 sb_getdev(addr, retp)
294 	void *addr;
295 	struct audio_device *retp;
296 {
297 	struct sbdsp_softc *sc = addr;
298 	static char *names[] = SB_NAMES;
299 	char *config;
300 
301 	if (sc->sc_model == SB_JAZZ)
302 		strncpy(retp->name, "MV Jazz16", sizeof(retp->name));
303 	else
304 		strncpy(retp->name, "SoundBlaster", sizeof(retp->name));
305 	sprintf(retp->version, "%d.%02d",
306 		SBVER_MAJOR(sc->sc_version),
307 		SBVER_MINOR(sc->sc_version));
308 	if (0 <= sc->sc_model && sc->sc_model < sizeof names / sizeof names[0])
309 		config = names[sc->sc_model];
310 	else
311 		config = "??";
312 	strncpy(retp->config, config, sizeof(retp->config));
313 
314 	return 0;
315 }
316 
317 #if NMIDI > 0
318 
319 #define SBMPU(a) (&((struct sbdsp_softc *)addr)->sc_mpu_sc)
320 
321 int
322 sb_mpu401_open(addr, flags, iintr, ointr, arg)
323 	void *addr;
324 	int flags;
325 	void (*iintr)__P((void *, int));
326 	void (*ointr)__P((void *));
327 	void *arg;
328 {
329 	return mpu_open(SBMPU(addr), flags, iintr, ointr, arg);
330 }
331 
332 int
333 sb_mpu401_output(addr, d)
334 	void *addr;
335 	int d;
336 {
337 	return mpu_output(SBMPU(addr), d);
338 }
339 
340 void
341 sb_mpu401_close(addr)
342 	void *addr;
343 {
344 	mpu_close(SBMPU(addr));
345 }
346 
347 void
348 sb_mpu401_getinfo(addr, mi)
349 	void *addr;
350 	struct midi_info *mi;
351 {
352 	mi->name = "SB MPU-401 UART";
353 	mi->props = 0;
354 }
355 #endif
356