您的位置:首页 > 产品设计 > 产品经理

Asoc dapm(一) - kcontrol

2016-01-13 18:03 513 查看
Asoc dapm(一) - kcontrol

Asoc dapm(二) - kcontrol注册与使用

Asoc dapm(三) - dapm widgets & dapm kcontrol & dapm route

Asoc dapm(四) - dapm widgets & dapm route注册

Asoc dapm(五) - dapm widget链表更新

struct 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 */
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:control类型,通常是SNDRV_CTL_ELEM_IFACE_MIXER

name:kcontrol的名字,名字的命名规则遵循”源-方向-功能”

源可理解为control的输入端,如Master, PCM, CD, line等

方向代表kcontrol的数据流向,如Playback, Capture, Bypass, 也可以不定义,这时是双向的

功能,如Switch, Volume, Route等

通常会借助include/sound/soc.h文件中的宏来定义这个结构体,比如SOC_SINGLE等。

另外info, get, put三个字段是一些回调函数,常常使用sound/soc/soc-core.c文件提供的函数,而不用自己去实现。

kcontrol与dapm kcontrol区别

kcontrol通常用于控件的音量等控制,而dapm kcontrol相关的kcontrol则是用于widget电源管理的开关。

一些构造snd_kcontrol_new结构体的宏

SOC_SINGLE

#define SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) \
((unsigned long)&(struct soc_mixer_control) \
{.reg = xreg, .shift = xshift, .rshift = xshift, .max = xmax, \
.invert = xinvert})
#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) }


SOC_SINGLE定义最简单的控件,这种控件只有一个控制量,比如一个开关,或者是数值的变化(比如codec中的某个频率,FIFO大小等)

参数:xname(该控件的名字),reg(该控件对应的寄存器的地址),shift(控制位在寄存器中的位移),max(控件可设置的最大值),invert(设定值是否取反)

SOC_SINGLE_VALUE宏定义private_value字段,目的主要是为了填充struct soc_mixer_control结构体

当上层调用info, get, put函数的时候可以将kcontrol->private_value强制转换为struct soc_mixer_control类型,然后使用这个结构体中的reg, shift, max等数据。

SOC_SINGLE_TLV

#define SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) \
((unsigned long)&(struct soc_mixer_control) \
{.reg = xreg, .shift = xshift, .rshift = xshift, .max = xmax, \
.invert = xinvert})
#define SOC_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array) \
{   .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
SNDRV_CTL_ELEM_ACCESS_READWRITE,\
.tlv.p = (tlv_array), \
.info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\
.put = snd_soc_put_volsw, \
.private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }


该宏和SOC_SINGLE类似,唯一不同的是增加了tlv.p字段,适用于那些以dB为单位的kcontrol。

DECLARE_TLV_DB_SCALE(name, min, step, mute)宏来构造变量tlv_array。

参数name是变量的名字,min是最小值,step是步进值,如果mute=1,当该kcontrol处于最小值时会mute。

来看一个实例:

/* from 0 to 30 dB in 2 dB steps */
static DECLARE_TLV_DB_SCALE(vga_tlv, 0, 200, 0);

static const struct snd_kcontrol_new uda1380_snd_controls[] = {
...
SOC_SINGLE_TLV("Mic Capture Volume", UDA1380_ADC, 8, 15, 0, vga_tlv),   /* VGA_CTRL */
}


寄存器UDA1380_ADC的偏移8bit处定义了”Mic Capture Volume”,寄存器最大值为15,对应到dB的最小值是0dB,步进值是200*0.01dB=2dB,最大值是15*2dB=30dB

如上这样,寄存器的值与实际增益控制就有一个映射关系了。

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 }

#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 }
#define SOC_ENUM_SINGLE(xreg, xshift, xmax, xtexts) \
SOC_ENUM_DOUBLE(xreg, xshift, xshift, xmax, xtexts)
//xtexts就是mux或者mixer多个输入源的名字,是字符串数组


SOC_ENUM可以用来定义mux, mixer等有多个输入的控件。

SOC_ENUM_SINGLE或SOC_ENUM_DOUBLE用来构造SOC_ENUM中的private_value字段。当info、get、put函数被调用时,会将这个private_value转化成struct soc_enum结构体类型来对数据进行处理。

对于mixer

static const char *uda134x_dsp_setting[] = {"Flat", "Minimum1", "Minimum2", "Maximum"};
static const char *uda134x_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
static const char *uda134x_mixmode[] = {"Differential", "Analog1", "Analog2", "Both"};

static const struct soc_enum uda134x_mixer_enum[] = {
SOC_ENUM_SINGLE(UDA134X_DATA010, 0, 0x04, uda134x_dsp_setting),
SOC_ENUM_SINGLE(UDA134X_DATA010, 3, 0x04, uda134x_deemph),
SOC_ENUM_SINGLE(UDA134X_EA010, 0, 0x04, uda134x_mixmode),
};

static const struct snd_kcontrol_new uda1341_snd_controls[] = {
...
SOC_ENUM("Sound Processing Filter", uda134x_mixer_enum[0]),
SOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]),
SOC_ENUM("Input Mux", uda134x_mixer_enum[2]),
}


通过SOC_ENUM(“Sound Processing Filter”, uda134x_mixer_enum[0])定义了名为“Sound processing Filter”的mixer控件

从SOC_ENUM_SINGLE(UDA134X_DATA010, 0, 0x04, uda134x_dsp_setting)看出通过配置寄存器UDA134X_DATA010可以控制这个mixer控件的四个输入源”Flat”, “Minimum1”, “Minimum2”, “Maximum”,输入源可以选择其中的一个或者多个。

对于mux

对于mux也类似,如SOC_ENUM(“Input Mux”, uda134x_mixer_enum[2]),只不过mux一个时候只能有一个输入源,即”Differential”, “Analog1”, “Analog2”, “Both”中只能选择一个。

SOC_ENUM_EXT

#define SOC_ENUM_SINGLE_EXT(xmax, xtexts) \
{   .max = xmax, .texts = xtexts }
#define SOC_ENUM_EXT(xname, xenum, xhandler_get, xhandler_put) \
{   .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.info = snd_soc_info_enum_ext, \
.get = xhandler_get, .put = xhandler_put, \
.private_value = (unsigned long)&xenum }


SOC_ENUM_EXT与SOC_ENUM宏很类似,但是,SOC_ENUM中构造private_value字段使用SOC_ENUM_SINGLE或者SOC_ENUM_DOUBLE,这两个宏构造的时候都和具体寄存器的某个或者某两个bit相关,而SOC_ENUM_EXT中构造private_value字段使用SOC_ENUM_SINGLE_EXT,这个宏构造的时候只要初始化字符串数组就行了。

static const char *rt5659_micbias2_power_mode[] = {
"Disable", "Enable"
};

static const struct soc_enum rt5659_micbias2_power_enum =
SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rt5659_micbias2_power_mode),
rt5659_micbias2_power_mode);

static int rt5659_micbias2_power_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
int pwr = snd_soc_read(codec, RT5659_PWR_ANLG_2);

if (pwr & 0x0400)
ucontrol->value.integer.value[0] = 1;
else
ucontrol->value.integer.value[0] = 0;

return 0;
}

static int rt5659_micbias2_power_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
int pwr = ucontrol->value.integer.value[0];

if (pwr == 1) {
snd_soc_update_bits(codec, RT5659_PWR_ANLG_3, 0x0002, 0x0002);
snd_soc_update_bits(codec, RT5659_PWR_ANLG_1, 0x0200, 0x0200);
snd_soc_update_bits(codec, RT5659_PWR_ANLG_2, 0x0400, 0x0400);
dev_info(codec->dev, "enable micbias2 power\n");
} else {
snd_soc_update_bits(codec, RT5659_PWR_ANLG_3, 0x0002, 0x0000);
snd_soc_update_bits(codec, RT5659_PWR_ANLG_1, 0x0200, 0x0000);
snd_soc_update_bits(codec, RT5659_PWR_ANLG_2, 0x0400, 0x0000);
dev_info(codec->dev, "disable micbias2 power\n");
}

return 0;
}

SOC_ENUM_EXT("micbias2 power", rt5659_micbias2_power_enum,
rt5659_micbias2_power_get, rt5659_micbias2_power_put),


SOC_VALUE_ENUM

SOC_VALUE_ENUM 用于定义带values字段的snd_kcontrol_new结构体

SOC_VALUE_ENUM_SINGLE和SOC_VALUE_ENUM_DOUBLE 用于定义带values字段的soc_enum结构体

SOC_DOUBLE

SOC_SINGLE在寄存器中只控制一个变量,通常用于单声道;而SOC_DOUBLE可以同时在一个寄存器中控制两个相似的变量,通常是对左右声道的控制,用于立体声。

SOC_DOUBLE_R

与SOC_DOUBLE类似,用于左右声道的控制不在一个寄存器中的情况,参数中指定两个寄存器地址

SOC_DOUBLE_TLV

SOC_DOUBLE_R_TLV

其他

下面这些带EXT的宏需要我们自己定义get, put函数

SOC_SINGLE_EXT(xname, xreg, xshift, xmax, xinvert, xhandler_get, xhandler_put)

SOC_DOUBLE_EXT(xname, xreg, shift_left, shift_right, xmax, xinvert, xhandler_get, xhandler_put)

SOC_SINGLE_EXT_TLV(xname, xreg, xshift, xmax, xinvert, xhandler_get, xhandler_put, tlv_array)

SOC_DOUBLE_EXT_TLV(xname, xreg, shift_left, shift_right, xmax, xinvert, xhandler_get, xhandler_put, tlv_array)

SOC_DOUBLE_R_EXT_TLV(xname, reg_left, reg_right, xshift, xmax, xinvert, xhandler_get, xhandler_put, tlv_array)

SOC_ENUM_EXT(xname, xenum, xhandler_get, xhandler_put)

参考文章

ALSA声卡驱动中的DAPM详解之一:kcontrol
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: