diff options
author | Dave Airlie <airlied@redhat.com> | 2009-11-17 19:09:55 -0500 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2009-11-17 19:09:55 -0500 |
commit | 46557bef3f3834ac33031c7be27d39d90d507442 (patch) | |
tree | 5cfc4a9e1263fe0a15e516ca9695ee2f9b8899e4 /drivers/gpu/drm/i915/i915_irq.c | |
parent | 4efc50d697ed8d9a91f0005d922907a7b6c9290d (diff) | |
parent | d91d8a3f88059d93e34ac70d059153ec69a9ffc7 (diff) |
Merge branch 'drm-core-next' of ../linux-2.6 into drm-next
Diffstat (limited to 'drivers/gpu/drm/i915/i915_irq.c')
-rw-r--r-- | drivers/gpu/drm/i915/i915_irq.c | 102 |
1 files changed, 94 insertions, 8 deletions
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index 6c89f2ff2495..c3ceffa46ea0 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c | |||
@@ -31,6 +31,7 @@ | |||
31 | #include "drm.h" | 31 | #include "drm.h" |
32 | #include "i915_drm.h" | 32 | #include "i915_drm.h" |
33 | #include "i915_drv.h" | 33 | #include "i915_drv.h" |
34 | #include "i915_trace.h" | ||
34 | #include "intel_drv.h" | 35 | #include "intel_drv.h" |
35 | 36 | ||
36 | #define MAX_NOPID ((u32)~0) | 37 | #define MAX_NOPID ((u32)~0) |
@@ -279,7 +280,9 @@ irqreturn_t igdng_irq_handler(struct drm_device *dev) | |||
279 | } | 280 | } |
280 | 281 | ||
281 | if (gt_iir & GT_USER_INTERRUPT) { | 282 | if (gt_iir & GT_USER_INTERRUPT) { |
282 | dev_priv->mm.irq_gem_seqno = i915_get_gem_seqno(dev); | 283 | u32 seqno = i915_get_gem_seqno(dev); |
284 | dev_priv->mm.irq_gem_seqno = seqno; | ||
285 | trace_i915_gem_request_complete(dev, seqno); | ||
283 | DRM_WAKEUP(&dev_priv->irq_queue); | 286 | DRM_WAKEUP(&dev_priv->irq_queue); |
284 | } | 287 | } |
285 | 288 | ||
@@ -302,12 +305,25 @@ static void i915_error_work_func(struct work_struct *work) | |||
302 | drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t, | 305 | drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t, |
303 | error_work); | 306 | error_work); |
304 | struct drm_device *dev = dev_priv->dev; | 307 | struct drm_device *dev = dev_priv->dev; |
305 | char *event_string = "ERROR=1"; | 308 | char *error_event[] = { "ERROR=1", NULL }; |
306 | char *envp[] = { event_string, NULL }; | 309 | char *reset_event[] = { "RESET=1", NULL }; |
310 | char *reset_done_event[] = { "ERROR=0", NULL }; | ||
307 | 311 | ||
308 | DRM_DEBUG("generating error event\n"); | 312 | DRM_DEBUG("generating error event\n"); |
309 | 313 | kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, error_event); | |
310 | kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, envp); | 314 | |
315 | if (atomic_read(&dev_priv->mm.wedged)) { | ||
316 | if (IS_I965G(dev)) { | ||
317 | DRM_DEBUG("resetting chip\n"); | ||
318 | kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_event); | ||
319 | if (!i965_reset(dev, GDRST_RENDER)) { | ||
320 | atomic_set(&dev_priv->mm.wedged, 0); | ||
321 | kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_done_event); | ||
322 | } | ||
323 | } else { | ||
324 | printk("reboot required\n"); | ||
325 | } | ||
326 | } | ||
311 | } | 327 | } |
312 | 328 | ||
313 | /** | 329 | /** |
@@ -372,7 +388,7 @@ out: | |||
372 | * so userspace knows something bad happened (should trigger collection | 388 | * so userspace knows something bad happened (should trigger collection |
373 | * of a ring dump etc.). | 389 | * of a ring dump etc.). |
374 | */ | 390 | */ |
375 | static void i915_handle_error(struct drm_device *dev) | 391 | static void i915_handle_error(struct drm_device *dev, bool wedged) |
376 | { | 392 | { |
377 | struct drm_i915_private *dev_priv = dev->dev_private; | 393 | struct drm_i915_private *dev_priv = dev->dev_private; |
378 | u32 eir = I915_READ(EIR); | 394 | u32 eir = I915_READ(EIR); |
@@ -482,6 +498,16 @@ static void i915_handle_error(struct drm_device *dev) | |||
482 | I915_WRITE(IIR, I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT); | 498 | I915_WRITE(IIR, I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT); |
483 | } | 499 | } |
484 | 500 | ||
501 | if (wedged) { | ||
502 | atomic_set(&dev_priv->mm.wedged, 1); | ||
503 | |||
504 | /* | ||
505 | * Wakeup waiting processes so they don't hang | ||
506 | */ | ||
507 | printk("i915: Waking up sleeping processes\n"); | ||
508 | DRM_WAKEUP(&dev_priv->irq_queue); | ||
509 | } | ||
510 | |||
485 | queue_work(dev_priv->wq, &dev_priv->error_work); | 511 | queue_work(dev_priv->wq, &dev_priv->error_work); |
486 | } | 512 | } |
487 | 513 | ||
@@ -527,7 +553,7 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) | |||
527 | pipeb_stats = I915_READ(PIPEBSTAT); | 553 | pipeb_stats = I915_READ(PIPEBSTAT); |
528 | 554 | ||
529 | if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT) | 555 | if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT) |
530 | i915_handle_error(dev); | 556 | i915_handle_error(dev, false); |
531 | 557 | ||
532 | /* | 558 | /* |
533 | * Clear the PIPE(A|B)STAT regs before the IIR | 559 | * Clear the PIPE(A|B)STAT regs before the IIR |
@@ -599,8 +625,12 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) | |||
599 | } | 625 | } |
600 | 626 | ||
601 | if (iir & I915_USER_INTERRUPT) { | 627 | if (iir & I915_USER_INTERRUPT) { |
602 | dev_priv->mm.irq_gem_seqno = i915_get_gem_seqno(dev); | 628 | u32 seqno = i915_get_gem_seqno(dev); |
629 | dev_priv->mm.irq_gem_seqno = seqno; | ||
630 | trace_i915_gem_request_complete(dev, seqno); | ||
603 | DRM_WAKEUP(&dev_priv->irq_queue); | 631 | DRM_WAKEUP(&dev_priv->irq_queue); |
632 | dev_priv->hangcheck_count = 0; | ||
633 | mod_timer(&dev_priv->hangcheck_timer, jiffies + DRM_I915_HANGCHECK_PERIOD); | ||
604 | } | 634 | } |
605 | 635 | ||
606 | if (pipea_stats & vblank_status) { | 636 | if (pipea_stats & vblank_status) { |
@@ -695,6 +725,16 @@ void i915_user_irq_put(struct drm_device *dev) | |||
695 | spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags); | 725 | spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags); |
696 | } | 726 | } |
697 | 727 | ||
728 | void i915_trace_irq_get(struct drm_device *dev, u32 seqno) | ||
729 | { | ||
730 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | ||
731 | |||
732 | if (dev_priv->trace_irq_seqno == 0) | ||
733 | i915_user_irq_get(dev); | ||
734 | |||
735 | dev_priv->trace_irq_seqno = seqno; | ||
736 | } | ||
737 | |||
698 | static int i915_wait_irq(struct drm_device * dev, int irq_nr) | 738 | static int i915_wait_irq(struct drm_device * dev, int irq_nr) |
699 | { | 739 | { |
700 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | 740 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; |
@@ -880,6 +920,52 @@ int i915_vblank_swap(struct drm_device *dev, void *data, | |||
880 | return -EINVAL; | 920 | return -EINVAL; |
881 | } | 921 | } |
882 | 922 | ||
923 | struct drm_i915_gem_request *i915_get_tail_request(struct drm_device *dev) { | ||
924 | drm_i915_private_t *dev_priv = dev->dev_private; | ||
925 | return list_entry(dev_priv->mm.request_list.prev, struct drm_i915_gem_request, list); | ||
926 | } | ||
927 | |||
928 | /** | ||
929 | * This is called when the chip hasn't reported back with completed | ||
930 | * batchbuffers in a long time. The first time this is called we simply record | ||
931 | * ACTHD. If ACTHD hasn't changed by the time the hangcheck timer elapses | ||
932 | * again, we assume the chip is wedged and try to fix it. | ||
933 | */ | ||
934 | void i915_hangcheck_elapsed(unsigned long data) | ||
935 | { | ||
936 | struct drm_device *dev = (struct drm_device *)data; | ||
937 | drm_i915_private_t *dev_priv = dev->dev_private; | ||
938 | uint32_t acthd; | ||
939 | |||
940 | if (!IS_I965G(dev)) | ||
941 | acthd = I915_READ(ACTHD); | ||
942 | else | ||
943 | acthd = I915_READ(ACTHD_I965); | ||
944 | |||
945 | /* If all work is done then ACTHD clearly hasn't advanced. */ | ||
946 | if (list_empty(&dev_priv->mm.request_list) || | ||
947 | i915_seqno_passed(i915_get_gem_seqno(dev), i915_get_tail_request(dev)->seqno)) { | ||
948 | dev_priv->hangcheck_count = 0; | ||
949 | return; | ||
950 | } | ||
951 | |||
952 | if (dev_priv->last_acthd == acthd && dev_priv->hangcheck_count > 0) { | ||
953 | DRM_ERROR("Hangcheck timer elapsed... GPU hung\n"); | ||
954 | i915_handle_error(dev, true); | ||
955 | return; | ||
956 | } | ||
957 | |||
958 | /* Reset timer case chip hangs without another request being added */ | ||
959 | mod_timer(&dev_priv->hangcheck_timer, jiffies + DRM_I915_HANGCHECK_PERIOD); | ||
960 | |||
961 | if (acthd != dev_priv->last_acthd) | ||
962 | dev_priv->hangcheck_count = 0; | ||
963 | else | ||
964 | dev_priv->hangcheck_count++; | ||
965 | |||
966 | dev_priv->last_acthd = acthd; | ||
967 | } | ||
968 | |||
883 | /* drm_dma.h hooks | 969 | /* drm_dma.h hooks |
884 | */ | 970 | */ |
885 | static void igdng_irq_preinstall(struct drm_device *dev) | 971 | static void igdng_irq_preinstall(struct drm_device *dev) |