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 * );
- /*硬件参数释放*/
- int( * prepare)(struct snd_pcm_substream * , struct snd_soc_dai * );
- /*准备*/
- 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://lib.csdn.net/article/embeddeddevelopment/42002