pas.c revision 1.14
1/* $NetBSD: pas.c,v 1.14 1996/03/17 00:53:47 thorpej 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)); 103int pas_getdev __P((void *, struct audio_device *)); 104 105 106/* 107 * Define our interface to the higher level audio driver. 108 */ 109 110struct audio_hw_if pas_hw_if = { 111 pasopen, 112 sbdsp_close, 113 NULL, 114 sbdsp_set_in_sr, 115 sbdsp_get_in_sr, 116 sbdsp_set_out_sr, 117 sbdsp_get_out_sr, 118 sbdsp_query_encoding, 119 sbdsp_set_encoding, 120 sbdsp_get_encoding, 121 sbdsp_set_precision, 122 sbdsp_get_precision, 123 sbdsp_set_channels, 124 sbdsp_get_channels, 125 sbdsp_round_blocksize, 126 sbdsp_set_out_port, 127 sbdsp_get_out_port, 128 sbdsp_set_in_port, 129 sbdsp_get_in_port, 130 sbdsp_commit_settings, 131 sbdsp_get_silence, 132 mulaw_expand, 133 mulaw_compress, 134 sbdsp_dma_output, 135 sbdsp_dma_input, 136 sbdsp_haltdma, 137 sbdsp_haltdma, 138 sbdsp_contdma, 139 sbdsp_contdma, 140 sbdsp_speaker_ctl, 141 pas_getdev, 142 sbdsp_setfd, 143 sbdsp_mixer_set_port, 144 sbdsp_mixer_get_port, 145 sbdsp_mixer_query_devinfo, 146 0, /* not full-duplex */ 147 0 148}; 149 150/* The Address Translation code is used to convert I/O register addresses to 151 be relative to the given base -register */ 152 153static char *pasnames[] = { 154 "", 155 "Plus", 156 "CDPC", 157 "16", 158 "16Basic" 159}; 160 161static struct audio_device pas_device = { 162 "PAS,??", 163 "", 164 "pas" 165}; 166 167/*XXX assume default I/O base address */ 168#define pasread(p) inb(p) 169#define paswrite(d, p) outb(p, d) 170 171void 172pasconf(int model, int sbbase, int sbirq, int sbdrq) 173{ 174 int i; 175 176 paswrite(0x00, INTERRUPT_MASK); 177 /* Local timer control register */ 178 paswrite(0x36, SAMPLE_COUNTER_CONTROL); 179 /* Sample rate timer (16 bit) */ 180 paswrite(0x36, SAMPLE_RATE_TIMER); 181 paswrite(0, SAMPLE_RATE_TIMER); 182 /* Local timer control register */ 183 paswrite(0x74, SAMPLE_COUNTER_CONTROL); 184 /* Sample count register (16 bit) */ 185 paswrite(0x74, SAMPLE_BUFFER_COUNTER); 186 paswrite(0, SAMPLE_BUFFER_COUNTER); 187 188 paswrite(P_C_PCM_MONO | P_C_PCM_DAC_MODE | 189 P_C_MIXER_CROSS_L_TO_L | P_C_MIXER_CROSS_R_TO_R, 190 PCM_CONTROL); 191 paswrite(S_M_PCM_RESET | S_M_FM_RESET | 192 S_M_SB_RESET | S_M_MIXER_RESET, SERIAL_MIXER); 193 194/*XXX*/ 195 paswrite(I_C_1_BOOT_RESET_ENABLE|1, IO_CONFIGURATION_1); 196 197 paswrite(I_C_2_PCM_DMA_DISABLED, IO_CONFIGURATION_2); 198 paswrite(I_C_3_PCM_IRQ_DISABLED, IO_CONFIGURATION_3); 199 200#ifdef BROKEN_BUS_CLOCK 201 paswrite(S_C_1_PCS_ENABLE | S_C_1_PCS_STEREO | S_C_1_PCS_REALSOUND | 202 S_C_1_FM_EMULATE_CLOCK, SYSTEM_CONFIGURATION_1); 203#else 204 paswrite(S_C_1_PCS_ENABLE | S_C_1_PCS_STEREO | S_C_1_PCS_REALSOUND, 205 SYSTEM_CONFIGURATION_1); 206#endif 207 208 /*XXX*/ 209 paswrite(0, SYSTEM_CONFIGURATION_2); 210 paswrite(0, SYSTEM_CONFIGURATION_3); 211 212 /* Sets mute off and selects filter rate of 17.897 kHz */ 213 paswrite(F_F_MIXER_UNMUTE | 0x01, FILTER_FREQUENCY); 214 215 if (model == PAS_16 || model == PAS_16BASIC) 216 paswrite(8, PRESCALE_DIVIDER); 217 else 218 paswrite(0, PRESCALE_DIVIDER); 219 220 paswrite(P_M_MV508_ADDRESS | P_M_MV508_PCM, PARALLEL_MIXER); 221 paswrite(5, PARALLEL_MIXER); 222 223 /* 224 * Setup SoundBlaster emulation. 225 */ 226 paswrite((sbbase >> 4) & 0xf, EMULATION_ADDRESS); 227 paswrite(E_C_SB_IRQ_translate[sbirq] | E_C_SB_DMA_translate[sbdrq], 228 EMULATION_CONFIGURATION); 229 paswrite(C_E_SB_ENABLE, COMPATIBILITY_ENABLE); 230 231 /* 232 * Set mid-range levels. 233 */ 234 paswrite(P_M_MV508_ADDRESS | P_M_MV508_MODE, PARALLEL_MIXER); 235 paswrite(P_M_MV508_LOUDNESS | P_M_MV508_ENHANCE_NONE, PARALLEL_MIXER); 236 237 paswrite(P_M_MV508_ADDRESS | P_M_MV508_MASTER_A, PARALLEL_MIXER); 238 paswrite(50, PARALLEL_MIXER); 239 paswrite(P_M_MV508_ADDRESS | P_M_MV508_MASTER_B, PARALLEL_MIXER); 240 paswrite(50, PARALLEL_MIXER); 241 242 paswrite(P_M_MV508_ADDRESS | P_M_MV508_MIXER | P_M_MV508_SB, PARALLEL_MIXER); 243 paswrite(P_M_MV508_OUTPUTMIX | 30, PARALLEL_MIXER); 244 245 paswrite(P_M_MV508_ADDRESS | P_M_MV508_MIXER | P_M_MV508_MIC, PARALLEL_MIXER); 246 paswrite(P_M_MV508_INPUTMIX | 30, PARALLEL_MIXER); 247} 248 249int pasprobe __P((struct device *, void *, void *)); 250void pasattach __P((struct device *, struct device *, void *)); 251 252struct cfattach pas_ca = { 253 sizeof(struct pas_softc), pasprobe, pasattach 254}; 255 256struct cfdriver pas_cd = { 257 NULL, "pas", DV_DULL 258}; 259 260/* 261 * Probe / attach routines. 262 */ 263 264/* 265 * Probe for the soundblaster hardware. 266 */ 267int 268pasprobe(parent, match, aux) 269 struct device *parent; 270 void *match, *aux; 271{ 272 register struct pas_softc *sc = match; 273 register struct isa_attach_args *ia = aux; 274 register int iobase; 275 u_char id, t; 276 277 /* 278 * WARNING: Setting an option like W:1 or so that disables 279 * warm boot reset of the card will screw up this detect code 280 * something fierce. Adding code to handle this means possibly 281 * interfering with other cards on the bus if you have something 282 * on base port 0x388. SO be forewarned. 283 */ 284 /* Talk to first board */ 285 outb(MASTER_DECODE, 0xbc); 286 /* Set base address */ 287 288#if 0 289 /* XXX Need to setup pseudo device */ 290 /* XXX What are good io addrs ? */ 291 if (iobase != PAS_DEFAULT_BASE) { 292 printf("pas: configured iobase %d invalid\n", iobase); 293 return 0; 294 } 295#else 296 /* Start out talking to native PAS */ 297 iobase = PAS_DEFAULT_BASE; 298#endif 299 300 outb(MASTER_DECODE, iobase >> 2); 301 /* One wait-state */ 302 paswrite(1, WAIT_STATE); 303 304 id = pasread(INTERRUPT_MASK); 305 if (id == 0xff || id == 0xfe) { 306 /* sanity */ 307 DPRINTF(("pas: bogus card id\n")); 308 return 0; 309 } 310 /* 311 * We probably have a PAS-series board, now check for a 312 * PAS2-series board by trying to change the board revision 313 * bits. PAS2-series hardware won't let you do this because 314 * the bits are read-only. 315 */ 316 t = id ^ 0xe0; 317 paswrite(t, INTERRUPT_MASK); 318 t = inb(INTERRUPT_MASK); 319 paswrite(id, INTERRUPT_MASK); 320 321 if (t != id) { 322 /* Not a PAS2 */ 323 printf("pas: detected card but PAS2 test failed\n"); 324 return 0; 325 } 326 /*XXX*/ 327 t = pasread(OPERATION_MODE_1) & 0xf; 328 sc->model = O_M_1_to_card[t]; 329 if (sc->model != 0) { 330 sc->rev = pasread(BOARD_REV_ID); 331 } 332 else { 333 DPRINTF(("pas: bogus model id\n")); 334 return 0; 335 } 336 337 if (sc->model >= 0) { 338 if (ia->ia_irq == IRQUNK) { 339 printf("pas: sb emulation requires known irq\n"); 340 return (0); 341 } 342 pasconf(sc->model, ia->ia_iobase, ia->ia_irq, 1); 343 } else { 344 DPRINTF(("pas: could not probe pas\n")); 345 return (0); 346 } 347 348 /* Now a SoundBlaster */ 349 sc->sc_iobase = ia->ia_iobase; 350 /* and set the SB iobase into the DSP as well ... */ 351 sc->sc_sbdsp.sc_iobase = ia->ia_iobase; 352 if (sbdsp_reset(&sc->sc_sbdsp) < 0) { 353 DPRINTF(("pas: couldn't reset card\n")); 354 return 0; 355 } 356 357 /* 358 * Cannot auto-discover DMA channel. 359 */ 360 if (!SB_DRQ_VALID(ia->ia_drq)) { 361 printf("pas: configured dma chan %d invalid\n", ia->ia_drq); 362 return 0; 363 } 364#ifdef NEWCONFIG 365 /* 366 * If the IRQ wasn't compiled in, auto-detect it. 367 */ 368 if (ia->ia_irq == IRQUNK) { 369 ia->ia_irq = isa_discoverintr(pasforceintr, aux); 370 sbdsp_reset(&sc->sc_sbdsp); 371 if (!SB_IRQ_VALID(ia->ia_irq)) { 372 printf("pas: couldn't auto-detect interrupt"); 373 return 0; 374 } 375 } else 376#endif 377 if (!SB_IRQ_VALID(ia->ia_irq)) { 378 printf("pas: configured irq chan %d invalid\n", ia->ia_irq); 379 return 0; 380 } 381 382 sc->sc_sbdsp.sc_irq = ia->ia_irq; 383 sc->sc_sbdsp.sc_drq = ia->ia_drq; 384 385 if (sbdsp_probe(&sc->sc_sbdsp) == 0) { 386 DPRINTF(("pas: sbdsp probe failed\n")); 387 return 0; 388 } 389 390 ia->ia_iosize = SB_NPORT; 391 return 1; 392} 393 394#ifdef NEWCONFIG 395void 396pasforceintr(aux) 397 void *aux; 398{ 399 static char dmabuf; 400 struct isa_attach_args *ia = aux; 401 int iobase = ia->ia_iobase; 402 403 /* 404 * Set up a DMA read of one byte. 405 * XXX Note that at this point we haven't called 406 * at_setup_dmachan(). This is okay because it just 407 * allocates a buffer in case it needs to make a copy, 408 * and it won't need to make a copy for a 1 byte buffer. 409 * (I think that calling at_setup_dmachan() should be optional; 410 * if you don't call it, it will be called the first time 411 * it is needed (and you pay the latency). Also, you might 412 * never need the buffer anyway.) 413 */ 414 at_dma(DMAMODE_READ, &dmabuf, 1, ia->ia_drq); 415 if (pas_wdsp(iobase, SB_DSP_RDMA) == 0) { 416 (void)pas_wdsp(iobase, 0); 417 (void)pas_wdsp(iobase, 0); 418 } 419} 420#endif 421 422/* 423 * Attach hardware to driver, attach hardware driver to audio 424 * pseudo-device driver . 425 */ 426void 427pasattach(parent, self, aux) 428 struct device *parent, *self; 429 void *aux; 430{ 431 register struct pas_softc *sc = (struct pas_softc *)self; 432 struct isa_attach_args *ia = (struct isa_attach_args *)aux; 433 register int iobase = ia->ia_iobase; 434 int err; 435 436 sc->sc_iobase = iobase; 437 sc->sc_ih = isa_intr_establish(ia->ia_irq, IST_EDGE, IPL_AUDIO, 438 sbdsp_intr, &sc->sc_sbdsp); 439 440 printf(" ProAudio Spectrum %s [rev %d] ", pasnames[sc->model], sc->rev); 441 442 sbdsp_attach(&sc->sc_sbdsp); 443 444 sprintf(pas_device.name, "pas,%s", pasnames[sc->model]); 445 sprintf(pas_device.version, "%d", sc->rev); 446 447 if ((err = audio_hardware_attach(&pas_hw_if, &sc->sc_sbdsp)) != 0) 448 printf("pas: could not attach to audio pseudo-device driver (%d)\n", err); 449} 450 451int 452pasopen(dev, flags) 453 dev_t dev; 454 int flags; 455{ 456 struct pas_softc *sc; 457 int unit = AUDIOUNIT(dev); 458 459 if (unit >= pas_cd.cd_ndevs) 460 return ENODEV; 461 462 sc = pas_cd.cd_devs[unit]; 463 if (!sc) 464 return ENXIO; 465 466 return sbdsp_open(&sc->sc_sbdsp, dev, flags); 467} 468 469int 470pas_getdev(addr, retp) 471 void *addr; 472 struct audio_device *retp; 473{ 474 *retp = pas_device; 475 return 0; 476} 477