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", ®s, offsetof(hda_registers_t, gcap));
146 ihda_dump8 ("VMIN", ®s, offsetof(hda_registers_t, vmin));
147 ihda_dump8 ("VMAJ", ®s, offsetof(hda_registers_t, vmaj));
148 ihda_dump16("OUTPAY", ®s, offsetof(hda_registers_t, outpay));
149 ihda_dump16("INPAY", ®s, offsetof(hda_registers_t, inpay));
150 ihda_dump32("GCTL", ®s, offsetof(hda_registers_t, gctl));
151 ihda_dump16("WAKEEN", ®s, offsetof(hda_registers_t, wakeen));
152 ihda_dump16("STATESTS", ®s, offsetof(hda_registers_t, statests));
153 ihda_dump16("GSTS", ®s, offsetof(hda_registers_t, gsts));
154 ihda_dump16("OUTSTRMPAY", ®s, offsetof(hda_registers_t, outstrmpay));
155 ihda_dump16("INSTRMPAY", ®s, offsetof(hda_registers_t, instrmpay));
156 ihda_dump32("INTCTL", ®s, offsetof(hda_registers_t, intctl));
157 ihda_dump32("INTSTS", ®s, offsetof(hda_registers_t, intsts));
158 ihda_dump32("WALCLK", ®s, offsetof(hda_registers_t, walclk));
159 ihda_dump32("SSYNC", ®s, offsetof(hda_registers_t, ssync));
160 ihda_dump32("CORBLBASE", ®s, offsetof(hda_registers_t, corblbase));
161 ihda_dump32("CORBUBASE", ®s, offsetof(hda_registers_t, corbubase));
162 ihda_dump16("CORBWP", ®s, offsetof(hda_registers_t, corbwp));
163 ihda_dump16("CORBRP", ®s, offsetof(hda_registers_t, corbrp));
164 ihda_dump8 ("CORBCTL", ®s, offsetof(hda_registers_t, corbctl));
165 ihda_dump8 ("CORBSTS", ®s, offsetof(hda_registers_t, corbsts));
166 ihda_dump8 ("CORBSIZE", ®s, offsetof(hda_registers_t, corbsize));
167 ihda_dump32("RIRBLBASE", ®s, offsetof(hda_registers_t, rirblbase));
168 ihda_dump32("RIRBUBASE", ®s, offsetof(hda_registers_t, rirbubase));
169 ihda_dump16("RIRBWP", ®s, offsetof(hda_registers_t, rirbwp));
170 ihda_dump16("RINTCNT", ®s, offsetof(hda_registers_t, rintcnt));
171 ihda_dump8 ("RIRBCTL", ®s, offsetof(hda_registers_t, rirbctl));
172 ihda_dump8 ("RIRBSTS", ®s, offsetof(hda_registers_t, rirbsts));
173 ihda_dump8 ("RIRBSIZE", ®s, offsetof(hda_registers_t, rirbsize));
174 ihda_dump32("ICOI", ®s, offsetof(hda_registers_t, icoi));
175 ihda_dump32("ICII", ®s, offsetof(hda_registers_t, icii));
176 ihda_dump16("ICIS", ®s, offsetof(hda_registers_t, icis));
177 ihda_dump32("DPIBLBASE", ®s, offsetof(hda_registers_t, dpiblbase));
178 ihda_dump32("DPIBUBASE", ®s, offsetof(hda_registers_t, dpibubase));
179
180 uint16_t gcap = REG_RD(®s.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