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 <endian.h>
6#include <zircon/assert.h>
7#include <fbl/algorithm.h>
8#include <fbl/auto_lock.h>
9#include <string.h>
10
11#include <intel-hda/utils/intel-hda-registers.h>
12
13#include "debug-logging.h"
14#include "intel-hda-codec.h"
15#include "intel-hda-controller.h"
16
17namespace audio {
18namespace intel_hda {
19
20void IntelHDAController::WakeupIrqHandler() {
21    LOG(SPEW, "Waking up IRQ handler\n");
22    ZX_DEBUG_ASSERT(irq_wakeup_event_ != nullptr);
23    irq_wakeup_event_->Signal();
24}
25
26fbl::RefPtr<IntelHDACodec> IntelHDAController::GetCodec(uint id) {
27    ZX_DEBUG_ASSERT(id < countof(codecs_));
28    fbl::AutoLock codec_lock(&codec_lock_);
29    return codecs_[id];
30}
31
32void IntelHDAController::SnapshotRIRB() {
33    fbl::AutoLock rirb_lock(&rirb_lock_);
34
35    ZX_DEBUG_ASSERT(rirb_ && rirb_entry_count_ && rirb_mask_);
36    uint8_t rirbsts = REG_RD(&regs()->rirbsts);
37
38    unsigned int rirb_wr_ptr = REG_RD(&regs()->rirbwp) & rirb_mask_;
39    unsigned int pending     = (rirb_entry_count_ + rirb_wr_ptr - rirb_rd_ptr_) & rirb_mask_;
40
41    // Copy the current state of the RIRB into our snapshot memory.  Note: we
42    // loop at most up to 2 times in order to deal with the case where the
43    // active region of the ring buffer wraps around the end.
44    //
45    // TODO(johngro) : Make sure to invalidate cache for the memory region
46    // occupied by the RIRB before we copy into our snapshot if we are running
47    // on an architecure where cache coherency is not automatically managed for
48    // us via. something like snooping, or by a un-cached policy set on our
49    // mapped pages in the MMU. */
50    rirb_snapshot_cnt_ = 0;
51    while (pending) {
52         /* Intel HDA ring buffers are strange, see comments in
53          * intel_hda_codec_send_cmd. */
54        unsigned int tmp_rd = (rirb_rd_ptr_ + 1) & rirb_mask_;
55        unsigned int todo   = fbl::min(pending, (rirb_entry_count_ - tmp_rd));
56
57        memcpy(rirb_snapshot_ + rirb_snapshot_cnt_,
58               rirb_ + tmp_rd,
59               sizeof(rirb_snapshot_[0]) * todo);
60
61        rirb_rd_ptr_ = (rirb_rd_ptr_ + todo) & rirb_mask_;
62        rirb_snapshot_cnt_ += todo;
63        pending -= todo;
64    }
65
66    REG_WR(&regs()->rirbsts, rirbsts);
67
68    ZX_DEBUG_ASSERT(!pending);
69
70    LOG(SPEW, "RIRB has %u pending responses; WP is @%u\n", rirb_snapshot_cnt_, rirb_wr_ptr);
71
72    if (rirbsts & HDA_REG_RIRBSTS_OIS) {
73        // TODO(johngro) : Implement retry behavior for codec command and
74        // control.
75        //
76        // The OIS bit in the RIRBSTS register indicates that hardware has
77        // encountered a overrun while attempting to write to the Response Input
78        // Ring Buffer.  IOW - responses were received, but the controller was
79        // unable to write to system memory in time, and some of the responses
80        // were lost.  This should *really* never happen.  If it does, all bets
81        // are pretty much off.  Every command verb sent is supposed to receive
82        // a response from the codecs; if a response is dropped it can easily
83        // wedge a codec's command and control state machine.
84        //
85        // This problem is not limited to HW being unable to write to system
86        // memory in time.  There is no HW read pointer for the RIRB.  The
87        // implication of this is that HW has no way to know that it has overrun
88        // SW if SW is not keeping up.  If this was to happen, there would be no
89        // way for the system to know, it would just look like a large number of
90        // responses were lost.
91        //
92        // In either case, the only mitigation we could possibly implement would
93        // be a reasonable retry system at the codec driver level.
94        //
95        // Right now, we just log the error, ack the IRQ and move on.
96        LOG(ERROR, "CRITICAL ERROR: controller overrun detected while "
97                   "attempting to write to response input ring buffer.\n");
98    }
99}
100
101void IntelHDAController::ProcessRIRB() {
102    fbl::AutoLock rirb_lock(&rirb_lock_);
103    ZX_DEBUG_ASSERT(rirb_snapshot_cnt_ < HDA_RIRB_MAX_ENTRIES);
104    ZX_DEBUG_ASSERT(rirb_snapshot_cnt_ < rirb_entry_count_);
105
106    for (unsigned int i = 0; i < rirb_snapshot_cnt_; ++i) {
107        auto& resp = rirb_snapshot_[i];
108        resp.OnReceived();  // Fixup endianness
109
110        /* Figure out the codec this came from */
111        uint32_t caddr = resp.caddr();
112
113        /* Sanity checks */
114        if (caddr >= countof(codecs_)) {
115            LOG(ERROR, "Received %ssolicited response with illegal codec address (%u) "
116                       "[0x%08x, 0x%08x]\n",
117                       resp.unsolicited() ? "un" : "", caddr, resp.data, resp.data_ex);
118            continue;
119        }
120
121        auto codec = GetCodec(caddr);
122        if (!codec) {
123            LOG(ERROR, "Received %ssolicited response for non-existent codec address (%u) "
124                       "[0x%08x, 0x%08x]\n",
125                       resp.unsolicited() ? "un" : "", caddr, resp.data, resp.data_ex);
126            continue;
127        }
128
129        LOG(TRACE, "RX[%2u]: 0x%08x%s\n",
130                   caddr, resp.data, resp.unsolicited() ? " (unsolicited)" : "");
131
132        if (!resp.unsolicited()) {
133            fbl::unique_ptr<CodecCmdJob> job;
134
135            {
136                fbl::AutoLock corb_lock(&corb_lock_);
137
138                // If this was a solicited response, there needs to be an in-flight
139                // job waiting at the head of the in-flight queue which triggered
140                // it.
141                if (in_flight_corb_jobs_.is_empty()) {
142                    LOG(ERROR,
143                        "Received solicited response for codec address (%u) [0x%08x, 0x%08x] "
144                        "but no in-flight job is waiting for it\n",
145                        caddr, resp.data, resp.data_ex);
146                    continue;
147                }
148
149                // Grab the front of the in-flight queue.
150                job = in_flight_corb_jobs_.pop_front();
151            }
152
153            // Sanity checks complete.  Pass the response and the job which
154            // triggered it on to the codec.
155            codec->ProcessSolicitedResponse(resp, fbl::move(job));
156        } else {
157            auto codec = GetCodec(caddr);
158            if (!codec) {
159                LOG(ERROR,
160                    "Received unsolicited response for non-existent codec address (%u) "
161                    "[0x%08x, 0x%08x]\n", caddr, resp.data, resp.data_ex);
162                continue;
163            }
164
165            codec->ProcessUnsolicitedResponse(resp);
166        }
167    }
168
169    rirb_snapshot_cnt_ = 0;
170}
171
172void IntelHDAController::SendCodecCmdLocked(CodecCommand cmd) {
173    ZX_DEBUG_ASSERT(corb_space_ > 0);
174
175    // Write the command into the ring buffer and update the SW shadow of the
176    // write pointer.  We will update the HW write pointer later on when we
177    // commit the new CORB commands.
178    //
179    // Note: Intel's ring buffers are a bit wonky.  See Section 4.4.1.4, but the
180    // general idea is that to send a command, you do *not* write the command at
181    // WP and then bump the WP.  Instead you write the command to (WP + 1) %
182    // RING_SIZE, then update WP to be (WP + 1) % RING_SIZE.  IOW - The write
183    // pointer always points to the last command written, not the place where
184    // the next command will go.  This behavior holds in the RIRB direction as
185    // well.
186    corb_wr_ptr_ = (corb_wr_ptr_ + 1) & corb_mask_;
187    corb_[corb_wr_ptr_].data = htole32(cmd.data);
188    corb_space_--;
189}
190
191zx_status_t IntelHDAController::QueueCodecCmd(fbl::unique_ptr<CodecCmdJob>&& job) {
192    ZX_DEBUG_ASSERT(job != nullptr);
193    LOG(TRACE, "TX: Codec ID %u Node ID %hu Verb 0x%05x\n",
194               job->codec_id(), job->nid(), job->verb().val);
195
196    // Enter the lock, then check out the state of the ring buffer.  If the
197    // buffer is full, or if there are already commands backed up into the
198    // pending queue, just add the job to the end of the pending queue.
199    // Otherwise, actually write the command into the CORB, add the job to the
200    // end of the in-flight queue, and wakeup the IRQ thread.
201    //
202    fbl::AutoLock corb_lock(&corb_lock_);
203    ZX_DEBUG_ASSERT(corb_wr_ptr_ < corb_entry_count_);
204    ZX_DEBUG_ASSERT(corb_);
205
206    if (!corb_space_) {
207        // If we have no space in the CORB, there must be some jobs which are
208        // currently in-flight.
209        ZX_DEBUG_ASSERT(!in_flight_corb_jobs_.is_empty());
210        pending_corb_jobs_.push_back(fbl::move(job));
211    } else {
212        // Alternatively, if there is space in the CORB, the pending job queue
213        // had better be empty.
214        ZX_DEBUG_ASSERT(pending_corb_jobs_.is_empty());
215        SendCodecCmdLocked(job->command());
216        in_flight_corb_jobs_.push_back(fbl::move(job));
217    }
218
219    CommitCORBLocked();
220
221    return ZX_OK;
222}
223
224void IntelHDAController::ProcessCORB() {
225    fbl::AutoLock corb_lock(&corb_lock_);
226
227    // Check IRQ status for the CORB
228    uint8_t corbsts = REG_RD(&regs()->corbsts);
229    REG_WR(&regs()->corbsts, corbsts);
230
231    if (corbsts & HDA_REG_CORBSTS_MEI) {
232        // TODO(johngro) : Implement proper controller reset behavior.
233        //
234        // The MEI bit in CORBSTS indicates some form memory error detected by
235        // the controller while attempting to read from system memory.  This is
236        // Extremely Bad and should never happen.  If it does, the TRM suggests
237        // that all bets are off, and the only reasonable action is to
238        // completely shutdown and reset the controller.
239        //
240        // Right now, we do not implement this behavior.  Instead we log, then
241        // assert in debug builds.  In release builds, we simply ack the
242        // interrupt and move on.
243        //
244        LOG(ERROR, "CRITICAL ERROR: controller encountered an unrecoverable "
245                   "error attempting to read from system memory!\n");
246        ZX_DEBUG_ASSERT(false);
247    }
248
249    // Figure out how much space we have in the CORB
250    ComputeCORBSpaceLocked();
251
252    // While we have room in the CORB, and still have commands which are waiting
253    // to be sent out, move commands from the pending queue into the in-flight
254    // queue.
255    LOG(SPEW, "CORB has space for %u commands; WP is @%u\n", corb_space_, corb_wr_ptr_);
256    while (corb_space_ && !pending_corb_jobs_.is_empty()) {
257        auto job = pending_corb_jobs_.pop_front();
258
259        SendCodecCmdLocked(job->command());
260
261        in_flight_corb_jobs_.push_back(fbl::move(job));
262    }
263    LOG(SPEW, "Update CORB WP; WP is @%u\n", corb_wr_ptr_);
264
265    // Update the CORB write pointer.
266    CommitCORBLocked();
267}
268
269void IntelHDAController::ComputeCORBSpaceLocked() {
270    ZX_DEBUG_ASSERT(corb_entry_count_ && corb_mask_);
271    ZX_DEBUG_ASSERT(corb_wr_ptr_ == REG_RD(&regs()->corbwp));
272
273    unsigned int corb_rd_ptr = REG_RD(&regs()->corbrp) & corb_mask_;
274    unsigned int corb_used   = (corb_entry_count_ + corb_wr_ptr_ - corb_rd_ptr) & corb_mask_;
275
276    /* The way the Intel HDA command ring buffers work, it is impossible to ever
277     * be using more than N - 1 of the ring buffer entries.  Our available
278     * space should be the ring buffer size, minus the amt currently used, minus 1 */
279    ZX_DEBUG_ASSERT(corb_entry_count_   >  corb_used);
280    ZX_DEBUG_ASSERT(corb_max_in_flight_ >= corb_used);
281    corb_space_ = corb_max_in_flight_ - corb_used;
282}
283
284void IntelHDAController::CommitCORBLocked() {
285    // TODO(johngro) : Make sure to force a write back of the cache for the
286    // dirty portions of the CORB before we update the write pointer if we are
287    // running on an architecure where cache coherency is not automatically
288    // managed for us via. snooping or by an explicit uncached or write-thru
289    // policy set on our mapped pages in the MMU.
290    ZX_DEBUG_ASSERT(regs());
291    ZX_DEBUG_ASSERT(corb_entry_count_ && corb_mask_);
292    ZX_DEBUG_ASSERT(corb_wr_ptr_ < corb_entry_count_);
293    REG_WR(&regs()->corbwp, corb_wr_ptr_);
294}
295
296void IntelHDAController::ProcessStreamIRQ(uint32_t intsts) {
297    for (uint32_t i = 0; intsts; i++, intsts >>= 1) {
298        if (intsts & 0x1) {
299            ZX_DEBUG_ASSERT(i < countof(all_streams_));
300            ZX_DEBUG_ASSERT(all_streams_[i] != nullptr);
301            all_streams_[i]->ProcessStreamIRQ();
302        }
303    }
304}
305
306void IntelHDAController::ProcessControllerIRQ() {
307    // Start by checking for codec wake events.
308    uint16_t statests = REG_RD(&regs()->statests) & HDA_REG_STATESTS_MASK;
309    if (statests) {
310        REG_WR(&regs()->statests, statests);
311        uint32_t tmp = statests;
312        for (uint8_t i = 0u; statests && (i < countof(codecs_)); ++i, tmp >>= 1) {
313            if (!(tmp & 1u))
314                continue;
315
316            // TODO(johngro) : How is a codec supposed to signal a hot unplug
317            // event?  Docs clearly indicate that they can be hot plugged, and
318            // that you detect hot plug events by enabling wake events and
319            // checking the STATESTS register when you receive one, but they
320            // don't seem to give any indication of how to detect that a codec
321            // has been unplugged.
322            if (codecs_[i] == nullptr) {
323                codecs_[i] = IntelHDACodec::Create(*this, i);
324
325                // If we successfully created our codec, attempt to start it up.
326                // If it fails to start, release our reference to the codec.
327                if ((codecs_[i] != nullptr) && (codecs_[i]->Startup() != ZX_OK)) {
328                    codecs_[i] = nullptr;
329                }
330            } else {
331                codecs_[i]->ProcessWakeupEvt();
332            }
333        }
334    }
335}
336
337zx_status_t IntelHDAController::HandleIrq() {
338    if (GetState() != State::OPERATING) {
339        LOG(WARN, "IRQ Handler shutting down due to invalid state (%u)!\n",
340            static_cast<uint32_t>(GetState()));
341        return ZX_ERR_BAD_STATE;
342    }
343
344    // Take a snapshot of any pending responses ASAP in order to minimize
345    // the chance of an RIRB overflow.  We will process the responses which
346    // we snapshot-ed in a short while after we are done handling other
347    // important IRQ tasks.
348    SnapshotRIRB();
349
350    // Fetch the interrupt status word and dispatch as appropriate
351    uint32_t intsts = REG_RD(&regs()->intsts);
352
353    if (intsts & HDA_REG_INTCTL_SIE_MASK)
354        ProcessStreamIRQ(intsts & HDA_REG_INTCTL_SIE_MASK);
355
356    if (intsts & HDA_REG_INTCTL_CIE)
357        ProcessControllerIRQ();
358
359    if (dsp_ != nullptr) {
360        dsp_->ProcessIRQ();
361    }
362
363    ProcessRIRB();
364    ProcessCORB();
365
366    return ZX_OK;
367}
368
369}  // namespace intel_hda
370}  // namespace audio
371