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