ハードウェア側で DAC をアンプに接続すればそのまま音が出て——ドライバは不要。ではソフト側は、音を鳴らすために何をすればよいのか? Luo Zong(罗总)、Rockchip——「罗总开发笔记」WeChat から翻訳・注釈。
前回の記事では PDM マイクを扱いました。チップは制御不要で、DTS を設定するだけで録音できます。今回は方向を反転——再生です。
多くの DAC チップも同じく「お任せ」です。制御インターフェース不要、I2S データ線をつなぎ電源を入れるだけで音を出力します。ハードウェアエンジニアは大歓迎——手間いらず。では Linux で、こうした DAC を dummy-codec と組み合わせた場合、音声再生のために何行のドライバコードを書く必要があるのでしょうか?
答えは前回と同じ:0 行。ただし今回は DAI の方向を反転します。
.playback を宣言し、サウンドカードは /dev/snd/pcmC0D0p を公開する。dummy-codec の .playback 能力宣言が本記事の対象。DTS は相変わらず同じ三點セット。 aplay が直接再生する。第 1 回(PDM マイク)は キャプチャ 方向——データは SoC へ 流れ——サウンドカードは pcmC0D0c を公開。今回は反対側の 再生——データは SoC から出る:
第 1 回の DAI 能力まとめを思い出してください:
struct snd_soc_dai_driver {
.playback = { ... }, /* 宣言あり = このストリーム存在 = /dev/snd/pcmC0D0p */
.capture = { ... }, /* 宣言あり = このストリーム存在 = /dev/snd/pcmC0D0c */
};
TI の PCM5102A を例に——HiFi 界では見慣れた顔、多くのオーディオボードで使われます。そのハードウェア「要件リスト」を見てみましょう:
| 要件 | 必要? |
|---|---|
| I2S データ線 (BCLK / LRCK / DATA) | 必要——3 本の線 |
| MCLK マスタークロック | 不要——内蔵 PLL が BCLK からクロックを生成 |
| I2C / SPI 制御インターフェース | 不要——そもそもそのようなピンがない |
| レジスタ設定 | 不要——レジスタそのものが存在しない |
電源を入れ、I2S データを与えるだけで音が出ます。PDM マイク以上に楽——「クロックを必ず与えねば」さえ考えなくて済みます(BCLK は I2S に付きものですから)。
カーネルにはこのチップ専用のドライバ sound/soc/codecs/pcm5102a.c が既にあり、全体で 57 行。核心は単一の能力宣言です:
static struct snd_soc_dai_driver pcm5102a_dai = {
.name = "pcm5102a-hifi",
.playback = { /* 注意:再生のみ! */
.channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_384000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE
},
};
static int pcm5102a_probe(struct platform_device *pdev)
{
return devm_snd_soc_register_component(&pdev->dev, &soc_component_dev_pcm5102a,
&pcm5102a_dai, 1);
}
これだけ:「2 チャンネル再生可能」と宣言して登録する。 しかしそれすら不要——dummy-codec は .playback と .capture の両方を宣言するため、PCM5102A の枠を余るほど満たします。そもそもチップは制御不要なので、コーデックドライバは単なるプレースホルダーで、どのプレースホルダーでも構いません。
dummy_dai の .playback 部分第 1 回は dummy-codec.c の .capture(60–70 行)を見ました。本記事は対称的にその再生宣言(ソース 49–59 行)を見ます:
struct snd_soc_dai_driver dummy_dai = {
.name = "dummy_codec",
.playback = { /* ← 再生能力:本記事の焦点 */
.stream_name = "Dummy Playback", /* ストリーム名 */
.channels_min = 1, /* 最小 1 チャンネル */
.channels_max = 384, /* 最大 384 チャンネル */
.rates = SNDRV_PCM_RATE_CONTINUOUS, /* 任意サンプルレート */
.formats = (SNDRV_PCM_FMTBIT_S8 |
SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S20_3LE |
SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE), /* ビット深さ S8 ~ S32 */
},
... /* .capture は第 1 回で解説 */
};
.capture と文字通り対称で、変わるのは stream_name が "Dummy Playback" になるだけ。このブロックがあれば pcmC0D0p が生成され、aplay がそれを開けます。
PCM5102A の宣言と比べると、違いは「中身」だけ:
pcm5102a_dai | dummy_dai | |
|---|---|---|
| 再生 | 2 チャンネル、8k–384k(データシート準拠) | 1–384 チャンネル、任意サンプルレート(実チップなし——過剰宣言は自由) |
| キャプチャ | なし | あり(キャプチャシナリオ用に予約) |
実チップのドライバの能力宣言はデータシートに厳密に従う必要があり、過剰宣言するとオープン時にアプリがクラッシュします。 dummy は実チップとつながらないため過剰宣言しても構わず、実際の制約は CPU DAI 側が強制します。
第 1 回の PDM と同じレシピ、ただしインターフェースと方向が異なります:
/* ① Codec DAI:ダミーコーデックのプレースホルダー(第 1 回と同じドライバ) */
dummy_codec: dummy-codec {
status = "okay";
compatible = "rockchip,dummy-codec";
#sound-dai-cells = <0>;
};
/* ② CPU DAI:I2S コントローラを有効化+ピンマルチプレックス */
&i2s0 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&i2s0m0_sclk &i2s0m0_lrck
&i2s0m0_sdi0 &i2s0m0_sdo0>; /* BCLK / LRCK / データ線 */
};
/* ③ Machine:サウンドカードを組み立てる */
dummy_sound: dummy-sound {
status = "okay";
compatible = "rockchip,multicodecs-card";
rockchip,card-name = "rockchip-dummy"; /* サウンドカード名 */
rockchip,format = "i2s"; /* データ形式 */
rockchip,cpu = <&i2s0>; /* CPU DAI = I2S コントローラ */
rockchip,codec = <&dummy_codec>; /* Codec DAI = ダミー */
};
第 1 回から変わるのは 2 点:
| 第 1 回(PDM キャプチャ) | 本記事(DAC 再生) | |
|---|---|---|
| CPU DAI | &pdm | &i2s0 |
| データ方向 | キャプチャ(マイク → SoC) | 再生(SoC → DAC) |
三點構造は同一——これがフレームワークの価値です。方向を変え、インターフェースを変えてもレシピは維持されます。
# 1. サウンドカード登録確認
cat /proc/asound/cards
# 0 [dummy ]: rockchip-dummy
# 2. デバイスノード——今回は再生 (p)
ls /dev/snd/
# controlC0 pcmC0D0p timer
# 3. 再生
aplay -D hw:0,0 -c 2 -r 48000 -f S16_LE /tmp/sine.wav
# Android: tinyplay /tmp/sine.wav
# 4. ファイル参数が一致するか確認
ls -l /tmp/sine.wav
# 48000Hz × 2ch × 2 bytes、aplay の引数と一致
データの流れは第 1 回の正反対:
両記事を通じ、1 つはキャプチャ、1 つは再生。第 1 回の .capture と本記事の .playback を扱い、dummy-codec の能力宣言はこれで完全に解説されました。残りのコード(startup/probe)には触れず、書いたのは DTS のみです。
しかしすべてのハードウェアがこんなに簡単とは限りません:
これらがまさに、ドライバが信号で「何かをする」箇所です。次回の記事が答えます:MCLK、SPK-CTL、RESET はコードのどこにあるのか? dummy-codec.c の残りの startup と probe がついに登場します。
dummy-codec から実稼働コーデックドライバへ橋渡しを支援できます。
本記事は連載の 第 2 回 です。ソースコードは Rockchip BSP kernel 6.1(v6.1.162) に基づき、ハードウェアから高度な DAPM アーキテクチャまでを解説します。
startup が MCLK を開き、probe がリセットし、Machine が基板 GPIO を処理。参照ソースファイル:比較用 sound/soc/codecs/pcm5102a.c および dummy-codec.c。