当前位置: 首页 > news >正文

Android14音频子系统-Linux音频子系统ASoC-ALSA

文章目录

    • 1、术语
    • 2、概述
      • 1)资料快车
    • 3、预备工作
      • 1)codec - UDA1340 - 硬件规格
      • 2)ASOC-ALSA代码重点目录介绍
      • 3)ASOC-ALSA层级介绍
      • 4)了解基本的软硬件架构
    • 4、数据结构
    • 5、代码分析
      • 1)Machine
        • 1、总体流程介绍
        • 2、Machine代码流程图
          • 重点介绍
      • 2)Platform_driver
      • 3)codec

1、术语

1、asoc : alsa on chip  //嵌入式平台下的alsa,对ALSA进一步的封装
2、runtime : 记录substream的一些重要软件和硬件运行的环境和参数
3、DAI : Digital Audio Interface
4、component :组件(可以理解为 面向对象 中的对象)
5、SAI : ALSA Soc Digital Audio Interface(SAI)
6、topology : 拓扑
7、dpcm : dynamic pcm
8、BE :Blackend 后端9、mixer的含义? 在不同层级有不同含义?
1)在tinyalsa下 代表设置寄存器control
2)在硬件上和驱动层,代表mixer混合器(codec中的一个部件)10、component : 组件,可以理解为一个音频处理单元(可以是物理设备也可以是虚拟设备)-面向对象中的对象,每个处理单元都有独立的功能和控制接口,比如一个音频解码器、音频放大器、音频均衡器、DMA都是一个组件,组件化可以提高系统代码的模块化程度和可维护性;component是音频系统中的逻辑单元;

2、概述

1)资料快车

1、linux ALSA ASoc架构: https://blog.csdn.net/weixin_45437140/article/details/128637521

2、alsa框架介绍:https://blog.51cto.com/u_93011/8854396 原文(付费):https://blog.csdn.net/RadianceBlau/article/details/79432661

3、官方网站:https://www.alsa-project.org/main/index.php/ASoC

3、预备工作

1、首先要对硬件尽可能熟悉(专业名词、与主控的数据通信方式、芯片内部的部件作用);

2、深入代码前应该先掌握这套架构的设计对象和设计思想,那样我们才能更高的效率去看代码!

1)codec - UDA1340 - 硬件规格

1)功能框图
在这里插入图片描述

2)引脚介绍
在这里插入图片描述

重点介绍:

1、DSP:codec负责音效处理的单元2、codec中的音频路径是指?有哪些?
不同codec不尽相同,比如UDA1340
1)播放路径:CPU -> DATA(I2S) -> DSP -> INTERPOLATION FILTER -> NOISE SHAPER -> DAC -> VOUTL/VOUTR(DAC output left/right)
2)录音路径:VINL/VINR(ADC input left/right) -> ADC -> DC-CANCELATION FILTER -> DATA(I2S) -> CPU

2)ASOC-ALSA代码重点目录介绍

分为两部分
1、根据芯片厂商或平台进行分类,每个文件夹放置相应硬件平台的音频驱动代码
├─adi //Anolog Devices, Inc
├─amd //Advanced Mircro Devices, Inc
├─atmel //爱特梅尔
├─au1x //AMD下的au1x系统处理器
├─bcm //Broadcom
├─cirrus //Cirrus Logic, Inc
├─davinci //TI下的davinci系列处理器
├─dwc //ST Microelectronics下的dwc
├─fsl //飞思卡尔-Freescale,现为NXP
├─hisilicon //海思
├─img //Imagination Technologies Ltd.
├─intel //英特尔
├─jz4740 //杰睦(Ingenic)JZ4740系列处理器
├─kirkwood
├─mediatek //联发科
├─meson //Amlogic Meson系统
├─mxs //Freescale i.MX系列处理器
├─nuc900 //Nuvoton Nuclias系列处理器
├─omap //德州仪器(TI)OMAP系列处理器
├─pxa //Marvell PXA系列处理器
├─qcom //Qualcomm 高通
├─rockchip //瑞芯微
├─samsung //三星
├─sh //renesas(瑞萨 -日本)SH系列处理器
├─sirf //联发科(MediaTek)旗下的联发科数字(Sirf)音频驱动
├─spear //ST下的SPEAr 系列ARM处理器
├─sti //意法半导体(STMicroelectronics)
├─stm //意法半导体
├─sunxi //全志科技(Allwinner)
├─tegra //英伟达(NVIDIA)Tegra系列处理器
├─txx9  //Renesas(瑞萨 -日本) Txx9系列处理器
├─uniphier //富士通(Fujitsu)
├─ux500 //德州仪器(TI)UX500系列处理器
├─xtensa //Tensilica(现为Cadence)
└─zte //中兴
1) 众多的硬件平台,由此可见soc推出必要性,大大降低重复代码和复用
2) 认识以上厂商或芯片是有必要的,在linux代码中不同厂商和架构随处可见
3)Soc相关的驱动无非就是 I2S/PCM/AC97/I2C/SPDIF 通信接口的驱动代码2、通用类和框架类代码
├─generic //audio-graph-card和simple-card驱动代码,不针对特定硬件平台
├─codecs //通用的codecs驱动代码,不针对特定硬件平台
soc-* //asoc框架核心代码实现

3)ASOC-ALSA层级介绍

在这里插入图片描述

1、ASOC-ALSA是根据嵌入式设备特点,在ALSA基础上再封装一层而来,有了ALSA学习基础,这里就更容易学习AOSC-ALSA;

1)ALSA目前主要应用在个人电脑上的声卡,而ASOC针对嵌入式设备(一般为I2S接口设备的功放);

2)可以理解ALSA为通用的音频框架,ASOC-ALSA为专用于嵌入式设备的音频框架,根据需求来选择基于哪个架构来进行开发;

2、ASOC-ALSA解决什么问题?

1)codec驱动与soc cpu的代码过于紧密,导致不同平台有不同的codec驱动,而在嵌入式纷繁复杂的产品中,各种搭配导致代码冗余 ->

解决方法:将这codec和soc独立为单独的驱动;

2)一旦播放和录音时,驱动使能整个codec(有些codec有多路输入和输出),所有部件处于上电状态,但实际上有些部件是没有用到,对于移动设备来说,会十分浪费不必要的电量 -> 解决方法:设计DAPM解决,将各个寄存器按照 场景来划分 组合;

3)音频事件没有标准的方法来通知用户 -> 解决方法:

3、如何写ASOC? >> 开发时写codec端即可,使用ASOC封装好的API构造声卡设备

4)了解基本的软硬件架构

1)一般平台的音频系统硬件架构(高度抽象)
在这里插入图片描述

2)来看一个嵌入式领域的一个具体例子的硬件模型(基本都长这样),接下来以这个例子来进行学习
在这里插入图片描述

1)以pci - als300为例,上述的i2s/i2c/dma/timer驱动都放在android\kernel\fusion\4.19\sound\pci\als300.c 实现,那么如果换个平台,als300.c就需要重写;

2)如何改进?ASOC-ALSA结合硬件框架模型(满足市面上绝大多数的codec)来设计软件架构,让其通用且可复用;ASOC的做法将其组件化(类似面向对象),然后不同平台自行组装,相同的特性 单独抽离出来统一设计,硬件专有的实现 留接口给厂商自行挂接;那么组件(component) 对象可以分为:

1、platform - cpu的DMA

2、platform -cpu的I2S

3、Codec的I2S

4、Codec的I2C

以上均需要单独实现;

3)有面向对象编程的人不难看出linux音频驱动的结构与类图很像,将变化的东西封装了起来,层次清晰,方便理解和调试:

》内核代码是一直在变化,越高版本的Linux内核,逐渐往面向对象的方向发展;

4)设计对象说明 - 概念介绍

1、Machine
是指某一款机器,可以是某款设备,某款开发板,又或者是某款智能手机,由此可以看出每个Machine上的硬件实现可能都不一样,CPU不一样,Codec不一样,音频的输入、输出设备也不一样,Machine为CPU、Codec、输入输出设备提供了一个载体。Machine驱动 Machine驱动负责处理机器特有的一些控件和音频事件(例如,当播放音频时,需要先行打开一个放大器);单独的PlatformCodec驱动是不能工作的,它必须由Machine驱动把它们结合在一起才能完成整个设备的音频处理工作。2、Platform
一般是指某一个SoC平台,即CPU,比如pxaxxx,s3cxxxx,omapxxx等等,与音频相关的通常包含该SoC中的时钟、DMA、I2S、PCM等等,只要指定了SoC,那么我们可以认为它会有一个对应的Platform,它只与SoC相关,与Machine无关,这样我们就可以把Platform抽象出来,使得同一款SoC不用做任何的改动,就可以用在不同的Machine中。实际上,把Platform认为是某个SoC更好理解。Platform驱动包含了该SoC平台的音频DMA和音频接口的配置和控制(I2S,PCM,AC97等等);它也不能包含任何与板子或机器相关的代码。3、Codec
字面上的意思就是编解码器,Codec里面包含了I2S接口、D/A、A/D、Mixer、PA(功放),通常包含多种输入(Mic、Line-in、I2S、PCM)和多个输出(耳机、喇叭、听筒,Line-out),Codec和Platform一样,是可重用的部件,同一个Codec可以被不同的Machine使用。嵌入式Codec通常通过I2C对内部的寄存器进行控制。Codec驱动在ASoC中的一个重要设计原则就是要求Codec驱动是平台无关的,它包含了一些音频的控件(Controls),音频接口,DAMP(动态音频电源管理)的定义和某些Codec IO功能。为了保证平台无关性,任何特定于平台和机器的代码都要尽量移到Platform,所有的Codec驱动都要提供以下特性:
Codec DAI 和 PCM的配置信息;
Codec的IO控制方式(I2C,SPI等);
Mixer和其他的音频控件;
Codec的ALSA音频操作接口;必要时,也可以提供以下功能:
DAPM描述信息;
DAPM事件处理程序;
DAC数字静音控制

4、数据结构

整个ASoc是由一系列的数据结构组成(C语言实现的架构大多数如此),理清里面的数据结构就等同掌握ASoc的工作机理

在linux4.19上数据结构如下:
在这里插入图片描述

1、从ASOC-ALSA架构来看,1/2/3区域分别对应如下

在这里插入图片描述

2、linux4.9上使用component概念来代表cpu或codec,根据之前对于组件的分类:

1)4区域对应:platform - cpu的DMA、Codec的I2C (共用同一个结构体)

2)5区域对应:platform -cpu的I2S、Codec的I2S (共用同一个结构体)

3、所有的数据结构通过snd_soc_card作为总源头,把握这个主线索,串联其它的数据结构,其次在初始化时,根据开发者构造的snd_soc_card,找到选定的cpu和codec信息,将driver/ops放在snd_soc_pcm_runtime里(这部分在Machine里面再展开体现)

4、结构体(作用类似对象)对应一个实际的例子(wm8960) 如下
在这里插入图片描述

重点介绍:

1、machine会根据名字去匹配component (即cpu和codec) 对应的driver/dai,并放进snd_soc_pcm_runtime里面2. machine如何指定cpu和codec?
使用snd_soc_dai_link指定当前平台使用的 cpu 和 codec
struct snd_soc_dai_link *dai_link;  /* predefined links only */3、在旧版驱动中ASoC定义了三个全局的链表头变量:codec_list、dai_list、platform_list;linux 4.19在哪里?
均在snd_soc_pcm_runtime上体现
struct snd_soc_pcm_runtime {struct snd_soc_dai *codec_dai; struct snd_soc_dai *cpu_dai;  //一个平台一般只有一个cpu struct snd_soc_dai **codec_dais; //挂接多个codec dai,一个平台或有多个codec,对应dai_listunsigned int num_codecs;struct list_head component_list; //匹配platform_name,对应platform_list 
}4、总体而言,ASoC软件框架分为3部分:
a. platform (用来描述芯片的DAI接口、负责数据传输):DAI: snd_soc_dai_driver, 用来表示支持哪些格式数据, 提供设置格式的函数, 启动数据传输数据传输: snd_soc_component_driverb. codec (用来描述音频编解码芯片, 含有2部分: DAI接口、控制接口):DAI: snd_soc_dai_driver, 用来表示支持哪些格式数据, 提供设置格式的函数, 控制接口: snd_soc_component_driver读写芯片的寄存器c. machine (snd_soc_card, snd_soc_dai_link,用来确定使用哪一个platform, 哪一个codec芯片)最能调用ALSA接口进行分配/设置/注册 snd_card

5、代码分析

1)Machine

1、总体流程介绍
1、Machine代码示例(命名规则一般为:平台/芯片_codec)
1)android\kernel\fusion\4.19\sound\soc\samsung\rx1950_uda1380.c
//rx1950_uda1380.c命名解析:rx1950开发板_uda1380是codec音频芯片
2)android\kernel\fusion\4.19\sound\soc\samsung\smdk_wm8994.c
3)android\kernel\fusion\4.19\sound\soc\samsung\s3c24xx_uda134x.c
//s3c24xx 三星CPU + uda134x Philips codec
采用s3c24xx_uda134x.c作为主要的介绍(例子应该尽可能简单些,才更好地掌握框架内容,然后再由易及难,循序渐进)一、Machine
1.旧驱动写法
1)platform driver
module_init(rx1950_init);
static int __init rx1950_init(void){1. platform_device_alloc会触发soc-core.c里的同名platform_driver,最终调用snd_soc_register_card完成声卡的创建注册s3c24xx_snd_device = platform_device_alloc("soc-audio", -1); 
}
2)对应的platform device
android\kernel\fusion\4.19\sound\soc\soc-core.c
/* ASoC platform driver */
static struct platform_driver soc_driver = {.driver		= {.name		= "soc-audio",.pm		= &snd_soc_pm_ops,},.probe		= soc_probe,.remove		= soc_remove,
};static int soc_probe(struct platform_deivce *pdev)
{snd_soc_register_card(card); //将Machine驱动的配置注册到ASOC的核心,触发Codec和platform driver的初始化
}2.较新的驱动里使用devm_snd_soc_register_card完成声卡的创建注册
static int s3c24xx_uda134x_probe(struct platform_device *pdev)
{struct snd_soc_card *card = &snd_soc_s3c24xx_uda134x;struct s3c24xx_uda134x *priv;priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);card->dev = &pdev->dev;snd_soc_card_set_drvdata(card, priv);ret = devm_snd_soc_register_card(&pdev->dev, card);
}int devm_snd_soc_register_card(struct device *dev, struct snd_soc_card *card)
{struct snd_soc_card **ptr;1.将release挂到card里面去devres_alloc(devm_card_release, sizeof(*ptr), GFP_KERNEL);2.注册soc cardsnd_soc_register_card(card);3.添加到dev res里统一管理devres_add(dev, ptr);
}
EXPORT_SYMBOL_GPL(devm_snd_soc_register_card);使用devres_alloc() / devres_add()管理声卡资源,在设备卸载时自动注销声卡,简化驱动代码,降低泄漏资源的风险3.指定platform和codec
1)snd_soc_card (Machine所对应的数据结构体)
static struct snd_soc_card snd_soc_s3c24xx_uda134x = {.name = "S3C24XX_UDA134X",.owner = THIS_MODULE,.dai_link = &s3c24xx_uda134x_dai_link,.num_links = 1,
};2)snd_soc_dai_link参数详细说明
static struct snd_soc_dai_link s3c24xx_uda134x_dai_link = {.name = "UDA134X",.stream_name = "UDA134X",.codec_name = "uda134x-codec", //1.对应platform_driver uda134x_codec_driver()- 控制接口(uda134×使用的是L3接口).codec_dai_name = "uda134x-hifi",  //2.对应snd_soc_dai_driver uda134x_dai() - 数据传输.cpu_dai_name = "s3c24xx-iis",   //3. 对应platform_driver s3c24xx_iis_driver() - soc端的I2S驱动.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |SND_SOC_DAIFMT_CBS_CFS,.ops = &s3c24xx_uda134x_ops,.platform_name	= "s3c24xx-iis", //4.对应platform_driver s3c24xx_iis_driver()- 使用soc的哪个部件来传送音频数据,这个是可选的,预留给需要更多部件操作的soc填充;这里其实是需要用到dma,s3c24xx-iis里面封装了dma相关操作(如果还需要初始化更多soc部件,比如timer,也可以放这里去进行初始化),所以填s3c24xx-iis
};static const struct snd_soc_ops s3c24xx_uda134x_ops = {.startup = s3c24xx_uda134x_startup,.shutdown = s3c24xx_uda134x_shutdown,.hw_params = s3c24xx_uda134x_hw_params, //初始化platform端的硬件-这里主要是设置cpu端的I2S时钟
};小结:
1)由于machine代码涉及数据的匹配,C语言代码实现起来是相当复杂的(用C语言实现面向对象),这块需要好好啃下!
2)snd_soc_dai_link中填充platform、cpu dai、codec、codec_dai,稍后Machine驱动将会利用这些名字去匹配已经在系统中注册的platform,codec,dai,这些注册的部件都是在另外相应的Platform驱动和Codec驱动的代码文件中定义的,这样看来,Machine驱动的设备初始化代码无非就是选择Platform和Codec以及dai
2、Machine代码流程图

在这里插入图片描述

重点介绍
总入口函数devm_snd_soc_register_card,基本所有核心操作均在此函数实现1、
int snd_soc_register_card(struct snd_soc_card *card){1.检查link是否都设置了soc_init_dai_link(card, link); 2.初始化card里面的链表widgets/paths/dapm_list/aux_comp_list/component_dev_listsnd_soc_initialize_card_lists(card);INIT_LIST_HEAD(&card->dai_link_list); //dai_link listINIT_LIST_HEAD(&card->rtd_list); //rtd(runtime) list3.实例化card,将里面的设置的platform/codec等,根据name去其它驱动里面找出合适的驱动或实例,挂到card结构体里面snd_soc_instantiate_card(card);
}2、
snd_soc_instantiate_card()
{1. 找到cpu/codec的component -> pcm_runtime -> cardsoc_bind_dai_link(card, &card->dai_link[i]);soc_new_pcm_runtime(card, dai_link); //构造runtimesnd_soc_rtdcom_add(rtd, rtd->cpu_dai->component); //将cpu_dai component挂到rtd中去snd_soc_rtdcom_add(rtd, codec_dais[i]->component); //将codec_dais component挂到rtd中去snd_soc_rtdcom_add(rtd, component);/ /将platform component挂到rtd中去soc_add_pcm_runtime(card, rtd); //然后挂到card->rtd_list上2. 使用构造好的card,调用ALSA接口创建alsa cardsnd_card_new()3.执行各个snd_soc_component_driver的probesoc_probe_link_components(card, rtd, order);--soc_probe_component(card, component);----component->driver->probe(component); //调用各个snd_soc_component_driver *driver的probe4.执行各个snd_soc_dai的probesoc_probe_link_dais(card, rtd, order);--soc_probe_dai(cpu_dai, order); //cpu dai probe--soc_probe_dai(rtd->codec_dais[i], order); //codec dai probe5.调用ALSA接口注册声卡snd_card_register(card->snd_card);
}

2)Platform_driver

1、s3c24xx_uda134x.c对应的Platform代码位置
android\kernel\fusion\4.19\sound\soc\samsung\s3c24xx-i2s.c2、Platform要实现以下:
1)实现数据传输 - I2S(传数据给codec);
2)platform数据处理 - 一般是将用户空间传下来的数据保存在DMA,然后启动I2S,取出DMA数据发送给codec;3、代码介绍
static struct platform_driver s3c24xx_iis_driver = {.probe  = s3c24xx_iis_dev_probe,.driver = {.name = "s3c24xx-iis",},
};
module_platform_driver(s3c24xx_iis_driver);static int s3c24xx_iis_dev_probe(struct platform_device *pdev)
{1. ioremaps3c24xx_i2s.regs = devm_ioremap_resource(&pdev->dev, res);2. dmasamsung_asoc_dma_platform_register(&pdev->dev, NULL, NULL, NULL);3.注册 snd_soc_component_driver(s3c24xx_i2s_component - 一般是dma) 和 snd_soc_dai_driver (s3c24xx_i2s_dai - i2s),挂接到snd_soc_component中去devm_snd_soc_register_component(&pdev->dev,&s3c24xx_i2s_component, &s3c24xx_i2s_dai, 1);
}2、两类driver的实现
1)platform数据处理 -dma
static const struct snd_soc_component_driver s3c24xx_i2s_component = {.name		= "s3c24xx-i2s", //这里是空的实现,因为新版本驱动把dma挪到i2s下
};2)数据传输dai -I2S
static struct snd_soc_dai_driver s3c24xx_i2s_dai = {.probe = s3c24xx_i2s_probe,.suspend = s3c24xx_i2s_suspend,.resume = s3c24xx_i2s_resume,.playback = {  //描述playback的能力.channels_min = 2,.channels_max = 2,.rates = S3C24XX_I2S_RATES,.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},.capture = {  //描述capture的能力.channels_min = 2,.channels_max = 2,.rates = S3C24XX_I2S_RATES,.formats = SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE,},.ops = &s3c24xx_i2s_dai_ops, //capture和playback对应的ops
};static const struct snd_soc_dai_ops s3c24xx_i2s_dai_ops = {.trigger	= s3c24xx_i2s_trigger, //启动数据传输.hw_params	= s3c24xx_i2s_hw_params, //设置硬件参数.set_fmt	= s3c24xx_i2s_set_fmt,.set_clkdiv	= s3c24xx_i2s_set_clkdiv,.set_sysclk	= s3c24xx_i2s_set_sysclk,
};2、s3c24xx_i2s_hw_params设置硬件参数
static int s3c24xx_i2s_hw_params(struct snd_pcm_substream *substream,struct snd_pcm_hw_params *params,struct snd_soc_dai *dai)
{struct snd_dmaengine_dai_dma_data *dma_data;dma_data = snd_soc_dai_get_dma_data(dai, substream);writel(iismod, s3c24xx_i2s.regs + S3C2410_IISMOD);
}

3)codec

1、s3c24xx_uda134x.c对应的codec代码位置:
android\kernel\fusion\4.19\sound\soc\codecs\uda134x.c2、codec要实现什么功能?
1)DAI接口的操作;- I2S (传录音数据给CPU) 谁来控制?
2)控制接口的操作;- I2C/L33、代码介绍
codec的probe
static int uda134x_codec_probe(struct platform_device *pdev)
{1. 使用regmap来read/write寄存器,linux3.1引进的操作寄存器接口struct uda134x_priv *uda134x;uda134x->regmap = devm_regmap_init(&pdev->dev, NULL, pd, &uda134x_regmap_config);2.注册snd_soc_component_driver(soc_component_dev_uda134x - L3) 和 snd_soc_dai_driver(uda134x_dai - I2S),,挂接到snd_soc_component中去return devm_snd_soc_register_component(&pdev->dev,&soc_component_dev_uda134x, &uda134x_dai, 1);
}static const struct regmap_config uda134x_regmap_config = {.reg_bits = 8,.val_bits = 8,.max_register = UDA134X_DATA1,.reg_defaults = uda134x_reg_defaults,.num_reg_defaults = ARRAY_SIZE(uda134x_reg_defaults),.cache_type = REGCACHE_RBTREE,.reg_write = uda134x_regmap_write, //寄存器的读写
};2、两类driver的实现
1)一个是dai (I2S)-数据
static struct snd_soc_dai_driver uda134x_dai = {.name = "uda134x-hifi",/* 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,
};static const struct snd_soc_dai_ops uda134x_dai_ops = {.startup	= uda134x_startup,.shutdown	= uda134x_shutdown,.hw_params	= uda134x_hw_params,.digital_mute	= uda134x_mute,.set_sysclk	= uda134x_set_dai_sysclk,.set_fmt	= uda134x_set_dai_fmt,
};2)一个是codec(L3)-控制
为啥是放在codec里面实现?L3跟codec走,不是跟platform-CPU走,理论上应该与codec一起走
static const struct snd_soc_component_driver soc_component_dev_uda134x = {.probe			= uda134x_soc_probe,.set_bias_level		= uda134x_set_bias_level,.dapm_widgets		= uda134x_dapm_widgets,.num_dapm_widgets	= ARRAY_SIZE(uda134x_dapm_widgets),.dapm_routes		= uda134x_dapm_routes,.num_dapm_routes	= ARRAY_SIZE(uda134x_dapm_routes),.suspend_bias_off	= 1,.idle_bias_on		= 1,.use_pmdown_time	= 1,.endianness		= 1,.non_legacy_dai_naming	= 1,
};
使用DAPM/widgets/routes/kcontrol这套框架来进行控制,DAPM相关后续单独介绍
http://www.lqws.cn/news/521173.html

相关文章:

  • Linux RDMA网络配置手册
  • 2026-软件工程-《软件质量测试与保证》-期末复习—习题汇总
  • 【编程基本功】Win11中Git安装配置全攻略,包含Git以及图形化工具TortoiseGit
  • Qt开发1--Qt概述,安装,创建第一个Qt项目
  • 2 Qt中的空窗口外观设置和常用的基础部件
  • 【笔记】Docker 配置阿里云镜像加速(公共地址即开即用,无需手动创建实例)
  • C#高级:Winform桌面开发中DataGridView的详解(新)
  • 在 GitLab CI 中配置多任务
  • Tomcat
  • 用Rust写平衡三进制乘法器
  • Hoare逻辑与分离逻辑:从程序验证到内存推理的演进
  • ES10(ES2019)新特性整理
  • 华为运维工程师面试题(英语试题,内部资料)
  • mysql 5.1 升级 mysql 5.7 升级 mariadb10
  • RabbitMq中使用自定义的线程池
  • 基于R语言的亚组分析与森林图绘制1
  • 微算法科技融合Grover算法与统一哈希函数的混合经典-量子算法技术,可在多领域高效提升文本处理效率
  • win11搭建Python开发环境指南
  • MAC、IP地址、TCP、UDP、SSL、OSI模型
  • 【MCP 实战4-1】开发 OpenSearch MCP server
  • 南北差异之——理解业务和理解产品
  • spring项目启动sheel脚本
  • 惯性导航——陀螺仪
  • 解决git pull,push 每次操作输入账号密码问题
  • 基于STM32的个人健康助手的设计
  • 鸿蒙应用开发中的数据存储:SQLite与Preferences全面解析
  • 基于 opencv+yolov8+easyocr的车牌追踪识别
  • Kotlin 协程:全面解析与深度探索
  • 工业“三体”联盟:ethernet ip主转profinet网关重塑设备新规则
  • python哈尔滨中心医院用户移动端