/* * Copyright (c) 2019, 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: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * 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. * * * Neither the name of Texas Instruments Incorporated 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 OWNER 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 #include #include DeviceFamily_constructPath(driverlib/i2s.h) #include DeviceFamily_constructPath(driverlib/prcm.h) #include #include #include #include #include #define I2S_CLOCK_DIVIDER_MAX 1024U #define I2S_CLOCK_DIVIDER_MIN 2U #define I2S_NB_CHANNELS_MAX 8U #define I2S_RAW_CLOCK_48MHZ 48000000U /* Clock if not divided (48 MHz) */ #define I2S_MEMORY_LENGTH_16BITS_CC26XX 0U /* Internally used to set memory length to 16 bits */ #define I2S_MEMORY_LENGTH_24BITS_CC26XX 1U /* Internally used to set memory length to 24 bits */ /* Forward declarations */ static bool initObject(I2S_Handle handle, I2S_Params *params); static bool initIO(I2S_Handle handle); static void initHw(I2S_Handle handle); static void I2S_hwiIntFxn(uintptr_t arg); static int i2sPostNotify(unsigned int eventType, uintptr_t eventArg, uintptr_t clientArg); static void configSerialFormat(I2S_Handle handle); static void configChannels(I2S_Handle handle); static void configClocks(I2S_Handle handle); static void enableClocks(I2S_Handle handle); static bool computeSCKDivider(I2S_Handle handle, const I2S_Params *params, uint16_t *result); static bool computeWSDivider(I2S_Handle handle, const I2S_Params *params, uint16_t *result); static uint8_t getNumberOfChannels(const uint8_t channelsList); static uint32_t getBitRate(I2S_Handle handle, const I2S_Params *params); static uint16_t computeMemoryStep(I2S_Handle handle, I2S_DataInterfaceUse expectedUseSD0, I2S_DataInterfaceUse expectedUseSD1); static void updatePointer(I2S_Handle handle, I2SCC26XX_Interface *interface); /* Extern globals */ extern I2S_Config I2S_config[]; extern const uint_least8_t I2S_count; /* Static globals */ static bool isInitialized = (bool)false; /* * ======== I2S_init ======== */ void I2S_init(void) { uint_least8_t i; if (!isInitialized) { /* Call each instances' driver init function */ for (i = 0; i < I2S_count; i++) { I2S_Handle handle = (I2S_Handle)&(I2S_config[i]); I2SCC26XX_Object *object = (I2SCC26XX_Object *)handle->object; object->isOpen = (bool)false; } isInitialized = (bool)true; } } /* * ======== I2S_open ======== */ I2S_Handle I2S_open(uint_least8_t index, I2S_Params *params) { I2S_Handle handle; I2SCC26XX_Object *object; handle = (I2S_Handle)&(I2S_config[index]); object = handle->object; DebugP_assert(index < I2S_count); /* Check if module is initialized. */ if (!isInitialized || object->isOpen) { handle = NULL; } /* Initialization of the I2S-object and verification of the parameters. */ else if (!initObject(handle, params)) { /* The parameters provided are not correct. */ handle = NULL; } /* Configure IOs, make sure it was successful. */ else if (!initIO(handle)) { /* Another driver or application already using these pins. */ handle = NULL; } /* Set Power and register interrupts */ else { object->isOpen = (bool)true; /* Register power dependency - i.e. power up and enable clock for I2S. */ Power_setDependency(PowerCC26XX_PERIPH_I2S); /* Register notification functions */ Power_registerNotify(&object->i2sPostObj, PowerCC26XX_AWAKE_STANDBY, (Power_NotifyFxn)i2sPostNotify, (uintptr_t)handle); HwiP_Params hwiParams; I2SCC26XX_HWAttrs const *hwAttrs = handle->hwAttrs; /* Register HW interrupt */ HwiP_Params_init(&hwiParams); hwiParams.arg = (uintptr_t)handle; hwiParams.priority = hwAttrs->intPriority; HwiP_construct(&(object->hwi), INT_I2S_IRQ, (HwiP_Fxn)I2S_hwiIntFxn, &hwiParams); HwiP_clearInterrupt(INT_I2S_IRQ); } return handle; } /* * ======== I2S_close ======== */ void I2S_close(I2S_Handle handle) { I2SCC26XX_Object *object = handle->object; /* Disable I2S interrupts. */ I2SIntDisable(I2S0_BASE, I2S_INT_ALL); HwiP_destruct(&(object->hwi)); /* Wait for end of started transactions */ while((I2SInPointerNextGet(I2S0_BASE) != 0U) || (I2SOutPointerNextGet(I2S0_BASE) != 0U)){} while((I2SInPointerGet(I2S0_BASE) != 0U) || (I2SOutPointerGet(I2S0_BASE) != 0U)){} I2SInPointerSet(I2S0_BASE, 0U); I2SOutPointerSet(I2S0_BASE, 0U); I2SSampleStampInConfigure(I2S0_BASE, 0xFFFFU); I2SSampleStampOutConfigure(I2S0_BASE, 0xFFFFU); /* Disable I2S module */ I2SStop(I2S0_BASE); I2SSampleStampDisable(I2S0_BASE); /* Disable internal clocks */ PRCMAudioClockDisable(); /* Deallocate pins */ PIN_close(object->hPin); /* Unregister power notification objects */ Power_unregisterNotify(&object->i2sPostObj); /* Release power dependency - i.e. potentially power down serial domain. */ Power_releaseDependency(PowerCC26XX_PERIPH_I2S); /* Mark the module as available */ object->isOpen = (bool)false; } /* * ======== I2S_setReadQueueHead ======== */ void I2S_setReadQueueHead(I2S_Handle handle, I2S_Transaction *transaction){ DebugP_assert(&transaction != 0x0); I2SCC26XX_Object *object = handle->object; I2SCC26XX_Interface *interface = &object->read; interface->activeTransfer = transaction; } /* * ======== I2S_setWriteQueueHead ======== */ void I2S_setWriteQueueHead(I2S_Handle handle, I2S_Transaction *transaction){ DebugP_assert(&transaction != 0x0); I2SCC26XX_Object *object = handle->object; I2SCC26XX_Interface *interface = &object->write; interface->activeTransfer = transaction; } /* * ======== I2S_startClocks ======== */ void I2S_startClocks(I2S_Handle handle) { Power_setConstraint(PowerCC26XX_SB_DISALLOW); initHw(handle); enableClocks(handle); /* Configuring sample stamp generator will trigger the audio stream to start */ I2SSampleStampInConfigure(I2S0_BASE, 0); I2SSampleStampOutConfigure(I2S0_BASE, 0); } /* * ======== I2S_stopClocks ======== */ void I2S_stopClocks(I2S_Handle handle) { I2SIntClear(I2S0_BASE, I2S_INT_ALL); I2SIntDisable(I2S0_BASE, (uint32_t)I2S_INT_TIMEOUT | (uint32_t)I2S_INT_BUS_ERR | (uint32_t)I2S_INT_WCLK_ERR | (uint32_t)I2S_INT_PTR_ERR); I2SStop(I2S0_BASE); I2SSampleStampDisable(I2S0_BASE); PRCMAudioClockDisable(); PRCMLoadSet(); Power_releaseConstraint(PowerCC26XX_SB_DISALLOW); } /* * ======== I2S_startRead ======== */ void I2S_startRead(I2S_Handle handle) { I2SCC26XX_Object *object = handle->object; /* If a read interface is activated */ if(object->read.memoryStep != 0){ /* Enable I2S Hardware Interrupts */ I2SIntEnable(I2S0_BASE, (uint32_t)I2S_INT_DMA_IN | (uint32_t)I2S_INT_TIMEOUT | (uint32_t)I2S_INT_BUS_ERR | (uint32_t)I2S_INT_WCLK_ERR | (uint32_t)I2S_INT_PTR_ERR); /* At startup, INPTRNEXT must be written twice. The first value will be directly copy to INPTR */ object->ptrUpdateFxn(handle, &object->read); object->ptrUpdateFxn(handle, &object->read); /* Configuring sample stamp generator will trigger the audio stream to start */ if(object->read.delay != 0U) { I2SSampleStampInConfigure(I2S0_BASE, object->read.delay); I2SWclkCounterReset(I2S0_BASE); } } } /* * ======== I2S_startWrite ======== */ void I2S_startWrite(I2S_Handle handle) { I2SCC26XX_Object *object = handle->object; /* If a write interface is activated */ if(object->write.memoryStep != 0){ /* Enable I2S Hardware Interrupts */ I2SIntEnable(I2S0_BASE, (uint32_t)I2S_INT_DMA_OUT | (uint32_t)I2S_INT_TIMEOUT | (uint32_t)I2S_INT_BUS_ERR | (uint32_t)I2S_INT_WCLK_ERR | (uint32_t)I2S_INT_PTR_ERR); /* At startup, OUTPTRNEXT must be written twice. The first value will be directly copy to OUTPTR */ object->ptrUpdateFxn(handle, &object->write); object->ptrUpdateFxn(handle, &object->write); /* Configuring sample stamp generator will trigger the audio stream to start */ if(object->write.delay != 0U) { I2SSampleStampOutConfigure(I2S0_BASE, object->write.delay); I2SWclkCounterReset(I2S0_BASE); } } } /* * ======== I2S_stopRead ======== */ void I2S_stopRead(I2S_Handle handle) { I2SCC26XX_Object *object = handle->object; /* If a read interface is activated */ if(object->read.memoryStep != 0){ /* Disable DMA_IN interrupts: we do not need anymore to refresh IN_PTR */ I2SIntDisable(I2S0_BASE, I2S_INT_DMA_IN); /* Wait for end of started transfers */ while(I2SInPointerNextGet(I2S0_BASE) != 0U){} while(I2SInPointerGet(I2S0_BASE) != 0U){} I2SIntClear(I2S0_BASE, I2S_INT_DMA_IN); I2SIntClear(I2S0_BASE, I2S_INT_PTR_ERR); I2SSampleStampInConfigure(I2S0_BASE, 0xFFFFU); I2SInPointerSet(I2S0_BASE, 0U); } } /* * ======== I2S_stopWrite ======== */ void I2S_stopWrite(I2S_Handle handle) { I2SCC26XX_Object *object = handle->object; /* If a write interface is activated */ if(object->write.memoryStep != 0){ /* Disable DMA_OUT interrupts: we do not need anymore to refresh OUT_PTR */ I2SIntDisable(I2S0_BASE, I2S_INT_DMA_OUT); /* Wait for end of started transactions */ while(I2SOutPointerNextGet(I2S0_BASE) != 0U){} while(I2SOutPointerGet(I2S0_BASE) != 0U){} I2SIntClear(I2S0_BASE, I2S_INT_DMA_OUT); I2SIntClear(I2S0_BASE, I2S_INT_PTR_ERR); I2SSampleStampOutConfigure(I2S0_BASE, 0xFFFFU); I2SOutPointerSet(I2S0_BASE, 0U); } } /* * ======== I2S_hwiIntFxn ======== * Hwi function that processes I2S interrupts. */ static void I2S_hwiIntFxn(uintptr_t arg) { I2S_Handle handle = (I2S_Handle)arg; I2SCC26XX_Object *object = handle->object; uint16_t errStatus = 0U; uint32_t interruptStatus = I2SIntStatus(I2S0_BASE, (bool)true); /* I2S_INT_PTR_ERR flag should be consider if and only if I2S_INT_DMA_IN or I2S_INT_DMA_OUT is raised at the same time */ if((((interruptStatus & (uint32_t)I2S_INT_PTR_ERR) != 0U) && (((interruptStatus & (uint32_t)I2S_INT_DMA_IN) != 0U)) || ((interruptStatus & (uint32_t)I2S_INT_DMA_OUT) != 0U))) { if((interruptStatus & (uint32_t)I2S_INT_DMA_IN) != 0U) { /* Try to update IN_PTR */ object->ptrUpdateFxn(handle, &object->read); /* Check if we could clear I2S_INT_DMA_IN flag */ interruptStatus = I2SIntStatus(I2S0_BASE, (bool)true); /* I2S_INT_PTR_ERR is confirmed if we could not clear I2S_INT_DMA_IN flag */ if(((interruptStatus & (uint32_t)I2S_INT_PTR_ERR) != 0U) && ((interruptStatus & (uint32_t)I2S_INT_DMA_IN) != 0U)) { I2SIntClear(I2S0_BASE, I2S_INT_PTR_ERR); errStatus = errStatus | I2S_PTR_READ_ERROR; } } if((interruptStatus & (uint32_t)I2S_INT_DMA_OUT) != 0U) { /* Try to update OUT_PTR */ object->ptrUpdateFxn(handle, &object->write); /* Check if we could clear I2S_INT_DMA_OUT flag */ interruptStatus = I2SIntStatus(I2S0_BASE, (bool)true); /* I2S_INT_PTR_ERR is confirmed if we could not clear I2S_INT_DMA_OUT flag */ if(((interruptStatus & (uint32_t)I2S_INT_PTR_ERR) != 0U) && ((interruptStatus & (uint32_t)I2S_INT_DMA_OUT) != 0U)) { I2SIntClear(I2S0_BASE, I2S_INT_PTR_ERR); errStatus = errStatus | I2S_PTR_WRITE_ERROR; } } } else if((interruptStatus & (uint32_t)I2S_INT_PTR_ERR) != 0U) { /* I2S_INT_PTR_ERR must not be considered as no I2S_INT_DMA_xxx flag is set */ I2SIntClear(I2S0_BASE, I2S_INT_PTR_ERR); } if((interruptStatus & (uint32_t)I2S_INT_DMA_IN) != 0U) { object->ptrUpdateFxn(handle, &object->read); } if((interruptStatus & (uint32_t)I2S_INT_DMA_OUT) != 0U) { object->ptrUpdateFxn(handle, &object->write); } if((interruptStatus & (uint32_t)I2S_INT_TIMEOUT) != 0U) { I2SIntClear(I2S0_BASE, I2S_INT_TIMEOUT); errStatus = errStatus | I2S_TIMEOUT_ERROR; } if((interruptStatus & (uint32_t)I2S_INT_BUS_ERR) != 0U) { I2SIntClear(I2S0_BASE, I2S_INT_BUS_ERR); errStatus = errStatus | I2S_BUS_ERROR; } if((interruptStatus & (uint32_t)I2S_INT_WCLK_ERR) != 0U) { I2SIntClear(I2S0_BASE, I2S_INT_WCLK_ERR); errStatus = errStatus | I2S_WS_ERROR; } if(errStatus != 0U) { object->errorCallback(handle, errStatus, NULL); } } /* * ======== initHw ======== * This functions initializes the I2S hardware module. * * @pre Function assumes that the I2S handle is pointing to a hardware * module which has already been opened. */ static void initHw(I2S_Handle handle) { /* Configure serial format. */ configSerialFormat(handle); /* Configure the channels used on each data interface. */ configChannels(handle); /* Configure the clocks for the MCLK, SCK and WS signals. */ configClocks(handle); } /* * ======== updatePointer ======== */ static void updatePointer(I2S_Handle handle, I2SCC26XX_Interface *interface) { I2S_Transaction *transaction = interface->activeTransfer; if(transaction != NULL) { /* Critical section to prevent any modification or deletion of the current transaction */ uintptr_t key; key = HwiP_disable(); /* Transaction */ if((transaction->bytesTransferred + interface->memoryStep) > transaction->bufSize){ /* The current transaction is over */ I2S_Transaction *transactionFinished = transaction; transaction = (I2S_Transaction*)List_next(&transactionFinished->queueElement); interface->activeTransfer = transaction; transactionFinished->numberOfCompletions ++; transactionFinished->untransferredBytes = transactionFinished->bufSize - transactionFinished->bytesTransferred; transactionFinished->bytesTransferred = 0; if(transaction != NULL){ interface->pointerSet(I2S0_BASE, ((uint32_t)transaction->bufPtr + transaction->bytesTransferred)); transaction->bytesTransferred += interface->memoryStep; interface->callback(handle, I2S_TRANSACTION_SUCCESS, transactionFinished); } else { /* Not anymore transaction */ interface->callback(handle, I2S_ALL_TRANSACTIONS_SUCCESS, transactionFinished); } } else { interface->pointerSet(I2S0_BASE, ((uint32_t)transaction->bufPtr + transaction->bytesTransferred)); transaction->bytesTransferred += interface->memoryStep; } HwiP_restore(key); } else { /* No element in the queue: do nothing */ } } /* * ======== initObject ======== */ static bool initObject(I2S_Handle handle, I2S_Params *params) { I2SCC26XX_Object *object = handle->object; I2SCC26XX_DataInterface *SD0; I2SCC26XX_DataInterface *SD1; bool retVal = (bool)true; /* Get the pointer to the SD0 and SD1 interfaces*/ SD0 = &object->dataInterfaceSD0; SD1 = &object->dataInterfaceSD1; if(params == NULL) { /* This module cannot be open if the user does not provide the expected pointers callback */ /* So it is no point to try to load the default value here. */ retVal = (bool)false; } else { object->moduleRole = params->moduleRole; object->invertWS = params->invertWS; object->samplingEdge = params->samplingEdge; object->beforeWordPadding = params->beforeWordPadding; object->afterWordPadding = params->afterWordPadding; object->phaseType = params->phaseType; object->bitsPerWord = params->bitsPerWord; object->startUpDelay = params->startUpDelay; object->read.delay = 1; object->write.delay = 1; if(params->memorySlotLength == I2S_MEMORY_LENGTH_16BITS) {object->memorySlotLength = I2S_MEMORY_LENGTH_16BITS_CC26XX;} else if(params->memorySlotLength == I2S_MEMORY_LENGTH_24BITS) {object->memorySlotLength = I2S_MEMORY_LENGTH_24BITS_CC26XX;} else {retVal = (bool)false;} object->read.pointerSet = I2SInPointerSet; object->write.pointerSet = I2SOutPointerSet; object->ptrUpdateFxn = updatePointer; SD0->interfaceConfig = params->SD0Use; SD0->channelsUsed = params->SD0Channels; SD0->numberOfChannelsUsed = getNumberOfChannels((uint8_t)SD0->channelsUsed); SD1->interfaceConfig = params->SD1Use; SD1->channelsUsed = params->SD1Channels; SD1->numberOfChannelsUsed = getNumberOfChannels((uint8_t)SD1->channelsUsed); if(params->MCLKDivider == 1U) {retVal = (bool)false;} object->MCLKDivider = params->MCLKDivider; uint16_t SCKDivider = 0U; if (!computeSCKDivider(handle, params, &SCKDivider)) {retVal = (bool)false;} object->SCKDivider = SCKDivider; uint16_t WSDivider = 0U; if (!computeWSDivider(handle, params, &WSDivider)) {retVal = (bool)false;} object->WSDivider = WSDivider; object->read.memoryStep = computeMemoryStep(handle, I2S_SD0_INPUT, I2S_SD1_INPUT); object->write.memoryStep = computeMemoryStep(handle, I2S_SD0_OUTPUT, I2S_SD1_OUTPUT); /* If the user set a fixed length of the buffers, we can optimize the runtime by setting the DMA buffer length */ if(params->fixedBufferLength == 0U){retVal = (bool)false;} else{ if(params->fixedBufferLength != 1U) { uint16_t memoryStep = ((object->read.memoryStep > object->write.memoryStep)?object->read.memoryStep:object->write.memoryStep); if(memoryStep != 0U){ uint8_t dmaBuffSizeDivider = 1U; uint16_t dmaBuffSizeConfig = (uint16_t)(((((params->fixedBufferLength) / memoryStep ) * 2U) - 1U) / dmaBuffSizeDivider); /* The value of the DMA buffer size is limited to 255 */ while(dmaBuffSizeConfig > 255U) { dmaBuffSizeDivider = dmaBuffSizeDivider * 2; dmaBuffSizeConfig = (uint16_t)(((((params->fixedBufferLength) / memoryStep ) * 2U) - 1U) / dmaBuffSizeDivider); } object->dmaBuffSizeConfig = (uint8_t)dmaBuffSizeConfig; /* dmaBuffSizeConfig < 255 */ object->read.memoryStep = (object->read.memoryStep!=0U)? (uint16_t)(params->fixedBufferLength/dmaBuffSizeDivider) :0U; object->write.memoryStep = (object->write.memoryStep!=0U)? (uint16_t)(params->fixedBufferLength/dmaBuffSizeDivider) :0U; } } else { object->dmaBuffSizeConfig = 1U; } } object->read.callback = params->readCallback; if((object->read.callback == NULL) && (object->read.memoryStep != 0U)) {retVal = (bool)false;} object->write.callback = params->writeCallback; if((object->write.callback == NULL) && (object->write.memoryStep != 0U)) {retVal = (bool)false;} object->errorCallback = params->errorCallback; if((object->errorCallback == NULL)) {retVal = (bool)false;} object->read.activeTransfer = NULL; object->write.activeTransfer = NULL; } return retVal; } /* * ======== initIO ======== */ static bool initIO(I2S_Handle handle) { I2SCC26XX_Object *object; I2SCC26XX_HWAttrs const *hwAttrs; PIN_Config i2sPinTable[6]; bool retVal = (bool)true; uint32_t pinCLKstatus; uint32_t pinSD0status; uint32_t pinSD1status; /* Get the pointer to the object and hwAttrs */ object = handle->object; hwAttrs = handle->hwAttrs; if(object->moduleRole == I2S_MASTER) {pinCLKstatus = (uint32_t)IOC_STD_OUTPUT;} else {pinCLKstatus = (uint32_t)IOC_STD_INPUT;} if(object->dataInterfaceSD0.interfaceConfig == I2S_SD0_INPUT) {pinSD0status = (uint32_t)IOC_STD_INPUT;} else {pinSD0status = (uint32_t)IOC_STD_OUTPUT;} if(object->dataInterfaceSD1.interfaceConfig == I2S_SD1_INPUT) {pinSD1status = (uint32_t)IOC_STD_INPUT;} else {pinSD1status = (uint32_t)IOC_STD_OUTPUT;} i2sPinTable[0] = hwAttrs->pinMCLK | pinCLKstatus; i2sPinTable[1] = hwAttrs->pinWS | pinCLKstatus; i2sPinTable[2] = hwAttrs->pinSCK | pinCLKstatus; i2sPinTable[3] = hwAttrs->pinSD0 | pinSD0status; i2sPinTable[4] = hwAttrs->pinSD1 | pinSD1status; i2sPinTable[5] = PIN_TERMINATE; object->hPin = PIN_open(&object->pinState, i2sPinTable); if(hwAttrs->pinMCLK <= (PIN_Id)PINCC26XX_DIO30){ if(PIN_setOutputEnable(object->hPin, hwAttrs->pinMCLK, (bool)true) != (PIN_SUCCESS)) {retVal = (bool)false;} if(PINCC26XX_setMux(object->hPin, hwAttrs->pinMCLK, IOC_PORT_MCU_I2S_MCLK) != (PIN_SUCCESS)) {retVal = (bool)false;} } if(hwAttrs->pinWS <= (PIN_Id)PINCC26XX_DIO30){ if(PIN_setOutputEnable(object->hPin, hwAttrs->pinWS, (bool)true) != (PIN_SUCCESS)) {retVal = (bool)false;} if(PINCC26XX_setMux(object->hPin, hwAttrs->pinWS, IOC_PORT_MCU_I2S_WCLK) != (PIN_SUCCESS)) {retVal = (bool)false;} } if(hwAttrs->pinSCK <= (PIN_Id)PINCC26XX_DIO30){ if(PIN_setOutputEnable(object->hPin, hwAttrs->pinSCK, (bool)true) != (PIN_SUCCESS)) {retVal = (bool)false;} if(PINCC26XX_setMux(object->hPin, hwAttrs->pinSCK, IOC_PORT_MCU_I2S_BCLK) != (PIN_SUCCESS)) {retVal = (bool)false;} } if(hwAttrs->pinSD0 <= (PIN_Id)PINCC26XX_DIO30){ if(PIN_setOutputEnable(object->hPin, hwAttrs->pinSD0, (bool)true) != (PIN_SUCCESS)) {retVal = (bool)false;} if(PINCC26XX_setMux(object->hPin, hwAttrs->pinSD0, IOC_PORT_MCU_I2S_AD0) != (PIN_SUCCESS)) {retVal = (bool)false;} } if(hwAttrs->pinSD1 <= (PIN_Id)PINCC26XX_DIO30){ if(PIN_setOutputEnable(object->hPin, hwAttrs->pinSD1, (bool)true) != (PIN_SUCCESS)) {retVal = (bool)false;} if(PINCC26XX_setMux(object->hPin, hwAttrs->pinSD1, IOC_PORT_MCU_I2S_AD1) != (PIN_SUCCESS)) {retVal = (bool)false;} } return retVal; } /* * ======== getNumberOfChannels ======== */ static uint8_t getNumberOfChannels(const uint8_t channelsList) { uint8_t i = 0; uint8_t nbChannelsUsed = 0; for(i=0; i<=(I2S_NB_CHANNELS_MAX-1U); i++) { if(((channelsList >> i) & 1U) == 1U) { nbChannelsUsed ++; } } return nbChannelsUsed; } /* * ======== computeSCKDivider ======== */ static bool computeSCKDivider(I2S_Handle handle, const I2S_Params *params, uint16_t *result){ uint32_t expectedBitRate = getBitRate(handle, params); uint32_t freqDividerSCK = 0U; bool retVal = (bool)true; *result = 0; if (expectedBitRate == 0U) { retVal = (bool)false; } else { /* We want to round the integer division: ROUND(a/b)=(a+b/2)/b */ freqDividerSCK = ((I2S_RAW_CLOCK_48MHZ + expectedBitRate/2) / expectedBitRate); if ((freqDividerSCK < I2S_CLOCK_DIVIDER_MIN) || (freqDividerSCK > I2S_CLOCK_DIVIDER_MAX)) { retVal = (bool)false; } else { /* If we reach this code it means we have freqDividerSCK <= 1024 */ uint16_t u16FreqDividerSCK = (uint16_t)freqDividerSCK; *result = u16FreqDividerSCK; } } return retVal; } /* * ======== computeWSDivider ======== */ static bool computeWSDivider(I2S_Handle handle, const I2S_Params *params, uint16_t *result){ I2SCC26XX_Object const *object; I2SCC26XX_DataInterface const *SD0; I2SCC26XX_DataInterface const *SD1; bool retVal = (bool)true; uint8_t numbOfChannels = 0U; /* Get the pointer to the SD0 and SD1 interfaces*/ object = handle->object; SD0 = &object->dataInterfaceSD0; SD1 = &object->dataInterfaceSD1; *result = 0x0000; if(SD0->interfaceConfig) { numbOfChannels = (numbOfChannels > SD0->numberOfChannelsUsed)? numbOfChannels : SD0->numberOfChannelsUsed; } else { numbOfChannels = (numbOfChannels > SD1->numberOfChannelsUsed)? numbOfChannels : SD1->numberOfChannelsUsed; } uint16_t sampleLength = 0U; sampleLength += object->beforeWordPadding; sampleLength += object->bitsPerWord; sampleLength += object->afterWordPadding; /* No overflow risk as sampleLength<255+255+255 and numbOfChannels<=8 */ uint16_t numOfSCKCyles = (sampleLength * numbOfChannels); switch(object->phaseType) { case(I2S_PHASE_TYPE_DUAL) : /* WS is high for WDIV[9:0] (1 to 1023) SCK periods and low for WDIV[9:0] (1 to 1023) SCK periods. * WS frequency = SCK frequency / (2 x WDIV[9:0]) * Dual phase protocols don't accept more than two channels */ if (numbOfChannels <= 2U) { *result = sampleLength; } else { retVal = (bool)false; } break; case(I2S_PHASE_TYPE_SINGLE) : /* WS is high for 1 SCK period and low for WDIV[9:0] (1 to 1023) SCK periods. * WS frequency = SCK frequency / (1 + PRCM:I2SWCLKDIV.WDIV[9:0]) */ *result = (numOfSCKCyles - 1U); break; default : retVal = (bool)false; } return retVal; } /* * ======== getBitRate ======== */ static uint32_t getBitRate(I2S_Handle handle, const I2S_Params *params) { I2SCC26XX_Object const *object; I2SCC26XX_DataInterface const *SD0; I2SCC26XX_DataInterface const *SD1; uint32_t dataLength = 0U; /* Get the pointer to the SD0 and SD1 interfaces*/ object = handle->object; SD0 = &object->dataInterfaceSD0; SD1 = &object->dataInterfaceSD1; uint16_t sampleLength = 0U; sampleLength += object->beforeWordPadding; sampleLength += object->bitsPerWord; sampleLength += object->afterWordPadding; uint32_t samplePerChannelPerSecond = params->samplingFrequency; uint8_t numbOfChannels = 0U; if(SD0->interfaceConfig) { numbOfChannels = (object->phaseType == I2S_PHASE_TYPE_DUAL)? 2U : SD0->numberOfChannelsUsed; } else { numbOfChannels = (object->phaseType == I2S_PHASE_TYPE_DUAL)? 2U : SD1->numberOfChannelsUsed; } /* No risk of overflow: highest possible value is 24 000 000 (any higher value has no sense) */ dataLength = (uint32_t)((uint32_t)numbOfChannels * (uint32_t)sampleLength); return (dataLength * samplePerChannelPerSecond); } /* * ======== configSerialFormat ======== */ static void configSerialFormat(I2S_Handle handle) { I2SCC26XX_Object const *object; /* Get the pointer to the object*/ object = handle->object; I2SFormatConfigure(I2S0_BASE, object->beforeWordPadding, (uint8_t)object->memorySlotLength, (uint8_t)object->samplingEdge, (bool) (object->phaseType == I2S_PHASE_TYPE_DUAL), (object->bitsPerWord + object->afterWordPadding), object->startUpDelay); /* Prevent DMA start by setting an unreachable trigger */ I2SSampleStampInConfigure(I2S0_BASE, (uint16_t)((uint16_t)object->bitsPerWord + (uint16_t)object->afterWordPadding)); I2SSampleStampOutConfigure(I2S0_BASE, (uint16_t)((uint16_t)object->bitsPerWord + (uint16_t)object->afterWordPadding)); } /* * ======== configChannels ======== */ static void configChannels(I2S_Handle handle) { I2SCC26XX_Object const *object; I2SCC26XX_DataInterface const *SD0; I2SCC26XX_DataInterface const *SD1; /* Get the pointer to the SD0 and SD1 interfaces*/ object = handle->object; SD0 = &object->dataInterfaceSD0; SD1 = &object->dataInterfaceSD1; I2SFrameConfigure(I2S0_BASE, (uint8_t)SD0->interfaceConfig, (uint8_t)SD0->channelsUsed, (uint8_t)SD1->interfaceConfig, (uint8_t)SD1->channelsUsed); } /* * ======== configClocks ======== */ static void configClocks(I2S_Handle handle) { I2SCC26XX_Object const *object; /* Get the pointer to the object*/ object = handle->object; I2SWclkConfigure(I2S0_BASE, (bool)object->moduleRole, object->invertWS); /* Set internal audio clock source */ if(object->moduleRole) { PRCMAudioClockInternalSource(); PRCMAudioClockConfigOverride((uint8_t)object->samplingEdge, (uint8_t)object->phaseType, (uint32_t)object->MCLKDivider, (uint32_t)object->SCKDivider, (uint32_t)object->WSDivider); } /* Set external audio clock source */ else { PRCMAudioClockExternalSource(); } } /* * ======== enableClocks ======== */ static void enableClocks(I2S_Handle handle) { I2SCC26XX_Object const *object; /* Get the pointer to the object*/ object = handle->object; /* Enable internal clocks */ PRCMAudioClockEnable(); /* Enable sample stamps */ I2SSampleStampEnable(I2S0_BASE); I2SStart(I2S0_BASE, object->dmaBuffSizeConfig); /* Activate clocks (no clock is running before this call) * (clocks must be correctly set before) */ PRCMLoadSet(); } /* * ======== computeMemoryStep ======== */ static uint16_t computeMemoryStep(I2S_Handle handle, I2S_DataInterfaceUse expectedUseSD0, I2S_DataInterfaceUse expectedUseSD1) { I2SCC26XX_Object const *object; uint8_t numbOfChannels = 0; uint8_t sampleMemoryLength = 16; uint16_t memoryNeeded = 0; I2SCC26XX_DataInterface const *SD0; I2SCC26XX_DataInterface const *SD1; const uint8_t byteLength = 8U; /* Get the pointer to the object*/ object = handle->object; SD0 = &object->dataInterfaceSD0; SD1 = &object->dataInterfaceSD1; if(SD0->interfaceConfig == expectedUseSD0) { numbOfChannels += SD0->numberOfChannelsUsed; } if(SD1->interfaceConfig == expectedUseSD1) { numbOfChannels += SD1->numberOfChannelsUsed; } if(object->memorySlotLength == I2S_MEMORY_LENGTH_24BITS_CC26XX) { sampleMemoryLength = 24; } /*In the worst case we have 16x24x2=768 < 2^16 */ memoryNeeded = (uint16_t)((uint16_t)numbOfChannels * (uint16_t)sampleMemoryLength * (uint16_t)2U); /* bits to byte conversion: we manage to have full bytes */ if((memoryNeeded % byteLength) != 0U) { memoryNeeded += (byteLength - (memoryNeeded % byteLength)); } memoryNeeded = memoryNeeded / byteLength; return memoryNeeded; } /* * ======== i2sPostNotify ======== * This functions is called to notify the I2S driver of an ongoing transition * out of sleep mode. * * @pre Function assumes that the I2S handle (clientArg) is pointing to a * hardware module which has already been opened. */ static int i2sPostNotify(unsigned int eventType, uintptr_t eventArg, uintptr_t clientArg) { /* Reconfigure the hardware if returning from sleep */ if (eventType == (uint32_t)PowerCC26XX_AWAKE_STANDBY) { initHw((I2S_Handle)clientArg); } return Power_NOTIFYDONE; }