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