ALSA声卡驱动详解.docx

上传人:b****6 文档编号:3786810 上传时间:2022-11-25 格式:DOCX 页数:99 大小:303.67KB
下载 相关 举报
ALSA声卡驱动详解.docx_第1页
第1页 / 共99页
ALSA声卡驱动详解.docx_第2页
第2页 / 共99页
ALSA声卡驱动详解.docx_第3页
第3页 / 共99页
ALSA声卡驱动详解.docx_第4页
第4页 / 共99页
ALSA声卡驱动详解.docx_第5页
第5页 / 共99页
点击查看更多>>
下载资源
资源描述

ALSA声卡驱动详解.docx

《ALSA声卡驱动详解.docx》由会员分享,可在线阅读,更多相关《ALSA声卡驱动详解.docx(99页珍藏版)》请在冰豆网上搜索。

ALSA声卡驱动详解.docx

ALSA声卡驱动详解

ALSA声卡驱动详解

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

kcontrol

DAPM是DynamicAudioPowerManagement的缩写,直译过来就是动态音频电源管理的意思,DAPM是为了使基于linux的移动设备上的音频子系统,在任何时候都工作在最小功耗状态下。

DAPM对用户空间的应用程序来说是透明的,所有与电源相关的开关都在ASoccore中完成。

用户空间的应用程序无需对代码做出修改,也无需重新编译,DAPM根据当前激活的音频流(playback/capture)和声卡中的mixer等的配置来决定那些音频控件的电源开关被打开或关闭。

/*****************************************************************************************************/

声明:

本博内容均由

/*****************************************************************************************************/

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

snd_kcontrol_new结构

在正式讨论DAPM之前,我们需要先搞清楚ASoc中的一个重要的概念:

kcontrol,不熟悉的读者需要浏览一下我之前的文章:

LinuxALSA声卡驱动之四:

Control设备的创建。

通常,一个kcontrol代表着一个mixer(混音器),或者是一个mux(多路开关),又或者是一个音量控制器等等。

从上述文章中我们知道,定义一个kcontrol主要就是定义一个snd_kcontrol_new结构,为了方便讨论,这里再次给出它的定义:

[cpp] viewplaincopy

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;  

                                max - ucontrol->value.integer.value[1];  

        }  

  

        return 0;  

}  

上述代码一目了然,从private_value字段取出soc_mixer_control结构,利用该结构的信息,访问对应的寄存器,返回相应的值。

SOC_SINGLE_TLV  SOC_SINGLE_TLV是SOC_SINGLE的一种扩展,主要用于定义那些有增益控制的控件,例如音量控制器,EQ均衡器等等。

[cpp] viewplaincopy

#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) }  

从他的定义可以看出,用于设定寄存器信息的private_value字段的定义和SOC_SINGLE是一样的,甚至put、get回调函数也是使用同一套,唯一不同的是增加了一个tlv_array参数,并把它赋值给了tlv.p字段。

关于tlv,已经在LinuxALSA声卡驱动之四:

Control设备的创建中进行了阐述。

用户空间可以通过对声卡的control设备发起以下两种ioctl来访问tlv字段所指向的数组:

    SNDRV_CTL_IOCTL_TLV_READ

    SNDRV_CTL_IOCTL_TLV_WRITE

    SNDRV_CTL_IOCTL_TLV_COMMAND

通常,tlv_array用来描述寄存器的设定值与它所代表的实际意义之间的映射关系,最常用的就是用于音量控件时,设定值与对应的dB值之间的映射关系,请看以下例子:

[cpp] viewplaincopy

static const DECLARE_TLV_DB_SCALE(mixin_boost_tlv, 0, 900, 0);  

  

static const struct snd_kcontrol_new wm1811_snd_controls[] = {  

SOC_SINGLE_TLV("MIXINL IN1LP Boost Volume", WM8994_INPUT_MIXER_1, 7, 1, 0,  

               mixin_boost_tlv),  

SOC_SINGLE_TLV("MIXINL IN1RP Boost Volume", WM8994_INPUT_MIXER_1, 8, 1, 0,  

               mixin_boost_tlv),  

};  

DECLARE_TLV_DB_SCALE用于定义一个dB值映射的tlv_array,上述的例子表明,该tlv的类型是SNDRV_CTL_TLVT_DB_SCALE,寄存器的最小值对应是0dB,寄存器每增加一个单位值,对应的dB数增加是9dB(0.01dB*900),而由接下来的两组SOC_SINGLE_TLV定义可以看出,我们定义了两个boost控件,寄存器的地址都是WM8994_INPUT_MIXER_1,控制位分别是第7bit和第8bit,最大值是1,所以,该控件只能设定两个数值0和1,对应的dB值就是0dB和9dB。

SOC_DOUBLE  与SOC_SINGLE相对应,区别是SOC_SINGLE只控制一个变量,而SOC_DOUBLE则可以同时在一个寄存器中控制两个相似的变量,最常用的就是用于一些立体声的控件,我们需要同时对左右声道进行控制,因为多了一个声道,参数也就相应地多了一个shift位移值,

[cpp] viewplaincopy

#define SOC_DOUBLE(xname, reg, shift_left, shift_right, 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_DOUBLE_VALUE(reg, shift_left, shift_right, \  

                                          max, invert) }  

SOC_DOUBLE_R  与SOC_DOUBLE类似,对于左右声道的控制寄存器不一样的情况,使用SOC_DOUBLE_R来定义,参数中需要指定两个寄存器地址。

SOC_DOUBLE_TLV  与SOC_SINGLE_TLV对应的立体声版本,通常用于立体声音量控件的定义。

SOC_DOUBLE_R_TLV  左右声道有独立寄存器控制的SOC_DOUBLE_TLV版本

Mixer控件

Mixer控件用于音频通道的路由控制,由多个输入和一个输出组成,多个输入可以自由地混合在一起,形成混合后的输出:

     图1  Mixer混音器

对于Mixer控件,我们可以认为是多个简单控件的组合,通常,我们会为mixer的每个输入端都单独定义一个简单控件来控制该路输入的开启和关闭,反应在代码上,就是定义一个soc_kcontrol_new数组:

[cpp] viewplaincopy

static const struct snd_kcontrol_new left_speaker_mixer[] = {  

SOC_SINGLE("Input Switch", WM8993_SPEAKER_MIXER, 7, 1, 0),  

SOC_SINGLE("IN1LP Switch", WM8993_SPEAKER_MIXER, 5, 1, 0),  

SOC_SINGLE("Output Switch", WM8993_SPEAKER_MIXER, 3, 1, 0),  

SOC_SINGLE("DAC Switch", WM8993_SPEAKER_MIXER, 6, 1, 0),  

};  

以上这个mixer使用寄存器WM8993_SPEAKER_MIXER的第3,5,6,7位来分别控制4个输入端的开启和关闭。

Mux控件

mux控件与mixer控件类似,也是多个输入端和一个输出端的组合控件,与mixer控件不同的是,mux控件的多个输入端同时只能有一个被选中。

因此,mux控件所对应的寄存器,通常可以设定一段连续的数值,每个不同的数值对应不同的输入端被打开,与上述的mixer控件不同,ASoc用soc_enum结构来描述mux控件的寄存器信息:

[cpp] viewplaincopy

/* 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控件:

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

[cpp] viewplaincopy

static const char *drc_path_text[] = {  

        "ADC",  

        "DAC"  

};  

第二步,利用ASoc提供的辅助宏定义soc_enum结构,用于描述寄存器:

[cpp] viewplaincopy

static const struct soc_enum drc_path =  

        SOC_ENUM_SINGLE(WM8993_DRC_CONTROL_1, 14, 2, drc_path_text);  

第三步,利痛ASoc提供的辅助宏,定义soc_kcontrol_new结构,该结构最后用于注册该mux控件:

[cpp] viewplaincopy

static const struct snd_kcontrol_new wm8993_snd_controls[] = {  

SOC_DOUBLE_TLV(......),  

......  

SOC_ENUM("DRC Path", drc_path),  

......  

}  

以上几步定义了一个叫DRCPATH的mux控件,该控件具有两个输入选择,分别是来自ADC和DAC,用寄存器WM8993_DRC_CONTROL_1控制。

其中,soc_enum结构使用了辅助宏SOC_ENUM_SINGLE来定义,该宏的声明如下:

[cpp] viewplaincopy

#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,列出它的定义如下:

[cpp] viewplaincopy

#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结构,不过几个回调函数变了,我们看看get回调对应的snd_soc_get_enum_double函数:

[cpp] viewplaincopy

int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol,  

        struct snd_ctl_elem_value *ucontrol)  

{  

        struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);  

        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;  

        unsigned int val;  

  

        val = snd_soc_read(codec, e->reg);  

        ucontrol->value.enumerated.item[0]  

                = (val >> e->shift_l) & e->mask;  

        if (e->shift_l !

= e->shift_r)  

                ucontrol->value.enumerated.item[1] =  

                        (val >> e->shift_r) & e->mask;  

  

        return 0;  

}  

通过info回调函数则可以获取某个输入端所对应的名字,其实就是从soc_enum结构的texts数组中获得:

[cpp] viewplaincopy

int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol,  

        struct snd_ctl_elem_info *uinfo)  

{  

        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;  

  

        uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;  

        uinfo->count = e->shift_l == e->shift_r ?

 1 :

 2;  

        uinfo->value.enumerated.items = e->max;  

  

        if (uinfo->value.enumerated.item > e->max - 1)  

                uinfo->value.enumerated.item = e->max - 1;  

        strcpy(uinfo->value.enumerated.name,  

                e->texts[uinfo->value.enumerated.item]);  

        return 0;  

}  

以下是另外几个常用于定义mux控件的宏:

SOC_VALUE_ENUM_SINGLE  用于定义带values字段的soc_enum结构。

SOC_VALUE_ENUM_DOUBLE  SOC_VALUE_ENUM_SINGLE的立体声版本。

SOC_VALUE_ENUM   用于定义带values字段snd_kcontrol_new结构,这个有点特别,我们还是看看它的定义:

[cpp] viewplaincopy

#define SOC_VALUE_ENUM(xname, xenum) \  

{       .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,\  

        .info = snd_soc_info_enum_double, \  

        .get = snd_soc_get_value_enum_double, \  

        .put = snd_soc_put_value_enum_double, \  

        .private_value = (unsigned long)&xenum }  

从定义可以看出来,回调函数被换掉了,我们看看他的get回调:

[cpp] viewplaincopy

int snd_soc_get_value_enum_double(struct snd_kcontrol *kcontrol,  

        struct snd_ctl_elem_value *ucontrol)  

{  

        struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);  

        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;  

        unsigned int reg_val, val, mux;  

  

        reg_val = snd_soc_read(codec, e->reg);  

        val = (reg_val >> e->shift_l) & e->mask;  

        for (mux = 0; mux < e->max; mux++) {  

                if (val == e->values[mux])  

                        break;  

        }  

        ucontrol->value.enumerated.item[0] = mux;  

        if (e->shift_l !

= e->shift_r) {  

                val = (reg_val >> e->shift_r) & e->mask;  

                for (mux = 0; mux < e->max; mux++) {  

                        if (val == e->values[mux])  

                                break;  

                }  

                ucontrol->value.enumerated.item[1] = mux;  

        }  

  

        return 0;  

}  

与SOC_ENUM定义的mux不同,它没有直接返回寄存器的设定值,而是通过soc_enum结构中的values字段做了一次转换,与values数组中查找和寄存器相等的值,然后返回他在values数组中的索引值,所以,尽管寄存器的值可能是不连续的,但返回的值是连续的。

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 高中教育 > 语文

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1