/*!
    \file    gd32h7xx_sai.c
    \brief   SAI driver

    \version 2025-01-24, V1.4.0, firmware for GD32H7xx
*/

/*
    Copyright (c) 2025, GigaDevice Semiconductor Inc.

    Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

    1. Redistributions of source code must retain the above copyright notice, this
       list of conditions and the following disclaimer.
    2. 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.
    3. Neither the name of the copyright holder 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 "gd32h7xx_sai.h"

/* SAI pdm mode microphone delay mask */
#define PDM_MICROPHONE_DELAY_MASK            ((uint32_t)0x00000007U)
/*!< bit offset of MTFCNT in SAI_CFG1 */
#define CFG1_MTFCNT_OFFSET                   ((uint32_t)0x00000007U)

/*!
    \brief      reset SAI
    \param[in]  sai_periph: SAIx(x = 0,1,2)
    \param[out] none
    \retval     none
*/
void sai_deinit(uint32_t sai_periph)
{
    switch(sai_periph) {
        case SAI0:
            /* reset SAI0 */
            rcu_periph_reset_enable(RCU_SAI0RST);
            rcu_periph_reset_disable(RCU_SAI0RST);
            break;
        case SAI1:
            /* reset SAI1 */
            rcu_periph_reset_enable(RCU_SAI1RST);
            rcu_periph_reset_disable(RCU_SAI1RST);
            break;
        case SAI2:
            /* reset SAI2 */
            rcu_periph_reset_enable(RCU_SAI2RST);
            rcu_periph_reset_disable(RCU_SAI2RST);
            break;
        default:
            break;
    }
}

/*!
    \brief      initialize the parameter of SAI structure with a default value
    \param[in]  none
    \param[out] sai_init_stuct: the initialization data needed to initialize SAI
    \retval     none
*/
void sai_struct_para_init(sai_parameter_struct *sai_init_stuct)
{
    /* initialize the initpara struct member with the default value */
    sai_init_stuct->operating_mode         = SAI_MASTER_TRANSMITTER;
    sai_init_stuct->protocol               = SAI_PROTOCOL_POLYMORPHIC;
    sai_init_stuct->data_width             = SAI_DATAWIDTH_32BIT;
    sai_init_stuct->shift_dir              = SAI_SHIFT_MSB;
    sai_init_stuct->sample_edge            = SAI_SAMPEDGE_FALLING;
    sai_init_stuct->sync_mode              = SAI_SYNCMODE_ASYNC;
    sai_init_stuct->output_drive           = SAI_OUTPUT_WITH_SAIEN;
    sai_init_stuct->clk_div_bypass         = SAI_CLKDIV_BYPASS_OFF;
    sai_init_stuct->mclk_div               = SAI_MCLKDIV_1;
    sai_init_stuct->mclk_oversampling      = SAI_MCLK_OVERSAMP_256;
    sai_init_stuct->mclk_enable            = SAI_MCLK_DISABLE;
    sai_init_stuct->fifo_threshold         = SAI_FIFOTH_EMPTY;
}

/*!
    \brief      initialize the parameter of SAI frame structure with a default value
    \param[in]  none
    \param[out] sai_frame_init_struct: the initialization data needed to initialize SAI frame
    \retval     none
*/
void sai_frame_struct_para_init(sai_frame_parameter_struct *sai_frame_init_struct)
{
    /* initialize the initpara struct member with the default value */
    sai_frame_init_struct->frame_width                = 256U;
    sai_frame_init_struct->frame_sync_width           = 128U;
    sai_frame_init_struct->frame_sync_function        = SAI_FS_FUNC_START;
    sai_frame_init_struct->frame_sync_polarity        = SAI_FS_POLARITY_LOW;
    sai_frame_init_struct->frame_sync_offset          = SAI_FS_OFFSET_BEGINNING;
}

/*!
    \brief      initialize the parameter of SAI slot structure with a default value
    \param[in]  none
    \param[out] sai_slot_init_struct: the initialization data needed to initialize SAI slot
    \retval     none
*/
void sai_slot_struct_para_init(sai_slot_parameter_struct *sai_slot_init_struct)
{
    /* initialize the initpara struct member with the default value */
    sai_slot_init_struct->slot_number            = 16U;
    sai_slot_init_struct->slot_width             = SAI_SLOT_WIDTH_DATA;
    sai_slot_init_struct->data_offset            = 0U;
    sai_slot_init_struct->slot_active            = SAI_SLOT_ACTIVE_NONE;
}

/*!
    \brief      initialize SAI
    \param[in]  sai_periph: SAIx(x=0,1,2)
    \param[in]  block: specify which bolck is initialized
                only one parameter can be selected which is shown as below:
      \arg        SAI_BLOCKx(x = 0,1)
    \param[in]  sai_struct: SAI parameter initialization stuct members of the structure
                            and the member values are shown as below:
                    operating_mode: SAI_MASTER_TRANSMITTER, SAI_MASTER_RECEIVER, SAI_SLAVE_TRANSMITTER, SAI_SLAVE_RECEIVER;
                    protocol: SAI_PROTOCOL_POLYMORPHIC, SAI_PROTOCOL_SPDIF, SAI_PROTOCOL_AC97
                    data_width: SAI_DATA_WIDTH_xBIT (x = 8, 10, 16, 20, 24, 32)
                    shift_dir: SAI_SHIFT_MSB, SAI_SHIFT_LSB
                    sample_edge: SAI_SAMPEDGE_FALLING, SAI_SAMPEDGE_RISING
                    sync_mode: SAI_SYNCMODE_ASYNC, SAI_SYNCMODE_OTHERBLOCK, SAI_SYNCMODE_EXTERNALSAI
                    output_drive: SAI_OUTPUT_WITH_SAIEN, SAI_OUTPUT_NOW
                    clk_div_bypass: SAI_CLKDIV_BYPASS_OFF, SAI_CLKDIV_BYPASS_ON
                    mck_div: SAI_MCLKDIV_x (x = 1,2,..,63)
                    mck_oversampling: SAI_MASTERCLK_OVERSAMP_256, SAI_MASTERCLK_OVERSAMP_512
                    mck_enable: SAI_MASTERCLK_DISABLE, SAI_MASTERCLK_ENABLE
                    fifo_threshold: SAI_FIFOTH_EMPTY, SAI_FIFOTH_QUARTER, SAI_FIFOTH_HALF, SAI_FIFOTH_THREE_QUARTER, SAI_FIFOTH_FULL
    \param[out] none
    \retval     none
*/
void sai_init(uint32_t sai_periph, uint32_t block, sai_parameter_struct *sai_struct)
{
    uint32_t reg = 0U;

    /* configure the SAI CFGR0 value */
    reg = SAI_CFG0(sai_periph, block);
    reg &= ~(SAI_CFG0_OPTMOD | SAI_CFG0_PROT | \
             SAI_CFG0_DATAWD | SAI_CFG0_SHIFTDIR | \
             SAI_CFG0_SAMPEDGE | SAI_CFG0_SYNCMOD | \
             SAI_CFG0_ODRIV | SAI_CFG0_BYPASS | \
             SAI_CFG0_MDIV | SAI_CFG0_MOSPR | \
             SAI_CFG0_MCLKEN | SAI_CFG0_SAIEN);

    reg |= (uint32_t)(sai_struct->operating_mode | sai_struct->protocol | \
                      sai_struct->data_width | sai_struct->shift_dir | \
                      sai_struct->sample_edge | sai_struct->sync_mode | \
                      sai_struct->output_drive | sai_struct->clk_div_bypass | \
                      sai_struct->mclk_div | sai_struct->mclk_oversampling | \
                      sai_struct->mclk_enable);
    SAI_CFG0(sai_periph, block) = reg;

    /* configure the SAI CFGR1 FIFO threshold */
    reg = SAI_CFG1(sai_periph, block);
    reg &= ~SAI_CFG1_FFTH;
    reg |= (uint32_t)(sai_struct->fifo_threshold);
    SAI_CFG1(sai_periph, block) = reg;
}

/*!
    \brief      initialize SAI frame
    \param[in]  sai_periph: SAIx(x=0,1,2)
    \param[in]  block: specify which bolck is initialized
                only one parameter can be selected which is shown as below:
      \arg        SAI_BLOCKx(x=0,1)
    \param[in]  sai_frame_struct: SAI frame parameter initialization stuct members of the structure
                            and the member values are shown as below:
                    frame_width: 1~256, frame width
                    frame_sync_width: 1~128, frame synchronization active width
                    frame_sync_function: SAI_FS_FUNC_START, SAI_FS_FUNC_START_CHANNEL
                    frame_sync_polarity: SAI_FS_POLARITY_LOW, SAI_FS_POLARITY_HIGH
                    frame_sync_offset: SAI_FS_OFFSET_BEGINNING, SAI_FS_OFFSET_ONEBITBEFORE
    \param[out] none
    \retval     none
*/
void sai_frame_init(uint32_t sai_periph, uint32_t block, sai_frame_parameter_struct *sai_frame_struct)
{
    uint32_t reg = 0U;
    reg = SAI_FCFG(sai_periph, block);
    reg &= ~(SAI_FCFG_FWD | SAI_FCFG_FSAWD | SAI_FCFG_FSFUNC | \
             SAI_FCFG_FSPL | SAI_FCFG_FSOST);
    reg |= (uint32_t)(sai_frame_struct->frame_sync_offset | \
                      sai_frame_struct->frame_sync_polarity | \
                      sai_frame_struct->frame_sync_function | \
                      ((sai_frame_struct->frame_sync_width - 1U) << 8U) | \
                      (sai_frame_struct->frame_width - 1U));
    /* config the SAI freame */
    SAI_FCFG(sai_periph, block) = reg;
}

/*!
    \brief      initialize SAI slot
    \param[in]  sai_periph: SAIx(x=0,1,2)
    \param[in]  block: specify which bolck is initialized
                only one parameter can be selected which is shown as below:
      \arg        SAI_BLOCKx(x=0,1)
    \param[in]  sai_slot_struct: SAI slot parameter initialization stuct members of the structure
                            and the member values are shown as below:
                    slot_number: 1~16, slot number
                    slot_width: SAI_SLOTWIDTH_DATA, SAI_SLOTWIDTH_16BIT, SAI_SLOTWIDTH_32BIT
                    data_offset: 0~31, data offset
                    slot_active: one or more parameters can be selected, SAI_SLOT_ACTIVE_NONE, SAI_SLOT_ACTIVE_x(x=0..15), SAI_SLOT_ACTIVE_ALL
    \param[out] none
    \retval     none
*/
void sai_slot_init(uint32_t sai_periph, uint32_t block, sai_slot_parameter_struct *sai_slot_struct)
{
    uint32_t reg = 0U;
    reg = SAI_SCFG(sai_periph, block);
    reg &= ~(SAI_SCFG_DATAOST | SAI_SCFG_SLOTWD | SAI_SCFG_SLOTNUM | SAI_SCFG_SLOTAV);
    reg = (uint32_t)(((sai_slot_struct->slot_number - 1U) << 8U) | \
                       sai_slot_struct->slot_width | \
                       sai_slot_struct->data_offset | \
                       sai_slot_struct->slot_active);
    /* configure the SAI slot */
    SAI_SCFG(sai_periph, block) = reg;
}

/*!
    \brief      SAI enable
    \param[in]  sai_periph: SAIx(x=0,1,2)
    \param[in]  block: specify which bolck is initialized
                only one parameter can be selected which is shown as below:
      \arg        SAI_BLOCKx(x=0,1)
    \param[out] none
    \retval     none
*/
void sai_enable(uint32_t sai_periph, uint32_t block)
{
    SAI_CFG0(sai_periph, block) |= SAI_CFG0_SAIEN;
}

/*!
    \brief      SAI disable
    \param[in]  sai_periph: SAIx(x=0,1,2)
    \param[in]  block: specify which bolck is initialized
                only one parameter can be selected which is shown as below:
      \arg        SAI_BLOCKx(x=0,1)
    \param[out] none
    \retval     none
*/
void sai_disable(uint32_t sai_periph, uint32_t block)
{
    SAI_CFG0(sai_periph, block) &= ~SAI_CFG0_SAIEN;
}

/*!
    \brief      SAI serial data near inactive slot output management
    \param[in]  sai_periph: SAIx(x=0,1,2)
    \param[in]  block: specify which bolck is initialized
                only one parameter can be selected which is shown as below:
      \arg        SAI_BLOCKx(x=0,1)
    \param[in]  sdout: serial data output management
                only one parameter can be selected which is shown as below:
      \arg        SAI_SDLINE_DRIVE: SD line output is driven entirely during the audio frame
      \arg        SAI_SDLINE_RELEASE: SD line output is released near inactive slots
    \param[out] none
    \retval     none
*/
void sai_sdoutput_config(uint32_t sai_periph, uint32_t block, uint32_t sdout)
{
        SAI_CFG1(sai_periph, block) &= ~SAI_CFG1_SDOM;
        SAI_CFG1(sai_periph, block) |= sdout;
}

/*!
    \brief      configure SAI mono mode
    \param[in]  sai_periph: SAIx(x=0,1,2)
    \param[in]  block: specify which bolck is initialized
                only one parameter can be selected which is shown as below:
      \arg        SAI_BLOCKx (x=0,1)
    \param[in]  mono: stereo and mono mode selection
                only one parameter can be selected which is shown as below:
      \arg        SAI_STEREO_MODE: stereo mode
      \arg        SAI_MONO_MODE: mono mode
    \param[out] none
    \retval     none
*/
void sai_monomode_config(uint32_t sai_periph, uint32_t block, uint32_t mono)
{ 
     SAI_CFG0(sai_periph, block) &= ~SAI_CFG0_MONO;
     SAI_CFG0(sai_periph, block) |= mono ;
}

/*!
    \brief      configure SAI companding mode
    \param[in]  sai_periph: SAIx(x=0,1,2)
    \param[in]  block: specify which bolck is initialized
                only one parameter can be selected which is shown as below:
      \arg        SAI_BLOCKx (x=0,1)
    \param[in]  compander: compander mode
                only one parameter can be selected which is shown as below:
      \arg        SAI_COMPANDER_OFF: no compansion applies
      \arg        SAI_COMPANDER_ULAW: u-law algorithm
      \arg        SAI_COMPANDER_ALAW: A-law algorithm
    \param[in]  complement:complement mode
                only one parameter can be selected which is shown as below:
      \arg        SAI_COMPLEMENT_1S: data represented in 1's complement form
      \arg        SAI_COMPLEMENT_2S: data represented in 2's complement form
    \param[out] none
    \retval     none
*/
void sai_companding_config(uint32_t sai_periph, uint32_t block, uint32_t compander,
                           uint32_t complement)
{
    uint32_t reg = 0U;
    reg = SAI_CFG1(sai_periph, block);
    reg &= ~(SAI_CFG1_CPLMOD | SAI_CFG1_CPAMOD);
    reg |= (compander | complement);
    SAI_CFG1(sai_periph, block) = reg;
}

/*!
    \brief      SAI mute detected enable or mute send enable
    \param[in]  sai_periph: SAIx(x=0,1,2)
    \param[in]  block: specify which bolck is initialized
                only one parameter can be selected which is shown as below:
      \arg        SAI_BLOCKx (x=0,1)
    \param[out] none
    \retval     none
*/
void sai_mute_enable(uint32_t sai_periph, uint32_t block)
{
    SAI_CFG1(sai_periph, block) |= SAI_CFG1_MT;
}

/*!
    \brief      SAI mute detected disable or mute send disable
    \param[in]  sai_periph: SAIx(x=0,1,2)
    \param[in]  block: specify which bolck is initialized
                only one parameter can be selected which is shown as below:
      \arg        SAI_BLOCKx (x=0,1)
    \param[out] none
    \retval     none
*/
void sai_mute_disable(uint32_t sai_periph, uint32_t block)
{
    SAI_CFG1(sai_periph, block) &= ~SAI_CFG1_MT;
}

/*!
    \brief      configure SAI mute value
    \param[in]  sai_periph: SAIx(x=0,1,2)
    \param[in]  block: specify which bolck is initialized
                only one parameter can be selected which is shown as below:
      \arg        SAI_BLOCKx (x=0,1)
    \param[in]  value: mute value
                only one parameter can be selected which are shown as below:
      \arg        SAI_MUTESENT_0: 0 is sent via the serial data line when mute is on
      \arg        SAI_MUTESENT_LASTFREAM: If SLOTNUM is less or equals to two, last frame is sent via the serial data line
    \param[out] none
    \retval     none
*/
void sai_mute_value_config(uint32_t sai_periph, uint32_t block, uint32_t value)
{
    SAI_CFG1(sai_periph, block) &= ~SAI_CFG1_MTVAL;
    SAI_CFG1(sai_periph, block) |= value;
}

/*!
    \brief      configure SAI mute frame count
    \param[in]  sai_periph: SAIx(x=0,1,2)
    \param[in]  block: specify which bolck is initialized
                only one parameter can be selected which is shown as below:
      \arg        SAI_BLOCKx (x=0,1)
    \param[in]  count: 0~63, mute frame count
    \param[out] none
    \retval     none
*/
void sai_mute_count_config(uint32_t sai_periph, uint32_t block, uint32_t count)
{
    uint32_t reg = 0U;
    reg = SAI_CFG1(sai_periph, block);
    reg &= ~SAI_CFG1_MTFCNT;
    reg |= count << CFG1_MTFCNT_OFFSET;
    SAI_CFG1(sai_periph, block) = reg;
}

/*!
    \brief      SAI transmit data
    \param[in]  sai_periph: SAIx(x=0,1,2)
    \param[in]  block: specify which bolck is initialized
                only one parameter can be selected which is shown as below:
      \arg        SAI_BLOCKx (x=0,1)
    \param[in]  data: 32-bit data
    \param[out] none
    \retval     none
*/
void sai_data_transmit(uint32_t sai_periph, uint32_t block, uint32_t data)
{
    SAI_DATA(sai_periph, block) = data;
}

/*!
    \brief      SAI receive data
    \param[in]  sai_periph: SAIx(x=0,1,2)
    \param[in]  block: specify which bolck is initialized
                only one parameter can be selected which is shown as below:
      \arg        SAI_BLOCKx (x=0,1)
    \param[out] none
    \retval     received data
*/
uint32_t sai_data_receive(uint32_t sai_periph, uint32_t block)
{
    return SAI_DATA(sai_periph, block);
}

/*!
    \brief      get SAI fifo status
    \param[in]  sai_periph: SAIx(x=0,1,2)
    \param[in]  block: specify which bolck is initialized
                only one parameter can be selected which is shown as below:
      \arg        SAI_BLOCKx (x=0,1)
    \param[out] none
    \retval     state of fifo
      \arg        FIFO_EMPTY: empty
      \arg        FIFO_EMPTY_TO_1_4_FULL: empty < fifo_level <= 1/4_full
      \arg        FIFO_1_4_FULL_TO_1_2_FULL: 1/4_full < fifo_level <= 1/2_full
      \arg        FIFO_1_2_FULL_TO_3_4_FULL: 1/2_full < fifo_level <= 3/4_full
      \arg        FIFO_3_4_FULL_TO_FULL: 3/4_full < fifo_level < full
      \arg        FIFO_FULL: full
*/
sai_fifo_state_enum sai_fifo_status_get(uint32_t sai_periph, uint32_t block)
{
    sai_fifo_state_enum sai_fifo_state = FIFO_EMPTY;

    if(SAI_FIFO_STAT_EMPTY == (SAI_STAT(sai_periph, block) & SAI_STAT_FFSTAT)) {
        sai_fifo_state = FIFO_EMPTY;
    } else if(SAI_FIFO_STAT_QUARTER == (SAI_STAT(sai_periph, block) & SAI_STAT_FFSTAT)) {
        sai_fifo_state = FIFO_EMPTY_TO_1_4_FULL;
    } else if(SAI_FIFO_STAT_HALF == (SAI_STAT(sai_periph, block) & SAI_STAT_FFSTAT)) {
        sai_fifo_state = FIFO_1_4_FULL_TO_1_2_FULL;
    } else if(SAI_FIFO_STAT_THREE_QUARTER == (SAI_STAT(sai_periph, block) & SAI_STAT_FFSTAT)) {
        sai_fifo_state = FIFO_1_2_FULL_TO_3_4_FULL;
    } else if(SAI_FIFO_STAT_NEARFULL == (SAI_STAT(sai_periph, block) & SAI_STAT_FFSTAT)) {
        sai_fifo_state = FIFO_3_4_FULL_TO_FULL;
    } else {
        sai_fifo_state = FIFO_FULL;
    }

    return sai_fifo_state;
}

/*!
    \brief      SAI fifo flush
    \param[in]  sai_periph: SAIx(x=0,1,2)
    \param[in]  block: specify which bolck is initialized
                only one parameter can be selected which is shown as below:
      \arg        SAI_BLOCKx (x=0,1)
    \param[out] none
    \retval     none
*/
void sai_fifo_flush(uint32_t sai_periph, uint32_t block)
{
    SAI_CFG1(sai_periph, block) |= SAI_CFG1_FLUSH;
}

/*!
    \brief      enable SAI dma
    \param[in]  sai_periph: SAIx(x=0,1,2)
    \param[in]  block: specify which bolck is initialized
                only one parameter can be selected which is shown as below:
      \arg        SAI_BLOCKx (x=0,1)
    \param[out] none
    \retval     none
*/
void sai_dma_enable(uint32_t sai_periph, uint32_t block)
{
    SAI_CFG0(sai_periph, block) |= SAI_CFG0_DMAEN;
}

/*!
    \brief      disable SAI dma
    \param[in]  sai_periph: SAIx(x=0,1,2)
    \param[in]  block: specify which bolck is initialized
                only one parameter can be selected which is shown as below:
      \arg        SAI_BLOCKx (x=0,1)
    \param[out] none
    \retval     none
*/
void sai_dma_disable(uint32_t sai_periph, uint32_t block)
{
    SAI_CFG0(sai_periph, block) &= ~SAI_CFG0_DMAEN;
}

/*!
    \brief      SAI synchronization input select
    \param[in]  sai_periph: SAIx(x=0,1,2)
    \param[in]  input: specify which external SAI to be select for synchronization
                only one parameter can be selected which are shown as below:
      \arg        SAI_SYNCINPUT_SAI0: SAI1 or SAI2 selects the synchronization coming from SAI0
      \arg        SAI_SYNCINPUT_SAI1: SAI0 or SAI2 selects the synchronization coming from SAI1
      \arg        SAI_SYNCINPUT_SAI2: SAI0 or SAI1 selects the synchronization coming from SAI2
    \param[out] none
    \retval     none
*/
void sai_sync_input_config(uint32_t sai_periph, uint32_t input)
{
    uint32_t reg = 0U;
    reg = SAI_SYNCFG(sai_periph);
    reg &= ~SAI_SYNCFG_SYNI;
    reg |= input;
    SAI_SYNCFG(sai_periph) = reg;
}

/*!
    \brief      SAI synchronization output select
    \param[in]  sai_periph: SAIx(x=0,1,2)
    \param[in]  output: specify which block to be used for further synchronization for others SAI
                only one parameter can be selected which are shown as below:
      \arg        SAI_SYNCOUTPUT_OFF: no synchronization output signals
      \arg        SAI_SYNCOUTPUT_BLOCK0: block 0 used for further synchronization for others SAI
      \arg        SAI_SYNCOUTPUT_BLOCK1: block 1 used for further synchronization for others SAI
    \param[out] none
    \retval     none
*/
void sai_sync_output_config(uint32_t sai_periph, uint32_t output)
{
    uint32_t reg = 0U;
    reg = SAI_SYNCFG(sai_periph);
    reg &= ~SAI_SYNCFG_SYNO;
    reg |= output;
    SAI_SYNCFG(sai_periph) = reg;
}

/*!
    \brief      enable SAI pdm mode
    \param[in]  sai_periph: SAIx(x=0,1,2)
    \param[out] none
    \retval     none
*/
void sai_pdm_enable(uint32_t sai_periph)
{
    SAI_PDMCTL(sai_periph) |= SAI_PDMCTL_PDMEN;
}

/*!
    \brief      disable SAI pdm mode
    \param[in]  sai_periph: SAIx(x=0,1,2)
    \param[out] none
    \retval     none
*/
void sai_pdm_disable(uint32_t sai_periph)
{
    SAI_PDMCTL(sai_periph) &= ~SAI_PDMCTL_PDMEN;
}

/*!
    \brief      configure SAI pdm mode microphone number
    \param[in]  sai_periph: SAIx(x=0,1,2)
    \param[in]  microphonenum: 2, 4, 6 or 8(not applicable to GD32H7xx), select microphones number
    \param[out] none
    \retval     none
*/
void sai_pdm_microphone_number_config(uint32_t sai_periph, uint32_t microphonenum)
{
    uint32_t temp = SAI_PDMCTL(sai_periph);
    temp &= ~SAI_PDMCTL_MICNUMSEL;
    temp |= ((microphonenum / 2U - 1U) << 4U);
    SAI_PDMCTL(sai_periph) = temp;
}

/*!
    \brief      configure SAI pdm mode microphone delay
    \param[in]  sai_periph: SAIx(x=0,1,2)
    \param[in]  microphone: specify which microphone delay parameter to config
                only one parameter can be selected which are shown as below:
      \arg        SAI_PDM_MICROPHONE0_L: microphone 0 channel left
      \arg        SAI_PDM_MICROPHONE0_R: microphone 0 channel right
      \arg        SAI_PDM_MICROPHONE1_L: microphone 1 channel left
      \arg        SAI_PDM_MICROPHONE1_R: microphone 1 channel right
      \arg        SAI_PDM_MICROPHONE2_L: microphone 2 channel left
      \arg        SAI_PDM_MICROPHONE2_R: microphone 2 channel right
      \arg        SAI_PDM_MICROPHONE3_L: microphone 3 channel left, (not applicable to GD32H7xx)
      \arg        SAI_PDM_MICROPHONE3_R: microphone 3 channel right, (not applicable to GD32H7xx)
    \param[in]  delay: 0~7, the microphone data flow delay period
    \param[out] none
    \retval     none
*/
void sai_pdm_delay_config(uint32_t sai_periph, uint32_t microphone, uint32_t delay)
{
    uint32_t temp = SAI_PDMCFG(sai_periph);
    temp &= ~(PDM_MICROPHONE_DELAY_MASK << (microphone * 4U));
    temp |= (delay << (microphone * 4U));
    SAI_PDMCFG(sai_periph) = temp;
}

/*!
    \brief      enable SAI pdm mode clock line 0
    \param[in]  sai_periph: SAIx(x=0,1,2)
    \param[out] none
    \retval     none
*/
void sai_pdm_clk0_enable(uint32_t sai_periph)
{
    SAI_PDMCTL(sai_periph) |= SAI_PDMCTL_CLKL0EN;
}

/*!
    \brief      disable SAI pdm mode clock line 0
    \param[in]  sai_periph: SAIx(x=0,1,2)
    \param[out] none
    \retval     none
*/
void sai_pdm_clk0_disable(uint32_t sai_periph)
{
    SAI_PDMCTL(sai_periph) &= ~SAI_PDMCTL_CLKL0EN;
}

/*!
    \brief      enable SAI pdm mode clock line 1
    \param[in]  sai_periph: SAIx(x=0,1,2)
    \param[out] none
    \retval     none
*/
void sai_pdm_clk1_enable(uint32_t sai_periph)
{
    SAI_PDMCTL(sai_periph) |= SAI_PDMCTL_CLKL1EN;
}

/*!
    \brief      disable SAI pdm mode clock line 1
    \param[in]  sai_periph: SAIx(x=0,1,2)
    \param[out] none
    \retval     none
*/
void sai_pdm_clk1_disable(uint32_t sai_periph)
{
    SAI_PDMCTL(sai_periph) &= ~SAI_PDMCTL_CLKL1EN;
}

/*!
    \brief      enable the SAI interrupt
    \param[in]  sai_periph: SAIx(x=0,1,2)
    \param[in]  block: specify which bolck is initialized
                only one parameter can be selected which is shown as below:
      \arg        SAI_BLOCKx(x=0,1)
    \param[in]  interrupt: specify which interrupt to enable
                one or more parameters can be selected which are shown as below:
      \arg        SAI_INT_OUERR: FIFO overrun or underrun interrupt enable
      \arg        SAI_INT_MTDET: mute detection interrupt enable
      \arg        SAI_INT_ERRCK: error clock interrupt enable
      \arg        SAI_INT_FFREQ: FIFO request interrupt enable
      \arg        SAI_INT_ACNRDY: audio codec not ready interrupt enable
      \arg        SAI_INT_FSADET: frame synchronization advanced detection interrupt enable
      \arg        SAI_INT_FSPDET: frame synchronization postpone detection interrupt enable
    \param[out] none
    \retval     none
*/
void sai_interrupt_enable(uint32_t sai_periph, uint32_t block, uint32_t interrupt)
{
    SAI_INTEN(sai_periph, block) |= interrupt;
}

/*!
    \brief      disable the SAI interrupt
    \param[in]  sai_periph: SAIx(x=0,1,2)
    \param[in]  block: specify which bolck is initialized
                only one parameter can be selected which is shown as below:
      \arg        SAI_BLOCKx(x=0,1)
    \param[in]  interrupt: specify which interrupt to disable
                one or more parameters can be selected which are shown as below:
      \arg        SAI_INT_OUERR: FIFO overrun or underrun interrupt
      \arg        SAI_INT_MTDET: mute detection interrupt
      \arg        SAI_INT_ERRCK: error clock interrupt
      \arg        SAI_INT_FFREQ: FIFO request interrupt
      \arg        SAI_INT_ACNRDY: audio codec not ready interrupt
      \arg        SAI_INT_FSADET: frame synchronization advanced detection interrupt
      \arg        SAI_INT_FSPDET: frame synchronization postpone detection interrupt
    \param[out] none
    \retval     none
*/
void sai_interrupt_disable(uint32_t sai_periph, uint32_t block, uint32_t interrupt)
{
    SAI_INTEN(sai_periph, block) &= ~interrupt;
}

/*!
    \brief      get the SAI interrupt flag
    \param[in]  sai_periph: SAIx(x=0,1,2)
    \param[in]  block: specify which bolck is initialized
                only one parameter can be selected which is shown as below:
      \arg        SAI_BLOCKx(x=0,1)
    \param[in]  interrupt: specify which interrupt flag to get
                only one parameter can be selected which are shown as below:
      \arg        SAI_FLAG_OUERR: FIFO overrun or underrun interrupt flag
      \arg        SAI_FLAG_MTDET: mute detection interrupt flag
      \arg        SAI_FLAG_ERRCK: error clock interrupt flag
      \arg        SAI_FLAG_FFREQ: FIFO request interrupt flag
      \arg        SAI_FLAG_ACNRDY: audio codec not ready interrupt flag
      \arg        SAI_FLAG_FSADET: frame synchronization advanced detection interrupt flag
      \arg        SAI_FLAG_FSPDET: frame synchronization postpone detection interrupt flag
    \param[out] none
    \retval     FlagStatus: SET or RESET
*/
FlagStatus sai_interrupt_flag_get(uint32_t sai_periph, uint32_t block, uint32_t interrupt)
{
    uint32_t inten = 0U;
    inten = SAI_INTEN(sai_periph, block) & interrupt;
    if((RESET != (SAI_STAT(sai_periph, block) & interrupt)) && (RESET != inten)) {
        return SET;
    } else {
        return RESET;
    }
}

/*!
    \brief      clear the SAI interrupt flag
    \param[in]  sai_periph: SAIx(x=0,1,2)
    \param[in]  block: specify which bolck is initialized
                only one parameter can be selected which is shown as below:
      \arg        SAI_BLOCKx(x=0,1)
    \param[in]  interrupt: specify which interrupt flag to clear
                one or more parameters can be selected which are shown as below:
      \arg         SAI_FLAG_OUERR: FIFO overrun or underrun interrupt flag
      \arg         SAI_FLAG_MTDET: mute detection interrupt flag
      \arg         SAI_FLAG_ERRCK: error clock interrupt flag
      \arg         SAI_FLAG_ACNRDY: audio codec not ready interrupt flag
      \arg         SAI_FLAG_FSADET: frame synchronization advanced detection interrupt flag
      \arg         SAI_FLAG_FSPDET: frame synchronization postpone detection interrupt flag
    \param[out] none
    \retval     none
*/
void sai_interrupt_flag_clear(uint32_t sai_periph, uint32_t block, uint32_t interrupt)
{
    SAI_INTC(sai_periph, block) = interrupt;
}

/*!
    \brief      get the SAI flag
    \param[in]  sai_periph: SAIx(x=0,1,2)
    \param[in]  block: specify which bolck is initialized
                only one parameter can be selected which is shown as below:
      \arg        SAI_BLOCKx(x=0,1)
    \param[in]  flag: specify which flag to get
                only one parameter can be selected which are shown as below:
      \arg        SAI_FLAG_OUERR: FIFO overrun or underrun flag
      \arg        SAI_FLAG_MTDET: mute detection flag
      \arg        SAI_FLAG_ERRCK: error clock flag
      \arg        SAI_FLAG_FFREQ: FIFO request flag
      \arg        SAI_FLAG_ACNRDY: audio codec not ready flag
      \arg        SAI_FLAG_FSADET: frame synchronization advanced detection flag
      \arg        SAI_FLAG_FSPDET: frame synchronization postpone detection flag
    \param[out] none
    \retval     FlagStatus: SET or RESET
*/
FlagStatus sai_flag_get(uint32_t sai_periph, uint32_t block, uint32_t flag)
{
    if(RESET != (SAI_STAT(sai_periph, block) & flag)) {
        return SET;
    } else {
        return RESET;
    }
}

/*!
    \brief      clear the SAI flag
    \param[in]  sai_periph: SAIx(x=0,1,2)
    \param[in]  block: specify which bolck is initialized
                only one parameter can be selected which is shown as below:
      \arg        SAI_BLOCKx(x=0,1)
    \param[in]  flag: specify which flag to clear
                one or more parameters can be selected which are shown as below:
      \arg         SAI_FLAG_OUERR: FIFO overrun or underrun flag
      \arg         SAI_FLAG_MTDET: mute detection flag
      \arg         SAI_FLAG_ERRCK: error clock flag
      \arg         SAI_FLAG_ACNRDY: audio codec not ready flag
      \arg         SAI_FLAG_FSADET: frame synchronization advanced detection flag
      \arg         SAI_FLAG_FSPDET: frame synchronization postpone detection flag
    \param[out] none
    \retval     none
*/
void sai_flag_clear(uint32_t sai_periph, uint32_t block, uint32_t flag)
{
    SAI_INTC(sai_periph, block) = flag;
}
