- 论坛徽章:
- 0
|
内核Alsa之ASoC
http://www.alivepea.me/kernel/alsa-asoc/
ASoC是Alsa System on Chip的缩写,用于实现那些集成了声音控制器 的CPU,像移动设备中的arm/mips/atom等。它的设计目标如下:
解耦codec. codec的驱动不依赖具体的平台。
简单易用的I2S/PCM配置接口。让soc和codec的配置相匹配。
动态的电源管理DAPM。实现对用户空间透明的电源管理,各个widget按需供电,实现功耗最小化。
消除pop音。控制各个widget上下电的顺序消除pop音。
添加平台相关的控制。如earphone, speaker.
为实现这个目标,有了下面的ASoC架构。
ASOC架构
ASOC将soc_snd_card逻辑上分为codec/platform/machine三个组件。
Codec. 用于实现平台无关的功能,如寄存器读写接口,音频接口,各widgets的控制接口和DAPM的实现等。
Platform. 用于实现平台相关的DMA驱动和音频接口等。
Machine. 用于描述设备组件信息和特定的控制如耳机/外放等。
ASOC的架构如下图。
ASoC对于Alsa来说,就是分别注册PCM/CONTROL类型的snd_device设备,并实现相应的操 作方法集。图中DAI是数字音频接口,用于配置音频数据格式等。
Codec驱动向ASoC注册snd_soc_codec和snd_soc_dai设备。
Platform驱动向ASoC注册snd_soc_platform和snd_soc_dai设备。
Machine驱动通过snd_soc_dai_link绑定codec/dai/platform.
Widget是各个组件内部的小单元。处在活动通路上电,不在活动通路下电。ASoC的DAPM 正是通过控制这些Widget的上下电达到动态电源管理的效果。
path描述与其它widget的连接关系。
event用于通知该widget的上下电状态,power指示当前的上电状态。
control实现空间用户接口用于控制widget的音量/通路切换等。
对驱动开者来说,就可以很好的解耦了:
codec驱动的开发者,实现codec的IO读写方法,描述DAI支持的数据格式/操作方法和Widget的连接关系就可以了;
soc芯片的驱动开发者,Platform实现snd_pcm的操作方法集和DAI的配置如操作 DMA,I2S/AC97/PCM的设定等;
板级的开发者,描述Machine上codec与platform之间的总线连接, earphone/Speaker的布线情况就可以了。
数据结构
晒一下snd_soc_card数据结构的主要成员以一窥究竟吧。
struct snd_soc_card {
const char *name; /* 传递给snd_card字段 */
const char *long_name;
const char *driver_name;
struct device *dev; /* */
struct snd_card *snd_card; /* 指向alsa注册的snd_card结构 */
int (*probe)(struct snd_soc_card *card); /* 用于添加板级的widget如earphone等 */
int (*late_probe)(struct snd_soc_card *card);
int (*remove)(struct snd_soc_card *card);
/* callbacks */
int (*set_bias_level)(struct snd_soc_card *, /* DAPM 全局功耗级别设定 */
struct snd_soc_dapm_context *dapm,
enum snd_soc_bias_level level);
int (*set_bias_level_post)(struct snd_soc_card *,
struct snd_soc_dapm_context *dapm,
enum snd_soc_bias_level level);
/* CPU <--> Codec DAI links */
struct snd_soc_dai_link *dai_link; /* Machine驱动描述的各组件绑定关系 */
int num_links;
struct snd_soc_pcm_runtime *rtd; /* card注册后,dai_link对应的各组件数据结构 */
int num_rtd;
const struct snd_kcontrol_new *controls; /* Machine描述的特定control */
int num_controls;
/*
* Card-specific routes and widgets.
*/
const struct snd_soc_dapm_widget *dapm_widgets; /* Machine描述的特定的widget */
int num_dapm_widgets;
const struct snd_soc_dapm_route *dapm_routes; /* Machine描述的widgets之间的连通关系 */
int num_dapm_routes;
bool fully_routed; /* 不存在没法连通的widget */
/* lists of probed devices belonging to this card */
struct list_head codec_dev_list; /* card上的codec设备链接头 */
struct list_head platform_dev_list; /* card上platform链接头 */
struct list_head dai_dev_list; /* card上dai链接头 */
struct list_head widgets; /* 所有组件上widgets的链接头 */
struct list_head paths; /* 所有widgets之间path的链接头 */
struct list_head dapm_list; /* 各个组件dapm的链接头 */
struct list_head dapm_dirty; /* 需要更新power状态的widgets链接头 */
/* Generic DAPM context for the card */
struct snd_soc_dapm_context dapm;
struct snd_soc_dapm_stats dapm_stats;
struct snd_soc_dapm_update *update; /* 对widget除上电之外的操作,如通道切换等。 */
u32 pop_time; /* 在widget上下电时防pop音 */
};
没有code直接上结构体似乎没有多少意义,以后用Entity Relationship画图表示各个 模块的关系是不是好点呢?
调试
ASoC添加了DEBUGFS和FTRACE的调试支持。
在DEBUGFS下,可以查看一个各个组件及widgets的状态。
在FTRACE下,echo asoc >> set_event打开调试,就可以查看widget的上下电顺序, 通路的切换等。
看起来很贴心呐,是不是有点小激动啊☺
~EOF~
|
评分
-
查看全部评分
|