1、platform、codec、machine的关系:
Linux ALSA音频系统:platform,machine,codec - JavaShuo
machine驱动负责Platform和Codec之间的耦合以及部分和设备或板子特定的代码,单独的Platform和Codec驱动是不能工作的,它必须由Machine驱动把它们结合在一起才能完成整个设备的音频处理工作。
注:
machine:机器,codec和platform都是机器下的部件,有把部件整合在一起的意思。
2、DAI:Digital Audio Interfaces,即数字音频接口,如I2S, PCM
3、struct snd_ctl_elem_value结构体:
参考:snd_kcontrol的分析-mychz2011-ChinaUnix博客
linux alsa 音频路径切换_xiaojsj111的博客-CSDN博客_snd_soc_add_codec_controls
4、DAPM
4.1、DAPM简介:
DAPM是Dynamic Audio Power Management的缩写,直译过来就是动态音频电源管理的意思,DAPM是为了使基于linux的移动设备上的音频子系统,在任何时候都工作在最小功耗状态下。DAPM对用户空间的应用程序来说是透明的,所有与电源相关的开关都在ASoc core中完成。DAPM根据当前激活的音频流(playback/capture)和声卡中的mixer等的配置来决定那些音频控件的电源开关被打开或关闭。
4.2、 widget:
音频控件单元抽象为kcontrol,DAPM的基本单元是widget,使用snd_soc_dapm_widget结构表示,它是对kcontrol进一步的封装,是kcontrol和开关(有的是组件前面的Switch和组件组成一个widget,有的是后面的)的组合体。
widget把kcontrol和动态电源管理进行了有机的结合,同时还具备音频路径的连结功能,一个widget可以与它相邻的widget有某种动态的连结关系。
4.3、其它:
参考:Android音频(3)——ALSA声卡驱动——DAPM
08.音频系统:第003课_Linux音频驱动程序:第005节_DAPM_widget_route_path_江南才尽,年少无知!的博客-CSDN博客
文章错误纠正:
LINPUT2改为 LINPUT1
上面说的是sto2,下面说的是sto1,不统一,可以将上面的sto2改成sto1。
“可以看到其使用的红”中的红改成“宏”
重点1:
每个snd_kcontrol_new都填写了自己的寄存器,以及相应的位。可以看到其使用的宏也不一样了,前面都带有了SOC_DAPM。他们的不一样的,主要来自于put函数的不一样,除了设置自己的,还会去设置widgets(这里为混合器)的。
#define SOC_SINGLE(xname, reg, shift, max, invert) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\
.put = snd_soc_put_volsw, \
.private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) \
}
#define SOC_DAPM_SINGLE(xname, reg, shift, max, invert) \
{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
.info = snd_soc_info_volsw, \
.get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
.private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) \
}
注:widget:部件
sink:接收,投到某个地方(这个地方就是接收者)
5、TDM:Time Division Multiplexing 时分复用
Audio 传输接口及数据编码(一)PCM PDM TDM.md_mb5fd869d1d8388的技术博客_51CTO博客
6、ALSA驱动框架:
Linux ALSA驱动框架(一)--ALSA架构简介--声卡的创建_Linux技术芯的博客-CSDN博客_alsa
Linux ALSA声卡驱动之三:component、dai、codec以及platform之间的关系_MOON20704的博客-CSDN博客_snd_soc_register_component
8、代码分析:
8.1、platform驱动,平台驱动包括DMA和CPU_DAI两部分:
8.1.1、ADMA:AUDIO DMA
// sound\soc\tcc\tcc_adma_pcm.c
tcc_adma_pcm_probe
struct tcc_adma_pcm_t *adma_pcm = (struct tcc_adma_pcm_t*)devm_kzalloc(&pdev->dev,
sizeof(struct tcc_adma_pcm_t), GFP_KERNEL);
snd_soc_register_platform(&pdev->dev, &tcc_adma_pcm_platform);
snd_soc_register_platform函数分析:
int snd_soc_register_platform(struct device *dev,const struct snd_soc_platform_driver
*platform_drv) {
struct snd_soc_platform *platform;
platform = kzalloc(sizeof(struct snd_soc_platform), GFP_KERNEL);
ret = snd_soc_add_platform(dev, platform, platform_drv);
}
int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform,const struct
snd_soc_platform_driver *platform_drv) {
snd_soc_component_initialize(&platform->component,&platform_drv->component_driver,dev);
platform->dev = dev;
platform->driver = platform_drv;
snd_soc_component_add_unlocked(&platform->component); //platform对应的component部件
list_add(&platform->list, &platform_list);
}
8.1.2、cpu_dai: 设置I2S,I2S的配置在CPU端
// sound\soc\tcc\tcc_i2s.c
tcc_i2s_probe
devm_snd_soc_register_component(&pdev->dev, &tcc_i2s_component_drv,
&tcc_i2s_dai_drv[i2s->block_type], 1);
int devm_snd_soc_register_component(struct device *dev,const struct
snd_soc_component_driver *cmpnt_drv,struct snd_soc_dai_driver *dai_drv,
int num_dai){
snd_soc_register_component(dev, cmpnt_drv, dai_drv, num_dai);
}
int snd_soc_register_component(struct device *dev,const struct snd_soc_component_driver
*component_driver,struct snd_soc_dai_driver *dai_drv,int num_dai){
struct snd_soc_component *component; //cpu_dai对应的component部件
component = kzalloc(sizeof(*component), GFP_KERNEL);
snd_soc_component_initialize(component, component_driver, dev);
snd_soc_register_dais(component, dai_drv, num_dai, true);
snd_soc_component_add(component);
}
void snd_soc_component_add(struct snd_soc_component *component)
snd_soc_component_add_unlocked(component);
8.2、codec驱动,包括codec_dai
ak7738_spi_probe
ak7738->regmap = devm_regmap_init_spi(spi, &ak7738_regmap);
snd_soc_register_codec(&spi->dev,&soc_codec_dev_ak7738, &ak7738_dai[0],
ARRAY_SIZE(ak7738_dai));
devm_regmap_init_spi函数阅读:Linux regmap子系统分析之一 系统概述_shp1234的博客-CSDN博客
snd_soc_register_codec函数分析:
int snd_soc_register_codec(struct device *dev, const struct snd_soc_codec_driver
*codec_drv,struct snd_soc_dai_driver *dai_drv,int num_dai){
struct snd_soc_codec *codec;
struct snd_soc_dai *dai;
// create codec and codec include component,init component in codec below
codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL);
codec->component.codec = codec; //codec 对应的component部件
ret = snd_soc_component_initialize(&codec->component,&codec_drv->component_driver, dev);
codec->dev = dev;
codec->driver = codec_drv;
ret = snd_soc_register_dais(&codec->component, dai_drv, num_dai, false);
list_for_each_entry(dai, &codec->component.dai_list, list)
dai->codec = codec;
snd_soc_component_add_unlocked(&codec->component);
list_add(&codec->list, &codec_list);
}
snd_soc_component_initialize函数:
int snd_soc_component_initialize(struct snd_soc_component *component,
const struct snd_soc_component_driver *driver, struct device *dev){
component->dev = dev;
component->driver = driver;
component->probe = component->driver->probe;
component->remove = component->driver->remove;
INIT_LIST_HEAD(&component->dai_list);
}
snd_soc_register_dais函数:
int snd_soc_register_dais(struct snd_soc_component *component,
struct snd_soc_dai_driver *dai_drv, size_t count,bool legacy_dai_naming){
struct device *dev = component->dev;
struct snd_soc_dai *dai;
component->dai_drv = dai_drv;
for (i = 0; i < count; i++) {
dai = soc_add_dai(component, dai_drv + i,count == 1 && legacy_dai_naming);
}
}
soc_add_dai函数:
struct snd_soc_dai *soc_add_dai(struct snd_soc_component *component,
struct snd_soc_dai_driver *dai_drv,bool legacy_dai_naming){
struct device *dev = component->dev;
struct snd_soc_dai *dai;
dai = kzalloc(sizeof(struct snd_soc_dai), GFP_KERNEL);
dai->name = fmt_single_name(dev, &dai->id);
dai->component = component;
dai->dev = dev;
dai->driver = dai_drv;
list_add(&dai->list, &component->dai_list);
component->num_dai++;
}
snd_soc_component_add_unlocked函数,添加component。本驱动共有5个component:2个platform,2个cpu_dai,1个codec,详见设备树sound节点,至此,5个component的都已出现并添加完成,现在看看这个函数都干了什么。
static void snd_soc_component_add_unlocked(struct snd_soc_component *component)
{
if (!component->write && !component->read) {
//获取设备对应的regmap
if (!component->regmap)
component->regmap = dev_get_regmap(component->dev, NULL);
}
list_add(&component->list, &component_list);
}
struct regmap *dev_get_regmap(struct device *dev, const char *name)
{
struct regmap **r = devres_find(dev, dev_get_regmap_release,
dev_get_regmap_match, (void *)name);
return *r;
}
devres驱动阅读:devres驱动笔记_shp1234的博客-CSDN博客
8.3、machine驱动:
// soc/tcc/tcc-snd-card.c
tcc_snd_card_probe
struct snd_soc_card *card;
card = kzalloc(sizeof(struct snd_soc_card), GFP_KERNEL);
card->dev = &pdev->dev;
parse_tcc_snd_card_dt(pdev, card);
tcc_snd_card_kcontrol_init(card);
snd_soc_register_card(card);
int parse_tcc_snd_card_dt(struct platform_device *pdev, struct snd_soc_card *card)
{
struct device_node *node = pdev->dev.of_node;
struct tcc_card_info_t *card_info;
struct device_node *dai_link;
ssize_t alloc_size;
card_info = kzalloc(sizeof(struct tcc_card_info_t), GFP_KERNEL);
card_info->num_links = of_get_child_count(node); // child_node number
alloc_size = sizeof(struct snd_soc_dai_link) * card_info->num_links;
card_info->dai_link = kzalloc((size_t)alloc_size, GFP_KERNEL);
{
struct device_node *np;
int cnt = 0;
for_each_child_of_node((node), (np)) {
if (cnt < card_info->num_links) {
//遍历该节点下的所有dai_link子节点,并填充到struct snd_soc_dai_link结构体中
tcc_snd_card_sub_dai_link(np, &card_info->dai_link[cnt],
&card_info->dai_info[cnt]);
cnt++;
} else {
break;
}
}
}
}
snd_soc_dai_link结构体定义:
struct snd_soc_dai_link {
/* config - must be set by machine driver */
const char *name; /* Codec name */
const char *stream_name; /* Stream name */
const char *cpu_name;
struct device_node *cpu_of_node;
const char *cpu_dai_name;
const char *codec_name;
struct device_node *codec_of_node;
const char *codec_dai_name;
struct snd_soc_dai_link_component *codecs;
unsigned int num_codecs;
const char *platform_name;
struct device_node *platform_of_node;
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 */
const struct snd_soc_ops *ops;
......
struct list_head list; /* DAI link list of the soc card */
struct snd_soc_dobj dobj; /* For topology */
};
重点介绍如下几个字段:
codec_name:音频链路需要绑定的codec名称,声卡注册时会遍历codec_list,找到同名的codec并绑定;
platform_name:音频链路需要绑定的platform名称,声卡注册时会遍历platform_list,找到同名的platform并绑定;
cpu_dai_name:音频链路需要绑定的cpu_dai名称,声卡注册时会遍历dai_list,找到同名的dai并绑定;
ops:重点留意hw_params()回调,一般来说这个回调是要实现的,用于配codec,codec_dai,cpu_dai的数据格式和系统时钟。
tcc_snd_card_sub_dai_link函数:
遍历该节点下的所有dai_link子节点,并填充到struct snd_soc_dai_link结构体中。
int tcc_snd_card_sub_dai_link(struct device_node *node,
struct snd_soc_dai_link *dai_link,struct tcc_dai_info_t *dai_info){
struct device_node *platform_of_node;
struct device_node *dai_of_node;
struct device_node *codec_of_node;
platform_of_node = of_parse_phandle(node, "pcm", 0);
dai_of_node = of_parse_phandle(node, "dai", 0);
codec_of_node = of_parse_phandle(node, "codec", 0);
of_property_read_string(node, "stream-name", &dai_link->name);
of_property_read_string(node, "stream-name", &dai_link->stream_name);
of_property_read_string(node, "codec,dai-name", &dai_link->codec_dai_name);
dai_link->cpu_of_node = dai_of_node;
dai_link->platform_of_node = platform_of_node;
dai_link->codec_of_node = codec_of_node;
}
回到 tcc_snd_card_probe的tcc_snd_card_kcontrol_init函数,该函数旨在初始化machine中的所有控件。因为有2个dai-link,所以控件数量要乘2。
static const struct snd_kcontrol_new tcc_snd_controls[] = { //cpu_dai
SOC_ENUM_EXT(("DAI FORMAT"), (dai_format_enum[0]), (get_dai_format), (set_dai_format)),
SOC_ENUM_EXT(("DAI CLKINV"), (dai_clkinv_enum[0]), (get_dai_clkinv), (set_dai_clkinv)),
SOC_ENUM_EXT(("TDM Slots"), (tdm_slots_enum[0]), (get_tdm_slots), (set_tdm_slots)),
SOC_ENUM_EXT(("TDM Slot Width"), (tdm_width_enum[0]), (get_tdm_width), (set_tdm_width)),
SOC_SINGLE_BOOL_EXT(("Clock Continuous Mode"), (0), (get_continuous_clk_mode), (set_continous_clk_mode)),
};
int tcc_snd_card_kcontrol_init(struct snd_soc_card *card){
struct tcc_card_info_t *card_info = snd_soc_card_get_drvdata(card);
//num_links_no_tsnd:不是T-sound设备的链路个数,这里的两个dai-link都不是
int32_t num_controls, num_links_no_tsnd=0, offset_no_tsnd=0;
struct snd_kcontrol_new *controls;
for (i=0; i<card_info->num_links; i++) {
num_links_no_tsnd ++; //num_links_no_tsnd=2
}
//2条dai_link
num_controls = TCC_AUDIO_ARRAY_SIZE(tcc_snd_controls) * num_links_no_tsnd;
alloc_size = sizeof(struct snd_kcontrol_new) * num_controls;
controls = kzalloc(alloc_size, GFP_KERNEL);
//cpu_dai
alloc_size = sizeof(struct snd_kcontrol_new)* TCC_AUDIO_ARRAY_SIZE(tcc_snd_controls);
for (i=0; i<card_info->num_links; i++) {
//offset_no_tsnd先后取值为0、1,
//memcpy复制的目的地址先后分别是&controls[0]、
//&controls[TCC_AUDIO_ARRAY_SIZE(tcc_snd_controls)]
memcpy(&controls[(offset_no_tsnd*TCC_AUDIO_ARRAY_SIZE(tcc_snd_controls))],
tcc_snd_controls,alloc_size);
for (j=0; j<TCC_AUDIO_ARRAY_SIZE(tcc_snd_controls); j++) {
char tmp_name[255];
int offset_controls=
(int(offset_no_tsnd*TCC_AUDIO_ARRAY_SIZE(tcc_snd_controls));
//KCONTROL_HDR="Device"
(void) sprintf(tmp_name, KCONTROL_HDR"%d %s",
i,controls[offset_controls+j].name);
controls[offset_controls+j].name = kstrdup(tmp_name, GFP_KERNEL);
}
offset_no_tsnd ++;
}
/*注意:struct snd_soc_card和struct snd_card的controls的不同*/
card->controls = controls;
card->num_controls = num_controls;
}
snd_soc_register_card函数:
int snd_soc_register_card(struct snd_soc_card *card){
//初始化声卡中每个dai_link中codeccodec对应的struct snd_soc_dai_link_component结构体变量
for (i = 0; i < card->num_links; i++) {
struct snd_soc_dai_link *link = &card->dai_link[i];
ret = soc_init_dai_link(card, link);
}
snd_soc_initialize_card_lists(card);
ret = snd_soc_instantiate_card(card);
INIT_LIST_HEAD(&card->rtd_list);
card->num_rtd = 0;
}
static int soc_init_dai_link(struct snd_soc_card *card,struct snd_soc_dai_link *link){
ret = snd_soc_init_multicodec(card, link);
}
/************************************************************/
/*struct snd_soc_dai_link_component { */
/* const char *name; */
/* struct device_node *of_node; */
/* const char *dai_name; */
/*}; */
/************************************************************/
snd_soc_init_multicodec函数:初始化dai_link中codec对应的struct snd_soc_dai_link_component结构体变量。
int snd_soc_init_multicodec(struct snd_soc_card *card,struct snd_soc_dai_link *dai_link){
dai_link->num_codecs = 1;
dai_link->codecs = devm_kzalloc(card->dev,sizeof(struct
snd_soc_dai_link_component),GFP_KERNEL);
dai_link->codecs[0].name = dai_link->codec_name;
dai_link->codecs[0].of_node = dai_link->codec_of_node;
dai_link->codecs[0].dai_name = dai_link->codec_dai_name;
}
void snd_soc_initialize_card_lists(struct snd_soc_card *card){
INIT_LIST_HEAD(&card->aux_comp_list);
}
int snd_soc_instantiate_card(struct snd_soc_card *card){
for (i = 0; i < card->num_links; i++) {
//为每个dai_link分配一个对应的pcm设备运行时rtd,并初始化该rtd:
//将dai_link中的cpu_dai、codec_dai、codec、platform分别赋值给rtd,
//并将cpu_dai、codec、platform对应的component分别加入到rtd的component链表中。
ret = soc_bind_dai_link(card, &card->dai_link[i]);
}
for (i = 0; i < card->num_links; i++)
//将dai_link添加到card的dai_link_list链表里
snd_soc_add_dai_link(card, card->dai_link+i);
//#define SNDRV_DEFAULT_IDX1 (-1)
//#define SNDRV_DEFAULT_STR1 NULL
//新建一个struct snd_card对象,并通过snd_ctl_create函数创建了一个
//控制类型的snd_device,并将其加入到声卡的设备链表下
snd_card_new(card->dev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,card->owner, 0,
&card->snd_card);
for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;order++) {
/*rtd_list链表下有两个rtd对象*/
list_for_each_entry(rtd, &card->rtd_list, list)
//对rtd下的每个component初始化,包括dapm_widget、dai、controls、route等。
//定义的所有控件添加到card链表中
ret = soc_probe_link_components(card, rtd, order);
}
for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;order++) {
list_for_each_entry(rtd, &card->rtd_list, list) {
//对rtd里的cpu_dai、codec_dai调用probe函数初始化,初始化完成后创建pcm设备
ret = soc_probe_link_dais(card, rtd, order);
}
}
snd_soc_dapm_new_widgets(card);
//注册card链表下的所有snd_device
snd_card_register(card->snd_card);
}
/*************************************************************************************/
/* SoC machine DAI configuration, glues a codec and cpu DAI together *****************/
/* soc machine级的DAI配置,将codec和cpu的DAI绑定在一起*/
/* rtd:运行中的pcm设备,含有device结构体*/
/*struct snd_soc_pcm_runtime { *****************/
/* struct device *dev; *****************/
/* struct snd_soc_card *card; *****************/
/* struct snd_soc_dai_link *dai_link; *****************/
/* struct snd_pcm_ops ops; *****************/
/* struct snd_pcm *pcm; *****************/
/* struct snd_compr *compr; *****************/
/* struct snd_soc_codec *codec; *****************/
/* struct snd_soc_platform *platform; /* will be removed */ *****************/
/* struct snd_soc_dai *codec_dai; *****************/
/* struct snd_soc_dai *cpu_dai; *****************/
/* struct snd_soc_dai **codec_dais; *****************/
/* unsigned int num_codecs; *****************/
/* unsigned int num; *****************/
/* struct list_head list; *****************/
/* struct list_head component_list; *****************/
/*}; *****************/
/*************************************************************************************/
soc_bind_dai_link函数:为每个dai_link分配一个对应的pcm设备运行时rtd,并初始化该rtd:将dai_link中的cpu_dai、codec_dai、codec、platform分别赋值给rtd,并将cpu_dai、codec、platform对应的component分别加入到rtd的component链表中。
int soc_bind_dai_link(struct snd_soc_card *card,struct snd_soc_dai_link *dai_link)
/*rtd:运行中的pcm设备,d:device*/
struct snd_soc_pcm_runtime *rtd;
/*代表codecs的struct snd_soc_dai_link_component*/
struct snd_soc_dai_link_component *codecs = dai_link->codecs;
/*代表cpu_dai的struct snd_soc_dai_link_component*/
struct snd_soc_dai_link_component cpu_dai_component;
struct snd_soc_component *component;
struct snd_soc_dai **codec_dais;
/*断card里已有的pcm运行时(一个pcm运行时对应一个pcm设备)中是否已经包含这个dai_link*/
if (soc_is_dai_link_bound(card, dai_link)) {
dev_dbg(card->dev, "ASoC: dai link %s already bound\n",dai_link->name);
return 0;
}
/*新建一个pcm设备运行时,一个pcm设备运行时对应一个dai_link*/
rtd = soc_new_pcm_runtime(card, dai_link);
/*初始化代表cpu_dai的struct snd_soc_dai_link_component*/
cpu_dai_component.name = dai_link->cpu_name;
cpu_dai_component.of_node = dai_link->cpu_of_node;
cpu_dai_component.dai_name = dai_link->cpu_dai_name;
rtd->cpu_dai = snd_soc_find_dai(&cpu_dai_component);
/*将dai_link里的cpu_dai对应的component加入rtd的component队列里*/
/*rtdcom:rtd里component队列*/
snd_soc_rtdcom_add(rtd, rtd->cpu_dai->component);
//dai_link->num_codecs等于1,看snd_soc_init_multicodec函数
rtd->num_codecs = dai_link->num_codecs;
codec_dais = rtd->codec_dais;
for (i = 0; i < rtd->num_codecs; i++) {
//一个dai_link包含一个cpu_dai,一个codec,一个codec_dai,以及一个platform(dma),
//codecs数组长度为1,看snd_soc_init_multicodec函数
codec_dais[i] = snd_soc_find_dai(&codecs[i]);
/*一个codec有多个codec_dai,但是这些codec_dai的component都指向一个共同的component,*/
/*即codec对应的component,另外一个dai_link只包含一个codec_dai*/
/*将dai_link里的codec_dai对应的component加入rtd*/
snd_soc_rtdcom_add(rtd, codec_dais[i]->component);
}
rtd->codec_dai = codec_dais[0];
rtd->codec = rtd->codec_dai->codec;
list_for_each_entry(component, &component_list, list) {
platform_of_node = component->dev->of_node;
if (dai_link->platform_of_node) {
if (platform_of_node != dai_link->platform_of_node)
continue;
}
/*将dai_link里的platform对应的component加入rtd*/
snd_soc_rtdcom_add(rtd, component);
}
list_for_each_entry(platform, &platform_list, list) {
platform_of_node = platform->dev->of_node;
if (dai_link->platform_of_node) {
if (platform_of_node != dai_link->platform_of_node)
continue;
}
rtd->platform = platform;
}
/*添加pcm设备运行时到rtd_list队列里*/
soc_add_pcm_runtime(card, rtd);
soc_is_dai_link_bound函数判断card里已有的pcm运行时(一个pcm运行时对应一个pcm设备)中是否已经包含这个dai_link。一个pcm设备对应dai_link。
/*一个rtd绑定一个dai_link*/
bool soc_is_dai_link_bound(struct snd_soc_card *card,struct snd_soc_dai_link *dai_link){
struct snd_soc_pcm_runtime *rtd;
list_for_each_entry(rtd, &card->rtd_list, list) {
if (rtd->dai_link == dai_link)
return true;
}
return false;
}
soc_new_pcm_runtime函数:新建一个pcm设备运行时。
struct snd_soc_pcm_runtime *soc_new_pcm_runtime(struct snd_soc_card *card,
struct snd_soc_dai_link *dai_link){
struct snd_soc_pcm_runtime *rtd;
rtd = kzalloc(sizeof(struct snd_soc_pcm_runtime), GFP_KERNEL);
INIT_LIST_HEAD(&rtd->component_list);
rtd->card = card;
rtd->dai_link = dai_link;
rtd->codec_dais = kzalloc(sizeof(struct snd_soc_dai *) *
dai_link->num_codecs,GFP_KERNEL);
}
snd_soc_find_dai函数:查找dai,先是根据of_node节点、字符串名查找component组件,找到组件后再在该component的组件内根据dai的字符串名查找dai。一个component组件可能含有多个dai,如codec组件有多个AIF。
struct snd_soc_dai *snd_soc_find_dai(const struct snd_soc_dai_link_component *dlc)
{
struct snd_soc_component *component;
struct snd_soc_dai *dai;
struct device_node *component_of_node;
list_for_each_entry(component, &component_list, list) {
component_of_node = component->dev->of_node;
if (dlc->of_node && component_of_node != dlc->of_node)
continue;
if (dlc->name && strcmp(component->name, dlc->name))
continue;
list_for_each_entry(dai, &component->dai_list, list) {
if (dlc->dai_name && strcmp(dai->name, dlc->dai_name)
&& (!dai->driver->name || strcmp(dai->driver->name, dlc->dai_name)))
continue;
return dai;
}
}
}
snd_soc_rtdcom_add函数:将dai_link里的cpu_dai对应的component加入rtd的component队列里。
static int snd_soc_rtdcom_add(struct snd_soc_pcm_runtime *rtd,
struct snd_soc_component *component){
struct snd_soc_rtdcom_list *rtdcom;
struct snd_soc_rtdcom_list *new_rtdcom;
for_each_rtdcom(rtd, rtdcom) {
/* already connected */
if (rtdcom->component == component)
return 0;
}
new_rtdcom = kmalloc(sizeof(*new_rtdcom), GFP_KERNEL);
new_rtdcom->component = component;
INIT_LIST_HEAD(&new_rtdcom->list);
list_add_tail(&new_rtdcom->list, &rtd->component_list);
}
/*rtd的component_list链表里有三个component对象*/
#define for_each_rtdcom(rtd, rtdcom) \
list_for_each_entry(rtdcom, &(rtd)->component_list, list)
soc_add_pcm_runtime函数:添加pcm设备运行时到声卡的rtd_list队列里。
static void soc_add_pcm_runtime(struct snd_soc_card *card,struct snd_soc_pcm_runtime *rtd)
{
list_add_tail(&rtd->list, &card->rtd_list);
/*rtd_list链表下有两个rtd对象*/
rtd->num = card->num_rtd;
card->num_rtd++;
}
snd_soc_add_dai_link函数:将dai_link添加到card的dai_link_list队列里。
int snd_soc_add_dai_link(struct snd_soc_card *card,struct snd_soc_dai_link *dai_link)
{
if (dai_link->dobj.type && card->add_dai_link)
card->add_dai_link(card, dai_link);
list_add_tail(&dai_link->list, &card->dai_link_list);
card->num_dai_links++;
}
snd_card_new函数:新建一个struct snd_card对象,并通过snd_ctl_create函数把自己注册成一个控制类型的snd_device,并将其加入到声卡的设备链表下。
int snd_card_new(struct device *parent, int idx, const char *xid,struct module *module,
int extra_size,struct snd_card **card_ret){
struct snd_card *card;
card = kzalloc(sizeof(*card) + extra_size, GFP_KERNEL);
if(extra_size > 0)
card->private_data = (char *)card + sizeof(struct snd_card);
card->dev = parent;
card->module = module;
INIT_LIST_HEAD(&card->devices);
INIT_LIST_HEAD(&card->controls);
card->card_dev.parent = parent;
card->card_dev.class = sound_class;
kobject_set_name(&card->card_dev.kobj, "card%d", idx);
snd_ctl_create(card);
//card_ret二维指针获取一维指针
*card_ret = card;
}
snd_ctl_create函数:创建声卡下的控制类声音设备。
int snd_ctl_create(struct snd_card *card)
{
static struct snd_device_ops ops = {
.dev_free = snd_ctl_dev_free,
//注册控制类型的设备都是通过该函数
.dev_register = snd_ctl_dev_register,
.dev_disconnect = snd_ctl_dev_disconnect,
};
dev_set_name(&card->ctl_dev, "controlC%d", card->number);
//SNDRV_DEV_CONTROL表示是控制类型的设备,ops是控制类型的函数操作集,
//将声卡自身注册成控制类型的设备,并挂到自己的device链表上
snd_device_new(card, SNDRV_DEV_CONTROL, card, &ops);
}
snd_ctl_dev_register函数:注册控件类型的设备节点
int snd_ctl_dev_register(struct snd_device *device)
{
struct snd_card *card = device->device_data;
return snd_register_device(SNDRV_DEVICE_TYPE_CONTROL, card, -1,&snd_ctl_f_ops,
card, &card->ctl_dev);
}
static const struct file_operations snd_ctl_f_ops =
{
.owner = THIS_MODULE,
.read = snd_ctl_read,
.open = snd_ctl_open,
.release = snd_ctl_release,
.llseek = no_llseek,
.poll = snd_ctl_poll,
.unlocked_ioctl = snd_ctl_ioctl,
.compat_ioctl = snd_ctl_ioctl_compat,
.fasync = snd_ctl_fasync,
};
snd_device_new函数:创建声音设备,并按type值排序。
int snd_device_new(struct snd_card *card, enum snd_device_type type,void *device_data,
struct snd_device_ops *ops)
{
struct snd_device *dev;
struct list_head *p;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
INIT_LIST_HEAD(&dev->list);
dev->card = card;
dev->type = type;
dev->state = SNDRV_DEV_BUILD;
dev->device_data = device_data;
//不同类型的snd_device对应的ops也不同
dev->ops = ops;
/*从尾部往头遍历*/
list_for_each_prev(p, &card->devices) {
struct snd_device *pdev = list_entry(p, struct snd_device, list);
if ((unsigned int)pdev->type <= (unsigned int)type)
break;
}
/*dev->list插到p后面*/
/*如type=2,链表是1,3,5,7,从尾部往前遍历,所以插入之后链表是1,2,3,5,7*/
list_add(&dev->list, p);
}
/*设备属性都放在device_data下*/
struct snd_device {
struct list_head list; /* list of registered devices */
struct snd_card *card; /* card which holds this device:拥有这个设备的声卡 */
enum snd_device_state state; /* state of the device */
enum snd_device_type type; /* device type */
void *device_data; /* device structure */
struct snd_device_ops *ops; /* operations */
};
soc_probe_link_components函数:对rtd下的每个component初始化,包括dapm_widget、dai、controls、route等。
int soc_probe_link_components(struct snd_soc_card *card,struct snd_soc_pcm_runtime *rtd,
int order)
{
/*每个rtd对象对应一个dai_link里的三个component(codec、cpu_dai、platform)*/
for_each_rtdcom(rtd, rtdcom) {
component = rtdcom->component;
/*所有component->driver->probe_order=0*/
if (component->driver->probe_order == order) {
/*5个component(一个codec,两个cpu_dai,两个platform)依次执行了一遍*/
ret = soc_probe_component(card, component);
}
}
}
soc_probe_component函数:初始化component下的所有组成元素,包括dapm_widget、dai、controls、route等。
/*5个component(一个codec,两个cpu_dai,两个platform)依次执行了一遍*/
int soc_probe_component(struct snd_soc_card *card,struct snd_soc_component *component){
try_module_get(component->dev->driver->owner);
component->card = card;
if (component->driver->dapm_widgets) {
//逐一对widget指向的数组重新拷贝创建、初始化并加入到card的widget链表
snd_soc_dapm_new_controls(dapm,component->driver->dapm_widgets,
component->driver->num_dapm_widgets);
}
list_for_each_entry(dai, &component->dai_list, list) {
//创建dai widget,一个dai中含有两个流:playback和capture,
//一个流对应一个widget,stream名就是widget名
snd_soc_dapm_new_dai_widgets(dapm, dai);
}
if (component->probe) {
ret = component->probe(component);
}
//component:codec、cpu_dai、platform,在这里获取这些驱动里的定义controls控件
if (component->driver->controls)
snd_soc_add_component_controls(component,component->driver->controls,
component->driver->num_controls);
if (component->driver->dapm_routes)
snd_soc_dapm_add_routes(dapm,component->driver->dapm_routes,
component->driver->num_dapm_routes);
}
snd_soc_dapm_new_controls函数:逐一对widget指向的数组重新拷贝创建、初始化并加入到card的widget链表。
int snd_soc_dapm_new_controls(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_widget *widget,int num)
{
struct snd_soc_dapm_widget *w;
int i;
int ret = 0;
for (i = 0; i < num; i++) {
//widget指针指向struct snd_soc_dapm_widget结构的数组
w = snd_soc_dapm_new_control_unlocked(dapm, widget);
widget++;
}
}
snd_soc_dapm_new_control_unlocked函数:根据传入的widget重新拷贝创建一份、初始化并加入到card的widget链表;另外需要注意power_check回调函数、endpoints数组初始化。
struct snd_soc_dapm_widget * snd_soc_dapm_new_control_unlocked(
struct snd_soc_dapm_context *dapm,const struct snd_soc_dapm_widget *widget)
{
enum snd_soc_dapm_direction dir;
struct snd_soc_dapm_widget *w;
const char *prefix;
int ret;
//根据传入的widget重新拷贝创建一份
w = dapm_cnew_widget(widget);
......
w->name = kstrdup_const(widget->name, GFP_KERNEL);
switch (w->id) {
case snd_soc_dapm_mic:
w->is_ep = SND_SOC_DAPM_EP_SOURCE;
w->power_check = dapm_generic_check_power;
break;
case snd_soc_dapm_input:
w->is_ep = SND_SOC_DAPM_EP_SOURCE;
w->power_check = dapm_generic_check_power;
break;
case snd_soc_dapm_spk:
case snd_soc_dapm_hp:
w->is_ep = SND_SOC_DAPM_EP_SINK;
w->power_check = dapm_generic_check_power;
break;
case snd_soc_dapm_output:
w->is_ep = SND_SOC_DAPM_EP_SINK;
w->power_check = dapm_generic_check_power;
break;
case snd_soc_dapm_mux:
case snd_soc_dapm_mixer:
case snd_soc_dapm_adc:
case snd_soc_dapm_aif_out:
case snd_soc_dapm_dac:
case snd_soc_dapm_aif_in:
case snd_soc_dapm_pga:
case snd_soc_dapm_line:
w->power_check = dapm_generic_check_power;
break;
case snd_soc_dapm_kcontrol:
w->is_supply = 1;
w->power_check = dapm_supply_check_power;
break;
default:
w->power_check = dapm_always_on_check_power;
break;
}
w->dapm = dapm;
list_add_tail(&w->list, &dapm->card->widgets);
snd_soc_dapm_for_each_direction(dir) {
INIT_LIST_HEAD(&w->edges[dir]);
w->endpoints[dir] = -1;
}
w->connected = 1;
return w;
}
dapm_cnew_widget:根据传入的widget重新拷贝创建一份
static inline struct snd_soc_dapm_widget *dapm_cnew_widget(
const struct snd_soc_dapm_widget *_widget)
{
return kmemdup(_widget, sizeof(*_widget), GFP_KERNEL);
}
snd_soc_dapm_new_dai_widgets函数:创建dai widget,一个dai中含有两个流:playback和capture,一个流对应一个widget,stream名就是widget名。
int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
struct snd_soc_dai *dai)
{
//template模板,所有dai作为widget都是一样的,所以只要定义一个模板就行
struct snd_soc_dapm_widget template;
struct snd_soc_dapm_widget *w;
memset(&template, 0, sizeof(template));
template.reg = SND_SOC_NOPM;
//一个dai中含有两个流:playback和capture,一个流对应一个widget
//playback流对应的widget
if (dai->driver->playback.stream_name) {
template.id = snd_soc_dapm_dai_in;
//stream名就是widget名
template.name = dai->driver->playback.stream_name;
template.sname = dai->driver->playback.stream_name;
w = snd_soc_dapm_new_control_unlocked(dapm, &template);
w->priv = dai;
dai->playback_widget = w;
}
//capture流对应的widget
if (dai->driver->capture.stream_name) {
template.id = snd_soc_dapm_dai_out;
template.name = dai->driver->capture.stream_name;
template.sname = dai->driver->capture.stream_name;
w = snd_soc_dapm_new_control_unlocked(dapm, &template);
w->priv = dai;
dai->capture_widget = w;
}
return 0;
}
snd_soc_add_component_controls函数:通过component部件分别把codec、cpu_dai、platform驱动里定义的控件统统添加到card的controls的链表中
int snd_soc_add_component_controls(struct snd_soc_component *component,const struct snd_kcontrol_new *controls, unsigned int num_controls)
struct snd_card *card = component->card->snd_card;
return snd_soc_add_controls(card, component->dev, controls,num_controls, component->name_prefix, component);
int snd_soc_add_controls(struct snd_card *card, struct device *dev,
const struct snd_kcontrol_new *controls,
int num_controls,const char *prefix, void *data){
for (i = 0; i < num_controls; i++) {
const struct snd_kcontrol_new *control = &controls[i];
//先通过该控件的id查找链表中是否有该控件,有的话直接返回,
//没有的话加入到card的controls的链表中
err = snd_ctl_add(card, snd_soc_cnew(control, data,control->name, prefix));
}
}
snd_ctl_add函数:先通过该控件的id查找链表中是否有该控件,有的话直接返回,没有的话加入到card的controls的链表中。
int snd_ctl_add(struct snd_card *card, struct snd_kcontrol *kcontrol){
struct snd_ctl_elem_id id;
id = kcontrol->id;
//通过id查找控件,找到返回该控件,找不到返回NULL
if (snd_ctl_find_id(card, &id)) {
return -EBUSY;
}
list_add_tail(&kcontrol->list, &card->controls);
}
snd_ctl_find_id函数:通过id查找控件,找到返回该控件,找不到返回NULL。
struct snd_kcontrol *snd_ctl_find_id(struct snd_card *card,struct snd_ctl_elem_id *id)
{
struct snd_kcontrol *kctl;
list_for_each_entry(kctl, &card->controls, list) {
if (kctl->id.iface != id->iface)
continue;
if (kctl->id.device != id->device)
continue;
if (kctl->id.subdevice != id->subdevice)
continue;
if (strncmp(kctl->id.name, id->name, sizeof(kctl->id.name)))
continue;
if (kctl->id.index > id->index)
continue;
if (kctl->id.index + kctl->count <= id->index)
continue;
return kctl;
}
return NULL;
}
snd_soc_dapm_add_routes函数:详细看 audio子系统笔记(二)_shp1234的博客-CSDN博客
soc_probe_link_dais函数:对rtd里的cpu_dai、codec_dai调用probe函数初始化,初始化完成后创建pcm设备。
int soc_probe_link_dais(struct snd_soc_card *card,struct snd_soc_pcm_runtime *rtd,
int order){
struct snd_soc_dai_link *dai_link = rtd->dai_link;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
//调用dai下的probe函数进行初始化
soc_probe_dai(cpu_dai, order);
//rtd->num_codecs等于1,见上
for (i = 0; i < rtd->num_codecs; i++) {
ret = soc_probe_dai(rtd->codec_dais[i], order);
}
soc_new_pcm(rtd, rtd->num);
}
soc_probe_dai函数:调用dai下的probe函数进行初始化
int soc_probe_dai(struct snd_soc_dai *dai, int order){
if (dai->driver->probe)
ret = dai->driver->probe(dai);
}
}
struct snd_pcm、struct snd_pcm_str、struct snd_pcm_substream等结构体定义:
/*struct snd_pcm_str streams[2],是带有device结构的两个设备,snd_pcm封装了这两个设备,*/
/*是对设备的进一步衍生:可以表示为pcm设备,该pcm设备带2个子设备:两个输入输出流设备*/
struct snd_pcm {
struct snd_card *card;
struct list_head list;
int device; /* device number */
......
char id[64];
char name[80];
//该数组中的两个元素都是snd_pcm_str结构,分别代表playback stream和capture stream
struct snd_pcm_str streams[2];
void *private_data;
......
};
/*str: stream*/
struct snd_pcm_str {
int stream; /* stream (direction) */
struct snd_pcm *pcm;
/* -- substreams -- */
struct snd_pcm_substream *substream;
.....
struct snd_kcontrol *chmap_kctl; /* channel-mapping controls */
struct device dev;
};
struct snd_pcm_substream {
struct snd_pcm *pcm;
struct snd_pcm_str *pstr;
void *private_data; /* copied from pcm->private_data */
int number;
char name[32]; /* substream name */
int stream; /* stream (direction) */
struct snd_dma_buffer dma_buffer;
/* -- hardware operations -- */
const struct snd_pcm_ops *ops;
/* -- runtime information -- */
struct snd_pcm_runtime *runtime;
......
}
soc_new_pcm函数:分配一个struct snd_pcm实例,表示一个pcm类型的snd_device,再给该snd_device分配两个pcm stream, 分别对应playback和capture,每个pcm stream下都挂着一个substream,最后把该snd_pcm实例挂到card的设备链表下。
int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num){
struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *codec_dai;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_pcm *pcm;
// rtd->num_codecs等于1,见上
for (i = 0; i < rtd->num_codecs; i++) {
codec_dai = rtd->codec_dais[i];
if (codec_dai->driver->playback.channels_min)
playback = 1;
if (codec_dai->driver->capture.channels_min)
capture = 1;
}
capture = capture && cpu_dai->driver->capture.channels_min;
playback = playback && cpu_dai->driver->playback.channels_min;
snprintf(new_name, sizeof(new_name), "%s %s-%d",rtd->dai_link->stream_name,(rtd->num_codecs > 1) ? "multicodec" : rtd->codec_dai->name, num);
//新建一个snd_pcm实例,playback、capture都是1,&pcm二级指针获取一级指针
snd_pcm_new(rtd->card->snd_card, new_name, num, playback,capture, &pcm);
/*rtd->ops的结构体是struct snd_pcm_ops,见上*/
rtd->ops.open = soc_pcm_open;
rtd->ops.hw_params = soc_pcm_hw_params;
rtd->ops.prepare = soc_pcm_prepare;
rtd->ops.trigger = soc_pcm_trigger;
rtd->ops.hw_free = soc_pcm_hw_free;
rtd->ops.close = soc_pcm_close;
rtd->ops.pointer = soc_pcm_pointer;
rtd->ops.ioctl = soc_pcm_ioctl;
if (platform->driver->ops) {
rtd->ops.ack = platform->driver->ops->ack;
rtd->ops.copy_user = platform->driver->ops->copy_user;
rtd->ops.copy_kernel = platform->driver->ops->copy_kernel;
rtd->ops.fill_silence = platform->driver->ops->fill_silence;
rtd->ops.page = platform->driver->ops->page;
rtd->ops.mmap = platform->driver->ops->mmap;
}
if (playback)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &rtd->ops);
if (capture)
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &rtd->ops);
}
int snd_pcm_new
_snd_pcm_new
_snd_pcm_new函数:
参数device表示目前创建的是该声卡下的第几个pcm设备,第一个pcm设备从0开始;参数playback_count 表示该pcm将会有几个playback substream;参数capture_count 表示该pcm将会有几个capture substream。
该函数先是创建一个struct snd_pcm对象,该对象表示一个pcm类型的snd_device,然后再给该对象分配两个pcm stream,分别对应playback和capture,最后把该pcm类型的设备挂到card的设备链表下。
int _snd_pcm_new(struct snd_card *card, const char *id, int device,int playback_count,
int capture_count, bool internal,struct snd_pcm **rpcm){
struct snd_pcm *pcm;
//pcm类型的snd_device对应的ops
static struct snd_device_ops ops = {
.dev_free = snd_pcm_dev_free,
.dev_register = snd_pcm_dev_register,
.dev_disconnect = snd_pcm_dev_disconnect,
};
pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
pcm->card = card;
pcm->device = device;
//SNDRV_PCM_STREAM_PLAYBACK值为0,playback_count等于1
snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK,playback_count);
//SNDRV_PCM_STREAM_CAPTURE值为1,capture_count等于1
snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count);
//将pcm设备挂到card的设备链表上
snd_device_new(card, SNDRV_DEV_PCM, pcm, &ops);
*rpcm = pcm;
}
snd_pcm_new_stream函数: 初始化pcm stream,然后再给pcm stream分配一个substream并初始化。
int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count){
//struct snd_pcm_str名字中的str表示stream,struct snd_pcm有
//两个struct snd_pcm_str的对象, 一个表示playback,一个表示capture
struct snd_pcm_str *pstr = &pcm->streams[stream];
struct snd_pcm_substream *substream, *prev;
//stream值先后等于0、1
pstr->stream = stream;
pstr->pcm = pcm;
//substream_count等于1
pstr->substream_count = substream_count;
snd_device_initialize(&pstr->dev, pcm->card);
/*pcm设备节点名*/
dev_set_name(&pstr->dev, "pcmC%iD%i%c", pcm->card->number, pcm->device,stream ==
SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c');
for (idx = 0, prev = NULL; idx < substream_count; idx++) {
substream = kzalloc(sizeof(*substream), GFP_KERNEL);
substream->pcm = pcm;
substream->pstr = pstr;
substream->number = idx;
substream->stream = stream;
sprintf(substream->name, "subdevice #%i", idx);
substream->buffer_bytes_max = UINT_MAX;
//第一次循环:prev和pstr->substream都指向substream,
//所以所有的substream都在pstr->substream链表中
if (prev == NULL)
pstr->substream = substream;
else
prev->next = substream;
prev = substream;
}
snd_pcm_set_ops函数:把struct snd_pcm_ops类型的ops赋值给substream->ops。
void snd_pcm_set_ops(struct snd_pcm *pcm, int direction,
const struct snd_pcm_ops *ops)
{
struct snd_pcm_str *stream = &pcm->streams[direction];
struct snd_pcm_substream *substream;
for (substream = stream->substream; substream != NULL; substream = substream->next)
substream->ops = ops;
}
snd_soc_dapm_new_widget函数:创建widget下的控件
int snd_soc_dapm_new_widgets(struct snd_soc_card *card)
{
struct snd_soc_dapm_widget *w;
unsigned int val;
list_for_each_entry(w, &card->widgets, list)
{
if (w->new)
continue;
if (w->num_kcontrols) {
w->kcontrols = kzalloc(w->num_kcontrols*sizeof(struct snd_kcontrol *),
GFP_KERNEL);
}
switch(w->id) {
case snd_soc_dapm_switch:
case snd_soc_dapm_mixer:
case snd_soc_dapm_mixer_named_ctl:
dapm_new_mixer(w);
break;
case snd_soc_dapm_mux:
case snd_soc_dapm_demux:
dapm_new_mux(w);
break;
case snd_soc_dapm_pga:
case snd_soc_dapm_out_drv:
dapm_new_pga(w);
break;
case snd_soc_dapm_dai_link:
dapm_new_dai_link(w);
break;
default:
break;
}
/* Read the initial power state from the device */
if (w->reg >= 0) {
soc_dapm_read(w->dapm, w->reg, &val);
val = val >> w->shift;
val &= w->mask;
if (val == w->on_val)
w->power = 1;
}
w->new = 1;
dapm_mark_dirty(w, "new widget");
}
dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP);
return 0;
}
dapm_new_mixer函数:
static int dapm_new_mixer(struct snd_soc_dapm_widget *w)
{
int i;
struct snd_soc_dapm_path *path;
struct dapm_kcontrol_data *data;
for (i = 0; i < w->num_kcontrols; i++) {
//遍历所有输出到该widget的path
snd_soc_dapm_widget_for_each_source_path(w, path) {
if (path->name != (char *)w->kcontrol_news[i].name)
continue;
if (!w->kcontrols[i]) {
//判断是否是共享控件,如果是但其它widget还没有创建该控件,
//那么创建它,如果不是也创建它
dapm_create_or_share_kcontrol(w, i);
}
//把path添加到控件data的paths链表里
dapm_kcontrol_add_path(w->kcontrols[i], path);
data = snd_kcontrol_chip(w->kcontrols[i]);
}
}
return 0;
}
dapm_create_or_share_kcontrol函数:判断是否是共享控件,如果是但其它widget还没有创建该控件,那么创建它,如果不是也创建它。
static int dapm_create_or_share_kcontrol(struct snd_soc_dapm_widget *w,int kci)
{
struct snd_soc_dapm_context *dapm = w->dapm;
struct snd_card *card = dapm->card->snd_card;
const char *prefix;
size_t prefix_len;
int shared;
struct snd_kcontrol *kcontrol;
bool wname_in_long_name, kcname_in_long_name;
char *long_name = NULL;
const char *name;
int ret = 0;
//判断控件是否是共享控件
shared = dapm_is_shared_kcontrol(dapm, w, &w->kcontrol_news[kci],&kcontrol);
//wname_in_long_name:widget name是否存在
//kcname_in_long_name:kcontrol name是否存在
if (!kcontrol) {
if (shared) { //虽然是共享的控件,但是其它widget也还没有创建该控件
wname_in_long_name = false;
kcname_in_long_name = true;
} else {
switch (w->id) {
case snd_soc_dapm_switch:
case snd_soc_dapm_mixer:
case snd_soc_dapm_pga:
wname_in_long_name = true;
kcname_in_long_name = true;
break;
case snd_soc_dapm_demux:
case snd_soc_dapm_mux:
wname_in_long_name = true;
kcname_in_long_name = false;
break;
default:
return -EINVAL;
}
}
//如果widget name和kcontrol name都存在,name由这两个名字组成
if (wname_in_long_name && kcname_in_long_name) {
long_name = kasprintf(GFP_KERNEL, "%s %s",w->name + prefix_len,
w->kcontrol_news[kci].name);
name = long_name;
//如果只有widget name存在,kcontrol name不存在,name由widget name 组成
} else if (wname_in_long_name) {
long_name = NULL;
name = w->name + prefix_len;
//如果widget name不存在,kcontrol name存在,name由kcontrol name组成
} else {
long_name = NULL;
name = w->kcontrol_news[kci].name;
}
//创建控件
kcontrol = snd_soc_cnew(&w->kcontrol_news[kci], NULL, name,prefix);
//为控件分配一个与之相关的数据存储空间
ret = dapm_kcontrol_data_alloc(w, kcontrol, name);
//先通过该控件的id查找链表中是否有该控件,有的话直接返回,
//没有的话加入到card的controls的链表中
ret = snd_ctl_add(card, kcontrol);
}
//在控件data中分配一个widget链表,并把该控件所属的widget添加到该链表中
ret = dapm_kcontrol_add_widget(kcontrol, w);
//将创建初始化好的控件重新给到相关widget的相关控件位置上
if (ret == 0)
w->kcontrols[kci] = kcontrol;
}
dapm_is_shared_kcontrol函数:判断控件是否是共享控件
static int dapm_is_shared_kcontrol(struct snd_soc_dapm_context *dapm,
struct snd_soc_dapm_widget *kcontrolw,
const struct snd_kcontrol_new *kcontrol_new,
struct snd_kcontrol **kcontrol)
{
struct snd_soc_dapm_widget *w;
int i;
*kcontrol = NULL;
//遍历widgets链表,查找dapm相同又不是自己的其它widget,如果它的kcontrol_news数组中有
//指定的kcontrol_new,且kcontrols非空,那么这个控件就是两个widget共享的,返回1
list_for_each_entry(w, &dapm->card->widgets, list) {
if (w == kcontrolw || w->dapm != kcontrolw->dapm)
continue;
for (i = 0; i < w->num_kcontrols; i++) {
if (&w->kcontrol_news[i] == kcontrol_new) {
if (w->kcontrols)
*kcontrol = w->kcontrols[i];
return 1;
}
}
}
return 0;
}
dapm_kcontrol_data_alloc函数:为控件分配一个与之相关的数据存储空间
static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget,
struct snd_kcontrol *kcontrol, const char *ctrl_name)
{
struct dapm_kcontrol_data *data;
struct soc_mixer_control *mc;
struct soc_enum *e;
const char *name;
int ret;
data = kzalloc(sizeof(*data), GFP_KERNEL);
INIT_LIST_HEAD(&data->paths);
switch (widget->id) {
case snd_soc_dapm_switch:
case snd_soc_dapm_mixer:
case snd_soc_dapm_mixer_named_ctl:
mc = (struct soc_mixer_control *)kcontrol->private_value;
......
break;
case snd_soc_dapm_demux:
case snd_soc_dapm_mux:
e = (struct soc_enum *)kcontrol->private_value;
......
break;
default:
break;
}
kcontrol->private_data = data;
return 0;
}
dapm_kcontrol_add_widget函数:在控件data中分配一个widget链表,并把该控件所属的widget添加到该链表中。
static int dapm_kcontrol_add_widget(struct snd_kcontrol *kcontrol,
struct snd_soc_dapm_widget *widget)
{
//与该控件相关的数据结构
struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol);
struct snd_soc_dapm_widget_list *new_wlist;
unsigned int n;
if (data->wlist)
n = data->wlist->num_widgets + 1;
else
n = 1;
//分配一个struct snd_soc_dapm_widget_list结构体所需存储空间和n个widget指针所需空间
new_wlist = krealloc(data->wlist,sizeof(*new_wlist) + sizeof(widget) * n,GFP_KERNEL);
//widget添加到wlist链表中
new_wlist->widgets[n - 1] = widget;
new_wlist->num_widgets = n;
data->wlist = new_wlist;
return 0;
}
struct snd_soc_dapm_widget_list、struct dapm_kcontrol_data结构体:
struct snd_soc_dapm_widget_list {
int num_widgets;
struct snd_soc_dapm_widget *widgets[0];
};
struct dapm_kcontrol_data {
unsigned int value;
struct snd_soc_dapm_widget *widget;
struct list_head paths;
struct snd_soc_dapm_widget_list *wlist;
};
dapm_kcontrol_add_path函数:把path添加到控件data的paths链表里,就mixer和mux需要实现,
mixer一个控件对应一条路径,mux一个控件对应多条路径,所有可能和该控件连接的路径都是。
static void dapm_kcontrol_add_path(const struct snd_kcontrol *kcontrol,
struct snd_soc_dapm_path *path)
{
struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol);
list_add_tail(&path->list_kcontrol, &data->paths);
}
dapm_new_mux函数:
static int dapm_new_mux(struct snd_soc_dapm_widget *w)
{
struct snd_soc_dapm_context *dapm = w->dapm;
enum snd_soc_dapm_direction dir;
struct snd_soc_dapm_path *path;
const char *type;
int ret;
switch (w->id) {
case snd_soc_dapm_mux:
dir = SND_SOC_DAPM_DIR_OUT;
type = "mux";
break;
......
}
//mux的控件只有一个
if (w->num_kcontrols != 1) {
return -EINVAL;
}
dapm_create_or_share_kcontrol(w, 0);
//方向是输出,遍历所有指向w的路径
//如果mux是二选一,则有两条路径,路径上的连接状态并不影响它是路径,
//现在把这两条路径(path)添加到该mux的控件下
snd_soc_dapm_widget_for_each_path(w, dir, path) {
if (path->name)
dapm_kcontrol_add_path(w->kcontrols[0], path);
}
return 0;
}
snd_card_register函数:注册card链表下的所有snd_device
int snd_card_register(struct snd_card *card){
device_add(&card->card_dev);
snd_device_register_all(card);
}
int snd_device_register_all(struct snd_card *card)
{
struct snd_device *dev;
int err;
list_for_each_entry(dev, &card->devices, list) {
err = __snd_device_register(dev);
if (err < 0)
return err;
}
return 0;
}
static int __snd_device_register(struct snd_device *dev)
{
if (dev->state == SNDRV_DEV_BUILD) {
if (dev->ops->dev_register) {
//不同类型的snd_device对应的ops不同,具体见snd_device_new函数ops的传值
int err = dev->ops->dev_register(dev);
if (err < 0)
return err;
}
dev->state = SNDRV_DEV_REGISTERED;
}
return 0;
}
static int snd_pcm_dev_register(struct snd_device *device)
{
struct snd_pcm_substream *substream;
struct snd_pcm *pcm;
pcm = device->device_data;
//pcm设备添加到snd_pcm_devices链表上
snd_pcm_add(pcm);
for (cidx = 0; cidx < 2; cidx++) {
int devtype = -1;
if (pcm->streams[cidx].substream == NULL)
continue;
switch (cidx) {
case SNDRV_PCM_STREAM_PLAYBACK:
devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK;
break;
case SNDRV_PCM_STREAM_CAPTURE:
devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE;
break;
}
/* register pcm */
snd_register_device(devtype, pcm->card, pcm->device,
&snd_pcm_f_ops[cidx], pcm,&pcm->streams[cidx].dev);
for (substream = pcm->streams[cidx].substream; substream; substream =
substream->next)
snd_pcm_timer_init(substream);
}
}
const struct file_operations snd_pcm_f_ops[2] = {
{
.owner = THIS_MODULE,
.write = snd_pcm_write,
.write_iter = snd_pcm_writev,
.open = snd_pcm_playback_open,
.release = snd_pcm_release,
.llseek = no_llseek,
.poll = snd_pcm_playback_poll,
.unlocked_ioctl = snd_pcm_ioctl,
.compat_ioctl = snd_pcm_ioctl_compat,
.mmap = snd_pcm_mmap,
.fasync = snd_pcm_fasync,
.get_unmapped_area = snd_pcm_get_unmapped_area,
},
{
.owner = THIS_MODULE,
.read = snd_pcm_read,
.read_iter = snd_pcm_readv,
.open = snd_pcm_capture_open,
.release = snd_pcm_release,
.llseek = no_llseek,
.poll = snd_pcm_capture_poll,
.unlocked_ioctl = snd_pcm_ioctl,
.compat_ioctl = snd_pcm_ioctl_compat,
.mmap = snd_pcm_mmap,
.fasync = snd_pcm_fasync,
.get_unmapped_area = snd_pcm_get_unmapped_area,
}
};
struct snd_minor {
int type; /* SNDRV_DEVICE_TYPE_XXX */
int card; /* card number */
int device; /* device number */
const struct file_operations *f_ops; /* file operations */
void *private_data; /* private data for f_ops->open */
struct device *dev; /* device for sysfs */
struct snd_card *card_ptr; /* assigned card instance */
};
int snd_register_device(int type, struct snd_card *card, int dev,const struct file_operations *f_ops,void *private_data, struct device *device)
{
int minor;
struct snd_minor *preg;
preg = kmalloc(sizeof *preg, GFP_KERNEL);
preg->type = type;
preg->card = card ? card->number : -1;
preg->device = dev;
preg->f_ops = f_ops;
preg->private_data = private_data;
preg->card_ptr = card;
minor = snd_find_free_minor(type, card, dev);
preg->dev = device;
device->devt = MKDEV(major, minor); /* major:116 -->/dev/snd/* */
device_add(device);
snd_minors[minor] = preg;
}
内核Alsa之pcm_mb5ff97fc6948e0的技术博客_51CTO博客
9、其它:
fs:Sampling Frequency
mixer:混音器,将两种以上(包括两种)声音混合在一起输出
SRC:sample frequency converter:重采样,将某种采样频率(输入)换成另一种采样频率
HPF:高通过滤
LPF:低通过滤