[add] sai and wm8994 codec driver.
This commit is contained in:
parent
840af38dac
commit
c5612fd2e1
|
@ -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']
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue