diff options
Diffstat (limited to 'drivers/gpu/drm/i915/i915_irq.c')
-rw-r--r-- | drivers/gpu/drm/i915/i915_irq.c | 246 |
1 files changed, 239 insertions, 7 deletions
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c index b86b7b7130c..7ebc84c2881 100644 --- a/drivers/gpu/drm/i915/i915_irq.c +++ b/drivers/gpu/drm/i915/i915_irq.c | |||
@@ -26,6 +26,7 @@ | |||
26 | * | 26 | * |
27 | */ | 27 | */ |
28 | 28 | ||
29 | #include <linux/sysrq.h> | ||
29 | #include "drmP.h" | 30 | #include "drmP.h" |
30 | #include "drm.h" | 31 | #include "drm.h" |
31 | #include "i915_drm.h" | 32 | #include "i915_drm.h" |
@@ -41,9 +42,10 @@ | |||
41 | * we leave them always unmasked in IMR and then control enabling them through | 42 | * we leave them always unmasked in IMR and then control enabling them through |
42 | * PIPESTAT alone. | 43 | * PIPESTAT alone. |
43 | */ | 44 | */ |
44 | #define I915_INTERRUPT_ENABLE_FIX (I915_ASLE_INTERRUPT | \ | 45 | #define I915_INTERRUPT_ENABLE_FIX (I915_ASLE_INTERRUPT | \ |
45 | I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | \ | 46 | I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | \ |
46 | I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) | 47 | I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | \ |
48 | I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT) | ||
47 | 49 | ||
48 | /** Interrupts that we mask and unmask at runtime. */ | 50 | /** Interrupts that we mask and unmask at runtime. */ |
49 | #define I915_INTERRUPT_ENABLE_VAR (I915_USER_INTERRUPT) | 51 | #define I915_INTERRUPT_ENABLE_VAR (I915_USER_INTERRUPT) |
@@ -188,7 +190,7 @@ u32 i915_get_vblank_counter(struct drm_device *dev, int pipe) | |||
188 | low_frame = pipe ? PIPEBFRAMEPIXEL : PIPEAFRAMEPIXEL; | 190 | low_frame = pipe ? PIPEBFRAMEPIXEL : PIPEAFRAMEPIXEL; |
189 | 191 | ||
190 | if (!i915_pipe_enabled(dev, pipe)) { | 192 | if (!i915_pipe_enabled(dev, pipe)) { |
191 | DRM_ERROR("trying to get vblank count for disabled pipe %d\n", pipe); | 193 | DRM_DEBUG("trying to get vblank count for disabled pipe %d\n", pipe); |
192 | return 0; | 194 | return 0; |
193 | } | 195 | } |
194 | 196 | ||
@@ -217,7 +219,7 @@ u32 gm45_get_vblank_counter(struct drm_device *dev, int pipe) | |||
217 | int reg = pipe ? PIPEB_FRMCOUNT_GM45 : PIPEA_FRMCOUNT_GM45; | 219 | int reg = pipe ? PIPEB_FRMCOUNT_GM45 : PIPEA_FRMCOUNT_GM45; |
218 | 220 | ||
219 | if (!i915_pipe_enabled(dev, pipe)) { | 221 | if (!i915_pipe_enabled(dev, pipe)) { |
220 | DRM_ERROR("trying to get vblank count for disabled pipe %d\n", pipe); | 222 | DRM_DEBUG("trying to get vblank count for disabled pipe %d\n", pipe); |
221 | return 0; | 223 | return 0; |
222 | } | 224 | } |
223 | 225 | ||
@@ -232,7 +234,17 @@ static void i915_hotplug_work_func(struct work_struct *work) | |||
232 | drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t, | 234 | drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t, |
233 | hotplug_work); | 235 | hotplug_work); |
234 | struct drm_device *dev = dev_priv->dev; | 236 | struct drm_device *dev = dev_priv->dev; |
235 | 237 | struct drm_mode_config *mode_config = &dev->mode_config; | |
238 | struct drm_connector *connector; | ||
239 | |||
240 | if (mode_config->num_connector) { | ||
241 | list_for_each_entry(connector, &mode_config->connector_list, head) { | ||
242 | struct intel_output *intel_output = to_intel_output(connector); | ||
243 | |||
244 | if (intel_output->hot_plug) | ||
245 | (*intel_output->hot_plug) (intel_output); | ||
246 | } | ||
247 | } | ||
236 | /* Just fire off a uevent and let userspace tell us what to do */ | 248 | /* Just fire off a uevent and let userspace tell us what to do */ |
237 | drm_sysfs_hotplug_event(dev); | 249 | drm_sysfs_hotplug_event(dev); |
238 | } | 250 | } |
@@ -278,6 +290,201 @@ irqreturn_t igdng_irq_handler(struct drm_device *dev) | |||
278 | return ret; | 290 | return ret; |
279 | } | 291 | } |
280 | 292 | ||
293 | /** | ||
294 | * i915_error_work_func - do process context error handling work | ||
295 | * @work: work struct | ||
296 | * | ||
297 | * Fire an error uevent so userspace can see that a hang or error | ||
298 | * was detected. | ||
299 | */ | ||
300 | static void i915_error_work_func(struct work_struct *work) | ||
301 | { | ||
302 | drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t, | ||
303 | error_work); | ||
304 | struct drm_device *dev = dev_priv->dev; | ||
305 | char *event_string = "ERROR=1"; | ||
306 | char *envp[] = { event_string, NULL }; | ||
307 | |||
308 | DRM_DEBUG("generating error event\n"); | ||
309 | |||
310 | kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, envp); | ||
311 | } | ||
312 | |||
313 | /** | ||
314 | * i915_capture_error_state - capture an error record for later analysis | ||
315 | * @dev: drm device | ||
316 | * | ||
317 | * Should be called when an error is detected (either a hang or an error | ||
318 | * interrupt) to capture error state from the time of the error. Fills | ||
319 | * out a structure which becomes available in debugfs for user level tools | ||
320 | * to pick up. | ||
321 | */ | ||
322 | static void i915_capture_error_state(struct drm_device *dev) | ||
323 | { | ||
324 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
325 | struct drm_i915_error_state *error; | ||
326 | unsigned long flags; | ||
327 | |||
328 | spin_lock_irqsave(&dev_priv->error_lock, flags); | ||
329 | if (dev_priv->first_error) | ||
330 | goto out; | ||
331 | |||
332 | error = kmalloc(sizeof(*error), GFP_ATOMIC); | ||
333 | if (!error) { | ||
334 | DRM_DEBUG("out ot memory, not capturing error state\n"); | ||
335 | goto out; | ||
336 | } | ||
337 | |||
338 | error->eir = I915_READ(EIR); | ||
339 | error->pgtbl_er = I915_READ(PGTBL_ER); | ||
340 | error->pipeastat = I915_READ(PIPEASTAT); | ||
341 | error->pipebstat = I915_READ(PIPEBSTAT); | ||
342 | error->instpm = I915_READ(INSTPM); | ||
343 | if (!IS_I965G(dev)) { | ||
344 | error->ipeir = I915_READ(IPEIR); | ||
345 | error->ipehr = I915_READ(IPEHR); | ||
346 | error->instdone = I915_READ(INSTDONE); | ||
347 | error->acthd = I915_READ(ACTHD); | ||
348 | } else { | ||
349 | error->ipeir = I915_READ(IPEIR_I965); | ||
350 | error->ipehr = I915_READ(IPEHR_I965); | ||
351 | error->instdone = I915_READ(INSTDONE_I965); | ||
352 | error->instps = I915_READ(INSTPS); | ||
353 | error->instdone1 = I915_READ(INSTDONE1); | ||
354 | error->acthd = I915_READ(ACTHD_I965); | ||
355 | } | ||
356 | |||
357 | do_gettimeofday(&error->time); | ||
358 | |||
359 | dev_priv->first_error = error; | ||
360 | |||
361 | out: | ||
362 | spin_unlock_irqrestore(&dev_priv->error_lock, flags); | ||
363 | } | ||
364 | |||
365 | /** | ||
366 | * i915_handle_error - handle an error interrupt | ||
367 | * @dev: drm device | ||
368 | * | ||
369 | * Do some basic checking of regsiter state at error interrupt time and | ||
370 | * dump it to the syslog. Also call i915_capture_error_state() to make | ||
371 | * sure we get a record and make it available in debugfs. Fire a uevent | ||
372 | * so userspace knows something bad happened (should trigger collection | ||
373 | * of a ring dump etc.). | ||
374 | */ | ||
375 | static void i915_handle_error(struct drm_device *dev) | ||
376 | { | ||
377 | struct drm_i915_private *dev_priv = dev->dev_private; | ||
378 | u32 eir = I915_READ(EIR); | ||
379 | u32 pipea_stats = I915_READ(PIPEASTAT); | ||
380 | u32 pipeb_stats = I915_READ(PIPEBSTAT); | ||
381 | |||
382 | i915_capture_error_state(dev); | ||
383 | |||
384 | printk(KERN_ERR "render error detected, EIR: 0x%08x\n", | ||
385 | eir); | ||
386 | |||
387 | if (IS_G4X(dev)) { | ||
388 | if (eir & (GM45_ERROR_MEM_PRIV | GM45_ERROR_CP_PRIV)) { | ||
389 | u32 ipeir = I915_READ(IPEIR_I965); | ||
390 | |||
391 | printk(KERN_ERR " IPEIR: 0x%08x\n", | ||
392 | I915_READ(IPEIR_I965)); | ||
393 | printk(KERN_ERR " IPEHR: 0x%08x\n", | ||
394 | I915_READ(IPEHR_I965)); | ||
395 | printk(KERN_ERR " INSTDONE: 0x%08x\n", | ||
396 | I915_READ(INSTDONE_I965)); | ||
397 | printk(KERN_ERR " INSTPS: 0x%08x\n", | ||
398 | I915_READ(INSTPS)); | ||
399 | printk(KERN_ERR " INSTDONE1: 0x%08x\n", | ||
400 | I915_READ(INSTDONE1)); | ||
401 | printk(KERN_ERR " ACTHD: 0x%08x\n", | ||
402 | I915_READ(ACTHD_I965)); | ||
403 | I915_WRITE(IPEIR_I965, ipeir); | ||
404 | (void)I915_READ(IPEIR_I965); | ||
405 | } | ||
406 | if (eir & GM45_ERROR_PAGE_TABLE) { | ||
407 | u32 pgtbl_err = I915_READ(PGTBL_ER); | ||
408 | printk(KERN_ERR "page table error\n"); | ||
409 | printk(KERN_ERR " PGTBL_ER: 0x%08x\n", | ||
410 | pgtbl_err); | ||
411 | I915_WRITE(PGTBL_ER, pgtbl_err); | ||
412 | (void)I915_READ(PGTBL_ER); | ||
413 | } | ||
414 | } | ||
415 | |||
416 | if (IS_I9XX(dev)) { | ||
417 | if (eir & I915_ERROR_PAGE_TABLE) { | ||
418 | u32 pgtbl_err = I915_READ(PGTBL_ER); | ||
419 | printk(KERN_ERR "page table error\n"); | ||
420 | printk(KERN_ERR " PGTBL_ER: 0x%08x\n", | ||
421 | pgtbl_err); | ||
422 | I915_WRITE(PGTBL_ER, pgtbl_err); | ||
423 | (void)I915_READ(PGTBL_ER); | ||
424 | } | ||
425 | } | ||
426 | |||
427 | if (eir & I915_ERROR_MEMORY_REFRESH) { | ||
428 | printk(KERN_ERR "memory refresh error\n"); | ||
429 | printk(KERN_ERR "PIPEASTAT: 0x%08x\n", | ||
430 | pipea_stats); | ||
431 | printk(KERN_ERR "PIPEBSTAT: 0x%08x\n", | ||
432 | pipeb_stats); | ||
433 | /* pipestat has already been acked */ | ||
434 | } | ||
435 | if (eir & I915_ERROR_INSTRUCTION) { | ||
436 | printk(KERN_ERR "instruction error\n"); | ||
437 | printk(KERN_ERR " INSTPM: 0x%08x\n", | ||
438 | I915_READ(INSTPM)); | ||
439 | if (!IS_I965G(dev)) { | ||
440 | u32 ipeir = I915_READ(IPEIR); | ||
441 | |||
442 | printk(KERN_ERR " IPEIR: 0x%08x\n", | ||
443 | I915_READ(IPEIR)); | ||
444 | printk(KERN_ERR " IPEHR: 0x%08x\n", | ||
445 | I915_READ(IPEHR)); | ||
446 | printk(KERN_ERR " INSTDONE: 0x%08x\n", | ||
447 | I915_READ(INSTDONE)); | ||
448 | printk(KERN_ERR " ACTHD: 0x%08x\n", | ||
449 | I915_READ(ACTHD)); | ||
450 | I915_WRITE(IPEIR, ipeir); | ||
451 | (void)I915_READ(IPEIR); | ||
452 | } else { | ||
453 | u32 ipeir = I915_READ(IPEIR_I965); | ||
454 | |||
455 | printk(KERN_ERR " IPEIR: 0x%08x\n", | ||
456 | I915_READ(IPEIR_I965)); | ||
457 | printk(KERN_ERR " IPEHR: 0x%08x\n", | ||
458 | I915_READ(IPEHR_I965)); | ||
459 | printk(KERN_ERR " INSTDONE: 0x%08x\n", | ||
460 | I915_READ(INSTDONE_I965)); | ||
461 | printk(KERN_ERR " INSTPS: 0x%08x\n", | ||
462 | I915_READ(INSTPS)); | ||
463 | printk(KERN_ERR " INSTDONE1: 0x%08x\n", | ||
464 | I915_READ(INSTDONE1)); | ||
465 | printk(KERN_ERR " ACTHD: 0x%08x\n", | ||
466 | I915_READ(ACTHD_I965)); | ||
467 | I915_WRITE(IPEIR_I965, ipeir); | ||
468 | (void)I915_READ(IPEIR_I965); | ||
469 | } | ||
470 | } | ||
471 | |||
472 | I915_WRITE(EIR, eir); | ||
473 | (void)I915_READ(EIR); | ||
474 | eir = I915_READ(EIR); | ||
475 | if (eir) { | ||
476 | /* | ||
477 | * some errors might have become stuck, | ||
478 | * mask them. | ||
479 | */ | ||
480 | DRM_ERROR("EIR stuck: 0x%08x, masking\n", eir); | ||
481 | I915_WRITE(EMR, I915_READ(EMR) | eir); | ||
482 | I915_WRITE(IIR, I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT); | ||
483 | } | ||
484 | |||
485 | queue_work(dev_priv->wq, &dev_priv->error_work); | ||
486 | } | ||
487 | |||
281 | irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) | 488 | irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) |
282 | { | 489 | { |
283 | struct drm_device *dev = (struct drm_device *) arg; | 490 | struct drm_device *dev = (struct drm_device *) arg; |
@@ -319,15 +526,22 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) | |||
319 | pipea_stats = I915_READ(PIPEASTAT); | 526 | pipea_stats = I915_READ(PIPEASTAT); |
320 | pipeb_stats = I915_READ(PIPEBSTAT); | 527 | pipeb_stats = I915_READ(PIPEBSTAT); |
321 | 528 | ||
529 | if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT) | ||
530 | i915_handle_error(dev); | ||
531 | |||
322 | /* | 532 | /* |
323 | * Clear the PIPE(A|B)STAT regs before the IIR | 533 | * Clear the PIPE(A|B)STAT regs before the IIR |
324 | */ | 534 | */ |
325 | if (pipea_stats & 0x8000ffff) { | 535 | if (pipea_stats & 0x8000ffff) { |
536 | if (pipea_stats & PIPE_FIFO_UNDERRUN_STATUS) | ||
537 | DRM_DEBUG("pipe a underrun\n"); | ||
326 | I915_WRITE(PIPEASTAT, pipea_stats); | 538 | I915_WRITE(PIPEASTAT, pipea_stats); |
327 | irq_received = 1; | 539 | irq_received = 1; |
328 | } | 540 | } |
329 | 541 | ||
330 | if (pipeb_stats & 0x8000ffff) { | 542 | if (pipeb_stats & 0x8000ffff) { |
543 | if (pipeb_stats & PIPE_FIFO_UNDERRUN_STATUS) | ||
544 | DRM_DEBUG("pipe b underrun\n"); | ||
331 | I915_WRITE(PIPEBSTAT, pipeb_stats); | 545 | I915_WRITE(PIPEBSTAT, pipeb_stats); |
332 | irq_received = 1; | 546 | irq_received = 1; |
333 | } | 547 | } |
@@ -346,7 +560,8 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) | |||
346 | DRM_DEBUG("hotplug event received, stat 0x%08x\n", | 560 | DRM_DEBUG("hotplug event received, stat 0x%08x\n", |
347 | hotplug_status); | 561 | hotplug_status); |
348 | if (hotplug_status & dev_priv->hotplug_supported_mask) | 562 | if (hotplug_status & dev_priv->hotplug_supported_mask) |
349 | schedule_work(&dev_priv->hotplug_work); | 563 | queue_work(dev_priv->wq, |
564 | &dev_priv->hotplug_work); | ||
350 | 565 | ||
351 | I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status); | 566 | I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status); |
352 | I915_READ(PORT_HOTPLUG_STAT); | 567 | I915_READ(PORT_HOTPLUG_STAT); |
@@ -699,6 +914,7 @@ void i915_driver_irq_preinstall(struct drm_device * dev) | |||
699 | atomic_set(&dev_priv->irq_received, 0); | 914 | atomic_set(&dev_priv->irq_received, 0); |
700 | 915 | ||
701 | INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func); | 916 | INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func); |
917 | INIT_WORK(&dev_priv->error_work, i915_error_work_func); | ||
702 | 918 | ||
703 | if (IS_IGDNG(dev)) { | 919 | if (IS_IGDNG(dev)) { |
704 | igdng_irq_preinstall(dev); | 920 | igdng_irq_preinstall(dev); |
@@ -722,6 +938,7 @@ int i915_driver_irq_postinstall(struct drm_device *dev) | |||
722 | { | 938 | { |
723 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; | 939 | drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; |
724 | u32 enable_mask = I915_INTERRUPT_ENABLE_FIX | I915_INTERRUPT_ENABLE_VAR; | 940 | u32 enable_mask = I915_INTERRUPT_ENABLE_FIX | I915_INTERRUPT_ENABLE_VAR; |
941 | u32 error_mask; | ||
725 | 942 | ||
726 | DRM_INIT_WAITQUEUE(&dev_priv->irq_queue); | 943 | DRM_INIT_WAITQUEUE(&dev_priv->irq_queue); |
727 | 944 | ||
@@ -758,6 +975,21 @@ int i915_driver_irq_postinstall(struct drm_device *dev) | |||
758 | i915_enable_irq(dev_priv, I915_DISPLAY_PORT_INTERRUPT); | 975 | i915_enable_irq(dev_priv, I915_DISPLAY_PORT_INTERRUPT); |
759 | } | 976 | } |
760 | 977 | ||
978 | /* | ||
979 | * Enable some error detection, note the instruction error mask | ||
980 | * bit is reserved, so we leave it masked. | ||
981 | */ | ||
982 | if (IS_G4X(dev)) { | ||
983 | error_mask = ~(GM45_ERROR_PAGE_TABLE | | ||
984 | GM45_ERROR_MEM_PRIV | | ||
985 | GM45_ERROR_CP_PRIV | | ||
986 | I915_ERROR_MEMORY_REFRESH); | ||
987 | } else { | ||
988 | error_mask = ~(I915_ERROR_PAGE_TABLE | | ||
989 | I915_ERROR_MEMORY_REFRESH); | ||
990 | } | ||
991 | I915_WRITE(EMR, error_mask); | ||
992 | |||
761 | /* Disable pipe interrupt enables, clear pending pipe status */ | 993 | /* Disable pipe interrupt enables, clear pending pipe status */ |
762 | I915_WRITE(PIPEASTAT, I915_READ(PIPEASTAT) & 0x8000ffff); | 994 | I915_WRITE(PIPEASTAT, I915_READ(PIPEASTAT) & 0x8000ffff); |
763 | I915_WRITE(PIPEBSTAT, I915_READ(PIPEBSTAT) & 0x8000ffff); | 995 | I915_WRITE(PIPEBSTAT, I915_READ(PIPEBSTAT) & 0x8000ffff); |