標籤:alsa util controls 驅動 gfs 技術分享 音頻 控制項 int
ALSA Asoc架構如:
Asoc分為machine,platform,codec三大模組。關於machine,platform,codec的介紹參考自這裡http://blog.csdn.net/droidphone/article/details/7165482
- Machine 是指某一款機器,可以是某款裝置,某款開發板,又或者是某款智能手機,由此可以看出Machine幾乎是不可重用的,每個Machine上的硬體實現可能都不一樣,CPU不一樣,Codec不一樣,音訊輸入、輸出裝置也不一樣,Machine為CPU、Codec、輸入輸出裝置提供了一個載體。
- Platform 一般是指某一個SoC平台,比如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對內部的寄存器進行控制。
對應的會有machine, platform,codec driver。
Machine driver負責處理機器特有的一些控制項和音頻事件(例如,當播放音頻時,需要先行開啟一個放大器);單獨的Platform和Codec驅動是不能工作的,它必須由Machine驅動把它們結合在一起才能完成整個裝置的音頻處理工作。
platform driver,它包含了該SoC平台的音頻DMA和音頻介面的配置和控制(I2S,PCM,AC97等等);它也不能包含任何與板子或機器相關的代碼。
Codec driver,ASoC中的一個重要設計原則就是要求Codec驅動是平台無關的,它包含了一些音訊控制項(Controls),音頻介面,DAMP(動態音頻電源管理)的定義和某些Codec IO功能。為了保證硬體無關性,任何特定於平台和機器的代碼都要移到Platform和Machine驅動中。所有的Codec驅動都要提供以下特性:
- Codec DAI 和 PCM的配置資訊;
- Codec的IO控制方式(I2C,SPI等);
- Mixer和其他的音頻控制項;
- Codec的ALSA音頻操作介面;
也可以提供以下功能:
- DAPM描述資訊;
- DAPM事件處理常式;
- DAC數字靜音控制
從資料結構看來,snd_soc_card代表著Machine驅動,snd_soc_platform則代表著Platform驅動,snd_soc_codec和soc_codec_device則代表了Codec驅動,而snd_soc_dai_link則負責串連Platform和Codec。
snd_soc_dai是snd_soc_platform和snd_soc_codec的數字音頻介面,snd_soc_codec的dai為codec_dai,snd_soc_platform的dai為cpu_dai,snd_pcm是snd_soc_card執行個體化後註冊的音效卡類型.
下面以smdk_wm8580為例,看看再Asoc架構下的ALSA driver的編寫。
static struct snd_soc_dai_link smdk_dai[] = {//snd_soc_dai_link中,指定了Platform、Codec、codec_dai、cpu_dai的名字
[PRI_PLAYBACK] = { /* Primary Playback i/f */
.name = "WM8580 PAIF RX",
.stream_name = "Playback",
.cpu_dai_name = "samsung-i2s.0",
.codec_dai_name = "wm8580-hifi-playback",
.platform_name = "samsung-i2s.0",
.codec_name = "wm8580.0-001b",
.dai_fmt = SMDK_DAI_FMT,
.ops = &smdk_ops,
},
[PRI_CAPTURE] = { /* Primary Capture i/f */
.name = "WM8580 PAIF TX",
.stream_name = "Capture",
.cpu_dai_name = "samsung-i2s.0",
.codec_dai_name = "wm8580-hifi-capture",
.platform_name = "samsung-i2s.0",
.codec_name = "wm8580.0-001b",
.dai_fmt = SMDK_DAI_FMT,
.init = smdk_wm8580_init_paiftx,
.ops = &smdk_ops,
},
[SEC_PLAYBACK] = { /* Sec_Fifo Playback i/f */
.name = "Sec_FIFO TX",
.stream_name = "Playback",
.cpu_dai_name = "samsung-i2s-sec",
.codec_dai_name = "wm8580-hifi-playback",
.platform_name = "samsung-i2s-sec",
.codec_name = "wm8580.0-001b",
.dai_fmt = SMDK_DAI_FMT,
.ops = &smdk_ops,
},
};
static struct snd_soc_card smdk = {
.name = "SMDK-I2S",
.owner = THIS_MODULE,
.dai_link = smdk_dai,
.num_links = 2,
.dapm_widgets = smdk_wm8580_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(smdk_wm8580_dapm_widgets),
.dapm_routes = smdk_wm8580_audio_map,
.num_dapm_routes = ARRAY_SIZE(smdk_wm8580_audio_map),
};
static struct platform_device *smdk_snd_device;
static int __init smdk_audio_init(void)
{
int ret;
char *str;
if (machine_is_smdkc100()
|| machine_is_smdkv210() || machine_is_smdkc110()) {
smdk.num_links = 3;
} else if (machine_is_smdk6410()) {
str = (char *)smdk_dai[PRI_PLAYBACK].cpu_dai_name;
str[strlen(str) - 1] = ‘2‘;
str = (char *)smdk_dai[PRI_CAPTURE].cpu_dai_name;
str[strlen(str) - 1] = ‘2‘;
}
smdk_snd_device = platform_device_alloc("soc-audio", -1);//建立platform device,device 名字為soc-audio
if (!smdk_snd_device)
return -ENOMEM;
platform_set_drvdata(smdk_snd_device, &smdk);//將platform devcie的drvdata設定成snd_soc_card類型的smdk
ret = platform_device_add(smdk_snd_device);//將platform device 加到platform bus中。
if (ret)
platform_device_put(smdk_snd_device);
return ret;
}
module_init(smdk_audio_init);
static void __exit smdk_audio_exit(void)
{
platform_device_unregister(smdk_snd_device);
}
module_exit(smdk_audio_exit);
上述代碼主要是在init時,建立一個名字為soc-audio 的platform device, 並將platform device的drvdata設定成snd_soc_card類型的執行個體,最後將platform device 註冊到platform bus中,註冊過程主要在platform_device_add中,在前面介紹platform device 註冊過程時,有講到註冊時,如果platform bus上有名字為soc-audio的driver,則調用driver的probe函數。
名字為soc-audio的driver定義在soc-core.c中,
static struct platform_driver soc_driver = {
.driver= {
.name= "soc-audio",//名字與上面的platform device相同。
.pm= &snd_soc_pm_ops,
},
.probe= soc_probe,
.remove= soc_remove,
};
static int __init snd_soc_init(void)
{
snd_soc_debugfs_init();
snd_soc_util_init();
return platform_driver_register(&soc_driver);//註冊platform driver:soc_driver.
}
module_init(snd_soc_init);
ALSA driver--Asoc