/****************************************************************************** * * Copyright (C) 2022-2023 Maxim Integrated Products, Inc. (now owned by * Analog Devices, Inc.), * Copyright (C) 2023-2024 Analog Devices, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ******************************************************************************/ #include #include #include #include #include "mxc_device.h" #include "mxc_assert.h" #include "mxc_lock.h" #include "mxc_sys.h" #include "mxc_delay.h" #include "dma.h" #include "i2s_reva.h" #include "i2s.h" /* ***** Definitions ***** */ #define DATALENGTH_EIGHT (8) #define DATALENGTH_SIXTEEN (16) #define DATALENGTH_TWENTY (20) #define DATALENGTH_TWENTYFOUR (24) #define DATALENGTH_THIRTYTWO (32) // #define USE_LEGACY_I2S_DMA_CFG typedef struct { int rxCnt; int txCnt; bool async; } mxc_i2s_reva_txn_t; /* ****** Globals ****** */ static mxc_i2s_req_t *request; static void (*dma_cb)(int, int) = NULL; static void (*async_cb)(int) = NULL; static mxc_i2s_req_t txn_req; static mxc_i2s_reva_txn_t txn_state; static uint32_t txn_lock = 0; static void configure_data_sizes(mxc_i2s_reva_regs_t *i2s, uint8_t bits_word, uint8_t smp_sz, uint8_t wsize) { if (bits_word > 0) { MXC_SETFIELD(i2s->ctrl1ch0, MXC_F_I2S_REVA_CTRL1CH0_BITS_WORD, (bits_word - 1) << MXC_F_I2S_REVA_CTRL1CH0_BITS_WORD_POS); // Subtract 1 } else { MXC_SETFIELD(i2s->ctrl1ch0, MXC_F_I2S_REVA_CTRL1CH0_BITS_WORD, 0); // Clear to 0 } //Set sample length if defined: //The SMP_SIZE is equal to bitsWord when sampleSize == 0 or sampleSize > bitsWord if (smp_sz > 0) { MXC_SETFIELD(i2s->ctrl1ch0, MXC_F_I2S_REVA_CTRL1CH0_SMP_SIZE, (smp_sz - 1) << MXC_F_I2S_REVA_CTRL1CH0_SMP_SIZE_POS); // Subtract 1 } else { MXC_SETFIELD(i2s->ctrl1ch0, MXC_F_I2S_REVA_CTRL1CH0_SMP_SIZE, 0); // Clear to 0 } //Set datasize to load in FIFO MXC_SETFIELD(i2s->ctrl0ch0, MXC_F_I2S_REVA_CTRL0CH0_WSIZE, wsize << MXC_F_I2S_REVA_CTRL0CH0_WSIZE_POS); } /* ****** Functions ****** */ int MXC_I2S_RevA_Init(mxc_i2s_reva_regs_t *i2s, mxc_i2s_req_t *req) { if (((req->txData == NULL) || (req->rawData == NULL)) && (req->rxData == NULL)) { return E_NULL_PTR; } if (req->length == 0) { return E_BAD_PARAM; } request = req; if (req->stereoMode) { i2s->ctrl0ch0 |= (req->stereoMode << MXC_F_I2S_REVA_CTRL0CH0_STEREO_POS); } //Set RX Threshold 2 (default) i2s->ctrl0ch0 |= (2 << MXC_F_I2S_REVA_CTRL0CH0_RX_THD_VAL_POS); //Set justify MXC_SETFIELD(i2s->ctrl0ch0, MXC_F_I2S_REVA_CTRL0CH0_ALIGN, (req->justify) << MXC_F_I2S_REVA_CTRL0CH0_ALIGN_POS); if (MXC_I2S_ConfigData((mxc_i2s_req_t *)req) != E_NO_ERROR) { return E_BAD_PARAM; } MXC_I2S_SetFrequency(req->channelMode, req->clkdiv); return E_NO_ERROR; } int MXC_I2S_RevA_Shutdown(mxc_i2s_reva_regs_t *i2s) { MXC_I2S_DisableInt(0xFF); //Disable I2S TX and RX channel MXC_I2S_TXDisable(); MXC_I2S_RXDisable(); MXC_I2S_Flush(); //Clear all the registers. Not cleared on reset i2s->ctrl0ch0 = 0x00; i2s->dmach0 = 0x00; i2s->ctrl1ch0 = 0x00; i2s->ctrl0ch0 |= MXC_F_I2S_REVA_CTRL0CH0_RST; //Reset channel return E_NO_ERROR; } int MXC_I2S_RevA_ConfigData(mxc_i2s_reva_regs_t *i2s, mxc_i2s_req_t *req) { uint32_t dataMask; //Data pointers uint8_t *txdata_8 = (uint8_t *)req->txData; uint16_t *txdata_16 = (uint16_t *)req->txData; uint32_t *txdata_32 = (uint32_t *)req->txData; uint8_t *rawdata_8 = (uint8_t *)req->rawData; uint16_t *rawdata_16 = (uint16_t *)req->rawData; uint32_t *rawdata_32 = (uint32_t *)req->rawData; if ((req->txData == NULL) && (req->rxData == NULL)) { return E_NULL_PTR; } if (req->length == 0) { return E_BAD_PARAM; } // Clear configuration bits i2s->ctrl0ch0 &= ~MXC_F_I2S_REVA_CTRL0CH0_WSIZE; i2s->ctrl1ch0 &= ~MXC_F_I2S_REVA_CTRL1CH0_BITS_WORD; i2s->ctrl1ch0 &= ~MXC_F_I2S_REVA_CTRL1CH0_SMP_SIZE; configure_data_sizes(i2s, req->bitsWord, req->sampleSize, req->wordSize); MXC_SETFIELD(i2s->ctrl1ch0, MXC_F_I2S_REVA_CTRL1CH0_ADJUST, (req->adjust) << MXC_F_I2S_REVA_CTRL1CH0_ADJUST_POS); if (req->bitsWord <= DATALENGTH_EIGHT) { dataMask = 0x000000ff; if ((req->rawData != NULL) && (req->txData != NULL)) { for (uint32_t i = 0; i < req->length; i++) { *txdata_8++ = *rawdata_8++ & dataMask; } } } else if (req->bitsWord <= DATALENGTH_SIXTEEN) { dataMask = 0x0000ffff; if ((req->rawData != NULL) && (req->txData != NULL)) { for (uint32_t i = 0; i < req->length; i++) { *txdata_16++ = *rawdata_16++ & dataMask; } } } else if (req->bitsWord <= DATALENGTH_TWENTY) { dataMask = 0x00fffff; if ((req->rawData != NULL) && (req->txData != NULL)) { for (uint32_t i = 0; i < req->length; i++) { *txdata_32++ = (*rawdata_32++ & dataMask) << 12; } } } else if (req->bitsWord <= DATALENGTH_TWENTYFOUR) { dataMask = 0x00ffffff; if ((req->rawData != NULL) && (req->txData != NULL)) { for (uint32_t i = 0; i < req->length; i++) { *txdata_32++ = (*rawdata_32++ & dataMask) << 8; } } } else if (req->bitsWord <= DATALENGTH_THIRTYTWO) { dataMask = 0xffffffff; if ((req->rawData != NULL) && (req->txData != NULL)) { for (uint32_t i = 0; i < req->length; i++) { *txdata_32++ = *rawdata_32++ & dataMask; } } } return E_NO_ERROR; } void MXC_I2S_RevA_TXEnable(mxc_i2s_reva_regs_t *i2s) { MXC_SETFIELD(i2s->ctrl0ch0, MXC_F_I2S_REVA_CTRL0CH0_TX_EN, 1 << MXC_F_I2S_REVA_CTRL0CH0_TX_EN_POS); } void MXC_I2S_RevA_TXDisable(mxc_i2s_reva_regs_t *i2s) { MXC_SETFIELD(i2s->ctrl0ch0, MXC_F_I2S_REVA_CTRL0CH0_TX_EN, 0 << MXC_F_I2S_REVA_CTRL0CH0_TX_EN_POS); } void MXC_I2S_RevA_RXEnable(mxc_i2s_reva_regs_t *i2s) { MXC_SETFIELD(i2s->ctrl0ch0, MXC_F_I2S_REVA_CTRL0CH0_RX_EN, 1 << MXC_F_I2S_REVA_CTRL0CH0_RX_EN_POS); } void MXC_I2S_RevA_RXDisable(mxc_i2s_reva_regs_t *i2s) { MXC_SETFIELD(i2s->ctrl0ch0, MXC_F_I2S_REVA_CTRL0CH0_RX_EN, 0 << MXC_F_I2S_REVA_CTRL0CH0_RX_EN_POS); } int MXC_I2S_RevA_SetRXThreshold(mxc_i2s_reva_regs_t *i2s, uint8_t threshold) { if ((threshold == 0) || (threshold > 8)) { return E_NOT_SUPPORTED; } i2s->ctrl0ch0 |= (threshold << MXC_F_I2S_REVA_CTRL0CH0_RX_THD_VAL_POS); return E_NO_ERROR; } int MXC_I2S_RevA_SetFrequency(mxc_i2s_reva_regs_t *i2s, mxc_i2s_ch_mode_t mode, uint16_t clkdiv) { i2s->ctrl1ch0 &= ~MXC_F_I2S_REVA_CTRL1CH0_EN; MXC_SETFIELD(i2s->ctrl0ch0, MXC_F_I2S_REVA_CTRL0CH0_CH_MODE, (mode) << MXC_F_I2S_REVA_CTRL0CH0_CH_MODE_POS); i2s->ctrl1ch0 |= ((uint32_t)clkdiv) << MXC_F_I2S_REVA_CTRL1CH0_CLKDIV_POS; i2s->ctrl1ch0 |= MXC_F_I2S_REVA_CTRL1CH0_EN; return E_NO_ERROR; } int MXC_I2S_RevA_SetSampleRate(mxc_i2s_reva_regs_t *i2s, uint32_t smpl_rate, mxc_i2s_wsize_t smpl_sz, uint32_t src_clk) { int clk_div; clk_div = MXC_I2S_RevA_CalculateClockDiv(i2s, smpl_rate, smpl_sz, src_clk); if (clk_div < 0) { return clk_div; } i2s->ctrl1ch0 &= ~MXC_F_I2S_REVA_CTRL1CH0_EN; i2s->ctrl1ch0 |= ((uint32_t)clk_div) << MXC_F_I2S_REVA_CTRL1CH0_CLKDIV_POS; i2s->ctrl1ch0 |= MXC_F_I2S_REVA_CTRL1CH0_EN; return MXC_I2S_RevA_GetSampleRate(i2s, src_clk); } int MXC_I2S_RevA_GetSampleRate(mxc_i2s_reva_regs_t *i2s, uint32_t src_clk) { uint16_t word_sz, clk_div; uint32_t bclk; word_sz = (i2s->ctrl0ch0 & MXC_F_I2S_REVA_CTRL0CH0_WSIZE) >> MXC_F_I2S_REVA_CTRL0CH0_WSIZE_POS; clk_div = (i2s->ctrl1ch0 & MXC_F_I2S_REVA_CTRL1CH0_CLKDIV) >> MXC_F_I2S_REVA_CTRL1CH0_CLKDIV_POS; // Get clock divider value switch (word_sz) { // Get word size case MXC_I2S_WSIZE_BYTE: word_sz = 8; break; case MXC_I2S_WSIZE_HALFWORD: word_sz = 16; break; case MXC_I2S_WSIZE_WORD: default: word_sz = 32; break; } bclk = (src_clk / (clk_div + 1)) >> 1; // bclk_frequency = src_clk_frequency / (clk_divider + 1) / 2 return (bclk / word_sz) >> 1; // return sample rate (sample_rate = bclk_frequency / word_size / 2) } int MXC_I2S_RevA_CalculateClockDiv(mxc_i2s_reva_regs_t *i2s, uint32_t smpl_rate, mxc_i2s_wsize_t word_sz, uint32_t src_clk) { uint32_t bclk = 0; uint32_t word_size = 0; switch (word_sz) { // Get word size case MXC_I2S_WSIZE_BYTE: word_size = 8; break; case MXC_I2S_WSIZE_HALFWORD: word_size = 16; break; case MXC_I2S_WSIZE_WORD: word_size = 32; break; default: return E_BAD_PARAM; } bclk = smpl_rate * word_size * 2; // bclk_frequency = sample_rate * word_size * 2 if (bclk > src_clk) { return E_INVALID; } return (src_clk / (bclk * 2)) - 1; // clk_divider = src_clk_frequency / (bclk_frequency * 2) - 1 } void MXC_I2S_RevA_Flush(mxc_i2s_reva_regs_t *i2s) { i2s->ctrl0ch0 |= MXC_F_I2S_REVA_CTRL0CH0_FLUSH; while (i2s->ctrl0ch0 & MXC_F_I2S_REVA_CTRL0CH0_FLUSH) {} } static uint32_t write_tx_fifo(void *tx, mxc_i2s_wsize_t wordSize, int smpl_cnt) { uint32_t write_val = 0; if (wordSize == MXC_I2S_WSIZE_BYTE) { uint8_t *tx8 = (uint8_t *)tx; for (int i = 0; i < 4; i++) { write_val |= (tx8[smpl_cnt++] << (i * 8)); } } else if (wordSize == MXC_I2S_WSIZE_HALFWORD) { uint16_t *tx16 = (uint16_t *)tx; for (int i = 0; i < 2; i++) { write_val |= (tx16[smpl_cnt++] << (i * 16)); } } else if (wordSize == MXC_I2S_WSIZE_WORD) { uint32_t *tx32 = (uint32_t *)tx; write_val = tx32[smpl_cnt]; } return write_val; } int MXC_I2S_RevA_FillTXFIFO(mxc_i2s_reva_regs_t *i2s, void *txData, mxc_i2s_wsize_t wordSize, int len, int smpl_cnt) { int num_smpl = 0x4 >> wordSize; // Number of samples per FIFO write int sent = 0; // Total number of samples transmitted uint32_t fifo_write, fifo_avail; // Value to write to I2S TX FIFO if (txData == NULL) { // Check for bad parameters return E_NULL_PTR; } else if (wordSize > MXC_I2S_WSIZE_WORD) { return E_BAD_PARAM; } else if (len == 0) { return E_NO_ERROR; } len -= len % num_smpl; // TEST fifo_avail = 8 - ((i2s->dmach0 & MXC_F_I2S_REVA_DMACH0_TX_LVL) >> MXC_F_I2S_REVA_DMACH0_TX_LVL_POS); fifo_avail *= num_smpl; while (sent < len && sent < fifo_avail) { fifo_write = write_tx_fifo(txData, wordSize, sent + smpl_cnt); sent += num_smpl; i2s->fifoch0 = fifo_write; } return sent; } static void read_rx_fifo(mxc_i2s_reva_regs_t *i2s, void *rxData, mxc_i2s_wsize_t wordSize, int cnt) { uint32_t fifo_val = i2s->fifoch0; if (wordSize == MXC_I2S_WSIZE_BYTE) { uint8_t *rx8 = (uint8_t *)rxData; for (int i = 0; i < 4; i++) { rx8[cnt++] = fifo_val & 0xFF; fifo_val = fifo_val >> 8; } } else if (wordSize == MXC_I2S_WSIZE_HALFWORD) { uint16_t *rx16 = (uint16_t *)rxData; for (int i = 0; i < 2; i++) { rx16[cnt++] = fifo_val & 0xFFFF; fifo_val = fifo_val >> 16; } } else if (wordSize == MXC_I2S_WSIZE_WORD) { uint32_t *rx32 = (uint32_t *)rxData; rx32[cnt] = fifo_val; } } int MXC_I2S_RevA_ReadRXFIFO(mxc_i2s_reva_regs_t *i2s, void *rxData, mxc_i2s_wsize_t wordSize, int len, int smpl_cnt) { int received = 0; int num_smpl = 0x4 >> wordSize; uint32_t fifo_avail; if (rxData == NULL) { // Check for bad parameters return E_NULL_PTR; } else if (wordSize > MXC_I2S_WSIZE_WORD) { return E_BAD_PARAM; } else if (len == 0) { return E_NO_ERROR; } len -= len % num_smpl; fifo_avail = (i2s->dmach0 & MXC_F_I2S_REVA_DMACH0_RX_LVL) >> MXC_F_I2S_REVA_DMACH0_RX_LVL_POS; while (received < len && fifo_avail) { read_rx_fifo(i2s, rxData, wordSize, received + smpl_cnt); received += num_smpl; fifo_avail = (i2s->dmach0 & MXC_F_I2S_REVA_DMACH0_RX_LVL) >> MXC_F_I2S_REVA_DMACH0_RX_LVL_POS; } return received; } void MXC_I2S_RevA_EnableInt(mxc_i2s_reva_regs_t *i2s, uint32_t flags) { i2s->inten |= flags; } void MXC_I2S_RevA_DisableInt(mxc_i2s_reva_regs_t *i2s, uint32_t flags) { i2s->inten &= ~flags; } int MXC_I2S_RevA_GetFlags(mxc_i2s_reva_regs_t *i2s) { return (i2s->intfl & 0xF); } void MXC_I2S_RevA_ClearFlags(mxc_i2s_reva_regs_t *i2s, uint32_t flags) { i2s->intfl |= flags; } int MXC_I2S_RevA_Transaction(mxc_i2s_reva_regs_t *i2s, mxc_i2s_req_t *i2s_req) { int err; if (i2s_req->rawData != NULL && i2s_req->txData == NULL) { return E_INVALID; } else if (i2s_req->length == 0) { return E_INVALID; } else if (MXC_GetLock(&txn_lock, 1) != E_NO_ERROR) { return E_BUSY; } i2s->ctrl1ch0 &= ~MXC_F_I2S_REVA_CTRL1CH0_EN; // Disable I2S while it's being configured txn_req = *i2s_req; // Initialize transaction request state variables txn_state.rxCnt = txn_req.length; txn_state.txCnt = txn_req.length; txn_state.async = false; MXC_I2S_Flush(); if (txn_req.rawData != NULL && txn_req.txData != NULL) { // Set up transmit if transmit parameters valid txn_state.txCnt = 0; err = MXC_I2S_ConfigData(&txn_req); if (err) { MXC_FreeLock(&txn_lock); return err; } err = MXC_I2S_FillTXFIFO(txn_req.txData, txn_req.wordSize, txn_req.length, txn_state.txCnt); // Initialize TX FIFO if (err < E_NO_ERROR) { MXC_FreeLock(&txn_lock); return err; } txn_state.txCnt = err; MXC_I2S_TXEnable(); // Enable I2S transmit (Do this before Fill FIFO?) } if (txn_req.rxData != NULL) { // Setup I2S receive if receive parameters valid txn_state.rxCnt = 0; MXC_SETFIELD(i2s->ctrl0ch0, MXC_F_I2S_REVA_CTRL0CH0_RX_THD_VAL, (6 << MXC_F_I2S_REVA_CTRL0CH0_RX_THD_VAL_POS)); // Set RX threshold MXC_I2S_RXEnable(); // Enable I2S Receive } i2s->ctrl1ch0 |= MXC_F_I2S_REVA_CTRL1CH0_EN; // Enable I2S RX/TX while (MXC_GetLock(&txn_lock, 1) != E_NO_ERROR) { MXC_I2S_RevA_Handler(i2s); } MXC_FreeLock(&txn_lock); return E_NO_ERROR; } int MXC_I2S_RevA_TransactionAsync(mxc_i2s_reva_regs_t *i2s, mxc_i2s_req_t *i2s_req) { int err; uint32_t int_en = 0; if (i2s_req->rawData != NULL && i2s_req->txData == NULL) { return E_INVALID; } else if (i2s_req->length == 0) { return E_INVALID; } else if (MXC_GetLock(&txn_lock, 1) != E_NO_ERROR) { return E_BUSY; } i2s->ctrl1ch0 &= ~MXC_F_I2S_REVA_CTRL1CH0_EN; // Disable I2S while it's being configured txn_req = *i2s_req; // Initialize transacion request state variables txn_state.rxCnt = txn_req.length; txn_state.txCnt = txn_req.length; txn_state.async = true; MXC_I2S_Flush(); if (txn_req.rawData != NULL && txn_req.txData != NULL) { // Set up transmit if transmit parameters valid txn_state.txCnt = 0; err = MXC_I2S_ConfigData(&txn_req); if (err) { MXC_FreeLock(&txn_lock); return err; } int_en |= MXC_F_I2S_REVA_INTFL_TX_OB_CH0; // Enable TX one entry remaining interrupt err = MXC_I2S_FillTXFIFO(txn_req.txData, txn_req.wordSize, txn_req.length, txn_state.txCnt); // Initialize TX FIFO if (err < E_NO_ERROR) { MXC_FreeLock(&txn_lock); return err; } txn_state.txCnt = err; MXC_I2S_TXEnable(); // Enable I2S transmit (Do this before Fill FIFO?) } if (txn_req.rxData != NULL) { // Setup I2S receive if receive parameters valid txn_state.rxCnt = 0; int_en |= MXC_F_I2S_REVA_INTEN_RX_THD_CH0; // Enable RX threshold interrupt MXC_SETFIELD(i2s->ctrl0ch0, MXC_F_I2S_REVA_CTRL0CH0_RX_THD_VAL, (6 << MXC_F_I2S_REVA_CTRL0CH0_RX_THD_VAL_POS)); // Set RX threshold MXC_I2S_RXEnable(); // Enable I2S Receive } MXC_I2S_DisableInt(0xF); // Configure interrupts MXC_I2S_ClearFlags(0xF); MXC_I2S_EnableInt(int_en); i2s->ctrl1ch0 |= MXC_F_I2S_REVA_CTRL1CH0_EN; // Enable I2S RX/TX return E_NO_ERROR; } int MXC_I2S_RevA_TXDMAConfig(mxc_i2s_reva_regs_t *i2s, void *src_addr, int len) { int channel; mxc_dma_config_t config; mxc_dma_adv_config_t advConfig; mxc_dma_srcdst_t srcdst; MXC_DMA_Init(); i2s->dmach0 |= (2 << MXC_F_I2S_REVA_DMACH0_DMA_TX_THD_VAL_POS); //TX DMA Threshold channel = MXC_DMA_AcquireChannel(); if (channel < E_NO_ERROR) { return channel; } config.reqsel = MXC_DMA_REQUEST_I2STX; config.ch = channel; switch (request->wordSize) { case MXC_I2S_WSIZE_WORD: config.srcwd = MXC_DMA_WIDTH_WORD; config.dstwd = MXC_DMA_WIDTH_WORD; advConfig.burst_size = 4; break; case MXC_I2S_WSIZE_HALFWORD: config.srcwd = MXC_DMA_WIDTH_HALFWORD; config.dstwd = MXC_DMA_WIDTH_WORD; advConfig.burst_size = 2; break; case MXC_I2S_WSIZE_BYTE: config.srcwd = MXC_DMA_WIDTH_BYTE; config.dstwd = MXC_DMA_WIDTH_WORD; advConfig.burst_size = 1; break; default: config.srcwd = MXC_DMA_WIDTH_BYTE; config.dstwd = MXC_DMA_WIDTH_WORD; advConfig.burst_size = 1; break; } #ifndef USE_LEGACY_I2S_DMA_CFG advConfig.burst_size = 4; #endif config.srcinc_en = 1; config.dstinc_en = 0; advConfig.ch = channel; advConfig.prio = MXC_DMA_PRIO_HIGH; // 0 advConfig.reqwait_en = 0; advConfig.tosel = MXC_DMA_TIMEOUT_4_CLK; // 0 advConfig.pssel = MXC_DMA_PRESCALE_DISABLE; // 0 srcdst.ch = channel; srcdst.source = src_addr; srcdst.len = len; MXC_DMA_ConfigChannel(config, srcdst); MXC_DMA_AdvConfigChannel(advConfig); MXC_DMA_SetCallback(channel, dma_cb); MXC_I2S_TXEnable(); //Enable I2S TX i2s->dmach0 |= MXC_F_I2S_REVA_DMACH0_DMA_TX_EN; //Enable I2S DMA MXC_DMA_EnableInt(channel); MXC_DMA_Start(channel); MXC_DMA->ch[channel].ctrl |= MXC_F_DMA_CTRL_CTZ_IE; return channel; } int MXC_I2S_RevA_RXDMAConfig(mxc_i2s_reva_regs_t *i2s, void *dest_addr, int len) { int channel; mxc_dma_config_t config; mxc_dma_adv_config_t advConfig; mxc_dma_srcdst_t srcdst; MXC_DMA_Init(); i2s->dmach0 |= (6 << MXC_F_I2S_REVA_DMACH0_DMA_RX_THD_VAL_POS); //RX DMA Threshold channel = MXC_DMA_AcquireChannel(); if (channel < E_NO_ERROR) { return channel; } config.reqsel = MXC_DMA_REQUEST_I2SRX; config.ch = channel; switch (request->wordSize) { case MXC_I2S_WSIZE_WORD: config.srcwd = MXC_DMA_WIDTH_WORD; config.dstwd = MXC_DMA_WIDTH_WORD; advConfig.burst_size = 4; break; case MXC_I2S_WSIZE_HALFWORD: config.srcwd = MXC_DMA_WIDTH_WORD; config.dstwd = MXC_DMA_WIDTH_HALFWORD; advConfig.burst_size = 2; break; case MXC_I2S_WSIZE_BYTE: config.srcwd = MXC_DMA_WIDTH_WORD; config.dstwd = MXC_DMA_WIDTH_BYTE; advConfig.burst_size = 1; break; default: config.srcwd = MXC_DMA_WIDTH_WORD; config.dstwd = MXC_DMA_WIDTH_BYTE; advConfig.burst_size = 1; break; } #ifndef USE_LEGACY_I2S_DMA_CFG advConfig.burst_size = 4; #endif config.srcinc_en = 0; config.dstinc_en = 1; advConfig.ch = channel; advConfig.prio = MXC_DMA_PRIO_HIGH; // 0 advConfig.reqwait_en = 0; advConfig.tosel = MXC_DMA_TIMEOUT_4_CLK; // 0 advConfig.pssel = MXC_DMA_PRESCALE_DISABLE; // 0 srcdst.ch = channel; srcdst.dest = dest_addr; srcdst.len = len; MXC_DMA_ConfigChannel(config, srcdst); MXC_DMA_AdvConfigChannel(advConfig); MXC_DMA_SetCallback(channel, dma_cb); MXC_I2S_RXEnable(); //Enable I2S RX i2s->dmach0 |= MXC_F_I2S_REVA_DMACH0_DMA_RX_EN; //Enable I2S DMA MXC_DMA_EnableInt(channel); MXC_DMA_Start(channel); MXC_DMA->ch[channel].ctrl |= MXC_F_DMA_CTRL_CTZ_IE; return channel; } void MXC_I2S_RevA_Handler(mxc_i2s_reva_regs_t *i2s) { uint32_t flags = MXC_I2S_GetFlags(); if (txn_state.txCnt == txn_req.length && txn_state.rxCnt == txn_req.length) { MXC_I2S_DisableInt(MXC_F_I2S_REVA_INTEN_TX_OB_CH0 | MXC_F_I2S_REVA_INTEN_RX_THD_CH0); MXC_I2S_ClearFlags(MXC_F_I2S_REVA_INTFL_TX_OB_CH0 | MXC_F_I2S_REVA_INTFL_RX_THD_CH0); while (i2s->dmach0 & MXC_F_I2S_REVA_DMACH0_TX_LVL) {} MXC_I2S_TXDisable(); MXC_I2S_RXDisable(); if (async_cb != NULL && txn_state.async) { async_cb(E_NO_ERROR); } MXC_FreeLock(&txn_lock); } else if (txn_req.txData != NULL && (flags & MXC_F_I2S_REVA_INTFL_TX_OB_CH0)) { MXC_I2S_ClearFlags(MXC_F_I2S_REVA_INTFL_TX_OB_CH0); if (txn_state.txCnt < txn_req.length) { txn_state.txCnt += MXC_I2S_FillTXFIFO(txn_req.txData, txn_req.wordSize, (txn_req.length - txn_state.txCnt), txn_state.txCnt); } } else if (txn_req.rxData != NULL && (flags & MXC_F_I2S_REVA_INTFL_RX_THD_CH0)) { MXC_I2S_ClearFlags(MXC_F_I2S_REVA_INTFL_RX_THD_CH0); if (txn_state.rxCnt < txn_req.length) { txn_state.rxCnt += MXC_I2S_ReadRXFIFO(txn_req.rxData, txn_req.wordSize, (txn_req.length - txn_state.rxCnt), txn_state.rxCnt); } } } void MXC_I2S_RevA_RegisterDMACallback(void (*callback)(int, int)) { dma_cb = callback; } void MXC_I2S_RevA_RegisterAsyncCallback(void (*callback)(int)) { async_cb = callback; }