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