参考博客

宗旨

分析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链表之后则表示肮脏。

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐