zoukankan      html  css  js  c++  java
  • DAPM_widget_route_path简介

    移植的驱动程序中,可以播放声音但是不能录制声音。查看原理图:

    当录制声音时,模拟信号从MIC1进来,最终输入到编解码芯片的LINPUT1,然后经过一系列的开关和部件,到达ADC转换器,转换成数字信号后,传递给CPU。若将这条路径上涉及到的各个部件打开,需要设置一系列的寄存器。那么有哪些寄存器呢?打开WM8960的芯片手册,如下所示:

     将上面的图进行简化,如下所示:

     根据上篇博客中介绍的kcontrol内容,录音时需要将LINPUT1, LINPUT1 Switch,Left Boost Mixer,Boost Switch,Left Input Mixer以及Left ADC都要打开。我们仅仅是录音,就要去操作6个部件,即需要设置6个寄存器,这也太麻烦了。是否有改进的方法呢?此时DAPM就出场了,DAPM涉及route、path和widget这三个东西。我们首先了解一下DAPM的定义:

    DAPM是Dynamic Audio PowerManagement的缩写,直译过来就是动态音频电源管理的意思。

    DAPM是为了使基于Linux的移动设备上的音频子系统,在任何时候都工作在最小功耗状态下。

    DAPM对用户空间的应用程序来说是透明的,所有与电源相关的开关都在ASoc Core中完成。

    用户空间的应用程序无需对代码做出修改,也无需重新编译,DAPM根据当前激活的音频流(playback/capture)和声卡中的mixer等的配置来决定哪些音频控件的电源开关被打开或关闭。 

    对于Left Boost Mixer来说,只要LINPUT Switch中的一个被打开,那么Left Boost Mixer就应该被打开。因此对于应用程序而言,只要打开了LINPUT Switch中的3个中的某一个,那么Left Boost Mixer就应该被打开。
    Mixer有多个输入源,只要其中的某个开关使能,就顺便把Mixer使能。也就是说,应用程序不需要手动打开这个Mixer。

    对于A部分,可以用一个widget来描述,里面含有:
    a. 1个Mixer
    b. 3个开关,这些开关用kcontrol来表示,上面博客中说过,kcontrol中有info函数、put函数、get函数等等。

    在这里kcontrol中的put函数作了一些特殊的处理,我们知道在一般的kcontrol中,它只是设置自己的寄存器。但是这里不是这么做的,具体如何处理的,稍后会讲。

    在这里我们引入了Widget,通过Widget,可以减少对某些寄存器的操作。看一下,在wm8960中,对widget的定义。

    static const struct snd_soc_dapm_widget wm8960_dapm_widgets[] = {
    ...
    SND_SOC_DAPM_MIXER("Left Boost Mixer", WM8960_POWER1, 5, 0,
               wm8960_lin_boost, ARRAY_SIZE(wm8960_lin_boost)),
    ...
    };
    static const struct snd_kcontrol_new wm8960_lin_boost[] = {
    SOC_DAPM_SINGLE("LINPUT2 Switch", WM8960_LINPATH, 6, 1, 0),
    SOC_DAPM_SINGLE("LINPUT3 Switch", WM8960_LINPATH, 7, 1, 0),
    SOC_DAPM_SINGLE("LINPUT1 Switch", WM8960_LINPATH, 8, 1, 0),
    };

    用宏SND_SOC_DAPM_MIXER来表示一个Widget,用wm8960_lin_boost表示kcontrol。这些kcontrol,它使用的宏与普通的kcontrol所使用的宏不一样。
    对于普通的kcontrol,它使用的是SOC_SINGLE等来描述,而在这里它使用的是SOC_DAPM_SINGLE。它们的put函数是不同的。

     A部分是我们改进的一个地方,看一下那个简图还有哪些地方可以改进?

    对于简图中的LINPUT1、LINPUT2、LINPUT3这也是一些widget。显然,如果打开了LINPUT1 Switch,右边的Left Boost Mixer应该打开,左边的LINPUT1也应该被打开。
    如何知道LINPUT1 Switch左右两边连接哪些Widget呢?
    LINPUT 、LINPUT1 Switch 、Left Boost Mixer就是一个route。route可以用下面的形式进行表示:

    /*
     * DAPM audio route definition.
     *
     * Defines an audio route originating at source via control and finishing
     * at sink.
     */
    struct snd_soc_dapm_route {
        const char *sink;
        const char *control;
        const char *source;
    
        /* Note: currently only supported for links where source is a supply */
        int (*connected)(struct snd_soc_dapm_widget *source,
                 struct snd_soc_dapm_widget *sink);
    };
    static const struct snd_soc_dapm_route audio_paths[] = {
        { "Left Boost Mixer", "LINPUT1 Switch", "LINPUT1" },
        { "Left Boost Mixer", "LINPUT2 Switch", "LINPUT2" },
        { "Left Boost Mixer", "LINPUT3 Switch", "LINPUT3" },
    ...
    }

    从上面route的定义来看,它表明了kcontrol的名字,source widget的名字,sink widget的名字。我们需要将route转换成一个path
    看一下代码中,如何将一个route转换成一个path
    snd_soc_dapm_add_routes(dapm, audio_paths, ARRAY_SIZE(audio_paths));

    /**
     * snd_soc_dapm_add_routes - Add routes between DAPM widgets
     * @dapm: DAPM context
     * @route: audio routes
     * @num: number of routes
     *
     * Connects 2 dapm widgets together via a named audio path. The sink is
     * the widget receiving the audio signal, whilst the source is the sender
     * of the audio signal.
     *
     * Returns 0 for success else error. On error all resources can be freed
     * with a call to snd_soc_card_free().
     */
    int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm,
                    const struct snd_soc_dapm_route *route, int num)
    {
        int i, ret;
    
        for (i = 0; i < num; i++) {
            ret = snd_soc_dapm_add_route(dapm, route);
            if (ret < 0) {
                dev_err(dapm->dev, "Failed to add route %s->%s
    ",
                    route->source, route->sink);
                return ret;
            }
            route++;
        }
    
        return 0;
    }
    EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes);

    函数snd_soc_dapm_add_route,首先根据audio_paths中的名字找到相应的widget和kcontrol。找到对应的widget和kcontrol后,再去创建path。

    static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
                      const struct snd_soc_dapm_route *route)
    {
        struct snd_soc_dapm_path *path;
        struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w;
        struct snd_soc_dapm_widget *wtsource = NULL, *wtsink = NULL;
        const char *sink;
        const char *control = route->control;
        const char *source;
        char prefixed_sink[80];
        char prefixed_source[80];
        int ret = 0;
    
        if (dapm->codec && dapm->codec->name_prefix) {
            snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s",
                 dapm->codec->name_prefix, route->sink);
            sink = prefixed_sink;
            snprintf(prefixed_source, sizeof(prefixed_source), "%s %s",
                 dapm->codec->name_prefix, route->source);
            source = prefixed_source;
        } else {
            sink = route->sink;
            source = route->source;
        }
    
        /*
         * find src and dest widgets over all widgets but favor a widget from
         * current DAPM context
         */
        list_for_each_entry(w, &dapm->card->widgets, list) {
            if (!wsink && !(strcmp(w->name, sink))) {
                wtsink = w;
                if (w->dapm == dapm)
                    wsink = w;
                continue;
            }
            if (!wsource && !(strcmp(w->name, source))) {
                wtsource = w;
                if (w->dapm == dapm)
                    wsource = w;
            }
        }
        /* use widget from another DAPM context if not found from this */
        if (!wsink)
            wsink = wtsink;
        if (!wsource)
            wsource = wtsource;
    
        if (wsource == NULL || wsink == NULL)
            return -ENODEV;
    
        path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL);
        if (!path)
            return -ENOMEM;
    
        path->source = wsource;
        path->sink = wsink;
        path->connected = route->connected;
        INIT_LIST_HEAD(&path->list);
        INIT_LIST_HEAD(&path->list_source);
        INIT_LIST_HEAD(&path->list_sink);
    
        /* check for external widgets */
        if (wsink->id == snd_soc_dapm_input) {
            if (wsource->id == snd_soc_dapm_micbias ||
                wsource->id == snd_soc_dapm_mic ||
                wsource->id == snd_soc_dapm_line ||
                wsource->id == snd_soc_dapm_output)
                wsink->ext = 1;
        }
        if (wsource->id == snd_soc_dapm_output) {
            if (wsink->id == snd_soc_dapm_spk ||
                wsink->id == snd_soc_dapm_hp ||
                wsink->id == snd_soc_dapm_line ||
                wsink->id == snd_soc_dapm_input)
                wsource->ext = 1;
        }
    
        /* connect static paths */
        if (control == NULL) {
            list_add(&path->list, &dapm->card->paths);
            list_add(&path->list_sink, &wsink->sources);
            list_add(&path->list_source, &wsource->sinks);
            path->connect = 1;
            return 0;
        }
    
        /* connect dynamic paths */
        switch (wsink->id) {
        case snd_soc_dapm_adc:
        case snd_soc_dapm_dac:
        case snd_soc_dapm_pga:
        case snd_soc_dapm_out_drv:
        case snd_soc_dapm_input:
        case snd_soc_dapm_output:
        case snd_soc_dapm_siggen:
        case snd_soc_dapm_micbias:
        case snd_soc_dapm_vmid:
        case snd_soc_dapm_pre:
        case snd_soc_dapm_post:
        case snd_soc_dapm_supply:
        case snd_soc_dapm_regulator_supply:
        case snd_soc_dapm_aif_in:
        case snd_soc_dapm_aif_out:
        case snd_soc_dapm_dai:
            list_add(&path->list, &dapm->card->paths);
            list_add(&path->list_sink, &wsink->sources);
            list_add(&path->list_source, &wsource->sinks);
            path->connect = 1;
            return 0;
        case snd_soc_dapm_mux:
        case snd_soc_dapm_virt_mux:
        case snd_soc_dapm_value_mux:
            ret = dapm_connect_mux(dapm, wsource, wsink, path, control,
                &wsink->kcontrol_news[0]);
            if (ret != 0)
                goto err;
            break;
        case snd_soc_dapm_switch:
        case snd_soc_dapm_mixer:
        case snd_soc_dapm_mixer_named_ctl:
            ret = dapm_connect_mixer(dapm, wsource, wsink, path, control);
            if (ret != 0)
                goto err;
            break;
        case snd_soc_dapm_hp:
        case snd_soc_dapm_mic:
        case snd_soc_dapm_line:
        case snd_soc_dapm_spk:
            list_add(&path->list, &dapm->card->paths);
            list_add(&path->list_sink, &wsink->sources);
            list_add(&path->list_source, &wsource->sinks);
            path->connect = 0;
            return 0;
        }
        return 0;
    
    err:
        dev_warn(dapm->dev, "asoc: no dapm match for %s --> %s --> %s
    ",
             source, control, sink);
        kfree(path);
        return ret;
    }
    View Code
    /* dapm audio path between two widgets */
    struct snd_soc_dapm_path {
        const char *name;
        const char *long_name;
    
        /* source (input) and sink (output) widgets */
        struct snd_soc_dapm_widget *source;
        struct snd_soc_dapm_widget *sink;
        struct snd_kcontrol *kcontrol;
    
        /* status */
        u32 connect:1;    /* source and sink widgets are connected */
        u32 walked:1;    /* path has been walked */
        u32 weak:1;    /* path ignored for power management */
    
        int (*connected)(struct snd_soc_dapm_widget *source,
                 struct snd_soc_dapm_widget *sink);
    
        struct list_head list_source;
        struct list_head list_sink;
        struct list_head list;
    };

    至此,已经引入了wiget、route和path的概念。它们之间如何协同工作呢?

    要注意,本节的重点就是动态音频电源管理,省电是我们的宗旨。
    在上面我们已经说过,普通的kcontrol的put函数和DAPM中的put函数是不同的。那么DAPM中kcontrol中的put函数做了什么事情呢?

    SOC_DAPM_SINGLE("LINPUT1 Switch", WM8960_LINPATH, 8, 1, 0),
    /* dapm kcontrol types */
    #define SOC_DAPM_SINGLE(xname, reg, shift, max, invert) 
    {    .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, 
        .info = snd_soc_info_volsw, 
        .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, 
        .private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }

    看一下里面的put函数:

    snd_soc_dapm_put_volsw,它主要工作流程如下:

    a. 设置path connect等于1或0。使用tinymix,可以将LINPUT1 Switch打开或关闭,使能时,connnet=1,关闭时,connect=0.

    b. 

    snd_soc_dapm_mixer_update_power(widget, kcontrol, connect);
      dapm_power_widgets(widget->dapm, SND_SOC_DAPM_STREAM_NOP);

    在dapm_power_widgets函数中,主要工作如下:
    a.无人使用声卡,不设置不启动任何寄存器
    b.有APP使用声卡,也许会设置寄存器。为什么会使用也许,在回到这个问题之前,先引入complete path

    complete path介绍
    在wm8960寄存器简图中,有如下的path(只以LINPUT1为例)

    Path1: LINPUT1   LINPUT1 Switch   Left Boost Mixer
    Path2: Left Boost Mixer   Boost Switch  Left Input Mixer
    Path3: Left Input Mixer  Left ADC

    所谓complete path就是:

    LINPUT1---> LINPUT1 Switch---->Left Boost Mixer---->Boost Switch---->Left Input Mixer---->Left ADC

    在这条complete path中,总共有三条path

    static const struct snd_soc_dapm_route audio_paths[] = {
    { "Left Boost Mixer", "LINPUT1 Switch", "LINPUT1" },  //path1
    ...
    { "Left Input Mixer", "Boost Switch", "Left Boost Mixer", },   //path2
    ...
    { "Left ADC", NULL, "Left Input Mixer" },   //path3
    ...
    };

    基于省电的目的,complete上的各个path都是connect状态,并且有APP在使用声卡,才会启动所涉及的widget。这就回答了上面提到的也许二字。

    总结:
    a. tinymix设置普通的kcontrol:会直接设置寄存器
    b. tinymix设置DAPM的kcontrol:
    设置所在path的connect
    调用dapm_power_widgets
    c. tinyplay,tinycap在传输数据之前:
    调用dapm_power_widgets(dapm,event);
    d.dapm_power_widgets,在APP使用声卡的前提下,会找出complete path,设置上面的所有widget

    到这里,我们就可以回答本篇博客提出来的自己录制的声音,播放时,没有声音那个问题了。很大可能就是complete path上的某个path不是connect状态。

  • 相关阅读:
    LeetCode 230. Kth Smallest Element in a BST
    LeetCode 114. Flatten Binary Tree to Linked List
    LeetCode 222. Count Complete Tree Nodes
    LeetCode 129. Sum Root to Leaf Numbers
    LeetCode 113. Path Sum II
    LeetCode 257. Binary Tree Paths
    Java Convert String & Int
    Java Annotations
    LeetCode 236. Lowest Common Ancestor of a Binary Tree
    LeetCode 235. Lowest Common Ancestor of a Binary Search Tree
  • 原文地址:https://www.cnblogs.com/-glb/p/14411301.html
Copyright © 2011-2022 走看看