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_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);
이 케이스에서는 PCM 데이터가 DSP 를 통해 라우팅 되고, host CPU 는 컨트롤하는데에만 사용되고 런타임에는 sleep 할 수 있습니다.
Hostless link 를 제어하는 방법은 2가지가 있습니다.
링크를 CODEC ↔ CODEC 스타일로 구성하는 방법.
이 경우에는 DAPM graph 로 상태를 enable / disable 할 수 있습니다. 즉, mixer control 를 통해서 연결 / 연결 해제를 지원해야 합니다.
DAPM 그래프 상 FE 와 BE 사이에 가상 연결을 하는 방법.
FE 에 mixer control 를 등록합니다. 이 방법은 DAI link 간 더 많은 control 할 수 있게 해주지만, user space 에서 링크를 제어하기 위해 더 많은 코드가 필요합니다. 만일 HW 가 PCM operation 간의 fine grained 가 필요할 경우가 아니면 CODEC ↔ CODEC 을 추천합니다.
CODEC ↔ CODEC link
DAPM 그래프 내에서 path 를 enable 할 때 DAI link 가 enable 됩니다.
흔히 말하는 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;
};