1 // Copyright 2018 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/syscalls.h>
6
7 #include "intel-i915.h"
8 #include "interrupts.h"
9 #include "macros.h"
10 #include "registers.h"
11
12 namespace {
irq_handler(void * arg)13 static int irq_handler(void* arg) {
14 return static_cast<i915::Interrupts*>(arg)->IrqLoop();
15 }
16 } // namespace
17
18 namespace i915 {
19
Interrupts()20 Interrupts::Interrupts() { }
21
~Interrupts()22 Interrupts::~Interrupts() {
23 ZX_ASSERT(irq_ == ZX_HANDLE_INVALID);
24 }
25
Destroy()26 void Interrupts::Destroy() {
27 if (irq_ != ZX_HANDLE_INVALID) {
28 zx_interrupt_destroy(irq_.get());
29 thrd_join(irq_thread_, nullptr);
30
31 irq_.reset();
32 }
33 }
34
IrqLoop()35 int Interrupts::IrqLoop() {
36 for (;;) {
37 zx_time_t timestamp;
38 if (zx_interrupt_wait(irq_.get(), ×tamp) != ZX_OK) {
39 LOG_INFO("interrupt wait failed\n");
40 break;
41 }
42 auto interrupt_ctrl =
43 registers::MasterInterruptControl::Get().ReadFrom(controller_->mmio_space());
44 interrupt_ctrl.set_enable_mask(0);
45 interrupt_ctrl.WriteTo(controller_->mmio_space());
46
47 if (interrupt_ctrl.sde_int_pending()) {
48 auto sde_int_identity = registers::SdeInterruptBase::Get(registers
49 ::SdeInterruptBase::kSdeIntIdentity).ReadFrom(controller_->mmio_space());
50 auto hp_ctrl1 = registers::HotplugCtrl
51 ::Get(registers::DDI_A).ReadFrom(controller_->mmio_space());
52 auto hp_ctrl2 = registers::HotplugCtrl
53 ::Get(registers::DDI_E).ReadFrom(controller_->mmio_space());
54 for (uint32_t i = 0; i < registers::kDdiCount; i++) {
55 registers::Ddi ddi = registers::kDdis[i];
56 auto hp_ctrl = ddi < registers::DDI_E ? hp_ctrl1 : hp_ctrl2;
57 bool hp_detected = sde_int_identity.ddi_bit(ddi).get()
58 & (hp_ctrl.hpd_long_pulse(ddi).get() || hp_ctrl.hpd_short_pulse(ddi).get());
59 if (hp_detected) {
60 controller_->HandleHotplug(ddi, hp_ctrl.hpd_long_pulse(ddi).get());
61 }
62 }
63 // Write back the register to clear the bits
64 hp_ctrl1.WriteTo(controller_->mmio_space());
65 hp_ctrl2.WriteTo(controller_->mmio_space());
66 sde_int_identity.WriteTo(controller_->mmio_space());
67 }
68
69 if (interrupt_ctrl.de_pipe_c_int_pending()) {
70 HandlePipeInterrupt(registers::PIPE_C, timestamp);
71 } else if (interrupt_ctrl.de_pipe_b_int_pending()) {
72 HandlePipeInterrupt(registers::PIPE_B, timestamp);
73 } else if (interrupt_ctrl.de_pipe_a_int_pending()) {
74 HandlePipeInterrupt(registers::PIPE_A, timestamp);
75 }
76
77 {
78 fbl::AutoLock lock(&lock_);
79 if (interrupt_ctrl.reg_value() & interrupt_mask_) {
80 interrupt_cb_.callback(interrupt_cb_.ctx, interrupt_ctrl.reg_value());
81 }
82 }
83
84 interrupt_ctrl.set_enable_mask(1);
85 interrupt_ctrl.WriteTo(controller_->mmio_space());
86 }
87 return 0;
88 }
89
HandlePipeInterrupt(registers::Pipe pipe,zx_time_t timestamp)90 void Interrupts::HandlePipeInterrupt(registers::Pipe pipe, zx_time_t timestamp) {
91 registers::PipeRegs regs(pipe);
92 auto identity = regs.PipeDeInterrupt(regs.kIdentityReg).ReadFrom(controller_->mmio_space());
93 identity.WriteTo(controller_->mmio_space());
94
95 if (identity.vsync()) {
96 controller_->HandlePipeVsync(pipe, timestamp);
97 }
98 }
99
EnablePipeVsync(registers::Pipe pipe,bool enable)100 void Interrupts::EnablePipeVsync(registers::Pipe pipe, bool enable) {
101 registers::PipeRegs regs(pipe);
102 auto mask_reg = regs.PipeDeInterrupt(regs.kMaskReg).FromValue(0);
103 mask_reg.set_vsync(!enable);
104 mask_reg.WriteTo(controller_->mmio_space());
105
106 auto enable_reg = regs.PipeDeInterrupt(regs.kEnableReg).FromValue(0);
107 enable_reg.set_vsync(enable);
108 enable_reg.WriteTo(controller_->mmio_space());
109 }
110
EnableHotplugInterrupts()111 void Interrupts::EnableHotplugInterrupts() {
112 auto sfuse_strap = registers::SouthFuseStrap::Get().ReadFrom(controller_->mmio_space());
113 for (uint32_t i = 0; i < registers::kDdiCount; i++) {
114 registers::Ddi ddi = registers::kDdis[i];
115 bool enabled = (ddi == registers::DDI_A) || (ddi == registers::DDI_E)
116 || (ddi == registers::DDI_B && sfuse_strap.port_b_present())
117 || (ddi == registers::DDI_C && sfuse_strap.port_c_present())
118 || (ddi == registers::DDI_D && sfuse_strap.port_d_present());
119
120 auto hp_ctrl = registers::HotplugCtrl::Get(ddi).ReadFrom(controller_->mmio_space());
121 hp_ctrl.hpd_enable(ddi).set(enabled);
122 hp_ctrl.WriteTo(controller_->mmio_space());
123
124 auto mask = registers::SdeInterruptBase::Get(
125 registers::SdeInterruptBase::kSdeIntMask)
126 .ReadFrom(controller_->mmio_space());
127 mask.ddi_bit(ddi).set(!enabled);
128 mask.WriteTo(controller_->mmio_space());
129
130 auto enable = registers::SdeInterruptBase::Get(
131 registers::SdeInterruptBase::kSdeIntEnable)
132 .ReadFrom(controller_->mmio_space());
133 enable.ddi_bit(ddi).set(enabled);
134 enable.WriteTo(controller_->mmio_space());
135 }
136 }
137
SetInterruptCallback(const zx_intel_gpu_core_interrupt_t * callback,uint32_t interrupt_mask)138 zx_status_t Interrupts::SetInterruptCallback(const zx_intel_gpu_core_interrupt_t* callback,
139 uint32_t interrupt_mask) {
140 fbl::AutoLock lock(&lock_);
141 if (callback->callback != nullptr && interrupt_cb_.callback != nullptr) {
142 return ZX_ERR_ALREADY_BOUND;
143 }
144 interrupt_cb_ = *callback;
145 interrupt_mask_ = interrupt_mask;
146 return ZX_OK;
147 }
148
Init(Controller * controller)149 zx_status_t Interrupts::Init(Controller* controller) {
150 controller_ = controller;
151 ddk::MmioBuffer* mmio_space = controller_->mmio_space();
152
153 mtx_init(&lock_, mtx_plain);
154
155 // Disable interrupts here, re-enable them in ::FinishInit()
156 auto interrupt_ctrl = registers::MasterInterruptControl::Get().ReadFrom(mmio_space);
157 interrupt_ctrl.set_enable_mask(0);
158 interrupt_ctrl.WriteTo(mmio_space);
159
160 uint32_t irq_cnt = 0;
161 zx_status_t status = pci_query_irq_mode(controller_->pci(), ZX_PCIE_IRQ_MODE_LEGACY, &irq_cnt);
162 if (status != ZX_OK || !irq_cnt) {
163 LOG_ERROR("Failed to find interrupts (%d %d)\n", status, irq_cnt);
164 return ZX_ERR_INTERNAL;
165 }
166
167 if ((status = pci_set_irq_mode(controller_->pci(), ZX_PCIE_IRQ_MODE_LEGACY, 1)) != ZX_OK) {
168 LOG_ERROR("Failed to set irq mode (%d)\n", status);
169 return status;
170 }
171
172 if ((status = pci_map_interrupt(controller_->pci(), 0, irq_.reset_and_get_address())
173 != ZX_OK)) {
174 LOG_ERROR("Failed to map interrupt (%d)\n", status);
175 return status;
176 }
177
178 status = thrd_create_with_name(&irq_thread_, irq_handler, this, "i915-irq-thread");
179 if (status != ZX_OK) {
180 LOG_ERROR("Failed to create irq thread\n");
181 return status;
182 }
183
184 Resume();
185 return ZX_OK;
186 }
187
FinishInit()188 void Interrupts::FinishInit() {
189 auto ctrl = registers::MasterInterruptControl::Get().ReadFrom(controller_->mmio_space());
190 ctrl.set_enable_mask(1);
191 ctrl.WriteTo(controller_->mmio_space());
192 }
193
Resume()194 void Interrupts::Resume() {
195 EnableHotplugInterrupts();
196 }
197
198 } // namespace i915
199