asoc之動態PCM
動態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 *************
音訊驅動程式按如下方式處理: -
- 機器驅動程式接收Jack移除事件。
- 機器驅動程式或音訊HAL禁用耳機路徑。
- 由於路徑現已禁用,DPCM在DAI0上為耳機執行PCM觸發(停止),hw_free(),shutdown()操作。
- 機器驅動器或音訊HAL啟用揚聲器路徑。
- 由於路徑已啟用,DPCM為DAI1揚聲器的startup(),hw_params(),prepapre()和觸發(start)執行PCM操作。
在此示例中,機器驅動程式或使用者空間音訊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連結定義如下: -
static struct snd_soc_dai_link machine_dais[] = { { .name = "PCM0 System", .stream_name = "System Playback", .cpu_dai_name = "System Pin", .platform_name = "dsp-audio", .codec_name = "snd-soc-dummy", .codec_dai_name = "snd-soc-dummy-dai", .dynamic = 1, .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, .dpcm_playback = 1, }, .....< other FE and BE DAI links here > };
這個FE DAI連結非常類似於常規DAI連結,除了我們還將DAI連結設定為DPCM FE 。還應使用和 標誌設定支援的FE流方向。還可以選擇為每個FE指定觸發器呼叫的順序。這允許ASoC核心在其他元件之前或之後觸發DSP(因為某些DSP對訂購DAI / DSP啟動和停止序列有強烈要求)。dynamic = 1
dpcm_playback
dpcm_capture
上面的FE DAI將編解碼器和程式碼DAI設定為虛擬裝置,因為BE是動態的,並且將根據執行時配置而改變。
BE DAI配置如下: -
static struct snd_soc_dai_link machine_dais[] = { .....< FE DAI links here > { .name = "Codec Headset", .cpu_dai_name = "ssp-dai.0", .platform_name = "snd-soc-dummy", .no_pcm = 1, .codec_name = "rt5640.0-001c", .codec_dai_name = "rt5640-aif1", .ignore_suspend = 1, .ignore_pmdown_time = 1, .be_hw_params_fixup = hswult_ssp0_fixup, .ops = &haswell_ops, .dpcm_playback = 1, .dpcm_capture = 1, }, .....< other BE DAI links here > };
此BE DAI連結將DAI0連線到編解碼器(在本例中為RT5460 AIF1)。它設定no_pcm
標誌以標記它具有BE並使用dpcm_playback
及dpcm_capture
以上設定支援的流方向的標誌。
BE還設定了用於忽略暫停和PM停機時間的標誌。這允許BE在無主機模式下工作,其中主機CPU不像BT電話那樣傳輸資料: -
************* 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 covert the FE rate to 48k, stereo */ rate->min = rate->max = 48000; channels->min = channels->max = 2; /* set DAI0 to 16 bit */ snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - SNDRV_PCM_HW_PARAM_FIRST_MASK], SNDRV_PCM_FORMAT_S16_LE); return 0; }
其他PCM操作與常規DAI連結相同。必要時使用。
小部件圖連線
BEIO鏈路通常在初始化時由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。
- DAPM圖顯示了從FE DAI到BE的DSP音訊路由。
- 來自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連結,但需要更多使用者空間程式碼來控制連結。建議使用CODEC < - > CODEC,除非您的硬體需要更精細的PCM操作順序排序。
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_CBM_CFM, .params = &dai_params, } < ... more DAI links here ... >
這些引數用於在DAPM檢測到有效路徑時配置DAI hw_params(),然後呼叫PCM操作以啟動連結。當路徑不再有效時,DAPM還將呼叫適當的PCM操作來禁用DAI。
無主的FE
DAI連結由不讀取或寫入任何PCM資料的FE啟用。這意味著建立一個與兩個DAI連結的虛擬路徑相連的新FE。當FE PCM啟動時,DAI連結將啟動,當FE PCM停止時,將啟動DAI連結。請注意,FE PCM無法在此配置中讀取或寫入資料。