[HDAUDBUS_NEW] Import new SkyLake/KabyLake/ApolloLake HD audio bus driver from https://github.com/coolstar/sklhdaudbus/tree/master (BSD-3-Clause License)

[DOC] 3rd Party Files.txt: Add 'HD Audio Driver for Windows 10 / 11 with Skylake -> Raptor Lake DSP support'
CORE-15350, CORE-18776
This commit is contained in:
Oleg Dubinskiy
2025-11-08 18:42:00 +01:00
committed by Oleg Dubinskiy
parent 4e72ec8358
commit 7c2d923007
28 changed files with 6098 additions and 0 deletions

View File

@@ -0,0 +1,11 @@
Copyright 2024 CoolStar
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,14 @@
HD Audio Bus Driver for Intel Skylake and newer
* Exposes HD Audio devices
* Compatible with VMWare, QEMU, and Intel Skylake / Kaby Lake / Apollo Lake / Gemini Lake / Comet Lake / Tiger Lake / Jasper Lake / Alder Lake(-N) / Raptor Lake
* Compatible with Windows default codec driver (QEMU / VMWare)
* Compatible with Intel Display Audio driver (Intel Lakes, and Intel Haswell / Broadwell)
* Compatible with Realtek ALC283 driver (Intel Haswell / Broadwell)
* Compatible with NVIDIA RTX 30-40 Series (HDMI Audio)
* Compatible with Asus TUF Gaming X570 Plus (AMD Ryzen 5000)
* Compatible with Asus ROG Strix B850-A Gaming Wifi (AMD Ryzen 9000)
* Support accessing AudioDSP on Intel platforms
* Compatible with csaudiosstavs and csaudiointcsof
* Working Sleep / Wake
* Compatible with Windows 10 / 11 64-bit

View File

@@ -0,0 +1,398 @@
#include "driver.h"
#define ADSP_DECL 1
#include "adsp.h"
NTSTATUS ADSPGetResources(_In_ PVOID _context, _PCI_BAR* hdaBar, _PCI_BAR* adspBar, PVOID *ppcap, PNHLT_INFO nhltInfo, BUS_INTERFACE_STANDARD* pciConfig) {
if (!_context)
return STATUS_NO_SUCH_DEVICE;
PPDO_DEVICE_DATA devData = (PPDO_DEVICE_DATA)_context;
if (!devData->FdoContext) {
return STATUS_NO_SUCH_DEVICE;
}
if (hdaBar) {
*hdaBar = devData->FdoContext->m_BAR0;
}
if (adspBar) {
*adspBar = devData->FdoContext->m_BAR4;
}
if (ppcap) {
*ppcap = devData->FdoContext->ppcap;
}
if (nhltInfo) {
if (devData->FdoContext->nhlt) {
nhltInfo->nhlt = devData->FdoContext->nhlt;
nhltInfo->nhltSz = devData->FdoContext->nhltSz;
}
else if (devData->FdoContext->sofTplg) {
nhltInfo->nhlt = devData->FdoContext->sofTplg;
nhltInfo->nhltSz = devData->FdoContext->sofTplgSz;
}
}
if (pciConfig) {
*pciConfig = devData->FdoContext->BusInterface;
}
return STATUS_SUCCESS;
}
NTSTATUS ADSPSetPowerState(_In_ PVOID _context, _In_ DEVICE_POWER_STATE powerState) {
if (!_context)
return STATUS_NO_SUCH_DEVICE;
PPDO_DEVICE_DATA devData = (PPDO_DEVICE_DATA)_context;
if (!devData->FdoContext) {
return STATUS_NO_SUCH_DEVICE;
}
NTSTATUS status = STATUS_SUCCESS;
if (powerState == PowerDeviceD3) {
WdfDeviceResumeIdle(devData->FdoContext->WdfDevice);
} else if (powerState == PowerDeviceD0) {
status = WdfDeviceStopIdle(devData->FdoContext->WdfDevice, TRUE);
}
return status;
}
NTSTATUS ADSPRegisterInterrupt(_In_ PVOID _context, _In_ PADSP_INTERRUPT_CALLBACK callback, _In_ PVOID callbackContext) {
if (!_context)
return STATUS_NO_SUCH_DEVICE;
PPDO_DEVICE_DATA devData = (PPDO_DEVICE_DATA)_context;
if (!devData->FdoContext) {
return STATUS_NO_SUCH_DEVICE;
}
devData->FdoContext->dspInterruptCallback = callback;
devData->FdoContext->dspInterruptContext = callbackContext;
return STATUS_SUCCESS;
}
NTSTATUS ADSPUnregisterInterrupt(_In_ PVOID _context) {
if (!_context)
return STATUS_NO_SUCH_DEVICE;
PPDO_DEVICE_DATA devData = (PPDO_DEVICE_DATA)_context;
if (!devData->FdoContext) {
return STATUS_NO_SUCH_DEVICE;
}
devData->FdoContext->dspInterruptCallback = NULL;
devData->FdoContext->dspInterruptContext = NULL;
return STATUS_SUCCESS;
}
NTSTATUS ADSPGetRenderStream(_In_ PVOID _context, HDAUDIO_STREAM_FORMAT StreamFormat, PHANDLE Handle, _Out_ UINT8* streamTag) {
if (!_context)
return STATUS_NO_SUCH_DEVICE;
PPDO_DEVICE_DATA devData = (PPDO_DEVICE_DATA)_context;
if (!devData->FdoContext) {
return STATUS_NO_SUCH_DEVICE;
}
PFDO_CONTEXT fdoContext = devData->FdoContext;
NTSTATUS status = WdfDeviceStopIdle(devData->FdoContext->WdfDevice, TRUE);
if (!NT_SUCCESS(status)) {
return status;
}
WdfInterruptAcquireLock(devData->FdoContext->Interrupt);
for (UINT32 i = 0; i < fdoContext->playbackStreams; i++) {
int tag = fdoContext->playbackIndexOff + i;
PHDAC_STREAM stream = &fdoContext->streams[tag];
if (stream->PdoContext != NULL) {
continue;
}
stream->stripe = FALSE;
stream->PdoContext = devData;
stream->running = FALSE;
stream->streamFormat = StreamFormat;
int mask = HDA_PPCTL_PROCEN(stream->idx);
UINT32 val = 0;
val = read16(fdoContext->ppcap + HDA_REG_PP_PPCTL) & mask;
if (!val) {
hdac_update32(fdoContext->ppcap, HDA_REG_PP_PPCTL, mask, mask);
}
if (Handle)
*Handle = (HANDLE)stream;
if (streamTag)
*streamTag = stream->streamTag;
WdfInterruptReleaseLock(devData->FdoContext->Interrupt);
return STATUS_SUCCESS;
}
WdfInterruptReleaseLock(devData->FdoContext->Interrupt);
WdfDeviceResumeIdle(devData->FdoContext->WdfDevice);
return STATUS_INSUFFICIENT_RESOURCES;
}
NTSTATUS ADSPGetCaptureStream(_In_ PVOID _context, HDAUDIO_STREAM_FORMAT StreamFormat, PHANDLE Handle, _Out_ UINT8* streamTag) {
if (!_context)
return STATUS_NO_SUCH_DEVICE;
PPDO_DEVICE_DATA devData = (PPDO_DEVICE_DATA)_context;
if (!devData->FdoContext) {
return STATUS_NO_SUCH_DEVICE;
}
PFDO_CONTEXT fdoContext = devData->FdoContext;
NTSTATUS status = WdfDeviceStopIdle(devData->FdoContext->WdfDevice, TRUE);
if (!NT_SUCCESS(status)) {
return status;
}
WdfInterruptAcquireLock(devData->FdoContext->Interrupt);
for (UINT32 i = 0; i < fdoContext->captureStreams; i++) {
int tag = fdoContext->captureIndexOff + i;
PHDAC_STREAM stream = &fdoContext->streams[tag];
if (stream->PdoContext != NULL) {
continue;
}
stream->stripe = FALSE;
stream->PdoContext = devData;
stream->running = FALSE;
stream->streamFormat = StreamFormat;
int mask = HDA_PPCTL_PROCEN(stream->idx);
UINT32 val = 0;
val = read16(fdoContext->ppcap + HDA_REG_PP_PPCTL) & mask;
if (!val) {
hdac_update32(fdoContext->ppcap, HDA_REG_PP_PPCTL, mask, mask);
}
if (Handle)
*Handle = (HANDLE)stream;
if (streamTag)
*streamTag = stream->streamTag;
WdfInterruptReleaseLock(devData->FdoContext->Interrupt);
return STATUS_SUCCESS;
}
WdfInterruptReleaseLock(devData->FdoContext->Interrupt);
WdfDeviceResumeIdle(devData->FdoContext->WdfDevice);
return STATUS_INSUFFICIENT_RESOURCES;
}
NTSTATUS ADSPFreeStream(
_In_ PVOID _context,
_In_ HANDLE Handle
) {
SklHdAudBusPrint(DEBUG_LEVEL_VERBOSE, DBG_IOCTL, "%s called!\n", __func__);
PPDO_DEVICE_DATA devData = (PPDO_DEVICE_DATA)_context;
if (!devData->FdoContext) {
return STATUS_NO_SUCH_DEVICE;
}
PHDAC_STREAM stream = (PHDAC_STREAM)Handle;
if (!stream || stream->PdoContext != devData) {
return STATUS_INVALID_HANDLE;
}
WdfInterruptAcquireLock(devData->FdoContext->Interrupt);
if (stream->running) {
WdfInterruptReleaseLock(devData->FdoContext->Interrupt);
return STATUS_INVALID_DEVICE_REQUEST;
}
PFDO_CONTEXT fdoContext = devData->FdoContext;
int mask = HDA_PPCTL_PROCEN(stream->idx);
UINT32 val = 0;
val = read16(fdoContext->ppcap + HDA_REG_PP_PPCTL) & mask;
if (val) {
hdac_update32(fdoContext->ppcap, HDA_REG_PP_PPCTL, mask, 0);
}
stream->PdoContext = NULL;
WdfInterruptReleaseLock(devData->FdoContext->Interrupt);
WdfDeviceResumeIdle(devData->FdoContext->WdfDevice);
return STATUS_SUCCESS;
}
NTSTATUS ADSPPrepareDSP(
_In_ PVOID _context,
_In_ HANDLE Handle,
_In_ unsigned int ByteSize,
_In_ int NumBlocks,
_Out_ PVOID* bdlBuf
) {
PPDO_DEVICE_DATA devData = (PPDO_DEVICE_DATA)_context;
if (!devData->FdoContext) {
return STATUS_NO_SUCH_DEVICE;
}
PHDAC_STREAM stream = (PHDAC_STREAM)Handle;
if (!stream || stream->PdoContext != devData) {
return STATUS_INVALID_HANDLE;
}
WdfInterruptAcquireLock(devData->FdoContext->Interrupt);
if (stream->running) {
WdfInterruptReleaseLock(devData->FdoContext->Interrupt);
return STATUS_DEVICE_BUSY;
}
WdfInterruptReleaseLock(devData->FdoContext->Interrupt);
stream->mdlBuf = NULL;
stream->bufSz = ByteSize;
stream->numBlocks = (UINT16)NumBlocks;
hdac_stream_reset(stream);
/* reset BDL address */
stream_write32(stream, SD_BDLPL, 0);
stream_write32(stream, SD_BDLPU, 0);
hdac_stream_setup(stream);
if (bdlBuf)
*bdlBuf = stream->bdl;
return STATUS_SUCCESS;
}
NTSTATUS ADSPCleanupDSP(_In_ PVOID _context, _In_ HANDLE Handle) {
PPDO_DEVICE_DATA devData = (PPDO_DEVICE_DATA)_context;
if (!devData->FdoContext) {
return STATUS_NO_SUCH_DEVICE;
}
PHDAC_STREAM stream = (PHDAC_STREAM)Handle;
if (!stream || stream->PdoContext != devData) {
return STATUS_INVALID_HANDLE;
}
WdfInterruptAcquireLock(devData->FdoContext->Interrupt);
stream_write32(stream, SD_BDLPL, 0);
stream_write32(stream, SD_BDLPU, 0);
stream_write32(stream, SD_CTL, 0);
WdfInterruptReleaseLock(devData->FdoContext->Interrupt);
return STATUS_SUCCESS;
}
void ADSPStartStopDSP(_In_ PVOID _context, _In_ HANDLE Handle, BOOL startStop) {
PPDO_DEVICE_DATA devData = (PPDO_DEVICE_DATA)_context;
if (!devData->FdoContext) {
return;
}
PHDAC_STREAM stream = (PHDAC_STREAM)Handle;
if (!stream || stream->PdoContext != devData) {
return;
}
if (startStop)
hdac_stream_start(stream);
else
hdac_stream_stop(stream);
}
void ADSPEnableSPIB(_In_ PVOID _context, _In_ HANDLE Handle, UINT32 value) {
PPDO_DEVICE_DATA devData = (PPDO_DEVICE_DATA)_context;
if (!devData->FdoContext) {
return;
}
PHDAC_STREAM stream = (PHDAC_STREAM)Handle;
if (!stream || stream->PdoContext != devData) {
return;
}
if (!devData->FdoContext->spbcap) {
return;
}
UINT32 mask = (1 << stream->idx);
hdac_update32(devData->FdoContext->spbcap, HDA_REG_SPB_SPBFCCTL, mask, mask);
write32(stream->spib_addr, value);
}
void ADSPDisableSPIB(_In_ PVOID _context, _In_ HANDLE Handle) {
PPDO_DEVICE_DATA devData = (PPDO_DEVICE_DATA)_context;
if (!devData->FdoContext) {
return;
}
PHDAC_STREAM stream = (PHDAC_STREAM)Handle;
if (!stream || stream->PdoContext != devData) {
return;
}
if (!devData->FdoContext->spbcap) {
return;
}
UINT32 mask = (1 << stream->idx);
hdac_update32(devData->FdoContext->spbcap, HDA_REG_SPB_SPBFCCTL, mask, 0);
write32(stream->spib_addr, 0);
}
UINT32 ADSPStreamPosition(_In_ PVOID _context, _In_ HANDLE Handle) {
PPDO_DEVICE_DATA devData = (PPDO_DEVICE_DATA)_context;
if (!devData->FdoContext) {
return 0;
}
PHDAC_STREAM stream = (PHDAC_STREAM)Handle;
if (!stream || stream->PdoContext != devData) {
return 0;
}
return *stream->posbuf;
}
ADSP_BUS_INTERFACE ADSP_BusInterface(PVOID Context) {
ADSP_BUS_INTERFACE busInterface;
RtlZeroMemory(&busInterface, sizeof(ADSP_BUS_INTERFACE));
PPDO_DEVICE_DATA devData = (PPDO_DEVICE_DATA)Context;
busInterface.Size = sizeof(ADSP_BUS_INTERFACE);
busInterface.Version = 1;
busInterface.Context = Context;
busInterface.InterfaceReference = WdfDeviceInterfaceReferenceNoOp;
busInterface.InterfaceDereference = WdfDeviceInterfaceDereferenceNoOp;
busInterface.CtlrDevId = devData->CodecIds.CtlrDevId;
busInterface.GetResources = ADSPGetResources;
busInterface.SetDSPPowerState = ADSPSetPowerState;
busInterface.RegisterInterrupt = ADSPRegisterInterrupt;
busInterface.UnregisterInterrupt = ADSPUnregisterInterrupt;
busInterface.GetRenderStream = ADSPGetRenderStream;
busInterface.GetCaptureStream = ADSPGetCaptureStream;
busInterface.FreeStream = ADSPFreeStream;
busInterface.PrepareDSP = ADSPPrepareDSP;
busInterface.CleanupDSP = ADSPCleanupDSP;
busInterface.TriggerDSP = ADSPStartStopDSP;
busInterface.StreamPosition = ADSPStreamPosition;
busInterface.DSPEnableSPIB = ADSPEnableSPIB;
busInterface.DSPDisableSPIB = ADSPDisableSPIB;
return busInterface;
}

View File

@@ -0,0 +1,64 @@
#ifndef __ADSP_INTERFACE
#define __ADSP_INTERFACE
//
// The ADSP_BUS_INTERFACE interface GUID
//
// {752A2CAE-3455-4D18-A184-8B34B22632CE}
DEFINE_GUID(GUID_ADSP_BUS_INTERFACE,
0x752a2cae, 0x3455, 0x4d18, 0xa1, 0x84, 0x8b, 0x34, 0xb2, 0x26, 0x32, 0xce);
typedef struct _NHLT_INFO {
PVOID nhlt;
UINT64 nhltSz;
} NHLT_INFO, * PNHLT_INFO;
typedef _Must_inspect_result_ NTSTATUS(*PGET_ADSP_RESOURCES) (_In_ PVOID _context, _Out_ _PCI_BAR* hdaBar, _Out_ _PCI_BAR* adspBar, PVOID* ppcap, PNHLT_INFO nhltInfo, _Out_ BUS_INTERFACE_STANDARD* pciConfig);
typedef _Must_inspect_result_ NTSTATUS(*PDSP_SET_POWER_STATE) (_In_ PVOID _context, _In_ DEVICE_POWER_STATE newPowerState);
typedef _Must_inspect_result_ BOOL(*PADSP_INTERRUPT_CALLBACK)(PVOID context);
typedef _Must_inspect_result_ NTSTATUS(*PREGISTER_ADSP_INTERRUPT) (_In_ PVOID _context, _In_ PADSP_INTERRUPT_CALLBACK callback, _In_ PVOID callbackContext);
typedef _Must_inspect_result_ NTSTATUS(*PUNREGISTER_ADSP_INTERRUPT) (_In_ PVOID _context);
typedef _Must_inspect_result_ NTSTATUS(*PGET_STREAM)(_In_ PVOID _context, HDAUDIO_STREAM_FORMAT StreamFormat, PHANDLE Handle, _Out_ UINT8* streamTag);
typedef _Must_inspect_result_ NTSTATUS(*PFREE_STREAM)(_In_ PVOID _context, _In_ HANDLE Handle);
typedef _Must_inspect_result_ NTSTATUS(*PDSP_PREPARE_STREAM)(_In_ PVOID _context, _In_ HANDLE Handle, _In_ unsigned int ByteSize, _In_ int frags, _Out_ PVOID* bdlBuf);
typedef _Must_inspect_result_ NTSTATUS(*PDSP_CLEANUP_STREAM)(_In_ PVOID _context, _In_ HANDLE Handle);
typedef _Must_inspect_result_ UINT32(*PDSP_STREAM_POSITION)(_In_ PVOID _context, _In_ HANDLE Handle);
typedef void (*PDSP_START_STOP_STREAM)(_In_ PVOID _context, _In_ HANDLE Handle, BOOL startStop);
typedef void (*PDSP_ENABLE_SPIB)(_In_ PVOID _context, _In_ HANDLE Handle, UINT32 value);
typedef void (*PDSP_DISABLE_SPIB)(_In_ PVOID _context, _In_ HANDLE Handle);
typedef struct _ADSP_BUS_INTERFACE
{
//
// First we define the standard INTERFACE structure ...
//
USHORT Size;
USHORT Version;
PVOID Context;
PINTERFACE_REFERENCE InterfaceReference;
PINTERFACE_DEREFERENCE InterfaceDereference;
//
// Then we expand the structure with the ADSP_BUS_INTERFACE stuff.
UINT16 CtlrDevId;
PGET_ADSP_RESOURCES GetResources;
PDSP_SET_POWER_STATE SetDSPPowerState;
PREGISTER_ADSP_INTERRUPT RegisterInterrupt;
PUNREGISTER_ADSP_INTERRUPT UnregisterInterrupt;
PGET_STREAM GetRenderStream;
PGET_STREAM GetCaptureStream;
PFREE_STREAM FreeStream;
PDSP_PREPARE_STREAM PrepareDSP;
PDSP_CLEANUP_STREAM CleanupDSP;
PDSP_START_STOP_STREAM TriggerDSP;
PDSP_STREAM_POSITION StreamPosition;
PDSP_ENABLE_SPIB DSPEnableSPIB;
PDSP_DISABLE_SPIB DSPDisableSPIB;
} ADSP_BUS_INTERFACE, * PADSP_BUS_INTERFACE;
#ifndef ADSP_DECL
ADSP_BUS_INTERFACE ADSP_BusInterface(PVOID Context);
#endif
#endif

View File

@@ -0,0 +1,561 @@
#include "driver.h"
#include "adsp.h"
NTSTATUS
Bus_CreatePdo(
_In_ WDFDEVICE Device,
_In_ PWDFDEVICE_INIT DeviceInit,
_In_ PPDO_IDENTIFICATION_DESCRIPTION Desc
);
NTSTATUS
NTAPI
Bus_EvtChildListIdentificationDescriptionDuplicate(
WDFCHILDLIST DeviceList,
PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER SourceIdentificationDescription,
PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER DestinationIdentificationDescription
)
/*++
Routine Description:
It is called when the framework needs to make a copy of a description.
This happens when a request is made to create a new child device by
calling WdfChildListAddOrUpdateChildDescriptionAsPresent.
If this function is left unspecified, RtlCopyMemory will be used to copy the
source description to destination. Memory for the description is managed by the
framework.
NOTE: Callback is invoked with an internal lock held. So do not call out
to any WDF function which will require this lock
(basically any other WDFCHILDLIST api)
Arguments:
DeviceList - Handle to the default WDFCHILDLIST created by the framework.
SourceIdentificationDescription - Description of the child being created -memory in
the calling thread stack.
DestinationIdentificationDescription - Created by the framework in nonpaged pool.
Return Value:
NT Status code.
--*/
{
PPDO_IDENTIFICATION_DESCRIPTION src, dst;
UNREFERENCED_PARAMETER(DeviceList);
src = CONTAINING_RECORD(SourceIdentificationDescription,
PDO_IDENTIFICATION_DESCRIPTION,
Header);
dst = CONTAINING_RECORD(DestinationIdentificationDescription,
PDO_IDENTIFICATION_DESCRIPTION,
Header);
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"%s\n", __func__);
dst->FdoContext = src->FdoContext;
RtlCopyMemory(&dst->CodecIds, &src->CodecIds, sizeof(dst->CodecIds));
return STATUS_SUCCESS;
}
BOOLEAN
NTAPI
Bus_EvtChildListIdentificationDescriptionCompare(
WDFCHILDLIST DeviceList,
PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER FirstIdentificationDescription,
PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER SecondIdentificationDescription
)
/*++
Routine Description:
It is called when the framework needs to compare one description with another.
Typically this happens whenever a request is made to add a new child device.
If this function is left unspecified, RtlCompareMemory will be used to compare the
descriptions.
NOTE: Callback is invoked with an internal lock held. So do not call out
to any WDF function which will require this lock
(basically any other WDFCHILDLIST api)
Arguments:
DeviceList - Handle to the default WDFCHILDLIST created by the framework.
Return Value:
TRUE or FALSE.
--*/
{
PPDO_IDENTIFICATION_DESCRIPTION lhs, rhs;
UNREFERENCED_PARAMETER(DeviceList);
lhs = CONTAINING_RECORD(FirstIdentificationDescription,
PDO_IDENTIFICATION_DESCRIPTION,
Header);
rhs = CONTAINING_RECORD(SecondIdentificationDescription,
PDO_IDENTIFICATION_DESCRIPTION,
Header);
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"%s\n", __func__);
return (lhs->FdoContext == rhs->FdoContext) &&
(RtlCompareMemory(&lhs->CodecIds, &rhs->CodecIds, sizeof(lhs->CodecIds)) == sizeof(lhs->CodecIds));
}
VOID
NTAPI
Bus_EvtChildListIdentificationDescriptionCleanup(
_In_ WDFCHILDLIST DeviceList,
_Inout_ PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER IdentificationDescription
)
/*++
Routine Description:
It is called to free up any memory resources allocated as part of the description.
This happens when a child device is unplugged or ejected from the bus.
Memory for the description itself will be freed by the framework.
Arguments:
DeviceList - Handle to the default WDFCHILDLIST created by the framework.
IdentificationDescription - Description of the child being deleted
Return Value:
--*/
{
UNREFERENCED_PARAMETER(DeviceList);
UNREFERENCED_PARAMETER(IdentificationDescription);
}
NTSTATUS
NTAPI
Bus_EvtDeviceListCreatePdo(
WDFCHILDLIST DeviceList,
PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER IdentificationDescription,
PWDFDEVICE_INIT ChildInit
)
/*++
Routine Description:
Called by the framework in response to Query-Device relation when
a new PDO for a child device needs to be created.
Arguments:
DeviceList - Handle to the default WDFCHILDLIST created by the framework as part
of FDO.
IdentificationDescription - Decription of the new child device.
ChildInit - It's a opaque structure used in collecting device settings
and passed in as a parameter to CreateDevice.
Return Value:
NT Status code.
--*/
{
PPDO_IDENTIFICATION_DESCRIPTION pDesc;
PAGED_CODE();
pDesc = CONTAINING_RECORD(IdentificationDescription,
PDO_IDENTIFICATION_DESCRIPTION,
Header);
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"%s\n", __func__);
return Bus_CreatePdo(WdfChildListGetDevice(DeviceList),
ChildInit,
pDesc);
}
NTSTATUS
Bus_CreatePdo(
_In_ WDFDEVICE Device,
_In_ PWDFDEVICE_INIT DeviceInit,
_In_ PPDO_IDENTIFICATION_DESCRIPTION Desc
)
{
UNREFERENCED_PARAMETER(Device);
NTSTATUS status;
PPDO_DEVICE_DATA pdoData = NULL;
WDFDEVICE hChild = NULL;
WDF_QUERY_INTERFACE_CONFIG qiConfig;
WDF_OBJECT_ATTRIBUTES pdoAttributes;
WDF_DEVICE_PNP_CAPABILITIES pnpCaps;
WDF_DEVICE_POWER_CAPABILITIES powerCaps;
DECLARE_CONST_UNICODE_STRING(deviceLocation, L"High Definition Audio Bus");
DECLARE_UNICODE_STRING_SIZE(buffer, MAX_INSTANCE_ID_LEN);
DECLARE_UNICODE_STRING_SIZE(deviceId, MAX_INSTANCE_ID_LEN);
DECLARE_UNICODE_STRING_SIZE(compatId, MAX_INSTANCE_ID_LEN);
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"%s\n", __func__);
if (Desc->CodecIds.IsDSP) {
//
// Set DeviceType
//
WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_BUS_EXTENDER);
//
// Provide DeviceID, HardwareIDs, CompatibleIDs and InstanceId
//
status = RtlUnicodeStringPrintf(&deviceId, L"CSAUDIO\\ADSP&CTLR_VEN_%04X&CTLR_DEV_%04X",
Desc->CodecIds.CtlrVenId, Desc->CodecIds.CtlrDevId);
if (!NT_SUCCESS(status)) {
return status;
}
status = WdfPdoInitAssignInstanceID(DeviceInit, &deviceId);
if (!NT_SUCCESS(status)) {
return status;
}
status = WdfPdoInitAssignDeviceID(DeviceInit, &deviceId);
if (!NT_SUCCESS(status)) {
return status;
}
//
// NOTE: same string is used to initialize hardware id too
//
status = WdfPdoInitAddHardwareID(DeviceInit, &deviceId);
if (!NT_SUCCESS(status)) {
return status;
}
status = RtlUnicodeStringPrintf(&compatId, L"CSAUDIO\\ADSP&CTLR_VEN_%04X&CTLR_DEV_%04X",
Desc->CodecIds.CtlrVenId, Desc->CodecIds.CtlrDevId);
if (!NT_SUCCESS(status)) {
return status;
}
//
// NOTE: same string is used to initialize compat id too
//
status = WdfPdoInitAddCompatibleID(DeviceInit, &compatId);
if (!NT_SUCCESS(status)) {
return status;
}
}
else {
//
// Set DeviceType
//
WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_SOUND);
PWCHAR prefix = L"HDAUDIO";
PWCHAR funcPrefix = L"";
if (Desc->CodecIds.IsGraphicsCodec) {
funcPrefix = L"SGPC_";
}
//
// Provide DeviceID, HardwareIDs, CompatibleIDs and InstanceId
//
status = RtlUnicodeStringPrintf(&deviceId, L"%s\\%sFUNC_%02X&VEN_%04X&DEV_%04X&SUBSYS_%08X&REV_%04X",
prefix, funcPrefix, Desc->CodecIds.FuncId, Desc->CodecIds.VenId, Desc->CodecIds.DevId, Desc->CodecIds.SubsysId, Desc->CodecIds.RevId);
if (!NT_SUCCESS(status)) {
return status;
}
status = WdfPdoInitAssignDeviceID(DeviceInit, &deviceId);
if (!NT_SUCCESS(status)) {
return status;
}
//
// NOTE: same string is used to initialize hardware id too
//
status = WdfPdoInitAddHardwareID(DeviceInit, &deviceId);
if (!NT_SUCCESS(status)) {
return status;
}
//Add second hardware ID without Rev
status = RtlUnicodeStringPrintf(&deviceId, L"%s\\%sFUNC_%02X&VEN_%04X&DEV_%04X&SUBSYS_%08X",
prefix, funcPrefix, Desc->CodecIds.FuncId, Desc->CodecIds.VenId, Desc->CodecIds.DevId, Desc->CodecIds.SubsysId);
if (!NT_SUCCESS(status)) {
return status;
}
status = WdfPdoInitAddHardwareID(DeviceInit, &deviceId);
if (!NT_SUCCESS(status)) {
return status;
}
//Add Compatible Ids
{
status = RtlUnicodeStringPrintf(&compatId, L"%s\\%sFUNC_%02X&CTLR_VEN_%02X&CTLR_DEV_%02X&VEN_%04X&DEV_%04X&REV_%04X",
prefix, funcPrefix, Desc->CodecIds.FuncId, Desc->CodecIds.CtlrVenId, Desc->CodecIds.CtlrDevId, Desc->CodecIds.VenId, Desc->CodecIds.DevId, Desc->CodecIds.RevId);
if (!NT_SUCCESS(status)) {
return status;
}
status = WdfPdoInitAddCompatibleID(DeviceInit, &compatId);
if (!NT_SUCCESS(status)) {
return status;
}
status = RtlUnicodeStringPrintf(&compatId, L"%s\\%sFUNC_%02X&CTLR_VEN_%02X&VEN_%04X&DEV_%04X&REV_%04X",
prefix, funcPrefix, Desc->CodecIds.FuncId, Desc->CodecIds.CtlrVenId, Desc->CodecIds.VenId, Desc->CodecIds.DevId, Desc->CodecIds.RevId);
if (!NT_SUCCESS(status)) {
return status;
}
status = WdfPdoInitAddCompatibleID(DeviceInit, &compatId);
if (!NT_SUCCESS(status)) {
return status;
}
status = RtlUnicodeStringPrintf(&compatId, L"%s\\%sFUNC_%02X&VEN_%04X&DEV_%04X&REV_%04X",
prefix, funcPrefix, Desc->CodecIds.FuncId, Desc->CodecIds.VenId, Desc->CodecIds.DevId, Desc->CodecIds.RevId);
if (!NT_SUCCESS(status)) {
return status;
}
status = WdfPdoInitAddCompatibleID(DeviceInit, &compatId);
if (!NT_SUCCESS(status)) {
return status;
}
status = RtlUnicodeStringPrintf(&compatId, L"%s\\%sFUNC_%02X&CTLR_VEN_%02X&CTLR_DEV_%02X&VEN_%04X&DEV_%04X",
prefix, funcPrefix, Desc->CodecIds.FuncId, Desc->CodecIds.CtlrVenId, Desc->CodecIds.CtlrDevId, Desc->CodecIds.VenId, Desc->CodecIds.DevId);
if (!NT_SUCCESS(status)) {
return status;
}
status = WdfPdoInitAddCompatibleID(DeviceInit, &compatId);
if (!NT_SUCCESS(status)) {
return status;
}
status = RtlUnicodeStringPrintf(&compatId, L"%s\\%sFUNC_%02X&CTLR_VEN_%02X&VEN_%04X&DEV_%04X",
prefix, funcPrefix, Desc->CodecIds.FuncId, Desc->CodecIds.CtlrVenId, Desc->CodecIds.VenId, Desc->CodecIds.DevId);
if (!NT_SUCCESS(status)) {
return status;
}
status = WdfPdoInitAddCompatibleID(DeviceInit, &compatId);
if (!NT_SUCCESS(status)) {
return status;
}
status = RtlUnicodeStringPrintf(&compatId, L"%s\\%sFUNC_%02X&VEN_%04X&DEV_%04X",
prefix, funcPrefix, Desc->CodecIds.FuncId, Desc->CodecIds.VenId, Desc->CodecIds.DevId);
if (!NT_SUCCESS(status)) {
return status;
}
status = WdfPdoInitAddCompatibleID(DeviceInit, &compatId);
if (!NT_SUCCESS(status)) {
return status;
}
status = RtlUnicodeStringPrintf(&compatId, L"%s\\%sFUNC_%02X&CTLR_VEN_%02X&CTLR_DEV_%02X&VEN_%04X",
prefix, funcPrefix, Desc->CodecIds.FuncId, Desc->CodecIds.CtlrVenId, Desc->CodecIds.CtlrDevId, Desc->CodecIds.VenId);
if (!NT_SUCCESS(status)) {
return status;
}
status = WdfPdoInitAddCompatibleID(DeviceInit, &compatId);
if (!NT_SUCCESS(status)) {
return status;
}
status = RtlUnicodeStringPrintf(&compatId, L"%s\\%sFUNC_%02X&CTLR_VEN_%02X&VEN_%04X",
prefix, funcPrefix, Desc->CodecIds.FuncId, Desc->CodecIds.CtlrVenId, Desc->CodecIds.VenId);
if (!NT_SUCCESS(status)) {
return status;
}
status = WdfPdoInitAddCompatibleID(DeviceInit, &compatId);
if (!NT_SUCCESS(status)) {
return status;
}
status = RtlUnicodeStringPrintf(&compatId, L"%s\\%sFUNC_%02X&VEN_%04X",
prefix, funcPrefix, Desc->CodecIds.FuncId, Desc->CodecIds.VenId);
if (!NT_SUCCESS(status)) {
return status;
}
status = WdfPdoInitAddCompatibleID(DeviceInit, &compatId);
if (!NT_SUCCESS(status)) {
return status;
}
status = RtlUnicodeStringPrintf(&compatId, L"%s\\%sFUNC_%02X&CTLR_VEN_%02X&CTLR_DEV_%02X",
prefix, funcPrefix, Desc->CodecIds.FuncId, Desc->CodecIds.CtlrVenId, Desc->CodecIds.CtlrDevId);
if (!NT_SUCCESS(status)) {
return status;
}
status = WdfPdoInitAddCompatibleID(DeviceInit, &compatId);
if (!NT_SUCCESS(status)) {
return status;
}
status = RtlUnicodeStringPrintf(&compatId, L"%s\\%sFUNC_%02X&CTLR_VEN_%02X",
prefix, funcPrefix, Desc->CodecIds.FuncId, Desc->CodecIds.CtlrVenId);
if (!NT_SUCCESS(status)) {
return status;
}
status = WdfPdoInitAddCompatibleID(DeviceInit, &compatId);
if (!NT_SUCCESS(status)) {
return status;
}
status = RtlUnicodeStringPrintf(&compatId, L"%s\\%sFUNC_%02X",
prefix, funcPrefix, Desc->CodecIds.FuncId);
if (!NT_SUCCESS(status)) {
return status;
}
status = WdfPdoInitAddCompatibleID(DeviceInit, &compatId);
if (!NT_SUCCESS(status)) {
return status;
}
}
}
status = RtlUnicodeStringPrintf(&buffer, L"%02d", Desc->CodecIds.CodecAddress);
if (!NT_SUCCESS(status)) {
return status;
}
status = WdfPdoInitAssignInstanceID(DeviceInit, &buffer);
if (!NT_SUCCESS(status)) {
return status;
}
//
// Provide a description about the device. This text is usually read from
// the device. In the case of USB device, this text comes from the string
// descriptor. This text is displayed momentarily by the PnP manager while
// it's looking for a matching INF. If it finds one, it uses the Device
// Description from the INF file or the friendly name created by
// coinstallers to display in the device manager. FriendlyName takes
// precedence over the DeviceDesc from the INF file.
//
if (Desc->CodecIds.IsDSP) {
status = RtlUnicodeStringPrintf(&buffer,
L"Intel Audio DSP");
}
else {
status = RtlUnicodeStringPrintf(&buffer,
L"High Definition Audio Device");
}
if (!NT_SUCCESS(status)) {
return status;
}
//
// You can call WdfPdoInitAddDeviceText multiple times, adding device
// text for multiple locales. When the system displays the text, it
// chooses the text that matches the current locale, if available.
// Otherwise it will use the string for the default locale.
// The driver can specify the driver's default locale by calling
// WdfPdoInitSetDefaultLocale.
//
status = WdfPdoInitAddDeviceText(DeviceInit,
&buffer,
&deviceLocation,
MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US));
if (!NT_SUCCESS(status)) {
return status;
}
WdfPdoInitSetDefaultLocale(DeviceInit, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US));
//
// Initialize the attributes to specify the size of PDO device extension.
// All the state information private to the PDO will be tracked here.
//
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&pdoAttributes, PDO_DEVICE_DATA);
status = WdfDeviceCreate(&DeviceInit, &pdoAttributes, &hChild);
if (!NT_SUCCESS(status)) {
return status;
}
//
// Get the device context.
//
pdoData = PdoGetData(hChild);
pdoData->FdoContext = Desc->FdoContext;
RtlCopyMemory(&pdoData->CodecIds, &Desc->CodecIds, sizeof(Desc->CodecIds));
if (!Desc->CodecIds.IsDSP)
Desc->FdoContext->codecs[Desc->CodecIds.CodecAddress] = pdoData;
//
// Set some properties for the child device.
//
WDF_DEVICE_PNP_CAPABILITIES_INIT(&pnpCaps);
pnpCaps.Removable = WdfFalse;
pnpCaps.EjectSupported = Desc->CodecIds.IsGraphicsCodec ? WdfTrue : WdfFalse;
pnpCaps.SurpriseRemovalOK = Desc->CodecIds.IsGraphicsCodec ? WdfTrue : WdfFalse;
pnpCaps.Address = Desc->CodecIds.CodecAddress;
pnpCaps.UINumber = Desc->CodecIds.CodecAddress;
WdfDeviceSetPnpCapabilities(hChild, &pnpCaps);
WDF_DEVICE_POWER_CAPABILITIES_INIT(&powerCaps);
powerCaps.DeviceD1 = WdfTrue;
powerCaps.WakeFromD1 = WdfTrue;
powerCaps.DeviceWake = PowerDeviceD1;
powerCaps.DeviceState[PowerSystemWorking] = PowerDeviceD1;
powerCaps.DeviceState[PowerSystemSleeping1] = PowerDeviceD1;
powerCaps.DeviceState[PowerSystemSleeping2] = PowerDeviceD2;
powerCaps.DeviceState[PowerSystemSleeping3] = PowerDeviceD2;
powerCaps.DeviceState[PowerSystemHibernate] = PowerDeviceD3;
powerCaps.DeviceState[PowerSystemShutdown] = PowerDeviceD3;
WdfDeviceSetPowerCapabilities(hChild, &powerCaps);
if (Desc->CodecIds.IsDSP) {
ADSP_BUS_INTERFACE busInterface = ADSP_BusInterface(pdoData);
WDF_QUERY_INTERFACE_CONFIG_INIT(&qiConfig,
(PINTERFACE)&busInterface,
&GUID_ADSP_BUS_INTERFACE,
NULL);
status = WdfDeviceAddQueryInterface(hChild, &qiConfig);
}
else {
HDAUDIO_BUS_INTERFACE busInterface = HDA_BusInterface(pdoData);
WDF_QUERY_INTERFACE_CONFIG_INIT(&qiConfig,
(PINTERFACE)&busInterface,
&GUID_HDAUDIO_BUS_INTERFACE,
NULL);
status = WdfDeviceAddQueryInterface(hChild, &qiConfig);
if (!NT_SUCCESS(status)) {
return status;
}
HDAUDIO_BUS_INTERFACE_BDL busInterfaceBDL = HDA_BusInterfaceBDL(pdoData);
WDF_QUERY_INTERFACE_CONFIG_INIT(&qiConfig,
(PINTERFACE)&busInterfaceBDL,
&GUID_HDAUDIO_BUS_INTERFACE_BDL,
NULL);
status = WdfDeviceAddQueryInterface(hChild, &qiConfig);
if (!NT_SUCCESS(status)) {
return status;
}
HDAUDIO_BUS_INTERFACE_V2 busInterface2 = HDA_BusInterfaceV2(pdoData);
WDF_QUERY_INTERFACE_CONFIG_INIT(&qiConfig,
(PINTERFACE)&busInterface2,
&GUID_HDAUDIO_BUS_INTERFACE_V2,
NULL);
status = WdfDeviceAddQueryInterface(hChild, &qiConfig);
if (!NT_SUCCESS(status)) {
return status;
}
HDAUDIO_BUS_INTERFACE_V3 busInterface3 = HDA_BusInterfaceV3(pdoData);
WDF_QUERY_INTERFACE_CONFIG_INIT(&qiConfig,
(PINTERFACE)&busInterface3,
&GUID_HDAUDIO_BUS_INTERFACE_V3,
NULL);
status = WdfDeviceAddQueryInterface(hChild, &qiConfig);
if (!NT_SUCCESS(status)) {
return status;
}
}
return status;
}

View File

@@ -0,0 +1,90 @@
#if !defined(_SKLHDAUDBUS_BUSPDO_H_)
#define _SKLHDAUDBUS_BUSPDO_H_
#define MAX_INSTANCE_ID_LEN 80
typedef struct _CODEC_UNSOLICITED_CALLBACK {
BOOLEAN inUse;
PVOID Context;
PHDAUDIO_UNSOLICITED_RESPONSE_CALLBACK Routine;
} CODEC_UNSOLICITED_CALLBACK, *PCODEC_UNSOLICIT_CALLBACK;
typedef struct _CODEC_IDS {
UINT32 CodecAddress;
BOOL IsGraphicsCodec;
UINT8 FunctionGroupStartNode;
UINT16 CtlrDevId;
UINT16 CtlrVenId;
BOOL IsDSP;
UINT16 FuncId;
UINT16 VenId;
UINT16 DevId;
UINT32 SubsysId;
UINT16 RevId;
} CODEC_IDS, * PCODEC_IDS;
typedef struct _PDO_IDENTIFICATION_DESCRIPTION
{
WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER Header; // should contain this header
PFDO_CONTEXT FdoContext;
CODEC_IDS CodecIds;
} PDO_IDENTIFICATION_DESCRIPTION, * PPDO_IDENTIFICATION_DESCRIPTION;
#define MAX_UNSOLICIT_CALLBACKS 64 // limit is 64 for hdaudbus (See: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/hdaudio/nc-hdaudio-pregister_event_callback)
#define SUBTAG_MASK 0x3F
#define TAG_ADDR_SHIFT 6
//
// This is PDO device-extension.
//
typedef struct _PDO_DEVICE_DATA
{
PFDO_CONTEXT FdoContext;
CODEC_IDS CodecIds;
CODEC_UNSOLICITED_CALLBACK unsolitCallbacks[MAX_UNSOLICIT_CALLBACKS];
} PDO_DEVICE_DATA, * PPDO_DEVICE_DATA;
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(PDO_DEVICE_DATA, PdoGetData)
extern "C" {
NTSTATUS NTAPI
Bus_EvtChildListIdentificationDescriptionDuplicate(
WDFCHILDLIST DeviceList,
PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER SourceIdentificationDescription,
PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER DestinationIdentificationDescription
);
BOOLEAN NTAPI
Bus_EvtChildListIdentificationDescriptionCompare(
WDFCHILDLIST DeviceList,
PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER FirstIdentificationDescription,
PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER SecondIdentificationDescription
);
VOID NTAPI
Bus_EvtChildListIdentificationDescriptionCleanup(
_In_ WDFCHILDLIST DeviceList,
_Inout_ PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER IdentificationDescription
);
NTSTATUS NTAPI
Bus_EvtDeviceListCreatePdo(
WDFCHILDLIST DeviceList,
PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER IdentificationDescription,
PWDFDEVICE_INIT ChildInit
);
}
#endif

View File

@@ -0,0 +1,104 @@
#if !defined(_SKLHDAUDBUS_H_)
#define _SKLHDAUDBUS_H_
#define POOL_ZERO_DOWN_LEVEL_SUPPORT
#pragma warning(disable:4200) // suppress nameless struct/union warning
#pragma warning(disable:4201) // suppress nameless struct/union warning
#pragma warning(disable:4214) // suppress bit field types other than int warning
#include <ntddk.h>
#include <initguid.h>
//SGPC (Win10 1809+ support)
extern "C" {
#define __CPLUSPLUS
#include <windef.h>
#include <winerror.h>
#include <wingdi.h>
#include <d3dkmddi.h>
#include <d3dkmthk.h>
}
#include <wdm.h>
#include <wdmguid.h>
#include <wdf.h>
#include <ntintsafe.h>
#include <ntstrsafe.h>
#include <hdaudio.h>
#include <portcls.h>
#include "hda_registers.h"
#include "fdo.h"
#include "buspdo.h"
#include "hdac_controller.h"
#include "hdac_stream.h"
#include "hda_verbs.h"
#define DRIVERNAME "sklhdaudbus.sys: "
#define SKLHDAUDBUS_POOL_TAG 'SADH'
#define VEN_INTEL 0x8086
#define VEN_ATI 0x1002
#define VEN_AMD 0x1022
#define VEN_NVIDIA 0x10DE
#define VEN_VMWARE 0x15AD
#include "regfuncs.h"
NTSTATUS HDA_WaitForTransfer(
PFDO_CONTEXT fdoCtx,
UINT16 codecAddr,
_In_ ULONG Count,
_Inout_updates_(Count)
PHDAUDIO_CODEC_TRANSFER CodecTransfer
);
HDAUDIO_BUS_INTERFACE HDA_BusInterface(PVOID Context);
HDAUDIO_BUS_INTERFACE_V2 HDA_BusInterfaceV2(PVOID Context);
HDAUDIO_BUS_INTERFACE_V3 HDA_BusInterfaceV3(PVOID Context);
HDAUDIO_BUS_INTERFACE_BDL HDA_BusInterfaceBDL(PVOID Context);
#define IS_BXT(ven, dev) (ven == VEN_INTEL && dev == 0x5a98)
static inline void mdelay(LONG msec) {
LARGE_INTEGER Interval;
Interval.QuadPart = -10 * 1000 * (LONGLONG)msec;
KeDelayExecutionThread(KernelMode, FALSE, &Interval);
}
static inline void udelay(LONG usec) {
LARGE_INTEGER Interval;
Interval.QuadPart = -10 * (LONGLONG)usec;
KeDelayExecutionThread(KernelMode, FALSE, &Interval);
}
//
// Helper macros
//
#if DBG
#define DEBUG_LEVEL_ERROR 1
#define DEBUG_LEVEL_INFO 2
#define DEBUG_LEVEL_VERBOSE 3
#define DBG_INIT 1
#define DBG_PNP 2
#define DBG_IOCTL 4
#define SklHdAudBusDebugLevel 100
#define SklHdAudBusDebugCategories (DBG_INIT | DBG_PNP | DBG_IOCTL)
#define SklHdAudBusPrint(dbglevel, dbgcategory, fmt, ...) { \
if (SklHdAudBusDebugLevel >= dbglevel && \
(SklHdAudBusDebugCategories & dbgcategory)) \
{ \
DbgPrint(DRIVERNAME); \
DbgPrint(fmt, ##__VA_ARGS__); \
} \
}
#else
#define SklHdAudBusPrint(dbglevel, fmt, ...) { \
}
#endif
#endif

View File

@@ -0,0 +1,851 @@
#include "driver.h"
#include "nhlt.h"
#include "sof-tplg.h"
EVT_WDF_DEVICE_PREPARE_HARDWARE Fdo_EvtDevicePrepareHardware;
EVT_WDF_DEVICE_RELEASE_HARDWARE Fdo_EvtDeviceReleaseHardware;
EVT_WDF_DEVICE_D0_ENTRY Fdo_EvtDeviceD0Entry;
EVT_WDF_DEVICE_D0_ENTRY_POST_INTERRUPTS_ENABLED Fdo_EvtDeviceD0EntryPostInterrupts;
EVT_WDF_DEVICE_D0_EXIT Fdo_EvtDeviceD0Exit;
EVT_WDF_DEVICE_SELF_MANAGED_IO_INIT Fdo_EvtDeviceSelfManagedIoInit;
void CheckHDAGraphicsRegistryKeys(PFDO_CONTEXT fdoCtx);
NTSTATUS
NTAPI
HDAGraphicsPowerInterfaceCallback(
PVOID NotificationStruct,
PVOID Context
);
NTSTATUS
NTAPI
Fdo_Initialize(
_In_ PFDO_CONTEXT FdoCtx
);
NTSTATUS
NTAPI
Fdo_Create(
_Inout_ PWDFDEVICE_INIT DeviceInit
)
{
WDF_CHILD_LIST_CONFIG config;
WDF_OBJECT_ATTRIBUTES attributes;
WDF_PNPPOWER_EVENT_CALLBACKS pnpPowerCallbacks;
PFDO_CONTEXT fdoCtx;
WDFDEVICE wdfDevice;
NTSTATUS status;
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"%s\n", __func__);
WDF_PNPPOWER_EVENT_CALLBACKS_INIT(&pnpPowerCallbacks);
pnpPowerCallbacks.EvtDevicePrepareHardware = Fdo_EvtDevicePrepareHardware;
pnpPowerCallbacks.EvtDeviceReleaseHardware = Fdo_EvtDeviceReleaseHardware;
pnpPowerCallbacks.EvtDeviceD0Entry = Fdo_EvtDeviceD0Entry;
pnpPowerCallbacks.EvtDeviceD0EntryPostInterruptsEnabled = Fdo_EvtDeviceD0EntryPostInterrupts;
pnpPowerCallbacks.EvtDeviceD0Exit = Fdo_EvtDeviceD0Exit;
pnpPowerCallbacks.EvtDeviceSelfManagedIoInit = Fdo_EvtDeviceSelfManagedIoInit;
WdfDeviceInitSetPnpPowerEventCallbacks(DeviceInit, &pnpPowerCallbacks);
//
// WDF_ DEVICE_LIST_CONFIG describes how the framework should handle
// dynamic child enumeration on behalf of the driver writer.
// Since we are a bus driver, we need to specify identification description
// for our child devices. This description will serve as the identity of our
// child device. Since the description is opaque to the framework, we
// have to provide bunch of callbacks to compare, copy, or free
// any other resources associated with the description.
//
WDF_CHILD_LIST_CONFIG_INIT(&config,
sizeof(PDO_IDENTIFICATION_DESCRIPTION),
Bus_EvtDeviceListCreatePdo // callback to create a child device.
);
//
// This function pointer will be called when the framework needs to copy a
// identification description from one location to another. An implementation
// of this function is only necessary if the description contains description
// relative pointer values (like LIST_ENTRY for instance) .
// If set to NULL, the framework will use RtlCopyMemory to copy an identification .
// description. In this sample, it's not required to provide these callbacks.
// they are added just for illustration.
//
config.EvtChildListIdentificationDescriptionDuplicate =
Bus_EvtChildListIdentificationDescriptionDuplicate;
//
// This function pointer will be called when the framework needs to compare
// two identificaiton descriptions. If left NULL a call to RtlCompareMemory
// will be used to compare two identificaiton descriptions.
//
config.EvtChildListIdentificationDescriptionCompare =
Bus_EvtChildListIdentificationDescriptionCompare;
//
// This function pointer will be called when the framework needs to free a
// identification description. An implementation of this function is only
// necessary if the description contains dynamically allocated memory
// (by the driver writer) that needs to be freed. The actual identification
// description pointer itself will be freed by the framework.
//
config.EvtChildListIdentificationDescriptionCleanup =
Bus_EvtChildListIdentificationDescriptionCleanup;
//
// Tell the framework to use the built-in childlist to track the state
// of the device based on the configuration we just created.
//
WdfFdoInitSetDefaultChildListConfig(DeviceInit,
&config,
WDF_NO_OBJECT_ATTRIBUTES);
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, FDO_CONTEXT);
status = WdfDeviceCreate(&DeviceInit, &attributes, &wdfDevice);
if (!NT_SUCCESS(status)) {
SklHdAudBusPrint(DEBUG_LEVEL_ERROR, DBG_INIT,
"WdfDriverCreate failed %x\n", status);
goto Exit;
}
/*{
WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS IdleSettings;
WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_INIT(&IdleSettings, IdleCannotWakeFromS0);
IdleSettings.IdleTimeoutType = SystemManagedIdleTimeoutWithHint;
IdleSettings.IdleTimeout = 1000;
IdleSettings.UserControlOfIdleSettings = IdleDoNotAllowUserControl;
WdfDeviceAssignS0IdleSettings(wdfDevice, &IdleSettings);
}*/
{
WDF_DEVICE_STATE deviceState;
WDF_DEVICE_STATE_INIT(&deviceState);
deviceState.NotDisableable = WdfFalse;
WdfDeviceSetDeviceState(wdfDevice, &deviceState);
}
fdoCtx = Fdo_GetContext(wdfDevice);
fdoCtx->WdfDevice = wdfDevice;
status = Fdo_Initialize(fdoCtx);
if (!NT_SUCCESS(status))
{
goto Exit;
}
CheckHDAGraphicsRegistryKeys(fdoCtx);
Exit:
return status;
}
NTSTATUS
NTAPI
Fdo_Initialize(
_In_ PFDO_CONTEXT FdoCtx
)
{
NTSTATUS status;
WDFDEVICE device;
WDF_INTERRUPT_CONFIG interruptConfig;
device = FdoCtx->WdfDevice;
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"%s\n", __func__);
//
// Create an interrupt object for hardware notifications
//
WDF_INTERRUPT_CONFIG_INIT(
&interruptConfig,
hda_interrupt,
hda_dpc);
status = WdfInterruptCreate(
device,
&interruptConfig,
WDF_NO_OBJECT_ATTRIBUTES,
&FdoCtx->Interrupt);
if (!NT_SUCCESS(status))
{
SklHdAudBusPrint(DEBUG_LEVEL_ERROR, DBG_PNP,
"Error creating WDF interrupt object - %!STATUS!",
status);
return status;
}
status = WdfWaitLockCreate(WDF_NO_OBJECT_ATTRIBUTES, &FdoCtx->GraphicsDevicesCollectionWaitLock);
if (!NT_SUCCESS(status))
{
SklHdAudBusPrint(DEBUG_LEVEL_ERROR, DBG_PNP,
"Error creating WDF wait lock - %!STATUS!",
status);
return status;
}
status = WdfCollectionCreate(WDF_NO_OBJECT_ATTRIBUTES, &FdoCtx->GraphicsDevicesCollection);
if (!NT_SUCCESS(status))
{
SklHdAudBusPrint(DEBUG_LEVEL_ERROR, DBG_PNP,
"Error creating WDF collection - %!STATUS!",
status);
return status;
}
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
Fdo_EvtDevicePrepareHardware(
_In_ WDFDEVICE Device,
_In_ WDFCMRESLIST ResourcesRaw,
_In_ WDFCMRESLIST ResourcesTranslated
)
{
UNREFERENCED_PARAMETER(ResourcesRaw);
BOOLEAN fBar0Found = FALSE;
BOOLEAN fBar4Found = FALSE;
NTSTATUS status;
PFDO_CONTEXT fdoCtx;
ULONG resourceCount;
fdoCtx = Fdo_GetContext(Device);
resourceCount = WdfCmResourceListGetCount(ResourcesTranslated);
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"%s\n", __func__);
status = WdfFdoQueryForInterface(Device, &GUID_BUS_INTERFACE_STANDARD, (PINTERFACE)&fdoCtx->BusInterface, sizeof(BUS_INTERFACE_STANDARD), PCI_BUS_INTERFACE_STANDARD_VERSION, NULL);
if (!NT_SUCCESS(status)) {
return status;
}
for (ULONG i = 0; i < resourceCount; i++)
{
PCM_PARTIAL_RESOURCE_DESCRIPTOR pDescriptor;
pDescriptor = WdfCmResourceListGetDescriptor(
ResourcesTranslated, i);
switch (pDescriptor->Type)
{
case CmResourceTypeMemory:
//Look for BAR0 and BAR4
if (fBar0Found == FALSE) {
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"Found BAR0: 0x%llx (size 0x%lx)\n", pDescriptor->u.Memory.Start.QuadPart, pDescriptor->u.Memory.Length);
fdoCtx->m_BAR0.Base.Base = MmMapIoSpace(pDescriptor->u.Memory.Start, pDescriptor->u.Memory.Length, MmNonCached);
fdoCtx->m_BAR0.Len = pDescriptor->u.Memory.Length;
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"Mapped to %p\n", fdoCtx->m_BAR0.Base.baseptr);
fBar0Found = TRUE;
}
else if (fBar4Found == FALSE) {
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"Found BAR4: 0x%llx (size 0x%lx)\n", pDescriptor->u.Memory.Start.QuadPart, pDescriptor->u.Memory.Length);
//BAR4 is an optional ADSP memory mapping
fdoCtx->m_BAR4.Base.Base = MmMapIoSpace(pDescriptor->u.Memory.Start, pDescriptor->u.Memory.Length, MmNonCached);
fdoCtx->m_BAR4.Len = pDescriptor->u.Memory.Length;
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"Mapped to %p\n", fdoCtx->m_BAR4.Base.baseptr);
fBar4Found = TRUE;
}
break;
}
}
if (fdoCtx->m_BAR0.Base.Base == NULL) {
status = STATUS_NOT_FOUND; //BAR0 is required
return status;
}
fdoCtx->BusInterface.GetBusData(fdoCtx->BusInterface.Context, PCI_WHICHSPACE_CONFIG, &fdoCtx->venId, FIELD_OFFSET(PCI_COMMON_HEADER, VendorID), sizeof(UINT16));
fdoCtx->BusInterface.GetBusData(fdoCtx->BusInterface.Context, PCI_WHICHSPACE_CONFIG, &fdoCtx->devId, FIELD_OFFSET(PCI_COMMON_HEADER, DeviceID), sizeof(UINT16));
fdoCtx->BusInterface.GetBusData(fdoCtx->BusInterface.Context, PCI_WHICHSPACE_CONFIG, &fdoCtx->revId, FIELD_OFFSET(PCI_COMMON_HEADER, RevisionID), sizeof(UINT8));
//mlcap & lctl (hda_intel_init_chip)
if (fdoCtx->venId == VEN_INTEL) {
//read bus capabilities
unsigned int cur_cap;
unsigned int offset;
unsigned int counter = 0;
offset = hda_read16(fdoCtx, LLCH);
#define HDAC_MAX_CAPS 10
/* Lets walk the linked capabilities list */
do {
cur_cap = read32(fdoCtx->m_BAR0.Base.baseptr + offset);
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"Capability version: 0x%x\n",
(cur_cap & HDA_CAP_HDR_VER_MASK) >> HDA_CAP_HDR_VER_OFF);
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"HDA capability ID: 0x%x\n",
(cur_cap & HDA_CAP_HDR_ID_MASK) >> HDA_CAP_HDR_ID_OFF);
if (cur_cap == (unsigned int)-1) {
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"Invalid capability reg read\n");
break;
}
switch ((cur_cap & HDA_CAP_HDR_ID_MASK) >> HDA_CAP_HDR_ID_OFF) {
case HDA_ML_CAP_ID:
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"Found ML capability\n");
fdoCtx->mlcap = fdoCtx->m_BAR0.Base.baseptr + offset;
break;
case HDA_GTS_CAP_ID:
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"Found GTS capability offset=%x\n", offset);
break;
case HDA_PP_CAP_ID:
/* PP capability found, the Audio DSP is present */
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"Found PP capability offset=%x\n", offset);
fdoCtx->ppcap = fdoCtx->m_BAR0.Base.baseptr + offset;
break;
case HDA_SPB_CAP_ID:
/* SPIB capability found, handler function */
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"Found SPB capability\n");
fdoCtx->spbcap = fdoCtx->m_BAR0.Base.baseptr + offset;
break;
case HDA_DRSM_CAP_ID:
/* DMA resume capability found, handler function */
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"Found DRSM capability\n");
break;
default:
SklHdAudBusPrint(DEBUG_LEVEL_ERROR, DBG_INIT, "Unknown capability %d\n", cur_cap);
cur_cap = 0;
break;
}
counter++;
if (counter > HDAC_MAX_CAPS) {
SklHdAudBusPrint(DEBUG_LEVEL_ERROR, DBG_INIT, "We exceeded HDAC capabilities!!!\n");
break;
}
/* read the offset of next capability */
offset = cur_cap & HDA_CAP_HDR_NXT_PTR_MASK;
} while (offset);
}
status = GetHDACapabilities(fdoCtx);
if (!NT_SUCCESS(status)) {
return status;
}
fdoCtx->streams = (PHDAC_STREAM)ExAllocatePoolZero(NonPagedPool, sizeof(HDAC_STREAM) * fdoCtx->numStreams, SKLHDAUDBUS_POOL_TAG);
if (!fdoCtx->streams) {
return STATUS_NO_MEMORY;
}
PHYSICAL_ADDRESS maxAddr;
maxAddr.QuadPart = fdoCtx->is64BitOK ? MAXULONG64 : MAXULONG32;
fdoCtx->posbuf = MmAllocateContiguousMemory(PAGE_SIZE, maxAddr);
if (!fdoCtx->posbuf) {
return STATUS_NO_MEMORY;
}
RtlZeroMemory(fdoCtx->posbuf, PAGE_SIZE);
fdoCtx->rb = (UINT8 *)MmAllocateContiguousMemory(PAGE_SIZE, maxAddr);
if (!fdoCtx->rb) {
return STATUS_NO_MEMORY;
}
RtlZeroMemory(fdoCtx->rb, PAGE_SIZE);
//Init Streams
{
UINT8 i;
UINT8 streamTags[2] = { 0, 0 };
for (i = 0; i < fdoCtx->numStreams; i++) {
int isCapture = (i >= fdoCtx->captureIndexOff &&
i < fdoCtx->captureIndexOff + fdoCtx->captureStreams);
/* stream tag must be unique throughout
* the stream direction group,
* valid values 1...15
* use separate stream tag
*/
UINT8 tag = ++streamTags[isCapture];
{
UINT64 idx = i;
PHDAC_STREAM stream = &fdoCtx->streams[i];
stream->FdoContext = fdoCtx;
/* offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
stream->sdAddr = fdoCtx->m_BAR0.Base.baseptr + (0x20 * idx + 0x80);
/* int mask: SDI0=0x01, SDI1=0x02, ... SDO3=0x80 */
stream->int_sta_mask = 1 << i;
stream->idx = i;
if (fdoCtx->venId == VEN_INTEL)
stream->streamTag = tag;
else
stream->streamTag = i + 1;
stream->posbuf = (UINT32 *)(((UINT8 *)fdoCtx->posbuf) + (idx * 8));
stream->spib_addr = NULL;
if (fdoCtx->spbcap) {
stream->spib_addr = fdoCtx->spbcap + HDA_SPB_BASE + (HDA_SPB_INTERVAL * idx) + HDA_SPB_SPIB;
}
stream->bdl = (PHDAC_BDLENTRY)MmAllocateContiguousMemory(BDL_SIZE, maxAddr);
if (stream->bdl) {
RtlZeroMemory(stream->bdl, BDL_SIZE);
}
}
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"Stream tag (idx %d): %d\n", i, tag);
}
}
fdoCtx->nhlt = NULL;
fdoCtx->nhltSz = 0;
{ //Check NHLT for Intel SST
NTSTATUS status2 = NHLTCheckSupported(Device);
if (NT_SUCCESS(status2)) {
UINT64 nhltAddr;
UINT64 nhltSz;
status2 = NHLTQueryTableAddress(Device, &nhltAddr, &nhltSz);
if (NT_SUCCESS(status2)) {
PHYSICAL_ADDRESS nhltBaseAddr;
nhltBaseAddr.QuadPart = nhltAddr;
if (nhltAddr != 0 && nhltSz != 0) {
fdoCtx->nhlt = MmMapIoSpace(nhltBaseAddr, nhltSz, MmCached);
if (!fdoCtx->nhlt) {
return STATUS_NO_MEMORY;
}
fdoCtx->nhltSz = nhltSz;
}
}
}
}
fdoCtx->sofTplg = NULL;
fdoCtx->sofTplgSz = 0;
{ //Check topology for Intel SOF
SOF_TPLG sofTplg = { 0 };
NTSTATUS status2 = GetSOFTplg(Device, &sofTplg);
if (NT_SUCCESS(status2) && sofTplg.magic == SOFTPLG_MAGIC) {
fdoCtx->sofTplg = ExAllocatePoolUninitialized(NonPagedPool, sofTplg.length, SKLHDAUDBUS_POOL_TAG);
RtlCopyMemory(fdoCtx->sofTplg, &sofTplg, sofTplg.length);
fdoCtx->sofTplgSz = sofTplg.length;
}
}
status = STATUS_SUCCESS;
return status;
}
NTSTATUS
NTAPI
Fdo_EvtDeviceReleaseHardware(
_In_ WDFDEVICE Device,
_In_ WDFCMRESLIST ResourcesTranslated
)
{
PFDO_CONTEXT fdoCtx;
UNREFERENCED_PARAMETER(ResourcesTranslated);
fdoCtx = Fdo_GetContext(Device);
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"%s\n", __func__);
if (fdoCtx->GraphicsDevicesCollection) {
for (ULONG i = 0; i < WdfCollectionGetCount(fdoCtx->GraphicsDevicesCollection); i++) {
WDFIOTARGET ioTarget = (WDFIOTARGET)WdfCollectionGetItem(fdoCtx->GraphicsDevicesCollection, i);
PGRAPHICSIOTARGET_CONTEXT ioTargetContext = GraphicsIoTarget_GetContext(ioTarget);
if (ioTargetContext->graphicsPowerRegisterOutput.DeviceHandle && ioTargetContext->graphicsPowerRegisterOutput.UnregisterCb) {
NTSTATUS status = ioTargetContext->graphicsPowerRegisterOutput.UnregisterCb(ioTargetContext->graphicsPowerRegisterOutput.DeviceHandle, fdoCtx);
if (!NT_SUCCESS(status)) {
SklHdAudBusPrint(DEBUG_LEVEL_ERROR, DBG_INIT, "Warning: unregister failed with status 0x%x\n", status);
}
}
}
}
if (fdoCtx->GraphicsNotificationHandle) {
IoUnregisterPlugPlayNotification(fdoCtx->GraphicsNotificationHandle);
}
if (fdoCtx->nhlt) {
MmUnmapIoSpace(fdoCtx->nhlt, fdoCtx->nhltSz);
fdoCtx->nhlt = NULL;
}
if (fdoCtx->sofTplg)
ExFreePoolWithTag(fdoCtx->sofTplg, SKLHDAUDBUS_POOL_TAG);
if (fdoCtx->posbuf)
MmFreeContiguousMemory(fdoCtx->posbuf);
if (fdoCtx->rb)
MmFreeContiguousMemory(fdoCtx->rb);
if (fdoCtx->streams) {
for (UINT32 i = 0; i < fdoCtx->numStreams; i++) {
PHDAC_STREAM stream = &fdoCtx->streams[i];
if (stream->bdl) {
MmFreeContiguousMemory(stream->bdl);
stream->bdl = NULL;
}
}
ExFreePoolWithTag(fdoCtx->streams, SKLHDAUDBUS_POOL_TAG);
}
if (fdoCtx->m_BAR0.Base.Base) {
MmUnmapIoSpace(fdoCtx->m_BAR0.Base.Base, fdoCtx->m_BAR0.Len);
fdoCtx->m_BAR0.Base.Base = NULL;
}
if (fdoCtx->m_BAR4.Base.Base) {
MmUnmapIoSpace(fdoCtx->m_BAR4.Base.Base, fdoCtx->m_BAR4.Len);
fdoCtx->m_BAR4.Base.Base = NULL;
}
return STATUS_SUCCESS;
}
#define ENABLE_HDA 1
NTSTATUS
NTAPI
Fdo_EvtDeviceD0Entry(
_In_ WDFDEVICE Device,
_In_ WDF_POWER_DEVICE_STATE PreviousState
)
{
UNREFERENCED_PARAMETER(PreviousState);
NTSTATUS status;
PFDO_CONTEXT fdoCtx;
fdoCtx = Fdo_GetContext(Device);
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"%s\n", __func__);
status = STATUS_SUCCESS;
if (fdoCtx->venId == VEN_INTEL) {
UINT32 val;
pci_read_cfg_dword(&fdoCtx->BusInterface, INTEL_HDA_CGCTL, &val);
val = val & ~INTEL_HDA_CGCTL_MISCBDCGE;
pci_write_cfg_dword(&fdoCtx->BusInterface, INTEL_HDA_CGCTL, val);
}
else if (fdoCtx->venId == VEN_AMD || fdoCtx->venId == VEN_ATI) {
update_pci_byte(&fdoCtx->BusInterface, ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR, 0x07, ATI_SB450_HDAUDIO_ENABLE_SNOOP);
}
else if (fdoCtx->venId == VEN_NVIDIA) {
update_pci_byte(&fdoCtx->BusInterface, NVIDIA_HDA_TRANSREG_ADDR, 0x0f, NVIDIA_HDA_ENABLE_COHBIT);
update_pci_byte(&fdoCtx->BusInterface, NVIDIA_HDA_ISTRM_COH, 0x01, NVIDIA_HDA_ENABLE_COHBIT);
update_pci_byte(&fdoCtx->BusInterface, NVIDIA_HDA_OSTRM_COH, 0x01, NVIDIA_HDA_ENABLE_COHBIT);
}
//Reset CORB / RIRB
RtlZeroMemory(&fdoCtx->corb, sizeof(fdoCtx->corb));
RtlZeroMemory(&fdoCtx->rirb, sizeof(fdoCtx->rirb));
fdoCtx->processRirb = FALSE;
status = StartHDAController(fdoCtx);
if (fdoCtx->venId == VEN_INTEL) {
UINT32 val;
pci_read_cfg_dword(&fdoCtx->BusInterface, INTEL_HDA_CGCTL, &val);
val = val | INTEL_HDA_CGCTL_MISCBDCGE;
pci_write_cfg_dword(&fdoCtx->BusInterface, INTEL_HDA_CGCTL, val);
hda_update32(fdoCtx, VS_EM2, HDA_VS_EM2_DUM, HDA_VS_EM2_DUM);
}
if (!NT_SUCCESS(status)) {
return status;
}
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"hda bus initialized\n");
return status;
}
NTSTATUS
NTAPI
Fdo_EvtDeviceD0EntryPostInterrupts(
_In_ WDFDEVICE Device,
_In_ WDF_POWER_DEVICE_STATE PreviousState
)
{
UNREFERENCED_PARAMETER(PreviousState);
NTSTATUS status;
PFDO_CONTEXT fdoCtx;
status = STATUS_SUCCESS;
fdoCtx = Fdo_GetContext(Device);
#if ENABLE_HDA
for (UINT8 addr = 0; addr < HDA_MAX_CODECS; addr++) {
KeInitializeEvent(&fdoCtx->rirb.xferEvent[addr], NotificationEvent, FALSE);
if (((fdoCtx->codecMask >> addr) & 0x1) == 0)
continue;
if (fdoCtx->UseSGPCCodec && fdoCtx->GraphicsCodecAddress == addr)
continue;
UINT32 cmdTmpl = (addr << 28) | (AC_NODE_ROOT << 20) |
(AC_VERB_PARAMETERS << 8);
ULONG vendorDevice;
if (!NT_SUCCESS(RunSingleHDACmd(fdoCtx, cmdTmpl | AC_PAR_VENDOR_ID, &vendorDevice))) { //Some codecs might need a kickstart
//First attempt failed. Retry
NTSTATUS status2 = RunSingleHDACmd(fdoCtx, cmdTmpl | AC_PAR_VENDOR_ID, &vendorDevice); //If this fails, something is wrong.
if (!NT_SUCCESS(status2)) {
SklHdAudBusPrint(DEBUG_LEVEL_ERROR, DBG_INIT, "Warning: Failed to wake up codec %d: 0x%x", addr, status2);
}
}
}
#endif
return status;
}
NTSTATUS
NTAPI
Fdo_EvtDeviceD0Exit(
_In_ WDFDEVICE Device,
_In_ WDF_POWER_DEVICE_STATE TargetState
)
{
UNREFERENCED_PARAMETER(TargetState);
NTSTATUS status;
PFDO_CONTEXT fdoCtx;
fdoCtx = Fdo_GetContext(Device);
status = StopHDAController(fdoCtx);
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"%s\n", __func__);
return status;
}
void
NTAPI
Fdo_EnumerateCodec(
PFDO_CONTEXT fdoCtx,
UINT8 addr
)
{
UINT32 cmdTmpl = (addr << 28) | (AC_NODE_ROOT << 20) |
(AC_VERB_PARAMETERS << 8);
ULONG funcType = 0, vendorDevice, subsysId, revId, nodeCount;
if (!NT_SUCCESS(RunSingleHDACmd(fdoCtx, cmdTmpl | AC_PAR_VENDOR_ID, &vendorDevice))) {
return;
}
if (!NT_SUCCESS(RunSingleHDACmd(fdoCtx, cmdTmpl | AC_PAR_REV_ID, &revId))) {
return;
}
if (!NT_SUCCESS(RunSingleHDACmd(fdoCtx, cmdTmpl | AC_PAR_NODE_COUNT, &nodeCount))) {
return;
}
fdoCtx->numCodecs += 1;
UINT8 startID = (nodeCount >> 16) & 0xFF;
nodeCount = (nodeCount & 0x7FFF);
UINT16 mainFuncGrp = 0;
{
UINT16 nid = startID;
for (UINT32 i = 0; i < nodeCount; i++, nid++) {
UINT32 cmd = (addr << 28) | (nid << 20) |
(AC_VERB_PARAMETERS << 8) | AC_PAR_FUNCTION_TYPE;
if (!NT_SUCCESS(RunSingleHDACmd(fdoCtx, cmd, &funcType))) {
continue;
}
switch (funcType & 0xFF) {
case AC_GRP_AUDIO_FUNCTION:
case AC_GRP_MODEM_FUNCTION:
mainFuncGrp = nid;
break;
}
}
}
UINT32 cmd = (addr << 28) | (mainFuncGrp << 20) |
(AC_VERB_GET_SUBSYSTEM_ID << 8);
RunSingleHDACmd(fdoCtx, cmd, &subsysId);
PDO_IDENTIFICATION_DESCRIPTION description;
//
// Initialize the description with the information about the detected codec.
//
WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT(
&description.Header,
sizeof(description)
);
description.FdoContext = fdoCtx;
description.CodecIds.CtlrDevId = fdoCtx->devId;
description.CodecIds.CtlrVenId = fdoCtx->venId;
description.CodecIds.CodecAddress = addr;
if (fdoCtx->UseSGPCCodec && addr == fdoCtx->GraphicsCodecAddress)
description.CodecIds.IsGraphicsCodec = TRUE;
else
description.CodecIds.IsGraphicsCodec = FALSE;
description.CodecIds.FunctionGroupStartNode = startID;
description.CodecIds.IsDSP = FALSE;
description.CodecIds.FuncId = funcType & 0xFF;
description.CodecIds.VenId = (vendorDevice >> 16) & 0xFFFF;
description.CodecIds.DevId = vendorDevice & 0xFFFF;
description.CodecIds.SubsysId = subsysId;
description.CodecIds.RevId = (revId >> 8) & 0xFFFF;
//
// Call the framework to add this child to the childlist. This call
// will internaly call our DescriptionCompare callback to check
// whether this device is a new device or existing device. If
// it's a new device, the framework will call DescriptionDuplicate to create
// a copy of this description in nonpaged pool.
// The actual creation of the child device will happen when the framework
// receives QUERY_DEVICE_RELATION request from the PNP manager in
// response to InvalidateDeviceRelations call made as part of adding
// a new child.
//
WdfChildListAddOrUpdateChildDescriptionAsPresent(
WdfFdoGetDefaultChildList(fdoCtx->WdfDevice), &description.Header,
NULL); // AddressDescription
}
NTSTATUS
NTAPI
Fdo_EvtDeviceSelfManagedIoInit(
_In_ WDFDEVICE Device
)
{
NTSTATUS status = STATUS_SUCCESS;
PFDO_CONTEXT fdoCtx;
fdoCtx = Fdo_GetContext(Device);
WdfChildListBeginScan(WdfFdoGetDefaultChildList(Device));
fdoCtx->numCodecs = 0;
#if ENABLE_HDA
for (UINT8 addr = 0; addr < HDA_MAX_CODECS; addr++) {
fdoCtx->codecs[addr] = NULL;
if (((fdoCtx->codecMask >> addr) & 0x1) == 0)
continue;
if (fdoCtx->UseSGPCCodec && fdoCtx->GraphicsCodecAddress == addr)
continue;
Fdo_EnumerateCodec(fdoCtx, addr);
}
if (fdoCtx->mlcap) {
IoRegisterPlugPlayNotification(
EventCategoryDeviceInterfaceChange,
PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
(PVOID)&GUID_DEVINTERFACE_GRAPHICSPOWER,
WdfDriverWdmGetDriverObject(WdfDeviceGetDriver(fdoCtx->WdfDevice)),
HDAGraphicsPowerInterfaceCallback,
(PVOID)fdoCtx,
&fdoCtx->GraphicsNotificationHandle
);
}
#endif
fdoCtx->dspInterruptCallback = NULL;
if (fdoCtx->m_BAR4.Base.Base) { //Populate ADSP if present
PDO_IDENTIFICATION_DESCRIPTION description;
//
// Initialize the description with the information about the detected codec.
//
WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT(
&description.Header,
sizeof(description)
);
description.FdoContext = fdoCtx;
description.CodecIds.CtlrDevId = fdoCtx->devId;
description.CodecIds.CtlrVenId = fdoCtx->venId;
description.CodecIds.CodecAddress = 0x10000000;
description.CodecIds.IsDSP = TRUE;
//
// Call the framework to add this child to the childlist. This call
// will internaly call our DescriptionCompare callback to check
// whether this device is a new device or existing device. If
// it's a new device, the framework will call DescriptionDuplicate to create
// a copy of this description in nonpaged pool.
// The actual creation of the child device will happen when the framework
// receives QUERY_DEVICE_RELATION request from the PNP manager in
// response to InvalidateDeviceRelations call made as part of adding
// a new child.
//
status = WdfChildListAddOrUpdateChildDescriptionAsPresent(
WdfFdoGetDefaultChildList(Device), &description.Header,
NULL); // AddressDescription
}
WdfChildListEndScan(WdfFdoGetDefaultChildList(Device));
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"hda scan complete\n");
return status;
}

View File

@@ -0,0 +1,179 @@
#if !defined(_SKLHDAUDBUS_FDO_H_)
#define _SKLHDAUDBUS_FDO_H_
union baseaddr {
PVOID Base;
UINT8* baseptr;
};
typedef struct _PCI_BAR {
union baseaddr Base;
ULONG Len;
} PCI_BAR, * PPCI_BAR;
#include "adsp.h"
#define HDA_UNSOL_QUEUE_SIZE 64
#define MAX_NOTIF_EVENTS 16
#define HDA_MAX_CODECS 16 /* limit by controller side */
struct _FDO_CONTEXT;
struct _PDO_DEVICE_DATA;
typedef struct _HDAC_STREAM_CALLBACK {
BOOLEAN InUse;
PDEVICE_OBJECT Fdo;
PHDAUDIO_DMA_NOTIFICATION_CALLBACK NotificationCallback;
PVOID CallbackContext;
} HDAC_STREAM_CALLBACK, *PHDAC_STREAM_CALLBACK;
typedef struct _HDAC_ISR_CALLBACK {
BOOLEAN IOC;
PHDAUDIO_BDL_ISR IsrCallback;
PVOID CallbackContext;
} HDAC_ISR_CALLBACK, *PHDAC_ISR_CALLBACK;
typedef struct _HDAC_BDLENTRY {
UINT32 lowAddr;
UINT32 highAddr;
UINT32 len;
UINT32 ioc;
} HDAC_BDLENTRY, *PHDAC_BDLENTRY;
typedef struct _HDAC_STREAM {
struct _FDO_CONTEXT* FdoContext;
struct _PDO_DEVICE_DATA* PdoContext;
PMDL mdlBuf;
UINT32* posbuf;
PVOID dmaBuf;
HDAC_BDLENTRY* bdl;
BOOLEAN stripe;
UINT32 bufSz;
UINT32 periodBytes;
UINT32 fifoSize;
UINT16 numBlocks;
UINT8* sdAddr;
UINT32 int_sta_mask;
UINT8* spib_addr; //spbcap
UINT8 streamTag;
UINT8 idx;
HDAUDIO_STREAM_FORMAT streamFormat;
PKEVENT registeredEvents[MAX_NOTIF_EVENTS];
HDAC_STREAM_CALLBACK registeredCallbacks[MAX_NOTIF_EVENTS];
HDAC_ISR_CALLBACK isr;
BOOLEAN running;
BOOLEAN irqReceived;
} HDAC_STREAM, *PHDAC_STREAM;
typedef struct _HDAC_RIRB {
UINT32 response;
UINT32 response_ex;
} HDAC_RIRB, *PHDAC_RIRB;
typedef struct _HDAC_CODEC_XFER {
PHDAUDIO_CODEC_TRANSFER xfer[HDA_MAX_CORB_ENTRIES];
} HDAC_CODEC_XFER, *PHDAC_CODEC_XFER;
typedef struct _HDAC_RB {
union {
UINT32* buf;
PHDAC_RIRB rirbbuf;
};
PHYSICAL_ADDRESS addr;
UINT16 rp, wp;
LONG cmds[HDA_MAX_CODECS];
KEVENT xferEvent[HDA_MAX_CODECS];
HDAC_CODEC_XFER xfer[HDA_MAX_CODECS];
} HDAC_RB, *PHDAC_RB;
typedef struct _GRAPHICSWORKITEM_CONTEXT {
struct _FDO_CONTEXT* FdoContext;
UNICODE_STRING GPUDeviceSymlink;
} GRAPHICSWORKITEM_CONTEXT, *PGRAPHICSWORKITEM_CONTEXT;
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(GRAPHICSWORKITEM_CONTEXT, GraphicsWorkitem_GetContext)
typedef struct _GRAPHICSIOTARGET_CONTEXT {
struct _FDO_CONTEXT* FdoContext;
DXGK_GRAPHICSPOWER_REGISTER_OUTPUT graphicsPowerRegisterOutput;
} GRAPHICSIOTARGET_CONTEXT, *PGRAPHICSIOTARGET_CONTEXT;
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(GRAPHICSIOTARGET_CONTEXT, GraphicsIoTarget_GetContext)
typedef struct _FDO_CONTEXT
{
WDFDEVICE WdfDevice;
UINT16 venId;
UINT16 devId;
UINT8 revId;
PCI_BAR m_BAR0; //required
PCI_BAR m_BAR4; //Intel AudioDSP
BUS_INTERFACE_STANDARD BusInterface; //PCI Bus Interface
WDFINTERRUPT Interrupt;
//Graphics Notifications
PVOID GraphicsNotificationHandle;
WDFWAITLOCK GraphicsDevicesCollectionWaitLock;
WDFCOLLECTION GraphicsDevicesCollection;
ULONG GraphicsCodecAddress;
BOOLEAN UseSGPCCodec;
BOOLEAN GraphicsCodecConnected;
UINT8 *mlcap;
UINT8 *ppcap;
UINT8 *spbcap;
BOOLEAN ControllerEnabled;
BOOLEAN is64BitOK;
UINT16 hwVersion;
UINT32 captureIndexOff;
UINT32 playbackIndexOff;
UINT32 captureStreams;
UINT32 playbackStreams;
UINT32 numStreams;
PHDAC_STREAM streams;
struct _PDO_DEVICE_DATA* codecs[HDA_MAX_CODECS];
PADSP_INTERRUPT_CALLBACK dspInterruptCallback;
PVOID dspInterruptContext;
PVOID nhlt;
UINT64 nhltSz;
PVOID sofTplg;
UINT64 sofTplgSz;
//bit flags of detected codecs
UINT16 codecMask;
USHORT numCodecs;
HDAC_RB corb;
HDAC_RB rirb;
BOOL processRirb;
UINT8 *rb; //CORB and RIRB buffers
PVOID posbuf;
} FDO_CONTEXT, * PFDO_CONTEXT;
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(FDO_CONTEXT, Fdo_GetContext)
NTSTATUS
NTAPI
Fdo_Create(
_Inout_ PWDFDEVICE_INIT DeviceInit
);
#endif

View File

@@ -0,0 +1,321 @@
#define HDA_REG_GCAP 0x00
#define HDA_GCAP_64OK (1 << 0) /* 64bit address support */
#define HDA_GCAP_NSDO (3 << 1) /* # of serial data out signals */
#define HDA_GCAP_BSS (31 << 3) /* # of bidirectional streams */
#define HDA_GCAP_ISS (15 << 8) /* # of input streams */
#define HDA_GCAP_OSS (15 << 12) /* # of output streams */
#define HDA_REG_VMIN 0x02
#define HDA_REG_VMAJ 0x03
#define HDA_REG_OUTPAY 0x04
#define HDA_REG_INPAY 0x06
#define HDA_REG_GCTL 0x08
#define HDA_GCTL_RESET (1 << 0) /* controller reset */
#define HDA_GCTL_FCNTRL (1 << 1) /* flush control */
#define HDA_GCTL_UNSOL (1 << 8) /* accept unsol. response enable */
#define HDA_REG_WAKEEN 0x0c
#define HDA_REG_STATESTS 0x0e
#define HDA_REG_GSTS 0x10
#define HDA_GSTS_FSTS (1 << 1) /* flush status */
#define HDA_REG_GCAP2 0x12
#define HDA_REG_LLCH 0x14
#define HDA_REG_OUTSTRMPAY 0x18
#define HDA_REG_INSTRMPAY 0x1A
#define HDA_REG_INTCTL 0x20
#define HDA_REG_INTSTS 0x24
#define HDA_REG_WALLCLK 0x30 /* 24Mhz source */
#define HDA_REG_WALLCLKA 0x2030 /* 24Mhz source */
#define HDA_REG_OLD_SSYNC 0x34 /* SSYNC for old ICH */
#define HDA_REG_SSYNC 0x38
#define HDA_REG_CORBLBASE 0x40
#define HDA_REG_CORBUBASE 0x44
#define HDA_REG_CORBWP 0x48
#define HDA_REG_CORBRP 0x4a
#define HDA_CORBRP_RST (1 << 15) /* read pointer reset */
#define HDA_REG_CORBCTL 0x4c
#define HDA_CORBCTL_RUN (1 << 1) /* enable DMA */
#define HDA_CORBCTL_CMEIE (1 << 0) /* enable memory error irq */
#define HDA_REG_CORBSTS 0x4d
#define HDA_CORBSTS_CMEI (1 << 0) /* memory error indication */
#define HDA_REG_CORBSIZE 0x4e
#define HDA_REG_RIRBLBASE 0x50
#define HDA_REG_RIRBUBASE 0x54
#define HDA_REG_RIRBWP 0x58
#define HDA_RIRBWP_RST (1 << 15) /* write pointer reset */
#define HDA_REG_RINTCNT 0x5a
#define HDA_REG_RIRBCTL 0x5c
#define HDA_RBCTL_IRQ_EN (1 << 0) /* enable IRQ */
#define HDA_RBCTL_DMA_EN (1 << 1) /* enable DMA */
#define HDA_RBCTL_OVERRUN_EN (1 << 2) /* enable overrun irq */
#define HDA_REG_RIRBSTS 0x5d
#define HDA_RBSTS_IRQ (1 << 0) /* response irq */
#define HDA_RBSTS_OVERRUN (1 << 2) /* overrun irq */
#define HDA_REG_RIRBSIZE 0x5e
#define HDA_REG_IC 0x60
#define HDA_REG_IR 0x64
#define HDA_REG_IRS 0x68
#define HDA_IRS_VALID (1<<1)
#define HDA_IRS_BUSY (1<<0)
#define HDA_REG_DPLBASE 0x70
#define HDA_REG_DPUBASE 0x74
#define HDA_DPLBASE_ENABLE 0x1 /* Enable position buffer */
/* SD offset: SDI0=0x80, SDI1=0xa0, ... SDO3=0x160 */
enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
/* stream register offsets from stream base */
#define HDA_REG_SD_CTL 0x00
#define HDA_REG_SD_CTL_3B 0x02 /* 3rd byte of SD_CTL register */
#define HDA_REG_SD_STS 0x03
#define HDA_REG_SD_LPIB 0x04
#define HDA_REG_SD_CBL 0x08
#define HDA_REG_SD_LVI 0x0c
#define HDA_REG_SD_FIFOW 0x0e
#define HDA_REG_SD_FIFOSIZE 0x10
#define HDA_REG_SD_FORMAT 0x12
#define HDA_REG_SD_FIFOL 0x14
#define HDA_REG_SD_BDLPL 0x18
#define HDA_REG_SD_BDLPU 0x1c
#define HDA_REG_SD_LPIBA 0x2004
/* GTS registers */
#define HDA_REG_LLCH 0x14
#define HDA_REG_GTS_BASE 0x520
#define HDA_REG_GTSCC (HDA_REG_GTS_BASE + 0x00)
#define HDA_REG_WALFCC (HDA_REG_GTS_BASE + 0x04)
#define HDA_REG_TSCCL (HDA_REG_GTS_BASE + 0x08)
#define HDA_REG_TSCCU (HDA_REG_GTS_BASE + 0x0C)
#define HDA_REG_LLPFOC (HDA_REG_GTS_BASE + 0x14)
#define HDA_REG_LLPCL (HDA_REG_GTS_BASE + 0x18)
#define HDA_REG_LLPCU (HDA_REG_GTS_BASE + 0x1C)
/* Haswell/Broadwell display HD-A controller Extended Mode registers */
#define HDA_REG_HSW_EM4 0x100c
#define HDA_REG_HSW_EM5 0x1010
/* Skylake/Broxton vendor-specific registers */
#define HDA_REG_VS_EM1 0x1000
#define HDA_REG_VS_INRC 0x1004
#define HDA_REG_VS_OUTRC 0x1008
#define HDA_REG_VS_FIFOTRK 0x100C
#define HDA_REG_VS_FIFOTRK2 0x1010
#define HDA_REG_VS_EM2 0x1030
#define HDA_REG_VS_EM3L 0x1038
#define HDA_REG_VS_EM3U 0x103C
#define HDA_REG_VS_EM4L 0x1040
#define HDA_REG_VS_EM4U 0x1044
#define HDA_REG_VS_LTRP 0x1048
#define HDA_REG_VS_D0I3C 0x104A
#define HDA_REG_VS_PCE 0x104B
#define HDA_REG_VS_L2MAGC 0x1050
#define HDA_REG_VS_L2LAHPT 0x1054
#define HDA_REG_VS_SDXDPIB_XBASE 0x1084
#define HDA_REG_VS_SDXDPIB_XINTERVAL 0x20
#define HDA_REG_VS_SDXEFIFOS_XBASE 0x1094
#define HDA_REG_VS_SDXEFIFOS_XINTERVAL 0x20
/* PCI space */
#define HDA_PCIREG_TCSEL 0x44
/*
* other constants
*/
/* max number of fragments - we may use more if allocating more pages for BDL */
#define BDL_SIZE 4096
#define HDA_MAX_BDL_ENTRIES (BDL_SIZE / 16)
/*
* max buffer size - artificial 4MB limit per stream to avoid big allocations
* In theory it can be really big, but as it is per stream on systems with many streams memory could
* be quickly saturated if userspace requests maximum buffer size for each of them.
*/
#define HDA_MAX_BUF_SIZE (4*1024*1024)
/* RIRB int mask: overrun[2], response[0] */
#define RIRB_INT_RESPONSE 0x01
#define RIRB_INT_OVERRUN 0x04
#define RIRB_INT_MASK 0x05
/* STATESTS int mask: S3,SD2,SD1,SD0 */
#define STATESTS_INT_MASK ((1 << HDA_MAX_CODECS) - 1)
/* SD_CTL bits */
#define SD_CTL_STREAM_RESET 0x01 /* stream reset bit */
#define SD_CTL_DMA_START 0x02 /* stream DMA start bit */
#define SD_CTL_STRIPE (3 << 16) /* stripe control */
#define SD_CTL_TRAFFIC_PRIO (1 << 18) /* traffic priority */
#define SD_CTL_DIR (1 << 19) /* bi-directional stream */
#define SD_CTL_STREAM_TAG_MASK (0xf << 20)
#define SD_CTL_STREAM_TAG_SHIFT 20
/* SD_CTL and SD_STS */
#define SD_INT_DESC_ERR 0x10 /* descriptor error interrupt */
#define SD_INT_FIFO_ERR 0x08 /* FIFO error interrupt */
#define SD_INT_COMPLETE 0x04 /* completion interrupt */
#define SD_INT_MASK (SD_INT_DESC_ERR|SD_INT_FIFO_ERR|\
SD_INT_COMPLETE)
#define SD_CTL_STRIPE_MASK 0x3 /* stripe control mask */
/* SD_STS */
#define SD_STS_FIFO_READY 0x20 /* FIFO ready */
/* INTCTL and INTSTS */
#define HDA_INT_ALL_STREAM 0xff /* all stream interrupts */
#define HDA_INT_CTRL_EN 0x40000000 /* controller interrupt enable bit */
#define HDA_INT_GLOBAL_EN 0x80000000 /* global interrupt enable bit */
/* below are so far hardcoded - should read registers in future */
#define HDA_MAX_CORB_ENTRIES 256
#define HDA_MAX_RIRB_ENTRIES 256
/* Capability header Structure */
#define HDA_REG_CAP_HDR 0x0
#define HDA_CAP_HDR_VER_OFF 28
#define HDA_CAP_HDR_VER_MASK (0xF << HDA_CAP_HDR_VER_OFF)
#define HDA_CAP_HDR_ID_OFF 16
#define HDA_CAP_HDR_ID_MASK (0xFFF << HDA_CAP_HDR_ID_OFF)
#define HDA_CAP_HDR_NXT_PTR_MASK 0xFFFF
/* registers of Software Position Based FIFO Capability Structure */
#define HDA_SPB_CAP_ID 0x4
#define HDA_REG_SPB_BASE_ADDR 0x700
#define HDA_REG_SPB_SPBFCH 0x00
#define HDA_REG_SPB_SPBFCCTL 0x04
/* Base used to calculate the iterating register offset */
#define HDA_SPB_BASE 0x08
/* Interval used to calculate the iterating register offset */
#define HDA_SPB_INTERVAL 0x08
/* SPIB base */
#define HDA_SPB_SPIB 0x00
/* SPIB MAXFIFO base*/
#define HDA_SPB_MAXFIFO 0x04
/* registers of Global Time Synchronization Capability Structure */
#define HDA_GTS_CAP_ID 0x1
#define HDA_REG_GTS_GTSCH 0x00
#define HDA_REG_GTS_GTSCD 0x04
#define HDA_REG_GTS_GTSCTLAC 0x0C
#define HDA_GTS_BASE 0x20
#define HDA_GTS_INTERVAL 0x20
/* registers for Processing Pipe Capability Structure */
#define HDA_PP_CAP_ID 0x3
#define HDA_REG_PP_PPCH 0x10
#define HDA_REG_PP_PPCTL 0x04
#define HDA_PPCTL_PIE (1<<31)
#define HDA_PPCTL_GPROCEN (1<<30)
/* _X_ = dma engine # and cannot * exceed 29 (per spec max 30 dma engines) */
#define HDA_PPCTL_PROCEN(_X_) (1<<(_X_))
#define HDA_REG_PP_PPSTS 0x08
#define HDA_PPHC_BASE 0x10
#define HDA_PPHC_INTERVAL 0x10
#define HDA_REG_PPHCLLPL 0x0
#define HDA_REG_PPHCLLPU 0x4
#define HDA_REG_PPHCLDPL 0x8
#define HDA_REG_PPHCLDPU 0xC
#define HDA_PPLC_BASE 0x10
#define HDA_PPLC_MULTI 0x10
#define HDA_PPLC_INTERVAL 0x10
#define HDA_REG_PPLCCTL 0x0
#define HDA_PPLCCTL_STRM_BITS 4
#define HDA_PPLCCTL_STRM_SHIFT 20
#define HDA_REG_MASK(bit_num, offset) \
(((1 << (bit_num)) - 1) << (offset))
#define HDA_PPLCCTL_STRM_MASK \
HDA_REG_MASK(HDA_PPLCCTL_STRM_BITS, HDA_PPLCCTL_STRM_SHIFT)
#define HDA_PPLCCTL_RUN (1<<1)
#define HDA_PPLCCTL_STRST (1<<0)
#define HDA_REG_PPLCFMT 0x4
#define HDA_REG_PPLCLLPL 0x8
#define HDA_REG_PPLCLLPU 0xC
/* registers for Multiple Links Capability Structure */
#define HDA_ML_CAP_ID 0x2
#define HDA_REG_ML_MLCH 0x00
#define HDA_REG_ML_MLCD 0x04
#define HDA_ML_BASE 0x40
#define HDA_ML_INTERVAL 0x40
#define HDA_REG_ML_LCAP 0x00
#define HDA_REG_ML_LCTL 0x04
#define HDA_REG_ML_LOSIDV 0x08
#define HDA_REG_ML_LSDIID 0x0C
#define HDA_REG_ML_LPSOO 0x10
#define HDA_REG_ML_LPSIO 0x12
#define HDA_REG_ML_LWALFC 0x18
#define HDA_REG_ML_LOUTPAY 0x20
#define HDA_REG_ML_LINPAY 0x30
/* bit0 is reserved, with BIT(1) mapping to stream1 */
#define ML_LOSIDV_STREAM_MASK 0xFFFE
#define ML_LCTL_SCF_MASK 0xF
#define HDA_MLCTL_SPA (0x1 << 16)
#define HDA_MLCTL_CPA (0x1 << 23)
#define HDA_MLCTL_SPA_SHIFT 16
#define HDA_MLCTL_CPA_SHIFT 23
/* registers for DMA Resume Capability Structure */
#define HDA_DRSM_CAP_ID 0x5
#define HDA_REG_DRSM_CTL 0x4
/* Base used to calculate the iterating register offset */
#define HDA_DRSM_BASE 0x08
/* Interval used to calculate the iterating register offset */
#define HDA_DRSM_INTERVAL 0x08
/* Global time synchronization registers */
#define GTSCC_TSCCD_MASK 0x80000000
#define GTSCC_TSCCD_SHIFT BIT(31)
#define GTSCC_TSCCI_MASK 0x20
#define GTSCC_CDMAS_DMA_DIR_SHIFT 4
#define WALFCC_CIF_MASK 0x1FF
#define WALFCC_FN_SHIFT 9
#define HDA_CLK_CYCLES_PER_FRAME 512
/*
* An error occurs near frame "rollover". The clocks in frame value indicates
* whether this error may have occurred. Here we use the value of 10. Please
* see the errata for the right number [<10]
*/
#define HDA_MAX_CYCLE_VALUE 499
#define HDA_MAX_CYCLE_OFFSET 10
#define HDA_MAX_CYCLE_READ_RETRY 10
#define TSCCU_CCU_SHIFT 32
#define LLPC_CCU_SHIFT 32
/* Defines for Intel SCH HDA snoop control */
#define INTEL_HDA_CGCTL 0x48
#define INTEL_HDA_CGCTL_MISCBDCGE (0x1 << 6)
#define INTEL_SCH_HDA_DEVC 0x78
#define INTEL_SCH_HDA_DEVC_NOSNOOP (0x1<<11)
#define HDA_VS_EM2_DUM (1 << 23)
/* Defines for ATI HD Audio support in SB450 south bridge */
#define ATI_SB450_HDAUDIO_MISC_CNTR2_ADDR 0x42
#define ATI_SB450_HDAUDIO_ENABLE_SNOOP 0x02
/* Defines for Nvidia HDA support */
#define NVIDIA_HDA_TRANSREG_ADDR 0x4e
#define NVIDIA_HDA_ENABLE_COHBITS 0x0f
#define NVIDIA_HDA_ISTRM_COH 0x4d
#define NVIDIA_HDA_OSTRM_COH 0x4c
#define NVIDIA_HDA_ENABLE_COHBIT 0x01
/* Defines for Intel SCH HDA snoop control */
#define INTEL_HDA_CGCTL 0x48
#define INTEL_HDA_CGCTL_MISCBDCGE (0x1 << 6)
#define INTEL_SCH_HDA_DEVC 0x78
#define INTEL_SCH_HDA_DEVC_NOSNOOP (0x1<<11)

View File

@@ -0,0 +1,535 @@
#if !defined(_HDA_VERBS_H_)
#define _HDA_VERBS_H_
#define AC_NODE_ROOT 0x00
/*
* function group types
*/
enum {
AC_GRP_AUDIO_FUNCTION = 0x01,
AC_GRP_MODEM_FUNCTION = 0x02,
};
/*
* GET verbs
*/
#define AC_VERB_GET_STREAM_FORMAT 0x0a00
#define AC_VERB_GET_AMP_GAIN_MUTE 0x0b00
#define AC_VERB_GET_PROC_COEF 0x0c00
#define AC_VERB_GET_COEF_INDEX 0x0d00
#define AC_VERB_PARAMETERS 0x0f00
#define AC_VERB_GET_CONNECT_SEL 0x0f01
#define AC_VERB_GET_CONNECT_LIST 0x0f02
#define AC_VERB_GET_PROC_STATE 0x0f03
#define AC_VERB_GET_SDI_SELECT 0x0f04
#define AC_VERB_GET_POWER_STATE 0x0f05
#define AC_VERB_GET_CONV 0x0f06
#define AC_VERB_GET_PIN_WIDGET_CONTROL 0x0f07
#define AC_VERB_GET_UNSOLICITED_RESPONSE 0x0f08
#define AC_VERB_GET_PIN_SENSE 0x0f09
#define AC_VERB_GET_BEEP_CONTROL 0x0f0a
#define AC_VERB_GET_EAPD_BTLENABLE 0x0f0c
#define AC_VERB_GET_DIGI_CONVERT_1 0x0f0d
#define AC_VERB_GET_DIGI_CONVERT_2 0x0f0e /* unused */
#define AC_VERB_GET_VOLUME_KNOB_CONTROL 0x0f0f
/* f10-f1a: GPIO */
#define AC_VERB_GET_GPIO_DATA 0x0f15
#define AC_VERB_GET_GPIO_MASK 0x0f16
#define AC_VERB_GET_GPIO_DIRECTION 0x0f17
#define AC_VERB_GET_GPIO_WAKE_MASK 0x0f18
#define AC_VERB_GET_GPIO_UNSOLICITED_RSP_MASK 0x0f19
#define AC_VERB_GET_GPIO_STICKY_MASK 0x0f1a
#define AC_VERB_GET_CONFIG_DEFAULT 0x0f1c
/* f20: AFG/MFG */
#define AC_VERB_GET_SUBSYSTEM_ID 0x0f20
#define AC_VERB_GET_STRIPE_CONTROL 0x0f24
#define AC_VERB_GET_CVT_CHAN_COUNT 0x0f2d
#define AC_VERB_GET_HDMI_DIP_SIZE 0x0f2e
#define AC_VERB_GET_HDMI_ELDD 0x0f2f
#define AC_VERB_GET_HDMI_DIP_INDEX 0x0f30
#define AC_VERB_GET_HDMI_DIP_DATA 0x0f31
#define AC_VERB_GET_HDMI_DIP_XMIT 0x0f32
#define AC_VERB_GET_HDMI_CP_CTRL 0x0f33
#define AC_VERB_GET_HDMI_CHAN_SLOT 0x0f34
#define AC_VERB_GET_DEVICE_SEL 0xf35
#define AC_VERB_GET_DEVICE_LIST 0xf36
/*
* SET verbs
*/
#define AC_VERB_SET_STREAM_FORMAT 0x200
#define AC_VERB_SET_AMP_GAIN_MUTE 0x300
#define AC_VERB_SET_PROC_COEF 0x400
#define AC_VERB_SET_COEF_INDEX 0x500
#define AC_VERB_SET_CONNECT_SEL 0x701
#define AC_VERB_SET_PROC_STATE 0x703
#define AC_VERB_SET_SDI_SELECT 0x704
#define AC_VERB_SET_POWER_STATE 0x705
#define AC_VERB_SET_CHANNEL_STREAMID 0x706
#define AC_VERB_SET_PIN_WIDGET_CONTROL 0x707
#define AC_VERB_SET_UNSOLICITED_ENABLE 0x708
#define AC_VERB_SET_PIN_SENSE 0x709
#define AC_VERB_SET_BEEP_CONTROL 0x70a
#define AC_VERB_SET_EAPD_BTLENABLE 0x70c
#define AC_VERB_SET_DIGI_CONVERT_1 0x70d
#define AC_VERB_SET_DIGI_CONVERT_2 0x70e
#define AC_VERB_SET_DIGI_CONVERT_3 0x73e
#define AC_VERB_SET_VOLUME_KNOB_CONTROL 0x70f
#define AC_VERB_SET_GPIO_DATA 0x715
#define AC_VERB_SET_GPIO_MASK 0x716
#define AC_VERB_SET_GPIO_DIRECTION 0x717
#define AC_VERB_SET_GPIO_WAKE_MASK 0x718
#define AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK 0x719
#define AC_VERB_SET_GPIO_STICKY_MASK 0x71a
#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_0 0x71c
#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_1 0x71d
#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_2 0x71e
#define AC_VERB_SET_CONFIG_DEFAULT_BYTES_3 0x71f
#define AC_VERB_SET_EAPD 0x788
#define AC_VERB_SET_CODEC_RESET 0x7ff
#define AC_VERB_SET_STRIPE_CONTROL 0x724
#define AC_VERB_SET_CVT_CHAN_COUNT 0x72d
#define AC_VERB_SET_HDMI_DIP_INDEX 0x730
#define AC_VERB_SET_HDMI_DIP_DATA 0x731
#define AC_VERB_SET_HDMI_DIP_XMIT 0x732
#define AC_VERB_SET_HDMI_CP_CTRL 0x733
#define AC_VERB_SET_HDMI_CHAN_SLOT 0x734
#define AC_VERB_SET_DEVICE_SEL 0x735
/*
* Parameter IDs
*/
#define AC_PAR_VENDOR_ID 0x00
#define AC_PAR_SUBSYSTEM_ID 0x01
#define AC_PAR_REV_ID 0x02
#define AC_PAR_NODE_COUNT 0x04
#define AC_PAR_FUNCTION_TYPE 0x05
#define AC_PAR_AUDIO_FG_CAP 0x08
#define AC_PAR_AUDIO_WIDGET_CAP 0x09
#define AC_PAR_PCM 0x0a
#define AC_PAR_STREAM 0x0b
#define AC_PAR_PIN_CAP 0x0c
#define AC_PAR_AMP_IN_CAP 0x0d
#define AC_PAR_CONNLIST_LEN 0x0e
#define AC_PAR_POWER_STATE 0x0f
#define AC_PAR_PROC_CAP 0x10
#define AC_PAR_GPIO_CAP 0x11
#define AC_PAR_AMP_OUT_CAP 0x12
#define AC_PAR_VOL_KNB_CAP 0x13
#define AC_PAR_DEVLIST_LEN 0x15
#define AC_PAR_HDMI_LPCM_CAP 0x20
/*
* AC_VERB_PARAMETERS results (32bit)
*/
/* Function Group Type */
#define AC_FGT_TYPE (0xff<<0)
#define AC_FGT_TYPE_SHIFT 0
#define AC_FGT_UNSOL_CAP (1<<8)
/* Audio Function Group Capabilities */
#define AC_AFG_OUT_DELAY (0xf<<0)
#define AC_AFG_IN_DELAY (0xf<<8)
#define AC_AFG_BEEP_GEN (1<<16)
/* Audio Widget Capabilities */
#define AC_WCAP_STEREO (1<<0) /* stereo I/O */
#define AC_WCAP_IN_AMP (1<<1) /* AMP-in present */
#define AC_WCAP_OUT_AMP (1<<2) /* AMP-out present */
#define AC_WCAP_AMP_OVRD (1<<3) /* AMP-parameter override */
#define AC_WCAP_FORMAT_OVRD (1<<4) /* format override */
#define AC_WCAP_STRIPE (1<<5) /* stripe */
#define AC_WCAP_PROC_WID (1<<6) /* Proc Widget */
#define AC_WCAP_UNSOL_CAP (1<<7) /* Unsol capable */
#define AC_WCAP_CONN_LIST (1<<8) /* connection list */
#define AC_WCAP_DIGITAL (1<<9) /* digital I/O */
#define AC_WCAP_POWER (1<<10) /* power control */
#define AC_WCAP_LR_SWAP (1<<11) /* L/R swap */
#define AC_WCAP_CP_CAPS (1<<12) /* content protection */
#define AC_WCAP_CHAN_CNT_EXT (7<<13) /* channel count ext */
#define AC_WCAP_DELAY (0xf<<16)
#define AC_WCAP_DELAY_SHIFT 16
#define AC_WCAP_TYPE (0xf<<20)
#define AC_WCAP_TYPE_SHIFT 20
/* supported PCM rates and bits */
#define AC_SUPPCM_RATES (0xfff << 0)
#define AC_SUPPCM_BITS_8 (1<<16)
#define AC_SUPPCM_BITS_16 (1<<17)
#define AC_SUPPCM_BITS_20 (1<<18)
#define AC_SUPPCM_BITS_24 (1<<19)
#define AC_SUPPCM_BITS_32 (1<<20)
/* supported PCM stream format */
#define AC_SUPFMT_PCM (1<<0)
#define AC_SUPFMT_FLOAT32 (1<<1)
#define AC_SUPFMT_AC3 (1<<2)
/* GP I/O count */
#define AC_GPIO_IO_COUNT (0xff<<0)
#define AC_GPIO_O_COUNT (0xff<<8)
#define AC_GPIO_O_COUNT_SHIFT 8
#define AC_GPIO_I_COUNT (0xff<<16)
#define AC_GPIO_I_COUNT_SHIFT 16
#define AC_GPIO_UNSOLICITED (1<<30)
#define AC_GPIO_WAKE (1<<31)
/* Converter stream, channel */
#define AC_CONV_CHANNEL (0xf<<0)
#define AC_CONV_STREAM (0xf<<4)
#define AC_CONV_STREAM_SHIFT 4
/* Input converter SDI select */
#define AC_SDI_SELECT (0xf<<0)
/* stream format id */
#define AC_FMT_CHAN_SHIFT 0
#define AC_FMT_CHAN_MASK (0x0f << 0)
#define AC_FMT_BITS_SHIFT 4
#define AC_FMT_BITS_MASK (7 << 4)
#define AC_FMT_BITS_8 (0 << 4)
#define AC_FMT_BITS_16 (1 << 4)
#define AC_FMT_BITS_20 (2 << 4)
#define AC_FMT_BITS_24 (3 << 4)
#define AC_FMT_BITS_32 (4 << 4)
#define AC_FMT_DIV_SHIFT 8
#define AC_FMT_DIV_MASK (7 << 8)
#define AC_FMT_MULT_SHIFT 11
#define AC_FMT_MULT_MASK (7 << 11)
#define AC_FMT_BASE_SHIFT 14
#define AC_FMT_BASE_48K (0 << 14)
#define AC_FMT_BASE_44K (1 << 14)
#define AC_FMT_TYPE_SHIFT 15
#define AC_FMT_TYPE_PCM (0 << 15)
#define AC_FMT_TYPE_NON_PCM (1 << 15)
/* Unsolicited response control */
#define AC_UNSOL_TAG (0x3f<<0)
#define AC_UNSOL_ENABLED (1<<7)
#define AC_USRSP_EN AC_UNSOL_ENABLED
/* Unsolicited responses */
#define AC_UNSOL_RES_TAG (0x3f<<26)
#define AC_UNSOL_RES_TAG_SHIFT 26
#define AC_UNSOL_RES_SUBTAG (0x1f<<21)
#define AC_UNSOL_RES_SUBTAG_SHIFT 21
#define AC_UNSOL_RES_DE (0x3f<<15) /* Device Entry
* (for DP1.2 MST)
*/
#define AC_UNSOL_RES_DE_SHIFT 15
#define AC_UNSOL_RES_IA (1<<2) /* Inactive (for DP1.2 MST) */
#define AC_UNSOL_RES_ELDV (1<<1) /* ELD Data valid (for HDMI) */
#define AC_UNSOL_RES_PD (1<<0) /* pinsense detect */
#define AC_UNSOL_RES_CP_STATE (1<<1) /* content protection */
#define AC_UNSOL_RES_CP_READY (1<<0) /* content protection */
/* Pin widget capabilies */
#define AC_PINCAP_IMP_SENSE (1<<0) /* impedance sense capable */
#define AC_PINCAP_TRIG_REQ (1<<1) /* trigger required */
#define AC_PINCAP_PRES_DETECT (1<<2) /* presence detect capable */
#define AC_PINCAP_HP_DRV (1<<3) /* headphone drive capable */
#define AC_PINCAP_OUT (1<<4) /* output capable */
#define AC_PINCAP_IN (1<<5) /* input capable */
#define AC_PINCAP_BALANCE (1<<6) /* balanced I/O capable */
/* Note: This LR_SWAP pincap is defined in the Realtek ALC883 specification,
* but is marked reserved in the Intel HDA specification.
*/
#define AC_PINCAP_LR_SWAP (1<<7) /* L/R swap */
/* Note: The same bit as LR_SWAP is newly defined as HDMI capability
* in HD-audio specification
*/
#define AC_PINCAP_HDMI (1<<7) /* HDMI pin */
#define AC_PINCAP_DP (1<<24) /* DisplayPort pin, can
* coexist with AC_PINCAP_HDMI
*/
#define AC_PINCAP_VREF (0x37<<8)
#define AC_PINCAP_VREF_SHIFT 8
#define AC_PINCAP_EAPD (1<<16) /* EAPD capable */
#define AC_PINCAP_HBR (1<<27) /* High Bit Rate */
/* Vref status (used in pin cap) */
#define AC_PINCAP_VREF_HIZ (1<<0) /* Hi-Z */
#define AC_PINCAP_VREF_50 (1<<1) /* 50% */
#define AC_PINCAP_VREF_GRD (1<<2) /* ground */
#define AC_PINCAP_VREF_80 (1<<4) /* 80% */
#define AC_PINCAP_VREF_100 (1<<5) /* 100% */
/* Amplifier capabilities */
#define AC_AMPCAP_OFFSET (0x7f<<0) /* 0dB offset */
#define AC_AMPCAP_OFFSET_SHIFT 0
#define AC_AMPCAP_NUM_STEPS (0x7f<<8) /* number of steps */
#define AC_AMPCAP_NUM_STEPS_SHIFT 8
#define AC_AMPCAP_STEP_SIZE (0x7f<<16) /* step size 0-32dB
* in 0.25dB
*/
#define AC_AMPCAP_STEP_SIZE_SHIFT 16
#define AC_AMPCAP_MUTE (1<<31) /* mute capable */
#define AC_AMPCAP_MUTE_SHIFT 31
/* driver-specific amp-caps: using bits 24-30 */
#define AC_AMPCAP_MIN_MUTE (1 << 30) /* min-volume = mute */
/* Connection list */
#define AC_CLIST_LENGTH (0x7f<<0)
#define AC_CLIST_LONG (1<<7)
/* Supported power status */
#define AC_PWRST_D0SUP (1<<0)
#define AC_PWRST_D1SUP (1<<1)
#define AC_PWRST_D2SUP (1<<2)
#define AC_PWRST_D3SUP (1<<3)
#define AC_PWRST_D3COLDSUP (1<<4)
#define AC_PWRST_S3D3COLDSUP (1<<29)
#define AC_PWRST_CLKSTOP (1<<30)
#define AC_PWRST_EPSS (1U<<31)
/* Power state values */
#define AC_PWRST_SETTING (0xf<<0)
#define AC_PWRST_ACTUAL (0xf<<4)
#define AC_PWRST_ACTUAL_SHIFT 4
#define AC_PWRST_D0 0x00
#define AC_PWRST_D1 0x01
#define AC_PWRST_D2 0x02
#define AC_PWRST_D3 0x03
#define AC_PWRST_ERROR (1<<8)
#define AC_PWRST_CLK_STOP_OK (1<<9)
#define AC_PWRST_SETTING_RESET (1<<10)
/* Processing capabilies */
#define AC_PCAP_BENIGN (1<<0)
#define AC_PCAP_NUM_COEF (0xff<<8)
#define AC_PCAP_NUM_COEF_SHIFT 8
/* Volume knobs capabilities */
#define AC_KNBCAP_NUM_STEPS (0x7f<<0)
#define AC_KNBCAP_DELTA (1<<7)
/* HDMI LPCM capabilities */
#define AC_LPCMCAP_48K_CP_CHNS (0x0f<<0) /* max channels w/ CP-on */
#define AC_LPCMCAP_48K_NO_CHNS (0x0f<<4) /* max channels w/o CP-on */
#define AC_LPCMCAP_48K_20BIT (1<<8) /* 20b bitrate supported */
#define AC_LPCMCAP_48K_24BIT (1<<9) /* 24b bitrate supported */
#define AC_LPCMCAP_96K_CP_CHNS (0x0f<<10) /* max channels w/ CP-on */
#define AC_LPCMCAP_96K_NO_CHNS (0x0f<<14) /* max channels w/o CP-on */
#define AC_LPCMCAP_96K_20BIT (1<<18) /* 20b bitrate supported */
#define AC_LPCMCAP_96K_24BIT (1<<19) /* 24b bitrate supported */
#define AC_LPCMCAP_192K_CP_CHNS (0x0f<<20) /* max channels w/ CP-on */
#define AC_LPCMCAP_192K_NO_CHNS (0x0f<<24) /* max channels w/o CP-on */
#define AC_LPCMCAP_192K_20BIT (1<<28) /* 20b bitrate supported */
#define AC_LPCMCAP_192K_24BIT (1<<29) /* 24b bitrate supported */
#define AC_LPCMCAP_44K (1<<30) /* 44.1kHz support */
#define AC_LPCMCAP_44K_MS (1<<31) /* 44.1kHz-multiplies support */
/* Display pin's device list length */
#define AC_DEV_LIST_LEN_MASK 0x3f
#define AC_MAX_DEV_LIST_LEN 64
/*
* Control Parameters
*/
/* Amp gain/mute */
#define AC_AMP_MUTE (1<<7)
#define AC_AMP_GAIN (0x7f)
#define AC_AMP_GET_INDEX (0xf<<0)
#define AC_AMP_GET_LEFT (1<<13)
#define AC_AMP_GET_RIGHT (0<<13)
#define AC_AMP_GET_OUTPUT (1<<15)
#define AC_AMP_GET_INPUT (0<<15)
#define AC_AMP_SET_INDEX (0xf<<8)
#define AC_AMP_SET_INDEX_SHIFT 8
#define AC_AMP_SET_RIGHT (1<<12)
#define AC_AMP_SET_LEFT (1<<13)
#define AC_AMP_SET_INPUT (1<<14)
#define AC_AMP_SET_OUTPUT (1<<15)
/* DIGITAL1 bits */
#define AC_DIG1_ENABLE (1<<0)
#define AC_DIG1_V (1<<1)
#define AC_DIG1_VCFG (1<<2)
#define AC_DIG1_EMPHASIS (1<<3)
#define AC_DIG1_COPYRIGHT (1<<4)
#define AC_DIG1_NONAUDIO (1<<5)
#define AC_DIG1_PROFESSIONAL (1<<6)
#define AC_DIG1_LEVEL (1<<7)
/* DIGITAL2 bits */
#define AC_DIG2_CC (0x7f<<0)
/* DIGITAL3 bits */
#define AC_DIG3_ICT (0xf<<0)
#define AC_DIG3_KAE (1<<7)
/* Pin widget control - 8bit */
#define AC_PINCTL_EPT (0x3<<0)
#define AC_PINCTL_EPT_NATIVE 0
#define AC_PINCTL_EPT_HBR 3
#define AC_PINCTL_VREFEN (0x7<<0)
#define AC_PINCTL_VREF_HIZ 0 /* Hi-Z */
#define AC_PINCTL_VREF_50 1 /* 50% */
#define AC_PINCTL_VREF_GRD 2 /* ground */
#define AC_PINCTL_VREF_80 4 /* 80% */
#define AC_PINCTL_VREF_100 5 /* 100% */
#define AC_PINCTL_IN_EN (1<<5)
#define AC_PINCTL_OUT_EN (1<<6)
#define AC_PINCTL_HP_EN (1<<7)
/* Pin sense - 32bit */
#define AC_PINSENSE_IMPEDANCE_MASK (0x7fffffff)
#define AC_PINSENSE_PRESENCE (1<<31)
#define AC_PINSENSE_ELDV (1<<30) /* ELD valid (HDMI) */
/* EAPD/BTL enable - 32bit */
#define AC_EAPDBTL_BALANCED (1<<0)
#define AC_EAPDBTL_EAPD (1<<1)
#define AC_EAPDBTL_LR_SWAP (1<<2)
/* HDMI ELD data */
#define AC_ELDD_ELD_VALID (1<<31)
#define AC_ELDD_ELD_DATA 0xff
/* HDMI DIP size */
#define AC_DIPSIZE_ELD_BUF (1<<3) /* ELD buf size of packet size */
#define AC_DIPSIZE_PACK_IDX (0x07<<0) /* packet index */
/* HDMI DIP index */
#define AC_DIPIDX_PACK_IDX (0x07<<5) /* packet idnex */
#define AC_DIPIDX_BYTE_IDX (0x1f<<0) /* byte index */
/* HDMI DIP xmit (transmit) control */
#define AC_DIPXMIT_MASK (0x3<<6)
#define AC_DIPXMIT_DISABLE (0x0<<6) /* disable xmit */
#define AC_DIPXMIT_ONCE (0x2<<6) /* xmit once then disable */
#define AC_DIPXMIT_BEST (0x3<<6) /* best effort */
/* HDMI content protection (CP) control */
#define AC_CPCTRL_CES (1<<9) /* current encryption state */
#define AC_CPCTRL_READY (1<<8) /* ready bit */
#define AC_CPCTRL_SUBTAG (0x1f<<3) /* subtag for unsol-resp */
#define AC_CPCTRL_STATE (3<<0) /* current CP request state */
/* Converter channel <-> HDMI slot mapping */
#define AC_CVTMAP_HDMI_SLOT (0xf<<0) /* HDMI slot number */
#define AC_CVTMAP_CHAN (0xf<<4) /* converter channel number */
/* configuration default - 32bit */
#define AC_DEFCFG_SEQUENCE (0xf<<0)
#define AC_DEFCFG_DEF_ASSOC (0xf<<4)
#define AC_DEFCFG_ASSOC_SHIFT 4
#define AC_DEFCFG_MISC (0xf<<8)
#define AC_DEFCFG_MISC_SHIFT 8
#define AC_DEFCFG_MISC_NO_PRESENCE (1<<0)
#define AC_DEFCFG_COLOR (0xf<<12)
#define AC_DEFCFG_COLOR_SHIFT 12
#define AC_DEFCFG_CONN_TYPE (0xf<<16)
#define AC_DEFCFG_CONN_TYPE_SHIFT 16
#define AC_DEFCFG_DEVICE (0xf<<20)
#define AC_DEFCFG_DEVICE_SHIFT 20
#define AC_DEFCFG_LOCATION (0x3f<<24)
#define AC_DEFCFG_LOCATION_SHIFT 24
#define AC_DEFCFG_PORT_CONN (0x3<<30)
#define AC_DEFCFG_PORT_CONN_SHIFT 30
/* Display pin's device list entry */
#define AC_DE_PD (1<<0)
#define AC_DE_ELDV (1<<1)
#define AC_DE_IA (1<<2)
/* device types (0x0-0xf) */
enum {
AC_JACK_LINE_OUT,
AC_JACK_SPEAKER,
AC_JACK_HP_OUT,
AC_JACK_CD,
AC_JACK_SPDIF_OUT,
AC_JACK_DIG_OTHER_OUT,
AC_JACK_MODEM_LINE_SIDE,
AC_JACK_MODEM_HAND_SIDE,
AC_JACK_LINE_IN,
AC_JACK_AUX,
AC_JACK_MIC_IN,
AC_JACK_TELEPHONY,
AC_JACK_SPDIF_IN,
AC_JACK_DIG_OTHER_IN,
AC_JACK_OTHER = 0xf,
};
/* jack connection types (0x0-0xf) */
enum {
AC_JACK_CONN_UNKNOWN,
AC_JACK_CONN_1_8,
AC_JACK_CONN_1_4,
AC_JACK_CONN_ATAPI,
AC_JACK_CONN_RCA,
AC_JACK_CONN_OPTICAL,
AC_JACK_CONN_OTHER_DIGITAL,
AC_JACK_CONN_OTHER_ANALOG,
AC_JACK_CONN_DIN,
AC_JACK_CONN_XLR,
AC_JACK_CONN_RJ11,
AC_JACK_CONN_COMB,
AC_JACK_CONN_OTHER = 0xf,
};
/* jack colors (0x0-0xf) */
enum {
AC_JACK_COLOR_UNKNOWN,
AC_JACK_COLOR_BLACK,
AC_JACK_COLOR_GREY,
AC_JACK_COLOR_BLUE,
AC_JACK_COLOR_GREEN,
AC_JACK_COLOR_RED,
AC_JACK_COLOR_ORANGE,
AC_JACK_COLOR_YELLOW,
AC_JACK_COLOR_PURPLE,
AC_JACK_COLOR_PINK,
AC_JACK_COLOR_WHITE = 0xe,
AC_JACK_COLOR_OTHER,
};
/* Jack location (0x0-0x3f) */
/* common case */
enum {
AC_JACK_LOC_NONE,
AC_JACK_LOC_REAR,
AC_JACK_LOC_FRONT,
AC_JACK_LOC_LEFT,
AC_JACK_LOC_RIGHT,
AC_JACK_LOC_TOP,
AC_JACK_LOC_BOTTOM,
};
/* bits 4-5 */
enum {
AC_JACK_LOC_EXTERNAL = 0x00,
AC_JACK_LOC_INTERNAL = 0x10,
AC_JACK_LOC_SEPARATE = 0x20,
AC_JACK_LOC_OTHER = 0x30,
};
enum {
/* external on primary chasis */
AC_JACK_LOC_REAR_PANEL = 0x07,
AC_JACK_LOC_DRIVE_BAY,
/* internal */
AC_JACK_LOC_RISER = 0x17,
AC_JACK_LOC_HDMI,
AC_JACK_LOC_ATAPI,
/* others */
AC_JACK_LOC_MOBILE_IN = 0x37,
AC_JACK_LOC_MOBILE_OUT,
};
/* Port connectivity (0-3) */
enum {
AC_JACK_PORT_COMPLEX,
AC_JACK_PORT_NONE,
AC_JACK_PORT_FIXED,
AC_JACK_PORT_BOTH,
};
/* max. codec address */
#define HDA_MAX_CODEC_ADDRESS 0x0f
#endif

View File

@@ -0,0 +1,449 @@
#include "driver.h"
//New
NTSTATUS ResetHDAController(PFDO_CONTEXT fdoCtx, BOOLEAN wakeup) {
UINT32 gctl;
//Clear STATESTS
hda_write16(fdoCtx, STATESTS, STATESTS_INT_MASK);
//Stop all Streams DMA Engine
for (UINT32 i = 0; i < fdoCtx->numStreams; i++) {
hdac_stream_stop(&fdoCtx->streams[i]);
}
//Stop CORB and RIRB
hda_write8(fdoCtx, CORBCTL, 0);
hda_write8(fdoCtx, RIRBCTL, 0);
//Reset DMA position buffer
hda_write32(fdoCtx, DPLBASE, 0);
hda_write32(fdoCtx, DPUBASE, 0);
//Reset the controller for at least 100 us
gctl = hda_read32(fdoCtx, GCTL);
hda_write32(fdoCtx, GCTL, gctl & ~HDA_GCTL_RESET);
for (int count = 0; count < 1000; count++) {
gctl = hda_read32(fdoCtx, GCTL);
if (!(gctl & HDA_GCTL_RESET)) {
break;
}
udelay(10);
}
if (gctl & HDA_GCTL_RESET) {
SklHdAudBusPrint(DEBUG_LEVEL_ERROR, DBG_INIT, "%s Error: unable to put controller in reset\n", __func__);
return STATUS_DEVICE_POWER_FAILURE;
}
//If wakeup not requested, leave in reset state
if (!wakeup)
return STATUS_SUCCESS;
udelay(100);
gctl = hda_read32(fdoCtx, GCTL);
hda_write32(fdoCtx, GCTL, gctl | HDA_GCTL_RESET);
for (int count = 0; count < 1000; count++) {
gctl = hda_read32(fdoCtx, GCTL);
if (gctl & HDA_GCTL_RESET) {
break;
}
udelay(10);
}
if (!(gctl & HDA_GCTL_RESET)) {
SklHdAudBusPrint(DEBUG_LEVEL_ERROR, DBG_INIT, "%s Error: controller stuck in reset\n", __func__);
return STATUS_DEVICE_POWER_FAILURE;
}
//Wait for codecs to finish their own reset sequence. Delay from VoodooHDA so it resets properly
udelay(1000);
if (!fdoCtx->codecMask) {
fdoCtx->codecMask = hda_read16(fdoCtx, STATESTS);
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"codec mask = 0x%lx\n", fdoCtx->codecMask);
}
return STATUS_SUCCESS;
}
NTSTATUS GetHDACapabilities(PFDO_CONTEXT fdoCtx) {
UINT16 gcap = hda_read16(fdoCtx, GCAP);
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"chipset global capabilities = 0x%x\n", gcap);
fdoCtx->is64BitOK = !!(gcap & 0x1);
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"64 bit OK? %d\n", fdoCtx->is64BitOK);
fdoCtx->hwVersion = (hda_read8(fdoCtx, VMAJ) << 8) | hda_read8(fdoCtx, VMIN);
fdoCtx->captureStreams = (gcap >> 8) & 0x0f;
fdoCtx->playbackStreams = (gcap >> 12) & 0x0f;
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"streams (cap %d, playback %d)\n", fdoCtx->captureStreams, fdoCtx->playbackStreams);
fdoCtx->captureIndexOff = 0;
fdoCtx->playbackIndexOff = fdoCtx->captureStreams;
fdoCtx->numStreams = fdoCtx->captureStreams + fdoCtx->playbackStreams;
UINT8 corbSize = hda_read8(fdoCtx, CORBSIZE);
if (!(corbSize & 0x40)) {
return STATUS_DEVICE_FEATURE_NOT_SUPPORTED; //CORB must support 256
}
UINT8 rirbSize = hda_read8(fdoCtx, RIRBSIZE);
if (!(rirbSize & 0x40)) {
return STATUS_DEVICE_FEATURE_NOT_SUPPORTED; //RIRB must support 256
}
return STATUS_SUCCESS;
}
void HDAInitCorb(PFDO_CONTEXT fdoCtx) {
//Set the corb size to 256 entries
hda_write8(fdoCtx, CORBSIZE, 0x02);
//Setup CORB address
fdoCtx->corb.buf = (UINT32*)fdoCtx->rb;
fdoCtx->corb.addr = MmGetPhysicalAddress(fdoCtx->corb.buf);
hda_write32(fdoCtx, CORBLBASE, fdoCtx->corb.addr.LowPart);
hda_write32(fdoCtx, CORBUBASE, fdoCtx->corb.addr.HighPart);
//Set WP and RP
fdoCtx->corb.wp = 0;
hda_write16(fdoCtx, CORBWP, fdoCtx->corb.wp);
hda_write16(fdoCtx, CORBRP, HDA_CORBRP_RST);
udelay(10); //Delay for 10 us to reset
hda_write16(fdoCtx, CORBRP, 0);
}
void HDAInitRirb(PFDO_CONTEXT fdoCtx) {
//Set the rirb size to 256 entries
hda_write8(fdoCtx, RIRBSIZE, 0x02);
//Setup RIRB address
fdoCtx->rirb.buf = (UINT32*)(fdoCtx->rb + 0x800);
fdoCtx->rirb.addr = MmGetPhysicalAddress(fdoCtx->rirb.buf);
RtlZeroMemory(fdoCtx->rirb.cmds, sizeof(fdoCtx->rirb.cmds));
hda_write32(fdoCtx, RIRBLBASE, fdoCtx->rirb.addr.LowPart);
hda_write32(fdoCtx, RIRBUBASE, fdoCtx->rirb.addr.HighPart);
//Set WP and RP
fdoCtx->rirb.rp = 0;
hda_write16(fdoCtx, RIRBWP, HDA_RIRBWP_RST);
//Set interrupt threshold
hda_write16(fdoCtx, RINTCNT, 1);
//Enable Received response reporting
hda_write8(fdoCtx, RIRBCTL, HDA_RBCTL_IRQ_EN);
}
void HDAStartCorb(PFDO_CONTEXT fdoCtx) {
UINT8 corbCTL;
corbCTL = hda_read8(fdoCtx, CORBCTL);
corbCTL |= HDA_CORBCTL_RUN;
hda_write8(fdoCtx, CORBCTL, corbCTL);
}
void HDAStartRirb(PFDO_CONTEXT fdoCtx) {
UINT8 rirbCTL;
rirbCTL = hda_read8(fdoCtx, RIRBCTL);
rirbCTL |= HDA_RBCTL_DMA_EN;
hda_write8(fdoCtx, RIRBCTL, rirbCTL);
}
NTSTATUS StartHDAController(PFDO_CONTEXT fdoCtx) {
NTSTATUS status;
status = ResetHDAController(fdoCtx, TRUE);
if (!NT_SUCCESS(status)) {
goto exit;
}
//Clear STATESTS
hda_write16(fdoCtx, STATESTS, STATESTS_INT_MASK);
HDAInitCorb(fdoCtx);
HDAInitRirb(fdoCtx);
HDAStartCorb(fdoCtx);
HDAStartRirb(fdoCtx);
//Enabling Controller Interrupt
hda_write32(fdoCtx, GCTL, hda_read32(fdoCtx, GCTL) | HDA_GCTL_UNSOL);
hda_write32(fdoCtx, INTCTL, hda_read32(fdoCtx, INTCTL) | HDA_INT_CTRL_EN | HDA_INT_GLOBAL_EN);
{
//Program position buffer
PHYSICAL_ADDRESS posbufAddr = MmGetPhysicalAddress(fdoCtx->posbuf);
hda_write32(fdoCtx, DPLBASE, posbufAddr.LowPart);
hda_write32(fdoCtx, DPUBASE, posbufAddr.HighPart);
}
udelay(1000);
fdoCtx->ControllerEnabled = TRUE;
exit:
return status;
}
NTSTATUS StopHDAController(PFDO_CONTEXT fdoCtx) {
NTSTATUS status = ResetHDAController(fdoCtx, FALSE);
fdoCtx->ControllerEnabled = FALSE;
return status;
}
static UINT16 HDACommandAddr(UINT32 cmd) {
return (cmd >> 28) & 0xF;
}
NTSTATUS SendHDACmds(PFDO_CONTEXT fdoCtx, ULONG count, PHDAUDIO_CODEC_TRANSFER CodecTransfer) {
WdfInterruptAcquireLock(fdoCtx->Interrupt);
for (ULONG i = 0; i < count; i++) {
PHDAUDIO_CODEC_TRANSFER transfer = &CodecTransfer[i];
RtlZeroMemory(&transfer->Input, sizeof(transfer->Input));
UINT16 addr = HDACommandAddr(transfer->Output.Command);
//Add command to corb
UINT16 wp = hda_read16(fdoCtx, CORBWP);
if (wp == 0xffff) {
//Something wrong, controller likely went to sleep
SklHdAudBusPrint(DEBUG_LEVEL_ERROR, DBG_IOCTL,
"%s: device not found\n", __func__);
WdfInterruptReleaseLock(fdoCtx->Interrupt);
return STATUS_DEVICE_DOES_NOT_EXIST;
}
wp++;
wp %= HDA_MAX_CORB_ENTRIES;
UINT16 rp = hda_read16(fdoCtx, CORBRP);
if (wp == rp) {
//Oops it's full
SklHdAudBusPrint(DEBUG_LEVEL_ERROR, DBG_IOCTL,
"%s: device busy\n", __func__);
WdfInterruptReleaseLock(fdoCtx->Interrupt);
return STATUS_RETRY;
}
LONG oldVal = InterlockedIncrement(&fdoCtx->rirb.cmds[addr]);
fdoCtx->rirb.xfer[addr].xfer[oldVal - 1] = transfer;
fdoCtx->corb.buf[wp] = transfer->Output.Command;
hda_write16(fdoCtx, CORBWP, wp);
}
WdfInterruptReleaseLock(fdoCtx->Interrupt);
return STATUS_SUCCESS;
}
NTSTATUS RunSingleHDACmd(PFDO_CONTEXT fdoCtx, ULONG val, ULONG* res) {
HDAUDIO_CODEC_TRANSFER transfer = { 0 };
transfer.Output.Command = val;
SklHdAudBusPrint(DEBUG_LEVEL_ERROR, DBG_IOCTL,
"%s: Called. Command: 0x%x\n", __func__, val);
NTSTATUS status = SendHDACmds(fdoCtx, 1, &transfer);
if (!NT_SUCCESS(status)) {
return status;
}
UINT16 addr = HDACommandAddr(transfer.Output.Command);
status = HDA_WaitForTransfer(fdoCtx, addr, 1, &transfer);
if (transfer.Input.IsValid && res) {
*res = transfer.Input.Response;
}
return status;
}
#define HDA_RIRB_EX_UNSOL_EV (1<<4)
static void HDAFlushRIRB(PFDO_CONTEXT fdoCtx) {
UINT16 wp, addr;
wp = hda_read16(fdoCtx, RIRBWP);
if (wp == 0xffff) {
//Invalid WP
return;
}
if (wp == fdoCtx->rirb.wp)
return;
fdoCtx->rirb.wp = wp;
while (fdoCtx->rirb.rp != wp) {
fdoCtx->rirb.rp++;
fdoCtx->rirb.rp %= HDA_MAX_RIRB_ENTRIES;
HDAC_RIRB rirb = fdoCtx->rirb.rirbbuf[fdoCtx->rirb.rp];
addr = rirb.response_ex & 0xf;
if (addr >= HDA_MAX_CODECS) {
SklHdAudBusPrint(DEBUG_LEVEL_ERROR, DBG_IOCTL,
"Unexpected unsolicited response %x: %x\n",
rirb.response, rirb.response_ex);
}
else if (rirb.response_ex & HDA_RIRB_EX_UNSOL_EV) {
HDAUDIO_CODEC_RESPONSE response;
RtlZeroMemory(&response, sizeof(HDAUDIO_CODEC_RESPONSE));
response.SDataIn = addr;
response.Response = rirb.response;
response.IsUnsolicitedResponse = 1;
response.IsValid = 1;
SklHdAudBusPrint(DEBUG_LEVEL_ERROR, DBG_IOCTL,
"Unsol response on 0x%x: 0x%x\n", addr, response.Response);
PPDO_DEVICE_DATA codec = fdoCtx->codecs[addr];
if (!codec || codec->FdoContext != fdoCtx)
continue;
UINT Tag = response.Unsolicited.Tag;
CODEC_UNSOLICITED_CALLBACK callback = codec->unsolitCallbacks[Tag];
if (callback.inUse && callback.Routine) {
callback.Routine(response, callback.Context);
}
}
else if (InterlockedAdd(&fdoCtx->rirb.cmds[addr], 0)) {
PHDAC_CODEC_XFER codecXfer = &fdoCtx->rirb.xfer[addr];
if (codecXfer->xfer[0]) {
codecXfer->xfer[0]->Input.SDataIn = addr;
codecXfer->xfer[0]->Input.Response = rirb.response;
codecXfer->xfer[0]->Input.IsValid = 1;
SklHdAudBusPrint(DEBUG_LEVEL_ERROR, DBG_IOCTL,
"Got response on 0x%x for 0x%x: 0x%x\n", addr, codecXfer->xfer[0]->Output.Command, rirb.response);
}
else {
SklHdAudBusPrint(DEBUG_LEVEL_ERROR, DBG_IOCTL,
"Got response 0x%x but no xfer!\n", rirb.response);
}
RtlMoveMemory(&codecXfer->xfer[0], &codecXfer->xfer[1], sizeof(PHDAUDIO_CODEC_TRANSFER) * (HDA_MAX_CORB_ENTRIES - 1));
codecXfer->xfer[HDA_MAX_CORB_ENTRIES - 1] = NULL;
if (!InterlockedDecrement(&fdoCtx->rirb.cmds[addr])) {
SklHdAudBusPrint(DEBUG_LEVEL_VERBOSE, DBG_IOCTL, "Empty queue for 0x%x\n", addr);
KeSetEvent(&fdoCtx->rirb.xferEvent[addr], IO_NO_INCREMENT, FALSE);
}
}
else {
SklHdAudBusPrint(DEBUG_LEVEL_ERROR, DBG_IOCTL,
"Unexpected unsolicited response from address %d %x\n", addr,
rirb.response);
}
}
}
int hda_stream_interrupt(PFDO_CONTEXT fdoCtx, unsigned int status) {
int handled = 0;
UINT8 sd_status;
for (UINT32 i = 0; i < fdoCtx->numStreams; i++) {
PHDAC_STREAM stream = &fdoCtx->streams[i];
if (status & stream->int_sta_mask) {
sd_status = stream_read8(stream, SD_STS);
stream_write8(stream, SD_STS, SD_INT_MASK);
handled |= 1 << stream->idx;
if (sd_status & SD_INT_COMPLETE) {
if (stream->isr.IOC && stream->isr.IsrCallback) {
stream->isr.IsrCallback(
stream->isr.CallbackContext,
sd_status);
}
stream->irqReceived = TRUE;
}
}
}
return handled;
}
BOOLEAN
NTAPI
hda_interrupt(
WDFINTERRUPT Interrupt,
ULONG MessageID) {
UNREFERENCED_PARAMETER(MessageID);
WDFDEVICE Device = WdfInterruptGetDevice(Interrupt);
PFDO_CONTEXT fdoCtx = Fdo_GetContext(Device);
BOOLEAN handled = FALSE;
if (fdoCtx->dspInterruptCallback) {
handled = (BOOLEAN)fdoCtx->dspInterruptCallback(fdoCtx->dspInterruptContext);
}
if (!fdoCtx->ControllerEnabled)
return handled;
UINT32 status = hda_read32(fdoCtx, INTSTS);
if (status == 0 || status == 0xffffffff)
return handled;
handled = TRUE;
if (hda_stream_interrupt(fdoCtx, status)) {
WdfInterruptQueueDpcForIsr(Interrupt);
}
status = hda_read8(fdoCtx, RIRBSTS);
if (status & RIRB_INT_MASK) {
hda_write8(fdoCtx, RIRBSTS, RIRB_INT_MASK);
if (status & RIRB_INT_RESPONSE) {
fdoCtx->processRirb = TRUE;
WdfInterruptQueueDpcForIsr(Interrupt);
}
}
return handled;
}
void
NTAPI
hda_dpc(
WDFINTERRUPT Interrupt,
WDFOBJECT AssociatedObject
) {
UNREFERENCED_PARAMETER(AssociatedObject);
WDFDEVICE Device = WdfInterruptGetDevice(Interrupt);
PFDO_CONTEXT fdoCtx = Fdo_GetContext(Device);
for (UINT32 i = 0; i < fdoCtx->numStreams; i++) {
PHDAC_STREAM stream = &fdoCtx->streams[i];
if (stream->irqReceived) {
stream->irqReceived = FALSE;
for (int j = 0; j < MAX_NOTIF_EVENTS; j++) {
if (stream->registeredCallbacks[j].InUse) {
LARGE_INTEGER unknownVal = { 0 };
KeQuerySystemTime(&unknownVal);
stream->registeredCallbacks[j].NotificationCallback(stream->registeredCallbacks[j].CallbackContext, unknownVal);
}
}
for (int j = 0; j < MAX_NOTIF_EVENTS; j++) {
if (stream->registeredEvents[j]) {
KeSetEvent(stream->registeredEvents[j], IO_NO_INCREMENT, FALSE);
}
}
}
}
if (fdoCtx->processRirb) {
fdoCtx->processRirb = FALSE;
HDAFlushRIRB(fdoCtx);
}
}

View File

@@ -0,0 +1,25 @@
#if !defined(_HDA_CONTROLLER_H_)
#define _HDA_CONTROLLER_H_
typedef struct _HDA_ASYNC_CONTEXT {
PPDO_DEVICE_DATA devData;
ULONG Count;
PHDAUDIO_CODEC_TRANSFER CodecTransfer;
PHDAUDIO_TRANSFER_COMPLETE_CALLBACK Callback;
PVOID CallbackContext;
} HDA_ASYNC_CONTEXT, * PHDA_ASYNC_CONTEXT;
WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(HDA_ASYNC_CONTEXT, HDAAsyncWorkItem_GetContext);
//New
NTSTATUS GetHDACapabilities(PFDO_CONTEXT fdoCtx);
NTSTATUS StartHDAController(PFDO_CONTEXT fdoCtx);
NTSTATUS StopHDAController(PFDO_CONTEXT fdoCtx);
NTSTATUS SendHDACmds(PFDO_CONTEXT fdoCtx, ULONG count, PHDAUDIO_CODEC_TRANSFER CodecTransfer);
NTSTATUS RunSingleHDACmd(PFDO_CONTEXT fdoCtx, ULONG val, ULONG* res);
//Old
BOOLEAN NTAPI hda_interrupt(WDFINTERRUPT Interrupt, ULONG MessageID);
void NTAPI hda_dpc(WDFINTERRUPT Interrupt, WDFOBJECT AssociatedObject);
#endif

View File

@@ -0,0 +1,176 @@
#include "driver.h"
void hdac_stream_start(PHDAC_STREAM stream) {
hda_read32(stream->FdoContext, WALLCLK);
/* enable SIE */
hda_update32(stream->FdoContext, INTCTL, 1 << stream->idx, 1 << stream->idx);
/* set stripe control */
if (stream->stripe) {
UINT8 stripe_ctl = 1;
stream_update8(stream, SD_CTL_3B, SD_CTL_STRIPE_MASK,
stripe_ctl);
}
/* set DMA start and interrupt mask */
stream_update8(stream, SD_CTL, 0, SD_CTL_DMA_START | SD_INT_MASK);
stream->running = TRUE;
}
void hdac_stream_clear(PHDAC_STREAM stream) {
stream_update8(stream, SD_CTL, SD_CTL_DMA_START | SD_INT_MASK, 0);
stream_write8(stream, SD_STS, SD_INT_MASK); /* to be sure */
if (stream->stripe)
stream_update8(stream, SD_CTL_3B, SD_CTL_STRIPE_MASK, 0);
stream->running = FALSE;
}
void hdac_stream_stop(PHDAC_STREAM stream) {
hdac_stream_clear(stream);
/* disable SIE */
hda_update32(stream->FdoContext, INTCTL, 1 << stream->idx, 0);
}
void hdac_stream_reset(PHDAC_STREAM stream) {
hdac_stream_clear(stream);
stream_update8(stream, SD_CTL, 0, SD_CTL_STREAM_RESET);
KeStallExecutionProcessor(3);
int timeout = 300;
UCHAR val;
do {
val = stream_read8(stream, SD_CTL) & SD_CTL_STREAM_RESET;
if (val)
break;
} while (--timeout);
val &= ~SD_CTL_STREAM_RESET;
stream_write8(stream, SD_CTL, val);
KeStallExecutionProcessor(3);
timeout = 300;
/* waiting for hardware to report that the stream is out of reset */
do {
val = stream_read8(stream, SD_CTL) & SD_CTL_STREAM_RESET;
if (!val)
break;
} while (--timeout);
if (stream->posbuf)
*stream->posbuf = 0;
}
UINT16 hdac_format(PHDAC_STREAM stream) {
UINT16 format = 0;
switch (stream->streamFormat.SampleRate) {
case 8000:
format = HDA_RATE(48, 1, 6);
break;
case 9600:
format = HDA_RATE(48, 1, 5);
break;
case 11025:
format = HDA_RATE(44, 1, 4);
break;
case 16000:
format = HDA_RATE(48, 1, 3);
break;
case 22050:
format = HDA_RATE(44, 1, 2);
break;
case 32000:
format = HDA_RATE(48, 2, 3);
break;
case 44100:
format = HDA_RATE(44, 1, 1);
break;
case 48000:
format = HDA_RATE(48, 1, 1);
break;
case 88200:
format = HDA_RATE(44, 2, 1);
break;
case 96000:
format = HDA_RATE(48, 2, 1);
break;
case 176400:
format = HDA_RATE(44, 4, 1);
break;
case 192000:
format = HDA_RATE(48, 4, 1);
break;
}
{
UINT16 channels = stream->streamFormat.NumberOfChannels;
if (channels == 0 || channels > 8)
return 0;
format |= channels - 1;
switch (stream->streamFormat.ContainerSize) {
case 8:
format |= AC_FMT_BITS_8;
break;
case 16:
format |= AC_FMT_BITS_16;
break;
case 20:
format |= AC_FMT_BITS_20;
break;
case 24:
format |= AC_FMT_BITS_24;
break;
case 32:
format |= AC_FMT_BITS_32;
break;
}
}
return format;
}
void hdac_stream_setup(PHDAC_STREAM stream) {
/* make sure the run bit is zero for SD */
hdac_stream_clear(stream);
UINT val;
/* program the stream_tag */
val = stream_read32(stream, SD_CTL);
val = (val & ~SD_CTL_STREAM_TAG_MASK) |
(stream->streamTag << SD_CTL_STREAM_TAG_SHIFT);
stream_write32(stream, SD_CTL, val);
/* program the length of samples in cyclic buffer */
stream_write32(stream, SD_CBL, stream->bufSz);
/* program the stream format */
/* this value needs to be the same as the one programmed */
UINT16 format = hdac_format(stream);
stream_write16(stream, SD_FORMAT, format);
/* program the stream LVI (last valid index) of the BDL */
stream_write16(stream, SD_LVI, stream->numBlocks - 1);
/* program the BDL address */
/* lower BDL address */
PHYSICAL_ADDRESS bdlAddr = MmGetPhysicalAddress(stream->bdl);
stream_write32(stream, SD_BDLPL, bdlAddr.LowPart);
/* upper BDL address */
stream_write32(stream, SD_BDLPU, bdlAddr.HighPart);
//Enable position buffer
if (!(hda_read32(stream->FdoContext, DPLBASE) & HDA_DPLBASE_ENABLE)){
PHYSICAL_ADDRESS posbufAddr = MmGetPhysicalAddress(stream->FdoContext->posbuf);
hda_write32(stream->FdoContext, DPLBASE, posbufAddr.LowPart | HDA_DPLBASE_ENABLE);
}
/* set the interrupt enable bits in the descriptor control register */
stream_update32(stream, SD_CTL, 0, SD_INT_MASK);
stream->fifoSize = 0;
stream->fifoSize = stream_read16(stream, SD_FIFOSIZE) + 1;
}

View File

@@ -0,0 +1,6 @@
UINT16 hdac_format(PHDAC_STREAM stream);
void hdac_stream_start(PHDAC_STREAM stream);
void hdac_stream_stop(PHDAC_STREAM stream);
void hdac_stream_reset(PHDAC_STREAM stream);
void hdac_stream_setup(PHDAC_STREAM stream);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,278 @@
#include "driver.h"
#include "nhlt.h"
#include <acpiioct.h>
DEFINE_GUID(GUID_SST_NHLT,
0xA69F886E, 0x6CEB, 0x4594, 0xA4, 0x1F, 0x7B, 0x5D, 0xCE, 0x24, 0xC5, 0x53);
NTSTATUS
NHLTQuery(
_In_ WDFDEVICE FxDevice,
_In_ ULONG Arg1,
_In_ ULONG Arg2,
_Out_ WDFMEMORY* outputBufferMemoryArg
) {
ULONG_PTR bytesReturned;
NTSTATUS status = STATUS_ACPI_NOT_INITIALIZED;
WDFMEMORY inputBufferMemory = NULL;
PACPI_EVAL_INPUT_BUFFER_COMPLEX_EX inputBuffer = NULL;
PACPI_METHOD_ARGUMENT inputArgument = NULL;
ACPI_EVAL_OUTPUT_BUFFER outputHeader;
WDFMEMORY outputBufferMemory = NULL;
WDF_MEMORY_DESCRIPTOR inputBufferMemoryDescriptor;
WDF_MEMORY_DESCRIPTOR outputBufferMemoryDescriptor;
ULONG inputBufferSize =
(ULONG)(
FIELD_OFFSET(ACPI_EVAL_INPUT_BUFFER_COMPLEX_EX, Argument) +
ACPI_METHOD_ARGUMENT_LENGTH(sizeof(GUID)) +
ACPI_METHOD_ARGUMENT_LENGTH(sizeof(ULONG)) +
ACPI_METHOD_ARGUMENT_LENGTH(sizeof(ULONG)) +
ACPI_METHOD_ARGUMENT_LENGTH(0)
);
status = WdfMemoryCreate(WDF_NO_OBJECT_ATTRIBUTES,
PagedPool,
SKLHDAUDBUS_POOL_TAG,
inputBufferSize,
&inputBufferMemory,
(PVOID*)&inputBuffer);
if (!NT_SUCCESS(status)) {
SklHdAudBusPrint(DEBUG_LEVEL_ERROR, DBG_INIT, "Failed to create input buffer\n");
goto end;
}
RtlZeroMemory(inputBuffer, inputBufferSize);
inputBuffer->Signature = ACPI_EVAL_INPUT_BUFFER_COMPLEX_SIGNATURE_V1_EX;
status = RtlStringCchPrintfA(
inputBuffer->MethodName,
sizeof(inputBuffer->MethodName),
"_DSM"
);
if (!NT_SUCCESS(status)) {
SklHdAudBusPrint(DEBUG_LEVEL_ERROR, DBG_INIT, "Failed to write method name\n");
goto end;
}
inputBuffer->Size = inputBufferSize;
inputBuffer->ArgumentCount = 4;
inputArgument = inputBuffer->Argument;
//arg 0
ACPI_METHOD_SET_ARGUMENT_BUFFER(inputArgument,
&GUID_SST_NHLT,
sizeof(GUID_SST_NHLT));
inputArgument = ACPI_METHOD_NEXT_ARGUMENT(inputArgument);
ACPI_METHOD_SET_ARGUMENT_INTEGER(inputArgument, Arg1);
inputArgument = ACPI_METHOD_NEXT_ARGUMENT(inputArgument);
ACPI_METHOD_SET_ARGUMENT_INTEGER(inputArgument, Arg2);
inputArgument = ACPI_METHOD_NEXT_ARGUMENT(inputArgument);
RtlZeroMemory(inputArgument, sizeof(*inputArgument));
inputArgument->Type = ACPI_METHOD_ARGUMENT_PACKAGE_EX;
inputArgument->DataLength = 0;
WDF_MEMORY_DESCRIPTOR_INIT_HANDLE(&inputBufferMemoryDescriptor,
inputBufferMemory,
0);
RtlZeroMemory(&outputHeader, sizeof(outputHeader));
WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&outputBufferMemoryDescriptor,
&outputHeader,
sizeof(outputHeader));
status = WdfIoTargetSendIoctlSynchronously(WdfDeviceGetIoTarget(FxDevice),
NULL,
IOCTL_ACPI_EVAL_METHOD_EX,
&inputBufferMemoryDescriptor,
&outputBufferMemoryDescriptor,
NULL,
&bytesReturned);
if (status == STATUS_BUFFER_OVERFLOW) {
status = STATUS_SUCCESS;
}
else if (!NT_SUCCESS(status)) {
SklHdAudBusPrint(DEBUG_LEVEL_ERROR, DBG_INIT, "Failed first ioctl\n");
goto end;
}
status = WdfMemoryCreate(WDF_NO_OBJECT_ATTRIBUTES,
PagedPool,
SKLHDAUDBUS_POOL_TAG,
outputHeader.Length,
&outputBufferMemory,
(PVOID*)NULL);
if (!NT_SUCCESS(status)) {
SklHdAudBusPrint(DEBUG_LEVEL_ERROR, DBG_INIT, "Failed to create output buffer\n");
goto end;
}
WDF_MEMORY_DESCRIPTOR_INIT_HANDLE(&outputBufferMemoryDescriptor,
outputBufferMemory,
0);
status = WdfIoTargetSendIoctlSynchronously(WdfDeviceGetIoTarget(FxDevice),
NULL,
IOCTL_ACPI_EVAL_METHOD_EX,
&inputBufferMemoryDescriptor,
&outputBufferMemoryDescriptor,
NULL,
&bytesReturned);
if (!NT_SUCCESS(status)) {
SklHdAudBusPrint(DEBUG_LEVEL_ERROR, DBG_INIT, "Failed to do 2nd ioctl\n");
goto end;
}
end:
if (inputBufferMemory != NULL) {
WdfObjectDelete(inputBufferMemory);
inputBufferMemory = NULL;
}
if (!NT_SUCCESS(status)) {
if (outputBufferMemory != NULL) {
WdfObjectDelete(outputBufferMemory);
outputBufferMemory = NULL;
}
}
else {
*outputBufferMemoryArg = outputBufferMemory;
}
return status;
}
NTSTATUS NHLTCheckSupported(_In_ WDFDEVICE FxDevice) {
WDFMEMORY outputBufferMemory;
NTSTATUS status = NHLTQuery(FxDevice, NHLTRev1, NHLTSupportQuery, &outputBufferMemory);
if (!NT_SUCCESS(status)) {
return status;
}
if (!outputBufferMemory) {
return STATUS_NO_MEMORY;
}
PACPI_EVAL_OUTPUT_BUFFER outputBuffer = (PACPI_EVAL_OUTPUT_BUFFER)WdfMemoryGetBuffer(outputBufferMemory, NULL);
if (outputBuffer->Count < 1) {
status = STATUS_INVALID_DEVICE_OBJECT_PARAMETER;
goto end;
}
{
PACPI_METHOD_ARGUMENT argument = outputBuffer->Argument;
UCHAR supportedQueries = argument->Data[0];
if ((supportedQueries & 0x3) == 0) {
status = STATUS_NOT_SUPPORTED;
}
}
end:
if (outputBufferMemory != NULL) {
WdfObjectDelete(outputBufferMemory);
outputBufferMemory = NULL;
}
return status;
}
void parseACPI(UINT8* res, UINT32 offset, UINT32 sz, UINT64* nhltAddr, UINT64* nhltSz);
NTSTATUS NHLTQueryTableAddress(_In_ WDFDEVICE FxDevice, UINT64 *nhltAddr, UINT64 *nhltSz) {
WDFMEMORY outputBufferMemory;
NTSTATUS status = NHLTQuery(FxDevice, NHLTRev1, NHLTMemoryAddress, &outputBufferMemory);
if (!NT_SUCCESS(status)) {
return status;
}
if (!outputBufferMemory) {
return STATUS_NO_MEMORY;
}
PACPI_EVAL_OUTPUT_BUFFER outputBuffer = (PACPI_EVAL_OUTPUT_BUFFER)WdfMemoryGetBuffer(outputBufferMemory, NULL);
if (outputBuffer->Count < 1) {
status = STATUS_INVALID_DEVICE_OBJECT_PARAMETER;
goto end;
}
{
PACPI_METHOD_ARGUMENT argument = outputBuffer->Argument;
UINT8* res = argument->Data;
UINT32 sz = argument->DataLength;
*nhltAddr = 0;
*nhltSz = 0;
parseACPI(res, 0, sz, nhltAddr, nhltSz);
}
if (nhltAddr == 0 || nhltSz == 0) {
status = STATUS_UNSUCCESSFUL;
}
end:
if (outputBufferMemory != NULL) {
WdfObjectDelete(outputBufferMemory);
outputBufferMemory = NULL;
}
return status;
}
//Begin ACPI parser
#define ACPI_RESOURCE_NAME_ADDRESS64 0x8A
/*
* LARGE descriptors
*/
#define AML_RESOURCE_LARGE_HEADER_COMMON \
UINT8 DescriptorType;\
UINT16 ResourceLength;
#define AML_RESOURCE_ADDRESS_COMMON \
UINT8 ResourceType; \
UINT8 Flags; \
UINT8 SpecificFlags;
#include <pshpack1.h>
typedef struct aml_resource_address64
{
AML_RESOURCE_LARGE_HEADER_COMMON
AML_RESOURCE_ADDRESS_COMMON
UINT64 Granularity;
UINT64 Minimum;
UINT64 Maximum;
UINT64 TranslationOffset;
UINT64 AddressLength;
} AML_RESOURCE_ADDRESS64;
#include <poppack.h>
void parseACPIMemory64(UINT8* res, UINT32 offset, UINT32 sz, UINT64 *nhltAddr, UINT64 *nhltSz) {
if (offset + 3 > sz)
return;
UINT8 opcode = res[offset];
if (opcode != ACPI_RESOURCE_NAME_ADDRESS64)
return;
AML_RESOURCE_ADDRESS64* address64 = (AML_RESOURCE_ADDRESS64*)(res + offset);
*nhltAddr = address64->Minimum;
*nhltSz = address64->AddressLength;
}
void parseACPI(UINT8* res, UINT32 offset, UINT32 sz, UINT64* nhltAddr, UINT64* nhltSz) {
if (offset + 3 > sz)
return;
UINT8 opcode = res[offset];
UINT16 len;
memcpy(&len, res + offset + 1, sizeof(UINT16));
if (opcode == ACPI_RESOURCE_NAME_ADDRESS64)
parseACPIMemory64(res, offset, sz, nhltAddr, nhltSz);
offset += (len + 3);
parseACPI(res, offset, sz, nhltAddr, nhltSz);
}

View File

@@ -0,0 +1,11 @@
enum NHLTQueryRevision {
NHLTRev1 = 1
};
enum NHLTQuery {
NHLTSupportQuery = 0,
NHLTMemoryAddress
};
NTSTATUS NHLTCheckSupported(_In_ WDFDEVICE FxDevice);
NTSTATUS NHLTQueryTableAddress(_In_ WDFDEVICE FxDevice, UINT64* nhltAddr, UINT64* nhltSz);

View File

@@ -0,0 +1,78 @@
static inline UINT8 read8(PVOID addr) {
return READ_REGISTER_UCHAR((PUCHAR)addr);
}
static inline void write8(PVOID addr, UINT8 data) {
WRITE_REGISTER_UCHAR((PUCHAR)addr, (UCHAR)data);
}
static inline UINT16 read16(PVOID addr) {
return READ_REGISTER_USHORT((PUSHORT)addr);
}
static inline void write16(PVOID addr, UINT16 data) {
WRITE_REGISTER_USHORT((PUSHORT)addr, (USHORT)data);
}
static inline UINT32 read32(PVOID addr) {
return READ_REGISTER_ULONG((PULONG)addr);
}
static inline void write32(PVOID addr, UINT32 data) {
WRITE_REGISTER_ULONG((PULONG)addr, (ULONG)data);
}
static inline void pci_read_cfg_byte(PBUS_INTERFACE_STANDARD pciInterface, UINT reg, BYTE* data) {
pciInterface->GetBusData(pciInterface->Context, PCI_WHICHSPACE_CONFIG, data, reg, sizeof(BYTE));
}
static inline void pci_read_cfg_dword(PBUS_INTERFACE_STANDARD pciInterface, UINT reg, UINT32* data) {
pciInterface->GetBusData(pciInterface->Context, PCI_WHICHSPACE_CONFIG, data, reg, sizeof(UINT32));
}
static inline void pci_write_cfg_byte(PBUS_INTERFACE_STANDARD pciInterface, UINT reg, BYTE data) {
pciInterface->SetBusData(pciInterface->Context, PCI_WHICHSPACE_CONFIG, &data, reg, sizeof(BYTE));
}
static inline void pci_write_cfg_dword(PBUS_INTERFACE_STANDARD pciInterface, UINT reg, UINT32 data) {
pciInterface->GetBusData(pciInterface->Context, PCI_WHICHSPACE_CONFIG, &data, reg, sizeof(UINT32));
}
static inline void update_pci_byte(PBUS_INTERFACE_STANDARD pciInterface, UINT reg, BYTE mask, BYTE val) {
BYTE data;
pci_read_cfg_byte(pciInterface, reg, &data);
data &= ~mask;
data |= (val & mask);
pci_write_cfg_byte(pciInterface, reg, data);
}
#define HDA_RATE(base, mult, div) \
(AC_FMT_BASE_##base##K | (((mult) - 1) << AC_FMT_MULT_SHIFT) | \
(((div) - 1) << AC_FMT_DIV_SHIFT))
#define hda_read8(ctx, reg) read8((ctx)->m_BAR0.Base.baseptr + HDA_REG_##reg)
#define hda_write8(ctx, reg, data) write8((ctx)->m_BAR0.Base.baseptr + HDA_REG_##reg, data)
#define hda_update8(ctx, reg, mask, val) hda_write8(ctx, reg, (hda_read8(ctx, reg) & ~(mask)) | (val))
#define hda_read16(ctx, reg) read16((ctx)->m_BAR0.Base.baseptr + HDA_REG_##reg)
#define hda_write16(ctx, reg, data) write16((ctx)->m_BAR0.Base.baseptr + HDA_REG_##reg, data)
#define hda_update16(ctx, reg, mask, val) hda_write16(ctx, reg, (hda_read16(ctx, reg) & ~(mask)) | (val))
#define hda_read32(ctx, reg) read32((ctx)->m_BAR0.Base.baseptr + HDA_REG_##reg)
#define hda_write32(ctx, reg, data) write32((ctx)->m_BAR0.Base.baseptr + HDA_REG_##reg, data)
#define hda_update32(ctx, reg, mask, val) hda_write32(ctx, reg, (hda_read32(ctx, reg) & ~(mask)) | (val))
#define stream_read8(ctx, reg) read8((ctx)->sdAddr + HDA_REG_##reg)
#define stream_write8(ctx, reg, data) write8((ctx)->sdAddr + HDA_REG_##reg, data)
#define stream_update8(ctx, reg, mask, val) stream_write8(ctx, reg, (stream_read8(ctx, reg) & ~(mask)) | (val))
#define stream_read16(ctx, reg) read16((ctx)->sdAddr + HDA_REG_##reg)
#define stream_write16(ctx, reg, data) write16((ctx)->sdAddr + HDA_REG_##reg, data)
#define stream_update16(ctx, reg, mask, val) stream_write16(ctx, reg, (stream_read16(ctx, reg) & ~(mask)) | (val))
#define stream_read32(ctx, reg) read32((ctx)->sdAddr + HDA_REG_##reg)
#define stream_write32(ctx, reg, data) write32((ctx)->sdAddr + HDA_REG_##reg, data)
#define stream_update32(ctx, reg, mask, val) stream_write32(ctx, reg, (stream_read32(ctx, reg) & ~(mask)) | (val))
/* update register macro */
#define hdac_update32(addr, reg, mask, val) \
write32(addr + reg, ((read32(addr + reg) & ~(mask)) | (val)))
#define hdac_update16(addr, reg, mask, val) \
write16(addr + reg,((read16(addr + reg) & ~(mask)) | (val)))

View File

@@ -0,0 +1,14 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by sklhdaudbus.rc
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 101
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

View File

@@ -0,0 +1,340 @@
#include "driver.h"
NTSTATUS
NTAPI
HDAGraphics_EvtIoTargetQueryRemove(
WDFIOTARGET ioTarget
) {
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"Device Removal Notification\n");
WdfIoTargetCloseForQueryRemove(ioTarget);
return STATUS_SUCCESS;
}
void
NTAPI
HDAGraphics_EvtIoTargetRemoveCanceled(
WDFIOTARGET ioTarget
) {
WDF_IO_TARGET_OPEN_PARAMS openParams;
NTSTATUS status;
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"Device Removal Cancel\n");
WDF_IO_TARGET_OPEN_PARAMS_INIT_REOPEN(&openParams);
status = WdfIoTargetOpen(ioTarget, &openParams);
if (!NT_SUCCESS(status)) {
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"WdfIoTargetOpen failed 0x%x\n", status);
WdfObjectDelete(ioTarget);
return;
}
}
void
NTAPI
HDAGraphics_EvtIoTargetRemoveComplete(
WDFIOTARGET ioTarget
) {
PFDO_CONTEXT fdoCtx;
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"Device Removal Complete\n");
PGRAPHICSIOTARGET_CONTEXT ioTargetContext;
ioTargetContext = GraphicsIoTarget_GetContext(ioTarget);
fdoCtx = ioTargetContext->FdoContext;
WdfWaitLockAcquire(fdoCtx->GraphicsDevicesCollectionWaitLock, NULL);
WdfCollectionRemove(fdoCtx->GraphicsDevicesCollection, ioTarget);
WdfWaitLockRelease(fdoCtx->GraphicsDevicesCollectionWaitLock);
WdfObjectDelete(ioTarget);
}
void
HDAGraphicsPowerNotificationCallback(
PVOID GraphicsDeviceHandle,
DEVICE_POWER_STATE NewGrfxPowerState,
BOOLEAN PreNotification,
PVOID PrivateHandle
) {
PFDO_CONTEXT fdoCtx = (PFDO_CONTEXT)PrivateHandle;
UNREFERENCED_PARAMETER(GraphicsDeviceHandle);
UNREFERENCED_PARAMETER(NewGrfxPowerState);
UNREFERENCED_PARAMETER(PreNotification);
UNREFERENCED_PARAMETER(fdoCtx);
//No-Op
}
void
HDAGraphicsPowerRemovalNotificationCallback(
PVOID GraphicsDeviceHandle,
PVOID PrivateHandle
) {
PFDO_CONTEXT fdoCtx = (PFDO_CONTEXT)PrivateHandle;
UNREFERENCED_PARAMETER(GraphicsDeviceHandle);
UNREFERENCED_PARAMETER(fdoCtx);
//No-Op
}
void
NTAPI
Fdo_EnumerateCodec(
PFDO_CONTEXT fdoCtx,
UINT8 addr
);
void
NTAPI
EjectGraphicsCodec(PFDO_CONTEXT fdoCtx) {
if (!fdoCtx->UseSGPCCodec) {
return;
}
if (!fdoCtx->codecs[fdoCtx->GraphicsCodecAddress])
return;
fdoCtx->GraphicsCodecConnected = FALSE;
PDO_IDENTIFICATION_DESCRIPTION description;//
// Initialize the description with the information about the detected codec.
//
WDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER_INIT(
&description.Header,
sizeof(description)
);
description.FdoContext = fdoCtx;
RtlCopyMemory(&description.CodecIds, &fdoCtx->codecs[fdoCtx->GraphicsCodecAddress]->CodecIds, sizeof(description.CodecIds));
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"Ejecting Gfx Codec\n");
WdfInterruptAcquireLock(fdoCtx->Interrupt);
WdfChildListUpdateChildDescriptionAsMissing(WdfFdoGetDefaultChildList(fdoCtx->WdfDevice), &description.Header);
//Don't null FdoContext to allow SGPC Audio driver to unregister callbacks / events and cleanup
WdfInterruptReleaseLock(fdoCtx->Interrupt);
}
void
NTAPI
EnumerateGraphicsCodec(PFDO_CONTEXT fdoCtx) {
if (!fdoCtx->UseSGPCCodec) {
return;
}
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"Enumerating Gfx Codec\n");
fdoCtx->GraphicsCodecConnected = TRUE;
Fdo_EnumerateCodec(fdoCtx, (UINT8)fdoCtx->GraphicsCodecAddress);
}
void HDAGraphicsPowerFStateNotificationCallback(
PVOID GraphicsDeviceHandle,
ULONG ComponentIndex,
UINT NewFState,
BOOLEAN PreNotification,
PVOID PrivateHandle
) {
PFDO_CONTEXT fdoCtx = (PFDO_CONTEXT)PrivateHandle;
UNREFERENCED_PARAMETER(GraphicsDeviceHandle);
UNREFERENCED_PARAMETER(ComponentIndex);
if (NewFState) {
if (PreNotification) {
EjectGraphicsCodec(fdoCtx);
}
else {
}
}
else {
if (PreNotification) {
}
else {
EnumerateGraphicsCodec(fdoCtx);
}
}
}
void HDAGraphicsPowerInitialComponentStateCallback( //https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/d3dkmthk/nc-d3dkmthk-pdxgk_initial_component_state
PVOID GraphicsDeviceHandle,
PVOID PrivateHandle,
ULONG ComponentIndex,
BOOLEAN IsBlockingType,
UINT InitialFState,
GUID ComponentGuid,
UINT PowerComponentMappingFlag
) {
UNREFERENCED_PARAMETER(GraphicsDeviceHandle);
UNREFERENCED_PARAMETER(ComponentIndex);
UNREFERENCED_PARAMETER(IsBlockingType);
UNREFERENCED_PARAMETER(ComponentGuid);
PFDO_CONTEXT fdoCtx = (PFDO_CONTEXT)PrivateHandle;
if (PowerComponentMappingFlag) {
}
else {
if (InitialFState) {
} else {
EnumerateGraphicsCodec(fdoCtx);
}
}
}
void
NTAPI
HDAGraphicsPowerInterfaceAdd(WDFWORKITEM WorkItem) {
PGRAPHICSWORKITEM_CONTEXT workItemContext = GraphicsWorkitem_GetContext(WorkItem);
PFDO_CONTEXT fdoCtx = workItemContext->FdoContext;
PUNICODE_STRING graphicsDeviceSymlink = &workItemContext->GPUDeviceSymlink;
WDF_OBJECT_ATTRIBUTES attributes;
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, GRAPHICSIOTARGET_CONTEXT);
WDFIOTARGET ioTarget;
WDF_IO_TARGET_OPEN_PARAMS openParams;
PGRAPHICSIOTARGET_CONTEXT ioTargetContext;
NTSTATUS status = WdfIoTargetCreate(fdoCtx->WdfDevice, &attributes, &ioTarget);
if (!NT_SUCCESS(status)) {
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"WdfIoTargetCreate failed 0x%x\n", status);
goto exit;
}
ioTargetContext = GraphicsIoTarget_GetContext(ioTarget);
ioTargetContext->FdoContext = fdoCtx;
WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME(
&openParams,
graphicsDeviceSymlink,
STANDARD_RIGHTS_ALL);
openParams.ShareAccess = FILE_SHARE_WRITE | FILE_SHARE_READ;
openParams.EvtIoTargetQueryRemove = HDAGraphics_EvtIoTargetQueryRemove;
openParams.EvtIoTargetRemoveCanceled = HDAGraphics_EvtIoTargetRemoveCanceled;
openParams.EvtIoTargetRemoveComplete = HDAGraphics_EvtIoTargetRemoveComplete;
status = WdfIoTargetOpen(ioTarget, &openParams);
if (!NT_SUCCESS(status)) {
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"WdfIoTargetOpen failed with status 0x%x\n", status);
WdfObjectDelete(ioTarget);
goto exit;
}
WdfWaitLockAcquire(fdoCtx->GraphicsDevicesCollectionWaitLock, NULL);
WdfCollectionAdd(fdoCtx->GraphicsDevicesCollection, ioTarget);
WdfWaitLockRelease(fdoCtx->GraphicsDevicesCollectionWaitLock);
DXGK_GRAPHICSPOWER_REGISTER_INPUT graphicsPowerRegisterInput;
graphicsPowerRegisterInput = { 0 };
graphicsPowerRegisterInput.Version = DXGK_GRAPHICSPOWER_VERSION;
graphicsPowerRegisterInput.PrivateHandle = fdoCtx;
graphicsPowerRegisterInput.PowerNotificationCb = HDAGraphicsPowerNotificationCallback;
graphicsPowerRegisterInput.RemovalNotificationCb = HDAGraphicsPowerRemovalNotificationCallback;
graphicsPowerRegisterInput.FStateNotificationCb = HDAGraphicsPowerFStateNotificationCallback;
graphicsPowerRegisterInput.InitialComponentStateCb = HDAGraphicsPowerInitialComponentStateCallback;
WDF_MEMORY_DESCRIPTOR inputDescriptor;
WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&inputDescriptor, &graphicsPowerRegisterInput, sizeof(graphicsPowerRegisterInput));
WDF_MEMORY_DESCRIPTOR outputDescriptor;
WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&outputDescriptor, &ioTargetContext->graphicsPowerRegisterOutput, sizeof(ioTargetContext->graphicsPowerRegisterOutput));
status = WdfIoTargetSendInternalIoctlSynchronously(ioTarget,
WDF_NO_HANDLE,
IOCTL_INTERNAL_GRAPHICSPOWER_REGISTER,
&inputDescriptor, &outputDescriptor, NULL, NULL);
if (!NT_SUCCESS(status)) {
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"IOCTL_INTERNAL_GRAPHICSPOWER_REGISTER failed with status 0x%x\n", status);
goto exit;
}
exit:
WdfObjectDelete(WorkItem);
}
NTSTATUS
NTAPI
HDAGraphicsPowerInterfaceCallback(
PVOID NotificationStruct,
PVOID Context
) {
NTSTATUS status = STATUS_SUCCESS;
PFDO_CONTEXT fdoCtx = (PFDO_CONTEXT)Context;
PDEVICE_INTERFACE_CHANGE_NOTIFICATION devNotificationStruct = (PDEVICE_INTERFACE_CHANGE_NOTIFICATION)NotificationStruct;
if (!IsEqualGUID(devNotificationStruct->InterfaceClassGuid, GUID_DEVINTERFACE_GRAPHICSPOWER)) {
return STATUS_NOT_SUPPORTED;
}
if (IsEqualGUID(devNotificationStruct->Event, GUID_DEVICE_INTERFACE_ARRIVAL)) {
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"Graphics Arrival Notification!\n");
status = RtlUnicodeStringValidate(devNotificationStruct->SymbolicLinkName);
if (!NT_SUCCESS(status)) {
return status;
}
WDF_WORKITEM_CONFIG workItemConfig;
WDF_WORKITEM_CONFIG_INIT(&workItemConfig, HDAGraphicsPowerInterfaceAdd);
WDF_OBJECT_ATTRIBUTES attributes;
WDFWORKITEM workItem;
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
WDF_OBJECT_ATTRIBUTES_SET_CONTEXT_TYPE(
&attributes,
GRAPHICSWORKITEM_CONTEXT
);
attributes.ParentObject = fdoCtx->WdfDevice;
WdfWorkItemCreate(&workItemConfig, &attributes, &workItem);
PGRAPHICSWORKITEM_CONTEXT workItemContext = GraphicsWorkitem_GetContext(workItem);
workItemContext->FdoContext = fdoCtx;
workItemContext->GPUDeviceSymlink = *devNotificationStruct->SymbolicLinkName;
WdfWorkItemEnqueue(workItem);
}
return status;
}
void CheckHDAGraphicsRegistryKeys(PFDO_CONTEXT fdoCtx) {
NTSTATUS status;
WDFKEY driverKey;
status = WdfDeviceOpenRegistryKey(fdoCtx->WdfDevice, PLUGPLAY_REGKEY_DRIVER, READ_CONTROL, NULL, &driverKey);
if (!NT_SUCCESS(status)) {
return;
}
WDFKEY settingsKey;
DECLARE_CONST_UNICODE_STRING(DriverSettings, L"Settings");
DECLARE_CONST_UNICODE_STRING(GfxSharedCodecAddress, L"GfxSharedCodecAddress");
status = WdfRegistryOpenKey(driverKey, &DriverSettings, READ_CONTROL, NULL, &settingsKey);
if (!NT_SUCCESS(status)) {
goto closeDriverKey;
}
ULONG GfxCodecAddr;
status = WdfRegistryQueryULong(settingsKey, &GfxSharedCodecAddress, &GfxCodecAddr);
if (NT_SUCCESS(status)) {
fdoCtx->UseSGPCCodec = TRUE;
fdoCtx->GraphicsCodecAddress = GfxCodecAddr;
}
WdfRegistryClose(settingsKey);
closeDriverKey:
WdfRegistryClose(driverKey);
}

View File

@@ -0,0 +1,60 @@
#include "driver.h"
NTSTATUS
NTAPI
SklHdAudBusEvtDeviceAdd(
_In_ WDFDRIVER Driver,
_Inout_ PWDFDEVICE_INIT DeviceInit
)
{
UNREFERENCED_PARAMETER(Driver);
NTSTATUS status;
//
// Initialize all the properties specific to the device.
// Framework has default values for the one that are not
// set explicitly here. So please read the doc and make sure
// you are okay with the defaults.
//
WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_BUS_EXTENDER);
status = Fdo_Create(DeviceInit);
return status;
}
extern "C" NTSTATUS
NTAPI
DriverEntry(
__in PDRIVER_OBJECT DriverObject,
__in PUNICODE_STRING RegistryPath
)
{
WDF_DRIVER_CONFIG config;
NTSTATUS status;
WDFDRIVER wdfDriver;
SklHdAudBusPrint(DEBUG_LEVEL_INFO, DBG_INIT,
"Driver Entry\n");
//
// Default to NonPagedPoolNx for non paged pool allocations where supported.
//
ExInitializeDriverRuntime(DrvRtPoolNxOptIn);
WDF_DRIVER_CONFIG_INIT(&config, SklHdAudBusEvtDeviceAdd);
status = WdfDriverCreate(DriverObject,
RegistryPath,
WDF_NO_OBJECT_ATTRIBUTES,
&config,
&wdfDriver
);
if (!NT_SUCCESS(status))
{
SklHdAudBusPrint(DEBUG_LEVEL_ERROR, DBG_INIT,
"WdfDriverCreate failed %x\n", status);
}
return status;
}

View File

@@ -0,0 +1,101 @@
;/*++
;
;Copyright (c) CoolStar. All rights reserved.
;
;Module Name:
; sklhdaudbus.inf
;
;Abstract:
; INF file for installing the SKL HD Aud Bus Driver
;
;
;--*/
[Version]
Signature = "$WINDOWS NT$"
Class = System
ClassGuid = {4d36e97d-e325-11ce-bfc1-08002be10318}
Provider = CoolStar
DriverVer = 8/15/2022,1.0.0
CatalogFile = sklhdaudbus.cat
PnpLockdown = 1
[DestinationDirs]
DefaultDestDir = 12
; ================= Class section =====================
[SourceDisksNames]
1 = %DiskId1%,,,""
[SourceDisksFiles]
sklhdaudbus.sys = 1,,
[SignatureAttributes]
sklhdaudbus.sys=SignatureAttributes.DRM
[SignatureAttributes.DRM]
DRMLevel=1300
[SignatureAttributes.PETrust]
PETrust=true
;*****************************************
; SklHDAudBus Install Section
;*****************************************
[Manufacturer]
%StdMfg%=Standard,NT$ARCH$
; Decorated model section take precedence over undecorated
; ones on XP and later.
[Standard.NT$ARCH$]
%SklHDAudBus.DeviceDesc%=SklHDAudBus_Device, PCI\VEN_8086&DEV_9D70&CC_0401 ;Intel Skylake
%SklHDAudBus.DeviceDesc%=SklHDAudBus_Device, PCI\VEN_8086&DEV_9D71&CC_0401 ;Intel Kaby Lake
%SklHDAudBus.DeviceDesc%=SklHDAudBus_Device, PCI\VEN_8086&DEV_5A98&CC_0401 ;Intel Apollo Lake
%SklHDAudBus.DeviceDesc%=SklHDAudBus_Device, PCI\VEN_8086&DEV_3198&CC_0401 ;Intel Gemini Lake
%SklHDAudBus.DeviceDesc%=SklHDAudBus_Device, PCI\VEN_8086&DEV_02C8&CC_0401 ;Intel Comet Lake
%SklHDAudBus.DeviceDesc%=SklHDAudBus_Device, PCI\VEN_8086&DEV_4DC8&CC_0401 ;Intel Jasper Lake
%SklHDAudBus.DeviceDesc%=SklHDAudBus_Device, PCI\VEN_8086&DEV_A0C8&CC_0401 ;Intel Tiger Lake
%SklHDAudBus.DeviceDesc%=SklHDAudBus_Device, PCI\VEN_8086&DEV_51C8&CC_0401 ;Intel Alder Lake-P
%SklHDAudBus.DeviceDesc%=SklHDAudBus_Device, PCI\VEN_8086&DEV_51CC&CC_0401 ;Intel Alder Lake-M
%SklHDAudBus.DeviceDesc%=SklHDAudBus_Device, PCI\VEN_8086&DEV_54C8&CC_0401 ;Intel Alder Lake-N
%SklHDAudBus.DeviceDesc%=SklHDAudBus_Device, PCI\VEN_8086&DEV_51CA&CC_0401 ;Intel Raptor Lake-P
%SklHDAudBus.DeviceDesc%=SklHDAudBus_Device, PCI\VEN_8086&DEV_7E28&CC_0401 ;Intel Meteor Lake-P
%SklHDAudBus.DeviceDesc%=SklHDAudBus_Device, PCI\VEN_8086&DEV_7728&CC_0401 ;Intel Arrow Lake-P
%SklHDAudBus.DeviceDesc%=SklHDAudBus_Device, PCI\VEN_8086&DEV_A828&CC_0401 ;Intel Lunar Lake-P
[SklHDAudBus_Device.NT]
CopyFiles=Drivers_Dir
[SklHDAudBus_Device.NT.HW]
AddReg=SklHDAudBus_AddReg
Include=pci.inf
Needs=PciD3ColdSupported.HW
[Drivers_Dir]
sklhdaudbus.sys
[SklHDAudBus_AddReg]
; Set to 1 to connect the first interrupt resource found, 0 to leave disconnected
HKR,Settings,"ConnectInterrupt",0x00010001,0
;-------------- Service installation
[SklHDAudBus_Device.NT.Services]
AddService = SklHDAudBus,%SPSVCINST_ASSOCSERVICE%, SklHDAudBus_Service_Inst
; -------------- SklHDAudBus driver install sections
[SklHDAudBus_Service_Inst]
DisplayName = %SklHDAudBus.SVCDESC%
ServiceType = 1 ; SERVICE_KERNEL_DRIVER
StartType = 3 ; SERVICE_DEMAND_START
ErrorControl = 1 ; SERVICE_ERROR_NORMAL
ServiceBinary = %12%\sklhdaudbus.sys
LoadOrderGroup = Base
[Strings]
SPSVCINST_ASSOCSERVICE= 0x00000002
StdMfg = "CoolStar"
DiskId1 = "CoolStar HD Audio Installation Disk #1"
SklHDAudBus.DeviceDesc = "CoolStar HD Audio"
SklHDAudBus.SVCDESC = "CoolStar HD Audio Service"

View File

@@ -0,0 +1,41 @@
/*++
Copyright (c) Microsoft Corporation All Rights Reserved
Module Name:
sklhdaudbus.rc
Abstract:
--*/
#include <windows.h>
#define VER_FILETYPE VFT_DRV
#define VER_FILESUBTYPE VFT2_DRV_SYSTEM
#define VER_FILEDESCRIPTION_STR "CoolStar HD Audio"
#define VER_INTERNALNAME_STR "sklhdaudbus.sys"
#define VER_ORIGINALFILENAME_STR "sklhdaudbus.sys"
#define VER_LEGALCOPYRIGHT_YEARS "2024"
#define VER_LEGALCOPYRIGHT_STR "Copyright (C) " VER_LEGALCOPYRIGHT_YEARS " CoolStar."
#define VER_FILEVERSION 1,0,5,0
#define VER_PRODUCTVERSION_STR "1.0.5.0"
#define VER_PRODUCTVERSION 1,0,5,0
#define LVER_PRODUCTVERSION_STR L"1.0.5.0"
#define VER_FILEFLAGSMASK (VS_FF_DEBUG | VS_FF_PRERELEASE)
#ifdef DEBUG
#define VER_FILEFLAGS (VS_FF_DEBUG)
#else
#define VER_FILEFLAGS (0)
#endif
#define VER_FILEOS VOS_NT_WINDOWS32
#define VER_COMPANYNAME_STR "CoolStar"
#define VER_PRODUCTNAME_STR "CoolStar HD Audio"
#include "common.ver"

View File

@@ -0,0 +1,158 @@
#include "driver.h"
#include <acpiioct.h>
#include "sof-tplg.h"
DEFINE_GUID(GUID_ACPI_DSD,
0xdaffd814, 0x6eba, 0x4d8c, 0x8a, 0x91, 0xbc, 0x9b, 0xbf, 0x4a, 0xa3, 0x01);
void copyDSDParam(PACPI_METHOD_ARGUMENT dsdParameterData, char** buf) {
RtlZeroMemory(buf, 32);
RtlCopyMemory(buf, dsdParameterData->Data, min(dsdParameterData->DataLength, 32));
}
NTSTATUS
GetSOFTplg(
_In_ WDFDEVICE FxDevice,
SOF_TPLG* sofTplg
)
{
if (!sofTplg) {
return STATUS_INVALID_PARAMETER;
}
NTSTATUS status = STATUS_ACPI_NOT_INITIALIZED;
ACPI_EVAL_INPUT_BUFFER_EX inputBuffer;
RtlZeroMemory(&inputBuffer, sizeof(inputBuffer));
inputBuffer.Signature = ACPI_EVAL_INPUT_BUFFER_SIGNATURE_EX;
status = RtlStringCchPrintfA(
inputBuffer.MethodName,
sizeof(inputBuffer.MethodName),
"_DSD"
);
if (!NT_SUCCESS(status)) {
return status;
}
ACPI_EVAL_OUTPUT_BUFFER outputSizeBuffer = { 0 };
WDF_MEMORY_DESCRIPTOR outputSizeMemDesc;
WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&outputSizeMemDesc, &outputSizeBuffer, (ULONG)sizeof(outputSizeBuffer));
WDF_MEMORY_DESCRIPTOR inputMemDesc;
WDF_MEMORY_DESCRIPTOR_INIT_BUFFER(&inputMemDesc, &inputBuffer, (ULONG)sizeof(inputBuffer));
// Send the request along
status = WdfIoTargetSendInternalIoctlSynchronously(
WdfDeviceGetIoTarget(FxDevice),
NULL,
IOCTL_ACPI_EVAL_METHOD_EX,
&inputMemDesc,
&outputSizeMemDesc,
NULL,
NULL
);
if (status != STATUS_BUFFER_OVERFLOW) {
// Method might not exist?
return status;
}
WDFMEMORY outputMemory;
PACPI_EVAL_OUTPUT_BUFFER outputBuffer;
size_t outputArgumentBufferSize = outputSizeBuffer.Length;
size_t outputBufferSize = FIELD_OFFSET(ACPI_EVAL_OUTPUT_BUFFER, Argument) + outputArgumentBufferSize;
WDF_OBJECT_ATTRIBUTES attributes;
WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
attributes.ParentObject = FxDevice;
status = WdfMemoryCreate(&attributes,
NonPagedPoolNx,
0,
outputBufferSize,
&outputMemory,
(PVOID*)&outputBuffer);
if (!NT_SUCCESS(status)) {
return status;
}
RtlZeroMemory(outputBuffer, outputBufferSize);
WDF_MEMORY_DESCRIPTOR outputMemDesc;
WDF_MEMORY_DESCRIPTOR_INIT_HANDLE(&outputMemDesc, outputMemory, NULL);
status = WdfIoTargetSendInternalIoctlSynchronously(
WdfDeviceGetIoTarget(FxDevice),
NULL,
IOCTL_ACPI_EVAL_METHOD_EX,
&inputMemDesc,
&outputMemDesc,
NULL,
NULL
);
if (!NT_SUCCESS(status)) {
goto Exit;
}
if (outputBuffer->Signature != ACPI_EVAL_OUTPUT_BUFFER_SIGNATURE) {
goto Exit;
}
SklHdAudBusPrint(DEBUG_LEVEL_ERROR, DBG_PNP,
"Evaluted %s successfully\n", inputBuffer.MethodName);
if (outputBuffer->Count % 2) {
status = STATUS_ACPI_INVALID_DATA;
goto Exit;
}
status = STATUS_NOT_FOUND;
RtlZeroMemory(sofTplg, sizeof(*sofTplg));
sofTplg->magic = SOFTPLG_MAGIC;
sofTplg->length = sizeof(*sofTplg);
{
PACPI_METHOD_ARGUMENT currArgument = &outputBuffer->Argument[0];
for (ULONG i = 0; i < outputBuffer->Count; i += 2) {
PACPI_METHOD_ARGUMENT guidArg = currArgument;
currArgument = ACPI_METHOD_NEXT_ARGUMENT(currArgument);
PACPI_METHOD_ARGUMENT packageArg = currArgument;
currArgument = ACPI_METHOD_NEXT_ARGUMENT(currArgument);
if (guidArg->Type != ACPI_METHOD_ARGUMENT_BUFFER ||
guidArg->DataLength != 16 ||
packageArg->Type != ACPI_METHOD_ARGUMENT_PACKAGE) {
break;
}
if (memcmp(guidArg->Data, &GUID_ACPI_DSD, guidArg->DataLength) != 0) {
continue;
}
status = STATUS_SUCCESS;
FOR_EACH_ACPI_METHOD_ARGUMENT(dsdParameter, (PACPI_METHOD_ARGUMENT)packageArg->Data, (PACPI_METHOD_ARGUMENT)(packageArg->Data + packageArg->DataLength)) {
PACPI_METHOD_ARGUMENT dsdParameterName = (PACPI_METHOD_ARGUMENT)dsdParameter->Data;
PACPI_METHOD_ARGUMENT dsdParameterData = ACPI_METHOD_NEXT_ARGUMENT(dsdParameterName);
if (strncmp((const char*)&dsdParameterName->Data[0], "speaker-tplg", dsdParameterName->DataLength) == 0) {
copyDSDParam(dsdParameterData, (char **)&sofTplg->speaker_tplg);
}
else if (strncmp((const char*)&dsdParameterName->Data[0], "hp-tplg", dsdParameterName->DataLength) == 0) {
copyDSDParam(dsdParameterData, (char**)&sofTplg->hp_tplg);
}
else if (strncmp((const char*)&dsdParameterName->Data[0], "mic-tplg", dsdParameterName->DataLength) == 0) {
copyDSDParam(dsdParameterData, (char**)&sofTplg->dmic_tplg);
}
}
}
}
Exit:
if (outputMemory != WDF_NO_HANDLE) {
WdfObjectDelete(outputMemory);
}
return status;
}

View File

@@ -0,0 +1,11 @@
#define SOFTPLG_MAGIC '$SOF'
typedef struct _SOF_TPLG {
UINT32 magic;
UINT32 length;
char speaker_tplg[32];
char hp_tplg[32];
char dmic_tplg[32];
} SOF_TPLG, *PSOF_TPLG;
NTSTATUS GetSOFTplg(WDFDEVICE FxDevice, SOF_TPLG *sofTplg);

View File

@@ -0,0 +1,43 @@
#include <evntrace.h>
#ifndef _TRACE_H_
#define _TRACE_H_
extern "C"
{
//
// Tracing Definitions:
//
// Control GUID:
// {73e3b785-f5fb-423e-94a9-56627fea9053}
//
#define WPP_CONTROL_GUIDS \
WPP_DEFINE_CONTROL_GUID( \
SpbTestToolTraceGuid, \
(73e3b785,f5fb,423e,94a9,56627fea9053), \
WPP_DEFINE_BIT(TRACE_FLAG_WDFLOADING) \
WPP_DEFINE_BIT(TRACE_FLAG_SPBAPI) \
WPP_DEFINE_BIT(TRACE_FLAG_OTHER) \
)
}
#define WPP_LEVEL_FLAGS_LOGGER(level,flags) WPP_LEVEL_LOGGER(flags)
#define WPP_LEVEL_FLAGS_ENABLED(level, flags) (WPP_LEVEL_ENABLED(flags) && WPP_CONTROL(WPP_BIT_ ## flags).Level >= level)
#define Trace CyapaPrint
#define FuncEntry
#define FuncExit
#define WPP_INIT_TRACING
#define WPP_CLEANUP
#define TRACE_FLAG_SPBAPI 0
#define TRACE_FLAG_WDFLOADING 0
// begin_wpp config
// FUNC FuncEntry{LEVEL=TRACE_LEVEL_VERBOSE}(FLAGS);
// FUNC FuncExit{LEVEL=TRACE_LEVEL_VERBOSE}(FLAGS);
// USEPREFIX(FuncEntry, "%!STDPREFIX! [%!FUNC!] --> entry");
// USEPREFIX(FuncExit, "%!STDPREFIX! [%!FUNC!] <--");
// end_wpp
#endif _TRACE_H_

View File

@@ -174,6 +174,12 @@ Used Version: 1.2.6
License: BSD-3-Clause (https://opensource.org/licenses/BSD-3-Clause)
URL: https://code.google.com/archive/p/cmediadrivers/
Title: HD Audio Driver for Windows 10 / 11 with Skylake -> Raptor Lake DSP support
Path: drivers/wdm/audio/hdaudbus_new
Used Version: git commit ad50bcc
License: BSD-3-Clause (https://opensource.org/licenses/BSD-3-Clause)
URL: https://github.com/coolstar/sklhdaudbus/tree/master
Title: PCI hardware IDs database
Path: hal/halx86/legacy/bus/pci_classes.ids
Path: hal/halx86/legacy/bus/pci_vendors.ids