| /****************************************************************************** |
| * 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 - 2016, 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 <driverlib/i2s.h> |
| |
| //***************************************************************************** |
| // |
| // 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)); |
| (void)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)); |
| (void)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)); |
| (void)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)); |
| (void)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)); |
| (void)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)); |
| (void)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)); |
| (void)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; |
| (void)ui32Base; |
| (void)ui32Channel; |
| |
| // |
| // 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); |
| } |