diff options
Diffstat (limited to 'drivers/gpu/drm/vmwgfx/vmwgfx_irq.c')
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_irq.c | 295 |
1 files changed, 295 insertions, 0 deletions
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c b/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c new file mode 100644 index 000000000000..9e0f0306eedb --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c | |||
@@ -0,0 +1,295 @@ | |||
1 | /************************************************************************** | ||
2 | * | ||
3 | * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA | ||
4 | * All Rights Reserved. | ||
5 | * | ||
6 | * Permission is hereby granted, free of charge, to any person obtaining a | ||
7 | * copy of this software and associated documentation files (the | ||
8 | * "Software"), to deal in the Software without restriction, including | ||
9 | * without limitation the rights to use, copy, modify, merge, publish, | ||
10 | * distribute, sub license, and/or sell copies of the Software, and to | ||
11 | * permit persons to whom the Software is furnished to do so, subject to | ||
12 | * the following conditions: | ||
13 | * | ||
14 | * The above copyright notice and this permission notice (including the | ||
15 | * next paragraph) shall be included in all copies or substantial portions | ||
16 | * of the Software. | ||
17 | * | ||
18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
20 | * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL | ||
21 | * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, | ||
22 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR | ||
23 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE | ||
24 | * USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
25 | * | ||
26 | **************************************************************************/ | ||
27 | |||
28 | #include "drmP.h" | ||
29 | #include "vmwgfx_drv.h" | ||
30 | |||
31 | #define VMW_FENCE_WRAP (1 << 24) | ||
32 | |||
33 | irqreturn_t vmw_irq_handler(DRM_IRQ_ARGS) | ||
34 | { | ||
35 | struct drm_device *dev = (struct drm_device *)arg; | ||
36 | struct vmw_private *dev_priv = vmw_priv(dev); | ||
37 | uint32_t status; | ||
38 | |||
39 | spin_lock(&dev_priv->irq_lock); | ||
40 | status = inl(dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); | ||
41 | spin_unlock(&dev_priv->irq_lock); | ||
42 | |||
43 | if (status & SVGA_IRQFLAG_ANY_FENCE) | ||
44 | wake_up_all(&dev_priv->fence_queue); | ||
45 | if (status & SVGA_IRQFLAG_FIFO_PROGRESS) | ||
46 | wake_up_all(&dev_priv->fifo_queue); | ||
47 | |||
48 | if (likely(status)) { | ||
49 | outl(status, dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); | ||
50 | return IRQ_HANDLED; | ||
51 | } | ||
52 | |||
53 | return IRQ_NONE; | ||
54 | } | ||
55 | |||
56 | static bool vmw_fifo_idle(struct vmw_private *dev_priv, uint32_t sequence) | ||
57 | { | ||
58 | uint32_t busy; | ||
59 | |||
60 | mutex_lock(&dev_priv->hw_mutex); | ||
61 | busy = vmw_read(dev_priv, SVGA_REG_BUSY); | ||
62 | mutex_unlock(&dev_priv->hw_mutex); | ||
63 | |||
64 | return (busy == 0); | ||
65 | } | ||
66 | |||
67 | |||
68 | bool vmw_fence_signaled(struct vmw_private *dev_priv, | ||
69 | uint32_t sequence) | ||
70 | { | ||
71 | __le32 __iomem *fifo_mem = dev_priv->mmio_virt; | ||
72 | struct vmw_fifo_state *fifo_state; | ||
73 | bool ret; | ||
74 | |||
75 | if (likely(dev_priv->last_read_sequence - sequence < VMW_FENCE_WRAP)) | ||
76 | return true; | ||
77 | |||
78 | dev_priv->last_read_sequence = ioread32(fifo_mem + SVGA_FIFO_FENCE); | ||
79 | if (likely(dev_priv->last_read_sequence - sequence < VMW_FENCE_WRAP)) | ||
80 | return true; | ||
81 | |||
82 | fifo_state = &dev_priv->fifo; | ||
83 | if (!(fifo_state->capabilities & SVGA_FIFO_CAP_FENCE) && | ||
84 | vmw_fifo_idle(dev_priv, sequence)) | ||
85 | return true; | ||
86 | |||
87 | /** | ||
88 | * Below is to signal stale fences that have wrapped. | ||
89 | * First, block fence submission. | ||
90 | */ | ||
91 | |||
92 | down_read(&fifo_state->rwsem); | ||
93 | |||
94 | /** | ||
95 | * Then check if the sequence is higher than what we've actually | ||
96 | * emitted. Then the fence is stale and signaled. | ||
97 | */ | ||
98 | |||
99 | ret = ((dev_priv->fence_seq - sequence) > VMW_FENCE_WRAP); | ||
100 | up_read(&fifo_state->rwsem); | ||
101 | |||
102 | return ret; | ||
103 | } | ||
104 | |||
105 | int vmw_fallback_wait(struct vmw_private *dev_priv, | ||
106 | bool lazy, | ||
107 | bool fifo_idle, | ||
108 | uint32_t sequence, | ||
109 | bool interruptible, | ||
110 | unsigned long timeout) | ||
111 | { | ||
112 | struct vmw_fifo_state *fifo_state = &dev_priv->fifo; | ||
113 | |||
114 | uint32_t count = 0; | ||
115 | uint32_t signal_seq; | ||
116 | int ret; | ||
117 | unsigned long end_jiffies = jiffies + timeout; | ||
118 | bool (*wait_condition)(struct vmw_private *, uint32_t); | ||
119 | DEFINE_WAIT(__wait); | ||
120 | |||
121 | wait_condition = (fifo_idle) ? &vmw_fifo_idle : | ||
122 | &vmw_fence_signaled; | ||
123 | |||
124 | /** | ||
125 | * Block command submission while waiting for idle. | ||
126 | */ | ||
127 | |||
128 | if (fifo_idle) | ||
129 | down_read(&fifo_state->rwsem); | ||
130 | signal_seq = dev_priv->fence_seq; | ||
131 | ret = 0; | ||
132 | |||
133 | for (;;) { | ||
134 | prepare_to_wait(&dev_priv->fence_queue, &__wait, | ||
135 | (interruptible) ? | ||
136 | TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); | ||
137 | if (wait_condition(dev_priv, sequence)) | ||
138 | break; | ||
139 | if (time_after_eq(jiffies, end_jiffies)) { | ||
140 | DRM_ERROR("SVGA device lockup.\n"); | ||
141 | break; | ||
142 | } | ||
143 | if (lazy) | ||
144 | schedule_timeout(1); | ||
145 | else if ((++count & 0x0F) == 0) { | ||
146 | /** | ||
147 | * FIXME: Use schedule_hr_timeout here for | ||
148 | * newer kernels and lower CPU utilization. | ||
149 | */ | ||
150 | |||
151 | __set_current_state(TASK_RUNNING); | ||
152 | schedule(); | ||
153 | __set_current_state((interruptible) ? | ||
154 | TASK_INTERRUPTIBLE : | ||
155 | TASK_UNINTERRUPTIBLE); | ||
156 | } | ||
157 | if (interruptible && signal_pending(current)) { | ||
158 | ret = -ERESTART; | ||
159 | break; | ||
160 | } | ||
161 | } | ||
162 | finish_wait(&dev_priv->fence_queue, &__wait); | ||
163 | if (ret == 0 && fifo_idle) { | ||
164 | __le32 __iomem *fifo_mem = dev_priv->mmio_virt; | ||
165 | iowrite32(signal_seq, fifo_mem + SVGA_FIFO_FENCE); | ||
166 | } | ||
167 | wake_up_all(&dev_priv->fence_queue); | ||
168 | if (fifo_idle) | ||
169 | up_read(&fifo_state->rwsem); | ||
170 | |||
171 | return ret; | ||
172 | } | ||
173 | |||
174 | int vmw_wait_fence(struct vmw_private *dev_priv, | ||
175 | bool lazy, uint32_t sequence, | ||
176 | bool interruptible, unsigned long timeout) | ||
177 | { | ||
178 | long ret; | ||
179 | unsigned long irq_flags; | ||
180 | struct vmw_fifo_state *fifo = &dev_priv->fifo; | ||
181 | |||
182 | if (likely(dev_priv->last_read_sequence - sequence < VMW_FENCE_WRAP)) | ||
183 | return 0; | ||
184 | |||
185 | if (likely(vmw_fence_signaled(dev_priv, sequence))) | ||
186 | return 0; | ||
187 | |||
188 | vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC); | ||
189 | |||
190 | if (!(fifo->capabilities & SVGA_FIFO_CAP_FENCE)) | ||
191 | return vmw_fallback_wait(dev_priv, lazy, true, sequence, | ||
192 | interruptible, timeout); | ||
193 | |||
194 | if (!(dev_priv->capabilities & SVGA_CAP_IRQMASK)) | ||
195 | return vmw_fallback_wait(dev_priv, lazy, false, sequence, | ||
196 | interruptible, timeout); | ||
197 | |||
198 | mutex_lock(&dev_priv->hw_mutex); | ||
199 | if (atomic_add_return(1, &dev_priv->fence_queue_waiters) > 0) { | ||
200 | spin_lock_irqsave(&dev_priv->irq_lock, irq_flags); | ||
201 | outl(SVGA_IRQFLAG_ANY_FENCE, | ||
202 | dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); | ||
203 | vmw_write(dev_priv, SVGA_REG_IRQMASK, | ||
204 | vmw_read(dev_priv, SVGA_REG_IRQMASK) | | ||
205 | SVGA_IRQFLAG_ANY_FENCE); | ||
206 | spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags); | ||
207 | } | ||
208 | mutex_unlock(&dev_priv->hw_mutex); | ||
209 | |||
210 | if (interruptible) | ||
211 | ret = wait_event_interruptible_timeout | ||
212 | (dev_priv->fence_queue, | ||
213 | vmw_fence_signaled(dev_priv, sequence), | ||
214 | timeout); | ||
215 | else | ||
216 | ret = wait_event_timeout | ||
217 | (dev_priv->fence_queue, | ||
218 | vmw_fence_signaled(dev_priv, sequence), | ||
219 | timeout); | ||
220 | |||
221 | if (unlikely(ret == -ERESTARTSYS)) | ||
222 | ret = -ERESTART; | ||
223 | else if (unlikely(ret == 0)) | ||
224 | ret = -EBUSY; | ||
225 | else if (likely(ret > 0)) | ||
226 | ret = 0; | ||
227 | |||
228 | mutex_lock(&dev_priv->hw_mutex); | ||
229 | if (atomic_dec_and_test(&dev_priv->fence_queue_waiters)) { | ||
230 | spin_lock_irqsave(&dev_priv->irq_lock, irq_flags); | ||
231 | vmw_write(dev_priv, SVGA_REG_IRQMASK, | ||
232 | vmw_read(dev_priv, SVGA_REG_IRQMASK) & | ||
233 | ~SVGA_IRQFLAG_ANY_FENCE); | ||
234 | spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags); | ||
235 | } | ||
236 | mutex_unlock(&dev_priv->hw_mutex); | ||
237 | |||
238 | return ret; | ||
239 | } | ||
240 | |||
241 | void vmw_irq_preinstall(struct drm_device *dev) | ||
242 | { | ||
243 | struct vmw_private *dev_priv = vmw_priv(dev); | ||
244 | uint32_t status; | ||
245 | |||
246 | if (!(dev_priv->capabilities & SVGA_CAP_IRQMASK)) | ||
247 | return; | ||
248 | |||
249 | spin_lock_init(&dev_priv->irq_lock); | ||
250 | status = inl(dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); | ||
251 | outl(status, dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); | ||
252 | } | ||
253 | |||
254 | int vmw_irq_postinstall(struct drm_device *dev) | ||
255 | { | ||
256 | return 0; | ||
257 | } | ||
258 | |||
259 | void vmw_irq_uninstall(struct drm_device *dev) | ||
260 | { | ||
261 | struct vmw_private *dev_priv = vmw_priv(dev); | ||
262 | uint32_t status; | ||
263 | |||
264 | if (!(dev_priv->capabilities & SVGA_CAP_IRQMASK)) | ||
265 | return; | ||
266 | |||
267 | mutex_lock(&dev_priv->hw_mutex); | ||
268 | vmw_write(dev_priv, SVGA_REG_IRQMASK, 0); | ||
269 | mutex_unlock(&dev_priv->hw_mutex); | ||
270 | |||
271 | status = inl(dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); | ||
272 | outl(status, dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); | ||
273 | } | ||
274 | |||
275 | #define VMW_FENCE_WAIT_TIMEOUT 3*HZ; | ||
276 | |||
277 | int vmw_fence_wait_ioctl(struct drm_device *dev, void *data, | ||
278 | struct drm_file *file_priv) | ||
279 | { | ||
280 | struct drm_vmw_fence_wait_arg *arg = | ||
281 | (struct drm_vmw_fence_wait_arg *)data; | ||
282 | unsigned long timeout; | ||
283 | |||
284 | if (!arg->cookie_valid) { | ||
285 | arg->cookie_valid = 1; | ||
286 | arg->kernel_cookie = jiffies + VMW_FENCE_WAIT_TIMEOUT; | ||
287 | } | ||
288 | |||
289 | timeout = jiffies; | ||
290 | if (time_after_eq(timeout, (unsigned long)arg->kernel_cookie)) | ||
291 | return -EBUSY; | ||
292 | |||
293 | timeout = (unsigned long)arg->kernel_cookie - timeout; | ||
294 | return vmw_wait_fence(vmw_priv(dev), true, arg->sequence, true, timeout); | ||
295 | } | ||