Conversation
topology/pre-process-dapm.c
Outdated
| #include "pre-processor.h" | ||
|
|
||
| /* function to create the widget config node */ | ||
| int tplg_create_widget_config_template(snd_config_t **wtemplate) |
There was a problem hiding this comment.
Template functions are almost same. Could we have one universal function which will accept an array of string / type structures as argument to reduce the code ? I believe that it would be more readable. Also, if the template functions are called one time, the array may be defined in (moved to) the caller function to reduce the exported functions.
There was a problem hiding this comment.
@Perex I've removed the template functions now. But I pre-defined the array for each class type. I didnt define the array in the caller because the caller is shared for multiple object types.
topology/pre-process-base.c
Outdated
|
|
||
| ret = snd_config_set_string(cfg, item_name); | ||
| if (ret < 0) | ||
| return ret; |
topology/pre-process-base.c
Outdated
| } | ||
| } | ||
|
|
||
| snd_config_for_each(i, next, item_config) { |
|
First of all, thank you for your work and for accepting of my proposals. I like this way of the implementation. I think that we're very close for the merge. I'll did a quick very incomplete review. I'll do further review in next days. |
Thank you so much for the feedback @Perex. I've been thinking about how to unfy the template functions too. I can work on that in the next couple of days along with your other comments. |
9730180 to
e419cde
Compare
|
@Perex sorry to bug you but I was wondering if you have more feedback for me to address in this PR. I have one open that I need to address and that is regarding adding the computation for automatic attributes in classes. I will add that patch in the next couple of days. |
There was a problem hiding this comment.
I don't have any other comments to the code (except the two things bellow). We can improve it on the go.
Are the memory allocations checked using valgrind or other memory checker / sanitizer ?
... and I apologize for the delay, I'm pretty busy with other things these days.
| if (snd_config_get_id(cfg, &class_type) < 0) | ||
| return NULL; | ||
|
|
||
| first = snd_config_iterator_first(cfg); |
There was a problem hiding this comment.
The first iterator may be equal to snd_config_iterator_end() here (empty compound).. The class pointer may be invalid then. Please, add a check.
topology/pre-processor.h
Outdated
| int tplg_config_make_add(snd_config_t **config, const char *id, snd_config_type_t type, | ||
| snd_config_t *parent); | ||
|
|
||
| /* template configs */ |
There was a problem hiding this comment.
I would move the templates neaby the object_build_map[] declaration. I don't see any benefit to have this structure description split to multiple files. But it's just a suggestion - if you have a good reason, I can be convinced.
There was a problem hiding this comment.
@Perex addressed both your comments now. We are adding more complex topologies to test the new parser and we will fix any issues we find along the way.
No worries at all. We have verified the memory allocations using valgrind and there are no errors. I will address your comments and push the changes soon. |
This patch adds support for pre-processing the Topology2.0. The '-p' switch add pre-processing support during compilation and the '-P' switch is for converting the Topology2.0 configuration file into the existing syntax. Topology2.0 is a high level keyword extension on top of the existing ALSA conf topology format designed to: 1) Simplify the ALSA conf topology definitions by providing high level "classes" so topology designers need to write less config for common object definitions. 2) Allow simple reuse of objects. Define once and reuse (like M4) with the ability to alter objects configuration attributes from defaults. 3) Allow data type and value verification. This is not done today and frequently crops up in FW bug reports. Common Topology Classes ----------------------- Topology today has some common classes that are often reused throughout with slightly altered configurations. i.e. widgets (components), pipelines, dais and controls. Topology2.0 introduces the high level concept of reusable "class" like definition for that can be used to create topology objects. Common Topology Attributes -------------------------- Topology defines a lot of attributes per object with different types and constraints. Today there is no easy way to validate type or constraints and this can lead to many hard to find problems in FW at runtime. A new keyword "DefineAttribute" has been added to define attribute constraints such as min value, max value, enum_values etc. This then allows alsatplg to validate each topology object attribute. Topology Classes define the list of attributes that they use and whether the attribute is mandatory, can be overridden by parent users or is immutable. This also helps alsatplg emit the appropriate errors for attribute misuse. Class constructor attributes ---------------------------- Some attributes in the class definition are declared as constructor attributes and these will be used to construct the name of the object. For ex: for the host widget, the index and direction are constructor attributes and the name for the widget is derived as follows: host.1.playback or host.2.capture etc. Attribute Inheritance: ---------------------- One of the key features of Topology2.0 is how the attribute values are propagated from a parent object to a child object. For ex: a pipeline object can pass down the pipeline_id attribute to all its widgets. Inheritance is implicit when an object and its embedded child objects have matching names for a attribute/argument. Attribute values set explicitly in an object instance always has precedence over the values inherited from the parent object. Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com> 1 1 1
Add a couple of helper functions to print debug messages and the generated config. Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Add a couple of helper functions for searching config by ID and creating and adding configs to a parent config. Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
The pre-processor needs to concatinate strings separated by '.' for building object names from constructor attribute values and searching for configs with ID's containing strings separate by '.'. Add a helper function to concat strings in the specified input format. Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
…inition
Add a helper function to look up the class definition for
an object. ex: for an object instance, Object.Widget.pga.0{}, the
function returns the config pointing to Class.Widget.pga{} in
the input conf.
Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
…tion in class Add a helper function look up attribute definition in the "DefineAttribute" config in the class definition. Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
…ints
Add helper functions to check if an attribute is
mandatory, immutable or unique in the class definition.
ex: for a host widget component, these are defined
as follows:
attributes {
#
# host objects instantiated within the same alsaconf node must have unique
# direction attribute
#
unique "direction"
mandatory [
"type"
"stream_name"
]
immutable [
"uuid"
]
}
Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
…e attribute in a class
Every class must have a unique attribute that will be used
to instantiate the object. The value provided for this
attribute must be unique within the same alsaconf node for
objects of the same class. Add a helper function to get the
name of the attribute that must have a unique value in the
object instance.
For example, when instantiating 2 buffer widgets within a pipeline,
they must be given unique instance attribute values as:
Object.Widget.buffer.0{} and Object.Widget.buffer.1{}.
Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Add a helper function to get attribute type from the attribute definition and convert them to SND_CONFIG_TYPE_* values. When no type if provided for an attribute, type defaults to integer. Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
… attribute in class
Some attributes may have the token_ref set which is
used to look up the token value for the tuple data
that is appended to the object's private data.
For example, in the buffer widget object:
DefineAttribute."size" {
# Token reference and type
token_ref "sof_tkn_buffer.word"
}
The token_ref must include the reference to the vendor
token object name followed by the type of the tuple.
Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
…values to integer tuple values
Some attributes have valid values that need to be converted
to integer tuple values before it is appended to the
object's private data:
For ex, the buffer widget object's "caps" attribute has the
following definition:
DefineAttribute."caps" {
type "string"
# Token reference and type
token_ref "sof_tkn_buffer.word"
constraints {
value_ref "sof_tkn_mem"
valid_values [
"dai"
"host"
"pass"
"comp"
]
tuple_values [
113
113
113
65
]
}
}
Depending on the user input, the value string values for "caps"
will be converted to the appropriate tuple values.
Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Add support for pre-processing object instances in the input config. An object's attributes can be set in multiple places such as, within the object instance, default values in the class defnition, inherited from a parent object or explicitly set in a parent object. Before converting the object config into the relevant section in the existing syntax, all the attribute values must be consolidated into one place so that it is easy to verify if all mandatory attributes are set. Also, the name of the object will be constructed from the attributes defined in the attributes.constructor[] config in the class definition and the unique attribute's value must be set from the value passed in the object instance. This patch create a temporary config for each object instance and populates its unique attribute value. The rest of the steps will be added in the following patches. Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Object attributes can be set in multiple places. Search for the attribute value in the following order: 1. Value set in object instance 2. Default value set in the object's class definition 3. Inherited value from the parent object 4. Value set in the object instance embedded in the parent object 5. Value set in the object instance embedded in the parent class definition Mandatory attributes must be found in one of the above, resulting in an error if not found. Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Attributes can have constraints set for valid values, min
or max values. If the attribute value is set in an object,
the value must be validated against the set constraints.
An example for attribute constraint would be:
DefineAttribute."direction" {
constraints {
valid_values [
"playback"
"capture"
]
tuple_values [
0
1
]
}
}
where the tuple_values array would translate the valid_values of
playback as 0 and capture as 1.
Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
…tor attributes
An object's name is derived from its constructor attribute
values separated by '.'. For example, the name for the
host widget objects is derived from its index and direction
attribute values as follows:
Object.Widget.host."playback" {
index 2
}
The name for the host widget object would be host.2.playback.
Alternatively, if the object has a name attribute, the class
definition may skip the constructor attributes and the name attribute
value will be used instead.
Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
The pre-processor converts the Topology2.0 objects into the relevant sections by looking for attributes defined in the template config for the section and reading the attribute values from the object instance config. The structure struct build_function_map contains the mapping of the build function to use for each object based on the type and name for the class that the object belongs to. The manifest object is the simplest with no attributes. So, the build function simply creates a new Section called SectionManifest which will be populated with the data section in the following patches. Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
…ken reference
Objects that have attributes with token_ref need
to have separate SectionData and SectionVendorTuples
for each unique token_ref based on the attribute_sets
in the object's attribute_set_list.
Add the tplg_pp_add_object_data()
function to add the data[] refs in the object and also
the respective SectionData and SectionVendorTuples.
For example for the pga object:
Object.Widget.pga."0" {
pipeline_id 2
format s24le
type pga
no_pm 1
uuid "7e:67:7e:b7:f4:5f:88:41:af:14:fb:a8:bd:bf:8"
period_sink_count 2
period_source_count 2
ramp_step_ms 250
ramp_step_type "linear"
mixer.0 {
....
}
mixer.0.name "2 Master Playback Volume"
}
The following sections will be added:
SectionWidget.'pga.2.0' {
index 2
type pga
no_pm 1
mixer [
"2 Master Playback Volume"
]
bytes [
]
data [
"pga.2.0.sof_tkn_comp.word"
"pga.2.0.sof_tkn_comp.string"
"pga.2.0.sof_tkn_comp.uuid"
"pga.2.0.sof_tkn_volume.word"
]
}
SectionData."pga.2.0.sof_tkn_comp.word" {
tuples "pga.2.0.sof_tkn_comp.word"
}
SectionData."pga.2.0.sof_tkn_comp.string" {
tuples "pga.2.0.sof_tkn_comp.string"
}
SectionData."pga.2.0.sof_tkn_comp.uuid" {
tuples "pga.2.0.sof_tkn_comp.uuid"
}
SectionData."pga.2.0.sof_tkn_volume.word" {
tuples "pga.2.0.sof_tkn_volume.word"
}
SectionVendorTuples."pga.2.0.sof_tkn_comp.word" {
tokens "sof_tkn_comp"
tuples."word" {
period_source_count "2"
period_sink_count "2"
}
}
SectionVendorTuples."pga.2.0.sof_tkn_comp.string" {
tokens "sof_tkn_comp"
tuples."string" {
format "s24le"
}
}
SectionVendorTuples."pga.2.0.sof_tkn_comp.uuid" {
tokens "sof_tkn_comp"
tuples."uuid" {
uuid "7e:67:7e:b7:f4:5f:88:41:af:14:fb:a8:bd:bf:8"
}
}
SectionVendorTuples."pga.2.0.sof_tkn_volume.word" {
tokens "sof_tkn_volume"
tuples."word" {
ramp_step_ms "250"
ramp_step_type "0"
}
}
Note that the ramp_step_type of "linear" is converted
to the tuple value 0.
Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
…config
Add a helper function to retrieve the config node
pointing to the section name for a given object.
For ex: for the object, Object.Widget.pga.1{},
the function returns the config with id, "SectionWidget"
in the output config.
Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Pre-process data objects, create the SectionData and update
the parent object with the reference to the object.
For example, the following object instance:
Object.Base.data."SOF_ABI" {
bytes "0x03,0x12,0x01"
}
would update the SectionManifest as follows:
SectionManifest."sof_manifest" {
data [
"SOF_ABI"
]
}
Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Add support for pre-processing VendorToken objects.
For ex:
Object.Base.VendorToken."sof_tkn_dai" {
dmac_config 153
dai_type 154
index 155
direction 156
}
would be converted to:
SectionVendorTokens."sof_tkn_dai" {
dmac_config 153
dai_type 154
index 155
direction 156
}
Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Add support for pre-processing DAPM widget opbects.
For ex:
Object.Widget.pga."0" {
pipeline_id 1
no_pm true
type pga
}
will be converted to:
SectionWidget.'pga.0' {
index 1
type pga
no_pm 1
}
Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Add support for pre-processing TLV objects
For example:
Object.Base.tlv."vtlv_m64s2" {}
will be converted to:
SectionTLV.'vtlv_m64s2' {}
And the mixer controle section will be updated to add
the reference to the tlv object.
Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Add support for pre-processing scale/ops/channel objects
and adding the converted config to the relevant sections.
For ex:
Object.Base.channel."fl" {
shift 0
reg 1
}
Object.Base.channel."fr" {
reg 1
shift 1
}
Will be converted to:
channel {
fl {
reg 1
shift 0
}
fr {
reg 1
shift 1
}
}
And added to the SectionControlMixer that this object belongs to.
Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Add support for pre-processing mixer and byte control objects.
For ex: a pga widget with a mixer control as follows:
Object.pga"0" {
...
mixer.0 {
index 2
max 32
name "2 MasterPlaybackControl"
Object.Base.channel."fl" {
shift 0
}
Object.Base.channel."fr" {
}
Object.Base.tlv."vtlv_m64s2" {
Object.Base.scale."m64s2" {
mute 1
}
}
Object.Base.ops."ctl" {
info "volsw"
alsa-project#256 binds the mixer control to volume get/put handlers
get 256
put 256
}
access [
read_write
tlv_read
]
}
}
Would be converted to:
SectionControlMixer.'2 Master Playback Volume' {
index 2
max 32
channel {
fl {
reg 1
}
fr {
reg 1
shift 1
}
}
tlv "vtlv_m64s2"
ops.0 {
info volsw
get 256
put 256
}
access [
read_write
tlv_read
]
}
and the SectionWidget for pga.2.0 would be updated to add the mixer references as follows:
SectionWidget.'pga.2.0' {
...
mixer [
"2 Master Playback Volume"
]
}
Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
DAPM route objects such as:
Object.Base.route."1" {
source "dai.SSP.0.dai.capture"
sink "buffer.2.1"
}
will be converted to:
SectionGraph."Endpoint.route.1" {
index 0
lines [
"dai.SSP.0.capture, , buffer.2.1"
]
}
If the source/sink names are references to objects within a parent pipeline
object, the index attribute value can be skipped and it will be
populated when the object is pre-processed
Object.Pipeline.volume-capture."1" {
Object.Base.route."1" {
source "pga..0"
sink "buffer..0"
}
}
The reference pga..0 will need to be resolved to
get the widget name pga.1.0 and buffer..0 will
be resolved to buffer.1.0 before creating the SectionGraph as follows:
SectionGraph."volume-capture.1.route.1" {
index 2
lines [
"pga.1.0, , buffer.1.0"
]
}
Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Add support for pre-processing PCM and BE DAI objects:
Object.PCM.pcm."0" {
name "Port0"
direction "duplex"
}
will be converted to:
SectionPCM.'Port0' {}
The capabilities and dai configs will be added those objects are pre-processed.
An ex of DAI object would be:
Object.Dai.SSP."0" {
direction "duplex"
stream_name "NoCodec-0"
id 0
default_hw_conf_id 0
format "s24le"
quirks "lbm_mode"
sample_bits 24
}
converted to:
SectionBE {
'SSP.0.duplex' {
id 0
stream_name NoCodec-0
default_hw_conf_id 0
data [
'SSP.0.duplex.sof_tkn_intel_ssp.word'
'SSP.0.duplex.sof_tkn_dai.word'
'SSP.0.duplex.sof_tkn_dai.string'
'SSP.0.duplex.sof_tkn_intel_ssp.short'
]
}
Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Add supprt for hwcfg objects:
For ex:
Object.Base.hw_config."SSP0 hw_config 0" {
id 0
mclk_freq 24000000
bclk_freq 4800000
tdm_slot_width 25
}
would get converted to:
SectionHWConfig {
'SSP0 hw_config 0' {
id 0
format I2S
bclk codec_consumer
bclk_freq 4800000
fsync codec_consumer
fsync_freq 48000
mclk codec_mclk_in
mclk_freq 24000000
tdm_slots 2
tdm_slot_width 25
tx_slots 3
rx_slots 3
}
}
and the corresponding SectionBE will be updated with the hwcfgs reference as:
hw_configs [
'SSP0 hw_config 0'
]
Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Add support for fe_dai objects:
For ex:
Object.PCM.pcm."0" {
name "Port0"
direction "duplex"
Object.Base.fe_dai."Port 0" {}
}
will be converted to update the SectionPCM as follows:
SectionPCM {
Port0 {
id 0
dai {
'Port 0' {
id 0
}
}
}
Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Add support for processing pcm_caps objects.
For ex:
Object.PCM.pcm."0" {
name "Port0"
direction "duplex"
Object.Base.fe_dai."Port 0" {}
Object.PCM.pcm_caps."playback" {
name "Port0 Playback"
}
Object.PCM.pcm_caps."capture" {
name "Port0 Capture"
}
}
Would convert into:
SectionPCMCapabilities {
'Port0 Playback' {
formats 'S32_LE,S24_LE,S16_LE'
rate_min 48000
rate_max 48000
channels_min 2
channels_max 2
periods_min 2
periods_max 16
period_size_min 192
period_size_max 16384
buffer_size_min 65536
buffer_size_max 65536
}
'Port0 Capture' {
formats 'S32_LE,S24_LE,S16_LE'
rate_min 48000
rate_max 48000
channels_min 2
channels_max 2
periods_min 2
periods_max 16
period_size_min 192
period_size_max 16384
buffer_size_min 65536
buffer_size_max 65536
}
}
and the SectionPCM updated as follows:
SectionPCM {
Port0 {
id 0
dai {
'Port 0' {
id 0
}
}
pcm {
playback {
capabilities 'Port0 Playback'
}
capture {
capabilities 'Port0 Capture'
}
}
}
}
Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
Add support for processing object instances embedded
within objects and classes. For example:
Object.Control.mixer."0" {
#Channel register and shift for Front Left/Right
Object.Base.channel."fl" {
shift 0
}
Object.Base.channel."fr" {
}
Object.Base.tlv."vtlv_m64s2" {
Object.Base.scale."m64s2" {
mute 1
}
}
Object.Base.ops."ctl" {
info "volsw"
alsa-project#256 binds the mixer control to volume get/put handlers
get 256
put 256
}
}
and pga class embeds the mixer objects as follows:
Class.Widget."pga" {
...
Object.Control {
mixer."0" {...}
mixer."1" {...}
}
The pre-processor starts with the top-pevel PGA widget object
and processes the mixer objects in the class definition.
This will recursively pre-processes its child objects to add the channels,
tlv and ops.
Signed-off-by: Ranjani Sridharan <ranjani.sridharan@linux.intel.com>
e419cde to
e2152da
Compare
|
@perexg Can I bother you to please take a look at the updated version? From a basic 2.0 syntax point of view, this PR covers everything. We would like to additional support to the class definition as enhacements after this is merged. |
perexg
left a comment
There was a problem hiding this comment.
Merged with some cosmetic tab/spaces changes. Thank you for your work. Create a new PR for bug-fixes. Thank you.
|
Thank you so much @perexg. We plan to start using topology 2.0 in the SOF 1.9 release and in preparation for that we will start contributing bug fixes and/or enhancements. |
Add support for pre-processing Topology2.0 conf file and building the tplg binary.
An example implementation of topology with Topology2.0 can be found here:
thesofproject/sof#3983
Introduction to Topology 2.0
Topology2.0 is a high level keyword extension on top of the existing ALSA
conf topology format designed to:
Simplify the ALSA conf topology definitions by providing high level
"classes" so topology designers need to write less config for common
object definitions.
Allow simple reuse of objects. Define once and reuse (like M4) with
the ability to alter objects configuration attributes from defaults.
Allow data type and value verification. This is not done today and
frequently crops up in FW bug reports.
Common Topology Classes
Topology today has some common classes that are often reused throughout
with slightly altered configurations. i.e. widgets (components),
pipelines, dais and controls.
Topology2.0 introduces the high level concept of reusable "class" like
definition for a AIF_IN/AIF_OUT type object that can be used to create
topology objects.
Common Topology Attributes
Topology defines a lot of attributes per object with different types
and constraints. Today there is no easy way to validate type or
constraints and this can lead to many hard to find problems in FW at
runtime.
A new keyword "DefineAttribute" has been added to define attribute
type, size, min value, max value, enum_values. This then allows
alsatplg to validate each topology object attribute.
Topology Classes define the list of attributes that they use and
whether the attribute is mandatory, can be overridden by parent users
or is immutable. This also helps alsatplg emit the appropriate errors
for attribute misuse.
Attribute validation
One of the main features of Topology2.0 is the ability to add constraints to
attributes to define a set of valid values, specify min/max values etc in
the class definition.
Attribute Inheritance
One of the key features of Topology2.0 is howthe attribute values are
propagated from a parent object to a child object. This is accomplished
by adding attributes/arguments with the same name for a parent and an
object. By doing so, when creating a child object, the value for the
common attribute is populated from the parent. If the value is provided
in the child object instance, then it overrides the value coming from
the parent.
ALSA Conf Parser
All the changes being proposed and discussed here must be 100%
compliant with the ALSA conf parser. i.e. no syntax changes or
changes to semantics for any existing keyword.
It's intended that there will be NO changes to the ALSA conf parser