// Copyright 2017 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #pragma once #include #include #include #include #include #include "utils.h" namespace audio { namespace intel_hda { namespace codecs { #define DECLARE_THUNK(_name) \ zx_status_t _name(const Command& cmd, const CodecResponse& resp) __TA_REQUIRES(obj_lock()); class RealtekStream : public IntelHDAStreamBase { public: RealtekStream(const StreamProperties& props); protected: friend class fbl::RefPtr; virtual ~RealtekStream() { } // IntelHDAStreamBase implementation zx_status_t OnActivateLocked() __TA_REQUIRES(obj_lock()) final; void OnDeactivateLocked() __TA_REQUIRES(obj_lock()) final; void OnChannelDeactivateLocked(const dispatcher::Channel& channel) __TA_REQUIRES(obj_lock()) final; zx_status_t OnDMAAssignedLocked() __TA_REQUIRES(obj_lock()) final; zx_status_t OnSolicitedResponseLocked(const CodecResponse& resp) __TA_REQUIRES(obj_lock()) final; zx_status_t OnUnsolicitedResponseLocked(const CodecResponse& resp) __TA_REQUIRES(obj_lock()) final; zx_status_t BeginChangeStreamFormatLocked(const audio_proto::StreamSetFmtReq& fmt) __TA_REQUIRES(obj_lock()) final; zx_status_t FinishChangeStreamFormatLocked(uint16_t encoded_fmt) __TA_REQUIRES(obj_lock()) final; void OnGetGainLocked(audio_proto::GetGainResp* out_resp) __TA_REQUIRES(obj_lock()) final; void OnSetGainLocked(const audio_proto::SetGainReq& req, audio_proto::SetGainResp* out_resp) __TA_REQUIRES(obj_lock()) final; void OnPlugDetectLocked(dispatcher::Channel* response_channel, const audio_proto::PlugDetectReq& req, audio_proto::PlugDetectResp* out_resp) __TA_REQUIRES(obj_lock()) final; void OnGetStringLocked(const audio_proto::GetStringReq& req, audio_proto::GetStringResp* out_resp) __TA_REQUIRES(obj_lock()) final; private: struct Command { using Thunk = zx_status_t (RealtekStream::*)(const Command& cmd, const CodecResponse& resp); const uint16_t nid; const CodecVerb verb; const Thunk thunk = nullptr; }; // Capabilities common to both converters and pin complexes. struct CommonCaps { AudioWidgetCaps widget_caps; AmpCaps amp_caps; bool has_amp = false; float max_gain = 0.0; float min_gain = 0.0; float gain_step = 0.0; }; // Capabilities for converters struct ConverterCaps : public CommonCaps { SampleCaps sample_caps; }; // Capabilities for pin complexes struct PinComplexCaps : public CommonCaps { ConfigDefaults cfg_defaults; PinCaps pin_caps; bool async_plug_det = false; uint8_t unsol_tag; }; // Declare a slab allocator for PendingCommands. Note; it needs to be made // our friend in order to see the definition of the PendingCommand private // inner class. class PendingCommand; using PCAT = fbl::StaticSlabAllocatorTraits, 4096>; using PendingCommandAllocator = fbl::SlabAllocator; friend PendingCommandAllocator; class PendingCommand : public fbl::DoublyLinkedListable>, public fbl::SlabAllocated { public: const Command& cmd() const { return cmd_; } zx_status_t Invoke(RealtekStream* stream, const CodecResponse& resp) __TA_REQUIRES(stream->obj_lock()) { ZX_DEBUG_ASSERT((stream != nullptr) && (cmd_.thunk != nullptr)); return ((*stream).*(cmd_.thunk))(cmd_, resp); } private: // Hide our constructor and make the allocator our friend so that people // do not accidentally allocate a pending command using std::new friend PendingCommandAllocator; PendingCommand(const Command& cmd) : cmd_(cmd) { } Command cmd_; }; // TODO(johngro) : Elminiate this complexity if/when we get to the point // that audio streams have a 1:1 relationship with their clients (instead of // 1:many) struct NotifyTarget : fbl::DoublyLinkedListable> { explicit NotifyTarget(fbl::RefPtr&& ch) : channel(ch) { } fbl::RefPtr channel; }; using NotifyTargetList = fbl::DoublyLinkedList>; // Bits used to track setup state machine progress. static constexpr uint32_t PIN_COMPLEX_SETUP_COMPLETE = (1u << 0); static constexpr uint32_t CONVERTER_SETUP_COMPLETE = (1u << 1); static constexpr uint32_t PLUG_STATE_SETUP_COMPLETE = (1u << 2); static constexpr uint32_t DMA_ASSIGNMENT_COMPLETE = (1u << 3); static constexpr uint32_t STREAM_PUBLISHED = (1u << 31); static constexpr uint32_t ALL_SETUP_COMPLETE = PIN_COMPLEX_SETUP_COMPLETE | CONVERTER_SETUP_COMPLETE | PLUG_STATE_SETUP_COMPLETE | DMA_ASSIGNMENT_COMPLETE; static uint8_t ComputeGainSteps(const CommonCaps& caps, float target_gai); zx_status_t RunCmdLocked(const Command& cmd) __TA_REQUIRES(obj_lock()); zx_status_t RunCmdListLocked(const Command* list, size_t count, bool force_all = false) __TA_REQUIRES(obj_lock()); zx_status_t DisableConverterLocked(bool force_all = false) __TA_REQUIRES(obj_lock()); zx_status_t UpdateConverterGainLocked(float target_gain) __TA_REQUIRES(obj_lock()); float ComputeCurrentGainLocked() __TA_REQUIRES(obj_lock()); zx_status_t SendGainUpdatesLocked() __TA_REQUIRES(obj_lock()); void AddPDNotificationTgtLocked(dispatcher::Channel* channel) __TA_REQUIRES(obj_lock()); void RemovePDNotificationTgtLocked(const dispatcher::Channel& channel) __TA_REQUIRES(obj_lock()); // Setup state machine methods. zx_status_t UpdateSetupProgressLocked(uint32_t stage) __TA_REQUIRES(obj_lock()); zx_status_t FinalizeSetupLocked() __TA_REQUIRES(obj_lock()); void DumpStreamPublishedLocked() __TA_REQUIRES(obj_lock()); void DumpAmpCaps(const CommonCaps& caps, const char* tag); DECLARE_THUNK(ProcessPinWidgetCaps); DECLARE_THUNK(ProcessPinAmpCaps); DECLARE_THUNK(ProcessPinCfgDefaults); DECLARE_THUNK(ProcessPinCaps); DECLARE_THUNK(ProcessPinState); DECLARE_THUNK(ProcessConverterWidgetCaps); DECLARE_THUNK(ProcessConverterAmpCaps); DECLARE_THUNK(ProcessConverterSampleSizeRate); DECLARE_THUNK(ProcessConverterSampleFormats); bool can_mute() const __TA_REQUIRES(obj_lock()) { return (conv_.has_amp && conv_.amp_caps.can_mute()) || (pc_.has_amp && pc_.amp_caps.can_mute()); } const StreamProperties props_; fbl::DoublyLinkedList> pending_cmds_ __TA_GUARDED(obj_lock()); // Setup state machine progress. uint32_t setup_progress_ __TA_GUARDED(obj_lock()) = 0; bool format_set_ __TA_GUARDED(obj_lock()) = false; // Current gain and plug detect settings. uint8_t cur_conv_gain_steps_ __TA_GUARDED(obj_lock()) = 0; uint8_t cur_pc_gain_steps_ __TA_GUARDED(obj_lock()) = 0; bool cur_mute_ __TA_GUARDED(obj_lock()) = false; bool plug_state_ __TA_GUARDED(obj_lock()) = true; zx_time_t last_plug_time_ __TA_GUARDED(obj_lock()) = 0; NotifyTargetList plug_notify_targets_ __TA_GUARDED(obj_lock()); // Converter and pin complex capabilities. ConverterCaps conv_ __TA_GUARDED(obj_lock()); PinComplexCaps pc_ __TA_GUARDED(obj_lock()); }; #undef DECLARE_THUNK } // namespace codecs } // namespace intel_hda } // namespace audio // TODO(johngro) : Right now, there is no really good way to hide a static slab // allocator from the rest of the world. It is not really much of a concern // here, but it seems odd to have a private inner class which can be // instantiated by things outside of the class. // // We should probably either fix static slab allocators so that they can be made // private inner classes as well, or just go ahead and make these slab allocated // bookkeeping classes non-inner-classes. FWD_DECL_STATIC_SLAB_ALLOCATOR(audio::intel_hda::codecs::RealtekStream::PCAT);