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