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