blob: 15d7a93415dd7284b9d980ec2996e4720c00305e [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 <zircon/assert.h>
#include <zircon/thread_annotations.h>
#include <fbl/auto_lock.h>
#include <string.h>
#include <intel-hda/utils/intel-hda-registers.h>
#include "codec-cmd-job.h"
#include "debug-logging.h"
#include "intel-hda-controller.h"
namespace audio {
namespace intel_hda {
namespace {
fbl::Mutex snapshot_regs_buffer_lock;
ihda_controller_snapshot_regs_resp_t snapshot_regs_buffer TA_GUARDED(snapshot_regs_buffer_lock);
} // anon namespace
zx_status_t IntelHDAController::SnapshotRegs(dispatcher::Channel* channel,
const ihda_controller_snapshot_regs_req_t& req) {
ZX_DEBUG_ASSERT(channel != nullptr);
// TODO(johngro) : What an enormous PITA. Every register needs to be
// accessed with the proper sized transaction on the PCI bus, so we cannot
// just use memcpy to do this. Life will be better when we have VMOs in
// place. Then, we can implement the IOCTL by simply cloning the reigster
// VMO (reducing its rights to read-only in the process) and sending it back
// to the calling process. The diagnostic util can then put their own
// cycles on the PCI bus.
fbl::AutoLock lock(&snapshot_regs_buffer_lock);
auto regs_ptr = reinterpret_cast<hda_registers_t*>(snapshot_regs_buffer.snapshot);
auto& out_regs = *regs_ptr;
static_assert(sizeof(snapshot_regs_buffer.snapshot) == sizeof(hda_registers_t),
"Register snapshot buffer size does not match register file size!");
snapshot_regs_buffer.hdr = req.hdr;
memset(&out_regs, 0, sizeof(out_regs));
out_regs.gcap = REG_RD(&regs()->gcap);
out_regs.vmin = REG_RD(&regs()->vmin);
out_regs.vmaj = REG_RD(&regs()->vmaj);
out_regs.outpay = REG_RD(&regs()->outpay);
out_regs.inpay = REG_RD(&regs()->inpay);
out_regs.gctl = REG_RD(&regs()->gctl);
out_regs.wakeen = REG_RD(&regs()->wakeen);
out_regs.statests = REG_RD(&regs()->statests);
out_regs.gsts = REG_RD(&regs()->gsts);
out_regs.outstrmpay = REG_RD(&regs()->outstrmpay);
out_regs.instrmpay = REG_RD(&regs()->instrmpay);
out_regs.intctl = REG_RD(&regs()->intctl);
out_regs.intsts = REG_RD(&regs()->intsts);
out_regs.walclk = REG_RD(&regs()->walclk);
out_regs.ssync = REG_RD(&regs()->ssync);
out_regs.corblbase = REG_RD(&regs()->corblbase);
out_regs.corbubase = REG_RD(&regs()->corbubase);
out_regs.corbwp = REG_RD(&regs()->corbwp);
out_regs.corbrp = REG_RD(&regs()->corbrp);
out_regs.corbctl = REG_RD(&regs()->corbctl);
out_regs.corbsts = REG_RD(&regs()->corbsts);
out_regs.corbsize = REG_RD(&regs()->corbsize);
out_regs.rirblbase = REG_RD(&regs()->rirblbase);
out_regs.rirbubase = REG_RD(&regs()->rirbubase);
out_regs.rirbwp = REG_RD(&regs()->rirbwp);
out_regs.rintcnt = REG_RD(&regs()->rintcnt);
out_regs.rirbctl = REG_RD(&regs()->rirbctl);
out_regs.rirbsts = REG_RD(&regs()->rirbsts);
out_regs.rirbsize = REG_RD(&regs()->rirbsize);
out_regs.icoi = REG_RD(&regs()->icoi);
out_regs.icii = REG_RD(&regs()->icii);
out_regs.icis = REG_RD(&regs()->icis);
out_regs.dpiblbase = REG_RD(&regs()->dpiblbase);
out_regs.dpibubase = REG_RD(&regs()->dpibubase);
uint16_t gcap = REG_RD(&regs()->gcap);
unsigned int stream_cnt = HDA_REG_GCAP_ISS(gcap)
+ HDA_REG_GCAP_OSS(gcap)
+ HDA_REG_GCAP_BSS(gcap);
for (unsigned int i = 0; i < stream_cnt; ++i) {
auto& sin = regs()->stream_desc[i];
auto& sout = out_regs.stream_desc[i];
sout.ctl_sts.w = REG_RD(&sin.ctl_sts.w);
sout.lpib = REG_RD(&sin.lpib);
sout.cbl = REG_RD(&sin.cbl);
sout.lvi = REG_RD(&sin.lvi);
sout.fifod = REG_RD(&sin.fifod);
sout.fmt = REG_RD(&sin.fmt);
sout.bdpl = REG_RD(&sin.bdpl);
sout.bdpu = REG_RD(&sin.bdpu);
}
return channel->Write(&snapshot_regs_buffer, sizeof(snapshot_regs_buffer));
}
} // namespace intel_hda
} // namespace audio