Embedded : : Linux/: : ALSA

[ALSA] 9. DPCM (Dynamic PCM)

Jay.P Morgan 2026. 3. 11. 15:21

 

 

  9.  DPCM (Dynamic PCM)

 

  9.1  Front-End / Back-End 분리

 

DPCM (Dynamic PCM)은 ASoC의 고급 기능으로, 모바일 기기나 차량용 오디오처럼 복잡한 오디오 라우팅이 필요한 시스템을 위해 고안된 구조입니다. 하나의 PCM 스트림을 여러 백엔드 (코덱/디지털 인터페이스)로 동적 라우팅할 수 있습니다. 주로 DSP 기반 시스템에서 사용됩니다.

  • Front-End (FE) DAI: 애플리케이션이 여는 PCM 디바이스. 사용자 공간 인터페이스.
  • Back-End (BE) DAI: 실제 하드웨어 (코덱, HDMI, BT, S/PDIF 등). 여러 FE가 하나의 BE를 공유하거나 동적 전환 가능.

 

 

1. 기존 ALSA의 한계와 DPCM의 탄생

과거의 단순한 오디오 시스템에서는 유저 프로그램(예: 음악 플레이어)이 PCM 장치(예: /dev/snd/pcmC0D0p)를 열면, 그 장치가 물리적인 오디오 코덱(스피커)과 1:1로 하드코딩되어 고정되어 있었습니다. 이 방식은 음악을 듣다가 블루투스 이어폰을 연결하면, 기존 스트림을 완전히 닫고 블루투스용 PCM을 새로 열어야 하는 치명적인 단점이 있었습니다. (음악이 끊김)

이를 해결하기 위해 DPCM은 오디오 경로를 FE(Front-End) BE(Back-End) 두 개의 가상 경로로 쪼개버렸습니다.

 

2. DPCM의 핵심 구조 (이미지 분석)

① FE (Front-End) DAI: 유저를 위한 가상 문

  • 이미지의 초록색 박스 (FE DAI 0, 1, 2)
  • 음악 플레이어, 통화 앱 등 유저 스페이스(User Space) 프로그램이 바라보는 가상의 PCM 장치입니다.
  • 앱 입장에서는 내 소리가 스피커로 가는지 HDMI로 가는지 알 필요 없이, 그냥 이 FE PCM을 열고 데이터를 밀어 넣기만 하면 됩니다.

② BE (Back-End) DAI: 실제 하드웨어 출구

  • 이미지 우측의 박스들 (Codec, BT, HDMI, S/PDIF)
  • 실제로 소리를 밖으로 내보내는 물리적인 하드웨어 인터페이스들입니다.

③ DSP Routing / Mixing (가장 중요한 부분!)

  • 이미지 하단의 짙은 회색 박스
  • FE로 들어온 오디오 데이터를 어떤 BE로 보낼지 결정하는 "동적 라우터(공유기)" 역할을 합니다.
  • DAPM (Dynamic Audio Power Management)이라는 전원/경로 관리 시스템과 연동되어, 런타임(음악이 재생되는 도중)에 스위치를 조작하듯 경로를 바꿀 수 있습니다.

 

 

3. DPCM의 주요 장점 (왜 쓸까요?)

  1. 끊김 없는 동적 라우팅 (Seamless Routing): 음악 플레이어(FE)가 재생을 멈추지 않은 상태에서, 소리의 출력 방향을 스마트폰 스피커(BE: Codec)에서 블루투스(BE: BT)로 즉시 전환할 수 있습니다.
  2. 멀티플렉싱 및 믹싱:
    • 내비게이션 안내음(FE 1)과 음악(FE 0)을 동시에 스피커(BE Codec)로 섞어서(Mixing) 보낼 수 있습니다.
    • 하나의 음악(FE 0)을 뒷좌석 모니터(HDMI)와 앞좌석 스피커(Codec) 양쪽으로 동시에 쪼개서 보낼 수도 있습니다.

 

요약하자면

DPCM은 앱(FE)과 하드웨어(BE) 사이의 직접적인 연결을 끊고, 중간에 똑똑한 교환원(DSP Routing)을 배치하여 앱을 끄지 않고도 소리의 목적지를 마음대로 바꿀 수 있게 해주는 라우팅 프레임워크입니다.

 

 

 

 

  9.2  FE/BE DAI Link 설정

 

 
  /* Front-End DAI */
  static struct snd_soc_dai_link fe_dai_links[] = {
         {
                  .name = "Media Playback",
                  .stream_name = "Media",
                  .cpus = COMP_CPU("sst-media-cpu-dai"),
                  .codecs = COMP_DUMMY(),
                  .platforms = COMP_PLATFORM("sst-dsp"),
                  .dynamic = 1,    /* FE는 dynamic = 1 */
                  .dpcm_playback = 1,
         },
         {
                  .name = "Voice Call",
                  .stream_name = "Voice",
                  .cpus = COMP_CPU("sst-voice-cpu-dai"),
                  .codecs = COMP_DUMMY(),
                  .platforms = COMP_PLATFORM("sst-dsp"),
                  .dynamic = 1,
                  .dpcm_playback = 1,
                  .dpcm_capture = 1,
         },
  };
 
  /* Back-End DAIs */
  static struct snd_soc_dai_link be_dai_links[] = {
         {
                  .name = "Codec",
                  .id = 0,
                  .cpus = COMP_CPU("ssp2-port"),
                  .codecs = COMP_CODEC("wm8731.1-001a", "wm8731-hifi"),
                  .platforms = COMP_DUMMY(),
                  .no_pcm = 1,       /* BE는 no_pcm = 1 (사용자 공간 노출 안 함) */
                  .dpcm_playback = 1,
                  .dpcm_capture = 1,
                  .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
                  SND_SOC_DAIFMT_CBS_CFS,
         },
         {
                  .name = "HDMI",
                  .id = 1,
                  .cpus = COMP_CPU("hdmi-port"),
                  .codecs = COMP_CODEC("hdmi-audio-codec", "i2s-hifi"),
                  .platforms = COMP_DUMMY(),
                  .no_pcm = 1,
                  .dpcm_playback = 1,
         },
  };
 
  /* DAPM routes로 FE와 BE 연결 */
  static const struct snd_soc_dapm_route dpcm_routes[] = {
         /* Media FE → Codec BE */
         {"Codec Playback", NULL, "sst media0_out"},
         {"sst media0_in", NULL, "Codec Capture"},
 
         /* Media FE → HDMI BE */
         {"HDMI Playback", NULL, "sst media0_out"},
 
         /* Voice FE → Codec BE only */
         {"Codec Playback", NULL, "sst voice_out"},
         {"sst voice_in", NULL, "Codec Capture"},
  };
 
Trigger 전파: DPCM에서는 FE의 START/STOP 트리거가 활성 BE에 자동 전파됩니다. SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_BESPOKE 플래그로 트리거 순서를 제어할 수 있습니다.

 

 

 

실제 커널 드라이버 수준에서 DPCM을 구현하는 것은 결국 "어떤 FE(Front-End)가 어떤 BE(Back-End)와 연결될 수 있는가"를 정의하는 과정입니다.

이 연결은 크게 머신 드라이버(Machine Driver)의 C 코드와 디바이스 트리(Device Tree)의 설정을 통해 이루어집니다.

 

1. 머신 드라이버 (C 코드): DAI Link 설정

DPCM의 핵심은 struct snd_soc_dai_link 구조체에서 dynamic 필드와 no_pcm 필드를 적절히 사용하는 것입니다.

① Front-End (FE) 설정

유저가 직접 여는 가상의 PCM 장치입니다.

 
  static struct snd_soc_dai_link my_dpcm_fe_dai_link[] = {
        {
                .name = "Audio Playback",
                .stream_name = "Audio Playback",
                .cpu_dai_name = "multimedia1-cpu-dai",      // DSP 측 FE DAI
                .codec_name = "snd-soc-dummy",                // FE는 실제 코덱이 없으므로 dummy 사용
                .codec_dai_name = "snd-soc-dummy-dai",
                .dynamic = 1,                                                      // ★ 이 링크가 FE임을 선언
                .dpcm_playback = 1,                                         // 재생 지원
                .ops = &my_fe_ops,
        },
  };
 

 

② Back-End (BE) 설정

실제 하드웨어(코덱, I2S 등)와 연결되는 통로입니다. 유저에게는 PCM 장치로 보이지 않습니다.

 
  static struct snd_soc_dai_link my_dpcm_be_dai_link[] = {
        {
                .name = "Codec-Output",
                .cpu_dai_name = "i2s-dai",                         // 실제 I2S 컨트롤러
                .codec_name = "wm8731.0-001a",            // 실제 물리 코덱
                .codec_dai_name = "wm8731-hifi",
                .no_pcm = 1,                                                  // ★ 유저용 PCM 장치를 생성하지 않음 (BE 선언)
                .dpcm_playback = 1,
                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF,
        },
  };

 


2. DAPM Routing: 고속도로 연결하기

DAI Link만 정의한다고 소리가 흐르지는 않습니다. FE와 BE 사이를 연결하는 **DAPM 경로(Route)**를 지정해줘야 합니다.

 
  static const struct snd_soc_dapm_route my_audio_map[] = {
        // "FE의 스트림"을 "BE의 CPU DAI"에 연결
        { "i2s-dai Playback", NULL, "Audio Playback" },
 
        // 필요에 따라 믹서나 다른 위젯을 중간에 배치 가능
        { "Speaker", NULL, "wm8731-hifi Playback" },
  };
 

 

3. 디바이스 트리 (Device Tree) 설정

최신 커널에서는 머신 드라이버의 상당 부분을 디바이스 트리에서 simple-audio-card나 audio-graph-card를 통해 설정할 수 있습니다.

 

 
  sound {
         compatible = "my-audio-card";
 
         // Front-End 정의
         fe_port: simple-audio-card,dai-link@0 {
                  format = "i2s";
                  cpu {
                           sound-dai = <&dsp_fe_dai>;
                  };
                  // 코덱은 dummy 처리
         };

         // Back-End 정의
         be_port: simple-audio-card,dai-link@1 {
                  format = "i2s";
                  cpu {
                           sound-dai = <&i2s_controller>;
                  };
                  codec {
                           sound-dai = <&wm8731_codec>;
                  };
         };

         // 라우팅 (DAPM)
         routing =
                  "DSP FE Playback", "I2S BE Playback",
                  "I2S BE Playback", "Codec Playback";
  };
 

 

 

4. 요약: DPCM 구현의 3단계

  1. FE 정의: dynamic = 1로 설정하여 유저용 가상 PCM 노드 생성.
  2. BE 정의: no_pcm = 1로 설정하여 하드웨어 전용 통로 확보.
  3. Route 연결: snd_soc_dapm_route를 통해 FE와 BE 사이의 데이터 흐름(Path) 결정.

이렇게 설정하면, 런타임에 유저가 FE PCM을 열었을 때 ALSA 코어는 DAPM 경로를 추적하여 활성화된 BE를 찾아 자동으로 데이터를 라우팅합니다.

'Embedded : : Linux > : : ALSA' 카테고리의 다른 글

[ALSA] 11. Sound Open Firmware (SOF)  (0) 2026.03.11
[ALSA] 10. ASoC Topology 프레임워크  (0) 2026.03.11
[ALSA] 8. Compress Offload  (0) 2026.03.11
[ALSA] 7. HW Dep (Hardware Dependent)  (0) 2026.03.11
[ALSA] 6. Timer API  (0) 2026.03.11