Commit | Line | Data |
---|---|---|
fb1d9738 JB |
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 | ||
1925d456 TH |
67 | void vmw_update_sequence(struct vmw_private *dev_priv, |
68 | struct vmw_fifo_state *fifo_state) | |
69 | { | |
70 | __le32 __iomem *fifo_mem = dev_priv->mmio_virt; | |
71 | ||
72 | uint32_t sequence = ioread32(fifo_mem + SVGA_FIFO_FENCE); | |
73 | ||
74 | if (dev_priv->last_read_sequence != sequence) { | |
75 | dev_priv->last_read_sequence = sequence; | |
76 | vmw_fence_pull(&fifo_state->fence_queue, sequence); | |
77 | } | |
78 | } | |
fb1d9738 JB |
79 | |
80 | bool vmw_fence_signaled(struct vmw_private *dev_priv, | |
81 | uint32_t sequence) | |
82 | { | |
fb1d9738 JB |
83 | struct vmw_fifo_state *fifo_state; |
84 | bool ret; | |
85 | ||
86 | if (likely(dev_priv->last_read_sequence - sequence < VMW_FENCE_WRAP)) | |
87 | return true; | |
88 | ||
1925d456 TH |
89 | fifo_state = &dev_priv->fifo; |
90 | vmw_update_sequence(dev_priv, fifo_state); | |
fb1d9738 JB |
91 | if (likely(dev_priv->last_read_sequence - sequence < VMW_FENCE_WRAP)) |
92 | return true; | |
93 | ||
fb1d9738 JB |
94 | if (!(fifo_state->capabilities & SVGA_FIFO_CAP_FENCE) && |
95 | vmw_fifo_idle(dev_priv, sequence)) | |
96 | return true; | |
97 | ||
fb1d9738 JB |
98 | /** |
99 | * Then check if the sequence is higher than what we've actually | |
100 | * emitted. Then the fence is stale and signaled. | |
101 | */ | |
102 | ||
85b9e487 TH |
103 | ret = ((atomic_read(&dev_priv->fence_seq) - sequence) |
104 | > VMW_FENCE_WRAP); | |
fb1d9738 JB |
105 | |
106 | return ret; | |
107 | } | |
108 | ||
109 | int vmw_fallback_wait(struct vmw_private *dev_priv, | |
110 | bool lazy, | |
111 | bool fifo_idle, | |
112 | uint32_t sequence, | |
113 | bool interruptible, | |
114 | unsigned long timeout) | |
115 | { | |
116 | struct vmw_fifo_state *fifo_state = &dev_priv->fifo; | |
117 | ||
118 | uint32_t count = 0; | |
119 | uint32_t signal_seq; | |
120 | int ret; | |
121 | unsigned long end_jiffies = jiffies + timeout; | |
122 | bool (*wait_condition)(struct vmw_private *, uint32_t); | |
123 | DEFINE_WAIT(__wait); | |
124 | ||
125 | wait_condition = (fifo_idle) ? &vmw_fifo_idle : | |
126 | &vmw_fence_signaled; | |
127 | ||
128 | /** | |
129 | * Block command submission while waiting for idle. | |
130 | */ | |
131 | ||
132 | if (fifo_idle) | |
133 | down_read(&fifo_state->rwsem); | |
85b9e487 | 134 | signal_seq = atomic_read(&dev_priv->fence_seq); |
fb1d9738 JB |
135 | ret = 0; |
136 | ||
137 | for (;;) { | |
138 | prepare_to_wait(&dev_priv->fence_queue, &__wait, | |
139 | (interruptible) ? | |
140 | TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); | |
141 | if (wait_condition(dev_priv, sequence)) | |
142 | break; | |
143 | if (time_after_eq(jiffies, end_jiffies)) { | |
144 | DRM_ERROR("SVGA device lockup.\n"); | |
145 | break; | |
146 | } | |
147 | if (lazy) | |
148 | schedule_timeout(1); | |
149 | else if ((++count & 0x0F) == 0) { | |
150 | /** | |
151 | * FIXME: Use schedule_hr_timeout here for | |
152 | * newer kernels and lower CPU utilization. | |
153 | */ | |
154 | ||
155 | __set_current_state(TASK_RUNNING); | |
156 | schedule(); | |
157 | __set_current_state((interruptible) ? | |
158 | TASK_INTERRUPTIBLE : | |
159 | TASK_UNINTERRUPTIBLE); | |
160 | } | |
161 | if (interruptible && signal_pending(current)) { | |
3d3a5b32 | 162 | ret = -ERESTARTSYS; |
fb1d9738 JB |
163 | break; |
164 | } | |
165 | } | |
166 | finish_wait(&dev_priv->fence_queue, &__wait); | |
167 | if (ret == 0 && fifo_idle) { | |
168 | __le32 __iomem *fifo_mem = dev_priv->mmio_virt; | |
169 | iowrite32(signal_seq, fifo_mem + SVGA_FIFO_FENCE); | |
170 | } | |
171 | wake_up_all(&dev_priv->fence_queue); | |
172 | if (fifo_idle) | |
173 | up_read(&fifo_state->rwsem); | |
174 | ||
175 | return ret; | |
176 | } | |
177 | ||
178 | int vmw_wait_fence(struct vmw_private *dev_priv, | |
179 | bool lazy, uint32_t sequence, | |
180 | bool interruptible, unsigned long timeout) | |
181 | { | |
182 | long ret; | |
183 | unsigned long irq_flags; | |
184 | struct vmw_fifo_state *fifo = &dev_priv->fifo; | |
185 | ||
186 | if (likely(dev_priv->last_read_sequence - sequence < VMW_FENCE_WRAP)) | |
187 | return 0; | |
188 | ||
189 | if (likely(vmw_fence_signaled(dev_priv, sequence))) | |
190 | return 0; | |
191 | ||
192 | vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC); | |
193 | ||
194 | if (!(fifo->capabilities & SVGA_FIFO_CAP_FENCE)) | |
195 | return vmw_fallback_wait(dev_priv, lazy, true, sequence, | |
196 | interruptible, timeout); | |
197 | ||
198 | if (!(dev_priv->capabilities & SVGA_CAP_IRQMASK)) | |
199 | return vmw_fallback_wait(dev_priv, lazy, false, sequence, | |
200 | interruptible, timeout); | |
201 | ||
202 | mutex_lock(&dev_priv->hw_mutex); | |
203 | if (atomic_add_return(1, &dev_priv->fence_queue_waiters) > 0) { | |
204 | spin_lock_irqsave(&dev_priv->irq_lock, irq_flags); | |
205 | outl(SVGA_IRQFLAG_ANY_FENCE, | |
206 | dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); | |
207 | vmw_write(dev_priv, SVGA_REG_IRQMASK, | |
208 | vmw_read(dev_priv, SVGA_REG_IRQMASK) | | |
209 | SVGA_IRQFLAG_ANY_FENCE); | |
210 | spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags); | |
211 | } | |
212 | mutex_unlock(&dev_priv->hw_mutex); | |
213 | ||
214 | if (interruptible) | |
215 | ret = wait_event_interruptible_timeout | |
216 | (dev_priv->fence_queue, | |
217 | vmw_fence_signaled(dev_priv, sequence), | |
218 | timeout); | |
219 | else | |
220 | ret = wait_event_timeout | |
221 | (dev_priv->fence_queue, | |
222 | vmw_fence_signaled(dev_priv, sequence), | |
223 | timeout); | |
224 | ||
3d3a5b32 | 225 | if (unlikely(ret == 0)) |
fb1d9738 JB |
226 | ret = -EBUSY; |
227 | else if (likely(ret > 0)) | |
228 | ret = 0; | |
229 | ||
230 | mutex_lock(&dev_priv->hw_mutex); | |
231 | if (atomic_dec_and_test(&dev_priv->fence_queue_waiters)) { | |
232 | spin_lock_irqsave(&dev_priv->irq_lock, irq_flags); | |
233 | vmw_write(dev_priv, SVGA_REG_IRQMASK, | |
234 | vmw_read(dev_priv, SVGA_REG_IRQMASK) & | |
235 | ~SVGA_IRQFLAG_ANY_FENCE); | |
236 | spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags); | |
237 | } | |
238 | mutex_unlock(&dev_priv->hw_mutex); | |
239 | ||
240 | return ret; | |
241 | } | |
242 | ||
243 | void vmw_irq_preinstall(struct drm_device *dev) | |
244 | { | |
245 | struct vmw_private *dev_priv = vmw_priv(dev); | |
246 | uint32_t status; | |
247 | ||
248 | if (!(dev_priv->capabilities & SVGA_CAP_IRQMASK)) | |
249 | return; | |
250 | ||
251 | spin_lock_init(&dev_priv->irq_lock); | |
252 | status = inl(dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); | |
253 | outl(status, dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); | |
254 | } | |
255 | ||
256 | int vmw_irq_postinstall(struct drm_device *dev) | |
257 | { | |
258 | return 0; | |
259 | } | |
260 | ||
261 | void vmw_irq_uninstall(struct drm_device *dev) | |
262 | { | |
263 | struct vmw_private *dev_priv = vmw_priv(dev); | |
264 | uint32_t status; | |
265 | ||
266 | if (!(dev_priv->capabilities & SVGA_CAP_IRQMASK)) | |
267 | return; | |
268 | ||
269 | mutex_lock(&dev_priv->hw_mutex); | |
270 | vmw_write(dev_priv, SVGA_REG_IRQMASK, 0); | |
271 | mutex_unlock(&dev_priv->hw_mutex); | |
272 | ||
273 | status = inl(dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); | |
274 | outl(status, dev_priv->io_start + VMWGFX_IRQSTATUS_PORT); | |
275 | } | |
276 | ||
277 | #define VMW_FENCE_WAIT_TIMEOUT 3*HZ; | |
278 | ||
279 | int vmw_fence_wait_ioctl(struct drm_device *dev, void *data, | |
280 | struct drm_file *file_priv) | |
281 | { | |
282 | struct drm_vmw_fence_wait_arg *arg = | |
283 | (struct drm_vmw_fence_wait_arg *)data; | |
284 | unsigned long timeout; | |
285 | ||
286 | if (!arg->cookie_valid) { | |
287 | arg->cookie_valid = 1; | |
288 | arg->kernel_cookie = jiffies + VMW_FENCE_WAIT_TIMEOUT; | |
289 | } | |
290 | ||
291 | timeout = jiffies; | |
292 | if (time_after_eq(timeout, (unsigned long)arg->kernel_cookie)) | |
293 | return -EBUSY; | |
294 | ||
295 | timeout = (unsigned long)arg->kernel_cookie - timeout; | |
296 | return vmw_wait_fence(vmw_priv(dev), true, arg->sequence, true, timeout); | |
297 | } |