/*
 * Copyright 2017-2021 NXP
 * this software is owned or controlled by nxp and may only be used strictly
 * in accordance with the applicable license terms. by expressly accepting
 * such terms or by downloading, installing, activating and/or otherwise using
 * the software, you are agreeing that you have read, and that you agree to
 * comply with and are bound by, such license terms. if you do not agree to be
 * bound by the applicable license terms, then you may not retain, install,
 * activate or otherwise use the software.
 */

#include <stdbool.h>
#include <stdint.h>
#include "fsl_sai.h"
#include "fsl_dma.h"
#include "fsl_dmamux.h"
#include "utils.h"
#include "error.h"
#include "board.h"
#include "dma_interface.h"
#include "audio.h"
#include "audio_tx.h"
#include "audio_common.h"
#include "audio_ringbuffer.h"
#include "audio_internal.h"
#include "clock_config.h"
#include "fsl_gpio.h"
#include "fsl_port.h"

/* ---------------------------------------------------------------------------- */
/* Defines                                                                      */
/* ---------------------------------------------------------------------------- */
/** Audio buffer size */
#define AUDIO_OUTPUT_RING_BUFFER_SIZE  4096


/** Audio Output DMA Configuration type */
#define AUDIO_OUTPUT_DMA_MODULO        (kDMA_Modulo4KBytes)

/** Current audio Service status */
static uint32_t s_audioService_TxStatus = 0;

/** Set a status flag of the audio Service */
#define AUDIO_SERVICE_SET_STATUS(x)  (s_audioService_TxStatus |= ((x)))

/** Clears a status flag of the audio Service */
#define AUDIO_SERVICE_CLEAR_STATUS(x)  (s_audioService_TxStatus &= ~((x)))

/** Check the current status of the audio Service */
#define AUDIO_SERVICE_CHECK_STATUS(x)  (s_audioService_TxStatus & ((x)))

volatile uint32_t g_audioTxISRBusy = 0;

extern uint8_t g_I2sFormatIndex;

typedef struct _i2s_tx_format_config_t
{
    uint32_t sampleRate;
    uint32_t  wordLength;
}i2s_tx_format_config_t;

i2s_tx_format_config_t g_I2sTxFormat[3] = 
{
    {47619, 28},  // DIV 8
    {48000, 25},  // DIV 9
    {48387, 31},  // DIV 7
};

/* -------------------------------------------------------------------------
 * Local function prototypes
 * ------------------------------------------------------------------------- */

/**
 * @brief Configure Transmitter DMA
 *
 * @param DmaChannel DMA Channel id
 * @param DmaLinkChannel DMA link/next channel id
 * @return void.
 */
static void audio_ConfigTxDMA(uint8_t DmaChannel, uint8_t DmaLinkChannel);

/**
 * @brief Configure Transmitter Link DMA
 *
 * @param DmaChannel DMA Channel id
 * @return void.
 */
static void audio_ConfigureLinkTxDma(uint8_t DmaChannel);

/**
 * @brief Call back function of DMA Tx
 *
 * @param handle pointer to DMA handle
 * @param userData pointer to Callback userdata
 * @return void.
 */
static void audio_DMATxCallback(struct _dma_handle *handle, void *userData);

/**
 * @brief Get samples to be transmitted from the user-registered callback (passed to @ref audio_StartTx),
 *        and write them into the output ring buffer.
 *
 * @param[in]  nbSamples Number of audio samples to get.
 */
static void audio_GetAndTransmitSamples(uint16_t nbSamples);

/**
 * @brief Configurate the clock divider of I2S_BCLK and I2S wordlength to adjust I2S_WS.
 */
void audio_SetI2sWS(void);

/* ---------------------------------------------------------------------------- */
/* Local variables                                                              */
/* ---------------------------------------------------------------------------- */

/** ring buffer required to allocate the output samples of the application */
phRingBuffer_t s_audioService_RingBufferOut;

RAM_1K_ALIGNED_SECTION uint8_t ALIGN(AUDIO_OUTPUT_RING_BUFFER_SIZE) s_audioService_BufferOut[AUDIO_OUTPUT_RING_BUFFER_SIZE];

static dma_handle_t s_audioService_DMATxHandle;

static dma_handle_t s_audioService_DMALinkTxHandle;

static dma_transfer_config_t s_audioService_DMATxConfig;

static dma_transfer_config_t s_audioService_DMALinkTxConfig;

static dma_channel_link_config_t s_audioService_DMAChannelTxConfig;

/**
 * Callback passed to @ref audio_StartTx to get the samples to be transmitted.
 */
static audio_get_samples_cb s_audio_GetSamplesCb;

/** Buffer required to transfer the data used by the linked channel*/
static uint32_t s_audioService_DMALinkData[2];

extern uint8_t g_I2sBclkSwitchFlag;
extern uint32_t g_i2sSampleRate;
/* ---------------------------------------------------------------------------- */
/* Local functions-TX                                                           */
/* ---------------------------------------------------------------------------- */

uint32_t audio_GetBytesLeftInTransfer(void)
{
    uint32_t *address = (uint32_t *)dma_GetBcrRegisterAddress(AUDIO_SERVICE_DMA_BASE, AUDIO_SERVICE_DMA_CHANNEL_OUT);
    return DMA_DSR_BCR_BCR(*address);
}

static void audio_GetAndTransmitSamples(uint16_t nbSamples)
{
    /* we're not using @ref audio_ringbuffer_Write for optimization reasons;
       instead we let the callback write to the output ring buffer directly */
    uint8_t audioTxSampleSizeBytes = audio_GetTxSampleSizeBytes();
    uint16_t nbBytes = nbSamples * audioTxSampleSizeBytes;

    /* write until the end of the buffer */
    if ((s_audioService_RingBufferOut.pWritePointer + nbBytes) > (uint8_t *)s_audioService_RingBufferOut.EndAddress) {
        uint16_t nbBytesEnd = (uint8_t *)s_audioService_RingBufferOut.EndAddress - s_audioService_RingBufferOut.pWritePointer + 1;
        uint16_t nbSamplesEnd = nbBytesEnd / audioTxSampleSizeBytes;
        s_audio_GetSamplesCb((void *)s_audioService_RingBufferOut.pWritePointer, nbSamplesEnd);
        s_audioService_RingBufferOut.pWritePointer = (uint8_t *)s_audioService_RingBufferOut.StartAddress;
        nbSamples -= nbSamplesEnd;
        nbBytes -= nbBytesEnd;
    }

    /* write the remaining samples */
    s_audio_GetSamplesCb((void *)s_audioService_RingBufferOut.pWritePointer, nbSamples);
    s_audioService_RingBufferOut.pWritePointer += nbBytes;
}

static void AudioTxLatencyDebug(bool sendingAudioToI2s)
{
    if (g_AudioServiceCtxt.userConfig.enableLatencyDebug) {
        //AUDIO_LatencyDebug_SendingAudioToI2s(sendingAudioToI2s);
    }
}

/**
 * Configure Tx DMA
 */
static void audio_ConfigTxDMA(uint8_t DmaChannel, uint8_t DmaLinkChannel)
{
    static uint8_t isDmaTxConfigured = false;
    uint32_t I2sDataRegisterAddress;
    uint8_t audioTxSampleSizeBytes = audio_GetTxSampleSizeBytes();

    if (isDmaTxConfigured == false) {
        dma_ConfigureDMA(&s_audioService_DMATxHandle, DmaChannel, AUDIO_SERVICE_DMA_TX_MUX_SOURCE);
        isDmaTxConfigured = true;
    }

    I2sDataRegisterAddress = SAI_TxGetDataRegisterAddress(AUDIO_SERVICE_I2S_BASEADDR, 0);

    /* Request to move the equivalent of 1ms of audio AUDIO_SERVICE_SYSTEM_SAMPLING_RATE * audioTxSampleSizeBytes * AUDIO_CHANNELS */
    DMA_PrepareTransfer(&s_audioService_DMATxConfig, (void *)(&s_audioService_BufferOut[0]), audioTxSampleSizeBytes, \
                        (void *)I2sDataRegisterAddress, audioTxSampleSizeBytes,
                        TX_AUDIO_DMA_TRANSFER_SIZE(audioTxSampleSizeBytes), kDMA_MemoryToPeripheral);

    DMA_SubmitTransfer(&s_audioService_DMATxHandle, &s_audioService_DMATxConfig, kDMA_NoOptions);

    dma_DisableHwRequest(AUDIO_SERVICE_DMA_BASE, DmaChannel, false);

    /** enable cycle steal to make a sample size transfer on each I2S request */
    DMA_EnableCycleSteal(AUDIO_SERVICE_DMA_BASE, DmaChannel, true);

    /** Modulo only for source, destination is peripheral, hence, disabled */
    DMA_SetModulo(AUDIO_SERVICE_DMA_BASE, DmaChannel, AUDIO_OUTPUT_DMA_MODULO, kDMA_ModuloDisable);

    /* configure the link channel to enable BCR reload by DMA itself        */
    /* this is required due to bandwidth issues at servicing the DMA ISR    */
    /* and the next I2S request                                             */
    s_audioService_DMAChannelTxConfig.linkType = kDMA_ChannelLinkChannel1AfterBCR0;
    s_audioService_DMAChannelTxConfig.channel1 = DmaLinkChannel;

    DMA_SetChannelLinkConfig(AUDIO_SERVICE_DMA_BASE, DmaChannel, &s_audioService_DMAChannelTxConfig);
}

/**
 * Configure Tx Link DMA
 */
static void audio_ConfigureLinkTxDma(uint8_t DmaChannel)
{
    static uint8_t isLinkChannelConfigured = false;
    uint32_t LinkDmaBcrRegisterAddress;
    uint8_t audioTxSampleSizeBytes = audio_GetTxSampleSizeBytes();

    if (isLinkChannelConfigured == false) {
        /** Sets the Callback and Initialize DMA Mux */
        dma_ConfigureLinkDMA(&s_audioService_DMALinkTxHandle, DmaChannel, audio_DMATxCallback);

        isLinkChannelConfigured = true;
    }

    /** Gets the DMA Destination address */
    LinkDmaBcrRegisterAddress = dma_GetBcrRegisterAddress(AUDIO_SERVICE_DMA_BASE, AUDIO_SERVICE_DMA_CHANNEL_OUT);

    s_audioService_DMALinkData[0] = DMA_DSR_BCR_DONE_MASK;
    s_audioService_DMALinkData[1] = TX_AUDIO_DMA_TRANSFER_SIZE(audioTxSampleSizeBytes);

    DMA_PrepareTransfer(&s_audioService_DMALinkTxConfig, (void *)(&s_audioService_DMALinkData[0]), sizeof(uint32_t), \
                        (void *)LinkDmaBcrRegisterAddress, sizeof(uint32_t), sizeof(s_audioService_DMALinkData), kDMA_MemoryToPeripheral);

    /** Submits the transfer request */
    DMA_SubmitTransfer(&s_audioService_DMALinkTxHandle, &s_audioService_DMALinkTxConfig, kDMA_EnableInterrupt);

    /** Sets the Transfer cycle type False-Continues: True-steps */
    DMA_EnableCycleSteal(AUDIO_SERVICE_DMA_BASE, DmaChannel, false);
}

/**
 * Call back function of DMA Tx
 */

extern uint8_t g_I2sBclkSwitchFlag;
static void audio_DMATxCallback(struct _dma_handle *handle, void *userData)
{
    (void)handle;
    (void)userData;
    g_audioTxISRBusy = 1;
    
    uint8_t audioTxSampleSizeBytes = audio_GetTxSampleSizeBytes();
    
    if(g_I2sBclkSwitchFlag)
    {
        while(!(I2S0->TCSR & I2S_TCSR_FWF_MASK))
        {
        }
        I2S0->TCSR = ((I2S0->TCSR & 0xFFE3FFFFU) & (~I2S_TCSR_TE_MASK));
        while((I2S0->TCSR & I2S_TCSR_TE_MASK))
        {
        }
        
        /* adjust I2S_WS frenquency */        
        audio_SetI2sWS();

        SAI_TxEnable(AUDIO_SERVICE_I2S_BASEADDR, true);

        while(!(I2S0->TCSR & I2S_TCSR_BCE_MASK))
        {
        }
        __NOP();
    }


    AudioTxLatencyDebug(true);

    /* request the next samples: equivalent of 1 DMA transfer */
    uint16_t nbSamples = (TX_AUDIO_DMA_TRANSFER_SIZE(audioTxSampleSizeBytes) / audioTxSampleSizeBytes);
    
    g_audioTxISRBusy = 0;
    
    audio_GetAndTransmitSamples(nbSamples); /* write mixed samples into the transmitter ring buffer */

    AudioTxLatencyDebug(false);

    /** re-configure the dma */
    audio_ConfigureLinkTxDma(AUDIO_SERVICE_DMA_CHANNEL_LINK_OUT);

    
    /** trigger proper events */

    AUDIO_SERVICE_SET_STATUS(AUDIO_SERVICE_TX_COMPLETE_MASK);

    if (g_AudioServiceCtxt.userConfig.audioTxWriteCompleteCb) {
        g_AudioServiceCtxt.userConfig.audioTxWriteCompleteCb(TX_AUDIO_DMA_TRANSFER_SIZE(audioTxSampleSizeBytes), kERROR_Ok);
    }
}

static void audio_TxSpaceWarningCb(uint32_t Filling, void *pCtxt)
{
    (void)Filling;    /* make release build happy */
    (void)pCtxt;      /* make release build happy */

    PDBG("TxSpaceWarning, F=%d", Filling);
}

inline static sai_master_slave_t audio_GetMasterSlave(audioCtrlFormat_t audioFormat)
{
    return (audioFormat == AUDIO_SERVICE_FORMAT_I2S_MASTER) ? kSAI_Master : kSAI_Slave;
}
/* ---------------------------------------------------------------------------- */
/* Public Functions-TX                                                          */
/* ---------------------------------------------------------------------------- */
/**
 * Initializes and starts the I2S-Tx as part of audio-SAI
 */
void audio_InitTx(void)
{
    port_pin_config_t port_gpio_config = {
        .pullSelect = kPORT_PullDown,
        .mux = kPORT_MuxAsGpio
    };

    gpio_pin_config_t gpio_high_config =
    {
        kGPIO_DigitalOutput,
        0, /*set default value to logic "1" */
    };

    /* test pins */
    PORT_SetPinConfig(PORTA, 1, &port_gpio_config);
    GPIO_PinInit(GPIOA, 1, &gpio_high_config);

    PORT_SetPinConfig(PORTA, 5, &port_gpio_config);
    GPIO_PinInit(GPIOA, 5, &gpio_high_config);

    PORT_SetPinConfig(PORTB, 3, &port_gpio_config);
    GPIO_PinInit(GPIOB, 3, &gpio_high_config);

    s_audio_GetSamplesCb = NULL;
    NVIC_DisableIRQ(AUDIO_DMA_INTERRUPT_SOURCE);
    NVIC_SetPriority(AUDIO_DMA_INTERRUPT_SOURCE, AUDIO_DMA_INTERRUPT_PRIORITY);
    
    sai_transceiver_t audioService_TxConfig = { 0 };

    /* I2S slave in asynch mode */
    SAI_GetClassicI2SConfig(&audioService_TxConfig,
                                AUDIO_IF_WORD_LENGTH_32_BITS,
                                kSAI_Stereo,
                                kSAI_Channel0Mask);
   
    g_AudioServiceCtxt.userConfig.audioIfWordLength = AUDIO_SERVICE_AUDIO_IF_WORD_LENGTH_32_BITS;
    audioService_TxConfig.masterSlave = kSAI_Master;
    
    SAI_TxSetConfig(AUDIO_SERVICE_I2S_BASEADDR, &audioService_TxConfig);
    
    audio_SetTxI2sConfig(g_AudioServiceCtxt.userConfig.audioIfWordLength, audioService_TxConfig.masterSlave,
                         g_AudioServiceCtxt.userConfig.audioFormat);

    SAI_TxSetChannelFIFOMask(AUDIO_SERVICE_I2S_BASEADDR, 0);

    SAI_TxEnable(AUDIO_SERVICE_I2S_BASEADDR, true);

    audio_ConfigTxDMA(AUDIO_SERVICE_DMA_CHANNEL_OUT, AUDIO_SERVICE_DMA_CHANNEL_LINK_OUT);

    audio_ConfigureLinkTxDma(AUDIO_SERVICE_DMA_CHANNEL_LINK_OUT);

    AUDIO_SERVICE_SET_STATUS(AUDIO_SERVICE_TX_COMPLETE_MASK);
}

void audio_StartTx(audio_get_samples_cb getEmptySamplesCb, audio_get_samples_cb getMixedSamplesCb)
{
    assert(getEmptySamplesCb);
    assert(getMixedSamplesCb);
    uint8_t audioTxSampleSizeBytes = audio_GetTxSampleSizeBytes();

    if (!AUDIO_SERVICE_CHECK_STATUS(AUDIO_SERVICE_TX_STARTED_MASK)) {
        AUDIO_SERVICE_SET_STATUS(AUDIO_SERVICE_TX_COMPLETE_MASK);

        AudioTxLatencyDebug(true);
        
        uint16_t nbSamples = 2 * (TX_AUDIO_DMA_TRANSFER_SIZE(audioTxSampleSizeBytes) / audioTxSampleSizeBytes);
        s_audio_GetSamplesCb = getEmptySamplesCb; 
        audio_GetAndTransmitSamples(nbSamples);   /* get and write samples into the output ring buffer */

        AudioTxLatencyDebug(false);

        s_audio_GetSamplesCb = getMixedSamplesCb;  /* set callback for getting mixed samples */
        
        /** Start the DMA and the SAI TX */
        DMA_EnableChannelRequest(AUDIO_SERVICE_DMA_BASE, AUDIO_SERVICE_DMA_CHANNEL_OUT);
        SAI_TxEnableDMA(AUDIO_SERVICE_I2S_BASEADDR, kSAI_FIFOWarningDMAEnable, true);

        SAI_TxEnableInterrupts(AUDIO_SERVICE_I2S_BASEADDR, kSAI_FIFOErrorInterruptEnable);
        EnableIRQ(I2S0_IRQn);

        SAI_TxSetChannelFIFOMask(AUDIO_SERVICE_I2S_BASEADDR, 1); 
        audio_EnableI2sPins(true);

        /** set the internal flags */
        AUDIO_SERVICE_SET_STATUS(AUDIO_SERVICE_TX_STARTED_MASK);
        AUDIO_SERVICE_CLEAR_STATUS(AUDIO_SERVICE_TX_COMPLETE_MASK);
    }
}

void audio_StopTx(void)
{
    /* Disable interrupts to prevent further preemption */
    SAI_TxDisableInterrupts(AUDIO_SERVICE_I2S_BASEADDR, kSAI_FIFOErrorInterruptEnable);
    DisableIRQ(I2S0_IRQn);

    /** Disables the Tx DMA */
    SAI_TxEnableDMA(AUDIO_SERVICE_I2S_BASEADDR, kSAI_FIFOWarningDMAEnable, false);
    /** Disables the Tx data channel */
    SAI_TxSetChannelFIFOMask(AUDIO_SERVICE_I2S_BASEADDR, 0);
    /** Aborts DMA transfer */
    DMA_AbortTransfer(&s_audioService_DMATxHandle);
    DMA_AbortTransfer(&s_audioService_DMALinkTxHandle);
    /** Re-Configure Tx DMA */
    audio_ConfigTxDMA(AUDIO_SERVICE_DMA_CHANNEL_OUT, AUDIO_SERVICE_DMA_CHANNEL_LINK_OUT);
    /** Re-Configure Tx Link DMA */
    audio_ConfigureLinkTxDma(AUDIO_SERVICE_DMA_CHANNEL_LINK_OUT);
    /** Resets the internal transmitter logic including the FIFO pointers */
    SAI_TxSoftwareReset(AUDIO_SERVICE_I2S_BASEADDR, kSAI_ResetAll);

    /** resets the audio buffer address to start*/
    audio_ringbuffer_Reset(&s_audioService_RingBufferOut);

    /** Resets the AudioService status */
    AUDIO_SERVICE_CLEAR_STATUS(AUDIO_SERVICE_TX_STARTED_MASK);
    AUDIO_SERVICE_SET_STATUS(AUDIO_SERVICE_TX_COMPLETE_MASK);
}

void audio_RingbufferTxInit(void)
{
    uint8_t audioTxSampleSizeBytes = audio_GetTxSampleSizeBytes();
    /** Initialize the ring buffer for tx */
    audio_ringbuffer_Init(
        &s_audioService_RingBufferOut,
        (uint8_t *)&s_audioService_BufferOut[0],
        sizeof(s_audioService_BufferOut));
    /** Configure a warning callback when exceeding the ring buffer filling margins */
    audio_ringbuffer_ConfigureFillingWarning(
        &s_audioService_RingBufferOut,
        TX_AUDIO_DMA_TRANSFER_SIZE(audioTxSampleSizeBytes),
        audio_TxSpaceWarningCb,
        NULL);
}

void audio_ResetTx(void)
{
    SAI_TxSoftwareReset(AUDIO_SERVICE_I2S_BASEADDR, kSAI_ResetTypeSoftware);
}

error_t audio_SetTxI2sConfig(audioIfWordLength_t audioIfWordLength, sai_master_slave_t masterSlave, audioCtrlFormat_t audioFormat)
{
    uint8_t wordLength;
    uint8_t firstBitShifted;
    switch (audioIfWordLength) {
        case AUDIO_SERVICE_AUDIO_IF_WORD_LENGTH_16_BITS:
            wordLength = AUDIO_IF_WORD_LENGTH_16_BITS;
            firstBitShifted = AUDIO_IF_WORD_LENGTH_16_BITS;
            break;
        case AUDIO_SERVICE_AUDIO_IF_WORD_LENGTH_32_BITS:
            /* DMA module on KL27 does not support 24-bit transfers, hence 32 bit. */
            wordLength = AUDIO_IF_WORD_LENGTH_25_BITS;
            firstBitShifted = AUDIO_IF_WORD_LENGTH_32_BITS;
            break;
        default:
            return kERROR_Fail;
            break;
    }

    /* Tx is Stereo, so 2 channels */
    SAI_TxSetBitClockRate(AUDIO_SERVICE_I2S_BASEADDR,
                          48000000,
                          (AUDIO_SERVICE_SYSTEM_SAMPLING_RATE * 1000),
                          wordLength,
                          TX_AUDIO_CHANNELS);

    sai_serial_data_t config = {
        .dataOrder = kSAI_DataMSB,
        .dataWord0Length = wordLength,          /* sets the channel 1 frame count */
        .dataWordNLength = wordLength,          /* sets the channel 2 frame count */
        .dataFirstBitShifted = firstBitShifted, /* decides the position of data */
        .dataWordNum = AUDIO_I2S_FRAME_SIZE,    /* changes the frame frequency */
        .dataMaskedWord = kSAI_Stereo,
    };
    SAI_TxSetSerialDataConfig(AUDIO_SERVICE_I2S_BASEADDR, &config);

    if (audioFormat != AUDIO_SERVICE_FORMAT_TDM_TYPICAL) {
        sai_frame_sync_t frameSyncConfig = {
            .frameSyncWidth = wordLength,
            .frameSyncEarly = true,
            .frameSyncPolarity = kSAI_PolarityActiveLow
        };
        SAI_TxSetFrameSyncConfig(AUDIO_SERVICE_I2S_BASEADDR, masterSlave, &frameSyncConfig);
    }
    return kERROR_Ok;
}

uint8_t audio_GetTxSampleSizeBytes(void)
{
    const uint8_t audioTxSampleSizeBytes[AUDIO_SERVICE_AUDIO_IF_WORD_LENGTH_Count] = {
        /* For AUDIO_SERVICE_AUDIO_IF_WORD_LENGTH_16_BITS */
        AUDIO_IF_WORD_LENGTH_2_BYTES,
        /* For AUDIO_SERVICE_AUDIO_IF_WORD_LENGTH_32_BITS */
        AUDIO_IF_WORD_LENGTH_4_BYTES
    };
    return audioTxSampleSizeBytes[g_AudioServiceCtxt.userConfig.audioIfWordLength];
}

void audio_SetI2sWS(void)
{
    uint32_t i = 0;
    uint8_t wordLength;
    uint32_t sampleRate;
    
    wordLength =  g_I2sTxFormat[g_I2sFormatIndex].wordLength;
    sampleRate =  g_I2sTxFormat[g_I2sFormatIndex].sampleRate;

    if( g_I2sFormatIndex == 2)
    {
        GPIOA->PSOR = 1<<1;
        GPIOA->PCOR = 1<<5;
        GPIOB->PCOR = 1<<3;

        I2S0->TCR5 &= ~I2S_TCR5_WNW_MASK;
        I2S0->TCR5 &= ~I2S_TCR5_W0W_MASK;
        I2S0->TCR5 |= I2S_TCR5_WNW(wordLength - 1U) | I2S_TCR5_W0W(wordLength - 1U) ;

        I2S0->TCR4 &= ~I2S_TCR4_SYWD_MASK;
        I2S0->TCR4 |= I2S_TCR4_SYWD(wordLength-1);

        SAI_TxSetBitClockRate(I2S0, 48000000, sampleRate, wordLength, 2);
    }
    else if( g_I2sFormatIndex == 1)
    {
        GPIOB->PSOR = 1<<3;
        GPIOA->PCOR = 1<<5;
        GPIOA->PCOR = 1<<1;

        I2S0->TCR5 &= ~I2S_TCR5_WNW_MASK;
        I2S0->TCR5 &= ~I2S_TCR5_W0W_MASK;
        I2S0->TCR5 |= I2S_TCR5_WNW(wordLength - 1U) | I2S_TCR5_W0W(wordLength - 1U) ;

        I2S0->TCR4 &= ~I2S_TCR4_SYWD_MASK;
        I2S0->TCR4 |= I2S_TCR4_SYWD(wordLength-1);

        SAI_TxSetBitClockRate(I2S0, 48000000, sampleRate, wordLength, 2);
     }
    else if( g_I2sFormatIndex == 0)
    {
        GPIOA->PSOR = 1<<5;
        GPIOA->PCOR = 1<<1;
        GPIOB->PCOR = 1<<3;

        I2S0->TCR5 &= ~I2S_TCR5_WNW_MASK;
        I2S0->TCR5 &= ~I2S_TCR5_W0W_MASK;
        I2S0->TCR5 |= I2S_TCR5_WNW(wordLength - 1U) | I2S_TCR5_W0W(wordLength - 1U) ;

        I2S0->TCR4 &= ~I2S_TCR4_SYWD_MASK;
        I2S0->TCR4 |= I2S_TCR4_SYWD(wordLength-1);

        SAI_TxSetBitClockRate(I2S0, 48000000, sampleRate, wordLength, 2);

    }
    g_I2sBclkSwitchFlag = 0;
}

/** @} */
