/* * 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; }