//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // [3933529] IOReturn AppleTopazAudio::performSetPowerState ( UInt32 currentPowerState, UInt32 pendingPowerState ) { IOReturn result = kIOReturnError; debugIOLog ( 6, "+ AppleTopazAudio::performSetPowerState ( %d, %d ) where pendingPowerState = %d", currentPowerState, pendingPowerState, pendingPowerState ); switch ( pendingPowerState ) { case kIOAudioDeviceSleep: if ( kIOAudioDeviceActive == mPluginCurrentPowerState ) // [3954187] { result = performDeviceSleep (); } else { CODEC_Reset (); result = kIOReturnSuccess; } break; case kIOAudioDeviceIdle: if ( kIOAudioDeviceActive == mPluginCurrentPowerState ) // [3954187] { result = performDeviceSleep (); } else { CODEC_Reset (); result = kIOReturnSuccess; } break; case kIOAudioDeviceActive: result = performDeviceWake (); break; } mPluginCurrentPowerState = pendingPowerState; debugIOLog ( 6, "- AppleTopazAudio::performSetPowerState ( %d, %d ) where pendingPowerState = %d, returns 0x%lX", currentPowerState, pendingPowerState, pendingPowerState, result ); return result; }
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Reset the device and flush the cached register set to the device. This // is necessary for portable CPUs where power will have been removed from // the device during sleep. After flushing the cached register values to // the device, place the device into the RUN state. IOReturn AppleTopazAudio::performDeviceWake () { IOReturn result = kIOReturnError; switch ( mCodecID ) { case kCodec_CS8406: debugIOLog ( 5, "+ AppleTopazAudio::performDeviceWake () using AppleTopazPluginCS8406" ); break; case kCodec_CS8416: debugIOLog ( 5, "+ AppleTopazAudio::performDeviceWake () using AppleTopazPluginCS8416" ); break; case kCodec_CS8420: debugIOLog ( 5, "+ AppleTopazAudio::performDeviceWake () using AppleTopazPluginCS8420" ); break; default: debugIOLog ( 5, "+ AppleTopazAudio::performDeviceWake () using AppleTopazPluginUNKNOWN" ); break; } CODEC_Reset (); if ( 0 != mTopazPlugin ) { result = mTopazPlugin->performDeviceWake (); } // [3333215] Codec operation may not be restored after wake from // sleep so start state machine to recover codec operation. // [3344893,3352595] Recovery is only implemented if running on the internal // clock source since failure is associated with the sample rate converter // and the sample rate converter is not in use when running on the external // clock source. // [4176829] The CS8416 does not have a sample rate converter, so we should // not employ the state machine for it. If we do, the transition to // kMachine2_setRxd_ILRCK, which calls useInternalCLK, and the transition to // kMachine2_setRxd_AES3, which calls useExternalCLK, will cause two pops // if things are un-muted. if ( ( kTRANSPORT_MASTER_CLOCK == mClockSource ) && ( false == mDisableStateMachine2 ) ) { mCurrentMachine2State = kMachine2_startState; } else { mCurrentMachine2State = kMachine2_idleState; } mOptimizePollForUserClient_counter = kINITIAL_VALUE_FOR_POLL_FOR_USER_CLIENT_COUNT; // [3686032] initialize so log will show register dump a while longer switch ( mCodecID ) { case kCodec_CS8406: debugIOLog ( 5, "- AppleTopazAudio::performDeviceWake () using AppleTopazPluginCS8406 returns %lX", result ); break; case kCodec_CS8416: debugIOLog ( 5, "- AppleTopazAudio::performDeviceWake () using AppleTopazPluginCS8416 returns %lX", result ); break; case kCodec_CS8420: debugIOLog ( 5, "- AppleTopazAudio::performDeviceWake () using AppleTopazPluginCS8420 returns %lX", result ); break; default: debugIOLog ( 5, "- AppleTopazAudio::performDeviceWake () using AppleTopazPluginUNKNOWN returns %lX", result ); break; } return result; }
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // generalErrorRecovery: // // Save away the current run state before stopping the S/PDIF codec. // Then reset the S/PDIF codec and restore all the register states. // Complete the recovery by restoring the original S/PDIF codec run state. // void AppleTopazAudio::generalRecovery ( void ) { UInt8 data; FailIf ( 0 == mTopazPlugin, Exit ); if ( !mGeneralRecoveryInProcess ) { mGeneralRecoveryInProcess = TRUE; data = mTopazPlugin->setStopMode (); CODEC_Reset(); mTopazPlugin->flushControlRegisters (); mTopazPlugin->setRunMode ( data ); if ( false == mDisableStateMachine2 ) { mCurrentMachine2State = kMachine2_startState; } else { mCurrentMachine2State = kMachine2_idleState; } mGeneralRecoveryInProcess = FALSE; } Exit: return; }
/******************************************************************************* * Function Name : CODEC_Config * Description : Configure the Codec in Headphone mode. * Input : - OutputDevice: OutputDeviceHEADPHONE or OutputDeviceSPEAKER * : - I2S_Standard: I2S communication standard could be I2S_Standard_Phillips * : I2S_Standard_MSB or I2S_Standard_LSB. * : - I2S_MCLKOutput: could be I2S_MCLKOutput_ * : - Volume: * Output : None * Return : 0-> correct communication, else wrong communication *******************************************************************************/ uint32_t CODEC_Config(uint16_t OutputDevice, uint16_t I2S_Standard, uint16_t I2S_MCLKOutput, uint8_t Volume) { uint32_t Standard = 0, counter = 0; /* Configure the IO Expander (Codec Reset pin) */ IOE_Config(); /* Configure the Codec related IOs */ CODEC_GPIO_Config(); /* Reset the Codec Registers */ CODEC_Reset(); /* Determine the I2S standard used */ switch (I2S_Standard) { case I2S_Standard_Phillips: Standard = 0x04; break; case I2S_Standard_MSB: Standard = 0x00; break; default : Standard = 0x08; break; } /* Initialization phase --------------------------------------*/ /* Keep Codec powered OFF */ counter += CODEC_WriteRegister(0x02, 0x01); switch (OutputDevice) { case OutputDevice_SPEAKER: counter += CODEC_WriteRegister(0x04, 0xFA); // SPK always ON & HP always OFF break; case OutputDevice_HEADPHONE: counter += CODEC_WriteRegister(0x04, 0xAF); // SPK always OFF & HP always ON break; case OutputDevice_BOTH: counter += CODEC_WriteRegister(0x04, 0xAA); // SPK always ON & HP always ON break; case OutputDevice_AUTO: counter += CODEC_WriteRegister(0x04, 0x05);// Detect the HP or the SPK automatically break; } /* Clock configuration: Auto detection */ counter += CODEC_WriteRegister(0x05, 0x81); //Auto speed detection /* Config the Slave Mode and the audio Standard */ counter += CODEC_WriteRegister(0x06, Standard); /* Set the Master volume */ CODEC_ControlVolume(Volume); /* Power on the Codec */ counter += CODEC_WriteRegister(0x02, 0x9E); /* Return the counter value */ return counter; }
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bool AppleTopazAudio::preDMAEngineInit (UInt32 autoClockSelectionIsBeingUsed) { UInt8 data; bool result = false; debugIOLog (3, "+ AppleTopazAudio::preDMAEngineInit ()" ); FailIf ( NULL == mPlatformInterface, Exit ); FailIf ( NULL == mAudioDeviceProvider, Exit ); // [3648867] // [3648867] Ask the Provider for the transport interface index. There // is a hard association or binding between the transport interface index // and the CS84xx device address as follows: // // AD2 AD1 AD0 I2C Address Transport Index // // 0 0 0 0x20 0 // 0 0 1 0x22 1 // 0 1 0 0x24 2 // 0 1 1 0x26 3 // 1 0 0 0x28 4 // 1 0 1 0x2A 5 // 1 1 0 0x2C 6 // 1 1 1 0x2E 7 // // Since the transport index shifted by one is equal to the offset from the // I2C base address, the target address can be easily calculated as follows: mOptimizePollForUserClient_counter = kINITIAL_VALUE_FOR_POLL_FOR_USER_CLIENT_COUNT; if ( autoClockSelectionIsBeingUsed ) { mDisableStateMachine2 = true; } else { mDisableStateMachine2 = false; } CODEC_Reset (); data = CODEC_ReadID(); FailIf ( 0 == data, Exit ); data &= kCS84XX_ID_MASK; data >>= baID; switch ( data ) { case cs8406_id: mCodecID = kCodec_CS8406; break; case cs8416_id: mCodecID = kCodec_CS8416; break; case cs8420_id: mCodecID = kCodec_CS8420; break; default: FailIf ( true, Exit ); break; } switch ( data ) { case cs8406_id: mTopazPlugin = AppleTopazPluginFactory::createTopazPlugin ( kCS8406_CODEC ); break; case cs8416_id: mTopazPlugin = AppleTopazPluginFactory::createTopazPlugin ( kCS8416_CODEC ); break; case cs8420_id: mTopazPlugin = AppleTopazPluginFactory::createTopazPlugin ( kCS8420_CODEC ); break; default: FailIf ( true, Exit ); break; } FailIf (NULL == mTopazPlugin, Exit); mTopazPlugin->initPlugin ( mPlatformInterface, mAudioDeviceProvider, mCodecID ); // [3648867] mTopazPlugin->initCodecRegisterCache (); result = mTopazPlugin->preDMAEngineInit ( autoClockSelectionIsBeingUsed ); Exit: debugIOLog (3, "- AppleTopazAudio::preDMAEngineInit () returns %d", result ); return result; }
/** * @brief Configures the audio peripherals. * @param OutputDevice: OUTPUT_DEVICE_SPEAKER, OUTPUT_DEVICE_HEADPHONE, * OUTPUT_DEVICE_BOTH or OUTPUT_DEVICE_AUTO . * @param Volume: Initial volume level (from 0 (Mute) to 100 (Max)) * @param AudioFreq: Audio frequency used to play the audio stream. * @note This function configure also that the I2S PLL input clock. * @retval 0 if correct communication, else wrong communication */ uint8_t BSP_AUDIO_OUT_Init(uint16_t OutputDevice, uint8_t Volume, uint32_t AudioFreq) { uint8_t ret = AUDIO_ERROR; uint32_t deviceid = 0x00; RCC_PeriphCLKInitTypeDef RCC_ExCLKInitStruct; uint8_t index = 0, freqindex = 0xFF; for(index = 0; index < 8; index++) { if(I2SFreq[index] == AudioFreq) { freqindex = index; } } HAL_RCCEx_GetPeriphCLKConfig(&RCC_ExCLKInitStruct); if(freqindex != 0xFF) { /* I2S clock config PLLI2S_VCO = f(VCO clock) = f(PLLI2S clock input) × (PLLI2SN/PLLM) I2SCLK = f(PLLI2S clock output) = f(VCO clock) / PLLI2SR */ RCC_ExCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_I2S; RCC_ExCLKInitStruct.PLLI2S.PLLI2SN = I2SPLLN[freqindex]; RCC_ExCLKInitStruct.PLLI2S.PLLI2SR = I2SPLLR[freqindex]; HAL_RCCEx_PeriphCLKConfig(&RCC_ExCLKInitStruct); } else /* Default PLL I2S configuration */ { /* I2S clock config PLLI2S_VCO = f(VCO clock) = f(PLLI2S clock input) × (PLLI2SN/PLLM) I2SCLK = f(PLLI2S clock output) = f(VCO clock) / PLLI2SR */ RCC_ExCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_I2S; RCC_ExCLKInitStruct.PLLI2S.PLLI2SN = 258; RCC_ExCLKInitStruct.PLLI2S.PLLI2SR = 3; HAL_RCCEx_PeriphCLKConfig(&RCC_ExCLKInitStruct); } /* Reset the Codec Registers */ CODEC_Reset(); deviceid = cs43l22_drv.ReadID(AUDIO_I2C_ADDRESS); if((deviceid & CS43L22_ID_MASK) == CS43L22_ID) { /* Initialize the audio driver structure */ audio_drv = &cs43l22_drv; ret = AUDIO_OK; } else { ret = AUDIO_ERROR; } if(ret == AUDIO_OK) { audio_drv->Init(AUDIO_I2C_ADDRESS, OutputDevice, Volume, AudioFreq); /* I2S data transfer preparation: Prepare the Media to be used for the audio transfer from memory to I2S peripheral */ /* Configure the I2S peripheral */ I2Sx_Init(AudioFreq); } return ret; }