1 /* $OpenBSD: sb.c,v 1.25 2008/04/21 00:32:42 jakemsr 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, IST_EDGE, 266 IPL_AUDIO, sbdsp_intr, sc, sc->sc_dev.dv_xname); 267 268 sbdsp_attach(sc); 269 270 #if NMIDI > 0 271 sc->sc_hasmpu = 0; 272 if (ISSB16CLASS(sc) && sc->sc_mpu_sc.iobase != 0) { 273 sc->sc_mpu_sc.iot = sc->sc_iot; 274 if (mpu_find(&sc->sc_mpu_sc)) { 275 sc->sc_hasmpu = 1; 276 mhw = &sb_mpu401_hw_if; 277 } 278 } 279 midi_attach_mi(mhw, sc, &sc->sc_dev); 280 #endif 281 282 audio_attach_mi(&sb_hw_if, sc, &sc->sc_dev); 283 284 arg.type = AUDIODEV_TYPE_OPL; 285 arg.hwif = 0; 286 arg.hdl = 0; 287 (void)config_found(&sc->sc_dev, &arg, audioprint); 288 } 289 290 /* 291 * Various routines to interface to higher level audio driver 292 */ 293 294 int 295 sb_getdev(addr, retp) 296 void *addr; 297 struct audio_device *retp; 298 { 299 struct sbdsp_softc *sc = addr; 300 static char *names[] = SB_NAMES; 301 char *config; 302 303 if (sc->sc_model == SB_JAZZ) 304 strlcpy(retp->name, "MV Jazz16", sizeof retp->name); 305 else 306 strlcpy(retp->name, "SoundBlaster", sizeof retp->name); 307 snprintf(retp->version, sizeof retp->version, "%d.%02d", 308 SBVER_MAJOR(sc->sc_version), 309 SBVER_MINOR(sc->sc_version)); 310 if (0 <= sc->sc_model && sc->sc_model < sizeof names / sizeof names[0]) 311 config = names[sc->sc_model]; 312 else 313 config = "??"; 314 strlcpy(retp->config, config, sizeof retp->config); 315 316 return 0; 317 } 318 319 #if NMIDI > 0 320 321 #define SBMPU(a) (&((struct sbdsp_softc *)addr)->sc_mpu_sc) 322 323 int 324 sb_mpu401_open(addr, flags, iintr, ointr, arg) 325 void *addr; 326 int flags; 327 void (*iintr)(void *, int); 328 void (*ointr)(void *); 329 void *arg; 330 { 331 return mpu_open(SBMPU(addr), flags, iintr, ointr, arg); 332 } 333 334 int 335 sb_mpu401_output(addr, d) 336 void *addr; 337 int d; 338 { 339 return mpu_output(SBMPU(addr), d); 340 } 341 342 void 343 sb_mpu401_close(addr) 344 void *addr; 345 { 346 mpu_close(SBMPU(addr)); 347 } 348 349 void 350 sb_mpu401_getinfo(addr, mi) 351 void *addr; 352 struct midi_info *mi; 353 { 354 mi->name = "SB MPU-401 UART"; 355 mi->props = 0; 356 } 357 #endif 358