浏览代码

[add] sai and wm8994 codec driver.

thread-liu 4 年之前
父节点
当前提交
c5612fd2e1

+ 0 - 2
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']
 

+ 1 - 1
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   */

+ 197 - 1
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

+ 9 - 0
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"

+ 4 - 0
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')

+ 572 - 0
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 <dfs_posix.h>
+
+#define DRV_DEBUG
+#define LOG_TAG             "drv.sound"
+#include <drv_log.h>
+
+#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

+ 782 - 0
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 <drv_wm8994.h>
+
+#define DRV_DEBUG
+#define LOG_TAG     "drv.wm8994"
+#include <drv_log.h>
+
+#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

+ 80 - 0
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