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