ALSA学习(5)——ASoC架构中的Machine

news2024/12/23 10:08:22

参考博客:https://blog.csdn.net/DroidPhone/article/details/7231605
(以下内容皆为原博客转载)

文章目录

  • 一、注册Platform Device
  • 二、注册Platform Driver
  • 三、初始化入口soc_probe()

一、注册Platform Device

ASoC把声卡注册为Platform Device,我们以装配有WM8994的一款Samsung的开发板SMDK为例子做说明,WM8994是一颗Wolfson生产的多功能Codec芯片。
代码的位于:/sound/soc/samsung/smdk_wm8994.c,我们关注模块的初始化函数:

static int __init smdk_audio_init(void)
{
	int ret;
 
	smdk_snd_device = platform_device_alloc("soc-audio", -1);
	if (!smdk_snd_device)
		return -ENOMEM;
	platform_set_drvdata(smdk_snd_device, &smdk);
	ret = platform_device_add(smdk_snd_device);
	if (ret)
		platform_device_put(smdk_snd_device);

	return ret;
}

由此可见,模块初始化时,注册了一个名为soc-audio的Platform设备,同时把smdk设到platform_device结构的dev.drvdata字段中,这里引出了第一个数据结构snd_soc_card的实例smdk,他的定义如下:

static struct snd_soc_dai_link smdk_dai[] = {
	{ /* Primary DAI i/f */
		.name = "WM8994 AIF1",
		.stream_name = "Pri_Dai",
		.cpu_dai_name = "samsung-i2s.0",
		.codec_dai_name = "wm8994-aif1",
		.platform_name = "samsung-audio",
		.codec_name = "wm8994-codec",
		.init = smdk_wm8994_init_paiftx,
		.ops = &smdk_ops,
	}, { /* Sec_Fifo Playback i/f */
		.name = "Sec_FIFO TX",
		.stream_name = "Sec_Dai",
		.cpu_dai_name = "samsung-i2s.4",
		.codec_dai_name = "wm8994-aif1",
		.platform_name = "samsung-audio",
		.codec_name = "wm8994-codec",
		.ops = &smdk_ops,
	},
};
 
static struct snd_soc_card smdk = {
	.name = "SMDK-I2S",
	.owner = THIS_MODULE,
	.dai_link = smdk_dai,
	.num_links = ARRAY_SIZE(smdk_dai),
};

通过snd_soc_card结构,又引出了Machine驱动的另外两个个数据结构:
snd_soc_dai_link(实例:smdk_dai[] )
snd_soc_ops(实例:smdk_ops )
其中,snd_soc_dai_link中,指定了Platform、Codec、codec_dai、cpu_dai的名字,稍后Machine驱动将会利用这些名字去匹配已经在系统中注册的platform,codec,dai,这些注册的部件都是在另外相应的Platform驱动和Codec驱动的代码文件中定义的,这样看来,Machine驱动的设备初始化代码无非就是选择合适Platform和Codec以及dai,用他们填充以上几个数据结构,然后注册Platform设备即可。当然还要实现连接Platform和Codec的dai_link对应的ops实现,本例就是smdk_ops,它只实现了hw_params函数:smdk_hw_params

二、注册Platform Driver

按照Linux的设备模型,有platform_device,就一定会有platform_driver。ASoC的platform_driver在以下文件中定义:sound/soc/soc-core.c。

还是先从模块的入口看起:

static int __init snd_soc_init(void)
{
    ......
	return platform_driver_register(&soc_driver);
}

soc_driver的定义如下:

/* ASoC platform driver */
static struct platform_driver soc_driver = {
	.driver		= {
		.name		= "soc-audio",
		.owner		= THIS_MODULE,
		.pm		= &soc_pm_ops,
	},
	.probe		= soc_probe,
	.remove		= soc_remove,
};

我们看到platform_driver的name字段为soc-audio,正好与platform_device中的名字相同,按照Linux的设备模型,platform总线会匹配这两个名字相同的device和driver,同时会触发soc_probe的调用,它正是整个ASoC驱动初始化的入口。

三、初始化入口soc_probe()

soc_probe函数本身很简单,它先从platform_device参数中取出snd_soc_card,然后调用snd_soc_register_card,通过snd_soc_register_card,为snd_soc_pcm_runtime数组申请内存,每一个dai_link对应snd_soc_pcm_runtime数组的一个单元,然后把snd_soc_card中的dai_link配置复制到相应的snd_soc_pcm_runtime中,最后,大部分的工作都在snd_soc_instantiate_card中实现,下面就看看snd_soc_instantiate_card做了些什么:

该函数首先利用card->instantiated来判断该卡是否已经实例化,如果已经实例化则直接返回,否则遍历每一对dai_link,进行codec、platform、dai的绑定工作,下只是代码的部分选节,详细的代码请直接参考完整的代码树。

	/* bind DAIs */
	for (i = 0; i < card->num_links; i++)
		soc_bind_dai_link(card, i);

ASoC定义了三个全局的链表头变量:codec_list、dai_list、platform_list,系统中所有的Codec、DAI、Platform都在注册时连接到这三个全局链表上。soc_bind_dai_link函数逐个扫描这三个链表,根据card->dai_link[]中的名称进行匹配,匹配后把相应的codec,dai和platform实例赋值到card->rtd[]中(snd_soc_pcm_runtime)。经过这个过程后,snd_soc_pcm_runtime:(card->rtd)中保存了本Machine中使用的Codec,DAI和Platform驱动的信息。
snd_soc_instantiate_card接着初始化Codec的寄存器缓存,然后调用标准的alsa函数创建声卡实例:

	/* card bind complete so register a sound card */
	ret = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
			card->owner, 0, &card->snd_card);
	card->snd_card->dev = card->dev;
 
	card->dapm.bias_level = SND_SOC_BIAS_OFF;
	card->dapm.dev = card->dev;
	card->dapm.card = card;
	list_add(&card->dapm.list, &card->dapm_list);

然后,依次调用各个子结构的probe函数:

	/* initialise the sound card only once */
	if (card->probe) {
		ret = card->probe(card);
		if (ret < 0)
			goto card_probe_error;
	}
 
	/* early DAI link probe */
	for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
			order++) {
		for (i = 0; i < card->num_links; i++) {
			ret = soc_probe_dai_link(card, i, order);
			if (ret < 0) {
				pr_err("asoc: failed to instantiate card %s: %d\n",
			       card->name, ret);
				goto probe_dai_err;
			}
		}
	}
 
	for (i = 0; i < card->num_aux_devs; i++) {
		ret = soc_probe_aux_dev(card, i);
		if (ret < 0) {
			pr_err("asoc: failed to add auxiliary devices %s: %d\n",
			       card->name, ret);
			goto probe_aux_dev_err;
		}
	}

在上面的soc_probe_dai_link()函数中做了比较多的事情,把他展开继续讨论:

static int soc_probe_dai_link(struct snd_soc_card *card, int num, int order)
{
        ......
	/* set default power off timeout */
	rtd->pmdown_time = pmdown_time;
 
	/* probe the cpu_dai */
	if (!cpu_dai->probed &&
			cpu_dai->driver->probe_order == order) {
 
		if (cpu_dai->driver->probe) {
			ret = cpu_dai->driver->probe(cpu_dai);
		}
		cpu_dai->probed = 1;
		/* mark cpu_dai as probed and add to card dai list */
		list_add(&cpu_dai->card_list, &card->dai_dev_list);
	}
 
	/* probe the CODEC */
	if (!codec->probed &&
			codec->driver->probe_order == order) {
		ret = soc_probe_codec(card, codec);
	}
 
	/* probe the platform */
	if (!platform->probed &&
			platform->driver->probe_order == order) {
		ret = soc_probe_platform(card, platform);
	}
 
	/* probe the CODEC DAI */
	if (!codec_dai->probed && codec_dai->driver->probe_order == order) {
		if (codec_dai->driver->probe) {
			ret = codec_dai->driver->probe(codec_dai);
		}
 
		/* mark codec_dai as probed and add to card dai list */
		codec_dai->probed = 1;
		list_add(&codec_dai->card_list, &card->dai_dev_list);
	}
 
	/* complete DAI probe during last probe */
	if (order != SND_SOC_COMP_ORDER_LAST)
		return 0;
 
	ret = soc_post_component_init(card, codec, num, 0);
	if (ret)
		return ret;
        ......
	/* create the pcm */
	ret = soc_new_pcm(rtd, num);
        ........
	return 0;
}

该函数出了挨个调用了codec,dai和platform驱动的probe函数外,在最后还调用了soc_new_pcm()函数用于创建标准alsa驱动的pcm逻辑设备。现在把该函数的部分代码也贴出来:

/* create a new pcm */
int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
{
	......
	struct snd_pcm_ops *soc_pcm_ops = &rtd->ops;
 
	soc_pcm_ops->open	= soc_pcm_open;
	soc_pcm_ops->close	= soc_pcm_close;
	soc_pcm_ops->hw_params	= soc_pcm_hw_params;
	soc_pcm_ops->hw_free	= soc_pcm_hw_free;
	soc_pcm_ops->prepare	= soc_pcm_prepare;
	soc_pcm_ops->trigger	= soc_pcm_trigger;
	soc_pcm_ops->pointer	= soc_pcm_pointer;
 
	ret = snd_pcm_new(rtd->card->snd_card, new_name,
			num, playback, capture, &pcm);
 
	/* DAPM dai link stream work */
	INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work);
 
	rtd->pcm = pcm;
	pcm->private_data = rtd;
	if (platform->driver->ops) {
		soc_pcm_ops->mmap = platform->driver->ops->mmap;
		soc_pcm_ops->pointer = platform->driver->ops->pointer;
		soc_pcm_ops->ioctl = platform->driver->ops->ioctl;
		soc_pcm_ops->copy = platform->driver->ops->copy;
		soc_pcm_ops->silence = platform->driver->ops->silence;
		soc_pcm_ops->ack = platform->driver->ops->ack;
		soc_pcm_ops->page = platform->driver->ops->page;
	}
 
	if (playback)
		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, soc_pcm_ops);
 
	if (capture)
		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, soc_pcm_ops);
 
	if (platform->driver->pcm_new) {
		ret = platform->driver->pcm_new(rtd);
		if (ret < 0) {
			pr_err("asoc: platform pcm constructor failed\n");
			return ret;
		}
	}
 
	pcm->private_free = platform->driver->pcm_free;
	return ret;
}

该函数首先初始化snd_soc_runtime中的snd_pcm_ops字段,也就是rtd->ops中的部分成员,例如open,close,hw_params等,紧接着调用标准alsa驱动中的创建pcm的函数snd_pcm_new()创建声卡的pcm实例,pcm的private_data字段设置为该runtime变量rtd,然后用platform驱动中的snd_pcm_ops替换部分pcm中的snd_pcm_ops字段,最后,调用platform驱动的pcm_new回调,该回调实现该platform下的dma内存申请和dma初始化等相关工作。到这里,声卡和他的pcm实例创建完成。

回到snd_soc_instantiate_card函数,完成snd_card和snd_pcm的创建后,接着对dapm和dai支持的格式做出一些初始化合设置工作后,调用了 card->late_probe(card)进行一些最后的初始化合设置工作,最后则是调用标准alsa驱动的声卡注册函数对声卡进行注册:

	if (card->late_probe) {
		ret = card->late_probe(card);
		if (ret < 0) {
			dev_err(card->dev, "%s late_probe() failed: %d\n",
				card->name, ret);
			goto probe_aux_dev_err;
		}
	}
 
	snd_soc_dapm_new_widgets(&card->dapm);
 
	if (card->fully_routed)
		list_for_each_entry(codec, &card->codec_dev_list, card_list)
			snd_soc_dapm_auto_nc_codec_pins(codec);
 
	ret = snd_card_register(card->snd_card);
	if (ret < 0) {
		printk(KERN_ERR "asoc: failed to register soundcard for %s\n", card->name);
		goto probe_aux_dev_err;
	}

至此,整个Machine驱动的初始化已经完成,通过各个子结构的probe调用,实际上,也完成了部分Platfrom驱动和Codec驱动的初始化工作,整个过程可以用一下的序列图表示:
基于3.0内核 soc_probe序列图
在这里插入图片描述
基于2.6.35 soc_probe序列图
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1350978.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

大发汽车紧急关闭日本4家工厂,停工时间延长至明年1月|百能云芯

大发汽车近期的生产线关闭引起了广泛关注&#xff0c;丰田汽车旗下的大发汽车公司因为不当测试和资料造假问题而被迫采取了紧急措施&#xff0c;关闭了其在日本的全部4家工厂的生产线。这一决定不仅对大发汽车本身产生潜在影响&#xff0c;还牵涉到数千家汽车零部件制造商及其员…

51单片机项目(26)——基于51单片机的超声波测距protues仿真

1.功能设计 用51单片机做的超声波测距系统&#xff0c;用的传感器是HCSR04&#xff0c;将距离实时显示在LCD1602屏幕上&#xff01;&#xff01;内含keil工程 完整的protues文件 可以运行&#xff01;&#xff01;&#xff01; 仿真截图&#xff1a;&#xff08;有一丢丢的误差…

【RocketMQ每日一问】RocketMQ5.0POP消费模式如何实现的?

1 什么是 Pop 消费 RocketMQ 5.0 中引入了一种新的消费模式&#xff1a;Pop 消费模式。 我们知道 RocketMQ 原来有两种消费模式&#xff1a;Pull 模式消费和 Push 模式消费&#xff0c;其中 Push 模式指的是 Broker 将消息主动“推送”给消费者&#xff0c;它的背后其实是消费…

【Java进阶篇】字符串常量、字符串常量池详解

字符串常量、字符串常量池详解 ✔️字符串常量池是如何实现的?✔️字符串常量从哪来的? ✔️字符串常量是什么时候进入到字符串常量池的? ✔️字符串常量池是如何实现的? 字符串常量池 (String Constant Pool) 是Java中一块特殊的内存区域&#xff0c;用于存储字符串常量。…

【052】基于Springboot、Vuey电影购票管理系统(附完整源码、数据库)

**基于Springboot、Vue、Mysql的电影购票管理系统&#xff08;附源码、数据库&#xff09;&#xff0c;超级完整的项目&#xff0c;值得下载&#xff01;&#xff01; 链接在博客最底下**电影购票管理系统源码及数据库百度云链接&#xff1a; https://pan.baidu.com/s/1loetDV…

Mnist手写体数字数据集介绍与在Pytorch中使用

1.介绍 MNIST&#xff08;Modified National Institute of Standards and Technology&#xff09;数据集是一个广泛用于机器学习和计算机视觉研究的常用数据集之一。它由手写数字图像组成&#xff0c;包括0到9的数字&#xff0c;每张图像都是28x28像素的灰度图像&#xff0c;图…

IO模型及高性能网络架构分析

IO调用 操作系统一次IO过程&#xff1a; 应用程序发起的一次IO操作包含两个阶段&#xff1a; IO调用&#xff1a;应用程序进程向操作系统内核发起调用。 IO执行&#xff1a;操作系统内核完成IO操作。 操作系统内核完成IO操作还包括两个过程&#xff1a; 准备数据阶段&#…

2023年终总结---做一个有生活的人

2023总结 【工作】2023一整年都在阿里淘天集团工作。作为工作的第二年&#xff0c;学习到很多知识和设计方法&#xff0c;从而某个中心的左膀右臂。敢在需求评审中说不&#xff0c;说话变的有底气。中间经历了组织架构的一次一次调整&#xff0c;当时面试自己的一个师兄因家庭原…

CSS-4

平面转换 整体认识 div {margin: 100px 0;width: 100px;height: 100px;background-color: pink;/* 过渡效果 */transition: all 1s;}/* 当鼠标悬停到div时&#xff0c;进行平面转换 */div:hover {transform: translate(800px) rotate(360deg) scale(2) skew(180deg);}作用&…

Python爬虫——使用代理IP池维护虚拟用户

目录 前言 一、什么是代理IP池&#xff1f; 二、爬取代理IP 三、验证代理IP的可用性 四、维护代理IP池 五、使用代理IP池进行爬取 六、总结 前言 在进行Web爬取时&#xff0c;使用代理IP是一种常见的策略&#xff0c;它可以帮助我们隐藏真实IP地址&#xff0c;绕过网站…

PyQT 多进程

在PyQt中&#xff0c;图形化界面&#xff08;GUI&#xff09;是运行在主线程中的&#xff0c;而多进程是在独立的进程中执行的。默认情况下&#xff0c;多进程之间是无法直接共享图形化界面的。 然而&#xff0c;有几种方法可以在多进程中与PyQt的图形化界面进行通信&#xff…

Wnmp本地部署结合内网穿透实现任意浏览器远程访问本地服务

最近&#xff0c;我发现了一个超级强大的人工智能学习网站。它以通俗易懂的方式呈现复杂的概念&#xff0c;而且内容风趣幽默。我觉得它对大家可能会有所帮助&#xff0c;所以我在此分享。点击这里跳转到网站。 文章目录 前言1.Wnmp下载安装2.Wnmp设置3.安装cpolar内网穿透3.1…

Linux驱动开发学习笔记7《并发与竞争》

目录 一、并发与竞争 1、并发与竞争简介 2、保护内容是什么 二、原子操作 1、 原子操作简介 2、原子整形操作API 函数 3、原子位操作API 函数 4、实验 &#xff08;1&#xff09; 修改设备树文件 &#xff08;2&#xff09; LED 驱动修改 &#xff08;3&#xff09;…

2023年度总结———豫见及遇见

目录 一.AI 人工智能方向 二.华为数通网络方向 三.腾讯云AI绘画方向 四.年度心得总结板块 博主C站主页&#xff1a;知孤云出岫_网络,计算机,计算机网络教案-CSDN博客 博主腾讯云主页&#xff1a; 知孤云出岫 - 个人中心 - 腾讯云开发者社区-腾讯云 2023年年度词&#xff1a…

集合高级知识点

集合高级 1. HashSet 底层原理 HashSet 的特点&#xff1a; HashSet 实现了 Set 接口HashSet 底层实质上是 HashMap可以存放 null 值&#xff0c;但是只能有一个 nullHashSet 不保证元素是有序的&#xff0c;取决于 hash 后&#xff0c;再确定索引的结果&#xff0c;即不保证…

Python 从入门到精通之通俗易懂学闭包

系列 Python从入门到精通之安装与快速入门-CSDN博客 Python从入门到精通之基本数据类型和变量-CSDN博客 Python从入门到精通之集合&#xff08;List列表、Tuple元组、Dict字典、Set&#xff09;-CSDN博客 Python从入门到精通之条件语句、循环语句和函数-CSDN博客 Python从…

单片机原理及应用:计数按键控制数码管显示

承接上文&#xff0c;我们来介绍一下按键和数码管的配合工作&#xff0c;由于数码管显示的字符和位数多种多样&#xff0c;无法做到一个字符对应一个按键&#xff0c;所以程序主要记录按键的使用次数来切换数码管的显示。 #include <reg52.h> //包含reg52.h头…

如何使用SeaFile搭建本地私有云盘并结合cpolar实现远程访问

文章目录 1. 前言2. SeaFile云盘设置2.1 SeaFile的安装环境设置2.2 SeaFile下载安装2.3 SeaFile的配置 3. cpolar内网穿透3.1 Cpolar下载安装3.2 Cpolar的注册3.3 Cpolar云端设置3.4 Cpolar本地设置 4.公网访问测试5.结语 1. 前言 现在我们身边的只能设备越来越多&#xff0c;…

2024洗地机哪家强?口碑洗地机推荐

现如今&#xff0c;智能家电在人们生活中变得越来越受欢迎&#xff0c;例如智能洗地机的出现&#xff0c;不仅省时省力&#xff0c;还实现了家务清洁的自由。在家庭中&#xff0c;地面清洁一直是一个令人头疼的问题&#xff0c;各种智能家居品牌通过开发各种智能家电产品来解决…

【工具】vscode搜索结果及工程目录的文件夹、文件的排除

&#x1f41a;作者简介&#xff1a;花神庙码农&#xff08;专注于Linux、WLAN、TCP/IP、Python等技术方向&#xff09;&#x1f433;博客主页&#xff1a;花神庙码农 &#xff0c;地址&#xff1a;https://blog.csdn.net/qxhgd&#x1f310;系列专栏&#xff1a;善假于物&#…