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