ALSA driver---DAPM 2
Posted fellow1988
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ALSA driver---DAPM 2相关的知识,希望对你有一定的参考价值。
定义widget
There are 4 power domains within DAPM:
- 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.
- 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
- Path domain – audio subsystem signal paths. Automatically set when mixer and mux settings are changed by the user. e.g. alsamixer, amixer.
- 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。
以上是关于ALSA driver---DAPM 2的主要内容,如果未能解决你的问题,请参考以下文章