我們知道,asoc框架里面主要包含machine codec platform 這三大部分:
machine:單板相關(guān)內(nèi)容,表明聲卡中所用的主芯片(Platform是指Soc)、編解碼芯片(codec)是哪一個。主芯片里的接口(DAI(全稱Digital Audio Interface)接口)接到哪里去。CPU DAI是哪一個,codec DAI是哪一個,DMA是哪個。
platform:用于實現(xiàn)平臺相關(guān)內(nèi)容,如IIS(DAI)(設(shè)置接口)和DMA(傳輸數(shù)據(jù))。
codec:用于實現(xiàn)平臺無關(guān)的功能,即編解碼芯片驅(qū)動, DAI和控制接口(控制音量)。
但是有些場合,我們是不需要一個“真實”的 codec 做處理的,例如藍(lán)牙通話,這時候只要一個虛擬聲卡即可。這里提供一個虛擬聲卡的驅(qū)動:
/*
?*?Driver?for?generic?Bluetooth?SCO?link
?*?Copyright?2011?Lars-Peter?Clausen?<lars@metafoo.de>
?*
?*??This?program?is?free?software;?you?can?redistribute??it?and/or?modify?it
?*??under??the?terms?of??the?GNU?General??Public?License?as?published?by?the
?*??Free?Software?Foundation;??either?version?2?of?the??License,?or?(at?your
?*??option)?any?later?version.
?*
?*/
#include?<linux/init.h>
#include?<linux/module.h>
#include?<linux/platform_device.h>
#include?<sound/soc.h>
static?const?struct?snd_soc_dapm_widget?bt_sco_widgets[]?=?{
?SND_SOC_DAPM_INPUT("RX"),
?SND_SOC_DAPM_OUTPUT("TX"),
};
static?const?struct?snd_soc_dapm_route?bt_sco_routes[]?=?{
?{?"Capture",?NULL,?"RX"?},
?{?"TX",?NULL,?"Playback"?},
};
static?int?btsco_set_sysclk(struct?snd_soc_dai?*dai,?int?clk_id,?unsigned?int?freq,?int?dir){
????pr_debug("%s:?clk_id=%d,?freq=%d.n",?__func__,?clk_id,?freq);
????return?0;
}
static?int?btsco_set_pll(struct?snd_soc_dai?*dai,?int?pll_id,?int?source,?unsigned?int?freq_in,?unsigned?int?freq_out){
????pr_debug("%s:?pll_id=%d,?freq_in=%d,?freq_out=%d.n",?__func__,?pll_id,?freq_in,?freq_out);
????return?0;
}
static?int?btsco_set_clkdiv(struct?snd_soc_dai?*dai,?int?div_id,?int?div){
????pr_debug("%s:?div_id=%d,?div=%d.n",?__func__,?div_id,?div);
????return?0;
}
static?int?btsco_hw_params(struct?snd_pcm_substream?*substream,?struct?snd_pcm_hw_params?*params,?struct?snd_soc_dai?*dai){
????pr_debug("%s?.n",?__func__);
????return?0;
}
static?int?btsco_hw_free(struct?snd_pcm_substream?*substream,?struct?snd_soc_dai?*dai){
????pr_debug("%s?.n",?__func__);
????return?0;
}
static?int?btsco_set_fmt(struct?snd_soc_dai?*dai,?unsigned?int?fmt){
????pr_debug("%s:?fmt=%d.n",?__func__,?fmt);
????return?0;
}
static?const?struct?snd_soc_dai_ops?btsco_dai_ops?=?{
????.set_sysclk?=?btsco_set_sysclk,
????.set_pll?=?btsco_set_pll,
????.set_clkdiv?=?btsco_set_clkdiv,
????.hw_params?=?btsco_hw_params,
????.hw_free?=?btsco_hw_free,
????.set_fmt?=?btsco_set_fmt,
};
static?struct?snd_soc_dai_driver?bt_sco_dai[]?=?{
?{
??.name?=?"bt-sco-pcm",
??.capture?=?{
????.stream_name?=?"Capture",
???.channels_min?=?1,
???.channels_max?=?2,
???.rates?=?SNDRV_PCM_RATE_8000_48000?|?SNDRV_PCM_RATE_KNOT,
???.formats?=?SNDRV_PCM_FMTBIT_S16_LE|SNDRV_PCM_FMTBIT_S20_3LE?|?SNDRV_PCM_FMTBIT_S24_LE?|?SNDRV_PCM_FMTBIT_S32_LE,
??},
????????.ops?=?&btsco_dai_ops,
?},
};
static?struct?snd_soc_codec_driver?soc_codec_dev_bt_sco?=?{
?.component_driver?=?{
??.dapm_widgets??=?bt_sco_widgets,
??.num_dapm_widgets?=?ARRAY_SIZE(bt_sco_widgets),
??.dapm_routes??=?bt_sco_routes,
??.num_dapm_routes?=?ARRAY_SIZE(bt_sco_routes),
?},
};
static?int?bt_sco_probe(struct?platform_device?*pdev)
{
????pr_debug("bt_sco_probe.n");
?return?snd_soc_register_codec(&pdev->dev,?&soc_codec_dev_bt_sco,
??????????&bt_sco_dai[0],?1);
}
static?int?bt_sco_remove(struct?platform_device?*pdev)
{
?snd_soc_unregister_codec(&pdev->dev);
?return?0;
}
static?const?struct?platform_device_id?bt_sco_driver_ids[]?=?{
?{
??.name??=?"dfbmcs320",
?},
?{
??.name??=?"bt-sco",
?},
?{},
};
MODULE_DEVICE_TABLE(platform,?bt_sco_driver_ids);
#if?defined(CONFIG_OF)
static?const?struct?of_device_id?bt_sco_codec_of_match[]?=?{
?{?.compatible?=?"delta,dfbmcs320",?},
?{?.compatible?=?"linux,bt-sco",?},
?{},
};
MODULE_DEVICE_TABLE(of,?bt_sco_codec_of_match);
#endif
static?struct?platform_driver?bt_sco_driver?=?{
?.driver?=?{
??.name?=?"bt-sco",
??.of_match_table?=?of_match_ptr(bt_sco_codec_of_match),
?},
?.probe?=?bt_sco_probe,
?.remove?=?bt_sco_remove,
?.id_table?=?bt_sco_driver_ids,
};
module_platform_driver(bt_sco_driver);
MODULE_DESCRIPTION("ASoC?generic?bluetooth?sco?link?driver");
MODULE_LICENSE("GPL");
這個虛擬到聲卡驅(qū)動是通用的,就一個虛擬codec,里面啥都沒做,就規(guī)定了一些參數(shù):最大兩通道,采樣率在8k~48k,支持16、20、24、32bit位寬。
既然codec是通用的,那么machine是否也有通用的例子的?
還真有?。?!就是simple card framework
Simple card顧名思義,簡單通用的machine driver,代碼實現(xiàn)路徑如下:sound/soc/generic/simple-card.c。需要在內(nèi)核開啟如下配置才能使用:
Device Drivers ?---> ?<> Sound card support ?---> ?<> ? Advanced Linux Sound Architecture ?---> <> ? ALSA for SoC audio support ?---> <> ? ASoC Simple sound card support
同時,我們還需要在設(shè)備樹中進(jìn)行相應(yīng)到配置:
sound?{
????????compatible?=?"simple-audio-card";
????????simple-audio-card,name?=?"Dummy-Audio-Card";
????????simple-audio-card,format?=?"dsp_a";
????????simple-audio-card,bitclock-master?=?<&dailink0_master>;
????????simple-audio-card,frame-master?=?<&dailink0_master>;
????????simple-audio-card,mclk-fs?=?<512>;?//codec做主,主控供給編解碼芯片用的時鐘?512FS
????????simple-audio-card,cpu?{
????????????sound-dai?=?<&i2s>;
????????};
????????dailink0_master:?simple-audio-card,codec?{
????????????sound-dai?=?<&bt_sco>;
????????};
????};?
&i2s?{
????#sound-dai-cells?=?<0>;
????status?=?"okay";
};
&bt_sco?{
????#sound-dai-cells?=?<0>;
?compatible?=?"linux,bt-sco";
????status?=?"okay";
};
這里設(shè)備數(shù)配置到時:選擇藍(lán)牙做主設(shè)備,pcm格式。
關(guān)于simple-card里的描述,差不多可以歸納為:
-
- simple-audio-card,name:用戶指定的音頻聲卡名稱。simple-audio-card,widgets:指定音頻編解碼器DAPM小部件(widgets),每個條目都是一對字符串:“template-wname”,“user-supplied-wname”?!?template-wname”是模板小部件名稱,可選為:“Microphone”, “Line”, “Headphone”, “Speaker”。“user-supplied-wname”是用戶指定的窗口小部件名稱。simple-audio-card,routing:音頻組件之間的連接列表。
- 每個條目都是一對字符串,第一個是目的(sink),第二個是源(source)。simple-audio-card,pin-switches:包含以下組件的窗口小部件名稱的字符串列表必須創(chuàng)建哪個引腳開關(guān)。simple-audio-card,dai-link:用于dai-link里指定CPU和CODEC子節(jié)點。simple-audio-card,format:CPU / CODEC通用音頻格式??蛇x格式為:“ i2s”,“ right_j”,“ left_j”,“ dsp_a”“ dsp_b”,“ ac97”,“ pdm”,“ msb”,“ lsb”。frame-master:指向CPU或者CODEC,表示dai-link fram 主機(jī)。bitclock-master:指向CPU或者CODEC,表示dai-link的位時鐘主控。bitclock-inversion:布爾屬性,如果添加則表明dai-link使用位時鐘反轉(zhuǎn)。frame-inversion:布爾屬性。 如果添加則表明dai-link使用幀時鐘反轉(zhuǎn)。
舉幾個具體到例子:
simple-audio-card,pin-switches?=?"Headphone","HpSpeaker","LINEOUT";
等價于驅(qū)動里
static?const?struct?snd_kcontrol_new?sunxi_card_controls[]?=?{
?SOC_DAPM_PIN_SWITCH("Headphone"),
?SOC_DAPM_PIN_SWITCH("HpSpeaker"),
?SOC_DAPM_PIN_SWITCH("LINEOUT"),
};
snd_soc_add_card_controls(card,?sunxi_card_controls,
????ARRAY_SIZE(sunxi_card_controls));
simple-audio-card,widgets?=?"Microphone",?"HeadphoneMic",
??????"Microphone",?"Main?Mic";
等價于驅(qū)動里
static?const?struct?snd_soc_dapm_widget?sunxi_card_dapm_widgets[]?=?{
?SND_SOC_DAPM_MIC("HeadphoneMic",?NULL),
?SND_SOC_DAPM_MIC("Main?Mic",?NULL),
};
snd_soc_dapm_new_controls(dapm,?sunxi_card_dapm_widgets,
????ARRAY_SIZE(sunxi_card_dapm_widgets));
simple-audio-card,routing?=?"MainMic?Bias",?"Main?Mic",
?????"MIC1",?"MainMic?Bias",
?????"MIC2",?"HeadphoneMic";
等價于驅(qū)動里
static?const?struct?snd_soc_dapm_route?sunxi_card_routes[]?=?{
?{"MainMic?Bias",?NULL,?"Main?Mic"},
?{"MIC1",?NULL,?"MainMic?Bias"},
?{"MIC2",?NULL,?"HeadphoneMic"},
};
snd_soc_dapm_add_routes(dapm,?sunxi_card_routes,
????ARRAY_SIZE(sunxi_card_routes));
最后,更多實現(xiàn)詳情可以參考內(nèi)核文檔:Documentation/devicetree/bindings/sound/simple-card.txt