/*
 * Copyright (c) 2022 HPMicro
 *
 * SPDX-License-Identifier: BSD-3-Clause
 *
 */
#ifndef HPM_FFA_DRV_H
#define HPM_FFA_DRV_H

#include "hpm_common.h"
#include "hpm_ffa_regs.h"
#include "hpm_soc_feature.h"
#include "hpm_soc_ip_feature.h"

/**
 * @brief FFA driver APIs
 * @defgroup ffa_interface FFA driver APIs
 * @ingroup ffa_interfaces
 * @{
 *
 */

/***********************************************************************************************************************
 *
 * Definitions
 *
 **********************************************************************************************************************/
/**
 * @brief Operation commands supported by FFA
 */
#define FFA_OPCMD_FIR (0U)      /* !< FIR operation command index */
#define FFA_OPCMD_FFT (2U)      /* !< FFT operation command index */

/**
 * @brief Data type supported by FFA
 */
#define FFA_DATA_TYPE_REAL_Q31      (0U)    /* !< FFA Data type:  Real Q31 */
#define FFA_DATA_TYPE_REAL_Q15      (1U)    /* !< FFA Data type: Real Q15 */
#define FFA_DATA_TYPE_COMPLEX_Q31   (2U)    /* !< FFA Data type: Complex Q31 */
#define FFA_DATA_TYPE_COMPLEX_Q15   (3U)    /* !< FFA Data type: Complex Q15 */
#if defined(HPM_IP_FEATURE_FFA_FP32) && HPM_IP_FEATURE_FFA_FP32
#define FFA_DATA_TYPE_COMPLEX_FP32  (4U)    /* !< FFA Data type: Complex Q15 */
#define FFA_DATA_TYPE_REAL_FP32     (5U)    /* !< FFA Data type: Complex Q15 */
#endif

/**
 * @brief FFA Q31 data type definition
 */
typedef int32_t ffa_q31_t;

/**
 * @brief FFA Q15 data type definition
 */
typedef int16_t ffa_q15_t;

/**
 * @brief FFA complex Q31 data type definition
 */
typedef struct {
    ffa_q31_t real;
    ffa_q31_t image;
} ffa_complex_q31_t;

/**
 * @brief FFA complex Q15 data type definition
 */
typedef struct {
    ffa_q15_t real;
    ffa_q15_t image;
} ffa_complex_q15_t;

/**
 * @brief FFT transform context
 */
typedef struct {
    uint16_t is_ifft;           /* !< Is Inverse FFT transform */
    uint8_t src_data_type;      /* !< Source data type */
    uint8_t dst_data_type;      /* !< Destination date type */
    uint32_t num_points;        /* !< Number of points */
    const void *src;            /* !< Source data buffer */
    void *dst;                  /* !< Destination Data buffer */
    uint32_t interrupt_mask;    /* !< Interrupt mask */
} fft_xfer_t;

/**
 * @brief FIR transform context
 */
typedef struct {
    uint16_t data_type;         /* !< Data type */
    uint16_t coef_taps;         /* !< Coefficient taps */
    uint32_t input_taps;        /* !< Input data taps */
    const void *src;            /* !< Source data buffer */
    const void *coeff;          /* !< Coefficient data buffer */
    void *dst;                  /* !< Destination data buffer */
    uint32_t interrupt_mask;    /* !< Interrupt mask */
} fir_xfer_t;

/**
 * @brief FFA error codes
 */
enum {
    status_ffa_fir_overflow = MAKE_STATUS(status_group_ffa, 0),         /* !< FIR overflow */
    status_ffa_fft_overflow = MAKE_STATUS(status_group_ffa, 1),         /* !< FFR overflow */
    status_ffa_write_error = MAKE_STATUS(status_group_ffa, 2),          /* !< FFA write error */
    status_ffa_read_next_error = MAKE_STATUS(status_group_ffa, 3),      /* !< FFA read next data error */
    status_ffa_read_error = MAKE_STATUS(status_group_ffa, 4),           /*!< FFA read error */
};

#if defined(HPM_IP_FEATURE_FFA_FP32) && HPM_IP_FEATURE_FFA_FP32
typedef enum {
    input_data = 0,
    output_data = 1,
    coeff_data = 2
} ffa_fp32_status_source_t;
#endif

#ifdef __cplusplus
extern "C" {
#endif

/**
 * @brief Enable FFA module and start an specified FFA operation
 *
 * @param [in] ptr FFA base address
 */
static inline void ffa_enable(FFA_Type *ptr)
{
    ptr->CTRL = (ptr->CTRL & ~FFA_CTRL_SFTRST_MASK) | FFA_CTRL_EN_MASK;
}

/**
 * @brief Stop FFA module
 *
 * @param [in] ptr FFA base address
 */
static inline void ffa_disable(FFA_Type *ptr)
{
    ptr->CTRL = (ptr->CTRL & ~FFA_CTRL_EN_MASK) | FFA_CTRL_SFTRST_MASK;
}

/**
 * @brief Get FFA status
 *
 * @param [in] ptr FFA base address
 * @return FFA status register value
 */
static inline uint32_t ffa_get_status(FFA_Type *ptr)
{
    return ptr->STATUS;
}

/**
 * @brief Enable FFA Interrupt
 *
 * @param [in] ptr FFA base address
 * @param [in] mask FFA interrupt mask
 */
static inline void ffa_enable_interrupt(FFA_Type *ptr, uint32_t mask)
{
    ptr->INT_EN |= mask;
}

/**
 * @brief Disable FFA interrupt
 *
 * @param [in] ptr FFA base address
 * @param [in] mask FFA interrupt mask
 */
static inline void ffa_disable_interrupt(FFA_Type *ptr, uint32_t mask)
{
    ptr->INT_EN &= ~mask;
}

#if defined(HPM_IP_FEATURE_FFA_FP32) && HPM_IP_FEATURE_FFA_FP32

static inline void ffa_enable_fp32_interrupt(FFA_Type *ptr, uint32_t mask)
{
    ptr->FP_CTRL |= mask;
}

static inline void ffa_disable_fp32_interrupt(FFA_Type *ptr, uint32_t mask)
{
    ptr->FP_CTRL &= ~mask;
}

static inline void ffa_set_fp_status_source(FFA_Type *ptr, ffa_fp32_status_source_t source)
{
    ptr->FP_CTRL = FFA_FP_CTRL_EXP_ST_SEL_SET(source);
}

static inline void ffa_enable_fp_bias(FFA_Type *ptr)
{
    ptr->FP_CTRL |= FFA_FP_CTRL_OPT_BIAS_EXP_MASK;
}

static inline void ffa_disable_fp_bias(FFA_Type *ptr)
{
    ptr->FP_CTRL &= ~FFA_FP_CTRL_OPT_BIAS_EXP_MASK;
}

static inline void ffa_set_coef_max_index(FFA_Type *ptr, uint8_t max)
{
    ptr->FP_CTRL = (ptr->FP_CTRL & ~FFA_FP_CTRL_COEF_MAX_MASK) | FFA_FP_CTRL_COEF_MAX_SET(max);
}

static inline void ffa_set_output_max_index(FFA_Type *ptr, uint8_t max)
{
    ptr->FP_CTRL = (ptr->FP_CTRL & ~FFA_FP_CTRL_OUT_MAX_MASK) | FFA_FP_CTRL_OUT_MAX_SET(max);
}

static inline void ffa_set_input_max_index(FFA_Type *ptr, uint8_t max)
{
    ptr->FP_CTRL = (ptr->FP_CTRL & ~FFA_FP_CTRL_IN_MAX_MASK) | FFA_FP_CTRL_IN_MAX_SET(max);
}

static inline uint32_t ffa_get_fp_status(FFA_Type *ptr)
{
    return ptr->FP_ST;
}

#endif

/**
 * @brief Start an FFT operation
 *
 * @param [in] ptr FFA base address
 * @param [in] fft_xfer FFT transform context
 */
void ffa_start_fft(FFA_Type *ptr, fft_xfer_t *fft_xfer);

/**
 * @brief Start an FIR operation
 *
 * @param [in] ptr FFA base address
 * @param [in] fir_xfer FIR transform context
 */
void ffa_start_fir(FFA_Type *ptr, fir_xfer_t *fir_xfer);

/**
 * @brief Perform FFT transformation in blocking mode
 *
 * @param [in] ptr FFA base address
 * @param [in, out] fft_xfer FFT transform context
 * @return FFT operation result
 */
hpm_stat_t ffa_calculate_fft_blocking(FFA_Type *ptr, fft_xfer_t *fft_xfer);

/**
 * @brief Perform FIR transform in blocking mode
 *
 * @param [in] ptr FFA base address
 * @param [in, out] fir_xfer FIR transform context
 * @return FIR operation result
 */
hpm_stat_t ffa_calculate_fir_blocking(FFA_Type *ptr, fir_xfer_t *fir_xfer);


#ifdef __cplusplus
}
#endif
/**
 * @}
 *
 */


#endif /* HPM_FFA_DRV_H */
