File:  [NeXTSTEP 3.3 examples] / Examples / DriverKit / SoundBlaster8 / SoundBlaster8_reloc.tproj / SoundBlaster8Inline.h
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 17:49:02 2018 UTC (8 years, 1 month ago) by root
Branches: NeXT, MAIN
CVS tags: NeXTSTEP33, HEAD
Sample Programs from NeXSTEP 3.3

/*
 * Copyright (c) 1994 NeXT Computer, Inc.  All rights reserved. 
 *
 * HISTORY
 * 4-Mar-94    Rakesh Dubey at NeXT
 *      Created. 
 */

/*
 * Base address. 
 */
static unsigned int sbBaseRegisterAddress = 0;

/*
 * Register addresses. The base register addressess are determined at
 * run-time. 
 */

static unsigned int sbResetReg = 0;
static unsigned int sbReadDataReg = 0;
static unsigned int sbWriteDataOrCommandReg = 0;

static unsigned int sbWriteBufferStatusReg = 0;
static unsigned int sbDataAvailableStatusReg = 0;

static __inline__
void
assignDSPRegAddresses(void)
{
    sbResetReg = 
        (sbBaseRegisterAddress + SB_DSP_RESET_OFFSET);
    sbReadDataReg = 
        (sbBaseRegisterAddress + SB_DSP_READ_DATA_OFFSET);
    sbWriteDataOrCommandReg = 
        (sbBaseRegisterAddress + SB_DSP_WRITE_DATA_OR_COMMAND_OFFSET);
        
    sbWriteBufferStatusReg = 
        (sbBaseRegisterAddress + SB_DSP_WRITE_BUFFER_STATUS_OFFSET);
    sbDataAvailableStatusReg = 
        (sbBaseRegisterAddress + SB_DSP_DATA_AVAILABLE_STATUS_OFFSET);
}


/*
 * Print what is being written. 
 */
static __inline__
void
outbV(unsigned int address, unsigned int data)
{
#ifdef DEBUG
    IOLog("SoundBlaster8: Writing %x at address %x\n", data, address);
#endif DEBUG
    outb(address, data);
}

static unsigned int sbMixerAddressReg = 0;
static unsigned int sbMixerDataReg = 0;

static __inline__
void
assignMixerRegAddresses(void)
{
    sbMixerAddressReg = 
        (sbBaseRegisterAddress + SB_MIXER_ADDRESS_OFFSET);
    sbMixerDataReg = 
        (sbBaseRegisterAddress + SB_MIXER_DATA_OFFSET);
}

/*
 * Shadow registers for volume. Add more to this list when necessary. 
 */

static sbStereoMixerRegister_t volMaster = 	{0};
static sbStereoMixerRegister_t volFM = 		{0};
static sbStereoMixerRegister_t volLine = 	{0};
static sbStereoMixerRegister_t volVoc = 	{0};

static sbMonoMixerRegister_t volMic = 		{0};
static sbMonoMixerRegister_t volCD = 		{0};

static sbRecordingMode_t sbRecord = 		{0};
static sbPlaybackMode_t sbPlayback = 		{0};


#define MAX_WAIT_FOR_DATA_AVAILABLE     	2000
#define SB_WAIT_DELAY                   	10
#define SB_RESET_DELAY                  	100

static  __inline__
BOOL
dspReadWait()
{
    int     i;
    unsigned int val;

    for (i = 0; i < MAX_WAIT_FOR_DATA_AVAILABLE; i++) {
	IODelay(SB_WAIT_DELAY);
	val = inb(sbDataAvailableStatusReg);
	if (val & 0x080)   /* MSB == 1 before reading */
	    return YES;
    }

    /* Reset DSP, hopefully we will recover. */
    outbV(sbResetReg, 0x01);
    IODelay(SB_ADDRESS_WRITE_DELAY);
    outbV(sbResetReg, 0x00);
    IODelay(SB_ADDRESS_WRITE_DELAY);
    IODelay(SB_RESET_DELAY);

#ifdef DEBUG
    IOLog("SoundBlaster8: DSP not ready for reading!\n");
#endif DEBUG

    return NO;
}

static __inline__
BOOL 
dspWriteWait()
{
    int     i;
    unsigned int val;

    for (i = 0; i < MAX_WAIT_FOR_DATA_AVAILABLE; i++) {
	IODelay(SB_WAIT_DELAY);
	val = inb(sbWriteBufferStatusReg);
	if (!(val & 0x080))	/* MSB == 0 before writing */
	    return YES;
    }

    /* Reset DSP */
    outbV(sbResetReg, 0x01);
    IODelay(SB_ADDRESS_WRITE_DELAY);
    outbV(sbResetReg, 0x00);
    IODelay(SB_ADDRESS_WRITE_DELAY);
    IODelay(SB_RESET_DELAY);

#ifdef DEBUG
    IOLog("SoundBlaster8: DSP not ready for writing!\n");
#endif DEBUG

    return NO;
}

/*
 * Send some data or command to SoundBlaster8 DSP. 
 */
static
BOOL
writeToDSP(unsigned int dataOrCommand)
{
    outbV(sbWriteDataOrCommandReg, dataOrCommand);
    IODelay(SB_DATA_WRITE_DELAY);

#ifdef DEBUG
    //IOLog("SoundBlaster8: Wrote DSP command %x\n", dataOrCommand);
#endif DEBUG

    return YES;
}

/*
 * Read from the SoundBlaster DSP. 
 */
static
unsigned int
readFromDSP(void)
{
    unsigned int val;
    
    val = inb(sbReadDataReg);
    IODelay(SB_DATA_READ_DELAY);

#ifdef DEBUG
    IOLog("SoundBlaster8: read from DSP %x\n", val);
#endif DEBUG

    return val;
}
    
/*
 * Function to read from the Mixer registers. 
 */
static __inline__
unsigned int
inbIXMixer(unsigned int address)
{
    unsigned int val = 0xff;
        
    outbV(sbMixerAddressReg, address);
    IODelay(SB_ADDRESS_WRITE_DELAY);
    val = inb(sbMixerDataReg);
    
#ifdef DEBUG
    IOLog("SoundBlaster8: Mixer address %x. Read %x\n", address, val);
#endif DEBUG
    return val;
}

/*
 * Function to write to the Mixer registers. 
 */
static  __inline__
void
outbIXMixer(unsigned int address, unsigned int val)
{
    outbV(sbMixerAddressReg, address);
    IODelay(SB_ADDRESS_WRITE_DELAY);
    outbV(sbMixerDataReg, val);
    IODelay(SB_DATA_WRITE_DELAY);

#ifdef DEBUG
    IOLog("SoundBlaster8: Mixer address %x. Wrote %x\n", address, val);
#endif DEBUG
}

/*
 * Initialize DSP registers. There aren't any.
 */

static __inline__
void
initDSPRegisters(void)
{
}

/*
 * Initialize the registers on the Mixer. 
 */

static __inline__
void
initMixerRegisters(void)
{
    if (sbCardType.mixerPresent == NO)
        return;

#ifdef DEBUG
    IOLog("SoundBlaster8: Initializing mixer registers.\n");
#endif DEBUG

    /*
     * First set the volume controlling registers to their default values. 
     */
    volMaster.reg.left = 10;
    volMaster.reg.right = 10;
    outbIXMixer(MC_MASTER_VOLUME, volMaster.data);
    
    volFM.reg.left = 0;
    volFM.reg.right = 0;
    outbIXMixer(MC_FM_VOLUME, volFM.data);
    
    volCD = 0;
    outbIXMixer(MC_CD_VOLUME, volCD);
    
    volLine.reg.left = 0;
    volLine.reg.right = 0;
    outbIXMixer(MC_LINE_VOLUME, volLine.data);
    
    volVoc.reg.left = 10;
    volVoc.reg.right = 10;
    outbIXMixer(MC_VOC_VOLUME, volVoc.data);
    
    /* Microphone can go only upto 7. */
    volMic = 6;
    outbIXMixer(MC_MICROPHONE_VOLUME, volMic);
    
    /* 
     * Now set the record and playback mode registers.
     */
    sbRecord.data = 0;
    sbRecord.reg.source = SB_RECORD_SOURCE_MIC;
    sbRecord.reg.inputFilter = SB_RECORD_ANFI_OFF;
    sbRecord.reg.highFreq = SB_RECORD_FREQ_HIGH;
    outbIXMixer(MC_RECORD_CONTROL, sbRecord.data);
    
    sbPlayback.data = 0;
    sbPlayback.reg.outputFilter = SB_PLAYBACK_DNFI_OFF;
    sbPlayback.reg.stereo = SB_PLAYBACK_STEREO;
    outbIXMixer(MC_PLAYBACK_CONTROL, sbPlayback.data);
}

/*
 * Input can be either microphone level or line level. We don't support other
 * inputs. 
 */

static __inline__
void
setInputLevel(unsigned int level)
{
    if (sbCardType.mixerPresent == NO)
        return;
        
    if (level == LINE_LEVEL_INPUT)      {
        sbRecord.reg.source = SB_RECORD_SOURCE_LINE;
    } else {
        sbRecord.reg.source = SB_RECORD_SOURCE_MIC;
    }
    
    outbIXMixer(MC_RECORD_CONTROL, sbRecord.data);
}

/*
 * Output level cannot be changed. 
 */

static __inline__
void
setOutputLevel(unsigned int channel, unsigned int level)
{
#ifdef DEBUG
    IOLog("SoundBlaster8: Audio output level is fixed.\n");
#endif DEBUG
}

/*
 * Initialize the hardware registers. 
 */
static __inline__
void 
initRegisters(void)
{
    initDSPRegisters();
    initMixerRegisters();
}


/*
 * These two routines are used to avoid clicks. 
 */

static __inline__
void 
muteOutput(void)
{
    sbStereoMixerRegister_t volMaster = {0};
    
    if (sbCardType.mixerPresent == NO)
        return;
                
    volMaster.reg.left = 0;
    volMaster.reg.right = 0;
    
    outbIXMixer(MC_MASTER_VOLUME, volMaster.data);
}

/*
 * This takes it back to the old values. So it is not exactly unmute.
 */
static __inline__
void 
unMuteOutput(void)
{
    if (sbCardType.mixerPresent == NO)
        return;
        
    outbIXMixer(MC_MASTER_VOLUME, volMaster.data);
}

/*
 * This routine does a quick reset of the card. This is needed because
 * apparently the SoundBlaster8 cards need to be reset if you go from the
 * high speed to the low speed mode (wonderful world of hardware). 
 */

static __inline__
void
resetDSPQuick(void)
{
    outbV(sbResetReg, 0x01);
    IODelay(SB_ADDRESS_WRITE_DELAY);
    outbV(sbResetReg, 0x00);
    IODelay(SB_ADDRESS_WRITE_DELAY);
    
    /* It takes about 100us to reset */
    dspReadWait();
    if (readFromDSP() != 0xaa)    {
        IOLog("SoundBlaster8: Can not reset DSP.\n");
    }
}


#define SB_DELAY                       		100
#define MAX_RESET_WAIT                  	1000

/*
 * Reset the SoundBlaster card. This routine also detects if the card is
 * present and the type of card. 
 */
static __inline__
void
resetDSP(void)
{
    unsigned int val;

    /*
     * Assume no sound card in the system. 
     */
    sbCardType.version = SB_NONE;
    sbCardType.name = "";
    sbCardType.majorVersion = 0;
    sbCardType.minorVersion = 0;
    sbCardType.mixerPresent = NO;
    
    outbV(sbResetReg, 0x01);
    IODelay(SB_ADDRESS_WRITE_DELAY);
    outbV(sbResetReg, 0x00);
    IODelay(SB_ADDRESS_WRITE_DELAY);
    
    /* Now we can read the data. */
    dspReadWait();
    val = readFromDSP();
    if (val == 0xaa)    {
#ifdef DEBUG
        IOLog("SoundBlaster8: DSP detected.\n");
#endif DEBUG
        IOSleep(1);
    } else {
#ifdef DEBUG
        IOLog("SoundBlaster8: Read ID %x is wrong.\n", val);
        IOLog("SoundBlaster8: SoundBlaster not detected at address 0x%0x.\n", 
                sbBaseRegisterAddress);
#endif DEBUG
        return;
    }

    /*
     * We have a SoundBlaster card. We will upgrade it to a pro if we detect
     * a mixer as well. 
     */
    sbCardType.version = SB_CLASSIC;

    /*
     * Another confirmatory test here. This is not documented in the SB SDK
     * so it might fail on some compatible cards. Maybe we should just print
     * a warning message if this test fails. 
     */
    dspWriteWait();
    writeToDSP(DC_INVERT_BYTE);
    
    dspWriteWait();
    writeToDSP(0x43);   /* Send some test pattern. */
    
    dspReadWait();
    val = readFromDSP();
    
    if (val == 0xbc)    {
#ifdef DEBUG
        IOLog("SoundBlaster8: Invert test passed.\n");
#endif DEBUG
    } else      {
#ifdef DEBUG
        IOLog("SoundBlaster8: Invert test failed!!\n");
        IOLog("SoundBlaster8: SoundBlaster not detected at address 0x%0x.\n", 
                sbBaseRegisterAddress);
#endif DEBUG
    }

    /*
     * Reset the DSP here because sometimes you may get crazy values as
     * version. So just to be on the safe side.. 
     */
    resetDSPQuick();
    
    /*
     * Now we know that a SoundBlaster or compatible card exists. We need to
     * find the version number to decide the type of card.
     */
    dspWriteWait();
    writeToDSP(DC_GET_VERSION);

    dspReadWait();
    sbCardType.majorVersion = readFromDSP() & 0x0f;
    sbCardType.minorVersion = readFromDSP() & 0x0f;
    
#ifdef DEBUG
    //IOLog("SoundBlaster8: Major 0x%x Minor 0x%x\n", sbCardType.majorVersion, sbCardType.minorVersion);
#endif DEBUG

    /*
     * Upgrade the card to SB_20 or SB_16 depending upon what the version
     * number reads. 
     */
    if (sbCardType.majorVersion >= 2)
        sbCardType.version = SB_20;
 
    if (sbCardType.majorVersion >= 4)
        sbCardType.version = SB_16;
}



static __inline__
void
resetMixer(void)
{
    unsigned int val1, val2;
    
    /*
     * Reset the mixer by sending zero to both address and data ports.
     */
     outbIXMixer(0x0, 0x0);

    /*
     * Now try to write and then read from one of the mixer registers. 
     */
     outbIXMixer(MC_MASTER_VOLUME, 0x15);
     outbIXMixer(MC_MICROPHONE_VOLUME, 0x13);
     
     val1 = inbIXMixer(MC_MASTER_VOLUME);
     val2 = inbIXMixer(MC_MICROPHONE_VOLUME);

    if ((val1 == 0x15) && (val2 == 0x13)) {
	sbCardType.mixerPresent = YES;
    } else {
	sbCardType.mixerPresent = NO;
    }
                
    /*
     * Try once more, so that we are really sure. 
     */
     outbIXMixer(MC_LINE_VOLUME, 0x17);
     outbIXMixer(MC_FM_VOLUME, 0x19);

     val1 = inbIXMixer(MC_LINE_VOLUME);
     val2 = inbIXMixer(MC_FM_VOLUME);

    if ((val1 == 0x17) && (val2 == 0x19)) {
	sbCardType.mixerPresent = YES;
    } else {
	sbCardType.mixerPresent = NO;
    }

    /*
     * We have a pro card if we found the mixer. 
     */
    if (sbCardType.mixerPresent == YES) {
        sbCardType.version = SB_PRO;
        outbIXMixer(0x0, 0x0);          /* reset the mixer to a good state */
    }
     
#ifdef DEBUG
     if (sbCardType.mixerPresent == YES)
        IOLog("SoundBlaster8: Mixer detected.\n");
     else
        IOLog("SoundBlaster8: Mixer not detected.\n");
#endif DEBUG
}


/*
 * Reset all hardware and bring us back to the default state. 
 */
static __inline__
void 
resetHardware(void)
{
    resetDSP();
    resetMixer();
    initRegisters();
}

/*
 * There seems to be no way to change input gain. 
 */
static  __inline__
void 
setInputGain(unsigned int channel, unsigned int gain)
{
    if (sbCardType.mixerPresent == NO)
        return;
        
    volMic = gain;
    outbIXMixer(MC_MICROPHONE_VOLUME, volMic);
    
#ifdef DEBUG
    IOLog("SoundBlaster8: set input gain %d\n", gain);
#endif DEBUG
}

static  __inline__
void
setOutputAttenuation(unsigned int channel, unsigned int attenuation)
{
    if (sbCardType.mixerPresent == NO)
        return;
        
    if (channel == LEFT_CHANNEL) {
        volMaster.reg.left = attenuation;
        outbIXMixer(MC_MASTER_VOLUME, volMaster.data);

    } else {
        volMaster.reg.right = attenuation;
        outbIXMixer(MC_MASTER_VOLUME, volMaster.data);
    }
#ifdef DEBUG
    IOLog("SoundBlaster8: set output attenuation %d\n", attenuation);
#endif DEBUG
}

/*
 * This will not work for SB16 running in SBPro compatibility mode. The mixer
 * registers need to be written to mute output. 
 */

static __inline__
void
enableAudioOutput(BOOL enable)
{
    dspWriteWait();
    if (enable) {
        writeToDSP(DC_TURN_ON_SPEAKER);
    } else {
        writeToDSP(DC_TURN_OFF_SPEAKER);
    }
}

static  __inline__
void    setSampleBufferCounter(unsigned int count)
{
    if (!lowSpeedDMA)	{
	dspWriteWait();
        writeToDSP(DC_SET_BLOCK_SIZE);
    } 
    
    count -= 1;
    dspWriteWait();
    writeToDSP(count & 0x0ff);
    dspWriteWait();
    writeToDSP((count >> 8) & 0x0ff);
#ifdef DEBUG
    //IOLog("SoundBlaster8: buffer counter set to %x\n", count);
#endif DEBUG
}

/*
 * Start DMA. Command patterns are different depending upon whether we are
 * doing low speed or high speed transfers. 
 */
     
static __inline__
void startDMA(unsigned int direction)
{       
    dspWriteWait();
    if (lowSpeedDMA)    {
        if (direction == DMA_DIRECTION_IN) 
            writeToDSP(DC_START_LS_DMA_ADC_8);
        else
            writeToDSP(DC_START_LS_DMA_DAC_8);
    } else {
        if (direction == DMA_DIRECTION_IN) 
            writeToDSP(DC_START_HS_DMA_ADC_8);
        else
            writeToDSP(DC_START_HS_DMA_DAC_8);
    }
}

/* No can do */
static __inline__
void enableCodecInterrupts(void)
{
}

static __inline__
void disableCodecInterrupts(void)
{
}

static __inline__
void stopDMA(void)
{
    writeToDSP(DC_HALT_DMA);
}

/*
 * This routine will stop input dma. 
 */
static __inline__
void stopDMAInput(void)
{
    stopDMA();
}

/*
 * Likewise, but for output dma. 
 */
static __inline__
void stopDMAOutput(void)
{
    stopDMA();
}

/*
 * Select between DSP_MONO_MODE and DSP_STEREO_MODE mode. Note that stereo
 * recording is undocumented so it could potentially break on some clone
 * cards. 
 */
static __inline__
void setCodecDataMode(unsigned int mode, unsigned int dir)
{
    if (sbCardType.mixerPresent == NO)
        return;
        
    if (dir == DMA_DIRECTION_OUT) {
	if (mode == DSP_STEREO_MODE) {
	    sbPlayback.reg.stereo = SB_PLAYBACK_STEREO;
	} else {
	    sbPlayback.reg.stereo = SB_PLAYBACK_MONO;
	}
	outbIXMixer(MC_PLAYBACK_CONTROL, sbPlayback.data);
    } else if (dir == DMA_DIRECTION_IN)	{
	dspWriteWait();
	if (mode == DSP_STEREO_MODE) {
	    writeToDSP(DC_RECORD_IN_STEREO);
	} else {
	    writeToDSP(DC_RECORD_IN_MONO);
	}
    }
}


static __inline__
void setCodecSamplingRate(unsigned int rate)
{
    unsigned int timeConstant;
    
    /* Sanity check. */
    if (rate < SB_MIN_SAMPLE_RATE)
        rate = SB_MIN_SAMPLE_RATE;
    else if (rate > SB_MAX_SAMPLE_RATE)
        rate = SB_MAX_SAMPLE_RATE;
        
    dspWriteWait();
    if (lowSpeedDMA)    {
        timeConstant = 256 - (1000*1000)/rate;
        writeToDSP(DC_SET_TIME_CONSTANT);
	dspWriteWait();
        writeToDSP(timeConstant);
#ifdef DEBUG
    IOLog("SoundBlaster8: Sample rate = %u, timeConstant = %x\n", rate, timeConstant);
#endif DEBUG
    } else {
        timeConstant = 65536 - (256*1000*1000)/rate;
        writeToDSP(DC_SET_TIME_CONSTANT);
	dspWriteWait();
        writeToDSP(timeConstant >> 8);
#ifdef DEBUG
    IOLog("SoundBlaster8: Sample rate = %u, timeConstant = %x\n", rate, timeConstant >> 8);
#endif DEBUG
    }
}

/*
 * We test here if the user supplied dma/irq selections are correct. Actually
 * it is more complicated than this because not all kinds of cards can use
 * all dma/irq combinations. We simply allow the superset and avoid
 * complicated version dependent verification. (Available interrupts are 3,
 * 5, 7 for SBPro and 5, 7, 10 for other kinds, clone cards may have slight
 * differences.) 
 */

static  __inline__
BOOL
checkSelectedDMAAndIRQ(unsigned int channel, unsigned int irq)
{
    BOOL status = YES;

    if ((channel != 0) && (channel != 1) && (channel != 3)) {
        IOLog("SoundBlaster8: Audio DMA channel is %d.\n", channel);
        IOLog("SoundBlaster8: Audio DMA channel must be one of 0, 1, 3.\n");
        status = NO;
    }
    if ((irq != 3) && (irq != 5) &&
        (irq != 7) && (irq != 10)) {
        IOLog("SoundBlaster8: Audio irq is %d.\n", irq);
        IOLog("SoundBlaster8: Audio IRQ must be one of 3, 5, 7, 10.\n");
        status = NO;
    }
    
    return status;
}


unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.