SDM845的音频路径分析

目录

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的整个流程。 

【分析】

1、PA wsa991x:

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

2、Cpu - FE:

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

....

3、ADSP:

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)

4、codec:

Adsp的配置文件hardware/qcom/audio/configs/sdm845/mixer_paths_tavail.xml

    
        
        
        
        
        
        
        
        
        
    

Cpu_be dai与codec dai都属于静态dai,两个静态dai widget之间的链接在card实例化的时候就已经完成了。

A、Cpu_be_dai:

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链表上。

B、codec dai:

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。

C、dai_link:

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",
    ...
}

1)、dai_link的绑定:

->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

2)、dai widget与dai widget的链接:  

->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。

3)、dai widget与stream 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。

D、codec 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

E、Codec与pa直接的widget链接

在dts:sdm845-audio-overlay.dtsi的card的routing中:

"SpkrLeft IN", "SPK1 OUT",

SPK1 OUT --> SpkrLeft IN

你可能感兴趣的:(ALSA音频驱动)