【ASOC全解析(三)】machine原理和实战

【ASOC全解析(三)】machine原理和实战

  • 一、machine概述与原理
    • 1.1 machine的主要职责
    • 1.2 本文的主要内容
  • 二、machine程序示例
    • 完整的machine驱动代码示例
    • 扩展知识:SND_SOC_DAILINK_DEFS
      • 使用方法说明
      • 总结与其它说明

/*****************************************************************************************************************/

声明: 本博客内容均由https://blog.csdn.net/weixin_47702410原创,转载or引用请注明出处,谢谢!

创作不易,如果文章对你有帮助,麻烦点赞 收藏支持~感谢

/*****************************************************************************************************************/

一、machine概述与原理

在ASOC中,Machine层,也称为“机器层”或“板级层”,是ASoC架构中负责将Codec和Platform层连接起来的部分。它是ASoC中与硬件平台相关的部分,通常包含了特定于机器(如开发板或产品)的音频路由和控制逻辑。

1.1 machine的主要职责

Machine层的主要职责包括:

  • 定义音频路径:Machine层定义了音频数据在系统中的流动路径。这包括输入(如麦克风)和输出(如扬声器)设备的连接方式,以及它们如何与Codec设备进行交互。

  • 控制音频接口:Machine层负责管理和配置音频接口,例如I2S、PCM、AC97等,这些接口用于在Codec和SoC之间传输音频数据。

  • 音频路由和混音:Machine层定义了音频信号的路由,例如将哪个音频输入路由到哪个输出,以及如何混合不同的音频源。

  • DAPM(Dynamic Audio Power Management)配置:Machine层配置DAPM策略,以便在不使用某些音频组件时关闭它们的电源,从而节省能源。

  • 板级初始化代码:Machine层包含了特定于机器的初始化代码,这些代码在系统启动时执行,用于初始化音频硬件和ASoC组件。

  • 提供用户空间接口:Machine层通过ALSA提供了用户空间接口,允许应用程序控制音频硬件,例如调整音量、改变音频路由等。

Machine层通常是由设备制造商或社区开发者针对特定硬件平台编写的,因为它需要详细了解硬件的音频架构和功能。在Linux内核源代码中,Machine层的代码通常位于sound/soc/目录下的与特定板相关的子目录中。

Machine层的实现通常涉及到编写一个Machine驱动,这个驱动会注册一个snd_soc_card结构,该结构描述了音频卡的各种属性,包括DAI(Digital Audio Interface)链接、Codec信息、平台信息以及DAPM路由等。这个驱动还会处理与音频相关的平台特定事件,比如耳机插入检测或按钮控制等。

1.2 本文的主要内容

本文针对DAI的连接为主,进行相关的实践。即将所有组件驱动程序(例如Codec端、platform端和 DAI)粘合在一起的代码。

其它功能会关联ALSA Core层或者DAPM或者指定硬件相关的知识,后续有时间再另作文章介绍。

二、machine程序示例

针对DAI的连接,主要是有三个步骤:

  • 1、定义DAI_Link连接codec和platform
  • 2、定义声卡,需要定义snd_soc_card
  • 3、注册声卡

上面的这三点,主要是调用API函数snd_soc_register_card完成(这个函数很重要,但内容较多,后续有时间再出一版文章详细说明)

完整的machine驱动代码示例

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>SND_SOC_DAILINK_DEFS(mycodec,DAILINK_COMP_ARRAY(COMP_CPU("myplat.0")),DAILINK_COMP_ARRAY(COMP_CODEC("my_codec.0", "my_codec_dai")),DAILINK_COMP_ARRAY(COMP_PLATFORM("myplat.0")));static int my_card_init(struct snd_soc_pcm_runtime *rtd)
{printk("%s,line:%d\n",__func__,__LINE__);return 0;
}static int my_card_hw_params(struct snd_pcm_substream *substream,struct snd_pcm_hw_params *params) {struct snd_soc_pcm_runtime *rtd = substream->private_data;//struct snd_soc_dai *codec_dai = rtd->codec_dai;struct snd_soc_card *card = rtd->card;unsigned int freq;//int ret;int stream_flag;printk("%s,line:%d\n",__func__,__LINE__);switch (params_rate(params)) {case    8000:case    12000:case    16000:case    24000:case    32000:case    48000:case    96000:case    192000:freq = 24576000;break;case    11025:case    22050:case    44100:freq = 22579200;break;default:dev_err(card->dev, "invalid rate setting\n");return -EINVAL;}/* the substream type: 0->playback, 1->capture */stream_flag = substream->stream;return 0;
}static struct snd_soc_ops my_card_ops = {.hw_params = my_card_hw_params,
};static struct snd_soc_dai_link my_card_dai_link[] = {{.name       = "my-codec",.stream_name    = "MY-CODEC",SND_SOC_DAILINK_REG(mycodec),
/*SND_SOC_DAILINK_REG(mycodec)等效于.codec_name = "my_codec.0",.codec_dai_name = "my_codec_dai",.cpu_dai_name   = "myplat.0",.platform_name  = "myplat.0",
*/.init       = my_card_init,.ops        = &my_card_ops,},
};static struct snd_soc_card snd_soc_my_card = {.name           = "my-codec",.owner          = THIS_MODULE,.dai_link       = my_card_dai_link,.num_links      = ARRAY_SIZE(my_card_dai_link),
};static int mymachine_probe(struct platform_device *pdev) {int ret = 0;struct snd_soc_card *card = &snd_soc_my_card;printk("-----%s----\n",__func__);/* register the soc card */card->dev = &pdev->dev;ret = snd_soc_register_card(card);if (ret < 0) {dev_err(&pdev->dev, "snd_soc_register_card failed %d\n", ret);return -1;}return ret;
}static int mymachine_remove(struct platform_device *pdev){printk("-----%s----\n",__func__);return 0;
}static void mymachine_pdev_release(struct device *dev){printk("-----%s----\n",__func__);
}static struct platform_device mymachine_pdev = {.name           = "mymachine",.dev.release    = mymachine_pdev_release,
};static struct platform_driver mymachine_pdrv = {.probe      = mymachine_probe,.remove     = mymachine_remove,.driver     = {.name   = "mymachine",},
};static int __init mymachine_init(void)
{int ret;ret = platform_device_register(&mymachine_pdev);if (ret)return ret;ret = platform_driver_register(&mymachine_pdrv);if (ret)platform_device_unregister(&mymachine_pdev);return ret;
}static void __exit mymachine_exit(void)
{platform_driver_unregister(&mymachine_pdrv);platform_device_unregister(&mymachine_pdev);
}module_init(mymachine_init);
module_exit(mymachine_exit);
MODULE_LICENSE("GPL");

扩展知识:SND_SOC_DAILINK_DEFS

从Linux 5.x开始,Linux使用宏SND_SOC_DAILINK_REG快捷定义DAI_LINK中的有关codec和platform连接关系。

其本质和5.x之前的版本并无实质的变化,仅是在形式上采用了一种更加便捷的定义方法。相关源码主要参考“linux/v5.10.177/source/include/sound/soc.h“

本小节主要扩展解释一下下面的语句:

SND_SOC_DAILINK_DEFS(mycodec,DAILINK_COMP_ARRAY(COMP_CPU("myplat.0")),DAILINK_COMP_ARRAY(COMP_CODEC("my_codec.0", "my_codec_dai")),DAILINK_COMP_ARRAY(COMP_PLATFORM("myplat.0")));SND_SOC_DAILINK_REG(mycodec),

SND_SOC_DAILINK_DEFS的定义如下:

#define SND_SOC_DAILINK_DEFS(name, cpu, codec, platform...) \SND_SOC_DAILINK_DEF(name##_cpus, cpu);          \SND_SOC_DAILINK_DEF(name##_codecs, codec);      \SND_SOC_DAILINK_DEF(name##_platforms, platform)

代入得到:

#define SND_SOC_DAILINK_DEFS(mycodec, COMP_CPU("myplat.0"), COMP_CODEC("my_codec.0", "my_codec_dai"), COMP_PLATFORM("myplat.0"))    \
//...

再展开上面的三个宏可以得到:

#define SND_SOC_DAILINK_DEFS(mycodec, { .dai_name = "myplat.0", }, { .name = "my_codec.0", .dai_name = "my_codec_dai", }, { .name = "myplat.0" })   \
//...

将上面的结果完整代入SND_SOC_DAILINK_DEF,即可以得到完整的SND_SOC_DAILINK_DEFS展开结果:

    SND_SOC_DAILINK_DEF(mycodec_cpus,  { .dai_name = "myplat.0", });            \SND_SOC_DAILINK_DEF(mycodec_codecs, { .name = "my_codec.0", .dai_name = "my_codec_dai", });     \SND_SOC_DAILINK_DEF(mycodec_platforms, { .name = "myplat.0" })

进一步代入SND_SOC_DAILINK_DEF可以得到:

static struct snd_soc_dai_link_component mycodec_cpus[] = { .dai_name = "myplat.0", };static struct snd_soc_dai_link_component mycodec_codecs[]   = {.name = "my_codec.0", .dai_name = "my_codec_dai", };static struct snd_soc_dai_link_component mycodec_platforms[]    = {.name = "myplat.0" };

使用方法说明

使用方法

SND_SOC_DAILINK_REG(name),

这个宏展开后如下:

    .cpus           = mycodec_cpus,.num_cpus       = ARRAY_SIZE(mycoded_cpus),.codecs         = mycodec_codecs,.num_codecs     = ARRAY_SIZE(mycodec_codecs),.platforms      = mycodec_platforms,.num_platforms  = ARRAY_SIZE(mycodec_platforms),

总结与其它说明

SND_SOC_DAILINK_DEFS(mycodec,DAILINK_COMP_ARRAY(COMP_CPU("myplat.0")),DAILINK_COMP_ARRAY(COMP_CODEC("my_codec.0", "my_codec_dai")),DAILINK_COMP_ARRAY(COMP_PLATFORM("myplat.0")));static struct snd_soc_dai_link my_card_dai_link[] = {{.name       = "my-codec",.stream_name    = "MY-CODEC",SND_SOC_DAILINK_REG(mycodec),.init       = my_card_init,.ops        = &my_card_ops,},
};//***************************************************************
//最终展开是:
static struct snd_soc_dai_link_component mycodec_cpus[] = { .dai_name = "myplat.0", 
};static struct snd_soc_dai_link_component mycodec_codecs[]   = {.name = "my_codec.0", .dai_name = "my_codec_dai", 
};static struct snd_soc_dai_link_component mycodec_platforms[]    = {.name = "myplat.0" 
};static struct snd_soc_dai_link my_card_dai_link[] = {{.name       = "my-codec",.stream_name    = "MY-CODEC",.cpus           = mycodec_cpus,.num_cpus       = ARRAY_SIZE(mycodec_cpus),.codecs         = mycodec_codecs,.num_codecs     = ARRAY_SIZE(mycodec_codecs),.platforms      = mycodec_platforms,.num_platforms  = ARRAY_SIZE(mycodec_platforms),.init       = my_card_init,.ops        = &my_card_ops,},
};

其它说明
Linux源码中也另外提供了三种DAI_Link的定义方式,下面直接分析其内容的含义,先看Linux官方的说明:

//include/sound/soc.h
/** Sample 1 : Single CPU/Codec/Platform** SND_SOC_DAILINK_DEFS(test,*  DAILINK_COMP_ARRAY(COMP_CPU("cpu_dai")),*  DAILINK_COMP_ARRAY(COMP_CODEC("codec", "codec_dai")),*  DAILINK_COMP_ARRAY(COMP_PLATFORM("platform")));** struct snd_soc_dai_link link = {*  ...*  SND_SOC_DAILINK_REG(test),* };** Sample 2 : Multi CPU/Codec, no Platform** SND_SOC_DAILINK_DEFS(test,*  DAILINK_COMP_ARRAY(COMP_CPU("cpu_dai1"),*             COMP_CPU("cpu_dai2")),*  DAILINK_COMP_ARRAY(COMP_CODEC("codec1", "codec_dai1"),*             COMP_CODEC("codec2", "codec_dai2")));** struct snd_soc_dai_link link = {*  ...*  SND_SOC_DAILINK_REG(test),* };** Sample 3 : Define each CPU/Codec/Platform manually** SND_SOC_DAILINK_DEF(test_cpu,*      DAILINK_COMP_ARRAY(COMP_CPU("cpu_dai1"),*                 COMP_CPU("cpu_dai2")));* SND_SOC_DAILINK_DEF(test_codec,*      DAILINK_COMP_ARRAY(COMP_CODEC("codec1", "codec_dai1"),*                 COMP_CODEC("codec2", "codec_dai2")));* SND_SOC_DAILINK_DEF(test_platform,*      DAILINK_COMP_ARRAY(COMP_PLATFORM("platform")));** struct snd_soc_dai_link link = {*  ...*  SND_SOC_DAILINK_REG(test_cpu,*              test_codec,*              test_platform),* };** Sample 4 : Sample3 without platform** struct snd_soc_dai_link link = {*  ...*  SND_SOC_DAILINK_REG(test_cpu,*              test_codec);* };*/

这段注释提供了几个示例来说明如何使用SND_SOC_DAILINK_DEFS和SND_SOC_DAILINK_REG宏来定义和注册SoC音频dailink。一个dailink通常包含一个CPU(DMA引擎),一个或多个编解码器,以及一个平台(音频控制器硬件)。

下面是每个示例的解释:

  • 示例 1:单个CPU/Codec/Platform
SND_SOC_DAILINK_DEFS(test,DAILINK_COMP_ARRAY(COMP_CPU("cpu_dai")),DAILINK_COMP_ARRAY(COMP_CODEC("codec", "codec_dai")),DAILINK_COMP_ARRAY(COMP_PLATFORM("platform")));

这个示例展示了如何定义一个包含单个CPU、Codec和Platform的dailink。SND_SOC_DAILINK_DEFS宏用于创建三个数组:test_cpus、test_codecs和test_platforms,每个数组包含一个组件。

struct snd_soc_dai_link link = {...SND_SOC_DAILINK_REG(test),
};

然后,SND_SOC_DAILINK_REG宏用于在snd_soc_dai_link结构体中注册这些组件。

  • 示例 2:多个CPU/Codec,没有Platform
SND_SOC_DAILINK_DEFS(test,DAILINK_COMP_ARRAY(COMP_CPU("cpu_dai1"),COMP_CPU("cpu_dai2")),DAILINK_COMP_ARRAY(COMP_CODEC("codec1", "codec_dai1"),COMP_CODEC("codec2", "codec_dai2")));

这个示例展示了如何定义一个包含多个CPU和Codec,但没有Platform的dailink。SND_SOC_DAILINK_DEFS宏创建了两个数组:test_cpus和test_codecs,每个数组包含两个组件。

struct snd_soc_dai_link link = {...SND_SOC_DAILINK_REG(test),
};

SND_SOC_DAILINK_REG宏在这里假定Platform是空的,并在snd_soc_dai_link结构体中注册CPU和Codec组件。

  • 示例 3:手动定义每个CPU/Codec/Platform
SND_SOC_DAILINK_DEF(test_cpu,DAILINK_COMP_ARRAY(COMP_CPU("cpu_dai1"),COMP_CPU("cpu_dai2")));
SND_SOC_DAILINK_DEF(test_codec,DAILINK_COMP_ARRAY(COMP_CODEC("codec1", "codec_dai1"),COMP_CODEC("codec2", "codec_dai2")));
SND_SOC_DAILINK_DEF(test_platform,DAILINK_COMP_ARRAY(COMP_PLATFORM("platform")));

这个示例展示了如何手动定义每个CPU、Codec和Platform组件。SND_SOC_DAILINK_DEF宏用于创建三个独立的数组。

struct snd_soc_dai_link link = {...SND_SOC_DAILINK_REG(test_cpu,test_codec,test_platform),
};

然后,SND_SOC_DAILINK_REG宏用于在snd_soc_dai_link结构体中注册这些组件。

  • 示例 4:没有Platform的示例3
struct snd_soc_dai_link link = {...SND_SOC_DAILINK_REG(test_cpu,test_codec);
};

这个示例与示例3类似,但它省略了Platform组件。SND_SOC_DAILINK_REG宏在这里只注册CPU和Codec组件。

这些示例展示了如何灵活地使用宏来定义和注册不同配置的dailinks,这对于SoC音频驱动的开发非常重要。

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

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

相关文章

珠海长隆海洋王国游记

7月28日&#xff0c;我、爸爸、赵镕沁、安兆涵、李雨葳和他们的爸爸妈妈一起去珠海长隆海洋王国玩。 我们一进园区&#xff0c;首先来到了海豚湾&#xff0c;这里有中华白海豚&#xff0c;它的皮肤是白灰色的 &#xff0c;面带笑容&#xff0c;非常可爱&#xff0c;它是国家一级…

基于SpringBoot的教务管理系统设计与实现(源码+调试)

项目描述 临近学期结束&#xff0c;还是毕业设计&#xff0c;你还在做java程序网络编程&#xff0c;期末作业&#xff0c;老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。今天给大家介绍一篇基于SpringBoot的教务管…

QuestDB时序数据库快速入门

简介 QuestDB是一个开源的高性能时序数据库&#xff0c;专门用于处理时间序列相关的数据存储与查询&#xff1b; QuestDB使用列式存储模型。数据存储在表中&#xff0c;每列存储在其自己的文件和其自己的本机格式中。新数据被附加到每列的底部&#xff0c;以便能够按照与摄取…

flask web 学习之用户认证与会话管理

文章目录 一、安装及初始化二、设置用户加载回调函数三、定义User类四、实现登陆操作五、自定义登陆过程六、使用请求加载器自定义登录七、匿名用户功能八、记住我功能 Flask-Login是一个用于处理用户认证和会话管理的Flask插件。它简化了在Flask应用程序中实现用户登录、登出以…

别再局限于Android和iOS了尝试鸿蒙APP系统开发吧!

最近&#xff0c;多家互联网公司也发布了鸿蒙OS的App开发工程师的岗位&#xff0c;开启了抢人大战&#xff0c;有的企业开出了近百万的年薪招聘鸿蒙OS工程师&#xff0c;而华为甚至为鸿蒙OS资深架构师开出了100万元—160万元的年薪。 「纯血」鸿蒙开启&#xff0c;欲与 Andori…

WEBDYNPRO FPM 框架

框架搭建 1、FPM_OVP_COMPONENT 1 METHOD change_toolbar_btn .2 * enabled "ABAP_TRUE可用 ABAP_FALSE不可用3 * visibility "01不可见 02可见4 DATA: ls_btn TYPE if_fpm_ovp>ty_s_toolbar_button.5 CHECK wd_this->mo_cnr IS BOUND.6 7 TRY .8 …

2011-2022年北大数字普惠金融指数“第五期”(包括省市县)

2011-2022年北大数字普惠金融指数“第五期”&#xff08;包括省市县&#xff09; 1、时间&#xff1a;2011-2022年 其中县级的时间为2014-2022年 2、来源&#xff1a;北大数字普惠金融指数 3、范围&#xff1a;全国31省&#xff0c;337个地级市以及2800个县 4、指标&#x…

API调试?试试Apipost

你是否经常遇到接口开发过程中的各种问题&#xff1f;或许你曾为接口测试与调试的繁琐流程而烦恼。不要担心&#xff01;今天我将向大家介绍一款功能强大、易于上手的接口测试工具——Apipost&#xff0c;并带你深入了解如何玩转它&#xff0c;轻松实现接口测试与调试。 什么是…

面试百问之count(1) 和 count(*) 区别是什么?

count(1) 和 count(*) 是 SQL 中用于统计行数的两种常见方式&#xff0c;它们的区别在于统计的对象不同。 count(1) &#xff1a;这种写法中&#xff0c;括号里的参数并不影响最终的计算结果&#xff0c;常见的用法是传入一个常数值&#xff08;比如1&#xff09;&#xff0c;因…

linux更新内核

内核介绍 官网链接:https://kernel.org 内核下载库: https://mirrors.edge.kernel.org/pub/linux/kernel/ 更新软件源 rootcary:~# apt-get update rootcary:~# sudo apt-get install libncurses5-dev build-essential kernel-package flex bison libelf-dev libssl-dev 下…

[小脚本] maya 命令行常用操作

其实这些代码大部分是从 chatgpt 中生成的。 骨骼命名 import maya.cmds as cmdsdef rename_bones():selected_bones cmds.ls(type"joint") # 获取选中的骨骼for bone in selected_bones:if "_" in bone:new_name bone.split("_")[0] # 获…

机器学习实验报告——Bayes算法

目录 一、算法介绍 1.1算法背景 1.2算法假设 1.3 贝叶斯与朴素贝叶斯 1.4算法原理 二、算法推导 2.1朴素贝叶斯介绍 2.2朴素贝叶斯算法推导 2.2.1先验后验概率 2.2.2条件概率公式 2.3 独立性假设 2.4 朴素贝叶斯推导 三、算法实现 3.1数据集描述 3.2代码实现 四…

Qt容器QVector

添加元素 QVector<QString> vStarts;//添加元素vStarts <<"张艺兴"; //添加单个元素vStarts <<"鹿晗" <<"热巴"; //添加多个元素vStarts.append("杨紫"); //用append添加元素vStarts.prepend("Angleb…

SpringBoot:Bean生命周期自定义初始化和销毁

&#x1f3e1;浩泽学编程&#xff1a;个人主页 &#x1f525; 推荐专栏&#xff1a;《深入浅出SpringBoot》《java项目分享》 《RabbitMQ》《Spring》《SpringMVC》 &#x1f6f8;学无止境&#xff0c;不骄不躁&#xff0c;知行合一 文章目录 前言一、Bean注解指…

如何做好一个信息系统项目经理,一个项目经理的个人体会和经验总结(三)

前言 今天我们继续聊聊在 项目开发阶段&#xff0c;项目经理需要做好的事情 &#x1f603; 二、项目开发阶段&#xff08;续&#xff09; 4. 控制好项目开发质量 要控制好项目开发质量&#xff0c;主要是依赖测试&#xff0c;好的产品都是靠不断地测试&#xff0c;不断地试…

《WebKit 技术内幕》学习之四(3): 资源加载和网络栈

3. 网络栈 3.1 WebKit的网络设施 WebKit的资源加载其实是交由各个移植来实现的&#xff0c;所以WebCore其实并没有什么特别的基础设施&#xff0c;每个移植的网络实现是非常不一样的。 从WebKit的代码结构中可以看出&#xff0c;网络部分代码的确比较少的&#xff0c;它们都在…

西方企业在与中国的竞争中,无可避免地“效仿中国”

长期以来&#xff0c;在西方观察家的视野里&#xff0c;中国科技领域的成功突破主要归结于三大支柱&#xff1a;一是中国建立了完备的基础设施网络&#xff1b;二是大量创新型企业如雨后春笋般涌现&#xff0c;以惊人的速度追赶乃至超越美国硅谷的企业&#xff1b;三是这些创新…

CentOS防火墙基本操作

CentOS操作系统中的防火墙可以使用firewalld或iptables来进行配置。 firewalld&#xff08;默认&#xff09;&#xff1a; 查看当前状态&#xff1a;systemctl status firewalld 开启/关闭防火墙服务&#xff1a;sudo systemctl start/stop firewalld 设置开机自动启动/不启…

【Docker】Docker学习③ - Docker镜像管理

【Docker】Docker学习③ - Docker镜像管理 三、Docker镜像管理1. 搜索镜像2. 下载镜像3. 查看本地镜像4. 镜像导出5. 镜像导入6. 删除镜像7. 容器操作基础命令7.1 从镜像启动一个容器7.2 显示正在运行的容器7.3 显示所有容器7.4 删除运行中的容器7.5 随机映射端口7.6 指定端口映…

wps word 文档里的空白空间太大了

wps word 文档里的空白空间太大了&#xff0c;如下图1 点击【页面】--->【页边距】&#xff0c;把左边、右边的页边距调为0厘米。如下图2 点击【视图】--->【显示比例】从75%改为页宽&#xff0c;页宽的意思是使页面的宽度与窗口的宽度一致。如下图3 图1