audio子系统笔记

1、platform、codec、machine的关系:

Linux ALSA音频系统:platform,machine,codec - JavaShuo

 machine驱动负责PlatformCodec之间的耦合以及部分和设备或板子特定的代码,单独的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:低通过滤

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值