DAPM-基础及control控件

本文主要介绍DAPM的基础知识以及kcontrol控件

DAPM的概念

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

常用控件(control)

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

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;
};

.iface:控件的类型
.name:控件的名字,control的作用按照名字来归类
.index:该control在card里的编号
.access:访问类型,可用多个bit 或 在一起
.private_value:该字段包含了一个任意的长整数类型值。该值可以通过info,get,put这几个回调函数访问。你可以自己决定如何使用该字段,例如可以把它拆分成多个位域,又或者是一个指针,指向某一个数据结构。

需要注意的是,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:是否逻辑取反

还有各种各样的宏可以定义各种控件

SOC_SINGLE_TLV            用于定义那些有增益控制的控件
SOC_DOUBLE                   与SOC_SINGLE相对应,区别是它可以控制一个寄存器里的两个位
SOC_DOUBLE_R              与SOC_DOUBLE类似,可以指定两个寄存器的位
SOC_DOUBLE_TLV          与SOC_SINGLE_TLV对应的立体声版本
SOC_DOUBLE_R_TLV      左右声道有独立寄存器控制的SOC_DOUBLE_TLV版本

MIXER控件

mux控件与mixer控件类似,也是多个输入端和一个输出端的组合控件,与mixer控件不同的是,mux控件的多个输入端同时只能有一个被选中。因此,mux控件所对应的寄存器,通常可以设定一段连续的数值,每个不同的数值对应不同的输入端被打开,与上述的mixer控件不同,ASoC用soc_enum结构来描述mux控件的寄存器信息:

/* enumerated kcontrol */
struct soc_enum {
        unsigned short reg;
        unsigned short reg2;
        unsigned char shift_l;
        unsigned char shift_r;
        unsigned int max;
        unsigned int mask;
        const char * const *texts;
        const unsigned int *values;
};

两个寄存器地址和位移字段:reg,reg2,shift_l,shift_r,用于描述左右声道的控制寄存器信息。字符串数组指针用于描述每个输入端对应的名字,value字段则指向一个数组,该数组定义了寄存器可以选择的值,每个值对应一个输入端,如果value是一组连续的值,通常我们可以忽略values参数。下面我们先看看如何定义一个mux控件:

1.定义字符串和values数组,以下的例子因为values是连续的,所以不用定义:

static const char *drc_path_text[] = {
        "ADC",
        "DAC"
};

2.利用ASoC提供的辅助宏定义soc_enum结构,用于描述寄存器

static const struct soc_enum drc_path =
        SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_1, 14, 2, drc_path_text);

3.利用ASoc提供的辅助宏定义soc_kcontrol_new结构,该结构最后用于注册该mux控件

static const struct snd_kcontrol_new wm8993_snd_controls[] = {
		SOC_DOUBLE_TLV(......),
		......
		SOC_ENUM("DRC Path", drc_path),
		......
}

4.以上几步定义了一个叫DRC PATH的mux控件,该控件具有两个输入选择,分别是来自ADC和DAC,用寄存器WM8993_DRC_CONTROL_1控制。其中,soc_enum结构使用了辅助宏SOC_ENUM_SINGLE来定义,该宏的声明如下:

#define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmax, xtexts) \
{       .reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \
        .max = xmax, .texts = xtexts, \
        .mask = xmax ? roundup_pow_of_two(xmax) - 1 : 0
}
#define SOC_ENUM_SINGLE(xreg, xshift, xmax, xtexts) \
        SOC_ENUM_DOUBLE(xreg, xshift, xshift, xmax, xtexts)

定义soc_kcontrol_new结构时使用了SOC_ENUM,它的定义如下:

#define SOC_ENUM(xname, xenum) \
{       .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,\
        .info = snd_soc_info_enum_double, \
        .get = snd_soc_get_enum_double, .put = snd_soc_put_enum_double,\
        .private_value = (unsigned long)&xenum }

依然是使用private_value字段记录soc_enum结构,不过几个回调函数变了,这些函数本质上都是通过soc_enum结构体拿到控件的private_value成员的信息来操作寄存器

以下是另外几个常用于定义mux控件的宏(注意不是定义枚举): SOC_VALUE_ENUM_SINGLE           用于定义带values字段的soc_enum结构。SOC_VALUE_ENUM_DOUBLE         SOC_VALUE_ENUM_SINGLE的立体声版本
SOC_VALUE_ENUM                          用于定义带values字段snd_kcontrol_new结构

其他控件

其实,除了以上介绍的几种常用的控件,ASoc还为我们提供了另外一些控件定义辅助宏,详细的请读者参考include/sound/soc.h。这里列举几个:

需要自己定义get和put回调时,可以使用以下这些带EXT的版本:

  • SOC_SINGLE_EXT

  • SOC_DOUBLE_EXT

  • SOC_SINGLE_EXT_TLV

  • SOC_DOUBLE_EXT_TLV

  • SOC_DOUBLE_R_EXT_TLV

  • SOC_ENUM_EXT

猜你喜欢

转载自blog.csdn.net/hhx123456798/article/details/123301635