概述
1,概述
2,ALSA框架介绍
3,ALSA声卡注册过程
4,ALSA设备节点创建
5,tinyalsa调用流程
1,概述
声卡创建过程:
1, 创建声卡实例. snd_card_create
2, 创建逻辑设备. snd_device_new
3, 设置PCM逻辑设备的substream的操作函数(cpu_dai的操作方法)
4, snd_card_register,将声卡实例注册进ALSA框架内. 生成第二步创建的设备节点 /dev/snd/下的play与cap节点
前三部分是系统初始化部分,最后一部分是将audio注册到ALSA框架,是核心部分
2,ALSA框架介绍
ALSA将整个框架分为三个模块 platform/machine/codec
模块作用:
Platform:SOC芯片部分,与平台本身相关。例如:平台支持I2S与PCM,I2S具体又细分为I2S0,I2S1,SPDIF等。平台最理想情况是可以适配任何的codec
例如:sound/snd/amlogic下的代码,代表amlogic平台;其下面的code都是与其平台相关的
Codec:绝大多数的codec芯片,都是外置的;sound/soc/codec/ 不多做解释
Machine:这个就是电路板,将SOC芯片和Codec芯片适配集成在一块;一般在device tree下或者在 平台路径下指定文件(例如tv)进行配置 eg: sound/soc/amlogic/meson
注册声卡的入口:snd_soc_register_card or devm_snd_soc_register_card
工作:
解析device tree,将platform/cpu_dai/codec_dai/codec注册进ASoC对应的各个组件链表中,填充snd_soc_dai_link
然后执行snd_soc_register_card,最终生成pcm与control设备节点。
从device tree上可以看出,alsa的几部分结构: platform_list;cup_list(即cpu_dai);codec_list; codec
aml_snd_tv {
compatible = "amlogic, txlx-snd-tv";
status = "okay";
aml-sound-card,format = "i2s";
aml_sound_card,name = "AML-TVAUDIO";
pinctrl-names = "audio_i2s";
pinctrl-0 = <&aml_audio_i2s>;
aux_dev = <&tas5731>;
cpu_list = <&cpudai0 &cpudai1 &cpudai2 &cpudai3>;
codec_list = <&codec0 &codec1 &codec2 &codec3>;
plat_list = <&i2s_plat &i2s_plat &pcm_plat &i2s_plat>;
cpudai0: cpudai0 {
sound-dai = <&i2s_dai>;
};
cpudai1: cpudai1 {
sound-dai = <&spdif_dai>;
};
cpudai2: cpudai2 {
sound-dai = <&pcm_dai>;
};
cpudai3: cpudai3 {
sound-dai = <&i2s2_dai>;
};
codec0: codec0 {
sound-dai = <&amlogic_codec>;
};
codec1: codec1 {
sound-dai = <&spdif_codec>;
};
codec2: codec2 {
sound-dai = <&pcm_codec>;
};
codec3: codec3 {
sound-dai = <&dummy_codec>;
};
};
platform部分:
填充一个结构struct snd_soc_platform ,然后把这个结构填充到platform_list中
snd_soc_register_component() //将struct snd_soc_dai添加进dai_list链表中!
codec部分:
snd_soc_register_codec()
codec、codec_dai注册进codec_list和dai_list中
codec_list前者负责codec的逻辑配置(可控tinymix下的属性)。dai_list是codec接口部分描述,如i2s or pcm
machine分析:
把具体电路板上的platform和codec指定到一块
结构snd_soc_dai_link来指定,这个结构会把platform与codec适配到一起。把platform、cpu_dai、codec_dai以及codec 适配到一起,形成一条audio in/out的通路 <device tree里面存在对应的框架>
注意: platform与codec的dai都会放到dai_list链表中
3,ALSA声卡注册过程
注册入口:devm_snd_soc_register_card 或者 snd_soc_register_card
snd_soc_register_card()
devm_snd_soc_register_card
snd_soc_register_card —— 注册声卡
snd_soc_instantiate_card —— 声卡初始化
soc_bind_dai_link —— 检查struct snd_soc_dai_link中,dai_list/codec_list/platform_list中是否存在;
snd_card_create —— ALSA核心函数,创建声卡,同时内部自动创建了control逻辑设备;
snd_soc_dapm_new_controls():DAPM音频电源动态管理;
soc_probe_link_components():调用soc_probe_codec/soc_probe_platform,分别初始化snd_soc_dai_link中指定的codec和platform驱动代码里的东西,包括DAPM,但不包括cpu_dai/codec_dai;
soc_probe_link_dais():初始化snd_soc_dai_link中指定的cpu_dai和codec_dai驱动里的东西;调用soc_new_pcm(),这个函数首先创建了pcm逻辑设备,调用的是snd_device_new()的PCM类型的封装函数snd_pcm_new(),接着调用snd_pcm_set_ops()来给创建的pcm逻辑设备的substream提供pcm操作,最后调用platform中的pcm_new()函数,这个函数用来分配初始化dma;
snd_soc_dai_set_fmt():如果提供回调,分别设置snd_soc_dai_link中的cpu_dai、codec_dai中的接口格式;
snd_card_regster():ALSA核心函数,注册声卡;
整个ALSA的架构,当用户在操作alsa设备节点时,通过ALSA架构,最终调用pcm逻辑设备的substream的pcm操作,这些操作是我们之前在调用soc_probe_link_dais()中,调用ALSA核心函数snd_pcm_set_ops()设置的,这些设置的回调函数,最终有分别调用了snd_soc_dai_link中指定的platform/cpu_dai/codec_dai的同名函数。
4,ALSA设备节点创建
/dev/snd/下面的设备节点是字符设备,kernel中注册字符设备需要两个函数:
register_chrdev():alsa_sound_init()中调用,主要用来向kernel注册一个主设备为116,次设备号范围从0-255的字符设备;
device_create():snd_card_register()中调用snd_register_device_all(),遍历snd_card中的devices链表,调用每个组件的回调函数,回调函数中调用了device_create()来产生设备节点;
说明:pcm逻辑设备在一般情况下,会创建两种设备节点,分别是playback类型的substream的设备节点和capture类型的substream的设备节点,也就是我们看到的/dev/snd/pcmc0d*p和/dev/snd/pcmc0d*c;substream数量可以有多个,但是大多数情况只有一个;snd_card的devices链表中的组件是通过snd_device_new()加入的,而snd_pcm_new()/snd_ctl_create()对snd_device_new()进行了封装;
5,tinyalsa调用流程
以pcm_open为例:
1,open字符设备节点,找到真正注册的设备open方法soc_pcm_open(来自/sound/soc/soc-pcm.c )
snd_pcm_open
snd_pcm_open_file
snd_pcm_open_substream
substream->ops->open
这个open函数是调用snd_pcm_set_ops()时设置的,soc_pcm_open(来自sound/soc/soc-pcm.c )
这个函数会分别调用cpu_dai、platform、codec_dai、dai_link中的start/open
2,类似的pcm_prepare():
实际上就是一个ioctl调用;
到内核代码中找snd_pcm_f_ops结构数组,找到unlocked_ioctl的函数指针snd_pcm_playback_ioctl,
snd_pcm_prepare
snd_pcm_action_prepare
snd_pcm_do_prepare
substream->ops->prepare()
最终调用soc_pcm_prepare(),进去后,会发现分别调用了:
platform->driver->ops->prepare():platform驱动中的snd_soc_platform_driver结构体中对应的接口;
codec_dai->driver->ops->prepare():platform驱动中的cpu_dai接口的snd_soc_dai_driver结构体中对应的接口;
cpu_dai->driver->ops->prepare():codec驱动中的codec_dai接口的snd_soc_dai_driver结构体中对应的接口;
so, tinyalsa上的控制修改,完全可以通过修改sound/soc/soc-pcm.c里面对应的函数,实现需求上的特殊修改。
最后,附kernel里面的sound源码路径:alsa分析源码路径kernel/sound
最后
以上就是细腻枫叶为你收集整理的ALSA驱动分析的全部内容,希望文章能够帮你解决ALSA驱动分析所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复