include/sound/soc-dpcm.h 참고

 

enum 정리

snd_soc_dpcm_update : 실행할 runtime_update 값. 업데이트를 안할 것인지, BE 혹인 FE 를 업데이트할 것인지 나타냄
 - SND_SOC_DPCM_UPDATE_NO
 - SND_SOC_DPCM_UPDATE_BE
 - SND_SOC_DPCM_UPDATE_FE

snd_soc_dpcm_link_state : 새로운 링크를 만들 것인지, 만들어진 링크를 분해할 것인지 나타냄
 - SND_SOC_DPCM_LINK_STATE_NEW
 - SND_SOC_DPCM_LINK_STATE_FREE

snd_soc_dpcm_state : PCM 프론트엔트에서 PCM 백엔드의 상태를 나타냄
 - SND_SOC_DPCM_STATE_NEW
 - SND_SOC_DPCM_STATE_OPEN
 - SND_SOC_DPCM_STATE_HW_PARAMS
 - SND_SOC_DPCM_STATE_PREPARE
 - SND_SOC_DPCM_STATE_START
 - SND_SOC_DPCM_STATE_STOP
 - SND_SOC_DPCM_STATE_PAUSED
 - SND_SOC_DPCM_STATE_SUSPEND
 - SND_SOC_DPCM_STATE_HW_FREE
 - SND_SOC_DPCM_STATE_CLOSE

snd_soc_dpcm_trigger : trigger 가 발생했을 때 어떤 trigger 가 발생했는지 나타냄
 - SND_SOC_DPCM_TRIGGER_PRE
 - SND_SOC_DPCM_TRIGGER_HOST
 - SND_SOC_DPCM_TRIGGER_BESPOKE

 

Dynamic PCM link 구조체

이 구조체는 FE DAI 와 BE DAI 를 runtime 에 연결하고, link 상태와 hw_params 정보를 저장한다.

struct snd_soc_dpcm {
        /* FE and BE DAIs*/
        struct snd_soc_pcm_runtime *be;
        struct snd_soc_pcm_runtime *fe;

        /* link state */
        enum snd_soc_dpcm_link_state state;

        /* list of BE and FE for this DPCM link */
        struct list_head list_be;
        struct list_head list_fe;

        /* hw params for this link - may be different for each link */
        struct snd_pcm_hw_params hw_params;
#ifdef CONFIG_DEBUG_FS
        struct dentry *debugfs_state;
#endif
};
struct snd_soc_dpcm_runtime {
        struct list_head be_clients;
        struct list_head fe_clients;

        int users;
        struct snd_pcm_runtime *runtime;
        struct snd_pcm_hw_params hw_params;

        /* state and update */
        enum snd_soc_dpcm_update runtime_update;
        enum snd_soc_dpcm_state state;

        int trigger_pending; /* trigger cmd + 1 if pending, 0 if not */
};

 

궁금증 .. dpcm API 들도 snd_soc_pcm_runtime 을 쓰는데, snd_soc_dpcm_runtime 은 언제 쓰이는지...? 

 -> 쫓아가봤는데 쓰이질 않음..

 

DPCM APIs

/* can this BE stop and free */
int snd_soc_dpcm_can_be_free_stop(struct snd_soc_pcm_runtime *fe,
                struct snd_soc_pcm_runtime *be, int stream);

/* can this BE perform a hw_params() */
int snd_soc_dpcm_can_be_params(struct snd_soc_pcm_runtime *fe,
                struct snd_soc_pcm_runtime *be, int stream);

/* is the current PCM operation for this FE ? */
int snd_soc_dpcm_fe_can_update(struct snd_soc_pcm_runtime *fe, int stream);

/* is the current PCM operation for this BE ? */
int snd_soc_dpcm_be_can_update(struct snd_soc_pcm_runtime *fe,
                struct snd_soc_pcm_runtime *be, int stream);

/* get the substream for this BE */
struct snd_pcm_substream *
        snd_soc_dpcm_get_substream(struct snd_soc_pcm_runtime *be, int stream);

/* get the BE runtime state */
enum snd_soc_dpcm_state
        snd_soc_dpcm_be_get_state(struct snd_soc_pcm_runtime *be, int stream);

/* set the BE runtime state */
void snd_soc_dpcm_be_set_state(struct snd_soc_pcm_runtime *be, int stream,
        enum snd_soc_dpcm_state state);

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 를 할 수 없습니다.

 

ALSA는 Audio Linux Sound Architecture의 약자.

리눅스에서 Audio를 구현하고자 할 때 사용되는 공개 라이브러리이다.

많이 사용되는 ALSA와 ALSA보다 몸집이 작은 tinyALSA, 이 두 라이브러리의 기본 API에 대해 설명한다.

 

 

  • ALSA Overview
  • tinyALSA

    • Data Structure

      • PCM config
      • PCM Structure
    • PCM Open
    • PCM Read
    • PCM Write
    • PCM Close
  • ALSA
    • Data Structures
    • PCM Open
    • PCM HW Params
    • PCM SW Params
    • PCM Prepare
    • PCM Start
    • PCM Avail
    • PCM Read
    • PCM Write
    • PCM Close
  • ALSA lib 참고 웹페이지

    • ALSA
    • Tiny ALSA

 

 


ALSA Overview

 

흔히 말하는 ALSA는 보통 User Space에서 ALSA library를 가져다 쓰는 것을 말한다.

ALSA application으로는 aplay, arecode, amixer, tinyplay 등이 있으며, 오디오 어플리케이션을 쓸 때 이 코드를 많이 참고하여 사용한다.


 

 

 

tinyALSA

Android에는 tinyALSA가 올라가있다.

tinyALSA의 경우에는 단순히 Open, Read/Write, Close만 해주면 audio 사용이 가능하다.

 

 

Data Structure

tinyALSA를 사용하기 위해서는 PCM Handler와 PCM Config가 필요하다.

 

PCM config

struct pcm_config {
    unsigned int channels;
    unsigned int rate;
    unsigned int period_size;
    unsigned int period_count;
    enum pcm_format format;
    /* Values to use for the ALSA start, stop and silence thresholds.  Setting
     * any one of these values to 0 will cause the default tinyalsa values to be
     * used instead.  Tinyalsa defaults are as follows.
     *
     * start_threshold   : period_count * period_size
     * stop_threshold    : period_count * period_size
     * silence_threshold : 0
     */
    unsigned int start_threshold;
    unsigned int stop_threshold;
    unsigned int silence_threshold;
    /* Minimum number of frames available before pcm_mmap_write() will actually
     * write into the kernel buffer. Only used if the stream is opened in mmap mode
     * (pcm_open() called with PCM_MMAP flag set).   Use 0 for default.
     */
    int avail_min;
};

pcm config 구조체에서는 channel 개수, rate, period size, period count, audio format에 대한 설정을 해준다.

  • format
    • PCM_FORMAT_S16_LE
      • Signed 16-bit, Little Endian
    • 그 외 PCM_FORMAT_S16_BE, PCM_FORMAT_S24_LE, PCM_FORMAT_S24_BE
    • tinyalsa\pcm.h 에서 확인 가능
  • period size
    • 한번에 읽어오는 frame의 개수
  • period count
    • buffer = period size * period count
  • start threshold
    • PCM start 하기에 최소한으로 필요한 frame 개수
  • stop threshold
    • PCM stop 하기에 최소한으로 필요한 frame 개수

 

PCM Structure

pcm 구조체

struct pcm {
    int fd;
    unsigned int flags;
    int running:1;
    int underruns;
    unsigned int buffer_size;
    unsigned int boundary;
    char error[PCM_ERROR_MAX];
    struct pcm_config config;
    struct snd_pcm_mmap_status *mmap_status;
    struct snd_pcm_mmap_control *mmap_control;
    struct snd_pcm_sync_ptr *sync_ptr;
    void *mmap_buffer;
    unsigned int noirq_frames_per_msec;
    int wait_for_avail_min;
};

 

PCM Open

struct pcm *pcm_open(unsigned int card, unsigned int device, unsigned int flags, const struct pcm_config *config)

Description

pcm open을 하고 pcm 구조체를 받아온다.

Parameters

card - sound card 번호

device - dai-link 번호

ex) device tree에서 sound card 0의 dai-link 0번 i2S를 사용한다면, card와 device에 각각 0을 넘겨주면 된다.

flags - pcm open flags

ex) PCM_OUT, PCM_IN, PCM_MMAP, PCM_NOIRQ, PCM_NORESTART, PCM_MONOTONIC, PCM_NONBLOCK, PCM_STATE_PREPARED .. etc

config - pcm config 구조체

Return

pcm 구조체

 

PCM Read

int pcm_read(struct pcm *pcm, void *data, unsigned int count)

Description

data에 pcm read한 데이터를 count만큼 가져온다.

Parameters

pcm - open할 때 반환받은 pcm 구조체

data - audio data를 저장할 buffer

count - frame 당 byte 개수

보통 하나의 frame에 몇 byte가 있는지 반환하는 pcm_frames_to_bytes(const struct pcm *pcm, unsigned int frames) 함수를 사용해서 count 값을 넣어준다.

Returns

0일 경우, 성공

-EPIPE의 경우, underrun이나 overrun 발생할 때 반환

-ESTRPIPE 의 경우, device가 suspend 상태일 때 반환

-EBADFD의 경우, 어플리케이션과 alsa-lib 사이에 연결이 끊어진 경우에 반환

-ENOTTY, -ENODEV 의 경우, device가 물리적으로 해제된 경우에 반환

 

PCM Write

int pcm_write(struct pcm *pcm, const void *data, unsigned int count)

Description

data에 저장된 데이터를 count만큼 쓴다.

Parameters

pcm - open할 때 반환받은 pcm 구조체

data - audio data를 저장된 buffer

count - frame 당 byte 개수

보통 하나의 frame에 몇 byte가 있는지 반환하는 pcm_frames_to_bytes(const struct pcm *pcm, unsigned int frames) 함수를 사용해서 count 값을 넣어준다.

Returns

0 일 경우, 성공

-EPIPE의 경우, underrun이나 overrun 발생할 때 반환

-ESTRPIPE 의 경우, device가 suspend 상태일 때 반환

-EBADFD의 경우, 어플리케이션과 alsa-lib 사이에 연결이 끊어진 경우에 반환

-ENOTTY, -ENODEV 의 경우, device가 물리적으로 해제된 경우에 반환


PCM Close

int pcm_close(struct pcm *pcm)

Description

pcm을 close한다.

Parameters

pcm - open할 때 반환받은 pcm 구조체

Returns

항상 0 반환

 

 

 

 

 

 

ALSA

Linux에서는 ALSA를 사용한다.

ALSA의 경우에는 open, hw parameter 설정, sw parameter 설정, prepare, start, (avail 체크), read/write, close 과정으로 audio를 다룬다.

 

 

Data Structures

PCM structure

typedef struct  _snd_pcm snd_pcm_t

PCM HW config

typedef struct  _snd_pcm_hw_params snd_pcm_hw_params_t

PCM SW config

typedef struct  _snd_pcm_sw_params snd_pcm_sw_params_t

 

PCM Open

int snd_pcm_open(snd_pcm_t **pcmp, const char *name, snd_pcm_stream_t stream, int mode)

Description

pcm을 open한다.

Parameters

pcmp - open할 때 반환 받을 pcm 구조체

name - PCM 식별자

(ex) "hw:0,2"

stream - playback 목적으로 pcm open을 하는지, capture 목적으로 pcm open을 하는지 넘겨줌

(ex) SND_PCM_STREAM_PLAYBACK, SND_PCM_STREAM_CAPTURE

mode - open mode

(ex) SND_PCM_NONBLOCK, SND_PCM_ASYNC

Returns

항상 0 반환

 

PCM HW Params

Hardware Parameter란 audio format, channel 개수, sampling rate, period size, buffer size 등을 말한다.

 

ALSA Library FunctionDescription

snd_pcm_hw_params_malloc snd_pcm_hw_params_t 구조체 메모리 할당
snd_pcm_hw_params_any snd_pcm_hw_params_t 구조체 초기화
snd_pcm_hw_params_set_access access type 설정 (ex) SND_PCM_ACCESS_RW_INTERLEAVED, SND_PCM_ACCESS_RW_NONINTERLEAVED
snd_pcm_hw_params_set_format audio format 설정 (ex) SND_PCM_FORMAT_S16_LE
snd_pcm_hw_params_set_channels audio channel 설정
snd_pcm_hw_params_set_buffer_size audio buffer size 설정
snd_pcm_hw_params_set_rate_near 샘플레이트 설정 시 하드웨어가 정확한 샘플레이트를 지원하지 않는다면 가장 근접한 가능한 레이트를 사용하도록 설정
snd_pcm_hw_params_set_period_size alsa period_size를 설정
snd_pcm_hw_params

snd_pcm_hw_params_t 구조체에 저장된 PCM hardware configuration을 하드웨어에 알려주고 snd_pcm_prepare를 호출

snd_pcm_hw_params_free snd_pcm_hw_params_t 구조체 free


참고 웹페이지  https://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m___h_w___params.html#ga6e2dd8efbb7a4084bd05e6cc458d84f7

 

 

 

<cf> interleaved access vs non-interleaved access

 

  • Interleaved mode
    • 하나의 buffer에 channel sample이 번갈아가며 저장
  • Non-interleaved mode
    • 각 채널별로 buffer가 존재하여 sample이 저장됨

 

 

 

PCM SW Params

Software Parameter란 start threshold, stop threshold, avail min 등을 말한다.

 

ALSA Library FunctionDescription

snd_pcm_sw_params_malloc snd_pcm_sw_params_t 구조체 할당
snd_pcm_sw_params_current 현재 SW config를 snd_pcm_sw_params_t 구조체에 저장
snd_pcm_sw_params_set_start_threshold start threshold 설정 (보통 period size로 설정) ※ byte 크기로 넘겨주는 것에 유의
snd_pcm_sw_params_set_avail_min 최소 avail 설정 (보통 period size으로 설정)
snd_pcm_sw_params snd_pcm_sw_params_t 구조체에 저장된 PCM software configuration을 아래 layer에 알려줌
snd_pcm_sw_params_free snd_pcm_sw_params_t 구조체 free

참고 웹페이지 https://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m___s_w___params.html

 

(cf) alsa에서 말하는 avail 이란, buffer에서 빈 공간 frame 개수를 말한다. delay는 buffer에 있는 데이터가 들은 frame 개수를 말한다.

 

PCM Prepare

int snd_pcm_prepare(snd_pcm_t *pcm)

Description

pcm을 prepare한다.

Parameters

pcm - open할 때 반환 받은 pcm 구조체

Returns

0 일 경우, 성공

-EPIPE의 경우, underrun이나 overrun 발생할 때 반환

-ESTRPIPE 의 경우, device가 suspend 상태일 때 반환

-EBADFD의 경우, 어플리케이션과 alsa-lib 사이에 연결이 끊어진 경우에 반환

-ENOTTY, -ENODEV 의 경우, device가 물리적으로 해제된 경우에 반환


PCM Start

int snd_pcm_start(snd_pcm_t *pcm)

Description

pcm을 start한다.

Parameters

pcm - open할 때 반환 받은 pcm 구조체

Returns

0 일 경우, 성공

-EPIPE의 경우, underrun이나 overrun 발생할 때 반환

-ESTRPIPE 의 경우, device가 suspend 상태일 때 반환

-EBADFD의 경우, 어플리케이션과 alsa-lib 사이에 연결이 끊어진 경우에 반환

-ENOTTY, -ENODEV 의 경우, device가 물리적으로 해제된 경우에 반환


PCM Avail

snd_pcm_sframes_t snd_pcm_avail(snd_pcm_t *pcm)

Description

읽히거나 쓸 준비가 된 frame 개수를 반환한다. (ALSA에서 보통 avail은 버퍼의 비어있는 공간, delay는 버퍼의 데이터가 있는 공간을 말한다.)

Parameters

pcm - open할 때 반환 받은 pcm 구조체

Returns

양수일 경우, frame 개수 반환

-EPIPE의 경우, underrun이나 overrun 발생할 때 반환

-ESTRPIPE 의 경우, device가 suspend 상태일 때 반환

-EBADFD의 경우, 어플리케이션과 alsa-lib 사이에 연결이 끊어진 경우에 반환

-ENOTTY, -ENODEV 의 경우, device가 물리적으로 해제된 경우에 반환

 

PCM Read

snd_pcm_sframes_t snd_pcm_readi(snd_pcm_t *pcm, void buffer, snd_pcm_uframes_t size)

Description

interleaved frame을 읽는다.

만일 PCM이 blocking mode로 열렸을 경우, 모든 frame이 찰 때까지 기다린다. underrun이 발생할 경우에만 읽어온 frame 개수가 적을 수 있다.

non-blocking mode일 경우, 기다리지 않는다.

Parameters

pcm - open할 때 반환 받은 pcm 구조체

buffer - 읽어오려는 frame을 담을 버퍼

size - 읽으려는 frame 개수

Returns

양수일 경우, 읽어온 frame 개수 반환

-EPIPE의 경우, underrun이나 overrun 발생할 때 반환

underrun이 발생 하는 경우, snd_pcm_prepare를 호출해야 함.

-ESTRPIPE 의 경우, device가 suspend 상태일 때 반환

suspend 상태인 경우, snd_pcm_resume을 해준 뒤 snd_pcm_prepare 호출해야 함.

-EBADFD의 경우, 올바른 state가 아닐 경우 반환

SND_PCM_STATE_PREPARED 상태나 SND_PCM_STATE_RUNNING 상태일 때만 read/write가 가능

snd_pcm_sframes_t snd_pcm_readn(snd_pcm_t *pcm, void buffer, snd_pcm_uframes_t size)

Description

non-interleaved frame을 읽는다.

만일 PCM이 blocking mode로 열렸을 경우, 모든 frame이 찰 때까지 기다린다. underrun이 발생할 경우에만 읽어온 frame 개수가 적을 수 있다.

non-blocking mode일 경우, 기다리지 않는다.

Parameters

pcm - open할 때 반환 받은 pcm 구조체

buffer - 읽어오려는 frame을 담을 버퍼

size - 읽으려는 frame 개수

Returns

양수일 경우, 읽어온 frame 개수 반환

-EPIPE의 경우, underrun이나 overrun 발생할 때 반환

underrun이 발생 하는 경우, snd_pcm_prepare를 호출해야 함.

-ESTRPIPE 의 경우, device가 suspend 상태일 때 반환

suspend 상태인 경우, snd_pcm_resume을 해준 뒤 snd_pcm_prepare 호출해야 함.

-EBADFD의 경우, 올바른 state가 아닐 경우 반환

SND_PCM_STATE_PREPARED 상태나 SND_PCM_STATE_RUNNING 상태일 때만 read/write가 가능

PCM Write

snd_pcm_sframes_t snd_pcm_writei(snd_pcm_t *pcm, void buffer, snd_pcm_uframes_t size)

Description

interleaved frame을 쓴다.

만일 PCM이 blocking mode로 열렸을 경우, 모든 frame이 play 될때까지 기다리거나 playback ring buffer에 넣을 때까지 기다린다. underrun이 발생할 경우에만 읽어온 frame 개수가 적을 수 있다.

non-blocking mode일 경우, 기다리지 않는다.

Parameters

pcm - open할 때 반환 받은 pcm 구조체

buffer - 읽어오려는 frame을 담을 버퍼

size - 읽으려는 frame 개수

Returns

양수일 경우, 읽어온 frame 개수 반환

-EPIPE의 경우, underrun이 발생할 때 반환

underrun이 발생 하는 경우, snd_pcm_prepare를 호출해야 함

-ESTRPIPE 의 경우, device가 suspend 상태일 때 반환

suspend 상태인 경우, snd_pcm_resume을 해준 뒤 snd_pcm_prepare 호출해야 함

-EBADFD의 경우, 올바른 state가 아닐 경우 반환

SND_PCM_STATE_PREPARED 상태나 SND_PCM_STATE_RUNNING 상태일 때만 read/write가 가능

 

snd_pcm_sframes_t snd_pcm_writen(snd_pcm_t *pcm, void buffer, snd_pcm_uframes_t size)

Description

non-interleaved frame을 쓴다.

만일 PCM이 blocking mode로 열렸을 경우, 모든 frame이 play 될때까지 기다리거나 playback ring buffer에 넣을 때까지 기다린다. underrun이 발생할 경우에만 읽어온 frame 개수가 적을 수 있다.

non-blocking mode일 경우, 기다리지 않는다.

Parameters

pcm - open할 때 반환 받은 pcm 구조체

buffer - 읽어오려는 frame을 담을 버퍼

size - 읽으려는 frame 개수

Returns

양수일 경우, 읽어온 frame 개수 반환

-EPIPE의 경우, underrun이 발생할 때 반환

underrun이 발생 하는 경우, snd_pcm_prepare를 호출해야 함

-ESTRPIPE 의 경우, device가 suspend 상태일 때 반환

suspend 상태인 경우, snd_pcm_resume을 해준 뒤 snd_pcm_prepare 호출해야 함

-EBADFD의 경우, 올바른 state가 아닐 경우 반환

SND_PCM_STATE_PREPARED 상태나 SND_PCM_STATE_RUNNING 상태일 때만 read/write가 가능


PCM Close

int snd_pcm_close(snd_pcm_t *pcm)

Description

pcm을 close한다.

Parameters

pcm - open할 때 반환 받은 pcm 구조체

Returns

0 일 경우, 성공

음수의 경우 실패

 

 

 

ALSA library 참고 웹페이지

ALSA

ALSA libWeb Page

PCM interface https://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m.html#ga042aba7262a4cbb4d444b6fc08cb7124
HW params https://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m___h_w___params.html
SW Params https://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m___s_w___params.html#ga79b12cbbd309750156261e7f5a39167b

 

Tiny ALSA

ALSA libWeb Page

github https://github.com/tinyalsa/tinyalsa

 

 

+ Recent posts