|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 1994 NeXT Computer, Inc. All rights reserved. ! 3: * ! 4: * HISTORY ! 5: * 4-Mar-94 Rakesh Dubey at NeXT ! 6: * Created. ! 7: */ ! 8: ! 9: #import "SoundBlaster8.h" ! 10: #import "SoundBlaster8Registers.h" ! 11: ! 12: #import <driverkit/generalFuncs.h> ! 13: ! 14: static const char codecDeviceName[] = "SoundBlaster8"; ! 15: static const char codecDeviceKind[] = "Audio"; ! 16: ! 17: static sbCardParameters_t sbCardType; // hardware type ! 18: static BOOL lowSpeedDMA; // different programming ! 19: ! 20: /* ! 21: * Include inline functions. ! 22: */ ! 23: #import "SoundBlaster8Inline.h" ! 24: ! 25: @implementation SoundBlaster8 ! 26: ! 27: /* ! 28: * Probe and initialize new instance ! 29: */ ! 30: + (BOOL) probe:deviceDescription ! 31: { ! 32: SoundBlaster8 *dev; ! 33: IORange *portRangeList; ! 34: int numPortRanges; ! 35: unsigned int baseAddress; ! 36: ! 37: #ifdef DEBUG ! 38: int i; ! 39: #endif DEBUG ! 40: ! 41: dev = [self alloc]; ! 42: if (dev == nil) ! 43: return NO; ! 44: ! 45: portRangeList = [deviceDescription portRangeList]; ! 46: numPortRanges = [deviceDescription numPortRanges]; ! 47: ! 48: if (numPortRanges < 1) ! 49: return NO; ! 50: ! 51: #ifdef DEBUG ! 52: for (i=0; i < numPortRanges; i++) { ! 53: IOLog("SoundBlaster8: port %x %d\n", ! 54: portRangeList[i].start, portRangeList[i].size); ! 55: } ! 56: #endif DEBUG ! 57: ! 58: baseAddress = portRangeList[0].start; ! 59: #ifdef DEBUG ! 60: IOLog("SoundBlaster8: Base address = 0x%x.\n", baseAddress); ! 61: #endif DEBUG ! 62: ! 63: ! 64: /* ! 65: * Check base address to verify if this is a legal address. ! 66: */ ! 67: ! 68: if ((baseAddress == SB_BASE_ADDRESS_1) || ! 69: (baseAddress == SB_BASE_ADDRESS_2) || ! 70: (baseAddress == SB_BASE_ADDRESS_3) || ! 71: (baseAddress == SB_BASE_ADDRESS_4) || ! 72: (baseAddress == SB_BASE_ADDRESS_5) || ! 73: (baseAddress == SB_BASE_ADDRESS_6)) { ! 74: sbBaseRegisterAddress = baseAddress; ! 75: } else { ! 76: IOLog("SoundBlaster8: Invalid port address 0x%0x.\n", baseAddress); ! 77: [dev free]; ! 78: return NO; ! 79: } ! 80: ! 81: /* ! 82: * Now assign all SB DSP and Mixer registers their addresses. ! 83: */ ! 84: assignDSPRegAddresses(); ! 85: assignMixerRegAddresses(); ! 86: ! 87: return [dev initFromDeviceDescription:deviceDescription] != nil; ! 88: } ! 89: ! 90: ! 91: - (BOOL)reset ! 92: { ! 93: unsigned int channel = [[self deviceDescription] channel]; ! 94: unsigned int interrupt = [[self deviceDescription] interrupt]; ! 95: ! 96: IOReturn ioReturn; ! 97: ! 98: [self setName:codecDeviceName]; ! 99: [self setDeviceKind:codecDeviceKind]; ! 100: ! 101: /* ! 102: * Are user selections valid? ! 103: */ ! 104: if (checkSelectedDMAAndIRQ(channel, interrupt) == NO) { ! 105: return NO; ! 106: } ! 107: ! 108: /* ! 109: * Now that all hardware parameters have been assigned and/or verified ! 110: * initialize the hardware. ! 111: */ ! 112: [self initializeHardware]; ! 113: ! 114: /* ! 115: * This driver is only for 8-bit Sound Blaster cards. If this is not one ! 116: * of these systems we quit since the test is fully reliable. ! 117: */ ! 118: ! 119: switch (sbCardType.version) { ! 120: case SB_CLASSIC: ! 121: sbCardType.name = "Classic"; ! 122: break; ! 123: case SB_20: ! 124: sbCardType.name = "2.0"; ! 125: break; ! 126: case SB_PRO: ! 127: sbCardType.name = "Pro"; ! 128: break; ! 129: case SB_16: ! 130: sbCardType.name = "Pro"; ! 131: sbCardType.version = SB_PRO; /* SB16 will emulate it */ ! 132: break; ! 133: default: { ! 134: IOLog("SoundBlaster8: Hardware not detected at port 0x%0x.\n", ! 135: sbBaseRegisterAddress); ! 136: return NO; ! 137: } ! 138: } ! 139: ! 140: IOLog("SoundBlaster8: Sound Blaster %s (ver %d.%d) at port 0x%0x.\n", ! 141: sbCardType.name, ! 142: sbCardType.majorVersion, sbCardType.minorVersion, ! 143: sbBaseRegisterAddress); ! 144: ! 145: /* ! 146: * Initialize DMA controller. ! 147: */ ! 148: ! 149: [self disableChannel: 0]; ! 150: ! 151: /* ! 152: * This call is only applicable in EISA systems. All dma channels ! 153: * that are available to this driver in ISA machines are 8-bit. So we do ! 154: * this setup only for EISA machines. ! 155: */ ! 156: if ([self isEISAPresent]) { ! 157: ioReturn = [self setDMATransferWidth:IO_8Bit forChannel:0]; ! 158: if (ioReturn != IO_R_SUCCESS) { ! 159: IOLog("SoundBlaster8: could not set transfer width to 8 bits, error %d.\n", ioReturn); ! 160: return NO; ! 161: } ! 162: } ! 163: ! 164: ioReturn = [self setTransferMode: IO_Single forChannel: 0]; ! 165: if (ioReturn != IO_R_SUCCESS) { ! 166: IOLog("%s: dma transfer mode error %d\n", [self name], ioReturn); ! 167: return NO; ! 168: } ! 169: ! 170: /* ! 171: * We will program the DMA controller in auto-init mode but the card is ! 172: * in single cycle mode. So at every interrupt we only need to reprogram ! 173: * the card. ! 174: */ ! 175: ioReturn = [self setAutoinitialize: YES forChannel: 0]; ! 176: if (ioReturn != IO_R_SUCCESS) { ! 177: IOLog("%s: dma auto initialize error %d", [self name], ioReturn); ! 178: return NO; ! 179: } ! 180: ! 181: return YES; ! 182: } ! 183: ! 184: ! 185: - (void) initializeHardware ! 186: { ! 187: resetHardware(); ! 188: } ! 189: ! 190: /* ! 191: * Converts gain (0 - 32768) into hardware supported gain (0 - 7). If the ! 192: * input source is line (not supported now), simply double the gain. ! 193: */ ! 194: ! 195: - (void)updateInputGainLeft ! 196: { ! 197: unsigned int gain = [self inputGainLeft]; ! 198: unsigned int left = 0; ! 199: ! 200: if (gain) ! 201: left = ((gain * MAX_INPUT_GAIN_MICROPHONE)/32768); ! 202: else ! 203: left = gain; // minimum input gain = 0 ! 204: ! 205: setInputGain(LEFT_CHANNEL, left); ! 206: #ifdef DEBUG ! 207: IOLog("%s: updateInputGainLeft %d based on gain %d\n", [self name],left, gain); ! 208: #endif DEBUG ! 209: } ! 210: ! 211: /* ! 212: * Converts gain (0 - 32768) into hardware supported gain (0 - 7) ! 213: */ ! 214: ! 215: - (void)updateInputGainRight ! 216: { ! 217: unsigned int gain = [self inputGainRight]; ! 218: unsigned int right = 0; ! 219: ! 220: if (gain) ! 221: right = ((gain * MAX_INPUT_GAIN_MICROPHONE)/32768); ! 222: else ! 223: right = gain; // minimum input gain = 0 ! 224: ! 225: setInputGain(RIGHT_CHANNEL, right); ! 226: #ifdef DEBUG ! 227: IOLog("%s: updateInputGainRight %d based on gain %d\n", [self name], right, gain); ! 228: #endif DEBUG ! 229: } ! 230: ! 231: - (void)updateOutputMute ! 232: { ! 233: enableAudioOutput(! [self isOutputMuted]); ! 234: } ! 235: ! 236: /* ! 237: * (0) - (-84) needs to be converted to hardware supported (0) - (15) ! 238: */ ! 239: - (void) updateOutputAttenuationLeft ! 240: { ! 241: unsigned int attenuation = [self outputAttenuationLeft] + 84; ! 242: unsigned int left = 0; ! 243: ! 244: left = ((attenuation * MAX_MASTER_OUTPUT_VOLUME)/84); ! 245: ! 246: setOutputAttenuation(LEFT_CHANNEL, left); ! 247: ! 248: #ifdef DEBUG ! 249: IOLog("%s: converted la: %d into %d\n", [self name], attenuation, left); ! 250: #endif DEBUG ! 251: } ! 252: ! 253: /* ! 254: * (0) - (-84) needs to be converted to hardware supported (0) - (15) ! 255: */ ! 256: - (void) updateOutputAttenuationRight ! 257: { ! 258: unsigned int attenuation = [self outputAttenuationRight] + 84; ! 259: unsigned int right = 0; ! 260: ! 261: right = ((attenuation * MAX_MASTER_OUTPUT_VOLUME)/84); ! 262: ! 263: setOutputAttenuation(RIGHT_CHANNEL, right); ! 264: ! 265: #ifdef DEBUG ! 266: IOLog("SoundBlaster8: converted ra: %d into %d\n", attenuation, right); ! 267: #endif DEBUG ! 268: } ! 269: ! 270: /* ! 271: * Program DSP. ! 272: */ ! 273: - (void)updateSampleRate ! 274: { ! 275: unsigned int rate; ! 276: unsigned int mode; ! 277: ! 278: rate = [self sampleRate]; ! 279: mode = ([self channelCount] == 2) ? DSP_STEREO_MODE : DSP_MONO_MODE; ! 280: ! 281: /* ! 282: * Programming sequence depends upon whether we are doing a low speed or ! 283: * high speed transfer. Rather messy, see SB SDK page 12-5. ! 284: */ ! 285: if (sbCardType.version == SB_CLASSIC) { ! 286: lowSpeedDMA = YES; ! 287: } else if (sbCardType.version == SB_20) { ! 288: if (currentDMADirection == DMA_DIRECTION_IN) ! 289: lowSpeedDMA = (rate < SB_20_LOW_SPEED_RECORD) ? YES : NO; ! 290: else ! 291: lowSpeedDMA = (rate < SB_20_LOW_SPEED_PLAYBACK) ? YES : NO; ! 292: } else if (sbCardType.version == SB_PRO) { ! 293: if (mode == DSP_STEREO_MODE) ! 294: rate *= 2; ! 295: lowSpeedDMA = (rate < SB_PRO_LOW_SPEED) ? YES : NO; ! 296: } ! 297: ! 298: setCodecDataMode(mode, currentDMADirection); ! 299: setCodecSamplingRate(rate); ! 300: } ! 301: ! 302: ! 303: /* ! 304: * Sets the DMA Counter Load register which decides when the next interrupt ! 305: * will arrive. ! 306: */ ! 307: - (void) setBufferCount:(int)count ! 308: { ! 309: setSampleBufferCounter(count); ! 310: } ! 311: ! 312: ! 313: - (IOReturn) enableAllInterrupts ! 314: { ! 315: enableCodecInterrupts(); ! 316: return [super enableAllInterrupts]; ! 317: } ! 318: ! 319: - (void) disableAllInterrupts ! 320: { ! 321: disableCodecInterrupts(); ! 322: [super disableAllInterrupts]; ! 323: } ! 324: ! 325: /* ! 326: * Return NO if the hardware does not support this particular playback/record ! 327: * request. The parameter scheme in soundkit does not fit the anarchy ! 328: * of PC world very well. ! 329: */ ! 330: - (BOOL)isValidRequest: (BOOL)isRead ! 331: { ! 332: unsigned int rate; ! 333: unsigned int mode; ! 334: unsigned int encoding; ! 335: ! 336: rate = [self sampleRate]; ! 337: encoding = [self dataEncoding]; ! 338: mode = ([self channelCount] == 2) ? DSP_STEREO_MODE : DSP_MONO_MODE; ! 339: ! 340: #ifdef DEBUG ! 341: IOLog("SoundBlaster8: rate: %d ", rate); ! 342: ! 343: if (mode == DSP_MONO_MODE) ! 344: IOLog("dataMode: mono "); ! 345: else if (mode == DSP_STEREO_MODE) ! 346: IOLog("dataMode: stereo "); ! 347: else ! 348: IOLog("dataMode: unknown "); ! 349: ! 350: if (encoding == NX_SoundStreamDataEncoding_Linear16) ! 351: IOLog("dataEncoding: linear 16\n"); ! 352: else if (encoding == NX_SoundStreamDataEncoding_Linear8) ! 353: IOLog("dataEncoding: linear 8\n"); ! 354: else if (encoding == NX_SoundStreamDataEncoding_Mulaw8) ! 355: IOLog("dataEncoding: mulaw 8\n"); ! 356: else if (encoding == NX_SoundStreamDataEncoding_Alaw8) ! 357: IOLog("dataEncoding: Alaw 8\n"); ! 358: else ! 359: IOLog("dataEncoding: unknown\n"); ! 360: #endif DEBUG ! 361: ! 362: if (sbCardType.version == SB_PRO) { ! 363: if ((mode == DSP_STEREO_MODE) && ! 364: (rate > SB_PRO_LOW_SPEED)) ! 365: return NO; ! 366: } ! 367: ! 368: if (sbCardType.version == SB_20) { ! 369: if (isRead && rate > SB_20_LOW_SPEED_RECORD) ! 370: return NO; ! 371: } ! 372: ! 373: if (sbCardType.version == SB_CLASSIC) { ! 374: if (isRead && rate > SB_CLASSIC_MAX_SPEED_RECORD) ! 375: return NO; ! 376: if (!isRead && rate > SB_CLASSIC_MAX_SPEED_PLAYBACK) ! 377: return NO; ! 378: } ! 379: ! 380: return YES; ! 381: } ! 382: ! 383: ! 384: - (BOOL) startDMAForChannel: (unsigned int) localChannel ! 385: read: (BOOL) isRead ! 386: buffer: (IOEISADMABuffer) buffer ! 387: bufferSizeForInterrupts: (unsigned int) bufferSize ! 388: { ! 389: IOReturn ioReturn; ! 390: ! 391: #ifdef DEBUG ! 392: IOLog("SoundBlaster8: startDMAForChannel\n"); ! 393: #endif DEBUG ! 394: ! 395: isValidRequest = [self isValidRequest:isRead]; ! 396: ! 397: interruptTimedOut = NO; ! 398: ! 399: if (isValidRequest == NO) { ! 400: IOLog("%s: unsupported %s mode.\n", [self name], ! 401: isRead ? "recording" : "playback"); ! 402: ! 403: if (isRead) ! 404: return YES; ! 405: else ! 406: return NO; ! 407: } ! 408: ! 409: if (isRead) ! 410: currentDMADirection = DMA_DIRECTION_IN; ! 411: else ! 412: currentDMADirection = DMA_DIRECTION_OUT; ! 413: ! 414: /* ! 415: * Output must be off while recording. ! 416: */ ! 417: if (![self isOutputMuted]) ! 418: enableAudioOutput(isRead ? NO : YES); ! 419: ! 420: [self updateSampleRate]; ! 421: ! 422: dmaDescriptorSize = bufferSize; // used by interrupt handler ! 423: ! 424: #ifdef DEBUG ! 425: if (lowSpeedDMA) ! 426: IOLog("SoundBlaster8: starting low speed DMA "); ! 427: else ! 428: IOLog("SoundBlaster8: starting high speed DMA "); ! 429: ! 430: if (isRead) ! 431: IOLog("input.\n"); ! 432: else ! 433: IOLog("output.\n"); ! 434: #endif DEBUG ! 435: ! 436: ioReturn = [self startDMAForBuffer: buffer channel: localChannel]; ! 437: ! 438: if (ioReturn != IO_R_SUCCESS) { ! 439: IOLog("%s: could not start DMA channel error %d\n", ! 440: [self name], ioReturn); ! 441: return NO; ! 442: } ! 443: ! 444: ioReturn = [self enableChannel: localChannel]; ! 445: ! 446: if (ioReturn != IO_R_SUCCESS) { ! 447: IOLog("%s: could not enable DMA channel error %d\n", ! 448: [self name], ioReturn); ! 449: return NO; ! 450: } ! 451: ! 452: (void) [self enableAllInterrupts]; ! 453: ! 454: /* ! 455: * The order is important here. See SB SDK page 12-8 and 12-13. ! 456: */ ! 457: if (lowSpeedDMA) { ! 458: if (isRead) { ! 459: startDMA(DMA_DIRECTION_IN); ! 460: } else { ! 461: startDMA(DMA_DIRECTION_OUT); ! 462: } ! 463: [self setBufferCount: dmaDescriptorSize]; ! 464: } else { ! 465: [self setBufferCount: dmaDescriptorSize]; ! 466: if (isRead) { ! 467: startDMA(DMA_DIRECTION_IN); ! 468: } else { ! 469: startDMA(DMA_DIRECTION_OUT); ! 470: } ! 471: } ! 472: ! 473: return YES; ! 474: } ! 475: ! 476: - (void) stopDMAForChannel: (unsigned int) localChannel read: (BOOL) isRead ! 477: { ! 478: #ifdef DEBUG ! 479: IOLog("SoundBlaster8: stopDMAForChannel\n"); ! 480: #endif DEBUG ! 481: ! 482: /* ! 483: * DMA request was denied bacause of lack of hardware support. ! 484: */ ! 485: if (isValidRequest == NO) ! 486: return; ! 487: ! 488: if (isRead) { ! 489: stopDMAInput(); ! 490: } else { ! 491: stopDMAOutput(); ! 492: } ! 493: ! 494: (void)[self disableAllInterrupts]; ! 495: ! 496: /* ! 497: * Disable channel only after disabling capture and playback. ! 498: */ ! 499: [self disableChannel: localChannel]; ! 500: ! 501: if (isRead == NO) ! 502: enableAudioOutput(NO); ! 503: ! 504: /* ! 505: * Reset DSP to stop high speed DMA transfer. This is necessary since the ! 506: * current "DMA block" might be continuing in case the transfer was ! 507: * interrupted. ! 508: */ ! 509: if (lowSpeedDMA == NO) { ! 510: resetDSPQuick(); ! 511: } ! 512: } ! 513: ! 514: static void clearInterrupts(void) ! 515: { ! 516: /* ! 517: * Acknowledge and clear the interrupt. ! 518: */ ! 519: ! 520: inb(sbDataAvailableStatusReg); ! 521: } ! 522: ! 523: - (IOAudioInterruptClearFunc) interruptClearFunc ! 524: { ! 525: return clearInterrupts; ! 526: } ! 527: ! 528: - (void) interruptOccurredForInput: (BOOL *) serviceInput ! 529: forOutput: (BOOL *) serviceOutput ! 530: { ! 531: #ifdef DEBUG ! 532: IOLog("SoundBlaster8: handleHardwareInterrupt\n"); ! 533: #endif DEBUG ! 534: ! 535: /* ! 536: * Acknowledge and clear the interrupt. ! 537: */ ! 538: ! 539: inb(sbDataAvailableStatusReg); ! 540: ! 541: /* ! 542: * We do not have simultaneous playback and record in SB. ! 543: */ ! 544: if (currentDMADirection == DMA_DIRECTION_OUT) ! 545: *serviceOutput = YES; ! 546: else ! 547: *serviceInput = YES; ! 548: ! 549: if (lowSpeedDMA) { ! 550: if (currentDMADirection == DMA_DIRECTION_IN) { ! 551: startDMA(DMA_DIRECTION_IN); ! 552: } else { ! 553: startDMA(DMA_DIRECTION_OUT); ! 554: } ! 555: [self setBufferCount: dmaDescriptorSize]; /* needed here */ ! 556: } else { ! 557: //[self setBufferCount: dmaDescriptorSize]; /* but not here */ ! 558: if (currentDMADirection == DMA_DIRECTION_IN) { ! 559: startDMA(DMA_DIRECTION_IN); ! 560: } else { ! 561: startDMA(DMA_DIRECTION_OUT); ! 562: } ! 563: } ! 564: } ! 565: ! 566: /* ! 567: * This routine will be called if interrupts are not being received. Some ! 568: * cards seem to lock up once in a while. ! 569: */ ! 570: - (void) timeoutOccurred ! 571: { ! 572: #ifdef DEBUG ! 573: IOLog("%s: timeout occurred.\n", [self name]); ! 574: #endif DEBUG ! 575: ! 576: if (interruptTimedOut == NO) { ! 577: resetDSPQuick(); ! 578: interruptTimedOut = YES; // reset only once ! 579: } ! 580: } ! 581: ! 582: /* ! 583: * Choose between different input sources. ! 584: */ ! 585: ! 586: - (void)setAnalogInputSource:(NXSoundParameterTag) val ! 587: { ! 588: if (val == NX_SoundDeviceAnalogInputSource_Microphone) { ! 589: setInputLevel(MICROPHONE_LEVEL_INPUT); ! 590: } else if (val == NX_SoundDeviceAnalogInputSource_LineIn) { ! 591: setInputLevel(LINE_LEVEL_INPUT); ! 592: } else { ! 593: setInputLevel(MICROPHONE_LEVEL_INPUT); // default ! 594: } ! 595: } ! 596: ! 597: /* ! 598: * Parameter access. ! 599: */ ! 600: ! 601: - (BOOL)acceptsContinuousSamplingRates ! 602: { ! 603: return YES; ! 604: } ! 605: ! 606: - (void)getSamplingRatesLow:(int *)lowRate ! 607: high:(int *)highRate ! 608: { ! 609: *lowRate = 4000; ! 610: *highRate = 44100; ! 611: } ! 612: ! 613: - (void)getSamplingRates:(int *)rates ! 614: count:(unsigned int *)numRates ! 615: { ! 616: /* Return some supported rates */ ! 617: rates[0] = 4000; ! 618: rates[1] = 8000; ! 619: rates[2] = 11025; ! 620: rates[3] = 22050; ! 621: rates[4] = 44100; ! 622: *numRates = 5; ! 623: } ! 624: ! 625: - (void)getDataEncodings: (NXSoundParameterTag *)encodings ! 626: count:(unsigned int *)numEncodings ! 627: { ! 628: encodings[0] = NX_SoundStreamDataEncoding_Linear8; ! 629: *numEncodings = 1; ! 630: } ! 631: ! 632: - (unsigned int)channelCountLimit ! 633: { ! 634: return (sbCardType.version == SB_PRO) ? 2 : 1; /* stereo and mono */ ! 635: } ! 636: ! 637: @end ! 638:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.