Mercurial > illumos > illumos-gate
changeset 11260:eb8c6f2097e8
PSARC 2009/425 Additional ioctls for GEM support in i915 driver
PSARC 2009/474 Additional IOCTL Support in Agpgart Driver
6815826 GEM should be supported in drm driver
6904304 System panic in pci_get_available_prop() in busra.c
line wrap: on
line diff
--- a/usr/src/uts/common/Makefile.files Fri Dec 04 16:46:47 2009 -0800 +++ b/usr/src/uts/common/Makefile.files Sat Dec 05 13:25:40 2009 +0800 @@ -1056,7 +1056,7 @@ drm_auth.o drm_bufs.o drm_context.o drm_dma.o \ drm_drawable.o drm_drv.o drm_fops.o drm_ioctl.o drm_irq.o \ drm_lock.o drm_memory.o drm_msg.o drm_pci.o drm_scatter.o \ - ati_pcigart.o + drm_cache.o drm_gem.o drm_mm.o ati_pcigart.o FM_OBJS += devfm.o devfm_machdep.o
--- a/usr/src/uts/common/io/busra.c Fri Dec 04 16:46:47 2009 -0800 +++ b/usr/src/uts/common/io/busra.c Sat Dec 05 13:25:40 2009 +0800 @@ -444,7 +444,7 @@ * Update dip's "available" property, adding this piece of * resource to the pool. */ - (void) pci_put_available_prop(dip, base, len, type); + (void) pci_put_available_prop(dipmap->ra_dip, base, len, type); done: return (NDI_SUCCESS); @@ -732,7 +732,8 @@ * resource from the pool. */ if ((rval == NDI_SUCCESS) || (rval == NDI_RA_PARTIAL_REQ)) - (void) pci_get_available_prop(dip, *retbasep, *retlenp, type); + (void) pci_get_available_prop(dipmap->ra_dip, + *retbasep, *retlenp, type); return (rval); }
--- a/usr/src/uts/common/io/drm/drm.h Fri Dec 04 16:46:47 2009 -0800 +++ b/usr/src/uts/common/io/drm/drm.h Sat Dec 05 13:25:40 2009 +0800 @@ -13,6 +13,7 @@ /* * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * Copyright (c) 2009, Intel Corporation. * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -665,6 +666,19 @@ struct drm_wait_vblank_reply reply; } drm_wait_vblank_t; +#define _DRM_PRE_MODESET 1 +#define _DRM_POST_MODESET 2 + +/** + * DRM_IOCTL_MODESET_CTL ioctl argument type + * + * \sa drmModesetCtl(). + */ +typedef struct drm_modeset_ctl { + uint32_t crtc; + uint32_t cmd; +} drm_modeset_ctl_t; + /** * DRM_IOCTL_AGP_ENABLE ioctl argument type. * @@ -737,6 +751,34 @@ int drm_dd_minor; } drm_set_version_t; +/** DRM_IOCTL_GEM_CLOSE ioctl argument type */ +typedef struct drm_gem_close { + /** Handle of the object to be closed. */ + uint32_t handle; + uint32_t pad; +} drm_gem_close_t; + +/** DRM_IOCTL_GEM_FLINK ioctl argument type */ +typedef struct drm_gem_flink { + /** Handle for the object being named */ + uint32_t handle; + + /** Returned global name */ + uint32_t name; +} drm_gem_flink_t; + +/** DRM_IOCTL_GEM_OPEN ioctl argument type */ +typedef struct drm_gem_open { + /** Name of object being opened */ + uint32_t name; + + /** Returned handle for the object */ + uint32_t handle; + + /** Returned size of the object */ + uint64_t size; +} drm_gem_open_t; + /** * \name Ioctls Definitions */ @@ -756,6 +798,10 @@ #define DRM_IOCTL_GET_CLIENT DRM_IOWR(0x05, drm_client_t) #define DRM_IOCTL_GET_STATS DRM_IOR( 0x06, drm_stats_t) #define DRM_IOCTL_SET_VERSION DRM_IOWR(0x07, drm_set_version_t) +#define DRM_IOCTL_MODESET_CTL DRM_IOW(0x08, drm_modeset_ctl_t) +#define DRM_IOCTL_GEM_CLOSE DRM_IOW (0x09, drm_gem_close_t) +#define DRM_IOCTL_GEM_FLINK DRM_IOWR(0x0a, drm_gem_flink_t) +#define DRM_IOCTL_GEM_OPEN DRM_IOWR(0x0b, drm_gem_open_t) #define DRM_IOCTL_SET_UNIQUE DRM_IOW( 0x10, drm_unique_t) #define DRM_IOCTL_AUTH_MAGIC DRM_IOW( 0x11, drm_auth_t)
--- a/usr/src/uts/common/io/drm/drmP.h Fri Dec 04 16:46:47 2009 -0800 +++ b/usr/src/uts/common/io/drm/drmP.h Sat Dec 05 13:25:40 2009 +0800 @@ -5,6 +5,7 @@ /* * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * Copyright (c) 2009, Intel Corporation. * All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -151,8 +152,9 @@ #define DRM_LOCK_OWNED() ASSERT(mutex_owned(&dev->dev_lock)) #define spin_lock_irqsave(l, flag) mutex_enter(l) #define spin_unlock_irqrestore(u, flag) mutex_exit(u) -#define spin_lock(l) mutex_enter(l) -#define spin_unlock(u) mutex_exit(u) +#define spin_lock(l) mutex_enter(l) +#define spin_unlock(u) mutex_exit(u) + #define DRM_UDELAY(sec) delay(drv_usectohz(sec *1000)) #define DRM_MEMORYBARRIER() @@ -166,12 +168,16 @@ drm_device_t *dev1, intptr_t data, drm_file_t *fpriv, int mode #define DRM_COPYFROM_WITH_RETURN(dest, src, size) \ - if (ddi_copyin(src, dest, size, 0)) \ - return (EFAULT) + if (ddi_copyin((src), (dest), (size), 0)) { \ + DRM_ERROR("%s: copy from user failed", __func__); \ + return (EFAULT); \ + } #define DRM_COPYTO_WITH_RETURN(dest, src, size) \ - if (ddi_copyout((src), (dest), (size), 0)) \ - return (EFAULT) + if (ddi_copyout((src), (dest), (size), 0)) { \ + DRM_ERROR("%s: copy to user failed", __func__); \ + return (EFAULT); \ + } #define DRM_COPY_FROM_USER(dest, src, size) \ ddi_copyin((src), (dest), (size), 0) /* flag for src */ @@ -222,6 +228,8 @@ mutex_exit(&(q)->lock); \ } +#define jiffies ddi_get_lbolt() + #define DRM_WAIT_ON(ret, q, timeout, condition) \ mutex_enter(&(q)->lock); \ while (!(condition)) { \ @@ -239,6 +247,21 @@ } \ mutex_exit(&(q)->lock); +#define DRM_WAIT(ret, q, condition) \ +mutex_enter(&(q)->lock); \ +if (!(condition)) { \ + ret = cv_timedwait_sig(&(q)->cv, &(q)->lock, jiffies + 30 * DRM_HZ); \ + if (ret == -1) { \ + /* gfx maybe hang */ \ + if (!(condition)) \ + ret = -2; \ + } else { \ + ret = 0; \ + } \ +} \ +mutex_exit(&(q)->lock); + + #define DRM_GETSAREA() \ { \ drm_local_map_t *map; \ @@ -255,8 +278,8 @@ #define LOCK_TEST_WITH_RETURN(dev, fpriv) \ if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) || \ dev->lock.filp != fpriv) { \ - DRM_ERROR("called without lock held"); \ - return (EINVAL); \ + DRM_DEBUG("%s called without lock held", __func__); \ + return (EINVAL); \ } #define DRM_IRQ_ARGS caddr_t arg @@ -281,6 +304,11 @@ #define PAGE_ALIGN(addr) (((addr) + DRM_PAGE_SIZE - 1) & DRM_PAGE_MASK) #define DRM_SUSER(p) (crgetsgid(p) == 0 || crgetsuid(p) == 0) +#define DRM_GEM_OBJIDR_HASHNODE 1024 +#define idr_list_for_each(entry, head) \ + for (int key = 0; key < DRM_GEM_OBJIDR_HASHNODE; key++) \ + list_for_each(entry, &(head)->next[key]) + /* * wait for 400 milliseconds */ @@ -378,6 +406,88 @@ } drm_buf_entry_t; typedef TAILQ_HEAD(drm_file_list, drm_file) drm_file_list_t; + +/* BEGIN CSTYLED */ +typedef struct drm_local_map { + unsigned long offset; /* Physical address (0 for SAREA) */ + unsigned long size; /* Physical size (bytes) */ + drm_map_type_t type; /* Type of memory mapped */ + drm_map_flags_t flags; /* Flags */ + void *handle; /* User-space: "Handle" to pass to mmap */ + /* Kernel-space: kernel-virtual address */ + int mtrr; /* Boolean: MTRR used */ + /* Private data */ + int rid; /* PCI resource ID for bus_space */ + int kernel_owned; /* Boolean: 1= initmapped, 0= addmapped */ + caddr_t dev_addr; /* base device address */ + ddi_acc_handle_t dev_handle; /* The data access handle */ + ddi_umem_cookie_t drm_umem_cookie; /* For SAREA alloc and free */ + TAILQ_ENTRY(drm_local_map) link; +} drm_local_map_t; +/* END CSTYLED */ + +/* + * This structure defines the drm_mm memory object, which will be used by the + * DRM for its buffer objects. + */ +struct drm_gem_object { + /* Reference count of this object */ + atomic_t refcount; + + /* Handle count of this object. Each handle also holds a reference */ + atomic_t handlecount; + + /* Related drm device */ + struct drm_device *dev; + + int flink; + /* + * Size of the object, in bytes. Immutable over the object's + * lifetime. + */ + size_t size; + + /* + * Global name for this object, starts at 1. 0 means unnamed. + * Access is covered by the object_name_lock in the related drm_device + */ + int name; + + /* + * Memory domains. These monitor which caches contain read/write data + * related to the object. When transitioning from one set of domains + * to another, the driver is called to ensure that caches are suitably + * flushed and invalidated + */ + uint32_t read_domains; + uint32_t write_domain; + + /* + * While validating an exec operation, the + * new read/write domain values are computed here. + * They will be transferred to the above values + * at the point that any cache flushing occurs + */ + uint32_t pending_read_domains; + uint32_t pending_write_domain; + + void *driver_private; + + drm_local_map_t *map; + ddi_dma_handle_t dma_hdl; + ddi_acc_handle_t acc_hdl; + caddr_t kaddr; + size_t real_size; /* real size of memory */ + pfn_t *pfnarray; +}; + +struct idr_list { + struct idr_list *next, *prev; + struct drm_gem_object *obj; + uint32_t handle; + caddr_t contain_ptr; +}; + struct drm_file { TAILQ_ENTRY(drm_file) link; int authenticated; @@ -389,6 +499,13 @@ drm_magic_t magic; unsigned long ioctl_count; void *driver_priv; + /* Mapping of mm object handles to object pointers. */ + struct idr_list object_idr; + /* Lock for synchronization of access to object_idr. */ + kmutex_t table_lock; + + dev_t dev; + cred_t *credp; }; typedef struct drm_lock_data { @@ -467,26 +584,26 @@ drm_dma_handle_t *dmah_gart; /* Handle to PCI memory */ } drm_sg_mem_t; -typedef TAILQ_HEAD(drm_map_list, drm_local_map) drm_map_list_t; +/* + * Generic memory manager structs + */ -/* BEGIN CSTYLED */ -typedef struct drm_local_map { - unsigned long offset; /* Physical address (0 for SAREA) */ - unsigned long size; /* Physical size (bytes) */ - drm_map_type_t type; /* Type of memory mapped */ - drm_map_flags_t flags; /* Flags */ - void *handle; /* User-space: "Handle" to pass to mmap */ - /* Kernel-space: kernel-virtual address */ - int mtrr; /* Boolean: MTRR used */ - /* Private data */ - int rid; /* PCI resource ID for bus_space */ - int kernel_owned; /* Boolean: 1= initmapped, 0= addmapped */ - caddr_t dev_addr; /* base device address */ - ddi_acc_handle_t dev_handle; /* The data access handle */ - ddi_umem_cookie_t drm_umem_cookie; /* For SAREA alloc and free */ - TAILQ_ENTRY(drm_local_map) link; -} drm_local_map_t; -/* END CSTYLED */ +struct drm_mm_node { + struct list_head fl_entry; + struct list_head ml_entry; + int free; + unsigned long start; + unsigned long size; + struct drm_mm *mm; + void *private; +}; + +struct drm_mm { + struct list_head fl_entry; + struct list_head ml_entry; +}; + +typedef TAILQ_HEAD(drm_map_list, drm_local_map) drm_map_list_t; typedef TAILQ_HEAD(drm_vbl_sig_list, drm_vbl_sig) drm_vbl_sig_list_t; typedef struct drm_vbl_sig { @@ -556,6 +673,16 @@ int (*enable_vblank)(struct drm_device *dev, int crtc); void (*disable_vblank)(struct drm_device *dev, int crtc); + /* + * Driver-specific constructor for drm_gem_objects, to set up + * obj->driver_private. + * + * Returns 0 on success. + */ + int (*gem_init_object) (struct drm_gem_object *obj); + void (*gem_free_object) (struct drm_gem_object *obj); + + drm_ioctl_desc_t *driver_ioctls; int max_driver_ioctl; @@ -577,6 +704,7 @@ unsigned use_vbl_irq :1; unsigned use_vbl_irq2 :1; unsigned use_mtrr :1; + unsigned use_gem; }; /* @@ -607,11 +735,12 @@ int flags; /* Flags to open(2) */ /* Locks */ - kmutex_t vbl_lock; /* protects vblank operations */ - kmutex_t dma_lock; /* protects dev->dma */ - kmutex_t irq_lock; /* protects irq condition checks */ - kmutex_t dev_lock; /* protects everything else */ + kmutex_t vbl_lock; /* protects vblank operations */ + kmutex_t dma_lock; /* protects dev->dma */ + kmutex_t irq_lock; /* protects irq condition checks */ + kmutex_t dev_lock; /* protects everything else */ drm_lock_data_t lock; /* Information on hardware lock */ + kmutex_t struct_mutex; /* < For others */ /* Usage Counters */ int open_count; /* Outstanding files open */ @@ -651,7 +780,12 @@ drm_vbl_sig_list_t vbl_sig_list; drm_vbl_sig_list_t vbl_sig_list2; - + /* + * At load time, disabling the vblank interrupt won't be allowed since + * old clients may not call the modeset ioctl and therefore misbehave. + * Once the modeset ioctl *has* been called though, we can safely + * disable them when unused. + */ int vblank_disable_allowed; wait_queue_head_t vbl_queue; /* vbl wait channel */ @@ -672,13 +806,13 @@ u32 *last_vblank; /* so we don't call enable more than */ atomic_t *vblank_enabled; - /* for compensation of spurious wraparounds */ - u32 *vblank_premodeset; + /* Display driver is setting mode */ + int *vblank_inmodeset; /* Don't wait while crtc is likely disabled */ - int *vblank_suspend; + int *vblank_suspend; /* size of vblank counter register */ - u32 max_vblank_count; - int num_crtcs; + u32 max_vblank_count; + int num_crtcs; kmutex_t tasklet_lock; void (*locked_tasklet_func)(struct drm_device *dev); @@ -698,6 +832,22 @@ u32 *drw_bitfield; unsigned int drw_info_length; drm_drawable_info_t **drw_info; + + /* \name GEM information */ + /* @{ */ + kmutex_t object_name_lock; + struct idr_list object_name_idr; + atomic_t object_count; + atomic_t object_memory; + atomic_t pin_count; + atomic_t pin_memory; + atomic_t gtt_count; + atomic_t gtt_memory; + uint32_t gtt_total; + uint32_t invalidate_domains; /* domains pending invalidation */ + uint32_t flush_domains; /* domains pending flush */ + /* @} */ + /* * Saving S3 context */ @@ -767,8 +917,8 @@ int drm_vblank_get(struct drm_device *dev, int crtc); void drm_vblank_put(struct drm_device *dev, int crtc); int drm_vblank_init(struct drm_device *dev, int num_crtcs); -void drm_locked_tasklet(drm_device_t *, void(*func)(drm_device_t *)); void drm_vblank_cleanup(struct drm_device *dev); +int drm_modeset_ctl(DRM_IOCTL_ARGS); /* AGP/GART support (drm_agpsupport.c) */ int drm_device_is_agp(drm_device_t *); @@ -776,10 +926,21 @@ drm_agp_head_t *drm_agp_init(drm_device_t *); void drm_agp_fini(drm_device_t *); int drm_agp_do_release(drm_device_t *); -void *drm_agp_allocate_memory(size_t, uint32_t); -int drm_agp_free_memory(void *); +void *drm_agp_allocate_memory(size_t pages, + uint32_t type, drm_device_t *dev); +int drm_agp_free_memory(agp_allocate_t *handle, drm_device_t *dev); int drm_agp_bind_memory(unsigned int, uint32_t, drm_device_t *); int drm_agp_unbind_memory(unsigned long, drm_device_t *); +int drm_agp_bind_pages(drm_device_t *dev, + pfn_t *pages, + unsigned long num_pages, + uint32_t gtt_offset); +int drm_agp_unbind_pages(drm_device_t *dev, + unsigned long num_pages, + uint32_t gtt_offset, + uint32_t type); +void drm_agp_chipset_flush(struct drm_device *dev); +void drm_agp_rebind(struct drm_device *dev); /* kstat support (drm_kstats.c) */ int drm_init_kstats(drm_device_t *); @@ -797,6 +958,8 @@ int drm_unlock(DRM_IOCTL_ARGS); int drm_version(DRM_IOCTL_ARGS); int drm_setversion(DRM_IOCTL_ARGS); +/* Cache management (drm_cache.c) */ +void drm_clflush_pages(caddr_t *pages, unsigned long num_pages); /* Misc. IOCTL support (drm_ioctl.c) */ int drm_irq_by_busid(DRM_IOCTL_ARGS); @@ -858,9 +1021,16 @@ int drm_sg_alloc(DRM_IOCTL_ARGS); int drm_sg_free(DRM_IOCTL_ARGS); +/* drm_mm.c */ +struct drm_mm_node *drm_mm_get_block(struct drm_mm_node *parent, + unsigned long size, unsigned alignment); +struct drm_mm_node *drm_mm_search_free(const struct drm_mm *mm, + unsigned long size, + unsigned alignment, int best_match); + +extern void drm_mm_clean_ml(const struct drm_mm *mm); extern int drm_debug_flag; - /* We add function to support DRM_DEBUG,DRM_ERROR,DRM_INFO */ extern void drm_debug(const char *fmt, ...); extern void drm_error(const char *fmt, ...); @@ -905,4 +1075,30 @@ extern int drm_open_helper(drm_device_t *, drm_cminor_t *, int, int, cred_t *); +/* Graphics Execution Manager library functions (drm_gem.c) */ +int drm_gem_init(struct drm_device *dev); +void drm_gem_object_free(struct drm_gem_object *obj); +struct drm_gem_object *drm_gem_object_alloc(struct drm_device *dev, + size_t size); +void drm_gem_object_handle_free(struct drm_gem_object *obj); + +void drm_gem_object_reference(struct drm_gem_object *obj); +void drm_gem_object_unreference(struct drm_gem_object *obj); + +int drm_gem_handle_create(struct drm_file *file_priv, + struct drm_gem_object *obj, + int *handlep); +void drm_gem_object_handle_reference(struct drm_gem_object *obj); + +void drm_gem_object_handle_unreference(struct drm_gem_object *obj); + +struct drm_gem_object *drm_gem_object_lookup(struct drm_file *filp, + int handle); +int drm_gem_close_ioctl(DRM_IOCTL_ARGS); +int drm_gem_flink_ioctl(DRM_IOCTL_ARGS); +int drm_gem_open_ioctl(DRM_IOCTL_ARGS); +void drm_gem_open(struct drm_file *file_private); +void drm_gem_release(struct drm_device *dev, struct drm_file *file_private); + + #endif /* _DRMP_H */
--- a/usr/src/uts/common/io/drm/drm_agpsupport.c Fri Dec 04 16:46:47 2009 -0800 +++ b/usr/src/uts/common/io/drm/drm_agpsupport.c Sat Dec 05 13:25:40 2009 +0800 @@ -1,5 +1,5 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -10,6 +10,7 @@ /* * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * Copyright (c) 2009, Intel Corporation. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -37,8 +38,6 @@ * */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include "drm.h" #include "drmP.h" @@ -193,6 +192,7 @@ dev->agp->mode = modes.mode; setup.agps_mode = (uint32_t)modes.mode; + DRM_DEBUG("drm_agp_enable: dev->agp->mode=%lx", modes.mode); ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_SETUP, @@ -205,6 +205,7 @@ dev->agp->base = dev->agp->agp_info.agpi_aperbase; dev->agp->enabled = 1; + DRM_DEBUG("drm_agp_enable: dev->agp->base=0x%lx", dev->agp->base); return (0); } @@ -247,6 +248,8 @@ dev->agp->memory->prev = entry; dev->agp->memory = entry; + DRM_DEBUG("entry->phys_addr %lx", entry->phys_addr); + /* physical is used only by i810 driver */ request.physical = alloc.agpa_physical; request.handle = (unsigned long)entry->handle; @@ -383,7 +386,6 @@ drm_agp_head_t *agp = NULL; int retval, rval; - DRM_DEBUG("drm_agp_init\n"); agp = kmem_zalloc(sizeof (drm_agp_head_t), KM_SLEEP); retval = ldi_ident_from_dip(dev->dip, &agp->agpgart_li); @@ -437,14 +439,14 @@ /*ARGSUSED*/ void * -drm_agp_allocate_memory(size_t pages, uint32_t type) +drm_agp_allocate_memory(size_t pages, uint32_t type, drm_device_t *dev) { return (NULL); } /*ARGSUSED*/ int -drm_agp_free_memory(void *handle) +drm_agp_free_memory(agp_allocate_t *handle, drm_device_t *dev) { return (1); } @@ -494,3 +496,92 @@ entry->bound = 0; return (0); } + +/* + * Binds a collection of pages into AGP memory at the given offset, returning + * the AGP memory structure containing them. + * + * No reference is held on the pages during this time -- it is up to the + * caller to handle that. + */ +int +drm_agp_bind_pages(drm_device_t *dev, + pfn_t *pages, + unsigned long num_pages, + uint32_t gtt_offset) +{ + + agp_bind_pages_t bind; + int ret, rval; + + bind.agpb_pgstart = gtt_offset / AGP_PAGE_SIZE; + bind.agpb_pgcount = num_pages; + bind.agpb_pages = pages; + ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_PAGES_BIND, + (intptr_t)&bind, FKIOCTL, kcred, &rval); + if (ret) { + DRM_ERROR("AGPIOC_PAGES_BIND failed ret %d", ret); + return (ret); + } + return (0); +} + +int +drm_agp_unbind_pages(drm_device_t *dev, + unsigned long num_pages, + uint32_t gtt_offset, + uint32_t type) +{ + + agp_unbind_pages_t unbind; + int ret, rval; + + unbind.agpb_pgstart = gtt_offset / AGP_PAGE_SIZE; + unbind.agpb_pgcount = num_pages; + unbind.agpb_type = type; + ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_PAGES_UNBIND, + (intptr_t)&unbind, FKIOCTL, kcred, &rval); + if (ret) { + DRM_DEBUG("drm_agp_unbind_pages AGPIOC_PAGES_UNBIND failed"); + return (ret); + } + return (0); +} + +/* + * Certain Intel chipsets contains a global write buffer, and this can require + * flushing from the drm or X.org to make sure all data has hit RAM before + * initiating a GPU transfer, due to a lack of coherency with the integrated + * graphics device and this buffer. + */ +void +drm_agp_chipset_flush(struct drm_device *dev) +{ + int ret, rval; + + DRM_DEBUG("agp_chipset_flush"); + ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_FLUSHCHIPSET, + (intptr_t)0, FKIOCTL, kcred, &rval); + if (ret != 0) { + DRM_ERROR("Failed to drm_agp_chipset_flush ret %d", ret); + } +} + +/* + * The pages are evict on suspend, so re-bind it at resume time + */ +void +drm_agp_rebind(struct drm_device *dev) +{ + int ret, rval; + + if (!dev->agp) { + return; + } + + ret = ldi_ioctl(dev->agp->agpgart_lh, AGPIOC_PAGES_REBIND, + (intptr_t)0, FKIOCTL, kcred, &rval); + if (ret != 0) { + DRM_ERROR("rebind failed %d", ret); + } +}
--- a/usr/src/uts/common/io/drm/drm_atomic.h Fri Dec 04 16:46:47 2009 -0800 +++ b/usr/src/uts/common/io/drm/drm_atomic.h Sat Dec 05 13:25:40 2009 +0800 @@ -11,6 +11,7 @@ /* * Copyright 2004 Eric Anholt + * Copyright (c) 2009, Intel Corporation. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -57,7 +58,7 @@ #define atomic_inc(p) atomic_add_int(p, 1) #define atomic_dec(p) atomic_dec_uint(p) #define atomic_add(n, p) atomic_add_int(p, n) -#define atomic_sub(n, p) atomic_dec_uint(p, n) +#define atomic_sub(n, p) atomic_add_int(p, -n) #define atomic_set_int(p, bits) atomic_or_uint(p, bits) #define atomic_clear_int(p, bits) atomic_and_uint(p, ~(bits)) #define atomic_cmpset_int(p, c, n) \
--- a/usr/src/uts/common/io/drm/drm_bufs.c Fri Dec 04 16:46:47 2009 -0800 +++ b/usr/src/uts/common/io/drm/drm_bufs.c Sat Dec 05 13:25:40 2009 +0800 @@ -5,6 +5,7 @@ /* * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * Copyright (c) 2009, Intel Corporation. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -170,7 +171,7 @@ break; case _DRM_CONSISTENT: - cmn_err(CE_WARN, "%d DRM_AGP_CONSISTENT", __LINE__); + DRM_ERROR("%d DRM_AGP_CONSISTENT", __LINE__); return (ENOTSUP); case _DRM_AGP: map->offset += dev->agp->base;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/io/drm/drm_cache.c Sat Dec 05 13:25:40 2009 +0800 @@ -0,0 +1,67 @@ +/* + * + * Copyright(c) 2006-2007 Tungsten Graphics, Inc., Cedar Park, TX., USA + * Copyright (c) 2009, Intel Corporation. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files(the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice(including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +/* + * Authors: Thomas Hellström <thomas-at-tungstengraphics-dot-com> + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <sys/x86_archext.h> +#include <vm/seg_kmem.h> +#include "drmP.h" + +extern void clflush_insn(caddr_t addr); +extern void mfence_insn(void); + +static void +drm_clflush_page(caddr_t page) +{ + unsigned int i; + + if (page == NULL) + return; + + for (i = 0; i < PAGE_SIZE; i += x86_clflush_size) + clflush_insn(page + i); + mfence_insn(); +} + +void +drm_clflush_pages(caddr_t *pages, unsigned long num_pages) +{ + + if (x86_feature & X86_CLFSH) { + unsigned long i; + + for (i = 0; i < num_pages; i++) + drm_clflush_page(pages[i]); + } +}
--- a/usr/src/uts/common/io/drm/drm_drv.c Fri Dec 04 16:46:47 2009 -0800 +++ b/usr/src/uts/common/io/drm/drm_drv.c Sat Dec 05 13:25:40 2009 +0800 @@ -5,6 +5,7 @@ /* * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * Copyright (c) 2009, Intel Corporation. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -61,6 +62,14 @@ {drm_getstats, 0}, [DRM_IOCTL_NR(DRM_IOCTL_SET_VERSION)] = {drm_setversion, DRM_MASTER|DRM_ROOT_ONLY}, + [DRM_IOCTL_NR(DRM_IOCTL_MODESET_CTL)] = + {drm_modeset_ctl, 0}, + [DRM_IOCTL_NR(DRM_IOCTL_GEM_CLOSE)] = + {drm_gem_close_ioctl, 0}, + [DRM_IOCTL_NR(DRM_IOCTL_GEM_FLINK)] = + {drm_gem_flink_ioctl, DRM_AUTH}, + [DRM_IOCTL_NR(DRM_IOCTL_GEM_OPEN)] = + {drm_gem_open_ioctl, DRM_AUTH}, [DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)] = {drm_setunique, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY}, [DRM_IOCTL_NR(DRM_IOCTL_BLOCK)] = @@ -141,6 +150,8 @@ {drm_update_draw, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY}, }; +extern void idr_list_free(struct idr_list *head); + const char * drm_find_description(int vendor, int device, drm_pci_id_list_t *idlist) { @@ -263,7 +274,7 @@ if (entry->bound) (void) drm_agp_unbind_memory( (unsigned long)entry->handle, dev); - (void) drm_agp_free_memory(entry->handle); + (void) drm_agp_free_memory(entry->handle, dev); drm_free(entry, sizeof (*entry), DRM_MEM_AGPLISTS); } dev->agp->memory = NULL; @@ -337,6 +348,15 @@ goto error; } + if (dev->driver->use_gem == 1) { + retcode = drm_gem_init(dev); + if (retcode) { + DRM_ERROR("Cannot initialize graphics execution " + "manager (GEM)\n"); + goto error; + } + } + if (drm_init_kstats(dev)) { DRM_ERROR("drm_attach => drm_load: init kstats error"); retcode = EFAULT; @@ -375,6 +395,11 @@ drm_ctxbitmap_cleanup(dev); + if (dev->driver->use_gem == 1) { + idr_list_free(&dev->object_name_idr); + mutex_destroy(&dev->object_name_lock); + } + DRM_LOCK(); (void) drm_lastclose(dev); DRM_UNLOCK(); @@ -393,6 +418,10 @@ mutex_destroy(&dev->dev_lock); mutex_destroy(&dev->drw_lock); mutex_destroy(&dev->tasklet_lock); + + dev->gtt_total = 0; + atomic_set(&dev->pin_memory, 0); + DRM_ERROR("drm_unload"); } @@ -464,12 +493,17 @@ "retake lock not implemented yet"); } - if (dev->driver->use_dma) + if (dev->driver->use_dma) { drm_reclaim_buffers(dev, fpriv); + } + if (dev->driver->use_gem == 1) { + drm_gem_release(dev, fpriv); + } - if (dev->driver->postclose != NULL) + if (dev->driver->postclose != NULL) { dev->driver->postclose(dev, fpriv); + } TAILQ_REMOVE(&dev->files, fpriv, link); drm_free(fpriv, sizeof (*fpriv), DRM_MEM_FILES);
--- a/usr/src/uts/common/io/drm/drm_fops.c Fri Dec 04 16:46:47 2009 -0800 +++ b/usr/src/uts/common/io/drm/drm_fops.c Sat Dec 05 13:25:40 2009 +0800 @@ -1,5 +1,5 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -11,6 +11,7 @@ /*- * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * Copyright (c) 2009, Intel Corporation. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -41,8 +42,6 @@ /* END CSTYLED */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include "drmP.h" /*ARGSUSED*/ @@ -110,6 +109,9 @@ /* for compatibility root is always authenticated */ priv->authenticated = DRM_SUSER(credp); + if (dev->driver->use_gem == 1) + drm_gem_open(priv); + if (dev->driver->open) { retcode = dev->driver->open(dev, priv); if (retcode != 0) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/io/drm/drm_gem.c Sat Dec 05 13:25:40 2009 +0800 @@ -0,0 +1,698 @@ +/* + * Copyright (c) 2009, Intel Corporation. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Eric Anholt <eric@anholt.net> + * + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <vm/anon.h> +#include <vm/seg_kmem.h> +#include <vm/seg_kp.h> +#include <vm/seg_map.h> +#include <sys/fcntl.h> +#include <sys/vnode.h> +#include <sys/file.h> +#include <sys/bitmap.h> +#include <sys/ddi.h> +#include <sys/sunddi.h> +#include <gfx_private.h> +#include "drmP.h" +#include "drm.h" + +/* + * @file drm_gem.c + * + * This file provides some of the base ioctls and library routines for + * the graphics memory manager implemented by each device driver. + * + * Because various devices have different requirements in terms of + * synchronization and migration strategies, implementing that is left up to + * the driver, and all that the general API provides should be generic -- + * allocating objects, reading/writing data with the cpu, freeing objects. + * Even there, platform-dependent optimizations for reading/writing data with + * the CPU mean we'll likely hook those out to driver-specific calls. However, + * the DRI2 implementation wants to have at least allocate/mmap be generic. + * + * The goal was to have swap-backed object allocation managed through + * struct file. However, file descriptors as handles to a struct file have + * two major failings: + * - Process limits prevent more than 1024 or so being used at a time by + * default. + * - Inability to allocate high fds will aggravate the X Server's select() + * handling, and likely that of many GL client applications as well. + * + * This led to a plan of using our own integer IDs(called handles, following + * DRM terminology) to mimic fds, and implement the fd syscalls we need as + * ioctls. The objects themselves will still include the struct file so + * that we can transition to fds if the required kernel infrastructure shows + * up at a later date, and as our interface with shmfs for memory allocation. + */ + +void +idr_list_init(struct idr_list *head) +{ + struct idr_list *entry; + /* HASH for accelerate */ + entry = kmem_zalloc(DRM_GEM_OBJIDR_HASHNODE + * sizeof (struct idr_list), KM_NOSLEEP); + head->next = entry; + for (int i = 0; i < DRM_GEM_OBJIDR_HASHNODE; i++) { + INIT_LIST_HEAD(&entry[i]); + } +} + +int +idr_list_get_new_above(struct idr_list *head, + struct drm_gem_object *obj, + int *handlep) +{ + struct idr_list *entry; + int key; + entry = kmem_zalloc(sizeof (*entry), KM_NOSLEEP); + key = obj->name % DRM_GEM_OBJIDR_HASHNODE; + list_add(entry, &head->next[key], NULL); + entry->obj = obj; + entry->handle = obj->name; + *handlep = obj->name; + return (0); +} + +struct drm_gem_object * +idr_list_find(struct idr_list *head, + uint32_t name) +{ + struct idr_list *entry; + int key; + key = name % DRM_GEM_OBJIDR_HASHNODE; + + list_for_each(entry, &head->next[key]) { + if (entry->handle == name) + return (entry->obj); + } + return (NULL); +} + +int +idr_list_remove(struct idr_list *head, + uint32_t name) +{ + struct idr_list *entry, *temp; + int key; + key = name % DRM_GEM_OBJIDR_HASHNODE; + list_for_each_safe(entry, temp, &head->next[key]) { + if (entry->handle == name) { + list_del(entry); + kmem_free(entry, sizeof (*entry)); + return (0); + } + } + DRM_ERROR("Failed to remove the object %d", name); + return (-1); +} + +void +idr_list_free(struct idr_list *head) +{ + struct idr_list *entry, *temp; + for (int key = 0; key < DRM_GEM_OBJIDR_HASHNODE; key++) { + list_for_each_safe(entry, temp, &head->next[key]) { + list_del(entry); + kmem_free(entry, sizeof (*entry)); + } + } + kmem_free(head->next, + DRM_GEM_OBJIDR_HASHNODE * sizeof (struct idr_list)); + head->next = NULL; +} + +int +idr_list_empty(struct idr_list *head) +{ + int empty; + for (int key = 0; key < DRM_GEM_OBJIDR_HASHNODE; key++) { + empty = list_empty(&(head)->next[key]); + if (!empty) + return (empty); + } + return (1); +} + +static uint32_t shfile_name = 0; +#define SHFILE_NAME_MAX 0xffffffff + +/* + * will be set to 1 for 32 bit x86 systems only, in startup.c + */ +extern int segkp_fromheap; +extern ulong_t *segkp_bitmap; + +void +drm_gem_object_reference(struct drm_gem_object *obj) +{ + atomic_inc(&obj->refcount); +} + +void +drm_gem_object_unreference(struct drm_gem_object *obj) +{ + if (obj == NULL) + return; + + atomic_sub(1, &obj->refcount); + if (obj->refcount == 0) + drm_gem_object_free(obj); +} + +void +drm_gem_object_handle_reference(struct drm_gem_object *obj) +{ + drm_gem_object_reference(obj); + atomic_inc(&obj->handlecount); +} + +void +drm_gem_object_handle_unreference(struct drm_gem_object *obj) +{ + if (obj == NULL) + return; + + /* + * Must bump handle count first as this may be the last + * ref, in which case the object would disappear before we + * checked for a name + */ + atomic_sub(1, &obj->handlecount); + if (obj->handlecount == 0) + drm_gem_object_handle_free(obj); + drm_gem_object_unreference(obj); +} + +/* + * Initialize the GEM device fields + */ + +int +drm_gem_init(struct drm_device *dev) +{ + mutex_init(&dev->object_name_lock, NULL, MUTEX_DRIVER, NULL); + idr_list_init(&dev->object_name_idr); + + atomic_set(&dev->object_count, 0); + atomic_set(&dev->object_memory, 0); + atomic_set(&dev->pin_count, 0); + atomic_set(&dev->pin_memory, 0); + atomic_set(&dev->gtt_count, 0); + atomic_set(&dev->gtt_memory, 0); + return (0); +} + +/* + * Allocate a GEM object of the specified size with shmfs backing store + */ +struct drm_gem_object * +drm_gem_object_alloc(struct drm_device *dev, size_t size) +{ + static ddi_dma_attr_t dma_attr = { + DMA_ATTR_V0, + 0U, /* dma_attr_addr_lo */ + 0xffffffffU, /* dma_attr_addr_hi */ + 0xffffffffU, /* dma_attr_count_max */ + 4096, /* dma_attr_align */ + 0x1fffU, /* dma_attr_burstsizes */ + 1, /* dma_attr_minxfer */ + 0xffffffffU, /* dma_attr_maxxfer */ + 0xffffffffU, /* dma_attr_seg */ + 1, /* dma_attr_sgllen, variable */ + 4, /* dma_attr_granular */ + 0 /* dma_attr_flags */ + }; + static ddi_device_acc_attr_t acc_attr = { + DDI_DEVICE_ATTR_V0, + DDI_NEVERSWAP_ACC, + DDI_MERGING_OK_ACC + }; + struct drm_gem_object *obj; + ddi_dma_cookie_t cookie; + uint_t cookie_cnt; + drm_local_map_t *map; + + pgcnt_t real_pgcnt, pgcnt = btopr(size); + uint32_t paddr, cookie_end; + int i, n; + + obj = kmem_zalloc(sizeof (struct drm_gem_object), KM_NOSLEEP); + if (obj == NULL) + return (NULL); + + obj->dev = dev; + obj->flink = 0; + obj->size = size; + + if (shfile_name == SHFILE_NAME_MAX) { + DRM_ERROR("No name space for object"); + goto err1; + } else { + obj->name = ++shfile_name; + } + + dma_attr.dma_attr_sgllen = (int)pgcnt; + + if (ddi_dma_alloc_handle(dev->dip, &dma_attr, + DDI_DMA_DONTWAIT, NULL, &obj->dma_hdl)) { + DRM_ERROR("drm_gem_object_alloc: " + "ddi_dma_alloc_handle failed"); + goto err1; + } + if (ddi_dma_mem_alloc(obj->dma_hdl, ptob(pgcnt), &acc_attr, + IOMEM_DATA_UC_WR_COMBINE, DDI_DMA_DONTWAIT, NULL, + &obj->kaddr, &obj->real_size, &obj->acc_hdl)) { + DRM_ERROR("drm_gem_object_alloc: " + "ddi_dma_mem_alloc failed"); + goto err2; + } + if (ddi_dma_addr_bind_handle(obj->dma_hdl, NULL, + obj->kaddr, obj->real_size, DDI_DMA_RDWR, + DDI_DMA_DONTWAIT, NULL, &cookie, &cookie_cnt) + != DDI_DMA_MAPPED) { + DRM_ERROR("drm_gem_object_alloc: " + "ddi_dma_addr_bind_handle failed"); + goto err3; + } + + real_pgcnt = btopr(obj->real_size); + + obj->pfnarray = kmem_zalloc(real_pgcnt * sizeof (pfn_t), KM_NOSLEEP); + if (obj->pfnarray == NULL) { + goto err4; + } + for (n = 0, i = 1; ; i++) { + for (paddr = cookie.dmac_address, + cookie_end = cookie.dmac_address + cookie.dmac_size; + paddr < cookie_end; + paddr += PAGESIZE) { + obj->pfnarray[n++] = btop(paddr); + if (n >= real_pgcnt) + goto addmap; + } + if (i >= cookie_cnt) + break; + ddi_dma_nextcookie(obj->dma_hdl, &cookie); + } + +addmap: + map = drm_alloc(sizeof (struct drm_local_map), DRM_MEM_MAPS); + if (map == NULL) { + goto err5; + } + + map->handle = obj; + map->offset = (uintptr_t)map->handle; + map->offset &= 0xffffffffUL; + map->dev_addr = map->handle; + map->size = obj->real_size; + map->type = _DRM_TTM; + map->flags = _DRM_WRITE_COMBINING | _DRM_REMOVABLE; + map->drm_umem_cookie = + gfxp_umem_cookie_init(obj->kaddr, obj->real_size); + if (map->drm_umem_cookie == NULL) { + goto err6; + } + + obj->map = map; + + atomic_set(&obj->refcount, 1); + atomic_set(&obj->handlecount, 1); + if (dev->driver->gem_init_object != NULL && + dev->driver->gem_init_object(obj) != 0) { + goto err7; + } + atomic_inc(&dev->object_count); + atomic_add(obj->size, &dev->object_memory); + + return (obj); + +err7: + gfxp_umem_cookie_destroy(map->drm_umem_cookie); +err6: + drm_free(map, sizeof (struct drm_local_map), DRM_MEM_MAPS); +err5: + kmem_free(obj->pfnarray, real_pgcnt * sizeof (pfn_t)); +err4: + (void) ddi_dma_unbind_handle(obj->dma_hdl); +err3: + ddi_dma_mem_free(&obj->acc_hdl); +err2: + ddi_dma_free_handle(&obj->dma_hdl); +err1: + kmem_free(obj, sizeof (struct drm_gem_object)); + + return (NULL); +} + +/* + * Removes the mapping from handle to filp for this object. + */ +static int +drm_gem_handle_delete(struct drm_file *filp, int handle) +{ + struct drm_device *dev; + struct drm_gem_object *obj; + int err; + /* + * This is gross. The idr system doesn't let us try a delete and + * return an error code. It just spews if you fail at deleting. + * So, we have to grab a lock around finding the object and then + * doing the delete on it and dropping the refcount, or the user + * could race us to double-decrement the refcount and cause a + * use-after-free later. Given the frequency of our handle lookups, + * we may want to use ida for number allocation and a hash table + * for the pointers, anyway. + */ + spin_lock(&filp->table_lock); + + /* Check if we currently have a reference on the object */ + obj = idr_list_find(&filp->object_idr, handle); + if (obj == NULL) { + spin_unlock(&filp->table_lock); + DRM_ERROR("obj %d is not in tne list, failed to close", handle); + return (EINVAL); + } + dev = obj->dev; + + /* Release reference and decrement refcount. */ + err = idr_list_remove(&filp->object_idr, handle); + if (err == -1) + DRM_ERROR("%s", __func__); + + spin_unlock(&filp->table_lock); + + spin_lock(&dev->struct_mutex); + drm_gem_object_handle_unreference(obj); + spin_unlock(&dev->struct_mutex); + return (0); +} + +/* + * Create a handle for this object. This adds a handle reference + * to the object, which includes a regular reference count. Callers + * will likely want to dereference the object afterwards. + */ +int +drm_gem_handle_create(struct drm_file *file_priv, + struct drm_gem_object *obj, + int *handlep) +{ + int ret; + + /* + * Get the user-visible handle using idr. + */ +again: + /* ensure there is space available to allocate a handle */ + + /* do the allocation under our spinlock */ + spin_lock(&file_priv->table_lock); + ret = idr_list_get_new_above(&file_priv->object_idr, obj, handlep); + spin_unlock(&file_priv->table_lock); + if (ret == -EAGAIN) + goto again; + + if (ret != 0) { + DRM_ERROR("Failed to create handle"); + return (ret); + } + + drm_gem_object_handle_reference(obj); + return (0); +} + +/* Returns a reference to the object named by the handle. */ +struct drm_gem_object * +drm_gem_object_lookup(struct drm_file *filp, + int handle) +{ + struct drm_gem_object *obj; + + spin_lock(&filp->table_lock); + + /* Check if we currently have a reference on the object */ + obj = idr_list_find(&filp->object_idr, handle); + if (obj == NULL) { + spin_unlock(&filp->table_lock); + DRM_ERROR("object_lookup failed, handle %d", handle); + return (NULL); + } + + drm_gem_object_reference(obj); + + spin_unlock(&filp->table_lock); + + return (obj); +} + +/* + * Releases the handle to an mm object. + */ +/*ARGSUSED*/ +int +drm_gem_close_ioctl(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + struct drm_gem_close args; + int ret; + + if (!(dev->driver->use_gem == 1)) + return (ENODEV); + + DRM_COPYFROM_WITH_RETURN(&args, + (void *)data, sizeof (args)); + + ret = drm_gem_handle_delete(fpriv, args.handle); + + return (ret); +} + +/* + * Create a global name for an object, returning the name. + * + * Note that the name does not hold a reference; when the object + * is freed, the name goes away. + */ +/*ARGSUSED*/ +int +drm_gem_flink_ioctl(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + struct drm_gem_flink args; + struct drm_gem_object *obj; + int ret, handle; + + if (!(dev->driver->use_gem == 1)) + return (ENODEV); + + DRM_COPYFROM_WITH_RETURN(&args, + (void *)data, sizeof (args)); + obj = drm_gem_object_lookup(fpriv, args.handle); + if (obj == NULL) + return (EINVAL); + handle = args.handle; + spin_lock(&dev->object_name_lock); + if (!obj->flink) { + /* only creat a node in object_name_idr, no update anything */ + ret = idr_list_get_new_above(&dev->object_name_idr, + obj, &handle); + obj->flink = obj->name; + /* Allocate a reference for the name table. */ + drm_gem_object_reference(obj); + } + /* + * Leave the reference from the lookup around as the + * name table now holds one + */ + args.name = obj->name; + + spin_unlock(&dev->object_name_lock); + ret = DRM_COPY_TO_USER((void *) data, &args, sizeof (args)); + if (ret != 0) + DRM_ERROR(" gem flink error! %d", ret); + + spin_lock(&dev->struct_mutex); + drm_gem_object_unreference(obj); + spin_unlock(&dev->struct_mutex); + + return (ret); +} + +/* + * Open an object using the global name, returning a handle and the size. + * + * This handle (of course) holds a reference to the object, so the object + * will not go away until the handle is deleted. + */ +/*ARGSUSED*/ +int +drm_gem_open_ioctl(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + struct drm_gem_open args; + struct drm_gem_object *obj; + int ret; + int handle; + + if (!(dev->driver->use_gem == 1)) { + DRM_ERROR("Not support GEM"); + return (ENODEV); + } + DRM_COPYFROM_WITH_RETURN(&args, + (void *) data, sizeof (args)); + + spin_lock(&dev->object_name_lock); + + obj = idr_list_find(&dev->object_name_idr, args.name); + + if (obj) + drm_gem_object_reference(obj); + spin_unlock(&dev->object_name_lock); + if (!obj) { + DRM_ERROR("Can't find the obj %d", args.name); + return (ENOENT); + } + + ret = drm_gem_handle_create(fpriv, obj, &handle); + spin_lock(&dev->struct_mutex); + drm_gem_object_unreference(obj); + spin_unlock(&dev->struct_mutex); + + args.handle = args.name; + args.size = obj->size; + + ret = DRM_COPY_TO_USER((void *) data, &args, sizeof (args)); + if (ret != 0) + DRM_ERROR(" gem open error! %d", ret); + return (ret); +} + +/* + * Called at device open time, sets up the structure for handling refcounting + * of mm objects. + */ +void +drm_gem_open(struct drm_file *file_private) +{ + idr_list_init(&file_private->object_idr); + mutex_init(&file_private->table_lock, NULL, MUTEX_DRIVER, NULL); +} + +/* + * Called at device close to release the file's + * handle references on objects. + */ +static void +drm_gem_object_release_handle(struct drm_gem_object *obj) +{ + drm_gem_object_handle_unreference(obj); +} + +/* + * Called at close time when the filp is going away. + * + * Releases any remaining references on objects by this filp. + */ +void +drm_gem_release(struct drm_device *dev, struct drm_file *file_private) +{ + struct idr_list *entry; + spin_lock(&dev->struct_mutex); + + idr_list_for_each(entry, &file_private->object_idr) + drm_gem_object_release_handle(entry->obj); + + idr_list_free(&file_private->object_idr); + spin_unlock(&dev->struct_mutex); + +} + +/* + * Called after the last reference to the object has been lost. + * + * Frees the object + */ +void +drm_gem_object_free(struct drm_gem_object *obj) +{ + struct drm_device *dev = obj->dev; + struct drm_local_map *map = obj->map; + + if (dev->driver->gem_free_object != NULL) + dev->driver->gem_free_object(obj); + + gfxp_umem_cookie_destroy(map->drm_umem_cookie); + drm_free(map, sizeof (struct drm_local_map), DRM_MEM_MAPS); + + kmem_free(obj->pfnarray, btopr(obj->real_size) * sizeof (pfn_t)); + + (void) ddi_dma_unbind_handle(obj->dma_hdl); + ddi_dma_mem_free(&obj->acc_hdl); + ddi_dma_free_handle(&obj->dma_hdl); + + atomic_dec(&dev->object_count); + atomic_sub(obj->size, &dev->object_memory); + kmem_free(obj, sizeof (struct drm_gem_object)); +} + +/* + * Called after the last handle to the object has been closed + * + * Removes any name for the object. Note that this must be + * called before drm_gem_object_free or we'll be touching + * freed memory + */ +void +drm_gem_object_handle_free(struct drm_gem_object *obj) +{ + int err; + struct drm_device *dev = obj->dev; + /* Remove any name for this object */ + spin_lock(&dev->object_name_lock); + if (obj->flink) { + err = idr_list_remove(&dev->object_name_idr, obj->name); + if (err == -1) + DRM_ERROR("%s", __func__); + obj->flink = 0; + spin_unlock(&dev->object_name_lock); + /* + * The object name held a reference to this object, drop + * that now. + */ + drm_gem_object_unreference(obj); + } else + + spin_unlock(&dev->object_name_lock); + +}
--- a/usr/src/uts/common/io/drm/drm_irq.c Fri Dec 04 16:46:47 2009 -0800 +++ b/usr/src/uts/common/io/drm/drm_irq.c Sat Dec 05 13:25:40 2009 +0800 @@ -4,6 +4,7 @@ */ /* * Copyright 2003 Eric Anholt + * Copyright (c) 2009, Intel Corporation. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -119,6 +120,8 @@ dev->num_crtcs, DRM_MEM_DRIVER); drm_free(dev->last_vblank, sizeof (u32) * dev->num_crtcs, DRM_MEM_DRIVER); + drm_free(dev->vblank_inmodeset, sizeof (*dev->vblank_inmodeset) * + dev->num_crtcs, DRM_MEM_DRIVER); dev->num_crtcs = 0; } @@ -159,6 +162,12 @@ dev->last_vblank = drm_alloc(num_crtcs * sizeof (u32), DRM_MEM_DRIVER); if (!dev->last_vblank) goto err; + + dev->vblank_inmodeset = drm_alloc(num_crtcs * sizeof (int), + DRM_MEM_DRIVER); + if (!dev->vblank_inmodeset) + goto err; + /* Zero per-crtc vblank stuff */ for (i = 0; i < num_crtcs; i++) { DRM_INIT_WAITQUEUE(&dev->vbl_queues[i], DRM_INTR_PRI(dev)); @@ -380,6 +389,70 @@ DRM_SPINUNLOCK(&dev->vbl_lock); } +/* + * drm_modeset_ctl - handle vblank event counter changes across mode switch + * @DRM_IOCTL_ARGS: standard ioctl arguments + * + * Applications should call the %_DRM_PRE_MODESET and %_DRM_POST_MODESET + * ioctls around modesetting so that any lost vblank events are accounted for. + * + * Generally the counter will reset across mode sets. If interrupts are + * enabled around this call, we don't have to do anything since the counter + * will have already been incremented. + */ +/*ARGSUSED*/ +int +drm_modeset_ctl(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + struct drm_modeset_ctl modeset; + int crtc, ret = 0; + + /* If drm_vblank_init() hasn't been called yet, just no-op */ + if (!dev->num_crtcs) + goto out; + + DRM_COPYFROM_WITH_RETURN(&modeset, (void *)data, + sizeof (modeset)); + + crtc = modeset.crtc; + if (crtc >= dev->num_crtcs) { + ret = -EINVAL; + goto out; + } + + /* + * To avoid all the problems that might happen if interrupts + * were enabled/disabled around or between these calls, we just + * have the kernel take a reference on the CRTC (just once though + * to avoid corrupting the count if multiple, mismatch calls occur), + * so that interrupts remain enabled in the interim. + */ + switch (modeset.cmd) { + case _DRM_PRE_MODESET: + if (!dev->vblank_inmodeset[crtc]) { + dev->vblank_inmodeset[crtc] = 1; + ret = drm_vblank_get(dev, crtc); + } + break; + case _DRM_POST_MODESET: + if (dev->vblank_inmodeset[crtc]) { + DRM_SPINLOCK(&dev->vbl_lock); + dev->vblank_disable_allowed = 1; + dev->vblank_inmodeset[crtc] = 0; + DRM_SPINUNLOCK(&dev->vbl_lock); + drm_vblank_put(dev, crtc); + } + break; + default: + ret = -EINVAL; + break; + } + +out: + return (ret); +} + /*ARGSUSED*/ int drm_wait_vblank(DRM_IOCTL_ARGS) @@ -390,7 +463,7 @@ unsigned int sequence; if (!dev->irq_enabled) { - DRM_DEBUG("wait vblank, EINVAL"); + DRM_ERROR("wait vblank, EINVAL"); return (EINVAL); } #ifdef _MULTI_DATAMODEL @@ -411,19 +484,20 @@ if (vblwait.request.type & ~(_DRM_VBLANK_TYPES_MASK | _DRM_VBLANK_FLAGS_MASK)) { - cmn_err(CE_WARN, "drm_wait_vblank: wrong request type 0x%x", + DRM_ERROR("drm_wait_vblank: wrong request type 0x%x", vblwait.request.type); return (EINVAL); } flags = vblwait.request.type & _DRM_VBLANK_FLAGS_MASK; crtc = flags & _DRM_VBLANK_SECONDARY ? 1 : 0; - if (crtc >= dev->num_crtcs) + if (crtc >= dev->num_crtcs) { + DRM_ERROR("wait vblank operation not support"); return (ENOTSUP); - + } ret = drm_vblank_get(dev, crtc); if (ret) { - DRM_DEBUG("can't get drm vblank"); + DRM_ERROR("can't get drm vblank %d", ret); return (ret); } sequence = drm_vblank_count(dev, crtc); @@ -449,7 +523,7 @@ /* * Don't block process, send signal when vblank interrupt */ - DRM_DEBUG("NOT SUPPORT YET, SHOULD BE ADDED"); + DRM_ERROR("NOT SUPPORT YET, SHOULD BE ADDED"); cmn_err(CE_WARN, "NOT SUPPORT YET, SHOULD BE ADDED"); ret = EINVAL; goto done; @@ -457,8 +531,9 @@ /* block until vblank interupt */ /* shared code returns -errno */ DRM_WAIT_ON(ret, &dev->vbl_queues[crtc], 3 * DRM_HZ, - ((drm_vblank_count(dev, crtc) - - vblwait.request.sequence) <= (1 << 23))); + (((drm_vblank_count(dev, crtc) + - vblwait.request.sequence) <= (1 << 23)) || + !dev->irq_enabled)); if (ret != EINTR) { struct timeval now; (void) uniqtime(&now); @@ -503,33 +578,4 @@ { atomic_inc(&dev->_vblank_count[crtc]); DRM_WAKEUP(&dev->vbl_queues[crtc]); - drm_vbl_send_signals(dev); } - -/* - * Schedule a tasklet to call back a driver hook with the HW lock held. - * - * \param dev DRM device. - * \param func Driver callback. - * - * This is intended for triggering actions that require the HW lock from an - * interrupt handler. The lock will be grabbed ASAP after the interrupt handler - * completes. Note that the callback may be called from interrupt or process - * context, it must not make any assumptions about this. Also, the HW lock will - * be held with the kernel context or any client context. - */ - -void -drm_locked_tasklet(drm_device_t *dev, void (*func)(drm_device_t *)) -{ - mutex_enter(&dev->tasklet_lock); - - if (dev->locked_tasklet_func) { - mutex_exit(&dev->tasklet_lock); - return; - } - - dev->locked_tasklet_func = func; - - mutex_exit(&dev->tasklet_lock); -}
--- a/usr/src/uts/common/io/drm/drm_linux_list.h Fri Dec 04 16:46:47 2009 -0800 +++ b/usr/src/uts/common/io/drm/drm_linux_list.h Sat Dec 05 13:25:40 2009 +0800 @@ -5,6 +5,7 @@ /* * - * Copyright 2003 Eric Anholt + * Copyright (c) 2009, Intel Corporation. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -31,33 +32,48 @@ * */ -#pragma ident "%Z%%M% %I% %E% SMI" +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ #ifndef _DRM_LINUX_LIST_H_ #define _DRM_LINUX_LIST_H_ struct list_head { struct list_head *next, *prev; + caddr_t contain_ptr; }; /* Cheat, assume the list_head is at the start of the struct */ -#define list_entry(entry, type, member) (type *)(entry) +#define list_entry(entry, type, member) (type *)(uintptr_t)(entry->contain_ptr) #define INIT_LIST_HEAD(head) { \ (head)->next = head; \ (head)->prev = head; \ + (head)->contain_ptr = (caddr_t)head; \ } -#define list_add_tail(entry, head) { \ +#define list_add(entry, head, con_ptr) { \ + (head)->next->prev = entry; \ + (entry)->next = (head)->next; \ + (entry)->prev = head; \ + (head)->next = entry; \ + (entry)->contain_ptr = con_ptr; \ +} + +#define list_add_tail(entry, head, con_ptr) { \ (entry)->prev = (head)->prev; \ (entry)->next = head; \ (head)->prev->next = entry; \ (head)->prev = entry; \ + (entry)->contain_ptr = con_ptr; \ } #define list_del(entry) { \ (entry)->next->prev = (entry)->prev; \ (entry)->prev->next = (entry)->next; \ + (entry)->contain_ptr = NULL; \ } #define list_for_each(entry, head) \ @@ -65,7 +81,19 @@ #define list_for_each_safe(entry, temp, head) \ for (entry = (head)->next, temp = (entry)->next; \ - temp != head; \ + entry != head; \ entry = temp, temp = temp->next) +#define list_del_init(entry) { \ + list_del(entry); \ + INIT_LIST_HEAD(entry); \ +} + +#define list_move_tail(entry, head, con_ptr) { \ + list_del(entry); \ + list_add_tail(entry, head, con_ptr); \ +} + +#define list_empty(head) ((head)->next == head) + #endif /* _DRM_LINUX_LIST_H_ */
--- a/usr/src/uts/common/io/drm/drm_memory.c Fri Dec 04 16:46:47 2009 -0800 +++ b/usr/src/uts/common/io/drm/drm_memory.c Sat Dec 05 13:25:40 2009 +0800 @@ -1,5 +1,5 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -10,6 +10,7 @@ /* * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * Copyright (c) 2009, Intel Corporation. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -37,8 +38,6 @@ * */ -#pragma ident "%Z%%M% %I% %E% SMI" - #include "drmP.h" /* Device memory access structure */ @@ -135,6 +134,7 @@ break; } } + kmem_free(regs, (size_t)length); return (regnum); error: @@ -204,8 +204,8 @@ map->dev_addr = iomap.drm_base; DRM_DEBUG( - "map->handle is %p map->dev_addr is %lx", - (void *)map->handle, (unsigned long)map->dev_addr); + "map->handle is %p map->dev_addr is %lx map->size %x", + (void *)map->handle, (unsigned long)map->dev_addr, map->size); return (0); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/common/io/drm/drm_mm.c Sat Dec 05 13:25:40 2009 +0800 @@ -0,0 +1,336 @@ +/* + * + * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA. + * Copyright (c) 2009, Intel Corporation. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files(the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice(including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * + */ + +/* + * Generic simple memory manager implementation. Intended to be used as a base + * class implementation for more advanced memory managers. + * + * Note that the algorithm used is quite simple and there might be substantial + * performance gains if a smarter free list is implemented. + * Currently it is just an + * unordered stack of free regions. This could easily be improved if an RB-tree + * is used instead. At least if we expect heavy fragmentation. + * + * Aligned allocations can also see improvement. + * + * Authors: + * Thomas Hellström <thomas-at-tungstengraphics-dot-com> + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include "drmP.h" + +unsigned long +drm_mm_tail_space(struct drm_mm *mm) +{ + struct list_head *tail_node; + struct drm_mm_node *entry; + + tail_node = mm->ml_entry.prev; + entry = list_entry(tail_node, struct drm_mm_node, ml_entry); + if (!entry->free) + return (0); + + return (entry->size); +} + +int +drm_mm_remove_space_from_tail(struct drm_mm *mm, unsigned long size) +{ + struct list_head *tail_node; + struct drm_mm_node *entry; + + tail_node = mm->ml_entry.prev; + entry = list_entry(tail_node, struct drm_mm_node, ml_entry); + if (!entry->free) + return (ENOMEM); + + if (entry->size <= size) + return (ENOMEM); + + entry->size -= size; + return (0); +} + + +static int +drm_mm_create_tail_node(struct drm_mm *mm, + unsigned long start, + unsigned long size) +{ + struct drm_mm_node *child; + + child = (struct drm_mm_node *) + drm_alloc(sizeof (*child), DRM_MEM_MM); + if (!child) + return (ENOMEM); + + child->free = 1; + child->size = size; + child->start = start; + child->mm = mm; + + list_add_tail(&child->ml_entry, &mm->ml_entry, (caddr_t)child); + list_add_tail(&child->fl_entry, &mm->fl_entry, (caddr_t)child); + + return (0); +} + + +int +drm_mm_add_space_to_tail(struct drm_mm *mm, unsigned long size) +{ + struct list_head *tail_node; + struct drm_mm_node *entry; + + tail_node = mm->ml_entry.prev; + entry = list_entry(tail_node, struct drm_mm_node, ml_entry); + if (!entry->free) { + return (drm_mm_create_tail_node(mm, + entry->start + entry->size, size)); + } + entry->size += size; + return (0); +} + +static struct drm_mm_node * +drm_mm_split_at_start(struct drm_mm_node *parent, + unsigned long size) +{ + struct drm_mm_node *child; + + child = (struct drm_mm_node *) + drm_alloc(sizeof (*child), DRM_MEM_MM); + if (!child) + return (NULL); + + INIT_LIST_HEAD(&child->fl_entry); + + child->free = 0; + child->size = size; + child->start = parent->start; + child->mm = parent->mm; + + list_add_tail(&child->ml_entry, &parent->ml_entry, (caddr_t)child); + INIT_LIST_HEAD(&child->fl_entry); + + parent->size -= size; + parent->start += size; + return (child); +} + +/* + * Put a block. Merge with the previous and / or next block if they are free. + * Otherwise add to the free stack. + */ + +void +drm_mm_put_block(struct drm_mm_node *cur) +{ + + struct drm_mm *mm = cur->mm; + struct list_head *cur_head = &cur->ml_entry; + struct list_head *root_head = &mm->ml_entry; + struct drm_mm_node *prev_node = NULL; + struct drm_mm_node *next_node; + + int merged = 0; + + if (cur_head->prev != root_head) { + prev_node = list_entry(cur_head->prev, + struct drm_mm_node, ml_entry); + if (prev_node->free) { + prev_node->size += cur->size; + merged = 1; + } + } + if (cur_head->next != root_head) { + next_node = list_entry(cur_head->next, + struct drm_mm_node, ml_entry); + if (next_node->free) { + if (merged) { + prev_node->size += next_node->size; + list_del(&next_node->ml_entry); + list_del(&next_node->fl_entry); + drm_free(next_node, + sizeof (*next_node), DRM_MEM_MM); + } else { + next_node->size += cur->size; + next_node->start = cur->start; + merged = 1; + } + } + } + if (!merged) { + cur->free = 1; + list_add(&cur->fl_entry, &mm->fl_entry, (caddr_t)cur); + } else { + list_del(&cur->ml_entry); + drm_free(cur, sizeof (*cur), DRM_MEM_MM); + } +} + +struct drm_mm_node * +drm_mm_get_block(struct drm_mm_node *parent, + unsigned long size, + unsigned alignment) +{ + + struct drm_mm_node *align_splitoff = NULL; + struct drm_mm_node *child; + unsigned tmp = 0; + + if (alignment) + tmp = parent->start % alignment; + + if (tmp) { + align_splitoff = drm_mm_split_at_start(parent, alignment - tmp); + if (!align_splitoff) + return (NULL); + } + + if (parent->size == size) { + list_del_init(&parent->fl_entry); + parent->free = 0; + return (parent); + } else { + child = drm_mm_split_at_start(parent, size); + } + + if (align_splitoff) + drm_mm_put_block(align_splitoff); + + return (child); +} + +struct drm_mm_node * +drm_mm_search_free(const struct drm_mm *mm, + unsigned long size, + unsigned alignment, + int best_match) +{ + struct list_head *list; + const struct list_head *free_stack = &mm->fl_entry; + struct drm_mm_node *entry; + struct drm_mm_node *best; + unsigned long best_size; + unsigned wasted; + + best = NULL; + best_size = ~0UL; + + list_for_each(list, free_stack) { + entry = list_entry(list, struct drm_mm_node, fl_entry); + wasted = 0; + + if (entry->size < size) + continue; + + if (alignment) { + register unsigned tmp = entry->start % alignment; + if (tmp) + wasted += alignment - tmp; + } + + + if (entry->size >= size + wasted) { + if (!best_match) + return (entry); + if (size < best_size) { + best = entry; + best_size = entry->size; + } + } + } + + return (best); +} + +int +drm_mm_clean(struct drm_mm *mm) +{ + struct list_head *head = &mm->ml_entry; + + return (head->next->next == head); +} + +int +drm_mm_init(struct drm_mm *mm, unsigned long start, unsigned long size) +{ + INIT_LIST_HEAD(&mm->ml_entry); + INIT_LIST_HEAD(&mm->fl_entry); + + return (drm_mm_create_tail_node(mm, start, size)); +} + + +void +drm_mm_takedown(struct drm_mm *mm) +{ + struct list_head *bnode = mm->fl_entry.next; + struct drm_mm_node *entry; + + entry = list_entry(bnode, struct drm_mm_node, fl_entry); + + if (entry->ml_entry.next != &mm->ml_entry || + entry->fl_entry.next != &mm->fl_entry) { + DRM_ERROR("Memory manager not clean. Delaying takedown\n"); + return; + } + + list_del(&entry->fl_entry); + list_del(&entry->ml_entry); + + drm_free(entry, sizeof (*entry), DRM_MEM_MM); +} + +void +drm_mm_clean_ml(const struct drm_mm *mm) +{ + const struct list_head *mlstack = &mm->ml_entry; + struct list_head *list, *temp; + struct drm_mm_node *entry; + + if (mlstack->next == NULL) + return; + + list_for_each_safe(list, temp, mlstack) { + entry = list_entry(list, struct drm_mm_node, ml_entry); + DRM_DEBUG("ml_entry 0x%x, size 0x%x, start 0x%x", + entry, entry->size, entry->start); + + list_del(&entry->fl_entry); + list_del(&entry->ml_entry); + drm_free(entry, sizeof (*entry), DRM_MEM_MM); + } +}
--- a/usr/src/uts/common/io/drm/drm_sunmod.c Fri Dec 04 16:46:47 2009 -0800 +++ b/usr/src/uts/common/io/drm/drm_sunmod.c Sat Dec 05 13:25:40 2009 +0800 @@ -20,7 +20,7 @@ */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -98,7 +98,6 @@ D_NEW | D_MTSAFE |D_DEVMAP /* cb_flag */ }; - int _init(void) { @@ -499,6 +498,9 @@ ((ioctl->flags & DRM_MASTER) && !fpriv->master)) return (EACCES); + fpriv->dev = dev; + fpriv->credp = credp; + retval = func(dp, arg, fpriv, mode); return (retval); @@ -514,7 +516,7 @@ drm_inst_state_t *mstate; drm_device_t *dp; ddi_umem_cookie_t cookie; - drm_local_map_t *map; + drm_local_map_t *map = NULL; unsigned long aperbase; u_offset_t handle; offset_t koff; @@ -528,6 +530,11 @@ DDI_NEVERSWAP_ACC, DDI_STRICTORDER_ACC, }; + static ddi_device_acc_attr_t gem_dev_attr = { + DDI_DEVICE_ATTR_V0, + DDI_NEVERSWAP_ACC, + DDI_MERGING_OK_ACC + }; mstate = drm_sup_devt_to_state(dev); if (mstate == NULL) @@ -560,29 +567,55 @@ return (EINVAL); } - /* - * We will solve 32-bit application on 64-bit kernel - * issue later, now, we just use low 32-bit - */ - handle = (u_offset_t)offset; - handle &= 0xffffffff; mutex_enter(&dp->dev_lock); - TAILQ_FOREACH(map, &dp->maplist, link) { - if (handle == - ((u_offset_t)((uintptr_t)map->handle) & 0xffffffff)) - break; + + if (dp->driver->use_gem == 1) { + struct idr_list *entry; + drm_cminor_t *mp; + + mp = drm_find_file_by_minor(dp, minor); + if (!mp) { + mutex_exit(&dp->dev_lock); + DRM_ERROR("drm_sun_devmap: can't find authenticator"); + return (EACCES); + } + + spin_lock(&dp->struct_mutex); + idr_list_for_each(entry, &(mp->fpriv->object_idr)) { + if ((uintptr_t)entry->obj == (u_offset_t)offset) { + map = entry->obj->map; + goto goon; + } + } +goon: + spin_unlock(&dp->struct_mutex); } - /* - * Temporarily, because offset is phys_addr for register - * and framebuffer, is kernel virtual_addr for others - * Maybe we will use hash table to solve this issue later. - */ if (map == NULL) { + /* + * We will solve 32-bit application on 64-bit kernel + * issue later, now, we just use low 32-bit + */ + handle = (u_offset_t)offset; + handle &= 0xffffffff; + TAILQ_FOREACH(map, &dp->maplist, link) { - if (handle == (map->offset & 0xffffffff)) + if (handle == + ((u_offset_t)((uintptr_t)map->handle) & 0xffffffff)) break; } + + /* + * Temporarily, because offset is phys_addr for register + * and framebuffer, is kernel virtual_addr for others + * Maybe we will use hash table to solve this issue later. + */ + if (map == NULL) { + TAILQ_FOREACH(map, &dp->maplist, link) { + if (handle == (map->offset & 0xffffffff)) + break; + } + } } if (map == NULL) { @@ -704,6 +737,20 @@ *maplen = length; break; + case _DRM_TTM: + if (map->drm_umem_cookie == NULL) + return (EINVAL); + + if (gfxp_devmap_umem_setup(dhp, dp->dip, + NULL, map->drm_umem_cookie, 0, map->size, PROT_ALL, + IOMEM_DATA_UC_WR_COMBINE | DEVMAP_ALLOW_REMAP, + &gem_dev_attr)) { + cmn_err(CE_WARN, "devmap:failed, retval=%d", ret); + return (DDI_FAILURE); + } + *maplen = map->size; + return (DDI_SUCCESS); + default: return (DDI_FAILURE); }
--- a/usr/src/uts/common/sys/agp/agpdefs.h Fri Dec 04 16:46:47 2009 -0800 +++ b/usr/src/uts/common/sys/agp/agpdefs.h Sat Dec 05 13:25:40 2009 +0800 @@ -20,6 +20,11 @@ */ /* + * Copyright (c) 2009, Intel Corporation. + * All Rights Reserved. + */ + +/* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -103,6 +108,7 @@ #define INTEL_BR_Q45 0x2e108086 #define INTEL_BR_G45 0x2e208086 #define INTEL_BR_G41 0x2e308086 +#define INTEL_BR_B43 0x2e408086 /* AGP common register offset in pci configuration space */ #define AGP_CONF_MISC 0x51 /* one byte */ @@ -172,6 +178,36 @@ #define INTEL_IGD_Q45 0x2e128086 #define INTEL_IGD_G45 0x2e228086 #define INTEL_IGD_G41 0x2e328086 +#define INTEL_IGD_B43 0x2e428086 + +/* Intel 915 and 945 series */ +#define IS_INTEL_915(device) ((device == INTEL_IGD_915) || \ + (device == INTEL_IGD_915GM) || \ + (device == INTEL_IGD_945) || \ + (device == INTEL_IGD_945GM) || \ + (device == INTEL_IGD_945GME)) + +/* Intel 965 series */ +#define IS_INTEL_965(device) ((device == INTEL_IGD_946GZ) || \ + (device == INTEL_IGD_965G1) || \ + (device == INTEL_IGD_965Q) || \ + (device == INTEL_IGD_965G2) || \ + (device == INTEL_IGD_965GM) || \ + (device == INTEL_IGD_965GME) || \ + (device == INTEL_IGD_GM45) || \ + IS_INTEL_G4X(device)) + +/* Intel G33 series */ +#define IS_INTEL_X33(device) ((device == INTEL_IGD_Q35) || \ + (device == INTEL_IGD_G33) || \ + (device == INTEL_IGD_Q33)) + +/* Intel G4X series */ +#define IS_INTEL_G4X(device) ((device == INTEL_IGD_EL) || \ + (device == INTEL_IGD_Q45) || \ + (device == INTEL_IGD_G45) || \ + (device == INTEL_IGD_G41) || \ + (device == INTEL_IGD_B43)) /* register offsets in PCI config space */ #define I8XX_CONF_GMADR 0x10 /* GMADR of i8xx series */ @@ -296,6 +332,7 @@ #define GIGA_MASK 0xC0000000 #define UI32_MASK 0xffffffffU #define MAXAPERMEGAS 0x1000 /* Aper size no more than 4G */ +#define MINAPERMEGAS 192 #endif /* _KERNEL */
--- a/usr/src/uts/common/sys/agp/agpgart_impl.h Fri Dec 04 16:46:47 2009 -0800 +++ b/usr/src/uts/common/sys/agp/agpgart_impl.h Sat Dec 05 13:25:40 2009 +0800 @@ -1,13 +1,16 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright (c) 2009, Intel Corporation. + * All Rights Reserved. + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SYS_AGPGART_IMPL_H #define _SYS_AGPGART_IMPL_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif @@ -123,6 +126,12 @@ } agp_info32_t; #endif /* _MULTI_DATAMODEL */ +struct list_head { + struct list_head *next, *prev; + struct igd_gtt_seg *gttseg; +}; + + typedef struct agpgart_softstate { dev_info_t *asoft_dip; kmutex_t asoft_instmutex; @@ -147,6 +156,7 @@ /* all registered agp device in here */ agp_registered_dev_t asoft_devreg; kstat_t *asoft_ksp; + struct list_head mapped_list; } agpgart_softstate_t; typedef struct agpgart_ctx {
--- a/usr/src/uts/common/sys/agp/agpmaster_io.h Fri Dec 04 16:46:47 2009 -0800 +++ b/usr/src/uts/common/sys/agp/agpmaster_io.h Sat Dec 05 13:25:40 2009 +0800 @@ -20,15 +20,18 @@ */ /* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Copyright (c) 2009, Intel Corporation. + * All Rights Reserved. + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SYS_AGPMASTER_IO_H #define _SYS_AGPMASTER_IO_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif
--- a/usr/src/uts/common/sys/agp/agptarget_io.h Fri Dec 04 16:46:47 2009 -0800 +++ b/usr/src/uts/common/sys/agp/agptarget_io.h Sat Dec 05 13:25:40 2009 +0800 @@ -1,13 +1,11 @@ /* - * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _SYS_AGPTARGET_IO_H #define _SYS_AGPTARGET_IO_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif @@ -27,6 +25,9 @@ #define AGP_TARGET_FLUSH_GTLB _IO(AGPTARGETIOC_BASE, 35) #define AGP_TARGET_CONFIGURE _IO(AGPTARGETIOC_BASE, 36) #define AGP_TARGET_UNCONFIG _IO(AGPTARGETIOC_BASE, 37) +#define INTEL_CHIPSET_FLUSH_SETUP _IO(AGPTARGETIOC_BASE, 38) +#define INTEL_CHIPSET_FLUSH _IO(AGPTARGETIOC_BASE, 39) +#define INTEL_CHIPSET_FLUSH_FREE _IO(AGPTARGETIOC_BASE, 40) /* Internal agp info struct */ typedef struct _i_agp_info {
--- a/usr/src/uts/common/sys/agpgart.h Fri Dec 04 16:46:47 2009 -0800 +++ b/usr/src/uts/common/sys/agpgart.h Sat Dec 05 13:25:40 2009 +0800 @@ -1,10 +1,11 @@ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright (c) 2000 Doug Rabson + * Copyright (c) 2009, Intel Corporation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -33,8 +34,6 @@ #ifndef _SYS_AGPGART_H #define _SYS_AGPGART_H -#pragma ident "%Z%%M% %I% %E% SMI" - #ifdef __cplusplus extern "C" { #endif @@ -69,6 +68,18 @@ uint32_t agpa_physical; /* for i810 only, private */ } agp_allocate_t; +typedef struct _agp_bind_pages { + uint32_t agpb_pgstart; + pfn_t *agpb_pages; + unsigned long agpb_pgcount; +} agp_bind_pages_t; + +typedef struct _agp_unbind_pages { + uint32_t agpb_pgstart; + unsigned long agpb_pgcount; + uint32_t agpb_type; +} agp_unbind_pages_t; + typedef struct _agp_bind { int32_t agpb_key; uint32_t agpb_pgstart; @@ -92,6 +103,10 @@ #define AGPIOC_IOREMAP_FREE _IO(AGPIOC_BASE, 9) #define AGPIOC_READ _IO(AGPIOC_BASE, 10) #define AGPIOC_WRITE _IO(AGPIOC_BASE, 11) +#define AGPIOC_FLUSHCHIPSET _IO(AGPIOC_BASE, 12) +#define AGPIOC_PAGES_BIND _IOW(AGPIOC_BASE, 13, agp_bind_pages_t) +#define AGPIOC_PAGES_UNBIND _IOW(AGPIOC_BASE, 14, agp_unbind_pages_t) +#define AGPIOC_PAGES_REBIND _IO(AGPIOC_BASE, 15) /* AGP status register bits definition */ #define AGPSTAT_RQ_MASK 0xff000000 /* target only */
--- a/usr/src/uts/intel/Makefile.files Fri Dec 04 16:46:47 2009 -0800 +++ b/usr/src/uts/intel/Makefile.files Sat Dec 05 13:25:40 2009 +0800 @@ -146,7 +146,8 @@ GDA_OBJS += gda.o GHD_OBJS += ghd.o ghd_debug.o ghd_dma.o ghd_queue.o ghd_scsa.o \ ghd_scsi.o ghd_timer.o ghd_waitq.o ghd_gcmd.o -I915_OBJS += i915_dma.o i915_drv.o i915_irq.o i915_mem.o +I915_OBJS += i915_dma.o i915_drv.o i915_irq.o i915_mem.o \ + i915_gem.o i915_gem_debug.o i915_gem_tiling.o NSKERN_OBJS += nsc_asm.o PCICFG_OBJS += pcicfg.o PCI_PCINEXUS_OBJS += pci_pci.o
--- a/usr/src/uts/intel/agptarget/Makefile Fri Dec 04 16:46:47 2009 -0800 +++ b/usr/src/uts/intel/agptarget/Makefile Sat Dec 05 13:25:40 2009 +0800 @@ -1,12 +1,10 @@ # -# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # uts/intel/agptarget/Makefile # # -#pragma ident "%Z%%M% %I% %E% SMI" -# # This makefile drives the framework of agp protocol # (agptarget) kernel module. # @@ -24,6 +22,11 @@ ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE) # +# dependency +# +LDFLAGS += -dy -Nmisc/busra + +# # Include common rules. # include $(UTSBASE)/intel/Makefile.intel
--- a/usr/src/uts/intel/io/agpgart/agpgart.c Fri Dec 04 16:46:47 2009 -0800 +++ b/usr/src/uts/intel/io/agpgart/agpgart.c Sat Dec 05 13:25:40 2009 +0800 @@ -1,5 +1,10 @@ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * Copyright (c) 2009, Intel Corporation. + * All Rights Reserved. + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* @@ -59,8 +64,55 @@ #define IS_TRUE_AGP(type) (((type) == ARC_INTELAGP) || \ ((type) == ARC_AMD64AGP)) +#define AGP_HASH_NODE 1024 + +static void +list_head_init(struct list_head *head) { + struct list_head *entry, *tmp; + /* HASH for accelerate */ + entry = kmem_zalloc(AGP_HASH_NODE * + sizeof (struct list_head), KM_NOSLEEP); + head->next = entry; + for (int i = 0; i < AGP_HASH_NODE; i++) { + tmp = &entry[i]; + tmp->next = tmp; + tmp->prev = tmp; + tmp->gttseg = NULL; + } +} + +static void +list_head_add_new(struct list_head *head, + igd_gtt_seg_t *gttseg) +{ + struct list_head *entry, *tmp; + int key; + entry = kmem_zalloc(sizeof (*entry), KM_NOSLEEP); + key = gttseg->igs_pgstart % AGP_HASH_NODE; + tmp = &head->next[key]; + tmp->next->prev = entry; + entry->next = tmp->next; + entry->prev = tmp; + tmp->next = entry; + entry->gttseg = gttseg; +} + +static void +list_head_del(struct list_head *entry) { + (entry)->next->prev = (entry)->prev; \ + (entry)->prev->next = (entry)->next; \ + (entry)->gttseg = NULL; \ +} + +#define list_head_for_each_safe(entry, temp, head) \ + for (int key = 0; key < AGP_HASH_NODE; key++) \ + for (entry = (&(head)->next[key])->next, temp = (entry)->next; \ + entry != &(head)->next[key]; \ + entry = temp, temp = temp->next) + + #define agpinfo_default_to_32(v, v32) \ - { \ + { \ (v32).agpi32_version = (v).agpi_version; \ (v32).agpi32_devid = (v).agpi_devid; \ (v32).agpi32_mode = (v).agpi_mode; \ @@ -1134,7 +1186,7 @@ * based on the ammount of physical pages. * The algorithm is: compare the aperture size with 1/4 of total * physical pages, and use the smaller one to for the max available - * pages. + * pages. But the minimum video memory should be 192M. * * Arguments: * aper_size system agp aperture size (in MB) @@ -1145,14 +1197,18 @@ static uint32_t get_max_pages(uint32_t aper_size) { - uint32_t i, j; + uint32_t i, j, size; ASSERT(aper_size <= MAXAPERMEGAS); i = AGP_MB2PAGES(aper_size); j = (physmem >> 2); - return ((i < j) ? i : j); + size = ((i < j) ? i : j); + + if (size < AGP_MB2PAGES(MINAPERMEGAS)) + size = AGP_MB2PAGES(MINAPERMEGAS); + return (size); } /* @@ -2438,6 +2494,8 @@ AGP_MAXKEYS * (sizeof (keytable_ent_t)), KM_SLEEP); + list_head_init(&softstate->mapped_list); + return (DDI_SUCCESS); err4: agp_fini_kstats(softstate); @@ -2484,6 +2542,21 @@ st->asoft_table = 0; } + struct list_head *entry, *temp, *head; + igd_gtt_seg_t *gttseg; + list_head_for_each_safe(entry, temp, &st->mapped_list) { + gttseg = entry->gttseg; + list_head_del(entry); + kmem_free(entry, sizeof (*entry)); + kmem_free(gttseg->igs_phyaddr, + sizeof (uint32_t) * gttseg->igs_npage); + kmem_free(gttseg, sizeof (igd_gtt_seg_t)); + } + head = &st->mapped_list; + kmem_free(head->next, + AGP_HASH_NODE * sizeof (struct list_head)); + head->next = NULL; + ddi_remove_minor_node(dip, AGPGART_DEVNODE); agp_fini_kstats(st); ldi_ident_release(st->asoft_li); @@ -2557,6 +2630,7 @@ int instance = AGP_DEV2INST(*dev); agpgart_softstate_t *softstate; int rc = 0; + uint32_t devid; if (secpolicy_gart_access(credp)) { AGPDB_PRINT2((CE_WARN, "agpgart_open: permission denied")); @@ -2612,6 +2686,21 @@ return (EIO); } + devid = softstate->asoft_info.agpki_mdevid; + if (IS_INTEL_915(devid) || + IS_INTEL_965(devid) || + IS_INTEL_X33(devid) || + IS_INTEL_G4X(devid)) { + rc = ldi_ioctl(softstate->asoft_devreg.agprd_targethdl, + INTEL_CHIPSET_FLUSH_SETUP, 0, FKIOCTL, kcred, 0); + } + if (rc) { + AGPDB_PRINT2((CE_WARN, + "agpgart_open: Intel chipset flush setup error")); + lyr_end(&softstate->asoft_devreg); + mutex_exit(&softstate->asoft_instmutex); + return (EIO); + } mutex_exit(&softstate->asoft_instmutex); return (0); } @@ -2698,6 +2787,8 @@ { int instance = AGP_DEV2INST(dev); agpgart_softstate_t *softstate; + int rc = 0; + uint32_t devid; softstate = ddi_get_soft_state(agpgart_glob_soft_handle, instance); if (softstate == NULL) { @@ -2721,6 +2812,19 @@ release_control(softstate); } + devid = softstate->asoft_info.agpki_mdevid; + if (IS_INTEL_915(devid) || + IS_INTEL_965(devid) || + IS_INTEL_X33(devid) || + IS_INTEL_G4X(devid)) { + rc = ldi_ioctl(softstate->asoft_devreg.agprd_targethdl, + INTEL_CHIPSET_FLUSH_FREE, 0, FKIOCTL, kcred, 0); + } + if (rc) { + AGPDB_PRINT2((CE_WARN, + "agpgart_open: Intel chipset flush free error")); + } + if (lyr_unconfig_devices(&softstate->asoft_devreg)) { AGPDB_PRINT2((CE_WARN, "agpgart_close: lyr_unconfig_device error")); @@ -3020,6 +3124,144 @@ return (0); } +static int +ioctl_agpgart_flush_chipset(agpgart_softstate_t *st) +{ + ldi_handle_t hdl; + uint32_t devid; + int rc = 0; + devid = st->asoft_info.agpki_mdevid; + hdl = st->asoft_devreg.agprd_targethdl; + if (IS_INTEL_915(devid) || + IS_INTEL_965(devid) || + IS_INTEL_X33(devid) || + IS_INTEL_G4X(devid)) { + rc = ldi_ioctl(hdl, INTEL_CHIPSET_FLUSH, 0, FKIOCTL, kcred, 0); + } + return (rc); +} + +static int +ioctl_agpgart_pages_bind(agpgart_softstate_t *st, void *arg, int flags) +{ + agp_bind_pages_t bind_info; + uint32_t pg_offset; + int err = 0; + ldi_handle_t hdl; + uint32_t npages; + igd_gtt_seg_t *gttseg; + uint32_t i; + int rval; + if (ddi_copyin(arg, &bind_info, + sizeof (agp_bind_pages_t), flags) != 0) { + return (EFAULT); + } + + gttseg = (igd_gtt_seg_t *)kmem_zalloc(sizeof (igd_gtt_seg_t), + KM_SLEEP); + + pg_offset = bind_info.agpb_pgstart; + + gttseg->igs_pgstart = pg_offset; + npages = (uint32_t)bind_info.agpb_pgcount; + gttseg->igs_npage = npages; + + gttseg->igs_type = AGP_NORMAL; + gttseg->igs_phyaddr = (uint32_t *)kmem_zalloc + (sizeof (uint32_t) * gttseg->igs_npage, KM_SLEEP); + + for (i = 0; i < npages; i++) { + gttseg->igs_phyaddr[i] = bind_info.agpb_pages[i] << + GTT_PAGE_SHIFT; + } + + hdl = st->asoft_devreg.agprd_masterhdl; + if (ldi_ioctl(hdl, I8XX_ADD2GTT, (intptr_t)gttseg, FKIOCTL, + kcred, &rval)) { + AGPDB_PRINT2((CE_WARN, "ioctl_agpgart_pages_bind: start0x%x", + gttseg->igs_pgstart)); + AGPDB_PRINT2((CE_WARN, "ioctl_agpgart_pages_bind: pages=0x%x", + gttseg->igs_npage)); + AGPDB_PRINT2((CE_WARN, "ioctl_agpgart_pages_bind: type=0x%x", + gttseg->igs_type)); + err = -1; + } + + list_head_add_new(&st->mapped_list, gttseg); + return (err); +} + +static int +ioctl_agpgart_pages_unbind(agpgart_softstate_t *st, void *arg, int flags) +{ + agp_unbind_pages_t unbind_info; + int rval; + ldi_handle_t hdl; + igd_gtt_seg_t *gttseg; + + if (ddi_copyin(arg, &unbind_info, sizeof (unbind_info), flags) != 0) { + return (EFAULT); + } + + struct list_head *entry, *temp; + list_head_for_each_safe(entry, temp, &st->mapped_list) { + if (entry->gttseg->igs_pgstart == unbind_info.agpb_pgstart) { + gttseg = entry->gttseg; + /* not unbind if VT switch */ + if (unbind_info.agpb_type) { + list_head_del(entry); + kmem_free(entry, sizeof (*entry)); + } + break; + } + } + ASSERT(gttseg != NULL); + gttseg->igs_pgstart = unbind_info.agpb_pgstart; + ASSERT(gttseg->igs_npage == unbind_info.agpb_pgcount); + + hdl = st->asoft_devreg.agprd_masterhdl; + if (ldi_ioctl(hdl, I8XX_REM_GTT, (intptr_t)gttseg, FKIOCTL, + kcred, &rval)) + return (-1); + + if (unbind_info.agpb_type) { + kmem_free(gttseg->igs_phyaddr, sizeof (uint32_t) * + gttseg->igs_npage); + kmem_free(gttseg, sizeof (igd_gtt_seg_t)); + } + + return (0); +} + +static int +ioctl_agpgart_pages_rebind(agpgart_softstate_t *st) +{ + int rval; + ldi_handle_t hdl; + igd_gtt_seg_t *gttseg; + int err = 0; + + hdl = st->asoft_devreg.agprd_masterhdl; + struct list_head *entry, *temp; + list_head_for_each_safe(entry, temp, &st->mapped_list) { + gttseg = entry->gttseg; + list_head_del(entry); + kmem_free(entry, sizeof (*entry)); + if (ldi_ioctl(hdl, I8XX_ADD2GTT, (intptr_t)gttseg, FKIOCTL, + kcred, &rval)) { + AGPDB_PRINT2((CE_WARN, "agpgart_pages_rebind errori")); + err = -1; + break; + } + kmem_free(gttseg->igs_phyaddr, sizeof (uint32_t) * + gttseg->igs_npage); + kmem_free(gttseg, sizeof (igd_gtt_seg_t)); + + } + return (err); + +} + /*ARGSUSED*/ static int agpgart_ioctl(dev_t dev, int cmd, intptr_t intarg, int flags, @@ -3065,6 +3307,18 @@ case AGPIOC_UNBIND: retval = ioctl_agpgart_unbind(softstate, arg, flags); break; + case AGPIOC_FLUSHCHIPSET: + retval = ioctl_agpgart_flush_chipset(softstate); + break; + case AGPIOC_PAGES_BIND: + retval = ioctl_agpgart_pages_bind(softstate, arg, flags); + break; + case AGPIOC_PAGES_UNBIND: + retval = ioctl_agpgart_pages_unbind(softstate, arg, flags); + break; + case AGPIOC_PAGES_REBIND: + retval = ioctl_agpgart_pages_rebind(softstate); + break; default: AGPDB_PRINT2((CE_WARN, "agpgart_ioctl: wrong argument")); retval = ENXIO;
--- a/usr/src/uts/intel/io/agpgart/agptarget.c Fri Dec 04 16:46:47 2009 -0800 +++ b/usr/src/uts/intel/io/agpgart/agptarget.c Sat Dec 05 13:25:40 2009 +0800 @@ -20,6 +20,11 @@ */ /* + * Copyright (c) 2009, Intel Corporation. + * All Rights Reserved. + */ + +/* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -32,6 +37,7 @@ #include <sys/stat.h> #include <sys/ddi.h> #include <sys/sunddi.h> +#include <sys/sunndi.h> #include <sys/modctl.h> #include <sys/sunldi.h> #include <sys/pci.h> @@ -44,6 +50,37 @@ #define INST2NODENUM(inst) (inst) #define DEV2INST(dev) (getminor(dev)) +static ddi_device_acc_attr_t dev_attr = { + DDI_DEVICE_ATTR_V0, + DDI_NEVERSWAP_ACC, + DDI_STRICTORDER_ACC, +}; + +static struct _i9xx_private_compat { + uint64_t physical; /* physical address */ + uint_t size; /* size of mapping */ + uint_t regnum; /* register number */ + caddr_t flush_page; /* kernel virtual address */ + ddi_acc_handle_t handle; /* data access handle */ +} i9xx_private; + +#define I915_IFPADDR 0x60 +#define I965_IFPADDR 0x70 + +#define HIADDR(n) ((uint32_t)(((uint64_t)(n) & \ + 0xFFFFFFFF00000000ULL) >> 32)) +#define LOADDR(n) ((uint32_t)((uint64_t)(n) & 0x00000000FFFFFFFF)) + +/* + * Using for GEM to flush the chipset global + * write buffers on certain intel chipset + */ + +static void +intel_chipset_flush_setup(dev_info_t *dip, + ddi_acc_handle_t pci_acc_hdl, + int gms_off); + typedef struct agp_target_softstate { dev_info_t *tsoft_dip; ddi_acc_handle_t tsoft_pcihdl; @@ -376,6 +413,8 @@ {INTEL_BR_G45, I8XX_CONF_GC, I8XX_GC_MODE_MASK, GMS_SIZE(gms_G4X), gms_G4X}, {INTEL_BR_G41, I8XX_CONF_GC, I8XX_GC_MODE_MASK, + GMS_SIZE(gms_G4X), gms_G4X}, + {INTEL_BR_B43, I8XX_CONF_GC, I8XX_GC_MODE_MASK, GMS_SIZE(gms_G4X), gms_G4X} }; static int @@ -489,6 +528,145 @@ return (DDI_SUCCESS); } +static void +intel_chipset_flush_setup(dev_info_t *dip, + ddi_acc_handle_t pci_acc_hdl, int gms_off) +{ + uint32_t temp_hi, temp_lo; + ndi_ra_request_t request; + uint64_t answer; + uint64_t alen; + pci_regspec_t *regs, *regs2; + int n_reg, length; + uint32_t i, regnum, ret; + ddi_acc_handle_t conf_hdl = pci_acc_hdl; + uint32_t phys_hi_mask = 0; + + bzero((caddr_t)&request, sizeof (ndi_ra_request_t)); + request.ra_flags |= NDI_RA_ALIGN_SIZE | NDI_RA_ALLOC_BOUNDED; + request.ra_boundbase = 0; + request.ra_boundlen = 0xffffffff; + request.ra_len = AGP_PAGE_SIZE; + + /* IS_I965 || IS_G33 || IS_G4X */ + if (gms_off > 11) { + temp_hi = pci_config_get32(conf_hdl, I965_IFPADDR + 4); + temp_lo = pci_config_get32(conf_hdl, I965_IFPADDR); + phys_hi_mask |= PCI_ADDR_MEM64 | I965_IFPADDR; + } else { + temp_lo = pci_config_get32(conf_hdl, I915_IFPADDR); + phys_hi_mask |= PCI_ADDR_MEM32 | I915_IFPADDR; + } + + if (!(temp_lo & 0x1)) { + /* allocate space from the allocator */ + if (ndi_ra_alloc(ddi_get_parent(dip), + &request, &answer, &alen, + NDI_RA_TYPE_MEM, NDI_RA_PASS) + != NDI_SUCCESS) { + return; + } + TARGETDB_PRINT2((CE_WARN, "addr = 0x%x.0x%x len [0x%x]\n", + HIADDR(answer), + LOADDR(answer), + (uint32_t)alen)); + + if (gms_off > 11) { + pci_config_put32(conf_hdl, I965_IFPADDR + 4, + HIADDR(answer)); + pci_config_put32(conf_hdl, I965_IFPADDR, + LOADDR(answer) | 0x1); + } else { + pci_config_put32(conf_hdl, I915_IFPADDR, + LOADDR(answer) | 0x1); + } + } + else + { + temp_lo &= ~0x1; + answer = ((uint64_t)temp_hi << 32) | temp_lo; + } + + temp_hi = pci_config_get32(conf_hdl, I965_IFPADDR + 4); + temp_lo = pci_config_get32(conf_hdl, I965_IFPADDR); + + /* set pci props */ + if (ddi_dev_nregs(dip, &n_reg) == DDI_FAILURE) { + TARGETDB_PRINT2((CE_WARN, "init_chipset_flush failed")); + n_reg = 0; + return; + } + + if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, + "reg", (caddr_t)®s, &length) != + DDI_PROP_SUCCESS) { + TARGETDB_PRINT2((CE_WARN, "init_chipset_flush failed!")); + return; + } + + regnum = length / sizeof (pci_regspec_t); + + TARGETDB_PRINT2((CE_WARN, "reg regnum %d", regnum)); + + regs2 = kmem_alloc((regnum + 1) * sizeof (pci_regspec_t), KM_SLEEP); + if (regs2 == NULL) { + + TARGETDB_PRINT2((CE_WARN, "init_chipset_flush failed")); + goto error; + } + if (memcpy(regs2, regs, (size_t)length) == NULL) { + TARGETDB_PRINT2((CE_WARN, "init_chipset_flush failed")); + kmem_free(regs2, (regnum + 1) * sizeof (pci_regspec_t)); + goto error; + } + + /* Bus=0, Dev=0, Func=0 0x82001000 */ + regs2[regnum].pci_phys_hi = PCI_REG_REL_M | phys_hi_mask; + regs2[regnum].pci_phys_mid = HIADDR(answer); + regs2[regnum].pci_phys_low = LOADDR(answer); + regs2[regnum].pci_size_hi = 0x00000000; + regs2[regnum].pci_size_low = AGP_PAGE_SIZE; + kmem_free(regs, (size_t)length); + regs = regs2; + + i = ndi_prop_update_int_array(DDI_DEV_T_NONE, + dip, "reg", (int *)regs, (uint_t)5 * (regnum + 1)); + if (i != DDI_PROP_SUCCESS) { + TARGETDB_PRINT2((CE_WARN, "Failed to update reg %d", i)); + kmem_free(regs2, (regnum + 1) * sizeof (pci_regspec_t)); + return; + } + kmem_free(regs2, (regnum + 1) * sizeof (pci_regspec_t)); + regs = NULL; + + if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, + "reg", (caddr_t)®s, &length) != + DDI_PROP_SUCCESS) { + TARGETDB_PRINT2((CE_WARN, "init_chipset_flush: failed1!")); + goto error; + } + regnum = length / sizeof (pci_regspec_t); + kmem_free(regs, (size_t)length); + + i9xx_private.physical = answer; + i9xx_private.size = AGP_PAGE_SIZE; + i9xx_private.regnum = regnum - 1; + ret = ddi_regs_map_setup(dip, i9xx_private.regnum, + (caddr_t *)&(i9xx_private.flush_page), 0, + i9xx_private.size, &dev_attr, + (ddi_acc_handle_t *)&i9xx_private.handle); + + if (ret != DDI_SUCCESS) { + TARGETDB_PRINT2((CE_WARN, "chipset_flush do_ioremap failed ")); + i9xx_private.handle = NULL; + return; + } + return; +error: + if (regs) + kmem_free(regs, (size_t)length); +} + static int agp_target_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) { @@ -795,6 +973,29 @@ break; } + case INTEL_CHIPSET_FLUSH_SETUP: + { + intel_chipset_flush_setup(st->tsoft_dip, + st->tsoft_pcihdl, st->tsoft_gms_off); + break; + } + case INTEL_CHIPSET_FLUSH: + { + if (i9xx_private.handle != NULL) + ddi_put32(i9xx_private.handle, + (uint32_t *)(uintptr_t)i9xx_private.flush_page, 1); + + break; + } + case INTEL_CHIPSET_FLUSH_FREE: + { + if (i9xx_private.handle != NULL) { + ddi_regs_map_free( + (ddi_acc_handle_t *)&i9xx_private.handle); + i9xx_private.handle = NULL; + } + break; + } default: mutex_exit(&st->tsoft_lock); return (ENXIO);
--- a/usr/src/uts/intel/io/agpmaster/agpmaster.c Fri Dec 04 16:46:47 2009 -0800 +++ b/usr/src/uts/intel/io/agpmaster/agpmaster.c Sat Dec 05 13:25:40 2009 +0800 @@ -20,6 +20,11 @@ */ /* + * Copyright (c) 2009, Intel Corporation. + * All Rights Reserved. + */ + +/* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -87,35 +92,6 @@ #define IS_IGD(agpmaster) ((agpmaster->agpm_dev_type == DEVICE_IS_I810) || \ (agpmaster->agpm_dev_type == DEVICE_IS_I830)) - -/* Intel 915 and 945 series */ -#define IS_INTEL_915(agpmaster) ((agpmaster->agpm_id == INTEL_IGD_915) || \ - (agpmaster->agpm_id == INTEL_IGD_915GM) || \ - (agpmaster->agpm_id == INTEL_IGD_945) || \ - (agpmaster->agpm_id == INTEL_IGD_945GM) || \ - (agpmaster->agpm_id == INTEL_IGD_945GME)) - -/* Intel 965 series */ -#define IS_INTEL_965(agpmaster) ((agpmaster->agpm_id == INTEL_IGD_946GZ) || \ - (agpmaster->agpm_id == INTEL_IGD_965G1) || \ - (agpmaster->agpm_id == INTEL_IGD_965Q) || \ - (agpmaster->agpm_id == INTEL_IGD_965G2) || \ - (agpmaster->agpm_id == INTEL_IGD_965GM) || \ - (agpmaster->agpm_id == INTEL_IGD_965GME) || \ - (agpmaster->agpm_id == INTEL_IGD_GM45) || \ - IS_INTEL_G4X(agpmaster)) - -/* Intel G33 series */ -#define IS_INTEL_X33(agpmaster) ((agpmaster->agpm_id == INTEL_IGD_Q35) || \ - (agpmaster->agpm_id == INTEL_IGD_G33) || \ - (agpmaster->agpm_id == INTEL_IGD_Q33)) - -/* Intel G4X series */ -#define IS_INTEL_G4X(agpmaster) ((agpmaster->agpm_id == INTEL_IGD_EL) || \ - (agpmaster->agpm_id == INTEL_IGD_Q45) || \ - (agpmaster->agpm_id == INTEL_IGD_G45) || \ - (agpmaster->agpm_id == INTEL_IGD_G41)) - static struct modlmisc modlmisc = { &mod_miscops, "AGP master interfaces" }; @@ -288,7 +264,7 @@ off_t gmadr_off; /* GMADR offset in PCI config space */ int status; - if (IS_INTEL_X33(agpmaster)) { + if (IS_INTEL_X33(agpmaster->agpm_id)) { /* Intel 3 series are similar with 915/945 series */ status = ddi_regs_map_setup(devi, I915_GTTADDR, >T_ADDR(agpmaster), 0, 0, &i8xx_dev_access, @@ -303,13 +279,13 @@ gmadr_off = I915_CONF_GMADR; /* Different computing method used in getting aperture size. */ apersize = i3XX_apersize(pci_acc_hdl); - } else if (IS_INTEL_965(agpmaster)) { + } else if (IS_INTEL_965(agpmaster->agpm_id)) { status = ddi_regs_map_setup(devi, I965_GTTMMADR, &MMIO_BASE(agpmaster), 0, 0, &i8xx_dev_access, &MMIO_HANDLE(agpmaster)); CHECK_STATUS(status); if ((agpmaster->agpm_id == INTEL_IGD_GM45) || - IS_INTEL_G4X(agpmaster)) + IS_INTEL_G4X(agpmaster->agpm_id)) GTT_ADDR(agpmaster) = MMIO_BASE(agpmaster) + GM45_GTT_OFFSET; else @@ -319,7 +295,7 @@ gmadr_off = I915_CONF_GMADR; apersize = i965_apersize(agpmaster); - } else if (IS_INTEL_915(agpmaster)) { + } else if (IS_INTEL_915(agpmaster->agpm_id)) { /* I915/945 series */ status = ddi_regs_map_setup(devi, I915_GTTADDR, >T_ADDR(agpmaster), 0, 0, &i8xx_dev_access, @@ -650,6 +626,7 @@ case INTEL_IGD_Q45: case INTEL_IGD_G45: case INTEL_IGD_G41: + case INTEL_IGD_B43: master_softc->agpm_dev_type = DEVICE_IS_I830; break; default: /* unknown id */
--- a/usr/src/uts/intel/io/drm/drm_pciids.h Fri Dec 04 16:46:47 2009 -0800 +++ b/usr/src/uts/intel/io/drm/drm_pciids.h Sat Dec 05 13:25:40 2009 +0800 @@ -207,6 +207,7 @@ {0x8086, 0x2E12, CHIP_I9XX|CHIP_I965, "Intel Q45"}, \ {0x8086, 0x2E22, CHIP_I9XX|CHIP_I965, "Intel G45"}, \ {0x8086, 0x2E32, CHIP_I9XX|CHIP_I965, "Intel G41"}, \ + {0x8086, 0x2E42, CHIP_I9XX|CHIP_I965, "Intel B43"}, \ {0, 0, 0, NULL} #ifdef __cplusplus
--- a/usr/src/uts/intel/io/drm/i915_dma.c Fri Dec 04 16:46:47 2009 -0800 +++ b/usr/src/uts/intel/io/drm/i915_dma.c Sat Dec 05 13:25:40 2009 +0800 @@ -4,6 +4,7 @@ */ /* * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. + * Copyright (c) 2009, Intel Corporation. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -99,7 +100,9 @@ (void) memset(dev_priv->hw_status_page, 0, PAGE_SIZE); I915_WRITE(HWS_PGA, dev_priv->dma_status_page); - DRM_DEBUG("Enabled hardware status page\n"); + (void) I915_READ(HWS_PGA); + + DRM_DEBUG("Enabled hardware status page add 0x%lx read GEM HWS 0x%x\n",dev_priv->hw_status_page, READ_HWSP(dev_priv, 0x20)); return 0; } @@ -108,6 +111,7 @@ drm_i915_private_t *dev_priv = dev->dev_private; if (!I915_NEED_GFX_HWS(dev)) { if (dev_priv->status_page_dmah) { + DRM_DEBUG("free status_page_dmal %x", dev_priv->status_page_dmah); drm_pci_free(dev, dev_priv->status_page_dmah); dev_priv->status_page_dmah = NULL; /* Need to rewrite hardware status page */ @@ -115,36 +119,15 @@ } } else { if (dev_priv->status_gfx_addr) { + DRM_DEBUG("free status_gfx_addr %x", dev_priv->status_gfx_addr); dev_priv->status_gfx_addr = 0; drm_core_ioremapfree(&dev_priv->hws_map, dev); I915_WRITE(HWS_PGA, 0x1ffff000); } } + } -#if I915_RING_VALIDATE -/** - * Validate the cached ring tail value - * - * If the X server writes to the ring and DRM doesn't - * reload the head and tail pointers, it will end up writing - * data to the wrong place in the ring, causing havoc. - */ -void i915_ring_validate(struct drm_device *dev, const char *func, int line) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - drm_i915_ring_buffer_t *ring = &(dev_priv->ring); - u32 tail = I915_READ(PRB0_TAIL) & HEAD_ADDR; - u32 head = I915_READ(PRB0_HEAD) & HEAD_ADDR; - - if (tail != ring->tail) { - DRM_ERROR("%s:%d head sw %x, hw %x. tail sw %x hw %x\n", - func, line, - ring->head, head, ring->tail, tail); - } -} -#endif - void i915_kernel_lost_context(drm_device_t * dev) { drm_i915_private_t *dev_priv = dev->dev_private; @@ -177,11 +160,13 @@ dev_priv->ring.map.size = 0; } +#ifdef I915_HAVE_GEM + if (I915_NEED_GFX_HWS(dev)) +#endif i915_free_hardware_status(dev); dev_priv->sarea = NULL; dev_priv->sarea_priv = NULL; - dev_priv->mmio_map = NULL; return 0; } @@ -200,30 +185,18 @@ return (EINVAL); } - /* - * mmio_map will be destoried after DMA clean up. We should not - * access mmio_map in suspend or resume process. - */ - - dev_priv->mmio_map = drm_core_findmap(dev, init->mmio_offset); - - if (!dev_priv->mmio_map) { - dev->dev_private = (void *)dev_priv; - (void) i915_dma_cleanup(dev); - DRM_ERROR("can not find mmio map!\n"); - return (EINVAL); - } - - if (init->sarea_priv_offset) - dev_priv->sarea_priv = (drm_i915_sarea_t *) - ((unsigned long) dev_priv->sarea->handle + - init->sarea_priv_offset); - else { - /* No sarea_priv for you! */ - dev_priv->sarea_priv = NULL; - } + dev_priv->sarea_priv = (drm_i915_sarea_t *)(uintptr_t) + ((u8 *) dev_priv->sarea->handle + + init->sarea_priv_offset); if (init->ring_size != 0) { + if (dev_priv->ring.ring_obj != NULL) { + (void) i915_dma_cleanup(dev); + DRM_ERROR("Client tried to initialize ringbuffer in " + "GEM mode\n"); + return -EINVAL; + } + dev_priv->ring.Size = init->ring_size; dev_priv->ring.tail_mask = dev_priv->ring.Size - 1; @@ -241,35 +214,18 @@ " ring buffer\n"); return (ENOMEM); } - - dev_priv->ring.virtual_start = (u8 *)dev_priv->ring.map.dev_addr; } + dev_priv->ring.virtual_start = (u8 *)dev_priv->ring.map.dev_addr; dev_priv->cpp = init->cpp; - - if (dev_priv->sarea_priv) - dev_priv->sarea_priv->pf_current_page = 0; - - /* We are using separate values as placeholders for mechanisms for - * private backbuffer/depthbuffer usage. - */ + dev_priv->back_offset = init->back_offset; + dev_priv->front_offset = init->front_offset; + dev_priv->current_page = 0; + dev_priv->sarea_priv->pf_current_page = dev_priv->current_page; /* Allow hardware batchbuffers unless told otherwise. */ dev_priv->allow_batchbuffer = 1; - - /* Init HWS */ - if (!I915_NEED_GFX_HWS(dev)) { - (void) i915_init_hardware_status(dev); - } - - /* Enable vblank on pipe A for older X servers - */ - dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A; - -#ifdef I915_HAVE_BUFFER - drm_bo_driver_init(dev); -#endif return 0; } @@ -277,8 +233,6 @@ { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - DRM_DEBUG("i915_dma_resume\n"); - if (!dev_priv->sarea) { DRM_ERROR("can not find sarea!\n"); return (EINVAL); @@ -295,7 +249,7 @@ DRM_ERROR("Can not find hardware status page\n"); return (EINVAL); } - DRM_DEBUG("hw status page @ %p\n", dev_priv->hw_status_page); + DRM_DEBUG("i915_dma_resume hw status page @ %p\n", dev_priv->hw_status_page); if (!I915_NEED_GFX_HWS(dev)) I915_WRITE(HWS_PGA, dev_priv->dma_status_page); @@ -419,26 +373,30 @@ int i; RING_LOCALS; - if ((dwords+1) * sizeof(int) >= dev_priv->ring.Size - 8) + if ((dwords+1) * sizeof(int) >= dev_priv->ring.Size - 8) { + DRM_ERROR(" emit cmds invalid arg"); return (EINVAL); - + } BEGIN_LP_RING((dwords+1)&~1); for (i = 0; i < dwords;) { int cmd, sz; - if (DRM_COPY_FROM_USER_UNCHECKED(&cmd, &buffer[i], sizeof(cmd))) + if (DRM_COPY_FROM_USER_UNCHECKED(&cmd, &buffer[i], sizeof(cmd))) { + DRM_ERROR("emit cmds failed to get cmd from user"); return (EINVAL); - + } - if ((sz = validate_cmd(cmd)) == 0 || i + sz > dwords) + if ((sz = validate_cmd(cmd)) == 0 || i + sz > dwords) { + DRM_ERROR("emit cmds invalid"); return (EINVAL); - + } OUT_RING(cmd); while (++i, --sz) { if (DRM_COPY_FROM_USER_UNCHECKED(&cmd, &buffer[i], sizeof(cmd))) { + DRM_ERROR("emit cmds failed get cmds"); return (EINVAL); } OUT_RING(cmd); @@ -462,6 +420,7 @@ RING_LOCALS; if (DRM_COPY_FROM_USER_UNCHECKED(&box, &boxes[i], sizeof(box))) { + DRM_ERROR("emit box failed to copy from user"); return (EFAULT); } @@ -501,49 +460,25 @@ drm_i915_private_t *dev_priv = dev->dev_private; RING_LOCALS; - if (++dev_priv->counter > BREADCRUMB_MASK) { - dev_priv->counter = 1; - DRM_DEBUG("Breadcrumb counter wrapped around\n"); - } - + dev_priv->counter++; + if (dev_priv->counter > 0x7FFFFFFFUL) + dev_priv->counter = 0; if (dev_priv->sarea_priv) dev_priv->sarea_priv->last_enqueue = dev_priv->counter; BEGIN_LP_RING(4); OUT_RING(MI_STORE_DWORD_INDEX); - OUT_RING(5 << MI_STORE_DWORD_INDEX_SHIFT); + OUT_RING(I915_BREADCRUMB_INDEX << MI_STORE_DWORD_INDEX_SHIFT); OUT_RING(dev_priv->counter); OUT_RING(0); ADVANCE_LP_RING(); } - -void i915_emit_mi_flush(drm_device_t *dev, uint32_t flush) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - uint32_t flush_cmd = MI_FLUSH; - RING_LOCALS; - - flush_cmd |= flush; - - i915_kernel_lost_context(dev); - - BEGIN_LP_RING(4); - OUT_RING(flush_cmd); - OUT_RING(0); - OUT_RING(0); - OUT_RING(0); - ADVANCE_LP_RING(); -} - static int i915_dispatch_cmdbuffer(drm_device_t * dev, drm_i915_cmdbuffer_t * cmd) { -#ifdef I915_HAVE_FENCE - drm_i915_private_t *dev_priv = dev->dev_private; -#endif int nbox = cmd->num_cliprects; int i = 0, count, ret; @@ -570,10 +505,6 @@ } i915_emit_breadcrumb( dev ); -#ifdef I915_HAVE_FENCE - if (unlikely((dev_priv->counter & 0xFF) == 0)) - drm_fence_flush_old(dev, 0, dev_priv->counter); -#endif return 0; } @@ -628,78 +559,53 @@ return 0; } -static void i915_do_dispatch_flip(struct drm_device * dev, int plane, int sync) +static int i915_dispatch_flip(struct drm_device * dev, int planes) { drm_i915_private_t *dev_priv = dev->dev_private; - u32 num_pages, current_page, next_page, dspbase; - int shift = 2 * plane, x, y; RING_LOCALS; - /* Calculate display base offset */ - num_pages = dev_priv->sarea_priv->third_handle ? 3 : 2; - current_page = (dev_priv->sarea_priv->pf_current_page >> shift) & 0x3; - next_page = (current_page + 1) % num_pages; - - switch (next_page) { - default: - case 0: - dspbase = dev_priv->sarea_priv->front_offset; - break; - case 1: - dspbase = dev_priv->sarea_priv->back_offset; - break; - case 2: - dspbase = dev_priv->sarea_priv->third_offset; - break; - } - - if (plane == 0) { - x = dev_priv->sarea_priv->planeA_x; - y = dev_priv->sarea_priv->planeA_y; - } else { - x = dev_priv->sarea_priv->planeB_x; - y = dev_priv->sarea_priv->planeB_y; - } - - dspbase += (y * dev_priv->sarea_priv->pitch + x) * dev_priv->cpp; - - - DRM_DEBUG("plane=%d current_page=%d dspbase=0x%x\n", plane, current_page, dspbase); - - BEGIN_LP_RING(4); - OUT_RING(sync ? 0 : - (MI_WAIT_FOR_EVENT | (plane ? MI_WAIT_FOR_PLANE_B_FLIP : - MI_WAIT_FOR_PLANE_A_FLIP))); - OUT_RING(CMD_OP_DISPLAYBUFFER_INFO | (sync ? 0 : ASYNC_FLIP) | - (plane ? DISPLAY_PLANE_B : DISPLAY_PLANE_A)); - OUT_RING(dev_priv->sarea_priv->pitch * dev_priv->cpp); - OUT_RING(dspbase); - ADVANCE_LP_RING(); - - dev_priv->sarea_priv->pf_current_page &= ~(0x3 << shift); - dev_priv->sarea_priv->pf_current_page |= next_page << shift; -} - -void i915_dispatch_flip(struct drm_device * dev, int planes, int sync) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - int i; + if (!dev_priv->sarea_priv) + return -EINVAL; DRM_DEBUG("planes=0x%x pfCurrentPage=%d\n", planes, dev_priv->sarea_priv->pf_current_page); - i915_emit_mi_flush(dev, MI_READ_FLUSH | MI_EXE_FLUSH); + i915_kernel_lost_context(dev); - for (i = 0; i < 2; i++) - if (planes & (1 << i)) - i915_do_dispatch_flip(dev, i, sync); + BEGIN_LP_RING(2); + OUT_RING(MI_FLUSH | MI_READ_FLUSH); + OUT_RING(0); + ADVANCE_LP_RING(); - i915_emit_breadcrumb(dev); -#ifdef I915_HAVE_FENCE - if (unlikely(!sync && ((dev_priv->counter & 0xFF) == 0))) - drm_fence_flush_old(dev, 0, dev_priv->counter); -#endif + BEGIN_LP_RING(6); + OUT_RING(CMD_OP_DISPLAYBUFFER_INFO | ASYNC_FLIP); + OUT_RING(0); + if (dev_priv->current_page == 0) { + OUT_RING(dev_priv->back_offset); + dev_priv->current_page = 1; + } else { + OUT_RING(dev_priv->front_offset); + dev_priv->current_page = 0; + } + OUT_RING(0); + ADVANCE_LP_RING(); + BEGIN_LP_RING(2); + OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_PLANE_A_FLIP); + OUT_RING(0); + ADVANCE_LP_RING(); + + dev_priv->sarea_priv->last_enqueue = dev_priv->counter++; + + BEGIN_LP_RING(4); + OUT_RING(MI_STORE_DWORD_INDEX); + OUT_RING(I915_BREADCRUMB_INDEX << MI_STORE_DWORD_INDEX_SHIFT); + OUT_RING(dev_priv->counter); + OUT_RING(0); + ADVANCE_LP_RING(); + + dev_priv->sarea_priv->pf_current_page = dev_priv->current_page; + return 0; } static int i915_quiescent(drm_device_t * dev) @@ -723,11 +629,16 @@ /*ARGSUSED*/ static int i915_flush_ioctl(DRM_IOCTL_ARGS) { + int ret; DRM_DEVICE; LOCK_TEST_WITH_RETURN(dev, fpriv); - return i915_quiescent(dev); + spin_lock(&dev->struct_mutex); + ret = i915_quiescent(dev); + spin_unlock(&dev->struct_mutex); + + return ret; } /*ARGSUSED*/ @@ -762,7 +673,6 @@ DRM_COPYFROM_WITH_RETURN(&batch, (void *) data, sizeof(batch)); - DRM_DEBUG("i915 batchbuffer, start %x used %d cliprects %d, counter %d\n", batch.start, batch.used, batch.num_cliprects, dev_priv->counter); @@ -773,10 +683,12 @@ batch.num_cliprects * sizeof(drm_clip_rect_t))) return (EFAULT); -*/ + +*/ - + spin_lock(&dev->struct_mutex); ret = i915_dispatch_batchbuffer(dev, &batch); + spin_unlock(&dev->struct_mutex); sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); return ret; @@ -825,7 +737,9 @@ } */ + spin_lock(&dev->struct_mutex); ret = i915_dispatch_cmdbuffer(dev, &cmdbuf); + spin_unlock(&dev->struct_mutex); if (ret) { DRM_ERROR("i915_dispatch_cmdbuffer failed\n"); return ret; @@ -835,46 +749,23 @@ return 0; } -static void i915_do_cleanup_pageflip(drm_device_t * dev) -{ - drm_i915_private_t *dev_priv = dev->dev_private; - int i, planes, num_pages = dev_priv->sarea_priv->third_handle ? 3 : 2; - - DRM_DEBUG("i915_do_cleanup_pageflip\n"); - - for (i = 0, planes = 0; i < 2; i++) - if (dev_priv->sarea_priv->pf_current_page & (0x3 << (2 * i))) { - dev_priv->sarea_priv->pf_current_page = - (dev_priv->sarea_priv->pf_current_page & - ~(0x3 << (2 * i))) | ((num_pages - 1) << (2 * i)); - - planes |= 1 << i; - } - - if (planes) - i915_dispatch_flip(dev, planes, 0); - -} - /*ARGSUSED*/ static int i915_flip_bufs(DRM_IOCTL_ARGS) { DRM_DEVICE; drm_i915_flip_t param; + int ret; DRM_COPYFROM_WITH_RETURN(¶m, (drm_i915_flip_t *) data, sizeof(param)); DRM_DEBUG("i915_flip_bufs\n"); LOCK_TEST_WITH_RETURN(dev, fpriv); - /* This is really planes */ - if (param.pipes & ~0x3) { - DRM_ERROR("Invalid planes 0x%x, only <= 0x3 is valid\n", - param.pipes); - return -EINVAL; - } - i915_dispatch_flip(dev, param.pipes, 0); - return 0; + + spin_lock(&dev->struct_mutex); + ret = i915_dispatch_flip(dev, param.pipes); + spin_unlock(&dev->struct_mutex); + return ret; } /*ARGSUSED*/ @@ -916,8 +807,11 @@ case I915_PARAM_CHIPSET_ID: value = dev->pci_device; break; + case I915_PARAM_HAS_GEM: + value = dev->driver->use_gem; + break; default: - DRM_ERROR("Unknown parameter %d\n", param.param); + DRM_ERROR("Unknown get parameter %d\n", param.param); return (EINVAL); } @@ -953,7 +847,7 @@ dev_priv->allow_batchbuffer = param.value; break; default: - DRM_ERROR("unknown parameter %d\n", param.param); + DRM_ERROR("unknown set parameter %d\n", param.param); return (EINVAL); } @@ -976,7 +870,7 @@ } DRM_COPYFROM_WITH_RETURN(&hws, (drm_i915_hws_addr_t __user *) data, sizeof(hws)); - DRM_DEBUG("set status page addr 0x%08x\n", (u32)hws.addr); +DRM_ERROR("i915_set_status_page set status page addr 0x%08x\n", (u32)hws.addr); dev_priv->status_gfx_addr = hws.addr & (0x1ffff<<12); DRM_DEBUG("set gfx_addr 0x%08x\n", dev_priv->status_gfx_addr); @@ -1013,7 +907,8 @@ int i915_driver_load(drm_device_t *dev, unsigned long flags) { struct drm_i915_private *dev_priv; - int ret = 0; + unsigned long base, size; + int ret = 0, mmio_bar = IS_I9XX(dev) ? 0 : 1; /* i915 has 4 more counters */ dev->counters += 4; @@ -1030,8 +925,54 @@ dev->dev_private = (void *)dev_priv; dev_priv->dev = dev; - mutex_init(&dev_priv->swaps_lock, "swap", MUTEX_DRIVER, NULL); + /* Add register map (needed for suspend/resume) */ + + base = drm_get_resource_start(dev, mmio_bar); + size = drm_get_resource_len(dev, mmio_bar); + dev_priv->mmio_map = drm_alloc(sizeof (drm_local_map_t), DRM_MEM_MAPS); + dev_priv->mmio_map->offset = base; + dev_priv->mmio_map->size = size; + dev_priv->mmio_map->type = _DRM_REGISTERS; + dev_priv->mmio_map->flags = _DRM_REMOVABLE; + drm_ioremap(dev, dev_priv->mmio_map); + + DRM_DEBUG("i915_driverload mmio %p mmio_map->dev_addr %x", dev_priv->mmio_map, dev_priv->mmio_map->dev_addr); + +#if defined(__i386) + dev->driver->use_gem = 0; +#else + if (IS_I965G(dev)) { + dev->driver->use_gem = 1; + } else { + dev->driver->use_gem = 0; + } +#endif /* __i386 */ + + dev->driver->get_vblank_counter = i915_get_vblank_counter; + dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ +#if defined(__i386) + if (IS_G4X(dev) || IS_GM45(dev)) +#else + if (IS_G4X(dev)) +#endif + { + dev->max_vblank_count = 0xffffffff; /* full 32 bit counter */ + dev->driver->get_vblank_counter = gm45_get_vblank_counter; + } + + +#ifdef I915_HAVE_GEM + i915_gem_load(dev); +#endif + + if (!I915_NEED_GFX_HWS(dev)) { + ret = i915_init_hardware_status(dev); + if(ret) + return ret; + } + mutex_init(&dev_priv->user_irq_lock, "userirq", MUTEX_DRIVER, NULL); + mutex_init(&dev_priv->error_lock, "error_lock", MUTEX_DRIVER, NULL); ret = drm_vblank_init(dev, I915_NUM_PIPE); if (ret) { @@ -1048,8 +989,8 @@ i915_free_hardware_status(dev); - DRM_FINI_WAITQUEUE(&dev_priv->irq_queue); - mutex_destroy(&dev_priv->swaps_lock); + drm_rmmap(dev, dev_priv->mmio_map); + mutex_destroy(&dev_priv->user_irq_lock); drm_free(dev->dev_private, sizeof(drm_i915_private_t), @@ -1059,6 +1000,25 @@ return 0; } +/*ARGSUSED*/ +int i915_driver_open(drm_device_t * dev, struct drm_file *file_priv) +{ + struct drm_i915_file_private *i915_file_priv; + + DRM_DEBUG("\n"); + i915_file_priv = (struct drm_i915_file_private *) + drm_alloc(sizeof(*i915_file_priv), DRM_MEM_FILES); + + if (!i915_file_priv) + return -ENOMEM; + + file_priv->driver_priv = i915_file_priv; + + i915_file_priv->mm.last_gem_seqno = 0; + i915_file_priv->mm.last_gem_throttle_seqno = 0; + + return 0; +} void i915_driver_lastclose(drm_device_t * dev) { @@ -1068,43 +1028,28 @@ if (!dev_priv) return; -#ifdef I915_HAVE_BUFFER - if (dev_priv->val_bufs) { - vfree(dev_priv->val_bufs); - dev_priv->val_bufs = NULL; - } +#ifdef I915_HAVE_GEM + i915_gem_lastclose(dev); #endif - DRM_GETSAREA(); - if (dev_priv->sarea_priv) - i915_do_cleanup_pageflip(dev); if (dev_priv->agp_heap) i915_mem_takedown(&(dev_priv->agp_heap)); -#if defined(I915_HAVE_BUFFER) - if (dev_priv->sarea_kmap.virtual) { - drm_bo_kunmap(&dev_priv->sarea_kmap); - dev_priv->sarea_kmap.virtual = NULL; - dev->lock.hw_lock = NULL; - dev->sigdata.lock = NULL; - } - - if (dev_priv->sarea_bo) { - mutex_lock(&dev->struct_mutex); - drm_bo_usage_deref_locked(&dev_priv->sarea_bo); - mutex_unlock(&dev->struct_mutex); - dev_priv->sarea_bo = NULL; - } -#endif (void) i915_dma_cleanup(dev); } void i915_driver_preclose(drm_device_t * dev, drm_file_t *fpriv) { - if (dev->dev_private) { - drm_i915_private_t *dev_priv = dev->dev_private; - i915_mem_release(dev, fpriv, dev_priv->agp_heap); - } + drm_i915_private_t *dev_priv = dev->dev_private; + i915_mem_release(dev, fpriv, dev_priv->agp_heap); +} + +/*ARGSUSED*/ +void i915_driver_postclose(drm_device_t * dev, struct drm_file *file_priv) +{ + struct drm_i915_file_private *i915_file_priv = file_priv->driver_priv; + + drm_free(i915_file_priv, sizeof(*i915_file_priv), DRM_MEM_FILES); } drm_ioctl_desc_t i915_ioctls[] = { @@ -1135,13 +1080,49 @@ [DRM_IOCTL_NR(DRM_I915_DESTROY_HEAP)] = {i915_mem_destroy_heap, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY}, [DRM_IOCTL_NR(DRM_I915_SET_VBLANK_PIPE)] = - {i915_vblank_pipe_set, DRM_AUTH}, + {i915_vblank_pipe_set, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY}, [DRM_IOCTL_NR(DRM_I915_GET_VBLANK_PIPE)] = {i915_vblank_pipe_get, DRM_AUTH}, [DRM_IOCTL_NR(DRM_I915_VBLANK_SWAP)] = {i915_vblank_swap, DRM_AUTH}, [DRM_IOCTL_NR(DRM_I915_HWS_ADDR)] = {i915_set_status_page, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY}, +#ifdef I915_HAVE_GEM + [DRM_IOCTL_NR(DRM_I915_GEM_INIT)] = + {i915_gem_init_ioctl, DRM_AUTH}, + [DRM_IOCTL_NR(DRM_I915_GEM_EXECBUFFER)] = + {i915_gem_execbuffer, DRM_AUTH}, + [DRM_IOCTL_NR(DRM_I915_GEM_PIN)] = + {i915_gem_pin_ioctl, DRM_AUTH|DRM_ROOT_ONLY}, + [DRM_IOCTL_NR(DRM_I915_GEM_UNPIN)] = + {i915_gem_unpin_ioctl, DRM_AUTH|DRM_ROOT_ONLY}, + [DRM_IOCTL_NR(DRM_I915_GEM_BUSY)] = + {i915_gem_busy_ioctl, DRM_AUTH}, + [DRM_IOCTL_NR(DRM_I915_GEM_THROTTLE)] = + {i915_gem_throttle_ioctl, DRM_AUTH}, + [DRM_IOCTL_NR(DRM_I915_GEM_ENTERVT)] = + {i915_gem_entervt_ioctl, DRM_AUTH}, + [DRM_IOCTL_NR(DRM_I915_GEM_LEAVEVT)] = + {i915_gem_leavevt_ioctl, DRM_AUTH}, + [DRM_IOCTL_NR(DRM_I915_GEM_CREATE)] = + {i915_gem_create_ioctl, 0}, + [DRM_IOCTL_NR(DRM_I915_GEM_PREAD)] = + {i915_gem_pread_ioctl, 0}, + [DRM_IOCTL_NR(DRM_I915_GEM_PWRITE)] = + {i915_gem_pwrite_ioctl, 0}, + [DRM_IOCTL_NR(DRM_I915_GEM_MMAP)] = + {i915_gem_mmap_ioctl, 0}, + [DRM_IOCTL_NR(DRM_I915_GEM_SET_DOMAIN)] = + {i915_gem_set_domain_ioctl, 0}, + [DRM_IOCTL_NR(DRM_I915_GEM_SW_FINISH)] = + {i915_gem_sw_finish_ioctl, 0}, + [DRM_IOCTL_NR(DRM_I915_GEM_SET_TILING)] = + {i915_gem_set_tiling, 0}, + [DRM_IOCTL_NR(DRM_I915_GEM_GET_TILING)] = + {i915_gem_get_tiling, 0}, + [DRM_IOCTL_NR(DRM_I915_GEM_GET_APERTURE)] = + {i915_gem_get_aperture_ioctl, 0}, +#endif }; int i915_max_ioctl = DRM_ARRAY_SIZE(i915_ioctls); @@ -1163,12 +1144,3 @@ return 1; } -/*ARGSUSED*/ -int i915_driver_firstopen(struct drm_device *dev) -{ -#ifdef I915_HAVE_BUFFER - drm_bo_driver_init(dev); -#endif - return 0; -} -
--- a/usr/src/uts/intel/io/drm/i915_drm.h Fri Dec 04 16:46:47 2009 -0800 +++ b/usr/src/uts/intel/io/drm/i915_drm.h Sat Dec 05 13:25:40 2009 +0800 @@ -2,6 +2,7 @@ /* * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. + * Copyright (c) 2009, Intel Corporation. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -113,21 +114,26 @@ unsigned int rotated_tiled; unsigned int rotated2_tiled; - int planeA_x; - int planeA_y; - int planeA_w; - int planeA_h; - int planeB_x; - int planeB_y; - int planeB_w; - int planeB_h; + int pipeA_x; + int pipeA_y; + int pipeA_w; + int pipeA_h; + int pipeB_x; + int pipeB_y; + int pipeB_w; + int pipeB_h; + int pad1; /* Triple buffering */ drm_handle_t third_handle; int third_offset; int third_size; unsigned int third_tiled; + unsigned int front_bo_handle; + unsigned int back_bo_handle; + unsigned int third_bo_handle; + unsigned int depth_bo_handle; } drm_i915_sarea_t; /* Driver specific fence types and classes. @@ -168,6 +174,24 @@ #define DRM_I915_GET_VBLANK_PIPE 0x0e #define DRM_I915_VBLANK_SWAP 0x0f #define DRM_I915_HWS_ADDR 0x11 +#define DRM_I915_GEM_INIT 0x13 +#define DRM_I915_GEM_EXECBUFFER 0x14 +#define DRM_I915_GEM_PIN 0x15 +#define DRM_I915_GEM_UNPIN 0x16 +#define DRM_I915_GEM_BUSY 0x17 +#define DRM_I915_GEM_THROTTLE 0x18 +#define DRM_I915_GEM_ENTERVT 0x19 +#define DRM_I915_GEM_LEAVEVT 0x1a +#define DRM_I915_GEM_CREATE 0x1b +#define DRM_I915_GEM_PREAD 0x1c +#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_I915_GEM_SET_TILING 0x21 +#define DRM_I915_GEM_GET_TILING 0x22 +#define DRM_I915_GEM_GET_APERTURE 0x23 +#define DRM_I915_GEM_MMAP_GTT 0x24 #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) @@ -185,6 +209,24 @@ #define DRM_IOCTL_I915_SET_VBLANK_PIPE DRM_IOW( DRM_COMMAND_BASE + DRM_I915_SET_VBLANK_PIPE, drm_i915_vblank_pipe_t) #define DRM_IOCTL_I915_GET_VBLANK_PIPE DRM_IOR( DRM_COMMAND_BASE + DRM_I915_GET_VBLANK_PIPE, drm_i915_vblank_pipe_t) #define DRM_IOCTL_I915_VBLANK_SWAP DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_VBLANK_SWAP, drm_i915_vblank_swap_t) +#define DRM_IOCTL_I915_GEM_INIT DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_INIT, struct drm_i915_gem_init) +#define DRM_IOCTL_I915_GEM_EXECBUFFER DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_EXECBUFFER, struct drm_i915_gem_execbuffer) +#define DRM_IOCTL_I915_GEM_PIN DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_PIN, struct drm_i915_gem_pin) +#define DRM_IOCTL_I915_GEM_UNPIN DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_UNPIN, struct drm_i915_gem_unpin) +#define DRM_IOCTL_I915_GEM_BUSY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_BUSY, struct drm_i915_gem_busy) +#define DRM_IOCTL_I915_GEM_THROTTLE DRM_IO ( DRM_COMMAND_BASE + DRM_I915_GEM_THROTTLE) +#define DRM_IOCTL_I915_GEM_ENTERVT DRM_IO(DRM_COMMAND_BASE + DRM_I915_GEM_ENTERVT) +#define DRM_IOCTL_I915_GEM_LEAVEVT DRM_IO(DRM_COMMAND_BASE + DRM_I915_GEM_LEAVEVT) +#define DRM_IOCTL_I915_GEM_CREATE DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_CREATE, struct drm_i915_gem_create) +#define DRM_IOCTL_I915_GEM_PREAD DRM_IOW (DRM_COMMAND_BASE + DRM_I915_GEM_PREAD, struct drm_i915_gem_pread) +#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_MMAP_GTT DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MMAP_GTT, struct drm_i915_gem_mmap_gtt) +#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) +#define DRM_IOCTL_I915_GEM_SET_TILING DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_SET_TILING, struct drm_i915_gem_set_tiling) +#define DRM_IOCTL_I915_GEM_GET_TILING DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_GET_TILING, struct drm_i915_gem_get_tiling) +#define DRM_IOCTL_I915_GEM_GET_APERTURE DRM_IOR (DRM_COMMAND_BASE + DRM_I915_GEM_GET_APERTURE, struct drm_i915_gem_get_aperture) /* Asynchronous page flipping: */ @@ -260,6 +302,7 @@ #define I915_PARAM_ALLOW_BATCHBUFFER 2 #define I915_PARAM_LAST_DISPATCH 3 #define I915_PARAM_CHIPSET_ID 4 +#define I915_PARAM_HAS_GEM 5 typedef struct drm_i915_getparam { int param; @@ -335,9 +378,365 @@ unsigned int sequence; } drm_i915_vblank_swap_t; +#define I915_MMIO_READ 0 +#define I915_MMIO_WRITE 1 + +#define I915_MMIO_MAY_READ 0x1 +#define I915_MMIO_MAY_WRITE 0x2 + +#define MMIO_REGS_IA_PRIMATIVES_COUNT 0 +#define MMIO_REGS_IA_VERTICES_COUNT 1 +#define MMIO_REGS_VS_INVOCATION_COUNT 2 +#define MMIO_REGS_GS_PRIMITIVES_COUNT 3 +#define MMIO_REGS_GS_INVOCATION_COUNT 4 +#define MMIO_REGS_CL_PRIMITIVES_COUNT 5 +#define MMIO_REGS_CL_INVOCATION_COUNT 6 +#define MMIO_REGS_PS_INVOCATION_COUNT 7 +#define MMIO_REGS_PS_DEPTH_COUNT 8 + +typedef struct drm_i915_mmio_entry { + unsigned int flag; + unsigned int offset; + unsigned int size; +} drm_i915_mmio_entry_t; + +typedef struct drm_i915_mmio { + unsigned int read_write:1; + unsigned int reg:31; + void __user *data; +} drm_i915_mmio_t; + typedef struct drm_i915_hws_addr { uint64_t addr; } drm_i915_hws_addr_t; +struct drm_i915_gem_init { + /** + * Beginning offset in the GTT to be managed by the DRM memory + * manager. + */ + uint64_t gtt_start; + /** + * Ending offset in the GTT to be managed by the DRM memory + * manager. + */ + uint64_t gtt_end; + +}; + +struct drm_i915_gem_create { + /** + * Requested size for the object. + * + * The (page-aligned) allocated size for the object will be returned. + */ + uint64_t size; + /** + * Returned handle for the object. + * + * Object handles are nonzero. + */ + uint32_t handle; + uint32_t pad; +}; + +struct drm_i915_gem_pread { + /** Handle for the object being read. */ + uint32_t handle; + uint32_t pad; + /** Offset into the object to read from */ + uint64_t offset; + /** Length of data to read */ + uint64_t size; + /** + * Pointer to write the data into. + * + * This is a fixed-size type for 32/64 compatibility. + */ + uint64_t data_ptr; +}; + +struct drm_i915_gem_pwrite { + /** Handle for the object being written to. */ + uint32_t handle; + uint32_t pad; + /** Offset into the object to write to */ + uint64_t offset; + /** Length of data to write */ + uint64_t size; + /** + * Pointer to read the data from. + * + * This is a fixed-size type for 32/64 compatibility. + */ + uint64_t data_ptr; +}; + +struct drm_i915_gem_mmap { + /** Handle for the object being mapped. */ + uint32_t handle; + uint32_t pad; + /** Offset in the object to map. */ + uint64_t offset; + /** + * Length of data to map. + * + * The value will be page-aligned. + */ + uint64_t size; + /** + * Returned pointer the data was mapped at. + * + * This is a fixed-size type for 32/64 compatibility. + */ + uint64_t addr_ptr; +}; + +struct drm_i915_gem_mmap_gtt { + /** Handle for the object being mapped. */ + uint32_t handle; + uint32_t pad; + /** + * Fake offset to use for subsequent mmap call + * + * This is a fixed-size type for 32/64 compatibility. + */ + uint64_t offset; +}; + +struct drm_i915_gem_set_domain { + /** Handle for the object */ + uint32_t handle; + + /** New read domains */ + uint32_t read_domains; + + /** New write 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. + * + * It's appealing to make this be an index into the mm_validate_entry + * list to refer to the buffer, but this allows the driver to create + * a relocation list for state buffers and not re-write it per + * exec using the buffer. + */ + uint32_t target_handle; + + /** + * Value to be added to the offset of the target buffer to make up + * the relocation entry. + */ + uint32_t delta; + + /** Offset in the buffer the relocation entry will be written into */ + uint64_t offset; + + /** + * Offset value of the target buffer that the relocation entry was last + * written as. + * + * If the buffer has the same offset as last time, we can skip syncing + * and writing the relocation. This value is written back out by + * the execbuffer ioctl when the relocation is written. + */ + uint64_t presumed_offset; + + /** + * Target memory domains read by this operation. + */ + uint32_t read_domains; + + /** + * Target memory domains written by this operation. + * + * Note that only one domain may be written by the whole + * execbuffer operation, so that where there are conflicts, + * the application will get -EINVAL back. + */ + uint32_t write_domain; +}; + +/** @{ + * Intel memory domains + * + * Most of these just align with the various caches in + * the system and are used to flush and invalidate as + * objects end up cached in different domains. + */ +/** CPU cache */ +#define I915_GEM_DOMAIN_CPU 0x00000001 +/** Render cache, used by 2D and 3D drawing */ +#define I915_GEM_DOMAIN_RENDER 0x00000002 +/** Sampler cache, used by texture engine */ +#define I915_GEM_DOMAIN_SAMPLER 0x00000004 +/** Command queue, used to load batch buffers */ +#define I915_GEM_DOMAIN_COMMAND 0x00000008 +/** Instruction cache, used by shader programs */ +#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 { + /** + * User's handle for a buffer to be bound into the GTT for this + * operation. + */ + uint32_t handle; + + /** Number of relocations to be performed on this buffer */ + uint32_t relocation_count; + + /** + * Pointer to array of struct drm_i915_gem_relocation_entry containing + * the relocations to be performed in this buffer. + */ + uint64_t relocs_ptr; + + /** Required alignment in graphics aperture */ + uint64_t alignment; + + /** + * Returned value of the updated offset of the object, for future + * presumed_offset writes. + */ + uint64_t offset; + +}; + +struct drm_i915_gem_execbuffer { + /** + * List of buffers to be validated with their relocations to be + * performend on them. + * + * This is a pointer to an array of struct drm_i915_gem_validate_entry. + * + * These buffers must be listed in an order such that all relocations + * a buffer is performing refer to buffers that have already appeared + * in the validate list. + */ + uint64_t buffers_ptr; + uint32_t buffer_count; + + /** Offset in the batchbuffer to start execution from. */ + uint32_t batch_start_offset; + /** Bytes used in batchbuffer from batch_start_offset */ + uint32_t batch_len; + uint32_t DR1; + uint32_t DR4; + uint32_t num_cliprects; + /** This is a struct drm_clip_rect *cliprects */ + uint64_t cliprects_ptr; +}; + +struct drm_i915_gem_pin { + /** Handle of the buffer to be pinned. */ + uint32_t handle; + uint32_t pad; + + /** alignment required within the aperture */ + uint64_t alignment; + + /** Returned GTT offset of the buffer. */ + uint64_t offset; +}; + + +struct drm_i915_gem_unpin { + /** Handle of the buffer to be unpinned. */ + uint32_t handle; + uint32_t pad; +}; + +struct drm_i915_gem_busy { + /** Handle of the buffer to check for busy */ + uint32_t handle; + + /** Return busy status (1 if busy, 0 if idle) */ + uint32_t busy; +}; + +#define I915_TILING_NONE 0 +#define I915_TILING_X 1 +#define I915_TILING_Y 2 + +#define I915_BIT_6_SWIZZLE_NONE 0 +#define I915_BIT_6_SWIZZLE_9 1 +#define I915_BIT_6_SWIZZLE_9_10 2 +#define I915_BIT_6_SWIZZLE_9_11 3 +#define I915_BIT_6_SWIZZLE_9_10_11 4 +/* Not seen by userland */ +#define I915_BIT_6_SWIZZLE_UNKNOWN 5 + +struct drm_i915_gem_set_tiling { + /** Handle of the buffer to have its tiling state updated */ + uint32_t handle; + + /** + * Tiling mode for the object (I915_TILING_NONE, I915_TILING_X, + * I915_TILING_Y). + * + * This value is to be set on request, and will be updated by the + * kernel on successful return with the actual chosen tiling layout. + * + * The tiling mode may be demoted to I915_TILING_NONE when the system + * has bit 6 swizzling that can't be managed correctly by GEM. + * + * Buffer contents become undefined when changing tiling_mode. + */ + uint32_t tiling_mode; + + /** + * Stride in bytes for the object when in I915_TILING_X or + * I915_TILING_Y. + */ + uint32_t stride; + + /** + * Returned address bit 6 swizzling required for CPU access through + * mmap mapping. + */ + uint32_t swizzle_mode; +}; + +struct drm_i915_gem_get_tiling { + /** Handle of the buffer to get tiling state for. */ + uint32_t handle; + + /** + * Current tiling mode for the object (I915_TILING_NONE, I915_TILING_X, + * I915_TILING_Y). + */ + uint32_t tiling_mode; + + /** + * Returned address bit 6 swizzling required for CPU access through + * mmap mapping. + */ + uint32_t swizzle_mode; +}; + +struct drm_i915_gem_get_aperture { + /** Total size of the aperture used by i915_gem_execbuffer, in bytes */ + uint64_t aper_size; + + /** + * Available space in the aperture used by i915_gem_execbuffer, in + * bytes + */ + uint64_t aper_available_size; +}; + #endif /* _I915_DRM_H */
--- a/usr/src/uts/intel/io/drm/i915_drv.c Fri Dec 04 16:46:47 2009 -0800 +++ b/usr/src/uts/intel/io/drm/i915_drv.c Sat Dec 05 13:25:40 2009 +0800 @@ -7,6 +7,7 @@ /* * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * Copyright (c) 2009, Intel Corporation. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -431,23 +432,120 @@ vga_reg_put8(®map, VGA_DACDATA, s3_priv->saveDACDATA[i]); } -static int -i915_resume(struct drm_device *dev) +/** + * i915_save_display - save display & mode info + * @dev: DRM device + * + * Save mode timings and display info. + */ +void i915_save_display(struct drm_device *dev) { - ddi_acc_handle_t conf_hdl; struct s3_i915_private *s3_priv = dev->s3_private; - int i; + + /* Display arbitration control */ + s3_priv->saveDSPARB = S3_READ(DSPARB); + + /* + * Pipe & plane A info. + */ + s3_priv->savePIPEACONF = S3_READ(PIPEACONF); + s3_priv->savePIPEASRC = S3_READ(PIPEASRC); + s3_priv->saveFPA0 = S3_READ(FPA0); + s3_priv->saveFPA1 = S3_READ(FPA1); + s3_priv->saveDPLL_A = S3_READ(DPLL_A); + if (IS_I965G(dev)) + s3_priv->saveDPLL_A_MD = S3_READ(DPLL_A_MD); + s3_priv->saveHTOTAL_A = S3_READ(HTOTAL_A); + s3_priv->saveHBLANK_A = S3_READ(HBLANK_A); + s3_priv->saveHSYNC_A = S3_READ(HSYNC_A); + s3_priv->saveVTOTAL_A = S3_READ(VTOTAL_A); + s3_priv->saveVBLANK_A = S3_READ(VBLANK_A); + s3_priv->saveVSYNC_A = S3_READ(VSYNC_A); + s3_priv->saveBCLRPAT_A = S3_READ(BCLRPAT_A); + + s3_priv->saveDSPACNTR = S3_READ(DSPACNTR); + s3_priv->saveDSPASTRIDE = S3_READ(DSPASTRIDE); + s3_priv->saveDSPASIZE = S3_READ(DSPASIZE); + s3_priv->saveDSPAPOS = S3_READ(DSPAPOS); + s3_priv->saveDSPABASE = S3_READ(DSPABASE); + if (IS_I965G(dev)) { + s3_priv->saveDSPASURF = S3_READ(DSPASURF); + s3_priv->saveDSPATILEOFF = S3_READ(DSPATILEOFF); + } + i915_save_palette(dev, PIPE_A); + s3_priv->savePIPEASTAT = S3_READ(PIPEASTAT); - if (pci_config_setup(dev->dip, &conf_hdl) != DDI_SUCCESS) { - DRM_ERROR(("i915_resume: pci_config_setup fail")); - return (DDI_FAILURE); + /* + * Pipe & plane B info + */ + s3_priv->savePIPEBCONF = S3_READ(PIPEBCONF); + s3_priv->savePIPEBSRC = S3_READ(PIPEBSRC); + s3_priv->saveFPB0 = S3_READ(FPB0); + s3_priv->saveFPB1 = S3_READ(FPB1); + s3_priv->saveDPLL_B = S3_READ(DPLL_B); + if (IS_I965G(dev)) + s3_priv->saveDPLL_B_MD = S3_READ(DPLL_B_MD); + s3_priv->saveHTOTAL_B = S3_READ(HTOTAL_B); + s3_priv->saveHBLANK_B = S3_READ(HBLANK_B); + s3_priv->saveHSYNC_B = S3_READ(HSYNC_B); + s3_priv->saveVTOTAL_B = S3_READ(VTOTAL_B); + s3_priv->saveVBLANK_B = S3_READ(VBLANK_B); + s3_priv->saveVSYNC_B = S3_READ(VSYNC_B); + s3_priv->saveBCLRPAT_A = S3_READ(BCLRPAT_A); + + s3_priv->saveDSPBCNTR = S3_READ(DSPBCNTR); + s3_priv->saveDSPBSTRIDE = S3_READ(DSPBSTRIDE); + s3_priv->saveDSPBSIZE = S3_READ(DSPBSIZE); + s3_priv->saveDSPBPOS = S3_READ(DSPBPOS); + s3_priv->saveDSPBBASE = S3_READ(DSPBBASE); + if (IS_I965GM(dev) || IS_GM45(dev)) { + s3_priv->saveDSPBSURF = S3_READ(DSPBSURF); + s3_priv->saveDSPBTILEOFF = S3_READ(DSPBTILEOFF); } + i915_save_palette(dev, PIPE_B); + s3_priv->savePIPEBSTAT = S3_READ(PIPEBSTAT); + + /* + * CRT state + */ + s3_priv->saveADPA = S3_READ(ADPA); + /* - * Nexus driver will resume pci config space and set the power state - * for its children. So we needn't resume them explicitly here. - * see pci_pre_resume for detail. + * LVDS state */ - pci_config_put8(conf_hdl, LBB, s3_priv->saveLBB); + s3_priv->savePP_CONTROL = S3_READ(PP_CONTROL); + s3_priv->savePFIT_PGM_RATIOS = S3_READ(PFIT_PGM_RATIOS); + s3_priv->saveBLC_PWM_CTL = S3_READ(BLC_PWM_CTL); + if (IS_I965G(dev)) + s3_priv->saveBLC_PWM_CTL2 = S3_READ(BLC_PWM_CTL2); + if (IS_MOBILE(dev) && !IS_I830(dev)) + s3_priv->saveLVDS = S3_READ(LVDS); + if (!IS_I830(dev) && !IS_845G(dev)) + s3_priv->savePFIT_CONTROL = S3_READ(PFIT_CONTROL); + s3_priv->saveLVDSPP_ON = S3_READ(LVDSPP_ON); + s3_priv->saveLVDSPP_OFF = S3_READ(LVDSPP_OFF); + s3_priv->savePP_CYCLE = S3_READ(PP_CYCLE); + + /* FIXME: save TV & SDVO state */ + + /* FBC state */ + s3_priv->saveFBC_CFB_BASE = S3_READ(FBC_CFB_BASE); + s3_priv->saveFBC_LL_BASE = S3_READ(FBC_LL_BASE); + s3_priv->saveFBC_CONTROL2 = S3_READ(FBC_CONTROL2); + s3_priv->saveFBC_CONTROL = S3_READ(FBC_CONTROL); + + /* VGA state */ + s3_priv->saveVCLK_DIVISOR_VGA0 = S3_READ(VCLK_DIVISOR_VGA0); + s3_priv->saveVCLK_DIVISOR_VGA1 = S3_READ(VCLK_DIVISOR_VGA1); + s3_priv->saveVCLK_POST_DIV = S3_READ(VCLK_POST_DIV); + s3_priv->saveVGACNTRL = S3_READ(VGACNTRL); + + i915_save_vga(dev); +} + +void i915_restore_display(struct drm_device *dev) +{ + struct s3_i915_private *s3_priv = dev->s3_private; S3_WRITE(DSPARB, s3_priv->saveDSPARB); @@ -566,6 +664,37 @@ S3_WRITE(VCLK_DIVISOR_VGA1, s3_priv->saveVCLK_DIVISOR_VGA1); S3_WRITE(VCLK_POST_DIV, s3_priv->saveVCLK_POST_DIV); drv_usecwait(150); + + i915_restore_vga(dev); +} +static int +i915_resume(struct drm_device *dev) +{ + ddi_acc_handle_t conf_hdl; + struct s3_i915_private *s3_priv = dev->s3_private; + int i; + + if (pci_config_setup(dev->dip, &conf_hdl) != DDI_SUCCESS) { + DRM_ERROR(("i915_resume: pci_config_setup fail")); + return (DDI_FAILURE); + } + /* + * Nexus driver will resume pci config space and set the power state + * for its children. So we needn't resume them explicitly here. + * see pci_pre_resume for detail. + */ + pci_config_put8(conf_hdl, LBB, s3_priv->saveLBB); + + if (IS_I965G(dev) && IS_MOBILE(dev)) + S3_WRITE(MCHBAR_RENDER_STANDBY, s3_priv->saveRENDERSTANDBY); + if (IS_I965GM(dev)) + (void) S3_READ(MCHBAR_RENDER_STANDBY); + + S3_WRITE(HWS_PGA, s3_priv->saveHWS); + if (IS_I965GM(dev)) + (void) S3_READ(HWS_PGA); + + i915_restore_display(dev); /* Clock gating state */ S3_WRITE (D_STATE, s3_priv->saveD_STATE); @@ -584,14 +713,18 @@ for (i = 0; i < 3; i++) S3_WRITE(SWF30 + (i << 2), s3_priv->saveSWF2[i]); - i915_restore_vga(dev); + if (IS_I965GM(dev)) { + S3_WRITE(I915REG_PGTBL_CTRL, s3_priv->pgtbl_ctl); + (void) S3_READ(I915REG_PGTBL_CTRL); + } - S3_WRITE(I915REG_PGTBL_CTRL, s3_priv->pgtbl_ctl); - (void) pci_config_teardown(&conf_hdl); + drm_agp_rebind(dev); + return (DDI_SUCCESS); } + static int i915_suspend(struct drm_device *dev) { @@ -599,7 +732,6 @@ struct s3_i915_private *s3_priv = dev->s3_private; int i; - if (pci_config_setup(dev->dip, &conf_hdl) != DDI_SUCCESS) { DRM_ERROR(("i915_suspend: pci_config_setup fail")); return (DDI_FAILURE); @@ -611,109 +743,19 @@ */ s3_priv->saveLBB = pci_config_get8(conf_hdl, LBB); - /* Display arbitration control */ - s3_priv->saveDSPARB = S3_READ(DSPARB); - - /* - * Pipe & plane A info. - */ - s3_priv->savePIPEACONF = S3_READ(PIPEACONF); - s3_priv->savePIPEASRC = S3_READ(PIPEASRC); - s3_priv->saveFPA0 = S3_READ(FPA0); - s3_priv->saveFPA1 = S3_READ(FPA1); - s3_priv->saveDPLL_A = S3_READ(DPLL_A); - if (IS_I965G(dev)) - s3_priv->saveDPLL_A_MD = S3_READ(DPLL_A_MD); - s3_priv->saveHTOTAL_A = S3_READ(HTOTAL_A); - s3_priv->saveHBLANK_A = S3_READ(HBLANK_A); - s3_priv->saveHSYNC_A = S3_READ(HSYNC_A); - s3_priv->saveVTOTAL_A = S3_READ(VTOTAL_A); - s3_priv->saveVBLANK_A = S3_READ(VBLANK_A); - s3_priv->saveVSYNC_A = S3_READ(VSYNC_A); - s3_priv->saveBCLRPAT_A = S3_READ(BCLRPAT_A); - - s3_priv->saveDSPACNTR = S3_READ(DSPACNTR); - s3_priv->saveDSPASTRIDE = S3_READ(DSPASTRIDE); - s3_priv->saveDSPASIZE = S3_READ(DSPASIZE); - s3_priv->saveDSPAPOS = S3_READ(DSPAPOS); - s3_priv->saveDSPABASE = S3_READ(DSPABASE); - if (IS_I965G(dev)) { - s3_priv->saveDSPASURF = S3_READ(DSPASURF); - s3_priv->saveDSPATILEOFF = S3_READ(DSPATILEOFF); - } - i915_save_palette(dev, PIPE_A); - s3_priv->savePIPEASTAT = S3_READ(PIPEASTAT); + if (IS_I965G(dev) && IS_MOBILE(dev)) + s3_priv->saveRENDERSTANDBY = S3_READ(MCHBAR_RENDER_STANDBY); - /* - * Pipe & plane B info - */ - s3_priv->savePIPEBCONF = S3_READ(PIPEBCONF); - s3_priv->savePIPEBSRC = S3_READ(PIPEBSRC); - s3_priv->saveFPB0 = S3_READ(FPB0); - s3_priv->saveFPB1 = S3_READ(FPB1); - s3_priv->saveDPLL_B = S3_READ(DPLL_B); - if (IS_I965G(dev)) - s3_priv->saveDPLL_B_MD = S3_READ(DPLL_B_MD); - s3_priv->saveHTOTAL_B = S3_READ(HTOTAL_B); - s3_priv->saveHBLANK_B = S3_READ(HBLANK_B); - s3_priv->saveHSYNC_B = S3_READ(HSYNC_B); - s3_priv->saveVTOTAL_B = S3_READ(VTOTAL_B); - s3_priv->saveVBLANK_B = S3_READ(VBLANK_B); - s3_priv->saveVSYNC_B = S3_READ(VSYNC_B); - s3_priv->saveBCLRPAT_A = S3_READ(BCLRPAT_A); + /* Hardware status page */ + s3_priv->saveHWS = S3_READ(HWS_PGA); - s3_priv->saveDSPBCNTR = S3_READ(DSPBCNTR); - s3_priv->saveDSPBSTRIDE = S3_READ(DSPBSTRIDE); - s3_priv->saveDSPBSIZE = S3_READ(DSPBSIZE); - s3_priv->saveDSPBPOS = S3_READ(DSPBPOS); - s3_priv->saveDSPBBASE = S3_READ(DSPBBASE); - if (IS_I965GM(dev) || IS_GM45(dev)) { - s3_priv->saveDSPBSURF = S3_READ(DSPBSURF); - s3_priv->saveDSPBTILEOFF = S3_READ(DSPBTILEOFF); - } - i915_save_palette(dev, PIPE_B); - s3_priv->savePIPEBSTAT = S3_READ(PIPEBSTAT); - - /* - * CRT state - */ - s3_priv->saveADPA = S3_READ(ADPA); - - /* - * LVDS state - */ - s3_priv->savePP_CONTROL = S3_READ(PP_CONTROL); - s3_priv->savePFIT_PGM_RATIOS = S3_READ(PFIT_PGM_RATIOS); - s3_priv->saveBLC_PWM_CTL = S3_READ(BLC_PWM_CTL); - if (IS_I965G(dev)) - s3_priv->saveBLC_PWM_CTL2 = S3_READ(BLC_PWM_CTL2); - if (IS_MOBILE(dev) && !IS_I830(dev)) - s3_priv->saveLVDS = S3_READ(LVDS); - if (!IS_I830(dev) && !IS_845G(dev)) - s3_priv->savePFIT_CONTROL = S3_READ(PFIT_CONTROL); - s3_priv->saveLVDSPP_ON = S3_READ(LVDSPP_ON); - s3_priv->saveLVDSPP_OFF = S3_READ(LVDSPP_OFF); - s3_priv->savePP_CYCLE = S3_READ(PP_CYCLE); - - /* FIXME: save TV & SDVO state */ - - /* FBC state */ - s3_priv->saveFBC_CFB_BASE = S3_READ(FBC_CFB_BASE); - s3_priv->saveFBC_LL_BASE = S3_READ(FBC_LL_BASE); - s3_priv->saveFBC_CONTROL2 = S3_READ(FBC_CONTROL2); - s3_priv->saveFBC_CONTROL = S3_READ(FBC_CONTROL); + i915_save_display(dev); /* Interrupt state */ s3_priv->saveIIR = S3_READ(IIR); s3_priv->saveIER = S3_READ(IER); s3_priv->saveIMR = S3_READ(IMR); - /* VGA state */ - s3_priv->saveVCLK_DIVISOR_VGA0 = S3_READ(VCLK_DIVISOR_VGA0); - s3_priv->saveVCLK_DIVISOR_VGA1 = S3_READ(VCLK_DIVISOR_VGA1); - s3_priv->saveVCLK_POST_DIV = S3_READ(VCLK_POST_DIV); - s3_priv->saveVGACNTRL = S3_READ(VGACNTRL); - /* Clock gating state */ s3_priv->saveD_STATE = S3_READ(D_STATE); s3_priv->saveCG_2D_DIS = S3_READ(CG_2D_DIS); @@ -732,12 +774,11 @@ for (i = 0; i < 3; i++) s3_priv->saveSWF2[i] = S3_READ(SWF30 + (i << 2)); - - i915_save_vga(dev); /* * Save page table control register */ - s3_priv->pgtbl_ctl = S3_READ(I915REG_PGTBL_CTRL); + if (IS_I965GM(dev)) + s3_priv->pgtbl_ctl = S3_READ(I915REG_PGTBL_CTRL); (void) pci_config_teardown(&conf_hdl); @@ -960,16 +1001,20 @@ driver->buf_priv_size = 1; /* No dev_priv */ driver->load = i915_driver_load; driver->unload = i915_driver_unload; + driver->open = i915_driver_open; driver->preclose = i915_driver_preclose; + driver->postclose = i915_driver_postclose; driver->lastclose = i915_driver_lastclose; driver->device_is_agp = i915_driver_device_is_agp; - driver->get_vblank_counter = i915_get_vblank_counter; driver->enable_vblank = i915_enable_vblank; driver->disable_vblank = i915_disable_vblank; driver->irq_preinstall = i915_driver_irq_preinstall; driver->irq_postinstall = i915_driver_irq_postinstall; driver->irq_uninstall = i915_driver_irq_uninstall; - driver->irq_handler = i915_driver_irq_handler; + driver->irq_handler = i915_driver_irq_handler; + + driver->gem_init_object = i915_gem_init_object; + driver->gem_free_object = i915_gem_free_object; driver->driver_ioctls = i915_ioctls; driver->max_driver_ioctl = i915_max_ioctl;
--- a/usr/src/uts/intel/io/drm/i915_drv.h Fri Dec 04 16:46:47 2009 -0800 +++ b/usr/src/uts/intel/io/drm/i915_drv.h Sat Dec 05 13:25:40 2009 +0800 @@ -5,6 +5,7 @@ /* * * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. + * Copyright (c) 2009, Intel Corporation. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -44,12 +45,14 @@ #define DRIVER_NAME "i915" #define DRIVER_DESC "Intel Graphics" -#define DRIVER_DATE "20060929" +#define DRIVER_DATE "20080730" #if defined(__SVR4) && defined(__sun) #define spinlock_t kmutex_t #endif +#define I915_NUM_PIPE 2 + #define I915_NUM_PIPE 2 /* Interface history: @@ -58,15 +61,19 @@ * 1.2: Add Power Management * 1.3: Add vblank support * 1.4: Fix cmdbuffer path, add heap destroy + * 1.5: Add vblank pipe configuration + * 1.6: - New ioctl for scheduling buffer swaps on vertical blank + * - Support vertical blank on secondary display pipe */ #define DRIVER_MAJOR 1 -#define DRIVER_MINOR 5 +#define DRIVER_MINOR 6 #define DRIVER_PATCHLEVEL 0 #if defined(__linux__) #define I915_HAVE_FENCE #define I915_HAVE_BUFFER #endif +#define I915_HAVE_GEM 1 typedef struct _drm_i915_ring_buffer { int tail_mask; @@ -76,6 +83,7 @@ int tail; int space; drm_local_map_t map; + struct drm_gem_object *ring_obj; } drm_i915_ring_buffer_t; struct mem_block { @@ -102,6 +110,8 @@ uint32_t saveDSPACNTR; uint32_t saveDSPBCNTR; uint32_t saveDSPARB; + uint32_t saveRENDERSTANDBY; + uint32_t saveHWS; uint32_t savePIPEACONF; uint32_t savePIPEBCONF; uint32_t savePIPEASRC; @@ -187,6 +197,22 @@ uint8_t saveCR[37]; } s3_i915_private_t; +struct drm_i915_error_state { + u32 eir; + u32 pgtbl_er; + u32 pipeastat; + u32 pipebstat; + u32 ipeir; + u32 ipehr; + u32 instdone; + u32 acthd; + u32 instpm; + u32 instps; + u32 instdone1; + u32 seqno; + struct timeval time; +}; + typedef struct drm_i915_private { struct drm_device *dev; @@ -202,38 +228,214 @@ uint32_t counter; unsigned int status_gfx_addr; drm_local_map_t hws_map; - + struct drm_gem_object *hws_obj; + unsigned int cpp; + int back_offset; + int front_offset; + int current_page; + int page_flipping; wait_queue_head_t irq_queue; atomic_t irq_received; + /** Protects user_irq_refcount and irq_mask_reg */ + spinlock_t user_irq_lock; + /** Refcount for i915_user_irq_get() versus i915_user_irq_put(). */ + int user_irq_refcount; + /** Cached value of IMR to avoid reads in updating the bitfield */ + int irq_mask_reg; + uint32_t pipestat[2]; int tex_lru_log_granularity; int allow_batchbuffer; struct mem_block *agp_heap; unsigned int sr01, adpa, ppcr, dvob, dvoc, lvds; int vblank_pipe; - spinlock_t user_irq_lock; - int user_irq_refcount; - int fence_irq_on; - uint32_t irq_mask_reg; - int irq_enabled; + + spinlock_t error_lock; + struct drm_i915_error_state *first_error; + + struct { + struct drm_mm gtt_space; + + drm_local_map_t gtt_mapping; + /** + * List of objects currently involved in rendering from the + * ringbuffer. + * + * A reference is held on the buffer while on this list. + */ + struct list_head active_list; + + /** + * List of objects which are not in the ringbuffer but which + * still have a write_domain which needs to be flushed before + * unbinding. + * + * A reference is held on the buffer while on this list. + */ + struct list_head flushing_list; + + /** + * LRU list of objects which are not in the ringbuffer and + * are ready to unbind, but are still in the GTT. + * + * A reference is not held on the buffer while on this list, + * as merely being GTT-bound shouldn't prevent its being + * freed, and we'll pull it off the list in the free path. + */ + struct list_head inactive_list; -#ifdef I915_HAVE_FENCE - uint32_t flush_sequence; - uint32_t flush_flags; - uint32_t flush_pending; - uint32_t saved_flush_status; -#endif -#ifdef I915_HAVE_BUFFER - void *agp_iomap; -#endif - spinlock_t swaps_lock; - drm_i915_vbl_swap_t vbl_swaps; - unsigned int swaps_pending; + /** + * List of breadcrumbs associated with GPU requests currently + * outstanding. + */ + struct list_head request_list; + + 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. + * + * This is set between LeaveVT and EnterVT. It needs to be + * replaced with a semaphore. It also needs to be + * 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; + + /** Bit 6 swizzling required for X tiling */ + uint32_t bit_6_swizzle_x; + /** Bit 6 swizzling required for Y tiling */ + uint32_t bit_6_swizzle_y; + } mm; } drm_i915_private_t; +struct drm_track { + struct drm_track *next, *prev; + caddr_t contain_ptr; + struct drm_gem_object *obj; + uint32_t name; + uint64_t offset; + +}; + +/** driver private structure attached to each drm_gem_object */ +struct drm_i915_gem_object { + /** This object's place on the active/flushing/inactive lists */ + struct list_head list; + + struct drm_gem_object *obj; + + /** Current space allocated to this object in the GTT, if any. */ + struct drm_mm_node *gtt_space; + + + /** + * This is set if the object is on the active or flushing lists + * (has pending rendering), and is not set if it's on inactive (ready + * to be unbound). + */ + 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. */ + int agp_mem; + + caddr_t *page_list; + + pfn_t *pfnarray; + /** + * Current offset of the object in GTT space. + * + * This is the same as gtt_space->start + */ + uint32_t gtt_offset; + + /** Boolean whether this object has a valid gtt offset. */ + int gtt_bound; + + /** How many users have pinned this object in GTT space */ + int pin_count; + + /** Breadcrumb of last rendering to the buffer. */ + uint32_t last_rendering_seqno; + + /** Current tiling mode for the object. */ + uint32_t tiling_mode; + uint32_t stride; + /** + * Flagging of which individual pages are valid in GEM_DOMAIN_CPU when + * GEM_DOMAIN_CPU is not in the object's read domain. + */ + uint8_t *page_cpu_valid; + /** User space pin count and filp owning the pin */ + uint32_t user_pin_count; + struct drm_file *pin_filp; + /** + * Used for checking the object doesn't appear more than once + * in an execbuffer object list. + */ + int in_execbuffer; +}; + +/** + * Request queue structure. + * + * The request queue allows us to note sequence numbers that have been emitted + * and may be associated with active buffers to be retired. + * + * By keeping this list, we can avoid having to do questionable + * sequence-number comparisons on buffer last_rendering_seqnos, and associate + * an emission time with seqnos for tracking how far ahead of the GPU we are. + */ +struct drm_i915_gem_request { + struct list_head list; + + /** GEM sequence number associated with this request. */ + uint32_t seqno; + + /** Time at which this request was emitted, in jiffies. */ + unsigned long emitted_jiffies; + + /** Cache domains that were flushed at the start of the request. */ + uint32_t flush_domains; + +}; + +struct drm_i915_file_private { + struct { + uint32_t last_gem_seqno; + uint32_t last_gem_throttle_seqno; + } mm; +}; + + enum intel_chip_family { CHIP_I8XX = 0x01, CHIP_I9XX = 0x02, @@ -243,13 +445,18 @@ extern drm_ioctl_desc_t i915_ioctls[]; extern int i915_max_ioctl; +extern void i915_save_display(struct drm_device *dev); +extern void i915_restore_display(struct drm_device *dev); /* i915_dma.c */ extern void i915_kernel_lost_context(drm_device_t * dev); extern int i915_driver_load(struct drm_device *, unsigned long flags); extern int i915_driver_unload(struct drm_device *dev); +extern int i915_driver_open(drm_device_t * dev, drm_file_t *file_priv); extern void i915_driver_lastclose(drm_device_t * dev); extern void i915_driver_preclose(drm_device_t * dev, drm_file_t *filp); +extern void i915_driver_postclose(drm_device_t * dev, + struct drm_file *file_priv); extern int i915_driver_device_is_agp(drm_device_t * dev); extern long i915_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); @@ -257,9 +464,8 @@ struct drm_clip_rect __user *boxes, int i, int DR1, int DR4); extern void i915_emit_breadcrumb(struct drm_device *dev); -extern void i915_dispatch_flip(struct drm_device * dev, int pipes, int sync); extern void i915_emit_mi_flush(drm_device_t *dev, uint32_t flush); - +extern void i915_handle_error(struct drm_device *dev); /* i915_irq.c */ extern int i915_irq_emit(DRM_IOCTL_ARGS); @@ -268,14 +474,15 @@ extern int i915_enable_vblank(struct drm_device *dev, int crtc); extern void i915_disable_vblank(struct drm_device *dev, int crtc); extern u32 i915_get_vblank_counter(struct drm_device *dev, int crtc); +extern u32 gm45_get_vblank_counter(struct drm_device *dev, int crtc); extern irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS); extern int i915_driver_irq_preinstall(drm_device_t * dev); extern void i915_driver_irq_postinstall(drm_device_t * dev); extern void i915_driver_irq_uninstall(drm_device_t * dev); extern int i915_emit_irq(drm_device_t * dev); extern int i915_vblank_swap(DRM_IOCTL_ARGS); -extern void i915_user_irq_on(drm_i915_private_t *dev_priv); -extern void i915_user_irq_off(drm_i915_private_t *dev_priv); +extern void i915_user_irq_on(drm_device_t * dev); +extern void i915_user_irq_off(drm_device_t * dev); extern int i915_vblank_pipe_set(DRM_IOCTL_ARGS); extern int i915_vblank_pipe_get(DRM_IOCTL_ARGS); @@ -292,6 +499,45 @@ extern void mark_block(drm_device_t *, struct mem_block *, int); extern void free_block(struct mem_block *); +/* i915_gem.c */ +int i915_gem_init_ioctl(DRM_IOCTL_ARGS); +int i915_gem_create_ioctl(DRM_IOCTL_ARGS); +int i915_gem_pread_ioctl(DRM_IOCTL_ARGS); +int i915_gem_pwrite_ioctl(DRM_IOCTL_ARGS); +int i915_gem_mmap_ioctl(DRM_IOCTL_ARGS); +int i915_gem_set_domain_ioctl(DRM_IOCTL_ARGS); +int i915_gem_sw_finish_ioctl(DRM_IOCTL_ARGS); +int i915_gem_execbuffer(DRM_IOCTL_ARGS); +int i915_gem_pin_ioctl(DRM_IOCTL_ARGS); +int i915_gem_unpin_ioctl(DRM_IOCTL_ARGS); +int i915_gem_busy_ioctl(DRM_IOCTL_ARGS); +int i915_gem_throttle_ioctl(DRM_IOCTL_ARGS); +int i915_gem_entervt_ioctl(DRM_IOCTL_ARGS); +int i915_gem_leavevt_ioctl(DRM_IOCTL_ARGS); +int i915_gem_set_tiling(DRM_IOCTL_ARGS); +int i915_gem_get_tiling(DRM_IOCTL_ARGS); +int i915_gem_get_aperture_ioctl(DRM_IOCTL_ARGS); +void i915_gem_load(struct drm_device *dev); +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); +int i915_gem_object_unbind(struct drm_gem_object *obj, uint32_t type); +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); +void i915_gem_retire_work_handler(void *dev); +void i915_gem_clflush_object(struct drm_gem_object *obj); +int i915_gem_init_ringbuffer(struct drm_device *dev); + +/* i915_gem_tiling.c */ +void i915_gem_detect_bit_6_swizzle(struct drm_device *dev); + +/* i915_gem_debug.c */ +void i915_gem_command_decode(uint32_t *data, int count, + uint32_t hw_offset, struct drm_device *dev); +/* i915_gem_regdump.c */ +int i915_reg_dump_show(struct drm_device *dev, void *v); #ifdef I915_HAVE_FENCE /* i915_fence.c */ @@ -327,7 +573,7 @@ #define S3_WRITE(reg, val) \ *(uint32_t volatile *)((uintptr_t)s3_priv->saveAddr + (reg)) = (val) -#define I915_VERBOSE 0 +#define I915_VERBOSE 0 #define I915_RING_VALIDATE 0 #if I915_RING_VALIDATE @@ -340,9 +586,20 @@ #define RING_LOCALS unsigned int outring, ringmask, outcount; \ volatile unsigned char *virt; + +#define I915_RING_VALIDATE 0 + +#if I915_RING_VALIDATE +void i915_ring_validate(struct drm_device *dev, const char *func, int line); +#define I915_RING_DO_VALIDATE(dev) i915_ring_validate(dev, __FUNCTION__, __LINE__) +#else +#define I915_RING_DO_VALIDATE(dev) +#endif + #if I915_VERBOSE #define BEGIN_LP_RING(n) do { \ DRM_DEBUG("BEGIN_LP_RING(%d)\n", (n)); \ + DRM_DEBUG("dev_priv->ring.virtual_start (%lx)\n", (dev_priv->ring.virtual_start)); \ I915_RING_DO_VALIDATE(dev); \ if (dev_priv->ring.space < (n)*4) \ (void) i915_wait_ring(dev, (n)*4, __FUNCTION__); \ @@ -401,6 +658,10 @@ /* Extended config space */ #define LBB 0xf4 +#define GDRST 0xc0 +#define GDRST_FULL (0<<2) +#define GDRST_RENDER (1<<2) +#define GDRST_MEDIA (3<<2) /* VGA stuff */ @@ -469,6 +730,7 @@ #define BB2_END_ADDR_MASK (~0x7) #define I915REG_PGTBL_CTRL 0x2020 +#define IPEIR 0x02088 #define HWSTAM 0x02098 #define IIR 0x020a4 #define IMR 0x020a8 @@ -479,6 +741,22 @@ #define PIPEBSTAT 0x71024 #define ACTHD_I965 0x02074 #define HWS_PGA 0x02080 +#define IPEIR_I965 0x02064 +#define IPEHR_I965 0x02068 +#define INSTDONE_I965 0x0206c +#define INSTPS 0x02070 /* 965+ only */ +#define INSTDONE1 0x0207c /* 965+ only */ +#define IPEHR 0x0208c +#define INSTDONE 0x02090 +#define EIR 0x020b0 +#define EMR 0x020b4 +#define ESR 0x020b8 +#define GM45_ERROR_PAGE_TABLE (1<<5) +#define GM45_ERROR_MEM_PRIV (1<<4) +#define I915_ERROR_PAGE_TABLE (1<<4) +#define GM45_ERROR_CP_PRIV (1<<3) +#define I915_ERROR_MEMORY_REFRESH (1<<1) +#define I915_ERROR_INSTRUCTION (1<<0) #define PIPE_VBLANK_INTERRUPT_ENABLE (1UL<<17) #define I915_VBLANK_CLEAR (1UL<<1) @@ -526,8 +804,10 @@ #define RING_VALID_MASK 0x00000001 #define RING_VALID 0x00000001 #define RING_INVALID 0x00000000 +#define PGTBL_ER 0x02024 #define PRB0_TAIL 0x02030 #define PRB0_HEAD 0x02034 +#define PRB0_START 0x02038 #define PRB0_CTL 0x0203c #define GFX_OP_SCISSOR ((0x3<<29)|(0x1c<<24)|(0x10<<19)) #define SC_UPDATE_SCISSOR (0x1<<1) @@ -578,11 +858,25 @@ #define CMD_OP_DESTBUFFER_INFO ((0x3<<29)|(0x1d<<24)|(0x8e<<16)|1) -#define BREADCRUMB_BITS 31 -#define BREADCRUMB_MASK ((1U << BREADCRUMB_BITS) - 1) - -#define READ_BREADCRUMB(dev_priv) (((volatile u32*)(dev_priv->hw_status_page))[5]) +/** + * Reads a dword out of the status page, which is written to from the command + * queue by automatic updates, MI_REPORT_HEAD, MI_STORE_DATA_INDEX, or + * MI_STORE_DATA_IMM. + * + * The following dwords have a reserved meaning: + * 0x00: ISR copy, updated when an ISR bit not set in the HWSTAM changes. + * 0x04: ring 0 head pointer + * 0x05: ring 1 head pointer (915-class) + * 0x06: ring 2 head pointer (915-class) + * 0x10-0x1b: Context status DWords (GM45) + * 0x1f: Last written status offset. (GM45) + * + * The area from dword 0x20 to 0x3ff is available for driver usage. + */ #define READ_HWSP(dev_priv, reg) (((volatile u32*)(dev_priv->hw_status_page))[reg]) +#define READ_BREADCRUMB(dev_priv) READ_HWSP(dev_priv, I915_BREADCRUMB_INDEX) +#define I915_GEM_HWS_INDEX 0x20 +#define I915_BREADCRUMB_INDEX 0x21 /* * add here for S3 support @@ -702,6 +996,34 @@ #define PALETTE_A 0x0a000 #define PALETTE_B 0x0a800 +/* MCH MMIO space */ + +/* + * MCHBAR mirror. + * + * This mirrors the MCHBAR MMIO space whose location is determined by + * device 0 function 0's pci config register 0x44 or 0x48 and matches it in + * every way. It is not accessible from the CP register read instructions. + * + */ +#define MCHBAR_MIRROR_BASE 0x10000 + +/** 915-945 and GM965 MCH register controlling DRAM channel access */ +#define DCC 0x10200 +#define DCC_ADDRESSING_MODE_SINGLE_CHANNEL (0 << 0) +#define DCC_ADDRESSING_MODE_DUAL_CHANNEL_ASYMMETRIC (1 << 0) +#define DCC_ADDRESSING_MODE_DUAL_CHANNEL_INTERLEAVED (2 << 0) +#define DCC_ADDRESSING_MODE_MASK (3 << 0) +#define DCC_CHANNEL_XOR_DISABLE (1 << 10) +#define DCC_CHANNEL_XOR_BIT_17 (1 << 9) + +/** 965 MCH register controlling DRAM channel configuration */ +#define C0DRB3 0x10206 +#define C1DRB3 0x10606 + +/** GM965 GM45 render standby register */ +#define MCHBAR_RENDER_STANDBY 0x111B8 + #define FPA0 0x06040 #define FPA1 0x06044 #define FPB0 0x06048 @@ -834,11 +1156,13 @@ * - pipe enabled * - LVDS/DVOB/DVOC on */ -# define PP_READY (1 << 30) # define PP_SEQUENCE_NONE (0 << 28) -# define PP_SEQUENCE_ON (1 << 28) # define PP_SEQUENCE_OFF (2 << 28) -# define PP_SEQUENCE_MASK 0x30000000 +#define PP_READY (1 << 30) +#define PP_SEQUENCE_NONE (0 << 28) +#define PP_SEQUENCE_ON (1 << 28) +#define PP_SEQUENCE_OFF (2 << 28) +#define PP_SEQUENCE_MASK 0x30000000 #define PP_CONTROL 0x61204 -# define POWER_TARGET_ON (1 << 0) +#define POWER_TARGET_ON (1 << 0) #define LVDSPP_ON 0x61208 #define LVDSPP_OFF 0x6120c @@ -864,8 +1188,10 @@ #define FBC_CTL_INTERVAL_SHIFT (16) #define FBC_CTL_UNCOMPRESSIBLE (1<<14) #define FBC_CTL_STRIDE_SHIFT (5) -#define FBC_CTL_FENCENO (1<<0) #define FBC_COMMAND 0x0320c -#define FBC_CMD_COMPRESS (1<<0) #define FBC_STATUS 0x03210 +#define FBC_CTL_FENCENO (1<<0) +#define FBC_COMMAND 0x0320c +#define FBC_CMD_COMPRESS (1<<0) +#define FBC_STATUS 0x03210 #define FBC_STAT_COMPRESSING (1<<31) #define FBC_STAT_COMPRESSED (1<<30) #define FBC_STAT_MODIFIED (1<<29) @@ -914,6 +1240,7 @@ #define I915_DISPLAY_PIPE_B_EVENT_INTERRUPT (1<<4) #define I915_DEBUG_INTERRUPT (1<<2) #define I915_USER_INTERRUPT (1<<1) +#define I915_ASLE_INTERRUPT (1<<0) #define I915_FIFO_UNDERRUN_STATUS (1UL<<31) #define I915_CRC_ERROR_ENABLE (1UL<<29) @@ -941,6 +1268,12 @@ #define PIPE_VBLANK_INTERRUPT_STATUS (1UL<<1) #define I915_OVERLAY_UPDATED_STATUS (1UL<<0) +/* GM45+ just has to be different */ +#define PIPEA_FRMCOUNT_GM45 0x70040 +#define PIPEA_FLIPCOUNT_GM45 0x70044 +#define PIPEB_FRMCOUNT_GM45 0x71040 +#define PIPEB_FLIPCOUNT_GM45 0x71044 + /* * Some BIOS scratch area registers. The 845 (and 830?) store the amount * of video memory available to the BIOS in SWF1. @@ -978,6 +1311,7 @@ #define PCI_DEVICE_ID_INTEL_82Q45_IG 0x2e12 #define PCI_DEVICE_ID_INTEL_82G45_IG 0x2e22 #define PCI_DEVICE_ID_INTEL_82G41_IG 0x2e32 +#define PCI_DEVICE_ID_INTEL_82B43_IG 0x2e42 #define IS_I830(dev) ((dev)->pci_device == PCI_DEVICE_ID_INTEL_82830_CGC) @@ -1002,6 +1336,7 @@ (dev)->pci_device == PCI_DEVICE_ID_INTEL_EL_IG || \ (dev)->pci_device == PCI_DEVICE_ID_INTEL_82Q45_IG || \ (dev)->pci_device == PCI_DEVICE_ID_INTEL_82G45_IG || \ + (dev)->pci_device == PCI_DEVICE_ID_INTEL_82B43_IG || \ (dev)->pci_device == PCI_DEVICE_ID_INTEL_82G41_IG) #define IS_I965GM(dev) ((dev)->pci_device == PCI_DEVICE_ID_INTEL_GM965_IG) @@ -1011,6 +1346,7 @@ #define IS_G4X(dev) ((dev)->pci_device == PCI_DEVICE_ID_INTEL_EL_IG || \ (dev)->pci_device == PCI_DEVICE_ID_INTEL_82Q45_IG || \ (dev)->pci_device == PCI_DEVICE_ID_INTEL_82G45_IG || \ + (dev)->pci_device == PCI_DEVICE_ID_INTEL_82B43_IG || \ (dev)->pci_device == PCI_DEVICE_ID_INTEL_82G41_IG) #define IS_G33(dev) ((dev)->pci_device == PCI_DEVICE_ID_INTEL_82G33_IG || \ @@ -1023,6 +1359,15 @@ #define IS_MOBILE(dev) (IS_I830(dev) || IS_I85X(dev) || IS_I915GM(dev) || \ IS_I945GM(dev) || IS_I965GM(dev) || IS_GM45(dev)) +#define IS_IGDG(dev) ((dev)->pci_device == 0xa001) +#define IS_IGDGM(dev) ((dev)->pci_device == 0xa011) +#define IS_IGD(dev) (IS_IGDG(dev) || IS_IGDGM(dev)) + #define I915_NEED_GFX_HWS(dev) (IS_G33(dev) || IS_GM45(dev) || IS_G4X(dev)) +/* With the 945 and later, Y tiling got adjusted so that it was 32 128-byte + * rows, which changed the alignment requirements and fence programming. + */ +#define HAS_128_BYTE_Y_TILING(dev) (IS_I9XX(dev) && !(IS_I915G(dev) || \ + IS_I915GM(dev))) #endif /* _I915_DRV_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/intel/io/drm/i915_gem.c Sat Dec 05 13:25:40 2009 +0800 @@ -0,0 +1,2906 @@ +/* BEGIN CSTYLED */ + +/* + * Copyright (c) 2009, Intel Corporation. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Eric Anholt <eric@anholt.net> + * + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <sys/x86_archext.h> +#include <sys/vfs_opreg.h> +#include "drmP.h" +#include "drm.h" +#include "i915_drm.h" +#include "i915_drv.h" + +#ifndef roundup +#define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) +#endif /* !roundup */ + +#define I915_GEM_GPU_DOMAINS (~(I915_GEM_DOMAIN_CPU | I915_GEM_DOMAIN_GTT)) + +static timeout_id_t worktimer_id = NULL; + +extern int drm_mm_init(struct drm_mm *mm, + unsigned long start, unsigned long size); +extern void drm_mm_put_block(struct drm_mm_node *cur); +extern int choose_addr(struct as *as, caddr_t *addrp, size_t len, offset_t off, + int vacalign, uint_t flags); + +static void +i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj, + uint32_t read_domains, + uint32_t write_domain); +static void i915_gem_object_flush_gpu_write_domain(struct drm_gem_object *obj); +static void i915_gem_object_flush_gtt_write_domain(struct drm_gem_object *obj); +static void i915_gem_object_flush_cpu_write_domain(struct drm_gem_object *obj); +static int i915_gem_object_set_to_gtt_domain(struct drm_gem_object *obj, + int write); +static int i915_gem_object_set_to_cpu_domain(struct drm_gem_object *obj, + int write); +static int i915_gem_object_set_cpu_read_domain_range(struct drm_gem_object *obj, + uint64_t offset, + uint64_t size); +static void i915_gem_object_set_to_full_cpu_read_domain(struct drm_gem_object *obj); +static void i915_gem_object_free_page_list(struct drm_gem_object *obj); +static int i915_gem_object_wait_rendering(struct drm_gem_object *obj); +static int i915_gem_object_get_page_list(struct drm_gem_object *obj); + +static void +i915_gem_cleanup_ringbuffer(struct drm_device *dev); + +/*ARGSUSED*/ +int +i915_gem_init_ioctl(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_gem_init args; + + if (dev->driver->use_gem != 1) + return ENODEV; + + DRM_COPYFROM_WITH_RETURN(&args, + (struct drm_i915_gem_init *) data, sizeof(args)); + + spin_lock(&dev->struct_mutex); + + if ((args.gtt_start >= args.gtt_end) || + ((args.gtt_start & (PAGE_SIZE - 1)) != 0) || + ((args.gtt_end & (PAGE_SIZE - 1)) != 0)) { + spin_unlock(&dev->struct_mutex); + DRM_ERROR("i915_gem_init_ioctel invalid arg 0x%lx args.start 0x%lx end 0x%lx", &args, args.gtt_start, args.gtt_end); + return EINVAL; + } + + dev->gtt_total = (uint32_t) (args.gtt_end - args.gtt_start); + + drm_mm_init(&dev_priv->mm.gtt_space, (unsigned long) args.gtt_start, + dev->gtt_total); + DRM_DEBUG("i915_gem_init_ioctl dev->gtt_total %x, dev_priv->mm.gtt_space 0x%x gtt_start 0x%lx", dev->gtt_total, dev_priv->mm.gtt_space, args.gtt_start); + ASSERT(dev->gtt_total != 0); + + spin_unlock(&dev->struct_mutex); + + + return 0; +} + +/*ARGSUSED*/ +int +i915_gem_get_aperture_ioctl(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + struct drm_i915_gem_get_aperture args; + int ret; + + if (dev->driver->use_gem != 1) + return ENODEV; + + args.aper_size = (uint64_t)dev->gtt_total; + args.aper_available_size = (args.aper_size - + atomic_read(&dev->pin_memory)); + + ret = DRM_COPY_TO_USER((struct drm_i915_gem_get_aperture __user *) data, &args, sizeof(args)); + + if ( ret != 0) + DRM_ERROR(" i915_gem_get_aperture_ioctl error! %d", ret); + + DRM_DEBUG("i915_gem_get_aaperture_ioctl called sizeof %d, aper_size 0x%x, aper_available_size 0x%x\n", sizeof(args), dev->gtt_total, args.aper_available_size); + + return 0; +} + +/** + * Creates a new mm object and returns a handle to it. + */ +/*ARGSUSED*/ +int +i915_gem_create_ioctl(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + struct drm_i915_gem_create args; + struct drm_gem_object *obj; + int handlep; + int ret; + + if (dev->driver->use_gem != 1) + return ENODEV; + + DRM_COPYFROM_WITH_RETURN(&args, + (struct drm_i915_gem_create *) data, sizeof(args)); + + + args.size = (uint64_t) roundup(args.size, PAGE_SIZE); + + if (args.size == 0) { + DRM_ERROR("Invalid obj size %d", args.size); + return EINVAL; + } + /* Allocate the new object */ + obj = drm_gem_object_alloc(dev, args.size); + if (obj == NULL) { + DRM_ERROR("Failed to alloc obj"); + return ENOMEM; + } + + ret = drm_gem_handle_create(fpriv, obj, &handlep); + spin_lock(&dev->struct_mutex); + drm_gem_object_handle_unreference(obj); + spin_unlock(&dev->struct_mutex); + if (ret) + return ret; + + args.handle = handlep; + + ret = DRM_COPY_TO_USER((struct drm_i915_gem_create *) data, &args, sizeof(args)); + + if ( ret != 0) + DRM_ERROR(" gem create error! %d", ret); + + DRM_DEBUG("i915_gem_create_ioctl object name %d, size 0x%lx, list 0x%lx, obj 0x%lx",handlep, args.size, &fpriv->object_idr, obj); + + return 0; +} + +/** + * Reads data from the object referenced by handle. + * + * On error, the contents of *data are undefined. + */ +/*ARGSUSED*/ +int +i915_gem_pread_ioctl(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + struct drm_i915_gem_pread args; + struct drm_gem_object *obj; + int ret; + + if (dev->driver->use_gem != 1) + return ENODEV; + + DRM_COPYFROM_WITH_RETURN(&args, + (struct drm_i915_gem_pread __user *) data, sizeof(args)); + + obj = drm_gem_object_lookup(fpriv, args.handle); + if (obj == NULL) + return EBADF; + + /* Bounds check source. + * + * XXX: This could use review for overflow issues... + */ + if (args.offset > obj->size || args.size > obj->size || + args.offset + args.size > obj->size) { + drm_gem_object_unreference(obj); + DRM_ERROR("i915_gem_pread_ioctl invalid args"); + return EINVAL; + } + + spin_lock(&dev->struct_mutex); + + ret = i915_gem_object_set_cpu_read_domain_range(obj, args.offset, args.size); + if (ret != 0) { + drm_gem_object_unreference(obj); + spin_unlock(&dev->struct_mutex); + DRM_ERROR("pread failed to read domain range ret %d!!!", ret); + return EFAULT; + } + + unsigned long unwritten = 0; + uint32_t *user_data; + user_data = (uint32_t *) (uintptr_t) args.data_ptr; + + unwritten = DRM_COPY_TO_USER(user_data, obj->kaddr + args.offset, args.size); + if (unwritten) { + ret = EFAULT; + DRM_ERROR("i915_gem_pread error!!! unwritten %d", unwritten); + } + + drm_gem_object_unreference(obj); + spin_unlock(&dev->struct_mutex); + + return ret; +} + +/*ARGSUSED*/ +static int +i915_gem_gtt_pwrite(struct drm_device *dev, struct drm_gem_object *obj, + struct drm_i915_gem_pwrite *args, + struct drm_file *file_priv) +{ + uint32_t *user_data; + int ret = 0; + unsigned long unwritten = 0; + + user_data = (uint32_t *) (uintptr_t) args->data_ptr; + spin_lock(&dev->struct_mutex); + ret = i915_gem_object_pin(obj, 0); + if (ret) { + spin_unlock(&dev->struct_mutex); + DRM_ERROR("i915_gem_gtt_pwrite failed to pin ret %d", ret); + return ret; + } + + ret = i915_gem_object_set_to_gtt_domain(obj, 1); + if (ret) + goto err; + + DRM_DEBUG("obj %d write domain 0x%x read domain 0x%x", obj->name, obj->write_domain, obj->read_domains); + + unwritten = DRM_COPY_FROM_USER(obj->kaddr + args->offset, user_data, args->size); + if (unwritten) { + ret = EFAULT; + DRM_ERROR("i915_gem_gtt_pwrite error!!! unwritten %d", unwritten); + goto err; + } + +err: + i915_gem_object_unpin(obj); + spin_unlock(&dev->struct_mutex); + if (ret) + DRM_ERROR("i915_gem_gtt_pwrite error %d", ret); + return ret; +} + +/*ARGSUSED*/ +int +i915_gem_shmem_pwrite(struct drm_device *dev, struct drm_gem_object *obj, + struct drm_i915_gem_pwrite *args, + struct drm_file *file_priv) +{ + DRM_ERROR(" i915_gem_shmem_pwrite Not support"); + return -1; +} + +/** + * Writes data to the object referenced by handle. + * + * On error, the contents of the buffer that were to be modified are undefined. + */ +/*ARGSUSED*/ +int +i915_gem_pwrite_ioctl(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + struct drm_i915_gem_pwrite args; + struct drm_gem_object *obj; + struct drm_i915_gem_object *obj_priv; + int ret = 0; + + if (dev->driver->use_gem != 1) + return ENODEV; + + ret = DRM_COPY_FROM_USER(&args, + (struct drm_i915_gem_pwrite __user *) data, sizeof(args)); + if (ret) + DRM_ERROR("i915_gem_pwrite_ioctl failed to copy from user"); + obj = drm_gem_object_lookup(fpriv, args.handle); + if (obj == NULL) + return EBADF; + obj_priv = obj->driver_private; + DRM_DEBUG("i915_gem_pwrite_ioctl, obj->name %d",obj->name); + + /* 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) { + drm_gem_object_unreference(obj); + DRM_ERROR("i915_gem_pwrite_ioctl invalid arg"); + return EINVAL; + } + + /* We can only do the GTT pwrite on untiled buffers, as otherwise + * it would end up going through the fenced access, and we'll get + * different detiling behavior between reading and writing. + * pread/pwrite currently are reading and writing from the CPU + * perspective, requiring manual detiling by the client. + */ + if (obj_priv->tiling_mode == I915_TILING_NONE && + dev->gtt_total != 0) + ret = i915_gem_gtt_pwrite(dev, obj, &args, fpriv); + else + ret = i915_gem_shmem_pwrite(dev, obj, &args, fpriv); + + if (ret) + DRM_ERROR("pwrite failed %d\n", ret); + + drm_gem_object_unreference(obj); + + return ret; +} + +/** + * Called when user space prepares to use an object with the CPU, either + * through the mmap ioctl's mapping or a GTT mapping. + */ +/*ARGSUSED*/ +int +i915_gem_set_domain_ioctl(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + struct drm_i915_gem_set_domain args; + struct drm_gem_object *obj; + int ret = 0; + + if (dev->driver->use_gem != 1) + return ENODEV; + + DRM_COPYFROM_WITH_RETURN(&args, + (struct drm_i915_gem_set_domain __user *) data, sizeof(args)); + + uint32_t read_domains = args.read_domains; + uint32_t write_domain = args.write_domain; + + /* Only handle setting domains to types used by the CPU. */ + if (write_domain & ~(I915_GEM_DOMAIN_CPU | I915_GEM_DOMAIN_GTT)) + ret = EINVAL; + + if (read_domains & ~(I915_GEM_DOMAIN_CPU | I915_GEM_DOMAIN_GTT)) + ret = EINVAL; + + /* Having something in the write domain implies it's in the read + * domain, and only that read domain. Enforce that in the request. + */ + if (write_domain != 0 && read_domains != write_domain) + ret = EINVAL; + if (ret) { + DRM_ERROR("set_domain invalid read or write"); + return EINVAL; + } + + obj = drm_gem_object_lookup(fpriv, args.handle); + if (obj == NULL) + return EBADF; + + spin_lock(&dev->struct_mutex); + DRM_DEBUG("set_domain_ioctl %p(name %d size 0x%x), %08x %08x\n", + obj, obj->name, obj->size, args.read_domains, args.write_domain); + + if (read_domains & I915_GEM_DOMAIN_GTT) { + ret = i915_gem_object_set_to_gtt_domain(obj, write_domain != 0); + + /* Silently promote "you're not bound, there was nothing to do" + * to success, since the client was just asking us to + * make sure everything was done. + */ + if (ret == EINVAL) + ret = 0; + } else { + ret = i915_gem_object_set_to_cpu_domain(obj, write_domain != 0); + } + + drm_gem_object_unreference(obj); + spin_unlock(&dev->struct_mutex); + if (ret) + DRM_ERROR("i915_set_domain_ioctl ret %d", ret); + return ret; +} + +/** + * Called when user space has done writes to this buffer + */ +/*ARGSUSED*/ +int +i915_gem_sw_finish_ioctl(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + struct drm_i915_gem_sw_finish args; + struct drm_gem_object *obj; + struct drm_i915_gem_object *obj_priv; + int ret = 0; + + if (dev->driver->use_gem != 1) + return ENODEV; + + DRM_COPYFROM_WITH_RETURN(&args, + (struct drm_i915_gem_sw_finish __user *) data, sizeof(args)); + + spin_lock(&dev->struct_mutex); + obj = drm_gem_object_lookup(fpriv, args.handle); + if (obj == NULL) { + spin_unlock(&dev->struct_mutex); + return EBADF; + } + + DRM_DEBUG("%s: sw_finish %d (%p name %d size 0x%x)\n", + __func__, args.handle, obj, obj->name, obj->size); + + obj_priv = obj->driver_private; + /* Pinned buffers may be scanout, so flush the cache */ + if (obj_priv->pin_count) + { + i915_gem_object_flush_cpu_write_domain(obj); + } + + drm_gem_object_unreference(obj); + spin_unlock(&dev->struct_mutex); + return ret; +} + +/** + * Maps the contents of an object, returning the address it is mapped + * into. + * + * While the mapping holds a reference on the contents of the object, it doesn't + * imply a ref on the object itself. + */ +/*ARGSUSED*/ +int +i915_gem_mmap_ioctl(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + struct drm_i915_gem_mmap args; + struct drm_gem_object *obj; + caddr_t vvaddr = NULL; + int ret; + + if (dev->driver->use_gem != 1) + return ENODEV; + + DRM_COPYFROM_WITH_RETURN( + &args, (struct drm_i915_gem_mmap __user *)data, + sizeof (struct drm_i915_gem_mmap)); + + obj = drm_gem_object_lookup(fpriv, args.handle); + if (obj == NULL) + return EBADF; + + ret = ddi_devmap_segmap(fpriv->dev, (off_t)obj->map->handle, + ttoproc(curthread)->p_as, &vvaddr, obj->map->size, + PROT_ALL, PROT_ALL, MAP_SHARED, fpriv->credp); + if (ret) + return ret; + + spin_lock(&dev->struct_mutex); + drm_gem_object_unreference(obj); + spin_unlock(&dev->struct_mutex); + + args.addr_ptr = (uint64_t)(uintptr_t)vvaddr; + + DRM_COPYTO_WITH_RETURN( + (struct drm_i915_gem_mmap __user *)data, + &args, sizeof (struct drm_i915_gem_mmap)); + + return 0; +} + +static void +i915_gem_object_free_page_list(struct drm_gem_object *obj) +{ + struct drm_i915_gem_object *obj_priv = obj->driver_private; + if (obj_priv->page_list == NULL) + return; + + kmem_free(obj_priv->page_list, + btop(obj->size) * sizeof(caddr_t)); + + obj_priv->page_list = NULL; +} + +static void +i915_gem_object_move_to_active(struct drm_gem_object *obj, uint32_t seqno) +{ + struct drm_device *dev = obj->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_gem_object *obj_priv = obj->driver_private; + + /* Add a reference if we're newly entering the active list. */ + if (!obj_priv->active) { + drm_gem_object_reference(obj); + obj_priv->active = 1; + } + /* Move from whatever list we were on to the tail of execution. */ + list_move_tail(&obj_priv->list, + &dev_priv->mm.active_list, (caddr_t)obj_priv); + obj_priv->last_rendering_seqno = seqno; +} + +static void +i915_gem_object_move_to_flushing(struct drm_gem_object *obj) +{ + struct drm_device *dev = obj->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_gem_object *obj_priv = obj->driver_private; + + list_move_tail(&obj_priv->list, &dev_priv->mm.flushing_list, (caddr_t)obj_priv); + obj_priv->last_rendering_seqno = 0; +} + +static void +i915_gem_object_move_to_inactive(struct drm_gem_object *obj) +{ + struct drm_device *dev = obj->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_gem_object *obj_priv = obj->driver_private; + + if (obj_priv->pin_count != 0) + { + list_del_init(&obj_priv->list); + } else { + list_move_tail(&obj_priv->list, &dev_priv->mm.inactive_list, (caddr_t)obj_priv); + } + obj_priv->last_rendering_seqno = 0; + if (obj_priv->active) { + obj_priv->active = 0; + drm_gem_object_unreference(obj); + } +} + +/** + * Creates a new sequence number, emitting a write of it to the status page + * plus an interrupt, which will trigger i915_user_interrupt_handler. + * + * Must be called with struct_lock held. + * + * Returned sequence numbers are nonzero on success. + */ +static uint32_t +i915_add_request(struct drm_device *dev, uint32_t flush_domains) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_gem_request *request; + uint32_t seqno; + int was_empty; + RING_LOCALS; + + request = drm_calloc(1, sizeof(*request), DRM_MEM_DRIVER); + if (request == NULL) { + DRM_ERROR("Failed to alloc request"); + return 0; + } + /* Grab the seqno we're going to make this request be, and bump the + * next (skipping 0 so it can be the reserved no-seqno value). + */ + seqno = dev_priv->mm.next_gem_seqno; + dev_priv->mm.next_gem_seqno++; + if (dev_priv->mm.next_gem_seqno == 0) + dev_priv->mm.next_gem_seqno++; + + DRM_DEBUG("add_request seqno = %d dev 0x%lx", seqno, dev); + + BEGIN_LP_RING(4); + OUT_RING(MI_STORE_DWORD_INDEX); + OUT_RING(I915_GEM_HWS_INDEX << MI_STORE_DWORD_INDEX_SHIFT); + OUT_RING(seqno); + OUT_RING(0); + ADVANCE_LP_RING(); + + BEGIN_LP_RING(2); + OUT_RING(0); + OUT_RING(MI_USER_INTERRUPT); + ADVANCE_LP_RING(); + + request->seqno = seqno; + request->emitted_jiffies = jiffies; + was_empty = list_empty(&dev_priv->mm.request_list); + list_add_tail(&request->list, &dev_priv->mm.request_list, (caddr_t)request); + + /* Associate any objects on the flushing list matching the write + * domain we're flushing with our flush. + */ + if (flush_domains != 0) { + struct drm_i915_gem_object *obj_priv, *next; + + obj_priv = list_entry(dev_priv->mm.flushing_list.next, struct drm_i915_gem_object, list), + next = list_entry(obj_priv->list.next, struct drm_i915_gem_object, list); + for(; &obj_priv->list != &dev_priv->mm.flushing_list; + obj_priv = next, + next = list_entry(next->list.next, struct drm_i915_gem_object, list)) { + struct drm_gem_object *obj = obj_priv->obj; + + if ((obj->write_domain & flush_domains) == + obj->write_domain) { + obj->write_domain = 0; + i915_gem_object_move_to_active(obj, seqno); + } + } + + } + + if (was_empty && !dev_priv->mm.suspended) + { + /* change to delay HZ and then run work (not insert to workqueue of Linux) */ + worktimer_id = timeout(i915_gem_retire_work_handler, (void *) dev, DRM_HZ); + DRM_DEBUG("i915_gem: schedule_delayed_work"); + } + return seqno; +} + +/** + * Command execution barrier + * + * Ensures that all commands in the ring are finished + * before signalling the CPU + */ +uint32_t +i915_retire_commands(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + uint32_t cmd = MI_FLUSH | MI_NO_WRITE_FLUSH; + uint32_t flush_domains = 0; + RING_LOCALS; + + /* The sampler always gets flushed on i965 (sigh) */ + if (IS_I965G(dev)) + flush_domains |= I915_GEM_DOMAIN_SAMPLER; + BEGIN_LP_RING(3); + OUT_RING(cmd); + OUT_RING(0); /* noop */ + ADVANCE_LP_RING(); + + return flush_domains; +} + +/** + * Moves buffers associated only with the given active seqno from the active + * to inactive list, potentially freeing them. + */ +static void +i915_gem_retire_request(struct drm_device *dev, + struct drm_i915_gem_request *request) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + /* Move any buffers on the active list that are no longer referenced + * by the ringbuffer to the flushing/inactive lists as appropriate. + */ + while (!list_empty(&dev_priv->mm.active_list)) { + struct drm_gem_object *obj; + struct drm_i915_gem_object *obj_priv; + + obj_priv = list_entry(dev_priv->mm.active_list.next, + struct drm_i915_gem_object, + list); + obj = obj_priv->obj; + + /* If the seqno being retired doesn't match the oldest in the + * list, then the oldest in the list must still be newer than + * this seqno. + */ + if (obj_priv->last_rendering_seqno != request->seqno) + return; + + DRM_DEBUG("%s: retire %d moves to inactive list %p\n", + __func__, request->seqno, obj); + + if (obj->write_domain != 0) { + i915_gem_object_move_to_flushing(obj); + } else { + i915_gem_object_move_to_inactive(obj); + } + } +} + +/** + * Returns true if seq1 is later than seq2. + */ +static int +i915_seqno_passed(uint32_t seq1, uint32_t seq2) +{ + return (int32_t)(seq1 - seq2) >= 0; +} + +uint32_t +i915_get_gem_seqno(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + + return READ_HWSP(dev_priv, I915_GEM_HWS_INDEX); +} + +/** + * This function clears the request list as sequence numbers are passed. + */ +void +i915_gem_retire_requests(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + uint32_t seqno; + + seqno = i915_get_gem_seqno(dev); + + while (!list_empty(&dev_priv->mm.request_list)) { + struct drm_i915_gem_request *request; + uint32_t retiring_seqno; + request = (struct drm_i915_gem_request *)(uintptr_t)(dev_priv->mm.request_list.next->contain_ptr); + retiring_seqno = request->seqno; + + if (i915_seqno_passed(seqno, retiring_seqno) || + dev_priv->mm.wedged) { + i915_gem_retire_request(dev, request); + + list_del(&request->list); + drm_free(request, sizeof(*request), DRM_MEM_DRIVER); + } else + break; + } +} + +void +i915_gem_retire_work_handler(void *device) +{ + struct drm_device *dev = (struct drm_device *)device; + drm_i915_private_t *dev_priv = dev->dev_private; + + spin_lock(&dev->struct_mutex); + + /* Return if gem idle */ + if (worktimer_id == NULL) { + spin_unlock(&dev->struct_mutex); + return; + } + + i915_gem_retire_requests(dev); + if (!dev_priv->mm.suspended && !list_empty(&dev_priv->mm.request_list)) + { + DRM_DEBUG("i915_gem: schedule_delayed_work"); + worktimer_id = timeout(i915_gem_retire_work_handler, (void *) dev, DRM_HZ); + } + spin_unlock(&dev->struct_mutex); +} + +/** + * i965_reset - reset chip after a hang + * @dev: drm device to reset + * @flags: reset domains + * + * Reset the chip. Useful if a hang is detected. + * + * 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 + */ +void i965_reset(struct drm_device *dev, u8 flags) +{ + ddi_acc_handle_t conf_hdl; + drm_i915_private_t *dev_priv = dev->dev_private; + int timeout = 0; + uint8_t gdrst; + + if (flags & GDRST_FULL) + i915_save_display(dev); + + if (pci_config_setup(dev->dip, &conf_hdl) != DDI_SUCCESS) { + DRM_ERROR(("i915_reset: pci_config_setup fail")); + return; + } + + /* + * Set the reset bit, wait for reset, then clear it. Hardware + * will clear the status bit (bit 1) when it's actually ready + * for action again. + */ + gdrst = pci_config_get8(conf_hdl, GDRST); + pci_config_put8(conf_hdl, GDRST, gdrst | flags); + drv_usecwait(50); + pci_config_put8(conf_hdl, GDRST, gdrst | 0xfe); + + /* ...we don't want to loop forever though, 500ms should be plenty */ + do { + drv_usecwait(100); + gdrst = pci_config_get8(conf_hdl, GDRST); + } while ((gdrst & 2) && (timeout++ < 5)); + + /* 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 (!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); + i915_kernel_lost_context(dev); + + drm_irq_install(dev); + } + + /* + * Display needs restore too... + */ + if (flags & GDRST_FULL) + i915_restore_display(dev); +} + +/** + * Waits for a sequence number to be signaled, and cleans up the + * request and object lists appropriately for that event. + */ +int +i915_wait_request(struct drm_device *dev, uint32_t seqno) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + int ret = 0; + + ASSERT(seqno != 0); + + if (!i915_seqno_passed(i915_get_gem_seqno(dev), seqno)) { + dev_priv->mm.waiting_gem_seqno = seqno; + i915_user_irq_on(dev); + DRM_WAIT(ret, &dev_priv->irq_queue, + (i915_seqno_passed(i915_get_gem_seqno(dev), seqno) || + dev_priv->mm.wedged)); + i915_user_irq_off(dev); + dev_priv->mm.waiting_gem_seqno = 0; + } + if (dev_priv->mm.wedged) { + ret = EIO; + } + + /* GPU maybe hang, reset needed*/ + if (ret == -2 && (seqno > i915_get_gem_seqno(dev))) { + if (IS_I965G(dev)) { + DRM_ERROR("GPU hang detected try to reset ... wait for irq_queue seqno %d, now seqno %d", seqno, i915_get_gem_seqno(dev)); + dev_priv->mm.wedged = 1; + i965_reset(dev, GDRST_RENDER); + i915_gem_retire_requests(dev); + dev_priv->mm.wedged = 0; + } + else + DRM_ERROR("GPU hang detected.... reboot required"); + return 0; + } + /* Directly dispatch request retiring. While we have the work queue + * to handle this, the waiter on a request often wants an associated + * buffer to have made it to the inactive list, and we would need + * a separate wait queue to handle that. + */ + if (ret == 0) + i915_gem_retire_requests(dev); + + return ret; +} + +static void +i915_gem_flush(struct drm_device *dev, + uint32_t invalidate_domains, + uint32_t flush_domains) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + uint32_t cmd; + RING_LOCALS; + + DRM_DEBUG("%s: invalidate %08x flush %08x\n", __func__, + invalidate_domains, flush_domains); + + if (flush_domains & I915_GEM_DOMAIN_CPU) + drm_agp_chipset_flush(dev); + + if ((invalidate_domains | flush_domains) & ~(I915_GEM_DOMAIN_CPU | + I915_GEM_DOMAIN_GTT)) { + /* + * read/write caches: + * + * I915_GEM_DOMAIN_RENDER is always invalidated, but is + * only flushed if MI_NO_WRITE_FLUSH is unset. On 965, it is + * also flushed at 2d versus 3d pipeline switches. + * + * read-only caches: + * + * I915_GEM_DOMAIN_SAMPLER is flushed on pre-965 if + * MI_READ_FLUSH is set, and is always flushed on 965. + * + * I915_GEM_DOMAIN_COMMAND may not exist? + * + * I915_GEM_DOMAIN_INSTRUCTION, which exists on 965, is + * invalidated when MI_EXE_FLUSH is set. + * + * I915_GEM_DOMAIN_VERTEX, which exists on 965, is + * invalidated with every MI_FLUSH. + * + * TLBs: + * + * On 965, TLBs associated with I915_GEM_DOMAIN_COMMAND + * and I915_GEM_DOMAIN_CPU in are invalidated at PTE write and + * I915_GEM_DOMAIN_RENDER and I915_GEM_DOMAIN_SAMPLER + * are flushed at any MI_FLUSH. + */ + + cmd = MI_FLUSH | MI_NO_WRITE_FLUSH; + if ((invalidate_domains|flush_domains) & + I915_GEM_DOMAIN_RENDER) + cmd &= ~MI_NO_WRITE_FLUSH; + if (!IS_I965G(dev)) { + /* + * On the 965, the sampler cache always gets flushed + * and this bit is reserved. + */ + if (invalidate_domains & I915_GEM_DOMAIN_SAMPLER) + cmd |= MI_READ_FLUSH; + } + if (invalidate_domains & I915_GEM_DOMAIN_INSTRUCTION) + cmd |= MI_EXE_FLUSH; + + DRM_DEBUG("%s: queue flush %08x to ring\n", __func__, cmd); + + BEGIN_LP_RING(2); + OUT_RING(cmd); + OUT_RING(0); /* noop */ + ADVANCE_LP_RING(); + } +} + +/** + * Ensures that all rendering to the object has completed and the object is + * safe to unbind from the GTT or access from the CPU. + */ +static int +i915_gem_object_wait_rendering(struct drm_gem_object *obj) +{ + struct drm_device *dev = obj->dev; + struct drm_i915_gem_object *obj_priv = obj->driver_private; + int ret, seqno; + + /* This function only exists to support waiting for existing rendering, + * not for emitting required flushes. + */ + + if((obj->write_domain & I915_GEM_GPU_DOMAINS) != 0) { + DRM_ERROR("write domain should not be GPU DOMAIN %d", obj_priv->active); + return 0; + } + + /* If there is rendering queued on the buffer being evicted, wait for + * it. + */ + if (obj_priv->active) { + DRM_DEBUG("%s: object %d %p wait for seqno %08x\n", + __func__, obj->name, obj, obj_priv->last_rendering_seqno); + + seqno = obj_priv->last_rendering_seqno; + if (seqno == 0) { + DRM_DEBUG("last rendering maybe finished"); + return 0; + } + ret = i915_wait_request(dev, seqno); + if (ret != 0) { + DRM_ERROR("%s: i915_wait_request request->seqno %d now %d\n", __func__, seqno, i915_get_gem_seqno(dev)); + return ret; + } + } + + return 0; +} + +/** + * Unbinds an object from the GTT aperture. + */ +int +i915_gem_object_unbind(struct drm_gem_object *obj, uint32_t type) +{ + struct drm_device *dev = obj->dev; + struct drm_i915_gem_object *obj_priv = obj->driver_private; + int ret = 0; + + if (obj_priv->gtt_space == NULL) + return 0; + + if (obj_priv->pin_count != 0) { + DRM_ERROR("Attempting to unbind pinned buffer\n"); + return EINVAL; + } + + /* Wait for any rendering to complete + */ + ret = i915_gem_object_wait_rendering(obj); + if (ret) { + DRM_ERROR("wait_rendering failed: %d\n", ret); + return ret; + } + + /* Move the object to the CPU domain to ensure that + * any possible CPU writes while it's not in the GTT + * are flushed when we go to remap it. This will + * also ensure that all pending GPU writes are finished + * before we unbind. + */ + ret = i915_gem_object_set_to_cpu_domain(obj, 1); + if (ret) { + DRM_ERROR("set_domain failed: %d\n", ret); + return ret; + } + + if (!obj_priv->agp_mem) { + drm_agp_unbind_pages(dev, obj->size / PAGE_SIZE, obj_priv->gtt_offset, type); + obj_priv->agp_mem = -1; + } + + ASSERT(!obj_priv->active); + + 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_mm_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)) + list_del_init(&obj_priv->list); + + return 0; +} + +static int +i915_gem_evict_something(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_gem_object *obj; + struct drm_i915_gem_object *obj_priv; + int ret = 0; + + for (;;) { + /* If there's an inactive buffer available now, grab it + * and be done. + */ + if (!list_empty(&dev_priv->mm.inactive_list)) { + obj_priv = list_entry(dev_priv->mm.inactive_list.next, + struct drm_i915_gem_object, + list); + obj = obj_priv->obj; + ASSERT(!(obj_priv->pin_count != 0)); + DRM_DEBUG("%s: evicting %d\n", __func__, obj->name); + ASSERT(!(obj_priv->active)); + /* Wait on the rendering and unbind the buffer. */ + ret = i915_gem_object_unbind(obj, 1); + break; + } + /* If we didn't get anything, but the ring is still processing + * things, wait for one of those things to finish and hopefully + * leave us a buffer to evict. + */ + if (!list_empty(&dev_priv->mm.request_list)) { + struct drm_i915_gem_request *request; + + request = list_entry(dev_priv->mm.request_list.next, + struct drm_i915_gem_request, + 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 + * assume that waiting freed and unbound something, + * so there should now be some space in the GTT + */ + if (!list_empty(&dev_priv->mm.inactive_list)) + continue; + break; + } + + /* If we didn't have anything on the request list but there + * are buffers awaiting a flush, emit one and try again. + * When we wait on it, those buffers waiting for that flush + * will get moved to inactive. + */ + if (!list_empty(&dev_priv->mm.flushing_list)) { + obj_priv = list_entry(dev_priv->mm.flushing_list.next, + struct drm_i915_gem_object, + list); + obj = obj_priv->obj; + + i915_gem_flush(dev, + obj->write_domain, + obj->write_domain); + (void) i915_add_request(dev, obj->write_domain); + + obj = NULL; + continue; + } + + DRM_ERROR("inactive empty %d request empty %d " + "flushing empty %d\n", + list_empty(&dev_priv->mm.inactive_list), + list_empty(&dev_priv->mm.request_list), + list_empty(&dev_priv->mm.flushing_list)); + /* If we didn't do any of the above, there's nothing to be done + * and we just can't fit it in. + */ + return ENOMEM; + } + return ret; +} + +static int +i915_gem_evict_everything(struct drm_device *dev) +{ + int ret; + + for (;;) { + ret = i915_gem_evict_something(dev); + if (ret != 0) + break; + } + if (ret == ENOMEM) + return 0; + else + DRM_ERROR("evict_everything ret %d", ret); + return ret; +} + +/** + * Finds free space in the GTT aperture and binds the object there. + */ +static int +i915_gem_object_bind_to_gtt(struct drm_gem_object *obj, uint32_t alignment) +{ + struct drm_device *dev = obj->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_gem_object *obj_priv = obj->driver_private; + struct drm_mm_node *free_space; + int page_count, ret; + + if (dev_priv->mm.suspended) + return EBUSY; + if (alignment == 0) + alignment = PAGE_SIZE; + if (alignment & (PAGE_SIZE - 1)) { + DRM_ERROR("Invalid object alignment requested %u\n", alignment); + return EINVAL; + } + + if (obj_priv->gtt_space) { + DRM_ERROR("Already bind!!"); + return 0; + } +search_free: + free_space = drm_mm_search_free(&dev_priv->mm.gtt_space, + (unsigned long) obj->size, alignment, 0); + if (free_space != NULL) { + obj_priv->gtt_space = drm_mm_get_block(free_space, (unsigned long) obj->size, + alignment); + if (obj_priv->gtt_space != NULL) { + obj_priv->gtt_space->private = obj; + obj_priv->gtt_offset = obj_priv->gtt_space->start; + } + } + if (obj_priv->gtt_space == NULL) { + /* If the gtt is empty and we're still having trouble + * fitting our object in, we're out of memory. + */ + if (list_empty(&dev_priv->mm.inactive_list) && + list_empty(&dev_priv->mm.flushing_list) && + list_empty(&dev_priv->mm.active_list)) { + DRM_ERROR("GTT full, but LRU list empty\n"); + return ENOMEM; + } + + ret = i915_gem_evict_something(dev); + if (ret != 0) { + DRM_ERROR("Failed to evict a buffer %d\n", ret); + return ret; + } + goto search_free; + } + + ret = i915_gem_object_get_page_list(obj); + if (ret) { + drm_mm_put_block(obj_priv->gtt_space); + obj_priv->gtt_space = NULL; + DRM_ERROR("bind to gtt failed to get page list"); + return ret; + } + + page_count = obj->size / PAGE_SIZE; + /* Create an AGP memory structure pointing at our pages, and bind it + * into the GTT. + */ + DRM_DEBUG("Binding object %d of page_count %d at gtt_offset 0x%x obj->pfnarray = 0x%lx", + obj->name, page_count, obj_priv->gtt_offset, obj->pfnarray); + + obj_priv->agp_mem = drm_agp_bind_pages(dev, + obj->pfnarray, + page_count, + obj_priv->gtt_offset); + if (obj_priv->agp_mem) { + i915_gem_object_free_page_list(obj); + drm_mm_put_block(obj_priv->gtt_space); + obj_priv->gtt_space = NULL; + DRM_ERROR("Failed to bind pages obj %d, obj 0x%lx", obj->name, obj); + return ENOMEM; + } + atomic_inc(&dev->gtt_count); + atomic_add(obj->size, &dev->gtt_memory); + + /* Assert that the object is not currently in any GPU domain. As it + * wasn't in the GTT, there shouldn't be any way it could have been in + * a GPU cache + */ + ASSERT(!(obj->read_domains & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT))); + ASSERT(!(obj->write_domain & ~(I915_GEM_DOMAIN_CPU|I915_GEM_DOMAIN_GTT))); + + return 0; +} + +void +i915_gem_clflush_object(struct drm_gem_object *obj) +{ + struct drm_i915_gem_object *obj_priv = obj->driver_private; + + /* If we don't have a page list set up, then we're not pinned + * to GPU, and we can ignore the cache flush because it'll happen + * again at bind time. + */ + + if (obj_priv->page_list == NULL) + return; + drm_clflush_pages(obj_priv->page_list, obj->size / PAGE_SIZE); +} + +/** Flushes any GPU write domain for the object if it's dirty. */ +static void +i915_gem_object_flush_gpu_write_domain(struct drm_gem_object *obj) +{ + struct drm_device *dev = obj->dev; + uint32_t seqno; + + if ((obj->write_domain & I915_GEM_GPU_DOMAINS) == 0) + return; + + /* Queue the GPU write cache flushing we need. */ + i915_gem_flush(dev, 0, obj->write_domain); + seqno = i915_add_request(dev, obj->write_domain); + DRM_DEBUG("flush_gpu_write_domain seqno = %d", seqno); + obj->write_domain = 0; + i915_gem_object_move_to_active(obj, seqno); +} + +/** Flushes the GTT write domain for the object if it's dirty. */ +static void +i915_gem_object_flush_gtt_write_domain(struct drm_gem_object *obj) +{ + if (obj->write_domain != I915_GEM_DOMAIN_GTT) + return; + + /* No actual flushing is required for the GTT write domain. Writes + * to it immediately go to main memory as far as we know, so there's + * no chipset flush. It also doesn't land in render cache. + */ + obj->write_domain = 0; +} + +/** Flushes the CPU write domain for the object if it's dirty. */ +static void +i915_gem_object_flush_cpu_write_domain(struct drm_gem_object *obj) +{ + struct drm_device *dev = obj->dev; + + if (obj->write_domain != I915_GEM_DOMAIN_CPU) + return; + + i915_gem_clflush_object(obj); + drm_agp_chipset_flush(dev); + obj->write_domain = 0; +} + +/** + * Moves a single object to the GTT read, and possibly write domain. + * + * This function returns when the move is complete, including waiting on + * flushes to occur. + */ +static int +i915_gem_object_set_to_gtt_domain(struct drm_gem_object *obj, int write) +{ + struct drm_i915_gem_object *obj_priv = obj->driver_private; + int ret; + + /* Not valid to be called on unbound objects. */ + if (obj_priv->gtt_space == NULL) + return EINVAL; + + i915_gem_object_flush_gpu_write_domain(obj); + /* Wait on any GPU rendering and flushing to occur. */ + ret = i915_gem_object_wait_rendering(obj); + if (ret != 0) { + DRM_ERROR("set_to_gtt_domain wait_rendering ret %d", ret); + return ret; + } + /* If we're writing through the GTT domain, then CPU and GPU caches + * will need to be invalidated at next use. + */ + if (write) + obj->read_domains &= I915_GEM_DOMAIN_GTT; + i915_gem_object_flush_cpu_write_domain(obj); + + DRM_DEBUG("i915_gem_object_set_to_gtt_domain obj->read_domains %x ", obj->read_domains); + /* It should now be out of any other write domains, and we can update + * the domain values for our changes. + */ + ASSERT(!((obj->write_domain & ~I915_GEM_DOMAIN_GTT) != 0)); + obj->read_domains |= I915_GEM_DOMAIN_GTT; + if (write) { + obj->write_domain = I915_GEM_DOMAIN_GTT; + obj_priv->dirty = 1; + } + + return 0; +} + +/** + * Moves a single object to the CPU read, and possibly write domain. + * + * This function returns when the move is complete, including waiting on + * flushes to occur. + */ +static int +i915_gem_object_set_to_cpu_domain(struct drm_gem_object *obj, int write) +{ + struct drm_device *dev = obj->dev; + int ret; + + + i915_gem_object_flush_gpu_write_domain(obj); + /* Wait on any GPU rendering and flushing to occur. */ + + ret = i915_gem_object_wait_rendering(obj); + if (ret != 0) + return ret; + + i915_gem_object_flush_gtt_write_domain(obj); + + /* If we have a partially-valid cache of the object in the CPU, + * finish invalidating it and free the per-page flags. + */ + i915_gem_object_set_to_full_cpu_read_domain(obj); + + /* Flush the CPU cache if it's still invalid. */ + if ((obj->read_domains & I915_GEM_DOMAIN_CPU) == 0) { + i915_gem_clflush_object(obj); + drm_agp_chipset_flush(dev); + obj->read_domains |= I915_GEM_DOMAIN_CPU; + } + + /* It should now be out of any other write domains, and we can update + * the domain values for our changes. + */ + ASSERT(!((obj->write_domain & ~I915_GEM_DOMAIN_CPU) != 0)); + + /* If we're writing through the CPU, then the GPU read domains will + * need to be invalidated at next use. + */ + if (write) { + obj->read_domains &= I915_GEM_DOMAIN_CPU; + obj->write_domain = I915_GEM_DOMAIN_CPU; + } + + return 0; +} + +/* + * Set the next domain for the specified object. This + * may not actually perform the necessary flushing/invaliding though, + * as that may want to be batched with other set_domain operations + * + * This is (we hope) the only really tricky part of gem. The goal + * is fairly simple -- track which caches hold bits of the object + * and make sure they remain coherent. A few concrete examples may + * help to explain how it works. For shorthand, we use the notation + * (read_domains, write_domain), e.g. (CPU, CPU) to indicate the + * a pair of read and write domain masks. + * + * Case 1: the batch buffer + * + * 1. Allocated + * 2. Written by CPU + * 3. Mapped to GTT + * 4. Read by GPU + * 5. Unmapped from GTT + * 6. Freed + * + * Let's take these a step at a time + * + * 1. Allocated + * Pages allocated from the kernel may still have + * cache contents, so we set them to (CPU, CPU) always. + * 2. Written by CPU (using pwrite) + * The pwrite function calls set_domain (CPU, CPU) and + * this function does nothing (as nothing changes) + * 3. Mapped by GTT + * This function asserts that the object is not + * currently in any GPU-based read or write domains + * 4. Read by GPU + * i915_gem_execbuffer calls set_domain (COMMAND, 0). + * As write_domain is zero, this function adds in the + * current read domains (CPU+COMMAND, 0). + * flush_domains is set to CPU. + * invalidate_domains is set to COMMAND + * clflush is run to get data out of the CPU caches + * then i915_dev_set_domain calls i915_gem_flush to + * emit an MI_FLUSH and drm_agp_chipset_flush + * 5. Unmapped from GTT + * i915_gem_object_unbind calls set_domain (CPU, CPU) + * flush_domains and invalidate_domains end up both zero + * so no flushing/invalidating happens + * 6. Freed + * yay, done + * + * Case 2: The shared render buffer + * + * 1. Allocated + * 2. Mapped to GTT + * 3. Read/written by GPU + * 4. set_domain to (CPU,CPU) + * 5. Read/written by CPU + * 6. Read/written by GPU + * + * 1. Allocated + * Same as last example, (CPU, CPU) + * 2. Mapped to GTT + * Nothing changes (assertions find that it is not in the GPU) + * 3. Read/written by GPU + * execbuffer calls set_domain (RENDER, RENDER) + * flush_domains gets CPU + * invalidate_domains gets GPU + * clflush (obj) + * MI_FLUSH and drm_agp_chipset_flush + * 4. set_domain (CPU, CPU) + * flush_domains gets GPU + * invalidate_domains gets CPU + * wait_rendering (obj) to make sure all drawing is complete. + * This will include an MI_FLUSH to get the data from GPU + * to memory + * clflush (obj) to invalidate the CPU cache + * Another MI_FLUSH in i915_gem_flush (eliminate this somehow?) + * 5. Read/written by CPU + * cache lines are loaded and dirtied + * 6. Read written by GPU + * Same as last GPU access + * + * Case 3: The constant buffer + * + * 1. Allocated + * 2. Written by CPU + * 3. Read by GPU + * 4. Updated (written) by CPU again + * 5. Read by GPU + * + * 1. Allocated + * (CPU, CPU) + * 2. Written by CPU + * (CPU, CPU) + * 3. Read by GPU + * (CPU+RENDER, 0) + * flush_domains = CPU + * invalidate_domains = RENDER + * clflush (obj) + * MI_FLUSH + * drm_agp_chipset_flush + * 4. Updated (written) by CPU again + * (CPU, CPU) + * flush_domains = 0 (no previous write domain) + * invalidate_domains = 0 (no new read domains) + * 5. Read by GPU + * (CPU+RENDER, 0) + * flush_domains = CPU + * invalidate_domains = RENDER + * clflush (obj) + * MI_FLUSH + * drm_agp_chipset_flush + */ +static void +i915_gem_object_set_to_gpu_domain(struct drm_gem_object *obj, + uint32_t read_domains, + 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; + + DRM_DEBUG("%s: object %p read %08x -> %08x write %08x -> %08x\n", + __func__, obj, + obj->read_domains, read_domains, + obj->write_domain, write_domain); + /* + * If the object isn't moving to a new write domain, + * let the object stay in multiple read domains + */ + if (write_domain == 0) + read_domains |= obj->read_domains; + else + obj_priv->dirty = 1; + + /* + * Flush the current write domain if + * the new read domains don't match. Invalidate + * any read domains which differ from the old + * write domain + */ + if (obj->write_domain && obj->write_domain != read_domains) { + flush_domains |= obj->write_domain; + invalidate_domains |= read_domains & ~obj->write_domain; + } + /* + * Invalidate any read caches which may have + * stale data. That is, any new read domains. + */ + invalidate_domains |= read_domains & ~obj->read_domains; + if ((flush_domains | invalidate_domains) & I915_GEM_DOMAIN_CPU) { + DRM_DEBUG("%s: CPU domain flush %08x invalidate %08x\n", + __func__, flush_domains, invalidate_domains); + i915_gem_clflush_object(obj); + } + + if ((write_domain | flush_domains) != 0) + obj->write_domain = write_domain; + obj->read_domains = read_domains; + + dev->invalidate_domains |= invalidate_domains; + dev->flush_domains |= flush_domains; + + DRM_DEBUG("%s: read %08x write %08x invalidate %08x flush %08x\n", + __func__, + obj->read_domains, obj->write_domain, + dev->invalidate_domains, dev->flush_domains); + +} + +/** + * Moves the object from a partially CPU read to a full one. + * + * Note that this only resolves i915_gem_object_set_cpu_read_domain_range(), + * and doesn't handle transitioning from !(read_domains & I915_GEM_DOMAIN_CPU). + */ +static void +i915_gem_object_set_to_full_cpu_read_domain(struct drm_gem_object *obj) +{ + struct drm_device *dev = obj->dev; + struct drm_i915_gem_object *obj_priv = obj->driver_private; + + if (!obj_priv->page_cpu_valid) + return; + + /* If we're partially in the CPU read domain, finish moving it in. + */ + if (obj->read_domains & I915_GEM_DOMAIN_CPU) { + int i; + + for (i = 0; i <= (obj->size - 1) / PAGE_SIZE; i++) { + if (obj_priv->page_cpu_valid[i]) + continue; + drm_clflush_pages(obj_priv->page_list + i, 1); + } + drm_agp_chipset_flush(dev); + } + + /* Free the page_cpu_valid mappings which are now stale, whether + * or not we've got I915_GEM_DOMAIN_CPU. + */ + drm_free(obj_priv->page_cpu_valid, obj->size / PAGE_SIZE, + DRM_MEM_DRIVER); + obj_priv->page_cpu_valid = NULL; +} + +/** + * Set the CPU read domain on a range of the object. + * + * The object ends up with I915_GEM_DOMAIN_CPU in its read flags although it's + * not entirely valid. The page_cpu_valid member of the object flags which + * pages have been flushed, and will be respected by + * i915_gem_object_set_to_cpu_domain() if it's called on to get a valid mapping + * of the whole object. + * + * This function returns when the move is complete, including waiting on + * flushes to occur. + */ +static int +i915_gem_object_set_cpu_read_domain_range(struct drm_gem_object *obj, + uint64_t offset, uint64_t size) +{ + struct drm_i915_gem_object *obj_priv = obj->driver_private; + int i, ret; + + if (offset == 0 && size == obj->size) + return i915_gem_object_set_to_cpu_domain(obj, 0); + + i915_gem_object_flush_gpu_write_domain(obj); + /* Wait on any GPU rendering and flushing to occur. */ + ret = i915_gem_object_wait_rendering(obj); + if (ret != 0) + return ret; + i915_gem_object_flush_gtt_write_domain(obj); + + /* If we're already fully in the CPU read domain, we're done. */ + if (obj_priv->page_cpu_valid == NULL && + (obj->read_domains & I915_GEM_DOMAIN_CPU) != 0) + return 0; + + /* Otherwise, create/clear the per-page CPU read domain flag if we're + * newly adding I915_GEM_DOMAIN_CPU + */ + if (obj_priv->page_cpu_valid == NULL) { + obj_priv->page_cpu_valid = drm_calloc(1, obj->size / PAGE_SIZE, + DRM_MEM_DRIVER); + if (obj_priv->page_cpu_valid == NULL) + return ENOMEM; + } else if ((obj->read_domains & I915_GEM_DOMAIN_CPU) == 0) + (void) memset(obj_priv->page_cpu_valid, 0, obj->size / PAGE_SIZE); + + /* Flush the cache on any pages that are still invalid from the CPU's + * perspective. + */ + for (i = offset / PAGE_SIZE; i <= (offset + size - 1) / PAGE_SIZE; + i++) { + if (obj_priv->page_cpu_valid[i]) + continue; + + drm_clflush_pages(obj_priv->page_list + i, 1); + obj_priv->page_cpu_valid[i] = 1; + } + + /* It should now be out of any other write domains, and we can update + * the domain values for our changes. + */ + ASSERT(!((obj->write_domain & ~I915_GEM_DOMAIN_CPU) != 0)); + + obj->read_domains |= I915_GEM_DOMAIN_CPU; + + return 0; +} + +/** + * Pin an object to the GTT and evaluate the relocations landing in it. + */ +static int +i915_gem_object_pin_and_relocate(struct drm_gem_object *obj, + struct drm_file *file_priv, + struct drm_i915_gem_exec_object *entry) +{ + struct drm_i915_gem_relocation_entry reloc; + struct drm_i915_gem_relocation_entry __user *relocs; + struct drm_i915_gem_object *obj_priv = obj->driver_private; + int i, ret; + + /* Choose the GTT offset for our buffer and put it there. */ + ret = i915_gem_object_pin(obj, (uint32_t) entry->alignment); + if (ret) { + DRM_ERROR("failed to pin"); + return ret; + } + entry->offset = obj_priv->gtt_offset; + + relocs = (struct drm_i915_gem_relocation_entry __user *) + (uintptr_t) entry->relocs_ptr; + /* Apply the relocations, using the GTT aperture to avoid cache + * flushing requirements. + */ + for (i = 0; i < entry->relocation_count; i++) { + struct drm_gem_object *target_obj; + struct drm_i915_gem_object *target_obj_priv; + uint32_t reloc_val, reloc_offset, *reloc_entry; + + ret = DRM_COPY_FROM_USER(&reloc, relocs + i, sizeof(reloc)); + if (ret != 0) { + i915_gem_object_unpin(obj); + DRM_ERROR("failed to copy from user"); + return ret; + } + + target_obj = drm_gem_object_lookup(file_priv, + reloc.target_handle); + if (target_obj == NULL) { + i915_gem_object_unpin(obj); + return EBADF; + } + target_obj_priv = target_obj->driver_private; + + /* The target buffer should have appeared before us in the + * exec_object list, so it should have a GTT space bound by now. + */ + if (target_obj_priv->gtt_space == NULL) { + DRM_ERROR("No GTT space found for object %d\n", + reloc.target_handle); + drm_gem_object_unreference(target_obj); + i915_gem_object_unpin(obj); + return EINVAL; + } + + if (reloc.offset > obj->size - 4) { + DRM_ERROR("Relocation beyond object bounds: " + "obj %p target %d offset %d size %d.\n", + obj, reloc.target_handle, + (int) reloc.offset, (int) obj->size); + drm_gem_object_unreference(target_obj); + i915_gem_object_unpin(obj); + return EINVAL; + } + if (reloc.offset & 3) { + DRM_ERROR("Relocation not 4-byte aligned: " + "obj %p target %d offset %d.\n", + obj, reloc.target_handle, + (int) reloc.offset); + drm_gem_object_unreference(target_obj); + i915_gem_object_unpin(obj); + return EINVAL; + } + + if (reloc.write_domain & I915_GEM_DOMAIN_CPU || + reloc.read_domains & I915_GEM_DOMAIN_CPU) { + DRM_ERROR("reloc with read/write CPU domains: " + "obj %p target %d offset %d " + "read %08x write %08x", + obj, reloc.target_handle, + (int) reloc.offset, + reloc.read_domains, + reloc.write_domain); + drm_gem_object_unreference(target_obj); + i915_gem_object_unpin(obj); + return EINVAL; + } + + if (reloc.write_domain && target_obj->pending_write_domain && + reloc.write_domain != target_obj->pending_write_domain) { + DRM_ERROR("Write domain conflict: " + "obj %p target %d offset %d " + "new %08x old %08x\n", + obj, reloc.target_handle, + (int) reloc.offset, + reloc.write_domain, + target_obj->pending_write_domain); + drm_gem_object_unreference(target_obj); + i915_gem_object_unpin(obj); + return EINVAL; + } + DRM_DEBUG("%s: obj %p offset %08x target %d " + "read %08x write %08x gtt %08x " + "presumed %08x delta %08x\n", + __func__, + obj, + (int) reloc.offset, + (int) reloc.target_handle, + (int) reloc.read_domains, + (int) reloc.write_domain, + (int) target_obj_priv->gtt_offset, + (int) reloc.presumed_offset, + reloc.delta); + + target_obj->pending_read_domains |= reloc.read_domains; + target_obj->pending_write_domain |= reloc.write_domain; + + /* If the relocation already has the right value in it, no + * more work needs to be done. + */ + if (target_obj_priv->gtt_offset == reloc.presumed_offset) { + drm_gem_object_unreference(target_obj); + continue; + } + + ret = i915_gem_object_set_to_gtt_domain(obj, 1); + if (ret != 0) { + drm_gem_object_unreference(target_obj); + i915_gem_object_unpin(obj); + return EINVAL; + } + + /* Map the page containing the relocation we're going to + * perform. + */ + + int reloc_base = (reloc.offset & ~(PAGE_SIZE-1)); + reloc_offset = reloc.offset & (PAGE_SIZE-1); + reloc_entry = (uint32_t *)(uintptr_t)(obj_priv->page_list[reloc_base/PAGE_SIZE] + reloc_offset); + reloc_val = target_obj_priv->gtt_offset + reloc.delta; + *reloc_entry = reloc_val; + + /* Write the updated presumed offset for this entry back out + * to the user. + */ + reloc.presumed_offset = target_obj_priv->gtt_offset; + ret = DRM_COPY_TO_USER(relocs + i, &reloc, sizeof(reloc)); + if (ret != 0) { + drm_gem_object_unreference(target_obj); + i915_gem_object_unpin(obj); + DRM_ERROR("%s: Failed to copy to user ret %d", __func__, ret); + return ret; + } + + drm_gem_object_unreference(target_obj); + } + + return 0; +} + +/** Dispatch a batchbuffer to the ring + */ +static int +i915_dispatch_gem_execbuffer(struct drm_device *dev, + struct drm_i915_gem_execbuffer *exec, + uint64_t exec_offset) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_clip_rect __user *boxes = (struct drm_clip_rect __user *) + (uintptr_t) exec->cliprects_ptr; + int nbox = exec->num_cliprects; + int i = 0, count; + uint64_t exec_start, exec_len; + RING_LOCALS; + + exec_start = exec_offset + exec->batch_start_offset; + exec_len = exec->batch_len; + + if ((exec_start | exec_len) & 0x7) { + DRM_ERROR("alignment\n"); + return EINVAL; + } + + if (!exec_start) { + DRM_ERROR("wrong arg"); + return EINVAL; + } + + count = nbox ? nbox : 1; + + for (i = 0; i < count; i++) { + if (i < nbox) { + int ret = i915_emit_box(dev, boxes, i, + exec->DR1, exec->DR4); + if (ret) { + DRM_ERROR("i915_emit_box %d DR1 0x%lx DRI2 0x%lx", ret, exec->DR1, exec->DR4); + return ret; + } + } + if (IS_I830(dev) || IS_845G(dev)) { + BEGIN_LP_RING(4); + OUT_RING(MI_BATCH_BUFFER); + OUT_RING(exec_start | MI_BATCH_NON_SECURE); + OUT_RING(exec_start + exec_len - 4); + OUT_RING(0); + ADVANCE_LP_RING(); + } else { + BEGIN_LP_RING(2); + if (IS_I965G(dev)) { + OUT_RING(MI_BATCH_BUFFER_START | + (2 << 6) | + (3 << 9) | + MI_BATCH_NON_SECURE_I965); + OUT_RING(exec_start); + + } else { + OUT_RING(MI_BATCH_BUFFER_START | + (2 << 6)); + OUT_RING(exec_start | MI_BATCH_NON_SECURE); + } + ADVANCE_LP_RING(); + } + } + /* XXX breadcrumb */ + return 0; +} + +/* Throttle our rendering by waiting until the ring has completed our requests + * emitted over 20 msec ago. + * + * This should get us reasonable parallelism between CPU and GPU but also + * relatively low latency when blocking on a particular request to finish. + */ +static int +i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file_priv) +{ + struct drm_i915_file_private *i915_file_priv = file_priv->driver_priv; + int ret = 0; + uint32_t seqno; + + spin_lock(&dev->struct_mutex); + seqno = i915_file_priv->mm.last_gem_throttle_seqno; + i915_file_priv->mm.last_gem_throttle_seqno = + i915_file_priv->mm.last_gem_seqno; + if (seqno) { + ret = i915_wait_request(dev, seqno); + if (ret != 0) + DRM_ERROR("%s: i915_wait_request request->seqno %d now %d\n", __func__, seqno, i915_get_gem_seqno(dev)); + } + spin_unlock(&dev->struct_mutex); + return ret; +} + +/*ARGSUSED*/ +int +i915_gem_execbuffer(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_file_private *i915_file_priv = fpriv->driver_priv; + struct drm_i915_gem_execbuffer args; + struct drm_i915_gem_exec_object *exec_list = NULL; + struct drm_gem_object **object_list = NULL; + struct drm_gem_object *batch_obj; + struct drm_i915_gem_object *obj_priv; + int ret = 0, i, pinned = 0; + uint64_t exec_offset; + uint32_t seqno, flush_domains; + int pin_tries; + + if (dev->driver->use_gem != 1) + return ENODEV; + + DRM_COPYFROM_WITH_RETURN(&args, + (struct drm_i915_gem_execbuffer __user *) data, sizeof(args)); + + DRM_DEBUG("buffer_count %d len %x\n", args.buffer_count, args.batch_len); + + if (args.buffer_count < 1) { + DRM_ERROR("execbuf with %d buffers\n", args.buffer_count); + return EINVAL; + } + /* Copy in the exec list from userland */ + exec_list = drm_calloc(sizeof(*exec_list), args.buffer_count, + DRM_MEM_DRIVER); + object_list = drm_calloc(sizeof(*object_list), args.buffer_count, + DRM_MEM_DRIVER); + if (exec_list == NULL || object_list == NULL) { + DRM_ERROR("Failed to allocate exec or object list " + "for %d buffers\n", + args.buffer_count); + ret = ENOMEM; + goto pre_mutex_err; + } + + ret = DRM_COPY_FROM_USER(exec_list, + (struct drm_i915_gem_exec_object __user *) + (uintptr_t) args.buffers_ptr, + sizeof(*exec_list) * args.buffer_count); + if (ret != 0) { + DRM_ERROR("copy %d exec entries failed %d\n", + args.buffer_count, ret); + goto pre_mutex_err; + } + spin_lock(&dev->struct_mutex); + + if (dev_priv->mm.wedged) { + DRM_ERROR("Execbuf while wedged\n"); + spin_unlock(&dev->struct_mutex); + return EIO; + } + + if (dev_priv->mm.suspended) { + DRM_ERROR("Execbuf while VT-switched.\n"); + spin_unlock(&dev->struct_mutex); + return EBUSY; + } + + /* Look up object handles */ + for (i = 0; i < args.buffer_count; i++) { + object_list[i] = drm_gem_object_lookup(fpriv, + exec_list[i].handle); + if (object_list[i] == NULL) { + DRM_ERROR("Invalid object handle %d at index %d\n", + exec_list[i].handle, i); + ret = EBADF; + goto err; + } + obj_priv = object_list[i]->driver_private; + if (obj_priv->in_execbuffer) { + DRM_ERROR("Object[%d] (%d) %p appears more than once in object list in args.buffer_count %d \n", + i, object_list[i]->name, object_list[i], args.buffer_count); + + ret = EBADF; + goto err; + } + + obj_priv->in_execbuffer = 1; + } + + /* Pin and relocate */ + for (pin_tries = 0; ; pin_tries++) { + ret = 0; + for (i = 0; i < args.buffer_count; i++) { + object_list[i]->pending_read_domains = 0; + object_list[i]->pending_write_domain = 0; + ret = i915_gem_object_pin_and_relocate(object_list[i], + fpriv, + &exec_list[i]); + if (ret) { + DRM_ERROR("Not all object pinned"); + break; + } + pinned = i + 1; + } + /* success */ + if (ret == 0) + { + DRM_DEBUG("gem_execbuffer pin_relocate success"); + break; + } + /* error other than GTT full, or we've already tried again */ + if (ret != ENOMEM || pin_tries >= 1) { + if (ret != ERESTART) + DRM_ERROR("Failed to pin buffers %d\n", ret); + goto err; + } + + /* unpin all of our buffers */ + for (i = 0; i < pinned; i++) + i915_gem_object_unpin(object_list[i]); + pinned = 0; + + /* evict everyone we can from the aperture */ + ret = i915_gem_evict_everything(dev); + if (ret) + goto err; + } + + /* Set the pending read domains for the batch buffer to COMMAND */ + batch_obj = object_list[args.buffer_count-1]; + batch_obj->pending_read_domains = I915_GEM_DOMAIN_COMMAND; + batch_obj->pending_write_domain = 0; + + /* Zero the gloabl flush/invalidate flags. These + * will be modified as each object is bound to the + * gtt + */ + dev->invalidate_domains = 0; + dev->flush_domains = 0; + + for (i = 0; i < args.buffer_count; i++) { + struct drm_gem_object *obj = object_list[i]; + + /* Compute new gpu domains and update invalidate/flush */ + i915_gem_object_set_to_gpu_domain(obj, + obj->pending_read_domains, + obj->pending_write_domain); + } + + if (dev->invalidate_domains | dev->flush_domains) { + + DRM_DEBUG("%s: invalidate_domains %08x flush_domains %08x Then flush\n", + __func__, + dev->invalidate_domains, + dev->flush_domains); + i915_gem_flush(dev, + dev->invalidate_domains, + dev->flush_domains); + if (dev->flush_domains) { + (void) i915_add_request(dev, dev->flush_domains); + + } + } + + for (i = 0; i < args.buffer_count; i++) { + struct drm_gem_object *obj = object_list[i]; + + obj->write_domain = obj->pending_write_domain; + } + + exec_offset = exec_list[args.buffer_count - 1].offset; + + /* Exec the batchbuffer */ + ret = i915_dispatch_gem_execbuffer(dev, &args, exec_offset); + if (ret) { + DRM_ERROR("dispatch failed %d\n", ret); + goto err; + } + + /* + * Ensure that the commands in the batch buffer are + * finished before the interrupt fires + */ + flush_domains = i915_retire_commands(dev); + + /* + * Get a seqno representing the execution of the current buffer, + * which we can wait on. We would like to mitigate these interrupts, + * likely by only creating seqnos occasionally (so that we have + * *some* interrupts representing completion of buffers that we can + * wait on when trying to clear up gtt space). + */ + seqno = i915_add_request(dev, flush_domains); + ASSERT(!(seqno == 0)); + i915_file_priv->mm.last_gem_seqno = seqno; + for (i = 0; i < args.buffer_count; i++) { + struct drm_gem_object *obj = object_list[i]; + i915_gem_object_move_to_active(obj, seqno); + DRM_DEBUG("%s: move to exec list %p\n", __func__, obj); + } + +err: + if (object_list != NULL) { + for (i = 0; i < pinned; i++) + i915_gem_object_unpin(object_list[i]); + + for (i = 0; i < args.buffer_count; i++) { + if (object_list[i]) { + obj_priv = object_list[i]->driver_private; + obj_priv->in_execbuffer = 0; + } + drm_gem_object_unreference(object_list[i]); + } + } + spin_unlock(&dev->struct_mutex); + + if (!ret) { + /* Copy the new buffer offsets back to the user's exec list. */ + ret = DRM_COPY_TO_USER((struct drm_i915_relocation_entry __user *) + (uintptr_t) args.buffers_ptr, + exec_list, + sizeof(*exec_list) * args.buffer_count); + if (ret) + DRM_ERROR("failed to copy %d exec entries " + "back to user (%d)\n", + args.buffer_count, ret); + } + +pre_mutex_err: + drm_free(object_list, sizeof(*object_list) * args.buffer_count, + DRM_MEM_DRIVER); + drm_free(exec_list, sizeof(*exec_list) * args.buffer_count, + DRM_MEM_DRIVER); + + return ret; +} + +int +i915_gem_object_pin(struct drm_gem_object *obj, uint32_t alignment) +{ + struct drm_device *dev = obj->dev; + struct drm_i915_gem_object *obj_priv = obj->driver_private; + int ret; + + if (obj_priv->gtt_space == NULL) { + ret = i915_gem_object_bind_to_gtt(obj, alignment); + if (ret != 0) { + DRM_ERROR("Failure to bind: %d", ret); + return ret; + } + } + obj_priv->pin_count++; + + /* If the object is not active and not pending a flush, + * remove it from the inactive list + */ + 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 | + I915_GEM_DOMAIN_GTT)) == 0 && + !list_empty(&obj_priv->list)) + list_del_init(&obj_priv->list); + } + return 0; +} + +void +i915_gem_object_unpin(struct drm_gem_object *obj) +{ + struct drm_device *dev = obj->dev; + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_i915_gem_object *obj_priv = obj->driver_private; + obj_priv->pin_count--; + ASSERT(!(obj_priv->pin_count < 0)); + ASSERT(!(obj_priv->gtt_space == NULL)); + + /* If the object is no longer pinned, and is + * neither active nor being flushed, then stick it on + * the inactive list + */ + if (obj_priv->pin_count == 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, (caddr_t)obj_priv); + atomic_dec(&dev->pin_count); + atomic_sub(obj->size, &dev->pin_memory); + } +} + +/*ARGSUSED*/ +int +i915_gem_pin_ioctl(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + struct drm_i915_gem_pin args; + struct drm_gem_object *obj; + struct drm_i915_gem_object *obj_priv; + int ret; + + if (dev->driver->use_gem != 1) + return ENODEV; + + DRM_COPYFROM_WITH_RETURN(&args, + (struct drm_i915_gem_pin __user *) data, sizeof(args)); + + spin_lock(&dev->struct_mutex); + + obj = drm_gem_object_lookup(fpriv, args.handle); + if (obj == NULL) { + DRM_ERROR("Bad handle in i915_gem_pin_ioctl(): %d\n", + args.handle); + spin_unlock(&dev->struct_mutex); + return EBADF; + } + DRM_DEBUG("i915_gem_pin_ioctl obj->name %d", obj->name); + obj_priv = obj->driver_private; + + if (obj_priv->pin_filp != NULL && obj_priv->pin_filp != fpriv) { + DRM_ERROR("Already pinned in i915_gem_pin_ioctl(): %d\n", + args.handle); + drm_gem_object_unreference(obj); + spin_unlock(&dev->struct_mutex); + return EINVAL; + } + + obj_priv->user_pin_count++; + obj_priv->pin_filp = fpriv; + if (obj_priv->user_pin_count == 1) { + ret = i915_gem_object_pin(obj, args.alignment); + if (ret != 0) { + drm_gem_object_unreference(obj); + spin_unlock(&dev->struct_mutex); + return ret; + } + } + + /* XXX - flush the CPU caches for pinned objects + * as the X server doesn't manage domains yet + */ + i915_gem_object_flush_cpu_write_domain(obj); + args.offset = obj_priv->gtt_offset; + + ret = DRM_COPY_TO_USER((struct drm_i915_gem_pin __user *) data, &args, sizeof(args)); + if ( ret != 0) + DRM_ERROR(" gem pin ioctl error! %d", ret); + + drm_gem_object_unreference(obj); + spin_unlock(&dev->struct_mutex); + + return 0; +} + +/*ARGSUSED*/ +int +i915_gem_unpin_ioctl(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + struct drm_i915_gem_pin args; + struct drm_gem_object *obj; + struct drm_i915_gem_object *obj_priv; + + if (dev->driver->use_gem != 1) + return ENODEV; + + DRM_COPYFROM_WITH_RETURN(&args, + (struct drm_i915_gem_pin __user *) data, sizeof(args)); + + spin_lock(&dev->struct_mutex); + + obj = drm_gem_object_lookup(fpriv, args.handle); + if (obj == NULL) { + DRM_ERROR("Bad handle in i915_gem_unpin_ioctl(): %d\n", + args.handle); + spin_unlock(&dev->struct_mutex); + return EBADF; + } + obj_priv = obj->driver_private; + DRM_DEBUG("i915_gem_unpin_ioctl, obj->name %d", obj->name); + if (obj_priv->pin_filp != fpriv) { + DRM_ERROR("Not pinned by caller in i915_gem_pin_ioctl(): %d\n", + args.handle); + drm_gem_object_unreference(obj); + spin_unlock(&dev->struct_mutex); + return EINVAL; + } + obj_priv->user_pin_count--; + if (obj_priv->user_pin_count == 0) { + obj_priv->pin_filp = NULL; + i915_gem_object_unpin(obj); + } + drm_gem_object_unreference(obj); + spin_unlock(&dev->struct_mutex); + return 0; +} + +/*ARGSUSED*/ +int +i915_gem_busy_ioctl(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + struct drm_i915_gem_busy args; + struct drm_gem_object *obj; + struct drm_i915_gem_object *obj_priv; + int ret; + + if (dev->driver->use_gem != 1) + return ENODEV; + + DRM_COPYFROM_WITH_RETURN(&args, + (struct drm_i915_gem_busy __user *) data, sizeof(args)); + + spin_lock(&dev->struct_mutex); + obj = drm_gem_object_lookup(fpriv, args.handle); + if (obj == NULL) { + DRM_ERROR("Bad handle in i915_gem_busy_ioctl(): %d\n", + args.handle); + spin_unlock(&dev->struct_mutex); + return EBADF; + } + + obj_priv = obj->driver_private; + /* Don't count being on the flushing list against the object being + * done. Otherwise, a buffer left on the flushing list but not getting + * flushed (because nobody's flushing that domain) won't ever return + * unbusy and get reused by libdrm's bo cache. The other expected + * consumer of this interface, OpenGL's occlusion queries, also specs + * that the objects get unbusy "eventually" without any interference. + */ + args.busy = obj_priv->active && obj_priv->last_rendering_seqno != 0; + DRM_DEBUG("i915_gem_busy_ioctl call obj->name %d busy %d", obj->name, args.busy); + + ret = DRM_COPY_TO_USER((struct drm_i915_gem_busy __user *) data, &args, sizeof(args)); + if ( ret != 0) + DRM_ERROR(" gem busy error! %d", ret); + + drm_gem_object_unreference(obj); + spin_unlock(&dev->struct_mutex); + return 0; +} + +/*ARGSUSED*/ +int +i915_gem_throttle_ioctl(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + + if (dev->driver->use_gem != 1) + return ENODEV; + + return i915_gem_ring_throttle(dev, fpriv); +} + +static int +i915_gem_object_get_page_list(struct drm_gem_object *obj) +{ + struct drm_i915_gem_object *obj_priv = obj->driver_private; + caddr_t va; + long i; + + if (obj_priv->page_list) + return 0; + pgcnt_t np = btop(obj->size); + + obj_priv->page_list = kmem_zalloc(np * sizeof(caddr_t), KM_SLEEP); + if (obj_priv->page_list == NULL) { + DRM_ERROR("Faled to allocate page list\n"); + return ENOMEM; + } + + for (i = 0, va = obj->kaddr; i < np; i++, va += PAGESIZE) { + obj_priv->page_list[i] = va; + } + return 0; +} + + +int i915_gem_init_object(struct drm_gem_object *obj) +{ + struct drm_i915_gem_object *obj_priv; + + obj_priv = drm_calloc(1, sizeof(*obj_priv), DRM_MEM_DRIVER); + if (obj_priv == NULL) + return ENOMEM; + + /* + * We've just allocated pages from the kernel, + * so they've just been written by the CPU with + * zeros. They'll need to be clflushed before we + * use them with the GPU. + */ + obj->write_domain = I915_GEM_DOMAIN_CPU; + obj->read_domains = I915_GEM_DOMAIN_CPU; + + obj->driver_private = obj_priv; + obj_priv->obj = obj; + INIT_LIST_HEAD(&obj_priv->list); + return 0; +} + +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); + + DRM_DEBUG("%s: obj %d",__func__, obj->name); + + (void) i915_gem_object_unbind(obj, 1); + if (obj_priv->page_cpu_valid != NULL) + drm_free(obj_priv->page_cpu_valid, obj->size / PAGE_SIZE, DRM_MEM_DRIVER); + drm_free(obj->driver_private, sizeof(*obj_priv), DRM_MEM_DRIVER); +} + +/** 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, uint32_t type) +{ + struct drm_gem_object *obj; + struct drm_i915_gem_object *obj_priv; + int ret; + + while (!list_empty(head)) { + obj_priv = list_entry(head->next, + struct drm_i915_gem_object, + list); + obj = obj_priv->obj; + + if (obj_priv->pin_count != 0) { + DRM_ERROR("Pinned object in unbind list\n"); + spin_unlock(&dev->struct_mutex); + return EINVAL; + } + DRM_DEBUG("%s: obj %d type %d",__func__, obj->name, type); + ret = i915_gem_object_unbind(obj, type); + if (ret != 0) { + DRM_ERROR("Error unbinding object in LeaveVT: %d\n", + ret); + spin_unlock(&dev->struct_mutex); + return ret; + } + } + + + return 0; +} + +static int +i915_gem_idle(struct drm_device *dev, uint32_t type) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + uint32_t seqno, cur_seqno, last_seqno; + int stuck, ret; + + spin_lock(&dev->struct_mutex); + + if (dev_priv->mm.suspended || dev_priv->ring.ring_obj == NULL) { + spin_unlock(&dev->struct_mutex); + 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; + + /* Cancel the retire work handler, wait for it to finish if running + */ + if (worktimer_id != NULL) { + (void) untimeout(worktimer_id); + worktimer_id = NULL; + } + + 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) { + spin_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; + } + } + DRM_UDELAY(10); + last_seqno = cur_seqno; + } + dev_priv->mm.waiting_gem_seqno = 0; + + i915_gem_retire_requests(dev); + + /* Empty the active and flushing lists to inactive. If there's + * anything left at this point, it means that we're wedged and + * nothing good's going to happen by leaving them there. So strip + * the GPU domains and just stuff them onto inactive. + */ + while (!list_empty(&dev_priv->mm.active_list)) { + struct drm_i915_gem_object *obj_priv; + + obj_priv = list_entry(dev_priv->mm.active_list.next, + struct drm_i915_gem_object, + list); + obj_priv->obj->write_domain &= ~I915_GEM_GPU_DOMAINS; + i915_gem_object_move_to_inactive(obj_priv->obj); + } + + while (!list_empty(&dev_priv->mm.flushing_list)) { + struct drm_i915_gem_object *obj_priv; + + obj_priv = list_entry(dev_priv->mm.flushing_list.next, + struct drm_i915_gem_object, + list); + obj_priv->obj->write_domain &= ~I915_GEM_GPU_DOMAINS; + i915_gem_object_move_to_inactive(obj_priv->obj); + } + + /* Move all inactive buffers out of the GTT. */ + ret = i915_gem_evict_from_list(dev, &dev_priv->mm.inactive_list, type); + ASSERT(list_empty(&dev_priv->mm.inactive_list)); + if (ret) { + spin_unlock(&dev->struct_mutex); + return ret; + } + + i915_gem_cleanup_ringbuffer(dev); + spin_unlock(&dev->struct_mutex); + + return 0; +} + +static int +i915_gem_init_hws(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_gem_object *obj; + struct drm_i915_gem_object *obj_priv; + int ret; + + /* If we need a physical address for the status page, it's already + * initialized at driver load time. + */ + if (!I915_NEED_GFX_HWS(dev)) + return 0; + + + obj = drm_gem_object_alloc(dev, 4096); + if (obj == NULL) { + DRM_ERROR("Failed to allocate status page\n"); + return ENOMEM; + } + + obj_priv = obj->driver_private; + + ret = i915_gem_object_pin(obj, 4096); + if (ret != 0) { + drm_gem_object_unreference(obj); + return ret; + } + + dev_priv->status_gfx_addr = obj_priv->gtt_offset; + dev_priv->hws_map.offset = dev->agp->agp_info.agpi_aperbase + obj_priv->gtt_offset; + dev_priv->hws_map.size = 4096; + dev_priv->hws_map.type = 0; + dev_priv->hws_map.flags = 0; + dev_priv->hws_map.mtrr = 0; + + drm_core_ioremap(&dev_priv->hws_map, dev); + if (dev_priv->hws_map.handle == NULL) { + DRM_ERROR("Failed to map status page.\n"); + (void) memset(&dev_priv->hws_map, 0, sizeof(dev_priv->hws_map)); + drm_gem_object_unreference(obj); + return EINVAL; + } + + dev_priv->hws_obj = obj; + + dev_priv->hw_status_page = dev_priv->hws_map.handle; + + (void) memset(dev_priv->hw_status_page, 0, PAGE_SIZE); + I915_WRITE(HWS_PGA, dev_priv->status_gfx_addr); + (void) I915_READ(HWS_PGA); /* posting read */ + DRM_DEBUG("hws offset: 0x%08x\n", dev_priv->status_gfx_addr); + + return 0; +} + +static void +i915_gem_cleanup_hws(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_gem_object *obj; + + if (dev_priv->hws_obj == NULL) + return; + + obj = dev_priv->hws_obj; + + drm_core_ioremapfree(&dev_priv->hws_map, dev); + i915_gem_object_unpin(obj); + drm_gem_object_unreference(obj); + dev_priv->hws_obj = NULL; + + (void) memset(&dev_priv->hws_map, 0, sizeof(dev_priv->hws_map)); + dev_priv->hw_status_page = NULL; + + /* Write high address into HWS_PGA when disabling. */ + I915_WRITE(HWS_PGA, 0x1ffff000); +} + +int +i915_gem_init_ringbuffer(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_gem_object *obj; + struct drm_i915_gem_object *obj_priv; + int ret; + u32 head; + + ret = i915_gem_init_hws(dev); + if (ret != 0) + return ret; + obj = drm_gem_object_alloc(dev, 128 * 1024); + if (obj == NULL) { + DRM_ERROR("Failed to allocate ringbuffer\n"); + i915_gem_cleanup_hws(dev); + return ENOMEM; + } + + obj_priv = obj->driver_private; + ret = i915_gem_object_pin(obj, 4096); + if (ret != 0) { + drm_gem_object_unreference(obj); + i915_gem_cleanup_hws(dev); + return ret; + } + + /* Set up the kernel mapping for the ring. */ + dev_priv->ring.Size = obj->size; + dev_priv->ring.tail_mask = obj->size - 1; + + dev_priv->ring.map.offset = dev->agp->agp_info.agpi_aperbase + obj_priv->gtt_offset; + dev_priv->ring.map.size = obj->size; + dev_priv->ring.map.type = 0; + dev_priv->ring.map.flags = 0; + dev_priv->ring.map.mtrr = 0; + + drm_core_ioremap(&dev_priv->ring.map, dev); + if (dev_priv->ring.map.handle == NULL) { + DRM_ERROR("Failed to map ringbuffer.\n"); + (void) memset(&dev_priv->ring, 0, sizeof(dev_priv->ring)); + drm_gem_object_unreference(obj); + i915_gem_cleanup_hws(dev); + return EINVAL; + } + + dev_priv->ring.ring_obj = obj; + + dev_priv->ring.virtual_start = (u8 *) dev_priv->ring.map.handle; + + /* Stop the ring if it's running. */ + I915_WRITE(PRB0_CTL, 0); + I915_WRITE(PRB0_HEAD, 0); + I915_WRITE(PRB0_TAIL, 0); + + + /* Initialize the ring. */ + I915_WRITE(PRB0_START, obj_priv->gtt_offset); + head = I915_READ(PRB0_HEAD) & HEAD_ADDR; + + /* G45 ring initialization fails to reset head to zero */ + if (head != 0) { + DRM_ERROR("Ring head not reset to zero " + "ctl %08x head %08x tail %08x start %08x\n", + I915_READ(PRB0_CTL), + I915_READ(PRB0_HEAD), + I915_READ(PRB0_TAIL), + I915_READ(PRB0_START)); + I915_WRITE(PRB0_HEAD, 0); + + DRM_ERROR("Ring head forced to zero " + "ctl %08x head %08x tail %08x start %08x\n", + I915_READ(PRB0_CTL), + I915_READ(PRB0_HEAD), + I915_READ(PRB0_TAIL), + I915_READ(PRB0_START)); + } + + I915_WRITE(PRB0_CTL, + ((obj->size - 4096) & RING_NR_PAGES) | + RING_NO_REPORT | + RING_VALID); + + head = I915_READ(PRB0_HEAD) & HEAD_ADDR; + + /* If the head is still not zero, the ring is dead */ + if (head != 0) { + DRM_ERROR("Ring initialization failed " + "ctl %08x head %08x tail %08x start %08x\n", + I915_READ(PRB0_CTL), + I915_READ(PRB0_HEAD), + I915_READ(PRB0_TAIL), + I915_READ(PRB0_START)); + return EIO; + } + + /* Update our cache of the ring state */ + i915_kernel_lost_context(dev); + + return 0; +} + +static void +i915_gem_cleanup_ringbuffer(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + + if (dev_priv->ring.ring_obj == NULL) + return; + + drm_core_ioremapfree(&dev_priv->ring.map, dev); + + i915_gem_object_unpin(dev_priv->ring.ring_obj); + drm_gem_object_unreference(dev_priv->ring.ring_obj); + dev_priv->ring.ring_obj = NULL; + (void) memset(&dev_priv->ring, 0, sizeof(dev_priv->ring)); + i915_gem_cleanup_hws(dev); +} + +/*ARGSUSED*/ +int +i915_gem_entervt_ioctl(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + drm_i915_private_t *dev_priv = dev->dev_private; + int ret; + + if (dev->driver->use_gem != 1) + return ENODEV; + + if (dev_priv->mm.wedged) { + DRM_ERROR("Reenabling wedged hardware, good luck\n"); + dev_priv->mm.wedged = 0; + } + /* Set up the kernel mapping for the ring. */ + dev_priv->mm.gtt_mapping.offset = dev->agp->agp_info.agpi_aperbase; + dev_priv->mm.gtt_mapping.size = dev->agp->agp_info.agpi_apersize; + dev_priv->mm.gtt_mapping.type = 0; + dev_priv->mm.gtt_mapping.flags = 0; + dev_priv->mm.gtt_mapping.mtrr = 0; + + drm_core_ioremap(&dev_priv->mm.gtt_mapping, dev); + + spin_lock(&dev->struct_mutex); + dev_priv->mm.suspended = 0; + ret = i915_gem_init_ringbuffer(dev); + if (ret != 0) + return ret; + + spin_unlock(&dev->struct_mutex); + + drm_irq_install(dev); + + return 0; +} + +/*ARGSUSED*/ +int +i915_gem_leavevt_ioctl(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + drm_i915_private_t *dev_priv = dev->dev_private; + int ret; + + if (dev->driver->use_gem != 1) + return ENODEV; + + ret = i915_gem_idle(dev, 0); + drm_irq_uninstall(dev); + + drm_core_ioremapfree(&dev_priv->mm.gtt_mapping, dev); + return ret; +} + +void +i915_gem_lastclose(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + int ret; + + ret = i915_gem_idle(dev, 1); + if (ret) + DRM_ERROR("failed to idle hardware: %d\n", ret); + + drm_mm_clean_ml(&dev_priv->mm.gtt_space); +} + +void +i915_gem_load(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + + INIT_LIST_HEAD(&dev_priv->mm.active_list); + INIT_LIST_HEAD(&dev_priv->mm.flushing_list); + INIT_LIST_HEAD(&dev_priv->mm.inactive_list); + INIT_LIST_HEAD(&dev_priv->mm.request_list); + dev_priv->mm.next_gem_seqno = 1; + + i915_gem_detect_bit_6_swizzle(dev); + +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/intel/io/drm/i915_gem_debug.c Sat Dec 05 13:25:40 2009 +0800 @@ -0,0 +1,1120 @@ +/* BEGIN CSTYLED */ + +/* + * Copyright (c) 2009, Intel Corporation. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Keith Packard <keithp@keithp.com> + * + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include "drmP.h" +#include "drm.h" +#include "i915_drm.h" +#include "i915_drv.h" + +#define BUFFER_FAIL(_count, _len, _name) { \ + DRM_ERROR("Buffer size too small in %s (%d < %d)\n", \ + (_name), (_count), (_len)); \ + (*failures)++; \ + return count; \ +} + + +static uint32_t saved_s2 = 0, saved_s4 = 0; +static char saved_s2_set = 0, saved_s4_set = 0; + +static float +int_as_float(uint32_t intval) +{ + union intfloat { + uint32_t i; + float f; + } uval; + + uval.i = intval; + return uval.f; +} + +static void +instr_out(uint32_t *data, uint32_t hw_offset, unsigned int index, + const char *fmt, ...) +{ + + DRM_ERROR("0x%08x: 0x%08x:%s ", hw_offset + index * 4, data[index], + index == 0 ? "" : " "); + va_list ap; + + va_start(ap, fmt); + vcmn_err(CE_WARN, fmt, ap); + va_end(ap); + +} + +static int +decode_mi(uint32_t *data, int count, uint32_t hw_offset, int *failures) +{ + unsigned int opcode; + + struct { + uint32_t opcode; + int min_len; + int max_len; + char *name; + } opcodes_mi[] = { + { 0x08, 1, 1, "MI_ARB_ON_OFF" }, + { 0x0a, 1, 1, "MI_BATCH_BUFFER_END" }, + { 0x31, 2, 2, "MI_BATCH_BUFFER_START" }, + { 0x14, 3, 3, "MI_DISPLAY_BUFFER_INFO" }, + { 0x04, 1, 1, "MI_FLUSH" }, + { 0x22, 3, 3, "MI_LOAD_REGISTER_IMM" }, + { 0x13, 2, 2, "MI_LOAD_SCAN_LINES_EXCL" }, + { 0x12, 2, 2, "MI_LOAD_SCAN_LINES_INCL" }, + { 0x00, 1, 1, "MI_NOOP" }, + { 0x11, 2, 2, "MI_OVERLAY_FLIP" }, + { 0x07, 1, 1, "MI_REPORT_HEAD" }, + { 0x18, 2, 2, "MI_SET_CONTEXT" }, + { 0x20, 3, 4, "MI_STORE_DATA_IMM" }, + { 0x21, 3, 4, "MI_STORE_DATA_INDEX" }, + { 0x24, 3, 3, "MI_STORE_REGISTER_MEM" }, + { 0x02, 1, 1, "MI_USER_INTERRUPT" }, + { 0x03, 1, 1, "MI_WAIT_FOR_EVENT" }, + }; + + + for (opcode = 0; opcode < sizeof(opcodes_mi) / sizeof(opcodes_mi[0]); + opcode++) { + if ((data[0] & 0x1f800000) >> 23 == opcodes_mi[opcode].opcode) { + unsigned int len = 1, i; + + instr_out(data, hw_offset, 0, "%s\n", opcodes_mi[opcode].name); + if (opcodes_mi[opcode].max_len > 1) { + len = (data[0] & 0x000000ff) + 2; + if (len < opcodes_mi[opcode].min_len || + len > opcodes_mi[opcode].max_len) + { + DRM_ERROR("Bad length in %s\n", + opcodes_mi[opcode].name); + } + } + + for (i = 1; i < len; i++) { + if (i >= count) + BUFFER_FAIL(count, len, opcodes_mi[opcode].name); + instr_out(data, hw_offset, i, "dword %d\n", i); + } + + return len; + } + } + + instr_out(data, hw_offset, 0, "MI UNKNOWN\n"); + (*failures)++; + return 1; +} + +static int +decode_2d(uint32_t *data, int count, uint32_t hw_offset, int *failures) +{ + unsigned int opcode, len; + char *format = NULL; + + struct { + uint32_t opcode; + int min_len; + int max_len; + char *name; + } opcodes_2d[] = { + { 0x40, 5, 5, "COLOR_BLT" }, + { 0x43, 6, 6, "SRC_COPY_BLT" }, + { 0x01, 8, 8, "XY_SETUP_BLT" }, + { 0x11, 9, 9, "XY_SETUP_MONO_PATTERN_SL_BLT" }, + { 0x03, 3, 3, "XY_SETUP_CLIP_BLT" }, + { 0x24, 2, 2, "XY_PIXEL_BLT" }, + { 0x25, 3, 3, "XY_SCANLINES_BLT" }, + { 0x26, 4, 4, "Y_TEXT_BLT" }, + { 0x31, 5, 134, "XY_TEXT_IMMEDIATE_BLT" }, + { 0x50, 6, 6, "XY_COLOR_BLT" }, + { 0x51, 6, 6, "XY_PAT_BLT" }, + { 0x76, 8, 8, "XY_PAT_CHROMA_BLT" }, + { 0x72, 7, 135, "XY_PAT_BLT_IMMEDIATE" }, + { 0x77, 9, 137, "XY_PAT_CHROMA_BLT_IMMEDIATE" }, + { 0x52, 9, 9, "XY_MONO_PAT_BLT" }, + { 0x59, 7, 7, "XY_MONO_PAT_FIXED_BLT" }, + { 0x53, 8, 8, "XY_SRC_COPY_BLT" }, + { 0x54, 8, 8, "XY_MONO_SRC_COPY_BLT" }, + { 0x71, 9, 137, "XY_MONO_SRC_COPY_IMMEDIATE_BLT" }, + { 0x55, 9, 9, "XY_FULL_BLT" }, + { 0x55, 9, 137, "XY_FULL_IMMEDIATE_PATTERN_BLT" }, + { 0x56, 9, 9, "XY_FULL_MONO_SRC_BLT" }, + { 0x75, 10, 138, "XY_FULL_MONO_SRC_IMMEDIATE_PATTERN_BLT" }, + { 0x57, 12, 12, "XY_FULL_MONO_PATTERN_BLT" }, + { 0x58, 12, 12, "XY_FULL_MONO_PATTERN_MONO_SRC_BLT" }, + }; + + switch ((data[0] & 0x1fc00000) >> 22) { + case 0x50: + instr_out(data, hw_offset, 0, + "XY_COLOR_BLT (rgb %sabled, alpha %sabled, dst tile %d)\n", + (data[0] & (1 << 20)) ? "en" : "dis", + (data[0] & (1 << 21)) ? "en" : "dis", + (data[0] >> 11) & 1); + + len = (data[0] & 0x000000ff) + 2; + if (len != 6) + DRM_ERROR("Bad count in XY_COLOR_BLT\n"); + if (count < 6) + BUFFER_FAIL(count, len, "XY_COLOR_BLT"); + + switch ((data[1] >> 24) & 0x3) { + case 0: + format="8"; + break; + case 1: + format="565"; + break; + case 2: + format="1555"; + break; + case 3: + format="8888"; + break; + } + + instr_out(data, hw_offset, 1, "format %s, pitch %d, " + "clipping %sabled\n", format, + (short)(data[1] & 0xffff), + data[1] & (1 << 30) ? "en" : "dis"); + instr_out(data, hw_offset, 2, "(%d,%d)\n", + data[2] & 0xffff, data[2] >> 16); + instr_out(data, hw_offset, 3, "(%d,%d)\n", + data[3] & 0xffff, data[3] >> 16); + instr_out(data, hw_offset, 4, "offset 0x%08x\n", data[4]); + instr_out(data, hw_offset, 5, "color\n"); + return len; + case 0x53: + instr_out(data, hw_offset, 0, + "XY_SRC_COPY_BLT (rgb %sabled, alpha %sabled, " + "src tile %d, dst tile %d)\n", + (data[0] & (1 << 20)) ? "en" : "dis", + (data[0] & (1 << 21)) ? "en" : "dis", + (data[0] >> 15) & 1, + (data[0] >> 11) & 1); + + len = (data[0] & 0x000000ff) + 2; + if (len != 8) + DRM_ERROR("Bad count in XY_SRC_COPY_BLT\n"); + if (count < 8) + BUFFER_FAIL(count, len, "XY_SRC_COPY_BLT"); + + switch ((data[1] >> 24) & 0x3) { + case 0: + format="8"; + break; + case 1: + format="565"; + break; + case 2: + format="1555"; + break; + case 3: + format="8888"; + break; + } + + instr_out(data, hw_offset, 1, "format %s, dst pitch %d, " + "clipping %sabled\n", format, + (short)(data[1] & 0xffff), + data[1] & (1 << 30) ? "en" : "dis"); + instr_out(data, hw_offset, 2, "dst (%d,%d)\n", + data[2] & 0xffff, data[2] >> 16); + instr_out(data, hw_offset, 3, "dst (%d,%d)\n", + data[3] & 0xffff, data[3] >> 16); + instr_out(data, hw_offset, 4, "dst offset 0x%08x\n", data[4]); + instr_out(data, hw_offset, 5, "src (%d,%d)\n", + data[5] & 0xffff, data[5] >> 16); + instr_out(data, hw_offset, 6, "src pitch %d\n", + (short)(data[6] & 0xffff)); + instr_out(data, hw_offset, 7, "src offset 0x%08x\n", data[7]); + return len; + } + + for (opcode = 0; opcode < sizeof(opcodes_2d) / sizeof(opcodes_2d[0]); + opcode++) { + if ((data[0] & 0x1fc00000) >> 22 == opcodes_2d[opcode].opcode) { + unsigned int i; + + len = 1; + instr_out(data, hw_offset, 0, "%s\n", opcodes_2d[opcode].name); + if (opcodes_2d[opcode].max_len > 1) { + len = (data[0] & 0x000000ff) + 2; + if (len < opcodes_2d[opcode].min_len || + len > opcodes_2d[opcode].max_len) + { + DRM_ERROR("Bad count in %s\n", opcodes_2d[opcode].name); + } + } + + for (i = 1; i < len; i++) { + if (i >= count) + BUFFER_FAIL(count, len, opcodes_2d[opcode].name); + instr_out(data, hw_offset, i, "dword %d\n", i); + } + + return len; + } + } + + instr_out(data, hw_offset, 0, "2D UNKNOWN\n"); + (*failures)++; + return 1; +} + +/*ARGSUSED*/ +static int +decode_3d_1c(uint32_t *data, int count, uint32_t hw_offset, int *failures) +{ + switch ((data[0] & 0x00f80000) >> 19) { + case 0x11: + instr_out(data, hw_offset, 0, "3DSTATE_DEPTH_SUBRECTANGLE_DISALBE\n"); + return 1; + case 0x10: + instr_out(data, hw_offset, 0, "3DSTATE_SCISSOR_ENABLE\n"); + return 1; + case 0x01: + instr_out(data, hw_offset, 0, "3DSTATE_MAP_COORD_SET_I830\n"); + return 1; + case 0x0a: + instr_out(data, hw_offset, 0, "3DSTATE_MAP_CUBE_I830\n"); + return 1; + case 0x05: + instr_out(data, hw_offset, 0, "3DSTATE_MAP_TEX_STREAM_I830\n"); + return 1; + } + + instr_out(data, hw_offset, 0, "3D UNKNOWN\n"); + (*failures)++; + return 1; +} + +static int +decode_3d_1d(uint32_t *data, int count, uint32_t hw_offset, int *failures, int i830) +{ + unsigned int len, i, c, opcode, word, map, sampler, instr; + + struct { + uint32_t opcode; + int i830_only; + int min_len; + int max_len; + char *name; + } opcodes_3d_1d[] = { + { 0x8e, 0, 3, 3, "3DSTATE_BUFFER_INFO" }, + { 0x86, 0, 4, 4, "3DSTATE_CHROMA_KEY" }, + { 0x9c, 0, 1, 1, "3DSTATE_CLEAR_PARAMETERS" }, + { 0x88, 0, 2, 2, "3DSTATE_CONSTANT_BLEND_COLOR" }, + { 0x99, 0, 2, 2, "3DSTATE_DEFAULT_DIFFUSE" }, + { 0x9a, 0, 2, 2, "3DSTATE_DEFAULT_SPECULAR" }, + { 0x98, 0, 2, 2, "3DSTATE_DEFAULT_Z" }, + { 0x97, 0, 2, 2, "3DSTATE_DEPTH_OFFSET_SCALE" }, + { 0x85, 0, 2, 2, "3DSTATE_DEST_BUFFER_VARIABLES" }, + { 0x80, 0, 5, 5, "3DSTATE_DRAWING_RECTANGLE" }, + { 0x8e, 0, 3, 3, "3DSTATE_BUFFER_INFO" }, + { 0x9d, 0, 65, 65, "3DSTATE_FILTER_COEFFICIENTS_4X4" }, + { 0x9e, 0, 4, 4, "3DSTATE_MONO_FILTER" }, + { 0x89, 0, 4, 4, "3DSTATE_FOG_MODE" }, + { 0x8f, 0, 2, 16, "3DSTATE_MAP_PALLETE_LOAD_32" }, + { 0x81, 0, 3, 3, "3DSTATE_SCISSOR_RECTANGLE" }, + { 0x83, 0, 2, 2, "3DSTATE_SPAN_STIPPLE" }, + { 0x8c, 1, 2, 2, "3DSTATE_MAP_COORD_TRANSFORM_I830" }, + { 0x8b, 1, 2, 2, "3DSTATE_MAP_VERTEX_TRANSFORM_I830" }, + { 0x8d, 1, 3, 3, "3DSTATE_W_STATE_I830" }, + { 0x01, 1, 2, 2, "3DSTATE_COLOR_FACTOR_I830" }, + { 0x02, 1, 2, 2, "3DSTATE_MAP_COORD_SETBIND_I830" }, + }; + + switch ((data[0] & 0x00ff0000) >> 16) { + case 0x07: + /* This instruction is unusual. A 0 length means just 1 DWORD instead of + * 2. The 0 length is specified in one place to be unsupported, but + * stated to be required in another, and 0 length LOAD_INDIRECTs appear + * to cause no harm at least. + */ + instr_out(data, hw_offset, 0, "3DSTATE_LOAD_INDIRECT\n"); + len = (data[0] & 0x000000ff) + 1; + i = 1; + if (data[0] & (0x01 << 8)) { + if (i + 2 >= count) + BUFFER_FAIL(count, len, "3DSTATE_LOAD_INDIRECT"); + instr_out(data, hw_offset, i++, "SIS.0\n"); + instr_out(data, hw_offset, i++, "SIS.1\n"); + } + if (data[0] & (0x02 << 8)) { + if (i + 1 >= count) + BUFFER_FAIL(count, len, "3DSTATE_LOAD_INDIRECT"); + instr_out(data, hw_offset, i++, "DIS.0\n"); + } + if (data[0] & (0x04 << 8)) { + if (i + 2 >= count) + BUFFER_FAIL(count, len, "3DSTATE_LOAD_INDIRECT"); + instr_out(data, hw_offset, i++, "SSB.0\n"); + instr_out(data, hw_offset, i++, "SSB.1\n"); + } + if (data[0] & (0x08 << 8)) { + if (i + 2 >= count) + BUFFER_FAIL(count, len, "3DSTATE_LOAD_INDIRECT"); + instr_out(data, hw_offset, i++, "MSB.0\n"); + instr_out(data, hw_offset, i++, "MSB.1\n"); + } + if (data[0] & (0x10 << 8)) { + if (i + 2 >= count) + BUFFER_FAIL(count, len, "3DSTATE_LOAD_INDIRECT"); + instr_out(data, hw_offset, i++, "PSP.0\n"); + instr_out(data, hw_offset, i++, "PSP.1\n"); + } + if (data[0] & (0x20 << 8)) { + if (i + 2 >= count) + BUFFER_FAIL(count, len, "3DSTATE_LOAD_INDIRECT"); + instr_out(data, hw_offset, i++, "PSC.0\n"); + instr_out(data, hw_offset, i++, "PSC.1\n"); + } + if (len != i) { + DRM_ERROR("Bad count in 3DSTATE_LOAD_INDIRECT\n"); + (*failures)++; + return len; + } + return len; + case 0x04: + instr_out(data, hw_offset, 0, "3DSTATE_LOAD_STATE_IMMEDIATE_1\n"); + len = (data[0] & 0x0000000f) + 2; + i = 1; + for (word = 0; word <= 7; word++) { + if (data[0] & (1 << (4 + word))) { + if (i >= count) + BUFFER_FAIL(count, len, "3DSTATE_LOAD_STATE_IMMEDIATE_1"); + + /* save vertex state for decode */ + if (word == 2) { + saved_s2_set = 1; + saved_s2 = data[i]; + } + if (word == 4) { + saved_s4_set = 1; + saved_s4 = data[i]; + } + + instr_out(data, hw_offset, i++, "S%d\n", word); + } + } + if (len != i) { + DRM_ERROR("Bad count in 3DSTATE_LOAD_INDIRECT\n"); + (*failures)++; + } + return len; + case 0x00: + instr_out(data, hw_offset, 0, "3DSTATE_MAP_STATE\n"); + len = (data[0] & 0x0000003f) + 2; + + i = 1; + for (map = 0; map <= 15; map++) { + if (data[1] & (1 << map)) { + if (i + 3 >= count) + BUFFER_FAIL(count, len, "3DSTATE_MAP_STATE"); + instr_out(data, hw_offset, i++, "map %d MS2\n", map); + instr_out(data, hw_offset, i++, "map %d MS3\n", map); + instr_out(data, hw_offset, i++, "map %d MS4\n", map); + } + } + if (len != i) { + DRM_ERROR("Bad count in 3DSTATE_MAP_STATE\n"); + (*failures)++; + return len; + } + return len; + case 0x06: + instr_out(data, hw_offset, 0, "3DSTATE_PIXEL_SHADER_CONSTANTS\n"); + len = (data[0] & 0x000000ff) + 2; + + i = 1; + for (c = 0; c <= 31; c++) { + if (data[1] & (1 << c)) { + if (i + 4 >= count) + BUFFER_FAIL(count, len, "3DSTATE_PIXEL_SHADER_CONSTANTS"); + instr_out(data, hw_offset, i, "C%d.X = %f\n", + c, int_as_float(data[i])); + i++; + instr_out(data, hw_offset, i, "C%d.Y = %f\n", + c, int_as_float(data[i])); + i++; + instr_out(data, hw_offset, i, "C%d.Z = %f\n", + c, int_as_float(data[i])); + i++; + instr_out(data, hw_offset, i, "C%d.W = %f\n", + c, int_as_float(data[i])); + i++; + } + } + if (len != i) { + DRM_ERROR("Bad count in 3DSTATE_MAP_STATE\n"); + (*failures)++; + } + return len; + case 0x05: + instr_out(data, hw_offset, 0, "3DSTATE_PIXEL_SHADER_PROGRAM\n"); + len = (data[0] & 0x000000ff) + 2; + if ((len - 1) % 3 != 0 || len > 370) { + DRM_ERROR("Bad count in 3DSTATE_PIXEL_SHADER_PROGRAM\n"); + (*failures)++; + } + i = 1; + for (instr = 0; instr < (len - 1) / 3; instr++) { + if (i + 3 >= count) + BUFFER_FAIL(count, len, "3DSTATE_MAP_STATE"); + instr_out(data, hw_offset, i++, "PS%03x\n", instr); + instr_out(data, hw_offset, i++, "PS%03x\n", instr); + instr_out(data, hw_offset, i++, "PS%03x\n", instr); + } + return len; + case 0x01: + if (i830) + break; + instr_out(data, hw_offset, 0, "3DSTATE_SAMPLER_STATE\n"); + len = (data[0] & 0x0000003f) + 2; + i = 1; + for (sampler = 0; sampler <= 15; sampler++) { + if (data[1] & (1 << sampler)) { + if (i + 3 >= count) + BUFFER_FAIL(count, len, "3DSTATE_SAMPLER_STATE"); + instr_out(data, hw_offset, i++, "sampler %d SS2\n", + sampler); + instr_out(data, hw_offset, i++, "sampler %d SS3\n", + sampler); + instr_out(data, hw_offset, i++, "sampler %d SS4\n", + sampler); + } + } + if (len != i) { + DRM_ERROR("Bad count in 3DSTATE_SAMPLER_STATE\n"); + (*failures)++; + } + return len; + } + + for (opcode = 0; opcode < sizeof(opcodes_3d_1d) / sizeof(opcodes_3d_1d[0]); + opcode++) + { + if (opcodes_3d_1d[opcode].i830_only && !i830) + continue; + + if (((data[0] & 0x00ff0000) >> 16) == opcodes_3d_1d[opcode].opcode) { + len = 1; + + instr_out(data, hw_offset, 0, "%s\n", opcodes_3d_1d[opcode].name); + if (opcodes_3d_1d[opcode].max_len > 1) { + len = (data[0] & 0x0000ffff) + 2; + if (len < opcodes_3d_1d[opcode].min_len || + len > opcodes_3d_1d[opcode].max_len) + { + DRM_ERROR("Bad count in %s\n", + opcodes_3d_1d[opcode].name); + (*failures)++; + } + } + + for (i = 1; i < len; i++) { + if (i >= count) + BUFFER_FAIL(count, len, opcodes_3d_1d[opcode].name); + instr_out(data, hw_offset, i, "dword %d\n", i); + } + + return len; + } + } + + instr_out(data, hw_offset, 0, "3D UNKNOWN\n"); + (*failures)++; + return 1; +} + +static int +decode_3d_primitive(uint32_t *data, int count, uint32_t hw_offset, + int *failures) +{ + char immediate = (data[0] & (1 << 23)) == 0; + unsigned int len, i; + char *primtype; + + switch ((data[0] >> 18) & 0xf) { + case 0x0: primtype = "TRILIST"; break; + case 0x1: primtype = "TRISTRIP"; break; + case 0x2: primtype = "TRISTRIP_REVERSE"; break; + case 0x3: primtype = "TRIFAN"; break; + case 0x4: primtype = "POLYGON"; break; + case 0x5: primtype = "LINELIST"; break; + case 0x6: primtype = "LINESTRIP"; break; + case 0x7: primtype = "RECTLIST"; break; + case 0x8: primtype = "POINTLIST"; break; + case 0x9: primtype = "DIB"; break; + case 0xa: primtype = "CLEAR_RECT"; break; + default: primtype = "unknown"; break; + } + + /* XXX: 3DPRIM_DIB not supported */ + if (immediate) { + len = (data[0] & 0x0003ffff) + 2; + instr_out(data, hw_offset, 0, "3DPRIMITIVE inline %s\n", primtype); + if (count < len) + BUFFER_FAIL(count, len, "3DPRIMITIVE inline"); + if (!saved_s2_set || !saved_s4_set) { + DRM_ERROR("unknown vertex format\n"); + for (i = 1; i < len; i++) { + instr_out(data, hw_offset, i, + " vertex data (%f float)\n", + int_as_float(data[i])); + } + } else { + unsigned int vertex = 0; + for (i = 1; i < len;) { + unsigned int tc; + +#define VERTEX_OUT(fmt, ...) { \ + if (i < len) \ + instr_out(data, hw_offset, i, " V%d."fmt"\n", vertex, __VA_ARGS__); \ + else \ + DRM_ERROR(" missing data in V%d\n", vertex); \ + i++; \ +} + + VERTEX_OUT("X = %f", int_as_float(data[i])); + VERTEX_OUT("Y = %f", int_as_float(data[i])); + switch (saved_s4 >> 6 & 0x7) { + case 0x1: + VERTEX_OUT("Z = %f", int_as_float(data[i])); + break; + case 0x2: + VERTEX_OUT("Z = %f", int_as_float(data[i])); + VERTEX_OUT("W = %f", int_as_float(data[i])); + break; + case 0x3: + break; + case 0x4: + VERTEX_OUT("W = %f", int_as_float(data[i])); + break; + default: + DRM_ERROR("bad S4 position mask\n"); + } + + if (saved_s4 & (1 << 10)) { + VERTEX_OUT("color = (A=0x%02x, R=0x%02x, G=0x%02x, " + "B=0x%02x)", + data[i] >> 24, + (data[i] >> 16) & 0xff, + (data[i] >> 8) & 0xff, + data[i] & 0xff); + } + if (saved_s4 & (1 << 11)) { + VERTEX_OUT("spec = (A=0x%02x, R=0x%02x, G=0x%02x, " + "B=0x%02x)", + data[i] >> 24, + (data[i] >> 16) & 0xff, + (data[i] >> 8) & 0xff, + data[i] & 0xff); + } + if (saved_s4 & (1 << 12)) + VERTEX_OUT("width = 0x%08x)", data[i]); + + for (tc = 0; tc <= 7; tc++) { + switch ((saved_s2 >> (tc * 4)) & 0xf) { + case 0x0: + VERTEX_OUT("T%d.X = %f", tc, int_as_float(data[i])); + VERTEX_OUT("T%d.Y = %f", tc, int_as_float(data[i])); + break; + case 0x1: + VERTEX_OUT("T%d.X = %f", tc, int_as_float(data[i])); + VERTEX_OUT("T%d.Y = %f", tc, int_as_float(data[i])); + VERTEX_OUT("T%d.Z = %f", tc, int_as_float(data[i])); + break; + case 0x2: + VERTEX_OUT("T%d.X = %f", tc, int_as_float(data[i])); + VERTEX_OUT("T%d.Y = %f", tc, int_as_float(data[i])); + VERTEX_OUT("T%d.Z = %f", tc, int_as_float(data[i])); + VERTEX_OUT("T%d.W = %f", tc, int_as_float(data[i])); + break; + case 0x3: + VERTEX_OUT("T%d.X = %f", tc, int_as_float(data[i])); + break; + case 0x4: + VERTEX_OUT("T%d.XY = 0x%08x half-float", tc, data[i]); + break; + case 0x5: + VERTEX_OUT("T%d.XY = 0x%08x half-float", tc, data[i]); + VERTEX_OUT("T%d.ZW = 0x%08x half-float", tc, data[i]); + break; + case 0xf: + break; + default: + DRM_ERROR("bad S2.T%d format\n", tc); + } + } + vertex++; + } + } + } else { + /* indirect vertices */ + len = data[0] & 0x0000ffff; /* index count */ + if (data[0] & (1 << 17)) { + /* random vertex access */ + if (count < (len + 1) / 2 + 1) + BUFFER_FAIL(count, (len + 1) / 2 + 1, "3DPRIMITIVE random indirect"); + instr_out(data, hw_offset, 0, + "3DPRIMITIVE random indirect %s (%d)\n", primtype, len); + if (len == 0) { + /* vertex indices continue until 0xffff is found */ + for (i = 1; i < count; i++) { + if ((data[i] & 0xffff) == 0xffff) { + instr_out(data, hw_offset, i, + " indices: (terminator)\n"); + return i; + } else if ((data[i] >> 16) == 0xffff) { + instr_out(data, hw_offset, i, + " indices: 0x%04x, " + "(terminator)\n", + data[i] & 0xffff); + return i; + } else { + instr_out(data, hw_offset, i, + " indices: 0x%04x, 0x%04x\n", + data[i] & 0xffff, data[i] >> 16); + } + } + DRM_ERROR("3DPRIMITIVE: no terminator found in index buffer\n"); + (*failures)++; + return count; + } else { + /* fixed size vertex index buffer */ + for (i = 0; i < len; i += 2) { + if (i * 2 == len - 1) { + instr_out(data, hw_offset, i, + " indices: 0x%04x\n", + data[i] & 0xffff); + } else { + instr_out(data, hw_offset, i, + " indices: 0x%04x, 0x%04x\n", + data[i] & 0xffff, data[i] >> 16); + } + } + } + return (len + 1) / 2 + 1; + } else { + /* sequential vertex access */ + if (count < 2) + BUFFER_FAIL(count, 2, "3DPRIMITIVE seq indirect"); + instr_out(data, hw_offset, 0, + "3DPRIMITIVE sequential indirect %s, %d starting from " + "%d\n", primtype, len, data[1] & 0xffff); + instr_out(data, hw_offset, 1, " start\n"); + return 2; + } + } + + return len; +} + +static int +decode_3d(uint32_t *data, int count, uint32_t hw_offset, int *failures) +{ + unsigned int opcode; + + struct { + uint32_t opcode; + int min_len; + int max_len; + char *name; + } opcodes_3d[] = { + { 0x06, 1, 1, "3DSTATE_ANTI_ALIASING" }, + { 0x08, 1, 1, "3DSTATE_BACKFACE_STENCIL_OPS" }, + { 0x09, 1, 1, "3DSTATE_BACKFACE_STENCIL_MASKS" }, + { 0x16, 1, 1, "3DSTATE_COORD_SET_BINDINGS" }, + { 0x15, 1, 1, "3DSTATE_FOG_COLOR" }, + { 0x0b, 1, 1, "3DSTATE_INDEPENDENT_ALPHA_BLEND" }, + { 0x0d, 1, 1, "3DSTATE_MODES_4" }, + { 0x0c, 1, 1, "3DSTATE_MODES_5" }, + { 0x07, 1, 1, "3DSTATE_RASTERIZATION_RULES" }, + }; + + switch ((data[0] & 0x1f000000) >> 24) { + case 0x1f: + return decode_3d_primitive(data, count, hw_offset, failures); + case 0x1d: + return decode_3d_1d(data, count, hw_offset, failures, 0); + case 0x1c: + return decode_3d_1c(data, count, hw_offset, failures); + } + + for (opcode = 0; opcode < sizeof(opcodes_3d) / sizeof(opcodes_3d[0]); + opcode++) { + if ((data[0] & 0x1f000000) >> 24 == opcodes_3d[opcode].opcode) { + unsigned int len = 1, i; + + instr_out(data, hw_offset, 0, "%s\n", opcodes_3d[opcode].name); + if (opcodes_3d[opcode].max_len > 1) { + len = (data[0] & 0xff) + 2; + if (len < opcodes_3d[opcode].min_len || + len > opcodes_3d[opcode].max_len) + { + DRM_ERROR("Bad count in %s\n", opcodes_3d[opcode].name); + } + } + + for (i = 1; i < len; i++) { + if (i >= count) + BUFFER_FAIL(count, len, opcodes_3d[opcode].name); + instr_out(data, hw_offset, i, "dword %d\n", i); + } + return len; + } + } + + instr_out(data, hw_offset, 0, "3D UNKNOWN\n"); + (*failures)++; + return 1; +} + +static const char * +get_965_surfacetype(unsigned int surfacetype) +{ + switch (surfacetype) { + case 0: return "1D"; + case 1: return "2D"; + case 2: return "3D"; + case 3: return "CUBE"; + case 4: return "BUFFER"; + case 7: return "NULL"; + default: return "unknown"; + } +} + +static const char * +get_965_depthformat(unsigned int depthformat) +{ + switch (depthformat) { + case 0: return "s8_z24float"; + case 1: return "z32float"; + case 2: return "z24s8"; + case 5: return "z16"; + default: return "unknown"; + } +} + +static int +decode_3d_965(uint32_t *data, int count, uint32_t hw_offset, int *failures) +{ + unsigned int opcode, len; + + struct { + uint32_t opcode; + int min_len; + int max_len; + char *name; + } opcodes_3d[] = { + { 0x6000, 3, 3, "URB_FENCE" }, + { 0x6001, 2, 2, "CS_URB_STATE" }, + { 0x6002, 2, 2, "CONSTANT_BUFFER" }, + { 0x6101, 6, 6, "STATE_BASE_ADDRESS" }, + { 0x6102, 2, 2 , "STATE_SIP" }, + { 0x6104, 1, 1, "3DSTATE_PIPELINE_SELECT" }, + { 0x680b, 1, 1, "3DSTATE_VF_STATISTICS" }, + { 0x6904, 1, 1, "3DSTATE_PIPELINE_SELECT" }, + { 0x7800, 7, 7, "3DSTATE_PIPELINED_POINTERS" }, + { 0x7801, 6, 6, "3DSTATE_BINDING_TABLE_POINTERS" }, + { 0x780b, 1, 1, "3DSTATE_VF_STATISTICS" }, + { 0x7808, 5, 257, "3DSTATE_VERTEX_BUFFERS" }, + { 0x7809, 3, 256, "3DSTATE_VERTEX_ELEMENTS" }, + /* 0x7808: 3DSTATE_VERTEX_BUFFERS */ + /* 0x7809: 3DSTATE_VERTEX_ELEMENTS */ + { 0x7900, 4, 4, "3DSTATE_DRAWING_RECTANGLE" }, + { 0x7901, 5, 5, "3DSTATE_CONSTANT_COLOR" }, + { 0x7905, 5, 7, "3DSTATE_DEPTH_BUFFER" }, + { 0x7906, 2, 2, "3DSTATE_POLY_STIPPLE_OFFSET" }, + { 0x7907, 33, 33, "3DSTATE_POLY_STIPPLE_PATTERN" }, + { 0x7908, 3, 3, "3DSTATE_LINE_STIPPLE" }, + { 0x7909, 2, 2, "3DSTATE_GLOBAL_DEPTH_OFFSET_CLAMP" }, + { 0x790a, 3, 3, "3DSTATE_AA_LINE_PARAMETERS" }, + { 0x7b00, 6, 6, "3DPRIMITIVE" }, + }; + + len = (data[0] & 0x0000ffff) + 2; + + switch ((data[0] & 0xffff0000) >> 16) { + case 0x6101: + if (len != 6) + DRM_ERROR("Bad count in STATE_BASE_ADDRESS\n"); + if (count < 6) + BUFFER_FAIL(count, len, "STATE_BASE_ADDRESS"); + + instr_out(data, hw_offset, 0, + "STATE_BASE_ADDRESS\n"); + + if (data[1] & 1) { + instr_out(data, hw_offset, 1, "General state at 0x%08x\n", + data[1] & ~1); + } else + instr_out(data, hw_offset, 1, "General state not updated\n"); + + if (data[2] & 1) { + instr_out(data, hw_offset, 2, "Surface state at 0x%08x\n", + data[2] & ~1); + } else + instr_out(data, hw_offset, 2, "Surface state not updated\n"); + + if (data[3] & 1) { + instr_out(data, hw_offset, 3, "Indirect state at 0x%08x\n", + data[3] & ~1); + } else + instr_out(data, hw_offset, 3, "Indirect state not updated\n"); + + if (data[4] & 1) { + instr_out(data, hw_offset, 4, "General state upper bound 0x%08x\n", + data[4] & ~1); + } else + instr_out(data, hw_offset, 4, "General state not updated\n"); + + if (data[5] & 1) { + instr_out(data, hw_offset, 5, "Indirect state upper bound 0x%08x\n", + data[5] & ~1); + } else + instr_out(data, hw_offset, 5, "Indirect state not updated\n"); + + return len; + case 0x7800: + if (len != 7) + DRM_ERROR("Bad count in 3DSTATE_PIPELINED_POINTERS\n"); + if (count < 7) + BUFFER_FAIL(count, len, "3DSTATE_PIPELINED_POINTERS"); + + instr_out(data, hw_offset, 0, + "3DSTATE_PIPELINED_POINTERS\n"); + instr_out(data, hw_offset, 1, "VS state\n"); + instr_out(data, hw_offset, 2, "GS state\n"); + instr_out(data, hw_offset, 3, "Clip state\n"); + instr_out(data, hw_offset, 4, "SF state\n"); + instr_out(data, hw_offset, 5, "WM state\n"); + instr_out(data, hw_offset, 6, "CC state\n"); + return len; + case 0x7801: + if (len != 6) + DRM_ERROR("Bad count in 3DSTATE_BINDING_TABLE_POINTERS\n"); + if (count < 6) + BUFFER_FAIL(count, len, "3DSTATE_BINDING_TABLE_POINTERS"); + + instr_out(data, hw_offset, 0, + "3DSTATE_BINDING_TABLE_POINTERS\n"); + instr_out(data, hw_offset, 1, "VS binding table\n"); + instr_out(data, hw_offset, 2, "GS binding table\n"); + instr_out(data, hw_offset, 3, "Clip binding table\n"); + instr_out(data, hw_offset, 4, "SF binding table\n"); + instr_out(data, hw_offset, 5, "WM binding table\n"); + + return len; + + case 0x7900: + if (len != 4) + DRM_ERROR("Bad count in 3DSTATE_DRAWING_RECTANGLE\n"); + if (count < 4) + BUFFER_FAIL(count, len, "3DSTATE_DRAWING_RECTANGLE"); + + instr_out(data, hw_offset, 0, + "3DSTATE_DRAWING_RECTANGLE\n"); + instr_out(data, hw_offset, 1, "top left: %d,%d\n", + data[1] & 0xffff, + (data[1] >> 16) & 0xffff); + instr_out(data, hw_offset, 2, "bottom right: %d,%d\n", + data[2] & 0xffff, + (data[2] >> 16) & 0xffff); + instr_out(data, hw_offset, 3, "origin: %d,%d\n", + (int)data[3] & 0xffff, + ((int)data[3] >> 16) & 0xffff); + + return len; + + case 0x7905: + if (len != 5) + DRM_ERROR("Bad count in 3DSTATE_DEPTH_BUFFER\n"); + if (count < 5) + BUFFER_FAIL(count, len, "3DSTATE_DEPTH_BUFFER"); + + instr_out(data, hw_offset, 0, + "3DSTATE_DEPTH_BUFFER\n"); + instr_out(data, hw_offset, 1, "%s, %s, pitch = %d bytes, %stiled\n", + get_965_surfacetype(data[1] >> 29), + get_965_depthformat((data[1] >> 18) & 0x7), + (data[1] & 0x0001ffff) + 1, + data[1] & (1 << 27) ? "" : "not "); + instr_out(data, hw_offset, 2, "depth offset\n"); + instr_out(data, hw_offset, 3, "%dx%d\n", + ((data[3] & 0x0007ffc0) >> 6) + 1, + ((data[3] & 0xfff80000) >> 19) + 1); + instr_out(data, hw_offset, 4, "volume depth\n"); + + return len; + } + + for (opcode = 0; opcode < sizeof(opcodes_3d) / sizeof(opcodes_3d[0]); + opcode++) { + if ((data[0] & 0xffff0000) >> 16 == opcodes_3d[opcode].opcode) { + unsigned int i; + len = 1; + + instr_out(data, hw_offset, 0, "%s\n", opcodes_3d[opcode].name); + if (opcodes_3d[opcode].max_len > 1) { + len = (data[0] & 0xff) + 2; + if (len < opcodes_3d[opcode].min_len || + len > opcodes_3d[opcode].max_len) + { + DRM_ERROR("Bad count in %s\n", opcodes_3d[opcode].name); + } + } + + for (i = 1; i < len; i++) { + if (i >= count) + BUFFER_FAIL(count, len, opcodes_3d[opcode].name); + instr_out(data, hw_offset, i, "dword %d\n", i); + } + return len; + } + } + + instr_out(data, hw_offset, 0, "3D UNKNOWN\n"); + (*failures)++; + return 1; +} + + +static int +decode_3d_i830(uint32_t *data, int count, uint32_t hw_offset, int *failures) +{ + unsigned int opcode; + + struct { + uint32_t opcode; + int min_len; + int max_len; + char *name; + } opcodes_3d[] = { + { 0x02, 1, 1, "3DSTATE_MODES_3" }, + { 0x03, 1, 1, "3DSTATE_ENABLES_1"}, + { 0x04, 1, 1, "3DSTATE_ENABLES_2"}, + { 0x05, 1, 1, "3DSTATE_VFT0"}, + { 0x06, 1, 1, "3DSTATE_AA"}, + { 0x07, 1, 1, "3DSTATE_RASTERIZATION_RULES" }, + { 0x08, 1, 1, "3DSTATE_MODES_1" }, + { 0x09, 1, 1, "3DSTATE_STENCIL_TEST" }, + { 0x0a, 1, 1, "3DSTATE_VFT1"}, + { 0x0b, 1, 1, "3DSTATE_INDPT_ALPHA_BLEND" }, + { 0x0c, 1, 1, "3DSTATE_MODES_5" }, + { 0x0d, 1, 1, "3DSTATE_MAP_BLEND_OP" }, + { 0x0e, 1, 1, "3DSTATE_MAP_BLEND_ARG" }, + { 0x0f, 1, 1, "3DSTATE_MODES_2" }, + { 0x15, 1, 1, "3DSTATE_FOG_COLOR" }, + { 0x16, 1, 1, "3DSTATE_MODES_4" }, + }; + + switch ((data[0] & 0x1f000000) >> 24) { + case 0x1f: + return decode_3d_primitive(data, count, hw_offset, failures); + case 0x1d: + return decode_3d_1d(data, count, hw_offset, failures, 1); + case 0x1c: + return decode_3d_1c(data, count, hw_offset, failures); + } + + for (opcode = 0; opcode < sizeof(opcodes_3d) / sizeof(opcodes_3d[0]); + opcode++) { + if ((data[0] & 0x1f000000) >> 24 == opcodes_3d[opcode].opcode) { + unsigned int len = 1, i; + + instr_out(data, hw_offset, 0, "%s\n", opcodes_3d[opcode].name); + if (opcodes_3d[opcode].max_len > 1) { + len = (data[0] & 0xff) + 2; + if (len < opcodes_3d[opcode].min_len || + len > opcodes_3d[opcode].max_len) + { + DRM_ERROR("Bad count in %s\n", opcodes_3d[opcode].name); + } + } + + for (i = 1; i < len; i++) { + if (i >= count) + BUFFER_FAIL(count, len, opcodes_3d[opcode].name); + instr_out(data, hw_offset, i, "dword %d\n", i); + } + return len; + } + } + + instr_out(data, hw_offset, 0, "3D UNKNOWN\n"); + (*failures)++; + return 1; +} + +void i915_gem_command_decode(uint32_t *data, int count, uint32_t hw_offset, struct drm_device *dev) +{ + int index = 0; + int failures = 0; + + while (index < count) { + switch ((data[index] & 0xe0000000) >> 29) { + case 0x0: + index += decode_mi(data + index, count - index, + hw_offset + index * 4, &failures); + break; + case 0x2: + index += decode_2d(data + index, count - index, + hw_offset + index * 4, &failures); + break; + case 0x3: + if (IS_I965G(dev)) { + index += decode_3d_965(data + index, count - index, + hw_offset + index * 4, &failures); + } else if (IS_I9XX(dev)) { + index += decode_3d(data + index, count - index, + hw_offset + index * 4, &failures); + } else { + index += decode_3d_i830(data + index, count - index, + hw_offset + index * 4, &failures); + } + break; + default: + instr_out(data, hw_offset, index, "UNKNOWN\n"); + failures++; + index++; + break; + } + } +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/usr/src/uts/intel/io/drm/i915_gem_tiling.c Sat Dec 05 13:25:40 2009 +0800 @@ -0,0 +1,383 @@ +/* BEGIN CSTYLED */ + +/* + * Copyright (c) 2009, Intel Corporation. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Authors: + * Eric Anholt <eric@anholt.net> + * + */ + +/* + * Copyright 2009 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include "drmP.h" +#include "drm.h" +#include "i915_drm.h" +#include "i915_drv.h" + +/** @file i915_gem_tiling.c + * + * Support for managing tiling state of buffer objects. + * + * The idea behind tiling is to increase cache hit rates by rearranging + * pixel data so that a group of pixel accesses are in the same cacheline. + * Performance improvement from doing this on the back/depth buffer are on + * the order of 30%. + * + * Intel architectures make this somewhat more complicated, though, by + * adjustments made to addressing of data when the memory is in interleaved + * mode (matched pairs of DIMMS) to improve memory bandwidth. + * For interleaved memory, the CPU sends every sequential 64 bytes + * to an alternate memory channel so it can get the bandwidth from both. + * + * The GPU also rearranges its accesses for increased bandwidth to interleaved + * memory, and it matches what the CPU does for non-tiled. However, when tiled + * it does it a little differently, since one walks addresses not just in the + * X direction but also Y. So, along with alternating channels when bit + * 6 of the address flips, it also alternates when other bits flip -- Bits 9 + * (every 512 bytes, an X tile scanline) and 10 (every two X tile scanlines) + * are common to both the 915 and 965-class hardware. + * + * The CPU also sometimes XORs in higher bits as well, to improve + * bandwidth doing strided access like we do so frequently in graphics. This + * is called "Channel XOR Randomization" in the MCH documentation. The result + * is that the CPU is XORing in either bit 11 or bit 17 to bit 6 of its address + * decode. + * + * All of this bit 6 XORing has an effect on our memory management, + * as we need to make sure that the 3d driver can correctly address object + * contents. + * + * If we don't have interleaved memory, all tiling is safe and no swizzling is + * required. + * + * When bit 17 is XORed in, we simply refuse to tile at all. Bit + * 17 is not just a page offset, so as we page an objet out and back in, + * individual pages in it will have different bit 17 addresses, resulting in + * each 64 bytes being swapped with its neighbor! + * + * Otherwise, if interleaved, we have to tell the 3d driver what the address + * swizzling it needs to do is, since it's writing with the CPU to the pages + * (bit 6 and potentially bit 11 XORed in), and the GPU is reading from the + * pages (bit 6, 9, and 10 XORed in), resulting in a cumulative bit swizzling + * required by the CPU of XORing in bit 6, 9, 10, and potentially 11, in order + * to match what the GPU expects. + */ + +/** + * Detects bit 6 swizzling of address lookup between IGD access and CPU + * access through main memory. + */ +void +i915_gem_detect_bit_6_swizzle(struct drm_device *dev) +{ + drm_i915_private_t *dev_priv = dev->dev_private; + uint32_t swizzle_x = I915_BIT_6_SWIZZLE_UNKNOWN; + uint32_t swizzle_y = I915_BIT_6_SWIZZLE_UNKNOWN; + + if (!IS_I9XX(dev)) { + /* As far as we know, the 865 doesn't have these bit 6 + * swizzling issues. + */ + swizzle_x = I915_BIT_6_SWIZZLE_NONE; + swizzle_y = I915_BIT_6_SWIZZLE_NONE; + } else if (IS_MOBILE(dev)) { + uint32_t dcc; + + /* On mobile 9xx chipsets, channel interleave by the CPU is + * determined by DCC. For single-channel, neither the CPU + * nor the GPU do swizzling. For dual channel interleaved, + * the GPU's interleave is bit 9 and 10 for X tiled, and bit + * 9 for Y tiled. The CPU's interleave is independent, and + * can be based on either bit 11 (haven't seen this yet) or + * bit 17 (common). + */ + + dcc = I915_READ(DCC); + switch (dcc & DCC_ADDRESSING_MODE_MASK) { + case DCC_ADDRESSING_MODE_SINGLE_CHANNEL: + case DCC_ADDRESSING_MODE_DUAL_CHANNEL_ASYMMETRIC: + swizzle_x = I915_BIT_6_SWIZZLE_NONE; + swizzle_y = I915_BIT_6_SWIZZLE_NONE; + break; + case DCC_ADDRESSING_MODE_DUAL_CHANNEL_INTERLEAVED: + if (dcc & DCC_CHANNEL_XOR_DISABLE) { + /* This is the base swizzling by the GPU for + * tiled buffers. + */ + swizzle_x = I915_BIT_6_SWIZZLE_9_10; + swizzle_y = I915_BIT_6_SWIZZLE_9; + } else if ((dcc & DCC_CHANNEL_XOR_BIT_17) == 0) { + /* Bit 11 swizzling by the CPU in addition. */ + swizzle_x = I915_BIT_6_SWIZZLE_9_10_11; + swizzle_y = I915_BIT_6_SWIZZLE_9_11; + } else { + /* Bit 17 swizzling by the CPU in addition. */ + swizzle_x = I915_BIT_6_SWIZZLE_UNKNOWN; + swizzle_y = I915_BIT_6_SWIZZLE_UNKNOWN; + } + break; + } + if (dcc == 0xffffffff) { + DRM_ERROR("Couldn't read from MCHBAR. " + "Disabling tiling.\n"); + swizzle_x = I915_BIT_6_SWIZZLE_UNKNOWN; + swizzle_y = I915_BIT_6_SWIZZLE_UNKNOWN; + } + } else { + /* The 965, G33, and newer, have a very flexible memory + * configuration. It will enable dual-channel mode + * (interleaving) on as much memory as it can, and the GPU + * will additionally sometimes enable different bit 6 + * swizzling for tiled objects from the CPU. + * + * Here's what I found on the G965: + * slot fill memory size swizzling + * 0A 0B 1A 1B 1-ch 2-ch + * 512 0 0 0 512 0 O + * 512 0 512 0 16 1008 X + * 512 0 0 512 16 1008 X + * 0 512 0 512 16 1008 X + * 1024 1024 1024 0 2048 1024 O + * + * We could probably detect this based on either the DRB + * matching, which was the case for the swizzling required in + * the table above, or from the 1-ch value being less than + * the minimum size of a rank. + */ + if (I915_READ16(C0DRB3) != I915_READ16(C1DRB3)) { + swizzle_x = I915_BIT_6_SWIZZLE_NONE; + swizzle_y = I915_BIT_6_SWIZZLE_NONE; + } else { + swizzle_x = I915_BIT_6_SWIZZLE_9_10; + swizzle_y = I915_BIT_6_SWIZZLE_9; + } + } + + dev_priv->mm.bit_6_swizzle_x = swizzle_x; + dev_priv->mm.bit_6_swizzle_y = swizzle_y; +} + + +/** + * Returns the size of the fence for a tiled object of the given size. + */ +static int +i915_get_fence_size(struct drm_device *dev, int size) +{ + int i; + int start; + + if (IS_I965G(dev)) { + /* The 965 can have fences at any page boundary. */ + + return (size + PAGE_SIZE-1) & ~(PAGE_SIZE-1); + } else { + /* Align the size to a power of two greater than the smallest + * fence size. + */ + if (IS_I9XX(dev)) + start = 1024 * 1024; + else + start = 512 * 1024; + + for (i = start; i < size; i <<= 1) + ; + + return i; + } +} + +/* Check pitch constriants for all chips & tiling formats */ +static int +i915_tiling_ok(struct drm_device *dev, int stride, int size, int tiling_mode) +{ + int tile_width; + + /* Linear is always fine */ + if (tiling_mode == I915_TILING_NONE) + return 1; + + if (tiling_mode == I915_TILING_Y && HAS_128_BYTE_Y_TILING(dev)) + tile_width = 128; + else + tile_width = 512; + + if (stride == 0) + return 0; + + /* 965+ just needs multiples of tile width */ + if (IS_I965G(dev)) { + if (stride & (tile_width - 1)) + return 0; + return 1; + } + + /* Pre-965 needs power of two tile widths */ + if (stride < tile_width) + return 0; + + if (stride & (stride - 1)) + return 0; + + /* We don't handle the aperture area covered by the fence being bigger + * than the object size. + */ + if (i915_get_fence_size(dev, size) != size) + return 0; + + return 1; +} + +/** + * Sets the tiling mode of an object, returning the required swizzling of + * bit 6 of addresses in the object. + */ +/*ARGSUSED*/ +int +i915_gem_set_tiling(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + struct drm_i915_gem_set_tiling args; + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_gem_object *obj; + struct drm_i915_gem_object *obj_priv; + int ret; + + if (dev->driver->use_gem != 1) + return ENODEV; + + DRM_COPYFROM_WITH_RETURN(&args, + (struct drm_i915_gem_set_tiling __user *) data, sizeof(args)); + + obj = drm_gem_object_lookup(fpriv, args.handle); + if (obj == NULL) + return EINVAL; + obj_priv = obj->driver_private; + + if (!i915_tiling_ok(dev, args.stride, obj->size, args.tiling_mode)) { + drm_gem_object_unreference(obj); + DRM_DEBUG("i915 tiling is not OK"); + return EINVAL; + } + + spin_lock(&dev->struct_mutex); + + if (args.tiling_mode == I915_TILING_NONE) { + args.swizzle_mode = I915_BIT_6_SWIZZLE_NONE; + } else { + if (args.tiling_mode == I915_TILING_X) + args.swizzle_mode = dev_priv->mm.bit_6_swizzle_x; + else + args.swizzle_mode = dev_priv->mm.bit_6_swizzle_y; + /* If we can't handle the swizzling, make it untiled. */ + if (args.swizzle_mode == I915_BIT_6_SWIZZLE_UNKNOWN) { + args.tiling_mode = I915_TILING_NONE; + args.swizzle_mode = I915_BIT_6_SWIZZLE_NONE; + } + } + + if (args.tiling_mode != obj_priv->tiling_mode) { + int ret; + + /* Unbind the object, as switching tiling means we're + * switching the cache organization due to fencing, probably. + */ + ret = i915_gem_object_unbind(obj, 1); + if (ret != 0) { + args.tiling_mode = obj_priv->tiling_mode; + spin_unlock(&dev->struct_mutex); + drm_gem_object_unreference(obj); + DRM_ERROR("tiling switch!! unbind error %d", ret); + return ret; + } + obj_priv->tiling_mode = args.tiling_mode; + } + obj_priv->stride = args.stride; + + ret = DRM_COPY_TO_USER((struct drm_i915_gem_set_tiling __user *) data, &args, sizeof(args)); + if ( ret != 0) + DRM_ERROR(" gem set tiling error! %d", ret); + + drm_gem_object_unreference(obj); + spin_unlock(&dev->struct_mutex); + + return 0; +} + +/** + * Returns the current tiling mode and required bit 6 swizzling for the object. + */ +/*ARGSUSED*/ +int +i915_gem_get_tiling(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + struct drm_i915_gem_get_tiling args; + drm_i915_private_t *dev_priv = dev->dev_private; + struct drm_gem_object *obj; + struct drm_i915_gem_object *obj_priv; + int ret; + + if (dev->driver->use_gem != 1) + return ENODEV; + + DRM_COPYFROM_WITH_RETURN(&args, + (struct drm_i915_gem_get_tiling __user *) data, sizeof(args)); + + obj = drm_gem_object_lookup(fpriv, args.handle); + if (obj == NULL) + return EINVAL; + obj_priv = obj->driver_private; + + spin_lock(&dev->struct_mutex); + + args.tiling_mode = obj_priv->tiling_mode; + switch (obj_priv->tiling_mode) { + case I915_TILING_X: + args.swizzle_mode = dev_priv->mm.bit_6_swizzle_x; + break; + case I915_TILING_Y: + args.swizzle_mode = dev_priv->mm.bit_6_swizzle_y; + break; + case I915_TILING_NONE: + args.swizzle_mode = I915_BIT_6_SWIZZLE_NONE; + break; + default: + DRM_ERROR("unknown tiling mode\n"); + } + + + + ret = DRM_COPY_TO_USER((struct drm_i915_gem_get_tiling __user *) data, &args, sizeof(args)); + if ( ret != 0) + DRM_ERROR(" gem get tiling error! %d", ret); + + drm_gem_object_unreference(obj); + spin_unlock(&dev->struct_mutex); + + return 0; +}
--- a/usr/src/uts/intel/io/drm/i915_irq.c Fri Dec 04 16:46:47 2009 -0800 +++ b/usr/src/uts/intel/io/drm/i915_irq.c Sat Dec 05 13:25:40 2009 +0800 @@ -4,6 +4,7 @@ */ /* * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. + * Copyright (c) 2009, Intel Corporation. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -41,67 +42,79 @@ #define MAX_NOPID ((u32)~0) -/* - * These are the interrupts used by the driver +/** + * Interrupts that are always left unmasked. + * + * Since pipe events are edge-triggered from the PIPESTAT register to IIR, + * we leave them always unmasked in IMR and then control enabling them through + * PIPESTAT alone. */ -#define I915_INTERRUPT_ENABLE_MASK (I915_USER_INTERRUPT | \ - I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | \ - I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) + +#define I915_INTERRUPT_ENABLE_FIX (I915_ASLE_INTERRUPT | \ + I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | \ + I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | \ + I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT) + +/** Interrupts that we mask and unmask at runtime. */ +#define I915_INTERRUPT_ENABLE_VAR (I915_USER_INTERRUPT) + +/** These are all of the interrupts used by the driver */ +#define I915_INTERRUPT_ENABLE_MASK (I915_INTERRUPT_ENABLE_FIX | \ + I915_INTERRUPT_ENABLE_VAR) static inline void i915_enable_irq(drm_i915_private_t *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); - } + 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(drm_i915_private_t *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); + dev_priv->irq_mask_reg |= mask; + I915_WRITE(IMR, dev_priv->irq_mask_reg); + (void) I915_READ(IMR); + } +} + +static inline uint32_t +i915_pipestat(int pipe) +{ + if (pipe == 0) + return PIPEASTAT; + if (pipe == 1) + return PIPEBSTAT; + return 0; +} + +void +i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, uint32_t mask) +{ + if ((dev_priv->pipestat[pipe] & mask) != mask) { + u32 reg = i915_pipestat(pipe); + + dev_priv->pipestat[pipe] |= mask; + /* Enable the interrupt, clear any pending status */ + I915_WRITE(reg, dev_priv->pipestat[pipe] | (mask >> 16)); + (void) I915_READ(reg); } } - /** - * i915_get_pipe - return the the pipe associated with a given plane - * @dev: DRM device - * @plane: plane to look for - * - * The Intel Mesa & 2D drivers call the vblank routines with a plane number - * rather than a pipe number, since they may not always be equal. This routine - * maps the given @plane back to a pipe number. - */ -static int -i915_get_pipe(struct drm_device *dev, int plane) -{ - drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - u32 dspcntr; - - dspcntr = plane ? I915_READ(DSPBCNTR) : I915_READ(DSPACNTR); - return dspcntr & DISPPLANE_SEL_PIPE_MASK ? 1 : 0; -} +void +i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask) +{ + if ((dev_priv->pipestat[pipe] & mask) != 0) { + u32 reg = i915_pipestat(pipe); -/** - * i915_get_plane - return the the plane associated with a given pipe - * @dev: DRM device - * @pipe: pipe to look for - * - * The Intel Mesa & 2D drivers call the vblank routines with a plane number - * rather than a plane number, since they may not always be equal. This routine - * maps the given @pipe back to a plane number. - */ -static int -i915_get_plane(struct drm_device *dev, int pipe) -{ - if (i915_get_pipe(dev, 0) == pipe) - return 0; - return 1; + dev_priv->pipestat[pipe] &= ~mask; + I915_WRITE(reg, dev_priv->pipestat[pipe]); + (void) I915_READ(reg); + } } /** @@ -125,283 +138,18 @@ return 0; } -/** - * Emit a synchronous flip. - * - * This function must be called with the drawable spinlock held. - */ -static void -i915_dispatch_vsync_flip(struct drm_device *dev, struct drm_drawable_info *drw, - int plane) -{ - drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - drm_i915_sarea_t *sarea_priv = dev_priv->sarea_priv; - u16 x1, y1, x2, y2; - int pf_planes = 1 << plane; - - DRM_SPINLOCK_ASSERT(&dev->drw_lock); - - /* If the window is visible on the other plane, we have to flip on that - * plane as well. - */ - if (plane == 1) { - x1 = sarea_priv->planeA_x; - y1 = sarea_priv->planeA_y; - x2 = x1 + sarea_priv->planeA_w; - y2 = y1 + sarea_priv->planeA_h; - } else { - x1 = sarea_priv->planeB_x; - y1 = sarea_priv->planeB_y; - x2 = x1 + sarea_priv->planeB_w; - y2 = y1 + sarea_priv->planeB_h; - } - - if (x2 > 0 && y2 > 0) { - int i, num_rects = drw->num_rects; - struct drm_clip_rect *rect = drw->rects; - - for (i = 0; i < num_rects; i++) - if (!(rect[i].x1 >= x2 || rect[i].y1 >= y2 || - rect[i].x2 <= x1 || rect[i].y2 <= y1)) { - pf_planes = 0x3; - - break; - } - } - - i915_dispatch_flip(dev, pf_planes, 1); -} - -/** - * Emit blits for scheduled buffer swaps. - * - * This function will be called with the HW lock held. - */ -static void i915_vblank_tasklet(drm_device_t *dev) -{ - drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - struct list_head *list, *tmp, hits, *hit; - int nhits, slice[2], upper[2], lower[2], i, num_pages; - unsigned counter[2]; - struct drm_drawable_info *drw; - drm_i915_sarea_t *sarea_priv = dev_priv->sarea_priv; - u32 cpp = dev_priv->cpp, offsets[3]; - u32 cmd = (cpp == 4) ? (XY_SRC_COPY_BLT_CMD | - XY_SRC_COPY_BLT_WRITE_ALPHA | - XY_SRC_COPY_BLT_WRITE_RGB) - : XY_SRC_COPY_BLT_CMD; - u32 src_pitch = sarea_priv->pitch * cpp; - u32 dst_pitch = sarea_priv->pitch * cpp; - /* COPY rop (0xcc), map cpp to magic color depth constants */ - u32 ropcpp = (0xcc << 16) | ((cpp - 1) << 24); - RING_LOCALS; - - if (IS_I965G(dev) && sarea_priv->front_tiled) { - cmd |= XY_SRC_COPY_BLT_DST_TILED; - dst_pitch >>= 2; - } - if (IS_I965G(dev) && sarea_priv->back_tiled) { - cmd |= XY_SRC_COPY_BLT_SRC_TILED; - src_pitch >>= 2; - } - - counter[0] = drm_vblank_count(dev, 0); - counter[1] = drm_vblank_count(dev, 1); - - INIT_LIST_HEAD(&hits); - - nhits = 0; - - /* No irqsave/restore necessary. This tasklet may be run in an - * interrupt context or normal context, but we don't have to worry - * about getting interrupted by something acquiring the lock, because - * we are the interrupt context thing that acquires the lock. - */ - DRM_SPINLOCK(&dev_priv->swaps_lock); - - /* Find buffer swaps scheduled for this vertical blank */ - list_for_each_safe(list, tmp, &dev_priv->vbl_swaps.head) { - drm_i915_vbl_swap_t *vbl_swap = - list_entry(list, drm_i915_vbl_swap_t, head); - int pipe = i915_get_pipe(dev, vbl_swap->plane); - - if ((counter[pipe] - vbl_swap->sequence) > (1<<23)) - continue; - - list_del(list); - dev_priv->swaps_pending--; - drm_vblank_put(dev, pipe); - - DRM_SPINUNLOCK(&dev_priv->swaps_lock); - DRM_SPINLOCK(&dev->drw_lock); - - drw = drm_get_drawable_info(dev, vbl_swap->drw_id); - - if (!drw) { - DRM_SPINUNLOCK(&dev->drw_lock); - drm_free(vbl_swap, sizeof(*vbl_swap), DRM_MEM_DRIVER); - DRM_SPINLOCK(&dev_priv->swaps_lock); - continue; - } - - list_for_each(hit, &hits) { - drm_i915_vbl_swap_t *swap_cmp = - list_entry(hit, drm_i915_vbl_swap_t, head); - struct drm_drawable_info *drw_cmp = - drm_get_drawable_info(dev, swap_cmp->drw_id); - - if (drw_cmp && - drw_cmp->rects[0].y1 > drw->rects[0].y1) { - list_add_tail(list, hit); - break; - } - } - - DRM_SPINUNLOCK(&dev->drw_lock); - - /* List of hits was empty, or we reached the end of it */ - if (hit == &hits) - list_add_tail(list, hits.prev); - - nhits++; - - DRM_SPINLOCK(&dev_priv->swaps_lock); - } - - DRM_SPINUNLOCK(&dev_priv->swaps_lock); - - if (nhits == 0) { - return; - } - - i915_kernel_lost_context(dev); - - upper[0] = upper[1] = 0; - slice[0] = max(sarea_priv->planeA_h / nhits, 1); - slice[1] = max(sarea_priv->planeB_h / nhits, 1); - lower[0] = sarea_priv->planeA_y + slice[0]; - lower[1] = sarea_priv->planeB_y + slice[0]; - - offsets[0] = sarea_priv->front_offset; - offsets[1] = sarea_priv->back_offset; - offsets[2] = sarea_priv->third_offset; - num_pages = sarea_priv->third_handle ? 3 : 2; - - DRM_SPINLOCK(&dev->drw_lock); - - /* Emit blits for buffer swaps, partitioning both outputs into as many - * slices as there are buffer swaps scheduled in order to avoid tearing - * (based on the assumption that a single buffer swap would always - * complete before scanout starts). - */ - for (i = 0; i++ < nhits; - upper[0] = lower[0], lower[0] += slice[0], - upper[1] = lower[1], lower[1] += slice[1]) { - int init_drawrect = 1; - - if (i == nhits) - lower[0] = lower[1] = sarea_priv->height; - - list_for_each(hit, &hits) { - drm_i915_vbl_swap_t *swap_hit = - list_entry(hit, drm_i915_vbl_swap_t, head); - struct drm_clip_rect *rect; - int num_rects, plane, front, back; - unsigned short top, bottom; - - drw = drm_get_drawable_info(dev, swap_hit->drw_id); - - if (!drw) - continue; - - plane = swap_hit->plane; - - if (swap_hit->flip) { - i915_dispatch_vsync_flip(dev, drw, plane); - continue; - } - - if (init_drawrect) { - int width = sarea_priv->width; - int height = sarea_priv->height; - if (IS_I965G(dev)) { - BEGIN_LP_RING(4); - OUT_RING(GFX_OP_DRAWRECT_INFO_I965); - OUT_RING(0); - OUT_RING(((width - 1) & 0xffff) | ((height - 1) << 16)); - OUT_RING(0); - ADVANCE_LP_RING(); - } else { - BEGIN_LP_RING(6); - OUT_RING(GFX_OP_DRAWRECT_INFO); - OUT_RING(0); - OUT_RING(0); - OUT_RING(((width - 1) & 0xffff) | ((height - 1) << 16)); - OUT_RING(0); - OUT_RING(0); - ADVANCE_LP_RING(); - } - - sarea_priv->ctxOwner = DRM_KERNEL_CONTEXT; - - init_drawrect = 0; - } - - rect = drw->rects; - top = upper[plane]; - bottom = lower[plane]; - - front = (dev_priv->sarea_priv->pf_current_page >> - (2 * plane)) & 0x3; - back = (front + 1) % num_pages; - - for (num_rects = drw->num_rects; num_rects--; rect++) { - int y1 = max(rect->y1, top); - int y2 = min(rect->y2, bottom); - - if (y1 >= y2) - continue; - - BEGIN_LP_RING(8); - OUT_RING(cmd); - OUT_RING(ropcpp | dst_pitch); - OUT_RING((y1 << 16) | rect->x1); - OUT_RING((y2 << 16) | rect->x2); - OUT_RING(offsets[front]); - OUT_RING((y1 << 16) | rect->x1); - OUT_RING(src_pitch); - OUT_RING(offsets[back]); - ADVANCE_LP_RING(); - } - } - } - - DRM_SPINUNLOCK(&dev->drw_lock); - - list_for_each_safe(hit, tmp, &hits) { - drm_i915_vbl_swap_t *swap_hit = - list_entry(hit, drm_i915_vbl_swap_t, head); - - list_del(hit); - - drm_free(swap_hit, sizeof(*swap_hit), DRM_MEM_DRIVER); - } -} - -u32 i915_get_vblank_counter(struct drm_device *dev, int plane) +u32 i915_get_vblank_counter(struct drm_device *dev, int pipe) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; unsigned long high_frame; unsigned long low_frame; u32 high1, high2, low, count; - int pipe; - pipe = i915_get_pipe(dev, plane); high_frame = pipe ? PIPEBFRAMEHIGH : PIPEAFRAMEHIGH; low_frame = pipe ? PIPEBFRAMEPIXEL : PIPEAFRAMEPIXEL; if (!i915_pipe_enabled(dev, pipe)) { - DRM_DEBUG("trying to get vblank count for disabled pipe %d\n", pipe); + DRM_ERROR("trying to get vblank count for disabled pipe %d\n", pipe); return 0; } @@ -421,15 +169,211 @@ count = (high1 << 8) | low; - /* count may be reset by other driver(e.g. 2D driver), - we have no way to know if it is wrapped or resetted - when count is zero. do a rough guess. - */ - if (count < dev->last_vblank[pipe] && dev->last_vblank[pipe] < dev->max_vblank_count/2) - dev->last_vblank[pipe]=0; return count; } +/** + * i915_capture_error_state - capture an error record for later analysis + * @dev: drm device + * + * Should be called when an error is detected (either a hang or an error + * interrupt) to capture error state from the time of the error. Fills + * out a structure which becomes available in debugfs for user level tools + * to pick up. + */ +static void i915_capture_error_state(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + struct drm_i915_error_state *error; + + spin_lock_irqsave(&dev_priv->error_lock, flags); +#if 0 + if (dev_priv->first_error) + goto out; +#endif + error = drm_alloc(sizeof(*error), DRM_MEM_DRIVER); + if (!error) { + DRM_DEBUG("out ot memory, not capturing error state\n"); + goto out; + } + + error->eir = I915_READ(EIR); + error->pgtbl_er = I915_READ(PGTBL_ER); + error->pipeastat = I915_READ(PIPEASTAT); + error->pipebstat = I915_READ(PIPEBSTAT); + error->instpm = I915_READ(INSTPM); + if (!IS_I965G(dev)) { + error->ipeir = I915_READ(IPEIR); + error->ipehr = I915_READ(IPEHR); + error->instdone = I915_READ(INSTDONE); + error->acthd = I915_READ(ACTHD); + } else { + error->ipeir = I915_READ(IPEIR_I965); + error->ipehr = I915_READ(IPEHR_I965); + error->instdone = I915_READ(INSTDONE_I965); + error->instps = I915_READ(INSTPS); + error->instdone1 = I915_READ(INSTDONE1); + error->acthd = I915_READ(ACTHD_I965); + } + + (void) uniqtime(&error->time); + + dev_priv->first_error = error; + + DRM_DEBUG("Time: %ld s %ld us\n", error->time.tv_sec, + error->time.tv_usec); + DRM_DEBUG("EIR: 0x%08x\n", error->eir); + DRM_DEBUG(" PGTBL_ER: 0x%08x\n", error->pgtbl_er); + DRM_DEBUG(" INSTPM: 0x%08x\n", error->instpm); + DRM_DEBUG(" IPEIR: 0x%08x\n", error->ipeir); + DRM_DEBUG(" IPEHR: 0x%08x\n", error->ipehr); + DRM_DEBUG(" INSTDONE: 0x%08x\n", error->instdone); + DRM_DEBUG(" ACTHD: 0x%08x\n", error->acthd); + DRM_DEBUG(" DMA_FADD_P: 0x%08x\n", I915_READ(0x2078)); + if (IS_I965G(dev)) { + DRM_DEBUG(" INSTPS: 0x%08x\n", error->instps); + DRM_DEBUG(" INSTDONE1: 0x%08x\n", error->instdone1); + } + drm_free(error, sizeof(*error), DRM_MEM_DRIVER); +out: + spin_unlock_irqrestore(&dev_priv->error_lock, flags); +} + +/** + * i915_handle_error - handle an error interrupt + * @dev: drm device + * + * Do some basic checking of regsiter state at error interrupt time and + * dump it to the syslog. Also call i915_capture_error_state() to make + * sure we get a record and make it available in debugfs. Fire a uevent + * so userspace knows something bad happened (should trigger collection + * of a ring dump etc.). + */ +void i915_handle_error(struct drm_device *dev) +{ + struct drm_i915_private *dev_priv = dev->dev_private; + u32 eir = I915_READ(EIR); + u32 pipea_stats = I915_READ(PIPEASTAT); + u32 pipeb_stats = I915_READ(PIPEBSTAT); + + i915_capture_error_state(dev); + + DRM_DEBUG("render error detected, EIR: 0x%08x\n", + eir); + + if (IS_G4X(dev)) { + if (eir & (GM45_ERROR_MEM_PRIV | GM45_ERROR_CP_PRIV)) { + u32 ipeir = I915_READ(IPEIR_I965); + + DRM_DEBUG(" IPEIR: 0x%08x\n", + I915_READ(IPEIR_I965)); + DRM_DEBUG(" IPEHR: 0x%08x\n", + I915_READ(IPEHR_I965)); + DRM_DEBUG(" INSTDONE: 0x%08x\n", + I915_READ(INSTDONE_I965)); + DRM_DEBUG(" INSTPS: 0x%08x\n", + I915_READ(INSTPS)); + DRM_DEBUG(" INSTDONE1: 0x%08x\n", + I915_READ(INSTDONE1)); + DRM_DEBUG(" ACTHD: 0x%08x\n", + I915_READ(ACTHD_I965)); + I915_WRITE(IPEIR_I965, ipeir); + (void)I915_READ(IPEIR_I965); + } + if (eir & GM45_ERROR_PAGE_TABLE) { + u32 pgtbl_err = I915_READ(PGTBL_ER); + DRM_DEBUG("page table error\n"); + DRM_DEBUG(" PGTBL_ER: 0x%08x\n", + pgtbl_err); + I915_WRITE(PGTBL_ER, pgtbl_err); + (void)I915_READ(PGTBL_ER); + } + } + + if (IS_I9XX(dev)) { + if (eir & I915_ERROR_PAGE_TABLE) { + u32 pgtbl_err = I915_READ(PGTBL_ER); + DRM_DEBUG("page table error\n"); + DRM_DEBUG("PGTBL_ER: 0x%08x\n", + pgtbl_err); + I915_WRITE(PGTBL_ER, pgtbl_err); + (void)I915_READ(PGTBL_ER); + } + } + + if (eir & I915_ERROR_MEMORY_REFRESH) { + DRM_DEBUG("memory refresh error\n"); + DRM_DEBUG("PIPEASTAT: 0x%08x\n", + pipea_stats); + DRM_DEBUG("PIPEBSTAT: 0x%08x\n", + pipeb_stats); + /* pipestat has already been acked */ + } + if (eir & I915_ERROR_INSTRUCTION) { + DRM_DEBUG("instruction error\n"); + DRM_DEBUG(" INSTPM: 0x%08x\n", + I915_READ(INSTPM)); + if (!IS_I965G(dev)) { + u32 ipeir = I915_READ(IPEIR); + + DRM_DEBUG(" IPEIR: 0x%08x\n", + I915_READ(IPEIR)); + DRM_DEBUG(" IPEHR: 0x%08x\n", + I915_READ(IPEHR)); + DRM_DEBUG(" INSTDONE: 0x%08x\n", + I915_READ(INSTDONE)); + DRM_DEBUG(" ACTHD: 0x%08x\n", + I915_READ(ACTHD)); + I915_WRITE(IPEIR, ipeir); + (void)I915_READ(IPEIR); + } else { + u32 ipeir = I915_READ(IPEIR_I965); + + DRM_DEBUG(" IPEIR: 0x%08x\n", + I915_READ(IPEIR_I965)); + DRM_DEBUG(" IPEHR: 0x%08x\n", + I915_READ(IPEHR_I965)); + DRM_DEBUG(" INSTDONE: 0x%08x\n", + I915_READ(INSTDONE_I965)); + DRM_DEBUG(" INSTPS: 0x%08x\n", + I915_READ(INSTPS)); + DRM_DEBUG(" INSTDONE1: 0x%08x\n", + I915_READ(INSTDONE1)); + DRM_DEBUG(" ACTHD: 0x%08x\n", + I915_READ(ACTHD_I965)); + I915_WRITE(IPEIR_I965, ipeir); + (void)I915_READ(IPEIR_I965); + } + } + + I915_WRITE(EIR, eir); + (void)I915_READ(EIR); + eir = I915_READ(EIR); + if (eir) { + /* + * some errors might have become stuck, + * mask them. + */ + DRM_DEBUG("EIR stuck: 0x%08x, masking\n", eir); + I915_WRITE(EMR, I915_READ(EMR) | eir); + I915_WRITE(IIR, I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT); + } + +} + +u32 gm45_get_vblank_counter(struct drm_device *dev, int pipe) +{ + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; + int reg = pipe ? PIPEB_FRMCOUNT_GM45 : PIPEA_FRMCOUNT_GM45; + + if (!i915_pipe_enabled(dev, pipe)) { + DRM_ERROR("trying to get vblank count for disabled pipe %d\n", pipe); + return 0; + } + + return I915_READ(reg); +} + irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS) { drm_device_t *dev = (drm_device_t *) (void *) arg; @@ -437,69 +381,67 @@ u32 iir; u32 pipea_stats = 0, pipeb_stats = 0; int vblank = 0; + iir = I915_READ(IIR); - atomic_inc(&dev_priv->irq_received); - if (iir == 0) { return IRQ_NONE; } - - if (iir & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT) { - pipea_stats = I915_READ(PIPEASTAT); +start: - /* The vblank interrupt gets enabled even if we didn't ask for - it, so make sure it's shut down again */ - if (!(dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_A)) - pipea_stats &= ~(PIPE_START_VBLANK_INTERRUPT_ENABLE | - PIPE_VBLANK_INTERRUPT_ENABLE); - else 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 (dev_priv->sarea_priv) { + if (dev_priv->hw_status_page) + dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); } - if (iir & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) { - pipeb_stats = I915_READ(PIPEBSTAT); - - /* The vblank interrupt gets enabled even if we didn't ask for - it, so make sure it's shut down again */ - if (!(dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B)) - pipeb_stats &= ~(PIPE_START_VBLANK_INTERRUPT_ENABLE | - PIPE_VBLANK_INTERRUPT_ENABLE); - else 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); - } - - if (dev_priv->sarea_priv) - dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); I915_WRITE(IIR, iir); (void) I915_READ(IIR); /* Flush posted writes */ + + if (iir & I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT) + i915_handle_error(dev); + 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 (vblank) { - if (dev_priv->swaps_pending > 0) - drm_locked_tasklet(dev, i915_vblank_tasklet); - } + if (iir & I915_DISPLAY_PIPE_A_EVENT_INTERRUPT) { + pipea_stats = I915_READ(PIPEASTAT); + + /* The vblank interrupt gets enabled even if we didn't ask for + it, so make sure it's shut down again */ + if (!(dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_A)) + pipea_stats &= ~(PIPE_START_VBLANK_INTERRUPT_ENABLE | + PIPE_VBLANK_INTERRUPT_ENABLE); + else if (pipea_stats & (PIPE_START_VBLANK_INTERRUPT_STATUS| + PIPE_VBLANK_INTERRUPT_STATUS)) + { + vblank++; + drm_handle_vblank(dev, 0); + } - return IRQ_HANDLED; + I915_WRITE(PIPEASTAT, pipea_stats); + } + if (iir & I915_DISPLAY_PIPE_B_EVENT_INTERRUPT) { + pipeb_stats = I915_READ(PIPEBSTAT); + + /* The vblank interrupt gets enabled even if we didn't ask for + it, so make sure it's shut down again */ + if (!(dev_priv->vblank_pipe & DRM_I915_VBLANK_PIPE_B)) + pipeb_stats &= ~(PIPE_START_VBLANK_INTERRUPT_ENABLE | + PIPE_VBLANK_INTERRUPT_ENABLE); + else if (pipeb_stats & (PIPE_START_VBLANK_INTERRUPT_STATUS| + PIPE_VBLANK_INTERRUPT_STATUS)) + { + vblank++; + drm_handle_vblank(dev, 1); + } + + I915_WRITE(PIPEBSTAT, pipeb_stats); + } + return IRQ_HANDLED; } @@ -510,31 +452,71 @@ RING_LOCALS; i915_kernel_lost_context(dev); - - i915_emit_breadcrumb(dev); + + dev_priv->counter++; + if (dev_priv->counter > 0x7FFFFFFFUL) + dev_priv->counter = 1; + if (dev_priv->sarea_priv) + dev_priv->sarea_priv->last_enqueue = dev_priv->counter; - BEGIN_LP_RING(2); - OUT_RING(0); +#if defined(__i386) + if (IS_GM45(dev)) { + BEGIN_LP_RING(3); + OUT_RING(MI_STORE_DWORD_INDEX); + OUT_RING(I915_BREADCRUMB_INDEX << MI_STORE_DWORD_INDEX_SHIFT); + OUT_RING(dev_priv->counter); + ADVANCE_LP_RING(); + + (void) READ_BREADCRUMB(dev_priv); + BEGIN_LP_RING(2); + OUT_RING(0); + OUT_RING(MI_USER_INTERRUPT); + ADVANCE_LP_RING(); + } else { +#endif /* __i386 */ + BEGIN_LP_RING(4); + OUT_RING(MI_STORE_DWORD_INDEX); + OUT_RING(I915_BREADCRUMB_INDEX << MI_STORE_DWORD_INDEX_SHIFT); + OUT_RING(dev_priv->counter); OUT_RING(MI_USER_INTERRUPT); ADVANCE_LP_RING(); +#if defined(__i386) + } +#endif /* __i386 */ + +#if defined(__i386) + if (IS_I965GM(dev) || IS_GM45(dev)) +#else + if (IS_I965GM(dev)) +#endif /* __i386 */ + { + (void) READ_BREADCRUMB(dev_priv); + BEGIN_LP_RING(2); + OUT_RING(0); + OUT_RING(0); + ADVANCE_LP_RING(); + (void) READ_BREADCRUMB(dev_priv); + } return dev_priv->counter; } -void i915_user_irq_on(drm_i915_private_t *dev_priv) +void i915_user_irq_on(struct drm_device *dev) { + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; spin_lock(&dev_priv->user_irq_lock); - if (dev_priv->irq_enabled && (++dev_priv->user_irq_refcount == 1)){ + if (dev->irq_enabled && (++dev_priv->user_irq_refcount == 1)){ i915_enable_irq(dev_priv, I915_USER_INTERRUPT); } spin_unlock(&dev_priv->user_irq_lock); } -void i915_user_irq_off(drm_i915_private_t *dev_priv) +void i915_user_irq_off(struct drm_device *dev) { + drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; spin_lock(&dev_priv->user_irq_lock); - if (dev_priv->irq_enabled && (--dev_priv->user_irq_refcount == 0)) { + if (dev->irq_enabled && (--dev_priv->user_irq_refcount == 0)) { i915_disable_irq(dev_priv, I915_USER_INTERRUPT); } spin_unlock(&dev_priv->user_irq_lock); @@ -546,30 +528,25 @@ drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; int ret = 0; - - - if (!dev_priv) { - DRM_ERROR("called with no initialization\n"); - return -EINVAL; - } - - - + if (!dev_priv) { + DRM_ERROR("called with no initialization\n"); + return -EINVAL; + } if (READ_BREADCRUMB(dev_priv) >= irq_nr) { - if (dev_priv->sarea_priv) + if (dev_priv->sarea_priv) { dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); + } return 0; } - DRM_DEBUG("i915_wait_irq: irq_nr=%d breadcrumb=%d\n", irq_nr, READ_BREADCRUMB(dev_priv)); - i915_user_irq_on(dev_priv); + i915_user_irq_on(dev); DRM_WAIT_ON(ret, &dev_priv->irq_queue, 3 * DRM_HZ, READ_BREADCRUMB(dev_priv) >= irq_nr); - i915_user_irq_off(dev_priv); + i915_user_irq_off(dev); - if (ret == EBUSY || ret == EINTR) { + if (ret == EBUSY) { DRM_DEBUG("%d: EBUSY -- rec: %d emitted: %d\n", ret, READ_BREADCRUMB(dev_priv), (int)dev_priv->counter); @@ -599,6 +576,7 @@ return (EINVAL); } + if (ddi_model_convert_from(mode & FMODELS) == DDI_MODEL_ILP32) { drm_i915_irq_emit32_t irq_emit32; @@ -610,7 +588,9 @@ DRM_COPYFROM_WITH_RETURN(&emit, (drm_i915_irq_emit_t __user *) data, sizeof(emit)); + spin_lock(&dev->struct_mutex); result = i915_emit_irq(dev); + spin_unlock(&dev->struct_mutex); if (DRM_COPY_TO_USER(emit.irq_seq, &result, sizeof(int))) { DRM_ERROR("copy_to_user\n"); @@ -640,107 +620,37 @@ return i915_wait_irq(dev, irqwait.irq_seq); } -int i915_enable_vblank(struct drm_device *dev, int plane) +int i915_enable_vblank(struct drm_device *dev, int pipe) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - int pipe = i915_get_pipe(dev, plane); - u32 pipestat_reg = 0; - u32 mask_reg = 0; - u32 pipestat; + int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF; + u32 pipeconf; - switch (pipe) { - case 0: - pipestat_reg = PIPEASTAT; - mask_reg |= I915_DISPLAY_PIPE_A_EVENT_INTERRUPT; - break; - case 1: - pipestat_reg = PIPEBSTAT; - mask_reg |= I915_DISPLAY_PIPE_B_EVENT_INTERRUPT; - break; - default: - DRM_ERROR("tried to enable vblank on non-existent pipe %d\n", - pipe); - break; - } + pipeconf = I915_READ(pipeconf_reg); + if (!(pipeconf & PIPEACONF_ENABLE)) + return -EINVAL; - if (pipestat_reg) - { - pipestat = I915_READ (pipestat_reg); - /* - * Older chips didn't have the start vblank interrupt, - * but - */ - if (IS_I965G (dev)) - pipestat |= PIPE_START_VBLANK_INTERRUPT_ENABLE; - else - pipestat |= PIPE_VBLANK_INTERRUPT_ENABLE; - /* - * Clear any pending status - */ - pipestat |= (PIPE_START_VBLANK_INTERRUPT_STATUS | - PIPE_VBLANK_INTERRUPT_STATUS); - I915_WRITE(pipestat_reg, pipestat); - } - DRM_SPINLOCK(&dev_priv->user_irq_lock); - i915_enable_irq(dev_priv, mask_reg); - DRM_SPINUNLOCK(&dev_priv->user_irq_lock); + spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); + if (IS_I965G(dev)) + i915_enable_pipestat(dev_priv, pipe, + PIPE_START_VBLANK_INTERRUPT_ENABLE); + else + i915_enable_pipestat(dev_priv, pipe, + PIPE_VBLANK_INTERRUPT_ENABLE); + spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags); return 0; } -void i915_disable_vblank(struct drm_device *dev, int plane) -{ - drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - int pipe = i915_get_pipe(dev, plane); - u32 pipestat_reg = 0; - u32 mask_reg = 0; - u32 pipestat; - - switch (pipe) { - case 0: - pipestat_reg = PIPEASTAT; - mask_reg |= I915_DISPLAY_PIPE_A_EVENT_INTERRUPT; - break; - case 1: - pipestat_reg = PIPEBSTAT; - mask_reg |= I915_DISPLAY_PIPE_B_EVENT_INTERRUPT; - break; - default: - DRM_ERROR("tried to disable vblank on non-existent pipe %d\n", - pipe); - break; - } - - DRM_SPINLOCK(&dev_priv->user_irq_lock); - i915_disable_irq(dev_priv, mask_reg); - DRM_SPINUNLOCK(&dev_priv->user_irq_lock); - - if (pipestat_reg) - { - pipestat = I915_READ (pipestat_reg); - pipestat &= ~(PIPE_START_VBLANK_INTERRUPT_ENABLE | - PIPE_VBLANK_INTERRUPT_ENABLE); - /* - * Clear any pending status - */ - pipestat |= (PIPE_START_VBLANK_INTERRUPT_STATUS | - PIPE_VBLANK_INTERRUPT_STATUS); - I915_WRITE(pipestat_reg, pipestat); - (void) I915_READ(pipestat_reg); - } -} - - -static void i915_enable_interrupt (drm_device_t *dev) +void i915_disable_vblank(struct drm_device *dev, int pipe) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - dev_priv->irq_mask_reg = 0xffffffff; - I915_WRITE(IMR, dev_priv->irq_mask_reg); - I915_WRITE(IER, I915_INTERRUPT_ENABLE_MASK); - (void) I915_READ (IER); - - dev_priv->irq_enabled = 1; + spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags); + i915_disable_pipestat(dev_priv, pipe, + PIPE_VBLANK_INTERRUPT_ENABLE | + PIPE_START_VBLANK_INTERRUPT_ENABLE); + spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags); } /* Set the vblank monitor pipe @@ -771,7 +681,6 @@ return -EINVAL; } - DRM_COPYFROM_WITH_RETURN(&pipe, (drm_i915_vblank_pipe_t __user *)data, sizeof (pipe)); pipe.pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; @@ -785,159 +694,22 @@ /*ARGSUSED*/ int i915_vblank_swap(DRM_IOCTL_ARGS) { - DRM_DEVICE; - drm_i915_private_t *dev_priv = dev->dev_private; - drm_i915_vblank_swap_t *swap; - drm_i915_vbl_swap_t *vbl_swap; - unsigned int pipe, seqtype, curseq, plane; - struct list_head *list; - int ret; - - if (!dev_priv) { - DRM_ERROR("%s called with no initialization\n", __func__); - return -EINVAL; - } - - if (!dev_priv->sarea_priv || dev_priv->sarea_priv->rotation) { - DRM_DEBUG("Rotation not supported\n"); - return -EINVAL; - } - - DRM_COPYFROM_WITH_RETURN(&swap, (drm_i915_vblank_swap_t __user *)data, sizeof (swap)); - - if (swap->seqtype & ~(_DRM_VBLANK_RELATIVE | _DRM_VBLANK_ABSOLUTE | - _DRM_VBLANK_SECONDARY | _DRM_VBLANK_NEXTONMISS | - _DRM_VBLANK_FLIP)) { - DRM_ERROR("Invalid sequence type 0x%x\n", swap->seqtype); - return -EINVAL; - } - - plane = (swap->seqtype & _DRM_VBLANK_SECONDARY) ? 1 : 0; - pipe = i915_get_pipe(dev, plane); - - seqtype = swap->seqtype & (_DRM_VBLANK_RELATIVE | _DRM_VBLANK_ABSOLUTE); - - if (!(dev_priv->vblank_pipe & (1 << pipe))) { - DRM_ERROR("Invalid pipe %d\n", pipe); - return -EINVAL; - } - - spin_lock_irqsave(&dev->drw_lock, irqflags); - - /* It makes no sense to schedule a swap for a drawable that doesn't have - * valid information at this point. E.g. this could mean that the X - * server is too old to push drawable information to the DRM, in which - * case all such swaps would become ineffective. - */ - if (!drm_get_drawable_info(dev, swap->drawable)) { - spin_unlock_irqrestore(&dev->drw_lock, irqflags); - DRM_DEBUG("Invalid drawable ID %d\n", swap->drawable); - return -EINVAL; - } - - spin_unlock_irqrestore(&dev->drw_lock, irqflags); - - /* - * We take the ref here and put it when the swap actually completes - * in the tasklet. - */ - ret = drm_vblank_get(dev, pipe); - if (ret) - return ret; - curseq = drm_vblank_count(dev, pipe); - - if (seqtype == _DRM_VBLANK_RELATIVE) - swap->sequence += curseq; - - if ((curseq - swap->sequence) <= (1<<23)) { - if (swap->seqtype & _DRM_VBLANK_NEXTONMISS) { - swap->sequence = curseq + 1; - } else { - DRM_DEBUG("Missed target sequence\n"); - drm_vblank_put(dev, pipe); - return -EINVAL; - } - } - - if (swap->seqtype & _DRM_VBLANK_FLIP) { - swap->sequence--; + /* The delayed swap mechanism was fundamentally racy, and has been + * removed. The model was that the client requested a delayed flip/swap + * from the kernel, then waited for vblank before continuing to perform + * rendering. The problem was that the kernel might wake the client + * up before it dispatched the vblank swap (since the lock has to be + * held while touching the ringbuffer), in which case the client would + * clear and start the next frame before the swap occurred, and + * flicker would occur in addition to likely missing the vblank. + * + * In the absence of this ioctl, userland falls back to a correct path + * of waiting for a vblank, then dispatching the swap on its own. + * Context switching to userland and back is plenty fast enough for + * meeting the requirements of vblank swapping. + */ + return -EINVAL; - if ((curseq - swap->sequence) <= (1<<23)) { - struct drm_drawable_info *drw; - - LOCK_TEST_WITH_RETURN(dev, fpriv); - - spin_lock_irqsave(&dev->drw_lock, irqflags); - - drw = drm_get_drawable_info(dev, swap->drawable); - - if (!drw) { - spin_unlock_irqrestore(&dev->drw_lock, - irqflags); - DRM_DEBUG("Invalid drawable ID %d\n", - swap->drawable); - drm_vblank_put(dev, pipe); - return -EINVAL; - } - - i915_dispatch_vsync_flip(dev, drw, plane); - - spin_unlock_irqrestore(&dev->drw_lock, irqflags); - - drm_vblank_put(dev, pipe); - return 0; - } - } - - spin_lock_irqsave(&dev_priv->swaps_lock, irqflags); - - list_for_each(list, &dev_priv->vbl_swaps.head) { - vbl_swap = list_entry(list, drm_i915_vbl_swap_t, head); - - if (vbl_swap->drw_id == swap->drawable && - vbl_swap->plane == plane && - vbl_swap->sequence == swap->sequence) { - vbl_swap->flip = (swap->seqtype & _DRM_VBLANK_FLIP); - spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); - DRM_DEBUG("Already scheduled\n"); - return 0; - } - } - - spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); - - if (dev_priv->swaps_pending >= 100) { - DRM_DEBUG("Too many swaps queued\n"); - drm_vblank_put(dev, pipe); - return -EBUSY; - } - - vbl_swap = drm_calloc(1, sizeof(*vbl_swap), DRM_MEM_DRIVER); - - if (!vbl_swap) { - DRM_ERROR("Failed to allocate memory to queue swap\n"); - drm_vblank_put(dev, pipe); - return -ENOMEM; - } - - DRM_DEBUG("vbl_swap\n"); - - vbl_swap->drw_id = swap->drawable; - vbl_swap->plane = plane; - vbl_swap->sequence = swap->sequence; - vbl_swap->flip = (swap->seqtype & _DRM_VBLANK_FLIP); - - if (vbl_swap->flip) - swap->sequence++; - - spin_lock_irqsave(&dev_priv->swaps_lock, irqflags); - - list_add_tail(&vbl_swap->head, &dev_priv->vbl_swaps.head); - dev_priv->swaps_pending++; - - spin_unlock_irqrestore(&dev_priv->swaps_lock, irqflags); - - return 0; } /* drm_dma.h hooks @@ -949,57 +721,76 @@ if (!dev_priv->mmio_map) return -EINVAL; - I915_WRITE(HWSTAM, 0xeffe); + I915_WRITE16(HWSTAM, 0xeffe); + I915_WRITE(PIPEASTAT, 0); + I915_WRITE(PIPEBSTAT, 0); I915_WRITE(IMR, 0xffffffff); - I915_WRITE(IER, 0x0); + I915_WRITE16(IER, 0x0); + (void) I915_READ(IER); return 0; } void i915_driver_irq_postinstall(drm_device_t * dev) { + int error_mask; drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - INIT_LIST_HEAD(&dev_priv->vbl_swaps.head); - dev_priv->swaps_pending = 0; + dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; + + /* Unmask the interrupts that we always want on. */ + dev_priv->irq_mask_reg = ~I915_INTERRUPT_ENABLE_FIX; + + dev_priv->pipestat[0] = 0; + dev_priv->pipestat[1] = 0; - dev_priv->user_irq_refcount = 0; - dev_priv->irq_mask_reg = 0xffffffff; + /* + * Enable some error detection, note the instruction error mask + * bit is reserved, so we leave it masked. + */ + if (IS_G4X(dev)) { + error_mask = ~(GM45_ERROR_PAGE_TABLE | + GM45_ERROR_MEM_PRIV | + GM45_ERROR_CP_PRIV | + I915_ERROR_MEMORY_REFRESH); + } else { + error_mask = ~(I915_ERROR_PAGE_TABLE | + I915_ERROR_MEMORY_REFRESH); + } + I915_WRITE(EMR, error_mask); - dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; - dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */ + /* Disable pipe interrupt enables, clear pending pipe status */ + I915_WRITE(PIPEASTAT, I915_READ(PIPEASTAT) & 0x8000ffff); + I915_WRITE(PIPEBSTAT, I915_READ(PIPEBSTAT) & 0x8000ffff); + /* Clear pending interrupt status */ + I915_WRITE(IIR, I915_READ(IIR)); - i915_enable_interrupt(dev); + I915_WRITE(IMR, dev_priv->irq_mask_reg); + I915_WRITE(IER, I915_INTERRUPT_ENABLE_MASK); + (void) I915_READ(IER); DRM_INIT_WAITQUEUE(&dev_priv->irq_queue, DRM_INTR_PRI(dev)); - /* - * Initialize the hardware status page IRQ location. - */ - - I915_WRITE(INSTPM, (1 << 5) | (1 << 21)); return; } void i915_driver_irq_uninstall(drm_device_t * dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; - u32 temp; - if ((!dev_priv) || (dev_priv->irq_enabled == 0)) + if ((!dev_priv) || (dev->irq_enabled == 0)) return; dev_priv->vblank_pipe = 0; - dev_priv->irq_enabled = 0; I915_WRITE(HWSTAM, 0xffffffff); + I915_WRITE(PIPEASTAT, 0); + I915_WRITE(PIPEBSTAT, 0); I915_WRITE(IMR, 0xffffffff); I915_WRITE(IER, 0x0); - temp = I915_READ(PIPEASTAT); - I915_WRITE(PIPEASTAT, temp); - temp = I915_READ(PIPEBSTAT); - I915_WRITE(PIPEBSTAT, temp); - temp = I915_READ(IIR); - I915_WRITE(IIR, temp); + I915_WRITE(PIPEASTAT, I915_READ(PIPEASTAT) & 0x8000ffff); + I915_WRITE(PIPEBSTAT, I915_READ(PIPEBSTAT) & 0x8000ffff); + I915_WRITE(IIR, I915_READ(IIR)); + DRM_FINI_WAITQUEUE(&dev_priv->irq_queue); }