1// Copyright 2017 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <zircon/assert.h>
6#include <zircon/thread_annotations.h>
7#include <fbl/auto_lock.h>
8#include <string.h>
9
10#include <intel-hda/utils/intel-hda-registers.h>
11
12#include "codec-cmd-job.h"
13#include "debug-logging.h"
14#include "intel-hda-controller.h"
15
16namespace audio {
17namespace intel_hda {
18
19namespace {
20fbl::Mutex snapshot_regs_buffer_lock;
21ihda_controller_snapshot_regs_resp_t snapshot_regs_buffer TA_GUARDED(snapshot_regs_buffer_lock);
22}  // anon namespace
23
24zx_status_t IntelHDAController::SnapshotRegs(dispatcher::Channel* channel,
25                                             const ihda_controller_snapshot_regs_req_t& req) {
26    ZX_DEBUG_ASSERT(channel != nullptr);
27
28    // TODO(johngro) : What an enormous PITA.  Every register needs to be
29    // accessed with the proper sized transaction on the PCI bus, so we cannot
30    // just use memcpy to do this.  Life will be better when we have VMOs in
31    // place.  Then, we can implement the IOCTL by simply cloning the reigster
32    // VMO (reducing its rights to read-only in the process) and sending it back
33    // to the calling process.  The diagnostic util can then put their own
34    // cycles on the PCI bus.
35    fbl::AutoLock lock(&snapshot_regs_buffer_lock);
36
37    auto  regs_ptr = reinterpret_cast<hda_registers_t*>(snapshot_regs_buffer.snapshot);
38    auto& out_regs = *regs_ptr;
39
40    static_assert(sizeof(snapshot_regs_buffer.snapshot) == sizeof(hda_registers_t),
41                  "Register snapshot buffer size does not match  register file size!");
42
43    snapshot_regs_buffer.hdr = req.hdr;
44    memset(&out_regs, 0, sizeof(out_regs));
45
46    out_regs.gcap       = REG_RD(&regs()->gcap);
47    out_regs.vmin       = REG_RD(&regs()->vmin);
48    out_regs.vmaj       = REG_RD(&regs()->vmaj);
49    out_regs.outpay     = REG_RD(&regs()->outpay);
50    out_regs.inpay      = REG_RD(&regs()->inpay);
51    out_regs.gctl       = REG_RD(&regs()->gctl);
52    out_regs.wakeen     = REG_RD(&regs()->wakeen);
53    out_regs.statests   = REG_RD(&regs()->statests);
54    out_regs.gsts       = REG_RD(&regs()->gsts);
55    out_regs.outstrmpay = REG_RD(&regs()->outstrmpay);
56    out_regs.instrmpay  = REG_RD(&regs()->instrmpay);
57    out_regs.intctl     = REG_RD(&regs()->intctl);
58    out_regs.intsts     = REG_RD(&regs()->intsts);
59    out_regs.walclk     = REG_RD(&regs()->walclk);
60    out_regs.ssync      = REG_RD(&regs()->ssync);
61    out_regs.corblbase  = REG_RD(&regs()->corblbase);
62    out_regs.corbubase  = REG_RD(&regs()->corbubase);
63    out_regs.corbwp     = REG_RD(&regs()->corbwp);
64    out_regs.corbrp     = REG_RD(&regs()->corbrp);
65    out_regs.corbctl    = REG_RD(&regs()->corbctl);
66    out_regs.corbsts    = REG_RD(&regs()->corbsts);
67    out_regs.corbsize   = REG_RD(&regs()->corbsize);
68    out_regs.rirblbase  = REG_RD(&regs()->rirblbase);
69    out_regs.rirbubase  = REG_RD(&regs()->rirbubase);
70    out_regs.rirbwp     = REG_RD(&regs()->rirbwp);
71    out_regs.rintcnt    = REG_RD(&regs()->rintcnt);
72    out_regs.rirbctl    = REG_RD(&regs()->rirbctl);
73    out_regs.rirbsts    = REG_RD(&regs()->rirbsts);
74    out_regs.rirbsize   = REG_RD(&regs()->rirbsize);
75    out_regs.icoi       = REG_RD(&regs()->icoi);
76    out_regs.icii       = REG_RD(&regs()->icii);
77    out_regs.icis       = REG_RD(&regs()->icis);
78    out_regs.dpiblbase  = REG_RD(&regs()->dpiblbase);
79    out_regs.dpibubase  = REG_RD(&regs()->dpibubase);
80
81    uint16_t gcap = REG_RD(&regs()->gcap);
82    unsigned int stream_cnt = HDA_REG_GCAP_ISS(gcap)
83                            + HDA_REG_GCAP_OSS(gcap)
84                            + HDA_REG_GCAP_BSS(gcap);
85
86    for (unsigned int i = 0; i < stream_cnt; ++i) {
87        auto& sin  = regs()->stream_desc[i];
88        auto& sout = out_regs.stream_desc[i];
89
90        sout.ctl_sts.w = REG_RD(&sin.ctl_sts.w);
91        sout.lpib      = REG_RD(&sin.lpib);
92        sout.cbl       = REG_RD(&sin.cbl);
93        sout.lvi       = REG_RD(&sin.lvi);
94        sout.fifod     = REG_RD(&sin.fifod);
95        sout.fmt       = REG_RD(&sin.fmt);
96        sout.bdpl      = REG_RD(&sin.bdpl);
97        sout.bdpu      = REG_RD(&sin.bdpu);
98    }
99
100    return channel->Write(&snapshot_regs_buffer, sizeof(snapshot_regs_buffer));
101}
102
103}  // namespace intel_hda
104}  // namespace audio
105