pas.c revision 1.10
1/* $NetBSD: pas.c,v 1.10 1995/11/10 05:05:18 mycroft Exp $ */ 2 3/* 4 * Copyright (c) 1991-1993 Regents of the University of California. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the Computer Systems 18 * Engineering Group at Lawrence Berkeley Laboratory. 19 * 4. Neither the name of the University nor of the Laboratory may be used 20 * to endorse or promote products derived from this software without 21 * specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 * 35 */ 36/* 37 * Todo: 38 * - look at other PAS drivers (for PAS native suport) 39 * - use common sb.c once emulation is setup 40 */ 41 42#include <sys/param.h> 43#include <sys/systm.h> 44#include <sys/errno.h> 45#include <sys/ioctl.h> 46#include <sys/syslog.h> 47#include <sys/device.h> 48#include <sys/proc.h> 49 50#include <machine/cpu.h> 51#include <machine/pio.h> 52 53#include <sys/audioio.h> 54#include <dev/audio_if.h> 55#include <dev/mulaw.h> 56 57#include <dev/isa/isavar.h> 58#include <dev/isa/isadmavar.h> 59 60#include <dev/isa/sbdspvar.h> 61#include <dev/isa/sbreg.h> 62 63#define DEFINE_TRANSLATIONS 64#include <dev/isa/pasreg.h> 65 66#ifdef AUDIO_DEBUG 67#define DPRINTF(x) if (pasdebug) printf x 68int pasdebug = 0; 69#else 70#define DPRINTF(x) 71#endif 72 73/* 74 * Software state, per SoundBlaster card. 75 * The soundblaster has multiple functionality, which we must demultiplex. 76 * One approach is to have one major device number for the soundblaster card, 77 * and use different minor numbers to indicate which hardware function 78 * we want. This would make for one large driver. Instead our approach 79 * is to partition the design into a set of drivers that share an underlying 80 * piece of hardware. Most things are hard to share, for example, the audio 81 * and midi ports. For audio, we might want to mix two processes' signals, 82 * and for midi we might want to merge streams (this is hard due to 83 * running status). Moreover, we should be able to re-use the high-level 84 * modules with other kinds of hardware. In this module, we only handle the 85 * most basic communications with the sb card. 86 */ 87struct pas_softc { 88 struct device sc_dev; /* base device */ 89 struct isadev sc_id; /* ISA device */ 90 void *sc_ih; /* interrupt vectoring */ 91 92 int sc_iobase; /* PAS iobase */ 93 int sc_irq; /* PAS irq */ 94 int sc_drq; /* PAS drq */ 95 96 int model; 97 int rev; 98 99 struct sbdsp_softc sc_sbdsp; 100}; 101 102int pasopen __P((dev_t, int)); 103 104int pasprobe(); 105void pasattach(); 106 107int pas_getdev __P((void *, struct audio_device *)); 108 109 110/* 111 * Define our interface to the higher level audio driver. 112 */ 113 114struct audio_hw_if pas_hw_if = { 115 pasopen, 116 sbdsp_close, 117 NULL, 118 sbdsp_set_in_sr, 119 sbdsp_get_in_sr, 120 sbdsp_set_out_sr, 121 sbdsp_get_out_sr, 122 sbdsp_query_encoding, 123 sbdsp_set_encoding, 124 sbdsp_get_encoding, 125 sbdsp_set_precision, 126 sbdsp_get_precision, 127 sbdsp_set_channels, 128 sbdsp_get_channels, 129 sbdsp_round_blocksize, 130 sbdsp_set_out_port, 131 sbdsp_get_out_port, 132 sbdsp_set_in_port, 133 sbdsp_get_in_port, 134 sbdsp_commit_settings, 135 sbdsp_get_silence, 136 mulaw_expand, 137 mulaw_compress, 138 sbdsp_dma_output, 139 sbdsp_dma_input, 140 sbdsp_haltdma, 141 sbdsp_haltdma, 142 sbdsp_contdma, 143 sbdsp_contdma, 144 sbdsp_speaker_ctl, 145 pas_getdev, 146 sbdsp_setfd, 147 sbdsp_mixer_set_port, 148 sbdsp_mixer_get_port, 149 sbdsp_mixer_query_devinfo, 150 0, /* not full-duplex */ 151 0 152}; 153 154/* The Address Translation code is used to convert I/O register addresses to 155 be relative to the given base -register */ 156 157static char *pasnames[] = { 158 "", 159 "Plus", 160 "CDPC", 161 "16", 162 "16Basic" 163}; 164 165static struct audio_device pas_device = { 166 "PAS,??", 167 "", 168 "pas" 169}; 170 171/*XXX assume default I/O base address */ 172#define pasread(p) inb(p) 173#define paswrite(d, p) outb(p, d) 174 175void 176pasconf(int model, int sbbase, int sbirq, int sbdrq) 177{ 178 int i; 179 180 paswrite(0x00, INTERRUPT_MASK); 181 /* Local timer control register */ 182 paswrite(0x36, SAMPLE_COUNTER_CONTROL); 183 /* Sample rate timer (16 bit) */ 184 paswrite(0x36, SAMPLE_RATE_TIMER); 185 paswrite(0, SAMPLE_RATE_TIMER); 186 /* Local timer control register */ 187 paswrite(0x74, SAMPLE_COUNTER_CONTROL); 188 /* Sample count register (16 bit) */ 189 paswrite(0x74, SAMPLE_BUFFER_COUNTER); 190 paswrite(0, SAMPLE_BUFFER_COUNTER); 191 192 paswrite(P_C_PCM_MONO | P_C_PCM_DAC_MODE | 193 P_C_MIXER_CROSS_L_TO_L | P_C_MIXER_CROSS_R_TO_R, 194 PCM_CONTROL); 195 paswrite(S_M_PCM_RESET | S_M_FM_RESET | 196 S_M_SB_RESET | S_M_MIXER_RESET, SERIAL_MIXER); 197 198/*XXX*/ 199 paswrite(I_C_1_BOOT_RESET_ENABLE|1, IO_CONFIGURATION_1); 200 201 paswrite(I_C_2_PCM_DMA_DISABLED, IO_CONFIGURATION_2); 202 paswrite(I_C_3_PCM_IRQ_DISABLED, IO_CONFIGURATION_3); 203 204#ifdef BROKEN_BUS_CLOCK 205 paswrite(S_C_1_PCS_ENABLE | S_C_1_PCS_STEREO | S_C_1_PCS_REALSOUND | 206 S_C_1_FM_EMULATE_CLOCK, SYSTEM_CONFIGURATION_1); 207#else 208 paswrite(S_C_1_PCS_ENABLE | S_C_1_PCS_STEREO | S_C_1_PCS_REALSOUND, 209 SYSTEM_CONFIGURATION_1); 210#endif 211 212 /*XXX*/ 213 paswrite(0, SYSTEM_CONFIGURATION_2); 214 paswrite(0, SYSTEM_CONFIGURATION_3); 215 216 /* Sets mute off and selects filter rate of 17.897 kHz */ 217 paswrite(F_F_MIXER_UNMUTE | 0x01, FILTER_FREQUENCY); 218 219 if (model == PAS_16 || model == PAS_16BASIC) 220 paswrite(8, PRESCALE_DIVIDER); 221 else 222 paswrite(0, PRESCALE_DIVIDER); 223 224 paswrite(P_M_MV508_ADDRESS | P_M_MV508_PCM, PARALLEL_MIXER); 225 paswrite(5, PARALLEL_MIXER); 226 227 /* 228 * Setup SoundBlaster emulation. 229 */ 230 paswrite((sbbase >> 4) & 0xf, EMULATION_ADDRESS); 231 paswrite(E_C_SB_IRQ_translate[sbirq] | E_C_SB_DMA_translate[sbdrq], 232 EMULATION_CONFIGURATION); 233 paswrite(C_E_SB_ENABLE, COMPATIBILITY_ENABLE); 234 235 /* 236 * Set mid-range levels. 237 */ 238 paswrite(P_M_MV508_ADDRESS | P_M_MV508_MODE, PARALLEL_MIXER); 239 paswrite(P_M_MV508_LOUDNESS | P_M_MV508_ENHANCE_NONE, PARALLEL_MIXER); 240 241 paswrite(P_M_MV508_ADDRESS | P_M_MV508_MASTER_A, PARALLEL_MIXER); 242 paswrite(50, PARALLEL_MIXER); 243 paswrite(P_M_MV508_ADDRESS | P_M_MV508_MASTER_B, PARALLEL_MIXER); 244 paswrite(50, PARALLEL_MIXER); 245 246 paswrite(P_M_MV508_ADDRESS | P_M_MV508_MIXER | P_M_MV508_SB, PARALLEL_MIXER); 247 paswrite(P_M_MV508_OUTPUTMIX | 30, PARALLEL_MIXER); 248 249 paswrite(P_M_MV508_ADDRESS | P_M_MV508_MIXER | P_M_MV508_MIC, PARALLEL_MIXER); 250 paswrite(P_M_MV508_INPUTMIX | 30, PARALLEL_MIXER); 251} 252 253struct cfdriver pascd = { 254 NULL, "pas", pasprobe, pasattach, DV_DULL, sizeof(struct pas_softc) 255}; 256 257/* 258 * Probe / attach routines. 259 */ 260 261/* 262 * Probe for the soundblaster hardware. 263 */ 264int 265pasprobe(parent, self, aux) 266 struct device *parent, *self; 267 void *aux; 268{ 269 register struct pas_softc *sc = (void *)self; 270 register struct isa_attach_args *ia = aux; 271 register int iobase; 272 u_char id, t; 273 274 /* 275 * WARNING: Setting an option like W:1 or so that disables 276 * warm boot reset of the card will screw up this detect code 277 * something fierce. Adding code to handle this means possibly 278 * interfering with other cards on the bus if you have something 279 * on base port 0x388. SO be forewarned. 280 */ 281 /* Talk to first board */ 282 outb(MASTER_DECODE, 0xbc); 283 /* Set base address */ 284 285#if 0 286 /* XXX Need to setup pseudo device */ 287 /* XXX What are good io addrs ? */ 288 if (iobase != PAS_DEFAULT_BASE) { 289 printf("pas: configured iobase %d invalid\n", iobase); 290 return 0; 291 } 292#else 293 /* Start out talking to native PAS */ 294 iobase = PAS_DEFAULT_BASE; 295#endif 296 297 outb(MASTER_DECODE, iobase >> 2); 298 /* One wait-state */ 299 paswrite(1, WAIT_STATE); 300 301 id = pasread(INTERRUPT_MASK); 302 if (id == 0xff || id == 0xfe) { 303 /* sanity */ 304 DPRINTF(("pas: bogus card id\n")); 305 return 0; 306 } 307 /* 308 * We probably have a PAS-series board, now check for a 309 * PAS2-series board by trying to change the board revision 310 * bits. PAS2-series hardware won't let you do this because 311 * the bits are read-only. 312 */ 313 t = id ^ 0xe0; 314 paswrite(t, INTERRUPT_MASK); 315 t = inb(INTERRUPT_MASK); 316 paswrite(id, INTERRUPT_MASK); 317 318 if (t != id) { 319 /* Not a PAS2 */ 320 printf("pas: detected card but PAS2 test failed\n"); 321 return 0; 322 } 323 /*XXX*/ 324 t = pasread(OPERATION_MODE_1) & 0xf; 325 sc->model = O_M_1_to_card[t]; 326 if (sc->model != 0) { 327 sc->rev = pasread(BOARD_REV_ID); 328 } 329 else { 330 DPRINTF(("pas: bogus model id\n")); 331 return 0; 332 } 333 334 if (sc->model >= 0) { 335 if (ia->ia_irq == IRQUNK) { 336 printf("pas: sb emulation requires known irq\n"); 337 return (0); 338 } 339 pasconf(sc->model, ia->ia_iobase, ia->ia_irq, 1); 340 } else { 341 DPRINTF(("pas: could not probe pas\n")); 342 return (0); 343 } 344 345 /* Now a SoundBlaster */ 346 sc->sc_iobase = ia->ia_iobase; 347 /* and set the SB iobase into the DSP as well ... */ 348 sc->sc_sbdsp.sc_iobase = ia->ia_iobase; 349 if (sbdsp_reset(&sc->sc_sbdsp) < 0) { 350 DPRINTF(("pas: couldn't reset card\n")); 351 return 0; 352 } 353 354 /* 355 * Cannot auto-discover DMA channel. 356 */ 357 if (!SB_DRQ_VALID(ia->ia_drq)) { 358 printf("pas: configured dma chan %d invalid\n", ia->ia_drq); 359 return 0; 360 } 361#ifdef NEWCONFIG 362 /* 363 * If the IRQ wasn't compiled in, auto-detect it. 364 */ 365 if (ia->ia_irq == IRQUNK) { 366 ia->ia_irq = isa_discoverintr(pasforceintr, aux); 367 sbdsp_reset(&sc->sc_sbdsp); 368 if (!SB_IRQ_VALID(ia->ia_irq)) { 369 printf("pas: couldn't auto-detect interrupt"); 370 return 0; 371 } 372 } else 373#endif 374 if (!SB_IRQ_VALID(ia->ia_irq)) { 375 printf("pas: configured irq chan %d invalid\n", ia->ia_irq); 376 return 0; 377 } 378 379 sc->sc_sbdsp.sc_irq = ia->ia_irq; 380 sc->sc_sbdsp.sc_drq = ia->ia_drq; 381 382 if (sbdsp_probe(&sc->sc_sbdsp) == 0) { 383 DPRINTF(("pas: sbdsp probe failed\n")); 384 return 0; 385 } 386 387 ia->ia_iosize = SB_NPORT; 388 return 1; 389} 390 391#ifdef NEWCONFIG 392void 393pasforceintr(aux) 394 void *aux; 395{ 396 static char dmabuf; 397 struct isa_attach_args *ia = aux; 398 int iobase = ia->ia_iobase; 399 400 /* 401 * Set up a DMA read of one byte. 402 * XXX Note that at this point we haven't called 403 * at_setup_dmachan(). This is okay because it just 404 * allocates a buffer in case it needs to make a copy, 405 * and it won't need to make a copy for a 1 byte buffer. 406 * (I think that calling at_setup_dmachan() should be optional; 407 * if you don't call it, it will be called the first time 408 * it is needed (and you pay the latency). Also, you might 409 * never need the buffer anyway.) 410 */ 411 at_dma(1, &dmabuf, 1, ia->ia_drq); 412 if (pas_wdsp(iobase, SB_DSP_RDMA) == 0) { 413 (void)pas_wdsp(iobase, 0); 414 (void)pas_wdsp(iobase, 0); 415 } 416} 417#endif 418 419/* 420 * Attach hardware to driver, attach hardware driver to audio 421 * pseudo-device driver . 422 */ 423void 424pasattach(parent, self, aux) 425 struct device *parent, *self; 426 void *aux; 427{ 428 register struct pas_softc *sc = (struct pas_softc *)self; 429 struct isa_attach_args *ia = (struct isa_attach_args *)aux; 430 register int iobase = ia->ia_iobase; 431 int err; 432 433 sc->sc_iobase = iobase; 434 sc->sc_ih = isa_intr_establish(ia->ia_irq, ISA_IST_EDGE, ISA_IPL_AUDIO, 435 sbdsp_intr, &sc->sc_sbdsp); 436 437 printf(" ProAudio Spectrum %s [rev %d] ", pasnames[sc->model], sc->rev); 438 439 sbdsp_attach(&sc->sc_sbdsp); 440 441 sprintf(pas_device.name, "pas,%s", pasnames[sc->model]); 442 sprintf(pas_device.version, "%d", sc->rev); 443 444 if ((err = audio_hardware_attach(&pas_hw_if, &sc->sc_sbdsp)) != 0) 445 printf("pas: could not attach to audio pseudo-device driver (%d)\n", err); 446} 447 448int 449pasopen(dev, flags) 450 dev_t dev; 451 int flags; 452{ 453 struct pas_softc *sc; 454 int unit = AUDIOUNIT(dev); 455 456 if (unit >= pascd.cd_ndevs) 457 return ENODEV; 458 459 sc = pascd.cd_devs[unit]; 460 if (!sc) 461 return ENXIO; 462 463 return sbdsp_open(&sc->sc_sbdsp, dev, flags); 464} 465 466int 467pas_getdev(addr, retp) 468 void *addr; 469 struct audio_device *retp; 470{ 471 *retp = pas_device; 472 return 0; 473} 474