ALSA声卡驱动详解(6)

2019-08-31 00:43

left_speaker_mixer和right_speaker_mixer来完成,两个widget具备电源属性,所以,当这两个widget在一条有效的音频路径上时,dapm框架可以通过寄存器WM8993_POWER_MANAGEMENT_3的第8位和第9位控制它的电源状态。

第三步,定义这些widget的连接路径: [cpp] view plaincopy

static const struct snd_soc_dapm_route routes[] = { ......

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

......

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

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

通过第一步的定义,我们知道DACL Mux和DACR Mux有两个输入引脚,分别是 Left Right

而SPKL和SPKR有四个输入选择引脚,分别是: Input Switch

IN1LP Switch/IN1RP Switch Output Switch DAC Switch

所以,很显然,上面的路径定义的意思就是: AIFINL连接到DACL Mux的Left输入脚 AIFINR连接到DACL Mux的Right输入脚 AIFINL连接到DACR Mux的Left输入脚 AIFINR连接到DACR Mux的Right输入脚 DACL连接到SPKL的DAC Switch输入脚 DACR连接到SPKR的DAC Switch输入脚

第四步,在codec驱动的probe回调中注册这些widget和路径: [cpp] view plaincopy

static int wm8993_probe(struct snd_soc_codec *codec) {

......

snd_soc_dapm_new_controls(dapm, wm8993_dapm_widgets,

26

ARRAY_SIZE(wm8993_dapm_widgets));

......

snd_soc_dapm_add_routes(dapm, routes, ARRAY_SIZE(routes));

...... }

在machine驱动中,我们可以用同样的方式定义和注册板子特有的widget和路径信息。

1.ALSA声卡驱动中的DAPM详解之四:在驱动程序中初始化并注册widget和route

前几篇文章我们从dapm的数据结构入手,了解了代表音频控件的widget,代表连接路径的route以及用于连接两个widget的path。之前都是一些概念的讲解以及对数据结构中各个字段的说明,从本章开始,我们要从代码入手,分析dapm的详细工作原理: 如何注册widget

如何连接两个widget

一个widget的状态裱画如何传递到整个音频路径中

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

声明:本博内容均由http://blog.csdn.net/droidphone原创,转载请注明出处,谢谢!

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

dapm context

在讨论widget的注册之前,我们先了解另一个概念:dapm context,直译过来的意思是dapm上下文,这个好像不好理解,其实我们可以这么理解:dapm把整个音频系统,按照功能和偏置电压级别,划分为若干个电源域,每个域包含各自的widget,每个域中的所有widget通常都处于同一个偏置电压级别上,而一个电源域就是一个dapm context,通常会有以下几种dapm context: 属于codec中的widget位于一个dapm context中 属于platform的widget位于一个dapm context中 属于整个声卡的widget位于一个dapm context中

对于音频系统的硬件来说,通常要提供合适的偏置电压才能正常地工作,有了dapm context这种组织方式,我们可以方便地对同一组widget进行统一的偏置电压管理,ASoc用snd_soc_dapm_context结构来表示一个dapm context: [cpp] view plaincopy

27

struct snd_soc_dapm_context {

enum snd_soc_bias_level bias_level;

enum snd_soc_bias_level suspend_bias_level; struct delayed_work delayed_work;

unsigned int idle_bias_off:1; /* Use BIAS_OFF instead of STANDBY */

struct snd_soc_dapm_update *update;

void (*seq_notifier)(struct snd_soc_dapm_context *, enum snd_soc_dapm_type, int);

struct device *dev; /* from parent - for debug */ struct snd_soc_codec *codec; /* parent codec */

struct snd_soc_platform *platform; /* parent platform */

struct snd_soc_card *card; /* parent card */

/* used during DAPM updates */

enum snd_soc_bias_level target_bias_level; struct list_head list;

int (*stream_event)(struct snd_soc_dapm_context *dapm, int event);

#ifdef CONFIG_DEBUG_FS

struct dentry *debugfs_dapm; #endif };

snd_soc_bias_level的取值范围是以下几种: SND_SOC_BIAS_OFF

SND_SOC_BIAS_STANDBY SND_SOC_BIAS_PREPARE SND_SOC_BIAS_ON

snd_soc_dapm_context被内嵌到代表codec、platform、card、dai的结构体中: [cpp] view plaincopy struct snd_soc_codec { ......

/* dapm */

struct snd_soc_dapm_context dapm; ...... };

struct snd_soc_platform {

28

......

/* dapm */

struct snd_soc_dapm_context dapm; ...... };

struct snd_soc_card { ......

/* dapm */

struct snd_soc_dapm_context dapm; ...... }; :

struct snd_soc_dai { ......

/* dapm */

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

代表widget结构snd_soc_dapm_widget中,有一个snd_soc_dapm_context结构指针,指向所属的codec、platform、card、或dai的dapm结构。同时,所有的dapm结构,通过它的list字段,链接到代表声卡的snd_soc_card结构的dapm_list链表头字段。 创建和注册widget

我们已经知道,一个widget用snd_soc_dapm_widget结构体来描述,通常,我们会根据音频硬件的组成,分别在声卡的codec驱动、platform驱动和machine驱动中定义一组widget,这些widget用数组进行组织,我们一般会使用dapm框架提供的大量的辅助宏来定义这些widget数组,辅助宏的说明请参考前一偏文章:ALSA声卡驱动中的DAPM详解之三:如何定义各种widget。 codec驱动中注册 我们知道,我们会通过ASoc提供的api函数

snd_soc_register_codec来注册一个codec驱动,该函数的第二个参数是一个snd_soc_codec_driver结构指针,这个snd_soc_codec_driver结构需要我们在codec驱动中显式地进行定义,其中有几个与dapm框架有关的字段: [cpp] view plaincopy

struct snd_soc_codec_driver { ......

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

const struct snd_kcontrol_new *controls; int num_controls;

29

const struct snd_soc_dapm_widget *dapm_widgets; int num_dapm_widgets;

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

我们只要把我们定义好的snd_soc_dapm_widget结构数组的地址和widget的数量赋值到dapm_widgets和num_dapm_widgets字段即可,这样,经过

snd_soc_register_codec注册codec后,在machine驱动匹配上该codec时,系统会判断这两个字段是否被赋值,如果有,它会调佣dapm框架提供的api来创建和注册widget,注意这里我说还要创建这个词,你可能比较奇怪,既然代表

widget的snd_soc_dapm_widget结构数组已经在codec驱动中定义好了,为什么还要在创建?事实上,我们在codec驱动中定义的widget数组只是作为一个模板,dapm框架会根据该模板重新申请内存并初始化各个widget。我们看看实际的例子可能是这样的: [cpp] view plaincopy

static const struct snd_soc_dapm_widget wm8993_dapm_widgets[] = {

......

SND_SOC_DAPM_SUPPLY(\, SND_SOC_NOPM, 0, 0, NULL, 0),

SND_SOC_DAPM_AIF_IN(\, \, 0, SND_SOC_NOPM, 0, 0),

SND_SOC_DAPM_AIF_IN(\, \, 1, SND_SOC_NOPM, 0, 0),

...... };

static struct snd_soc_codec_driver soc_codec_dev_wm8993 = { .probe = codec_xxx_probe, ......

.dapm_widgets = &wm8993_dapm_widgets[0],

.num_dapm_widgets = ARRAY_SIZE(wm8993_dapm_widgets),

...... };

static int codec_wm8993_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) {

......

ret = snd_soc_register_codec(&i2c->dev,

&soc_codec_dev_wm8993, &wm8993_dai, 1); ......

30


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

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

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

马上注册会员

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