route_path添加过程分析

本篇博客主要分析如何将audio_paths添加到内核中

首先看一下,audio_paths的定义:

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" },

    { "Left Input Mixer", "Boost Switch", "Left Boost Mixer", },
    { "Left Input Mixer", NULL, "LINPUT1", },  /* Really Boost Switch */
    { "Left Input Mixer", NULL, "LINPUT2" },
    { "Left Input Mixer", NULL, "LINPUT3" },
   .....
};

在代码中,使用函数snd_soc_dapm_add_routes将route添加到内核中,如下所示:

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);
        .....
        route++;
    }

    return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes);

这个函数的关键就是snd_soc_dapm_add_route,在分析该函数之前,我们首先看一下route的种类:

1)常规route

{"sink" NULL "source"}

表示由route构造出来的path->connect = 1
2)sink widget是Mixer

{"Mixer", name1, "source1"}
{"Mixer", name2, "source2"}
即:
source1----->name1------>Mixer
source2----->name2------>Mixer

name1和name2代表两个snd_kcontrol_new,这两个kcontrol在Mixer widget中,该wiget含有:
a.Mixer本身的信息
b.2个snd_kcontrol_new: name1和name2
可以通过操作某个kcontrol来打开某条path
3)sink widget是Mux

{"Mux"  value1, "source1"}
{"Mux"  value2, "source2"}
source1 ---------value1
                  
                   
                    -----------Mux 


source2 --------- value2   

Mux widget含有:
a.Mux本身信息
b.1个snd_kcontrol_new,它有两种取值:value和value2

这三种route是如何转换成path的呢?
猜测:
1)首先要找出source widget和sink widget,至于kcontrol,这个地方不会去找。为什么?
上篇博客中已经介绍,widget中kcontrol的创建是在machine驱动中,而这个地方只是codec驱动,还没有创建kcontrol,因此它在这个地方是不会去找的。
2)构造path

path->source = wsource;
path->sink = wsink;

3)设置
path->connect的值:
a.如果kcontrol = NULL,则它是一条static path,此时path->connect=1.
b.如果kcontrol !=NULL,则它是一条dynamic path,它的path->connect是如何设置呢?
这个地方是根据widget sink的类型决定的,
b.1 sink widget为ADC、DAC等的path,该path一般来说都是直连的(无开关)

snd_soc_dapm_adc
snd_soc_dapm_dac
snd_soc_dapm_pga
snd_soc_dapm_out_drv
snd_soc_dapm_input
snd_soc_dapm_output
snd_soc_dapm_siggen
snd_soc_dapm_micbias
snd_soc_dapm_vmid
snd_soc_dapm_pre
snd_soc_dapm_post
snd_soc_dapm_supply
snd_soc_dapm_regulator_supply
snd_soc_dapm_aif_in
snd_soc_dapm_aif_out
snd_soc_dapm_dai

//将path放入dapm->card->paths链表中
list_add(&path->list, &dapm->card->paths);

//将path的sink放入到widget的sources链表中。如何理解:
//在widget中,source就是source,sink就是sink。而在audio_routes,即形成的path中,格式是sink---->source.
//因此path中的sink刚好对应的是widget中的source
list_add(&path->list_sink, &wsink->sources);
list_add(&path->list_source, &wsource->sinks);
path->connect = 1;

b.2 对于sink widget是Mixer的path,path->connect 需要根据寄存器的值进行确定

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);
                dapm_set_path_status

b.3 对于sink widget是Mux的path,path->connect需要根据寄存器的值进行确定

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]);
                dapm_set_path_status//读寄存器,通过读出来的值再去判断该path是否连通

b.4 对于sink widget可以动态拔插的headphone、mic、line、speaker,它默认将path->connect设置为0。以后再根据实际情况,将path->connect设置为1.
比如说,插上耳机,对应的path设置为1

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;

snd_soc_dapm_add_routes会导致path的创建,以及链表关系的形成,path->connect值的设置。但是注意一点:
Mux和Mixer中snd_kcontrol此时还没有被创建,snd_kcontrol的创建是在函数snd_soc_dapm_new_widgets中。

附:snd_soc_dapm_add_route源码

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
原文地址:https://www.cnblogs.com/-glb/p/14414358.html