在上一篇博客我们介绍了ALSA子系统的软件架构,同时介绍了ALSA CORE核心数据结构和相关API。本节我们将会介绍ASoC软件体系中音频三大驱动模块:Codec、Platform 和Machine。
一、ASoC核心数据结构
我们首先来了解Codec、Platform 和Machine驱动中涉及到的数据结构,知道每个数据结构以及成员的含义之后,再去看源码就容易了。本节介绍的数据结构大部分位于include/sound/soc.h头文件中。
1.1 Machine
ASoC中使用struct snd_soc_card数据结构来描述SoC声卡的所有信息,需要将该数据结构与我们上一节介绍的ALSA CORE中的struct snd_card区分开来;struct snd_soc_card定义在include/sound/soc.h;
/* SoC card */ struct snd_soc_card { const char *name; const char *long_name; const char *driver_name; const char *components; #ifdef CONFIG_DMI char dmi_longname[80]; #endif /* CONFIG_DMI */ char topology_shortname[32]; struct device *dev; struct snd_card *snd_card; struct module *owner; struct mutex mutex; struct mutex dapm_mutex; /* Mutex for PCM operations */ struct mutex pcm_mutex; enum snd_soc_pcm_subclass pcm_subclass; int (*probe)(struct snd_soc_card *card); int (*late_probe)(struct snd_soc_card *card); void (*fixup_controls)(struct snd_soc_card *card); int (*remove)(struct snd_soc_card *card); /* the pre and post PM functions are used to do any PM work before and * after the codec and DAI's do any PM work. */ int (*suspend_pre)(struct snd_soc_card *card); int (*suspend_post)(struct snd_soc_card *card); int (*resume_pre)(struct snd_soc_card *card); int (*resume_post)(struct snd_soc_card *card); /* callbacks */ int (*set_bias_level)(struct snd_soc_card *, struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level); int (*set_bias_level_post)(struct snd_soc_card *, struct snd_soc_dapm_context *dapm, enum snd_soc_bias_level level); int (*add_dai_link)(struct snd_soc_card *, struct snd_soc_dai_link *link); void (*remove_dai_link)(struct snd_soc_card *, struct snd_soc_dai_link *link); long pmdown_time; /* CPU <--> Codec DAI links */ struct snd_soc_dai_link *dai_link; /* predefined links only */ int num_links; /* predefined links only */ struct list_head rtd_list; int num_rtd; /* optional codec specific configuration */ struct snd_soc_codec_conf *codec_conf; int num_configs; /* * optional auxiliary devices such as amplifiers or codecs with DAI * link unused */ struct snd_soc_aux_dev *aux_dev; int num_aux_devs; struct list_head aux_comp_list; const struct snd_kcontrol_new *controls; int num_controls; /* * Card-specific routes and widgets. * Note: of_dapm_xxx for Device Tree; Otherwise for driver build-in. */ const struct snd_soc_dapm_widget *dapm_widgets; int num_dapm_widgets; const struct snd_soc_dapm_route *dapm_routes; int num_dapm_routes; const struct snd_soc_dapm_widget *of_dapm_widgets; int num_of_dapm_widgets; const struct snd_soc_dapm_route *of_dapm_routes; int num_of_dapm_routes; /* lists of probed devices belonging to this card */ struct list_head component_dev_list; struct list_head list; struct list_head widgets; struct list_head paths; struct list_head dapm_list; struct list_head dapm_dirty; /* attached dynamic objects */ struct list_head dobj_list; /* Generic DAPM context for the card */ struct snd_soc_dapm_context dapm; struct snd_soc_dapm_stats dapm_stats; struct snd_soc_dapm_update *update; #ifdef CONFIG_DEBUG_FS struct dentry *debugfs_card_root; #endif #ifdef CONFIG_PM_SLEEP struct work_struct deferred_resume_work; #endif u32 pop_time; /* bit field */ unsigned int instantiated:1; unsigned int topology_shortname_created:1; unsigned int fully_routed:1; unsigned int disable_route_checks:1; unsigned int probed:1; unsigned int component_chaining:1; void *drvdata; };
这个数据结构的内容比较多,我们只挑一些重点说一下:
- name:名称;
- long_name:更详细的名称;
- driver_name:驱动程序的名称;
- components:组件名称;
- dev:分配给此声卡的设备;一般设置为平台设备的device;
- snd_card:ALSA CORE中的声卡设备;
- owner:指向驱动程序拥有者模块的指针;
- pcm_subclass:PCM子类的枚举类型;
- probe:该数据结构注册到内核时调用;
- late_probe:
- remove:
- add_dai_link:
- remove_dai_link:
- dai_link:指向struct snd_soc_dai_link数组,每一元素描述了CPU <--> Codec DAI links;
- num_links:dai_link指向的数组的长度;
- codec_conf:
- aux_dev:
- num_controls:
- num_dapm_widgets:
- num_dapm_routes:
- num_of_dapm_widgets:
- num_of_dapm_routes:
- component_dev_list:
- widgets:DAPM控件的链表;
- paths:DAPM路径的链表;
- dapm_list:
- dapm_dirty:
- drvdata:驱动程序的私有数据结构;
1.1.1 struct snd_soc_dai_link
ASoC使用struct snd_soc_dai_link数据结构来描述音频链路以及板级操作函数,在snd_soc_dai_link中,指定了platform、codec、codec_dai、cpu_dai的名字,Machine驱动将会利用这些名字去匹配已经在系统中注册的platform,codec,dai,这些注册的部件都是在另外相应的Platform驱动和Codec驱动的代码文件中定义的。
struct snd_soc_dai_link定义在include/sound/soc.h:
struct snd_soc_dai_link { /* config - must be set by machine driver */ const char *name; /* Codec name */ const char *stream_name; /* Stream name */ /* * You MAY specify the link's CPU-side device, either by device name, * or by DT/OF node, but not both. If this information is omitted, * the CPU-side DAI is matched using .cpu_dai_name only, which hence * must be globally unique. These fields are currently typically used * only for codec to codec links, or systems using device tree. */ /* * You MAY specify the DAI name of the CPU DAI. If this information is * omitted, the CPU-side DAI is matched using .cpu_name/.cpu_of_node * only, which only works well when that device exposes a single DAI. */ struct snd_soc_dai_link_component *cpus; unsigned int num_cpus; /* * You MUST specify the link's codec, either by device name, or by * DT/OF node, but not both. */ /* You MUST specify the DAI name within the codec */ struct snd_soc_dai_link_component *codecs; unsigned int num_codecs; /* * You MAY specify the link's platform/PCM/DMA driver, either by * device name, or by DT/OF node, but not both. Some forms of link * do not need a platform. In such case, platforms are not mandatory. */ struct snd_soc_dai_link_component *platforms; unsigned int num_platforms; int id; /* optional ID for machine driver link identification */ const struct snd_soc_pcm_stream *params; unsigned int num_params; unsigned int dai_fmt; /* format to set on init */ enum snd_soc_dpcm_trigger trigger[2]; /* trigger type for DPCM */ /* codec/machine specific init - e.g. add machine controls */ int (*init)(struct snd_soc_pcm_runtime *rtd); /* codec/machine specific exit - dual of init() */ void (*exit)(struct snd_soc_pcm_runtime *rtd); /* optional hw_params re-writing for BE and FE sync */ int (*be_hw_params_fixup)(struct snd_soc_pcm_runtime *rtd, struct snd_pcm_hw_params *params); /* machine stream operations */ const struct snd_soc_ops *ops; const struct snd_soc_compr_ops *compr_ops; /* Mark this pcm with non atomic ops */ unsigned int nonatomic:1; /* For unidirectional dai links */ unsigned int playback_only:1; unsigned int capture_only:1; /* Keep DAI active over suspend */ unsigned int ignore_suspend:1; /* Symmetry requirements */ unsigned int symmetric_rate:1; unsigned int symmetric_channels:1; unsigned int symmetric_sample_bits:1; /* Do not create a PCM for this DAI link (Backend link) */ unsigned int no_pcm:1; /* This DAI link can route to other DAI links at runtime (Frontend)*/ unsigned int dynamic:1; /* DPCM capture and Playback support */ unsigned int dpcm_capture:1; unsigned int dpcm_playback:1; /* DPCM used FE & BE merged format */ unsigned int dpcm_merged_format:1; /* DPCM used FE & BE merged channel */ unsigned int dpcm_merged_chan:1; /* DPCM used FE & BE merged rate */ unsigned int dpcm_merged_rate:1; /* pmdown_time is ignored at stop */ unsigned int ignore_pmdown_time:1; /* Do not create a PCM for this DAI link (Backend link) */ unsigned int ignore:1; /* This flag will reorder stop sequence. By enabling this flag * DMA controller stop sequence will be invoked first followed by * CPU DAI driver stop sequence */ unsigned int stop_dma_first:1; #ifdef CONFIG_SND_SOC_TOPOLOGY struct snd_soc_dobj dobj; /* For topology */ #endif };
这个数据结构的内容比较多,我们只挑一些重点说一下:
- name:指定Codec名称,必须配置;
- stream_name:指定Stream名称,必须配置;
- cpus:指定CPU端的数字音频接口(DAI);
- num_cpus:CPU端DAI 数量;
- codecs:指定Codec端DAI;
- num_codecs:Codec端 DAI数量;
- platforms:指定platform驱动程序;
- num_platforms:platform驱动程序数量;
- id:可选的链接 ID,用于识别Machine driver link;
- params:指定PCM流参数;
- num_params:PCM流参数数量;
- dai_fmt:在初始化时设置的音频格式;
- trigger:DPCM(Direct Pulse Code Modulation)触发类型;
- init:初始化函数,例如添加Machine controls;
- exit:退出函数;
- be_hw_params_fixup:可选的硬件参数重写函数;
- ops:流操作函数,这个字段比较重要;
- compr_ops:数据压缩操作函数;
- nonatomic:标记 PCM 是否使用非原子操作;
- playback_only:标记 PCM 流是否只支持播放;
- capture_only:标记 PCM 流是否只支持捕获;
- ignore_suspend:标记 PCM 是否在挂起时保持 DAI 活动状态;
- symmetric_rate:标记 PCM 采样率是否对称;
- symmetric_channels:标记 PCM 通道数是否对称;
- symmetric_sample_bits:标记 PCM 采样位数是否对称;
- no_pcm:标记 PCM 流是否需要创建;
- dynamic:标记该 DAI 链接是否可以在运行时路由到其他 DAI 链接;
- dpcm_capture:标记是否支持 DPCM 捕获;
- dpcm_playback:标记是否支持 DPCM 播放;
- dpcm_merged_format:标记是否使用合并格式的 DPCM;
- dpcm_merged_chan:标记是否使用合并通道的 DPCM;
- dpcm_merged_rate:标记是否使用合并采样率的 DPCM;
- ignore_pmdown_time:标记是否忽略 pmdown_time 停止时间;pmdown_time 是一种 PCM 的停止时间戳,用于控制 PCM 流在空闲一段时间后自动停止以降低功耗;
- ignore:标记该 DAI 链接是否需要创建 PCM;
- stop_dma_first:标记是否对停止序列进行排序;
1.1.2 struct snd_soc_ops
ASoC使用struct snd_soc_ops来描述音频操作集,定义在include/sound/soc.h;
/* SoC audio ops */ struct snd_soc_ops { int (*startup)(struct snd_pcm_substream *); void (*shutdown)(struct snd_pcm_substream *); int (*hw_params)(struct snd_pcm_substream *, struct snd_pcm_hw_params *); int (*hw_free)(struct snd_pcm_substream *); int (*prepare)(struct snd_pcm_substream *); int (*trigger)(struct snd_pcm_substream *, int); }
其中:
- startup:在 PCM子流上启动音频传输期间调用;
- shutdown:在PCM子流上关闭音频传输期间调用;
- hw_params:更改PCM子流硬件参数期间调用;
- hw_free:释放PCM子流资源期间调用;
- prepare:准备PCM子流传输期间调用;
- trigger:在PCM子流上触发执行操作期间调用;
1.1.3 struct snd_soc_dai
ALSA中使用structsnd_soc_dai数据结构来描述DAI运行时数据,定义在include/sound/soc-dai.h:
/* * Digital Audio Interface runtime data. * * Holds runtime data for a DAI. */ struct snd_soc_dai { const char *name; int id; struct device *dev; /* driver ops */ struct snd_soc_dai_driver *driver; /* DAI runtime info */ struct snd_soc_dai_stream stream[SNDRV_PCM_STREAM_LAST + 1]; /* 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_component *component; struct list_head list; /* function mark */ struct snd_pcm_substream *mark_startup; struct snd_pcm_substream *mark_hw_params; struct snd_pcm_substream *mark_trigger; struct snd_compr_stream *mark_compr_startup; /* bit field */ unsigned int probed:1; };
该数据结构包含以下字段:
- name:DAI的名称;
- id:DAI的标识符;
- dev:指向包含DAI的设备的指针;
- driver:指向dai驱动结构的指针;
- stream:采集和播放流的数组,其中包含有关流的信息;
- rate:如果强制对称,则为采样率;
- channels:如果强制对称,则为通道数;
- sample_bits:如果强制对称,则为采样位数;
- component:指向父组件(通常是 platform或codec)的指针;
- list:用于将DAI添加到其父组件的DAI列表中;
- mark_startup:用于标记PCM启动事件的指针;
- mark_hw_params:用于标记PCM硬件参数变化事件的指针;
- mark_trigger:用于标记PCM触发事件的指针;
- mark_compr_startup:用于标记压缩流启动事件的指针;
- probed:标记DAI是否已经探测完成;
1.1.4 struct snd_soc_dai_driver
ALSA中使用structsnd_soc_dai_driver数据结构来描述DAI驱动,定义在include/sound/soc-dai.h:
/* * 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); /* 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); /* 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_rate:1; unsigned int symmetric_channels:1; unsigned int symmetric_sample_bits:1; /* probe ordering - for components with runtime dependencies */ int probe_order; int remove_order; };
该数据结构包含了以下字段:
- name:指定DAI的名称;
- id:可选的 DAI 标识符,用于在注册期间区分多个DAI;
- base:可选的 DAI 寄存器基地址;
- dobj:DAI 对象,包含 DAI 及其父组件的句柄;
- probe:可选的DAI探测回调函数;
- remove:可选的DAI卸载回调函数;
- compress_new:可选的压缩 DAI 创建回调函数;
- pcm_new:可选的 PCM 创建回调函数;
- ops:指向本dai的snd_soc_dai_ops结构;
- cops:DAI 压缩操作函数指针表;
- capture:描述capture的能力;
- playbook:描述playback的能力;
- symmetric_rate:标记 DAI 采样率是否对称;
- symmetric_channels:标记 DAI 通道数是否对称;
- symmetric_sample_bits:标记 DAI 采样位数是否对称;
- probe_order:DAI 探测顺序;
- remove_order:DAI 卸载顺序;
1.1.5 struct snd_soc_dai_ops
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 (*get_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); int (*set_stream)(struct snd_soc_dai *dai, void *stream, int direction); void *(*get_stream)(struct snd_soc_dai *dai, int direction); /* * DAI digital mute - optional. * Called by soc-core to minimise any pops. */ 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 *); int (*hw_params)(struct snd_pcm_substream *, struct snd_pcm_hw_params *, struct snd_soc_dai *); int (*hw_free)(struct snd_pcm_substream *, struct snd_soc_dai *); int (*prepare)(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 *); /* * Format list for auto selection. * Format will be increased if priority format was * not selected. * see * snd_soc_dai_get_fmt() */ u64 *auto_selectable_formats; int num_auto_selectable_formats; /* bit field */ unsigned int no_capture_mute:1; };
1.2 Codec
描述Codec的最主要的几个数据结构分别是:snd_soc_codec,snd_soc_codec_driver,snd_soc_dai,snd_soc_dai_driver,其中:
- snd_soc_dai和snd_soc_dai_driver在ASoC的Platform驱动中也会使用到;我们在Machine中已经介绍过;
- Platform和Codec的DAI通过snd_soc_dai_link结构,在Machine驱动中进行绑定连接。
1.2.1 struct snd_soc_codec
1.2.1 struct snd_soc_codec_driver
音频编解码芯片描述及操作函数,如控件/微件/音频路由的描述信息、时钟配置、IO 控制等.
1.3 Platform
1.3.1 struct snd_soc_platform
1.3.2 struct snd_platform_driver
音频 dma 设备描述及操作函数
二、ASoC核心API
2.1 注册声卡设备
snd_soc_register_card
参考文章