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