//*****************************************************************************
//
//! @file am_hal_pdm.h
//!
//! @brief API for the PDM module
//!
//! @addtogroup
//! @ingroup
//! @{
//
//*****************************************************************************

//*****************************************************************************
//
// Copyright (c) 2021, 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 release_sdk_3_0_0-742e5ac27c of the AmbiqSuite Development Package.
//
//*****************************************************************************

#ifndef AM_HAL_PDM_H
#define AM_HAL_PDM_H

#ifdef __cplusplus
extern "C"
{
#endif

//*****************************************************************************
//
// CMSIS-style macro for handling a variable IOS module number.
//
//*****************************************************************************
#define AM_REG_PDM_NUM_MODULES                       1
#define PDMn(n) ((PDM_Type*)(PDM_BASE + (n * (PDM_BASE - PDM_BASE))))

//*****************************************************************************
//
// DMA threshold minimum.
//
// The PDM DMA works best if its threshold value is set to a multiple of 4
// between 16 and 24, but it will technically allow threshold settings between
// 4 and 24. This macro sets the minimum threshold value that the HAL layer
// will allow.
//
//*****************************************************************************
#define AM_HAL_PDM_DMA_THRESHOLD_MIN        16

//*****************************************************************************
//
// PDM-specific error conditions.
//
//*****************************************************************************
typedef enum
{
    //
    // The PDM HAL will throw this error if it can't find a threshold value to
    // match the total-count value passed in by a caller requesting a DMA
    // transfer. The PDM hardware requires all DMA transactions to be evenly
    // divisible in chunks of one FIFO size or smaller. Try changing your
    // ui32TotalCount value to a more evenly divisible number.
    //
    AM_HAL_PDM_STATUS_BAD_TOTALCOUNT = AM_HAL_STATUS_MODULE_SPECIFIC_START,
}
am_hal_pdm_status_e;

//*****************************************************************************
//
// Gain settings.
//
//*****************************************************************************
typedef enum
{
    AM_HAL_PDM_GAIN_P405DB = PDM_PCFG_PGALEFT_P405DB,
    AM_HAL_PDM_GAIN_P390DB = PDM_PCFG_PGALEFT_P390DB,
    AM_HAL_PDM_GAIN_P375DB = PDM_PCFG_PGALEFT_P375DB,
    AM_HAL_PDM_GAIN_P360DB = PDM_PCFG_PGALEFT_P360DB,
    AM_HAL_PDM_GAIN_P345DB = PDM_PCFG_PGALEFT_P345DB,
    AM_HAL_PDM_GAIN_P330DB = PDM_PCFG_PGALEFT_P330DB,
    AM_HAL_PDM_GAIN_P315DB = PDM_PCFG_PGALEFT_P315DB,
    AM_HAL_PDM_GAIN_P300DB = PDM_PCFG_PGALEFT_P300DB,
    AM_HAL_PDM_GAIN_P285DB = PDM_PCFG_PGALEFT_P285DB,
    AM_HAL_PDM_GAIN_P270DB = PDM_PCFG_PGALEFT_P270DB,
    AM_HAL_PDM_GAIN_P255DB = PDM_PCFG_PGALEFT_P255DB,
    AM_HAL_PDM_GAIN_P240DB = PDM_PCFG_PGALEFT_P240DB,
    AM_HAL_PDM_GAIN_P225DB = PDM_PCFG_PGALEFT_P225DB,
    AM_HAL_PDM_GAIN_P210DB = PDM_PCFG_PGALEFT_P210DB,
    AM_HAL_PDM_GAIN_P195DB = PDM_PCFG_PGALEFT_P195DB,
    AM_HAL_PDM_GAIN_P180DB = PDM_PCFG_PGALEFT_P180DB,
    AM_HAL_PDM_GAIN_P165DB = PDM_PCFG_PGALEFT_P165DB,
    AM_HAL_PDM_GAIN_P150DB = PDM_PCFG_PGALEFT_P150DB,
    AM_HAL_PDM_GAIN_P135DB = PDM_PCFG_PGALEFT_P135DB,
    AM_HAL_PDM_GAIN_P120DB = PDM_PCFG_PGALEFT_P120DB,
    AM_HAL_PDM_GAIN_P105DB = PDM_PCFG_PGALEFT_P105DB,
    AM_HAL_PDM_GAIN_P90DB  = PDM_PCFG_PGALEFT_P90DB,
    AM_HAL_PDM_GAIN_P75DB  = PDM_PCFG_PGALEFT_P75DB,
    AM_HAL_PDM_GAIN_P60DB  = PDM_PCFG_PGALEFT_P60DB,
    AM_HAL_PDM_GAIN_P45DB  = PDM_PCFG_PGALEFT_P45DB,
    AM_HAL_PDM_GAIN_P30DB  = PDM_PCFG_PGALEFT_P30DB,
    AM_HAL_PDM_GAIN_P15DB  = PDM_PCFG_PGALEFT_P15DB,
    AM_HAL_PDM_GAIN_0DB    = PDM_PCFG_PGALEFT_0DB,
    AM_HAL_PDM_GAIN_M15DB  = PDM_PCFG_PGALEFT_M15DB,
    AM_HAL_PDM_GAIN_M300DB = PDM_PCFG_PGALEFT_M300DB,
    AM_HAL_PDM_GAIN_M45DB  = PDM_PCFG_PGALEFT_M45DB,
    AM_HAL_PDM_GAIN_M60DB  = PDM_PCFG_PGALEFT_M60DB,
}
am_hal_pdm_gain_e;

//*****************************************************************************
//
// Clock Source selection.
//
//*****************************************************************************
typedef enum
{
    AM_HAL_PDM_INTERNAL_CLK = PDM_VCFG_SELAP_INTERNAL,
    AM_HAL_PDM_I2S_CLK      = PDM_VCFG_SELAP_I2S,
}
am_hal_pdm_clksrc_e;

//*****************************************************************************
//
// PDM internal clock speed selection.
//
//*****************************************************************************
typedef enum
{
    AM_HAL_PDM_CLK_DISABLE = PDM_VCFG_PDMCLKSEL_DISABLE,
    AM_HAL_PDM_CLK_12MHZ   = PDM_VCFG_PDMCLKSEL_12MHz,
    AM_HAL_PDM_CLK_6MHZ    = PDM_VCFG_PDMCLKSEL_6MHz,
    AM_HAL_PDM_CLK_3MHZ    = PDM_VCFG_PDMCLKSEL_3MHz,
    AM_HAL_PDM_CLK_1_5MHZ  = PDM_VCFG_PDMCLKSEL_1_5MHz,
    AM_HAL_PDM_CLK_750KHZ  = PDM_VCFG_PDMCLKSEL_750KHz,
    AM_HAL_PDM_CLK_375KHZ  = PDM_VCFG_PDMCLKSEL_375KHz,
    AM_HAL_PDM_CLK_187KHZ  = PDM_VCFG_PDMCLKSEL_187KHz,
}
am_hal_pdm_clkspd_e;

//*****************************************************************************
//
// PDM clock divider setting.
//
//*****************************************************************************
typedef enum
{
    AM_HAL_PDM_MCLKDIV_4 = PDM_PCFG_MCLKDIV_MCKDIV4,
    AM_HAL_PDM_MCLKDIV_3 = PDM_PCFG_MCLKDIV_MCKDIV3,
    AM_HAL_PDM_MCLKDIV_2 = PDM_PCFG_MCLKDIV_MCKDIV2,
    AM_HAL_PDM_MCLKDIV_1 = PDM_PCFG_MCLKDIV_MCKDIV1,
}
am_hal_pdm_mclkdiv_e;

//*****************************************************************************
//
// PCM Channel Select.
//
//*****************************************************************************
typedef enum
{
    AM_HAL_PDM_CHANNEL_LEFT   = PDM_VCFG_CHSET_LEFT,
    AM_HAL_PDM_CHANNEL_RIGHT  = PDM_VCFG_CHSET_RIGHT,
    AM_HAL_PDM_CHANNEL_STEREO = PDM_VCFG_CHSET_STEREO,
}
am_hal_pdm_chset_e;

//*****************************************************************************
//
// PDM power state settings.
//
//*****************************************************************************
#define AM_HAL_PDM_POWER_ON           AM_HAL_SYSCTRL_WAKE
#define AM_HAL_PDM_POWER_OFF          AM_HAL_SYSCTRL_NORMALSLEEP

//*****************************************************************************
//
// PDM interrupts.
//
//*****************************************************************************
#define AM_HAL_PDM_INT_DERR           PDM_INTSTAT_DERR_Msk
#define AM_HAL_PDM_INT_DCMP           PDM_INTSTAT_DCMP_Msk
#define AM_HAL_PDM_INT_UNDFL          PDM_INTSTAT_UNDFL_Msk
#define AM_HAL_PDM_INT_OVF            PDM_INTSTAT_OVF_Msk
#define AM_HAL_PDM_INT_THR            PDM_INTSTAT_THR_Msk

//*****************************************************************************
//
// Configuration structure for the PDM
//
//*****************************************************************************
typedef struct
{
    // Clock
    am_hal_pdm_mclkdiv_e eClkDivider;

    // Gain
    am_hal_pdm_gain_e eLeftGain;
    am_hal_pdm_gain_e eRightGain;

    // Decimation Rate
    uint32_t ui32DecimationRate;

    // Filters
    bool bHighPassEnable;
    uint32_t ui32HighPassCutoff;

    // PDMCLKSEL
    am_hal_pdm_clkspd_e ePDMClkSpeed;

    // BCLKINV
    bool bInvertI2SBCLK;

    // SELAP
    am_hal_pdm_clksrc_e ePDMClkSource;

    // DMICKDEL
    bool bPDMSampleDelay;

    // PCMPACK
    bool bDataPacking;

    // CHSET
    am_hal_pdm_chset_e ePCMChannels;

    uint32_t ui32GainChangeDelay;

    bool bI2SEnable;

    bool bSoftMute;

    bool bLRSwap;
}
am_hal_pdm_config_t;

//*****************************************************************************
//
// DMA transfer structure
//
//*****************************************************************************
typedef struct
{
    uint32_t ui32TargetAddr;
    uint32_t ui32TotalCount;
}
am_hal_pdm_transfer_t;

// Init/De-init.
extern uint32_t am_hal_pdm_initialize(uint32_t ui32Module, void **ppHandle);
extern uint32_t am_hal_pdm_deinitialize(void *pHandle);

// Power
extern uint32_t am_hal_pdm_power_control(void *pHandle, am_hal_sysctrl_power_state_e ePowerState, bool bRetainState);

// Config
extern uint32_t am_hal_pdm_configure(void *pHandle, am_hal_pdm_config_t *psConfig);

// Enable/Disable
extern uint32_t am_hal_pdm_enable(void *pHandle);
extern uint32_t am_hal_pdm_disable(void *pHandle);

// Gather PDM data.
extern uint32_t am_hal_pdm_dma_start(void *pHandle, am_hal_pdm_transfer_t *pDmaCfg);

// Flush the PDM FIFO.
extern uint32_t am_hal_pdm_fifo_flush(void *pHandle);

// I2S Passthrough
extern uint32_t am_hal_pdm_i2s_enable(void *pHandle);
extern uint32_t am_hal_pdm_i2s_disable(void *pHandle);

// Interrupts.
extern uint32_t am_hal_pdm_interrupt_enable(void *pHandle, uint32_t ui32IntMask);
extern uint32_t am_hal_pdm_interrupt_disable(void *pHandle, uint32_t ui32IntMask);
extern uint32_t am_hal_pdm_interrupt_clear(void *pHandle, uint32_t ui32IntMask);
extern uint32_t am_hal_pdm_interrupt_status_get(void *pHandle, uint32_t *pui32Status, bool bEnabledOnly);

#ifdef __cplusplus
}
#endif

#endif // AM_HAL_PDM_H

//*****************************************************************************
//
// End Doxygen group.
//! @}
//
//*****************************************************************************
