added prescaler for 16 bit pwm in LPC1347 target

Fork of mbed-dev by mbed official

Revision:
144:ef7eb2e8f9f7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/targets/hal/TARGET_Freescale/TARGET_KSDK2_MCUS/TARGET_KL43Z/drivers/fsl_sai.c	Fri Sep 02 15:07:44 2016 +0100
@@ -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