动态 PCM¶
描述¶
动态 PCM 允许 ALSA PCM 设备在 PCM 流运行时将其 PCM 音频数字路由到各种数字端点。例如,PCM0 可以将数字音频路由到 I2S DAI0、I2S DAI1 或 PDM DAI2。这对于公开多个 ALSA PCM 并可以路由到多个 DAI 的片上系统 DSP 驱动程序非常有用。
DPCM 运行时路由由 ALSA 混音器设置确定,方式与 ASoC 编解码器驱动程序中模拟信号的路由方式相同。DPCM 使用表示 DSP 内部音频路径的 DAPM 图,并使用混音器设置来确定每个 ALSA PCM 使用的路径。
DPCM 重用所有现有的组件编解码器、平台和 DAI 驱动程序,无需任何修改。
基于片上系统的 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。
示例 - 将播放从 DAI0 切换到 DAI1 的 DPCM¶
正在耳机上播放音频。过了一会儿,用户取下耳机,音频继续在扬声器上播放。
在 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
*************
音频驱动程序按如下方式处理:-
机器驱动程序接收到插孔移除事件。
机器驱动程序或音频 HAL 禁用耳机路径。
DPCM 对耳机上的 DAI0 运行 PCM 触发(停止)、hw_free()、shutdown() 操作,因为该路径现已禁用。
机器驱动程序或音频 HAL 启用扬声器路径。
DPCM 对扬声器的 DAI1 运行 startup()、hw_params()、prepare() 和 trigger(start) 等 PCM 操作,因为该路径已启用。
在此示例中,机器驱动程序或用户空间音频 HAL 可以更改路由,然后 DPCM 将负责管理 DAI PCM 操作以启动或关闭链接。音频播放在此转换期间不会停止。
DPCM 机器驱动程序¶
启用 DPCM 的 ASoC 机器驱动程序与普通的机器驱动程序类似,只是我们还必须:-
定义 FE 和 BE DAI 链接。
定义任何 FE/BE PCM 操作。
定义小部件图连接。
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},
},
.....< other FE and BE DAI links here >
};
此 FE DAI 链接与常规 DAI 链接非常相似,只是我们还使用 dynamic = 1
将 DAI 链接设置为 DPCM FE。还可以选择指定每个 FE 的触发调用顺序。这允许 ASoC 核心在其他组件之前或之后触发 DSP(因为某些 DSP 对 DAI/DSP 的启动和停止序列的顺序有严格的要求)。
上面的 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,
},
.....< other BE DAI links here >
};
此 BE DAI 链接将 DAI0 连接到编解码器(在本例中为 RT5460 AIF1)。它设置 no_pcm
标志以将其标记为 BE。
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、调制解调器 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、16 位、立体声的固定速率运行 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 convert the FE rate to 48k, stereo */
rate->min = rate->max = 48000;
channels->min = channels->max = 2;
/* set DAI0 to 16 bit */
params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
return 0;
}
其他 PCM 操作与常规 DAI 链接相同。根据需要使用。
小部件图连接¶
BE DAI 链接通常由 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 平台驱动程序必须实现:-
前端 PCM DAI - 例如,struct snd_soc_dai_driver。
显示从 FE DAI 到 BE 的 DSP 音频路由的 DAPM 图。
DSP 图中的 DAPM 小部件。
用于增益、路由等的混音器。
DMA 配置。
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 仅用于控制,并且可以在流运行时休眠。
主机可以通过以下方式控制无主机链接:-
将链接配置为 CODEC <-> CODEC 样式链接。在这种情况下,链接由 DAPM 图的状态启用或禁用。这通常意味着有一个混音器控件可用于连接或断开两个 DAI 之间的路径。
无主机 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,
.c2c_params = &dai_params,
.num_c2c_params = 1,
}
< ... more DAI links here ... >
当 DAPM 检测到有效路径然后调用 PCM 操作来启动链接时,这些参数用于配置 DAI hw_params()。当路径不再有效时,DAPM 还会调用相应的 PCM 操作来禁用 DAI。
无主机 FE¶
DAI 链接由不读取或写入任何 PCM 数据的 FE 启用。这意味着创建一个新的 FE,该 FE 通过虚拟路径连接到两个 DAI 链接。当启动 FE PCM 时,将启动 DAI 链接,而当停止 FE PCM 时,将停止 DAI 链接。请注意,在此配置中,FE PCM 不能读取或写入数据。