IndustrialDeviceController/Software/MT3620_IDC_RTApp/lib/I2S.c (392 lines of code) (raw):

/* Copyright (c) Codethink Ltd. All rights reserved. Licensed under the MIT License. */ #include "I2S.h" #include "NVIC.h" #include "mt3620/i2s.h" #include "mt3620/dma.h" #include <stdbool.h> #define I2S_BUFFER_SIZE 2048 // Each interface has two buffers per stream for double buffering. static __attribute__((section(".sysram"))) uint8_t I2S_Buffer[MT3620_I2S_COUNT][MT3620_I2S_STREAM_COUNT][I2S_BUFFER_SIZE] = { 0 }; typedef struct { bool enable; unsigned channels; bool (*callback)(void *buffer, uintptr_t size); } I2S_Settings; struct I2S { bool open; uint32_t id; I2S_Settings out; I2S_Settings in; }; static I2S context[MT3620_I2S_COUNT] = {0}; // TODO: Clean this up. #define I2S_PRIORITY 2 static inline unsigned I2S_UnitToID(Platform_Unit unit) { if ((unit < MT3620_UNIT_I2S0) || (unit > MT3620_UNIT_I2S1)) { return MT3620_I2S_COUNT; } return (unit - MT3620_UNIT_I2S0); } // There are some sample-rates implied by the datasheet but not officially supported // Enable this macro to test them #undef I2S_ALLOW_UNOFFICIAL_SAMPLE_RATE // Sample rates will be allowed if they're within this threshold #define I2S_SAMPLE_RATE_THRESH_PERCENT 5 static mt3620_i2s_sr_t I2S_SampleRate(unsigned target) { unsigned low = (target * (100 - I2S_SAMPLE_RATE_THRESH_PERCENT)) / 100; unsigned high = (target * (100 + I2S_SAMPLE_RATE_THRESH_PERCENT)) / 100; unsigned i; for (i = 0; i < MT3620_I2S_SR_COUNT; i++) { #ifndef I2S_ALLOW_UNOFFICIAL_SAMPLE_RATE if (!MT3620_I2S_SR_IS_OFFICIAL(i)) { continue; } #endif unsigned rate = MT3620_I2S_SR_CALC(i); if ((rate >= low) && (rate <= high)) { return i; } } return MT3620_I2S_SR_COUNT; } I2S *I2S_Open(Platform_Unit unit, unsigned mclk) { unsigned id = I2S_UnitToID(unit); if (id >= MT3620_I2S_COUNT) { return NULL; } if (context[id].open) { return NULL; } // TODO: Check if non-PLL'd 26M is actually core clock. mt3620_i2s_clk_sel_e ext_mclk_sel; switch (mclk) { case 0: case 16000000: ext_mclk_sel = MT3620_I2S_CLK_SEL_XPLL_16M; break; case 26000000: ext_mclk_sel = MT3620_I2S_CLK_SEL_XPLL_26M; break; default: return NULL; } // Reset & Clear FIFOs MT3620_I2S_FIELD_WRITE(id, soft_reset, glb_soft_rst, true ); MT3620_I2S_FIELD_WRITE(id, soft_reset, glb_soft_rst, false); mt3620_i2s_global_control_t global_control = { .mask = mt3620_i2s[id]->global_control }; global_control.en = false; global_control.dlfifo_en = false; global_control.ulfifo_en = false; global_control.engen_en = true; global_control.ext_io_ck = 1; global_control.ext = true; global_control.ext_lrsw = false; global_control.mclk_output_en = (mclk != 0); global_control.i2s_in_clk_en = true; global_control.i2s_out_clk_en = true; global_control.x26m_sel = true; // Select XPLL 26MHz as we assume it's more accurate. global_control.ext_bclk_inv = true; global_control.neg_cap = true; global_control.ext_mclk_sel = ext_mclk_sel; global_control.loopback = false; mt3620_i2s[id]->global_control = global_control.mask; context[id].id = id; context[id].open = true; context[id].out.enable = false; context[id].in.enable = false; return &context[id]; } void I2S_Close(I2S *handle) { if (!handle || !handle->open) { return; } // Disable UL & DL. MT3620_I2S_FIELD_WRITE(handle->id, dl_control, en, false); MT3620_I2S_FIELD_WRITE(handle->id, ul_control, en, false); mt3620_i2s_global_control_t global_control = { .mask = mt3620_i2s[handle->id]->global_control }; global_control.en = false; global_control.dlfifo_en = false; global_control.ulfifo_en = false; global_control.engen_en = false; global_control.mclk_output_en = false; global_control.i2s_in_clk_en = false; global_control.i2s_out_clk_en = false; mt3620_i2s[handle->id]->global_control = global_control.mask; // Disable DMA mt3620_dma_global->ch_en_clr = (1U << MT3620_I2S_DMA_TX(handle->id)); mt3620_dma_global->ch_en_clr = (1U << MT3620_I2S_DMA_RX(handle->id)); mt3620_i2s[handle->id]->dma_if_control = 0; handle->open = false; } static void I2S_OutputUpdate(I2S *handle) { if (!handle || !handle->open || !handle->out.enable) { return; } unsigned id = handle->id; volatile mt3620_dma_t * const dma = &mt3620_dma[MT3620_I2S_DMA_TX(id)]; unsigned size = MT3620_DMA_FIELD_READ(MT3620_I2S_DMA_TX(id), con, size); uintptr_t remain = (dma->ffsize - dma->ffcnt) << size; uintptr_t swptr = MT3620_DMA_FIELD_READ(MT3620_I2S_DMA_TX(id), swptr, swptr); // Handle buffer wrap-around. if ((swptr + remain) >= (dma->ffsize << size)) { uintptr_t partial = (dma->ffsize << size) - swptr; if (!handle->out.callback(&I2S_Buffer[handle->id][0][swptr], partial)) { return; } // Toggle the wrap bit and set swptr to zero. dma->swptr = (dma->swptr ^ 0x00010000) & 0xFFFF0000; swptr = 0; remain -= partial; } if ((remain > 0) && handle->out.callback( &I2S_Buffer[handle->id][0][swptr], remain)) { dma->swptr += remain; } MT3620_DMA_FIELD_WRITE(MT3620_I2S_DMA_TX(id), ackint, ack, 1); } int32_t I2S_Output( I2S *handle, I2S_Format format, unsigned channels, unsigned bits, unsigned rate, bool (*callback)(void *data, uintptr_t size)) { if (!handle) { return ERROR_PARAMETER; } if (!handle->open) { return ERROR_HANDLE_CLOSED; } unsigned id = handle->id; bool enable = (format != I2S_FORMAT_NONE); bool tdm = false; mt3620_i2s_sr_t sr = MT3620_I2S_SR_48K; if (enable) { if (channels == 0) { return ERROR_PARAMETER; } switch (format) { case I2S_FORMAT_I2S: if (channels > 2) { return ERROR_PARAMETER; } break; case I2S_FORMAT_TDM: if ((channels > 4) || (channels % 2)) { return ERROR_UNSUPPORTED; } tdm = true; break; default: return ERROR_PARAMETER; } if (bits != 16) { return ERROR_UNSUPPORTED; } sr = I2S_SampleRate(rate); if (sr >= MT3620_I2S_SR_COUNT) { return ERROR_UNSUPPORTED; } } mt3620_i2s_global_control_t global_control = { .mask = mt3620_i2s[id]->global_control }; if (enable) { global_control.dl_mono = (channels == 1); global_control.dl_mono_dup = (channels == 1); global_control.i2s_out_clk_en = true; global_control.dl_empty_value_en = true; global_control.clk_sel_out = MT3620_I2S_CLK_SEL_EXTERNAL; mt3620_i2s[id]->global_control = global_control.mask; } mt3620_i2s_dl_control_t dl_control = { .mask = mt3620_i2s[id]->dl_control }; dl_control.en = enable; if (enable) { dl_control.wlen = MT3620_I2S_WLEN_16BIT; dl_control.src = MT3620_I2S_SRC_SLAVE; dl_control.fmt = (tdm ? MT3620_I2S_FMT_TDM : MT3620_I2S_FMT_I2S); dl_control.wsinv = tdm; dl_control.dlfifo_2deq = (tdm && (channels == 4)); dl_control.sr = sr; dl_control.bit_per_s = (tdm && (channels == 4) ? 1 : 0); dl_control.ws_rsync = true; // Functional Spec recommends this be on. dl_control.msb_offset = 0; dl_control.ch_per_s = (tdm && (channels == 4) ? 1 : 0); } mt3620_i2s[id]->dl_control = dl_control.mask; if (enable) { // Enable DMA. mt3620_dma_global->ch_en_set = (1U << MT3620_I2S_DMA_TX(id)); MT3620_DMA_FIELD_WRITE(MT3620_I2S_DMA_TX(id), start, str, false); mt3620_dma_con_t dma_con_tx = { .mask = mt3620_dma[MT3620_I2S_DMA_TX(id)].con }; dma_con_tx.dir = 0; dma_con_tx.iten = true; dma_con_tx.dreq = true; dma_con_tx.size = (channels == 1 ? 1 : 2); mt3620_dma[MT3620_I2S_DMA_TX(id)].con = dma_con_tx.mask; mt3620_dma[MT3620_I2S_DMA_TX(id)].fixaddr = (void *)mt3620_i2s_fifo[id]; mt3620_dma[MT3620_I2S_DMA_TX(id)].pgmaddr = (void *)I2S_Buffer[id][0]; mt3620_dma[MT3620_I2S_DMA_TX(id)].swptr = 0; mt3620_dma[MT3620_I2S_DMA_TX(id)].ffsize = (I2S_BUFFER_SIZE >> dma_con_tx.size); mt3620_dma[MT3620_I2S_DMA_TX(id)].count = (mt3620_dma[MT3620_I2S_DMA_TX(id)].ffsize / 4); } else { MT3620_DMA_FIELD_WRITE(MT3620_I2S_DMA_TX(id), start, str, false); mt3620_dma_global->ch_en_clr = (1U << MT3620_I2S_DMA_TX(id)); } // Enable DMA handshakes for master mode. mt3620_i2s_dma_if_control_t dma_if_control = { .mask = mt3620_i2s[id]->dma_if_control }; dma_if_control.dl_dmareq_mi_num = (enable ? 1 : 0); dma_if_control.dl_ahb_early_en = enable; dma_if_control.dl_dma_mode_sel = enable; mt3620_i2s[id]->dma_if_control = dma_if_control.mask; // Enable I2S. global_control.en = (enable || handle->in.enable); global_control.dlfifo_en = enable; mt3620_i2s[id]->global_control = global_control.mask; handle->out.enable = enable; handle->out.channels = channels; handle->out.callback = callback; if (enable) { MT3620_DMA_FIELD_WRITE(MT3620_I2S_DMA_TX(id), start, str, true); } return ERROR_NONE; } static void I2S_InputUpdate(I2S *handle) { if (!handle || !handle->open || !handle->in.enable) { return; } unsigned id = handle->id; volatile mt3620_dma_t * const dma = &mt3620_dma[MT3620_I2S_DMA_RX(id)]; unsigned size = MT3620_DMA_FIELD_READ(MT3620_I2S_DMA_RX(id), con, size); uintptr_t remain = (dma->ffsize - dma->ffcnt) << size; uintptr_t swptr = MT3620_DMA_FIELD_READ(MT3620_I2S_DMA_RX(id), swptr, swptr); // Handle buffer wrap-around. if ((swptr + remain) >= (dma->ffsize << size)) { uintptr_t partial = (dma->ffsize << size) - swptr; if (!handle->in.callback(&I2S_Buffer[handle->id][1][swptr], partial)) { return; } // Toggle the wrap bit and set swptr to zero. dma->swptr = (dma->swptr ^ 0x00010000) & 0xFFFF0000; swptr = 0; remain -= partial; } if ((remain > 0) && handle->in.callback( &I2S_Buffer[handle->id][1][swptr], remain)) { dma->swptr += remain; } MT3620_DMA_FIELD_WRITE(MT3620_I2S_DMA_RX(id), ackint, ack, 1); } int32_t I2S_Input( I2S *handle, I2S_Format format, unsigned channels, unsigned bits, unsigned rate, bool (*callback)(void *data, uintptr_t size)) { if (!handle) { return ERROR_PARAMETER; } if (!handle->open) { return ERROR_HANDLE_CLOSED; } unsigned id = handle->id; bool enable = (format != I2S_FORMAT_NONE); bool tdm = false; mt3620_i2s_sr_t sr = MT3620_I2S_SR_48K; if (enable) { if (channels == 0) { return ERROR_PARAMETER; } switch (format) { case I2S_FORMAT_I2S: if (channels > 2) { return ERROR_PARAMETER; } break; case I2S_FORMAT_TDM: if ((channels > 4) || (channels % 2)) { return ERROR_UNSUPPORTED; } tdm = true; break; default: return ERROR_PARAMETER; } if (bits != 16) { return ERROR_UNSUPPORTED; } sr = I2S_SampleRate(rate); if (sr >= MT3620_I2S_SR_COUNT) { return ERROR_UNSUPPORTED; } } mt3620_i2s_global_control_t global_control = { .mask = mt3620_i2s[id]->global_control }; if (enable) { global_control.ul_empty_value_en = false; global_control.clk_sel_in = MT3620_I2S_CLK_SEL_EXTERNAL; mt3620_i2s[id]->global_control = global_control.mask; } mt3620_i2s_ul_control_t ul_control = { .mask = mt3620_i2s[id]->ul_control }; ul_control.en = enable; if (enable) { ul_control.wlen = MT3620_I2S_WLEN_16BIT; ul_control.src = MT3620_I2S_SRC_SLAVE; ul_control.fmt = (tdm ? MT3620_I2S_FMT_TDM : MT3620_I2S_FMT_I2S); ul_control.wsinv = tdm; ul_control.sr = sr; ul_control.bit_per_s = (tdm && (channels == 4) ? 1 : 0); ul_control.ws_rsync = true; // Functional Spec recommends this be on. ul_control.down_rate = false; // TODO: Figure out when this should be used. ul_control.msb_offset = 0; ul_control.update_word = 8; ul_control.ch_per_s = (tdm && (channels == 4) ? 1 : 0); ul_control.lr_swap = false; } mt3620_i2s[id]->ul_control = ul_control.mask; if (enable) { // Enable DMA. mt3620_dma_global->ch_en_set = (1U << MT3620_I2S_DMA_RX(id)); MT3620_DMA_FIELD_WRITE(MT3620_I2S_DMA_RX(id), start, str, false); mt3620_dma_con_t dma_con_rx = { .mask = mt3620_dma[MT3620_I2S_DMA_RX(id)].con }; dma_con_rx.dir = 1; dma_con_rx.iten = true; dma_con_rx.dreq = true; dma_con_rx.size = (channels == 1 ? 1 : 2); mt3620_dma[MT3620_I2S_DMA_RX(id)].con = dma_con_rx.mask; mt3620_dma[MT3620_I2S_DMA_RX(id)].fixaddr = (void *)mt3620_i2s_fifo[id]; mt3620_dma[MT3620_I2S_DMA_RX(id)].pgmaddr = (void *)I2S_Buffer[id][1]; mt3620_dma[MT3620_I2S_DMA_RX(id)].swptr = 0; mt3620_dma[MT3620_I2S_DMA_RX(id)].ffsize = (I2S_BUFFER_SIZE >> dma_con_rx.size); mt3620_dma[MT3620_I2S_DMA_RX(id)].count = ((mt3620_dma[MT3620_I2S_DMA_RX(id)].ffsize * 3) / 4); } else { MT3620_DMA_FIELD_WRITE(MT3620_I2S_DMA_RX(id), start, str, false); mt3620_dma_global->ch_en_clr = (1U << MT3620_I2S_DMA_RX(id)); } // Enable DMA handshakes for master mode. mt3620_i2s_dma_if_control_t dma_if_control = { .mask = mt3620_i2s[id]->dma_if_control }; dma_if_control.ul_dmareq_mi_num = (enable ? 1 : 0); dma_if_control.ul_ahb_early_en = enable; dma_if_control.ul_dma_mode_sel = enable; mt3620_i2s[id]->dma_if_control = dma_if_control.mask; // Enable I2S. global_control.en = (enable || handle->out.enable); global_control.ulfifo_en = enable; mt3620_i2s[id]->global_control = global_control.mask; handle->in.enable = enable; handle->in.callback = callback; if (enable) { MT3620_DMA_FIELD_WRITE(MT3620_I2S_DMA_RX(id), start, str, true); } return ERROR_NONE; } unsigned I2S_GetOutputSampleRate(I2S *handle) { if (!handle || !handle->open || !handle->out.enable) { return 0; } return MT3620_I2S_SR_CALC(MT3620_I2S_FIELD_READ(handle->id, dl_control, sr)); } unsigned I2S_GetInputSampleRate(I2S *handle) { if (!handle || !handle->open || !handle->in.enable) { return 0; } return MT3620_I2S_SR_CALC(MT3620_I2S_FIELD_READ(handle->id, ul_control, sr)); } void m4dma_irq_b_i2s0_tx(void) { I2S_OutputUpdate(&context[0]); } void m4dma_irq_b_i2s0_rx(void) { I2S_InputUpdate(&context[0]); } void m4dma_irq_b_i2s1_tx(void) { I2S_OutputUpdate(&context[1]); } void m4dma_irq_b_i2s1_rx(void) { I2S_InputUpdate(&context[1]); }