aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm
diff options
context:
space:
mode:
authorDaniel Vetter <daniel.vetter@ffwll.ch>2009-09-15 16:57:37 -0400
committerEric Anholt <eric@anholt.net>2009-11-05 17:47:09 -0500
commit03f77ea5972e6a2363152aec692744cac824daba (patch)
treead63609747a62f22f2117c62bce38f17532f72ed /drivers/gpu/drm
parent5a5a0c64a99d7542c48c99d1a8bbb49e665842be (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.c17
-rw-r--r--drivers/gpu/drm/i915/intel_drv.h9
-rw-r--r--drivers/gpu/drm/i915/intel_overlay.c167
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)
1784static void intel_crtc_dpms_overlay(struct intel_crtc *intel_crtc, bool enable) 1784static 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
134struct intel_crtc { 139struct intel_crtc {
@@ -209,6 +214,8 @@ extern int intel_framebuffer_create(struct drm_device *dev,
209extern void intel_setup_overlay(struct drm_device *dev); 214extern void intel_setup_overlay(struct drm_device *dev);
210extern void intel_cleanup_overlay(struct drm_device *dev); 215extern void intel_cleanup_overlay(struct drm_device *dev);
211extern int intel_overlay_switch_off(struct intel_overlay *overlay); 216extern int intel_overlay_switch_off(struct intel_overlay *overlay);
217extern int intel_overlay_recover_from_interrupt(struct intel_overlay *overlay,
218 int interruptible);
212extern int intel_overlay_put_image(struct drm_device *dev, void *data, 219extern int intel_overlay_put_image(struct drm_device *dev, void *data,
213 struct drm_file *file_priv); 220 struct drm_file *file_priv);
214extern int intel_overlay_attrs(struct drm_device *dev, void *data, 221extern 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. */
387int 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);