From cca6df99b1a48250f5d4c8ecc38980b46fcaadbc Mon Sep 17 00:00:00 2001 From: CoolStar Date: Mon, 27 Nov 2023 19:18:32 -0800 Subject: [PATCH] Fix ES8388 vendor & Add I2S Bus driver (#14) * Fix mfgr on es8323 * Add I2S Bus driver (required for reading ACPI properties) --- build/RockchipDrivers.sln | 6 + drivers/audio/codecs/es8323/es8323.inx | 2 +- drivers/audio/rk3xi2sbus/LICENSE.txt | 13 + drivers/audio/rk3xi2sbus/README.md | 4 + drivers/audio/rk3xi2sbus/adsp.cpp | 105 +++++ drivers/audio/rk3xi2sbus/adsp.h | 47 +++ drivers/audio/rk3xi2sbus/buspdo.cpp | 328 +++++++++++++++ drivers/audio/rk3xi2sbus/buspdo.h | 64 +++ drivers/audio/rk3xi2sbus/driver.h | 53 +++ drivers/audio/rk3xi2sbus/fdo.cpp | 378 ++++++++++++++++++ drivers/audio/rk3xi2sbus/fdo.h | 43 ++ drivers/audio/rk3xi2sbus/resource.h | 14 + drivers/audio/rk3xi2sbus/rk-tplg.cpp | 166 ++++++++ drivers/audio/rk3xi2sbus/rk-tplg.h | 16 + drivers/audio/rk3xi2sbus/rk3xi2sbus.cpp | 58 +++ drivers/audio/rk3xi2sbus/rk3xi2sbus.inx | 88 ++++ drivers/audio/rk3xi2sbus/rk3xi2sbus.rc | 41 ++ drivers/audio/rk3xi2sbus/rk3xi2sbus.vcxproj | 110 +++++ .../rk3xi2sbus/rk3xi2sbus.vcxproj.Filters | 68 ++++ 19 files changed, 1603 insertions(+), 1 deletion(-) create mode 100644 drivers/audio/rk3xi2sbus/LICENSE.txt create mode 100644 drivers/audio/rk3xi2sbus/README.md create mode 100644 drivers/audio/rk3xi2sbus/adsp.cpp create mode 100644 drivers/audio/rk3xi2sbus/adsp.h create mode 100644 drivers/audio/rk3xi2sbus/buspdo.cpp create mode 100644 drivers/audio/rk3xi2sbus/buspdo.h create mode 100644 drivers/audio/rk3xi2sbus/driver.h create mode 100644 drivers/audio/rk3xi2sbus/fdo.cpp create mode 100644 drivers/audio/rk3xi2sbus/fdo.h create mode 100644 drivers/audio/rk3xi2sbus/resource.h create mode 100644 drivers/audio/rk3xi2sbus/rk-tplg.cpp create mode 100644 drivers/audio/rk3xi2sbus/rk-tplg.h create mode 100644 drivers/audio/rk3xi2sbus/rk3xi2sbus.cpp create mode 100644 drivers/audio/rk3xi2sbus/rk3xi2sbus.inx create mode 100644 drivers/audio/rk3xi2sbus/rk3xi2sbus.rc create mode 100644 drivers/audio/rk3xi2sbus/rk3xi2sbus.vcxproj create mode 100644 drivers/audio/rk3xi2sbus/rk3xi2sbus.vcxproj.Filters diff --git a/build/RockchipDrivers.sln b/build/RockchipDrivers.sln index 29d2de2..7e49a62 100644 --- a/build/RockchipDrivers.sln +++ b/build/RockchipDrivers.sln @@ -13,6 +13,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pl330dma", "..\drivers\dma\ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "es8323", "..\drivers\audio\codecs\es8323\es8323.vcxproj", "{15D34676-980D-4406-8C05-AB95AA5B43CA}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rk3xi2sbus", "..\drivers\audio\rk3xi2sbus\rk3xi2sbus.vcxproj", "{BA95E25D-392E-4BC9-B481-E4D9FB9AFFB4}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM64 = Debug|ARM64 @@ -49,6 +51,10 @@ Global {15D34676-980D-4406-8C05-AB95AA5B43CA}.Release|ARM64.ActiveCfg = Release|ARM64 {15D34676-980D-4406-8C05-AB95AA5B43CA}.Release|ARM64.Build.0 = Release|ARM64 {15D34676-980D-4406-8C05-AB95AA5B43CA}.Release|ARM64.Deploy.0 = Release|ARM64 + {BA95E25D-392E-4BC9-B481-E4D9FB9AFFB4}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {BA95E25D-392E-4BC9-B481-E4D9FB9AFFB4}.Release|ARM64.ActiveCfg = Release|ARM64 + {BA95E25D-392E-4BC9-B481-E4D9FB9AFFB4}.Release|ARM64.Build.0 = Release|ARM64 + {BA95E25D-392E-4BC9-B481-E4D9FB9AFFB4}.Release|ARM64.Deploy.0 = Release|ARM64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/drivers/audio/codecs/es8323/es8323.inx b/drivers/audio/codecs/es8323/es8323.inx index 6d39350..98ac95c 100644 --- a/drivers/audio/codecs/es8323/es8323.inx +++ b/drivers/audio/codecs/es8323/es8323.inx @@ -71,7 +71,7 @@ LoadOrderGroup = Base [Strings] SPSVCINST_ASSOCSERVICE= 0x00000002 -StdMfg = "CoolStar" +StdMfg = "Everest" DiskId1 = "Everest 8323 Installation Disk #1" Es8323.DeviceDesc = "Everest 8323 I2S Audio" Es8388.DeviceDesc = "Everest 8388 I2S Audio" diff --git a/drivers/audio/rk3xi2sbus/LICENSE.txt b/drivers/audio/rk3xi2sbus/LICENSE.txt new file mode 100644 index 0000000..3880e06 --- /dev/null +++ b/drivers/audio/rk3xi2sbus/LICENSE.txt @@ -0,0 +1,13 @@ +Copyright 2023 CoolStar + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/drivers/audio/rk3xi2sbus/README.md b/drivers/audio/rk3xi2sbus/README.md new file mode 100644 index 0000000..d93dd34 --- /dev/null +++ b/drivers/audio/rk3xi2sbus/README.md @@ -0,0 +1,4 @@ +Rockchip Bus Driver for Rockchip 3xxx I2S + +* Exposes I2S resources +* Support accessing topology \ No newline at end of file diff --git a/drivers/audio/rk3xi2sbus/adsp.cpp b/drivers/audio/rk3xi2sbus/adsp.cpp new file mode 100644 index 0000000..0ac8cb6 --- /dev/null +++ b/drivers/audio/rk3xi2sbus/adsp.cpp @@ -0,0 +1,105 @@ +#include "driver.h" +#define ADSP_DECL 1 +#include "adsp.h" + +NTSTATUS ADSPGetResources(_In_ PVOID _context, _Out_ _PCI_BAR* acpBar, PTPLG_INFO tplgInfo) { + if (!_context) + return STATUS_NO_SUCH_DEVICE; + + PPDO_DEVICE_DATA devData = (PPDO_DEVICE_DATA)_context; + if (!devData->FdoContext) { + return STATUS_NO_SUCH_DEVICE; + } + + if (acpBar) { + *acpBar = devData->FdoContext->m_MMIO; + } + + if (tplgInfo) { + if (devData->FdoContext->rkTplg) { + tplgInfo->rkTplg = devData->FdoContext->rkTplg; + tplgInfo->rkTplgSz = devData->FdoContext->rkTplgSz; + } + } + + 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_ PADSP_DPC_CALLBACK dpcCallback, _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->dspDPCCallback = dpcCallback; + 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 ADSPQueueDpcForInterrupt(_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; + } + + BOOL ret = WdfInterruptQueueDpcForIsr(devData->FdoContext->Interrupt); + return ret ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL; +} + +RKDSP_BUS_INTERFACE RKDSP_BusInterface(PVOID Context) { + RKDSP_BUS_INTERFACE busInterface; + RtlZeroMemory(&busInterface, sizeof(RKDSP_BUS_INTERFACE)); + + busInterface.Size = sizeof(RKDSP_BUS_INTERFACE); + busInterface.Version = 1; + busInterface.Context = Context; + busInterface.InterfaceReference = WdfDeviceInterfaceReferenceNoOp; + busInterface.InterfaceDereference = WdfDeviceInterfaceDereferenceNoOp; + busInterface.GetResources = ADSPGetResources; + busInterface.SetDSPPowerState = ADSPSetPowerState; + busInterface.RegisterInterrupt = ADSPRegisterInterrupt; + busInterface.UnregisterInterrupt = ADSPUnregisterInterrupt; + busInterface.QueueDPCForInterrupt = ADSPQueueDpcForInterrupt; + + return busInterface; +} \ No newline at end of file diff --git a/drivers/audio/rk3xi2sbus/adsp.h b/drivers/audio/rk3xi2sbus/adsp.h new file mode 100644 index 0000000..b5b9015 --- /dev/null +++ b/drivers/audio/rk3xi2sbus/adsp.h @@ -0,0 +1,47 @@ +#ifndef __ADSP_INTERFACE +#define __ADSP_INTERFACE +// +// The GUID_RKDSP_BUS_INTERFACE interface GUID +// +// {863DD2AC-5B54-4C57-8487-782F9ADFE8D7} +DEFINE_GUID(GUID_RKDSP_BUS_INTERFACE, + 0x863dd2ac, 0x5b54, 0x4c57, 0x84, 0x87, 0x78, 0x2f, 0x9a, 0xdf, 0xe8, 0xd7); + +typedef struct _TPLG_INFO { + PVOID rkTplg; + UINT64 rkTplgSz; +} TPLG_INFO, * PTPLG_INFO; + +typedef _Must_inspect_result_ NTSTATUS(*PGET_ADSP_RESOURCES) (_In_ PVOID _context, _Out_ _PCI_BAR* acpBar, PTPLG_INFO tplgInfo); +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_ VOID(*PADSP_DPC_CALLBACK)(PVOID context); +typedef _Must_inspect_result_ NTSTATUS (*PADSP_QUEUE_DPC)(PVOID context); +typedef _Must_inspect_result_ NTSTATUS(*PREGISTER_ADSP_INTERRUPT) (_In_ PVOID _context, _In_ PADSP_INTERRUPT_CALLBACK callback, _In_ PADSP_DPC_CALLBACK dpcCallback, _In_ PVOID callbackContext); +typedef _Must_inspect_result_ NTSTATUS(*PUNREGISTER_ADSP_INTERRUPT) (_In_ PVOID _context); + +typedef struct _RKDSP_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. + + PGET_ADSP_RESOURCES GetResources; + PDSP_SET_POWER_STATE SetDSPPowerState; + PREGISTER_ADSP_INTERRUPT RegisterInterrupt; + PUNREGISTER_ADSP_INTERRUPT UnregisterInterrupt; + PADSP_QUEUE_DPC QueueDPCForInterrupt; +} RKDSP_BUS_INTERFACE, * PRKDSP_BUS_INTERFACE; + +#ifndef ADSP_DECL +RKDSP_BUS_INTERFACE RKDSP_BusInterface(PVOID Context); +#endif +#endif \ No newline at end of file diff --git a/drivers/audio/rk3xi2sbus/buspdo.cpp b/drivers/audio/rk3xi2sbus/buspdo.cpp new file mode 100644 index 0000000..c8e7d11 --- /dev/null +++ b/drivers/audio/rk3xi2sbus/buspdo.cpp @@ -0,0 +1,328 @@ +#include "driver.h" +#include "adsp.h" + +NTSTATUS +Bus_CreatePdo( + _In_ WDFDEVICE Device, + _In_ PWDFDEVICE_INIT DeviceInit, + _In_ PPDO_IDENTIFICATION_DESCRIPTION Desc +); + +NTSTATUS +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); + + RKI2SBusPrint(DEBUG_LEVEL_INFO, DBG_INIT, + "%s\n", __func__); + + dst->FdoContext = src->FdoContext; + RtlCopyMemory(&dst->CodecIds, &src->CodecIds, sizeof(dst->CodecIds)); + + return STATUS_SUCCESS; +} + +BOOLEAN +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); + + RKI2SBusPrint(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 +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 +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); + + RKI2SBusPrint(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"Rockchip I2S Controller"); + 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); + + RKI2SBusPrint(DEBUG_LEVEL_INFO, DBG_INIT, + "%s\n", __func__); + + if (Desc->CodecIds.IsDSP) { + // + // Set DeviceType + // + WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_BUS_EXTENDER); + + //Provide InstanceId + status = RtlUnicodeStringPrintf(&deviceId, L"00000000"); + if (!NT_SUCCESS(status)) { + return status; + } + + status = WdfPdoInitAssignInstanceID(DeviceInit, &deviceId); + if (!NT_SUCCESS(status)) { + return status; + } + + // + // Provide DeviceID, HardwareIDs, CompatibleIDs + status = RtlUnicodeStringPrintf(&deviceId, L"CSAUDIO\\RK&CLTR"); + 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\\RK&CLTR"); + 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; + } + } + + status = RtlUnicodeStringPrintf(&buffer, L"0"); + 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"Rockchip I2S Controller"); + } + 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, + 0x409); + if (!NT_SUCCESS(status)) { + return status; + } + + WdfPdoInitSetDefaultLocale(DeviceInit, 0x409); + + // + // 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)); + + // + // Set some properties for the child device. + // + WDF_DEVICE_PNP_CAPABILITIES_INIT(&pnpCaps); + pnpCaps.Removable = WdfFalse; + pnpCaps.EjectSupported = WdfFalse; + pnpCaps.SurpriseRemovalOK = WdfFalse; + pnpCaps.UniqueID = WdfTrue; + + pnpCaps.Address = 0; + pnpCaps.UINumber = 0; + + 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); + + RKDSP_BUS_INTERFACE busInterface = RKDSP_BusInterface(pdoData); + WDF_QUERY_INTERFACE_CONFIG_INIT(&qiConfig, + (PINTERFACE)&busInterface, + &GUID_RKDSP_BUS_INTERFACE, + NULL); + status = WdfDeviceAddQueryInterface(hChild, &qiConfig); + + return status; +} \ No newline at end of file diff --git a/drivers/audio/rk3xi2sbus/buspdo.h b/drivers/audio/rk3xi2sbus/buspdo.h new file mode 100644 index 0000000..ca2af1c --- /dev/null +++ b/drivers/audio/rk3xi2sbus/buspdo.h @@ -0,0 +1,64 @@ +#if !defined(_RKI2SBUS_BUSPDO_H_) +#define _RKI2SBUS_BUSPDO_H_ + +#define MAX_INSTANCE_ID_LEN 80 + +typedef struct _CODEC_IDS { + BOOL IsDSP; +} 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; + +// +// This is PDO device-extension. +// +typedef struct _PDO_DEVICE_DATA +{ + PFDO_CONTEXT FdoContext; + + CODEC_IDS CodecIds; + +} PDO_DEVICE_DATA, * PPDO_DEVICE_DATA; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(PDO_DEVICE_DATA, PdoGetData) + +extern "C" { + + NTSTATUS + Bus_EvtChildListIdentificationDescriptionDuplicate( + WDFCHILDLIST DeviceList, + PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER SourceIdentificationDescription, + PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER DestinationIdentificationDescription + ); + + BOOLEAN + Bus_EvtChildListIdentificationDescriptionCompare( + WDFCHILDLIST DeviceList, + PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER FirstIdentificationDescription, + PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER SecondIdentificationDescription + ); + + VOID + Bus_EvtChildListIdentificationDescriptionCleanup( + _In_ WDFCHILDLIST DeviceList, + _Inout_ PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER IdentificationDescription + ); + + NTSTATUS + Bus_EvtDeviceListCreatePdo( + WDFCHILDLIST DeviceList, + PWDF_CHILD_IDENTIFICATION_DESCRIPTION_HEADER IdentificationDescription, + PWDFDEVICE_INIT ChildInit + ); + +} + +#endif diff --git a/drivers/audio/rk3xi2sbus/driver.h b/drivers/audio/rk3xi2sbus/driver.h new file mode 100644 index 0000000..0bdeea3 --- /dev/null +++ b/drivers/audio/rk3xi2sbus/driver.h @@ -0,0 +1,53 @@ +#if !defined(_RKI2SBUS_H_) +#define _RKI2SBUS_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 +#include + +#include +#include +#include +#include +#include +#include + +#include "fdo.h" +#include "buspdo.h" + +#define DRIVERNAME "acpbus.sys: " +#define RKI2SBUS_POOL_TAG 'SIKR' + +// +// Helper macros +// + +#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 + +static ULONG RKI2SBusDebugLevel = 100; +static ULONG RKI2SBusDebugCatagories = DBG_INIT || DBG_PNP || DBG_IOCTL; + +#if 0 +#define RKI2SBusPrint(dbglevel, dbgcatagory, fmt, ...) { \ + if (RKI2SBusDebugLevel >= dbglevel && \ + (RKI2SBusDebugCatagories && dbgcatagory)) \ + { \ + DbgPrint(DRIVERNAME); \ + DbgPrint(fmt, __VA_ARGS__); \ + } \ +} +#else +#define RKI2SBusPrint(dbglevel, fmt, ...) { \ +} +#endif +#endif \ No newline at end of file diff --git a/drivers/audio/rk3xi2sbus/fdo.cpp b/drivers/audio/rk3xi2sbus/fdo.cpp new file mode 100644 index 0000000..973f897 --- /dev/null +++ b/drivers/audio/rk3xi2sbus/fdo.cpp @@ -0,0 +1,378 @@ +#include "driver.h" +#include "rk-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_EXIT Fdo_EvtDeviceD0Exit; +EVT_WDF_DEVICE_SELF_MANAGED_IO_INIT Fdo_EvtDeviceSelfManagedIoInit; + +NTSTATUS +Fdo_Initialize( + _In_ PFDO_CONTEXT FdoCtx +); + +NTSTATUS +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; + + RKI2SBusPrint(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.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)) { + RKI2SBusPrint(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; + } + +Exit: + return status; +} + +BOOLEAN dsp_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); + } + + return handled; +} + +VOID dsp_dpc( + WDFINTERRUPT Interrupt, + WDFOBJECT AssociatedObject) { + + UNREFERENCED_PARAMETER(AssociatedObject); + + WDFDEVICE Device = WdfInterruptGetDevice(Interrupt); + PFDO_CONTEXT fdoCtx = Fdo_GetContext(Device); + + if (fdoCtx->dspDPCCallback) { + fdoCtx->dspDPCCallback(fdoCtx->dspInterruptContext); + } +} + +NTSTATUS +Fdo_Initialize( + _In_ PFDO_CONTEXT FdoCtx +) +{ + NTSTATUS status; + WDFDEVICE device; + WDF_INTERRUPT_CONFIG interruptConfig; + + device = FdoCtx->WdfDevice; + + RKI2SBusPrint(DEBUG_LEVEL_INFO, DBG_INIT, + "%s\n", __func__); + + // + // Create an interrupt object for hardware notifications + // + WDF_INTERRUPT_CONFIG_INIT( + &interruptConfig, + dsp_interrupt, + dsp_dpc); + + status = WdfInterruptCreate( + device, + &interruptConfig, + WDF_NO_OBJECT_ATTRIBUTES, + &FdoCtx->Interrupt); + + if (!NT_SUCCESS(status)) + { + RKI2SBusPrint(DEBUG_LEVEL_ERROR, DBG_PNP, + "Error creating WDF interrupt object - %!STATUS!", + status); + + return status; + } + + return STATUS_SUCCESS; +} + +NTSTATUS +Fdo_EvtDevicePrepareHardware( + _In_ WDFDEVICE Device, + _In_ WDFCMRESLIST ResourcesRaw, + _In_ WDFCMRESLIST ResourcesTranslated +) +{ + UNREFERENCED_PARAMETER(ResourcesRaw); + + BOOLEAN fMmioFound = FALSE; + NTSTATUS status; + PFDO_CONTEXT fdoCtx; + ULONG resourceCount; + + fdoCtx = Fdo_GetContext(Device); + resourceCount = WdfCmResourceListGetCount(ResourcesTranslated); + + RKI2SBusPrint(DEBUG_LEVEL_INFO, DBG_INIT, + "%s\n", __func__); + + for (ULONG i = 0; i < resourceCount; i++) + { + PCM_PARTIAL_RESOURCE_DESCRIPTOR pDescriptor; + + pDescriptor = WdfCmResourceListGetDescriptor( + ResourcesTranslated, i); + + switch (pDescriptor->Type) + { + case CmResourceTypeMemory: + //Look for MMIO + if (fMmioFound == FALSE) { + RKI2SBusPrint(DEBUG_LEVEL_INFO, DBG_INIT, + "Found MMIO: 0x%llx (size 0x%lx)\n", pDescriptor->u.Memory.Start.QuadPart, pDescriptor->u.Memory.Length); + + fdoCtx->m_MMIO.Base.Base = MmMapIoSpace(pDescriptor->u.Memory.Start, pDescriptor->u.Memory.Length, MmNonCached); + fdoCtx->m_MMIO.PhysAddr = pDescriptor->u.Memory.Start; + fdoCtx->m_MMIO.Len = pDescriptor->u.Memory.Length; + + RKI2SBusPrint(DEBUG_LEVEL_INFO, DBG_INIT, + "Mapped to %p\n", fdoCtx->m_MMIO.Base.baseptr); + fMmioFound = TRUE; + } + break; + } + } + + if (fdoCtx->m_MMIO.Base.Base == NULL) { + status = STATUS_NOT_FOUND; //MMIO is required + return status; + } + + fdoCtx->rkTplg = NULL; + fdoCtx->rkTplgSz = 0; + + { //Check topology for Rockchip I2S + RK_TPLG rkTplg = { 0 }; + NTSTATUS status2 = GetRKTplg(Device, &rkTplg); + if (NT_SUCCESS(status2) && rkTplg.magic == RKTPLG_MAGIC) { + fdoCtx->rkTplg = ExAllocatePoolZero(NonPagedPool, rkTplg.length, RKI2SBUS_POOL_TAG); + RtlCopyMemory(fdoCtx->rkTplg, &rkTplg, rkTplg.length); + fdoCtx->rkTplgSz = rkTplg.length; + } + } + + status = STATUS_SUCCESS; + + return status; +} + +NTSTATUS +Fdo_EvtDeviceReleaseHardware( + _In_ WDFDEVICE Device, + _In_ WDFCMRESLIST ResourcesTranslated +) +{ + PFDO_CONTEXT fdoCtx; + + UNREFERENCED_PARAMETER(ResourcesTranslated); + + fdoCtx = Fdo_GetContext(Device); + + RKI2SBusPrint(DEBUG_LEVEL_INFO, DBG_INIT, + "%s\n", __func__); + + if (fdoCtx->rkTplg) + ExFreePoolWithTag(fdoCtx->rkTplg, RKI2SBUS_POOL_TAG); + + if (fdoCtx->m_MMIO.Base.Base) + MmUnmapIoSpace(fdoCtx->m_MMIO.Base.Base, fdoCtx->m_MMIO.Len); + + return STATUS_SUCCESS; +} + +NTSTATUS +Fdo_EvtDeviceD0Entry( + _In_ WDFDEVICE Device, + _In_ WDF_POWER_DEVICE_STATE PreviousState +) +{ + UNREFERENCED_PARAMETER(PreviousState); + + NTSTATUS status; + PFDO_CONTEXT fdoCtx; + + fdoCtx = Fdo_GetContext(Device); + + RKI2SBusPrint(DEBUG_LEVEL_INFO, DBG_INIT, + "%s\n", __func__); + + status = STATUS_SUCCESS; + + if (!NT_SUCCESS(status)) { + return status; + } + + return status; +} + +NTSTATUS +Fdo_EvtDeviceD0Exit( + _In_ WDFDEVICE Device, + _In_ WDF_POWER_DEVICE_STATE TargetState +) +{ + UNREFERENCED_PARAMETER(Device); + UNREFERENCED_PARAMETER(TargetState); + + return STATUS_SUCCESS; +} + +NTSTATUS +Fdo_EvtDeviceSelfManagedIoInit( + _In_ WDFDEVICE Device +) +{ + NTSTATUS status = STATUS_SUCCESS; + PFDO_CONTEXT fdoCtx; + + fdoCtx = Fdo_GetContext(Device); + + WdfChildListBeginScan(WdfFdoGetDefaultChildList(Device)); + + fdoCtx->dspInterruptCallback = NULL; + + 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.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)); + + RKI2SBusPrint(DEBUG_LEVEL_INFO, DBG_INIT, + "scan complete\n"); + return status; +} \ No newline at end of file diff --git a/drivers/audio/rk3xi2sbus/fdo.h b/drivers/audio/rk3xi2sbus/fdo.h new file mode 100644 index 0000000..48d4cd5 --- /dev/null +++ b/drivers/audio/rk3xi2sbus/fdo.h @@ -0,0 +1,43 @@ +#if !defined(_RKI2SBUS_FDO_H_) +#define _RKI2SBUS_FDO_H_ + +union baseaddr { + PVOID Base; + UINT8* baseptr; +}; + +typedef struct _PCI_BAR { + union baseaddr Base; + PHYSICAL_ADDRESS PhysAddr; + ULONG Len; +} PCI_BAR, * PPCI_BAR; + +#include "adsp.h" + +struct _FDO_CONTEXT; +struct _PDO_DEVICE_DATA; + +typedef struct _FDO_CONTEXT +{ + WDFDEVICE WdfDevice; + + PCI_BAR m_MMIO; //required + WDFINTERRUPT Interrupt; + + BOOLEAN ControllerEnabled; + + PADSP_INTERRUPT_CALLBACK dspInterruptCallback; + PADSP_DPC_CALLBACK dspDPCCallback; + PVOID dspInterruptContext; + PVOID rkTplg; + UINT64 rkTplgSz; +} FDO_CONTEXT, * PFDO_CONTEXT; + +WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(FDO_CONTEXT, Fdo_GetContext) + +NTSTATUS +Fdo_Create( + _Inout_ PWDFDEVICE_INIT DeviceInit +); + +#endif \ No newline at end of file diff --git a/drivers/audio/rk3xi2sbus/resource.h b/drivers/audio/rk3xi2sbus/resource.h new file mode 100644 index 0000000..72493ca --- /dev/null +++ b/drivers/audio/rk3xi2sbus/resource.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by acpbus.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 diff --git a/drivers/audio/rk3xi2sbus/rk-tplg.cpp b/drivers/audio/rk3xi2sbus/rk-tplg.cpp new file mode 100644 index 0000000..987cafa --- /dev/null +++ b/drivers/audio/rk3xi2sbus/rk-tplg.cpp @@ -0,0 +1,166 @@ +#include "driver.h" +#include +#include "rk-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)); +} + +void copyDSDParamNum(PACPI_METHOD_ARGUMENT dsdParameterData, UINT32* buf) { + RtlZeroMemory(buf, sizeof(*buf)); + RtlCopyMemory(buf, dsdParameterData->Data, min(dsdParameterData->DataLength, sizeof(*buf))); +} + +NTSTATUS +GetRKTplg( + _In_ WDFDEVICE FxDevice, + RK_TPLG* rkTplg +) +{ + if (!rkTplg) { + 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; + } + + RKI2SBusPrint(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(rkTplg, sizeof(*rkTplg)); + rkTplg->magic = RKTPLG_MAGIC; + rkTplg->length = sizeof(*rkTplg); + + 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; + +#define CHECK_STR(dsdParameter, str) (dsdParameter->DataLength >= strlen(str) && strncmp((const char *)&dsdParameter->Data[0], str, dsdParameter->DataLength) == 0) + + 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 (CHECK_STR(dsdParameterName, "rockchip,dma")) { + copyDSDParam(dsdParameterData, (char **)&rkTplg->dma_name); + } + else if (CHECK_STR(dsdParameterName, "rockchip,tplg")) { + copyDSDParam(dsdParameterData, (char**)&rkTplg->audio_tplg); + } + else if (CHECK_STR(dsdParameterName, "rockchip,tx")) { + copyDSDParamNum(dsdParameterData, &rkTplg->tx); + } + else if (CHECK_STR(dsdParameterName, "rockchip,rx")) { + copyDSDParamNum(dsdParameterData, &rkTplg->rx); + } + } + } + +Exit: + if (outputMemory != WDF_NO_HANDLE) { + WdfObjectDelete(outputMemory); + } + return status; +} \ No newline at end of file diff --git a/drivers/audio/rk3xi2sbus/rk-tplg.h b/drivers/audio/rk3xi2sbus/rk-tplg.h new file mode 100644 index 0000000..c7316be --- /dev/null +++ b/drivers/audio/rk3xi2sbus/rk-tplg.h @@ -0,0 +1,16 @@ +#ifndef _ROCKCHIP_TPLG +#define _ROCKCHIP_TPLG + +#define RKTPLG_MAGIC '$RKC' + +typedef struct _RK_TPLG { + UINT32 magic; + UINT32 length; + char dma_name[32]; + UINT32 tx; + UINT32 rx; + char audio_tplg[32]; +} RK_TPLG, *PRK_TPLG; + +NTSTATUS GetRKTplg(WDFDEVICE FxDevice, RK_TPLG *rkTplg); +#endif \ No newline at end of file diff --git a/drivers/audio/rk3xi2sbus/rk3xi2sbus.cpp b/drivers/audio/rk3xi2sbus/rk3xi2sbus.cpp new file mode 100644 index 0000000..5d5aee8 --- /dev/null +++ b/drivers/audio/rk3xi2sbus/rk3xi2sbus.cpp @@ -0,0 +1,58 @@ +#include "driver.h" + +NTSTATUS +RKI2SBusEvtDeviceAdd( + _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 +DriverEntry( +__in PDRIVER_OBJECT DriverObject, +__in PUNICODE_STRING RegistryPath +) +{ + WDF_DRIVER_CONFIG config; + NTSTATUS status; + WDFDRIVER wdfDriver; + + RKI2SBusPrint(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, RKI2SBusEvtDeviceAdd); + + status = WdfDriverCreate(DriverObject, + RegistryPath, + WDF_NO_OBJECT_ATTRIBUTES, + &config, + &wdfDriver + ); + if (!NT_SUCCESS(status)) + { + RKI2SBusPrint(DEBUG_LEVEL_ERROR, DBG_INIT, + "WdfDriverCreate failed %x\n", status); + } + + return status; +} diff --git a/drivers/audio/rk3xi2sbus/rk3xi2sbus.inx b/drivers/audio/rk3xi2sbus/rk3xi2sbus.inx new file mode 100644 index 0000000..71e024f --- /dev/null +++ b/drivers/audio/rk3xi2sbus/rk3xi2sbus.inx @@ -0,0 +1,88 @@ +;/*++ +; +;Copyright (c) CoolStar. All rights reserved. +; +;Module Name: +; rk3xi2sbus.inf +; +;Abstract: +; INF file for installing the Rockchip I2S Bus Driver +; +; +;--*/ + +[Version] +Signature = "$WINDOWS NT$" +Class = System +ClassGuid = {4d36e97d-e325-11ce-bfc1-08002be10318} +Provider = CoolStar +DriverVer = 8/15/2022,1.0.0 +CatalogFile = rk3xi2sbus.cat +PnpLockdown = 1 + +[DestinationDirs] +DefaultDestDir = 12 + +; ================= Class section ===================== + +[SourceDisksNames] +1 = %DiskId1%,,,"" + +[SourceDisksFiles] +rk3xi2sbus.sys = 1,, + +[SignatureAttributes] +rk3xi2sbus.sys=SignatureAttributes.DRM + +[SignatureAttributes.DRM] +DRMLevel=1300 + +[SignatureAttributes.PETrust] +PETrust=true + +;***************************************** +; RK3XI2SBus Install Section +;***************************************** + +[Manufacturer] +%StdMfg%=Standard,NT$ARCH$ + +; Decorated model section take precedence over undecorated +; ones on XP and later. +[Standard.NT$ARCH$] +%RK3XI2SBus.DeviceDesc%=RK3XI2SBus_Device, ACPI\RKCP3003 + +[RK3XI2SBus_Device.NT] +CopyFiles=Drivers_Dir + +[RK3XI2SBus_Device.NT.HW] +AddReg=RK3XI2SBus_AddReg +Include=pci.inf +Needs=PciD3ColdSupported.HW + +[Drivers_Dir] +rk3xi2sbus.sys + +[RK3XI2SBus_AddReg] +; Set to 1 to connect the first interrupt resource found, 0 to leave disconnected +HKR,Settings,"ConnectInterrupt",0x00010001,0 + +;-------------- Service installation +[RK3XI2SBus_Device.NT.Services] +AddService = RK3XI2SBus,%SPSVCINST_ASSOCSERVICE%, RK3XI2SBus_Service_Inst + +; -------------- RK3XI2SBus driver install sections +[RK3XI2SBus_Service_Inst] +DisplayName = %RK3XI2SBus.SVCDESC% +ServiceType = 1 ; SERVICE_KERNEL_DRIVER +StartType = 3 ; SERVICE_DEMAND_START +ErrorControl = 1 ; SERVICE_ERROR_NORMAL +ServiceBinary = %12%\rk3xi2sbus.sys +LoadOrderGroup = Base + +[Strings] +SPSVCINST_ASSOCSERVICE= 0x00000002 +StdMfg = "Rockchip" +DiskId1 = "Rockchip I2S Bus Installation Disk #1" +RK3XI2SBus.DeviceDesc = "Rockchip I2S Bus" +RK3XI2SBus.SVCDESC = "Rockchip I2S Bus Service" \ No newline at end of file diff --git a/drivers/audio/rk3xi2sbus/rk3xi2sbus.rc b/drivers/audio/rk3xi2sbus/rk3xi2sbus.rc new file mode 100644 index 0000000..88150a5 --- /dev/null +++ b/drivers/audio/rk3xi2sbus/rk3xi2sbus.rc @@ -0,0 +1,41 @@ +/*++ + +Copyright (c) Microsoft Corporation All Rights Reserved + +Module Name: + + rk3xi2sbus.rc + +Abstract: + +--*/ + +#include + +#define VER_FILETYPE VFT_DRV +#define VER_FILESUBTYPE VFT2_DRV_SYSTEM +#define VER_FILEDESCRIPTION_STR "Rockchip I2S Bus" +#define VER_INTERNALNAME_STR "rk3xi2sbus.sys" +#define VER_ORIGINALFILENAME_STR "rk3xi2sbus.sys" + +#define VER_LEGALCOPYRIGHT_YEARS "2023" +#define VER_LEGALCOPYRIGHT_STR "Copyright (C) " VER_LEGALCOPYRIGHT_YEARS " CoolStar." + +#define VER_FILEVERSION 1,0,0,0 +#define VER_PRODUCTVERSION_STR "1.0.0.0" +#define VER_PRODUCTVERSION 1,0,0,0 +#define LVER_PRODUCTVERSION_STR L"1.0.0.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 "Rockchip I2S Bus" + +#include "common.ver" \ No newline at end of file diff --git a/drivers/audio/rk3xi2sbus/rk3xi2sbus.vcxproj b/drivers/audio/rk3xi2sbus/rk3xi2sbus.vcxproj new file mode 100644 index 0000000..4dc29c1 --- /dev/null +++ b/drivers/audio/rk3xi2sbus/rk3xi2sbus.vcxproj @@ -0,0 +1,110 @@ + + + + + Debug + ARM64 + + + Release + ARM64 + + + + {BA95E25D-392E-4BC9-B481-E4D9FB9AFFB4} + {1bc93793-694f-48fe-9372-81e2b05556fd} + v4.5 + 11.0 + Win8.1 Debug + Win32 + rk3xi2sbus + $(LatestTargetPlatformVersion) + rk3xi2sbus + + + + Windows10 + true + WindowsKernelModeDriver10.0 + Driver + KMDF + + + Windows10 + false + WindowsKernelModeDriver10.0 + Driver + KMDF + + + + + + + + + + + + DbgengKernelDebugger + + + DbgengKernelDebugger + + + + trace.h + true + false + false + + + 1.0.0 + + + SHA256 + + + + + trace.h + true + false + false + + + 1.0.0 + + + SHA256 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/drivers/audio/rk3xi2sbus/rk3xi2sbus.vcxproj.Filters b/drivers/audio/rk3xi2sbus/rk3xi2sbus.vcxproj.Filters new file mode 100644 index 0000000..e4c5b7b --- /dev/null +++ b/drivers/audio/rk3xi2sbus/rk3xi2sbus.vcxproj.Filters @@ -0,0 +1,68 @@ + + + + + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;* + {E10CB9FB-4852-4353-85F5-667D4D2A13DD} + + + h;hpp;hxx;hm;inl;inc;xsd + {EC8940D8-5E2B-49AB-AB85-7CABCAE698D1} + + + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml + {42A1FDF4-6DB4-41B2-9423-8002A20D1B0D} + + + inf;inv;inx;mof;mc; + {FD9F921C-D1EF-421B-A692-F23423B32E64} + + + + + Driver Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file