388 lines
13 KiB
C
388 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;
|
|
};
|
|
|
|
// 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);
|