/* * Copyright (c) 2021-2023 HPMicro * * SPDX-License-Identifier: BSD-3-Clause * */ #ifndef HPM_I2S_DRV_H #define HPM_I2S_DRV_H #include "hpm_common.h" #include "hpm_soc_feature.h" #include "hpm_i2s_regs.h" #include "hpm_i2s_common.h" /** * @brief I2S driver APIs * @defgroup i2s_interface I2S driver APIs * @ingroup io_interfaces * @{ */ /** * @brief I2S data line */ #define I2S_DATA_LINE_0 (0U) #define I2S_DATA_LINE_1 (1U) #define I2S_DATA_LINE_2 (2U) #define I2S_DATA_LINE_3 (3U) #define I2S_DATA_LINE_MAX I2S_DATA_LINE_3 /** * @brief I2S config */ typedef struct i2s_config { bool invert_mclk_out; bool invert_mclk_in; bool use_external_mclk; bool invert_bclk_out; bool invert_bclk_in; bool use_external_bclk; bool invert_fclk_out; bool invert_fclk_in; bool use_external_fclk; bool enable_mclk_out; bool frame_start_at_rising_edge; uint16_t tx_fifo_threshold; uint16_t rx_fifo_threshold; } i2s_config_t; /** * @brief I2S transfer config */ typedef struct i2x_transfer_config { uint32_t sample_rate; bool enable_tdm_mode; uint8_t channel_num_per_frame; uint8_t channel_length; /* 16-bit or 32-bit */ uint8_t audio_depth; /* 16-bit, 24-bit, 32-bit */ bool master_mode; uint8_t protocol; uint8_t data_line; uint32_t channel_slot_mask; } i2s_transfer_config_t; typedef enum { i2s_tx_fifo_threshold_irq_mask = I2S_CTRL_TXDNIE_MASK, i2s_rx_fifo_threshold_irq_mask = I2S_CTRL_RXDAIE_MASK, i2s_fifo_error_irq_mask = I2S_CTRL_ERRIE_MASK, /*CFGR |= I2S_CFGR_TDM_EN_MASK; } /** * @brief disable TDM * * @param [in] ptr I2S base address */ static inline void i2s_disable_tdm(I2S_Type *ptr) { ptr->CFGR &= ~I2S_CFGR_TDM_EN_MASK; } /** * @brief update rx fifo threshold * * @param [in] ptr I2S base address * @param [in] threshold fifo threshold value */ static inline void i2s_update_rx_fifo_threshold(I2S_Type *ptr, uint8_t threshold) { ptr->FIFO_THRESH = (ptr->FIFO_THRESH & ~I2S_FIFO_THRESH_RX_MASK) | I2S_FIFO_THRESH_RX_SET(threshold); } /** * @brief update tx fifo threshold * * @param [in] ptr I2S base address * @param [in] threshold fifo threshold value */ static inline void i2s_update_tx_fifo_threshold(I2S_Type *ptr, uint8_t threshold) { ptr->FIFO_THRESH = (ptr->FIFO_THRESH & ~I2S_FIFO_THRESH_TX_MASK) | I2S_FIFO_THRESH_TX_SET(threshold); } /** * @brief open BCLK * * @param [in] ptr I2S base address */ static inline void i2s_ungate_bclk(I2S_Type *ptr) { ptr->CFGR &= ~I2S_CFGR_BCLK_GATEOFF_MASK; } /** * @brief gete off BCLK * * @param [in] ptr I2S base address */ static inline void i2s_gate_bclk(I2S_Type *ptr) { ptr->CFGR |= I2S_CFGR_BCLK_GATEOFF_MASK; } /** * @brief open MCLK * * @param [in] ptr I2S base address */ static inline void i2s_ungate_mclk(I2S_Type *ptr) { ptr->MISC_CFGR &= ~I2S_MISC_CFGR_MCLK_GATEOFF_MASK; } /** * @brief gate off MCLK * * @param [in] ptr I2S base address */ static inline void i2s_gate_mclk(I2S_Type *ptr) { ptr->MISC_CFGR |= I2S_MISC_CFGR_MCLK_GATEOFF_MASK; } /** * @brief enable TX dma request * * @param [in] ptr I2S base address */ static inline void i2s_enable_tx_dma_request(I2S_Type *ptr) { ptr->CTRL |= I2S_CTRL_TX_DMA_EN_MASK; } /** * @brief disable TX dma request * * @param [in] ptr I2S base address */ static inline void i2s_disable_tx_dma_request(I2S_Type *ptr) { ptr->CTRL &= ~I2S_CTRL_TX_DMA_EN_MASK; } /** * @brief enable RX dma request * * @param [in] ptr I2S base address */ static inline void i2s_enable_rx_dma_request(I2S_Type *ptr) { ptr->CTRL |= I2S_CTRL_RX_DMA_EN_MASK; } /** * @brief disable RX dma request * * @param [in] ptr I2S base address */ static inline void i2s_disable_rx_dma_request(I2S_Type *ptr) { ptr->CTRL &= ~I2S_CTRL_RX_DMA_EN_MASK; } /** * @brief enable IRQ * * @param [in] ptr I2S base address * @param [in] mask irq bit mask */ static inline void i2s_enable_irq(I2S_Type *ptr, uint32_t mask) { ptr->CTRL |= mask; } /** * @brief disable IRQ * * @param [in] ptr I2S base address * @param [in] mask irq bit mask */ static inline void i2s_disable_irq(I2S_Type *ptr, uint32_t mask) { ptr->CTRL &= ~mask; } /** * @brief I2S enable * * @note dropped API, please use i2s_start * * @param [in] ptr I2S base address */ static inline void i2s_enable(I2S_Type *ptr) { ptr->CTRL |= I2S_CTRL_I2S_EN_MASK; } /** * @brief I2S disable * * @note dropped API, please use i2s_stop * * @param [in] ptr I2S base address */ static inline void i2s_disable(I2S_Type *ptr) { ptr->CTRL &= ~I2S_CTRL_I2S_EN_MASK; } /** * @brief I2S start * * @param [in] ptr I2S base address */ static inline void i2s_start(I2S_Type *ptr) { ptr->CTRL |= I2S_CTRL_I2S_EN_MASK; } /** * @brief I2S stop * * @param [in] ptr I2S base address */ static inline void i2s_stop(I2S_Type *ptr) { ptr->CTRL &= ~I2S_CTRL_I2S_EN_MASK; } /** * @brief I2S enable rx function * * @param [in] ptr I2S base address * @param [in] rx_mask rx data line mask */ static inline void i2s_enable_rx(I2S_Type *ptr, uint8_t rx_mask) { ptr->CTRL |= I2S_CTRL_RX_EN_SET(rx_mask); } /** * @brief I2S disable rx function * * @param [in] ptr I2S base address * @param [in] rx_mask rx data line mask */ static inline void i2s_disable_rx(I2S_Type *ptr, uint8_t rx_mask) { ptr->CTRL &= ~I2S_CTRL_RX_EN_SET(rx_mask); } /** * @brief I2S enable tx function * * @param [in] ptr I2S base address * @param [in] tx_mask tx data line mask */ static inline void i2s_enable_tx(I2S_Type *ptr, uint8_t tx_mask) { ptr->CTRL |= I2S_CTRL_TX_EN_SET(tx_mask); } /** * @brief I2S disbale tx function * * @param [in] ptr I2S base address * @param [in] tx_mask tx data line mask */ static inline void i2s_disable_tx(I2S_Type *ptr, uint8_t tx_mask) { ptr->CTRL &= ~I2S_CTRL_TX_EN_SET(tx_mask); } /** * @brief I2S reset clock generator * * @param [in] ptr I2S base address */ static inline void i2s_reset_clock_gen(I2S_Type *ptr) { ptr->CTRL |= I2S_CTRL_SFTRST_CLKGEN_MASK; ptr->CTRL &= ~I2S_CTRL_SFTRST_CLKGEN_MASK; } /** * @brief I2S reset tx function * * @note This API will disable I2S, reset tx function * Please ensure that there is a valid BCLK when calling this function * * @param [in] ptr I2S base address */ static inline void i2s_reset_tx(I2S_Type *ptr) { /* disable I2S */ ptr->CTRL &= ~I2S_CTRL_I2S_EN_MASK; /* reset tx and clear fifo */ ptr->CTRL |= (I2S_CTRL_TXFIFOCLR_MASK | I2S_CTRL_SFTRST_TX_MASK); ptr->CTRL &= ~(I2S_CTRL_TXFIFOCLR_MASK | I2S_CTRL_SFTRST_TX_MASK); } /** * @brief I2S reset rx function * * @note This API will disable I2S, reset rx function * Please ensure that there is a valid BCLK when calling this function * * @param [in] ptr I2S base address */ static inline void i2s_reset_rx(I2S_Type *ptr) { /* disable I2S */ ptr->CTRL &= ~I2S_CTRL_I2S_EN_MASK; /* reset rx and clear fifo */ ptr->CTRL |= (I2S_CTRL_RXFIFOCLR_MASK | I2S_CTRL_SFTRST_RX_MASK); ptr->CTRL &= ~(I2S_CTRL_RXFIFOCLR_MASK | I2S_CTRL_SFTRST_RX_MASK); } /** * @brief I2S reset tx and rx function * * @note This API will disable I2S, reset tx/rx function * Please ensure that there is a valid BCLK when calling this function * * @param [in] ptr I2S base address */ static inline void i2s_reset_tx_rx(I2S_Type *ptr) { /* disable I2S */ ptr->CTRL &= ~I2S_CTRL_I2S_EN_MASK; /* reset tx/rx and clear fifo */ ptr->CTRL |= (I2S_CTRL_TXFIFOCLR_MASK | I2S_CTRL_RXFIFOCLR_MASK | I2S_CTRL_SFTRST_TX_MASK | I2S_CTRL_SFTRST_RX_MASK); ptr->CTRL &= ~(I2S_CTRL_TXFIFOCLR_MASK | I2S_CTRL_RXFIFOCLR_MASK | I2S_CTRL_SFTRST_TX_MASK | I2S_CTRL_SFTRST_RX_MASK); } /** * @brief I2S reset tx/rx and clock generator module * * @note This API will disable I2S, reset tx/rx and clock generator module * This function uses an internal clock to generate BCLK, then do reset operation, * and finally restores the previous clock settings * * @param [in] ptr I2S base address */ void i2s_reset_all(I2S_Type *ptr); /** * @brief I2S get tx fifo level * * @param [in] ptr I2S base address * * @retval I2S tx fifo level */ static inline uint32_t i2s_get_tx_fifo_level(I2S_Type *ptr) { return ptr->TFIFO_FILLINGS; } /** * @brief I2S get data line tx fifo level * * @param [in] ptr I2S base address * @param [in] line I2S data line * * @retval I2S data line tx fifo level */ static inline uint32_t i2s_get_tx_line_fifo_level(I2S_Type *ptr, uint8_t line) { return (i2s_get_tx_fifo_level(ptr) & (0xFF << (line << 3))) >> (line << 3); } /** * @brief I2S get rx fifo level * * @param [in] ptr I2S base address * * @retval I2S rx fifo level */ static inline uint32_t i2s_get_rx_fifo_level(I2S_Type *ptr) { return ptr->RFIFO_FILLINGS; } /** * @brief I2S get data line rx fifo level * * @param [in] ptr I2S base address * @param [in] line I2S data line * * @retval I2S data line rx fifo level */ static inline uint32_t i2s_get_rx_line_fifo_level(I2S_Type *ptr, uint8_t line) { return (i2s_get_rx_fifo_level(ptr) & (0xFF << (line << 3))) >> (line << 3); } /** * @brief Check I2S data line status * * @param[in] ptr I2S base address * @param[in] line I2S data line * * @retval i2s_data_line_rx_fifo_avail data in rx fifo >= threshold * @retval i2s_data_line_tx_fifo_avail data in tx fifo <= threshold * @retval i2s_data_line_rx_fifo_overrun rx fifo overrun occured * @retval i2s_data_line_tx_fifo_underrun tx fifo underrun occured */ static inline uint32_t i2s_check_data_line_status(I2S_Type *ptr, uint8_t line) { volatile uint32_t reg_val = ptr->STA; uint32_t bit_mask; uint32_t stat = 0; bit_mask = 1 << (I2S_STA_RX_DA_SHIFT + line); if ((bit_mask & reg_val) != 0) { stat |= i2s_data_line_rx_fifo_avail; } bit_mask = 1 << (I2S_STA_TX_DN_SHIFT + line); if ((bit_mask & reg_val) != 0) { stat |= i2s_data_line_tx_fifo_avail; } bit_mask = 1 << (I2S_STA_RX_OV_SHIFT + line); if ((bit_mask & reg_val) != 0) { stat |= i2s_data_line_rx_fifo_overrun; ptr->STA = bit_mask; /* clear flag: W1C*/ } bit_mask = 1 << (I2S_STA_TX_UD_SHIFT + line); if ((bit_mask & reg_val) != 0) { stat |= i2s_data_line_tx_fifo_underrun; ptr->STA = bit_mask; /* clear flag: W1C*/ } return stat; } /** * @brief I2S get IRQ status * * @param [in] ptr I2S base address * * @retval I2S STA register value */ static inline uint32_t i2s_get_irq_status(I2S_Type *ptr) { return ptr->STA; } /** * @brief I2S stop transfer * * @param [in] ptr I2S base address */ static inline void i2s_stop_transfer(I2S_Type *ptr) { i2s_disable(ptr); } /** * @brief I2S config tx * * @note This API will disable I2S and configure parameters, could call i2s_enable() to enable I2S * * @param [in] ptr I2S base address * @param [in] mclk_in_hz mclk frequency in Hz * @param [in] config i2s_transfer_config_t * @retval hpm_stat_t status_invalid_argument or status_success */ hpm_stat_t i2s_config_tx(I2S_Type *ptr, uint32_t mclk_in_hz, i2s_transfer_config_t *config); /** * @brief I2S config tx for slave * * @note This API will disable I2S and configure parameters, could call i2s_enable() to enable I2S * * @param [in] ptr I2S base address * @param [in] config i2s_transfer_config_t */ hpm_stat_t i2s_config_tx_slave(I2S_Type *ptr, i2s_transfer_config_t *config); /** * @brief I2S config rx * * @note This API will disable I2S and configure parameters, could call i2s_enable() to enable I2S * * @param [in] ptr I2S base address * @param [in] mclk_in_hz mclk frequency in Hz * @param [in] config i2s_transfer_config_t * @retval hpm_stat_t status_invalid_argument or status_success */ hpm_stat_t i2s_config_rx(I2S_Type *ptr, uint32_t mclk_in_hz, i2s_transfer_config_t *config); /** * @brief I2S config rx for slave * * @note This API will disable I2S and configure parameters, could call i2s_enable() to enable I2S * * @param [in] ptr I2S base address * @param [in] config i2s_transfer_config_t * @retval hpm_stat_t status_invalid_argument or status_success */ hpm_stat_t i2s_config_rx_slave(I2S_Type *ptr, i2s_transfer_config_t *config); /** * @brief I2S config transfer * * @note This API will disable I2S and configure parameters, could call i2s_enable() to enable I2S * * @param [in] ptr I2S base address * @param [in] mclk_in_hz mclk frequency in Hz * @param [in] config i2s_transfer_config_t * @retval hpm_stat_t status_invalid_argument or status_success */ hpm_stat_t i2s_config_transfer(I2S_Type *ptr, uint32_t mclk_in_hz, i2s_transfer_config_t *config); /** * @brief I2S config transfer for slave * * @note This API will disable I2S and configure parameters, could call i2s_enable() to enable I2S * * @param [in] ptr I2S base address * @param [in] config i2s_transfer_config_t * @retval hpm_stat_t status_invalid_argument or status_success */ hpm_stat_t i2s_config_transfer_slave(I2S_Type *ptr, i2s_transfer_config_t *config); /** * @brief I2S send data * * @param [in] ptr I2S base address * @param [in] tx_line_index data line * @param [in] data data to be written */ static inline void i2s_send_data(I2S_Type *ptr, uint8_t tx_line_index, uint32_t data) { ptr->TXD[tx_line_index] = data; } /** * @brief I2S receive data * * @param [in] ptr I2S base address * @param [in] rx_line_index data line * @param [out] data point to store data address */ static inline void i2s_receive_data(I2S_Type *ptr, uint8_t rx_line_index, uint32_t *data) { *data = ptr->RXD[rx_line_index]; } /** * @brief I2S send data in buff * * @param [in] ptr I2S base address * @param [in] tx_line_index data line * @param [in] samplebits audio data width * @param [in] src source data buff * @param [in] size data size * * @retval I2S sent data size in byte */ uint32_t i2s_send_buff(I2S_Type *ptr, uint8_t tx_line_index, uint8_t samplebits, uint8_t *src, uint32_t size); /** * @brief I2S receive data in buff * * @param [in] ptr I2S base address * @param [in] rx_line_index data line * @param [in] samplebits audio data width * @param [out] dst target data buff * @param [in] size data size * * @retval I2S sent data size in byte */ uint32_t i2s_receive_buff(I2S_Type *ptr, uint8_t rx_line_index, uint8_t samplebits, uint8_t *dst, uint32_t size); /** * @brief I2S get default config * * @param [in] ptr I2S base address * @param [out] config i2s_config_t */ void i2s_get_default_config(I2S_Type *ptr, i2s_config_t *config); /** * @brief I2S initialization * * @param [in] ptr I2S base address * @param [in] config i2s_config_t */ void i2s_init(I2S_Type *ptr, i2s_config_t *config); /** * @brief I2S get default transfer config for pdm * * @param [out] transfer i2s_transfer_config_t */ void i2s_get_default_transfer_config_for_pdm(i2s_transfer_config_t *transfer); /** * @brief I2S get default transfer config for dao * * @param [out] transfer i2s_transfer_config_t */ void i2s_get_default_transfer_config_for_dao(i2s_transfer_config_t *transfer); /** * @brief I2S get default transfer config * * @param [out] transfer i2s_transfer_config_t */ void i2s_get_default_transfer_config(i2s_transfer_config_t *transfer); /** * @brief I2S fill dummy data into TX fifo * * @note workaround: fill dummy data into TX fifo to avoid TX underflow during tx start * * @param [in] ptr I2S base address * @param [in] data_line data line * @param [in] data_count dummy data count, This value should be the same as the number of audio channels * * @retval status_success if no error occurred */ hpm_stat_t i2s_fill_tx_dummy_data(I2S_Type *ptr, uint8_t data_line, uint8_t data_count); /** * @} */ #ifdef __cplusplus } #endif #endif /* HPM_I2S_DRV_H */