ASoC USB 支援¶
概述¶
為了利用 ALSA 中現有的 USB 音訊裝置支援,引入了 ASoC USB API,允許子系統交換配置資訊。
一個潛在的用例是支援 USB 音訊解除安裝,這是一種允許音訊子系統中存在替代的、功耗最佳化的路徑來處理透過 USB 匯流排傳輸音訊資料的實現。 這將允許主處理器在更長的持續時間內保持較低的功耗模式。 以下是一個 ASoC 和 ALSA 元件如何連線在一起以實現此目的的設計示例
USB | ASoC
| _________________________
| | ASoC Platform card |
| |_________________________|
| | |
| ___V____ ____V____
| |ASoC BE | |ASoC FE |
| |DAI LNK | |DAI LNK |
| |________| |_________|
| ^ ^ ^
| | |________|
| ___V____ |
| |SoC-USB | |
________ ________ | | |
|USB SND |<--->|USBSND |<------------>|________| |
|(card.c)| |offld |<---------- |
|________| |________|___ | | |
^ ^ | | | ____________V_________
| | | | | |IPC |
__ V_______________V_____ | | | |______________________|
|USB SND (endpoint.c) | | | | ^
|_________________________| | | | |
^ | | | ___________V___________
| | | |->|audio DSP |
___________V_____________ | | |_______________________|
|XHCI HCD |<- |
|_________________________| |
SoC USB 驅動程式¶
結構體¶
struct snd_soc_usb
list: SND SoC 結構體列表的連結串列頭
component: 對 ASoC 元件的引用
connection_status_cb: 回撥函式,用於通知連線事件
update_offload_route_info: 回撥函式,用於獲取選定的 USB 音效卡/PCM 裝置
priv_data: 驅動程式資料
可以使用 ASoC 平臺卡裝置或 USB 裝置 (udev->dev) 引用 snd_soc_usb 結構。 這是由 ASoC BE DAI 連結建立的,USB 音訊實體將能夠使用此結構將資訊傳遞給 ASoC BE DAI 連結。
struct snd_soc_usb_device
card_idx: 與 USB 音訊裝置關聯的音效卡索引
chip_idx: USB 音訊晶片陣列索引
cpcm_idx: 與 USB 音訊裝置關聯的捕獲 pcm 裝置索引
ppcm_idx: 與 USB 音訊裝置關聯的回放 pcm 裝置索引
num_playback: 回放流的數量
num_capture: 捕獲流的數量
list: USB 音訊裝置列表的連結串列頭
struct snd_soc_usb_device 由 USB 音訊解除安裝驅動程式建立。 這將攜帶基本引數/限制,這些引數/限制將用於確定此 USB 音訊裝置可能的解除安裝路徑。
函式¶
int snd_soc_usb_find_supported_format(int card_idx,
struct snd_pcm_hw_params *params, int direction)
card_idx: USB 音訊晶片陣列的索引。
params: 來自 USB DPCM BE DAI 連結的請求的 PCM 引數
direction: 捕獲或回放
snd_soc_usb_find_supported_format() 確保外部 DSP 請求的音訊配置檔案受 USB 裝置支援。
成功返回 0,失敗返回 -EOPNOTSUPP。
int snd_soc_usb_connect(struct device *usbdev, struct snd_soc_usb_device *sdev)
usbdev: 發現的 usb 裝置
sdev: 裝置的功能
snd_soc_usb_connect() 通知 ASoC USB DCPM BE DAI 連結檢測到 USB 音訊裝置。 這可以在 BE DAI 驅動程式中用於跟蹤可用的 USB 音訊裝置。 這旨在由位於 USB SND 中的 USB 解除安裝驅動程式呼叫。
成功返回 0,失敗返回負錯誤程式碼。
int snd_soc_usb_disconnect(struct device *usbdev, struct snd_soc_usb_device *sdev)
usbdev: 已移除的 usb 裝置
sdev: 要釋放的功能
snd_soc_usb_disconnect() 通知 ASoC USB DCPM BE DAI 連結 USB 音訊裝置已移除。 這旨在由位於 USB SND 中的 USB 解除安裝驅動程式呼叫。
void *snd_soc_usb_find_priv_data(struct device *usbdev)
usbdev: 用於引用以查詢私有資料的 usb 裝置
snd_soc_usb_find_priv_data() 獲取儲存到 SoC USB 裝置的私有資料。
成功返回指向 priv_data 的指標,失敗返回 NULL。
int snd_soc_usb_setup_offload_jack(struct snd_soc_component *component,
struct snd_soc_jack *jack)
component: 要新增插孔的 ASoC 元件
jack: 要填充的插孔元件
snd_soc_usb_setup_offload_jack() 是一個幫助程式,用於將聲音插孔控制新增到平臺音效卡。 這將允許在支援 USB 音訊解除安裝的設計中使用一致的命名。 此外,這將使插孔能夠通知更改。
成功返回 0,否則返回負數。
int snd_soc_usb_update_offload_route(struct device *dev, int card, int pcm,
int direction, enum snd_soc_usb_kctl path,
long *route)
dev: 用於查詢解除安裝路徑對映的 USB 裝置
card: USB 音效卡索引
pcm: USB 音訊 PCM 裝置索引
direction: 用於獲取解除安裝路由資訊的方向
path: kcontrol 選擇器 - pcm 裝置或卡索引
route: 音訊解除安裝路徑的音效卡和 pcm 索引的對映。 這是一個包含兩個整數的陣列,將按特定順序攜帶音效卡和 pcm 裝置索引。 這可以用作 kcontrol 輸出的陣列。
snd_soc_usb_update_offload_route() 呼叫註冊的回撥函式到 USB BE DAI 連結,以獲取有關用於執行裝置的 USB 音訊解除安裝的對映 ASoC 裝置的資訊。 route 可能是指向 kcontrol 值輸出陣列的指標,該陣列在讀取 kcontrol 時攜帶值。
成功返回 0,否則返回負數。
struct snd_soc_usb *snd_soc_usb_allocate_port(struct snd_soc_component *component,
void *data);
component: DPCM BE DAI 連結元件
data: 私有資料
snd_soc_usb_allocate_port() 分配一個 SoC USB 裝置並填充用於進一步操作的標準引數。
成功返回指向 struct soc_usb 的指標,錯誤返回負數。
void snd_soc_usb_free_port(struct snd_soc_usb *usb);
usb: 要釋放的 SoC USB 裝置
snd_soc_usb_free_port() 釋放一個 SoC USB 裝置。
void snd_soc_usb_add_port(struct snd_soc_usb *usb);
usb: 要新增的 SoC USB 裝置
snd_soc_usb_add_port() 將分配的 SoC USB 裝置新增到 SOC USB 框架。 新增後,可以透過進一步的操作引用此裝置。
void snd_soc_usb_remove_port(struct snd_soc_usb *usb);
usb: 要移除的 SoC USB 裝置
snd_soc_usb_remove_port() 從 SoC USB 框架中移除一個 SoC USB 裝置。 移除裝置後,任何 SOC USB 操作都將無法引用已移除的裝置。
如何註冊到 SoC USB¶
ASoC DPCM USB BE DAI 連結是在元件繫結時負責分配和註冊 SoC USB 實體的實體。 同樣,它也將負責釋放分配的資源。 下面可以顯示一個示例
static int q6usb_component_probe(struct snd_soc_component *component)
{
...
data->usb = snd_soc_usb_allocate_port(component, 1, &data->priv);
if (!data->usb)
return -ENOMEM;
usb->connection_status_cb = q6usb_alsa_connection_cb;
ret = snd_soc_usb_add_port(usb);
if (ret < 0) {
dev_err(component->dev, "failed to add usb port\n");
goto free_usb;
}
...
}
static void q6usb_component_remove(struct snd_soc_component *component)
{
...
snd_soc_usb_remove_port(data->usb);
snd_soc_usb_free_port(data->usb);
}
static const struct snd_soc_component_driver q6usb_dai_component = {
.probe = q6usb_component_probe,
.remove = q6usb_component_remove,
.name = "q6usb-dai-component",
...
};
BE DAI 連結可以作為呼叫的一部分傳遞供應商特定資訊以分配 SoC USB 裝置。 這將允許位於 USB SND 中的 USB 解除安裝驅動程式訪問任何 BE DAI 連結引數或設定。
USB 音訊裝置連線流程¶
USB 裝置可以在任何時間點熱插拔到 USB 埠。 BE DAI 連結應瞭解物理 USB 埠的當前狀態,即,是否有任何連線了音訊介面的 USB 裝置。 connection_status_cb() 可用於通知 BE DAI 連結任何更改。
每當存在 USB SND 介面繫結或移除事件時,都會呼叫此函式,使用 snd_soc_usb_connect() 或 snd_soc_usb_disconnect()
static void qc_usb_audio_offload_probe(struct snd_usb_audio *chip)
{
...
snd_soc_usb_connect(usb_get_usb_backend(udev), sdev);
...
}
static void qc_usb_audio_offload_disconnect(struct snd_usb_audio *chip)
{
...
snd_soc_usb_disconnect(usb_get_usb_backend(chip->dev), dev->sdev);
...
}
為了考慮到驅動程式或裝置的存在不保證的情況,USB SND 公開了 snd_usb_rediscover_devices() 以重新發送任何已識別的 USB 音訊介面的連線事件。 考慮以下情況
- usb_audio_probe()
--> USB 音訊流被分配並儲存到 usb_chip[]--> 將連線事件傳播到 USB SND 中的 USB 解除安裝驅動程式--> snd_soc_usb_connect() 退出,因為 USB BE DAI 連結未準備好- BE DAI 連結元件探測
--> 探測 DAI 連結並分配 SoC USB 埠--> 錯過了 USB 音訊裝置連線事件
為了確保不會錯過連線事件,在註冊 SoC USB 裝置時執行 snd_usb_rediscover_devices()。 現在,當發生 BE DAI 連結元件探測時,以下突出顯示了該序列
- BE DAI 連結元件探測
--> 探測 DAI 連結並分配 SoC USB 埠--> 新增 SoC USB 裝置,並執行 snd_usb_rediscover_devices()- snd_usb_rediscover_devices()
--> 遍歷 usb_chip[],對於非 NULL 條目,發出connection_status_cb()
如果 USB 解除安裝驅動程式未繫結,而 USB SND 已準備好,則在模組初始化期間呼叫 snd_usb_rediscover_devices()。 這允許透過以下流程啟用解除安裝路徑
- usb_audio_probe()
--> USB 音訊流被分配並儲存到 usb_chip[]--> 將連線事件傳播到 USB SND 中的 USB 解除安裝驅動程式--> USB 解除安裝驅動程式 未 準備好!- BE DAI 連結元件探測
--> 探測 DAI 連結並分配 SoC USB 埠--> 由於缺少 USB 解除安裝驅動程式,因此沒有 USB 連線事件- USB 解除安裝驅動程式探測
--> qc_usb_audio_offload_init()--> 呼叫 snd_usb_rediscover_devices() 以通知裝置