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 #include "audio-stream-in.h"
5
6 #include <ddk/binding.h>
7 #include <ddk/debug.h>
8 #include <ddk/driver.h>
9 #include <ddk/platform-defs.h>
10 #include <math.h>
11
12 #include <optional>
13 #include <utility>
14
15 namespace audio {
16 namespace sherlock {
17
18 // Expects 4 mics.
19 constexpr size_t kNumberOfChannels = 4;
20 // Calculate ring buffer size for 1 second of 16-bit, 48kHz.
21 constexpr size_t kRingBufferSize = fbl::round_up<size_t, size_t>(48000 * 2 * kNumberOfChannels,
22 ZX_PAGE_SIZE);
23
SherlockAudioStreamIn(zx_device_t * parent)24 SherlockAudioStreamIn::SherlockAudioStreamIn(zx_device_t* parent)
25 : SimpleAudioStream(parent, true /* is input */) {
26 }
27
Init()28 zx_status_t SherlockAudioStreamIn::Init() {
29 zx_status_t status;
30
31 status = InitPDev();
32 if (status != ZX_OK) {
33 return status;
34 }
35
36 status = AddFormats();
37 if (status != ZX_OK) {
38 return status;
39 }
40 // Set our gain capabilities.
41 cur_gain_state_.cur_gain = 0;
42 cur_gain_state_.cur_mute = false;
43 cur_gain_state_.cur_agc = false;
44 cur_gain_state_.min_gain = 0;
45 cur_gain_state_.max_gain = 0;
46 cur_gain_state_.gain_step = 0;
47 cur_gain_state_.can_mute = false;
48 cur_gain_state_.can_agc = false;
49
50 snprintf(device_name_, sizeof(device_name_), "sherlock-audio-in");
51 snprintf(mfr_name_, sizeof(mfr_name_), "unknown");
52 snprintf(prod_name_, sizeof(prod_name_), "sherlock");
53
54 unique_id_ = AUDIO_STREAM_UNIQUE_ID_BUILTIN_MICROPHONE;
55
56 return ZX_OK;
57 }
58
InitPDev()59 zx_status_t SherlockAudioStreamIn::InitPDev() {
60 pdev_protocol_t pdev;
61 zx_status_t status = device_get_protocol(parent(), ZX_PROTOCOL_PDEV, &pdev);
62 if (status) {
63 return status;
64 }
65 pdev_ = ddk::PDev(&pdev);
66
67 status = pdev_->GetBti(0, &bti_);
68 if (status != ZX_OK) {
69 zxlogf(ERROR, "%s could not obtain bti - %d\n", __func__, status);
70 return status;
71 }
72 std::optional<ddk::MmioBuffer> mmio0, mmio1;
73 status = pdev_->MapMmio(0, &mmio0);
74 if (status != ZX_OK) {
75 return status;
76 }
77 status = pdev_->MapMmio(1, &mmio1);
78 if (status != ZX_OK) {
79 return status;
80 }
81
82 pdm_ = AmlPdmDevice::Create(std::move(*mmio0),
83 std::move(*mmio1),
84 HIFI_PLL,
85 7, // clk_div for mclk = T931_HIFI_PLL_RATE/clk_div = 219.43 MHz.
86 499, // clk_div for pdm_dclk = T931_HIFI_PLL_RATE/clk_div = 3.07MHz.
87 TODDR_B);
88 if (pdm_ == nullptr) {
89 zxlogf(ERROR, "%s failed to create pdm device\n", __func__);
90 return ZX_ERR_NO_MEMORY;
91 }
92
93 InitBuffer(kRingBufferSize);
94
95 pdm_->SetBuffer(pinned_ring_buffer_.region(0).phys_addr,
96 pinned_ring_buffer_.region(0).size);
97
98 pdm_->ConfigPdmIn((1 << kNumberOfChannels) - 1); // First kNumberOfChannels channels.
99
100 pdm_->Sync();
101
102 return ZX_OK;
103 }
104
ChangeFormat(const audio_proto::StreamSetFmtReq & req)105 zx_status_t SherlockAudioStreamIn::ChangeFormat(const audio_proto::StreamSetFmtReq& req) {
106 fifo_depth_ = pdm_->fifo_depth();
107 external_delay_nsec_ = 0;
108
109 // At this time only one format is supported, and hardware is initialized
110 // during driver binding, so nothing to do at this time.
111 return ZX_OK;
112 }
113
GetBuffer(const audio_proto::RingBufGetBufferReq & req,uint32_t * out_num_rb_frames,zx::vmo * out_buffer)114 zx_status_t SherlockAudioStreamIn::GetBuffer(const audio_proto::RingBufGetBufferReq& req,
115 uint32_t* out_num_rb_frames,
116 zx::vmo* out_buffer) {
117 uint32_t rb_frames =
118 static_cast<uint32_t>(pinned_ring_buffer_.region(0).size) / frame_size_;
119
120 if (req.min_ring_buffer_frames > rb_frames) {
121 return ZX_ERR_OUT_OF_RANGE;
122 }
123 zx_status_t status;
124 constexpr uint32_t rights = ZX_RIGHT_READ | ZX_RIGHT_WRITE | ZX_RIGHT_MAP | ZX_RIGHT_TRANSFER;
125 status = ring_buffer_vmo_.duplicate(rights, out_buffer);
126 if (status != ZX_OK) {
127 return status;
128 }
129
130 *out_num_rb_frames = rb_frames;
131
132 pdm_->SetBuffer(pinned_ring_buffer_.region(0).phys_addr,
133 rb_frames * frame_size_);
134 return ZX_OK;
135 }
136
Start(uint64_t * out_start_time)137 zx_status_t SherlockAudioStreamIn::Start(uint64_t* out_start_time) {
138 *out_start_time = pdm_->Start();
139
140 uint32_t notifs = LoadNotificationsPerRing();
141 if (notifs) {
142 us_per_notification_ = static_cast<uint32_t>(
143 1000 * pinned_ring_buffer_.region(0).size / (frame_size_ * 48 * notifs));
144 notify_timer_->Arm(zx_deadline_after(ZX_USEC(us_per_notification_)));
145 } else {
146 us_per_notification_ = 0;
147 }
148 return ZX_OK;
149 }
150
151 // Timer handler for sending out position notifications.
ProcessRingNotification()152 zx_status_t SherlockAudioStreamIn::ProcessRingNotification() {
153 ZX_ASSERT(us_per_notification_ != 0);
154
155 notify_timer_->Arm(zx_deadline_after(ZX_USEC(us_per_notification_)));
156
157 audio_proto::RingBufPositionNotify resp = {};
158 resp.hdr.cmd = AUDIO_RB_POSITION_NOTIFY;
159
160 resp.ring_buffer_pos = pdm_->GetRingPosition();
161 return NotifyPosition(resp);
162 }
163
InitPost()164 zx_status_t SherlockAudioStreamIn::InitPost() {
165
166 notify_timer_ = dispatcher::Timer::Create();
167 if (notify_timer_ == nullptr) {
168 return ZX_ERR_NO_MEMORY;
169 }
170
171 dispatcher::Timer::ProcessHandler thandler(
172 [pdm = this](dispatcher::Timer * timer)->zx_status_t {
173 OBTAIN_EXECUTION_DOMAIN_TOKEN(t, pdm->domain_);
174 return pdm->ProcessRingNotification();
175 });
176
177 return notify_timer_->Activate(domain_, std::move(thandler));
178 }
179
Stop()180 zx_status_t SherlockAudioStreamIn::Stop() {
181 notify_timer_->Cancel();
182 us_per_notification_ = 0;
183 pdm_->Stop();
184 return ZX_OK;
185 }
186
AddFormats()187 zx_status_t SherlockAudioStreamIn::AddFormats() {
188 fbl::AllocChecker ac;
189 supported_formats_.reserve(1, &ac);
190 if (!ac.check()) {
191 zxlogf(ERROR, "Out of memory, can not create supported formats list\n");
192 return ZX_ERR_NO_MEMORY;
193 }
194
195 audio_stream_format_range_t range;
196 range.min_channels = kNumberOfChannels;
197 range.max_channels = kNumberOfChannels;
198 range.sample_formats = AUDIO_SAMPLE_FORMAT_16BIT;
199 range.min_frames_per_second = 48000;
200 range.max_frames_per_second = 48000;
201 range.flags = ASF_RANGE_FLAG_FPS_48000_FAMILY;
202
203 supported_formats_.push_back(range);
204
205 return ZX_OK;
206 }
207
InitBuffer(size_t size)208 zx_status_t SherlockAudioStreamIn::InitBuffer(size_t size) {
209 // TODO(ZX-3149): Per johngro's suggestion preallocate contiguous memory (say in
210 // platform bus) since we are likely to fail after running for a while and we need to
211 // init again (say the devhost is restarted).
212 zx_status_t status = zx_vmo_create_contiguous(bti_.get(), size, 0,
213 ring_buffer_vmo_.reset_and_get_address());
214 if (status != ZX_OK) {
215 zxlogf(ERROR, "%s failed to allocate ring buffer vmo - %d\n", __func__, status);
216 return status;
217 }
218
219 status = pinned_ring_buffer_.Pin(ring_buffer_vmo_, bti_, ZX_VM_PERM_READ | ZX_VM_PERM_WRITE);
220 if (status != ZX_OK) {
221 zxlogf(ERROR, "%s failed to pin ring buffer vmo - %d\n", __func__, status);
222 return status;
223 }
224 if (pinned_ring_buffer_.region_count() != 1) {
225 zxlogf(ERROR, "%s buffer is not contiguous", __func__);
226 return ZX_ERR_NO_MEMORY;
227 }
228
229 return ZX_OK;
230 }
231
232 } //namespace sherlock
233 } //namespace audio
234
pdm_audio_bind(void * ctx,zx_device_t * parent)235 extern "C" zx_status_t pdm_audio_bind(void* ctx, zx_device_t* parent) {
236
237 auto stream =
238 audio::SimpleAudioStream::Create<audio::sherlock::SherlockAudioStreamIn>(parent);
239 if (stream == nullptr) {
240 return ZX_ERR_NO_MEMORY;
241 }
242
243 return ZX_OK;
244 }
245