alsa笔记
ALSA(Advanced Linux Sound Architecture)简介
以上是android和linux系统的音频整体架构图,他们不同的区别主要是在用户空间,Linux通过ALSA-Lib来和ALSA交互,而android则是tingyAlsa,其位于aosp源码根目录的/external/tinyalsa下;
在Kernel层,Alsa向上封装的Driver驱动,通过向上暴露设备位于/dev/snd/目录的设备节点,提供交互
还有重要ALSA ASoc是ALSA的核心部分,它将音频系统分为Codec、Platform和Machine模块,并为每个模块提供相关的注册接口,协作各个模块,提供灵活的音频解决方案,除上面3个模块外,还有DAPM动态音频电源管理、pcm substream等音频数据处理。
hardware:包含platform、codec和machine,他们三者的关系如下图:
dai(Digital Audio Interfaces)表示数字音频接口,一种抽象的传递音频数据接口
Platform模块包含dma、cpu,其中dma允许外设(磁盘、网络接口)快速从内存中存取数据而不需要CPU处理,这里的cpu_dai相当于平台内部硬件资源,将音频数据取出来后,通过音频数字接口DAI,与Codec_dai进行数据收发;通常其相关的底层数字接口有i2s、pcm、tdm和S/PDIF等等,相关接口可参考数字音频接口,具有平台相关性
Codec模块通常与Platfrom模块的硬件是分开的,外接专门codec芯片,提供编解码、混音、DAC、ADC等等功能,此块驱动与平台无关,通过codec_dai与Platfrom的cpu_dai连接,用于数据收发,其传输协议根据cpu_dai的硬件能力来定,比如i2s、pcm、tdm等等
Machine模块则是将dma、cpu和codec三个dai连接在一起,形成一条通路,提供完整的音频数据收发功能;因为这三个dai,每个都可能存在1个或多个
ALSA源码通常位于:kernel/sound/目录下,其子目录soc是各个产商的驱动相关代码;子目录core与产商无关,通用代码Asoc core
cpu_dai通过数字接口dai与codec_dai连接,实际codec内部的编解码、ADC、DAC和音量控制等都是在外部DSP芯片中,但是codec_dai相关的驱动代码在平台侧,这块驱动也是通过I2S等接口与外部dsp芯片通信,传递寄存器地址和指令来执行外部dsp的功能;如AK7738、TAS6424、SAF7759等;但也有平台侧集成了外部audio dsp的功能,如Snapdragon 888、Jacinto 7 DRA829V等
ASoc Core通用结构体
通常在创建如如dma、cpu和codec的dai结构时,各个模块已定义了snd_soc_component_driver变量,他们在向alsa core注册自身时,会在core创建snd_soc_component,一一对应,建立连接,最后在将snd_soc_component加入到一个全局链表,后续需要的时候就从这个全局链表中去找,结构体拓扑图如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-g3G6hjlP-1733753825327)(component.png)]
结构体如下:
//componet组件,可以理解有i2s、adma等component组件
struct snd_soc_component {
const char *name;
int id;
const char *name_prefix;
struct device *dev;
struct snd_soc_card *card;
unsigned int active;
unsigned int ignore_pmdown_time:1; /* pmdown_time is ignored at stop */
unsigned int registered_as_component:1;
unsigned int suspended:1; /* is in suspend PM state */
struct list_head list;
struct list_head card_aux_list; /* for auxiliary bound components */
struct list_head card_list;
struct snd_soc_dai_driver *dai_drv; //指向snd_soc_dai_driver,位于qcom-i2s.c中的qcom_i2s_dai_drv实例中,是cpu_dai
int num_dai;
const struct snd_soc_component_driver *driver; //这个driver位于qcom-i2s.c中的qcom_i2s_component_drv实例中,
/*dai_list保存的是snd_soc_componet的链表,比如当前的componet属于cpu,则这个dai_list对应的是cpu接口相关的snd_soc_dai,每个dai的支持的能力不一样,如单声道、多声道等*/
struct list_head dai_list;
int (*read)(struct snd_soc_component *, unsigned int, unsigned int *);
int (*write)(struct snd_soc_component *, unsigned int, unsigned int);
struct regmap *regmap;
int val_bytes;
struct mutex io_mutex;
/* attached dynamic objects */
struct list_head dobj_list;
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs_root;
#endif
/*
* DO NOT use any of the fields below in drivers, they are temporary and
* are going to be removed again soon. If you use them in driver code the
* driver will be marked as BROKEN when these fields are removed.
*/
/* Don't use these, use snd_soc_component_get_dapm() */
struct snd_soc_dapm_context dapm; //这是实体不是指针
struct snd_soc_codec *codec; //指向codec结构体
int (*probe)(struct snd_soc_component *);
void (*remove)(struct snd_soc_component *);
int (*suspend)(struct snd_soc_component *);
int (*resume)(struct snd_soc_component *);
int (*set_sysclk)(struct snd_soc_component *component,
int clk_id, int source, unsigned int freq, int dir);
int (*set_pll)(struct snd_soc_component *component, int pll_id,
int source, unsigned int freq_in, unsigned int freq_out);
int (*set_jack)(struct snd_soc_component *component,
struct snd_soc_jack *jack, void *data);
/* machine specific init */
int (*init)(struct snd_soc_component *component);
#ifdef CONFIG_DEBUG_FS
void (*init_debugfs)(struct snd_soc_component *component);
const char *debugfs_prefix;
#endif
};
/* 每个component对应的driver,可以是i2s、adma */
struct snd_soc_component_driver {
const char *name;
/* Default control and setup, added after probe() is run */
const struct snd_kcontrol_new *controls;
unsigned int num_controls;
const struct snd_soc_dapm_widget *dapm_widgets;
unsigned int num_dapm_widgets;
const struct snd_soc_dapm_route *dapm_routes;
unsigned int num_dapm_routes;
int (*probe)(struct snd_soc_component *);
void (*remove)(struct snd_soc_component *);
int (*suspend)(struct snd_soc_component *);
int (*resume)(struct snd_soc_component *);
/* component wide operations */
int (*set_sysclk)(struct snd_soc_component *component,
int clk_id, int source, unsigned int freq, int dir);
int (*set_pll)(struct snd_soc_component *component, int pll_id,
int source, unsigned int freq_in, unsigned int freq_out);
int (*set_jack)(struct snd_soc_component *component,
struct snd_soc_jack *jack, void *data);
/* DT */
int (*of_xlate_dai_name)(struct snd_soc_component *component,
struct of_phandle_args *args,
const char **dai_name);
int (*of_xlate_dai_id)(struct snd_soc_component *comment,
struct device_node *endpoint);
void (*seq_notifier)(struct snd_soc_component *, enum snd_soc_dapm_type,
int subseq);
int (*stream_event)(struct snd_soc_component *, int event);
/* probe ordering - for components with runtime dependencies */
int probe_order; //系统启动有个order顺序,probe_order是当前结构体在某个order启动了
int remove_order;
};
//kcontrol主要给用户空间提供可以访问控制音频codec芯片的多路开关接口或组件,如Mixer等
struct snd_kcontrol_new {
snd_ctl_elem_iface_t iface; /* interface identifier类型 */
unsigned int device; /* device/client number */
unsigned int subdevice; /* subdevice (substream) number */
const unsigned char *name; /* ASCII name of item */
unsigned int index; /* index of item 在声卡中的编号*/
unsigned int access; /* access rights 访问类型,按bit位来区分*/
unsigned int count; /* count of same elements */
snd_kcontrol_info_t *info;
snd_kcontrol_get_t *get;
snd_kcontrol_put_t *put;
union {
snd_kcontrol_tlv_rw_t *c;
const unsigned int *p;
} tlv;
unsigned long private_value; /*通过info、get和put可以访问该私有值,可以是一个soc_mixer_control*/
};
/*
* Digital Audio Interface Driver.
*
* Describes the Digital Audio Interface in terms of its ALSA, DAI and AC97
* operations and capabilities. Codec and platform drivers will register this
* structure for every DAI they have.
*
* This structure covers the clocking, formating and ALSA operations for each
* interface.
*/
struct snd_soc_dai_driver {
/* DAI description */
const char *name;
unsigned int id;
unsigned int base;
struct snd_soc_dobj dobj;
/* DAI driver callbacks */
int (*probe)(struct snd_soc_dai *dai);
int (*remove)(struct snd_soc_dai *dai);
int (*suspend)(struct snd_soc_dai *dai);
int (*resume)(struct snd_soc_dai *dai);
/* compress dai */
int (*compress_new)(struct snd_soc_pcm_runtime *rtd, int num);
/* Optional Callback used at pcm creation*/
int (*pcm_new)(struct snd_soc_pcm_runtime *rtd,
struct snd_soc_dai *dai);
/* DAI is also used for the control bus */
bool bus_control;
/* ops */
const struct snd_soc_dai_ops *ops;
const struct snd_soc_cdai_ops *cops;
/* DAI capabilities */
struct snd_soc_pcm_stream capture;
struct snd_soc_pcm_stream playback;
unsigned int symmetric_rates:1;
unsigned int symmetric_channels:1;
unsigned int symmetric_samplebits:1;
/* probe ordering - for components with runtime dependencies */
int probe_order;
int remove_order;
};
struct snd_soc_dai_ops {
/*
* DAI clocking configuration, all optional.
* Called by soc_card drivers, normally in their hw_params.
*/
int (*set_sysclk)(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir);
int (*set_pll)(struct snd_soc_dai *dai, int pll_id, int source,
unsigned int freq_in, unsigned int freq_out);
int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div);
int (*set_bclk_ratio)(struct snd_soc_dai *dai, unsigned int ratio);
/*
* DAI format configuration
* Called by soc_card drivers, normally in their hw_params.
*/
int (*set_fmt)(struct snd_soc_dai *dai, unsigned int fmt);
int (*xlate_tdm_slot_mask)(unsigned int slots,
unsigned int *tx_mask, unsigned int *rx_mask);
int (*set_tdm_slot)(struct snd_soc_dai *dai,
unsigned int tx_mask, unsigned int rx_mask,
int slots, int slot_width);
int (*set_channel_map)(struct snd_soc_dai *dai,
unsigned int tx_num, unsigned int *tx_slot,
unsigned int rx_num, unsigned int *rx_slot);
int (*set_tristate)(struct snd_soc_dai *dai, int tristate);
/*
* DAI digital mute - optional.
* Called by soc-core to minimise any pops.
*/
int (*digital_mute)(struct snd_soc_dai *dai, int mute);
int (*mute_stream)(struct snd_soc_dai *dai, int mute, int stream);
/*
* ALSA PCM audio operations - all optional.
* Called by soc-core during audio PCM operations.
*/
int (*startup)(struct snd_pcm_substream *,
struct snd_soc_dai *);
void (*shutdown)(struct snd_pcm_substream *,
struct snd_soc_dai *);
/*
* NOTE: Commands passed to the trigger function are not necessarily
* compatible with the current state of the dai. For example this
* sequence of commands is possible: START STOP STOP.
* So do not unconditionally use refcounting functions in the trigger
* function, e.g. clk_enable/disable.
*/
int (*trigger)(struct snd_pcm_substream *, int,
struct snd_soc_dai *);
int (*bespoke_trigger)(struct snd_pcm_substream *, int,
struct snd_soc_dai *);
/*
* For hardware based FIFO caused delay reporting.
* Optional.
*/
snd_pcm_sframes_t (*delay)(struct snd_pcm_substream *,
struct snd_soc_dai *);
};
/*
* Digital Audio Interface runtime data.
*
* Holds runtime data for a DAI., 位于snd_soc_pcm_runtime中codec_dais成员中
*/
struct snd_soc_dai {
const char *name; //通常和snd_soc_dai_driver的name一致
int id;
struct device *dev;
/* driver ops */
struct snd_soc_dai_driver *driver;
/* DAI runtime info */
unsigned int capture_active:1; /* stream is in use */
unsigned int playback_active:1; /* stream is in use */
unsigned int symmetric_rates:1;
unsigned int symmetric_channels:1;
unsigned int symmetric_samplebits:1;
unsigned int probed:1;
unsigned int active;
struct snd_soc_dapm_widget *playback_widget;
struct snd_soc_dapm_widget *capture_widget;
/* DAI DMA data */
void *playback_dma_data; //标记了从dma传输过来的数据类型,是单通道还是多通道等等
void *capture_dma_data;
/* Symmetry data - only valid if symmetry is being enforced */
unsigned int rate;
unsigned int channels;
unsigned int sample_bits;
/* parent platform/codec */
struct snd_soc_codec *codec;
struct snd_soc_component *component;
/* CODEC TDM slot masks and params (for fixup) */
unsigned int tx_mask;
unsigned int rx_mask;
struct list_head list; //list_head用于链表,会加入到snd_soc_componet组件的list链表中去管理
};
电源管理结构体
/* dapm widget */
struct snd_soc_dapm_widget {
enum s