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

参考博客: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.mzph.cn/news/594245.shtml

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

相关文章

Typora+PicGo+Gitee构建云存储图片

创建Gitee仓库 首先&#xff0c;打开工作台 - Gitee.com&#xff0c;自行注册一个账户 注册完后&#xff0c;新建一个仓库&#xff08;记得仓库要开源&#xff09; 然后创建完仓库后&#xff0c;鼠标移动到右上角头像位置&#xff0c;选择设置&#xff0c;并点击&#xff…

终于学会听英文歌了:A Sad Me In Your Eyes

A Sad Me In Your Eyes 来源&#xff1a; https://lyricstranslate.com/en/ln-party-sad-me-your-eyes-lyrics.html Fire can’t burn in my eyes If without your smile Snow can cover your smile If without your love When you think of me, I’ve gone too far I can’t …

了解OpenApi和Swagger

Swagger 和 OpenAPI 是一种用于描述 RESTful API 的规范和工具集合。在本文中&#xff0c;我们将探讨 Swagger 和 OpenAPI 的概念、作用、使用方法以及优缺点。 Swagger 和 OpenAPI 的概念 Swagger 是一种用于描述 RESTful API 的规范。它提供了一种简单的方式来描述 API 的请…

Python 热力图的绘制(Matplotlib篇-12)

Python 热力图的绘制(Matplotlib篇-12)         🍹博主 侯小啾 感谢您的支持与信赖。☀️ 🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ�…

如何做好档案数字化前的鉴定工作

要做好档案数字化前的鉴定工作&#xff0c;可以按照以下步骤进行&#xff1a; 1. 确定鉴定目标&#xff1a;明确要鉴定的档案的内容、数量和性质&#xff0c;确定鉴定的范围和目标。 2. 进行档案清点&#xff1a;对档案进行全面清点和登记&#xff0c;包括数量、种类、状况等信…

立体匹配算法(Stereo correspondence)

SGM(Semi-Global Matching)原理&#xff1a; SGM的原理在wiki百科和matlab官网上有比较详细的解释&#xff1a; wiki matlab 如果想完全了解原理还是建议看原论文 paper&#xff08;我就不看了&#xff0c;懒癌犯了。&#xff09; 优质论文解读和代码实现 一位大神自己用c实现…

如何在Windows安装Wnmp服务并实现固定地址远程访问

文章目录 前言1.Wnmp下载安装2.Wnmp设置3.安装cpolar内网穿透3.1 注册账号3.2 下载cpolar客户端3.3 登录cpolar web ui管理界面3.4 创建公网地址 4.固定公网地址访问 前言 WNMP是Windows系统下的绿色NginxMysqlPHP环境集成套件包&#xff0c;安装完成后即可得到一个Nginx MyS…

程序员提问的艺术:28.4K Star指南,告别成为办公室讨厌鬼!

Github: https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way 原文&#xff1a;http://www.catb.org/~esr/faqs/smart-questions.html ✅为什么讨厌某些提问者 未自行尝试解决问题&#xff1a; ❌“怎么用Java写一个排序算法&#xff1f;” &#x1f44d;&#…

计算机毕业设计 基于SpringBoot的工作量统计系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

IDEA断点调试

IDEA断点调试 断点调试是一种在程序执行过程中暂停执行并逐步检查代码状态的方法。它允许开发者在程序运行到特定位置时暂停执行&#xff0c;查看变量的值、执行过程和调用栈等信息&#xff0c;从而更好地理解代码的运行情况和解决问题。可以帮助我们查看java底层源代码的执行…

day 57 算法训练|动态规划part17

参考&#xff1a;代码随想录 647. 回文子串 1. dp数组&#xff08;dp table&#xff09;以及下标的含义 是不是能找到一种递归关系&#xff0c;也就是判断一个子字符串&#xff08;字符串的下表范围[i,j]&#xff09;是否回文&#xff0c;依赖于&#xff0c;子字符串&#x…

Amos各版本安装指南

Amos下载链接 https://pan.baidu.com/s/1uyblN8Q-knNKkqQVlNnXTw?pwd0531 1.鼠标右击【Amos28】压缩包&#xff08;win11及以上系统需先点击“显示更多选项”&#xff09;选择【解压到 Amos28】。 2.打开解压后的文件夹&#xff0c;鼠标右击【Amos28】选择【以管理员身份运行…

AUTOSAR软件架构描述文档,AUTOSAR_EXP_LayeredSoftwareArchitecture

AUTOSAR软件架构描述文档&#xff0c;我们常见的经典的CP架构及OS双核等架构描述 下载链接&#xff1a;https://www.autosar.org/fileadmin/standards/R21-11/CP/AUTOSAR_EXP_LayeredSoftwareArchitecture.pdf

macos下php 5.6 7.0 7.4 8.0 8.3 8.4全版本PHP开发环境安装方法

在macos中如果使用brew 官方默认的core tap 只可以安装官方最新的稳定版PHP, 如果想要安装 php 5.6 或者 php 8.4版本的PHP就需要使用第三方的tap , 这里分享一个比较全面的brew tap shivammathur/php 这个tap里面包含了从php5.6到最新版php8.4的所有可用最新版本PHP, 而且是同…

IDEA设置新建类注释、手动注释详解

文章目录 一、背景二、模板三、设置方法1、新建类注释设置2、手动注释设置 一、背景 每次在一台新电脑安装idea&#xff0c;都需要重新设置idea注释配置&#xff0c;说常用吧&#xff0c;也就新安装时才用&#xff0c;时间久步骤容易忘记&#xff0c;所以用此文章记录一下。 二…

阿里云服务器系统盘高效云盘、ESSD Entry云盘、SSD云盘、ESSD云盘测评

阿里云服务器系统盘或数据盘支持多种云盘类型&#xff0c;如高效云盘、ESSD Entry云盘、SSD云盘、ESSD云盘、ESSD PL-X云盘及ESSD AutoPL云盘等&#xff0c;阿里云百科aliyunbaike.com详细介绍不同云盘说明及单盘容量、最大/最小IOPS、最大/最小吞吐量、单路随机写平均时延等性…

Mysql的基本用法(上)非常详细、快速上手

上篇结束了java基础&#xff0c;本篇主要对Mysql中的一些常用的方法进行了总结&#xff0c;主要对查询方法进行了讲解&#xff0c;包括重要的多表查询用到的内连接和外连接等&#xff0c;以下代码可以直接复制到可视化软件中&#xff0c;方便阅读以及练习&#xff1b; SELECT *…

H5C3练习心得 2024.01.03(文字加载动画效果)--transition,动画渲染,遮罩层

&#xff08;一&#xff09;transition&#xff08;过渡效果&#xff09; 1.详解 通常将css的属性值更改后&#xff0c;浏览器会立即更新新的样式&#xff0c;例如在鼠标悬停在元素上时&#xff0c;通过 :hover 选择器定义的样式会立即应用在元素上。 在 CSS3 中加入了一项过…

C#中使用正则表达式实现汉字转拼音

目录 一、正则表达式基础 二、实例 1.程序入口Form1.cs 2.类库PinYin.cs 三、生成效果 四、实例中的知识点 1.Regex 2.ToCharArray() 3.Regex.IsMatch 方法 &#xff08;1&#xff09;定义 &#xff08;2&#xff09;重载 &#xff08;3&#xff09;IsMatch(Stri…

开发手机中控软件:从零开始的代码之旅!

在这个智能化风靡的时代&#xff0c;手机中控软件成为了许多人的梦想&#xff0c;通过一款软件&#xff0c;我们可以轻松地控制家中的各种智能设备&#xff0c;实现智能家居的美好愿景。 但是&#xff0c;开发手机中控软件并非易事&#xff0c;需要具备一定的编程知识和技能&a…