-
Notifications
You must be signed in to change notification settings - Fork 351
docs: add topology2 README with structure and conventions #10623
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,326 @@ | ||
| # SOF Topology2 | ||
|
|
||
| Topology2 is the second-generation ALSA topology definition system for SOF (Sound Open | ||
| Firmware). It defines audio processing pipelines, PCM streams, DAI configurations, and | ||
| routing graphs using `.conf` files that are compiled into binary `.tplg` files consumed by | ||
| the SOF firmware at runtime. | ||
|
|
||
| The build pipeline works as follows: `.conf` source files are processed by `alsatplg` | ||
| (the ALSA Topology Configuration compiler) to produce `.tplg` binary files. The cmake | ||
| build system orchestrates this compilation, with each topology target specified as a tuple | ||
| of input configuration, output name, and variable overrides. | ||
|
|
||
| Topology2 uses a class-based object model built on four core concepts: | ||
|
|
||
| * **Classes** (`Class.Pipeline`, `Class.Widget`, `Class.PCM`) define reusable templates | ||
| with default attribute values | ||
| * **Objects** (`Object.Pipeline`, `Object.Widget`, `Object.PCM`) instantiate classes with | ||
| specific parameter values | ||
| * **Define blocks** provide variable substitution using `$VARIABLE` syntax, enabling | ||
| parameterized topologies | ||
| * **IncludeByKey** enables conditional includes based on variable values, used primarily | ||
| for platform-specific overrides | ||
|
|
||
| Building topologies requires `alsatplg` version 1.2.7 or later. The version check is | ||
| enforced in `CMakeLists.txt` at configure time. | ||
|
|
||
| ## Directory Structure | ||
|
|
||
| ```text | ||
| tools/topology/topology2/ | ||
| ├── CMakeLists.txt # Build system entry point | ||
| ├── get_abi.sh # ABI version extraction script | ||
| ├── cavs-sdw.conf # SoundWire topology entry point | ||
| ├── sof-hda-generic.conf # HDA generic topology entry point | ||
| ├── cavs-mixin-mixout-hda.conf # HDA with mixer pipelines | ||
| ├── cavs-nocodec.conf # SSP nocodec topology | ||
| ├── ... # Other top-level .conf entry points | ||
| ├── include/ | ||
| │ ├── common/ # Core class definitions (PCM, route, audio formats) | ||
| │ ├── components/ # Widget/component classes (gain, mixin, EQ, DRC) | ||
| │ ├── controls/ # Control classes (mixer, enum, bytes) | ||
| │ ├── dais/ # DAI classes (SSP, DMIC, HDA, ALH) | ||
| │ └── pipelines/ # Pipeline template classes | ||
| │ └── cavs/ # CAVS-architecture pipeline classes | ||
| ├── platform/ | ||
| │ └── intel/ # Platform-specific overrides (tgl, mtl, lnl, ptl) | ||
| ├── production/ # CMake targets for production topologies | ||
| │ ├── tplg-targets-ace1.cmake # ACE1 (MTL) targets | ||
| │ ├── tplg-targets-ace2.cmake # ACE2 (LNL) targets | ||
| │ ├── tplg-targets-ace3.cmake # ACE3 (PTL) targets | ||
| │ └── ... # Additional platform target files | ||
| ├── development/ # CMake targets for development/testing | ||
| └── doc/ # Doxygen documentation source | ||
| ``` | ||
|
|
||
| ## Best Practices for Adding New Topology Definitions | ||
|
|
||
| ### Topology Structure | ||
|
|
||
| A top-level topology `.conf` file follows a layered configuration pattern: | ||
|
|
||
| ```conf | ||
| # 1. Search directories | ||
| <searchdir:include> | ||
| <searchdir:include/common> | ||
| <searchdir:include/components> | ||
| <searchdir:include/dais> | ||
| <searchdir:include/pipelines/cavs> | ||
| <searchdir:platform/intel> | ||
|
|
||
| # 2. Include class files | ||
| <vendor-token.conf> | ||
| <tokens.conf> | ||
| <pcm.conf> | ||
| <host-copier-gain-mixin-playback.conf> | ||
| <mixout-gain-alh-dai-copier-playback.conf> | ||
|
|
||
| # 3. Define block (default variable values) | ||
| Define { | ||
| PLATFORM "" | ||
| NUM_HDMIS 3 | ||
| DEEP_BUFFER_PCM_ID 31 | ||
| } | ||
|
|
||
| # 4. Platform overrides (conditional includes) | ||
| IncludeByKey.PLATFORM { | ||
| "mtl" "platform/intel/mtl.conf" | ||
| "lnl" "platform/intel/lnl.conf" | ||
| "ptl" "platform/intel/ptl.conf" | ||
| } | ||
|
|
||
| # 5. Conditional feature includes | ||
| IncludeByKey.NUM_HDMIS { | ||
| "3" "platform/intel/hdmi-generic.conf" | ||
| } | ||
|
|
||
| # 6. DAI, Pipeline, PCM objects | ||
| # 7. Route definitions | ||
| ``` | ||
|
|
||
| ### Reusing Existing Bases | ||
|
|
||
| The most common way to add a new topology is to reuse an existing base `.conf` file and | ||
| override variables through a cmake target entry. Targets are defined in | ||
| `production/tplg-targets-*.cmake` files using a tuple format: | ||
|
|
||
| ```text | ||
| "input-conf;output-name;variables" | ||
| ``` | ||
|
|
||
| For example, to add a new SoundWire topology variant for ACE2 (Lunar Lake): | ||
|
|
||
| ```text | ||
| "cavs-sdw\;sof-lnl-sdw-cs42l43-l0-cs35l56-l12\;PLATFORM=lnl,NUM_SDW_AMP_LINKS=2" | ||
| ``` | ||
|
|
||
| The first element is the base `.conf` file (without extension), the second is the output | ||
| `.tplg` filename, and the third is a comma-separated list of variable overrides. | ||
|
|
||
| ### Creating a New Base Topology | ||
|
|
||
| When existing bases do not cover a new use case, create a new top-level `.conf` file: | ||
|
|
||
| 1. Create a new `.conf` file in `tools/topology/topology2/` following the layered | ||
| structure described above | ||
| 2. Include the required class files from `include/` directories via search directives | ||
| 3. Define default variables in a `Define` block | ||
| 4. Add `IncludeByKey.PLATFORM` entries for platform-specific overrides | ||
| 5. Instantiate DAI, Pipeline, and PCM objects with appropriate IDs | ||
| 6. Define routes connecting FE mixin outputs to BE mixout inputs | ||
| 7. Register the topology as a cmake target in the appropriate | ||
| `production/tplg-targets-*.cmake` file | ||
|
|
||
| ### PCM ID Conventions | ||
|
|
||
| PCM IDs identify audio streams exposed to userspace via ALSA. Each PCM ID must be unique | ||
| within a single topology. Different topology families (SoundWire vs HDA) use different | ||
| default ID ranges for the same endpoint types. | ||
|
|
||
| **SoundWire PCM IDs:** | ||
|
|
||
| | Endpoint | Default PCM ID | Override Variable | | ||
| |---|---|---| | ||
| | Jack (playback/capture) | 0 | — | | ||
| | Speaker amplifier | 2 | — | | ||
| | SDW DMIC | 4 | — | | ||
| | HDMI 1 | 5 | `HDMI1_PCM_ID` | | ||
| | HDMI 2 | 6 | `HDMI2_PCM_ID` | | ||
| | HDMI 3 | 7 | `HDMI3_PCM_ID` | | ||
| | PCH DMIC0 | 10 | `DMIC0_PCM_ID` | | ||
| | PCH DMIC1 | 11 | `DMIC1_PCM_ID` | | ||
| | Jack Echo Ref | 11 | `SDW_JACK_ECHO_REF_PCM_ID` | | ||
| | Speaker Echo Ref | 12 | `SDW_SPK_ECHO_REF_PCM_ID` | | ||
| | Bluetooth | 2 or 20 | `BT_PCM_ID` | | ||
| | Deep Buffer (Jack) | 31 | `DEEP_BUFFER_PCM_ID` | | ||
| | Deep Buffer (Speaker) | 35 | `DEEP_BUFFER_PCM_ID_2` | | ||
| | DMIC Deep Buffer | 46 | `DMIC0_DEEP_BUFFER_PCM_ID` | | ||
| | Compressed Playback 1 | 50 | `COMPR_PCM_ID` | | ||
| | Compressed Playback 2 | 52 | `COMPR_2_PCM_ID` | | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Compress Speaker |
||
|
|
||
| > **Note:** Bluetooth defaults to PCM ID 2 in some topologies and 20 in others. Use the | ||
| > `BT_PCM_ID` override variable to set the correct value when BT coexists with a speaker | ||
| > amplifier (which also uses PCM ID 2 by default). | ||
|
|
||
| **HDA PCM IDs:** | ||
|
|
||
| | Endpoint | Default PCM ID | Override Variable | | ||
| |---|---|---| | ||
| | HDA Analog | 0 | — | | ||
| | HDMI 1 | 3 | `HDMI1_PCM_ID` | | ||
| | HDMI 2 | 4 | `HDMI2_PCM_ID` | | ||
| | HDMI 3 | 5 | `HDMI3_PCM_ID` | | ||
| | DMIC0 | 6 | `DMIC0_PCM_ID` | | ||
| | Deep Buffer | 31 | `DEEP_BUFFER_PCM_ID` | | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. | Compress HDA Analog | 50 | |
||
|
|
||
| Key rules: | ||
|
|
||
| * PCM ID 0 is always the primary playback endpoint | ||
| * PCM IDs must be unique within a single topology | ||
| * When features coexist (SDW + PCH DMIC + HDMI), adjust IDs via `Define` overrides in | ||
| cmake targets to avoid conflicts | ||
| * Different topology families (SDW vs HDA) use different default ID ranges for the same | ||
| endpoint types | ||
|
|
||
| ### Pipeline ID Conventions | ||
|
|
||
| Pipeline IDs are set via the `index` attribute on pipeline objects. Front-end (FE) and | ||
| back-end (BE) pipelines are paired, with the FE pipeline at index N and the BE pipeline | ||
| at index N+1. | ||
|
|
||
| In SoundWire topologies, pipeline indexes follow the convention documented in | ||
| `sdw-amp-generic.conf` and `sdw-dmic-generic.conf`: pipeline index = PCM ID × 10. HDMI | ||
| pipelines use a stride-10 pattern where the host pipeline is at N0 and the DAI pipeline | ||
| is at N1 (50/51, 60/61, 70/71, 80/81). | ||
|
|
||
| **SoundWire Pipeline IDs:** | ||
|
|
||
| | Pipeline | Default Index | Override Variable | | ||
| |---|---|---| | ||
| | Jack Playback FE / BE | 0 / 1 | — | | ||
| | Jack Capture FE / BE | 10 / 11 | — | | ||
| | Deep Buffer (Jack) | 15 | `DEEP_BUFFER_PIPELINE_ID` | | ||
| | Deep Buffer (Speaker) | 16 | `DEEP_BUFFER_PIPELINE_ID_2` | | ||
| | Speaker FE / BE | 20 / 21 | — | | ||
| | SDW DMIC FE / BE | 40 / 41 | `SDW_DMIC_HOST_PIPELINE_ID` | | ||
| | HDMI 1 Host / DAI | 50 / 51 | `HDMI1_HOST_PIPELINE_ID` / `HDMI1_DAI_PIPELINE_ID` | | ||
| | HDMI 2 Host / DAI | 60 / 61 | `HDMI2_HOST_PIPELINE_ID` / `HDMI2_DAI_PIPELINE_ID` | | ||
| | HDMI 3 Host / DAI | 70 / 71 | `HDMI3_HOST_PIPELINE_ID` / `HDMI3_DAI_PIPELINE_ID` | | ||
| | HDMI 4 Host / DAI | 80 / 81 | `HDMI4_HOST_PIPELINE_ID` / `HDMI4_DAI_PIPELINE_ID` | | ||
| | Compressed 1 / 2 | 90 / 92 | `COMPR_PIPELINE_ID` / `COMPR_2_PIPELINE_ID` | | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Compress Jack and Speaker... |
||
| | PCH DMIC0 Host / DAI | 100 / 101 | `DMIC0_HOST_PIPELINE_ID` / `DMIC0_DAI_PIPELINE_ID` | | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why only these random pipeline IDs? There is pipeline with ID 22 for example and based on this readme, it is up for grab, when if you define a pipeline with 22, it will have really fun side effects as it is used by something else... |
||
|
|
||
| **HDA Pipeline IDs:** | ||
|
|
||
| | Pipeline | Default Index | Override Variable | | ||
| |---|---|---| | ||
| | Analog Playback FE / BE | 1 / 2 | — | | ||
| | Analog Capture FE / BE | 3 / 4 | — | | ||
| | DMIC0 Host / DAI | 11 / 12 | `DMIC0_HOST_PIPELINE_ID` / `DMIC0_DAI_PIPELINE_ID` | | ||
| | Deep Buffer | 15 | `DEEP_BUFFER_PIPELINE_ID` | | ||
| | HDMI 1 Host / DAI | 50 / 51 | `HDMI1_HOST_PIPELINE_ID` / `HDMI1_DAI_PIPELINE_ID` | | ||
| | HDMI 2 Host / DAI | 60 / 61 | `HDMI2_HOST_PIPELINE_ID` / `HDMI2_DAI_PIPELINE_ID` | | ||
| | HDMI 3 Host / DAI | 70 / 71 | `HDMI3_HOST_PIPELINE_ID` / `HDMI3_DAI_PIPELINE_ID` | | ||
| | HDMI 4 Host / DAI | 80 / 81 | `HDMI4_HOST_PIPELINE_ID` / `HDMI4_DAI_PIPELINE_ID` | | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Compress HDA Analog is 90 and who knows what pipeline IDs are in use in HDA topology, I would not dare to guess... |
||
|
|
||
| Key rules: | ||
|
|
||
| * FE and BE pipelines are paired: FE = N, BE = N+1 | ||
| * SDW convention: pipeline index = PCM ID × 10 (documented in `sdw-amp-generic.conf` and | ||
| `sdw-dmic-generic.conf`) | ||
| * HDMI uses stride-10: Host = N0, DAI = N1 | ||
| * Pipeline IDs must be unique within a single topology | ||
| * When adding new endpoints, select IDs in unused ranges that do not conflict with | ||
| existing assignments | ||
|
|
||
| ### Widget Naming | ||
|
|
||
| Widget names follow the convention `<type>.<pipeline-index>.<instance>`. Examples: | ||
|
|
||
| * `gain.1.1` — gain widget in pipeline 1, instance 1 | ||
| * `mixin.15.1` — mixin widget in pipeline 15, instance 1 | ||
| * `host-copier.0.playback` — host copier in pipeline 0, playback direction | ||
| * `dai-copier.1.ALH` — DAI copier in pipeline 1, ALH type | ||
|
|
||
| ### Route Definitions | ||
|
|
||
| Routes connect FE pipeline mixin outputs to BE pipeline mixout inputs. This is the | ||
| primary mechanism for linking front-end and back-end pipelines: | ||
|
|
||
| ```conf | ||
| Object.Base.route [ | ||
| { | ||
| source "mixin.15.1" | ||
| sink "mixout.2.1" | ||
| } | ||
| ] | ||
| ``` | ||
|
|
||
| Multiple FE pipelines can feed into a single BE mixout. For example, both a normal | ||
| playback pipeline and a deep buffer pipeline can route to the same DAI output: | ||
|
|
||
| ```text | ||
| host-copier.0 -> gain.0 -> mixin.0 ─┐ | ||
| ├─> mixout.1 -> gain.1 -> dai-copier.1 -> DAI | ||
| host-copier.15 -> gain.15 -> mixin.15┘ | ||
| ``` | ||
|
|
||
| ### Platform Overrides | ||
|
|
||
| Platform-specific configurations are applied using the `IncludeByKey.PLATFORM` mechanism. | ||
| Each platform `.conf` file under `platform/intel/` contains `Define` blocks that override | ||
| variables such as `DMIC_DRIVER_VERSION`, `SSP_BLOB_VERSION`, and `NUM_HDMIS`. | ||
|
|
||
| Supported platforms: | ||
|
|
||
| * `tgl` — Tiger Lake / Alder Lake (CAVS 2.5) | ||
| * `mtl` — Meteor Lake (ACE 1.x) | ||
| * `lnl` — Lunar Lake (ACE 2.x) | ||
| * `ptl` — Panther Lake (ACE 3.x) | ||
|
|
||
| ```conf | ||
| IncludeByKey.PLATFORM { | ||
| "mtl" "platform/intel/mtl.conf" | ||
| "lnl" "platform/intel/lnl.conf" | ||
| "ptl" "platform/intel/ptl.conf" | ||
| } | ||
| ``` | ||
|
|
||
| ### Registering CMake Targets | ||
|
|
||
| Production topologies are registered in `production/tplg-targets-*.cmake` files. Each | ||
| target is a semicolon-separated tuple: | ||
|
|
||
| ```text | ||
| "input-conf;output-name;variable1=value1,variable2=value2" | ||
| ``` | ||
|
|
||
| Select the cmake file matching the target platform generation: | ||
|
|
||
| | Platform | CMake Target File | | ||
| |---|---| | ||
| | Tiger Lake / Alder Lake | `tplg-targets-cavs25.cmake` | | ||
| | Meteor Lake | `tplg-targets-ace1.cmake` | | ||
| | Lunar Lake | `tplg-targets-ace2.cmake` | | ||
| | Panther Lake | `tplg-targets-ace3.cmake` | | ||
| | HDA generic | `tplg-targets-hda-generic.cmake` | | ||
|
|
||
| Development and testing topologies go in `development/tplg-targets.cmake`. | ||
|
|
||
| ## Building Topologies | ||
|
|
||
| Configure the build with cmake and build the topology targets: | ||
|
|
||
| ```bash | ||
| mkdir build && cd build | ||
| cmake .. | ||
| make -j$(nproc) | ||
| ``` | ||
|
|
||
| To build a specific topology target: | ||
|
|
||
| ```bash | ||
| make sof-lnl-sdw-cs42l43-l0-cs35l56-l12 | ||
| ``` | ||
|
|
||
| The compiled `.tplg` files are placed in the build output directory. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Compress Jack Out