1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <inttypes.h>
6 #include <limits.h>
7 #include <ddk/debug.h>
8 #include <ddk/device.h>
9 #include <ddk/protocol/intelhda/dsp.h>
10 #include <zircon/process.h>
11 
12 #include "errors.h"
13 #include "nhlt.h"
14 
15 /**
16  * Reference:
17  *
18  * Intel Smart Sound Technology Audio DSP Non-HD Audio ACPI High Level Design
19  * Architecture Guide/Overview
20  * Revision 0.7
21  * November 2015
22  *
23  * 561555_SST Non-HD Audio ACPI HLD v0 7_DRAFT.pdf
24  */
25 
26 static const uint8_t NHLT_UUID[] = {
27 /* 0000 */ 0x6E, 0x88, 0x9F, 0xA6, 0xEB, 0x6C, 0x94, 0x45,
28 /* 0008 */ 0xA4, 0x1F, 0x7B, 0x5D, 0xCE, 0x24, 0xC5, 0x53
29 };
30 
nhlt_publish_metadata(zx_device_t * dev,uint8_t bbn,uint64_t adr,ACPI_HANDLE object)31 zx_status_t nhlt_publish_metadata(zx_device_t* dev, uint8_t bbn, uint64_t adr, ACPI_HANDLE object) {
32     zx_status_t status = ZX_OK;
33 
34     // parameters
35     ACPI_OBJECT objs[] = {
36     {   // uuid
37         .Buffer.Type = ACPI_TYPE_BUFFER,
38         .Buffer.Length = sizeof(NHLT_UUID),
39         .Buffer.Pointer = (void*)NHLT_UUID,
40     },
41     {   // revision id
42         .Integer.Type = ACPI_TYPE_INTEGER,
43         .Integer.Value = 1,
44     },
45     {   // function id
46         .Integer.Type = ACPI_TYPE_INTEGER,
47         .Integer.Value = 1,
48     },
49     };
50     ACPI_OBJECT_LIST params = {
51         .Count = countof(objs),
52         .Pointer = objs,
53     };
54 
55     // output buffer
56     ACPI_BUFFER out = {
57         .Length = ACPI_ALLOCATE_BUFFER,
58         .Pointer = NULL,
59     };
60 
61     // Fetch the NHLT resource
62     ACPI_STATUS acpi_status = AcpiEvaluateObject(object, (char*)"_DSM", &params, &out);
63     if (acpi_status != AE_OK) {
64         zxlogf(TRACE, "acpi: failed to fetch NHLT blob (acpi_status %u)\n", acpi_status);
65         return acpi_to_zx_status(acpi_status);
66     }
67 
68     ACPI_OBJECT* out_obj = out.Pointer;
69     if (out_obj->Type != ACPI_TYPE_BUFFER) {
70         zxlogf(ERROR, "acpi: unexpected object type (%u) for NHLT blob\n", out_obj->Type);
71         status = ZX_ERR_INTERNAL;
72         goto out;
73     }
74 
75     ACPI_RESOURCE* res = NULL;
76     acpi_status = AcpiBufferToResource(out_obj->Buffer.Pointer, out_obj->Buffer.Length, &res);
77     if (acpi_status != AE_OK) {
78         zxlogf(ERROR, "acpi: failed to parse NHLT resource (acpi_status %u)\n", acpi_status);
79         status = acpi_to_zx_status(acpi_status);
80         goto out;
81     }
82 
83     if (res->Type != ACPI_RESOURCE_TYPE_ADDRESS64) {
84         zxlogf(ERROR, "acpi: unexpected NHLT resource type (%u)\n", res->Type);
85         status = ZX_ERR_INTERNAL;
86         goto out;
87     }
88 
89     zx_paddr_t paddr = (zx_paddr_t)res->Data.Address64.Address.Minimum;
90     size_t size = (size_t)res->Data.Address64.Address.AddressLength;
91 
92     // Read the blob
93     zx_handle_t vmo;
94     zx_paddr_t page_start = ROUNDDOWN(paddr, PAGE_SIZE);
95     size_t page_offset = (paddr & (PAGE_SIZE-1));
96     size_t page_size = ROUNDUP(page_offset + size, PAGE_SIZE);
97     status = zx_vmo_create_physical(get_root_resource(), page_start, page_size, &vmo);
98     if (status != ZX_OK) {
99         zxlogf(ERROR, "acpi: failed to create NHLT VMO (res %d)\n", status);
100         goto out;
101     }
102 
103     // We cannot read physical VMOs directly and must map it
104     zx_vaddr_t vaddr = 0;
105     status = zx_vmar_map(zx_vmar_root_self(), ZX_VM_PERM_READ, 0, vmo, 0, page_size, &vaddr);
106     if (status != ZX_OK) {
107         zxlogf(ERROR, "acpi: failed to map NHLT blob (res %d)\n", status);
108         goto out;
109     }
110     void* nhlt = (void*)(vaddr + page_offset);
111 
112     // Publish the NHLT as metadata on the future PCI device node...
113     // The canonical path to the PCI device is /dev/sys/pci/<b:d.f>
114     char path[PATH_MAX];
115     snprintf(path, sizeof(path), "/dev/sys/pci/%02x:%02x.%01x", bbn,
116                                  (unsigned)((adr >> 16) & 0xFFFF), (unsigned)(adr & 0xFFFF));
117     status = device_publish_metadata(dev, path, *(uint32_t*)MD_KEY_NHLT, nhlt, size);
118     if (status != ZX_OK) {
119         zxlogf(ERROR, "acpi: failed to publish NHLT metadata (res %d)\n", status);
120     }
121 
122     zxlogf(TRACE, "acpi: published NHLT metadata for device at %s\n", path);
123 
124     zx_vmar_unmap(zx_vmar_root_self(), vaddr, ROUNDUP(size, PAGE_SIZE));
125 out:
126     ACPI_FREE(out.Pointer);
127     return status;
128 }
129