Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 174521
b: refs/heads/master
c: 03f77ea
h: refs/heads/master
i:
  174519: 575a807
v: v3
  • Loading branch information
Daniel Vetter authored and Eric Anholt committed Nov 5, 2009
1 parent 3a545a3 commit 223d7e2
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 35 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 5a5a0c64a99d7542c48c99d1a8bbb49e665842be
refs/heads/master: 03f77ea5972e6a2363152aec692744cac824daba
17 changes: 16 additions & 1 deletion trunk/drivers/gpu/drm/i915/intel_display.c
Original file line number Diff line number Diff line change
Expand Up @@ -1784,11 +1784,26 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
static void intel_crtc_dpms_overlay(struct intel_crtc *intel_crtc, bool enable)
{
struct intel_overlay *overlay;
int ret;

if (!enable && intel_crtc->overlay) {
overlay = intel_crtc->overlay;
mutex_lock(&overlay->dev->struct_mutex);
intel_overlay_switch_off(overlay);
for (;;) {
ret = intel_overlay_switch_off(overlay);
if (ret == 0)
break;

ret = intel_overlay_recover_from_interrupt(overlay, 0);
if (ret != 0) {
/* overlay doesn't react anymore. Usually
* results in a black screen and an unkillable
* X server. */
BUG();
overlay->hw_wedged = HW_WEDGED;
break;
}
}
mutex_unlock(&overlay->dev->struct_mutex);
}
/* Let userspace switch the overlay on again. In most cases userspace
Expand Down
9 changes: 8 additions & 1 deletion trunk/drivers/gpu/drm/i915/intel_drv.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,13 @@ struct intel_overlay {
struct drm_i915_gem_object *reg_bo;
void *virt_addr;
/* flip handling */
int hw_wedged;
uint32_t last_flip_req;
int hw_wedged;
#define HW_WEDGED 1
#define NEEDS_WAIT_FOR_FLIP 2
#define RELEASE_OLD_VID 3
#define SWITCH_OFF_STAGE_1 4
#define SWITCH_OFF_STAGE_2 5
};

struct intel_crtc {
Expand Down Expand Up @@ -209,6 +214,8 @@ extern int intel_framebuffer_create(struct drm_device *dev,
extern void intel_setup_overlay(struct drm_device *dev);
extern void intel_cleanup_overlay(struct drm_device *dev);
extern int intel_overlay_switch_off(struct intel_overlay *overlay);
extern int intel_overlay_recover_from_interrupt(struct intel_overlay *overlay,
int interruptible);
extern int intel_overlay_put_image(struct drm_device *dev, void *data,
struct drm_file *file_priv);
extern int intel_overlay_attrs(struct drm_device *dev, void *data,
Expand Down
167 changes: 135 additions & 32 deletions trunk/drivers/gpu/drm/i915/intel_overlay.c
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,9 @@ static int intel_overlay_on(struct intel_overlay *overlay)

BUG_ON(overlay->active);

overlay->active = 1;
overlay->hw_wedged = NEEDS_WAIT_FOR_FLIP;

BEGIN_LP_RING(6);
OUT_RING(MI_FLUSH);
OUT_RING(MI_NOOP);
Expand All @@ -231,15 +234,16 @@ static int intel_overlay_on(struct intel_overlay *overlay)
OUT_RING(MI_NOOP);
ADVANCE_LP_RING();

ret = i915_lp_ring_sync(dev);
if (ret != 0) {
DRM_ERROR("intel overlay: ring sync failed, hw likely wedged\n");
overlay->hw_wedged = 1;
return 0;
}
overlay->last_flip_req = i915_add_request(dev, NULL, 0);
if (overlay->last_flip_req == 0)
return -ENOMEM;

overlay->active = 1;
ret = i915_do_wait_request(dev, overlay->last_flip_req, 1);
if (ret != 0)
return ret;

overlay->hw_wedged = 0;
overlay->last_flip_req = 0;
return 0;
}

Expand Down Expand Up @@ -283,7 +287,6 @@ static int intel_overlay_wait_flip(struct intel_overlay *overlay)

if (overlay->last_flip_req != 0) {
ret = i915_do_wait_request(dev, overlay->last_flip_req, 0);

if (ret != 0)
return ret;

Expand All @@ -296,19 +299,24 @@ static int intel_overlay_wait_flip(struct intel_overlay *overlay)
}

/* synchronous slowpath */
overlay->hw_wedged = RELEASE_OLD_VID;

BEGIN_LP_RING(2);
OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
OUT_RING(MI_NOOP);
ADVANCE_LP_RING();

/* run in lockstep with the hw for easier testing */
ret = i915_lp_ring_sync(dev);
if (ret != 0) {
DRM_ERROR("intel overlay: ring sync failed, hw likely wedged\n");
overlay->hw_wedged = 1;
}
overlay->last_flip_req = i915_add_request(dev, NULL, 0);
if (overlay->last_flip_req == 0)
return -ENOMEM;

return ret;
ret = i915_do_wait_request(dev, overlay->last_flip_req, 1);
if (ret != 0)
return ret;

overlay->hw_wedged = 0;
overlay->last_flip_req = 0;
return 0;
}

/* overlay needs to be disabled in OCMD reg */
Expand All @@ -329,6 +337,8 @@ static int intel_overlay_off(struct intel_overlay *overlay)
flip_addr |= OFC_UPDATE;

/* wait for overlay to go idle */
overlay->hw_wedged = SWITCH_OFF_STAGE_1;

BEGIN_LP_RING(6);
OUT_RING(MI_FLUSH);
OUT_RING(MI_NOOP);
Expand All @@ -338,14 +348,17 @@ static int intel_overlay_off(struct intel_overlay *overlay)
OUT_RING(MI_NOOP);
ADVANCE_LP_RING();

ret = i915_lp_ring_sync(dev);
if (ret != 0) {
DRM_ERROR("intel overlay: ring sync failed, hw likely wedged\n");
overlay->hw_wedged = 1;
overlay->last_flip_req = i915_add_request(dev, NULL, 0);
if (overlay->last_flip_req == 0)
return -ENOMEM;

ret = i915_do_wait_request(dev, overlay->last_flip_req, 1);
if (ret != 0)
return ret;
}

/* turn overlay off */
overlay->hw_wedged = SWITCH_OFF_STAGE_2;

BEGIN_LP_RING(6);
OUT_RING(MI_FLUSH);
OUT_RING(MI_NOOP);
Expand All @@ -355,18 +368,100 @@ static int intel_overlay_off(struct intel_overlay *overlay)
OUT_RING(MI_NOOP);
ADVANCE_LP_RING();

ret = i915_lp_ring_sync(dev);
if (ret != 0) {
DRM_ERROR("intel overlay: ring sync failed, hw likely wedged\n");
overlay->hw_wedged = 1;
overlay->last_flip_req = i915_add_request(dev, NULL, 0);
if (overlay->last_flip_req == 0)
return -ENOMEM;

ret = i915_do_wait_request(dev, overlay->last_flip_req, 1);
if (ret != 0)
return ret;
}

overlay->active = 0;

overlay->hw_wedged = 0;
overlay->last_flip_req = 0;
return ret;
}

/* recover from an interruption due to a signal
* We have to be careful not to repeat work forever an make forward progess. */
int intel_overlay_recover_from_interrupt(struct intel_overlay *overlay,
int interruptible)
{
struct drm_device *dev = overlay->dev;
drm_i915_private_t *dev_priv = dev->dev_private;
struct drm_gem_object *obj;
u32 flip_addr;
int ret;
RING_LOCALS;

if (overlay->hw_wedged == HW_WEDGED)
return -EIO;

if (overlay->last_flip_req == 0) {
overlay->last_flip_req = i915_add_request(dev, NULL, 0);
if (overlay->last_flip_req == 0)
return -ENOMEM;
}

ret = i915_do_wait_request(dev, overlay->last_flip_req, interruptible);
if (ret != 0)
return ret;

switch (overlay->hw_wedged) {
case RELEASE_OLD_VID:
obj = overlay->old_vid_bo->obj;
i915_gem_object_unpin(obj);
drm_gem_object_unreference(obj);
overlay->old_vid_bo = NULL;
break;
case SWITCH_OFF_STAGE_1:
flip_addr = overlay->flip_addr;
flip_addr |= OFC_UPDATE;

overlay->hw_wedged = SWITCH_OFF_STAGE_2;

BEGIN_LP_RING(6);
OUT_RING(MI_FLUSH);
OUT_RING(MI_NOOP);
OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_OFF);
OUT_RING(flip_addr);
OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
OUT_RING(MI_NOOP);
ADVANCE_LP_RING();

overlay->last_flip_req = i915_add_request(dev, NULL, 0);
if (overlay->last_flip_req == 0)
return -ENOMEM;

ret = i915_do_wait_request(dev, overlay->last_flip_req,
interruptible);
if (ret != 0)
return ret;

case SWITCH_OFF_STAGE_2:
printk("switch off 2\n");

BUG_ON(!overlay->vid_bo);
obj = overlay->vid_bo->obj;

i915_gem_object_unpin(obj);
drm_gem_object_unreference(obj);
overlay->vid_bo = NULL;

overlay->crtc->overlay = NULL;
overlay->crtc = NULL;

overlay->active = 0;
break;
default:
BUG_ON(overlay->hw_wedged != NEEDS_WAIT_FOR_FLIP);
}

overlay->hw_wedged = 0;
overlay->last_flip_req = 0;
return 0;
}

/* Wait for pending overlay flip and release old frame.
* Needs to be called before the overlay register are changed
* via intel_overlay_(un)map_regs_atomic */
Expand All @@ -375,13 +470,15 @@ static int intel_overlay_release_old_vid(struct intel_overlay *overlay)
int ret;
struct drm_gem_object *obj;

/* only wait if there is actually an old frame to release to
* guarantee forward progress */
if (!overlay->old_vid_bo)
return 0;

ret = intel_overlay_wait_flip(overlay);
if (ret != 0)
return ret;

if (!overlay->old_vid_bo)
return 0;

obj = overlay->old_vid_bo->obj;
i915_gem_object_unpin(obj);
drm_gem_object_unreference(obj);
Expand Down Expand Up @@ -646,9 +743,6 @@ int intel_overlay_do_put_image(struct intel_overlay *overlay,
BUG_ON(!mutex_is_locked(&dev->mode_config.mutex));
BUG_ON(!overlay);

if (overlay->hw_wedged)
return -EBUSY;

ret = intel_overlay_release_old_vid(overlay);
if (ret != 0)
return ret;
Expand Down Expand Up @@ -761,6 +855,9 @@ int intel_overlay_switch_off(struct intel_overlay *overlay)
intel_overlay_unmap_regs_atomic(overlay);

ret = intel_overlay_off(overlay);
if (ret != 0)
return ret;

/* never have the overlay hw on without showing a frame */
BUG_ON(!overlay->vid_bo);
obj = overlay->vid_bo->obj;
Expand Down Expand Up @@ -1002,6 +1099,12 @@ int intel_overlay_put_image(struct drm_device *dev, void *data,
mutex_lock(&dev->mode_config.mutex);
mutex_lock(&dev->struct_mutex);

if (overlay->hw_wedged) {
ret = intel_overlay_recover_from_interrupt(overlay, 1);
if (ret != 0)
goto out_unlock;
}

if (overlay->crtc != crtc) {
struct drm_display_mode *mode = &crtc->base.mode;
ret = intel_overlay_switch_off(overlay);
Expand Down

0 comments on commit 223d7e2

Please sign in to comment.