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() 以通知设备