1. 程式人生 > >asoc之動態PCM

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
                    *************

音訊驅動程式按如下方式處理: -

  1. 機器驅動程式接收Jack移除事件。
  2. 機器驅動程式或音訊HAL禁用耳機路徑。
  3. 由於路徑現已禁用,DPCM在DAI0上為耳機執行PCM觸發(停止),hw_free(),shutdown()操作。
  4. 機器驅動器或音訊HAL啟用揚聲器路徑。
  5. 由於路徑已啟用,DPCM為DAI1揚聲器的startup(),hw_params(),prepapre()和觸發(start)執行PCM操作。

在此示例中,機器驅動程式或使用者空間音訊HAL可以改變路由,然後DPCM將負責管理DAI PCM操作以使連結上行或下行。在此過渡期間,音訊播放不會停止。

DPCM機器驅動程式

啟用DPCM的ASoC機器驅動程式與普通機器驅動程式類似,但我們還必須:

  1. 定義FE和BE DAI連結。
  2. 定義任何FE / BE PCM操作。
  3. 定義小部件圖形連線。

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 = 1dpcm_playbackdpcm_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_playbackdpcm_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(&params->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平臺驅動程式必須實現: -

  1. 前端PCM DAI - 即struct snd_soc_dai_driver。
  2. DAPM圖顯示了從FE DAI到BE的DSP音訊路由。
  3. 來自DSP圖的DAPM小部件。
  4. 用於增益,路由等的混頻器
  5. DMA配置。
  6. 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僅用於控制,並且可以在流的執行時期間休眠。

主持人可以通過以下方式控制無主機連結: -

  1. 將連結配置為CODEC < - > CODEC樣式連結。在這種情況下,連結由DAPM圖的狀態啟用或禁用。這通常意味著有一個混音器控制元件可用於連線或斷開兩個DAI之間的路徑。
  2. 無主的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無法在此配置中讀取或寫入資料。