diff options
author | =?utf-8?q?Michel_D=C3=A4nzer?= <michel@tungstengraphics.com> | 2006-10-24 09:37:43 -0400 |
---|---|---|
committer | airlied <airlied@linux.ie> | 2006-12-06 23:53:29 -0500 |
commit | a6b54f3f5050c0cbc0c35dd48064846c6302706b (patch) | |
tree | ed0a17808058150916a56120a10109d70a2dd426 /drivers/char/drm | |
parent | 049b323321bbcb476b799f50dc6444c0ed5a0e0e (diff) |
drm: i915: Add ioctl for scheduling buffer swaps at vertical blanks.
This uses the core facility to schedule a driver callback that will be called
ASAP after the given vertical blank interrupt with the HW lock held.
Signed-off-by: Dave Airlie <airlied@linux.ie>
Diffstat (limited to 'drivers/char/drm')
-rw-r--r-- | drivers/char/drm/i915_dma.c | 2 | ||||
-rw-r--r-- | drivers/char/drm/i915_drm.h | 9 | ||||
-rw-r--r-- | drivers/char/drm/i915_drv.h | 17 | ||||
-rw-r--r-- | drivers/char/drm/i915_irq.c | 183 |
4 files changed, 211 insertions, 0 deletions
diff --git a/drivers/char/drm/i915_dma.c b/drivers/char/drm/i915_dma.c index fb7913ff5286..9354ce3b0093 100644 --- a/drivers/char/drm/i915_dma.c +++ b/drivers/char/drm/i915_dma.c | |||
@@ -162,6 +162,7 @@ static int i915_initialize(drm_device_t * dev, | |||
162 | 162 | ||
163 | dev_priv->ring.virtual_start = dev_priv->ring.map.handle; | 163 | dev_priv->ring.virtual_start = dev_priv->ring.map.handle; |
164 | 164 | ||
165 | dev_priv->cpp = init->cpp; | ||
165 | dev_priv->back_offset = init->back_offset; | 166 | dev_priv->back_offset = init->back_offset; |
166 | dev_priv->front_offset = init->front_offset; | 167 | dev_priv->front_offset = init->front_offset; |
167 | dev_priv->current_page = 0; | 168 | dev_priv->current_page = 0; |
@@ -782,6 +783,7 @@ drm_ioctl_desc_t i915_ioctls[] = { | |||
782 | [DRM_IOCTL_NR(DRM_I915_DESTROY_HEAP)] = { i915_mem_destroy_heap, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY }, | 783 | [DRM_IOCTL_NR(DRM_I915_DESTROY_HEAP)] = { i915_mem_destroy_heap, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY }, |
783 | [DRM_IOCTL_NR(DRM_I915_SET_VBLANK_PIPE)] = { i915_vblank_pipe_set, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY }, | 784 | [DRM_IOCTL_NR(DRM_I915_SET_VBLANK_PIPE)] = { i915_vblank_pipe_set, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY }, |
784 | [DRM_IOCTL_NR(DRM_I915_GET_VBLANK_PIPE)] = { i915_vblank_pipe_get, DRM_AUTH }, | 785 | [DRM_IOCTL_NR(DRM_I915_GET_VBLANK_PIPE)] = { i915_vblank_pipe_get, DRM_AUTH }, |
786 | [DRM_IOCTL_NR(DRM_I915_VBLANK_SWAP)] = {i915_vblank_swap, DRM_AUTH}, | ||
785 | }; | 787 | }; |
786 | 788 | ||
787 | int i915_max_ioctl = DRM_ARRAY_SIZE(i915_ioctls); | 789 | int i915_max_ioctl = DRM_ARRAY_SIZE(i915_ioctls); |
diff --git a/drivers/char/drm/i915_drm.h b/drivers/char/drm/i915_drm.h index 6af83e613f27..8926beb5a61f 100644 --- a/drivers/char/drm/i915_drm.h +++ b/drivers/char/drm/i915_drm.h | |||
@@ -132,6 +132,7 @@ typedef struct _drm_i915_sarea { | |||
132 | #define DRM_I915_DESTROY_HEAP 0x0c | 132 | #define DRM_I915_DESTROY_HEAP 0x0c |
133 | #define DRM_I915_SET_VBLANK_PIPE 0x0d | 133 | #define DRM_I915_SET_VBLANK_PIPE 0x0d |
134 | #define DRM_I915_GET_VBLANK_PIPE 0x0e | 134 | #define DRM_I915_GET_VBLANK_PIPE 0x0e |
135 | #define DRM_I915_VBLANK_SWAP 0x0f | ||
135 | 136 | ||
136 | #define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t) | 137 | #define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t) |
137 | #define DRM_IOCTL_I915_FLUSH DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH) | 138 | #define DRM_IOCTL_I915_FLUSH DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH) |
@@ -243,4 +244,12 @@ typedef struct drm_i915_vblank_pipe { | |||
243 | int pipe; | 244 | int pipe; |
244 | } drm_i915_vblank_pipe_t; | 245 | } drm_i915_vblank_pipe_t; |
245 | 246 | ||
247 | /* Schedule buffer swap at given vertical blank: | ||
248 | */ | ||
249 | typedef struct drm_i915_vblank_swap { | ||
250 | drm_drawable_t drawable; | ||
251 | unsigned int pipe; | ||
252 | unsigned int sequence; | ||
253 | } drm_i915_vblank_swap_t; | ||
254 | |||
246 | #endif /* _I915_DRM_H_ */ | 255 | #endif /* _I915_DRM_H_ */ |
diff --git a/drivers/char/drm/i915_drv.h b/drivers/char/drm/i915_drv.h index 5f0c4fa04da2..334b0ce8181d 100644 --- a/drivers/char/drm/i915_drv.h +++ b/drivers/char/drm/i915_drv.h | |||
@@ -71,6 +71,13 @@ struct mem_block { | |||
71 | DRMFILE filp; /* 0: free, -1: heap, other: real files */ | 71 | DRMFILE filp; /* 0: free, -1: heap, other: real files */ |
72 | }; | 72 | }; |
73 | 73 | ||
74 | typedef struct _drm_i915_vbl_swap { | ||
75 | struct list_head head; | ||
76 | drm_drawable_t drw_id; | ||
77 | unsigned int pipe; | ||
78 | unsigned int sequence; | ||
79 | } drm_i915_vbl_swap_t; | ||
80 | |||
74 | typedef struct drm_i915_private { | 81 | typedef struct drm_i915_private { |
75 | drm_local_map_t *sarea; | 82 | drm_local_map_t *sarea; |
76 | drm_local_map_t *mmio_map; | 83 | drm_local_map_t *mmio_map; |
@@ -83,6 +90,7 @@ typedef struct drm_i915_private { | |||
83 | dma_addr_t dma_status_page; | 90 | dma_addr_t dma_status_page; |
84 | unsigned long counter; | 91 | unsigned long counter; |
85 | 92 | ||
93 | unsigned int cpp; | ||
86 | int back_offset; | 94 | int back_offset; |
87 | int front_offset; | 95 | int front_offset; |
88 | int current_page; | 96 | int current_page; |
@@ -98,6 +106,10 @@ typedef struct drm_i915_private { | |||
98 | struct mem_block *agp_heap; | 106 | struct mem_block *agp_heap; |
99 | unsigned int sr01, adpa, ppcr, dvob, dvoc, lvds; | 107 | unsigned int sr01, adpa, ppcr, dvob, dvoc, lvds; |
100 | int vblank_pipe; | 108 | int vblank_pipe; |
109 | |||
110 | spinlock_t swaps_lock; | ||
111 | drm_i915_vbl_swap_t vbl_swaps; | ||
112 | unsigned int swaps_pending; | ||
101 | } drm_i915_private_t; | 113 | } drm_i915_private_t; |
102 | 114 | ||
103 | extern drm_ioctl_desc_t i915_ioctls[]; | 115 | extern drm_ioctl_desc_t i915_ioctls[]; |
@@ -124,6 +136,7 @@ extern void i915_driver_irq_postinstall(drm_device_t * dev); | |||
124 | extern void i915_driver_irq_uninstall(drm_device_t * dev); | 136 | extern void i915_driver_irq_uninstall(drm_device_t * dev); |
125 | extern int i915_vblank_pipe_set(DRM_IOCTL_ARGS); | 137 | extern int i915_vblank_pipe_set(DRM_IOCTL_ARGS); |
126 | extern int i915_vblank_pipe_get(DRM_IOCTL_ARGS); | 138 | extern int i915_vblank_pipe_get(DRM_IOCTL_ARGS); |
139 | extern int i915_vblank_swap(DRM_IOCTL_ARGS); | ||
127 | 140 | ||
128 | /* i915_mem.c */ | 141 | /* i915_mem.c */ |
129 | extern int i915_mem_alloc(DRM_IOCTL_ARGS); | 142 | extern int i915_mem_alloc(DRM_IOCTL_ARGS); |
@@ -257,6 +270,10 @@ extern int i915_wait_ring(drm_device_t * dev, int n, const char *caller); | |||
257 | 270 | ||
258 | #define GFX_OP_DRAWRECT_INFO_I965 ((0x7900<<16)|0x2) | 271 | #define GFX_OP_DRAWRECT_INFO_I965 ((0x7900<<16)|0x2) |
259 | 272 | ||
273 | #define XY_SRC_COPY_BLT_CMD ((2<<29)|(0x53<<22)|6) | ||
274 | #define XY_SRC_COPY_BLT_WRITE_ALPHA (1<<21) | ||
275 | #define XY_SRC_COPY_BLT_WRITE_RGB (1<<20) | ||
276 | |||
260 | #define MI_BATCH_BUFFER ((0x30<<23)|1) | 277 | #define MI_BATCH_BUFFER ((0x30<<23)|1) |
261 | #define MI_BATCH_BUFFER_START (0x31<<23) | 278 | #define MI_BATCH_BUFFER_START (0x31<<23) |
262 | #define MI_BATCH_BUFFER_END (0xA<<23) | 279 | #define MI_BATCH_BUFFER_END (0xA<<23) |
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 | } |