1 // Copyright 2017 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 <zircon/device/intel-hda.h>
6 #include <lib/fdio/io.h>
7 
8 #include <fbl/algorithm.h>
9 #include <intel-hda/utils/intel-hda-registers.h>
10 
11 #include <utility>
12 
13 #include "intel_hda_controller.h"
14 
15 namespace audio {
16 namespace intel_hda {
17 
18 IntelHDAController::ControllerTree IntelHDAController::controllers_;
19 
ihda_dump_sdctl(const char * name,const void * base,size_t offset,bool crlf=true)20 static int ihda_dump_sdctl(const char* name, const void* base, size_t offset, bool crlf = true) {
21     uint32_t val = *reinterpret_cast<int32_t*>(reinterpret_cast<intptr_t>(base) + offset);
22     val &= 0xFFFFFF;
23     return printf("[%02zx] %10s : %06x   (%u)%s",
24                   offset, name, val, val, crlf ? "\n" : "");
25 }
26 
ihda_dump32(const char * name,const void * base,size_t offset,bool crlf=true)27 static int ihda_dump32(const char* name, const void* base, size_t offset, bool crlf = true) {
28     uint32_t val = *reinterpret_cast<uint32_t*>(reinterpret_cast<intptr_t>(base) + offset);
29     return printf("[%02zx] %10s : %08x (%u)%s",
30                   offset, name, val, val, crlf ? "\n" : "");
31 }
32 
ihda_dump16(const char * name,const void * base,size_t offset,bool crlf=true)33 static int ihda_dump16(const char* name, const void* base, size_t offset, bool crlf = true) {
34     uint16_t val = *reinterpret_cast<uint16_t*>(reinterpret_cast<intptr_t>(base) + offset);
35     return printf("[%02zx] %10s : %04hx     (%hu)%s",
36                   offset, name, val, val, crlf ? "\n" : "");
37 }
38 
ihda_dump8(const char * name,const void * base,size_t offset,bool crlf=true)39 static int ihda_dump8(const char* name, const void* base, size_t offset, bool crlf = true) {
40     uint8_t val = *reinterpret_cast<int8_t*>(reinterpret_cast<intptr_t>(base) + offset);
41     return printf("[%02zx] %10s : %02x       (%u)%s",
42                   offset, name, val, val, crlf ? "\n" : "");
43 }
44 
pad(int done,int width)45 static void pad(int done, int width) {
46     if (done < 0) return;
47     while (done < width) {
48         printf(" ");
49         done++;
50     }
51 }
52 
ihda_dump_stream_regs(const char * name,size_t count,const hda_stream_desc_regs_t * regs)53 static void ihda_dump_stream_regs(const char* name,
54                                   size_t count,
55                                   const hda_stream_desc_regs_t* regs) {
56     static const struct {
57         const char* name;
58         int (*dump_fn)(const char*, const void*, size_t, bool);
59         size_t offset;
60     } STREAM_REGS[] = {
61         { "CTL",   ihda_dump_sdctl, offsetof(hda_stream_desc_regs_t, ctl_sts.w) },
62         { "STS",   ihda_dump8,      offsetof(hda_stream_desc_regs_t, ctl_sts.b.sts) },
63         { "LPIB",  ihda_dump32,     offsetof(hda_stream_desc_regs_t, lpib) },
64         { "CBL",   ihda_dump32,     offsetof(hda_stream_desc_regs_t, cbl) },
65         { "LVI",   ihda_dump16,     offsetof(hda_stream_desc_regs_t, lvi) },
66         { "FIFOD", ihda_dump16,     offsetof(hda_stream_desc_regs_t, fifod) },
67         { "FMT",   ihda_dump16,     offsetof(hda_stream_desc_regs_t, fmt) },
68         { "BDPL",  ihda_dump32,     offsetof(hda_stream_desc_regs_t, bdpl) },
69         { "BDPU",  ihda_dump32,     offsetof(hda_stream_desc_regs_t, bdpu) },
70     };
71     static const size_t COLUMNS = 4;
72     static const int    COLUMN_WIDTH = 40;
73     int done;
74 
75     for (size_t i = 0; i < count; i += COLUMNS) {
76         size_t todo = count - i;
77         if (todo > COLUMNS)
78             todo = COLUMNS;
79 
80         printf("\n");
81         for (size_t j = 0; j < todo; ++j) {
82             done = printf("%s %zu/%zu", name, i + j + 1, count);
83             if ((j + 1) < todo)
84                 pad(done, COLUMN_WIDTH);
85         }
86         printf("\n");
87 
88         for (size_t reg = 0; reg < fbl::count_of(STREAM_REGS); ++reg) {
89             for (size_t j = 0; j < todo; ++j) {
90                 const hda_stream_desc_regs_t* r = regs + i + j;
91                 done = STREAM_REGS[reg].dump_fn(STREAM_REGS[reg].name,
92                                                 r,
93                                                 STREAM_REGS[reg].offset,
94                                                 false);
95                 if ((j + 1) < todo)
96                     pad(done, COLUMN_WIDTH);
97             }
98             printf("\n");
99         }
100     }
101 }
102 
Enumerate()103 zx_status_t IntelHDAController::Enumerate() {
104     static const char* const DEV_PATH = "/dev/class/intel-hda";
105 
106     zx_status_t res = ZirconDevice::Enumerate(nullptr, DEV_PATH,
107     [](void*, uint32_t id, const char* const dev_name) -> zx_status_t {
108         fbl::AllocChecker ac;
109         fbl::unique_ptr<IntelHDAController> dev(new (&ac) IntelHDAController(id, dev_name));
110         if (!ac.check()) {
111             return ZX_ERR_NO_MEMORY;
112         }
113 
114         if (!controllers_.insert_or_find(std::move(dev)))
115             return ZX_ERR_INTERNAL;
116 
117         return ZX_OK;
118     });
119 
120     if (res != ZX_OK)
121         return res;
122 
123     return ZX_OK;
124 }
125 
DumpRegs(int argc,const char ** argv)126 zx_status_t IntelHDAController::DumpRegs(int argc, const char** argv) {
127     zx_status_t res = Connect();
128 
129     if (res != ZX_OK)
130         return res;
131 
132     ihda_controller_snapshot_regs_req_t req;
133     ihda_controller_snapshot_regs_resp_t resp;
134 
135     InitRequest(&req, IHDA_CONTROLLER_CMD_SNAPSHOT_REGS);
136     res = CallDevice(req, &resp);
137     if (res != ZX_OK)
138         return res;
139 
140     const auto  regs_ptr = reinterpret_cast<hda_registers_t*>(resp.snapshot);
141     const auto& regs     = *regs_ptr;
142 
143     printf("Registers for Intel HDA Device #%u\n", id_);
144 
145     ihda_dump16("GCAP",       &regs, offsetof(hda_registers_t, gcap));
146     ihda_dump8 ("VMIN",       &regs, offsetof(hda_registers_t, vmin));
147     ihda_dump8 ("VMAJ",       &regs, offsetof(hda_registers_t, vmaj));
148     ihda_dump16("OUTPAY",     &regs, offsetof(hda_registers_t, outpay));
149     ihda_dump16("INPAY",      &regs, offsetof(hda_registers_t, inpay));
150     ihda_dump32("GCTL",       &regs, offsetof(hda_registers_t, gctl));
151     ihda_dump16("WAKEEN",     &regs, offsetof(hda_registers_t, wakeen));
152     ihda_dump16("STATESTS",   &regs, offsetof(hda_registers_t, statests));
153     ihda_dump16("GSTS",       &regs, offsetof(hda_registers_t, gsts));
154     ihda_dump16("OUTSTRMPAY", &regs, offsetof(hda_registers_t, outstrmpay));
155     ihda_dump16("INSTRMPAY",  &regs, offsetof(hda_registers_t, instrmpay));
156     ihda_dump32("INTCTL",     &regs, offsetof(hda_registers_t, intctl));
157     ihda_dump32("INTSTS",     &regs, offsetof(hda_registers_t, intsts));
158     ihda_dump32("WALCLK",     &regs, offsetof(hda_registers_t, walclk));
159     ihda_dump32("SSYNC",      &regs, offsetof(hda_registers_t, ssync));
160     ihda_dump32("CORBLBASE",  &regs, offsetof(hda_registers_t, corblbase));
161     ihda_dump32("CORBUBASE",  &regs, offsetof(hda_registers_t, corbubase));
162     ihda_dump16("CORBWP",     &regs, offsetof(hda_registers_t, corbwp));
163     ihda_dump16("CORBRP",     &regs, offsetof(hda_registers_t, corbrp));
164     ihda_dump8 ("CORBCTL",    &regs, offsetof(hda_registers_t, corbctl));
165     ihda_dump8 ("CORBSTS",    &regs, offsetof(hda_registers_t, corbsts));
166     ihda_dump8 ("CORBSIZE",   &regs, offsetof(hda_registers_t, corbsize));
167     ihda_dump32("RIRBLBASE",  &regs, offsetof(hda_registers_t, rirblbase));
168     ihda_dump32("RIRBUBASE",  &regs, offsetof(hda_registers_t, rirbubase));
169     ihda_dump16("RIRBWP",     &regs, offsetof(hda_registers_t, rirbwp));
170     ihda_dump16("RINTCNT",    &regs, offsetof(hda_registers_t, rintcnt));
171     ihda_dump8 ("RIRBCTL",    &regs, offsetof(hda_registers_t, rirbctl));
172     ihda_dump8 ("RIRBSTS",    &regs, offsetof(hda_registers_t, rirbsts));
173     ihda_dump8 ("RIRBSIZE",   &regs, offsetof(hda_registers_t, rirbsize));
174     ihda_dump32("ICOI",       &regs, offsetof(hda_registers_t, icoi));
175     ihda_dump32("ICII",       &regs, offsetof(hda_registers_t, icii));
176     ihda_dump16("ICIS",       &regs, offsetof(hda_registers_t, icis));
177     ihda_dump32("DPIBLBASE",  &regs, offsetof(hda_registers_t, dpiblbase));
178     ihda_dump32("DPIBUBASE",  &regs, offsetof(hda_registers_t, dpibubase));
179 
180     uint16_t gcap = REG_RD(&regs.gcap);
181     unsigned int input_stream_cnt  = HDA_REG_GCAP_ISS(gcap);
182     unsigned int output_stream_cnt = HDA_REG_GCAP_OSS(gcap);
183     unsigned int bidir_stream_cnt  = HDA_REG_GCAP_BSS(gcap);
184     const hda_stream_desc_regs_t* sregs = regs.stream_desc;
185 
186     ihda_dump_stream_regs("Input Stream",  input_stream_cnt,  sregs); sregs += input_stream_cnt;
187     ihda_dump_stream_regs("Output Stream", output_stream_cnt, sregs); sregs += output_stream_cnt;
188     ihda_dump_stream_regs("Bi-dir Stream", bidir_stream_cnt,  sregs);
189 
190     return ZX_OK;
191 }
192 
193 }  // namespace audio
194 }  // namespace intel_hda
195