Linux ASOC音频驱动架构分析
2016-06-01 16:36
459 查看
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
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
在(1)中的snd_soc_dai_ops结构体则描述CODEC的时钟、PLL以及各式设置,主要包括set_sysclk()、set_pll()、set_clkdiv()、set_fmt()等成员函数。
位于内核源代码include/sound/soc-dai.h
(3)CODEC的mixer控制
ASoC中定义了一组宏来描述CODEC的mixer控制,这组宏可以方便地将mixer名和对应的寄存器进行绑定,主要包括:
位于内核源代码include/sound/soc.h
SOC_SINGLE(xname, reg, shift, max, invert) /*参数xname是mixer的名字,reg是控制该mixer的寄存器,shift对应寄存器内的位,max是进行操作时的最大数,invert表明是否极性倒序或翻转*/
SOC_DOUBLE(xname, xreg, shift_left, shift_right, xmax, xinvert)
SOC_ENUM(xname, xenum)
SOC_ENUM_SINGLE(xreg, xshift, xmax, xtexts)
/*带x的,如xreg、xmax、xinvert、xenum、xshift、xtexts数据类型是unsigned long,不带x的为默认类型,见上面两个结构体。
4)CODEC音频操作
在ASoC驱动的CODEC部分也需要关心音频流开始采集和播放时的一些动作,如hw_params()、hw_free()、prepare()、trigger()这些操作,不过与原始ALSA不同的是,在CODEC驱动的这些函数中,不关心CPU端,而只关心CODEC本身,由结构体and_soc_ops描述。
位于内核源代码include/sound/soc.h
这个结构体在linux2.6.32.2中已经使用位于include/sound/soc-dai.h中snd_soc_dai_ops结构体来描述,见sound/soc/codecs/uda134x.c
以上是对于ASoC CODEC驱动的工作进行描述和几个重要的数据结构。
2.ASoC平台驱动
首先,在ASoC平台驱动部分,同样存在着CODEC驱动中的snd_soc_dai、snd_soc_dai_ops、snd_soc_ops(这个结构体在linux2.6.32.2中已经交给snd_soc_dai_ops结构体来描述)这3个结构体的实例用于描述DAI和DAI上操作,不过不同的是,在平台驱动中,它们只描述CPU相关的部分而不描述CODEC。除此之外,在ASoC平台驱动中,必须实现完整的DMA驱动,即传统ALSA的snd_pcm_ops结构体成员函数trigger()、pointer()等。因此ASoC平台驱动通常由DAI和DMA两部分组成:
snd_soc_dai、snd_soc_dai_ops、snd_soc_ops这3个结构体和在CODEC驱动中一样,只是在这里只描述CPU相关的部分,对CPU中设计到的寄存器进行设置;
snd_pcm_ops结构体描述DMA操作和设置;
snd_pcm_ops结构体
位于内核源代码include/sound/pcm.h
3.ASoC板驱动
ASoC板驱动直接与板对应,对于一块确定的电路板,其SoC和CODEC都是确定的,因此板驱动将ASoC CODEC驱动和CPU端的平台驱动进行绑定,这个绑定用数据结构snd_soc_dai_link描述
位于内核源代码include/sound/soc.h
原文地址:http://www.linuxidc.com/Linux/2012-10/73066.htm
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 *); /*硬件参数释放*/ int (*prepare)(struct snd_pcm_substream *, struct snd_soc_dai *); /*准 c48e 备*/ int (*trigger)(struct snd_pcm_substream *, int, struct snd_soc_dai *); /*在PCM被开始、停止或暂停时被调用*/ };
(3)CODEC的mixer控制
ASoC中定义了一组宏来描述CODEC的mixer控制,这组宏可以方便地将mixer名和对应的寄存器进行绑定,主要包括:
位于内核源代码include/sound/soc.h
/* mixer control */ /*mixer控制*/ struct soc_mixer_control { int min, max; unsigned int reg, rreg, shift, rshift, invert; }; /* 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 **texts; const unsigned int *values; void *dapm; };
SOC_SINGLE(xname, reg, shift, max, invert) /*参数xname是mixer的名字,reg是控制该mixer的寄存器,shift对应寄存器内的位,max是进行操作时的最大数,invert表明是否极性倒序或翻转*/
SOC_DOUBLE(xname, xreg, shift_left, shift_right, xmax, xinvert)
SOC_ENUM(xname, xenum)
SOC_ENUM_SINGLE(xreg, xshift, xmax, xtexts)
/*带x的,如xreg、xmax、xinvert、xenum、xshift、xtexts数据类型是unsigned long,不带x的为默认类型,见上面两个结构体。
4)CODEC音频操作
在ASoC驱动的CODEC部分也需要关心音频流开始采集和播放时的一些动作,如hw_params()、hw_free()、prepare()、trigger()这些操作,不过与原始ALSA不同的是,在CODEC驱动的这些函数中,不关心CPU端,而只关心CODEC本身,由结构体and_soc_ops描述。
位于内核源代码include/sound/soc.h
/* SoC audio ops */ struct snd_soc_ops { int (*startup)(struct snd_pcm_substream *); void (*shutdown)(struct snd_pcm_substream *); int (*hw_params)(struct snd_pcm_substream *, struct snd_pcm_hw_params *); int (*hw_free)(struct snd_pcm_substream *); int (*prepare)(struct snd_pcm_substream *); int (*trigger)(struct snd_pcm_substream *, int); };
这个结构体在linux2.6.32.2中已经使用位于include/sound/soc-dai.h中snd_soc_dai_ops结构体来描述,见sound/soc/codecs/uda134x.c
以上是对于ASoC CODEC驱动的工作进行描述和几个重要的数据结构。
2.ASoC平台驱动
首先,在ASoC平台驱动部分,同样存在着CODEC驱动中的snd_soc_dai、snd_soc_dai_ops、snd_soc_ops(这个结构体在linux2.6.32.2中已经交给snd_soc_dai_ops结构体来描述)这3个结构体的实例用于描述DAI和DAI上操作,不过不同的是,在平台驱动中,它们只描述CPU相关的部分而不描述CODEC。除此之外,在ASoC平台驱动中,必须实现完整的DMA驱动,即传统ALSA的snd_pcm_ops结构体成员函数trigger()、pointer()等。因此ASoC平台驱动通常由DAI和DMA两部分组成:
snd_soc_dai、snd_soc_dai_ops、snd_soc_ops这3个结构体和在CODEC驱动中一样,只是在这里只描述CPU相关的部分,对CPU中设计到的寄存器进行设置;
snd_pcm_ops结构体描述DMA操作和设置;
snd_pcm_ops结构体
位于内核源代码include/sound/pcm.h
struct snd_pcm_ops { int (*open)(struct snd_pcm_substream *substream); int (*close)(struct snd_pcm_substream *substream); int (*ioctl)(struct snd_pcm_substream * substream, unsigned int cmd, void *arg); /*驱动中通常可以给ioctl()成员函数传递通用的snd_pcm_lib_ioctl()函数*/ int (*hw_params)(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params); /* hw_params()成员函数将在应用程序设置硬件参数(PCM子流的周期大小、缓冲区大小和格式等)的时候被调用*/ int (*hw_free)(struct snd_pcm_substream *substream); /*释放由hw_params()分配的资源*/ int (*prepare)(struct snd_pcm_substream *substream); /*当PCM被“准备”时,prepare()函数被调用,在其中可以设置采样率、格式等*/ int (*trigger)(struct snd_pcm_substream *substream, int cmd); /*trigger()成员函数在PCM被开始、停止或暂停时调用*/ snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *substream); /*pointer()函数用于PCM中间层查询目前缓冲区的硬件设置,该函数以帧的形式返回0~buffer_sise-1的位置(ALSA 0.5.x中为字节形式),此函数也是原子的*/ int (*copy)(struct snd_pcm_substream *substream, int channel, snd_pcm_uframes_t pos, void __user *buf, snd_pcm_uframes_t count); int (*silence)(struct snd_pcm_substream *substream, int channel, snd_pcm_uframes_t pos, snd_pcm_uframes_t count); struct page *(*page)(struct snd_pcm_substream *substream, unsigned long offset); int (*mmap)(struct snd_pcm_substream *substream, struct vm_area_struct *vma); int (*ack)(struct snd_pcm_substream *substream); };
3.ASoC板驱动
ASoC板驱动直接与板对应,对于一块确定的电路板,其SoC和CODEC都是确定的,因此板驱动将ASoC CODEC驱动和CPU端的平台驱动进行绑定,这个绑定用数据结构snd_soc_dai_link描述
位于内核源代码include/sound/soc.h
/* SoC machine DAI configuration, glues a codec and cpu DAI together */ struct snd_soc_dai_link { char *name; /* Codec name */ char *stream_name; /* Stream name */ /* DAI */ struct snd_soc_dai *codec_dai; struct snd_soc_dai *cpu_dai; /* machine stream operations */ /*板流操作*/ struct snd_soc_ops *ops; /* codec/machine specific init - e.g. add machine controls */ /*codec/machine 特定的初始化*/ int (*init)(struct snd_soc_codec *codec); /* Symmetry requirements */ unsigned int symmetric_rates:1; /* Symmetry data - only valid if symmetry is being enforced */ unsigned int rate; /* DAI pcm */ struct snd_pcm *pcm; };
原文地址:http://www.linuxidc.com/Linux/2012-10/73066.htm
相关文章推荐
- Linux socket 初步
- Linux Kernel 4.0 RC5 发布!
- linux lsof详解
- linux 文件权限
- Linux 执行数学运算
- 10 篇对初学者和专家都有用的 Linux 命令教程
- Linux 与 Windows 对UNICODE 的处理方式
- Ubuntu12.04下QQ完美走起啊!走起啊!有木有啊!
- 解決Linux下Android开发真机调试设备不被识别问题
- 运维入门
- 运维提升
- Linux 自检和 SystemTap
- Ubuntu Linux使用体验
- c语言实现hashmap(转载)
- Linux 信号signal处理机制
- linux下mysql添加用户
- Scientific Linux 5.5 图形安装教程
- Linux 下无损图片压缩小工具介绍