하드웨어 측에서 DAC를 앰프에 연결하면 그냥 재생됩니다 — 드라이버가 필요 없습니다. 그렇다면 소프트웨어 측에서 소리를 내기 위해 무엇이 필요할까요? Luo Zong (罗总), Rockchip — "罗总开发笔记" WeChat의 글을 번역·주석 달아 정리했습니다.
지난 글은 PDM 마이크를 다뤘습니다 — 칩에 제어가 필요 없고 DTS만 설정하면 녹음됩니다. 이번에는 방향을 뒤집어 재생(playback)입니다.
많은 DAC 칩도 똑같이 "손을 안 대도 됩니다": 제어 인터페이스가 필요 없고 I2S 데이터 선과 전원만 연결하면 소리가 납니다. 하드웨어 엔지니어들이 좋아하는 방식 — 공들이지 않아도 됩니다. 그렇다면 Linux에서 이런 DAC를 dummy-codec과 짝지으면 오디오 재생을 위해 드라이버 코드를 몇 줄이나 써야 할까요?
답은 지난번과 같습니다: 0줄. 다만 이번에는 DAI 방향을 뒤집습니다.
.playback을 선언하고, 사운드 카드는 /dev/snd/pcmC0D0p를 노출합니다.dummy-codec의 .playback capability 선언이 이 글의 주제입니다. DTS는 여전히 동일한 3피스 조합. aplay로 바로 재생됩니다.Part 1(PDM 마이크)은 capture 방향 — 데이터가 SoC 안으로 흐름 — 이고 사운드 카드는 pcmC0D0c를 노출했습니다. 이번에는 반대편인 playback — 데이터가 SoC 밖으로 흐름 — 으로 뒤집습니다:
Part 1의 DAI capability 요약을 떠올려 보세요:
struct snd_soc_dai_driver {
.playback = { ... }, /* declared = this stream exists = /dev/snd/pcmC0D0p */
.capture = { ... }, /* declared = this stream exists = /dev/snd/pcmC0D0c */
};
TI의 PCM5102A — HiFi 세계에서 친숙한 얼굴로 많은 오디오 보드에 쓰입니다. 이 칩의 하드웨어 "요구 사항 목록"을 보세요:
| Requirement | Needed? |
|---|---|
| I2S data lines (BCLK / LRCK / DATA) | Yes — 3 wires |
| MCLK master clock | No — internal PLL derives the clock from BCLK |
| I2C / SPI control interface | No — the chip has no such pins |
| Register configuration | No — there are no registers |
전원을 켜고 I2S 데이터를 넣으면 소리가 납니다. PDM 마이크보다 더 손을 안 대도 됩니다 — "클럭을 줘야 해"라고 생각할 필요조차 없습니다(BCLK는 I2S에 당연히 딸려옵니다).
커널에는 이 칩 전용 드라이버 sound/soc/codecs/pcm5102a.c가 이미 있으며 총 57줄입니다. 핵심은 단일 capability 선언 하나:
static struct snd_soc_dai_driver pcm5102a_dai = {
.name = "pcm5102a-hifi",
.playback = { /* NOTE: playback only! */
.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의 슬롯을 충분히 커버합니다. 어차피 칩에 제어가 필요 없으니 codec 드라이버는 그저 자리 채움일 뿐 — 어떤 자리 채움도 무방합니다.
dummy_dai의 .playback 부분Part 1은 dummy-codec.c의 .capture(60–70행)를 봤습니다. 이 글은 대칭적으로 그 playback 선언(소스 49–59행)을 봅니다:
struct snd_soc_dai_driver dummy_dai = {
.name = "dummy_codec",
.playback = { /* ← Playback capability: this article's focus */
.stream_name = "Dummy Playback", /* stream name */
.channels_min = 1, /* min 1 channel */
.channels_max = 384, /* max 384 channels */
.rates = SNDRV_PCM_RATE_CONTINUOUS, /* any sample rate */
.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), /* bit depth S8 ~ S32 */
},
... /* .capture was covered in Part 1 */
};
.capture와 글자 하나하나 대칭이며, stream_name만 "Dummy Playback"으로 바뀝니다. 이 블록이 있으면 pcmC0D0p가 만들어지고 aplay가 열 수 있습니다.
PCM5102A 선언과 비교하면 차이는 "내용물"뿐입니다:
pcm5102a_dai | dummy_dai | |
|---|---|---|
| playback | 2 channels, 8k–384k (per the datasheet) | 1–384 channels, any sample rate (no real chip — overshoot is free) |
| capture | none | yes (reserved for capture scenarios) |
실제 칩 드라이버의 capability 선언은 데이터시트를 따라야 합니다 — 과하게 선언하면 애플리케이션이 열릴 때 크래시합니다. dummy는 실제 칩과 연결되지 않으므로 과하게 선언해도 괜찮습니다. 실제 제약은 CPU DAI 쪽에서 강제합니다.
Part 1의 PDM과 같은 레시피, 다만 인터페이스와 방향이 다릅니다:
/* ① Codec DAI: dummy-codec placeholder (same driver as Part 1) */
dummy_codec: dummy-codec {
status = "okay";
compatible = "rockchip,dummy-codec";
#sound-dai-cells = <0>;
};
/* ② CPU DAI: enable the I2S controller + pin mux */
&i2s0 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&i2s0m0_sclk &i2s0m0_lrck
&i2s0m0_sdi0 &i2s0m0_sdo0>; /* BCLK / LRCK / data lines */
};
/* ③ Machine: assemble the sound card */
dummy_sound: dummy-sound {
status = "okay";
compatible = "rockchip,multicodecs-card";
rockchip,card-name = "rockchip-dummy"; /* sound-card name */
rockchip,format = "i2s"; /* data format */
rockchip,cpu = <&i2s0>; /* CPU DAI = I2S controller */
rockchip,codec = <&dummy_codec>; /* Codec DAI = dummy */
};
Part 1 대비 두 가지가 바뀝니다:
| Part 1 (PDM capture) | This article (DAC playback) | |
|---|---|---|
| CPU DAI | &pdm | &i2s0 |
| Data direction | capture (mic → SoC) | playback (SoC → DAC) |
3피스 구조는 완전히 동일합니다 — 그것이 프레임워크의 가치입니다. 방향을 바꾸고 인터페이스를 바꿔도 레시피는 그대로.
# 1. Sound card registered
cat /proc/asound/cards
# 0 [dummy ]: rockchip-dummy
# 2. Device node — this time it's the playback (p)
ls /dev/snd/
# controlC0 pcmC0D0p timer
# 3. Play
aplay -D hw:0,0 -c 2 -r 48000 -f S16_LE /tmp/sine.wav
# Android: tinyplay /tmp/sine.wav
# 4. Check the file parameters match
ls -l /tmp/sine.wav
# 48000Hz × 2ch × 2 bytes, matches aplay's arguments
데이터 흐름은 Part 1의 정확히 반대입니다:
두 글을 통틀어 capture 하나와 playback 하나, Part 1의 .capture와 이 글의 .playback을 다뤘습니다 — dummy-codec의 capability 선언이 이제 완전히 설명되었습니다. 나머지 코드(startup / probe)는 건드리지 않았고, 여러분이 쓴 것은 DTS뿐입니다.
하지만 모든 하드웨어가 이렇게 쉽지는 않습니다:
이것들이 바로 드라이버가 신호로 "무언가를 하는" 곳입니다. 다음 글은 답합니다: MCLK, SPK-CTL, RESET은 코드의 어디에 있는가? dummy-codec.c의 남은 startup과 probe가 드디어 무대에 오릅니다.
dummy-codec에서 귀사의 양산 codec 드라이버로 가교하는 것을 도울 수 있습니다.
이 글은 시리즈의 Part 2입니다. 소스 코드는 Rockchip BSP kernel 6.1 (v6.1.162) 기준이며, 하드웨어부터 고급 DAPM 아키텍처까지 안내합니다.
startup이 MCLK를 열고, probe가 리셋하며, Machine이 보드 GPIO를 처리.참조한 소스 파일: 비교용 sound/soc/codecs/pcm5102a.c와 dummy-codec.c.