diff --git a/bsp/lpc55sxx/Libraries/drivers/SConscript b/bsp/lpc55sxx/Libraries/drivers/SConscript index 2d38a9a0f3..ab3ab0540e 100644 --- a/bsp/lpc55sxx/Libraries/drivers/SConscript +++ b/bsp/lpc55sxx/Libraries/drivers/SConscript @@ -43,6 +43,12 @@ if GetDepend('BSP_USING_WDT'): if GetDepend('BSP_USING_PWM'): src += ['drv_pwm.c'] +if GetDepend('BSP_USING_I2S'): + src += ['drv_i2s.c'] + +if GetDepend('BSP_USING_WM8904'): + src += ['drv_sound_wm8904.c'] + path = [cwd,cwd + '/config'] group = DefineGroup('Drivers', src, depend = [''], CPPPATH = path) diff --git a/bsp/lpc55sxx/Libraries/drivers/drv_i2s.c b/bsp/lpc55sxx/Libraries/drivers/drv_i2s.c new file mode 100644 index 0000000000..efbf848416 --- /dev/null +++ b/bsp/lpc55sxx/Libraries/drivers/drv_i2s.c @@ -0,0 +1,301 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-03-12 Vandoul the first version + */ + +#include +#include "fsl_i2s.h" +#include "fsl_i2s_dma.h" +#include "drv_i2s.h" + +#define DBG_TAG "DRVI2S" +#include "rtdbg.h" + +#ifdef BSP_USING_I2S + +#if !defined(BSP_USING_I2S0) && \ + !defined(BSP_USING_I2S1) && \ + !defined(BSP_USING_I2S2) && \ + !defined(BSP_USING_I2S3) && \ + !defined(BSP_USING_I2S4) && \ + !defined(BSP_USING_I2S5) && \ + !defined(BSP_USING_I2S6) && \ + !defined(BSP_USING_I2S7) +#error "Please define at least one I2Sx" +#endif + +#include + +enum { +#ifdef BSP_USING_I2S0 + I2S0_INDEX, +#endif +#ifdef BSP_USING_I2S1 + I2S1_INDEX, +#endif +#ifdef BSP_USING_I2S2 + I2S2_INDEX, +#endif +#ifdef BSP_USING_I2S3 + I2S3_INDEX, +#endif +#ifdef BSP_USING_I2S4 + I2S4_INDEX, +#endif +#ifdef BSP_USING_I2S5 + I2S5_INDEX, +#endif +#ifdef BSP_USING_I2S6 + I2S6_INDEX, +#endif +#ifdef BSP_USING_I2S7 + I2S7_INDEX, +#endif +}; + +struct lpc_i2s_clock_and_irq_param +{ + clock_attach_id_t i2s_clock; + reset_ip_name_t i2s_reset_bit; + IRQn_Type irq_type; +}; + +struct lpc_i2s +{ + struct rt_device device; + i2s_handle_t i2s_handle; + struct lpc_i2s_config config; + uint32_t index; + I2S_Type *i2s_base; + const char *device_name; +}; + +#define LPC_I2S_CONFIG_MODE_IS_SLAVE(dev) ((dev)->config.mode == LPC_I2S_CONFIG_MODE_SLAVE) +#define LPC_I2S_CONFIG_MODE_IS_MASTER(dev) ((dev)->config.mode == LPC_I2S_CONFIG_MODE_MASTER) + +#define LPC_I2S_CLOCK_AND_IRQ_PARAM_INIT(index) {.i2s_clock = kPLL0_DIV_to_FLEXCOMM##index, .i2s_reset_bit = kFC##index##_RST_SHIFT_RSTn, .irq_type = FLEXCOMM##index##_IRQn,} +const static struct lpc_i2s_clock_and_irq_param lpc_i2s_clock_and_irq_param_table[] = +{ +#ifdef BSP_USING_I2S0 +// {.i2s_clock = kPLL0_DIV_to_FLEXCOMM0, .i2s_reset_bit = kFC0_RST_SHIFT_RSTn, .irq_type = FLEXCOMM0_IRQn,}, + LPC_I2S_CLOCK_AND_IRQ_PARAM_INIT(0), +#endif +#ifdef BSP_USING_I2S1 + LPC_I2S_CLOCK_AND_IRQ_PARAM_INIT(1), +#endif +#ifdef BSP_USING_I2S2 + LPC_I2S_CLOCK_AND_IRQ_PARAM_INIT(2), +#endif +#ifdef BSP_USING_I2S3 + LPC_I2S_CLOCK_AND_IRQ_PARAM_INIT(3), +#endif +#ifdef BSP_USING_I2S4 + LPC_I2S_CLOCK_AND_IRQ_PARAM_INIT(4), +#endif +#ifdef BSP_USING_I2S5 + LPC_I2S_CLOCK_AND_IRQ_PARAM_INIT(5), +#endif +#ifdef BSP_USING_I2S6 + LPC_I2S_CLOCK_AND_IRQ_PARAM_INIT(6), +#endif +#ifdef BSP_USING_I2S7 + LPC_I2S_CLOCK_AND_IRQ_PARAM_INIT(7), +#endif +}; + +static struct lpc_i2s lpc_i2s_table[] = +{ +#ifdef BSP_USING_I2S0 + {.index = I2S0_INDEX,.i2s_base = I2S0,.device_name = "i2s0"}, +#endif +#ifdef BSP_USING_I2S1 + {.index = I2S1_INDEX,.i2s_base = I2S1,.device_name = "i2s1"}, +#endif +#ifdef BSP_USING_I2S2 + {.index = I2S2_INDEX,.i2s_base = I2S2,.device_name = "i2s2"}, +#endif +#ifdef BSP_USING_I2S3 + {.index = I2S3_INDEX,.i2s_base = I2S3,.device_name = "i2s3"}, +#endif +#ifdef BSP_USING_I2S4 + {.index = I2S4_INDEX,.i2s_base = I2S4,.device_name = "i2s4"}, +#endif +#ifdef BSP_USING_I2S5 + {.index = I2S5_INDEX,.i2s_base = I2S5,.device_name = "i2s5"}, +#endif +#ifdef BSP_USING_I2S6 + {.index = I2S6_INDEX,.i2s_base = I2S6,.device_name = "i2s6"}, +#endif +#ifdef BSP_USING_I2S7 + {.index = I2S7_INDEX,.i2s_base = I2S7,.device_name = "i2s7"}, +#endif +}; + +static void transfer_callback(I2S_Type *base, i2s_handle_t *handle, status_t completionStatus, void *userData) +{ + struct lpc_i2s *i2s_dev = rt_container_of(handle, struct lpc_i2s, i2s_handle); + if(LPC_I2S_CONFIG_MODE_IS_SLAVE(i2s_dev)) + { + if(i2s_dev->device.rx_indicate != RT_NULL) + { + i2s_dev->device.rx_indicate(&i2s_dev->device, completionStatus); + } + } + else + { + if(i2s_dev->device.tx_complete != RT_NULL) + { + i2s_dev->device.tx_complete(&i2s_dev->device, RT_NULL); + } + } +} + +static void i2s_clock_and_irq_config(struct lpc_i2s *dev) +{ + const struct lpc_i2s_clock_and_irq_param *clock_and_irq_param = &lpc_i2s_clock_and_irq_param_table[dev->index]; +// CLOCK_SetClkDiv(kCLOCK_DivPll0Clk, 0U, true); +// CLOCK_SetClkDiv(kCLOCK_DivPll0Clk, 1U, false); + CLOCK_AttachClk(clock_and_irq_param->i2s_clock); + RESET_PeripheralReset(clock_and_irq_param->i2s_reset_bit); + NVIC_ClearPendingIRQ(clock_and_irq_param->irq_type); + /* Enable interrupts for I2S */ + EnableIRQ(clock_and_irq_param->irq_type); +} +rt_err_t rt_i2s_init(rt_device_t dev) +{ + struct lpc_i2s *i2s_dev = rt_container_of(dev, struct lpc_i2s, device); + i2s_clock_and_irq_config(i2s_dev); + return RT_EOK; +} +rt_err_t rt_i2s_open(rt_device_t dev, rt_uint16_t oflag) +{ + struct lpc_i2s *i2s_dev = rt_container_of(dev, struct lpc_i2s, device); + i2s_config_t config; + if(i2s_dev->config.mode == LPC_I2S_CONFIG_MODE_SLAVE) + { + RT_ASSERT(i2s_dev->config.is_blocking == 0); + I2S_RxGetDefaultConfig(&config); + config.divider = CLOCK_GetPll0OutFreq()/i2s_dev->config.sampling_rate/i2s_dev->config.data_bits/i2s_dev->config.channels; + config.masterSlave = kI2S_MasterSlaveNormalSlave; + I2S_RxInit(i2s_dev->i2s_base, &config); + I2S_RxTransferCreateHandle(i2s_dev->i2s_base, &i2s_dev->i2s_handle, transfer_callback, NULL); + } + else if(i2s_dev->config.mode == LPC_I2S_CONFIG_MODE_MASTER) + { + RT_ASSERT(i2s_dev->config.is_blocking == 0); + I2S_TxGetDefaultConfig(&config); + config.divider = CLOCK_GetPll0OutFreq()/i2s_dev->config.sampling_rate/i2s_dev->config.data_bits/i2s_dev->config.channels; + config.masterSlave = kI2S_MasterSlaveNormalMaster; + I2S_TxInit(i2s_dev->i2s_base, &config); + I2S_TxTransferCreateHandle(i2s_dev->i2s_base, &i2s_dev->i2s_handle, transfer_callback, NULL); + } + return RT_EOK; +} +rt_err_t rt_i2s_close(rt_device_t dev) +{ + struct lpc_i2s *i2s_dev = rt_container_of(dev, struct lpc_i2s, device); + I2S_Deinit(i2s_dev->i2s_base); + return RT_EOK; +} +rt_ssize_t rt_i2s_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size) +{ + struct lpc_i2s *i2s_dev = rt_container_of(dev, struct lpc_i2s, device); + if(!LPC_I2S_CONFIG_MODE_IS_SLAVE(i2s_dev)) + { + return -RT_ERROR; + } + i2s_transfer_t transfer; + transfer.data = buffer; + transfer.dataSize = size; + if(kStatus_Success == I2S_RxTransferNonBlocking(i2s_dev->i2s_base, &i2s_dev->i2s_handle, transfer)) + return size; + else + return -RT_EBUSY; +} +rt_ssize_t rt_i2s_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size) +{ + struct lpc_i2s *i2s_dev = rt_container_of(dev, struct lpc_i2s, device); + if(!LPC_I2S_CONFIG_MODE_IS_MASTER(i2s_dev)) + { + return -RT_ERROR; + } + i2s_transfer_t transfer; + transfer.data = (uint8_t *)buffer; + transfer.dataSize = size; + if(kStatus_Success == I2S_TxTransferNonBlocking(i2s_dev->i2s_base, &i2s_dev->i2s_handle, transfer)) + return size; + else + return -RT_EBUSY; +} +rt_err_t rt_i2s_control(rt_device_t dev, int cmd, void *args) +{ + struct lpc_i2s *i2s_dev = rt_container_of(dev, struct lpc_i2s, device); + rt_err_t ret = RT_EOK; + RT_ASSERT(dev != RT_NULL); + RT_ASSERT(args != RT_NULL); + switch(cmd) + { + case RT_I2S_CTRL_RESET: + i2s_clock_and_irq_config(i2s_dev); + break; + case RT_I2S_CTRL_SET_CONFIG: + { + struct lpc_i2s_config *config = (struct lpc_i2s_config *)args; + i2s_dev->config = *config; + } + break; + default: + ret = -RT_ERROR; + break; + } + return ret; +} + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops i2s_core_ops = +{ + rt_i2s_init, + rt_i2s_open, + rt_i2s_close, + rt_i2s_read, + rt_i2s_write, + rt_i2s_control, +}; +#endif /* RT_USING_DEVICE_OPS */ + +int rt_hw_i2s_init(void) +{ + struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; + int i; + + for (i = 0; i < sizeof(lpc_i2s_table) / sizeof(lpc_i2s_table[0]); i++) + { + #ifdef RT_USING_DEVICE_OPS + lpc_i2s_table[i].device.ops = &i2s_core_ops; + #else + lpc_i2s_table[i].device.init = rt_i2s_init; + lpc_i2s_table[i].device.open = rt_i2s_open; + lpc_i2s_table[i].device.close = rt_i2s_close; + lpc_i2s_table[i].device.read = rt_i2s_read; + lpc_i2s_table[i].device.write = rt_i2s_write; + lpc_i2s_table[i].device.control = rt_i2s_control; + #endif + + /* register UART device */ + rt_device_register(&lpc_i2s_table[i].device, + lpc_i2s_table[i].device_name, + RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX); + } + + return 0; +} + +INIT_BOARD_EXPORT(rt_hw_i2s_init); + +#endif + diff --git a/bsp/lpc55sxx/Libraries/drivers/drv_i2s.h b/bsp/lpc55sxx/Libraries/drivers/drv_i2s.h new file mode 100644 index 0000000000..616c6d3143 --- /dev/null +++ b/bsp/lpc55sxx/Libraries/drivers/drv_i2s.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-03-12 Vandoul the first version + */ + +#ifndef __DRV_I2S_H__ +#define __DRV_I2S_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#pragma pack(push,1) +struct lpc_i2s_config +{ + uint32_t sampling_rate; + uint8_t mode; + uint8_t data_bits; + uint8_t channels; + uint8_t is_blocking; +}; +#pragma pack(pop) + +#define LPC_I2S_CONFIG_MODE_NULL 0 +#define LPC_I2S_CONFIG_MODE_SLAVE 1 +#define LPC_I2S_CONFIG_MODE_MASTER 2 + +#define RT_I2S_CTRL_RESET (RT_DEVICE_CTRL_BASE(Bus) + 1) +#define RT_I2S_CTRL_SET_CONFIG (RT_DEVICE_CTRL_BASE(Bus) + 2) + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/bsp/lpc55sxx/Libraries/drivers/drv_sound_wm8904.c b/bsp/lpc55sxx/Libraries/drivers/drv_sound_wm8904.c new file mode 100644 index 0000000000..7ac19ffbe7 --- /dev/null +++ b/bsp/lpc55sxx/Libraries/drivers/drv_sound_wm8904.c @@ -0,0 +1,1123 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-03-12 Vandoul the first version + */ + +#include +#include +#include +#include "drv_sound_wm8904.h" + +/** + * @brief object of wm8904. + */ +struct drv_sound_wm8904{ + struct rt_i2c_bus_device *i2c_bus; + rt_device_t i2s_bus; + int i2c_addr; +}; + +rt_err_t wm8904_write_register(struct drv_sound_wm8904 *dev, rt_uint8_t reg, rt_uint16_t value) +{ + struct rt_i2c_msg msg[2]; + rt_uint8_t buf[2]; + buf[0] = (value>>8)&0xFF; + buf[1] = value&0xFF; + msg[0].addr = dev->i2c_addr; + msg[0].buf = ® + msg[0].flags = RT_I2C_WR; + msg[0].len = 1; + msg[1].addr = dev->i2c_addr; + msg[1].buf = buf; + msg[1].flags = RT_I2C_WR|RT_I2C_NO_START; + msg[1].len = 2; + if(rt_i2c_transfer(dev->i2c_bus, msg, 2) != 2) + { + return -RT_ERROR; + } + return RT_EOK; +} + +rt_err_t wm8904_read_register(struct drv_sound_wm8904 *dev, rt_uint8_t reg, rt_uint16_t *value) +{ + struct rt_i2c_msg msg[2]; + rt_uint8_t buf[2]; + msg[0].addr = dev->i2c_addr; + msg[0].buf = ® + msg[0].flags = RT_I2C_WR; + msg[0].len = 1; + msg[1].addr = dev->i2c_addr; + msg[1].buf = buf; + msg[1].flags = RT_I2C_RD|RT_I2C_NO_START; + msg[1].len = 2; + if(rt_i2c_transfer(dev->i2c_bus, msg, 2) != 2) + { + return -RT_ERROR; + } + return RT_EOK; +} + +rt_err_t wm8904_modify_register(struct drv_sound_wm8904 *dev, rt_uint8_t reg, rt_uint16_t mask, rt_uint16_t value) +{ + rt_uint16_t reg_value; + rt_err_t ret; + + ret = wm8904_read_register(dev, reg, ®_value); + if(ret != RT_EOK) + { + return ret; + } + reg_value &= (uint16_t)~mask; + reg_value |= value; + return wm8904_write_register(dev, reg, reg_value); +} + +static rt_err_t wm8904_wait_on_write_sequencer(struct drv_sound_wm8904 *dev) +{ + rt_err_t ret; + rt_uint16_t value; + + do { + ret = wm8904_read_register(dev, WM8904_WRT_SEQUENCER_4, &value); + }while((ret == RT_EOK) && ((value & 1U) != 0u)); + return ret; +} + +static rt_err_t wm8904_updateformat(struct drv_sound_wm8904 *dev, wm8904_fs_ratio_t fs_ratio, wm8904_sample_rate_t sample_rate, wm8904_bit_width_t bit_width) +{ + rt_err_t err = RT_EOK; + /** Disable SYSCLK */ + err = wm8904_write_register(dev, WM8904_CLK_RATES_2, 0x00); + if(err != RT_EOK) + { + return err; + } + /** Set clock ratio and sample rate */ + err = wm8904_write_register(dev, WM8904_CLK_RATES_1, (rt_uint16_t)((rt_uint16_t)fs_ratio << 10U) | (rt_uint16_t)sample_rate); + if(err != RT_EOK) + { + return err; + } + /** Set bit resolution. */ + err = wm8904_modify_register(dev, WM8904_AUDIO_IF_1, (0x000CU), (rt_uint16_t)bit_width << 2U); + if(err != RT_EOK) + { + return err; + } + /** Enable SYSCLK */ + err = wm8904_write_register(dev, WM8904_CLK_RATES_2, 0x1007); + if(err != RT_EOK) + { + return err; + } + return RT_EOK; +} + +static rt_err_t wm8904_i2c_bus_init(struct rt_i2c_bus_device *i2c_bus) +{ + (void)i2c_bus; + return RT_EOK; +} +static rt_err_t wm8904_i2s_bus_init(rt_device_t i2s_bus) +{ + (void)i2s_bus; + return RT_EOK; +} + +const static uint16_t wm8904_init_list[][2] = { + /* TOCLK_RATE_DIV16=0, TOCLK_RATE_x4=1, SR_MODE=0, MCLK_DIV=1 + * (Required for MMCs: SGY, KRT see erratum CE000546) */ + {WM8904_CLK_RATES_0, 0xA45F}, + /* INL_ENA=1, INR ENA=1 */ + {WM8904_POWER_MGMT_0, 0x0003}, + /* HPL_PGA_ENA=1, HPR_PGA_ENA=1 */ + {WM8904_POWER_MGMT_2, 0x0003}, + /* DACL_ENA=1, DACR_ENA=1, ADCL_ENA=1, ADCR_ENA=1 */ + {WM8904_POWER_MGMT_6, 0x000F}, + /* ADC_OSR128=1 */ + {WM8904_ANALOG_ADC_0, 0x0001}, + /* DACL_DATINV=0, DACR_DATINV=0, DAC_BOOST=00, LOOPBACK=0, AIFADCL_SRC=0, + * AIFADCR_SRC=1, AIFDACL_SRC=0, AIFDACR_SRC=1, ADC_COMP=0, ADC_COMPMODE=0, + * DAC_COMP=0, DAC_COMPMODE=0 */ + {WM8904_AUDIO_IF_0, 0x0050}, + /* DAC_MONO=0, DAC_SB_FILT-0, DAC_MUTERATE=0, DAC_UNMUTE RAMP=0, + * DAC_OSR128=1, DAC_MUTE=0, DEEMPH=0 (none) */ + {WM8904_DAC_DIG_1, 0x0040}, + /* LINMUTE=0, LIN_VOL=0_0101 */ + {WM8904_ANALOG_LEFT_IN_0, 0x0005}, + /* RINMUTE=0, RIN VOL=0_0101 LINEOUTL RMV SHORT-1, LINEOUTL ENA_OUTP=1, + * LINEOUTL_ENA_DLY=1, LINEOUTL_ENA=1, LINEOUTR_RMV_SHORT-1, + * LINEOUTR_ENA_OUTP=1 */ + {WM8904_ANALOG_RIGHT_IN_0, 0x0005}, + /* HPOUTL_MUTE=0, HPOUT_VU=0, HPOUTLZC=0, HPOUTL_VOL=10_1101 */ + {WM8904_ANALOG_OUT1_LEFT, 0x00AD}, + /* HPOUTR_MUTE=0, HPOUT_VU=0, HPOUTRZC=0, HPOUTR_VOL=10_1101 */ + {WM8904_ANALOG_OUT1_RIGHT, 0x00AD}, + /* Enable DC servos for headphone out */ + {WM8904_DC_SERVO_0, 0x0003}, + /* HPL_RMV_SHORT=1, HPL_ENA_OUTP=1, HPL_ENA_DLY=1, HPL_ENA=1, + * HPR_RMV_SHORT=1, HPR_ENA_OUTP=1, HPR_ENA_DLY=1, HPR_ENA=1 */ + {WM8904_ANALOG_HP_0, 0x00FF}, + /* CP_DYN_PWR=1 */ + {WM8904_CLS_W_0, 0x0001}, + /* CP_ENA=1 */ + {WM8904_CHRG_PUMP_0, 0x0001}, + /* TOCLK_RATE_DIV16=0, TOCLK_RATE_x4=1, SR_MODE=0, MCLK_DIV=1 + * (Required for MMCs: SGY, KRT see erratum CE000546) */ + {WM8904_CLK_RATES_0, 0xA45F}, + /* INL_ENA=1, INR ENA=1 */ + {WM8904_POWER_MGMT_0, 0x0003}, + /* HPL_PGA_ENA=1, HPR_PGA_ENA=1 */ + {WM8904_POWER_MGMT_2, 0x0003}, + /* DACL_ENA=1, DACR_ENA=1, ADCL_ENA=1, ADCR_ENA=1 */ + {WM8904_POWER_MGMT_6, 0x000F}, + /* ADC_OSR128=1 */ + {WM8904_ANALOG_ADC_0, 0x0001}, + /* DACL_DATINV=0, DACR_DATINV=0, DAC_BOOST=00, LOOPBACK=0, AIFADCL_SRC=0, + * AIFADCR_SRC=1, AIFDACL_SRC=0, AIFDACR_SRC=1, ADC_COMP=0, ADC_COMPMODE=0, + * DAC_COMP=0, DAC_COMPMODE=0 */ + {WM8904_AUDIO_IF_0, 0x0050}, + /* DAC_MONO=0, DAC_SB_FILT-0, DAC_MUTERATE=0, DAC_UNMUTE RAMP=0, + * DAC_OSR128=1, DAC_MUTE=0, DEEMPH=0 (none) */ + {WM8904_DAC_DIG_1, 0x0040}, + /* LINMUTE=0, LIN_VOL=0_0101 */ + {WM8904_ANALOG_LEFT_IN_0, 0x0005}, + /* RINMUTE=0, RIN VOL=0_0101 LINEOUTL RMV SHORT-1, LINEOUTL ENA_OUTP=1, + * LINEOUTL_ENA_DLY=1, LINEOUTL_ENA=1, LINEOUTR_RMV_SHORT-1, + * LINEOUTR_ENA_OUTP=1 */ + {WM8904_ANALOG_RIGHT_IN_0, 0x0005}, + /* HPOUTL_MUTE=0, HPOUT_VU=0, HPOUTLZC=0, HPOUTL_VOL=10_1101 */ + {WM8904_ANALOG_OUT1_LEFT, 0x00AD}, + /* HPOUTR_MUTE=0, HPOUT_VU=0, HPOUTRZC=0, HPOUTR_VOL=10_1101 */ + {WM8904_ANALOG_OUT1_RIGHT, 0x00AD}, + /* Enable DC servos for headphone out */ + {WM8904_DC_SERVO_0, 0x0003}, + /* HPL_RMV_SHORT=1, HPL_ENA_OUTP=1, HPL_ENA_DLY=1, HPL_ENA=1, + * HPR_RMV_SHORT=1, HPR_ENA_OUTP=1, HPR_ENA_DLY=1, HPR_ENA=1 */ + {WM8904_ANALOG_HP_0, 0x00FF}, + /* CP_DYN_PWR=1 */ + {WM8904_CLS_W_0, 0x0001}, + /* CP_ENA=1 */ + {WM8904_CHRG_PUMP_0, 0x0001}, +}; +rt_err_t wm8904_init(struct drv_sound_wm8904 *dev, struct wm8904_config *config) +{ + rt_err_t ret; + + dev->i2c_bus = rt_i2c_bus_device_find(config->i2c_bus_name); + if(dev->i2c_bus == RT_NULL) + { + return -RT_EINVAL; + } + dev->i2s_bus = rt_device_find(config->i2s_bus_name); + if(dev->i2s_bus == RT_NULL) + { + return -RT_EINVAL; + } + + wm8904_i2c_bus_init(dev->i2c_bus); + wm8904_i2s_bus_init(dev->i2s_bus); + + ret = wm8904_write_register(dev, WM8904_RESET, 0x0000); + if(ret != RT_EOK) + { + return ret; + } + + /* MCLK_INV=0, SYSCLK_SRC=0, TOCLK_RATE=0, OPCLK_ENA=1, + * CLK_SYS_ENA=1, CLK_DSP_ENA=1, TOCLK_ENA=1 */ + ret = wm8904_write_register(dev, WM8904_CLK_RATES_2, 0x000F); + if(ret != RT_EOK) + { + return ret; + } + + /* WSEQ_ENA=1, WSEQ_WRITE_INDEX=0_0000 */ + ret = wm8904_write_register(dev, WM8904_WRT_SEQUENCER_0, 0x0100); + if(ret != RT_EOK) + { + return ret; + } + + /* WSEQ_ABORT=0, WSEQ_START=1, WSEQ_START_INDEX=00_0000 */ + ret = wm8904_write_register(dev, WM8904_WRT_SEQUENCER_3, 0x0100); + if(ret != RT_EOK) + { + return ret; + } + + /* WSEQ_ENA=1, WSEQ_SRITE_INDEX=0_0000 */ + ret = wm8904_write_register(dev, WM8904_WRT_SEQUENCER_0, 0x0100); + if(ret != RT_EOK) + { + return ret; + } + + ret = wm8904_wait_on_write_sequencer(dev); + if(ret != RT_EOK) + { + return ret; + } + + for(int i=0; i> (sysclk_div & 0x1U); + + if((sysclk / bclk > 48U) || (bclk / sample_rate > 2047U) || (bclk / sample_rate < 8U)) + { + return -RT_EINVAL; + } + + err = wm8904_read_register(dev, WM8904_AUDIO_IF_2, &audio_interface); + if(err != RT_EOK) + { + return err; + } + + audio_interface &= ~(rt_uint16_t)0x1FU; + bclk_div = (sysclk * 10U) / bclk; + + switch(bclk_div) + { + case 10: + audio_interface |= 0U; + break; + case 15: + audio_interface |= 1U; + break; + case 20: + audio_interface |= 2U; + break; + case 30: + audio_interface |= 3U; + break; + case 40: + audio_interface |= 4U; + break; + case 50: + audio_interface |= 5U; + break; + case 55: + audio_interface |= 6U; + break; + case 60: + audio_interface |= 7U; + break; + case 80: + audio_interface |= 8U; + break; + case 100: + audio_interface |= 9U; + break; + case 110: + audio_interface |= 10U; + break; + case 120: + audio_interface |= 11U; + break; + case 160: + audio_interface |= 12U; + break; + case 200: + audio_interface |= 13U; + break; + case 220: + audio_interface |= 14U; + break; + case 240: + audio_interface |= 15U; + break; + case 250: + audio_interface |= 16U; + break; + case 300: + audio_interface |= 17U; + break; + case 320: + audio_interface |= 18U; + break; + case 440: + audio_interface |= 19U; + break; + case 480: + audio_interface |= 20U; + break; + default: + err = -RT_EINVAL; + break; + } + if(err != RT_EOK) + { + return err; + } + + /** bclk divider */ + err = wm8904_write_register(dev, WM8904_AUDIO_IF_2, audio_interface); + if(err != RT_EOK) + { + return err; + } + + err = wm8904_modify_register(dev, WM8904_GPIO_CONTROL_4, 0x8FU, 1U); + if(err != RT_EOK) + { + return err; + } + /** LRCLK direction and divider */ + audio_interface = (rt_uint16_t)((1UL << 11U) | (bclk / sample_rate)); + err = wm8904_modify_register(dev, WM8904_AUDIO_IF_3, 0xFFFU, audio_interface); + if(err != RT_EOK) + { + return err; + } + return RT_EOK; +} + +rt_err_t wm8904_set_fll_config(struct drv_sound_wm8904 *dev, wm8904_fll_config_t *config) +{ + RT_ASSERT(dev != RT_NULL); + RT_ASSERT(config != RT_NULL); + + rt_uint32_t reference_clock = config->ref_clock_hz; + rt_uint32_t input_divider = 0U; + rt_uint32_t fvco = 0U, output_div = 0U, ratio = 0U; + rt_uint32_t n = 0U, k = 0U; + + /* it is recommended that the highest possible frequency - within the 13.5MHz limit - should be selected */ + if(reference_clock < 13500000U) + { + input_divider = 0; + } + else if(reference_clock / 2U < 13500000U) + { + input_divider = 1; + } + else if(reference_clock / 4U < 13500000U) + { + input_divider = 2; + } + else + { + input_divider = 3; + } + + if(reference_clock / (1UL << input_divider) > 13500000) + { + return -RT_EINVAL; + } + + reference_clock = reference_clock / (1UL << input_divider); + + for (output_div = 4U; output_div <= 64U; output_div++) + { + fvco = output_div * config->output_clock_hz; + if ((fvco >= 90000000U) && (fvco <= 100000000U)) + { + break; + } + } + + if(reference_clock <= 64000U) + { + ratio = 4U; + } + else if(reference_clock <= 128000U) + { + ratio = 3U; + } + else if(reference_clock <= 256000U) + { + ratio = 2U; + } + else if(reference_clock <= 1000000U) + { + ratio = 1U; + } + else + { + ratio = 0U; + } + + n = fvco / ((ratio + 1U) * reference_clock); + k = (rt_uint32_t)((rt_uint64_t)fvco * 1000000U) / ((ratio + 1U) * reference_clock); + if(n != 0U) + { + k = k - n * 1000000U; + } + k = (rt_uint32_t)((rt_uint64_t)k * 65536U) / 1000000U; + + /* configure WM8904 */ + if (wm8904_modify_register(dev, WM8904_FLL_CONTROL_1, 7U, 4U) != RT_EOK) + { + return -RT_ERROR; + } + + /* configure WM8904 */ + if (wm8904_modify_register(dev, WM8904_FLL_CONTROL_2, 0x3F07U, (rt_uint16_t)(((output_div - 1U) << 8U) | ratio)) != RT_EOK) + { + return -RT_ERROR; + } + + if (wm8904_write_register(dev, WM8904_FLL_CONTROL_3, (rt_uint16_t)k) != RT_EOK) + { + return -RT_ERROR; + } + + if (wm8904_modify_register(dev, WM8904_FLL_CONTROL_4, 0x7FE0U, (rt_uint16_t)(n << 5U)) != RT_EOK) + { + return -RT_ERROR; + } + + if (wm8904_write_register(dev, WM8904_FLL_CONTROL_5, (rt_uint16_t)((input_divider << 3U) | (rt_uint16_t)config->source)) != RT_EOK) + { + return -RT_ERROR; + } + + if (wm8904_modify_register(dev, WM8904_FLL_CONTROL_1, 1U, 1) != RT_EOK) + { + return -RT_ERROR; + } + + /** enable GPIO1 output fll output clock */ + if (wm8904_write_register(dev, WM8904_GPIO_CONTROL_1, (rt_uint16_t)9U) != RT_EOK) + { + return -RT_ERROR; + } + + return RT_EOK; +} + +rt_err_t wm8904_set_protocol(struct drv_sound_wm8904 *dev, wm8904_protocol_t p) +{ + return wm8904_modify_register(dev, WM8904_AUDIO_IF_1, (0x0003U | (1U << 4U)), (rt_uint16_t)p); +} + +rt_err_t wm8904_select_lrc_polarity(struct drv_sound_wm8904 *dev, rt_uint32_t polarity) +{ + return wm8904_modify_register(dev, WM8904_AUDIO_IF_1, 0x0010U, (rt_uint16_t)polarity); +} + +rt_err_t wm8904_enable_dac_time_slot(struct drv_sound_wm8904 *dev, wm8904_timeslot_t timeslot) +{ + return wm8904_modify_register(dev, WM8904_AUDIO_IF_1, ((rt_uint16_t)3U << 12U), (((rt_uint16_t)1U << 13U) | ((rt_uint16_t)timeslot << 12U))); +} + +rt_err_t wm8904_enable_adc_time_slot(struct drv_sound_wm8904 *dev, wm8904_timeslot_t timeslot) +{ + return wm8904_modify_register(dev, WM8904_AUDIO_IF_1, ((rt_uint16_t)3U << 10U), (((rt_uint16_t)1U << 11U) | ((rt_uint16_t)timeslot << 10U))); +} + +rt_err_t wm8904_check_audio_format(struct drv_sound_wm8904 *dev, wm8904_audio_format_t *format, rt_uint32_t mclk_freq) +{ + RT_ASSERT((dev != RT_NULL) && (format != RT_NULL)); + + rt_err_t err = RT_EOK; + rt_uint16_t mclk_div = 0U; + rt_uint32_t sample_rate = 0U; + rt_uint32_t fs_ratio = 0; + wm8904_sample_rate_t reg_sample_rate = format->sampleRate; + + err = wm8904_read_register(dev, WM8904_CLK_RATES_0, &mclk_div); + if(err != RT_EOK) + { + return err; + } + + switch(format->sampleRate) + { + case WM8904_SAMPLERATE_8kHz: + sample_rate = 8000; + break; + case WM8904_SAMPLERATE_12kHz : + sample_rate = 12000; + break; + case WM8904_SAMPLERATE_16kHz : + sample_rate = 16000; + break; + case WM8904_SAMPLERATE_24kHz : + sample_rate = 24000; + break; + case WM8904_SAMPLERATE_32kHz : + sample_rate = 32000; + break; + case WM8904_SAMPLERATE_48kHz : + sample_rate = 48000; + break; + case WM8904_SAMPLERATE_11025Hz: + sample_rate = 11025; + reg_sample_rate = WM8904_SAMPLERATE_12kHz; + break; + case WM8904_SAMPLERATE_22050Hz: + sample_rate = 22050; + reg_sample_rate = WM8904_SAMPLERATE_24kHz; + break; + case WM8904_SAMPLERATE_44100Hz: + sample_rate = 44100; + reg_sample_rate = WM8904_SAMPLERATE_48kHz; + break; + default: + err = -RT_EINVAL; + break; + } + + if(err != RT_EOK) + { + return err; + } + + fs_ratio = (mclk_freq >> (mclk_div & 0x1U)) / sample_rate; + + switch(fs_ratio) + { + case 64: + format->fsRatio = WM8904_FSRATIO_64X; + break; + case 128 : + format->fsRatio = WM8904_FSRATIO_128X; + break; + case 192 : + format->fsRatio = WM8904_FSRATIO_192X; + break; + case 256 : + format->fsRatio = WM8904_FSRATIO_256X; + break; + case 384 : + format->fsRatio = WM8904_FSRATIO_384X; + break; + case 512 : + format->fsRatio = WM8904_FSRATIO_512X; + break; + case 768 : + format->fsRatio = WM8904_FSRATIO_768X; + break; + case 1024: + format->fsRatio = WM8904_FSRATIO_1024X; + break; + case 1408: + format->fsRatio = WM8904_FSRATIO_1408X; + break; + case 1536: + format->fsRatio = WM8904_FSRATIO_1536X; + break; + default: + err = -RT_EINVAL; + break; + } + + if(err != RT_EOK) + { + return err; + } + return wm8904_updateformat(dev, format->fsRatio, reg_sample_rate, format->bitWidth); +} + +rt_err_t wm8904_set_audio_format(struct drv_sound_wm8904 *dev, rt_uint32_t sysclk, rt_uint32_t sample_rate, rt_uint32_t bit_width) +{ + rt_uint32_t ratio = 0; + rt_err_t err = RT_EOK; + wm8904_bit_width_t reg_bit_width = WM8904_BITWIDTH_32; + wm8904_sample_rate_t reg_sample_reate = WM8904_SAMPLERATE_48kHz; + wm8904_fs_ratio_t reg_fsratio = WM8904_FSRATIO_1536X; + + rt_uint16_t temp_reg = 0U; + + err = wm8904_read_register(dev, WM8904_CLK_RATES_0, &temp_reg); + if(err != RT_EOK) + { + return err; + } + + switch(sample_rate) + { + case 8000: + sample_rate = WM8904_SAMPLERATE_8kHz; + break; + case 11025: + sample_rate = WM8904_SAMPLERATE_12kHz; + break; + case 12000: + sample_rate = WM8904_SAMPLERATE_12kHz; + break; + case 16000: + sample_rate = WM8904_SAMPLERATE_16kHz; + break; + case 22050: + sample_rate = WM8904_SAMPLERATE_24kHz; + break; + case 24000: + sample_rate = WM8904_SAMPLERATE_24kHz; + break; + case 32000: + sample_rate = WM8904_SAMPLERATE_32kHz; + break; + case 44100: + sample_rate = WM8904_SAMPLERATE_48kHz; + break; + case 48000: + sample_rate = WM8904_SAMPLERATE_48kHz; + break; + default: + err = -RT_EINVAL; + break; + } + if(err != RT_EOK) + { + return err; + } + + switch(bit_width) + { + case 16: + reg_bit_width = WM8904_BITWIDTH_16; + break; + case 20: + reg_bit_width = WM8904_BITWIDTH_20; + break; + case 24: + reg_bit_width = WM8904_BITWIDTH_24; + break; + case 32: + reg_bit_width = WM8904_BITWIDTH_32; + break; + default: + err = -RT_EINVAL; + break; + } + if(err != RT_EOK) + { + return err; + } + + ratio = (sysclk >> (temp_reg & 0x1U)) / sample_rate; + + switch(ratio) + { + case 64: + reg_fsratio = WM8904_FSRATIO_64X; + break; + case 128 : + reg_fsratio = WM8904_FSRATIO_128X; + break; + case 192 : + reg_fsratio = WM8904_FSRATIO_192X; + break; + case 256 : + reg_fsratio = WM8904_FSRATIO_256X; + break; + case 384 : + reg_fsratio = WM8904_FSRATIO_384X; + break; + case 512 : + reg_fsratio = WM8904_FSRATIO_512X; + break; + case 768 : + reg_fsratio = WM8904_FSRATIO_768X; + break; + case 1024: + reg_fsratio = WM8904_FSRATIO_1024X; + break; + case 1408: + reg_fsratio = WM8904_FSRATIO_1408X; + break; + case 1536: + reg_fsratio = WM8904_FSRATIO_1536X; + break; + default: + err = -RT_EINVAL; + break; + } + + if(err != RT_EOK) + { + return err; + } + err = wm8904_updateformat(dev, reg_fsratio, reg_sample_reate, reg_bit_width); + if(err != RT_EOK) + { + return err; + } + err = wm8904_read_register(dev, WM8904_AUDIO_IF_1, &temp_reg); + if(err != RT_EOK) + { + return err; + } + if((temp_reg & (1UL << 6U)) != 0) + { + err = wm8904_set_master_clock(dev, sysclk, sample_rate, bit_width); + } + return err; +} + +rt_err_t wm8904_set_volume(struct drv_sound_wm8904 *dev, rt_uint16_t volume_left, rt_uint16_t volume_right) +{ + RT_ASSERT(volume_left <= WM8904_MAP_HEADPHONE_LINEOUT_MAX_VOLUME); + RT_ASSERT(volume_right <= WM8904_MAP_HEADPHONE_LINEOUT_MAX_VOLUME); + + rt_err_t err = RT_EOK; + + err = wm8904_modify_register(dev, WM8904_ANALOG_OUT1_LEFT, 0x1BF, volume_left); + if(err != RT_EOK) + { + return err; + } + err = wm8904_modify_register(dev, WM8904_ANALOG_OUT1_RIGHT, 0x1BF, ((rt_uint16_t)volume_right | 0x0080U)); + if(err != RT_EOK) + { + return err; + } + + return RT_EOK; +} + +rt_err_t wm8904_set_mute(struct drv_sound_wm8904 *dev, rt_bool_t mute_left, rt_bool_t mute_right) +{ + rt_err_t err = RT_EOK; + rt_uint16_t left = (rt_uint16_t)(mute_left ? 0x0100U : 0x0000U); + rt_uint16_t right = (rt_uint16_t)(mute_right ? 0x0100U : 0x0000U); + + err = wm8904_modify_register(dev, WM8904_ANALOG_OUT1_LEFT, 0x0100U, left); + if(err != RT_EOK) + { + return err; + } + err = wm8904_modify_register(dev, WM8904_ANALOG_OUT1_RIGHT, 0x0180U, ((rt_uint16_t)right | 0x0080U)); + if(err != RT_EOK) + { + return err; + } + + return err; +} + +rt_err_t wm8904_set_channel_volume(struct drv_sound_wm8904 *dev, rt_uint32_t channel, rt_uint32_t volume) +{ + RT_ASSERT(volume <= WM8904_MAP_HEADPHONE_LINEOUT_MAX_VOLUME); + + rt_err_t err = RT_EOK; + + /* headphone left channel 0x1BF means unmute the OUT and reset the OUT volume update bit and volume range fields */ + if((channel & (rt_uint32_t)WM8904_HEADPHONE_LEFT) != 0U) + { + err = wm8904_modify_register(dev, WM8904_ANALOG_OUT1_LEFT, 0x1BFU, (rt_uint16_t)volume | 0x80U); + } + /* headphone right channel */ + if((channel & (rt_uint32_t)WM8904_HEADPHONE_RIGHT) != 0U) + { + err = wm8904_modify_register(dev, WM8904_ANALOG_OUT1_RIGHT, 0x1BFU, (rt_uint16_t)volume | 0x80U); + } + /* line out left channel */ + if((channel & (rt_uint32_t)WM8904_LINEOUT_LEFT) != 0U) + { + err = wm8904_modify_register(dev, WM8904_ANALOG_OUT2_LEFT, 0x1BFU, (rt_uint16_t)volume | 0x80U); + } + /* line out right channel */ + if((channel & (rt_uint32_t)WM8904_LINEOUT_RIGHT) != 0U) + { + err = wm8904_modify_register(dev, WM8904_ANALOG_OUT2_RIGHT, 0x1BFU, (rt_uint16_t)volume | 0x80U); + } + + return err; +} + +rt_err_t wm8904_set_channel_mute(struct drv_sound_wm8904 *dev, rt_uint32_t channel, rt_bool_t is_mute) +{ + rt_err_t err = RT_EOK; + rt_uint16_t reg_value = 0U, reg_mask = 0U; + + reg_value = is_mute ? 0x180U : 0x80U; + reg_mask = 0x100U; + + /* headphone left channel */ + if((channel & (rt_uint32_t)WM8904_HEADPHONE_LEFT) != 0U) + { + err = wm8904_modify_register(dev, WM8904_ANALOG_OUT1_LEFT, reg_mask, reg_value); + } + + /* headphone right channel */ + if((channel & (rt_uint32_t)WM8904_HEADPHONE_RIGHT) != 0U) + { + err = wm8904_modify_register(dev, WM8904_ANALOG_OUT1_RIGHT, reg_mask, reg_value); + } + + /* line out left channel */ + if((channel & (rt_uint32_t)WM8904_LINEOUT_LEFT) != 0U) + { + err = wm8904_modify_register(dev, WM8904_ANALOG_OUT2_LEFT, reg_mask, reg_value); + } + + /* line out right channel */ + if((channel & (rt_uint32_t)WM8904_LINEOUT_RIGHT) != 0U) + { + err = wm8904_modify_register(dev, WM8904_ANALOG_OUT2_RIGHT, reg_mask, reg_value); + } + return err; +} + +rt_err_t wm8904_enable_dac_volume(struct drv_sound_wm8904 *dev, rt_uint8_t volume) +{ + rt_err_t err = RT_EOK; + err = wm8904_write_register(dev, WM8904_DAC_DIGITAL_VOLUME_LEFT, (rt_uint16_t)(volume | 0x100UL)); + if(err == RT_EOK) + { + err = wm8904_write_register(dev, WM8904_DAC_DIGITAL_VOLUME_RIGHT, (rt_uint16_t)(volume | 0x100UL)); + } + return err; +} + +rt_err_t wm8904_set_module_power(struct drv_sound_wm8904 *dev, wm8904_module_t module, rt_bool_t is_enabled) +{ + rt_uint8_t reg_addr = 0, reg_bit_mask = 0U, reg_value = 0U; + rt_err_t err = RT_EOK; + + switch(module) + { + case WM8904_MODULE_ADC: + reg_addr = WM8904_POWER_MGMT_6; + reg_bit_mask = 3U; + reg_value = is_enabled ? 3U : 0U; + break; + case WM8904_MODULE_DAC: + reg_addr = WM8904_POWER_MGMT_6; + reg_bit_mask = 0xCU; + reg_value = is_enabled ? 0xCU : 0U; + break; + case WM8904_MODULE_PGA: + reg_addr = WM8904_POWER_MGMT_0; + reg_bit_mask = 3U; + reg_value = is_enabled ? 3U : 0U; + break; + case WM8904_MODULE_HEADPHONE: + reg_addr = WM8904_POWER_MGMT_2; + reg_bit_mask = 3U; + reg_value = is_enabled ? 3U : 0U; + break; + case WM8904_MODULE_LINEOUT: + reg_addr = WM8904_POWER_MGMT_3; + reg_bit_mask = 3U; + reg_value = is_enabled ? 3U : 0U; + break; + default: + err = -RT_EINVAL; + break; + } + + if(err == RT_EOK) + { + err = wm8904_modify_register(dev, reg_addr, reg_bit_mask, reg_value); + } + return err; +} + +rt_err_t wm8904_set_record(struct drv_sound_wm8904 *dev, rt_uint32_t record_source) +{ + rt_uint8_t reg_left_addr = WM8904_ANALOG_LEFT_IN_1, reg_right_addr = WM8904_ANALOG_RIGHT_IN_1; + rt_uint16_t reg_left_value = 0U, reg_right_value = 0U, reg_bit_mask = 0U; + rt_err_t err = RT_EOK; + + switch(record_source) + { + case WM8904_RECORD_SOURCE_DIFFERENTIAL_LINE: + reg_left_value = 1U; + reg_right_value = 1U; + reg_bit_mask = 0x3FU; + break; + case WM8904_RECORD_SOURCE_DIFFERENTIAL_MIC: + reg_left_value = 2U; + reg_right_value = 2U; + reg_bit_mask = 0x3FU; + break; + case WM8904_RECORD_SOURCE_LINE_INPUT: + reg_left_value = 0U; + reg_right_value = 0U; + reg_bit_mask = 0x3FU; + break; + case WM8904_RECORD_SOURCE_DIGITAL_MIC: + reg_left_value = 1U << 12; + reg_left_addr = WM8904_DAC_DIG_0; + reg_right_addr = 0U; + reg_bit_mask = 1U << 12; + break; + default: + err = -RT_EINVAL; + break; + } + + if(err != RT_EOK) + { + return err; + } + err = wm8904_modify_register(dev, reg_left_addr, reg_bit_mask, reg_left_value); + if((err == RT_EOK) && (reg_right_addr != 0U)) + { + err = wm8904_modify_register(dev, reg_right_addr, reg_bit_mask, reg_right_value); + } + return err; +} + +rt_err_t wm8904_set_record_channel(struct drv_sound_wm8904 *dev, rt_uint32_t left_record_channel, rt_uint32_t right_record_channel) +{ + rt_uint8_t reg_left_addr = WM8904_ANALOG_LEFT_IN_1, reg_right_addr = WM8904_ANALOG_RIGHT_IN_1; + rt_uint16_t reg_left_value = 0U, reg_right_value = 0U, reg_bit_mask; + rt_err_t err = RT_EOK; + rt_uint8_t left_positive_channel = 0U, left_negative_channel = 0U, right_positive_channel = 0U, right_negative_channel = 0U; + + if((left_record_channel & (rt_uint32_t)WM8904_RECORD_CHANNEL_DIFFERENTIAL_POSITIVE1) != 0U) + { + left_positive_channel = 0U; + } + else if((left_record_channel & (rt_uint32_t)WM8904_RECORD_CHANNEL_DIFFERENTIAL_POSITIVE2) != 0U) + { + left_positive_channel = 1U; + } + else + { + left_positive_channel = 2U; + } + + if((left_record_channel & (rt_uint32_t)WM8904_RECORD_CHANNEL_DIFFERENTIAL_NEGATIVE1) != 0U) + { + left_negative_channel = 0U; + } + else if((left_record_channel & (rt_uint32_t)WM8904_RECORD_CHANNEL_DIFFERENTIAL_NEGATIVE2) != 0U) + { + left_negative_channel = 1U; + } + else if((left_record_channel & (rt_uint32_t)WM8904_RECORD_CHANNEL_DIFFERENTIAL_NEGATIVE3) != 0U) + { + left_negative_channel = 2U; + } + else + { + left_negative_channel = left_positive_channel; + } + + if((right_record_channel & (rt_uint32_t)WM8904_RECORD_CHANNEL_DIFFERENTIAL_POSITIVE1) != 0U) + { + right_positive_channel = 0U; + } + else if((right_record_channel & (rt_uint32_t)WM8904_RECORD_CHANNEL_DIFFERENTIAL_POSITIVE2) != 0U) + { + right_positive_channel = 1U; + } + else + { + right_positive_channel = 2U; + } + + if((right_record_channel & (rt_uint32_t)WM8904_RECORD_CHANNEL_DIFFERENTIAL_NEGATIVE1) != 0U) + { + right_negative_channel = 0U; + } + else if((right_record_channel & (rt_uint32_t)WM8904_RECORD_CHANNEL_DIFFERENTIAL_NEGATIVE2) != 0U) + { + right_negative_channel = 1U; + } + else if((right_record_channel & (rt_uint32_t)WM8904_RECORD_CHANNEL_DIFFERENTIAL_NEGATIVE3) != 0U) + { + right_negative_channel = 2U; + } + else + { + right_negative_channel = right_positive_channel; + } + + reg_left_value = (((rt_uint16_t)left_negative_channel & 3U) << 4U) | (((rt_uint16_t)left_positive_channel & 3U) << 2U); + reg_right_value = (((rt_uint16_t)right_negative_channel & 3U) << 4U) | (((rt_uint16_t)right_positive_channel & 3U) << 2U); + reg_bit_mask = 0x3CU; + + err = wm8904_modify_register(dev, reg_left_addr, reg_bit_mask, reg_left_value); + if(err == RT_EOK) + { + return wm8904_modify_register(dev, reg_right_addr, reg_bit_mask, reg_right_value); + } + return -RT_ERROR; +} + +rt_err_t wm8904_set_play(struct drv_sound_wm8904 *dev, rt_uint32_t play_source) +{ + rt_uint16_t reg_value = 0U, reg_bit_mask = 0xFU; + + /* source from PGA */ + if(play_source == (rt_uint32_t)WM8904_PLAY_SOURCE_PGA) + { + reg_value |= (3U << 2U) | 3U; + } else + /* source from DAC */ + if(play_source == (rt_uint32_t)WM8904_PLAY_SOURCE_DAC) + { + reg_value &= (rt_uint16_t) ~((3U << 2U) | 3U); + } + + return wm8904_modify_register(dev, WM8904_ANALOG_OUT12_ZC, reg_bit_mask, reg_value); +} + +/*******************************************************************************************/ +rt_err_t wm8904_audio_getcaps(struct rt_audio_device *audio, struct rt_audio_caps *caps) +{ + return RT_EOK; +} +rt_err_t wm8904_audio_configure(struct rt_audio_device *audio, struct rt_audio_caps *caps) +{ + return RT_EOK; +} +rt_err_t wm8904_audio_init(struct rt_audio_device *audio) +{ + return RT_EOK; +} +rt_err_t wm8904_audio_start(struct rt_audio_device *audio, int stream) +{ + return RT_EOK; +} +rt_err_t wm8904_audio_stop(struct rt_audio_device *audio, int stream) +{ + return RT_EOK; +} +rt_ssize_t wm8904_audio_transmit(struct rt_audio_device *audio, const void *writeBuf, void *readBuf, rt_size_t size) +{ + return 0; +} +void wm8904_audio_buffer_info(struct rt_audio_device *audio, struct rt_audio_buf_info *info) +{ +} +/** + * + */ +struct rt_audio_ops wm8904_audio_ops = +{ + .getcaps = wm8904_audio_getcaps, + .configure = wm8904_audio_configure, + .init = wm8904_audio_init, + .start = wm8904_audio_start, + .stop = wm8904_audio_stop, + .transmit = wm8904_audio_transmit, + .buffer_info = wm8904_audio_buffer_info, +}; diff --git a/bsp/lpc55sxx/Libraries/drivers/drv_sound_wm8904.h b/bsp/lpc55sxx/Libraries/drivers/drv_sound_wm8904.h new file mode 100644 index 0000000000..8b052869b4 --- /dev/null +++ b/bsp/lpc55sxx/Libraries/drivers/drv_sound_wm8904.h @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-03-12 Vandoul the first version + */ + +#ifndef __DRV_SOUND_WM8904_H__ +#define __DRV_SOUND_WM8904_H__ + +#include + +typedef enum +{ + WM8904_RESET = 0x00, + WM8904_ANALOG_ADC_0 = 0x0A, + WM8904_POWER_MGMT_0 = 0x0C, + WM8904_POWER_MGMT_2 = 0x0E, + WM8904_POWER_MGMT_3 = 0x0F, + WM8904_POWER_MGMT_6 = 0x12, + WM8904_CLK_RATES_0 = 0x14, + WM8904_CLK_RATES_1 = 0x15, + WM8904_CLK_RATES_2 = 0x16, + WM8904_AUDIO_IF_0 = 0x18, + WM8904_AUDIO_IF_1 = 0x19, + WM8904_AUDIO_IF_2 = 0x1A, + WM8904_AUDIO_IF_3 = 0x1B, + WM8904_DAC_DIG_1 = 0x21, + WM8904_DAC_DIG_0 = 0x27, + WM8904_ANALOG_LEFT_IN_0 = 0x2C, + WM8904_ANALOG_RIGHT_IN_0 = 0x2D, + WM8904_ANALOG_LEFT_IN_1 = 0x2E, + WM8904_ANALOG_RIGHT_IN_1 = 0x2F, + WM8904_ANALOG_OUT1_LEFT = 0x39, + WM8904_ANALOG_OUT1_RIGHT = 0x3A, + WM8904_ANALOG_OUT12_ZC = 0x3D, + WM8904_DC_SERVO_0 = 0x43, + WM8904_ANALOG_HP_0 = 0x5A, + WM8904_CHRG_PUMP_0 = 0x62, + WM8904_CLS_W_0 = 0x68, + WM8904_WRT_SEQUENCER_0 = 0x6C, + WM8904_WRT_SEQUENCER_3 = 0x6F, + WM8904_WRT_SEQUENCER_4 = 0x70, + WM8904_DAC_DIGITAL_VOLUME_LEFT = 0x1E, + WM8904_DAC_DIGITAL_VOLUME_RIGHT = 0x1F, + WM8904_ADC_DIGITAL_VOLUME_LEFT = 0x24, + WM8904_ADC_DIGITAL_VOLUME_RIGHT = 0x25, + WM8904_ANALOG_OUT2_LEFT = 0x3B, + WM8904_ANALOG_OUT2_RIGHT = 0x3C, + /* FLL control register */ + WM8904_FLL_CONTROL_1 = 0x74, + WM8904_FLL_CONTROL_2 = 0x75, + WM8904_FLL_CONTROL_3 = 0x76, + WM8904_FLL_CONTROL_4 = 0x77, + WM8904_FLL_CONTROL_5 = 0x78, + /* GPIO control register */ + WM8904_GPIO_CONTROL_1 = 0x79, + WM8904_GPIO_CONTROL_2 = 0x7A, + WM8904_GPIO_CONTROL_3 = 0x7B, + WM8904_GPIO_CONTROL_4 = 0x7C, + /* FLL nco */ + WM89004_FLL_NCO_TEST_0 = 0xF7, + WM89004_FLL_NCO_TEST_1 = 0xF8, +}wm8904_reg_t; + +#define WM8904_LRC_POLARITY_POS (4U) +#define WM8904_LRC_POLARITY_NOOMAL (0) +#define WM8904_LRC_POLARITY_INVERTED (1U << WM8904_LRC_POLARITY_POS) + +typedef enum _wm8904_module +{ + WM8904_MODULE_ADC = 0, /*!< module ADC */ + WM8904_MODULE_DAC, /*!< module DAC */ + WM8904_MODULE_PGA, /*!< module PGA */ + WM8904_MODULE_HEADPHONE, /*!< module headphone */ + WM8904_MODULE_LINEOUT, /*!< module line out */ +}wm8904_module_t; + +enum +{ + WM8904_HEADPHONE_LEFT = 1U, + WM8904_HEADPHONE_RIGHT = 2U, + WM8904_LINEOUT_LEFT = 4U, + WM8904_LINEOUT_RIGHT = 8U, +}; + +typedef enum _wm8904_timeslot{ + WM8904_TIMESLOT_0 = 0U, + WM8904_TIMESLOT_1, +}wm8904_timeslot_t; + +typedef enum +{ + WM8904_PROTOCOL_RIGHT_JUSTIFIED = 0x00, + WM8904_PROTOCOL_LEFT_JUSTIFIED = 0x01, + WM8904_PROTOCOL_I2S = 0x02, + WM8904_PROTOCOL_PCMA = 0x03, + WM8904_PROTOCOL_PCMB = 0x13, +}wm8904_protocol_t; + +/*! @brief The SYSCLK / fs ratio. */ +typedef enum _wm8904_fs_ratio +{ + WM8904_FSRATIO_64X = 0x0, /*!< SYSCLK is 64 * sample rate * frame width */ + WM8904_FSRATIO_128X = 0x1, /*!< SYSCLK is 128 * sample rate * frame width */ + WM8904_FSRATIO_192X = 0x2, /*!< SYSCLK is 192 * sample rate * frame width */ + WM8904_FSRATIO_256X = 0x3, /*!< SYSCLK is 256 * sample rate * frame width */ + WM8904_FSRATIO_384X = 0x4, /*!< SYSCLK is 384 * sample rate * frame width */ + WM8904_FSRATIO_512X = 0x5, /*!< SYSCLK is 512 * sample rate * frame width */ + WM8904_FSRATIO_768X = 0x6, /*!< SYSCLK is 768 * sample rate * frame width */ + WM8904_FSRATIO_1024X = 0x7, /*!< SYSCLK is 1024 * sample rate * frame width */ + WM8904_FSRATIO_1408X = 0x8, /*!< SYSCLK is 1408 * sample rate * frame width */ + WM8904_FSRATIO_1536X = 0x9 /*!< SYSCLK is 1536 * sample rate * frame width */ +} wm8904_fs_ratio_t; + +/*! @brief Sample rate. */ +typedef enum _wm8904_sample_rate +{ + WM8904_SAMPLERATE_8kHz = 0x0, /*!< 8 kHz */ + WM8904_SAMPLERATE_12kHz = 0x1, /*!< 12kHz */ + WM8904_SAMPLERATE_16kHz = 0x2, /*!< 16kHz */ + WM8904_SAMPLERATE_24kHz = 0x3, /*!< 24kHz */ + WM8904_SAMPLERATE_32kHz = 0x4, /*!< 32kHz */ + WM8904_SAMPLERATE_48kHz = 0x5, /*!< 48kHz */ + WM8904_SAMPLERATE_11025Hz = 0x6, /*!< 11.025kHz */ + WM8904_SAMPLERATE_22050Hz = 0x7, /*!< 22.05kHz */ + WM8904_SAMPLERATE_44100Hz = 0x8 /*!< 44.1kHz */ +} wm8904_sample_rate_t; + +/*! @brief Bit width. */ +typedef enum _wm8904_bit_width +{ + WM8904_BITWIDTH_16 = 0x0, /*!< 16 bits */ + WM8904_BITWIDTH_20 = 0x1, /*!< 20 bits */ + WM8904_BITWIDTH_24 = 0x2, /*!< 24 bits */ + WM8904_BITWIDTH_32 = 0x3 /*!< 32 bits */ +} wm8904_bit_width_t; + +enum +{ + WM8904_RECORD_SOURCE_DIFFERENTIAL_LINE = 1U, /*!< record source from differential line */ + WM8904_RECORD_SOURCE_LINE_INPUT = 2U, /*!< record source from line input */ + WM8904_RECORD_SOURCE_DIFFERENTIAL_MIC = 4U, /*!< record source from differential mic */ + WM8904_RECORD_SOURCE_DIGITAL_MIC = 8U, /*!< record source from digital microphone */ +}; + +enum +{ + WM8904_RECORD_CHANNEL_LEFT1 = 1U, /*!< left record channel 1 */ + WM8904_RECORD_CHANNEL_LEFT2 = 2U, /*!< left record channel 2 */ + WM8904_RECORD_CHANNEL_LEFT3 = 4U, /*!< left record channel 3 */ + WM8904_RECORD_CHANNEL_RIGHT1 = 1U, /*!< right record channel 1 */ + WM8904_RECORD_CHANNEL_RIGHT2 = 2U, /*!< right record channel 2 */ + WM8904_RECORD_CHANNEL_RIGHT3 = 4U, /*!< right record channel 3 */ + WM8904_RECORD_CHANNEL_DIFFERENTIAL_POSITIVE1 = 1U, /*!< differential positive record channel 1 */ + WM8904_RECORD_CHANNEL_DIFFERENTIAL_POSITIVE2 = 2U, /*!< differential positive record channel 2 */ + WM8904_RECORD_CHANNEL_DIFFERENTIAL_POSITIVE3 = 4U, /*!< differential positive record channel 3 */ + WM8904_RECORD_CHANNEL_DIFFERENTIAL_NEGATIVE1 = 8U, /*!< differential negative record channel 1 */ + WM8904_RECORD_CHANNEL_DIFFERENTIAL_NEGATIVE2 = 16U, /*!< differential negative record channel 2 */ + WM8904_RECORD_CHANNEL_DIFFERENTIAL_NEGATIVE3 = 32U, /*!< differential negative record channel 3 */ +}; + +/*! @brief wm8904 play source + * + */ +enum +{ + WM8904_PLAY_SOURCE_PGA = 1U, /*!< play source PGA, bypass ADC */ + WM8904_PLAY_SOURCE_DAC = 4U, /*!< play source Input3 */ +}; + +/*! @brief wm8904_fll_clk_source */ +typedef enum _wm8904_fll_clk_source +{ + WM8904_FLL_CLK_SOURCE_MCLK = 0U, /**< wm8904 FLL clock source from MCLK */ +}wm8904_fll_clk_source_t; + +/*! @brief wm8904 fll configuration */ +typedef struct _wm8904_fll_config +{ + wm8904_fll_clk_source_t source; /*!< fll reference clock source */ + rt_uint32_t ref_clock_hz; /*!< fll reference clock frequency */ + rt_uint32_t output_clock_hz; /*!< fll output clock frequency */ +}wm8904_fll_config_t; + +/*! @brief Audio format configuration. */ +typedef struct _wm8904_audio_format +{ + wm8904_fs_ratio_t fsRatio; /*!< SYSCLK / fs ratio */ + wm8904_sample_rate_t sampleRate; /*!< Sample rate */ + wm8904_bit_width_t bitWidth; /*!< Bit width */ +} wm8904_audio_format_t; + +struct wm8904_config +{ + const char *i2c_bus_name; + const char *i2s_bus_name; + int i2c_addr; + wm8904_protocol_t protocol; + wm8904_audio_format_t format; +}; + +#define WM8904_I2C_ADDRESS (0x1A) +#define WM8904_I2C_BITRATE (400000U) + +/* WM8904 maximum volume */ +#define WM8904_MAP_HEADPHONE_LINEOUT_MAX_VOLUME 0x3FU +#define WM8904_DAC_MAX_VOLUME 0xC0U + +#endif +