Asoc dapm(一) - kcontrol

来源:互联网 发布:印度经济 知乎 编辑:程序博客网 时间:2024/06/05 23:52
  1. Asoc dapm(一) - kcontrol
  2. Asoc dapm(二) - kcontrol注册与使用
  3. Asoc dapm(三) - dapm widgets & dapm kcontrol & dapm route
  4. Asoc dapm(四) - dapm widgets & dapm route注册
  5. 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)


参考文章

  1. ALSA声卡驱动中的DAPM详解之一:kcontrol
0 0
原创粉丝点击