動態 PCM¶
描述¶
動態 PCM 允許 ALSA PCM 裝置在 PCM 流執行時將其 PCM 音訊數字路由到各種數字端點。例如,PCM0 可以將數字音訊路由到 I2S DAI0、I2S DAI1 或 PDM DAI2。這對於公開多個 ALSA PCM 並能路由到多個 DAI 的片上系統 (SoC) DSP 驅動程式非常有用。
DPCM 執行時路由由 ALSA 混音器設定決定,其方式與 ASoC 編解碼器驅動程式中模擬訊號的路由方式相同。DPCM 使用表示 DSP 內部音訊路徑的 DAPM 圖,並使用混音器設定來確定每個 ALSA PCM 使用的路徑。
DPCM 無需任何修改即可重用所有現有元件的編解碼器、平臺和 DAI 驅動程式。
基於 SoC DSP 的電話音訊系統¶
考慮以下電話音訊子系統。本文件中的所有示例都將以此為例 :-
| Front End PCMs | SoC DSP | Back End DAIs | Audio devices |
*************
PCM0 <------------> * * <----DAI0-----> Codec Headset
* *
PCM1 <------------> * * <----DAI1-----> Codec Speakers
* DSP *
PCM2 <------------> * * <----DAI2-----> MODEM
* *
PCM3 <------------> * * <----DAI3-----> BT
* *
* * <----DAI4-----> DMIC
* *
* * <----DAI5-----> FM
*************
此圖顯示了一個簡單的智慧手機音訊子系統。它支援藍牙、FM 數字廣播、揚聲器、耳機插孔、數字麥克風和蜂窩調變解調器。此音效卡公開 4 個 DSP 前端 (FE) ALSA PCM 裝置並支援 6 個後端 (BE) DAI。每個 FE PCM 都可以將音訊資料數字路由到任何 BE DAI。FE PCM 裝置也可以將音訊路由到多個 BE DAI。
示例 - DPCM 將播放從 DAI0 切換到 DAI1¶
音訊正在耳機播放。過了一會兒,使用者取下耳機,音訊繼續在揚聲器上播放。
PCM0 到耳機的播放流程如下所示 :-
*************
PCM0 <============> * * <====DAI0=====> Codec Headset
* *
PCM1 <------------> * * <----DAI1-----> Codec Speakers
* DSP *
PCM2 <------------> * * <----DAI2-----> MODEM
* *
PCM3 <------------> * * <----DAI3-----> BT
* *
* * <----DAI4-----> DMIC
* *
* * <----DAI5-----> FM
*************
使用者從插孔中移除耳機,因此現在必須使用揚聲器 :-
*************
PCM0 <============> * * <----DAI0-----> Codec Headset
* *
PCM1 <------------> * * <====DAI1=====> Codec Speakers
* DSP *
PCM2 <------------> * * <----DAI2-----> MODEM
* *
PCM3 <------------> * * <----DAI3-----> BT
* *
* * <----DAI4-----> DMIC
* *
* * <----DAI5-----> FM
*************
音訊驅動程式處理過程如下 :-
機器驅動程式接收到插孔拔出事件。
機器驅動程式或音訊 HAL 停用耳機路徑。
由於耳機路徑已停用,DPCM 對 DAI0 執行 PCM trigger(stop)、hw_free()、shutdown() 操作。
機器驅動程式或音訊 HAL 啟用揚聲器路徑。
由於路徑已啟用,DPCM 對 DAI1 揚聲器執行 PCM ops 的 startup()、hw_params()、prepare() 和 trigger(start) 操作。
在此示例中,機器驅動程式或使用者空間音訊 HAL 可以更改路由,然後 DPCM 將負責管理 DAI PCM 操作,以啟用或停用鏈路。在此轉換過程中音訊播放不會停止。
DPCM 機器驅動程式¶
啟用 DPCM 的 ASoC 機器驅動程式與普通機器驅動程式類似,但我們還需要 :-
定義 FE 和 BE DAI 鏈路。
定義任何 FE/BE PCM 操作。
定義部件圖連線。
FE 和 BE DAI 鏈路¶
| Front End PCMs | SoC DSP | Back End DAIs | Audio devices |
*************
PCM0 <------------> * * <----DAI0-----> Codec Headset
* *
PCM1 <------------> * * <----DAI1-----> Codec Speakers
* DSP *
PCM2 <------------> * * <----DAI2-----> MODEM
* *
PCM3 <------------> * * <----DAI3-----> BT
* *
* * <----DAI4-----> DMIC
* *
* * <----DAI5-----> FM
*************
對於上面的示例,我們必須定義 4 個 FE DAI 鏈路和 6 個 BE DAI 鏈路。FE DAI 鏈路定義如下 :-
SND_SOC_DAILINK_DEFS(pcm0,
DAILINK_COMP_ARRAY(COMP_CPU("System Pin")),
DAILINK_COMP_ARRAY(COMP_DUMMY()),
DAILINK_COMP_ARRAY(COMP_PLATFORM("dsp-audio")));
static struct snd_soc_dai_link machine_dais[] = {
{
.name = "PCM0 System",
.stream_name = "System Playback",
SND_SOC_DAILINK_REG(pcm0),
.dynamic = 1,
.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
},
.....< other FE and BE DAI links here >
};
此 FE DAI 鏈路與常規 DAI 鏈路非常相似,不同之處在於我們還將 DAI 鏈路設定為 DPCM FE,並帶有 dynamic = 1。還有一個選項可以指定每個 FE 的觸發呼叫順序。這允許 ASoC 核心在其他元件之前或之後觸發 DSP(因為某些 DSP 對 DAI/DSP 啟動和停止序列的順序有嚴格要求)。
上面的 FE DAI 將編解碼器和程式碼 DAI 設定為虛擬裝置,因為 BE 是動態的,並且會根據執行時配置而改變。
BE DAI 配置如下 :-
SND_SOC_DAILINK_DEFS(headset,
DAILINK_COMP_ARRAY(COMP_CPU("ssp-dai.0")),
DAILINK_COMP_ARRAY(COMP_CODEC("rt5640.0-001c", "rt5640-aif1")));
static struct snd_soc_dai_link machine_dais[] = {
.....< FE DAI links here >
{
.name = "Codec Headset",
SND_SOC_DAILINK_REG(headset),
.no_pcm = 1,
.ignore_suspend = 1,
.ignore_pmdown_time = 1,
.be_hw_params_fixup = hswult_ssp0_fixup,
.ops = &haswell_ops,
},
.....< other BE DAI links here >
};
此 BE DAI 鏈路將 DAI0 連線到編解碼器(在本例中為 RT5460 AIF1)。它設定了 no_pcm 標誌以將其標記為 BE。
BE 還設定了忽略掛起和 PM 停機時間的標誌。這允許 BE 在無主機模式下工作,其中主機 CPU 不傳輸資料,例如藍牙電話呼叫 :-
*************
PCM0 <------------> * * <----DAI0-----> Codec Headset
* *
PCM1 <------------> * * <----DAI1-----> Codec Speakers
* DSP *
PCM2 <------------> * * <====DAI2=====> MODEM
* *
PCM3 <------------> * * <====DAI3=====> BT
* *
* * <----DAI4-----> DMIC
* *
* * <----DAI5-----> FM
*************
這允許主機 CPU 在 DSP、MODEM DAI 和 BT DAI 仍在執行時進入休眠狀態。
如果編解碼器是由外部管理的裝置,BE DAI 鏈路也可以將編解碼器設定為虛擬裝置。
同樣,如果 CPU DAI 由 DSP 韌體管理,BE DAI 也可以設定一個虛擬 CPU DAI。
FE/BE PCM 操作¶
上面的 BE 還公開了一些 PCM 操作和一個 fixup 回撥。fixup 回撥由機器驅動程式用於根據 FE hw 引數(重新)配置 DAI。即 DSP 可以對 FE 到 BE 進行 SRC 或 ASRC。
例如,DSP 將所有 FE hw 引數轉換為以 48k、16bit、立體聲的固定速率執行,用於 DAI0。這意味著 DAI0 的所有 FE hw_params 都必須在機器驅動程式中固定,以便 DAI 無論 FE 配置如何都能以所需配置執行。
static int dai0_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
struct snd_interval *rate = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_RATE);
struct snd_interval *channels = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_CHANNELS);
/* The DSP will convert the FE rate to 48k, stereo */
rate->min = rate->max = 48000;
channels->min = channels->max = 2;
/* set DAI0 to 16 bit */
params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
return 0;
}
其他 PCM 操作與常規 DAI 鏈路相同。根據需要使用。
部件圖連線¶
BE DAI 鏈路通常在初始化時由 ASoC DAPM 核心連線到圖中。但是,如果 BE 編解碼器或 BE DAI 是虛擬的,則必須在驅動程式中明確設定 :-
/* BE for codec Headset - DAI0 is dummy and managed by DSP FW */
{"DAI0 CODEC IN", NULL, "AIF1 Capture"},
{"AIF1 Playback", NULL, "DAI0 CODEC OUT"},
編寫 DPCM DSP 驅動程式¶
DPCM DSP 驅動程式看起來很像一個標準的平臺類 ASoC 驅動程式,並結合了編解碼器類驅動程式的元素。DSP 平臺驅動程式必須實現 :-
前端 PCM DAI - 即 struct snd_soc_dai_driver。
顯示從 FE DAI 到 BE 的 DSP 音訊路由的 DAPM 圖。
來自 DSP 圖的 DAPM 部件。
用於增益、路由等的混音器。
DMA 配置。
BE AIF 部件。
第 6 項對於將音訊路由到 DSP 外部很重要。需要為每個 BE 和每個流方向定義 AIF。例如,對於上面的 BE DAI0,我們將有 :-
SND_SOC_DAPM_AIF_IN("DAI0 RX", NULL, 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_OUT("DAI0 TX", NULL, 0, SND_SOC_NOPM, 0, 0),
BE AIF 用於將 DSP 圖連線到其他元件驅動程式(例如編解碼器圖)的圖。
無主機 PCM 流¶
無主機 PCM 流是指不透過主機 CPU 路由的流。一個例子是從聽筒到調變解調器的電話呼叫。
*************
PCM0 <------------> * * <----DAI0-----> Codec Headset
* *
PCM1 <------------> * * <====DAI1=====> Codec Speakers/Mic
* DSP *
PCM2 <------------> * * <====DAI2=====> MODEM
* *
PCM3 <------------> * * <----DAI3-----> BT
* *
* * <----DAI4-----> DMIC
* *
* * <----DAI5-----> FM
*************
在這種情況下,PCM 資料透過 DSP 路由。在此用例中,主機 CPU 僅用於控制,並且可以在流執行時進入休眠狀態。
主機可以透過以下方式控制無主機鏈路 :-
將鏈路配置為 CODEC <-> CODEC 樣式鏈路。在這種情況下,鏈路透過 DAPM 圖的狀態啟用或停用。這通常意味著有一個混音器控制,可用於連線或斷開兩個 DAI 之間的路徑。
無主機 FE。此 FE 與 DAPM 圖上的 BE DAI 鏈路有虛擬連線。然後由 FE 執行控制作為常規 PCM 操作。此方法對 DAI 鏈路提供更多控制,但需要更多的使用者空間程式碼來控制鏈路。除非您的硬體需要更精細的 PCM 操作序列,否則建議使用 CODEC<->CODEC。
CODEC <-> CODEC 鏈路¶
當 DAPM 在 DAPM 圖中檢測到有效路徑時,此 DAI 鏈路會啟用。機器驅動程式為 DAI 鏈路設定了一些附加引數,即
static const struct snd_soc_pcm_stream dai_params = {
.formats = SNDRV_PCM_FMTBIT_S32_LE,
.rate_min = 8000,
.rate_max = 8000,
.channels_min = 2,
.channels_max = 2,
};
static struct snd_soc_dai_link dais[] = {
< ... more DAI links above ... >
{
.name = "MODEM",
.stream_name = "MODEM",
.cpu_dai_name = "dai2",
.codec_dai_name = "modem-aif1",
.codec_name = "modem",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBP_CFP,
.c2c_params = &dai_params,
.num_c2c_params = 1,
}
< ... more DAI links here ... >
當 DAPM 檢測到有效路徑時,這些引數用於配置 DAI hw_params(),然後呼叫 PCM 操作來啟動鏈路。當路徑不再有效時,DAPM 還會呼叫相應的 PCM 操作來停用 DAI。
無主機 FE¶
DAI 鏈路由不讀取或寫入任何 PCM 資料的 FE 啟用。這意味著建立一個新的 FE,該 FE 透過虛擬路徑連線到兩個 DAI 鏈路。當 FE PCM 啟動時,DAI 鏈路將啟動;當 FE PCM 停止時,DAI 鏈路將停止。請注意,在此配置中,FE PCM 無法讀取或寫入資料。