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(®s()->rirbsts); 37 38 unsigned int rirb_wr_ptr = REG_RD(®s()->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(®s()->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(®s()->corbsts); 229 REG_WR(®s()->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(®s()->corbwp)); 272 273 unsigned int corb_rd_ptr = REG_RD(®s()->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(®s()->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(®s()->statests) & HDA_REG_STATESTS_MASK; 309 if (statests) { 310 REG_WR(®s()->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(®s()->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