DSP

asoc之动态PCM

2019-07-13 21:08发布

动态PCM

描述

动态PCM允许ALSA PCM设备在PCM流运行期间以数字方式将其PCM音频路由到各种数字端点。例如,PCM0可以将数字音频路由到I2S DAI0,I2S DAI1或PDM DAI2。这对于暴露多个ALSA PCM并可以路由到多个DAI的SoC DSP驱动程序非常有用。 DPCM运行时路由由ALSA混频器设置决定,其方式与在ASoC编解码器驱动程序中路由模拟信号的方式相同。DPCM使用表示DSP内部音频路径的DAPM图表,并使用混音器设置来确定每个ALSA PCM使用的补丁。 DPCM无需任何修改即可重复使用所有现有的组件编解码器,平台和DAI驱动程序。

基于SoC的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。

示例 - DPCM从DAI0切换回放到DAI1

音频正在播放到耳机。一段时间后,用户移除耳机,音频继续在扬声器上播放。 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 ************* 音频驱动程序按如下方式处理: -
  1. 机器驱动程序接收Jack移除事件。
  2. 机器驱动程序或音频HAL禁用耳机路径。
  3. 由于路径现已禁用,DPCM在DAI0上为耳机运行PCM触发(停止),hw_free(),shutdown()操作。
  4. 机器驱动器或音频HAL启用扬声器路径。
  5. 由于路径已启用,DPCM为DAI1扬声器的startup(),hw_params(),prepapre()和触发(start)运行PCM操作。
在此示例中,机器驱动程序或用户空间音频HAL可以改变路由,然后DPCM将负责管理DAI PCM操作以使链接上行或下行。在此过渡期间,音频播放不会停止。

DPCM机器驱动程序

启用DPCM的ASoC机器驱动程序与普通机器驱动程序类似,但我们还必须:
  1. 定义FE和BE DAI链接。
  2. 定义任何FE / BE PCM操作。
  3. 定义小部件图形连接。

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}, .dpcm_playback = 1, }, .....< other FE and BE DAI links here > }; 这个FE DAI链接非常类似于常规DAI链接,除了我们还将DAI链接设置为DPCM FE 。还应使用和 标志设置支持的FE流方向。还可以选择为每个FE指定触发器调用的顺序。这允许ASoC内核在其他组件之前或之后触发DSP(因为某些DSP对订购DAI / DSP启动和停止序列有强烈要求)。dynamic = 1dpcm_playbackdpcm_capture 上面的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, .dpcm_playback = 1, .dpcm_capture = 1, }, .....< other BE DAI links here > }; 此BE DAI链接将DAI0连接到编解码器(在本例中为RT5460 AIF1)。它设置no_pcm标志以标记它具有BE并使用dpcm_playbackdpcm_capture以上设置支持的流方向的标志。 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,MODEM 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,16bit的固定速率,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 covert the FE rate to 48k, stereo */ rate->min = rate->max = 48000; channels->min = channels->max = 2; /* set DAI0 to 16 bit */ snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - SNDRV_PCM_HW_PARAM_FIRST_MASK], SNDRV_PCM_FORMAT_S16_LE); return 0; } 其他PCM操作与常规DAI链接相同。必要时使用。

小部件图连接

BEIO链路通常在初始化时由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平台驱动程序必须实现: -
  1. 前端PCM DAI - 即struct snd_soc_dai_driver。
  2. DAPM图显示了从FE DAI到BE的DSP音频路由。
  3. 来自DSP图的DAPM小部件。
  4. 用于增益,路由等的混频器
  5. DMA配置。
  6. 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仅用于控制,并且可以在流的运行时期间休眠。 主持人可以通过以下方式控制无主机链接: -
  1. 将链接配置为CODEC < - > CODEC样式链接。在这种情况下,链接由DAPM图的状态启用或禁用。这通常意味着有一个混音器控件可用于连接或断开两个DAI之间的路径。
  2. 无主的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, .params = &dai_params, } < ... more DAI links here ... > 这些参数用于在DAPM检测到有效路径时配置DAI hw_params(),然后调用PCM操作以启动链接。当路径不再有效时,DAPM还将调用适当的PCM操作来禁用DAI。

无主的FE

DAI链接由不读取或写入任何PCM数据的FE启用。这意味着创建一个与两个DAI链接的虚拟路径相连的新FE。当FE PCM启动时,DAI链接将启动,当FE PCM停止时,将启动DAI链接。请注意,FE PCM无法在此配置中读取或写入数据。