]>
Commit | Line | Data |
---|---|---|
0d6aa60b | 1 | /* i915_irq.c -- IRQ support for the I915 -*- linux-c -*- |
1da177e4 | 2 | */ |
0d6aa60b | 3 | /* |
1da177e4 LT |
4 | * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. |
5 | * All Rights Reserved. | |
bc54fd1a DA |
6 | * |
7 | * Permission is hereby granted, free of charge, to any person obtaining a | |
8 | * copy of this software and associated documentation files (the | |
9 | * "Software"), to deal in the Software without restriction, including | |
10 | * without limitation the rights to use, copy, modify, merge, publish, | |
11 | * distribute, sub license, and/or sell copies of the Software, and to | |
12 | * permit persons to whom the Software is furnished to do so, subject to | |
13 | * the following conditions: | |
14 | * | |
15 | * The above copyright notice and this permission notice (including the | |
16 | * next paragraph) shall be included in all copies or substantial portions | |
17 | * of the Software. | |
18 | * | |
19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
20 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
21 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. | |
22 | * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR | |
23 | * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | |
24 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | |
25 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
26 | * | |
0d6aa60b | 27 | */ |
1da177e4 LT |
28 | |
29 | #include "drmP.h" | |
30 | #include "drm.h" | |
31 | #include "i915_drm.h" | |
32 | #include "i915_drv.h" | |
33 | ||
1da177e4 | 34 | #define MAX_NOPID ((u32)~0) |
1da177e4 | 35 | |
ed4cb414 EA |
36 | /** These are the interrupts used by the driver */ |
37 | #define I915_INTERRUPT_ENABLE_MASK (I915_USER_INTERRUPT | \ | |
38 | I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT | \ | |
39 | I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT) | |
40 | ||
41 | static inline void | |
42 | i915_enable_irq(drm_i915_private_t *dev_priv, u32 mask) | |
43 | { | |
44 | if ((dev_priv->irq_mask_reg & mask) != 0) { | |
45 | dev_priv->irq_mask_reg &= ~mask; | |
46 | I915_WRITE(IMR, dev_priv->irq_mask_reg); | |
47 | (void) I915_READ(IMR); | |
48 | } | |
49 | } | |
50 | ||
51 | static inline void | |
52 | i915_disable_irq(drm_i915_private_t *dev_priv, u32 mask) | |
53 | { | |
54 | if ((dev_priv->irq_mask_reg & mask) != mask) { | |
55 | dev_priv->irq_mask_reg |= mask; | |
56 | I915_WRITE(IMR, dev_priv->irq_mask_reg); | |
57 | (void) I915_READ(IMR); | |
58 | } | |
59 | } | |
60 | ||
a6b54f3f MD |
61 | /** |
62 | * Emit blits for scheduled buffer swaps. | |
63 | * | |
64 | * This function will be called with the HW lock held. | |
65 | */ | |
84b1fd10 | 66 | static void i915_vblank_tasklet(struct drm_device *dev) |
a6b54f3f MD |
67 | { |
68 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | |
af6061af | 69 | unsigned long irqflags; |
3188a24c | 70 | struct list_head *list, *tmp, hits, *hit; |
af6061af DA |
71 | int nhits, nrects, slice[2], upper[2], lower[2], i; |
72 | unsigned counter[2] = { atomic_read(&dev->vbl_received), | |
73 | atomic_read(&dev->vbl_received2) }; | |
c60ce623 | 74 | struct drm_drawable_info *drw; |
3188a24c | 75 | drm_i915_sarea_t *sarea_priv = dev_priv->sarea_priv; |
af6061af | 76 | u32 cpp = dev_priv->cpp; |
3188a24c MD |
77 | u32 cmd = (cpp == 4) ? (XY_SRC_COPY_BLT_CMD | |
78 | XY_SRC_COPY_BLT_WRITE_ALPHA | | |
79 | XY_SRC_COPY_BLT_WRITE_RGB) | |
80 | : XY_SRC_COPY_BLT_CMD; | |
7b832b56 KP |
81 | u32 src_pitch = sarea_priv->pitch * cpp; |
82 | u32 dst_pitch = sarea_priv->pitch * cpp; | |
83 | u32 ropcpp = (0xcc << 16) | ((cpp - 1) << 24); | |
3188a24c | 84 | RING_LOCALS; |
a6b54f3f | 85 | |
3d25802e | 86 | if (IS_I965G(dev) && sarea_priv->front_tiled) { |
7b832b56 KP |
87 | cmd |= XY_SRC_COPY_BLT_DST_TILED; |
88 | dst_pitch >>= 2; | |
89 | } | |
3d25802e | 90 | if (IS_I965G(dev) && sarea_priv->back_tiled) { |
7b832b56 KP |
91 | cmd |= XY_SRC_COPY_BLT_SRC_TILED; |
92 | src_pitch >>= 2; | |
93 | } | |
94 | ||
a6b54f3f MD |
95 | DRM_DEBUG("\n"); |
96 | ||
3188a24c MD |
97 | INIT_LIST_HEAD(&hits); |
98 | ||
99 | nhits = nrects = 0; | |
100 | ||
af6061af | 101 | spin_lock_irqsave(&dev_priv->swaps_lock, irqflags); |
a6b54f3f | 102 | |
3188a24c | 103 | /* Find buffer swaps scheduled for this vertical blank */ |
a6b54f3f MD |
104 | list_for_each_safe(list, tmp, &dev_priv->vbl_swaps.head) { |
105 | drm_i915_vbl_swap_t *vbl_swap = | |
106 | list_entry(list, drm_i915_vbl_swap_t, head); | |
a6b54f3f | 107 | |
af6061af | 108 | if ((counter[vbl_swap->pipe] - vbl_swap->sequence) > (1<<23)) |
3188a24c MD |
109 | continue; |
110 | ||
111 | list_del(list); | |
112 | dev_priv->swaps_pending--; | |
113 | ||
114 | spin_unlock(&dev_priv->swaps_lock); | |
115 | spin_lock(&dev->drw_lock); | |
a6b54f3f | 116 | |
3188a24c | 117 | drw = drm_get_drawable_info(dev, vbl_swap->drw_id); |
a6b54f3f | 118 | |
3188a24c MD |
119 | if (!drw) { |
120 | spin_unlock(&dev->drw_lock); | |
121 | drm_free(vbl_swap, sizeof(*vbl_swap), DRM_MEM_DRIVER); | |
122 | spin_lock(&dev_priv->swaps_lock); | |
123 | continue; | |
124 | } | |
a6b54f3f | 125 | |
3188a24c MD |
126 | list_for_each(hit, &hits) { |
127 | drm_i915_vbl_swap_t *swap_cmp = | |
128 | list_entry(hit, drm_i915_vbl_swap_t, head); | |
c60ce623 | 129 | struct drm_drawable_info *drw_cmp = |
3188a24c | 130 | drm_get_drawable_info(dev, swap_cmp->drw_id); |
a6b54f3f | 131 | |
3188a24c MD |
132 | if (drw_cmp && |
133 | drw_cmp->rects[0].y1 > drw->rects[0].y1) { | |
134 | list_add_tail(list, hit); | |
135 | break; | |
a6b54f3f | 136 | } |
3188a24c | 137 | } |
a6b54f3f | 138 | |
3188a24c | 139 | spin_unlock(&dev->drw_lock); |
a6b54f3f | 140 | |
3188a24c MD |
141 | /* List of hits was empty, or we reached the end of it */ |
142 | if (hit == &hits) | |
143 | list_add_tail(list, hits.prev); | |
a6b54f3f | 144 | |
3188a24c | 145 | nhits++; |
a6b54f3f | 146 | |
3188a24c MD |
147 | spin_lock(&dev_priv->swaps_lock); |
148 | } | |
149 | ||
af6061af DA |
150 | if (nhits == 0) { |
151 | spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); | |
ac741ab7 | 152 | return; |
af6061af DA |
153 | } |
154 | ||
155 | spin_unlock(&dev_priv->swaps_lock); | |
3188a24c | 156 | |
ac741ab7 | 157 | i915_kernel_lost_context(dev); |
3188a24c | 158 | |
af6061af DA |
159 | if (IS_I965G(dev)) { |
160 | BEGIN_LP_RING(4); | |
161 | ||
162 | OUT_RING(GFX_OP_DRAWRECT_INFO_I965); | |
163 | OUT_RING(0); | |
164 | OUT_RING(((sarea_priv->width - 1) & 0xffff) | ((sarea_priv->height - 1) << 16)); | |
165 | OUT_RING(0); | |
166 | ADVANCE_LP_RING(); | |
167 | } else { | |
168 | BEGIN_LP_RING(6); | |
ac741ab7 | 169 | |
af6061af DA |
170 | OUT_RING(GFX_OP_DRAWRECT_INFO); |
171 | OUT_RING(0); | |
172 | OUT_RING(0); | |
173 | OUT_RING(sarea_priv->width | sarea_priv->height << 16); | |
174 | OUT_RING(sarea_priv->width | sarea_priv->height << 16); | |
175 | OUT_RING(0); | |
176 | ||
177 | ADVANCE_LP_RING(); | |
178 | } | |
179 | ||
180 | sarea_priv->ctxOwner = DRM_KERNEL_CONTEXT; | |
181 | ||
182 | upper[0] = upper[1] = 0; | |
183 | slice[0] = max(sarea_priv->pipeA_h / nhits, 1); | |
184 | slice[1] = max(sarea_priv->pipeB_h / nhits, 1); | |
185 | lower[0] = sarea_priv->pipeA_y + slice[0]; | |
186 | lower[1] = sarea_priv->pipeB_y + slice[0]; | |
3188a24c MD |
187 | |
188 | spin_lock(&dev->drw_lock); | |
189 | ||
190 | /* Emit blits for buffer swaps, partitioning both outputs into as many | |
191 | * slices as there are buffer swaps scheduled in order to avoid tearing | |
192 | * (based on the assumption that a single buffer swap would always | |
193 | * complete before scanout starts). | |
194 | */ | |
195 | for (i = 0; i++ < nhits; | |
196 | upper[0] = lower[0], lower[0] += slice[0], | |
197 | upper[1] = lower[1], lower[1] += slice[1]) { | |
198 | if (i == nhits) | |
199 | lower[0] = lower[1] = sarea_priv->height; | |
200 | ||
201 | list_for_each(hit, &hits) { | |
202 | drm_i915_vbl_swap_t *swap_hit = | |
203 | list_entry(hit, drm_i915_vbl_swap_t, head); | |
c60ce623 | 204 | struct drm_clip_rect *rect; |
af6061af | 205 | int num_rects, pipe; |
3188a24c MD |
206 | unsigned short top, bottom; |
207 | ||
208 | drw = drm_get_drawable_info(dev, swap_hit->drw_id); | |
209 | ||
210 | if (!drw) | |
211 | continue; | |
212 | ||
213 | rect = drw->rects; | |
af6061af DA |
214 | pipe = swap_hit->pipe; |
215 | top = upper[pipe]; | |
216 | bottom = lower[pipe]; | |
3188a24c MD |
217 | |
218 | for (num_rects = drw->num_rects; num_rects--; rect++) { | |
219 | int y1 = max(rect->y1, top); | |
220 | int y2 = min(rect->y2, bottom); | |
221 | ||
222 | if (y1 >= y2) | |
223 | continue; | |
224 | ||
225 | BEGIN_LP_RING(8); | |
226 | ||
227 | OUT_RING(cmd); | |
7b832b56 | 228 | OUT_RING(ropcpp | dst_pitch); |
3188a24c MD |
229 | OUT_RING((y1 << 16) | rect->x1); |
230 | OUT_RING((y2 << 16) | rect->x2); | |
af6061af | 231 | OUT_RING(sarea_priv->front_offset); |
3188a24c | 232 | OUT_RING((y1 << 16) | rect->x1); |
7b832b56 | 233 | OUT_RING(src_pitch); |
af6061af | 234 | OUT_RING(sarea_priv->back_offset); |
3188a24c MD |
235 | |
236 | ADVANCE_LP_RING(); | |
237 | } | |
a6b54f3f MD |
238 | } |
239 | } | |
240 | ||
af6061af | 241 | spin_unlock_irqrestore(&dev->drw_lock, irqflags); |
3188a24c MD |
242 | |
243 | list_for_each_safe(hit, tmp, &hits) { | |
244 | drm_i915_vbl_swap_t *swap_hit = | |
245 | list_entry(hit, drm_i915_vbl_swap_t, head); | |
246 | ||
247 | list_del(hit); | |
248 | ||
249 | drm_free(swap_hit, sizeof(*swap_hit), DRM_MEM_DRIVER); | |
250 | } | |
a6b54f3f MD |
251 | } |
252 | ||
1da177e4 LT |
253 | irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) |
254 | { | |
84b1fd10 | 255 | struct drm_device *dev = (struct drm_device *) arg; |
1da177e4 | 256 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; |
e4a7b1d1 | 257 | u32 pipea_stats, pipeb_stats; |
ed4cb414 | 258 | u32 iir; |
1da177e4 | 259 | |
585fb111 JB |
260 | pipea_stats = I915_READ(PIPEASTAT); |
261 | pipeb_stats = I915_READ(PIPEBSTAT); | |
6e5fca53 | 262 | |
ed4cb414 EA |
263 | if (dev->pdev->msi_enabled) |
264 | I915_WRITE(IMR, ~0); | |
265 | iir = I915_READ(IIR); | |
a6b54f3f | 266 | |
ed4cb414 | 267 | DRM_DEBUG("iir=%08x\n", iir); |
af6061af | 268 | |
ed4cb414 EA |
269 | if (iir == 0) { |
270 | if (dev->pdev->msi_enabled) { | |
271 | I915_WRITE(IMR, dev_priv->irq_mask_reg); | |
272 | (void) I915_READ(IMR); | |
273 | } | |
af6061af | 274 | return IRQ_NONE; |
ed4cb414 | 275 | } |
af6061af | 276 | |
ed4cb414 EA |
277 | I915_WRITE(IIR, iir); |
278 | if (dev->pdev->msi_enabled) | |
279 | I915_WRITE(IMR, dev_priv->irq_mask_reg); | |
280 | (void) I915_READ(IIR); /* Flush posted writes */ | |
af6061af DA |
281 | |
282 | dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); | |
283 | ||
ed4cb414 | 284 | if (iir & I915_USER_INTERRUPT) |
ac741ab7 | 285 | DRM_WAKEUP(&dev_priv->irq_queue); |
af6061af | 286 | |
ed4cb414 EA |
287 | if (iir & (I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT | |
288 | I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT)) { | |
af6061af DA |
289 | int vblank_pipe = dev_priv->vblank_pipe; |
290 | ||
291 | if ((vblank_pipe & | |
292 | (DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B)) | |
293 | == (DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B)) { | |
ed4cb414 | 294 | if (iir & I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT) |
af6061af | 295 | atomic_inc(&dev->vbl_received); |
ed4cb414 | 296 | if (iir & I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT) |
af6061af | 297 | atomic_inc(&dev->vbl_received2); |
ed4cb414 | 298 | } else if (((iir & I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT) && |
af6061af | 299 | (vblank_pipe & DRM_I915_VBLANK_PIPE_A)) || |
ed4cb414 | 300 | ((iir & I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT) && |
af6061af DA |
301 | (vblank_pipe & DRM_I915_VBLANK_PIPE_B))) |
302 | atomic_inc(&dev->vbl_received); | |
303 | ||
304 | DRM_WAKEUP(&dev->vbl_queue); | |
305 | drm_vbl_send_signals(dev); | |
306 | ||
2228ed67 MD |
307 | if (dev_priv->swaps_pending > 0) |
308 | drm_locked_tasklet(dev, i915_vblank_tasklet); | |
585fb111 | 309 | I915_WRITE(PIPEASTAT, |
af6061af | 310 | pipea_stats|I915_VBLANK_INTERRUPT_ENABLE| |
585fb111 JB |
311 | PIPE_VBLANK_INTERRUPT_STATUS); |
312 | I915_WRITE(PIPEBSTAT, | |
af6061af | 313 | pipeb_stats|I915_VBLANK_INTERRUPT_ENABLE| |
585fb111 | 314 | PIPE_VBLANK_INTERRUPT_STATUS); |
0d6aa60b | 315 | } |
1da177e4 LT |
316 | |
317 | return IRQ_HANDLED; | |
318 | } | |
319 | ||
af6061af | 320 | static int i915_emit_irq(struct drm_device * dev) |
1da177e4 LT |
321 | { |
322 | drm_i915_private_t *dev_priv = dev->dev_private; | |
1da177e4 LT |
323 | RING_LOCALS; |
324 | ||
325 | i915_kernel_lost_context(dev); | |
326 | ||
3e684eae | 327 | DRM_DEBUG("\n"); |
1da177e4 | 328 | |
c29b669c | 329 | dev_priv->sarea_priv->last_enqueue = ++dev_priv->counter; |
1da177e4 | 330 | |
c29b669c AH |
331 | if (dev_priv->counter > 0x7FFFFFFFUL) |
332 | dev_priv->sarea_priv->last_enqueue = dev_priv->counter = 1; | |
333 | ||
334 | BEGIN_LP_RING(6); | |
585fb111 JB |
335 | OUT_RING(MI_STORE_DWORD_INDEX); |
336 | OUT_RING(5 << MI_STORE_DWORD_INDEX_SHIFT); | |
c29b669c AH |
337 | OUT_RING(dev_priv->counter); |
338 | OUT_RING(0); | |
1da177e4 | 339 | OUT_RING(0); |
585fb111 | 340 | OUT_RING(MI_USER_INTERRUPT); |
1da177e4 | 341 | ADVANCE_LP_RING(); |
bc5f4523 | 342 | |
c29b669c | 343 | return dev_priv->counter; |
1da177e4 LT |
344 | } |
345 | ||
ed4cb414 EA |
346 | static void i915_user_irq_get(struct drm_device *dev) |
347 | { | |
348 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | |
349 | ||
350 | spin_lock(&dev_priv->user_irq_lock); | |
351 | if (dev->irq_enabled && (++dev_priv->user_irq_refcount == 1)) | |
352 | i915_enable_irq(dev_priv, I915_USER_INTERRUPT); | |
353 | spin_unlock(&dev_priv->user_irq_lock); | |
354 | } | |
355 | ||
356 | static void i915_user_irq_put(struct drm_device *dev) | |
357 | { | |
358 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | |
359 | ||
360 | spin_lock(&dev_priv->user_irq_lock); | |
361 | BUG_ON(dev->irq_enabled && dev_priv->user_irq_refcount <= 0); | |
362 | if (dev->irq_enabled && (--dev_priv->user_irq_refcount == 0)) | |
363 | i915_disable_irq(dev_priv, I915_USER_INTERRUPT); | |
364 | spin_unlock(&dev_priv->user_irq_lock); | |
365 | } | |
366 | ||
84b1fd10 | 367 | static int i915_wait_irq(struct drm_device * dev, int irq_nr) |
1da177e4 LT |
368 | { |
369 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | |
370 | int ret = 0; | |
371 | ||
3e684eae | 372 | DRM_DEBUG("irq_nr=%d breadcrumb=%d\n", irq_nr, |
1da177e4 LT |
373 | READ_BREADCRUMB(dev_priv)); |
374 | ||
ed4cb414 EA |
375 | if (READ_BREADCRUMB(dev_priv) >= irq_nr) { |
376 | dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); | |
1da177e4 | 377 | return 0; |
ed4cb414 | 378 | } |
1da177e4 LT |
379 | |
380 | dev_priv->sarea_priv->perf_boxes |= I915_BOX_WAIT; | |
381 | ||
ed4cb414 | 382 | i915_user_irq_get(dev); |
1da177e4 LT |
383 | DRM_WAIT_ON(ret, dev_priv->irq_queue, 3 * DRM_HZ, |
384 | READ_BREADCRUMB(dev_priv) >= irq_nr); | |
ed4cb414 | 385 | i915_user_irq_put(dev); |
1da177e4 | 386 | |
20caafa6 | 387 | if (ret == -EBUSY) { |
3e684eae | 388 | DRM_ERROR("EBUSY -- rec: %d emitted: %d\n", |
1da177e4 LT |
389 | READ_BREADCRUMB(dev_priv), (int)dev_priv->counter); |
390 | } | |
391 | ||
af6061af | 392 | dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); |
1da177e4 LT |
393 | return ret; |
394 | } | |
395 | ||
af6061af DA |
396 | static int i915_driver_vblank_do_wait(struct drm_device *dev, unsigned int *sequence, |
397 | atomic_t *counter) | |
398 | { | |
399 | drm_i915_private_t *dev_priv = dev->dev_private; | |
400 | unsigned int cur_vblank; | |
401 | int ret = 0; | |
402 | ||
403 | if (!dev_priv) { | |
404 | DRM_ERROR("called with no initialization\n"); | |
405 | return -EINVAL; | |
406 | } | |
407 | ||
408 | DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ, | |
409 | (((cur_vblank = atomic_read(counter)) | |
410 | - *sequence) <= (1<<23))); | |
411 | ||
412 | *sequence = cur_vblank; | |
413 | ||
414 | return ret; | |
415 | } | |
416 | ||
417 | ||
418 | int i915_driver_vblank_wait(struct drm_device *dev, unsigned int *sequence) | |
419 | { | |
420 | return i915_driver_vblank_do_wait(dev, sequence, &dev->vbl_received); | |
421 | } | |
422 | ||
423 | int i915_driver_vblank_wait2(struct drm_device *dev, unsigned int *sequence) | |
424 | { | |
425 | return i915_driver_vblank_do_wait(dev, sequence, &dev->vbl_received2); | |
426 | } | |
427 | ||
1da177e4 LT |
428 | /* Needs the lock as it touches the ring. |
429 | */ | |
c153f45f EA |
430 | int i915_irq_emit(struct drm_device *dev, void *data, |
431 | struct drm_file *file_priv) | |
1da177e4 | 432 | { |
1da177e4 | 433 | drm_i915_private_t *dev_priv = dev->dev_private; |
c153f45f | 434 | drm_i915_irq_emit_t *emit = data; |
1da177e4 LT |
435 | int result; |
436 | ||
6c340eac | 437 | LOCK_TEST_WITH_RETURN(dev, file_priv); |
1da177e4 LT |
438 | |
439 | if (!dev_priv) { | |
3e684eae | 440 | DRM_ERROR("called with no initialization\n"); |
20caafa6 | 441 | return -EINVAL; |
1da177e4 LT |
442 | } |
443 | ||
1da177e4 LT |
444 | result = i915_emit_irq(dev); |
445 | ||
c153f45f | 446 | if (DRM_COPY_TO_USER(emit->irq_seq, &result, sizeof(int))) { |
1da177e4 | 447 | DRM_ERROR("copy_to_user\n"); |
20caafa6 | 448 | return -EFAULT; |
1da177e4 LT |
449 | } |
450 | ||
451 | return 0; | |
452 | } | |
453 | ||
454 | /* Doesn't need the hardware lock. | |
455 | */ | |
c153f45f EA |
456 | int i915_irq_wait(struct drm_device *dev, void *data, |
457 | struct drm_file *file_priv) | |
1da177e4 | 458 | { |
1da177e4 | 459 | drm_i915_private_t *dev_priv = dev->dev_private; |
c153f45f | 460 | drm_i915_irq_wait_t *irqwait = data; |
1da177e4 LT |
461 | |
462 | if (!dev_priv) { | |
3e684eae | 463 | DRM_ERROR("called with no initialization\n"); |
20caafa6 | 464 | return -EINVAL; |
1da177e4 LT |
465 | } |
466 | ||
c153f45f | 467 | return i915_wait_irq(dev, irqwait->irq_seq); |
1da177e4 LT |
468 | } |
469 | ||
702880f2 DA |
470 | /* Set the vblank monitor pipe |
471 | */ | |
c153f45f EA |
472 | int i915_vblank_pipe_set(struct drm_device *dev, void *data, |
473 | struct drm_file *file_priv) | |
702880f2 | 474 | { |
702880f2 | 475 | drm_i915_private_t *dev_priv = dev->dev_private; |
c153f45f | 476 | drm_i915_vblank_pipe_t *pipe = data; |
ed4cb414 | 477 | u32 enable_mask = 0, disable_mask = 0; |
702880f2 DA |
478 | |
479 | if (!dev_priv) { | |
3e684eae | 480 | DRM_ERROR("called with no initialization\n"); |
20caafa6 | 481 | return -EINVAL; |
702880f2 DA |
482 | } |
483 | ||
c153f45f | 484 | if (pipe->pipe & ~(DRM_I915_VBLANK_PIPE_A|DRM_I915_VBLANK_PIPE_B)) { |
3e684eae | 485 | DRM_ERROR("called with invalid pipe 0x%x\n", pipe->pipe); |
20caafa6 | 486 | return -EINVAL; |
5b51694a MD |
487 | } |
488 | ||
ed4cb414 EA |
489 | if (pipe->pipe & DRM_I915_VBLANK_PIPE_A) |
490 | enable_mask |= I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT; | |
491 | else | |
492 | disable_mask |= I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT; | |
493 | ||
494 | if (pipe->pipe & DRM_I915_VBLANK_PIPE_B) | |
495 | enable_mask |= I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT; | |
496 | else | |
497 | disable_mask |= I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT; | |
5b51694a | 498 | |
ed4cb414 EA |
499 | i915_enable_irq(dev_priv, enable_mask); |
500 | i915_disable_irq(dev_priv, disable_mask); | |
501 | ||
502 | dev_priv->vblank_pipe = pipe->pipe; | |
af6061af | 503 | |
5b51694a | 504 | return 0; |
702880f2 DA |
505 | } |
506 | ||
c153f45f EA |
507 | int i915_vblank_pipe_get(struct drm_device *dev, void *data, |
508 | struct drm_file *file_priv) | |
702880f2 | 509 | { |
702880f2 | 510 | drm_i915_private_t *dev_priv = dev->dev_private; |
c153f45f | 511 | drm_i915_vblank_pipe_t *pipe = data; |
702880f2 DA |
512 | u16 flag; |
513 | ||
514 | if (!dev_priv) { | |
3e684eae | 515 | DRM_ERROR("called with no initialization\n"); |
20caafa6 | 516 | return -EINVAL; |
702880f2 DA |
517 | } |
518 | ||
ed4cb414 | 519 | flag = I915_READ(IMR); |
c153f45f | 520 | pipe->pipe = 0; |
585fb111 | 521 | if (flag & I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT) |
c153f45f | 522 | pipe->pipe |= DRM_I915_VBLANK_PIPE_A; |
585fb111 | 523 | if (flag & I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT) |
c153f45f EA |
524 | pipe->pipe |= DRM_I915_VBLANK_PIPE_B; |
525 | ||
702880f2 DA |
526 | return 0; |
527 | } | |
528 | ||
a6b54f3f MD |
529 | /** |
530 | * Schedule buffer swap at given vertical blank. | |
531 | */ | |
c153f45f EA |
532 | int i915_vblank_swap(struct drm_device *dev, void *data, |
533 | struct drm_file *file_priv) | |
a6b54f3f | 534 | { |
a6b54f3f | 535 | drm_i915_private_t *dev_priv = dev->dev_private; |
c153f45f | 536 | drm_i915_vblank_swap_t *swap = data; |
a6b54f3f | 537 | drm_i915_vbl_swap_t *vbl_swap; |
af6061af | 538 | unsigned int pipe, seqtype, curseq; |
a0b136bb | 539 | unsigned long irqflags; |
a6b54f3f MD |
540 | struct list_head *list; |
541 | ||
542 | if (!dev_priv) { | |
543 | DRM_ERROR("%s called with no initialization\n", __func__); | |
20caafa6 | 544 | return -EINVAL; |
a6b54f3f MD |
545 | } |
546 | ||
af6061af | 547 | if (dev_priv->sarea_priv->rotation) { |
a6b54f3f | 548 | DRM_DEBUG("Rotation not supported\n"); |
20caafa6 | 549 | return -EINVAL; |
a6b54f3f MD |
550 | } |
551 | ||
c153f45f | 552 | if (swap->seqtype & ~(_DRM_VBLANK_RELATIVE | _DRM_VBLANK_ABSOLUTE | |
af6061af | 553 | _DRM_VBLANK_SECONDARY | _DRM_VBLANK_NEXTONMISS)) { |
c153f45f | 554 | DRM_ERROR("Invalid sequence type 0x%x\n", swap->seqtype); |
20caafa6 | 555 | return -EINVAL; |
541f29aa MD |
556 | } |
557 | ||
af6061af | 558 | pipe = (swap->seqtype & _DRM_VBLANK_SECONDARY) ? 1 : 0; |
541f29aa | 559 | |
c153f45f | 560 | seqtype = swap->seqtype & (_DRM_VBLANK_RELATIVE | _DRM_VBLANK_ABSOLUTE); |
541f29aa | 561 | |
541f29aa MD |
562 | if (!(dev_priv->vblank_pipe & (1 << pipe))) { |
563 | DRM_ERROR("Invalid pipe %d\n", pipe); | |
20caafa6 | 564 | return -EINVAL; |
a6b54f3f MD |
565 | } |
566 | ||
567 | spin_lock_irqsave(&dev->drw_lock, irqflags); | |
568 | ||
c153f45f | 569 | if (!drm_get_drawable_info(dev, swap->drawable)) { |
a6b54f3f | 570 | spin_unlock_irqrestore(&dev->drw_lock, irqflags); |
c153f45f | 571 | DRM_DEBUG("Invalid drawable ID %d\n", swap->drawable); |
20caafa6 | 572 | return -EINVAL; |
a6b54f3f MD |
573 | } |
574 | ||
575 | spin_unlock_irqrestore(&dev->drw_lock, irqflags); | |
576 | ||
af6061af | 577 | curseq = atomic_read(pipe ? &dev->vbl_received2 : &dev->vbl_received); |
541f29aa | 578 | |
2228ed67 | 579 | if (seqtype == _DRM_VBLANK_RELATIVE) |
c153f45f | 580 | swap->sequence += curseq; |
2228ed67 | 581 | |
c153f45f EA |
582 | if ((curseq - swap->sequence) <= (1<<23)) { |
583 | if (swap->seqtype & _DRM_VBLANK_NEXTONMISS) { | |
584 | swap->sequence = curseq + 1; | |
2228ed67 | 585 | } else { |
541f29aa | 586 | DRM_DEBUG("Missed target sequence\n"); |
20caafa6 | 587 | return -EINVAL; |
541f29aa | 588 | } |
541f29aa MD |
589 | } |
590 | ||
2228ed67 MD |
591 | spin_lock_irqsave(&dev_priv->swaps_lock, irqflags); |
592 | ||
a6b54f3f MD |
593 | list_for_each(list, &dev_priv->vbl_swaps.head) { |
594 | vbl_swap = list_entry(list, drm_i915_vbl_swap_t, head); | |
595 | ||
c153f45f | 596 | if (vbl_swap->drw_id == swap->drawable && |
af6061af | 597 | vbl_swap->pipe == pipe && |
c153f45f | 598 | vbl_swap->sequence == swap->sequence) { |
a6b54f3f MD |
599 | spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); |
600 | DRM_DEBUG("Already scheduled\n"); | |
601 | return 0; | |
602 | } | |
603 | } | |
604 | ||
605 | spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); | |
606 | ||
21fa60ed MD |
607 | if (dev_priv->swaps_pending >= 100) { |
608 | DRM_DEBUG("Too many swaps queued\n"); | |
20caafa6 | 609 | return -EBUSY; |
21fa60ed MD |
610 | } |
611 | ||
54583bf4 | 612 | vbl_swap = drm_calloc(1, sizeof(*vbl_swap), DRM_MEM_DRIVER); |
a6b54f3f MD |
613 | |
614 | if (!vbl_swap) { | |
615 | DRM_ERROR("Failed to allocate memory to queue swap\n"); | |
20caafa6 | 616 | return -ENOMEM; |
a6b54f3f MD |
617 | } |
618 | ||
619 | DRM_DEBUG("\n"); | |
620 | ||
c153f45f | 621 | vbl_swap->drw_id = swap->drawable; |
af6061af | 622 | vbl_swap->pipe = pipe; |
c153f45f | 623 | vbl_swap->sequence = swap->sequence; |
a6b54f3f MD |
624 | |
625 | spin_lock_irqsave(&dev_priv->swaps_lock, irqflags); | |
626 | ||
d5b0d1b5 | 627 | list_add_tail(&vbl_swap->head, &dev_priv->vbl_swaps.head); |
a6b54f3f MD |
628 | dev_priv->swaps_pending++; |
629 | ||
630 | spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); | |
631 | ||
632 | return 0; | |
633 | } | |
634 | ||
1da177e4 LT |
635 | /* drm_dma.h hooks |
636 | */ | |
84b1fd10 | 637 | void i915_driver_irq_preinstall(struct drm_device * dev) |
1da177e4 LT |
638 | { |
639 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | |
640 | ||
ed4cb414 EA |
641 | I915_WRITE(HWSTAM, 0xfffe); |
642 | I915_WRITE(IMR, 0x0); | |
643 | I915_WRITE(IER, 0x0); | |
1da177e4 LT |
644 | } |
645 | ||
af6061af | 646 | void i915_driver_irq_postinstall(struct drm_device * dev) |
1da177e4 LT |
647 | { |
648 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | |
649 | ||
a6399bdd | 650 | spin_lock_init(&dev_priv->swaps_lock); |
a6b54f3f MD |
651 | INIT_LIST_HEAD(&dev_priv->vbl_swaps.head); |
652 | dev_priv->swaps_pending = 0; | |
653 | ||
af6061af DA |
654 | if (!dev_priv->vblank_pipe) |
655 | dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A; | |
ed4cb414 EA |
656 | |
657 | /* Set initial unmasked IRQs to just the selected vblank pipes. */ | |
658 | dev_priv->irq_mask_reg = ~0; | |
659 | if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_A) | |
660 | dev_priv->irq_mask_reg &= ~I915_DISPLAY_PIPE_A_VBLANK_INTERRUPT; | |
661 | if (dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B) | |
662 | dev_priv->irq_mask_reg &= ~I915_DISPLAY_PIPE_B_VBLANK_INTERRUPT; | |
663 | ||
664 | I915_WRITE(IMR, dev_priv->irq_mask_reg); | |
665 | I915_WRITE(IER, I915_INTERRUPT_ENABLE_MASK); | |
666 | (void) I915_READ(IER); | |
667 | ||
1da177e4 LT |
668 | DRM_INIT_WAITQUEUE(&dev_priv->irq_queue); |
669 | } | |
670 | ||
84b1fd10 | 671 | void i915_driver_irq_uninstall(struct drm_device * dev) |
1da177e4 LT |
672 | { |
673 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | |
af6061af | 674 | u16 temp; |
91e3738e | 675 | |
1da177e4 LT |
676 | if (!dev_priv) |
677 | return; | |
678 | ||
ed4cb414 EA |
679 | I915_WRITE(HWSTAM, 0xffff); |
680 | I915_WRITE(IMR, 0xffff); | |
681 | I915_WRITE(IER, 0x0); | |
af6061af | 682 | |
ed4cb414 EA |
683 | temp = I915_READ(IIR); |
684 | I915_WRITE(IIR, temp); | |
1da177e4 | 685 | } |