123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361 |
- /*
- * Copyright (c) 2006-2021, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2019-11-17 LiWeiHao First implementation
- */
- #include "drv_sound.h"
- #include "fsl_common.h"
- #include "fsl_iocon.h"
- #include "fsl_dma.h"
- #include "fsl_i2s.h"
- #include "fsl_i2s_dma.h"
- #include "fsl_wm8904.h"
- #include "fsl_i2c.h"
- #define TX_FIFO_SIZE (4096)
- #define I2S_TX I2S1
- #define I2S_RX I2S0
- #define I2S_DMA_TX 15
- #define I2S_DMA_RX 12
- #ifndef CODEC_I2C_NAME
- #define CODEC_I2C_NAME "i2c4"
- #endif
- struct sound_device
- {
- wm8904_handle_t wm8904_handle;
- dma_handle_t tx_dma_handle;
- i2s_dma_handle_t tx_i2s_dma_handle;
- struct rt_audio_device audio;
- struct rt_audio_configure replay_config;
- rt_uint8_t volume;
- rt_uint8_t *tx_fifo;
- };
- const pll_setup_t pll_setup =
- {
- .syspllctrl = SYSCON_SYSPLLCTRL_BANDSEL_MASK | SYSCON_SYSPLLCTRL_SELP(0x1FU) | SYSCON_SYSPLLCTRL_SELI(0x8U),
- .syspllndec = SYSCON_SYSPLLNDEC_NDEC(0x2DU),
- .syspllpdec = SYSCON_SYSPLLPDEC_PDEC(0x42U),
- .syspllssctrl = {SYSCON_SYSPLLSSCTRL0_MDEC(0x34D3U) | SYSCON_SYSPLLSSCTRL0_SEL_EXT_MASK, 0x00000000U},
- .pllRate = 24576000U, /* 16 bits * 2 channels * 44.1 kHz * 16 */
- .flags = PLL_SETUPFLAG_WAITLOCK
- };
- static struct sound_device snd_dev;
- void i2s_tx_transfer_callback(I2S_Type *base,
- i2s_dma_handle_t *handle,
- status_t completionStatus,
- void *userData)
- {
- struct sound_device *snd_dev = (struct sound_device *)userData;
- rt_audio_tx_complete(&snd_dev->audio);
- }
- static rt_err_t lpc_audio_init(struct rt_audio_device *audio)
- {
- i2s_config_t tx_i2s_config;
- wm8904_config_t wm8904_config;
- CLOCK_EnableClock(kCLOCK_Iocon);
- CLOCK_EnableClock(kCLOCK_InputMux);
- CLOCK_EnableClock(kCLOCK_Gpio0);
- CLOCK_EnableClock(kCLOCK_Gpio1);
- CLOCK_AttachClk(kFRO12M_to_SYS_PLL);
- CLOCK_AttachClk(kSYS_PLL_to_FLEXCOMM7);
- RESET_PeripheralReset(kFC7_RST_SHIFT_RSTn);
- CLOCK_SetPLLFreq(&pll_setup);
- CLOCK_AttachClk(kSYS_PLL_to_MCLK);
- SYSCON->MCLKDIV = SYSCON_MCLKDIV_DIV(0U);
- // Flexcomm 7 I2S Tx
- IOCON_PinMuxSet(IOCON, 1, 12, IOCON_FUNC4 | IOCON_DIGITAL_EN); /* Flexcomm 7 / SCK */
- IOCON_PinMuxSet(IOCON, 1, 13, IOCON_FUNC4 | IOCON_DIGITAL_EN); /* Flexcomm 7 / SDA */
- IOCON_PinMuxSet(IOCON, 1, 14, IOCON_FUNC4 | IOCON_DIGITAL_EN); /* Flexcomm 7 / WS */
- /* MCLK output for I2S */
- IOCON_PinMuxSet(IOCON, 1, 17, IOCON_FUNC4 | IOCON_MODE_INACT | IOCON_DIGITAL_EN);
- SYSCON->MCLKIO = 1U;
- WM8904_GetDefaultConfig(&wm8904_config);
- snd_dev.wm8904_handle.i2c = (struct rt_i2c_bus_device *)rt_device_find(CODEC_I2C_NAME);
- if (WM8904_Init(&snd_dev.wm8904_handle, &wm8904_config) != kStatus_Success)
- {
- rt_kprintf("wm8904 init failed\n");
- return -RT_ERROR;
- }
- WM8904_SetMute(&snd_dev.wm8904_handle, RT_TRUE, RT_TRUE);
- I2S_TxGetDefaultConfig(&tx_i2s_config);
- tx_i2s_config.divider = CLOCK_GetPllOutFreq() / 48000U / 16 / 2;
- I2S_TxInit(I2S_TX, &tx_i2s_config);
- DMA_Init(DMA0);
- DMA_EnableChannel(DMA0, I2S_DMA_TX);
- DMA_SetChannelPriority(DMA0, I2S_DMA_TX, kDMA_ChannelPriority3);
- DMA_CreateHandle(&snd_dev.tx_dma_handle, DMA0, I2S_DMA_TX);
- I2S_TxTransferCreateHandleDMA(I2S_TX,
- &snd_dev.tx_i2s_dma_handle,
- &snd_dev.tx_dma_handle,
- i2s_tx_transfer_callback,
- (void *)&snd_dev);
- return RT_EOK;
- }
- static rt_err_t lpc_audio_start(struct rt_audio_device *audio, int stream)
- {
- RT_ASSERT(audio != RT_NULL);
- if (stream == AUDIO_STREAM_REPLAY)
- {
- struct rt_audio_caps caps;
- caps.main_type = AUDIO_TYPE_MIXER;
- caps.sub_type = AUDIO_MIXER_VOLUME;
- audio->ops->getcaps(audio, &caps);
- audio->ops->configure(audio, &caps);
- rt_audio_tx_complete(audio);
- }
- return RT_EOK;
- }
- static rt_err_t lpc_audio_stop(struct rt_audio_device *audio, int stream)
- {
- if (stream == AUDIO_STREAM_REPLAY)
- {
- WM8904_SetMute(&snd_dev.wm8904_handle, RT_TRUE, RT_TRUE);
- I2S_TransferAbortDMA(I2S_TX, &snd_dev.tx_i2s_dma_handle);
- }
- return RT_EOK;
- }
- static rt_err_t lpc_audio_getcaps(struct rt_audio_device *audio, struct rt_audio_caps *caps)
- {
- rt_err_t result = RT_EOK;
- struct sound_device *snd_dev;
- RT_ASSERT(audio != RT_NULL);
- snd_dev = (struct sound_device *)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_OUTPUT: /* Provide capabilities of OUTPUT unit */
- {
- switch (caps->sub_type)
- {
- case AUDIO_DSP_PARAM:
- caps->udata.config.samplerate = snd_dev->replay_config.samplerate;
- caps->udata.config.channels = snd_dev->replay_config.channels;
- caps->udata.config.samplebits = snd_dev->replay_config.samplebits;
- break;
- case AUDIO_DSP_SAMPLERATE:
- caps->udata.config.samplerate = snd_dev->replay_config.samplerate;
- break;
- case AUDIO_DSP_CHANNELS:
- caps->udata.config.channels = snd_dev->replay_config.channels;
- break;
- case AUDIO_DSP_SAMPLEBITS:
- caps->udata.config.samplebits = snd_dev->replay_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 = snd_dev->volume;
- break;
- default:
- result = -RT_ERROR;
- break;
- }
- break;
- }
- default:
- result = -RT_ERROR;
- break;
- }
- return result;
- }
- static rt_err_t lpc_audio_configure(struct rt_audio_device *audio, struct rt_audio_caps *caps)
- {
- rt_err_t result = RT_EOK;
- struct sound_device *snd_dev = audio->parent.user_data;
- switch (caps->main_type)
- {
- case AUDIO_TYPE_MIXER:
- {
- switch (caps->sub_type)
- {
- case AUDIO_MIXER_MUTE:
- {
- WM8904_SetMute(&snd_dev->wm8904_handle, RT_TRUE, RT_TRUE);
- snd_dev->volume = 0;
- break;
- }
- case AUDIO_MIXER_VOLUME:
- {
- int volume = caps->udata.value / 2;
- WM8904_SetMute(&snd_dev->wm8904_handle, RT_FALSE, RT_FALSE);
- WM8904_SetVolume(&snd_dev->wm8904_handle, volume, volume);
- snd_dev->volume = volume;
- break;
- }
- }
- break;
- }
- case AUDIO_TYPE_OUTPUT:
- {
- switch (caps->sub_type)
- {
- case AUDIO_DSP_PARAM:
- {
- struct rt_audio_configure config = caps->udata.config;
- i2s_config_t tx_i2s_config;
- snd_dev->replay_config.channels = config.channels;
- snd_dev->replay_config.samplebits = config.samplebits;
- snd_dev->replay_config.samplerate = config.samplerate;
- I2S_TxGetDefaultConfig(&tx_i2s_config);
- tx_i2s_config.divider = CLOCK_GetPllOutFreq() / config.samplerate / 16 / 2;
- I2S_TxInit(I2S_TX, &tx_i2s_config);
- break;
- }
- case AUDIO_DSP_SAMPLERATE:
- {
- struct rt_audio_configure config = caps->udata.config;
- i2s_config_t tx_i2s_config;
- snd_dev->replay_config.samplerate = config.samplerate;
- I2S_TxGetDefaultConfig(&tx_i2s_config);
- tx_i2s_config.divider = CLOCK_GetPllOutFreq() / config.samplerate / 16 / 2;
- I2S_TxInit(I2S_TX, &tx_i2s_config);
- break;
- }
- default:
- result = -RT_ERROR;
- break;
- }
- break;
- }
- }
- return result;
- }
- static rt_size_t lpc_audio_transmit(struct rt_audio_device *audio, const void *writeBuf, void *readBuf, rt_size_t size)
- {
- RT_ASSERT(audio != RT_NULL);
- i2s_transfer_t transfer;
- transfer.data = (uint8_t *)writeBuf;
- transfer.dataSize = size;
- I2S_TxTransferSendDMA(I2S_TX, &snd_dev.tx_i2s_dma_handle, transfer);
- return RT_EOK;
- }
- static void lpc_audio_buffer_info(struct rt_audio_device *audio, struct rt_audio_buf_info *info)
- {
- RT_ASSERT(audio != RT_NULL);
- /**
- * TX_FIFO
- * +----------------+----------------+
- * | block1 | block2 |
- * +----------------+----------------+
- * \ block_size /
- */
- info->buffer = snd_dev.tx_fifo;
- info->total_size = TX_FIFO_SIZE;
- info->block_size = TX_FIFO_SIZE / 2;
- info->block_count = 2;
- }
- static struct rt_audio_ops audio_ops =
- {
- .getcaps = lpc_audio_getcaps,
- .configure = lpc_audio_configure,
- .init = lpc_audio_init,
- .start = lpc_audio_start,
- .stop = lpc_audio_stop,
- .transmit = lpc_audio_transmit,
- .buffer_info = lpc_audio_buffer_info,
- };
- int rt_hw_sound_init(void)
- {
- rt_uint8_t *tx_fifo = RT_NULL;
- tx_fifo = rt_malloc(TX_FIFO_SIZE);
- if (tx_fifo == NULL)
- {
- return -RT_ENOMEM;
- }
- snd_dev.tx_fifo = tx_fifo;
- /* init default configuration */
- {
- snd_dev.replay_config.samplerate = 44100;
- snd_dev.replay_config.channels = 2;
- snd_dev.replay_config.samplebits = 16;
- snd_dev.volume = 30;
- }
- snd_dev.audio.ops = &audio_ops;
- rt_audio_register(&snd_dev.audio, "sound0", RT_DEVICE_FLAG_WRONLY, &snd_dev);
- return RT_EOK;
- }
- INIT_DEVICE_EXPORT(rt_hw_sound_init);
|