blob: 1666d6551630ebfaf2eff54ced28446ed37d08c6 [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 <lib/zx/handle.h>
#include <lib/zx/vmo.h>
#include <fbl/intrusive_wavl_tree.h>
#include <fbl/ref_counted.h>
#include <fbl/ref_ptr.h>
#include <fbl/unique_ptr.h>
#include <lib/fzl/pinned-vmo.h>
#include <lib/fzl/vmo-mapper.h>
#include <zircon/thread_annotations.h>
#include <audio-proto/audio-proto.h>
#include <dispatcher-pool/dispatcher-channel.h>
#include <intel-hda/utils/intel-hda-registers.h>
#include <intel-hda/utils/utils.h>
#include "debug-logging.h"
#include "utils.h"
namespace audio {
namespace intel_hda {
class IntelHDACodec;
class IntelHDAStream : public fbl::RefCounted<IntelHDAStream>,
public fbl::WAVLTreeContainable<fbl::RefPtr<IntelHDAStream>> {
public:
using RefPtr = fbl::RefPtr<IntelHDAStream>;
using Tree = fbl::WAVLTree<uint16_t, RefPtr>;
enum class Type { INVALID, INPUT, OUTPUT, BIDIR };
// Hardware allows buffer descriptor lists (BDLs) to be up to 256
// entries long.
static constexpr size_t MAX_BDL_LENGTH = 256;
static fbl::RefPtr<IntelHDAStream> Create(
Type type,
uint16_t id,
hda_stream_desc_regs_t* regs,
const fbl::RefPtr<RefCountedBti>& pci_bti);
const char* log_prefix() const { return log_prefix_; }
Type type() const { return type_; }
Type configured_type() const { return configured_type_; }
uint8_t tag() const { return tag_; }
uint16_t id() const { return id_; }
uint16_t dma_id() const {
ZX_DEBUG_ASSERT(id() > 0);
return static_cast<uint16_t>(id() - 1);
}
uint16_t GetKey() const { return id(); }
zx_status_t SetStreamFormat(const fbl::RefPtr<dispatcher::ExecutionDomain>& domain,
uint16_t encoded_fmt,
zx::channel* client_endpoint_out) TA_EXCL(channel_lock_);
void Deactivate() TA_EXCL(channel_lock_);
void ProcessStreamIRQ() TA_EXCL(notif_lock_);
private:
friend class IntelHDAController; // Controllers have access to stuff like Reset and Configure
friend class fbl::RefPtr<IntelHDAStream>; // Only our ref ptrs may destruct us.
IntelHDAStream(Type type,
uint16_t id,
hda_stream_desc_regs_t* regs,
const fbl::RefPtr<RefCountedBti>& pci_bti);
~IntelHDAStream();
zx_status_t Initialize();
void DeactivateLocked() TA_REQ(channel_lock_);
void EnsureStoppedLocked() TA_REQ(channel_lock_) { EnsureStopped(regs_); }
// Client request handlers
zx_status_t ProcessClientRequest(dispatcher::Channel* channel) TA_EXCL(channel_lock_);
void ProcessClientDeactivate(const dispatcher::Channel* channel) TA_EXCL(channel_lock_);
zx_status_t ProcessGetFifoDepthLocked(const audio_proto::RingBufGetFifoDepthReq& req)
TA_REQ(channel_lock_);
zx_status_t ProcessGetBufferLocked(const audio_proto::RingBufGetBufferReq& req)
TA_REQ(channel_lock_);
zx_status_t ProcessStartLocked(const audio_proto::RingBufStartReq& req) TA_REQ(channel_lock_);
zx_status_t ProcessStopLocked(const audio_proto::RingBufStopReq& req) TA_REQ(channel_lock_);
// Release the client ring buffer (if one has been assigned)
void ReleaseRingBufferLocked() TA_REQ(channel_lock_);
// Enter and exit the HW reset state.
//
// TODO(johngro) : leaving streams in reset at all times seems to have
// trouble with locking up the hardware (it becomes completely unresponsive
// to reset, both stream reset and top level reset). One day we should
// figure out why; in the meantime, do not leave streams held in reset for
// any length of time.
void Reset() { Reset(regs_); }
// Called during stream allocation and release to configure the type of
// stream (in the case of a bi-directional stream) and the tag that the
// stream will put into the outbound SDO frames.
void Configure(Type type, uint8_t tag);
// Static helpers which can be used during early initialization
static void EnsureStopped(hda_stream_desc_regs_t* regs);
static void Reset(hda_stream_desc_regs_t* regs);
// Accessor for the CPU accessible view of the Buffer Descriptor List
IntelHDABDLEntry* bdl() const {
return reinterpret_cast<IntelHDABDLEntry*>(bdl_cpu_mem_.start());
}
// Parameters determined construction time.
const Type type_ = Type::INVALID;
const uint16_t id_ = 0;
hda_stream_desc_regs_t* const regs_ = nullptr;
// Parameters determined at allocation time.
Type configured_type_;
uint8_t tag_;
// Log prefix storage
char log_prefix_[LOG_PREFIX_STORAGE] = { 0 };
// A reference to our controller's BTI. We will need to this to grant the
// controller access to the BDLs and the ring buffers that this stream needs
// to operate.
const fbl::RefPtr<RefCountedBti> pci_bti_;
// Storage allocated for this stream context's buffer descriptor list.
fzl::VmoMapper bdl_cpu_mem_;
fzl::PinnedVmo bdl_hda_mem_;
// The channel used by the application to talk to us once our format has
// been set by the codec.
fbl::Mutex channel_lock_;
fbl::RefPtr<dispatcher::Channel> channel_ TA_GUARDED(channel_lock_);
fzl::PinnedVmo pinned_ring_buffer_ TA_GUARDED(channel_lock_);
// Parameters determined after stream format configuration.
uint16_t encoded_fmt_ = 0;
uint16_t fifo_depth_ = 0;
uint32_t bytes_per_frame_ TA_GUARDED(channel_lock_) = 0;
// Parameters determined after ring buffer allocation.
uint32_t cyclic_buffer_length_ TA_GUARDED(channel_lock_) = 0;
uint32_t bdl_last_valid_index_ TA_GUARDED(channel_lock_) = 0;
// Start/stop flag.
bool running_ TA_GUARDED(channel_lock_) = false;
// State used by the IRQ thread to deliver position update notifications.
fbl::Mutex notif_lock_ TA_ACQ_AFTER(channel_lock_);
fbl::RefPtr<dispatcher::Channel> irq_channel_ TA_GUARDED(notif_lock_);
};
} // namespace intel_hda
} // namespace audio