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/binding.h>
8#include <ddk/device.h>
9#include <ddk/protocol/intel-hda-codec.h>
10#include <lib/zx/handle.h>
11#include <fbl/mutex.h>
12#include <fbl/ref_counted.h>
13#include <fbl/ref_ptr.h>
14#include <fbl/unique_ptr.h>
15#include <stdint.h>
16#include <string.h>
17#include <zircon/thread_annotations.h>
18
19#include <dispatcher-pool/dispatcher-channel.h>
20#include <dispatcher-pool/dispatcher-execution-domain.h>
21#include <intel-hda/utils/codec-commands.h>
22#include <intel-hda/utils/intel-hda-proto.h>
23#include <intel-hda/utils/intel-hda-registers.h>
24
25#include "codec-cmd-job.h"
26#include "debug-logging.h"
27#include "intel-hda-stream.h"
28
29namespace audio {
30namespace intel_hda {
31
32class IntelHDAController;
33struct CodecResponse;
34
35class IntelHDACodec : public fbl::RefCounted<IntelHDACodec> {
36public:
37    enum class State {
38        PROBING,
39        FINDING_DRIVER,
40        OPERATING,
41        SHUTTING_DOWN,
42        SHUT_DOWN,
43        FATAL_ERROR,
44    };
45
46    static fbl::RefPtr<IntelHDACodec> Create(IntelHDAController& controller, uint8_t codec_id);
47
48    zx_status_t Startup();
49    void ProcessSolicitedResponse(const CodecResponse& resp, fbl::unique_ptr<CodecCmdJob>&& job);
50    void ProcessUnsolicitedResponse(const CodecResponse& resp);
51    void ProcessWakeupEvt();
52
53    // TODO (johngro) : figure out shutdown... Currently, this expected to
54    // execute synchronously, which does not allow codec drivers any opportunity
55    // to perform a graceful shutdown.
56    //
57    // OTOH - If our driver is being unloaded by the device manager, in theory,
58    // it should have already unloaded all of the codecs, giving them a chances
59    // to quiesce their hardware in the process.
60    void Shutdown();
61
62    uint8_t id()  const { return codec_id_; }
63    State state() const { return state_; }
64    const char* log_prefix() const { return log_prefix_; }
65
66    // Debug/Diags
67    void DumpState();
68
69private:
70    friend class fbl::RefPtr<IntelHDACodec>;
71
72    using ProbeParseCbk = zx_status_t (IntelHDACodec::*)(const CodecResponse& resp);
73    struct ProbeCommandListEntry {
74        CodecVerb     verb;
75        ProbeParseCbk parse;
76    };
77
78    static constexpr size_t PROP_PROTOCOL    = 0;
79    static constexpr size_t PROP_VID         = 1;
80    static constexpr size_t PROP_DID         = 2;
81    static constexpr size_t PROP_MAJOR_REV   = 3;
82    static constexpr size_t PROP_MINOR_REV   = 4;
83    static constexpr size_t PROP_VENDOR_REV  = 5;
84    static constexpr size_t PROP_VENDOR_STEP = 6;
85    static constexpr size_t PROP_COUNT       = 7;
86
87    static zx_protocol_device_t CODEC_DEVICE_THUNKS;
88    static ihda_codec_protocol_ops_t CODEC_PROTO_THUNKS;
89
90    IntelHDACodec(IntelHDAController& controller, uint8_t codec_id);
91    virtual ~IntelHDACodec() { ZX_DEBUG_ASSERT(state_ == State::SHUT_DOWN); }
92
93    zx_status_t PublishDevice();
94
95    void SendCORBResponse(const fbl::RefPtr<dispatcher::Channel>& channel,
96                          const CodecResponse& resp,
97                          uint32_t transaction_id = IHDA_INVALID_TRANSACTION_ID);
98
99    // Parsers for device probing
100    zx_status_t ParseVidDid(const CodecResponse& resp);
101    zx_status_t ParseRevisionId(const CodecResponse& resp);
102
103    // ZX_PROTOCOL_IHDA_CODEC Interface
104    zx_status_t CodecGetDispatcherChannel(zx_handle_t* channel_out);
105
106    // Thunks for interacting with clients and codec drivers.
107    zx_status_t DeviceIoctl(uint32_t op, void* out_buf, size_t out_len, size_t* out_actual);
108    zx_status_t ProcessClientRequest(dispatcher::Channel* channel, bool is_driver_channel);
109    void ProcessClientDeactivate(const dispatcher::Channel* channel);
110    zx_status_t ProcessGetIDs(dispatcher::Channel* channel, const ihda_proto::GetIDsReq& req);
111    zx_status_t ProcessSendCORBCmd(dispatcher::Channel* channel,
112                                   const ihda_proto::SendCORBCmdReq& req);
113    zx_status_t ProcessRequestStream(dispatcher::Channel* channel,
114                                     const ihda_proto::RequestStreamReq& req);
115    zx_status_t ProcessReleaseStream(dispatcher::Channel* channel,
116                                     const ihda_proto::ReleaseStreamReq& req);
117    zx_status_t ProcessSetStreamFmt(dispatcher::Channel* channel,
118                                    const ihda_proto::SetStreamFmtReq& req);
119
120    // Reference to our owner.
121    IntelHDAController& controller_;
122
123    // State management.
124    State state_ = State::PROBING;
125    uint probe_rx_ndx_ = 0;
126
127    // Driver connection state
128    fbl::Mutex codec_driver_channel_lock_;
129    fbl::RefPtr<dispatcher::Channel> codec_driver_channel_ TA_GUARDED(codec_driver_channel_lock_);
130
131    // Device properties.
132    const uint8_t codec_id_;
133    zx_device_prop_t dev_props_[PROP_COUNT];
134    zx_device_t* dev_node_ = nullptr;
135    struct {
136        uint16_t vid;
137        uint16_t did;
138        uint8_t  ihda_vmaj;
139        uint8_t  ihda_vmin;
140        uint8_t  rev_id;
141        uint8_t  step_id;
142    } props_;
143
144    // Log prefix storage
145    char log_prefix_[LOG_PREFIX_STORAGE] = { 0 };
146
147    // Dispatcher framework state.
148    fbl::RefPtr<dispatcher::ExecutionDomain> default_domain_;
149
150    // Active DMA streams
151    fbl::Mutex          active_streams_lock_;
152    IntelHDAStream::Tree active_streams_ TA_GUARDED(active_streams_lock_);
153
154    static ProbeCommandListEntry PROBE_COMMANDS[];
155};
156
157}  // namespace intel_hda
158}  // namespace audio
159