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 #pragma once 6 7 #include <ddk/device.h> 8 #include <ddk/driver.h> 9 #include <ddk/protocol/intelhda/codec.h> 10 #include <lib/zx/handle.h> 11 #include <fbl/intrusive_wavl_tree.h> 12 #include <fbl/mutex.h> 13 #include <fbl/ref_ptr.h> 14 15 #include <dispatcher-pool/dispatcher-execution-domain.h> 16 #include <intel-hda/utils/codec-commands.h> 17 #include <intel-hda/utils/intel-hda-proto.h> 18 19 namespace audio { 20 namespace intel_hda { 21 namespace codecs { 22 23 class IntelHDAStreamBase; 24 25 class IntelHDACodecDriverBase : public fbl::RefCounted<IntelHDACodecDriverBase> { 26 public: 27 virtual void Shutdown(); 28 virtual zx_status_t Suspend(uint32_t flags); 29 30 // Properties codec_device()31 zx_device_t* codec_device() const { return codec_device_; } create_time()32 zx_time_t create_time() const { return create_time_; } 33 34 // Unsolicited tag allocation for streams 35 zx_status_t AllocateUnsolTag(const IntelHDAStreamBase& stream, uint8_t* out_tag); 36 void ReleaseUnsolTag(const IntelHDAStreamBase& stream, uint8_t tag); 37 void ReleaseAllUnsolTags(const IntelHDAStreamBase& stream); 38 39 protected: 40 static constexpr uint32_t CODEC_TID = 0xFFFFFFFF; 41 42 IntelHDACodecDriverBase(); ~IntelHDACodecDriverBase()43 virtual ~IntelHDACodecDriverBase() { } 44 45 /////////////////////////////////////////////////////////////////////////// 46 // 47 // Methods used by derived classes in order to implement their driver. 48 // 49 /////////////////////////////////////////////////////////////////////////// 50 51 // Bind should only ever be called exactly once (during driver 52 // instantiation). Drivers must make sure that no other methods are in 53 // flight during a call to Bind. 54 zx_status_t Bind(zx_device_t* codec_dev, const char* name); 55 56 // Send a codec command to our codec device. 57 zx_status_t SendCodecCommand(uint16_t nid, CodecVerb verb, bool no_ack); 58 Start()59 virtual zx_status_t Start() { return ZX_OK; } ProcessUnsolicitedResponse(const CodecResponse & resp)60 virtual zx_status_t ProcessUnsolicitedResponse(const CodecResponse& resp) { return ZX_OK; } ProcessSolicitedResponse(const CodecResponse & resp)61 virtual zx_status_t ProcessSolicitedResponse (const CodecResponse& resp) { return ZX_OK; } 62 63 // Unsolicited tag allocation for codecs. AllocateUnsolTag(uint8_t * out_tag)64 zx_status_t AllocateUnsolTag(uint8_t* out_tag) { return AllocateUnsolTag(CODEC_TID, out_tag); } ReleaseUnsolTag(uint8_t tag)65 void ReleaseUnsolTag(uint8_t tag) { ReleaseUnsolTag(CODEC_TID, tag); } 66 67 fbl::RefPtr<IntelHDAStreamBase> GetActiveStream(uint32_t stream_id) 68 __TA_EXCLUDES(active_streams_lock_); 69 zx_status_t ActivateStream(const fbl::RefPtr<IntelHDAStreamBase>& stream) 70 __TA_EXCLUDES(active_streams_lock_); 71 zx_status_t DeactivateStream(uint32_t stream_id) 72 __TA_EXCLUDES(active_streams_lock_); 73 74 // Debug logging 75 virtual void PrintDebugPrefix() const; 76 77 private: 78 friend class fbl::RefPtr<IntelHDACodecDriverBase>; 79 80 union CodecChannelResponses { 81 ihda_proto::CmdHdr hdr; 82 ihda_proto::SendCORBCmdResp send_corb; 83 ihda_proto::RequestStreamResp request_stream; 84 ihda_proto::SetStreamFmtResp set_stream_fmt; 85 }; 86 87 void DeviceRelease(); 88 89 // Thunks for dispatching channel events. 90 zx_status_t ProcessClientRequest(dispatcher::Channel* channel); 91 void ProcessClientDeactivate(const dispatcher::Channel* channel); 92 93 // Unsolicited response tag to stream ID bookkeeping. 94 zx_status_t AllocateUnsolTag(uint32_t stream_id, uint8_t* out_tag); 95 void ReleaseUnsolTag (uint32_t stream_id, uint8_t tag); 96 void ReleaseAllUnsolTags(uint32_t stream_id); 97 zx_status_t MapUnsolTagToStreamId(uint8_t tag, uint32_t* out_stream_id); 98 99 // Called in order to unlink this device from the controller driver. After 100 // this call returns, the codec driver is guaranteed that no calls to any of 101 // the driver implemented callbacks (see below) are in flight, and that no 102 // new calls will be initiated. It is not safe to make this call during a 103 // controller callback. To unlink from a controller during a callback, 104 // return an error code from the callback. 105 void UnlinkFromController(); 106 107 zx_status_t ProcessStreamResponse(const fbl::RefPtr<IntelHDAStreamBase>& stream, 108 const CodecChannelResponses& resp, 109 uint32_t resp_size, 110 zx::handle&& rxed_handle); 111 112 static zx_protocol_device_t CODEC_DEVICE_THUNKS; 113 zx_device_t* codec_device_ = nullptr; 114 zx_time_t create_time_ = zx_clock_get_monotonic(); 115 116 fbl::Mutex device_channel_lock_; 117 fbl::RefPtr<dispatcher::Channel> device_channel_ __TA_GUARDED(device_channel_lock_); 118 119 using ActiveStreams = fbl::WAVLTree<uint32_t, fbl::RefPtr<IntelHDAStreamBase>>; 120 fbl::Mutex active_streams_lock_; 121 ActiveStreams active_streams_ __TA_GUARDED(active_streams_lock_); 122 123 fbl::Mutex shutdown_lock_ __TA_ACQUIRED_BEFORE(device_channel_lock_, active_streams_lock_); 124 bool shutting_down_ __TA_GUARDED(shutdown_lock_) = false; 125 126 // Dispatcher framework state 127 fbl::RefPtr<dispatcher::ExecutionDomain> default_domain_; 128 129 // State for tracking unsolicited response tag allocations. 130 // 131 // Note: If we wanted to save a bit of RAM, we could move this to a 132 // dynamically allocated list/tree based system. For now, however, this LUT 133 // is dirt simple and does the job. 134 fbl::Mutex unsol_tag_lock_; 135 uint64_t free_unsol_tags_ __TA_GUARDED(unsol_tag_lock_) = 0xFFFFFFFFFFFFFFFEu; 136 uint32_t unsol_tag_to_stream_id_map_[sizeof(free_unsol_tags_) << 3] 137 __TA_GUARDED(unsol_tag_lock_); 138 }; 139 140 } // namespace codecs 141 } // namespace intel_hda 142 } // namespace audio 143