內核資料: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,
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章