blob: b0adc7aed527111c62a981998ac46169c96183e5 [file] [log] [blame]
// 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 <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 < 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;
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", &regs, offsetof(hda_registers_t, gcap));
ihda_dump8 ("VMIN", &regs, offsetof(hda_registers_t, vmin));
ihda_dump8 ("VMAJ", &regs, offsetof(hda_registers_t, vmaj));
ihda_dump16("OUTPAY", &regs, offsetof(hda_registers_t, outpay));
ihda_dump16("INPAY", &regs, offsetof(hda_registers_t, inpay));
ihda_dump32("GCTL", &regs, offsetof(hda_registers_t, gctl));
ihda_dump16("WAKEEN", &regs, offsetof(hda_registers_t, wakeen));
ihda_dump16("STATESTS", &regs, offsetof(hda_registers_t, statests));
ihda_dump16("GSTS", &regs, offsetof(hda_registers_t, gsts));
ihda_dump16("OUTSTRMPAY", &regs, offsetof(hda_registers_t, outstrmpay));
ihda_dump16("INSTRMPAY", &regs, offsetof(hda_registers_t, instrmpay));
ihda_dump32("INTCTL", &regs, offsetof(hda_registers_t, intctl));
ihda_dump32("INTSTS", &regs, offsetof(hda_registers_t, intsts));
ihda_dump32("WALCLK", &regs, offsetof(hda_registers_t, walclk));
ihda_dump32("SSYNC", &regs, offsetof(hda_registers_t, ssync));
ihda_dump32("CORBLBASE", &regs, offsetof(hda_registers_t, corblbase));
ihda_dump32("CORBUBASE", &regs, offsetof(hda_registers_t, corbubase));
ihda_dump16("CORBWP", &regs, offsetof(hda_registers_t, corbwp));
ihda_dump16("CORBRP", &regs, offsetof(hda_registers_t, corbrp));
ihda_dump8 ("CORBCTL", &regs, offsetof(hda_registers_t, corbctl));
ihda_dump8 ("CORBSTS", &regs, offsetof(hda_registers_t, corbsts));
ihda_dump8 ("CORBSIZE", &regs, offsetof(hda_registers_t, corbsize));
ihda_dump32("RIRBLBASE", &regs, offsetof(hda_registers_t, rirblbase));
ihda_dump32("RIRBUBASE", &regs, offsetof(hda_registers_t, rirbubase));
ihda_dump16("RIRBWP", &regs, offsetof(hda_registers_t, rirbwp));
ihda_dump16("RINTCNT", &regs, offsetof(hda_registers_t, rintcnt));
ihda_dump8 ("RIRBCTL", &regs, offsetof(hda_registers_t, rirbctl));
ihda_dump8 ("RIRBSTS", &regs, offsetof(hda_registers_t, rirbsts));
ihda_dump8 ("RIRBSIZE", &regs, offsetof(hda_registers_t, rirbsize));
ihda_dump32("ICOI", &regs, offsetof(hda_registers_t, icoi));
ihda_dump32("ICII", &regs, offsetof(hda_registers_t, icii));
ihda_dump16("ICIS", &regs, offsetof(hda_registers_t, icis));
ihda_dump32("DPIBLBASE", &regs, offsetof(hda_registers_t, dpiblbase));
ihda_dump32("DPIBUBASE", &regs, offsetof(hda_registers_t, dpibubase));
// clang-format on
uint16_t gcap = REG_RD(&regs.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