/* ISR */ static void i2s_isr(void) { uint32_t stat; // Determine interrupt source stat = MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, SOC_I2S_STAT); // Check for errors if (stat & (1 << SOC_I2S_STAT_TDATA_UNDERR)) { if (i2s_info->cfg[I2S_CHANNEL_TX].cb_err) { i2s_info->cfg[I2S_CHANNEL_TX].cb_err_arg=(void *)stat; i2s_info->cfg[I2S_CHANNEL_TX].cb_err(i2s_info->cfg[I2S_CHANNEL_TX].cb_err_arg); } i2s_disable(I2S_CHANNEL_TX); } if (stat & (1 << SOC_I2S_STAT_RDATA_OVRERR)) { if (i2s_info->cfg[I2S_CHANNEL_RX].cb_err) { i2s_info->cfg[I2S_CHANNEL_RX].cb_err_arg=(void *)stat; i2s_info->cfg[I2S_CHANNEL_RX].cb_err(i2s_info->cfg[I2S_CHANNEL_RX].cb_err_arg); } i2s_disable(I2S_CHANNEL_RX); } return; }
static void i2s_dma_cb_done(void *num) { uint8_t channel = (uint32_t)num; uint32_t reg; if(channel == I2S_CHANNEL_TX) { if((0x00200000 & MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, SOC_I2S_CTRL)) && !(0x18000000 & MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, SOC_I2S_CTRL))) { for(int i = 0; i < 4; ++i) MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, SOC_I2S_DATA_REG) = 0x0; } do { reg = MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, i2s_reg_map[channel].fifo_stat); } while(reg & 0x000000FF); } if (i2s_info->cfg[channel].cb_done) { i2s_info->cfg[channel].cb_done(i2s_info->cfg[channel].cb_done_arg); } i2s_disable(channel); return; }
/* * apl_i2s_send - Send audio samples to I2s controller. * @me: I2sOps structure * @data: Audio samples * @length: Number of samples * * Send audio samples to I2s controller. */ static int apl_i2s_send(I2sOps *me, unsigned int *data, unsigned int length) { int i; uint64_t start; AplI2s *bus = container_of(me, AplI2s, ops); struct AplI2sRegs *i2s_reg = bus->regs; if (!bus->initialized) { if (apl_i2s_init(bus)) return -1; bus->initialized = 1; } if (length < LPE_SSP_FIFO_SIZE) { printf("%s : Invalid data size\n", __func__); return -1; } gpio_set(bus->sdmode_gpio, 1); for (i = 0; i < LPE_SSP_FIFO_SIZE; i++) writel(*data++, &i2s_reg->ssdr); i2s_enable(bus->regs); length -= LPE_SSP_FIFO_SIZE; while (length > 0) { start = timer_us(0); if (read_SSSR(bus->regs) & 0x4) { writel(*data++, &i2s_reg->ssdr); length--; } else { if (timer_us(start) > 100000) { i2s_disable(bus->regs); gpio_set(bus->sdmode_gpio, 0); printf("I2S Transfer Timeout\n"); return -1; } } } mdelay(1); gpio_set(bus->sdmode_gpio, 0); i2s_disable(bus->regs); return 0; }
void audio_disable(void) { if (audio_drv.enabled) { audio_drv.stream_enabled = false; i2s_disable(); audio_drv.enabled = false; } }
/* * apl_i2s_init - Initialize I2s. * @bus: i2s config structure * * Initialize I2s. */ static int apl_i2s_init(AplI2s *bus) { if (enable_DSP_SSP(bus)) return -1; i2s_disable(bus->regs); set_ssp_i2s_hw(bus->regs, bus->shim, bus->settings, bus->bits_per_sample); return 0; }
/* DMA Callbacks */ static void i2s_dma_cb_err(void *num) { uint8_t channel = (uint32_t)num; if (i2s_info->cfg[channel].cb_err) { i2s_info->cfg[channel].cb_err(i2s_info->cfg[channel].cb_err_arg); } i2s_disable(channel); return; }
/* Driver API */ DRIVER_API_RC soc_i2s_init() { int i; uint32_t reg; // Prep info struct for (i = 0; i < I2S_NUM_CHANNELS; i++) { i2s_info->en[i] = 0; i2s_info->cfgd[i] = 0; i2s_info->cfg[i].cb_done = NULL; i2s_info->cfg[i].cb_err = NULL; } // Enable global clock, use local clock gating per channel instead set_clock_gate(i2s_info->clk_gate_info, CLK_GATE_ON); // Setup ISR (and enable) SET_INTERRUPT_HANDLER(i2s_info->int_vector, i2s_interrupt_handler); SOC_UNMASK_INTERRUPTS(i2s_info->int_mask); // Set up control register reg = MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, SOC_I2S_CTRL); reg |= (1 << SOC_I2S_CTRL_TR_CFG_0); reg &= ~(1 << SOC_I2S_CTRL_TSYNC_LOOP_BACK); reg &= ~(1 << SOC_I2S_CTRL_RSYNC_LOOP_BACK); MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, SOC_I2S_CTRL) = reg; // Set the watermark levels MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, SOC_I2S_TFIFO_CTRL) &= 0xFFFCFFFF; MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, SOC_I2S_TFIFO_CTRL) |= (I2S_TFIFO_THR << SOC_I2S_TFIFO_CTRL_TAFULL_THRS); MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, SOC_I2S_RFIFO_CTRL) &= 0xFFFCFFFF; MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, SOC_I2S_RFIFO_CTRL) |= (I2S_RFIFO_THR << SOC_I2S_RFIFO_CTRL_RAFULL_THRS); // Enable global interrupt mask reg = MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, SOC_I2S_CID_CTRL); reg |= (1 << SOC_I2S_CID_CTRL_INTREQ_MASK); MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, SOC_I2S_CID_CTRL) = reg; // Initially, have all channels disabled for (i = 0; i < I2S_NUM_CHANNELS; i++) { i2s_disable(i); } return DRV_RC_OK; }
DRIVER_API_RC soc_i2s_stop_stream(void) { uint8_t channel = I2S_CHANNEL_TX; uint32_t save; if (channel >= I2S_NUM_CHANNELS) { return DRV_RC_FAIL; } else if (!(i2s_info->en[channel])) { return DRV_RC_FAIL; } save = interrupt_lock(); i2s_disable(channel); interrupt_unlock(save); return DRV_RC_OK; }
static void i2s_dma_cb_done(void *num) { uint8_t channel = (uint32_t)num; uint32_t reg; if(channel == I2S_CHANNEL_TX) { do { reg = MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, i2s_reg_map[channel].fifo_stat); } while(reg & 0x000000FF); } if (i2s_info->cfg[channel].cb_done) { i2s_info->cfg[channel].cb_done(i2s_info->cfg[channel].cb_done_arg); } i2s_disable(channel); return; }
void audio_reset(void) { bool enable_stream; tracef("%s(): audio system reset ...", __func__); enable_stream = audio_drv.stream_enabled; audio_drv.stream_enabled = false; codec_hw_reset(); tlv320_reset(); if (audio_drv.enabled) i2s_disable(); tlv320_init(); if (audio_drv.enabled) i2s_enable(); audio_drv.stream_enabled = enable_stream; }
DRIVER_API_RC soc_i2s_stream(uint32_t *buf, uint32_t len, uint32_t num_bufs) { DRIVER_API_RC ret; uint8_t channel = I2S_CHANNEL_TX; uint32_t reg; uint32_t len_per_buf; int i; struct soc_dma_xfer_item *dma_list; // Check channel no in use and configured if (channel >= I2S_NUM_CHANNELS) { return DRV_RC_FAIL; } else if (i2s_info->en[channel] || !(i2s_info->cfgd[channel])) { return DRV_RC_FAIL; } // Get a DMA channel ret = soc_dma_acquire(&(i2s_info->dma_ch[channel])); if (ret != DRV_RC_OK) { return DRV_RC_FAIL; } // Enable the channel i2s_enable(channel); // Determine the length of a single buffer if (num_bufs == 0) { len_per_buf = len; } else { len_per_buf = len / num_bufs; } // Prep some configuration i2s_info->dma_cfg[channel].type = SOC_DMA_TYPE_MEM2PER; i2s_info->dma_cfg[channel].dest_interface = SOC_DMA_INTERFACE_I2S_TX; i2s_info->dma_cfg[channel].dest_step_count = 0; i2s_info->dma_cfg[channel].src_step_count = 0; i2s_info->dma_cfg[channel].xfer.dest.delta = SOC_DMA_DELTA_NONE; i2s_info->dma_cfg[channel].xfer.dest.width = SOC_DMA_WIDTH_32; i2s_info->dma_cfg[channel].xfer.dest.addr = (void *)(SOC_I2S_BASE + SOC_I2S_DATA_REG); i2s_info->dma_cfg[channel].xfer.src.delta = SOC_DMA_DELTA_INCR; i2s_info->dma_cfg[channel].xfer.src.width = SOC_DMA_WIDTH_32; if (num_bufs == 0) { i2s_info->dma_cfg[channel].cb_done = i2s_dma_cb_done; i2s_info->dma_cfg[channel].cb_done_arg = (void *)((uint32_t)channel); } else { i2s_info->dma_cfg[channel].cb_block = i2s_dma_cb_block; i2s_info->dma_cfg[channel].cb_block_arg = (void *)((uint32_t)channel); } i2s_info->dma_cfg[channel].cb_err = i2s_dma_cb_err; i2s_info->dma_cfg[channel].cb_err_arg = (void *)((uint32_t)channel); // Setup the linked list for (i = 0; i < ((num_bufs == 0) ? 1 : num_bufs); i++) { if (i == 0) { dma_list = &(i2s_info->dma_cfg[channel].xfer); } else { ret = soc_dma_alloc_list_item(&dma_list, dma_list); if (ret != DRV_RC_OK) { goto fail; } } dma_list->src.addr = (void *)(&(buf[i * (len_per_buf / sizeof(uint32_t))])); dma_list->size = len_per_buf / sizeof(uint32_t); } // Create a circular list if we are doing circular buffering if (num_bufs != 0) { dma_list->next = &(i2s_info->dma_cfg[channel].xfer); } // Setup and start the DMA engine ret = soc_dma_config(&(i2s_info->dma_ch[channel]), &(i2s_info->dma_cfg[channel])); if (ret != DRV_RC_OK) { goto fail; } ret = soc_dma_start_transfer(&(i2s_info->dma_ch[channel])); if (ret != DRV_RC_OK) { goto fail; } // Enable the channel and let it go! reg = MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, i2s_reg_map[channel].ctrl); reg |= (1 << (i2s_reg_map[channel].ctrl_en)); reg |= (1 << (i2s_reg_map[channel].ctrl_sync_rst)); MMIO_REG_VAL_FROM_BASE(SOC_I2S_BASE, i2s_reg_map[channel].ctrl) = reg; return DRV_RC_OK; fail: i2s_disable(channel); soc_dma_release(&(i2s_info->dma_ch[channel])); return DRV_RC_FAIL; }
/** * \brief Test audio data transfer and receive. * * \param test Current test case. */ static void run_i2s_test(const struct test_case *test) { uint32_t i; struct i2s_config config; struct i2s_dev_inst dev_inst; Pdc *p_i2sc_pdc; Pdc *p_i2sc_pdc2; pdc_packet_t pdc_i2sc_packet_tx, pdc_i2sc_packet_rx; pdc_packet_t pdc2_i2sc_packet_tx, pdc2_i2sc_packet_rx; /* Set the configuration */ i2s_get_config_defaults(&config); config.data_format = I2S_DATE_16BIT; config.fs_ratio = I2S_FS_RATE_256; config.loopback = true; i2s_init(&dev_inst, I2SC0, &config); /* Enable the I2SC module. */ i2s_enable(&dev_inst); /* Get pointer to I2SC PDC register base */ p_i2sc_pdc = i2s_get_pdc_base(&dev_inst); p_i2sc_pdc2 = (Pdc *)((uint32_t)p_i2sc_pdc + 0x100U); /* Initialize PDC data packet for transfer */ pdc_i2sc_packet_tx.ul_addr = (uint32_t) output_samples_left; pdc_i2sc_packet_tx.ul_size = SOUND_SAMPLES; pdc_i2sc_packet_rx.ul_addr = (uint32_t) input_samples_left; pdc_i2sc_packet_rx.ul_size = SOUND_SAMPLES; pdc2_i2sc_packet_tx.ul_addr = (uint32_t) output_samples_right; pdc2_i2sc_packet_tx.ul_size = SOUND_SAMPLES; pdc2_i2sc_packet_rx.ul_addr = (uint32_t) input_samples_right; pdc2_i2sc_packet_rx.ul_size = SOUND_SAMPLES; /* Configure PDC for data transfer */ pdc_tx_init(p_i2sc_pdc, &pdc_i2sc_packet_tx, NULL); pdc_rx_init(p_i2sc_pdc, &pdc_i2sc_packet_rx, NULL); pdc_tx_init(p_i2sc_pdc2, &pdc2_i2sc_packet_tx, NULL); pdc_rx_init(p_i2sc_pdc2, &pdc2_i2sc_packet_rx, NULL); /* Enable PDC transfers */ pdc_enable_transfer(p_i2sc_pdc, PERIPH_PTCR_TXTEN | PERIPH_PTCR_RXTEN); pdc_enable_transfer(p_i2sc_pdc2, PERIPH_PTCR_TXTEN | PERIPH_PTCR_RXTEN); /* Enable the functions */ i2s_enable_transmission(&dev_inst); i2s_enable_clocks(&dev_inst); /** * Since the transfer and receive timing is not under control, we * need adjust here the enable sequence and add some delay * functions if it's needed. */ for (i = 0; i < 0x8; i++) { input_samples_left[i] = i; } i2s_enable_reception(&dev_inst); /* Wait transfer complete */ while (!(i2s_get_status(&dev_inst) & I2SC_SR_RXBUFF)) { } /** * Wait a moment to let the PDC finish. The status bit is cleared * before all transfer finish. */ delay_us(10); /* Disable the PDC module. */ pdc_disable_transfer(p_i2sc_pdc, PERIPH_PTCR_RXTDIS| PERIPH_PTCR_TXTDIS); pdc_disable_transfer(p_i2sc_pdc2, PERIPH_PTCR_RXTDIS| PERIPH_PTCR_TXTDIS); /* Disable the I2SC module. */ i2s_disable(&dev_inst); /* Compare the data. */ for (i = 0; i < SOUND_SAMPLES; i++) { if ((input_samples_left[i] != output_samples_left[i]) || (input_samples_right[i] != output_samples_right[i])) { flag = false; } } test_assert_true(test, flag == true, "Audio data did not match!"); }