blob: cb8b9dd707b2f518078785717a74bf929bd4a390 [file] [log] [blame]
// 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 <fbl/intrusive_double_list.h>
#include <fbl/ref_ptr.h>
#include <fbl/slab_allocator.h>
#include <intel-hda/codec-utils/stream-base.h>
#include <intel-hda/utils/codec-caps.h>
#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<RealtekStream>;
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<fbl::unique_ptr<PendingCommand>, 4096>;
using PendingCommandAllocator = fbl::SlabAllocator<PCAT>;
friend PendingCommandAllocator;
class PendingCommand : public fbl::DoublyLinkedListable<fbl::unique_ptr<PendingCommand>>,
public fbl::SlabAllocated<PCAT> {
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<fbl::unique_ptr<NotifyTarget>> {
explicit NotifyTarget(fbl::RefPtr<dispatcher::Channel>&& ch) : channel(ch) { }
fbl::RefPtr<dispatcher::Channel> channel;
};
using NotifyTargetList = fbl::DoublyLinkedList<fbl::unique_ptr<NotifyTarget>>;
// 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<fbl::unique_ptr<PendingCommand>> 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);