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