audio : machine,platform,codec.
machine : vbc-rxpx-codec-sc27xx.c //
platform : sprd-2stage-dmaengine-pcm.c
codec : sprd-codec.c
vbc-rxpx-codec-sc27xx.c
late_initcall_sync
platform_driver_register(&vbc_rxpx_codec_sc27xx_driver);
/* vbc_rxpx_codec_sc27xx_driver */
vbc_rxpx_codec_sc27xx_probe
asoc_sprd_card_ops.p.ops = sprd_dai_link_ops
/* parsing dt config, and fill the struct snd_soc_card */
asoc_sprd_card_probe(pdev, &card)
priv->snd_card.owner = THIS_MODULE;
priv->snd_card.dev = dev;
dai_link = priv->dai_link;
priv->snd_card.dai_link = dai_link;
priv->snd_card.num_links = num_links;//6
asoc_sprd_card_parse_of(np, priv)
snd_soc_of_parse_card_name(&priv->snd_card, "sprd-audio-card,name")//snd_soc_card->name = sprdphone
snd_soc_of_parse_audio_simple_widgets(&priv->snd_card,"sprd-audio-card,widgets")
card->of_dapm_widgets = widgets;//inter HP PA,inter Spk PA,inter Ear PA
card->num_of_dapm_widgets = num_widgets;//3
snd_soc_of_parse_audio_routing(&priv->snd_card,"sprd-audio-card,routing")
card->num_of_dapm_routes = num_routes;
card->of_dapm_routes = routes;
routes[i].sink//HPMIC,MIC,AUXMIC,inter HP PA,inter Spk PA...
routes[i].source//HP Mic Jack,Mic Jack,Aux Mic Jack,HP PA,Spk PA...
of_property_read_u32(node, "sprd-audio-card,fm-hw-rate", &val)//sprd_card_data->fm_hw_rate = 32000
of_property_read_u32(node, "sprd-audio-card,codec-replace-adc-rate",&val)//sprd_card_data->codec_replace_adc_rate = 48000
of_property_read_u32(node, "sprd-audio-card,fm-open-src", &val)//sprd_card_data->is_fm_open_src = 1
asoc_sprd_card_dai_link_of(np, priv, i, false)
asoc_sprd_card_sub_parse_of(cpu, &dai_props->cpu_dai,&dai_link->cpu_of_node,&dai_link->cpu_dai_name, &cpu_args)//get cpu_dai name
asoc_sprd_card_sub_parse_of(codec, &dai_props->codec_dai,&dai_link->codec_of_node,&dai_link->codec_dai_name, NULL)//get codec_dai name
/* copy cpu_dai_name/codec_dai_name to dai_link->stream_name and dai_link->name */
sprintf(name, "%s-%s", dai_link->cpu_dai_name,dai_link->codec_dai_name);
dai_link->name = dai_link->stream_name = name
/* Sprd headset */
asoc_sprd_card_parse_sprd_headset(node)
sprd_headset_probe(h_pdev)//probe sprd headset...
/* sprd_asoc_card_parse_ext_hook(dev, &priv->ext_hook) sprd_asoc_card_parse_smartamp_boost(dev, &priv->boost_data) */ //not use
sprd_asoc_board_comm_probe()
board_inter_pa_init()
board_pa_type_check(id)//id = BOARD_FUNC_SPK/BOARD_FUNC_SPK1/BOARD_FUNC_EAR/BOARD_FUNC_HP
board_ext_hook
SAFE_CALL(ext_hook->ext_ctrl[ext_ctrl_id], func_id, on)//
card->controls = sprd_asoc_card_controls.ptr
card->num_controls = sprd_asoc_card_controls.size
card->dapm_widgets = sprd_asoc_card_widgets.ptr
card->num_dapm_widgets = sprd_asoc_card_widgets.size
card->late_probe = vbc_rxpx_codec_sc27xx_late_probe
asoc_sprd_register_card(&pdev->dev, card)
devm_snd_soc_register_card(dev, card)
snd_soc_register_card(card)
snd_soc_init_multicodec(card, link)
snd_soc_dai_link->codecs[0].name = dai_link->codec_name
snd_soc_dai_link->codecs[0].of_node = dai_link->codec_of_node
snd_soc_dai_link->codecs[0].dai_name = dai_link->codec_dai_name
snd_soc_initialize_card_lists(card)
INIT_LIST_HEAD(&card->dapm_dirty)
INIT_LIST_HEAD(&card->dobj_list)
snd_soc_instantiate_card(card)
soc_bind_dai_link(card, i)
snd_soc_dai_link_component.name = dai_link->cpu_name
snd_soc_dai_link_component.of_node = dai_link->cpu_of_node
snd_soc_dai_link_component.dai_name = dai_link->cpu_dai_name
/* Single codec links expect codec and codec_dai in runtime data */
rtd->num_codecs = dai_link->num_codecs
rtd->codec_dai = codec_dais[0]
rtd->codec = rtd->codec_dai->codec
rtd->platform = platform
soc_bind_aux_dev(card, i)
snd_soc_init_codec_cache(codec)
snd_soc_cache_init(codec)
codec->reg_cache = kzalloc(reg_size, GFP_KERNEL)
snd_card_new(card->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,card->owner, 0, &card->snd_card)
snd_soc_dapm_new_controls(&card->dapm, card->dapm_widgets,card->num_dapm_widgets)
snd_soc_dapm_new_control_unlocked(dapm, widget)
/* create a new dapm widget */
dapm_cnew_widget(widget)
switch (w->id) snd_soc_dapm_regulator_supply/snd_soc_dapm_clock_supply
switch (w->id) //enum snd_soc_dapm_type
w->power_check = dapm_generic_check_power
w->dapm = dapm;//snd_soc_dapm_widget->dapm = snd_soc_card->dapm
INIT_LIST_HEAD(&w->list);
INIT_LIST_HEAD(&w->dirty);
list_add_tail(&w->list, &dapm->card->widgets);//sprd_asoc_card_widgets.ptr
/* probe all components used by DAI links on this card */
soc_probe_link_components(card, i, order)
/* probe all DAI links on this card */
soc_probe_link_dais(card, i, order)
soc_probe_dai(cpu_dai, order)
ret = dai->driver->probe(dai)
ret = dai_link->init(rtd)
soc_dpcm_debugfs_add(rtd)
soc_new_pcm(rtd, num)
snd_soc_dapm_link_dai_widgets(card)
snd_soc_dapm_add_path(w->dapm, src, sink, NULL, NULL)
...
snd_soc_dapm_connect_dai_link_widgets(card)
...
snd_soc_add_card_controls(card, card->controls, card->num_controls)
...
snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes,card->num_dapm_routes)
...
ret = card->late_probe(card)//card->late_probe = vbc_rxpx_codec_sc27xx_late_probe
snd_soc_dapm_new_widgets(card)
list_for_each_entry(w, &card->widgets, list)//遍历所有已经注册了的widget
if (w->new)//new字段用于判断该widget是否已经执行过snd_soc_dapm_new_widgets函数
if (w->num_kcontrols)//如果num_kcontrols字段有数值,表明该widget包含有若干个dapm kcontrol
dapm_new_mixer() // 对于mixer类型,用该函数创建dapm kcontrol
for (i = 0; i < w->num_kcontrols; i++)//根据kcontrol的数量做循环,逐个建立对应的kcontrol
dapm_create_or_share_kcontrol(w, i)
dapm_kcontrol_add_path(w->kcontrols[i], path)
list_add_tail(&path->list_kcontrol, &data->paths)//把kcontrol连接的path(path->list_kcontrol)加入到paths(dapm_kcontrol_data->paths)链表中
snd_soc_dapm_add_path(data->widget->dapm,data->widget,path->source, NULL, NULL)//增加一个虚拟的影子widget,该影子widget连接和输入端对应的源widget
dapm_new_mux() // 对于mux类型,用该函数创建dapm kcontrol
if (w->num_kcontrols != 1)//mux类型的widget,只有一个kcontrol
dapm_create_or_share_kcontrol(w, 0)//使用dapm_create_or_share_mixmux_kcontrol来创建这个kcontrol
dapm_kcontrol_add_path(w->kcontrols[0], path)//对每个输入端所连接的path都加入dapm_kcontrol_data结构的paths链表中,并且创建一个影子widget,用于支持autodisab
dapm_new_pga()// 对于pga类型,用该函数创建dapm kcontrol
dapm_create_or_share_kcontrol(w, i)
if (w->reg >= 0)//根据widget寄存器的当前值,初始化widget的电源状态,并设置到power字段
dapm_mark_dirty(w, "new widget")//widget加入到声卡的dapm_dirty链表中,表明该widget的状态发生了变化
dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP)//统一处理所有位于dapm_dirty链表上的widget的状态改变
snd_card_register(card->snd_card)
sprd-audio,controlCx,snd_kcontrol_new
_sprd_asoc_card_controls[]
struct sprd_array_size sprd_asoc_card_controls
card->controls = sprd_asoc_card_controls.ptr
asoc_sprd_register_card
devm_snd_soc_register_card(dev, card)
snd_soc_register_card(card)
snd_soc_instantiate_card(card)
if (card->controls)
snd_soc_add_card_controls(card, card->controls, card->num_controls)
snd_soc_add_controls(card, soc_card->dev, controls, num_controls, NULL, soc_card)
snd_ctl_add(card, snd_soc_cnew(control, data, control->name, prefix))
snd_soc_cnew(control, data, control->name, prefix)
kcontrol = snd_ctl_new1(&template, data)
kctl->put = ncontrol->put
list_add_tail(&kcontrol->list, &card->controls)