blob: 84c9e0d5b0b9349e54776072478b5296c1b8bcd0 [file] [log] [blame]
// Copyright 2018 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/binding.h>
#include <ddk/device.h>
#include <ddk/protocol/intelhda/codec.h>
#include <ddk/protocol/intelhda/dsp.h>
#include <fbl/mutex.h>
#include <lib/fzl/vmo-mapper.h>
#include <limits.h>
#include <stdint.h>
#include <string.h>
#include <threads.h>
#include <dispatcher-pool/dispatcher-channel.h>
#include <intel-hda/codec-utils/codec-driver-base.h>
#include <intel-hda/utils/intel-audio-dsp-ipc.h>
#include <intel-hda/utils/intel-hda-registers.h>
#include <intel-hda/utils/nhlt.h>
#include <intel-hda/utils/utils.h>
#include "debug-logging.h"
#include "intel-dsp-ipc.h"
#include "intel-dsp-stream.h"
#include "intel-dsp-topology.h"
namespace audio {
namespace intel_hda {
class IntelAudioDsp : public codecs::IntelHDACodecDriverBase {
public:
static fbl::RefPtr<IntelAudioDsp> Create();
const char* log_prefix() const { return log_prefix_; }
// Mailbox constants
static constexpr size_t MAILBOX_SIZE = 0x1000;
// IPC helper methods
void SendIpcMessage(const IpcMessage& message) {
// HIPCIE must be programmed before setting HIPCI.BUSY
REG_WR(&regs()->hipcie, message.extension);
REG_WR(&regs()->hipci, message.primary | ADSP_REG_HIPCI_BUSY);
}
void IpcMailboxWrite(const void* data, size_t size) {
mailbox_out_.Write(data, size);
}
void IpcMailboxRead(void* data, size_t size) {
mailbox_in_.Read(data, size);
}
zx_status_t StartPipeline(const DspPipeline& pipeline);
zx_status_t PausePipeline(const DspPipeline& pipeline);
zx_status_t DriverBind(zx_device_t* hda_dev) __WARN_UNUSED_RESULT;
void DeviceShutdown();
zx_status_t Suspend(uint32_t flags) override final;
private:
friend class fbl::RefPtr<IntelAudioDsp>;
IntelAudioDsp();
~IntelAudioDsp() { }
// Accessor for our mapped registers
adsp_registers_t* regs() const {
return reinterpret_cast<adsp_registers_t*>(mapped_regs_.start());
}
adsp_fw_registers_t* fw_regs() const;
zx_status_t SetupDspDevice();
zx_status_t ParseNhlt();
int InitThread();
zx_status_t Boot();
zx_status_t StripFirmware(const zx::vmo& fw, void* out, size_t* size_inout);
zx_status_t LoadFirmware();
zx_status_t GetI2SBlob(uint8_t bus_id, uint8_t direction, const AudioDataFormat& format,
const void** out_blob, size_t* out_size);
zx_status_t GetModulesInfo();
zx_status_t CreateHostDmaModule(uint8_t instance_id, uint8_t pipeline_id,
const CopierCfg& cfg);
zx_status_t CreateI2SModule(uint8_t instance_id, uint8_t pipeline_id,
uint8_t i2s_instance_id, uint8_t direction,
const CopierCfg& cfg);
zx_status_t CreateMixinModule(uint8_t instance_id, uint8_t pipeline_id,
const BaseModuleCfg& cfg);
zx_status_t CreateMixoutModule(uint8_t instance_id, uint8_t pipeline_id,
const BaseModuleCfg& cfg);
zx_status_t SetupPipelines();
zx_status_t RunPipeline(uint8_t pipeline_id);
bool IsCoreEnabled(uint8_t core_mask);
zx_status_t ResetCore(uint8_t core_mask);
zx_status_t UnResetCore(uint8_t core_mask);
zx_status_t PowerDownCore(uint8_t core_mask);
zx_status_t PowerUpCore(uint8_t core_mask);
void RunCore(uint8_t core_mask);
void EnableInterrupts();
// Interrupt handler.
void ProcessIrq();
zx_status_t CreateAndStartStreams();
// Debug
void DumpRegs();
void DumpNhlt(const nhlt_table_t* nhlt, size_t length);
void DumpFirmwareConfig(const TLVHeader* config, size_t length);
void DumpHardwareConfig(const TLVHeader* config, size_t length);
void DumpModulesInfo(const ModuleEntry* info, uint32_t count);
void DumpPipelineListInfo(const PipelineListInfo* info);
void DumpPipelineProps(const PipelineProps* props);
enum class State : uint8_t {
START,
INITIALIZING, // init thread running
OPERATING,
SHUT_DOWN,
ERROR = 0xFF,
};
State state_ = State::START;
// IPC
IntelDspIpc ipc_;
// IPC Mailboxes
class Mailbox {
public:
void Initialize(void* base, size_t size) {
base_ = base;
size_ = size;
}
size_t size() const { return size_; }
void Write(const void* data, size_t size) {
// It is the caller's responsibility to ensure size fits in the mailbox.
ZX_DEBUG_ASSERT(size <= size_);
memcpy(base_, data, size);
}
void Read(void* data, size_t size) {
// It is the caller's responsibility to ensure size fits in the mailbox.
ZX_DEBUG_ASSERT(size <= size_);
memcpy(data, base_, size);
}
private:
void* base_;
size_t size_;
};
Mailbox mailbox_in_;
Mailbox mailbox_out_;
// NHLT buffer.
uint8_t nhlt_buf_[PAGE_SIZE];
// I2S config
struct I2SConfig {
I2SConfig(uint8_t bid, uint8_t dir, const formats_config_t* f)
: valid(true), bus_id(bid), direction(dir), formats(f) { }
I2SConfig() = default;
bool valid = false;
uint8_t bus_id = 0;
uint8_t direction = 0;
const formats_config_t* formats = nullptr;
};
static constexpr size_t I2S_CONFIG_MAX = 8;
I2SConfig i2s_configs_[I2S_CONFIG_MAX];
// Module IDs
enum Module {
COPIER,
MIXIN,
MIXOUT,
MODULE_COUNT,
};
static constexpr uint16_t MODULE_ID_INVALID = 0xFFFF;
uint16_t module_ids_[to_underlying(Module::MODULE_COUNT)];
// Init thread
thrd_t init_thread_;
// Log prefix storage
char log_prefix_[LOG_PREFIX_STORAGE] = { 0 };
// Upstream HDA DSP protocol interface.
ihda_dsp_protocol_t ihda_dsp_;
// PCI registers
fzl::VmoMapper mapped_regs_;
// A reference to our controller's BTI. This is needed to load firmware to the DSP.
fbl::RefPtr<RefCountedBti> hda_bti_;
};
} // namespace intel_hda
} // namespace audio