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 <fbl/vector.h> 6 7#include <intel-hda/utils/codec-caps.h> 8#include <intel-hda/utils/codec-commands.h> 9#include <intel-hda/utils/codec-state.h> 10#include <intel-hda/utils/utils.h> 11 12#include "debug-logging.h" 13#include "realtek-stream.h" 14 15DECLARE_STATIC_SLAB_ALLOCATOR_STORAGE(audio::intel_hda::codecs::RealtekStream::PCAT, 16); 16 17namespace audio { 18namespace intel_hda { 19namespace codecs { 20 21RealtekStream::RealtekStream(const StreamProperties& props) 22 : IntelHDAStreamBase(props.stream_id, props.is_input), 23 props_(props) { 24 SetPersistentUniqueId(props.uid); 25} 26 27zx_status_t RealtekStream::DisableConverterLocked(bool force_all) { 28 const Command DISABLE_CONVERTER_VERBS[] = { 29 { props_.conv_nid, SET_AMPLIFIER_GAIN_MUTE(true, 0, is_input(), !is_input()) }, 30 { props_.pc_nid, SET_AMPLIFIER_GAIN_MUTE(true, 0, is_input(), !is_input()) }, 31 { props_.conv_nid, SET_CONVERTER_STREAM_CHAN(IHDA_INVALID_STREAM_TAG, 0) }, 32 { props_.conv_nid, SET_POWER_STATE(HDA_PS_D3HOT) }, 33 { props_.pc_nid, SET_POWER_STATE(HDA_PS_D3HOT) }, 34 }; 35 36 return RunCmdListLocked(DISABLE_CONVERTER_VERBS, countof(DISABLE_CONVERTER_VERBS), force_all); 37} 38 39zx_status_t RealtekStream::UpdateConverterGainLocked(float target_gain) { 40 if (!conv_.has_amp) 41 return ZX_ERR_NOT_SUPPORTED; 42 43 if ((target_gain < conv_.min_gain) || (target_gain > conv_.max_gain)) 44 return ZX_ERR_INVALID_ARGS; 45 46 ZX_DEBUG_ASSERT(conv_.gain_step > 0); 47 48 float tmp = ((target_gain - conv_.min_gain) + (conv_.gain_step / 2)) / conv_.gain_step; 49 ZX_DEBUG_ASSERT(static_cast<uint32_t>(tmp) <= conv_.amp_caps.num_steps()); 50 51 cur_conv_gain_steps_ = ComputeGainSteps(conv_, target_gain); 52 return ZX_OK; 53} 54 55float RealtekStream::ComputeCurrentGainLocked() { 56 return conv_.has_amp 57 ? conv_.min_gain + (cur_conv_gain_steps_ * conv_.gain_step) 58 : 0.0f; 59} 60 61zx_status_t RealtekStream::SendGainUpdatesLocked() { 62 zx_status_t res; 63 64 if (conv_.has_amp) { 65 bool mute = conv_.amp_caps.can_mute() ? cur_mute_ : false; 66 res = RunCmdLocked({ props_.conv_nid, SET_AMPLIFIER_GAIN_MUTE(mute, 67 cur_conv_gain_steps_, 68 is_input(), !is_input()) }); 69 if (res != ZX_OK) 70 return res; 71 } 72 73 if (pc_.has_amp) { 74 bool mute = pc_.amp_caps.can_mute() ? cur_mute_ : false; 75 res = RunCmdLocked({ props_.pc_nid, SET_AMPLIFIER_GAIN_MUTE(mute, 76 cur_pc_gain_steps_, 77 is_input(), !is_input()) }); 78 if (res != ZX_OK) 79 return res; 80 } 81 82 return ZX_OK; 83} 84 85// TODO(johngro) : re: the plug_notify_targets_ list. In theory, we could put 86// this in a tree indexed by the channel's owner context, or by the pointer 87// itself. This would make add/remove operations simpler, and faster in the 88// case that we had lots of clients. In reality, however, we are likely to 89// limit the interface moving forward so that we have only one client at a time 90// (in which case this becomes much simpler). Moving forward, we need to come 91// back and either simplify or optimize (as the situation warrents) once we know 92// how we are proceeding. 93void RealtekStream::AddPDNotificationTgtLocked(dispatcher::Channel* channel) { 94 bool duplicate = false; 95 for (auto& tgt : plug_notify_targets_) { 96 duplicate = (tgt.channel.get() == channel); 97 if (duplicate) 98 break; 99 } 100 101 if (!duplicate) { 102 fbl::RefPtr<dispatcher::Channel> c(channel); 103 fbl::unique_ptr<NotifyTarget> tgt(new NotifyTarget(fbl::move(c))); 104 plug_notify_targets_.push_back(fbl::move(tgt)); 105 } 106} 107 108void RealtekStream::RemovePDNotificationTgtLocked(const dispatcher::Channel& channel) { 109 for (auto& tgt : plug_notify_targets_) { 110 if (tgt.channel.get() == &channel) { 111 plug_notify_targets_.erase(tgt); 112 break; 113 } 114 } 115} 116 117// static 118uint8_t RealtekStream::ComputeGainSteps(const CommonCaps& caps, float target_gain) { 119 if (!caps.has_amp || !caps.amp_caps.num_steps()) 120 return 0; 121 122 if (target_gain < caps.min_gain) 123 return 0; 124 125 if (target_gain > caps.max_gain) 126 return static_cast<uint8_t>(caps.amp_caps.num_steps() - 1); 127 128 ZX_DEBUG_ASSERT(caps.gain_step > 0); 129 float tmp = ((target_gain - caps.min_gain) + (caps.gain_step / 2)) / caps.gain_step; 130 ZX_DEBUG_ASSERT(static_cast<uint32_t>(tmp) < caps.amp_caps.num_steps()); 131 132 return static_cast<uint8_t>(tmp); 133} 134 135zx_status_t RealtekStream::RunCmdLocked(const Command& cmd) { 136 fbl::unique_ptr<PendingCommand> pending_cmd; 137 bool want_response = (cmd.thunk != nullptr); 138 139 if (want_response) { 140 pending_cmd = PendingCommandAllocator::New(cmd); 141 if (pending_cmd == nullptr) 142 return ZX_ERR_NO_MEMORY; 143 } 144 145 zx_status_t res = SendCodecCommandLocked(cmd.nid, 146 cmd.verb, 147 want_response ? Ack::YES : Ack::NO); 148 VERBOSE_LOG("SEND: nid %2hu verb 0x%05x%s\n", cmd.nid, cmd.verb.val, want_response ? "*" : ""); 149 150 if ((res == ZX_OK) && want_response) 151 pending_cmds_.push_back(fbl::move(pending_cmd)); 152 153 return res; 154} 155 156zx_status_t RealtekStream::RunCmdListLocked(const Command* list, size_t count, bool force_all) { 157 ZX_DEBUG_ASSERT(list); 158 159 zx_status_t total_res = ZX_OK; 160 for (size_t i = 0; i < count; ++i) { 161 zx_status_t res = RunCmdLocked(list[i]); 162 163 if (res != ZX_OK) { 164 if (!force_all) 165 return res; 166 167 if (total_res == ZX_OK) 168 total_res = res; 169 } 170 } 171 172 return total_res; 173} 174 175void RealtekStream::OnDeactivateLocked() { 176 plug_notify_targets_.clear(); 177 DisableConverterLocked(true); 178} 179 180void RealtekStream::OnChannelDeactivateLocked(const dispatcher::Channel& channel) { 181 RemovePDNotificationTgtLocked(channel); 182} 183 184zx_status_t RealtekStream::OnDMAAssignedLocked() { 185 return UpdateSetupProgressLocked(DMA_ASSIGNMENT_COMPLETE); 186} 187 188zx_status_t RealtekStream::OnSolicitedResponseLocked(const CodecResponse& resp) { 189 if (pending_cmds_.is_empty()) { 190 LOG("Received solicited response (0x%08x), but no commands are pending!\n", resp.data); 191 return ZX_ERR_BAD_STATE; 192 } 193 194 auto pending_cmd = pending_cmds_.pop_front(); 195 VERBOSE_LOG("RECV: nid %2hu verb 0x%05x --> 0x%08x\n", 196 pending_cmd->cmd().nid, 197 pending_cmd->cmd().verb.val, 198 resp.data); 199 return pending_cmd->Invoke(this, resp); 200} 201 202zx_status_t RealtekStream::OnUnsolicitedResponseLocked(const CodecResponse& resp) { 203 // TODO(johngro) : Which bit should we be using as the pin sense bit? The 204 // Intel HDA spec only specifies what digital display pins are required to 205 // use; generally speaking unsolicited response payloads are supposed to be 206 // vendor specific. 207 // 208 // The only Realtek datasheets I have seen do not define which bit they will 209 // use. Experimentally, it seems like Realtek codecs use bit 3 for the pin 210 // sense bit, so this is what we use for now. 211 bool plugged = resp.data & (1u << 3); 212 213 if (plug_state_ != plugged) { 214 // Update our internal state. 215 plug_state_ = plugged; 216 last_plug_time_ = zx_clock_get_monotonic(); 217 218 // Inform anyone who has registered for notification. 219 ZX_DEBUG_ASSERT(pc_.async_plug_det); 220 if (!plug_notify_targets_.is_empty()) { 221 audio_proto::PlugDetectNotify notif; 222 223 notif.hdr.cmd = AUDIO_STREAM_PLUG_DETECT_NOTIFY; 224 notif.hdr.transaction_id = AUDIO_INVALID_TRANSACTION_ID; 225 notif.flags = static_cast<audio_pd_notify_flags_t>( 226 (plug_state_ ? (uint32_t)AUDIO_PDNF_PLUGGED : 0) | AUDIO_PDNF_CAN_NOTIFY); 227 notif.plug_state_time = last_plug_time_; 228 229 for (auto iter = plug_notify_targets_.begin(); iter != plug_notify_targets_.end(); ) { 230 zx_status_t res; 231 232 ZX_DEBUG_ASSERT(iter->channel != nullptr); 233 res = iter->channel->Write(¬if, sizeof(notif)); 234 if (res != ZX_OK) { 235 // If we have failed to send the notification over our 236 // client channel, something has gone fairly wrong. Remove 237 // the client from the notification list. 238 plug_notify_targets_.erase(iter++); 239 } else { 240 ++iter; 241 } 242 } 243 } 244 } 245 246 return ZX_OK; 247} 248 249zx_status_t RealtekStream::BeginChangeStreamFormatLocked(const audio_proto::StreamSetFmtReq& fmt) { 250 // Check the format arguments. 251 // 252 // Note: in the limited number of Realtek codecs I have seen so far, the 253 // channel count given by a converter's widget caps is *the* number of 254 // channels supported, not a maximum number of channels supported (as 255 // indicated by the Intel HDA specification). One can configure the number 256 // of channels in the format specifier to be less than the number maximum 257 // number of channels supported by the converter, but it will ignore you. 258 // 259 // For inputs, configuring a stereo input converter for mono will cause the 260 // converter to produce stereo frames anyway. The controller side DMA 261 // engine also does not seem smart enough to discard the extra sample (even 262 // though it was configured for mono as well) and you will end up capturing 263 // data an twice the rate you expected. 264 // 265 // For output, configuring a stereo output converter for mono seems to have 266 // no real effect on its behavior. It is still expecting stereo frames. 267 // When you configure the DMA engine for mono (as is the requirement given 268 // by Intel), the converter appears to be unhappy about the lack of samples 269 // in the frame and simply never produces any output. The Converter Channel 270 // Count control (section 7.3.3.35 of the Intel HDA spec) also appears to 271 // have no effect. This is not particularly surprising as it is supposed to 272 // only effect output converters, and only those with support for more than 273 // 2 channels, but I tried it anyway. 274 // 275 // Perhaps this is different for the 6xx series of codecs from Realtek (the 276 // 6 channel "surround sound ready" codecs); so far I have only worked with 277 // samples from the 2xx series (the stereo codec family). For now, however, 278 // insist that the format specified by the user exactly match the number of 279 // channels present in the converter we are using for this pipeline. 280 if (!fmt.channels || (fmt.channels != conv_.widget_caps.ch_count())) 281 return ZX_ERR_NOT_SUPPORTED; 282 283 if (!conv_.sample_caps.SupportsRate(fmt.frames_per_second) || 284 !conv_.sample_caps.SupportsFormat(fmt.sample_format)) 285 return ZX_ERR_NOT_SUPPORTED; 286 287 // Looks good, make sure that the converter is muted and not processing any stream tags. 288 format_set_ = false; 289 return DisableConverterLocked(); 290} 291 292zx_status_t RealtekStream::FinishChangeStreamFormatLocked(uint16_t encoded_fmt) { 293 zx_status_t res; 294 const Command ENABLE_CONVERTER_VERBS[] = { 295 { props_.conv_nid, SET_CONVERTER_FORMAT(encoded_fmt) }, 296 { props_.conv_nid, SET_CONVERTER_STREAM_CHAN(dma_stream_tag(), 0) }, 297 { props_.pc_nid, SET_POWER_STATE(HDA_PS_D0) }, 298 { props_.conv_nid, SET_POWER_STATE(HDA_PS_D0) }, 299 { props_.pc_nid, SET_ANALOG_PIN_WIDGET_CTRL(!is_input(), is_input(), 300 pc_.pin_caps.can_drive_headphones() ) }, 301 }; 302 303 res = RunCmdListLocked(ENABLE_CONVERTER_VERBS, countof(ENABLE_CONVERTER_VERBS)); 304 if (res != ZX_OK) 305 return res; 306 307 res = SendGainUpdatesLocked(); 308 if (res != ZX_OK) 309 return res; 310 311 format_set_ = true; 312 return ZX_OK; 313} 314 315void RealtekStream::OnGetGainLocked(audio_proto::GetGainResp* out_resp) { 316 ZX_DEBUG_ASSERT(out_resp); 317 318 if (conv_.has_amp) { 319 out_resp->cur_gain = ComputeCurrentGainLocked(); 320 out_resp->min_gain = conv_.min_gain; 321 out_resp->max_gain = conv_.max_gain; 322 out_resp->gain_step = conv_.gain_step; 323 } else { 324 out_resp->cur_gain = 0.0; 325 out_resp->min_gain = 0.0; 326 out_resp->max_gain = 0.0; 327 out_resp->gain_step = 0.0; 328 } 329 330 out_resp->cur_mute = cur_mute_; 331 out_resp->can_mute = can_mute(); 332} 333 334void RealtekStream::OnSetGainLocked(const audio_proto::SetGainReq& req, 335 audio_proto::SetGainResp* out_resp) { 336 zx_status_t res = ZX_OK; 337 bool mute_target = cur_mute_; 338 bool set_mute = req.flags & AUDIO_SGF_MUTE_VALID; 339 bool set_gain = req.flags & AUDIO_SGF_GAIN_VALID; 340 341 if (set_mute || set_gain) { 342 if (set_mute) { 343 if (!can_mute()) { 344 res = ZX_ERR_INVALID_ARGS; 345 } else { 346 mute_target = req.flags & AUDIO_SGF_MUTE; 347 } 348 } 349 350 if ((res == ZX_OK) && set_gain) 351 res = UpdateConverterGainLocked(req.gain); 352 } 353 354 if (res == ZX_OK) { 355 cur_mute_ = mute_target; 356 357 // Don't bother sending any update to the converter if the format is not currently set. 358 if (format_set_) 359 res = SendGainUpdatesLocked(); 360 } 361 362 if (out_resp != nullptr) { 363 out_resp->result = res; 364 out_resp->cur_mute = cur_mute_; 365 out_resp->cur_gain = ComputeCurrentGainLocked(); 366 } 367} 368 369void RealtekStream::OnPlugDetectLocked(dispatcher::Channel* response_channel, 370 const audio_proto::PlugDetectReq& req, 371 audio_proto::PlugDetectResp* out_resp) { 372 ZX_DEBUG_ASSERT(response_channel != nullptr); 373 374 // If our pin cannot perform presence detection, just fall back on the base class impl. 375 if (!pc_.pin_caps.can_pres_detect()) { 376 IntelHDAStreamBase::OnPlugDetectLocked(response_channel, req, out_resp); 377 return; 378 } 379 380 if (pc_.async_plug_det) { 381 // If we are capible of asynch plug detection, add or remove this client 382 // to/from the notify list before reporting the current state. Apps 383 // should not be setting both flags, but if they do, disable wins. 384 if (req.flags & AUDIO_PDF_DISABLE_NOTIFICATIONS) { 385 RemovePDNotificationTgtLocked(*response_channel); 386 } else if (req.flags & AUDIO_PDF_ENABLE_NOTIFICATIONS) { 387 AddPDNotificationTgtLocked(response_channel); 388 } 389 390 // Report the current plug detection state if the client expects a response. 391 if (out_resp) { 392 out_resp->flags = static_cast<audio_pd_notify_flags_t>( 393 (plug_state_ ? (uint32_t)AUDIO_PDNF_PLUGGED : 0) | 394 (pc_.async_plug_det ? (uint32_t)AUDIO_PDNF_CAN_NOTIFY : 0)); 395 out_resp->plug_state_time = last_plug_time_; 396 } 397 } else { 398 // TODO(johngro): In order to do proper polling support, we need to add 399 // the concept of a pending client request to the system. IOW - we need 400 // to create and run a state machine where we hold a reference to the 401 // client's response channel, and eventually respond to the client using 402 // the same transaction ID they requested state with. 403 // 404 // For now, if our hardware does not support async plug detect, we 405 // simply fall back on the default implementation which reports that we 406 // are hardwired and always plugged in. 407 IntelHDAStreamBase::OnPlugDetectLocked(response_channel, req, out_resp); 408 return; 409 } 410} 411 412void RealtekStream::OnGetStringLocked(const audio_proto::GetStringReq& req, 413 audio_proto::GetStringResp* out_resp) { 414 ZX_DEBUG_ASSERT(out_resp); 415 const char* requested_string = nullptr; 416 417 switch (req.id) { 418 case AUDIO_STREAM_STR_ID_MANUFACTURER: 419 requested_string = props_.mfr_name; 420 break; 421 422 case AUDIO_STREAM_STR_ID_PRODUCT: 423 requested_string = props_.product_name; 424 break; 425 426 default: 427 IntelHDAStreamBase::OnGetStringLocked(req, out_resp); 428 return; 429 } 430 431 int res = snprintf(reinterpret_cast<char*>(out_resp->str), sizeof(out_resp->str), "%s", 432 requested_string ? requested_string : "<unassigned>"); 433 ZX_DEBUG_ASSERT(res >= 0); 434 out_resp->result = ZX_OK; 435 out_resp->strlen = fbl::min<uint32_t>(res, sizeof(out_resp->str) - 1); 436 out_resp->id = req.id; 437} 438 439zx_status_t RealtekStream::UpdateSetupProgressLocked(uint32_t stage) { 440 ZX_DEBUG_ASSERT(!(setup_progress_ & STREAM_PUBLISHED)); 441 ZX_DEBUG_ASSERT(!(setup_progress_ & stage)); 442 443 setup_progress_ |= stage; 444 445 if (setup_progress_ == ALL_SETUP_COMPLETE) { 446 zx_status_t res = FinalizeSetupLocked(); 447 if (res != ZX_OK) 448 return res; 449 450 setup_progress_ |= STREAM_PUBLISHED; 451 DumpStreamPublishedLocked(); 452 return PublishDeviceLocked(); 453 } 454 455 return ZX_OK; 456} 457 458zx_status_t RealtekStream::FinalizeSetupLocked() { 459 // Stash the number of gain steps to use in the pin converter. This allows 460 // us to hardcode gain targets for things like mic boost. Eventually, we 461 // need to expose a way to detect this capability and control it via APIs, 462 // but for now we can get away with just setting it as part of the finalize 463 // step for setup. 464 cur_pc_gain_steps_ = ComputeGainSteps(pc_, props_.default_pc_gain); 465 466 // Compute the list of formats we support. 467 fbl::Vector<audio_proto::FormatRange> supported_formats; 468 zx_status_t res = MakeFormatRangeList(conv_.sample_caps, 469 conv_.widget_caps.ch_count(), 470 &supported_formats); 471 if (res != ZX_OK) { 472 DEBUG_LOG("Failed to compute supported format ranges! (res = %d)\n", res); 473 return res; 474 } 475 476 // At this point, we should have at least one sample encoding that we 477 // support. If we don't, then this output stream is pretty worthless. 478 if (!supported_formats.size()) { 479 DEBUG_LOG("WARNING - no sample encodings are supported by this audio stream! " 480 "(formats = 0x%08x, size/rates = 0x%08x)\n", 481 conv_.sample_caps.pcm_formats_, 482 conv_.sample_caps.pcm_size_rate_); 483 return ZX_ERR_NOT_SUPPORTED; 484 } 485 486 // Go over the list of format ranges produced and tweak it to account for 487 // seemingly non-standard Realtek codec behavior. Usually, when a converter 488 // says that it supports a maximum of N channels, you are supposed to be 489 // able to configure it for any number of channels in the set [1, N]. The 490 // Realtek codecs I have encountered so far, however, only support the 491 // number of channels they claim to support. IOW - If the converter says 492 // that max_channels == 2, and you configure it for 1 channel, it will still 493 // produce 2 audio frames per frame period. 494 for (auto& format : supported_formats) 495 format.min_channels = format.max_channels; 496 497 SetSupportedFormatsLocked(fbl::move(supported_formats)); 498 499 return ZX_OK; 500} 501 502void RealtekStream::DumpStreamPublishedLocked() { 503 if (!DEBUG_LOGGING) 504 return; 505 506 static const struct { 507 uint32_t flag; 508 uint32_t rate; 509 } RATE_LUT[] = { 510 { IHDA_PCM_RATE_384000, 384000 }, 511 { IHDA_PCM_RATE_192000, 192000 }, 512 { IHDA_PCM_RATE_176400, 176400 }, 513 { IHDA_PCM_RATE_96000, 96000 }, 514 { IHDA_PCM_RATE_88200, 88200 }, 515 { IHDA_PCM_RATE_48000, 48000 }, 516 { IHDA_PCM_RATE_44100, 44100 }, 517 { IHDA_PCM_RATE_32000, 32000 }, 518 { IHDA_PCM_RATE_22050, 22050 }, 519 { IHDA_PCM_RATE_16000, 16000 }, 520 { IHDA_PCM_RATE_11025, 11025 }, 521 { IHDA_PCM_RATE_8000, 8000 }, 522 }; 523 524 static const struct { 525 uint32_t flag; 526 uint32_t bits; 527 } BITS_LUT[] = { 528 { IHDA_PCM_SIZE_32BITS, 32 }, 529 { IHDA_PCM_SIZE_24BITS, 24 }, 530 { IHDA_PCM_SIZE_20BITS, 20 }, 531 { IHDA_PCM_SIZE_16BITS, 16 }, 532 { IHDA_PCM_SIZE_8BITS, 8 }, 533 }; 534 535 LOG("Setup complete, publishing %s stream\n", props_.is_input ? "input" : "output"); 536 LOG("Channels : %u\n", conv_.widget_caps.ch_count()); 537 538 LOG("Sample rates :"); 539 for (size_t i = 0; i < countof(RATE_LUT); ++i) { 540 const auto& entry = RATE_LUT[i]; 541 if (conv_.sample_caps.pcm_size_rate_ & entry.flag) 542 printf(" %u", entry.rate); 543 } 544 printf("\n"); 545 546 LOG("Sample bits :"); 547 for (size_t i = 0; i < countof(BITS_LUT); ++i) { 548 const auto& entry = BITS_LUT[i]; 549 if (conv_.sample_caps.pcm_size_rate_ & entry.flag) 550 printf(" %u", entry.bits); 551 } 552 printf("\n"); 553 554 DumpAmpCaps(conv_, "Conv"); 555 DumpAmpCaps(pc_, "PC"); 556 557 if (pc_.pin_caps.can_pres_detect()) { 558 LOG("Plug Detect : %s (current state %s)\n", 559 pc_.async_plug_det ? "Asynchronous" : "Poll-only", 560 plug_state_ ? "Plugged" : "Unplugged"); 561 } else { 562 LOG("Plug Detect : No\n"); 563 } 564 565} 566 567void RealtekStream::DumpAmpCaps(const CommonCaps& caps, const char* tag) { 568 if (caps.has_amp) { 569 LOG("%4s Gain control : [%.2f, %.2f] dB in %.2f dB steps (%s mute).\n", 570 tag, 571 caps.min_gain, 572 caps.max_gain, 573 caps.gain_step, 574 caps.amp_caps.can_mute() ? "can" : "cannot"); 575 } else { 576 LOG("%4s Gain control : 0dB fixed (cannot mute)\n", tag); 577 } 578} 579 580#define THUNK(_method) (&RealtekStream::_method) 581 582zx_status_t RealtekStream::OnActivateLocked() { 583 // Start by attempting to put our pin complex and converter into a disabled 584 // state. 585 zx_status_t res = DisableConverterLocked(); 586 if (res != ZX_OK) 587 return res; 588 589 // Start the setup process by fetching the widget caps for our converter and 590 // pin complex. This will let us know where various parameters (sample 591 // size/rate, stream format, amplifier caps, etc...) come from. Also, go 592 // ahead and fetch the pin caps so we have an idea of our presence detection 593 // capabilities. 594 const Command SETUP[] = { 595 { props_.pc_nid, GET_PARAM(CodecParam::AW_CAPS), THUNK(ProcessPinWidgetCaps) }, 596 { props_.pc_nid, GET_CONFIG_DEFAULT, THUNK(ProcessPinCfgDefaults) }, 597 { props_.pc_nid, GET_PARAM(CodecParam::PIN_CAPS), THUNK(ProcessPinCaps) }, 598 { props_.conv_nid, GET_PARAM(CodecParam::AW_CAPS), THUNK(ProcessConverterWidgetCaps) }, 599 }; 600 601 return RunCmdListLocked(SETUP, countof(SETUP)); 602} 603 604zx_status_t RealtekStream::ProcessPinWidgetCaps(const Command& cmd, const CodecResponse& resp) { 605 // Stash the pin's audio-widget caps. We will need it while processing the 606 // pin caps to determine if we need to register for async plug detection 607 // notifications before querying the initial pin state. 608 pc_.widget_caps.raw_data_ = resp.data; 609 610 // Does this pin complex have an amplifier? If so, we need to query what 611 // it's caps, so we know what it's mute capabilities and unity gain are. If 612 // not, we are done. 613 pc_.has_amp = is_input() 614 ? pc_.widget_caps.input_amp_present() 615 : pc_.widget_caps.output_amp_present(); 616 617 if (!pc_.has_amp) 618 return UpdateSetupProgressLocked(PIN_COMPLEX_SETUP_COMPLETE); 619 620 return RunCmdLocked({ pc_.widget_caps.amp_param_override() ? props_.pc_nid : props_.afg_nid, 621 GET_PARAM(AMP_CAPS(is_input())), 622 THUNK(ProcessPinAmpCaps) }); 623} 624 625zx_status_t RealtekStream::ProcessPinAmpCaps(const Command& cmd, const CodecResponse& resp) { 626 pc_.amp_caps.raw_data_ = resp.data; 627 628 pc_.gain_step = pc_.amp_caps.step_size_db(); 629 pc_.min_gain = pc_.amp_caps.min_gain_db(); 630 pc_.max_gain = pc_.amp_caps.max_gain_db(); 631 632 return UpdateSetupProgressLocked(PIN_COMPLEX_SETUP_COMPLETE); 633} 634 635zx_status_t RealtekStream::ProcessPinCfgDefaults(const Command& cmd, const CodecResponse& resp) { 636 pc_.cfg_defaults.raw_data_ = resp.data; 637 return ZX_OK; 638} 639 640zx_status_t RealtekStream::ProcessPinCaps(const Command& cmd, const CodecResponse& resp) { 641 pc_.pin_caps.raw_data_ = resp.data; 642 643 // Sanity check out input/output configuration. 644 if ((is_input() ? pc_.pin_caps.can_input() : pc_.pin_caps.can_output()) == false) { 645 const char* tag = is_input() ? "input" : "output"; 646 LOG("ERROR: Stream configured for %s, but pin complex cannot %s\n", tag, tag); 647 return ZX_ERR_BAD_STATE; 648 } 649 650 // Is the Jack Detect Override bit set in our config defaults? If so, 651 // force-clear all of the bits in the pin caps which indicate an ability to 652 // perform presence detection and impedence sensing. Even though hardware 653 // technically has the ability to perform presence detection, the 654 // BIOS/Device manufacturer is trying to tell us that presence detection 655 // circuitry has not been wired up, and that this stream is hardwired. 656 // 657 if (pc_.cfg_defaults.jack_detect_override()) { 658 static constexpr uint32_t mask = AW_PIN_CAPS_FLAG_CAN_IMPEDANCE_SENSE 659 | AW_PIN_CAPS_FLAG_TRIGGER_REQUIRED 660 | AW_PIN_CAPS_FLAG_CAN_PRESENCE_DETECT; 661 pc_.pin_caps.raw_data_ &= ~mask; 662 } 663 664 // Can this stream determine if it is connected or not? If not, then we 665 // just assume that we are always plugged in. 666 if (!pc_.pin_caps.can_pres_detect() || pc_.pin_caps.trig_required()) { 667 if (pc_.pin_caps.trig_required()) { 668 LOG("WARNING : Triggered impedence sense plug detect not supported. " 669 "Stream will always appear to be plugged in.\n"); 670 } 671 return UpdateSetupProgressLocked(PLUG_STATE_SETUP_COMPLETE); 672 } 673 674 // Looks like we support presence detection. Enable unsolicited 675 // notifications of pin state if supported, then query the initial pin 676 // state. 677 pc_.async_plug_det = pc_.widget_caps.can_send_unsol(); 678 if (pc_.async_plug_det) { 679 zx_status_t res = AllocateUnsolTagLocked(&pc_.unsol_tag); 680 if (res == ZX_OK) { 681 zx_status_t res = RunCmdLocked({ props_.pc_nid, 682 SET_UNSOLICITED_RESP_CTRL(true, pc_.unsol_tag) }); 683 if (res != ZX_OK) 684 return res; 685 } else { 686 LOG("WARNING : Failed to allocate unsolicited response tag from " 687 "codec pool (res %d). Asynchronous plug detection will be " 688 "disabled.\n", res); 689 pc_.async_plug_det = false; 690 } 691 } 692 693 // Now that notifications have been enabled (or not), query the initial pin state. 694 return RunCmdLocked({ props_.pc_nid, GET_PIN_SENSE, THUNK(ProcessPinState) }); 695} 696 697zx_status_t RealtekStream::ProcessPinState(const Command& cmd, const CodecResponse& resp) { 698 plug_state_ = PinSenseState(resp.data).presence_detect(); 699 last_plug_time_ = zx_clock_get_monotonic(); 700 return UpdateSetupProgressLocked(PLUG_STATE_SETUP_COMPLETE); 701} 702 703zx_status_t RealtekStream::ProcessConverterWidgetCaps(const Command& cmd, 704 const CodecResponse& resp) { 705 zx_status_t res; 706 707 conv_.widget_caps.raw_data_ = resp.data; 708 conv_.has_amp = is_input() ? conv_.widget_caps.input_amp_present() 709 : conv_.widget_caps.output_amp_present(); 710 711 // Fetch the amp caps (if any) either from the converter or the defaults 712 // from the function group if the converter has not overridden them. 713 if (conv_.has_amp) { 714 uint16_t nid = conv_.widget_caps.amp_param_override() ? props_.conv_nid : props_.afg_nid; 715 res = RunCmdLocked({ nid, 716 GET_PARAM(AMP_CAPS(is_input())), 717 THUNK(ProcessConverterAmpCaps) }); 718 if (res != ZX_OK) 719 return res; 720 } 721 722 // Fetch the supported sample rates, bit depth, and formats. 723 uint16_t nid = conv_.widget_caps.format_override() ? props_.conv_nid : props_.afg_nid; 724 const Command FETCH_FORMATS[] = { 725 { nid, GET_PARAM(CodecParam::SUPPORTED_PCM_SIZE_RATE), THUNK(ProcessConverterSampleSizeRate) }, 726 { nid, GET_PARAM(CodecParam::SUPPORTED_STREAM_FORMATS), THUNK(ProcessConverterSampleFormats) }, 727 }; 728 729 res = RunCmdListLocked(FETCH_FORMATS, countof(FETCH_FORMATS)); 730 if (res != ZX_OK) 731 return res; 732 733 return ZX_OK; 734} 735 736zx_status_t RealtekStream::ProcessConverterAmpCaps(const Command& cmd, const CodecResponse& resp) { 737 conv_.amp_caps.raw_data_ = resp.data; 738 739 conv_.gain_step = conv_.amp_caps.step_size_db(); 740 conv_.min_gain = conv_.amp_caps.min_gain_db(); 741 conv_.max_gain = conv_.amp_caps.max_gain_db(); 742 743 return UpdateConverterGainLocked(fbl::max(props_.default_conv_gain, conv_.min_gain)); 744} 745 746zx_status_t RealtekStream::ProcessConverterSampleSizeRate(const Command& cmd, 747 const CodecResponse& resp) { 748 conv_.sample_caps.pcm_size_rate_ = resp.data; 749 return ZX_OK; 750} 751 752zx_status_t RealtekStream::ProcessConverterSampleFormats(const Command& cmd, 753 const CodecResponse& resp) { 754 conv_.sample_caps.pcm_formats_ = resp.data; 755 return UpdateSetupProgressLocked(CONVERTER_SETUP_COMPLETE); 756} 757#undef THUNK 758 759} // namespace codecs 760} // namespace intel_hda 761} // namespace audio 762