diff options
Diffstat (limited to 'drivers/gpu/drm/i915')
-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); |