1// Copyright 2018 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <zircon/device/audio.h> 6 7#include "intel-audio-dsp.h" 8#include "intel-dsp-topology.h" 9 10namespace audio { 11namespace intel_hda { 12 13namespace { 14// Module config parameters extracted from kbl_i2s_chrome.conf 15 16// Set up 2 pipelines for system playback: 17// 1. copier[host DMA]->mixin 18// 2. mixout->copier[I2S DMA] 19// 2 Pipelines are needed because only one instance of a module can exist in a pipeline. 20constexpr uint8_t PIPELINE0_ID = 0; 21constexpr uint8_t PIPELINE1_ID = 1; 22 23// Set up 2 pipelines for system capture: 24// 2. copier[I2S DMA]->mixin 25// 3. mixout->copier[host DMA] 26// 2 Pipelines are needed because only one instance of a module can exist in a pipeline. 27constexpr uint8_t PIPELINE2_ID = 2; 28constexpr uint8_t PIPELINE3_ID = 3; 29 30// Module instance IDs. 31constexpr uint8_t HOST_OUT_COPIER_ID = 0; 32constexpr uint8_t I2S0_OUT_COPIER_ID = 1; 33constexpr uint8_t I2S0_IN_COPIER_ID = 2; 34constexpr uint8_t HOST_IN_COPIER_ID = 3; 35 36constexpr uint8_t HOST_OUT_MIXIN_ID = 0; 37constexpr uint8_t I2S0_IN_MIXIN_ID = 1; 38 39constexpr uint8_t I2S0_OUT_MIXOUT_ID = 0; 40constexpr uint8_t HOST_IN_MIXOUT_ID = 1; 41 42const struct PipelineConfig { 43 uint8_t id; 44 uint8_t priority; 45 uint8_t mem_pages; 46 bool lp; 47} PIPELINE_CFG[] = { 48{ 49 .id = PIPELINE0_ID, 50 .priority = 0, 51 .mem_pages = 2, 52 .lp = true, // false in config, keep running in low power mode for dev 53}, 54{ 55 .id = PIPELINE1_ID, 56 .priority = 0, 57 .mem_pages = 4, 58 .lp = true, 59}, 60{ 61 .id = PIPELINE2_ID, 62 .priority = 0, 63 .mem_pages = 2, 64 .lp = true, 65}, 66{ 67 .id = PIPELINE3_ID, 68 .priority = 0, 69 .mem_pages = 2, 70 .lp = true, 71}, 72}; 73 74// Use 48khz 16-bit stereo throughout 75const AudioDataFormat FMT_HOST = { 76 .sampling_frequency = SamplingFrequency::FS_48000HZ, 77 .bit_depth = BitDepth::DEPTH_16BIT, 78 .channel_map = 0xFFFFFF10, 79 .channel_config = ChannelConfig::CONFIG_STEREO, 80 .interleaving_style = InterleavingStyle::PER_CHANNEL, 81 .number_of_channels = 2, 82 .valid_bit_depth = 16, 83 .sample_type = SampleType::INT_MSB, 84 .reserved = 0, 85}; 86 87const AudioDataFormat FMT_I2S = { 88 .sampling_frequency = SamplingFrequency::FS_48000HZ, 89 .bit_depth = BitDepth::DEPTH_32BIT, 90 .channel_map = 0xFFFFFF10, 91 .channel_config = ChannelConfig::CONFIG_STEREO, 92 .interleaving_style = InterleavingStyle::PER_CHANNEL, 93 .number_of_channels = 2, 94 .valid_bit_depth = 16, 95 .sample_type = SampleType::INT_MSB, 96 .reserved = 0, 97}; 98 99// Mixer modules only operate on 32-bits 100const AudioDataFormat FMT_MIXER = { 101 .sampling_frequency = SamplingFrequency::FS_48000HZ, 102 .bit_depth = BitDepth::DEPTH_32BIT, 103 .channel_map = 0xFFFFFF10, 104 .channel_config = ChannelConfig::CONFIG_STEREO, 105 .interleaving_style = InterleavingStyle::PER_CHANNEL, 106 .number_of_channels = 2, 107 .valid_bit_depth = 32, 108 .sample_type = SampleType::INT_MSB, 109 .reserved = 0, 110}; 111 112const CopierCfg HOST_OUT_COPIER_CFG = { 113 .base_cfg = { 114 .cpc = 100000, 115 .ibs = 384, 116 .obs = 384, 117 .is_pages = 0, 118 .audio_fmt = { 119 .sampling_frequency = FMT_HOST.sampling_frequency, 120 .bit_depth = FMT_HOST.bit_depth, 121 .channel_map = FMT_HOST.channel_map, 122 .channel_config = FMT_HOST.channel_config, 123 .interleaving_style = FMT_HOST.interleaving_style, 124 .number_of_channels = FMT_HOST.number_of_channels, 125 .valid_bit_depth = FMT_HOST.valid_bit_depth, 126 .sample_type = FMT_HOST.sample_type, 127 .reserved = 0, 128 }, 129 }, 130 .out_fmt = { 131 .sampling_frequency = FMT_MIXER.sampling_frequency, 132 .bit_depth = FMT_MIXER.bit_depth, 133 .channel_map = FMT_MIXER.channel_map, 134 .channel_config = FMT_MIXER.channel_config, 135 .interleaving_style = FMT_MIXER.interleaving_style, 136 .number_of_channels = FMT_MIXER.number_of_channels, 137 .valid_bit_depth = FMT_MIXER.valid_bit_depth, 138 .sample_type = FMT_MIXER.sample_type, 139 .reserved = 0, 140 }, 141 .copier_feature_mask = 0, 142 .gtw_cfg = { 143 .node_id = HDA_GATEWAY_CFG_NODE_ID(DMA_TYPE_HDA_HOST_OUTPUT, 0), 144 .dma_buffer_size = 2 * 384, 145 .config_length = 0, 146 }, 147}; 148 149const CopierCfg HOST_IN_COPIER_CFG = { 150 .base_cfg = { 151 .cpc = 100000, 152 .ibs = 384, 153 .obs = 384, 154 .is_pages = 0, 155 .audio_fmt = { 156 .sampling_frequency = FMT_MIXER.sampling_frequency, 157 .bit_depth = FMT_MIXER.bit_depth, 158 .channel_map = FMT_MIXER.channel_map, 159 .channel_config = FMT_MIXER.channel_config, 160 .interleaving_style = FMT_MIXER.interleaving_style, 161 .number_of_channels = FMT_MIXER.number_of_channels, 162 .valid_bit_depth = FMT_MIXER.valid_bit_depth, 163 .sample_type = FMT_MIXER.sample_type, 164 .reserved = 0, 165 }, 166 }, 167 .out_fmt = { 168 .sampling_frequency = FMT_HOST.sampling_frequency, 169 .bit_depth = FMT_HOST.bit_depth, 170 .channel_map = FMT_HOST.channel_map, 171 .channel_config = FMT_HOST.channel_config, 172 .interleaving_style = FMT_HOST.interleaving_style, 173 .number_of_channels = FMT_HOST.number_of_channels, 174 .valid_bit_depth = FMT_HOST.valid_bit_depth, 175 .sample_type = FMT_HOST.sample_type, 176 .reserved = 0, 177 }, 178 .copier_feature_mask = 0, 179 .gtw_cfg = { 180 .node_id = HDA_GATEWAY_CFG_NODE_ID(DMA_TYPE_HDA_HOST_INPUT, 0), 181 .dma_buffer_size = 2 * 384, 182 .config_length = 0, 183 }, 184}; 185 186constexpr uint8_t I2S_OUT_INSTANCE_ID = 0; 187 188const CopierCfg I2S_OUT_COPIER_CFG = { 189 .base_cfg = { 190 .cpc = 100000, 191 .ibs = 384, 192 .obs = 384, 193 .is_pages = 0, 194 .audio_fmt = { 195 .sampling_frequency = FMT_MIXER.sampling_frequency, 196 .bit_depth = FMT_MIXER.bit_depth, 197 .channel_map = FMT_MIXER.channel_map, 198 .channel_config = FMT_MIXER.channel_config, 199 .interleaving_style = FMT_MIXER.interleaving_style, 200 .number_of_channels = FMT_MIXER.number_of_channels, 201 .valid_bit_depth = FMT_MIXER.valid_bit_depth, 202 .sample_type = FMT_MIXER.sample_type, 203 .reserved = 0, 204 }, 205 }, 206 .out_fmt = { 207 .sampling_frequency = FMT_I2S.sampling_frequency, 208 .bit_depth = FMT_I2S.bit_depth, 209 .channel_map = FMT_I2S.channel_map, 210 .channel_config = FMT_I2S.channel_config, 211 .interleaving_style = FMT_I2S.interleaving_style, 212 .number_of_channels = FMT_I2S.number_of_channels, 213 .valid_bit_depth = FMT_I2S.valid_bit_depth, 214 .sample_type = FMT_I2S.sample_type, 215 .reserved = 0, 216 }, 217 .copier_feature_mask = 0, 218 .gtw_cfg = { 219 .node_id = I2S_GATEWAY_CFG_NODE_ID(DMA_TYPE_I2S_LINK_OUTPUT, I2S_OUT_INSTANCE_ID, 0), 220 .dma_buffer_size = 2 * 384, 221 .config_length = 0, 222 }, 223}; 224 225constexpr uint8_t I2S_IN_INSTANCE_ID = 0; 226 227const CopierCfg I2S_IN_COPIER_CFG = { 228 .base_cfg = { 229 .cpc = 100000, 230 .ibs = 384, 231 .obs = 384, 232 .is_pages = 0, 233 .audio_fmt = { 234 .sampling_frequency = FMT_I2S.sampling_frequency, 235 .bit_depth = FMT_I2S.bit_depth, 236 .channel_map = FMT_I2S.channel_map, 237 .channel_config = FMT_I2S.channel_config, 238 .interleaving_style = FMT_I2S.interleaving_style, 239 .number_of_channels = FMT_I2S.number_of_channels, 240 .valid_bit_depth = FMT_I2S.valid_bit_depth, 241 .sample_type = FMT_I2S.sample_type, 242 .reserved = 0, 243 }, 244 }, 245 .out_fmt = { 246 .sampling_frequency = FMT_MIXER.sampling_frequency, 247 .bit_depth = FMT_MIXER.bit_depth, 248 .channel_map = FMT_MIXER.channel_map, 249 .channel_config = FMT_MIXER.channel_config, 250 .interleaving_style = FMT_MIXER.interleaving_style, 251 .number_of_channels = FMT_MIXER.number_of_channels, 252 .valid_bit_depth = FMT_MIXER.valid_bit_depth, 253 .sample_type = FMT_MIXER.sample_type, 254 .reserved = 0, 255 }, 256 .copier_feature_mask = 0, 257 .gtw_cfg = { 258 .node_id = I2S_GATEWAY_CFG_NODE_ID(DMA_TYPE_I2S_LINK_INPUT, I2S_IN_INSTANCE_ID, 0), 259 .dma_buffer_size = 2 * 384, 260 .config_length = 0, 261 }, 262}; 263 264const BaseModuleCfg MIXER_CFG = { 265 .cpc = 100000, 266 .ibs = 384, 267 .obs = 384, 268 .is_pages = 0, 269 .audio_fmt = { 270 .sampling_frequency = FMT_MIXER.sampling_frequency, 271 .bit_depth = FMT_MIXER.bit_depth, 272 .channel_map = FMT_MIXER.channel_map, 273 .channel_config = FMT_MIXER.channel_config, 274 .interleaving_style = FMT_MIXER.interleaving_style, 275 .number_of_channels = FMT_MIXER.number_of_channels, 276 .valid_bit_depth = FMT_MIXER.valid_bit_depth, 277 .sample_type = FMT_MIXER.sample_type, 278 .reserved = 0, 279 }, 280}; 281 282} // anon namespace 283 284zx_status_t IntelAudioDsp::GetI2SBlob(uint8_t bus_id, uint8_t direction, 285 const AudioDataFormat& format, 286 const void** out_blob, size_t* out_size) { 287 zx_status_t st = ZX_ERR_NOT_FOUND; 288 for (const auto& cfg : i2s_configs_) { 289 if (!cfg.valid) { 290 break; 291 } 292 if ((cfg.bus_id != bus_id) || (cfg.direction != direction)) { 293 continue; 294 } 295 // TODO better matching here 296 const formats_config_t* formats = cfg.formats; 297 const format_config_t* f = &formats->format_configs[0]; 298 for (size_t j = 0; j < formats->format_config_count; j++) { 299 if (format.valid_bit_depth != f->valid_bits_per_sample) { 300 f = reinterpret_cast<const format_config_t*>( 301 reinterpret_cast<const uint8_t*>(f) + 302 sizeof(*f) + 303 f->config.capabilities_size 304 ); 305 continue; 306 } 307 *out_blob = reinterpret_cast<const void*>(f->config.capabilities); 308 *out_size = static_cast<size_t>(f->config.capabilities_size); 309 st = ZX_OK; 310 break; 311 } 312 } 313 return st; 314} 315 316zx_status_t IntelAudioDsp::CreateHostDmaModule(uint8_t instance_id, uint8_t pipeline_id, 317 const CopierCfg& cfg) { 318 return ipc_.InitInstance(module_ids_[Module::COPIER], 319 instance_id, 320 ProcDomain::LOW_LATENCY, 321 0, 322 pipeline_id, 323 sizeof(cfg), 324 &cfg); 325} 326 327zx_status_t IntelAudioDsp::CreateI2SModule(uint8_t instance_id, uint8_t pipeline_id, 328 uint8_t i2s_instance_id, uint8_t direction, 329 const CopierCfg& cfg) { 330 const void* blob; 331 size_t blob_size; 332 zx_status_t st = GetI2SBlob(i2s_instance_id, direction, 333 (direction == NHLT_DIRECTION_RENDER) ? cfg.out_fmt : 334 cfg.base_cfg.audio_fmt, 335 &blob, &blob_size); 336 if (st != ZX_OK) { 337 LOG(ERROR, "I2S config (instance %u direction %u) not found\n", 338 i2s_instance_id, direction); 339 return st; 340 } 341 342 // Copy the I2S config blob 343 size_t cfg_size = sizeof(cfg) + blob_size; 344 ZX_DEBUG_ASSERT(cfg_size <= UINT16_MAX); 345 346 fbl::AllocChecker ac; 347 fbl::unique_ptr<uint8_t[]> cfg_buf(new (&ac) uint8_t[cfg_size]); 348 if (!ac.check()) { 349 LOG(ERROR, "out of memory while attempting to allocate copier config buffer\n"); 350 return ZX_ERR_NO_MEMORY; 351 } 352 memcpy(cfg_buf.get() + sizeof(cfg), blob, blob_size); 353 354 // Copy the copier config 355 memcpy(cfg_buf.get(), &cfg, sizeof(cfg)); 356 auto copier_cfg = reinterpret_cast<CopierCfg*>(cfg_buf.get()); 357 copier_cfg->gtw_cfg.config_length = static_cast<uint32_t>(blob_size); 358 359 return ipc_.InitInstance(module_ids_[Module::COPIER], 360 instance_id, 361 ProcDomain::LOW_LATENCY, 362 0, 363 pipeline_id, 364 static_cast<uint16_t>(cfg_size), 365 cfg_buf.get()); 366} 367 368zx_status_t IntelAudioDsp::CreateMixinModule(uint8_t instance_id, uint8_t pipeline_id, 369 const BaseModuleCfg& cfg) { 370 return ipc_.InitInstance(module_ids_[Module::MIXIN], 371 instance_id, 372 ProcDomain::LOW_LATENCY, 373 0, 374 pipeline_id, 375 sizeof(cfg), 376 &cfg); 377} 378 379zx_status_t IntelAudioDsp::CreateMixoutModule(uint8_t instance_id, uint8_t pipeline_id, 380 const BaseModuleCfg& cfg) { 381 return ipc_.InitInstance(module_ids_[Module::MIXOUT], 382 instance_id, 383 ProcDomain::LOW_LATENCY, 384 0, 385 pipeline_id, 386 sizeof(cfg), 387 &cfg); 388} 389 390zx_status_t IntelAudioDsp::SetupPipelines() { 391 ZX_DEBUG_ASSERT(module_ids_[Module::COPIER] != 0); 392 ZX_DEBUG_ASSERT(module_ids_[Module::MIXIN] != 0); 393 ZX_DEBUG_ASSERT(module_ids_[Module::MIXOUT] != 0); 394 395 zx_status_t st = ZX_OK; 396 397 // Create pipelines 398 for (const auto& cfg : PIPELINE_CFG) { 399 st = ipc_.CreatePipeline(cfg.id, cfg.priority, cfg.mem_pages, cfg.lp); 400 if (st != ZX_OK) { 401 return st; 402 } 403 } 404 405 // Create pipeline 0 modules. Host DMA -> mixin 406 // Modules must be created in order of source -> sink 407 st = CreateHostDmaModule(HOST_OUT_COPIER_ID, PIPELINE0_ID, HOST_OUT_COPIER_CFG); 408 if (st != ZX_OK) { 409 return st; 410 } 411 st = CreateMixinModule(HOST_OUT_MIXIN_ID, PIPELINE0_ID, MIXER_CFG); 412 if (st != ZX_OK) { 413 return st; 414 } 415 416 // Bind pipeline 0 417 st = ipc_.Bind(module_ids_[Module::COPIER], HOST_OUT_COPIER_ID, 0, 418 module_ids_[Module::MIXIN], HOST_OUT_MIXIN_ID, 0); 419 if (st != ZX_OK) { 420 return st; 421 } 422 423 // Create pipeline 1 modules. mixout -> I2S DMA 424 st = CreateMixoutModule(I2S0_OUT_MIXOUT_ID, PIPELINE1_ID, MIXER_CFG); 425 if (st != ZX_OK) { 426 return st; 427 } 428 429 st = CreateI2SModule(I2S0_OUT_COPIER_ID, PIPELINE1_ID, 430 I2S_OUT_INSTANCE_ID, NHLT_DIRECTION_RENDER, 431 I2S_OUT_COPIER_CFG); 432 if (st != ZX_OK) { 433 return st; 434 } 435 436 // Bind pipeline 1 437 st = ipc_.Bind(module_ids_[Module::MIXOUT], I2S0_OUT_MIXOUT_ID, 0, 438 module_ids_[Module::COPIER], I2S0_OUT_COPIER_ID, 0); 439 if (st != ZX_OK) { 440 return st; 441 } 442 443 // Create pipeline 2 modules. I2S DMA -> mixin 444 st = CreateI2SModule(I2S0_IN_COPIER_ID, PIPELINE2_ID, 445 I2S_IN_INSTANCE_ID, NHLT_DIRECTION_CAPTURE, 446 I2S_IN_COPIER_CFG); 447 if (st != ZX_OK) { 448 return st; 449 } 450 st = CreateMixinModule(I2S0_IN_MIXIN_ID, PIPELINE2_ID, MIXER_CFG); 451 if (st != ZX_OK) { 452 return st; 453 } 454 455 // Bind pipeline 2 456 st = ipc_.Bind(module_ids_[Module::COPIER], I2S0_IN_COPIER_ID, 0, 457 module_ids_[Module::MIXIN], I2S0_IN_MIXIN_ID, 0); 458 if (st != ZX_OK) { 459 return st; 460 } 461 462 // Create pipeline 3 modules. mixout -> Host DMA 463 st = CreateMixoutModule(HOST_IN_MIXOUT_ID, PIPELINE3_ID, MIXER_CFG); 464 if (st != ZX_OK) { 465 return st; 466 } 467 st = CreateHostDmaModule(HOST_IN_COPIER_ID, PIPELINE3_ID, HOST_IN_COPIER_CFG); 468 if (st != ZX_OK) { 469 return st; 470 } 471 472 // Bind pipeline 2 473 st = ipc_.Bind(module_ids_[Module::MIXOUT], HOST_IN_MIXOUT_ID, 0, 474 module_ids_[Module::COPIER], HOST_IN_COPIER_ID, 0); 475 if (st != ZX_OK) { 476 return st; 477 } 478 479 // Bind playback pipeline 480 st = ipc_.Bind(module_ids_[Module::MIXIN], HOST_OUT_MIXIN_ID, 0, 481 module_ids_[Module::MIXOUT], I2S0_OUT_MIXOUT_ID, 0); 482 if (st != ZX_OK) { 483 return st; 484 } 485 486 // Bind capture pipeline 487 st = ipc_.Bind(module_ids_[Module::MIXIN], I2S0_IN_MIXIN_ID, 0, 488 module_ids_[Module::MIXOUT], HOST_IN_MIXOUT_ID, 0); 489 if (st != ZX_OK) { 490 return st; 491 } 492 493 return ZX_OK; 494} 495 496zx_status_t IntelAudioDsp::StartPipeline(const DspPipeline& pipeline) { 497 // Sink first and then source 498 zx_status_t st = RunPipeline(pipeline.pl_sink); 499 if (st != ZX_OK) { 500 return st; 501 } 502 return RunPipeline(pipeline.pl_source); 503 // TODO Error recovery 504} 505 506zx_status_t IntelAudioDsp::PausePipeline(const DspPipeline& pipeline) { 507 zx_status_t st = ipc_.SetPipelineState(pipeline.pl_source, PipelineState::PAUSED, true); 508 if (st != ZX_OK) { 509 return st; 510 } 511 st = ipc_.SetPipelineState(pipeline.pl_sink, PipelineState::PAUSED, true); 512 if (st != ZX_OK) { 513 return st; 514 } 515 // Reset DSP DMA 516 st = ipc_.SetPipelineState(pipeline.pl_source, PipelineState::RESET, true); 517 if (st != ZX_OK) { 518 return st; 519 } 520 return ipc_.SetPipelineState(pipeline.pl_sink, PipelineState::RESET, true); 521 // TODO Error recovery 522} 523 524zx_status_t IntelAudioDsp::CreateAndStartStreams() { 525 zx_status_t res = ZX_OK; 526 527 // Create and publish the streams we will use. 528 static struct { 529 uint32_t stream_id; 530 bool is_input; 531 struct DspPipeline pipeline; 532 audio_stream_unique_id_t uid; 533 } STREAMS[] = { 534 // Speakers 535 { 536 .stream_id = 1, 537 .is_input = false, 538 .pipeline = { 539 .pl_source = PIPELINE0_ID, 540 .pl_sink = PIPELINE1_ID, 541 }, 542 .uid = AUDIO_STREAM_UNIQUE_ID_BUILTIN_SPEAKERS, 543 }, 544 }; 545 546 for (const auto& stream_def : STREAMS) { 547 auto stream = fbl::AdoptRef(new IntelDspStream(stream_def.stream_id, 548 stream_def.is_input, 549 stream_def.pipeline, 550 &stream_def.uid)); 551 552 res = ActivateStream(stream); 553 if (res != ZX_OK) { 554 LOG(ERROR, "Failed to activate %s stream id #%u (res %d)!", 555 stream_def.is_input ? "input" : "output", stream_def.stream_id, res); 556 return res; 557 } 558 } 559 560 return ZX_OK; 561} 562 563 564} // namespace intel_hda 565} // namespace audio 566