From 87e677e353995b833f86fb6ed5a4e4108ce0d9aa Mon Sep 17 00:00:00 2001 From: thread-liu Date: Tue, 24 Nov 2020 14:59:03 +0800 Subject: [PATCH] [add] audio driver for stm32mp1-dk1 --- bsp/stm32/libraries/STM32MPxx_HAL/SConscript | 2 - .../Src/stm32mp1xx_hal_sd.c | 4 +- .../CM4/Inc/stm32mp1xx_hal_conf.h | 2 +- .../stm32mp157a-st-discovery/board/Kconfig | 21 +- .../stm32mp157a-st-discovery/board/SConscript | 9 + .../stm32mp157a-st-discovery/board/board.h | 4 +- .../board/ports/audio/audio_play.c | 258 ++++++++ .../board/ports/audio/drv_cs42l51.c | 531 +++++++++++++++ .../board/ports/audio/drv_cs42l51.h | 198 ++++++ .../board/ports/audio/drv_mic.c | 390 +++++++++++ .../board/ports/audio/drv_sound.c | 603 ++++++++++++++++++ .../board/ports/drv_pmic.c | 71 +-- .../board/ports/drv_sdio.c | 5 +- 13 files changed, 2051 insertions(+), 47 deletions(-) create mode 100644 bsp/stm32/stm32mp157a-st-discovery/board/ports/audio/audio_play.c create mode 100644 bsp/stm32/stm32mp157a-st-discovery/board/ports/audio/drv_cs42l51.c create mode 100644 bsp/stm32/stm32mp157a-st-discovery/board/ports/audio/drv_cs42l51.h create mode 100644 bsp/stm32/stm32mp157a-st-discovery/board/ports/audio/drv_mic.c create mode 100644 bsp/stm32/stm32mp157a-st-discovery/board/ports/audio/drv_sound.c diff --git a/bsp/stm32/libraries/STM32MPxx_HAL/SConscript b/bsp/stm32/libraries/STM32MPxx_HAL/SConscript index 469909ae6e..dc63174324 100644 --- a/bsp/stm32/libraries/STM32MPxx_HAL/SConscript +++ b/bsp/stm32/libraries/STM32MPxx_HAL/SConscript @@ -75,8 +75,6 @@ if GetDepend(['RT_USING_SDIO']): src += ['STM32MP1xx_HAL_Driver/Src/stm32mp1xx_hal_sd.c'] if GetDepend(['RT_USING_AUDIO']): - src += ['STM32MP1xx_HAL_Driver/Src/stm32mp1xx_hal_i2s.c'] - src += ['STM32MP1xx_HAL_Driver/Src/stm32mp1xx_hal_i2s_ex.c'] src += ['STM32MP1xx_HAL_Driver/Src/stm32mp1xx_hal_sai.c'] src += ['STM32MP1xx_HAL_Driver/Src/stm32mp1xx_hal_sai_ex.c'] diff --git a/bsp/stm32/libraries/STM32MPxx_HAL/STM32MP1xx_HAL_Driver/Src/stm32mp1xx_hal_sd.c b/bsp/stm32/libraries/STM32MPxx_HAL/STM32MP1xx_HAL_Driver/Src/stm32mp1xx_hal_sd.c index 59044a2669..a7a01539d8 100644 --- a/bsp/stm32/libraries/STM32MPxx_HAL/STM32MP1xx_HAL_Driver/Src/stm32mp1xx_hal_sd.c +++ b/bsp/stm32/libraries/STM32MPxx_HAL/STM32MP1xx_HAL_Driver/Src/stm32mp1xx_hal_sd.c @@ -3236,7 +3236,7 @@ uint32_t SD_HighSpeed(SD_HandleTypeDef *hsd) { SD_hs[(8U*loop)+count] = SDMMC_ReadFIFO(hsd->Instance); } - loop += 8U; + loop ++; } if((HAL_GetTick()-Timeout) >= SDMMC_DATATIMEOUT) @@ -3351,7 +3351,7 @@ uint32_t SD_UltraHighSpeed(SD_HandleTypeDef *hsd) { SD_hs[(8U*loop)+count] = SDMMC_ReadFIFO(hsd->Instance); } - loop += 8U; + loop ++; } if((HAL_GetTick()-Timeout) >= SDMMC_DATATIMEOUT) diff --git a/bsp/stm32/stm32mp157a-st-discovery/board/CubeMX_Config/CM4/Inc/stm32mp1xx_hal_conf.h b/bsp/stm32/stm32mp157a-st-discovery/board/CubeMX_Config/CM4/Inc/stm32mp1xx_hal_conf.h index de873a6a1a..47a5e04456 100644 --- a/bsp/stm32/stm32mp157a-st-discovery/board/CubeMX_Config/CM4/Inc/stm32mp1xx_hal_conf.h +++ b/bsp/stm32/stm32mp157a-st-discovery/board/CubeMX_Config/CM4/Inc/stm32mp1xx_hal_conf.h @@ -57,7 +57,7 @@ /*#define HAL_PCD_MODULE_ENABLED */ /*#define HAL_QSPI_MODULE_ENABLED */ /*#define HAL_RNG_MODULE_ENABLED */ -/*#define HAL_SAI_MODULE_ENABLED */ +#define HAL_SAI_MODULE_ENABLED #define HAL_SD_MODULE_ENABLED /*#define HAL_MMC_MODULE_ENABLED */ /*#define HAL_RTC_MODULE_ENABLED */ diff --git a/bsp/stm32/stm32mp157a-st-discovery/board/Kconfig b/bsp/stm32/stm32mp157a-st-discovery/board/Kconfig index bc1fc2bb62..9191443847 100644 --- a/bsp/stm32/stm32mp157a-st-discovery/board/Kconfig +++ b/bsp/stm32/stm32mp157a-st-discovery/board/Kconfig @@ -64,6 +64,25 @@ menu "Onboard Peripheral Drivers" select RT_USING_DFS_ELMFAT default n + menuconfig BSP_USING_AUDIO + bool "Enable Audio Device" + select RT_USING_AUDIO + select BSP_USING_PMIC + select BSP_USING_SDMMC + select BSP_USING_I2C + select BSP_USING_I2C4 + default n + + if BSP_USING_AUDIO + config BSP_USING_AUDIO_PLAY + bool "Enable Audio Play" + default y + + config BSP_USING_AUDIO_RECORD + bool "Enable Audio Record" + default n + endif + endmenu menu "On-chip Peripheral Drivers" @@ -222,7 +241,7 @@ menu "On-chip Peripheral Drivers" default 181 endif menuconfig BSP_USING_I2C4 - bool "Enable I2C2 BUS (software simulation)" + bool "Enable I2C4 BUS (software simulation)" default n if BSP_USING_I2C4 comment "Notice: PD12 --> 60; PF15 --> 95" diff --git a/bsp/stm32/stm32mp157a-st-discovery/board/SConscript b/bsp/stm32/stm32mp157a-st-discovery/board/SConscript index 44a7c27173..781aae3189 100644 --- a/bsp/stm32/stm32mp157a-st-discovery/board/SConscript +++ b/bsp/stm32/stm32mp157a-st-discovery/board/SConscript @@ -46,6 +46,14 @@ if GetDepend(['BSP_USING_GBE']): if GetDepend(['BSP_USING_SDMMC']): src += Glob('ports/drv_sdio.c') +if GetDepend(['BSP_USING_AUDIO']): + src += Glob('ports/audio/drv_cs42l51.c') + src += Glob('ports/audio/drv_sound.c') + src += Glob('ports/audio/audio_play.c') + +if GetDepend(['BSP_USING_AUDIO_RECORD']): + src += Glob('ports/audio/drv_mic.c') + if GetDepend(['BSP_USING_OPENAMP']): src += Glob('CubeMX_Config/CM4/Src/ipcc.c') src += Glob('CubeMX_Config/CM4/Src/openamp.c') @@ -64,6 +72,7 @@ if GetDepend(['BSP_USING_OPENAMP']): path = [cwd] path += [cwd + '/CubeMX_Config/CM4/Inc'] path += [cwd + '/ports'] +path += [cwd + '/ports/audio'] if GetDepend(['BSP_USING_OPENAMP']): path += [cwd + '/ports/OpenAMP'] diff --git a/bsp/stm32/stm32mp157a-st-discovery/board/board.h b/bsp/stm32/stm32mp157a-st-discovery/board/board.h index 23fac89b39..18cab2141c 100644 --- a/bsp/stm32/stm32mp157a-st-discovery/board/board.h +++ b/bsp/stm32/stm32mp157a-st-discovery/board/board.h @@ -32,7 +32,7 @@ extern "C" { #if defined(BSP_USING_OPENAMP) -#define STM32_SRAM_BEGIN (uint32_t)0x10020000 +#define STM32_SRAM_BEGIN (uint32_t)0x10030000 #else #define STM32_SRAM_BEGIN (uint32_t)0x2FFF0000 #endif @@ -42,8 +42,6 @@ extern "C" { #define HEAP_BEGIN STM32_SRAM_BEGIN #define HEAP_END STM32_SRAM_END -#define HEAP_END STM32_SRAM_END - void SystemClock_Config(void); extern void _Error_Handler(char *s, int num); diff --git a/bsp/stm32/stm32mp157a-st-discovery/board/ports/audio/audio_play.c b/bsp/stm32/stm32mp157a-st-discovery/board/ports/audio/audio_play.c new file mode 100644 index 0000000000..315f6320d5 --- /dev/null +++ b/bsp/stm32/stm32mp157a-st-discovery/board/ports/audio/audio_play.c @@ -0,0 +1,258 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2020-11-24 thread-liu first version + */ + +#include +#include +#include + +#if defined(BSP_USING_AUDIO) && defined(BSP_USING_SDMMC) +#define BUFSZ 1024 +#define SOUND_DEVICE_NAME "sound0" +static rt_device_t snd_dev; + +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) +{ + 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); + + snd_dev = rt_device_find(SOUND_DEVICE_NAME); + + rt_device_open(snd_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(snd_dev, AUDIO_CTL_CONFIGURE, &caps); + + while (1) + { + int length; + + length = read(fd, buffer, BUFSZ); + + if (length <= 0) + break; + + rt_device_write(snd_dev, 0, buffer, length); + } + + rt_device_close(snd_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 + +#if defined(BSP_USING_AUDIO) && defined(BSP_USING_SDMMC) && defined(BSP_USING_AUDIO_RECORD) + +#define RECORD_TIME_MS 5000 +#define RECORD_SAMPLERATE 16000 +#define RECORD_CHANNEL 2 +#define RECORD_CHUNK_SZ ((RECORD_SAMPLERATE * RECORD_CHANNEL * 2) * 20 / 1000) + +#define MIC_DEVICE_NAME "mic0" +static rt_device_t mic_dev; + +struct wav_header +{ + char riff_id[4]; /* "RIFF" */ + int riff_datasize; /* RIFF chunk data size,exclude riff_id[4] and riff_datasize,total - 8 */ + char riff_type[4]; /* "WAVE" */ + char fmt_id[4]; /* "fmt " */ + int fmt_datasize; /* fmt chunk data size,16 for pcm */ + short fmt_compression_code; /* 1 for PCM */ + short fmt_channels; /* 1(mono) or 2(stereo) */ + int fmt_sample_rate; /* samples per second */ + int fmt_avg_bytes_per_sec; /* sample_rate * channels * bit_per_sample / 8 */ + short fmt_block_align; /* number bytes per sample, bit_per_sample * channels / 8 */ + short fmt_bit_per_sample; /* bits of each sample(8,16,32). */ + char data_id[4]; /* "data" */ + int data_datasize; /* data chunk size,pcm_size - 44 */ +}; + +static void wavheader_init(struct wav_header *header, int sample_rate, int channels, int datasize) +{ + memcpy(header->riff_id, "RIFF", 4); + header->riff_datasize = datasize + 44 - 8; + memcpy(header->riff_type, "WAVE", 4); + memcpy(header->fmt_id, "fmt ", 4); + header->fmt_datasize = 16; + header->fmt_compression_code = 1; + header->fmt_channels = channels; + header->fmt_sample_rate = sample_rate; + header->fmt_bit_per_sample = 16; + header->fmt_avg_bytes_per_sec = header->fmt_sample_rate * header->fmt_channels * header->fmt_bit_per_sample / 8; + header->fmt_block_align = header->fmt_bit_per_sample * header->fmt_channels / 8; + memcpy(header->data_id, "data", 4); + header->data_datasize = datasize; +} + +int wavrecord_sample(int argc, char **argv) +{ + int fd = -1; + uint8_t *buffer = NULL; + struct wav_header header; + struct rt_audio_caps caps = {0}; + int length, total_length = 0; + + if (argc != 2) + { + rt_kprintf("Usage:\n"); + rt_kprintf("wavrecord_sample file.wav\n"); + return -1; + } + + fd = open(argv[1], O_WRONLY | O_CREAT); + if (fd < 0) + { + rt_kprintf("open file for recording failed!\n"); + return -1; + } + write(fd, &header, sizeof(struct wav_header)); + + buffer = rt_malloc(RECORD_CHUNK_SZ); + if (buffer == RT_NULL) + goto __exit; + + mic_dev = rt_device_find(MIC_DEVICE_NAME); + if (mic_dev == RT_NULL) + goto __exit; + + rt_device_open(mic_dev, RT_DEVICE_OFLAG_RDONLY); + + caps.main_type = AUDIO_TYPE_INPUT; + caps.sub_type = AUDIO_DSP_PARAM; + caps.udata.config.samplerate = RECORD_SAMPLERATE; + caps.udata.config.channels = RECORD_CHANNEL; + caps.udata.config.samplebits = 16; + rt_device_control(mic_dev, AUDIO_CTL_CONFIGURE, &caps); + + while (1) + { + length = rt_device_read(mic_dev, 0, buffer, RECORD_CHUNK_SZ); + + if (length) + { + write(fd, buffer, length); + total_length += length; + } + + if ((total_length / RECORD_CHUNK_SZ) > (RECORD_TIME_MS / 20)) + break; + } + + /* write wav file head */ + wavheader_init(&header, RECORD_SAMPLERATE, RECORD_CHANNEL, total_length); + lseek(fd, 0, SEEK_SET); + write(fd, &header, sizeof(struct wav_header)); + close(fd); + + /* close audio mic device */ + rt_device_close(mic_dev); + +__exit: + if (fd >= 0) + close(fd); + + if (buffer) + rt_free(buffer); + + return 0; +} +MSH_CMD_EXPORT(wavrecord_sample, record voice to a wav file); + +#endif diff --git a/bsp/stm32/stm32mp157a-st-discovery/board/ports/audio/drv_cs42l51.c b/bsp/stm32/stm32mp157a-st-discovery/board/ports/audio/drv_cs42l51.c new file mode 100644 index 0000000000..e0ae5f1b5e --- /dev/null +++ b/bsp/stm32/stm32mp157a-st-discovery/board/ports/audio/drv_cs42l51.c @@ -0,0 +1,531 @@ +/* + * Copyright (c) 2006-2020, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2020-07-02 thread-liu first version + */ + +#include "board.h" + +#if defined(BSP_USING_AUDIO) +#include + +#define DRV_DEBUG +#define LOG_TAG "drv.audio" +#include + +/* CS42L51 address */ +#define CHIP_ADDRESS 0x4A +/* reset pin, active low */ +#define CS42L51_RESET_PIN GET_PIN(G, 9) + +static uint16_t CS42L51_Device = OUT_HEADPHONE; +static struct rt_i2c_bus_device *audio_dev = RT_NULL; + +/* i2c read reg */ +static rt_err_t read_reg(struct rt_i2c_bus_device *bus, rt_uint8_t reg, rt_uint8_t len, rt_uint8_t *buf) +{ + struct rt_i2c_msg msg[2] = {0, 0}; + + RT_ASSERT(bus != RT_NULL); + + msg[0].addr = CHIP_ADDRESS; /* Slave address */ + msg[0].flags = RT_I2C_WR; /* Write flag */ + msg[0].buf = ® /* Slave register address */ + msg[0].len = 1; /* Number of bytes sent */ + + 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_uint8_t reg, rt_uint8_t data) +{ + rt_uint8_t buf[2]; + struct rt_i2c_msg msgs; + + RT_ASSERT(bus != RT_NULL); + + buf[0] = reg; + buf[1] = data; + + msgs.addr = CHIP_ADDRESS; + msgs.flags = RT_I2C_WR; + msgs.buf = buf; + msgs.len = 2; + + if (rt_i2c_transfer(bus, &msgs, 1) == 1) + { + return RT_EOK; + } + + return RT_ERROR; +} + +/** + * @brief deinitializes cs42l51 low level. + * @retval none + */ +static void cs42l51_lowlevel_deinit(void) +{ + rt_uint8_t temp = 0; + + /* Mute DAC and ADC */ + read_reg(audio_dev, CS42L51_DAC_OUT_CTL, 1, &temp); + write_reg(audio_dev, CS42L51_DAC_OUT_CTL, (temp | 0x03)); + read_reg(audio_dev, CS42L51_ADC_INPUT, 1, &temp); + write_reg(audio_dev, CS42L51_ADC_INPUT, (temp | 0x03)); + + /* Disable soft ramp and zero cross */ + read_reg(audio_dev, CS42L51_ADC_CTL, 1, &temp); + write_reg(audio_dev, CS42L51_ADC_CTL, (temp & 0xF0)); + + /* Set PDN to 1 */ + read_reg(audio_dev, CS42L51_POWER_CTL1, 1, &temp); + write_reg(audio_dev, CS42L51_POWER_CTL1, (temp | 0x01)); + + /* Set all power down bits to 1 */ + write_reg(audio_dev, CS42L51_POWER_CTL1, 0x7F); + read_reg(audio_dev, CS42L51_MIC_POWER_CTL, 1, &temp); + write_reg(audio_dev, CS42L51_MIC_POWER_CTL, (temp | 0x0E)); + + /* Power off the codec */ + rt_pin_write(CS42L51_RESET_PIN, PIN_LOW); +} + +/** + * @brief initializes cs42l51 low level. + * @retval none + */ +static void cs42l51_lowlevel_init(void) +{ + rt_uint8_t temp = 0; + + /* Initialized RESET IO */ + rt_pin_mode(CS42L51_RESET_PIN, PIN_MODE_OUTPUT); + + /* Power off the cs42l51 */ + rt_pin_write(CS42L51_RESET_PIN, PIN_LOW); + + /* wait until power supplies are stable */ + rt_thread_mdelay(10); + + /* Power on the cs42l51 */ + rt_pin_write(CS42L51_RESET_PIN, PIN_HIGH); + + /* Wait at least 500ns after reset */ + rt_thread_mdelay(1); + + /* Set the device in standby mode */ + read_reg(audio_dev, CS42L51_POWER_CTL1, 1, &temp); + write_reg(audio_dev, CS42L51_POWER_CTL1, (temp | 0x01)); + + /* Set all power down bits to 1 */ + write_reg(audio_dev, CS42L51_POWER_CTL1, 0x7F); + read_reg(audio_dev, CS42L51_MIC_POWER_CTL, 1, &temp); + write_reg(audio_dev, CS42L51_MIC_POWER_CTL, (temp | 0x0E)); +} + +/** + * @brief Initializes CS42L51. + * @param Device: Audio type. + * @param bus_name I2C device name. + * @param volume: Initial output volume level (from 0 (-100dB) to 100 (0dB)). + * @retval 0 if correct communication, else wrong communication + */ +static rt_err_t cs42l51_init(uint16_t device, const char *bus_name, uint8_t volume) +{ + static uint8_t init_flag = 0; + rt_uint8_t temp = 0; + rt_uint8_t value = 0; + + /* check if codec is already initialized */ + if (init_flag == 0) + { + audio_dev = rt_i2c_bus_device_find(bus_name); + + if (audio_dev == RT_NULL) + { + LOG_E("%s bus not found\n", bus_name); + return -RT_ERROR; + } + /* hard reset cs42l51 */ + cs42l51_drv.reset(); + /* Wait at least 500ns after reset */ + rt_thread_mdelay(1); + /* Set the device in standby mode */ + read_reg(audio_dev, CS42L51_POWER_CTL1, 1, &temp); + write_reg(audio_dev, CS42L51_POWER_CTL1, (temp | 0x01)); + /* Set all power down bits to 1 */ + write_reg(audio_dev, CS42L51_POWER_CTL1, 0x7F); + read_reg(audio_dev, CS42L51_MIC_POWER_CTL, 1, &temp); + write_reg(audio_dev, CS42L51_MIC_POWER_CTL, (temp | 0x0E)); + + init_flag = 1; + } + else + { + /* Set all power down bits to 1 exept PDN to mute ADCs and DACs*/ + write_reg(audio_dev, CS42L51_POWER_CTL1, 0x7E); + read_reg(audio_dev, CS42L51_MIC_POWER_CTL, 1, &temp); + write_reg(audio_dev, CS42L51_MIC_POWER_CTL, (temp | 0x0E)); + /* Disable zero cross and soft ramp */ + read_reg(audio_dev, CS42L51_DAC_CTL, 1, &temp); + write_reg(audio_dev, CS42L51_DAC_CTL, (temp & 0xFC)); + + /* Power control : Enter standby (PDN = 1) */ + read_reg(audio_dev, CS42L51_POWER_CTL1, 1, &temp); + write_reg(audio_dev, CS42L51_POWER_CTL1, (temp | 0x01)); + } + /* Mic Power and Speed Control : Auto detect on, Speed mode SSM, tri state off, MCLK divide by 2 off */ + read_reg(audio_dev, CS42L51_MIC_POWER_CTL, 1, &temp); + write_reg(audio_dev, CS42L51_MIC_POWER_CTL, ((temp & 0x0E) | 0xA0)); + /* Interface control : Loopback off, Slave, I2S (SDIN and SOUT), Digital mix off, Mic mix off */ + write_reg(audio_dev, CS42L51_INTF_CTL, 0x0C); + /* Mic control : ADC single volume off, ADCB boost off, ADCA boost off, MicBias on AIN3B/MICIN2 pin, MicBias level 0.8xVA, MICB boost 16db, MICA boost 16dB */ + write_reg(audio_dev, CS42L51_MIC_CTL, 0x00); + /* ADC control : ADCB HPF off, ADCB HPF freeze off, ADCA HPF off, ADCA HPF freeze off, Soft ramp B off, Zero cross B off, Soft ramp A off, Zero cross A off */ + write_reg(audio_dev, CS42L51_ADC_CTL, 0x00); + /* ADC Input Select, Invert and Mute : AIN1B to PGAB, AIN3A to PreAmp to PGAA, ADCB invert off, ADCA invert off, ADCB mute on, ADCA mute off */ + write_reg(audio_dev, CS42L51_ADC_INPUT, 0x32); + /* DAC output control : HP Gain to 1, Single volume control off, PCM invert signals polarity off, DAC channels mute on */ + write_reg(audio_dev, CS42L51_DAC_OUT_CTL, 0xC3); + /* DAC control : Signal processing to DAC, Freeze off, De-emphasis off, Analog output auto mute off, DAC soft ramp */ + write_reg(audio_dev, CS42L51_DAC_CTL, 0x42); + /* ALCA and PGAA Control : ALCA soft ramp disable on, ALCA zero cross disable on, PGA A Gain 0dB */ + write_reg(audio_dev, CS42L51_ALC_PGA_CTL, 0xC0); + /* ALCB and PGAB Control : ALCB soft ramp disable on, ALCB zero cross disable on, PGA B Gain 0dB */ + write_reg(audio_dev, CS42L51_ALC_PGB_CTL, 0xC0); + /* ADCA Attenuator : 0dB */ + write_reg(audio_dev, CS42L51_ADCA_ATT, 0x00); + /* ADCB Attenuator : 0dB */ + write_reg(audio_dev, CS42L51_ADCB_ATT, 0x00); + /* ADCA mixer volume control : ADCA mixer channel mute on, ADCA mixer volume 0dB */ + write_reg(audio_dev, CS42L51_ADCA_VOL, 0x80); + /* ADCB mixer volume control : ADCB mixer channel mute on, ADCB mixer volume 0dB */ + write_reg(audio_dev, CS42L51_ADCB_VOL, 0x80); + /* PCMA mixer volume control : PCMA mixer channel mute off, PCMA mixer volume 0dB */ + write_reg(audio_dev, CS42L51_PCMA_VOL, 0x00); + /* PCMB mixer volume control : PCMB mixer channel mute off, PCMB mixer volume 0dB */ + write_reg(audio_dev, CS42L51_PCMB_VOL, 0x00); + /* PCM channel mixer : AOUTA Left, AOUTB Right */ + write_reg(audio_dev, CS42L51_PCM_MIXER, 0x00); + + if(device & OUT_HEADPHONE) + { + value = VOLUME_CONVERT(volume); + /* AOUTA volume control : AOUTA volume */ + write_reg(audio_dev, CS42L51_AOUTA_VOL, value); + /* AOUTB volume control : AOUTB volume */ + write_reg(audio_dev, CS42L51_AOUTB_VOL, value); + } + + CS42L51_Device = device; + + return RT_EOK; +} + +/** + * @brief Deinitialize the audio codec. + * @param None + * @retval None + */ +static void cs42l51_deinit(void) +{ + /* Deinitialize Audio Codec interface */ + rt_uint8_t temp = 0; + + /* Mute DAC and ADC */ + read_reg(audio_dev, CS42L51_DAC_OUT_CTL, 1, &temp); + write_reg(audio_dev, CS42L51_DAC_OUT_CTL, (temp | 0x03)); + read_reg(audio_dev, CS42L51_ADC_INPUT, 1, &temp); + write_reg(audio_dev, CS42L51_ADC_INPUT, (temp | 0x03)); + + /* Disable soft ramp and zero cross */ + read_reg(audio_dev, CS42L51_ADC_CTL, 1, &temp); + write_reg(audio_dev, CS42L51_ADC_CTL, (temp & 0xF0)); + + /* Set PDN to 1 */ + read_reg(audio_dev, CS42L51_POWER_CTL1, 1, &temp); + write_reg(audio_dev, CS42L51_POWER_CTL1, (temp | 0x01)); + + /* Set all power down bits to 1 */ + write_reg(audio_dev, CS42L51_POWER_CTL1, 0x7F); + read_reg(audio_dev, CS42L51_MIC_POWER_CTL, 1, &temp); + write_reg(audio_dev, CS42L51_MIC_POWER_CTL, (temp | 0x0E)); + + /* Power off CS42L51*/ + rt_pin_write(CS42L51_RESET_PIN, PIN_LOW); +} + +/** + * @brief Verify that we have a CS42L51. + * @retval 0 if correct communication, else wrong communication + */ + +static uint32_t cs42l51_read_id(void) +{ + uint8_t temp; + + /* read cs42l51 id */ + read_reg(audio_dev, CS42L51_CHIP_REV_ID, 1, &temp); + + if ((temp != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_A)) && + (temp != CS42L51_MK_CHIP_REV(CS42L51_CHIP_ID, CS42L51_CHIP_REV_B))) + { + LOG_E("device id : 0x%02x", temp); + return RT_ERROR; + } + + LOG_D("device id : 0x%02x", temp); + + return RT_EOK; +} + +/** + * @brief Start the audio Codec play feature. + * @note For this codec no Play options are required. + * @retval 0 if correct communication, else wrong communication + */ +static uint32_t cs42l51_play(void) +{ + rt_uint8_t temp = 0; + + switch (CS42L51_Device) + { + case OUT_HEADPHONE: + { + /* Unmute the output first */ + cs42l51_drv.set_mute(AUDIO_MUTE_OFF); + /* DAC control : Signal processing to DAC, Freeze off, De-emphasis off, Analog output auto mute off, DAC soft ramp */ + write_reg(audio_dev, CS42L51_DAC_CTL, 0x42); + /* Power control 1 : PDN_DACA, PDN_DACB disable. */ + read_reg(audio_dev, CS42L51_POWER_CTL1, 1, &temp); + write_reg(audio_dev, CS42L51_POWER_CTL1, (temp & 0x9F)); + break; + } + + case IN_LINE1: + { + /* ADC Input Select, Invert and Mute : AIN1B to PGAB, AIN1A to PGAA, ADCB invert off, ADCA invert off, ADCB mute off, ADCA mute off */ + write_reg(audio_dev, CS42L51_ADC_INPUT, 0x00); + /* Power control 1 : PDN_PGAA, PDN_PGAA, PDN_ADCB, PDN_ADCA disable. */ + read_reg(audio_dev, CS42L51_POWER_CTL1, 1, &temp); + write_reg(audio_dev, CS42L51_POWER_CTL1, (temp & 0x9F)); + break; + } + + case IN_MIC1: + { + /* ADC Input Select, Invert and Mute : AIN1B to PGAB, AIN3A to PreAmp to PGAA, ADCB invert off, ADCA invert off, ADCB mute on, ADCA mute off */ + write_reg(audio_dev, CS42L51_ADC_INPUT, 0x32); + /* Power control 1 : PDN_PGAA, PDN_ADCA disable. */ + read_reg(audio_dev, CS42L51_POWER_CTL1, 1, &temp); + write_reg(audio_dev, CS42L51_POWER_CTL1, (temp & 0xF5)); + /* Mic Power and Speed Control : PDN_MICA, PDN_MIC_BIAS disable. */ + read_reg(audio_dev, CS42L51_MIC_POWER_CTL, 1, &temp); + write_reg(audio_dev, CS42L51_MIC_POWER_CTL,(temp & 0xF9)); + break; + } + + case IN_MIC2: + { + /* Power control 1 : PDN_PGAB, PDN_ADCB disable. */ + read_reg(audio_dev, CS42L51_POWER_CTL1, 1, &temp); + write_reg(audio_dev, CS42L51_POWER_CTL1, (temp & 0xEB)); + /* Mic Power and Speed Control : PDN_MICB, PDN_MIC_BIAS disable. */ + read_reg(audio_dev, CS42L51_MIC_POWER_CTL, 1, &temp); + write_reg(audio_dev, CS42L51_MIC_POWER_CTL,(temp & 0xF5)); + break; + } + + default: + LOG_D("error audio play mode!"); + break; + } + + /* Power control : Exit standby (PDN = 0) */ + read_reg(audio_dev, CS42L51_POWER_CTL1, 1, &temp); + write_reg(audio_dev, CS42L51_POWER_CTL1, (temp & 0xFE)); + + return RT_EOK; +} + +/** + * @brief Pause playing on the audio codec. + * @param audio_dev: Device address on communication Bus. + * @retval 0 if correct communication, else wrong communication + */ +static uint32_t cs42l51_pause(void) +{ + + /* Pause the audio file playing */ + /* Mute the output first */ + return cs42l51_drv.set_mute(AUDIO_MUTE_ON); + +} + +/** + * @brief Resume playing on the audio codec. + * @param audio_dev: Device address on communication Bus. + * @retval 0 if correct communication, else wrong communication + */ +static uint32_t cs42l51_resume(void) +{ + /* Unmute the output */ + return cs42l51_drv.set_mute(AUDIO_MUTE_OFF); +} + +/** + * @brief Stop audio Codec playing. It powers down the codec. + * @retval 0 if correct communication, else wrong communication + */ +static uint32_t cs42l51_stop(void) +{ + rt_uint8_t temp = 0; + + /* Set all power down bits to 1 exept PDN to mute ADCs and DACs*/ + write_reg(audio_dev, 0x02, 0x7E); + read_reg(audio_dev, 0x03, 1, &temp); + write_reg(audio_dev, 0x03, (temp | 0x0E)); + + /* Disable zero cross and soft ramp */ + read_reg(audio_dev, 0x09, 1, &temp); + write_reg(audio_dev, 0x09, (temp & 0xFC)); + + /* Power control : Enter standby (PDN = 1) */ + read_reg(audio_dev, 0x02, 1, &temp); + write_reg(audio_dev, 0x02, (temp | 0x01)); + + return RT_EOK; +} + +/** + * @brief Set new frequency. + * @param AudioFreq: Audio frequency used to play the audio stream. + * @retval 0 if correct communication, else wrong communication + */ +static uint32_t cs42l51_set_frequency(uint32_t AudioFreq) +{ + return RT_EOK; +} + +/** + * @brief Set higher or lower the codec volume level. + * @param Volume: output volume level (from 0 (-100dB) to 100 (0dB)). + * @retval 0 if correct communication, else wrong communication + */ +static uint32_t cs42l51_set_volume(uint8_t Volume) +{ + uint8_t convertedvol = VOLUME_CONVERT(Volume); + + /* AOUTA volume control : AOUTA volume */ + write_reg(audio_dev, CS42L51_AOUTA_VOL, convertedvol); + /* AOUTB volume control : AOUTB volume */ + write_reg(audio_dev, CS42L51_AOUTB_VOL, convertedvol); + + return RT_EOK; +} + +/** + * @brief get higher or lower the codec volume level. + * @retval value if correct communication + */ +static uint32_t cs42l51_get_volume(void) +{ + rt_uint8_t temp = 0; + + /* AOUTA volume control : AOUTA volume */ + read_reg(audio_dev, CS42L51_AOUTA_VOL, 1, &temp); + + temp = VOLUME_INVERT(temp); + + return temp; +} + +/** +* @brief Enable or disable the mute feature on the audio codec. +* @param Cmd: AUDIO_MUTE_ON to enable the mute or AUDIO_MUTE_OFF to disable the +* mute mode. +* @retval 0 if correct communication, else wrong communication +*/ +static uint32_t cs42l51_set_mute(uint32_t cmd) +{ + rt_uint8_t temp = 0; + + /* Read DAC output control register */ + read_reg(audio_dev, 0x08, 1, &temp); + + /* Set the Mute mode */ + if(cmd == AUDIO_MUTE_ON) + { + /* Mute DAC channels */ + write_reg(audio_dev, CS42L51_DAC_OUT_CTL, (temp | 0x03)); + } + else /* AUDIO_MUTE_OFF Disable the Mute */ + { + /* Unmute DAC channels */ + write_reg(audio_dev, CS42L51_DAC_OUT_CTL, (temp & 0xFC)); + } + + return RT_EOK; +} + +/** + * @brief Switch dynamically (while audio file is played) the output target + * (speaker, headphone, etc). + * @note This function is currently not used (only headphone output device). + * @param Output: specifies the audio output device target. + * @retval 0 if correct communication, else wrong communication + */ +static uint32_t cs42l51_set_output_mode(uint8_t Output) +{ + return RT_EOK; +} + +/** + * @brief Reset CS42L51 registers. + * @retval 0 if correct communication, else wrong communication + */ +static uint32_t cs42l51_reset(void) +{ + cs42l51_lowlevel_deinit(); + + cs42l51_lowlevel_init(); + + return RT_EOK; +} + +/* Audio codec driver structure initialization */ +AUDIO_DrvTypeDef cs42l51_drv = +{ + cs42l51_init, + cs42l51_deinit, + cs42l51_read_id, + + cs42l51_play, + cs42l51_pause, + cs42l51_resume, + cs42l51_stop, + + cs42l51_set_frequency, + cs42l51_set_volume, + cs42l51_get_volume, + cs42l51_set_mute, + cs42l51_set_output_mode, + cs42l51_reset, +}; + +#endif diff --git a/bsp/stm32/stm32mp157a-st-discovery/board/ports/audio/drv_cs42l51.h b/bsp/stm32/stm32mp157a-st-discovery/board/ports/audio/drv_cs42l51.h new file mode 100644 index 0000000000..b3d7f78dca --- /dev/null +++ b/bsp/stm32/stm32mp157a-st-discovery/board/ports/audio/drv_cs42l51.h @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Date Author Notes + * 2020-07-02 thread-liu first version + */ + +#ifndef __DRV_CS42L51_H__ +#define __DRV_CS42L51_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct +{ + rt_err_t (*init)(uint16_t , const char *, uint8_t); + void (*deinit)(void); + uint32_t (*read_id)(void); + uint32_t (*play)(void); + uint32_t (*pause)(void); + uint32_t (*resume)(void); + uint32_t (*stop)(void); + uint32_t (*set_frequency)(uint32_t); + uint32_t (*set_volume)(uint8_t); + uint32_t (*get_volume)(void); + uint32_t (*set_mute)(uint32_t); + uint32_t (*set_output_mode)(uint8_t); + uint32_t (*reset)(void); +}AUDIO_DrvTypeDef; + +extern AUDIO_DrvTypeDef cs42l51_drv; + +/* CS42L51 register space */ +#define CS42L51_CHIP_ID 0x1B +#define CS42L51_CHIP_REV_A 0x00 +#define CS42L51_CHIP_REV_B 0x01 + +#define CS42L51_CHIP_REV_ID 0x01 +#define CS42L51_MK_CHIP_REV(a, b) ((a)<<3|(b)) + +#define CS42L51_POWER_CTL1 0x02 +#define CS42L51_POWER_CTL1_PDN_DACB (1<<6) +#define CS42L51_POWER_CTL1_PDN_DACA (1<<5) +#define CS42L51_POWER_CTL1_PDN_PGAB (1<<4) +#define CS42L51_POWER_CTL1_PDN_PGAA (1<<3) +#define CS42L51_POWER_CTL1_PDN_ADCB (1<<2) +#define CS42L51_POWER_CTL1_PDN_ADCA (1<<1) +#define CS42L51_POWER_CTL1_PDN (1<<0) + +#define CS42L51_MIC_POWER_CTL 0x03 +#define CS42L51_MIC_POWER_CTL_AUTO (1<<7) +#define CS42L51_MIC_POWER_CTL_SPEED(x) (((x)&3)<<5) +#define CS42L51_QSM_MODE 3 +#define CS42L51_HSM_MODE 2 +#define CS42L51_SSM_MODE 1 +#define CS42L51_DSM_MODE 0 +#define CS42L51_MIC_POWER_CTL_3ST_SP (1<<4) +#define CS42L51_MIC_POWER_CTL_PDN_MICB (1<<3) +#define CS42L51_MIC_POWER_CTL_PDN_MICA (1<<2) +#define CS42L51_MIC_POWER_CTL_PDN_BIAS (1<<1) +#define CS42L51_MIC_POWER_CTL_MCLK_DIV2 (1<<0) + +#define CS42L51_INTF_CTL 0x04 +#define CS42L51_INTF_CTL_LOOPBACK (1<<7) +#define CS42L51_INTF_CTL_MASTER (1<<6) +#define CS42L51_INTF_CTL_DAC_FORMAT(x) (((x)&7)<<3) +#define CS42L51_DAC_DIF_LJ24 0x00 +#define CS42L51_DAC_DIF_I2S 0x01 +#define CS42L51_DAC_DIF_RJ24 0x02 +#define CS42L51_DAC_DIF_RJ20 0x03 +#define CS42L51_DAC_DIF_RJ18 0x04 +#define CS42L51_DAC_DIF_RJ16 0x05 +#define CS42L51_INTF_CTL_ADC_I2S (1<<2) +#define CS42L51_INTF_CTL_DIGMIX (1<<1) +#define CS42L51_INTF_CTL_MICMIX (1<<0) + +#define CS42L51_MIC_CTL 0x05 +#define CS42L51_MIC_CTL_ADC_SNGVOL (1<<7) +#define CS42L51_MIC_CTL_ADCD_DBOOST (1<<6) +#define CS42L51_MIC_CTL_ADCA_DBOOST (1<<5) +#define CS42L51_MIC_CTL_MICBIAS_SEL (1<<4) +#define CS42L51_MIC_CTL_MICBIAS_LVL(x) (((x)&3)<<2) +#define CS42L51_MIC_CTL_MICB_BOOST (1<<1) +#define CS42L51_MIC_CTL_MICA_BOOST (1<<0) + +#define CS42L51_ADC_CTL 0x06 +#define CS42L51_ADC_CTL_ADCB_HPFEN (1<<7) +#define CS42L51_ADC_CTL_ADCB_HPFRZ (1<<6) +#define CS42L51_ADC_CTL_ADCA_HPFEN (1<<5) +#define CS42L51_ADC_CTL_ADCA_HPFRZ (1<<4) +#define CS42L51_ADC_CTL_SOFTB (1<<3) +#define CS42L51_ADC_CTL_ZCROSSB (1<<2) +#define CS42L51_ADC_CTL_SOFTA (1<<1) +#define CS42L51_ADC_CTL_ZCROSSA (1<<0) + +#define CS42L51_ADC_INPUT 0x07 +#define CS42L51_ADC_INPUT_AINB_MUX(x) (((x)&3)<<6) +#define CS42L51_ADC_INPUT_AINA_MUX(x) (((x)&3)<<4) +#define CS42L51_ADC_INPUT_INV_ADCB (1<<3) +#define CS42L51_ADC_INPUT_INV_ADCA (1<<2) +#define CS42L51_ADC_INPUT_ADCB_MUTE (1<<1) +#define CS42L51_ADC_INPUT_ADCA_MUTE (1<<0) + +#define CS42L51_DAC_OUT_CTL 0x08 +#define CS42L51_DAC_OUT_CTL_HP_GAIN(x) (((x)&7)<<5) +#define CS42L51_DAC_OUT_CTL_DAC_SNGVOL (1<<4) +#define CS42L51_DAC_OUT_CTL_INV_PCMB (1<<3) +#define CS42L51_DAC_OUT_CTL_INV_PCMA (1<<2) +#define CS42L51_DAC_OUT_CTL_DACB_MUTE (1<<1) +#define CS42L51_DAC_OUT_CTL_DACA_MUTE (1<<0) + +#define CS42L51_DAC_CTL 0x09 +#define CS42L51_DAC_CTL_DATA_SEL(x) (((x)&3)<<6) +#define CS42L51_DAC_CTL_FREEZE (1<<5) +#define CS42L51_DAC_CTL_DEEMPH (1<<3) +#define CS42L51_DAC_CTL_AMUTE (1<<2) +#define CS42L51_DAC_CTL_DACSZ(x) (((x)&3)<<0) + +#define CS42L51_ALC_PGA_CTL 0x0A +#define CS42L51_ALC_PGB_CTL 0x0B +#define CS42L51_ALC_PGX_ALCX_SRDIS (1<<7) +#define CS42L51_ALC_PGX_ALCX_ZCDIS (1<<6) +#define CS42L51_ALC_PGX_PGX_VOL(x) (((x)&0x1f)<<0) + +#define CS42L51_ADCA_ATT 0x0C +#define CS42L51_ADCB_ATT 0x0D + +#define CS42L51_ADCA_VOL 0x0E +#define CS42L51_ADCB_VOL 0x0F +#define CS42L51_PCMA_VOL 0x10 +#define CS42L51_PCMB_VOL 0x11 +#define CS42L51_MIX_MUTE_ADCMIX (1<<7) +#define CS42L51_MIX_VOLUME(x) (((x)&0x7f)<<0) + +#define CS42L51_BEEP_FREQ 0x12 +#define CS42L51_BEEP_VOL 0x13 +#define CS42L51_BEEP_CONF 0x14 + +#define CS42L51_TONE_CTL 0x15 +#define CS42L51_TONE_CTL_TREB(x) (((x)&0xf)<<4) +#define CS42L51_TONE_CTL_BASS(x) (((x)&0xf)<<0) + +#define CS42L51_AOUTA_VOL 0x16 +#define CS42L51_AOUTB_VOL 0x17 +#define CS42L51_PCM_MIXER 0x18 +#define CS42L51_LIMIT_THRES_DIS 0x19 +#define CS42L51_LIMIT_REL 0x1A +#define CS42L51_LIMIT_ATT 0x1B +#define CS42L51_ALC_EN 0x1C +#define CS42L51_ALC_REL 0x1D +#define CS42L51_ALC_THRES 0x1E +#define CS42L51_NOISE_CONF 0x1F + +#define CS42L51_STATUS 0x20 +#define CS42L51_STATUS_SP_CLKERR (1<<6) +#define CS42L51_STATUS_SPEA_OVFL (1<<5) +#define CS42L51_STATUS_SPEB_OVFL (1<<4) +#define CS42L51_STATUS_PCMA_OVFL (1<<3) +#define CS42L51_STATUS_PCMB_OVFL (1<<2) +#define CS42L51_STATUS_ADCA_OVFL (1<<1) +#define CS42L51_STATUS_ADCB_OVFL (1<<0) + +#define CS42L51_CHARGE_FREQ 0x21 +#define CS42L51_FIRSTREG 0x01 + +enum play_type { + NONE, + OUT_HEADPHONE, + IN_MIC1, + IN_MIC2, + IN_LINE1, + IN_LINE2, + IN_LINE3, +}; + +/* + * Hack: with register 0x21, it makes 33 registers. Looks like someone in the + * i2c layer doesn't like i2c smbus block read of 33 regs. Workaround by using + * 32 regs + */ +#define CS42L51_LASTREG 0x20 +#define CS42L51_NUMREGS (CS42L51_LASTREG - CS42L51_FIRSTREG + 1) + +#define VOLUME_CONVERT(Volume) ((Volume >= 100) ? 0 : ((uint8_t)(((Volume * 2) + 56)))) +#define VOLUME_INVERT(Volume) (((Volume) == 0U) ? 100U : ((uint8_t)(((Volume) - 56U) / 2U))) + +/* MUTE commands */ +#define AUDIO_MUTE_ON 1 +#define AUDIO_MUTE_OFF 0 + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bsp/stm32/stm32mp157a-st-discovery/board/ports/audio/drv_mic.c b/bsp/stm32/stm32mp157a-st-discovery/board/ports/audio/drv_mic.c new file mode 100644 index 0000000000..20e98d4826 --- /dev/null +++ b/bsp/stm32/stm32mp157a-st-discovery/board/ports/audio/drv_mic.c @@ -0,0 +1,390 @@ +/* + * Copyright (c) 2006-2020, 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 + +#if defined(BSP_USING_AUDIO_RECORD) + +#include "drv_cs42l51.h" + +//#define DRV_DEBUG +#define DBG_TAG "drv.audio" +#define DBG_LVL DBG_INFO +#include + +#define MIC_BUS_NAME "i2c4" + +/* SYSRAM */ +#define RX_FIFO_SIZE (4096) +#if defined(__CC_ARM) || defined(__CLANG_ARM) +rt_uint8_t MIC_RX_FIFO[RX_FIFO_SIZE] __attribute__((at(0x2FFC2000))); +#elif defined(__ICCARM__) +#pragma location = 0x2FFC2000 +rt_uint8_t MIC_RX_FIFO[RX_FIFO_SIZE]; +#elif defined ( __GNUC__ ) +rt_uint8_t MIC_RX_FIFO[RX_FIFO_SIZE] __attribute__((at(0x2FFC2000))); +#endif + +struct mic_device +{ + struct rt_audio_device audio; + struct rt_audio_configure record_config; + rt_uint8_t *rx_fifo; + rt_uint8_t volume; +}; + +static struct mic_device mic_dev = {0}; +static rt_uint16_t zero_frame[2] = {0}; + +extern SAI_HandleTypeDef hsai_BlockA2; +extern DMA_HandleTypeDef hdma_sai2_a; +extern SAI_HandleTypeDef hsai_BlockB2; +extern DMA_HandleTypeDef hdma_sai2_b; +extern void SAIA_Frequency_Set(uint32_t frequency); + +void SAIB_Init(void) +{ + HAL_SAI_DeInit(&hsai_BlockB2); + + hsai_BlockB2.Instance = SAI2_Block_B; + hsai_BlockB2.Init.AudioFrequency = SAI_AUDIO_FREQUENCY_44K; + hsai_BlockB2.Init.AudioMode = SAI_MODESLAVE_RX; + hsai_BlockB2.Init.Synchro = SAI_SYNCHRONOUS; + hsai_BlockB2.Init.OutputDrive = SAI_OUTPUTDRIVE_ENABLE; + hsai_BlockB2.Init.NoDivider = SAI_MASTERDIVIDER_ENABLE; + hsai_BlockB2.Init.FIFOThreshold = SAI_FIFOTHRESHOLD_1QF; + hsai_BlockB2.Init.Mckdiv = 0; + hsai_BlockB2.Init.MckOverSampling = SAI_MCK_OVERSAMPLING_DISABLE; + hsai_BlockB2.Init.MonoStereoMode = SAI_STEREOMODE; + hsai_BlockB2.Init.CompandingMode = SAI_NOCOMPANDING; + hsai_BlockB2.Init.TriState = SAI_OUTPUT_NOTRELEASED; + hsai_BlockB2.Init.PdmInit.Activation = DISABLE; + hsai_BlockB2.Init.PdmInit.MicPairsNbr = 1; + hsai_BlockB2.Init.PdmInit.ClockEnable = SAI_PDM_CLOCK1_ENABLE; + hsai_BlockB2.Init.Protocol = SAI_FREE_PROTOCOL; + hsai_BlockB2.Init.DataSize = SAI_DATASIZE_16; + hsai_BlockB2.Init.FirstBit = SAI_FIRSTBIT_MSB; + hsai_BlockB2.Init.ClockStrobing = SAI_CLOCKSTROBING_RISINGEDGE; + + hsai_BlockB2.FrameInit.FrameLength = 64; + hsai_BlockB2.FrameInit.ActiveFrameLength = 32; + hsai_BlockB2.FrameInit.FSDefinition = SAI_FS_CHANNEL_IDENTIFICATION; + hsai_BlockB2.FrameInit.FSPolarity = SAI_FS_ACTIVE_LOW; + hsai_BlockB2.FrameInit.FSOffset = SAI_FS_BEFOREFIRSTBIT; + + hsai_BlockB2.SlotInit.FirstBitOffset = 0; + hsai_BlockB2.SlotInit.SlotSize = SAI_SLOTSIZE_32B; + hsai_BlockB2.SlotInit.SlotNumber = 2; + hsai_BlockB2.SlotInit.SlotActive = SAI_SLOTACTIVE_0|SAI_SLOTACTIVE_1; + + /* DeInit SAI PDM input */ + HAL_SAI_DeInit(&hsai_BlockB2); + + /* Init SAI PDM input */ + if(HAL_OK != HAL_SAI_Init(&hsai_BlockB2)) + { + Error_Handler(); + } + + /* Enable SAI to generate clock used by audio driver */ + __HAL_SAI_ENABLE(&hsai_BlockB2); +} + +void SAIB_Channels_Set(uint8_t channels) +{ + if (channels == 1) + { + hsai_BlockB2.Init.MonoStereoMode = SAI_MONOMODE; + } + else + { + hsai_BlockB2.Init.MonoStereoMode = SAI_STEREOMODE; + } + + __HAL_SAI_DISABLE(&hsai_BlockB2); + HAL_SAI_Init(&hsai_BlockB2); + __HAL_SAI_ENABLE(&hsai_BlockB2); +} + +void DMA2_Stream4_IRQHandler(void) +{ + HAL_DMA_IRQHandler(&hdma_sai2_b); +} + +void HAL_SAI_RxHalfCpltCallback(SAI_HandleTypeDef *hsai) +{ + rt_audio_rx_done(&mic_dev.audio, &mic_dev.rx_fifo[0], RX_FIFO_SIZE / 2); +} + +void HAL_SAI_RxCpltCallback(SAI_HandleTypeDef *hsai) +{ + rt_audio_rx_done(&mic_dev.audio, &mic_dev.rx_fifo[RX_FIFO_SIZE / 2], RX_FIFO_SIZE / 2); +} + +static rt_err_t mic_getcaps(struct rt_audio_device *audio, struct rt_audio_caps *caps) +{ + rt_err_t result = RT_EOK; + struct mic_device *mic_dev; + + RT_ASSERT(audio != RT_NULL); + mic_dev = (struct mic_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_INPUT | AUDIO_TYPE_MIXER; + break; + + default: + result = -RT_ERROR; + break; + } + + break; + } + + case AUDIO_TYPE_INPUT: /* Provide capabilities of INPUT unit */ + { + switch (caps->sub_type) + { + case AUDIO_DSP_PARAM: + caps->udata.config.samplerate = mic_dev->record_config.samplerate; + caps->udata.config.channels = mic_dev->record_config.channels; + caps->udata.config.samplebits = mic_dev->record_config.samplebits; + break; + + case AUDIO_DSP_SAMPLERATE: + caps->udata.config.samplerate = mic_dev->record_config.samplerate; + break; + + case AUDIO_DSP_CHANNELS: + caps->udata.config.channels = mic_dev->record_config.channels; + break; + + case AUDIO_DSP_SAMPLEBITS: + caps->udata.config.samplebits = mic_dev->record_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 | AUDIO_MIXER_LINE; + break; + + case AUDIO_MIXER_VOLUME: + caps->udata.value = mic_dev->volume; + break; + + case AUDIO_MIXER_LINE: + break; + + default: + result = -RT_ERROR; + break; + } + + break; + } + + default: + result = -RT_ERROR; + break; + } + + return result; +} + +static rt_err_t mic_configure(struct rt_audio_device *audio, struct rt_audio_caps *caps) +{ + rt_err_t result = RT_EOK; + struct mic_device *mic_dev; + + RT_ASSERT(audio != RT_NULL); + mic_dev = (struct mic_device *)audio->parent.user_data; + + switch (caps->main_type) + { + case AUDIO_TYPE_MIXER: + { + switch (caps->sub_type) + { + case AUDIO_MIXER_VOLUME: + { + rt_uint32_t volume = caps->udata.value; + mic_dev->volume = volume; + LOG_D("set volume %d", volume); + break; + } + + default: + result = -RT_ERROR; + break; + } + + break; + } + + case AUDIO_TYPE_INPUT: + { + switch (caps->sub_type) + { + case AUDIO_DSP_PARAM: + { + SAIA_Frequency_Set(caps->udata.config.samplerate); + HAL_SAI_DMAStop(&hsai_BlockB2); + SAIB_Channels_Set(caps->udata.config.channels); + HAL_SAI_Transmit(&hsai_BlockA2, (uint8_t *)&zero_frame[0], 2, 0); + HAL_SAI_Receive_DMA(&hsai_BlockB2, mic_dev->rx_fifo, RX_FIFO_SIZE / 2); + + /* save configs */ + mic_dev->record_config.samplerate = caps->udata.config.samplerate; + mic_dev->record_config.channels = caps->udata.config.channels; + mic_dev->record_config.samplebits = caps->udata.config.samplebits; + LOG_D("set samplerate %d", mic_dev->record_config.samplerate); + LOG_D("set channels %d", mic_dev->record_config.channels); + break; + } + + case AUDIO_DSP_SAMPLERATE: + { + mic_dev->record_config.samplerate = caps->udata.config.samplerate; + LOG_D("set channels %d", mic_dev->record_config.channels); + break; + } + + case AUDIO_DSP_CHANNELS: + { + mic_dev->record_config.channels = caps->udata.config.channels; + LOG_D("set channels %d", mic_dev->record_config.channels); + break; + } + + default: + break; + } + + break; + } + + default: + break; + } + + return result; +} + +static rt_err_t mic_init(struct rt_audio_device *audio) +{ + struct mic_device *mic_dev; + RT_ASSERT(audio != RT_NULL); + + mic_dev = (struct mic_device *)audio->parent.user_data; + SAIB_Init(); + /* set default params */ + SAIB_Channels_Set(mic_dev->record_config.channels); + + return RT_EOK; +} + +static rt_err_t mic_start(struct rt_audio_device *audio, int stream) +{ + struct mic_device *mic_dev; + RT_ASSERT(audio != RT_NULL); + + mic_dev = (struct mic_device *)audio->parent.user_data; + if (stream == AUDIO_STREAM_RECORD) + { + cs42l51_drv.init(IN_MIC1, MIC_BUS_NAME, 40); + /* open receive */ + if (HAL_SAI_Receive_DMA(&hsai_BlockB2, mic_dev->rx_fifo, RX_FIFO_SIZE / 2) != HAL_OK) + { + return RT_ERROR; + } + /* supply clk */ + HAL_SAI_Transmit(&hsai_BlockA2, (uint8_t *)&zero_frame[0], 2, 0); + + cs42l51_drv.play(); + } + + return RT_EOK; +} + +static rt_err_t mic_stop(struct rt_audio_device *audio, int stream) +{ + if (stream == AUDIO_STREAM_RECORD) + { + HAL_SAI_DMAStop(&hsai_BlockB2); + HAL_SAI_Abort(&hsai_BlockB2); + cs42l51_drv.stop(); + } + + return RT_EOK; +} + +static struct rt_audio_ops mic_ops = +{ + .getcaps = mic_getcaps, + .configure = mic_configure, + .init = mic_init, + .start = mic_start, + .stop = mic_stop, + .transmit = RT_NULL, + .buffer_info = RT_NULL, +}; + +int rt_hw_mic_init(void) +{ + rt_err_t result = RT_EOK; + struct rt_device *device; + + rt_memset(MIC_RX_FIFO, 0, RX_FIFO_SIZE); + mic_dev.rx_fifo = MIC_RX_FIFO; + + /* init default configuration */ + { + mic_dev.record_config.samplerate = 44100; + mic_dev.record_config.channels = 2; + mic_dev.record_config.samplebits = 16; + mic_dev.volume = 55; + } + + /* register sound device */ + mic_dev.audio.ops = &mic_ops; + result = rt_audio_register(&mic_dev.audio, "mic0", RT_DEVICE_FLAG_RDONLY, &mic_dev); + + if (result != RT_EOK) + { + device = &(mic_dev.audio.parent); + rt_device_unregister(device); + LOG_E("mic device init error!"); + return RT_ERROR; + } + + return RT_EOK; +} + +INIT_DEVICE_EXPORT(rt_hw_mic_init); + +#endif diff --git a/bsp/stm32/stm32mp157a-st-discovery/board/ports/audio/drv_sound.c b/bsp/stm32/stm32mp157a-st-discovery/board/ports/audio/drv_sound.c new file mode 100644 index 0000000000..5d94bfc399 --- /dev/null +++ b/bsp/stm32/stm32mp157a-st-discovery/board/ports/audio/drv_sound.c @@ -0,0 +1,603 @@ +/* + * Copyright (c) 2006-2020, 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" +#include "drv_cs42l51.h" + +#ifdef BSP_USING_AUDIO + +//#define DRV_DEBUG +#define LOG_TAG "drv.audio" +#include + +#define SOUND_BUS_NAME "i2c4" + +/* SYSRAM */ +#define TX_FIFO_SIZE (4096) +#if defined(__CC_ARM) || defined(__CLANG_ARM) +rt_uint8_t AUDIO_TX_FIFO[TX_FIFO_SIZE] __attribute__((at(0x2FFC3000))); +#elif defined(__ICCARM__) +#pragma location = 0x2FFC3000 +rt_uint8_t AUDIO_TX_FIFO[TX_FIFO_SIZE]; +#elif defined ( __GNUC__ ) +rt_uint8_t AUDIO_TX_FIFO[TX_FIFO_SIZE] __attribute__((at(0x2FFC3000))); +#endif + +struct sound_device +{ + struct rt_audio_device audio; + struct rt_audio_configure replay_config; + rt_uint8_t *tx_fifo; + rt_uint8_t volume; +}; + +static struct sound_device snd_dev = {0}; + +SAI_HandleTypeDef hsai_BlockA2 = {0}; +DMA_HandleTypeDef hdma_sai2_a = {0}; +SAI_HandleTypeDef hsai_BlockB2 = {0}; +DMA_HandleTypeDef hdma_sai2_b = {0}; + +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_VERY_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; + 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(GPIOI, &GPIO_InitStruct); + + /* Configure DMA used for SAI2 */ + __HAL_RCC_DMAMUX_CLK_ENABLE(); + __HAL_RCC_DMA2_CLK_ENABLE(); + + hdma_sai2_a.Instance = DMA2_Stream5; + 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_DISABLE; + + 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(DMA2_Stream5_IRQn, 2, 0); + HAL_NVIC_EnableIRQ(DMA2_Stream5_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 + 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); + + __HAL_RCC_DMAMUX_CLK_ENABLE(); + __HAL_RCC_DMA2_CLK_ENABLE(); + + /* Peripheral DMA init*/ + hdma_sai2_b.Instance = DMA2_Stream4; + 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_DISABLE; + 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_LINKDMA(hsai,hdmarx,hdma_sai2_b); + 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(DMA2_Stream4_IRQn, 2, 0); + HAL_NVIC_EnableIRQ(DMA2_Stream4_IRQn); + } +} + +void HAL_SAI_MspDeInit(SAI_HandleTypeDef* hsai) +{ + /* SAI2 */ + if(hsai->Instance==SAI2_Block_A) + { + + /* Peripheral clock disable */ + __HAL_RCC_SAI2_CLK_DISABLE(); + + /**SAI2_A_Block_A GPIO Configuration + PE0 ------> SAI2_MCLK_A + PI7 ------> SAI2_FS_A + PI5 ------> SAI2_SCK_A + PI6 ------> SAI2_SD_A + */ + HAL_GPIO_DeInit(GPIOE, GPIO_PIN_0); + + HAL_GPIO_DeInit(GPIOI, GPIO_PIN_7|GPIO_PIN_5|GPIO_PIN_6); + + HAL_DMA_DeInit(hsai->hdmarx); + HAL_DMA_DeInit(hsai->hdmatx); + } + + if(hsai->Instance==SAI2_Block_B) + { + /* Peripheral clock disable */ + __HAL_RCC_SAI2_CLK_DISABLE(); + + /**SAI2_B_Block_B GPIO Configuration + PF11 ------> SAI2_SD_B + */ + HAL_GPIO_DeInit(GPIOF, GPIO_PIN_11); + + HAL_DMA_DeInit(hsai->hdmarx); + HAL_DMA_DeInit(hsai->hdmatx); + } +} + +static void rt_hw_sai2a_init(void) +{ + HAL_SAI_DeInit(&hsai_BlockA2); + 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_RISINGEDGE; + 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_EMPTY; + 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_32B; + hsai_BlockA2.SlotInit.SlotNumber = 2; + hsai_BlockA2.SlotInit.SlotActive = SAI_SLOTACTIVE_0 | SAI_SLOTACTIVE_1; + + 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 DMA2_Stream5_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) +{ + return; +} + +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: + caps->udata.value = cs42l51_drv.get_volume(); + 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; + + cs42l51_drv.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_ASSERT(audio != RT_NULL); + snd_dev = (struct sound_device *)audio->parent.user_data; + + cs42l51_drv.init(OUT_HEADPHONE, SOUND_BUS_NAME, 40); + + if (cs42l51_drv.read_id() != RT_EOK) + { + LOG_E("can't find low level audio device!"); + return RT_ERROR; + } + + rt_hw_sai2a_init(); + + /* set default params */ + SAIA_Frequency_Set(snd_dev->replay_config.samplerate); + SAIA_Channels_Set(snd_dev->replay_config.channels); + + return result; +} + +static rt_err_t sound_start(struct rt_audio_device *audio, int stream) +{ + struct sound_device *snd_dev; + + RT_ASSERT(audio != RT_NULL); + snd_dev = (struct sound_device *)audio->parent.user_data; + + if (stream == AUDIO_STREAM_REPLAY) + { + LOG_D("open sound device"); + + cs42l51_drv.init(OUT_HEADPHONE, SOUND_BUS_NAME, 60); /* set work mode */ + cs42l51_drv.play(); + + 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) +{ + RT_ASSERT(audio != RT_NULL); + + if (stream == AUDIO_STREAM_REPLAY) + { + HAL_SAI_DMAStop(&hsai_BlockA2); + HAL_SAI_Abort(&hsai_BlockA2); + cs42l51_drv.stop(); + + 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 *snd_dev; + + RT_ASSERT(audio != RT_NULL); + snd_dev = (struct sound_device *)audio->parent.user_data; + + /** + * TX_FIFO + * +----------------+----------------+ + * | block1 | block2 | + * +----------------+----------------+ + * \ block_size / + */ + info->buffer = snd_dev->tx_fifo; + info->total_size = TX_FIFO_SIZE; + info->block_size = TX_FIFO_SIZE / 2; + info->block_count = 2; +} + +static struct rt_audio_ops 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; + + /* register sound device */ + snd_dev.audio.ops = &snd_ops; + result = rt_audio_register(&snd_dev.audio, "sound0", RT_DEVICE_FLAG_WRONLY, &snd_dev); + 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); + +#endif diff --git a/bsp/stm32/stm32mp157a-st-discovery/board/ports/drv_pmic.c b/bsp/stm32/stm32mp157a-st-discovery/board/ports/drv_pmic.c index 95e385f1b8..c5f26596cc 100644 --- a/bsp/stm32/stm32mp157a-st-discovery/board/ports/drv_pmic.c +++ b/bsp/stm32/stm32mp157a-st-discovery/board/ports/drv_pmic.c @@ -19,6 +19,8 @@ #define LOG_TAG "drv.pmic" #include +#define I2C_NAME "i2c3" + static struct rt_i2c_bus_device *pmic_dev = RT_NULL; /* i2c read reg */ @@ -796,47 +798,47 @@ static rt_err_t rt_hw_pmic_init_register(void) stpmu1_write_reg(BUCK_ICC_TURNOFF_REG, 0x30); stpmu1_write_reg(LDO_ICC_TURNOFF_REG, 0x3b); - /* vddcore */ - STPMU1_Regulator_Voltage_Set(STPMU1_BUCK1, 1200); - STPMU1_Regulator_Enable(STPMU1_BUCK1); + /* vddcore */ + STPMU1_Regulator_Voltage_Set(STPMU1_BUCK1, 1200); + STPMU1_Regulator_Enable(STPMU1_BUCK1); - /* vddddr */ - STPMU1_Regulator_Voltage_Set(STPMU1_BUCK2, 1350); - STPMU1_Regulator_Enable(STPMU1_BUCK2); + /* vddddr */ + STPMU1_Regulator_Voltage_Set(STPMU1_BUCK2, 1350); + STPMU1_Regulator_Enable(STPMU1_BUCK2); - /* vdd */ - STPMU1_Regulator_Voltage_Set(STPMU1_BUCK3, 3300); - STPMU1_Regulator_Enable(STPMU1_BUCK3); + /* vdd */ + STPMU1_Regulator_Voltage_Set(STPMU1_BUCK3, 3300); + STPMU1_Regulator_Enable(STPMU1_BUCK3); - /* 3v3 */ - STPMU1_Regulator_Voltage_Set(STPMU1_BUCK4, 3300); - STPMU1_Regulator_Enable(STPMU1_BUCK4); +// /* 3v3 */ +// STPMU1_Regulator_Voltage_Set(STPMU1_BUCK4, 3300); +// STPMU1_Regulator_Enable(STPMU1_BUCK4); - /* vdda */ - STPMU1_Regulator_Voltage_Set(STPMU1_LDO1, 2900); - STPMU1_Regulator_Enable(STPMU1_LDO1); + /* 1v8_audio */ + STPMU1_Regulator_Voltage_Set(STPMU1_LDO1, 1800); + STPMU1_Regulator_Enable(STPMU1_LDO1); - /* 2v8 */ - STPMU1_Regulator_Voltage_Set(STPMU1_LDO2, 2800); - STPMU1_Regulator_Enable(STPMU1_LDO2); + /* vdd_emmc */ + STPMU1_Regulator_Voltage_Set(STPMU1_LDO2, 2900); + STPMU1_Regulator_Enable(STPMU1_LDO2); - /* vtt_ddr lod3 mode buck2/2 */ - STPMU1_Regulator_Voltage_Set(STPMU1_LDO3, 0xFFFF); - STPMU1_Regulator_Enable(STPMU1_LDO3); + /* vdd1_ddr */ + STPMU1_Regulator_Voltage_Set(STPMU1_LDO3, 0xFFFF); + STPMU1_Regulator_Enable(STPMU1_LDO3); - /* vdd_usb */ - STPMU1_Regulator_Voltage_Set(STPMU1_LDO4, 3300); - STPMU1_Regulator_Enable(STPMU1_LDO4); + /* vdd_usb */ + STPMU1_Regulator_Voltage_Set(STPMU1_LDO4, 3300); + STPMU1_Regulator_Enable(STPMU1_LDO4); - /* vdd_sd */ - STPMU1_Regulator_Voltage_Set(STPMU1_LDO5, 2900); - STPMU1_Regulator_Enable(STPMU1_LDO5); + /* vdda */ + STPMU1_Regulator_Voltage_Set(STPMU1_LDO5, 2900); + STPMU1_Regulator_Enable(STPMU1_LDO5); - /* 1v8 */ - STPMU1_Regulator_Voltage_Set(STPMU1_LDO6, 1800); - STPMU1_Regulator_Enable(STPMU1_LDO6); + /* 2v8 */ + STPMU1_Regulator_Voltage_Set(STPMU1_LDO6, 2800); + STPMU1_Regulator_Enable(STPMU1_LDO6); - STPMU1_Regulator_Enable(STPMU1_VREFDDR); + STPMU1_Regulator_Enable(STPMU1_VREFDDR); return RT_EOK; } @@ -884,7 +886,7 @@ static int pmic_init(void) { BSP_PMIC_MspInit(); - result = rt_hw_pmic_init("i2c3"); + result = rt_hw_pmic_init(I2C_NAME); if(result != RT_EOK) { LOG_D("stpmic init failed: %02x", result); @@ -893,10 +895,7 @@ static int pmic_init(void) } rt_hw_pmic_init_register(); - } - - if(IS_ENGINEERING_BOOT_MODE()) - { + __HAL_RCC_VREF_CLK_ENABLE(); HAL_SYSCFG_VREFBUF_HighImpedanceConfig(SYSCFG_VREFBUF_HIGH_IMPEDANCE_DISABLE); HAL_SYSCFG_EnableVREFBUF(); diff --git a/bsp/stm32/stm32mp157a-st-discovery/board/ports/drv_sdio.c b/bsp/stm32/stm32mp157a-st-discovery/board/ports/drv_sdio.c index 40f613b277..3e5085c3cd 100644 --- a/bsp/stm32/stm32mp157a-st-discovery/board/ports/drv_sdio.c +++ b/bsp/stm32/stm32mp157a-st-discovery/board/ports/drv_sdio.c @@ -521,7 +521,8 @@ int rt_hw_sdio_init(void) LOG_E("host create fail"); return RT_NULL; } - return 0; + + return RT_EOK; } INIT_DEVICE_EXPORT(rt_hw_sdio_init); @@ -547,7 +548,7 @@ int mnt_init(void) rt_kprintf("file system mount success!\n"); } - return 0; + return RT_EOK; } INIT_ENV_EXPORT(mnt_init);