ALSA总结之platform简析


前言

前文ALSA总结中提到,ASOC中的框架有三部分,Machine、Platform以及Codec,Machine主要是注册声卡实体,Codec是针对数模设备,本文主要针对ALSA 中Platform部分简要分析。

一、Platform/cpu

一般来说,Platform driver部分主要针对dma的初始化,cpu driver主要针对I2S的初始化,但是在驱动的注册过程中,一般将这两部分的初始化放在一起进行。

static int snd_afe_i2s_sc_probe(struct platform_device *pdev)
{
	//节选如下部分代码
	struct device *dev = &pdev->dev;
	struct sdrv_afe_i2s_sc *afe;
	int ret;
	struct resource *res;
	unsigned int irq_id, value;
	
	afe = devm_kzalloc(dev, sizeof(struct sdrv_afe_i2s_sc), GFP_KERNEL);
	if (afe == NULL){
		return -ENOMEM;
	}
	platform_set_drvdata(pdev, afe);//将pdev中的私有指针保存afe结构
	/* Get register and print */
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);//获取设备树中I2S的资源
	afe->regs = devm_ioremap_resource(afe->dev, res);//将物理地址转化为虚拟地址
	
	/* Get irq and setting */
	irq_id = platform_get_irq(pdev, 0);
	devm_request_irq(afe->dev, irq_id, sdrv_i2s_sc_irq_handler, 0, pdev->name, (void *)afe);

	devm_snd_dmaengine_pcm_register(dev, &sdrv_dmaengine_pcm_config, 0);//重点函数  Platform端 dma的初始化
	
	memcpy(&afe->dai_drv, &snd_afe_dai_template, sizeof(struct snd_soc_dai_driver));
	devm_snd_soc_register_component(dev, &snd_afe_soc_component,&afe->dai_drv,1);//重点函数   cpu端的初始化
	
	afe_i2s_sc_config(afe);
	return 0;
}

在前文中有提到,在声卡的注册过程中,会去配对已经注册的组件,则soc_probe_link_componentssoc_probe_link_dais二个配对函数,在soc_probe_link_components中会调用platform->probe,soc_probe_link_dais中会调用cpu->probe,这二个probe则在上面代码注释中的二个重点函数中进行注册。

二、devm_snd_dmaengine_pcm_register注册函数

int devm_snd_dmaengine_pcm_register(struct device *dev,
	const struct snd_dmaengine_pcm_config *config, unsigned int flags)
{
	struct device **ptr;
	int ret;

	ptr = devres_alloc(devm_dmaengine_pcm_release, sizeof(*ptr), GFP_KERNEL);
	if (!ptr)
		return -ENOMEM;

	ret = snd_dmaengine_pcm_register(dev, config, flags);
	if (ret == 0) {
		*ptr = dev;
		devres_add(dev, ptr);
	} else {
		devres_free(ptr);
	}

	return ret;
}
EXPORT_SYMBOL_GPL(devm_snd_dmaengine_pcm_register);

该函数主要分为两部分:
1、对设备的管理devres_alloc,用于卸载时可以自动释放设备申请的内存而不需要手动
2、真正的注册函数snd_dmaengine_pcm_register

snd_dmaengine_pcm_register函数

static const struct snd_pcm_ops dmaengine_pcm_ops = {  //该ops为DMA对应流的操作集
	.open		= dmaengine_pcm_open,
	.close		= snd_dmaengine_pcm_close,
	.ioctl		= snd_pcm_lib_ioctl,
	.hw_params	= dmaengine_pcm_hw_params,
	.hw_free	= snd_pcm_lib_free_pages,
	.trigger	= snd_dmaengine_pcm_trigger,
	.pointer	= dmaengine_pcm_pointer,
};

static const struct snd_soc_platform_driver dmaengine_pcm_platform = {
	.component_driver = {
		.probe_order = SND_SOC_COMP_ORDER_LATE,// snd_soc_component_driver 成员
	},
	.ops		= &dmaengine_pcm_ops,
	.pcm_new	= dmaengine_pcm_new, //初始化一个dma pcm流
};

int snd_dmaengine_pcm_register(struct device *dev,const struct snd_dmaengine_pcm_config *config, unsigned int flags)
{
	struct dmaengine_pcm *pcm;
	int ret;

	pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);//申请一个dmaengine_pcm结构内存
	if (!pcm)
		return -ENOMEM;

	pcm->config = config;
	pcm->flags = flags;

	ret = dmaengine_pcm_request_chan_of(pcm, dev, config); //通过设备树获取对应的DMA控制器通道
	if (ret)
		goto err_free_dma;
		
	//注册一个platform,绑定snd_soc_platform_driver 操作集
	ret = snd_soc_add_platform(dev, &pcm->platform,&dmaengine_pcm_platform);
	if (ret)
		goto err_free_dma;

	return 0;

err_free_dma:
	dmaengine_pcm_release_chan(pcm);
	kfree(pcm);
	return ret;
}

snd_dmaengine_pcm_register主要的函数是调用snd_soc_add_platform进行组件注册

int snd_soc_add_platform(struct device *dev, struct snd_soc_platform *platform,
		const struct snd_soc_platform_driver *platform_drv)
{
	int ret;
	//对component进行初始化,将component和component_driver绑定
	ret = snd_soc_component_initialize(&platform->component,&platform_drv->component_driver, dev);
	if (ret)
		return ret;

	platform->dev = dev;
	platform->driver = platform_drv;//将dmaengine_pcm_platform传过来给到 platform->driver
	
	/*dmaengine_pcm_platform没有probe和remove*/
	if (platform_drv->probe)
		platform->component.probe = snd_soc_platform_drv_probe;
	if (platform_drv->remove)
		platform->component.remove = snd_soc_platform_drv_remove;

#ifdef CONFIG_DEBUG_FS
	platform->component.debugfs_prefix = "platform";
#endif
	mutex_lock(&client_mutex);
	
	//将component插入到全局链表component_list中,在前文提到的匹配中查找component_list相对应
	snd_soc_component_add_unlocked(&platform->component);
	
	//将platform插入到全局链表platform_list中,在前文提到的匹配中查找platform_list相对应
	list_add(&platform->list, &platform_list);
	
	mutex_unlock(&client_mutex);

	return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_add_platform);

这样就完成了platform的注册,加入到静态全局内核链表中,等待声卡初始化时调用。
注意:platform中包含有component这个成员,platform_drv也包含了component_driver,但是上述代码可见 platform->driver 和component->driver是有各自的操作集,进行各自的初始化。

devm_snd_soc_register_component函数

static struct snd_soc_component_driver snd_afe_soc_component = { //对应的component 操作集 
	.name = "afe-dai",
    .probe = snd_afe_soc_platform_probe,
    .remove = snd_afe_soc_platform_remove,
	.dapm_widgets = afe_widgets,
	.num_dapm_widgets = ARRAY_SIZE(afe_widgets),
	.dapm_routes = afe_dapm_map,
	.num_dapm_routes = ARRAY_SIZE(afe_dapm_map),
};
static  struct snd_soc_dai_driver snd_afe_dai_template = { //对应的cpu端操作集
	.name = "snd-afe-sc-i2s-dai0",
	.probe = snd_soc_dai_probe,
	.remove = snd_soc_dai_remove,
	.playback =
	    {
		.stream_name = "I2S Playback",
		.formats = SND_FORMATS,
		.rates = SNDRV_PCM_RATE_8000_48000,
		.channels_min = SND_CHANNELS_MIN,
		.channels_max = SND_CHANNELS_MAX,
	    },
	.capture =
	    {
		.stream_name = "I2S Capture",
		.formats = SND_FORMATS,
		.rates = SNDRV_PCM_RATE_8000_48000,
		.channels_min = SND_CHANNELS_MIN,
		.channels_max = SND_CHANNELS_MAX,
	    },
	.ops = &snd_afe_dai_ops, //platform有ops,那我cpu端一样也有ops
	.symmetric_rates = 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)
{
	struct device **ptr;
	int ret;

	ptr = devres_alloc(devm_component_release, sizeof(*ptr), GFP_KERNEL);
	if (!ptr)
		return -ENOMEM;
	//将component 操作集 和cpu端操作集进行初始化
	ret = snd_soc_register_component(dev, cmpnt_drv, dai_drv, num_dai);
	if (ret == 0) {
		*ptr = dev;
		devres_add(dev, ptr);
	} else {
		devres_free(ptr);
	}

	return ret;
}
EXPORT_SYMBOL_GPL(devm_snd_soc_register_component);

所谓的cpu端也一样,分为两部分,主要调用snd_soc_register_component

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;
	int ret;
	//分配一个component,这里和前面的dma初始化不一样,cpu->driver中没包含,所以额外申请
	component = kzalloc(sizeof(*component), GFP_KERNEL);
	
	//对component进行初始化,将component和component_driver绑定
	 snd_soc_component_initialize(component, component_driver, dev);
	
	component->ignore_pmdown_time = true;
	component->registered_as_component = true;
	//对cpu端初始化
	snd_soc_register_dais(component, dai_drv, num_dai, true);
	
	//将component加入到全局链表component_list中
	snd_soc_component_add(component);
	return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_register_component);

static 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;
	unsigned int i;
	int ret;
	component->dai_drv = dai_drv;//将dai_drv保存一份在component的snd_soc_dai_driver成员中
	soc_add_dai(component, dai_drv + i,count == 1 && legacy_dai_naming);
	return 0;

}
static 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);//分配一个snd_soc_dai,看过前文的应该就知道了,这个结构体很熟悉

	//对name进行优化以及判断是否为空
	if (legacy_dai_naming &&(dai_drv->id == 0 || dai_drv->name == NULL)) {
		dai->name = fmt_single_name(dev, &dai->id);
	} else {
		dai->name = fmt_multiple_name(dev, dai_drv);
		if (dai_drv->id)
			dai->id = dai_drv->id;
		else
			dai->id = component->num_dai;
	}
	if (dai->name == NULL) {
		kfree(dai);
		return NULL;
	}
	dai->component = component;//component复制到dai->component中
	dai->dev = dev;
	dai->driver = dai_drv;//初始化cpu端的driver
	if (!dai->driver->ops)
		dai->driver->ops = &null_dai_ops;
	//将cpu初始化的实体加入到component的dai_list链表中,在声卡初始化时会调用
	list_add(&dai->list, &component->dai_list);
	component->num_dai++;

	return dai;
}

总结

至此,完成了声卡注册设备过程中,所需要的Platform/cpu初始化。
有一点需要注意,就是Platform并没有在dai_list,所以就可以解释为什么声卡注册过程中会遍历platform_list的原因,这样可以达到更换平台也不影响到cpu端和codec端的匹配。codec的初始化也是类似,所以就不再展开说明。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值