You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mynewt.apache.org by ma...@apache.org on 2016/10/11 16:25:31 UTC
[12/45] incubator-mynewt-core git commit: add Kinetis SDK 2.0 built
for FRDM-K64F
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/f8f2ebbf/hw/mcu/nxp/src/ext/sdk-2.0-frdm-k64f_b160321/devices/MK64F12/drivers/fsl_sai.c
----------------------------------------------------------------------
diff --git a/hw/mcu/nxp/src/ext/sdk-2.0-frdm-k64f_b160321/devices/MK64F12/drivers/fsl_sai.c b/hw/mcu/nxp/src/ext/sdk-2.0-frdm-k64f_b160321/devices/MK64F12/drivers/fsl_sai.c
new file mode 100644
index 0000000..a45e9e6
--- /dev/null
+++ b/hw/mcu/nxp/src/ext/sdk-2.0-frdm-k64f_b160321/devices/MK64F12/drivers/fsl_sai.c
@@ -0,0 +1,1048 @@
+/*
+ * Copyright (c) 2015, Freescale Semiconductor, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * o Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ *
+ * o Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ *
+ * o Neither the name of Freescale Semiconductor, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "fsl_sai.h"
+
+/*******************************************************************************
+ * Definitations
+ ******************************************************************************/
+enum _sai_transfer_state
+{
+ kSAI_Busy = 0x0U, /*!< SAI is busy */
+ kSAI_Idle, /*!< Transfer is done. */
+ kSAI_Error /*!< Transfer error occured. */
+};
+
+/*******************************************************************************
+ * Prototypes
+ ******************************************************************************/
+#if defined(FSL_FEATURE_SAI_HAS_MCLKDIV_REGISTER) && (FSL_FEATURE_SAI_HAS_MCLKDIV_REGISTER)
+
+/*!
+ * @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.
+ */
+uint32_t SAI_GetInstance(I2S_Type *base);
+
+/*!
+ * @brief sends a piece of data in non-blocking way.
+ *
+ * @param base SAI base pointer
+ * @param channel Data channel used.
+ * @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 bitWidth, uint8_t *buffer, uint32_t size);
+
+/*!
+ * @brief Receive a piece of data in non-blocking way.
+ *
+ * @param base SAI base pointer
+ * @param channel Data channel used.
+ * @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 bitWidth, uint8_t *buffer, uint32_t size);
+/*******************************************************************************
+ * Variables
+ ******************************************************************************/
+/*!@brief SAI handle pointer */
+sai_handle_t *s_saiHandle[FSL_FEATURE_SOC_I2S_COUNT][2];
+/* Base pointer array */
+static I2S_Type *const s_saiBases[] = I2S_BASE_PTRS;
+/* IRQ number array */
+static const IRQn_Type s_saiTxIRQ[] = I2S_TX_IRQS;
+static const IRQn_Type s_saiRxIRQ[] = I2S_RX_IRQS;
+/* Clock name array */
+static const clock_ip_name_t s_saiClock[] = SAI_CLOCKS;
+
+/*******************************************************************************
+ * Code
+ ******************************************************************************/
+#if defined(FSL_FEATURE_SAI_HAS_MCLKDIV_REGISTER) && (FSL_FEATURE_SAI_HAS_MCLKDIV_REGISTER)
+static void SAI_SetMasterClockDivider(I2S_Type *base, uint32_t mclk_Hz, uint32_t mclkSrcClock_Hz)
+{
+ uint32_t freq = mclkSrcClock_Hz;
+ 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;
+
+ /*In order to prevent overflow */
+ freq /= 100;
+ mclk_Hz /= 100;
+
+ /* Compute the max fract number */
+ max_fract = mclk_Hz * 4096 / freq + 1;
+ if (max_fract > 256)
+ {
+ max_fract = 256;
+ }
+
+ /* Looking for the closet frequency */
+ for (fract = 1; fract < max_fract; fract++)
+ {
+ mul_freq = freq * fract;
+ remaind = mul_freq % mclk_Hz;
+ divide = mul_freq / mclk_Hz;
+
+ /* Find the exactly frequency */
+ if (remaind == 0)
+ {
+ current_fract = fract;
+ current_divide = mul_freq / mclk_Hz;
+ break;
+ }
+
+ /* Closer to next one, set the closest to next data */
+ if (remaind > mclk_Hz / 2)
+ {
+ remaind = mclk_Hz - remaind;
+ divide += 1;
+ }
+
+ /* 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 - 1) | I2S_MDR_FRACT(current_fract - 1);
+
+ /* Waiting for the divider updated */
+ while (base->MCR & I2S_MCR_DUF_MASK)
+ {
+ }
+}
+#endif /* FSL_FEATURE_SAI_HAS_MCLKDIV_REGISTER */
+
+uint32_t SAI_GetInstance(I2S_Type *base)
+{
+ uint32_t instance;
+
+ /* Find the instance index from base address mappings. */
+ for (instance = 0; instance < FSL_FEATURE_SOC_I2S_COUNT; instance++)
+ {
+ if (s_saiBases[instance] == base)
+ {
+ break;
+ }
+ }
+
+ assert(instance < FSL_FEATURE_SOC_I2S_COUNT);
+
+ return instance;
+}
+
+static void SAI_WriteNonBlocking(I2S_Type *base, uint32_t channel, uint32_t bitWidth, uint8_t *buffer, uint32_t size)
+{
+ uint32_t i = 0;
+ uint8_t j = 0;
+ uint8_t bytesPerWord = bitWidth / 8U;
+ uint32_t data = 0;
+ uint32_t temp = 0;
+
+ for (i = 0; i < size / bytesPerWord; i++)
+ {
+ for (j = 0; j < bytesPerWord; j++)
+ {
+ temp = (uint32_t)(*buffer);
+ data |= (temp << (8U * j));
+ buffer++;
+ }
+ base->TDR[channel] = data;
+ data = 0;
+ }
+}
+
+static void SAI_ReadNonBlocking(I2S_Type *base, uint32_t channel, uint32_t bitWidth, uint8_t *buffer, uint32_t size)
+{
+ uint32_t i = 0;
+ uint8_t j = 0;
+ uint8_t bytesPerWord = bitWidth / 8U;
+ uint32_t data = 0;
+
+ for (i = 0; i < size / bytesPerWord; i++)
+ {
+ data = base->RDR[channel];
+ for (j = 0; j < bytesPerWord; j++)
+ {
+ *buffer = (data >> (8U * j)) & 0xFF;
+ buffer++;
+ }
+ }
+}
+
+void SAI_TxInit(I2S_Type *base, const sai_config_t *config)
+{
+ uint32_t val = 0;
+
+ /* Enable the SAI clock */
+ CLOCK_EnableClock(s_saiClock[SAI_GetInstance(base)]);
+
+#if defined(FSL_FEATURE_SAI_HAS_MCR) && (FSL_FEATURE_SAI_HAS_MCR)
+ /* Configure Master clock output enable */
+ base->MCR = I2S_MCR_MOE(config->mclkOutputEnable);
+
+ /* Master clock source setting */
+ val = (base->MCR & ~I2S_MCR_MICS_MASK);
+ base->MCR = (val | I2S_MCR_MICS(config->mclkSource));
+#endif /* FSL_FEATURE_SAI_HAS_MCR */
+
+ /* Configure audio protocol */
+ switch (config->protocol)
+ {
+ case 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);
+ break;
+
+ case 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);
+ break;
+
+ case 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);
+ break;
+
+ case 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);
+ break;
+
+ case kSAI_BusPCMB:
+ 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);
+ break;
+
+ default:
+ break;
+ }
+
+ /* 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 */
+ switch (config->syncMode)
+ {
+ case kSAI_ModeAsync:
+ val = base->TCR2;
+ val &= ~I2S_TCR2_SYNC_MASK;
+ base->TCR2 = (val | I2S_TCR2_SYNC(0U));
+ break;
+ case 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));
+ break;
+ case kSAI_ModeSyncWithOtherTx:
+ val = base->TCR2;
+ val &= ~I2S_TCR2_SYNC_MASK;
+ base->TCR2 = (val | I2S_TCR2_SYNC(2U));
+ break;
+ case kSAI_ModeSyncWithOtherRx:
+ val = base->TCR2;
+ val &= ~I2S_TCR2_SYNC_MASK;
+ base->TCR2 = (val | I2S_TCR2_SYNC(3U));
+ break;
+ default:
+ break;
+ }
+}
+
+void SAI_RxInit(I2S_Type *base, const sai_config_t *config)
+{
+ uint32_t val = 0;
+
+ /* Enable SAI clock first. */
+ CLOCK_EnableClock(s_saiClock[SAI_GetInstance(base)]);
+
+#if defined(FSL_FEATURE_SAI_HAS_MCR) && (FSL_FEATURE_SAI_HAS_MCR)
+ /* Configure Master clock output enable */
+ base->MCR = I2S_MCR_MOE(config->mclkOutputEnable);
+
+ /* Master clock source setting */
+ val = (base->MCR & ~I2S_MCR_MICS_MASK);
+ base->MCR = (val | I2S_MCR_MICS(config->mclkSource));
+#endif /* FSL_FEATURE_SAI_HAS_MCR */
+
+ /* Configure audio protocol */
+ switch (config->protocol)
+ {
+ case 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);
+ break;
+
+ case 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);
+ break;
+
+ case 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);
+ break;
+
+ case 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);
+ break;
+
+ case kSAI_BusPCMB:
+ 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);
+ break;
+
+ default:
+ break;
+ }
+
+ /* 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 */
+ switch (config->syncMode)
+ {
+ case kSAI_ModeAsync:
+ val = base->RCR2;
+ val &= ~I2S_RCR2_SYNC_MASK;
+ base->RCR2 = (val | I2S_RCR2_SYNC(0U));
+ break;
+ case 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));
+ break;
+ case kSAI_ModeSyncWithOtherTx:
+ val = base->RCR2;
+ val &= ~I2S_RCR2_SYNC_MASK;
+ base->RCR2 = (val | I2S_RCR2_SYNC(2U));
+ break;
+ case kSAI_ModeSyncWithOtherRx:
+ val = base->RCR2;
+ val &= ~I2S_RCR2_SYNC_MASK;
+ base->RCR2 = (val | I2S_RCR2_SYNC(3U));
+ break;
+ default:
+ break;
+ }
+}
+
+void SAI_Deinit(I2S_Type *base)
+{
+ SAI_TxEnable(base, false);
+ SAI_RxEnable(base, false);
+ CLOCK_DisableClock(s_saiClock[SAI_GetInstance(base)]);
+}
+
+void SAI_TxGetDefaultConfig(sai_config_t *config)
+{
+ config->bclkSource = kSAI_BclkSourceMclkDiv;
+ config->masterSlave = kSAI_Master;
+ config->mclkSource = kSAI_MclkSourceSysclk;
+ config->protocol = kSAI_BusLeftJustified;
+ config->syncMode = kSAI_ModeAsync;
+#if defined(FSL_FEATURE_SAI_HAS_MCR) && (FSL_FEATURE_SAI_HAS_MCR)
+ config->mclkOutputEnable = true;
+#endif /* FSL_FEATURE_SAI_HAS_MCR */
+}
+
+void SAI_RxGetDefaultConfig(sai_config_t *config)
+{
+ config->bclkSource = kSAI_BclkSourceMclkDiv;
+ config->masterSlave = kSAI_Master;
+ config->mclkSource = kSAI_MclkSourceSysclk;
+ config->protocol = kSAI_BusLeftJustified;
+ config->syncMode = kSAI_ModeSync;
+#if defined(FSL_FEATURE_SAI_HAS_MCR) && (FSL_FEATURE_SAI_HAS_MCR)
+ config->mclkOutputEnable = true;
+#endif /* FSL_FEATURE_SAI_HAS_MCR */
+}
+
+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;
+}
+
+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;
+}
+
+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);
+ }
+ else
+ {
+ /* Should not close RE even sync with Rx */
+ base->TCSR = ((base->TCSR & 0xFFE3FFFFU) & (~I2S_TCSR_TE_MASK));
+ }
+}
+
+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);
+ }
+ else
+ {
+ base->RCSR = ((base->RCSR & 0xFFE3FFFFU) & (~I2S_RCSR_RE_MASK));
+ }
+}
+
+void SAI_TxSetFormat(I2S_Type *base,
+ sai_transfer_format_t *format,
+ uint32_t mclkSourceClockHz,
+ uint32_t bclkSourceClockHz)
+{
+ uint32_t 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 (base->MCR & I2S_MCR_MOE_MASK)
+ {
+ SAI_SetMasterClockDivider(base, format->masterClockHz, mclkSourceClockHz);
+ }
+#endif /* FSL_FEATURE_SAI_HAS_MCLKDIV_REGISTER */
+
+ /* Set bclk if needed */
+ if (base->TCR2 & I2S_TCR2_BCD_MASK)
+ {
+ base->TCR2 &= ~I2S_TCR2_DIV_MASK;
+ base->TCR2 |= I2S_TCR2_DIV((bclkSourceClockHz / bclk) / 2U - 1U);
+ }
+
+ /* Set bitWidth */
+ if (format->protocol == kSAI_BusRightJustified)
+ {
+ base->TCR5 = I2S_TCR5_WNW(31U) | I2S_TCR5_W0W(31U) | I2S_TCR5_FBT(31U);
+ }
+ else
+ {
+ base->TCR5 = I2S_TCR5_WNW(31U) | I2S_TCR5_W0W(31U) | I2S_TCR5_FBT(format->bitWidth - 1);
+ }
+
+ /* Set mono or stereo */
+ base->TMR = (uint32_t)format->stereo;
+
+ /* Set data channel */
+ base->TCR3 &= ~I2S_TCR3_TCE_MASK;
+ base->TCR3 |= I2S_TCR3_TCE(1U << format->channel);
+
+#if defined(FSL_FEATURE_SAI_FIFO_COUNT) && (FSL_FEATURE_SAI_FIFO_COUNT > 1)
+ /* Set watermark */
+ base->TCR1 = format->watermark;
+#endif /* FSL_FEATURE_SAI_FIFO_COUNT */
+}
+
+void SAI_RxSetFormat(I2S_Type *base,
+ sai_transfer_format_t *format,
+ uint32_t mclkSourceClockHz,
+ uint32_t bclkSourceClockHz)
+{
+ uint32_t 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 (base->MCR & I2S_MCR_MOE_MASK)
+ {
+ SAI_SetMasterClockDivider(base, format->masterClockHz, mclkSourceClockHz);
+ }
+#endif /* FSL_FEATURE_SAI_HAS_MCLKDIV_REGISTER */
+
+ /* Set bclk if needed */
+ if (base->RCR2 & I2S_RCR2_BCD_MASK)
+ {
+ base->RCR2 &= ~I2S_RCR2_DIV_MASK;
+ base->RCR2 |= I2S_RCR2_DIV((bclkSourceClockHz / bclk) / 2U - 1U);
+ }
+
+ /* Set bitWidth */
+ if (format->protocol == kSAI_BusRightJustified)
+ {
+ base->RCR5 = I2S_RCR5_WNW(31U) | I2S_RCR5_W0W(31U) | I2S_RCR5_FBT(31U);
+ }
+ else
+ {
+ base->RCR5 = I2S_RCR5_WNW(31U) | I2S_RCR5_W0W(31U) | I2S_RCR5_FBT(format->bitWidth - 1);
+ }
+
+ /* Set mono or stereo */
+ base->RMR = (uint32_t)format->stereo;
+
+ /* Set data channel */
+ base->RCR3 &= ~I2S_RCR3_RCE_MASK;
+ base->RCR3 |= I2S_RCR3_RCE(1U << format->channel);
+
+#if defined(FSL_FEATURE_SAI_FIFO_COUNT) && (FSL_FEATURE_SAI_FIFO_COUNT > 1)
+ /* Set watermark */
+ base->RCR1 = format->watermark;
+#endif /* FSL_FEATURE_SAI_FIFO_COUNT */
+}
+
+void SAI_WriteBlocking(I2S_Type *base, uint32_t channel, uint32_t bitWidth, uint8_t *buffer, uint32_t size)
+{
+ uint32_t i = 0;
+ uint8_t bytesPerWord = bitWidth / 8U;
+
+ for (i = 0; i < size; i++)
+ {
+ /* Wait until it can write data */
+ while (!(base->TCSR & I2S_TCSR_FWF_MASK))
+ {
+ }
+
+ SAI_WriteNonBlocking(base, channel, bitWidth, buffer, bytesPerWord);
+ buffer += bytesPerWord;
+ }
+
+ /* Wait until the last data is sent */
+ while (!(base->TCSR & I2S_TCSR_FWF_MASK))
+ {
+ }
+}
+
+void SAI_ReadBlocking(I2S_Type *base, uint32_t channel, uint32_t bitWidth, uint8_t *buffer, uint32_t size)
+{
+ uint32_t i = 0;
+ uint8_t bytesPerWord = bitWidth / 8U;
+
+ for (i = 0; i < size; i++)
+ {
+ /* Wait until data is received */
+ while (!(base->RCSR & I2S_RCSR_FWF_MASK))
+ {
+ }
+
+ SAI_ReadNonBlocking(base, channel, bitWidth, buffer, bytesPerWord);
+ buffer += bytesPerWord;
+ }
+}
+
+void SAI_TransferTxCreateHandle(I2S_Type *base, sai_handle_t *handle, sai_transfer_callback_t callback, void *userData)
+{
+ assert(handle);
+
+ s_saiHandle[SAI_GetInstance(base)][0] = handle;
+
+ handle->callback = callback;
+ handle->userData = userData;
+
+ /* Enable Tx irq */
+ EnableIRQ(s_saiTxIRQ[SAI_GetInstance(base)]);
+}
+
+void SAI_TransferRxCreateHandle(I2S_Type *base, sai_handle_t *handle, sai_transfer_callback_t callback, void *userData)
+{
+ assert(handle);
+
+ s_saiHandle[SAI_GetInstance(base)][1] = handle;
+
+ handle->callback = callback;
+ handle->userData = userData;
+
+ /* Enable Rx irq */
+ EnableIRQ(s_saiRxIRQ[SAI_GetInstance(base)]);
+}
+
+status_t SAI_TransferTxSetFormat(I2S_Type *base,
+ sai_handle_t *handle,
+ sai_transfer_format_t *format,
+ uint32_t mclkSourceClockHz,
+ uint32_t bclkSourceClockHz)
+{
+ assert(handle);
+
+ if ((mclkSourceClockHz < format->sampleRate_Hz) || (bclkSourceClockHz < format->sampleRate_Hz))
+ {
+ return kStatus_InvalidArgument;
+ }
+
+ /* Copy format to handle */
+ handle->bitWidth = format->bitWidth;
+#if defined(FSL_FEATURE_SAI_FIFO_COUNT) && (FSL_FEATURE_SAI_FIFO_COUNT > 1)
+ handle->watermark = format->watermark;
+#endif
+ handle->channel = format->channel;
+
+ SAI_TxSetFormat(base, format, mclkSourceClockHz, bclkSourceClockHz);
+
+ return kStatus_Success;
+}
+
+status_t SAI_TransferRxSetFormat(I2S_Type *base,
+ sai_handle_t *handle,
+ sai_transfer_format_t *format,
+ uint32_t mclkSourceClockHz,
+ uint32_t bclkSourceClockHz)
+{
+ assert(handle);
+
+ if ((mclkSourceClockHz < format->sampleRate_Hz) || (bclkSourceClockHz < format->sampleRate_Hz))
+ {
+ return kStatus_InvalidArgument;
+ }
+
+ /* Copy format to handle */
+ handle->bitWidth = format->bitWidth;
+#if defined(FSL_FEATURE_SAI_FIFO_COUNT) && (FSL_FEATURE_SAI_FIFO_COUNT > 1)
+ handle->watermark = format->watermark;
+#endif
+ handle->channel = format->channel;
+
+ SAI_RxSetFormat(base, format, mclkSourceClockHz, bclkSourceClockHz);
+
+ return kStatus_Success;
+}
+
+status_t SAI_TransferSendNonBlocking(I2S_Type *base, sai_handle_t *handle, sai_transfer_t *xfer)
+{
+ assert(handle);
+
+ /* Check if the queue is full */
+ if (handle->saiQueue[handle->queueUser].data)
+ {
+ 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 + 1) % SAI_XFER_QUEUE_SIZE;
+
+ /* Set the state to busy */
+ handle->state = kSAI_Busy;
+
+/* Enable interrupt */
+#if defined(FSL_FEATURE_SAI_FIFO_COUNT) && (FSL_FEATURE_SAI_FIFO_COUNT > 1)
+ /* Use FIFO request interrupt and fifo error*/
+ SAI_TxEnableInterrupts(base, kSAI_FIFOErrorInterruptEnable | kSAI_FIFORequestInterruptEnable);
+#else
+ SAI_TxEnableInterrupts(base, kSAI_FIFOErrorInterruptEnable | kSAI_FIFOWarningInterruptEnable);
+#endif /* FSL_FEATURE_SAI_FIFO_COUNT */
+
+ /* Enable Tx transfer */
+ SAI_TxEnable(base, true);
+
+ return kStatus_Success;
+}
+
+status_t SAI_TransferReceiveNonBlocking(I2S_Type *base, sai_handle_t *handle, sai_transfer_t *xfer)
+{
+ assert(handle);
+
+ /* Check if the queue is full */
+ if (handle->saiQueue[handle->queueUser].data)
+ {
+ 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 + 1) % SAI_XFER_QUEUE_SIZE;
+
+ /* Set state to busy */
+ handle->state = kSAI_Busy;
+
+/* Enable interrupt */
+#if defined(FSL_FEATURE_SAI_FIFO_COUNT) && (FSL_FEATURE_SAI_FIFO_COUNT > 1)
+ /* Use FIFO request interrupt and fifo error*/
+ SAI_RxEnableInterrupts(base, kSAI_FIFOErrorInterruptEnable | kSAI_FIFORequestInterruptEnable);
+#else
+ SAI_RxEnableInterrupts(base, kSAI_FIFOErrorInterruptEnable | kSAI_FIFOWarningInterruptEnable);
+#endif /* FSL_FEATURE_SAI_FIFO_COUNT */
+
+ /* Enable Rx transfer */
+ SAI_RxEnable(base, true);
+
+ return kStatus_Success;
+}
+
+status_t SAI_TransferGetSendCount(I2S_Type *base, sai_handle_t *handle, size_t *count)
+{
+ assert(handle);
+
+ status_t status = kStatus_Success;
+
+ if (handle->state != kSAI_Busy)
+ {
+ status = kStatus_NoTransferInProgress;
+ }
+ else
+ {
+ *count = (handle->transferSize[handle->queueDriver] - handle->saiQueue[handle->queueDriver].dataSize);
+ }
+
+ return status;
+}
+
+status_t SAI_TransferGetReceiveCount(I2S_Type *base, sai_handle_t *handle, size_t *count)
+{
+ assert(handle);
+
+ status_t status = kStatus_Success;
+
+ if (handle->state != kSAI_Busy)
+ {
+ status = kStatus_NoTransferInProgress;
+ }
+ else
+ {
+ *count = (handle->transferSize[handle->queueDriver] - handle->saiQueue[handle->queueDriver].dataSize);
+ }
+
+ return status;
+}
+
+void SAI_TransferAbortSend(I2S_Type *base, sai_handle_t *handle)
+{
+ assert(handle);
+
+ /* Stop Tx transfer and disable interrupt */
+ SAI_TxEnable(base, false);
+#if defined(FSL_FEATURE_SAI_FIFO_COUNT) && (FSL_FEATURE_SAI_FIFO_COUNT > 1)
+ /* Use FIFO request interrupt and fifo error */
+ SAI_TxDisableInterrupts(base, kSAI_FIFOErrorInterruptEnable | kSAI_FIFORequestInterruptEnable);
+#else
+ SAI_TxDisableInterrupts(base, kSAI_FIFOErrorInterruptEnable | kSAI_FIFOWarningInterruptEnable);
+#endif /* FSL_FEATURE_SAI_FIFO_COUNT */
+
+ handle->state = kSAI_Idle;
+
+ /* Clear the queue */
+ memset(handle->saiQueue, 0, sizeof(sai_transfer_t) * SAI_XFER_QUEUE_SIZE);
+ handle->queueDriver = 0;
+ handle->queueUser = 0;
+}
+
+void SAI_TransferAbortReceive(I2S_Type *base, sai_handle_t *handle)
+{
+ assert(handle);
+
+ /* Stop Tx transfer and disable interrupt */
+ SAI_RxEnable(base, false);
+#if defined(FSL_FEATURE_SAI_FIFO_COUNT) && (FSL_FEATURE_SAI_FIFO_COUNT > 1)
+ /* Use FIFO request interrupt and fifo error */
+ SAI_RxDisableInterrupts(base, kSAI_FIFOErrorInterruptEnable | kSAI_FIFORequestInterruptEnable);
+#else
+ SAI_RxDisableInterrupts(base, kSAI_FIFOErrorInterruptEnable | kSAI_FIFOWarningInterruptEnable);
+#endif /* FSL_FEATURE_SAI_FIFO_COUNT */
+
+ handle->state = kSAI_Idle;
+
+ /* Clear the queue */
+ memset(handle->saiQueue, 0, sizeof(sai_transfer_t) * SAI_XFER_QUEUE_SIZE);
+ handle->queueDriver = 0;
+ handle->queueUser = 0;
+}
+
+void SAI_TransferTxHandleIRQ(I2S_Type *base, sai_handle_t *handle)
+{
+ assert(handle);
+
+ uint8_t *buffer = handle->saiQueue[handle->queueDriver].data;
+ uint8_t dataSize = handle->bitWidth / 8U;
+
+ /* Handle Error */
+ if (base->TCSR & I2S_TCSR_FEF_MASK)
+ {
+ /* Clear FIFO error flag to continue transfer */
+ SAI_TxClearStatusFlags(base, kSAI_FIFOErrorFlag);
+
+ /* Call the callback */
+ if (handle->callback)
+ {
+ (handle->callback)(base, handle, kStatus_SAI_TxError, handle->userData);
+ }
+ }
+
+/* Handle transfer */
+#if defined(FSL_FEATURE_SAI_FIFO_COUNT) && (FSL_FEATURE_SAI_FIFO_COUNT > 1)
+ if (base->TCSR & I2S_TCSR_FRF_MASK)
+ {
+ /* Judge if the data need to transmit is less than space */
+ uint8_t size = MIN((handle->saiQueue[handle->queueDriver].dataSize),
+ (size_t)((FSL_FEATURE_SAI_FIFO_COUNT - handle->watermark) * dataSize));
+
+ /* Copy the data from sai buffer to FIFO */
+ SAI_WriteNonBlocking(base, handle->channel, handle->bitWidth, buffer, size);
+
+ /* Update the internal counter */
+ handle->saiQueue[handle->queueDriver].dataSize -= size;
+ handle->saiQueue[handle->queueDriver].data += size;
+ }
+#else
+ if (base->TCSR & I2S_TCSR_FWF_MASK)
+ {
+ uint8_t size = MIN((handle->saiQueue[handle->queueDriver].dataSize), dataSize);
+
+ SAI_WriteNonBlocking(base, handle->channel, handle->bitWidth, buffer, size);
+
+ /* Update internal counter */
+ handle->saiQueue[handle->queueDriver].dataSize -= size;
+ handle->saiQueue[handle->queueDriver].data += size;
+ }
+#endif /* FSL_FEATURE_SAI_FIFO_COUNT */
+
+ /* If finished a blcok, call the callback function */
+ if (handle->saiQueue[handle->queueDriver].dataSize == 0U)
+ {
+ memset(&handle->saiQueue[handle->queueDriver], 0, sizeof(sai_transfer_t));
+ handle->queueDriver = (handle->queueDriver + 1) % SAI_XFER_QUEUE_SIZE;
+ if (handle->callback)
+ {
+ (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);
+ }
+}
+
+void SAI_TransferRxHandleIRQ(I2S_Type *base, sai_handle_t *handle)
+{
+ assert(handle);
+
+ uint8_t *buffer = handle->saiQueue[handle->queueDriver].data;
+ uint8_t dataSize = handle->bitWidth / 8U;
+
+ /* Handle Error */
+ if (base->RCSR & I2S_RCSR_FEF_MASK)
+ {
+ /* Clear FIFO error flag to continue transfer */
+ SAI_RxClearStatusFlags(base, kSAI_FIFOErrorFlag);
+
+ /* Call the callback */
+ if (handle->callback)
+ {
+ (handle->callback)(base, handle, kStatus_SAI_RxError, handle->userData);
+ }
+ }
+
+/* Handle transfer */
+#if defined(FSL_FEATURE_SAI_FIFO_COUNT) && (FSL_FEATURE_SAI_FIFO_COUNT > 1)
+ if (base->RCSR & I2S_RCSR_FRF_MASK)
+ {
+ /* Judge if the data need to transmit is less than space */
+ uint8_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->bitWidth, buffer, size);
+
+ /* Update the internal counter */
+ handle->saiQueue[handle->queueDriver].dataSize -= size;
+ handle->saiQueue[handle->queueDriver].data += size;
+ }
+#else
+ if (base->RCSR & I2S_RCSR_FWF_MASK)
+ {
+ uint8_t size = MIN((handle->saiQueue[handle->queueDriver].dataSize), dataSize);
+
+ SAI_ReadNonBlocking(base, handle->channel, handle->bitWidth, buffer, size);
+
+ /* Update internal state */
+ handle->saiQueue[handle->queueDriver].dataSize -= size;
+ handle->saiQueue[handle->queueDriver].data += size;
+ }
+#endif /* FSL_FEATURE_SAI_FIFO_COUNT */
+
+ /* If finished a blcok, call the callback function */
+ if (handle->saiQueue[handle->queueDriver].dataSize == 0U)
+ {
+ memset(&handle->saiQueue[handle->queueDriver], 0, sizeof(sai_transfer_t));
+ handle->queueDriver = (handle->queueDriver + 1) % SAI_XFER_QUEUE_SIZE;
+ if (handle->callback)
+ {
+ (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)
+#if defined(FSL_FEATURE_SAI_INT_SOURCE_NUM) && (FSL_FEATURE_SAI_INT_SOURCE_NUM == 1)
+void I2S0_DriverIRQHandler(void)
+{
+ if ((s_saiHandle[0][1]) && ((I2S0->RCSR & kSAI_FIFOWarningFlag) || (I2S0->RCSR & kSAI_FIFOErrorFlag)))
+ {
+ SAI_TransferRxHandleIRQ(I2S0, s_saiHandle[0][1]);
+ }
+ if ((s_saiHandle[0][0]) && ((I2S0->TCSR & kSAI_FIFOWarningFlag) || (I2S0->TCSR & kSAI_FIFOErrorFlag)))
+ {
+ SAI_TransferTxHandleIRQ(I2S0, s_saiHandle[0][0]);
+ }
+}
+#else
+void I2S0_Tx_DriverIRQHandler(void)
+{
+ assert(s_saiHandle[0][0]);
+ SAI_TransferTxHandleIRQ(I2S0, s_saiHandle[0][0]);
+}
+
+void I2S0_Rx_DriverIRQHandler(void)
+{
+ assert(s_saiHandle[0][1]);
+ SAI_TransferRxHandleIRQ(I2S0, s_saiHandle[0][1]);
+}
+#endif /* FSL_FEATURE_SAI_INT_SOURCE_NUM */
+#endif /* I2S0*/
+
+#if defined(I2S1)
+void I2S1_Tx_DriverIRQHandler(void)
+{
+ assert(s_saiHandle[1][0]);
+ SAI_TransferTxHandleIRQ(I2S1, s_saiHandle[1][0]);
+}
+
+void I2S1_Rx_DriverIRQHandler(void)
+{
+ assert(s_saiHandle[1][1]);
+ SAI_TransferRxHandleIRQ(I2S1, s_saiHandle[1][1]);
+}
+#endif
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/f8f2ebbf/hw/mcu/nxp/src/ext/sdk-2.0-frdm-k64f_b160321/devices/MK64F12/drivers/fsl_sai.h
----------------------------------------------------------------------
diff --git a/hw/mcu/nxp/src/ext/sdk-2.0-frdm-k64f_b160321/devices/MK64F12/drivers/fsl_sai.h b/hw/mcu/nxp/src/ext/sdk-2.0-frdm-k64f_b160321/devices/MK64F12/drivers/fsl_sai.h
new file mode 100644
index 0000000..72b6efd
--- /dev/null
+++ b/hw/mcu/nxp/src/ext/sdk-2.0-frdm-k64f_b160321/devices/MK64F12/drivers/fsl_sai.h
@@ -0,0 +1,850 @@
+/*
+ * Copyright (c) 2015, Freescale Semiconductor, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * o Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ *
+ * o Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ *
+ * o Neither the name of Freescale Semiconductor, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _FSL_SAI_H_
+#define _FSL_SAI_H_
+
+#include "fsl_common.h"
+
+/*!
+ * @addtogroup sai
+ * @{
+ */
+
+/*! @file */
+
+/*******************************************************************************
+ * Definitions
+ ******************************************************************************/
+
+/*! @name Driver version */
+/*@{*/
+#define FSL_SAI_DRIVER_VERSION (MAKE_VERSION(2, 1, 0)) /*!< Version 2.1.0 */
+/*@}*/
+
+/*! @brief SAI return status*/
+enum _sai_status_t
+{
+ 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 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 */
+ kSAI_Slave = 0x1U /*!< Slave mode */
+} sai_master_slave_t;
+
+/*! @brief Mono or stereo audio format */
+typedef enum _sai_mono_stereo
+{
+ kSAI_Stereo = 0x0U, /*!< Stereo sound. */
+ kSAI_MonoLeft, /*!< Only left channel have sound. */
+ kSAI_MonoRight /*!< Only Right channel have sound. */
+} sai_mono_stereo_t;
+
+/*! @brief Synchronous or asynchronous mode */
+typedef enum _sai_sync_mode
+{
+ kSAI_ModeAsync = 0x0U, /*!< Asynchronous mode */
+ kSAI_ModeSync, /*!< Synchronous mode (with receiver or transmit) */
+ kSAI_ModeSyncWithOtherTx, /*!< Synchronous with another SAI transmit */
+ kSAI_ModeSyncWithOtherRx /*!< Synchronous with another SAI receiver */
+} sai_sync_mode_t;
+
+/*! @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;
+
+/*! @brief Bit clock source */
+typedef enum _sai_bclk_source
+{
+ kSAI_BclkSourceBusclk = 0x0U, /*!< Bit clock using bus clock */
+ kSAI_BclkSourceMclkDiv, /*!< Bit clock using master clock divider */
+ kSAI_BclkSourceOtherSai0, /*!< Bit clock from other SAI device */
+ kSAI_BclkSourceOtherSai1 /*!< Bit clock from other SAI device */
+} sai_bclk_source_t;
+
+/*! @brief The SAI interrupt enable flag */
+enum _sai_interrupt_enable_t
+{
+ 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_FIFO_COUNT) && (FSL_FEATURE_SAI_FIFO_COUNT > 1)
+ kSAI_FIFORequestInterruptEnable = I2S_TCSR_FRIE_MASK, /*!< FIFO request, means reached watermark */
+#endif /* FSL_FEATURE_SAI_FIFO_COUNT */
+};
+
+/*! @brief The DMA request sources */
+enum _sai_dma_enable_t
+{
+ kSAI_FIFOWarningDMAEnable = I2S_TCSR_FWDE_MASK, /*!< FIFO warning caused by the DMA request */
+#if defined(FSL_FEATURE_SAI_FIFO_COUNT) && (FSL_FEATURE_SAI_FIFO_COUNT > 1)
+ kSAI_FIFORequestDMAEnable = I2S_TCSR_FRDE_MASK, /*!< FIFO request caused by the DMA request */
+#endif /* FSL_FEATURE_SAI_FIFO_COUNT */
+};
+
+/*! @brief The SAI status flag */
+enum _sai_flags
+{
+ 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_FIFO_COUNT) && (FSL_FEATURE_SAI_FIFO_COUNT > 1)
+ kSAI_FIFORequestFlag = I2S_TCSR_FRF_MASK, /*!< FIFO request flag. */
+#endif /* FSL_FEATURE_SAI_FIFO_COUNT */
+ 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 configure 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 */
+#endif /* FSL_FEATURE_SAI_HAS_MCR */
+ sai_mclk_source_t mclkSource; /*!< Master Clock source */
+ sai_bclk_source_t bclkSource; /*!< Bit Clock source */
+ sai_master_slave_t masterSlave; /*!< Master or slave */
+} sai_config_t;
+
+/*!@brief SAI transfer queue size, user can refine it according to use case. */
+#define SAI_XFER_QUEUE_SIZE (4)
+
+/*! @brief Audio sample rate */
+typedef enum _sai_sample_rate
+{
+ kSAI_SampleRate8KHz = 8000U, /*!< Sample rate 8000Hz */
+ kSAI_SampleRate11025Hz = 11025U, /*!< Sample rate 11025Hz */
+ kSAI_SampleRate12KHz = 12000U, /*!< Sample rate 12000Hz */
+ kSAI_SampleRate16KHz = 16000U, /*!< Sample rate 16000Hz */
+ kSAI_SampleRate22050Hz = 22050U, /*!< Sample rate 22050Hz */
+ kSAI_SampleRate24KHz = 24000U, /*!< Sample rate 24000Hz */
+ kSAI_SampleRate32KHz = 32000U, /*!< Sample rate 32000Hz */
+ kSAI_SampleRate44100Hz = 44100U, /*!< Sample rate 44100Hz */
+ kSAI_SampleRate48KHz = 48000U, /*!< Sample rate 48000Hz */
+ kSAI_SampleRate96KHz = 96000U /*!< Sample rate 96000Hz */
+} 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;
+
+/*! @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/32bits */
+ sai_mono_stereo_t stereo; /*!< Mono or stereo */
+ uint32_t masterClockHz; /*!< Master clock frequency in Hz */
+#if defined(FSL_FEATURE_SAI_FIFO_COUNT) && (FSL_FEATURE_SAI_FIFO_COUNT > 1)
+ uint8_t watermark; /*!< Watermark value */
+#endif /* FSL_FEATURE_SAI_FIFO_COUNT */
+ uint8_t channel; /*!< Data channel used in transfer.*/
+ sai_protocol_t protocol; /*!< Which audio protocol used */
+} sai_transfer_format_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
+{
+ 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/32bits */
+ uint8_t channel; /*!< Transfer channel */
+ 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_FIFO_COUNT) && (FSL_FEATURE_SAI_FIFO_COUNT > 1)
+ uint8_t watermark; /*!< Watermark value */
+#endif
+};
+
+/*******************************************************************************
+ * API
+ ******************************************************************************/
+
+#if defined(__cplusplus)
+extern "C" {
+#endif /*_cplusplus*/
+
+/*!
+ * @name Initialization and deinitialization
+ * @{
+ */
+
+/*!
+ * @brief Initializes the SAI Tx peripheral.
+ *
+ * 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 configure structure.
+*/
+void SAI_TxInit(I2S_Type *base, const sai_config_t *config);
+
+/*!
+ * @brief Initializes the the SAI Rx peripheral.
+ *
+ * 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 configure structure.
+ */
+void SAI_RxInit(I2S_Type *base, const sai_config_t *config);
+
+/*!
+ * @brief Sets the SAI Tx configuration structure to default values.
+ *
+ * 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().
+ * 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.
+ *
+ * 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().
+ * 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 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 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 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);
+
+/*! @} */
+
+/*!
+ * @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 source 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);
+}
+
+/*! @} */
+
+/*!
+ * @name Interrupts
+ * @{
+ */
+
+/*!
+ * @brief Enables SAI Tx interrupt requests.
+ *
+ * @param base SAI base pointer
+ * @param mask interrupt source
+ * The parameter can be a combination of the following source 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 SAI Rx interrupt requests.
+ *
+ * @param base SAI base pointer
+ * @param mask interrupt source
+ * The parameter can be a combination of the following source 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 SAI Tx interrupt requests.
+ *
+ * @param base SAI base pointer
+ * @param mask interrupt source
+ * The parameter can be a combination of the following source 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 SAI Rx interrupt requests.
+ *
+ * @param base SAI base pointer
+ * @param mask interrupt source
+ * The parameter can be a combination of the following source 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 SAI Tx DMA requests.
+ * @param base SAI base pointer
+ * @param mask DMA source
+ * The parameter can be combination of the following source 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 SAI Rx DMA requests.
+ * @param base SAI base pointer
+ * @param mask DMA source
+ * The parameter can be a combination of the following source 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 SAI DMA transfer configuration.
+ *
+ * @param base SAI base pointer.
+ * @param channel Which data channel used.
+ * @return data register address.
+ */
+static inline uint32_t SAI_TxGetDataRegisterAddress(I2S_Type *base, uint32_t channel)
+{
+ return (uint32_t)(&(base->TDR)[channel]);
+}
+
+/*!
+ * @brief Gets the SAI Rx data register address.
+ *
+ * This API is used to provide a transfer address for SAI DMA transfer configuration.
+ *
+ * @param base SAI base pointer.
+ * @param channel Which data channel used.
+ * @return data register address.
+ */
+static inline uint32_t SAI_RxGetDataRegisterAddress(I2S_Type *base, uint32_t channel)
+{
+ return (uint32_t)(&(base->RDR)[channel]);
+}
+
+/*! @} */
+
+/*!
+ * @name Bus Operations
+ * @{
+ */
+
+/*!
+ * @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.
+ *
+ * @param base SAI base 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.
+*/
+void SAI_TxSetFormat(I2S_Type *base,
+ 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.
+ *
+ * @param base SAI base 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.
+*/
+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 a 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 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 a 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 Reads data from 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 SAI Tx transactional APIs. Call
+ * this function one time to get the handle initialized.
+ *
+ * @param base SAI base pointer
+ * @param handle SAI handle pointer.
+ * @param callback pointer to 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 SAI Rx transactional APIs. Call
+ * this function one time to get the handle initialized.
+ *
+ * @param base SAI base pointer.
+ * @param handle SAI handle pointer.
+ * @param callback pointer to 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 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.
+ *
+ * @param base SAI base pointer.
+ * @param handle SAI 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 a master
+ * clock, this value should equal to masterClockHz in format.
+ * @return Status of this function. Return value is one of 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.
+ *
+ * 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 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.
+ * @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 sai_handle_t structure which stores the transfer state
+ * @param xfer pointer to 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 sai_handle_t structure which stores the transfer state
+ * @param xfer pointer to 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 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 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 sai_handle_t structure which stores the transfer state.
+ */
+void SAI_TransferAbortSend(I2S_Type *base, sai_handle_t *handle);
+
+/*!
+ * @brief Aborts the the current IRQ receive.
+ *
+ * @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 sai_handle_t structure which stores the transfer state.
+ */
+void SAI_TransferAbortReceive(I2S_Type *base, sai_handle_t *handle);
+
+/*!
+ * @brief Tx interrupt handler.
+ *
+ * @param base SAI base pointer.
+ * @param handle pointer to 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 sai_handle_t structure.
+ */
+void SAI_TransferRxHandleIRQ(I2S_Type *base, sai_handle_t *handle);
+
+/*! @} */
+
+#if defined(__cplusplus)
+}
+#endif /*_cplusplus*/
+
+/*! @} */
+
+#endif /* _FSL_SAI_H_ */
http://git-wip-us.apache.org/repos/asf/incubator-mynewt-core/blob/f8f2ebbf/hw/mcu/nxp/src/ext/sdk-2.0-frdm-k64f_b160321/devices/MK64F12/drivers/fsl_sai_edma.c
----------------------------------------------------------------------
diff --git a/hw/mcu/nxp/src/ext/sdk-2.0-frdm-k64f_b160321/devices/MK64F12/drivers/fsl_sai_edma.c b/hw/mcu/nxp/src/ext/sdk-2.0-frdm-k64f_b160321/devices/MK64F12/drivers/fsl_sai_edma.c
new file mode 100644
index 0000000..9b1b2f6
--- /dev/null
+++ b/hw/mcu/nxp/src/ext/sdk-2.0-frdm-k64f_b160321/devices/MK64F12/drivers/fsl_sai_edma.c
@@ -0,0 +1,379 @@
+/*
+ * Copyright (c) 2015, Freescale Semiconductor, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * o Redistributions of source code must retain the above copyright notice, this list
+ * of conditions and the following disclaimer.
+ *
+ * o Redistributions in binary form must reproduce the above copyright notice, this
+ * list of conditions and the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ *
+ * o Neither the name of Freescale Semiconductor, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "fsl_sai_edma.h"
+
+/*******************************************************************************
+ * Definitations
+ ******************************************************************************/
+/* Used for 32byte aligned */
+#define STCD_ADDR(address) (edma_tcd_t *)(((uint32_t)address + 32) & ~0x1FU)
+
+/*<! 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;
+
+enum _sai_edma_transfer_state
+{
+ kSAI_Busy = 0x0U, /*!< SAI is busy */
+ kSAI_Idle, /*!< Transfer is done. */
+};
+
+/*<! Private handle only used for internally. */
+static sai_edma_private_handle_t s_edmaPrivateHandle[FSL_FEATURE_SOC_I2S_COUNT][2];
+
+/*******************************************************************************
+ * Prototypes
+ ******************************************************************************/
+/*!
+ * @brief Get the instance number for SAI.
+ *
+ * @param base SAI base pointer.
+ */
+extern 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 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;
+
+ /* If finished a blcok, call the callback function */
+ memset(&saiHandle->saiQueue[saiHandle->queueDriver], 0, sizeof(sai_transfer_t));
+ saiHandle->queueDriver = (saiHandle->queueDriver + 1) % SAI_XFER_QUEUE_SIZE;
+ if (saiHandle->callback)
+ {
+ (saiHandle->callback)(privHandle->base, saiHandle, kStatus_SAI_TxIdle, saiHandle->userData);
+ }
+
+ /* If all data finished, just stop the transfer */
+ if (saiHandle->saiQueue[saiHandle->queueDriver].data == NULL)
+ {
+ SAI_TransferAbortSendEDMA(privHandle->base, saiHandle);
+ }
+}
+
+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;
+
+ /* If finished a blcok, call the callback function */
+ memset(&saiHandle->saiQueue[saiHandle->queueDriver], 0, sizeof(sai_transfer_t));
+ saiHandle->queueDriver = (saiHandle->queueDriver + 1) % SAI_XFER_QUEUE_SIZE;
+ if (saiHandle->callback)
+ {
+ (saiHandle->callback)(privHandle->base, saiHandle, kStatus_SAI_RxIdle, saiHandle->userData);
+ }
+
+ /* If all data finished, just stop the transfer */
+ if (saiHandle->saiQueue[saiHandle->queueDriver].data == NULL)
+ {
+ SAI_TransferAbortReceiveEDMA(privHandle->base, saiHandle);
+ }
+}
+
+void SAI_TransferTxCreateHandleEDMA(
+ I2S_Type *base, sai_edma_handle_t *handle, sai_edma_callback_t callback, void *userData, edma_handle_t *dmaHandle)
+{
+ assert(handle && dmaHandle);
+
+ uint32_t instance = SAI_GetInstance(base);
+
+ /* Set sai base to handle */
+ handle->dmaHandle = dmaHandle;
+ handle->callback = callback;
+ handle->userData = userData;
+
+ /* Set SAI state to idle */
+ handle->state = kSAI_Idle;
+
+ s_edmaPrivateHandle[instance][0].base = base;
+ s_edmaPrivateHandle[instance][0].handle = handle;
+
+ /* Need to use scatter gather */
+ EDMA_InstallTCDMemory(dmaHandle, STCD_ADDR(handle->tcd), SAI_XFER_QUEUE_SIZE);
+
+ /* Install callback for Tx dma channel */
+ EDMA_SetCallback(dmaHandle, SAI_TxEDMACallback, &s_edmaPrivateHandle[instance][0]);
+}
+
+void SAI_TransferRxCreateHandleEDMA(
+ I2S_Type *base, sai_edma_handle_t *handle, sai_edma_callback_t callback, void *userData, edma_handle_t *dmaHandle)
+{
+ assert(handle && dmaHandle);
+
+ uint32_t instance = SAI_GetInstance(base);
+
+ /* Set sai base to handle */
+ handle->dmaHandle = dmaHandle;
+ handle->callback = callback;
+ handle->userData = userData;
+
+ /* Set SAI state to idle */
+ handle->state = kSAI_Idle;
+
+ s_edmaPrivateHandle[instance][1].base = base;
+ s_edmaPrivateHandle[instance][1].handle = handle;
+
+ /* Need to use scatter gather */
+ EDMA_InstallTCDMemory(dmaHandle, STCD_ADDR(handle->tcd), SAI_XFER_QUEUE_SIZE);
+
+ /* Install callback for Tx dma channel */
+ EDMA_SetCallback(dmaHandle, SAI_RxEDMACallback, &s_edmaPrivateHandle[instance][1]);
+}
+
+void SAI_TransferTxSetFormatEDMA(I2S_Type *base,
+ sai_edma_handle_t *handle,
+ sai_transfer_format_t *format,
+ uint32_t mclkSourceClockHz,
+ uint32_t bclkSourceClockHz)
+{
+ assert(handle && format);
+
+ /* Configure the audio format to SAI registers */
+ SAI_TxSetFormat(base, format, mclkSourceClockHz, bclkSourceClockHz);
+
+ /* Get the tranfer size from format, this should be used in EDMA configuration */
+ handle->bytesPerFrame = format->bitWidth / 8U;
+
+ /* Update the data channel SAI used */
+ handle->channel = format->channel;
+#if defined(FSL_FEATURE_SAI_FIFO_COUNT) && (FSL_FEATURE_SAI_FIFO_COUNT > 1)
+ handle->count = FSL_FEATURE_SAI_FIFO_COUNT - format->watermark;
+#else
+ handle->count = 1U;
+#endif /* FSL_FEATURE_SAI_FIFO_COUNT */
+}
+
+void SAI_TransferRxSetFormatEDMA(I2S_Type *base,
+ sai_edma_handle_t *handle,
+ sai_transfer_format_t *format,
+ uint32_t mclkSourceClockHz,
+ uint32_t bclkSourceClockHz)
+{
+ assert(handle && format);
+
+ /* Configure the audio format to SAI registers */
+ SAI_RxSetFormat(base, format, mclkSourceClockHz, bclkSourceClockHz);
+
+ /* Get the tranfer size from format, this should be used in EDMA configuration */
+ handle->bytesPerFrame = format->bitWidth / 8U;
+
+ /* Update the data channel SAI used */
+ handle->channel = format->channel;
+
+#if defined(FSL_FEATURE_SAI_FIFO_COUNT) && (FSL_FEATURE_SAI_FIFO_COUNT > 1)
+ handle->count = format->watermark;
+#else
+ handle->count = 1U;
+#endif /* FSL_FEATURE_SAI_FIFO_COUNT */
+}
+
+status_t SAI_TransferSendEDMA(I2S_Type *base, sai_edma_handle_t *handle, sai_transfer_t *xfer)
+{
+ assert(handle && xfer);
+
+ edma_transfer_config_t config = {0};
+ uint32_t destAddr = SAI_TxGetDataRegisterAddress(base, handle->channel);
+
+ /* Check if input parameter invalid */
+ if ((xfer->data == NULL) || (xfer->dataSize == 0U))
+ {
+ return kStatus_InvalidArgument;
+ }
+
+ if (handle->saiQueue[handle->queueUser].data)
+ {
+ return kStatus_SAI_QueueFull;
+ }
+
+ /* Change the state of handle */
+ handle->state = 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 + 1) % SAI_XFER_QUEUE_SIZE;
+
+ /* Prepare edma configure */
+ EDMA_PrepareTransfer(&config, xfer->data, handle->bytesPerFrame, (void *)destAddr, handle->bytesPerFrame,
+ handle->count * handle->bytesPerFrame, xfer->dataSize, kEDMA_MemoryToPeripheral);
+
+ EDMA_SubmitTransfer(handle->dmaHandle, &config);
+
+ /* 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);
+
+ return kStatus_Success;
+}
+
+status_t SAI_TransferReceiveEDMA(I2S_Type *base, sai_edma_handle_t *handle, sai_transfer_t *xfer)
+{
+ assert(handle && xfer);
+
+ edma_transfer_config_t config = {0};
+ uint32_t srcAddr = SAI_RxGetDataRegisterAddress(base, handle->channel);
+
+ /* Check if input parameter invalid */
+ if ((xfer->data == NULL) || (xfer->dataSize == 0U))
+ {
+ return kStatus_InvalidArgument;
+ }
+
+ if (handle->saiQueue[handle->queueUser].data)
+ {
+ return kStatus_SAI_QueueFull;
+ }
+
+ /* Change the state of handle */
+ handle->state = 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 + 1) % SAI_XFER_QUEUE_SIZE;
+
+ /* Prepare edma configure */
+ EDMA_PrepareTransfer(&config, (void *)srcAddr, handle->bytesPerFrame, xfer->data, handle->bytesPerFrame,
+ handle->count * handle->bytesPerFrame, xfer->dataSize, kEDMA_PeripheralToMemory);
+
+ EDMA_SubmitTransfer(handle->dmaHandle, &config);
+
+ /* Start DMA transfer */
+ EDMA_StartTransfer(handle->dmaHandle);
+
+ /* Enable DMA enable bit */
+ SAI_RxEnableDMA(base, kSAI_FIFORequestDMAEnable, true);
+
+ /* Enable SAI Rx clock */
+ SAI_RxEnable(base, true);
+
+ return kStatus_Success;
+}
+
+void SAI_TransferAbortSendEDMA(I2S_Type *base, sai_edma_handle_t *handle)
+{
+ assert(handle);
+
+ /* Disable dma */
+ EDMA_AbortTransfer(handle->dmaHandle);
+
+ /* Disable DMA enable bit */
+ SAI_TxEnableDMA(base, kSAI_FIFORequestDMAEnable, false);
+
+ /* Set the handle state */
+ handle->state = kSAI_Idle;
+}
+
+void SAI_TransferAbortReceiveEDMA(I2S_Type *base, sai_edma_handle_t *handle)
+{
+ assert(handle);
+
+ /* Disable dma */
+ EDMA_AbortTransfer(handle->dmaHandle);
+
+ /* Disable DMA enable bit */
+ SAI_RxEnableDMA(base, kSAI_FIFORequestDMAEnable, false);
+
+ /* Set the handle state */
+ handle->state = kSAI_Idle;
+}
+
+status_t SAI_TransferGetSendCountEDMA(I2S_Type *base, sai_edma_handle_t *handle, size_t *count)
+{
+ assert(handle);
+
+ status_t status = kStatus_Success;
+
+ if (handle->state != kSAI_Busy)
+ {
+ status = kStatus_NoTransferInProgress;
+ }
+ else
+ {
+ *count = (handle->transferSize[handle->queueDriver] -
+ EDMA_GetRemainingBytes(handle->dmaHandle->base, handle->dmaHandle->channel));
+ }
+
+ return status;
+}
+
+status_t SAI_TransferGetReceiveCountEDMA(I2S_Type *base, sai_edma_handle_t *handle, size_t *count)
+{
+ assert(handle);
+
+ status_t status = kStatus_Success;
+
+ if (handle->state != kSAI_Busy)
+ {
+ status = kStatus_NoTransferInProgress;
+ }
+ else
+ {
+ *count = (handle->transferSize[handle->queueDriver] -
+ EDMA_GetRemainingBytes(handle->dmaHandle->base, handle->dmaHandle->channel));
+ }
+
+ return status;
+}