diff options
Diffstat (limited to '')
-rw-r--r-- | bsps/arm/imxrt/mcux-sdk/drivers/sai/fsl_sai.c | 3831 | ||||
-rw-r--r-- | bsps/arm/imxrt/mcux-sdk/drivers/sai/fsl_sai.h | 1586 | ||||
-rw-r--r-- | bsps/arm/imxrt/mcux-sdk/drivers/sai/fsl_sai_edma.c | 1033 | ||||
-rw-r--r-- | bsps/arm/imxrt/mcux-sdk/drivers/sai/fsl_sai_edma.h | 367 | ||||
-rw-r--r-- | bsps/arm/imxrt/mcux-sdk/drivers/sai/fsl_sai_sdma.h | 238 |
5 files changed, 7055 insertions, 0 deletions
diff --git a/bsps/arm/imxrt/mcux-sdk/drivers/sai/fsl_sai.c b/bsps/arm/imxrt/mcux-sdk/drivers/sai/fsl_sai.c new file mode 100644 index 0000000000..1e37b4cd8f --- /dev/null +++ b/bsps/arm/imxrt/mcux-sdk/drivers/sai/fsl_sai.c @@ -0,0 +1,3831 @@ +/* + * Copyright (c) 2016, Freescale Semiconductor, Inc. + * Copyright 2016-2022 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "fsl_sai.h" + +/* Component ID definition, used by tools. */ +#ifndef FSL_COMPONENT_ID +#define FSL_COMPONENT_ID "platform.drivers.sai" +#endif + +/******************************************************************************* + * Definitations + ******************************************************************************/ +/*! @brief _sai_transfer_state sai transfer state.*/ +enum +{ + kSAI_Busy = 0x0U, /*!< SAI is busy */ + kSAI_Idle, /*!< Transfer is done. */ + kSAI_Error /*!< Transfer error occurred. */ +}; + +/*! @brief Typedef for sai tx interrupt handler. */ +typedef void (*sai_tx_isr_t)(I2S_Type *base, sai_handle_t *saiHandle); + +/*! @brief Typedef for sai rx interrupt handler. */ +typedef void (*sai_rx_isr_t)(I2S_Type *base, sai_handle_t *saiHandle); + +/*! @brief check flag avalibility */ +#define IS_SAI_FLAG_SET(reg, flag) (((reg) & ((uint32_t)flag)) != 0UL) +/******************************************************************************* + * Prototypes + ******************************************************************************/ +/*! + * @brief sai get rx enabled interrupt status. + * + * + * @param base SAI base pointer. + * @param enableFlag enable flag to check. + * @param statusFlag status flag to check. + */ +static bool SAI_RxGetEnabledInterruptStatus(I2S_Type *base, uint32_t enableFlag, uint32_t statusFlag); + +/*! + * @brief sai get tx enabled interrupt status. + * + * + * @param base SAI base pointer. + * @param enableFlag enable flag to check. + * @param statusFlag status flag to check. + */ +static bool SAI_TxGetEnabledInterruptStatus(I2S_Type *base, uint32_t enableFlag, uint32_t statusFlag); + +/*! + * @brief Set the master clock divider. + * + * This API will compute the master clock divider according to master clock frequency and master + * clock source clock source frequency. + * + * @param base SAI base pointer. + * @param mclk_Hz Mater clock frequency in Hz. + * @param mclkSrcClock_Hz Master clock source frequency in Hz. + */ +static bool SAI_TxGetEnabledInterruptStatus(I2S_Type *base, uint32_t enableFlag, uint32_t statusFlag); + +#if ((defined(FSL_FEATURE_SAI_HAS_MCLKDIV_REGISTER) && (FSL_FEATURE_SAI_HAS_MCLKDIV_REGISTER)) || \ + (defined(FSL_FEATURE_SAI_HAS_MCR_MCLK_POST_DIV) && (FSL_FEATURE_SAI_HAS_MCR_MCLK_POST_DIV))) + +/*! + * @brief Set the master clock divider. + * + * This API will compute the master clock divider according to master clock frequency and master + * clock source clock source frequency. + * + * @param base SAI base pointer. + * @param mclk_Hz Mater clock frequency in Hz. + * @param mclkSrcClock_Hz Master clock source frequency in Hz. + */ +static void SAI_SetMasterClockDivider(I2S_Type *base, uint32_t mclk_Hz, uint32_t mclkSrcClock_Hz); +#endif /* FSL_FEATURE_SAI_HAS_MCLKDIV_REGISTER */ + +/*! + * @brief Get the instance number for SAI. + * + * @param base SAI base pointer. + */ +static uint32_t SAI_GetInstance(I2S_Type *base); + +/*! + * @brief sends a piece of data in non-blocking way. + * + * @param base SAI base pointer + * @param channel start channel number. + * @param channelMask enabled channels mask. + * @param endChannel end channel numbers. + * @param bitWidth How many bits in a audio word, usually 8/16/24/32 bits. + * @param buffer Pointer to the data to be written. + * @param size Bytes to be written. + */ +static void SAI_WriteNonBlocking(I2S_Type *base, + uint32_t channel, + uint32_t channelMask, + uint32_t endChannel, + uint8_t bitWidth, + uint8_t *buffer, + uint32_t size); + +/*! + * @brief Receive a piece of data in non-blocking way. + * + * @param base SAI base pointer + * @param channel start channel number. + * @param channelMask enabled channels mask. + * @param endChannel end channel numbers. + * @param bitWidth How many bits in a audio word, usually 8/16/24/32 bits. + * @param buffer Pointer to the data to be read. + * @param size Bytes to be read. + */ +static void SAI_ReadNonBlocking(I2S_Type *base, + uint32_t channel, + uint32_t channelMask, + uint32_t endChannel, + uint8_t bitWidth, + uint8_t *buffer, + uint32_t size); + +/*! + * @brief Get classic I2S mode configurations. + * + * @param config transceiver configurations + * @param bitWidth audio data bitWidth. + * @param mode audio data channel + * @param saiChannelMask channel mask value to enable + */ +static void SAI_GetCommonConfig(sai_transceiver_t *config, + sai_word_width_t bitWidth, + sai_mono_stereo_t mode, + uint32_t saiChannelMask); +/******************************************************************************* + * Variables + ******************************************************************************/ +/* Base pointer array */ +static I2S_Type *const s_saiBases[] = I2S_BASE_PTRS; +/*!@brief SAI handle pointer */ +static sai_handle_t *s_saiHandle[ARRAY_SIZE(s_saiBases)][2]; +/* IRQ number array */ +static const IRQn_Type s_saiTxIRQ[] = I2S_TX_IRQS; +static const IRQn_Type s_saiRxIRQ[] = I2S_RX_IRQS; +#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) +/* Clock name array */ +static const clock_ip_name_t s_saiClock[] = SAI_CLOCKS; +#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ +/*! @brief Pointer to tx IRQ handler for each instance. */ +static sai_tx_isr_t s_saiTxIsr; +/*! @brief Pointer to tx IRQ handler for each instance. */ +static sai_rx_isr_t s_saiRxIsr; + +/******************************************************************************* + * Code + ******************************************************************************/ +static bool SAI_RxGetEnabledInterruptStatus(I2S_Type *base, uint32_t enableFlag, uint32_t statusFlag) +{ + uint32_t rcsr = base->RCSR; + + return IS_SAI_FLAG_SET(rcsr, enableFlag) && IS_SAI_FLAG_SET(rcsr, statusFlag); +} + +static bool SAI_TxGetEnabledInterruptStatus(I2S_Type *base, uint32_t enableFlag, uint32_t statusFlag) +{ + uint32_t tcsr = base->TCSR; + + return IS_SAI_FLAG_SET(tcsr, enableFlag) && IS_SAI_FLAG_SET(tcsr, statusFlag); +} + +#if ((defined(FSL_FEATURE_SAI_HAS_MCLKDIV_REGISTER) && (FSL_FEATURE_SAI_HAS_MCLKDIV_REGISTER)) || \ + (defined(FSL_FEATURE_SAI_HAS_MCR_MCLK_POST_DIV) && (FSL_FEATURE_SAI_HAS_MCR_MCLK_POST_DIV))) +static void SAI_SetMasterClockDivider(I2S_Type *base, uint32_t mclk_Hz, uint32_t mclkSrcClock_Hz) +{ + assert(mclk_Hz <= mclkSrcClock_Hz); + + uint32_t sourceFreq = mclkSrcClock_Hz / 100U; /*In order to prevent overflow */ + uint32_t targetFreq = mclk_Hz / 100U; /*In order to prevent overflow */ + +#if FSL_FEATURE_SAI_HAS_MCR_MCLK_POST_DIV + uint32_t postDivider = sourceFreq / targetFreq; + + /* if source equal to target, then disable divider */ + if (postDivider == 1U) + { + base->MCR &= ~I2S_MCR_DIVEN_MASK; + } + else + { + base->MCR = (base->MCR & (~I2S_MCR_DIV_MASK)) | I2S_MCR_DIV(postDivider / 2U - 1U) | I2S_MCR_DIVEN_MASK; + } +#endif +#if FSL_FEATURE_SAI_HAS_MCLKDIV_REGISTER + uint16_t fract, divide; + uint32_t remaind = 0; + uint32_t current_remainder = 0xFFFFFFFFU; + uint16_t current_fract = 0; + uint16_t current_divide = 0; + uint32_t mul_freq = 0; + uint32_t max_fract = 256; + + /* Compute the max fract number */ + max_fract = targetFreq * 4096U / sourceFreq + 1U; + if (max_fract > 256U) + { + max_fract = 256U; + } + + /* Looking for the closet frequency */ + for (fract = 1; fract < max_fract; fract++) + { + mul_freq = sourceFreq * fract; + remaind = mul_freq % targetFreq; + divide = (uint16_t)(mul_freq / targetFreq); + + /* Find the exactly frequency */ + if (remaind == 0U) + { + current_fract = fract; + current_divide = (uint16_t)(mul_freq / targetFreq); + break; + } + + /* Closer to next one, set the closest to next data */ + if (remaind > mclk_Hz / 2U) + { + remaind = targetFreq - remaind; + divide += 1U; + } + + /* Update the closest div and fract */ + if (remaind < current_remainder) + { + current_fract = fract; + current_divide = divide; + current_remainder = remaind; + } + } + + /* Fill the computed fract and divider to registers */ + base->MDR = I2S_MDR_DIVIDE(current_divide - 1UL) | I2S_MDR_FRACT(current_fract - 1UL); + + /* Waiting for the divider updated */ + while ((base->MCR & I2S_MCR_DUF_MASK) != 0UL) + { + } +#endif +} +#endif /* FSL_FEATURE_SAI_HAS_MCLKDIV_REGISTER */ + +static uint32_t SAI_GetInstance(I2S_Type *base) +{ + uint32_t instance; + + /* Find the instance index from base address mappings. */ + for (instance = 0; instance < ARRAY_SIZE(s_saiBases); instance++) + { + if (s_saiBases[instance] == base) + { + break; + } + } + + assert(instance < ARRAY_SIZE(s_saiBases)); + + return instance; +} + +static void SAI_WriteNonBlocking(I2S_Type *base, + uint32_t channel, + uint32_t channelMask, + uint32_t endChannel, + uint8_t bitWidth, + uint8_t *buffer, + uint32_t size) +{ + uint32_t i = 0, j = 0U; + uint8_t m = 0; + uint8_t bytesPerWord = bitWidth / 8U; + uint32_t data = 0; + uint32_t temp = 0; + + for (i = 0; i < size / bytesPerWord; i++) + { + for (j = channel; j <= endChannel; j++) + { + if (IS_SAI_FLAG_SET((1UL << j), channelMask)) + { + for (m = 0; m < bytesPerWord; m++) + { + temp = (uint32_t)(*buffer); + data |= (temp << (8U * m)); + buffer++; + } + base->TDR[j] = data; + data = 0; + } + } + } +} + +static void SAI_ReadNonBlocking(I2S_Type *base, + uint32_t channel, + uint32_t channelMask, + uint32_t endChannel, + uint8_t bitWidth, + uint8_t *buffer, + uint32_t size) +{ + uint32_t i = 0, j = 0; + uint8_t m = 0; + uint8_t bytesPerWord = bitWidth / 8U; + uint32_t data = 0; + + for (i = 0; i < size / bytesPerWord; i++) + { + for (j = channel; j <= endChannel; j++) + { + if (IS_SAI_FLAG_SET((1UL << j), channelMask)) + { + data = base->RDR[j]; + for (m = 0; m < bytesPerWord; m++) + { + *buffer = (uint8_t)(data >> (8U * m)) & 0xFFU; + buffer++; + } + } + } + } +} + +static void SAI_GetCommonConfig(sai_transceiver_t *config, + sai_word_width_t bitWidth, + sai_mono_stereo_t mode, + uint32_t saiChannelMask) +{ + assert(NULL != config); + assert(saiChannelMask != 0U); + + (void)memset(config, 0, sizeof(sai_transceiver_t)); + + config->channelMask = (uint8_t)saiChannelMask; + /* sync mode default configurations */ + config->syncMode = kSAI_ModeAsync; + + /* master mode default */ + config->masterSlave = kSAI_Master; + + /* bit default configurations */ + config->bitClock.bclkSrcSwap = false; + config->bitClock.bclkInputDelay = false; + config->bitClock.bclkPolarity = kSAI_SampleOnRisingEdge; + config->bitClock.bclkSource = kSAI_BclkSourceMclkDiv; + + /* frame sync default configurations */ + config->frameSync.frameSyncWidth = (uint8_t)bitWidth; + config->frameSync.frameSyncEarly = true; +#if defined(FSL_FEATURE_SAI_HAS_ON_DEMAND_MODE) && FSL_FEATURE_SAI_HAS_ON_DEMAND_MODE + config->frameSync.frameSyncGenerateOnDemand = false; +#endif + config->frameSync.frameSyncPolarity = kSAI_PolarityActiveLow; + + /* serial data default configurations */ +#if defined(FSL_FEATURE_SAI_HAS_CHANNEL_MODE) && FSL_FEATURE_SAI_HAS_CHANNEL_MODE + config->serialData.dataMode = kSAI_DataPinStateOutputZero; +#endif + config->serialData.dataOrder = kSAI_DataMSB; + config->serialData.dataWord0Length = (uint8_t)bitWidth; + config->serialData.dataWordLength = (uint8_t)bitWidth; + config->serialData.dataWordNLength = (uint8_t)bitWidth; + config->serialData.dataFirstBitShifted = (uint8_t)bitWidth; + config->serialData.dataWordNum = 2U; + config->serialData.dataMaskedWord = (uint32_t)mode; + +#if defined(FSL_FEATURE_SAI_HAS_FIFO_FUNCTION_AFTER_ERROR) && FSL_FEATURE_SAI_HAS_FIFO_FUNCTION_AFTER_ERROR + config->fifo.fifoContinueOneError = true; +#endif +} + +/*! + * brief Initializes the SAI Tx peripheral. + * + * deprecated Do not use this function. It has been superceded by @ref SAI_Init + * + * Ungates the SAI clock, resets the module, and configures SAI Tx with a configuration structure. + * The configuration structure can be custom filled or set with default values by + * SAI_TxGetDefaultConfig(). + * + * note This API should be called at the beginning of the application to use + * the SAI driver. Otherwise, accessing the SAIM module can cause a hard fault + * because the clock is not enabled. + * + * param base SAI base pointer + * param config SAI configuration structure. + */ +void SAI_TxInit(I2S_Type *base, const sai_config_t *config) +{ + uint32_t val = 0; + +#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) + /* Enable the SAI clock */ + (void)CLOCK_EnableClock(s_saiClock[SAI_GetInstance(base)]); +#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ + +#if defined(FSL_FEATURE_SAI_HAS_MCR) && (FSL_FEATURE_SAI_HAS_MCR) +#if !(defined(FSL_FEATURE_SAI_HAS_NO_MCR_MICS) && (FSL_FEATURE_SAI_HAS_NO_MCR_MICS)) + /* Master clock source setting */ + val = (base->MCR & ~I2S_MCR_MICS_MASK); + base->MCR = (val | I2S_MCR_MICS(config->mclkSource)); +#endif + + /* Configure Master clock output enable */ + val = (base->MCR & ~I2S_MCR_MOE_MASK); + base->MCR = (val | I2S_MCR_MOE(config->mclkOutputEnable)); +#endif /* FSL_FEATURE_SAI_HAS_MCR */ + + SAI_TxReset(base); + + /* Configure audio protocol */ + if (config->protocol == kSAI_BusLeftJustified) + { + base->TCR2 |= I2S_TCR2_BCP_MASK; + base->TCR3 &= ~I2S_TCR3_WDFL_MASK; + base->TCR4 = I2S_TCR4_MF(1U) | I2S_TCR4_SYWD(31U) | I2S_TCR4_FSE(0U) | I2S_TCR4_FSP(0U) | I2S_TCR4_FRSZ(1U); + } + else if (config->protocol == kSAI_BusRightJustified) + { + base->TCR2 |= I2S_TCR2_BCP_MASK; + base->TCR3 &= ~I2S_TCR3_WDFL_MASK; + base->TCR4 = I2S_TCR4_MF(1U) | I2S_TCR4_SYWD(31U) | I2S_TCR4_FSE(0U) | I2S_TCR4_FSP(0U) | I2S_TCR4_FRSZ(1U); + } + else if (config->protocol == kSAI_BusI2S) + { + base->TCR2 |= I2S_TCR2_BCP_MASK; + base->TCR3 &= ~I2S_TCR3_WDFL_MASK; + base->TCR4 = I2S_TCR4_MF(1U) | I2S_TCR4_SYWD(31U) | I2S_TCR4_FSE(1U) | I2S_TCR4_FSP(1U) | I2S_TCR4_FRSZ(1U); + } + else if (config->protocol == kSAI_BusPCMA) + { + base->TCR2 &= ~I2S_TCR2_BCP_MASK; + base->TCR3 &= ~I2S_TCR3_WDFL_MASK; + base->TCR4 = I2S_TCR4_MF(1U) | I2S_TCR4_SYWD(0U) | I2S_TCR4_FSE(1U) | I2S_TCR4_FSP(0U) | I2S_TCR4_FRSZ(1U); + } + else + { + base->TCR2 &= ~I2S_TCR2_BCP_MASK; + base->TCR3 &= ~I2S_TCR3_WDFL_MASK; + base->TCR4 = I2S_TCR4_MF(1U) | I2S_TCR4_SYWD(0U) | I2S_TCR4_FSE(0U) | I2S_TCR4_FSP(0U) | I2S_TCR4_FRSZ(1U); + } + + /* Set master or slave */ + if (config->masterSlave == kSAI_Master) + { + base->TCR2 |= I2S_TCR2_BCD_MASK; + base->TCR4 |= I2S_TCR4_FSD_MASK; + + /* Bit clock source setting */ + val = base->TCR2 & (~I2S_TCR2_MSEL_MASK); + base->TCR2 = (val | I2S_TCR2_MSEL(config->bclkSource)); + } + else + { + base->TCR2 &= ~I2S_TCR2_BCD_MASK; + base->TCR4 &= ~I2S_TCR4_FSD_MASK; + } + + /* Set Sync mode */ + if (config->syncMode == kSAI_ModeAsync) + { + val = base->TCR2; + val &= ~I2S_TCR2_SYNC_MASK; + base->TCR2 = (val | I2S_TCR2_SYNC(0U)); + } + if (config->syncMode == kSAI_ModeSync) + { + val = base->TCR2; + val &= ~I2S_TCR2_SYNC_MASK; + base->TCR2 = (val | I2S_TCR2_SYNC(1U)); + /* If sync with Rx, should set Rx to async mode */ + val = base->RCR2; + val &= ~I2S_RCR2_SYNC_MASK; + base->RCR2 = (val | I2S_RCR2_SYNC(0U)); + } +#if defined(FSL_FEATURE_SAI_HAS_SYNC_WITH_ANOTHER_SAI) && (FSL_FEATURE_SAI_HAS_SYNC_WITH_ANOTHER_SAI) + if (config->syncMode == kSAI_ModeSyncWithOtherTx) + { + val = base->TCR2; + val &= ~I2S_TCR2_SYNC_MASK; + base->TCR2 = (val | I2S_TCR2_SYNC(2U)); + } + if (config->syncMode == kSAI_ModeSyncWithOtherRx) + { + val = base->TCR2; + val &= ~I2S_TCR2_SYNC_MASK; + base->TCR2 = (val | I2S_TCR2_SYNC(3U)); + } +#endif /* FSL_FEATURE_SAI_HAS_SYNC_WITH_ANOTHER_SAI */ + +#if defined(FSL_FEATURE_SAI_HAS_FIFO_FUNCTION_AFTER_ERROR) && FSL_FEATURE_SAI_HAS_FIFO_FUNCTION_AFTER_ERROR + SAI_TxSetFIFOErrorContinue(base, true); +#endif /* FSL_FEATURE_SAI_HAS_FIFO_FUNCTION_AFTER_ERROR */ +} + +/*! + * brief Initializes the SAI Rx peripheral. + * + * deprecated Do not use this function. It has been superceded by @ref SAI_Init + * + * Ungates the SAI clock, resets the module, and configures the SAI Rx with a configuration structure. + * The configuration structure can be custom filled or set with default values by + * SAI_RxGetDefaultConfig(). + * + * note This API should be called at the beginning of the application to use + * the SAI driver. Otherwise, accessing the SAI module can cause a hard fault + * because the clock is not enabled. + * + * param base SAI base pointer + * param config SAI configuration structure. + */ +void SAI_RxInit(I2S_Type *base, const sai_config_t *config) +{ + uint32_t val = 0; + +#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) + /* Enable SAI clock first. */ + (void)CLOCK_EnableClock(s_saiClock[SAI_GetInstance(base)]); +#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ + +#if defined(FSL_FEATURE_SAI_HAS_MCR) && (FSL_FEATURE_SAI_HAS_MCR) +#if !(defined(FSL_FEATURE_SAI_HAS_NO_MCR_MICS) && (FSL_FEATURE_SAI_HAS_NO_MCR_MICS)) + /* Master clock source setting */ + val = (base->MCR & ~I2S_MCR_MICS_MASK); + base->MCR = (val | I2S_MCR_MICS(config->mclkSource)); +#endif + + /* Configure Master clock output enable */ + val = (base->MCR & ~I2S_MCR_MOE_MASK); + base->MCR = (val | I2S_MCR_MOE(config->mclkOutputEnable)); +#endif /* FSL_FEATURE_SAI_HAS_MCR */ + + SAI_RxReset(base); + + /* Configure audio protocol */ + if (config->protocol == kSAI_BusLeftJustified) + { + base->RCR2 |= I2S_RCR2_BCP_MASK; + base->RCR3 &= ~I2S_RCR3_WDFL_MASK; + base->RCR4 = I2S_RCR4_MF(1U) | I2S_RCR4_SYWD(31U) | I2S_RCR4_FSE(0U) | I2S_RCR4_FSP(0U) | I2S_RCR4_FRSZ(1U); + } + else if (config->protocol == kSAI_BusRightJustified) + { + base->RCR2 |= I2S_RCR2_BCP_MASK; + base->RCR3 &= ~I2S_RCR3_WDFL_MASK; + base->RCR4 = I2S_RCR4_MF(1U) | I2S_RCR4_SYWD(31U) | I2S_RCR4_FSE(0U) | I2S_RCR4_FSP(0U) | I2S_RCR4_FRSZ(1U); + } + else if (config->protocol == kSAI_BusI2S) + { + base->RCR2 |= I2S_RCR2_BCP_MASK; + base->RCR3 &= ~I2S_RCR3_WDFL_MASK; + base->RCR4 = I2S_RCR4_MF(1U) | I2S_RCR4_SYWD(31U) | I2S_RCR4_FSE(1U) | I2S_RCR4_FSP(1U) | I2S_RCR4_FRSZ(1U); + } + else if (config->protocol == kSAI_BusPCMA) + { + base->RCR2 &= ~I2S_RCR2_BCP_MASK; + base->RCR3 &= ~I2S_RCR3_WDFL_MASK; + base->RCR4 = I2S_RCR4_MF(1U) | I2S_RCR4_SYWD(0U) | I2S_RCR4_FSE(1U) | I2S_RCR4_FSP(0U) | I2S_RCR4_FRSZ(1U); + } + else + { + base->RCR2 &= ~I2S_RCR2_BCP_MASK; + base->RCR3 &= ~I2S_RCR3_WDFL_MASK; + base->RCR4 = I2S_RCR4_MF(1U) | I2S_RCR4_SYWD(0U) | I2S_RCR4_FSE(0U) | I2S_RCR4_FSP(0U) | I2S_RCR4_FRSZ(1U); + } + + /* Set master or slave */ + if (config->masterSlave == kSAI_Master) + { + base->RCR2 |= I2S_RCR2_BCD_MASK; + base->RCR4 |= I2S_RCR4_FSD_MASK; + + /* Bit clock source setting */ + val = base->RCR2 & (~I2S_RCR2_MSEL_MASK); + base->RCR2 = (val | I2S_RCR2_MSEL(config->bclkSource)); + } + else + { + base->RCR2 &= ~I2S_RCR2_BCD_MASK; + base->RCR4 &= ~I2S_RCR4_FSD_MASK; + } + + /* Set Sync mode */ + if (config->syncMode == kSAI_ModeAsync) + { + val = base->RCR2; + val &= ~I2S_RCR2_SYNC_MASK; + base->RCR2 = (val | I2S_RCR2_SYNC(0U)); + } + if (config->syncMode == kSAI_ModeSync) + { + val = base->RCR2; + val &= ~I2S_RCR2_SYNC_MASK; + base->RCR2 = (val | I2S_RCR2_SYNC(1U)); + /* If sync with Tx, should set Tx to async mode */ + val = base->TCR2; + val &= ~I2S_TCR2_SYNC_MASK; + base->TCR2 = (val | I2S_TCR2_SYNC(0U)); + } +#if defined(FSL_FEATURE_SAI_HAS_SYNC_WITH_ANOTHER_SAI) && (FSL_FEATURE_SAI_HAS_SYNC_WITH_ANOTHER_SAI) + if (config->syncMode == kSAI_ModeSyncWithOtherTx) + { + val = base->RCR2; + val &= ~I2S_RCR2_SYNC_MASK; + base->RCR2 = (val | I2S_RCR2_SYNC(2U)); + } + if (config->syncMode == kSAI_ModeSyncWithOtherRx) + { + val = base->RCR2; + val &= ~I2S_RCR2_SYNC_MASK; + base->RCR2 = (val | I2S_RCR2_SYNC(3U)); + } +#endif /* FSL_FEATURE_SAI_HAS_SYNC_WITH_ANOTHER_SAI */ + +#if defined(FSL_FEATURE_SAI_HAS_FIFO_FUNCTION_AFTER_ERROR) && FSL_FEATURE_SAI_HAS_FIFO_FUNCTION_AFTER_ERROR + SAI_RxSetFIFOErrorContinue(base, true); +#endif /* FSL_FEATURE_SAI_HAS_FIFO_FUNCTION_AFTER_ERROR */ +} + +/*! + * brief Initializes the SAI peripheral. + * + * This API gates the SAI clock. The SAI module can't operate unless SAI_Init is called to enable the clock. + * + * param base SAI base pointer + */ +void SAI_Init(I2S_Type *base) +{ +#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) + /* Enable the SAI clock */ + (void)CLOCK_EnableClock(s_saiClock[SAI_GetInstance(base)]); +#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ + +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + /* disable interrupt and DMA request*/ + base->TCSR &= + ~(I2S_TCSR_FRIE_MASK | I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK | I2S_TCSR_FRDE_MASK | I2S_TCSR_FWDE_MASK); + base->RCSR &= + ~(I2S_RCSR_FRIE_MASK | I2S_RCSR_FWIE_MASK | I2S_RCSR_FEIE_MASK | I2S_RCSR_FRDE_MASK | I2S_RCSR_FWDE_MASK); +#else + /* disable interrupt and DMA request*/ + base->TCSR &= ~(I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK | I2S_TCSR_FWDE_MASK); + base->RCSR &= ~(I2S_RCSR_FWIE_MASK | I2S_RCSR_FEIE_MASK | I2S_RCSR_FWDE_MASK); +#endif +} + +/*! + * brief De-initializes the SAI peripheral. + * + * This API gates the SAI clock. The SAI module can't operate unless SAI_TxInit + * or SAI_RxInit is called to enable the clock. + * + * param base SAI base pointer + */ +void SAI_Deinit(I2S_Type *base) +{ + SAI_TxEnable(base, false); + SAI_RxEnable(base, false); +#if !(defined(FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) && FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL) + (void)CLOCK_DisableClock(s_saiClock[SAI_GetInstance(base)]); +#endif /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL */ +} + +/*! + * brief Sets the SAI Tx configuration structure to default values. + * + * deprecated Do not use this function. It has been superceded by @ref + * SAI_GetClassicI2SConfig, SAI_GetLeftJustifiedConfig,SAI_GetRightJustifiedConfig, SAI_GetDSPConfig,SAI_GetTDMConfig + * + * This API initializes the configuration structure for use in SAI_TxConfig(). + * The initialized structure can remain unchanged in SAI_TxConfig(), or it can be modified + * before calling SAI_TxConfig(). + * This is an example. + code + sai_config_t config; + SAI_TxGetDefaultConfig(&config); + endcode + * + * param config pointer to master configuration structure + */ +void SAI_TxGetDefaultConfig(sai_config_t *config) +{ + /* Initializes the configure structure to zero. */ + (void)memset(config, 0, sizeof(*config)); + + config->bclkSource = kSAI_BclkSourceMclkDiv; + config->masterSlave = kSAI_Master; +#if defined(FSL_FEATURE_SAI_HAS_MCR) && (FSL_FEATURE_SAI_HAS_MCR) + config->mclkOutputEnable = true; +#if !(defined(FSL_FEATURE_SAI_HAS_NO_MCR_MICS) && (FSL_FEATURE_SAI_HAS_NO_MCR_MICS)) + config->mclkSource = kSAI_MclkSourceSysclk; +#endif +#endif /* FSL_FEATURE_SAI_HAS_MCR */ + config->protocol = kSAI_BusI2S; + config->syncMode = kSAI_ModeAsync; +} + +/*! + * brief Sets the SAI Rx configuration structure to default values. + * + * deprecated Do not use this function. It has been superceded by @ref + * SAI_GetClassicI2SConfig,SAI_GetLeftJustifiedConfig,SAI_GetRightJustifiedConfig,SAI_GetDSPConfig,SAI_GetTDMConfig + * + * This API initializes the configuration structure for use in SAI_RxConfig(). + * The initialized structure can remain unchanged in SAI_RxConfig() or it can be modified + * before calling SAI_RxConfig(). + * This is an example. + code + sai_config_t config; + SAI_RxGetDefaultConfig(&config); + endcode + * + * param config pointer to master configuration structure + */ +void SAI_RxGetDefaultConfig(sai_config_t *config) +{ + /* Initializes the configure structure to zero. */ + (void)memset(config, 0, sizeof(*config)); + + config->bclkSource = kSAI_BclkSourceMclkDiv; + config->masterSlave = kSAI_Master; +#if defined(FSL_FEATURE_SAI_HAS_MCR) && (FSL_FEATURE_SAI_HAS_MCR) + config->mclkOutputEnable = true; +#if !(defined(FSL_FEATURE_SAI_HAS_NO_MCR_MICS) && (FSL_FEATURE_SAI_HAS_NO_MCR_MICS)) + config->mclkSource = kSAI_MclkSourceSysclk; +#endif +#endif /* FSL_FEATURE_SAI_HAS_MCR */ + config->protocol = kSAI_BusI2S; + config->syncMode = kSAI_ModeSync; +} + +/*! + * brief Resets the SAI Tx. + * + * This function enables the software reset and FIFO reset of SAI Tx. After reset, clear the reset bit. + * + * param base SAI base pointer + */ +void SAI_TxReset(I2S_Type *base) +{ + /* Set the software reset and FIFO reset to clear internal state */ + base->TCSR = I2S_TCSR_SR_MASK | I2S_TCSR_FR_MASK; + + /* Clear software reset bit, this should be done by software */ + base->TCSR &= ~I2S_TCSR_SR_MASK; + + /* Reset all Tx register values */ + base->TCR2 = 0; + base->TCR3 = 0; + base->TCR4 = 0; + base->TCR5 = 0; + base->TMR = 0; +} + +/*! + * brief Resets the SAI Rx. + * + * This function enables the software reset and FIFO reset of SAI Rx. After reset, clear the reset bit. + * + * param base SAI base pointer + */ +void SAI_RxReset(I2S_Type *base) +{ + /* Set the software reset and FIFO reset to clear internal state */ + base->RCSR = I2S_RCSR_SR_MASK | I2S_RCSR_FR_MASK; + + /* Clear software reset bit, this should be done by software */ + base->RCSR &= ~I2S_RCSR_SR_MASK; + + /* Reset all Rx register values */ + base->RCR2 = 0; + base->RCR3 = 0; + base->RCR4 = 0; + base->RCR5 = 0; + base->RMR = 0; +} + +/*! + * brief Enables/disables the SAI Tx. + * + * param base SAI base pointer + * param enable True means enable SAI Tx, false means disable. + */ +void SAI_TxEnable(I2S_Type *base, bool enable) +{ + if (enable) + { + /* If clock is sync with Rx, should enable RE bit. */ + if (((base->TCR2 & I2S_TCR2_SYNC_MASK) >> I2S_TCR2_SYNC_SHIFT) == 0x1U) + { + base->RCSR = ((base->RCSR & 0xFFE3FFFFU) | I2S_RCSR_RE_MASK); + } + base->TCSR = ((base->TCSR & 0xFFE3FFFFU) | I2S_TCSR_TE_MASK); + /* Also need to clear the FIFO error flag before start */ + SAI_TxClearStatusFlags(base, kSAI_FIFOErrorFlag); + } + else + { + /* If Rx not in sync with Tx, then disable Tx, otherwise, shall not disable Tx */ + if (((base->RCR2 & I2S_RCR2_SYNC_MASK) >> I2S_RCR2_SYNC_SHIFT) != 0x1U) + { + /* Disable TE bit */ + base->TCSR = ((base->TCSR & 0xFFE3FFFFU) & (~I2S_TCSR_TE_MASK)); + } + } +} + +/*! + * brief Enables/disables the SAI Rx. + * + * param base SAI base pointer + * param enable True means enable SAI Rx, false means disable. + */ +void SAI_RxEnable(I2S_Type *base, bool enable) +{ + if (enable) + { + /* If clock is sync with Tx, should enable TE bit. */ + if (((base->RCR2 & I2S_RCR2_SYNC_MASK) >> I2S_RCR2_SYNC_SHIFT) == 0x1U) + { + base->TCSR = ((base->TCSR & 0xFFE3FFFFU) | I2S_TCSR_TE_MASK); + } + base->RCSR = ((base->RCSR & 0xFFE3FFFFU) | I2S_RCSR_RE_MASK); + /* Also need to clear the FIFO error flag before start */ + SAI_RxClearStatusFlags(base, kSAI_FIFOErrorFlag); + } + else + { + /* If Tx not in sync with Rx, then disable Rx, otherwise, shall not disable Rx */ + if (((base->TCR2 & I2S_TCR2_SYNC_MASK) >> I2S_TCR2_SYNC_SHIFT) != 0x1U) + { + /* Disable RE bit */ + base->RCSR = ((base->RCSR & 0xFFE3FFFFU) & (~I2S_RCSR_RE_MASK)); + } + } +} + +/*! + * brief Do software reset or FIFO reset . + * + * FIFO reset means clear all the data in the FIFO, and make the FIFO pointer both to 0. + * Software reset means clear the Tx internal logic, including the bit clock, frame count etc. But software + * reset will not clear any configuration registers like TCR1~TCR5. + * This function will also clear all the error flags such as FIFO error, sync error etc. + * + * param base SAI base pointer + * param resetType Reset type, FIFO reset or software reset + */ +void SAI_TxSoftwareReset(I2S_Type *base, sai_reset_type_t resetType) +{ + base->TCSR |= (uint32_t)resetType; + + /* Clear the software reset */ + base->TCSR &= ~I2S_TCSR_SR_MASK; +} + +/*! + * brief Do software reset or FIFO reset . + * + * FIFO reset means clear all the data in the FIFO, and make the FIFO pointer both to 0. + * Software reset means clear the Rx internal logic, including the bit clock, frame count etc. But software + * reset will not clear any configuration registers like RCR1~RCR5. + * This function will also clear all the error flags such as FIFO error, sync error etc. + * + * param base SAI base pointer + * param resetType Reset type, FIFO reset or software reset + */ +void SAI_RxSoftwareReset(I2S_Type *base, sai_reset_type_t resetType) +{ + base->RCSR |= (uint32_t)resetType; + + /* Clear the software reset */ + base->RCSR &= ~I2S_RCSR_SR_MASK; +} + +/*! + * brief Set the Tx channel FIFO enable mask. + * + * param base SAI base pointer + * param mask Channel enable mask, 0 means all channel FIFO disabled, 1 means channel 0 enabled, + * 3 means both channel 0 and channel 1 enabled. + */ +void SAI_TxSetChannelFIFOMask(I2S_Type *base, uint8_t mask) +{ + base->TCR3 &= ~I2S_TCR3_TCE_MASK; + base->TCR3 |= I2S_TCR3_TCE(mask); +} + +/*! + * brief Set the Rx channel FIFO enable mask. + * + * param base SAI base pointer + * param mask Channel enable mask, 0 means all channel FIFO disabled, 1 means channel 0 enabled, + * 3 means both channel 0 and channel 1 enabled. + */ +void SAI_RxSetChannelFIFOMask(I2S_Type *base, uint8_t mask) +{ + base->RCR3 &= ~I2S_RCR3_RCE_MASK; + base->RCR3 |= I2S_RCR3_RCE(mask); +} + +/*! + * brief Set the Tx data order. + * + * param base SAI base pointer + * param order Data order MSB or LSB + */ +void SAI_TxSetDataOrder(I2S_Type *base, sai_data_order_t order) +{ + uint32_t val = (base->TCR4) & (~I2S_TCR4_MF_MASK); + + val |= I2S_TCR4_MF(order); + base->TCR4 = val; +} + +/*! + * brief Set the Rx data order. + * + * param base SAI base pointer + * param order Data order MSB or LSB + */ +void SAI_RxSetDataOrder(I2S_Type *base, sai_data_order_t order) +{ + uint32_t val = (base->RCR4) & (~I2S_RCR4_MF_MASK); + + val |= I2S_RCR4_MF(order); + base->RCR4 = val; +} + +/*! + * brief Set the Tx data order. + * + * param base SAI base pointer + * param order Data order MSB or LSB + */ +void SAI_TxSetBitClockPolarity(I2S_Type *base, sai_clock_polarity_t polarity) +{ + uint32_t val = (base->TCR2) & (~I2S_TCR2_BCP_MASK); + + val |= I2S_TCR2_BCP(polarity); + base->TCR2 = val; +} + +/*! + * brief Set the Rx data order. + * + * param base SAI base pointer + * param order Data order MSB or LSB + */ +void SAI_RxSetBitClockPolarity(I2S_Type *base, sai_clock_polarity_t polarity) +{ + uint32_t val = (base->RCR2) & (~I2S_RCR2_BCP_MASK); + + val |= I2S_RCR2_BCP(polarity); + base->RCR2 = val; +} + +/*! + * brief Set the Tx data order. + * + * param base SAI base pointer + * param order Data order MSB or LSB + */ +void SAI_TxSetFrameSyncPolarity(I2S_Type *base, sai_clock_polarity_t polarity) +{ + uint32_t val = (base->TCR4) & (~I2S_TCR4_FSP_MASK); + + val |= I2S_TCR4_FSP(polarity); + base->TCR4 = val; +} + +/*! + * brief Set the Rx data order. + * + * param base SAI base pointer + * param order Data order MSB or LSB + */ +void SAI_RxSetFrameSyncPolarity(I2S_Type *base, sai_clock_polarity_t polarity) +{ + uint32_t val = (base->RCR4) & (~I2S_RCR4_FSP_MASK); + + val |= I2S_RCR4_FSP(polarity); + base->RCR4 = val; +} + +#if defined(FSL_FEATURE_SAI_HAS_FIFO_PACKING) && FSL_FEATURE_SAI_HAS_FIFO_PACKING +/*! + * brief Set Tx FIFO packing feature. + * + * param base SAI base pointer. + * param pack FIFO pack type. It is element of sai_fifo_packing_t. + */ +void SAI_TxSetFIFOPacking(I2S_Type *base, sai_fifo_packing_t pack) +{ + uint32_t val = base->TCR4; + + val &= ~I2S_TCR4_FPACK_MASK; + val |= I2S_TCR4_FPACK(pack); + base->TCR4 = val; +} + +/*! + * brief Set Rx FIFO packing feature. + * + * param base SAI base pointer. + * param pack FIFO pack type. It is element of sai_fifo_packing_t. + */ +void SAI_RxSetFIFOPacking(I2S_Type *base, sai_fifo_packing_t pack) +{ + uint32_t val = base->RCR4; + + val &= ~I2S_RCR4_FPACK_MASK; + val |= I2S_RCR4_FPACK(pack); + base->RCR4 = val; +} +#endif /* FSL_FEATURE_SAI_HAS_FIFO_PACKING */ + +/*! + * brief Transmitter bit clock rate configurations. + * + * param base SAI base pointer. + * param sourceClockHz, bit clock source frequency. + * param sampleRate audio data sample rate. + * param bitWidth, audio data bitWidth. + * param channelNumbers, audio channel numbers. + */ +void SAI_TxSetBitClockRate( + I2S_Type *base, uint32_t sourceClockHz, uint32_t sampleRate, uint32_t bitWidth, uint32_t channelNumbers) +{ + uint32_t tcr2 = base->TCR2; + uint32_t bitClockDiv = 0; + uint32_t bitClockFreq = sampleRate * bitWidth * channelNumbers; + + assert(sourceClockHz >= bitClockFreq); + + tcr2 &= ~I2S_TCR2_DIV_MASK; + /* need to check the divided bclk, if bigger than target, then divider need to re-calculate. */ + bitClockDiv = sourceClockHz / bitClockFreq; + /* for the condition where the source clock is smaller than target bclk */ + if (bitClockDiv == 0U) + { + bitClockDiv++; + } + /* recheck the divider if properly or not, to make sure output blck not bigger than target*/ + if ((sourceClockHz / bitClockDiv) > bitClockFreq) + { + bitClockDiv++; + } + +#if defined(FSL_FEATURE_SAI_HAS_BCLK_BYPASS) && (FSL_FEATURE_SAI_HAS_BCLK_BYPASS) + /* if bclk same with MCLK, bypass the divider */ + if (bitClockDiv == 1U) + { + tcr2 |= I2S_TCR2_BYP_MASK; + } + else +#endif + { + tcr2 |= I2S_TCR2_DIV(bitClockDiv / 2U - 1UL); + } + + base->TCR2 = tcr2; +} + +/*! + * brief Receiver bit clock rate configurations. + * + * param base SAI base pointer. + * param sourceClockHz, bit clock source frequency. + * param sampleRate audio data sample rate. + * param bitWidth, audio data bitWidth. + * param channelNumbers, audio channel numbers. + */ +void SAI_RxSetBitClockRate( + I2S_Type *base, uint32_t sourceClockHz, uint32_t sampleRate, uint32_t bitWidth, uint32_t channelNumbers) +{ + uint32_t rcr2 = base->RCR2; + uint32_t bitClockDiv = 0; + uint32_t bitClockFreq = sampleRate * bitWidth * channelNumbers; + + assert(sourceClockHz >= bitClockFreq); + + rcr2 &= ~I2S_RCR2_DIV_MASK; + /* need to check the divided bclk, if bigger than target, then divider need to re-calculate. */ + bitClockDiv = sourceClockHz / bitClockFreq; + /* for the condition where the source clock is smaller than target bclk */ + if (bitClockDiv == 0U) + { + bitClockDiv++; + } + /* recheck the divider if properly or not, to make sure output blck not bigger than target*/ + if ((sourceClockHz / bitClockDiv) > bitClockFreq) + { + bitClockDiv++; + } + +#if defined(FSL_FEATURE_SAI_HAS_BCLK_BYPASS) && (FSL_FEATURE_SAI_HAS_BCLK_BYPASS) + /* if bclk same with MCLK, bypass the divider */ + if (bitClockDiv == 1U) + { + rcr2 |= I2S_RCR2_BYP_MASK; + } + else +#endif + { + rcr2 |= I2S_RCR2_DIV(bitClockDiv / 2U - 1UL); + } + + base->RCR2 = rcr2; +} + +/*! + * brief Transmitter Bit clock configurations. + * + * param base SAI base pointer. + * param masterSlave master or slave. + * param config bit clock other configurations, can be NULL in slave mode. + */ +void SAI_TxSetBitclockConfig(I2S_Type *base, sai_master_slave_t masterSlave, sai_bit_clock_t *config) +{ + uint32_t tcr2 = base->TCR2; + + if ((masterSlave == kSAI_Master) || (masterSlave == kSAI_Bclk_Master_FrameSync_Slave)) + { + assert(config != NULL); + + tcr2 &= ~(I2S_TCR2_BCD_MASK | I2S_TCR2_BCP_MASK | I2S_TCR2_BCI_MASK | I2S_TCR2_BCS_MASK | I2S_TCR2_MSEL_MASK); + tcr2 |= I2S_TCR2_BCD(1U) | I2S_TCR2_BCP(config->bclkPolarity) | I2S_TCR2_BCI(config->bclkInputDelay) | + I2S_TCR2_BCS(config->bclkSrcSwap) | I2S_TCR2_MSEL(config->bclkSource); + } + else + { + tcr2 &= ~(I2S_TCR2_BCD_MASK); + tcr2 |= I2S_TCR2_BCP(config->bclkPolarity); + } + + base->TCR2 = tcr2; +} + +/*! + * brief Receiver Bit clock configurations. + * + * param base SAI base pointer. + * param masterSlave master or slave. + * param config bit clock other configurations, can be NULL in slave mode. + */ +void SAI_RxSetBitclockConfig(I2S_Type *base, sai_master_slave_t masterSlave, sai_bit_clock_t *config) +{ + uint32_t rcr2 = base->RCR2; + + if ((masterSlave == kSAI_Master) || (masterSlave == kSAI_Bclk_Master_FrameSync_Slave)) + { + assert(config != NULL); + + rcr2 &= ~(I2S_RCR2_BCD_MASK | I2S_RCR2_BCP_MASK | I2S_RCR2_BCI_MASK | I2S_RCR2_BCS_MASK | I2S_RCR2_MSEL_MASK); + rcr2 |= I2S_RCR2_BCD(1U) | I2S_RCR2_BCP(config->bclkPolarity) | I2S_RCR2_BCI(config->bclkInputDelay) | + I2S_RCR2_BCS(config->bclkSrcSwap) | I2S_RCR2_MSEL(config->bclkSource); + } + else + { + rcr2 &= ~(I2S_RCR2_BCD_MASK); + rcr2 |= I2S_RCR2_BCP(config->bclkPolarity); + } + + base->RCR2 = rcr2; +} + +#if (defined(FSL_FEATURE_SAI_HAS_MCR) && (FSL_FEATURE_SAI_HAS_MCR)) || \ + (defined(FSL_FEATURE_SAI_HAS_MCLKDIV_REGISTER) && (FSL_FEATURE_SAI_HAS_MCLKDIV_REGISTER)) +/*! + * brief Master clock configurations. + * + * param base SAI base pointer. + * param config master clock configurations. + */ +void SAI_SetMasterClockConfig(I2S_Type *base, sai_master_clock_t *config) +{ + assert(config != NULL); + +#if defined(FSL_FEATURE_SAI_HAS_MCR) && (FSL_FEATURE_SAI_HAS_MCR) + uint32_t val = 0; +#if !(defined(FSL_FEATURE_SAI_HAS_NO_MCR_MICS) && (FSL_FEATURE_SAI_HAS_NO_MCR_MICS)) + /* Master clock source setting */ + val = (base->MCR & ~I2S_MCR_MICS_MASK); + base->MCR = (val | I2S_MCR_MICS(config->mclkSource)); +#endif + + /* Configure Master clock output enable */ + val = (base->MCR & ~I2S_MCR_MOE_MASK); + base->MCR = (val | I2S_MCR_MOE(config->mclkOutputEnable)); +#endif /* FSL_FEATURE_SAI_HAS_MCR */ + +#if ((defined(FSL_FEATURE_SAI_HAS_MCLKDIV_REGISTER) && (FSL_FEATURE_SAI_HAS_MCLKDIV_REGISTER)) || \ + (defined(FSL_FEATURE_SAI_HAS_MCR_MCLK_POST_DIV) && (FSL_FEATURE_SAI_HAS_MCR_MCLK_POST_DIV))) + /* Check if master clock divider enabled, then set master clock divider */ + if (config->mclkOutputEnable) + { + SAI_SetMasterClockDivider(base, config->mclkHz, config->mclkSourceClkHz); + } +#endif /* FSL_FEATURE_SAI_HAS_MCLKDIV_REGISTER */ +} +#endif + +#if FSL_SAI_HAS_FIFO_EXTEND_FEATURE +/*! + * brief SAI transmitter fifo configurations. + * + * param base SAI base pointer. + * param config fifo configurations. + */ +void SAI_TxSetFifoConfig(I2S_Type *base, sai_fifo_t *config) +{ + assert(config != NULL); +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((config->fifoWatermark == 0U) || + (config->fifoWatermark > (uint8_t)((uint32_t)FSL_FEATURE_SAI_FIFO_COUNTn(base)))) + { + config->fifoWatermark = (uint8_t)((uint32_t)FSL_FEATURE_SAI_FIFO_COUNTn(base) / 2U); + } +#endif + + uint32_t tcr4 = base->TCR4; + +#if defined(FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) && FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE + tcr4 &= ~I2S_TCR4_FCOMB_MASK; + tcr4 |= I2S_TCR4_FCOMB(config->fifoCombine); +#endif + +#if defined(FSL_FEATURE_SAI_HAS_FIFO_FUNCTION_AFTER_ERROR) && FSL_FEATURE_SAI_HAS_FIFO_FUNCTION_AFTER_ERROR + tcr4 &= ~I2S_TCR4_FCONT_MASK; + /* ERR05144: not set FCONT = 1 when TMR > 0, the transmit shift register may not load correctly that will cause TX + * not work */ + if (base->TMR == 0U) + { + tcr4 |= I2S_TCR4_FCONT(config->fifoContinueOneError); + } +#endif + +#if defined(FSL_FEATURE_SAI_HAS_FIFO_PACKING) && FSL_FEATURE_SAI_HAS_FIFO_PACKING + tcr4 &= ~I2S_TCR4_FPACK_MASK; + tcr4 |= I2S_TCR4_FPACK(config->fifoPacking); +#endif + + base->TCR4 = tcr4; + +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + base->TCR1 = (base->TCR1 & (~I2S_TCR1_TFW_MASK)) | I2S_TCR1_TFW(config->fifoWatermark); +#endif +} + +/*! + * brief SAI receiver fifo configurations. + * + * param base SAI base pointer. + * param config fifo configurations. + */ +void SAI_RxSetFifoConfig(I2S_Type *base, sai_fifo_t *config) +{ + assert(config != NULL); +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((config->fifoWatermark == 0U) || + (config->fifoWatermark > (uint8_t)((uint32_t)FSL_FEATURE_SAI_FIFO_COUNTn(base)))) + { + config->fifoWatermark = (uint8_t)((uint32_t)FSL_FEATURE_SAI_FIFO_COUNTn(base) / 2U); + } +#endif + uint32_t rcr4 = base->RCR4; + +#if defined(FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) && FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE + rcr4 &= ~I2S_RCR4_FCOMB_MASK; + rcr4 |= I2S_RCR4_FCOMB(config->fifoCombine); +#endif + +#if defined(FSL_FEATURE_SAI_HAS_FIFO_FUNCTION_AFTER_ERROR) && FSL_FEATURE_SAI_HAS_FIFO_FUNCTION_AFTER_ERROR + rcr4 &= ~I2S_RCR4_FCONT_MASK; + rcr4 |= I2S_RCR4_FCONT(config->fifoContinueOneError); +#endif + +#if defined(FSL_FEATURE_SAI_HAS_FIFO_PACKING) && FSL_FEATURE_SAI_HAS_FIFO_PACKING + rcr4 &= ~I2S_RCR4_FPACK_MASK; + rcr4 |= I2S_RCR4_FPACK(config->fifoPacking); +#endif + + base->RCR4 = rcr4; + +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + base->RCR1 = (base->RCR1 & (~I2S_RCR1_RFW_MASK)) | I2S_RCR1_RFW(config->fifoWatermark); +#endif +} +#endif + +/*! + * brief SAI transmitter Frame sync configurations. + * + * param base SAI base pointer. + * param masterSlave master or slave. + * param config frame sync configurations, can be NULL in slave mode. + */ +void SAI_TxSetFrameSyncConfig(I2S_Type *base, sai_master_slave_t masterSlave, sai_frame_sync_t *config) +{ + assert(config != NULL); + assert((config->frameSyncWidth - 1UL) <= (I2S_TCR4_SYWD_MASK >> I2S_TCR4_SYWD_SHIFT)); + + uint32_t tcr4 = base->TCR4; + + tcr4 &= ~(I2S_TCR4_FSE_MASK | I2S_TCR4_FSP_MASK | I2S_TCR4_FSD_MASK | I2S_TCR4_SYWD_MASK); + +#if defined(FSL_FEATURE_SAI_HAS_ON_DEMAND_MODE) && FSL_FEATURE_SAI_HAS_ON_DEMAND_MODE + tcr4 &= ~I2S_TCR4_ONDEM_MASK; + tcr4 |= I2S_TCR4_ONDEM(config->frameSyncGenerateOnDemand); +#endif + + tcr4 |= + I2S_TCR4_FSE(config->frameSyncEarly) | I2S_TCR4_FSP(config->frameSyncPolarity) | + I2S_TCR4_FSD(((masterSlave == kSAI_Master) || (masterSlave == kSAI_Bclk_Slave_FrameSync_Master)) ? 1UL : 0U) | + I2S_TCR4_SYWD(config->frameSyncWidth - 1UL); + + base->TCR4 = tcr4; +} + +/*! + * brief SAI receiver Frame sync configurations. + * + * param base SAI base pointer. + * param masterSlave master or slave. + * param config frame sync configurations, can be NULL in slave mode. + */ +void SAI_RxSetFrameSyncConfig(I2S_Type *base, sai_master_slave_t masterSlave, sai_frame_sync_t *config) +{ + assert(config != NULL); + assert((config->frameSyncWidth - 1UL) <= (I2S_RCR4_SYWD_MASK >> I2S_RCR4_SYWD_SHIFT)); + + uint32_t rcr4 = base->RCR4; + + rcr4 &= ~(I2S_RCR4_FSE_MASK | I2S_RCR4_FSP_MASK | I2S_RCR4_FSD_MASK | I2S_RCR4_SYWD_MASK); + +#if defined(FSL_FEATURE_SAI_HAS_ON_DEMAND_MODE) && FSL_FEATURE_SAI_HAS_ON_DEMAND_MODE + rcr4 &= ~I2S_RCR4_ONDEM_MASK; + rcr4 |= I2S_RCR4_ONDEM(config->frameSyncGenerateOnDemand); +#endif + + rcr4 |= + I2S_RCR4_FSE(config->frameSyncEarly) | I2S_RCR4_FSP(config->frameSyncPolarity) | + I2S_RCR4_FSD(((masterSlave == kSAI_Master) || (masterSlave == kSAI_Bclk_Slave_FrameSync_Master)) ? 1UL : 0U) | + I2S_RCR4_SYWD(config->frameSyncWidth - 1UL); + + base->RCR4 = rcr4; +} + +/*! + * brief SAI transmitter Serial data configurations. + * + * param base SAI base pointer. + * param config serial data configurations. + */ +void SAI_TxSetSerialDataConfig(I2S_Type *base, sai_serial_data_t *config) +{ + assert(config != NULL); + + uint32_t tcr4 = base->TCR4; + + base->TCR5 = I2S_TCR5_WNW(config->dataWordNLength - 1UL) | I2S_TCR5_W0W(config->dataWord0Length - 1UL) | + I2S_TCR5_FBT(config->dataFirstBitShifted - 1UL); + base->TMR = config->dataMaskedWord; +#if defined(FSL_FEATURE_SAI_HAS_FIFO_FUNCTION_AFTER_ERROR) && FSL_FEATURE_SAI_HAS_FIFO_FUNCTION_AFTER_ERROR + /* ERR05144: not set FCONT = 1 when TMR > 0, the transmit shift register may not load correctly that will cause TX + * not work */ + if (config->dataMaskedWord > 0U) + { + tcr4 &= ~I2S_TCR4_FCONT_MASK; + } +#endif + tcr4 &= ~(I2S_TCR4_FRSZ_MASK | I2S_TCR4_MF_MASK); + tcr4 |= I2S_TCR4_FRSZ(config->dataWordNum - 1UL) | I2S_TCR4_MF(config->dataOrder); + +#if defined(FSL_FEATURE_SAI_HAS_CHANNEL_MODE) && FSL_FEATURE_SAI_HAS_CHANNEL_MODE + tcr4 &= ~I2S_TCR4_CHMOD_MASK; + tcr4 |= I2S_TCR4_CHMOD(config->dataMode); +#endif + + base->TCR4 = tcr4; +} + +/*! + * @brief SAI receiver Serial data configurations. + * + * @param base SAI base pointer. + * @param config serial data configurations. + */ +void SAI_RxSetSerialDataConfig(I2S_Type *base, sai_serial_data_t *config) +{ + assert(config != NULL); + + uint32_t rcr4 = base->RCR4; + + base->RCR5 = I2S_RCR5_WNW(config->dataWordNLength - 1UL) | I2S_RCR5_W0W(config->dataWord0Length - 1UL) | + I2S_RCR5_FBT(config->dataFirstBitShifted - 1UL); + base->RMR = config->dataMaskedWord; + + rcr4 &= ~(I2S_RCR4_FRSZ_MASK | I2S_RCR4_MF_MASK); + rcr4 |= I2S_RCR4_FRSZ(config->dataWordNum - 1uL) | I2S_RCR4_MF(config->dataOrder); + + base->RCR4 = rcr4; +} + +/*! + * brief SAI transmitter configurations. + * + * param base SAI base pointer. + * param config transmitter configurations. + */ +void SAI_TxSetConfig(I2S_Type *base, sai_transceiver_t *config) +{ + assert(config != NULL); + assert(FSL_FEATURE_SAI_CHANNEL_COUNTn(base) != -1); + + uint8_t i = 0U; + uint32_t val = 0U; + uint8_t channelNums = 0U; + + /* reset transmitter */ + SAI_TxReset(base); + + /* if channel mask is not set, then format->channel must be set, + use it to get channel mask value */ + if (config->channelMask == 0U) + { + config->channelMask = 1U << config->startChannel; + } + + for (i = 0U; i < (uint32_t)FSL_FEATURE_SAI_CHANNEL_COUNTn(base); i++) + { + if (IS_SAI_FLAG_SET(1UL << i, config->channelMask)) + { + channelNums++; + config->endChannel = i; + } + } + + for (i = 0U; i < (uint32_t)FSL_FEATURE_SAI_CHANNEL_COUNTn(base); i++) + { + if (IS_SAI_FLAG_SET((1UL << i), config->channelMask)) + { + config->startChannel = i; + break; + } + } + + config->channelNums = channelNums; +#if defined(FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) && (FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) + /* make sure combine mode disabled while multipe channel is used */ + if (config->channelNums > 1U) + { + base->TCR4 &= ~I2S_TCR4_FCOMB_MASK; + } +#endif + + /* Set data channel */ + base->TCR3 &= ~I2S_TCR3_TCE_MASK; + base->TCR3 |= I2S_TCR3_TCE(config->channelMask); + + if (config->syncMode == kSAI_ModeAsync) + { + val = base->TCR2; + val &= ~I2S_TCR2_SYNC_MASK; + base->TCR2 = (val | I2S_TCR2_SYNC(0U)); + } + if (config->syncMode == kSAI_ModeSync) + { + val = base->TCR2; + val &= ~I2S_TCR2_SYNC_MASK; + base->TCR2 = (val | I2S_TCR2_SYNC(1U)); + /* If sync with Rx, should set Rx to async mode */ + val = base->RCR2; + val &= ~I2S_RCR2_SYNC_MASK; + base->RCR2 = (val | I2S_RCR2_SYNC(0U)); + } +#if defined(FSL_FEATURE_SAI_HAS_SYNC_WITH_ANOTHER_SAI) && (FSL_FEATURE_SAI_HAS_SYNC_WITH_ANOTHER_SAI) + if (config->syncMode == kSAI_ModeSyncWithOtherTx) + { + val = base->TCR2; + val &= ~I2S_TCR2_SYNC_MASK; + base->TCR2 = (val | I2S_TCR2_SYNC(2U)); + } + if (config->syncMode == kSAI_ModeSyncWithOtherRx) + { + val = base->TCR2; + val &= ~I2S_TCR2_SYNC_MASK; + base->TCR2 = (val | I2S_TCR2_SYNC(3U)); + } +#endif /* FSL_FEATURE_SAI_HAS_SYNC_WITH_ANOTHER_SAI */ + + /* bit clock configurations */ + SAI_TxSetBitclockConfig(base, config->masterSlave, &config->bitClock); + /* serial data configurations */ + SAI_TxSetSerialDataConfig(base, &config->serialData); + /* frame sync configurations */ + SAI_TxSetFrameSyncConfig(base, config->masterSlave, &config->frameSync); +#if FSL_SAI_HAS_FIFO_EXTEND_FEATURE + /* fifo configurations */ + SAI_TxSetFifoConfig(base, &config->fifo); +#endif +} + +/*! + * brief SAI transmitter transfer configurations. + * + * This function initializes the TX, include bit clock, frame sync, master clock, serial data and fifo configurations. + * + * param base SAI base pointer. + * param handle SAI handle pointer. + * param config tranmitter configurations. + */ +void SAI_TransferTxSetConfig(I2S_Type *base, sai_handle_t *handle, sai_transceiver_t *config) +{ + assert(handle != NULL); + assert(config != NULL); + assert(config->channelNums <= (uint32_t)FSL_FEATURE_SAI_CHANNEL_COUNTn(base)); + + handle->bitWidth = config->frameSync.frameSyncWidth; +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((config->fifo.fifoWatermark == 0U) || + (config->fifo.fifoWatermark > (uint8_t)((uint32_t)FSL_FEATURE_SAI_FIFO_COUNTn(base)))) + { + config->fifo.fifoWatermark = (uint8_t)((uint32_t)FSL_FEATURE_SAI_FIFO_COUNTn(base) / 2U); + } + handle->watermark = config->fifo.fifoWatermark; +#endif + + /* transmitter configurations */ + SAI_TxSetConfig(base, config); + + handle->channel = config->startChannel; + /* used for multi channel */ + handle->channelMask = config->channelMask; + handle->channelNums = config->channelNums; + handle->endChannel = config->endChannel; +} + +/*! + * brief SAI receiver configurations. + * + * param base SAI base pointer. + * param config transmitter configurations. + */ +void SAI_RxSetConfig(I2S_Type *base, sai_transceiver_t *config) +{ + assert(config != NULL); + assert(FSL_FEATURE_SAI_CHANNEL_COUNTn(base) != -1); + + uint8_t i = 0U; + uint32_t val = 0U; + uint8_t channelNums = 0U; + + /* reset receiver */ + SAI_RxReset(base); + + /* if channel mask is not set, then format->channel must be set, + use it to get channel mask value */ + if (config->channelMask == 0U) + { + config->channelMask = 1U << config->startChannel; + } + + for (i = 0U; i < (uint32_t)FSL_FEATURE_SAI_CHANNEL_COUNTn(base); i++) + { + if (IS_SAI_FLAG_SET((1UL << i), config->channelMask)) + { + channelNums++; + config->endChannel = i; + } + } + + for (i = 0U; i < (uint32_t)FSL_FEATURE_SAI_CHANNEL_COUNTn(base); i++) + { + if (IS_SAI_FLAG_SET((1UL << i), config->channelMask)) + { + config->startChannel = i; + break; + } + } + + config->channelNums = channelNums; +#if defined(FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) && (FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) + /* make sure combine mode disabled while multipe channel is used */ + if (config->channelNums > 1U) + { + base->RCR4 &= ~I2S_RCR4_FCOMB_MASK; + } +#endif + + /* Set data channel */ + base->RCR3 &= ~I2S_RCR3_RCE_MASK; + base->RCR3 |= I2S_RCR3_RCE(config->channelMask); + + /* Set Sync mode */ + if (config->syncMode == kSAI_ModeAsync) + { + val = base->RCR2; + val &= ~I2S_RCR2_SYNC_MASK; + base->RCR2 = (val | I2S_RCR2_SYNC(0U)); + } + if (config->syncMode == kSAI_ModeSync) + { + val = base->RCR2; + val &= ~I2S_RCR2_SYNC_MASK; + base->RCR2 = (val | I2S_RCR2_SYNC(1U)); + /* If sync with Tx, should set Tx to async mode */ + val = base->TCR2; + val &= ~I2S_TCR2_SYNC_MASK; + base->TCR2 = (val | I2S_TCR2_SYNC(0U)); + } +#if defined(FSL_FEATURE_SAI_HAS_SYNC_WITH_ANOTHER_SAI) && (FSL_FEATURE_SAI_HAS_SYNC_WITH_ANOTHER_SAI) + if (config->syncMode == kSAI_ModeSyncWithOtherTx) + { + val = base->RCR2; + val &= ~I2S_RCR2_SYNC_MASK; + base->RCR2 = (val | I2S_RCR2_SYNC(2U)); + } + if (config->syncMode == kSAI_ModeSyncWithOtherRx) + { + val = base->RCR2; + val &= ~I2S_RCR2_SYNC_MASK; + base->RCR2 = (val | I2S_RCR2_SYNC(3U)); + } +#endif /* FSL_FEATURE_SAI_HAS_SYNC_WITH_ANOTHER_SAI */ + + /* bit clock configurations */ + SAI_RxSetBitclockConfig(base, config->masterSlave, &config->bitClock); + /* serial data configurations */ + SAI_RxSetSerialDataConfig(base, &config->serialData); + /* frame sync configurations */ + SAI_RxSetFrameSyncConfig(base, config->masterSlave, &config->frameSync); +#if FSL_SAI_HAS_FIFO_EXTEND_FEATURE + /* fifo configurations */ + SAI_RxSetFifoConfig(base, &config->fifo); +#endif +} + +/*! + * brief SAI receiver transfer configurations. + * + * This function initializes the TX, include bit clock, frame sync, master clock, serial data and fifo configurations. + * + * param base SAI base pointer. + * param handle SAI handle pointer. + * param config tranmitter configurations. + */ +void SAI_TransferRxSetConfig(I2S_Type *base, sai_handle_t *handle, sai_transceiver_t *config) +{ + assert(handle != NULL); + assert(config != NULL); + + handle->bitWidth = config->frameSync.frameSyncWidth; +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((config->fifo.fifoWatermark == 0U) || + (config->fifo.fifoWatermark > (uint8_t)((uint32_t)FSL_FEATURE_SAI_FIFO_COUNTn(base)))) + { + config->fifo.fifoWatermark = (uint8_t)((uint32_t)FSL_FEATURE_SAI_FIFO_COUNTn(base) / 2U); + } + handle->watermark = config->fifo.fifoWatermark; +#endif + + /* receiver configurations */ + SAI_RxSetConfig(base, config); + + handle->channel = config->startChannel; + /* used for multi channel */ + handle->channelMask = config->channelMask; + handle->channelNums = config->channelNums; + handle->endChannel = config->endChannel; +} + +/*! + * brief Get classic I2S mode configurations. + * + * param config transceiver configurations. + * param bitWidth audio data bitWidth. + * param mode audio data channel. + * param saiChannelMask channel mask value to enable. + */ +void SAI_GetClassicI2SConfig(sai_transceiver_t *config, + sai_word_width_t bitWidth, + sai_mono_stereo_t mode, + uint32_t saiChannelMask) +{ + SAI_GetCommonConfig(config, bitWidth, mode, saiChannelMask); +} + +/*! + * brief Get left justified mode configurations. + * + * param config transceiver configurations. + * param bitWidth audio data bitWidth. + * param mode audio data channel. + * param saiChannelMask channel mask value to enable. + */ +void SAI_GetLeftJustifiedConfig(sai_transceiver_t *config, + sai_word_width_t bitWidth, + sai_mono_stereo_t mode, + uint32_t saiChannelMask) +{ + assert(NULL != config); + assert(saiChannelMask != 0U); + + SAI_GetCommonConfig(config, bitWidth, mode, saiChannelMask); + + config->frameSync.frameSyncEarly = false; + config->frameSync.frameSyncPolarity = kSAI_PolarityActiveHigh; +} + +/*! + * brief Get right justified mode configurations. + * + * param config transceiver configurations. + * param bitWidth audio data bitWidth. + * param mode audio data channel. + * param saiChannelMask channel mask value to enable. + */ +void SAI_GetRightJustifiedConfig(sai_transceiver_t *config, + sai_word_width_t bitWidth, + sai_mono_stereo_t mode, + uint32_t saiChannelMask) +{ + assert(NULL != config); + assert(saiChannelMask != 0U); + + SAI_GetCommonConfig(config, bitWidth, mode, saiChannelMask); + + config->frameSync.frameSyncEarly = false; + config->frameSync.frameSyncPolarity = kSAI_PolarityActiveHigh; +} + +/*! + * brief Get DSP mode configurations. + * + * note DSP mode is also called PCM mode which support MODE A and MODE B, + * DSP/PCM MODE A configuration flow. RX is similiar but uses SAI_RxSetConfig instead of SAI_TxSetConfig: + * code + * SAI_GetDSPConfig(config, kSAI_FrameSyncLenOneBitClk, bitWidth, kSAI_Stereo, channelMask) + * config->frameSync.frameSyncEarly = true; + * SAI_TxSetConfig(base, config) + * endcode + * + * DSP/PCM MODE B configuration flow for TX. RX is similiar but uses SAI_RxSetConfig instead of SAI_TxSetConfig: + * code + * SAI_GetDSPConfig(config, kSAI_FrameSyncLenOneBitClk, bitWidth, kSAI_Stereo, channelMask) + * SAI_TxSetConfig(base, config) + * endcode + * + * param config transceiver configurations. + * param frameSyncWidth length of frame sync. + * param bitWidth audio data bitWidth. + * param mode audio data channel. + * param saiChannelMask mask value of the channel to enable. + */ +void SAI_GetDSPConfig(sai_transceiver_t *config, + sai_frame_sync_len_t frameSyncWidth, + sai_word_width_t bitWidth, + sai_mono_stereo_t mode, + uint32_t saiChannelMask) +{ + assert(NULL != config); + assert(saiChannelMask != 0U); + + SAI_GetCommonConfig(config, bitWidth, mode, saiChannelMask); + + /* frame sync default configurations */ + switch (frameSyncWidth) + { + case kSAI_FrameSyncLenOneBitClk: + config->frameSync.frameSyncWidth = 1U; + break; + default: + assert(false); + break; + } + config->frameSync.frameSyncEarly = false; + config->frameSync.frameSyncPolarity = kSAI_PolarityActiveHigh; +} + +/*! + * brief Get TDM mode configurations. + * + * param config transceiver configurations. + * param bitWidth audio data bitWidth. + * param mode audio data channel. + * param saiChannelMask channel mask value to enable. + */ +void SAI_GetTDMConfig(sai_transceiver_t *config, + sai_frame_sync_len_t frameSyncWidth, + sai_word_width_t bitWidth, + uint32_t dataWordNum, + uint32_t saiChannelMask) +{ + assert(NULL != config); + assert(saiChannelMask != 0U); + assert(dataWordNum <= 32U); + + SAI_GetCommonConfig(config, bitWidth, kSAI_Stereo, saiChannelMask); + + /* frame sync default configurations */ + switch (frameSyncWidth) + { + case kSAI_FrameSyncLenOneBitClk: + config->frameSync.frameSyncWidth = 1U; + break; + case kSAI_FrameSyncLenPerWordWidth: + break; + default: + assert(false); + break; + } + config->frameSync.frameSyncEarly = false; + config->frameSync.frameSyncPolarity = kSAI_PolarityActiveHigh; + config->serialData.dataWordNum = (uint8_t)dataWordNum; +} + +/*! + * brief Configures the SAI Tx audio format. + * + * deprecated Do not use this function. It has been superceded by @ref SAI_TxSetConfig + * + * The audio format can be changed at run-time. This function configures the sample rate and audio data + * format to be transferred. + * + * param base SAI base pointer. + * param format Pointer to the SAI audio data format structure. + * param mclkSourceClockHz SAI master clock source frequency in Hz. + * param bclkSourceClockHz SAI bit clock source frequency in Hz. If the bit clock source is a master + * clock, this value should equal the masterClockHz. + */ +void SAI_TxSetFormat(I2S_Type *base, + sai_transfer_format_t *format, + uint32_t mclkSourceClockHz, + uint32_t bclkSourceClockHz) +{ + assert(FSL_FEATURE_SAI_CHANNEL_COUNTn(base) != -1); + + uint32_t bclk = 0; + uint32_t val = 0; + uint8_t i = 0U, channelNums = 0U; + uint32_t divider = 0U; + + if (format->isFrameSyncCompact) + { + bclk = format->sampleRate_Hz * format->bitWidth * (format->stereo == kSAI_Stereo ? 2U : 1U); + val = (base->TCR4 & (~I2S_TCR4_SYWD_MASK)); + val |= I2S_TCR4_SYWD(format->bitWidth - 1U); + base->TCR4 = val; + } + else + { + bclk = format->sampleRate_Hz * 32U * 2U; + } + +/* Compute the mclk */ +#if defined(FSL_FEATURE_SAI_HAS_MCLKDIV_REGISTER) && (FSL_FEATURE_SAI_HAS_MCLKDIV_REGISTER) + /* Check if master clock divider enabled, then set master clock divider */ + if (IS_SAI_FLAG_SET(base->MCR, I2S_MCR_MOE_MASK)) + { + SAI_SetMasterClockDivider(base, format->masterClockHz, mclkSourceClockHz); + } +#endif /* FSL_FEATURE_SAI_HAS_MCLKDIV_REGISTER */ + + /* Set bclk if needed */ + if (IS_SAI_FLAG_SET(base->TCR2, I2S_TCR2_BCD_MASK)) + { + base->TCR2 &= ~I2S_TCR2_DIV_MASK; + /* need to check the divided bclk, if bigger than target, then divider need to re-calculate. */ + divider = bclkSourceClockHz / bclk; + /* for the condition where the source clock is smaller than target bclk */ + if (divider == 0U) + { + divider++; + } + /* recheck the divider if properly or not, to make sure output blck not bigger than target*/ + if ((bclkSourceClockHz / divider) > bclk) + { + divider++; + } + +#if defined(FSL_FEATURE_SAI_HAS_BCLK_BYPASS) && (FSL_FEATURE_SAI_HAS_BCLK_BYPASS) + /* if bclk same with MCLK, bypass the divider */ + if (divider == 1U) + { + base->TCR2 |= I2S_TCR2_BYP_MASK; + } + else +#endif + { + base->TCR2 |= I2S_TCR2_DIV(divider / 2U - 1U); + } + } + + /* Set bitWidth */ + val = (format->isFrameSyncCompact) ? (format->bitWidth - 1U) : 31U; + if (format->protocol == kSAI_BusRightJustified) + { + base->TCR5 = I2S_TCR5_WNW(val) | I2S_TCR5_W0W(val) | I2S_TCR5_FBT(val); + } + else + { + if (IS_SAI_FLAG_SET(base->TCR4, I2S_TCR4_MF_MASK)) + { + base->TCR5 = I2S_TCR5_WNW(val) | I2S_TCR5_W0W(val) | I2S_TCR5_FBT(format->bitWidth - 1UL); + } + else + { + base->TCR5 = I2S_TCR5_WNW(val) | I2S_TCR5_W0W(val) | I2S_TCR5_FBT(0); + } + } + + /* Set mono or stereo */ + base->TMR = (uint32_t)format->stereo; + + /* if channel mask is not set, then format->channel must be set, + use it to get channel mask value */ + if (format->channelMask == 0U) + { + format->channelMask = 1U << format->channel; + } + + /* if channel nums is not set, calculate it here according to channelMask*/ + for (i = 0U; i < (uint32_t)FSL_FEATURE_SAI_CHANNEL_COUNTn(base); i++) + { + if (IS_SAI_FLAG_SET((1UL << i), format->channelMask)) + { + channelNums++; + format->endChannel = i; + } + } + + for (i = 0U; i < (uint32_t)FSL_FEATURE_SAI_CHANNEL_COUNTn(base); i++) + { + if (IS_SAI_FLAG_SET((1UL << i), format->channelMask)) + { + format->channel = i; + break; + } + } + + format->channelNums = channelNums; +#if defined(FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) && (FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) + /* make sure combine mode disabled while multipe channel is used */ + if (format->channelNums > 1U) + { + base->TCR4 &= ~I2S_TCR4_FCOMB_MASK; + } +#endif + + /* Set data channel */ + base->TCR3 &= ~I2S_TCR3_TCE_MASK; + base->TCR3 |= I2S_TCR3_TCE(format->channelMask); + +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + /* Set watermark */ + base->TCR1 = format->watermark; +#endif /* FSL_FEATURE_SAI_HAS_FIFO */ +} + +/*! + * brief Configures the SAI Rx audio format. + * + * deprecated Do not use this function. It has been superceded by @ref SAI_RxSetConfig + * + * The audio format can be changed at run-time. This function configures the sample rate and audio data + * format to be transferred. + * + * param base SAI base pointer. + * param format Pointer to the SAI audio data format structure. + * param mclkSourceClockHz SAI master clock source frequency in Hz. + * param bclkSourceClockHz SAI bit clock source frequency in Hz. If the bit clock source is a master + * clock, this value should equal the masterClockHz. + */ +void SAI_RxSetFormat(I2S_Type *base, + sai_transfer_format_t *format, + uint32_t mclkSourceClockHz, + uint32_t bclkSourceClockHz) +{ + assert(FSL_FEATURE_SAI_CHANNEL_COUNTn(base) != -1); + + uint32_t bclk = 0; + uint32_t val = 0; + uint8_t i = 0U, channelNums = 0U; + uint32_t divider = 0U; + + if (format->isFrameSyncCompact) + { + bclk = format->sampleRate_Hz * format->bitWidth * (format->stereo == kSAI_Stereo ? 2U : 1U); + val = (base->RCR4 & (~I2S_RCR4_SYWD_MASK)); + val |= I2S_RCR4_SYWD(format->bitWidth - 1U); + base->RCR4 = val; + } + else + { + bclk = format->sampleRate_Hz * 32U * 2U; + } + +/* Compute the mclk */ +#if defined(FSL_FEATURE_SAI_HAS_MCLKDIV_REGISTER) && (FSL_FEATURE_SAI_HAS_MCLKDIV_REGISTER) + /* Check if master clock divider enabled */ + if (IS_SAI_FLAG_SET(base->MCR, I2S_MCR_MOE_MASK)) + { + SAI_SetMasterClockDivider(base, format->masterClockHz, mclkSourceClockHz); + } +#endif /* FSL_FEATURE_SAI_HAS_MCLKDIV_REGISTER */ + + /* Set bclk if needed */ + if (IS_SAI_FLAG_SET(base->RCR2, I2S_RCR2_BCD_MASK)) + { + base->RCR2 &= ~I2S_RCR2_DIV_MASK; + /* need to check the divided bclk, if bigger than target, then divider need to re-calculate. */ + divider = bclkSourceClockHz / bclk; + /* for the condition where the source clock is smaller than target bclk */ + if (divider == 0U) + { + divider++; + } + /* recheck the divider if properly or not, to make sure output blck not bigger than target*/ + if ((bclkSourceClockHz / divider) > bclk) + { + divider++; + } +#if defined(FSL_FEATURE_SAI_HAS_BCLK_BYPASS) && (FSL_FEATURE_SAI_HAS_BCLK_BYPASS) + /* if bclk same with MCLK, bypass the divider */ + if (divider == 1U) + { + base->RCR2 |= I2S_RCR2_BYP_MASK; + } + else +#endif + { + base->RCR2 |= I2S_RCR2_DIV(divider / 2U - 1U); + } + } + + /* Set bitWidth */ + val = (format->isFrameSyncCompact) ? (format->bitWidth - 1U) : 31U; + if (format->protocol == kSAI_BusRightJustified) + { + base->RCR5 = I2S_RCR5_WNW(val) | I2S_RCR5_W0W(val) | I2S_RCR5_FBT(val); + } + else + { + if (IS_SAI_FLAG_SET(base->RCR4, I2S_RCR4_MF_MASK)) + { + base->RCR5 = I2S_RCR5_WNW(val) | I2S_RCR5_W0W(val) | I2S_RCR5_FBT(format->bitWidth - 1UL); + } + else + { + base->RCR5 = I2S_RCR5_WNW(val) | I2S_RCR5_W0W(val) | I2S_RCR5_FBT(0UL); + } + } + + /* Set mono or stereo */ + base->RMR = (uint32_t)format->stereo; + + /* if channel mask is not set, then format->channel must be set, + use it to get channel mask value */ + if (format->channelMask == 0U) + { + format->channelMask = 1U << format->channel; + } + + /* if channel nums is not set, calculate it here according to channelMask*/ + for (i = 0U; i < (uint32_t)FSL_FEATURE_SAI_CHANNEL_COUNTn(base); i++) + { + if (IS_SAI_FLAG_SET((1UL << i), format->channelMask)) + { + channelNums++; + format->endChannel = i; + } + } + + for (i = 0U; i < (uint32_t)FSL_FEATURE_SAI_CHANNEL_COUNTn(base); i++) + { + if (IS_SAI_FLAG_SET((1UL << i), format->channelMask)) + { + format->channel = i; + break; + } + } + + format->channelNums = channelNums; + +#if defined(FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) && (FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) + /* make sure combine mode disabled while multipe channel is used */ + if (format->channelNums > 1U) + { + base->RCR4 &= ~I2S_RCR4_FCOMB_MASK; + } +#endif + + /* Set data channel */ + base->RCR3 &= ~I2S_RCR3_RCE_MASK; + /* enable all the channel */ + base->RCR3 |= I2S_RCR3_RCE(format->channelMask); + +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + /* Set watermark */ + base->RCR1 = format->watermark; +#endif /* FSL_FEATURE_SAI_HAS_FIFO */ +} + +/*! + * brief Sends data using a blocking method. + * + * note This function blocks by polling until data is ready to be sent. + * + * param base SAI base pointer. + * param channel Data channel used. + * param bitWidth How many bits in an audio word; usually 8/16/24/32 bits. + * param buffer Pointer to the data to be written. + * param size Bytes to be written. + */ +void SAI_WriteBlocking(I2S_Type *base, uint32_t channel, uint32_t bitWidth, uint8_t *buffer, uint32_t size) +{ + uint32_t i = 0; + uint32_t bytesPerWord = bitWidth / 8U; +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + bytesPerWord = (((uint32_t)FSL_FEATURE_SAI_FIFO_COUNTn(base) - base->TCR1) * bytesPerWord); +#endif + + while (i < size) + { + /* Wait until it can write data */ + while (!(IS_SAI_FLAG_SET(base->TCSR, I2S_TCSR_FWF_MASK))) + { + } + + SAI_WriteNonBlocking(base, channel, 1UL << channel, channel, (uint8_t)bitWidth, buffer, bytesPerWord); + buffer = (uint8_t *)((uintptr_t)buffer + bytesPerWord); + i += bytesPerWord; + } + + /* Wait until the last data is sent */ + while (!(IS_SAI_FLAG_SET(base->TCSR, I2S_TCSR_FWF_MASK))) + { + } +} + +/*! + * brief Sends data to multi channel using a blocking method. + * + * note This function blocks by polling until data is ready to be sent. + * + * param base SAI base pointer. + * param channel Data channel used. + * param channelMask channel mask. + * param bitWidth How many bits in an audio word; usually 8/16/24/32 bits. + * param buffer Pointer to the data to be written. + * param size Bytes to be written. + */ +void SAI_WriteMultiChannelBlocking( + I2S_Type *base, uint32_t channel, uint32_t channelMask, uint32_t bitWidth, uint8_t *buffer, uint32_t size) +{ + assert(FSL_FEATURE_SAI_CHANNEL_COUNTn(base) != -1); + + uint32_t i = 0, j = 0; + uint32_t bytesPerWord = bitWidth / 8U; + uint32_t channelNums = 0U, endChannel = 0U; + +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + bytesPerWord = (((uint32_t)FSL_FEATURE_SAI_FIFO_COUNTn(base) - base->TCR1) * bytesPerWord); +#endif + + for (i = 0U; (i < (uint32_t)FSL_FEATURE_SAI_CHANNEL_COUNTn(base)); i++) + { + if (IS_SAI_FLAG_SET((1UL << i), channelMask)) + { + channelNums++; + endChannel = i; + } + } + + bytesPerWord *= channelNums; + + while (j < size) + { + /* Wait until it can write data */ + while (!(IS_SAI_FLAG_SET(base->TCSR, I2S_TCSR_FWF_MASK))) + { + } + + SAI_WriteNonBlocking(base, channel, channelMask, endChannel, (uint8_t)bitWidth, buffer, + bytesPerWord * channelNums); + buffer = (uint8_t *)((uintptr_t)buffer + bytesPerWord * channelNums); + j += bytesPerWord * channelNums; + } + + /* Wait until the last data is sent */ + while (!(IS_SAI_FLAG_SET(base->TCSR, I2S_TCSR_FWF_MASK))) + { + } +} + +/*! + * brief Receives multi channel data using a blocking method. + * + * note This function blocks by polling until data is ready to be sent. + * + * param base SAI base pointer. + * param channel Data channel used. + * param channelMask channel mask. + * param bitWidth How many bits in an audio word; usually 8/16/24/32 bits. + * param buffer Pointer to the data to be read. + * param size Bytes to be read. + */ +void SAI_ReadMultiChannelBlocking( + I2S_Type *base, uint32_t channel, uint32_t channelMask, uint32_t bitWidth, uint8_t *buffer, uint32_t size) +{ + assert(FSL_FEATURE_SAI_CHANNEL_COUNTn(base) != -1); + + uint32_t i = 0, j = 0; + uint32_t bytesPerWord = bitWidth / 8U; + uint32_t channelNums = 0U, endChannel = 0U; +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + bytesPerWord = base->RCR1 * bytesPerWord; +#endif + for (i = 0U; (i < (uint32_t)FSL_FEATURE_SAI_CHANNEL_COUNTn(base)); i++) + { + if (IS_SAI_FLAG_SET((1UL << i), channelMask)) + { + channelNums++; + endChannel = i; + } + } + + bytesPerWord *= channelNums; + + while (j < size) + { + /* Wait until data is received */ + while (!(IS_SAI_FLAG_SET(base->RCSR, I2S_RCSR_FWF_MASK))) + { + } + + SAI_ReadNonBlocking(base, channel, channelMask, endChannel, (uint8_t)bitWidth, buffer, + bytesPerWord * channelNums); + buffer = (uint8_t *)((uintptr_t)buffer + bytesPerWord * channelNums); + j += bytesPerWord * channelNums; + } +} + +/*! + * brief Receives data using a blocking method. + * + * note This function blocks by polling until data is ready to be sent. + * + * param base SAI base pointer. + * param channel Data channel used. + * param bitWidth How many bits in an audio word; usually 8/16/24/32 bits. + * param buffer Pointer to the data to be read. + * param size Bytes to be read. + */ +void SAI_ReadBlocking(I2S_Type *base, uint32_t channel, uint32_t bitWidth, uint8_t *buffer, uint32_t size) +{ + uint32_t i = 0; + uint32_t bytesPerWord = bitWidth / 8U; +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + bytesPerWord = base->RCR1 * bytesPerWord; +#endif + + while (i < size) + { + /* Wait until data is received */ + while (!(IS_SAI_FLAG_SET(base->RCSR, I2S_RCSR_FWF_MASK))) + { + } + + SAI_ReadNonBlocking(base, channel, 1UL << channel, channel, (uint8_t)bitWidth, buffer, bytesPerWord); + buffer = (uint8_t *)((uintptr_t)buffer + bytesPerWord); + i += bytesPerWord; + } +} + +/*! + * brief Initializes the SAI Tx handle. + * + * This function initializes the Tx handle for the SAI Tx transactional APIs. Call + * this function once to get the handle initialized. + * + * param base SAI base pointer + * param handle SAI handle pointer. + * param callback Pointer to the user callback function. + * param userData User parameter passed to the callback function + */ +void SAI_TransferTxCreateHandle(I2S_Type *base, sai_handle_t *handle, sai_transfer_callback_t callback, void *userData) +{ + assert(handle != NULL); + + /* Zero the handle */ + (void)memset(handle, 0, sizeof(*handle)); + + s_saiHandle[SAI_GetInstance(base)][0] = handle; + + handle->callback = callback; + handle->userData = userData; + handle->base = base; + + /* Set the isr pointer */ + s_saiTxIsr = SAI_TransferTxHandleIRQ; + + /* Enable Tx irq */ + (void)EnableIRQ(s_saiTxIRQ[SAI_GetInstance(base)]); +} + +/*! + * brief Initializes the SAI Rx handle. + * + * This function initializes the Rx handle for the SAI Rx transactional APIs. Call + * this function once to get the handle initialized. + * + * param base SAI base pointer. + * param handle SAI handle pointer. + * param callback Pointer to the user callback function. + * param userData User parameter passed to the callback function. + */ +void SAI_TransferRxCreateHandle(I2S_Type *base, sai_handle_t *handle, sai_transfer_callback_t callback, void *userData) +{ + assert(handle != NULL); + + /* Zero the handle */ + (void)memset(handle, 0, sizeof(*handle)); + + s_saiHandle[SAI_GetInstance(base)][1] = handle; + + handle->callback = callback; + handle->userData = userData; + handle->base = base; + + /* Set the isr pointer */ + s_saiRxIsr = SAI_TransferRxHandleIRQ; + + /* Enable Rx irq */ + (void)EnableIRQ(s_saiRxIRQ[SAI_GetInstance(base)]); +} + +/*! + * brief Configures the SAI Tx audio format. + * + * deprecated Do not use this function. It has been superceded by @ref SAI_TransferTxSetConfig + * + * The audio format can be changed at run-time. This function configures the sample rate and audio data + * format to be transferred. + * + * param base SAI base pointer. + * param handle SAI handle pointer. + * param format Pointer to the SAI audio data format structure. + * param mclkSourceClockHz SAI master clock source frequency in Hz. + * param bclkSourceClockHz SAI bit clock source frequency in Hz. If a bit clock source is a master + * clock, this value should equal the masterClockHz in format. + * return Status of this function. Return value is the status_t. + */ +status_t SAI_TransferTxSetFormat(I2S_Type *base, + sai_handle_t *handle, + sai_transfer_format_t *format, + uint32_t mclkSourceClockHz, + uint32_t bclkSourceClockHz) +{ + assert(handle != NULL); + + if ((bclkSourceClockHz < format->sampleRate_Hz) +#if defined(FSL_FEATURE_SAI_HAS_MCLKDIV_REGISTER) && (FSL_FEATURE_SAI_HAS_MCLKDIV_REGISTER) + || (mclkSourceClockHz < format->sampleRate_Hz) +#endif + ) + { + return kStatus_InvalidArgument; + } + + /* Copy format to handle */ + handle->bitWidth = (uint8_t)format->bitWidth; +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + handle->watermark = format->watermark; +#endif + + SAI_TxSetFormat(base, format, mclkSourceClockHz, bclkSourceClockHz); + + handle->channel = format->channel; + /* used for multi channel */ + handle->channelMask = format->channelMask; + handle->channelNums = format->channelNums; + handle->endChannel = format->endChannel; + + return kStatus_Success; +} + +/*! + * brief Configures the SAI Rx audio format. + * + * deprecated Do not use this function. It has been superceded by @ref SAI_TransferRxSetConfig + * + * The audio format can be changed at run-time. This function configures the sample rate and audio data + * format to be transferred. + * + * param base SAI base pointer. + * param handle SAI handle pointer. + * param format Pointer to the SAI audio data format structure. + * param mclkSourceClockHz SAI master clock source frequency in Hz. + * param bclkSourceClockHz SAI bit clock source frequency in Hz. If a bit clock source is a master + * clock, this value should equal the masterClockHz in format. + * return Status of this function. Return value is one of status_t. + */ +status_t SAI_TransferRxSetFormat(I2S_Type *base, + sai_handle_t *handle, + sai_transfer_format_t *format, + uint32_t mclkSourceClockHz, + uint32_t bclkSourceClockHz) +{ + assert(handle != NULL); + + if ((bclkSourceClockHz < format->sampleRate_Hz) +#if defined(FSL_FEATURE_SAI_HAS_MCLKDIV_REGISTER) && (FSL_FEATURE_SAI_HAS_MCLKDIV_REGISTER) + || (mclkSourceClockHz < format->sampleRate_Hz) +#endif + ) + { + return kStatus_InvalidArgument; + } + + /* Copy format to handle */ + handle->bitWidth = (uint8_t)format->bitWidth; +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + handle->watermark = format->watermark; +#endif + + SAI_RxSetFormat(base, format, mclkSourceClockHz, bclkSourceClockHz); + + handle->channel = format->channel; + /* used for multi channel */ + handle->channelMask = format->channelMask; + handle->channelNums = format->channelNums; + handle->endChannel = format->endChannel; + + return kStatus_Success; +} + +/*! + * brief Performs an interrupt non-blocking send transfer on SAI. + * + * note This API returns immediately after the transfer initiates. + * Call the SAI_TxGetTransferStatusIRQ to poll the transfer status and check whether + * the transfer is finished. If the return status is not kStatus_SAI_Busy, the transfer + * is finished. + * + * param base SAI base pointer. + * param handle Pointer to the sai_handle_t structure which stores the transfer state. + * param xfer Pointer to the sai_transfer_t structure. + * retval kStatus_Success Successfully started the data receive. + * retval kStatus_SAI_TxBusy Previous receive still not finished. + * retval kStatus_InvalidArgument The input parameter is invalid. + */ +status_t SAI_TransferSendNonBlocking(I2S_Type *base, sai_handle_t *handle, sai_transfer_t *xfer) +{ + assert(handle != NULL); + assert(handle->channelNums <= (uint32_t)FSL_FEATURE_SAI_CHANNEL_COUNTn(base)); + + /* Check if the queue is full */ + if (handle->saiQueue[handle->queueUser].data != NULL) + { + return kStatus_SAI_QueueFull; + } + + /* Add into queue */ + handle->transferSize[handle->queueUser] = xfer->dataSize; + handle->saiQueue[handle->queueUser].data = xfer->data; + handle->saiQueue[handle->queueUser].dataSize = xfer->dataSize; + handle->queueUser = (handle->queueUser + 1U) % (uint8_t)SAI_XFER_QUEUE_SIZE; + + /* Set the state to busy */ + handle->state = (uint32_t)kSAI_Busy; + + /* Enable interrupt */ +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + /* Use FIFO request interrupt and fifo error*/ + SAI_TxEnableInterrupts(base, I2S_TCSR_FEIE_MASK | I2S_TCSR_FRIE_MASK); +#else + SAI_TxEnableInterrupts(base, I2S_TCSR_FEIE_MASK | I2S_TCSR_FWIE_MASK); +#endif /* FSL_FEATURE_SAI_HAS_FIFO */ + + /* Enable Tx transfer */ + SAI_TxEnable(base, true); + + return kStatus_Success; +} + +/*! + * brief Performs an interrupt non-blocking receive transfer on SAI. + * + * note This API returns immediately after the transfer initiates. + * Call the SAI_RxGetTransferStatusIRQ to poll the transfer status and check whether + * the transfer is finished. If the return status is not kStatus_SAI_Busy, the transfer + * is finished. + * + * param base SAI base pointer + * param handle Pointer to the sai_handle_t structure which stores the transfer state. + * param xfer Pointer to the sai_transfer_t structure. + * retval kStatus_Success Successfully started the data receive. + * retval kStatus_SAI_RxBusy Previous receive still not finished. + * retval kStatus_InvalidArgument The input parameter is invalid. + */ +status_t SAI_TransferReceiveNonBlocking(I2S_Type *base, sai_handle_t *handle, sai_transfer_t *xfer) +{ + assert(handle != NULL); + assert(handle->channelNums <= (uint32_t)FSL_FEATURE_SAI_CHANNEL_COUNTn(base)); + + /* Check if the queue is full */ + if (handle->saiQueue[handle->queueUser].data != NULL) + { + return kStatus_SAI_QueueFull; + } + + /* Add into queue */ + handle->transferSize[handle->queueUser] = xfer->dataSize; + handle->saiQueue[handle->queueUser].data = xfer->data; + handle->saiQueue[handle->queueUser].dataSize = xfer->dataSize; + handle->queueUser = (handle->queueUser + 1U) % (uint8_t)SAI_XFER_QUEUE_SIZE; + + /* Set state to busy */ + handle->state = (uint32_t)kSAI_Busy; + +/* Enable interrupt */ +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + /* Use FIFO request interrupt and fifo error*/ + SAI_RxEnableInterrupts(base, I2S_TCSR_FEIE_MASK | I2S_TCSR_FRIE_MASK); +#else + SAI_RxEnableInterrupts(base, I2S_TCSR_FEIE_MASK | I2S_TCSR_FWIE_MASK); +#endif /* FSL_FEATURE_SAI_HAS_FIFO */ + + /* Enable Rx transfer */ + SAI_RxEnable(base, true); + + return kStatus_Success; +} + +/*! + * brief Gets a set byte count. + * + * param base SAI base pointer. + * param handle Pointer to the sai_handle_t structure which stores the transfer state. + * param count Bytes count sent. + * retval kStatus_Success Succeed get the transfer count. + * retval kStatus_NoTransferInProgress There is not a non-blocking transaction currently in progress. + */ +status_t SAI_TransferGetSendCount(I2S_Type *base, sai_handle_t *handle, size_t *count) +{ + assert(handle != NULL); + + status_t status = kStatus_Success; + uint32_t queueDriverIndex = handle->queueDriver; + + if (handle->state != (uint32_t)kSAI_Busy) + { + status = kStatus_NoTransferInProgress; + } + else + { + *count = (handle->transferSize[queueDriverIndex] - handle->saiQueue[queueDriverIndex].dataSize); + } + + return status; +} + +/*! + * brief Gets a received byte count. + * + * param base SAI base pointer. + * param handle Pointer to the sai_handle_t structure which stores the transfer state. + * param count Bytes count received. + * retval kStatus_Success Succeed get the transfer count. + * retval kStatus_NoTransferInProgress There is not a non-blocking transaction currently in progress. + */ +status_t SAI_TransferGetReceiveCount(I2S_Type *base, sai_handle_t *handle, size_t *count) +{ + assert(handle != NULL); + + status_t status = kStatus_Success; + uint32_t queueDriverIndex = handle->queueDriver; + + if (handle->state != (uint32_t)kSAI_Busy) + { + status = kStatus_NoTransferInProgress; + } + else + { + *count = (handle->transferSize[queueDriverIndex] - handle->saiQueue[queueDriverIndex].dataSize); + } + + return status; +} + +/*! + * brief Aborts the current send. + * + * note This API can be called any time when an interrupt non-blocking transfer initiates + * to abort the transfer early. + * + * param base SAI base pointer. + * param handle Pointer to the sai_handle_t structure which stores the transfer state. + */ +void SAI_TransferAbortSend(I2S_Type *base, sai_handle_t *handle) +{ + assert(handle != NULL); + + /* Stop Tx transfer and disable interrupt */ + SAI_TxEnable(base, false); +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + /* Use FIFO request interrupt and fifo error */ + SAI_TxDisableInterrupts(base, I2S_TCSR_FEIE_MASK | I2S_TCSR_FRIE_MASK); +#else + SAI_TxDisableInterrupts(base, I2S_TCSR_FEIE_MASK | I2S_TCSR_FWIE_MASK); +#endif /* FSL_FEATURE_SAI_HAS_FIFO */ + + handle->state = (uint32_t)kSAI_Idle; + + /* Clear the queue */ + (void)memset(handle->saiQueue, 0, sizeof(sai_transfer_t) * (uint8_t)SAI_XFER_QUEUE_SIZE); + handle->queueDriver = 0; + handle->queueUser = 0; +} + +/*! + * brief Aborts the current IRQ receive. + * + * note This API can be called when an interrupt non-blocking transfer initiates + * to abort the transfer early. + * + * param base SAI base pointer + * param handle Pointer to the sai_handle_t structure which stores the transfer state. + */ +void SAI_TransferAbortReceive(I2S_Type *base, sai_handle_t *handle) +{ + assert(handle != NULL); + + /* Stop Tx transfer and disable interrupt */ + SAI_RxEnable(base, false); +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + /* Use FIFO request interrupt and fifo error */ + SAI_RxDisableInterrupts(base, I2S_TCSR_FEIE_MASK | I2S_TCSR_FRIE_MASK); +#else + SAI_RxDisableInterrupts(base, I2S_TCSR_FEIE_MASK | I2S_TCSR_FWIE_MASK); +#endif /* FSL_FEATURE_SAI_HAS_FIFO */ + + handle->state = (uint32_t)kSAI_Idle; + + /* Clear the queue */ + (void)memset(handle->saiQueue, 0, sizeof(sai_transfer_t) * (uint8_t)SAI_XFER_QUEUE_SIZE); + handle->queueDriver = 0; + handle->queueUser = 0; +} + +/*! + * brief Terminate all SAI send. + * + * This function will clear all transfer slots buffered in the sai queue. If users only want to abort the + * current transfer slot, please call SAI_TransferAbortSend. + * + * param base SAI base pointer. + * param handle SAI eDMA handle pointer. + */ +void SAI_TransferTerminateSend(I2S_Type *base, sai_handle_t *handle) +{ + assert(handle != NULL); + + /* Abort the current transfer */ + SAI_TransferAbortSend(base, handle); + + /* Clear all the internal information */ + (void)memset(handle->saiQueue, 0, sizeof(handle->saiQueue)); + (void)memset(handle->transferSize, 0, sizeof(handle->transferSize)); + + handle->queueUser = 0U; + handle->queueDriver = 0U; +} + +/*! + * brief Terminate all SAI receive. + * + * This function will clear all transfer slots buffered in the sai queue. If users only want to abort the + * current transfer slot, please call SAI_TransferAbortReceive. + * + * param base SAI base pointer. + * param handle SAI eDMA handle pointer. + */ +void SAI_TransferTerminateReceive(I2S_Type *base, sai_handle_t *handle) +{ + assert(handle != NULL); + + /* Abort the current transfer */ + SAI_TransferAbortReceive(base, handle); + + /* Clear all the internal information */ + (void)memset(handle->saiQueue, 0, sizeof(handle->saiQueue)); + (void)memset(handle->transferSize, 0, sizeof(handle->transferSize)); + + handle->queueUser = 0U; + handle->queueDriver = 0U; +} + +/*! + * brief Tx interrupt handler. + * + * param base SAI base pointer. + * param handle Pointer to the sai_handle_t structure. + */ +void SAI_TransferTxHandleIRQ(I2S_Type *base, sai_handle_t *handle) +{ + assert(handle != NULL); + + uint8_t *buffer = handle->saiQueue[handle->queueDriver].data; + uint32_t dataSize = (handle->bitWidth / 8UL) * handle->channelNums; + + /* Handle Error */ + if (IS_SAI_FLAG_SET(base->TCSR, I2S_TCSR_FEF_MASK)) + { + /* Clear FIFO error flag to continue transfer */ + SAI_TxClearStatusFlags(base, I2S_TCSR_FEF_MASK); + + /* Reset FIFO for safety */ + SAI_TxSoftwareReset(base, kSAI_ResetTypeFIFO); + + /* Call the callback */ + if (handle->callback != NULL) + { + (handle->callback)(base, handle, kStatus_SAI_TxError, handle->userData); + } + } + +/* Handle transfer */ +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if (IS_SAI_FLAG_SET(base->TCSR, I2S_TCSR_FRF_MASK)) + { + /* Judge if the data need to transmit is less than space */ + size_t size = MIN((handle->saiQueue[handle->queueDriver].dataSize), + (size_t)(((uint32_t)FSL_FEATURE_SAI_FIFO_COUNTn(base) - handle->watermark) * dataSize)); + + /* Copy the data from sai buffer to FIFO */ + SAI_WriteNonBlocking(base, handle->channel, handle->channelMask, handle->endChannel, handle->bitWidth, buffer, + size); + + /* Update the internal counter */ + handle->saiQueue[handle->queueDriver].dataSize -= size; + handle->saiQueue[handle->queueDriver].data = (uint8_t *)((uintptr_t)buffer + size); + } +#else + if (IS_SAI_FLAG_SET(base->TCSR, I2S_TCSR_FWF_MASK)) + { + size_t size = MIN((handle->saiQueue[handle->queueDriver].dataSize), dataSize); + + SAI_WriteNonBlocking(base, handle->channel, handle->channelMask, handle->endChannel, handle->bitWidth, buffer, + size); + + /* Update internal counter */ + handle->saiQueue[handle->queueDriver].dataSize -= size; + handle->saiQueue[handle->queueDriver].data = (uint8_t *)((uintptr_t)buffer + size); + } +#endif /* FSL_FEATURE_SAI_HAS_FIFO */ + + /* If finished a block, call the callback function */ + if (handle->saiQueue[handle->queueDriver].dataSize == 0U) + { + (void)memset(&handle->saiQueue[handle->queueDriver], 0, sizeof(sai_transfer_t)); + handle->queueDriver = (handle->queueDriver + 1U) % (uint8_t)SAI_XFER_QUEUE_SIZE; + if (handle->callback != NULL) + { + (handle->callback)(base, handle, kStatus_SAI_TxIdle, handle->userData); + } + } + + /* If all data finished, just stop the transfer */ + if (handle->saiQueue[handle->queueDriver].data == NULL) + { + SAI_TransferAbortSend(base, handle); + } +} + +/*! + * brief Tx interrupt handler. + * + * param base SAI base pointer. + * param handle Pointer to the sai_handle_t structure. + */ +void SAI_TransferRxHandleIRQ(I2S_Type *base, sai_handle_t *handle) +{ + assert(handle != NULL); + + uint8_t *buffer = handle->saiQueue[handle->queueDriver].data; + uint32_t dataSize = (handle->bitWidth / 8UL) * handle->channelNums; + + /* Handle Error */ + if (IS_SAI_FLAG_SET(base->RCSR, I2S_RCSR_FEF_MASK)) + { + /* Clear FIFO error flag to continue transfer */ + SAI_RxClearStatusFlags(base, I2S_TCSR_FEF_MASK); + + /* Reset FIFO for safety */ + SAI_RxSoftwareReset(base, kSAI_ResetTypeFIFO); + + /* Call the callback */ + if (handle->callback != NULL) + { + (handle->callback)(base, handle, kStatus_SAI_RxError, handle->userData); + } + } + +/* Handle transfer */ +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if (IS_SAI_FLAG_SET(base->RCSR, I2S_RCSR_FRF_MASK)) + { + /* Judge if the data need to transmit is less than space */ + size_t size = MIN((handle->saiQueue[handle->queueDriver].dataSize), handle->watermark * dataSize); + + /* Copy the data from sai buffer to FIFO */ + SAI_ReadNonBlocking(base, handle->channel, handle->channelMask, handle->endChannel, handle->bitWidth, buffer, + size); + + /* Update the internal counter */ + handle->saiQueue[handle->queueDriver].dataSize -= size; + handle->saiQueue[handle->queueDriver].data = (uint8_t *)((uintptr_t)buffer + size); + } +#else + if (IS_SAI_FLAG_SET(base->RCSR, I2S_RCSR_FWF_MASK)) + { + size_t size = MIN((handle->saiQueue[handle->queueDriver].dataSize), dataSize); + + SAI_ReadNonBlocking(base, handle->channel, handle->channelMask, handle->endChannel, handle->bitWidth, buffer, + size); + + /* Update internal state */ + handle->saiQueue[handle->queueDriver].dataSize -= size; + handle->saiQueue[handle->queueDriver].data = (uint8_t *)((uintptr_t)buffer + size); + } +#endif /* FSL_FEATURE_SAI_HAS_FIFO */ + + /* If finished a block, call the callback function */ + if (handle->saiQueue[handle->queueDriver].dataSize == 0U) + { + (void)memset(&handle->saiQueue[handle->queueDriver], 0, sizeof(sai_transfer_t)); + handle->queueDriver = (handle->queueDriver + 1U) % (uint8_t)SAI_XFER_QUEUE_SIZE; + if (handle->callback != NULL) + { + (handle->callback)(base, handle, kStatus_SAI_RxIdle, handle->userData); + } + } + + /* If all data finished, just stop the transfer */ + if (handle->saiQueue[handle->queueDriver].data == NULL) + { + SAI_TransferAbortReceive(base, handle); + } +} + +#if defined(I2S0) +void I2S0_DriverIRQHandler(void); +void I2S0_DriverIRQHandler(void) +{ +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[0][1] != NULL) && SAI_RxGetEnabledInterruptStatus(I2S0, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[0][1] != NULL) && SAI_RxGetEnabledInterruptStatus(I2S0, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiRxIsr(I2S0, s_saiHandle[0][1]); + } +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[0][0] != NULL) && SAI_TxGetEnabledInterruptStatus(I2S0, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[0][0] != NULL) && SAI_TxGetEnabledInterruptStatus(I2S0, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiTxIsr(I2S0, s_saiHandle[0][0]); + } + SDK_ISR_EXIT_BARRIER; +} + +void I2S0_Tx_DriverIRQHandler(void); +void I2S0_Tx_DriverIRQHandler(void) +{ + assert(s_saiHandle[0][0] != NULL); + s_saiTxIsr(I2S0, s_saiHandle[0][0]); + SDK_ISR_EXIT_BARRIER; +} + +void I2S0_Rx_DriverIRQHandler(void); +void I2S0_Rx_DriverIRQHandler(void) +{ + assert(s_saiHandle[0][1] != NULL); + s_saiRxIsr(I2S0, s_saiHandle[0][1]); + SDK_ISR_EXIT_BARRIER; +} +#endif /* I2S0*/ + +#if defined(I2S1) +void I2S1_DriverIRQHandler(void); +void I2S1_DriverIRQHandler(void) +{ +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[1][1] != NULL) && SAI_RxGetEnabledInterruptStatus(I2S1, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[1][1] != NULL) && SAI_RxGetEnabledInterruptStatus(I2S1, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiRxIsr(I2S1, s_saiHandle[1][1]); + } + +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[1][0] != NULL) && SAI_TxGetEnabledInterruptStatus(I2S1, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[1][0] != NULL) && SAI_TxGetEnabledInterruptStatus(I2S1, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiTxIsr(I2S1, s_saiHandle[1][0]); + } + SDK_ISR_EXIT_BARRIER; +} + +void I2S1_Tx_DriverIRQHandler(void); +void I2S1_Tx_DriverIRQHandler(void) +{ + assert(s_saiHandle[1][0] != NULL); + s_saiTxIsr(I2S1, s_saiHandle[1][0]); + SDK_ISR_EXIT_BARRIER; +} + +void I2S1_Rx_DriverIRQHandler(void); +void I2S1_Rx_DriverIRQHandler(void) +{ + assert(s_saiHandle[1][1] != NULL); + s_saiRxIsr(I2S1, s_saiHandle[1][1]); + SDK_ISR_EXIT_BARRIER; +} +#endif /* I2S1*/ + +#if defined(I2S2) +void I2S2_DriverIRQHandler(void); +void I2S2_DriverIRQHandler(void) +{ +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[2][1] != NULL) && SAI_RxGetEnabledInterruptStatus(I2S2, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[2][1] != NULL) && SAI_RxGetEnabledInterruptStatus(I2S2, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiRxIsr(I2S2, s_saiHandle[2][1]); + } + +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[2][0] != NULL) && SAI_TxGetEnabledInterruptStatus(I2S2, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[2][0] != NULL) && SAI_TxGetEnabledInterruptStatus(I2S2, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiTxIsr(I2S2, s_saiHandle[2][0]); + } + SDK_ISR_EXIT_BARRIER; +} + +void I2S2_Tx_DriverIRQHandler(void); +void I2S2_Tx_DriverIRQHandler(void) +{ + assert(s_saiHandle[2][0] != NULL); + s_saiTxIsr(I2S2, s_saiHandle[2][0]); + SDK_ISR_EXIT_BARRIER; +} + +void I2S2_Rx_DriverIRQHandler(void); +void I2S2_Rx_DriverIRQHandler(void) +{ + assert(s_saiHandle[2][1] != NULL); + s_saiRxIsr(I2S2, s_saiHandle[2][1]); + SDK_ISR_EXIT_BARRIER; +} +#endif /* I2S2*/ + +#if defined(I2S3) +void I2S3_DriverIRQHandler(void); +void I2S3_DriverIRQHandler(void) +{ +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[3][1] != NULL) && SAI_RxGetEnabledInterruptStatus(I2S3, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[3][1] != NULL) && SAI_RxGetEnabledInterruptStatus(I2S3, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiRxIsr(I2S3, s_saiHandle[3][1]); + } +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[3][0] != NULL) && SAI_TxGetEnabledInterruptStatus(I2S3, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[3][0] != NULL) && SAI_TxGetEnabledInterruptStatus(I2S3, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiTxIsr(I2S3, s_saiHandle[3][0]); + } + SDK_ISR_EXIT_BARRIER; +} + +void I2S3_Tx_DriverIRQHandler(void); +void I2S3_Tx_DriverIRQHandler(void) +{ + assert(s_saiHandle[3][0] != NULL); + s_saiTxIsr(I2S3, s_saiHandle[3][0]); + SDK_ISR_EXIT_BARRIER; +} + +void I2S3_Rx_DriverIRQHandler(void); +void I2S3_Rx_DriverIRQHandler(void) +{ + assert(s_saiHandle[3][1] != NULL); + s_saiRxIsr(I2S3, s_saiHandle[3][1]); + SDK_ISR_EXIT_BARRIER; +} +#endif /* I2S3*/ + +#if defined(I2S4) +void I2S4_DriverIRQHandler(void); +void I2S4_DriverIRQHandler(void) +{ +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[4][1] != NULL) && SAI_RxGetEnabledInterruptStatus(I2S4, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[4][1] != NULL) && SAI_RxGetEnabledInterruptStatus(I2S4, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiRxIsr(I2S4, s_saiHandle[4][1]); + } + +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[4][0] != NULL) && SAI_TxGetEnabledInterruptStatus(I2S4, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[4][0] != NULL) && SAI_TxGetEnabledInterruptStatus(I2S4, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiTxIsr(I2S4, s_saiHandle[4][0]); + } + SDK_ISR_EXIT_BARRIER; +} + +void I2S4_Tx_DriverIRQHandler(void); +void I2S4_Tx_DriverIRQHandler(void) +{ + assert(s_saiHandle[4][0] != NULL); + s_saiTxIsr(I2S4, s_saiHandle[4][0]); + SDK_ISR_EXIT_BARRIER; +} + +void I2S4_Rx_DriverIRQHandler(void); +void I2S4_Rx_DriverIRQHandler(void) +{ + assert(s_saiHandle[4][1] != NULL); + s_saiRxIsr(I2S4, s_saiHandle[4][1]); + SDK_ISR_EXIT_BARRIER; +} +#endif + +#if defined(FSL_FEATURE_SAI_SAI5_SAI6_SHARE_IRQ) && (FSL_FEATURE_SAI_SAI5_SAI6_SHARE_IRQ) && defined(I2S5) && \ + defined(I2S6) +void I2S56_DriverIRQHandler(void); +void I2S56_DriverIRQHandler(void) +{ + /* use index 5 to get handle when I2S5 & I2S6 share IRQ NUMBER */ + I2S_Type *base = s_saiHandle[5][1]->base; +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[5][1] != NULL) && SAI_RxGetEnabledInterruptStatus(base, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[5][1] != NULL) && SAI_RxGetEnabledInterruptStatus(base, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiRxIsr(base, s_saiHandle[5][1]); + } + + base = s_saiHandle[5][0]->base; +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[5][0] != NULL) && SAI_TxGetEnabledInterruptStatus(base, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[5][0] != NULL) && SAI_TxGetEnabledInterruptStatus(base, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiTxIsr(base, s_saiHandle[5][0]); + } + SDK_ISR_EXIT_BARRIER; +} + +void I2S56_Tx_DriverIRQHandler(void); +void I2S56_Tx_DriverIRQHandler(void) +{ + /* use index 5 to get handle when I2S5 & I2S6 share IRQ NUMBER */ + assert(s_saiHandle[5][0] != NULL); + s_saiTxIsr(s_saiHandle[5][0]->base, s_saiHandle[5][0]); + SDK_ISR_EXIT_BARRIER; +} + +void I2S56_Rx_DriverIRQHandler(void); +void I2S56_Rx_DriverIRQHandler(void) +{ + /* use index 5 to get handle when I2S5 & I2S6 share IRQ NUMBER */ + assert(s_saiHandle[5][1] != NULL); + s_saiRxIsr(s_saiHandle[5][1]->base, s_saiHandle[5][1]); + SDK_ISR_EXIT_BARRIER; +} + +#else + +#if defined(I2S5) +void I2S5_DriverIRQHandler(void); +void I2S5_DriverIRQHandler(void) +{ +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[5][1] != NULL) && SAI_RxGetEnabledInterruptStatus(I2S5, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[5][1] != NULL) && SAI_RxGetEnabledInterruptStatus(I2S5, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiRxIsr(I2S5, s_saiHandle[5][1]); + } +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[5][0] != NULL) && SAI_TxGetEnabledInterruptStatus(I2S5, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[5][0] != NULL) && SAI_TxGetEnabledInterruptStatus(I2S5, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiTxIsr(I2S5, s_saiHandle[5][0]); + } + SDK_ISR_EXIT_BARRIER; +} + +void I2S5_Tx_DriverIRQHandler(void); +void I2S5_Tx_DriverIRQHandler(void) +{ + assert(s_saiHandle[5][0] != NULL); + s_saiTxIsr(I2S5, s_saiHandle[5][0]); + SDK_ISR_EXIT_BARRIER; +} + +void I2S5_Rx_DriverIRQHandler(void); +void I2S5_Rx_DriverIRQHandler(void) +{ + assert(s_saiHandle[5][1] != NULL); + s_saiRxIsr(I2S5, s_saiHandle[5][1]); + SDK_ISR_EXIT_BARRIER; +} +#endif + +#if defined(I2S6) +void I2S6_DriverIRQHandler(void); +void I2S6_DriverIRQHandler(void) +{ +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[6][1] != NULL) && SAI_RxGetEnabledInterruptStatus(I2S6, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[6][1] != NULL) && SAI_RxGetEnabledInterruptStatus(I2S6, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiRxIsr(I2S6, s_saiHandle[6][1]); + } +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[6][0] != NULL) && SAI_TxGetEnabledInterruptStatus(I2S6, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[6][0] != NULL) && SAI_TxGetEnabledInterruptStatus(I2S6, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiTxIsr(I2S6, s_saiHandle[6][0]); + } + SDK_ISR_EXIT_BARRIER; +} + +void I2S6_Tx_DriverIRQHandler(void); +void I2S6_Tx_DriverIRQHandler(void) +{ + assert(s_saiHandle[6][0] != NULL); + s_saiTxIsr(I2S6, s_saiHandle[6][0]); + SDK_ISR_EXIT_BARRIER; +} + +void I2S6_Rx_DriverIRQHandler(void); +void I2S6_Rx_DriverIRQHandler(void) +{ + assert(s_saiHandle[6][1] != NULL); + s_saiRxIsr(I2S6, s_saiHandle[6][1]); + SDK_ISR_EXIT_BARRIER; +} +#endif +#endif + +#if defined(AUDIO__SAI0) +void AUDIO_SAI0_INT_DriverIRQHandler(void); +void AUDIO_SAI0_INT_DriverIRQHandler(void) +{ +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[0][1] != NULL) && + SAI_RxGetEnabledInterruptStatus(AUDIO__SAI0, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[0][1] != NULL) && + SAI_RxGetEnabledInterruptStatus(AUDIO__SAI0, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiRxIsr(AUDIO__SAI0, s_saiHandle[0][1]); + } + +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[0][0] != NULL) && + SAI_TxGetEnabledInterruptStatus(AUDIO__SAI0, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[0][0] != NULL) && + SAI_TxGetEnabledInterruptStatus(AUDIO__SAI0, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiTxIsr(AUDIO__SAI0, s_saiHandle[0][0]); + } + SDK_ISR_EXIT_BARRIER; +} +#endif /* AUDIO__SAI0 */ + +#if defined(AUDIO__SAI1) +void AUDIO_SAI1_INT_DriverIRQHandler(void); +void AUDIO_SAI1_INT_DriverIRQHandler(void) +{ +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[1][1] != NULL) && + SAI_RxGetEnabledInterruptStatus(AUDIO__SAI1, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[1][1] != NULL) && + SAI_RxGetEnabledInterruptStatus(AUDIO__SAI1, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiRxIsr(AUDIO__SAI1, s_saiHandle[1][1]); + } +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[1][0] != NULL) && + SAI_TxGetEnabledInterruptStatus(AUDIO__SAI1, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[1][0] != NULL) && + SAI_TxGetEnabledInterruptStatus(AUDIO__SAI1, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiTxIsr(AUDIO__SAI1, s_saiHandle[1][0]); + } + SDK_ISR_EXIT_BARRIER; +} +#endif /* AUDIO__SAI1 */ + +#if defined(AUDIO__SAI2) +void AUDIO_SAI2_INT_DriverIRQHandler(void); +void AUDIO_SAI2_INT_DriverIRQHandler(void) +{ +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[2][1] != NULL) && + SAI_RxGetEnabledInterruptStatus(AUDIO__SAI2, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[2][1] != NULL) && + SAI_RxGetEnabledInterruptStatus(AUDIO__SAI2, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiRxIsr(AUDIO__SAI2, s_saiHandle[2][1]); + } +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[2][0] != NULL) && + SAI_TxGetEnabledInterruptStatus(AUDIO__SAI2, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[2][0] != NULL) && + SAI_TxGetEnabledInterruptStatus(AUDIO__SAI2, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiTxIsr(AUDIO__SAI2, s_saiHandle[2][0]); + } + SDK_ISR_EXIT_BARRIER; +} +#endif /* AUDIO__SAI2 */ + +#if defined(AUDIO__SAI3) +void AUDIO_SAI3_INT_DriverIRQHandler(void); +void AUDIO_SAI3_INT_DriverIRQHandler(void) +{ +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[3][1] != NULL) && + SAI_RxGetEnabledInterruptStatus(AUDIO__SAI3, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[3][1] != NULL) && + SAI_RxGetEnabledInterruptStatus(AUDIO__SAI3, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiRxIsr(AUDIO__SAI3, s_saiHandle[3][1]); + } +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[3][0] != NULL) && + SAI_TxGetEnabledInterruptStatus(AUDIO__SAI3, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[3][0] != NULL) && + SAI_TxGetEnabledInterruptStatus(AUDIO__SAI3, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiTxIsr(AUDIO__SAI3, s_saiHandle[3][0]); + } + SDK_ISR_EXIT_BARRIER; +} +#endif + +#if defined(AUDIO__SAI6) +void AUDIO_SAI6_INT_DriverIRQHandler(void); +void AUDIO_SAI6_INT_DriverIRQHandler(void) +{ +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[6][1] != NULL) && + SAI_RxGetEnabledInterruptStatus(AUDIO__SAI6, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[6][1] != NULL) && + SAI_RxGetEnabledInterruptStatus(AUDIO__SAI6, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiRxIsr(AUDIO__SAI6, s_saiHandle[6][1]); + } +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[6][0] != NULL) && + SAI_TxGetEnabledInterruptStatus(AUDIO__SAI6, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[6][0] != NULL) && + SAI_TxGetEnabledInterruptStatus(AUDIO__SAI6, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiTxIsr(AUDIO__SAI6, s_saiHandle[6][0]); + } + SDK_ISR_EXIT_BARRIER; +} +#endif /* AUDIO__SAI6 */ + +#if defined(AUDIO__SAI7) +void AUDIO_SAI7_INT_DriverIRQHandler(void); +void AUDIO_SAI7_INT_DriverIRQHandler(void) +{ +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[7][1] != NULL) && + SAI_RxGetEnabledInterruptStatus(AUDIO__SAI7, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[7][1] != NULL) && + SAI_RxGetEnabledInterruptStatus(AUDIO__SAI7, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiRxIsr(AUDIO__SAI7, s_saiHandle[7][1]); + } +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[7][0] != NULL) && + SAI_TxGetEnabledInterruptStatus(AUDIO__SAI7, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[7][0] != NULL) && + SAI_TxGetEnabledInterruptStatus(AUDIO__SAI7, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiTxIsr(AUDIO__SAI7, s_saiHandle[7][0]); + } + SDK_ISR_EXIT_BARRIER; +} +#endif /* AUDIO__SAI7 */ + +#if defined(ADMA__SAI0) +void ADMA_SAI0_INT_DriverIRQHandler(void); +void ADMA_SAI0_INT_DriverIRQHandler(void) +{ +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[1][1] != NULL) && + SAI_RxGetEnabledInterruptStatus(ADMA__SAI0, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[1][1] != NULL) && + SAI_RxGetEnabledInterruptStatus(ADMA__SAI0, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiRxIsr(ADMA__SAI0, s_saiHandle[1][1]); + } +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[1][0] != NULL) && + SAI_TxGetEnabledInterruptStatus(ADMA__SAI0, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[1][0] != NULL) && + SAI_TxGetEnabledInterruptStatus(ADMA__SAI0, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiTxIsr(ADMA__SAI0, s_saiHandle[1][0]); + } + SDK_ISR_EXIT_BARRIER; +} +#endif /* ADMA__SAI0 */ + +#if defined(ADMA__SAI1) +void ADMA_SAI1_INT_DriverIRQHandler(void); +void ADMA_SAI1_INT_DriverIRQHandler(void) +{ +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[1][1] != NULL) && + SAI_RxGetEnabledInterruptStatus(ADMA__SAI1, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[1][1] != NULL) && + SAI_RxGetEnabledInterruptStatus(ADMA__SAI1, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiRxIsr(ADMA__SAI1, s_saiHandle[1][1]); + } +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[1][0] != NULL) && + SAI_TxGetEnabledInterruptStatus(ADMA__SAI1, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[1][0] != NULL) && + SAI_TxGetEnabledInterruptStatus(ADMA__SAI1, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiTxIsr(ADMA__SAI1, s_saiHandle[1][0]); + } + SDK_ISR_EXIT_BARRIER; +} +#endif /* ADMA__SAI1 */ + +#if defined(ADMA__SAI2) +void ADMA_SAI2_INT_DriverIRQHandler(void); +void ADMA_SAI2_INT_DriverIRQHandler(void) +{ +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[1][1] != NULL) && + SAI_RxGetEnabledInterruptStatus(ADMA__SAI2, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[1][1] != NULL) && + SAI_RxGetEnabledInterruptStatus(ADMA__SAI2, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiRxIsr(ADMA__SAI2, s_saiHandle[1][1]); + } +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[1][0] != NULL) && + SAI_TxGetEnabledInterruptStatus(ADMA__SAI2, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[1][0] != NULL) && + SAI_TxGetEnabledInterruptStatus(ADMA__SAI2, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiTxIsr(ADMA__SAI2, s_saiHandle[1][0]); + } + SDK_ISR_EXIT_BARRIER; +} +#endif /* ADMA__SAI2 */ + +#if defined(ADMA__SAI3) +void ADMA_SAI3_INT_DriverIRQHandler(void); +void ADMA_SAI3_INT_DriverIRQHandler(void) +{ +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[1][1] != NULL) && + SAI_RxGetEnabledInterruptStatus(ADMA__SAI3, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[1][1] != NULL) && + SAI_RxGetEnabledInterruptStatus(ADMA__SAI3, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiRxIsr(ADMA__SAI3, s_saiHandle[1][1]); + } +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[1][0] != NULL) && + SAI_TxGetEnabledInterruptStatus(ADMA__SAI3, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[1][0] != NULL) && + SAI_TxGetEnabledInterruptStatus(ADMA__SAI3, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiTxIsr(ADMA__SAI3, s_saiHandle[1][0]); + } + SDK_ISR_EXIT_BARRIER; +} +#endif /* ADMA__SAI3 */ + +#if defined(ADMA__SAI4) +void ADMA_SAI4_INT_DriverIRQHandler(void); +void ADMA_SAI4_INT_DriverIRQHandler(void) +{ +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[1][1] != NULL) && + SAI_RxGetEnabledInterruptStatus(ADMA__SAI4, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[1][1] != NULL) && + SAI_RxGetEnabledInterruptStatus(ADMA__SAI4, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiRxIsr(ADMA__SAI4, s_saiHandle[1][1]); + } + +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[1][0] != NULL) && + SAI_TxGetEnabledInterruptStatus(ADMA__SAI4, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[1][0] != NULL) && + SAI_TxGetEnabledInterruptStatus(ADMA__SAI4, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiTxIsr(ADMA__SAI4, s_saiHandle[1][0]); + } + SDK_ISR_EXIT_BARRIER; +} +#endif /* ADMA__SAI4 */ + +#if defined(ADMA__SAI5) +void ADMA_SAI5_INT_DriverIRQHandler(void); +void ADMA_SAI5_INT_DriverIRQHandler(void) +{ +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[1][1] != NULL) && + SAI_RxGetEnabledInterruptStatus(ADMA__SAI5, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[1][1] != NULL) && + SAI_RxGetEnabledInterruptStatus(ADMA__SAI5, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiRxIsr(ADMA__SAI5, s_saiHandle[1][1]); + } +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[1][0] != NULL) && + SAI_TxGetEnabledInterruptStatus(ADMA__SAI5, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[1][0] != NULL) && + SAI_TxGetEnabledInterruptStatus(ADMA__SAI5, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiTxIsr(ADMA__SAI5, s_saiHandle[1][0]); + } + SDK_ISR_EXIT_BARRIER; +} +#endif /* ADMA__SAI5 */ + +#if defined(SAI0) +void SAI0_DriverIRQHandler(void); +void SAI0_DriverIRQHandler(void) +{ +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[0][1] != NULL) && SAI_RxGetEnabledInterruptStatus(SAI0, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[0][1] != NULL) && SAI_RxGetEnabledInterruptStatus(SAI0, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiRxIsr(SAI0, s_saiHandle[0][1]); + } +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[0][0] != NULL) && SAI_TxGetEnabledInterruptStatus(SAI0, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[0][0] != NULL) && SAI_TxGetEnabledInterruptStatus(SAI0, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiTxIsr(SAI0, s_saiHandle[0][0]); + } + SDK_ISR_EXIT_BARRIER; +} +#endif /* SAI0 */ + +#if defined(SAI1) +void SAI1_DriverIRQHandler(void); +void SAI1_DriverIRQHandler(void) +{ +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[1][1] != NULL) && SAI_RxGetEnabledInterruptStatus(SAI1, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[1][1] != NULL) && SAI_RxGetEnabledInterruptStatus(SAI1, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiRxIsr(SAI1, s_saiHandle[1][1]); + } +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[1][0] != NULL) && SAI_TxGetEnabledInterruptStatus(SAI1, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[1][0] != NULL) && SAI_TxGetEnabledInterruptStatus(SAI1, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiTxIsr(SAI1, s_saiHandle[1][0]); + } + SDK_ISR_EXIT_BARRIER; +} +#endif /* SAI1 */ + +#if defined(SAI2) +void SAI2_DriverIRQHandler(void); +void SAI2_DriverIRQHandler(void) +{ +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[2][1] != NULL) && SAI_RxGetEnabledInterruptStatus(SAI2, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[2][1] != NULL) && SAI_RxGetEnabledInterruptStatus(SAI2, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiRxIsr(SAI2, s_saiHandle[2][1]); + } +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[2][0] != NULL) && SAI_TxGetEnabledInterruptStatus(SAI2, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[2][0] != NULL) && SAI_TxGetEnabledInterruptStatus(SAI2, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiTxIsr(SAI2, s_saiHandle[2][0]); + } + SDK_ISR_EXIT_BARRIER; +} +#endif /* SAI2 */ + +#if defined(SAI3) +void SAI3_DriverIRQHandler(void); +void SAI3_DriverIRQHandler(void) +{ +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[3][1] != NULL) && SAI_RxGetEnabledInterruptStatus(SAI3, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[3][1] != NULL) && SAI_RxGetEnabledInterruptStatus(SAI3, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiRxIsr(SAI3, s_saiHandle[3][1]); + } +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[3][0] != NULL) && SAI_TxGetEnabledInterruptStatus(SAI3, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[3][0] != NULL) && SAI_TxGetEnabledInterruptStatus(SAI3, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiTxIsr(SAI3, s_saiHandle[3][0]); + } + SDK_ISR_EXIT_BARRIER; +} + +void SAI3_TX_DriverIRQHandler(void); +void SAI3_TX_DriverIRQHandler(void) +{ + assert(s_saiHandle[3][0] != NULL); + s_saiTxIsr(SAI3, s_saiHandle[3][0]); + SDK_ISR_EXIT_BARRIER; +} + +void SAI3_RX_DriverIRQHandler(void); +void SAI3_RX_DriverIRQHandler(void) +{ + assert(s_saiHandle[3][1] != NULL); + s_saiRxIsr(SAI3, s_saiHandle[3][1]); + SDK_ISR_EXIT_BARRIER; +} +#endif /* SAI3 */ + +#if defined(SAI4) +void SAI4_DriverIRQHandler(void); +void SAI4_DriverIRQHandler(void) +{ +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[4][1] != NULL) && SAI_RxGetEnabledInterruptStatus(SAI4, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[4][1] != NULL) && SAI_RxGetEnabledInterruptStatus(SAI4, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiRxIsr(SAI4, s_saiHandle[4][1]); + } +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[4][0] != NULL) && SAI_TxGetEnabledInterruptStatus(SAI4, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[4][0] != NULL) && SAI_TxGetEnabledInterruptStatus(SAI4, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiTxIsr(SAI4, s_saiHandle[4][0]); + } + SDK_ISR_EXIT_BARRIER; +} +#endif /* SAI4 */ + +#if defined(SAI5) +void SAI5_DriverIRQHandler(void); +void SAI5_DriverIRQHandler(void) +{ +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[5][1] != NULL) && SAI_RxGetEnabledInterruptStatus(SAI5, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[5][1] != NULL) && SAI_RxGetEnabledInterruptStatus(SAI5, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiRxIsr(SAI5, s_saiHandle[5][1]); + } +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[5][0] != NULL) && SAI_TxGetEnabledInterruptStatus(SAI5, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[5][0] != NULL) && SAI_TxGetEnabledInterruptStatus(SAI5, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiTxIsr(SAI5, s_saiHandle[5][0]); + } + SDK_ISR_EXIT_BARRIER; +} +#endif /* SAI5 */ + +#if defined(SAI6) +void SAI6_DriverIRQHandler(void); +void SAI6_DriverIRQHandler(void) +{ +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[6][1] != NULL) && SAI_RxGetEnabledInterruptStatus(SAI6, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[6][1] != NULL) && SAI_RxGetEnabledInterruptStatus(SAI6, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiRxIsr(SAI6, s_saiHandle[6][1]); + } +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + if ((s_saiHandle[6][0] != NULL) && SAI_TxGetEnabledInterruptStatus(SAI6, (I2S_TCSR_FRIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FRF_MASK | I2S_TCSR_FEF_MASK))) +#else + if ((s_saiHandle[6][0] != NULL) && SAI_TxGetEnabledInterruptStatus(SAI6, (I2S_TCSR_FWIE_MASK | I2S_TCSR_FEIE_MASK), + (I2S_TCSR_FWF_MASK | I2S_TCSR_FEF_MASK))) +#endif + { + s_saiTxIsr(SAI6, s_saiHandle[6][0]); + } + SDK_ISR_EXIT_BARRIER; +} +#endif /* SAI6 */
\ No newline at end of file diff --git a/bsps/arm/imxrt/mcux-sdk/drivers/sai/fsl_sai.h b/bsps/arm/imxrt/mcux-sdk/drivers/sai/fsl_sai.h new file mode 100644 index 0000000000..a080aa81b8 --- /dev/null +++ b/bsps/arm/imxrt/mcux-sdk/drivers/sai/fsl_sai.h @@ -0,0 +1,1586 @@ +/* + * Copyright (c) 2015, Freescale Semiconductor, Inc. + * Copyright 2016-2022 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _FSL_SAI_H_ +#define _FSL_SAI_H_ + +#include "fsl_common.h" + +/*! + * @addtogroup sai_driver SAI Driver + * @{ + */ + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/*! @name Driver version */ +/*@{*/ +#define FSL_SAI_DRIVER_VERSION (MAKE_VERSION(2, 3, 8)) /*!< Version 2.3.8 */ +/*@}*/ + +/*! @brief _sai_status_t, SAI return status.*/ +enum +{ + kStatus_SAI_TxBusy = MAKE_STATUS(kStatusGroup_SAI, 0), /*!< SAI Tx is busy. */ + kStatus_SAI_RxBusy = MAKE_STATUS(kStatusGroup_SAI, 1), /*!< SAI Rx is busy. */ + kStatus_SAI_TxError = MAKE_STATUS(kStatusGroup_SAI, 2), /*!< SAI Tx FIFO error. */ + kStatus_SAI_RxError = MAKE_STATUS(kStatusGroup_SAI, 3), /*!< SAI Rx FIFO error. */ + kStatus_SAI_QueueFull = MAKE_STATUS(kStatusGroup_SAI, 4), /*!< SAI transfer queue is full. */ + kStatus_SAI_TxIdle = MAKE_STATUS(kStatusGroup_SAI, 5), /*!< SAI Tx is idle */ + kStatus_SAI_RxIdle = MAKE_STATUS(kStatusGroup_SAI, 6) /*!< SAI Rx is idle */ +}; + +/*! @brief _sai_channel_mask,.sai channel mask value, actual channel numbers is depend soc specific */ +enum +{ + kSAI_Channel0Mask = 1 << 0U, /*!< channel 0 mask value */ + kSAI_Channel1Mask = 1 << 1U, /*!< channel 1 mask value */ + kSAI_Channel2Mask = 1 << 2U, /*!< channel 2 mask value */ + kSAI_Channel3Mask = 1 << 3U, /*!< channel 3 mask value */ + kSAI_Channel4Mask = 1 << 4U, /*!< channel 4 mask value */ + kSAI_Channel5Mask = 1 << 5U, /*!< channel 5 mask value */ + kSAI_Channel6Mask = 1 << 6U, /*!< channel 6 mask value */ + kSAI_Channel7Mask = 1 << 7U, /*!< channel 7 mask value */ +}; + +/*! @brief Define the SAI bus type */ +typedef enum _sai_protocol +{ + kSAI_BusLeftJustified = 0x0U, /*!< Uses left justified format.*/ + kSAI_BusRightJustified, /*!< Uses right justified format. */ + kSAI_BusI2S, /*!< Uses I2S format. */ + kSAI_BusPCMA, /*!< Uses I2S PCM A format.*/ + kSAI_BusPCMB /*!< Uses I2S PCM B format. */ +} sai_protocol_t; + +/*! @brief Master or slave mode */ +typedef enum _sai_master_slave +{ + kSAI_Master = 0x0U, /*!< Master mode include bclk and frame sync */ + kSAI_Slave = 0x1U, /*!< Slave mode include bclk and frame sync */ + kSAI_Bclk_Master_FrameSync_Slave = 0x2U, /*!< bclk in master mode, frame sync in slave mode */ + kSAI_Bclk_Slave_FrameSync_Master = 0x3U, /*!< bclk in slave mode, frame sync in master mode */ +} sai_master_slave_t; + +/*! @brief Mono or stereo audio format */ +typedef enum _sai_mono_stereo +{ + kSAI_Stereo = 0x0U, /*!< Stereo sound. */ + kSAI_MonoRight, /*!< Only Right channel have sound. */ + kSAI_MonoLeft /*!< Only left channel have sound. */ +} sai_mono_stereo_t; + +/*! @brief SAI data order, MSB or LSB */ +typedef enum _sai_data_order +{ + kSAI_DataLSB = 0x0U, /*!< LSB bit transferred first */ + kSAI_DataMSB /*!< MSB bit transferred first */ +} sai_data_order_t; + +/*! @brief SAI clock polarity, active high or low */ +typedef enum _sai_clock_polarity +{ + kSAI_PolarityActiveHigh = 0x0U, /*!< Drive outputs on rising edge */ + kSAI_PolarityActiveLow = 0x1U, /*!< Drive outputs on falling edge */ + kSAI_SampleOnFallingEdge = 0x0U, /*!< Sample inputs on falling edge */ + kSAI_SampleOnRisingEdge = 0x1U, /*!< Sample inputs on rising edge */ +} sai_clock_polarity_t; + +/*! @brief Synchronous or asynchronous mode */ +typedef enum _sai_sync_mode +{ + kSAI_ModeAsync = 0x0U, /*!< Asynchronous mode */ + kSAI_ModeSync, /*!< Synchronous mode (with receiver or transmit) */ +#if defined(FSL_FEATURE_SAI_HAS_SYNC_WITH_ANOTHER_SAI) && (FSL_FEATURE_SAI_HAS_SYNC_WITH_ANOTHER_SAI) + kSAI_ModeSyncWithOtherTx, /*!< Synchronous with another SAI transmit */ + kSAI_ModeSyncWithOtherRx /*!< Synchronous with another SAI receiver */ +#endif /* FSL_FEATURE_SAI_HAS_SYNC_WITH_ANOTHER_SAI */ +} sai_sync_mode_t; + +#if !(defined(FSL_FEATURE_SAI_HAS_NO_MCR_MICS) && (FSL_FEATURE_SAI_HAS_NO_MCR_MICS)) +/*! @brief Mater clock source */ +typedef enum _sai_mclk_source +{ + kSAI_MclkSourceSysclk = 0x0U, /*!< Master clock from the system clock */ + kSAI_MclkSourceSelect1, /*!< Master clock from source 1 */ + kSAI_MclkSourceSelect2, /*!< Master clock from source 2 */ + kSAI_MclkSourceSelect3 /*!< Master clock from source 3 */ +} sai_mclk_source_t; +#endif + +/*! @brief Bit clock source */ +typedef enum _sai_bclk_source +{ + kSAI_BclkSourceBusclk = 0x0U, /*!< Bit clock using bus clock */ + /* General device bit source definition */ + kSAI_BclkSourceMclkOption1 = 0x1U, /*!< Bit clock MCLK option 1 */ + kSAI_BclkSourceMclkOption2 = 0x2U, /*!< Bit clock MCLK option2 */ + kSAI_BclkSourceMclkOption3 = 0x3U, /*!< Bit clock MCLK option3 */ + /* Kinetis device bit clock source definition */ + kSAI_BclkSourceMclkDiv = 0x1U, /*!< Bit clock using master clock divider */ + kSAI_BclkSourceOtherSai0 = 0x2U, /*!< Bit clock from other SAI device */ + kSAI_BclkSourceOtherSai1 = 0x3U /*!< Bit clock from other SAI device */ +} sai_bclk_source_t; + +/*! @brief _sai_interrupt_enable_t, The SAI interrupt enable flag */ +enum +{ + kSAI_WordStartInterruptEnable = + I2S_TCSR_WSIE_MASK, /*!< Word start flag, means the first word in a frame detected */ + kSAI_SyncErrorInterruptEnable = I2S_TCSR_SEIE_MASK, /*!< Sync error flag, means the sync error is detected */ + kSAI_FIFOWarningInterruptEnable = I2S_TCSR_FWIE_MASK, /*!< FIFO warning flag, means the FIFO is empty */ + kSAI_FIFOErrorInterruptEnable = I2S_TCSR_FEIE_MASK, /*!< FIFO error flag */ +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + kSAI_FIFORequestInterruptEnable = I2S_TCSR_FRIE_MASK, /*!< FIFO request, means reached watermark */ +#endif /* FSL_FEATURE_SAI_HAS_FIFO */ +}; + +/*! @brief _sai_dma_enable_t, The DMA request sources */ +enum +{ + kSAI_FIFOWarningDMAEnable = I2S_TCSR_FWDE_MASK, /*!< FIFO warning caused by the DMA request */ +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + kSAI_FIFORequestDMAEnable = I2S_TCSR_FRDE_MASK, /*!< FIFO request caused by the DMA request */ +#endif /* FSL_FEATURE_SAI_HAS_FIFO */ +}; + +/*! @brief _sai_flags, The SAI status flag */ +enum +{ + kSAI_WordStartFlag = I2S_TCSR_WSF_MASK, /*!< Word start flag, means the first word in a frame detected */ + kSAI_SyncErrorFlag = I2S_TCSR_SEF_MASK, /*!< Sync error flag, means the sync error is detected */ + kSAI_FIFOErrorFlag = I2S_TCSR_FEF_MASK, /*!< FIFO error flag */ +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + kSAI_FIFORequestFlag = I2S_TCSR_FRF_MASK, /*!< FIFO request flag. */ +#endif /* FSL_FEATURE_SAI_HAS_FIFO */ + kSAI_FIFOWarningFlag = I2S_TCSR_FWF_MASK, /*!< FIFO warning flag */ +}; + +/*! @brief The reset type */ +typedef enum _sai_reset_type +{ + kSAI_ResetTypeSoftware = I2S_TCSR_SR_MASK, /*!< Software reset, reset the logic state */ + kSAI_ResetTypeFIFO = I2S_TCSR_FR_MASK, /*!< FIFO reset, reset the FIFO read and write pointer */ + kSAI_ResetAll = I2S_TCSR_SR_MASK | I2S_TCSR_FR_MASK /*!< All reset. */ +} sai_reset_type_t; + +#if defined(FSL_FEATURE_SAI_HAS_FIFO_PACKING) && FSL_FEATURE_SAI_HAS_FIFO_PACKING +/*! + * @brief The SAI packing mode + * The mode includes 8 bit and 16 bit packing. + */ +typedef enum _sai_fifo_packing +{ + kSAI_FifoPackingDisabled = 0x0U, /*!< Packing disabled */ + kSAI_FifoPacking8bit = 0x2U, /*!< 8 bit packing enabled */ + kSAI_FifoPacking16bit = 0x3U /*!< 16bit packing enabled */ +} sai_fifo_packing_t; +#endif /* FSL_FEATURE_SAI_HAS_FIFO_PACKING */ + +/*! @brief SAI user configuration structure */ +typedef struct _sai_config +{ + sai_protocol_t protocol; /*!< Audio bus protocol in SAI */ + sai_sync_mode_t syncMode; /*!< SAI sync mode, control Tx/Rx clock sync */ +#if defined(FSL_FEATURE_SAI_HAS_MCR) && (FSL_FEATURE_SAI_HAS_MCR) + bool mclkOutputEnable; /*!< Master clock output enable, true means master clock divider enabled */ +#if !(defined(FSL_FEATURE_SAI_HAS_NO_MCR_MICS) && (FSL_FEATURE_SAI_HAS_NO_MCR_MICS)) + sai_mclk_source_t mclkSource; /*!< Master Clock source */ +#endif /* FSL_FEATURE_SAI_HAS_MCR */ +#endif + sai_bclk_source_t bclkSource; /*!< Bit Clock source */ + sai_master_slave_t masterSlave; /*!< Master or slave */ +} sai_config_t; + +#ifndef SAI_XFER_QUEUE_SIZE +/*!@brief SAI transfer queue size, user can refine it according to use case. */ +#define SAI_XFER_QUEUE_SIZE (4U) +#endif + +/*! @brief Audio sample rate */ +typedef enum _sai_sample_rate +{ + kSAI_SampleRate8KHz = 8000U, /*!< Sample rate 8000 Hz */ + kSAI_SampleRate11025Hz = 11025U, /*!< Sample rate 11025 Hz */ + kSAI_SampleRate12KHz = 12000U, /*!< Sample rate 12000 Hz */ + kSAI_SampleRate16KHz = 16000U, /*!< Sample rate 16000 Hz */ + kSAI_SampleRate22050Hz = 22050U, /*!< Sample rate 22050 Hz */ + kSAI_SampleRate24KHz = 24000U, /*!< Sample rate 24000 Hz */ + kSAI_SampleRate32KHz = 32000U, /*!< Sample rate 32000 Hz */ + kSAI_SampleRate44100Hz = 44100U, /*!< Sample rate 44100 Hz */ + kSAI_SampleRate48KHz = 48000U, /*!< Sample rate 48000 Hz */ + kSAI_SampleRate96KHz = 96000U, /*!< Sample rate 96000 Hz */ + kSAI_SampleRate192KHz = 192000U, /*!< Sample rate 192000 Hz */ + kSAI_SampleRate384KHz = 384000U, /*!< Sample rate 384000 Hz */ +} sai_sample_rate_t; + +/*! @brief Audio word width */ +typedef enum _sai_word_width +{ + kSAI_WordWidth8bits = 8U, /*!< Audio data width 8 bits */ + kSAI_WordWidth16bits = 16U, /*!< Audio data width 16 bits */ + kSAI_WordWidth24bits = 24U, /*!< Audio data width 24 bits */ + kSAI_WordWidth32bits = 32U /*!< Audio data width 32 bits */ +} sai_word_width_t; + +#if defined(FSL_FEATURE_SAI_HAS_CHANNEL_MODE) && FSL_FEATURE_SAI_HAS_CHANNEL_MODE +/*! @brief sai data pin state definition */ +typedef enum _sai_data_pin_state +{ + kSAI_DataPinStateTriState = + 0U, /*!< transmit data pins are tri-stated when slots are masked or channels are disabled */ + kSAI_DataPinStateOutputZero = 1U, /*!< transmit data pins are never tri-stated and will output zero when slots + are masked or channel disabled */ +} sai_data_pin_state_t; +#endif + +#if defined(FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) && FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE +/*! @brief sai fifo combine mode definition */ +typedef enum _sai_fifo_combine +{ + kSAI_FifoCombineDisabled = 0U, /*!< sai fifo combine mode disabled */ + kSAI_FifoCombineModeEnabledOnRead, /*!< sai fifo combine mode enabled on FIFO reads */ + kSAI_FifoCombineModeEnabledOnWrite, /*!< sai fifo combine mode enabled on FIFO write */ + kSAI_FifoCombineModeEnabledOnReadWrite, /*!< sai fifo combined mode enabled on FIFO read/writes */ +} sai_fifo_combine_t; +#endif + +/*! @brief sai transceiver type */ +typedef enum _sai_transceiver_type +{ + kSAI_Transmitter = 0U, /*!< sai transmitter */ + kSAI_Receiver = 1U, /*!< sai receiver */ +} sai_transceiver_type_t; + +/*! @brief sai frame sync len */ +typedef enum _sai_frame_sync_len +{ + kSAI_FrameSyncLenOneBitClk = 0U, /*!< 1 bit clock frame sync len for DSP mode */ + kSAI_FrameSyncLenPerWordWidth = 1U, /*!< Frame sync length decided by word width */ +} sai_frame_sync_len_t; + +/*! @brief sai transfer format */ +typedef struct _sai_transfer_format +{ + uint32_t sampleRate_Hz; /*!< Sample rate of audio data */ + uint32_t bitWidth; /*!< Data length of audio data, usually 8/16/24/32 bits */ + sai_mono_stereo_t stereo; /*!< Mono or stereo */ +#if defined(FSL_FEATURE_SAI_HAS_MCLKDIV_REGISTER) && (FSL_FEATURE_SAI_HAS_MCLKDIV_REGISTER) + uint32_t masterClockHz; /*!< Master clock frequency in Hz */ +#endif /* FSL_FEATURE_SAI_HAS_MCLKDIV_REGISTER */ +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + uint8_t watermark; /*!< Watermark value */ +#endif /* FSL_FEATURE_SAI_HAS_FIFO */ + + /* for the multi channel usage, user can provide channelMask Oonly, then sai driver will handle + * other parameter carefully, such as + * channelMask = kSAI_Channel0Mask | kSAI_Channel1Mask | kSAI_Channel4Mask + * then in SAI_RxSetFormat/SAI_TxSetFormat function, channel/endChannel/channelNums will be calculated. + * for the single channel usage, user can provide channel or channel mask only, such as, + * channel = 0 or channelMask = kSAI_Channel0Mask. + */ + uint8_t channel; /*!< Transfer start channel */ + uint8_t channelMask; /*!< enabled channel mask value, reference _sai_channel_mask */ + uint8_t endChannel; /*!< end channel number */ + uint8_t channelNums; /*!< Total enabled channel numbers */ + + sai_protocol_t protocol; /*!< Which audio protocol used */ + bool isFrameSyncCompact; /*!< True means Frame sync length is configurable according to bitWidth, false means frame + sync length is 64 times of bit clock. */ +} sai_transfer_format_t; + +#if (defined(FSL_FEATURE_SAI_HAS_MCR) && (FSL_FEATURE_SAI_HAS_MCR)) || \ + (defined(FSL_FEATURE_SAI_HAS_MCLKDIV_REGISTER) && (FSL_FEATURE_SAI_HAS_MCLKDIV_REGISTER)) +/*! @brief master clock configurations */ +typedef struct _sai_master_clock +{ +#if defined(FSL_FEATURE_SAI_HAS_MCR) && (FSL_FEATURE_SAI_HAS_MCR) + bool mclkOutputEnable; /*!< master clock output enable */ +#if !(defined(FSL_FEATURE_SAI_HAS_NO_MCR_MICS) && (FSL_FEATURE_SAI_HAS_NO_MCR_MICS)) + sai_mclk_source_t mclkSource; /*!< Master Clock source */ +#endif +#endif + +#if (defined(FSL_FEATURE_SAI_HAS_MCR) && (FSL_FEATURE_SAI_HAS_MCR)) || \ + (defined(FSL_FEATURE_SAI_HAS_MCLKDIV_REGISTER) && (FSL_FEATURE_SAI_HAS_MCLKDIV_REGISTER)) + uint32_t mclkHz; /*!< target mclk frequency */ + uint32_t mclkSourceClkHz; /*!< mclk source frequency*/ +#endif +} sai_master_clock_t; +#endif + +/*! @brief sai fifo feature*/ +#if (defined(FSL_FEATURE_SAI_HAS_FIFO_FUNCTION_AFTER_ERROR) && FSL_FEATURE_SAI_HAS_FIFO_FUNCTION_AFTER_ERROR) || \ + (defined(FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) && FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) || \ + (defined(FSL_FEATURE_SAI_HAS_FIFO_PACKING) && FSL_FEATURE_SAI_HAS_FIFO_PACKING) || \ + (defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO)) +#define FSL_SAI_HAS_FIFO_EXTEND_FEATURE 1 +#else +#define FSL_SAI_HAS_FIFO_EXTEND_FEATURE 0 +#endif + +#if FSL_SAI_HAS_FIFO_EXTEND_FEATURE +/*! @brief sai fifo configurations */ +typedef struct _sai_fifo +{ +#if defined(FSL_FEATURE_SAI_HAS_FIFO_FUNCTION_AFTER_ERROR) && FSL_FEATURE_SAI_HAS_FIFO_FUNCTION_AFTER_ERROR + bool fifoContinueOneError; /*!< fifo continues when error occur */ +#endif + +#if defined(FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) && FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE + sai_fifo_combine_t fifoCombine; /*!< fifo combine mode */ +#endif + +#if defined(FSL_FEATURE_SAI_HAS_FIFO_PACKING) && FSL_FEATURE_SAI_HAS_FIFO_PACKING + sai_fifo_packing_t fifoPacking; /*!< fifo packing mode */ +#endif +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + uint8_t fifoWatermark; /*!< fifo watermark */ +#endif +} sai_fifo_t; +#endif + +/*! @brief sai bit clock configurations */ +typedef struct _sai_bit_clock +{ + bool bclkSrcSwap; /*!< bit clock source swap */ + bool bclkInputDelay; /*!< bit clock actually used by the transmitter is delayed by the pad output delay, + this has effect of decreasing the data input setup time, but increasing the data output valid + time .*/ + sai_clock_polarity_t bclkPolarity; /*!< bit clock polarity */ + sai_bclk_source_t bclkSource; /*!< bit Clock source */ +} sai_bit_clock_t; + +/*! @brief sai frame sync configurations */ +typedef struct _sai_frame_sync +{ + uint8_t frameSyncWidth; /*!< frame sync width in number of bit clocks */ + bool frameSyncEarly; /*!< TRUE is frame sync assert one bit before the first bit of frame + FALSE is frame sync assert with the first bit of the frame */ + +#if defined(FSL_FEATURE_SAI_HAS_ON_DEMAND_MODE) && FSL_FEATURE_SAI_HAS_ON_DEMAND_MODE + bool frameSyncGenerateOnDemand; /*!< internal frame sync is generated when FIFO waring flag is clear */ +#endif + + sai_clock_polarity_t frameSyncPolarity; /*!< frame sync polarity */ + +} sai_frame_sync_t; + +/*! @brief sai serial data configurations */ +typedef struct _sai_serial_data +{ +#if defined(FSL_FEATURE_SAI_HAS_CHANNEL_MODE) && FSL_FEATURE_SAI_HAS_CHANNEL_MODE + sai_data_pin_state_t dataMode; /*!< sai data pin state when slots masked or channel disabled */ +#endif + + sai_data_order_t dataOrder; /*!< configure whether the LSB or MSB is transmitted first */ + uint8_t dataWord0Length; /*!< configure the number of bits in the first word in each frame */ + uint8_t dataWordNLength; /*!< configure the number of bits in the each word in each frame, except the first word */ + uint8_t dataWordLength; /*!< used to record the data length for dma transfer */ + uint8_t + dataFirstBitShifted; /*!< Configure the bit index for the first bit transmitted for each word in the frame */ + uint8_t dataWordNum; /*!< configure the number of words in each frame */ + uint32_t dataMaskedWord; /*!< configure whether the transmit word is masked */ +} sai_serial_data_t; + +/*! @brief sai transceiver configurations */ +typedef struct _sai_transceiver +{ + sai_serial_data_t serialData; /*!< serial data configurations */ + sai_frame_sync_t frameSync; /*!< ws configurations */ + sai_bit_clock_t bitClock; /*!< bit clock configurations */ +#if FSL_SAI_HAS_FIFO_EXTEND_FEATURE + sai_fifo_t fifo; /*!< fifo configurations */ +#endif + sai_master_slave_t masterSlave; /*!< transceiver is master or slave */ + + sai_sync_mode_t syncMode; /*!< transceiver sync mode */ + + uint8_t startChannel; /*!< Transfer start channel */ + uint8_t channelMask; /*!< enabled channel mask value, reference _sai_channel_mask */ + uint8_t endChannel; /*!< end channel number */ + uint8_t channelNums; /*!< Total enabled channel numbers */ + +} sai_transceiver_t; + +/*! @brief SAI transfer structure */ +typedef struct _sai_transfer +{ + uint8_t *data; /*!< Data start address to transfer. */ + size_t dataSize; /*!< Transfer size. */ +} sai_transfer_t; + +typedef struct _sai_handle sai_handle_t; + +/*! @brief SAI transfer callback prototype */ +typedef void (*sai_transfer_callback_t)(I2S_Type *base, sai_handle_t *handle, status_t status, void *userData); + +/*! @brief SAI handle structure */ +struct _sai_handle +{ + I2S_Type *base; /*!< base address */ + + uint32_t state; /*!< Transfer status */ + sai_transfer_callback_t callback; /*!< Callback function called at transfer event*/ + void *userData; /*!< Callback parameter passed to callback function*/ + uint8_t bitWidth; /*!< Bit width for transfer, 8/16/24/32 bits */ + + /* for the multi channel usage, user can provide channelMask Oonly, then sai driver will handle + * other parameter carefully, such as + * channelMask = kSAI_Channel0Mask | kSAI_Channel1Mask | kSAI_Channel4Mask + * then in SAI_RxSetFormat/SAI_TxSetFormat function, channel/endChannel/channelNums will be calculated. + * for the single channel usage, user can provide channel or channel mask only, such as, + * channel = 0 or channelMask = kSAI_Channel0Mask. + */ + uint8_t channel; /*!< Transfer start channel */ + uint8_t channelMask; /*!< enabled channel mask value, refernece _sai_channel_mask */ + uint8_t endChannel; /*!< end channel number */ + uint8_t channelNums; /*!< Total enabled channel numbers */ + + sai_transfer_t saiQueue[SAI_XFER_QUEUE_SIZE]; /*!< Transfer queue storing queued transfer */ + size_t transferSize[SAI_XFER_QUEUE_SIZE]; /*!< Data bytes need to transfer */ + volatile uint8_t queueUser; /*!< Index for user to queue transfer */ + volatile uint8_t queueDriver; /*!< Index for driver to get the transfer data and size */ +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + uint8_t watermark; /*!< Watermark value */ +#endif +}; + +/******************************************************************************* + * API + ******************************************************************************/ + +#if defined(__cplusplus) +extern "C" { +#endif /*_cplusplus*/ + +/*! + * @name Initialization and deinitialization + * @{ + */ + +/*! + * @brief Initializes the SAI Tx peripheral. + * @deprecated Do not use this function. It has been superceded by @ref SAI_Init + * + * Ungates the SAI clock, resets the module, and configures SAI Tx with a configuration structure. + * The configuration structure can be custom filled or set with default values by + * SAI_TxGetDefaultConfig(). + * + * @note This API should be called at the beginning of the application to use + * the SAI driver. Otherwise, accessing the SAIM module can cause a hard fault + * because the clock is not enabled. + * + * @param base SAI base pointer + * @param config SAI configuration structure. + */ +void SAI_TxInit(I2S_Type *base, const sai_config_t *config); + +/*! + * @brief Initializes the SAI Rx peripheral. + * @deprecated Do not use this function. It has been superceded by @ref SAI_Init + * + * Ungates the SAI clock, resets the module, and configures the SAI Rx with a configuration structure. + * The configuration structure can be custom filled or set with default values by + * SAI_RxGetDefaultConfig(). + * + * @note This API should be called at the beginning of the application to use + * the SAI driver. Otherwise, accessing the SAI module can cause a hard fault + * because the clock is not enabled. + * + * @param base SAI base pointer + * @param config SAI configuration structure. + */ +void SAI_RxInit(I2S_Type *base, const sai_config_t *config); + +/*! + * @brief Sets the SAI Tx configuration structure to default values. + * @deprecated Do not use this function. It has been superceded by + * @ref SAI_GetClassicI2SConfig, @ref SAI_GetLeftJustifiedConfig , @ref SAI_GetRightJustifiedConfig, @ref + SAI_GetDSPConfig, @ref SAI_GetTDMConfig + * + * This API initializes the configuration structure for use in SAI_TxConfig(). + * The initialized structure can remain unchanged in SAI_TxConfig(), or it can be modified + * before calling SAI_TxConfig(). + * This is an example. + @code + sai_config_t config; + SAI_TxGetDefaultConfig(&config); + @endcode + * + * @param config pointer to master configuration structure + */ +void SAI_TxGetDefaultConfig(sai_config_t *config); + +/*! + * @brief Sets the SAI Rx configuration structure to default values. + * @deprecated Do not use this function. It has been superceded by + * @ref SAI_GetClassicI2SConfig, @ref SAI_GetLeftJustifiedConfig , @ref SAI_GetRightJustifiedConfig, @ref + SAI_GetDSPConfig, @ref SAI_GetTDMConfig + * + * This API initializes the configuration structure for use in SAI_RxConfig(). + * The initialized structure can remain unchanged in SAI_RxConfig() or it can be modified + * before calling SAI_RxConfig(). + * This is an example. + @code + sai_config_t config; + SAI_RxGetDefaultConfig(&config); + @endcode + * + * @param config pointer to master configuration structure + */ +void SAI_RxGetDefaultConfig(sai_config_t *config); + +/*! + * @brief Initializes the SAI peripheral. + * + * This API gates the SAI clock. The SAI module can't operate unless SAI_Init is called to enable the clock. + * + * @param base SAI base pointer. + */ +void SAI_Init(I2S_Type *base); + +/*! + * @brief De-initializes the SAI peripheral. + * + * This API gates the SAI clock. The SAI module can't operate unless SAI_TxInit + * or SAI_RxInit is called to enable the clock. + * + * @param base SAI base pointer. + */ +void SAI_Deinit(I2S_Type *base); + +/*! + * @brief Resets the SAI Tx. + * + * This function enables the software reset and FIFO reset of SAI Tx. After reset, clear the reset bit. + * + * @param base SAI base pointer + */ +void SAI_TxReset(I2S_Type *base); + +/*! + * @brief Resets the SAI Rx. + * + * This function enables the software reset and FIFO reset of SAI Rx. After reset, clear the reset bit. + * + * @param base SAI base pointer + */ +void SAI_RxReset(I2S_Type *base); + +/*! + * @brief Enables/disables the SAI Tx. + * + * @param base SAI base pointer. + * @param enable True means enable SAI Tx, false means disable. + */ +void SAI_TxEnable(I2S_Type *base, bool enable); + +/*! + * @brief Enables/disables the SAI Rx. + * + * @param base SAI base pointer. + * @param enable True means enable SAI Rx, false means disable. + */ +void SAI_RxEnable(I2S_Type *base, bool enable); + +/*! + * @brief Set Rx bit clock direction. + * + * Select bit clock direction, master or slave. + * + * @param base SAI base pointer. + * @param masterSlave reference sai_master_slave_t. + */ +static inline void SAI_TxSetBitClockDirection(I2S_Type *base, sai_master_slave_t masterSlave) +{ + if (masterSlave == kSAI_Master) + { + base->TCR2 |= I2S_TCR2_BCD_MASK; + } + else + { + base->TCR2 &= ~I2S_TCR2_BCD_MASK; + } +} + +/*! + * @brief Set Rx bit clock direction. + * + * Select bit clock direction, master or slave. + * + * @param base SAI base pointer. + * @param masterSlave reference sai_master_slave_t. + */ +static inline void SAI_RxSetBitClockDirection(I2S_Type *base, sai_master_slave_t masterSlave) +{ + if (masterSlave == kSAI_Master) + { + base->RCR2 |= I2S_RCR2_BCD_MASK; + } + else + { + base->RCR2 &= ~I2S_RCR2_BCD_MASK; + } +} + +/*! + * @brief Set Rx frame sync direction. + * + * Select frame sync direction, master or slave. + * + * @param base SAI base pointer. + * @param masterSlave reference sai_master_slave_t. + */ +static inline void SAI_RxSetFrameSyncDirection(I2S_Type *base, sai_master_slave_t masterSlave) +{ + if (masterSlave == kSAI_Master) + { + base->RCR4 |= I2S_RCR4_FSD_MASK; + } + else + { + base->RCR4 &= ~I2S_RCR4_FSD_MASK; + } +} + +/*! + * @brief Set Tx frame sync direction. + * + * Select frame sync direction, master or slave. + * + * @param base SAI base pointer. + * @param masterSlave reference sai_master_slave_t. + */ +static inline void SAI_TxSetFrameSyncDirection(I2S_Type *base, sai_master_slave_t masterSlave) +{ + if (masterSlave == kSAI_Master) + { + base->TCR4 |= I2S_TCR4_FSD_MASK; + } + else + { + base->TCR4 &= ~I2S_TCR4_FSD_MASK; + } +} + +/*! + * @brief Transmitter bit clock rate configurations. + * + * @param base SAI base pointer. + * @param sourceClockHz Bit clock source frequency. + * @param sampleRate Audio data sample rate. + * @param bitWidth Audio data bitWidth. + * @param channelNumbers Audio channel numbers. + */ +void SAI_TxSetBitClockRate( + I2S_Type *base, uint32_t sourceClockHz, uint32_t sampleRate, uint32_t bitWidth, uint32_t channelNumbers); + +/*! + * @brief Receiver bit clock rate configurations. + * + * @param base SAI base pointer. + * @param sourceClockHz Bit clock source frequency. + * @param sampleRate Audio data sample rate. + * @param bitWidth Audio data bitWidth. + * @param channelNumbers Audio channel numbers. + */ +void SAI_RxSetBitClockRate( + I2S_Type *base, uint32_t sourceClockHz, uint32_t sampleRate, uint32_t bitWidth, uint32_t channelNumbers); + +/*! + * @brief Transmitter Bit clock configurations. + * + * @param base SAI base pointer. + * @param masterSlave master or slave. + * @param config bit clock other configurations, can be NULL in slave mode. + */ +void SAI_TxSetBitclockConfig(I2S_Type *base, sai_master_slave_t masterSlave, sai_bit_clock_t *config); + +/*! + * @brief Receiver Bit clock configurations. + * + * @param base SAI base pointer. + * @param masterSlave master or slave. + * @param config bit clock other configurations, can be NULL in slave mode. + */ +void SAI_RxSetBitclockConfig(I2S_Type *base, sai_master_slave_t masterSlave, sai_bit_clock_t *config); + +#if (defined(FSL_FEATURE_SAI_HAS_MCR) && (FSL_FEATURE_SAI_HAS_MCR)) || \ + (defined(FSL_FEATURE_SAI_HAS_MCLKDIV_REGISTER) && (FSL_FEATURE_SAI_HAS_MCLKDIV_REGISTER)) +/*! + * @brief Master clock configurations. + * + * @param base SAI base pointer. + * @param config master clock configurations. + */ +void SAI_SetMasterClockConfig(I2S_Type *base, sai_master_clock_t *config); +#endif + +#if FSL_SAI_HAS_FIFO_EXTEND_FEATURE +/*! + * @brief SAI transmitter fifo configurations. + * + * @param base SAI base pointer. + * @param config fifo configurations. + */ +void SAI_TxSetFifoConfig(I2S_Type *base, sai_fifo_t *config); + +/*! + * @brief SAI receiver fifo configurations. + * + * @param base SAI base pointer. + * @param config fifo configurations. + */ +void SAI_RxSetFifoConfig(I2S_Type *base, sai_fifo_t *config); +#endif + +/*! + * @brief SAI transmitter Frame sync configurations. + * + * @param base SAI base pointer. + * @param masterSlave master or slave. + * @param config frame sync configurations, can be NULL in slave mode. + */ +void SAI_TxSetFrameSyncConfig(I2S_Type *base, sai_master_slave_t masterSlave, sai_frame_sync_t *config); + +/*! + * @brief SAI receiver Frame sync configurations. + * + * @param base SAI base pointer. + * @param masterSlave master or slave. + * @param config frame sync configurations, can be NULL in slave mode. + */ +void SAI_RxSetFrameSyncConfig(I2S_Type *base, sai_master_slave_t masterSlave, sai_frame_sync_t *config); + +/*! + * @brief SAI transmitter Serial data configurations. + * + * @param base SAI base pointer. + * @param config serial data configurations. + */ +void SAI_TxSetSerialDataConfig(I2S_Type *base, sai_serial_data_t *config); + +/*! + * @brief SAI receiver Serial data configurations. + * + * @param base SAI base pointer. + * @param config serial data configurations. + */ +void SAI_RxSetSerialDataConfig(I2S_Type *base, sai_serial_data_t *config); + +/*! + * @brief SAI transmitter configurations. + * + * @param base SAI base pointer. + * @param config transmitter configurations. + */ +void SAI_TxSetConfig(I2S_Type *base, sai_transceiver_t *config); + +/*! + * @brief SAI receiver configurations. + * + * @param base SAI base pointer. + * @param config receiver configurations. + */ +void SAI_RxSetConfig(I2S_Type *base, sai_transceiver_t *config); + +/*! + * @brief Get classic I2S mode configurations. + * + * @param config transceiver configurations. + * @param bitWidth audio data bitWidth. + * @param mode audio data channel. + * @param saiChannelMask mask value of the channel to be enable. + */ +void SAI_GetClassicI2SConfig(sai_transceiver_t *config, + sai_word_width_t bitWidth, + sai_mono_stereo_t mode, + uint32_t saiChannelMask); + +/*! + * @brief Get left justified mode configurations. + * + * @param config transceiver configurations. + * @param bitWidth audio data bitWidth. + * @param mode audio data channel. + * @param saiChannelMask mask value of the channel to be enable. + */ +void SAI_GetLeftJustifiedConfig(sai_transceiver_t *config, + sai_word_width_t bitWidth, + sai_mono_stereo_t mode, + uint32_t saiChannelMask); + +/*! + * @brief Get right justified mode configurations. + * + * @param config transceiver configurations. + * @param bitWidth audio data bitWidth. + * @param mode audio data channel. + * @param saiChannelMask mask value of the channel to be enable. + */ +void SAI_GetRightJustifiedConfig(sai_transceiver_t *config, + sai_word_width_t bitWidth, + sai_mono_stereo_t mode, + uint32_t saiChannelMask); + +/*! + * @brief Get TDM mode configurations. + * + * @param config transceiver configurations. + * @param frameSyncWidth length of frame sync. + * @param bitWidth audio data word width. + * @param dataWordNum word number in one frame. + * @param saiChannelMask mask value of the channel to be enable. + */ +void SAI_GetTDMConfig(sai_transceiver_t *config, + sai_frame_sync_len_t frameSyncWidth, + sai_word_width_t bitWidth, + uint32_t dataWordNum, + uint32_t saiChannelMask); + +/*! + * @brief Get DSP mode configurations. + * + * @note DSP mode is also called PCM mode which support MODE A and MODE B, + * DSP/PCM MODE A configuration flow. RX is similiar but uses SAI_RxSetConfig instead of SAI_TxSetConfig: + * @code + * SAI_GetDSPConfig(config, kSAI_FrameSyncLenOneBitClk, bitWidth, kSAI_Stereo, channelMask) + * config->frameSync.frameSyncEarly = true; + * SAI_TxSetConfig(base, config) + * @endcode + * + * DSP/PCM MODE B configuration flow for TX. RX is similiar but uses SAI_RxSetConfig instead of SAI_TxSetConfig: + * @code + * SAI_GetDSPConfig(config, kSAI_FrameSyncLenOneBitClk, bitWidth, kSAI_Stereo, channelMask) + * SAI_TxSetConfig(base, config) + * @endcode + * + * @param config transceiver configurations. + * @param frameSyncWidth length of frame sync. + * @param bitWidth audio data bitWidth. + * @param mode audio data channel. + * @param saiChannelMask mask value of the channel to enable. + */ +void SAI_GetDSPConfig(sai_transceiver_t *config, + sai_frame_sync_len_t frameSyncWidth, + sai_word_width_t bitWidth, + sai_mono_stereo_t mode, + uint32_t saiChannelMask); +/*! @} */ + +/*! + * @name Status + * @{ + */ + +/*! + * @brief Gets the SAI Tx status flag state. + * + * @param base SAI base pointer + * @return SAI Tx status flag value. Use the Status Mask to get the status value needed. + */ +static inline uint32_t SAI_TxGetStatusFlag(I2S_Type *base) +{ + return base->TCSR; +} + +/*! + * @brief Clears the SAI Tx status flag state. + * + * @param base SAI base pointer + * @param mask State mask. It can be a combination of the following source if defined: + * @arg kSAI_WordStartFlag + * @arg kSAI_SyncErrorFlag + * @arg kSAI_FIFOErrorFlag + */ +static inline void SAI_TxClearStatusFlags(I2S_Type *base, uint32_t mask) +{ + base->TCSR = ((base->TCSR & 0xFFE3FFFFU) | mask); +} + +/*! + * @brief Gets the SAI Tx status flag state. + * + * @param base SAI base pointer + * @return SAI Rx status flag value. Use the Status Mask to get the status value needed. + */ +static inline uint32_t SAI_RxGetStatusFlag(I2S_Type *base) +{ + return base->RCSR; +} + +/*! + * @brief Clears the SAI Rx status flag state. + * + * @param base SAI base pointer + * @param mask State mask. It can be a combination of the following sources if defined. + * @arg kSAI_WordStartFlag + * @arg kSAI_SyncErrorFlag + * @arg kSAI_FIFOErrorFlag + */ +static inline void SAI_RxClearStatusFlags(I2S_Type *base, uint32_t mask) +{ + base->RCSR = ((base->RCSR & 0xFFE3FFFFU) | mask); +} + +/*! + * @brief Do software reset or FIFO reset . + * + * FIFO reset means clear all the data in the FIFO, and make the FIFO pointer both to 0. + * Software reset means clear the Tx internal logic, including the bit clock, frame count etc. But software + * reset will not clear any configuration registers like TCR1~TCR5. + * This function will also clear all the error flags such as FIFO error, sync error etc. + * + * @param base SAI base pointer + * @param tresetType Reset type, FIFO reset or software reset + */ +void SAI_TxSoftwareReset(I2S_Type *base, sai_reset_type_t resetType); + +/*! + * @brief Do software reset or FIFO reset . + * + * FIFO reset means clear all the data in the FIFO, and make the FIFO pointer both to 0. + * Software reset means clear the Rx internal logic, including the bit clock, frame count etc. But software + * reset will not clear any configuration registers like RCR1~RCR5. + * This function will also clear all the error flags such as FIFO error, sync error etc. + * + * @param base SAI base pointer + * @param resetType Reset type, FIFO reset or software reset + */ +void SAI_RxSoftwareReset(I2S_Type *base, sai_reset_type_t resetType); + +/*! + * @brief Set the Tx channel FIFO enable mask. + * + * @param base SAI base pointer + * @param mask Channel enable mask, 0 means all channel FIFO disabled, 1 means channel 0 enabled, + * 3 means both channel 0 and channel 1 enabled. + */ +void SAI_TxSetChannelFIFOMask(I2S_Type *base, uint8_t mask); + +/*! + * @brief Set the Rx channel FIFO enable mask. + * + * @param base SAI base pointer + * @param mask Channel enable mask, 0 means all channel FIFO disabled, 1 means channel 0 enabled, + * 3 means both channel 0 and channel 1 enabled. + */ +void SAI_RxSetChannelFIFOMask(I2S_Type *base, uint8_t mask); + +/*! + * @brief Set the Tx data order. + * + * @param base SAI base pointer + * @param order Data order MSB or LSB + */ +void SAI_TxSetDataOrder(I2S_Type *base, sai_data_order_t order); + +/*! + * @brief Set the Rx data order. + * + * @param base SAI base pointer + * @param order Data order MSB or LSB + */ +void SAI_RxSetDataOrder(I2S_Type *base, sai_data_order_t order); + +/*! + * @brief Set the Tx data order. + * + * @param base SAI base pointer + * @param polarity + */ +void SAI_TxSetBitClockPolarity(I2S_Type *base, sai_clock_polarity_t polarity); + +/*! + * @brief Set the Rx data order. + * + * @param base SAI base pointer + * @param polarity + */ +void SAI_RxSetBitClockPolarity(I2S_Type *base, sai_clock_polarity_t polarity); + +/*! + * @brief Set the Tx data order. + * + * @param base SAI base pointer + * @param polarity + */ +void SAI_TxSetFrameSyncPolarity(I2S_Type *base, sai_clock_polarity_t polarity); + +/*! + * @brief Set the Rx data order. + * + * @param base SAI base pointer + * @param polarity + */ +void SAI_RxSetFrameSyncPolarity(I2S_Type *base, sai_clock_polarity_t polarity); + +#if defined(FSL_FEATURE_SAI_HAS_FIFO_PACKING) && FSL_FEATURE_SAI_HAS_FIFO_PACKING +/*! + * @brief Set Tx FIFO packing feature. + * + * @param base SAI base pointer. + * @param pack FIFO pack type. It is element of sai_fifo_packing_t. + */ +void SAI_TxSetFIFOPacking(I2S_Type *base, sai_fifo_packing_t pack); + +/*! + * @brief Set Rx FIFO packing feature. + * + * @param base SAI base pointer. + * @param pack FIFO pack type. It is element of sai_fifo_packing_t. + */ +void SAI_RxSetFIFOPacking(I2S_Type *base, sai_fifo_packing_t pack); +#endif /* FSL_FEATURE_SAI_HAS_FIFO_PACKING */ + +#if defined(FSL_FEATURE_SAI_HAS_FIFO_FUNCTION_AFTER_ERROR) && FSL_FEATURE_SAI_HAS_FIFO_FUNCTION_AFTER_ERROR +/*! + * @brief Set Tx FIFO error continue. + * + * FIFO error continue mode means SAI will keep running while FIFO error occurred. If this feature + * not enabled, SAI will hang and users need to clear FEF flag in TCSR register. + * + * @param base SAI base pointer. + * @param isEnabled Is FIFO error continue enabled, true means enable, false means disable. + */ +static inline void SAI_TxSetFIFOErrorContinue(I2S_Type *base, bool isEnabled) +{ + if (isEnabled) + { + base->TCR4 |= I2S_TCR4_FCONT_MASK; + } + else + { + base->TCR4 &= ~I2S_TCR4_FCONT_MASK; + } +} + +/*! + * @brief Set Rx FIFO error continue. + * + * FIFO error continue mode means SAI will keep running while FIFO error occurred. If this feature + * not enabled, SAI will hang and users need to clear FEF flag in RCSR register. + * + * @param base SAI base pointer. + * @param isEnabled Is FIFO error continue enabled, true means enable, false means disable. + */ +static inline void SAI_RxSetFIFOErrorContinue(I2S_Type *base, bool isEnabled) +{ + if (isEnabled) + { + base->RCR4 |= I2S_RCR4_FCONT_MASK; + } + else + { + base->RCR4 &= ~I2S_RCR4_FCONT_MASK; + } +} +#endif + +/*! @} */ + +/*! + * @name Interrupts + * @{ + */ + +/*! + * @brief Enables the SAI Tx interrupt requests. + * + * @param base SAI base pointer + * @param mask interrupt source + * The parameter can be a combination of the following sources if defined. + * @arg kSAI_WordStartInterruptEnable + * @arg kSAI_SyncErrorInterruptEnable + * @arg kSAI_FIFOWarningInterruptEnable + * @arg kSAI_FIFORequestInterruptEnable + * @arg kSAI_FIFOErrorInterruptEnable + */ +static inline void SAI_TxEnableInterrupts(I2S_Type *base, uint32_t mask) +{ + base->TCSR = ((base->TCSR & 0xFFE3FFFFU) | mask); +} + +/*! + * @brief Enables the SAI Rx interrupt requests. + * + * @param base SAI base pointer + * @param mask interrupt source + * The parameter can be a combination of the following sources if defined. + * @arg kSAI_WordStartInterruptEnable + * @arg kSAI_SyncErrorInterruptEnable + * @arg kSAI_FIFOWarningInterruptEnable + * @arg kSAI_FIFORequestInterruptEnable + * @arg kSAI_FIFOErrorInterruptEnable + */ +static inline void SAI_RxEnableInterrupts(I2S_Type *base, uint32_t mask) +{ + base->RCSR = ((base->RCSR & 0xFFE3FFFFU) | mask); +} + +/*! + * @brief Disables the SAI Tx interrupt requests. + * + * @param base SAI base pointer + * @param mask interrupt source + * The parameter can be a combination of the following sources if defined. + * @arg kSAI_WordStartInterruptEnable + * @arg kSAI_SyncErrorInterruptEnable + * @arg kSAI_FIFOWarningInterruptEnable + * @arg kSAI_FIFORequestInterruptEnable + * @arg kSAI_FIFOErrorInterruptEnable + */ +static inline void SAI_TxDisableInterrupts(I2S_Type *base, uint32_t mask) +{ + base->TCSR = ((base->TCSR & 0xFFE3FFFFU) & (~mask)); +} + +/*! + * @brief Disables the SAI Rx interrupt requests. + * + * @param base SAI base pointer + * @param mask interrupt source + * The parameter can be a combination of the following sources if defined. + * @arg kSAI_WordStartInterruptEnable + * @arg kSAI_SyncErrorInterruptEnable + * @arg kSAI_FIFOWarningInterruptEnable + * @arg kSAI_FIFORequestInterruptEnable + * @arg kSAI_FIFOErrorInterruptEnable + */ +static inline void SAI_RxDisableInterrupts(I2S_Type *base, uint32_t mask) +{ + base->RCSR = ((base->RCSR & 0xFFE3FFFFU) & (~mask)); +} + +/*! @} */ + +/*! + * @name DMA Control + * @{ + */ + +/*! + * @brief Enables/disables the SAI Tx DMA requests. + * @param base SAI base pointer + * @param mask DMA source + * The parameter can be combination of the following sources if defined. + * @arg kSAI_FIFOWarningDMAEnable + * @arg kSAI_FIFORequestDMAEnable + * @param enable True means enable DMA, false means disable DMA. + */ +static inline void SAI_TxEnableDMA(I2S_Type *base, uint32_t mask, bool enable) +{ + if (enable) + { + base->TCSR = ((base->TCSR & 0xFFE3FFFFU) | mask); + } + else + { + base->TCSR = ((base->TCSR & 0xFFE3FFFFU) & (~mask)); + } +} + +/*! + * @brief Enables/disables the SAI Rx DMA requests. + * @param base SAI base pointer + * @param mask DMA source + * The parameter can be a combination of the following sources if defined. + * @arg kSAI_FIFOWarningDMAEnable + * @arg kSAI_FIFORequestDMAEnable + * @param enable True means enable DMA, false means disable DMA. + */ +static inline void SAI_RxEnableDMA(I2S_Type *base, uint32_t mask, bool enable) +{ + if (enable) + { + base->RCSR = ((base->RCSR & 0xFFE3FFFFU) | mask); + } + else + { + base->RCSR = ((base->RCSR & 0xFFE3FFFFU) & (~mask)); + } +} + +/*! + * @brief Gets the SAI Tx data register address. + * + * This API is used to provide a transfer address for the SAI DMA transfer configuration. + * + * @param base SAI base pointer. + * @param channel Which data channel used. + * @return data register address. + */ +static inline uintptr_t SAI_TxGetDataRegisterAddress(I2S_Type *base, uint32_t channel) +{ + return (uintptr_t)(&(base->TDR)[channel]); +} + +/*! + * @brief Gets the SAI Rx data register address. + * + * This API is used to provide a transfer address for the SAI DMA transfer configuration. + * + * @param base SAI base pointer. + * @param channel Which data channel used. + * @return data register address. + */ +static inline uintptr_t SAI_RxGetDataRegisterAddress(I2S_Type *base, uint32_t channel) +{ + return (uintptr_t)(&(base->RDR)[channel]); +} + +/*! @} */ + +/*! + * @name Bus Operations + * @{ + */ + +/*! + * @brief Configures the SAI Tx audio format. + * @deprecated Do not use this function. It has been superceded by @ref SAI_TxSetConfig + * + * The audio format can be changed at run-time. This function configures the sample rate and audio data + * format to be transferred. + * + * @param base SAI base pointer. + * @param format Pointer to the SAI audio data format structure. + * @param mclkSourceClockHz SAI master clock source frequency in Hz. + * @param bclkSourceClockHz SAI bit clock source frequency in Hz. If the bit clock source is a master + * clock, this value should equal the masterClockHz. + */ +void SAI_TxSetFormat(I2S_Type *base, + sai_transfer_format_t *format, + uint32_t mclkSourceClockHz, + uint32_t bclkSourceClockHz); + +/*! + * @brief Configures the SAI Rx audio format. + * @deprecated Do not use this function. It has been superceded by @ref SAI_RxSetConfig + * + * The audio format can be changed at run-time. This function configures the sample rate and audio data + * format to be transferred. + * + * @param base SAI base pointer. + * @param format Pointer to the SAI audio data format structure. + * @param mclkSourceClockHz SAI master clock source frequency in Hz. + * @param bclkSourceClockHz SAI bit clock source frequency in Hz. If the bit clock source is a master + * clock, this value should equal the masterClockHz. + */ +void SAI_RxSetFormat(I2S_Type *base, + sai_transfer_format_t *format, + uint32_t mclkSourceClockHz, + uint32_t bclkSourceClockHz); + +/*! + * @brief Sends data using a blocking method. + * + * @note This function blocks by polling until data is ready to be sent. + * + * @param base SAI base pointer. + * @param channel Data channel used. + * @param bitWidth How many bits in an audio word; usually 8/16/24/32 bits. + * @param buffer Pointer to the data to be written. + * @param size Bytes to be written. + */ +void SAI_WriteBlocking(I2S_Type *base, uint32_t channel, uint32_t bitWidth, uint8_t *buffer, uint32_t size); + +/*! + * @brief Sends data to multi channel using a blocking method. + * + * @note This function blocks by polling until data is ready to be sent. + * + * @param base SAI base pointer. + * @param channel Data channel used. + * @param channelMask channel mask. + * @param bitWidth How many bits in an audio word; usually 8/16/24/32 bits. + * @param buffer Pointer to the data to be written. + * @param size Bytes to be written. + */ +void SAI_WriteMultiChannelBlocking( + I2S_Type *base, uint32_t channel, uint32_t channelMask, uint32_t bitWidth, uint8_t *buffer, uint32_t size); + +/*! + * @brief Writes data into SAI FIFO. + * + * @param base SAI base pointer. + * @param channel Data channel used. + * @param data Data needs to be written. + */ +static inline void SAI_WriteData(I2S_Type *base, uint32_t channel, uint32_t data) +{ + base->TDR[channel] = data; +} + +/*! + * @brief Receives data using a blocking method. + * + * @note This function blocks by polling until data is ready to be sent. + * + * @param base SAI base pointer. + * @param channel Data channel used. + * @param bitWidth How many bits in an audio word; usually 8/16/24/32 bits. + * @param buffer Pointer to the data to be read. + * @param size Bytes to be read. + */ +void SAI_ReadBlocking(I2S_Type *base, uint32_t channel, uint32_t bitWidth, uint8_t *buffer, uint32_t size); + +/*! + * @brief Receives multi channel data using a blocking method. + * + * @note This function blocks by polling until data is ready to be sent. + * + * @param base SAI base pointer. + * @param channel Data channel used. + * @param channelMask channel mask. + * @param bitWidth How many bits in an audio word; usually 8/16/24/32 bits. + * @param buffer Pointer to the data to be read. + * @param size Bytes to be read. + */ +void SAI_ReadMultiChannelBlocking( + I2S_Type *base, uint32_t channel, uint32_t channelMask, uint32_t bitWidth, uint8_t *buffer, uint32_t size); + +/*! + * @brief Reads data from the SAI FIFO. + * + * @param base SAI base pointer. + * @param channel Data channel used. + * @return Data in SAI FIFO. + */ +static inline uint32_t SAI_ReadData(I2S_Type *base, uint32_t channel) +{ + return base->RDR[channel]; +} + +/*! @} */ + +/*! + * @name Transactional + * @{ + */ + +/*! + * @brief Initializes the SAI Tx handle. + * + * This function initializes the Tx handle for the SAI Tx transactional APIs. Call + * this function once to get the handle initialized. + * + * @param base SAI base pointer + * @param handle SAI handle pointer. + * @param callback Pointer to the user callback function. + * @param userData User parameter passed to the callback function + */ +void SAI_TransferTxCreateHandle(I2S_Type *base, sai_handle_t *handle, sai_transfer_callback_t callback, void *userData); + +/*! + * @brief Initializes the SAI Rx handle. + * + * This function initializes the Rx handle for the SAI Rx transactional APIs. Call + * this function once to get the handle initialized. + * + * @param base SAI base pointer. + * @param handle SAI handle pointer. + * @param callback Pointer to the user callback function. + * @param userData User parameter passed to the callback function. + */ +void SAI_TransferRxCreateHandle(I2S_Type *base, sai_handle_t *handle, sai_transfer_callback_t callback, void *userData); + +/*! + * @brief SAI transmitter transfer configurations. + * + * This function initializes the Tx, include bit clock, frame sync, master clock, serial data and fifo + * configurations. + * + * @param base SAI base pointer. + * @param handle SAI handle pointer. + * @param config tranmitter configurations. + */ +void SAI_TransferTxSetConfig(I2S_Type *base, sai_handle_t *handle, sai_transceiver_t *config); + +/*! + * @brief SAI receiver transfer configurations. + * + * This function initializes the Rx, include bit clock, frame sync, master clock, serial data and fifo + * configurations. + * + * @param base SAI base pointer. + * @param handle SAI handle pointer. + * @param config receiver configurations. + */ +void SAI_TransferRxSetConfig(I2S_Type *base, sai_handle_t *handle, sai_transceiver_t *config); + +/*! + * @brief Configures the SAI Tx audio format. + * @deprecated Do not use this function. It has been superceded by @ref SAI_TransferTxSetConfig + * + * The audio format can be changed at run-time. This function configures the sample rate and audio data + * format to be transferred. + * + * @param base SAI base pointer. + * @param handle SAI handle pointer. + * @param format Pointer to the SAI audio data format structure. + * @param mclkSourceClockHz SAI master clock source frequency in Hz. + * @param bclkSourceClockHz SAI bit clock source frequency in Hz. If a bit clock source is a master + * clock, this value should equal the masterClockHz in format. + * @return Status of this function. Return value is the status_t. + */ +status_t SAI_TransferTxSetFormat(I2S_Type *base, + sai_handle_t *handle, + sai_transfer_format_t *format, + uint32_t mclkSourceClockHz, + uint32_t bclkSourceClockHz); + +/*! + * @brief Configures the SAI Rx audio format. + * @deprecated Do not use this function. It has been superceded by @ref SAI_TransferRxSetConfig + * + * The audio format can be changed at run-time. This function configures the sample rate and audio data + * format to be transferred. + * + * @param base SAI base pointer. + * @param handle SAI handle pointer. + * @param format Pointer to the SAI audio data format structure. + * @param mclkSourceClockHz SAI master clock source frequency in Hz. + * @param bclkSourceClockHz SAI bit clock source frequency in Hz. If a bit clock source is a master + * clock, this value should equal the masterClockHz in format. + * @return Status of this function. Return value is one of status_t. + */ +status_t SAI_TransferRxSetFormat(I2S_Type *base, + sai_handle_t *handle, + sai_transfer_format_t *format, + uint32_t mclkSourceClockHz, + uint32_t bclkSourceClockHz); + +/*! + * @brief Performs an interrupt non-blocking send transfer on SAI. + * + * @note This API returns immediately after the transfer initiates. + * Call the SAI_TxGetTransferStatusIRQ to poll the transfer status and check whether + * the transfer is finished. If the return status is not kStatus_SAI_Busy, the transfer + * is finished. + * + * @param base SAI base pointer. + * @param handle Pointer to the sai_handle_t structure which stores the transfer state. + * @param xfer Pointer to the sai_transfer_t structure. + * @retval kStatus_Success Successfully started the data receive. + * @retval kStatus_SAI_TxBusy Previous receive still not finished. + * @retval kStatus_InvalidArgument The input parameter is invalid. + */ +status_t SAI_TransferSendNonBlocking(I2S_Type *base, sai_handle_t *handle, sai_transfer_t *xfer); + +/*! + * @brief Performs an interrupt non-blocking receive transfer on SAI. + * + * @note This API returns immediately after the transfer initiates. + * Call the SAI_RxGetTransferStatusIRQ to poll the transfer status and check whether + * the transfer is finished. If the return status is not kStatus_SAI_Busy, the transfer + * is finished. + * + * @param base SAI base pointer + * @param handle Pointer to the sai_handle_t structure which stores the transfer state. + * @param xfer Pointer to the sai_transfer_t structure. + * @retval kStatus_Success Successfully started the data receive. + * @retval kStatus_SAI_RxBusy Previous receive still not finished. + * @retval kStatus_InvalidArgument The input parameter is invalid. + */ +status_t SAI_TransferReceiveNonBlocking(I2S_Type *base, sai_handle_t *handle, sai_transfer_t *xfer); + +/*! + * @brief Gets a set byte count. + * + * @param base SAI base pointer. + * @param handle Pointer to the sai_handle_t structure which stores the transfer state. + * @param count Bytes count sent. + * @retval kStatus_Success Succeed get the transfer count. + * @retval kStatus_NoTransferInProgress There is not a non-blocking transaction currently in progress. + */ +status_t SAI_TransferGetSendCount(I2S_Type *base, sai_handle_t *handle, size_t *count); + +/*! + * @brief Gets a received byte count. + * + * @param base SAI base pointer. + * @param handle Pointer to the sai_handle_t structure which stores the transfer state. + * @param count Bytes count received. + * @retval kStatus_Success Succeed get the transfer count. + * @retval kStatus_NoTransferInProgress There is not a non-blocking transaction currently in progress. + */ +status_t SAI_TransferGetReceiveCount(I2S_Type *base, sai_handle_t *handle, size_t *count); + +/*! + * @brief Aborts the current send. + * + * @note This API can be called any time when an interrupt non-blocking transfer initiates + * to abort the transfer early. + * + * @param base SAI base pointer. + * @param handle Pointer to the sai_handle_t structure which stores the transfer state. + */ +void SAI_TransferAbortSend(I2S_Type *base, sai_handle_t *handle); + +/*! + * @brief Aborts the current IRQ receive. + * + * @note This API can be called when an interrupt non-blocking transfer initiates + * to abort the transfer early. + * + * @param base SAI base pointer + * @param handle Pointer to the sai_handle_t structure which stores the transfer state. + */ +void SAI_TransferAbortReceive(I2S_Type *base, sai_handle_t *handle); + +/*! + * @brief Terminate all SAI send. + * + * This function will clear all transfer slots buffered in the sai queue. If users only want to abort the + * current transfer slot, please call SAI_TransferAbortSend. + * + * @param base SAI base pointer. + * @param handle SAI eDMA handle pointer. + */ +void SAI_TransferTerminateSend(I2S_Type *base, sai_handle_t *handle); + +/*! + * @brief Terminate all SAI receive. + * + * This function will clear all transfer slots buffered in the sai queue. If users only want to abort the + * current transfer slot, please call SAI_TransferAbortReceive. + * + * @param base SAI base pointer. + * @param handle SAI eDMA handle pointer. + */ +void SAI_TransferTerminateReceive(I2S_Type *base, sai_handle_t *handle); + +/*! + * @brief Tx interrupt handler. + * + * @param base SAI base pointer. + * @param handle Pointer to the sai_handle_t structure. + */ +void SAI_TransferTxHandleIRQ(I2S_Type *base, sai_handle_t *handle); + +/*! + * @brief Tx interrupt handler. + * + * @param base SAI base pointer. + * @param handle Pointer to the sai_handle_t structure. + */ +void SAI_TransferRxHandleIRQ(I2S_Type *base, sai_handle_t *handle); + +/*! @} */ + +#if defined(__cplusplus) +} +#endif /*_cplusplus*/ + +/*! @} */ + +#endif /* _FSL_SAI_H_ */
\ No newline at end of file diff --git a/bsps/arm/imxrt/mcux-sdk/drivers/sai/fsl_sai_edma.c b/bsps/arm/imxrt/mcux-sdk/drivers/sai/fsl_sai_edma.c new file mode 100644 index 0000000000..47aa11def9 --- /dev/null +++ b/bsps/arm/imxrt/mcux-sdk/drivers/sai/fsl_sai_edma.c @@ -0,0 +1,1033 @@ +/* + * Copyright (c) 2015, Freescale Semiconductor, Inc. + * Copyright 2016-2021 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "fsl_sai_edma.h" + +/* Component ID definition, used by tools. */ +#ifndef FSL_COMPONENT_ID +#define FSL_COMPONENT_ID "platform.drivers.sai_edma" +#endif + +/******************************************************************************* + * Definitions + ******************************************************************************/ +/* Used for 32byte aligned */ +#define STCD_ADDR(address) (edma_tcd_t *)(((uint32_t)(address) + 32UL) & ~0x1FU) + +static I2S_Type *const s_saiBases[] = I2S_BASE_PTRS; +/* Only support 2 and 4 channel */ +#define SAI_CHANNEL_MAP_MODULO(channel) (channel == 2U ? kEDMA_Modulo8bytes : kEDMA_Modulo16bytes) + +/*<! Structure definition for uart_edma_private_handle_t. The structure is private. */ +typedef struct sai_edma_private_handle +{ + I2S_Type *base; + sai_edma_handle_t *handle; +} sai_edma_private_handle_t; + +/*! @brief sai_edma_transfer_state, sai edma transfer state.*/ +enum +{ + kSAI_Busy = 0x0U, /*!< SAI is busy */ + kSAI_BusyLoopTransfer, /*!< SAI is busy for Loop transfer */ + kSAI_Idle, /*!< Transfer is done. */ +}; + +/*<! Private handle only used for internally. */ +static sai_edma_private_handle_t s_edmaPrivateHandle[ARRAY_SIZE(s_saiBases)][2]; + +/******************************************************************************* + * Prototypes + ******************************************************************************/ +/*! + * @brief Get the instance number for SAI. + * + * @param base SAI base pointer. + */ +static uint32_t SAI_GetInstance(I2S_Type *base); + +/*! + * @brief SAI EDMA callback for send. + * + * @param handle pointer to sai_edma_handle_t structure which stores the transfer state. + * @param userData Parameter for user callback. + * @param done If the DMA transfer finished. + * @param tcds The TCD index. + */ +static void SAI_TxEDMACallback(edma_handle_t *handle, void *userData, bool done, uint32_t tcds); + +/*! + * @brief SAI EDMA callback for receive. + * + * @param handle pointer to sai_edma_handle_t structure which stores the transfer state. + * @param userData Parameter for user callback. + * @param done If the DMA transfer finished. + * @param tcds The TCD index. + */ +static void SAI_RxEDMACallback(edma_handle_t *handle, void *userData, bool done, uint32_t tcds); + +/******************************************************************************* + * Code + ******************************************************************************/ +static uint32_t SAI_GetInstance(I2S_Type *base) +{ + uint32_t instance; + + /* Find the instance index from base address mappings. */ + for (instance = 0; instance < ARRAY_SIZE(s_saiBases); instance++) + { + if (s_saiBases[instance] == base) + { + break; + } + } + + assert(instance < ARRAY_SIZE(s_saiBases)); + + return instance; +} + +static void SAI_TxEDMACallback(edma_handle_t *handle, void *userData, bool done, uint32_t tcds) +{ + sai_edma_private_handle_t *privHandle = (sai_edma_private_handle_t *)userData; + sai_edma_handle_t *saiHandle = privHandle->handle; + status_t status = kStatus_SAI_TxBusy; + + if (saiHandle->state != (uint32_t)kSAI_BusyLoopTransfer) + { + if (saiHandle->queueDriver + tcds > (uint32_t)SAI_XFER_QUEUE_SIZE) + { + (void)memset(&saiHandle->saiQueue[saiHandle->queueDriver], 0, + sizeof(sai_transfer_t) * ((uint32_t)SAI_XFER_QUEUE_SIZE - saiHandle->queueDriver)); + (void)memset(&saiHandle->saiQueue[0U], 0, + sizeof(sai_transfer_t) * (saiHandle->queueDriver + tcds - (uint32_t)SAI_XFER_QUEUE_SIZE)); + } + else + { + (void)memset(&saiHandle->saiQueue[saiHandle->queueDriver], 0, sizeof(sai_transfer_t) * tcds); + } + saiHandle->queueDriver = (uint8_t)((saiHandle->queueDriver + tcds) % (uint32_t)SAI_XFER_QUEUE_SIZE); + + /* If all data finished, just stop the transfer */ + if (saiHandle->saiQueue[saiHandle->queueDriver].data == NULL) + { + /* Disable DMA enable bit */ + SAI_TxEnableDMA(privHandle->base, kSAI_FIFORequestDMAEnable, false); + EDMA_AbortTransfer(handle); + status = kStatus_SAI_TxIdle; + } + } + + /* If finished a block, call the callback function */ + if (saiHandle->callback != NULL) + { + (saiHandle->callback)(privHandle->base, saiHandle, status, saiHandle->userData); + } +} + +static void SAI_RxEDMACallback(edma_handle_t *handle, void *userData, bool done, uint32_t tcds) +{ + sai_edma_private_handle_t *privHandle = (sai_edma_private_handle_t *)userData; + sai_edma_handle_t *saiHandle = privHandle->handle; + status_t status = kStatus_SAI_RxBusy; + + if (saiHandle->state != (uint32_t)kSAI_BusyLoopTransfer) + { + if (saiHandle->queueDriver + tcds > (uint32_t)SAI_XFER_QUEUE_SIZE) + { + (void)memset(&saiHandle->saiQueue[saiHandle->queueDriver], 0, + sizeof(sai_transfer_t) * ((uint32_t)SAI_XFER_QUEUE_SIZE - saiHandle->queueDriver)); + (void)memset(&saiHandle->saiQueue[0U], 0, + sizeof(sai_transfer_t) * (saiHandle->queueDriver + tcds - (uint32_t)SAI_XFER_QUEUE_SIZE)); + } + else + { + (void)memset(&saiHandle->saiQueue[saiHandle->queueDriver], 0, sizeof(sai_transfer_t) * tcds); + } + saiHandle->queueDriver = (uint8_t)((saiHandle->queueDriver + tcds) % (uint32_t)SAI_XFER_QUEUE_SIZE); + + /* If all data finished, just stop the transfer */ + if (saiHandle->saiQueue[saiHandle->queueDriver].data == NULL) + { + /* Disable DMA enable bit */ + SAI_RxEnableDMA(privHandle->base, kSAI_FIFORequestDMAEnable, false); + EDMA_AbortTransfer(handle); + status = kStatus_SAI_RxIdle; + } + } + + /* If finished a block, call the callback function */ + if (saiHandle->callback != NULL) + { + (saiHandle->callback)(privHandle->base, saiHandle, status, saiHandle->userData); + } +} + +/*! + * brief Initializes the SAI eDMA handle. + * + * This function initializes the SAI master DMA handle, which can be used for other SAI master transactional APIs. + * Usually, for a specified SAI instance, call this API once to get the initialized handle. + * + * param base SAI base pointer. + * param handle SAI eDMA handle pointer. + * param base SAI peripheral base address. + * param callback Pointer to user callback function. + * param userData User parameter passed to the callback function. + * param dmaHandle eDMA handle pointer, this handle shall be static allocated by users. + */ +void SAI_TransferTxCreateHandleEDMA( + I2S_Type *base, sai_edma_handle_t *handle, sai_edma_callback_t callback, void *userData, edma_handle_t *txDmaHandle) +{ + assert((handle != NULL) && (txDmaHandle != NULL)); + + uint32_t instance = SAI_GetInstance(base); + + /* Zero the handle */ + (void)memset(handle, 0, sizeof(*handle)); + + /* Set sai base to handle */ + handle->dmaHandle = txDmaHandle; + handle->callback = callback; + handle->userData = userData; + + /* Set SAI state to idle */ + handle->state = (uint32_t)kSAI_Idle; + + s_edmaPrivateHandle[instance][0].base = base; + s_edmaPrivateHandle[instance][0].handle = handle; + + /* Need to use scatter gather */ + EDMA_InstallTCDMemory(txDmaHandle, (edma_tcd_t *)(STCD_ADDR(handle->tcd)), SAI_XFER_QUEUE_SIZE); + + /* Install callback for Tx dma channel */ + EDMA_SetCallback(txDmaHandle, SAI_TxEDMACallback, &s_edmaPrivateHandle[instance][0]); +} + +/*! + * brief Initializes the SAI Rx eDMA handle. + * + * This function initializes the SAI slave DMA handle, which can be used for other SAI master transactional APIs. + * Usually, for a specified SAI instance, call this API once to get the initialized handle. + * + * param base SAI base pointer. + * param handle SAI eDMA handle pointer. + * param base SAI peripheral base address. + * param callback Pointer to user callback function. + * param userData User parameter passed to the callback function. + * param dmaHandle eDMA handle pointer, this handle shall be static allocated by users. + */ +void SAI_TransferRxCreateHandleEDMA( + I2S_Type *base, sai_edma_handle_t *handle, sai_edma_callback_t callback, void *userData, edma_handle_t *rxDmaHandle) +{ + assert((handle != NULL) && (rxDmaHandle != NULL)); + + uint32_t instance = SAI_GetInstance(base); + + /* Zero the handle */ + (void)memset(handle, 0, sizeof(*handle)); + + /* Set sai base to handle */ + handle->dmaHandle = rxDmaHandle; + handle->callback = callback; + handle->userData = userData; + + /* Set SAI state to idle */ + handle->state = (uint32_t)kSAI_Idle; + + s_edmaPrivateHandle[instance][1].base = base; + s_edmaPrivateHandle[instance][1].handle = handle; + + /* Need to use scatter gather */ + EDMA_InstallTCDMemory(rxDmaHandle, STCD_ADDR(handle->tcd), SAI_XFER_QUEUE_SIZE); + + /* Install callback for Tx dma channel */ + EDMA_SetCallback(rxDmaHandle, SAI_RxEDMACallback, &s_edmaPrivateHandle[instance][1]); +} + +/*! + * brief Configures the SAI Tx audio format. + * + * deprecated Do not use this function. It has been superceded by ref SAI_TransferTxSetConfigEDMA + * + * The audio format can be changed at run-time. This function configures the sample rate and audio data + * format to be transferred. This function also sets the eDMA parameter according to formatting requirements. + * + * param base SAI base pointer. + * param handle SAI eDMA handle pointer. + * param format Pointer to SAI audio data format structure. + * param mclkSourceClockHz SAI master clock source frequency in Hz. + * param bclkSourceClockHz SAI bit clock source frequency in Hz. If bit clock source is master + * clock, this value should equals to masterClockHz in format. + * retval kStatus_Success Audio format set successfully. + * retval kStatus_InvalidArgument The input argument is invalid. + */ +void SAI_TransferTxSetFormatEDMA(I2S_Type *base, + sai_edma_handle_t *handle, + sai_transfer_format_t *format, + uint32_t mclkSourceClockHz, + uint32_t bclkSourceClockHz) +{ + assert((handle != NULL) && (format != NULL)); + + /* Configure the audio format to SAI registers */ + SAI_TxSetFormat(base, format, mclkSourceClockHz, bclkSourceClockHz); + + /* Get the transfer size from format, this should be used in EDMA configuration */ + if (format->bitWidth == 24U) + { + handle->bytesPerFrame = 4U; + } + else + { + handle->bytesPerFrame = (uint8_t)(format->bitWidth / 8U); + } + + /* Update the data channel SAI used */ + handle->channel = format->channel; + + /* Clear the channel enable bits until do a send/receive */ + base->TCR3 &= ~I2S_TCR3_TCE_MASK; +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + handle->count = (uint8_t)((uint32_t)FSL_FEATURE_SAI_FIFO_COUNTn(base) - format->watermark); +#else + handle->count = 1U; +#endif /* FSL_FEATURE_SAI_HAS_FIFO */ +} + +/*! + * brief Configures the SAI Tx. + * + * note SAI eDMA supports data transfer in a multiple SAI channels if the FIFO Combine feature is supported. + * To activate the multi-channel transfer enable SAI channels by filling the channelMask + * of sai_transceiver_t with the corresponding values of _sai_channel_mask enum, enable the FIFO Combine + * mode by assigning kSAI_FifoCombineModeEnabledOnWrite to the fifoCombine member of sai_fifo_combine_t + * which is a member of sai_transceiver_t. + * This is an example of multi-channel data transfer configuration step. + * code + * sai_transceiver_t config; + * SAI_GetClassicI2SConfig(&config, kSAI_WordWidth16bits, kSAI_Stereo, kSAI_Channel0Mask|kSAI_Channel1Mask); + * config.fifo.fifoCombine = kSAI_FifoCombineModeEnabledOnWrite; + * SAI_TransferTxSetConfigEDMA(I2S0, &edmaHandle, &config); + * endcode + * param base SAI base pointer. + * param handle SAI eDMA handle pointer. + * param saiConfig sai configurations. + */ +void SAI_TransferTxSetConfigEDMA(I2S_Type *base, sai_edma_handle_t *handle, sai_transceiver_t *saiConfig) +{ + assert((handle != NULL) && (saiConfig != NULL)); + + /* Configure the audio format to SAI registers */ + SAI_TxSetConfig(base, saiConfig); + +#if defined(FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) && FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE + /* Allow multi-channel transfer only if FIFO Combine mode is enabled */ + assert( + (saiConfig->channelNums <= 1U) || + ((saiConfig->channelNums > 1U) && ((saiConfig->fifo.fifoCombine == kSAI_FifoCombineModeEnabledOnWrite) || + (saiConfig->fifo.fifoCombine == kSAI_FifoCombineModeEnabledOnReadWrite)))); +#endif + + /* Get the transfer size from format, this should be used in EDMA configuration */ + if (saiConfig->serialData.dataWordLength == 24U) + { + handle->bytesPerFrame = 4U; + } + else + { + handle->bytesPerFrame = saiConfig->serialData.dataWordLength / 8U; + } + /* Update the data channel SAI used */ + handle->channel = saiConfig->startChannel; + handle->channelMask = saiConfig->channelMask; + handle->channelNums = saiConfig->channelNums; + + /* Clear the channel enable bits until do a send/receive */ + base->TCR3 &= ~I2S_TCR3_TCE_MASK; +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + handle->count = (uint8_t)((uint32_t)FSL_FEATURE_SAI_FIFO_COUNTn(base) - saiConfig->fifo.fifoWatermark); +#else + handle->count = 1U; +#endif /* FSL_FEATURE_SAI_HAS_FIFO */ +} + +/*! + * brief Configures the SAI Rx audio format. + * + * deprecated Do not use this function. It has been superceded by ref SAI_TransferRxSetConfigEDMA + * + * The audio format can be changed at run-time. This function configures the sample rate and audio data + * format to be transferred. This function also sets the eDMA parameter according to formatting requirements. + * + * param base SAI base pointer. + * param handle SAI eDMA handle pointer. + * param format Pointer to SAI audio data format structure. + * param mclkSourceClockHz SAI master clock source frequency in Hz. + * param bclkSourceClockHz SAI bit clock source frequency in Hz. If a bit clock source is the master + * clock, this value should equal to masterClockHz in format. + * retval kStatus_Success Audio format set successfully. + * retval kStatus_InvalidArgument The input argument is invalid. + */ +void SAI_TransferRxSetFormatEDMA(I2S_Type *base, + sai_edma_handle_t *handle, + sai_transfer_format_t *format, + uint32_t mclkSourceClockHz, + uint32_t bclkSourceClockHz) +{ + assert((handle != NULL) && (format != NULL)); + + /* Configure the audio format to SAI registers */ + SAI_RxSetFormat(base, format, mclkSourceClockHz, bclkSourceClockHz); + + /* Get the transfer size from format, this should be used in EDMA configuration */ + if (format->bitWidth == 24U) + { + handle->bytesPerFrame = 4U; + } + else + { + handle->bytesPerFrame = (uint8_t)(format->bitWidth / 8U); + } + + /* Update the data channel SAI used */ + handle->channel = format->channel; + + /* Clear the channel enable bits until do a send/receive */ + base->RCR3 &= ~I2S_RCR3_RCE_MASK; +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + handle->count = format->watermark; +#else + handle->count = 1U; +#endif /* FSL_FEATURE_SAI_HAS_FIFO */ +} + +/*! + * brief Configures the SAI Rx. + * + * note SAI eDMA supports data transfer in a multiple SAI channels if the FIFO Combine feature is supported. + * To activate the multi-channel transfer enable SAI channels by filling the channelMask + * of sai_transceiver_t with the corresponding values of _sai_channel_mask enum, enable the FIFO Combine + * mode by assigning kSAI_FifoCombineModeEnabledOnRead to the fifoCombine member of sai_fifo_combine_t + * which is a member of sai_transceiver_t. + * This is an example of multi-channel data transfer configuration step. + * code + * sai_transceiver_t config; + * SAI_GetClassicI2SConfig(&config, kSAI_WordWidth16bits, kSAI_Stereo, kSAI_Channel0Mask|kSAI_Channel1Mask); + * config.fifo.fifoCombine = kSAI_FifoCombineModeEnabledOnRead; + * SAI_TransferRxSetConfigEDMA(I2S0, &edmaHandle, &config); + * endcode + * param base SAI base pointer. + * param handle SAI eDMA handle pointer. + * param saiConfig sai configurations. + */ +void SAI_TransferRxSetConfigEDMA(I2S_Type *base, sai_edma_handle_t *handle, sai_transceiver_t *saiConfig) +{ + assert((handle != NULL) && (saiConfig != NULL)); + + /* Configure the audio format to SAI registers */ + SAI_RxSetConfig(base, saiConfig); + +#if defined(FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) && FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE + /* Allow multi-channel transfer only if FIFO Combine mode is enabled */ + assert( + (saiConfig->channelNums <= 1U) || + ((saiConfig->channelNums > 1U) && ((saiConfig->fifo.fifoCombine == kSAI_FifoCombineModeEnabledOnRead) || + (saiConfig->fifo.fifoCombine == kSAI_FifoCombineModeEnabledOnReadWrite)))); +#endif + + /* Get the transfer size from format, this should be used in EDMA configuration */ + if (saiConfig->serialData.dataWordLength == 24U) + { + handle->bytesPerFrame = 4U; + } + else + { + handle->bytesPerFrame = saiConfig->serialData.dataWordLength / 8U; + } + + /* Update the data channel SAI used */ + handle->channel = saiConfig->startChannel; + handle->channelMask = saiConfig->channelMask; + handle->channelNums = saiConfig->channelNums; + /* Clear the channel enable bits until do a send/receive */ + base->RCR3 &= ~I2S_RCR3_RCE_MASK; +#if defined(FSL_FEATURE_SAI_HAS_FIFO) && (FSL_FEATURE_SAI_HAS_FIFO) + handle->count = saiConfig->fifo.fifoWatermark; +#else + handle->count = 1U; +#endif /* FSL_FEATURE_SAI_HAS_FIFO */ +} + +/*! + * brief Performs a non-blocking SAI transfer using DMA. + * + * note This interface returns immediately after the transfer initiates. Call + * SAI_GetTransferStatus to poll the transfer status and check whether the SAI transfer is finished. + * + * This function support multi channel transfer, + * 1. for the sai IP support fifo combine mode, application should enable the fifo combine mode, no limitation + * on channel numbers + * 2. for the sai IP not support fifo combine mode, sai edma provide another solution which using + * EDMA modulo feature, but support 2 or 4 channels only. + * + * param base SAI base pointer. + * param handle SAI eDMA handle pointer. + * param xfer Pointer to the DMA transfer structure. + * retval kStatus_Success Start a SAI eDMA send successfully. + * retval kStatus_InvalidArgument The input argument is invalid. + * retval kStatus_TxBusy SAI is busy sending data. + */ +status_t SAI_TransferSendEDMA(I2S_Type *base, sai_edma_handle_t *handle, sai_transfer_t *xfer) +{ + assert((handle != NULL) && (xfer != NULL)); + + edma_transfer_config_t config = {0}; + uint32_t destAddr = SAI_TxGetDataRegisterAddress(base, handle->channel); + uint32_t destOffset = 0U; + + /* Check if input parameter invalid */ + if ((xfer->data == NULL) || (xfer->dataSize == 0U)) + { + return kStatus_InvalidArgument; + } + + if (handle->saiQueue[handle->queueUser].data != NULL) + { + return kStatus_SAI_QueueFull; + } + + /* Change the state of handle */ + handle->state = (uint32_t)kSAI_Busy; + + /* Update the queue state */ + handle->transferSize[handle->queueUser] = xfer->dataSize; + handle->saiQueue[handle->queueUser].data = xfer->data; + handle->saiQueue[handle->queueUser].dataSize = xfer->dataSize; + handle->queueUser = (handle->queueUser + 1U) % (uint8_t)SAI_XFER_QUEUE_SIZE; + +#if !(defined(FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) && FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) + if (handle->channelNums > 1U) + { + destOffset = sizeof(uint32_t); + } +#endif + + /* Prepare edma configure */ + EDMA_PrepareTransferConfig(&config, xfer->data, (uint32_t)handle->bytesPerFrame, (int16_t)handle->bytesPerFrame, + (uint32_t *)destAddr, (uint32_t)handle->bytesPerFrame, (int16_t)destOffset, + (uint32_t)handle->count * handle->bytesPerFrame, xfer->dataSize); + + /* Store the initially configured eDMA minor byte transfer count into the SAI handle */ + handle->nbytes = handle->count * handle->bytesPerFrame; + + if (EDMA_SubmitTransfer(handle->dmaHandle, &config) != kStatus_Success) + { + return kStatus_SAI_QueueFull; + } + +#if !(defined(FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) && FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) + if (handle->channelNums > 1U) + { + if ((handle->channelNums % 2U) != 0U) + { + return kStatus_InvalidArgument; + } + + EDMA_SetModulo(handle->dmaHandle->base, handle->dmaHandle->channel, kEDMA_ModuloDisable, + SAI_CHANNEL_MAP_MODULO(handle->channelNums)); + } +#endif + /* Start DMA transfer */ + EDMA_StartTransfer(handle->dmaHandle); + + /* Enable DMA enable bit */ + SAI_TxEnableDMA(base, kSAI_FIFORequestDMAEnable, true); + + /* Enable SAI Tx clock */ + SAI_TxEnable(base, true); + + /* Enable the channel FIFO */ + base->TCR3 |= I2S_TCR3_TCE(handle->channelMask); + + return kStatus_Success; +} + +/*! + * brief Performs a non-blocking SAI receive using eDMA. + * + * note This interface returns immediately after the transfer initiates. Call + * the SAI_GetReceiveRemainingBytes to poll the transfer status and check whether the SAI transfer is finished. + * + * This function support multi channel transfer, + * 1. for the sai IP support fifo combine mode, application should enable the fifo combine mode, no limitation + * on channel numbers + * 2. for the sai IP not support fifo combine mode, sai edma provide another solution which using + * EDMA modulo feature, but support 2 or 4 channels only. + * + * param base SAI base pointer + * param handle SAI eDMA handle pointer. + * param xfer Pointer to DMA transfer structure. + * retval kStatus_Success Start a SAI eDMA receive successfully. + * retval kStatus_InvalidArgument The input argument is invalid. + * retval kStatus_RxBusy SAI is busy receiving data. + */ +status_t SAI_TransferReceiveEDMA(I2S_Type *base, sai_edma_handle_t *handle, sai_transfer_t *xfer) +{ + assert((handle != NULL) && (xfer != NULL)); + + edma_transfer_config_t config = {0}; + uint32_t srcAddr = SAI_RxGetDataRegisterAddress(base, handle->channel); + uint32_t srcOffset = 0U; + + /* Check if input parameter invalid */ + if ((xfer->data == NULL) || (xfer->dataSize == 0U)) + { + return kStatus_InvalidArgument; + } + + if (handle->saiQueue[handle->queueUser].data != NULL) + { + return kStatus_SAI_QueueFull; + } + + /* Change the state of handle */ + handle->state = (uint32_t)kSAI_Busy; + + /* Update queue state */ + handle->transferSize[handle->queueUser] = xfer->dataSize; + handle->saiQueue[handle->queueUser].data = xfer->data; + handle->saiQueue[handle->queueUser].dataSize = xfer->dataSize; + handle->queueUser = (handle->queueUser + 1U) % (uint8_t)SAI_XFER_QUEUE_SIZE; + +#if !(defined(FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) && FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) + if (handle->channelNums > 1U) + { + srcOffset = sizeof(uint32_t); + } +#endif + + /* Prepare edma configure */ + EDMA_PrepareTransferConfig(&config, (uint32_t *)srcAddr, (uint32_t)handle->bytesPerFrame, (int16_t)srcOffset, + xfer->data, (uint32_t)handle->bytesPerFrame, (int16_t)handle->bytesPerFrame, + (uint32_t)handle->count * handle->bytesPerFrame, xfer->dataSize); + /* Store the initially configured eDMA minor byte transfer count into the SAI handle */ + handle->nbytes = handle->count * handle->bytesPerFrame; + + if (EDMA_SubmitTransfer(handle->dmaHandle, &config) != kStatus_Success) + { + return kStatus_SAI_QueueFull; + } + +#if !(defined(FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) && FSL_FEATURE_SAI_HAS_FIFO_COMBINE_MODE) + if (handle->channelNums > 1U) + { + if ((handle->channelNums % 2U) != 0U) + { + return kStatus_InvalidArgument; + } + + EDMA_SetModulo(handle->dmaHandle->base, handle->dmaHandle->channel, SAI_CHANNEL_MAP_MODULO(handle->channelNums), + kEDMA_ModuloDisable); + } +#endif + /* Start DMA transfer */ + EDMA_StartTransfer(handle->dmaHandle); + + /* Enable DMA enable bit */ + SAI_RxEnableDMA(base, kSAI_FIFORequestDMAEnable, true); + + /* Enable the channel FIFO */ + base->RCR3 |= I2S_RCR3_RCE(handle->channelMask); + + /* Enable SAI Rx clock */ + SAI_RxEnable(base, true); + + return kStatus_Success; +} + +/*! + * brief Performs a non-blocking SAI loop transfer using eDMA. + * + * note This function support loop transfer only,such as A->B->...->A, application must be aware of + * that the more counts of the loop transfer, then more tcd memory required, as the function use the tcd pool in + * sai_edma_handle_t, so application could redefine the SAI_XFER_QUEUE_SIZE to determine the proper TCD pool size. + * This function support one sai channel only. + * + * Once the loop transfer start, application can use function SAI_TransferAbortSendEDMA to stop the loop transfer. + * + * param base SAI base pointer. + * param handle SAI eDMA handle pointer. + * param xfer Pointer to the DMA transfer structure, should be a array with elements counts >=1(loopTransferCount). + * param loopTransferCount the counts of xfer array. + * retval kStatus_Success Start a SAI eDMA send successfully. + * retval kStatus_InvalidArgument The input argument is invalid. + */ +status_t SAI_TransferSendLoopEDMA(I2S_Type *base, + sai_edma_handle_t *handle, + sai_transfer_t *xfer, + uint32_t loopTransferCount) +{ + assert((handle != NULL) && (xfer != NULL)); + + edma_transfer_config_t config = {0}; + uint32_t destAddr = SAI_TxGetDataRegisterAddress(base, handle->channel); + sai_transfer_t *transfer = xfer; + edma_tcd_t *currentTCD = STCD_ADDR(handle->tcd); + uint32_t tcdIndex = 0U; + + /* Change the state of handle */ + handle->state = (uint32_t)kSAI_Busy; + + for (uint32_t i = 0U; i < loopTransferCount; i++) + { + transfer = &xfer[i]; + + if ((transfer->data == NULL) || (transfer->dataSize == 0U) || (tcdIndex >= (uint32_t)SAI_XFER_QUEUE_SIZE)) + { + return kStatus_InvalidArgument; + } + + /* Update the queue state */ + handle->transferSize[tcdIndex] = transfer->dataSize; + handle->saiQueue[tcdIndex].data = transfer->data; + handle->saiQueue[tcdIndex].dataSize = transfer->dataSize; + + /* Prepare edma configure */ + EDMA_PrepareTransfer(&config, transfer->data, handle->bytesPerFrame, (uint32_t *)destAddr, + handle->bytesPerFrame, (uint32_t)handle->count * handle->bytesPerFrame, transfer->dataSize, + kEDMA_MemoryToPeripheral); + + if (i == (loopTransferCount - 1U)) + { + EDMA_TcdSetTransferConfig(¤tTCD[tcdIndex], &config, ¤tTCD[0U]); + EDMA_TcdEnableInterrupts(¤tTCD[tcdIndex], (uint32_t)kEDMA_MajorInterruptEnable); + handle->state = (uint32_t)kSAI_BusyLoopTransfer; + break; + } + else + { + EDMA_TcdSetTransferConfig(¤tTCD[tcdIndex], &config, ¤tTCD[tcdIndex + 1U]); + EDMA_TcdEnableInterrupts(¤tTCD[tcdIndex], (uint32_t)kEDMA_MajorInterruptEnable); + } + + tcdIndex = tcdIndex + 1U; + } + + EDMA_InstallTCD(handle->dmaHandle->base, handle->dmaHandle->channel, ¤tTCD[0]); + /* Start DMA transfer */ + EDMA_StartTransfer(handle->dmaHandle); + + /* Enable DMA enable bit */ + SAI_TxEnableDMA(base, kSAI_FIFORequestDMAEnable, true); + + /* Enable SAI Tx clock */ + SAI_TxEnable(base, true); + + /* Enable the channel FIFO */ + base->TCR3 |= I2S_TCR3_TCE(1UL << handle->channel); + + return kStatus_Success; +} + +/*! + * brief Performs a non-blocking SAI loop transfer using eDMA. + * + * note This function support loop transfer only,such as A->B->...->A, application must be aware of + * that the more counts of the loop transfer, then more tcd memory required, as the function use the tcd pool in + * sai_edma_handle_t, so application could redefine the SAI_XFER_QUEUE_SIZE to determine the proper TCD pool size. + * This function support one sai channel only. + * + * Once the loop transfer start, application can use function SAI_TransferAbortReceiveEDMA to stop the loop transfer. + * + * param base SAI base pointer. + * param handle SAI eDMA handle pointer. + * param xfer Pointer to the DMA transfer structure, should be a array with elements counts >=1(loopTransferCount). + * param loopTransferCount the counts of xfer array. + * retval kStatus_Success Start a SAI eDMA receive successfully. + * retval kStatus_InvalidArgument The input argument is invalid. + */ +status_t SAI_TransferReceiveLoopEDMA(I2S_Type *base, + sai_edma_handle_t *handle, + sai_transfer_t *xfer, + uint32_t loopTransferCount) +{ + assert((handle != NULL) && (xfer != NULL)); + + edma_transfer_config_t config = {0}; + uint32_t srcAddr = SAI_RxGetDataRegisterAddress(base, handle->channel); + sai_transfer_t *transfer = xfer; + edma_tcd_t *currentTCD = STCD_ADDR(handle->tcd); + uint32_t tcdIndex = 0U; + + /* Change the state of handle */ + handle->state = (uint32_t)kSAI_Busy; + + for (uint32_t i = 0U; i < loopTransferCount; i++) + { + transfer = &xfer[i]; + + if ((tcdIndex >= (uint32_t)SAI_XFER_QUEUE_SIZE) || (xfer->data == NULL) || (xfer->dataSize == 0U)) + { + return kStatus_InvalidArgument; + } + + /* Update the queue state */ + handle->transferSize[tcdIndex] = transfer->dataSize; + handle->saiQueue[tcdIndex].data = transfer->data; + handle->saiQueue[tcdIndex].dataSize = transfer->dataSize; + + /* Prepare edma configure */ + EDMA_PrepareTransfer(&config, (uint32_t *)srcAddr, handle->bytesPerFrame, transfer->data, handle->bytesPerFrame, + (uint32_t)handle->count * handle->bytesPerFrame, transfer->dataSize, + kEDMA_PeripheralToMemory); + + if (i == (loopTransferCount - 1U)) + { + EDMA_TcdSetTransferConfig(¤tTCD[tcdIndex], &config, ¤tTCD[0U]); + EDMA_TcdEnableInterrupts(¤tTCD[tcdIndex], (uint32_t)kEDMA_MajorInterruptEnable); + handle->state = (uint32_t)kSAI_BusyLoopTransfer; + break; + } + else + { + EDMA_TcdSetTransferConfig(¤tTCD[tcdIndex], &config, ¤tTCD[tcdIndex + 1U]); + EDMA_TcdEnableInterrupts(¤tTCD[tcdIndex], (uint32_t)kEDMA_MajorInterruptEnable); + } + + tcdIndex = tcdIndex + 1U; + } + + EDMA_InstallTCD(handle->dmaHandle->base, handle->dmaHandle->channel, ¤tTCD[0]); + /* Start DMA transfer */ + EDMA_StartTransfer(handle->dmaHandle); + /* Enable DMA enable bit */ + SAI_RxEnableDMA(base, kSAI_FIFORequestDMAEnable, true); + + /* Enable the channel FIFO */ + base->RCR3 |= I2S_RCR3_RCE(1UL << handle->channel); + + /* Enable SAI Rx clock */ + SAI_RxEnable(base, true); + + return kStatus_Success; +} + +/*! + * brief Aborts a SAI transfer using eDMA. + * + * This function only aborts the current transfer slots, the other transfer slots' information still kept + * in the handler. If users want to terminate all transfer slots, just call SAI_TransferTerminateSendEDMA. + * + * param base SAI base pointer. + * param handle SAI eDMA handle pointer. + */ +void SAI_TransferAbortSendEDMA(I2S_Type *base, sai_edma_handle_t *handle) +{ + assert(handle != NULL); + + /* Disable dma */ + EDMA_AbortTransfer(handle->dmaHandle); + + /* Disable the channel FIFO */ + base->TCR3 &= ~I2S_TCR3_TCE_MASK; + + /* Disable DMA enable bit */ + SAI_TxEnableDMA(base, kSAI_FIFORequestDMAEnable, false); + + /* Disable Tx */ + SAI_TxEnable(base, false); + + /* If Tx is disabled, reset the FIFO pointer and clear error flags */ + if ((base->TCSR & I2S_TCSR_TE_MASK) == 0UL) + { + base->TCSR |= (I2S_TCSR_FR_MASK | I2S_TCSR_SR_MASK); + base->TCSR &= ~I2S_TCSR_SR_MASK; + } + + /* Handle the queue index */ + (void)memset(&handle->saiQueue[handle->queueDriver], 0, sizeof(sai_transfer_t)); + handle->queueDriver = (handle->queueDriver + 1U) % (uint8_t)SAI_XFER_QUEUE_SIZE; + + /* Set the handle state */ + handle->state = (uint32_t)kSAI_Idle; +} + +/*! + * brief Aborts a SAI receive using eDMA. + * + * This function only aborts the current transfer slots, the other transfer slots' information still kept + * in the handler. If users want to terminate all transfer slots, just call SAI_TransferTerminateReceiveEDMA. + * + * param base SAI base pointer. + * param handle SAI eDMA handle pointer. + */ +void SAI_TransferAbortReceiveEDMA(I2S_Type *base, sai_edma_handle_t *handle) +{ + assert(handle != NULL); + + /* Disable dma */ + EDMA_AbortTransfer(handle->dmaHandle); + + /* Disable the channel FIFO */ + base->RCR3 &= ~I2S_RCR3_RCE_MASK; + + /* Disable DMA enable bit */ + SAI_RxEnableDMA(base, kSAI_FIFORequestDMAEnable, false); + + /* Disable Rx */ + SAI_RxEnable(base, false); + + /* If Rx is disabled, reset the FIFO pointer and clear error flags */ + if ((base->RCSR & I2S_RCSR_RE_MASK) == 0UL) + { + base->RCSR |= (I2S_RCSR_FR_MASK | I2S_RCSR_SR_MASK); + base->RCSR &= ~I2S_RCSR_SR_MASK; + } + + /* Handle the queue index */ + (void)memset(&handle->saiQueue[handle->queueDriver], 0, sizeof(sai_transfer_t)); + handle->queueDriver = (handle->queueDriver + 1U) % (uint8_t)SAI_XFER_QUEUE_SIZE; + + /* Set the handle state */ + handle->state = (uint32_t)kSAI_Idle; +} + +/*! + * brief Terminate all SAI send. + * + * This function will clear all transfer slots buffered in the sai queue. If users only want to abort the + * current transfer slot, please call SAI_TransferAbortSendEDMA. + * + * param base SAI base pointer. + * param handle SAI eDMA handle pointer. + */ +void SAI_TransferTerminateSendEDMA(I2S_Type *base, sai_edma_handle_t *handle) +{ + assert(handle != NULL); + + /* Abort the current transfer */ + SAI_TransferAbortSendEDMA(base, handle); + + /* Clear all the internal information */ + (void)memset(handle->tcd, 0, sizeof(handle->tcd)); + (void)memset(handle->saiQueue, 0, sizeof(handle->saiQueue)); + (void)memset(handle->transferSize, 0, sizeof(handle->transferSize)); + + handle->queueUser = 0U; + handle->queueDriver = 0U; +} + +/*! + * brief Terminate all SAI receive. + * + * This function will clear all transfer slots buffered in the sai queue. If users only want to abort the + * current transfer slot, please call SAI_TransferAbortReceiveEDMA. + * + * param base SAI base pointer. + * param handle SAI eDMA handle pointer. + */ +void SAI_TransferTerminateReceiveEDMA(I2S_Type *base, sai_edma_handle_t *handle) +{ + assert(handle != NULL); + + /* Abort the current transfer */ + SAI_TransferAbortReceiveEDMA(base, handle); + + /* Clear all the internal information */ + (void)memset(handle->tcd, 0, sizeof(handle->tcd)); + (void)memset(handle->saiQueue, 0, sizeof(handle->saiQueue)); + (void)memset(handle->transferSize, 0, sizeof(handle->transferSize)); + + handle->queueUser = 0U; + handle->queueDriver = 0U; +} + +/*! + * brief Gets byte count sent by SAI. + * + * param base SAI base pointer. + * param handle SAI eDMA handle pointer. + * param count Bytes count sent by SAI. + * retval kStatus_Success Succeed get the transfer count. + * retval kStatus_NoTransferInProgress There is no non-blocking transaction in progress. + */ +status_t SAI_TransferGetSendCountEDMA(I2S_Type *base, sai_edma_handle_t *handle, size_t *count) +{ + assert(handle != NULL); + + status_t status = kStatus_Success; + + if (handle->state != (uint32_t)kSAI_Busy) + { + status = kStatus_NoTransferInProgress; + } + else + { + *count = (handle->transferSize[handle->queueDriver] - + (uint32_t)handle->nbytes * + EDMA_GetRemainingMajorLoopCount(handle->dmaHandle->base, handle->dmaHandle->channel)); + } + + return status; +} + +/*! + * brief Gets byte count received by SAI. + * + * param base SAI base pointer + * param handle SAI eDMA handle pointer. + * param count Bytes count received by SAI. + * retval kStatus_Success Succeed get the transfer count. + * retval kStatus_NoTransferInProgress There is no non-blocking transaction in progress. + */ +status_t SAI_TransferGetReceiveCountEDMA(I2S_Type *base, sai_edma_handle_t *handle, size_t *count) +{ + assert(handle != NULL); + + status_t status = kStatus_Success; + + if (handle->state != (uint32_t)kSAI_Busy) + { + status = kStatus_NoTransferInProgress; + } + else + { + *count = (handle->transferSize[handle->queueDriver] - + (uint32_t)handle->nbytes * + EDMA_GetRemainingMajorLoopCount(handle->dmaHandle->base, handle->dmaHandle->channel)); + } + + return status; +} + +/*! + * @rief Gets valid transfer slot. + * + * This function can be used to query the valid transfer request slot that the application can submit. + * It should be called in the critical section, that means the application could call it in the corresponding callback + * function or disable IRQ before calling it in the application, otherwise, the returned value may not correct. + * + * param base SAI base pointer + * param handle SAI eDMA handle pointer. + * retval valid slot count that application submit. + */ +uint32_t SAI_TransferGetValidTransferSlotsEDMA(I2S_Type *base, sai_edma_handle_t *handle) +{ + uint32_t validSlot = 0U; + + for (uint32_t i = 0U; i < (uint32_t)SAI_XFER_QUEUE_SIZE; i++) + { + if (handle->saiQueue[i].data == NULL) + { + validSlot++; + } + } + + return validSlot; +} diff --git a/bsps/arm/imxrt/mcux-sdk/drivers/sai/fsl_sai_edma.h b/bsps/arm/imxrt/mcux-sdk/drivers/sai/fsl_sai_edma.h new file mode 100644 index 0000000000..c5cb4d6ece --- /dev/null +++ b/bsps/arm/imxrt/mcux-sdk/drivers/sai/fsl_sai_edma.h @@ -0,0 +1,367 @@ +/* + * Copyright (c) 2015, Freescale Semiconductor, Inc. + * Copyright 2016-2021 NXP + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef _FSL_SAI_EDMA_H_ +#define _FSL_SAI_EDMA_H_ + +#include "fsl_edma.h" +#include "fsl_sai.h" + +/*! + * @addtogroup sai_edma SAI EDMA Driver + * @ingroup sai + * @{ + */ + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/*! @name Driver version */ +/*@{*/ +#define FSL_SAI_EDMA_DRIVER_VERSION (MAKE_VERSION(2, 5, 0)) /*!< Version 2.5.0 */ +/*@}*/ + +typedef struct sai_edma_handle sai_edma_handle_t; + +/*! @brief SAI eDMA transfer callback function for finish and error */ +typedef void (*sai_edma_callback_t)(I2S_Type *base, sai_edma_handle_t *handle, status_t status, void *userData); + +/*! @brief SAI DMA transfer handle, users should not touch the content of the handle.*/ +struct sai_edma_handle +{ + edma_handle_t *dmaHandle; /*!< DMA handler for SAI send */ + uint8_t nbytes; /*!< eDMA minor byte transfer count initially configured. */ + uint8_t bytesPerFrame; /*!< Bytes in a frame */ + uint8_t channelMask; /*!< Enabled channel mask value, reference _sai_channel_mask */ + uint8_t channelNums; /*!< total enabled channel nums */ + uint8_t channel; /*!< Which data channel */ + uint8_t count; /*!< The transfer data count in a DMA request */ + uint32_t state; /*!< Internal state for SAI eDMA transfer */ + sai_edma_callback_t callback; /*!< Callback for users while transfer finish or error occurs */ + void *userData; /*!< User callback parameter */ + uint8_t tcd[(SAI_XFER_QUEUE_SIZE + 1U) * sizeof(edma_tcd_t)]; /*!< TCD pool for eDMA transfer. */ + sai_transfer_t saiQueue[SAI_XFER_QUEUE_SIZE]; /*!< Transfer queue storing queued transfer. */ + size_t transferSize[SAI_XFER_QUEUE_SIZE]; /*!< Data bytes need to transfer */ + volatile uint8_t queueUser; /*!< Index for user to queue transfer. */ + volatile uint8_t queueDriver; /*!< Index for driver to get the transfer data and size */ +}; + +/******************************************************************************* + * APIs + ******************************************************************************/ +#if defined(__cplusplus) +extern "C" { +#endif + +/*! + * @name eDMA Transactional + * @{ + */ + +/*! + * @brief Initializes the SAI eDMA handle. + * + * This function initializes the SAI master DMA handle, which can be used for other SAI master transactional APIs. + * Usually, for a specified SAI instance, call this API once to get the initialized handle. + * + * @param base SAI base pointer. + * @param handle SAI eDMA handle pointer. + * @param base SAI peripheral base address. + * @param callback Pointer to user callback function. + * @param userData User parameter passed to the callback function. + * @param txDmaHandle eDMA handle pointer, this handle shall be static allocated by users. + */ +void SAI_TransferTxCreateHandleEDMA(I2S_Type *base, + sai_edma_handle_t *handle, + sai_edma_callback_t callback, + void *userData, + edma_handle_t *txDmaHandle); + +/*! + * @brief Initializes the SAI Rx eDMA handle. + * + * This function initializes the SAI slave DMA handle, which can be used for other SAI master transactional APIs. + * Usually, for a specified SAI instance, call this API once to get the initialized handle. + * + * @param base SAI base pointer. + * @param handle SAI eDMA handle pointer. + * @param base SAI peripheral base address. + * @param callback Pointer to user callback function. + * @param userData User parameter passed to the callback function. + * @param rxDmaHandle eDMA handle pointer, this handle shall be static allocated by users. + */ +void SAI_TransferRxCreateHandleEDMA(I2S_Type *base, + sai_edma_handle_t *handle, + sai_edma_callback_t callback, + void *userData, + edma_handle_t *rxDmaHandle); + +/*! + * @brief Configures the SAI Tx audio format. + * + * @deprecated Do not use this function. It has been superceded by @ref SAI_TransferTxSetConfigEDMA + * + * The audio format can be changed at run-time. This function configures the sample rate and audio data + * format to be transferred. This function also sets the eDMA parameter according to formatting requirements. + * + * @param base SAI base pointer. + * @param handle SAI eDMA handle pointer. + * @param format Pointer to SAI audio data format structure. + * @param mclkSourceClockHz SAI master clock source frequency in Hz. + * @param bclkSourceClockHz SAI bit clock source frequency in Hz. If bit clock source is master + * clock, this value should equals to masterClockHz in format. + * @retval kStatus_Success Audio format set successfully. + * @retval kStatus_InvalidArgument The input argument is invalid. + */ +void SAI_TransferTxSetFormatEDMA(I2S_Type *base, + sai_edma_handle_t *handle, + sai_transfer_format_t *format, + uint32_t mclkSourceClockHz, + uint32_t bclkSourceClockHz); + +/*! + * @brief Configures the SAI Rx audio format. + * + * @deprecated Do not use this function. It has been superceded by @ref SAI_TransferRxSetConfigEDMA + * + * The audio format can be changed at run-time. This function configures the sample rate and audio data + * format to be transferred. This function also sets the eDMA parameter according to formatting requirements. + * + * @param base SAI base pointer. + * @param handle SAI eDMA handle pointer. + * @param format Pointer to SAI audio data format structure. + * @param mclkSourceClockHz SAI master clock source frequency in Hz. + * @param bclkSourceClockHz SAI bit clock source frequency in Hz. If a bit clock source is the master + * clock, this value should equal to masterClockHz in format. + * @retval kStatus_Success Audio format set successfully. + * @retval kStatus_InvalidArgument The input argument is invalid. + */ +void SAI_TransferRxSetFormatEDMA(I2S_Type *base, + sai_edma_handle_t *handle, + sai_transfer_format_t *format, + uint32_t mclkSourceClockHz, + uint32_t bclkSourceClockHz); + +/*! + * @brief Configures the SAI Tx. + * + * @note SAI eDMA supports data transfer in a multiple SAI channels if the FIFO Combine feature is supported. + * To activate the multi-channel transfer enable SAI channels by filling the channelMask + * of sai_transceiver_t with the corresponding values of _sai_channel_mask enum, enable the FIFO Combine + * mode by assigning kSAI_FifoCombineModeEnabledOnWrite to the fifoCombine member of sai_fifo_combine_t + * which is a member of sai_transceiver_t. + * This is an example of multi-channel data transfer configuration step. + * @code + * sai_transceiver_t config; + * SAI_GetClassicI2SConfig(&config, kSAI_WordWidth16bits, kSAI_Stereo, kSAI_Channel0Mask|kSAI_Channel1Mask); + * config.fifo.fifoCombine = kSAI_FifoCombineModeEnabledOnWrite; + * SAI_TransferTxSetConfigEDMA(I2S0, &edmaHandle, &config); + * @endcode + * + * @param base SAI base pointer. + * @param handle SAI eDMA handle pointer. + * @param saiConfig sai configurations. + */ +void SAI_TransferTxSetConfigEDMA(I2S_Type *base, sai_edma_handle_t *handle, sai_transceiver_t *saiConfig); + +/*! + * @brief Configures the SAI Rx. + * + * @note SAI eDMA supports data transfer in a multiple SAI channels if the FIFO Combine feature is supported. + * To activate the multi-channel transfer enable SAI channels by filling the channelMask + * of sai_transceiver_t with the corresponding values of _sai_channel_mask enum, enable the FIFO Combine + * mode by assigning kSAI_FifoCombineModeEnabledOnRead to the fifoCombine member of sai_fifo_combine_t + * which is a member of sai_transceiver_t. + * This is an example of multi-channel data transfer configuration step. + * @code + * sai_transceiver_t config; + * SAI_GetClassicI2SConfig(&config, kSAI_WordWidth16bits, kSAI_Stereo, kSAI_Channel0Mask|kSAI_Channel1Mask); + * config.fifo.fifoCombine = kSAI_FifoCombineModeEnabledOnRead; + * SAI_TransferRxSetConfigEDMA(I2S0, &edmaHandle, &config); + * @endcode + * @param base SAI base pointer. + * @param handle SAI eDMA handle pointer. + * @param saiConfig sai configurations. + */ +void SAI_TransferRxSetConfigEDMA(I2S_Type *base, sai_edma_handle_t *handle, sai_transceiver_t *saiConfig); + +/*! + * @brief Performs a non-blocking SAI transfer using DMA. + * + * @note This interface returns immediately after the transfer initiates. Call + * SAI_GetTransferStatus to poll the transfer status and check whether the SAI transfer is finished. + * + * This function support multi channel transfer, + * 1. for the sai IP support fifo combine mode, application should enable the fifo combine mode, no limitation + * on channel numbers + * 2. for the sai IP not support fifo combine mode, sai edma provide another solution which using + * EDMA modulo feature, but support 2 or 4 channels only. + * + * @param base SAI base pointer. + * @param handle SAI eDMA handle pointer. + * @param xfer Pointer to the DMA transfer structure. + * @retval kStatus_Success Start a SAI eDMA send successfully. + * @retval kStatus_InvalidArgument The input argument is invalid. + * @retval kStatus_TxBusy SAI is busy sending data. + */ +status_t SAI_TransferSendEDMA(I2S_Type *base, sai_edma_handle_t *handle, sai_transfer_t *xfer); + +/*! + * @brief Performs a non-blocking SAI receive using eDMA. + * + * @note This interface returns immediately after the transfer initiates. Call + * the SAI_GetReceiveRemainingBytes to poll the transfer status and check whether the SAI transfer is finished. + * + * This function support multi channel transfer, + * 1. for the sai IP support fifo combine mode, application should enable the fifo combine mode, no limitation + * on channel numbers + * 2. for the sai IP not support fifo combine mode, sai edma provide another solution which using + * EDMA modulo feature, but support 2 or 4 channels only. + * + * @param base SAI base pointer + * @param handle SAI eDMA handle pointer. + * @param xfer Pointer to DMA transfer structure. + * @retval kStatus_Success Start a SAI eDMA receive successfully. + * @retval kStatus_InvalidArgument The input argument is invalid. + * @retval kStatus_RxBusy SAI is busy receiving data. + */ +status_t SAI_TransferReceiveEDMA(I2S_Type *base, sai_edma_handle_t *handle, sai_transfer_t *xfer); + +/*! + * @brief Performs a non-blocking SAI loop transfer using eDMA. + * + * @note This function support loop transfer only,such as A->B->...->A, application must be aware of + * that the more counts of the loop transfer, then more tcd memory required, as the function use the tcd pool in + * sai_edma_handle_t, so application could redefine the SAI_XFER_QUEUE_SIZE to determine the proper TCD pool size. + * This function support one sai channel only. + * + * Once the loop transfer start, application can use function SAI_TransferAbortSendEDMA to stop the loop transfer. + * + * @param base SAI base pointer. + * @param handle SAI eDMA handle pointer. + * @param xfer Pointer to the DMA transfer structure, should be a array with elements counts >=1(loopTransferCount). + * @param loopTransferCount the counts of xfer array. + * @retval kStatus_Success Start a SAI eDMA send successfully. + * @retval kStatus_InvalidArgument The input argument is invalid. + */ +status_t SAI_TransferSendLoopEDMA(I2S_Type *base, + sai_edma_handle_t *handle, + sai_transfer_t *xfer, + uint32_t loopTransferCount); + +/*! + * @brief Performs a non-blocking SAI loop transfer using eDMA. + * + * @note This function support loop transfer only,such as A->B->...->A, application must be aware of + * that the more counts of the loop transfer, then more tcd memory required, as the function use the tcd pool in + * sai_edma_handle_t, so application could redefine the SAI_XFER_QUEUE_SIZE to determine the proper TCD pool size. + * This function support one sai channel only. + * + * Once the loop transfer start, application can use function SAI_TransferAbortReceiveEDMA to stop the loop transfer. + * + * @param base SAI base pointer. + * @param handle SAI eDMA handle pointer. + * @param xfer Pointer to the DMA transfer structure, should be a array with elements counts >=1(loopTransferCount). + * @param loopTransferCount the counts of xfer array. + * @retval kStatus_Success Start a SAI eDMA receive successfully. + * @retval kStatus_InvalidArgument The input argument is invalid. + */ +status_t SAI_TransferReceiveLoopEDMA(I2S_Type *base, + sai_edma_handle_t *handle, + sai_transfer_t *xfer, + uint32_t loopTransferCount); + +/*! + * @brief Terminate all SAI send. + * + * This function will clear all transfer slots buffered in the sai queue. If users only want to abort the + * current transfer slot, please call SAI_TransferAbortSendEDMA. + * + * @param base SAI base pointer. + * @param handle SAI eDMA handle pointer. + */ +void SAI_TransferTerminateSendEDMA(I2S_Type *base, sai_edma_handle_t *handle); + +/*! + * @brief Terminate all SAI receive. + * + * This function will clear all transfer slots buffered in the sai queue. If users only want to abort the + * current transfer slot, please call SAI_TransferAbortReceiveEDMA. + * + * @param base SAI base pointer. + * @param handle SAI eDMA handle pointer. + */ +void SAI_TransferTerminateReceiveEDMA(I2S_Type *base, sai_edma_handle_t *handle); + +/*! + * @brief Aborts a SAI transfer using eDMA. + * + * This function only aborts the current transfer slots, the other transfer slots' information still kept + * in the handler. If users want to terminate all transfer slots, just call SAI_TransferTerminateSendEDMA. + * + * @param base SAI base pointer. + * @param handle SAI eDMA handle pointer. + */ +void SAI_TransferAbortSendEDMA(I2S_Type *base, sai_edma_handle_t *handle); + +/*! + * @brief Aborts a SAI receive using eDMA. + * + * This function only aborts the current transfer slots, the other transfer slots' information still kept + * in the handler. If users want to terminate all transfer slots, just call SAI_TransferTerminateReceiveEDMA. + * + * @param base SAI base pointer + * @param handle SAI eDMA handle pointer. + */ +void SAI_TransferAbortReceiveEDMA(I2S_Type *base, sai_edma_handle_t *handle); + +/*! + * @brief Gets byte count sent by SAI. + * + * @param base SAI base pointer. + * @param handle SAI eDMA handle pointer. + * @param count Bytes count sent by SAI. + * @retval kStatus_Success Succeed get the transfer count. + * @retval kStatus_NoTransferInProgress There is no non-blocking transaction in progress. + */ +status_t SAI_TransferGetSendCountEDMA(I2S_Type *base, sai_edma_handle_t *handle, size_t *count); + +/*! + * @brief Gets byte count received by SAI. + * + * @param base SAI base pointer + * @param handle SAI eDMA handle pointer. + * @param count Bytes count received by SAI. + * @retval kStatus_Success Succeed get the transfer count. + * @retval kStatus_NoTransferInProgress There is no non-blocking transaction in progress. + */ +status_t SAI_TransferGetReceiveCountEDMA(I2S_Type *base, sai_edma_handle_t *handle, size_t *count); + +/*! + * @brief Gets valid transfer slot. + * + * This function can be used to query the valid transfer request slot that the application can submit. + * It should be called in the critical section, that means the application could call it in the corresponding callback + * function or disable IRQ before calling it in the application, otherwise, the returned value may not correct. + * + * @param base SAI base pointer + * @param handle SAI eDMA handle pointer. + * @retval valid slot count that application submit. + */ +uint32_t SAI_TransferGetValidTransferSlotsEDMA(I2S_Type *base, sai_edma_handle_t *handle); + +/*! @} */ + +#if defined(__cplusplus) +} +#endif + +/*! + * @} + */ +#endif diff --git a/bsps/arm/imxrt/mcux-sdk/drivers/sai/fsl_sai_sdma.h b/bsps/arm/imxrt/mcux-sdk/drivers/sai/fsl_sai_sdma.h new file mode 100644 index 0000000000..64dd81b69c --- /dev/null +++ b/bsps/arm/imxrt/mcux-sdk/drivers/sai/fsl_sai_sdma.h @@ -0,0 +1,238 @@ +/* + * Copyright 2017 - 2021 NXP + * All rights reserved. + * + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef _FSL_SAI_SDMA_H_ +#define _FSL_SAI_SDMA_H_ + +#include "fsl_sai.h" +#include "fsl_sdma.h" + +/*! + * @addtogroup sai_sdma SAI SDMA Driver + * @ingroup sai + * @{ + */ + +/******************************************************************************* + * Definitions + ******************************************************************************/ + +/*! @name Driver version */ +/*@{*/ +#define FSL_SAI_SDMA_DRIVER_VERSION (MAKE_VERSION(2, 5, 3)) /*!< Version 2.5.3 */ +/*@}*/ + +typedef struct _sai_sdma_handle sai_sdma_handle_t; + +/*! @brief SAI SDMA transfer callback function for finish and error */ +typedef void (*sai_sdma_callback_t)(I2S_Type *base, sai_sdma_handle_t *handle, status_t status, void *userData); + +/*! @brief SAI DMA transfer handle, users should not touch the content of the handle. */ +struct _sai_sdma_handle +{ + sdma_handle_t *dmaHandle; /*!< DMA handler for SAI send */ + uint8_t bytesPerFrame; /*!< Bytes in a frame */ + uint8_t channel; /*!< start data channel */ + uint8_t channelNums; /*!< total transfer channel numbers, used for multififo */ + uint8_t channelMask; /*!< enabled channel mask value, refernece _sai_channel_mask */ + uint8_t fifoOffset; /*!< fifo address offset between multifo*/ + uint32_t count; /*!< The transfer data count in a DMA request */ + uint32_t state; /*!< Internal state for SAI SDMA transfer */ + uint32_t eventSource; /*!< SAI event source number */ + sai_sdma_callback_t callback; /*!< Callback for users while transfer finish or error occurs */ + void *userData; /*!< User callback parameter */ + sdma_buffer_descriptor_t bdPool[SAI_XFER_QUEUE_SIZE]; /*!< BD pool for SDMA transfer. */ + sai_transfer_t saiQueue[SAI_XFER_QUEUE_SIZE]; /*!< Transfer queue storing queued transfer. */ + size_t transferSize[SAI_XFER_QUEUE_SIZE]; /*!< Data bytes need to transfer */ + volatile uint8_t queueUser; /*!< Index for user to queue transfer. */ + volatile uint8_t queueDriver; /*!< Index for driver to get the transfer data and size */ +}; + +/******************************************************************************* + * APIs + ******************************************************************************/ +#if defined(__cplusplus) +extern "C" { +#endif +/*! + * @name SDMA Transactional + * @{ + */ + +/*! + * @brief Initializes the SAI SDMA handle. + * + * This function initializes the SAI master DMA handle, which can be used for other SAI master transactional APIs. + * Usually, for a specified SAI instance, call this API once to get the initialized handle. + * + * @param base SAI base pointer. + * @param handle SAI SDMA handle pointer. + * @param base SAI peripheral base address. + * @param callback Pointer to user callback function. + * @param userData User parameter passed to the callback function. + * @param dmaHandle SDMA handle pointer, this handle shall be static allocated by users. + * @param eventSource SAI event source number. + */ +void SAI_TransferTxCreateHandleSDMA(I2S_Type *base, + sai_sdma_handle_t *handle, + sai_sdma_callback_t callback, + void *userData, + sdma_handle_t *dmaHandle, + uint32_t eventSource); + +/*! + * @brief Initializes the SAI Rx SDMA handle. + * + * This function initializes the SAI slave DMA handle, which can be used for other SAI master transactional APIs. + * Usually, for a specified SAI instance, call this API once to get the initialized handle. + * + * @param base SAI base pointer. + * @param handle SAI SDMA handle pointer. + * @param base SAI peripheral base address. + * @param callback Pointer to user callback function. + * @param userData User parameter passed to the callback function. + * @param dmaHandle SDMA handle pointer, this handle shall be static allocated by users. + * @param eventSource SAI event source number. + */ +void SAI_TransferRxCreateHandleSDMA(I2S_Type *base, + sai_sdma_handle_t *handle, + sai_sdma_callback_t callback, + void *userData, + sdma_handle_t *dmaHandle, + uint32_t eventSource); + +/*! + * @brief Configures the SAI Tx audio format. + * + * The audio format can be changed at run-time. This function configures the sample rate and audio data + * format to be transferred. This function also sets the SDMA parameter according to formatting requirements. + * + * @param base SAI base pointer. + * @param handle SAI SDMA handle pointer. + * @param format Pointer to SAI audio data format structure. + * @param mclkSourceClockHz SAI master clock source frequency in Hz. + * @param bclkSourceClockHz SAI bit clock source frequency in Hz. If bit clock source is master + * clock, this value should equals to masterClockHz in format. + * @retval kStatus_Success Audio format set successfully. + * @retval kStatus_InvalidArgument The input argument is invalid. + */ +void SAI_TransferTxSetFormatSDMA(I2S_Type *base, + sai_sdma_handle_t *handle, + sai_transfer_format_t *format, + uint32_t mclkSourceClockHz, + uint32_t bclkSourceClockHz); + +/*! + * @brief Configures the SAI Rx audio format. + * + * The audio format can be changed at run-time. This function configures the sample rate and audio data + * format to be transferred. This function also sets the SDMA parameter according to formatting requirements. + * + * @param base SAI base pointer. + * @param handle SAI SDMA handle pointer. + * @param format Pointer to SAI audio data format structure. + * @param mclkSourceClockHz SAI master clock source frequency in Hz. + * @param bclkSourceClockHz SAI bit clock source frequency in Hz. If a bit clock source is the master + * clock, this value should equal to masterClockHz in format. + * @retval kStatus_Success Audio format set successfully. + * @retval kStatus_InvalidArgument The input argument is invalid. + */ +void SAI_TransferRxSetFormatSDMA(I2S_Type *base, + sai_sdma_handle_t *handle, + sai_transfer_format_t *format, + uint32_t mclkSourceClockHz, + uint32_t bclkSourceClockHz); + +/*! + * @brief Performs a non-blocking SAI transfer using DMA. + * + * @note This interface returns immediately after the transfer initiates. Call + * SAI_GetTransferStatus to poll the transfer status and check whether the SAI transfer is finished. + * + * @param base SAI base pointer. + * @param handle SAI SDMA handle pointer. + * @param xfer Pointer to the DMA transfer structure. + * @retval kStatus_Success Start a SAI SDMA send successfully. + * @retval kStatus_InvalidArgument The input argument is invalid. + * @retval kStatus_TxBusy SAI is busy sending data. + */ +status_t SAI_TransferSendSDMA(I2S_Type *base, sai_sdma_handle_t *handle, sai_transfer_t *xfer); + +/*! + * @brief Performs a non-blocking SAI receive using SDMA. + * + * @note This interface returns immediately after the transfer initiates. Call + * the SAI_GetReceiveRemainingBytes to poll the transfer status and check whether the SAI transfer is finished. + * + * @param base SAI base pointer + * @param handle SAI SDMA handle pointer. + * @param xfer Pointer to DMA transfer structure. + * @retval kStatus_Success Start a SAI SDMA receive successfully. + * @retval kStatus_InvalidArgument The input argument is invalid. + * @retval kStatus_RxBusy SAI is busy receiving data. + */ +status_t SAI_TransferReceiveSDMA(I2S_Type *base, sai_sdma_handle_t *handle, sai_transfer_t *xfer); + +/*! + * @brief Aborts a SAI transfer using SDMA. + * + * @param base SAI base pointer. + * @param handle SAI SDMA handle pointer. + */ +void SAI_TransferAbortSendSDMA(I2S_Type *base, sai_sdma_handle_t *handle); + +/*! + * @brief Aborts a SAI receive using SDMA. + * + * @param base SAI base pointer + * @param handle SAI SDMA handle pointer. + */ +void SAI_TransferAbortReceiveSDMA(I2S_Type *base, sai_sdma_handle_t *handle); + +/*! + * @brief Terminate all the SAI sdma receive transfer. + * + * @param base SAI base pointer. + * @param handle SAI SDMA handle pointer. + */ +void SAI_TransferTerminateReceiveSDMA(I2S_Type *base, sai_sdma_handle_t *handle); + +/*! + * @brief Terminate all the SAI sdma send transfer. + * + * @param base SAI base pointer. + * @param handle SAI SDMA handle pointer. + */ +void SAI_TransferTerminateSendSDMA(I2S_Type *base, sai_sdma_handle_t *handle); + +/*! + * brief Configures the SAI RX. + * + * param base SAI base pointer. + * param handle SAI SDMA handle pointer. + * param saiConig sai configurations. + */ +void SAI_TransferRxSetConfigSDMA(I2S_Type *base, sai_sdma_handle_t *handle, sai_transceiver_t *saiConfig); + +/*! + * brief Configures the SAI Tx. + * + * param base SAI base pointer. + * param handle SAI SDMA handle pointer. + * param saiConig sai configurations. + */ +void SAI_TransferTxSetConfigSDMA(I2S_Type *base, sai_sdma_handle_t *handle, sai_transceiver_t *saiConfig); +/*! @} */ + +#if defined(__cplusplus) +} +#endif + +/*! + * @} + */ +#endif |