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 <fbl/algorithm.h>
6 #include <fbl/alloc_checker.h>
7 #include <fbl/unique_ptr.h>
8 #include <limits>
9 #include <math.h>
10 #include <new>
11 #include <utility>
12
13 #include "hdmitx.h"
14 #include "vim-audio.h"
15 #include "vim-display.h"
16
17 namespace audio {
18 namespace vim2 {
19
20 // Create a ring buffer large enough to hold 1 second of 48kHz stereo 16-bit
21 // audio.
22 //
23 // TODO(johngro): Look into what it would take to remove the restriction that
24 // this buffer be contiguous so that we can more easily map the buffer on the
25 // fly without needing to take precious contiguous memory.
26 constexpr size_t SPDIF_RB_SIZE = fbl::round_up<size_t, size_t>(48000 * 2 * 2u, PAGE_SIZE);
27
~Vim2Audio()28 Vim2Audio::~Vim2Audio() {
29 }
30
Init(const pdev_protocol_t * pdev)31 zx_status_t Vim2Audio::Init(const pdev_protocol_t* pdev) {
32 zx_status_t res;
33
34 // Get a hold of our registers.
35 regs_ = Registers::Create(pdev, MMIO_AUD_OUT, &res);
36 if (res != ZX_OK) {
37 DISP_ERROR("Error mapping registers (mmio_id %u, res %d)\n", MMIO_AUD_OUT, res);
38 return res;
39 }
40 ZX_DEBUG_ASSERT(regs_ != nullptr);
41
42 // Place the various units into reset
43 //
44 // TODO(johngro): Add I2S to this list, right now we are only managing SPDIF
45 Vim2SpdifAudioStream::Disable(*regs_);
46
47 // Obtain our BTI from the platform manager
48 res = pdev_get_bti(pdev, 0, audio_bti_.reset_and_get_address());
49 if (res != ZX_OK) {
50 DISP_ERROR("Failed to get audio BTI handle! (res = %d)\n", res);
51 return res;
52 }
53
54 // Now that we have our BTI, and we have quiesced our hardware, we can
55 // release any quarantined VMOs which may be lingering from a previous
56 // crash. Note, it should be impossible for this to fail.
57 res = audio_bti_.release_quarantine();
58 ZX_DEBUG_ASSERT(res == ZX_OK);
59
60 // Allocate the buffer we will use for SPDIF
61 //
62 // TODO(johngro): How do we guarantee that this memory's phys location is
63 // below the 4GB mark?
64 zx::vmo spdif_rb_vmo;
65 res = zx_vmo_create_contiguous(audio_bti_.get(),
66 SPDIF_RB_SIZE,
67 0,
68 spdif_rb_vmo.reset_and_get_address());
69 if (res != ZX_OK) {
70 DISP_ERROR("Failed to allocate %zu byte ring buffer! (res = %d)\n", SPDIF_RB_SIZE, res);
71 return res;
72 }
73
74 spdif_rb_vmo_ = RefCountedVmo::Create(std::move(spdif_rb_vmo));
75 if (spdif_rb_vmo_ == nullptr) {
76 DISP_ERROR("Failed to allocate RefCountedVmo\n");
77 return ZX_ERR_NO_MEMORY;
78 }
79
80 return ZX_OK;
81 }
82
OnDisplayAdded(const vim2_display_t * display,uint64_t display_id)83 void Vim2Audio::OnDisplayAdded(const vim2_display_t* display, uint64_t display_id) {
84 if (spdif_stream_ != nullptr) {
85 ZX_DEBUG_ASSERT(spdif_stream_->display_id() != display_id);
86 return;
87 }
88
89 if (!display->p) {
90 zxlogf(WARN, "HDMI parameters are not set up. Cannot enable audio!\n");
91 return;
92 }
93
94 // Pin our VMO so that HW can access it.
95 fzl::PinnedVmo pinned_spdif_rb;
96 zx_status_t res;
97 res = pinned_spdif_rb.Pin(spdif_rb_vmo_->vmo(), audio_bti_, ZX_VM_PERM_READ);
98 if (res != ZX_OK) {
99 DISP_ERROR("Failed to pin %zu byte ring buffer! (res = %d)\n", SPDIF_RB_SIZE, res);
100 return;
101 }
102
103 // Sanity check the pinned VMO.
104 if (pinned_spdif_rb.region_count() != 1) {
105 DISP_ERROR("Audio ring buffer VMO is not contiguous! (regions = %u)\n",
106 pinned_spdif_rb.region_count());
107 return;
108 }
109
110 const auto& r = pinned_spdif_rb.region(0);
111 if ((r.phys_addr + r.size - 1) > std::numeric_limits<uint32_t>::max()) {
112 DISP_ERROR("Audio ring buffer VMO is not below 4GB! [0x%zx, 0x%zx]\n",
113 r.phys_addr,
114 r.phys_addr + r.size);
115 return;
116 }
117
118 spdif_stream_ = SimpleAudioStream::Create<Vim2SpdifAudioStream>(display,
119 regs_,
120 spdif_rb_vmo_,
121 std::move(pinned_spdif_rb),
122 display_id);
123 }
124
OnDisplayRemoved(uint64_t display_id)125 void Vim2Audio::OnDisplayRemoved(uint64_t display_id) {
126 if (spdif_stream_ && (spdif_stream_->display_id() == display_id)) {
127 spdif_stream_->Shutdown();
128 spdif_stream_ = nullptr;
129 }
130 }
131
132 } // namespace vim2
133 } // namespace audio
134
135 extern "C" {
136
vim2_audio_create(const pdev_protocol_t * pdev,vim2_audio_t ** out_audio)137 zx_status_t vim2_audio_create(const pdev_protocol_t* pdev,
138 vim2_audio_t **out_audio) {
139 ZX_DEBUG_ASSERT(pdev != nullptr);
140 ZX_DEBUG_ASSERT(out_audio != nullptr);
141 *out_audio = nullptr;
142
143 if (*out_audio != nullptr) {
144 return ZX_ERR_BAD_STATE;
145 }
146
147 fbl::AllocChecker ac;
148 auto audio = fbl::make_unique_checked<audio::vim2::Vim2Audio>(&ac);
149 if (!ac.check()) {
150 return ZX_ERR_NO_MEMORY;
151 }
152
153 zx_status_t res = audio->Init(pdev);
154 if (res != ZX_OK) {
155 return res;
156 }
157
158 *out_audio = reinterpret_cast<vim2_audio_t*>(audio.release());
159 return ZX_OK;
160 }
161
vim2_audio_shutdown(vim2_audio_t ** inout_audio)162 void vim2_audio_shutdown(vim2_audio_t** inout_audio) {
163 ZX_DEBUG_ASSERT(inout_audio);
164 delete reinterpret_cast<audio::vim2::Vim2Audio*>(*inout_audio);
165 *inout_audio = nullptr;
166 }
167
vim2_audio_on_display_added(const vim2_display_t * display,uint64_t display_id)168 void vim2_audio_on_display_added(const vim2_display_t* display, uint64_t display_id) {
169 if (!display->audio) {
170 zxlogf(WARN, "Failed to add audio stream; missing Vim2Audio instance!\n");
171 return;
172 }
173
174 auto cpp_audio = reinterpret_cast<audio::vim2::Vim2Audio*>(display->audio);
175 cpp_audio->OnDisplayAdded(display, display_id);
176 }
177
vim2_audio_on_display_removed(const vim2_display_t * display,uint64_t display_id)178 void vim2_audio_on_display_removed(const vim2_display_t* display, uint64_t display_id) {
179 if (!display->audio) {
180 zxlogf(WARN, "Failed to add audio stream; missing Vim2Audio instance!\n");
181 return;
182 }
183
184 auto cpp_audio = reinterpret_cast<audio::vim2::Vim2Audio*>(display->audio);
185 cpp_audio->OnDisplayRemoved(display_id);
186 }
187
188 } // extern "C"
189