drm/i915: Implement GPU reset on i965
This patch puts in place the machinery to attempt to reset the GPU. This will be used when attempting to recover from a GPU hang. Signed-off-by: Owain G. Ainsworth <oga@openbsd.org> Signed-off-by: Ben Gamari <bgamari.foss@gmail.com> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
This commit is contained in:
parent
f65d94211e
commit
11ed50ec2a
|
@ -1173,6 +1173,9 @@ static int i915_load_modeset_init(struct drm_device *dev,
|
|||
drm_mm_init(&dev_priv->vram, 0, prealloc_size);
|
||||
DRM_INFO("set up %ldM of stolen space\n", prealloc_size / (1024*1024));
|
||||
|
||||
/* We're off and running w/KMS */
|
||||
dev_priv->mm.suspended = 0;
|
||||
|
||||
/* Let GEM Manage from end of prealloc space to end of aperture.
|
||||
*
|
||||
* However, leave one page at the end still bound to the scratch page.
|
||||
|
@ -1184,7 +1187,9 @@ static int i915_load_modeset_init(struct drm_device *dev,
|
|||
*/
|
||||
i915_gem_do_init(dev, prealloc_size, agp_size - 4096);
|
||||
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
ret = i915_gem_init_ringbuffer(dev);
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
|
@ -1433,6 +1438,9 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Start out suspended */
|
||||
dev_priv->mm.suspended = 1;
|
||||
|
||||
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
|
||||
ret = i915_load_modeset_init(dev, prealloc_start,
|
||||
prealloc_size, agp_size);
|
||||
|
|
|
@ -127,6 +127,130 @@ static int i915_resume(struct drm_device *dev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* i965_reset - reset chip after a hang
|
||||
* @dev: drm device to reset
|
||||
* @flags: reset domains
|
||||
*
|
||||
* Reset the chip. Useful if a hang is detected. Returns zero on successful
|
||||
* reset or otherwise an error code.
|
||||
*
|
||||
* Procedure is fairly simple:
|
||||
* - reset the chip using the reset reg
|
||||
* - re-init context state
|
||||
* - re-init hardware status page
|
||||
* - re-init ring buffer
|
||||
* - re-init interrupt state
|
||||
* - re-init display
|
||||
*/
|
||||
int i965_reset(struct drm_device *dev, u8 flags)
|
||||
{
|
||||
drm_i915_private_t *dev_priv = dev->dev_private;
|
||||
unsigned long timeout;
|
||||
u8 gdrst;
|
||||
/*
|
||||
* We really should only reset the display subsystem if we actually
|
||||
* need to
|
||||
*/
|
||||
bool need_display = true;
|
||||
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
|
||||
/*
|
||||
* Clear request list
|
||||
*/
|
||||
i915_gem_retire_requests(dev);
|
||||
|
||||
if (need_display)
|
||||
i915_save_display(dev);
|
||||
|
||||
if (IS_I965G(dev) || IS_G4X(dev)) {
|
||||
/*
|
||||
* Set the domains we want to reset, then the reset bit (bit 0).
|
||||
* Clear the reset bit after a while and wait for hardware status
|
||||
* bit (bit 1) to be set
|
||||
*/
|
||||
pci_read_config_byte(dev->pdev, GDRST, &gdrst);
|
||||
pci_write_config_byte(dev->pdev, GDRST, gdrst | flags | ((flags == GDRST_FULL) ? 0x1 : 0x0));
|
||||
udelay(50);
|
||||
pci_write_config_byte(dev->pdev, GDRST, gdrst & 0xfe);
|
||||
|
||||
/* ...we don't want to loop forever though, 500ms should be plenty */
|
||||
timeout = jiffies + msecs_to_jiffies(500);
|
||||
do {
|
||||
udelay(100);
|
||||
pci_read_config_byte(dev->pdev, GDRST, &gdrst);
|
||||
} while ((gdrst & 0x1) && time_after(timeout, jiffies));
|
||||
|
||||
if (gdrst & 0x1) {
|
||||
WARN(true, "i915: Failed to reset chip\n");
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
return -EIO;
|
||||
}
|
||||
} else {
|
||||
DRM_ERROR("Error occurred. Don't know how to reset this chip.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Ok, now get things going again... */
|
||||
|
||||
/*
|
||||
* Everything depends on having the GTT running, so we need to start
|
||||
* there. Fortunately we don't need to do this unless we reset the
|
||||
* chip at a PCI level.
|
||||
*
|
||||
* Next we need to restore the context, but we don't use those
|
||||
* yet either...
|
||||
*
|
||||
* Ring buffer needs to be re-initialized in the KMS case, or if X
|
||||
* was running at the time of the reset (i.e. we weren't VT
|
||||
* switched away).
|
||||
*/
|
||||
if (drm_core_check_feature(dev, DRIVER_MODESET) ||
|
||||
!dev_priv->mm.suspended) {
|
||||
drm_i915_ring_buffer_t *ring = &dev_priv->ring;
|
||||
struct drm_gem_object *obj = ring->ring_obj;
|
||||
struct drm_i915_gem_object *obj_priv = obj->driver_private;
|
||||
dev_priv->mm.suspended = 0;
|
||||
|
||||
/* Stop the ring if it's running. */
|
||||
I915_WRITE(PRB0_CTL, 0);
|
||||
I915_WRITE(PRB0_TAIL, 0);
|
||||
I915_WRITE(PRB0_HEAD, 0);
|
||||
|
||||
/* Initialize the ring. */
|
||||
I915_WRITE(PRB0_START, obj_priv->gtt_offset);
|
||||
I915_WRITE(PRB0_CTL,
|
||||
((obj->size - 4096) & RING_NR_PAGES) |
|
||||
RING_NO_REPORT |
|
||||
RING_VALID);
|
||||
if (!drm_core_check_feature(dev, DRIVER_MODESET))
|
||||
i915_kernel_lost_context(dev);
|
||||
else {
|
||||
ring->head = I915_READ(PRB0_HEAD) & HEAD_ADDR;
|
||||
ring->tail = I915_READ(PRB0_TAIL) & TAIL_ADDR;
|
||||
ring->space = ring->head - (ring->tail + 8);
|
||||
if (ring->space < 0)
|
||||
ring->space += ring->Size;
|
||||
}
|
||||
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
drm_irq_uninstall(dev);
|
||||
drm_irq_install(dev);
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* Display needs restore too...
|
||||
*/
|
||||
if (need_display)
|
||||
i915_restore_display(dev);
|
||||
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int __devinit
|
||||
i915_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
{
|
||||
|
|
|
@ -624,6 +624,7 @@ extern long i915_compat_ioctl(struct file *filp, unsigned int cmd,
|
|||
extern int i915_emit_box(struct drm_device *dev,
|
||||
struct drm_clip_rect *boxes,
|
||||
int i, int DR1, int DR4);
|
||||
extern int i965_reset(struct drm_device *dev, u8 flags);
|
||||
|
||||
/* i915_irq.c */
|
||||
void i915_hangcheck_elapsed(unsigned long data);
|
||||
|
|
|
@ -482,6 +482,14 @@ static void i915_handle_error(struct drm_device *dev)
|
|||
I915_WRITE(IIR, I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT);
|
||||
}
|
||||
|
||||
if (dev_priv->mm.wedged) {
|
||||
/*
|
||||
* Wakeup waiting processes so they don't hang
|
||||
*/
|
||||
printk("i915: Waking up sleeping processes\n");
|
||||
DRM_WAKEUP(&dev_priv->irq_queue);
|
||||
}
|
||||
|
||||
queue_work(dev_priv->wq, &dev_priv->error_work);
|
||||
}
|
||||
|
||||
|
|
|
@ -85,6 +85,10 @@
|
|||
#define I915_GC_RENDER_CLOCK_200_MHZ (1 << 0)
|
||||
#define I915_GC_RENDER_CLOCK_333_MHZ (4 << 0)
|
||||
#define LBB 0xf4
|
||||
#define GDRST 0xc0
|
||||
#define GDRST_FULL (0<<2)
|
||||
#define GDRST_RENDER (1<<2)
|
||||
#define GDRST_MEDIA (3<<2)
|
||||
|
||||
/* VGA stuff */
|
||||
|
||||
|
|
Loading…
Reference in New Issue