diff --git a/os/arch/arm/src/amebasmart/amebasmart_i2c.c b/os/arch/arm/src/amebasmart/amebasmart_i2c.c index 2a9e92bdcf..4a276be08a 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, @@ -371,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 d1a495b33c..2d4d1237a4 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; + 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) + i2s_irq_handler rx_isr_handler_ext; +#endif #endif i2s_err_cb_t err_cb; /* registered error callback function */ @@ -195,11 +206,16 @@ 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)*/ + + 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 */ @@ -222,11 +238,14 @@ 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, .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 +258,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, // PU + .i2s_sclk_pin = PB_21, // PU + .i2s_ws_pin = PA_16, // PU + .i2s_sd_tx_pin = NC, // NC for TX! + .i2s_sd_rx_pin = PB_29, // PU (REWORK) + + .i2s_idx = I2S_NUM_2, + .rxenab = 1, + .txenab = 0, + .tdmenab = 1, +}; +#endif + /**************************************************************************** * Private Function Prototypes ****************************************************************************/ @@ -308,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 ****************************************************************************/ @@ -340,6 +381,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_tdm_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}; /**************************************************************************** @@ -800,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; } @@ -814,13 +890,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; } @@ -832,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); @@ -950,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); } } } @@ -1039,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"); @@ -1068,7 +1148,6 @@ 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"); /* Start transfer */ ret = i2s_rx_start(priv); @@ -1084,21 +1163,174 @@ static int i2s_receive(struct i2s_dev_s *dev, struct ap_buffer_s *apb, i2s_callb #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; + + /* 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); + + 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; + +#else + i2serr("ERROR: I2S has no receiver\n"); + UNUSED(priv); + return -ENOSYS; +#endif +} + /*! - * @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) { struct amebasmart_i2s_s *priv = (struct amebasmart_i2s_s *)data; i2s_t *obj = priv->i2s_object; +#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); + } + } + +#ifdef CONFIG_PM + /* Release PM lock */ + bsp_pm_domain_control(BSP_I2S_DRV, 0); +#endif + } +#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) @@ -1413,13 +1645,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 +1694,36 @@ 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) { + lldbg("XX %d\n", priv->tdmenab); + 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 +1749,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 +1904,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 * @@ -1638,6 +1937,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; @@ -1713,6 +2020,34 @@ 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); + priv->tdmenab = 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_RXCL_16; + + /* FIFO size */ + priv->i2s_object->fifo_num = SP_RX_FIFO8; // for 8ch tdm + } +#endif } /**************************************************************************** @@ -1768,7 +2103,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 +2214,118 @@ 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 + + //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_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 + + /* 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 +2338,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 +2346,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; @@ -1926,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 11c027b24e..6bbee7fe22 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. @@ -389,6 +514,15 @@ static uint32_t i2s_clock_select(i2s_t *obj) 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) + /* 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 + Audio_Clock_Choose(PLL_CLK, &Init_Params, &Clock_Params); obj->clock = Clock_Params.Clock; @@ -589,7 +723,8 @@ void i2s_send_page(i2s_t *obj, uint32_t *pbuf) #ifdef CONFIG_AMEBASMART_I2S_RX void i2s_recv_page(i2s_t *obj) { - uint8_t i2s_index = obj->i2s_idx; + 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]); @@ -599,6 +734,32 @@ void i2s_recv_page(i2s_t *obj) 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]); + + 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; + } + + /* 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 /** @@ -608,6 +769,14 @@ 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) { + 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,23 +794,31 @@ void i2s_disable(i2s_t *obj, bool is_suspend) { SP_GDMA_STRUCT *l_SPGdmaStruct = &SPGdmaStruct; +#if defined(CONFIG_AMEBASMART_I2S_TDM) + // 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); + 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); 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); + } } /** @@ -661,6 +838,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; + + lldbg("in pause\n"); + + // turn on MCLK if master + if (obj->role == MASTER) { + AUDIO_SP_SetMclk(obj->i2s_idx, DISABLE); + } + + if (obj->direction == I2S_DIR_TX) { + GDMA_ClearINT(l_SPGdmaStruct->SpTxGdmaInitStruct.GDMA_Index, l_SPGdmaStruct->SpTxGdmaInitStruct.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_SPGdmaStruct->SpRxGdmaInitStructExt.GDMA_Index, l_SPGdmaStruct->SpRxGdmaInitStructExt.GDMA_ChNum); + AUDIO_SP_DmaCmd(obj->i2s_idx, DISABLE); + AUDIO_SP_RXStart(obj->i2s_idx, DISABLE); + } +} +#endif + /** * @brief Resume I2S interrupt and function. * @param obj: I2S object defined in application software. @@ -677,7 +879,46 @@ 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 *sp_str = &SPGdmaStruct; + + u32 *pbuf_int, pbuf_ext; + u8 res0, res1; + + // 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 { + 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); + } +} +#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; @@ -698,7 +939,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); @@ -712,44 +953,6 @@ static void i2s_tdm_tx_isr_ext(void *sp_data) I2SUserCB.TxCCB(I2SUserCB.TxCBId, (char*)pbuf); } -static void i2s_tdm_rx_isr(void *sp_data) -{ - SP_GDMA_STRUCT *gs = sp_data; - PGDMA_InitTypeDef GDMA_InitStruct; - GDMA_InitStruct = &(gs->SpRxGdmaInitStruct); - - uint8_t i2s_index = gs->i2s_idx; - - /* Clear Pending ISR */ - GDMA_ClearINT(GDMA_InitStruct->GDMA_Index, GDMA_InitStruct->GDMA_ChNum); - i2s_release_rx_page(i2s_index); - - /* 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); -} - -static void i2s_tdm_rx_isr_ext(void *sp_data) -{ - SP_GDMA_STRUCT_EXT *gs = sp_data; - PGDMA_InitTypeDef GDMA_InitStruct; - GDMA_InitStruct = &(gs->SpRxGdmaInitStructExt); - - uint8_t i2s_index = gs->i2s_idx; - - /* Clear Pending ISR */ - GDMA_ClearINT(GDMA_InitStruct->GDMA_Index, GDMA_InitStruct->GDMA_ChNum); - i2s_release_rx_page(i2s_index); - - /* 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); -} - /** * @brief Register TX interrupt handler. * @param obj: I2S object defined in application software. @@ -763,7 +966,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 */ @@ -774,8 +976,57 @@ 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 + +#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; + PGDMA_InitTypeDef GDMA_InitStruct; + GDMA_InitStruct = &(gs->SpRxGdmaInitStruct); + + uint8_t i2s_index = gs->i2s_idx; + + /* Clear Pending ISR*/ + GDMA_ClearINT(GDMA_InitStruct->GDMA_Index, GDMA_InitStruct->GDMA_ChNum); + 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; + + pRX_BLOCK prx_block = &(sp_rx_info.rx_block[sp_rx_info.rx_usr_cnt]); + 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 *gs = sp_data; + PGDMA_InitTypeDef GDMA_InitStruct; + GDMA_InitStruct = &(gs->SpRxGdmaInitStructExt); + + uint8_t i2s_index = gs->i2s_idx; + /* Clear Pending ISR*/ + GDMA_ClearINT(GDMA_InitStruct->GDMA_Index, GDMA_InitStruct->GDMA_ChNum); + 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; + + 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); } /** @@ -791,21 +1042,15 @@ 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 + /** * @brief Enable I2S interrupt and function. * @param obj: I2S object defined in application software. @@ -824,17 +1069,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) { @@ -844,9 +1084,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); } @@ -883,8 +1120,7 @@ void i2s_tdm_set_param(i2s_t *obj, int channel_num, int rate, int word_len) { uint32_t clock_mode = 0; - assert_param(IS_SP_CHN_NUM(obj->channel_num)); - assert_param(obj->channel_num == SP_CH_STEREO); + lldbg("in TDM set_param!\n"); clock_mode = i2s_clock_select(obj); @@ -896,31 +1132,38 @@ 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); + + /* 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); } } @@ -932,7 +1175,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); @@ -944,10 +1186,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); }