//*****************************************************************************
//
//! @file am_vos_board_setup.c
//!
//! @brief Setup board peripherals
//
//*****************************************************************************

//*****************************************************************************
//
// Copyright (c) 2024, Ambiq Micro, 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:
//
// 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.
//
// Third party software included in this distribution is subject to the
// additional license terms as defined in the /docs/licenses directory.
//
// 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.
//
// This is part of revision ambiqvos_r4.5-fdfa8cf6a4 of the AmbiqSuite Development Package.
//
//*****************************************************************************

#include "am_vos_sys_config.h"
#include "am_vos_board_setup.h"

#include "am_vos_utils.h"

#include "am_vos_task.h"
#include "am_vos_init.h"
#include "am_vos_spp.h"
#include "am_vos_audio.h"
#include "am_vos_logic.h"

#if configUSE_BLE
#include "am_vos_ble.h"
#endif // configUSE_BLE

#if configUSE_AAD
#include "am_devices_t5838.h"
#endif // configUSE_AAD

#if configUSE_DTCM_SRAM_LP
#define AM_ALLOC_AREA_DEFINE
#else
#define AM_ALLOC_AREA_DEFINE AM_SHARED_RW
#endif

VosBoardInfo g_sVosBrd =
{
#if USE_DMIC_PDM
    .pvPDMHandle = NULL,
#endif // USE_DMIC_PDM

#if USE_AMIC_AUDADC
    .pvAUDADCHandle = NULL,
    .bAUDADCDMAError = false,
    
    .sAudadcGainConfig.ui32LGA        = 0,       // 0 code
    .sAudadcGainConfig.ui32HGADELTA   = 0,        // delta from the LGA field
    .sAudadcGainConfig.ui32LGB        = 0,       // 0 code
    .sAudadcGainConfig.ui32HGBDELTA   = 0,        // delta from the LGB field

    .sAudadcGainConfig.eUpdateMode    = AM_HAL_AUDADC_GAIN_UPDATE_IMME,
#endif // USE_AMIC_AUDADC

#if configUSE_MUTE_MIC
    .bMicStatus = true,
#endif // configUSE_MUTE_MIC

#if configUSE_SPP_P2A
    .fPGAdB = 0,
#endif

    .bBurstModeAvailable = false
};

#if (AM_VOS_BENCHMARK_TIMER_CLK == AM_VOS_BENCH_TIMER_DWT)
volatile unsigned int *DWT_CYCCNT  ;
volatile unsigned int *DWT_CONTROL ;
volatile unsigned int *SCB_DEMCR   ;

#define DWT_CYCCNTENA_BIT       (1UL<<0)            // CYCCNTENA bit in DWT_CONTROL register

void am_vos_bench_dwt_reset(){
    DWT_CYCCNT   = (unsigned int volatile *)0xE0001004; //address of the register
    DWT_CONTROL  = (unsigned int volatile *)0xE0001000; //address of the register
    SCB_DEMCR    = (unsigned int volatile *)0xE000EDFC; //address of the register
    *SCB_DEMCR   = *SCB_DEMCR | 0x01000000;
    *DWT_CYCCNT  = 0; // reset the counter
    *DWT_CONTROL = 0; 
}

void am_vos_bench_dwt_start(){
    *DWT_CONTROL = *DWT_CONTROL | DWT_CYCCNTENA_BIT; // enable the counter
}

void am_vos_bench_dwt_stop(){
    *DWT_CONTROL = *DWT_CONTROL & ~DWT_CYCCNTENA_BIT; // disable the counter    
}

unsigned int am_vos_bench_dwt_getcycle(){
    return *DWT_CYCCNT;
}
#endif // (AM_VOS_BENCHMARK_TIMER_CLK == AM_VOS_BENCH_TIMER_DWT)

void am_vos_timer_benchmark_init(void)
{
    am_hal_timer_config_t sTimer5Config;
    
    am_hal_timer_default_config_set(&sTimer5Config);
    
    // Set up Timer5.
#if (AM_VOS_BENCHMARK_TIMER_CLK == AM_VOS_BENCH_TIMER_6MHZ)
    sTimer5Config.eInputClock = TIMER_CTRL0_TMR0CLK_HFRC_DIV16; // 96MHz / 16 = 6 MHz
#elif (AM_VOS_BENCHMARK_TIMER_CLK == AM_VOS_BENCH_TIMER_32KHZ)
    sTimer5Config.eInputClock = AM_HAL_TIMER_CLOCK_XT;
#endif
    sTimer5Config.eFunction = AM_HAL_TIMER_FN_UPCOUNT;

    am_hal_timer_clear(5);
    am_hal_timer_config(5, &sTimer5Config);

#if (AM_VOS_BENCHMARK_TIMER_CLK == AM_VOS_BENCH_TIMER_6MHZ)
    am_hal_timer_compare0_set(5, 6 * 1024 * 1024 * 5);
#elif (AM_VOS_BENCHMARK_TIMER_CLK == AM_VOS_BENCH_TIMER_32KHZ)
    am_hal_timer_compare0_set(5, 32768 * 100);
#endif

    am_hal_timer_start(5);
}

uint32_t am_vos_timer_benchmark_read(void)
{
    return am_hal_timer_read(5);
}

void am_vos_timer_benchmark_clear_and_start(void)
{
    am_hal_timer_clear(5);
}

void
am_vos_gpio_enable_irq(uint32_t ui32GroupIrq, uint32_t gpio)
{
    uint32_t    ui32IntStatus;

    am_hal_gpio_interrupt_irq_status_get(ui32GroupIrq, false, &ui32IntStatus);
    if(ui32IntStatus & (1 << (gpio % 32)))
    {
        am_hal_gpio_interrupt_irq_clear(ui32GroupIrq, ui32IntStatus);
        AM_VOS_LOG_DEBUG("ui32IntStatus 0x%x\n", ui32IntStatus);
    }

    am_hal_gpio_interrupt_control(AM_HAL_GPIO_INT_CHANNEL_0,
                                  AM_HAL_GPIO_INT_CTRL_INDV_ENABLE,
                                  (void *)&gpio);
}

void
am_vos_gpio_disable_irq(uint32_t gpio)
{
    am_hal_gpio_interrupt_control(AM_HAL_GPIO_INT_CHANNEL_0,
                                  AM_HAL_GPIO_INT_CTRL_INDV_DISABLE,
                                  (void *)&gpio);
}

#if configUSE_PUSH_TO_TALK || configUSE_MUTE_MIC || configUSE_RTT_RECORDER
void
am_vos_button_int_register(void)
{
    uint32_t IntNum = 0;
#if configUSE_RTT_RECORDER
    IntNum = RTT_DUMP_BUTTON;
    am_hal_gpio_interrupt_register(AM_HAL_GPIO_INT_CHANNEL_0, RTT_DUMP_BUTTON,
                                   (am_hal_gpio_handler_t)am_vos_rtt_amu2s_process, NULL);
    am_hal_gpio_interrupt_control(AM_HAL_GPIO_INT_CHANNEL_0,
                                  AM_HAL_GPIO_INT_CTRL_INDV_ENABLE,
                                  (void *)&IntNum);
#endif // configUSE_RTT_RECORDER
#if configUSE_PAIRING_MODE_BTN && configUSE_BLE
    IntNum = PAIRING_MODE_BUTTON;
    am_hal_gpio_interrupt_register(AM_HAL_GPIO_INT_CHANNEL_0, PAIRING_MODE_BUTTON,
                                   (am_hal_gpio_handler_t)am_vos_pairing_mode_process, NULL);
    am_hal_gpio_interrupt_control(AM_HAL_GPIO_INT_CHANNEL_0,
                                  AM_HAL_GPIO_INT_CTRL_INDV_ENABLE,
                                  (void *)&IntNum);
#endif // configUSE_PAIRING_MODE_BTN
#if configUSE_PUSH_TO_TALK
    IntNum = PUSH_TO_TALK_BUTTON;
    am_hal_gpio_interrupt_register(AM_HAL_GPIO_INT_CHANNEL_0, PUSH_TO_TALK_BUTTON,
                                   (am_hal_gpio_handler_t)am_vos_push_to_talk_process, NULL);
    am_hal_gpio_interrupt_control(AM_HAL_GPIO_INT_CHANNEL_0,
                                  AM_HAL_GPIO_INT_CTRL_INDV_ENABLE,
                                  (void *)&IntNum);
#endif // configUSE_PUSH_TO_TALK
#if configUSE_MUTE_MIC
    IntNum = MUTE_MIC_BUTTON;
    am_hal_gpio_interrupt_register(AM_HAL_GPIO_INT_CHANNEL_0, MUTE_MIC_BUTTON,
                                   (am_hal_gpio_handler_t)am_vos_mute_mic_process, NULL);
    am_hal_gpio_interrupt_control(AM_HAL_GPIO_INT_CHANNEL_0,
                                  AM_HAL_GPIO_INT_CTRL_INDV_ENABLE,
                                  (void *)&IntNum);
#endif // configUSE_MUTE_MIC
}

void
am_vos_button_init(void)
{
    uint32_t ui32IntStatus;

    am_hal_gpio_pinconfig(AM_BSP_GPIO_BUTTON0, g_AM_BSP_GPIO_BUTTON0);
    am_hal_gpio_pinconfig(AM_BSP_GPIO_BUTTON1, g_AM_BSP_GPIO_BUTTON1);

    am_vos_button_int_register();

    //
    // Clear the GPIO Interrupt (write to clear).
    //
    AM_CRITICAL_BEGIN
    am_hal_gpio_interrupt_irq_status_get(GPIO0_001F_IRQn, false, &ui32IntStatus);
    am_hal_gpio_interrupt_irq_clear(GPIO0_001F_IRQn, ui32IntStatus);
    AM_CRITICAL_END
}
#endif // configUSE_PUSH_TO_TALK || configUSE_MUTE_MIC || configUSE_RTT_RECORDER

#if configUSE_AAD
void
am_vos_aad_gpio_init(void)
{
    uint32_t IntNum = AM_T5838_WAKE_PIN;
    am_hal_gpio_pincfg_t sPinCfg = {0,};

    sPinCfg.GP.cfg_b.uFuncSel           = AAD_WAKE_PIN_MODE_SEL, // GPIO
    sPinCfg.GP.cfg_b.eGPInput           = AM_HAL_GPIO_PIN_INPUT_ENABLE;
    sPinCfg.GP.cfg_b.eIntDir            = AM_HAL_GPIO_PIN_INTDIR_LO2HI;
    sPinCfg.GP.cfg_b.eDriveStrength     = AM_HAL_GPIO_PIN_DRIVESTRENGTH_0P1X;
    
    //sPinCfg.GP.cfg_b.ePullup            = AM_HAL_GPIO_PIN_PULLUP_12K;
    sPinCfg.GP.cfg_b.ePullup            = AM_HAL_GPIO_PIN_PULLUP_NONE;

    am_hal_gpio_pinconfig(AM_T5838_WAKE_PIN, sPinCfg);

    am_hal_gpio_interrupt_register(AM_HAL_GPIO_INT_CHANNEL_0, IntNum, am_vos_aad_handler, NULL);
    am_hal_gpio_interrupt_control(AM_HAL_GPIO_INT_CHANNEL_0,
                                  AM_HAL_GPIO_INT_CTRL_INDV_DISABLE,
                                  (void *)&IntNum);
}
#endif // configUSE_AAD

//-----------------------------------------------------------------------------
// METHOD:  am_vos_gpio_init
// PURPOSE: Setup I/O pins
//-----------------------------------------------------------------------------
//*****************************************************************************
//
// CTRL_BUTTON0 : RTT recorder switch button / Membrain switch button - Push to Talk (Maya)
// CTRL_BUTTON1 : Push To Talk / Power on-off switch (Maya)
// CTRL_BUTTON2 : MIC Mute button
// AM_T5838_WAKE_PIN : AAD Wake GPIO pin
//
//*****************************************************************************
void
am_vos_gpio_init(void)
{
#if configUSE_PUSH_TO_TALK || configUSE_MUTE_MIC || configUSE_RTT_RECORDER
    am_vos_button_init();
#endif // configUSE_PUSH_TO_TALK || configUSE_MUTE_MIC || configUSE_RTT_RECORDER

#if configUSE_LEDs
    am_devices_led_array_init(am_bsp_psLEDs, AM_BSP_NUM_LEDS);
#endif

#if configUSE_AAD
    am_vos_aad_gpio_init();
#endif // configUSE_AAD

    NVIC_SetPriority(GPIO0_001F_IRQn, AM_IRQ_PRIORITY_DEFAULT);
    NVIC_EnableIRQ(GPIO0_001F_IRQn);
}

#if USE_DMIC_PDM
static const IRQn_Type g_ePdmInterrupts[] =
{
    PDM0_IRQn,
#if defined(AM_PART_APOLLO4B) || defined(AM_PART_APOLLO4P)
    PDM1_IRQn,
    PDM2_IRQn,
    PDM3_IRQn
#endif // defined(AM_PART_APOLLO4B) || defined(AM_PART_APOLLO4P)
};

//*****************************************************************************
//
// Start a transaction to get some number of bytes from the PDM interface.
//
//*****************************************************************************
AM_ALLOC_AREA_DEFINE uint32_t gpui32PdmDmaBuf[PCM_FRAME_SIZE_SAMPLES * USE_MIC_NUM * 2];

void
am_vos_pdm_trigger_dma(void)
{
    //
    // Configure DMA and target address.
    //
    g_sVosBrd.sPdmTransfer.ui32TotalCount = (PCM_FRAME_SIZE_SAMPLES * PCM_SAMPLE_BYTES * USE_MIC_NUM * 2);
    g_sVosBrd.sPdmTransfer.ui32TargetAddr = (uint32_t)(&gpui32PdmDmaBuf[0]);
    g_sVosBrd.sPdmTransfer.ui32TargetAddrReverse = g_sVosBrd.sPdmTransfer.ui32TargetAddr + g_sVosBrd.sPdmTransfer.ui32TotalCount;

    //
    // Start the data transfer.
    //
    am_hal_pdm_dma_start(g_sVosBrd.pvPDMHandle, &(g_sVosBrd.sPdmTransfer));
}

//-----------------------------------------------------------------------------
// METHOD:  am_vos_pdm_init
// PURPOSE: PDM module configuration
//-----------------------------------------------------------------------------
void
am_vos_pdm_init(void)
{
#if configUSE_SPP_P2A
    g_sVosBrd.fPGAdB = AM_PGA_DEFAULT_GAIN_DB;
#endif

    // PDM configuration information.
    am_hal_pdm_config_t sPdmConfig =
    {
        //
        // Example setting:
        //
#if USE_PDM_CLK_SRC_HFRC_24MHZ
        .ePDMClkSpeed = AM_HAL_PDM_CLK_HFRC_24MHZ,
        .eClkDivider = AM_HAL_PDM_MCLKDIV_1,
#elif USE_PDM_CLK_SRC_HFRC2ADJ_24MHZ
        .ePDMClkSpeed = AM_HAL_PDM_CLK_HFRC2ADJ_24_576MHZ,
        .eClkDivider = AM_HAL_PDM_MCLKDIV_1,
#elif USE_PDM_CLK_SRC_HFXTAL_32MHZ
        .ePDMClkSpeed = AM_HAL_PDM_CLK_HFXTAL,
        .eClkDivider = AM_HAL_PDM_MCLKDIV_2,
#endif

#if USE_PDM_CLK_SRC_HFRC_24MHZ || USE_PDM_CLK_SRC_HFRC2ADJ_24MHZ
#if USE_PDM_CLK_750KHZ
        //  750 KHz PDM CLK OUT:
        //      AM_HAL_PDM_CLK_24MHZ, AM_HAL_PDM_MCLKDIV_1, AM_HAL_PDM_PDMA_CLKO_DIV15
        //  15.625KHz 24bit Sampling:
        //      DecimationRate = 24
        .ePDMAClkOutDivder = AM_HAL_PDM_PDMA_CLKO_DIV15,
        .ui32DecimationRate = 24,
#elif USE_PDM_CLK_1_5MHZ
        //  1.5MHz PDM CLK OUT:
        //      AM_HAL_PDM_CLK_24MHZ, AM_HAL_PDM_MCLKDIV_1, AM_HAL_PDM_PDMA_CLKO_DIV7
        //  15.625KHz 24bit Sampling:
        //      DecimationRate = 48
        .ePDMAClkOutDivder = AM_HAL_PDM_PDMA_CLKO_DIV7,
        .ui32DecimationRate = 48,
#endif // USE_PDM_CLK_1_5MHZ
#elif USE_PDM_CLK_SRC_HFXTAL_32MHZ
        .ePDMAClkOutDivder = AM_HAL_PDM_PDMA_CLKO_DIV13,
        .ui32DecimationRate = 24,
#endif
        .eStepSize = AM_HAL_PDM_GAIN_STEP_0_13DB,

        .bHighPassEnable = AM_HAL_PDM_HIGH_PASS_ENABLE,
        .ui32HighPassCutoff = 0x3,

#if configUSE_SPP_P2A
        .eLeftGain = (g_sVosBrd.fPGAdB - (AM_PGA_LOWEST_GAIN_DB)) / AM_PGA_ADJ_STEP_DB,
        .eRightGain = (g_sVosBrd.fPGAdB - (AM_PGA_LOWEST_GAIN_DB)) / AM_PGA_ADJ_STEP_DB,
#else
        .eLeftGain = (am_hal_pdm_gain_e)(AM_PGA_DEFAULT_GAIN_DB - (AM_PGA_LOWEST_GAIN_DB)) / AM_PGA_ADJ_STEP_DB,
        .eRightGain = (am_hal_pdm_gain_e)(AM_PGA_DEFAULT_GAIN_DB - (AM_PGA_LOWEST_GAIN_DB)) / AM_PGA_ADJ_STEP_DB,
#endif

        .bDataPacking = 1,
#if USE_MIC_DUAL
        .ePCMChannels = AM_HAL_PDM_CHANNEL_STEREO,
#elif USE_MIC_SINGLE
        .ePCMChannels = AM_HAL_PDM_CHANNEL_LEFT,
#endif

        .bPDMSampleDelay = AM_HAL_PDM_CLKOUT_PHSDLY_NONE,
        .ui32GainChangeDelay = AM_HAL_PDM_CLKOUT_DELAY_NONE,

        .bSoftMute = 0,
        .bLRSwap = 0,
    };
    //
    // Initialize, power-up, and configure the PDM.
    //
    am_hal_pdm_initialize(USE_PDM_MODULE, &g_sVosBrd.pvPDMHandle);
    am_hal_pdm_power_control(g_sVosBrd.pvPDMHandle, AM_HAL_PDM_POWER_ON, false);

#if USE_PDM_CLK_SRC_HFXTAL_32MHZ
    // use external XTHS, not reference clock
    am_hal_mcuctrl_control_arg_t ctrlArgs = g_amHalMcuctrlArgDefault;
    ctrlArgs.ui32_arg_hfxtal_user_mask  = 1 << (AM_HAL_HCXTAL_PDM_BASE_EN + USE_PDM_MODULE);
    am_hal_mcuctrl_control(AM_HAL_MCUCTRL_CONTROL_EXTCLK32M_NORMAL, &ctrlArgs);
    am_util_delay_us(500);
#elif USE_PDM_CLK_SRC_HFRC2ADJ_24MHZ
    // use external XTHS, not reference clock
    am_hal_mcuctrl_control_arg_t ctrlArgs = g_amHalMcuctrlArgDefault;
    ctrlArgs.ui32_arg_hfxtal_user_mask  = 1 << (AM_HAL_HCXTAL_PDM_BASE_EN + USE_PDM_MODULE);
    am_hal_mcuctrl_control(AM_HAL_MCUCTRL_CONTROL_EXTCLK32M_KICK_START, &ctrlArgs);

    // enable HFRC2
    am_hal_clkgen_control(AM_HAL_CLKGEN_CONTROL_HFRC2_START, false);
    am_util_delay_us(200);      // wait for FLL to lock

    // set HF2ADJ for 24.576MHz output
    am_hal_clkgen_control(AM_HAL_CLKGEN_CONTROL_HF2ADJ_ENABLE, false);
    am_util_delay_us(500);      // wait for adj to apply
#endif // USE_PDM_CLK_SRC_HFXTAL_32MHZ, USE_PDM_CLK_SRC_HFRC2ADJ_24MHZ

    am_hal_pdm_configure(g_sVosBrd.pvPDMHandle, &sPdmConfig);

    //
    // Configure the necessary pins.
    //

#if USE_DMIC_MB0 || USE_DMIC_MB0_T5838
    am_hal_gpio_pinconfig(AM_BSP_GPIO_PDM0_CLK, g_AM_BSP_GPIO_PDM0_CLK);
    am_hal_gpio_pinconfig(AM_BSP_GPIO_PDM0_DATA, g_AM_BSP_GPIO_PDM0_DATA);
#elif USE_DMIC_MB1
    am_hal_gpio_pinconfig(AM_BSP_GPIO_PDM2_CLK, g_AM_BSP_GPIO_PDM2_CLK);
    am_hal_gpio_pinconfig(AM_BSP_GPIO_PDM2_DATA, g_AM_BSP_GPIO_PDM2_DATA);
#elif USE_DMIC_MB2
    am_hal_gpio_pinconfig(AM_BSP_GPIO_PDM1_CLK, g_AM_BSP_GPIO_PDM1_CLK);
    am_hal_gpio_pinconfig(AM_BSP_GPIO_PDM1_DATA, g_AM_BSP_GPIO_PDM1_DATA);
#endif

    //am_hal_pdm_fifo_flush(g_sVosBrd.pvPDMHandle);

    //
    // Configure and enable PDM interrupts (set up to trigger on DMA
    // completion).
    //
    am_hal_pdm_interrupt_enable(g_sVosBrd.pvPDMHandle, (AM_HAL_PDM_INT_DERR
                                            | AM_HAL_PDM_INT_DCMP
                                            | AM_HAL_PDM_INT_UNDFL
                                            | AM_HAL_PDM_INT_OVF));

    NVIC_EnableIRQ(g_ePdmInterrupts[USE_PDM_MODULE]);
    NVIC_SetPriority(g_ePdmInterrupts[USE_PDM_MODULE], NVIC_configKERNEL_INTERRUPT_PRIORITY);
}

void
am_vos_pdm_deinit(void)
{
    if(g_sVosBrd.pvPDMHandle == NULL)
      return;

    am_hal_pdm_interrupt_clear(g_sVosBrd.pvPDMHandle, (AM_HAL_PDM_INT_DERR
                                            | AM_HAL_PDM_INT_DCMP
                                            | AM_HAL_PDM_INT_UNDFL
                                            | AM_HAL_PDM_INT_OVF));

    am_hal_pdm_interrupt_disable(g_sVosBrd.pvPDMHandle, (AM_HAL_PDM_INT_DERR
                                            | AM_HAL_PDM_INT_DCMP
                                            | AM_HAL_PDM_INT_UNDFL
                                            | AM_HAL_PDM_INT_OVF));

    NVIC_DisableIRQ(g_ePdmInterrupts[USE_PDM_MODULE]);

#if USE_DMIC_MB0 || (USE_DMIC_MB0_T5838 && (T5838_AAD_A || T5838_AAD_D2))
    am_hal_gpio_pinconfig(AM_BSP_GPIO_PDM0_CLK, am_hal_gpio_pincfg_disabled);
    am_hal_gpio_pinconfig(AM_BSP_GPIO_PDM0_DATA, am_hal_gpio_pincfg_disabled);
#elif USE_DMIC_MB1
    am_hal_gpio_pinconfig(AM_BSP_GPIO_PDM2_CLK, am_hal_gpio_pincfg_disabled);
    am_hal_gpio_pinconfig(AM_BSP_GPIO_PDM2_DATA, am_hal_gpio_pincfg_disabled);
#elif USE_DMIC_MB2
    am_hal_gpio_pinconfig(AM_BSP_GPIO_PDM1_CLK, am_hal_gpio_pincfg_disabled);
    am_hal_gpio_pinconfig(AM_BSP_GPIO_PDM1_DATA, am_hal_gpio_pincfg_disabled);
#endif

    am_hal_pdm_disable(g_sVosBrd.pvPDMHandle);
    am_hal_pdm_power_control(g_sVosBrd.pvPDMHandle, AM_HAL_PDM_POWER_OFF, false);

    am_hal_pdm_deinitialize(g_sVosBrd.pvPDMHandle);

#if USE_DMIC_MB0 || (USE_DMIC_MB0_T5838 && (T5838_AAD_A || T5838_AAD_D2))
    am_hal_gpio_pinconfig(AM_BSP_GPIO_PDM0_CLK, am_hal_gpio_pincfg_output);
    am_hal_gpio_state_write(AM_BSP_GPIO_PDM0_CLK, AM_HAL_GPIO_OUTPUT_CLEAR);
    am_hal_gpio_pinconfig(AM_BSP_GPIO_PDM0_DATA, am_hal_gpio_pincfg_output);
    am_hal_gpio_state_write(AM_BSP_GPIO_PDM0_DATA, AM_HAL_GPIO_OUTPUT_CLEAR);
#elif USE_DMIC_MB1
    am_hal_gpio_pinconfig(AM_BSP_GPIO_PDM2_CLK, am_hal_gpio_pincfg_output);
    am_hal_gpio_state_write(AM_BSP_GPIO_PDM2_CLK, AM_HAL_GPIO_OUTPUT_CLEAR);
    am_hal_gpio_pinconfig(AM_BSP_GPIO_PDM2_DATA, am_hal_gpio_pincfg_output);
    am_hal_gpio_state_write(AM_BSP_GPIO_PDM2_DATA, AM_HAL_GPIO_OUTPUT_CLEAR);
#elif USE_DMIC_MB2
    am_hal_gpio_pinconfig(AM_BSP_GPIO_PDM1_CLK, am_hal_gpio_pincfg_output);
    am_hal_gpio_state_write(AM_BSP_GPIO_PDM1_CLK, AM_HAL_GPIO_OUTPUT_CLEAR);
    am_hal_gpio_pinconfig(AM_BSP_GPIO_PDM1_DATA, am_hal_gpio_pincfg_output);
    am_hal_gpio_state_write(AM_BSP_GPIO_PDM1_DATA, AM_HAL_GPIO_OUTPUT_CLEAR);
#endif

    g_sVosBrd.pvPDMHandle = NULL;
}
#endif // USE_DMIC_PDM

#if USE_AMIC_AUDADC
//*****************************************************************************
//
// AUDADC Sample buffer.
//
//*****************************************************************************
AM_ALLOC_AREA_DEFINE uint32_t g_ui32AUDADCSampleBuffer[PCM_FRAME_SIZE_SAMPLES * USE_MIC_NUM * 2];   // 2 times buffer size for ping/pong buffering.

#ifdef AM_PART_APOLLO4B
// AXI Scratch buffer
// Need to allocate 20 Words even though we only need 16, to ensure we have 16 Byte alignment
AM_SHARED_RW uint32_t g_ui32AxiScratchBuf[20];
#endif

//*****************************************************************************
//
// Configure the AUDADC.
//
//*****************************************************************************
#if AUDADC_TRIGGER_TIMER6
void
timer6_init(void)
{
  am_hal_timer_config_t sTimer6Config;
  
  am_hal_timer_default_config_set(&sTimer6Config);
  
  // Set up Timer6.
  sTimer6Config.eInputClock = AM_HAL_TIMER_CLOCK_XT;
  sTimer6Config.eFunction = AM_HAL_TIMER_FN_UPCOUNT;

  am_hal_timer_clear(6);
  am_hal_timer_config(6, &sTimer6Config);

  //
  // Set up timer 6 to AUDADC trigger enable.
  //
  TIMER->GLOBEN_b.AUDADCEN = TIMER_GLOBEN_AUDADCEN_EN;
  
  //
  // Set up timer 6 to 16 KHz.
  //
  am_hal_timer_compare0_set(6, 2); // 16 KHz
}
#endif

void
am_vos_audadc_config_dma(void)
{
    //
    // Configure the AUDADC to use DMA for the sample transfer.
    //
    g_sVosBrd.sAUDADCDMAConfig.bDynamicPriority = true;
    g_sVosBrd.sAUDADCDMAConfig.ePriority = AM_HAL_AUDADC_PRIOR_SERVICE_IMMED;
    g_sVosBrd.sAUDADCDMAConfig.bDMAEnable = true;
    g_sVosBrd.sAUDADCDMAConfig.ui32SampleCount = PCM_FRAME_SIZE_SAMPLES * USE_MIC_NUM;
    g_sVosBrd.sAUDADCDMAConfig.ui32TargetAddress = (uint32_t)(&g_ui32AUDADCSampleBuffer[0]);
    g_sVosBrd.sAUDADCDMAConfig.ui32TargetAddressReverse = (uint32_t)(&g_ui32AUDADCSampleBuffer[g_sVosBrd.sAUDADCDMAConfig.ui32SampleCount]);
    //g_sVosBrd.sAUDADCDMAConfig.ui32TargetAddressReverse = g_sVosBrd.sAUDADCDMAConfig.ui32TargetAddress + sizeof(uint32_t)* g_sVosBrd.sAUDADCDMAConfig.ui32SampleCount;

    if (AM_HAL_STATUS_SUCCESS != am_hal_audadc_configure_dma(g_sVosBrd.pvAUDADCHandle, &(g_sVosBrd.sAUDADCDMAConfig)))
    {
        am_util_stdio_printf("Error - configuring AUDADC DMA failed.\n");
    }

    //
    // Reset the AUDADC DMA flags.
    //
    g_sVosBrd.bAUDADCDMAError = false;
}

void
am_vos_audadc_config(void)
{
    //
    // Set up the AUDADC configuration parameters. These settings are reasonable
    // for accurate measurements at a low sample rate.
    //
    am_hal_audadc_config_t           AUDADCConfig =
    {
#if AUDADC_CLKSRC_HFRC
        .eClock             = AM_HAL_AUDADC_CLKSEL_HFRC_48MHz,
#elif AUDADC_CLKSRC_HFRC2_ADJ
        .eClock             = AM_HAL_AUDADC_CLKSEL_HFRC2_48MHz,
#elif AUDADC_CLKSRC_XTHS
        .eClock             = AM_HAL_AUDADC_CLKSEL_XTHS_24MHz,
#endif
        .ePolarity          = AM_HAL_AUDADC_TRIGPOL_RISING,
        .eTrigger           = AM_HAL_AUDADC_TRIGSEL_SOFTWARE,
#if AUDADC_LPMODE
        .eClockMode         = AM_HAL_AUDADC_CLKMODE_LOW_POWER,
        .ePowerMode         = AM_HAL_AUDADC_LPMODE1,
        .eSampMode          = AM_HAL_AUDADC_SAMPMODE_LP,
#else
        .eClockMode         = AM_HAL_AUDADC_CLKMODE_LOW_LATENCY,
        .ePowerMode         = AM_HAL_AUDADC_LPMODE0,
        .eSampMode          = AUDADC_CFG_SAMPMODE_MED,
#endif
        .eRepeat            = AM_HAL_AUDADC_REPEATING_SCAN,

#if AUDADC_TRIGGER_TIMER6
        .eRepeatTrigger     = AM_HAL_AUDADC_RPTTRIGSEL_TMR,
#else
        .eRepeatTrigger     = AM_HAL_AUDADC_RPTTRIGSEL_INT,
#endif
    };

#if (!AUDADC_TRIGGER_TIMER6)
    //
    // Set up internal repeat trigger timer
    //
    am_hal_audadc_irtt_config_t      AUDADCIrttConfig =
    {
        .bIrttEnable        = true,
        .eClkDiv            = AM_HAL_AUDADC_RPTT_CLK_DIV32,

#if AUDADC_CLKSRC_XTHS
        .ui32IrttCountMax   = 62,  //sample rate = eClock/eClkDiv/(ui32IrttCountMax+1)
#else
        .ui32IrttCountMax   = 93,  //sample rate = eClock/eClkDiv/(ui32IrttCountMax+1)
#endif // AUDADC_CLKSRC_XTHS
    };
#endif // (!AUDADC_TRIGGER_TIMER6)
    
    //
    // Initialize the AUDADC and get the handle.
    //
    if ( AM_HAL_STATUS_SUCCESS != am_hal_audadc_initialize(0, &g_sVosBrd.pvAUDADCHandle) )
    {
        am_util_stdio_printf("Error - reservation of the AUDADC instance failed.\n");
    }

    //
    // Power on the AUDADC.
    //
    if (AM_HAL_STATUS_SUCCESS != am_hal_audadc_power_control(g_sVosBrd.pvAUDADCHandle,
                                                          AM_HAL_SYSCTRL_WAKE,
                                                          false) )
    {
        am_util_stdio_printf("Error - AUDADC power on failed.\n");
    }

#if AUDADC_CLKSRC_HFRC2_ADJ
    //Enable hfrc2.
    am_hal_clkgen_control(AM_HAL_CLKGEN_CONTROL_HFRC2_START, false);
    am_util_delay_us(200);

    am_hal_mcuctrl_control_arg_t ctrlArgs = g_amHalMcuctrlArgDefault;
    ctrlArgs.b_arg_enable_HfXtalClockout  = 1 << AM_HAL_HFXTAL_AUADC_EN;

    // hfrc2 adj.
    am_hal_mcuctrl_control(AM_HAL_MCUCTRL_CONTROL_EXTCLK32M_KICK_START, &ctrlArgs);
    am_util_delay_us(1500);

    // set HF2ADJ for 24.576MHz output
    am_hal_clkgen_control(AM_HAL_CLKGEN_CONTROL_HF2ADJ_ENABLE, false);
    am_util_delay_us(500);      // wait for adj to apply
#elif AUDADC_CLKSRC_XTHS
    am_hal_mcuctrl_control_arg_t ctrlArgs = g_amHalMcuctrlArgDefault;
    ctrlArgs.b_arg_enable_HfXtalClockout  = 1 << AM_HAL_HFXTAL_AUADC_EN;

    // hfrc2 adj.
    am_hal_mcuctrl_control(AM_HAL_MCUCTRL_CONTROL_EXTCLK32M_NORMAL, &ctrlArgs);
    am_util_delay_us(1500);
#endif // AUDADC_CLKSRC_XTHS

    if (AM_HAL_STATUS_SUCCESS != am_hal_audadc_configure(g_sVosBrd.pvAUDADCHandle, &AUDADCConfig))
    {
        am_util_stdio_printf("Error - configuring AUDADC failed.\n");
    }

#if AUDADC_TRIGGER_TIMER6
    timer6_init();
#else
    //
    // Set up internal repeat trigger timer
    //
    am_hal_audadc_configure_irtt(g_sVosBrd.pvAUDADCHandle, &AUDADCIrttConfig);
#endif

    //
    // Enable the AUDADC.
    //
    if (AM_HAL_STATUS_SUCCESS != am_hal_audadc_enable(g_sVosBrd.pvAUDADCHandle))
    {
        am_util_stdio_printf("Error - enabling AUDADC failed.\n");
    }

#if AUDADC_TRIGGER_TIMER6
    am_hal_timer_start(6);
#else
    //
    // Enable internal repeat trigger timer
    //
    am_hal_audadc_irtt_enable(g_sVosBrd.pvAUDADCHandle);
#endif

    //
    // Configure the AUDADC to use DMA for the sample transfer.
    //
    am_vos_audadc_config_dma();

    //
    // For this example, the samples will be coming in slowly. This means we
    // can afford to wake up for every conversion.
    //
#if AUDADC_DCMP_EN
    am_hal_audadc_interrupt_enable(g_sVosBrd.pvAUDADCHandle, AM_HAL_AUDADC_INT_DERR | AM_HAL_AUDADC_INT_DCMP );
#else
    am_hal_audadc_interrupt_enable(g_sVosBrd.pvAUDADCHandle, AM_HAL_AUDADC_INT_FIFOOVR1 | AM_HAL_AUDADC_INT_DERR | AM_HAL_AUDADC_INT_DCMP ); // AM_HAL_AUDADC_INT_FIFOOVR2
#endif
}

//*****************************************************************************
//
// Configure the AUDADC SLOT.
//
//*****************************************************************************
void
am_vos_audadc_slot_config(void)
{
    am_hal_audadc_slot_config_t      AUDADCSlotConfig;

    //
    // Set up an AUDADC slot
    //
    AUDADCSlotConfig.eMeasToAvg      = AM_HAL_AUDADC_SLOT_AVG_1;
    AUDADCSlotConfig.ePrecisionMode  = AM_HAL_AUDADC_SLOT_12BIT;
#if AUDADC_CLKSRC_XTHS
    AUDADCSlotConfig.ui32TrkCyc      = 24;
#else
    AUDADCSlotConfig.ui32TrkCyc      = 34;
#endif
    AUDADCSlotConfig.eChannel        = AM_HAL_AUDADC_SLOT_CHSEL_SE0;
    AUDADCSlotConfig.bWindowCompare  = false;
    AUDADCSlotConfig.bEnabled        = true;

    if (AM_HAL_STATUS_SUCCESS != am_hal_audadc_configure_slot(g_sVosBrd.pvAUDADCHandle, 0, &AUDADCSlotConfig))
    {
        am_util_stdio_printf("Error - configuring AUDADC Slot 0 failed.\n");
    }

    AUDADCSlotConfig.eChannel        = AM_HAL_AUDADC_SLOT_CHSEL_SE1;
    if (AM_HAL_STATUS_SUCCESS != am_hal_audadc_configure_slot(g_sVosBrd.pvAUDADCHandle, 1, &AUDADCSlotConfig))
    {
        am_util_stdio_printf("Error - configuring AUDADC Slot 1 failed.\n");
    }

#if USE_MIC_DUAL
    AUDADCSlotConfig.eChannel        = AM_HAL_AUDADC_SLOT_CHSEL_SE2;
    if (AM_HAL_STATUS_SUCCESS != am_hal_audadc_configure_slot(g_sVosBrd.pvAUDADCHandle, 2, &AUDADCSlotConfig))
    {
        am_util_stdio_printf("Error - configuring AUDADC Slot 2 failed.\n");
    }

    AUDADCSlotConfig.eChannel        = AM_HAL_AUDADC_SLOT_CHSEL_SE3;
    if (AM_HAL_STATUS_SUCCESS != am_hal_audadc_configure_slot(g_sVosBrd.pvAUDADCHandle, 3, &AUDADCSlotConfig))
    {
        am_util_stdio_printf("Error - configuring AUDADC Slot 3 failed.\n");
    }
#endif // USE_MIC_DUAL
}

void
am_vos_audadc_pga_init(void)
{
#if defined(AM_PART_APOLLO4B)
    am_hal_daxi_control(AM_HAL_DAXI_CONTROL_AXIMEM, (uint8_t *)((uint32_t)(g_ui32AxiScratchBuf + 3) & ~0xF));
#elif defined(AM_PART_APOLLO4P) || defined(AM_PART_APOLLO4L)
    //am_hal_daxi_control(AM_HAL_DAXI_CONTROL_ENABLE, NULL);
#endif

    //
    // Power up PrePGA
    //
    am_hal_audadc_refgen_powerup();

    am_hal_audadc_pga_powerup(0);
    am_hal_audadc_pga_powerup(1);
#if USE_MIC_DUAL
    am_hal_audadc_pga_powerup(2);
    am_hal_audadc_pga_powerup(3);
#endif // USE_MIC_DUAL

    am_hal_audadc_gain_set(0, 2 * PREAMP_FULL_GAIN);
    am_hal_audadc_gain_set(1, 2 * PREAMP_FULL_GAIN);
#if USE_MIC_DUAL
    am_hal_audadc_gain_set(2, 2 * PREAMP_FULL_GAIN);
    am_hal_audadc_gain_set(3, 2 * PREAMP_FULL_GAIN);
#endif // USE_MIC_DUAL

    //
    //  Turn on mic bias
    //
    am_hal_audadc_micbias_powerup(24);
    am_util_delay_ms(400);
}

void
am_vos_audadc_init(void)
{
    am_vos_audadc_pga_init();

    //
    // Configure the ADC
    //
    am_vos_audadc_config();

#if configUSE_SPP_P2A
    g_sVosBrd.fPGAdB = CH_A0_GAIN_DB;
#endif

    // Gain setting
    g_sVosBrd.sAudadcGainConfig.ui32LGA = (uint32_t)((float)CH_A0_GAIN_DB * 2 + 12); // LG 12dB, LGA = 36
    g_sVosBrd.sAudadcGainConfig.ui32HGADELTA = (uint32_t)((float)(CH_A1_GAIN_DB * 2 + 12)) - g_sVosBrd.sAudadcGainConfig.ui32LGA; // HGDelta = 12
#if USE_MIC_DUAL
    g_sVosBrd.sAudadcGainConfig.ui32LGB = (uint32_t)((float)CH_B0_GAIN_DB*2 + 12);
    g_sVosBrd.sAudadcGainConfig.ui32HGBDELTA = (uint32_t)((float)(CH_B1_GAIN_DB*2 + 12)) - g_sVosBrd.sAudadcGainConfig.ui32LGB;
#endif // USE_MIC_DUAL

    g_sVosBrd.sAudadcGainConfig.eUpdateMode = AM_HAL_AUDADC_GAIN_UPDATE_IMME;
    am_hal_audadc_internal_pga_config(g_sVosBrd.pvAUDADCHandle, &g_sVosBrd.sAudadcGainConfig);

    // Configure the slot
    am_vos_audadc_slot_config();

#if defined(AM_PART_APOLLO4P) && AUDADC_DC_OFFSET_CAL
    //
    // Calculate DC offset calibartion parameter.
    //
    if (AM_HAL_STATUS_SUCCESS != am_hal_audadc_slot_dc_offset_calculate(g_sVosBrd.pvAUDADCHandle, 4, &g_sVosBrd.sOffsetCalib))
    {
        am_util_stdio_printf("Error - failed to calculate offset calibartion parameter.\n");
    }
#endif

    NVIC_SetPriority(AUDADC0_IRQn, AM_IRQ_PRIORITY_DEFAULT);
    NVIC_EnableIRQ(AUDADC0_IRQn);
    am_hal_interrupt_master_enable();
}

void
am_vos_audadc_deinit(void)
{
    if(g_sVosBrd.pvAUDADCHandle == NULL)
      return;

#if AUDADC_TRIGGER_TIMER6
    am_hal_timer_stop(6);
#else
    //
    // Enable internal repeat trigger timer
    //
    am_hal_audadc_irtt_disable(g_sVosBrd.pvAUDADCHandle);
#endif

#if AUDADC_DCMP_EN
    am_hal_audadc_interrupt_clear(g_sVosBrd.pvAUDADCHandle, (AM_HAL_AUDADC_INT_DERR
                                            | AM_HAL_AUDADC_INT_DCMP));
    am_hal_audadc_interrupt_disable(g_sVosBrd.pvAUDADCHandle, (AM_HAL_AUDADC_INT_DERR
                                            | AM_HAL_AUDADC_INT_DCMP));
#else
    am_hal_audadc_interrupt_clear(g_sVosBrd.pvAUDADCHandle, (AM_HAL_AUDADC_INT_DERR
                                            | AM_HAL_AUDADC_INT_DCMP | AM_HAL_AUDADC_INT_FIFOOVR1));
    am_hal_audadc_interrupt_disable(g_sVosBrd.pvAUDADCHandle, (AM_HAL_AUDADC_INT_DERR
                                            | AM_HAL_AUDADC_INT_DCMP | AM_HAL_AUDADC_INT_FIFOOVR1));
#endif

    NVIC_DisableIRQ(AUDADC0_IRQn);

    am_hal_audadc_disable(g_sVosBrd.pvAUDADCHandle);
    am_hal_audadc_power_control(g_sVosBrd.pvAUDADCHandle, AM_HAL_SYSCTRL_DEEPSLEEP, false);

    am_hal_audadc_deinitialize(g_sVosBrd.pvAUDADCHandle);
    g_sVosBrd.pvAUDADCHandle = NULL;
}
#endif // USE_AMIC_AUDADC

void
am_vos_mic_enable(void)
{
    AM_VOS_LOG_INFO("[AM-VoS] am_vos_mic_enable()\n");
#if USE_DMIC_PDM
    am_hal_pdm_interrupt_enable(g_sVosBrd.pvPDMHandle, (AM_HAL_PDM_INT_DERR
                                            | AM_HAL_PDM_INT_DCMP
                                            | AM_HAL_PDM_INT_UNDFL
                                            | AM_HAL_PDM_INT_OVF));

    if(g_sVosBrd.pvPDMHandle)
    {
        am_vos_pdm_trigger_dma();
        am_hal_pdm_enable(g_sVosBrd.pvPDMHandle);
    }
    else
        AM_VOS_LOG_WARNING("Failed to PDM enable : PDM handle is NULL\n");
#endif // USE_DMIC_PDM

#if USE_AMIC_AUDADC
    //
    // Enable the ADC.
    //
    if (AM_HAL_STATUS_SUCCESS != am_hal_audadc_enable(g_sVosBrd.pvAUDADCHandle))
    {
        am_util_stdio_printf("Error - enabling AUDADC failed.\n");
    }

    //
    // Trigger the ADC sampling for the first time manually.
    //
    if (AM_HAL_STATUS_SUCCESS != am_hal_audadc_sw_trigger(g_sVosBrd.pvAUDADCHandle))
    {
        am_util_stdio_printf("Error - triggering the AUDADC failed.\n");
    }
#endif // USE_AMIC_AUDADC
}

void
am_vos_mic_disable(void)
{
    AM_VOS_LOG_INFO("[AM-VoS] am_vos_mic_disable()\n");
#if USE_DMIC_PDM
    if(g_sVosBrd.pvPDMHandle == NULL)
    {
        AM_VOS_LOG_WARNING("Failed to PDM disable : PDM handle is NULL\n");
        return;
    }

    am_hal_pdm_disable(g_sVosBrd.pvPDMHandle);
#endif // USE_DMIC_PDM
    
#if USE_AMIC_AUDADC
    if(g_sVosBrd.pvAUDADCHandle == NULL)
    {
        AM_VOS_LOG_WARNING("Failed to AUDADC disable : AUDADC handle is NULL\n");
        return;
    } 

    //
    // Disable the AUDADC.
    //
    if (AM_HAL_STATUS_SUCCESS != am_hal_audadc_disable(g_sVosBrd.pvAUDADCHandle))
    {
        am_util_stdio_printf("Error - disabling ADC failed.\n");
    }
#endif // USE_AMIC_AUDADC
}

void
am_vos_aad_mic_enable(void)
{
#if USE_DMIC_PDM
    am_vos_pdm_init();

    if(g_sVosBrd.pvPDMHandle)
    {
        am_hal_pdm_enable(g_sVosBrd.pvPDMHandle);
        am_vos_pdm_trigger_dma();
    }
    else
        AM_VOS_LOG_WARNING("Failed to PDM enable : PDM handle is NULL\n");
#endif // USE_DMIC_PDM

#if USE_AMIC_AUDADC
    am_vos_audadc_init();
#endif // USE_AMIC_AUDADC

#if configUSE_AAD
    am_device_t5838_aad_disable();
#endif // configUSE_AAD
}

void
am_vos_aad_mic_disable(void)
{
#if USE_DMIC_PDM
    am_vos_pdm_deinit();
#endif // USE_DMIC_PDM

#if USE_AMIC_AUDADC
    am_vos_audadc_deinit();
#endif // USE_AMIC_AUDADC

#if configUSE_AAD
    am_device_t5838_aad_enable();
#endif // configUSE_AAD
}

void
am_vos_burst_mode_setup(void)
{
#if defined (AM_PART_APOLLO4B) || defined(AM_PART_APOLLO4P) ||  defined(AM_PART_APOLLO4L)
#if configUSE_BURST_MODE_ALWAYS_ON || configUSE_BLE_BURST_MODE
    uint32_t ui32Result = 0;
    if(g_sVosBrd.bBurstMode == true) {
        //AM_VOS_LOG_DEBUG("BST NOP\n");
        return;
    }

    ui32Result = am_hal_pwrctrl_mcu_mode_select(AM_HAL_PWRCTRL_MCU_MODE_HIGH_PERFORMANCE);
    if (ui32Result == AM_HAL_STATUS_SUCCESS)
    {
        g_sVosBrd.bBurstMode = true;
        AM_VOS_LOG_WARNING("\n[AM-VoS] Burst Mode (192MHz)\n\n");
    }
    else
    {
        AM_VOS_LOG_WARNING("Failed to Enable Burst Mode operation %d\n", ui32Result);
    }
#else
    uint32_t ui32Result = 0;
    if(g_sVosBrd.bBurstMode == false) {
        //AM_VOS_LOG_DEBUG("NOR NOP\n");
        return;
    }

    ui32Result = am_hal_pwrctrl_mcu_mode_select(AM_HAL_PWRCTRL_MCU_MODE_LOW_POWER);
    // Make sure we are in "Normal" mode.
    if (ui32Result == AM_HAL_STATUS_SUCCESS)
    {
        g_sVosBrd.bBurstMode = false;
        AM_VOS_LOG_WARNING("\n[AM-VoS] Normal Mode (86MHz)\n\n");
    }
    else
    {
        AM_VOS_LOG_WARNING("Failed to Disable Burst Mode operation %d\n", ui32Result);
    }
#endif
    return;
#endif
}

// burst to 96MHz (only supported with Apollo 3)
void am_vos_burst_mode_enable(void)
{
#if defined (AM_PART_APOLLO4B) || defined(AM_PART_APOLLO4P) ||  defined(AM_PART_APOLLO4L)
//#if configUSE_BURST_MODE_ALWAYS_ON || configUSE_BLE_BURST_MODE || configUSE_AIC_LITE_LAYOUT
#if configUSE_BURST_MODE_ALWAYS_ON || configUSE_BLE_BURST_MODE
    uint32_t ui32Result = 0;
    if(g_sVosBrd.bBurstMode == true) {
        //AM_VOS_LOG_DEBUG("BST NOP\n");
        return;
    }

    ui32Result = am_hal_pwrctrl_mcu_mode_select(AM_HAL_PWRCTRL_MCU_MODE_HIGH_PERFORMANCE);
    if (ui32Result == AM_HAL_STATUS_SUCCESS)
    {
        g_sVosBrd.bBurstMode = true;
        AM_VOS_LOG_DEBUG("\n[AM-VoS] Burst Mode (192MHz)\n\n");
    }
    else
    {
        AM_VOS_LOG_WARNING("Failed to Enable Burst Mode operation %d\n", ui32Result);
    }
#endif // configUSE_BURST_MODE_ALWAYS_ON || configUSE_BLE_BURST_MODE
#endif // AM_PART_APOLLO4B, AM_PART_APOLLO4P, AM_PART_APOLLO4L
}

void am_vos_burst_mode_disable(void)
{
#if defined (AM_PART_APOLLO4B) || defined(AM_PART_APOLLO4P) || defined(AM_PART_APOLLO4L)
//#if configUSE_BLE_BURST_MODE || configUSE_AIC_LITE_LAYOUT
#if configUSE_BLE_BURST_MODE
    uint32_t ui32Result = 0;
    if(g_sVosBrd.bBurstMode == false) {
        //AM_VOS_LOG_DEBUG("NOR NOP\n");
        return;
    }

    ui32Result = am_hal_pwrctrl_mcu_mode_select(AM_HAL_PWRCTRL_MCU_MODE_LOW_POWER);
    // Make sure we are in "Normal" mode.
    if (ui32Result == AM_HAL_STATUS_SUCCESS)
    {
        g_sVosBrd.bBurstMode = false;
        AM_VOS_LOG_DEBUG("\n[AM-VoS] Normal Mode (96MHz)\n\n");
    }
    else
    {
        AM_VOS_LOG_WARNING("Failed to Disable Burst Mode operation %d\n", ui32Result);
    }
#endif // configUSE_BLE_BURST_MODE
#endif // AM_PART_APOLLO4B, AM_PART_APOLLO4P, AM_PART_APOLLO4L
}

//-----------------------------------------------------------------------------
// METHOD:  ApolloBoardInitialization
// PURPOSE: Setup board peripherals
//-----------------------------------------------------------------------------
void
am_vos_board_init(void)
{
    am_vos_gpio_init();

#if configUSE_AAD && (T5838_AAD_A || T5838_AAD_D2)
    am_device_t5838_aad_init();
#endif // configUSE_AAD && (T5838_AAD_A || T5838_AAD_D2)
    
#if USE_AMIC_AUDADC
    am_vos_audadc_init();
#endif // USE_AMIC_AUDADC

#if USE_DMIC_PDM
    am_vos_pdm_init();
#endif // USE_DMIC_PDM

#if configUSE_LEDs
    am_vos_logic_led_swirl(1);
#endif // configUSE_LEDs

    //
    // Enable debug printf messages using ITM on SWO pin
    //
#if configUSE_PRINTF_SWO
    //init SWO print here
    am_bsp_itm_printf_enable();
#endif // configUSE_PRINTF_SWO

    am_vos_print_system_info();

    am_vos_burst_mode_setup();

#if configUSE_LOG_UART0 || configUSE_PRINTF_UART0
    am_bsp_uart_printf_enable();
#endif // configUSE_LOG_UART0 || configUSE_PRINTF_UART0

#if configUSE_LOG_UART1
    am_vos_uart_init(UART1_MODULE);
    am_vos_uart_enable(UART1_MODULE);
#endif // configUSE_LOG_UART1

#if VOS_MEASUREMENT_ENABLE
    am_vos_timer_benchmark_init();
#endif // VOS_MEASUREMENT_ENABLE

    // master interrupt enable
    am_hal_interrupt_master_enable();

#if configUSE_BLE
    am_vos_ble_task_setup();
#endif // configUSE_BLE
}   // End BoardInit

void
am_vos_mic_fifo_flush(void)
{
#if USE_DMIC_PDM
    if(g_sVosBrd.pvPDMHandle)
        am_hal_pdm_fifo_flush(g_sVosBrd.pvPDMHandle);
#endif // USE_DMIC_PDM
}

#if configUSE_LOG_UART0 || configUSE_PRINTF_UART0
void
am_vos_uart_process(void)
{
    am_vos_task_queue_element_t QueueElement;
    char transmit_buff[UART_TRANSMIT_BUFFER];
    int32_t transmit_length = 0;
    uint32_t index = 0;

    am_vos_task_read(AM_VOS_TASK_UART0, &QueueElement);

    switch(QueueElement.ui32MessageType)
    {
        case AM_VOS_MESSAGE_STR:
            transmit_length = (int32_t)strlen(&(g_sAmUtil.pcStdioBuff[QueueElement.info.ui32Indx]));
            for(index=0; index < transmit_length; index++)
            {
                transmit_buff[index] = g_sAmUtil.pcStdioBuff[QueueElement.info.ui32Indx + index];
            }
            transmit_buff[index] = 0;
            am_bsp_uart_string_print(transmit_buff);
            break;

        default:
            break;
    }
}
#endif // configUSE_LOG_UART0 || configUSE_PRINTF_UART0

//*****************************************************************************
//
// Simple button detection function with debounce
//
//*****************************************************************************
bool
am_vos_button_gpio_check(uint32_t gpio)
{
    uint32_t pinVal;

    //
    // Debounce for 500 ms.
    // We're triggered for falling edge - so we expect a consistent LOW here
    //
    for(uint8_t i = 0; i < 5; i++)
    {
        pinVal = 0;
        am_util_delay_ms(100);
        am_hal_gpio_state_read(gpio, AM_HAL_GPIO_INPUT_READ, &pinVal);

        if(pinVal == 1)
        {
            return true;
        }
    }

    return false;
}

#if configUSE_MUTE_MIC
void
am_vos_mute_mic_toggle(void)
{
    // disable interrupt on this pin
    AM_CRITICAL_BEGIN_VOS;
    // button pressed, process to mute or unmute the mic
    if(g_sVosBrd.bMicStatus == true)
    {
        // mics are on, mute them
        //
        // Notes: if the system has push-to-talk, we should keep the
        // functionality of the system, e.g. ble connection
        // Otherwise, we should be able to shut down everything and go to deep sleep
        //
#if USE_DMIC_PDM
        am_vos_pdm_deinit();
#endif // USE_DMIC_PDM
#if USE_AMIC_AUDADC
        am_vos_mic_disable();
#endif // USE_AMIC_AUDADC
        am_vos_reset_detected_flag();
        am_vos_audio_flush_ring_buffer();

        g_sVosBrd.bMicStatus = false;

        AM_VOS_LOG_WARNING("== MICs are muted! ==\n");
    }
    else
    {
        // mics are off, unmute them
#if USE_DMIC_PDM
        am_vos_pdm_init();
        if(g_sVosBrd.pvPDMHandle)
            am_hal_pdm_enable(g_sVosBrd.pvPDMHandle);
        am_vos_pdm_trigger_dma();
#endif // USE_DMIC_PDM
#if USE_AMIC_AUDADC
        am_vos_mic_enable();
#endif // USE_AMIC_AUDADC
        am_vos_reset_detected_flag();
        am_vos_audio_flush_ring_buffer();

        g_sVosBrd.bMicStatus = true;
        AM_VOS_LOG_WARNING("== MICs are unmuted! ==\n");
    }
    AM_CRITICAL_END_VOS;
}
#endif // configUSE_MUTE_MIC

