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