// 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 #include #include #include #include #include "intel_hda_controller.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(reinterpret_cast(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(reinterpret_cast(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(reinterpret_cast(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(reinterpret_cast(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[] = { { "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) }, }; 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 < fbl::count_of(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; fbl::unique_ptr 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::DumpRegs(int argc, const char** argv) { zx_status_t res = Connect(); if (res != ZX_OK) return res; ihda_controller_snapshot_regs_req_t req; ihda_controller_snapshot_regs_resp_t resp; InitRequest(&req, IHDA_CONTROLLER_CMD_SNAPSHOT_REGS); res = CallDevice(req, &resp); if (res != ZX_OK) return res; const auto regs_ptr = reinterpret_cast(resp.snapshot); const auto& regs = *regs_ptr; printf("Registers for Intel HDA Device #%u\n", id_); 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)); uint16_t gcap = REG_RD(®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 audio } // namespace intel_hda