knx/source/ti/drivers/i2s/I2SCC26XX.c
Nanosonde e51b65f8c2 Squashed 'examples/knx-cc1310/coresdk_cc13xx_cc26xx/' content from commit 0d78d32
git-subtree-dir: examples/knx-cc1310/coresdk_cc13xx_cc26xx
git-subtree-split: 0d78d3280357416a5c0388148cda13717c9ffaa5
2020-10-21 10:00:49 +02:00

959 lines
34 KiB
C

/*
* 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 <stdlib.h>
#include <ti/devices/DeviceFamily.h>
#include DeviceFamily_constructPath(driverlib/i2s.h)
#include DeviceFamily_constructPath(driverlib/prcm.h)
#include <ti/drivers/dpl/DebugP.h>
#include <ti/drivers/I2S.h>
#include <ti/drivers/i2s/I2SCC26XX.h>
#include <ti/drivers/Power.h>
#include <ti/drivers/power/PowerCC26XX.h>
#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;
}