SoundWire 中的音频流

音频流是在以下各项之间创建的逻辑或虚拟连接:

  1. 系统内存缓冲区和编解码器

  2. DSP 内存缓冲区和编解码器

  3. FIFO 和编解码器

  4. 编解码器和编解码器

通常由 DMA 通道通过数据链路驱动。音频流包含一个或多个数据通道。流中的所有通道必须具有相同的采样率和相同的采样大小。

假设使用 SoundWire 接口打开一个具有两个通道(左声道和右声道)的流。以下是一些在 SoundWire 中表示流的方式。

内存(系统内存、DSP 内存或 FIFO)中的流采样

-------------------------
| L | R | L | R | L | R |
-------------------------

示例 1:具有 L 和 R 通道的立体声流从主设备渲染到从设备。主设备和从设备都使用单个端口。

+---------------+                    Clock Signal  +---------------+
|    Master     +----------------------------------+     Slave     |
|   Interface   |                                  |   Interface   |
|               |                                  |       1       |
|               |                     Data Signal  |               |
|    L  +  R    +----------------------------------+    L  +  R    |
|     (Data)    |     Data Direction               |     (Data)    |
+---------------+  +----------------------->       +---------------+

示例 2:具有 L 和 R 通道的立体声流从从设备捕获到主设备。主设备和从设备都使用单个端口。

+---------------+                    Clock Signal  +---------------+
|    Master     +----------------------------------+     Slave     |
|   Interface   |                                  |   Interface   |
|               |                                  |       1       |
|               |                     Data Signal  |               |
|    L  +  R    +----------------------------------+    L  +  R    |
|     (Data)    |     Data Direction               |     (Data)    |
+---------------+  <-----------------------+       +---------------+

示例 3:具有 L 和 R 通道的立体声流由主设备渲染。每个 L 和 R 通道由两个不同的从设备接收。主设备和两个从设备都使用单个端口。

+---------------+                    Clock Signal  +---------------+
|    Master     +---------+------------------------+     Slave     |
|   Interface   |         |                        |   Interface   |
|               |         |                        |       1       |
|               |         |           Data Signal  |               |
|    L  +  R    +---+------------------------------+       L       |
|     (Data)    |   |     |    Data Direction      |     (Data)    |
+---------------+   |     |   +------------->      +---------------+
                    |     |
                    |     |
                    |     |                        +---------------+
                    |     +----------------------> |     Slave     |
                    |                              |   Interface   |
                    |                              |       2       |
                    |                              |               |
                    +----------------------------> |       R       |
                                                   |     (Data)    |
                                                   +---------------+

示例 4:具有 L 和 R 通道的立体声流由主设备渲染。两个 L 和 R 通道都由两个不同的从设备接收。主设备和两个从设备都使用处理 L+R 的单个端口。每个从设备在本地处理 L + R 数据,通常基于静态配置或动态方向,并且可以驱动一个或多个扬声器。

+---------------+                    Clock Signal  +---------------+
|    Master     +---------+------------------------+     Slave     |
|   Interface   |         |                        |   Interface   |
|               |         |                        |       1       |
|               |         |           Data Signal  |               |
|    L  +  R    +---+------------------------------+     L + R     |
|     (Data)    |   |     |    Data Direction      |     (Data)    |
+---------------+   |     |   +------------->      +---------------+
                    |     |
                    |     |
                    |     |                        +---------------+
                    |     +----------------------> |     Slave     |
                    |                              |   Interface   |
                    |                              |       2       |
                    |                              |               |
                    +----------------------------> |     L + R     |
                                                   |     (Data)    |
                                                   +---------------+

示例 5:具有 L 和 R 通道的立体声流由主设备的两个不同端口渲染,并且仅由从设备接口的单个端口接收。

+--------------------+
|                    |
|     +--------------+                             +----------------+
|     |             ||                             |                |
|     |  Data Port  ||  L Channel                  |                |
|     |      1      |------------+                 |                |
|     |  L Channel  ||           |                 +-----+----+     |
|     |   (Data)    ||           |   L + R Channel ||    Data |     |
| Master  +----------+           | +---+---------> ||    Port |     |
| Interface          |           |                 ||     1   |     |
|     +--------------+           |                 ||         |     |
|     |             ||           |                 +----------+     |
|     |  Data Port  |------------+                 |                |
|     |      2      ||  R Channel                  |     Slave      |
|     |  R Channel  ||                             |   Interface    |
|     |   (Data)    ||                             |       1        |
|     +--------------+         Clock Signal        |     L  +  R    |
|                    +---------------------------> |      (Data)    |
+--------------------+                             |                |
                                                   +----------------+

示例 6:具有 L 和 R 通道的立体声流由 2 个主设备渲染,每个主设备渲染一个通道,并由两个不同的从设备接收,每个从设备接收一个通道。两个主设备和两个从设备都使用单个端口。

+---------------+                    Clock Signal  +---------------+
|    Master     +----------------------------------+     Slave     |
|   Interface   |                                  |   Interface   |
|       1       |                                  |       1       |
|               |                     Data Signal  |               |
|       L       +----------------------------------+       L       |
|     (Data)    |     Data Direction               |     (Data)    |
+---------------+  +----------------------->       +---------------+

+---------------+                    Clock Signal  +---------------+
|    Master     +----------------------------------+     Slave     |
|   Interface   |                                  |   Interface   |
|       2       |                                  |       2       |
|               |                     Data Signal  |               |
|       R       +----------------------------------+       R       |
|     (Data)    |     Data Direction               |     (Data)    |
+---------------+  +----------------------->       +---------------+

示例 7:具有 L 和 R 通道的立体声流由 2 个主设备渲染,每个主设备都渲染两个通道。每个从设备接收 L + R。这与示例 4 的应用程序相同,但从设备放置在单独的链路上。

+---------------+                    Clock Signal  +---------------+
|    Master     +----------------------------------+     Slave     |
|   Interface   |                                  |   Interface   |
|       1       |                                  |       1       |
|               |                     Data Signal  |               |
|     L + R     +----------------------------------+     L + R     |
|     (Data)    |     Data Direction               |     (Data)    |
+---------------+  +----------------------->       +---------------+

+---------------+                    Clock Signal  +---------------+
|    Master     +----------------------------------+     Slave     |
|   Interface   |                                  |   Interface   |
|       2       |                                  |       2       |
|               |                     Data Signal  |               |
|     L + R     +----------------------------------+     L + R     |
|     (Data)    |     Data Direction               |     (Data)    |
+---------------+  +----------------------->       +---------------+

示例 8:4 通道流由 2 个主设备渲染,每个主设备渲染 2 个通道。每个从设备接收 2 个通道。

+---------------+                    Clock Signal  +---------------+
|    Master     +----------------------------------+     Slave     |
|   Interface   |                                  |   Interface   |
|       1       |                                  |       1       |
|               |                     Data Signal  |               |
|    L1 + R1    +----------------------------------+    L1 + R1    |
|     (Data)    |     Data Direction               |     (Data)    |
+---------------+  +----------------------->       +---------------+

+---------------+                    Clock Signal  +---------------+
|    Master     +----------------------------------+     Slave     |
|   Interface   |                                  |   Interface   |
|       2       |                                  |       2       |
|               |                     Data Signal  |               |
|     L2 + R2   +----------------------------------+    L2 + R2    |
|     (Data)    |     Data Direction               |     (Data)    |
+---------------+  +----------------------->       +---------------+

注 1:在上述多链路情况下,要锁定,需要获取全局锁,然后继续锁定总线实例。但是,在这种情况下,调用方框架 (ASoC DPCM) 保证卡上的流操作始终是串行化的。因此,不存在竞争条件,因此无需全局锁。

注 2:可以配置从设备以接收在给定流的链路上发送的所有通道(示例 4)或仅接收数据子集(示例 3)。从设备的配置不是由 SoundWire 子系统 API 处理的,而是由 snd_soc_dai_set_tdm_slot() API 处理的。平台或机器驱动程序通常会配置使用哪些槽。例如,对于示例 4,所有设备将使用相同的槽,而对于示例 3,从设备 1 将使用例如槽 0,从设备 2 将使用槽 1。

注 3:多个接收端口可以为 SoundWire 帧中的相同位槽提取相同的信息,但是多个发送端口应配置为不同的位槽配置。这与 I2S/PCM TDM 用法具有相同的限制。

SoundWire 流管理流程

流定义

  1. 当前流:这被分类为必须执行操作(如准备、启用、禁用、取消准备等)的流。

  2. 活动流:这被分类为总线上已激活的流,而不是当前流。总线上可以有多个活动流。

SoundWire 总线管理在 SoundWire 总线上渲染/捕获的每个流的流操作。本节介绍总线上为每个分配/释放的流完成的总线操作。以下是总线为每个音频流维护的流状态。

SoundWire 流状态

下面显示了 SoundWire 流状态和状态转换图。

+-----------+     +------------+     +----------+     +----------+
| ALLOCATED +---->| CONFIGURED +---->| PREPARED +---->| ENABLED  |
|   STATE   |     |    STATE   |     |  STATE   |     |  STATE   |
+-----------+     +------------+     +---+--+---+     +----+-----+
                                         ^  ^              ^
                                         |  |              |
                                       __|  |___________   |
                                      |                 |  |
                                      v                 |  v
         +----------+           +-----+------+        +-+--+-----+
         | RELEASED |<----------+ DEPREPARED |<-------+ DISABLED |
         |  STATE   |           |   STATE    |        |  STATE   |
         +----------+           +------------+        +----------+

注:仅当 ALSA/ASoC 级别支持 INFO_PAUSE 标志时,SDW_STREAM_ENABLEDSDW_STREAM_DISABLED 之间的状态转换才相关。同样,SDW_DISABLED_STATESDW_PREPARED_STATE 之间的转换取决于 INFO_RESUME 标志。

注 2:该框架实现了基本的状态转换检查,但例如,不检查从 DISABLED 到 ENABLED 的转换在特定平台上是否有效。此类测试需要在 ALSA/ASoC 级别添加。

流状态操作

以下部分说明了作为流状态转换的一部分,总线在主设备和从设备上完成的操作。

SDW_STREAM_ALLOCATED

流的分配状态。这是流的初始状态。进入此状态之前执行的操作

  1. 为流分配流运行时。此流运行时用作对流执行的所有操作的参考。

  2. 分配并初始化用于保存流运行时信息的资源。这保存所有与流相关的信息,例如流类型 (PCM/PDM) 和参数、与流关联的主设备和从设备接口、流状态等。

在所有上述操作成功后,流状态设置为 SDW_STREAM_ALLOCATED

总线实现以下 API 来分配流,每个流需要调用一次。从 ASoC DPCM 框架来看,此流状态可能与 .startup() 操作相关联。

int sdw_alloc_stream(char * stream_name);

SoundWire 核心提供 sdw_startup_stream() 辅助函数,该函数通常在 dailink .startup() 回调期间调用,该函数执行流分配并为连接到流的所有 DAI 设置流指针。

SDW_STREAM_CONFIGURED

流的配置状态。进入此状态之前执行的操作

  1. 此处更新在 SDW_STREAM_ALLOCATED 状态下为流信息分配的资源。这包括流参数、与当前流关联的主设备和从设备运行时信息。

  2. 与当前流关联的所有主设备和从设备都向总线提供端口信息,其中包括主设备和从设备为当前流分配的端口号及其通道掩码。

在所有上述操作成功后,流状态设置为 SDW_STREAM_CONFIGURED

总线实现以下 API 用于 CONFIG 状态,该状态需要由与流关联的各自的主设备和从设备调用。这些 API 只能由各自的主设备和从设备调用一次。从 ASoC DPCM 框架来看,此流状态与 .hw_params() 操作相关联。

int sdw_stream_add_master(struct sdw_bus * bus,
              struct sdw_stream_config * stream_config,
              const struct sdw_ports_config * ports_config,
              struct sdw_stream_runtime * stream);

int sdw_stream_add_slave(struct sdw_slave * slave,
              struct sdw_stream_config * stream_config,
              const struct sdw_ports_config * ports_config,
              struct sdw_stream_runtime * stream);

SDW_STREAM_PREPARED

流的准备状态。进入此状态之前执行的操作

  1. 在恢复操作的情况下,省略步骤 1 和 2,因为总线带宽是已知的。

  2. 基于当前流以及总线上已激活的流计算总线参数,例如带宽、帧形状、时钟频率。需要重新计算以适应总线上的当前流。

  3. 根据步骤 1 中计算的帧形状和时钟频率,计算所有主设备和从设备端口的传输和端口参数,以用于当前以及已激活的流。

  4. 将计算出的总线和传输参数编程到主设备和从设备的寄存器中。交替库(当前未使用的库)上完成库寄存器编程。为不在交替库上的已激活流启用端口。这样做是为了不中断已激活的流。

  5. 一旦所有值都已编程,总线就会启动切换到交替库,其中所有新编程的值都会生效。

  6. 通过编程 PrepareCtrl 寄存器来准备当前流的主设备和从设备的端口。

在上述所有操作成功完成后,流状态将设置为 SDW_STREAM_PREPARED

总线为 PREPARE 状态实现了以下 API,每个流需要调用一次。从 ASoC DPCM 框架来看,此流状态与 .prepare() 操作相关联。由于 .trigger() 操作可能不遵循 .prepare(),因此允许从 SDW_STREAM_PREPARED 直接转换为 SDW_STREAM_DEPREPARED

int sdw_prepare_stream(struct sdw_stream_runtime * stream);

SDW_STREAM_ENABLED

流的使能状态。进入此状态后,数据端口将被启用。进入此状态之前执行的操作:

  1. 在 SDW_STREAM_PREPARED 状态下计算的所有值都将被编程到备用存储区(当前未使用的存储区)。它包括对已激活的流进行编程。

  2. 通过编程 ChannelEn 寄存器,将当前流的所有主端口和从端口在备用存储区(当前未使用的存储区)上启用。

  3. 一旦所有值都被编程,总线就会启动切换到备用存储区,所有新编程的值都会生效,并且与当前流关联的端口将被启用。

在上述所有操作成功完成后,流状态将设置为 SDW_STREAM_ENABLED

总线为 ENABLE 状态实现了以下 API,每个流需要调用一次。从 ASoC DPCM 框架来看,此流状态与 .trigger() 启动操作相关联。

int sdw_enable_stream(struct sdw_stream_runtime * stream);

SDW_STREAM_DISABLED

流的禁用状态。退出此状态后,数据端口将被禁用。进入此状态之前执行的操作:

  1. 通过编程 ChannelEn 寄存器,将当前流的所有主端口和从端口在备用存储区(当前未使用的存储区)上禁用。

  2. 总线和活动流的当前所有配置都被编程到备用存储区(当前未使用的存储区)。

  3. 一旦所有值都被编程,总线就会启动切换到备用存储区,所有新编程的值都会生效,并且与当前流关联的端口将被禁用。

在上述所有操作成功完成后,流状态将设置为 SDW_STREAM_DISABLED

总线为 DISABLED 状态实现了以下 API,每个流需要调用一次。从 ASoC DPCM 框架来看,此流状态与 .trigger() 停止操作相关联。

当支持 INFO_PAUSE 标志时,允许直接转换为 SDW_STREAM_ENABLED

对于 ASoC 将使用 .prepare() 回调的恢复操作,流可以从 SDW_STREAM_DISABLED 转换为 SDW_STREAM_PREPARED,恢复所有必需的设置,但不更新带宽和位分配。

int sdw_disable_stream(struct sdw_stream_runtime * stream);

SDW_STREAM_DEPREPARED

流的取消准备状态。进入此状态之前执行的操作:

  1. 通过编程 PrepareCtrl 寄存器,取消准备当前流的所有主端口和从端口。

  2. 当前流的有效载荷带宽从总线总带宽需求中减少,并通过执行存储区切换等操作来计算和应用新参数。

在上述所有操作成功完成后,流状态将设置为 SDW_STREAM_DEPREPARED

总线为 DEPREPARED 状态实现了以下 API,每个流需要调用一次。ALSA/ASoC 没有“取消准备”的概念,并且此流状态到 ALSA/ASoC 操作的映射可能是特定于实现的。

当支持 INFO_PAUSE 标志时,流状态与 .hw_free() 操作相关联 - 流不会在 TRIGGER_STOP 时取消准备。

其他实现可能会在 TRIGGER_STOP 时转换为 SDW_STREAM_DEPREPARED 状态,如果它们需要通过 SDW_STREAM_PREPARED 状态转换。

int sdw_deprepare_stream(struct sdw_stream_runtime * stream);

SDW_STREAM_RELEASED

流的释放状态。进入此状态之前执行的操作:

  1. 释放与当前流关联的所有主端口和从端口的端口资源。

  2. 释放与当前流关联的主端口和从端口的运行时资源。

  3. 释放与当前流关联的流运行时资源。

在上述所有操作成功完成后,流状态将设置为 SDW_STREAM_RELEASED

总线为 RELEASE 状态实现了以下 API,所有与流关联的主端口和从端口都需要调用这些 API。从 ASoC DPCM 框架来看,此流状态与 .hw_free() 操作相关联。

int sdw_stream_remove_master(struct sdw_bus * bus,
              struct sdw_stream_runtime * stream);
int sdw_stream_remove_slave(struct sdw_slave * slave,
              struct sdw_stream_runtime * stream);

.shutdown() ASoC DPCM 操作调用以下总线 API,以释放分配为 ALLOCATED 状态的一部分的流。

在 .shutdown() 中,维护流状态的数据结构将被释放。

void sdw_release_stream(struct sdw_stream_runtime * stream);

SoundWire 核心提供了一个 sdw_shutdown_stream() 辅助函数,通常在 dailink .shutdown() 回调期间调用,它会清除连接到流的所有 DAIS 的流指针,并释放为该流分配的内存。

不支持

  1. 在两个流之间或跨流之间,不支持使用具有多个通道的单个端口。例如,即使理论上在 SoundWire 中是可能的,也不可以使用具有 4 个通道的端口来处理 2 个独立的立体声流。