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。
- DAI驱动(AC97/I2S/PCM)
- Machine类驱动:描述和绑定其他Component的胶水层,处理Machine相关Control和Machine层级的音频事件。
4.2 DAPM和Endpoint Widget
DAPM可以划分为4个电源域:Codec bias domain,Platform/Machine domain,Path domain,Stream domain。
- 1DAPM Widgets
- 1.1CODEC Domain Widgets
- 1.2Platform Domain Widgets
- 1.2.1SND_SOC_DAPM_INPUT:Codec的输入引脚。
- 1.2.2SND_SOC_DAPM_OUTPUT:Codec的输出引脚。
- 1.2.3SND_SOC_DAPM_MIC:Microphone。
- 1.2.4SND_SOC_DAPM_HP:Headphone。
- 1.2.5SND_SOC_DAPM_SPK:Speaker。
- 1.2.6SND_SOC_DAPM_LINE:Line 输入或输出。
- SND_SOC_DAPM_SIGGEN:信号发生器。
- 1.3Path domain widgets
- 1.3.1SND_SOC_DAPM_PGA:可编程增益放大器或衰减小部件。
- 1.3.1.1SOC_PGA_ARRAY
- 1.3.2SND_SOC_DAPM_MIXER:将多个模拟信号混合到一个模拟信号。
- 1.3.2.1SND_SOC_DAPM_MIXER_NAMED_CTL
- 1.3.3SND_SOC_DAPM_MICBIAS
- 1.3.4SND_SOC_DAPM_SWITCH:开关切换。
- 1.3.5SND_SOC_DAPM_MUX:切换选择多路输入中的一路作为输出。
- 1.3.6SND_SOC_DAPM_VALUE_MUX
- 1.4Pre-DAPM and Post-DAPM event widgets
- 1.4.1SND_SOC_DAPM_PRE:PRE widget。
- 1.4.2SND_SOC_DAPM_POST:POST widget。
- 1.5Stream Domain Widgets
- 1.5.1SND_SOC_DAPM_AIF_IN:AIF输入。
- 1.5.2SND_SOC_DAPM_AIF_OUT:AIF输出。
- 1.5.3SND_SOC_DAPM_DAC:DAC。
- 1.5.4SND_SOC_DAPM_ADC:ADC。
- SND_SOC_DAPM_CLOCK_SUPPLY:时钟提供。
- 1.6Generic widgets
- 1.6.1SND_SOC_DAPM_REG:寄存器。
- 1.6.2SND_SOC_DAPM_SUPPLY:时钟或电源提供。
- SND_SOC_DAPM_REGULATOR_SUPPLY:电源提供。
- 1.7Control Types
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进行分类,详细如下:
- Management of Cards and Devices
- PCM API
- Control/Mixer API
- MIDI API
- Proc Info API
- Compress Ofload
- ASoC
- Miscellaneous Functions
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_component和rockchip_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_widgets和mc_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路径如下: