blob: e3bc07de10259aef728b33ea59b9ca0d6a409cba [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "nhlt.h"
#include <inttypes.h>
#include <lib/zircon-internal/align.h>
#include <limits.h>
#include <stdio.h>
#include <zircon/process.h>
#include <ddk/debug.h>
#include <ddk/device.h>
#include <fbl/auto_call.h>
#include "errors.h"
/**
* Reference:
*
* Intel Smart Sound Technology Audio DSP Non-HD Audio ACPI High Level Design
* Architecture Guide/Overview
* Revision 0.7
* November 2015
*
* 561555_SST Non-HD Audio ACPI HLD v0 7_DRAFT.pdf
*/
static const uint8_t NHLT_UUID[] = {
/* 0000 */ 0x6E, 0x88, 0x9F, 0xA6, 0xEB, 0x6C, 0x94, 0x45,
/* 0008 */ 0xA4, 0x1F, 0x7B, 0x5D, 0xCE, 0x24, 0xC5, 0x53};
zx_status_t nhlt_publish_metadata(zx_device_t* dev, uint8_t bbn, uint64_t adr, ACPI_HANDLE object) {
zx_status_t status = ZX_OK;
// parameters
ACPI_OBJECT objs[] = {
{
// uuid
.Buffer =
{
.Type = ACPI_TYPE_BUFFER,
.Length = sizeof(NHLT_UUID),
.Pointer = (uint8_t*)NHLT_UUID,
},
},
{
// revision id
.Integer =
{
.Type = ACPI_TYPE_INTEGER,
.Value = 1,
},
},
{
// function id
.Integer =
{
.Type = ACPI_TYPE_INTEGER,
.Value = 1,
},
},
};
ACPI_OBJECT_LIST params = {
.Count = countof(objs),
.Pointer = objs,
};
// output buffer
ACPI_BUFFER out = {
.Length = ACPI_ALLOCATE_BUFFER,
.Pointer = NULL,
};
// Fetch the NHLT resource
ACPI_STATUS acpi_status = AcpiEvaluateObject(object, (char*)"_DSM", &params, &out);
if (acpi_status != AE_OK) {
zxlogf(ERROR, "acpi: failed to fetch NHLT blob (acpi_status 0x%x)", acpi_status);
return acpi_to_zx_status(acpi_status);
}
auto release_object = fbl::MakeAutoCall([&out] { ACPI_FREE(out.Pointer); });
ACPI_OBJECT* out_obj = static_cast<ACPI_OBJECT*>(out.Pointer);
if (out_obj->Type != ACPI_TYPE_BUFFER) {
zxlogf(ERROR, "acpi: unexpected object type (%u) for NHLT blob", out_obj->Type);
return ZX_ERR_INTERNAL;
}
ACPI_RESOURCE* res = NULL;
acpi_status = AcpiBufferToResource(out_obj->Buffer.Pointer,
static_cast<uint16_t>(out_obj->Buffer.Length), &res);
if (acpi_status != AE_OK) {
zxlogf(ERROR, "acpi: failed to parse NHLT resource (acpi_status 0x%x)", acpi_status);
return acpi_to_zx_status(acpi_status);
}
if (res->Type != ACPI_RESOURCE_TYPE_ADDRESS64) {
zxlogf(ERROR, "acpi: unexpected NHLT resource type (%u)", res->Type);
return ZX_ERR_INTERNAL;
}
zx_paddr_t paddr = (zx_paddr_t)res->Data.Address64.Address.Minimum;
size_t size = (size_t)res->Data.Address64.Address.AddressLength;
// Read the blob
zx_handle_t vmo;
zx_paddr_t page_start = ZX_ROUNDDOWN(paddr, PAGE_SIZE);
size_t page_offset = (paddr & (PAGE_SIZE - 1));
size_t page_size = ZX_ROUNDUP(page_offset + size, PAGE_SIZE);
// Please do not use get_root_resource() in new code. See ZX-1467.
status = zx_vmo_create_physical(get_root_resource(), page_start, page_size, &vmo);
if (status != ZX_OK) {
zxlogf(ERROR, "acpi: failed to create NHLT VMO (res %d)", status);
return status;
}
// We cannot read physical VMOs directly and must map it
zx_vaddr_t vaddr = 0;
status = zx_vmar_map(zx_vmar_root_self(), ZX_VM_PERM_READ, 0, vmo, 0, page_size, &vaddr);
if (status != ZX_OK) {
zxlogf(ERROR, "acpi: failed to map NHLT blob (res %d)", status);
return status;
}
void* nhlt = (void*)(vaddr + page_offset);
// Publish the NHLT as metadata on the future PCI device node...
// The canonical path to the PCI device is /dev/sys/pci/<b:d.f>
char path[PATH_MAX];
snprintf(path, sizeof(path), "/dev/sys/pci/%02x:%02x.%01x", bbn, (unsigned)((adr >> 16) & 0xFFFF),
(unsigned)(adr & 0xFFFF));
status = device_publish_metadata(dev, path, *(uint32_t*)"NHLT", nhlt, size);
if (status != ZX_OK) {
zxlogf(ERROR, "acpi: failed to publish NHLT metadata (res %d)", status);
}
zxlogf(TRACE, "acpi: published NHLT metadata for device at %s", path);
zx_vmar_unmap(zx_vmar_root_self(), vaddr, ZX_ROUNDUP(size, PAGE_SIZE));
return status;
}