Asoc dapm(一) - kcontrol

  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
发布了272 篇原创文章 · 获赞 150 · 访问量 128万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览