Files
nt-lite/code/drivers/audio/hda/hda.h
T

390 lines
13 KiB
C

// Intel High Definition Audio transport declarations shared by the controller and stream implementation.
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <ntlite/ddi/io_driver.h>
#include <ntlite/ddi/pci.h>
#include <ntlite/ddk/ddk.h>
#define HDA_CONTROLLER_MAGIC 0x4144484Eu
#define HDA_MAX_CONTROLLERS 8u
#define HDA_MAX_CODECS 15u
#define HDA_MAX_NODE_ID 0xFFu
#define HDA_MAX_UNSOLICITED_RESPONSES 32u
#define HDA_OUTPUT_SAMPLE_RATE 48000u
#define HDA_OUTPUT_CHANNELS 2u
#define HDA_OUTPUT_VALID_BITS 16u
#define HDA_OUTPUT_BYTES_PER_SAMPLE 2u
#define HDA_OUTPUT_BYTES_PER_FRAME 4u
#define HDA_OUTPUT_PERIOD_FRAMES 512u
#define HDA_OUTPUT_PERIOD_COUNT 4u
#define HDA_OUTPUT_PERIOD_BYTES (HDA_OUTPUT_PERIOD_FRAMES * HDA_OUTPUT_BYTES_PER_FRAME)
#define HDA_OUTPUT_BUFFER_BYTES (HDA_OUTPUT_PERIOD_COUNT * HDA_OUTPUT_PERIOD_BYTES)
#define HDA_OUTPUT_STREAM_FORMAT 0x0011u
#define HDA_OUTPUT_STREAM_TAG 1u
#define HDA_MAX_LOGICAL_ENDPOINTS 3u
#define HDA_REG_GCAP 0x00u
#define HDA_REG_VMIN 0x02u
#define HDA_REG_VMAJ 0x03u
#define HDA_REG_GCTL 0x08u
#define HDA_REG_STATESTS 0x0Eu
#define HDA_REG_INTCTL 0x20u
#define HDA_REG_INTSTS 0x24u
#define HDA_REG_WALLCLK 0x30u
#define HDA_REG_CORBLBASE 0x40u
#define HDA_REG_CORBUBASE 0x44u
#define HDA_REG_CORBWP 0x48u
#define HDA_REG_CORBRP 0x4Au
#define HDA_REG_CORBCTL 0x4Cu
#define HDA_REG_CORBSTS 0x4Du
#define HDA_REG_CORBSIZE 0x4Eu
#define HDA_REG_RIRBLBASE 0x50u
#define HDA_REG_RIRBUBASE 0x54u
#define HDA_REG_RIRBWP 0x58u
#define HDA_REG_RINTCNT 0x5Au
#define HDA_REG_RIRBCTL 0x5Cu
#define HDA_REG_RIRBSTS 0x5Du
#define HDA_REG_RIRBSIZE 0x5Eu
#define HDA_REG_DPLBASE 0x70u
#define HDA_REG_DPUBASE 0x74u
#define HDA_REG_STREAM_BASE 0x80u
#define HDA_REG_STREAM_STRIDE 0x20u
#define HDA_GCAP_64OK 0x0001u
#define HDA_GCAP_BSS_MASK 0x00F8u
#define HDA_GCAP_BSS_SHIFT 3u
#define HDA_GCAP_ISS_MASK 0x0F00u
#define HDA_GCAP_ISS_SHIFT 8u
#define HDA_GCAP_OSS_MASK 0xF000u
#define HDA_GCAP_OSS_SHIFT 12u
#define HDA_GCTL_RESET 0x00000001u
#define HDA_GCTL_UNSOLICITED_ENABLE 0x00000100u
#define HDA_INT_CONTROLLER_ENABLE 0x40000000u
#define HDA_INT_GLOBAL_ENABLE 0x80000000u
#define HDA_INT_CONTROLLER_STATUS 0x40000000u
#define HDA_INT_GLOBAL_STATUS 0x80000000u
#define HDA_CORBRP_POINTER_MASK 0x00FFu
#define HDA_CORBRP_RESET 0x8000u
#define HDA_CORBCTL_MEMORY_ERROR_INTERRUPT 0x01u
#define HDA_CORBCTL_RUN 0x02u
#define HDA_CORBSTS_MEMORY_ERROR 0x01u
#define HDA_RIRBWP_RESET 0x8000u
#define HDA_RIRBCTL_RESPONSE_INTERRUPT 0x01u
#define HDA_RIRBCTL_RUN 0x02u
#define HDA_RIRBCTL_OVERRUN_INTERRUPT 0x04u
#define HDA_RIRBSTS_RESPONSE 0x01u
#define HDA_RIRBSTS_OVERRUN 0x04u
#define HDA_RIRB_RESPONSE_CODEC_MASK 0x0000000Fu
#define HDA_RIRB_RESPONSE_UNSOLICITED 0x00000010u
#define HDA_RING_SIZE_CAP_2 0x10u
#define HDA_RING_SIZE_CAP_16 0x20u
#define HDA_RING_SIZE_CAP_256 0x40u
#define HDA_RING_SIZE_SELECT_2 0x00u
#define HDA_RING_SIZE_SELECT_16 0x01u
#define HDA_RING_SIZE_SELECT_256 0x02u
#define HDA_DPLBASE_ENABLE 0x00000001u
#define HDA_STREAM_REG_CTL 0x00u
#define HDA_STREAM_REG_STS 0x03u
#define HDA_STREAM_REG_LPIB 0x04u
#define HDA_STREAM_REG_CBL 0x08u
#define HDA_STREAM_REG_LVI 0x0Cu
#define HDA_STREAM_REG_FORMAT 0x12u
#define HDA_STREAM_REG_BDLPL 0x18u
#define HDA_STREAM_REG_BDLPU 0x1Cu
#define HDA_STREAM_CTL_RESET 0x000001u
#define HDA_STREAM_CTL_RUN 0x000002u
#define HDA_STREAM_CTL_INTERRUPT_COMPLETE 0x000004u
#define HDA_STREAM_CTL_INTERRUPT_FIFO_ERROR 0x000008u
#define HDA_STREAM_CTL_INTERRUPT_DESCRIPTOR_ERROR 0x000010u
#define HDA_STREAM_CTL_INTERRUPT_MASK 0x00001Cu
#define HDA_STREAM_CTL_TAG_SHIFT 20u
#define HDA_STREAM_CTL_TAG_MASK 0x00F00000u
#define HDA_STREAM_STS_COMPLETE 0x04u
#define HDA_STREAM_STS_FIFO_ERROR 0x08u
#define HDA_STREAM_STS_DESCRIPTOR_ERROR 0x10u
#define HDA_STREAM_STS_ERROR_MASK 0x18u
#define HDA_STREAM_STS_INTERRUPT_MASK 0x1Cu
#define HDA_BDL_FLAG_INTERRUPT_ON_COMPLETION 0x00000001u
#define HDA_VERB_GET_PARAMETER(parameterId) (0xF0000u | ((uint32_t)(parameterId) & 0xFFu))
#define HDA_PARAMETER_VENDOR_ID 0x00u
#define HDA_CONTROLLER_RESET_TIMEOUT_US 100000u
#define HDA_STREAM_RESET_TIMEOUT_US 10000u
#define HDA_RING_RESET_TIMEOUT_US 10000u
#define HDA_CODEC_COMMAND_TIMEOUT_US 100000u
#define HDA_CODEC_COMMAND_ATTEMPTS 2u
#define HDA_CODEC_RESET_SETTLE_US 1000u
#define HDA_STREAM_ERROR_WINDOW_US 1000000ull
#define HDA_STREAM_ERRORS_BEFORE_CONTROLLER_RESET 3u
#define HDA_RECOVERY_NONE 0u
#define HDA_RECOVERY_STREAM 1u
#define HDA_RECOVERY_CONTROLLER 2u
#pragma pack(push, 1)
typedef struct HDA_BDL_ENTRY {
uint64_t Address;
uint32_t Length;
uint32_t Flags;
} HDA_BDL_ENTRY;
typedef struct HDA_RIRB_ENTRY {
uint32_t Response;
uint32_t ResponseExtra;
} HDA_RIRB_ENTRY;
#pragma pack(pop)
_Static_assert(sizeof(HDA_BDL_ENTRY) == 16u, "HDA BDL entries must be 16 bytes");
_Static_assert(sizeof(HDA_RIRB_ENTRY) == 8u, "HDA RIRB entries must be 8 bytes");
typedef struct HDA_CONTROLLER HDA_CONTROLLER;
typedef struct HDA_CODEC_STATE HDA_CODEC_STATE;
typedef struct HDA_OUTPUT_DESCRIPTION {
void* PeriodBuffer;
size_t PeriodBufferBytes;
uint32_t PeriodCount;
uint32_t FramesPerPeriod;
size_t PeriodStrideBytes;
IO_AUDIO_FORMAT HardwareFormat;
uint8_t StreamTag;
} HDA_OUTPUT_DESCRIPTION;
typedef struct HDA_TRANSPORT_CALLBACKS {
// Reports one BDL-owned period after completion interrupt processing.
void (*PeriodComplete)(void* context,
uint32_t completedPeriodIndex,
uint64_t presentedFrames,
uint64_t finePositionFrames,
uint64_t interruptCount,
uint64_t timestampTicks);
// Delivers one unsolicited codec response outside the controller lock.
void (*UnsolicitedResponse)(void* context,
uint8_t codecAddress,
uint32_t response,
uint32_t responseExtra);
// Restores codec programming in a muted state while transport start and stop are serialized.
KSTATUS (*RestoreAfterControllerReset)(void* context);
// Unmutes the restored route only after controller recovery has restarted DMA.
KSTATUS (*ResumeAfterControllerReset)(void* context);
// Reports a recovery failure that leaves the transport stopped.
void (*TransportFailure)(void* context, KSTATUS status);
} HDA_TRANSPORT_CALLBACKS;
typedef struct HDA_UNSOLICITED_RESPONSE {
uint32_t Response;
uint32_t ResponseExtra;
} HDA_UNSOLICITED_RESPONSE;
typedef struct HDA_OUTPUT_RING {
IO_DMA_BUFFER* Dma;
uint8_t* Buffer;
uint64_t DeviceAddress;
uint8_t NextLogicalPeriod;
uint64_t PresentedFrames;
} HDA_OUTPUT_RING;
typedef struct HDA_OUTPUT_STREAM {
uint8_t DescriptorIndex;
uint8_t StreamTag;
uint8_t BdlPeriodMap[HDA_OUTPUT_PERIOD_COUNT];
uint8_t NextCompletionBdl;
bool Prepared;
bool Running;
volatile int32_t PendingPeriodInterrupts;
volatile int32_t PendingDmaErrors;
HDA_OUTPUT_RING Rings[HDA_MAX_LOGICAL_ENDPOINTS];
uint8_t ActiveRingIndex;
IO_DMA_BUFFER* BdlDma;
HDA_BDL_ENTRY* Bdl;
uint64_t BdlDeviceAddress;
uint64_t PositionRunBaseFrames;
uint64_t LastPositionFrames;
uint64_t PositionWrapFrames;
uint32_t LastHardwarePositionBytes;
bool PositionInitialized;
uint64_t InterruptCount;
uint64_t PeriodCompletionCount;
uint64_t DmaErrorCount;
uint64_t StreamResetCount;
uint64_t PositionBufferReadCount;
uint64_t LpibReadCount;
uint64_t PositionDiscontinuityCount;
} HDA_OUTPUT_STREAM;
struct HDA_CONTROLLER {
uint32_t Magic;
DEVICE_OBJECT* Fdo;
DEVICE_OBJECT* Pdo;
DEVICE_OBJECT* LowerDevice;
PCI_DEVICE_INFO PciInfo;
volatile uint8_t* Registers;
uint64_t RegisterBytes;
uint16_t Gcap;
uint8_t InputStreamCount;
uint8_t OutputStreamCount;
uint8_t BidirectionalStreamCount;
uint8_t TotalStreamCount;
uint16_t CodecMask;
uint8_t Irq;
bool UsingMsi;
bool Started;
bool Removed;
bool SurpriseRemoved;
SPINLOCK StateLock;
KDPC Dpc;
volatile int32_t IsrActive;
volatile int32_t DpcActive;
IO_DMA_ADAPTER* DmaAdapter;
IO_DMA_BUFFER* CorbDma;
IO_DMA_BUFFER* RirbDma;
IO_DMA_BUFFER* PositionDma;
uint32_t* Corb;
HDA_RIRB_ENTRY* Rirb;
volatile uint64_t* PositionBuffer;
uint64_t CorbDeviceAddress;
uint64_t RirbDeviceAddress;
uint64_t PositionDeviceAddress;
uint16_t CorbEntryCount;
uint16_t RirbEntryCount;
uint16_t CorbWritePointer;
uint16_t RirbReadPointer;
MUTEX* CommandMutex;
MUTEX* StreamMutex;
bool CommandWaiting;
bool CommandResponseReady;
uint8_t CommandCodecAddress;
uint32_t CommandResponse;
uint32_t CommandResponseExtra;
uint64_t CodecCommandCount;
uint64_t CodecCommandRetryCount;
uint64_t CodecCommandFailureCount;
uint64_t RirbResponseCount;
uint64_t RirbUnsolicitedCount;
uint64_t RirbOverrunCount;
uint64_t CorbMemoryErrorCount;
HDA_UNSOLICITED_RESPONSE Unsolicited[HDA_MAX_UNSOLICITED_RESPONSES];
uint8_t UnsolicitedRead;
uint8_t UnsolicitedWrite;
uint8_t UnsolicitedCount;
HDA_OUTPUT_STREAM Output;
HDA_TRANSPORT_CALLBACKS Callbacks;
void* CallbackContext;
SEMAPHORE* RecoverySemaphore;
EVENT* RecoveryFinished;
THREAD* RecoveryThread;
bool RecoveryWorkerRunning;
bool RecoveryInProgress;
uint8_t PendingRecovery;
uint64_t StreamErrorWindowStart;
uint32_t StreamErrorsInWindow;
uint64_t ControllerResetCount;
HDA_CODEC_STATE* Codec;
uint64_t InterruptCount;
uint64_t ControllerInterruptCount;
};
extern const NTL_DDK* gHdaDdk;
// Returns one byte from the controller register aperture.
uint8_t HdaRead8(const HDA_CONTROLLER* controller, uint32_t offset);
// Returns one word from the controller register aperture.
uint16_t HdaRead16(const HDA_CONTROLLER* controller, uint32_t offset);
// Returns one dword from the controller register aperture.
uint32_t HdaRead32(const HDA_CONTROLLER* controller, uint32_t offset);
// Writes one byte to the controller register aperture.
void HdaWrite8(HDA_CONTROLLER* controller, uint32_t offset, uint8_t value);
// Writes one word to the controller register aperture.
void HdaWrite16(HDA_CONTROLLER* controller, uint32_t offset, uint16_t value);
// Writes one dword to the controller register aperture.
void HdaWrite32(HDA_CONTROLLER* controller, uint32_t offset, uint32_t value);
// Returns the register offset of the controller's one output stream descriptor.
uint32_t HdaOutputStreamBase(const HDA_CONTROLLER* controller);
// Allocates the transport and output DMA resources for one controller.
KSTATUS HdaAllocateDma(HDA_CONTROLLER* controller);
// Releases all DMA and synchronization resources owned by one controller.
void HdaReleaseDma(HDA_CONTROLLER* controller);
// Resets and initializes the controller, command rings, position buffer, and output descriptor.
KSTATUS HdaInitializeController(HDA_CONTROLLER* controller);
// Arms controller and output-stream interrupts after ISR registration.
void HdaArmInterrupts(HDA_CONTROLLER* controller);
// Stops hardware access without releasing software resources.
void HdaQuiesceController(HDA_CONTROLLER* controller);
// Sends one 20-bit codec verb payload through CORB/RIRB with one retry.
KSTATUS HdaCodecVerb(HDA_CONTROLLER* controller,
uint8_t codecAddress,
uint8_t nodeId,
uint32_t verbPayload,
uint32_t* responseOut);
// Copies one logical endpoint ring contract for codec and endpoint setup.
KSTATUS HdaDescribeOutput(HDA_CONTROLLER* controller,
uint8_t ringIndex,
HDA_OUTPUT_DESCRIPTION* descriptionOut);
// Selects one logical endpoint ring while the stream is stopped.
KSTATUS HdaSelectOutputRing(HDA_CONTROLLER* controller, uint8_t ringIndex);
// Replaces the transport callbacks while the output stream is stopped.
KSTATUS HdaSetTransportCallbacks(HDA_CONTROLLER* controller,
const HDA_TRANSPORT_CALLBACKS* callbacks,
void* context);
// Starts the prepared fixed-format output stream.
KSTATUS HdaStartOutput(HDA_CONTROLLER* controller);
// Stops the output stream while retaining its DMA ring.
void HdaStopOutput(HDA_CONTROLLER* controller);
// Returns the monotonic fine position derived from the position buffer or LPIB.
uint64_t HdaQueryOutputPositionFrames(HDA_CONTROLLER* controller);
// Captures one controller interrupt and queues deferred processing.
void HdaHandleInterrupt(HDA_CONTROLLER* controller);
// Runs period delivery, unsolicited callbacks, and recovery scheduling at DPC level.
void HdaDpcRoutine(KDPC* dpc, void* context, void* systemArgument1, void* systemArgument2);
// Starts the passive recovery worker for one controller.
KSTATUS HdaStartRecoveryWorker(HDA_CONTROLLER* controller);
// Stops and joins the passive recovery worker for one controller.
void HdaStopRecoveryWorker(HDA_CONTROLLER* controller);
// Discovers codec topology and registers usable analog render endpoints.
KSTATUS HdaCodecInitialize(HDA_CONTROLLER* controller);
// Stops jack workers and unregisters codec endpoints.
void HdaCodecStop(HDA_CONTROLLER* controller, IO_AUDIO_REMOVAL_MODE mode);
// Releases codec topology storage after interrupt work is quiescent.
void HdaCodecDestroy(HDA_CONTROLLER* controller);