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