Mini2440开发板
Kernel:linux 2.6.32.2
音频基于i2s总线接口(和l3总线接口对混音器进行设置)
Linux ASoC音频设备驱动
ASoC驱动的组成
ASoC(ALSA System on Chip)是ALSA在SoC方面的发展和演变,它在本质上仍然属于ALSA,但是在ALSA架构的基础上对CPU相关的代码和CODEC相关的代码进行了分离。其原因是,采用传统ALSA架构的情况下,同一型号的CODEC工作于不同的CPU时,需要不同的驱动,这不符合代码重用的要求。
ASoC主要由3部分组成。
(1)CODEC驱动。这一部分只关心CODEC本身,与CPU平台相关的特性不由此部分操作。
(2)平台驱动。这一部分只关心CPU本身,不关心CODEC。它主要处理两个问题:DMA引擎和SoC集成的PCM、I2S或AC’97数字接口的控制。
(3)板驱动。也称为machine驱动,这一部分将平台驱动和CODEC驱动绑定在一起,描述了板一级的硬件特征。
在以上3部分中,1和2基本上都可以仍然是通用的驱动了,也就是说,CODEC驱动认为自己可以连接任意CPU,而CPU的I2S、PCM、或AC’97接口对应的平台驱动则认为自己可以连接任意符合接口类型的CODEC,只有3是不通用的,由特性的电路板上具体的CPU和CODEC确定,因此它很像一个插座,上面插上了CODEC和平台这两个插头。
在以上三部分之上的是ASoC核心层,由内核源代码中的sound/soc/soc-core.c实现,查看其源代码发现它完全是一个传统的ALSA驱动。因此,对于基于ASoC架构的声卡驱动而言,alsa-lia以及ALSA的一系列utility仍然是可用的,如amixer、aplay均无需针对ASoC进行任何改动。而ASoC的用户编程方法也和ALSA完全一致。
内核源代码的Documentation/sound/slsa/soc目录包含了ASoC相关的文档。
1.ASoC CODEC驱动
在ASoC架构下,CODEC驱动负责如下工作:
(1)CODEC DAI(Digital Audio Interfaces)和配置PCM,由结构体snd_soc_dai来描述,形容playback、capture的属性以及DAI接口的操作。
位于内核源代码include/sound/soc-dai.h
/*
* Digital Audio Interface runtime data.
*
* Holds runtime data for a DAI.
*/
struct snd_soc_dai {
/* DAI description */ /*DAI的描述*/
char *name;
unsigned int id;
int ac97_control;
struct device *dev;
void *ac97_pdata; /* platform_data for the ac97 codec */ /*ac97平台数据*/
/* DAI callbacks */
int (*probe)(struct platform_device *pdev,
struct snd_soc_dai *dai);
void (*remove)(struct platform_device *pdev,
struct snd_soc_dai *dai);
int (*suspend)(struct snd_soc_dai *dai);
int (*resume)(struct snd_soc_dai *dai);
/* ops */
struct snd_soc_dai_ops *ops;
/* DAI capabilities */ /*DAI的能力*/
struct snd_soc_pcm_stream capture; /*录音*/
struct snd_soc_pcm_stream playback; /*放音*/
unsigned int symmetric_rates:1;
/* DAI runtime info */ /*DAI运行时的信息*/
struct snd_pcm_runtime *runtime;
struct snd_soc_codec *codec;
unsigned int active;
unsigned char pop_wait:1;
void *dma_data;
/* DAI private data */ /*DAI私有数据*/
void *private_data;
/* parent platform */ /*父平台*/
struct snd_soc_platform *platform;
struct list_head list; /*循环、双向链表*/
};
2)CODEC IO操作、动态音频电源管理以及时钟、PLL等控制
在(1)中的snd_soc_codec 结构体是对CODEC本身I/O控制以及动态音频电源管理(Dynamic Audio Power Management,DAPM)的描述。它描述I2C、SPI或AC’97如何读写CODEC寄存器并容纳DAPM链表,核心成员为read()、write()、hw_write()、hw_read()、dapm_widgets、dapm_paths等。
位于内核源代码include/sound/soc.h
/* SoC Audio Codec */
struct snd_soc_codec {
char *name;
struct module *owner;
struct mutex mutex;
struct device *dev;
struct snd_soc_device *socdev;
struct list_head list;
/* callbacks */
int (*set_bias_level)(struct snd_soc_codec *,
enum snd_soc_bias_level level);
/* runtime */
struct snd_card *card;
struct snd_ac97 *ac97; /* for ad-hoc ac97 devices */
unsigned int active;
unsigned int pcm_devs;
void *private_data;
/* codec IO */
void *control_data; /* codec control (i2c/3wire) data */
unsigned int (*read)(struct snd_soc_codec *, unsigned int);
int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);
int (*display_register)(struct snd_soc_codec *, char *,
size_t, unsigned int);
int (*volatile_register)(unsigned int);
int (*readable_register)(unsigned int);
hw_write_t hw_write;
unsigned int (*hw_read)(struct snd_soc_codec *, unsigned int);
void *reg_cache;
short reg_cache_size;
short reg_cache_step;
/* dapm */
u32 pop_time;
struct list_head dapm_widgets;
struct list_head dapm_paths;
enum snd_soc_bias_level bias_level;
enum snd_soc_bias_level suspend_bias_level;
struct delayed_work delayed_work;
/* codec DAI's */
struct snd_soc_dai *dai;
unsigned int num_dai;
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs_reg;
struct dentry *debugfs_pop_time;
struct dentry *debugfs_dapm;
#endif
};
在(1)中的snd_soc_dai_ops结构体则描述CODEC的时钟、PLL以及各式设置,主要包括set_sysclk()、set_pll()、set_clkdiv()、set_fmt()等成员函数。
位于内核源代码include/sound/soc-dai.h
/*
* Digital Audio Interface.
*
* Describes the Digital Audio Interface in terms of its ALSA, DAI and AC97
* operations and capabilities. Codec and platform drivers will register this
* structure for every DAI they have.
*
* This structure covers the clocking, formating and ALSA operations for each
* interface.
*/
struct snd_soc_dai_ops {
/*
* DAI clocking configuration, all optional.
* Called by soc_card drivers, normally in their hw_params.
*/ /*DAI时钟配置*/
int (*set_sysclk)(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir);
int (*set_pll)(struct snd_soc_dai *dai,
int pll_id, unsigned int freq_in, unsigned int freq_out);
int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div);
/*
* DAI format configuration
* Called by soc_card drivers, normally in their hw_params.
*/ /*DAI格式设置*/
int (*set_fmt)(struct snd_soc_dai *dai, unsigned int fmt);
int (*set_tdm_slot)(struct snd_soc_dai *dai,
unsigned int tx_mask, unsigned int rx_mask,
int slots, int slot_width);
int (*set_tristate)(struct snd_soc_dai *dai, int tristate);
/*
* DAI digital mute - optional.
* Called by soc-core to minimise any pops.
*/ /*数字静音*/
int (*digital_mute)(struct snd_soc_dai *dai, int mute);
/*
* ALSA PCM audio operations - all optional.
* Called by soc-core during audio PCM operations.
*/ /*在操作PCM时由soc-core调用进行PCM操作*/
/*音频流开始采集和播放时的一些动作*/
int (*startup)(struct snd_pcm_substream *,
struct snd_soc_dai *); /*启动*/
void (*shutdown)(struct snd_pcm_substream *,
struct snd_soc_dai *); /*关闭*/
int (*hw_params)(struct snd_pcm_substream *,
struct snd_pcm_hw_params *, struct snd_soc_dai *); /*硬件参数设置*/
int (*hw_free)(struct snd_pcm_substream *,
struct snd_soc_dai *); /*硬件参数释放*/