參考博客
宗旨
分析dapm的代碼不應以弄清除dapm原理爲目標,而應該是出於可以看懂和編寫codec或者platform驅動目的。畢竟代碼萬萬,linux kernel已經封裝的接口又不需要驅動開發者實現。驅動開發者不應該浪費有限的生命。
snd_soc_dapm_context
dapm把整個音頻系統,按照功能和偏置電壓級別,劃分爲若干個電源域,每個域包含各自的widget,每個域中的所有widget通常都處於同一個偏置電壓級別上,而一個電源域就是一個dapm context。snd_soc_dapm_context被內嵌到代表codec、platform、card、dai的結構體中:
struct snd_soc_codec {
......
struct snd_soc_dapm_context dapm;
......
};
struct snd_soc_platform {
......
struct snd_soc_dapm_context dapm;
......
};
struct snd_soc_card {
......
struct snd_soc_dapm_context dapm;
......
};
struct snd_soc_dai {
......
struct snd_soc_dapm_context dapm;
......
};
snd_soc_dapm_widget
所有snd_soc_dapm_widget註冊時都掛載在snd_soc_card的widget鏈表。
struct snd_soc_card {
......
struct list_head widgets;
......
};
DAPM之內部API
- dapm_widget_power_check
static int dapm_widget_power_check(struct snd_soc_dapm_widget *w)
{
if (w->power_checked)
return w->new_power;
if (w->force)
w->new_power = 1;
else
w->new_power = w->power_check(w);
w->power_checked = true;
return w->new_power;
}
/* Generic check to see if a widget should be powered.
*/
static int dapm_generic_check_power(struct snd_soc_dapm_widget *w)
{
int in, out;
DAPM_UPDATE_STAT(w, power_checks);
in = is_connected_input_ep(w);
dapm_clear_walk(w->dapm);
out = is_connected_output_ep(w);
dapm_clear_walk(w->dapm);
return out != 0 && in != 0;
}
檢查snd_soc_dapm_widget需要上電還是下電,也就是新的電源狀態。這裏power一詞應該用英語的角度去理解,power up/down,power意思處理電源。dapm_generic_check_power函數是snd_soc_dapm_widget的check_power成員函數的一個特例,函數思路是如果同時連接到激活的輸入端(is_connected_input_ep非零)和激活的輸出端(is_connected_output_ep非零)則返回1(也就是需要上電),否則返回0(也就是需要下電)。容易理解,既然一條線路輸入輸出都已經上電,那麼中間路徑的控件自然需要上電。
- dapm_power_widgets
/*
* Scan each dapm widget for complete audio path.
* A complete path is a route that has valid endpoints i.e.:-
*
* o DAC to output pin.
* o Input Pin to ADC.
* o Input pin to Output pin (bypass, sidetone)
* o DAC to ADC (loopback).
*/
static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
{
...
/* Check which widgets we need to power and store them in
* lists indicating if they should be powered up or down. We
* only check widgets that have been flagged as dirty but note
* that new widgets may be added to the dirty list while we
* iterate.
*/
list_for_each_entry(w, &card->dapm_dirty, dirty) {
dapm_power_one_widget(w, &up_list, &down_list);
}
...
dapm_seq_run(dapm, &down_list, event, false);
...
dapm_seq_run(dapm, &up_list, event, true);
...
}
dapm_power_widgets處理widgets電源,將聲卡所有骯髒widgets電源進行處理,該上電的上電,該下電的下電。函數思路是遍歷聲卡骯髒widget列表,按照固定下/上電順序插入到down_list/up_list列表。然後,先將down_list的widget下電。最後將up_list的widget上電。值得注意的是dapm_power_one_widget除了將widget插入到down_list或者up_list外,還會將通過snd_soc_dapm_path連接相鄰widget污染(自己要上電,則告訴相鄰的widget也要上電;反之下電亦然)。代碼參考dapm_widget_set_power函數,此處不做詳述。
DAPM之外部API
- dapm_mark_dirty
static bool dapm_dirty_widget(struct snd_soc_dapm_widget *w)
{
return !list_empty(&w->dirty);
}
void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason)
{
if (!dapm_dirty_widget(w)) {
dev_vdbg(w->dapm->dev, "Marking %s dirty due to %s\n",
w->name, reason);
list_add_tail(&w->dirty, &w->dapm->card->dapm_dirty);
}
}
EXPORT_SYMBOL_GPL(dapm_mark_dirty);
如果snd_soc_dapm_widget的dirty鏈表未加入任何鏈表則表示乾淨,加入snd_soc_card的dapm_dirty鏈表之後則表示骯髒。