Chinaunix首页 | 论坛 | 博客
  • 博客访问: 163815
  • 博文数量: 36
  • 博客积分: 830
  • 博客等级: 准尉
  • 技术积分: 409
  • 用 户 组: 普通用户
  • 注册时间: 2012-08-10 16:23
文章分类
文章存档

2013年(1)

2012年(35)

分类: 嵌入式

2012-12-05 11:31:05

Mini2440开发板

 

Kernellinux 2.6.32.2

 

音频基于i2s总线接口(和l3总线接口对混音器进行设置)

 

Linux ASoC音频设备驱动

 

ASoC驱动的组成

 

ASoCALSA System on Chip)是ALSASoC方面的发展和演变,它在本质上仍然属于ALSA,但是在ALSA架构的基础上对CPU相关的代码和CODEC相关的代码进行了分离。其原因是,采用传统ALSA架构的情况下,同一型号的CODEC工作于不同的CPU时,需要不同的驱动,这不符合代码重用的要求。

 

ASoC主要由3部分组成。

 

1CODEC驱动。这一部分只关心CODEC本身,与CPU平台相关的特性不由此部分操作。

 

2)平台驱动。这一部分只关心CPU本身,不关心CODEC。它主要处理两个问题:DMA引擎和SoC集成的PCMI2SAC97数字接口的控制。

 

3)板驱动。也称为machine驱动,这一部分将平台驱动和CODEC驱动绑定在一起,描述了板一级的硬件特征。

 

在以上3部分中,12基本上都可以仍然是通用的驱动了,也就是说,CODEC驱动认为自己可以连接任意CPU,而CPUI2SPCM、或AC97接口对应的平台驱动则认为自己可以连接任意符合接口类型的CODEC,只有3是不通用的,由特性的电路板上具体的CPUCODEC确定,因此它很像一个插座,上面插上了CODEC和平台这两个插头。

 

在以上三部分之上的是ASoC核心层,由内核源代码中的sound/soc/soc-core.c实现,查看其源代码发现它完全是一个传统的ALSA驱动。因此,对于基于ASoC架构的声卡驱动而言,alsa-lia以及ALSA的一系列utility仍然是可用的,如amixeraplay均无需针对ASoC进行任何改动。而ASoC的用户编程方法也和ALSA完全一致。

 

内核源代码的Documentation/sound/slsa/soc目录包含了ASoC相关的文档。

 

1.ASoC CODEC驱动

 

ASoC架构下,CODEC驱动负责如下工作:

 

1CODEC DAIDigital Audio Interfaces)和配置PCM,由结构体snd_soc_dai来描述,形容playbackcapture的属性以及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;         /*循环、双向链表*/

 

};

2CODEC IO操作、动态音频电源管理以及时钟、PLL等控制

 

在(1)中的snd_soc_codec 结构体是对CODEC本身I/O控制以及动态音频电源管理(Dynamic Audio Power ManagementDAPM)的描述。它描述I2CSPIAC97如何读写CODEC寄存器并容纳DAPM链表,核心成员为read()write()hw_write()hw_read()dapm_widgetsdapm_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被开始、停止或暂停时被调用*/

};

 

3CODECmixer控制

 

ASoC中定义了一组宏来描述CODECmixer控制,这组宏可以方便地将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) /*参数xnamemixer的名字,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的,如xregxmaxxinvertxenumxshiftxtexts数据类型是unsigned long,不带x的为默认类型,见上面两个结构体。

 

4CODEC音频操作

 

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.hsnd_soc_dai_ops结构体来描述,见sound/soc/codecs/uda134x.c

 

 以上是对于ASoC CODEC驱动的工作进行描述和几个重要的数据结构。

 

 

 

2.ASoC平台驱动

 

首先,在ASoC平台驱动部分,同样存在着CODEC驱动中的snd_soc_daisnd_soc_dai_opssnd_soc_ops(这个结构体在linux2.6.32.2中已经交给snd_soc_dai_ops结构体来描述)这3个结构体的实例用于描述DAIDAI上操作,不过不同的是,在平台驱动中,它们只描述CPU相关的部分而不描述CODEC。除此之外,在ASoC平台驱动中,必须实现完整的DMA驱动,即传统ALSAsnd_pcm_ops结构体成员函数trigger()pointer()等。因此ASoC平台驱动通常由DAIDMA两部分组成:

 

snd_soc_daisnd_soc_dai_opssnd_soc_ops3个结构体和在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板驱动直接与板对应,对于一块确定的电路板,其SoCCODEC都是确定的,因此板驱动将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;

 

};

 

在板驱动的模块初始化函数中,会通过platform_device_add()注册一个名为“soc-audio”的platform设备,这是因为soc-core.c注册了一个名为“soc-audio”的platform驱动,因此,在板驱动中注册“soc-audio”设备会引起两者的匹配,从而引发一系列的初始化操作。尤其值得一提的是,“soc-audio”设备的私有数据需要为一个snd_soc_device的结构体实体。

 

总结:

 

snd_soc_device结构体是对ASoC设备的整体封装,包括了封装板用的snd_soc_machine(machine成员)[此结构体在linux 2.6.32.2中是snd_soc_card(card成员)]、封装ASoC CODEC设备用的snd_soc_codec_device(codec_devc成员)、封装ASoC平台设备用snd_soc_platform(platform成员)

 

CODEC驱动中提供给板驱动两个结构体:一个是关于codec DAI和对PCM配置的结构体snd_soc_dai另一个是封装ASoC CODEC设备用的snd_soc_codec_device结构体;

 

ASoC平台设备驱动中也提供两个:一个是关于CPU相关的DAI操作设置另一个是对ASoC平台设备的封装snd_soc_platform结构体;

 

在板驱动中,将CODEC驱动和平台驱动中的snd_soc_dai进行绑定,用snd_soc_dai_link结构体实现,同时把板的硬件的板流操作snd_soc_ops结构体的实现填充进去,

 

snd_soc_card结构体对板驱动的封装,把snd_soc_dai_linksnd_soc_platform填充进去,

 

snd_soc_device结构体对ASoC设备一个整体封装,把snd_soc_cardsnd_soc_codec_deviceuda134x_platform_data进行填充。

 

mini2440uda1341音频驱动中以ASoC为架构:

 

ASoC驱动有以下三部分组成:

 

1CODEC驱动:由内核源代码sound/soc/codecs/uda134x.c实现

 

2)平台驱动:由内核源代码sound/soc/s3c24xx/s3c24xx-i2s.c实现CPU端的DAI驱动,由sound/soc/s3c24xx/s3c24xx_pcm.c实现CPU端的DMA驱动

 

3)板驱动:由内核源代码sound/soc/s3c24xx/s3c24xx_uda134x.c实现,它将第一部分和第二部分进行绑定。

 

在以上三部分之上的是ASoC核心层,由内核源代码中的sound/soc/soc-core.c实现,查看其源代码发现它完全是一个传统的ALSA驱动。

 

本篇文章来源于 Linux公社网站(www.linuxidc.com)  原文链接:http://www.linuxidc.com/Linux/2012-10/73066p6.htm

阅读(3827) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~