1// Copyright 2017 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 "qemu-stream.h"
6
7#include <fbl/vector.h>
8
9namespace audio {
10namespace intel_hda {
11namespace codecs {
12
13static constexpr uint8_t UNITY_GAIN = 74;
14static const audio_stream_unique_id_t microphone_id = AUDIO_STREAM_UNIQUE_ID_BUILTIN_MICROPHONE;
15static const audio_stream_unique_id_t speaker_id = AUDIO_STREAM_UNIQUE_ID_BUILTIN_SPEAKERS;
16
17QemuStream::QemuStream(uint32_t stream_id, bool is_input, uint16_t converter_nid)
18    : IntelHDAStreamBase(stream_id, is_input),
19      converter_nid_(converter_nid) {
20    SetPersistentUniqueId(is_input ? microphone_id : speaker_id);
21}
22
23zx_status_t QemuStream::DisableConverterLocked(bool force_all) {
24    const CodecVerb DISABLE_CONVERTER_VERBS[] = {
25        SET_AMPLIFIER_GAIN_MUTE(true, 0, is_input(), !is_input()),
26        SET_CONVERTER_STREAM_CHAN(IHDA_INVALID_STREAM_TAG, 0),
27    };
28
29    return RunCmdListLocked(DISABLE_CONVERTER_VERBS, countof(DISABLE_CONVERTER_VERBS), force_all);
30}
31
32zx_status_t QemuStream::RunCmdListLocked(const CodecVerb* list, size_t count, bool force_all) {
33    ZX_DEBUG_ASSERT(list);
34
35    zx_status_t total_res = ZX_OK;
36    for (size_t i = 0; i < count; ++i) {
37        const auto& verb = list[i];
38
39        zx_status_t res = SendCodecCommandLocked(converter_nid_, verb, Ack::NO);
40        if ((res != ZX_OK) && !force_all)
41            return res;
42
43        if (total_res == ZX_OK)
44            total_res = res;
45    }
46
47    return total_res;
48}
49
50zx_status_t QemuStream::OnActivateLocked() {
51    fbl::Vector<audio_proto::FormatRange> supported_formats;
52
53    audio_proto::FormatRange range;
54    range.sample_formats = AUDIO_SAMPLE_FORMAT_16BIT;
55    range.min_channels = 1;
56    range.max_channels = 2;
57    range.min_frames_per_second = 16000;
58    range.max_frames_per_second = 96000;
59    range.flags = ASF_RANGE_FLAG_FPS_48000_FAMILY | ASF_RANGE_FLAG_FPS_44100_FAMILY;
60
61    fbl::AllocChecker ac;
62    supported_formats.push_back(range, &ac);
63    if (!ac.check())
64        return ZX_ERR_NO_MEMORY;
65
66    SetSupportedFormatsLocked(fbl::move(supported_formats));
67
68    return DisableConverterLocked();
69}
70
71void QemuStream::OnDeactivateLocked() {
72    DisableConverterLocked(true);
73}
74
75zx_status_t QemuStream::BeginChangeStreamFormatLocked(const audio_proto::StreamSetFmtReq& fmt) {
76    return DisableConverterLocked();
77}
78
79zx_status_t QemuStream::FinishChangeStreamFormatLocked(uint16_t encoded_fmt) {
80    const CodecVerb ENABLE_CONVERTER_VERBS[] = {
81        SET_CONVERTER_FORMAT(encoded_fmt),
82        SET_CONVERTER_STREAM_CHAN(dma_stream_tag(), 0),
83        SET_AMPLIFIER_GAIN_MUTE(false, UNITY_GAIN, is_input(), !is_input()),
84    };
85
86    return RunCmdListLocked(ENABLE_CONVERTER_VERBS, countof(ENABLE_CONVERTER_VERBS));
87}
88
89void QemuStream::OnGetStringLocked(const audio_proto::GetStringReq& req,
90                                   audio_proto::GetStringResp* out_resp) {
91    ZX_DEBUG_ASSERT(out_resp);
92    const char* str = nullptr;
93
94    switch (req.id) {
95        case AUDIO_STREAM_STR_ID_MANUFACTURER:
96            str = "QEMU";
97            break;
98
99        case AUDIO_STREAM_STR_ID_PRODUCT:
100            str = is_input() ? "Builtin Microphone" : "Builtin Speakers";
101            break;
102
103        default:
104            IntelHDAStreamBase::OnGetStringLocked(req, out_resp);
105            return;
106    }
107
108    int res = snprintf(reinterpret_cast<char*>(out_resp->str), sizeof(out_resp->str), "%s",
109                       str ? str : "<unassigned>");
110    ZX_DEBUG_ASSERT(res >= 0);
111    out_resp->result = ZX_OK;
112    out_resp->strlen = fbl::min<uint32_t>(res, sizeof(out_resp->str) - 1);
113    out_resp->id = req.id;
114}
115
116
117}  // namespace codecs
118}  // namespace intel_hda
119}  // namespace audio
120