/* * AppleTexasAudio.cpp * Apple02Audio * * Created by nthompso on Tue Jul 03 2001. * * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * The contents of this file constitute Original Code as defined in and * are subject to the Apple Public Source License Version 1.1 (the * "License"). You may not use this file except in compliance with the * License. Please obtain a copy of the License at * http://www.apple.com/publicsource and read it before using this file. * * This Original Code and all software distributed under the License are * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ * * This file contains a template for an Apple02Audio based driver. * The driver is derived from the Apple02Audio class. * */ #include #include #include #include #include #include #include #include #include "AudioI2SHardwareConstants.h" #include "AppleTexasAudio.h" #include "AppleTexasEQPrefs.cpp" #define super Apple02Audio //#define durationMillisecond 1000 // number of microseconds in a millisecond OSDefineMetaClassAndStructors(AppleTexasAudio, Apple02Audio) // Globals in this file EQPrefsPtr gEQPrefs = &theEQPrefs; //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #pragma mark +UNIX LIKE FUNCTIONS // init, free, probe are the "Classical Unix driver functions" that you'll like as not find // in other device drivers. Note that there are no start and stop methods. The code for start // effectively moves to the sndHWInitialize routine. Also note that the initHardware method // effectively does very little other than calling the inherited method, which in turn calls // sndHWInitialize, so all of the init code should be in the sndHWInitialize routine. // -------------------------------------------------------------------------- // ::init() // call into superclass and initialize. bool AppleTexasAudio::init(OSDictionary *properties) { bool result = FALSE; debugIOLog ( 3, "+ AppleTexasAudio::init ( %p )", properties ); if ( super::init ( properties ) ) { gVolLeft = 0; gVolRight = 0; gVolMuteActive = false; gModemSoundActive = false; result = TRUE; } debugIOLog (3, "- AppleTexasAudio::init ( %p ) returns %d", properties, result ); return result; } // -------------------------------------------------------------------------- // ::free // call inherited free void AppleTexasAudio::free() { IOWorkLoop *workLoop; debugIOLog (3, "+ AppleTexasAudio::free()"); CLEAN_RELEASE(hdpnMuteRegMem); CLEAN_RELEASE(ampMuteRegMem); CLEAN_RELEASE(hwResetRegMem); CLEAN_RELEASE(headphoneExtIntGpioMem); CLEAN_RELEASE(dallasExtIntGpioMem); CLEAN_RELEASE(audioI2SControl); if (NULL != ioBaseAddressMemory) { ioBaseAddressMemory->release(); } if (NULL != ioClockBaseAddressMemory) { ioClockBaseAddressMemory->release(); } workLoop = getWorkLoop(); if (NULL != workLoop) { if (NULL != headphoneIntEventSource && NULL != headphoneIntProvider) workLoop->removeEventSource (headphoneIntEventSource); if (NULL != dallasIntEventSource && NULL != dallasIntProvider) workLoop->removeEventSource (dallasIntEventSource); if (NULL != dallasHandlerTimer) workLoop->removeEventSource (dallasHandlerTimer); if (NULL != notifierHandlerTimer) workLoop->removeEventSource (notifierHandlerTimer); if ( NULL != deferHeadphoneHandlerTimer ) { workLoop->removeEventSource ( deferHeadphoneHandlerTimer ); } // [3103075] rbm 11/27/2002 } publishResource (gAppleAudioVideoJackStateKey, NULL); CLEAN_RELEASE(headphoneIntEventSource); CLEAN_RELEASE(dallasIntEventSource); super::free(); debugIOLog (3, "- AppleTexasAudio::free()"); } // -------------------------------------------------------------------------- // ::probe // called at load time, to see if this driver really does match with a device. In our // case we check the registry to ensure we are loading on the appropriate hardware. IOService* AppleTexasAudio::probe(IOService *provider, SInt32 *score) { IORegistryEntry * sound = 0; IOService * result = 0; debugIOLog ( 3, "+ AppleTexasAudio::probe ( %p, %p )", provider, score ); // Finds the possible candidate for sound, to be used in // reading the caracteristics of this hardware: super::probe ( provider, score ); *score = kIODefaultProbeScore; sound = provider->childFromPath("sound", gIODTPlane); // we are on a new world : the registry is assumed to be fixed if ( sound ) { OSData * tmpData = OSDynamicCast ( OSData, sound->getProperty ( kModelPropName ) ); if ( tmpData ) { if ( tmpData->isEqualTo ( kTexasModelName, sizeof ( kTexasModelName ) -1 ) ) { *score = *score+1; debugIOLog ( 3, " increasing score %d", *score ); result = this; } } if ( !result ) { sound->release (); } } debugIOLog ( 3, "- AppleTexasAudio::probe ( %p, %p ) returns %p", provider, score, result ); return result; } // -------------------------------------------------------------------------- // ::initHardware // Don't do a whole lot in here, but do call the inherited inithardware method. // in turn this is going to call sndHWInitialize to perform initialization. All // of the code to initialize and start the device needs to be in that routine, this // is kept as simple as possible. bool AppleTexasAudio::initHardware(IOService *provider) { debugIOLog ( 3, "+ AppleTexasAudio::initHardware ( %p )", provider ); // calling the superclasses initHarware will indirectly call our // sndHWInitialize() method. So we don't need to do a whole lot // in this function. super::initHardware(provider); debugIOLog ( 3, "- AppleTexasAudio::initHardware ( %p ) returns TRUE", provider ); return TRUE; } // -------------------------------------------------------------------------- // Method: timerCallback // // Purpose: // This is a static method that gets called from a timer task at regular intervals // Generally we do not want to do a lot of work here, we simply turn around and call // the appropriate method to perform out periodic tasks. void AppleTexasAudio::timerCallback(OSObject *target, IOAudioDevice *device) { // probably don't want this on since we will get called a lot... // debugIOLog (3, "+ AppleTexasAudio::timerCallback"); AppleTexasAudio * templateDriver; templateDriver = OSDynamicCast (AppleTexasAudio, target); if (templateDriver) { templateDriver->checkStatus(false); } // probably don't want this on since we will get called a lot... // debugIOLog (3, "- AppleTexasAudio::timerCallback"); return; } // -------------------------------------------------------------------------- // Method: checkStatus // // Purpose: // poll the detects, note this should prolly be done with interrupts rather // than by polling if interrupts are supported void AppleTexasAudio::checkStatus(bool force) { // probably don't want this on since we will get called a lot... // debugIOLog (3, "+ AppleTexasAudio::checkStatus"); // probably don't want this on since we will get called a lot... // debugIOLog (3, "- AppleTexasAudio::checkStatus"); } /*************************** sndHWXXXX functions******************************/ /* These functions should be common to all Apple hardware drivers and be */ /* declared as virtual in the superclass. This is the only place where we */ /* should manipulate the hardware. Order is given by the UI policy system */ /* The set of functions should be enough to implement the policy */ /*****************************************************************************/ #pragma mark +HARDWARE REGISTER MANIPULATION /************************** Hardware Register Manipulation ********************/ // Hardware specific functions : These are all virtual functions and we have to // implement these in the driver class // -------------------------------------------------------------------------- // ::sndHWInitialize // hardware specific initialization needs to be in here, together with the code // required to start audio on the device. // // There are three sections of memory mapped I/O that are directly accessed by the Apple02Audio. These // include the GPIOs, I2S DMA Channel Registers and I2S control registers. They fall within the memory map // as follows: // ~ ~ // |______________________________| // | | // | I2S Control | // |______________________________| <- soundConfigSpace = ioBase + i2s0BaseOffset ...OR... ioBase + i2s1BaseOffset // | | // ~ ~ // ~ ~ // |______________________________| // | | // | I2S DMA Channel | // |______________________________| <- i2sDMA = ioBase + i2s0_DMA ...OR... ioBase + i2s1_DMA // | | // ~ ~ // ~ ~ // |______________________________| // | FCRs | // | GPIO | <- gpio = ioBase + gpioOffsetAddress // | ExtIntGPIO | <- fcr = ioBase + fcrOffsetAddress // |______________________________| <- ioConfigurationBaseAddress // | | // ~ ~ // // The I2S DMA Channel is mapped in by the Apple02DBDMAAudioDMAEngine. Only the I2S control registers are // mapped in by the AudioI2SControl. The Apple I/O Configuration Space (i.e. FCRs, GPIOs and ExtIntGPIOs) // are mapped in by the subclass of Apple02Audio. The FCRs must also be mapped in by the AudioI2SControl // object as the init method must enable the I2S I/O Module for which the AudioI2SControl object is // being instantiated for. // void AppleTexasAudio::sndHWInitialize(IOService *provider) { IOReturn err; IORegistryEntry *i2s; IORegistryEntry *macio; IORegistryEntry *gpio; IORegistryEntry *i2c; IORegistryEntry *deq; IORegistryEntry *intSource; IORegistryEntry *headphoneMute; IORegistryEntry *ampMute; IORegistryEntry *hardwareReset; OSData *tmpData; IOMemoryMap *map; UInt32 *hdpnMuteGpioAddr; UInt32 *ampMuteGpioAddr; UInt32 *hwResetGpioAddr; UInt32 *headphoneExtIntGpioAddr; UInt32 *dallasExtIntGpioAddr; UInt32 *i2cAddrPtr; UInt32 *tmpPtr; UInt32 loopCnt; UInt8 data[kBIQwidth]; // space for biggest register size UInt8 curValue; bool hasInput; debugIOLog ( 3, "+ AppleTexasAudio::sndHWInitialize ( %p )", provider ); ourProvider = provider; hasInput = (bool) HasInput (); savedNanos = 0; previousPowerState = kIOAudioDeviceActive; i2s = NULL; macio = NULL; gpio = NULL; i2c = NULL; i2s = ourProvider->getParentEntry (gIODTPlane); FailIf (!i2s, Exit); macio = i2s->getParentEntry (gIODTPlane); FailIf (!macio, Exit); gpio = macio->childFromPath (kGPIODTEntry, gIODTPlane); FailIf (!gpio, Exit); i2c = macio->childFromPath (kI2CDTEntry, gIODTPlane); setProperty (kSpeakerConnectError, speakerConnectFailed); // Determine which systems to exclude from the default behavior of releasing the headphone // mute after 200 milliseconds delay [2660341]. tmpData = OSDynamicCast (OSData, macio->getProperty (kDeviceIDPropName)); FailIf (!tmpData, Exit); deviceID = (UInt32)tmpData->getBytesNoCopy (); // get the physical address of the i2c cell that the sound chip (Digital EQ, "deq") is connected to. deq = i2c->childFromPath (kDigitalEQDTEntry, gIODTPlane); FailIf (!deq, Exit); tmpData = OSDynamicCast (OSData, deq->getProperty (kI2CAddress)); deq->release (); FailIf (!tmpData, Exit); i2cAddrPtr = (UInt32*)tmpData->getBytesNoCopy (); DEQAddress = *i2cAddrPtr; DEQAddress = DEQAddress >> 1; // have to shift down because I2C driver will shift up on writes // get the physical address of the gpio pin for setting the headphone mute headphoneMute = FindEntryByProperty (gpio, kAudioGPIO, kHeadphoneAmpEntry); FailIf (!headphoneMute, Exit); tmpData = OSDynamicCast (OSData, headphoneMute->getProperty (kAAPLAddress)); FailIf (!tmpData, Exit); hdpnMuteGpioAddr = (UInt32*)tmpData->getBytesNoCopy (); tmpData = OSDynamicCast (OSData, headphoneMute->getProperty (kAudioGPIOActiveState)); tmpPtr = (UInt32*)tmpData->getBytesNoCopy (); hdpnActiveState = *tmpPtr; // take the hard coded memory address that's in the boot rom, and convert it a virtual address hdpnMuteRegMem = IODeviceMemory::withRange (*hdpnMuteGpioAddr, sizeof (UInt8)); map = hdpnMuteRegMem->map (0); hdpnMuteGpio = (UInt8*)map->getVirtualAddress (); intSource = 0; // get the interrupt provider for the Dallas speaker insertion interrupt intSource = FindEntryByProperty (gpio, kOneWireBus, kSpeakerID); if (NULL != intSource) { dallasIntProvider = OSDynamicCast (IOService, intSource); FailIf (!dallasIntProvider, Exit); // get the active state of the dallas speaker inserted pin tmpData = OSDynamicCast (OSData, intSource->getProperty (kAudioGPIOActiveState)); if (!tmpData) { dallasInsertedActiveState = 1; } else { tmpPtr = (UInt32*)tmpData->getBytesNoCopy (); dallasInsertedActiveState = *tmpPtr; } // get the physical address of the pin for detecting the dallas speaker insertion/removal tmpData = OSDynamicCast (OSData, intSource->getProperty (kAAPLAddress)); FailIf (!tmpData, Exit); dallasExtIntGpioAddr = (UInt32*)tmpData->getBytesNoCopy (); // take the hard coded memory address that's in the boot rom, and convert it a virtual address dallasExtIntGpioMem = IODeviceMemory::withRange (*dallasExtIntGpioAddr, sizeof (UInt8)); map = dallasExtIntGpioMem->map (0); dallasExtIntGpio = (UInt8*)map->getVirtualAddress (); curValue = *dallasExtIntGpio; curValue = curValue | (1 << 7); *dallasExtIntGpio = curValue; } else { debugIOLog (3, " Dallas Speaker interrupt source NOT FOUND"); } intSource = 0; // get the interrupt provider for the headphone insertion interrupt intSource = FindEntryByProperty (gpio, kAudioGPIO, kHeadphoneDetectInt); if (!intSource) intSource = FindEntryByProperty (gpio, kCompatible, kKWHeadphoneDetectInt); FailIf (!intSource, Exit); headphoneIntProvider = OSDynamicCast (IOService, intSource); FailIf (!headphoneIntProvider, Exit); // We only want to publish the jack state if this hardware has video on its headphone connector tmpData = OSDynamicCast (OSData, intSource->getProperty (kVideoPropertyEntry)); debugIOLog (3, " kVideoPropertyEntry = %p", tmpData); if (NULL != tmpData) { hasVideo = TRUE; gAppleAudioVideoJackStateKey = OSSymbol::withCStringNoCopy ("AppleAudioVideoJackState"); debugIOLog (3, " has video in headphone"); } // get the active state of the headphone inserted pin // This should really be gotten from the sound-objects property, but we're not parsing that yet. tmpData = OSDynamicCast (OSData, intSource->getProperty (kAudioGPIOActiveState)); if (NULL == tmpData) { headphoneInsertedActiveState = 1; } else { tmpPtr = (UInt32*)tmpData->getBytesNoCopy (); headphoneInsertedActiveState = *tmpPtr; } // get the physical address of the pin for detecting the headphone insertion/removal tmpData = OSDynamicCast (OSData, intSource->getProperty (kAAPLAddress)); FailIf (!tmpData, Exit); headphoneExtIntGpioAddr = (UInt32*)tmpData->getBytesNoCopy (); // take the hard coded memory address that's in the boot rom, and convert it a virtual address headphoneExtIntGpioMem = IODeviceMemory::withRange (*headphoneExtIntGpioAddr, sizeof (UInt8)); map = headphoneExtIntGpioMem->map (0); headphoneExtIntGpio = (UInt8*)map->getVirtualAddress (); curValue = *headphoneExtIntGpio; curValue = curValue | (1 << 7); *headphoneExtIntGpio = curValue; // get the physical address of the gpio pin for setting the amplifier mute ampMute = FindEntryByProperty (gpio, kAudioGPIO, kAmpEntry); FailIf (!ampMute, Exit); tmpData = OSDynamicCast (OSData, ampMute->getProperty (kAAPLAddress)); FailIf (!tmpData, Exit); ampMuteGpioAddr = (UInt32*)tmpData->getBytesNoCopy (); tmpData = OSDynamicCast (OSData, ampMute->getProperty (kAudioGPIOActiveState)); tmpPtr = (UInt32*)tmpData->getBytesNoCopy (); ampActiveState = *tmpPtr; // take the hard coded memory address that's in the boot rom, and convert it a virtual address ampMuteRegMem = IODeviceMemory::withRange (*ampMuteGpioAddr, sizeof (UInt8)); map = ampMuteRegMem->map (0); ampMuteGpio = (UInt8*)map->getVirtualAddress (); // get the physical address of the gpio pin for setting the hardware reset hardwareReset = FindEntryByProperty (gpio, kAudioGPIO, kHWResetEntry); FailIf (!hardwareReset, Exit); tmpData = OSDynamicCast (OSData, hardwareReset->getProperty (kAAPLAddress)); FailIf (!tmpData, Exit); hwResetGpioAddr = (UInt32*)tmpData->getBytesNoCopy (); tmpData = OSDynamicCast (OSData, hardwareReset->getProperty (kAudioGPIOActiveState)); tmpPtr = (UInt32*)tmpData->getBytesNoCopy (); hwResetActiveState = *tmpPtr; // take the hard coded memory address that's in the boot rom, and convert it a virtual address hwResetRegMem = IODeviceMemory::withRange (*hwResetGpioAddr, sizeof (UInt8)); map = hwResetRegMem->map (0); hwResetGpio = (UInt8*)map->getVirtualAddress (); FailIf (!findAndAttachI2C (provider), Exit); layoutID = GetDeviceID (); debugIOLog (3, " layoutID = %ld", layoutID); ExcludeHPMuteRelease (layoutID); i2sSerialFormat = ( kClockSource45MHz | ( 1 << kMClkDivisorShift ) | ( 1 << kSClkDivisorShift ) | kSClkMaster | kSerialFormat64x ); drc.compressionRatioNumerator = kDrcRatioNumerator; drc.compressionRatioDenominator = kDrcRationDenominator; drc.threshold = kDrcThresholdMax; drc.maximumVolume = kDefaultMaximumVolume; drc.enable = false; // Initialize the TAS3001C as follows: // Mode: normal // SCLK: 64 fs // input serial mode: i2s // output serial mode: i2s // serial word length: 16 bits // Dynamic range control: disabled // Volume (left & right): muted // Treble / Bass: unity // Biquad filters: unity data[0] = ( kNormalLoad << kFL ) | ( k64fs << kSC ) | TAS_I2S_MODE | ( TAS_WORD_LENGTH << kW0 ); TAS3001C_WriteRegister( kMainCtrlReg, data ); // default to normal load mode, 16 bit I2S data[0] = ( kDrcDisable << kEN ) | ( kCompression3to1 << kCR ); data[1] = kDefaultCompThld; TAS3001C_WriteRegister( kDynamicRangeCtrlReg, data ); for( loopCnt = 0; loopCnt < kVOLwidth; loopCnt++ ) // init to volume = muted data[loopCnt] = 0; TAS3001C_WriteRegister( kVolumeCtrlReg, data ); data[0] = 0x72; // treble = unity 0.0 dB TAS3001C_WriteRegister( kTrebleCtrlReg, data ); data[0] = 0x3E; // bass = unity = 0.0 dB TAS3001C_WriteRegister( kBassCtrlReg, data ); data[0] = 0x10; // output mixer to unity = 0.0 dB data[1] = 0x00; data[2] = 0x00; TAS3001C_WriteRegister( kMixer1CtrlReg, data ); data[0] = 0x00; // call progress mixer to mute = -70.0 dB data[1] = 0x00; data[2] = 0x00; TAS3001C_WriteRegister( kMixer2CtrlReg, data ); for( loopCnt = 1; loopCnt < kBIQwidth; loopCnt++ ) // all biquads to unity gain all pass mode data[loopCnt] = 0x00; data[0] = 0x10; TAS3001C_WriteRegister( kLeftBiquad0CtrlReg, data ); TAS3001C_WriteRegister( kLeftBiquad1CtrlReg, data ); TAS3001C_WriteRegister( kLeftBiquad2CtrlReg, data ); TAS3001C_WriteRegister( kLeftBiquad3CtrlReg, data ); TAS3001C_WriteRegister( kLeftBiquad4CtrlReg, data ); TAS3001C_WriteRegister( kLeftBiquad5CtrlReg, data ); TAS3001C_WriteRegister( kRightBiquad0CtrlReg, data ); TAS3001C_WriteRegister( kRightBiquad1CtrlReg, data ); TAS3001C_WriteRegister( kRightBiquad2CtrlReg, data ); TAS3001C_WriteRegister( kRightBiquad3CtrlReg, data ); TAS3001C_WriteRegister( kRightBiquad4CtrlReg, data ); TAS3001C_WriteRegister( kRightBiquad5CtrlReg, data ); // All this config should go in a single method: map = provider->mapDeviceMemoryWithIndex (Apple02DBDMAAudioDMAEngine::kDBDMADeviceIndex); FailIf (!map, Exit); // the i2s stuff is in a separate class. make an instance of the class and store AudioI2SInfo tempInfo; tempInfo.map = map; tempInfo.i2sSerialFormat = kSerialFormat64x; // [3060321] rbm 2 Oct 2002 // create an object, this will get inited. audioI2SControl = AudioI2SControl::create(&tempInfo) ; FailIf (NULL == audioI2SControl, Exit); audioI2SControl->retain(); err = TAS3001C_Initialize( kFORCE_RESET_SETUP_TIME ); // reset the TAS3001C and flush the shadow contents to the HW dallasDriver = NULL; dallasDriverNotifier = addNotification (gIOPublishNotification, serviceMatching ("AppleDallasDriver"), (IOServiceNotificationHandler)&DallasDriverPublished, this); if (NULL != dallasDriver) dallasDriverNotifier->remove (); Exit: if (NULL != gpio) gpio->release (); if (NULL != i2c) i2c->release (); debugIOLog ( 3, "- AppleTexasAudio::sndHWInitialize ( %p )", provider ); return; } // -------------------------------------------------------------------------- void AppleTexasAudio::sndHWPostDMAEngineInit (IOService *provider) { IOWorkLoop *workLoop; debugIOLog (3, "+ AppleTexasAudio::sndHWPostDMAEngineInit ( %p )", provider); if (NULL != driverDMAEngine) driverDMAEngine->setSampleLatencies (kTexasOutputSampleLatency, kTexasInputSampleLatency); workLoop = getWorkLoop(); FailIf (NULL == workLoop, Exit); if (NULL != headphoneIntProvider) { headphoneIntEventSource = IOInterruptEventSource::interruptEventSource (this, headphoneInterruptHandler, headphoneIntProvider, 0); FailIf (NULL == headphoneIntEventSource, Exit); workLoop->addEventSource (headphoneIntEventSource); } // Create a (primary) device interrupt source for dallas and attach it to the work loop if (NULL != dallasIntProvider) { // Create a timer event source dallasHandlerTimer = IOTimerEventSource::timerEventSource (this, DallasInterruptHandlerTimer); if (NULL != dallasHandlerTimer) { workLoop->addEventSource (dallasHandlerTimer); } notifierHandlerTimer = IOTimerEventSource::timerEventSource (this, DisplaySpeakersNotFullyConnected); if (NULL != notifierHandlerTimer) { workLoop->addEventSource (notifierHandlerTimer); } dallasIntEventSource = IOFilterInterruptEventSource::filterInterruptEventSource (this, dallasInterruptHandler, interruptFilter, dallasIntProvider, 0); FailIf (NULL == dallasIntEventSource, Exit); workLoop->addEventSource (dallasIntEventSource); if (NULL != outputSelector) { outputSelector->addAvailableSelection(kIOAudioOutputPortSubTypeExternalSpeaker, "ExtSpeakers"); } } deferHeadphoneHandlerTimer = IOTimerEventSource::timerEventSource (this, deferHeadphoneHandler); // [3103075] rbm 11/27/2002 if (NULL != deferHeadphoneHandlerTimer) { workLoop->addEventSource (deferHeadphoneHandlerTimer); } if ( TRUE == IsSpeakerConnected() ) { if (NULL != dallasDriver) { UInt8 bEEPROM[32]; Boolean result; speakerID = 0; familyID = 0; // dallasIntEventSource isn't enabled yet, so we don't have to disable it before this call result = dallasDriver->getSpeakerID (bEEPROM); if (TRUE == result) { // The speakers have been successfully probed dallasSpeakersProbed = TRUE; familyID = bEEPROM[0]; speakerID = bEEPROM[1]; debugIOLog (3, " speakerID = %ld", speakerID); } else { debugIOLog (3, " speakerID unknown, probe failed"); } } } else { speakerID = 0; familyID = 0; dallasSpeakersProbed = FALSE; } if ( kIOAudioDeviceActive == previousPowerState ) { // [3731023] begin { Do not perform interrupt dispatch when hardware is not awake if (FALSE == IsHeadphoneConnected ()) { if ( NULL == dallasInterruptHandler ) { // [3117811] SetActiveOutput (kSndHWOutput2, kBiquadUntouched); } if (TRUE == hasVideo) { // Tell the video driver about the jack state change in case a video connector was plugged in publishResource (gAppleAudioVideoJackStateKey, kOSBooleanFalse); } if (NULL != dallasIntProvider) { // Set the correct EQ DallasInterruptHandlerTimer (this, 0); } else { DeviceInterruptService (); } } else { if (NULL != headphoneIntProvider) { // Set amp mutes accordingly deferHeadphoneHandler (this, deferHeadphoneHandlerTimer); } } } // } end [3731023] if (NULL == outVolRight && NULL != outVolLeft) { // If they are running mono at boot time, set the right channel's last value to an illegal value // so it will come up in stereo and center balanced if they plug in speakers or headphones later. lastRightVol = kOUT_OF_BOUNDS_VOLUME_VALUE; lastLeftVol = outVolLeft->getIntValue (); } if (NULL != headphoneIntEventSource) headphoneIntEventSource->enable (); if (NULL != dallasIntEventSource) dallasIntEventSource->enable (); Exit: debugIOLog (3, "- AppleTexasAudio::sndHWPostDMAEngineInit ( %p )", provider); return; } // -------------------------------------------------------------------------- UInt32 AppleTexasAudio::sndHWGetCurrentSampleFrame (void) { return audioI2SControl->GetFrameCountReg (); } // -------------------------------------------------------------------------- void AppleTexasAudio::sndHWSetCurrentSampleFrame (UInt32 value) { audioI2SControl->SetFrameCountReg (value); } // -------------------------------------------------------------------------- UInt32 AppleTexasAudio::sndHWGetInSenseBits(void) { debugIOLog (3, "± AppleTexasAudio::sndHWGetInSenseBits() returns 0"); return 0; } // -------------------------------------------------------------------------- // we can't read the registers back, so return the value in the shadow reg. UInt32 AppleTexasAudio::sndHWGetRegister(UInt32 regNum) { debugIOLog ( 3, "± AppleTexasAudio::sndHWGetRegister ( %d ) returns 0", regNum ); return 0; } // -------------------------------------------------------------------------- // set the reg over i2c and make sure the value is cached in the shadow reg so we can "get it back" IOReturn AppleTexasAudio::sndHWSetRegister(UInt32 regNum, UInt32 val) { return(kIOReturnSuccess); } #pragma mark +HARDWARE IO ACTIVATION /************************** Manipulation of input and outputs ***********************/ /********(These functions are enough to implement the simple UI policy)**************/ // -------------------------------------------------------------------------- UInt32 AppleTexasAudio::sndHWGetActiveOutputExclusive(void) { return 0; } // -------------------------------------------------------------------------- IOReturn AppleTexasAudio::sndHWSetActiveOutputExclusive(UInt32 outputPort ) { return kIOReturnSuccess; } // -------------------------------------------------------------------------- UInt32 AppleTexasAudio::sndHWGetActiveInputExclusive(void) { return 0; } // -------------------------------------------------------------------------- IOReturn AppleTexasAudio::sndHWSetActiveInputExclusive(UInt32 input ) { return kIOReturnSuccess; } #pragma mark +CONTROL FUNCTIONS // control function // -------------------------------------------------------------------------- bool AppleTexasAudio::sndHWGetSystemMute(void) { debugIOLog (3, "± AppleTexasAudio::sndHWGetSystemMute() returns %d", gVolMuteActive); return (gVolMuteActive); } // -------------------------------------------------------------------------- IOReturn AppleTexasAudio::setModemSound(bool state) { UInt8 data[3]; IOReturn myReturn = kIOReturnSuccess; if(gModemSoundActive == state) goto EXIT; if(state) { // we turned it on data[0] = 0x10; } else { data[0] = 0x00; } data[1] = 0x00; data[2] = 0x00; myReturn = TAS3001C_WriteRegister( kMixer2CtrlReg, data ); gModemSoundActive = state; EXIT: return(myReturn); } // -------------------------------------------------------------------------- IOReturn AppleTexasAudio::sndHWSetSystemMute(bool mutestate) { IOReturn result; result = kIOReturnSuccess; debugIOLog ( 3, "+ AppleTexasAudio::sndHWSetSystemMute ( %d )", mutestate ); if (true == mutestate) { if (false == gVolMuteActive) { // mute the part gVolMuteActive = mutestate ; result = SetVolumeCoefficients (0, 0); } } else { // unmute the part gVolMuteActive = mutestate ; result = SetVolumeCoefficients (volumeTable[(UInt32)gVolLeft], volumeTable[(UInt32)gVolRight]); } debugIOLog ( 3, "- AppleTexasAudio::sndHWSetSystemMute ( %d ) returns 0x%lX", mutestate, result ); return (result); } // -------------------------------------------------------------------------- bool AppleTexasAudio::sndHWSetSystemVolume ( UInt32 leftVolume, UInt32 rightVolume ) { bool result; result = TRUE; debugIOLog (3, "+ AppleTexasAudio::sndHWSetSystemVolume ( %ld, %ld)", leftVolume, rightVolume); if ( ( gVolLeft != (SInt32)leftVolume ) || ( gVolRight != (SInt32)rightVolume ) ) { gVolLeft = leftVolume; gVolRight = rightVolume; if (NULL != outVolLeft) { lastLeftVol = gVolLeft; } if (NULL != outVolRight) { lastRightVol = gVolRight; } result = ( kIOReturnSuccess == SetVolumeCoefficients ( volumeTable[(UInt32)gVolLeft], volumeTable[(UInt32)gVolRight] ) ); } debugIOLog (3, "- AppleTexasAudio::sndHWSetSystemVolume ( %ld, %ld ) returns %d", leftVolume, rightVolume, ( result == kIOReturnSuccess ) ); return result; } // -------------------------------------------------------------------------- IOReturn AppleTexasAudio::sndHWSetSystemVolume ( UInt32 value ) { debugIOLog ( 3, "+ AppleTexasAudio::sndHWSetSystemVolume ( %ld )", value ); IOReturn myReturn = kIOReturnError; // just call the default function in this class with the same val for left and right. if ( true == sndHWSetSystemVolume( value, value ) ) { myReturn = kIOReturnSuccess; } debugIOLog ( 3, "- AppleTexasAudio::sndHWSetSystemVolume ( %ld ) returns 0x%lX", value, myReturn ); return(myReturn); } // -------------------------------------------------------------------------- IOReturn AppleTexasAudio::sndHWSetPlayThrough(bool playthroughstate) { return kIOReturnSuccess; } // -------------------------------------------------------------------------- IOReturn AppleTexasAudio::sndHWSetSystemInputGain(UInt32 leftGain, UInt32 rightGain) { return kIOReturnSuccess; } // -------------------------------------------------------------------------- // You either have only a master volume control, or you have both volume controls. // This function must be called on the workloop as it will be calling back to IOAudioFamily IOReturn AppleTexasAudio::AdjustControls (void) { IOFixed mindBVol; IOFixed maxdBVol; Boolean mustUpdate; debugIOLog ( 3, "+ AppleTexasAudio::AdjustControls()" ); FailIf (NULL == driverDMAEngine, Exit); mustUpdate = FALSE; mindBVol = volumedBTable[minVolume]; maxdBVol = volumedBTable[maxVolume]; // Must update if any of the following conditions exist: // 1. No master volume control exists AND the master volume is the target // 2. The master volume control exists AND the master volume is not the target // 3. the minimum or maximum dB volume setting for the left volume control changes // 4. the minimum or maximum dB volume setting for the right volume control changes if ((NULL == outVolMaster && TRUE == useMasterVolumeControl) || (NULL != outVolMaster && FALSE == useMasterVolumeControl) || (NULL != outVolLeft && outVolLeft->getMinValue () != minVolume) || (NULL != outVolLeft && outVolLeft->getMaxValue () != maxVolume) || (NULL != outVolRight && outVolRight->getMinValue () != minVolume) || (NULL != outVolRight && outVolRight->getMaxValue () != maxVolume)) { mustUpdate = TRUE; } if (TRUE == mustUpdate) { debugIOLog ( 3, " mindBVol = %lx, maxdBVol = %lx", mindBVol, maxdBVol ); driverDMAEngine->pauseAudioEngine (); driverDMAEngine->beginConfigurationChange (); if (TRUE == useMasterVolumeControl) { // We have only the master volume control (possibly not created yet) and have to remove the other volume controls (possibly don't exist) if (NULL == outVolMaster) { // remove the existing left and right volume controls if (NULL != outVolLeft) { lastLeftVol = outVolLeft->getIntValue (); driverDMAEngine->removeDefaultAudioControl (outVolLeft); outVolLeft = NULL; } if (NULL != outVolRight) { lastRightVol = outVolRight->getIntValue (); driverDMAEngine->removeDefaultAudioControl (outVolRight); outVolRight = NULL; } // Create the master control outVolMaster = IOAudioLevelControl::createVolumeControl((lastLeftVol + lastRightVol) / 2, minVolume, maxVolume, mindBVol, maxdBVol, kIOAudioControlChannelIDAll, kIOAudioControlChannelNameAll, kOutVolMaster, kIOAudioControlUsageOutput); if (NULL != outVolMaster) { driverDMAEngine->addDefaultAudioControl(outVolMaster); outVolMaster->setValueChangeHandler((IOAudioControl::IntValueChangeHandler)outputControlChangeHandler, this); outVolMaster->flushValue (); } } } else { // or we have both controls (possibly not created yet) and we have to remove the master volume control (possibly doesn't exist) if (NULL == outVolLeft) { // Have to create the control again... if (lastLeftVol > kMAXIMUM_LEGAL_VOLUME_VALUE && NULL != outVolMaster) { lastLeftVol = outVolMaster->getIntValue (); } outVolLeft = IOAudioLevelControl::createVolumeControl (lastLeftVol, kMinimumVolume, kMaximumVolume, mindBVol, maxdBVol, kIOAudioControlChannelIDDefaultLeft, kIOAudioControlChannelNameLeft, kOutVolLeft, kIOAudioControlUsageOutput); if (NULL != outVolLeft) { driverDMAEngine->addDefaultAudioControl (outVolLeft); outVolLeft->setValueChangeHandler ((IOAudioControl::IntValueChangeHandler)outputControlChangeHandler, this); } } if (NULL == outVolRight) { // Have to create the control again... if (lastRightVol > kMAXIMUM_LEGAL_VOLUME_VALUE && NULL != outVolMaster) { lastRightVol = outVolMaster->getIntValue (); } outVolRight = IOAudioLevelControl::createVolumeControl (lastRightVol, kMinimumVolume, kMaximumVolume, mindBVol, maxdBVol, kIOAudioControlChannelIDDefaultRight, kIOAudioControlChannelNameRight, kOutVolRight, kIOAudioControlUsageOutput); if (NULL != outVolRight) { driverDMAEngine->addDefaultAudioControl (outVolRight); outVolRight->setValueChangeHandler ((IOAudioControl::IntValueChangeHandler)outputControlChangeHandler, this); } } if (NULL != outVolMaster) { driverDMAEngine->removeDefaultAudioControl (outVolMaster); outVolMaster = NULL; } if (NULL != outVolLeft && NULL != outVolRight) { outVolLeft->flushValue (); outVolRight->flushValue (); } } if (NULL != outVolMaster) { outVolMaster->setMinValue (minVolume); outVolMaster->setMinDB (mindBVol); outVolMaster->setMaxValue (maxVolume); outVolMaster->setMaxDB (maxdBVol); if (outVolMaster->getIntValue () > maxVolume) { outVolMaster->setValue (maxVolume); } } if (NULL != outVolLeft) { outVolLeft->setMinValue (minVolume); outVolLeft->setMinDB (mindBVol); outVolLeft->setMaxValue (maxVolume); outVolLeft->setMaxDB (maxdBVol); if (outVolLeft->getIntValue () > maxVolume) { outVolLeft->setValue (maxVolume); } } if (NULL != outVolRight) { outVolRight->setMinValue (minVolume); outVolRight->setMinDB (mindBVol); outVolRight->setMaxValue (maxVolume); outVolRight->setMaxDB (maxdBVol); if (outVolRight->getIntValue () > maxVolume) { outVolRight->setValue (maxVolume); } } driverDMAEngine->completeConfigurationChange (); driverDMAEngine->resumeAudioEngine (); } Exit: debugIOLog ( 3, "- AppleTexasAudio::AdjustControls() returns kIOReturnSuccess" ); return kIOReturnSuccess; } #pragma mark +INDENTIFICATION // -------------------------------------------------------------------------- // ::sndHWGetType // Identification - the only thing this driver supports is the DACA3550 part, return that. UInt32 AppleTexasAudio::sndHWGetType(void ) { debugIOLog (3, "± AppleTexasAudio::sndHWGetType() returns 0x%lX", kSndHWTypeTumbler); return (UInt32)kSndHWTypeTumbler ; } // -------------------------------------------------------------------------- // ::sndHWGetManufactuer // return the detected part's manufacturer. I think Daca is a single sourced part // from Micronas Intermetall. Always return just that. UInt32 AppleTexasAudio::sndHWGetManufacturer(void ) { debugIOLog ( 3, "± AppleTexasAudio::sndHWGetManufacturer() returns %d", kSndHWManfTI ); return (UInt32)kSndHWManfTI ; } #pragma mark +DETECT ACTIVATION & DEACTIVATION // -------------------------------------------------------------------------- // ::setDeviceDetectionActive // turn on detection, TODO move to superclass?? void AppleTexasAudio::setDeviceDetectionActive(void) { mCanPollStatus = true ; debugIOLog (3, "± AppleTexasAudio::setDeviceDetectionActive"); return ; } // -------------------------------------------------------------------------- // ::setDeviceDetectionInActive // turn off detection, TODO move to superclass?? void AppleTexasAudio::setDeviceDetectionInActive(void) { mCanPollStatus = false ; debugIOLog (3, "± AppleTexasAudio::setDeviceDetectionInActive"); return ; } #pragma mark +POWER MANAGEMENT // -------------------------------------------------------------------------- // Power Management IOReturn AppleTexasAudio::sndHWSetPowerState (IOAudioDevicePowerState theState) { IOReturn result; switch ( theState ) { case kIOAudioDeviceActive: debugIOLog (3, "+ AppleTexasAudio::sndHWSetPowerState ( %d kIOAudioDeviceActive )", theState); break; case kIOAudioDeviceIdle: debugIOLog (3, "+ AppleTexasAudio::sndHWSetPowerState ( %d kIOAudioDeviceIdle )", theState); break; case kIOAudioDeviceSleep: debugIOLog (3, "+ AppleTexasAudio::sndHWSetPowerState ( %d kIOAudioDeviceSleep )", theState); break; default: debugIOLog (3, "+ AppleTexasAudio::sndHWSetPowerState ( %d )", theState); break; } result = kIOReturnSuccess; switch (theState) { case kIOAudioDeviceActive: if (kIOAudioDeviceIdle == previousPowerState) { result = performDeviceIdleWake (); } else if (kIOAudioDeviceSleep == previousPowerState) { result = performDeviceWake (); } else { debugIOLog (3, " requested kIOAudioDeviceActive when already kIOAudioDeviceActive"); } break; case kIOAudioDeviceIdle: result = performDeviceIdleSleep (); break; case kIOAudioDeviceSleep: result = performDeviceSleep (); break; } previousPowerState = theState; switch ( theState ) { case kIOAudioDeviceActive: debugIOLog (3, "- AppleTexasAudio::sndHWSetPowerState ( %d kIOAudioDeviceActive ) returns 0x%lX, currentPowerState %d", theState, result, previousPowerState ); break; case kIOAudioDeviceIdle: debugIOLog (3, "- AppleTexasAudio::sndHWSetPowerState ( %d kIOAudioDeviceIdle ) returns 0x%lX, currentPowerState %d", theState, result, previousPowerState ); break; case kIOAudioDeviceSleep: debugIOLog (3, "- AppleTexasAudio::sndHWSetPowerState ( %d kIOAudioDeviceSleep ) returns 0x%lX, currentPowerState %d", theState, result, previousPowerState ); break; default: debugIOLog (3, "- AppleTexasAudio::sndHWSetPowerState ( %d ) returns 0x%lX, currentPowerState %d", theState, result, previousPowerState ); break; } return result; } // -------------------------------------------------------------------------- IOReturn AppleTexasAudio::performDeviceIdleSleep () { IOService * keyLargo; const OSSymbol* funcSymbolName = NULL; // [3323977] debugIOLog (3, "+ AppleTexasAudio::performDeviceIdleSleep()"); keyLargo = NULL; // Mute the amps to avoid pops and clicks... SetActiveOutput (kSndHWOutputNone, kBiquadUntouched); // ...then hold the RESET line... GpioWrite (hwResetGpio, ASSERT_GPIO (hwResetActiveState)); // ...wait for the part to settle... IODelay (100); keyLargo = IOService::waitForService (IOService::serviceMatching ("KeyLargo")); if (NULL != keyLargo) { funcSymbolName = OSSymbol::withCString ( "keyLargo_powerI2S" ); // [3323977] FailIf ( NULL == funcSymbolName, Exit ); // [3323977] // ...and turn off the i2s clocks... switch ( i2SInterfaceNumber ) { case kUseI2SCell0: keyLargo->callPlatformFunction (funcSymbolName, false, (void *)false, (void *)0, 0, 0); break; case kUseI2SCell1: keyLargo->callPlatformFunction (funcSymbolName, false, (void *)false, (void *)1, 0, 0); break; } funcSymbolName->release (); // [3323977] } Exit: debugIOLog (3, "- AppleTexasAudio::performDeviceIdleSleep() returns kIOReturnSuccess"); return kIOReturnSuccess; } // -------------------------------------------------------------------------- IOReturn AppleTexasAudio::performDeviceIdleWake () { IOService * keyLargo; IOReturn err; const OSSymbol* funcSymbolName = NULL; // [3323977] debugIOLog (3, "+ AppleTexasAudio::performDeviceIdleWake()"); err = kIOReturnError; keyLargo = NULL; keyLargo = IOService::waitForService (IOService::serviceMatching ("KeyLargo")); if (NULL != keyLargo) { funcSymbolName = OSSymbol::withCString ( "keyLargo_powerI2S" ); // [3323977] FailIf ( NULL == funcSymbolName, Exit ); // [3323977] // Turn on the i2s clocks... switch ( i2SInterfaceNumber ) { case kUseI2SCell0: keyLargo->callPlatformFunction (funcSymbolName, false, (void *)true, (void *)0, 0, 0); break; case kUseI2SCell1: keyLargo->callPlatformFunction (funcSymbolName, false, (void *)true, (void *)1, 0, 0); break; } funcSymbolName->release (); // [3323977] } // ...wait for the clocks to settle... IODelay (100); // ...and then release the RESET line... GpioWrite (hwResetGpio, NEGATE_GPIO (hwResetActiveState)); // ...wait for the part to wake up... IOSleep (10); // ...then bring everything back up the way it should be. err = TAS3001C_Initialize (kFORCE_RESET_SETUP_TIME); // reset the TAS3001C and flush the shadow contents to the HW if ( TRUE == IsSpeakerConnected() ) { if (NULL != dallasDriver && FALSE == dallasSpeakersProbed) { UInt8 bEEPROM[32]; Boolean result; speakerID = 0; familyID = 0; dallasIntEventSource->disable (); result = dallasDriver->getSpeakerID (bEEPROM); dallasIntEventSource->enable (); if (TRUE == result) { // The speakers have been successfully probed dallasSpeakersProbed = TRUE; familyID = bEEPROM[0]; speakerID = bEEPROM[1]; debugIOLog (3, " speakerID = %ld", speakerID); } else { debugIOLog (3, " speakerID unknown, probe failed"); } } } else { speakerID = 0; familyID = 0; dallasSpeakersProbed = FALSE; } if (FALSE == IsHeadphoneConnected ()) { if ( NULL == dallasIntProvider ) { // [3117811] SetActiveOutput (kSndHWOutput2, kBiquadUntouched); } if (TRUE == hasVideo && FALSE != headphonesConnected) { // Tell the video driver about the jack state change in case a video connector was plugged in publishResource (gAppleAudioVideoJackStateKey, kOSBooleanFalse); } if (NULL != dallasIntProvider) { // Set the correct EQ DallasInterruptHandlerTimer (this, 0); } else { DeviceInterruptService (); } } else { if (NULL != headphoneIntProvider) { // Set amp mutes accordingly deferHeadphoneHandler (this, deferHeadphoneHandlerTimer); } } Exit: debugIOLog (3, "- AppleTexasAudio::performDeviceIdleWake() returns 0x%lX", err ); return err; } // -------------------------------------------------------------------------- IOReturn AppleTexasAudio::performDeviceSleep () { IOService * keyLargo; const OSSymbol* funcSymbolName = NULL; // [3323977] debugIOLog ( 3, "+ AppleTexasAudio::performDeviceSleep()" ); keyLargo = NULL; // Mute the amps to avoid pops and clicks... SetActiveOutput (kSndHWOutputNone, kBiquadUntouched); // ...then hold the RESET line... GpioWrite (hwResetGpio, ASSERT_GPIO (hwResetActiveState)); keyLargo = IOService::waitForService (IOService::serviceMatching ("KeyLargo")); if (NULL != keyLargo) { funcSymbolName = OSSymbol::withCString ( "keyLargo_powerI2S" ); // [3323977] FailIf ( NULL == funcSymbolName, Exit ); // [3323977] // ...and turn off the i2s clocks... switch ( i2SInterfaceNumber ) { case kUseI2SCell0: keyLargo->callPlatformFunction (funcSymbolName, false, (void *)false, (void *)0, 0, 0); break; case kUseI2SCell1: keyLargo->callPlatformFunction (funcSymbolName, false, (void *)false, (void *)1, 0, 0); break; } funcSymbolName->release (); // [3323977] } Exit: debugIOLog ( 3, "- AppleTexasAudio::performDeviceSleep() returns 0x%lX", kIOReturnSuccess ); return kIOReturnSuccess; } // -------------------------------------------------------------------------- IOReturn AppleTexasAudio::performDeviceWake () { IOService * keyLargo; IOReturn err; const OSSymbol* funcSymbolName = NULL; // [3323977] debugIOLog (3, "+ AppleTexasAudio::performDeviceWake()"); err = kIOReturnError; // Mute the amps to avoid pops and clicks... SetActiveOutput (kSndHWOutputNone, kBiquadUntouched); // ...then hold the RESET line... GpioWrite (hwResetGpio, ASSERT_GPIO (hwResetActiveState)); keyLargo = NULL; err = kIOReturnError; keyLargo = IOService::waitForService (IOService::serviceMatching ("KeyLargo")); if (NULL != keyLargo) { funcSymbolName = OSSymbol::withCString ( "keyLargo_powerI2S" ); // [3323977] FailIf ( NULL == funcSymbolName, Exit ); // [3323977] // ...turn on the i2s clocks... switch ( i2SInterfaceNumber ) { case kUseI2SCell0: keyLargo->callPlatformFunction (funcSymbolName, false, (void *)true, (void *)0, 0, 0); break; case kUseI2SCell1: keyLargo->callPlatformFunction (funcSymbolName, false, (void *)true, (void *)1, 0, 0); break; } funcSymbolName->release (); // [3323977] } // ...wait for the clocks to settle... IODelay (100); // ...then release the RESET line... GpioWrite (hwResetGpio, NEGATE_GPIO (hwResetActiveState)); // ...wait for the part to wake up... IOSleep (150); // ...then bring everything back up the way it should be. err = TAS3001C_Initialize (kFORCE_RESET_SETUP_TIME); // reset the TAS3001C and flush the shadow contents to the HW if ( TRUE == IsSpeakerConnected() ) { if (NULL != dallasDriver && FALSE == dallasSpeakersProbed && TRUE == IsSpeakerConnected ()) { UInt8 bEEPROM[32]; Boolean result; speakerID = 0; familyID = 0; dallasIntEventSource->disable (); result = dallasDriver->getSpeakerID (bEEPROM); dallasIntEventSource->enable (); if (TRUE == result) { // The speakers have been successfully probed dallasSpeakersProbed = TRUE; familyID = bEEPROM[0]; speakerID = bEEPROM[1]; debugIOLog (3, " speakerID = %ld", speakerID); } else { debugIOLog (3, " speakerID unknown, probe failed"); } } } else { speakerID = 0; familyID = 0; dallasSpeakersProbed = FALSE; } if (FALSE == IsHeadphoneConnected ()) { if ( NULL == dallasIntProvider ) { // [3117811] SetActiveOutput (kSndHWOutput2, kBiquadUntouched); } if (TRUE == hasVideo && FALSE != headphonesConnected) { // Tell the video driver about the jack state change in case a video connector was plugged in publishResource (gAppleAudioVideoJackStateKey, kOSBooleanFalse); } if (NULL != dallasIntProvider) { // Set the correct EQ DallasInterruptHandlerTimer (this, 0); } else { DeviceInterruptService (); } } else { if (NULL != headphoneIntProvider) { // Set amp mutes accordingly deferHeadphoneHandler (this, deferHeadphoneHandlerTimer); } } Exit: debugIOLog (3, "- AppleTexasAudio::performDeviceWake() returns 0x%lX", err); return err; } // -------------------------------------------------------------------------- // ::sndHWGetConnectedDevices // TODO: Move to superclass UInt32 AppleTexasAudio::sndHWGetConnectedDevices( void ) { debugIOLog (3, "± AppleTexasAudio::sndHWGetConnectedDevices() returns 0x%lX", currentDevices); return currentDevices ; } // -------------------------------------------------------------------------- UInt32 AppleTexasAudio::sndHWGetProgOutput ( void ) { debugIOLog (3, "± AppleTexasAudio::sndHWGetProgOutput() returns 0"); return 0; } // -------------------------------------------------------------------------- IOReturn AppleTexasAudio::sndHWSetProgOutput ( UInt32 outputBits ) { debugIOLog ( 3, "± AppleTexasAudio::sndHWSetProgOutput ( 0x%lX ) returns kIOReturnSuccess", outputBits ); return kIOReturnSuccess; } #pragma mark +INTERRUPT HANDLERS // -------------------------------------------------------------------------- Boolean AppleTexasAudio::IsSpeakerConnected (void) { UInt8 dallasSenseContents; Boolean connection; connection = FALSE; if (NULL != dallasIntProvider) { dallasSenseContents = *(dallasExtIntGpio); if ((dallasSenseContents & (1 << 1)) == (dallasInsertedActiveState << 1)) { connection = TRUE; } else { connection = FALSE; } } debugIOLog ( 3, "± AppleTexasAudio::IsSpeakerConnected() returns %d", connection ); return connection; } // ====================================================================================== // This timer is used to defer a headphone interrupt until the audio system has completed // a transition to the wake state. The timer will re-queue itself if the audio system // has not reached the wake state. [3103075] rbm 11/27/2002 void AppleTexasAudio::deferHeadphoneHandler (OSObject *owner, IOTimerEventSource *sender) { AbsoluteTime fireTime; UInt64 nanos; AppleTexasAudio * device; device = OSDynamicCast (AppleTexasAudio, owner); FailIf (NULL == device, Exit); if ( kIOAudioDeviceSleep != device->ourPowerState ) { // Audio system is awake so handle the interrupt normally. [3103075] device->RealHeadphoneInterruptHandler (0, 0); } else { // Audio system is not awake so defer the interrupt until audio is awake. [3103075] clock_get_uptime (&fireTime); absolutetime_to_nanoseconds (fireTime, &nanos); nanos += kDeferInsertionDelayNanos; // Schedule 250 milliseconds in the future... if (NULL != device->deferHeadphoneHandlerTimer /* && NULL != sender */) { nanoseconds_to_absolutetime (nanos, &fireTime); device->deferHeadphoneHandlerTimer->cancelTimeout (); device->deferHeadphoneHandlerTimer->wakeAtTime (fireTime); } } Exit: return; } // ====================================================================================== // Defers the invoking of the Dallas one-wireȘ protocol until the Vdd has stabilized. // The timer is also used to defer a dallas interrupt until the audio system has completed // a transition to the wake state. The timer will re-queue itself if the audio system // has not reached the wake state. [3103075] rbm 11/27/2002 void AppleTexasAudio::DallasInterruptHandlerTimer (OSObject *owner, IOTimerEventSource *sender) { AppleTexasAudio * device; IOCommandGate * cg; AbsoluteTime currTime; OSNumber * activeOutput; AbsoluteTime fireTime; UInt64 nanos; device = OSDynamicCast (AppleTexasAudio, owner); FailIf (NULL == device, Exit); if ( kIOAudioDeviceActive == device->ourPowerState ) { // Audio system is awake so handle the interrupt normally. [3103075] device->dallasSpeakersConnected = device->IsSpeakerConnected (); debugIOLog (3, " dallas speakers connected = %d", (unsigned int)device->dallasSpeakersConnected); if (TRUE == device->dallasSpeakersProbed && FALSE == device->dallasSpeakersConnected) { // They've unplugged the dallas speakers, so we'll need to check them out if they get plugged back in. device->dallasSpeakersProbed = FALSE; } if (FALSE == device->IsHeadphoneConnected ()) { // Set the proper EQ cg = device->getCommandGate (); if (NULL != cg) { cg->runAction (DeviceInterruptServiceAction); } device->SetActiveOutput (kSndHWOutput2, kBiquadUntouched); clock_get_uptime (&currTime); absolutetime_to_nanoseconds (currTime, &device->savedNanos); if (TRUE == device->dallasSpeakersConnected) { if (NULL != device->outputSelector) { activeOutput = OSNumber::withNumber (kIOAudioOutputPortSubTypeExternalSpeaker, 32); device->outputSelector->hardwareValueChanged (activeOutput); activeOutput->release (); } } else { if (NULL != device->outputSelector) { activeOutput = OSNumber::withNumber (kIOAudioOutputPortSubTypeInternalSpeaker, 32); device->outputSelector->hardwareValueChanged (activeOutput); activeOutput->release (); } } } if (NULL != device->dallasIntEventSource) { device->dallasIntEventSource->enable(); } } else { // Audio system is not awake so defer the interrupt until audio is awake. [3103075] if (NULL != device->dallasHandlerTimer) { clock_get_uptime (&fireTime); absolutetime_to_nanoseconds (fireTime, &nanos); nanos += kInsertionDelayNanos; // Schedule 4s in the future... nanoseconds_to_absolutetime (nanos, &fireTime); if ( NULL != sender ) { sender->cancelTimeout (); sender->wakeAtTime (fireTime); } } } Exit: return; } // ====================================================================================================== // Set a flag to say if the dallas speakers are plugged in or not so we know which EQ to use. void AppleTexasAudio::RealDallasInterruptHandler (IOInterruptEventSource *source, int count) { AbsoluteTime fireTime; UInt64 nanos; // call DallasInterruptHandlerTimer in a bit to check for the dallas rom (and complete speaker insertion). if (NULL != dallasHandlerTimer) { clock_get_uptime (&fireTime); absolutetime_to_nanoseconds (fireTime, &nanos); nanos += kInsertionDelayNanos; // Schedule 4s in the future... nanoseconds_to_absolutetime (nanos, &fireTime); dallasHandlerTimer->cancelTimeout (); dallasHandlerTimer->wakeAtTime (fireTime); } return; } // ====================================================================================================== // Put changes in RealDallasInterruptHandler, not in here because this is a static member and the // RealDallasInterruptHandler is in virtual space!!! // This function is invoked from the hardware interrupt handler: // gpio service provider // dallasInterruptHandler <<--------- YOU ARE HERE! // RealDallasInterruptHandler // DallasInterruptTimerHandler void AppleTexasAudio::dallasInterruptHandler(OSObject *owner, IOInterruptEventSource *source, int count) { AbsoluteTime currTime; UInt64 currNanos; AppleTexasAudio * appleTexasAudio; appleTexasAudio = OSDynamicCast (AppleTexasAudio, owner); FailIf (!appleTexasAudio, Exit); // Need this disable for when we call dallasInterruptHandler instead of going through the interrupt filter appleTexasAudio->dallasIntEventSource->disable(); clock_get_uptime (&currTime); absolutetime_to_nanoseconds (currTime, &currNanos); if ((currNanos - appleTexasAudio->savedNanos) > 10000000) { appleTexasAudio->RealDallasInterruptHandler (source, count); } else { appleTexasAudio->dallasIntEventSource->enable(); } Exit: return; } // static "action" function to connect to our object // return TRUE if you want the handler function to be called, or FALSE if you don't want it to be called. bool AppleTexasAudio::interruptFilter(OSObject *owner, IOFilterInterruptEventSource *src) { AppleTexasAudio* self = (AppleTexasAudio*) owner; self->dallasIntEventSource->disable(); return (true); } // -------------------------------------------------------------------------- // This is called to tell the user that they may not have plugged their speakers in all the way. void AppleTexasAudio::DisplaySpeakersNotFullyConnected (OSObject *owner, IOTimerEventSource *sender) { AppleTexasAudio * appleTexasAudio; IOCommandGate * cg; AbsoluteTime currTime; UInt32 deviceID; UInt8 bEEPROM[32]; Boolean result; IOReturn error; debugIOLog ( 3, "+ DisplaySpeakersNotFullyConnected ( %p, %p )", owner, sender ); appleTexasAudio = OSDynamicCast (AppleTexasAudio, owner); FailIf (!appleTexasAudio, Exit); deviceID = appleTexasAudio->GetDeviceMatch (); if (kExternalSpeakersActive == deviceID) { if (NULL != appleTexasAudio->dallasDriver) { appleTexasAudio->dallasIntEventSource->disable (); result = appleTexasAudio->dallasDriver->getSpeakerID (bEEPROM); appleTexasAudio->dallasIntEventSource->enable (); clock_get_uptime (&currTime); absolutetime_to_nanoseconds (currTime, &appleTexasAudio->savedNanos); if (FALSE == result && TRUE == appleTexasAudio->IsSpeakerConnected() ) { // FALSE == failure for DallasDriver [3103075] rbm 12/2/2002 error = KUNCUserNotificationDisplayNotice ( 0, // Timeout in seconds 0, // Flags (for later usage) "", // iconPath (not supported yet) "", // soundPath (not supported yet) "/System/Library/Extensions/Apple02Audio.kext", // localizationPath "HeaderOfDallasPartialInsert", // the header "StringOfDallasPartialInsert", "ButtonOfDallasPartialInsert"); if (kIOReturnSuccess != error) { appleTexasAudio->notifierHandlerTimer->setTimeoutMS (kNotifyTimerDelay); } else { IOLog ("The device plugged into the Apple speaker mini-jack cannot be recognized."); IOLog ("Remove the plug from the jack. Then plug it back in and make sure it is fully inserted."); } } else { // Speakers are fully plugged in now, so load the proper EQ for them appleTexasAudio->dallasIntEventSource->disable (); cg = appleTexasAudio->getCommandGate (); if (NULL != cg) { cg->runAction (DeviceInterruptServiceAction); } appleTexasAudio->dallasIntEventSource->enable (); clock_get_uptime (&currTime); absolutetime_to_nanoseconds (currTime, &appleTexasAudio->savedNanos); } } } Exit: debugIOLog ( 3, "- DisplaySpeakersNotFullyConnected ( %p, %p )", owner, sender ); return; } // -------------------------------------------------------------------------- Boolean AppleTexasAudio::IsHeadphoneConnected (void) { UInt8 headphoneSenseContents; Boolean connection; // check the state of the extint-gpio15 pin for the actual state of the headphone jack // do this because we get a false interrupt when waking from sleep that makes us think // that the headphones were removed during sleep, even if they are still connected. debugIOLog ( 3, "+ AppleTexasAudio::IsHeadphoneConnected()" ); connection = FALSE; if (NULL != headphoneIntEventSource) { headphoneSenseContents = *headphoneExtIntGpio; if ((headphoneSenseContents & (1 << 1)) == (headphoneInsertedActiveState << 1)) { // headphones are inserted connection = TRUE; } else { // headphones are not inserted connection = FALSE; } } debugIOLog ( 3, "- AppleTexasAudio::IsHeadphoneConnected() returns %d", connection ); return connection; } // -------------------------------------------------------------------------- // make sure we're on the command gate because DeviceInterruptService will make calls to IOAudioFamily IOReturn AppleTexasAudio::DeviceInterruptServiceAction (OSObject *owner, void *arg1, void *arg2, void *arg3, void *arg4) { AppleTexasAudio * appleTexasAudio; appleTexasAudio = OSDynamicCast (AppleTexasAudio, owner); FailIf (!appleTexasAudio, Exit); appleTexasAudio->DeviceInterruptService (); Exit: return kIOReturnSuccess; } // -------------------------------------------------------------------------- void AppleTexasAudio::RealHeadphoneInterruptHandler (IOInterruptEventSource *source, int count) { IOCommandGate * cg; OSNumber * activeOutput; debugIOLog ( 3, "+ AppleTexasAudio::RealHeadphoneInterruptHandler ( %p, %d )", source, count ); // Audio system is awake so handle the interrupt normally. [3103075] SetActiveOutput (kSndHWOutputNone, kBiquadUntouched); cg = getCommandGate (); if (NULL != cg) { cg->runAction (DeviceInterruptServiceAction); } headphonesConnected = IsHeadphoneConnected (); if (TRUE == headphonesConnected) { SetActiveOutput (kSndHWOutput1, kBiquadUntouched); if (NULL != outputSelector) { activeOutput = OSNumber::withNumber (kIOAudioOutputPortSubTypeHeadphones, 32); outputSelector->hardwareValueChanged (activeOutput); activeOutput->release (); } } else { SetActiveOutput (kSndHWOutput2, kBiquadUntouched); if (NULL != outputSelector) { if (FALSE == IsSpeakerConnected()) { activeOutput = OSNumber::withNumber (kIOAudioOutputPortSubTypeInternalSpeaker, 32); } else { activeOutput = OSNumber::withNumber (kIOAudioOutputPortSubTypeExternalSpeaker, 32); } outputSelector->hardwareValueChanged (activeOutput); activeOutput->release (); } } if (TRUE == hasVideo) { // Tell the video driver about the jack state change in case a video connector was plugged in publishResource (gAppleAudioVideoJackStateKey, headphonesConnected ? kOSBooleanTrue : kOSBooleanFalse); } debugIOLog ( 3, "- AppleTexasAudio::RealHeadphoneInterruptHandler ( %p, %d )", source, count ); } // -------------------------------------------------------------------------- void AppleTexasAudio::headphoneInterruptHandler (OSObject *owner, IOInterruptEventSource *source, int count) { AppleTexasAudio * appleTexasAudio; appleTexasAudio = OSDynamicCast (AppleTexasAudio, owner); FailIf (!appleTexasAudio, Exit); appleTexasAudio->deferHeadphoneHandler (appleTexasAudio, appleTexasAudio->deferHeadphoneHandlerTimer); Exit: return; } #if 0 // =========================================================================================== // Override the Apple02Audio implementation here as part of [3103075]. This is necessary // because any jack state changes are deferred while the audio system is sleeping. We need // the audio hardware to wake up so that any UI changes resulting from a jack state change // can be updated (i.e. removal or construction of the balance control when going from stereo // to mono or mono to stereo respectively). [3103075] rbm 27 Nov 2002 IOReturn AppleTexasAudio::performPowerStateChange(IOAudioDevicePowerState oldPowerState, IOAudioDevicePowerState newPowerState, UInt32 *microsecondsUntilComplete) { IOReturn result; debugIOLog (3, "+ AppleTexasAudio::performPowerStateChange ( %d, %d, %d ), ourPowerState = %d", oldPowerState, newPowerState, microsecondsUntilComplete, ourPowerState); if (NULL != theAudioPowerObject && NULL != microsecondsUntilComplete) { *microsecondsUntilComplete = theAudioPowerObject->GetTimeToChangePowerState (ourPowerState, newPowerState); } result = IOAudioDevice::performPowerStateChange (oldPowerState, newPowerState, microsecondsUntilComplete); if (NULL != theAudioPowerObject) { switch (newPowerState) { case kIOAudioDeviceSleep: if (ourPowerState == kIOAudioDeviceActive) { outputMuteChange (TRUE); // Mute before turning off power theAudioPowerObject->setHardwarePowerOff (); ourPowerState = newPowerState; } break; case kIOAudioDeviceIdle: if (ourPowerState == kIOAudioDeviceActive) { outputMuteChange (TRUE); // Mute before turning off power theAudioPowerObject->setHardwarePowerOff (); ourPowerState = kIOAudioDeviceSleep; } else if (ourPowerState == kIOAudioDeviceSleep && NULL != dallasDriver) { outputMuteChange (TRUE); // Mute before turning off power theAudioPowerObject->setHardwarePowerOn (); ourPowerState = kIOAudioDeviceActive; } break; case kIOAudioDeviceActive: theAudioPowerObject->setHardwarePowerOn (); if (NULL != outMute) { outMute->flushValue (); // Restore hardware to the user's selected state } ourPowerState = newPowerState; break; default: break; } } debugIOLog (3, "- AppleTexasAudio::performPowerStateChange ( %d, %d, %d ) returns 0x%lX, ourPowerState = %d", oldPowerState, newPowerState, microsecondsUntilComplete, result, ourPowerState); return result; } #endif #pragma mark +DIRECT HARDWARE MANIPULATION // -------------------------------------------------------------------------- UInt8 * AppleTexasAudio::getGPIOAddress (UInt32 gpioSelector) { UInt8 * gpioAddress; gpioAddress = NULL; switch (gpioSelector) { case kHeadphoneMuteSel: gpioAddress = hdpnMuteGpio; break; case kHeadphoneDetecteSel: gpioAddress = headphoneExtIntGpio; break; case kAmplifierMuteSel: gpioAddress = ampMuteGpio; break; case kSpeakerIDSel: gpioAddress = dallasExtIntGpio; break; case kCodecResetSel: gpioAddress = hwResetGpio; break; } if ( NULL == gpioAddress ) { debugIOLog (3, "± AppleTexasAudio::getGPIOAddress ( %d ) returns NULL", (unsigned int)gpioSelector ); } return gpioAddress; } // -------------------------------------------------------------------------- Boolean AppleTexasAudio::getGPIOActiveState (UInt32 gpioSelector) { Boolean activeState; activeState = NULL; switch (gpioSelector) { case kHeadphoneMuteSel: activeState = hdpnActiveState; break; case kHeadphoneDetecteSel: activeState = headphoneInsertedActiveState; break; case kAmplifierMuteSel: activeState = ampActiveState; break; case kSpeakerIDSel: activeState = dallasInsertedActiveState; break; case kCodecResetSel: activeState = hwResetActiveState; break; default: debugIOLog (3, "± AppleTexasAudio::getGPIOActiveState ( %d ) UNKNOWN", (unsigned int)gpioSelector ); break; } return activeState; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ void AppleTexasAudio::setGPIOActiveState ( UInt32 selector, UInt8 gpioActiveState ) { gpioActiveState = 0 == gpioActiveState ? FALSE : TRUE; switch ( selector ) { case kHeadphoneMuteSel: hdpnActiveState = gpioActiveState; break; case kHeadphoneDetecteSel: headphoneInsertedActiveState = gpioActiveState; break; case kAmplifierMuteSel: ampActiveState = gpioActiveState; break; case kSpeakerIDSel: dallasInsertedActiveState = gpioActiveState; break; case kCodecResetSel: hwResetActiveState = gpioActiveState; break; default: debugIOLog (3, "± AppleTexasAudio::setGPIOActiveState ( %d, %d ) UNKNOWN", (unsigned int)selector, gpioActiveState ); break; } return; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Boolean AppleTexasAudio::checkGpioAvailable ( UInt32 selector ) { Boolean result = FALSE; switch ( selector ) { case kHeadphoneMuteSel: if ( NULL != hdpnMuteGpio ) { result = TRUE; } break; case kHeadphoneDetecteSel: if ( NULL != headphoneExtIntGpio ) { result = TRUE; } break; case kAmplifierMuteSel: if ( NULL != ampMuteGpio ) { result = TRUE; } break; case kSpeakerIDSel: if ( NULL != dallasExtIntGpio ) { result = TRUE; } break; case kCodecResetSel: if ( NULL != hwResetGpio ) { result = TRUE; } break; } return result; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // [3060321] Fixed kI2sDataWordFormatRegisterSelector to reference the // data word format register. IOReturn AppleTexasAudio::readHWReg32 ( UInt32 selector, UInt32 * registerData ) { IOReturn result = kIOReturnError; if ( NULL != registerData ) { result = kIOReturnSuccess; switch ( selector ) { case kI2sSerialFormatRegisterSelector: *registerData = audioI2SControl->GetSerialFormatReg(); break; case kI2sDataWordFormatRegisterSelector: *registerData = audioI2SControl->GetDataWordSizesReg(); break; case kFeatureControlRegister1Selector: *registerData = audioI2SControl->FCR1GetReg(); break; case kFeatureControlRegister3Selector: *registerData = audioI2SControl->FCR3GetReg(); break; case kI2s1SerialFormatRegisterSelector: *registerData = audioI2SControl->GetSerialFormatReg(); break; case kI2s1DataWordFormatRegisterSelector: *registerData = audioI2SControl->GetDataWordSizesReg(); break; default: result = kIOReturnError; break; } } return result; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // [3060321] Fixed kI2sDataWordFormatRegisterSelector to reference the // data word format register. IOReturn AppleTexasAudio::writeHWReg32 ( UInt32 selector, UInt32 registerData ) { IOReturn result = kIOReturnError; result = kIOReturnSuccess; switch ( selector ) { case kI2sSerialFormatRegisterSelector: audioI2SControl->SetSerialFormatReg( registerData ); break; case kI2sDataWordFormatRegisterSelector: audioI2SControl->SetDataWordSizesReg( registerData ); break; case kFeatureControlRegister1Selector: audioI2SControl->Fcr1SetReg( registerData ); break; case kFeatureControlRegister3Selector: audioI2SControl->Fcr3SetReg( registerData ); break; case kI2s1SerialFormatRegisterSelector: audioI2SControl->SetSerialFormatReg( registerData ); break; case kI2s1DataWordFormatRegisterSelector: audioI2SControl->SetFrameMatchReg( registerData ); break; default: result = kIOReturnError; break; } return result; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ IOReturn AppleTexasAudio::readCodecReg ( UInt32 selector, void * registerData, UInt32 * registerDataSize ) { UInt32 codecCacheSize; IOReturn err = kIOReturnError; codecCacheSize = sizeof ( TAS3001C_ShadowReg ); if ( NULL != registerDataSize && NULL != registerData ) { if ( codecCacheSize <= *registerDataSize && 0 != codecCacheSize && 0 == selector ) { for ( UInt32 index = 0; index < codecCacheSize; index++ ) { ((UInt8*)registerData)[index] = ((UInt8*)&shadowRegs)[index]; } err = kIOReturnSuccess; if ( kIOReturnSuccess == err ) { *registerDataSize = codecCacheSize; } } } return err; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ IOReturn AppleTexasAudio::writeCodecReg ( UInt32 selector, void * registerData ) { UInt32 codecRegSize; IOReturn err = 0; err = getCodecRegSize ( selector, &codecRegSize ); if ( kIOReturnSuccess == err ) { err = TAS3001C_WriteRegister( (UInt8)selector, (UInt8*)registerData ); } return err; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ UInt8 AppleTexasAudio::readGPIO (UInt32 selector) { UInt8 * address; UInt8 gpioValue; gpioValue = NULL; address = getGPIOAddress (selector); if (NULL != address) gpioValue = GpioReadByte (address); return (gpioValue); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ IOReturn AppleTexasAudio::getVolumePRAM ( UInt32 * pramDataPtr ) { UInt8 curPRAMVol; IODTPlatformExpert * platform; IOReturn err = kIOReturnError; curPRAMVol = 0; if ( NULL != pramDataPtr ) { platform = OSDynamicCast(IODTPlatformExpert,getPlatform()); if (platform) { platform->readXPRAM((IOByteCount)kPRamVolumeAddr, &curPRAMVol, (IOByteCount)1); *pramDataPtr = (UInt32)curPRAMVol; err = kIOReturnSuccess; } } return err; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ IOReturn AppleTexasAudio::getDmaState ( UInt32 * dmaStatePtr ) { IOReturn err = kIOReturnError; if ( NULL != dmaStatePtr && NULL != driverDMAEngine ) { *dmaStatePtr = (UInt32)driverDMAEngine->getDmaState(); err = kIOReturnSuccess; } return err; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ IOReturn AppleTexasAudio::getStreamFormat ( IOAudioStreamFormat * streamFormatPtr ) { IOReturn err = kIOReturnError; if ( NULL != streamFormatPtr ) { err = driverDMAEngine->getAudioStreamFormat( streamFormatPtr ); } return err; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ IOReturn AppleTexasAudio::readPowerState ( UInt32 selector, IOAudioDevicePowerState * powerState ) { IOReturn err = kIOReturnError; if ( NULL != powerState ) { *powerState = ourPowerState; err = kIOReturnSuccess; } return err; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ IOReturn AppleTexasAudio::setPowerState ( UInt32 selector, IOAudioDevicePowerState powerState ) { UInt32 microsecondsUntilComplete; IOReturn err = kIOReturnError; if ( NULL != powerState ) { err = performPowerStateChange ( ourPowerState, powerState, µsecondsUntilComplete ); } return err; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // User Client Support IOReturn AppleTexasAudio::setBiquadCoefficients ( UInt32 selector, void * biquadCoefficients, UInt32 coefficientSize ) { IOReturn err; IOReturn totalErr = kIOReturnError; if ( kMaxBiquadWidth >= coefficientSize && NULL != biquadCoefficients && 0 == selector ) { totalErr = kIOReturnSuccess; for ( UInt32 index = 0; index < ( kTumblerNumBiquads * kTumblerMaxStreamCnt ); index ++ ) { if ( kTumblerNumBiquads > index ) { err = SndHWSetOutputBiquad ( kStreamFrontLeft, index, (FourDotTwenty*)biquadCoefficients ); } else { err = SndHWSetOutputBiquad ( kStreamFrontRight, index - kTumblerNumBiquads, (FourDotTwenty*)biquadCoefficients ); } (( EQFilterCoefficients*)biquadCoefficients)++; if ( err ) { totalErr = err; } } } return totalErr; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // User Client Support IOReturn AppleTexasAudio::getBiquadInformation ( UInt32 scalarArg1, void * outStructPtr, IOByteCount * outStructSizePtr ) { #pragma unused ( scalarArg1 ) IOReturn result = kIOReturnError; if ( NULL != outStructPtr && NULL != outStructSizePtr ) { if ( *outStructSizePtr >= ( sizeof ( BiquadInfoList ) + ( sizeof ( UInt32 ) * ( kTumblerCoefficientsPerBiquad - 1 ) ) ) ) { ((BiquadInfoListPtr)outStructPtr)->numBiquad = kTumblerNumBiquads; ((BiquadInfoListPtr)outStructPtr)->numCoefficientsPerBiquad = kTumblerCoefficientsPerBiquad; ((BiquadInfoListPtr)outStructPtr)->biquadCoefficientBitWidth = kTumblerCoefficientBitWidth; ((BiquadInfoListPtr)outStructPtr)->coefficientIntegerBitWidth = kTumblerCoefficientIntegerBitWidth; ((BiquadInfoListPtr)outStructPtr)->coefficientFractionBitWidth = kTumblerCoefficientFractionBitWidth; ((BiquadInfoListPtr)outStructPtr)->coefficientOrder[0] = 'b0 '; ((BiquadInfoListPtr)outStructPtr)->coefficientOrder[1] = 'b1 '; ((BiquadInfoListPtr)outStructPtr)->coefficientOrder[2] = 'b2 '; ((BiquadInfoListPtr)outStructPtr)->coefficientOrder[3] = 'a1 '; ((BiquadInfoListPtr)outStructPtr)->coefficientOrder[4] = 'a2 '; *outStructSizePtr = ( sizeof ( BiquadInfoList ) + ( sizeof ( UInt32 ) * ( kTumblerCoefficientsPerBiquad - 1 ) ) ); result = kIOReturnSuccess; } } return result; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // User Client Support IOReturn AppleTexasAudio::getProcessingParameters ( UInt32 scalarArg1, void * outStructPtr, IOByteCount * outStructSizePtr ) { UInt32 index; IOReturn err = kIOReturnNotReadable; if ( NULL != outStructPtr && NULL != outStructSizePtr ) { if ( kMaxProcessingParamSize >= *outStructSizePtr ) { for ( index = 0; index < ( kMaxProcessingParamSize / sizeof(UInt32) ); index++ ) { ((UInt32*)outStructPtr)[index] = mProcessingParams[index]; } /* STUB: Insert code here (see Aram and/or Joe) */ /* END STUB */ err = kIOReturnSuccess; } } return (err); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // User Client Support // If loading software EQ, the scalarArg1 will be set to 'true' to indicate // that hardware EQ should be disabled. If re-enabling hardware EQ then // scalarArg1 will be set to 'false' and the proper EQ for the current // hardware environment (jack state, speaker model, etc.) must be applied. IOReturn AppleTexasAudio::setProcessingParameters ( UInt32 scalarArg1, void * inStructPtr, UInt32 inStructSize ) { EQPrefsElementPtr eqPrefs; UInt32 index; IOReturn err = kIOReturnNotReadable; debugIOLog ( 3, "+ AppleTexasAudio::setProcessingParameters ( %ld, %p, %ld )", scalarArg1, inStructPtr, inStructSize ); if ( NULL != inStructPtr && kMaxProcessingParamSize >= inStructSize ) { disableLoadingEQFromFile = (bool)scalarArg1; for ( index = 0; index < ( kMaxProcessingParamSize / sizeof(UInt32) ); index++ ) { mProcessingParams[index] = ((UInt32*)inStructPtr)[index]; } if ( disableLoadingEQFromFile ) { SetUnityGainAllPass(); } else { err = GetCustomEQCoefficients (layoutID, deviceID, speakerID, &eqPrefs); if ( kIOReturnSuccess == err && NULL != eqPrefs ) { DRCInfo localDRC; // Set the dynamic range compressor coefficients. localDRC.compressionRatioNumerator = eqPrefs->drcCompressionRatioNumerator; localDRC.compressionRatioDenominator = eqPrefs->drcCompressionRatioDenominator; localDRC.threshold = eqPrefs->drcThreshold; localDRC.maximumVolume = eqPrefs->drcMaximumVolume; localDRC.enable = (Boolean)((UInt32)(eqPrefs->drcEnable)); err = SndHWSetDRC ((DRCInfoPtr)&localDRC); FailIf ( kIOReturnSuccess != err, Exit ); err = SndHWSetOutputBiquadGroup (eqPrefs->filterCount, eqPrefs->filter[0].coefficient); FailIf ( kIOReturnSuccess != err, Exit ); } else { SetUnityGainAllPass (); } } /* STUB: Insert code here (see Aram and/or Joe) */ //driverDMAEngine->set_swEQ(inStructPtr); /* END STUB */ err = kIOReturnSuccess; } Exit: debugIOLog ( 3, "- AppleTexasAudio::setProcessingParameters ( %ld, %p, %ld ) returns %lX", scalarArg1, inStructPtr, inStructSize, err ); return (err); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ IOReturn AppleTexasAudio::invokeInternalFunction ( UInt32 functionSelector, void * inData ) { switch ( functionSelector ) { case kInvokeHeadphoneInterruptHandler: RealHeadphoneInterruptHandler ( 0, 0 ); break; case kInvokeSpeakerInterruptHandler: RealDallasInterruptHandler ( 0, 0 ); break; } return kIOReturnSuccess; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ void AppleTexasAudio::writeGPIO (UInt32 selector, UInt8 data) { UInt8 * address; UInt32 gpioValue; gpioValue = NULL; address = getGPIOAddress (selector); if (NULL != address) GpioWriteByte (address, data); return; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Return 'true' if the 'gpioDDR' bit is non-zero. UInt8 AppleTexasAudio::GpioGetDDR( UInt8* gpioAddress ) { UInt8 gpioData; Boolean result; result = gpioDDR_INPUT; if( NULL != gpioAddress ) { gpioData = *gpioAddress; if( 0 != ( gpioData & ( 1 << gpioDDR ) ) ) { result = gpioDDR_OUTPUT; } } debugIOLog ( 5, "± AppleTexasAudio::GpioGetDDR ( %p ) returns 0x%X", gpioAddress, result ); return result; } // -------------------------------------------------------------------------- UInt8 AppleTexasAudio::GpioReadByte( UInt8* gpioAddress ) { UInt8 gpioData; gpioData = 0; if( NULL != gpioAddress ) { gpioData = *gpioAddress; } debugIOLog ( 5, "± AppleTexasAudio::GpioReadByte ( %p ) returns 0x%X", gpioAddress, gpioData ); return gpioData; } // -------------------------------------------------------------------------- void AppleTexasAudio::GpioWriteByte( UInt8* gpioAddress, UInt8 gpioData ) { if( NULL != gpioAddress ) { *gpioAddress = gpioData; debugIOLog (5, "± AppleTexasAudio::GpioWrite ( 0x%8.0X, 0x%2.0X )", (unsigned int)gpioAddress, gpioData); } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Return 'true' if the 'gpioData' bit is non-zero. This function does not // return the state of the pin. Boolean AppleTexasAudio::GpioRead( UInt8* gpioAddress ) { UInt8 gpioData; Boolean result; result = 0; if( NULL != gpioAddress ) { gpioData = *gpioAddress; if( 0 != ( gpioData & ( 1 << gpioDATA ) ) ) { result = 1; } } debugIOLog ( 5, "± AppleTexasAudio::GpioRead ( %p ) returns 0x%X", gpioAddress, result ); return result; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Sets the 'gpioDDR' to OUTPUT and sets the 'gpioDATA' to the 'data' state. void AppleTexasAudio::GpioWrite( UInt8* gpioAddress, UInt8 data ) { UInt8 gpioData; if( NULL != gpioAddress ) { if( 0 == data ) gpioData = ( gpioDDR_OUTPUT << gpioDDR ) | ( 0 << gpioDATA ); else gpioData = ( gpioDDR_OUTPUT << gpioDDR ) | ( 1 << gpioDATA ); *gpioAddress = gpioData; debugIOLog (5, "± AppleTexasAudio::GpioWrite( 0x%8.0X, 0x%2.0X ), *gpioAddress 0x%2.0X", (unsigned int)gpioAddress, gpioData, *gpioAddress ); } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Returns information regarding the CPU ID, port ID and speaker ID. The // speaker ID is a concatenation of the Dallas ROM 8 bit fields to form a // 32 bit field where the family ID occupies the least significant byte of // the 32 bit field. The remaining fields appear in ascending order. // // speakerID = 0xCCFFTTSS where: CC = Connected // FF = DallasID.deviceFamily // TT = DallasID.deviceType // SS = DallasID.deviceSubType (not supported currently, returns 0) // IOReturn AppleTexasAudio::readSpkrID ( UInt32 selector, UInt32 * speakerIDPtr ) { UInt8 bEEPROM[32]; IOReturn result = kIOReturnError; if ( NULL != speakerIDPtr ) { for ( UInt32 index = 0; index < 4; index++ ) { bEEPROM[index] = 0; } if ( selector ) { // Force a ROM access here. WARNING: This may cause corruption the equalizer set // and should only be used with caution for diagnostic purposes [3053696] if ( IsSpeakerConnected () && NULL != dallasDriver ) { if ( dallasDriver->getSpeakerID (bEEPROM) ) { *speakerIDPtr = ( IsSpeakerConnected () << kSpeakerID_Connected ) | ( bEEPROM[0] << kSpeakerID_Family ) | ( bEEPROM[1] << kSpeakerID_Type ) | ( bEEPROM[2] << kSpeakerID_SubType ); } } } else { // Default method for accessing speaker ID information is to use the data that // has been cached by the speaker detect interrupt handler. *speakerIDPtr = ( IsHeadphoneConnected () << kHeadphone_Connected ) | \ ( IsSpeakerConnected () << kSpeakerID_Connected ) | \ ( familyID << kSpeakerID_Family ) | \ ( speakerID << kSpeakerID_Type ) | \ ( 0 << kSpeakerID_SubType ); } result = kIOReturnSuccess; } return result; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ IOReturn AppleTexasAudio::getCodecRegSize ( UInt32 selector, UInt32 * codecRegSizePtr ) { IOReturn err = kIOReturnError; if ( NULL != codecRegSizePtr ) { *codecRegSizePtr = 0; err = kIOReturnSuccess; switch( selector ) { case kMainCtrlReg: *codecRegSizePtr = kMCRwidth; break; case kDynamicRangeCtrlReg: *codecRegSizePtr = kDRCwidth; break; case kVolumeCtrlReg: *codecRegSizePtr = kVOLwidth; break; case kTrebleCtrlReg: *codecRegSizePtr = kTREwidth; break; case kBassCtrlReg: *codecRegSizePtr = kBASwidth; break; case kMixer1CtrlReg: *codecRegSizePtr = kMIXwidth; break; case kMixer2CtrlReg: *codecRegSizePtr = kMIXwidth; break; case kLeftBiquad0CtrlReg: *codecRegSizePtr = kBIQwidth; break; case kLeftBiquad1CtrlReg: *codecRegSizePtr = kBIQwidth; break; case kLeftBiquad2CtrlReg: *codecRegSizePtr = kBIQwidth; break; case kLeftBiquad3CtrlReg: *codecRegSizePtr = kBIQwidth; break; case kLeftBiquad4CtrlReg: *codecRegSizePtr = kBIQwidth; break; case kLeftBiquad5CtrlReg: *codecRegSizePtr = kBIQwidth; break; case kRightBiquad0CtrlReg: *codecRegSizePtr = kBIQwidth; break; case kRightBiquad1CtrlReg: *codecRegSizePtr = kBIQwidth; break; case kRightBiquad2CtrlReg: *codecRegSizePtr = kBIQwidth; break; case kRightBiquad3CtrlReg: *codecRegSizePtr = kBIQwidth; break; case kRightBiquad4CtrlReg: *codecRegSizePtr = kBIQwidth; break; case kRightBiquad5CtrlReg: *codecRegSizePtr = kBIQwidth; break; default: err = kIOReturnBadArgument; break; } if ( kIOReturnSuccess != err ) { debugIOLog (3, "± AppleTexasAudio::getCodecRegSize ( %X, %X ) returns %X", (unsigned int)selector, (unsigned int)codecRegSizePtr, (unsigned int)err ); } } return err; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ IOReturn AppleTexasAudio::InitEQSerialMode (UInt32 mode, Boolean restoreOnNormal) { IOReturn err; UInt8 initData; UInt8 previousData; debugIOLog (3, "+ AppleTexasAudio::InitEQSerialMode (%8lX, %d)", mode, restoreOnNormal); initData = (kNormalLoad << kFL); if (kSetFastLoadMode == mode) initData = (kFastLoad << kFL); err = TAS3001C_ReadRegister ( kMainCtrlReg, &previousData ); initData |= (k64fs << kSC) | TAS_I2S_MODE | (TAS_WORD_LENGTH << kW0); err = TAS3001C_WriteRegister ( kMainCtrlReg, &initData ); // If restoring to normal load mode then restore the settings of all // registers that have been corrupted by entering fast load mode (i.e. // volume, bass, treble, mixer1 and mixer2). Restoration only occurs // if going from a previous state of Fast Load to a new state of Normal Load. if (kRestoreOnNormal == restoreOnNormal && ((kFastLoad << kFL) == (kFastLoad << kFL) & previousData)) { if ((kNormalLoad << kFL) == (initData & (kFastLoad << kFL))) { TAS3001C_WriteRegister (kVolumeCtrlReg, (UInt8*)shadowRegs.sVOL); TAS3001C_WriteRegister (kMixer1CtrlReg, (UInt8*)shadowRegs.sMX1); TAS3001C_WriteRegister (kMixer2CtrlReg, (UInt8*)shadowRegs.sMX2); TAS3001C_WriteRegister (kTrebleCtrlReg, (UInt8*)shadowRegs.sTRE); TAS3001C_WriteRegister (kBassCtrlReg, (UInt8*)shadowRegs.sBAS); } } debugIOLog ( 3, "- AppleTexasAudio::InitEQSerialMode (%8lX, %d) returns 0x%lX", mode, restoreOnNormal, err ); return err; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Updates the amplifier mute state and delays for amplifier settling if // the amplifier mute state is not the current mute state. IOReturn AppleTexasAudio::SetAmplifierMuteState( UInt32 ampID, Boolean muteState ) { IOReturn err; Boolean curMuteState; debugIOLog (3, "+ AppleTexasAudio::SetAmplifierMuteState ( %ld, %d )", ampID, muteState ); err = kIOReturnSuccess; switch( ampID ) { case kHEADPHONE_AMP: curMuteState = GpioRead( hdpnMuteGpio ); if( muteState != curMuteState ) { GpioWrite( hdpnMuteGpio, muteState ); debugIOLog (3, " updated HEADPHONE mute to %d", muteState ); } break; case kSPEAKER_AMP: curMuteState = GpioRead( ampMuteGpio ); if( muteState != curMuteState ) { GpioWrite( ampMuteGpio, muteState ); debugIOLog (3, " updated AMP mute to %d", muteState ); } break; default: err = kIOReturnBadArgument; break; } debugIOLog (3, "- AppleTexasAudio::SetAmplifierMuteState ( %ld, %d ) returns 0x%lX", ampID, muteState, err ); return err; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ IOReturn AppleTexasAudio::SetVolumeCoefficients( UInt32 left, UInt32 right ) { UInt8 volumeData[kVOLwidth]; IOReturn err; debugIOLog (3, "+ AppleTexasAudio::SetVolumeCoefficients ( 0x%lx, 0x%lx )", left, right); volumeData[2] = left; volumeData[1] = left >> 8; volumeData[0] = left >> 16; volumeData[5] = right; volumeData[4] = right >> 8; volumeData[3] = right >> 16; err = TAS3001C_WriteRegister( kVolumeCtrlReg, volumeData ); debugIOLog (3, "- AppleTexasAudio::SetVolumeCoefficients ( 0x%lx, 0x%lx ) returns 0x%lX", left, right, err); return err; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // This routine will perform a reset of the TAS3001C and then initialize // all registers within the TAS3001C to the values already held within // the shadow registers. The RESET sequence must not be performed until // the I2S clocks are running. The TAS3001C may hold the I2C bus signals // SDA and SCL low until the reset sequence (high->low->high) has been // completed. IOReturn AppleTexasAudio::TAS3001C_Initialize(UInt32 resetFlag) { IOReturn err; UInt32 retryCount; UInt8 oldMode; Boolean done; switch( resetFlag ) { case kFORCE_RESET_SETUP_TIME: debugIOLog (3, "+ TAS3001C_Initialize( %s )", "kFORCE_RESET_SETUP_TIME" ); break; case kNO_FORCE_RESET_SETUP_TIME: debugIOLog (3, "+ TAS3001C_Initialize( %s )", "kNO_FORCE_RESET_SETUP_TIME" ); break; default: debugIOLog (3, "+ TAS3001C_Initialize( %s )", "UNKNOWN" ); break; } err = kIOReturnDeviceError; done = false; oldMode = 0; retryCount = 0; if (!semaphores) { semaphores = 1; mPollingMode = false; // try to run with interrupts, will be set to true if we get errors. do{ debugIOLog (4, " RESETTING, retryCount %ld", retryCount ); TAS3001C_Reset( resetFlag ); // cycle reset from 1 through 0 and back to 1 if( 0 == oldMode ) TAS3001C_ReadRegister( kMainCtrlReg, &oldMode ); // save previous load mode err = InitEQSerialMode( kSetFastLoadMode, kDontRestoreOnNormal ); // set fast load mode for biquad initialization FailIf( kIOReturnSuccess != err, AttemptToRetry ); err = TAS3001C_WriteRegister( kLeftBiquad0CtrlReg, (UInt8*)shadowRegs.sLB0 ); FailIf( kIOReturnSuccess != err, AttemptToRetry ); err = TAS3001C_WriteRegister( kLeftBiquad1CtrlReg, (UInt8*)shadowRegs.sLB1 ); FailIf( kIOReturnSuccess != err, AttemptToRetry ); err = TAS3001C_WriteRegister( kLeftBiquad2CtrlReg, (UInt8*)shadowRegs.sLB2 ); FailIf( kIOReturnSuccess != err, AttemptToRetry ); err = TAS3001C_WriteRegister( kLeftBiquad3CtrlReg, (UInt8*)shadowRegs.sLB3 ); FailIf( kIOReturnSuccess != err, AttemptToRetry ); err = TAS3001C_WriteRegister( kLeftBiquad4CtrlReg, (UInt8*)shadowRegs.sLB4 ); FailIf( kIOReturnSuccess != err, AttemptToRetry ); err = TAS3001C_WriteRegister( kLeftBiquad5CtrlReg, (UInt8*)shadowRegs.sLB5 ); FailIf( kIOReturnSuccess != err, AttemptToRetry ); err = TAS3001C_WriteRegister( kRightBiquad0CtrlReg, (UInt8*)shadowRegs.sRB0 ); FailIf( kIOReturnSuccess != err, AttemptToRetry ); err = TAS3001C_WriteRegister( kRightBiquad1CtrlReg, (UInt8*)shadowRegs.sRB1 ); FailIf( kIOReturnSuccess != err, AttemptToRetry ); err = TAS3001C_WriteRegister( kRightBiquad1CtrlReg, (UInt8*)shadowRegs.sRB1 ); FailIf( kIOReturnSuccess != err, AttemptToRetry ); err = TAS3001C_WriteRegister( kRightBiquad2CtrlReg, (UInt8*)shadowRegs.sRB2 ); FailIf( kIOReturnSuccess != err, AttemptToRetry ); err = TAS3001C_WriteRegister( kRightBiquad3CtrlReg, (UInt8*)shadowRegs.sRB3 ); FailIf( kIOReturnSuccess != err, AttemptToRetry ); err = TAS3001C_WriteRegister( kRightBiquad4CtrlReg, (UInt8*)shadowRegs.sRB4 ); FailIf( kIOReturnSuccess != err, AttemptToRetry ); err = InitEQSerialMode( kSetNormalLoadMode, kDontRestoreOnNormal ); // set normal load mode for most register initialization FailIf( kIOReturnSuccess != err, AttemptToRetry ); err = TAS3001C_WriteRegister( kDynamicRangeCtrlReg, (UInt8*)shadowRegs.sDRC ); FailIf( kIOReturnSuccess != err, AttemptToRetry ); err = TAS3001C_WriteRegister( kVolumeCtrlReg, (UInt8*)shadowRegs.sVOL ); FailIf( kIOReturnSuccess != err, AttemptToRetry ); err = TAS3001C_WriteRegister( kTrebleCtrlReg, (UInt8*)shadowRegs.sTRE ); FailIf( kIOReturnSuccess != err, AttemptToRetry ); err = TAS3001C_WriteRegister( kBassCtrlReg, (UInt8*)shadowRegs.sBAS ); FailIf( kIOReturnSuccess != err, AttemptToRetry ); err = TAS3001C_WriteRegister( kMixer1CtrlReg, (UInt8*)shadowRegs.sMX1 ); FailIf( kIOReturnSuccess != err, AttemptToRetry ); err = TAS3001C_WriteRegister( kMixer2CtrlReg, (UInt8*)shadowRegs.sMX2 ); FailIf( kIOReturnSuccess != err, AttemptToRetry ); err = TAS3001C_WriteRegister( kMainCtrlReg, &oldMode ); // restore previous load mode FailIf( kIOReturnSuccess != err, AttemptToRetry ); AttemptToRetry: if( kIOReturnSuccess == err ) { // terminate when successful done = true; } else { mPollingMode = true; } retryCount++; } while ( !done && ( kRESET_MAX_RETRY_COUNT != retryCount ) ); semaphores = 0; if( kRESET_MAX_RETRY_COUNT == retryCount ) { debugIOLog (3, "##### FATAL ERROR: CANNOT INITIALIZE TAS3001 #####" ); } } debugIOLog (3, "- TAS3001C_Initialize( %ld ) returns %d", resetFlag, err ); return err; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Reading registers with the TAS3001C is not possible. A shadow register // is maintained for each TAS3001C hardware register. Whenever a write // operation is performed on a hardware register, the data is written to // the shadow register. Read operations copy data from the shadow register // to the client register buffer. IOReturn AppleTexasAudio::TAS3001C_ReadRegister(UInt8 regAddr, UInt8* registerData) { UInt32 registerSize; UInt32 regByteIndex; UInt8 *shadowPtr; IOReturn err; err = kIOReturnSuccess; // quiet warnings caused by a complier that can't really figure out if something is going to be used uninitialized or not. registerSize = 0; shadowPtr = NULL; switch( regAddr ) { case kMainCtrlReg: shadowPtr = (UInt8*)shadowRegs.sMCR; registerSize = kMCRwidth; break; case kDynamicRangeCtrlReg: shadowPtr = (UInt8*)shadowRegs.sDRC; registerSize = kDRCwidth; break; case kVolumeCtrlReg: shadowPtr = (UInt8*)shadowRegs.sVOL; registerSize = kVOLwidth; break; case kTrebleCtrlReg: shadowPtr = (UInt8*)shadowRegs.sTRE; registerSize = kTREwidth; break; case kBassCtrlReg: shadowPtr = (UInt8*)shadowRegs.sBAS; registerSize = kBASwidth; break; case kMixer1CtrlReg: shadowPtr = (UInt8*)shadowRegs.sMX1; registerSize = kMIXwidth; break; case kMixer2CtrlReg: shadowPtr = (UInt8*)shadowRegs.sMX2; registerSize = kMIXwidth; break; case kLeftBiquad0CtrlReg: shadowPtr = (UInt8*)shadowRegs.sLB0; registerSize = kBIQwidth; break; case kLeftBiquad1CtrlReg: shadowPtr = (UInt8*)shadowRegs.sLB1; registerSize = kBIQwidth; break; case kLeftBiquad2CtrlReg: shadowPtr = (UInt8*)shadowRegs.sLB2; registerSize = kBIQwidth; break; case kLeftBiquad3CtrlReg: shadowPtr = (UInt8*)shadowRegs.sLB3; registerSize = kBIQwidth; break; case kLeftBiquad4CtrlReg: shadowPtr = (UInt8*)shadowRegs.sLB4; registerSize = kBIQwidth; break; case kLeftBiquad5CtrlReg: shadowPtr = (UInt8*)shadowRegs.sLB5; registerSize = kBIQwidth; break; case kRightBiquad0CtrlReg: shadowPtr = (UInt8*)shadowRegs.sRB0; registerSize = kBIQwidth; break; case kRightBiquad1CtrlReg: shadowPtr = (UInt8*)shadowRegs.sRB1; registerSize = kBIQwidth; break; case kRightBiquad2CtrlReg: shadowPtr = (UInt8*)shadowRegs.sRB2; registerSize = kBIQwidth; break; case kRightBiquad3CtrlReg: shadowPtr = (UInt8*)shadowRegs.sRB3; registerSize = kBIQwidth; break; case kRightBiquad4CtrlReg: shadowPtr = (UInt8*)shadowRegs.sRB4; registerSize = kBIQwidth; break; case kRightBiquad5CtrlReg: shadowPtr = (UInt8*)shadowRegs.sRB5; registerSize = kBIQwidth; break; default: err = kIOReturnDeviceError; break; } if( kIOReturnSuccess == err ) { for( regByteIndex = 0; regByteIndex < registerSize; regByteIndex++ ) { registerData[regByteIndex] = shadowPtr[regByteIndex]; } } if( kIOReturnSuccess != err ) { debugIOLog ( 3, "± AppleTexasAudio::TAS3001C_ReadRegister ( 0x%2X, 0x%8X ) returns 0x%lX", regAddr, (unsigned int)registerData, err ); } return err; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ IOReturn AppleTexasAudio::TAS3001C_Reset(UInt32 resetFlag){ IOReturn err = kIOReturnSuccess; switch ( resetFlag ) { case kFORCE_RESET_SETUP_TIME: debugIOLog (3, "+ TAS3001C_Reset( %lu = kFORCE_RESET_SETUP_TIME )", resetFlag ); break; case kNO_FORCE_RESET_SETUP_TIME: debugIOLog (3, "+ TAS3001C_Reset( %lu = kNO_FORCE_RESET_SETUP_TIME )", resetFlag ); break; default: debugIOLog (3, "+ TAS3001C_Reset( %lu = UNKNOWN )", resetFlag ); break; } ClockSource clockSource; UInt32 sclkDivisor; UInt32 mclkDivisor; UInt32 dataFormat; // [3060321] rbm 2 Oct 2002 UInt32 myFrameRate; dataFormat = ( ( 2 << kNumChannelsInShift ) | kDataIn16 | ( 2 << kNumChannelsOutShift ) | kDataOut16 ); // [3060321] rbm 2 Oct 2002 myFrameRate = frameRate (0); if ( NULL != audioI2SControl ) { FailIf ( FALSE == audioI2SControl->setSampleParameters (myFrameRate, 256, &clockSource, &mclkDivisor, &sclkDivisor, kSndIOFormatI2S64x ), Exit); // [3060321] The data word format register and serial format register require that the I2S clocks be stopped and // restarted before the register value is applied to operation of the I2S IOM. We now pass the data // word format to setSerialFormatRegister as that method stops the clocks when applying the value // to the serial format register. That method now also sets the data word format register while // the clocks are stopped. rbm 2 Oct 2002 audioI2SControl->setSerialFormatRegister ( clockSource, mclkDivisor, sclkDivisor, kSndIOFormatI2S64x, dataFormat ); } if( hwResetActiveState == GpioRead( hwResetGpio ) || !GpioGetDDR( hwResetGpio ) || resetFlag ) // if reset never was performed { GpioWrite( hwResetGpio, 0 == hwResetActiveState ? 1 : 0 ); // negate RESET // I think we really only have to reset it for a millisecond or two IOSleep (200); } else { IOSleep (3); } GpioWrite( hwResetGpio, hwResetActiveState ); // Assert RESET IOSleep (3); GpioWrite( hwResetGpio, 0 == hwResetActiveState ? 1 : 0 ); // negate RESET IOSleep (3); Exit: switch ( resetFlag ) { case kFORCE_RESET_SETUP_TIME: debugIOLog (3, "- TAS3001C_Reset( %lu = kFORCE_RESET_SETUP_TIME ) returns 0x%lX", resetFlag, err ); break; case kNO_FORCE_RESET_SETUP_TIME: debugIOLog (3, "- TAS3001C_Reset( %lu = kNO_FORCE_RESET_SETUP_TIME ) returns 0x%lX", resetFlag, err ); break; default: debugIOLog (3, "- TAS3001C_Reset( %lu = UNKNOWN ) returns 0x%lX", resetFlag, err ); break; } return err; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // [3731023] All write transactions will target the register cache so that // read / modify / write operations may be supported. If an AudioI2SControl // exists the it is assumed that the I2S clocks, which are required in order // to access the TAS3001C, are present and all write operations will also // be applied to the TAS3001C. IOReturn AppleTexasAudio::TAS3001C_WriteRegister ( UInt8 regAddr, UInt8* registerData ){ // Build to always write to hardware. This will expose hardware accesses that occur when the device is // not in the kIOAudioDeviceActive state so that better power management mechanisms can be developed. UInt32 registerSize = 0; UInt8 *shadowPtr = NULL; bool success; IOReturn err = kIOReturnSuccess; debugIOLog ( 4, "+ AppleTexasAudio::TAS3001C_WriteRegister ( 0x%2X, %p )", regAddr, registerData ); switch ( regAddr ) { case kMainCtrlReg: shadowPtr = (UInt8*)shadowRegs.sMCR; registerSize = kMCRwidth; break; case kDynamicRangeCtrlReg: shadowPtr = (UInt8*)shadowRegs.sDRC; registerSize = kDRCwidth; break; case kVolumeCtrlReg: shadowPtr = (UInt8*)shadowRegs.sVOL; registerSize = kVOLwidth; break; case kTrebleCtrlReg: shadowPtr = (UInt8*)shadowRegs.sTRE; registerSize = kTREwidth; break; case kBassCtrlReg: shadowPtr = (UInt8*)shadowRegs.sBAS; registerSize = kBASwidth; break; case kMixer1CtrlReg: shadowPtr = (UInt8*)shadowRegs.sMX1; registerSize = kMIXwidth; break; case kMixer2CtrlReg: shadowPtr = (UInt8*)shadowRegs.sMX2; registerSize = kMIXwidth; break; case kLeftBiquad0CtrlReg: shadowPtr = (UInt8*)shadowRegs.sLB0; registerSize = kBIQwidth; break; case kLeftBiquad1CtrlReg: shadowPtr = (UInt8*)shadowRegs.sLB1; registerSize = kBIQwidth; break; case kLeftBiquad2CtrlReg: shadowPtr = (UInt8*)shadowRegs.sLB2; registerSize = kBIQwidth; break; case kLeftBiquad3CtrlReg: shadowPtr = (UInt8*)shadowRegs.sLB3; registerSize = kBIQwidth; break; case kLeftBiquad4CtrlReg: shadowPtr = (UInt8*)shadowRegs.sLB4; registerSize = kBIQwidth; break; case kLeftBiquad5CtrlReg: shadowPtr = (UInt8*)shadowRegs.sLB5; registerSize = kBIQwidth; break; case kRightBiquad0CtrlReg: shadowPtr = (UInt8*)shadowRegs.sRB0; registerSize = kBIQwidth; break; case kRightBiquad1CtrlReg: shadowPtr = (UInt8*)shadowRegs.sRB1; registerSize = kBIQwidth; break; case kRightBiquad2CtrlReg: shadowPtr = (UInt8*)shadowRegs.sRB2; registerSize = kBIQwidth; break; case kRightBiquad3CtrlReg: shadowPtr = (UInt8*)shadowRegs.sRB3; registerSize = kBIQwidth; break; case kRightBiquad4CtrlReg: shadowPtr = (UInt8*)shadowRegs.sRB4; registerSize = kBIQwidth; break; case kRightBiquad5CtrlReg: shadowPtr = (UInt8*)shadowRegs.sRB5; registerSize = kBIQwidth; break; default: err = kIOReturnBadArgument; break; } if ( kIOReturnSuccess == err ) { for ( UInt32 regByteIndex = 0; regByteIndex < registerSize; regByteIndex++ ) { shadowPtr[regByteIndex] = registerData[regByteIndex]; } if ( NULL != audioI2SControl ) { if ( openI2C () ) { debugIOLog ( 5, " interface->writeI2CBus( addr 0x%X, subaddr 0x%X, %p, %d )", DEQAddress, regAddr, registerData, registerSize ); success = interface->writeI2CBus ( DEQAddress, regAddr, registerData, registerSize ); closeI2C(); if ( !success ) { err = TAS3001C_Initialize ( kNO_FORCE_RESET_SETUP_TIME ); } } else { debugIOLog ( 5, " couldn't open the I2C bus!" ); err = kIOReturnNoDevice; } } } debugIOLog ( 4, "- AppleTexasAudio::TAS3001C_WriteRegister ( 0x%2X, %p ) returns 0x%lX", regAddr, registerData, err ); return err; } #pragma mark +UTILITY FUNCTIONS //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bool AppleTexasAudio::DallasDriverPublished (AppleTexasAudio * appleTexasAudio, void * refCon, IOService * newService) { bool resultCode; resultCode = FALSE; FailIf (NULL == appleTexasAudio, Exit); FailIf (NULL == newService, Exit); appleTexasAudio->dallasDriver = (AppleDallasDriver *)newService; appleTexasAudio->attach (appleTexasAudio->dallasDriver); appleTexasAudio->dallasDriver->open (appleTexasAudio); if (NULL != appleTexasAudio->dallasDriverNotifier) appleTexasAudio->dallasDriverNotifier->remove (); resultCode = TRUE; Exit: return resultCode; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Set state equal to if external speakers are around. If active, restore // preferred mute state. If inactive, mute in hardware. // Has to be called on the IOAudioFamily workloop (because we do the config change of the controls)!!! // // IMPORTANT!!! Interrupts must be disabled before calling this function if Dallas speakers has been plugged in // IMPORTANT!!! to make sure that when it calls dallasDriver->getSpeakerID you don't get stuck in an infinite // IMPORTANT!!! loop of interrupts. // void AppleTexasAudio::DeviceInterruptService (void) { EQPrefsElementPtr eqPrefs; IOReturn err; UInt32 deviceID; Boolean result; UInt8 bEEPROM[32]; OSNumber * headphoneState; // For [2926907] debugIOLog (3, "+ AppleTexasAudio::DeviceInterruptService()"); err = kIOReturnSuccess; deviceID = GetDeviceMatch (); // for 2749470 if (NULL != driverDMAEngine) { if (kInternalSpeakerActive == deviceID) { // when it's just the internal speaker, figure out if we have to mute the right channel switch (layoutID) { case layoutTangent: case layoutP57: driverDMAEngine->setRightChanMixed (FALSE); useMasterVolumeControl = TRUE; break; case layoutP57b: case layoutTessera: case layoutP79: driverDMAEngine->setRightChanMixed (TRUE); useMasterVolumeControl = TRUE; break; default: driverDMAEngine->setRightChanMixed (FALSE); useMasterVolumeControl = FALSE; } } else { // If it's external speakers or headphones, don't mute the right channel and create it if we've already deleted it driverDMAEngine->setRightChanMixed (FALSE); useMasterVolumeControl = FALSE; } } debugIOLog (3, " dallasDriver %x, dallasSpeakersConnected %x, dallasSpeakersProbed %x", (unsigned int)dallasDriver, (unsigned int)dallasSpeakersConnected, (unsigned int)dallasSpeakersProbed ); if (NULL != dallasDriver && TRUE == dallasSpeakersConnected && FALSE == dallasSpeakersProbed) { // get the layoutID from the IORegistry for the machine we're running on (which is the machine's device-id) // deviceMatch is set from sound objects, but we're hard coding it using a table at the moment speakerID = 0; familyID = 0; result = FALSE; // FALSE == failure from dallasDriver->getSpeakerID bEEPROM[0] = 0; bEEPROM[1] = 0; result = dallasDriver->getSpeakerID (bEEPROM); dallasSpeakersProbed = TRUE; debugIOLog (3, " DallasDriver result = %d speakerID = %ld", result, speakerID); speakerConnectFailed = TRUE; if (TRUE == result) { if ( kDallasDeviceFamilySpeaker == bEEPROM[0] ) { // If the Dallas speakers are misinserted, set registry up for our MacBuddy buddies no matter what the output device is familyID = bEEPROM[0]; speakerID = bEEPROM[1]; speakerConnectFailed = FALSE; } } setProperty (kSpeakerConnectError, speakerConnectFailed); if (kExternalSpeakersActive == deviceID && TRUE == speakerConnectFailed && TRUE == IsSpeakerConnected()) { // [3103075] rbm 12/2/2002 // Only put up our alert if the Dallas speakers are the output device DisplaySpeakersNotFullyConnected (this, NULL); } } if ( !dallasSpeakersConnected ) { // begin [3017286] rbm 08/07/2002 familyID = 0; speakerID = 0; } // [3017286] end rbm 08/07/2002 if ( !disableLoadingEQFromFile ) { err = GetCustomEQCoefficients (layoutID, deviceID, speakerID, &eqPrefs); } else { err = kIOReturnSuccess; } if (kIOReturnSuccess == err && NULL != eqPrefs) { DRCInfo localDRC; // Set the dynamic range compressor coefficients. localDRC.compressionRatioNumerator = eqPrefs->drcCompressionRatioNumerator; localDRC.compressionRatioDenominator = eqPrefs->drcCompressionRatioDenominator; localDRC.threshold = eqPrefs->drcThreshold; localDRC.maximumVolume = eqPrefs->drcMaximumVolume; localDRC.enable = (Boolean)((UInt32)(eqPrefs->drcEnable)); err = SndHWSetDRC ((DRCInfoPtr)&localDRC); FailIf ( kIOReturnSuccess != err, Exit ); err = SndHWSetOutputBiquadGroup (eqPrefs->filterCount, eqPrefs->filter[0].coefficient); FailIf ( kIOReturnSuccess != err, Exit ); } else { err = SetUnityGainAllPass (); FailIf ( kIOReturnSuccess != err, Exit ); } // Set the level controls to their (possibly) new min and max values minVolume = kMinimumVolume; maxVolume = kMaximumVolume + drc.maximumVolume; debugIOLog (3, " minVolume = %ld, maxVolume = %ld", minVolume, maxVolume); AdjustControls (); // For [2926907] if (NULL != headphoneConnection) { if (TRUE == IsHeadphoneConnected ()) { headphoneState = OSNumber::withNumber (1, 32); } else { headphoneState = OSNumber::withNumber ((long long unsigned int)0, 32); } (void)headphoneConnection->hardwareValueChanged (headphoneState); headphoneState->release (); } // end [2926907] Exit: debugIOLog (3, "- AppleTexasAudio::DeviceInterruptService()"); return; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // This routine will set the global Boolean dontReleaseHPMute to 'true' if // the layout references a system where the hardware implements a headphone // mute circuit that deviates from the standard TAS3001 implementation and // requires a behavior that the Headphone Mute remain asserted when the // headphone is muted. This is a deviation from the standard behavior where // the headphone mute is released after 250 milliseconds. Just add new // 'case' for each system to be excluded from the default behavior above // the 'case layoutP29' with no 'break' and let the code fall through to // the 'case layoutP29' statement. Standard hardware implementations that // adhere to the default behavior do not require any code change. [2660341] void AppleTexasAudio::ExcludeHPMuteRelease (UInt32 layout) { switch (layout) { case layoutP92: /* Fall through to set dontReleaseHPMute = true */ case layoutP54: /* Fall through to set dontReleaseHPMute = true */ case layoutP29: dontReleaseHPMute = true; break; default: dontReleaseHPMute = false; break; } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ UInt32 AppleTexasAudio::GetDeviceMatch (void) { UInt32 theDeviceMatch; if (TRUE == IsHeadphoneConnected ()) theDeviceMatch = kHeadphonesActive; // headphones are connected else if (TRUE == dallasSpeakersConnected) theDeviceMatch = kExternalSpeakersActive; // headphones aren't connected and external Dallas speakers are connected else theDeviceMatch = kInternalSpeakerActive; // headphones aren't connected and external Dallas speakers aren't connected return theDeviceMatch; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ IORegistryEntry *AppleTexasAudio::FindEntryByNameAndProperty (const IORegistryEntry * start, const char * name, const char * key, UInt32 value) { OSIterator *iterator; IORegistryEntry *theEntry; IORegistryEntry *tmpReg; OSNumber *tmpNumber; theEntry = NULL; iterator = NULL; FailIf (NULL == start, Exit); iterator = start->getChildIterator (gIOServicePlane); FailIf (NULL == iterator, Exit); while (NULL == theEntry && (tmpReg = OSDynamicCast (IORegistryEntry, iterator->getNextObject ())) != NULL) { if (strcmp (tmpReg->getName (), name) == 0) { tmpNumber = OSDynamicCast (OSNumber, tmpReg->getProperty (key)); if (NULL != tmpNumber && tmpNumber->unsigned32BitValue () == value) { theEntry = tmpReg; theEntry->retain(); } } } Exit: if (NULL != iterator) { iterator->release (); } return theEntry; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ IORegistryEntry *AppleTexasAudio::FindEntryByProperty (const IORegistryEntry * start, const char * key, const char * value) { OSIterator *iterator; IORegistryEntry *theEntry; IORegistryEntry *tmpReg; OSData *tmpData; theEntry = NULL; iterator = start->getChildIterator (gIODTPlane); FailIf (NULL == iterator, Exit); while (NULL == theEntry && (tmpReg = OSDynamicCast (IORegistryEntry, iterator->getNextObject ())) != NULL) { tmpData = OSDynamicCast (OSData, tmpReg->getProperty (key)); if (NULL != tmpData && tmpData->isEqualTo (value, strlen (value))) { theEntry = tmpReg; } } Exit: if (NULL != iterator) { iterator->release (); } return theEntry; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ IOReturn AppleTexasAudio::GetCustomEQCoefficients (UInt32 layoutID, UInt32 deviceID, UInt32 speakerID, EQPrefsElementPtr *filterSettings) { IOReturn err; Boolean found; UInt32 index; EQPrefsElementPtr eqElementPtr; debugIOLog (3, "+ AppleTexasAudio::GetCustomEQCoefficients (%lX, %lX, %lX, %p)", layoutID, deviceID, speakerID, filterSettings); err = kIOReturnError; FailIf (0 == layoutID, Exit); FailIf (NULL == filterSettings, Exit); FailIf (NULL == gEQPrefs, Exit); found = FALSE; eqElementPtr = NULL; *filterSettings = NULL; for (index = 0; index < gEQPrefs->eqCount && !found; index++) { eqElementPtr = &(gEQPrefs->eq[index]); if ((eqElementPtr->layoutID == layoutID) && (eqElementPtr->deviceID == deviceID) && (eqElementPtr->speakerID == speakerID)) { found = TRUE; } } if (TRUE == found) { *filterSettings = eqElementPtr; err = kIOReturnSuccess; } Exit: debugIOLog (3, "- AppleTexasAudio::GetCustomEQCoefficients (%lX, %lX, %lX, %p) returns %lX", layoutID, deviceID, speakerID, filterSettings, err); return err; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ UInt32 AppleTexasAudio::GetDeviceID (void) { IORegistryEntry *sound; OSData *tmpData; UInt32 *deviceID; UInt32 theDeviceID; theDeviceID = 0; sound = ourProvider->childFromPath (kSoundEntry, gIODTPlane); FailIf (!sound, Exit); tmpData = OSDynamicCast (OSData, sound->getProperty (kDeviceID)); FailIf (!tmpData, Exit); deviceID = (UInt32*)tmpData->getBytesNoCopy (); if (NULL != deviceID) { theDeviceID = *deviceID; } Exit: debugIOLog ( 3, "± AppleTexasAudio::GetDeviceID() returns %d", theDeviceID ); return theDeviceID; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Boolean AppleTexasAudio::HasInput (void) { IORegistryEntry *sound; OSData *tmpData; UInt32 *numInputs; Boolean hasInput; hasInput = false; sound = ourProvider->childFromPath (kSoundEntry, gIODTPlane); FailIf (!sound, Exit); tmpData = OSDynamicCast (OSData, sound->getProperty (kNumInputs)); FailIf (!tmpData, Exit); numInputs = (UInt32*)tmpData->getBytesNoCopy (); if (*numInputs > 1) { hasInput = true; } Exit: debugIOLog ( 3, "± AppleTexasAudio::HasInput() returns %d", hasInput ); return hasInput; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ IOReturn AppleTexasAudio::SetActiveOutput (UInt32 output, Boolean touchBiquad) { IOReturn err; debugIOLog (3, "+ AppleTexasAudio::SndHWSetActiveOutput (output %ld, touchBiquad %d)", output, touchBiquad); err = kIOReturnSuccess; if (touchBiquad) SetUnityGainAllPass (); switch (output) { case kSndHWOutputNone: SetAmplifierMuteState (kHEADPHONE_AMP, ASSERT_GPIO (hdpnActiveState)); // mute SetAmplifierMuteState (kSPEAKER_AMP, ASSERT_GPIO (ampActiveState)); // mute break; case kSndHWOutput1: SetAmplifierMuteState (kHEADPHONE_AMP, NEGATE_GPIO (hdpnActiveState)); // unmute SetAmplifierMuteState (kSPEAKER_AMP, ASSERT_GPIO (ampActiveState)); // mute IOSleep (kAmpRecoveryMuteDuration); // Wait for the amp to become active break; case kSndHWOutput2: // fall through to kSndHWOutput4 case kSndHWOutput3: // fall through to kSndHWOutput4 case kSndHWOutput4: // The TA1101B amplifier can 'crowbar' when inserting the speaker jack. // Muting the amplifier will release it from the crowbar state. SetAmplifierMuteState (kHEADPHONE_AMP, ASSERT_GPIO (hdpnActiveState)); // mute SetAmplifierMuteState (kSPEAKER_AMP, NEGATE_GPIO (ampActiveState)); // unmute IOSleep (kAmpRecoveryMuteDuration); // Wait for the amp to become active if (!dontReleaseHPMute) // [2660341] unmute if std hw SetAmplifierMuteState (kHEADPHONE_AMP, NEGATE_GPIO (hdpnActiveState)); // unmute break; } debugIOLog (3, "- AppleTexasAudio::SndHWSetActiveOutput (output %ld, touchBiquad %d) returns 0x%lX", output, touchBiquad, err ); return err; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ void AppleTexasAudio::SetBiquadInfoToUnityAllPass (void) { UInt32 index; debugIOLog (3, "+ AppleTexasAudio::SetBiquadInfoToUnityAllPass ()"); for (index = 0; index < kNumberOfBiquadCoefficients; index++) { biquadGroupInfo[index++] = 1.0; // b0 biquadGroupInfo[index++] = 0.0; // b1 biquadGroupInfo[index++] = 0.0; // b2 biquadGroupInfo[index++] = 0.0; // a1 biquadGroupInfo[index] = 0.0; // a2 } debugIOLog (3, "- AppleTexasAudio::SetBiquadInfoToUnityAllPass ()"); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // This routine will only restore unity gain all pass coefficients to the // biquad registers. All other coefficients to be passed through exported // functions via the sound hardware plug-in manager libaray. IOReturn AppleTexasAudio::SetUnityGainAllPass (void) { UInt32 prevLoadMode; int biquadRefnum; DRCInfo localDRC; IOReturn result = kIOReturnError; debugIOLog ( 3, "+ AppleTexasAudio::SetUnityGainAllPass ()" ); // save previous load mode prevLoadMode = 0 == (shadowRegs.sMCR[0] & (kFastLoad << kFL)) ? kSetNormalLoadMode : kSetFastLoadMode; debugIOLog (3, " shadowRegs.sMCR[0] %2X, prevLoadMode %ld", shadowRegs.sMCR[0], prevLoadMode); // prepare for proper effect of fast load mode (i.e. unity all pass) if (kSetFastLoadMode == prevLoadMode) { result = InitEQSerialMode (kSetNormalLoadMode, kRestoreOnNormal); FailIf ( kIOReturnSuccess != result, Exit ); } // force unity all pass biquad coefficients result = InitEQSerialMode (kSetFastLoadMode, kDontRestoreOnNormal); FailIf ( kIOReturnSuccess != result, Exit ); // Set the biquad coefficients in the shadow registers to 'unity all pass' so that // any future attempt to set the biquads is applied to the hardware registers (i.e. // make sure that the shadow register accurately reflects the current state so that // a data compare in the future does not cause a write operation to be bypassed). for (biquadRefnum = 0; biquadRefnum < kNumberOfBiquadsPerChannel; biquadRefnum++) { result = TAS3001C_WriteRegister ( kLeftBiquad0CtrlReg + biquadRefnum, (UInt8*)kBiquad0db ); FailIf ( kIOReturnSuccess != result, Exit ); result = TAS3001C_WriteRegister ( kRightBiquad0CtrlReg + biquadRefnum, (UInt8*)kBiquad0db ); FailIf ( kIOReturnSuccess != result, Exit ); } SetBiquadInfoToUnityAllPass (); result = InitEQSerialMode (kSetNormalLoadMode, kRestoreOnNormal); // go to normal load mode and restore registers after default FailIf ( kIOReturnSuccess != result, Exit ); // Need to restore volume & mixer control registers after going to fast load mode localDRC.compressionRatioNumerator = kDrcRatioNumerator; localDRC.compressionRatioDenominator = kDrcRationDenominator; localDRC.threshold = kDrcUnityThresholdHW; localDRC.maximumVolume = kDefaultMaximumVolume; localDRC.enable = false; result = SndHWSetDRC (&localDRC); FailIf ( kIOReturnSuccess != result, Exit ); // restore previous load mode if (kSetFastLoadMode == prevLoadMode) { result = InitEQSerialMode (kSetFastLoadMode, kDontRestoreOnNormal); FailIf ( kIOReturnSuccess != result, Exit ); } debugIOLog (3, " shadowRegs.sMCR[0] %8X, prevLoadMode %ld", shadowRegs.sMCR[0], prevLoadMode); Exit: debugIOLog ( 3, "- AppleTexasAudio::SetUnityGainAllPass () returns 0x%lX", result ); return result; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // When disabling Dynamic Range Compression, don't check the other elements // of the DRCInfo structure. When enabling DRC, clip the compression threshold // to a valid range for the target hardware & validate that the compression // ratio is supported by the target hardware. The maximumVolume argument // will dynamically apply the zero index reference point into the volume // gain translation table and will force an update of the volume registers. IOReturn AppleTexasAudio::SndHWSetDRC( DRCInfoPtr theDRCSettings ) { IOReturn err; UInt8 regData[kDRCwidth]; Boolean enableUpdated; debugIOLog (3, "+ AppleTexasAudio::SndHWSetDRC( theDRCSettings %p )", theDRCSettings ); err = kIOReturnSuccess; enableUpdated = false; FailWithAction( NULL == theDRCSettings, err = kIOReturnBadArgument, Exit ); debugIOLog (3, " compressionRatioNumerator %ld, compressionRatioDenominator %ld", theDRCSettings->compressionRatioNumerator, theDRCSettings->compressionRatioDenominator ); debugIOLog (3, " threshold %ld, maximumVolume %ld", theDRCSettings->threshold, theDRCSettings->maximumVolume ); debugIOLog (3, " enable %d", theDRCSettings->enable ); if( TRUE == theDRCSettings->enable ) { debugIOLog (3, " enable DRC" ); // Turn off the dynamic range compression and update the globals DRC enable state. // The compression threshold is represented by a 4.4 number with a value of 15.0 // representing 0.0 dB and decrementing 0.0625 dB per count as the threshold is // moved down toward -36.0625 dB. Each count represents 0.375 dB. regData[0] = ( kDrcEnable << kEN ) | ( kCompression3to1 << kCR ); // divide by 1000 to remove the 1000's that the constants were multiplied by regData[1] = (UInt8)( kDrcUnityThresholdHW - ((SInt32)( -theDRCSettings->threshold / kDrcThresholdStepSize ) / 1000) ); if ( kIOAudioDeviceActive != previousPowerState ) { // [3593390] Update the cache and avoid hw access if not awake debugIOLog ( 3, " targeting shadow register due to power state: %d", previousPowerState ); err = TAS3001C_WriteRegister( kDynamicRangeCtrlReg, regData ); } else { debugIOLog ( 3, " targeting hardware register due to power state: %d", previousPowerState ); err = TAS3001C_WriteRegister( kDynamicRangeCtrlReg, regData ); } FailIf( kIOReturnSuccess != err, Exit ); if( drc.enable != theDRCSettings->enable ) { enableUpdated = true; } drc.enable = theDRCSettings->enable; debugIOLog (3, " drc.compressionRatioNumerator %ld", drc.compressionRatioNumerator ); debugIOLog (3, " drc.compressionRatioDenominator %ld", drc.compressionRatioDenominator ); debugIOLog (3, " drc.threshold %ld", drc.threshold ); debugIOLog (3, " drc.enable %d", drc.enable ); // The current volume setting needs to be scaled against the new range of volume // control and applied to the hardware. if( drc.maximumVolume != theDRCSettings->maximumVolume || enableUpdated ) { drc.maximumVolume = theDRCSettings->maximumVolume; } } else { debugIOLog (3, " disable DRC" ); // Turn off the dynamic range compression and update the globals DRC enable state err = TAS3001C_ReadRegister( kDynamicRangeCtrlReg, regData ); FailIf( kIOReturnSuccess != err, Exit ); regData[0] = ( kDrcDisable << kEN ) | ( kCompression3to1 << kCR ); // [2580249,2667007] Dynamic range control = disabled at 3:1 compression regData[1] = kDefaultCompThld; // [2580249] Default threshold is 0.0 dB if ( kIOAudioDeviceActive != previousPowerState ) { // [3593390] Update the cache and avoid hw access if not awake debugIOLog ( 3, " targeting shadow register due to power state: %d", previousPowerState ); err = TAS3001C_WriteRegister( kDynamicRangeCtrlReg, regData ); } else { debugIOLog ( 3, " targeting hardware register due to power state: %d", previousPowerState ); err = TAS3001C_WriteRegister( kDynamicRangeCtrlReg, regData ); } FailIf( kIOReturnSuccess != err, Exit ); drc.enable = false; } drc.compressionRatioNumerator = theDRCSettings->compressionRatioNumerator; drc.compressionRatioDenominator = theDRCSettings->compressionRatioDenominator; drc.threshold = theDRCSettings->threshold; drc.maximumVolume = theDRCSettings->maximumVolume; Exit: debugIOLog (3, "- AppleTexasAudio::SndHWSetDRC: err = %d", err ); return err; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // NOTE: This function does not utilize fast mode loading as to do so would // revert all biquad coefficients not addressed by this execution instance to // unity all pass. Expect DSP processing delay if this function is used. It // is recommended that SndHWSetOutputBiquadGroup be used instead. IOReturn AppleTexasAudio::SndHWSetOutputBiquad( UInt32 streamID, UInt32 biquadRefNum, FourDotTwenty *biquadCoefficients ) { IOReturn err; UInt32 coefficientIndex; UInt32 tumblerBiquadIndex; UInt32 biquadGroupIndex; UInt8 tumblerBiquad[kTumblerCoefficientsPerBiquad * kTumblerNumBiquads]; debugIOLog ( 3, "+ AppleTexasAudio::SndHWSetOutputBiquad( '%4s', %0.2d )", &streamID, biquadRefNum ); err = kIOReturnSuccess; FailWithAction( kTumblerMaxBiquadRefNum < biquadRefNum || NULL == biquadCoefficients, err = kIOReturnBadArgument, Exit ); FailWithAction( kStreamStereo != streamID && kStreamFrontLeft != streamID && kStreamFrontRight != streamID, err = kIOReturnBadArgument, Exit ); tumblerBiquadIndex = 0; biquadGroupIndex = biquadRefNum * kTumblerCoefficientsPerBiquad; if( kStreamFrontRight == streamID ) { biquadGroupIndex += kNumberOfBiquadCoefficientsPerChannel; } for( coefficientIndex = 0; coefficientIndex < kTumblerCoefficientsPerBiquad; coefficientIndex++ ) { tumblerBiquad[tumblerBiquadIndex++] = biquadCoefficients[coefficientIndex].integerAndFraction1; tumblerBiquad[tumblerBiquadIndex++] = biquadCoefficients[coefficientIndex].fraction2; tumblerBiquad[tumblerBiquadIndex++] = biquadCoefficients[coefficientIndex].fraction3; } err = SetOutputBiquadCoefficients( streamID, biquadRefNum, tumblerBiquad ); Exit: debugIOLog ( 3, "- AppleTexasAudio::SndHWSetOutputBiquad( '%4s', %0.2d ) returns 0x%lX", &streamID, biquadRefNum, err ); return err; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ IOReturn AppleTexasAudio::SndHWSetOutputBiquadGroup( UInt32 biquadFilterCount, FourDotTwenty *biquadCoefficients ) { UInt32 index; IOReturn err; debugIOLog ( 3, "+ AppleTexasAudio::SndHWSetOutputBiquadGroup( %d, %2.6f )", biquadFilterCount, biquadCoefficients ); FailWithAction( 0 == biquadFilterCount || NULL == biquadCoefficients, err = kIOReturnBadArgument, Exit ); err = kIOReturnSuccess; InitEQSerialMode( kSetFastLoadMode, kDontRestoreOnNormal ); index = 0; do { if( index >= ( biquadFilterCount / 2 ) ) { err = SndHWSetOutputBiquad( kStreamFrontRight, index - ( biquadFilterCount / 2 ), biquadCoefficients ); } else { err = SndHWSetOutputBiquad( kStreamFrontLeft, index, biquadCoefficients ); } index++; biquadCoefficients += kNumberOfCoefficientsPerBiquad; } while ( ( index < biquadFilterCount ) && ( kIOReturnSuccess == err ) ); InitEQSerialMode( kSetNormalLoadMode, kRestoreOnNormal ); Exit: debugIOLog ( 3, "- AppleTexasAudio::SndHWSetOutputBiquadGroup( %d, %2.6f ) returns 0x%lX", biquadFilterCount, biquadCoefficients, err ); return err; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ IOReturn AppleTexasAudio::SetOutputBiquadCoefficients( UInt32 streamID, UInt32 biquadRefNum, UInt8 *biquadCoefficients ) { IOReturn err; debugIOLog (3, "+ AppleTexasAudio::SetOutputBiquadCoefficients ( '%4s', %ld, %p )", (char*)&streamID, biquadRefNum, biquadCoefficients ); err = kIOReturnSuccess; FailWithAction ( kTumblerMaxBiquadRefNum < biquadRefNum || NULL == biquadCoefficients, err = kIOReturnBadArgument, Exit ); FailWithAction ( kStreamStereo != streamID && kStreamFrontLeft != streamID && kStreamFrontRight != streamID, err = kIOReturnBadArgument, Exit ); switch ( biquadRefNum ) { case kBiquadRefNum_0: switch( streamID ) { case kStreamFrontLeft: err = TAS3001C_WriteRegister( kLeftBiquad0CtrlReg, biquadCoefficients ); break; case kStreamFrontRight: err = TAS3001C_WriteRegister( kRightBiquad0CtrlReg, biquadCoefficients ); break; case kStreamStereo: err = TAS3001C_WriteRegister( kLeftBiquad0CtrlReg, biquadCoefficients ); err = TAS3001C_WriteRegister( kRightBiquad0CtrlReg, biquadCoefficients ); break; } break; case kBiquadRefNum_1: switch( streamID ) { case kStreamFrontLeft: err = TAS3001C_WriteRegister( kLeftBiquad1CtrlReg, biquadCoefficients ); break; case kStreamFrontRight: err = TAS3001C_WriteRegister( kRightBiquad1CtrlReg, biquadCoefficients ); break; case kStreamStereo: err = TAS3001C_WriteRegister( kLeftBiquad1CtrlReg, biquadCoefficients ); err = TAS3001C_WriteRegister( kRightBiquad1CtrlReg, biquadCoefficients ); break; } break; case kBiquadRefNum_2: switch( streamID ) { case kStreamFrontLeft: err = TAS3001C_WriteRegister( kLeftBiquad2CtrlReg, biquadCoefficients ); break; case kStreamFrontRight: err = TAS3001C_WriteRegister( kRightBiquad2CtrlReg, biquadCoefficients ); break; case kStreamStereo: err = TAS3001C_WriteRegister( kLeftBiquad2CtrlReg, biquadCoefficients ); err = TAS3001C_WriteRegister( kRightBiquad2CtrlReg, biquadCoefficients ); break; } break; case kBiquadRefNum_3: switch( streamID ) { case kStreamFrontLeft: err = TAS3001C_WriteRegister( kLeftBiquad3CtrlReg, biquadCoefficients ); break; case kStreamFrontRight: err = TAS3001C_WriteRegister( kRightBiquad3CtrlReg, biquadCoefficients ); break; case kStreamStereo: err = TAS3001C_WriteRegister( kLeftBiquad3CtrlReg, biquadCoefficients ); err = TAS3001C_WriteRegister( kRightBiquad3CtrlReg, biquadCoefficients ); break; } break; case kBiquadRefNum_4: switch( streamID ) { case kStreamFrontLeft: err = TAS3001C_WriteRegister( kLeftBiquad4CtrlReg, biquadCoefficients ); break; case kStreamFrontRight: err = TAS3001C_WriteRegister( kRightBiquad4CtrlReg, biquadCoefficients ); break; case kStreamStereo: err = TAS3001C_WriteRegister( kLeftBiquad4CtrlReg, biquadCoefficients ); err = TAS3001C_WriteRegister( kRightBiquad4CtrlReg, biquadCoefficients ); break; } break; case kBiquadRefNum_5: switch( streamID ) { case kStreamFrontLeft: err = TAS3001C_WriteRegister( kLeftBiquad5CtrlReg, biquadCoefficients ); break; case kStreamFrontRight: err = TAS3001C_WriteRegister( kRightBiquad5CtrlReg, biquadCoefficients ); break; case kStreamStereo: err = TAS3001C_WriteRegister( kLeftBiquad5CtrlReg, biquadCoefficients ); err = TAS3001C_WriteRegister( kRightBiquad5CtrlReg, biquadCoefficients ); break; } break; } Exit: debugIOLog (3, "- AppleTexasAudio::SetOutputBiquadCoefficients ( '%4s', %ld, %p ) returns 0x%lX", (char*)&streamID, biquadRefNum, biquadCoefficients, err ); return err; } #pragma mark +I2C FUNCTIONS // Taken from PPCDACA.cpp // -------------------------------------------------------------------------- // Method: getI2CPort // // Purpose: // returns the i2c port to use for the audio chip. UInt32 AppleTexasAudio::getI2CPort() { UInt32 result = 0; debugIOLog ( 7, "+ AppleTexasAudio::getI2CPort()" ); if ( NULL != ourProvider ) { OSData *t; t = OSDynamicCast ( OSData, ourProvider->getProperty ( "AAPL,i2c-port-select" ) ); // we don't need a port select on Tangent, but look anyway if ( NULL != t ) { result = *((UInt32*)t->getBytesNoCopy()); } } debugIOLog ( 7, "- AppleTexasAudio::getI2CPort() returns %d", result ); return result; } // -------------------------------------------------------------------------- // Method: openI2C // // Purpose: // opens and sets up the i2c bus bool AppleTexasAudio::openI2C() { bool result = FALSE; debugIOLog ( 7, "+ AppleTexasAudio::openI2C()" ); // Open the interface and sets it in the wanted mode: FailIf ( NULL == interface, Exit ); FailIf ( !interface->openI2CBus ( getI2CPort() ), Exit); interface->setStandardSubMode (); if ( !interface->setPollingMode ( mPollingMode ) ) { mPollingMode = true; interface->setPollingMode ( mPollingMode ); } result = TRUE; Exit: debugIOLog ( 7, "- AppleTexasAudio::openI2C() returns %d" ); return result; } // -------------------------------------------------------------------------- // Method: closeI2C // // Purpose: // closes the i2c bus void AppleTexasAudio::closeI2C () { // Closes the bus so other can access to it: interface->closeI2CBus (); } // -------------------------------------------------------------------------- // Method: findAndAttachI2C // // Purpose: // Attaches to the i2c interface: bool AppleTexasAudio::findAndAttachI2C(IOService *provider) { const OSSymbol * i2cDriverName; IOService * i2cCandidate; bool result = FALSE; // Searches the i2c: i2cDriverName = OSSymbol::withCStringNoCopy("PPCI2CInterface.i2c-mac-io"); i2cCandidate = waitForService(resourceMatching(i2cDriverName)); // interface = OSDynamicCast(PPCI2CInterface, i2cCandidate->getProperty(i2cDriverName)); interface = (PPCI2CInterface*)i2cCandidate->getProperty(i2cDriverName); FailIf ( NULL == interface, Exit ); // Make sure that we hold the interface: interface->retain(); result = TRUE; Exit: debugIOLog ( 3, "± AppleTexasAudio::findAndAttachI2C ( %p ) returns %d", provider, result ); return result; } // -------------------------------------------------------------------------- // Method: detachFromI2C // // Purpose: // detaches from the I2C bool AppleTexasAudio::detachFromI2C(IOService* /*provider*/) { if (interface) { // delete interface; interface->release(); interface = 0; } return (true); } // -------------------------------------------------------------------------- // Method: frameRate // // Purpose: // returns the frame rate as in the registry, if it is // not found in the registry, it returns the default value. #define kCommonFrameRate 44100 UInt32 AppleTexasAudio::frameRate(UInt32 index) { UInt32 result = kCommonFrameRate; if ( ourProvider ) { OSData *t = OSDynamicCast ( OSData, ourProvider->getProperty ( "sample-rates" ) ); if ( NULL != t ) { UInt32 *fArray = (UInt32*)(t->getBytesNoCopy()); if ((fArray != NULL) && (index < fArray[0])){ // I could do >> 16, but in this way the code is portable and // however any decent compiler will recognize this as a shift // and do the right thing. result = fArray[index + 1] / (UInt32)65536; } } } debugIOLog ( 3, "± AppleTexasAudio::frameRate (%ld) returns 0x%lX", index, result ); return result; }