1 /* $OpenBSD: sb.c,v 1.35 2022/11/02 10:41:34 kn 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
47 #include <machine/cpu.h>
48 #include <machine/intr.h>
49 #include <machine/bus.h>
50
51 #include <sys/audioio.h>
52 #include <dev/audio_if.h>
53 #include <dev/midi_if.h>
54
55 #include <dev/isa/isavar.h>
56 #include <dev/isa/isadmavar.h>
57
58 #include <dev/isa/sbreg.h>
59 #include <dev/isa/sbvar.h>
60 #include <dev/isa/sbdspvar.h>
61
62 struct cfdriver sb_cd = {
63 NULL, "sb", DV_DULL
64 };
65
66 #if NMIDI > 0
67 int sb_mpu401_open(void *, int, void (*iintr)(void *, int),
68 void (*ointr)(void *), void *arg);
69 void sb_mpu401_close(void *);
70 int sb_mpu401_output(void *, int);
71 void sb_mpu401_getinfo(void *, struct midi_info *);
72
73 const struct midi_hw_if sb_midi_hw_if = {
74 sbdsp_midi_open,
75 sbdsp_midi_close,
76 sbdsp_midi_output,
77 0, /* flush */
78 sbdsp_midi_getinfo,
79 0, /* ioctl */
80 };
81
82 const struct midi_hw_if sb_mpu401_hw_if = {
83 sb_mpu401_open,
84 sb_mpu401_close,
85 sb_mpu401_output,
86 0, /* flush */
87 sb_mpu401_getinfo,
88 0, /* ioctl */
89 };
90 #endif
91
92 /*
93 * Define our interface to the higher level audio driver.
94 */
95
96 const struct audio_hw_if sb_hw_if = {
97 .open = sbdsp_open,
98 .close = sbdsp_close,
99 .set_params = sbdsp_set_params,
100 .round_blocksize = sbdsp_round_blocksize,
101 .halt_output = sbdsp_haltdma,
102 .halt_input = sbdsp_haltdma,
103 .set_port = sbdsp_mixer_set_port,
104 .get_port = sbdsp_mixer_get_port,
105 .query_devinfo = sbdsp_mixer_query_devinfo,
106 .allocm = sb_malloc,
107 .freem = sb_free,
108 .round_buffersize = sb_round,
109 .trigger_output = sbdsp_trigger_output,
110 .trigger_input = sbdsp_trigger_input,
111 };
112
113 #ifdef AUDIO_DEBUG
114 #define DPRINTF(x) if (sbdebug) printf x
115 int sbdebug = 0;
116 #else
117 #define DPRINTF(x)
118 #endif
119
120 /*
121 * Probe / attach routines.
122 */
123
124
125 int
sbmatch(struct sbdsp_softc * sc)126 sbmatch(struct sbdsp_softc *sc)
127 {
128 static u_char drq_conf[8] = {
129 0x01, 0x02, -1, 0x08, -1, 0x20, 0x40, 0x80
130 };
131
132 static u_char irq_conf[11] = {
133 -1, -1, 0x01, -1, -1, 0x02, -1, 0x04, -1, 0x01, 0x08
134 };
135
136 if (sbdsp_probe(sc) == 0)
137 return 0;
138
139 /*
140 * Cannot auto-discover DMA channel.
141 */
142 if (ISSBPROCLASS(sc)) {
143 if (!SBP_DRQ_VALID(sc->sc_drq8)) {
144 DPRINTF(("%s: configured dma chan %d invalid\n",
145 sc->sc_dev.dv_xname, sc->sc_drq8));
146 return 0;
147 }
148 } else {
149 if (!SB_DRQ_VALID(sc->sc_drq8)) {
150 DPRINTF(("%s: configured dma chan %d invalid\n",
151 sc->sc_dev.dv_xname, sc->sc_drq8));
152 return 0;
153 }
154 }
155
156 if (0 <= sc->sc_drq16 && sc->sc_drq16 <= 3)
157 /*
158 * XXX Some ViBRA16 cards seem to have two 8 bit DMA
159 * channels. I've no clue how to use them, so ignore
160 * one of them for now. -- augustss@netbsd.org
161 */
162 sc->sc_drq16 = -1;
163
164 if (ISSB16CLASS(sc)) {
165 if (sc->sc_drq16 == -1)
166 sc->sc_drq16 = sc->sc_drq8;
167 if (!SB16_DRQ_VALID(sc->sc_drq16)) {
168 DPRINTF(("%s: configured dma chan %d invalid\n",
169 sc->sc_dev.dv_xname, sc->sc_drq16));
170 return 0;
171 }
172 } else
173 sc->sc_drq16 = sc->sc_drq8;
174
175 if (ISSBPROCLASS(sc)) {
176 if (!SBP_IRQ_VALID(sc->sc_irq)) {
177 DPRINTF(("%s: configured irq %d invalid\n",
178 sc->sc_dev.dv_xname, sc->sc_irq));
179 return 0;
180 }
181 } else {
182 if (!SB_IRQ_VALID(sc->sc_irq)) {
183 DPRINTF(("%s: configured irq %d invalid\n",
184 sc->sc_dev.dv_xname, sc->sc_irq));
185 return 0;
186 }
187 }
188
189 if (ISSB16CLASS(sc)) {
190 int w, r;
191 #if 0
192 DPRINTF(("%s: old drq conf %02x\n", sc->sc_dev.dv_xname,
193 sbdsp_mix_read(sc, SBP_SET_DRQ)));
194 DPRINTF(("%s: try drq conf %02x\n", sc->sc_dev.dv_xname,
195 drq_conf[sc->sc_drq16] | drq_conf[sc->sc_drq8]));
196 #endif
197 w = drq_conf[sc->sc_drq16] | drq_conf[sc->sc_drq8];
198 sbdsp_mix_write(sc, SBP_SET_DRQ, w);
199 r = sbdsp_mix_read(sc, SBP_SET_DRQ) & 0xeb;
200 if (r != w) {
201 DPRINTF(("%s: setting drq mask %02x failed, got %02x\n", sc->sc_dev.dv_xname, w, r));
202 return 0;
203 }
204 #if 0
205 DPRINTF(("%s: new drq conf %02x\n", sc->sc_dev.dv_xname,
206 sbdsp_mix_read(sc, SBP_SET_DRQ)));
207 #endif
208
209 #if 0
210 DPRINTF(("%s: old irq conf %02x\n", sc->sc_dev.dv_xname,
211 sbdsp_mix_read(sc, SBP_SET_IRQ)));
212 DPRINTF(("%s: try irq conf %02x\n", sc->sc_dev.dv_xname,
213 irq_conf[sc->sc_irq]));
214 #endif
215 w = irq_conf[sc->sc_irq];
216 sbdsp_mix_write(sc, SBP_SET_IRQ, w);
217 r = sbdsp_mix_read(sc, SBP_SET_IRQ) & 0x0f;
218 if (r != w) {
219 DPRINTF(("%s: setting irq mask %02x failed, got %02x\n",
220 sc->sc_dev.dv_xname, w, r));
221 return 0;
222 }
223 #if 0
224 DPRINTF(("%s: new irq conf %02x\n", sc->sc_dev.dv_xname,
225 sbdsp_mix_read(sc, SBP_SET_IRQ)));
226 #endif
227 }
228
229 return 1;
230 }
231
232
233 void
sbattach(struct sbdsp_softc * sc)234 sbattach(struct sbdsp_softc *sc)
235 {
236 struct audio_attach_args arg;
237 #if NMIDI > 0
238 const struct midi_hw_if *mhw = &sb_midi_hw_if;
239 #endif
240
241 sc->sc_ih = isa_intr_establish(sc->sc_ic, sc->sc_irq,
242 IST_EDGE, IPL_AUDIO | IPL_MPSAFE,
243 sbdsp_intr, sc, sc->sc_dev.dv_xname);
244
245 sbdsp_attach(sc);
246
247 #if NMIDI > 0
248 sc->sc_hasmpu = 0;
249 if (ISSB16CLASS(sc) && sc->sc_mpu_sc.iobase != 0) {
250 sc->sc_mpu_sc.iot = sc->sc_iot;
251 if (mpu_find(&sc->sc_mpu_sc)) {
252 sc->sc_hasmpu = 1;
253 mhw = &sb_mpu401_hw_if;
254 }
255 }
256 midi_attach_mi(mhw, sc, &sc->sc_dev);
257 #endif
258
259 audio_attach_mi(&sb_hw_if, sc, NULL, &sc->sc_dev);
260
261 arg.type = AUDIODEV_TYPE_OPL;
262 arg.hwif = 0;
263 arg.hdl = 0;
264 (void)config_found(&sc->sc_dev, &arg, audioprint);
265 }
266
267 /*
268 * Various routines to interface to higher level audio driver
269 */
270
271 #if NMIDI > 0
272
273 #define SBMPU(a) (&((struct sbdsp_softc *)addr)->sc_mpu_sc)
274
275 int
sb_mpu401_open(void * addr,int flags,void (* iintr)(void *,int),void (* ointr)(void *),void * arg)276 sb_mpu401_open(void *addr, int flags, void (*iintr)(void *, int),
277 void (*ointr)(void *), void *arg)
278 {
279 return mpu_open(SBMPU(addr), flags, iintr, ointr, arg);
280 }
281
282 int
sb_mpu401_output(void * addr,int d)283 sb_mpu401_output(void *addr, int d)
284 {
285 return mpu_output(SBMPU(addr), d);
286 }
287
288 void
sb_mpu401_close(void * addr)289 sb_mpu401_close(void *addr)
290 {
291 mpu_close(SBMPU(addr));
292 }
293
294 void
sb_mpu401_getinfo(void * addr,struct midi_info * mi)295 sb_mpu401_getinfo(void *addr, struct midi_info *mi)
296 {
297 mi->name = "SB MPU-401 UART";
298 mi->props = 0;
299 }
300 #endif
301