基于mini2440的Linux音频驱动完全解读(三)——ASoc层的初始化(2)

本文深入探讨Linux音频驱动,重点关注ASoc层的初始化。通过分析平台链表`platform`和设备链表`dai_list`的注册过程,揭示了`snd_soc_register_platform`、`snd_soc_register_dai`在内核中的工作原理,这些函数最终都会调用`snd_soc_instantiate_cards`来完成初始化。理解这一过程有助于更好地理解alsa驱动的初始化流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

下面就看看上面提到的两个链表platform和dai_list。

先看platform

搜索platform,发现在函数snd_soc_register_platform中,将snd_soc_platform类型的变量加入到了这个链表,现在就要看是谁调用了snd_soc_register_platform这个函数。之后,在s3c24xx_soc_platform_init中找到了调用这个函数的代码,s3c24xx_soc_platform_init是一个用module_init修饰的模块初始化函数。

在这里,向内核注册的snd_soc_platform类型的变量为:

struct snd_soc_platform s3c24xx_soc_platform = {
	.name		= "s3c24xx-audio",
	.pcm_ops 	= &s3c24xx_pcm_ops,
	.pcm_new	= s3c24xx_pcm_new,
	.pcm_free	= s3c24xx_pcm_free_dma_buffers,
};

下面分析函数snd_soc_register_platform,看看是怎么注册的。

int snd_soc_register_platform(struct snd_soc_platform *platform)
{
	if (!platform->name)
		return -EINVAL;

	INIT_LIST_HEAD(&platform->list);

	mutex_lock(&client_mutex);
	list_add(&platform->list, &platform_list);
	snd_soc_instantiate_cards();
	mutex_unlock(&client_mutex);

	pr_debug("Registered platform '%s'\n", platform->name);

	return 0;
}

看了这个函数之后,发现和snd_soc_register_card一样,也调用了snd_soc_instantiate_cards这个函数,也就是又执行了一遍snd_soc_instantiate_card中匹配的过程。

 

再看dai_list这个链表。链表dai_list中的节点是在函数snd_soc_register_dai中添加的。模块初始化函数uda134x_init调用了snd_soc_register_dai,向内核注册一个snd_soc_dai类型的变量uda134x_dai。

struct snd_soc_dai uda134x_dai = {
	.name = "UDA134X",
	/* playback capabilities */
	.playback = {
		.stream_name = "Playback",
		.channels_min = 1,
		.channels_max = 2,
		.rates = UDA134X_RATES,
		.formats = UDA134X_FORMATS,
	},
	/* capture capabilities */
	.capture = {
		.stream_name = "Capture",
		.channels_min = 1,
		.channels_max = 2,
		.rates = UDA134X_RATES,
		.formats = UDA134X_FORMATS,
	},
	/* pcm operations */
	.ops = &uda134x_dai_ops,
};

注册dai的函数定义如下。

int snd_soc_register_dai(struct snd_soc_dai *dai)
{
	if (!dai->name)
		return -EINVAL;

	/* The device should become mandatory over time */
	if (!dai->dev)
		printk(KERN_WARNING "No device for DAI %s\n", dai->name);

	if (!dai->ops)
		dai->ops = &null_dai_ops;

	INIT_LIST_HEAD(&dai->list);

	mutex_lock(&client_mutex);
	list_add(&dai->list, &dai_list);
	snd_soc_instantiate_cards();
	mutex_unlock(&client_mutex);

	pr_debug("Registered DAI '%s'\n", dai->name);

	return 0;
}

看到这里,发现这个函数同样调用了snd_soc_instantiate_cards函数。

看到这里,我们发现不管是调用了snd_soc_register_card、snd_soc_register_dai和snd_soc_register_platform的哪个一个,都会调用snd_soc_instantiate_card函数,只有上面那三个register函数都调用了,snd_soc_instantiate_card才会真正的执行成功。


现在再看一看那个被我们三次碰见的函数snd_soc_instantiate_card。回顾以下soc_probe的流程。

static void snd_soc_instantiate_card(struct snd_soc_card *card)
{
	/* 见上面的分析,但是这里有个疑问,card->dev到底是什么东西呢?这里的card-dev在soc_probe中赋值,pdev = s3c24xx_uda134x_snd_device */
	struct platform_device *pdev = container_of(card->dev,
						    struct platform_device,
						    dev);
	/* 在前面soc_probe函数中执行过card->socdev = socdev,那么这里codec_dev = soc_codec_dev_uda134x */
	struct snd_soc_codec_device *codec_dev = card->socdev->codec_dev;
	struct snd_soc_platform *platform;
	struct snd_soc_dai *dai;
	int i, found, ret, ac97;
	/* 判断是否已经instantiate(实例化),若已经instantiate,这直接返回 */
	if (card->instantiated)
		return;

	found = 0;
/* 遍历list链表,找到card->platform == platform时,则找到了与之匹配的platform,因此我们需要查找加入platform_list链表中的节点,搜索发现,这个链表的节点,是在snd_soc_register_platform 函数中加入的,我们将在稍后分析这个函数*/
	list_for_each_entry(platform, &platform_list, list)
		/* 如果snd_soc_s3c24xx_uda134x->platform即s3c24xx_soc_platform 在 platform_list 中,则found = 1,说明card找到与之匹配的platform */
		if (card->platform == platform) {
			found = 1;
			break;
		}
	if (!found) {
		dev_dbg(card->dev, "Platform %s not registered\n",
			card->platform->name);
		return;
	}

	ac97 = 0;
	/* card = snd_soc_s3c24xx_uda134x,他的num_link = 1*/
	for (i = 0; i < card->num_links; i++) {
		found = 0;
		/* 再遍历dai_list这个链表,这个链表的节点是在snd_soc_register_dai函数中加入的 */
		list_for_each_entry(dai, &dai_list, list)
			/* card->dai_link[i].cpu_dai = s3c24xx_i2s_dai,如果在dai_list中有s3c24xx_i2s_dai,则found = 1,找到card与之匹配的cpu_dai(digital audio interface) */
			if (card->dai_link[i].cpu_dai == dai) {
				found = 1;
				break;
			}
		if (!found) {
			dev_dbg(card->dev, "DAI %s not registered\n",
				card->dai_link[i].cpu_dai->name);
			return;
		}
		/* 判断是否有ac97控制 */
		if (card->dai_link[i].cpu_dai->ac97_control)
			ac97 = 1;
	}

	for (i = 0; i < card->num_links; i++) {
		/* card->dai_link[i].codec_dai = uda134x_dai,如果uda134x_dai没有ops,则将null_dai_ops赋给ops */
		if (!card->dai_link[i].codec_dai->ops)
			card->dai_link[i].codec_dai->ops = &null_dai_ops;
	}

	/* If we have AC97 in the system then don't wait for the
	 * codec.  This will need revisiting if we have to handle
	 * systems with mixed AC97 and non-AC97 parts.  Only check for
	 * DAIs currently; we can't do this per link since some AC97
	 * codecs have non-AC97 DAIs.
	 */
	if (!ac97)
		for (i = 0; i < card->num_links; i++) {
			found = 0;
			list_for_each_entry(dai, &dai_list, list)
				if (card->dai_link[i].codec_dai == dai) {
					found = 1;
					break;
				}
			if (!found) {
				dev_dbg(card->dev, "DAI %s not registered\n",
					card->dai_link[i].codec_dai->name);
				return;
			}
		}

	/* Note that we do not current check for codec components */

	dev_dbg(card->dev, "All components present, instantiating\n");

	/* Found everything, bring it up */
	/* 如果card->probe函数,则调用,snd_soc_s3c24xx_uda134x没有定义probe函数,不执行 */
	if (card->probe) {
		ret = card->probe(pdev);
		if (ret < 0)
			return;
	}

	for (i = 0; i < card->num_links; i++) {
		/* 实际上cpu_dai = s3c24xx_i2s_dai */
		struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
		/* 如果s3c24xx_i2s_dai->probe函数,则调用,我们这有有probe函数 */
		if (cpu_dai->probe) {
			/* 实际上调用的是s3c24xx_i2s_probe */
			ret = cpu_dai->probe(pdev, cpu_dai);
			if (ret < 0)
				goto cpu_dai_err;
		}
	}
	/* codec_dev分析了,是soc_codec_dev_uda134x,存在probe函数 */
	if (codec_dev->probe) {
		/* 实际调用的是uda134x_soc_probe */
		ret = codec_dev->probe(pdev);
		if (ret < 0)
			goto cpu_dai_err;
	}
	/* 这个就是我们前面在platform_list中找到的那个platform,实际上就是s3c24xx_soc_platform ,它没有probe函数 */
	if (platform->probe) {
		ret = platform->probe(pdev);
		if (ret < 0)
			goto platform_err;
	}
	/* 电源管理相关工作队列的初始化 */
	/* DAPM stream work */
	INIT_DELAYED_WORK(&card->delayed_work, close_delayed_work);
#ifdef CONFIG_PM
	/* deferred resume work */
	INIT_WORK(&card->deferred_resume_work, soc_resume_deferred);
#endif
	/* instantiate完成 */
	card->instantiated = 1;

	return;
/* 以下为错误处理 */
platform_err:
	if (codec_dev->remove)
		codec_dev->remove(pdev);

cpu_dai_err:
	for (i--; i >= 0; i--) {
		struct snd_soc_dai *cpu_dai = card->dai_link[i].cpu_dai;
		if (cpu_dai->remove)
			cpu_dai->remove(pdev, cpu_dai);
	}

	if (card->remove)
		card->remove(pdev);
}

当第二次看这个函数的时候,我们可以更加清晰地了解alsa驱动初始化过程。用一个图来说明:




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值