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 <audio-proto-utils/format-utils.h> 6#include <ddk/debug.h> 7#include <ddk/device.h> 8#include <fbl/algorithm.h> 9#include <fbl/limits.h> 10#include <zircon/device/audio.h> 11#include <lib/zx/vmar.h> 12 13#include "a113-pdm.h" 14#include "dispatcher-pool/dispatcher-thread-pool.h" 15#include "gauss-pdm-input-stream.h" 16 17namespace audio { 18namespace gauss { 19 20GaussPdmInputStream::~GaussPdmInputStream() {} 21 22// static 23zx_status_t GaussPdmInputStream::Create(zx_device_t* parent) { 24 zxlogf(DEBUG1, "%s\n", __func__); 25 26 auto domain = dispatcher::ExecutionDomain::Create(); 27 if (domain == nullptr) { 28 return ZX_ERR_NO_MEMORY; 29 } 30 31 auto stream = 32 fbl::AdoptRef(new GaussPdmInputStream(parent, fbl::move(domain))); 33 34 zx_status_t res = stream->Bind("pdm-audio-driver", parent); 35 if (res == ZX_OK) { 36 // If bind/setup has succeeded, then the devmgr now controls our 37 // lifecycle and will release us when finished with us. Let go of our 38 // local reference. 39 // 40 __UNUSED auto dummy = stream.leak_ref(); 41 } 42 43 return ZX_OK; 44} 45 46zx_status_t GaussPdmInputStream::Bind(const char* devname, 47 zx_device_t* parent) { 48 ZX_DEBUG_ASSERT(!supported_formats_.size()); 49 a113_audio_device_init(&audio_device_, parent); 50 51 audio_stream_format_range_t range; 52 range.min_channels = 8; 53 range.max_channels = 8; 54 range.min_frames_per_second = 48000; 55 range.max_frames_per_second = 48000; 56 range.flags = ASF_RANGE_FLAG_FPS_48000_FAMILY; 57 range.sample_formats = AUDIO_SAMPLE_FORMAT_16BIT; 58 supported_formats_.push_back(range); 59 60 a113_pdm_arb_config(&audio_device_); 61 62 // Register interrupt and start irq handling thread. 63 zx_status_t status = pdev_map_interrupt( 64 &audio_device_.pdev, 0 /* PDM IRQ */, &audio_device_.pdm_irq); 65 66 if (status != ZX_OK) { 67 zxlogf(ERROR, "Could not map interrupt.\n"); 68 goto finished; 69 } 70 71 status = pdev_get_bti(&audio_device_.pdev, 0, &audio_device_.bti); 72 if (status != ZX_OK) { 73 zxlogf(ERROR, "Could not get bti.\n"); 74 goto finished; 75 } 76 77 status = thrd_create_with_name( 78 &irqthrd_, 79 [](void* thiz) -> int { 80 return reinterpret_cast<GaussPdmInputStream*>(thiz)->IrqThread(); 81 }, 82 this, "pdm_irq_thread"); 83 84 if (status != ZX_OK) { 85 zxlogf(ERROR, "Could not start irq thread.\n"); 86 } 87 88finished: 89 if (status != ZX_OK) { 90 zx_handle_close(audio_device_.pdm_irq); 91 zx_handle_close(audio_device_.bti); 92 return status; 93 } 94 95 return GaussPdmInputStreamBase::DdkAdd(devname); 96} 97 98void GaussPdmInputStream::DdkUnbind() { 99 zxlogf(DEBUG1, "%s\n", __func__); 100 // Close all of our client event sources if we have not already. 101 default_domain_->Deactivate(); 102 103 // Unpublish our device node. 104 DdkRemove(); 105} 106 107void GaussPdmInputStream::DdkRelease() { 108 zxlogf(DEBUG1, "%s\n", __func__); 109 110 // Shutdown irq thread. 111 zx_interrupt_destroy(audio_device_.pdm_irq); 112 thrd_join(irqthrd_, nullptr); 113 114 zx_handle_close(audio_device_.pdm_irq); 115 zx_handle_close(audio_device_.bti); 116 117 // Reclaim our reference from the driver framework and let it go out of 118 // scope. If this is our last reference (it should be), we will destruct 119 // immediately afterwards. 120 auto thiz = fbl::internal::MakeRefPtrNoAdopt(this); 121} 122 123zx_status_t GaussPdmInputStream::DdkIoctl(uint32_t op, const void* in_buf, 124 size_t in_len, void* out_buf, 125 size_t out_len, size_t* out_actual) { 126 zxlogf(DEBUG1, "%s\n", __func__); 127 // The only IOCTL we support is get channel. 128 if (op != AUDIO_IOCTL_GET_CHANNEL) { 129 return ZX_ERR_NOT_SUPPORTED; 130 } 131 132 if ((out_buf == nullptr) || (out_actual == nullptr) || 133 (out_len != sizeof(zx_handle_t))) { 134 return ZX_ERR_INVALID_ARGS; 135 } 136 137 fbl::AutoLock lock(&lock_); 138 139 // Attempt to allocate a new driver channel and bind it to us. If we don't 140 // already have an stream_channel_, flag this channel is the privileged 141 // connection (The connection which is allowed to do things like change 142 // formats). 143 bool privileged = (stream_channel_ == nullptr); 144 auto channel = dispatcher::Channel::Create(); 145 if (channel == nullptr) 146 return ZX_ERR_NO_MEMORY; 147 148 dispatcher::Channel::ProcessHandler phandler([ 149 stream = fbl::WrapRefPtr(this), privileged 150 ](dispatcher::Channel * channel)->zx_status_t { 151 OBTAIN_EXECUTION_DOMAIN_TOKEN(t, stream->default_domain_); 152 return stream->ProcessStreamChannel(channel, privileged); 153 }); 154 155 dispatcher::Channel::ChannelClosedHandler chandler; 156 if (privileged) { 157 chandler = dispatcher::Channel::ChannelClosedHandler( 158 [stream = fbl::WrapRefPtr(this)](const dispatcher::Channel* channel) 159 ->void { 160 OBTAIN_EXECUTION_DOMAIN_TOKEN(t, stream->default_domain_); 161 stream->DeactivateStreamChannel(channel); 162 }); 163 } 164 165 zx::channel client_endpoint; 166 zx_status_t res = 167 channel->Activate(&client_endpoint, default_domain_, 168 fbl::move(phandler), fbl::move(chandler)); 169 if (res == ZX_OK) { 170 if (privileged) { 171 ZX_DEBUG_ASSERT(stream_channel_ == nullptr); 172 stream_channel_ = fbl::move(channel); 173 } 174 175 *(reinterpret_cast<zx_handle_t*>(out_buf)) = client_endpoint.release(); 176 *out_actual = sizeof(zx_handle_t); 177 } 178 179 return res; 180} 181 182#define HREQ(_cmd, _payload, _handler, _allow_noack, ...) \ 183 case _cmd: \ 184 if (req_size != sizeof(req._payload)) { \ 185 zxlogf(ERROR, "Bad " #_cmd " response length (%u != %zu)\n", \ 186 req_size, sizeof(req._payload)); \ 187 return ZX_ERR_INVALID_ARGS; \ 188 } \ 189 if (!_allow_noack && (req.hdr.cmd & AUDIO_FLAG_NO_ACK)) { \ 190 zxlogf(ERROR, "NO_ACK flag not allowed for " #_cmd "\n"); \ 191 return ZX_ERR_INVALID_ARGS; \ 192 } \ 193 return _handler(channel, req._payload, ##__VA_ARGS__); 194zx_status_t 195GaussPdmInputStream::ProcessStreamChannel(dispatcher::Channel* channel, 196 bool privileged) { 197 ZX_DEBUG_ASSERT(channel != nullptr); 198 199 union { 200 audio_proto::CmdHdr hdr; 201 audio_proto::StreamGetFmtsReq get_formats; 202 audio_proto::StreamSetFmtReq set_format; 203 audio_proto::GetGainReq get_gain; 204 audio_proto::SetGainReq set_gain; 205 audio_proto::PlugDetectReq plug_detect; 206 audio_proto::GetUniqueIdReq get_unique_id; 207 audio_proto::GetStringReq get_string; 208 } req; 209 210 static_assert( 211 sizeof(req) <= 256, 212 "Request buffer is getting to be too large to hold on the stack!"); 213 214 uint32_t req_size; 215 zx_status_t res = channel->Read(&req, sizeof(req), &req_size); 216 if (res != ZX_OK) 217 return res; 218 219 if ((req_size < sizeof(req.hdr) || 220 (req.hdr.transaction_id == AUDIO_INVALID_TRANSACTION_ID))) 221 return ZX_ERR_INVALID_ARGS; 222 223 // Strip the NO_ACK flag from the request before selecting the dispatch 224 // target. 225 auto cmd = static_cast<audio_proto::Cmd>(req.hdr.cmd & ~AUDIO_FLAG_NO_ACK); 226 switch (cmd) { 227 HREQ(AUDIO_STREAM_CMD_GET_FORMATS, get_formats, OnGetStreamFormats, 228 false); 229 HREQ(AUDIO_STREAM_CMD_SET_FORMAT, set_format, OnSetStreamFormat, false, 230 privileged); 231 HREQ(AUDIO_STREAM_CMD_GET_GAIN, get_gain, OnGetGain, false); 232 HREQ(AUDIO_STREAM_CMD_SET_GAIN, set_gain, OnSetGain, true); 233 HREQ(AUDIO_STREAM_CMD_PLUG_DETECT, plug_detect, OnPlugDetect, true); 234 HREQ(AUDIO_STREAM_CMD_GET_UNIQUE_ID, get_unique_id, OnGetUniqueId, false); 235 HREQ(AUDIO_STREAM_CMD_GET_STRING, get_string, OnGetString, false); 236 default: 237 zxlogf(ERROR, "Unrecognized stream command 0x%04x\n", req.hdr.cmd); 238 return ZX_ERR_NOT_SUPPORTED; 239 } 240} 241 242zx_status_t 243GaussPdmInputStream::ProcessRingBufferChannel(dispatcher::Channel* channel) { 244 zxlogf(DEBUG1, "%s\n", __func__); 245 ZX_DEBUG_ASSERT(channel != nullptr); 246 247 fbl::AutoLock lock(&lock_); 248 249 union { 250 audio_proto::CmdHdr hdr; 251 audio_proto::RingBufGetFifoDepthReq get_fifo_depth; 252 audio_proto::RingBufGetBufferReq get_buffer; 253 audio_proto::RingBufStartReq rb_start; 254 audio_proto::RingBufStopReq rb_stop; 255 } req; 256 257 static_assert( 258 sizeof(req) <= 256, 259 "Request buffer is getting to be too large to hold on the stack!"); 260 261 uint32_t req_size; 262 zx_status_t res = channel->Read(&req, sizeof(req), &req_size); 263 if (res != ZX_OK) 264 return res; 265 266 if ((req_size < sizeof(req.hdr) || 267 (req.hdr.transaction_id == AUDIO_INVALID_TRANSACTION_ID))) 268 return ZX_ERR_INVALID_ARGS; 269 270 // Strip the NO_ACK flag from the request before selecting the dispatch 271 // target. 272 auto cmd = static_cast<audio_proto::Cmd>(req.hdr.cmd & ~AUDIO_FLAG_NO_ACK); 273 switch (cmd) { 274 HREQ(AUDIO_RB_CMD_GET_FIFO_DEPTH, get_fifo_depth, OnGetFifoDepth, 275 false); 276 HREQ(AUDIO_RB_CMD_GET_BUFFER, get_buffer, OnGetBuffer, false); 277 HREQ(AUDIO_RB_CMD_START, rb_start, OnStart, false); 278 HREQ(AUDIO_RB_CMD_STOP, rb_stop, OnStop, false); 279 default: 280 zxlogf(ERROR, "Unrecognized ring buffer command 0x%04x\n", req.hdr.cmd); 281 return ZX_ERR_NOT_SUPPORTED; 282 } 283 284 return ZX_ERR_NOT_SUPPORTED; 285} 286#undef HREQ 287 288zx_status_t GaussPdmInputStream::OnGetStreamFormats( 289 dispatcher::Channel* channel, const audio_proto::StreamGetFmtsReq& req) { 290 zxlogf(DEBUG1, "%s\n", __func__); 291 ZX_DEBUG_ASSERT(channel != nullptr); 292 uint16_t formats_sent = 0; 293 audio_proto::StreamGetFmtsResp resp = { }; 294 295 if (supported_formats_.size() > fbl::numeric_limits<uint16_t>::max()) { 296 zxlogf(ERROR, "Too many formats (%zu) to send during " 297 "AUDIO_STREAM_CMD_GET_FORMATS request!\n", 298 supported_formats_.size()); 299 return ZX_ERR_INTERNAL; 300 } 301 302 resp.hdr = req.hdr; 303 resp.format_range_count = static_cast<uint16_t>(supported_formats_.size()); 304 305 do { 306 uint16_t todo, payload_sz; 307 zx_status_t res; 308 309 todo = fbl::min<uint16_t>( 310 static_cast<uint16_t>(supported_formats_.size() - formats_sent), 311 AUDIO_STREAM_CMD_GET_FORMATS_MAX_RANGES_PER_RESPONSE); 312 payload_sz = 313 static_cast<uint16_t>(sizeof(resp.format_ranges[0]) * todo); 314 315 resp.first_format_range_ndx = formats_sent; 316 ::memcpy(resp.format_ranges, supported_formats_.get() + formats_sent, 317 payload_sz); 318 319 res = channel->Write(&resp, sizeof(resp)); 320 if (res != ZX_OK) { 321 zxlogf(ERROR, 322 "Failed to send get stream formats response (res %d)\n", 323 res); 324 return res; 325 } 326 327 formats_sent = (uint16_t)(formats_sent + todo); 328 } while (formats_sent < supported_formats_.size()); 329 330 return ZX_OK; 331} 332 333zx_status_t 334GaussPdmInputStream::OnSetStreamFormat(dispatcher::Channel* channel, 335 const audio_proto::StreamSetFmtReq& req, 336 bool privileged) { 337 zxlogf(DEBUG1, "%s\n", __func__); 338 ZX_DEBUG_ASSERT(channel != nullptr); 339 340 zx::channel client_rb_channel; 341 audio_proto::StreamSetFmtResp resp = { }; 342 343 resp.hdr = req.hdr; 344 345 // Only the privileged stream channel is allowed to change the format. 346 if (!privileged) { 347 ZX_DEBUG_ASSERT(channel == stream_channel_.get()); 348 resp.result = ZX_ERR_ACCESS_DENIED; 349 goto finished; 350 } 351 352 // TODO(almasrymina): for now, we only support this one frame rate. 353 if (req.frames_per_second != frame_rate_) { 354 resp.result = ZX_ERR_INVALID_ARGS; 355 goto finished; 356 } 357 358 // Determine the frame size. 359 frame_size_ = 360 audio::utils::ComputeFrameSize(req.channels, req.sample_format); 361 if (!frame_size_) { 362 zxlogf(ERROR, "Failed to compute frame size (ch %hu fmt 0x%08x)\n", 363 req.channels, req.sample_format); 364 resp.result = ZX_ERR_INTERNAL; 365 goto finished; 366 } 367 368 // Looks like we are going ahead with this format change. Tear down any 369 // exiting ring buffer interface before proceeding. 370 { 371 fbl::AutoLock lock(&lock_); 372 if (rb_channel_ != nullptr) { 373 rb_channel_->Deactivate(); 374 rb_channel_.reset(); 375 } 376 377 // Create a new ring buffer channel which can be used to move bulk data 378 // and bind it to us. 379 rb_channel_ = dispatcher::Channel::Create(); 380 if (rb_channel_ == nullptr) { 381 resp.result = ZX_ERR_NO_MEMORY; 382 } else { 383 dispatcher::Channel::ProcessHandler 384 phandler([stream = fbl::WrapRefPtr(this)](dispatcher::Channel * 385 channel) 386 ->zx_status_t { 387 OBTAIN_EXECUTION_DOMAIN_TOKEN( 388 t, stream->default_domain_); 389 return stream->ProcessRingBufferChannel( 390 channel); 391 }); 392 393 dispatcher::Channel::ChannelClosedHandler 394 chandler([stream = fbl::WrapRefPtr(this)]( 395 const dispatcher::Channel* channel) 396 ->void { 397 OBTAIN_EXECUTION_DOMAIN_TOKEN( 398 t, stream->default_domain_); 399 stream->DeactivateRingBufferChannel(channel); 400 }); 401 402 resp.result = 403 rb_channel_->Activate(&client_rb_channel, default_domain_, 404 fbl::move(phandler), fbl::move(chandler)); 405 if (resp.result != ZX_OK) { 406 rb_channel_.reset(); 407 } 408 } 409 } 410 411 a113_audio_register_toddr(&audio_device_); 412 413finished: 414 if (resp.result == ZX_OK) { 415 // TODO(johngro): Report the actual external delay. 416 resp.external_delay_nsec = 0; 417 } else { 418 fbl::AutoLock lock(&lock_); 419 420 if (rb_channel_) { 421 rb_channel_->Deactivate(); 422 rb_channel_.reset(); 423 } 424 } 425 return channel->Write(&resp, sizeof(resp), fbl::move(client_rb_channel)); 426} 427 428int GaussPdmInputStream::IrqThread() { 429 zxlogf(DEBUG1, "Starting irq thread.\n"); 430 431 zx_status_t status; 432 433 uint32_t last_notification_offset = 0; 434 435 for (;;) { 436 status = zx_interrupt_wait(audio_device_.pdm_irq, nullptr); 437 if (status != ZX_OK) { 438 zxlogf(DEBUG1, "audio_pdm_input: interrupt error: %d.\n", status); 439 break; 440 } 441 442 a113_toddr_clear_interrupt(&audio_device_, 0x4); 443 444 uint32_t offset = 445 a113_toddr_get_position(&audio_device_) - 446 a113_ee_audio_read(&audio_device_, EE_AUDIO_TODDR_B_START_ADDR); 447 448 vmo_helper_.printoffsetinvmo(offset); 449 450 audio_proto::RingBufPositionNotify resp; 451 resp.ring_buffer_pos = offset; 452 resp.hdr.cmd = AUDIO_RB_POSITION_NOTIFY; 453 resp.hdr.transaction_id = AUDIO_INVALID_TRANSACTION_ID; 454 455 size_t data_available = 456 offset >= last_notification_offset 457 ? offset - last_notification_offset 458 : offset + ring_buffer_size_.load() - last_notification_offset; 459 460 if (notifications_per_ring_.load() && 461 data_available >= 462 ring_buffer_size_.load() / notifications_per_ring_.load()) { 463 fbl::AutoLock lock(&lock_); 464 if (!rb_channel_) { 465 zxlogf(DEBUG1, "No rb_channel. Ignoring spurious interrupt.\n"); 466 continue; 467 } 468 rb_channel_->Write(&resp, sizeof(resp)); 469 } 470 } 471 472 zxlogf(DEBUG1, "Leaving irq thread.\n"); 473 474 return ZX_OK; 475} 476 477zx_status_t GaussPdmInputStream::OnGetGain(dispatcher::Channel* channel, 478 const audio_proto::GetGainReq& req) { 479 zxlogf(DEBUG1, "%s\n", __func__); 480 481 ZX_DEBUG_ASSERT(channel != nullptr); 482 audio_proto::GetGainResp resp = { }; 483 484 resp.hdr = req.hdr; 485 resp.cur_mute = false; 486 resp.cur_gain = 0.0; 487 resp.can_mute = false; 488 resp.min_gain = 0.0; 489 resp.max_gain = 0.0; 490 resp.gain_step = 0.0; 491 492 return channel->Write(&resp, sizeof(resp)); 493} 494 495zx_status_t GaussPdmInputStream::OnSetGain(dispatcher::Channel* channel, 496 const audio_proto::SetGainReq& req) { 497 ZX_DEBUG_ASSERT(channel != nullptr); 498 if (req.hdr.cmd & AUDIO_FLAG_NO_ACK) 499 return ZX_OK; 500 501 audio_proto::SetGainResp resp = { }; 502 resp.hdr = req.hdr; 503 504 // We don't support setting gain for now. 505 resp.result = ZX_ERR_INVALID_ARGS; 506 507 return channel->Write(&resp, sizeof(resp)); 508} 509 510zx_status_t 511GaussPdmInputStream::OnPlugDetect(dispatcher::Channel* channel, 512 const audio_proto::PlugDetectReq& req) { 513 zxlogf(DEBUG1, "%s\n", __func__); 514 515 if (req.hdr.cmd & AUDIO_FLAG_NO_ACK) 516 return ZX_OK; 517 518 audio_proto::PlugDetectResp resp = { }; 519 resp.hdr = req.hdr; 520 resp.flags = static_cast<audio_pd_notify_flags_t>(AUDIO_PDNF_HARDWIRED | 521 AUDIO_PDNF_PLUGGED); 522 return channel->Write(&resp, sizeof(resp)); 523} 524 525zx_status_t GaussPdmInputStream::OnGetUniqueId(dispatcher::Channel* channel, 526 const audio_proto::GetUniqueIdReq& req) { 527 audio_proto::GetUniqueIdResp resp; 528 529 static const audio_stream_unique_id_t mic_id = AUDIO_STREAM_UNIQUE_ID_BUILTIN_MICROPHONE; 530 resp.hdr = req.hdr; 531 resp.unique_id = mic_id; 532 533 return channel->Write(&resp, sizeof(resp)); 534} 535 536zx_status_t GaussPdmInputStream::OnGetString(dispatcher::Channel* channel, 537 const audio_proto::GetStringReq& req) { 538 audio_proto::GetStringResp resp; 539 540 resp.hdr = req.hdr; 541 resp.id = req.id; 542 543 const char* str; 544 switch (req.id) { 545 case AUDIO_STREAM_STR_ID_MANUFACTURER: str = "Gauss"; break; 546 case AUDIO_STREAM_STR_ID_PRODUCT: str = "Builtin Microphone"; break; 547 default: str = nullptr; break; 548 } 549 550 if (str == nullptr) { 551 resp.result = ZX_ERR_NOT_FOUND; 552 resp.strlen = 0; 553 } else { 554 int res = snprintf(reinterpret_cast<char*>(resp.str), sizeof(resp.str), "%s", str); 555 ZX_DEBUG_ASSERT(res >= 0); 556 resp.result = ZX_OK; 557 resp.strlen = fbl::min<uint32_t>(res, sizeof(resp.str) - 1); 558 } 559 560 return channel->Write(&resp, sizeof(resp)); 561} 562 563zx_status_t GaussPdmInputStream::OnGetFifoDepth( 564 dispatcher::Channel* channel, 565 const audio_proto::RingBufGetFifoDepthReq& req) { 566 zxlogf(DEBUG1, "%s\n", __func__); 567 568 audio_proto::RingBufGetFifoDepthResp resp = { }; 569 570 resp.hdr = req.hdr; 571 resp.result = ZX_OK; 572 resp.fifo_depth = static_cast<uint32_t>(fifo_depth_); 573 574 return channel->Write(&resp, sizeof(resp)); 575} 576 577zx_status_t 578GaussPdmInputStream::OnGetBuffer(dispatcher::Channel* channel, 579 const audio_proto::RingBufGetBufferReq& req) { 580 zxlogf(DEBUG1, "%s\n", __func__); 581 582 audio_proto::RingBufGetBufferResp resp = { }; 583 zx::vmo client_rb_handle; 584 uint32_t client_rights; 585 586 resp.hdr = req.hdr; 587 resp.result = ZX_ERR_INTERNAL; 588 589 vmo_helper_.DestroyVmo(); 590 591 uint32_t notifications_per_ring = 592 req.notifications_per_ring ? req.notifications_per_ring : 1; 593 594 uint32_t requested_period_size = 595 req.min_ring_buffer_frames * frame_size_ / notifications_per_ring; 596 597 uint32_t period_size = fbl::round_up(requested_period_size, 598 static_cast<uint32_t>(fifo_depth_)); 599 600 ring_buffer_size_.store(fbl::round_up(period_size * notifications_per_ring, 601 static_cast<uint32_t>(PAGE_SIZE))); 602 603 // TODO(johngro) : Come back here and fix this. Right now, we know that our 604 // frame size is always going to be 16 bytes (8 channels, 2 bytes per 605 // channel), and that our ring buffer size is always going to be a multiple 606 // of pages (4k, hence divisible by 16), so this should always be the case. 607 // 608 // Moving forward, if we ever want to support other frame sizes (in 609 // particular, things which may not be a power of two), it would be good to 610 // make this code more generic. We have a few requirements to obey, 611 // however. Not only must the ring buffer size be a multiple of frame size, 612 // it must also be a multiple of 8; hence a multiple of LCM(frame_size, 8). 613 // It would be really handy to have a fbl:: version of fbl::gcd and fbl::lcm 614 // to handle these calulations. Perhaps, by the time that I come back and 615 // address this, we will. 616 if (ring_buffer_size_.load() % frame_size_) { 617 zxlogf(ERROR, "Frame size (%u) does not divide ring buffer size (%zu)\n", 618 frame_size_, ring_buffer_size_.load()); 619 goto finished; 620 } 621 622 notifications_per_ring_.store(req.notifications_per_ring); 623 624 zxlogf(DEBUG1, "ring_buffer_size=%lu\n", ring_buffer_size_.load()); 625 zxlogf(DEBUG1, "req.notifications_per_ring=%u\n", 626 req.notifications_per_ring); 627 628 // Create the ring buffer vmo we will use to share memory with the client. 629 resp.result = vmo_helper_.AllocateVmo(audio_device_.bti, 630 ring_buffer_size_.load()); 631 if (resp.result != ZX_OK) { 632 zxlogf(ERROR, "Failed to create ring buffer (size %lu)\n", 633 ring_buffer_size_.load()); 634 goto finished; 635 } 636 637 zx_paddr_t start_address; 638 zx_paddr_t end_address; 639 640 resp.result = vmo_helper_.GetVmoRange(&start_address); 641 if (resp.result != ZX_OK) { 642 zxlogf(ERROR, "Failed to get range.\n"); 643 goto finished; 644 } 645 646 // -8 because the addresses are indexed 0 -> size-8. The TODDR processes 647 // data in chunks of 8 bytes. 648 end_address = start_address + ring_buffer_size_.load() - 8; 649 650 a113_toddr_set_buf(&audio_device_, (uint32_t)start_address, 651 (uint32_t)end_address); 652 a113_toddr_set_intrpt(&audio_device_, 653 static_cast<uint32_t>(period_size / 8)); 654 655 // TODO(almasrymina): TODDR and pdm configuration is hardcoded for now, 656 // since we only support the one format. Need to revisit this when we 657 // support more. 658 a113_toddr_select_src(&audio_device_, PDMIN); 659 a113_toddr_set_format(&audio_device_, RJ_16BITS, 31, 16); 660 661 a113_toddr_set_fifos(&audio_device_, 0x40); 662 a113_pdm_ctrl(&audio_device_, 16); 663 a113_pdm_filter_ctrl(&audio_device_); 664 665 // Create the client's handle to the ring buffer vmo and set it back to 666 // them. 667 client_rights = ZX_RIGHT_TRANSFER | ZX_RIGHT_MAP | ZX_RIGHT_READ; 668 669 resp.result = vmo_helper_.Duplicate(client_rights, &client_rb_handle); 670 if (resp.result != ZX_OK) { 671 zxlogf(ERROR, "Failed to duplicate ring buffer handle (res %d)\n", 672 resp.result); 673 goto finished; 674 } 675 676 ZX_DEBUG_ASSERT((ring_buffer_size_.load() / frame_size_) <= 677 fbl::numeric_limits<decltype(resp.num_ring_buffer_frames)>::max()); 678 resp.num_ring_buffer_frames = 679 static_cast<decltype(resp.num_ring_buffer_frames)>(ring_buffer_size_.load() / frame_size_); 680 681finished: 682 zx_status_t res; 683 if (resp.result == ZX_OK) { 684 ZX_DEBUG_ASSERT(client_rb_handle.is_valid()); 685 res = channel->Write(&resp, sizeof(resp), fbl::move(client_rb_handle)); 686 } else { 687 res = channel->Write(&resp, sizeof(resp)); 688 } 689 690 return res; 691} 692 693zx_status_t 694GaussPdmInputStream::OnStart(dispatcher::Channel* channel, 695 const audio_proto::RingBufStartReq& req) { 696 zxlogf(DEBUG1, "%s\n", __func__); 697 698 audio_proto::RingBufStartResp resp = { }; 699 resp.hdr = req.hdr; 700 701 a113_pdm_fifo_reset(&audio_device_); 702 a113_toddr_enable(&audio_device_, 1); 703 a113_pdm_enable(&audio_device_, 1); 704 resp.start_time = zx_clock_get_monotonic(); 705 706 resp.result = ZX_OK; 707 return channel->Write(&resp, sizeof(resp)); 708} 709 710zx_status_t 711GaussPdmInputStream::OnStop(dispatcher::Channel* channel, 712 const audio_proto::RingBufStopReq& req) { 713 zxlogf(DEBUG1, "%s\n", __func__); 714 715 audio_proto::RingBufStopResp resp = { }; 716 717 a113_toddr_enable(&audio_device_, 0); 718 a113_pdm_enable(&audio_device_, 0); 719 720 resp.hdr = req.hdr; 721 722 return channel->Write(&resp, sizeof(resp)); 723} 724 725void GaussPdmInputStream::DeactivateStreamChannel( 726 const dispatcher::Channel* channel) { 727 zxlogf(DEBUG1, "%s\n", __func__); 728 729 fbl::AutoLock lock(&lock_); 730 731 ZX_DEBUG_ASSERT(stream_channel_.get() == channel); 732 ZX_DEBUG_ASSERT(rb_channel_.get() != channel); 733 734 stream_channel_.reset(); 735} 736 737void GaussPdmInputStream::DeactivateRingBufferChannel( 738 const dispatcher::Channel* channel) { 739 zxlogf(DEBUG1, "%s\n", __func__); 740 741 fbl::AutoLock lock(&lock_); 742 743 ZX_DEBUG_ASSERT(stream_channel_.get() != channel); 744 ZX_DEBUG_ASSERT(rb_channel_.get() == channel); 745 746 a113_audio_unregister_toddr(&audio_device_); 747 748 rb_channel_->Deactivate(); 749 rb_channel_.reset(); 750} 751 752} // namespace gauss 753} // namespace audio 754 755extern "C" zx_status_t gauss_pdm_input_bind(void* ctx, zx_device_t* device, 756 void** cookie) { 757 zxlogf(DEBUG1, "gauss_pdm_input_bind\n"); 758 audio::gauss::GaussPdmInputStream::Create(device); 759 return ZX_OK; 760} 761 762extern "C" void gauss_pdm_input_release(void*) { 763 zxlogf(DEBUG1, "gauss_pdm_input_release\n"); 764 dispatcher::ThreadPool::ShutdownAll(); 765} 766