From c5612fd2e1196ccae30fe5062667f0f19d1e28ad Mon Sep 17 00:00:00 2001 From: thread-liu Date: Wed, 2 Dec 2020 17:28:35 +0800 Subject: [PATCH] [add] sai and wm8994 codec driver. --- bsp/stm32/libraries/STM32MPxx_HAL/SConscript | 2 - .../CM4/Inc/stm32mp1xx_hal_conf.h | 2 +- .../CM4/Src/stm32mp1xx_hal_msp.c | 198 ++++- bsp/stm32/stm32mp157a-st-ev1/board/Kconfig | 9 + bsp/stm32/stm32mp157a-st-ev1/board/SConscript | 4 + .../board/ports/drv_sound.c | 572 +++++++++++++ .../board/ports/drv_wm8994.c | 782 ++++++++++++++++++ .../board/ports/drv_wm8994.h | 80 ++ 8 files changed, 1645 insertions(+), 4 deletions(-) create mode 100644 bsp/stm32/stm32mp157a-st-ev1/board/ports/drv_sound.c create mode 100644 bsp/stm32/stm32mp157a-st-ev1/board/ports/drv_wm8994.c create mode 100644 bsp/stm32/stm32mp157a-st-ev1/board/ports/drv_wm8994.h diff --git a/bsp/stm32/libraries/STM32MPxx_HAL/SConscript b/bsp/stm32/libraries/STM32MPxx_HAL/SConscript index 1e1d1934dc..298fb80409 100644 --- a/bsp/stm32/libraries/STM32MPxx_HAL/SConscript +++ b/bsp/stm32/libraries/STM32MPxx_HAL/SConscript @@ -67,8 +67,6 @@ if GetDepend(['RT_USING_SDIO']): src += ['STM32MP1xx_HAL_Driver/Src/stm32mp1xx_ll_delayblock.c'] if GetDepend(['RT_USING_AUDIO']): - src += ['STM32MP1xx_HAL_Driver/Src/stm32mp1xx_hal_i2s.c'] - src += ['STM32MP1xx_HAL_Driver/Src/stm32mp1xx_hal_i2s_ex.c'] src += ['STM32MP1xx_HAL_Driver/Src/stm32mp1xx_hal_sai.c'] src += ['STM32MP1xx_HAL_Driver/Src/stm32mp1xx_hal_sai_ex.c'] diff --git a/bsp/stm32/stm32mp157a-st-ev1/board/CubeMX_Config/CM4/Inc/stm32mp1xx_hal_conf.h b/bsp/stm32/stm32mp157a-st-ev1/board/CubeMX_Config/CM4/Inc/stm32mp1xx_hal_conf.h index d03109a24f..572739c375 100644 --- a/bsp/stm32/stm32mp157a-st-ev1/board/CubeMX_Config/CM4/Inc/stm32mp1xx_hal_conf.h +++ b/bsp/stm32/stm32mp157a-st-ev1/board/CubeMX_Config/CM4/Inc/stm32mp1xx_hal_conf.h @@ -57,7 +57,7 @@ /*#define HAL_PCD_MODULE_ENABLED */ /*#define HAL_QSPI_MODULE_ENABLED */ /*#define HAL_RNG_MODULE_ENABLED */ -/*#define HAL_SAI_MODULE_ENABLED */ +#define HAL_SAI_MODULE_ENABLED #define HAL_SD_MODULE_ENABLED /*#define HAL_MMC_MODULE_ENABLED */ /*#define HAL_RTC_MODULE_ENABLED */ diff --git a/bsp/stm32/stm32mp157a-st-ev1/board/CubeMX_Config/CM4/Src/stm32mp1xx_hal_msp.c b/bsp/stm32/stm32mp157a-st-ev1/board/CubeMX_Config/CM4/Src/stm32mp1xx_hal_msp.c index 523df62925..5bf014d8b9 100644 --- a/bsp/stm32/stm32mp157a-st-ev1/board/CubeMX_Config/CM4/Src/stm32mp1xx_hal_msp.c +++ b/bsp/stm32/stm32mp157a-st-ev1/board/CubeMX_Config/CM4/Src/stm32mp1xx_hal_msp.c @@ -26,7 +26,9 @@ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN TD */ - +DMA_HandleTypeDef hdma_sai2_a = {0}; +DMA_HandleTypeDef hdma_sai2_b = {0}; +DMA_HandleTypeDef hdma_sai4_a = {0}; /* USER CODE END TD */ /* Private define ------------------------------------------------------------*/ @@ -1201,6 +1203,200 @@ void HAL_SD_MspDeInit(SD_HandleTypeDef* hsd) } } +void HAL_SAI_MspInit(SAI_HandleTypeDef* hsai) +{ + GPIO_InitTypeDef GPIO_InitStruct = {0}; + RCC_PeriphCLKInitTypeDef PeriphClkInit = {0}; + + /* SAI2 */ + if (hsai->Instance==SAI2_Block_A) + { + /* Peripheral clock enable */ + if(IS_ENGINEERING_BOOT_MODE()) + { + /** Initializes the peripherals clock + */ + PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_SAI2; + PeriphClkInit.Sai2ClockSelection = RCC_SAI2CLKSOURCE_PLL3_Q; + if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) + { + Error_Handler(); + } + } + + __HAL_RCC_GPIOE_CLK_ENABLE(); + __HAL_RCC_GPIOI_CLK_ENABLE(); + __HAL_RCC_GPIOF_CLK_ENABLE(); + __HAL_RCC_SAI2_CLK_ENABLE(); + + /**SAI2_A_Block_A GPIO Configuration + PE0 ------> SAI2_MCLK_A + PI7 ------> SAI2_FS_A + PI5 ------> SAI2_SCK_A + PI6 ------> SAI2_SD_A + */ + GPIO_InitStruct.Pin = GPIO_PIN_0; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; + GPIO_InitStruct.Alternate = GPIO_AF10_SAI2; + HAL_GPIO_Init(GPIOE, &GPIO_InitStruct); + + GPIO_InitStruct.Pin = GPIO_PIN_7|GPIO_PIN_5|GPIO_PIN_6; + HAL_GPIO_Init(GPIOI, &GPIO_InitStruct); + + /* Configure DMA used for SAI2 */ + __HAL_RCC_DMAMUX_CLK_ENABLE(); + __HAL_RCC_DMA1_CLK_ENABLE(); + + hdma_sai2_a.Instance = DMA1_Stream0; + hdma_sai2_a.Init.Request = DMA_REQUEST_SAI2_A; + hdma_sai2_a.Init.Direction = DMA_MEMORY_TO_PERIPH; + hdma_sai2_a.Init.PeriphInc = DMA_PINC_DISABLE; + hdma_sai2_a.Init.MemInc = DMA_MINC_ENABLE; + hdma_sai2_a.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; + hdma_sai2_a.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; + hdma_sai2_a.Init.Mode = DMA_CIRCULAR; + hdma_sai2_a.Init.Priority = DMA_PRIORITY_HIGH; + hdma_sai2_a.Init.FIFOMode = DMA_FIFOMODE_ENABLE; + hdma_sai2_a.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; + hdma_sai2_a.Init.MemBurst = DMA_MBURST_SINGLE; + hdma_sai2_a.Init.PeriphBurst = DMA_PBURST_SINGLE; + + HAL_DMA_DeInit(&hdma_sai2_a); + if (HAL_DMA_Init(&hdma_sai2_a) != HAL_OK) + { + Error_Handler(); + } + __HAL_LINKDMA(hsai,hdmatx,hdma_sai2_a); + __HAL_DMA_ENABLE(&hdma_sai2_a); + + HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 0x02, 0); + HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn); + } + + if (hsai->Instance==SAI2_Block_B) + { + /* Peripheral clock enable */ + if (IS_ENGINEERING_BOOT_MODE()) + { + /** Initializes the peripherals clock + */ + PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_SAI2; + PeriphClkInit.Sai2ClockSelection = RCC_SAI2CLKSOURCE_PLL3_Q; + if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) + { + Error_Handler(); + } + + } + __HAL_RCC_GPIOF_CLK_ENABLE(); + __HAL_RCC_SAI2_CLK_ENABLE(); + + /**SAI2_B_Block_B GPIO Configuration + PE12 ------> SAI2_MCLK_B + PE13 ------> SAI2_FS_B + PE14 ------> SAI2_SCK_B + PF11 ------> SAI2_SD_B + */ + + GPIO_InitStruct.Pin = GPIO_PIN_11; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + GPIO_InitStruct.Alternate = GPIO_AF10_SAI2; + HAL_GPIO_Init(GPIOF, &GPIO_InitStruct); + + GPIO_InitStruct.Pin = GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14; + HAL_GPIO_Init(GPIOE, &GPIO_InitStruct); + + __HAL_RCC_DMAMUX_CLK_ENABLE(); + __HAL_RCC_DMA1_CLK_ENABLE(); + + /* Peripheral DMA init*/ + hdma_sai2_b.Instance = DMA1_Stream1; + hdma_sai2_b.Init.Request = DMA_REQUEST_SAI2_B; + hdma_sai2_b.Init.Direction = DMA_PERIPH_TO_MEMORY; + hdma_sai2_b.Init.PeriphInc = DMA_PINC_DISABLE; + hdma_sai2_b.Init.MemInc = DMA_MINC_ENABLE; + hdma_sai2_b.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; + hdma_sai2_b.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; + hdma_sai2_b.Init.Mode = DMA_CIRCULAR; + hdma_sai2_b.Init.Priority = DMA_PRIORITY_HIGH; + hdma_sai2_b.Init.FIFOMode = DMA_FIFOMODE_ENABLE; + hdma_sai2_b.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; + hdma_sai2_b.Init.MemBurst = DMA_MBURST_SINGLE; + hdma_sai2_b.Init.PeriphBurst = DMA_PBURST_SINGLE; + + HAL_DMA_DeInit(&hdma_sai2_b); + if (HAL_DMA_Init(&hdma_sai2_b) != HAL_OK) + { + Error_Handler(); + } + __HAL_LINKDMA(hsai,hdmarx,hdma_sai2_b); + __HAL_DMA_ENABLE(&hdma_sai2_b); + HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 0x02, 0); + HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn); + } + /* SAI4 */ + if(hsai->Instance==SAI4_Block_A) + { + /* Peripheral clock enable */ + if(IS_ENGINEERING_BOOT_MODE()) + { + /** Initializes the peripherals clock + */ + PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_SAI4; + PeriphClkInit.Sai4ClockSelection = RCC_SAI4CLKSOURCE_PLL3_Q; + if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) + { + Error_Handler(); + } + + } + __HAL_RCC_GPIOB_CLK_ENABLE(); + __HAL_RCC_SAI4_CLK_ENABLE(); + + /**SAI4_A_Block_A GPIO Configuration + PB5 ------> SAI4_SD_A + */ + GPIO_InitStruct.Pin = GPIO_PIN_5; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; + GPIO_InitStruct.Alternate = GPIO_AF10_SAI4; + HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); + + /* Peripheral DMA init*/ + __HAL_RCC_DMAMUX_CLK_ENABLE(); + __HAL_RCC_DMA1_CLK_ENABLE(); + + hdma_sai4_a.Instance = DMA1_Stream2; + hdma_sai4_a.Init.Request = DMA_REQUEST_SAI4_A; + hdma_sai4_a.Init.Direction = DMA_MEMORY_TO_PERIPH; + hdma_sai4_a.Init.PeriphInc = DMA_PINC_DISABLE; + hdma_sai4_a.Init.MemInc = DMA_MINC_ENABLE; + hdma_sai4_a.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; + hdma_sai4_a.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; + hdma_sai4_a.Init.Mode = DMA_CIRCULAR; + hdma_sai4_a.Init.Priority = DMA_PRIORITY_HIGH; + hdma_sai4_a.Init.FIFOMode = DMA_FIFOMODE_ENABLE; + hdma_sai4_a.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; + hdma_sai4_a.Init.MemBurst = DMA_MBURST_SINGLE; + hdma_sai4_a.Init.PeriphBurst = DMA_PBURST_SINGLE; + + HAL_DMA_DeInit(&hdma_sai4_a); + if (HAL_DMA_Init(&hdma_sai4_a) != HAL_OK) + { + Error_Handler(); + } + __HAL_LINKDMA(hsai,hdmatx,hdma_sai4_a); + __HAL_DMA_ENABLE(&hdma_sai4_a); + HAL_NVIC_SetPriority(DMA1_Stream2_IRQn, 0x02, 0); + HAL_NVIC_EnableIRQ(DMA1_Stream2_IRQn); + } +} + /** * @brief This function is executed in case of error occurrence. * @retval None diff --git a/bsp/stm32/stm32mp157a-st-ev1/board/Kconfig b/bsp/stm32/stm32mp157a-st-ev1/board/Kconfig index 2851e9aa2f..1e601d8377 100644 --- a/bsp/stm32/stm32mp157a-st-ev1/board/Kconfig +++ b/bsp/stm32/stm32mp157a-st-ev1/board/Kconfig @@ -63,6 +63,15 @@ menu "Onboard Peripheral Drivers" default n endif endif + + config BSP_USING_AUDIO + bool "Enable Audio Device (WM8994)" + select RT_USING_AUDIO + select BSP_USING_PMIC + select BSP_USING_I2C + select BSP_USING_I2C2 + default n + endmenu menu "On-chip Peripheral Drivers" diff --git a/bsp/stm32/stm32mp157a-st-ev1/board/SConscript b/bsp/stm32/stm32mp157a-st-ev1/board/SConscript index 53043e2f75..26ec814da7 100644 --- a/bsp/stm32/stm32mp157a-st-ev1/board/SConscript +++ b/bsp/stm32/stm32mp157a-st-ev1/board/SConscript @@ -28,6 +28,10 @@ if GetDepend(['BSP_USING_SD_CARD']): if GetDepend(['BSP_USING_EMMC']): src += Glob('ports/drv_emmc.c') +if GetDepend(['BSP_USING_AUDIO']): + src += Glob('ports/drv_wm8994.c') + src += Glob('ports/drv_sound.c') + if GetDepend(['BSP_USING_OPENAMP']): src += Glob('CubeMX_Config/CM4/Src/ipcc.c') src += Glob('CubeMX_Config/CM4/Src/openamp.c') diff --git a/bsp/stm32/stm32mp157a-st-ev1/board/ports/drv_sound.c b/bsp/stm32/stm32mp157a-st-ev1/board/ports/drv_sound.c new file mode 100644 index 0000000000..b9cfae9fb2 --- /dev/null +++ b/bsp/stm32/stm32mp157a-st-ev1/board/ports/drv_sound.c @@ -0,0 +1,572 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2019-07-31 Zero-Free first implementation + * 2020-07-02 thread-liu Porting for STM32MP1 + */ + +#include "board.h" + +#ifdef BSP_USING_AUDIO +#include "drv_wm8994.h" +#include + +#define DRV_DEBUG +#define LOG_TAG "drv.sound" +#include + +#define SOUND_BUS_NAME "i2c2" + +#define TX_FIFO_SIZE (4096) +#if defined(__CC_ARM) || defined(__CLANG_ARM) +__attribute__((at(0x2FFC2000))) +#elif defined ( __GNUC__ ) +__attribute__((at(0x2FFC2000))) +#elif defined(__ICCARM__) +#pragma location = 0x2FFC2000 +#endif +static rt_uint8_t AUDIO_TX_FIFO[TX_FIFO_SIZE]; + +struct sound_device +{ + struct rt_audio_device audio; + struct rt_audio_configure replay_config; + rt_device_t decoder; + rt_uint8_t *tx_fifo; + rt_uint8_t volume; +}; +static struct sound_device snd_dev = {0}; + +SAI_HandleTypeDef hsai_BlockA2 = {0}; +extern DMA_HandleTypeDef hdma_sai2_a; + +static void rt_hw_sai2a_init(void) +{ + hsai_BlockA2.Instance = SAI2_Block_A; + hsai_BlockA2.Init.Protocol = SAI_FREE_PROTOCOL; + hsai_BlockA2.Init.AudioMode = SAI_MODEMASTER_TX; + hsai_BlockA2.Init.DataSize = SAI_DATASIZE_16; + hsai_BlockA2.Init.FirstBit = SAI_FIRSTBIT_MSB; + hsai_BlockA2.Init.ClockStrobing = SAI_CLOCKSTROBING_FALLINGEDGE; + hsai_BlockA2.Init.Synchro = SAI_ASYNCHRONOUS; + hsai_BlockA2.Init.OutputDrive = SAI_OUTPUTDRIVE_ENABLE; + hsai_BlockA2.Init.NoDivider = SAI_MASTERDIVIDER_ENABLE; + hsai_BlockA2.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_1QF; + hsai_BlockA2.Init.AudioFrequency = SAI_AUDIO_FREQUENCY_44K; + hsai_BlockA2.Init.SynchroExt = SAI_SYNCEXT_DISABLE; + hsai_BlockA2.Init.MonoStereoMode = SAI_STEREOMODE; + hsai_BlockA2.Init.CompandingMode = SAI_NOCOMPANDING; + hsai_BlockA2.Init.TriState = SAI_OUTPUT_NOTRELEASED; + hsai_BlockA2.Init.PdmInit.Activation = DISABLE; + hsai_BlockA2.Init.PdmInit.MicPairsNbr = 0; + hsai_BlockA2.Init.PdmInit.ClockEnable = SAI_PDM_CLOCK1_ENABLE; + + hsai_BlockA2.FrameInit.FrameLength = 64; + hsai_BlockA2.FrameInit.ActiveFrameLength = 32; + hsai_BlockA2.FrameInit.FSDefinition = SAI_FS_CHANNEL_IDENTIFICATION; + hsai_BlockA2.FrameInit.FSPolarity = SAI_FS_ACTIVE_LOW; + hsai_BlockA2.FrameInit.FSOffset = SAI_FS_BEFOREFIRSTBIT; + + hsai_BlockA2.SlotInit.FirstBitOffset = 0; + hsai_BlockA2.SlotInit.SlotSize = SAI_SLOTSIZE_DATASIZE; + hsai_BlockA2.SlotInit.SlotNumber = 2; + hsai_BlockA2.SlotInit.SlotActive = (SAI_SLOTACTIVE_0 | SAI_SLOTACTIVE_1 | SAI_SLOTACTIVE_2 | SAI_SLOTACTIVE_3); + + if(HAL_OK != HAL_SAI_Init(&hsai_BlockA2)) + { + Error_Handler(); + } + /* Enable SAI to generate clock used by audio driver */ + __HAL_SAI_ENABLE(&hsai_BlockA2); +} + +void DMA1_Stream0_IRQHandler(void) +{ + HAL_DMA_IRQHandler(&hdma_sai2_a); +} + +void HAL_SAI_TxHalfCpltCallback(SAI_HandleTypeDef *hsai) +{ + if (hsai == &hsai_BlockA2) + { + rt_audio_tx_complete(&snd_dev.audio); + } +} + +void HAL_SAI_TxCpltCallback(SAI_HandleTypeDef *hsai) +{ + if (hsai == &hsai_BlockA2) + { + rt_audio_tx_complete(&snd_dev.audio); + } +} + +void SAIA_Frequency_Set(uint32_t frequency) +{ + /* Disable SAI peripheral to allow access to SAI internal registers */ + __HAL_SAI_DISABLE(&hsai_BlockA2); + /* Update the SAI audio frequency configuration */ + hsai_BlockA2.Init.AudioFrequency = frequency; + HAL_SAI_Init(&hsai_BlockA2); + /* Enable SAI peripheral to generate MCLK */ + __HAL_SAI_ENABLE(&hsai_BlockA2); +} + +void SAIA_Channels_Set(uint8_t channels) +{ + if (channels == 1) + { + hsai_BlockA2.Init.MonoStereoMode = SAI_MONOMODE; + } + else + { + hsai_BlockA2.Init.MonoStereoMode = SAI_STEREOMODE; + } + + __HAL_SAI_DISABLE(&hsai_BlockA2); + HAL_SAI_Init(&hsai_BlockA2); + __HAL_SAI_ENABLE(&hsai_BlockA2); +} + +/** + * RT-Thread Audio Device Driver Interface + */ +static rt_err_t sound_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: + rt_device_control(snd_dev->decoder, GET_VOLUME, &(caps->udata.value)); + break; + + default: + result = -RT_ERROR; + break; + } + + break; + } + + default: + result = -RT_ERROR; + break; + } + + return result; +} + +static rt_err_t sound_configure(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_MIXER: + { + switch (caps->sub_type) + { + case AUDIO_MIXER_VOLUME: + { + rt_uint8_t volume = caps->udata.value; + + rt_device_control(snd_dev->decoder, SET_VOLUME, &volume); + + snd_dev->volume = volume; + + LOG_D("set volume %d", volume); + break; + } + + default: + result = -RT_ERROR; + break; + } + + break; + } + + case AUDIO_TYPE_OUTPUT: + { + switch (caps->sub_type) + { + case AUDIO_DSP_PARAM: + { + /* set samplerate */ + SAIA_Frequency_Set(caps->udata.config.samplerate); + /* set channels */ + SAIA_Channels_Set(caps->udata.config.channels); + + /* save configs */ + snd_dev->replay_config.samplerate = caps->udata.config.samplerate; + snd_dev->replay_config.channels = caps->udata.config.channels; + snd_dev->replay_config.samplebits = caps->udata.config.samplebits; + LOG_D("set samplerate %d", snd_dev->replay_config.samplerate); + break; + } + + case AUDIO_DSP_SAMPLERATE: + { + SAIA_Frequency_Set(caps->udata.config.samplerate); + snd_dev->replay_config.samplerate = caps->udata.config.samplerate; + LOG_D("set samplerate %d", snd_dev->replay_config.samplerate); + break; + } + + case AUDIO_DSP_CHANNELS: + { + SAIA_Channels_Set(caps->udata.config.channels); + snd_dev->replay_config.channels = caps->udata.config.channels; + LOG_D("set channels %d", snd_dev->replay_config.channels); + break; + } + + case AUDIO_DSP_SAMPLEBITS: + { + /* not support */ + snd_dev->replay_config.samplebits = caps->udata.config.samplebits; + break; + } + + default: + result = -RT_ERROR; + break; + } + + break; + } + + default: + break; + } + + return result; +} + +static rt_err_t sound_init(struct rt_audio_device *audio) +{ + rt_err_t result = RT_EOK; + struct sound_device *snd_dev; + rt_uint16_t play_type = OUTPUT_DEVICE_HEADPHONE; + + RT_ASSERT(audio != RT_NULL); + snd_dev = (struct sound_device *)audio->parent.user_data; + + rt_hw_sai2a_init(); + + /* set default params */ + SAIA_Frequency_Set(snd_dev->replay_config.samplerate); + SAIA_Channels_Set(snd_dev->replay_config.channels); + + /* set audio play type */ + rt_device_control(snd_dev->decoder, SET_PLAY_TYPE, &play_type); + /* open lowlevel audio device */ + rt_device_open(snd_dev->decoder, RT_DEVICE_OFLAG_WRONLY); + rt_device_init(snd_dev->decoder); + /* check device id */ + result = rt_device_control(snd_dev->decoder, GET_ID, RT_NULL); + if (result != RT_EOK) + { + LOG_E("can't find low level audio device!"); + return RT_ERROR; + } + + return result; +} + +static rt_err_t sound_start(struct rt_audio_device *audio, int stream) +{ + struct sound_device *snd_dev; + rt_uint16_t play_type = OUTPUT_DEVICE_HEADPHONE; + + RT_ASSERT(audio != RT_NULL); + snd_dev = (struct sound_device *)audio->parent.user_data; + + if (stream == AUDIO_STREAM_REPLAY) + { + LOG_D("open sound device"); + + rt_device_control(snd_dev->decoder, SET_PLAY_TYPE, &play_type); + rt_device_control(snd_dev->decoder, START_PLAY, RT_NULL); + + if (HAL_SAI_Transmit_DMA(&hsai_BlockA2, snd_dev->tx_fifo, TX_FIFO_SIZE / 2) != HAL_OK) + { + return RT_ERROR; + } + } + + return RT_EOK; +} + +static rt_err_t sound_stop(struct rt_audio_device *audio, int stream) +{ + struct sound_device *device; + RT_ASSERT(audio != RT_NULL); + device = (struct sound_device *)audio->parent.user_data; + + if (stream == AUDIO_STREAM_REPLAY) + { + HAL_SAI_DMAStop(&hsai_BlockA2); + + rt_device_close(device->decoder); + + LOG_D("close sound device"); + } + + return RT_EOK; +} + +static void sound_buffer_info(struct rt_audio_device *audio, struct rt_audio_buf_info *info) +{ + struct sound_device *device; + + RT_ASSERT(audio != RT_NULL); + device = (struct sound_device *)audio->parent.user_data; + + info->buffer = device->tx_fifo; + info->total_size = TX_FIFO_SIZE; + info->block_size = TX_FIFO_SIZE / 2; + info->block_count = 2; +} + +static struct rt_audio_ops snd_ops = +{ + .getcaps = sound_getcaps, + .configure = sound_configure, + .init = sound_init, + .start = sound_start, + .stop = sound_stop, + .transmit = RT_NULL, + .buffer_info = sound_buffer_info, +}; + +int rt_hw_sound_init(void) +{ + rt_err_t result = RT_EOK; + struct rt_device *device = RT_NULL; + + rt_memset(AUDIO_TX_FIFO, 0, TX_FIFO_SIZE); + snd_dev.tx_fifo = AUDIO_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 = 55; + + /* find lowlevel decoder device*/ + snd_dev.decoder = rt_device_find("decoder"); + if (snd_dev.decoder == RT_NULL) + { + LOG_E("cant't find lowlevel decoder deivce!"); + return RT_ERROR; + } + + /* register sound device */ + snd_dev.audio.ops = &snd_ops; + result = rt_audio_register(&snd_dev.audio, "sound0", RT_DEVICE_FLAG_WRONLY, &snd_dev); + /* check sound device register success or not */ + if (result != RT_EOK) + { + device = &(snd_dev.audio.parent); + rt_device_unregister(device); + LOG_E("sound device init error!"); + return RT_ERROR; + } + + return RT_EOK; +} + +INIT_APP_EXPORT(rt_hw_sound_init); + + +struct RIFF_HEADER_DEF +{ + char riff_id[4]; // 'R','I','F','F' + uint32_t riff_size; + char riff_format[4]; // 'W','A','V','E' +}; + +struct WAVE_FORMAT_DEF +{ + uint16_t FormatTag; + uint16_t Channels; + uint32_t SamplesPerSec; + uint32_t AvgBytesPerSec; + uint16_t BlockAlign; + uint16_t BitsPerSample; +}; + +struct FMT_BLOCK_DEF +{ + char fmt_id[4]; // 'f','m','t',' ' + uint32_t fmt_size; + struct WAVE_FORMAT_DEF wav_format; +}; + +struct DATA_BLOCK_DEF +{ + char data_id[4]; // 'R','I','F','F' + uint32_t data_size; +}; + +struct wav_info +{ + struct RIFF_HEADER_DEF header; + struct FMT_BLOCK_DEF fmt_block; + struct DATA_BLOCK_DEF data_block; +}; + +int wavplay_sample(int argc, char **argv) +{ +#define BUFSZ 1024 +#define SOUND_DEVICE_NAME "sound0" +static rt_device_t sound_dev; + + int fd = -1; + uint8_t *buffer = NULL; + struct wav_info *info = NULL; + struct rt_audio_caps caps = {0}; + + if (argc != 2) + { + rt_kprintf("Usage:\n"); + rt_kprintf("wavplay_sample song.wav\n"); + return 0; + } + + fd = open(argv[1], O_WRONLY); + if (fd < 0) + { + rt_kprintf("open file failed!\n"); + goto __exit; + } + + buffer = rt_malloc(BUFSZ); + if (buffer == RT_NULL) + goto __exit; + + info = (struct wav_info *) rt_malloc(sizeof * info); + if (info == RT_NULL) + goto __exit; + + if (read(fd, &(info->header), sizeof(struct RIFF_HEADER_DEF)) <= 0) + goto __exit; + if (read(fd, &(info->fmt_block), sizeof(struct FMT_BLOCK_DEF)) <= 0) + goto __exit; + if (read(fd, &(info->data_block), sizeof(struct DATA_BLOCK_DEF)) <= 0) + goto __exit; + + rt_kprintf("wav information:\n"); + rt_kprintf("samplerate %d\n", info->fmt_block.wav_format.SamplesPerSec); + rt_kprintf("channel %d\n", info->fmt_block.wav_format.Channels); + + sound_dev = rt_device_find(SOUND_DEVICE_NAME); + + rt_device_open(sound_dev, RT_DEVICE_OFLAG_WRONLY); + + caps.main_type = AUDIO_TYPE_OUTPUT; + caps.sub_type = AUDIO_DSP_PARAM; + caps.udata.config.samplerate = info->fmt_block.wav_format.SamplesPerSec; + caps.udata.config.channels = info->fmt_block.wav_format.Channels; + caps.udata.config.samplebits = 16; + rt_device_control(sound_dev, AUDIO_CTL_CONFIGURE, &caps); + + while (1) + { + int length; + + length = read(fd, buffer, BUFSZ); + + if (length <= 0) + break; + + rt_device_write(sound_dev, 0, buffer, length); + } + + rt_device_close(sound_dev); + +__exit: + + if (fd >= 0) + close(fd); + + if (buffer) + rt_free(buffer); + + if (info) + rt_free(info); + + return 0; +} + +MSH_CMD_EXPORT(wavplay_sample, play wav file); + +#endif diff --git a/bsp/stm32/stm32mp157a-st-ev1/board/ports/drv_wm8994.c b/bsp/stm32/stm32mp157a-st-ev1/board/ports/drv_wm8994.c new file mode 100644 index 0000000000..b162d76b0f --- /dev/null +++ b/bsp/stm32/stm32mp157a-st-ev1/board/ports/drv_wm8994.c @@ -0,0 +1,782 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2020-07-02 thread-liu first version + */ + +#include "board.h" + +#if defined(BSP_USING_AUDIO) +#include + +#define DRV_DEBUG +#define LOG_TAG "drv.wm8994" +#include + +#define CHIP_ADDRESS 0x1B /* wm8994 address */ +#define I2C_NAME "i2c2" + +struct wm8994_dev +{ + struct rt_device dev; + struct rt_i2c_bus_device *i2c_bus; + rt_uint16_t id; + rt_uint16_t type; +}; +static struct wm8994_dev rt_wm8994 = {0}; + +/* i2c read reg */ +static rt_err_t read_reg(struct rt_i2c_bus_device *bus, rt_uint16_t reg, rt_uint8_t len, rt_uint8_t *buf) +{ + struct rt_i2c_msg msg[2] = {0, 0}; + static rt_uint8_t i2c_reg[2] = {0, 0}; + + RT_ASSERT(bus != RT_NULL); + + i2c_reg[0] = ((uint16_t)(reg >> 8) & 0xFF); + i2c_reg[1] = ((uint16_t)(reg) & 0xFF); + + msg[0].addr = CHIP_ADDRESS; + msg[0].flags = RT_I2C_WR; + msg[0].buf = i2c_reg; + msg[0].len = 2; + + msg[1].addr = CHIP_ADDRESS; + msg[1].flags = RT_I2C_RD; + msg[1].len = len; + msg[1].buf = buf; + + if (rt_i2c_transfer(bus, msg, 2) == 2) + { + return RT_EOK; + } + + return RT_ERROR; +} + +/* i2c write reg */ +static rt_err_t write_reg(struct rt_i2c_bus_device *bus, rt_uint16_t reg, rt_uint16_t data) +{ + rt_uint8_t buf[4]; + struct rt_i2c_msg msgs; + + RT_ASSERT(bus != RT_NULL); + + buf[0] = ((uint16_t)(reg >> 8) & 0xFF); + buf[1] = ((uint16_t)(reg) & 0xFF); + + buf[2] = ((uint16_t)(data >> 8) & 0xFF); + buf[3] = ((uint16_t)(data) & 0xFF); + + msgs.addr = CHIP_ADDRESS; + msgs.flags = RT_I2C_WR; + msgs.buf = buf; + msgs.len = 4; + + if (rt_i2c_transfer(bus, &msgs, 1) == 1) + { + return RT_EOK; + } + + return RT_ERROR; +} + +static rt_err_t wm8994_set_output_mode(struct rt_i2c_bus_device *bus, rt_uint16_t mode) +{ + switch (mode & 0x000F) + { + case OUTPUT_DEVICE_SPEAKER: + /* Enable DAC1 (Left), Enable DAC1 (Right), + * Disable DAC2 (Left), Disable DAC2 (Right) */ + write_reg(bus, 0x0005, 0x0C0C); + + /* Enable the AIF1 Timeslot 0 (Left) to DAC 1 (Left) mixer path */ + write_reg(bus, 0x0601, 0x0000); + + /* Enable the AIF1 Timeslot 0 (Right) to DAC 1 (Right) mixer path */ + write_reg(bus, 0x0602, 0x0000); + + /* Disable the AIF1 Timeslot 1 (Left) to DAC 2 (Left) mixer path */ + write_reg(bus, 0x0604, 0x0002); + + /* Disable the AIF1 Timeslot 1 (Right) to DAC 2 (Right) mixer path */ + write_reg(bus, 0x0605, 0x0002); + break; + + case OUTPUT_DEVICE_HEADPHONE: + /* Disable DAC1 (Left), Disable DAC1 (Right), + Enable DAC2 (Left), Enable DAC2 (Right)*/ + write_reg(bus, 0x05, 0x0303); + + /* Enable the AIF1 Timeslot 0 (Left) to DAC 1 (Left) mixer path */ + write_reg(bus, 0x0601, 0x01); + + /* Enable the AIF1 Timeslot 0 (Right) to DAC 1 (Right) mixer path */ + write_reg(bus, 0x0602, 0x01); + + /* Disable the AIF1 Timeslot 1 (Left) to DAC 2 (Left) mixer path */ + write_reg(bus, 0x0604, 0x00); + + /* Disable the AIF1 Timeslot 1 (Right) to DAC 2 (Right) mixer path */ + write_reg(bus, 0x0605, 0x00); + break; + + case OUTPUT_DEVICE_BOTH: + default: + break; + } + + return RT_EOK; +} + +static rt_err_t wm8994_set_input_mode(struct rt_i2c_bus_device *bus, rt_uint16_t mode) +{ + switch (mode & 0x01F0) + { + case INPUT_DEVICE_DIGITAL_MICROPHONE_2: + /* Enable AIF1ADC2 (Left), Enable AIF1ADC2 (Right) + * Enable DMICDAT2 (Left), Enable DMICDAT2 (Right) + * Enable Left ADC, Enable Right ADC */ + write_reg(bus, 0x04, 0x0C30); + + /* Enable AIF1 DRC2 Signal Detect & DRC in AIF1ADC2 Left/Right Timeslot 1 */ + write_reg(bus, 0x0450, 0x00DB); + + /* Disable IN1L, IN1R, IN2L, IN2R, Enable Thermal sensor & shutdown */ + write_reg(bus, 0x02, 0x6000); + + /* Enable the DMIC2(Left) to AIF1 Timeslot 1 (Left) mixer path */ + write_reg(bus, 0x0608, 0x0002); + + /* Enable the DMIC2(Right) to AIF1 Timeslot 1 (Right) mixer path */ + write_reg(bus, 0x0609, 0x0002); + + /* GPIO1 pin configuration GP1_DIR = output, GP1_FN = AIF1 DRC2 signal detect */ + write_reg(bus, 0x0700, 0x000E); + break; + + case INPUT_DEVICE_INPUT_LINE_1: + /* IN1LN_TO_IN1L, IN1LP_TO_VMID, IN1RN_TO_IN1R, IN1RP_TO_VMID */ + write_reg(bus, 0x28, 0x0011); + + /* Disable mute on IN1L_TO_MIXINL and +30dB on IN1L PGA output */ + write_reg(bus, 0x29, 0x0035); + + /* Disable mute on IN1R_TO_MIXINL, Gain = +30dB */ + write_reg(bus, 0x2A, 0x0035); + + /* Enable AIF1ADC1 (Left), Enable AIF1ADC1 (Right) + * Enable Left ADC, Enable Right ADC */ + write_reg(bus, 0x04, 0x0303); + + /* Enable AIF1 DRC1 Signal Detect & DRC in AIF1ADC1 Left/Right Timeslot 0 */ + write_reg(bus, 0x0440, 0x00DB); + + /* Enable IN1L and IN1R, Disable IN2L and IN2R, Enable Thermal sensor & shutdown */ + write_reg(bus, 0x02, 0x6350); + + /* Enable the ADCL(Left) to AIF1 Timeslot 0 (Left) mixer path */ + write_reg(bus, 0x0606, 0x0002); + + /* Enable the ADCR(Right) to AIF1 Timeslot 0 (Right) mixer path */ + write_reg(bus, 0x0607, 0x0002); + + /* GPIO1 pin configuration GP1_DIR = output, GP1_FN = AIF1 DRC1 signal detect */ + write_reg(bus, 0x0700, 0x000D); + break; + + case INPUT_DEVICE_DIGITAL_MICROPHONE_1: + /* Enable AIF1ADC1 (Left), Enable AIF1ADC1 (Right) + * Enable DMICDAT1 (Left), Enable DMICDAT1 (Right) + * Enable Left ADC, Enable Right ADC */ + write_reg(bus, 0x04, 0x030C); + + /* Enable AIF1 DRC2 Signal Detect & DRC in AIF1ADC1 Left/Right Timeslot 0 */ + write_reg(bus, 0x0440, 0x00DB); + + /* Disable IN1L, IN1R, IN2L, IN2R, Enable Thermal sensor & shutdown */ + write_reg(bus, 0x02, 0x6350); + + /* Enable the DMIC2(Left) to AIF1 Timeslot 0 (Left) mixer path */ + write_reg(bus, 0x0606, 0x0002); + + /* Enable the DMIC2(Right) to AIF1 Timeslot 0 (Right) mixer path */ + write_reg(bus, 0x0607, 0x0002); + + /* GPIO1 pin configuration GP1_DIR = output, GP1_FN = AIF1 DRC1 signal detect */ + write_reg(bus, 0x0700, 0x000D); + break; + + case INPUT_DEVICE_DIGITAL_MIC1_MIC2: + /* Enable AIF1ADC1 (Left), Enable AIF1ADC1 (Right) + * Enable DMICDAT1 (Left), Enable DMICDAT1 (Right) + * Enable Left ADC, Enable Right ADC */ + write_reg(bus, 0x04, 0x0F3C); + + /* Enable AIF1 DRC2 Signal Detect & DRC in AIF1ADC2 Left/Right Timeslot 1 */ + write_reg(bus, 0x0450, 0x00DB); + + /* Enable AIF1 DRC2 Signal Detect & DRC in AIF1ADC1 Left/Right Timeslot 0 */ + write_reg(bus, 0x0440, 0x00DB); + + /* Disable IN1L, IN1R, Enable IN2L, IN2R, Thermal sensor & shutdown */ + write_reg(bus, 0x02, 0x63A0); + + /* Enable the DMIC2(Left) to AIF1 Timeslot 0 (Left) mixer path */ + write_reg(bus, 0x0606, 0x0002); + + /* Enable the DMIC2(Right) to AIF1 Timeslot 0 (Right) mixer path */ + write_reg(bus, 0x0607, 0x0002); + + /* Enable the DMIC2(Left) to AIF1 Timeslot 1 (Left) mixer path */ + write_reg(bus, 0x0608, 0x0002); + + /* Enable the DMIC2(Right) to AIF1 Timeslot 1 (Right) mixer path */ + write_reg(bus, 0x0609, 0x0002); + + /* GPIO1 pin configuration GP1_DIR = output, GP1_FN = AIF1 DRC1 signal detect */ + write_reg(bus, 0x0700, 0x000D); + break; + + case INPUT_DEVICE_INPUT_LINE_2: + default: + /* Actually, no other input devices supported */ + break; + } + + return RT_EOK; +} + +static rt_err_t _wm8994_init(struct wm8994_dev *dev) +{ + RT_ASSERT(dev != RT_NULL); + + /* wm8994 Errata Work-Arounds */ + write_reg(dev->i2c_bus, 0x0102, 0x0003); + write_reg(dev->i2c_bus, 0x0817, 0x0000); + write_reg(dev->i2c_bus, 0x0102, 0x0000); + + /* Enable VMID soft start (fast), Start-up Bias Current Enabled */ + write_reg(dev->i2c_bus, 0x0039, 0x006C); + + /* Enable bias generator, Enable VMID */ + if ((dev->type & 0x01F0) != 0) + { + /* audio input */ + write_reg(dev->i2c_bus, 0x0001, 0x0013); + } + else + { + /* audio output */ + write_reg(dev->i2c_bus, 0x0001, 0x0003); + } + rt_thread_mdelay(50); + + if ((dev->type & 0x000F) != 0 ) + { + /* Path Configurations for output */ + wm8994_set_output_mode(dev->i2c_bus, dev->type); + } + if ((dev->type & 0x01F0) != 0 ) + { + /* Path Configurations for input */ + wm8994_set_input_mode(dev->i2c_bus, dev->type); + } + + if (dev->type & INPUT_DEVICE_DIGITAL_MIC1_MIC2) + { + /* AIF1 Word Length = 16-bits, AIF1 Format = DSP mode */ + write_reg(dev->i2c_bus, 0x0300, 0x4018); + } + else + { + /* AIF1 Word Length = 16-bits, AIF1 Format = I2S (Default Register Value) */ + write_reg(dev->i2c_bus, 0x0300, 0x4010); + } + + /* slave mode */ + write_reg(dev->i2c_bus, 0x0302, 0x0000); + + /* Enable the DSP processing clock for AIF1, Enable the core clock */ + write_reg(dev->i2c_bus, 0x0208, 0x000A); + + /* Enable AIF1 Clock, AIF1 Clock Source = MCLK1 pin */ + write_reg(dev->i2c_bus, 0x0200, 0x0001); + + /* Audio output selected */ + if ((dev->type & 0x000F) != 0 ) + { + if (dev->type & OUTPUT_DEVICE_HEADPHONE) + { + /* Select DAC1 (Left) to Left Headphone Output PGA (HPOUT1LVOL) path */ + write_reg(dev->i2c_bus, 0x2D, 0x0100); + + /* Select DAC1 (Right) to Right Headphone Output PGA (HPOUT1RVOL) path */ + write_reg(dev->i2c_bus, 0x2E, 0x0100); + + /* Startup sequence for Headphone */ + write_reg(dev->i2c_bus, 0x0110, 0x8100); + + rt_thread_mdelay(300); + + /* Soft un-Mute the AIF1 Timeslot 0 DAC1 path L&R */ + write_reg(dev->i2c_bus, 0x0420, 0x0000); + } + + /* Enable SPKRVOL PGA, Enable SPKMIXR, Enable SPKLVOL PGA, Enable SPKMIXL */ + write_reg(dev->i2c_bus, 0x03, 0x0300); + + /* Left Speaker Mixer Volume = 0dB */ + write_reg(dev->i2c_bus, 0x22, 0x0000); + + /* Speaker output mode = Class D, Right Speaker Mixer Volume = 0dB ((0x23, 0x0100) = class AB)*/ + write_reg(dev->i2c_bus, 0x23, 0x0000); + + /* Unmute DAC2 (Left) to Left Speaker Mixer (SPKMIXL) path, + Unmute DAC2 (Right) to Right Speaker Mixer (SPKMIXR) path */ + write_reg(dev->i2c_bus, 0x36, 0x0300); + + /* Enable bias generator, Enable VMID, Enable SPKOUTL, Enable SPKOUTR */ + write_reg(dev->i2c_bus, 0x01, 0x3003); + + /* Headphone/Speaker Enable */ + + if (dev->type & INPUT_DEVICE_DIGITAL_MIC1_MIC2) + { + /* Enable Class W, Class W Envelope Tracking = AIF1 Timeslots 0 and 1 */ + write_reg(dev->i2c_bus, 0x51, 0x0205); + } + else + { + /* Enable Class W, Class W Envelope Tracking = AIF1 Timeslot 0 */ + write_reg(dev->i2c_bus, 0x51, 0x0005); + } + + /* Enable bias generator, Enable VMID, Enable HPOUT1 (Left) and Enable HPOUT1 (Right) input stages */ + /* idem for Speaker */ + write_reg(dev->i2c_bus, 0x01, 0x3303); + + /* Enable HPOUT1 (Left) and HPOUT1 (Right) intermediate stages */ + write_reg(dev->i2c_bus, 0x60, 0x0022); + + /* Enable Charge Pump */ + write_reg(dev->i2c_bus, 0x4C, 0x9F25); + + /* Add Delay */ + rt_thread_mdelay(15); + + /* Select DAC1 (Left) to Left Headphone Output PGA (HPOUT1LVOL) path */ + write_reg(dev->i2c_bus, 0x2D, 0x0001); + + /* Select DAC1 (Right) to Right Headphone Output PGA (HPOUT1RVOL) path */ + write_reg(dev->i2c_bus, 0x2E, 0x0001); + + /* Enable Left Output Mixer (MIXOUTL), Enable Right Output Mixer (MIXOUTR) */ + /* idem for SPKOUTL and SPKOUTR */ + write_reg(dev->i2c_bus, 0x03, 0x0330); + + /* Enable DC Servo and trigger start-up mode on left and right channels */ + write_reg(dev->i2c_bus, 0x54, 0x0033); + + /* Add Delay */ + rt_thread_mdelay(200); + + /* Enable HPOUT1 (Left) and HPOUT1 (Right) intermediate and output stages. Remove clamps */ + write_reg(dev->i2c_bus, 0x60, 0x00EE); + + /* Unmute DAC 1 (Left) */ + write_reg(dev->i2c_bus, 0x0610, 0x00C0); + + /* Unmute DAC 1 (Right) */ + write_reg(dev->i2c_bus, 0x0611, 0x00C0); + + /* Unmute the AIF1 Timeslot 0 DAC path */ + write_reg(dev->i2c_bus, 0x0420, 0x0000); + + /* Unmute DAC 2 (Left) */ + write_reg(dev->i2c_bus, 0x0612, 0x00C0); + + /* Unmute DAC 2 (Right) */ + write_reg(dev->i2c_bus, 0x0613, 0x00C0); + + /* Unmute the AIF1 Timeslot 1 DAC2 path */ + write_reg(dev->i2c_bus, 0x0422, 0x0000); + + } + + /* Audio input selected */ + if ((dev->type & 0x01F0) != 0 ) + { + if ((dev->type & INPUT_DEVICE_DIGITAL_MICROPHONE_1) || (dev->type & INPUT_DEVICE_DIGITAL_MICROPHONE_2)) + { + /* Enable Microphone bias 1 generator, Enable VMID */ + write_reg(dev->i2c_bus, 0x01, 0x0013); + + /* ADC oversample enable */ + write_reg(dev->i2c_bus, 0x0620, 0x0002); + + /* AIF ADC2 HPF enable, HPF cut = voice mode 1 fc=127Hz at fs=8kHz */ + write_reg(dev->i2c_bus, 0x0411, 0x3800); + } + else if (dev->type & INPUT_DEVICE_DIGITAL_MIC1_MIC2) + { + /* Enable Microphone bias 1 generator, Enable VMID */ + write_reg(dev->i2c_bus, 0x01, 0x0013); + + /* ADC oversample enable */ + write_reg(dev->i2c_bus, 0x0620, 0x0002); + + /* AIF ADC1 HPF enable, HPF cut = voice mode 1 fc=127Hz at fs=8kHz */ + write_reg(dev->i2c_bus, 0x0410, 0x1800); + + /* AIF ADC2 HPF enable, HPF cut = voice mode 1 fc=127Hz at fs=8kHz */ + write_reg(dev->i2c_bus, 0x0411, 0x1800); + } + else if ((dev->type & INPUT_DEVICE_INPUT_LINE_1) || (dev->type & INPUT_DEVICE_INPUT_LINE_2)) + { + + /* Disable mute on IN1L, IN1L Volume = +0dB */ + write_reg(dev->i2c_bus, 0x18, 0x000B); + + /* Disable mute on IN1R, IN1R Volume = +0dB */ + write_reg(dev->i2c_bus, 0x1A, 0x000B); + + /* AIF ADC1 HPF enable, HPF cut = hifi mode fc=4Hz at fs=48kHz */ + write_reg(dev->i2c_bus, 0x0410, 0x1800); + } + } + + /* Return communication control value */ + return RT_EOK; + +} + +static rt_err_t _read_id(struct rt_i2c_bus_device *bus, rt_uint16_t *id) +{ + rt_uint8_t read_value[2]; + + read_reg(bus, 0x0000, 2, read_value); + *id = ((uint16_t)(read_value[0] << 8) & 0xFF00); + *id |= ((uint16_t)(read_value[1])& 0x00FF); + + if (*id != WM8994_ID) + { + LOG_E("error id: 0x%04x", *id); + return RT_ERROR; + } + + LOG_I("wm8994 init success, id: %04x", *id); + + return RT_EOK; +} + +static rt_err_t _set_mute(struct rt_i2c_bus_device *bus, uint32_t cmd) +{ + /* Set the Mute mode */ + if (cmd == AUDIO_MUTE_ON) + { + /* Soft Mute the AIF1 Timeslot 0 DAC1 path L&R */ + write_reg(bus, 0x420, 0x0200); + + /* Soft Mute the AIF1 Timeslot 1 DAC2 path L&R */ + write_reg(bus, 0x422, 0x0200); + } + else /* AUDIO_MUTE_OFF Disable the Mute */ + { + /* Unmute the AIF1 Timeslot 0 DAC1 path L&R */ + write_reg(bus, 0x420, 0x0010); + + /* Unmute the AIF1 Timeslot 1 DAC2 path L&R */ + write_reg(bus, 0x422, 0x0010); + } + + return RT_EOK; +} + +static rt_err_t _play(struct rt_i2c_bus_device *bus) +{ + _set_mute(bus, AUDIO_MUTE_OFF); + + return RT_EOK; +} + +static rt_err_t _set_volume(struct rt_i2c_bus_device *bus, rt_uint16_t type, rt_uint8_t volume) +{ + rt_uint8_t convertedvol = VOLUME_CONVERT(volume); + + if (type & 0x000F) + { + /* Output volume */ + if(convertedvol > 0x3E) + { + /* Unmute audio codec */ + _set_mute(bus, AUDIO_MUTE_OFF); + + /* Left Headphone Volume */ + write_reg(bus, 0x1C, 0x3F | 0x140); + + /* Right Headphone Volume */ + write_reg(bus, 0x1D, 0x3F | 0x140); + + /* Left Speaker Volume */ + write_reg(bus, 0x26, 0x3F | 0x140); + + /* Right Speaker Volume */ + write_reg(bus, 0x27, 0x3F | 0x140); + } + else if (volume == 0) + { + /* Mute audio codec */ + _set_mute(bus, AUDIO_MUTE_ON); + } + else + { + /* Unmute audio codec */ + _set_mute(bus, AUDIO_MUTE_OFF); + + /* Left Headphone Volume */ + write_reg(bus, 0x1C, convertedvol | 0x140); + + /* Right Headphone Volume */ + write_reg(bus, 0x1D, convertedvol | 0x140); + + /* Left Speaker Volume */ + write_reg(bus, 0x26, convertedvol | 0x140); + + /* Right Speaker Volume */ + write_reg(bus, 0x27, convertedvol | 0x140); + } + } + /* Input volume */ + else + { + convertedvol = VOLUME_IN_CONVERT(volume); + + /* Left AIF1 ADC1 volume */ + write_reg(bus, 0x400, convertedvol | 0x100); + + /* Right AIF1 ADC1 volume */ + write_reg(bus, 0x401, convertedvol | 0x100); + + /* Left AIF1 ADC2 volume */ + write_reg(bus, 0x404, convertedvol | 0x100); + + /* Right AIF1 ADC2 volume */ + write_reg(bus, 0x405, convertedvol | 0x100); + } + + return RT_EOK; +} + +static rt_err_t _get_volume(struct rt_i2c_bus_device *bus, rt_uint32_t *value) +{ + rt_uint8_t read_value[2]; + + read_reg(bus, 0x001C, 2, read_value); + + *value = ((uint16_t)(read_value[0] << 8) & 0xFF00); + *value |= ((uint16_t)(read_value[1])& 0x00FF); + + return RT_EOK; +} + +static rt_err_t _set_frequency(struct rt_i2c_bus_device *bus, rt_uint32_t freq) +{ + switch (freq) + { + case AUDIO_FREQUENCY_8K: + write_reg(bus, 0x210, 0x0003); + break; + + case AUDIO_FREQUENCY_16K: + write_reg(bus, 0x210, 0x0033); + break; + + case AUDIO_FREQUENCY_32K: + write_reg(bus, 0x210, 0x0063); + break; + + case AUDIO_FREQUENCY_48K: + write_reg(bus, 0x210, 0x0083); + break; + + case AUDIO_FREQUENCY_96K: + write_reg(bus, 0x210, 0x00A3); + break; + + case AUDIO_FREQUENCY_11K: + write_reg(bus, 0x210, 0x0013); + break; + + case AUDIO_FREQUENCY_22K: + write_reg(bus, 0x210, 0x0043); + break; + + case AUDIO_FREQUENCY_44K: + write_reg(bus, 0x210, 0x0073); + break; + + default: + write_reg(bus, 0x210, 0x0083); + break; + } + + return RT_EOK; +} + +static rt_err_t _reset(struct rt_i2c_bus_device *bus) +{ + /* Reset Codec by writing in 0x0000 address register */ + write_reg(bus, 0x0000, 0x0000); + + return RT_EOK; +} + +static rt_err_t rt_wm8994_init(rt_device_t dev) +{ + RT_ASSERT(dev != RT_NULL); + rt_err_t result = RT_EOK; + static rt_uint16_t old_type = DEVICE_NONE; + + struct wm8994_dev *device = (struct wm8994_dev *)dev; + + if (old_type == device->type) + { + return RT_EOK; + } + + old_type = device->type; + + device->i2c_bus = rt_i2c_bus_device_find(I2C_NAME); + if (device->i2c_bus == RT_NULL) + { + LOG_E("can't find %c deivce", I2C_NAME); + return RT_ERROR; + } + + result = _wm8994_init(device); + /* set volume */ + _set_volume(device->i2c_bus, device->type, VOLUME_CONVERT(100)); + /* set frequency */ + _set_frequency(device->i2c_bus, AUDIO_FREQUENCY_44K); + + return result; +} + +static rt_err_t rt_wm8994_open(rt_device_t dev, rt_uint16_t oflag) +{ + RT_ASSERT(dev != RT_NULL); + + return RT_EOK; +} + +static rt_err_t rt_wm8994_close(rt_device_t dev) +{ + RT_ASSERT(dev != RT_NULL); + struct wm8994_dev *device = (struct wm8994_dev *)dev; + + _set_mute(device->i2c_bus, AUDIO_MUTE_ON); + + /* Mute the AIF1 Timeslot 0 DAC1 path */ + write_reg(device->i2c_bus, 0x420, 0x0200); + /* Mute the AIF1 Timeslot 1 DAC2 path */ + write_reg(device->i2c_bus, 0x422, 0x0200); + /* Disable DAC1L_TO_HPOUT1L */ + write_reg(device->i2c_bus, 0x2D, 0x0000); + /* Disable DAC1R_TO_HPOUT1R */ + write_reg(device->i2c_bus, 0x2E, 0x0000); + /* Disable DAC1 and DAC2 */ + write_reg(device->i2c_bus, 0x05, 0x0000); + /* Reset Codec by writing in 0x0000 address register */ + write_reg(device->i2c_bus, 0x0000, 0x0000); + + return RT_EOK; +} + +static rt_size_t rt_wm8994_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size) +{ + RT_ASSERT(dev != RT_NULL); + + return RT_EOK; +} + +static rt_size_t rt_wm8994_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size) +{ + RT_ASSERT(dev != RT_NULL); + + return RT_EOK; +} + +static rt_err_t rt_wm8994_control(rt_device_t dev, int cmd, void *args) +{ + RT_ASSERT(dev != RT_NULL); + struct wm8994_dev *device = (struct wm8994_dev *)dev; + rt_err_t result = RT_EOK; + switch (cmd) + { + case GET_ID: + result = _read_id(device->i2c_bus, (rt_uint16_t*)args); + break; + + case SET_FREQUENCE: + result = _set_frequency(device->i2c_bus, (*(rt_uint32_t *)args)); + break; + + case SET_VOLUME: + result = _set_volume(device->i2c_bus, device->type, (*(rt_uint8_t*)args)); + break; + + case GET_VOLUME: + result = _get_volume(device->i2c_bus, (rt_uint32_t *)args); + break; + + case SET_MUTE: + result = _set_mute(device->i2c_bus, (*(rt_uint32_t*)args)); + break; + + case SET_RESET: + result = _reset(device->i2c_bus); + break; + + case START_PLAY: + result = _play(device->i2c_bus); + break; + + case SET_PLAY_TYPE: + device->type = 0; + device->type = *(rt_uint32_t *)args; + rt_wm8994_init(dev); + break; + + default: + LOG_D("not support cmd"); + break; + } + + return result; +} + +int wm8994_init(void) +{ + rt_wm8994.dev.type = RT_Device_Class_Sound; + rt_wm8994.dev.init = rt_wm8994_init; + rt_wm8994.dev.open = rt_wm8994_open; + rt_wm8994.dev.close = rt_wm8994_close; + rt_wm8994.dev.read = rt_wm8994_read; + rt_wm8994.dev.write = rt_wm8994_write; + rt_wm8994.dev.control = rt_wm8994_control; + rt_wm8994.dev.user_data = RT_NULL; + + rt_device_register(&rt_wm8994.dev, "decoder", RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_REMOVABLE | RT_DEVICE_FLAG_STANDALONE); + + LOG_I("lowlevel decoder device init success!"); + + return RT_EOK; +} +INIT_DEVICE_EXPORT(wm8994_init); + +#endif diff --git a/bsp/stm32/stm32mp157a-st-ev1/board/ports/drv_wm8994.h b/bsp/stm32/stm32mp157a-st-ev1/board/ports/drv_wm8994.h new file mode 100644 index 0000000000..872e02b162 --- /dev/null +++ b/bsp/stm32/stm32mp157a-st-ev1/board/ports/drv_wm8994.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2020-07-02 thread-liu first version + */ + +#ifndef __DRV_WM8994_H__ +#define __DRV_WM8994_H__ + +#include "board.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum{ + GET_ID, + SET_FREQUENCE, + SET_VOLUME, + GET_VOLUME, + SET_MUTE, + SET_RESET, + START_PLAY, + SET_PLAY_TYPE, +}; + +/* codec device play type */ +#define DEVICE_NONE ((uint16_t)0x0000) +#define OUTPUT_DEVICE_SPEAKER ((uint16_t)0x0001) +#define OUTPUT_DEVICE_HEADPHONE ((uint16_t)0x0002) +#define OUTPUT_DEVICE_BOTH ((uint16_t)0x0004) +#define OUTPUT_DEVICE_AUTO ((uint16_t)0x0008) +#define INPUT_DEVICE_DIGITAL_MICROPHONE_1 ((uint16_t)0x0010) +#define INPUT_DEVICE_DIGITAL_MICROPHONE_2 ((uint16_t)0x0020) +#define INPUT_DEVICE_INPUT_LINE_1 ((uint16_t)0x0040) +#define INPUT_DEVICE_INPUT_LINE_2 ((uint16_t)0x0080) +#define INPUT_DEVICE_DIGITAL_MIC1_MIC2 ((uint16_t)0x0100) + +/* volume levels values */ +#define DEFAULT_VOLMIN 0x00 +#define DEFAULT_VOLMAX 0xFF +#define DEFAULT_VOLSTEP 0x04 + +#define AUDIO_PAUSE 0 +#define AUDIO_RESUME 1 + +/* Codec POWER DOWN modes */ +#define CODEC_PDWN_HW 1 +#define CODEC_PDWN_SW 2 + +/* MUTE commands */ +#define AUDIO_MUTE_ON 1 +#define AUDIO_MUTE_OFF 0 + +/* AUDIO FREQUENCY */ +#define AUDIO_FREQUENCY_192K ((uint32_t)192000) +#define AUDIO_FREQUENCY_96K ((uint32_t)96000) +#define AUDIO_FREQUENCY_48K ((uint32_t)48000) +#define AUDIO_FREQUENCY_44K ((uint32_t)44100) +#define AUDIO_FREQUENCY_32K ((uint32_t)32000) +#define AUDIO_FREQUENCY_22K ((uint32_t)22050) +#define AUDIO_FREQUENCY_16K ((uint32_t)16000) +#define AUDIO_FREQUENCY_11K ((uint32_t)11025) +#define AUDIO_FREQUENCY_8K ((uint32_t)8000) + +#define VOLUME_CONVERT(Volume) (((Volume) > 100)? 100:((uint8_t)(((Volume) * 63) / 100))) +#define VOLUME_IN_CONVERT(Volume) (((Volume) >= 100)? 239:((uint8_t)(((Volume) * 240) / 100))) + +#define WM8994_ID 0x8994 +#define WM8994_CHIPID_ADDR 0x00 + +#ifdef __cplusplus +} +#endif + +#endif