alsa之dapm筆記

參考博客

宗旨

分析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鏈表之後則表示骯髒。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章