Linux ALSA prief 结合N329&N970 platform

Linux ALSA prief

  • 1. ALSA(缩写) : Advanced Linux Sound Architecture

高级(先进)声音架构

Android 移动设备 TinyALSA 架构,ALSA的简化(切割)版

  • 2. OSS: Open Sound System

开放声音系统,有部分已经不适应音源系统的发展,不再开源;主要在linux2.4 使用。

  • ALSA是一个完全开放源代码的音频驱动程序集。除了像OSS那样提供了一组内核驱动程序模块之外,ALSA还专门为简化应用程序的编写提供了相应的函数库,与OSS提供的基于ioctl的原始编程接口相比,ALSA函数库使用起来要更加方便一些。利用该函数库,开发人员可以方便快捷的开发出自己的应用程序,细节则留给函数库内部处理。当然 ALSA也提供了类似于OSS的系统接口,不过ALSA的开发者建议应用程序开发者使用音频函数库而不是驱动程序的API。
  • http://alsa-project.org
  • Alsa-lib alsa-utils alsa-tools etc

 

  • 设备文件/dev/snd
  • crw-rw—T+ 1 root audio 116, 6 8月 8 09:39 controlC0
  • crw-rw—T+ 1 root audio 116, 2 8月 8 09:39 midiC0D0
  • crw-rw—T+ 1 root audio 116, 5 8月 8 09:40 pcmC0D0c
  • crw-rw—T+ 1 root audio 116, 4 8月 8 09:40 mixerC0D0
  • crw-rw—T+ 1 root audio 116, 3 8月 8 09:39 pcmC0D0p
  • crw-rw—T+ 1 root audio 116, 1 8月 8 09:39 seq
  • crw-rw—T+ 1 root audio 116, 33 8月 8 09:39 timer
  • ALSA 音频体系为应用层提供了七种API函数接口:
  • 1.设备信息接口 /proc/asound
  • 2.设备控制接口       /dev/snd/controlCx
  • 3. 混音器设备接口 /dev/snd/mixerCxDx
  • 4. PCM 设备接口     /dev/snd/pcmCxDx
  • 5. 原始MIDI设备接口 /dev/snd/midiCxDx
  • 6. 音序器(或声音合成synthesizer)设备接口 /dev/snd/seq
  • 7. 定时器接口 /dev/snd/timer

 

  • 根据声卡的实际能力,驱动实际上可以挂上更多种类的设备,在include/sound/core.h中,定义了以下设备类型:
  • #define SNDRV_DEV_TOPLEVEL     ((__force snd_device_type_t) 0)
  • #define SNDRV_DEV_CONTROL ((__force snd_device_type_t) 1)
  • #define SNDRV_DEV_LOWLEVEL_PRE ((__force snd_device_type_t) 2)
  • #define SNDRV_DEV_LOWLEVEL_NORMAL ((__force snd_device_type_t) 0x1000)
  • #define SNDRV_DEV_PCM ((__force snd_device_type_t) 0x1001)
  • #define SNDRV_DEV_RAWMIDI       ((__force snd_device_type_t) 0x1002)
  • #define SNDRV_DEV_TIMER         ((__force snd_device_type_t) 0x1003)
  • #define SNDRV_DEV_SEQUENCER     ((__force snd_device_type_t) 0x1004)
  • #define SNDRV_DEV_HWDEP         ((__force snd_device_type_t) 0x1005)
  • #define SNDRV_DEV_INFO         ((__force snd_device_type_t) 0x1006)
  • #define SNDRV_DEV_BUS           ((__force snd_device_type_t) 0x1007)
  • #define SNDRV_DEV_CODEC         ((__force snd_device_type_t) 0x1008)
  • #define SNDRV_DEV_JACK         ((__force snd_device_type_t) 0x1009)
  • #define SNDRV_DEV_COMPRESS     ((__force snd_device_type_t) 0x100A)
  • #define SNDRV_DEV_LOWLEVEL     ((__force snd_device_type_t) 0x2000)
  • 通常,我们更关心的是pcm和control设备

移动设备(嵌入式)中ALSA(ASoc)

  • ASoc —-> ALSA system on chip
  • 是建立在标准ALSA驱动层上,为了更好地支持嵌入式处理器和移动设备中的音频Codec的一套软件体系;
  • ASoC不能单独存在,他只是建立在标准ALSA驱动上的一个它必须和标准的ALSA驱动框架相结合才能工作.
  • 硬件架构:嵌入式设备的音频系统可以被划分为板载硬件(Machine)、Soc(Platform)、Codec三大部分,如下图:
  • Machine 是指机器,可以是某款设备,某款开发板,或者是某款智能手机等,为设备提供了一个载体.
  • Platform 一般是指某一个SoC平台,比如nucxxx,pxaxxx,s3cxxxx,omapxxx等,与音频相关的通常包含该SoC中的时钟、DMA、I2S、PCM等,只要指定了SoC,那么我们可以认为它会有一个对应的Platform,它只与SoC相关,与Machine无关,这样我们就可以把Platform抽象出来,使得同一款SoC不用做任何的改动,就可以用在不同的Machine中.实际上,把Platform认为是某个SoC更好理解.
  • Codec 就是编解码器,Codec里面包含了I2S接口、D/A、A/D、Mixer、PA(功放),通常包含多种输入(Mic、Line-in、I2S、PCM)和多个输出(耳机、喇叭、听筒,Line-out),Codec和Platform一样,是可重用的部件,同一个Codec可以被不同的Machine使用.嵌入式Codec通常通过I2C/SPI对内部的寄存器进行控制.
  • 软件架构:
  • 在软件层面,ASoC也把嵌入式设备的音频系统同样分为3大部分,Machine,Platform和Codec.
  • Codec驱动 ASoC中的一个重要设计原则就是要求Codec驱动是平台无关的,它包含了一些音频的控件(Controls),音频接口,DAMP(动态音频电源管理)的定义和某些Codec IO功能.为了保证硬件无关性,任何特定于平台和机器的代码都要移到Platform和Machine驱动中.所有的Codec驱动都要提供以下特性:
  • Codec DAI 和 PCM的配置信息;
  • Codec的IO控制方式(I2C,SPI等);
  • Mixer和其他的音频控件;
  • Codec的ALSA音频操作接口
  • 必要时,也可以提供以下功能:
  • DAPM描述信息;
  • DAPM事件处理程序;
  • DAC数字静音控制
  • Platform驱动 它包含了该SoC平台的音频DMA和音频接口的配置和控制(I2S,PCM,AC97等);它也不能包含任何与板子或机器相关的代码.
  • Machine驱动 Machine驱动负责处理机器特有的一些控件和音频事件(例如,当播放音频时,需要先行打开一个放大器);单独的Platform和Codec驱动是不能工作的,它必须由Machine驱动把它们结合在一起才能完成整个设备的音频处理工作.
  • 数据结构:/include/sound/soc.h
  • struct device_node;
  • struct snd_jack;
  • struct snd_soc_card;
  • struct snd_soc_pcm_stream;
  • struct snd_soc_ops;
  • struct snd_soc_pcm_runtime;
  • struct snd_soc_dai;
  • struct snd_soc_dai_driver;
  • struct snd_soc_platform;
  • struct snd_soc_dai_link;
  • struct snd_soc_device
  • struct snd_soc_platform_driver;
  • struct snd_soc_codec;
  • struct snd_soc_codec_driver;
  • struct snd_soc_component;
  • struct snd_soc_component_driver;
  • struct soc_enum;
  • struct snd_soc_jack;
  • struct snd_soc_jack_zone;
  • struct snd_soc_jack_pin;
  • struct snd_soc_cache_ops;

Asoc driver flow

  • 1. 创建声卡: snd_card_create() function:

–注册Snd_soc_register_card()—->实例化snd_soc_instantiate_card()—->创建snd_card_create();

  • 2.创建PCM设备
  • 3. 创建Control设备
  • 4. machine driver
  • 5. codec driver
  • 6. platform driver
  • Linux 2.6.35.4:
  • static struct snd_soc_ops w55fa92_audio_ops = {
  • .hw_params = w55fa92_audio_hw_params,
  • };
  • static struct snd_soc_dai_link w55fa92evb_i2s_dai = {
  • .name           = “IIS”,
  • .stream_name = “IIS HiFi”,
  • .cpu_dai = &w55fa92_i2s_dai,
  • .codec_dai = &nau8822_dai,
  • .ops           = &w55fa92_audio_ops,
  • };
  • static struct snd_soc_card w55fa92evb_audio_machine = {
  • .name           = “W55FA92_IIS”,
  • .dai_link = &w55fa92evb_i2s_dai,
  • .num_links = 1,
  • .platform       = &w55fa92_soc_platform,
  • };
  • static struct snd_soc_device w55fa92_i2s_devdata = {
  • .card           = &w55fa92evb_audio_machine,
  • .codec_dev = &soc_codec_dev_nau8822,
  • };

 

  • Linux 3.10.x :
  • static struct snd_soc_ops nuc970_audio_ops = {
  • .hw_params = nuc970_audio_hw_params,
  • };
  • static struct snd_soc_dai_link nuc970evb_i2s_dai = {
  • .name           = “IIS”,
  • .stream_name = “IIS HiFi”,
  • .cpu_dai_name = “nuc970-audio-i2s”,
  • .codec_dai_name = “nau8822-hifi”,
  • .codec_name = “nau8822.0-001a”,

.platform_name = “nuc970-audio-pcm.0”,

.ops           = &nuc970_audio_ops,

  • };
  • static struct snd_soc_card nuc970evb_audio_machine = {
  • .name           = “nuc970_IIS”,
  • .owner         = THIS_MODULE,
  • .dai_link = &nuc970evb_i2s_dai,
  • .num_links = 1,
  • };
  • static struct platform_driver nuc970_audio_driver = {
  • .driver         = {
  • .name   = “nuc970-audio”,
  • .owner = THIS_MODULE,
  • },
  • .probe         = nuc970_audio_probe,
  • .remove         = nuc970_audio_remove,
  • };
  • static int nuc970_audio_probe(struct platform_device *pdev)
  • {
  • struct snd_soc_card *card = &nuc970evb_audio_machine;
  • int ret;
  • card->dev = &pdev->dev;
  • ret = snd_soc_register_card(card);   … …
  • }

 

Create PCM

  • PCM是什? PCM是Pulse code modulation的缩写,中文译名是脉冲编码调制
  • 音频驱动的两大核心任务就是:
  • playback    如何把用户空间的应用程序发过来的PCM数据,转化为人耳可以辨别的模拟音频
  • capture     把mic拾取到得模拟信号,经过采样、量化,转换为PCM信号送回给用户空间的应用程序
  • 在嵌入式系统中,大多数情况下是一个声卡,一个pcm实例,pcm下面有一个playback和capture stream,playback和capture下面各自有一个substream

Create Control

  • control的名字需要遵循一些标准,通常可以分成3部分来定义control的名字:源–方向–功能.
  • 源  可以理解为该control的输入端,alsa已经预定义了一些常用的源,例如:Master,PCM,CD,Line等等.
  • 方向  代表该control的数据流向,例如:Playback,Capture,Bypass,Bypass Capture等等,也可以不定义方向,这时表示该Control是双向的(playback和capture).
  • 功能  根据control的功能,可以是以下字符串:Switch,Volume,Route等等
  • struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new * kcontrolnew, void * private_data);
  • int snd_ctl_add(struct snd_card * card, struct snd_kcontrol * kcontrol);
  • 用以下最简单的方式创建control:
  • err = snd_ctl_add(card, snd_ctl_new1(&my_control, chip));
  • if (err < 0)
  • return err;
  • Control设备和PCM设备一样,都属于声卡下的逻辑设备
  • control设备则在snd_card_create()内被创建,snd_card_create()通过调用snd_ctl_create()函数创建control设备节点.所以我们无需显式地创建control设备,只要建立声卡,control设备被自动地创建.
  • control设备的名字遵循一定的规则:controlCxx,这里的xx代表声卡的编号.我们也可以通过代码正是这一点,下面的是snd_ctl_dev_register()函数的代码
  • /*
  • * registration of the control device
  • */
  • static int snd_ctl_dev_register(struct snd_device *device)
  • {
  • struct snd_card *card = device->device_data;
  • int err, cardnum;
  • char name[16];
  • if (snd_BUG_ON(!card))
  • return -ENXIO;
  • cardnum = card->number;
  • if (snd_BUG_ON(cardnum < 0 || cardnum >= SNDRV_CARDS))
  • return -ENXIO;
  • sprintf(name, “controlC%i”, cardnum);
  • if ((err = snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,
  • &snd_ctl_f_ops, card, name)) < 0)
  • return err;
  • return 0;
  • }

Android/Linux音频配置文件 asound.conf   alsa.conf   asoundrc

  • asound.conf文件作用:主要用来做Android/Linux音频的路由控制,不同路由可以设置多个寄存器的不同配置组合,形成不同的音频通道,从而当路由改变时,底层codec通道随之改变.
  • 应用层直接改变音频codec的寄存器,具体而言:linux驱动中创建snd_kcontrol_new控制结构,并关联到codec寄存器,asound.conf文件通过控制结构名字,就能关联到寄存器,从而改变寄存器的值。
  • asound.conf文件通过改变配置,从而改变路由,通过路由改变codec的寄存器,底层codec通道随之改变,形成不同的音频通道。