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