ALSA driver---DAPM 2

定义widget

There are 4 power domains within DAPM:

  1. Codec domain – VREF, VMID (core codec and audio power). Usually controlled at codec probe/remove and suspend/resume, although can be set at stream time if power is not needed for sidetone, etc.
  2. Platform/Machine domain – physically connected inputs and outputs. Is platform/machine and user action specific, is configured by the machine driver and responds to asynchronous events. e.g when HP are inserted
  3. Path domain – audio subsystem signal paths. Automatically set when mixer and mux settings are changed by the user. e.g. alsamixer, amixer.
  4. Stream domain – DAC's and ADC's. Enabled and disabled when stream playback/capture is started and stopped respectively. e.g. aplay, arecord.

DAPM框架为我们提供了大量的辅助宏用来定义各种各样的widget控件.

widget 结构体如下:

* dapm widget */
struct snd_soc_dapm_widget {
    enum snd_soc_dapm_type id;
    const char *name;        /* widget name */
    const char *sname;    /* stream name */
    struct list_head list;
    struct snd_soc_dapm_context *dapm;

    void *priv;                /* widget specific data */
    struct regulator *regulator;        /* attached regulator */
    const struct snd_soc_pcm_stream *params; /* params for dai links */
    unsigned int num_params; /* number of params for dai links */
    unsigned int params_select; /* currently selected param for dai link */

    /* dapm control */
    int reg;                /* negative reg = no direct dapm */
    unsigned char shift;            /* bits to shift */
    unsigned int mask;            /* non-shifted mask */
    unsigned int on_val;            /* on state value */
    unsigned int off_val;            /* off state value */
    unsigned char power:1;            /* block power status */
    unsigned char active:1;            /* active stream on DAC, ADC's */
    unsigned char connected:1;        /* connected codec pin */
    unsigned char new:1;            /* cnew complete */
    unsigned char force:1;            /* force state */
    unsigned char ignore_suspend:1;         /* kept enabled over suspend */
    unsigned char new_power:1;        /* power from this run */
    unsigned char power_checked:1;        /* power checked this run */
    unsigned char is_supply:1;        /* Widget is a supply type widget */
    unsigned char is_ep:2;            /* Widget is a endpoint type widget */
    int subseq;                /* sort within widget type */

    int (*power_check)(struct snd_soc_dapm_widget *w);

    /* external events */
    unsigned short event_flags;        /* flags to specify event types */
    int (*event)(struct snd_soc_dapm_widget*, struct snd_kcontrol *, int);

    /* kcontrols that relate to this widget */
    int num_kcontrols;
    const struct snd_kcontrol_new *kcontrol_news;
    struct snd_kcontrol **kcontrols;
    struct snd_soc_dobj dobj;

    /* widget input and output edges */
    struct list_head edges[2];

    /* used during DAPM updates */
    struct list_head work_list;
    struct list_head power_list;
    struct list_head dirty;
    int endpoints[2];

    struct clk *clk;
};

widget的type:

/* dapm widget types */
enum snd_soc_dapm_type {
    snd_soc_dapm_input = 0,        /* input pin */
    snd_soc_dapm_output,        /* output pin */
    snd_soc_dapm_mux,            /* selects 1 analog signal from many inputs */
    snd_soc_dapm_demux,            /* connects the input to one of multiple outputs */
    snd_soc_dapm_mixer,            /* mixes several analog signals together */
    snd_soc_dapm_mixer_named_ctl,        /* mixer with named controls */
    snd_soc_dapm_pga,            /* programmable gain/attenuation (volume) */
    snd_soc_dapm_out_drv,            /* output driver */
    snd_soc_dapm_adc,            /* analog to digital converter */
    snd_soc_dapm_dac,            /* digital to analog converter */
    snd_soc_dapm_micbias,        /* microphone bias (power) - DEPRECATED: use snd_soc_dapm_supply */
    snd_soc_dapm_mic,            /* microphone */
    snd_soc_dapm_hp,            /* headphones */
    snd_soc_dapm_spk,            /* speaker */
    snd_soc_dapm_line,            /* line input/output */
    snd_soc_dapm_switch,        /* analog switch */
    snd_soc_dapm_vmid,            /* codec bias/vmid - to minimise pops */
    snd_soc_dapm_pre,            /* machine specific pre widget - exec first */
    snd_soc_dapm_post,            /* machine specific post widget - exec last */
    snd_soc_dapm_supply,        /* power/clock supply */
    snd_soc_dapm_regulator_supply,    /* external regulator */
    snd_soc_dapm_clock_supply,    /* external clock */
    snd_soc_dapm_aif_in,        /* audio interface input */
    snd_soc_dapm_aif_out,        /* audio interface output */
    snd_soc_dapm_siggen,        /* signal generator */
    snd_soc_dapm_sink,
    snd_soc_dapm_dai_in,        /* link to DAI structure */
    snd_soc_dapm_dai_out,
    snd_soc_dapm_dai_link,        /* link between two DAI structures */
    snd_soc_dapm_kcontrol,        /* Auto-disabled kcontrol */
};

 

codec domain:

/* codec domain */
#define SND_SOC_DAPM_VMID(wname) \
{    .id = snd_soc_dapm_vmid, .name = wname, .kcontrol_news = NULL, \
    .num_kcontrols = 0}

 

platform domain:

platform domain的widget分别对应信号发生器,输入引脚,输出引脚,麦克风,耳机,扬声器,线路输入接口。其中的reg字段被设置为SND_SOC_NOPM(-1),表明这些widget是没有寄存器控制位来控制widget的电源状态的。麦克风,耳机,扬声器,线路输入接口这几种widget,还可以定义一个dapm事件回调函数wevent,从event_flags字段的设置可以看出,他们只会响应SND_SOC_DAPM_POST_PMU(上电后)和SND_SOC_DAPM_PMD(下电前)事件,这几个widget通常会在machine驱动中定义,而SND_SOC_DAPM_INPUT和SND_SOC_DAPM_OUTPUT则用来定义codec芯片的输出输入脚,通常在codec驱动中定义,最后,在machine驱动中增加相应的route,把麦克风和耳机等widget与相应的codec输入输出引脚的widget连接起来。

/* platform domain */
#define SND_SOC_DAPM_SIGGEN(wname) \
{    .id = snd_soc_dapm_siggen, .name = wname, .kcontrol_news = NULL, \
    .num_kcontrols = 0, .reg = SND_SOC_NOPM }
#define SND_SOC_DAPM_SINK(wname) \
{    .id = snd_soc_dapm_sink, .name = wname, .kcontrol_news = NULL, \
    .num_kcontrols = 0, .reg = SND_SOC_NOPM }
#define SND_SOC_DAPM_INPUT(wname) \
{    .id = snd_soc_dapm_input, .name = wname, .kcontrol_news = NULL, \
    .num_kcontrols = 0, .reg = SND_SOC_NOPM }
#define SND_SOC_DAPM_OUTPUT(wname) \
{    .id = snd_soc_dapm_output, .name = wname, .kcontrol_news = NULL, \
    .num_kcontrols = 0, .reg = SND_SOC_NOPM }
#define SND_SOC_DAPM_MIC(wname, wevent) \
{    .id = snd_soc_dapm_mic, .name = wname, .kcontrol_news = NULL, \
    .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
    .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD}
#define SND_SOC_DAPM_HP(wname, wevent) \
{    .id = snd_soc_dapm_hp, .name = wname, .kcontrol_news = NULL, \
    .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
    .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD}
#define SND_SOC_DAPM_SPK(wname, wevent) \
{    .id = snd_soc_dapm_spk, .name = wname, .kcontrol_news = NULL, \
    .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
    .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD}
#define SND_SOC_DAPM_LINE(wname, wevent) \
{    .id = snd_soc_dapm_line, .name = wname, .kcontrol_news = NULL, \
    .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
    .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD}

  

path domain:

path domain 的widget通常是对普通kcontrols控件的再封装,增加音频路径和电源管理功能,所以这种widget会包含一个或多个kcontrol,这些widget的reg和shift字段是需要赋值的,说明这些widget是有相应的电源控制寄存器的,DAPM框架在扫描和更新音频路径时,会利用这些寄存器来控制widget的电源状态,使得它们的供电状态是按需分配的,需要的时候(在有效的音频路径上)上电,不需要的时候(不再有效的音频路径上)下电。

#define SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert) \
    .reg = wreg, .mask = 1, .shift = wshift, \
    .on_val = winvert ? 0 : 1, .off_val = winvert ? 1 : 0

/* path domain */
#define SND_SOC_DAPM_PGA(wname, wreg, wshift, winvert,\
     wcontrols, wncontrols) \
{    .id = snd_soc_dapm_pga, .name = wname, \
    SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
    .kcontrol_news = wcontrols, .num_kcontrols = wncontrols}
#define SND_SOC_DAPM_OUT_DRV(wname, wreg, wshift, winvert,\
     wcontrols, wncontrols) \
{    .id = snd_soc_dapm_out_drv, .name = wname, \
    SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
    .kcontrol_news = wcontrols, .num_kcontrols = wncontrols}
#define SND_SOC_DAPM_MIXER(wname, wreg, wshift, winvert, \
     wcontrols, wncontrols)\
{    .id = snd_soc_dapm_mixer, .name = wname, \
    SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
    .kcontrol_news = wcontrols, .num_kcontrols = wncontrols}
#define SND_SOC_DAPM_MIXER_NAMED_CTL(wname, wreg, wshift, winvert, \
     wcontrols, wncontrols)\
{       .id = snd_soc_dapm_mixer_named_ctl, .name = wname, \
    SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
    .kcontrol_news = wcontrols, .num_kcontrols = wncontrols}
/* DEPRECATED: use SND_SOC_DAPM_SUPPLY */
#define SND_SOC_DAPM_MICBIAS(wname, wreg, wshift, winvert) \
{    .id = snd_soc_dapm_micbias, .name = wname, \
    SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
    .kcontrol_news = NULL, .num_kcontrols = 0}
#define SND_SOC_DAPM_SWITCH(wname, wreg, wshift, winvert, wcontrols) \
{    .id = snd_soc_dapm_switch, .name = wname, \
    SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
    .kcontrol_news = wcontrols, .num_kcontrols = 1}
#define SND_SOC_DAPM_MUX(wname, wreg, wshift, winvert, wcontrols) \
{    .id = snd_soc_dapm_mux, .name = wname, \
    SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
    .kcontrol_news = wcontrols, .num_kcontrols = 1}
#define SND_SOC_DAPM_DEMUX(wname, wreg, wshift, winvert, wcontrols) \
{    .id = snd_soc_dapm_demux, .name = wname, \
    SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
    .kcontrol_news = wcontrols, .num_kcontrols = 1}

这些widget需要完成和的mixer、mux等控件同样的功能,实际上,这是通过它们包含的kcontrol控件来完成的,这些kcontrol我们需要在定义widget前先定义好,然后通过wcontrols和num_kcontrols参数传递给这些辅助定义宏。dapm利用这些kcontrol完成音频路径的控制。不过,对于widget来说,它的任务还不止这些,dapm还要动态地管理这些音频路径的连结关系,以便可以根据这些连接关系来控制这些widget的电源状态,如果按照普通的方法定义这些kcontrol,是无法达到这个目的的,因此,dapm为我们提供了另外一套定义宏,由它们完成这些被widget包含的kcontrol的定义。

/* 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, 0) }
#define SOC_DAPM_SINGLE_AUTODISABLE(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, 1) }
#define SOC_DAPM_SINGLE_VIRT(xname, max) \
    SOC_DAPM_SINGLE(xname, SND_SOC_NOPM, 0, max, 0)
#define SOC_DAPM_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array) \
{    .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
    .info = snd_soc_info_volsw, \
    .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE,\
    .tlv.p = (tlv_array), \
    .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
    .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) }
#define SOC_DAPM_SINGLE_TLV_AUTODISABLE(xname, reg, shift, max, invert, tlv_array) \
{    .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
    .info = snd_soc_info_volsw, \
    .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE,\
    .tlv.p = (tlv_array), \
    .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
    .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 1) }
#define SOC_DAPM_SINGLE_TLV_VIRT(xname, max, tlv_array) \
    SOC_DAPM_SINGLE(xname, SND_SOC_NOPM, 0, max, 0, tlv_array)
#define SOC_DAPM_ENUM(xname, xenum) \
{    .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
    .info = snd_soc_info_enum_double, \
     .get = snd_soc_dapm_get_enum_double, \
     .put = snd_soc_dapm_put_enum_double, \
      .private_value = (unsigned long)&xenum }
#define SOC_DAPM_ENUM_EXT(xname, xenum, xget, xput) \
{    .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
    .info = snd_soc_info_enum_double, \
    .get = xget, \
    .put = xput, \
    .private_value = (unsigned long)&xenum }
#define SOC_DAPM_PIN_SWITCH(xname) \
{    .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname " Switch", \
    .info = snd_soc_dapm_info_pin_switch, \
    .get = snd_soc_dapm_get_pin_switch, \
    .put = snd_soc_dapm_put_pin_switch, \
    .private_value = (unsigned long)xname }

以看出,SOC_DAPM_SINGLE对应与普通控件的SOC_SINGLE,SOC_DAPM_SINGLE_TLV对应SOC_SINGLE_TLV......,相比普通的kcontrol控件,dapm的kcontrol控件只是把info,get,put回调函数换掉了。dapm kcontrol的put回调函数不仅仅会更新控件本身的状态,他还会把这种变化传递到相邻的dapm kcontrol,相邻的dapm kcontrol又会传递这个变化到他自己相邻的dapm kcontrol,直到音频路径的末端,通过这种机制,只要改变其中一个widget的连接状态,与之相关的所有widget都会被扫描并测试一下自身是否还在有效的音频路径中,从而可以动态地改变自身的电源状态,这就是dapm的精髓所在。

 

stream domain:

这些widget主要包含音频输入/输出接口,ADC/DAC等等:

/* stream domain */
#define SND_SOC_DAPM_AIF_IN(wname, stname, wslot, wreg, wshift, winvert) \
{    .id = snd_soc_dapm_aif_in, .name = wname, .sname = stname, \
    SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), }
#define SND_SOC_DAPM_AIF_IN_E(wname, stname, wslot, wreg, wshift, winvert, \
                  wevent, wflags)                \
{    .id = snd_soc_dapm_aif_in, .name = wname, .sname = stname, \
    SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
    .event = wevent, .event_flags = wflags }
#define SND_SOC_DAPM_AIF_OUT(wname, stname, wslot, wreg, wshift, winvert) \
{    .id = snd_soc_dapm_aif_out, .name = wname, .sname = stname, \
    SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), }
#define SND_SOC_DAPM_AIF_OUT_E(wname, stname, wslot, wreg, wshift, winvert, \
                 wevent, wflags)                \
{    .id = snd_soc_dapm_aif_out, .name = wname, .sname = stname, \
    SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
    .event = wevent, .event_flags = wflags }
#define SND_SOC_DAPM_DAC(wname, stname, wreg, wshift, winvert) \
{    .id = snd_soc_dapm_dac, .name = wname, .sname = stname, \
    SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert) }
#define SND_SOC_DAPM_DAC_E(wname, stname, wreg, wshift, winvert, \
               wevent, wflags)                \
{    .id = snd_soc_dapm_dac, .name = wname, .sname = stname, \
    SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
    .event = wevent, .event_flags = wflags}

#define SND_SOC_DAPM_ADC(wname, stname, wreg, wshift, winvert) \
{    .id = snd_soc_dapm_adc, .name = wname, .sname = stname, \
    SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), }
#define SND_SOC_DAPM_ADC_E(wname, stname, wreg, wshift, winvert, \
               wevent, wflags)                \
{    .id = snd_soc_dapm_adc, .name = wname, .sname = stname, \
    SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
    .event = wevent, .event_flags = wflags}
#define SND_SOC_DAPM_CLOCK_SUPPLY(wname) \
{    .id = snd_soc_dapm_clock_supply, .name = wname, \
    .reg = SND_SOC_NOPM, .event = dapm_clock_event, \
    .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD }

定义route

route 表示widget的连接路径(Destination Widget <=== Path Name <=== Source Widget)。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);
};

 

以sound/soc/codecs/tlv320aic23.c 为例,以下是tlv320 codec driver定义的widget定义的widgets和route

DAPM Widgets:

static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = {
    SND_SOC_DAPM_DAC("DAC", "Playback", TLV320AIC23_PWR, 3, 1),
    SND_SOC_DAPM_ADC("ADC", "Capture", TLV320AIC23_PWR, 2, 1),
    SND_SOC_DAPM_MUX("Capture Source", SND_SOC_NOPM, 0, 0,
             &tlv320aic23_rec_src_mux_controls),
    SND_SOC_DAPM_MIXER("Output Mixer", TLV320AIC23_PWR, 4, 1,
               &tlv320aic23_output_mixer_controls[0],
               ARRAY_SIZE(tlv320aic23_output_mixer_controls)),
    SND_SOC_DAPM_PGA("Line Input", TLV320AIC23_PWR, 0, 1, NULL, 0),
    SND_SOC_DAPM_PGA("Mic Input", TLV320AIC23_PWR, 1, 1, NULL, 0),

    SND_SOC_DAPM_OUTPUT("LHPOUT"),
    SND_SOC_DAPM_OUTPUT("RHPOUT"),
    SND_SOC_DAPM_OUTPUT("LOUT"),
    SND_SOC_DAPM_OUTPUT("ROUT"),

    SND_SOC_DAPM_INPUT("LLINEIN"),
    SND_SOC_DAPM_INPUT("RLINEIN"),

    SND_SOC_DAPM_INPUT("MICIN"),
};

widget kcontrol:

/* PGA Mixer controls for Line and Mic switch */
static const struct snd_kcontrol_new tlv320aic23_output_mixer_controls[] = {
    SOC_DAPM_SINGLE("Line Bypass Switch", TLV320AIC23_ANLG, 3, 1, 0),
    SOC_DAPM_SINGLE("Mic Sidetone Switch", TLV320AIC23_ANLG, 5, 1, 0),
    SOC_DAPM_SINGLE("Playback Switch", TLV320AIC23_ANLG, 4, 1, 0),
};

DAPM routes:

static const struct snd_soc_dapm_route tlv320aic23_intercon[] = {
    /* Output Mixer */
    {"Output Mixer", "Line Bypass Switch", "Line Input"},
    {"Output Mixer", "Playback Switch", "DAC"},
    {"Output Mixer", "Mic Sidetone Switch", "Mic Input"},

    /* Outputs */
    {"RHPOUT", NULL, "Output Mixer"},
    {"LHPOUT", NULL, "Output Mixer"},
    {"LOUT", NULL, "Output Mixer"},
    {"ROUT", NULL, "Output Mixer"},

    /* Inputs */
    {"Line Input", "NULL", "LLINEIN"},
    {"Line Input", "NULL", "RLINEIN"},

    {"Mic Input", "NULL", "MICIN"},

    /* input mux */
    {"Capture Source", "Line", "Line Input"},
    {"Capture Source", "Mic", "Mic Input"},
    {"ADC", NULL, "Capture Source"},

};

capture audio path:

LLININ->Line Input->Catpture source ->ADC

MICIN->Mic Input->Catpute source->ADC

playback audio path:

DAC->Ouptput Mixer->LOUT/LHPOUT/ROUT/RHPOUT

LLININ->Line Input->Output Mixer->LOUT

MICIN->Mic Input ->Output Mixer->LOUT/LHPOUT/ROUT/RHPOUT

 

创建widget:

DAPM widget和route定义在CPU DAI driver和Codec driver的component driver.当调用snd_soc_register_component()注册CPU DAI , 调用snd_soc_register_codec()注册Codec时,都会创建snd_soc_component类型的component, 并调用snd_soc_component_initialize()将component driver中定义的widgets和route赋值给component。

 在snd_soc_instantiate_card()会调用soc_probe_link_component()->soc_probe_component()来probe component.

static int soc_probe_component(struct snd_soc_card *card,
    struct snd_soc_component *component)
{
    struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
    struct snd_soc_dai *dai;
    int ret;

    if (!strcmp(component->name, "snd-soc-dummy"))
        return 0;

    if (component->card) {
        if (component->card != card) {
            dev_err(component->dev,
                "Trying to bind component to card \"%s\" but is already bound to card \"%s\"\n",
                card->name, component->card->name);
            return -ENODEV;
        }
        return 0;
    }

    if (!try_module_get(component->dev->driver->owner))
        return -ENODEV;

    component->card = card;
    dapm->card = card;
    soc_set_name_prefix(card, component);

    soc_init_component_debugfs(component);

    if (component->dapm_widgets) {
        ret = snd_soc_dapm_new_controls(dapm, component->dapm_widgets,
            component->num_dapm_widgets);

        if (ret != 0) {
            dev_err(component->dev,
                "Failed to create new controls %d\n", ret);
            goto err_probe;
        }
    }

    list_for_each_entry(dai, &component->dai_list, list) {
        ret = snd_soc_dapm_new_dai_widgets(dapm, dai);
        if (ret != 0) {
            dev_err(component->dev,
                "Failed to create DAI widgets %d\n", ret);
            goto err_probe;
        }
    }

    if (component->probe) {
        ret = component->probe(component);
        if (ret < 0) {
            dev_err(component->dev,
                "ASoC: failed to probe component %d\n", ret);
            goto err_probe;
        }

        WARN(dapm->idle_bias_off &&
            dapm->bias_level != SND_SOC_BIAS_OFF,
            "codec %s can not start from non-off bias with idle_bias_off==1\n",
            component->name);
    }

    /* machine specific init */
    if (component->init) {
        ret = component->init(component);
        if (ret < 0) {
            dev_err(component->dev,
                "Failed to do machine specific init %d\n", ret);
            goto err_probe;
        }
    }

    if (component->controls)
        snd_soc_add_component_controls(component, component->controls,
                     component->num_controls);
    if (component->dapm_routes)
        snd_soc_dapm_add_routes(dapm, component->dapm_routes,
                    component->num_dapm_routes);

    list_add(&dapm->list, &card->dapm_list);

    /* This is a HACK and will be removed soon */
    if (component->codec)
        list_add(&component->codec->card_list, &card->codec_dev_list);

    return 0;

err_probe:
    soc_cleanup_component_debugfs(component);
    component->card = NULL;
    module_put(component->dev->driver->owner);

    return ret;
}

在soc_probe_component()里面,

调用snd_soc_dapm_new_controls()对component->dapm_widgets中每个widget创建新的widget,将widget加到card->widgets链表中。

/**
 * snd_soc_dapm_new_controls - create new dapm controls
 * @dapm: DAPM context
 * @widget: widget array
 * @num: number of widgets
 *
 * Creates new DAPM controls based upon the templates.
 *
 * Returns 0 for success else error.
 */
int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm,
    const struct snd_soc_dapm_widget *widget,
    int num)
{
    struct snd_soc_dapm_widget *w;
    int i;
    int ret = 0;

    mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
    for (i = 0; i < num; i++) {
        w = snd_soc_dapm_new_control_unlocked(dapm, widget);
        if (IS_ERR(w)) {
            ret = PTR_ERR(w);
            /* Do not nag about probe deferrals */
            if (ret == -EPROBE_DEFER)
                break;
            dev_err(dapm->dev,
                "ASoC: Failed to create DAPM control %s (%d)\n",
                widget->name, ret);
            break;
        }
        if (!w) {
            dev_err(dapm->dev,
                "ASoC: Failed to create DAPM control %s\n",
                widget->name);
            ret = -ENOMEM;
            break;
        }
        widget++;
    }
    mutex_unlock(&dapm->card->dapm_mutex);
    return ret;
}

创建widget由snd_soc_dapm_new_control_unlocked()完成,在此函数中设定每个widget的power_check()函数,并将创建的widget加到card->widget链表中。。

struct snd_soc_dapm_widget *
snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,
             const struct snd_soc_dapm_widget *widget)
{
    enum snd_soc_dapm_direction dir;
    struct snd_soc_dapm_widget *w;
    const char *prefix;
    int ret;

    if ((w = dapm_cnew_widget(widget)) == NULL)
        return NULL;

    switch (w->id) {
    case snd_soc_dapm_regulator_supply:
        w->regulator = devm_regulator_get(dapm->dev, w->name);
        if (IS_ERR(w->regulator)) {
            ret = PTR_ERR(w->regulator);
            if (ret == -EPROBE_DEFER)
                return ERR_PTR(ret);
            dev_err(dapm->dev, "ASoC: Failed to request %s: %d\n",
                w->name, ret);
            return NULL;
        }

        if (w->on_val & SND_SOC_DAPM_REGULATOR_BYPASS) {
            ret = regulator_allow_bypass(w->regulator, true);
            if (ret != 0)
                dev_warn(w->dapm->dev,
                     "ASoC: Failed to bypass %s: %d\n",
                     w->name, ret);
        }
        break;
    case snd_soc_dapm_clock_supply:
#ifdef CONFIG_CLKDEV_LOOKUP
        w->clk = devm_clk_get(dapm->dev, w->name);
        if (IS_ERR(w->clk)) {
            ret = PTR_ERR(w->clk);
            if (ret == -EPROBE_DEFER)
                return ERR_PTR(ret);
            dev_err(dapm->dev, "ASoC: Failed to request %s: %d\n",
                w->name, ret);
            return NULL;
        }
#else
        return NULL;
#endif
        break;
    default:
        break;
    }

    prefix = soc_dapm_prefix(dapm);
    if (prefix)
        w->name = kasprintf(GFP_KERNEL, "%s %s", prefix, widget->name);
    else
        w->name = kstrdup_const(widget->name, GFP_KERNEL);
    if (w->name == NULL) {
        kfree(w);
        return NULL;
    }

    switch (w->id) {
    case snd_soc_dapm_mic:
        w->is_ep = SND_SOC_DAPM_EP_SOURCE;
        w->power_check = dapm_generic_check_power;
        break;
    case snd_soc_dapm_input:
        if (!dapm->card->fully_routed)
            w->is_ep = SND_SOC_DAPM_EP_SOURCE;
        w->power_check = dapm_generic_check_power;
        break;
    case snd_soc_dapm_spk:
    case snd_soc_dapm_hp:
        w->is_ep = SND_SOC_DAPM_EP_SINK;
        w->power_check = dapm_generic_check_power;
        break;
    case snd_soc_dapm_output:
        if (!dapm->card->fully_routed)
            w->is_ep = SND_SOC_DAPM_EP_SINK;
        w->power_check = dapm_generic_check_power;
        break;
    case snd_soc_dapm_vmid:
    case snd_soc_dapm_siggen:
        w->is_ep = SND_SOC_DAPM_EP_SOURCE;
        w->power_check = dapm_always_on_check_power;
        break;
    case snd_soc_dapm_sink:
        w->is_ep = SND_SOC_DAPM_EP_SINK;
        w->power_check = dapm_always_on_check_power;
        break;

    case snd_soc_dapm_mux:
    case snd_soc_dapm_demux:
    case snd_soc_dapm_switch:
    case snd_soc_dapm_mixer:
    case snd_soc_dapm_mixer_named_ctl:
    case snd_soc_dapm_adc:
    case snd_soc_dapm_aif_out:
    case snd_soc_dapm_dac:
    case snd_soc_dapm_aif_in:
    case snd_soc_dapm_pga:
    case snd_soc_dapm_out_drv:
    case snd_soc_dapm_micbias:
    case snd_soc_dapm_line:
    case snd_soc_dapm_dai_link:
    case snd_soc_dapm_dai_out:
    case snd_soc_dapm_dai_in:
        w->power_check = dapm_generic_check_power;
        break;
    case snd_soc_dapm_supply:
    case snd_soc_dapm_regulator_supply:
    case snd_soc_dapm_clock_supply:
    case snd_soc_dapm_kcontrol:
        w->is_supply = 1;
        w->power_check = dapm_supply_check_power;
        break;
    default:
        w->power_check = dapm_always_on_check_power;
        break;
    }

    w->dapm = dapm;
    INIT_LIST_HEAD(&w->list);
    INIT_LIST_HEAD(&w->dirty);
    list_add_tail(&w->list, &dapm->card->widgets);

    snd_soc_dapm_for_each_direction(dir) {
        INIT_LIST_HEAD(&w->edges[dir]);
        w->endpoints[dir] = -1;
    }

    /* machine layer sets up unconnected pins and insertions */
    w->connected = 1;
    return w;
}

基于component->dai_list中的dai创建dai widget,也将此widget加到card->widgets链表中。

基于component->dapm_routes创建dapm path,并将创建的path加到card->paths.

你可能感兴趣的:(ALSA driver---DAPM 2)