asoc之动态PCM

动态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无法在此配置中读取或写入数据。

你可能感兴趣的:(linux设备与驱动)