diff options
Diffstat (limited to 'drivers/char/drm/i915_irq.c')
-rw-r--r-- | drivers/char/drm/i915_irq.c | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/drivers/char/drm/i915_irq.c b/drivers/char/drm/i915_irq.c index 33d40187696a..a93f1f37ec6a 100644 --- a/drivers/char/drm/i915_irq.c +++ b/drivers/char/drm/i915_irq.c | |||
@@ -37,6 +37,99 @@ | |||
37 | 37 | ||
38 | #define MAX_NOPID ((u32)~0) | 38 | #define MAX_NOPID ((u32)~0) |
39 | 39 | ||
40 | /** | ||
41 | * Emit blits for scheduled buffer swaps. | ||
42 | * | ||
43 | * This function will be called with the HW lock held. | ||
44 | */ | ||
45 | static void i915_vblank_tasklet(drm_device_t *dev) | ||
46 | { | ||
47 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | ||
48 | unsigned int irqflags; | ||
49 | struct list_head *list, *tmp; | ||
50 | |||
51 | DRM_DEBUG("\n"); | ||
52 | |||
53 | spin_lock_irqsave(&dev_priv->swaps_lock, irqflags); | ||
54 | |||
55 | list_for_each_safe(list, tmp, &dev_priv->vbl_swaps.head) { | ||
56 | drm_i915_vbl_swap_t *vbl_swap = | ||
57 | list_entry(list, drm_i915_vbl_swap_t, head); | ||
58 | atomic_t *counter = vbl_swap->pipe ? &dev->vbl_received2 : | ||
59 | &dev->vbl_received; | ||
60 | |||
61 | if ((atomic_read(counter) - vbl_swap->sequence) <= (1<<23)) { | ||
62 | drm_drawable_info_t *drw; | ||
63 | |||
64 | spin_unlock(&dev_priv->swaps_lock); | ||
65 | |||
66 | spin_lock(&dev->drw_lock); | ||
67 | |||
68 | drw = drm_get_drawable_info(dev, vbl_swap->drw_id); | ||
69 | |||
70 | if (drw) { | ||
71 | int i, num_rects = drw->num_rects; | ||
72 | drm_clip_rect_t *rect = drw->rects; | ||
73 | drm_i915_sarea_t *sarea_priv = | ||
74 | dev_priv->sarea_priv; | ||
75 | u32 cpp = dev_priv->cpp; | ||
76 | u32 cmd = (cpp == 4) ? (XY_SRC_COPY_BLT_CMD | | ||
77 | XY_SRC_COPY_BLT_WRITE_ALPHA | | ||
78 | XY_SRC_COPY_BLT_WRITE_RGB) | ||
79 | : XY_SRC_COPY_BLT_CMD; | ||
80 | u32 pitchropcpp = (sarea_priv->pitch * cpp) | | ||
81 | (0xcc << 16) | (cpp << 23) | | ||
82 | (1 << 24); | ||
83 | RING_LOCALS; | ||
84 | |||
85 | i915_kernel_lost_context(dev); | ||
86 | |||
87 | BEGIN_LP_RING(6); | ||
88 | |||
89 | OUT_RING(GFX_OP_DRAWRECT_INFO); | ||
90 | OUT_RING(0); | ||
91 | OUT_RING(0); | ||
92 | OUT_RING(sarea_priv->width | | ||
93 | sarea_priv->height << 16); | ||
94 | OUT_RING(sarea_priv->width | | ||
95 | sarea_priv->height << 16); | ||
96 | OUT_RING(0); | ||
97 | |||
98 | ADVANCE_LP_RING(); | ||
99 | |||
100 | sarea_priv->ctxOwner = DRM_KERNEL_CONTEXT; | ||
101 | |||
102 | for (i = 0; i < num_rects; i++, rect++) { | ||
103 | BEGIN_LP_RING(8); | ||
104 | |||
105 | OUT_RING(cmd); | ||
106 | OUT_RING(pitchropcpp); | ||
107 | OUT_RING((rect->y1 << 16) | rect->x1); | ||
108 | OUT_RING((rect->y2 << 16) | rect->x2); | ||
109 | OUT_RING(sarea_priv->front_offset); | ||
110 | OUT_RING((rect->y1 << 16) | rect->x1); | ||
111 | OUT_RING(pitchropcpp & 0xffff); | ||
112 | OUT_RING(sarea_priv->back_offset); | ||
113 | |||
114 | ADVANCE_LP_RING(); | ||
115 | } | ||
116 | } | ||
117 | |||
118 | spin_unlock(&dev->drw_lock); | ||
119 | |||
120 | spin_lock(&dev_priv->swaps_lock); | ||
121 | |||
122 | list_del(list); | ||
123 | |||
124 | drm_free(vbl_swap, sizeof(*vbl_swap), DRM_MEM_DRIVER); | ||
125 | |||
126 | dev_priv->swaps_pending--; | ||
127 | } | ||
128 | } | ||
129 | |||
130 | spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); | ||
131 | } | ||
132 | |||
40 | irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) | 133 | irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) |
41 | { | 134 | { |
42 | drm_device_t *dev = (drm_device_t *) arg; | 135 | drm_device_t *dev = (drm_device_t *) arg; |
@@ -72,6 +165,8 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) | |||
72 | 165 | ||
73 | DRM_WAKEUP(&dev->vbl_queue); | 166 | DRM_WAKEUP(&dev->vbl_queue); |
74 | drm_vbl_send_signals(dev); | 167 | drm_vbl_send_signals(dev); |
168 | |||
169 | drm_locked_tasklet(dev, i915_vblank_tasklet); | ||
75 | } | 170 | } |
76 | 171 | ||
77 | return IRQ_HANDLED; | 172 | return IRQ_HANDLED; |
@@ -271,6 +366,90 @@ int i915_vblank_pipe_get(DRM_IOCTL_ARGS) | |||
271 | return 0; | 366 | return 0; |
272 | } | 367 | } |
273 | 368 | ||
369 | /** | ||
370 | * Schedule buffer swap at given vertical blank. | ||
371 | */ | ||
372 | int i915_vblank_swap(DRM_IOCTL_ARGS) | ||
373 | { | ||
374 | DRM_DEVICE; | ||
375 | drm_i915_private_t *dev_priv = dev->dev_private; | ||
376 | drm_i915_vblank_swap_t swap; | ||
377 | drm_i915_vbl_swap_t *vbl_swap; | ||
378 | unsigned int irqflags; | ||
379 | struct list_head *list; | ||
380 | |||
381 | if (!dev_priv) { | ||
382 | DRM_ERROR("%s called with no initialization\n", __func__); | ||
383 | return DRM_ERR(EINVAL); | ||
384 | } | ||
385 | |||
386 | if (dev_priv->sarea_priv->rotation) { | ||
387 | DRM_DEBUG("Rotation not supported\n"); | ||
388 | return DRM_ERR(EINVAL); | ||
389 | } | ||
390 | |||
391 | if (dev_priv->swaps_pending >= 100) { | ||
392 | DRM_DEBUG("Too many swaps queued\n"); | ||
393 | return DRM_ERR(EBUSY); | ||
394 | } | ||
395 | |||
396 | DRM_COPY_FROM_USER_IOCTL(swap, (drm_i915_vblank_swap_t __user *) data, | ||
397 | sizeof(swap)); | ||
398 | |||
399 | if (swap.pipe > 1 || !(dev_priv->vblank_pipe & (1 << swap.pipe))) { | ||
400 | DRM_ERROR("Invalid pipe %d\n", swap.pipe); | ||
401 | return DRM_ERR(EINVAL); | ||
402 | } | ||
403 | |||
404 | spin_lock_irqsave(&dev->drw_lock, irqflags); | ||
405 | |||
406 | if (!drm_get_drawable_info(dev, swap.drawable)) { | ||
407 | spin_unlock_irqrestore(&dev->drw_lock, irqflags); | ||
408 | DRM_ERROR("Invalid drawable ID %d\n", swap.drawable); | ||
409 | return DRM_ERR(EINVAL); | ||
410 | } | ||
411 | |||
412 | spin_unlock_irqrestore(&dev->drw_lock, irqflags); | ||
413 | |||
414 | spin_lock_irqsave(&dev_priv->swaps_lock, irqflags); | ||
415 | |||
416 | list_for_each(list, &dev_priv->vbl_swaps.head) { | ||
417 | vbl_swap = list_entry(list, drm_i915_vbl_swap_t, head); | ||
418 | |||
419 | if (vbl_swap->drw_id == swap.drawable && | ||
420 | vbl_swap->pipe == swap.pipe && | ||
421 | vbl_swap->sequence == swap.sequence) { | ||
422 | spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); | ||
423 | DRM_DEBUG("Already scheduled\n"); | ||
424 | return 0; | ||
425 | } | ||
426 | } | ||
427 | |||
428 | spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); | ||
429 | |||
430 | vbl_swap = drm_calloc(1, sizeof(vbl_swap), DRM_MEM_DRIVER); | ||
431 | |||
432 | if (!vbl_swap) { | ||
433 | DRM_ERROR("Failed to allocate memory to queue swap\n"); | ||
434 | return DRM_ERR(ENOMEM); | ||
435 | } | ||
436 | |||
437 | DRM_DEBUG("\n"); | ||
438 | |||
439 | vbl_swap->drw_id = swap.drawable; | ||
440 | vbl_swap->pipe = swap.pipe; | ||
441 | vbl_swap->sequence = swap.sequence; | ||
442 | |||
443 | spin_lock_irqsave(&dev_priv->swaps_lock, irqflags); | ||
444 | |||
445 | list_add_tail((struct list_head *)vbl_swap, &dev_priv->vbl_swaps.head); | ||
446 | dev_priv->swaps_pending++; | ||
447 | |||
448 | spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); | ||
449 | |||
450 | return 0; | ||
451 | } | ||
452 | |||
274 | /* drm_dma.h hooks | 453 | /* drm_dma.h hooks |
275 | */ | 454 | */ |
276 | void i915_driver_irq_preinstall(drm_device_t * dev) | 455 | void i915_driver_irq_preinstall(drm_device_t * dev) |
@@ -286,6 +465,10 @@ void i915_driver_irq_postinstall(drm_device_t * dev) | |||
286 | { | 465 | { |
287 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | 466 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; |
288 | 467 | ||
468 | dev_priv->swaps_lock = SPIN_LOCK_UNLOCKED; | ||
469 | INIT_LIST_HEAD(&dev_priv->vbl_swaps.head); | ||
470 | dev_priv->swaps_pending = 0; | ||
471 | |||
289 | i915_enable_interrupt(dev); | 472 | i915_enable_interrupt(dev); |
290 | DRM_INIT_WAITQUEUE(&dev_priv->irq_queue); | 473 | DRM_INIT_WAITQUEUE(&dev_priv->irq_queue); |
291 | } | 474 | } |