From 058db6b994df4a45928be0cb40d8d7cc792a8ba4 Mon Sep 17 00:00:00 2001 From: lhenry-realtek <176550561+lhenry-realtek@users.noreply.github.com> Date: Wed, 27 Nov 2024 11:59:50 +0800 Subject: [PATCH 1/8] os/board/rtl8730e: add driver layer implemenation for i2s tdm Add initial porting/os layer commit - TDM parameters fixed on I2S2 to generate required signal for AIS25BA - DMA is not tested and likely requires further implementation Config: - RTL8730E Peripheral Support: [ ] AMEBASMART_I2C [*] Amebasmart I2S [*] I2S_2 [*] Enable I2S RX [*] Enable I2S TDM Support - Device Drivers: [ ] I2C Driver Support [*] I2S Driver Support - Application Configuration > Examples: [*] I2S character driver test [*] Use I2S receiver [ ] Use I2S transmitter /dev/i2schar4 I2S character device path --- os/arch/arm/src/amebasmart/amebasmart_i2s.c | 274 +++++++++++++++++- .../mbed/targets/hal/rtl8730e/i2s_api.c | 158 +++++++--- os/board/rtl8730e/src/rtl8730e_i2schar.c | 13 +- 3 files changed, 408 insertions(+), 37 deletions(-) diff --git a/os/arch/arm/src/amebasmart/amebasmart_i2s.c b/os/arch/arm/src/amebasmart/amebasmart_i2s.c index d1a495b33c..a670612d64 100644 --- a/os/arch/arm/src/amebasmart/amebasmart_i2s.c +++ b/os/arch/arm/src/amebasmart/amebasmart_i2s.c @@ -168,6 +168,9 @@ struct amebasmart_i2s_config_s { uint8_t i2s_idx; /* I2S index*/ uint8_t rxenab : 1; /* True: RX transfers enabled */ uint8_t txenab : 1; /* True: TX transfers enabled */ +#if defined(I2S_HAVE_TDM) && (0 < I2S_HAVE_TDM) + uint8_t tdmenab: 1; /* True: TDM is enabled */ +#endif }; /* The state of the one I2S peripheral */ @@ -180,12 +183,20 @@ struct amebasmart_i2s_s { #if defined(I2S_HAVE_TX) && (0 < I2S_HAVE_TX) uint8_t i2s_tx_buf[(I2S_DMA_PAGE_NUM + 1) * I2S_DMA_PAGE_SIZE]__attribute__((aligned(64))); /* Allocate buffer for use in TX, must I2S_DMA_PAGE_NUM+1 for zero buffer */ i2s_irq_handler tx_isr_handler; +#if defined(I2S_HAVE_TDM) && (0 < I2S_HAVE_TDM) + uint8_t i2s_tx_buf_ext[(I2S_DMA_PAGE_NUM + 1) * I2S_DMA_PAGE_SIZE]__attribute__((aligned(64))); /* Allocate buffer for use in TX, must I2S_DMA_PAGE_NUM+1 for zero buffer */ + i2s_irq_handler tx_isr_handler_ext; +#endif #endif #if defined(I2S_HAVE_RX) && (0 < I2S_HAVE_RX) //uint8_t i2s_rx_buf[(I2S_DMA_PAGE_NUM + 1) * I2S_DMA_PAGE_SIZE]__attribute__((aligned(64))); /* Allocate buffer for use in RX, must I2S_DMA_PAGE_NUM+1 for zero buffer */ uint8_t* i2s_rx_buf; i2s_irq_handler rx_isr_handler; +#if defined(I2S_HAVE_TDM) && (0 < I2S_HAVE_TDM) + uint8_t* i2s_rx_buf_ext; + i2s_irq_handler rx_isr_handler_ext; +#endif #endif i2s_err_cb_t err_cb; /* registered error callback function */ @@ -195,6 +206,9 @@ struct amebasmart_i2s_s { uint8_t rxenab : 1; /* True: RX transfers enabled */ uint8_t txenab : 1; /* True: TX transfers enabled */ +#if defined(I2S_HAVE_TDM) && (0 < I2S_HAVE_TDM) + uint8_t tdmenab : 1; /* True: TDM Enabled (this will disable MULTIIO)*/ +#endif int sample_rate; /*!< I2S sample rate */ int channel_num; /*!< Number of channels */ @@ -227,6 +241,9 @@ static const struct amebasmart_i2s_config_s amebasmart_i2s2_config = { .i2s_idx = I2S_NUM_2, .rxenab = 0, .txenab = 1, +#if defined(I2S_HAVE_TDM) && (0 < I2S_HAVE_TDM) + .tdmenab = 0, +#endif }; static const struct amebasmart_i2s_config_s amebasmart_i2s3_config = { @@ -239,8 +256,27 @@ static const struct amebasmart_i2s_config_s amebasmart_i2s3_config = { .i2s_idx = I2S_NUM_3, .rxenab = 1, .txenab = 0, +#if defined(I2S_HAVE_TDM) && (0 < I2S_HAVE_TDM) + .tdmenab = 0, +#endif }; +#if defined(I2S_HAVE_TDM) && (0 < I2S_HAVE_TDM) +/* currently support I2S2 */ +static const struct amebasmart_i2s_config_s amebasmart_i2s2_tdm_config = { + .i2s_mclk_pin = PB_22, + .i2s_sclk_pin = PB_21, + .i2s_ws_pin = PA_16, + .i2s_sd_tx_pin = PB_10, + .i2s_sd_rx_pin = PB_19, + + .i2s_idx = I2S_NUM_2, + .rxenab = 1, + .txenab = 0, + .tdmenab = 1, +}; +#endif + /**************************************************************************** * Private Function Prototypes ****************************************************************************/ @@ -340,6 +376,28 @@ static const struct i2s_ops_s g_i2sops = { .i2s_err_cb_register = i2s_err_cb_register, }; +#if defined(I2S_HAVE_TDM) && (0 < I2S_HAVE_TDM) +/* I2S device operations */ +static const struct i2s_ops_s g_i2stdm_ops = { + /* Receiver methods */ + + .i2s_rxsamplerate = i2s_samplerate, + .i2s_rxdatawidth = i2s_rxdatawidth, + .i2s_receive = i2s_receive, + + /* Transmitter methods */ + + .i2s_txsamplerate = i2s_samplerate, + .i2s_txdatawidth = i2s_txdatawidth, + .i2s_send = i2s_send, + + .i2s_stop = i2s_stop, + .i2s_pause = i2s_pause, + .i2s_resume = i2s_resume, + .i2s_err_cb_register = i2s_err_cb_register, +}; +#endif + static struct amebasmart_i2s_s *g_i2sdevice[I2S_NUM_MAX] = {NULL}; /**************************************************************************** @@ -1413,13 +1471,29 @@ static int i2s_pause(struct i2s_dev_s *dev, i2s_ch_dir_t dir) #if defined(I2S_HAVE_TX) && (0 < I2S_HAVE_TX) if (dir == I2S_TX && priv->txenab) { +#if defined(I2S_HAVE_TDM) && (0 < I2S_HAVE_TDM) + if (priv->tdmenab) { + ameba_i2s_tdm_pause(priv->i2s_object); + } else { + ameba_i2s_pause(priv->i2s_object); + } +#else ameba_i2s_pause(priv->i2s_object); +#endif } #endif #if defined(I2S_HAVE_RX) && (0 < I2S_HAVE_RX) if (dir == I2S_RX && priv->rxenab) { +#if defined(I2S_HAVE_TDM) && (0 < I2S_HAVE_TDM) + if (priv->tdmenab) { + ameba_i2s_tdm_pause(priv->i2s_object); + } else { + ameba_i2s_pause(priv->i2s_object); + } +#else ameba_i2s_pause(priv->i2s_object); +#endif } #endif @@ -1446,15 +1520,35 @@ static int i2s_resume(struct i2s_dev_s *dev, i2s_ch_dir_t dir) DEBUGASSERT(priv); #if defined(I2S_HAVE_TX) && (0 < I2S_HAVE_TX) +#if defined(I2S_HAVE_TDM) && (0 < I2S_HAVE_TDM) if (dir == I2S_TX && priv->txenab) { + if (priv->tdmenab) { + ameba_i2s_tdm_resume(priv->i2s_object); + } else { + ameba_i2s_resume(priv->i2s_object); + } + } +#else + if (dir == I2S_TX) { ameba_i2s_resume(priv->i2s_object); } #endif +#endif #if defined(I2S_HAVE_RX) && (0 < I2S_HAVE_RX) +#if defined(I2S_HAVE_TDM) && (0 < I2S_HAVE_TDM) if (dir == I2S_RX && priv->rxenab) { + if (priv->tdmenab) { + ameba_i2s_tdm_resume(priv->i2s_object); + } else { + ameba_i2s_resume(priv->i2s_object); + } + } +#else + if (dir == I2S_RX) { ameba_i2s_resume(priv->i2s_object); } +#endif #endif return OK; @@ -1480,15 +1574,31 @@ static int i2s_stop_transfer(struct i2s_dev_s *dev, i2s_ch_dir_t dir) DEBUGASSERT(priv); #if defined(I2S_HAVE_TX) && (0 < I2S_HAVE_TX) +#if defined(I2S_HAVE_TDM) && (0 < I2S_HAVE_TDM) + if (dir == I2S_TX && priv->tdmenab) { + ameba_i2s_tdm_pause(priv->i2s_object); + } else { + ameba_i2s_pause(priv->i2s_object); + } +#else if (dir == I2S_TX) { ameba_i2s_pause(priv->i2s_object); } #endif +#endif #if defined(I2S_HAVE_RX) && (0 < I2S_HAVE_RX) +#if defined(I2S_HAVE_TDM) && (0 < I2S_HAVE_TDM) + if (dir == I2S_RX && priv->tdmenab) { + ameba_i2s_tdm_pause(priv->i2s_object); + } else { + ameba_i2s_pause(priv->i2s_object); + } +#else if (dir == I2S_RX) { ameba_i2s_pause(priv->i2s_object); } +#endif #endif return OK; @@ -1619,6 +1729,20 @@ int amebasmart_i2s_isr_initialize(struct amebasmart_i2s_s *priv) return 0; } +#if defined(I2S_HAVE_TDM) && (0 < I2S_HAVE_TDM) +int amebasmart_i2s_tdm_isr_initialize(struct amebasmart_i2s_s *priv) +{ + /* Attach the GPIO peripheral to the allocated CPU interrupt */ +#if defined(I2S_HAVE_TX) && (0 < I2S_HAVE_TX) + i2s_tdm_tx_irq_handler(priv->i2s_object, (i2s_irq_handler)priv->tx_isr_handler, (uint32_t)priv); +#endif +#if defined(I2S_HAVE_RX) && (0 < I2S_HAVE_RX) + i2s_tdm_rx_irq_handler(priv->i2s_object, (i2s_irq_handler)priv->rx_isr_handler, (uint32_t)priv); +#endif + return 0; +} +#endif + /**************************************************************************** * Name: i2s_samplerate * @@ -1713,6 +1837,33 @@ static void i2s_getdefaultconfig(struct amebasmart_i2s_s *priv) } priv->i2s_object->mode = MULTIIO; + + /* modify configurations if TDM is enabled */ +#if defined(I2S_HAVE_TDM) && (0 < I2S_HAVE_TDM) + if (priv->config->tdmenab) { + dbg("init for tdm: %d \n", priv->config->tdmenab); + /* TDM configuration fixed for ais25ba */ + priv->i2s_object->mode = TDM; + priv->i2s_object->role = MASTER; + priv->i2s_object->direction = SP_DIR_RX; + + /* sample rate */ + priv->i2s_object->sampling_rate = SP_16K; + priv->sample_rate = SP_16K; + + /* num channels */ + priv->i2s_object->channel_num = SP_CH_STEREO; + priv->channel_num = SP_CH_STEREO; + + /* word and channel length */ + priv->bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT; + priv->i2s_object->word_length = WL_16b; // SP_RXWL_16; + priv->i2s_object->channel_length = SP_CL_16; + + /* FIFO size */ + priv->i2s_object->fifo_num = SP_RX_FIFO8; // for 8ch tdm + } +#endif } /**************************************************************************** @@ -1768,7 +1919,7 @@ static int i2s_allocate_wd(struct amebasmart_i2s_s *priv) ****************************************************************************/ /**************************************************************************** - * Name: amebasmart_i2s_initialize + * Name: amebasmart_i2s_tdm_initialize * * Description: * Initialize the selected i2s port @@ -1879,6 +2030,124 @@ struct i2s_dev_s *amebasmart_i2s_initialize(uint16_t port, bool is_reinit) kmm_free(priv); return NULL; } + +#if defined(I2S_HAVE_TDM) && (0 < I2S_HAVE_TDM) +struct i2s_dev_s *amebasmart_i2s_tdm_initialize(uint16_t port, bool is_reinit) +{ + struct amebasmart_i2s_config_s *hw_config_s = NULL; + struct amebasmart_i2s_s *priv; + int ret; + + /* Assign HW configuration */ + if (port == I2S_NUM_2) { + hw_config_s = (struct amebasmart_i2s_config_s *)&amebasmart_i2s2_tdm_config; // TDM config on I2S2 + } else { + i2serr("Please select I2S2 bus for TDM\n"); + return NULL; + } + + if (!is_reinit) { + if (g_i2sdevice[port] != NULL) { + return &g_i2sdevice[port]->dev; + } + + /* Allocate a new state structure for this chip select. NOTE that there + * is no protection if the same chip select is used in two different + * chip select structures. + */ + priv = (struct amebasmart_i2s_s *)kmm_zalloc(sizeof(struct amebasmart_i2s_s)); + if (!priv) { + i2serr("ERROR: Failed to allocate a chip select structure\n"); + return NULL; + } + } else { + /* The I2S structure should exist after system wakeup */ + priv = g_i2sdevice[port]; + DEBUGASSERT(priv); + } + + priv->i2s_object = (i2s_t *)kmm_zalloc(sizeof(i2s_t)); + DEBUGASSERT(priv->i2s_object); + /* Config values initialization */ + priv->config = hw_config_s; /* Get HW configuation */ + + /* Get default configuration */ + i2s_getdefaultconfig(priv); + + /* Initialize buffering */ +#if defined(I2S_HAVE_RX) && (0 < I2S_HAVE_RX) + i2s_buf_rx_initialize(priv); +#endif + +#if defined(I2S_HAVE_TX) && (0 < I2S_HAVE_TX) + i2s_buf_tx_initialize(priv); +#endif + printf("tdmenab: %d \n", priv->config->tdmenab); + printf("ChLen: %d \n", priv->i2s_object->channel_length); + printf("WLen: %d \n", priv->i2s_object->word_length); + printf("ChNum: %d \n", priv->i2s_object->channel_num); + printf("SR: %d \n", priv->i2s_object->sampling_rate); + printf("FIFOnum: %d \n", priv->i2s_object->fifo_num); + + //Init_Params.chn_cnt = (sp_obj.tdmmode + 1) * 2; + /* I2s object initialization */ + i2s_tdm_init(priv->i2s_object, hw_config_s->i2s_sclk_pin, hw_config_s->i2s_ws_pin, hw_config_s->i2s_sd_tx_pin, hw_config_s->i2s_sd_rx_pin, hw_config_s->i2s_mclk_pin); + + /* Initialize buffering */ +#if defined(I2S_HAVE_TX) && (0 < I2S_HAVE_TX) + i2s_set_dma_buffer(priv->i2s_object, (char *)priv->i2s_tx_buf, NULL, I2S_DMA_PAGE_NUM, I2S_DMA_PAGE_SIZE); /* Allocate DMA Buffer for TX */ +#endif + + /* configures IRQ */ +#if defined(I2S_HAVE_RX) && (0 < I2S_HAVE_RX) + priv->rx_isr_handler = (i2s_irq_handler)&i2s_transfer_rx_handleirq; + +#if defined(I2S_HAVE_TDM) && (0 < I2S_HAVE_TDM) + priv->rx_isr_handler_ext = (i2s_irq_handler)&i2s_transfer_rx_handleirq; +#endif + +#endif + +#if defined(I2S_HAVE_TX) && (0 < I2S_HAVE_TX) + priv->tx_isr_handler = (i2s_irq_handler)&i2s_transfer_tx_handleirq; + +#if defined(I2S_HAVE_TDM) && (0 < I2S_HAVE_TDM) + priv->tx_isr_handler_ext = (i2s_irq_handler)&i2s_transfer_tx_handleirq; +#endif + +#endif + + //ret = amebasmart_i2s_isr_initialize(priv); + ret = amebasmart_i2s_tdm_isr_initialize(priv); // TDM + if (ret != OK) { + i2serr("I2S initialize (TDM): isr fails\n"); + goto errout_with_alloc; + } + /* Initialize the I2S priv device structure */ + sem_init(&priv->exclsem, 0, 1); + priv->dev.ops = &g_i2stdm_ops; // TDM + + ret = i2s_allocate_wd(priv); + if (ret != OK) { + goto errout_with_alloc; + } + /* Basic settings */ + //priv->i2s_num = priv->i2s_object->i2s_idx; + if (!is_reinit) { + g_i2sdevice[port] = priv; + } + + /* Success exit */ + return &priv->dev; + + /* Failure exits */ +errout_with_alloc: + sem_destroy(&priv->exclsem); + kmm_free(priv); + return NULL; +} +#endif + #endif /* I2S_HAVE_RX || I2S_HAVE_TX */ #endif /* CONFIG_AUDIO */ @@ -1891,7 +2160,7 @@ static void amebasmart_i2s_suspend(uint16_t port) kmm_free(priv->i2s_object); priv->i2s_object = NULL; sem_destroy(&priv->exclsem); - sem_destroy(&priv->bufsem_tx); + #if defined(I2S_HAVE_RX) && (0 < I2S_HAVE_RX) if (priv->rx.dog) { wd_delete(priv->rx.dog); @@ -1899,6 +2168,7 @@ static void amebasmart_i2s_suspend(uint16_t port) } #endif #if defined(I2S_HAVE_TX) && (0 < I2S_HAVE_TX) + sem_destroy(&priv->bufsem_tx); if (priv->tx.dog) { wd_delete(priv->tx.dog); priv->tx.dog = NULL; diff --git a/os/board/rtl8730e/src/component/mbed/targets/hal/rtl8730e/i2s_api.c b/os/board/rtl8730e/src/component/mbed/targets/hal/rtl8730e/i2s_api.c index 11c027b24e..13bcd2733e 100755 --- a/os/board/rtl8730e/src/component/mbed/targets/hal/rtl8730e/i2s_api.c +++ b/os/board/rtl8730e/src/component/mbed/targets/hal/rtl8730e/i2s_api.c @@ -383,10 +383,17 @@ static uint32_t i2s_clock_select(i2s_t *obj) uint32_t clock_mode = 0; AUDIO_ClockParams Clock_Params; AUDIO_InitParams Init_Params; - +#if defined(CONFIG_AMEBASMART_I2S_TDM) + Init_Params.chn_len = obj->channel_length; + Init_Params.chn_cnt = (obj->fifo_num + 1) * 2; +#else Init_Params.chn_len = SP_CL_32; Init_Params.chn_cnt = obj->channel_num; - Init_Params.sr = obj->sampling_rate;; +#endif + + + Init_Params.sr = obj->sampling_rate; + dbg("ChLen: %d ChCnt: %d Sr: %d\n", obj->channel_length, obj->channel_num, obj->sampling_rate); Init_Params.codec_multiplier_with_rate = 256; Init_Params.sport_mclk_fixed_max = (uint32_t) NULL; Audio_Clock_Choose(PLL_CLK, &Init_Params, &Clock_Params); @@ -415,6 +422,7 @@ static uint32_t i2s_clock_select(i2s_t *obj) clock_mode = PLL_CLOCK_98P304M / Clock_Params.PLL_DIV; break; } + dbg("CLK: %d mode: %f div: %d\n", Clock_Params.Clock, clock_mode, Clock_Params.PLL_DIV); return clock_mode; } @@ -608,6 +616,13 @@ void i2s_recv_page(i2s_t *obj) */ void i2s_enable(i2s_t *obj) { +#if defined(CONFIG_AMEBASMART_I2S_TDM) + // turn on MCLK if master + if (obj->role == MASTER) { + AUDIO_SP_SetMclk(obj->i2s_idx, ENABLE); + } +#endif + AUDIO_SP_DmaCmd(obj->i2s_idx, ENABLE); if (obj->direction == I2S_DIR_TX) { AUDIO_SP_TXStart(obj->i2s_idx, ENABLE); @@ -625,20 +640,40 @@ void i2s_disable(i2s_t *obj, bool is_suspend) { SP_GDMA_STRUCT *l_SPGdmaStruct = &SPGdmaStruct; +#if defined(CONFIG_AMEBASMART_I2S_TDM) + SP_GDMA_STRUCT_EXT *l_SPGdmaStructExt = &SPGdmaStructExt; + // turn off MCLK if master + if (obj->role == MASTER) { + AUDIO_SP_SetMclk(obj->i2s_idx, DISABLE); + } +#endif + if (obj->direction == I2S_DIR_TX) { GDMA_ClearINT(l_SPGdmaStruct->SpTxGdmaInitStruct.GDMA_Index, l_SPGdmaStruct->SpTxGdmaInitStruct.GDMA_ChNum); GDMA_Abort(l_SPGdmaStruct->SpTxGdmaInitStruct.GDMA_Index, l_SPGdmaStruct->SpTxGdmaInitStruct.GDMA_ChNum); GDMA_ChnlFree(l_SPGdmaStruct->SpTxGdmaInitStruct.GDMA_Index, l_SPGdmaStruct->SpTxGdmaInitStruct.GDMA_ChNum); + +#if defined(CONFIG_AMEBASMART_I2S_TDM) && defined(CONFIG_AMEBASMART_I2S_TX) + GDMA_ClearINT(l_SPGdmaStructExt->SpTxGdmaInitStructExt.GDMA_Index, l_SPGdmaStructExt->SpTxGdmaInitStructExt.GDMA_ChNum); + GDMA_Abort(l_SPGdmaStructExt->SpTxGdmaInitStructExt.GDMA_Index, l_SPGdmaStructExt->SpTxGdmaInitStructExt.GDMA_ChNum); + GDMA_ChnlFree(l_SPGdmaStructExt->SpTxGdmaInitStructExt.GDMA_Index, l_SPGdmaStructExt->SpTxGdmaInitStructExt.GDMA_ChNum); +#endif + AUDIO_SP_DmaCmd(obj->i2s_idx, DISABLE); AUDIO_SP_TXStart(obj->i2s_idx, DISABLE); if (is_suspend) { AUDIO_SP_Deinit(obj->i2s_idx, obj->direction); } } else { - GDMA_ClearINT(l_SPGdmaStruct->SpRxGdmaInitStruct.GDMA_Index, l_SPGdmaStruct->SpRxGdmaInitStruct.GDMA_ChNum); GDMA_Cmd(l_SPGdmaStruct->SpRxGdmaInitStruct.GDMA_Index, l_SPGdmaStruct->SpRxGdmaInitStruct.GDMA_ChNum, DISABLE); +#if defined(CONFIG_AMEBASMART_I2S_TDM) && defined(CONFIG_AMEBASMART_I2S_RX) + GDMA_ClearINT(l_SPGdmaStructExt->SpRxGdmaInitStructExt.GDMA_Index, l_SPGdmaStructExt->SpRxGdmaInitStructExt.GDMA_ChNum); + GDMA_Abort(l_SPGdmaStructExt->SpRxGdmaInitStructExt.GDMA_Index, l_SPGdmaStructExt->SpRxGdmaInitStructExt.GDMA_ChNum); + GDMA_ChnlFree(l_SPGdmaStructExt->SpRxGdmaInitStructExt.GDMA_Index, l_SPGdmaStructExt->SpRxGdmaInitStructExt.GDMA_ChNum); +#endif + AUDIO_SP_DmaCmd(obj->i2s_idx, DISABLE); AUDIO_SP_RXStart(obj->i2s_idx, DISABLE); } @@ -661,6 +696,31 @@ void ameba_i2s_pause(i2s_t *obj) { } } +#ifdef CONFIG_AMEBASMART_I2S_TDM +void ameba_i2s_tdm_pause(i2s_t *obj) { + + SP_GDMA_STRUCT *l_SPGdmaStruct = &SPGdmaStruct; + SP_GDMA_STRUCT_EXT *l_SPGdmaStructExt = &SPGdmaStructExt; + +#if defined(CONFIG_AMEBASMART_I2S_TDM) + // turn on MCLK if master + if (obj->role == MASTER) { + AUDIO_SP_SetMclk(obj->i2s_idx, DISABLE); + } +#endif + + if (obj->direction == I2S_DIR_TX) { + GDMA_ClearINT(l_SPGdmaStruct->SpTxGdmaInitStruct.GDMA_Index, l_SPGdmaStruct->SpTxGdmaInitStruct.GDMA_ChNum); + GDMA_ClearINT(l_SPGdmaStructExt->SpTxGdmaInitStructExt.GDMA_Index, l_SPGdmaStructExt->SpTxGdmaInitStructExt.GDMA_ChNum); + AUDIO_SP_DmaCmd(obj->i2s_idx, DISABLE); + AUDIO_SP_TXStart(obj->i2s_idx, DISABLE); + } else { + GDMA_Suspend(l_SPGdmaStruct->SpRxGdmaInitStruct.GDMA_Index, l_SPGdmaStruct->SpRxGdmaInitStruct.GDMA_ChNum); + GDMA_Suspend(l_SPGdmaStructExt->SpRxGdmaInitStructExt.GDMA_Index, l_SPGdmaStructExt->SpRxGdmaInitStructExt.GDMA_ChNum); + } +} +#endif + /** * @brief Resume I2S interrupt and function. * @param obj: I2S object defined in application software. @@ -677,7 +737,29 @@ void ameba_i2s_resume(i2s_t *obj) { } } -#if defined(CONFIG_AMEBASMART_I2S_TDM) +#ifdef CONFIG_AMEBASMART_I2S_TDM +void ameba_i2s_tdm_resume(i2s_t *obj) { + + SP_GDMA_STRUCT *l_SPGdmaStruct = &SPGdmaStruct; + SP_GDMA_STRUCT_EXT *l_SPGdmaStructExt = &SPGdmaStructExt; + + // turn on MCLK if master + if (obj->role == MASTER) { + AUDIO_SP_SetMclk(obj->i2s_idx, ENABLE); + } + + if (obj->direction == I2S_DIR_TX) { + AUDIO_SP_DmaCmd(obj->i2s_idx, ENABLE); + AUDIO_SP_TXStart(obj->i2s_idx, ENABLE); + } else { + GDMA_Resume(l_SPGdmaStruct->SpRxGdmaInitStruct.GDMA_Index, l_SPGdmaStruct->SpRxGdmaInitStruct.GDMA_ChNum); + GDMA_Resume(l_SPGdmaStructExt->SpRxGdmaInitStructExt.GDMA_Index, l_SPGdmaStructExt->SpRxGdmaInitStructExt.GDMA_ChNum); + } +} +#endif + +#if defined(CONFIG_AMEBASMART_I2S_TDM) +#if defined(CONFIG_AMEBASMART_I2S_TX) && (CONFIG_AMEBASMART_I2S_TX == 1) static void i2s_tdm_tx_isr(void *sp_data) { u32 *pbuf; @@ -712,6 +794,36 @@ static void i2s_tdm_tx_isr_ext(void *sp_data) I2SUserCB.TxCCB(I2SUserCB.TxCBId, (char*)pbuf); } +/** + * @brief Register TX interrupt handler. + * @param obj: I2S object defined in application software. + * @param handler: TX interrupt callback function. + * @param id: TX interrupt callback parameter. + * @retval none + */ +void i2s_tdm_tx_irq_handler(i2s_t *obj, i2s_irq_handler handler, uint32_t id) +{ + assert_param(IS_SP_SEL_I2S(obj->i2s_idx)); + + uint8_t i2s_index = obj->i2s_idx; + SP_GDMA_STRUCT *sp_str = &SPGdmaStruct; + SP_GDMA_STRUCT_EXT *sp_str_ext = &SPGdmaStructExt; + u32 *pbuf, pbuf_1; + + sp_str->i2s_idx = i2s_index; /* Store I2S index */ + sp_str_ext->i2s_idx = i2s_index; + + I2SUserCB.TxCCB = handler; + I2SUserCB.TxCBId = id; + + pbuf = (u32 *)i2s_get_ready_tx_page(i2s_index); + pbuf_1 = (u32 *)i2s_get_ready_tx_page(i2s_index); + AUDIO_SP_TXGDMA_Init(i2s_index, GDMA_INT, &sp_str->SpTxGdmaInitStruct, sp_str, (IRQ_FUN)i2s_tdm_tx_isr, (u8 *)pbuf, sp_tx_info.tx_page_size); + AUDIO_SP_TXGDMA_Init(i2s_index, GDMA_EXT, &sp_str_ext->SpTxGdmaInitStructExt, sp_str_ext, (IRQ_FUN)i2s_tdm_tx_isr_ext, (u8 *)pbuf_1, sp_tx_info.tx_page_size); +} +#endif + +#if defined(CONFIG_AMEBASMART_I2S_RX) && (CONFIG_AMEBASMART_I2S_RX == 1) static void i2s_tdm_rx_isr(void *sp_data) { SP_GDMA_STRUCT *gs = sp_data; @@ -750,34 +862,6 @@ static void i2s_tdm_rx_isr_ext(void *sp_data) i2s_get_free_rx_page(i2s_index); } -/** - * @brief Register TX interrupt handler. - * @param obj: I2S object defined in application software. - * @param handler: TX interrupt callback function. - * @param id: TX interrupt callback parameter. - * @retval none - */ -void i2s_tdm_tx_irq_handler(i2s_t *obj, i2s_irq_handler handler, uint32_t id) -{ - assert_param(IS_SP_SEL_I2S(obj->i2s_idx)); - - uint8_t i2s_index = obj->i2s_idx; - SP_GDMA_STRUCT *sp_str = &SPGdmaStruct; - SP_GDMA_STRUCT_EXT *sp_str_ext = &SPGdmaStructExt; - u32 *pbuf, pbuf_1; - - sp_str->i2s_idx = i2s_index; /* Store I2S index */ - sp_str_ext->i2s_idx = i2s_index; - - I2SUserCB.TxCCB = handler; - I2SUserCB.TxCBId = id; - - pbuf = (u32 *)i2s_get_ready_tx_page(i2s_index); - pbuf_1 = (u32 *)i2s_get_ready_tx_page(i2s_index); - AUDIO_SP_TXGDMA_Init(i2s_index, GDMA_INT, &sp_str->SpTxGdmaInitStruct, sp_str, (IRQ_FUN)i2s_tdm_tx_isr, (u8 *)pbuf, sp_tx_info.tx_page_size); - AUDIO_SP_TXGDMA_Init(i2s_index, GDMA_EXT, &sp_str_ext->SpTxGdmaInitStructExt, sp_str_ext, (IRQ_FUN)i2s_tdm_tx_isr_ext, (u8 *)pbuf_1, sp_tx_info.tx_page_size); -} - /** * @brief Register RX interrupt handler. * @param obj: I2S object defined in application software. @@ -806,6 +890,8 @@ void i2s_tdm_rx_irq_handler(i2s_t *obj, i2s_irq_handler handler, uint32_t id) AUDIO_SP_RXGDMA_Init(i2s_index, GDMA_EXT, &sp_str_ext->SpRxGdmaInitStructExt, sp_str_ext, (IRQ_FUN)i2s_tdm_rx_isr_ext, (u8 *)pbuf_1, sp_rx_info.rx_page_size); } +#endif + /** * @brief Enable I2S interrupt and function. * @param obj: I2S object defined in application software. @@ -915,12 +1001,14 @@ void i2s_tdm_set_param(i2s_t *obj, int channel_num, int rate, int word_len) SP_InitStruct.SP_SelClk = clock_mode; AUDIO_SP_Init(obj->i2s_idx, obj->direction, &SP_InitStruct); + dbg("SP_BClk: %ld Hz\n", SP_InitStruct.SP_Bclk); + AUDIO_SP_SetMasterSlave(obj->i2s_idx, obj->role); if (obj->role == MASTER) { - AUDIO_SP_SetMclk(obj->i2s_idx, ENABLE); + //AUDIO_SP_SetMclk(obj->i2s_idx, ENABLE); AUDIO_SP_SetMclkDiv(obj->i2s_idx, 0); } else { - AUDIO_SP_SetMclk(obj->i2s_idx, DISABLE); + //AUDIO_SP_SetMclk(obj->i2s_idx, DISABLE); } } @@ -980,6 +1068,8 @@ void i2s_tdm_init(i2s_t *obj, PinName sck, PinName ws, PinName sd_tx, PinName sd RCC_PeriphClockCmd(APBPeriph_SPORT3, APBPeriph_SPORT3_CLOCK, ENABLE); } + RCC_PeriphClockCmd(APBPeriph_AUDIO, APBPeriph_CLOCK_NULL, ENABLE); + Pinmux_Config(mck, pin_func); Pinmux_Config(sck, pin_func); Pinmux_Config(ws, pin_func); diff --git a/os/board/rtl8730e/src/rtl8730e_i2schar.c b/os/board/rtl8730e/src/rtl8730e_i2schar.c index c570d4ce46..7a83b3811e 100644 --- a/os/board/rtl8730e/src/rtl8730e_i2schar.c +++ b/os/board/rtl8730e/src/rtl8730e_i2schar.c @@ -42,6 +42,10 @@ #ifndef CONFIG_AMEBASMART_I2SCHAR_MINOR_3 #define CONFIG_AMEBASMART_I2SCHAR_MINOR_3 3 #endif + +#ifndef CONFIG_AMEBASMART_I2SCHAR_MINOR_4 +#define CONFIG_AMEBASMART_I2SCHAR_MINOR_4 4 +#endif /***************************************************************************** * Private Functions ****************************************************************************/ @@ -74,15 +78,22 @@ int i2schar_devinit(void) #if defined(CONFIG_AMEBASMART_I2S2) && !defined(CONFIG_AUDIO_ALC1019) /* Call amebasmart_i2s_initialize() to get an instance of the I2S interface */ /* Initialise I2S2 */ +#if defined(CONFIG_AMEBASMART_I2S_TDM) + i2s = amebasmart_i2s_tdm_initialize(I2S_NUM_2, I2S_INIT); +#else i2s = amebasmart_i2s_initialize(I2S_NUM_2, I2S_INIT); - +#endif if (!i2s) { lldbg("ERROR: Failed to get the amebasmart I2S driver\n"); return -ENODEV; } /* Register the I2S character driver at "/dev/i2schar2" */ +#if defined(CONFIG_AMEBASMART_I2S_TDM) + ret = i2schar_register(i2s, CONFIG_AMEBASMART_I2SCHAR_MINOR_4); +#else ret = i2schar_register(i2s, CONFIG_AMEBASMART_I2SCHAR_MINOR_2); +#endif if (ret < 0) { lldbg("ERROR: i2schar_register failed: %d\n", ret); return ret; From 110a416e567ae927e2f26767eac8541e0780ef97 Mon Sep 17 00:00:00 2001 From: lhenry-realtek <176550561+lhenry-realtek@users.noreply.github.com> Date: Wed, 27 Nov 2024 14:39:59 +0800 Subject: [PATCH 2/8] os/board/rtl8730e: fix clock configuration for signal generation Fix clock selection parameters to get targeted signal value: MCLK = 12.288MHz (OK) BCLK = 2.048MHz (OK) WCLK (need to setup Rx api in order to trigger WCLK activity) --- .../src/component/mbed/targets/hal/rtl8730e/i2s_api.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/os/board/rtl8730e/src/component/mbed/targets/hal/rtl8730e/i2s_api.c b/os/board/rtl8730e/src/component/mbed/targets/hal/rtl8730e/i2s_api.c index 13bcd2733e..3cad07e5fd 100755 --- a/os/board/rtl8730e/src/component/mbed/targets/hal/rtl8730e/i2s_api.c +++ b/os/board/rtl8730e/src/component/mbed/targets/hal/rtl8730e/i2s_api.c @@ -394,8 +394,13 @@ static uint32_t i2s_clock_select(i2s_t *obj) Init_Params.sr = obj->sampling_rate; dbg("ChLen: %d ChCnt: %d Sr: %d\n", obj->channel_length, obj->channel_num, obj->sampling_rate); +#if defined(CONFIG_AMEBASMART_I2S_TDM) + Init_Params.codec_multiplier_with_rate = 0; + Init_Params.sport_mclk_fixed_max = (uint32_t) 12288000; //12.288MHz +#else Init_Params.codec_multiplier_with_rate = 256; Init_Params.sport_mclk_fixed_max = (uint32_t) NULL; +#endif Audio_Clock_Choose(PLL_CLK, &Init_Params, &Clock_Params); obj->clock = Clock_Params.Clock; @@ -422,7 +427,7 @@ static uint32_t i2s_clock_select(i2s_t *obj) clock_mode = PLL_CLOCK_98P304M / Clock_Params.PLL_DIV; break; } - dbg("CLK: %d mode: %f div: %d\n", Clock_Params.Clock, clock_mode, Clock_Params.PLL_DIV); + dbg("CLK: %d div: %d\n", Clock_Params.Clock, Clock_Params.PLL_DIV); return clock_mode; } From 8df3379ea644d8f3db9ddc40fa1ac3d0b46326a0 Mon Sep 17 00:00:00 2001 From: lhenry-realtek <176550561+lhenry-realtek@users.noreply.github.com> Date: Fri, 29 Nov 2024 10:12:33 +0800 Subject: [PATCH 3/8] os/board/rtl8730e: add missing i2s init flow api calls - WCLK was not started because there was no I2S and DMA transaction being started, added calls to relevant APIs - Currently DMA for I2S TDM is not properly initialized and will hardfault when rx page, currently under investigation - User is required to call I2S_RESUME and I2S_PAUSE macros when attempting a TDM operation (e.g READ), as these macros will start WCLK (for MASTER mode) - Add some debug logging and revert changes to irrelevant files --- os/arch/arm/src/amebasmart/amebasmart_i2s.c | 39 ++++++++++++++----- .../mbed/targets/hal/rtl8730e/i2s_api.c | 14 ++++++- os/board/rtl8730e/src/rtl8730e_i2schar.c | 13 +------ 3 files changed, 42 insertions(+), 24 deletions(-) diff --git a/os/arch/arm/src/amebasmart/amebasmart_i2s.c b/os/arch/arm/src/amebasmart/amebasmart_i2s.c index a670612d64..080e98c1bf 100644 --- a/os/arch/arm/src/amebasmart/amebasmart_i2s.c +++ b/os/arch/arm/src/amebasmart/amebasmart_i2s.c @@ -190,11 +190,12 @@ struct amebasmart_i2s_s { #endif #if defined(I2S_HAVE_RX) && (0 < I2S_HAVE_RX) - //uint8_t i2s_rx_buf[(I2S_DMA_PAGE_NUM + 1) * I2S_DMA_PAGE_SIZE]__attribute__((aligned(64))); /* Allocate buffer for use in RX, must I2S_DMA_PAGE_NUM+1 for zero buffer */ - uint8_t* i2s_rx_buf; + uint8_t i2s_rx_buf[(I2S_DMA_PAGE_NUM + 1) * I2S_DMA_PAGE_SIZE]__attribute__((aligned(64))); /* Allocate buffer for use in RX, must I2S_DMA_PAGE_NUM+1 for zero buffer */ + //uint8_t* i2s_rx_buf; i2s_irq_handler rx_isr_handler; #if defined(I2S_HAVE_TDM) && (0 < I2S_HAVE_TDM) - uint8_t* i2s_rx_buf_ext; + //uint8_t* i2s_rx_buf_ext; + uint8_t i2s_rx_buf_ext[(I2S_DMA_PAGE_NUM + 1) * I2S_DMA_PAGE_SIZE]__attribute__((aligned(64))); /* Allocate buffer for use in RX, must I2S_DMA_PAGE_NUM+1 for zero buffer */ i2s_irq_handler rx_isr_handler_ext; #endif #endif @@ -236,7 +237,7 @@ static const struct amebasmart_i2s_config_s amebasmart_i2s2_config = { .i2s_sclk_pin = PB_21, .i2s_ws_pin = PA_16, .i2s_sd_tx_pin = PB_10, - .i2s_sd_rx_pin = PB_19, + .i2s_sd_rx_pin = PB_20, .i2s_idx = I2S_NUM_2, .rxenab = 0, @@ -872,13 +873,13 @@ static int i2s_rx_start(struct amebasmart_i2s_s *priv) /* Check if the DMA is IDLE */ if (!sq_empty(&priv->rx.act)) { - i2sinfo("[RX start] RX active!\n"); + lldbg("[RX start] RX active!\n"); return OK; } /* If there are no pending transfer, then bail returning success */ if (sq_empty(&priv->rx.pend)) { - i2sinfo("[RX start] RX pend empty!\n"); + lldbg("[RX start] RX pend empty!\n"); return OK; } @@ -1108,7 +1109,7 @@ static int i2s_receive(struct i2s_dev_s *dev, struct ap_buffer_s *apb, i2s_callb DEBUGASSERT(bfcontainer); i2s_exclsem_take(priv); - i2sinfo("RX Exclusive Enter\n"); + printf("RX Exclusive Enter\n"); /* Add a reference to the audio buffer */ apb_reference(apb); @@ -1126,12 +1127,12 @@ static int i2s_receive(struct i2s_dev_s *dev, struct ap_buffer_s *apb, i2s_callb flags = enter_critical_section(); sq_addlast((sq_entry_t *)bfcontainer, &priv->rx.pend); leave_critical_section(flags); - i2sinfo("i2s_rx_start\n"); + printf("i2s_rx_start\n"); /* Start transfer */ ret = i2s_rx_start(priv); i2s_exclsem_give(priv); - i2sinfo("RX Exclusive Exit\n"); + printf("RX Exclusive Exit\n"); return OK; @@ -1150,12 +1151,15 @@ static int i2s_receive(struct i2s_dev_s *dev, struct ap_buffer_s *apb, i2s_callb */ void i2s_transfer_rx_handleirq(void *data, char *pbuf) { + /* TODO: there is currently crash here as data/pbuf is NULL! */ + lldbg("handle irq %p %p\n", data, pbuf); struct amebasmart_i2s_s *priv = (struct amebasmart_i2s_s *)data; i2s_t *obj = priv->i2s_object; - /* submit a new page for receive */ i2s_recv_page(obj); + lldbg("rxpage\n"); + i2s_rxdma_callback(priv, OK); } @@ -1538,6 +1542,7 @@ static int i2s_resume(struct i2s_dev_s *dev, i2s_ch_dir_t dir) #if defined(I2S_HAVE_RX) && (0 < I2S_HAVE_RX) #if defined(I2S_HAVE_TDM) && (0 < I2S_HAVE_TDM) if (dir == I2S_RX && priv->rxenab) { + lldbg("XX %d\n", priv->tdmenab); if (priv->tdmenab) { ameba_i2s_tdm_resume(priv->i2s_object); } else { @@ -1762,6 +1767,14 @@ static uint32_t i2s_samplerate(struct i2s_dev_s *dev, uint32_t rate) struct amebasmart_i2s_s *priv = (struct amebasmart_i2s_s *)dev; DEBUGASSERT(priv && rate > 0); +#if defined(I2S_HAVE_TDM) && (0 < I2S_HAVE_TDM) + lldbg("called by ioctl!\n"); + if (priv->tdmenab) { + lldbg("tdm sample rate currently not changeable!\n"); + return priv->i2s_object->sampling_rate; + } +#endif + priv->i2s_object->sampling_rate = rate; priv->sample_rate = rate; @@ -1842,6 +1855,7 @@ static void i2s_getdefaultconfig(struct amebasmart_i2s_s *priv) #if defined(I2S_HAVE_TDM) && (0 < I2S_HAVE_TDM) if (priv->config->tdmenab) { dbg("init for tdm: %d \n", priv->config->tdmenab); + priv->tdmenab = priv->config->tdmenab; /* TDM configuration fixed for ais25ba */ priv->i2s_object->mode = TDM; priv->i2s_object->role = MASTER; @@ -2098,6 +2112,11 @@ struct i2s_dev_s *amebasmart_i2s_tdm_initialize(uint16_t port, bool is_reinit) i2s_set_dma_buffer(priv->i2s_object, (char *)priv->i2s_tx_buf, NULL, I2S_DMA_PAGE_NUM, I2S_DMA_PAGE_SIZE); /* Allocate DMA Buffer for TX */ #endif +#if defined(I2S_HAVE_RX) && (0 < I2S_HAVE_RX) + i2s_set_dma_buffer(priv->i2s_object, NULL, (char *)priv->i2s_rx_buf, I2S_DMA_PAGE_NUM, I2S_DMA_PAGE_SIZE); /* Allocate DMA Buffer for TX */ +#endif + + /* configures IRQ */ #if defined(I2S_HAVE_RX) && (0 < I2S_HAVE_RX) priv->rx_isr_handler = (i2s_irq_handler)&i2s_transfer_rx_handleirq; diff --git a/os/board/rtl8730e/src/component/mbed/targets/hal/rtl8730e/i2s_api.c b/os/board/rtl8730e/src/component/mbed/targets/hal/rtl8730e/i2s_api.c index 3cad07e5fd..d36260ea84 100755 --- a/os/board/rtl8730e/src/component/mbed/targets/hal/rtl8730e/i2s_api.c +++ b/os/board/rtl8730e/src/component/mbed/targets/hal/rtl8730e/i2s_api.c @@ -602,6 +602,7 @@ void i2s_send_page(i2s_t *obj, uint32_t *pbuf) #ifdef CONFIG_AMEBASMART_I2S_RX void i2s_recv_page(i2s_t *obj) { + lldbg("i2s rcv page called\n"); uint8_t i2s_index = obj->i2s_idx; pRX_BLOCK prx_block = &(sp_rx_info.rx_block[sp_rx_info.rx_usr_cnt]); @@ -621,6 +622,7 @@ void i2s_recv_page(i2s_t *obj) */ void i2s_enable(i2s_t *obj) { + lldbg("enable called!\n"); #if defined(CONFIG_AMEBASMART_I2S_TDM) // turn on MCLK if master if (obj->role == MASTER) { @@ -707,12 +709,12 @@ void ameba_i2s_tdm_pause(i2s_t *obj) { SP_GDMA_STRUCT *l_SPGdmaStruct = &SPGdmaStruct; SP_GDMA_STRUCT_EXT *l_SPGdmaStructExt = &SPGdmaStructExt; -#if defined(CONFIG_AMEBASMART_I2S_TDM) + lldbg("in pause\n"); + // turn on MCLK if master if (obj->role == MASTER) { AUDIO_SP_SetMclk(obj->i2s_idx, DISABLE); } -#endif if (obj->direction == I2S_DIR_TX) { GDMA_ClearINT(l_SPGdmaStruct->SpTxGdmaInitStruct.GDMA_Index, l_SPGdmaStruct->SpTxGdmaInitStruct.GDMA_ChNum); @@ -722,6 +724,8 @@ void ameba_i2s_tdm_pause(i2s_t *obj) { } else { GDMA_Suspend(l_SPGdmaStruct->SpRxGdmaInitStruct.GDMA_Index, l_SPGdmaStruct->SpRxGdmaInitStruct.GDMA_ChNum); GDMA_Suspend(l_SPGdmaStructExt->SpRxGdmaInitStructExt.GDMA_Index, l_SPGdmaStructExt->SpRxGdmaInitStructExt.GDMA_ChNum); + AUDIO_SP_DmaCmd(obj->i2s_idx, DISABLE); + AUDIO_SP_RXStart(obj->i2s_idx, DISABLE); } } #endif @@ -747,6 +751,7 @@ void ameba_i2s_tdm_resume(i2s_t *obj) { SP_GDMA_STRUCT *l_SPGdmaStruct = &SPGdmaStruct; SP_GDMA_STRUCT_EXT *l_SPGdmaStructExt = &SPGdmaStructExt; + lldbg("in resume\n"); // turn on MCLK if master if (obj->role == MASTER) { @@ -757,6 +762,8 @@ void ameba_i2s_tdm_resume(i2s_t *obj) { AUDIO_SP_DmaCmd(obj->i2s_idx, ENABLE); AUDIO_SP_TXStart(obj->i2s_idx, ENABLE); } else { + AUDIO_SP_DmaCmd(obj->i2s_idx, ENABLE); + AUDIO_SP_RXStart(obj->i2s_idx, ENABLE); GDMA_Resume(l_SPGdmaStruct->SpRxGdmaInitStruct.GDMA_Index, l_SPGdmaStruct->SpRxGdmaInitStruct.GDMA_ChNum); GDMA_Resume(l_SPGdmaStructExt->SpRxGdmaInitStructExt.GDMA_Index, l_SPGdmaStructExt->SpRxGdmaInitStructExt.GDMA_ChNum); } @@ -844,6 +851,7 @@ static void i2s_tdm_rx_isr(void *sp_data) /* Read data */ pRX_BLOCK prx_block = &(sp_rx_info.rx_block[sp_rx_info.rx_usr_cnt]); DCache_CleanInvalidate((uint32_t)prx_block->rx_addr, sp_rx_info.rx_page_size); + dbg("I2S: ISR0! %p\n", (void *)(uint32_t)prx_block->rx_addr); I2SUserCB.RxCCB((uint32_t)NULL, (void *)(uint32_t)prx_block->rx_addr); i2s_get_free_rx_page(i2s_index); } @@ -855,6 +863,7 @@ static void i2s_tdm_rx_isr_ext(void *sp_data) GDMA_InitStruct = &(gs->SpRxGdmaInitStructExt); uint8_t i2s_index = gs->i2s_idx; + dbg("I2S: ISR1!\n"); /* Clear Pending ISR */ GDMA_ClearINT(GDMA_InitStruct->GDMA_Index, GDMA_InitStruct->GDMA_ChNum); @@ -972,6 +981,7 @@ void i2s_tdm_disable(i2s_t *obj, bool is_suspend) */ void i2s_tdm_set_param(i2s_t *obj, int channel_num, int rate, int word_len) { + lldbg("tdm_set_param\n"); uint32_t clock_mode = 0; assert_param(IS_SP_CHN_NUM(obj->channel_num)); diff --git a/os/board/rtl8730e/src/rtl8730e_i2schar.c b/os/board/rtl8730e/src/rtl8730e_i2schar.c index 7a83b3811e..c570d4ce46 100644 --- a/os/board/rtl8730e/src/rtl8730e_i2schar.c +++ b/os/board/rtl8730e/src/rtl8730e_i2schar.c @@ -42,10 +42,6 @@ #ifndef CONFIG_AMEBASMART_I2SCHAR_MINOR_3 #define CONFIG_AMEBASMART_I2SCHAR_MINOR_3 3 #endif - -#ifndef CONFIG_AMEBASMART_I2SCHAR_MINOR_4 -#define CONFIG_AMEBASMART_I2SCHAR_MINOR_4 4 -#endif /***************************************************************************** * Private Functions ****************************************************************************/ @@ -78,22 +74,15 @@ int i2schar_devinit(void) #if defined(CONFIG_AMEBASMART_I2S2) && !defined(CONFIG_AUDIO_ALC1019) /* Call amebasmart_i2s_initialize() to get an instance of the I2S interface */ /* Initialise I2S2 */ -#if defined(CONFIG_AMEBASMART_I2S_TDM) - i2s = amebasmart_i2s_tdm_initialize(I2S_NUM_2, I2S_INIT); -#else i2s = amebasmart_i2s_initialize(I2S_NUM_2, I2S_INIT); -#endif + if (!i2s) { lldbg("ERROR: Failed to get the amebasmart I2S driver\n"); return -ENODEV; } /* Register the I2S character driver at "/dev/i2schar2" */ -#if defined(CONFIG_AMEBASMART_I2S_TDM) - ret = i2schar_register(i2s, CONFIG_AMEBASMART_I2SCHAR_MINOR_4); -#else ret = i2schar_register(i2s, CONFIG_AMEBASMART_I2SCHAR_MINOR_2); -#endif if (ret < 0) { lldbg("ERROR: i2schar_register failed: %d\n", ret); return ret; From f8268e05428c27d1a8978f406a13f42944b51959 Mon Sep 17 00:00:00 2001 From: lhenry-realtek <176550561+lhenry-realtek@users.noreply.github.com> Date: Mon, 23 Dec 2024 08:34:28 +0800 Subject: [PATCH 4/8] os/arch/arm/src/amebasmart: apply pinmux changes for TDM board Pin allocation is adjusted for TDM functionality - please refer to email chain for rework steps for first board revision (Dec 2024) Please note that as a result of this change, a new pinmux configuration needs to be maintained for TDM only! - PB29 (previously I2C0 SDA) --> I2S2 DIN0 - PA30 [or PB5] (previously unconnected) --> I2C0 SDA - PB20 (previously I2S2 DIN0) --> Unconnected --- os/arch/arm/src/amebasmart/amebasmart_i2c.c | 5 +++++ os/arch/arm/src/amebasmart/amebasmart_i2s.c | 10 +++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/os/arch/arm/src/amebasmart/amebasmart_i2c.c b/os/arch/arm/src/amebasmart/amebasmart_i2c.c index 2a9e92bdcf..3e36565cc1 100644 --- a/os/arch/arm/src/amebasmart/amebasmart_i2c.c +++ b/os/arch/arm/src/amebasmart/amebasmart_i2c.c @@ -302,8 +302,13 @@ static const struct amebasmart_i2c_config_s amebasmart_i2c0_config = { //.filtscl = CONFIG_I2C0_FILTSCL, //.filtsda = CONFIG_I2C0_FILTSDA, #if CONFIG_RTL8730E_BOARD_REVISION >= 5 +#ifdef CONFIG_AMEBASMART_I2S_TDM + .scl_pin = PB_30, + .sda_pin = PA_30, +#else .scl_pin = PB_30, .sda_pin = PB_29, +#endif #else .scl_pin = PA_10, .sda_pin = PA_9, diff --git a/os/arch/arm/src/amebasmart/amebasmart_i2s.c b/os/arch/arm/src/amebasmart/amebasmart_i2s.c index 080e98c1bf..9325e80518 100644 --- a/os/arch/arm/src/amebasmart/amebasmart_i2s.c +++ b/os/arch/arm/src/amebasmart/amebasmart_i2s.c @@ -265,11 +265,11 @@ static const struct amebasmart_i2s_config_s amebasmart_i2s3_config = { #if defined(I2S_HAVE_TDM) && (0 < I2S_HAVE_TDM) /* currently support I2S2 */ static const struct amebasmart_i2s_config_s amebasmart_i2s2_tdm_config = { - .i2s_mclk_pin = PB_22, - .i2s_sclk_pin = PB_21, - .i2s_ws_pin = PA_16, - .i2s_sd_tx_pin = PB_10, - .i2s_sd_rx_pin = PB_19, + .i2s_mclk_pin = PB_22, // PU + .i2s_sclk_pin = PB_21, // PU + .i2s_ws_pin = PA_16, // PU + .i2s_sd_tx_pin = PB_10, // PU + .i2s_sd_rx_pin = PB_29, // PU (REWORK) .i2s_idx = I2S_NUM_2, .rxenab = 1, From b7f87764274bdb5543210c2a98ce8caa3ef82742 Mon Sep 17 00:00:00 2001 From: lhenry-realtek <176550561+lhenry-realtek@users.noreply.github.com> Date: Mon, 23 Dec 2024 17:22:28 +0800 Subject: [PATCH 5/8] os/board/rtl8730e: working I2S TDM implementation with DMA This commit completes the DMA portion of the I2S implementation and allows for receive of data based on AIS25BA sensor TDM signal specification Please note that if TDM board v1 (Dec 2024) is used, rework is required! To test, build with AIS25BA sensor example and use "sensor" command in TASH --- os/arch/arm/src/amebasmart/amebasmart_i2s.c | 223 +++++++++++-- .../mbed/targets/hal/rtl8730e/i2s_api.c | 315 ++++++++++++------ 2 files changed, 408 insertions(+), 130 deletions(-) diff --git a/os/arch/arm/src/amebasmart/amebasmart_i2s.c b/os/arch/arm/src/amebasmart/amebasmart_i2s.c index 9325e80518..327949b9ce 100644 --- a/os/arch/arm/src/amebasmart/amebasmart_i2s.c +++ b/os/arch/arm/src/amebasmart/amebasmart_i2s.c @@ -191,11 +191,10 @@ struct amebasmart_i2s_s { #if defined(I2S_HAVE_RX) && (0 < I2S_HAVE_RX) uint8_t i2s_rx_buf[(I2S_DMA_PAGE_NUM + 1) * I2S_DMA_PAGE_SIZE]__attribute__((aligned(64))); /* Allocate buffer for use in RX, must I2S_DMA_PAGE_NUM+1 for zero buffer */ + uint8_t i2s_rx_buf_ext[(I2S_DMA_PAGE_NUM + 1) * I2S_DMA_PAGE_SIZE]__attribute__((aligned(64))); /* Allocate buffer for use in RX, must I2S_DMA_PAGE_NUM+1 for zero buffer */ //uint8_t* i2s_rx_buf; i2s_irq_handler rx_isr_handler; #if defined(I2S_HAVE_TDM) && (0 < I2S_HAVE_TDM) - //uint8_t* i2s_rx_buf_ext; - uint8_t i2s_rx_buf_ext[(I2S_DMA_PAGE_NUM + 1) * I2S_DMA_PAGE_SIZE]__attribute__((aligned(64))); /* Allocate buffer for use in RX, must I2S_DMA_PAGE_NUM+1 for zero buffer */ i2s_irq_handler rx_isr_handler_ext; #endif #endif @@ -209,12 +208,14 @@ struct amebasmart_i2s_s { uint8_t txenab : 1; /* True: TX transfers enabled */ #if defined(I2S_HAVE_TDM) && (0 < I2S_HAVE_TDM) uint8_t tdmenab : 1; /* True: TDM Enabled (this will disable MULTIIO)*/ + + struct ap_buffer_s *apb_rx; /* Pointer to application RX audio buffer for DMA to populate */ #endif int sample_rate; /*!< I2S sample rate */ int channel_num; /*!< Number of channels */ int bits_per_sample; /*!< Bits per sample */ - struct ap_buffer_s *apb_tx; /* Pointer to application TX audio buffer to track current TX byte count */ + struct ap_buffer_s *apb_tx; #if defined(I2S_HAVE_RX) && (0 < I2S_HAVE_RX) struct amebasmart_transport_s rx; /* RX transport state */ @@ -265,11 +266,11 @@ static const struct amebasmart_i2s_config_s amebasmart_i2s3_config = { #if defined(I2S_HAVE_TDM) && (0 < I2S_HAVE_TDM) /* currently support I2S2 */ static const struct amebasmart_i2s_config_s amebasmart_i2s2_tdm_config = { - .i2s_mclk_pin = PB_22, // PU - .i2s_sclk_pin = PB_21, // PU - .i2s_ws_pin = PA_16, // PU - .i2s_sd_tx_pin = PB_10, // PU - .i2s_sd_rx_pin = PB_29, // PU (REWORK) + .i2s_mclk_pin = PB_22, // PU + .i2s_sclk_pin = PB_21, // PU + .i2s_ws_pin = PA_16, // PU + .i2s_sd_tx_pin = PB_10, // PU + .i2s_sd_rx_pin = PB_29, // PU (REWORK) .i2s_idx = I2S_NUM_2, .rxenab = 1, @@ -345,6 +346,9 @@ static int i2s_receive(struct i2s_dev_s *dev, struct ap_buffer_s *apb, i2s_callb static int i2s_send(struct i2s_dev_s *dev, struct ap_buffer_s *apb, i2s_callback_t callback, void *arg, uint32_t timeout); static int i2s_err_cb_register(struct i2s_dev_s *dev, i2s_err_cb_t cb, void *arg); +/* tdm methods */ +static int i2s_tdm_receive(struct i2s_dev_s *dev, struct ap_buffer_s *apb, i2s_callback_t callback, void *arg, uint32_t timeout); + /**************************************************************************** * External functions ****************************************************************************/ @@ -384,7 +388,7 @@ static const struct i2s_ops_s g_i2stdm_ops = { .i2s_rxsamplerate = i2s_samplerate, .i2s_rxdatawidth = i2s_rxdatawidth, - .i2s_receive = i2s_receive, + .i2s_receive = i2s_tdm_receive, /* Transmitter methods */ @@ -859,9 +863,22 @@ static int i2s_rxdma_prep(struct amebasmart_i2s_s *priv, struct amebasmart_buffe apb->nbytes = 0; apb->curbyte = 0; - priv->i2s_rx_buf = apb->samp; - i2s_set_dma_buffer(priv->i2s_object, NULL, (char *)priv->i2s_rx_buf, I2S_DMA_PAGE_NUM, I2S_DMA_PAGE_SIZE); + //priv->i2s_rx_buf = apb->samp; +#if defined(I2S_HAVE_TDM) && (0 < I2S_HAVE_TDM) + if (priv->tdmenab) { + i2sinfo("tdm: set dma buffer! rxbuf: %p\n", priv->i2s_rx_buf); + i2s_tdm_set_dma_buffer(priv->i2s_object, NULL, (char *)priv->i2s_rx_buf, I2S_DMA_PAGE_NUM, I2S_DMA_PAGE_SIZE, GDMA_INT, apb->nmaxbytes); + if (priv->i2s_object->fifo_num >= SP_RX_FIFO8) { + i2sinfo("tdm: set dma buffer for EXT! rxbuf1: %p\n", priv->i2s_rx_buf); + i2s_tdm_set_dma_buffer(priv->i2s_object, NULL, (char *)priv->i2s_rx_buf_ext, I2S_DMA_PAGE_NUM, I2S_DMA_PAGE_SIZE, GDMA_EXT, apb->nmaxbytes); + } + } else +#else + { + i2s_set_dma_buffer(priv->i2s_object, NULL, (char *)priv->i2s_rx_buf, I2S_DMA_PAGE_NUM, I2S_DMA_PAGE_SIZE); + } +#endif return 0; } @@ -891,8 +908,12 @@ static int i2s_rx_start(struct amebasmart_i2s_s *priv) /* Add the container to the list of active DMAs */ sq_addlast((sq_entry_t *)bfcontainer, &priv->rx.act); - - i2s_recv_page(priv->i2s_object); +#if defined(I2S_HAVE_TDM) && (0 < I2S_HAVE_TDM) + if(priv->tdmenab == 0) +#endif + { + i2s_recv_page(priv->i2s_object); + } } leave_critical_section(flags); @@ -1009,7 +1030,8 @@ static void i2s_rx_schedule(struct amebasmart_i2s_s *priv, int result) /* Schedule the TX DMA done processing to occur on the worker thread. */ ret = work_queue(HPWORK, &priv->rx.work, i2s_rx_worker, priv, 0); if (ret != 0) { - i2serr("ERROR: Failed to queue RX work: %d\n", ret); + //i2serr("ERROR: Failed to queue RX work: %d\n", ret); + lldbg("ERROR: Failed to queue RX work: %d\n", ret); } } } @@ -1098,7 +1120,6 @@ static int i2s_receive(struct i2s_dev_s *dev, struct ap_buffer_s *apb, i2s_callb struct amebasmart_buffer_s *bfcontainer; irqstate_t flags; int ret; - printf("i2s_receive\n"); /* Has the RX channel been enabled? */ if (!priv->rxenab) { i2serr("ERROR: I2S has no receiver\n"); @@ -1109,7 +1130,7 @@ static int i2s_receive(struct i2s_dev_s *dev, struct ap_buffer_s *apb, i2s_callb DEBUGASSERT(bfcontainer); i2s_exclsem_take(priv); - printf("RX Exclusive Enter\n"); + i2sinfo("RX Exclusive Enter\n"); /* Add a reference to the audio buffer */ apb_reference(apb); @@ -1127,12 +1148,77 @@ static int i2s_receive(struct i2s_dev_s *dev, struct ap_buffer_s *apb, i2s_callb flags = enter_critical_section(); sq_addlast((sq_entry_t *)bfcontainer, &priv->rx.pend); leave_critical_section(flags); - printf("i2s_rx_start\n"); /* Start transfer */ ret = i2s_rx_start(priv); i2s_exclsem_give(priv); - printf("RX Exclusive Exit\n"); + i2sinfo("RX Exclusive Exit\n"); + + return OK; + +#else + i2serr("ERROR: I2S has no receiver\n"); + UNUSED(priv); + return -ENOSYS; +#endif +} + +static int i2s_tdm_receive(struct i2s_dev_s *dev, struct ap_buffer_s *apb, i2s_callback_t callback, void *arg, uint32_t timeout) +{ + struct amebasmart_i2s_s *priv = (struct amebasmart_i2s_s *)dev; + + i2sinfo("[TDM RX] apb=%p nmaxbytes=%d samp=%p arg=%p timeout=%d\n", apb, apb->nmaxbytes, apb->samp, arg, timeout); + + /* apb samp is the buffer user use to receive, nmaxbyte should be less than I2S_DMA_PAGE_NUM*I2S_DMA_PAGE_SIZE */ + i2s_init_buffer(apb->samp, apb->nmaxbytes); + +#if defined(I2S_HAVE_RX) && (0 < I2S_HAVE_RX) + struct amebasmart_buffer_s *bfcontainer; + irqstate_t flags; + int ret; + /* Has the RX channel been enabled? */ + if (!priv->rxenab) { + i2serr("ERROR: I2S has no receiver\n"); + return -EAGAIN; + } + + /* obtain a buffer container */ + bfcontainer = i2s_buf_rx_allocate(priv); + DEBUGASSERT(bfcontainer); + + i2s_exclsem_take(priv); + i2sinfo("RX Exclusive Enter\n"); + + /* Add a reference to the audio buffer */ + apb_reference(apb); + + /* Reference the application buffer for DMA operation to copy data into */ + priv->apb_rx = apb; + + /* Initialize the buffer container structure */ + bfcontainer->callback = (void *)callback; + bfcontainer->timeout = timeout; + bfcontainer->arg = arg; + bfcontainer->apb = apb; + bfcontainer->result = -EBUSY; + + /* Prepare DMA microcode (CHANGE THIS! THIS SHOULD SETUP DMA BUFFERS BEFORE CLOCK STARTS) */ + i2s_rxdma_prep(priv, bfcontainer); + + flags = enter_critical_section(); + sq_addlast((sq_entry_t *)bfcontainer, &priv->rx.pend); + leave_critical_section(flags); + /* Start transfer */ + ret = i2s_rx_start(priv); + + /* resume the clockgen if master, GDMA-SPORT ISR will trigger the callback to pass memory to app layer */ + if (priv->i2s_object->role == MASTER) { + //i2s_resume(priv, priv->i2s_object->direction); + ameba_i2s_tdm_resume(priv->i2s_object); + } + + i2s_exclsem_give(priv); + i2sinfo("RX Exclusive Exit\n"); return OK; @@ -1144,23 +1230,97 @@ static int i2s_receive(struct i2s_dev_s *dev, struct ap_buffer_s *apb, i2s_callb } /*! - * @brief Tx interrupt handler. + * @brief Rx interrupt handler. * * @param base pointer. * @param handle Pointer to the sai_handle_t structure. */ +extern volatile uint8_t DMA_Done, DMA_Done_1; void i2s_transfer_rx_handleirq(void *data, char *pbuf) { - /* TODO: there is currently crash here as data/pbuf is NULL! */ - lldbg("handle irq %p %p\n", data, pbuf); struct amebasmart_i2s_s *priv = (struct amebasmart_i2s_s *)data; i2s_t *obj = priv->i2s_object; - /* submit a new page for receive */ - i2s_recv_page(obj); - lldbg("rxpage\n"); +#if defined(I2S_HAVE_TDM) && (0 < I2S_HAVE_TDM) + if (priv->tdmenab) { + /* this callback will be called twice if 2 DMA channels are used, only allow processing when both completion flags are true */ + if (obj->fifo_num >= SP_RX_FIFO8) { + if (DMA_Done && DMA_Done_1) { + lldbg("rx complete 8CH! stopping clockgen! APB: %p\n", priv->apb_rx); + + /* stop clockgen */ + ameba_i2s_tdm_pause(obj); + + /* INTERNAL channel will have SLOT 1,2 followed by 5,6 */ + /* EXTERNAL channel will have SLOT 3,4 followed by 7,8 */ + u16 word_size; + switch(priv->i2s_object->channel_length) { + case SP_RXCL_16: + word_size = sizeof(u16); + break; + default: + word_size = sizeof(u32); + break; + } + + if (word_size == sizeof(u16)) { + u16 *dst_buf = (u16 *)priv->apb_rx->samp; + u32 *rx_int = (u32 *)priv->i2s_rx_buf; + u32 *rx_ext = (u32 *)priv->i2s_rx_buf_ext; + + for (u32 i = 0, j = 0; i < priv->apb_rx->nmaxbytes; i += 8, j += 4) { + dst_buf[i + 0] = rx_int[j + 0] >> 16; // slot 1 + dst_buf[i + 1] = rx_int[j + 1] >> 16; // slot 2 + dst_buf[i + 2] = rx_ext[j + 0] >> 16; // slot 3 + dst_buf[i + 3] = rx_ext[j + 1] >> 16; // slot 4 + + dst_buf[i + 4] = rx_int[j + 0] & 0xFFFF; // slot 5 + dst_buf[i + 5] = rx_int[j + 1] & 0xFFFF; // slot 6 + dst_buf[i + 6] = rx_ext[j + 0] & 0xFFFF; // slot 7 + dst_buf[i + 7] = rx_ext[j + 1] & 0xFFFF; // slot 8 + } + } else { + u32 *dst_buf = (u32 *)priv->apb_rx->samp; + u32 *rx_int = (u32 *)priv->i2s_rx_buf; + u32 *rx_ext = (u32 *)priv->i2s_rx_buf_ext; + + /* TODO: Verify correctness of this */ + for (u32 i = 0, j = 0; i < priv->apb_rx->nmaxbytes; i += 8, j += 4) { + dst_buf[i + 0] = rx_int[j + 0]; // slot 1 + dst_buf[i + 1] = rx_int[j + 1]; // slot 2 + dst_buf[i + 2] = rx_ext[j + 0]; // slot 3 + dst_buf[i + 3] = rx_ext[j + 1]; // slot 4 + + dst_buf[i + 4] = rx_int[j + 2]; // slot 5 + dst_buf[i + 5] = rx_int[j + 3]; // slot 6 + dst_buf[i + 6] = rx_ext[j + 2]; // slot 7 + dst_buf[i + 7] = rx_ext[j + 3]; // slot 8 + } + } + /* call upper layer callback */ + i2s_rxdma_callback(priv, OK); + } + } else { + if (DMA_Done) { + lldbg("rx complete! stopping clockgen\n"); + /* stop clockgen */ + ameba_i2s_tdm_pause(obj); + + /* copy DMA contents into buffer container ? */ + // TODO: for single channel DMA populate the buffer + + /* call upper layer callback */ + i2s_rxdma_callback(priv, OK); + } + } + } +#else + + /* submit a new page for receive */ + i2s_recv_page(obj); i2s_rxdma_callback(priv, OK); +#endif } #if defined(I2S_HAVE_RX) && (0 < I2S_HAVE_RX) @@ -1872,7 +2032,7 @@ static void i2s_getdefaultconfig(struct amebasmart_i2s_s *priv) /* word and channel length */ priv->bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT; priv->i2s_object->word_length = WL_16b; // SP_RXWL_16; - priv->i2s_object->channel_length = SP_CL_16; + priv->i2s_object->channel_length = SP_RXCL_16; /* FIFO size */ priv->i2s_object->fifo_num = SP_RX_FIFO8; // for 8ch tdm @@ -2096,12 +2256,6 @@ struct i2s_dev_s *amebasmart_i2s_tdm_initialize(uint16_t port, bool is_reinit) #if defined(I2S_HAVE_TX) && (0 < I2S_HAVE_TX) i2s_buf_tx_initialize(priv); #endif - printf("tdmenab: %d \n", priv->config->tdmenab); - printf("ChLen: %d \n", priv->i2s_object->channel_length); - printf("WLen: %d \n", priv->i2s_object->word_length); - printf("ChNum: %d \n", priv->i2s_object->channel_num); - printf("SR: %d \n", priv->i2s_object->sampling_rate); - printf("FIFOnum: %d \n", priv->i2s_object->fifo_num); //Init_Params.chn_cnt = (sp_obj.tdmmode + 1) * 2; /* I2s object initialization */ @@ -2109,14 +2263,9 @@ struct i2s_dev_s *amebasmart_i2s_tdm_initialize(uint16_t port, bool is_reinit) /* Initialize buffering */ #if defined(I2S_HAVE_TX) && (0 < I2S_HAVE_TX) - i2s_set_dma_buffer(priv->i2s_object, (char *)priv->i2s_tx_buf, NULL, I2S_DMA_PAGE_NUM, I2S_DMA_PAGE_SIZE); /* Allocate DMA Buffer for TX */ + i2s_tdm_set_dma_buffer(priv->i2s_object, (char *)priv->i2s_tx_buf, NULL, I2S_DMA_PAGE_NUM, I2S_DMA_PAGE_SIZE, GDMA_INT); /* Allocate DMA Buffer for TX */ #endif -#if defined(I2S_HAVE_RX) && (0 < I2S_HAVE_RX) - i2s_set_dma_buffer(priv->i2s_object, NULL, (char *)priv->i2s_rx_buf, I2S_DMA_PAGE_NUM, I2S_DMA_PAGE_SIZE); /* Allocate DMA Buffer for TX */ -#endif - - /* configures IRQ */ #if defined(I2S_HAVE_RX) && (0 < I2S_HAVE_RX) priv->rx_isr_handler = (i2s_irq_handler)&i2s_transfer_rx_handleirq; diff --git a/os/board/rtl8730e/src/component/mbed/targets/hal/rtl8730e/i2s_api.c b/os/board/rtl8730e/src/component/mbed/targets/hal/rtl8730e/i2s_api.c index d36260ea84..c1591c85f7 100755 --- a/os/board/rtl8730e/src/component/mbed/targets/hal/rtl8730e/i2s_api.c +++ b/os/board/rtl8730e/src/component/mbed/targets/hal/rtl8730e/i2s_api.c @@ -73,11 +73,20 @@ typedef struct { uint8_t rx_page_num; uint16_t rx_page_size; +#ifdef CONFIG_AMEBASMART_I2S_TDM + uint16_t rx_request_length; /* length of operation to receive */ + uint32_t *rx_user_buffer; /* buffer that will receive final merged contents */ +#endif + } SP_RX_INFO, *pSP_RX_INFO; typedef struct { GDMA_InitTypeDef SpTxGdmaInitStruct; /* Pointer to GDMA_InitTypeDef */ GDMA_InitTypeDef SpRxGdmaInitStruct; /* Pointer to GDMA_InitTypeDef */ + + GDMA_InitTypeDef SpTxGdmaInitStructExt; /* Pointer to GDMA_InitTypeDef */ + GDMA_InitTypeDef SpRxGdmaInitStructExt; /* Pointer to GDMA_InitTypeDef */ + uint8_t i2s_idx; } SP_GDMA_STRUCT; @@ -95,18 +104,13 @@ static struct GDMA_CH_LLI LliRx[SP_MAX_DMA_PAGE_NUM]; static I2S_USER_CB I2SUserCB; /* Pointer to I2S User Callback */ #endif #ifdef CONFIG_AMEBASMART_I2S_TDM -#define GDMA_SINGLE_MAX_SIZE (128*600) -#define GDMA_SINGLE_MAX_SIZE1 (128*600) +static SP_RX_INFO sp_rx_info_ext; -static volatile uint8_t DMA_Done, DMA_Done_1; /* Flag to indicate when DMA transfer is complete */ -typedef struct { - GDMA_InitTypeDef SpTxGdmaInitStructExt; /* Pointer to GDMA_InitTypeDef */ - GDMA_InitTypeDef SpRxGdmaInitStructExt; /* Pointer to GDMA_InitTypeDef */ - uint8_t i2s_idx; -} SP_GDMA_STRUCT_EXT; -static SP_GDMA_STRUCT_EXT SPGdmaStructExt; +volatile uint8_t DMA_Done, DMA_Done_1; /* Flag to indicate when DMA transfer is complete */ #endif +static void i2s_tdm_rx_isr(void *sp_data); +static void i2s_tdm_rx_isr_ext(void *sp_data); /** * @} */ @@ -170,6 +174,37 @@ static uint32_t *i2s_get_free_rx_page(uint8_t i2s_index) return (uint32_t *)sp_rx_info.rx_full_block.rx_addr; //for audio buffer full case } } + +#ifdef CONFIG_AMEBASMART_I2S_TDM +static uint32_t *i2s_tdm_get_free_rx_page(uint8_t i2s_index, uint32_t gdma_channel) +{ + pRX_BLOCK prx_block; + + if (gdma_channel == GDMA_INT) { + prx_block = &(sp_rx_info.rx_block[sp_rx_info.rx_gdma_cnt]); + + if (prx_block->rx_gdma_own) { + sp_rx_info.rx_full_flag = 0; + return (uint32_t *)prx_block->rx_addr; + } else { + sp_rx_info.rx_full_flag = 1; + return (uint32_t *)sp_rx_info.rx_full_block.rx_addr; //for audio buffer full case + } + + } else { + prx_block = &(sp_rx_info_ext.rx_block[sp_rx_info_ext.rx_gdma_cnt]); + + if (prx_block->rx_gdma_own) { + sp_rx_info_ext.rx_full_flag = 0; + return (uint32_t *)prx_block->rx_addr; + } else { + sp_rx_info_ext.rx_full_flag = 1; + return (uint32_t *)sp_rx_info_ext.rx_full_block.rx_addr; //for audio buffer full case + } + } +} +#endif + #endif #ifdef CONFIG_AMEBASMART_I2S_TX @@ -294,7 +329,6 @@ void i2s_set_dma_buffer(i2s_t *obj, char *tx_buf, char *rx_buf, sp_rx_info.rx_page_size = page_size; sp_rx_info.rx_page_num = page_num; - for (j = 0; j < page_num; j++) { LliRx[j].LliEle.Darx = (uint32_t)rx_buf + j * page_size; if (j == page_num - 1) { @@ -307,6 +341,97 @@ void i2s_set_dma_buffer(i2s_t *obj, char *tx_buf, char *rx_buf, #endif } +#ifdef CONFIG_AMEBASMART_I2S_TDM +void i2s_tdm_set_dma_buffer(i2s_t *obj, char *tx_buf, char *rx_buf, + uint32_t page_num, uint32_t page_size, uint8_t gdma_channel, uint16_t request_length) +{ + assert_param(IS_SP_SEL_I2S(obj->i2s_idx)); + +#if defined(CONFIG_AMEBASMART_I2S_TX) || defined(CONFIG_AMEBASMART_I2S_RX) + uint32_t i, j; +#endif + + if ((page_num < 2) || (page_num > 8) || (page_size < 8)) { + DBG_PRINTF(MODULE_I2S, LEVEL_INFO, "%s: PageNum(%d) valid value is 2~8; PageSize(%d must > 8)\r\n", \ + __FUNCTION__, page_num, page_size); + return; + } + + if (request_length == 0) { + DBG_PRINTF(MODULE_I2S, LEVEL_INFO, "%s: Request length needs to be nonzero\n", __FUNCTION__); + } +#ifdef CONFIG_AMEBASMART_I2S_TX + if (obj->direction == I2S_DIR_TX) { + + sp_tx_info.tx_gdma_cnt = 0; + sp_tx_info.tx_usr_cnt = 0; + sp_tx_info.tx_empty_flag = 0; + + for (i = 0; i < page_num; i++) { + sp_tx_info.tx_block[i].tx_gdma_own = 0; + sp_tx_info.tx_block[i].tx_addr = (uint32_t)tx_buf + i * page_size; + } + + sp_tx_info.tx_zero_block.tx_addr = (uint32_t)tx_buf + page_num * page_size; + + for (i = 0; i < page_size; i++) { + ((uint8_t *)(sp_tx_info.tx_zero_block.tx_addr))[i] = 0; + } + + sp_tx_info.tx_page_size = page_size; + sp_tx_info.tx_page_num = page_num; + /* static buffer has no LL setup*/ + } +#endif +#ifdef CONFIG_AMEBASMART_I2S_RX + else { + /* Setup GDMA INT channel parameters */ + if (gdma_channel == GDMA_INT) { + sp_rx_info.rx_gdma_cnt = 0; + sp_rx_info.rx_usr_cnt = 0; + sp_rx_info.rx_full_flag = 0; + sp_rx_info.rx_request_length = request_length; + + for (i = 0; i < page_num; i++) { + sp_rx_info.rx_block[i].rx_gdma_own = 1; + sp_rx_info.rx_block[i].rx_addr = (uint32_t)rx_buf + i * page_size; + } + + sp_rx_info.rx_full_block.rx_addr = (uint32_t)rx_buf + page_num * page_size; + + for (i = 0; i < page_size; i++) { + ((uint8_t *)(sp_rx_info.rx_full_block.rx_addr))[i] = 0; + } + + sp_rx_info.rx_page_size = page_size; + sp_rx_info.rx_page_num = page_num; + /* static buffer has no LL setup*/ + } else { + /* Setup GDMA EXT channel parameters */ + sp_rx_info_ext.rx_gdma_cnt = 0; + sp_rx_info_ext.rx_usr_cnt = 0; + sp_rx_info_ext.rx_full_flag = 0; + sp_rx_info_ext.rx_request_length = request_length; + + for (i = 0; i < page_num; i++) { + sp_rx_info_ext.rx_block[i].rx_gdma_own = 1; + sp_rx_info_ext.rx_block[i].rx_addr = (uint32_t)rx_buf + i * page_size; + } + + sp_rx_info_ext.rx_full_block.rx_addr = (uint32_t)rx_buf + page_num * page_size; + + for (i = 0; i < page_size; i++) { + ((uint8_t *)(sp_rx_info_ext.rx_full_block.rx_addr))[i] = 0; + } + + sp_rx_info_ext.rx_page_size = page_size; + sp_rx_info_ext.rx_page_num = page_num; + /* static buffer has no LL setup*/ + } + } +#endif +} +#endif /** * @brief Register TX interrupt handler. @@ -383,24 +508,24 @@ static uint32_t i2s_clock_select(i2s_t *obj) uint32_t clock_mode = 0; AUDIO_ClockParams Clock_Params; AUDIO_InitParams Init_Params; -#if defined(CONFIG_AMEBASMART_I2S_TDM) - Init_Params.chn_len = obj->channel_length; - Init_Params.chn_cnt = (obj->fifo_num + 1) * 2; -#else + Init_Params.chn_len = SP_CL_32; Init_Params.chn_cnt = obj->channel_num; -#endif - - - Init_Params.sr = obj->sampling_rate; - dbg("ChLen: %d ChCnt: %d Sr: %d\n", obj->channel_length, obj->channel_num, obj->sampling_rate); + Init_Params.sr = obj->sampling_rate;; + Init_Params.codec_multiplier_with_rate = 256; + Init_Params.sport_mclk_fixed_max = (uint32_t) NULL; + #if defined(CONFIG_AMEBASMART_I2S_TDM) + Init_Params.chn_len = SP_CL_16; + Init_Params.chn_cnt = (obj->fifo_num + 1) * 2; Init_Params.codec_multiplier_with_rate = 0; Init_Params.sport_mclk_fixed_max = (uint32_t) 12288000; //12.288MHz -#else - Init_Params.codec_multiplier_with_rate = 256; - Init_Params.sport_mclk_fixed_max = (uint32_t) NULL; #endif +//#else +// Init_Params.codec_multiplier_with_rate = 256; +// Init_Params.sport_mclk_fixed_max = (uint32_t) NULL; +//#endif + Audio_Clock_Choose(PLL_CLK, &Init_Params, &Clock_Params); obj->clock = Clock_Params.Clock; @@ -427,7 +552,6 @@ static uint32_t i2s_clock_select(i2s_t *obj) clock_mode = PLL_CLOCK_98P304M / Clock_Params.PLL_DIV; break; } - dbg("CLK: %d div: %d\n", Clock_Params.Clock, Clock_Params.PLL_DIV); return clock_mode; } @@ -603,7 +727,22 @@ void i2s_send_page(i2s_t *obj, uint32_t *pbuf) void i2s_recv_page(i2s_t *obj) { lldbg("i2s rcv page called\n"); - uint8_t i2s_index = obj->i2s_idx; + //uint8_t i2s_index = obj->i2s_idx; + + pRX_BLOCK prx_block = &(sp_rx_info.rx_block[sp_rx_info.rx_usr_cnt]); + + prx_block->rx_gdma_own = 1; + sp_rx_info.rx_usr_cnt++; + if (sp_rx_info.rx_usr_cnt == sp_rx_info.rx_page_num) { + sp_rx_info.rx_usr_cnt = 0; + } +} + +#ifdef CONFIG_AMEBASMART_I2S_TDM +void i2s_tdm_recv_page(i2s_t *obj) +{ + lldbg("i2s rcv page called\n"); + //uint8_t i2s_index = obj->i2s_idx; pRX_BLOCK prx_block = &(sp_rx_info.rx_block[sp_rx_info.rx_usr_cnt]); @@ -612,9 +751,20 @@ void i2s_recv_page(i2s_t *obj) if (sp_rx_info.rx_usr_cnt == sp_rx_info.rx_page_num) { sp_rx_info.rx_usr_cnt = 0; } + + /* if FIFO 8CH is used */ + if (obj->fifo_num >= SP_RX_FIFO8) { + prx_block = &(sp_rx_info_ext.rx_block[sp_rx_info_ext.rx_usr_cnt]); + sp_rx_info_ext.rx_usr_cnt++; + if (sp_rx_info_ext.rx_usr_cnt == sp_rx_info_ext.rx_page_num) { + sp_rx_info_ext.rx_usr_cnt = 0; + } + } } #endif +#endif + /** * @brief Enable I2S interrupt and function. * @param obj: I2S object defined in application software. @@ -648,7 +798,6 @@ void i2s_disable(i2s_t *obj, bool is_suspend) SP_GDMA_STRUCT *l_SPGdmaStruct = &SPGdmaStruct; #if defined(CONFIG_AMEBASMART_I2S_TDM) - SP_GDMA_STRUCT_EXT *l_SPGdmaStructExt = &SPGdmaStructExt; // turn off MCLK if master if (obj->role == MASTER) { AUDIO_SP_SetMclk(obj->i2s_idx, DISABLE); @@ -660,12 +809,6 @@ void i2s_disable(i2s_t *obj, bool is_suspend) GDMA_Abort(l_SPGdmaStruct->SpTxGdmaInitStruct.GDMA_Index, l_SPGdmaStruct->SpTxGdmaInitStruct.GDMA_ChNum); GDMA_ChnlFree(l_SPGdmaStruct->SpTxGdmaInitStruct.GDMA_Index, l_SPGdmaStruct->SpTxGdmaInitStruct.GDMA_ChNum); -#if defined(CONFIG_AMEBASMART_I2S_TDM) && defined(CONFIG_AMEBASMART_I2S_TX) - GDMA_ClearINT(l_SPGdmaStructExt->SpTxGdmaInitStructExt.GDMA_Index, l_SPGdmaStructExt->SpTxGdmaInitStructExt.GDMA_ChNum); - GDMA_Abort(l_SPGdmaStructExt->SpTxGdmaInitStructExt.GDMA_Index, l_SPGdmaStructExt->SpTxGdmaInitStructExt.GDMA_ChNum); - GDMA_ChnlFree(l_SPGdmaStructExt->SpTxGdmaInitStructExt.GDMA_Index, l_SPGdmaStructExt->SpTxGdmaInitStructExt.GDMA_ChNum); -#endif - AUDIO_SP_DmaCmd(obj->i2s_idx, DISABLE); AUDIO_SP_TXStart(obj->i2s_idx, DISABLE); if (is_suspend) { @@ -675,12 +818,6 @@ void i2s_disable(i2s_t *obj, bool is_suspend) GDMA_ClearINT(l_SPGdmaStruct->SpRxGdmaInitStruct.GDMA_Index, l_SPGdmaStruct->SpRxGdmaInitStruct.GDMA_ChNum); GDMA_Cmd(l_SPGdmaStruct->SpRxGdmaInitStruct.GDMA_Index, l_SPGdmaStruct->SpRxGdmaInitStruct.GDMA_ChNum, DISABLE); -#if defined(CONFIG_AMEBASMART_I2S_TDM) && defined(CONFIG_AMEBASMART_I2S_RX) - GDMA_ClearINT(l_SPGdmaStructExt->SpRxGdmaInitStructExt.GDMA_Index, l_SPGdmaStructExt->SpRxGdmaInitStructExt.GDMA_ChNum); - GDMA_Abort(l_SPGdmaStructExt->SpRxGdmaInitStructExt.GDMA_Index, l_SPGdmaStructExt->SpRxGdmaInitStructExt.GDMA_ChNum); - GDMA_ChnlFree(l_SPGdmaStructExt->SpRxGdmaInitStructExt.GDMA_Index, l_SPGdmaStructExt->SpRxGdmaInitStructExt.GDMA_ChNum); -#endif - AUDIO_SP_DmaCmd(obj->i2s_idx, DISABLE); AUDIO_SP_RXStart(obj->i2s_idx, DISABLE); } @@ -707,7 +844,6 @@ void ameba_i2s_pause(i2s_t *obj) { void ameba_i2s_tdm_pause(i2s_t *obj) { SP_GDMA_STRUCT *l_SPGdmaStruct = &SPGdmaStruct; - SP_GDMA_STRUCT_EXT *l_SPGdmaStructExt = &SPGdmaStructExt; lldbg("in pause\n"); @@ -718,12 +854,11 @@ void ameba_i2s_tdm_pause(i2s_t *obj) { if (obj->direction == I2S_DIR_TX) { GDMA_ClearINT(l_SPGdmaStruct->SpTxGdmaInitStruct.GDMA_Index, l_SPGdmaStruct->SpTxGdmaInitStruct.GDMA_ChNum); - GDMA_ClearINT(l_SPGdmaStructExt->SpTxGdmaInitStructExt.GDMA_Index, l_SPGdmaStructExt->SpTxGdmaInitStructExt.GDMA_ChNum); AUDIO_SP_DmaCmd(obj->i2s_idx, DISABLE); AUDIO_SP_TXStart(obj->i2s_idx, DISABLE); } else { GDMA_Suspend(l_SPGdmaStruct->SpRxGdmaInitStruct.GDMA_Index, l_SPGdmaStruct->SpRxGdmaInitStruct.GDMA_ChNum); - GDMA_Suspend(l_SPGdmaStructExt->SpRxGdmaInitStructExt.GDMA_Index, l_SPGdmaStructExt->SpRxGdmaInitStructExt.GDMA_ChNum); + GDMA_Suspend(l_SPGdmaStruct->SpRxGdmaInitStructExt.GDMA_Index, l_SPGdmaStruct->SpRxGdmaInitStructExt.GDMA_ChNum); AUDIO_SP_DmaCmd(obj->i2s_idx, DISABLE); AUDIO_SP_RXStart(obj->i2s_idx, DISABLE); } @@ -748,10 +883,10 @@ void ameba_i2s_resume(i2s_t *obj) { #ifdef CONFIG_AMEBASMART_I2S_TDM void ameba_i2s_tdm_resume(i2s_t *obj) { + SP_GDMA_STRUCT *sp_str = &SPGdmaStruct; - SP_GDMA_STRUCT *l_SPGdmaStruct = &SPGdmaStruct; - SP_GDMA_STRUCT_EXT *l_SPGdmaStructExt = &SPGdmaStructExt; - lldbg("in resume\n"); + u32 *pbuf_int, pbuf_ext; + u8 res0, res1; // turn on MCLK if master if (obj->role == MASTER) { @@ -762,10 +897,24 @@ void ameba_i2s_tdm_resume(i2s_t *obj) { AUDIO_SP_DmaCmd(obj->i2s_idx, ENABLE); AUDIO_SP_TXStart(obj->i2s_idx, ENABLE); } else { + pbuf_int = (u32 *)i2s_tdm_get_free_rx_page(obj->i2s_idx, GDMA_INT); + + /* Setup the RX Length to be equal to what the OS layer wants to obtain, even though we allocate a big page size up to 4K */ + res0 = AUDIO_SP_RXGDMA_Init(obj->i2s_idx, GDMA_INT, &sp_str->SpRxGdmaInitStruct, sp_str, (IRQ_FUN)i2s_tdm_rx_isr, (u8 *)pbuf_int, sp_rx_info.rx_request_length); + /* reset gdma flags */ + DMA_Done = 0; + + if (obj->fifo_num >= SP_RX_FIFO8) { + pbuf_ext = (u32 *)i2s_tdm_get_free_rx_page(obj->i2s_idx, GDMA_EXT); + res1 = AUDIO_SP_RXGDMA_Init(obj->i2s_idx, GDMA_EXT, &sp_str->SpRxGdmaInitStructExt, sp_str, (IRQ_FUN)i2s_tdm_rx_isr_ext, (u8 *)pbuf_ext, sp_rx_info_ext.rx_request_length); + /* reset gdma flags */ + DMA_Done_1 = 0; + } + AUDIO_SP_DmaCmd(obj->i2s_idx, ENABLE); + + /* Enable GDMA and SPORT handshake*/ AUDIO_SP_RXStart(obj->i2s_idx, ENABLE); - GDMA_Resume(l_SPGdmaStruct->SpRxGdmaInitStruct.GDMA_Index, l_SPGdmaStruct->SpRxGdmaInitStruct.GDMA_ChNum); - GDMA_Resume(l_SPGdmaStructExt->SpRxGdmaInitStructExt.GDMA_Index, l_SPGdmaStructExt->SpRxGdmaInitStructExt.GDMA_ChNum); } } #endif @@ -792,7 +941,7 @@ static void i2s_tdm_tx_isr(void *sp_data) static void i2s_tdm_tx_isr_ext(void *sp_data) { u32 *pbuf; - SP_GDMA_STRUCT_EXT *gs = sp_data; + SP_GDMA_STRUCT *gs = sp_data; PGDMA_InitTypeDef GDMA_InitStruct; GDMA_InitStruct = &(gs->SpTxGdmaInitStructExt); @@ -819,7 +968,6 @@ void i2s_tdm_tx_irq_handler(i2s_t *obj, i2s_irq_handler handler, uint32_t id) uint8_t i2s_index = obj->i2s_idx; SP_GDMA_STRUCT *sp_str = &SPGdmaStruct; - SP_GDMA_STRUCT_EXT *sp_str_ext = &SPGdmaStructExt; u32 *pbuf, pbuf_1; sp_str->i2s_idx = i2s_index; /* Store I2S index */ @@ -830,8 +978,8 @@ void i2s_tdm_tx_irq_handler(i2s_t *obj, i2s_irq_handler handler, uint32_t id) pbuf = (u32 *)i2s_get_ready_tx_page(i2s_index); pbuf_1 = (u32 *)i2s_get_ready_tx_page(i2s_index); + lldbg("TXGDMA pbuf: %p %p\n", pbuf, pbuf_1); AUDIO_SP_TXGDMA_Init(i2s_index, GDMA_INT, &sp_str->SpTxGdmaInitStruct, sp_str, (IRQ_FUN)i2s_tdm_tx_isr, (u8 *)pbuf, sp_tx_info.tx_page_size); - AUDIO_SP_TXGDMA_Init(i2s_index, GDMA_EXT, &sp_str_ext->SpTxGdmaInitStructExt, sp_str_ext, (IRQ_FUN)i2s_tdm_tx_isr_ext, (u8 *)pbuf_1, sp_tx_info.tx_page_size); } #endif @@ -844,36 +992,39 @@ static void i2s_tdm_rx_isr(void *sp_data) uint8_t i2s_index = gs->i2s_idx; - /* Clear Pending ISR */ + /* Clear Pending ISR*/ GDMA_ClearINT(GDMA_InitStruct->GDMA_Index, GDMA_InitStruct->GDMA_ChNum); - i2s_release_rx_page(i2s_index); + GDMA_Cmd(GDMA_InitStruct->GDMA_Index, GDMA_InitStruct->GDMA_ChNum, DISABLE); + GDMA_ChnlFree(GDMA_InitStruct->GDMA_Index, GDMA_InitStruct->GDMA_ChNum); + + /* set flag to inform os layer */ + DMA_Done = 1; - /* Read data */ pRX_BLOCK prx_block = &(sp_rx_info.rx_block[sp_rx_info.rx_usr_cnt]); - DCache_CleanInvalidate((uint32_t)prx_block->rx_addr, sp_rx_info.rx_page_size); - dbg("I2S: ISR0! %p\n", (void *)(uint32_t)prx_block->rx_addr); - I2SUserCB.RxCCB((uint32_t)NULL, (void *)(uint32_t)prx_block->rx_addr); - i2s_get_free_rx_page(i2s_index); + uint16_t* p = (uint16_t*)prx_block->rx_addr; + DCache_Invalidate((uint32_t)p, sp_rx_info.rx_request_length); + I2SUserCB.RxCCB((uint32_t)I2SUserCB.RxCBId, NULL); } static void i2s_tdm_rx_isr_ext(void *sp_data) { - SP_GDMA_STRUCT_EXT *gs = sp_data; + SP_GDMA_STRUCT *gs = sp_data; PGDMA_InitTypeDef GDMA_InitStruct; GDMA_InitStruct = &(gs->SpRxGdmaInitStructExt); uint8_t i2s_index = gs->i2s_idx; - dbg("I2S: ISR1!\n"); - - /* Clear Pending ISR */ + /* Clear Pending ISR*/ GDMA_ClearINT(GDMA_InitStruct->GDMA_Index, GDMA_InitStruct->GDMA_ChNum); - i2s_release_rx_page(i2s_index); + GDMA_Cmd(GDMA_InitStruct->GDMA_Index, GDMA_InitStruct->GDMA_ChNum, DISABLE); + GDMA_ChnlFree(GDMA_InitStruct->GDMA_Index, GDMA_InitStruct->GDMA_ChNum); - /* Read data */ - pRX_BLOCK prx_block = &(sp_rx_info.rx_block[sp_rx_info.rx_usr_cnt]); - DCache_CleanInvalidate((uint32_t)prx_block->rx_addr, sp_rx_info.rx_page_size); - I2SUserCB.RxCCB((uint32_t)NULL, (void *)(uint32_t)prx_block->rx_addr); - i2s_get_free_rx_page(i2s_index); + /* set flag to inform os layer */ + DMA_Done_1 = 1; + + pRX_BLOCK prx_block = &(sp_rx_info_ext.rx_block[sp_rx_info_ext.rx_usr_cnt]); + uint16_t* p = (uint16_t*)prx_block->rx_addr; + DCache_Invalidate((uint32_t)p, sp_rx_info_ext.rx_request_length); + I2SUserCB.RxCCB((uint32_t)I2SUserCB.RxCBId, NULL); } /** @@ -889,19 +1040,11 @@ void i2s_tdm_rx_irq_handler(i2s_t *obj, i2s_irq_handler handler, uint32_t id) uint8_t i2s_index = obj->i2s_idx; SP_GDMA_STRUCT *sp_str = &SPGdmaStruct; - SP_GDMA_STRUCT_EXT *sp_str_ext = &SPGdmaStructExt; - u32 *pbuf, pbuf_1; sp_str->i2s_idx = i2s_index; /* Store I2S index */ - sp_str_ext->i2s_idx = i2s_index; I2SUserCB.RxCCB = handler; I2SUserCB.RxCBId = id; - - pbuf = (u32 *)i2s_get_free_rx_page(i2s_index); - pbuf_1 = (u32 *)i2s_get_free_rx_page(i2s_index); - AUDIO_SP_RXGDMA_Init(i2s_index, GDMA_INT, &sp_str->SpRxGdmaInitStruct, sp_str, (IRQ_FUN)i2s_tdm_rx_isr, (u8 *)pbuf, sp_rx_info.rx_page_size); - AUDIO_SP_RXGDMA_Init(i2s_index, GDMA_EXT, &sp_str_ext->SpRxGdmaInitStructExt, sp_str_ext, (IRQ_FUN)i2s_tdm_rx_isr_ext, (u8 *)pbuf_1, sp_rx_info.rx_page_size); } #endif @@ -924,17 +1067,12 @@ void i2s_tdm_enable(i2s_t *obj) void i2s_tdm_disable(i2s_t *obj, bool is_suspend) { SP_GDMA_STRUCT *l_SPGdmaStruct = &SPGdmaStruct; - SP_GDMA_STRUCT_EXT *l_SPGdmaStructExt = &SPGdmaStructExt; if (obj->direction == I2S_DIR_TX) { GDMA_ClearINT(l_SPGdmaStruct->SpTxGdmaInitStruct.GDMA_Index, l_SPGdmaStruct->SpTxGdmaInitStruct.GDMA_ChNum); GDMA_Abort(l_SPGdmaStruct->SpTxGdmaInitStruct.GDMA_Index, l_SPGdmaStruct->SpTxGdmaInitStruct.GDMA_ChNum); GDMA_ChnlFree(l_SPGdmaStruct->SpTxGdmaInitStruct.GDMA_Index, l_SPGdmaStruct->SpTxGdmaInitStruct.GDMA_ChNum); - GDMA_ClearINT(l_SPGdmaStructExt->SpTxGdmaInitStructExt.GDMA_Index, l_SPGdmaStructExt->SpTxGdmaInitStructExt.GDMA_ChNum); - GDMA_Abort(l_SPGdmaStructExt->SpTxGdmaInitStructExt.GDMA_Index, l_SPGdmaStructExt->SpTxGdmaInitStructExt.GDMA_ChNum); - GDMA_ChnlFree(l_SPGdmaStructExt->SpTxGdmaInitStructExt.GDMA_Index, l_SPGdmaStructExt->SpTxGdmaInitStructExt.GDMA_ChNum); - AUDIO_SP_DmaCmd(obj->i2s_idx, DISABLE); AUDIO_SP_TXStart(obj->i2s_idx, DISABLE); if (is_suspend) { @@ -944,9 +1082,6 @@ void i2s_tdm_disable(i2s_t *obj, bool is_suspend) GDMA_ClearINT(l_SPGdmaStruct->SpRxGdmaInitStruct.GDMA_Index, l_SPGdmaStruct->SpRxGdmaInitStruct.GDMA_ChNum); GDMA_Cmd(l_SPGdmaStruct->SpRxGdmaInitStruct.GDMA_Index, l_SPGdmaStruct->SpRxGdmaInitStruct.GDMA_ChNum, DISABLE); - GDMA_ClearINT(l_SPGdmaStructExt->SpRxGdmaInitStructExt.GDMA_Index, l_SPGdmaStructExt->SpRxGdmaInitStructExt.GDMA_ChNum); - GDMA_Cmd(l_SPGdmaStructExt->SpRxGdmaInitStructExt.GDMA_Index, l_SPGdmaStructExt->SpRxGdmaInitStructExt.GDMA_ChNum, DISABLE); - AUDIO_SP_DmaCmd(obj->i2s_idx, DISABLE); AUDIO_SP_RXStart(obj->i2s_idx, DISABLE); } @@ -981,7 +1116,6 @@ void i2s_tdm_disable(i2s_t *obj, bool is_suspend) */ void i2s_tdm_set_param(i2s_t *obj, int channel_num, int rate, int word_len) { - lldbg("tdm_set_param\n"); uint32_t clock_mode = 0; assert_param(IS_SP_CHN_NUM(obj->channel_num)); @@ -1016,14 +1150,16 @@ void i2s_tdm_set_param(i2s_t *obj, int channel_num, int rate, int word_len) SP_InitStruct.SP_SelClk = clock_mode; AUDIO_SP_Init(obj->i2s_idx, obj->direction, &SP_InitStruct); - dbg("SP_BClk: %ld Hz\n", SP_InitStruct.SP_Bclk); + /* setup the role of the SPORT */ AUDIO_SP_SetMasterSlave(obj->i2s_idx, obj->role); + + /* stop the MCLK, it should only start when RESUME is called */ + AUDIO_SP_SetMclk(obj->i2s_idx, DISABLE); + + /* apply division on mclk if master only */ if (obj->role == MASTER) { - //AUDIO_SP_SetMclk(obj->i2s_idx, ENABLE); AUDIO_SP_SetMclkDiv(obj->i2s_idx, 0); - } else { - //AUDIO_SP_SetMclk(obj->i2s_idx, DISABLE); } } @@ -1035,7 +1171,6 @@ void i2s_tdm_set_param(i2s_t *obj, int channel_num, int rate, int word_len) void i2s_tdm_deinit(i2s_t *obj) { SP_GDMA_STRUCT *l_SPGdmaStruct = &SPGdmaStruct; - SP_GDMA_STRUCT_EXT *l_SPGdmaStructExt = &SPGdmaStructExt; if (obj->i2s_idx == I2S2) { RCC_PeriphClockCmd(APBPeriph_SPORT2, APBPeriph_SPORT2_CLOCK, DISABLE); @@ -1047,10 +1182,6 @@ void i2s_tdm_deinit(i2s_t *obj) GDMA_Cmd(l_SPGdmaStruct->SpTxGdmaInitStruct.GDMA_Index, l_SPGdmaStruct->SpTxGdmaInitStruct.GDMA_ChNum, DISABLE); GDMA_ChnlFree(l_SPGdmaStruct->SpTxGdmaInitStruct.GDMA_Index, l_SPGdmaStruct->SpTxGdmaInitStruct.GDMA_ChNum); - GDMA_ClearINT(l_SPGdmaStructExt->SpTxGdmaInitStructExt.GDMA_Index, l_SPGdmaStructExt->SpTxGdmaInitStructExt.GDMA_ChNum); - GDMA_Cmd(l_SPGdmaStructExt->SpTxGdmaInitStructExt.GDMA_Index, l_SPGdmaStructExt->SpTxGdmaInitStructExt.GDMA_ChNum, DISABLE); - GDMA_ChnlFree(l_SPGdmaStructExt->SpTxGdmaInitStructExt.GDMA_Index, l_SPGdmaStructExt->SpTxGdmaInitStructExt.GDMA_ChNum); - AUDIO_SP_Unregister(obj->i2s_idx, obj->direction); AUDIO_SP_Deinit(obj->i2s_idx, obj->direction); } @@ -1083,8 +1214,6 @@ void i2s_tdm_init(i2s_t *obj, PinName sck, PinName ws, PinName sd_tx, PinName sd RCC_PeriphClockCmd(APBPeriph_SPORT3, APBPeriph_SPORT3_CLOCK, ENABLE); } - RCC_PeriphClockCmd(APBPeriph_AUDIO, APBPeriph_CLOCK_NULL, ENABLE); - Pinmux_Config(mck, pin_func); Pinmux_Config(sck, pin_func); Pinmux_Config(ws, pin_func); From 3c3fff55bbeb73783d507db9bf93664f02840fcb Mon Sep 17 00:00:00 2001 From: lhenry-realtek <176550561+lhenry-realtek@users.noreply.github.com> Date: Tue, 31 Dec 2024 14:14:17 +0800 Subject: [PATCH 6/8] os/arch/arm/src/amebasmart: Update pinmux for PoC based on discussion I2C2 pins (PB10, PB11) are used instead of existing I2C0, pinmux change is updated. Verified working on TDM board for PoC --- os/arch/arm/src/amebasmart/amebasmart_i2c.c | 6 ++++++ os/arch/arm/src/amebasmart/amebasmart_i2s.c | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/os/arch/arm/src/amebasmart/amebasmart_i2c.c b/os/arch/arm/src/amebasmart/amebasmart_i2c.c index 3e36565cc1..4a276be08a 100644 --- a/os/arch/arm/src/amebasmart/amebasmart_i2c.c +++ b/os/arch/arm/src/amebasmart/amebasmart_i2c.c @@ -376,8 +376,14 @@ static const struct amebasmart_i2c_config_s amebasmart_i2c2_config = { //.busy_idle = CONFIG_I2C2_BUSYIDLE, //.filtscl = CONFIG_I2C2_FILTSCL, //.filtsda = CONFIG_I2C2_FILTSDA, +#ifdef CONFIG_AMEBASMART_I2S_TDM + .scl_pin = PB_11, + .sda_pin = PB_10, +#else .scl_pin = PA_29, .sda_pin = PA_28, +#endif + #ifndef CONFIG_I2C_SLAVE .mode = AMEBASMART_I2C_MASTER, #else diff --git a/os/arch/arm/src/amebasmart/amebasmart_i2s.c b/os/arch/arm/src/amebasmart/amebasmart_i2s.c index 327949b9ce..d2c14e3e7e 100644 --- a/os/arch/arm/src/amebasmart/amebasmart_i2s.c +++ b/os/arch/arm/src/amebasmart/amebasmart_i2s.c @@ -269,7 +269,7 @@ static const struct amebasmart_i2s_config_s amebasmart_i2s2_tdm_config = { .i2s_mclk_pin = PB_22, // PU .i2s_sclk_pin = PB_21, // PU .i2s_ws_pin = PA_16, // PU - .i2s_sd_tx_pin = PB_10, // PU + .i2s_sd_tx_pin = NC, // NC for TX! .i2s_sd_rx_pin = PB_29, // PU (REWORK) .i2s_idx = I2S_NUM_2, From 56d9cba0b33ea46cbacd6115f66832e621fc2cf7 Mon Sep 17 00:00:00 2001 From: lhenry-realtek <176550561+lhenry-realtek@users.noreply.github.com> Date: Thu, 2 Jan 2025 14:45:33 +0800 Subject: [PATCH 7/8] os/arch/arm/src/amebasmart: Support PM for I2S TDM in RX mode TDM RX will keep PM lock until DMA transfer is complete, then release the lock in ISR Verified on TDM board by removing delay causing tash to sleep instantly, then use pm_resume to enable board to sleep. While asleep, run sensor test command --- os/arch/arm/src/amebasmart/amebasmart_i2s.c | 22 +++++++++++++++++-- .../mbed/targets/hal/rtl8730e/i2s_api.c | 13 ++++++++--- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/os/arch/arm/src/amebasmart/amebasmart_i2s.c b/os/arch/arm/src/amebasmart/amebasmart_i2s.c index d2c14e3e7e..2d4d1237a4 100644 --- a/os/arch/arm/src/amebasmart/amebasmart_i2s.c +++ b/os/arch/arm/src/amebasmart/amebasmart_i2s.c @@ -1202,6 +1202,11 @@ static int i2s_tdm_receive(struct i2s_dev_s *dev, struct ap_buffer_s *apb, i2s_c bfcontainer->apb = apb; bfcontainer->result = -EBUSY; + /* Hold PM lock, this will be cleared when the RX DMA callback is ready */ +#ifdef CONFIG_PM + bsp_pm_domain_control(BSP_I2S_DRV, 1); +#endif + /* Prepare DMA microcode (CHANGE THIS! THIS SHOULD SETUP DMA BUFFERS BEFORE CLOCK STARTS) */ i2s_rxdma_prep(priv, bfcontainer); @@ -1314,6 +1319,11 @@ void i2s_transfer_rx_handleirq(void *data, char *pbuf) i2s_rxdma_callback(priv, OK); } } + +#ifdef CONFIG_PM + /* Release PM lock */ + bsp_pm_domain_control(BSP_I2S_DRV, 0); +#endif } #else @@ -2364,12 +2374,20 @@ static uint32_t rtk_i2s_resume(uint32_t expected_idle_time, void *param) (void)expected_idle_time; (void)param; +#ifdef CONFIG_AMEBASMART_I2S_TDM +#define I2S_INITIALIZE amebasmart_i2s_tdm_initialize +#else +#define I2S_INITIALIZE amebasmart_i2s_initialize +#endif + /* For PG Sleep, I2S HW will be lost power, thus a reinitialization is required here */ #ifdef CONFIG_AMEBASMART_I2S2 - (void)amebasmart_i2s_initialize(I2S_NUM_2, I2S_REINIT); + //(void)amebasmart_i2s_initialize(I2S_NUM_2, I2S_REINIT); + (void)I2S_INITIALIZE(I2S_NUM_2, I2S_REINIT); #endif #ifdef CONFIG_AMEBASMART_I2S3 - (void)amebasmart_i2s_initialize(I2S_NUM_3, I2S_REINIT); + //(void)amebasmart_i2s_initialize(I2S_NUM_3, I2S_REINIT); + (void)I2S_INITIALIZE(I2S_NUM_3, I2S_REINIT); #endif return 1; diff --git a/os/board/rtl8730e/src/component/mbed/targets/hal/rtl8730e/i2s_api.c b/os/board/rtl8730e/src/component/mbed/targets/hal/rtl8730e/i2s_api.c index c1591c85f7..db89f73453 100755 --- a/os/board/rtl8730e/src/component/mbed/targets/hal/rtl8730e/i2s_api.c +++ b/os/board/rtl8730e/src/component/mbed/targets/hal/rtl8730e/i2s_api.c @@ -811,9 +811,6 @@ void i2s_disable(i2s_t *obj, bool is_suspend) AUDIO_SP_DmaCmd(obj->i2s_idx, DISABLE); AUDIO_SP_TXStart(obj->i2s_idx, DISABLE); - if (is_suspend) { - AUDIO_SP_Deinit(obj->i2s_idx, obj->direction); - } } else { GDMA_ClearINT(l_SPGdmaStruct->SpRxGdmaInitStruct.GDMA_Index, l_SPGdmaStruct->SpRxGdmaInitStruct.GDMA_ChNum); GDMA_Cmd(l_SPGdmaStruct->SpRxGdmaInitStruct.GDMA_Index, l_SPGdmaStruct->SpRxGdmaInitStruct.GDMA_ChNum, DISABLE); @@ -821,6 +818,10 @@ void i2s_disable(i2s_t *obj, bool is_suspend) AUDIO_SP_DmaCmd(obj->i2s_idx, DISABLE); AUDIO_SP_RXStart(obj->i2s_idx, DISABLE); } + + if (is_suspend) { + AUDIO_SP_Deinit(obj->i2s_idx, obj->direction); + } } /** @@ -997,6 +998,8 @@ static void i2s_tdm_rx_isr(void *sp_data) GDMA_Cmd(GDMA_InitStruct->GDMA_Index, GDMA_InitStruct->GDMA_ChNum, DISABLE); GDMA_ChnlFree(GDMA_InitStruct->GDMA_Index, GDMA_InitStruct->GDMA_ChNum); + i2s_release_rx_page(i2s_index); + /* set flag to inform os layer */ DMA_Done = 1; @@ -1018,6 +1021,8 @@ static void i2s_tdm_rx_isr_ext(void *sp_data) GDMA_Cmd(GDMA_InitStruct->GDMA_Index, GDMA_InitStruct->GDMA_ChNum, DISABLE); GDMA_ChnlFree(GDMA_InitStruct->GDMA_Index, GDMA_InitStruct->GDMA_ChNum); + i2s_release_rx_page(i2s_index); + /* set flag to inform os layer */ DMA_Done_1 = 1; @@ -1118,6 +1123,8 @@ void i2s_tdm_set_param(i2s_t *obj, int channel_num, int rate, int word_len) { uint32_t clock_mode = 0; + lldbg("in TDM set_param!\n"); + assert_param(IS_SP_CHN_NUM(obj->channel_num)); assert_param(obj->channel_num == SP_CH_STEREO); From a732e88c1db69a66334bc94e5e5ea3c9a91868c4 Mon Sep 17 00:00:00 2001 From: lhenry-realtek <176550561+lhenry-realtek@users.noreply.github.com> Date: Fri, 3 Jan 2025 11:12:28 +0800 Subject: [PATCH 8/8] os/board/rtl8730e: convert hardcoded parameters to increase configurability of TDM Parameters to setup TDM are now configured from amebasmart_i2s.c OS layer, there is currently no error handling if clock setting is invalid MCLK in i2s_clock_select is also currently fixed until a better way to configure it is found --- .../mbed/targets/hal/rtl8730e/i2s_api.c | 34 ++++++++----------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/os/board/rtl8730e/src/component/mbed/targets/hal/rtl8730e/i2s_api.c b/os/board/rtl8730e/src/component/mbed/targets/hal/rtl8730e/i2s_api.c index db89f73453..512ceca4bf 100755 --- a/os/board/rtl8730e/src/component/mbed/targets/hal/rtl8730e/i2s_api.c +++ b/os/board/rtl8730e/src/component/mbed/targets/hal/rtl8730e/i2s_api.c @@ -516,15 +516,12 @@ static uint32_t i2s_clock_select(i2s_t *obj) Init_Params.sport_mclk_fixed_max = (uint32_t) NULL; #if defined(CONFIG_AMEBASMART_I2S_TDM) + /* TODO: modify this for configurability */ Init_Params.chn_len = SP_CL_16; Init_Params.chn_cnt = (obj->fifo_num + 1) * 2; Init_Params.codec_multiplier_with_rate = 0; Init_Params.sport_mclk_fixed_max = (uint32_t) 12288000; //12.288MHz #endif -//#else -// Init_Params.codec_multiplier_with_rate = 256; -// Init_Params.sport_mclk_fixed_max = (uint32_t) NULL; -//#endif Audio_Clock_Choose(PLL_CLK, &Init_Params, &Clock_Params); obj->clock = Clock_Params.Clock; @@ -972,7 +969,6 @@ void i2s_tdm_tx_irq_handler(i2s_t *obj, i2s_irq_handler handler, uint32_t id) u32 *pbuf, pbuf_1; sp_str->i2s_idx = i2s_index; /* Store I2S index */ - sp_str_ext->i2s_idx = i2s_index; I2SUserCB.TxCCB = handler; I2SUserCB.TxCBId = id; @@ -1125,9 +1121,6 @@ void i2s_tdm_set_param(i2s_t *obj, int channel_num, int rate, int word_len) lldbg("in TDM set_param!\n"); - assert_param(IS_SP_CHN_NUM(obj->channel_num)); - assert_param(obj->channel_num == SP_CH_STEREO); - clock_mode = i2s_clock_select(obj); /* Sport Deinit */ @@ -1138,22 +1131,25 @@ void i2s_tdm_set_param(i2s_t *obj, int channel_num, int rate, int word_len) AUDIO_SP_Reset(obj->i2s_idx); AUDIO_SP_StructInit(&SP_InitStruct); - if (obj->direction == I2S_DIR_TX) { - SP_InitStruct.SP_SelFIFO = SP_TX_FIFO8; - SP_InitStruct.SP_SelChLen = SP_TXCL_16; - SP_InitStruct.SP_SelWordLen = SP_TXWL_16; + SP_InitStruct.SP_SelFIFO = obj->fifo_num; // AUDIO_SPORT_T/Rx_FIFO + SP_InitStruct.SP_SelChLen = obj->channel_length; // AUDIO_SPORT_T/Rx_Channel_Length + SP_InitStruct.SP_SelWordLen = obj->word_length; // AUDIO_SPORT_Word_Length + SP_InitStruct.SP_SelI2SMonoStereo = obj->channel_num; // AUDIO_SPORT_Channel_Number + SP_InitStruct.SP_SR = obj->sampling_rate; // AUDIO_SPORT_Sample_Rate + + if (obj->fifo_num == SP_TX_FIFO8 || obj->fifo_num == SP_RX_FIFO8) { + SP_InitStruct.SP_SelTDM = I2S_TDM_8CH; + } else if (obj->fifo_num == SP_TX_FIFO6 || obj->fifo_num == SP_RX_FIFO6) { + SP_InitStruct.SP_SelTDM = I2S_TDM_6CH; + } else if (obj->fifo_num == SP_TX_FIFO4 || obj->fifo_num == SP_RX_FIFO4) { + SP_InitStruct.SP_SelTDM = I2S_TDM_4CH; } else { - SP_InitStruct.SP_SelFIFO = SP_RX_FIFO8; - SP_InitStruct.SP_SelChLen = SP_RXCL_16; - SP_InitStruct.SP_SelWordLen = SP_RXWL_16; + i2serr("Only 4/6/8 channel TDM supported!"); + return; } - /* hardcoded for now */ SP_InitStruct.SP_SetMultiIO = SP_RX_MULTIIO_DIS; SP_InitStruct.SP_SelDataFormat = SP_DF_I2S; - SP_InitStruct.SP_SelI2SMonoStereo = obj->channel_num; - SP_InitStruct.SP_SelTDM = I2S_TDM_8CH; - SP_InitStruct.SP_SR = SP_16K; SP_InitStruct.SP_SelClk = clock_mode; AUDIO_SP_Init(obj->i2s_idx, obj->direction, &SP_InitStruct);