這是現(xiàn)有 ALSA conf 拓?fù)涓袷街系母呒?jí)關(guān)鍵字?jǐn)U展,旨在:
- 通過(guò)提供高級(jí)的 “classes” 來(lái)簡(jiǎn)化 ALSA conf 拓?fù)涠x。通過(guò)這種方式,拓?fù)湓O(shè)計(jì)者可以為經(jīng)常定義的對(duì)象編寫更少的配置。
- 允許簡(jiǎn)單地重用對(duì)象。定義一次并重用(如 M4),能夠更改默認(rèn)的對(duì)象配置屬性。
- 允許數(shù)據(jù)類型和值驗(yàn)證。現(xiàn)在還沒有這樣做,并且經(jīng)常出現(xiàn)在固件錯(cuò)誤報(bào)告中。
要素
典型的 2.0 配置文件由以下組件組成:
- 類(Classes)
- 對(duì)象(Objects)
- 參數(shù)(Arguments)
- 條件包含(Conditional includes)
類
今天的拓?fù)溆幸恍┏R姷亩x,常常通過(guò)對(duì)配置進(jìn)行細(xì)微的改變來(lái)重用,例如 widgets(組件)、流水線、dais、pcm 和 controls。拓?fù)?2.0 引入了可重用的類定義的概念,你可以使用它來(lái)創(chuàng)建常用的拓?fù)鋵?duì)象。類通過(guò)一個(gè)新的關(guān)鍵字 Class 來(lái)定義。
類定義始終以 Class 關(guān)鍵字開頭,后跟兩個(gè)節(jié)點(diǎn)。第一個(gè)節(jié)點(diǎn)包含類的組,第二個(gè)節(jié)點(diǎn)包含類名。如:
Class.Base.data {}
請(qǐng)注意,‘.’ 是 alsaconf 語(yǔ)法中的節(jié)點(diǎn)分隔符。在上面的行中,Base 是類的組,data 是類名。目前,alsatplg 編譯器支持以下類組:widget、pipeline、DAI、control 和 base。大多數(shù)常用的拓?fù)鋵?duì)象都可以分為這些組之一。如果需要新的類組,則應(yīng)更新 alsatplg 編譯器以添加對(duì)其的支持。
類要素
簡(jiǎn)約的類定義應(yīng)包含以下內(nèi)容:
- 使用關(guān)鍵字
DefineAttribute聲明一個(gè)或多個(gè)屬性。屬性是用于描述對(duì)象的參數(shù)。比如:
DefineAttribute."name" {
type "string"
}
“name” 是字符串類型的屬性。
- 具有構(gòu)造函器數(shù)組和唯一屬性名稱的基本屬性限定符。屬性限定符應(yīng)該在類定義的
attributes {}節(jié)點(diǎn)中聲明。
# attribute qualifiers
attributes {
#
# This tells the compiler how to construct the object's name. For example, if the
# name attribute is set to "EQIIR-Coefficients", the object name will be
# constructed as "class_name.EQIIR-Coefficients"
#
!constructor [
"name"
]
#
# objects of the same class instantiated within the same alsaconf node have unique
# name attribute
#
unique "name"
}
一個(gè)簡(jiǎn)單的類
以下示例演示了具有兩個(gè)屬性和屬性限定符的簡(jiǎn)單類定義:
Class.Base."data" {
# name for the data object
DefineAttribute."name" {
type "string"
}
# bytes data
DefineAttribute."bytes" {
type "string"
}
# attribute qualifiers
attributes {
#
# This tells the compiler how to construct the object's name. For example, if the
# name attribute is set to "EQIIR-Coefficients", the object name will be
# constructed as "data.EQIIR-Coefficients"
#
!constructor [
"name"
]
#
# data objects instantiated within the same alsaconf node should have unique
# name attribute
#
unique "name"
}
}
data 類定義屬于 Base 類組,它包含兩個(gè)屬性,name 和 bytes,兩者的類型都是 string。除非另有指定,默認(rèn)情況下所有屬性都是 integer 類型的,如上例所示。目前,拓?fù)?2.0 僅支持 integer 和 string 類型的屬性。
屬性限定符用于描述如何從類定義實(shí)例化對(duì)象,并驗(yàn)證屬性值。
在上面的定義中,constructor 數(shù)組告訴編譯器如何構(gòu)建對(duì)象的名稱。使用名稱 EQIIR-Coefficients 實(shí)例化的 data 對(duì)象將被賦予名稱 data.EQIIR-Coefficients,即類名后跟 ‘.’,后跟由 ‘.’ 分隔的構(gòu)造器屬性值。
unique 限定符指示在同一個(gè) alsaconf 節(jié)點(diǎn)中實(shí)例化的多個(gè) data 對(duì)象的 name 屬性應(yīng)該具有唯一的值。如果在同一個(gè) alsaconf 節(jié)點(diǎn)內(nèi)實(shí)例化具有相同 name 屬性的兩個(gè) data 對(duì)象,則 不會(huì) 發(fā)生錯(cuò)誤,但兩個(gè)對(duì)象實(shí)例將被合并。此外,第二個(gè)實(shí)例中的屬性值將覆蓋第一個(gè)實(shí)例中的屬性值。因此,拓?fù)渚帉懻哂胸?zé)任確保同一父節(jié)點(diǎn)中的相同類的多個(gè)實(shí)例具有不同的唯一屬性值。
讓我們考慮另一個(gè)類定義示例,屬于類組 Widget 的 pga widget (在 tools/topology/topology2/include/components/volume.conf 文件中定義):
Class.Widget."pga" {
#
# Pipeline ID for the pga widget object
#
DefineAttribute."index" {}
#
# pga object instance
#
DefineAttribute."instance" {}
# attribute qualifiers
attributes {
#
# The PGA widget name is constructed using the index and instance
# attributes. For ex: "pga.1.1" or "pga.10.2" etc.
#
!constructor [
"index"
"instance"
]
#
# pga widget objects instantiated within the same alsaconf node should have unique
# instance attribute
#
unique "instance"
}
}
請(qǐng)注意,pga 對(duì)象名由類名 pga 后跟兩個(gè)屬性值,index 和 instance 構(gòu)成,比如,pga.1.1。由于屬性定義未指定類型,默認(rèn)情況下,這兩個(gè)屬性的類型都為 integer。在實(shí)踐中,唯一的 instance 屬性也應(yīng)該是構(gòu)造器的一部分。
屬性默認(rèn)值
擴(kuò)展類定義以為其屬性提供默認(rèn)值是可選的。讓我們?yōu)?pga 類添加一個(gè)類型為 string 的屬性 uuid,并給它一個(gè)默認(rèn)值:
Class.Widget."pga" {
#
# Pipeline ID for the pga widget object
#
DefineAttribute."index" {}
#
# pga object instance
#
DefineAttribute."instance" {}
DefineAttribute."uuid" {
type "string"
}
# attribute qualifiers
attributes {
#
# The PGA widget name is constructed using the index and instance
# attributes. For ex: "pga.1.1" or "pga.10.2" etc.
#
!constructor [
"index"
"instance"
]
#
# pga widget objects instantiated within the same alsaconf node should have unique
# instance attribute
#
unique "instance"
}
# default attribute values
uuid "7e:67:7e:b7:f4:5f:88:41:af:14:fb:a8:bd:bf:86:82"
}
所有 pga 對(duì)象將自動(dòng)獲得默認(rèn)的 uuid,如上面在類定義中指定的那樣。ALSA 拓?fù)?2.0 類定義的布局大體為 屬性聲明-對(duì)象構(gòu)造器數(shù)組聲明和屬性限定符-屬性默認(rèn)值。
高級(jí)屬性限定符
除了強(qiáng)制性的基本屬性限定符外,還可以使用以下的高級(jí)關(guān)鍵字限定類定義中的屬性:
- Mandatory:應(yīng)在對(duì)象實(shí)例中給由 mandatory 限定的屬性提供值,否則 alsatplg 編譯器將報(bào)錯(cuò)。類定義中具有默認(rèn)值的屬性不需要被限定為 mandatory。另請(qǐng)注意,構(gòu)造器數(shù)組中的屬性默認(rèn)為 mandatory 的,因?yàn)樗鼈兪菢?gòu)建對(duì)象名稱所必需的。
- Immutable:在類定義中設(shè)置的屬性值,不能在對(duì)象實(shí)例中修改。
- Deprecated:已棄用且不應(yīng)在對(duì)象實(shí)例中設(shè)置的屬性。
- Automatic:具體的值由 alsatplg 編譯器計(jì)算的屬性。
讓我們?cè)?pga 類定義中添加一些額外的屬性和高級(jí)限定符:
Class.Widget."pga" {
# attribute definitions
DefineAttribute.instance {
type "integer"
}
DefineAttribute.index {
type "integer"
}
DefineAttribute."type" {
type "string"
}
DefineAttribute."uuid" {
type "string"
}
DefineAttribute."preload_count" {}
# attribute qualifiers
attributes {
#
# The PGA widget name is constructed using the index and instance attributes.
# For ex: "pga.1.1" or "pga.10.2" etc.
#
!constructor [
"index"
"instance"
]
#
# immutable attributes should be given default values and cannot be modified in the object instance
#
!immutable [
"uuid"
"type"
]
#
# deprecated attributes should not be added in the object instance
#
!deprecated [
"preload_count"
]
#
# pga widget objects instantiated within the same alsaconf node should have
# unique instance attribute
#
unique "instance"
}
# default attribute values
type "pga"
uuid "7e:67:7e:b7:f4:5f:88:41:af:14:fb:a8:bd:bf:86:82"
}
除了 unique 屬性限定符外,包括構(gòu)造器 constructor 在內(nèi)的其它屬性限定符,在類定義的 attributes {} 節(jié)點(diǎn)中,屬性限定符前面加感嘆號(hào)(!),后面跟屬性名數(shù)組,在屬性名數(shù)組中,不同的屬性名用空白符隔開。
自動(dòng)屬性
在某些情況下,屬性值取決于其它屬性值,并且需要在構(gòu)建時(shí)計(jì)算。在類定義中這種屬性用 automatic 關(guān)鍵字修飾。請(qǐng)參閱 buffer 以獲取完整的類定義。
Class.Widget."buffer" {
# Other attributes skipped for simplicity.
#
# Buffer size in bytes. Will be calculated based on the parameters of the pipeline to in which the
# buffer object belongs
#
DefineAttribute."size" {
# Token reference and type
token_ref "sof_tkn_buffer.word"
}
attributes {
#
# size attribute value for buffer objects is computed in the compiler
#
!automatic [
"size"
]
}
}
在上面的示例中,buffer 的 size 屬性值基于 buffer 所屬的流水線的參數(shù)計(jì)算。目前,alsatplg 編譯器僅支持計(jì)算 buffer 對(duì)象的自動(dòng)屬性 size。如有必要,應(yīng)在 alsatplg 編譯器中添加對(duì)新的類定義中的自動(dòng)屬性的支持。
在目前版本的 alsatplg (1.2.11) 編譯器源碼中,沒有找到對(duì)于 automatic 和 deprecated 屬性修飾符的處理,只找到了對(duì)于 constructor、immutable、mandatory 和 unique 屬性修飾符的處理。
屬性約束
拓?fù)?2.0 的重要特性之一是驗(yàn)證提供給對(duì)象的值。這通過(guò)給屬性定義添加約束來(lái)實(shí)現(xiàn)??梢允褂?constraints 關(guān)鍵字來(lái)給屬性添加約束:
DefineAttribute."foo" {
constraints {}
}
目前,支持三種類型的約束:
- min:屬性最小值,僅適用于整數(shù)類型的屬性。
-
max:屬性最大值,僅適用于整數(shù)類型的屬性。
比如,pga 類定義可以擴(kuò)展為具有屬性ramp_step_ms,其 min 和 max 值如下:
DefineAttribute."ramp_step_ms" {
constraints {
min 200
max 500
}
}
-
valid values:可接受的人類可讀值的數(shù)組,僅適用于字符串類型的屬性。
例如,可以給 pga 類添加具有預(yù)定義值的ramp_step_type屬性,如下面這樣:
DefineAttribute."ramp_step_type" {
type "string"
constraints {
!valid_values [
"linear"
"log"
"linear_zc"
"log_zc"
]
}
}
當(dāng)使用不屬于 valid_values 數(shù)組的 ramp_step_type 值實(shí)例化 pga 類時(shí),alsatplg 編譯器會(huì)報(bào)錯(cuò)并輸出有效值列表。
具有 token 引用的屬性
通常,許多對(duì)象包含由元組數(shù)組集合組成的私有數(shù)據(jù)部分。類定義中的某些屬性可能需要打包到元組數(shù)組中。此類屬性通過(guò) token_ref 節(jié)點(diǎn)進(jìn)行標(biāo)識(shí),該節(jié)點(diǎn)包含屬性應(yīng)構(gòu)建到的元組數(shù)組的名稱。例如,pga 類中的 ramp_step_ms 和 ramp_step_type 屬性都需要添加到元組數(shù)組中。因此,它們包含 token_ref 節(jié)點(diǎn),其值為 volume.word,指示屬性應(yīng)與 word 類型的 volume 元組數(shù)組打包在一起:
#
# Volume ramp step in milliseconds
#
DefineAttribute."ramp_step_ms" {
# Token set reference name
token_ref "volume.word"
constraints {
min 200
max 500
}
}
DefineAttribute."ramp_step_type" {
type "string"
# Token set reference name
token_ref "volume.word"
constraints {
!valid_values [
"linear"
"log"
"linear_zc"
"log_zc"
]
}
}
有時(shí),屬性的 valid_values 值可能需要從人類可讀的值轉(zhuǎn)換為整數(shù)元組值,以便內(nèi)核驅(qū)動(dòng)程序可以正確地解析它們。在上面的示例中,ramp_step_type 的有效值被定義為人類可讀的字符串值,為 linear 和 log 等。這些值在添加到元組數(shù)組之前會(huì)被轉(zhuǎn)換為元組值 (0, 1, 等)。
DefineAttribute."ramp_step_type" {
type "string"
# Token set reference name
token_ref "volume.word"
constraints {
!valid_values [
"linear"
"log"
"linear_zc"
"log_zc"
]
!tuple_values [
0
1
2
3
]
}
}
完整的類定義
將所有內(nèi)容放在一起,以下示例演示了 pga Widget 類的完整定義 (sof/tools/topology/topology2/include/components/volume.conf):
Class.Widget."pga" {
#
# Pipeline ID for the pga widget object
#
DefineAttribute."index" {}
#
# pga object instance
#
DefineAttribute."instance" {}
#include common component definition
<include/components/widget-common.conf>
#
# Bespoke attributes for PGA
#
#
# Volume ramp step type. The values provided will be translated to integer values
# as specified in the tuple_values array.
# For example: "linear" is translated to 0, "log" to 1 etc.
#
DefineAttribute."ramp_step_type" {
type "string"
# Token set reference name
token_ref "volume.word"
constraints {
!valid_values [
"linear"
"log"
"linear_zc"
"log_zc"
]
!tuple_values [
0
1
2
3
]
}
}
#
# Volume ramp step in milliseconds
#
DefineAttribute."ramp_step_ms" {
# Token set reference name
token_ref "volume.word"
}
# Attribute categories
attributes {
#
# The PGA widget name would be constructed using the index and instance attributes.
# For ex: "pga.1.1" or "pga.10.2" etc.
#
!constructor [
"index"
"instance"
]
#
# immutable attributes cannot be modified in the object instance
#
!immutable [
"uuid"
"type"
]
#
# deprecated attributes should not be added in the object instance
#
!deprecated [
"preload_count"
]
#
# pga widget objects instantiated within the same alsaconf node must have unique
# instance attribute
#
unique "instance"
}
#
# pga widget mixer controls
#
Object.Control {
# volume mixer control
mixer."1" {
#Channel register and shift for Front Left/Right
Object.Base.channel.1 {
name "fl"
shift 0
}
Object.Base.channel.2 {
name "fr"
}
Object.Base.ops.1 {
name "ctl"
info "volsw"
#256 binds the mixer control to volume get/put handlers
get 256
put 256
}
max 32
}
# mute switch control
mixer."2" {
Object.Base.channel.1 {
name "flw"
reg 2
shift 0
}
Object.Base.channel.2 {
name "fl"
reg 2
shift 1
}
Object.Base.channel.3 {
name "fr"
reg 2
shift 2
}
Object.Base.channel.4 {
name "frw"
reg 2
shift 3
}
Object.Base.ops.1 {
name "ctl"
info "volsw"
#259 binds the mixer control to switch get/put handlers
get "259"
put "259"
}
#max 1 indicates switch type control
max 1
}
}
# Default attribute values for pga widget
type "pga"
uuid "7e:67:7e:b7:f4:5f:88:41:af:14:fb:a8:bd:bf:86:82"
no_pm "true"
ramp_step_type "linear"
ramp_step_ms 400
}
對(duì)象
對(duì)象用于實(shí)例化同一個(gè)類的多個(gè)實(shí)例,以避免重復(fù)的公共屬性定義。對(duì)象使用新的關(guān)鍵字 Object 后跟三個(gè)節(jié)點(diǎn)來(lái)實(shí)例化:
Object.Widget.pga."1" {}
這些節(jié)點(diǎn)指代以下元素:
- 對(duì)象的類所屬的類組。在這個(gè)例子中,類屬于
Widget類組。 - 類名稱。這里是
pga。 - 唯一的屬性值。這是在類定義中,被限定為
unique的屬性的值。這里是instance。在 ALSA 拓?fù)?2.0 中,對(duì)象不必須有一個(gè)字符串形式地對(duì)象名稱,或者說(shuō),對(duì)象名稱本身由對(duì)象的特定一個(gè)或幾個(gè)屬性值組成,這與 C++ 或 Java 這種面向?qū)ο缶幊陶Z(yǔ)言種的對(duì)象定義不同。這個(gè)部分通常由構(gòu)造器 constructor 數(shù)組中各個(gè)屬性的值組成。對(duì)象實(shí)例化時(shí),Object后只有unique的屬性。這要求任何類定義中,都需要有且僅有一個(gè)unique的屬性。unique的屬性是否要放進(jìn)類的構(gòu)造器數(shù)組中可選,既可以放進(jìn)去,也可以不放進(jìn)去。
使用 完整的類定義 一節(jié)中所述的 pga 類定義,可以通過(guò)以下方式實(shí)例化一個(gè) pga Widget 對(duì)象:
Object.Widget.pga."1" {
index 5
}
其中 1 是 pga 類定義中唯一的屬性 instance 的值,index 屬性的值為 5。由于類定義中不包含其它的 mandatory 屬性,因此上面的實(shí)例完全有效。
重要
不需要在對(duì)象實(shí)例中復(fù)制公共屬性值。對(duì)象自動(dòng)從其類定義中繼承屬性的默認(rèn)值。
修改默認(rèn)屬性
類定義中具有默認(rèn)值的屬性,可以通過(guò)在對(duì)象實(shí)例中指定新值來(lái)覆蓋:
Object.Widget.pga."1" {
index 5
ramp_step_ms 300
}
上面的對(duì)象,將類定義中 ramp_step_ms 的默認(rèn)值 200 ms 覆蓋為新值 300 ms。
類中的對(duì)象
類定義中還可以選擇包含需要為類對(duì)象的每個(gè)實(shí)例實(shí)例化的子對(duì)象。例如,pga Widget 對(duì)象通常始終包含音量混音器 Control。混音器 Control 類定義如下:
Class.Control."mixer" {
#
# Pipeline ID for the mixer object
#
DefineAttribute."index" {}
#
# Instance of mixer object in the same alsaconf node
#
DefineAttribute."instance" {}
#
# Mixer name. A mixer object is included in the built topology only if it is given a
# name
#
DefineAttribute."name" {
type "string"
}
#
# Max volume setting
#
DefineAttribute."max" {}
DefineAttribute."invert" {
type "string"
constraints {
!valid_values [
"true"
"false"
]
}
}
# use mute LED
DefineAttribute."mute_led_use" {
token_ref "sof_tkn_mute_led.word"
}
# LED direction
DefineAttribute."mute_led_direction" {
token_ref "sof_tkn_mute_led.word"
}
#
# access control for mixer
#
DefineAttribute."access" {
type "compound"
constraints {
!valid_values [
"read_write"
"tlv_read_write"
"read"
"write"
"volatile"
"tlv_read"
"tlv_write"
"tlv_command"
"inactive"
"lock"
"owner"
"tlv_callback"
]
}
}
attributes {
#
# The Mixer object name is constructed using the index and instance arguments.
# For ex: "mixer.1.1" or "mixer.10.2" etc.
#
!constructor [
"index"
"instance"
]
!mandatory [
"max"
]
#
# mixer control objects instantiated within the same alsaconf node should have unique
# index attribute
#
unique "instance"
}
# Default attribute values for mixer control
invert "false"
mute_led_use 0
mute_led_direction 0
}
可以將混音器 Control 對(duì)象添加到 pga Widget 類定義中:
Class.Widget."pga" {
# Attributes, qualifiers and default values are skipped for simplicity.
# Refer to the complete class definition in "Complete Class Definition" for details
# volume control for pga widget
Object.Control.mixer."1" {
name "My Volume Control"
max 32
}
}
}
混音器 Control My Volume Control 將以編程方式添加到所有 pga 對(duì)象中。
對(duì)象屬性繼承
在上面的對(duì)象實(shí)例化中需要注意的一件事是,混音器對(duì)象有兩個(gè)強(qiáng)制性(構(gòu)造器)屬性,index 和 instance,但實(shí)例化中卻少 index 屬性值。這是因?yàn)榛煲羝?Control 對(duì)象在實(shí)例化時(shí),從其父 pga 對(duì)象繼承了 index 屬性值。例如,考慮如下的 pga 對(duì)象實(shí)例:
Object.Widget.pga.1 {
index 5
}
pga 類定義中的混音器 Control 對(duì)象繼承索引值 5。僅當(dāng)子對(duì)象的類定義與其父類(子對(duì)象所屬的類,而不是類繼承)定義具有同名屬性時(shí),才會(huì)發(fā)生繼承。對(duì)于混音器 Control 類和 pga Widget 類,共同具有的屬性為 index。
設(shè)置子對(duì)象的屬性
讓我們?cè)俅慰紤]具有混音器 Control 對(duì)象的 pga 類定義:
Class.Widget."pga" {
# Attributes, qualifiers and default values are skipped for simplicity.
# Please refer to the complete class definition above for details
# volume control for pga widget
Object.Control.mixer."1" {
name "My Volume Control"
max 32
}
}
}
請(qǐng)注意,在 pga Widget 類定義中,設(shè)置了混音器 Control 對(duì)象的名稱。但是,理想情況下,每當(dāng)實(shí)例化新的 pga Widget 對(duì)象時(shí),我們都希望為混音器 Control 子對(duì)象指定一個(gè)新名稱。可以這樣做:
Object.Widget.pga."1" {
index 5
# volume control'
Object.Control.mixer."1" {
name "My Control Volume 5"
}
}
}
現(xiàn)在,混音器 Control 對(duì)象被賦予了名稱 My Control Volume 5。
嵌套對(duì)象
對(duì)象還可以實(shí)例化為其它對(duì)象實(shí)例中的子對(duì)象。例如,可以在實(shí)例化期間將開關(guān) Control 添加到 pga Widget 對(duì)象:
Object.Widget.pga."1" {
index 5
# volume control
Object.Control.mixer."1" {
name "My Control Volume 5"
}
}
# mute control
Object.Control.mixer."2" {
name "Mute Switch Control"
max 1
}
}
}
請(qǐng)注意兩個(gè)混音器 Control 對(duì)象的 unique 屬性如何不同以保持混音器實(shí)例的唯一性。為對(duì)象實(shí)例添加嵌套對(duì)象與上面的設(shè)置子對(duì)象的屬性類似,兩者的區(qū)別在于,嵌套的對(duì)象是沒有出現(xiàn)在其父類定義中的。
遞歸對(duì)象屬性繼承
對(duì)象可以嵌套在對(duì)象內(nèi),而后者又嵌套在其它對(duì)象內(nèi)。在這種情況下,屬性值可以從頂層父對(duì)象一路繼承。例如,考慮以下的 volume-playback 流水線的類定義:
Class.Pipeline."volume-playback" {
# Other attributes and qualifiers ommitted for simplicity
DefineAttribute."index" {}
DefineAttribute."format" {
type "string"
}
# pipeline objects
Object.Widget {
# Other objects ommitted for simplicity
pga."1" {}
}
}
請(qǐng)注意,上面的 pga Widget 對(duì)象沒有 index 屬性值。volume-playback 類對(duì)象像這樣實(shí)例化:
Object.Pipeline.volume-playback.1 {
index 1
format s24le
}
這確保 volume-playback 對(duì)象中的所有子對(duì)象都將從它繼承 index 屬性值,因此 pga Widget 對(duì)象將具有相同的 index 值。按照同樣的規(guī)則,pga Widget 對(duì)象中的混音器 Control 對(duì)象也將具有相同的 index 屬性值 1。
在父對(duì)象樹的深處設(shè)置子對(duì)象屬性
在 設(shè)置子對(duì)象的屬性 一節(jié)中,我們看到我們可以從父對(duì)象實(shí)例設(shè)置子對(duì)象的屬性值。例如,可以從 pga Widget 對(duì)象實(shí)例設(shè)置混音器 Control 對(duì)象的名稱。這可以進(jìn)一步擴(kuò)展,可以從 pga 對(duì)象的父對(duì)象設(shè)置混音器 Control 名稱??紤]上一節(jié)中的 volume-playback 對(duì)象實(shí)例。我們可以為 pga 對(duì)象設(shè)置其混音器 Control 名稱,如下所示:
Object.Pipeline.volume-playback.1 {
index 1
format s24le
Object.Widget.pga.1 {
Object.Control.mixer.1 {
name "My Control Volume 1"
}
}
}
頂層配置文件中的參數(shù)
參數(shù)用于傳遞構(gòu)建時(shí)參數(shù),它們可用于從同一個(gè)配置文件構(gòu)建出多個(gè)二進(jìn)制文件??紤]以下具有兩個(gè)流水線的頂層拓?fù)渑渲梦募?/p>
# arguments
@args [ DYNAMIC_PIPELINE ]
@args.DYNAMIC_PIPELINE {
type integer
default 0
}
Object.Pipeline {
volume-playback.1 {
dynamic_pipeline $DYNAMIC_PIPELINE
index 1
Object.Widget.pipeline.1 {
stream_name 'dai.HDA.0.playback'
}
Object.Widget.host.playback {
stream_name 'Passthrough Playback 0'
}
Object.Widget.pga.1 {
Object.Control.mixer.1 {
name '1 My Playback Volume'
}
}
format s24le
}
volume-playback.3 {
dynamic_pipeline $DYNAMIC_PIPELINE
index 3
Object.Widget.pipeline.1 {
stream_name 'dai.HDA.2.playback'
}
Object.Widget.host.playback {
stream_name 'Passthrough Playback 1'
}
Object.Widget.pga.1 {
Object.Control.mixer.1 {
name '3 My Playback Volume'
}
}
format s24le
}
}
在這個(gè)例子中,volume-playback 對(duì)象中的 dynamic_pipeline 屬性值,從編譯拓?fù)涠M(jìn)制文件時(shí),提供的 -DDYNAMIC_PIPELINE=1 或 -DDYNAMIC_PIPELINE=0 選項(xiàng)給 DYNAMIC_PIPELINE 參數(shù)的值中擴(kuò)展。
注意
alsatplg 編譯器僅解析機(jī)器拓?fù)湮募许攲庸?jié)點(diǎn)定義的參數(shù)。
包含
構(gòu)建頂層配置文件時(shí),它應(yīng)該包含正在實(shí)例化的所有對(duì)象的類定義,否則編譯器將報(bào)錯(cuò),指出缺少類定義。所有路徑均相對(duì)于環(huán)境變量 ALSA_CONFIG_DIR 指定的目錄。你可以像下面這樣為依賴指定包含路徑:
<searchdir:include>
<searchdir:include/controls>
<searchdir:include/components>
像下面這樣包含類定義:
<dai.conf>
<data.conf>
<pcm.conf>
<volume-playback.conf>
簡(jiǎn)單的機(jī)器拓?fù)?/h2>
一個(gè)機(jī)器拓?fù)渫ǔS梢韵虏糠纸M成:
- 包含路徑,指向類定義的搜索目錄
- Conf 文件 Includes,包含類定義
- 參數(shù)
- 流水線對(duì)象
- BE DAI 鏈接對(duì)象
- PCM 對(duì)象
- 頂層流水線連接
讓我們考慮一個(gè)簡(jiǎn)單的機(jī)器拓?fù)渑渲?/strong>文件,它包含一個(gè) volume-playback 流水線,一個(gè) HDA 類型的 DAI 鏈接,一個(gè)播放 PCM,和頂層連接:
# Include paths
<searchdir:include>
<searchdir:include/common>
<searchdir:include/components>
<searchdir:include/controls>
<searchdir:include/dais>
<searchdir:include/pipelines>
# Include class definitions
<vendor-token.conf>
<tokens.conf>
<volume-playback.conf>
<dai.conf>
<data.conf>
<pcm.conf>
<pcm_caps.conf>
<fe_dai.conf>
<hda.conf>
<hw_config.conf>
<manifest.conf>
<route.conf>
# arguments
@args.DYNAMIC_PIPELINE {
type integer
default 0
}
# DAI definition
Object.Dai {
HDA.0 {
name 'Analog Playback and Capture'
id 4
default_hw_conf_id 4
Object.Base.hw_config.HDA0 {}
Object.Widget.dai.1 {
direction playback
index 1
type dai_in
stream_name 'Analog Playback and Capture'
period_sink_count 0
period_source_count 2
format s32le
}
}
}
# Pipeline Definition
Object.Pipeline {
volume-playback.1 {
dynamic_pipeline $DYNAMIC_PIPELINE
index 1
Object.Widget.pipeline.1 {
stream_name 'dai.HDA.0.playback'
}
Object.Widget.host.playback {
stream_name 'Passthrough Playback 0'
}
Object.Widget.pga.1 {
Object.Control.mixer.1 {
name '1 My Playback Volume'
}
}
format s24le
}
}
# PCM Definitions
Object.PCM {
pcm.0 {
name 'HDA Analog'
Object.Base.fe_dai.'HDA Analog' {}
Object.PCM.pcm_caps.playback {
name 'Passthrough Playback 0'
formats 'S24_LE,S16_LE'
}
direction playback
id 0
}
}
# Top-level pipeline connection
# Buffer.1. -> dai.HDA.1.playback
Object.Base.route.1 {
source 'buffer.1.1'
sink 'dai.HDA.1.playback'
}
注意上面的配置文件,只包含 volume-playback 流水線中的緩沖區(qū) Widget buffer.1.1 和 dai Widget dai.HDA.1.playback 之間的頂層路由。volume-playback 流水線中的 widgets 之間的連接在類定義中定義。
讓我們深入了解一下 volume-playback 流水線的類定義,看看類定義中包含的路由對(duì)象。關(guān)于完整的類定義,請(qǐng)參閱 volume-playback。
Class.Pipeline."volume-playback" {
# pipeline attributes skipped for simplicity
attributes {
# pipeline name is constructed as "volume-playback.1"
!constructor [
"index"
]
!mandatory [
"format"
]
!immutable [
"direction"
]
#
# volume-playback objects instantiated within the same alsaconf node should have
# unique instance attribute
#
unique "instance"
}
# Widget objects that constitute the volume-playback pipeline
Object.Widget {
pipeline."1" {}
host."playback" {
type "aif_in"
}
buffer."1" {
periods 2
caps "host"
}
pga."1" {
Object.Control.mixer.1 {
Object.Base.tlv."vtlv_m64s2" {
Object.Base.scale."m64s2" {}
}
}
}
buffer."2" {
periods 2
caps "dai"
}
}
# Pipeline connections.
# The index attribute values for the source/sink widgets will be populated
# when the route objects are built
Object.Base {
route."1" {
source "host..playback"
sink "buffer..1"
}
route."2" {
source "buffer..1"
sink "pga..1"
}
route."3" {
source "pga..1"
sink "buffer..2"
}
}
# Default attribute values
direction "playback"
time_domain "timer"
period 1000
channels 2
rate 48000
priority 0
core 0
frames 0
mips 5000
}
流水線類定義相當(dāng)容易理解,除了路由對(duì)象實(shí)例外。我們?cè)龠M(jìn)一步分析一下。 路由類定義如下:
Class.Base."route" {
# sink widget name
DefineAttribute."sink" {
type "string"
}
# source widget name for route
DefineAttribute."source" {
type "string"
}
# control name for the route
DefineAttribute."control" {
type "string"
}
#
# Pipeline ID of the pipeline the route object belongs to
#
DefineAttribute."index" {}
# unique instance for route object in the same alsaconf node
DefineAttribute."instance" {}
attributes {
!constructor [
"instance"
]
!mandatory [
"source"
"sink"
]
#
# route objects instantiated within the same alsaconf node should have unique
# index attribute
#
unique "instance"
}
}
請(qǐng)注意,一個(gè)路由對(duì)象應(yīng)具有 instance、source 和 sink 屬性。
讓我們?cè)俅慰紤] volume-playback 類中的路由對(duì)象:
Object.Base {
route."1" {
source "host..playback"
sink "buffer..1"
}
route."2" {
source "buffer..1"
sink "pga..1"
}
route."3" {
source "pga..1"
sink "buffer..2"
}
}
請(qǐng)注意,source 和 sink 屬性是為所有路由定義的。比如,第二個(gè)路由對(duì)象 Object.Base.route.2,其 sink 屬性值為 pga..1。參閱 一個(gè)簡(jiǎn)單的類定義 中的 pga Widget 類定義,我們知道 pga Widget 對(duì)象的構(gòu)造器具有兩個(gè)屬性,index 和 instance。通過(guò)查看 widgets 列表,我們知道 volume-playback 類中的 pga Widget 實(shí)例為 1。但流水線中的 pga Widget 的 index 屬性值未知。它只能從頂級(jí)拓?fù)渑渲梦募性O(shè)置,如 簡(jiǎn)單機(jī)器拓?fù)?/a> 中所示。因此,在類定義中,index 屬性留空。當(dāng)構(gòu)建路由對(duì)象時(shí),alsatplg 編譯器將使用適當(dāng)?shù)闹堤畛?index 屬性。對(duì)于上面的機(jī)器拓?fù)洌瑢⑹褂谜_的流水線 ID 構(gòu)建路由對(duì)象 Object.base.route.2,如下所示:
Object.base.route.2 {
source "buffer.1.1"
sink "pga.1.1"
}
目前,alsatplg 只能為路由對(duì)象 source 和 sink 屬性填寫屬性值。如果需要,可以將此功能擴(kuò)展到其它類型的對(duì)象。
路由中設(shè)置 source 和 sink 屬性,指定 Widget 的方法是,類名后跟對(duì)象名,對(duì)象名的構(gòu)成為,以點(diǎn)號(hào)分割的對(duì)象所屬類定義中構(gòu)造器 constructor 數(shù)組中各屬性的值,如 host 類定義 (sof/tools/topology/topology2/include/components/host.conf):
Class.Widget."host" {
#
# Attributes for host widget
#
#
# Pipeline ID that the host widget belongs to
#
DefineAttribute."index" {}
#
# Host direction
#
DefineAttribute."direction" {
type "string"
constraints {
!valid_values [
"playback"
"capture"
]
}
}
. . . . . .
attributes {
#
# host objects instantiated within the same alsaconf node must have unique value for
# direction attribute
#
unique "direction"
#
# The host object name is constructed using the index and direction arguments.
# E.g. "host.0.capture" or "host.2.playback" etc
#
!constructor [
"index"
"direction"
]
路由中指定的 host 屬性為 host.$index.playback。
機(jī)器拓?fù)渑渲弥校鱾€(gè)組成部分各具有什么樣的含義?它們之間有什么關(guān)系,是如何關(guān)聯(lián)起來(lái)的?
條件包含
條件包含允許從同一輸入配置文件構(gòu)建多個(gè)拓?fù)涠M(jìn)制文件。例如,讓我們考慮 HDA 通用機(jī)器拓?fù)?。DMIC 的數(shù)量決定是否應(yīng)包含 DMIC 配置文件。這可以通過(guò)以下方式實(shí)現(xiàn):
@args.DMIC_COUNT {
type integer
default 0
}
# include DMIC config if needed
IncludeByKey.DMIC_INCLUDE {
"[1-4]" "include/platform/intel/dmic-generic.conf"
}
正則表達(dá)式 [1-4] 指示,如果 DMIC_COUNT 參數(shù)值在 1 到 4 之間,則應(yīng)包含 dmic-generic.conf 文件。假設(shè)頂層文件名為 sof-hda-generic.conf,你可以使用以下命令構(gòu)建兩個(gè)單獨(dú)的拓?fù)涠M(jìn)制文件:
- 對(duì)于沒有 DMIC 的機(jī)器:
alsatplg -p -c sof-hda-generic.conf -o sof-hda-generic.tplg
- 對(duì)于具有兩個(gè) DMIC 的機(jī)器:
alsatplg -D DMIC_COUNT=2 -p -c sof-hda-generic.conf -o sof-hda-generic-2ch.tplg
條件包含不僅限于頂層配置文件。你可以將它們添加到配置文件中的任何節(jié)點(diǎn),以在指定節(jié)點(diǎn)處包含配置。例如,我們可以為 EQIIR widget 的 byte controls 條件包含正確的過(guò)濾器系數(shù)。
在頂層拓?fù)湮募袨橄禂?shù)定義參數(shù):
@args.EQIIR_BYTES {
type string
default "highpass_40hz_0db_48khz"
}
然后包含系數(shù):
Object.Widget.eqiir.1 {
Object.Control.bytes.1 {
name "my eqiir byte control"
# EQIIR filter coefficients
IncludeByKey.EQIIR_BYTES {
"[highpass.40hz.0db.48khz]" "include/components/eqiir/highpass_40hz_0db_48khz.conf"
"[highpass.40hz.20db.48khz]" "include/components/eqiir/highpass_40hz_20db_48khz.conf"
}
}
}
構(gòu)建 2.0 配置文件
你可以使用 alsatplg 編譯拓?fù)?2.0 配置文件并生成拓?fù)涠M(jìn)制文件:
alsatplg <-D args=values> -p -c input.conf -o output.tplg
-D 開關(guān)用于給頂層配置文件傳遞逗號(hào)分隔的參數(shù)值。
你可以使用 -P 開關(guān)將 2.0 配置文件轉(zhuǎn)為 1.0 配置文件:
alsatplg <-D args=values> -P input.conf -o output.conf
拓?fù)涮嵝?/h2>
查看以下拓?fù)渥⒁馐马?xiàng):
- “index” 指流水線,widget 和 control 類組中的流水線 ID。
- DAI 類組對(duì)象中的 “id” 指的是內(nèi)核中機(jī)器驅(qū)動(dòng)程序中定義的鏈接 ID。
Alsaconf 提醒
查看以下 alsaconf 注意事項(xiàng):
- “.” 指節(jié)點(diǎn)分割符?!癴oo.bar value” 相當(dāng)于以下內(nèi)容:
foo {
bar value
}
- 數(shù)組用 [] 定義。例如:
!constructor [
"foo"
"bar"
]
我們建議在類定義中的數(shù)組定義中使用感嘆號(hào) (!)。如果從不同源多次包含類配置文件,則使用它可以確保數(shù)組項(xiàng)不重復(fù)。
Done.