ALSA声卡驱动详解(7)

2019-08-31 00:43

}

上面这种注册方法有个缺点,有时候我们为了代码的清晰,可能会根据功能把不同的widget定义成多个数组,但是snd_soc_codec_driver中只有一个

dapm_widgets字段,无法设定多个widget数组,这时候,我们需要主动在codec的probe回调中调用dapm框架提供的api来创建这些widget: [cpp] view plaincopy

static int wm8993_probe(struct snd_soc_codec *codec) {

......

snd_soc_dapm_new_controls(dapm, wm8993_dapm_widgets, ARRAY_SIZE(wm8993_dapm_widgets));

...... }

实际上,对于第一种方法,snd_soc_register_codec内部其实也是调用

snd_soc_dapm_new_controls来完成的。后面会有关于这个函数的详细分析。

platform驱动中注册 和codec驱动一样,我们会通过ASoc提供的api函数snd_soc_register_platform来注册一个platform驱动,该函数的第二个参数是一个snd_soc_platform_driver结构指针,snd_soc_platform_driver结构中同样也包含了与dapm相关的字段: [cpp] view plaincopy

struct snd_soc_platform_driver { ......

/* Default control and setup, added after probe() is run */

const struct snd_kcontrol_new *controls; int num_controls;

const struct snd_soc_dapm_widget *dapm_widgets; int num_dapm_widgets;

const struct snd_soc_dapm_route *dapm_routes; int num_dapm_routes; ...... }

要注册platform级别的widget,和codec驱动一样,只要把定义好的widget数组赋值给dapm_widgets和num_dapm_widgets字段即可,

snd_soc_register_platform函数注册paltform后,当machine驱动匹配上该

platform时,系统会自动完成创建和注册的工作。同理,我们也可以在platform驱动的probe回调函数中主动使用snd_soc_dapm_new_controls来完成widget的创建工作。具体的代码和codec驱动是类似的,这里就不贴了。

machine驱动中注册 有些widget可能不是位于codec中,例如一个独立的耳机放大器,或者是喇叭功放等,这种widget通常需要在machine驱动中注册,通常

31

他们的dapm context也从属于声卡(snd_soc_card)域。做法依然和codec驱动类似,通过代表声卡的snd_soc_card结构中的几个dapm字段完成: [cpp] view plaincopy struct snd_soc_card { ...... /*

* Card-specific routes and widgets. */

const struct snd_soc_dapm_widget *dapm_widgets; int num_dapm_widgets;

const struct snd_soc_dapm_route *dapm_routes; int num_dapm_routes; bool fully_routed; ...... }

只要把定义好的widget数组和数量赋值给dapm_widgets指针和

num_dapm_widgets即可,注册声卡使用的api:snd_soc_register_card(),也会通过snd_soc_dapm_new_controls来完成widget的创建工作。

注册音频路径

系统中注册的各种widget需要互相连接在一起才能协调工作,连接关系通过

snd_soc_dapm_route结构来定义,关于如何用snd_soc_dapm_route结构来定义路径信息,请参考:ALSA声卡驱动中的DAPM详解之三:如何定义各种widget中的\建立widget和route\一节的内容。通常,所有的路径信息会用一个

snd_soc_dapm_route结构数组来定义。和widget一样,路径信息也分别存在与codec驱动,machine驱动和platform驱动中,我们一样有两种方式来注册音频路径信息:

通过snd_soc_codec_driver/snd_soc_platform_driver/snd_soc_card结构中的dapm_routes和num_dapm_routes字段;

在codec、platform的的probe回调中主动注册音频路径,machine驱动中则通过snd_soc_dai_link结构的init回调函数来注册音频路径;

两种方法最终都是通过调用snd_soc_dapm_add_routes函数来完成音频路径的注册工作的。以下的代码片段是omap的pandora板子的machine驱动,使用第二种方法注册路径信息: [cpp] view plaincopy

static const struct snd_soc_dapm_widget omap3pandora_in_dapm_widgets[] = {

SND_SOC_DAPM_MIC(\, NULL), SND_SOC_DAPM_MIC(\, NULL), SND_SOC_DAPM_LINE(\, NULL),

32

};

static const struct snd_soc_dapm_route omap3pandora_out_map[] = {

{\, NULL, \},

{\, NULL, \}, {\, NULL, \},

{\, NULL, \}, };

static const struct snd_soc_dapm_route omap3pandora_in_map[] = {

{\, NULL, \}, {\, NULL, \},

{\, NULL, \}, {\, NULL, \},

{\, NULL, \},

{\, NULL, \}, };

static int omap3pandora_out_init(struct snd_soc_pcm_runtime *rtd) {

struct snd_soc_codec *codec = rtd->codec;

struct snd_soc_dapm_context *dapm = &codec->dapm; int ret;

/* All TWL4030 output pins are floating */ snd_soc_dapm_nc_pin(dapm, \); ......

//注册kcontrol控件

ret = snd_soc_dapm_new_controls(dapm, omap3pandora_out_dapm_widgets,

ARRAY_SIZE(omap3pandora_out_dapm_widgets));

if (ret < 0)

return ret;

//注册machine的音频路径

return snd_soc_dapm_add_routes(dapm, omap3pandora_out_map,

ARRAY_SIZE(omap3pandora_out_map)); }

33

static int omap3pandora_in_init(struct snd_soc_pcm_runtime *rtd) {

struct snd_soc_codec *codec = rtd->codec;

struct snd_soc_dapm_context *dapm = &codec->dapm; int ret;

/* Not comnnected */

snd_soc_dapm_nc_pin(dapm, \); ......

//注册kcontrol控件

ret = snd_soc_dapm_new_controls(dapm, omap3pandora_in_dapm_widgets,

ARRAY_SIZE(omap3pandora_in_dapm_widgets));

if (ret < 0)

return ret; //注册machine音频路径

return snd_soc_dapm_add_routes(dapm, omap3pandora_in_map,

ARRAY_SIZE(omap3pandora_in_map)); }

/* Digital audio interface glue - connects codec <--> CPU */ static struct snd_soc_dai_link omap3pandora_dai[] = { {

.name = \, ......

.init = omap3pandora_out_init, }, {

.name = \,

.stream_name = \, ......

.init = omap3pandora_in_init, } };

dai widget

上面几节的内容介绍了codec、platform以及machine级别的widget和route的注册方法,在dapm框架中,还有另外一种widget,它代表了一个dai(数字音频接口),关于dai的描述,请参考:Linux ALSA声卡驱动之七:ASoC架构中的Codec。dai按所在的位置,又分为cpu dai和codec dai,在硬件上,通常一个

34

cpu dai会连接一个codec dai,而在machine驱动中,我们要在snd_soc_card结构中指定一个叫做snd_soc_dai_link的结构,该结构定义了声卡使用哪一个cpu dai和codec dai进行连接。在Asoc中,一个dai用snd_soc_dai结构来表述,其中有几个字段和dapm框架有关: [cpp] view plaincopy struct snd_soc_dai { ......

struct snd_soc_dapm_widget *playback_widget; struct snd_soc_dapm_widget *capture_widget; struct snd_soc_dapm_context dapm; ...... }

dai由codec驱动和平台代码中的iis或pcm接口驱动注册,machine驱动负责找到snd_soc_dai_link中指定的一对cpu/codec dai,并把它们进行绑定。不管是cpu dai还是codec dai,通常会同时传输播放和录音的音频流的能力,所以我们可以看到,snd_soc_dai中有两个widget指针,分别代表播放流和录音流。这两个dai widget是何时创建的呢?下面我们逐一进行分析。 codec dai widget

首先,codec驱动在注册codec时,会传入该codec所支持的dai个数和记录dai信息的snd_soc_dai_driver结构指针: [cpp] view plaincopy

static struct snd_soc_dai_driver wm8993_dai = { .name = \, .playback = {

.stream_name = \, .channels_min = 1, .channels_max = 2,

.rates = WM8993_RATES,

.formats = WM8993_FORMATS, .sig_bits = 24, },

.capture = {

.stream_name = \, .channels_min = 1, .channels_max = 2,

.rates = WM8993_RATES,

.formats = WM8993_FORMATS, .sig_bits = 24, },

.ops = &wm8993_ops, .symmetric_rates = 1, };

35


ALSA声卡驱动详解(7).doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:龙虎山风景区上清镇水生态文明自主创建项目实施方案 - 图文

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: