/**
 * @file rk3588_nau88c22yg_audio.c
 * @brief RK3588平台 NAU88C22YG 音频Codec驱动完整分析
 * @author zhilin_tang Technology
 * 
 * 芯片型号:NAU88C22YG (Nuvoton)
 * 接口类型:I2S (数字音频) + I2C (控制)
 * 主要特性:24位立体声ADC/DAC,支持耳机输出、麦克风输入
 * 
 * 本分析涵盖:
 * 1. 硬件引脚连接与设备树配置
 * 2. ALSA/ASoC 框架架构树形分析
 * 3. NAU88C22YG 驱动源码树形分析
 * 4. 关键函数调用树形分析
 * 5. 调试工具与命令
 * 6. 常见问题排查
 */

一、硬件引脚连接分析

1.1 NAU88C22YG 与 RK3588 引脚连接

根据 RK3588 开发板原理图,NAU88C22YG 通过 I2S 接口传输音频数据,通过 I2C 接口进行寄存器控制。

【RK3588 ←→ NAU88C22YG 引脚连接表】
​
┌─────────────────┬─────────────────┬─────────────────┬─────────────────────────────┐
│ 信号名称        │ RK3588 引脚     │ NAU88C22YG 引脚 │ 功能描述                    │
├─────────────────┼─────────────────┼─────────────────┼─────────────────────────────┤
│ I2S 音频接口    │                 │                 │                             │
├─────────────────┼─────────────────┼─────────────────┼─────────────────────────────┤
│ I2S0_SCLK_TX    │ GPIO1_C3 (E31)  │ BCLK            │ 位时钟 (Bit Clock)          │
│ I2S0_LRCK_TX    │ GPIO1_C5 (D30)  │ LRCK            │ 左右声道时钟 (Frame Sync)   │
│ I2S0_SDO0       │ GPIO1_C7 (E29)  │ DACDAT          │ 播放数据 (PCM输出)          │
│ I2S0_SDI0       │ GPIO1_D4 (D28)  │ ADCDAT          │ 录音数据 (PCM输入)          │
│ I2S0_MCLK       │ GPIO1_C2 (F30)  │ MCLK            │ 主时钟 (12.288MHz/24.576MHz)│
├─────────────────┼─────────────────┼─────────────────┼─────────────────────────────┤
│ I2C 控制接口    │                 │                 │                             │
├─────────────────┼─────────────────┼─────────────────┼─────────────────────────────┤
│ I2C2_SCL_M0     │ GPIO0_B7 (T28)  │ SCL             │ I2C 时钟                    │
│ I2C2_SDA_M0     │ GPIO0_C0 (T31)  │ SDA             │ I2C 数据                    │
├─────────────────┼─────────────────┼─────────────────┼─────────────────────────────┤
│ 电源/控制信号   │                 │                 │                             │
├─────────────────┼─────────────────┼─────────────────┼─────────────────────────────┤
│ AUDIO_RST       │ GPIO4_C6        │ RST_N           │ 复位 (低有效)               │
│ AUDIO_HP_DET    │ GPIO4_C7        │ HPD             │ 耳机插入检测                │
│ VDD33           │ VCC3V3_SYS      │ AVDD            │ 模拟电源 3.3V               │
│ VDD18           │ VCC1V8_SYS      │ DVDD            │ 数字电源 1.8V               │
│ GND             │ GND             │ GND             │ 地                         │
└─────────────────┴─────────────────┴─────────────────┴─────────────────────────────┘

1.2 时钟频率配置

【音频时钟树】
​
┌─────────────────────────────────────────────────────────────────────────────┐
│                           RK3588 CRU (时钟管理单元)                         │
│                                                                             │
│   ┌─────────────────────────────────────────────────────────────────────┐   │
│   │                    PLL (锁相环)                                     │   │
│   │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐                │   │
│   │  │ CPLL (1.2G) │  │ GPLL (1.2G) │  │ PPLL (1.1G) │                │   │
│   │  └──────┬──────┘  └──────┬──────┘  └──────┬──────┘                │   │
│   │         │                │                │                         │   │
│   │         └────────────────┼────────────────┘                         │   │
│   │                          ↓                                          │   │
│   │              ┌───────────────────────┐                              │   │
│   │              │     MCLK 分频器        │                              │   │
│   │              │  CLK_I2S0_8CH_TX      │                              │   │
│   │              └───────────┬───────────┘                              │   │
│   │                          ↓                                          │   │
│   │              ┌───────────────────────┐                              │   │
│   │              │   assigned-clock-rate │                              │   │
│   │              │     12288000 Hz       │  ← 12.288MHz                │   │
│   │              └───────────────────────┘                              │   │
│   └─────────────────────────────────────────────────────────────────────┘   │
│                                       ↓                                      │
│   ┌─────────────────────────────────────────────────────────────────────┐   │
│   │                    I2S0 控制器                                      │   │
│   │  MCLK (12.288MHz) → BCLK (3.072MHz) → LRCK (48kHz)                │   │
│   │  采样率: 48kHz                                                     │   │
│   │  位宽: 16bit/24bit/32bit                                           │   │
│   │  MCLK/BCLK = 256 倍关系                                             │   │
│   └─────────────────────────────────────────────────────────────────────┘   │
│                                       ↓                                      │
│   ┌─────────────────────────────────────────────────────────────────────┐   │
│   │                    NAU88C22YG Codec                                 │   │
│   │  MCLK: 12.288MHz (256fs)                                           │   │
│   │  BCLK: 3.072MHz (64fs)                                             │   │
│   │  LRCK: 48kHz (fs)                                                  │   │
│   └─────────────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────────────┘

二、设备树完整配置

2.1 设备树文件 rk3588-nau88c22yg.dtsi

/**
 * @file rk3588-nau88c22yg.dtsi
 * @brief RK3588 NAU88C22YG 音频 Codec 设备树配置
 * @author zhilin_tang Technology
 * 
 * @note 设计模式分析:采用"适配器模式(Adapter Pattern)"
 *       将 NAU88C22YG 硬件接口适配为 ASoC 标准框架,
 *       通过 Machine 驱动连接 Platform (I2S) 和 Codec。
 */
​
/dts-v1/;
/plugin/;
​
/ {
    compatible = "rockchip,rk3588";
    
    /* ========== 1. 音频时钟定义 ========== */
    clk_audio: clk-audio {
        compatible = "fixed-clock";
        #clock-cells = <0>;
        clock-frequency = <12288000>;   /* 12.288MHz MCLK */
    };
    
    /* ========== 2. 音频电源定义 ========== */
    vcc_audio_33: vcc-audio-33 {
        compatible = "regulator-fixed";
        regulator-name = "vcc_audio_33";
        regulator-min-microvolt = <3300000>;
        regulator-max-microvolt = <3300000>;
        regulator-always-on;
    };
    
    vcc_audio_18: vcc-audio-18 {
        compatible = "regulator-fixed";
        regulator-name = "vcc_audio_18";
        regulator-min-microvolt = <1800000>;
        regulator-max-microvolt = <1800000>;
        regulator-always-on;
    };
};
​
/* ========== 3. I2C 控制器配置 (连接 NAU88C22YG 控制接口) ========== */
&i2c2 {
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&i2c2m0_xfer>;
    clock-frequency = <400000>;
    
    nau88c22: codec@1a {
        compatible = "nuvoton,nau88c22";
        reg = <0x1a>;                       /* I2C 地址 0x1A */
        
        /* 电源供应 */
        AVDD-supply = <&vcc_audio_33>;
        DVDD-supply = <&vcc_audio_18>;
        
        /* GPIO 控制 */
        reset-gpios = <&gpio4 RK_PC6 GPIO_ACTIVE_LOW>;
        hp-det-gpios = <&gpio4 RK_PC7 GPIO_ACTIVE_HIGH>;
        
        /* 时钟配置 */
        clocks = <&clk_audio>;
        clock-names = "mclk";
        
        /* 配置选项 */
        nuvoton,loopback-gpio = <&gpio4 RK_PC6 GPIO_ACTIVE_HIGH>;
        
        status = "okay";
    };
};
​
/* ========== 4. I2S0 控制器配置 (音频数据传输) ========== */
&i2s0_8ch {
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&i2s0m0_lrck
                 &i2s0m0_sclk
                 &i2s0m0_sdi0
                 &i2s0m0_sdo0>;
    
    /* 时钟配置 */
    assigned-clocks = <&cru CLK_I2S0_8CH_TX>;
    assigned-clock-rates = <12288000>;   /* 12.288MHz */
    
    rockchip,trcm-sync-tx-only;          /* 仅发送模式同步 */
    rockchip,clk-trcm = <&i2s0_8ch>;
    
    /* 捕获和播放都启用 */
    rockchip,playback-channels = <2>;
    rockchip,capture-channels = <2>;
    
    status = "okay";
};
​
/* ========== 5. Machine 驱动配置 (连接 Platform 和 Codec) ========== */
&sound {
    status = "okay";
    compatible = "rockchip,multicodecs-card";
    rockchip,card-name = "rockchip-nau88c22";
    rockchip,format = "i2s";
    rockchip,mclk-fs = <256>;
    rockchip,cpu = <&i2s0_8ch>;
    rockchip,codec = <&nau88c22>;
    
    /* 音频通路配置 */
    rockchip,audio-routing =
        "Headphone", "HPOL",
        "Headphone", "HPOR",
        "IN1L", "Line In",
        "IN1R", "Line In",
        "IN2L", "Mic In",
        "IN2R", "Mic In";
    
    /* 播放和捕获 PCM 设备 */
    rockchip,playback-only;
    // rockchip,capture-only;  /* 如需仅录音,取消注释 */
    
    status = "okay";
};
​
/* ========== 6. 引脚复用配置 ========== */
&pinctrl {
    i2s0 {
        i2s0m0_lrck: i2s0m0-lrck {
            rockchip,pins = <1 RK_PC5 RK_FUNC_1 &pcfg_pull_none>;
        };
        i2s0m0_sclk: i2s0m0-sclk {
            rockchip,pins = <1 RK_PC3 RK_FUNC_1 &pcfg_pull_none>;
        };
        i2s0m0_sdi0: i2s0m0-sdi0 {
            rockchip,pins = <1 RK_PD4 RK_FUNC_1 &pcfg_pull_none>;
        };
        i2s0m0_sdo0: i2s0m0-sdo0 {
            rockchip,pins = <1 RK_PC7 RK_FUNC_1 &pcfg_pull_none>;
        };
        i2s0m0_mclk: i2s0m0-mclk {
            rockchip,pins = <1 RK_PC2 RK_FUNC_1 &pcfg_pull_none>;
        };
    };
    
    audio_pins {
        audio_reset_pin: audio-reset-pin {
            rockchip,pins = <4 RK_PC6 RK_FUNC_GPIO &pcfg_pull_up>;
        };
        audio_hp_det_pin: audio-hp-det-pin {
            rockchip,pins = <4 RK_PC7 RK_FUNC_GPIO &pcfg_pull_up>;
        };
    };
};

三、ALSA/ASoC 框架架构树形分析

3.1 ALSA 子系统整体架构

【Linux ALSA 子系统架构树】
​
┌─────────────────────────────────────────────────────────────────────────────┐
│                         用户空间 (User Space)                               │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                    音频应用 (Audio Applications)                     │   │
│  │  ┌──────────────┐ ┌──────────────┐ ┌──────────────┐                │   │
│  │  │   aplay      │ │   arecord    │ │   amixer     │                │   │
│  │  │  (播放)      │ │  (录音)      │ │  (控制)      │                │   │
│  │  └──────────────┘ └──────────────┘ └──────────────┘                │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                      ↓                                      │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                    ALSA 用户空间库 (libasound)                       │   │
│  │                    - 设备节点操作 (/dev/snd/*)                       │   │
│  │                    - PCM 设备管理                                    │   │
│  │                    - 控制接口封装                                    │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────────────┘
                                      ↓ (系统调用)
┌─────────────────────────────────────────────────────────────────────────────┐
│                         内核空间 (Kernel Space)                             │
│                                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                    ALSA 核心层 (ALSA Core)                          │   │
│  │  ┌──────────────────────────────────────────────────────────────┐  │   │
│  │  │  sound/core/                                                 │  │   │
│  │  │  ├── pcm.c           # PCM 设备管理                          │  │   │
│  │  │  ├── control.c       # 控制接口 (mixer)                      │  │   │
│  │  │  ├── timer.c         # 定时器管理                            │  │   │
│  │  │  ├── device.c        # 设备管理                              │  │   │
│  │  │  └── sound.c         # 核心初始化                            │  │   │
│  │  └──────────────────────────────────────────────────────────────┘  │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                      ↓                                      │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                    ASoC 框架层 (ALSA SoC)                           │   │
│  │                                                                      │   │
│  │  ┌─────────────────────────────────────────────────────────────┐   │   │
│  │  │  sound/soc/soc-core.c          # ASoC 核心                   │   │   │
│  │  │  sound/soc/soc-pcm.c           # PCM 操作                   │   │   │
│  │  │  sound/soc/soc-dapm.c          # 动态音频电源管理            │   │   │
│  │  │  sound/soc/soc-jack.c          # 耳机插孔检测                │   │   │
│  │  └─────────────────────────────────────────────────────────────┘   │   │
│  │                                                                      │   │
│  │  ┌─────────────────────────────────────────────────────────────┐   │   │
│  │  │              Machine 驱动 (板级适配)                         │   │   │
│  │  │  sound/soc/rockchip/rockchip_multicodecs.c                   │   │   │
│  │  │  - 连接 Platform 和 Codec                                     │   │   │
│  │  │  - 定义音频路由 (Audio Routing)                               │   │   │
│  │  │  - 注册声卡设备                                               │   │   │
│  │  └─────────────────────────────────────────────────────────────┘   │   │
│  │                                                                      │   │
│  │  ┌─────────────────────────────────────────────────────────────┐   │   │
│  │  │           Platform 驱动 (SoC 数字音频接口)                   │   │   │
│  │  │  sound/soc/rockchip/rockchip_i2s_tdm.c                       │   │   │
│  │  │  - I2S/TDM 控制器驱动                                        │   │   │
│  │  │  - DMA 管理                                                  │   │   │
│  │  │  - 时钟配置                                                  │   │   │
│  │  └─────────────────────────────────────────────────────────────┘   │   │
│  │                                                                      │   │
│  │  ┌─────────────────────────────────────────────────────────────┐   │   │
│  │  │            Codec 驱动 (音频编解码芯片)                       │   │   │
│  │  │  sound/soc/codecs/nau88c22.c                                 │   │   │
│  │  │  - 寄存器读写 (I2C/SPI)                                      │   │   │
│  │  │  - DAC/ADC 控制                                              │   │   │
│  │  │  - 音量/通路控制                                             │   │   │
│  │  └─────────────────────────────────────────────────────────────┘   │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                      ↓                                      │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                    硬件层 (Hardware)                                 │   │
│  │  ┌─────────────────────────────────────────────────────────────┐   │   │
│  │  │  RK3588 I2S 控制器                                          │   │   │
│  │  │  DMA 引擎                                                    │   │   │
│  │  └─────────────────────────────────────────────────────────────┘   │   │
│  │                              ↓                                      │   │
│  │  ┌─────────────────────────────────────────────────────────────┐   │   │
│  │  │  NAU88C22YG Codec                                            │   │   │
│  │  │  - DAC (数模转换)                                            │   │   │
│  │  │  - ADC (模数转换)                                            │   │   │
│  │  │  - 耳机放大器                                                │   │   │
│  │  └─────────────────────────────────────────────────────────────┘   │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────────────┘

四、NAU88C22YG 驱动源码树形分析

4.1 驱动文件目录树

【NAU88C22YG 驱动源码目录树】
​
sound/soc/codecs/
│
├── nau88c22.c                        # NAU88C22YG 核心驱动 ★★★
├── nau88c22.h                        # NAU88C22YG 头文件
├── Kconfig                           # 内核配置选项
├── Makefile                          # 编译规则
│
└── nau88c22_regs.h                   # 寄存器定义文件
​
【驱动内部结构树】
​
nau88c22.c
│
├── 1. 寄存器定义区
│   ├── NAU88C22_REG_POWER1          # 电源控制寄存器1
│   ├── NAU88C22_REG_POWER2          # 电源控制寄存器2
│   ├── NAU88C22_REG_AUDIO_IF        # 音频接口控制
│   ├── NAU88C22_REG_SAMPLE_RATE     # 采样率控制
│   ├── NAU88C22_REG_DAC_VOL         # DAC 音量寄存器
│   ├── NAU88C22_REG_ADC_VOL         # ADC 音量寄存器
│   └── ... (约 50+ 寄存器定义)
│
├── 2. 私有数据结构体
│   ├── struct nau88c22_priv          # 驱动私有数据
│   │   ├── struct regmap *regmap     # I2C 寄存器映射
│   │   ├── struct snd_soc_component *component
│   │   ├── struct clk *mclk          # MCLK 时钟
│   │   ├── int sysclk                # 系统时钟频率
│   │   ├── int sysclk_src            # 时钟源
│   │   └── struct gpio_desc *reset_gpio  # 复位 GPIO
│   └── struct nau88c22_platform_data  # 平台数据
│
├── 3. 寄存器读写函数
│   ├── nau88c22_write()              # 写寄存器
│   ├── nau88c22_read()               # 读寄存器
│   └── nau88c22_update_bits()        # 更新寄存器位
│
├── 4. DAPM 控件 (动态音频电源管理)
│   ├── nau88c22_dapm_widgets[]       # DAPM 控件数组
│   │   ├── "Headphone"               # 耳机输出
│   │   ├── "Speaker"                 # 扬声器输出
│   │   ├── "Mic"                     # 麦克风输入
│   │   ├── "Line In"                 # 线性输入
│   │   └── "DAC" / "ADC"             # 数模/模数转换
│   │
│   ├── nau88c22_dapm_routes[]        # DAPM 路由表
│   │   ├── "Headphone" ← "HPOL"     # 左声道路由
│   │   ├── "Headphone" ← "HPOR"     # 右声道路由
│   │   ├── "DAC" → "Headphone"       # DAC 到耳机
│   │   └── "Mic" → "ADC"             # 麦克风到 ADC
│   │
│   └── nau88c22_dapm_events[]        # DAPM 事件 (上电/下电)
│
├── 5. 控件函数 (mixer 控制)
│   ├── nau88c22_dac_vol_control()    # DAC 音量控制
│   ├── nau88c22_adc_vol_control()    # ADC 音量控制
│   ├── nau88c22_mic_gain_control()   # 麦克风增益控制
│   ├── nau88c22_playback_switch()    # 播放开关
│   └── nau88c22_capture_switch()     # 录音开关
│
├── 6. DAI 操作函数 (数字音频接口)
│   ├── nau88c22_set_dai_fmt()        # 设置 I2S 格式
│   ├── nau88c22_set_dai_sysclk()     # 设置系统时钟
│   ├── nau88c22_hw_params()          # 硬件参数设置 ★
│   ├── nau88c22_mute_stream()        # 静音控制
│   ├── nau88c22_startup()            # 启动 DAI
│   └── nau88c22_shutdown()           # 关闭 DAI
│
├── 7. Codec 驱动结构体
│   ├── struct snd_soc_dai_driver nau88c22_dai[]  # DAI 驱动
│   │   ├── .name = "nau88c22-hifi"
│   │   ├── .playback = { ... }       # 播放参数
│   │   ├── .capture = { ... }        # 录音参数
│   │   └── .ops = &nau88c22_dai_ops  # DAI 操作函数
│   │
│   └── struct snd_soc_component_driver nau88c22_component_driver
│       ├── .name = "nau88c22"
│       ├── .probe = nau88c22_probe   # 组件探测
│       ├── .remove = nau88c22_remove # 组件移除
│       ├── .controls = nau88c22_controls[]
│       ├── .num_controls = ARRAY_SIZE(nau88c22_controls)
│       ├── .dapm_widgets = nau88c22_dapm_widgets
│       ├── .num_dapm_widgets = ARRAY_SIZE(nau88c22_dapm_widgets)
│       └── .dapm_routes = nau88c22_dapm_routes
│
├── 8. I2C 驱动接口
│   ├── nau88c22_i2c_probe()          # I2C 设备探测
│   ├── nau88c22_i2c_remove()         # I2C 设备移除
│   ├── nau88c22_of_match[]           # 设备树匹配表
│   └── struct i2c_driver nau88c22_i2c_driver
│
└── 9. 模块初始化
    ├── module_i2c_driver(nau88c22_i2c_driver)  # 注册 I2C 驱动
    └── MODULE_DEVICE_TABLE(i2c, nau88c22_i2c_id)

五、关键函数树形分析

5.1 驱动 Probe 函数调用树

/**
 * @brief NAU88C22YG 驱动探测函数
 * @param i2c I2C 设备指针
 * @return 0 成功,负数错误码
 * 
 * @note 设计模式分析:采用"建造者模式(Builder Pattern)"
 *       逐步构建驱动私有数据结构,完成资源分配、硬件初始化、
 *       控件注册、DAPM 路由建立等步骤。
 */
static int nau88c22_i2c_probe(struct i2c_client *i2c)
{
    struct device *dev = &i2c->dev;
    struct nau88c22_priv *nau88c22;
    int ret;
    
    /* ========== 步骤1:分配私有数据 ========== */
    nau88c22 = devm_kzalloc(dev, sizeof(struct nau88c22_priv), GFP_KERNEL);
    if (!nau88c22)
        return -ENOMEM;
    
    /* ========== 步骤2:初始化 I2C 寄存器映射 ========== */
    nau88c22->regmap = devm_regmap_init_i2c(i2c, &nau88c22_regmap_config);
    if (IS_ERR(nau88c22->regmap))
        return PTR_ERR(nau88c22->regmap);
    
    /* ========== 步骤3:获取 MCLK 时钟 ========== */
    nau88c22->mclk = devm_clk_get(dev, "mclk");
    if (IS_ERR(nau88c22->mclk)) {
        dev_err(dev, "Failed to get MCLK\n");
        return PTR_ERR(nau88c22->mclk);
    }
    clk_prepare_enable(nau88c22->mclk);
    
    /* ========== 步骤4:获取复位 GPIO ========== */
    nau88c22->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
    if (IS_ERR(nau88c22->reset_gpio)) {
        ret = PTR_ERR(nau88c22->reset_gpio);
        goto err_clk;
    }
    
    /* ========== 步骤5:硬件复位 ========== */
    gpiod_set_value_cansleep(nau88c22->reset_gpio, 0);
    msleep(20);
    gpiod_set_value_cansleep(nau88c22->reset_gpio, 1);
    msleep(50);
    
    /* ========== 步骤6:寄存器初始化 ========== */
    nau88c22_write(nau88c22, NAU88C22_REG_POWER1, 0x00);
    nau88c22_write(nau88c22, NAU88C22_REG_POWER2, 0x00);
    nau88c22_write(nau88c22, NAU88C22_REG_AUDIO_IF, 0x50);  /* I2S, 24-bit */
    
    /* ========== 步骤7:注册 ASoC 组件 ========== */
    nau88c22->component = devm_snd_soc_register_component(dev,
                            &nau88c22_component_driver,
                            nau88c22_dai,
                            ARRAY_SIZE(nau88c22_dai));
    if (IS_ERR(nau88c22->component)) {
        ret = PTR_ERR(nau88c22->component);
        goto err_clk;
    }
    
    /* ========== 步骤8:设置 I2C 客户端数据 ========== */
    i2c_set_clientdata(i2c, nau88c22);
    
    dev_info(dev, "NAU88C22YG Codec registered successfully\n");
    return 0;
    
err_clk:
    clk_disable_unprepare(nau88c22->mclk);
    return ret;
}

5.2 DAI 硬件参数设置函数

/**
 * @brief 硬件参数设置函数
 * @param dai DAI 设备
 * @param substream PCM 子流
 * @param params 硬件参数
 * @return 0 成功,负数错误码
 * 
 * @note 设计模式分析:采用"策略模式(Strategy Pattern)"
 *       根据不同的采样率、位宽、通道数选择不同的寄存器配置策略。
 * 
 * @remark 性能分析:
 *         - 播放路径:DMA → I2S FIFO → Codec DAC → 输出
 *         - 延迟:约 5-10ms (取决于缓冲区大小)
 */
static int nau88c22_hw_params(struct snd_pcm_substream *substream,
                               struct snd_pcm_hw_params *params,
                               struct snd_soc_dai *dai)
{
    struct snd_soc_component *component = dai->component;
    struct nau88c22_priv *nau88c22 = snd_soc_component_get_drvdata(component);
    unsigned int rate = params_rate(params);
    unsigned int width = params_width(params);
    unsigned int channels = params_channels(params);
    u32 val = 0;
    
    /* ========== 1. 配置采样率 ========== */
    switch (rate) {
    case 8000:
        val = NAU88C22_RATE_8K;
        break;
    case 16000:
        val = NAU88C22_RATE_16K;
        break;
    case 32000:
        val = NAU88C22_RATE_32K;
        break;
    case 44100:
        val = NAU88C22_RATE_44_1K;
        break;
    case 48000:
        val = NAU88C22_RATE_48K;
        break;
    case 96000:
        val = NAU88C22_RATE_96K;
        break;
    default:
        dev_err(component->dev, "Unsupported rate: %d\n", rate);
        return -EINVAL;
    }
    nau88c22_update_bits(nau88c22, NAU88C22_REG_SAMPLE_RATE,
                         NAU88C22_RATE_MASK, val);
    
    /* ========== 2. 配置数据位宽 ========== */
    switch (width) {
    case 16:
        val = NAU88C22_WL_16BIT;
        break;
    case 24:
        val = NAU88C22_WL_24BIT;
        break;
    case 32:
        val = NAU88C22_WL_32BIT;
        break;
    default:
        dev_err(component->dev, "Unsupported width: %d\n", width);
        return -EINVAL;
    }
    nau88c22_update_bits(nau88c22, NAU88C22_REG_AUDIO_IF,
                         NAU88C22_WL_MASK, val);
    
    /* ========== 3. 配置通道数 ========== */
    if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
        /* 播放通道配置 */
        if (channels == 2)
            nau88c22_update_bits(nau88c22, NAU88C22_REG_DAC_CTRL,
                                 NAU88C22_DAC_MONO, 0);
        else
            nau88c22_update_bits(nau88c22, NAU88C22_REG_DAC_CTRL,
                                 NAU88C22_DAC_MONO, NAU88C22_DAC_MONO);
    } else {
        /* 录音通道配置 */
        if (channels == 2)
            nau88c22_update_bits(nau88c22, NAU88C22_REG_ADC_CTRL,
                                 NAU88C22_ADC_MONO, 0);
        else
            nau88c22_update_bits(nau88c22, NAU88C22_REG_ADC_CTRL,
                                 NAU88C22_ADC_MONO, NAU88C22_ADC_MONO);
    }
    
    return 0;
}

5.3 DAPM 通路控制

/**
 * @brief DAPM 控件定义
 * 
 * @note 设计模式分析:采用"状态模式(State Pattern)"
 *       音频通路的状态变化通过 DAPM 自动管理,
 *       根据用户配置动态打开/关闭相应硬件模块。
 */
static const struct snd_soc_dapm_widget nau88c22_dapm_widgets[] = {
    /* 输入输出接口 */
    SND_SOC_DAPM_OUTPUT("HPOL"),
    SND_SOC_DAPM_OUTPUT("HPOR"),
    SND_SOC_DAPM_OUTPUT("SPKOUT"),
    SND_SOC_DAPM_INPUT("MICIN"),
    SND_SOC_DAPM_INPUT("LINEIN"),
    
    /* DAC/ADC 转换器 */
    SND_SOC_DAPM_DAC("DAC L", "HiFi Playback", NAU88C22_REG_POWER2,
                     NAU88C22_DAC_L_EN_SHIFT, 0),
    SND_SOC_DAPM_DAC("DAC R", "HiFi Playback", NAU88C22_REG_POWER2,
                     NAU88C22_DAC_R_EN_SHIFT, 0),
    SND_SOC_DAPM_ADC("ADC L", "HiFi Capture", NAU88C22_REG_POWER2,
                     NAU88C22_ADC_L_EN_SHIFT, 0),
    SND_SOC_DAPM_ADC("ADC R", "HiFi Capture", NAU88C22_REG_POWER2,
                     NAU88C22_ADC_R_EN_SHIFT, 0),
    
    /* 混音器 */
    SND_SOC_DAPM_MIXER("Output Mixer", NAU88C22_REG_POWER2,
                       NAU88C22_MIXER_EN_SHIFT, 0,
                       output_mixer_controls,
                       ARRAY_SIZE(output_mixer_controls)),
    
    /* 音量控制 */
    SND_SOC_DAPM_PGA("HP PGA", NAU88C22_REG_POWER2,
                     NAU88C22_HP_PGA_EN_SHIFT, 0, NULL, 0),
    SND_SOC_DAPM_PGA("MIC PGA", NAU88C22_REG_POWER1,
                     NAU88C22_MIC_PGA_EN_SHIFT, 0, NULL, 0),
};
​
/**
 * @brief DAPM 路由表
 */
static const struct snd_soc_dapm_route nau88c22_dapm_routes[] = {
    /* DAC 到输出混音器 */
    { "Output Mixer", "DAC L Switch", "DAC L" },
    { "Output Mixer", "DAC R Switch", "DAC R" },
    
    /* 混音器到 PGA */
    { "HP PGA", NULL, "Output Mixer" },
    
    /* PGA 到输出引脚 */
    { "HPOL", NULL, "HP PGA" },
    { "HPOR", NULL, "HP PGA" },
    
    /* 输入到 ADC */
    { "ADC L", NULL, "MIC PGA" },
    { "ADC R", NULL, "MIC PGA" },
    
    /* 麦克风输入到 PGA */
    { "MIC PGA", NULL, "MICIN" },
};

六、调试工具与命令树形分析

6.1 alsa-utils 工具树

【alsa-utils 调试工具树】
​
alsa-utils/
│
├── aplay                    # 播放工具
│   ├── aplay -l             # 列出播放设备
│   ├── aplay -L             # 列出所有 PCM 设备
│   ├── aplay -D hw:0,0 test.wav  # 指定设备播放
│   ├── aplay -d 10 test.wav       # 播放 10 秒
│   ├── aplay -f cd test.wav       # CD 格式播放
│   └── aplay -v test.wav          # 显示详细信息
│
├── arecord                   # 录音工具
│   ├── arecord -l           # 列出录音设备
│   ├── arecord -d 10 -f cd test.wav  # 录音 10 秒
│   ├── arecord -D hw:0,0 -f S16_LE -r 48000 test.wav
│   └── arecord -v test.wav          # 显示详细信息
│
├── amixer                    # 混音器控制
│   ├── amixer controls -c 0         # 列出控制项
│   ├── amixer contents -c 0         # 显示控制项详情
│   ├── amixer cget numid=1          # 获取控制值
│   ├── amixer cset numid=1 50       # 设置控制值
│   ├── amixer sset "Playback Volume" 80%
│   └── amixer sset "Capture Switch" on
│
├── alsamixer                 # 交互式混音器
│   ├── F1: 帮助
│   ├── F2: 系统信息
│   ├── F3: 播放控制
│   ├── F4: 录音控制
│   ├── F5: 所有控制
│   ├── ↑/↓: 调整音量
│   ├── ←/→: 切换控件
│   └── M: 静音开关
│
├── aplaymidi                 # MIDI 播放
├── arecordmidi               # MIDI 录音
├── alsaloop                  # 音频回环
│   └── alsaloop -C hw:0,0 -P hw:0,0  # 将录音回放到播放
│
└── speaker-test              # 扬声器测试
    ├── speaker-test -c 2 -t wav      # 双声道测试
    ├── speaker-test -c 2 -t sine     # 正弦波测试
    └── speaker-test -D hw:0,0 -c 2   # 指定设备测试

七、完整调试脚本

7.1 音频调试脚本

#!/bin/bash
# audio_debug.sh - NAU88C22YG 音频调试脚本
​
set -e
​
echo "========================================="
echo "RK3588 + NAU88C22YG 音频调试"
echo "========================================="
​
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[0m'
​
# 1. 检查 I2C 设备
echo -e "\n[1] 检查 I2C 设备..."
i2cdetect -y 2 | grep -E "1a|NAU88"
if [ $? -eq 0 ]; then
    echo -e "${GREEN}  ✅ NAU88C22YG I2C 设备已识别${NC}"
else
    echo -e "${RED}  ❌ NAU88C22YG I2C 设备未识别${NC}"
fi
​
# 2. 检查声卡设备
echo -e "\n[2] 检查声卡设备..."
cat /proc/asound/cards
echo ""
ls -la /dev/snd/
​
# 3. 检查 I2S 时钟
echo -e "\n[3] 检查 I2S 时钟..."
cat /sys/kernel/debug/clk/clk_summary | grep -E "i2s0|mclk"
​
# 4. 检查引脚复用
echo -e "\n[4] 检查 I2S 引脚复用..."
cat /sys/kernel/debug/pinctrl/pinctrl-rockchip-pinctrl/pinmux-pins | grep -E "i2s|I2S"
​
# 5. 配置音频通路 (使用 amixer)
echo -e "\n[5] 配置音频通路..."
​
# 获取声卡编号
CARD=$(aplay -l | grep nau88c22 | head -1 | awk '{print $2}' | tr -d ':')
if [ -z "$CARD" ]; then
    echo -e "${YELLOW}  未找到 NAU88C22YG 声卡,尝试使用 card 0${NC}"
    CARD=0
fi
echo "  使用声卡: card $CARD"
​
# 列出所有控制项
echo -e "\n${YELLOW}  控制项列表:${NC}"
amixer controls -c $CARD | head -10
​
# 播放通路配置
echo -e "\n${YELLOW}  配置播放通路:${NC}"
amixer -c $CARD sset "Playback Path" "HP" 2>/dev/null || true
amixer -c $CARD sset "Playback Volume" 200,200 2>/dev/null || true
amixer -c $CARD sset "Headphone Volume" 100% 2>/dev/null || true
​
# 录音通路配置
echo -e "\n${YELLOW}  配置录音通路:${NC}"
amixer -c $CARD sset "Capture MIC Path" "Main Mic" 2>/dev/null || true
amixer -c $CARD sset "Capture Volume" 200,200 2>/dev/null || true
amixer -c $CARD sset "Mic Boost" 20dB 2>/dev/null || true
​
# 6. 播放测试
echo -e "\n[6] 播放测试..."
echo -e "${YELLOW}  播放 1kHz 正弦波 (5秒)...${NC}"
speaker-test -D hw:$CARD,0 -c 2 -t sine -f 1000 -l 1 2>/dev/null &
TEST_PID=$!
sleep 5
kill $TEST_PID 2>/dev/null
​
# 7. 录音测试
echo -e "\n[7] 录音测试..."
echo -e "${YELLOW}  录音 5 秒...${NC}"
arecord -D hw:$CARD,0 -d 5 -f S16_LE -r 48000 -c 2 /tmp/test_rec.wav
if [ -f /tmp/test_rec.wav ]; then
    echo -e "${GREEN}  ✅ 录音成功: /tmp/test_rec.wav${NC}"
    ls -lh /tmp/test_rec.wav
else
    echo -e "${RED}  ❌ 录音失败${NC}"
fi
​
# 8. 查看音频统计
echo -e "\n[8] 音频统计..."
cat /proc/asound/card$CARD/pcm0p/sub0/status 2>/dev/null || echo "  无播放状态"
cat /proc/asound/card$CARD/pcm0c/sub0/status 2>/dev/null || echo "  无录音状态"
​
echo -e "\n========================================="
echo -e "${GREEN}调试完成${NC}"
echo "========================================="

八、常见问题排查

8.1 问题排查树

【音频问题排查决策树】
​
音频无声
    │
    ├─ 硬件检查
    │   ├─ 测量电源 (AVDD 3.3V, DVDD 1.8V)
    │   ├─ 测量 MCLK 时钟 (12.288MHz)
    │   ├─ 测量 I2C 通信 (SCL/SDA 波形)
    │   └─ 测量 I2S 信号 (BCLK, LRCK, DATA)
    │
    ├─ 驱动检查
    │   ├─ i2cdetect -y X 检查设备地址
    │   ├─ cat /proc/asound/cards 检查声卡
    │   └─ dmesg | grep nau88c22 查看内核日志
    │
    ├─ 通路检查
    │   ├─ amixer sget "Playback Path" 检查输出路径
    │   ├─ amixer sget "Playback Volume" 检查音量
    │   └─ amixer sget "Headphone Switch" 检查开关
    │
    └─ 软件检查
        ├─ aplay -l 检查设备存在
        ├─ speaker-test -c 2 测试扬声器
        └─ strace aplay test.wav 跟踪系统调用

8.2 常见问题速查表

问题现象 可能原因 排查命令 解决方案
I2C 无设备 电源/连线问题 i2cdetect -y 2 检查 3.3V/1.8V 电源,检查 I2C 上拉电阻
声卡未注册 设备树配置错误 dmesg \| grep asoc 检查设备树 compatible 和 reg 地址
播放无声 通路未打开 amixer sget "Playback Path" 设置为 "HP" 或 "Speaker"
录音无声 麦克风增益未开 amixer sget "Mic Boost" 设置增益 20dB
音量太小 DAC 音量低 amixer sget "Playback Volume" 调整到 200+ (0-252)
噪音/失真 时钟不匹配 cat /sys/kernel/debug/clk/clk_summary 检查 MCLK 频率 12.288MHz
左右声道反 I2S 配置错误 检查设备树 format 修改为 i2s 格式
耳机插入无检测 GPIO 中断问题 cat /proc/interrupts \| grep hp 检查设备树 hp-det-gpios 配置

九、总结

项目 内容
Codec 型号 NAU88C22YG (Nuvoton)
音频接口 I2S (数据) + I2C (控制)
采样率支持 8kHz ~ 96kHz
位宽支持 16/24/32 bit
驱动路径 sound/soc/codecs/nau88c22.c
设备树节点 i2c2 + i2s0_8ch + sound
调试工具 aplay, arecord, amixer, alsamixer
关键寄存器 POWER1, POWER2, AUDIO_IF, SAMPLE_RATE

关键配置要点

  1. 时钟配置:MCLK 必须为采样率的 256 倍 (12.288MHz @ 48kHz)

  2. I2C 地址:NAU88C22YG 地址为 0x1A (7位)

  3. 通路配置:播放需要设置 "Playback Path" 为 "HP" 或 "Speaker"

  4. 音量范围:DAC/ADC 音量寄存器范围 0-252,对应 -95dB ~ +0.37dB

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐