浏览代码

[bsp][lpc55sxx] add i2s and wm8904 driver

vandoul 2 年之前
父节点
当前提交
0822f02606

+ 6 - 0
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)

+ 301 - 0
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 <rtthread.h>
+#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 <rtdevice.h>
+
+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
+

+ 43 - 0
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 <rtthread.h>
+
+#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
+

+ 1123 - 0
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 <rtthread.h>
+#include <drivers/audio.h>
+#include <drivers/i2c.h>
+#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 = &reg;
+    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 = &reg;
+    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, &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<sizeof(wm8904_init_list)/sizeof(wm8904_init_list[0]); i++)
+    {
+        ret = wm8904_write_register(dev, wm8904_init_list[i][0], wm8904_init_list[i][1]);
+        if(ret != RT_EOK)
+        {
+            return ret;
+        }
+    }
+
+    return ret;
+}
+
+rt_err_t wm8904_deinit(struct drv_sound_wm8904 *dev)
+{
+    return wm8904_write_register(dev, WM8904_RESET, 0x0000);
+}
+
+rt_err_t wm8904_set_master_clock(struct drv_sound_wm8904 *dev, rt_uint32_t sysclk, rt_uint32_t sample_rate, rt_uint32_t bit_width)
+{
+    rt_uint32_t bclk = sample_rate * bit_width * 2U;
+    rt_uint32_t bclk_div = 0U;
+    rt_uint16_t audio_interface = 0U;
+    rt_err_t err = RT_EOK;
+    rt_uint16_t sysclk_div = 0;
+
+    err = wm8904_read_register(dev, WM8904_CLK_RATES_0, &sysclk_div);
+    sysclk = sysclk >> (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,
+};

+ 214 - 0
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 <rtthread.h>
+
+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
+