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 <ddk/device.h> 6#include <ddk/driver.h> 7#include <ddk/binding.h> 8#include <ddk/protocol/pci.h> 9#include <zircon/assert.h> 10#include <fbl/auto_call.h> 11#include <fbl/auto_lock.h> 12#include <stdio.h> 13#include <string.h> 14#include <threads.h> 15 16#include <dispatcher-pool/dispatcher-thread-pool.h> 17#include <intel-hda/utils/intel-hda-registers.h> 18#include <intel-hda/utils/intel-hda-proto.h> 19 20#include "debug-logging.h" 21#include "intel-hda-codec.h" 22#include "intel-hda-controller.h" 23#include "utils.h" 24 25namespace audio { 26namespace intel_hda { 27 28// static member variable declaration 29constexpr uint IntelHDAController::RIRB_RESERVED_RESPONSE_SLOTS; 30fbl::atomic_uint32_t IntelHDAController::device_id_gen_(0u); 31 32// Device interface thunks 33#define DEV(_ctx) static_cast<IntelHDAController*>(_ctx) 34zx_protocol_device_t IntelHDAController::CONTROLLER_DEVICE_THUNKS = { 35 .version = DEVICE_OPS_VERSION, 36 .get_protocol = nullptr, 37 .open = nullptr, 38 .open_at = nullptr, 39 .close = nullptr, 40 .unbind = [](void* ctx) { DEV(ctx)->DeviceShutdown(); }, 41 .release = [](void* ctx) { DEV(ctx)->DeviceRelease(); }, 42 .read = nullptr, 43 .write = nullptr, 44 .get_size = nullptr, 45 .ioctl = [](void* ctx, 46 uint32_t op, 47 const void* in_buf, 48 size_t in_len, 49 void* out_buf, 50 size_t out_len, 51 size_t* out_actual) -> zx_status_t { 52 return DEV(ctx)->DeviceIoctl(op, out_buf, out_len, out_actual); 53 }, 54 .suspend = nullptr, 55 .resume = nullptr, 56 .rxrpc = nullptr, 57 .message = nullptr, 58}; 59#undef DEV 60 61IntelHDAController::IntelHDAController() 62 : state_(static_cast<StateStorage>(State::STARTING)), 63 id_(device_id_gen_.fetch_add(1u)) { 64 snprintf(log_prefix_, sizeof(log_prefix_), "IHDA Controller (unknown BDF)"); 65} 66 67IntelHDAController::~IntelHDAController() { 68 ZX_DEBUG_ASSERT((GetState() == State::STARTING) || (GetState() == State::SHUT_DOWN)); 69 // TODO(johngro) : place the device into reset. 70 71 // Release our register window. 72 mapped_regs_.Unmap(); 73 74 // Release our IRQ. 75 irq_.reset(); 76 77 // Disable IRQs at the PCI level. 78 if (pci_.ops != nullptr) { 79 ZX_DEBUG_ASSERT(pci_.ctx != nullptr); 80 pci_.ops->set_irq_mode(pci_.ctx, ZX_PCIE_IRQ_MODE_DISABLED, 0); 81 } 82 83 // Let go of our stream state. 84 free_input_streams_.clear(); 85 free_output_streams_.clear(); 86 free_bidir_streams_.clear(); 87 88 // Unmap, unpin and release the memory we use for the command/response ring buffers. 89 cmd_buf_cpu_mem_.Unmap(); 90 cmd_buf_hda_mem_.Unpin(); 91 92 if (pci_.ops != nullptr) { 93 // TODO(johngro) : unclaim the PCI device. Right now, there is no way 94 // to do this aside from closing the device handle (which would 95 // seriously mess up the DevMgr's brain) 96 pci_.ops = nullptr; 97 pci_.ctx = nullptr; 98 } 99} 100 101fbl::RefPtr<IntelHDAStream> IntelHDAController::AllocateStream(IntelHDAStream::Type type) { 102 fbl::AutoLock lock(&stream_pool_lock_); 103 IntelHDAStream::Tree* src; 104 105 switch (type) { 106 case IntelHDAStream::Type::INPUT: src = &free_input_streams_; break; 107 case IntelHDAStream::Type::OUTPUT: src = &free_output_streams_; break; 108 109 // Users are not allowed to directly request bidirectional stream contexts. 110 // It's just what they end up with if there are no other choices. 111 default: 112 ZX_DEBUG_ASSERT(false); 113 return nullptr; 114 } 115 116 if (src->is_empty()) { 117 src = &free_bidir_streams_; 118 if (src->is_empty()) 119 return nullptr; 120 } 121 122 // Allocation fails if we cannot assign a unique tag to this stream. 123 uint8_t stream_tag = AllocateStreamTagLocked(type == IntelHDAStream::Type::INPUT); 124 if (!stream_tag) 125 return nullptr; 126 127 auto ret = src->pop_front(); 128 ret->Configure(type, stream_tag); 129 130 return ret; 131} 132 133void IntelHDAController::ReturnStream(fbl::RefPtr<IntelHDAStream>&& ptr) { 134 fbl::AutoLock lock(&stream_pool_lock_); 135 ReturnStreamLocked(fbl::move(ptr)); 136} 137 138void IntelHDAController::ReturnStreamLocked(fbl::RefPtr<IntelHDAStream>&& ptr) { 139 IntelHDAStream::Tree* dst; 140 141 ZX_DEBUG_ASSERT(ptr); 142 143 switch (ptr->type()) { 144 case IntelHDAStream::Type::INPUT: dst = &free_input_streams_; break; 145 case IntelHDAStream::Type::OUTPUT: dst = &free_output_streams_; break; 146 case IntelHDAStream::Type::BIDIR: dst = &free_bidir_streams_; break; 147 default: ZX_DEBUG_ASSERT(false); return; 148 } 149 150 ptr->Configure(IntelHDAStream::Type::INVALID, 0); 151 dst->insert(fbl::move(ptr)); 152} 153 154uint8_t IntelHDAController::AllocateStreamTagLocked(bool input) { 155 uint16_t& tag_pool = input ? free_input_tags_ : free_output_tags_; 156 157 for (uint8_t ret = 1; ret < (sizeof(tag_pool) << 3); ++ret) { 158 if (tag_pool & (1u << ret)) { 159 tag_pool = static_cast<uint16_t>(tag_pool & ~(1u << ret)); 160 return ret; 161 } 162 } 163 164 return 0; 165} 166 167void IntelHDAController::ReleaseStreamTagLocked(bool input, uint8_t tag) { 168 uint16_t& tag_pool = input ? free_input_tags_ : free_output_tags_; 169 170 ZX_DEBUG_ASSERT((tag > 0) && (tag <= 15)); 171 ZX_DEBUG_ASSERT((tag_pool & (1u << tag)) == 0); 172 173 tag_pool = static_cast<uint16_t>((tag_pool | (1u << tag))); 174} 175 176void IntelHDAController::DeviceShutdown() { 177 // Make sure we have closed all of the event sources (eg. IRQs, wakeup 178 // events, channels clients are using to talk to us, etc..) and that we have 179 // synchronized with any dispatch callbacks in flight. 180 default_domain_->Deactivate(); 181 182 // Disable all interrupts and place the device into reset on our way out. 183 if (regs() != nullptr) { 184 REG_WR(®s()->intctl, 0u); 185 REG_CLR_BITS(®s()->gctl, HDA_REG_GCTL_HWINIT); 186 } 187 188 // Shutdown and clean up all of our codecs. 189 for (auto& codec_ptr : codecs_) { 190 if (codec_ptr != nullptr) { 191 codec_ptr->Shutdown(); 192 codec_ptr.reset(); 193 } 194 } 195 196 // Any CORB jobs we may have had in progress may be discarded. 197 { 198 fbl::AutoLock corb_lock(&corb_lock_); 199 in_flight_corb_jobs_.clear(); 200 pending_corb_jobs_.clear(); 201 } 202 203 // Done. Clearly mark that we are now shut down. 204 SetState(State::SHUT_DOWN); 205} 206 207void IntelHDAController::DeviceRelease() { 208 // Take our unmanaged reference back from our published device node. 209 auto thiz = fbl::internal::MakeRefPtrNoAdopt(this); 210 211 // ASSERT that we have been properly shut down, then release the DDK's 212 // reference to our state as we allow thiz to go out of scope. 213 ZX_DEBUG_ASSERT(GetState() == State::SHUT_DOWN); 214 thiz.reset(); 215} 216 217zx_status_t IntelHDAController::DeviceIoctl(uint32_t op, 218 void* out_buf, 219 size_t out_len, 220 size_t* out_actual) { 221 dispatcher::Channel::ProcessHandler phandler( 222 [controller = fbl::WrapRefPtr(this)](dispatcher::Channel* channel) -> zx_status_t { 223 OBTAIN_EXECUTION_DOMAIN_TOKEN(t, controller->default_domain_); 224 return controller->ProcessClientRequest(channel); 225 }); 226 227 return HandleDeviceIoctl(op, out_buf, out_len, out_actual, 228 default_domain_, 229 fbl::move(phandler), 230 nullptr); 231} 232 233void IntelHDAController::RootDeviceRelease() { 234 // Take our unmanaged reference back from our published device node. 235 auto thiz = fbl::internal::MakeRefPtrNoAdopt(this); 236 // Now let go of it. 237 thiz.reset(); 238} 239 240zx_status_t IntelHDAController::ProcessClientRequest(dispatcher::Channel* channel) { 241 zx_status_t res; 242 uint32_t req_size; 243 union RequestBuffer { 244 ihda_cmd_hdr_t hdr; 245 ihda_get_ids_req_t get_ids; 246 ihda_controller_snapshot_regs_req_t snapshot_regs; 247 } req; 248 249 // TODO(johngro) : How large is too large? 250 static_assert(sizeof(req) <= 256, "Request buffer is too large to hold on the stack!"); 251 252 // Read the client request. 253 ZX_DEBUG_ASSERT(channel != nullptr); 254 res = channel->Read(&req, sizeof(req), &req_size); 255 if (res != ZX_OK) { 256 LOG(TRACE, "Failed to read client request (res %d)\n", res); 257 return res; 258 } 259 260 // Sanity checks 261 if (req_size < sizeof(req.hdr)) { 262 LOG(TRACE, "Client request too small to contain header (%u < %zu)\n", 263 req_size, sizeof(req.hdr)); 264 return ZX_ERR_INVALID_ARGS; 265 } 266 267 // Dispatch 268 LOG(SPEW, "Client Request 0x%04x len %u\n", req.hdr.cmd, req_size); 269 switch (req.hdr.cmd) { 270 case IHDA_CMD_GET_IDS: { 271 if (req_size != sizeof(req.get_ids)) { 272 LOG(TRACE, "Bad GET_IDS request length (%u != %zu)\n", 273 req_size, sizeof(req.get_ids)); 274 return ZX_ERR_INVALID_ARGS; 275 } 276 277 ZX_DEBUG_ASSERT(pci_dev_ != nullptr); 278 ZX_DEBUG_ASSERT(regs() != nullptr); 279 280 ihda_get_ids_resp_t resp; 281 resp.hdr = req.hdr; 282 resp.vid = pci_dev_info_.vendor_id; 283 resp.did = pci_dev_info_.device_id; 284 resp.ihda_vmaj = REG_RD(®s()->vmaj); 285 resp.ihda_vmin = REG_RD(®s()->vmin); 286 resp.rev_id = 0; 287 resp.step_id = 0; 288 289 return channel->Write(&resp, sizeof(resp)); 290 } 291 292 case IHDA_CONTROLLER_CMD_SNAPSHOT_REGS: 293 if (req_size != sizeof(req.snapshot_regs)) { 294 LOG(TRACE, "Bad SNAPSHOT_REGS request length (%u != %zu)\n", 295 req_size, sizeof(req.snapshot_regs)); 296 return ZX_ERR_INVALID_ARGS; 297 } 298 299 return SnapshotRegs(channel, req.snapshot_regs); 300 301 default: 302 return ZX_ERR_INVALID_ARGS; 303 } 304} 305 306#define DEV(_ctx) static_cast<IntelHDAController*>(_ctx) 307zx_protocol_device_t IntelHDAController::ROOT_DEVICE_THUNKS = { 308 .version = DEVICE_OPS_VERSION, 309 .get_protocol = nullptr, 310 .open = nullptr, 311 .open_at = nullptr, 312 .close = nullptr, 313 .unbind = nullptr, 314 .release = [](void* ctx) { DEV(ctx)->RootDeviceRelease(); }, 315 .read = nullptr, 316 .write = nullptr, 317 .get_size = nullptr, 318 .ioctl = nullptr, 319 .suspend = nullptr, 320 .resume = nullptr, 321 .rxrpc = nullptr, 322 .message = nullptr, 323}; 324#undef DEV 325 326zx_status_t IntelHDAController::DriverInit(void** out_ctx) { 327 // Note: It is assumed that calls to Init/Release are serialized by the 328 // pci_dev manager. If this assumption ever needs to be relaxed, explicit 329 // serialization will need to be added here. 330 331 return ZX_OK; 332} 333 334zx_status_t IntelHDAController::DriverBind(void* ctx, 335 zx_device_t* device) { 336 fbl::AllocChecker ac; 337 fbl::RefPtr<IntelHDAController> controller(fbl::AdoptRef(new (&ac) IntelHDAController())); 338 339 if (!ac.check()) { 340 return ZX_ERR_NO_MEMORY; 341 } 342 343 zx_status_t ret = controller->Init(device); 344 if (ret != ZX_OK) { 345 return ret; 346 } 347 348 // Initialize our device and fill out the protocol hooks 349 device_add_args_t args = { }; 350 args.version = DEVICE_ADD_ARGS_VERSION; 351 args.name = "intel-hda-controller"; 352 { 353 // use a different refptr to avoid problems in error path 354 auto ddk_ref = controller; 355 args.ctx = ddk_ref.leak_ref(); 356 } 357 args.ops = &ROOT_DEVICE_THUNKS; 358 args.flags = DEVICE_ADD_NON_BINDABLE; 359 360 // Publish the device. 361 ret = device_add(device, &args, nullptr); 362 if (ret != ZX_OK) { 363 controller.reset(); 364 } 365 return ret; 366} 367 368void IntelHDAController::DriverRelease(void* ctx) { 369 // If we are the last one out the door, turn off the lights in the thread pool. 370 dispatcher::ThreadPool::ShutdownAll(); 371} 372 373} // namespace intel_hda 374} // namespace audio 375 376extern "C" { 377zx_status_t ihda_init_hook(void** out_ctx) { 378 zx_status_t res = ::audio::intel_hda::DriverVmars::Initialize(); 379 380 if (res == ZX_OK) { 381 res = ::audio::intel_hda::IntelHDAController::DriverInit(out_ctx); 382 } 383 384 if (res != ZX_OK) { 385 ::audio::intel_hda::DriverVmars::Shutdown(); 386 } 387 388 return res; 389} 390 391zx_status_t ihda_bind_hook(void* ctx, zx_device_t* pci_dev) { 392 return ::audio::intel_hda::IntelHDAController::DriverBind(ctx, pci_dev); 393} 394 395void ihda_release_hook(void* ctx) { 396 ::audio::intel_hda::IntelHDAController::DriverRelease(ctx); 397 ::audio::intel_hda::DriverVmars::Shutdown(); 398} 399} // extern "C" 400 401