ALSA声卡驱动中的DAPM详解之一:kcontrol

DAPM是Dynamic Audio Power Management的缩写,直译过来就是动态音频电源管理的意思,DAPM是为了使基于linux的移动设备上的音频子系统,在任何时候都工作在最小功耗状态下。DAPM对用户空间的应用程序来说是透明的,所有与电源相关的开关都在ASoc core中完成。用户空间的应用程序无需对代码做出修改,也无需重新编译,DAPM根据当前激活的音频流(playback/capture)和声卡中的mixer等的配置来决定那些音频控件的电源开关被打开或关闭。

/*****************************************************************************************************/声明:本博内容均由原创,转载请注明出处,谢谢!/*****************************************************************************************************/

DAPM控件是由普通的soc音频控件演变而来的,所以本章的内容我们先从普通的soc音频控件开始。

snd_kcontrol_new结构

在正式讨论DAPM之前,我们需要先搞清楚ASoc中的一个重要的概念:kcontrol,不熟悉的读者需要浏览一下我之前的文章:Linux ALSA声卡驱动之四:Control设备的创建。通常,一个kcontrol代表着一个mixer(混音器),或者是一个mux(多路开关),又或者是一个音量控制器等等。 从上述文章中我们知道,定义一个kcontrol主要就是定义一个snd_kcontrol_new结构,为了方便讨论,这里再次给出它的定义:

struct snd_kcontrol_new {snd_ctl_elem_iface_t iface;/* interface identifier */unsigned int device;/* device/client number */unsigned int subdevice;/* subdevice (substream) number */const unsigned char *name;/* ASCII name of item */unsigned int index;/* index of item */unsigned int access;/* access rights */unsigned int count;/* count of same elements */snd_kcontrol_info_t *info;snd_kcontrol_get_t *get;snd_kcontrol_put_t *put;union {snd_kcontrol_tlv_rw_t *c;const unsigned int *p;} tlv;unsigned long private_value;};

回到Linux ALSA声卡驱动之四:Control设备的创建中,我们知道,对于每个控件,我们需要定义一个和他对应的snd_kcontrol_new结构,这些snd_kcontrol_new结构会在声卡的初始化阶段,通过snd_soc_add_codec_controls函数注册到系统中,用户空间就可以通过amixer或alsamixer等工具查看和设定这些控件的状态。

snd_kcontrol_new结构中,几个主要的字段是get,put,private_value,get回调函数用于获取该控件当前的状态值,而put回调函数则用于设置控件的状态值,而private_value字段则根据不同的控件类型有不同的意义,比如对于普通的控件,private_value字段可以用来定义该控件所对应的寄存器的地址以及对应的控制位在寄存器中的位置信息。值得庆幸的是,ASoc系统已经为我们准备了大量的宏定义,用于定义常用的控件,这些宏定义位于include/sound/soc.h中。下面我们分别讨论一下如何用这些预设的宏定义来定义一些常用的控件。简单型的控件

SOC_SINGLE SOC_SINGLE应该算是最简单的控件了,这种控件只有一个控制量,比如一个开关,或者是一个数值变量(比如Codec中某个频率,FIFO大小等等)。我们看看这个宏是如何定义的:

#define SOC_SINGLE(xname, reg, shift, max, invert) \{.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \.info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\.put = snd_soc_put_volsw, \.private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }宏定义的参数分别是:xname(该控件的名字),reg(该控件对应的寄存器的地址),shift(控制位在寄存器中的位移),max(控件可设置的最大值),invert(设定值是否逻辑取反)。这里又使用了一个宏来定义private_value字段:SOC_SINGLE_VALUE,我们看看它的定义:#define SOC_DOUBLE_VALUE(xreg, shift_left, shift_right, xmax, xinvert) \((unsigned long)&(struct soc_mixer_control) \{.reg = xreg, .rreg = xreg, .shift = shift_left, \.rshift = shift_right, .max = xmax, .platform_max = xmax, \.invert = xinvert})#define SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) \SOC_DOUBLE_VALUE(xreg, xshift, xshift, xmax, xinvert)这里实际上是定义了一个soc_mixer_control结构,然后把该结构的地址赋值给了private_value字段,soc_mixer_control结构是这样的:/* mixer control */struct soc_mixer_control {int min, max, platform_max;unsigned int reg, rreg, shift, rshift, invert;};看来soc_mixer_control是控件特征的真正描述者,它确定了该控件对应寄存器的地址,位移值,最大值和是否逻辑取反等特性,控件的put回调函数和get回调函数需要借助该结构来访问实际的寄存器。我们看看这get回调函数的定义:int snd_soc_get_volsw(struct snd_kcontrol *kcontrol,struct snd_ctl_elem_value *ucontrol){struct soc_mixer_control *mc =(struct soc_mixer_control *)kcontrol->private_value;struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);unsigned int reg = mc->reg;unsigned int reg2 = mc->rreg;unsigned int shift = mc->shift;unsigned int rshift = mc->rshift;int max = mc->max;unsigned int mask = (1 << fls(max)) – 1;unsigned int invert = mc->invert;ucontrol->value.integer.value[0] =(snd_soc_read(codec, reg) >> shift) & mask;if (invert)ucontrol->value.integer.value[0] =max – ucontrol->value.integer.value[0];if (snd_soc_volsw_is_stereo(mc)) {if (reg == reg2)ucontrol->value.integer.value[1] =(snd_soc_read(codec, reg) >> rshift) & mask;elseucontrol->value.integer.value[1] =(snd_soc_read(codec, reg2) >> shift) & mask;if (invert)ucontrol->value.integer.value[1] =max – ucontrol->value.integer.value[1];}return 0;}上述代码一目了然,从private_value字段取出soc_mixer_control结构,利用该结构的信息,访问对应的寄存器,返回相应的值。因为有梦,所以勇敢出发,选择出发,便只顾风雨兼程。

ALSA声卡驱动中的DAPM详解之一:kcontrol

相关文章:

你感兴趣的文章:

标签云: