ALSA声卡驱动详解(5)

2019-08-31 00:43

{ .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

还记得我们在Linux ALSA声卡驱动之七:ASoC架构中的Codec中的

snd_soc_dai结构吗?每个codec有多个dai,而cpu(通常就是指某个soc cpu芯片)也会有多个dai,dai注册时,dapm系统会为每个dai创建一个

21

snd_soc_dapm_dai_in或snd_soc_dapm_dai_out类型的widget,通常,这两种widget会和codec中具有相同的stream name的widget进行连接。另外一种情况,当系统中具有多个音频处理器(比如多个codec)时,他们之间可能会通过某两个dai进行连接,当machine驱动确认有这种配置时(通过判断dai_links结构中的param字段),会为他们建立一个dai link把他们绑定在一起,因为有连接关系,两个音频处理器之间的widget的电源状态就可以互相传递。 除了还有几个通用的widget,他们的定义方法如下: [cpp] view plaincopy

#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的定义。 [cpp] view plaincopy

#define SOC_DAPM_SINGLE(xname, reg, shift, max, invert) \\

{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \\

22

.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 \ch\, \\

.info = snd_soc_dapm_info_pin_switch, \\ .get = snd_soc_dapm_get_pin_switch, \\

23

.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: [cpp] view plaincopy

static const struct snd_kcontrol_new left_speaker_mixer[] = { SOC_DAPM_SINGLE(\, WM8993_SPEAKER_MIXER, 7, 1, 0), SOC_DAPM_SINGLE(\, WM8993_SPEAKER_MIXER, 5, 1, 0), SOC_DAPM_SINGLE(\, WM8993_SPEAKER_MIXER, 3, 1, 0),

SOC_DAPM_SINGLE(\, WM8993_SPEAKER_MIXER, 6, 1, 0), };

static const struct snd_kcontrol_new right_speaker_mixer[] = { SOC_DAPM_SINGLE(\, WM8993_SPEAKER_MIXER, 6, 1, 0), SOC_DAPM_SINGLE(\, WM8993_SPEAKER_MIXER, 4, 1, 0), SOC_DAPM_SINGLE(\, WM8993_SPEAKER_MIXER, 2, 1, 0),

SOC_DAPM_SINGLE(\, WM8993_SPEAKER_MIXER, 0, 1, 0), };

static const char *aif_text[] = { \, \ };

static const struct soc_enum aifinl_enum =

SOC_ENUM_SINGLE(WM8993_AUDIO_INTERFACE_2, 15, 2, aif_text);

24

static const struct snd_kcontrol_new aifinl_mux = SOC_DAPM_ENUM(\, 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_enum); 以上,我们定义了wm8993中左右声道的speaker mixer控件:

left_speaker_mixer和right_speaker_mixer,同时还为左右声道各定义了一个叫做AIFINL Mux和AIFINR Mux的输入选择mux控件。

第二步,定义真正的widget,包含第一步定义好的dapm控件: [cpp] view plaincopy

static const struct snd_soc_dapm_widget wm8993_dapm_widgets[] = {

......

SND_SOC_DAPM_AIF_IN(\, \, 0, SND_SOC_NOPM, 0, 0),

SND_SOC_DAPM_AIF_IN(\, \, 1, SND_SOC_NOPM, 0, 0),

......

SND_SOC_DAPM_MUX(\, SND_SOC_NOPM, 0, 0, &aifinl_mux),

SND_SOC_DAPM_MUX(\, SND_SOC_NOPM, 0, 0, &aifinr_mux),

SND_SOC_DAPM_MIXER(\, WM8993_POWER_MANAGEMENT_3, 8, 0, left_speaker_mixer, ARRAY_SIZE(left_speaker_mixer)),

SND_SOC_DAPM_MIXER(\, 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控制由上一步定义的

25


ALSA声卡驱动详解(5).doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:龙虎山风景区上清镇水生态文明自主创建项目实施方案 - 图文

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: