LXR | KVM | PM | Time | Interrupt | Systems Performance | Bootup Optimization

Linux音频(1):alsa架构和RK3588 PCM实例

Linux Sound Subsystem Documentation》对Kernel ALSA进行了详细介绍:Designs and Implementations介绍了ALSA的一些设计和实现;The ALSA Driver API分类介绍了ALSA API,然后Writing an ALSA Driver介绍了如何编写一个ALSA驱动,同时Advanced Linux Sound Architecture介绍了如何对驱动进行配置;对于嵌入式设备,ALSA SoC Layer给出了分层依据,以及不同层级驱动实现介绍。

 

 ALSA是Linux Kernel下用于管理音频设备和音频的软件架构,提供了标准接口,用于应用程序和硬件设备之间的音频通信,以及音频设备之间的通信。

整个Linux下ALSA相关软件架构可以分为两部分:

  • 用户空间
    • alsa-lib将对ALSA设备操作进行封装。
    • alsa-utils以及ALSA相关应用,对ALSA设备进行操作。
    • pulseaudio作为Daemon程序,对Card进行配置。
  • 内核空间
    • ALSA Core提供通用的Card、PCM、Control、DAI、DAPM等模块注册,以及调试接口。
    • ASoC驱动,包括:Codec驱动、Platform驱动、Machine驱动。

1 RK3588 Audio相关硬件架构

 RK3588 Audio相关硬件配置如下:

  • SoC
    • Audio Codec
    • VAD(Voice Activity Detect):在低功耗模式下,检测音频输入用于唤醒SoC。
    • I2S/SPDIF等接口。
    • 耳机插拔通过中断通知SoC。
  • Codec:外接Codec芯片,I2C进行控制,I2S收发数据。
  • Class D Amplifier:Speaker D类功放,通过GPIO进行开关。

 

通过I2C对Codec进行配置,数据通过I2S在Codec和SoC之间传输。

编写一个SoC ALSA解决方案的主要工作包括:

  • Platform(DAI I2S/PCM)驱动、Codec驱动、Machine驱动编写。
  • Platform/Codec/Machine相关的DAPM Widget、Route Path、Control编写。
  • pulseaudio的conf配置文件编写。
  • 音频效果调试、时序优化、Pop/Click声消除等。

2 ALSA内核配置

Device Drivers
  ->Sound card support
    ->Advanced Linux Sound Architecture
      Enable OSS Emulation
      PCM timer interface
      HR-timer backend support
      Dynamic device file minor numbers
        Max number of sound cards
      Sound Proc FS Support
        Verbose procfs contents
      Verbose printk--在printk里面显示printk对应的文件和行号等信息。
      Debug--通过/sys/module/snd/parameters/debug配置调试信息输出。
      Sequencer support
        Sequencer dummy client
        Use HR-timer as default sequencer timer
      Generic sound devices
      PCI sound devices
      HD-Audio
      Pre-allocated buffer size for HD-audio driver
      SPI sound devices
      USB sound devices
        USB Audio/MIDI driver--支持基于USB的音频设备。
      ALSA for SoC audio support
        ASoC support for Rockchip
          Rockchip Digital Loopback Driver
          Rockchip I2S Device Driver
          Rockchip I2S/TDM Device Driver
          Rockchip Multi-DAIS Device Driver
          Rockchip PDM Controller Driver
          Rockchip SAI Controller Driver
          Rockchip SPDIF Device Driver
          Rockchip SPDIFRX Device Driver
          Rockchip Voice Activity Detection Driver
          ASoC support for Rockchip boards using a MAX98090 codec
          ASoC support for Rockchip multicodecs
          ASoC support for Rockchip HDMI audio
          CODEC drivers
            Everest Semi ES8323 CODEC
          ASoC Simple sound card support

3 ALSA驱动文件

4 ALSA子系统

4.1 ASOC划分

ALSA SoC将嵌入式音频系统分为多种可复用的component驱动:

  • Codec类驱动:平台无关,包含Audio Control、Audio接口能力、Codec DAPM、Codec IO。
    • codec DAI和PCM描述。
    • codec IO控制,使用Regmap。
    • Mixer和audio control。
    • codec音频处理。
    • DAPM描述。
    • DAPM事件处理。
    • DAC数字静音处理。
  • Platform类驱动:Audio DMA、DAI、Audio DSP。
    • DAI驱动(AC97/I2S/PCM)
      • DAI描述。
      • DAI配置。
      • PCM描述。
      • sysclk配置。
      • suspend和resume。
    • DSP驱动
      • DAPM graph。
      • Mixer control。
      • DMA IO读写DSP buffer。
  • Machine类驱动:描述和绑定其他Component的胶水层,处理Machine相关Control和Machine层级的音频事件。

4.2 DAPM和Endpoint Widget

DAPM可以划分为4个电源域:Codec bias domain,Platform/Machine domain,Path domain,Stream domain。

4.3 子系统初始化

alsa_sound_init()注册Sound Card字符设备序号,并创建一系列procfs:

alsa_sound_init
  register_chrdev--注册CONFIG_SND_MAJOR类型字符设备,操作函数集为snd_fops。
  snd_info_init
    snd_info_create_entry--创建/proc/asound目录。
    create_subdir--创建/proc/asound/seq子目录。
    snd_info_version_init--创建/proc/asound/version,显示alsa版本号。
    snd_minor_info_init--创建/proc/asound/devices,显示所有主设备号为CONFIG_SND_MAJOR的设备。
    snd_card_info_init--创建/proc/asound/cards,显示当前注册的Sound Card。
    snd_info_minor_register

alsa_seq_device_init()创建Seq Bus,并创建响应的procfs:

alsa_seq_device_init
  bus_register--注册snd_seq_bus_type总线。
  seq_dev_proc_init--创建/proc/asound/seq/drivers。

hwdep类型设备提供了给用户空间对硬件设备进行控制和更新固件的手段:

alsa_hwdep_init
  snd_hwdep_proc_init--创建/proc/asound/hwdep节点。
  snd_ctl_register_ioctl/snd_ctl_register_ioctl_compat--增加hwdep类型Control的ioctl命令处理。

alsa_timer_init()创建ALSA timer设备和procfs:

alsa_timer_init
  snd_device_initialize
  snd_timer_register_system
  snd_register_device--创建/dev/snd/timers设备,操作函数集为snd_timer_f_ops。
  snd_timer_proc_init--创建/proc/asound/timers节点,显示系统创建的ALSA相关timer。

alsa_pcm_init()注册PCM Control相关ioctl命令处理,并创建pcm相关procfs:

alsa_pcm_init
  snd_ctl_register_ioctl/snd_ctl_register_ioctl_compat--注册PCM Control相关ioctl命令处理。
  snd_pcm_proc_init--创建/proc/asound/pcm节点,查看当前PCM设备列表。

alsa_rawmidi_init()注册midi相关ioctl命令处理:

alsa_rawmidi_init
  snd_ctl_unregister_ioctl/snd_ctl_unregister_ioctl_compat--注册midi相关ioctl命令处理。

alsa_seq_init()注册Seq设备、创建Seq相关proc接口等。

init_soundcore()创建sound类:

init_soundcore
  init_oss_soundcore--
  class_create--创建sound_class类。

snd_hrtimer_init()创建名称为HR timer的ALSA timer。

alsa_sound_last_init()遍历所有已注册的Sound Card,增加引用计数。

snd_soc_init()创建asoc的debugfs目录及节点等:

snd_soc_init
  snd_soc_debugfs_init--创建/sys/kernel/debug/asoc目录,以及dais和components两个节点。components遍历component_list列表;dais遍历每个component下的dai列表。
  snd_soc_util_init--注册"snd-soc-dummy"设备。
    soc_dummy_driver
      snd_soc_dummy_probe
        devm_snd_soc_register_component--注册"snd-soc-dummy-dai"等。
  soc_driver
    soc_probe
      devm_snd_soc_register_card
      snd_register_device

5 ALSA数据结构和API

The ALSA Driver API将ALSA API进行分类,详细如下:

devm_snd_soc_register_component()注册一个带资源管理的Component到ALSA子系统:

devm_snd_soc_register_component
  snd_soc_register_component
    snd_soc_component_initialize
    snd_soc_add_component
      snd_soc_register_dais
        snd_soc_register_dai
          list_add_tail--component->dai_list
      list_add--component_list

 devm_snd_soc_register_card()注册一个带资源管理的ALSA Sound Card:

devm_snd_soc_register_card
  snd_soc_register_card
    snd_soc_bind_card
      snd_soc_dapm_init--snd_soc_card.dapm初始化。
      soc_check_tplg_fes
      soc_bind_aux_dev--绑定snd_soc_aux_dev设备。
        snd_soc_component_set_aux
      snd_soc_add_pcm_runtime
        ->soc_new_pcm_runtime
          ->device_add_groups--为当前设备创建dapm_widget(显示DAPM Widget的On/Off状态)和pmdown_time属性。
      snd_card_new
        --分配struct snd_card,并初始化。
        --snd_ctl_create--创建/dev/snd/controlCx,即Cardx的Control节点。
          --snd_device_initialize
          --dev_set_name--设置设备名称为controlCx。
          --snd_device_new--创建ALSA设备Component,操作函数中注册成员snd_ctl_dev_register负责创建设备。
            --snd_ctl_dev_register
              --snd_register_device--设备对应的操作函数集为snd_ctl_f_ops
        --snd_info_card_create
          --create_subdir--创建/proc/asound/cardX目录。
          --snd_card_ro_proc_new--创建/proc/asound/cardX/id节点。
      --soc_init_card_debugfs--为每个snd_soc_card创建debugfs。
        --debugfs_create_u32--创建dapm_pop_time节点。
        --snd_soc_dapm_debugfs_init--创建dapm目录,以及bias_level节点。
      --soc_resume_init
      --snd_soc_dapm_new_controls--为snd_soc_card的dapm_widgets和of_dapm_widgets创建Controls。
      --snd_soc_card_probe--调用snd_soc_card的probe函数。
      --soc_probe_link_components--探测当前Card上DAI连接所使用的所有Component。
        --soc_probe_component
          --snd_soc_component_module_get_when_probe
          --soc_set_name_prefix
          --soc_init_component_debugfs--
          --snd_soc_dapm_init--初始化struct snd_soc_dapm_context结构体,将card和component关联起来。
          --snd_soc_dapm_new_controls--创建component所属的widget。
          --snd_soc_dapm_new_dai_widgets--创建DAI的Playback/Capture Widget。
          --snd_soc_component_probe--调用component的probe函数。
          --snd_soc_component_init--调用component的init函数。
          --snd_soc_add_component_controls--给当前Component添加一系列Controls。
          --snd_soc_dapm_add_routes--创建两个DAPM widget之间的路由。
      soc_probe_aux_devices--为Aux设备创建Component。
      soc_probe_link_dais
        snd_soc_pcm_dai_probe
      soc_init_pcm_runtime
        snd_soc_link_init
        soc_dpcm_debugfs_add
        soc_new_pcm--创建snc_pcm设备。
          snd_pcm_new
            _snd_pcm_new
              snd_pcm_new_stream--创建Playback和Capture两个Stream。
              snd_device_new--创建PCM类型的Sound设备,调用snd_pcm_dev_register创建设备。
                snd_pcm_dev_register
                  snd_register_device--创建/dev/snd/pcmCxDxp和/dev/snd/pcmCxDxc设备。操作函数集为snd_pcm_f_ops
          snd_soc_pcm_component_new
        snd_soc_pcm_dai_new
      snd_soc_dapm_link_dai_widgets
        snd_soc_dapm_add_path--创建Source和Sink之间的连接通路。
      snd_soc_dapm_connect_dai_link_widgets
      snd_soc_add_card_controls--根据snd_soc_card的controls创建Controls。
      snd_soc_dapm_add_routes--根据snd_soc_card的dapm_routes和of_dapm_routes创建路由。
      snd_soc_set_dmi_name
      soc_setup_card_name
      snd_component_add
      snd_soc_card_late_probe--调用snd_soc_card的late_probe函数。
      snd_soc_dapm_new_widgets--根据snd_soc_card的widgets列表,创建。
      snd_card_register--注册snd_card设备,并创建proc节点。
        snd_device_register_all
          __snd_device_register
        snd_info_card_register
          snd_info_register
          proc_symlink--创建到Card的链接。
      dapm_mark_endpoints_dirty
      snd_soc_dapm_sync

6 ALSA驱动

Component是对一系列功能的抽象,Component包含多个Widget。一个Control是对一个控制功能的抽象。Route是不同Widget之间的路由。

6.1 Platform class driver: DAI I2S

 RK3588的DAI使用i2s,DTS配置如下:

    i2s0_8ch: i2s@fe470000 {
        compatible = "rockchip,rk3588-i2s-tdm";
        reg = <0x0 0xfe470000 0x0 0x1000>;
        interrupts = <GIC_SPI 180 IRQ_TYPE_LEVEL_HIGH>;
        clocks = <&cru MCLK_I2S0_8CH_TX>, <&cru MCLK_I2S0_8CH_RX>, <&cru HCLK_I2S0_8CH>;
        clock-names = "mclk_tx", "mclk_rx", "hclk";
        assigned-clocks = <&cru CLK_I2S0_8CH_TX_SRC>, <&cru CLK_I2S0_8CH_RX_SRC>;
        assigned-clock-parents = <&cru PLL_AUPLL>, <&cru PLL_AUPLL>;
        dmas = <&dmac0 0>, <&dmac0 1>;
        dma-names = "tx", "rx";
        power-domains = <&power RK3588_PD_AUDIO>;
        resets = <&cru SRST_M_I2S0_8CH_TX>, <&cru SRST_M_I2S0_8CH_RX>;
        reset-names = "tx-m", "rx-m";
        rockchip,clk-trcm = <1>;
        pinctrl-names = "default";
        pinctrl-0 = <&i2s0_lrck
                 &i2s0_sclk
                 &i2s0_sdi0
                 &i2s0_sdi1
                 &i2s0_sdi2
                 &i2s0_sdi3
                 &i2s0_sdo0
                 &i2s0_sdo1
                 &i2s0_sdo2
                 &i2s0_sdo3>;
        #sound-dai-cells = <0>;
        status = "disabled";
    };

rockchip_i2s_tdm_driver作为DAI驱动,做如下工作:

  • 解析dts,处理寄存器映射、中断、时钟、复位、pinctrl等。
  • 注册中断处理函数rockchip_i2s_tdm_isr。
  • 将I2S TDM作为component注册到ALSA子系统。
  • PCM所使用的rx/tx DMA引擎注册。
rockchip_i2s_tdm_driver
  rockchip_i2s_tdm_probe
    rockchip_i2s_tdm_dai_prepare--分配一个初始化好的的struct snd_soc_dai_driver结构体。
    devm_platform_get_and_ioremap_resource
    devm_regmap_init_mmio
    platform_get_irq_optional
    devm_request_irq--如有中断,则注册处理函数。
      rockchip_i2s_tdm_isr
    rockchip_i2s_tdm_tx_path_prepare
    rockchip_i2s_tdm_rx_path_prepare
    devm_snd_soc_register_component--component和dai驱动分别为rockchip_i2s_tdm_componentrockchip_i2s_tdm_dai
      rockchip_i2s_tdm_snd_controls--创建"I2STDM Digital Loopback Mode",控制I2S传输模式。
      rockchip_i2s_tdm_dai_ops--I2S TDM接口操作函数。
    devm_snd_dmaengine_pcm_register

I2S TDM Component的Controls为:

static const struct snd_kcontrol_new rockchip_i2s_tdm_snd_controls[] = {
    SOC_ENUM_EXT("I2STDM Digital Loopback Mode", loopback_mode,
             rockchip_i2s_tdm_loopback_get,
             rockchip_i2s_tdm_loopback_put),--支持4种模式:"Disabled", "Mode1", "Mode2", "Mode2 Swap"。
};

I2S DAI驱动为:

    struct snd_soc_dai_driver rockchip_i2s_tdm_dai = {
        .probe = rockchip_i2s_tdm_dai_probe,
        .playback = {
            .stream_name = "Playback",
            .channels_min = 2,
            .channels_max = 16,
            .rates = SNDRV_PCM_RATE_8000_192000,
            .formats = (SNDRV_PCM_FMTBIT_S8 |
                    SNDRV_PCM_FMTBIT_S16_LE |
                    SNDRV_PCM_FMTBIT_S20_3LE |
                    SNDRV_PCM_FMTBIT_S24_LE |
                    SNDRV_PCM_FMTBIT_S32_LE |
                    SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE),
        },
        .capture = {
            .stream_name = "Capture",
            .channels_min = 2,
            .channels_max = 16,
            .rates = SNDRV_PCM_RATE_8000_192000,
            .formats = (SNDRV_PCM_FMTBIT_S8 |
                    SNDRV_PCM_FMTBIT_S16_LE |
                    SNDRV_PCM_FMTBIT_S20_3LE |
                    SNDRV_PCM_FMTBIT_S24_LE |
                    SNDRV_PCM_FMTBIT_S32_LE |
                    SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE),
        },
        .ops = &rockchip_i2s_tdm_dai_ops,
    };

static const struct snd_soc_dai_ops rockchip_i2s_tdm_dai_ops = {
    .startup = rockchip_i2s_tdm_startup,
    .shutdown = rockchip_i2s_tdm_shutdown,
    .hw_params = rockchip_i2s_tdm_hw_params,
    .set_sysclk = rockchip_i2s_tdm_set_sysclk,
    .set_fmt = rockchip_i2s_tdm_set_fmt,
    .set_tdm_slot = rockchip_dai_tdm_slot,
    .trigger = rockchip_i2s_tdm_trigger,
};

Platform相关的DAPM Widget、Control、Route:

  • DAPM Widget:Capture和Playback。
  • Control:rockchip_i2s_tdm_snd_controls。
  • Route:。

6.2 Codec class driver: ES8388

ES8388的框架图如下:

ALC:自动电平控制(Automatic Level Control,ALC),是针对由于器件本身变化,环境引起工作点变化等,在电路中加入的稳定电平的电路,在一定范围内,ALC电路自动纠正偏移的电平回到要求的数值。

使用ES8388作为Codec作为I2C Client:

&i2c7 {
    status = "okay";
    es8388: es8388@11 {
        status = "okay";
        #sound-dai-cells = <0>;
        compatible = "everest,es8388", "everest,es8323";
        reg = <0x11>;
        clocks = <&cru I2S0_8CH_MCLKOUT>;
        clock-names = "mclk";
        assigned-clocks = <&cru I2S0_8CH_MCLKOUT>;
        assigned-clock-rates = <12288000>;
        pinctrl-names = "default";
        pinctrl-0 = <&i2s0_mclk>;
    };
};

es8323_i2c_probe()初始化ES8388,做如下工作:

  • 使用I2C作为Regmap接口。
  • 通过I2C接口读取ES8388作为测试。
  • 将ES8388作为Component注册到ALSA子系统。
es8323_i2c_driver
  es8323_i2c_probe
    devm_regmap_init_i2c--将i2c注册为regmap设备。
    i2c_set_clientdata--分配一个es8323_priv结构体作为i2c_client的私有数据。
    devm_snd_soc_register_component--注册一个compoment,当设备被注销时component也同样会被注销。

Component对应的驱动函数集为soc_codec_dev_es8323:

static const struct snd_soc_component_driver soc_codec_dev_es8323 = {
    .probe = es8323_probe,
    .remove = es8323_remove,
    .suspend = es8323_suspend,
    .resume = es8323_resume,
    .set_bias_level = es8323_set_bias_level,
    .dapm_widgets        = es8323_dapm_widgets,
    .num_dapm_widgets    = ARRAY_SIZE(es8323_dapm_widgets),
    .dapm_routes        = audio_map,
    .num_dapm_routes    = ARRAY_SIZE(audio_map),
    .controls        = es8323_snd_controls,
    .num_controls        = ARRAY_SIZE(es8323_snd_controls),
};

static const struct snd_soc_dapm_widget es8323_dapm_widgets[] = {
    SND_SOC_DAPM_INPUT("LINPUT1"),
    SND_SOC_DAPM_INPUT("LINPUT2"),
    SND_SOC_DAPM_INPUT("RINPUT1"),
    SND_SOC_DAPM_INPUT("RINPUT2"),
    SND_SOC_DAPM_MUX("Left PGA Mux", SND_SOC_NOPM, 0, 0,
             &es8323_left_dac_mux_controls),--对应LIN1/LIN2/LIN1-RIN1/LIN2-RIN2输入的Mux。
    SND_SOC_DAPM_MUX("Right PGA Mux", SND_SOC_NOPM, 0, 0,
             &es8323_right_dac_mux_controls),--对应RIN1/RIN2/LIN1-RIN1/LIN2-RIN2输入的Mux。
    SND_SOC_DAPM_MICBIAS("Mic Bias", ES8323_ADCPOWER, 3, 1),--对应ES8388的Mic Bias。

    SND_SOC_DAPM_MUX("Differential Mux", SND_SOC_NOPM, 0, 0,
             &es8323_diffmux_controls),--选择LIN1-RIN1或者LIN2-RIN2。

    SND_SOC_DAPM_MUX("Left ADC Mux", SND_SOC_NOPM, 0, 0,
             &es8323_monomux_controls),--当Mono时选择Left ADC还是Right ADC。
    SND_SOC_DAPM_MUX("Right ADC Mux", SND_SOC_NOPM, 0, 0,
             &es8323_monomux_controls),--当Mono时选择Left ADC还是Right ADC。

    SND_SOC_DAPM_MUX("Left Line Mux", SND_SOC_NOPM, 0, 0,
             &es8323_left_line_controls),--对应LIN1/LIN2/micL输入的Mux。
    SND_SOC_DAPM_MUX("Right Line Mux", SND_SOC_NOPM, 0, 0,
             &es8323_right_line_controls),--对应RIN1/RIN2/micR输入的Mux。

    SND_SOC_DAPM_ADC("Right ADC", "Right Capture", ES8323_ADCPOWER, 4, 1),--对应ES8388框图中的Right ADC power。
    SND_SOC_DAPM_ADC("Left ADC", "Left Capture", ES8323_ADCPOWER, 5, 1),--对应ES8388框图中的Left ADC power。

    /* gModify.Cmmt Implement when suspend/startup */
    SND_SOC_DAPM_DAC("Right DAC", "Right Playback", ES8323_DACPOWER, 6, 1),--对应ES8388框图中的Right DAC power。
    SND_SOC_DAPM_DAC("Left DAC", "Left Playback", ES8323_DACPOWER, 7, 1),--对应ES8388框图中的Left DAC power。

    SND_SOC_DAPM_AIF_OUT("I2S OUT", "Capture",  0, SND_SOC_NOPM, 0, 0),
    SND_SOC_DAPM_AIF_IN("I2S IN", "Playback", 0, SND_SOC_NOPM, 0, 0),

    SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
               &es8323_left_mixer_controls[0],--DACL和LIN到mixL的Mixer设置。
               ARRAY_SIZE(es8323_left_mixer_controls)),
    SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
               &es8323_right_mixer_controls[0],--DACR和RIN到mixR的Mixer设置。
               ARRAY_SIZE(es8323_right_mixer_controls)),
    SND_SOC_DAPM_PGA("Right ADC Power", ES8323_ADCPOWER, 6, 1, NULL, 0),--对应Right analog input power。
    SND_SOC_DAPM_PGA("Left ADC Power", ES8323_ADCPOWER, 7, 1, NULL, 0),--对应Left analog input power。
    SND_SOC_DAPM_PGA("Right Out 2", ES8323_DACPOWER, 2, 0, NULL, 0),--对应ES8388框图中的ROUT2。
    SND_SOC_DAPM_PGA("Left Out 2", ES8323_DACPOWER, 3, 0, NULL, 0),--对应ES8388框图中的LOUT2。
    SND_SOC_DAPM_PGA("Right Out 1", ES8323_DACPOWER, 4, 0, NULL, 0),--对应ES8388框图中的ROUT1。
    SND_SOC_DAPM_PGA("Left Out 1", ES8323_DACPOWER, 5, 0, NULL, 0),--对应ES8388框图中的LOUT1。
    SND_SOC_DAPM_PGA("LAMP", ES8323_ADCCONTROL1, 4, 0, NULL, 0),--对应ES8388框图中的Left MicAmp增益调节。
    SND_SOC_DAPM_PGA("RAMP", ES8323_ADCCONTROL1, 0, 0, NULL, 0),--对应ES8388框图中的Right MicAmp增益调节。

    SND_SOC_DAPM_OUTPUT("LOUT1"),
    SND_SOC_DAPM_OUTPUT("ROUT1"),
    SND_SOC_DAPM_OUTPUT("LOUT2"),
    SND_SOC_DAPM_OUTPUT("ROUT2"),
    SND_SOC_DAPM_OUTPUT("VREF"),
};

static const struct snd_soc_dapm_route audio_map[] = {
    {"Left PGA Mux", "Line 1L", "LINPUT1"},
    {"Left PGA Mux", "Line 2L", "LINPUT2"},
    {"Left PGA Mux", "DifferentialL", "Differential Mux"},

    {"Right PGA Mux", "Line 1R", "RINPUT1"},
    {"Right PGA Mux", "Line 2R", "RINPUT2"},
    {"Right PGA Mux", "DifferentialR", "Differential Mux"},

    {"Differential Mux", "Line 1", "LINPUT1"},
    {"Differential Mux", "Line 1", "RINPUT1"},
    {"Differential Mux", "Line 2", "LINPUT2"},
    {"Differential Mux", "Line 2", "RINPUT2"},

    {"Left ADC Mux", "Stereo", "Right PGA Mux"},
    {"Left ADC Mux", "Stereo", "Left PGA Mux"},
    {"Left ADC Mux", "Mono (Left)", "Left PGA Mux"},

    {"Right ADC Mux", "Stereo", "Left PGA Mux"},
    {"Right ADC Mux", "Stereo", "Right PGA Mux"},
    {"Right ADC Mux", "Mono (Right)", "Right PGA Mux"},

    {"Left ADC Power", NULL, "Left ADC Mux"},
    {"Right ADC Power", NULL, "Right ADC Mux"},
    {"Left ADC", NULL, "Left ADC Power"},
    {"Right ADC", NULL, "Right ADC Power"},

    {"I2S OUT", NULL, "Left ADC"},
    {"I2S OUT", NULL, "Right ADC"},

    {"Left Line Mux", "Line 1L", "LINPUT1"},
    {"Left Line Mux", "Line 2L", "LINPUT2"},
    {"Left Line Mux", "MicL", "Left PGA Mux"},

    {"Right Line Mux", "Line 1R", "RINPUT1"},
    {"Right Line Mux", "Line 2R", "RINPUT2"},
    {"Right Line Mux", "MicR", "Right PGA Mux"},

    {"Right DAC", NULL, "I2S IN"},
    {"Left DAC", NULL, "I2S IN"},

    {"Left Mixer", "Left Playback Switch", "Left DAC"},
    {"Left Mixer", "Left Bypass Switch", "Left Line Mux"},

    {"Right Mixer", "Right Playback Switch", "Right DAC"},
    {"Right Mixer", "Right Bypass Switch", "Right Line Mux"},

    {"Left Out 1", NULL, "Left Mixer"},
    {"LOUT1", NULL, "Left Out 1"},
    {"Right Out 1", NULL, "Right Mixer"},
    {"ROUT1", NULL, "Right Out 1"},

    {"Left Out 2", NULL, "Left Mixer"},
    {"LOUT2", NULL, "Left Out 2"},
    {"Right Out 2", NULL, "Right Mixer"},
    {"ROUT2", NULL, "Right Out 2"},
};

static const struct snd_kcontrol_new es8323_snd_controls[] = {
    SOC_ENUM("3D Mode", es8323_enum[4]),
    SOC_SINGLE("ALC Capture Target Volume", ES8323_ADCCONTROL11, 4, 15, 0),
    SOC_SINGLE("ALC Capture Max PGA", ES8323_ADCCONTROL10, 3, 7, 0),
    SOC_SINGLE("ALC Capture Min PGA", ES8323_ADCCONTROL10, 0, 7, 0),
    SOC_ENUM("ALC Capture Function", es8323_enum[5]),
    SOC_SINGLE("ALC Capture ZC Switch", ES8323_ADCCONTROL13, 6, 1, 0),
    SOC_SINGLE("ALC Capture Hold Time", ES8323_ADCCONTROL11, 0, 15, 0),
    SOC_SINGLE("ALC Capture Decay Time", ES8323_ADCCONTROL12, 4, 15, 0),
    SOC_SINGLE("ALC Capture Attack Time", ES8323_ADCCONTROL12, 0, 15, 0),
    SOC_SINGLE("ALC Capture NG Threshold", ES8323_ADCCONTROL14, 3, 31, 0),
    SOC_ENUM("ALC Capture NG Type", es8323_enum[6]),
    SOC_SINGLE("ALC Capture NG Switch", ES8323_ADCCONTROL14, 0, 1, 0),
    SOC_SINGLE("ZC Timeout Switch", ES8323_ADCCONTROL13, 6, 1, 0),
    SOC_DOUBLE_R_TLV("Capture Digital Volume", ES8323_ADCCONTROL8,
             ES8323_ADCCONTROL9, 0, 192, 1, adc_tlv),
    SOC_SINGLE("Capture Mute", ES8323_ADCCONTROL7, 2, 1, 0),
    SOC_SINGLE_TLV("Left Channel Capture Volume", ES8323_ADCCONTROL1, 4, 8,
               0, bypass_tlv),
    SOC_SINGLE_TLV("Right Channel Capture Volume", ES8323_ADCCONTROL1, 0,
               8, 0, bypass_tlv),
    SOC_ENUM("Playback De-emphasis", es8323_enum[7]),
    SOC_ENUM("Capture Polarity", es8323_enum[8]),
    SOC_DOUBLE_R_TLV("PCM Volume", ES8323_DACCONTROL4, ES8323_DACCONTROL5,
             0, 192, 1, dac_tlv),--控制Left DAC和Right DAC的Volume。
    SOC_SINGLE_TLV("Left Mixer Left Bypass Volume", ES8323_DACCONTROL17, 3,
               7, 1, bypass_tlv2),
    SOC_SINGLE_TLV("Right Mixer Right Bypass Volume", ES8323_DACCONTROL20,
               3, 7, 1, bypass_tlv2),
    SOC_DOUBLE_R_TLV("Output 1 Playback Volume", ES8323_DACCONTROL24,
             ES8323_DACCONTROL25, 0, 33, 0, out_tlv),
    SOC_DOUBLE_R_TLV("Output 2 Playback Volume", ES8323_DACCONTROL26,
             ES8323_DACCONTROL27, 0, 33, 0, out_tlv),
};

es8323_dai定义了ES8388和SoC I2S TDM接口之间DAI的Plaback/Capture能力和操作函数集:

static struct snd_soc_dai_driver es8323_dai = {
    .name = "ES8323 HiFi",
    .playback = {
             .stream_name = "Playback",
             .channels_min = 1,
             .channels_max = 2,
             .rates = es8323_RATES,
             .formats = es8323_FORMATS,
             },
    .capture = {
            .stream_name = "Capture",
            .channels_min = 1,
            .channels_max = 2,
            .rates = es8323_RATES,
            .formats = es8323_FORMATS,
            },
    .ops = &es8323_ops,
    .symmetric_rates = 1,
};

 es8323_ops定义了DAI操作函数集:

static struct snd_soc_dai_ops es8323_ops = {
    .startup = es8323_pcm_startup,
    .hw_params = es8323_pcm_hw_params,
    .set_fmt = es8323_set_dai_fmt,
    .set_sysclk = es8323_set_dai_sysclk,
    .mute_stream = es8323_mute,
    .no_capture_mute = 1,
};

Codec相关的DAPM Widget、Control、Route:

  • DAPM Widget:es8323_dapm_widgets。
  • Control:es8323_snd_controls。
  • Route:audio_map。

6.3 Machine Class Driver

Machine相关dts定义了耳机检测、耳机按键检测、Speaker功放开关等,还定义了关联的DAI和Codec。

    es8388_sound: es8388-sound {
        status = "okay";
        compatible = "rockchip,multicodecs-card";
        rockchip,card-name = "rockchip-es8388";--Sound Card名称。
        hp-det-gpio = <&gpio1 RK_PC4 GPIO_ACTIVE_LOW>;--Headphone检测GPIO引脚。
        io-channels = <&saradc 3>;--耳机ADC作为按键检测接口。
        io-channel-names = "adc-detect";
        keyup-threshold-microvolt = <1800000>;
        poll-interval = <100>;
        spk-con-gpio = <&gpio3 RK_PB2 GPIO_ACTIVE_HIGH>;--Speaker功放开关GPIO。
        //hp-con-gpio = <&gpio3 RK_PB2 GPIO_ACTIVE_HIGH>;
        rockchip,format = "i2s";
        rockchip,mclk-fs = <256>;
        rockchip,cpu = <&i2s0_8ch>;--Card所使用的DAI接口。
        rockchip,codec = <&es8388>;--Card所使用的作为Platform设备的Codec。
        rockchip,audio-routing =--定义Card路由。
            "Headphone", "LOUT1",
            "Headphone", "ROUT1",
            "Speaker", "LOUT2",
            "Speaker", "ROUT2",
            "Headphone", "Headphone Power",
            "Headphone", "Headphone Power",
            "Speaker", "Speaker Power",
            "Speaker", "Speaker Power",
            "LINPUT2", "Main Mic",--Headset Mic插孔作为Main Mic使用。
            "RINPUT2", "Main Mic",
            "LINPUT1", "Headset Mic",
            "RINPUT1", "Headset Mic";
        pinctrl-names = "default";
        pinctrl-0 = <&hp_det>;
        play-pause-key {
            label = "playpause";
            linux,code = <KEY_PLAYPAUSE>;
            press-threshold-microvolt = <2000>;
        };
    };

 rk_multicodecs_probe()作为Machine Driver,处理SOC相关配置,以及将Codec和SoC DAI进行关联:

  • 获取Sound Card名称。
  • 初始化snd_soc_dai_link,关联DAI和Codec。
  • 初始化sound_soc_card,初始化dapm_widgets、controls、routes等。
  • 获取Speaker功放开关GPIO、HP检测GPIO、耳机按键检查ADC等,并注册中断、按键等。
  • 注册Sound Card。
rockchip_multicodecs_driver
  rk_multicodecs_probe
    wait_locked_card
    snd_soc_of_parse_card_name
    ->初始化名为"dailink-multicodecs"的struct snd_soc_dai_link,init初始化函数为rk_dailink_init;ops对应操作函数集为rk_ops;cpus对应数据来自于节点"rockchip,cpu";codecs数据来自于节点"rockchip,codec"。
      ->rk_dailink_init
        ->snd_soc_card_jack_new
          ->snd_jack_new
          ->snd_soc_jack_add_pins--对应的Jack Pin为jack_pins。
            ->snd_jack_add_new_kctl--为每个Jack Pin创建Control。
        ->snd_soc_jack_add_zones
        ->devm_request_threaded_irq--注册hp-det-gpio的中断处理函数headset_det_irq_thread()。上升沿和下降沿都可以触发。
          ->headset_det_irq_thread--中断中进行workqueue调度。
            ->adc_jack_handler--对应work处理函数。
    ->初始化struct snd_soc_card结构体,dapm_widgets和controls分别为mc_dapm_widgetsmc_controls
    devm_iio_channel_get--获取ADC检测通道。
    device_property_read_u32--读取"keyup-threshold-microvolt"值。
    mc_keys_load_keymap--解析play-pause-key,获取按键按下电压阈值和键值。
    devm_input_allocate_device--分配Input_dev设备。
    mc_keys_setup_polling--设置input设备poller,对应的worker函数为mc_keys_poller_work。
      ->mc_keys_poll
    mc_set_poll_interval--设置poll间隔。
    input_register_device--注册Headset上按键作为Input设备。
    devm_gpiod_get_optional--获取"spk-con-gpio"、"hp-con-gpio"、"hp-det"3个GPIO。其中hp-det作为中断对应的处理函数为headset_det_irq_thread。
    devm_extcon_dev_allocate--分配EXTCON_JACK_MICROPHONE和EXTCON_JACK_HEADPHONE两个extcon设备。
    devm_extcon_dev_register--注册headset的ExtCon设备。
    snd_soc_of_parse_audio_routing--解析"rockchip,audio-routing",初始化struct snd_soc_card的routes
    snd_soc_card_set_drvdata
    devm_snd_soc_register_card--带资源管理的注册声卡的接口,包含的dapm_widgets为mc_dapm_widgets,controls为mc_controls,dai_link操作函数为rk_ops。

adc_jack_handler()进行Jack插拔中断处理:

static void adc_jack_handler(struct work_struct *work)
{
    struct multicodecs_data *mc_data = container_of(to_delayed_work(work),
                          struct multicodecs_data,
                          handler);
    struct snd_soc_jack *jack_headset = mc_data->jack_headset;
    int adc, ret = 0;

    if (!gpiod_get_value(mc_data->hp_det_gpio)) {--检测hp-det-gpio的值,如果为低电平,即Jack拔出。
        snd_soc_jack_report(jack_headset, 0, SND_JACK_HEADSET);--去使能相关引脚;上报input子系统状态。
        extcon_set_state_sync(mc_data->extcon,
                EXTCON_JACK_HEADPHONE, false);--同步相关extcon状态。
        extcon_set_state_sync(mc_data->extcon,
                EXTCON_JACK_MICROPHONE, false);
        if (mc_data->poller)
            mc_keys_poller_stop(mc_data->poller);--停止poller。

        return;
    }
    if (!mc_data->adc) {--如果不存在adc通道,则Jack仅有Headphone,没有Mic。
        /* no ADC, so is headphone */
        snd_soc_jack_report(jack_headset, SND_JACK_HEADPHONE, SND_JACK_HEADSET);--
        extcon_set_state_sync(mc_data->extcon, EXTCON_JACK_HEADPHONE, true);
        extcon_set_state_sync(mc_data->extcon, EXTCON_JACK_MICROPHONE, false);
        return;
    }
    ret = iio_read_channel_processed(mc_data->adc, &adc);--读取ADC的值。
    if (ret < 0) {--读取错误,则认为Jack仅是Headphone。
        /* failed to read ADC, so assume headphone */
        snd_soc_jack_report(jack_headset, SND_JACK_HEADPHONE, SND_JACK_HEADSET);
        extcon_set_state_sync(mc_data->extcon, EXTCON_JACK_HEADPHONE, true);
        extcon_set_state_sync(mc_data->extcon, EXTCON_JACK_MICROPHONE, false);

    } else {--读取正确,认为Jack包含Headphone和Mic。
        snd_soc_jack_report(jack_headset,
                    snd_soc_jack_get_type(jack_headset, adc),
                    SND_JACK_HEADSET);
        extcon_set_state_sync(mc_data->extcon, EXTCON_JACK_HEADPHONE, true);

        if (snd_soc_jack_get_type(jack_headset, adc) == SND_JACK_HEADSET) {--根据ADC的值
            extcon_set_state_sync(mc_data->extcon, EXTCON_JACK_MICROPHONE, true);
            if (mc_data->poller)
                mc_keys_poller_start(mc_data->poller);
        }
    }
};

Machine驱动的Widget和Controls如下:

static const struct snd_soc_dapm_widget mc_dapm_widgets[] = {

    SND_SOC_DAPM_HP("Headphone", NULL),
    SND_SOC_DAPM_SPK("Speaker", NULL),
    SND_SOC_DAPM_MIC("Main Mic", NULL),
    SND_SOC_DAPM_MIC("Headset Mic", NULL),
    SND_SOC_DAPM_SUPPLY("Speaker Power",
                SND_SOC_NOPM, 0, 0,
                mc_spk_event,--通过spk-con-gpio开关功放。
                SND_SOC_DAPM_POST_PMU |
                SND_SOC_DAPM_PRE_PMD),
    SND_SOC_DAPM_SUPPLY("Headphone Power",
                SND_SOC_NOPM, 0, 0,
                mc_hp_event,--通过spk-con-gpio开关功放。
                SND_SOC_DAPM_POST_PMU |
                SND_SOC_DAPM_PRE_PMD),
};

static const struct snd_kcontrol_new mc_controls[] = {
    SOC_DAPM_PIN_SWITCH("Headphone"),
    SOC_DAPM_PIN_SWITCH("Speaker"),
    SOC_DAPM_PIN_SWITCH("Main Mic"),
    SOC_DAPM_PIN_SWITCH("Headset Mic"),
};

Machine相关的DAPM Widget、Control、Route:

  • DAPM Widget:mc_dapm_widgets。
  • Control:mc_controls。
  • Route:DTS的rockchip,audio-routing。

6.4 Audio Graph

在alsa中添加如下代码,建立Audio Graph的dot数据:

diff --git a/kernel/sound/soc/soc-dapm.c b/kernel/sound/soc/soc-dapm.c
index 2924d89bf..1a812a0cc 100644
--- a/kernel/sound/soc/soc-dapm.c
+++ b/kernel/sound/soc/soc-dapm.c
@@ -2830,6 +2830,7 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
     struct snd_soc_dapm_path *path;
     int ret;
 
+    printk("AudioGraph \"%s\" -> \"%s\"\n", wsource->name, wsink->name);
     if (wsink->is_supply && !wsource->is_supply) {
         dev_err(dapm->dev,
             "Connecting non-supply widget to supply widget is not supported (%s -> %s)\n",
@@ -2934,6 +2935,10 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
     unsigned int sink_ref = 0;
     unsigned int source_ref = 0;
     int ret;
+    if(route->control != NULL) {
+        printk("AudioGraph \"%s\" -> \"%s\"\n", route->control, route->sink);
+        printk("AudioGraph \"%s\" -> \"%s\"\n", route->source, route->control);
+    }
 
     prefix = soc_dapm_prefix(dapm);
     if (prefix) {
@@ -3612,6 +3617,47 @@ int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol,
     return ret;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_put_pin_switch);
+char *dapm_type[] = {
+    "Input",        /* input pin */
+    "Output",        /* output pin */
+    "Mux",            /* selects 1 analog signal from many inputs */
+    "Demux",            /* connects the input to one of multiple outputs */
+    "Mixer",            /* mixes several analog signals together */
+    "Mixer",        /* mixer with named controls */
+    "PGA",            /* programmable gain/attenuation (volume) */
+    "Output driver",            /* output driver */
+    "ADC",            /* analog to digital converter */
+    "DAC",            /* digital to analog converter */
+    "Mic bias",        /* microphone bias (power) - DEPRECATED: use snd_soc_dapm_supply */
+    "Mic",            /* microphone */
+    "Headphone",            /* headphones */
+    "Speaker",            /* speaker */
+    "Line IO",            /* line input/output */
+    "Switch",        /* analog switch */
+    "VMID",            /* codec bias/vmid - to minimise pops */
+    "Pre widget",            /* machine specific pre widget - exec first */
+    "Post widget",            /* machine specific post widget - exec last */
+    "Supply",        /* power/clock supply */
+    "Pinctrl",        /* pinctrl */
+    "Regulator",    /* external regulator */
+    "Clock",    /* external clock */
+    "AIF in",        /* audio interface input */
+    "AIF out",        /* audio interface output */
+    "Signal generator",        /* signal generator */
+    "Sink",
+    "DAI in",        /* link to DAI structure */
+    "DAI out",
+    "DAI link",        /* link between two DAI structures */
+    "kcontrol",        /* Auto-disabled kcontrol */
+    "Internal buffer",        /* DSP/CODEC internal buffer */
+    "Internal scheduler",        /* DSP/CODEC internal scheduler */
+    "Effect",        /* DSP/CODEC effect component */
+    "Src",        /* DSP/CODEC SRC component */
+    "Asrc",        /* DSP/CODEC ASRC component */
+    "Encoder",        /* FW/SW audio encoder component */
+    "Decoder",        /* FW/SW audio decoder component */
+
+};
 
 struct snd_soc_dapm_widget *
 snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,
@@ -3621,6 +3667,12 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,
     struct snd_soc_dapm_widget *w;
     const char *prefix;
     int ret;
+    printk("AudioGraph \"%s\"[shape=box label=\"Card(%s)\\nComponent(%s)\\nDAPM Widget(%s(%s))\"]\n",
+        widget->name==NULL?"NULL":widget->name,
+        dapm->card==NULL?"NULL":dapm->card->name,
+        dapm->component==NULL?"NULL":dapm->component->name,
+        widget->name==NULL?"NULL":widget->name,
+        dapm_type[widget->id]);
 
     if ((w = dapm_cnew_widget(widget)) == NULL)
         return ERR_PTR(-ENOMEM);

将删除dot数据插入:

digraph board {
    rankdir=TB
    concentrate=true

}

通过dot -Tpng alsa.dot  -o alsa.png生成如下图片:

7 ALSA调试节点

/dev/snd中存放Sound Card的Control和PCM设备节点:

/dev/snd/
|-- by-path
|   |-- platform-es8388-sound -> ../controlC0
|-- controlC0
|-- pcmC0D0c
|-- pcmC0D0p
|-- seq
`-- timer

/proc/asound/中提供ALSA通用信息,以及每个Sound Card的详细信息: 

/proc/asound/
|-- card0
|   |-- id
|   |-- pcm0c
|   |   |-- info
|   |   |-- sub0
|   |   |   |-- hw_params
|   |   |   |-- info
|   |   |   |-- prealloc
|   |   |   |-- prealloc_max
|   |   |   |-- status
|   |   |   |-- sw_params
|   |   |   `-- xrun_injection
|   |   `-- xrun_debug
|   `-- pcm0p
|       |-- info
|       |-- sub0
|       |   |-- hw_params
|       |   |-- info
|       |   |-- prealloc
|       |   |-- prealloc_max
|       |   |-- status
|       |   |-- sw_params
|       |   `-- xrun_injection
|       `-- xrun_debug
|-- cards
|-- devices
|-- hwdep
|-- pcm
|-- rockchipes8388 -> card0
|-- seq
|   |-- clients
|   |-- drivers
|   |-- queues
|   `-- timer
|-- timers
`-- version

/sys/class/sound/存放ALSA Sound类设备的链接:

/sys/class/sound/
|-- card0 -> ../../devices/platform/es8388-sound/sound/card0
|-- controlC0 -> ../../devices/platform/es8388-sound/sound/card0/controlC0
|-- pcmC0D0c -> ../../devices/platform/es8388-sound/sound/card0/pcmC0D0c
|-- pcmC0D0p -> ../../devices/platform/es8388-sound/sound/card0/pcmC0D0p
|-- seq -> ../../devices/virtual/sound/seq
`-- timer -> ../../devices/virtual/sound/timer

ASLA Sound  Card详细信息:

/sys/devices/platform/es8388-sound/sound/card0
|-- controlC0
|   |-- dev
|   |-- device -> ../../card0
|   |-- subsystem -> ../../../../../../class/sound
|   `-- uevent
|-- device -> ../../../es8388-sound
|-- id
|-- input2
|   |-- capabilities
|   |   |-- abs
|   |   |-- ev
|   |   |-- ff
|   |   |-- key
|   |   |-- led
|   |   |-- msc
|   |   |-- rel
|   |   |-- snd
|   |   `-- sw
|   |-- device -> ../../card0
|   |-- event2
|   |   |-- dev
|   |   |-- device -> ../../input2
|   |   |-- subsystem -> ../../../../../../../class/input
|   |   `-- uevent
|   |-- id
|   |   |-- bustype
|   |   |-- product
|   |   |-- vendor
|   |   `-- version
|   |-- modalias
|   |-- name
|   |-- phys
|   |-- properties
|   |-- subsystem -> ../../../../../../class/input
|   |-- uevent
|   `-- uniq
|-- number
|-- pcmC0D0c
|   |-- dev
|   |-- device -> ../../card0
|   |-- pcm_class
|   |-- subsystem -> ../../../../../../class/sound
|   `-- uevent
|-- pcmC0D0p
|   |-- dev
|   |-- device -> ../../card0
|   |-- pcm_class
|   |-- subsystem -> ../../../../../../class/sound
|   `-- uevent
|-- subsystem -> ../../../../../class/sound
`-- uevent

8 ALSA相关Tracepoint

Tracepoint为ALSA提供了一系列追踪函数锚点。

ASoC相关Tracepoint有:

/sys/kernel/debug/tracing/events/asoc/
|-- enable
|-- filter
|-- snd_soc_bias_level_done--设置Sound Card的Bias Level结束,不一定成功。
|-- snd_soc_bias_level_start--开始设置Sound Card的Bias Level。
|-- snd_soc_dapm_connected
|-- snd_soc_dapm_done
|-- snd_soc_dapm_path--显示当前DAPM的路径,前面有*号的表示处于连接状态。
|-- snd_soc_dapm_start--开始遍历DAPM Widget的up_list和down_list进行On/Off。
|-- snd_soc_dapm_walk_done--遍历DAPM Widget结束。
|-- snd_soc_dapm_widget_event_done
|-- snd_soc_dapm_widget_event_start
|-- snd_soc_dapm_widget_power--跟踪DAPM Widget的On/Off。
|-- snd_soc_jack_irq
|-- snd_soc_jack_notify
`-- snd_soc_jack_report--上报Jack状态Mask表示Jack类型,status表示插拔状态。

PCM相关Tracepoint有:

/sys/kernel/debug/tracing/events/snd_pcm/
|-- applptr
|-- applptr_start
|-- enable
|-- filter
|-- hw_interval_param--记录snd_pcm_hw_params的intervals参数。
|-- hw_mask_param--记录snd_pcm_hw_params的masks参数。
|-- hw_ptr_error
|-- hwptr
`-- xrun

打开追踪所有行为:

echo 1 > /sys/kernel/debug/tracing/events/asoc/enable
echo 1 > /sys/kernel/debug/tracing/events/snd_pcm/enable

追踪Playback/Capture的行为使用:

echo > /sys/kernel/debug/tracing/trace
echo 0 > /sys/kernel/debug/tracing/events/enable
echo 1 > /sys/kernel/debug/tracing/events/asoc/enable

使用arecord/aplay查看DAPM Path路径如下:

 

posted on 2024-05-01 23:59  ArnoldLu  阅读(2104)  评论(0编辑  收藏  举报

导航