ASoC Drivers

  • ASoC Machine Driver : Board driver 라고도 불리며, platform 과 DAI, CODEC 을 연결하는 드라이버. 
  • ASoC Platform Driver : ASoC Platform 드라이버는 DMA 드라이버, SoC DAI 드라이버와 DSP 드라이버로 나뉩니다. Platform 드라이버는 오직 SoC CPU 를 위한 드라이버이며, 특정 보드에 국한되지 않는 드라이버입니다.
    • ASoC DMA Driver
    • ASoC DAI Driver : SoC Controller 에서 찾을 수 있는 Digital Audio Interface 드라이버.
    • ASoC DSP Driver : DSP 드라이버. DPCM 구조 사용.
  • ASoC CODEC Driver : 코덱 드라이버.

 

 

 

 

 

 

DPCM analysis based on Kernel Document

 

 

Description

 

Dynamic PCM 은 타겟이 부팅된 후에 PCM audio 와 다양한 오디오 디바이스를 라우팅할 수 있게 해줍니다.

예를 들어, pcm0 는 I2S DAI0, I2S DAI1 에 연결할 수 있습니다. 여러 개의 ALSA PCM 을 여러개의 DAI에 연결할 수 있도록 해 SoC DSP 드라이버에 유용합니다.

DPCM 라우팅은 ALSA mixer 설정에 의해 결정됩니다.

DPCM 은 DSP 내부 audio path 를 나타내는 DAPM 그래프와 믹서 설정을 사용하여 각 ALSA PCM 에 사용되는 패치를 결정합니다.

DPCM은 기존의 모든 컴포넌트 코덱, 플랫폼 및 DAI 드라이버를 변경하지 않고 재사용할 수 있습니다.

 

 

 

 - Phone Audio System with SoC based 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 front end (FE) ALSA PCM 디바이스를 사용할 수 있으며, 6개의 back end (BE) DAI 를 사용할 수 있습니다.

각 FE PCM 은 모든 BE DAI 에 연결할 수 있습니다. FE PCM 디바이스는 1개 이상의 BE DAI 와 연결할 수 있습니다. (최대 BE: 8개)

 

 

 

 

 

 

 - Example - DPCM Switching playback from DAI0 to 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. Machine Driver 가 헤드셋 잭이 제거되는 이벤트를 받음
  2. Machine Driver 혹은 Audio HAL 이 헤드셋 path 를 disable
  3. disable 이 된 후에 DPCM 은 헤드셋과 연결된 DAI0 를 PCM stop trigger 와 hw_free(), shutdown() 오퍼레이션 수행
  4. Machine Driver 혹은 Audio HAL 은 스피커 path 를 enable
  5. DPCM 은 스피커 path 가 enable 된 후에 스피커와 연결된 DAI1 을 startup(), hw_params(), prepare() 를 수행하고 start trigger 발생 시킴

 

이 예시에서는 Machine Driver 혹은 Audio HAL 이 라우팅을 바꿀 수 있고, DPCM 은 link up / down 하기 위해 DAI PCM 오퍼레이션을 관리합니다.

오디오 재생은 이렇게 link 가 변하는 동안에도 멈추지 않습니다.

 

 

 

DPCM Machine Driver

 

이번 목차에서는 DPCM 의 Machine Driver 에 대해 알아보겠습니다.

DPCM 의 Machine Driver 는 다음 3가지를 제외하고는 기존 드라이버와 유사합니다.

  1. FE, BE DAI link 를 정의
  2. FE, BE PCM operation 을 정의
  3. Widget Graph Connection 을 정의

 

 

 

 

다음 그림을 예시로 FE DAI link 와 BE DAI link를 설정하는 방법을 설명하겠습니다.

 

| 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 link 와 6개의 BE DAI link 가 존재합니다.

다음과 같이 FE DAI link 를 선언합니다.

 

FE DAI link 예시

Machine Driver
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 >
};
  • dynamic = 1
    • Dynamic PCM 이라는 뜻
  • dpcm_playback = 1
    • PCM 을 Playback 으로 사용할 때 선언. dpcm_capture 와 함께 선언 가능.
  • dpcm_capture = 1
    • PCM 을 Capture 로 사용할 때 선언. dpcm_playback 와 함께 선언 가능.
  • .codec_name = "snd-soc-dummy",
    • BE 가 dynamic 하게 routing 에 따라 바뀌므로 dummy 로 선언
  • .codec_dai_name = "snd-soc-dummy-dai",
    • BE 가 dynamic 하게 routing 에 따라 바뀌므로 dummy 로 선언
  • .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
    • 다른 component 보다 앞에 혹은 뒤에 trigger 될 것인지 선언
    • 첫번째 인자는 Playback, 두번째 인자는 Capture
    • 종류
      • SND_SOC_DPCM_TRIGGER_PRE : FE 가 먼저 trigger
      • SND_SOC_DPCM_TRIGGER_POST :  BE 가 먼저 trigger
      • SND_SOC_DPCM_TRIGGER_BESPOKE : FE 와 BE 동시에 trigger (dai 쪽에 bespoke_trigger 가 구현이 되어야 함)
sound/soc/soc-pcm.c
2774     if (rtd->dai_link->dynamic) {
2775         rtd->ops.open       = dpcm_fe_dai_open;
2776         rtd->ops.hw_params  = dpcm_fe_dai_hw_params;
2777         rtd->ops.prepare    = dpcm_fe_dai_prepare;
2778         rtd->ops.trigger    = dpcm_fe_dai_trigger;
2779         rtd->ops.hw_free    = dpcm_fe_dai_hw_free;
2780         rtd->ops.close      = dpcm_fe_dai_close;
2781         rtd->ops.pointer    = soc_pcm_pointer;
2782         rtd->ops.ioctl      = soc_pcm_ioctl;
2783     } else {
2784         rtd->ops.open       = soc_pcm_open;
2785         rtd->ops.hw_params  = soc_pcm_hw_params;
2786         rtd->ops.prepare    = soc_pcm_prepare;
2787         rtd->ops.trigger    = soc_pcm_trigger;
2788         rtd->ops.hw_free    = soc_pcm_hw_free;
2789         rtd->ops.close      = soc_pcm_close;
2790         rtd->ops.pointer    = soc_pcm_pointer;
2791         rtd->ops.ioctl      = soc_pcm_ioctl;
2792     }
sound/soc/soc-pcm.c
2774     if (rtd->dai_link->dynamic) {
2775         rtd->ops.open       = dpcm_fe_dai_open;
2776         rtd->ops.hw_params  = dpcm_fe_dai_hw_params;
2777         rtd->ops.prepare    = dpcm_fe_dai_prepare;
2778         rtd->ops.trigger    = dpcm_fe_dai_trigger;
2779         rtd->ops.hw_free    = dpcm_fe_dai_hw_free;
2780         rtd->ops.close      = dpcm_fe_dai_close;
2781         rtd->ops.pointer    = soc_pcm_pointer;
2782         rtd->ops.ioctl      = soc_pcm_ioctl;
2783     } else {
2784         rtd->ops.open       = soc_pcm_open;
2785         rtd->ops.hw_params  = soc_pcm_hw_params;
2786         rtd->ops.prepare    = soc_pcm_prepare;
2787         rtd->ops.trigger    = soc_pcm_trigger;
2788         rtd->ops.hw_free    = soc_pcm_hw_free;
2789         rtd->ops.close      = soc_pcm_close;
2790         rtd->ops.pointer    = soc_pcm_pointer;
2791         rtd->ops.ioctl      = soc_pcm_ioctl;
2792     }

 

 

 

 

 

BE DAI link 예시

Machine Driver
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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 >
};
  • .platform_name = "snd-soc-dummy",
    • FE DAI Link 가 routing에 따라 바뀌므로 dummy 로 설정
  • .no_pcm = 1,
    • BE DAI link 라는 것을 표시
  • .ignore_suspend = 1,
    .ignore_pmdown_time = 1,
    • hostless 모드에서 사용 (ex. BT Call)
  • .dpcm_playback = 1,
    .dpcm_capture = 1,
    • playback 으로 사용할 것인지, capture 로 사용할 것인지 표시

 

 

cf) hostless 모드란?

.ignore_suspend = 1,
.ignore_pmdown_time = 1,

 

BT Call 과 같이 Host CPU 가 데이터를 전송하지 않는 모드

                    *************
PCM0 <------------> *           * <----DAI0-----> Codec Headset
                    *           *
PCM1 <------------> *           * <----DAI1-----> Codec Speakers
                    *   DSP     *
PCM2 <------------> *           * <====DAI2=====> MODEM
                    *           *
PCM3 <------------> *           * <====DAI3=====> BT
                    *           *
                    *           * <----DAI4-----> DMIC
                    *           *
                    *           * <----DAI5-----> FM
                    *************

 

이 모드는 BT 와 MODEM 이 동작 중이더라도 host CPU 는 sleep 할 수 있다.

 

 

 

 - FE / BE PCM Operations

 

BE dai link 에서는 fixup callback 이 필요합니다.

fixup callback 은 machine driver 에서 FE hw params 에 따라 DAI 구성을 설정하는 데에 사용됩니다. 즉, DSP 는 SRC 나 ASRC 를 FE 나 BE 에서 수행할 수 있습니다.

예를 들어, DSP 가 모든 FE hw params 를 DAI0, 48K, 16 bit, stereo channel 로 바꾼다고 하면, 모든 FE hw params 가 DAI0 로 고정되어야 하며, DAI 는 현재 FE 구성이 어떻든 요구된 사항으로 운영되어야 합니다. 

fixup callback 이 호출되는 시점은 dpcm_fe_dai_hw_params 내부에서, FE hw_params 와 BE hw_params 가 호출되기 이전에 호출됩니다.

 

Machine Driver
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT -
                                  SNDRV_PCM_HW_PARAM_FIRST_MASK],
                                  SNDRV_PCM_FORMAT_S16_LE);
      return 0;
}

 

다른 PCM operation 들은 표준 DAI link 에서 사용하는 PCM Operation 과 동일합니다.

 

 

 

 

 - Widget graph connections

 

 

 

Widget

 

- Desciption

Widget 은 DAPM 커널 문서에서 참고할 수 있습니다.

DAPM 에서의 Widget 은 전력에 영향을 주는 모든 component 를 말합니다. (ex, CODEC, Audio Interface, SRC 등)

DAPM 에는 4개의 Domain 이 있고 이에 따라 4 종류의 Widget 이 있습니다.

  • CODEC Bias domain
  • Platform / Machine domain
  • Path domain : mixer 와 mux 세팅이 유저에 의해 바뀔 때 자동으로 세팅되게 해주는 Widget. alsamixer 나 amixer 에 사용.
  • Stream domain :  스트림 재생 / 녹음이 시작되고 멈추는 것을 enable / disable 할 수 있는 Widget

 

 

 

- DAPM 매크로를 이용한 Widget 선언 예시

드라이버 소스 코드에서 다음과 같이 DAPM 에서 제공하는 매크로를 이용하여 Widget 을 등록할 수 있습니다.

 

  • Stream Domain Widget 예시

 

Widget 등록 매크로 예시
1
2
3
4
5
6
7
/* Stream Domain Widget Define */
#define SND_SOC_DAPM_AIF_IN(wname, stname, wslot, wreg, wshift, winvert)
#define SND_SOC_DAPM_AIF_OUT(wname, stname, wslot, wreg, wshift, winvert)
 
/* Stream Domain Widget Example */
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),

 

 

  • Path Domain Widget 예시
    3가지 input 중에 wm8731 output 으로 어떤 widget 을 선택할건지 MIXER 를 등록하는 예시를 들겠습니다.
    • Line Bypass Input
    • DAC (HiFi Playback)
    • Mic Sidetone Input
include/sound/soc-dapm.h
1
2
#define SOC_DAPM_SINGLE(xname, reg, shift, max, invert)
#define SND_SOC_DAPM_MIXER(wname, wreg, wshift, winvert, wcontrols, wncontrols)

 

Machine Driver
1
2
3
4
5
6
7
8
/* Output Mixer */
static const snd_kcontrol_new_t wm8731_output_mixer_controls[] = {
SOC_DAPM_SINGLE("Line Bypass Switch", WM8731_APANA, 3, 1, 0),
SOC_DAPM_SINGLE("Mic Sidetone Switch", WM8731_APANA, 5, 1, 0),
SOC_DAPM_SINGLE("HiFi Playback Switch", WM8731_APANA, 4, 1, 0),
};
 
SND_SOC_DAPM_MIXER("Output Mixer", WM8731_PWR, 4, 1, wm8731_output_mixer_controls, ARRAY_SIZE(wm8731_output_mixer_controls)),

 

 

 

 

 

- Device Tree 를 이용한 DAPM Widget 선언 예시

디바이스 트리를 사용한 예시입니다.

Device Tree
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
sound {
    compatible = "simple-audio-card";
    simple-audio-card,name = "VF610-Tower-Sound-Card";
    simple-audio-card,format = "left_j";
    simple-audio-card,bitclock-master = <&dailink0_master>;
    simple-audio-card,frame-master = <&dailink0_master>;
 
    simple-audio-card,widgets =
        "Microphone", "Microphone Jack",
        "Headphone", "Headphone Jack",
        "Speaker", "External Speaker";
 
    simple-audio-card,cpu {
        sound-dai = <&sh_fsi2 0>;
    };
 
    dailink0_master: simple-audio-card,codec {
        sound-dai = <&ak4648>;
        clocks = <&osc>;
    };
};

 

DAPM Widget 은 string 이 한 짝을 이뤄 동작합니다. (Document/devictree/bindings/widgets.txt 참고)

  "template-wname", "user-supplied-wname"

 

짝을 이루어 선언하지 않을 경우, 동작하지 않습니다.

첫번째 요소는 template widget name 을 나타내고, 두번째 요소는 user 가 정하는 widget name 입니다.

 

 

 

- Widget 이 실제 등록되는 장소

Widget은 snd_soc_card 구조체에 linked list 형태로 등록되어 연결이 되어 있고, Component 드라이버에서도 Widget list를 확인할 수 있습니다.

snd_soc_card 구조체에 선언된 Widget 들과 snd_soc_component_driver 에 등록된 Widget 은 동일하며, card 를 등록할 때 component 에 있는 Widget 들을 가져다 card 에 저장합니다.

 

 

 

 

 

 

 

 

 

 

 

Routing

DAPM 은 초기화 타이밍에 오디오 라우팅 맵을 참조하여 BE DAI link 를 연결합니다.

 

 

 

만일 BE CODEC 이나 BE DAI 가 dummy 일 경우, 반드시 아래가 드라이버가 반드시 선언이 되어야 합니다.

Machine Driver
1
2
3
4
5
/* BE for codec Headset -  DAI0 is dummy and managed by DSP FW */
static const struct snd_soc_dapm_route audio_map[] = {
    {"DAI0 CODEC IN", NULL, "AIF1 Capture"},
    {"AIF1 Playback", NULL, "DAI0 CODEC OUT"},
};

첫번째 요소는 sink, 두번째 요소는 control, 세번째 요소는 source 입니다.

 

include/sound/soc-dapm.h
1
2
3
4
5
6
7
8
9
struct snd_soc_dapm_route {
    const char *sink;
    const char *control;
    const char *source;
 
    /* Note: currently only supported for links where source is a supply */
    int (*connected)(struct snd_soc_dapm_widget *source,
             struct snd_soc_dapm_widget *sink);
};

 

 

 

다음과 같이 디바이스 트리에서 routing 을 선언할 수도 있습니다.

routing 은 string 을 한 쌍으로 선언해야 합니다.

첫번째 요소는 sink, 두번째 요소는 source 를 의미합니다.

Device Tree
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
sound {
    compatible = "simple-scu-audio-card";
 
    simple-audio-card,name = "rsnd-ak4643";
    simple-audio-card,format = "left_j";
    simple-audio-card,bitclock-master = <&sndcodec>;
    simple-audio-card,frame-master = <&sndcodec>;
 
    simple-audio-card,convert-rate = <48000>;
 
    simple-audio-card,prefix = "ak4642";
    simple-audio-card,routing = "ak4642 Playback", "DAI0 Playback",
            "DAI0 Capture", "ak4642 Capture";
 
    sndcpu: simple-audio-card,cpu {
        sound-dai = <&rcar_sound>;
    };
 
    sndcodec: simple-audio-card,codec {
        sound-dai = <&ak4643>;
        system-clock-frequency = <11289600>;
    };
};

위의 예시는 DAI0 Playback 에서 ak4642 로 음원을 재생하고, ak4642 에서 DAI0 로 음원을 녹음하는 예시를 나타냅니다.

 

 

하나의 Mixer 에 Line Input, DAC, MIC 와 같이 3 가지 input 이 들어갈 때의 디바이스 트리 예시입니다.

Device Tree
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
sound {
    compatible = "simple-scu-audio-card";
 
    simple-audio-card,name = "rsnd-ak4643";
    simple-audio-card,format = "left_j";
    simple-audio-card,bitclock-master = <&sndcodec>;
    simple-audio-card,frame-master = <&sndcodec>;
 
    simple-audio-card,convert-rate = <48000>;
 
    simple-audio-card,prefix = "ak4642";
    simple-audio-card,routing =
            "Output Mixer", "Line Input",
            "Output Mixer", "DAC",
            "Output Mixer", "Mic Bias";
 
    sndcpu: simple-audio-card,cpu {
        sound-dai = <&rcar_sound>;
    };
 
    sndcodec: simple-audio-card,codec {
        sound-dai = <&ak4643>;
        system-clock-frequency = <11289600>;
    };
};

 

ASRC 의 경우 디바이스 트리나 소스 코드에 widget 을 사용하지 않고 probe 시에 snd_soc_dapm_route 에 source 와 sink 를 직접 넣어줍니다.

 

 

 

 

 

 

 

 

 

 

Writing a DPCM DSP driver

 

DPCM DSP 드라이버는 코덱 클래스 드라이버로부터 요소들이 결합된 표준 플랫폼 클래스 ASoC 드라이버와 매우 비슷합니다. DSP driver는 다음이 구현되어야 합니다.

  1. FE PCM DAIs - i.e. struct snd_soc_dai_driver
  2. DAPM widgets
  3. DAPM graph showing DSP audio routing from FE DAIs to BEs
  4. Mixers for gains, routing, etc.
  5. DMA configuration
  6. BE AIF widgets.

 

 

6번 아이템은 DSP 외부의 오디오와 라우팅 하는데에 중요합니다. AIF 는 각 BE 와 각 스트림 방향이 정의되어야 합니다. 즉, BE DAI0 는 다음과 같아야 합니다.

Machine Driver
1
2
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 그래프와 다른 컴포넌트 드라이버 (ex. codec graph) 와 연결되는데 사용됩니다.

 

 

 

 

 

Hostless PCM streams

 

Hostless CPU 는 host CPU 에서 스트림이 라우팅되지 않는 것을 말합니다. 아래는 헤드셋으로 통화할 때 예시입니다.

 

                    *************
PCM0 <------------> *           * <----DAI0-----> Codec Headset
                    *           *
PCM1 <------------> *           * <====DAI1=====> Codec Speakers/Mic
                    *   DSP     *
PCM2 <------------> *           * <====DAI2=====> MODEM
                    *           *
PCM3 <------------> *           * <----DAI3-----> BT
                    *           *
                    *           * <----DAI4-----> DMIC
                    *           *
                    *           * <----DAI5-----> FM
                    *************

 

이 케이스에서는 PCM 데이터가 DSP 를 통해 라우팅 되고, host CPU 는 컨트롤하는데에만 사용되고 런타임에는 sleep 할 수 있습니다.

 

Hostless link 를 제어하는 방법은 2가지가 있습니다.

  1. 링크를 CODEC ↔ CODEC 스타일로 구성하는 방법.
    • 이 경우에는 DAPM graph 로 상태를 enable / disable 할 수 있습니다.
      즉, mixer control 를 통해서 연결 / 연결 해제를 지원해야 합니다.
  2. DAPM 그래프 상 FE 와 BE 사이에 가상 연결을 하는 방법.
    • FE 에 mixer control 를 등록합니다.
      이 방법은 DAI link 간 더 많은 control 할 수 있게 해주지만, user space 에서 링크를 제어하기 위해 더 많은 코드가 필요합니다.
      만일 HW 가 PCM operation 간의 fine grained 가 필요할 경우가 아니면 CODEC ↔ CODEC 을 추천합니다.

 

 

 

DAPM 그래프 내에서 path 를 enable 할 때 DAI link 가 enable 됩니다.

machine 드라이버는 DAI link 에 추가적은 parameter 를 세팅합니다.

 

CODEC ↔ CODEC link
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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 ... >
}

 

위 예시의 dai_params 는 DAPM 이 유효한 path 을 감지할 때 DAI 의 hw_parms() 오퍼레이션에서 사용됩니다. 그리고 link 를 start operation 을 호출합니다.

이 후에 DAPM 은 Path 가 유효하지 않아 졌을 때, 적절한 PCM operation 을 찾아서 DAI 를 disable 합니다.

 

 

 

Hostless FE

 

PCM data 를 아예 읽거나 쓰지 않는 DAI link 의 경우 FE 에 의해 enable 됩니다.

두 개의 DAI link 에 연결되는 가상의 path 와 연결되는 새로운 FE 를 만드는 것을 뜻합니다.

DAI link 들은 FE PCM 이 시작할 때 시작 해야 하고, FE PCM 이 멈출 때 멈추어야 합니다.

단, FE PCM 은 이 구성에서는 read / write 를 할 수 없습니다.

 

+ Recent posts