内核资料:ALSA资料

内核资料好多好多,一个人摸索真的很难。
最近又卡住了,没办法向前进了。声卡,资料真的好多。没有一个系统的视频或者学习资料真的感觉无从下手。

只能用时间耗了,先堆一些枯燥的资料。等我哪天茅塞顿开了,再来写心得吧!

Master clock:每一个音频子系统都需要一个主时钟,通常被称作MCLK或者SYSCLK,主时钟可以来自外部晶振,锁相环或者CPU系统时钟.某些时钟源是
可以配置的,通常为了省电会降低系统的工作频率.

DAI(digital audio interface) Clocks:通常由BCLK驱动(bit clock),主要用来驱动CPU和codec之间的数据链路.
同时 DAI 在每个audio封包的开始也有一个帧时钟.这个时钟也常被成为 LRCLK(左右声道时钟).这个时钟的频率和采样率频率相同.

BCLK的产生:
BCLK = MCLK / x 或者 BCLK = LRC * x 或者 BCLK = LRC * Channels * Word Size

这个时钟和CPU与codec关系比较大.一般来说尽可能降低你的BCLK,为了省电。

如果IC支持:常常可以看到我们用codec来驱动 audio clock,因为它的采样率通常都会被CPU准很多

ASoC Platform Driver:
1.一个ASoC平台驱动可以分为 音频DMA 和DAI 的配置和控制.它只针对CPU进行实现,绝不包含任何单板相关内容.

a.Audio DMA
一般 可支持ALSA(advanced linux sound arch)操作:

/* 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);
}; 

主要数据结构:

struct snd_soc_platform_driver {
    char *name;

    int (*probe)(struct platform_device *pdev);
    int (*remove)(struct platform_device *pdev);
    int (*suspend)(struct platform_device *pdev, struct snd_soc_cpu_dai *cpu_dai);
    int (*resume)(struct platform_device *pdev, struct snd_soc_cpu_dai *cpu_dai);

    /* pcm creation and destruction */
    int (*pcm_new)(struct snd_card *, struct snd_soc_codec_dai *, struct snd_pcm *);
    void (*pcm_free)(struct snd_pcm *);

    /*
     * For platform caused delay reporting.
     * Optional.
     */
    snd_pcm_sframes_t (*delay)(struct snd_pcm_substream *,
        struct snd_soc_dai *);

    /* platform stream ops */
    struct snd_pcm_ops *pcm_ops;
};

参考:http://www.alsa-project.org/~tiwai/writing-an-alsa-driver/
b.SoC DAI Drivers
每个DAI 必须提供以下描述信息:
1) 数字音频接口的描述信息
2) 数字音频接口的配置信息
3) PCM的相关描述说明
4) SYSCLK 的配置
5) Suspend(挂起) and resume(恢复) (可选)

ASoC 的DAI目前支持三种接口:AC97,I2S,PCM
1.AC97(Audio Codec97)
在便携式设备的声卡上比较常见的一种五线式接口.
有一个复位线和分时多路复用的SDATA_OUT(playback)和SDATA_IN(capture)
BCLK通常由codec驱动(通常12.288MHZ).帧率(48KHZ)总是由控制器驱动
每个AC97帧长12us,被分为13个时间间隙

早期的ISA声卡由于集成度不高,声卡上散布了大量元器件,后来随着技术和工艺水平的发展,出现了单芯片的声卡,只用一块芯片就可以完成声卡所有的功能。
但是由于声卡的数字部分和模拟部分集成在一起,很难降低电磁干扰对模拟部分的影响,使得ISA声卡信噪比并不理想。
AC97标准则提出“双芯片”结构,即将声卡的数字与模拟两部分分开,每个部分单独使用一块芯片。AC97标准结合了数字处理和模拟处理两方面的优点,一方面减少
了由模拟线路转换至数字线路时可能会出现的噪声,营造出了更加纯净的音质;另一方面,将音效处理集成到芯片组后,可以进一步降低成本

1997年后,市场上出现的PCI声卡大多数已经开始符合AC97规范,把模拟部分的电路从声卡芯片中独立出来,成为一块称之为“Audio Codec”(多媒体数字信号编解码器)
的小型芯片,而声卡的主芯片即数字部分则成为一块称之为“Digital Control”(数字信号控制器)的大芯片。
由此可见,AC97并不是某种声卡的代称,而是一种标准。

2.I2S
比较常见用于HIFI,机顶盒,便携式设备上的一种四线DAI
I2S协议比较灵活,控制器或者CODEC均可驱动 BCLK和 LRCLK(和采样率相同)
BCLK通常根据采样率和MCLK的变化而变化.
很少有设备拥有独立的DAC和ADC LRCLK。这样可以允许捕获和播放时采用不同的频率.

I2S数据传输格式:
1.MSB传输,在传送完毕LRC校验码后第一个BCLK的下降沿便开始传数据
2.MSB传输,左对齐方式
3.MSB传输,右对齐方式

2.PCM

四线接口,和I2S类似.当tx rx传送/接受数据,BCLK和SYNC线用来同步
BCLK通常根据采样率的变化而变化
PCM还支持时分多路复用(TDM):多个设备共享总线

PCM操作模式:

1.MSB,在第一帧同步时钟后,BCLK的第一个下降沿开始传输数据
2.MSB,同步帧的上升沿传送

ASoC Design

Dynamic Audio Power Management (DAPM)

ASoC 基本上将嵌入式音频系统划为三个部分:
1.codec驱动:编解码器的驱动程序是独立于平台,并包含音频控制、音频接口功能,编解码DAPM定义和编解码器IO功能.
2.Platform driver: 平台驱动程序包含音频DMA引擎和音频接口驱动程序(如I2S,AC97,PCM)
3.Machine driver:控制任何与机器相关的特殊操作和音频事件(在播放开始前打开一个放大器)

power domains within DAPM
1. Codec domain - VREF, VMID (core codec and audio power)
2. Platform/Machine domain
3. Path domain
4. Stream domain

DAPM Widgets
o Mixer - Mixes several analog signals into a single analog signal.
o Mux - An analog switch that outputs only one of many inputs.
o PGA - A programmable gain amplifier or attenuation widget.
o ADC - Analog to Digital Converter
o DAC - Digital to Analog Converter
o Switch - An analog switch
o Input - A codec input pin
o Output - A codec output pin
o Headphone - Headphone (and optional Jack)
o Mic - Mic (and optional Jack)
o Line - Line Input/Output (and optional Jack)
o Speaker - Speaker
o Supply - Power or clock supply widget used by other widgets.
o Pre - Special PRE widget (exec before all others)
o Post - Special POST widget (exec after all others)

Stream Domain Widgets

Stream Widgets relate to the stream power domain and only consist of ADCs
(analog to digital converters) and DACs (digital to analog converters).
SND_SOC_DAPM_DAC(name, stream name, reg, shift, invert),

Path Domain Widgets

SND_SOC_DAPM_PGA(name, reg, shift, invert, controls, num_controls)

/* Output Mixer */
static const snd_kcontrol_new_t wm8731_output_mixer_controls[] = {
SOC_DAPM_SINGLE("Line Bypass Switch", WM8731_APANA, 3, 1, 0),
SOC_DAPM_SINGLE("Mic Sidetone Switch", WM8731_APANA, 5, 1, 0),
SOC_DAPM_SINGLE("HiFi Playback Switch", WM8731_APANA, 4, 1, 0),
};

SND_SOC_DAPM_MIXER("Output Mixer", WM8731_PWR, 4, 1, wm8731_output_mixer_controls,
    ARRAY_SIZE(wm8731_output_mixer_controls)),

Platform/Machine domain Widgets
o Speaker Amp
o Microphone Bias
o Jack connectors
A machine widget is assigned to each
machine audio component (non codec) that can be independently powered.

static int spitz_mic_bias(struct snd_soc_dapm_widget* w, int event)
{
    gpio_set_value(SPITZ_GPIO_MIC_BIAS, SND_SOC_DAPM_EVENT_ON(event));
    return 0;
}

SND_SOC_DAPM_MIC("Mic Jack", spitz_mic_bias),

Codec Domain
Virtual Widgets
SND_SOC_DAPM_MIXER(“AC97 Mixer”, SND_SOC_DAPM_NOPM, 0, 0, NULL, 0),

Sink, Path, Source
snd_soc_dapm_connect_input(codec, sink, path, source);
codec互联机制:
需要将输入+链路+输出配置好,打通一条audio通路,才能播放/录制声音

Machine Widget Interconnections

/* ext speaker connected to codec pins LOUT2, ROUT2  */
{"Ext Spk", NULL , "ROUT2"},
{"Ext Spk", NULL , "LOUT2"},

Endpoint Widgets:

An endpoint is a start or end point (widget) of an audio signal within the
machine and includes the codec. e.g.

o Headphone Jack
o Internal Speaker
o Internal Mic
o Mic Jack
o Codec Pins

snd_soc_dapm_set_endpoint(codec, “Widget Name”, 0);

DAPM Widget Events

/* turn speaker amplifier on/off depending on use */
static int corgi_amp_event(struct snd_soc_dapm_widget *w, int event)
{
    gpio_set_value(CORGI_GPIO_APM_ON, SND_SOC_DAPM_EVENT_ON(event));
    return 0;
}

/* corgi machine dapm widgets */
static const struct snd_soc_dapm_widget wm8731_dapm_widgets =
    SND_SOC_DAPM_SPK("Ext Spk", corgi_amp_event);

Event types

The following event types are supported by event widgets.

/* dapm event types */
#define SND_SOC_DAPM_PRE_PMU    0x1     /* before widget power up */
#define SND_SOC_DAPM_POST_PMU   0x2     /* after widget power up */
#define SND_SOC_DAPM_PRE_PMD    0x4     /* before widget power down */
#define SND_SOC_DAPM_POST_PMD   0x8     /* after widget power down */
#define SND_SOC_DAPM_PRE_REG    0x10    /* before audio path setup */
#define SND_SOC_DAPM_POST_REG   0x20    /* after audio path setup */

ASoC Codec Driver:
Each codec driver must provide the following features:-

1) Codec DAI and PCM configuration
2) Codec control IO - using I2C, 3 Wire(SPI) or both APIs
3) Mixers and audio controls
4) Codec audio operations

Optionally, codec drivers can also provide:-

5) DAPM description.
6) DAPM event handler.
7) DAC Digital mute control.

Each codec driver must have a struct snd_soc_dai_driver to define its DAI and
PCM capabilities and operations. This struct is exported so that it can be
registered with the core by your machine driver.
例子:

static struct snd_soc_dai_ops wm8731_dai_ops = {
    .prepare    = wm8731_pcm_prepare,
    .hw_params  = wm8731_hw_params,
    .shutdown   = wm8731_shutdown,
    .digital_mute   = wm8731_mute,
    .set_sysclk = wm8731_set_dai_sysclk,
    .set_fmt    = wm8731_set_dai_fmt,
};

struct snd_soc_dai_driver wm8731_dai = {
    .name = "wm8731-hifi",
    .playback = {
        .stream_name = "Playback",
        .channels_min = 1,
        .channels_max = 2,
        .rates = WM8731_RATES,
        .formats = WM8731_FORMATS,},
    .capture = {
        .stream_name = "Capture",
        .channels_min = 1,
        .channels_max = 2,
        .rates = WM8731_RATES,
        .formats = WM8731_FORMATS,},
    .ops = &wm8731_dai_ops,
    .symmetric_rates = 1,
};

Codec control IO:

The codec can usually be controlled via an I2C or SPI style interface
(AC97 combines control with data in the DAI). The codec drivers provide
functions to read and write the codec registers along with supplying a
register cache:-

/* IO control data and register cache */
    void *control_data; /* codec control (i2c/3wire) data */
    void *reg_cache;
    unsigned int (*read)(struct snd_soc_codec *, unsigned int);
    int (*write)(struct snd_soc_codec *, unsigned int, unsigned int);

Codec hardware IO functions - usually points to either the I2C, SPI or AC97
read/write:-

hw_write_t hw_write;
hw_read_t hw_read;

Mixers and audio controls:

All the codec mixers and audio controls can be defined using the convenience
macros defined in soc.h.

#define SOC_SINGLE(xname, reg, shift, mask, invert)

Defines a single control as follows:-

xname = Control name e.g. “Playback Volume”
reg = codec register
shift = control bit(s) offset in register
mask = control bit size(s) e.g. mask of 7 = 3 bits
invert = the control is inverted

Codec Audio Operations

The codec driver also supports the following ALSA operations:-

/* 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 *);
};

DAPM description.

The Dynamic Audio Power Management description describes the codec power
components and their relationships and registers to the ASoC core.
Please read dapm.txt for details of building the description.

DAPM event handler

This function is a callback that handles codec domain PM calls and system
domain PM calls (e.g. suspend and resume). It is used to put the codec
to sleep when not in use.

Power states:-

SNDRV_CTL_POWER_D0: /* full On */
/* vref/mid, clk and osc on, active */

SNDRV_CTL_POWER_D1: /* partial On */
SNDRV_CTL_POWER_D2: /* partial On */

SNDRV_CTL_POWER_D3hot: /* Off, with power */
/* everything off except vref/vmid, inactive */

SNDRV_CTL_POWER_D3cold: /* Everything Off, without power */

Codec DAC digital mute control

Most codecs have a digital mute before the DACs that can be used to
minimise any system noise. The mute stops any digital data from
entering the DAC.

A callback can be created that is called by the core for each codec DAI
when the mute is applied or freed.

i.e.

static int wm8974_mute(struct snd_soc_dai *dai, int mute)
{
    struct snd_soc_codec *codec = dai->codec;
    u16 mute_reg = snd_soc_read(codec, WM8974_DAC) & 0xffbf;

    if (mute)
        snd_soc_write(codec, WM8974_DAC, mute_reg | 0x40);
    else
        snd_soc_write(codec, WM8974_DAC, mute_reg);
    return 0;
}

ASoC Machine Driver

The ASoC machine (or board) driver is the code that glues together the platform
and codec drivers.

/* SoC machine */
struct snd_soc_card {
    char *name;

    ...

    int (*probe)(struct platform_device *pdev);
    int (*remove)(struct platform_device *pdev);

    /* the pre and post PM functions are used to do any PM work before and
     * after the codec and DAIs do any PM work. */
    int (*suspend_pre)(struct platform_device *pdev, pm_message_t state);
    int (*suspend_post)(struct platform_device *pdev, pm_message_t state);
    int (*resume_pre)(struct platform_device *pdev);
    int (*resume_post)(struct platform_device *pdev);

    ...

    /* CPU <--> Codec DAI links  */
    struct snd_soc_dai_link *dai_link;
    int num_links;

    ...
};

Machine DAI Configuration

The machine DAI configuration glues all the codec and CPU DAIs together. It can
also be used to set up the DAI system clock and for any machine related DAI
initialisation e.g. the machine audio map can be connected to the codec audio
map, unconnected codec pins can be set as such.

struct snd_soc_dai_link is used to set up each DAI in your machine. e.g.

/* corgi digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link corgi_dai = {
    .name = "WM8731",
    .stream_name = "WM8731",
    .cpu_dai_name = "pxa-is2-dai",
    .codec_dai_name = "wm8731-hifi",
    .platform_name = "pxa-pcm-audio",
    .codec_name = "wm8713-codec.0-001a",
    .init = corgi_wm8731_init,
    .ops = &corgi_ops,
};

struct snd_soc_card then sets up the machine with its DAIs. e.g.

/* corgi audio machine driver */
static struct snd_soc_card snd_soc_corgi = {
    .name = "Corgi",
    .dai_link = &corgi_dai,
    .num_links = 1,
};

你可能感兴趣的:(内核学习)