文不对题~不要大惊小怪
全部博文(41)
分类: LINUX
2017-01-04 00:59:29
1. ALSA(Advanced Linux Sound Architecture, Linux音频框架)
2. ASoC(Alsa System on Chip)
ASoC逻辑上由platform<->machine<->codec driver3部分组成.
3. 声卡注册关键流程(MSM8916+PM8916)
1) 关键数据结构
点击(此处)折叠或打开
struct snd_soc_card {
........
/* CPU <--> Codec DAI links */
struct snd_soc_dai_link *dai_link;
int num_links;
struct snd_soc_pcm_runtime *rtd;
int num_rtd;
........
}
struct snd_soc_pcm_runtime {
........}
struct snd_pcm_ops ops;
........
struct snd_soc_codec *codec;
struct snd_soc_platform *platform;
struct snd_soc_dai *codec_dai;
struct snd_soc_dai *cpu_dai;
........
声卡数据结构,在machine driver中注册,如下:
msm8x16_asoc_machine_probe()->snd_soc_register_card()->snd_soc_instantiate_card().
其中,dai_link来自machine driver的定义:
点击(此处)折叠或打开
static struct snd_soc_dai_link msm8x16_dai[] = {
/* FrontEnd DAI Links */}
........
/* BankEnd I2S DAI Links */
........
从定义中可以看出,dai_link包括FrondEnd和BankEnd两部分.
rtd是在声卡注册中动态填充,如下:
msm8x16_asoc_machine_probe()->snd_soc_register_card()->snd_soc_instantiate_card()->soc_bind_dai_link().通过soc_bind_dai_link()将platform<->machine<->codec3部分(QCOM实现中对应的是codec<->codec_dai<->platform<->cpu_dai4个部分)动态绑定起来. codec/codec_dai/platform/cpu_dai4各部分的理解是:
codec - codec driver中创建的codec,并加入到codec_list链表;
codec_dai - codec driver中创建的codec_dai,并加入到dai_list链表;
platform - platform driver中创建的platform,并加入到platform_list链表;
cpu_dai - cpu driver, 对应msm端的hardware(如Multimedia1/2..., slimbus, mi2s etc.)创建的soc_snc_dai,并加入到dai_list链表.(QC实现通常是snd_soc_register_component()->snd_soc_register_dai())
soc_bind_dai_link()将codec, codec_dai, platform_dai, cpu_dai绑定好以后,继续调用soc_new_pcm创建设备pcm/ctrl, 流程是:
msm8x16_asoc_machine_probe()->snd_soc_register_card()->snd_soc_instantiate_card()->soc_bind_dai_link()->soc_probe_link_dais()->soc_new_pcm().
soc_new_pcm()会进一步填充struct snd_pcm_ops ops数据结构, 同时创建pcm/ctrl设备加入到card的device队列, 然后创建substream设备,并绑定subtream和pcm.
设备创建完成后,会继续调用snd_card_register创建pcm/ctrl设备节点, 从而生成/snd/dev下的设备节点.
2) machine driver
msm8x16.c->msm8x16_asoc_machine_probe()
machine driver声卡创建入口,对应于dtsi中的定义的machine device.
对于codec/platform/cpu对应的设备,可以参考dtsi文件中对声卡对应的定义,以MSM8916+PM8916为例,dtsi中有如下定义:
点击(此处)折叠或打开
&soc {
sound {}
compatible = "qcom,msm8x16-audio-codec";
qcom,model = "msm8x16-snd-card-mtp";
......
asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1",
"msm-voip-dsp", "msm-pcm-voice", "msm-pcm-loopback",
"msm-compress-dsp", "msm-pcm-hostless", "msm-pcm-afe",
"msm-lsm-client", "msm-pcm-routing", "msm-pcm-lpa";
asoc-cpu = <&dai_pri_auxpcm>, <&dai_hdmi>,
<&dai_mi2s0>, <&dai_mi2s1>, <&dai_mi2s2>, <&dai_mi2s3>,
<&sb_0_rx>, <&sb_0_tx>, <&sb_1_rx>, <&sb_1_tx>,
<&sb_3_rx>, <&sb_3_tx>, <&sb_4_rx>, <&sb_4_tx>,
<&bt_sco_rx>, <&bt_sco_tx>, <&int_fm_rx>, <&int_fm_tx>,
<&afe_pcm_rx>, <&afe_pcm_tx>, <&afe_proxy_rx>, <&afe_proxy_tx>,
<&incall_record_rx>, <&incall_record_tx>, <&incall_music_rx>,
<&incall_music_2_rx>;
asoc-cpu-names = "msm-dai-q6-auxpcm.1", "msm-dai-q6-hdmi.8",
"msm-dai-q6-mi2s.0", "msm-dai-q6-mi2s.1",
"msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3",
"msm-dai-q6-dev.16384", "msm-dai-q6-dev.16385",
"msm-dai-q6-dev.16386", "msm-dai-q6-dev.16387",
"msm-dai-q6-dev.16390", "msm-dai-q6-dev.16391",
"msm-dai-q6-dev.16392", "msm-dai-q6-dev.16393",
"msm-dai-q6-dev.12288", "msm-dai-q6-dev.12289",
"msm-dai-q6-dev.12292", "msm-dai-q6-dev.12293",
"msm-dai-q6-dev.224", "msm-dai-q6-dev.225",
"msm-dai-q6-dev.241", "msm-dai-q6-dev.240",
"msm-dai-q6-dev.32771", "msm-dai-q6-dev.32772",
"msm-dai-q6-dev.32773", "msm-dai-q6-dev.32770";
asoc-codec = <&stub_codec>, <&pm8916_tombak_dig>;
}
对应的文件有如下:
msm_dai_fe.c
msm-dai-q6-v2.c
msm_dai-slim.c
3) platform driver
msm-pcm-routing-v2.c
msm-pcm-voice-v2.c
msm-pcm-voip-v2.c
msm-pcm-q6dsp-v2.c
4) codec driver
msm8x16-wcd.c->msm8x16_wcd_spmi_probe()->snd_soc_register_codec(), codec创建入口.创建的codec结构加入到codec_list链表, 根据snd_soc_dai_driver创建的snd_soc_dai结构加入到dai_list链表.
1. dapm wiget主要类型
AIF_IN - 输入
AIN_OUT - 输出
INPUT - 输入
OUTPUT - 输出
SWITCH - ON/OFF
PGA - ON/OFF
MUX - 多路输入,一路输出
MIXER - 多路输入,一路输出
2. dapm创建
machine driver probe中,创建card的时候,会bind cpu_dai/codec_dai/platfrom.
msm8x16_asoc_machine_probe()->snd_soc_register_card()->snd_soc_instantiate_card()->soc_probe_link_components()
soc_probe_link_components()会依次调用已bind到snd_soc_pcm_runtime中的codec_driver->probe(), platform_driver->probe().
例如,
msm8x16-wcd.c中,codec_driver->probe()中会将codec侧的widget加入到card->widget中;
msm-pcm-routing-v2.c中,platform_driver->probe()中会将platform侧的widget加入card->widget中。
3. 音频PATH的配置
1) 需要参考codec内部结构以及route式样
2) 配置codec driver的snd_kcontrol_new, snd_soc_dapm_widget, snd_soc_dapm_route
snd_soc_dapm_route就是通过snd_kcontrol_new把snd_soc_dapm_widget连接起来,形成snd_soc_dapm_path.
3) snd_soc_dapm_route的结构与mixer paths中ctrl是对应的,其中每一个ctrl是widget按一定规则的组合.一条audio path是从始到终的与widget相关联的数据流向.
4. dapm上电
dapm要给一个widget上电的其中一个前提条件是:这个widget位于一条完整的音频路径上,而一条完整的音频路径的两头,必须是输入/输出引脚,或者是一个外部音频设备(哪些是外部音频设备,snd_soc_dapm_add_route()中source是外部音频设备的path,对应的sink的ext标志会设置成1),又或者是一个处于激活状态的音频流widget.
snd_soc_dapm_new_control() -> 创建dapm widget并根据widget类型赋值power_check()函数.
以按键音播放为例,当按键音播放的时候,播放device默认选择speaker,wideget此时调用的堆栈:
snd_pcm_playback_ioctl1--->
snd_pcm_common_ioctl1--->
snd_pcm_action_nonatomic--->
snd_pcm_action_single--->
snd_pcm_do_prepare--->
dpcm_fe_dai_prepare--->
soc_pcm_prepare--->
snd_soc_dapm_stream_event--->
dpcm_be_dai_prepare_async--->
soc_pcm_prepare--->
snd_soc_dapm_stream_event--->
dapm_power_widgets--->
dapm_widget_power_check--->
dapm_generic_check_power
以上发生在按键音播放,往pcm节点写数据的时刻.(需要注意的是,以上的流程应该是针对stream类型的widget。widget的类型可以网上搜)
调用到设备节点的ops结构const struct file_operations snd_pcm_f_ops的ioctl回调函数,ioctl回调函数调用prepare接口,继而调用到power_check函数.