Merge commit 'origin/drm-gem' into ms-gem

Conflicts:

	linux-core/drmP.h
	linux-core/drm_drv.c
	linux-core/drm_stub.c
	linux-core/i915_drv.c
	linux-core/i915_gem.c
	shared-core/i915_drv.h
	shared-core/i915_irq.c
main
Kristian Høgsberg 2008-07-07 15:43:43 -04:00
commit 3e02f7fd31
11 changed files with 807 additions and 279 deletions

View File

@ -109,11 +109,11 @@ struct _dri_bo_gem {
int validate_index;
/**
* Boolean whether set_domain to CPU is current
* Set when set_domain has been called
* Cleared when a batch has been submitted
* Boolean whether we've started swrast
* Set when the buffer has been mapped
* Cleared when the buffer is unmapped
*/
int cpu_domain_set;
int swrast;
/** Array passed to the DRM containing relocation information. */
struct drm_i915_gem_relocation_entry *relocs;
@ -485,25 +485,27 @@ dri_gem_bo_map(dri_bo *bo, int write_enable)
bo_gem->virtual = (void *)(uintptr_t)mmap_arg.addr_ptr;
}
bo->virtual = bo_gem->virtual;
bo_gem->swrast = 0;
bo_gem->mapped = 1;
DBG("bo_map: %d (%s) -> %p\n", bo_gem->gem_handle, bo_gem->name, bo_gem->virtual);
}
if (!bo_gem->cpu_domain_set) {
if (!bo_gem->swrast) {
set_domain.handle = bo_gem->gem_handle;
set_domain.read_domains = I915_GEM_DOMAIN_CPU;
set_domain.write_domain = write_enable ? I915_GEM_DOMAIN_CPU : 0;
if (write_enable)
set_domain.write_domain = I915_GEM_DOMAIN_CPU;
else
set_domain.write_domain = 0;
do {
ret = ioctl(bufmgr_gem->fd, DRM_IOCTL_I915_GEM_SET_DOMAIN,
&set_domain);
} while (ret == -1 && errno == EINTR);
if (ret != 0) {
fprintf (stderr, "%s:%d: Error setting memory domains %d (%08x %08x): %s .\n",
__FILE__, __LINE__,
bo_gem->gem_handle, set_domain.read_domains, set_domain.write_domain,
strerror (errno));
fprintf (stderr, "%s:%d: Error setting swrast %d: %s\n",
__FILE__, __LINE__, bo_gem->gem_handle, strerror (errno));
}
bo_gem->cpu_domain_set = 1;
bo_gem->swrast = 1;
}
return 0;
@ -512,13 +514,24 @@ dri_gem_bo_map(dri_bo *bo, int write_enable)
static int
dri_gem_bo_unmap(dri_bo *bo)
{
dri_bufmgr_gem *bufmgr_gem = (dri_bufmgr_gem *)bo->bufmgr;
dri_bo_gem *bo_gem = (dri_bo_gem *)bo;
struct drm_i915_gem_sw_finish sw_finish;
int ret;
if (bo == NULL)
return 0;
assert(bo_gem->mapped);
if (bo_gem->swrast) {
sw_finish.handle = bo_gem->gem_handle;
do {
ret = ioctl(bufmgr_gem->fd, DRM_IOCTL_I915_GEM_SW_FINISH,
&sw_finish);
} while (ret == -1 && errno == EINTR);
bo_gem->swrast = 0;
}
return 0;
}
@ -583,7 +596,7 @@ dri_gem_bo_wait_rendering(dri_bo *bo)
int ret;
set_domain.handle = bo_gem->gem_handle;
set_domain.read_domains = I915_GEM_DOMAIN_CPU;
set_domain.read_domains = I915_GEM_DOMAIN_GTT;
set_domain.write_domain = 0;
ret = ioctl (bufmgr_gem->fd, DRM_IOCTL_I915_GEM_SET_DOMAIN, &set_domain);
if (ret != 0) {
@ -744,8 +757,8 @@ dri_gem_post_submit(dri_bo *batch_buf)
dri_bo *bo = bufmgr_gem->exec_bos[i];
dri_bo_gem *bo_gem = (dri_bo_gem *)bo;
/* Need to call set_domain on next bo_map */
bo_gem->cpu_domain_set = 0;
/* Need to call swrast on next bo_map */
bo_gem->swrast = 0;
/* Disconnect the buffer from the validate list */
bo_gem->validate_index = -1;

View File

@ -811,6 +811,10 @@ struct drm_driver {
/* Master routines */
int (*master_create)(struct drm_device *dev, struct drm_master *master);
void (*master_destroy)(struct drm_device *dev, struct drm_master *master);
int (*proc_init)(struct drm_minor *minor);
void (*proc_cleanup)(struct drm_minor *minor);
/**
* Driver-specific constructor for drm_gem_objects, to set up
* obj->driver_private.
@ -1366,7 +1370,7 @@ extern void drm_put_master(struct drm_master *master);
extern int drm_get_dev(struct pci_dev *pdev, const struct pci_device_id *ent,
struct drm_driver *driver);
extern int drm_put_dev(struct drm_device *dev);
extern int drm_put_minor(struct drm_minor **minor);
extern int drm_put_minor(struct drm_device *dev, struct drm_minor **p);
extern unsigned int drm_debug; /* 1 to enable debug output */
extern struct class *drm_class;

View File

@ -353,7 +353,7 @@ static inline int kobject_uevent_env(struct kobject *kobj,
#endif
#if (defined(CONFIG_X86) && defined(CONFIG_X86_32) && defined(CONFIG_HIMEM))
#if (defined(CONFIG_X86) && defined(CONFIG_X86_32) && defined(CONFIG_HIGHMEM))
#define DRM_KMAP_ATOMIC_PROT_PFN
extern void *kmap_atomic_prot_pfn(unsigned long pfn, enum km_type type,
pgprot_t protection);

View File

@ -424,9 +424,10 @@ static void drm_cleanup(struct drm_device * dev)
drm_memrange_takedown(&dev->offset_manager);
drm_ht_remove(&dev->object_hash);
drm_put_minor(&dev->primary);
drm_put_minor(dev, &dev->primary);
if (drm_core_check_feature(dev, DRIVER_MODESET))
drm_put_minor(&dev->control);
drm_put_minor(dev, &dev->control);
if (drm_put_dev(dev))
DRM_ERROR("Cannot unload module\n");
}

View File

@ -309,6 +309,13 @@ static int drm_get_minor(struct drm_device *dev, struct drm_minor **minor, int t
DRM_ERROR("DRM: Failed to initialize /proc/dri.\n");
goto err_mem;
}
if (dev->driver->proc_init) {
ret = dev->driver->proc_init(new_minor);
if (ret) {
DRM_ERROR("DRM: Driver failed to initialize /proc/dri.\n");
goto err_mem;
}
}
} else
new_minor->dev_root = NULL;
@ -325,8 +332,11 @@ static int drm_get_minor(struct drm_device *dev, struct drm_minor **minor, int t
err_g2:
if (new_minor->type == DRM_MINOR_LEGACY)
if (new_minor->type == DRM_MINOR_LEGACY) {
if (dev->driver->proc_cleanup)
dev->driver->proc_cleanup(new_minor);
drm_proc_cleanup(new_minor, drm_proc_root);
}
err_mem:
kfree(new_minor);
err_idr:
@ -398,10 +408,10 @@ int drm_get_dev(struct pci_dev *pdev, const struct pci_device_id *ent,
return 0;
err_g5:
drm_put_minor(&dev->primary);
drm_put_minor(dev, &dev->primary);
err_g4:
if (drm_core_check_feature(dev, DRIVER_MODESET))
drm_put_minor(&dev->control);
drm_put_minor(dev, &dev->control);
err_g3:
if (!drm_fb_loaded)
pci_disable_device(pdev);
@ -452,13 +462,16 @@ int drm_put_dev(struct drm_device * dev)
* last minor released.
*
*/
int drm_put_minor(struct drm_minor **minor_p)
int drm_put_minor(struct drm_device *dev, struct drm_minor **minor_p)
{
struct drm_minor *minor = *minor_p;
DRM_DEBUG("release secondary minor %d\n", minor->index);
if (minor->type == DRM_MINOR_LEGACY)
if (minor->type == DRM_MINOR_LEGACY) {
if (dev->driver->proc_cleanup)
dev->driver->proc_cleanup(minor);
drm_proc_cleanup(minor, drm_proc_root);
}
drm_sysfs_device_remove(minor);
idr_remove(&drm_minors_idr, minor->index);

View File

@ -600,6 +600,8 @@ static struct drm_driver driver = {
.get_reg_ofs = drm_core_get_reg_ofs,
.master_create = i915_master_create,
.master_destroy = i915_master_destroy,
.proc_init = i915_gem_proc_init,
.proc_cleanup = i915_gem_proc_cleanup,
.ioctls = i915_ioctls,
.gem_init_object = i915_gem_init_object,
.gem_free_object = i915_gem_free_object,

View File

@ -27,6 +27,7 @@
#include "drmP.h"
#include "drm.h"
#include "drm_compat.h"
#include "i915_drm.h"
#include "i915_drv.h"
@ -36,6 +37,13 @@
#define WATCH_LRU 0
#define WATCH_RELOC 0
#define WATCH_INACTIVE 0
#define WATCH_PWRITE 0
#if WATCH_BUF | WATCH_EXEC | WATCH_PWRITE
static void
i915_gem_dump_object(struct drm_gem_object *obj, int len,
const char *where, uint32_t mark);
#endif
static int
i915_gem_object_set_domain(struct drm_gem_object *obj,
@ -47,6 +55,9 @@ i915_gem_set_domain(struct drm_gem_object *obj,
uint32_t read_domains,
uint32_t write_domain);
static void
i915_gem_clflush_object(struct drm_gem_object *obj);
int i915_gem_do_init(struct drm_device *dev, unsigned long start,
unsigned long end)
{
@ -158,6 +169,8 @@ i915_gem_pread_ioctl(struct drm_device *dev, void *data,
return 0;
}
#include "drm_compat.h"
/**
* Writes data to the object referenced by handle.
*
@ -169,41 +182,121 @@ i915_gem_pwrite_ioctl(struct drm_device *dev, void *data,
{
struct drm_i915_gem_pwrite *args = data;
struct drm_gem_object *obj;
ssize_t written;
struct drm_i915_gem_object *obj_priv;
ssize_t remain;
loff_t offset;
int ret;
char __user *user_data;
char *vaddr;
int i, o, l;
int ret = 0;
unsigned long pfn;
unsigned long unwritten;
obj = drm_gem_object_lookup(dev, file_priv, args->handle);
if (obj == NULL)
return -EINVAL;
/** Bounds check destination.
*
* XXX: This could use review for overflow issues...
*/
if (args->offset > obj->size || args->size > obj->size ||
args->offset + args->size > obj->size)
return -EFAULT;
user_data = (char __user *) (uintptr_t) args->data_ptr;
remain = args->size;
if (!access_ok(VERIFY_READ, user_data, remain))
return -EFAULT;
mutex_lock(&dev->struct_mutex);
ret = i915_gem_set_domain(obj, file_priv,
I915_GEM_DOMAIN_CPU, I915_GEM_DOMAIN_CPU);
ret = i915_gem_object_pin(obj, 0);
if (ret) {
drm_gem_object_unreference(obj);
mutex_unlock(&dev->struct_mutex);
return ret;
}
offset = args->offset;
ret = i915_gem_set_domain(obj, file_priv,
I915_GEM_DOMAIN_GTT, I915_GEM_DOMAIN_GTT);
if (ret)
goto fail;
written = vfs_write(obj->filp,
(char __user *)(uintptr_t) args->data_ptr,
args->size, &offset);
obj_priv = obj->driver_private;
offset = obj_priv->gtt_offset + args->offset;
obj_priv->dirty = 1;
if (written != args->size) {
drm_gem_object_unreference(obj);
mutex_unlock(&dev->struct_mutex);
if (written < 0)
return written;
else
return -EINVAL;
while (remain > 0) {
/** Operation in this page
*
* i = page number
* o = offset within page
* l = bytes to copy
*/
i = offset >> PAGE_SHIFT;
o = offset & (PAGE_SIZE-1);
l = remain;
if ((o + l) > PAGE_SIZE)
l = PAGE_SIZE - o;
pfn = (dev->agp->base >> PAGE_SHIFT) + i;
#ifdef DRM_KMAP_ATOMIC_PROT_PFN
/* kmap_atomic can't map IO pages on non-HIGHMEM kernels
*/
vaddr = kmap_atomic_prot_pfn(pfn, KM_USER0,
__pgprot(__PAGE_KERNEL));
#if WATCH_PWRITE
DRM_INFO("pwrite i %d o %d l %d pfn %ld vaddr %p\n",
i, o, l, pfn, vaddr);
#endif
unwritten = __copy_from_user_inatomic_nocache(vaddr + o, user_data, l);
kunmap_atomic(vaddr, KM_USER0);
if (unwritten)
#endif
{
vaddr = ioremap(pfn << PAGE_SHIFT, PAGE_SIZE);
#if WATCH_PWRITE
DRM_INFO("pwrite slow i %d o %d l %d pfn %ld vaddr %p\n",
i, o, l, pfn, vaddr);
#endif
if (vaddr == NULL) {
ret = -EFAULT;
goto fail;
}
unwritten = __copy_from_user(vaddr + o, user_data, l);
#if WATCH_PWRITE
DRM_INFO("unwritten %ld\n", unwritten);
#endif
iounmap(vaddr);
if (unwritten) {
ret = -EFAULT;
goto fail;
}
}
remain -= l;
user_data += l;
offset += l;
}
#if WATCH_PWRITE && 1
i915_gem_clflush_object(obj);
i915_gem_dump_object(obj, args->offset + args->size, __func__, ~0);
i915_gem_clflush_object(obj);
#endif
fail:
i915_gem_object_unpin (obj);
drm_gem_object_unreference(obj);
mutex_unlock(&dev->struct_mutex);
return 0;
#if WATCH_PWRITE
if (ret)
DRM_INFO("pwrite failed %d\n", ret);
#endif
return ret;
}
/**
@ -232,6 +325,45 @@ i915_gem_set_domain_ioctl(struct drm_device *dev, void *data,
return ret;
}
/**
* Called when user space has done writes to this buffer
*/
int
i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_i915_gem_sw_finish *args = data;
struct drm_gem_object *obj;
struct drm_i915_gem_object *obj_priv;
int ret = 0;
if (!(dev->driver->driver_features & DRIVER_GEM))
return -ENODEV;
mutex_lock(&dev->struct_mutex);
obj = drm_gem_object_lookup(dev, file_priv, args->handle);
if (obj == NULL) {
mutex_unlock(&dev->struct_mutex);
return -EINVAL;
}
#if WATCH_BUF
DRM_INFO("%s: sw_finish %d (%p)\n",
__func__, args->handle, obj);
#endif
obj_priv = obj->driver_private;
/** Pinned buffers may be scanout, so flush the cache
*/
if ((obj->write_domain & I915_GEM_DOMAIN_CPU) && obj_priv->pin_count) {
i915_gem_clflush_object(obj);
drm_agp_chipset_flush(dev);
}
drm_gem_object_unreference(obj);
mutex_unlock(&dev->struct_mutex);
return ret;
}
/**
* Maps the contents of an object, returning the address it is mapped
* into.
@ -285,8 +417,13 @@ i915_gem_object_free_page_list(struct drm_gem_object *obj)
for (i = 0; i < page_count; i++)
if (obj_priv->page_list[i] != NULL)
if (obj_priv->page_list[i] != NULL) {
if (obj_priv->dirty)
set_page_dirty(obj_priv->page_list[i]);
mark_page_accessed(obj_priv->page_list[i]);
page_cache_release(obj_priv->page_list[i]);
}
obj_priv->dirty = 0;
drm_free(obj_priv->page_list,
page_count * sizeof(struct page *),
@ -321,7 +458,7 @@ i915_verify_inactive(struct drm_device *dev, char *file, int line)
list_for_each_entry(obj_priv, &dev_priv->mm.inactive_list, list) {
obj = obj_priv->obj;
if (obj_priv->pin_count || obj_priv->active || (obj->write_domain & ~I915_GEM_DOMAIN_CPU))
if (obj_priv->pin_count || obj_priv->active || (obj->write_domain & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT))
DRM_ERROR("inactive %p (p %d a %d w %x) %s:%d\n",
obj,
obj_priv->pin_count, obj_priv->active, obj->write_domain, file, line);
@ -496,7 +633,7 @@ i915_seqno_passed(uint32_t seq1, uint32_t seq2)
return (int32_t)(seq1 - seq2) >= 0;
}
static uint32_t
uint32_t
i915_get_gem_seqno(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@ -524,7 +661,7 @@ i915_gem_retire_requests(struct drm_device *dev)
list);
retiring_seqno = request->seqno;
if (i915_seqno_passed(seqno, retiring_seqno)) {
if (i915_seqno_passed(seqno, retiring_seqno) || dev_priv->mm.wedged) {
i915_gem_retire_request(dev, request);
list_del(&request->list);
@ -564,12 +701,17 @@ i915_wait_request(struct drm_device *dev, uint32_t seqno)
BUG_ON(seqno == 0);
if (!i915_seqno_passed(i915_get_gem_seqno(dev), seqno)) {
dev_priv->mm.waiting_gem_seqno = seqno;
i915_user_irq_on(dev);
ret = wait_event_interruptible(dev_priv->irq_queue,
i915_seqno_passed(i915_get_gem_seqno(dev),
seqno));
seqno) || dev_priv->mm.wedged);
i915_user_irq_off(dev);
dev_priv->mm.waiting_gem_seqno = 0;
}
if (dev_priv->mm.wedged)
ret = -EIO;
if (ret)
DRM_ERROR("%s returns %d (awaiting %d at %d)\n",
__func__, ret, seqno, i915_get_gem_seqno(dev));
@ -602,7 +744,7 @@ i915_gem_flush(struct drm_device *dev,
if (flush_domains & I915_GEM_DOMAIN_CPU)
drm_agp_chipset_flush(dev);
if ((invalidate_domains|flush_domains) & ~I915_GEM_DOMAIN_CPU) {
if ((invalidate_domains|flush_domains) & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT)) {
/*
* read/write caches:
*
@ -670,7 +812,7 @@ i915_gem_object_wait_rendering(struct drm_gem_object *obj)
/* If there are writes queued to the buffer, flush and
* create a new seqno to wait for.
*/
if (obj->write_domain & ~(I915_GEM_DOMAIN_CPU)) {
if (obj->write_domain & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT)) {
uint32_t write_domain = obj->write_domain;
#if WATCH_BUF
DRM_INFO("%s: flushing object %p from write domain %08x\n",
@ -756,11 +898,13 @@ i915_gem_object_unbind(struct drm_gem_object *obj)
i915_gem_object_free_page_list(obj);
if (obj_priv->gtt_space) {
atomic_dec(&dev->gtt_count);
atomic_sub(obj->size, &dev->gtt_memory);
drm_memrange_put_block(obj_priv->gtt_space);
obj_priv->gtt_space = NULL;
}
/* Remove ourselves from the LRU list if present. */
if (!list_empty(&obj_priv->list))
@ -769,7 +913,7 @@ i915_gem_object_unbind(struct drm_gem_object *obj)
return 0;
}
#if WATCH_BUF | WATCH_EXEC
#if WATCH_BUF | WATCH_EXEC | WATCH_PWRITE
static void
i915_gem_dump_page(struct page *page, uint32_t start, uint32_t end,
uint32_t bias, uint32_t mark)
@ -886,6 +1030,8 @@ i915_gem_evict_something(struct drm_device *dev)
list);
ret = i915_wait_request(dev, request->seqno);
if (ret)
break;
/* if waiting caused an object to become inactive,
* then loop around and wait for it. Otherwise, we
@ -934,6 +1080,11 @@ i915_gem_object_get_page_list(struct drm_gem_object *obj)
{
struct drm_i915_gem_object *obj_priv = obj->driver_private;
int page_count, i;
struct address_space *mapping;
struct inode *inode;
struct page *page;
int ret;
if (obj_priv->page_list)
return 0;
@ -949,16 +1100,25 @@ i915_gem_object_get_page_list(struct drm_gem_object *obj)
return -ENOMEM;
}
inode = obj->filp->f_path.dentry->d_inode;
mapping = inode->i_mapping;
for (i = 0; i < page_count; i++) {
obj_priv->page_list[i] =
find_or_create_page(obj->filp->f_mapping, i, GFP_HIGHUSER);
if (obj_priv->page_list[i] == NULL) {
DRM_ERROR("Failed to find_or_create_page()\n");
i915_gem_object_free_page_list(obj);
return -ENOMEM;
page = find_get_page(mapping, i);
if (page == NULL || !PageUptodate(page)) {
if (page) {
page_cache_release(page);
page = NULL;
}
unlock_page(obj_priv->page_list[i]);
ret = shmem_getpage(inode, i, &page, SGP_DIRTY, NULL);
if (ret) {
DRM_ERROR("shmem_getpage failed: %d\n", ret);
i915_gem_object_free_page_list(obj);
return ret;
}
unlock_page(page);
}
obj_priv->page_list[i] = page;
}
return 0;
}
@ -1049,8 +1209,8 @@ i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, unsigned alignment)
* wasn't in the GTT, there shouldn't be any way it could have been in
* a GPU cache
*/
BUG_ON(obj->read_domains & ~I915_GEM_DOMAIN_CPU);
BUG_ON(obj->write_domain & ~I915_GEM_DOMAIN_CPU);
BUG_ON(obj->read_domains & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT));
BUG_ON(obj->write_domain & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT));
return 0;
}
@ -1187,13 +1347,16 @@ i915_gem_object_set_domain(struct drm_gem_object *obj,
uint32_t write_domain)
{
struct drm_device *dev = obj->dev;
struct drm_i915_gem_object *obj_priv = obj->driver_private;
uint32_t invalidate_domains = 0;
uint32_t flush_domains = 0;
int ret;
#if WATCH_BUF
DRM_INFO("%s: object %p read %08x write %08x\n",
__func__, obj, read_domains, write_domain);
DRM_INFO("%s: object %p read %08x -> %08x write %08x -> %08x\n",
__func__, obj,
obj->read_domains, read_domains,
obj->write_domain, write_domain);
#endif
/*
* If the object isn't moving to a new write domain,
@ -1201,6 +1364,8 @@ i915_gem_object_set_domain(struct drm_gem_object *obj,
*/
if (write_domain == 0)
read_domains |= obj->read_domains;
else
obj_priv->dirty = 1;
/*
* Flush the current write domain if
@ -1228,7 +1393,7 @@ i915_gem_object_set_domain(struct drm_gem_object *obj,
* flushed before the cpu cache is invalidated
*/
if ((invalidate_domains & I915_GEM_DOMAIN_CPU) &&
(flush_domains & ~I915_GEM_DOMAIN_CPU)) {
(flush_domains & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT))) {
ret = i915_gem_object_wait_rendering(obj);
if (ret)
return ret;
@ -1241,6 +1406,12 @@ i915_gem_object_set_domain(struct drm_gem_object *obj,
obj->read_domains = read_domains;
dev->invalidate_domains |= invalidate_domains;
dev->flush_domains |= flush_domains;
#if WATCH_BUF
DRM_INFO("%s: read %08x write %08x invalidate %08x flush %08x\n",
__func__,
obj->read_domains, obj->write_domain,
dev->invalidate_domains, dev->flush_domains);
#endif
return 0;
}
@ -1664,6 +1835,13 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
mutex_lock(&dev->struct_mutex);
i915_verify_inactive(dev, __FILE__, __LINE__);
if (dev_priv->mm.wedged) {
DRM_ERROR("Execbuf while wedged\n");
mutex_unlock(&dev->struct_mutex);
return -EIO;
}
if (dev_priv->mm.suspended) {
DRM_ERROR("Execbuf while VT-switched.\n");
mutex_unlock(&dev->struct_mutex);
@ -1844,7 +2022,7 @@ i915_gem_object_pin(struct drm_gem_object *obj, uint32_t alignment)
if (obj_priv->pin_count == 1) {
atomic_inc(&dev->pin_count);
atomic_add(obj->size, &dev->pin_memory);
if (!obj_priv->active && (obj->write_domain & ~I915_GEM_DOMAIN_CPU) == 0 &&
if (!obj_priv->active && (obj->write_domain & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT)) == 0 &&
!list_empty(&obj_priv->list))
list_del_init(&obj_priv->list);
}
@ -1870,7 +2048,7 @@ i915_gem_object_unpin(struct drm_gem_object *obj)
* the inactive list
*/
if (obj_priv->pin_count == 0) {
if (!obj_priv->active && (obj->write_domain & ~I915_GEM_DOMAIN_CPU) == 0)
if (!obj_priv->active && (obj->write_domain & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT)) == 0)
list_move_tail(&obj_priv->list,
&dev_priv->mm.inactive_list);
atomic_dec(&dev->pin_count);
@ -1906,6 +2084,14 @@ i915_gem_pin_ioctl(struct drm_device *dev, void *data,
return ret;
}
/** XXX - flush the CPU caches for pinned objects
* as the X server doesn't manage domains yet
*/
if (obj->write_domain & I915_GEM_DOMAIN_CPU) {
i915_gem_clflush_object(obj);
drm_agp_chipset_flush(dev);
obj->write_domain = 0;
}
args->offset = obj_priv->gtt_offset;
drm_gem_object_unreference(obj);
mutex_unlock(&dev->struct_mutex);
@ -1994,6 +2180,11 @@ int i915_gem_init_object(struct drm_gem_object *obj)
void i915_gem_free_object(struct drm_gem_object *obj)
{
struct drm_i915_gem_object *obj_priv = obj->driver_private;
while (obj_priv->pin_count > 0)
i915_gem_object_unpin(obj);
i915_gem_object_unbind(obj);
drm_free(obj->driver_private, 1, DRM_MEM_DRIVER);
@ -2007,43 +2198,123 @@ i915_gem_set_domain(struct drm_gem_object *obj,
{
struct drm_device *dev = obj->dev;
int ret;
uint32_t flush_domains;
BUG_ON(!mutex_is_locked(&dev->struct_mutex));
ret = i915_gem_object_set_domain(obj, read_domains, write_domain);
if (ret)
return ret;
i915_gem_dev_set_domain(obj->dev);
flush_domains = i915_gem_dev_set_domain(obj->dev);
if (flush_domains & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT))
(void) i915_add_request(dev, flush_domains);
return 0;
}
void
i915_gem_lastclose(struct drm_device *dev)
/** Unbinds all objects that are on the given buffer list. */
static int
i915_gem_evict_from_list(struct drm_device *dev, struct list_head *head)
{
struct drm_i915_private *dev_priv = dev->dev_private;
mutex_lock(&dev->struct_mutex);
/* Assume that the chip has been idled at this point. Just pull them
* off the execution list and unref them. Since this is the last
* close, this is also the last ref and they'll go away.
*/
while (!list_empty(&dev_priv->mm.active_list)) {
struct drm_gem_object *obj;
struct drm_i915_gem_object *obj_priv;
int ret;
obj_priv = list_first_entry(&dev_priv->mm.active_list,
while (!list_empty(head)) {
obj_priv = list_first_entry(head,
struct drm_i915_gem_object,
list);
obj = obj_priv->obj;
list_del_init(&obj_priv->list);
obj_priv->active = 0;
obj_priv->obj->write_domain = 0;
drm_gem_object_unreference(obj_priv->obj);
if (obj_priv->pin_count != 0) {
DRM_ERROR("Pinned object in unbind list\n");
mutex_unlock(&dev->struct_mutex);
return -EINVAL;
}
ret = i915_gem_object_unbind(obj);
if (ret != 0) {
DRM_ERROR("Error unbinding object in LeaveVT: %d\n",
ret);
mutex_unlock(&dev->struct_mutex);
return ret;
}
}
return 0;
}
static int
i915_gem_idle(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
uint32_t seqno, cur_seqno, last_seqno;
int stuck;
if (dev_priv->mm.suspended)
return 0;
/* Hack! Don't let anybody do execbuf while we don't control the chip.
* We need to replace this with a semaphore, or something.
*/
dev_priv->mm.suspended = 1;
i915_kernel_lost_context(dev);
/* Flush the GPU along with all non-CPU write domains
*/
i915_gem_flush(dev, ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT),
~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT));
seqno = i915_add_request(dev, ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT));
if (seqno == 0) {
mutex_unlock(&dev->struct_mutex);
return -ENOMEM;
}
dev_priv->mm.waiting_gem_seqno = seqno;
last_seqno = 0;
stuck = 0;
for (;;) {
cur_seqno = i915_get_gem_seqno(dev);
if (i915_seqno_passed(cur_seqno, seqno))
break;
if (last_seqno == cur_seqno) {
if (stuck++ > 100) {
DRM_ERROR("hardware wedged\n");
dev_priv->mm.wedged = 1;
DRM_WAKEUP(&dev_priv->irq_queue);
break;
}
}
msleep(10);
last_seqno = cur_seqno;
}
dev_priv->mm.waiting_gem_seqno = 0;
i915_gem_retire_requests(dev);
/* Active and flushing should now be empty as we've
* waited for a sequence higher than any pending execbuffer
*/
BUG_ON(!list_empty(&dev_priv->mm.active_list));
BUG_ON(!list_empty(&dev_priv->mm.flushing_list));
/* Request should now be empty as we've also waited
* for the last request in the list
*/
BUG_ON(!list_empty(&dev_priv->mm.request_list));
/* Move all buffers out of the GTT. */
i915_gem_evict_from_list(dev, &dev_priv->mm.inactive_list);
BUG_ON(!list_empty(&dev_priv->mm.active_list));
BUG_ON(!list_empty(&dev_priv->mm.flushing_list));
BUG_ON(!list_empty(&dev_priv->mm.inactive_list));
BUG_ON(!list_empty(&dev_priv->mm.request_list));
return 0;
}
int
@ -2128,6 +2399,11 @@ i915_gem_entervt_ioctl(struct drm_device *dev, void *data,
struct drm_i915_private *dev_priv = dev->dev_private;
int ret;
if (dev_priv->mm.wedged) {
DRM_ERROR("Renabling wedged hardware, good luck\n");
dev_priv->mm.wedged = 0;
}
ret = i915_gem_init_ringbuffer(dev);
if (ret != 0)
return ret;
@ -2142,91 +2418,293 @@ i915_gem_entervt_ioctl(struct drm_device *dev, void *data,
return 0;
}
/** Unbinds all objects that are on the given buffer list. */
static int
i915_gem_evict_from_list(struct drm_device *dev, struct list_head *head)
{
struct drm_gem_object *obj;
struct drm_i915_gem_object *obj_priv;
int ret;
while (!list_empty(head)) {
obj_priv = list_first_entry(head,
struct drm_i915_gem_object,
list);
obj = obj_priv->obj;
if (obj_priv->pin_count != 0) {
DRM_ERROR("Pinned object in unbind list\n");
mutex_unlock(&dev->struct_mutex);
return -EINVAL;
}
ret = i915_gem_object_unbind(obj);
if (ret != 0) {
DRM_ERROR("Error unbinding object in LeaveVT: %d\n",
ret);
mutex_unlock(&dev->struct_mutex);
return ret;
}
}
return 0;
}
int
i915_gem_leavevt_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_i915_private *dev_priv = dev->dev_private;
uint32_t seqno;
int ret;
mutex_lock(&dev->struct_mutex);
/* Hack! Don't let anybody do execbuf while we don't control the chip.
* We need to replace this with a semaphore, or something.
*/
dev_priv->mm.suspended = 1;
i915_kernel_lost_context(dev);
/* Flush the GPU along with all non-CPU write domains
*/
i915_gem_flush(dev, ~I915_GEM_DOMAIN_CPU, ~I915_GEM_DOMAIN_CPU);
seqno = i915_add_request(dev, ~I915_GEM_DOMAIN_CPU);
if (seqno == 0) {
mutex_unlock(&dev->struct_mutex);
return -ENOMEM;
}
ret = i915_wait_request(dev, seqno);
if (ret) {
mutex_unlock(&dev->struct_mutex);
return ret;
}
/* Active and flushing should now be empty as we've
* waited for a sequence higher than any pending execbuffer
*/
BUG_ON(!list_empty(&dev_priv->mm.active_list));
BUG_ON(!list_empty(&dev_priv->mm.flushing_list));
/* Request should now be empty as we've also waited
* for the last request in the list
*/
BUG_ON(!list_empty(&dev_priv->mm.request_list));
/* Move all buffers out of the GTT. */
i915_gem_evict_from_list(dev, &dev_priv->mm.inactive_list);
BUG_ON(!list_empty(&dev_priv->mm.active_list));
BUG_ON(!list_empty(&dev_priv->mm.flushing_list));
BUG_ON(!list_empty(&dev_priv->mm.inactive_list));
BUG_ON(!list_empty(&dev_priv->mm.request_list));
ret = i915_gem_idle(dev);
if (ret == 0)
i915_gem_cleanup_ringbuffer(dev);
mutex_unlock(&dev->struct_mutex);
return 0;
}
static int i915_gem_active_info(char *buf, char **start, off_t offset,
int request, int *eof, void *data)
{
struct drm_minor *minor = (struct drm_minor *) data;
struct drm_device *dev = minor->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_object *obj_priv;
int len = 0;
if (offset > DRM_PROC_LIMIT) {
*eof = 1;
return 0;
}
*start = &buf[offset];
*eof = 0;
DRM_PROC_PRINT("Active:\n");
list_for_each_entry(obj_priv, &dev_priv->mm.active_list,
list)
{
struct drm_gem_object *obj = obj_priv->obj;
if (obj->name) {
DRM_PROC_PRINT(" %p(%d): %08x %08x %d\n",
obj, obj->name,
obj->read_domains, obj->write_domain,
obj_priv->last_rendering_seqno);
} else {
DRM_PROC_PRINT(" %p: %08x %08x %d\n",
obj,
obj->read_domains, obj->write_domain,
obj_priv->last_rendering_seqno);
}
}
if (len > request + offset)
return request;
*eof = 1;
return len - offset;
}
static int i915_gem_flushing_info(char *buf, char **start, off_t offset,
int request, int *eof, void *data)
{
struct drm_minor *minor = (struct drm_minor *) data;
struct drm_device *dev = minor->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_object *obj_priv;
int len = 0;
if (offset > DRM_PROC_LIMIT) {
*eof = 1;
return 0;
}
*start = &buf[offset];
*eof = 0;
DRM_PROC_PRINT("Flushing:\n");
list_for_each_entry(obj_priv, &dev_priv->mm.flushing_list,
list)
{
struct drm_gem_object *obj = obj_priv->obj;
if (obj->name) {
DRM_PROC_PRINT(" %p(%d): %08x %08x %d\n",
obj, obj->name,
obj->read_domains, obj->write_domain,
obj_priv->last_rendering_seqno);
} else {
DRM_PROC_PRINT(" %p: %08x %08x %d\n", obj,
obj->read_domains, obj->write_domain,
obj_priv->last_rendering_seqno);
}
}
if (len > request + offset)
return request;
*eof = 1;
return len - offset;
}
static int i915_gem_inactive_info(char *buf, char **start, off_t offset,
int request, int *eof, void *data)
{
struct drm_minor *minor = (struct drm_minor *) data;
struct drm_device *dev = minor->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_object *obj_priv;
int len = 0;
if (offset > DRM_PROC_LIMIT) {
*eof = 1;
return 0;
}
*start = &buf[offset];
*eof = 0;
DRM_PROC_PRINT("Inactive:\n");
list_for_each_entry(obj_priv, &dev_priv->mm.inactive_list,
list)
{
struct drm_gem_object *obj = obj_priv->obj;
if (obj->name) {
DRM_PROC_PRINT(" %p(%d): %08x %08x %d\n",
obj, obj->name,
obj->read_domains, obj->write_domain,
obj_priv->last_rendering_seqno);
} else {
DRM_PROC_PRINT(" %p: %08x %08x %d\n", obj,
obj->read_domains, obj->write_domain,
obj_priv->last_rendering_seqno);
}
}
if (len > request + offset)
return request;
*eof = 1;
return len - offset;
}
static int i915_gem_request_info(char *buf, char **start, off_t offset,
int request, int *eof, void *data)
{
struct drm_minor *minor = (struct drm_minor *) data;
struct drm_device *dev = minor->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_gem_request *gem_request;
int len = 0;
if (offset > DRM_PROC_LIMIT) {
*eof = 1;
return 0;
}
*start = &buf[offset];
*eof = 0;
DRM_PROC_PRINT("Request:\n");
list_for_each_entry(gem_request, &dev_priv->mm.request_list,
list)
{
DRM_PROC_PRINT (" %d @ %d %08x\n",
gem_request->seqno,
(int) (jiffies - gem_request->emitted_jiffies),
gem_request->flush_domains);
}
if (len > request + offset)
return request;
*eof = 1;
return len - offset;
}
static int i915_gem_seqno_info(char *buf, char **start, off_t offset,
int request, int *eof, void *data)
{
struct drm_minor *minor = (struct drm_minor *) data;
struct drm_device *dev = minor->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
int len = 0;
if (offset > DRM_PROC_LIMIT) {
*eof = 1;
return 0;
}
*start = &buf[offset];
*eof = 0;
DRM_PROC_PRINT("Current sequence: %d\n", i915_get_gem_seqno(dev));
DRM_PROC_PRINT("Waiter sequence: %d\n", dev_priv->mm.waiting_gem_seqno);
DRM_PROC_PRINT("IRQ sequence: %d\n", dev_priv->mm.irq_gem_seqno);
if (len > request + offset)
return request;
*eof = 1;
return len - offset;
}
static int i915_interrupt_info(char *buf, char **start, off_t offset,
int request, int *eof, void *data)
{
struct drm_minor *minor = (struct drm_minor *) data;
struct drm_device *dev = minor->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
int len = 0;
if (offset > DRM_PROC_LIMIT) {
*eof = 1;
return 0;
}
*start = &buf[offset];
*eof = 0;
DRM_PROC_PRINT("Interrupt enable: %08x\n",
I915_READ(IER));
DRM_PROC_PRINT("Interrupt identity: %08x\n",
I915_READ(IIR));
DRM_PROC_PRINT("Interrupt mask: %08x\n",
I915_READ(IMR));
DRM_PROC_PRINT("Pipe A stat: %08x\n",
I915_READ(PIPEASTAT));
DRM_PROC_PRINT("Pipe B stat: %08x\n",
I915_READ(PIPEBSTAT));
DRM_PROC_PRINT("Interrupts received: %d\n",
atomic_read(&dev_priv->irq_received));
DRM_PROC_PRINT("Current sequence: %d\n",
i915_get_gem_seqno(dev));
DRM_PROC_PRINT("Waiter sequence: %d\n",
dev_priv->mm.waiting_gem_seqno);
DRM_PROC_PRINT("IRQ sequence: %d\n",
dev_priv->mm.irq_gem_seqno);
if (len > request + offset)
return request;
*eof = 1;
return len - offset;
}
static struct drm_proc_list {
const char *name; /**< file name */
int (*f) (char *, char **, off_t, int, int *, void *); /**< proc callback*/
} i915_gem_proc_list[] = {
{"i915_gem_active", i915_gem_active_info},
{"i915_gem_flushing", i915_gem_flushing_info},
{"i915_gem_inactive", i915_gem_inactive_info},
{"i915_gem_request", i915_gem_request_info},
{"i915_gem_seqno", i915_gem_seqno_info},
{"i915_gem_interrupt", i915_interrupt_info},
};
#define I915_GEM_PROC_ENTRIES ARRAY_SIZE(i915_gem_proc_list)
int i915_gem_proc_init(struct drm_minor *minor)
{
struct proc_dir_entry *ent;
int i, j;
for (i = 0; i < I915_GEM_PROC_ENTRIES; i++) {
ent = create_proc_entry(i915_gem_proc_list[i].name,
S_IFREG | S_IRUGO, minor->dev_root);
if (!ent) {
DRM_ERROR("Cannot create /proc/dri/.../%s\n",
i915_gem_proc_list[i].name);
for (j = 0; j < i; j++)
remove_proc_entry(i915_gem_proc_list[i].name,
minor->dev_root);
return -1;
}
ent->read_proc = i915_gem_proc_list[i].f;
ent->data = minor;
}
return 0;
}
void i915_gem_proc_cleanup(struct drm_minor *minor)
{
int i;
if (!minor->dev_root)
return;
for (i = 0; i < I915_GEM_PROC_ENTRIES; i++)
remove_proc_entry(i915_gem_proc_list[i].name, minor->dev_root);
}
void
i915_gem_lastclose(struct drm_device *dev)
{
int ret;
struct drm_i915_private *dev_priv = dev->dev_private;
mutex_lock(&dev->struct_mutex);
if (dev_priv->ring.ring_obj != NULL) {
ret = i915_gem_idle(dev);
if (ret)
DRM_ERROR("failed to idle hardware: %d\n", ret);
i915_gem_cleanup_ringbuffer(dev);
}
mutex_unlock(&dev->struct_mutex);
}

View File

@ -1093,6 +1093,7 @@ struct drm_ioctl_desc i915_ioctls[] = {
DRM_IOCTL_DEF(DRM_I915_GEM_PWRITE, i915_gem_pwrite_ioctl, 0),
DRM_IOCTL_DEF(DRM_I915_GEM_MMAP, i915_gem_mmap_ioctl, 0),
DRM_IOCTL_DEF(DRM_I915_GEM_SET_DOMAIN, i915_gem_set_domain_ioctl, 0),
DRM_IOCTL_DEF(DRM_I915_GEM_SW_FINISH, i915_gem_sw_finish_ioctl, 0),
};
int i915_max_ioctl = DRM_ARRAY_SIZE(i915_ioctls);

View File

@ -189,6 +189,7 @@ typedef struct drm_i915_sarea {
#define DRM_I915_GEM_PWRITE 0x1d
#define DRM_I915_GEM_MMAP 0x1e
#define DRM_I915_GEM_SET_DOMAIN 0x1f
#define DRM_I915_GEM_SW_FINISH 0x20
#define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t)
#define DRM_IOCTL_I915_FLUSH DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH)
@ -221,6 +222,7 @@ typedef struct drm_i915_sarea {
#define DRM_IOCTL_I915_GEM_PWRITE DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_PWRITE, struct drm_i915_gem_pwrite)
#define DRM_IOCTL_I915_GEM_MMAP DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MMAP, struct drm_i915_gem_mmap)
#define DRM_IOCTL_I915_GEM_SET_DOMAIN DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_SET_DOMAIN, struct drm_i915_gem_set_domain)
#define DRM_IOCTL_I915_GEM_SW_FINISH DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_SW_FINISH, struct drm_i915_gem_sw_finish)
/* Asynchronous page flipping:
*/
@ -505,6 +507,11 @@ struct drm_i915_gem_set_domain {
uint32_t write_domain;
};
struct drm_i915_gem_sw_finish {
/** Handle for the object */
uint32_t handle;
};
struct drm_i915_gem_relocation_entry {
/**
* Handle of the buffer being pointed to by this relocation entry.
@ -569,6 +576,8 @@ struct drm_i915_gem_relocation_entry {
#define I915_GEM_DOMAIN_INSTRUCTION 0x00000010
/** Vertex address cache */
#define I915_GEM_DOMAIN_VERTEX 0x00000020
/** GTT domain - aperture and scanout */
#define I915_GEM_DOMAIN_GTT 0x00000040
/** @} */
struct drm_i915_gem_exec_object {

View File

@ -134,7 +134,6 @@ struct drm_i915_private {
wait_queue_head_t irq_queue;
atomic_t irq_received;
atomic_t irq_emitted;
int tex_lru_log_granularity;
int allow_batchbuffer;
@ -235,6 +234,16 @@ struct drm_i915_private {
uint32_t next_gem_seqno;
/**
* Waiting sequence number, if any
*/
uint32_t waiting_gem_seqno;
/**
* Last seq seen at irq time
*/
uint32_t irq_gem_seqno;
/**
* Flag if the X Server, and thus DRM, is not currently in
* control of the device.
@ -244,6 +253,15 @@ struct drm_i915_private {
* transitioned away from for kernel modesetting.
*/
int suspended;
/**
* Flag if the hardware appears to be wedged.
*
* This is set when attempts to idle the device timeout.
* It prevents command submission from occuring and makes
* every pending request fail
*/
int wedged;
} mm;
struct work_struct user_interrupt_task;
@ -369,6 +387,12 @@ struct drm_i915_gem_object {
*/
int active;
/**
* This is set if the object has been written to since last bound
* to the GTT
*/
int dirty;
/** AGP memory structure for our GTT binding. */
DRM_AGP_MEM *agp_mem;
@ -521,6 +545,8 @@ int i915_gem_mmap_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int i915_gem_set_domain_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int i915_gem_execbuffer(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int i915_gem_pin_ioctl(struct drm_device *dev, void *data,
@ -535,11 +561,14 @@ int i915_gem_entervt_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int i915_gem_leavevt_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int i915_gem_proc_init(struct drm_minor *minor);
void i915_gem_proc_cleanup(struct drm_minor *minor);
int i915_gem_init_object(struct drm_gem_object *obj);
void i915_gem_free_object(struct drm_gem_object *obj);
int i915_gem_object_pin(struct drm_gem_object *obj, uint32_t alignment);
void i915_gem_object_unpin(struct drm_gem_object *obj);
void i915_gem_lastclose(struct drm_device *dev);
uint32_t i915_get_gem_seqno(struct drm_device *dev);
void i915_gem_retire_requests(struct drm_device *dev);
int i915_gem_init_ringbuffer(struct drm_device *dev);
void i915_gem_cleanup_ringbuffer(struct drm_device *dev);

View File

@ -42,6 +42,26 @@
I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | \
I915_DISPLAY_PIPE_B_EVENT_INTERRUPT)
static inline void
i915_enable_irq(struct drm_i915_private *dev_priv, uint32_t mask)
{
if ((dev_priv->irq_mask_reg & mask) != 0) {
dev_priv->irq_mask_reg &= ~mask;
I915_WRITE(IMR, dev_priv->irq_mask_reg);
(void) I915_READ(IMR);
}
}
static inline void
i915_disable_irq(struct drm_i915_private *dev_priv, uint32_t mask)
{
if ((dev_priv->irq_mask_reg & mask) != mask) {
dev_priv->irq_mask_reg |= mask;
I915_WRITE(IMR, dev_priv->irq_mask_reg);
(void) I915_READ(IMR);
}
}
/**
* i915_get_pipe - return the the pipe associated with a given plane
* @dev: DRM device
@ -510,17 +530,27 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
int hotplug = 0;
int vblank = 0;
/* On i8xx/i915 hw the IIR and IER are 16bit on i9xx its 32bit */
if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev))
iir = I915_READ(IIR);
else
iir = I915_READ16(IIR);
if (dev->pdev->msi_enabled)
I915_WRITE(IER, 0);
if (!iir)
I915_WRITE(IMR, ~0);
iir = I915_READ(IIR);
#if 0
DRM_DEBUG("flag=%08x\n", iir);
#endif
atomic_inc(&dev_priv->irq_received);
if (iir == 0) {
DRM_DEBUG ("iir 0x%08x im 0x%08x ie 0x%08x pipea 0x%08x pipeb 0x%08x\n",
iir,
I915_READ(IMR),
I915_READ(IER),
I915_READ(PIPEASTAT),
I915_READ(PIPEBSTAT));
if (dev->pdev->msi_enabled) {
I915_WRITE(IMR,
dev_priv->irq_mask_reg);
(void) I915_READ(IMR);
}
return IRQ_NONE;
}
/*
* Clear the PIPE(A|B)STAT regs before the IIR otherwise
@ -528,13 +558,19 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
*/
if (iir & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT) {
pipea_stats = I915_READ(PIPEASTAT);
if (pipea_stats & (PIPE_START_VBLANK_INTERRUPT_STATUS|
PIPE_VBLANK_INTERRUPT_STATUS))
{
vblank++;
drm_handle_vblank(dev, i915_get_plane(dev, 0));
I915_WRITE(PIPEASTAT, pipea_stats);
}
if (iir & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) {
pipeb_stats = I915_READ(PIPEBSTAT);
I915_WRITE(PIPEBSTAT, pipeb_stats);
}
I915_WRITE(IIR, iir);
if (dev->pdev->msi_enabled)
I915_WRITE(IMR, dev_priv->irq_mask_reg);
(void) I915_READ(IIR); /* Flush posted writes */
/* This is a global event, and not a pipe A event */
if (pipea_stats & PIPE_HOTPLUG_INTERRUPT_STATUS)
hotplug = 1;
@ -547,51 +583,31 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
I915_WRITE(TV_DAC, tvdac | TVDAC_STATE_CHG_EN);
}
I915_WRITE(PIPEASTAT, pipea_stats);
}
if (iir & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) {
pipeb_stats = I915_READ(PIPEBSTAT);
if (pipeb_stats & (PIPE_START_VBLANK_INTERRUPT_STATUS|
PIPE_VBLANK_INTERRUPT_STATUS))
{
vblank++;
drm_handle_vblank(dev, i915_get_plane(dev, 1));
}
I915_WRITE(PIPEBSTAT, pipeb_stats);
}
/* Clear the generated interrupt */
if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) {
I915_WRITE(IIR, iir);
(void) I915_READ(IIR);
} else {
I915_WRITE16(IIR, iir);
(void) I915_READ16(IIR);
}
if (dev->primary->master) {
master_priv = dev->primary->master->driver_priv;
master_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
}
if (iir & I915_USER_INTERRUPT) {
dev_priv->mm.irq_gem_seqno = i915_get_gem_seqno(dev);
DRM_WAKEUP(&dev_priv->irq_queue);
#ifdef I915_HAVE_FENCE
i915_fence_handler(dev);
#endif
}
if (pipea_stats & (I915_START_VBLANK_INTERRUPT_STATUS|
I915_VBLANK_INTERRUPT_STATUS)) {
vblank = 1;
if (pipea_stats & (PIPE_START_VBLANK_INTERRUPT_STATUS|
PIPE_VBLANK_INTERRUPT_STATUS)) {
vblank++;
drm_handle_vblank(dev, i915_get_plane(dev, 0));
}
if (pipeb_stats & (I915_START_VBLANK_INTERRUPT_STATUS|
I915_VBLANK_INTERRUPT_STATUS)) {
vblank = 1;
if (pipeb_stats & (PIPE_START_VBLANK_INTERRUPT_STATUS|
PIPE_VBLANK_INTERRUPT_STATUS)) {
vblank++;
drm_handle_vblank(dev, i915_get_plane(dev, 1));
}
if (vblank) {
if (dev_priv->swaps_pending > 0)
drm_locked_tasklet(dev, i915_vblank_tasklet);
@ -616,9 +632,6 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
i915_run_hotplug_tasklet(dev, temp2);
}
if (dev->pdev->msi_enabled)
I915_WRITE(IER, I915_INTERRUPT_ENABLE_MASK);
return IRQ_HANDLED;
}
@ -646,16 +659,9 @@ void i915_user_irq_on(struct drm_device *dev)
struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private;
DRM_SPINLOCK(&dev_priv->user_irq_lock);
if (dev_priv->irq_enabled && (++dev_priv->user_irq_refcount == 1)){
dev_priv->irq_mask_reg &= ~I915_USER_INTERRUPT;
if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev))
I915_WRITE(IMR, dev_priv->irq_mask_reg);
else
I915_WRITE16(IMR, dev_priv->irq_mask_reg);
I915_READ16(IMR);
}
if (dev_priv->irq_enabled && (++dev_priv->user_irq_refcount == 1))
i915_enable_irq(dev_priv, I915_USER_INTERRUPT);
DRM_SPINUNLOCK(&dev_priv->user_irq_lock);
}
void i915_user_irq_off(struct drm_device *dev)
@ -664,14 +670,8 @@ void i915_user_irq_off(struct drm_device *dev)
DRM_SPINLOCK(&dev_priv->user_irq_lock);
BUG_ON(dev_priv->irq_enabled && dev_priv->user_irq_refcount <= 0);
if (dev_priv->irq_enabled && (--dev_priv->user_irq_refcount == 0)) {
dev_priv->irq_mask_reg |= I915_USER_INTERRUPT;
if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev))
I915_WRITE(IMR, dev_priv->irq_mask_reg);
else
I915_WRITE16(IMR, dev_priv->irq_mask_reg);
I915_READ16(IMR);
}
if (dev_priv->irq_enabled && (--dev_priv->user_irq_refcount == 0))
i915_disable_irq(dev_priv, I915_USER_INTERRUPT);
DRM_SPINUNLOCK(&dev_priv->user_irq_lock);
}
@ -803,11 +803,7 @@ int i915_enable_vblank(struct drm_device *dev, int plane)
}
DRM_SPINLOCK(&dev_priv->user_irq_lock);
dev_priv->irq_mask_reg &= ~mask_reg;
if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev))
I915_WRITE(IMR, dev_priv->irq_mask_reg);
else
I915_WRITE16(IMR, dev_priv->irq_mask_reg);
i915_enable_irq(dev_priv, mask_reg);
DRM_SPINUNLOCK(&dev_priv->user_irq_lock);
return 0;
@ -837,11 +833,7 @@ void i915_disable_vblank(struct drm_device *dev, int plane)
}
DRM_SPINLOCK(&dev_priv->user_irq_lock);
dev_priv->irq_mask_reg |= mask_reg;
if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev))
I915_WRITE(IMR, dev_priv->irq_mask_reg);
else
I915_WRITE16(IMR, dev_priv->irq_mask_reg);
i915_disable_irq(dev_priv, mask_reg);
DRM_SPINUNLOCK(&dev_priv->user_irq_lock);
if (pipestat_reg) {
@ -863,7 +855,7 @@ void i915_enable_interrupt (struct drm_device *dev)
struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private;
struct drm_connector *o;
dev_priv->irq_mask_reg &= ~I915_USER_INTERRUPT;
dev_priv->irq_mask_reg &= ~0;
if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) {
if (dev->mode_config.num_connector)
@ -925,14 +917,9 @@ void i915_enable_interrupt (struct drm_device *dev)
}
}
if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) {
I915_WRITE(IMR, dev_priv->irq_mask_reg);
I915_WRITE(IER, ~dev_priv->irq_mask_reg);
} else {
I915_WRITE16(IMR, dev_priv->irq_mask_reg);
I915_WRITE16(IER, ~(u16)dev_priv->irq_mask_reg);
}
I915_WRITE(IER, I915_INTERRUPT_ENABLE_MASK);
(void) I915_READ (IER);
dev_priv->irq_enabled = 1;
}
@ -964,17 +951,15 @@ int i915_vblank_pipe_get(struct drm_device *dev, void *data,
{
struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_i915_vblank_pipe *pipe = data;
u16 flag;
u32 flag = 0;
if (!dev_priv) {
DRM_ERROR("called with no initialization\n");
return -EINVAL;
}
if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev))
flag = I915_READ(IER);
else
flag = I915_READ16(IER);
if (dev_priv->irq_enabled)
flag = ~dev_priv->irq_mask_reg;
pipe->pipe = 0;
if (flag & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT)
@ -1158,19 +1143,12 @@ void i915_driver_irq_preinstall(struct drm_device * dev)
tmp = I915_READ(PIPEBSTAT);
I915_WRITE(PIPEBSTAT, tmp);
I915_WRITE16(HWSTAM, 0xeffe);
if (IS_I9XX(dev) && !IS_I915G(dev) && !IS_I915GM(dev)) {
I915_WRITE(IMR, 0x0);
atomic_set(&dev_priv->irq_received, 0);
I915_WRITE(HWSTAM, 0xffff);
I915_WRITE(IER, 0x0);
tmp = I915_READ(IIR);
I915_WRITE(IIR, tmp);
} else {
I915_WRITE16(IMR, 0x0);
I915_WRITE16(IER, 0x0);
tmp = I915_READ16(IIR);
I915_WRITE16(IIR, tmp);
}
I915_WRITE(IMR, 0xffffffff);
I915_WRITE(IIR, 0xffffffff);
(void) I915_READ(IIR);
}
int i915_driver_irq_postinstall(struct drm_device * dev)