/****************************************************************************** * Filename: i2s.c * Revised: 2015-05-11 13:56:01 +0200 (Mon, 11 May 2015) * Revision: 43476 * * Description: Driver for the I2S. * * Copyright (c) 2015, Texas Instruments Incorporated * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1) Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2) Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3) Neither the name of the ORGANIZATION nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * ******************************************************************************/ #include //***************************************************************************** // // Handle support for DriverLib in ROM: // This section will undo prototype renaming made in the header file // //***************************************************************************** #if !defined(DOXYGEN) #undef I2SEnable #define I2SEnable NOROM_I2SEnable #undef I2SAudioFormatConfigure #define I2SAudioFormatConfigure NOROM_I2SAudioFormatConfigure #undef I2SChannelConfigure #define I2SChannelConfigure NOROM_I2SChannelConfigure #undef I2SBufferConfig #define I2SBufferConfig NOROM_I2SBufferConfig #undef I2SPointerUpdate #define I2SPointerUpdate NOROM_I2SPointerUpdate #undef I2SPointerSet #define I2SPointerSet NOROM_I2SPointerSet #undef I2SSampleStampConfigure #define I2SSampleStampConfigure NOROM_I2SSampleStampConfigure #undef I2SSampleStampGet #define I2SSampleStampGet NOROM_I2SSampleStampGet #endif //***************************************************************************** // // Global pointer to the current I2S data structure // //***************************************************************************** I2SControlTable *g_pControlTable; //***************************************************************************** // //! Enables the I2S module for operation // //***************************************************************************** void I2SEnable(uint32_t ui32Base) { // // Check the arguments. // ASSERT(I2SBaseValid(ui32Base)); // // Make sure the control table pointer is setup to a memory location. // if(!(g_pControlTable)) { return; } // // Write the address to the first input/output buffer. // HWREG(I2S0_BASE + I2S_O_AIFINPTRNEXT) = g_pControlTable->ui32InBase; g_pControlTable->ui32InOffset = 0; HWREG(I2S0_BASE + I2S_O_AIFOUTPTRNEXT) = g_pControlTable->ui32OutBase; g_pControlTable->ui32OutOffset = 0; // // Enable the I2S module. // HWREG(I2S0_BASE + I2S_O_AIFDMACFG) = (uint32_t)g_pControlTable->ui16DMABufSize - 1; } //***************************************************************************** // //! Configures the I2S module // //***************************************************************************** void I2SAudioFormatConfigure(uint32_t ui32Base, uint32_t ui32FmtCfg, uint32_t ui32BitClkDelay) { // // Check the arguments. // ASSERT(I2SBaseValid(ui32Base)); ASSERT(ui32BitClkDelay <= 255); // // Save the length of the audio words stored in memory. // g_pControlTable->ui16MemLen = (ui32FmtCfg & I2S_MEM_LENGTH_24) ? 24 : 16; // // Write the configuration. // HWREG(I2S0_BASE + I2S_O_AIFFMTCFG) = ui32FmtCfg | (ui32BitClkDelay << I2S_AIFFMTCFG_DATA_DELAY_S); } //**************************************************************************** // //! Setup the audio channel configuration // //**************************************************************************** void I2SChannelConfigure(uint32_t ui32Base, uint32_t ui32Chan0Cfg, uint32_t ui32Chan1Cfg, uint32_t ui32Chan2Cfg) { uint32_t ui32InChan; uint32_t ui32OutChan; uint32_t ui32ChanMask; // // Check the arguments. // ASSERT(I2SBaseValid(ui32Base)); ASSERT(ui32Chan0Cfg & (I2S_CHAN_CFG_MASK | I2S_LINE_MASK)) ASSERT(ui32Chan1Cfg & (I2S_CHAN_CFG_MASK | I2S_LINE_MASK)) ASSERT(ui32Chan2Cfg & (I2S_CHAN_CFG_MASK | I2S_LINE_MASK)) ui32InChan = 0; ui32OutChan = 0; // // Configure input/output channels. // HWREG(I2S0_BASE + I2S_O_AIFDIRCFG) = ((ui32Chan0Cfg << I2S_AIFDIRCFG_AD0_S) & I2S_AIFDIRCFG_AD0_M) | ((ui32Chan1Cfg << I2S_AIFDIRCFG_AD1_S) & I2S_AIFDIRCFG_AD1_M) | ((ui32Chan2Cfg << I2S_AIFDIRCFG_AD2_S) & I2S_AIFDIRCFG_AD2_M); // // Configure the valid channel mask. // HWREG(I2S0_BASE + I2S_O_AIFWMASK0) = (ui32Chan0Cfg >> 8) & I2S_AIFWMASK0_MASK_M; HWREG(I2S0_BASE + I2S_O_AIFWMASK1) = (ui32Chan1Cfg >> 8) & I2S_AIFWMASK1_MASK_M; HWREG(I2S0_BASE + I2S_O_AIFWMASK2) = (ui32Chan2Cfg >> 8) & I2S_AIFWMASK2_MASK_M; // // Resolve and save the number of input and output channels. // ui32ChanMask = (ui32Chan0Cfg & I2S_CHAN_CFG_MASK) >> 8; if(ui32Chan0Cfg & I2S_LINE_INPUT) { while(ui32ChanMask) { if(ui32ChanMask & 0x1) { ui32InChan++; } // // Shift down channel mask // ui32ChanMask >>= 1; } } else if(ui32Chan0Cfg & I2S_LINE_OUTPUT) { while(ui32ChanMask) { if(ui32ChanMask & 0x1) { ui32OutChan++; } // // Shift down channel mask // ui32ChanMask >>= 1; } } ui32ChanMask = (ui32Chan1Cfg & I2S_CHAN_CFG_MASK) >> 8; if(ui32Chan1Cfg & I2S_LINE_INPUT) { while(ui32ChanMask) { if(ui32ChanMask & 0x1) { ui32InChan++; } // // Shift down channel mask // ui32ChanMask >>= 1; } } else if(ui32Chan1Cfg & I2S_LINE_OUTPUT) { while(ui32ChanMask) { if(ui32ChanMask & 0x1) { ui32OutChan++; } // // Shift down channel mask // ui32ChanMask >>= 1; } } ui32ChanMask = (ui32Chan2Cfg & I2S_CHAN_CFG_MASK) >> 8; if(ui32Chan2Cfg & I2S_LINE_INPUT) { while(ui32ChanMask) { if(ui32ChanMask & 0x1) { ui32InChan++; } // // Shift down channel mask // ui32ChanMask >>= 1; } } else if(ui32Chan2Cfg & I2S_LINE_OUTPUT) { while(ui32ChanMask) { if(ui32ChanMask & 0x1) { ui32OutChan++; } // // Shift down channel mask // ui32ChanMask >>= 1; } } g_pControlTable->ui8InChan = (uint8_t)ui32InChan; g_pControlTable->ui8OutChan = (uint8_t)ui32OutChan; } //**************************************************************************** // //! Set the input buffer pointers // //**************************************************************************** void I2SBufferConfig(uint32_t ui32Base, uint32_t ui32InBufBase, uint32_t ui32OutBufBase, uint16_t ui16DMABufSize, uint16_t ui16ChanBufSize) { // // Check the arguments. // ASSERT(I2SBaseValid(ui32Base)); ASSERT(ui16DMABufSize > 0); // // Setup the input data pointer and buffer sizes. // g_pControlTable->ui16DMABufSize = ui16DMABufSize; g_pControlTable->ui16ChBufSize = ui16ChanBufSize; g_pControlTable->ui32InBase = ui32InBufBase; g_pControlTable->ui32OutBase = ui32OutBufBase; } //**************************************************************************** // //! Set the buffer pointers // //**************************************************************************** void I2SPointerSet(uint32_t ui32Base, bool bInput, void * pNextPointer) { // // Check the arguments. // ASSERT(I2SBaseValid(ui32Base)); // // Update the next input/output pointer with the correct address. // if(bInput == true) { HWREG(I2S0_BASE + I2S_O_AIFINPTRNEXT) = (uint32_t)pNextPointer; } else { HWREG(I2S0_BASE + I2S_O_AIFOUTPTRNEXT) = (uint32_t)pNextPointer; } } //**************************************************************************** // //! Update the buffer pointers // //**************************************************************************** void I2SPointerUpdate(uint32_t ui32Base, bool bInput) { uint32_t ui32NextPtr; // // Check the arguments. // ASSERT(I2SBaseValid(ui32Base)); // // Update the next input/output pointer with the correct address. // if(bInput == true) { ui32NextPtr = (g_pControlTable->ui8InChan * (g_pControlTable->ui16MemLen >> 3)) * g_pControlTable->ui16DMABufSize; g_pControlTable->ui32InOffset = ((g_pControlTable->ui32InOffset + ui32NextPtr) % g_pControlTable->ui16ChBufSize); HWREG(I2S0_BASE + I2S_O_AIFINPTRNEXT) = g_pControlTable->ui32InOffset + g_pControlTable->ui32InBase; } else { ui32NextPtr = (g_pControlTable->ui8OutChan * (g_pControlTable->ui16MemLen >> 3)) * g_pControlTable->ui16DMABufSize; g_pControlTable->ui32OutOffset = ((g_pControlTable->ui32OutOffset + ui32NextPtr) % g_pControlTable->ui16ChBufSize); HWREG(I2S0_BASE + I2S_O_AIFOUTPTRNEXT) = g_pControlTable->ui32OutOffset + g_pControlTable->ui32OutBase; } } //***************************************************************************** // //! Configure the sample stamp generator // //***************************************************************************** void I2SSampleStampConfigure(uint32_t ui32Base, bool bInput, bool bOutput) { uint32_t ui32Trigger; // // Check the arguments. // ASSERT(I2SBaseValid(ui32Base)); ui32Trigger = HWREG(I2S0_BASE + I2S_O_STMPWCNT); ui32Trigger = (ui32Trigger + 2) % g_pControlTable->ui16ChBufSize; // // Setup the sample stamp trigger for input streams. // if(bInput) { HWREG(I2S0_BASE + I2S_O_STMPINTRIG) = ui32Trigger; } // // Setup the sample stamp trigger for output streams. // if(bOutput) { HWREG(I2S0_BASE + I2S_O_STMPOUTTRIG) = ui32Trigger; } } //***************************************************************************** // //! Get the current value of a sample stamp counter // //***************************************************************************** uint32_t I2SSampleStampGet(uint32_t ui32Base, uint32_t ui32Channel) { uint32_t ui32FrameClkCnt; uint32_t ui32SysClkCnt; uint32_t ui32PeriodSysClkCnt; uint32_t ui32SampleStamp; // // Get the number of Frame clock counts since last stamp. // ui32FrameClkCnt = HWREG(I2S0_BASE + I2S_O_STMPWCNTCAPT0); // // Get the number of system clock ticks since last frame clock edge. // ui32SysClkCnt = HWREG(I2S0_BASE + I2S_O_STMPXCNTCAPT0); // // Get the number system clock ticks in the last frame clock period. // ui32PeriodSysClkCnt = HWREG(I2S0_BASE + I2S_O_STMPXPER); // // Calculate the sample stamp. // ui32SampleStamp = (ui32SysClkCnt << 16) / ui32PeriodSysClkCnt; ui32SampleStamp = (ui32SampleStamp > I2S_STMP_SATURATION) ? I2S_STMP_SATURATION : ui32SampleStamp; ui32SampleStamp |= (ui32FrameClkCnt << 16); return (ui32SampleStamp); }