diff options
author | Daniel Vetter <daniel.vetter@ffwll.ch> | 2009-09-15 16:57:37 -0400 |
---|---|---|
committer | Eric Anholt <eric@anholt.net> | 2009-11-05 17:47:09 -0500 |
commit | 03f77ea5972e6a2363152aec692744cac824daba (patch) | |
tree | ad63609747a62f22f2117c62bce38f17532f72ed /drivers/gpu/drm | |
parent | 5a5a0c64a99d7542c48c99d1a8bbb49e665842be (diff) |
drm/i915: implement interruptible sleeps in the overlay code
At least for the common case of userspace ioctls. When doing a
modeset operation, the wait is still uninterruptible. But considering
that failing to turn off the overlay when switching off the crtc it's
running on hangs the chip, it doesn't complicate matters _very_
much. There's just an unkillable X in addition to a black screen.
BUG() about it and explain in the code.
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Eric Anholt <eric@anholt.net>
Diffstat (limited to 'drivers/gpu/drm')
-rw-r--r-- | drivers/gpu/drm/i915/intel_display.c | 17 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/intel_drv.h | 9 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/intel_overlay.c | 167 |
3 files changed, 159 insertions, 34 deletions
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 6f818fadcbe3..7d3309bc0fd2 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c | |||
@@ -1784,11 +1784,26 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode) | |||
1784 | static void intel_crtc_dpms_overlay(struct intel_crtc *intel_crtc, bool enable) | 1784 | static void intel_crtc_dpms_overlay(struct intel_crtc *intel_crtc, bool enable) |
1785 | { | 1785 | { |
1786 | struct intel_overlay *overlay; | 1786 | struct intel_overlay *overlay; |
1787 | int ret; | ||
1787 | 1788 | ||
1788 | if (!enable && intel_crtc->overlay) { | 1789 | if (!enable && intel_crtc->overlay) { |
1789 | overlay = intel_crtc->overlay; | 1790 | overlay = intel_crtc->overlay; |
1790 | mutex_lock(&overlay->dev->struct_mutex); | 1791 | mutex_lock(&overlay->dev->struct_mutex); |
1791 | intel_overlay_switch_off(overlay); | 1792 | for (;;) { |
1793 | ret = intel_overlay_switch_off(overlay); | ||
1794 | if (ret == 0) | ||
1795 | break; | ||
1796 | |||
1797 | ret = intel_overlay_recover_from_interrupt(overlay, 0); | ||
1798 | if (ret != 0) { | ||
1799 | /* overlay doesn't react anymore. Usually | ||
1800 | * results in a black screen and an unkillable | ||
1801 | * X server. */ | ||
1802 | BUG(); | ||
1803 | overlay->hw_wedged = HW_WEDGED; | ||
1804 | break; | ||
1805 | } | ||
1806 | } | ||
1792 | mutex_unlock(&overlay->dev->struct_mutex); | 1807 | mutex_unlock(&overlay->dev->struct_mutex); |
1793 | } | 1808 | } |
1794 | /* Let userspace switch the overlay on again. In most cases userspace | 1809 | /* Let userspace switch the overlay on again. In most cases userspace |
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 5b503cb793ba..497240581c6a 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h | |||
@@ -127,8 +127,13 @@ struct intel_overlay { | |||
127 | struct drm_i915_gem_object *reg_bo; | 127 | struct drm_i915_gem_object *reg_bo; |
128 | void *virt_addr; | 128 | void *virt_addr; |
129 | /* flip handling */ | 129 | /* flip handling */ |
130 | int hw_wedged; | ||
131 | uint32_t last_flip_req; | 130 | uint32_t last_flip_req; |
131 | int hw_wedged; | ||
132 | #define HW_WEDGED 1 | ||
133 | #define NEEDS_WAIT_FOR_FLIP 2 | ||
134 | #define RELEASE_OLD_VID 3 | ||
135 | #define SWITCH_OFF_STAGE_1 4 | ||
136 | #define SWITCH_OFF_STAGE_2 5 | ||
132 | }; | 137 | }; |
133 | 138 | ||
134 | struct intel_crtc { | 139 | struct intel_crtc { |
@@ -209,6 +214,8 @@ extern int intel_framebuffer_create(struct drm_device *dev, | |||
209 | extern void intel_setup_overlay(struct drm_device *dev); | 214 | extern void intel_setup_overlay(struct drm_device *dev); |
210 | extern void intel_cleanup_overlay(struct drm_device *dev); | 215 | extern void intel_cleanup_overlay(struct drm_device *dev); |
211 | extern int intel_overlay_switch_off(struct intel_overlay *overlay); | 216 | extern int intel_overlay_switch_off(struct intel_overlay *overlay); |
217 | extern int intel_overlay_recover_from_interrupt(struct intel_overlay *overlay, | ||
218 | int interruptible); | ||
212 | extern int intel_overlay_put_image(struct drm_device *dev, void *data, | 219 | extern int intel_overlay_put_image(struct drm_device *dev, void *data, |
213 | struct drm_file *file_priv); | 220 | struct drm_file *file_priv); |
214 | extern int intel_overlay_attrs(struct drm_device *dev, void *data, | 221 | extern int intel_overlay_attrs(struct drm_device *dev, void *data, |
diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c index 85e07e4459ce..972d715245be 100644 --- a/drivers/gpu/drm/i915/intel_overlay.c +++ b/drivers/gpu/drm/i915/intel_overlay.c | |||
@@ -222,6 +222,9 @@ static int intel_overlay_on(struct intel_overlay *overlay) | |||
222 | 222 | ||
223 | BUG_ON(overlay->active); | 223 | BUG_ON(overlay->active); |
224 | 224 | ||
225 | overlay->active = 1; | ||
226 | overlay->hw_wedged = NEEDS_WAIT_FOR_FLIP; | ||
227 | |||
225 | BEGIN_LP_RING(6); | 228 | BEGIN_LP_RING(6); |
226 | OUT_RING(MI_FLUSH); | 229 | OUT_RING(MI_FLUSH); |
227 | OUT_RING(MI_NOOP); | 230 | OUT_RING(MI_NOOP); |
@@ -231,15 +234,16 @@ static int intel_overlay_on(struct intel_overlay *overlay) | |||
231 | OUT_RING(MI_NOOP); | 234 | OUT_RING(MI_NOOP); |
232 | ADVANCE_LP_RING(); | 235 | ADVANCE_LP_RING(); |
233 | 236 | ||
234 | ret = i915_lp_ring_sync(dev); | 237 | overlay->last_flip_req = i915_add_request(dev, NULL, 0); |
235 | if (ret != 0) { | 238 | if (overlay->last_flip_req == 0) |
236 | DRM_ERROR("intel overlay: ring sync failed, hw likely wedged\n"); | 239 | return -ENOMEM; |
237 | overlay->hw_wedged = 1; | ||
238 | return 0; | ||
239 | } | ||
240 | 240 | ||
241 | overlay->active = 1; | 241 | ret = i915_do_wait_request(dev, overlay->last_flip_req, 1); |
242 | if (ret != 0) | ||
243 | return ret; | ||
242 | 244 | ||
245 | overlay->hw_wedged = 0; | ||
246 | overlay->last_flip_req = 0; | ||
243 | return 0; | 247 | return 0; |
244 | } | 248 | } |
245 | 249 | ||
@@ -283,7 +287,6 @@ static int intel_overlay_wait_flip(struct intel_overlay *overlay) | |||
283 | 287 | ||
284 | if (overlay->last_flip_req != 0) { | 288 | if (overlay->last_flip_req != 0) { |
285 | ret = i915_do_wait_request(dev, overlay->last_flip_req, 0); | 289 | ret = i915_do_wait_request(dev, overlay->last_flip_req, 0); |
286 | |||
287 | if (ret != 0) | 290 | if (ret != 0) |
288 | return ret; | 291 | return ret; |
289 | 292 | ||
@@ -296,19 +299,24 @@ static int intel_overlay_wait_flip(struct intel_overlay *overlay) | |||
296 | } | 299 | } |
297 | 300 | ||
298 | /* synchronous slowpath */ | 301 | /* synchronous slowpath */ |
302 | overlay->hw_wedged = RELEASE_OLD_VID; | ||
303 | |||
299 | BEGIN_LP_RING(2); | 304 | BEGIN_LP_RING(2); |
300 | OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP); | 305 | OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP); |
301 | OUT_RING(MI_NOOP); | 306 | OUT_RING(MI_NOOP); |
302 | ADVANCE_LP_RING(); | 307 | ADVANCE_LP_RING(); |
303 | 308 | ||
304 | /* run in lockstep with the hw for easier testing */ | 309 | overlay->last_flip_req = i915_add_request(dev, NULL, 0); |
305 | ret = i915_lp_ring_sync(dev); | 310 | if (overlay->last_flip_req == 0) |
306 | if (ret != 0) { | 311 | return -ENOMEM; |
307 | DRM_ERROR("intel overlay: ring sync failed, hw likely wedged\n"); | ||
308 | overlay->hw_wedged = 1; | ||
309 | } | ||
310 | 312 | ||
311 | return ret; | 313 | ret = i915_do_wait_request(dev, overlay->last_flip_req, 1); |
314 | if (ret != 0) | ||
315 | return ret; | ||
316 | |||
317 | overlay->hw_wedged = 0; | ||
318 | overlay->last_flip_req = 0; | ||
319 | return 0; | ||
312 | } | 320 | } |
313 | 321 | ||
314 | /* overlay needs to be disabled in OCMD reg */ | 322 | /* overlay needs to be disabled in OCMD reg */ |
@@ -329,6 +337,8 @@ static int intel_overlay_off(struct intel_overlay *overlay) | |||
329 | flip_addr |= OFC_UPDATE; | 337 | flip_addr |= OFC_UPDATE; |
330 | 338 | ||
331 | /* wait for overlay to go idle */ | 339 | /* wait for overlay to go idle */ |
340 | overlay->hw_wedged = SWITCH_OFF_STAGE_1; | ||
341 | |||
332 | BEGIN_LP_RING(6); | 342 | BEGIN_LP_RING(6); |
333 | OUT_RING(MI_FLUSH); | 343 | OUT_RING(MI_FLUSH); |
334 | OUT_RING(MI_NOOP); | 344 | OUT_RING(MI_NOOP); |
@@ -338,14 +348,17 @@ static int intel_overlay_off(struct intel_overlay *overlay) | |||
338 | OUT_RING(MI_NOOP); | 348 | OUT_RING(MI_NOOP); |
339 | ADVANCE_LP_RING(); | 349 | ADVANCE_LP_RING(); |
340 | 350 | ||
341 | ret = i915_lp_ring_sync(dev); | 351 | overlay->last_flip_req = i915_add_request(dev, NULL, 0); |
342 | if (ret != 0) { | 352 | if (overlay->last_flip_req == 0) |
343 | DRM_ERROR("intel overlay: ring sync failed, hw likely wedged\n"); | 353 | return -ENOMEM; |
344 | overlay->hw_wedged = 1; | 354 | |
355 | ret = i915_do_wait_request(dev, overlay->last_flip_req, 1); | ||
356 | if (ret != 0) | ||
345 | return ret; | 357 | return ret; |
346 | } | ||
347 | 358 | ||
348 | /* turn overlay off */ | 359 | /* turn overlay off */ |
360 | overlay->hw_wedged = SWITCH_OFF_STAGE_2; | ||
361 | |||
349 | BEGIN_LP_RING(6); | 362 | BEGIN_LP_RING(6); |
350 | OUT_RING(MI_FLUSH); | 363 | OUT_RING(MI_FLUSH); |
351 | OUT_RING(MI_NOOP); | 364 | OUT_RING(MI_NOOP); |
@@ -355,18 +368,100 @@ static int intel_overlay_off(struct intel_overlay *overlay) | |||
355 | OUT_RING(MI_NOOP); | 368 | OUT_RING(MI_NOOP); |
356 | ADVANCE_LP_RING(); | 369 | ADVANCE_LP_RING(); |
357 | 370 | ||
358 | ret = i915_lp_ring_sync(dev); | 371 | overlay->last_flip_req = i915_add_request(dev, NULL, 0); |
359 | if (ret != 0) { | 372 | if (overlay->last_flip_req == 0) |
360 | DRM_ERROR("intel overlay: ring sync failed, hw likely wedged\n"); | 373 | return -ENOMEM; |
361 | overlay->hw_wedged = 1; | 374 | |
375 | ret = i915_do_wait_request(dev, overlay->last_flip_req, 1); | ||
376 | if (ret != 0) | ||
362 | return ret; | 377 | return ret; |
363 | } | ||
364 | 378 | ||
365 | overlay->active = 0; | 379 | overlay->active = 0; |
366 | 380 | overlay->hw_wedged = 0; | |
381 | overlay->last_flip_req = 0; | ||
367 | return ret; | 382 | return ret; |
368 | } | 383 | } |
369 | 384 | ||
385 | /* recover from an interruption due to a signal | ||
386 | * We have to be careful not to repeat work forever an make forward progess. */ | ||
387 | int intel_overlay_recover_from_interrupt(struct intel_overlay *overlay, | ||
388 | int interruptible) | ||
389 | { | ||
390 | struct drm_device *dev = overlay->dev; | ||
391 | drm_i915_private_t *dev_priv = dev->dev_private; | ||
392 | struct drm_gem_object *obj; | ||
393 | u32 flip_addr; | ||
394 | int ret; | ||
395 | RING_LOCALS; | ||
396 | |||
397 | if (overlay->hw_wedged == HW_WEDGED) | ||
398 | return -EIO; | ||
399 | |||
400 | if (overlay->last_flip_req == 0) { | ||
401 | overlay->last_flip_req = i915_add_request(dev, NULL, 0); | ||
402 | if (overlay->last_flip_req == 0) | ||
403 | return -ENOMEM; | ||
404 | } | ||
405 | |||
406 | ret = i915_do_wait_request(dev, overlay->last_flip_req, interruptible); | ||
407 | if (ret != 0) | ||
408 | return ret; | ||
409 | |||
410 | switch (overlay->hw_wedged) { | ||
411 | case RELEASE_OLD_VID: | ||
412 | obj = overlay->old_vid_bo->obj; | ||
413 | i915_gem_object_unpin(obj); | ||
414 | drm_gem_object_unreference(obj); | ||
415 | overlay->old_vid_bo = NULL; | ||
416 | break; | ||
417 | case SWITCH_OFF_STAGE_1: | ||
418 | flip_addr = overlay->flip_addr; | ||
419 | flip_addr |= OFC_UPDATE; | ||
420 | |||
421 | overlay->hw_wedged = SWITCH_OFF_STAGE_2; | ||
422 | |||
423 | BEGIN_LP_RING(6); | ||
424 | OUT_RING(MI_FLUSH); | ||
425 | OUT_RING(MI_NOOP); | ||
426 | OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_OFF); | ||
427 | OUT_RING(flip_addr); | ||
428 | OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP); | ||
429 | OUT_RING(MI_NOOP); | ||
430 | ADVANCE_LP_RING(); | ||
431 | |||
432 | overlay->last_flip_req = i915_add_request(dev, NULL, 0); | ||
433 | if (overlay->last_flip_req == 0) | ||
434 | return -ENOMEM; | ||
435 | |||
436 | ret = i915_do_wait_request(dev, overlay->last_flip_req, | ||
437 | interruptible); | ||
438 | if (ret != 0) | ||
439 | return ret; | ||
440 | |||
441 | case SWITCH_OFF_STAGE_2: | ||
442 | printk("switch off 2\n"); | ||
443 | |||
444 | BUG_ON(!overlay->vid_bo); | ||
445 | obj = overlay->vid_bo->obj; | ||
446 | |||
447 | i915_gem_object_unpin(obj); | ||
448 | drm_gem_object_unreference(obj); | ||
449 | overlay->vid_bo = NULL; | ||
450 | |||
451 | overlay->crtc->overlay = NULL; | ||
452 | overlay->crtc = NULL; | ||
453 | |||
454 | overlay->active = 0; | ||
455 | break; | ||
456 | default: | ||
457 | BUG_ON(overlay->hw_wedged != NEEDS_WAIT_FOR_FLIP); | ||
458 | } | ||
459 | |||
460 | overlay->hw_wedged = 0; | ||
461 | overlay->last_flip_req = 0; | ||
462 | return 0; | ||
463 | } | ||
464 | |||
370 | /* Wait for pending overlay flip and release old frame. | 465 | /* Wait for pending overlay flip and release old frame. |
371 | * Needs to be called before the overlay register are changed | 466 | * Needs to be called before the overlay register are changed |
372 | * via intel_overlay_(un)map_regs_atomic */ | 467 | * via intel_overlay_(un)map_regs_atomic */ |
@@ -375,13 +470,15 @@ static int intel_overlay_release_old_vid(struct intel_overlay *overlay) | |||
375 | int ret; | 470 | int ret; |
376 | struct drm_gem_object *obj; | 471 | struct drm_gem_object *obj; |
377 | 472 | ||
473 | /* only wait if there is actually an old frame to release to | ||
474 | * guarantee forward progress */ | ||
475 | if (!overlay->old_vid_bo) | ||
476 | return 0; | ||
477 | |||
378 | ret = intel_overlay_wait_flip(overlay); | 478 | ret = intel_overlay_wait_flip(overlay); |
379 | if (ret != 0) | 479 | if (ret != 0) |
380 | return ret; | 480 | return ret; |
381 | 481 | ||
382 | if (!overlay->old_vid_bo) | ||
383 | return 0; | ||
384 | |||
385 | obj = overlay->old_vid_bo->obj; | 482 | obj = overlay->old_vid_bo->obj; |
386 | i915_gem_object_unpin(obj); | 483 | i915_gem_object_unpin(obj); |
387 | drm_gem_object_unreference(obj); | 484 | drm_gem_object_unreference(obj); |
@@ -646,9 +743,6 @@ int intel_overlay_do_put_image(struct intel_overlay *overlay, | |||
646 | BUG_ON(!mutex_is_locked(&dev->mode_config.mutex)); | 743 | BUG_ON(!mutex_is_locked(&dev->mode_config.mutex)); |
647 | BUG_ON(!overlay); | 744 | BUG_ON(!overlay); |
648 | 745 | ||
649 | if (overlay->hw_wedged) | ||
650 | return -EBUSY; | ||
651 | |||
652 | ret = intel_overlay_release_old_vid(overlay); | 746 | ret = intel_overlay_release_old_vid(overlay); |
653 | if (ret != 0) | 747 | if (ret != 0) |
654 | return ret; | 748 | return ret; |
@@ -761,6 +855,9 @@ int intel_overlay_switch_off(struct intel_overlay *overlay) | |||
761 | intel_overlay_unmap_regs_atomic(overlay); | 855 | intel_overlay_unmap_regs_atomic(overlay); |
762 | 856 | ||
763 | ret = intel_overlay_off(overlay); | 857 | ret = intel_overlay_off(overlay); |
858 | if (ret != 0) | ||
859 | return ret; | ||
860 | |||
764 | /* never have the overlay hw on without showing a frame */ | 861 | /* never have the overlay hw on without showing a frame */ |
765 | BUG_ON(!overlay->vid_bo); | 862 | BUG_ON(!overlay->vid_bo); |
766 | obj = overlay->vid_bo->obj; | 863 | obj = overlay->vid_bo->obj; |
@@ -1002,6 +1099,12 @@ int intel_overlay_put_image(struct drm_device *dev, void *data, | |||
1002 | mutex_lock(&dev->mode_config.mutex); | 1099 | mutex_lock(&dev->mode_config.mutex); |
1003 | mutex_lock(&dev->struct_mutex); | 1100 | mutex_lock(&dev->struct_mutex); |
1004 | 1101 | ||
1102 | if (overlay->hw_wedged) { | ||
1103 | ret = intel_overlay_recover_from_interrupt(overlay, 1); | ||
1104 | if (ret != 0) | ||
1105 | goto out_unlock; | ||
1106 | } | ||
1107 | |||
1005 | if (overlay->crtc != crtc) { | 1108 | if (overlay->crtc != crtc) { |
1006 | struct drm_display_mode *mode = &crtc->base.mode; | 1109 | struct drm_display_mode *mode = &crtc->base.mode; |
1007 | ret = intel_overlay_switch_off(overlay); | 1110 | ret = intel_overlay_switch_off(overlay); |