286 lines
9.0 KiB
C
286 lines
9.0 KiB
C
/*
|
|
* Copyright 2021 NXP
|
|
* All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*/
|
|
|
|
#include "osa_common.h"
|
|
|
|
#include "app_definitions.h"
|
|
#include "board.h"
|
|
#include "streamer_pcm_app.h"
|
|
#include "fsl_debug_console.h"
|
|
#include "fsl_codec_common.h"
|
|
|
|
pcm_rtos_t pcmHandle = {0};
|
|
|
|
extern codec_handle_t codecHandle;
|
|
|
|
static void TxCallback(I2S_Type *base, i2s_dma_handle_t *handle, status_t completionStatus, void *userData)
|
|
{
|
|
pcm_rtos_t *pcm = (pcm_rtos_t *)userData;
|
|
BaseType_t reschedule;
|
|
xSemaphoreGiveFromISR(pcm->semaphoreTX, &reschedule);
|
|
portYIELD_FROM_ISR(reschedule);
|
|
}
|
|
|
|
static void RxCallback(I2S_Type *base, i2s_dma_handle_t *handle, status_t completionStatus, void *userData)
|
|
{
|
|
pcm_rtos_t *pcm = (pcm_rtos_t *)userData;
|
|
BaseType_t reschedule;
|
|
xSemaphoreGiveFromISR(pcm->semaphoreRX, &reschedule);
|
|
portYIELD_FROM_ISR(reschedule);
|
|
}
|
|
|
|
void streamer_pcm_init(void)
|
|
{
|
|
DMA_Init(DEMO_DMA);
|
|
|
|
DMA_EnableChannel(DEMO_DMA, DEMO_I2S_TX_CHANNEL);
|
|
DMA_SetChannelPriority(DEMO_DMA, DEMO_I2S_TX_CHANNEL, kDMA_ChannelPriority3);
|
|
DMA_CreateHandle(&pcmHandle.i2sTxDmaHandle, DEMO_DMA, DEMO_I2S_TX_CHANNEL);
|
|
|
|
DMA_EnableChannel(DEMO_DMA, DEMO_I2S_RX_CHANNEL);
|
|
DMA_SetChannelPriority(DEMO_DMA, DEMO_I2S_RX_CHANNEL, kDMA_ChannelPriority2);
|
|
DMA_CreateHandle(&pcmHandle.i2sRxDmaHandle, DEMO_DMA, DEMO_I2S_RX_CHANNEL);
|
|
}
|
|
|
|
pcm_rtos_t *streamer_pcm_open(uint32_t num_buffers)
|
|
{
|
|
pcmHandle.semaphoreTX = xSemaphoreCreateBinary();
|
|
return &pcmHandle;
|
|
}
|
|
|
|
pcm_rtos_t *streamer_pcm_rx_open(uint32_t num_buffers)
|
|
{
|
|
pcmHandle.semaphoreRX = xSemaphoreCreateBinary();
|
|
return &pcmHandle;
|
|
}
|
|
|
|
void streamer_pcm_start(pcm_rtos_t *pcm)
|
|
{
|
|
/* Interrupts already enabled - nothing to do.
|
|
* App/streamer can begin writing data to SAI. */
|
|
}
|
|
|
|
void streamer_pcm_close(pcm_rtos_t *pcm)
|
|
{
|
|
/* Stop playback. This will flush the SAI transmit buffers. */
|
|
if (pcm->i2sTxHandle.state != 0)
|
|
{
|
|
I2S_TransferAbortDMA(DEMO_I2S_TX, &pcm->i2sTxHandle);
|
|
}
|
|
vSemaphoreDelete(pcmHandle.semaphoreTX);
|
|
}
|
|
|
|
void streamer_pcm_rx_close(pcm_rtos_t *pcm)
|
|
{
|
|
if (pcm->i2sRxHandle.state != 0)
|
|
{
|
|
I2S_TransferAbortDMA(DEMO_I2S_RX, &pcm->i2sRxHandle);
|
|
}
|
|
vSemaphoreDelete(pcmHandle.semaphoreRX);
|
|
}
|
|
|
|
int streamer_pcm_write(pcm_rtos_t *pcm, uint8_t *data, uint32_t size)
|
|
{
|
|
/* Ensure write size is a multiple of 32, otherwise EDMA will assert
|
|
* failure. Round down for the last chunk of a file/stream. */
|
|
pcm->i2sTxTransfer.dataSize = size - (size % 32);
|
|
pcm->i2sTxTransfer.data = data;
|
|
|
|
if (pcm->isFirstTx)
|
|
{
|
|
/* need to queue two transmit buffers so when the first one
|
|
* finishes transfer, the other immediatelly starts */
|
|
I2S_TxTransferSendDMA(DEMO_I2S_TX, &pcm->i2sTxHandle, pcm->i2sTxTransfer);
|
|
I2S_TxTransferSendDMA(DEMO_I2S_TX, &pcm->i2sTxHandle, pcm->i2sTxTransfer);
|
|
pcm->isFirstTx = 0;
|
|
}
|
|
else
|
|
{
|
|
/* Wait for the previous transfer to finish */
|
|
if (xSemaphoreTake(pcm->semaphoreTX, portMAX_DELAY) != pdTRUE)
|
|
return -1;
|
|
|
|
I2S_TxTransferSendDMA(DEMO_I2S_TX, &pcm->i2sTxHandle, pcm->i2sTxTransfer);
|
|
I2S_TxTransferSendDMA(DEMO_I2S_TX, &pcm->i2sTxHandle, pcm->i2sTxTransfer);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int streamer_pcm_read(pcm_rtos_t *pcm, uint8_t *data, uint32_t size)
|
|
{
|
|
/* Ensure write size is a multiple of 32, otherwise EDMA will assert
|
|
* failure. Round down for the last chunk of a file/stream. */
|
|
pcm->i2sRxTransfer.dataSize = size - (size % 32);
|
|
pcm->i2sRxTransfer.data = data;
|
|
|
|
/* Start the first transfer */
|
|
if (pcm->isFirstRx)
|
|
{
|
|
/* need to queue two receive buffers so when the first one is filled,
|
|
* the other is immediatelly starts to be filled */
|
|
I2S_RxTransferReceiveDMA(DEMO_I2S_RX, &pcm->i2sRxHandle, pcm->i2sRxTransfer);
|
|
I2S_RxTransferReceiveDMA(DEMO_I2S_RX, &pcm->i2sRxHandle, pcm->i2sRxTransfer);
|
|
pcm->isFirstRx = 0;
|
|
}
|
|
else
|
|
{
|
|
/* Wait for the previous transfer to finish */
|
|
if (xSemaphoreTake(pcm->semaphoreRX, portMAX_DELAY) != pdTRUE)
|
|
return -1;
|
|
}
|
|
|
|
/* Start the consecutive transfer */
|
|
I2S_RxTransferReceiveDMA(DEMO_I2S_RX, &pcm->i2sRxHandle, pcm->i2sRxTransfer);
|
|
|
|
/* Enable I2S Tx due to clock availability for the codec (see board schematic). */
|
|
if (pcm->dummy_tx_enable)
|
|
I2S_Enable(DEMO_I2S_TX);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int streamer_pcm_setparams(
|
|
pcm_rtos_t *pcm, uint32_t sample_rate, uint32_t bit_width, uint8_t num_channels, bool tx, bool dummy_tx, int volume)
|
|
{
|
|
int ret = 0;
|
|
int divider = 0;
|
|
|
|
pcm->isFirstTx = tx ? 1U : pcm->isFirstTx;
|
|
pcm->isFirstRx = tx ? pcm->isFirstRx : 1U;
|
|
pcm->sample_rate = sample_rate;
|
|
pcm->bit_width = bit_width;
|
|
pcm->num_channels = num_channels;
|
|
pcm->dummy_tx_enable |= dummy_tx;
|
|
|
|
if (sample_rate % 8000 == 0 || sample_rate % 6000 == 0)
|
|
{
|
|
const pll_setup_t pll0Setup = {
|
|
.pllctrl = SYSCON_PLL0CTRL_CLKEN_MASK | SYSCON_PLL0CTRL_SELI(8U) | SYSCON_PLL0CTRL_SELP(31U),
|
|
.pllndec = SYSCON_PLL0NDEC_NDIV(125U),
|
|
.pllpdec = SYSCON_PLL0PDEC_PDIV(8U),
|
|
.pllsscg = {0x0U, (SYSCON_PLL0SSCG1_MDIV_EXT(3072U) | SYSCON_PLL0SSCG1_SEL_EXT_MASK)},
|
|
.pllRate = 24576000U,
|
|
.flags = PLL_SETUPFLAG_WAITLOCK};
|
|
/*!< Configure PLL to the desired values */
|
|
CLOCK_SetPLL0Freq(&pll0Setup);
|
|
}
|
|
else if (sample_rate % 11025 == 0)
|
|
{
|
|
const pll_setup_t pll0Setup = {
|
|
.pllctrl = SYSCON_PLL0CTRL_CLKEN_MASK | SYSCON_PLL0CTRL_SELI(8U) | SYSCON_PLL0CTRL_SELP(31U),
|
|
.pllndec = SYSCON_PLL0NDEC_NDIV(202U),
|
|
.pllpdec = SYSCON_PLL0PDEC_PDIV(8U),
|
|
.pllsscg = {0x0U, (SYSCON_PLL0SSCG1_MDIV_EXT(4561U) | SYSCON_PLL0SSCG1_SEL_EXT_MASK)},
|
|
.pllRate = 22579200U,
|
|
.flags = PLL_SETUPFLAG_WAITLOCK};
|
|
CLOCK_SetPLL0Freq(&pll0Setup); /*!< Configure PLL0 to the desired values */
|
|
}
|
|
|
|
divider = (CLOCK_GetPll0OutFreq() / sample_rate / bit_width / DEMO_CHANNEL_NUM);
|
|
|
|
/*
|
|
* masterSlave = kI2S_MasterSlaveNormalMaster;
|
|
* mode = kI2S_ModeI2sClassic;
|
|
* rightLow = false;
|
|
* leftJust = false;
|
|
* pdmData = false;
|
|
* sckPol = false;
|
|
* wsPol = false;
|
|
* divider = 1;
|
|
* oneChannel = false;
|
|
* dataLength = 16;
|
|
* frameLength = 32;
|
|
* position = 0;
|
|
* watermark = 4;
|
|
* txEmptyZero = true;
|
|
* pack48 = false;
|
|
*/
|
|
|
|
if (tx)
|
|
{
|
|
I2S_TxGetDefaultConfig(&pcmHandle.tx_config);
|
|
pcmHandle.tx_config.divider = divider;
|
|
pcmHandle.tx_config.masterSlave = DEMO_I2S_TX_MODE;
|
|
pcmHandle.tx_config.oneChannel = (pcm->num_channels == 1);
|
|
I2S_TxInit(DEMO_I2S_TX, &pcmHandle.tx_config);
|
|
I2S_TxTransferCreateHandleDMA(DEMO_I2S_TX, &pcmHandle.i2sTxHandle, &pcmHandle.i2sTxDmaHandle, TxCallback,
|
|
(void *)&pcmHandle);
|
|
}
|
|
else
|
|
{
|
|
I2S_RxGetDefaultConfig(&pcmHandle.rx_config);
|
|
pcmHandle.rx_config.divider = divider;
|
|
pcmHandle.rx_config.masterSlave = DEMO_I2S_RX_MODE;
|
|
pcmHandle.rx_config.oneChannel = (pcm->num_channels == 1);
|
|
I2S_RxInit(DEMO_I2S_RX, &pcmHandle.rx_config);
|
|
I2S_RxTransferCreateHandleDMA(DEMO_I2S_RX, &pcmHandle.i2sRxHandle, &pcmHandle.i2sRxDmaHandle, RxCallback,
|
|
(void *)&pcmHandle);
|
|
|
|
if (dummy_tx)
|
|
{
|
|
I2S_TxGetDefaultConfig(&pcmHandle.tx_config);
|
|
pcmHandle.tx_config.divider = divider;
|
|
pcmHandle.tx_config.masterSlave = DEMO_I2S_TX_MODE;
|
|
I2S_TxInit(DEMO_I2S_TX, &pcmHandle.tx_config);
|
|
}
|
|
}
|
|
|
|
ret = streamer_pcm_set_volume(pcm, 0);
|
|
if (ret != kStatus_Success)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
ret = CODEC_SetFormat(&codecHandle, CLOCK_GetPll0OutFreq(), sample_rate, bit_width);
|
|
if (ret != kStatus_Success)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
ret = streamer_pcm_set_volume(pcm, DEMO_VOLUME);
|
|
if (ret != kStatus_Success)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void streamer_pcm_getparams(pcm_rtos_t *pcm, uint32_t *sample_rate, uint32_t *bit_width, uint8_t *num_channels)
|
|
{
|
|
*sample_rate = pcm->sample_rate;
|
|
*bit_width = pcm->bit_width;
|
|
*num_channels = pcm->num_channels;
|
|
}
|
|
|
|
int streamer_pcm_mute(pcm_rtos_t *pcm, bool mute)
|
|
{
|
|
status_t ret;
|
|
ret = CODEC_SetMute(&codecHandle, kCODEC_PlayChannelHeadphoneLeft | kCODEC_PlayChannelHeadphoneRight, mute);
|
|
if (ret != kStatus_Success)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int streamer_pcm_set_volume(pcm_rtos_t *pcm, int volume)
|
|
{
|
|
int channel;
|
|
channel = (pcm->num_channels == 1) ? kCODEC_PlayChannelHeadphoneLeft :
|
|
kCODEC_PlayChannelHeadphoneLeft | kCODEC_PlayChannelHeadphoneRight;
|
|
|
|
if (volume <= 0)
|
|
CODEC_SetMute(&codecHandle, kCODEC_PlayChannelHeadphoneRight | kCODEC_PlayChannelHeadphoneLeft, true);
|
|
else
|
|
CODEC_SetVolume(&codecHandle, channel, volume > CODEC_VOLUME_MAX_VALUE ? CODEC_VOLUME_MAX_VALUE : volume);
|
|
|
|
return 0;
|
|
}
|