| // 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(®s()->gcap); |
| out_regs.vmin = REG_RD(®s()->vmin); |
| out_regs.vmaj = REG_RD(®s()->vmaj); |
| out_regs.outpay = REG_RD(®s()->outpay); |
| out_regs.inpay = REG_RD(®s()->inpay); |
| out_regs.gctl = REG_RD(®s()->gctl); |
| out_regs.wakeen = REG_RD(®s()->wakeen); |
| out_regs.statests = REG_RD(®s()->statests); |
| out_regs.gsts = REG_RD(®s()->gsts); |
| out_regs.outstrmpay = REG_RD(®s()->outstrmpay); |
| out_regs.instrmpay = REG_RD(®s()->instrmpay); |
| out_regs.intctl = REG_RD(®s()->intctl); |
| out_regs.intsts = REG_RD(®s()->intsts); |
| out_regs.walclk = REG_RD(®s()->walclk); |
| out_regs.ssync = REG_RD(®s()->ssync); |
| out_regs.corblbase = REG_RD(®s()->corblbase); |
| out_regs.corbubase = REG_RD(®s()->corbubase); |
| out_regs.corbwp = REG_RD(®s()->corbwp); |
| out_regs.corbrp = REG_RD(®s()->corbrp); |
| out_regs.corbctl = REG_RD(®s()->corbctl); |
| out_regs.corbsts = REG_RD(®s()->corbsts); |
| out_regs.corbsize = REG_RD(®s()->corbsize); |
| out_regs.rirblbase = REG_RD(®s()->rirblbase); |
| out_regs.rirbubase = REG_RD(®s()->rirbubase); |
| out_regs.rirbwp = REG_RD(®s()->rirbwp); |
| out_regs.rintcnt = REG_RD(®s()->rintcnt); |
| out_regs.rirbctl = REG_RD(®s()->rirbctl); |
| out_regs.rirbsts = REG_RD(®s()->rirbsts); |
| out_regs.rirbsize = REG_RD(®s()->rirbsize); |
| out_regs.icoi = REG_RD(®s()->icoi); |
| out_regs.icii = REG_RD(®s()->icii); |
| out_regs.icis = REG_RD(®s()->icis); |
| out_regs.dpiblbase = REG_RD(®s()->dpiblbase); |
| out_regs.dpibubase = REG_RD(®s()->dpibubase); |
| |
| uint16_t gcap = REG_RD(®s()->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 |