文章目錄
- 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相關后續單獨介紹