blob: 63b6f36b19e9d937757df28d1219ba0c19453329 [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 <ddk/device.h>
#include <ddk/driver.h>
#include <ddk/protocol/pci.h>
#include <zircon/types.h>
#include <fbl/atomic.h>
#include <fbl/intrusive_single_list.h>
#include <fbl/recycler.h>
#include <fbl/unique_ptr.h>
#include <threads.h>
#include <dispatcher-pool/dispatcher-execution-domain.h>
#include <intel-hda/utils/codec-commands.h>
#include <intel-hda/utils/intel-hda-registers.h>
#include <intel-hda/utils/intel-hda-proto.h>
#include "codec-cmd-job.h"
#include "intel-hda-codec.h"
#include "thread-annotations.h"
#include "utils.h"
namespace audio {
namespace intel_hda {
class IntelHDAController : public fbl::RefCounted<IntelHDAController> {
public:
IntelHDAController();
~IntelHDAController();
zx_status_t Init(zx_device_t* pci_dev);
void PrintDebugPrefix() const;
const char* dev_name() const { return device_get_name(dev_node_); }
zx_device_t* dev_node() { return dev_node_; }
unsigned int id() const { return id_; }
// CORB/RIRB
zx_status_t QueueCodecCmd(fbl::unique_ptr<CodecCmdJob>&& job) TA_EXCL(corb_lock_);
// DMA Streams
fbl::RefPtr<IntelHDAStream> AllocateStream(IntelHDAStream::Type type)
TA_EXCL(stream_pool_lock_);
void ReturnStream(fbl::RefPtr<IntelHDAStream>&& stream)
TA_EXCL(stream_pool_lock_);
static zx_status_t DriverInit(void** out_ctx);
static zx_status_t DriverBind(void* ctx, zx_device_t* device);
static void DriverRelease(void* ctx);
private:
using StateStorage = uint32_t;
enum class State : StateStorage {
STARTING,
OPERATING,
SHUTTING_DOWN,
SHUT_DOWN,
};
static constexpr uint RIRB_RESERVED_RESPONSE_SLOTS = 8u;
int IRQThread();
void WakeupIRQThread();
void ShutdownIRQThread();
// Internal stream bookkeeping.
void ReturnStreamLocked(fbl::RefPtr<IntelHDAStream>&& stream) TA_REQ (stream_pool_lock_);
uint8_t AllocateStreamTagLocked(bool input) TA_REQ (stream_pool_lock_);
void ReleaseStreamTagLocked (bool input, uint8_t tag_num) TA_REQ (stream_pool_lock_);
// Device interface implementation
void DeviceShutdown();
void DeviceRelease();
zx_status_t DeviceIoctl(uint32_t op, void* out_buf, size_t out_len, size_t* out_actual);
// Root device interface implementation
void RootDeviceRelease();
// State control
// TODO(johngro) : extend fbl::atomic to support enum classes as well.
void SetState(State state) { state_.store(static_cast<StateStorage>(state)); }
State GetState() { return static_cast<State>(state_.load()); }
// Codec lifetime maanagement
fbl::RefPtr<IntelHDACodec> GetCodec(uint id);
// Methods used during initialization
zx_status_t InitInternal(zx_device_t* pci_dev);
zx_status_t ResetControllerHW();
zx_status_t SetupPCIDevice(zx_device_t* pci_dev);
zx_status_t SetupPCIInterrupts();
zx_status_t SetupStreamDescriptors() TA_EXCL(stream_pool_lock_);
zx_status_t SetupCommandBufferSize(uint8_t* size_reg, unsigned int* entry_count);
zx_status_t SetupCommandBuffer() TA_EXCL(corb_lock_, rirb_lock_);
void WaitForIrqOrWakeup();
zx_status_t ResetCORBRdPtrLocked() TA_REQ(corb_lock_);
void SnapshotRIRB() TA_EXCL(corb_lock_, rirb_lock_);
void ProcessRIRB() TA_EXCL(corb_lock_, rirb_lock_);
void ProcessCORB() TA_EXCL(corb_lock_, rirb_lock_);
void ComputeCORBSpaceLocked() TA_REQ(corb_lock_);
void CommitCORBLocked() TA_REQ(corb_lock_);
void SendCodecCmdLocked(CodecCommand cmd) TA_REQ(corb_lock_);
void ProcessStreamIRQ(uint32_t intsts);
void ProcessControllerIRQ();
// Thunk for interacting with client channels
zx_status_t ProcessClientRequest(dispatcher::Channel* channel);
zx_status_t SnapshotRegs(dispatcher::Channel* channel,
const ihda_controller_snapshot_regs_req_t& req);
// Dispatcher framework state
fbl::RefPtr<dispatcher::ExecutionDomain> default_domain_;
// IRQ thread and state machine.
fbl::atomic<StateStorage> state_;
thrd_t irq_thread_;
bool irq_thread_started_ = false;
// Debug stuff
char debug_tag_[ZX_DEVICE_NAME_MAX] = { 0 };
// Upstream PCI device, protocol interface, and device info.
zx_device_t* pci_dev_ = nullptr;
pci_protocol_t pci_ = { nullptr, nullptr };
zx_pcie_device_info_t pci_dev_info_;
static zx_protocol_device_t ROOT_DEVICE_THUNKS;
// Unique ID and published HDA device node.
const uint32_t id_;
zx_device_t* dev_node_ = nullptr;
// PCI Registers and IRQ
zx_handle_t irq_handle_ = ZX_HANDLE_INVALID;
bool msi_irq_ = false;
zx_handle_t regs_handle_ = ZX_HANDLE_INVALID;
hda_registers_t* regs_ = nullptr;
// Contiguous physical memory allocated for the command buffer (CORB/RIRB)
// and Stream Buffer Desctiptor Lists (BDLs)
ContigPhysMem bdl_mem_ TA_GUARDED(stream_pool_lock_);
ContigPhysMem cmd_buf_mem_ TA_GUARDED(corb_lock_);
// Stream state
fbl::Mutex stream_pool_lock_;
IntelHDAStream::Tree free_input_streams_ TA_GUARDED(stream_pool_lock_);
IntelHDAStream::Tree free_output_streams_ TA_GUARDED(stream_pool_lock_);
IntelHDAStream::Tree free_bidir_streams_ TA_GUARDED(stream_pool_lock_);
uint16_t free_input_tags_ TA_GUARDED(stream_pool_lock_) = 0xFFFEu;
uint16_t free_output_tags_ TA_GUARDED(stream_pool_lock_) = 0xFFFEu;
// Array of pointers to all possible streams (used for O(1) lookup during IRQ dispatch)
fbl::RefPtr<IntelHDAStream> all_streams_[IntelHDAStream::MAX_STREAMS_PER_CONTROLLER];
// Codec bus command ring-buffer state (CORB/RIRB)
fbl::Mutex corb_lock_;
CodecCommand* corb_ TA_GUARDED(corb_lock_) = nullptr;
unsigned int corb_entry_count_ TA_GUARDED(corb_lock_) = 0;
unsigned int corb_mask_ TA_GUARDED(corb_lock_) = 0;
unsigned int corb_wr_ptr_ TA_GUARDED(corb_lock_) = 0;
unsigned int corb_space_ TA_GUARDED(corb_lock_) = 0;
unsigned int corb_max_in_flight_ TA_GUARDED(corb_lock_) = 0;
fbl::Mutex rirb_lock_ TA_ACQ_BEFORE(corb_lock_);
CodecResponse* rirb_ TA_GUARDED(rirb_lock_) = nullptr;
unsigned int rirb_entry_count_ TA_GUARDED(rirb_lock_) = 0;
unsigned int rirb_mask_ TA_GUARDED(rirb_lock_) = 0;
unsigned int rirb_rd_ptr_ TA_GUARDED(rirb_lock_) = 0;
unsigned int rirb_snapshot_cnt_ TA_GUARDED(rirb_lock_) = 0;
CodecResponse rirb_snapshot_[HDA_RIRB_MAX_ENTRIES] TA_GUARDED(rirb_lock_);
fbl::DoublyLinkedList<fbl::unique_ptr<CodecCmdJob>> in_flight_corb_jobs_
TA_GUARDED(corb_lock_);
fbl::DoublyLinkedList<fbl::unique_ptr<CodecCmdJob>> pending_corb_jobs_
TA_GUARDED(corb_lock_);
fbl::Mutex codec_lock_;
fbl::RefPtr<IntelHDACodec> codecs_[HDA_MAX_CODECS];
static fbl::atomic_uint32_t device_id_gen_;
static zx_protocol_device_t CONTROLLER_DEVICE_THUNKS;
};
} // namespace intel_hda
} // namespace audio