ALSA声卡驱动中的DAPM详解之三:如何定义各种widget
上一节中,介绍了DAPM框架中几个重要的数据结构:snd_soc_dapm_widget,snd_soc_dapm_path,snd_soc_dapm_route。其中snd_soc_dapm_path无需我们自己定义,它会在注册snd_soc_dapm_route时动态地生成,但是对于系统中的widget和route,我们是需要自己进行定义的,另外,widget所包含的kcontrol与普通的kcontrol有所不同,它们的定义方法与标准的kcontrol也有所不同。本节的内容我将会介绍如何使用DAPM系统提供的一些辅助宏定义来定义各种类型的widget和它所用到的kcontrol。
声明:本博内容均由http://blog.csdn.net/droidphone原创,转载请注明出处,谢谢!
/*****************************************************************************************************/
定义widget
和普通的kcontrol一样,DAPM框架为我们提供了大量的辅助宏用来定义各种各样的widget控件,这些宏定义根据widget的类型,按照它们的电源所在的域,被分为了几个域,他们分别是:
- codec域 比如VREF和VMID等提供参考电压的widget,这些widget通常在codec的probe/remove回调中进行控制,当然,在工作中如果没有音频流时,也可以适当地进行控制它们的开启与关闭。
- platform域 位于该域上的widget通常是针对平台或板子的一些需要物理连接的输入/输出接口,例如耳机、扬声器、麦克风,因为这些接口在每块板子上都可能不一样,所以通常它们是在machine驱动中进行定义和控制,并且也可以由用户空间的应用程序通过某种方式来控制它们的打开和关闭。
- 音频路径域 一般是指codec内部的mixer、mux等控制音频路径的widget,这些widget可以根据用户空间的设定连接关系,自动设定他们的电源状态。
- 音频数据流域 是指那些需要处理音频数据流的widget,例如ADC、DAC等等。
codec域widget的定义
目前,DAPM框架只提供了定义一个codec域widget的辅助宏:
- #define SND_SOC_DAPM_VMID(wname) \
- { .id = snd_soc_dapm_vmid, .name = wname, .kcontrol_news = NULL, \
- .num_kcontrols = 0}
platform域widget的定义
DAPM框架为我们提供了多种platform域widget的辅助定义宏:
- #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_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}
以上这些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连接起来。
音频路径(path)域widget的定义
这种widget通常是对普通kcontrols控件的再封装,增加音频路径和电源管理功能,所以这种widget会包含一个或多个kcontrol,普通kcontrol的定义方法我们在ALSA声卡驱动中的DAPM详解之一:kcontrol中已经介绍过,不过这些被包含的kcontrol不能使用这种方法定义,它们需要使用dapm框架提供的定义宏来定义,详细的讨论我们后面有介绍。这里先列出这些widget的定义宏:
- #define SND_SOC_DAPM_PGA(wname, wreg, wshift, winvert,\
- wcontrols, wncontrols) \
- { .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
- .invert = 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, .reg = wreg, .shift = wshift, \
- .invert = 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, .reg = wreg, .shift = wshift, \
- .invert = 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, .reg = wreg, \
- .shift = wshift, .invert = winvert, .kcontrol_news = wcontrols, \
- .num_kcontrols = wncontrols}
- #define SND_SOC_DAPM_MICBIAS(wname, wreg, wshift, winvert) \
- { .id = snd_soc_dapm_micbias, .name = wname, .reg = wreg, .shift = wshift, \
- .invert = winvert, .kcontrol_news = NULL, .num_kcontrols = 0}
- #define SND_SOC_DAPM_SWITCH(wname, wreg, wshift, winvert, wcontrols) \
- { .id = snd_soc_dapm_switch, .name = wname, .reg = wreg, .shift = wshift, \
- .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1}
- #define SND_SOC_DAPM_MUX(wname, wreg, wshift, winvert, wcontrols) \
- { .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \
- .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1}
- #define SND_SOC_DAPM_VIRT_MUX(wname, wreg, wshift, winvert, wcontrols) \
- { .id = snd_soc_dapm_virt_mux, .name = wname, .reg = wreg, .shift = wshift, \
- .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1}
- #define SND_SOC_DAPM_VALUE_MUX(wname, wreg, wshift, winvert, wcontrols) \
- { .id = snd_soc_dapm_value_mux, .name = wname, .reg = wreg, \
- .shift = wshift, .invert = winvert, .kcontrol_news = wcontrols, \
- .num_kcontrols = 1}
可以看出,这些widget的reg和shift字段是需要赋值的,说明这些widget是有相应的电源控制寄存器的,DAPM框架在扫描和更新音频路径时,会利用这些寄存器来控制widget的电源状态,使得它们的供电状态是按需分配的,需要的时候(在有效的音频路径上)上电,不需要的时候(不再有效的音频路径上)下电。这些widget需要完成和之前介绍的mixer、mux等控件同样的功能,实际上,这是通过它们包含的kcontrol控件来完成的,这些kcontrol我们需要在定义widget前先定义好,然后通过wcontrols和num_kcontrols参数传递给这些辅助定义宏。
如果需要自定义这些widget的dapm事件处理回调函数,也可以使用下面这些带“_E”后缀的版本:
- SND_SOC_DAPM_PGA_E
- SND_SOC_DAPM_OUT_DRV_E
- SND_SOC_DAPM_MIXER_E
- SND_SOC_DAPM_MIXER_NAMED_CTL_E
- SND_SOC_DAPM_SWITCH_E
- SND_SOC_DAPM_MUX_E
- SND_SOC_DAPM_VIRT_MUX_E
音频数据流(stream)域widget的定义
这些widget主要包含音频输入/输出接口,ADC/DAC等等:
- #define SND_SOC_DAPM_AIF_IN(wname, stname, wslot, wreg, wshift, winvert) \
- { .id = snd_soc_dapm_aif_in, .name = wname, .sname = stname, \
- .reg = wreg, .shift = wshift, .invert = 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, \
- .reg = wreg, .shift = wshift, .invert = 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, \
- .reg = wreg, .shift = wshift, .invert = 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, \
- .reg = wreg, .shift = wshift, .invert = 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, .reg = wreg, \
- .shift = wshift, .invert = winvert}
- #define SND_SOC_DAPM_DAC_E(wname, stname, wreg, wshift, winvert, \
- wevent, wflags) \
- { .id = snd_soc_dapm_dac, .name = wname, .sname = stname, .reg = wreg, \
- .shift = wshift, .invert = 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, .reg = wreg, \
- .shift = wshift, .invert = winvert}
- #define SND_SOC_DAPM_ADC_E(wname, stname, wreg, wshift, winvert, \
- wevent, wflags) \
- { .id = snd_soc_dapm_adc, .name = wname, .sname = stname, .reg = wreg, \
- .shift = wshift, .invert = 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 }
除了上面这些widget,还有另外三种widget没有提供显示的定义方法,它们的种类id分别是:
- snd_soc_dapm_dai_in
- snd_soc_dapm_dai_out
- snd_soc_dapm_dai_link
除了还有几个通用的widget,他们的定义方法如下:
- #define SND_SOC_DAPM_REG(wid, wname, wreg, wshift, wmask, won_val, woff_val) \
- { .id = wid, .name = wname, .kcontrol_news = NULL, .num_kcontrols = 0, \
- .reg = -((wreg) + 1), .shift = wshift, .mask = wmask, \
- .on_val = won_val, .off_val = woff_val, .event = dapm_reg_event, \
- .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD}
- #define SND_SOC_DAPM_SUPPLY(wname, wreg, wshift, winvert, wevent, wflags) \
- { .id = snd_soc_dapm_supply, .name = wname, .reg = wreg, \
- .shift = wshift, .invert = winvert, .event = wevent, \
- .event_flags = wflags}
- #define SND_SOC_DAPM_REGULATOR_SUPPLY(wname, wdelay, wflags) \
- { .id = snd_soc_dapm_regulator_supply, .name = wname, \
- .reg = SND_SOC_NOPM, .shift = wdelay, .event = dapm_regulator_event, \
- .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \
- .invert = wflags}
定义dapm kcontrol
上一节提到,对于音频路径上的mixer或mux类型的widget,它们包含了若干个kcontrol,这些被包含的kcontrol实际上就是我们之前讨论的mixer和mux等,dapm利用这些kcontrol完成音频路径的控制。不过,对于widget来说,它的任务还不止这些,dapm还要动态地管理这些音频路径的连结关系,以便可以根据这些连接关系来控制这些widget的电源状态,如果按照普通的方法定义这些kcontrol,是无法达到这个目的的,因此,dapm为我们提供了另外一套定义宏,由它们完成这些被widget包含的kcontrol的定义。
- #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) }
- #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) }
- #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_VIRT(xname, xenum) \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
- .info = snd_soc_info_enum_double, \
- .get = snd_soc_dapm_get_enum_virt, \
- .put = snd_soc_dapm_put_enum_virt, \
- .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_VALUE_ENUM(xname, xenum) \
- { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
- .info = snd_soc_info_enum_double, \
- .get = snd_soc_dapm_get_value_enum_double, \
- .put = snd_soc_dapm_put_value_enum_double, \
- .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的精髓所在。这里我只提一下这种概念,后续的章节会有较为详细的代码分析过程。
建立widget和route
上面介绍了一大堆的辅助宏,那么,对于一个实际的系统,我们怎么定义我们需要的widget?怎样定义widget的连接关系?下面我们还是以Wolfson公司的codec芯片WM8993为例子来了解这个过程。
第一步,利用辅助宏定义widget所需要的dapm kcontrol:
- static const struct snd_kcontrol_new left_speaker_mixer[] = {
- SOC_DAPM_SINGLE("Input Switch", WM8993_SPEAKER_MIXER, 7, 1, 0),
- SOC_DAPM_SINGLE("IN1LP Switch", WM8993_SPEAKER_MIXER, 5, 1, 0),
- SOC_DAPM_SINGLE("Output Switch", WM8993_SPEAKER_MIXER, 3, 1, 0),
- SOC_DAPM_SINGLE("DAC Switch", WM8993_SPEAKER_MIXER, 6, 1, 0),
- };
- static const struct snd_kcontrol_new right_speaker_mixer[] = {
- SOC_DAPM_SINGLE("Input Switch", WM8993_SPEAKER_MIXER, 6, 1, 0),
- SOC_DAPM_SINGLE("IN1RP Switch", WM8993_SPEAKER_MIXER, 4, 1, 0),
- SOC_DAPM_SINGLE("Output Switch", WM8993_SPEAKER_MIXER, 2, 1, 0),
- SOC_DAPM_SINGLE("DAC Switch", WM8993_SPEAKER_MIXER, 0, 1, 0),
- };
- static const char *aif_text[] = {
- "Left", "Right"
- };
- static const struct soc_enum aifinl_enum =
- SOC_ENUM_SINGLE(WM8993_AUDIO_INTERFACE_2, 15, 2, aif_text);
- static const struct snd_kcontrol_new aifinl_mux =
- SOC_DAPM_ENUM("AIFINL Mux", aifinl_enum);
- static const struct soc_enum aifinr_enum =
- SOC_ENUM_SINGLE(WM8993_AUDIO_INTERFACE_2, 14, 2, aif_text);
- static const struct snd_kcontrol_new aifinr_mux =
- SOC_DAPM_ENUM("AIFINR Mux", aifinr_enum);
以上,我们定义了wm8993中左右声道的speaker mixer控件:left_speaker_mixer和right_speaker_mixer,同时还为左右声道各定义了一个叫做AIFINL Mux和AIFINR Mux的输入选择mux控件。
第二步,定义真正的widget,包含第一步定义好的dapm控件:
- static const struct snd_soc_dapm_widget wm8993_dapm_widgets[] = {
- ......
- SND_SOC_DAPM_AIF_IN("AIFINL", "Playback", 0, SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_AIF_IN("AIFINR", "Playback", 1, SND_SOC_NOPM, 0, 0),
- ......
- SND_SOC_DAPM_MUX("DACL Mux", SND_SOC_NOPM, 0, 0, &aifinl_mux),
- SND_SOC_DAPM_MUX("DACR Mux", SND_SOC_NOPM, 0, 0, &aifinr_mux),
- SND_SOC_DAPM_MIXER("SPKL", WM8993_POWER_MANAGEMENT_3, 8, 0,
- left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)),
- SND_SOC_DAPM_MIXER("SPKR", WM8993_POWER_MANAGEMENT_3, 9, 0,
- right_speaker_mixer, ARRAY_SIZE(right_speaker_mixer)),
- ......
- };
这一步,为左右声道各自定义了一个mux widget:DACL Mux和DACR Mux,实际的多路选择由dapm kcontrol:aifinl_mux和aifinr_mux,来完成,因为传入了SND_SOC_NOPM参数,这两个widget不具备电源属性,但是mux的切换会影响到与之相连的其它具备电源属性的电源状态。我们还为左右声道的扬声器各自定义了一个mixer widget:SPKL和SPKR,具体的mixer控制由上一步定义的left_speaker_mixer和right_speaker_mixer来完成,两个widget具备电源属性,所以,当这两个widget在一条有效的音频路径上时,dapm框架可以通过寄存器WM8993_POWER_MANAGEMENT_3的第8位和第9位控制它的电源状态。
第三步,定义这些widget的连接路径:
- static const struct snd_soc_dapm_route routes[] = {
- ......
- { "DACL Mux", "Left", "AIFINL" },
- { "DACL Mux", "Right", "AIFINR" },
- { "DACR Mux", "Left", "AIFINL" },
- { "DACR Mux", "Right", "AIFINR" },
- ......
- { "SPKL", "DAC Switch", "DACL" },
- { "SPKL", NULL, "CLK_SYS" },
- { "SPKR", "DAC Switch", "DACR" },
- { "SPKR", NULL, "CLK_SYS" },
- };
通过第一步的定义,我们知道DACL Mux和DACR Mux有两个输入引脚,分别是
- Left
- Right
而SPKL和SPKR有四个输入选择引脚,分别是:
- Input Switch
- IN1LP Switch/IN1RP Switch
- Output Switch
- DAC Switch
所以,很显然,上面的路径定义的意思就是:
- AIFINL连接到DACL Mux的Left输入脚
- AIFINR连接到DACL Mux的Right输入脚
- AIFINL连接到DACR Mux的Left输入脚
- AIFINR连接到DACR Mux的Right输入脚
- DACL连接到SPKL的DAC Switch输入脚
- DACR连接到SPKR的DAC Switch输入脚
第四步,在codec驱动的probe回调中注册这些widget和路径:
- static int wm8993_probe(struct snd_soc_codec *codec)
- {
- ......
- snd_soc_dapm_new_controls(dapm, wm8993_dapm_widgets,
- ARRAY_SIZE(wm8993_dapm_widgets));
- ......
- snd_soc_dapm_add_routes(dapm, routes, ARRAY_SIZE(routes));
- ......
- }
在machine驱动中,我们可以用同样的方式定义和注册板子特有的widget和路径信息。
ALSA声卡驱动中的DAPM详解之三:如何定义各种widget的更多相关文章
- ALSA声卡驱动中的DAPM详解之四:在驱动程序中初始化并注册widget和route
前几篇文章我们从dapm的数据结构入手,了解了代表音频控件的widget,代表连接路径的route以及用于连接两个widget的path.之前都是一些概念的讲解以及对数据结构中各个字段的说明,从本章开 ...
- ALSA声卡驱动中的DAPM详解之七:dapm事件机制(dapm event)
前面的六篇文章,我们已经讨论了dapm关于动态电源管理的有关知识,包括widget的创建和初始化,widget之间的连接以及widget的上下电顺序等等.本章我们准备讨论dapm框架中的另一个机制:事 ...
- ALSA声卡驱动中的DAPM详解之六:精髓所在,牵一发而动全身
设计dapm的主要目的之一,就是希望声卡上的各种部件的电源按需分配,需要的就上电,不需要的就下电,使得整个音频系统总是处于最小的耗电状态,最主要的就是,这一切对用户空间的应用程序是透明的,也就是说,用 ...
- ALSA声卡驱动中的DAPM详解之五:建立widget之间的连接关系
前面我们主要着重于codec.platform.machine驱动程序中如何使用和建立dapm所需要的widget,route,这些是音频驱动开发人员必须要了解的内容,经过前几章的介绍,我们应该知道如 ...
- ALSA声卡驱动中的DAPM详解之一:kcontrol
DAPM是Dynamic Audio Power Management的缩写,直译过来就是动态音频电源管理的意思,DAPM是为了使基于linux的移动设备上的音频子系统,在任何时候都工作在最小功耗状态 ...
- ALSA声卡驱动中的DAPM详解之二:widget-具备路径和电源管理信息的kcontrol
上一篇文章中,我们介绍了音频驱动中对基本控制单元的封装:kcontrol.利用kcontrol,我们可以完成对音频系统中的mixer,mux,音量控制,音效控制,以及各种开关量的控制,通过对各种kco ...
- Linux ALSA声卡驱动之八:ASoC架构中的Platform
1. Platform驱动在ASoC中的作用 前面几章内容已经说过,ASoC被分为Machine,Platform和Codec三大部件,Platform驱动的主要作用是完成音频数据的管理,最终通过C ...
- Linux驱动开发必看详解神秘内核(完全转载)
Linux驱动开发必看详解神秘内核 完全转载-链接:http://blog.chinaunix.net/uid-21356596-id-1827434.html IT168 技术文档]在开始步入L ...
- Maven依赖中的scope详解,在eclipse里面用maven install可以编程成功,到服务器上用命令执行报VM crash错误
Maven依赖中的scope详解 项目中用了<scope>test</scope>在eclipse里面用maven install可以编译成功,到服务器上用命令执行报VM cr ...
随机推荐
- Masonry基础API
Masonry基础API mas_makeConstraints() 添加约束 mas_remakeConstraints() 移除之前的约束,重新添加新的约束 mas_updateConst ...
- Alpha项目测试
这个作业属于哪个课程 https://edu.cnblogs.com/campus/xnsy/SoftwareEngineeringClass1/homework/3338 这个作业要求在哪里 htt ...
- thymeleaf在开发环境正常,但用jar运行时报错 Error resolving template template might not exist or might not be accessible
解决方案: (1)配置中添加 spring.thymeleaf.prefix=classpath:/templates (2)指向模板的路径 不加 /
- at24c02系列和at24c256系列的比较
编号的含义: at24c02系列包括的有: 128(1K),256(2K),512(4K),1024(8K),2048(16K)字节(B) at24c256系列包括的有: 16384(128K),32 ...
- 关于zookeeper和zkfc的一些测试
1.停掉zookeeper集群 ****进程影响****** zkfc:报错无法连接zookeeper.ClientCnxn java.net.connectexception:拒绝连接,但不会shu ...
- 【Codeforces 479D】Long Jumps
[链接] 我是链接,点我呀:) [题意] 如果存在a[j]-a[i]=d 那么认为可以量出来长度d 现在给你量尺上的n个点. 问你最少要加多少个点,才能够量出来长度x和长度y [题解] 设dic1和d ...
- [luoguP1433] 吃奶酪(DP || Dfs)
传送门 深搜加剪纸可A(O(玄学) 1274ms) ——代码 #include <cmath> #include <cstdio> #include <iostream& ...
- 常州模拟赛d5t3 appoint
分析:这道题比较奇葩.因为字符串没有swap函数,所以一个一个字符串交换只有30分.但是我们可以不用直接交换字符串,而是交换字符串的指针,相当于当前位置是哪一个字符串,每次交换int,可以拿60分. ...
- Prime Land(poj 1365)
题意:这题题意难懂,看了题解才知道的.比如第二组sample,就是5^1*2^1=10, 求10-1即9的质因数分解,从大到小输出,即3^2.本来很简单的嘿,直接最快速幂+暴力最裸的就行了. #inc ...
- tyvj3737 逐个击破
描述 三大战役的平津战场上,傅作义集团在以北平.天津为中心,东起唐山西至张家口的铁路线上摆起子一字长蛇阵,并企图在溃败时从海上南逃或向西逃窜.为了就地歼敌不让其逃走,mzd制定了先切断敌人东洒两头退路 ...