/*
 * Copyright (c) 2021 HPMicro
 *
 * SPDX-License-Identifier: BSD-3-Clause
 *
 */


#ifndef HPM_DAO_DRV_H
#define HPM_DAO_DRV_H

#include "hpm_common.h"
#include "hpm_dao_regs.h"
#include "hpm_i2s_common.h"
#include "hpm_soc_feature.h"

/**
 * @brief DAO driver APIs
 * @defgroup dao_interface DAO driver APIs
 * @ingroup io_interfaces
 * @{
 */

/**
 * @brief DAO channel selection
 */
#define DAO_CHANNEL_LEFT_ONLY DAO_CTRL_LEFT_EN_MASK
#define DAO_CHANNEL_RIGHT_ONLY DAO_CTRL_RIGHT_EN_MASK
#define DAO_CHANNEL_BOTH \
    (DAO_CTRL_RIGHT_EN_MASK | DAO_CTRL_LEFT_EN_MASK)

/**
 * @brief DAO default output
 */
#define DAO_DEFAULT_OUTPUT_ALL_LOW (0U)
#define DAO_DEFAULT_OUTPUT_ALL_HIGH (1U)
#define DAO_DEFAULT_OUTPUT_P_HIGH_N_LOW (2U)
#define DAO_DEFAULT_OUTPUT_DISABLED (3U)

/**
 * @brief DAO config
 */
typedef struct dao_config {
    bool enable_mono_output;
    uint8_t default_output_level;
    uint8_t channel_count;
#if defined(HPM_IP_FEATURE_DAO_DATA_FORMAT_CONFIG) && (HPM_IP_FEATURE_DAO_DATA_FORMAT_CONFIG == 1)
    bool enable_tdm_mode;
    bool frame_start_at_rising_edge;
    uint8_t protocol;
    uint8_t channel_length;
    uint8_t audio_depth;
#endif
    uint8_t channel_slot_mask;
} dao_config_t;

typedef enum {
    dao_right_channel = DAO_CTRL_RIGHT_EN_MASK,
    dao_left_channel = DAO_CTRL_LEFT_EN_MASK,
} dao_channel_t;

#ifdef __cplusplus
extern "C" {
#endif

/**
 * @brief config high pass filter
 *
 * @param [in] ptr DAO base address
 * @param [in] hpf_coef_ma high pass filter a coefficient's complement
 * @param [in] hpf_coef_b high pass filter b coefficient
 * @param [in] enable
 *  @arg true: enable
 *  @arg false: disable
 */
static inline void dao_config_hpf(DAO_Type *ptr,
                                     uint32_t hpf_coef_ma,
                                     uint32_t hpf_coef_b,
                                     bool enable)
{
    ptr->HPF_MA = DAO_HPF_MA_COEF_SET(hpf_coef_ma);
    ptr->HPF_B = DAO_HPF_B_COEF_SET(hpf_coef_b);
    ptr->CTRL = (ptr->CTRL & ~DAO_CTRL_HPF_EN_MASK)
        | (enable ? DAO_CTRL_HPF_EN_MASK : 0);
}

/**
 * @brief enable high pass filter
 *
 * @param [in] ptr DAO base address
 */
static inline void dao_enable_hpf(DAO_Type *ptr)
{
    ptr->CTRL |= DAO_CTRL_HPF_EN_MASK;
}

/**
 * @brief disable high pass filter
 *
 * @param [in] ptr DAO base address
 */
static inline void dao_disable_hpf(DAO_Type *ptr)
{
    ptr->CTRL &= ~DAO_CTRL_HPF_EN_MASK;
}

/**
 * @brief enable channel
 *
 * @param [in] ptr DAO base address
 * @param [in] ch channel defined in dao_channel_t
 */
static inline void dao_enable_channel(DAO_Type *ptr, uint32_t ch)
{
    ptr->CTRL |= ch;
}

/**
 * @brief disable channel
 *
 * @param [in] ptr DAO base address
 * @param [in] ch channel defined in dao_channel_t
 */
static inline void dao_disable_channel(DAO_Type *ptr, uint32_t ch)
{
    ptr->CTRL &= ~ch;
}

/**
 * @brief enable mono output
 *
 * @param [in] ptr DAO base address
 */
static inline void dao_enable_mono_output(DAO_Type *ptr)
{
    ptr->CTRL |= DAO_CTRL_MONO_MASK;
}

/**
 * @brief disable mono output
 *
 * @param [in] ptr DAO base address
 */
static inline void dao_disable_mono_output(DAO_Type *ptr)
{
    ptr->CTRL &= ~DAO_CTRL_MONO_MASK;
}

/**
 * @brief enable remap
 *
 * @param [in] ptr DAO base address
 */
static inline void dao_enable_remap(DAO_Type *ptr)
{
    ptr->CTRL |= DAO_CTRL_REMAP_MASK;
}

/**
 * @brief disable remap
 *
 * @param [in] ptr DAO base address
 */
static inline void dao_disable_remap(DAO_Type *ptr)
{
    ptr->CTRL &= ~DAO_CTRL_REMAP_MASK;
}

/**
 * @brief invert output
 *
 * @param [in] ptr DAO base address
 * @param [in] invert
 *  @arg true: invert output
 *  @arg false: not invert output
 */
static inline void dao_invert_output(DAO_Type *ptr, bool invert)
{
    ptr->CTRL = (ptr->CTRL & DAO_CTRL_INVERT_MASK)
                | DAO_CTRL_INVERT_SET(invert);
}

/**
 * @brief force pads output with certain level
 *
 * @param [in] ptr DAO base address
 * @param [in] output output level
 */
static inline void dao_force_output(DAO_Type *ptr, uint8_t output)
{
    ptr->CTRL = (ptr->CTRL & DAO_CTRL_FALSE_LEVEL_MASK)
                | DAO_CTRL_FALSE_LEVEL_SET(output);
}

/**
 * @brief enable false run
 * when false run mode is enabled, the module continues to consume data, no actual output on pads.
 * @param [in] ptr DAO base address
 * @param [in] enable
 *  @arg true: enable
 *  @arg false: disable
 */
static inline void dao_enable_false_run(DAO_Type *ptr, bool enable)
{
    ptr->CTRL = (ptr->CTRL & DAO_CTRL_FALSE_RUN_MASK)
                | DAO_CTRL_FALSE_RUN_SET(enable);
}

/**
 * @brief software reset
 *
 * @param [in] ptr DAO base address
 */
static inline void dao_software_reset(DAO_Type *ptr)
{
    ptr->CMD |= DAO_CMD_SFTRST_MASK;
    ptr->CMD &= ~DAO_CMD_SFTRST_MASK;
}

/**
 * @brief check whether DAO is running
 *
 * @param [in] ptr DAO base address
 * @retval true if dao is running
 */
static inline bool dao_is_running(DAO_Type *ptr)
{
    return ptr->CMD & DAO_CMD_RUN_MASK;
}

/**
 * @brief start
 *
 * @param [in] ptr DAO base address
 */
static inline void dao_start(DAO_Type *ptr)
{
    ptr->CMD |= DAO_CMD_RUN_MASK;
}

/**
 * @brief stop
 *
 * @param [in] ptr DAO base address
 */
static inline void dao_stop(DAO_Type *ptr)
{
    ptr->CMD &= ~DAO_CMD_RUN_MASK;
}

/**
 * @brief initlization
 *
 * @param [in] ptr DAO base address
 * @param [in] config dao_config_t
 * @retval hpm_stat_t status_invalid_argument or status_success
 */
hpm_stat_t dao_init(DAO_Type *ptr, dao_config_t *config);

/**
 * @brief get default config
 *
 * @param [in] ptr DAO base address
 * @param [out] config dao_config_t
 */
void dao_get_default_config(DAO_Type *ptr, dao_config_t *config);

/**
 * @}
 */

#ifdef __cplusplus
}
#endif

#endif /* HPM_DAO_DRV_H */
