| // Copyright 2017 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 "intel_hda_controller.h" |
| |
| #include <lib/fdio/io.h> |
| #include <zircon/device/intel-hda.h> |
| |
| #include <iterator> |
| #include <utility> |
| |
| #include <fbl/algorithm.h> |
| #include <intel-hda/utils/intel-hda-registers.h> |
| |
| namespace audio { |
| namespace intel_hda { |
| |
| IntelHDAController::ControllerTree IntelHDAController::controllers_; |
| |
| static int ihda_dump_sdctl(const char* name, const void* base, size_t offset, bool crlf = true) { |
| uint32_t val = *reinterpret_cast<int32_t*>(reinterpret_cast<intptr_t>(base) + offset); |
| val &= 0xFFFFFF; |
| return printf("[%02zx] %10s : %06x (%u)%s", offset, name, val, val, crlf ? "\n" : ""); |
| } |
| |
| static int ihda_dump32(const char* name, const void* base, size_t offset, bool crlf = true) { |
| uint32_t val = *reinterpret_cast<uint32_t*>(reinterpret_cast<intptr_t>(base) + offset); |
| return printf("[%02zx] %10s : %08x (%u)%s", offset, name, val, val, crlf ? "\n" : ""); |
| } |
| |
| static int ihda_dump16(const char* name, const void* base, size_t offset, bool crlf = true) { |
| uint16_t val = *reinterpret_cast<uint16_t*>(reinterpret_cast<intptr_t>(base) + offset); |
| return printf("[%02zx] %10s : %04hx (%hu)%s", offset, name, val, val, crlf ? "\n" : ""); |
| } |
| |
| static int ihda_dump8(const char* name, const void* base, size_t offset, bool crlf = true) { |
| uint8_t val = *reinterpret_cast<int8_t*>(reinterpret_cast<intptr_t>(base) + offset); |
| return printf("[%02zx] %10s : %02x (%u)%s", offset, name, val, val, crlf ? "\n" : ""); |
| } |
| |
| static void pad(int done, int width) { |
| if (done < 0) |
| return; |
| while (done < width) { |
| printf(" "); |
| done++; |
| } |
| } |
| |
| static void ihda_dump_stream_regs(const char* name, size_t count, |
| const hda_stream_desc_regs_t* regs) { |
| static const struct { |
| const char* name; |
| int (*dump_fn)(const char*, const void*, size_t, bool); |
| size_t offset; |
| } STREAM_REGS[] = { |
| // clang-format off |
| { "CTL", ihda_dump_sdctl, offsetof(hda_stream_desc_regs_t, ctl_sts.w) }, |
| { "STS", ihda_dump8, offsetof(hda_stream_desc_regs_t, ctl_sts.b.sts) }, |
| { "LPIB", ihda_dump32, offsetof(hda_stream_desc_regs_t, lpib) }, |
| { "CBL", ihda_dump32, offsetof(hda_stream_desc_regs_t, cbl) }, |
| { "LVI", ihda_dump16, offsetof(hda_stream_desc_regs_t, lvi) }, |
| { "FIFOD", ihda_dump16, offsetof(hda_stream_desc_regs_t, fifod) }, |
| { "FMT", ihda_dump16, offsetof(hda_stream_desc_regs_t, fmt) }, |
| { "BDPL", ihda_dump32, offsetof(hda_stream_desc_regs_t, bdpl) }, |
| { "BDPU", ihda_dump32, offsetof(hda_stream_desc_regs_t, bdpu) }, |
| // clang-format on |
| }; |
| static const size_t COLUMNS = 4; |
| static const int COLUMN_WIDTH = 40; |
| int done; |
| |
| for (size_t i = 0; i < count; i += COLUMNS) { |
| size_t todo = count - i; |
| if (todo > COLUMNS) |
| todo = COLUMNS; |
| |
| printf("\n"); |
| for (size_t j = 0; j < todo; ++j) { |
| done = printf("%s %zu/%zu", name, i + j + 1, count); |
| if ((j + 1) < todo) |
| pad(done, COLUMN_WIDTH); |
| } |
| printf("\n"); |
| |
| for (size_t reg = 0; reg < std::size(STREAM_REGS); ++reg) { |
| for (size_t j = 0; j < todo; ++j) { |
| const hda_stream_desc_regs_t* r = regs + i + j; |
| done = STREAM_REGS[reg].dump_fn(STREAM_REGS[reg].name, r, STREAM_REGS[reg].offset, false); |
| if ((j + 1) < todo) |
| pad(done, COLUMN_WIDTH); |
| } |
| printf("\n"); |
| } |
| } |
| } |
| |
| zx_status_t IntelHDAController::Enumerate() { |
| static const char* const DEV_PATH = "/dev/class/intel-hda"; |
| |
| zx_status_t res = ZirconDevice::Enumerate( |
| nullptr, DEV_PATH, [](void*, uint32_t id, const char* const dev_name) -> zx_status_t { |
| fbl::AllocChecker ac; |
| std::unique_ptr<IntelHDAController> dev(new (&ac) IntelHDAController(id, dev_name)); |
| if (!ac.check()) { |
| return ZX_ERR_NO_MEMORY; |
| } |
| |
| if (!controllers_.insert_or_find(std::move(dev))) |
| return ZX_ERR_INTERNAL; |
| |
| return ZX_OK; |
| }); |
| |
| if (res != ZX_OK) |
| return res; |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t IntelHDAController::Probe(IntelHDADevice* result) { |
| return ProbeIntelHdaDevice(&device_, result); |
| } |
| |
| zx_status_t IntelHDAController::DumpRegs(int argc, const char** argv) { |
| zx_status_t res = device_.Connect(); |
| if (res != ZX_OK) { |
| return res; |
| } |
| |
| ihda_controller_snapshot_regs_req_t req; |
| ihda_controller_snapshot_regs_resp_t resp; |
| |
| ZirconDevice::InitRequest(&req, IHDA_CONTROLLER_CMD_SNAPSHOT_REGS); |
| res = device_.CallDevice(req, &resp); |
| if (res != ZX_OK) { |
| return res; |
| } |
| |
| const auto regs_ptr = reinterpret_cast<hda_registers_t*>(resp.snapshot); |
| const auto& regs = *regs_ptr; |
| |
| printf("Registers for Intel HDA Device #%u\n", id_); |
| |
| // clang-format off |
| ihda_dump16("GCAP", ®s, offsetof(hda_registers_t, gcap)); |
| ihda_dump8 ("VMIN", ®s, offsetof(hda_registers_t, vmin)); |
| ihda_dump8 ("VMAJ", ®s, offsetof(hda_registers_t, vmaj)); |
| ihda_dump16("OUTPAY", ®s, offsetof(hda_registers_t, outpay)); |
| ihda_dump16("INPAY", ®s, offsetof(hda_registers_t, inpay)); |
| ihda_dump32("GCTL", ®s, offsetof(hda_registers_t, gctl)); |
| ihda_dump16("WAKEEN", ®s, offsetof(hda_registers_t, wakeen)); |
| ihda_dump16("STATESTS", ®s, offsetof(hda_registers_t, statests)); |
| ihda_dump16("GSTS", ®s, offsetof(hda_registers_t, gsts)); |
| ihda_dump16("OUTSTRMPAY", ®s, offsetof(hda_registers_t, outstrmpay)); |
| ihda_dump16("INSTRMPAY", ®s, offsetof(hda_registers_t, instrmpay)); |
| ihda_dump32("INTCTL", ®s, offsetof(hda_registers_t, intctl)); |
| ihda_dump32("INTSTS", ®s, offsetof(hda_registers_t, intsts)); |
| ihda_dump32("WALCLK", ®s, offsetof(hda_registers_t, walclk)); |
| ihda_dump32("SSYNC", ®s, offsetof(hda_registers_t, ssync)); |
| ihda_dump32("CORBLBASE", ®s, offsetof(hda_registers_t, corblbase)); |
| ihda_dump32("CORBUBASE", ®s, offsetof(hda_registers_t, corbubase)); |
| ihda_dump16("CORBWP", ®s, offsetof(hda_registers_t, corbwp)); |
| ihda_dump16("CORBRP", ®s, offsetof(hda_registers_t, corbrp)); |
| ihda_dump8 ("CORBCTL", ®s, offsetof(hda_registers_t, corbctl)); |
| ihda_dump8 ("CORBSTS", ®s, offsetof(hda_registers_t, corbsts)); |
| ihda_dump8 ("CORBSIZE", ®s, offsetof(hda_registers_t, corbsize)); |
| ihda_dump32("RIRBLBASE", ®s, offsetof(hda_registers_t, rirblbase)); |
| ihda_dump32("RIRBUBASE", ®s, offsetof(hda_registers_t, rirbubase)); |
| ihda_dump16("RIRBWP", ®s, offsetof(hda_registers_t, rirbwp)); |
| ihda_dump16("RINTCNT", ®s, offsetof(hda_registers_t, rintcnt)); |
| ihda_dump8 ("RIRBCTL", ®s, offsetof(hda_registers_t, rirbctl)); |
| ihda_dump8 ("RIRBSTS", ®s, offsetof(hda_registers_t, rirbsts)); |
| ihda_dump8 ("RIRBSIZE", ®s, offsetof(hda_registers_t, rirbsize)); |
| ihda_dump32("ICOI", ®s, offsetof(hda_registers_t, icoi)); |
| ihda_dump32("ICII", ®s, offsetof(hda_registers_t, icii)); |
| ihda_dump16("ICIS", ®s, offsetof(hda_registers_t, icis)); |
| ihda_dump32("DPIBLBASE", ®s, offsetof(hda_registers_t, dpiblbase)); |
| ihda_dump32("DPIBUBASE", ®s, offsetof(hda_registers_t, dpibubase)); |
| // clang-format on |
| |
| uint16_t gcap = REG_RD((MMIO_PTR uint16_t*)(®s.gcap)); |
| unsigned int input_stream_cnt = HDA_REG_GCAP_ISS(gcap); |
| unsigned int output_stream_cnt = HDA_REG_GCAP_OSS(gcap); |
| unsigned int bidir_stream_cnt = HDA_REG_GCAP_BSS(gcap); |
| const hda_stream_desc_regs_t* sregs = regs.stream_desc; |
| |
| ihda_dump_stream_regs("Input Stream", input_stream_cnt, sregs); |
| sregs += input_stream_cnt; |
| ihda_dump_stream_regs("Output Stream", output_stream_cnt, sregs); |
| sregs += output_stream_cnt; |
| ihda_dump_stream_regs("Bi-dir Stream", bidir_stream_cnt, sregs); |
| |
| return ZX_OK; |
| } |
| |
| } // namespace intel_hda |
| } // namespace audio |