目录
1、PA wsa991x:
2、Cpu - FE:
3、ADSP:
4、codec:
A、Cpu_be_dai:
B、codec dai:
C、dai_link:
1)、dai_link的绑定:
2)、dai widget与dai widget的链接:
3)、dai widget与stream widget的链接:
D、codec route:
E、Codec与pa直接的widget链接
【目的】
结合SDM845处理器的Android内核代码来静态分析deep_buffer spker的playback路径,涉及dts的解析,pcm fe->dai be->dai_link->codec->pa->spker的整个流程。
【分析】
static const struct snd_soc_dapm_route wsa881x_audio_map[] = {
{"SWR DAC_Port", "Switch", "IN"},
{"RDAC", NULL, "SWR DAC_Port"},
{"SPKR PGA", NULL, "RDAC"},
{"SPKR", NULL, "SPKR PGA"},
};
PA的widget音频路径 IN ->(switch开关)SWR DAC_PORT->RDAC->SPKR PGA->SPKR
Dts可以看到该PA的描述
Sdm845-qrd-audio-overlay.dtsi中:
qcom,wsa-max-devs = <2>;
qcom,wsa-devs = <&wsa881x_0211>, <&wsa881x_0213>;
qcom,wsa-aux-dev-prefix = "SpkrLeft", "SpkrRight";
prefix的处理在machine的drv中:
Match id_table后->msm_wsa881x_init()
解析“qcom,wsa-aux-dev-prefix”,
msm_codec_conf[n].name_prefix保存pfefix name。
msm_aux_dev[n].init = msm_wsa881x_init;
card->codec_conf = msm_codec_conf;
在aux_dev初始化函数msm_wsa881x_init()中,填充了该codec的privdate的port属性pord_id,mask,rate,nun_ch,设置SpkrLeft IN/SpkrLeft SPKR、SpkrRight IN/SpkrRight SPKR这些端点widget->ignore_suspend = 1,及整条widget路径被suspend后,端点widget不会suspend。
Kcontrol添加prefix:
snd_soc_instantiate_card();
->soc_probe_link_components();
->soc_probe_component();
->soc_set_name_prefix(card, component);设置componnet->name_prefix为codec->codec_conf->name_prefix;
创建WIDGET:
snd_soc_dapm_new_widgets(card)
->Dapm_new_mixer()
/dapm_new_mux()
/dapm_new_pga()
->dapm_create_or_share_kcontrol();
->soc_dapm_prefix(dapm);
->snd_soc_cnew(..., name, prefix);
->if (prefix)
template.name = kasprintf(GFP_KERNEL, "%s %s", prefix, name);
->snd_ctl_new1(&template, data);
可以看到,Widget创建会对prefix解析,从component->prefix获取然后添加到widget的name前缀。
所以,PA的route中的widget都要添加prefix:SpkrLeft和SpkrRight
SpkrLeft IN -(switch)-> SpkrLeft SWR DAC_Port --> SpkrLeft SWR DAC_Port RDAC --> SpkrLeft SPKR PGA -->SpkrLeft SPKR
SpkrRight IN -(switch)-> SpkrRight SWR DAC_Port --> SpkrRight SWR DAC_Port RDAC --> SpkrRight SPKR PGA -->SpkrRight SPKR
Msm-dai-fe.c定义FE CPU DAI,属于platform的cpu dai,用于连接用户层。
.stream_name = "MultiMedia1 Playback",
.aif_name = "MM_DL1",
.stream_name = "MultiMedia1 Capture",
.aif_name = "MM_UL1",
.probe = fe_dai_probe
.stream_name = "MultiMedia2 Playback",
.aif_name = "MM_DL2",
.stream_name = "MultiMedia2 Capture",
.aif_name = "MM_UL2",
.stream_name = "VoIP Playback",
.aif_name = "VOIP_DL",
.stream_name = "VoIP Capture",
.aif_name = "VOIP_UL",
......
snd_soc_register_component(&pdev->dev, msm_fe_dai_component, msm_fe_dai, size);注册component进component_list链表。
在fe_dai_probe()中,静态widget的route添加:
对于playback和capture,只要定义了.stream_name和.aif_name,
静态定义route:
Stream_name -> aif_name,及stream_name的widget链接到aif_name的widget, ->snd_soc_dapm_add_routes();为该component的dapm添加route,实现
.stream_name -> .aif_name的链接。
.aif_name对应的widget在msm-pcm-routing-v2.c文件静态定义:
Static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = {
SND_SOC_DAPM_AIF_IN("MM_DL1", "MultiMedia1 Playback", 0, 0, 0, 0),
...
};
.stream_name对应的widget属于dai widget,在card实例化的时候动态创建:
->snd_soc_instantiate_card()
->soc_probe_link_dais(card, i, order);
->component = rtd->platform->component
->soc_probe_component(card, component);
->list_for_each_entry(dai, &component->dai_list, list)
->snd_soc_dapm_new_dai_widgets(dapm, dai);
//创建dai_widgets。
->if(dai->drivers->playback,.stream_name)
dai_widget.id = snd_soc_dapm_dai_in;
dai_widget.name = dai->drivers->playback,.stream_name;
dai_widget.sname = dai->drivers->playback,.stream_name;
snd_soc_dapm_new_control_unlocked(dapm, dai_widget);
...
list_add_tail(dai_widget->list, dapm->card->widgets);
可见,cpu dai widget在card实例化的时候动态创建,name为stream_name。
从而有了MultiMedia1 Playback(fe dai) --> MM_DL1
MultiMedia1 Capture(fe dai) --> MM_UL1
....
Msn-pcm-routing-v2.c静态定义widgets:
MM_DL1作为src有多个widget,到底哪个sink是MM_DL1的链接目标?
需要看dsp的配置文件mixer_paths_tavail.xml
这里规定了deep-buffer-playback的路由控件:
Audio Mixer:表示DSP路由功能;
MultiMedia1:表示deep-buffer-playback usacase对应的FE PCM,以dai-playback.stream为名;
SLIMBUS_0_RX:Speaker device所连接的BE DAI。
VAL:1表示连接,0表示断开连接。
所以该控件规则表示,把MultiMedia1 PCM与SLIMBUS_0_RX的BE DAI连接,BE DAI确定了连接的设备芯片。
Be dai定义在msm-dai-q6-v2.c,platform_driver_register(&msm_dai_q6_dev);
注册be platform设备:msm-dai-q6-dev
dts:msm-audio-lpass,dtsi中qcom,msm-dai-q6作为soc的platform设备,匹配compatible=”qcom,msm-dai-q6-dev”,进入msm_dai_q6_dev_probe()
-> snd_soc_register_component();
Struct snd_soc_dai_driver msm_dai_q6_slimbus_rx_dai[]
{
.stream_name = "Slimbus Playback",
.aif_name = "SLIMBUS_0_RX",
}
对应的dai probe函数msm_dai_q6_dai_probe()
->msm_dai_q6_dai_add_route()
->if (dai->driver->playback.stream_name &&
dai->driver->playback.aif_name)
->snd_soc_dapm_add_routes();为该be dai添加route:
.aif_name -> .stream_name
及:SLIMBUS_0_RX --> Slimbus Playback(BE dai)
MM_DL1 --> SLIMBUS_0_RX Audio Mixer --> SLIMBUS_0_RX --> Slimbus Playback (BE dai)
Adsp的配置文件hardware/qcom/audio/configs/sdm845/mixer_paths_tavail.xml
Cpu_be dai与codec dai都属于静态dai,两个静态dai widget之间的链接在card实例化的时候就已经完成了。
Msm-dai-q6-v2.c文件中,
static struct snd_soc_dai_driver msm_dai_q6_slimbus_rx_dai[] = {
.stream_name = "Slimbus Playback",
.id = XX,
}
在msm_dai_q6_init()中,注册platform设备驱动
Platform_dirver_register(&msm_dai_q6_dev);匹配dts:msm-audio-lpass.dtsi的soc平台设备:qcom,msm-dai-q6的compatible:“qcom,msm-dai-q6-dev”,根据ID来注册componnt:snd_soc_regiter_compnent(dev, component_drv, dai_drv);
->snd_soc_component_initialize();
->fmt_single_name();
->component->name=”msm-dai-q6-dev.16384”
->snd_soc_register_dais();
->soc_add_dai();
->if(!dai->name) dai->name = fmt_single_name(dev, id);
->dai->name = ”%s.%d”, dev->drv->name, dai->id
及dai->name = “msm-dai-q6-dev.16384”
->struct snd_soc_dai *dai = kzalloc();
Dai->driver = dai_driver;
List_add(dai->list, component->dai_list); 添加该dai_driver对应的dai到component的dai_list链表上。
Wcd934x.c中静态定义codec的dai_drive tavil_slim_dai
{
.name = “tavil_rx1”,
.stream_name = “AIF1 Playback”
}
通过snd_soc_register_codec()注册,component->name = tavil_codec,
component上有多个dai: tavil_slim_dai
一并注册到component->codec_dai_list链表上。
dai->name = dai[n].name 比如tavil_rx1。
Machine级别的dai_link用于链接cpu_dai和codec_dai,静态定义于sdm845.c文件中。
可以看到Slimbus Playback (BE dai)的widget定义如下:
static struct snd_soc_dai_link msm_tavil_be_dai_links[] =
{
.name = LPASS_BE_SLIMBUS_0_RX,
.stream_name = "Slimbus Playback",
.cpu_dai_name = "msm-dai-q6-dev.16384",
.codec_name = "tavil_codec",
.codec_dai_name = "tavil_rx1",
...
}
->snd_soc_instantiate_card(card);
->soc_bind_dai_link();
从component_list上查找cpu_name与component->name匹配的component,由于该dai_link没有定义cpu_name,根据
dai_link->cpu_of_node == component->of_node来匹配。
而dai_link->cpu_of_node
dai_link->platform_of_node
dai_link->codec_of_node
在machine的
msm_populate_dai_link_component_of_node()函数中填充,通过解析dts的”asoc-cpu”来获取cpu_of_node。
而component->of_node在be_dai注册时候通过snd_soc_register_component()添加。
绑定rtd->cpu_dai和rtd->codec_dai
->snd_soc_dapm_connect_dai_link_widgets(card);
->snd_soc_dapm_add_path();
根据playback和capture,链接cpu_dai到codec_dai。
及Slimbus Playback的widget --> AIF1 Playback的widget。
->snd_soc_instantiate_card();
->snd_soc_dapm_link_dai_widgets();
dai_widget在wcd934x.c的dai_driver->stream_name,card实例化的时候会为该dai_driver创建以.stream_name为名的dai widget;
stream_widget作为普通widget,静态定义在
tavil_dapm_widgets[] = {
SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM,
AIF1_PB, 0, tavil_codec_enable_rx,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
具有相同的stream_name,且类型为snd_soc_dapm_dai_in,
则AIF1 Playback --> AIF1 PB的weigit route。
在wcd934x-routing.h中,静态定义了该codec芯片内部的route,结合mix.xml文件:
{"SLIM RX0 MUX", "AIF1_PB", "AIF1 PB"},
{"SLIM RX0", NULL, "SLIM RX0 MUX"},
{"CDC_IF RX0 MUX", "SLIM RX0", "SLIM RX0"},
{"RX INT7_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"},
{"RX INT7_1 MIX1", NULL, "RX INT7_1 MIX1 INP0"},
{"RX INT7_1 INTERP", NULL, "RX INT7_1 MIX1"},
{"RX INT7 SEC MIX", NULL, "RX INT7_1 INTERP"},
{"RX INT7 MIX2", NULL, "RX INT7 SEC MIX"},
{"RX INT7 CHAIN", NULL, "RX INT7 MIX2"},
{"SPK1 OUT", NULL, "RX INT7 CHAIN"},
所以sperk在codec的widget的路径如下:
AIF1 Playback(dai widget) --> AIF1 Playback --> AIF1 PB --> SLIM RX0 MUX
--> SLIM RX0 --> CDC_IF RX0 MUX --> RX INT7_1 MIX1 INP0 --> RX INT7_1 MIX1
--> RX INT7_1 INTERP --> RX INT7 SEC MIX --> RX INT7 MIX2 --> RX INT7 CHAIN
-->SPK1 OUT
在dts:sdm845-audio-overlay.dtsi的card的routing中:
"SpkrLeft IN", "SPK1 OUT",
及SPK1 OUT --> SpkrLeft IN