123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580 |
- /*
- * Copyright (c) 2006-2022, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2018-4-30 misonyo the first version.
- */
- #include <rtthread.h>
- #include <rthw.h>
- #include <rtdef.h>
- #ifdef BSP_USING_AUDIO
- #define LOG_TAG "drv.sai"
- #include <drv_log.h>
- #include <rtdevice.h>
- #include "drivers/audio.h"
- #include "bsp_wm8960.h"
- #include "drv_i2c.h"
- #include "drv_sai.h"
- #define RX_DMA_FIFO_SIZE (2048)
- volatile rt_uint16_t rx_busy = 0;
- volatile rt_uint16_t tx_busy = 0;
- struct drv_sai sai_tx = {0};
- struct drv_sai sai_rx = {0};
- wm8960_config_t wm8960Config = {
- .route = kWM8960_RoutePlaybackandRecord,
- .rightInputSource = kWM8960_InputDifferentialMicInput2,
- .playSource = kWM8960_PlaySourceDAC,
- .slaveAddress = WM8960_I2C_ADDR,
- .bus = kWM8960_BusI2S,
- .format = {.mclk_HZ = 6144000U, .sampleRate = kWM8960_AudioSampleRate16KHz, .bitWidth = kWM8960_AudioBitWidth16bit},
- .master_slave = false,
- };
- const clock_audio_pll_config_t audioPllConfig =
- {
- .loopDivider = 32, /* PLL loop divider. Valid range for DIV_SELECT divider value: 27~54. */
- .postDivider = 1, /* Divider after the PLL, should only be 1, 2, 4, 8, 16. */
- .numerator = 77, /* 30 bit numerator of fractional loop divider. */
- .denominator = 100, /* 30 bit denominator of fractional loop divider */
- };
- sai_transfer_format_t format;
- sai_config_t config;
- sai_transfer_t xfer;
- struct imxrt_sai
- {
- struct rt_audio_device audio;
- struct rt_audio_configure play_config;
- rt_uint16_t volume;
- rt_uint8_t* tx_fifo;
- struct rt_i2c_bus_device* i2c_bus;
- rt_uint8_t* rx_fifo;
- };
- struct imxrt_sai imxrt_payer_dev = { 0 };
- static void sai_config(void)
- {
- #ifdef BSP_AUDIO_USING_DMA
- static struct saidma_tx_config sai_txdma = { .channel = 0U, .request = kDmaRequestMuxSai1Tx };
- sai_tx.dma_tx = &sai_txdma;
- sai_tx.dma_flag |= RT_DEVICE_FLAG_DMA_TX;
- #if defined (BSP_USING_AUDIO_RECORD)
- static struct saidma_rx_config sai_rxdma = { .channel = 1U, .request = kDmaRequestMuxSai1Rx };
- sai_rx.dma_rx = &sai_rxdma;
- #endif
- #endif
- }
- static void sai_TxDmaCallback(I2S_Type* base, sai_edma_handle_t* handle, rt_int32_t status, void* userData)
- {
- tx_busy = 1;
- rt_audio_tx_complete(&imxrt_payer_dev.audio);
- }
- #if defined (BSP_USING_AUDIO_RECORD)
- static void sai_RxDmaCallback(I2S_Type* base, sai_edma_handle_t* handle, rt_int32_t status, void* userData)
- {
- rx_busy = 1;
- rt_audio_rx_done(&imxrt_payer_dev.audio, &imxrt_payer_dev.rx_fifo[0], RX_DMA_FIFO_SIZE / 2);
- }
- #endif
- void BOARD_EnableSaiMclkOutput(rt_bool_t enable)
- {
- if(enable)
- {
- IOMUXC_GPR->GPR1 |= IOMUXC_GPR_GPR1_SAI1_MCLK_DIR_MASK;
- }
- else
- {
- IOMUXC_GPR->GPR1 &= (~IOMUXC_GPR_GPR1_SAI1_MCLK_DIR_MASK);
- }
- }
- void sai_format(void)
- {
- SAI_TransferTxCreateHandleEDMA(sai_tx.base, &sai_tx.dma_tx->txHandle, sai_TxDmaCallback, NULL, &sai_tx.dma_tx->edma);
- SAI_TransferTxSetFormatEDMA(sai_tx.base, &sai_tx.dma_tx->txHandle, &format, DEMO_SAI_CLK_FREQ, DEMO_SAI_CLK_FREQ);
- #if defined (BSP_USING_AUDIO_RECORD)
- SAI_TransferRxCreateHandleEDMA(sai_rx.base, &sai_rx.dma_rx->rxHandle, sai_RxDmaCallback, NULL, &sai_rx.dma_rx->edma);
- SAI_TransferRxSetFormatEDMA(sai_rx.base, &sai_rx.dma_rx->rxHandle, &format, DEMO_SAI_CLK_FREQ, DEMO_SAI_CLK_FREQ);
- #endif
- }
- void sai_init(void)
- {
- CLOCK_InitAudioPll(&audioPllConfig);
- CLOCK_SetMux(kCLOCK_Sai1Mux, DEMO_SAI1_CLOCK_SOURCE_SELECT);
- CLOCK_SetDiv(kCLOCK_Sai1PreDiv, DEMO_SAI1_CLOCK_SOURCE_PRE_DIVIDER);
- CLOCK_SetDiv(kCLOCK_Sai1Div, DEMO_SAI1_CLOCK_SOURCE_DIVIDER);
- BOARD_EnableSaiMclkOutput(RT_TRUE);
- EDMA_CreateHandle(&sai_tx.dma_tx->edma, DMA0, sai_tx.dma_tx->channel);
- DMAMUX_SetSource(DMAMUX, sai_tx.dma_tx->channel, (rt_uint8_t)sai_tx.dma_tx->request);
- DMAMUX_EnableChannel(DMAMUX, sai_tx.dma_tx->channel);
- SAI_TxGetDefaultConfig(&config);
- SAI_TxInit(sai_tx.base, &config);
- #if defined (BSP_USING_AUDIO_RECORD)
- EDMA_CreateHandle(&sai_rx.dma_rx->edma, DMA0, sai_rx.dma_rx->channel);
- DMAMUX_SetSource(DMAMUX, sai_rx.dma_rx->channel, (rt_uint8_t)sai_rx.dma_rx->request);
- DMAMUX_EnableChannel(DMAMUX, sai_rx.dma_rx->channel);
- SAI_RxGetDefaultConfig(&config);
- SAI_RxInit(sai_rx.base, &config);
- #endif
- format.bitWidth = kSAI_WordWidth16bits;
- format.channel = 0U;
- format.sampleRate_Hz = kSAI_SampleRate16KHz;
- format.masterClockHz = DEMO_SAI_CLK_FREQ;
- format.protocol = config.protocol;
- format.stereo = kSAI_Stereo;
- format.isFrameSyncCompact = true;
- format.watermark = FSL_FEATURE_SAI_FIFO_COUNT / 2U;
- SAI_TransferTxCreateHandleEDMA(sai_tx.base, &sai_tx.dma_tx->txHandle, sai_TxDmaCallback, NULL, &sai_tx.dma_tx->edma);
- SAI_TransferTxSetFormatEDMA(sai_tx.base, &sai_tx.dma_tx->txHandle, &format, DEMO_SAI_CLK_FREQ, DEMO_SAI_CLK_FREQ);
- #if defined (BSP_USING_AUDIO_RECORD)
- SAI_TransferRxCreateHandleEDMA(sai_rx.base, &sai_rx.dma_rx->rxHandle, sai_RxDmaCallback, NULL, &sai_rx.dma_rx->edma);
- SAI_TransferRxSetFormatEDMA(sai_rx.base, &sai_rx.dma_rx->rxHandle, &format, DEMO_SAI_CLK_FREQ, DEMO_SAI_CLK_FREQ);
- #endif
- }
- void SAI_samplerate_set(rt_uint32_t freq)
- {
- switch(freq)
- {
- case 48000:
- format.sampleRate_Hz = kSAI_SampleRate48KHz;
- break;
- case 44100:
- format.sampleRate_Hz = kSAI_SampleRate44100Hz;
- break;
- case 32000:
- format.sampleRate_Hz = kSAI_SampleRate32KHz;
- break;
- case 24000:
- format.sampleRate_Hz = kSAI_SampleRate24KHz;
- break;
- case 22050:
- format.sampleRate_Hz = kSAI_SampleRate22050Hz;
- break;
- case 16000:
- format.sampleRate_Hz = kSAI_SampleRate16KHz;
- break;
- case 12000:
- format.sampleRate_Hz = kSAI_SampleRate12KHz;
- break;
- case 11025:
- format.sampleRate_Hz = kSAI_SampleRate11025Hz;
- break;
- case 8000:
- format.sampleRate_Hz = kSAI_SampleRate8KHz;
- break;
- default:
- format.sampleRate_Hz = kSAI_SampleRate16KHz;
- break;
- }
- }
- void SAI_channels_set(rt_uint16_t channels)
- {
- switch(channels)
- {
- case 2:
- format.stereo = kSAI_Stereo;
- break;
- case 1:
- format.stereo = kSAI_MonoRight;
- break;
- case 0:
- format.stereo = kSAI_MonoLeft;
- break;
- default:
- format.stereo = kSAI_Stereo;
- break;
- }
- }
- void SAI_samplebits_set(rt_uint16_t samplebits)
- {
- switch(samplebits)
- {
- case 16:
- format.bitWidth = kSAI_WordWidth16bits;
- break;
- case 24:
- format.bitWidth = kSAI_WordWidth24bits;
- break;
- case 32:
- format.bitWidth = kSAI_WordWidth32bits;
- break;
- default:
- format.bitWidth = kSAI_WordWidth16bits;
- break;
- }
- }
- static rt_err_t imxrt_payer_getcaps(struct rt_audio_device* audio, struct rt_audio_caps* caps)
- {
- rt_err_t result = RT_EOK;
- RT_ASSERT(audio != RT_NULL);
- struct imxrt_sai* imxrt_audio = (struct imxrt_sai*)audio->parent.user_data;
- switch(caps->main_type)
- {
- case AUDIO_TYPE_QUERY: /* qurey the types of hw_codec device */
- {
- switch(caps->sub_type)
- {
- case AUDIO_TYPE_QUERY:
- caps->udata.mask = AUDIO_TYPE_OUTPUT | AUDIO_TYPE_MIXER;
- break;
- default:
- result = -RT_ERROR;
- break;
- }
- break;
- }
- case AUDIO_TYPE_INPUT:
- {
- switch(caps->sub_type)
- {
- case AUDIO_DSP_PARAM:
- caps->udata.config.channels = imxrt_audio->play_config.channels;
- caps->udata.config.samplebits = imxrt_audio->play_config.samplebits;
- caps->udata.config.samplerate = imxrt_audio->play_config.samplerate;
- break;
- case AUDIO_DSP_SAMPLERATE:
- caps->udata.config.samplerate = imxrt_audio->play_config.samplerate;
- break;
- case AUDIO_DSP_CHANNELS:
- caps->udata.config.channels = imxrt_audio->play_config.channels;
- break;
- case AUDIO_DSP_SAMPLEBITS:
- caps->udata.config.samplebits = imxrt_audio->play_config.samplebits;
- break;
- default:
- result = -RT_ERROR;
- break;
- }
- break;
- }
- case AUDIO_TYPE_OUTPUT: /* Provide capabilities of OUTPUT unit */
- {
- switch(caps->sub_type)
- {
- case AUDIO_DSP_PARAM:
- caps->udata.config.samplerate = imxrt_audio->play_config.samplerate;
- caps->udata.config.channels = imxrt_audio->play_config.channels;
- caps->udata.config.samplebits = imxrt_audio->play_config.samplebits;
- break;
- case AUDIO_DSP_SAMPLERATE:
- caps->udata.config.samplerate = imxrt_audio->play_config.samplerate;
- break;
- case AUDIO_DSP_CHANNELS:
- caps->udata.config.channels = imxrt_audio->play_config.channels;
- break;
- case AUDIO_DSP_SAMPLEBITS:
- caps->udata.config.samplebits = imxrt_audio->play_config.samplebits;
- break;
- default:
- result = -RT_ERROR;
- break;
- }
- break;
- }
- case AUDIO_TYPE_MIXER: /* report the Mixer Units */
- {
- switch(caps->sub_type)
- {
- case AUDIO_MIXER_QUERY:
- caps->udata.mask = AUDIO_MIXER_VOLUME;
- break;
- case AUDIO_MIXER_VOLUME:
- caps->udata.value = WM8960_GetVolume(imxrt_payer_dev.i2c_bus,kWM8960_ModuleDAC);
- break;
- default:
- result = -RT_ERROR;
- break;
- }
- break;
- }
- default:
- result = -RT_ERROR;
- break;
- }
- return result;
- }
- static rt_err_t imxrt_payer_configure(struct rt_audio_device* audio, struct rt_audio_caps* caps)
- {
- rt_err_t result = RT_EOK;
- RT_ASSERT(audio != RT_NULL);
- struct imxrt_sai* imxrt_audio = (struct imxrt_sai*)audio->parent.user_data;
- switch(caps->main_type)
- {
- case AUDIO_TYPE_MIXER:
- {
- switch(caps->sub_type)
- {
- case AUDIO_MIXER_MUTE:
- {
- /* set mute mode */
- WM8960_SetMute(imxrt_payer_dev.i2c_bus, kWM8960_ModuleDAC, RT_FALSE);
- break;
- }
- case AUDIO_MIXER_VOLUME:
- {
- int volume = caps->udata.value;
- imxrt_audio->volume = volume;
- /* set mixer volume */
- WM8960_SetVolume(imxrt_payer_dev.i2c_bus, kWM8960_ModuleDAC, volume);
- break;
- }
- default:
- result = -RT_ERROR;
- break;
- }
- break;
- }
- case AUDIO_TYPE_OUTPUT:
- {
- switch(caps->sub_type)
- {
- case AUDIO_DSP_PARAM:
- {
- struct rt_audio_configure config = caps->udata.config;
- imxrt_audio->play_config.samplerate = config.samplerate;
- imxrt_audio->play_config.samplebits = config.samplebits;
- imxrt_audio->play_config.channels = config.channels;
- SAI_channels_set(config.channels);
- SAI_samplerate_set(config.samplerate);
- SAI_samplebits_set(config.samplebits);
- break;
- }
- case AUDIO_DSP_SAMPLERATE:
- {
- imxrt_audio->play_config.samplerate = caps->udata.config.samplerate;
- SAI_samplerate_set(caps->udata.config.samplerate);
- break;
- }
- case AUDIO_DSP_CHANNELS:
- {
- imxrt_audio->play_config.channels = caps->udata.config.channels;
- SAI_channels_set(caps->udata.config.channels);
- break;
- }
- case AUDIO_DSP_SAMPLEBITS:
- {
- imxrt_audio->play_config.samplebits = caps->udata.config.samplebits;
- SAI_samplebits_set(caps->udata.config.samplebits);
- break;
- }
- default:
- result = -RT_ERROR;
- break;
- }
- break;
- }
- case AUDIO_TYPE_INPUT:
- {
- switch(caps->sub_type)
- {
- case AUDIO_DSP_PARAM:
- {
- imxrt_audio->play_config.samplerate = caps->udata.config.samplerate;
- imxrt_audio->play_config.channels = caps->udata.config.channels;
- imxrt_audio->play_config.samplebits = caps->udata.config.samplebits;
- SAI_TransferTerminateReceiveEDMA(sai_rx.base, &sai_rx.dma_rx->rxHandle);
- SAI_samplerate_set(caps->udata.config.samplerate);
- SAI_channels_set(caps->udata.config.channels);
- SAI_samplebits_set(caps->udata.config.samplebits);
- break;
- }
- case AUDIO_DSP_SAMPLERATE:
- {
- imxrt_audio->play_config.samplerate = caps->udata.config.samplerate;
- SAI_samplerate_set(caps->udata.config.samplerate);
- break;
- }
- case AUDIO_DSP_CHANNELS:
- {
- imxrt_audio->play_config.channels = caps->udata.config.channels;
- SAI_channels_set(caps->udata.config.channels);
- break;
- }
- case AUDIO_DSP_SAMPLEBITS:
- {
- imxrt_audio->play_config.samplebits = caps->udata.config.samplebits;
- SAI_samplebits_set(caps->udata.config.samplebits);
- break;
- }
- default:
- result = -RT_ERROR;
- break;
- }
- /* After set config, MCLK will stop */
- SAI_TxSoftwareReset(sai_tx.base, kSAI_ResetTypeSoftware);
- SAI_RxSoftwareReset(sai_rx.base, kSAI_ResetTypeSoftware);
- xfer.data = imxrt_payer_dev.tx_fifo; // +i * (AUD_FIFO_SIZE / 4);
- xfer.dataSize = AUD_BLOCK_SIZE;
- SAI_TransferSendEDMA(sai_tx.base, &sai_tx.dma_tx->txHandle, &xfer);
- SAI_TransferReceiveEDMA(sai_rx.base, &sai_rx.dma_rx->rxHandle, &xfer);
- break;
- }
- default:
- break;
- }
- return result;
- }
- static rt_err_t imxrt_payer_init(struct rt_audio_device* audio)
- {
- RT_ASSERT(audio != RT_NULL);
- imxrt_payer_dev.i2c_bus = (struct rt_i2c_bus_device*)rt_device_find(CODEC_I2C_NAME);
- sai_init();
- return RT_EOK;
- }
- static rt_err_t imxrt_payer_start(struct rt_audio_device* audio, int stream)
- {
- RT_ASSERT(audio != RT_NULL);
- sai_format();
- WM8960_init(imxrt_payer_dev.i2c_bus, &wm8960Config);
- xfer.data = imxrt_payer_dev.rx_fifo;
- xfer.dataSize = AUD_BLOCK_SIZE;
- #if defined (BSP_USING_AUDIO_RECORD)
- SAI_TransferReceiveEDMA(sai_rx.base, &sai_rx.dma_rx->rxHandle, &xfer);
- #endif
- SAI_TransferSendEDMA(sai_tx.base, &sai_tx.dma_tx->txHandle, &xfer);
- return RT_EOK;
- }
- static rt_err_t imxrt_payer_stop(struct rt_audio_device* audio, int stream)
- {
- RT_ASSERT(audio != RT_NULL);
- SAI_TransferTerminateSendEDMA(sai_tx.base, &sai_tx.dma_tx->txHandle);
- SAI_TransferTerminateReceiveEDMA(sai_rx.base, &sai_rx.dma_rx->rxHandle);
- WM8960_Deinit(imxrt_payer_dev.i2c_bus);
- return RT_EOK;
- }
- static rt_size_t imxrt_payer_transmit(struct rt_audio_device* audio, const void* writeBuf, void* readBuf, rt_size_t size)
- {
- RT_ASSERT(audio != RT_NULL);
- #if defined (BSP_USING_AUDIO_RECORD)
- xfer.data = imxrt_payer_dev.rx_fifo;
- xfer.dataSize = RX_DMA_FIFO_SIZE;
- SAI_TransferReceiveEDMA(sai_rx.base, &sai_rx.dma_rx->rxHandle, &xfer);
- SAI_TransferSendEDMA(sai_tx.base, &sai_tx.dma_tx->txHandle, &xfer);
- #else
- xfer.data = (rt_uint8_t*)writeBuf;
- xfer.dataSize = size;
- SAI_TransferSendEDMA(sai_tx.base, &sai_tx.dma_tx->txHandle, &xfer);
- #endif
- return size;
- }
- static void imxrt_payer_buffer_info(struct rt_audio_device* audio, struct rt_audio_buf_info* info)
- {
- RT_ASSERT(audio != RT_NULL);
- /**
- * AUD_FIFO
- * +----------------+----------------+
- * | block1 | block2 |
- * +----------------+----------------+
- * \ block_size /
- */
- info->buffer = imxrt_payer_dev.tx_fifo;
- info->total_size = AUD_DMA_FIFO_SIZE;
- info->block_size = AUD_DMA_FIFO_SIZE / 2;
- info->block_count = 2;
- }
- static struct rt_audio_ops imxrt_payer_ops =
- {
- .getcaps = imxrt_payer_getcaps,
- .configure = imxrt_payer_configure,
- .init = imxrt_payer_init,
- .start = imxrt_payer_start,
- .stop = imxrt_payer_stop,
- .transmit = imxrt_payer_transmit,
- .buffer_info = imxrt_payer_buffer_info,
- };
- int rt_hw_sound_init(void)
- {
- rt_uint8_t* tx_fifo = RT_NULL;
- rt_uint8_t* rx_fifo = RT_NULL;
- sai_tx.base = SAI1;
- sai_rx.base = SAI1;
- sai_tx.irqn = SAI1_IRQn;
- sai_config();
- tx_fifo = rt_calloc(1, AUD_DMA_FIFO_SIZE);
- rx_fifo = rt_calloc(1, AUD_DMA_FIFO_SIZE);
- if(tx_fifo == RT_NULL)
- {
- return -RT_ENOMEM;
- }
- rt_memset(tx_fifo, 0, AUD_DMA_FIFO_SIZE);
- imxrt_payer_dev.tx_fifo = tx_fifo;
- rt_memset(rx_fifo, 0, AUD_DMA_FIFO_SIZE);
- imxrt_payer_dev.rx_fifo = rx_fifo;
- imxrt_payer_dev.audio.ops = &imxrt_payer_ops;
- rt_audio_register(&imxrt_payer_dev.audio, "mic", RT_DEVICE_FLAG_RDWR, &imxrt_payer_dev);
- return RT_EOK;
- }
- INIT_DEVICE_EXPORT(rt_hw_sound_init);
- #endif /* BSP_USING_AUDIO*/
|