/**
 * @file tkl_i2s.c
 * @brief default weak implements of tuya pin
 *
 * @copyright Copyright 2020-2021 Tuya Inc. All Rights Reserved.
 *
 */

// --- BEGIN: user defines and implements ---
#include <os/mem.h>
#include <driver/i2s.h>
#include "tkl_output.h"
#include "tal_log.h"
#include "tkl_i2s.h"
#include "i2s_hal.h"
#include "tkl_audio.h"
#include "tkl_queue.h"
#include "tkl_system.h"
#include "bk_general_dma.h"

#include "i2s_hw.h"
#include <driver/i2s_types.h>
// --- END: user defines and implements ---

/***********************************************************
************************macro define************************
***********************************************************/
#define DMA_FRAME_SIZE    (16000 * 2 * 32 / 8 / 50)
#define BUFFER_NUM        (2)
#define DMA_MAGIC         (0xf0f0f0f0)
/***********************************************************
***********************typedef define***********************
***********************************************************/
typedef struct i2s_cb_config {
    i2s_data_handle_cb tx_cb;   //发送回调，dma发送完成时调用
    i2s_data_handle_cb rx_cb;   //接收回调，dma接收完成时调用
    dma_id_t dma_id;
    UINT32_T * dma_curr_buf;
}i2s_cb_config_t;

typedef struct 
{
    UINT32_T *dma_buffer;
}i2s_dma_buffer_t;

/***********************************************************
********************function declaration********************
***********************************************************/
static int ch0_tx_data_handle_cb(uint32_t size);
static int ch0_rx_data_handle_cb(uint32_t size);
static int ch1_tx_data_handle_cb(uint32_t size);
static int ch1_rx_data_handle_cb(uint32_t size);
static int ch2_tx_data_handle_cb(uint32_t size);
static int ch2_rx_data_handle_cb(uint32_t size);
/***********************************************************
***********************variable define**********************
***********************************************************/
i2s_cb_config_t m_i2s_cfg[TUYA_I2S_NUM_MAX] = {
    {ch0_tx_data_handle_cb,ch0_rx_data_handle_cb,DMA_ID_MAX},
    {ch1_tx_data_handle_cb,ch1_rx_data_handle_cb,DMA_ID_MAX},
    {ch2_tx_data_handle_cb,ch2_rx_data_handle_cb,DMA_ID_MAX}
};
static TUYA_I2S_BASE_CFG_T m_i2s_config[TUYA_I2S_NUM_MAX] = {0};
static TKL_QUEUE_HANDLE m_txrx_queue[TUYA_I2S_NUM_MAX] = {0}; //不可能既为发送设备又为接收设备，只需一个信号量
static i2s_dma_buffer_t m_buffer[TUYA_I2S_NUM_MAX][BUFFER_NUM] = {0};
/***********************************************************
***********************function define**********************
************************************************************/
/**
 * @brief Init i2s buffer
 *
 * @param[in] buffer_size: size of per buffer
 * @param[in] i2s_num: group id of i2s
 *
 * @return err or size
 */
static INT32_T i2s_buffer_init(UINT32_T buffer_size, UINT8_T i2s_num)
{
    INT32_T ret = 0;
    int level = 0;
    level = tkl_system_enter_critical();
    for (int j = 0; j < BUFFER_NUM; j++)
    {
        m_buffer[i2s_num][j].dma_buffer = os_malloc(buffer_size);
        if ((m_buffer[i2s_num][j].dma_buffer == NULL))
        {
            ret = -1L;
            break;
        }
    }
    if (ret != 0L)
    {
        for (int j = 0; j < BUFFER_NUM; j++)
        {
            if (m_buffer[i2s_num][j].dma_buffer)
            {
                os_free(m_buffer[i2s_num][j].dma_buffer);
                m_buffer[i2s_num][j].dma_buffer = NULL;
            }
        }
    }
    
    m_i2s_cfg[i2s_num].dma_curr_buf = m_buffer[i2s_num][0].dma_buffer;
    tkl_system_exit_critical(level);
    return ret;
}

/**
 * @brief deinit i2s buffer 
 *
 * @param[in] i2s_num: group id of i2s
 *
 * @return err or size
 */
static INT32_T i2s_buffer_deinit(UINT8_T i2s_num)
{
    int level = 0;
    level = tkl_system_enter_critical();
    for (int j = 0; j < BUFFER_NUM; j++)
    {
        if (m_buffer[i2s_num][j].dma_buffer)
        {
            os_free(m_buffer[i2s_num][j].dma_buffer);
            m_buffer[i2s_num][j].dma_buffer = NULL;
        }
    }
    tkl_system_exit_critical(level);
    return 0;
}

/**
 * @brief find a free i2s buffer 
 *
 * @param[out] buffers: address of i2s free buffer
 * @param[in] i2s_num: group id of i2s
 *
 * @return err or size
 */
static void i2s_find_buffer(void** buffers, UINT8_T i2s_num)
{
    if (buffers == NULL) {
        return;
    }
    *buffers = NULL;

    for (int i = BUFFER_NUM - 1; i >= 0; i--) {
        if (m_i2s_cfg[i2s_num].dma_curr_buf == m_buffer[i2s_num][i].dma_buffer) {
            int next_idx = (i == 0) ? (BUFFER_NUM - 1) : (i - 1); // 环形缓冲区
            *buffers = m_buffer[i2s_num][next_idx].dma_buffer;
            m_i2s_cfg[i2s_num].dma_curr_buf = *buffers;
            return;
        }
    }
}

/**
 * @brief i2s dma config
 *
 * @param[in] dma_id: dma id of i2s
 * @param[in] ring_buff_addr: address of i2s dma buffer
 * @param[in] ring_buff_size: transfer size of i2s dma buffer
 * @param[in] type: type of i2s
 * @param[in] chl_id: channel id of i2s dma
 * @param[in] data_handle_cb: callback function
 *
 * @return err or size
 */
static bk_err_t i2s_dma_config(dma_id_t dma_id, UINT32_T *ring_buff_addr, UINT32_T ring_buff_size, i2s_channel_id_t chl_id, i2s_txrx_type_t type, i2s_data_handle_cb data_handle_cb)
{
	bk_err_t ret = BK_OK;
	dma_config_t dma_config;
	uint32_t i2s_data_addr;
	UINT8_T i2s_index = i2s_hal_get_cfg_index();

	dma_config.mode = DMA_WORK_MODE_REPEAT;
	dma_config.chan_prio = 1;
	dma_config.src.width = DMA_DATA_WIDTH_32BITS;
	dma_config.dst.width = DMA_DATA_WIDTH_32BITS;
	dma_config.src.addr_inc_en = DMA_ADDR_INC_DISABLE;
	dma_config.src.addr_loop_en = DMA_ADDR_LOOP_ENABLE;
	dma_config.dst.addr_inc_en = DMA_ADDR_INC_ENABLE;
	dma_config.dst.addr_loop_en = DMA_ADDR_LOOP_ENABLE;
	/* get i2s address */
	bk_i2s_get_data_addr(chl_id, &i2s_data_addr);
	if (type == I2S_TXRX_TYPE_RX) {
		switch (chl_id) {
			case I2S_CHANNEL_1:
				if (i2s_index == 0) {
					dma_config.src.dev = DMA_DEV_I2S_RX;	//DMA_DEV_I2S_RX
				} else if (i2s_index == 1) {
					dma_config.src.dev = DMA_DEV_I2S1_RX;
				} else {
					dma_config.src.dev = DMA_DEV_I2S2_RX;
				}
				break;
			case I2S_CHANNEL_2:
				if (i2s_index == 0) {
					dma_config.src.dev = DMA_DEV_I2S_RX_CH1;
				} else if (i2s_index == 1) {
					dma_config.src.dev = DMA_DEV_I2S1_RX;
				} else {
					dma_config.src.dev = DMA_DEV_I2S2_RX;
				}
				break;
			case I2S_CHANNEL_3:
				if (i2s_index == 0) {
					dma_config.src.dev = DMA_DEV_I2S_RX_CH2;
				} else if (i2s_index == 1) {
					dma_config.src.dev = DMA_DEV_I2S1_RX;
				} else {
					dma_config.src.dev = DMA_DEV_I2S2_RX;
				}
				break;
			default:
				break;
		}
		dma_config.dst.dev = DMA_DEV_DTCM;
		dma_config.src.start_addr = i2s_data_addr;
		dma_config.src.end_addr = i2s_data_addr + 4;
		dma_config.dst.start_addr = (uint32_t)ring_buff_addr;
		dma_config.dst.end_addr = (uint32_t)ring_buff_addr + ring_buff_size;
	} else {
		dma_config.src.dev = DMA_DEV_DTCM;
		switch (chl_id) {
			case I2S_CHANNEL_1:
				if (i2s_index == 0) {
					dma_config.dst.dev = DMA_DEV_I2S;
				} else if (i2s_index == 1) {
					dma_config.dst.dev = DMA_DEV_I2S1;
				} else {
					dma_config.dst.dev = DMA_DEV_I2S2;
				}
				break;
			case I2S_CHANNEL_2:
				if (i2s_index == 0) {
					dma_config.dst.dev = DMA_DEV_I2S_CH1;
				} else if (i2s_index == 1) {
					dma_config.dst.dev = DMA_DEV_I2S1;
				} else {
					dma_config.dst.dev = DMA_DEV_I2S2;
				}
				break;
			case I2S_CHANNEL_3:
				if (i2s_index == 0) {
					dma_config.dst.dev = DMA_DEV_I2S_CH2;
				} else if (i2s_index == 1) {
					dma_config.dst.dev = DMA_DEV_I2S1;
				} else {
					dma_config.dst.dev = DMA_DEV_I2S2;
				}
				break;
			default:
				break;
		}
		dma_config.src.start_addr = (uint32_t)ring_buff_addr;
		dma_config.src.end_addr = (uint32_t)ring_buff_addr + ring_buff_size;
		dma_config.dst.start_addr = i2s_data_addr;
		dma_config.dst.end_addr = i2s_data_addr + 4;
	}

	/* init dma channel */
	ret = bk_dma_init(dma_id, &dma_config);
	if (ret != BK_OK) {
		bk_printf("audio adc dma channel init fail \r\n");
		return BK_FAIL;
	}

	/* set dma transfer length */
	bk_dma_set_transfer_len(dma_id, (ring_buff_size));

	if (data_handle_cb) {
		bk_dma_register_isr(dma_id, NULL, (void *)data_handle_cb);

		bk_dma_enable_finish_interrupt(dma_id);
	}
#if (CONFIG_SPE)
	bk_dma_set_src_sec_attr(dma_id, DMA_ATTR_SEC);
	bk_dma_set_dest_sec_attr(dma_id, DMA_ATTR_SEC);
#endif

	return BK_OK;
}

/**
 * @brief i2s dma config handle
 *
 * @param[in] i2s_num: group id of i2s
 * @param[in] type: type of i2s
 * @param[in] buff_size: transfer size of i2s dma
 * @param[in] data_handle_cb: callback function
 *
 * @return err or size
 */
static INT32_T i2s_dma_handle(TUYA_I2S_NUM_E i2s_num, i2s_txrx_type_t type, UINT32_T buff_size, i2s_data_handle_cb data_handle_cb)
{
    INT32_T ret = 0L;
    INT32_T dma_id = 0L;
    INT32_T err = 0L;
    UINT32_T *data = NULL;
	uint32_t temp_data = DMA_MAGIC;
    i2s_find_buffer((void **)&data, i2s_num);
	if (data == NULL) {
		bk_printf("malloc buff_addr fail \n");
		err = BK_FAIL;
		return err;
	}
    memset(data, 0, buff_size);
	/* init dma driver */
	ret = bk_dma_driver_init();
	if (ret != BK_OK) {
		bk_printf("dma driver init failed\r\n");
		err = BK_FAIL;
		return err;
    }
	/* allocate free DMA channel */
	dma_id = bk_dma_alloc(DMA_DEV_I2S);
	if ((dma_id < DMA_ID_0) || (dma_id >= DMA_ID_MAX)) {
		bk_printf("malloc adc dma fail \r\n");
		err = BK_FAIL;
		return err;
    }
	
	ret = i2s_dma_config(dma_id, data, buff_size, I2S_CHANNEL_1, type, data_handle_cb);
	if (ret != BK_OK) {
		bk_printf("dma config fail \r\n");
		err = BK_FAIL;
		return err;
	}
    
    /* start dma */
	if (dma_id != DMA_ID_MAX) {
		bk_dma_start(dma_id);
	}
    m_i2s_cfg[i2s_num].dma_id = dma_id;
	bk_i2s_write_data(1, &temp_data, 1);
	bk_i2s_write_data(2, &temp_data, 1);
	bk_i2s_write_data(3, &temp_data, 1);
    return 0;
}

/**
 * @brief i2s dma deconfig
 *
 * @param[in] i2s_num: group id of i2s
 *
 * @return err or size
 */
static void i2s_dma_deconfig(TUYA_I2S_NUM_E i2s_num)
{
	if (bk_dma_user(m_i2s_cfg[i2s_num].dma_id) == DMA_DEV_I2S) {
		bk_dma_stop(m_i2s_cfg[i2s_num].dma_id);
		bk_dma_deinit(m_i2s_cfg[i2s_num].dma_id);
		bk_dma_free(DMA_DEV_I2S, m_i2s_cfg[i2s_num].dma_id);
	}

	bk_dma_driver_deinit();
}

/**
 * @brief channel 0 write calback
 *
 * @param[in] size: size of buffer one frame
 *
 * @return err or size
 */
static int ch0_tx_data_handle_cb(uint32_t size)
{
    int ret = 0;
    VOID_T *data = NULL;
    if (size > DMA_FRAME_SIZE) {
        return 0;
    }
    ret = tkl_queue_fetch(m_txrx_queue[0], &data, 0);
    if (ret != BK_OK) {
        bk_printf("dma tx queue fail \r\n");
        return ret;
    }
    (void)bk_dma_stop(m_i2s_cfg[0].dma_id);
    ret = i2s_dma_config(m_i2s_cfg[0].dma_id, (UINT32_T *)data, DMA_FRAME_SIZE, I2S_CHANNEL_1, I2S_TXRX_TYPE_TX, ch0_tx_data_handle_cb);
    if (ret != BK_OK) {
        bk_printf("dma config fail \r\n");
        return ret;
    }
    /* start dma */
    (void)bk_dma_start(m_i2s_cfg[0].dma_id);

	return size;
}

/**
 * @brief channel 0 read calback
 *
 * @param[in] size: size of buffer one frame
 *
 * @return err or size
 */
static INT32_T ch0_rx_data_handle_cb(uint32_t size)
{
    VOID_T *data = NULL;
    int ret = 0;
    ret = tkl_queue_post(m_txrx_queue[0], &m_i2s_cfg[0].dma_curr_buf, 0);
    if (ret != BK_OK) {
        return ret;
    }
    i2s_find_buffer((void **)&data, 0);
    if (data != NULL)
    {
        (void)bk_dma_stop(m_i2s_cfg[0].dma_id);
        ret = i2s_dma_config(m_i2s_cfg[0].dma_id, (UINT32_T *)data, DMA_FRAME_SIZE, I2S_CHANNEL_1, I2S_TXRX_TYPE_RX, ch0_rx_data_handle_cb);
        if (ret != BK_OK) {
            bk_printf("dma config fail \r\n");
            return ret;
        }
        /* start dma */
        (void)bk_dma_start(m_i2s_cfg[0].dma_id);
    }
    else
    {   
        return -1L;
    }
	return size;
}

/**
 * @brief channel 1 write calback
 *
 * @param[in] size: size of buffer one frame
 *
 * @return err or size
 */
static int ch1_tx_data_handle_cb(uint32_t size)
{
    int ret = 0;
    VOID_T *data = NULL;
    if (size > DMA_FRAME_SIZE) {
        return 0;
    }
    ret = tkl_queue_fetch(m_txrx_queue[1], &data, 0);
    if (ret != BK_OK) {
        bk_printf("dma tx queue fail \r\n");
        return ret;
    }
    (void)bk_dma_stop(m_i2s_cfg[1].dma_id);
    ret = i2s_dma_config(m_i2s_cfg[1].dma_id, (UINT32_T *)data, DMA_FRAME_SIZE, I2S_CHANNEL_1, I2S_TXRX_TYPE_TX, ch1_tx_data_handle_cb);
    if (ret != BK_OK) {
        bk_printf("dma config fail \r\n");
        return ret;
    }
    /* start dma */
    (void)bk_dma_start(m_i2s_cfg[1].dma_id);

	return size;
}

/**
 * @brief channel 1 read calback
 *
 * @param[in] size: size of buffer one frame
 *
 * @return err or size
 */
static int ch1_rx_data_handle_cb(uint32_t size)
{
    VOID_T *data = NULL;
    int ret = 0;
    ret = tkl_queue_post(m_txrx_queue[1], &m_i2s_cfg[1].dma_curr_buf, 0);
    if (ret != BK_OK) {
        bk_printf("dma rx queue fail \r\n");
        return ret;
    }
    i2s_find_buffer((void **)&data, 1);
    if (data != NULL)
    {
        (void)bk_dma_stop(m_i2s_cfg[1].dma_id);
        ret = i2s_dma_config(m_i2s_cfg[1].dma_id, (UINT32_T *)data, DMA_FRAME_SIZE, I2S_CHANNEL_1, I2S_TXRX_TYPE_RX, ch1_rx_data_handle_cb);
        if (ret != BK_OK) {
            bk_printf("dma config fail \r\n");
            return ret;
        }
        /* start dma */
        (void)bk_dma_start(m_i2s_cfg[1].dma_id);
    }
    else
    {   
        return -1L;
    }
	return size;
}

/**
 * @brief channel 2 write calback
 *
 * @param[in] size: size of buffer one frame
 *
 * @return err or size
 */
static int ch2_tx_data_handle_cb(uint32_t size)
{
    int ret = 0;
    VOID_T *data = NULL;
    if (size > DMA_FRAME_SIZE) {
        return 0;
    }
    //发送完一帧，获取新的一帧
    ret = tkl_queue_fetch(m_txrx_queue[2], &data, 0);
    if (ret != BK_OK) {
        bk_printf("dma tx queue fail \r\n");
        return ret;
    }
    (void)bk_dma_stop(m_i2s_cfg[2].dma_id);
    ret = i2s_dma_config(m_i2s_cfg[2].dma_id, (UINT32_T *)data, DMA_FRAME_SIZE, I2S_CHANNEL_1, I2S_TXRX_TYPE_TX, ch2_tx_data_handle_cb);
    if (ret != BK_OK) {
        bk_printf("dma config fail \r\n");
        return ret;
    }
    /* start dma */
    (void)bk_dma_start(m_i2s_cfg[2].dma_id);
	return size;
}

/**
 * @brief channel 2 read calback
 *
 * @param[in] size: size of buffer one frame
 *
 * @return err or size
 */
static int ch2_rx_data_handle_cb(uint32_t size)
{
    VOID_T *data = NULL;
    int ret = 0;
    ret = tkl_queue_post(m_txrx_queue[2], &m_i2s_cfg[2].dma_curr_buf, 0);
    if (ret != BK_OK) {
        bk_printf("dma rx queue fail \r\n");
        return ret;
    }
    i2s_find_buffer((void **)&data, 2);
    if (data != NULL)
    {
        (void)bk_dma_stop(m_i2s_cfg[2].dma_id);
        ret = i2s_dma_config(m_i2s_cfg[2].dma_id, (UINT32_T *)data, DMA_FRAME_SIZE, I2S_CHANNEL_1, I2S_TXRX_TYPE_RX, ch2_rx_data_handle_cb);
        if (ret != BK_OK) {
            bk_printf("dma config fail \r\n");
            return ret;
        }
        /* start dma */
        (void)bk_dma_start(m_i2s_cfg[2].dma_id);
    }
    else
    {
        return -1L;
    }
	return size;
}

/**
 * @brief i2s init
 *
 * @param[in] i2c_pin: i2s pin number
 * @param[in] cfg: i2s configure
 *
 * @return OPRT_OK on success, others on error
 */
OPERATE_RET tkl_i2s_init(TUYA_I2S_NUM_E i2s_num, const TUYA_I2S_BASE_CFG_T *cfg)
{
    // --- BEGIN: user implements ---
    OPERATE_RET ret = 0;
    i2s_config_t i2s_config = DEFAULT_I2S_CONFIG();
    i2s_data_handle_cb i2s_cb = NULL;
    i2s_txrx_type_t type = I2S_TXRX_TYPE_MAX;

    if ((i2s_num >= TUYA_I2S_NUM_MAX) || (cfg == NULL)) {
        bk_printf("i2s port %d is invalid\n", i2s_num);
        return OPRT_INVALID_PARM;
    }

    if (cfg->mode & TUYA_I2S_MODE_SLAVE) {
        i2s_config.role = I2S_ROLE_SLAVE;
    }
    else {
        i2s_config.role = I2S_ROLE_MASTER;
    }
    switch (cfg->communication_format) {
        case I2S_COMM_FORMAT_STAND_I2S:
            i2s_config.work_mode = I2S_WORK_MODE_I2S;
            break;
        case I2S_COMM_FORMAT_STAND_MSB:
            i2s_config.work_mode = I2S_WORK_MODE_LEFTJUST;
            break;
        case I2S_COMM_FORMAT_STAND_PCM_SHORT:
            i2s_config.work_mode = I2S_WORK_MODE_SHORTFAMSYNC;
            break;
        case I2S_COMM_FORMAT_STAND_PCM_LONG:
            i2s_config.work_mode = I2S_WORK_MODE_LONGFAMSYNC;
            break;
        default:
            i2s_config.work_mode = I2S_WORK_MODE_I2S;
            break;
    }
    /*用于配置通道内数据的存储模式，I2S_LRCOM_STORE_16R16L：左右声道数据拼成一个32bit同时写入PCM_DAT中，低16bit对应左声道，高16bit对应右声道，即{R,L}->{R,L}->……（仅当数据长度小于16时才能配置此模式）。I2S_LRCOM_STORE_LRLR：左右声道数据按时间顺序交替写入PCM_DAT中，即L->R->L->R->…… 。*/
    switch (cfg->channel_format) {
        case TUYA_I2S_CHANNEL_FMT_RIGHT_LEFT:
            i2s_config.store_mode = I2S_LRCOM_STORE_16R16L;
            i2s_config.pcm_chl_num = 2;
            break;
        case TUYA_I2S_CHANNEL_FMT_ALL_RIGHT:
        case TUYA_I2S_CHANNEL_FMT_ALL_LEFT:
        case TUYA_I2S_CHANNEL_FMT_ONLY_RIGHT:
        case TUYA_I2S_CHANNEL_FMT_ONLY_LEFT:
            i2s_config.store_mode = I2S_LRCOM_STORE_LRLR;
            i2s_config.pcm_chl_num = 1;
            break;
        default:
            i2s_config.store_mode = I2S_LRCOM_STORE_16R16L;
            i2s_config.pcm_chl_num = 2;
            break;
    }
	switch (cfg->sample_rate) {
		case 1:		//8k
            i2s_config.samp_rate = I2S_SAMP_RATE_8000;
			break;

		case 2:		//11.025k
            i2s_config.samp_rate = I2S_SAMP_RATE_11025;
			break;

		case 3:		//12k
            i2s_config.samp_rate = I2S_SAMP_RATE_12000;
			break;

		case 4:		//16k
            i2s_config.samp_rate = I2S_SAMP_RATE_16000;
			break;

		case 5:		//22.05k
            i2s_config.samp_rate = I2S_SAMP_RATE_22050;
			break;

		case 6:		//24k
            i2s_config.samp_rate = I2S_SAMP_RATE_24000;
			break;

		case 7:		//32k
            i2s_config.samp_rate = I2S_SAMP_RATE_32000;
			break;

		case 8:		//44.1k
            i2s_config.samp_rate = I2S_SAMP_RATE_44100;
			break;

		case 9:		//48k
            i2s_config.samp_rate = I2S_SAMP_RATE_48000;
			break;

		default:
			break;
	}

    bk_i2s_driver_init();
    i2s_config.data_length = cfg->bits_per_sample;
    bk_i2s_init((i2s_gpio_group_id_t)i2s_num, &i2s_config);
    if (cfg->i2s_dma_flags) {
        ret = tkl_queue_create_init(&m_txrx_queue[i2s_num], sizeof(void *), BUFFER_NUM);
        if (ret != 0)
        {
            bk_i2s_deinit();
            bk_i2s_driver_deinit();
            bk_printf("tkl_queue_create_init %d\n", ret);
            return OPRT_CR_MUTEX_ERR;
        }
        ret = i2s_buffer_init(DMA_FRAME_SIZE, i2s_num);
        if ((ret != 0))
        {
            bk_i2s_deinit();
            bk_i2s_driver_deinit();
            tkl_queue_free(m_txrx_queue[i2s_num]);
            bk_printf("i2s_dma_handle failed %d\n", ret);
        }
        if (cfg->mode & TUYA_I2S_MODE_TX) {
            type = I2S_TXRX_TYPE_TX;
            i2s_cb = m_i2s_cfg[i2s_num].tx_cb;
        } else {
            type = I2S_TXRX_TYPE_RX;
            i2s_cb = m_i2s_cfg[i2s_num].rx_cb;
        }
        ret = i2s_dma_handle(i2s_num, type, DMA_FRAME_SIZE, i2s_cb);
        if ((ret != 0))
        {
            bk_i2s_deinit();
            bk_i2s_driver_deinit();
            i2s_buffer_deinit(i2s_num);
            tkl_queue_free(m_txrx_queue[i2s_num]);
            bk_printf("i2s_dma_handle failed %d\n", ret);
        }

    }
    bk_i2s_enable(I2S_ENABLE);
    memcpy(&m_i2s_config[i2s_num], cfg, sizeof(TUYA_I2S_BASE_CFG_T));
    
    return OPRT_OK;
    // --- END: user implements ---
}

/**
 * @brief tuya i2s deinit
 * 
 * @param[in] i2s_num: i2s port number
 *
 * @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
 */
OPERATE_RET tkl_i2s_deinit(TUYA_I2S_NUM_E i2s_num)
{
    // --- BEGIN: user implements ---
    VOID_T *data = NULL;
    if (i2s_num >= TUYA_I2S_NUM_MAX) {
        bk_printf("i2s port %d is invalid\n", i2s_num);
        return OPRT_INVALID_PARM;
    }

    if (m_i2s_config[i2s_num].i2s_dma_flags) {
        i2s_dma_deconfig(i2s_num);
    }
    bk_i2s_enable(I2S_DISABLE);
    bk_i2s_deinit();
    bk_i2s_driver_deinit();
    //清空缓存区数据
    while (tkl_queue_fetch(m_txrx_queue[i2s_num], &data, 0) == OPRT_OK)
    {
        ;
    }
    tkl_queue_free(m_txrx_queue[i2s_num]);
    i2s_buffer_deinit(i2s_num);
    os_memset(&m_i2s_config[i2s_num], 0, sizeof(TUYA_I2S_BASE_CFG_T));
    return OPRT_OK;
    // --- END: user implements ---
}

/**
 * @brief tuya i2s send
 * 
 * @param[in] i2s_num: i2s port number
 * @param[in] buff: i2s send buffer
 * @param[in] len: i2s send size
 *
 * @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
 */
OPERATE_RET tkl_i2s_send(TUYA_I2S_NUM_E i2s_num, VOID_T *buff, UINT32_T len)
{
    // --- BEGIN: user implements ---
    VOID_T *data = NULL;
    INT32_T ret = 0;
    uint32_t write_flag = 0U;
    if ((i2s_num >= TUYA_I2S_NUM_MAX) || ((m_i2s_config[i2s_num].i2s_dma_flags) && (len > DMA_FRAME_SIZE)) || (buff == NULL)) {
        bk_printf("i2s port %d is invalid\n", i2s_num);
        return OPRT_INVALID_PARM;
    }
    if ((m_i2s_config[i2s_num].i2s_dma_flags) && (m_i2s_config[i2s_num].mode & TUYA_I2S_MODE_TX)) {
        i2s_find_buffer((void **)&data, i2s_num);
        if (data == NULL)
        {
            return OPRT_SEND_ERR;
        }
        memcpy(data, buff, len);
        ret = tkl_queue_post(m_txrx_queue[i2s_num], &data, 0);
        if (ret != OPRT_OK)
        {
            return OPRT_SEND_ERR;
        }
    } else {
        bk_i2s_get_write_ready(&write_flag);
        if (write_flag) {
            BK_RETURN_ON_ERR(bk_i2s_write_data(i2s_num, buff, (UINT32_T)len));
        }
    }
    return OPRT_OK;
    // --- END: user implements ---
}

/**
 * @brief tuya i2s async recv
 * 
 * @param[in] i2s_num: i2s port number
 * @param[in] buff: i2s receive buffer
 * @param[in] len: i2s receive size, large than DMA_FRAME_SIZE
 *
 * @return return >= 0: number of data read; return < 0: read errror
 */
INT_T tkl_i2s_recv(TUYA_I2S_NUM_E i2s_num, VOID_T *buff, UINT32_T len)
{
    // --- BEGIN: user implements ---
    INT_T ret = 0L;
    VOID_T *data = NULL;
    uint32_t read_flag = 0U;
    if ((i2s_num >= TUYA_I2S_NUM_MAX) || ((m_i2s_config[i2s_num].i2s_dma_flags) && (len < DMA_FRAME_SIZE)) || (buff == NULL)) {
        bk_printf("i2s port %d is invalid\n", i2s_num);
        return OPRT_INVALID_PARM;
    }
    if ((m_i2s_config[i2s_num].i2s_dma_flags) && (m_i2s_config[i2s_num].mode & TUYA_I2S_MODE_RX)) {
        ret = tkl_queue_fetch(m_txrx_queue[i2s_num], &data, TKL_QUEUE_WAIT_FROEVER);
        if (ret != OPRT_OK)
        {
            return OPRT_RECV_ERR;
        }
        memcpy(buff, (UINT8_T *)data, DMA_FRAME_SIZE);
        ret = DMA_FRAME_SIZE;
    } else {
        bk_i2s_get_read_ready(&read_flag);
        if (read_flag) {
            BK_RETURN_ON_ERR(bk_i2s_read_data(buff, (UINT32_T)len));
        }
    }
    return ret;
    // --- END: user implements ---
}

/**
 * @brief tuya i2s send stop
 * 
 * @param[in] i2s_num: i2s port number
 *
 * @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
 */
OPERATE_RET tkl_i2s_send_stop(TUYA_I2S_NUM_E i2s_num)
{
    // --- BEGIN: user implements ---
    if (i2s_num >= TUYA_I2S_NUM_MAX) {
        bk_printf("i2s port %d is invalid\n", i2s_num);
        return OPRT_INVALID_PARM;
    }
    BK_RETURN_ON_ERR(bk_i2s_enable(I2S_DISABLE));
    return OPRT_OK;
    // --- END: user implements ---
}

/**
 * @brief tuya i2s recv stop
 * 
 * @param[in] i2s_num: i2s port number
 *
 * @return OPRT_OK on success. Others on error, please refer to tuya_error_code.h
 */
OPERATE_RET tkl_i2s_recv_stop(TUYA_I2S_NUM_E i2s_num)
{
    // --- BEGIN: user implements ---
    if (i2s_num >= TUYA_I2S_NUM_MAX) {
        bk_printf("i2s port %d is invalid\n", i2s_num);
        return OPRT_INVALID_PARM;
    }
    BK_RETURN_ON_ERR(bk_i2s_enable(I2S_DISABLE));
    return OPRT_OK;
    // --- END: user implements ---
}
