|
| 1 | +# SOF Topology2 |
| 2 | + |
| 3 | +Topology2 is the second-generation ALSA topology definition system for SOF (Sound Open |
| 4 | +Firmware). It defines audio processing pipelines, PCM streams, DAI configurations, and |
| 5 | +routing graphs using `.conf` files that are compiled into binary `.tplg` files consumed by |
| 6 | +the SOF firmware at runtime. |
| 7 | + |
| 8 | +The build pipeline works as follows: `.conf` source files are processed by `alsatplg` |
| 9 | +(the ALSA Topology Configuration compiler) to produce `.tplg` binary files. The cmake |
| 10 | +build system orchestrates this compilation, with each topology target specified as a tuple |
| 11 | +of input configuration, output name, and variable overrides. |
| 12 | + |
| 13 | +Topology2 uses a class-based object model built on four core concepts: |
| 14 | + |
| 15 | +* **Classes** (`Class.Pipeline`, `Class.Widget`, `Class.PCM`) define reusable templates |
| 16 | + with default attribute values |
| 17 | +* **Objects** (`Object.Pipeline`, `Object.Widget`, `Object.PCM`) instantiate classes with |
| 18 | + specific parameter values |
| 19 | +* **Define blocks** provide variable substitution using `$VARIABLE` syntax, enabling |
| 20 | + parameterized topologies |
| 21 | +* **IncludeByKey** enables conditional includes based on variable values, used primarily |
| 22 | + for platform-specific overrides |
| 23 | + |
| 24 | +Building topologies requires `alsatplg` version 1.2.7 or later. The version check is |
| 25 | +enforced in `CMakeLists.txt` at configure time. |
| 26 | + |
| 27 | +## Directory Structure |
| 28 | + |
| 29 | +```text |
| 30 | +tools/topology/topology2/ |
| 31 | +├── CMakeLists.txt # Build system entry point |
| 32 | +├── get_abi.sh # ABI version extraction script |
| 33 | +├── cavs-sdw.conf # SoundWire topology entry point |
| 34 | +├── sof-hda-generic.conf # HDA generic topology entry point |
| 35 | +├── cavs-mixin-mixout-hda.conf # HDA with mixer pipelines |
| 36 | +├── cavs-nocodec.conf # SSP nocodec topology |
| 37 | +├── ... # Other top-level .conf entry points |
| 38 | +├── include/ |
| 39 | +│ ├── common/ # Core class definitions (PCM, route, audio formats) |
| 40 | +│ ├── components/ # Widget/component classes (gain, mixin, EQ, DRC) |
| 41 | +│ ├── controls/ # Control classes (mixer, enum, bytes) |
| 42 | +│ ├── dais/ # DAI classes (SSP, DMIC, HDA, ALH) |
| 43 | +│ └── pipelines/ # Pipeline template classes |
| 44 | +│ └── cavs/ # CAVS-architecture pipeline classes |
| 45 | +├── platform/ |
| 46 | +│ └── intel/ # Platform-specific overrides (tgl, mtl, lnl, ptl) |
| 47 | +├── production/ # CMake targets for production topologies |
| 48 | +│ ├── tplg-targets-ace1.cmake # ACE1 (MTL) targets |
| 49 | +│ ├── tplg-targets-ace2.cmake # ACE2 (LNL) targets |
| 50 | +│ ├── tplg-targets-ace3.cmake # ACE3 (PTL) targets |
| 51 | +│ └── ... # Additional platform target files |
| 52 | +├── development/ # CMake targets for development/testing |
| 53 | +└── doc/ # Doxygen documentation source |
| 54 | +``` |
| 55 | + |
| 56 | +## Best Practices for Adding New Topology Definitions |
| 57 | + |
| 58 | +### Topology Structure |
| 59 | + |
| 60 | +A top-level topology `.conf` file follows a layered configuration pattern: |
| 61 | + |
| 62 | +```conf |
| 63 | +# 1. Search directories |
| 64 | +<searchdir:include> |
| 65 | +<searchdir:include/common> |
| 66 | +<searchdir:include/components> |
| 67 | +<searchdir:include/dais> |
| 68 | +<searchdir:include/pipelines/cavs> |
| 69 | +<searchdir:platform/intel> |
| 70 | +
|
| 71 | +# 2. Include class files |
| 72 | +<vendor-token.conf> |
| 73 | +<tokens.conf> |
| 74 | +<pcm.conf> |
| 75 | +<host-copier-gain-mixin-playback.conf> |
| 76 | +<mixout-gain-alh-dai-copier-playback.conf> |
| 77 | +
|
| 78 | +# 3. Define block (default variable values) |
| 79 | +Define { |
| 80 | + PLATFORM "" |
| 81 | + NUM_HDMIS 3 |
| 82 | + DEEP_BUFFER_PCM_ID 31 |
| 83 | +} |
| 84 | +
|
| 85 | +# 4. Platform overrides (conditional includes) |
| 86 | +IncludeByKey.PLATFORM { |
| 87 | + "mtl" "platform/intel/mtl.conf" |
| 88 | + "lnl" "platform/intel/lnl.conf" |
| 89 | + "ptl" "platform/intel/ptl.conf" |
| 90 | +} |
| 91 | +
|
| 92 | +# 5. Conditional feature includes |
| 93 | +IncludeByKey.NUM_HDMIS { |
| 94 | + "3" "platform/intel/hdmi-generic.conf" |
| 95 | +} |
| 96 | +
|
| 97 | +# 6. DAI, Pipeline, PCM objects |
| 98 | +# 7. Route definitions |
| 99 | +``` |
| 100 | + |
| 101 | +### Reusing Existing Bases |
| 102 | + |
| 103 | +The most common way to add a new topology is to reuse an existing base `.conf` file and |
| 104 | +override variables through a cmake target entry. Targets are defined in |
| 105 | +`production/tplg-targets-*.cmake` files using a tuple format: |
| 106 | + |
| 107 | +```text |
| 108 | +"input-conf;output-name;variables" |
| 109 | +``` |
| 110 | + |
| 111 | +For example, to add a new SoundWire topology variant for ACE2 (Lunar Lake): |
| 112 | + |
| 113 | +```text |
| 114 | +"cavs-sdw\;sof-lnl-sdw-cs42l43-l0-cs35l56-l12\;PLATFORM=lnl,NUM_SDW_AMP_LINKS=2" |
| 115 | +``` |
| 116 | + |
| 117 | +The first element is the base `.conf` file (without extension), the second is the output |
| 118 | +`.tplg` filename, and the third is a comma-separated list of variable overrides. |
| 119 | + |
| 120 | +### Creating a New Base Topology |
| 121 | + |
| 122 | +When existing bases do not cover a new use case, create a new top-level `.conf` file: |
| 123 | + |
| 124 | +1. Create a new `.conf` file in `tools/topology/topology2/` following the layered |
| 125 | + structure described above |
| 126 | +2. Include the required class files from `include/` directories via search directives |
| 127 | +3. Define default variables in a `Define` block |
| 128 | +4. Add `IncludeByKey.PLATFORM` entries for platform-specific overrides |
| 129 | +5. Instantiate DAI, Pipeline, and PCM objects with appropriate IDs |
| 130 | +6. Define routes connecting FE mixin outputs to BE mixout inputs |
| 131 | +7. Register the topology as a cmake target in the appropriate |
| 132 | + `production/tplg-targets-*.cmake` file |
| 133 | + |
| 134 | +### PCM ID Conventions |
| 135 | + |
| 136 | +PCM IDs identify audio streams exposed to userspace via ALSA. Each PCM ID must be unique |
| 137 | +within a single topology. Different topology families (SoundWire vs HDA) use different |
| 138 | +default ID ranges for the same endpoint types. |
| 139 | + |
| 140 | +**SoundWire PCM IDs:** |
| 141 | + |
| 142 | +| Endpoint | Default PCM ID | Override Variable | |
| 143 | +|---|---|---| |
| 144 | +| Jack (playback/capture) | 0 | — | |
| 145 | +| Speaker amplifier | 2 | — | |
| 146 | +| SDW DMIC | 4 | — | |
| 147 | +| HDMI 1 | 5 | `HDMI1_PCM_ID` | |
| 148 | +| HDMI 2 | 6 | `HDMI2_PCM_ID` | |
| 149 | +| HDMI 3 | 7 | `HDMI3_PCM_ID` | |
| 150 | +| PCH DMIC0 | 10 | `DMIC0_PCM_ID` | |
| 151 | +| PCH DMIC1 | 11 | `DMIC1_PCM_ID` | |
| 152 | +| Jack Echo Ref | 11 | `SDW_JACK_ECHO_REF_PCM_ID` | |
| 153 | +| Speaker Echo Ref | 12 | `SDW_SPK_ECHO_REF_PCM_ID` | |
| 154 | +| Bluetooth | 2 or 20 | `BT_PCM_ID` | |
| 155 | +| Deep Buffer (Jack) | 31 | `DEEP_BUFFER_PCM_ID` | |
| 156 | +| Deep Buffer (Speaker) | 35 | `DEEP_BUFFER_PCM_ID_2` | |
| 157 | +| DMIC Deep Buffer | 46 | `DMIC0_DEEP_BUFFER_PCM_ID` | |
| 158 | +| Compressed Playback 1 | 50 | `COMPR_PCM_ID` | |
| 159 | +| Compressed Playback 2 | 52 | `COMPR_2_PCM_ID` | |
| 160 | + |
| 161 | +> **Note:** Bluetooth defaults to PCM ID 2 in some topologies and 20 in others. Use the |
| 162 | +> `BT_PCM_ID` override variable to set the correct value when BT coexists with a speaker |
| 163 | +> amplifier (which also uses PCM ID 2 by default). |
| 164 | +
|
| 165 | +**HDA PCM IDs:** |
| 166 | + |
| 167 | +| Endpoint | Default PCM ID | Override Variable | |
| 168 | +|---|---|---| |
| 169 | +| HDA Analog | 0 | — | |
| 170 | +| HDMI 1 | 3 | `HDMI1_PCM_ID` | |
| 171 | +| HDMI 2 | 4 | `HDMI2_PCM_ID` | |
| 172 | +| HDMI 3 | 5 | `HDMI3_PCM_ID` | |
| 173 | +| DMIC0 | 6 | `DMIC0_PCM_ID` | |
| 174 | +| Deep Buffer | 31 | `DEEP_BUFFER_PCM_ID` | |
| 175 | + |
| 176 | +Key rules: |
| 177 | + |
| 178 | +* PCM ID 0 is always the primary playback endpoint |
| 179 | +* PCM IDs must be unique within a single topology |
| 180 | +* When features coexist (SDW + PCH DMIC + HDMI), adjust IDs via `Define` overrides in |
| 181 | + cmake targets to avoid conflicts |
| 182 | +* Different topology families (SDW vs HDA) use different default ID ranges for the same |
| 183 | + endpoint types |
| 184 | + |
| 185 | +### Pipeline ID Conventions |
| 186 | + |
| 187 | +Pipeline IDs are set via the `index` attribute on pipeline objects. Front-end (FE) and |
| 188 | +back-end (BE) pipelines are paired, with the FE pipeline at index N and the BE pipeline |
| 189 | +at index N+1. |
| 190 | + |
| 191 | +In SoundWire topologies, pipeline indexes follow the convention documented in |
| 192 | +`sdw-amp-generic.conf` and `sdw-dmic-generic.conf`: pipeline index = PCM ID × 10. HDMI |
| 193 | +pipelines use a stride-10 pattern where the host pipeline is at N0 and the DAI pipeline |
| 194 | +is at N1 (50/51, 60/61, 70/71, 80/81). |
| 195 | + |
| 196 | +**SoundWire Pipeline IDs:** |
| 197 | + |
| 198 | +| Pipeline | Default Index | Override Variable | |
| 199 | +|---|---|---| |
| 200 | +| Jack Playback FE / BE | 0 / 1 | — | |
| 201 | +| Jack Capture FE / BE | 10 / 11 | — | |
| 202 | +| Deep Buffer (Jack) | 15 | `DEEP_BUFFER_PIPELINE_ID` | |
| 203 | +| Deep Buffer (Speaker) | 16 | `DEEP_BUFFER_PIPELINE_ID_2` | |
| 204 | +| Speaker FE / BE | 20 / 21 | — | |
| 205 | +| SDW DMIC FE / BE | 40 / 41 | `SDW_DMIC_HOST_PIPELINE_ID` | |
| 206 | +| HDMI 1 Host / DAI | 50 / 51 | `HDMI1_HOST_PIPELINE_ID` / `HDMI1_DAI_PIPELINE_ID` | |
| 207 | +| HDMI 2 Host / DAI | 60 / 61 | `HDMI2_HOST_PIPELINE_ID` / `HDMI2_DAI_PIPELINE_ID` | |
| 208 | +| HDMI 3 Host / DAI | 70 / 71 | `HDMI3_HOST_PIPELINE_ID` / `HDMI3_DAI_PIPELINE_ID` | |
| 209 | +| HDMI 4 Host / DAI | 80 / 81 | `HDMI4_HOST_PIPELINE_ID` / `HDMI4_DAI_PIPELINE_ID` | |
| 210 | +| Compressed 1 / 2 | 90 / 92 | `COMPR_PIPELINE_ID` / `COMPR_2_PIPELINE_ID` | |
| 211 | +| PCH DMIC0 Host / DAI | 100 / 101 | `DMIC0_HOST_PIPELINE_ID` / `DMIC0_DAI_PIPELINE_ID` | |
| 212 | + |
| 213 | +**HDA Pipeline IDs:** |
| 214 | + |
| 215 | +| Pipeline | Default Index | Override Variable | |
| 216 | +|---|---|---| |
| 217 | +| Analog Playback FE / BE | 1 / 2 | — | |
| 218 | +| Analog Capture FE / BE | 3 / 4 | — | |
| 219 | +| DMIC0 Host / DAI | 11 / 12 | `DMIC0_HOST_PIPELINE_ID` / `DMIC0_DAI_PIPELINE_ID` | |
| 220 | +| Deep Buffer | 15 | `DEEP_BUFFER_PIPELINE_ID` | |
| 221 | +| HDMI 1 Host / DAI | 50 / 51 | `HDMI1_HOST_PIPELINE_ID` / `HDMI1_DAI_PIPELINE_ID` | |
| 222 | +| HDMI 2 Host / DAI | 60 / 61 | `HDMI2_HOST_PIPELINE_ID` / `HDMI2_DAI_PIPELINE_ID` | |
| 223 | +| HDMI 3 Host / DAI | 70 / 71 | `HDMI3_HOST_PIPELINE_ID` / `HDMI3_DAI_PIPELINE_ID` | |
| 224 | +| HDMI 4 Host / DAI | 80 / 81 | `HDMI4_HOST_PIPELINE_ID` / `HDMI4_DAI_PIPELINE_ID` | |
| 225 | + |
| 226 | +Key rules: |
| 227 | + |
| 228 | +* FE and BE pipelines are paired: FE = N, BE = N+1 |
| 229 | +* SDW convention: pipeline index = PCM ID × 10 (documented in `sdw-amp-generic.conf` and |
| 230 | + `sdw-dmic-generic.conf`) |
| 231 | +* HDMI uses stride-10: Host = N0, DAI = N1 |
| 232 | +* Pipeline IDs must be unique within a single topology |
| 233 | +* When adding new endpoints, select IDs in unused ranges that do not conflict with |
| 234 | + existing assignments |
| 235 | + |
| 236 | +### Widget Naming |
| 237 | + |
| 238 | +Widget names follow the convention `<type>.<pipeline-index>.<instance>`. Examples: |
| 239 | + |
| 240 | +* `gain.1.1` — gain widget in pipeline 1, instance 1 |
| 241 | +* `mixin.15.1` — mixin widget in pipeline 15, instance 1 |
| 242 | +* `host-copier.0.playback` — host copier in pipeline 0, playback direction |
| 243 | +* `dai-copier.1.ALH` — DAI copier in pipeline 1, ALH type |
| 244 | + |
| 245 | +### Route Definitions |
| 246 | + |
| 247 | +Routes connect FE pipeline mixin outputs to BE pipeline mixout inputs. This is the |
| 248 | +primary mechanism for linking front-end and back-end pipelines: |
| 249 | + |
| 250 | +```conf |
| 251 | +Object.Base.route [ |
| 252 | + { |
| 253 | + source "mixin.15.1" |
| 254 | + sink "mixout.2.1" |
| 255 | + } |
| 256 | +] |
| 257 | +``` |
| 258 | + |
| 259 | +Multiple FE pipelines can feed into a single BE mixout. For example, both a normal |
| 260 | +playback pipeline and a deep buffer pipeline can route to the same DAI output: |
| 261 | + |
| 262 | +```text |
| 263 | +host-copier.0 -> gain.0 -> mixin.0 ─┐ |
| 264 | + ├─> mixout.1 -> gain.1 -> dai-copier.1 -> DAI |
| 265 | +host-copier.15 -> gain.15 -> mixin.15┘ |
| 266 | +``` |
| 267 | + |
| 268 | +### Platform Overrides |
| 269 | + |
| 270 | +Platform-specific configurations are applied using the `IncludeByKey.PLATFORM` mechanism. |
| 271 | +Each platform `.conf` file under `platform/intel/` contains `Define` blocks that override |
| 272 | +variables such as `DMIC_DRIVER_VERSION`, `SSP_BLOB_VERSION`, and `NUM_HDMIS`. |
| 273 | + |
| 274 | +Supported platforms: |
| 275 | + |
| 276 | +* `tgl` — Tiger Lake / Alder Lake (CAVS 2.5) |
| 277 | +* `mtl` — Meteor Lake (ACE 1.x) |
| 278 | +* `lnl` — Lunar Lake (ACE 2.x) |
| 279 | +* `ptl` — Panther Lake (ACE 3.x) |
| 280 | + |
| 281 | +```conf |
| 282 | +IncludeByKey.PLATFORM { |
| 283 | + "mtl" "platform/intel/mtl.conf" |
| 284 | + "lnl" "platform/intel/lnl.conf" |
| 285 | + "ptl" "platform/intel/ptl.conf" |
| 286 | +} |
| 287 | +``` |
| 288 | + |
| 289 | +### Registering CMake Targets |
| 290 | + |
| 291 | +Production topologies are registered in `production/tplg-targets-*.cmake` files. Each |
| 292 | +target is a semicolon-separated tuple: |
| 293 | + |
| 294 | +```text |
| 295 | +"input-conf;output-name;variable1=value1,variable2=value2" |
| 296 | +``` |
| 297 | + |
| 298 | +Select the cmake file matching the target platform generation: |
| 299 | + |
| 300 | +| Platform | CMake Target File | |
| 301 | +|---|---| |
| 302 | +| Tiger Lake / Alder Lake | `tplg-targets-cavs25.cmake` | |
| 303 | +| Meteor Lake | `tplg-targets-ace1.cmake` | |
| 304 | +| Lunar Lake | `tplg-targets-ace2.cmake` | |
| 305 | +| Panther Lake | `tplg-targets-ace3.cmake` | |
| 306 | +| HDA generic | `tplg-targets-hda-generic.cmake` | |
| 307 | + |
| 308 | +Development and testing topologies go in `development/tplg-targets.cmake`. |
| 309 | + |
| 310 | +## Building Topologies |
| 311 | + |
| 312 | +Configure the build with cmake and build the topology targets: |
| 313 | + |
| 314 | +```bash |
| 315 | +mkdir build && cd build |
| 316 | +cmake .. |
| 317 | +make -j$(nproc) |
| 318 | +``` |
| 319 | + |
| 320 | +To build a specific topology target: |
| 321 | + |
| 322 | +```bash |
| 323 | +make sof-lnl-sdw-cs42l43-l0-cs35l56-l12 |
| 324 | +``` |
| 325 | + |
| 326 | +The compiled `.tplg` files are placed in the build output directory. |
0 commit comments