diff options
author | Jesse Barnes <jbarnes@virtuousgeek.org> | 2008-04-22 02:03:07 -0400 |
---|---|---|
committer | Dave Airlie <airlied@linux.ie> | 2008-04-26 04:01:07 -0400 |
commit | ac741ab71bb39e6977694ac0cc26678d8673cda4 (patch) | |
tree | f82e08399a0da5accba930444744b269479185dd /drivers/char/drm/drm_irq.c | |
parent | 2c14f28be2a3f2a2e9861b156d64fbe2bc7000c3 (diff) |
drm/vbl rework: rework how the drm deals with vblank.
Other Authors: Michel Dänzer <michel@tungstengraphics.com>
mga: Ian Romanick <idr@us.ibm.com>
via: Thomas Hellstrom <thomas-at-tungstengraphics-dot-com>
This re-works the DRM internals to provide a better interface for drivers
to expose vblank on multiple crtcs.
It also includes work done by Michel on making i915 triple buffering and pageflipping work properly.
Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/char/drm/drm_irq.c')
-rw-r--r-- | drivers/char/drm/drm_irq.c | 381 |
1 files changed, 325 insertions, 56 deletions
diff --git a/drivers/char/drm/drm_irq.c b/drivers/char/drm/drm_irq.c index 089c015c01d1..286f9d61e7d5 100644 --- a/drivers/char/drm/drm_irq.c +++ b/drivers/char/drm/drm_irq.c | |||
@@ -71,6 +71,117 @@ int drm_irq_by_busid(struct drm_device *dev, void *data, | |||
71 | return 0; | 71 | return 0; |
72 | } | 72 | } |
73 | 73 | ||
74 | static void vblank_disable_fn(unsigned long arg) | ||
75 | { | ||
76 | struct drm_device *dev = (struct drm_device *)arg; | ||
77 | unsigned long irqflags; | ||
78 | int i; | ||
79 | |||
80 | for (i = 0; i < dev->num_crtcs; i++) { | ||
81 | spin_lock_irqsave(&dev->vbl_lock, irqflags); | ||
82 | if (atomic_read(&dev->vblank_refcount[i]) == 0 && | ||
83 | dev->vblank_enabled[i]) { | ||
84 | dev->driver->disable_vblank(dev, i); | ||
85 | dev->vblank_enabled[i] = 0; | ||
86 | } | ||
87 | spin_unlock_irqrestore(&dev->vbl_lock, irqflags); | ||
88 | } | ||
89 | } | ||
90 | |||
91 | static void drm_vblank_cleanup(struct drm_device *dev) | ||
92 | { | ||
93 | /* Bail if the driver didn't call drm_vblank_init() */ | ||
94 | if (dev->num_crtcs == 0) | ||
95 | return; | ||
96 | |||
97 | del_timer(&dev->vblank_disable_timer); | ||
98 | |||
99 | vblank_disable_fn((unsigned long)dev); | ||
100 | |||
101 | drm_free(dev->vbl_queue, sizeof(*dev->vbl_queue) * dev->num_crtcs, | ||
102 | DRM_MEM_DRIVER); | ||
103 | drm_free(dev->vbl_sigs, sizeof(*dev->vbl_sigs) * dev->num_crtcs, | ||
104 | DRM_MEM_DRIVER); | ||
105 | drm_free(dev->_vblank_count, sizeof(*dev->_vblank_count) * | ||
106 | dev->num_crtcs, DRM_MEM_DRIVER); | ||
107 | drm_free(dev->vblank_refcount, sizeof(*dev->vblank_refcount) * | ||
108 | dev->num_crtcs, DRM_MEM_DRIVER); | ||
109 | drm_free(dev->vblank_enabled, sizeof(*dev->vblank_enabled) * | ||
110 | dev->num_crtcs, DRM_MEM_DRIVER); | ||
111 | drm_free(dev->last_vblank, sizeof(*dev->last_vblank) * dev->num_crtcs, | ||
112 | DRM_MEM_DRIVER); | ||
113 | drm_free(dev->vblank_premodeset, sizeof(*dev->vblank_premodeset) * | ||
114 | dev->num_crtcs, DRM_MEM_DRIVER); | ||
115 | drm_free(dev->vblank_offset, sizeof(*dev->vblank_offset) * dev->num_crtcs, | ||
116 | DRM_MEM_DRIVER); | ||
117 | |||
118 | dev->num_crtcs = 0; | ||
119 | } | ||
120 | |||
121 | int drm_vblank_init(struct drm_device *dev, int num_crtcs) | ||
122 | { | ||
123 | int i, ret = -ENOMEM; | ||
124 | |||
125 | setup_timer(&dev->vblank_disable_timer, vblank_disable_fn, | ||
126 | (unsigned long)dev); | ||
127 | spin_lock_init(&dev->vbl_lock); | ||
128 | atomic_set(&dev->vbl_signal_pending, 0); | ||
129 | dev->num_crtcs = num_crtcs; | ||
130 | |||
131 | dev->vbl_queue = drm_alloc(sizeof(wait_queue_head_t) * num_crtcs, | ||
132 | DRM_MEM_DRIVER); | ||
133 | if (!dev->vbl_queue) | ||
134 | goto err; | ||
135 | |||
136 | dev->vbl_sigs = drm_alloc(sizeof(struct list_head) * num_crtcs, | ||
137 | DRM_MEM_DRIVER); | ||
138 | if (!dev->vbl_sigs) | ||
139 | goto err; | ||
140 | |||
141 | dev->_vblank_count = drm_alloc(sizeof(atomic_t) * num_crtcs, | ||
142 | DRM_MEM_DRIVER); | ||
143 | if (!dev->_vblank_count) | ||
144 | goto err; | ||
145 | |||
146 | dev->vblank_refcount = drm_alloc(sizeof(atomic_t) * num_crtcs, | ||
147 | DRM_MEM_DRIVER); | ||
148 | if (!dev->vblank_refcount) | ||
149 | goto err; | ||
150 | |||
151 | dev->vblank_enabled = drm_calloc(num_crtcs, sizeof(int), | ||
152 | DRM_MEM_DRIVER); | ||
153 | if (!dev->vblank_enabled) | ||
154 | goto err; | ||
155 | |||
156 | dev->last_vblank = drm_calloc(num_crtcs, sizeof(u32), DRM_MEM_DRIVER); | ||
157 | if (!dev->last_vblank) | ||
158 | goto err; | ||
159 | |||
160 | dev->vblank_premodeset = drm_calloc(num_crtcs, sizeof(u32), | ||
161 | DRM_MEM_DRIVER); | ||
162 | if (!dev->vblank_premodeset) | ||
163 | goto err; | ||
164 | |||
165 | dev->vblank_offset = drm_calloc(num_crtcs, sizeof(u32), DRM_MEM_DRIVER); | ||
166 | if (!dev->vblank_offset) | ||
167 | goto err; | ||
168 | |||
169 | /* Zero per-crtc vblank stuff */ | ||
170 | for (i = 0; i < num_crtcs; i++) { | ||
171 | init_waitqueue_head(&dev->vbl_queue[i]); | ||
172 | INIT_LIST_HEAD(&dev->vbl_sigs[i]); | ||
173 | atomic_set(&dev->_vblank_count[i], 0); | ||
174 | atomic_set(&dev->vblank_refcount[i], 0); | ||
175 | } | ||
176 | |||
177 | return 0; | ||
178 | |||
179 | err: | ||
180 | drm_vblank_cleanup(dev); | ||
181 | return ret; | ||
182 | } | ||
183 | EXPORT_SYMBOL(drm_vblank_init); | ||
184 | |||
74 | /** | 185 | /** |
75 | * Install IRQ handler. | 186 | * Install IRQ handler. |
76 | * | 187 | * |
@@ -109,17 +220,6 @@ static int drm_irq_install(struct drm_device * dev) | |||
109 | 220 | ||
110 | DRM_DEBUG("irq=%d\n", dev->irq); | 221 | DRM_DEBUG("irq=%d\n", dev->irq); |
111 | 222 | ||
112 | if (drm_core_check_feature(dev, DRIVER_IRQ_VBL)) { | ||
113 | init_waitqueue_head(&dev->vbl_queue); | ||
114 | |||
115 | spin_lock_init(&dev->vbl_lock); | ||
116 | |||
117 | INIT_LIST_HEAD(&dev->vbl_sigs); | ||
118 | INIT_LIST_HEAD(&dev->vbl_sigs2); | ||
119 | |||
120 | dev->vbl_pending = 0; | ||
121 | } | ||
122 | |||
123 | /* Before installing handler */ | 223 | /* Before installing handler */ |
124 | dev->driver->irq_preinstall(dev); | 224 | dev->driver->irq_preinstall(dev); |
125 | 225 | ||
@@ -137,9 +237,14 @@ static int drm_irq_install(struct drm_device * dev) | |||
137 | } | 237 | } |
138 | 238 | ||
139 | /* After installing handler */ | 239 | /* After installing handler */ |
140 | dev->driver->irq_postinstall(dev); | 240 | ret = dev->driver->irq_postinstall(dev); |
241 | if (ret < 0) { | ||
242 | mutex_lock(&dev->struct_mutex); | ||
243 | dev->irq_enabled = 0; | ||
244 | mutex_unlock(&dev->struct_mutex); | ||
245 | } | ||
141 | 246 | ||
142 | return 0; | 247 | return ret; |
143 | } | 248 | } |
144 | 249 | ||
145 | /** | 250 | /** |
@@ -170,6 +275,8 @@ int drm_irq_uninstall(struct drm_device * dev) | |||
170 | 275 | ||
171 | free_irq(dev->irq, dev); | 276 | free_irq(dev->irq, dev); |
172 | 277 | ||
278 | drm_vblank_cleanup(dev); | ||
279 | |||
173 | dev->locked_tasklet_func = NULL; | 280 | dev->locked_tasklet_func = NULL; |
174 | 281 | ||
175 | return 0; | 282 | return 0; |
@@ -214,6 +321,148 @@ int drm_control(struct drm_device *dev, void *data, | |||
214 | } | 321 | } |
215 | 322 | ||
216 | /** | 323 | /** |
324 | * drm_vblank_count - retrieve "cooked" vblank counter value | ||
325 | * @dev: DRM device | ||
326 | * @crtc: which counter to retrieve | ||
327 | * | ||
328 | * Fetches the "cooked" vblank count value that represents the number of | ||
329 | * vblank events since the system was booted, including lost events due to | ||
330 | * modesetting activity. | ||
331 | */ | ||
332 | u32 drm_vblank_count(struct drm_device *dev, int crtc) | ||
333 | { | ||
334 | return atomic_read(&dev->_vblank_count[crtc]) + | ||
335 | dev->vblank_offset[crtc]; | ||
336 | } | ||
337 | EXPORT_SYMBOL(drm_vblank_count); | ||
338 | |||
339 | /** | ||
340 | * drm_update_vblank_count - update the master vblank counter | ||
341 | * @dev: DRM device | ||
342 | * @crtc: counter to update | ||
343 | * | ||
344 | * Call back into the driver to update the appropriate vblank counter | ||
345 | * (specified by @crtc). Deal with wraparound, if it occurred, and | ||
346 | * update the last read value so we can deal with wraparound on the next | ||
347 | * call if necessary. | ||
348 | */ | ||
349 | void drm_update_vblank_count(struct drm_device *dev, int crtc) | ||
350 | { | ||
351 | unsigned long irqflags; | ||
352 | u32 cur_vblank, diff; | ||
353 | |||
354 | /* | ||
355 | * Interrupts were disabled prior to this call, so deal with counter | ||
356 | * wrap if needed. | ||
357 | * NOTE! It's possible we lost a full dev->max_vblank_count events | ||
358 | * here if the register is small or we had vblank interrupts off for | ||
359 | * a long time. | ||
360 | */ | ||
361 | cur_vblank = dev->driver->get_vblank_counter(dev, crtc); | ||
362 | spin_lock_irqsave(&dev->vbl_lock, irqflags); | ||
363 | if (cur_vblank < dev->last_vblank[crtc]) { | ||
364 | diff = dev->max_vblank_count - | ||
365 | dev->last_vblank[crtc]; | ||
366 | diff += cur_vblank; | ||
367 | } else { | ||
368 | diff = cur_vblank - dev->last_vblank[crtc]; | ||
369 | } | ||
370 | dev->last_vblank[crtc] = cur_vblank; | ||
371 | spin_unlock_irqrestore(&dev->vbl_lock, irqflags); | ||
372 | |||
373 | atomic_add(diff, &dev->_vblank_count[crtc]); | ||
374 | } | ||
375 | EXPORT_SYMBOL(drm_update_vblank_count); | ||
376 | |||
377 | /** | ||
378 | * drm_vblank_get - get a reference count on vblank events | ||
379 | * @dev: DRM device | ||
380 | * @crtc: which CRTC to own | ||
381 | * | ||
382 | * Acquire a reference count on vblank events to avoid having them disabled | ||
383 | * while in use. Note callers will probably want to update the master counter | ||
384 | * using drm_update_vblank_count() above before calling this routine so that | ||
385 | * wakeups occur on the right vblank event. | ||
386 | * | ||
387 | * RETURNS | ||
388 | * Zero on success, nonzero on failure. | ||
389 | */ | ||
390 | int drm_vblank_get(struct drm_device *dev, int crtc) | ||
391 | { | ||
392 | unsigned long irqflags; | ||
393 | int ret = 0; | ||
394 | |||
395 | spin_lock_irqsave(&dev->vbl_lock, irqflags); | ||
396 | /* Going from 0->1 means we have to enable interrupts again */ | ||
397 | if (atomic_add_return(1, &dev->vblank_refcount[crtc]) == 1 && | ||
398 | !dev->vblank_enabled[crtc]) { | ||
399 | ret = dev->driver->enable_vblank(dev, crtc); | ||
400 | if (ret) | ||
401 | atomic_dec(&dev->vblank_refcount[crtc]); | ||
402 | else | ||
403 | dev->vblank_enabled[crtc] = 1; | ||
404 | } | ||
405 | spin_unlock_irqrestore(&dev->vbl_lock, irqflags); | ||
406 | |||
407 | return ret; | ||
408 | } | ||
409 | EXPORT_SYMBOL(drm_vblank_get); | ||
410 | |||
411 | /** | ||
412 | * drm_vblank_put - give up ownership of vblank events | ||
413 | * @dev: DRM device | ||
414 | * @crtc: which counter to give up | ||
415 | * | ||
416 | * Release ownership of a given vblank counter, turning off interrupts | ||
417 | * if possible. | ||
418 | */ | ||
419 | void drm_vblank_put(struct drm_device *dev, int crtc) | ||
420 | { | ||
421 | /* Last user schedules interrupt disable */ | ||
422 | if (atomic_dec_and_test(&dev->vblank_refcount[crtc])) | ||
423 | mod_timer(&dev->vblank_disable_timer, jiffies + 5*DRM_HZ); | ||
424 | } | ||
425 | EXPORT_SYMBOL(drm_vblank_put); | ||
426 | |||
427 | /** | ||
428 | * drm_modeset_ctl - handle vblank event counter changes across mode switch | ||
429 | * @DRM_IOCTL_ARGS: standard ioctl arguments | ||
430 | * | ||
431 | * Applications should call the %_DRM_PRE_MODESET and %_DRM_POST_MODESET | ||
432 | * ioctls around modesetting so that any lost vblank events are accounted for. | ||
433 | */ | ||
434 | int drm_modeset_ctl(struct drm_device *dev, void *data, | ||
435 | struct drm_file *file_priv) | ||
436 | { | ||
437 | struct drm_modeset_ctl *modeset = data; | ||
438 | int crtc, ret = 0; | ||
439 | u32 new; | ||
440 | |||
441 | crtc = modeset->arg; | ||
442 | if (crtc >= dev->num_crtcs) { | ||
443 | ret = -EINVAL; | ||
444 | goto out; | ||
445 | } | ||
446 | |||
447 | switch (modeset->cmd) { | ||
448 | case _DRM_PRE_MODESET: | ||
449 | dev->vblank_premodeset[crtc] = | ||
450 | dev->driver->get_vblank_counter(dev, crtc); | ||
451 | break; | ||
452 | case _DRM_POST_MODESET: | ||
453 | new = dev->driver->get_vblank_counter(dev, crtc); | ||
454 | dev->vblank_offset[crtc] = dev->vblank_premodeset[crtc] - new; | ||
455 | break; | ||
456 | default: | ||
457 | ret = -EINVAL; | ||
458 | break; | ||
459 | } | ||
460 | |||
461 | out: | ||
462 | return ret; | ||
463 | } | ||
464 | |||
465 | /** | ||
217 | * Wait for VBLANK. | 466 | * Wait for VBLANK. |
218 | * | 467 | * |
219 | * \param inode device inode. | 468 | * \param inode device inode. |
@@ -232,12 +481,13 @@ int drm_control(struct drm_device *dev, void *data, | |||
232 | * | 481 | * |
233 | * If a signal is not requested, then calls vblank_wait(). | 482 | * If a signal is not requested, then calls vblank_wait(). |
234 | */ | 483 | */ |
235 | int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_priv) | 484 | int drm_wait_vblank(struct drm_device *dev, void *data, |
485 | struct drm_file *file_priv) | ||
236 | { | 486 | { |
237 | union drm_wait_vblank *vblwait = data; | 487 | union drm_wait_vblank *vblwait = data; |
238 | struct timeval now; | 488 | struct timeval now; |
239 | int ret = 0; | 489 | int ret = 0; |
240 | unsigned int flags, seq; | 490 | unsigned int flags, seq, crtc; |
241 | 491 | ||
242 | if ((!dev->irq) || (!dev->irq_enabled)) | 492 | if ((!dev->irq) || (!dev->irq_enabled)) |
243 | return -EINVAL; | 493 | return -EINVAL; |
@@ -251,13 +501,13 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr | |||
251 | } | 501 | } |
252 | 502 | ||
253 | flags = vblwait->request.type & _DRM_VBLANK_FLAGS_MASK; | 503 | flags = vblwait->request.type & _DRM_VBLANK_FLAGS_MASK; |
504 | crtc = flags & _DRM_VBLANK_SECONDARY ? 1 : 0; | ||
254 | 505 | ||
255 | if (!drm_core_check_feature(dev, (flags & _DRM_VBLANK_SECONDARY) ? | 506 | if (crtc >= dev->num_crtcs) |
256 | DRIVER_IRQ_VBL2 : DRIVER_IRQ_VBL)) | ||
257 | return -EINVAL; | 507 | return -EINVAL; |
258 | 508 | ||
259 | seq = atomic_read((flags & _DRM_VBLANK_SECONDARY) ? &dev->vbl_received2 | 509 | drm_update_vblank_count(dev, crtc); |
260 | : &dev->vbl_received); | 510 | seq = drm_vblank_count(dev, crtc); |
261 | 511 | ||
262 | switch (vblwait->request.type & _DRM_VBLANK_TYPES_MASK) { | 512 | switch (vblwait->request.type & _DRM_VBLANK_TYPES_MASK) { |
263 | case _DRM_VBLANK_RELATIVE: | 513 | case _DRM_VBLANK_RELATIVE: |
@@ -276,8 +526,7 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr | |||
276 | 526 | ||
277 | if (flags & _DRM_VBLANK_SIGNAL) { | 527 | if (flags & _DRM_VBLANK_SIGNAL) { |
278 | unsigned long irqflags; | 528 | unsigned long irqflags; |
279 | struct list_head *vbl_sigs = (flags & _DRM_VBLANK_SECONDARY) | 529 | struct list_head *vbl_sigs = &dev->vbl_sigs[crtc]; |
280 | ? &dev->vbl_sigs2 : &dev->vbl_sigs; | ||
281 | struct drm_vbl_sig *vbl_sig; | 530 | struct drm_vbl_sig *vbl_sig; |
282 | 531 | ||
283 | spin_lock_irqsave(&dev->vbl_lock, irqflags); | 532 | spin_lock_irqsave(&dev->vbl_lock, irqflags); |
@@ -298,22 +547,26 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr | |||
298 | } | 547 | } |
299 | } | 548 | } |
300 | 549 | ||
301 | if (dev->vbl_pending >= 100) { | 550 | if (atomic_read(&dev->vbl_signal_pending) >= 100) { |
302 | spin_unlock_irqrestore(&dev->vbl_lock, irqflags); | 551 | spin_unlock_irqrestore(&dev->vbl_lock, irqflags); |
303 | return -EBUSY; | 552 | return -EBUSY; |
304 | } | 553 | } |
305 | 554 | ||
306 | dev->vbl_pending++; | ||
307 | |||
308 | spin_unlock_irqrestore(&dev->vbl_lock, irqflags); | 555 | spin_unlock_irqrestore(&dev->vbl_lock, irqflags); |
309 | 556 | ||
310 | if (! | 557 | vbl_sig = drm_calloc(1, sizeof(struct drm_vbl_sig), |
311 | (vbl_sig = | 558 | DRM_MEM_DRIVER); |
312 | drm_alloc(sizeof(struct drm_vbl_sig), DRM_MEM_DRIVER))) { | 559 | if (!vbl_sig) |
313 | return -ENOMEM; | 560 | return -ENOMEM; |
561 | |||
562 | ret = drm_vblank_get(dev, crtc); | ||
563 | if (ret) { | ||
564 | drm_free(vbl_sig, sizeof(struct drm_vbl_sig), | ||
565 | DRM_MEM_DRIVER); | ||
566 | return ret; | ||
314 | } | 567 | } |
315 | 568 | ||
316 | memset((void *)vbl_sig, 0, sizeof(*vbl_sig)); | 569 | atomic_inc(&dev->vbl_signal_pending); |
317 | 570 | ||
318 | vbl_sig->sequence = vblwait->request.sequence; | 571 | vbl_sig->sequence = vblwait->request.sequence; |
319 | vbl_sig->info.si_signo = vblwait->request.signal; | 572 | vbl_sig->info.si_signo = vblwait->request.signal; |
@@ -327,17 +580,20 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr | |||
327 | 580 | ||
328 | vblwait->reply.sequence = seq; | 581 | vblwait->reply.sequence = seq; |
329 | } else { | 582 | } else { |
330 | if (flags & _DRM_VBLANK_SECONDARY) { | 583 | unsigned long cur_vblank; |
331 | if (dev->driver->vblank_wait2) | 584 | |
332 | ret = dev->driver->vblank_wait2(dev, &vblwait->request.sequence); | 585 | ret = drm_vblank_get(dev, crtc); |
333 | } else if (dev->driver->vblank_wait) | 586 | if (ret) |
334 | ret = | 587 | return ret; |
335 | dev->driver->vblank_wait(dev, | 588 | DRM_WAIT_ON(ret, dev->vbl_queue[crtc], 3 * DRM_HZ, |
336 | &vblwait->request.sequence); | 589 | (((cur_vblank = drm_vblank_count(dev, crtc)) |
337 | 590 | - vblwait->request.sequence) <= (1 << 23))); | |
591 | drm_vblank_put(dev, crtc); | ||
338 | do_gettimeofday(&now); | 592 | do_gettimeofday(&now); |
593 | |||
339 | vblwait->reply.tval_sec = now.tv_sec; | 594 | vblwait->reply.tval_sec = now.tv_sec; |
340 | vblwait->reply.tval_usec = now.tv_usec; | 595 | vblwait->reply.tval_usec = now.tv_usec; |
596 | vblwait->reply.sequence = cur_vblank; | ||
341 | } | 597 | } |
342 | 598 | ||
343 | done: | 599 | done: |
@@ -348,44 +604,57 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr | |||
348 | * Send the VBLANK signals. | 604 | * Send the VBLANK signals. |
349 | * | 605 | * |
350 | * \param dev DRM device. | 606 | * \param dev DRM device. |
607 | * \param crtc CRTC where the vblank event occurred | ||
351 | * | 608 | * |
352 | * Sends a signal for each task in drm_device::vbl_sigs and empties the list. | 609 | * Sends a signal for each task in drm_device::vbl_sigs and empties the list. |
353 | * | 610 | * |
354 | * If a signal is not requested, then calls vblank_wait(). | 611 | * If a signal is not requested, then calls vblank_wait(). |
355 | */ | 612 | */ |
356 | void drm_vbl_send_signals(struct drm_device * dev) | 613 | static void drm_vbl_send_signals(struct drm_device * dev, int crtc) |
357 | { | 614 | { |
615 | struct drm_vbl_sig *vbl_sig, *tmp; | ||
616 | struct list_head *vbl_sigs; | ||
617 | unsigned int vbl_seq; | ||
358 | unsigned long flags; | 618 | unsigned long flags; |
359 | int i; | ||
360 | 619 | ||
361 | spin_lock_irqsave(&dev->vbl_lock, flags); | 620 | spin_lock_irqsave(&dev->vbl_lock, flags); |
362 | 621 | ||
363 | for (i = 0; i < 2; i++) { | 622 | vbl_sigs = &dev->vbl_sigs[crtc]; |
364 | struct drm_vbl_sig *vbl_sig, *tmp; | 623 | vbl_seq = drm_vblank_count(dev, crtc); |
365 | struct list_head *vbl_sigs = i ? &dev->vbl_sigs2 : &dev->vbl_sigs; | ||
366 | unsigned int vbl_seq = atomic_read(i ? &dev->vbl_received2 : | ||
367 | &dev->vbl_received); | ||
368 | 624 | ||
369 | list_for_each_entry_safe(vbl_sig, tmp, vbl_sigs, head) { | 625 | list_for_each_entry_safe(vbl_sig, tmp, vbl_sigs, head) { |
370 | if ((vbl_seq - vbl_sig->sequence) <= (1 << 23)) { | 626 | if ((vbl_seq - vbl_sig->sequence) <= (1 << 23)) { |
371 | vbl_sig->info.si_code = vbl_seq; | 627 | vbl_sig->info.si_code = vbl_seq; |
372 | send_sig_info(vbl_sig->info.si_signo, | 628 | send_sig_info(vbl_sig->info.si_signo, |
373 | &vbl_sig->info, vbl_sig->task); | 629 | &vbl_sig->info, vbl_sig->task); |
374 | 630 | ||
375 | list_del(&vbl_sig->head); | 631 | list_del(&vbl_sig->head); |
376 | 632 | ||
377 | drm_free(vbl_sig, sizeof(*vbl_sig), | 633 | drm_free(vbl_sig, sizeof(*vbl_sig), |
378 | DRM_MEM_DRIVER); | 634 | DRM_MEM_DRIVER); |
379 | 635 | atomic_dec(&dev->vbl_signal_pending); | |
380 | dev->vbl_pending--; | 636 | drm_vblank_put(dev, crtc); |
381 | } | 637 | } |
382 | } | ||
383 | } | 638 | } |
384 | 639 | ||
385 | spin_unlock_irqrestore(&dev->vbl_lock, flags); | 640 | spin_unlock_irqrestore(&dev->vbl_lock, flags); |
386 | } | 641 | } |
387 | 642 | ||
388 | EXPORT_SYMBOL(drm_vbl_send_signals); | 643 | /** |
644 | * drm_handle_vblank - handle a vblank event | ||
645 | * @dev: DRM device | ||
646 | * @crtc: where this event occurred | ||
647 | * | ||
648 | * Drivers should call this routine in their vblank interrupt handlers to | ||
649 | * update the vblank counter and send any signals that may be pending. | ||
650 | */ | ||
651 | void drm_handle_vblank(struct drm_device *dev, int crtc) | ||
652 | { | ||
653 | drm_update_vblank_count(dev, crtc); | ||
654 | DRM_WAKEUP(&dev->vbl_queue[crtc]); | ||
655 | drm_vbl_send_signals(dev, crtc); | ||
656 | } | ||
657 | EXPORT_SYMBOL(drm_handle_vblank); | ||
389 | 658 | ||
390 | /** | 659 | /** |
391 | * Tasklet wrapper function. | 660 | * Tasklet wrapper function. |