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 <fbl/intrusive_double_list.h>
8#include <fbl/ref_ptr.h>
9#include <fbl/slab_allocator.h>
10
11#include <intel-hda/codec-utils/stream-base.h>
12#include <intel-hda/utils/codec-caps.h>
13
14#include "utils.h"
15
16namespace audio {
17namespace intel_hda {
18namespace codecs {
19
20#define DECLARE_THUNK(_name) \
21    zx_status_t _name(const Command& cmd, const CodecResponse& resp) __TA_REQUIRES(obj_lock());
22
23class RealtekStream : public IntelHDAStreamBase {
24public:
25    RealtekStream(const StreamProperties& props);
26
27protected:
28    friend class fbl::RefPtr<RealtekStream>;
29
30    virtual ~RealtekStream() { }
31
32    // IntelHDAStreamBase implementation
33    zx_status_t OnActivateLocked()    __TA_REQUIRES(obj_lock()) final;
34    void        OnDeactivateLocked()  __TA_REQUIRES(obj_lock()) final;
35    void        OnChannelDeactivateLocked(const dispatcher::Channel& channel)
36        __TA_REQUIRES(obj_lock()) final;
37    zx_status_t OnDMAAssignedLocked() __TA_REQUIRES(obj_lock()) final;
38    zx_status_t OnSolicitedResponseLocked(const CodecResponse& resp)
39        __TA_REQUIRES(obj_lock()) final;
40    zx_status_t OnUnsolicitedResponseLocked(const CodecResponse& resp)
41        __TA_REQUIRES(obj_lock()) final;
42    zx_status_t BeginChangeStreamFormatLocked(const audio_proto::StreamSetFmtReq& fmt)
43        __TA_REQUIRES(obj_lock()) final;
44    zx_status_t FinishChangeStreamFormatLocked(uint16_t encoded_fmt)
45        __TA_REQUIRES(obj_lock()) final;
46    void OnGetGainLocked(audio_proto::GetGainResp* out_resp) __TA_REQUIRES(obj_lock()) final;
47    void OnSetGainLocked(const audio_proto::SetGainReq& req,
48                         audio_proto::SetGainResp* out_resp) __TA_REQUIRES(obj_lock()) final;
49    void OnPlugDetectLocked(dispatcher::Channel* response_channel,
50                            const audio_proto::PlugDetectReq& req,
51                            audio_proto::PlugDetectResp* out_resp) __TA_REQUIRES(obj_lock()) final;
52    void OnGetStringLocked(const audio_proto::GetStringReq& req,
53                           audio_proto::GetStringResp* out_resp) __TA_REQUIRES(obj_lock()) final;
54
55private:
56    struct Command {
57        using Thunk = zx_status_t (RealtekStream::*)(const Command& cmd,
58                                                     const CodecResponse& resp);
59        const uint16_t  nid;
60        const CodecVerb verb;
61        const Thunk     thunk = nullptr;
62    };
63
64    // Capabilities common to both converters and pin complexes.
65    struct CommonCaps {
66        AudioWidgetCaps widget_caps;
67        AmpCaps         amp_caps;
68        bool            has_amp   = false;
69        float           max_gain  = 0.0;
70        float           min_gain  = 0.0;
71        float           gain_step = 0.0;
72    };
73
74    // Capabilities for converters
75    struct ConverterCaps : public CommonCaps {
76        SampleCaps      sample_caps;
77    };
78
79    // Capabilities for pin complexes
80    struct PinComplexCaps : public CommonCaps {
81        ConfigDefaults  cfg_defaults;
82        PinCaps         pin_caps;
83        bool            async_plug_det = false;
84        uint8_t         unsol_tag;
85    };
86
87    // Declare a slab allocator for PendingCommands.  Note; it needs to be made
88    // our friend in order to see the definition of the PendingCommand private
89    // inner class.
90    class  PendingCommand;
91    using  PCAT = fbl::StaticSlabAllocatorTraits<fbl::unique_ptr<PendingCommand>, 4096>;
92    using  PendingCommandAllocator = fbl::SlabAllocator<PCAT>;
93    friend PendingCommandAllocator;
94
95    class PendingCommand : public fbl::DoublyLinkedListable<fbl::unique_ptr<PendingCommand>>,
96                           public fbl::SlabAllocated<PCAT> {
97    public:
98        const Command& cmd() const { return cmd_; }
99
100        zx_status_t Invoke(RealtekStream* stream,
101                           const CodecResponse& resp) __TA_REQUIRES(stream->obj_lock()) {
102            ZX_DEBUG_ASSERT((stream != nullptr) && (cmd_.thunk != nullptr));
103            return ((*stream).*(cmd_.thunk))(cmd_, resp);
104        }
105
106    private:
107        // Hide our constructor and make the allocator our friend so that people
108        // do not accidentally allocate a pending command using std::new
109        friend PendingCommandAllocator;
110        PendingCommand(const Command& cmd) : cmd_(cmd) { }
111        Command cmd_;
112    };
113
114    // TODO(johngro) : Elminiate this complexity if/when we get to the point
115    // that audio streams have a 1:1 relationship with their clients (instead of
116    // 1:many)
117    struct NotifyTarget : fbl::DoublyLinkedListable<fbl::unique_ptr<NotifyTarget>> {
118        explicit NotifyTarget(fbl::RefPtr<dispatcher::Channel>&& ch) : channel(ch) { }
119        fbl::RefPtr<dispatcher::Channel> channel;
120    };
121    using NotifyTargetList = fbl::DoublyLinkedList<fbl::unique_ptr<NotifyTarget>>;
122
123    // Bits used to track setup state machine progress.
124    static constexpr uint32_t PIN_COMPLEX_SETUP_COMPLETE = (1u << 0);
125    static constexpr uint32_t CONVERTER_SETUP_COMPLETE   = (1u << 1);
126    static constexpr uint32_t PLUG_STATE_SETUP_COMPLETE  = (1u << 2);
127    static constexpr uint32_t DMA_ASSIGNMENT_COMPLETE    = (1u << 3);
128    static constexpr uint32_t STREAM_PUBLISHED           = (1u << 31);
129    static constexpr uint32_t ALL_SETUP_COMPLETE         = PIN_COMPLEX_SETUP_COMPLETE
130                                                         | CONVERTER_SETUP_COMPLETE
131                                                         | PLUG_STATE_SETUP_COMPLETE
132                                                         | DMA_ASSIGNMENT_COMPLETE;
133
134    static uint8_t ComputeGainSteps(const CommonCaps& caps, float target_gai);
135
136    zx_status_t RunCmdLocked(const Command& cmd)
137        __TA_REQUIRES(obj_lock());
138
139    zx_status_t RunCmdListLocked(const Command* list, size_t count, bool force_all = false)
140        __TA_REQUIRES(obj_lock());
141
142    zx_status_t DisableConverterLocked(bool force_all = false) __TA_REQUIRES(obj_lock());
143    zx_status_t UpdateConverterGainLocked(float target_gain) __TA_REQUIRES(obj_lock());
144    float       ComputeCurrentGainLocked() __TA_REQUIRES(obj_lock());
145    zx_status_t SendGainUpdatesLocked() __TA_REQUIRES(obj_lock());
146    void        AddPDNotificationTgtLocked(dispatcher::Channel* channel) __TA_REQUIRES(obj_lock());
147    void        RemovePDNotificationTgtLocked(const dispatcher::Channel& channel)
148        __TA_REQUIRES(obj_lock());
149
150    // Setup state machine methods.
151    zx_status_t UpdateSetupProgressLocked(uint32_t stage) __TA_REQUIRES(obj_lock());
152    zx_status_t FinalizeSetupLocked() __TA_REQUIRES(obj_lock());
153    void DumpStreamPublishedLocked() __TA_REQUIRES(obj_lock());
154    void DumpAmpCaps(const CommonCaps& caps, const char* tag);
155    DECLARE_THUNK(ProcessPinWidgetCaps);
156    DECLARE_THUNK(ProcessPinAmpCaps);
157    DECLARE_THUNK(ProcessPinCfgDefaults);
158    DECLARE_THUNK(ProcessPinCaps);
159    DECLARE_THUNK(ProcessPinState);
160    DECLARE_THUNK(ProcessConverterWidgetCaps);
161    DECLARE_THUNK(ProcessConverterAmpCaps);
162    DECLARE_THUNK(ProcessConverterSampleSizeRate);
163    DECLARE_THUNK(ProcessConverterSampleFormats);
164
165    bool can_mute() const __TA_REQUIRES(obj_lock()) {
166        return (conv_.has_amp && conv_.amp_caps.can_mute()) ||
167               (pc_.has_amp && pc_.amp_caps.can_mute());
168    }
169
170    const StreamProperties props_;
171    fbl::DoublyLinkedList<fbl::unique_ptr<PendingCommand>> pending_cmds_ __TA_GUARDED(obj_lock());
172
173    // Setup state machine progress.
174    uint32_t setup_progress_ __TA_GUARDED(obj_lock()) = 0;
175    bool     format_set_     __TA_GUARDED(obj_lock()) = false;
176
177    // Current gain and plug detect settings.
178    uint8_t   cur_conv_gain_steps_ __TA_GUARDED(obj_lock()) = 0;
179    uint8_t   cur_pc_gain_steps_   __TA_GUARDED(obj_lock()) = 0;
180    bool      cur_mute_            __TA_GUARDED(obj_lock()) = false;
181    bool      plug_state_          __TA_GUARDED(obj_lock()) = true;
182    zx_time_t last_plug_time_      __TA_GUARDED(obj_lock()) = 0;
183    NotifyTargetList plug_notify_targets_ __TA_GUARDED(obj_lock());
184
185    // Converter and pin complex capabilities.
186    ConverterCaps  conv_ __TA_GUARDED(obj_lock());
187    PinComplexCaps pc_   __TA_GUARDED(obj_lock());
188};
189
190#undef DECLARE_THUNK
191
192}  // namespace codecs
193}  // namespace intel_hda
194}  // namespace audio
195
196// TODO(johngro) : Right now, there is no really good way to hide a static slab
197// allocator from the rest of the world.  It is not really much of a concern
198// here, but it seems odd to have a private inner class which can be
199// instantiated by things outside of the class.
200//
201// We should probably either fix static slab allocators so that they can be made
202// private inner classes as well, or just go ahead and make these slab allocated
203// bookkeeping classes non-inner-classes.
204FWD_DECL_STATIC_SLAB_ALLOCATOR(audio::intel_hda::codecs::RealtekStream::PCAT);
205
206