ASoC 编解码器类驱动

编解码器类驱动是通用的、与硬件无关的代码,用于配置编解码器、FM、MODEM、BT 或外部 DSP,以提供音频捕获和播放。它不应包含任何特定于目标平台或机器的代码。所有平台和机器特定的代码都应分别添加到平台和机器驱动程序中。

每个编解码器类驱动必须提供以下功能:-

  1. 编解码器 DAI 和 PCM 配置

  2. 编解码器控制 IO - 使用 RegMap API

  3. 混音器和音频控制

  4. 编解码器音频操作

  5. DAPM 描述。

  6. DAPM 事件处理程序。

可选地,编解码器驱动程序还可以提供:-

  1. DAC 数字静音控制。

最好将本指南与 sound/soc/codecs/ 中现有的编解码器驱动程序代码结合使用。

ASoC 编解码器驱动程序分解

编解码器 DAI 和 PCM 配置

每个编解码器驱动程序都必须有一个 struct snd_soc_dai_driver 来定义其 DAI 和 PCM 功能和操作。该结构已导出,因此您的机器驱动程序可以向核心注册它。

例如:

static struct snd_soc_dai_ops wm8731_dai_ops = {
      .prepare        = wm8731_pcm_prepare,
      .hw_params      = wm8731_hw_params,
      .shutdown       = wm8731_shutdown,
      .mute_stream    = wm8731_mute,
      .set_sysclk     = wm8731_set_dai_sysclk,
      .set_fmt        = wm8731_set_dai_fmt,
};

struct snd_soc_dai_driver wm8731_dai = {
      .name = "wm8731-hifi",
      .playback = {
              .stream_name = "Playback",
              .channels_min = 1,
              .channels_max = 2,
              .rates = WM8731_RATES,
              .formats = WM8731_FORMATS,},
      .capture = {
              .stream_name = "Capture",
              .channels_min = 1,
              .channels_max = 2,
              .rates = WM8731_RATES,
              .formats = WM8731_FORMATS,},
      .ops = &wm8731_dai_ops,
      .symmetric_rate = 1,
};

编解码器控制 IO

编解码器通常可以通过 I2C 或 SPI 样式的接口来控制(AC97 将控制与 DAI 中的数据结合在一起)。 编解码器驱动程序应使用 Regmap API 进行所有编解码器 IO。 请参阅 include/linux/regmap.h 和现有的编解码器驱动程序,以了解 regmap 的用法示例。

混音器和音频控制

可以使用 soc.h 中定义的便利宏来定义所有编解码器混音器和音频控制。

#define SOC_SINGLE(xname, reg, shift, mask, invert)

按如下方式定义单个控件:-

xname = Control name e.g. "Playback Volume"
reg = codec register
shift = control bit(s) offset in register
mask = control bit size(s) e.g. mask of 7 = 3 bits
invert = the control is inverted

其他宏包括:-

#define SOC_DOUBLE(xname, reg, shift_left, shift_right, mask, invert)

一个立体声控制

#define SOC_DOUBLE_R(xname, reg_left, reg_right, shift, mask, invert)

跨越 2 个寄存器的立体声控制

#define SOC_ENUM_SINGLE(xreg, xshift, xmask, xtexts)

按如下方式定义单个枚举控制:-

xreg = register
xshift = control bit(s) offset in register
xmask = control bit(s) size
xtexts = pointer to array of strings that describe each setting

#define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xtexts)

定义一个立体声枚举控制

编解码器音频操作

编解码器驱动程序还支持以下 ALSA PCM 操作:-

/* SoC audio ops */
struct snd_soc_ops {
      int (*startup)(struct snd_pcm_substream *);
      void (*shutdown)(struct snd_pcm_substream *);
      int (*hw_params)(struct snd_pcm_substream *, struct snd_pcm_hw_params *);
      int (*hw_free)(struct snd_pcm_substream *);
      int (*prepare)(struct snd_pcm_substream *);
};

有关详细信息,请参阅 ALSA 驱动程序 PCM 文档。 https://linuxkernel.org.cn/doc/html/latest/sound/kernel-api/writing-an-alsa-driver.html

DAPM 描述

动态音频电源管理描述描述了编解码器电源组件及其与 ASoC 核心的关系和寄存器。 请阅读 便携式设备的动态音频电源管理,以了解有关构建描述的详细信息。

另请参阅其他编解码器驱动程序中的示例。

DAPM 事件处理程序

此函数是一个回调,用于处理编解码器域 PM 调用和系统域 PM 调用(例如,挂起和恢复)。 它用于在不使用时使编解码器进入睡眠状态。

电源状态:-

SNDRV_CTL_POWER_D0: /* full On */
/* vref/mid, clk and osc on, active */

SNDRV_CTL_POWER_D1: /* partial On */
SNDRV_CTL_POWER_D2: /* partial On */

SNDRV_CTL_POWER_D3hot: /* Off, with power */
/* everything off except vref/vmid, inactive */

SNDRV_CTL_POWER_D3cold: /* Everything Off, without power */

编解码器 DAC 数字静音控制

大多数编解码器在 DAC 之前都有一个数字静音功能,可用于最大限度地减少任何系统噪声。 静音会阻止任何数字数据进入 DAC。

可以创建一个回调,当应用或释放静音时,核心会为每个编解码器 DAI 调用该回调。

即:

static int wm8974_mute(struct snd_soc_dai *dai, int mute, int direction)
{
      struct snd_soc_component *component = dai->component;
      u16 mute_reg = snd_soc_component_read(component, WM8974_DAC) & 0xffbf;

      if (mute)
              snd_soc_component_write(component, WM8974_DAC, mute_reg | 0x40);
      else
              snd_soc_component_write(component, WM8974_DAC, mute_reg);
      return 0;
}