Index: stable/10/sys/dev/drm2/drmP.h =================================================================== --- stable/10/sys/dev/drm2/drmP.h (revision 295622) +++ stable/10/sys/dev/drm2/drmP.h (revision 295623) @@ -1,1788 +1,1782 @@ /** * \file drmP.h * Private header for Direct Rendering Manager * * \author Rickard E. (Rik) Faith * \author Gareth Hughes */ /* * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. * Copyright (c) 2009-2010, Code Aurora Forum. * 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 * VA LINUX SYSTEMS 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. */ #include __FBSDID("$FreeBSD$"); #ifndef _DRM_P_H_ #define _DRM_P_H_ #if defined(_KERNEL) || defined(__KERNEL__) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(__i386__) || defined(__amd64__) #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define __OS_HAS_AGP (defined(CONFIG_AGP) || (defined(CONFIG_AGP_MODULE) && defined(MODULE))) #define __OS_HAS_MTRR (defined(CONFIG_MTRR)) struct drm_file; struct drm_device; #include #include #include "opt_compat.h" #include "opt_drm.h" #include "opt_syscons.h" #ifdef DRM_DEBUG #undef DRM_DEBUG #define DRM_DEBUG_DEFAULT_ON 1 #endif /* DRM_DEBUG */ #define DRM_DEBUGBITS_DEBUG 0x1 #define DRM_DEBUGBITS_KMS 0x2 #define DRM_DEBUGBITS_FAILED_IOCTL 0x4 #undef DRM_LINUX #define DRM_LINUX 0 /***********************************************************************/ /** \name DRM template customization defaults */ /*@{*/ /* driver capabilities and requirements mask */ #define DRIVER_USE_AGP 0x1 #define DRIVER_REQUIRE_AGP 0x2 #define DRIVER_USE_MTRR 0x4 #define DRIVER_PCI_DMA 0x8 #define DRIVER_SG 0x10 #define DRIVER_HAVE_DMA 0x20 #define DRIVER_HAVE_IRQ 0x40 #define DRIVER_IRQ_SHARED 0x80 #define DRIVER_IRQ_VBL 0x100 #define DRIVER_DMA_QUEUE 0x200 #define DRIVER_FB_DMA 0x400 #define DRIVER_IRQ_VBL2 0x800 #define DRIVER_GEM 0x1000 #define DRIVER_MODESET 0x2000 #define DRIVER_PRIME 0x4000 #define DRIVER_BUS_PCI 0x1 #define DRIVER_BUS_PLATFORM 0x2 #define DRIVER_BUS_USB 0x3 /***********************************************************************/ /** \name Begin the DRM... */ /*@{*/ #define DRM_DEBUG_CODE 2 /**< Include debugging code if > 1, then also include looping detection. */ #define DRM_MAGIC_HASH_ORDER 4 /**< Size of key hash table. Must be power of 2. */ #define DRM_KERNEL_CONTEXT 0 /**< Change drm_resctx if changed */ #define DRM_RESERVED_CONTEXTS 1 /**< Change drm_resctx if changed */ #define DRM_LOOPING_LIMIT 5000000 #define DRM_TIME_SLICE (HZ/20) /**< Time slice for GLXContexts */ #define DRM_LOCK_SLICE 1 /**< Time slice for lock, in jiffies */ #define DRM_FLAG_DEBUG 0x01 #define DRM_MAX_CTXBITMAP (PAGE_SIZE * 8) #define DRM_MAP_HASH_OFFSET 0x10000000 /*@}*/ /***********************************************************************/ /** \name Macros to make printk easier */ /*@{*/ /** * Error output. * * \param fmt printf() like format string. * \param arg arguments */ #define DRM_ERROR(fmt, ...) \ printf("error: [" DRM_NAME ":pid%d:%s] *ERROR* " fmt, \ DRM_CURRENTPID, __func__ , ##__VA_ARGS__) #define DRM_WARNING(fmt, ...) printf("warning: [" DRM_NAME "] " fmt , ##__VA_ARGS__) #define DRM_INFO(fmt, ...) printf("info: [" DRM_NAME "] " fmt , ##__VA_ARGS__) /** * Debug output. * * \param fmt printf() like format string. * \param arg arguments */ #define DRM_DEBUG(fmt, ...) do { \ if ((drm_debug & DRM_DEBUGBITS_DEBUG) != 0) \ printf("[" DRM_NAME ":pid%d:%s] " fmt, DRM_CURRENTPID, \ __func__ , ##__VA_ARGS__); \ } while (0) #define DRM_DEBUG_KMS(fmt, ...) do { \ if ((drm_debug & DRM_DEBUGBITS_KMS) != 0) \ printf("[" DRM_NAME ":KMS:pid%d:%s] " fmt, DRM_CURRENTPID,\ __func__ , ##__VA_ARGS__); \ } while (0) #define DRM_DEBUG_DRIVER(fmt, ...) do { \ if ((drm_debug & DRM_DEBUGBITS_KMS) != 0) \ printf("[" DRM_NAME ":KMS:pid%d:%s] " fmt, DRM_CURRENTPID,\ __func__ , ##__VA_ARGS__); \ } while (0) /*@}*/ /***********************************************************************/ /** \name Internal types and structures */ /*@{*/ #define DRM_ARRAY_SIZE(x) ARRAY_SIZE(x) #define DRM_LEFTCOUNT(x) (((x)->rp + (x)->count - (x)->wp) % ((x)->count + 1)) #define DRM_BUFCOUNT(x) ((x)->count - DRM_LEFTCOUNT(x)) #define DRM_IF_VERSION(maj, min) (maj << 16 | min) /** * Test that the hardware lock is held by the caller, returning otherwise. * * \param dev DRM device. * \param filp file pointer of the caller. */ #define LOCK_TEST_WITH_RETURN( dev, _file_priv ) \ do { \ if (!_DRM_LOCK_IS_HELD(_file_priv->master->lock.hw_lock->lock) || \ _file_priv->master->lock.file_priv != _file_priv) { \ DRM_ERROR( "%s called without lock held, held %d owner %p %p\n",\ __func__, _DRM_LOCK_IS_HELD(_file_priv->master->lock.hw_lock->lock),\ _file_priv->master->lock.file_priv, _file_priv); \ return -EINVAL; \ } \ } while (0) /** * Ioctl function type. * * \param inode device inode. * \param file_priv DRM file private pointer. * \param cmd command. * \param arg argument. */ typedef int drm_ioctl_t(struct drm_device *dev, void *data, struct drm_file *file_priv); #define DRM_IOCTL_NR(n) ((n) & 0xff) #define DRM_MAJOR 226 #define DRM_AUTH 0x1 #define DRM_MASTER 0x2 #define DRM_ROOT_ONLY 0x4 #define DRM_CONTROL_ALLOW 0x8 #define DRM_UNLOCKED 0x10 struct drm_ioctl_desc { unsigned long cmd; int flags; drm_ioctl_t *func; unsigned int cmd_drv; }; /** * Creates a driver or general drm_ioctl_desc array entry for the given * ioctl, for use by drm_ioctl(). */ #define DRM_IOCTL_DEF(ioctl, _func, _flags) \ [DRM_IOCTL_NR(ioctl)] = {.cmd = ioctl, .func = _func, .flags = _flags, .cmd_drv = 0} #define DRM_IOCTL_DEF_DRV(ioctl, _func, _flags) \ [DRM_IOCTL_NR(DRM_##ioctl)] = {.cmd = DRM_##ioctl, .func = _func, .flags = _flags, .cmd_drv = DRM_IOCTL_##ioctl} struct drm_magic_entry { struct list_head head; struct drm_hash_item hash_item; struct drm_file *priv; }; /** * DMA buffer. */ struct drm_buf { int idx; /**< Index into master buflist */ int total; /**< Buffer size */ int order; /**< log-base-2(total) */ int used; /**< Amount of buffer in use (for DMA) */ unsigned long offset; /**< Byte offset (used internally) */ void *address; /**< Address of buffer */ unsigned long bus_address; /**< Bus address of buffer */ struct drm_buf *next; /**< Kernel-only: used for free list */ __volatile__ int waiting; /**< On kernel DMA queue */ __volatile__ int pending; /**< On hardware DMA queue */ struct drm_file *file_priv; /**< Private of holding file descr */ int context; /**< Kernel queue for this buffer */ int while_locked; /**< Dispatch this buffer while locked */ enum { DRM_LIST_NONE = 0, DRM_LIST_FREE = 1, DRM_LIST_WAIT = 2, DRM_LIST_PEND = 3, DRM_LIST_PRIO = 4, DRM_LIST_RECLAIM = 5 } list; /**< Which list we're on */ int dev_priv_size; /**< Size of buffer private storage */ void *dev_private; /**< Per-buffer private storage */ }; struct drm_freelist { int initialized; /**< Freelist in use */ atomic_t count; /**< Number of free buffers */ struct drm_buf *next; /**< End pointer */ #ifdef FREEBSD_NOTYET wait_queue_head_t waiting; /**< Processes waiting on free bufs */ #endif /* defined(FREEBSD_NOTYET) */ int low_mark; /**< Low water mark */ int high_mark; /**< High water mark */ #ifdef FREEBSD_NOTYET atomic_t wfh; /**< If waiting for high mark */ spinlock_t lock; #endif /* defined(FREEBSD_NOTYET) */ }; typedef struct drm_dma_handle { void *vaddr; bus_addr_t busaddr; bus_dma_tag_t tag; bus_dmamap_t map; } drm_dma_handle_t; /** * Buffer entry. There is one of this for each buffer size order. */ struct drm_buf_entry { int buf_size; /**< size */ int buf_count; /**< number of buffers */ struct drm_buf *buflist; /**< buffer list */ int seg_count; int page_order; struct drm_dma_handle **seglist; struct drm_freelist freelist; }; /* Event queued up for userspace to read */ struct drm_pending_event { struct drm_event *event; struct list_head link; struct drm_file *file_priv; pid_t pid; /* pid of requester, no guarantee it's valid by the time we deliver the event, for tracing only */ void (*destroy)(struct drm_pending_event *event); }; /* initial implementaton using a linked list - todo hashtab */ struct drm_prime_file_private { struct list_head head; struct mtx lock; }; struct drm_file { int authenticated; pid_t pid; uid_t uid; drm_magic_t magic; unsigned long ioctl_count; struct list_head lhead; struct drm_minor *minor; unsigned long lock_count; void *driver_priv; struct drm_gem_names object_names; int is_master; /* this file private is a master for a minor */ struct drm_master *master; /* master this node is currently associated with N.B. not always minor->master */ struct list_head fbs; struct selinfo event_poll; struct list_head event_list; int event_space; struct drm_prime_file_private prime; }; /** * Lock data. */ struct drm_lock_data { struct drm_hw_lock *hw_lock; /**< Hardware lock */ /** Private of lock holder's file (NULL=kernel) */ struct drm_file *file_priv; wait_queue_head_t lock_queue; /**< Queue of blocked processes */ unsigned long lock_time; /**< Time of last lock in jiffies */ struct mtx spinlock; uint32_t kernel_waiters; uint32_t user_waiters; int idle_has_lock; }; /** * DMA data. */ struct drm_device_dma { struct drm_buf_entry bufs[DRM_MAX_ORDER + 1]; /**< buffers, grouped by their size order */ int buf_count; /**< total number of buffers */ struct drm_buf **buflist; /**< Vector of pointers into drm_device_dma::bufs */ int seg_count; int page_count; /**< number of pages */ unsigned long *pagelist; /**< page list */ unsigned long byte_count; enum { _DRM_DMA_USE_AGP = 0x01, _DRM_DMA_USE_SG = 0x02, _DRM_DMA_USE_FB = 0x04, _DRM_DMA_USE_PCI_RO = 0x08 } flags; }; /** * AGP memory entry. Stored as a doubly linked list. */ struct drm_agp_mem { unsigned long handle; /**< handle */ DRM_AGP_MEM *memory; unsigned long bound; /**< address */ int pages; struct list_head head; }; /** * AGP data. * * \sa drm_agp_init() and drm_device::agp. */ struct drm_agp_head { DRM_AGP_KERN agp_info; /**< AGP device information */ struct list_head memory; unsigned long mode; /**< AGP mode */ device_t bridge; int enabled; /**< whether the AGP bus as been enabled */ int acquired; /**< whether the AGP device has been acquired */ unsigned long base; int agp_mtrr; int cant_use_aperture; }; /** * Scatter-gather memory. */ struct drm_sg_mem { vm_offset_t vaddr; vm_paddr_t *busaddr; vm_pindex_t pages; }; struct drm_sigdata { int context; struct drm_hw_lock *lock; }; /** * Kernel side of a mapping */ #define DRM_MAP_HANDLE_BITS (sizeof(void *) == 4 ? 4 : 24) #define DRM_MAP_HANDLE_SHIFT (sizeof(void *) * 8 - DRM_MAP_HANDLE_BITS) struct drm_local_map { resource_size_t offset; /**< Requested physical address (0 for SAREA)*/ unsigned long size; /**< Requested physical size (bytes) */ enum drm_map_type type; /**< Type of memory to map */ enum drm_map_flags flags; /**< Flags */ void *handle; /**< User-space: "Handle" to pass to mmap() */ /**< Kernel-space: kernel-virtual address */ int mtrr; /**< MTRR slot used */ /* Private data */ drm_dma_handle_t *dmah; }; typedef struct drm_local_map drm_local_map_t; /** * Mappings list */ struct drm_map_list { struct list_head head; /**< list head */ struct drm_hash_item hash; struct drm_local_map *map; /**< mapping */ uint64_t user_token; struct drm_master *master; struct drm_mm_node *file_offset_node; /**< fake offset */ }; /** * Context handle list */ struct drm_ctx_list { struct list_head head; /**< list head */ drm_context_t handle; /**< context handle */ struct drm_file *tag; /**< associated fd private data */ }; /* location of GART table */ #define DRM_ATI_GART_MAIN 1 #define DRM_ATI_GART_FB 2 #define DRM_ATI_GART_PCI 1 #define DRM_ATI_GART_PCIE 2 #define DRM_ATI_GART_IGP 3 struct drm_ati_pcigart_info { int gart_table_location; int gart_reg_if; void *addr; dma_addr_t bus_addr; dma_addr_t table_mask; struct drm_dma_handle *table_handle; struct drm_local_map mapping; int table_size; struct drm_dma_handle *dmah; /* handle for ATI PCIGART table FIXME */ }; /** * GEM specific mm private for tracking GEM objects */ struct drm_gem_mm { struct unrhdr *idxunr; struct drm_open_hash offset_hash; /**< User token hash table for maps */ }; /** * 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 */ u_int refcount; /** Handle count of this object. Each handle also holds a reference */ atomic_t handle_count; /* number of handles on this object */ /** Related drm device */ struct drm_device *dev; /** File representing the shmem storage: filp in Linux parlance */ vm_object_t vm_obj; /* Mapping info for this object */ bool on_map; struct drm_hash_item map_list; /** * 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; #ifdef FREEBSD_NOTYET /* dma buf exported from this GEM object */ struct dma_buf *export_dma_buf; /* dma buf attachment backing this object */ struct dma_buf_attachment *import_attach; #endif /* FREEBSD_NOTYET */ }; #include /* per-master structure */ struct drm_master { u_int refcount; /* refcount for this master */ struct list_head head; /**< each minor contains a list of masters */ struct drm_minor *minor; /**< link back to minor we are a master for */ char *unique; /**< Unique identifier: e.g., busid */ int unique_len; /**< Length of unique field */ int unique_size; /**< amount allocated */ int blocked; /**< Blocked due to VC switch? */ /** \name Authentication */ /*@{ */ struct drm_open_hash magiclist; struct list_head magicfree; /*@} */ struct drm_lock_data lock; /**< Information on hardware lock */ void *driver_priv; /**< Private structure for driver to use */ }; /* Size of ringbuffer for vblank timestamps. Just double-buffer * in initial implementation. */ #define DRM_VBLANKTIME_RBSIZE 2 /* Flags and return codes for get_vblank_timestamp() driver function. */ #define DRM_CALLED_FROM_VBLIRQ 1 #define DRM_VBLANKTIME_SCANOUTPOS_METHOD (1 << 0) #define DRM_VBLANKTIME_INVBL (1 << 1) /* get_scanout_position() return flags */ #define DRM_SCANOUTPOS_VALID (1 << 0) #define DRM_SCANOUTPOS_INVBL (1 << 1) #define DRM_SCANOUTPOS_ACCURATE (1 << 2) struct drm_bus { int bus_type; int (*get_irq)(struct drm_device *dev); void (*free_irq)(struct drm_device *dev); const char *(*get_name)(struct drm_device *dev); int (*set_busid)(struct drm_device *dev, struct drm_master *master); int (*set_unique)(struct drm_device *dev, struct drm_master *master, struct drm_unique *unique); int (*irq_by_busid)(struct drm_device *dev, struct drm_irq_busid *p); /* hooks that are for PCI */ int (*agp_init)(struct drm_device *dev); }; /** * DRM driver structure. This structure represent the common code for * a family of cards. There will one drm_device for each card present * in this family */ struct drm_driver { int (*load) (struct drm_device *, unsigned long flags); int (*firstopen) (struct drm_device *); int (*open) (struct drm_device *, struct drm_file *); void (*preclose) (struct drm_device *, struct drm_file *file_priv); void (*postclose) (struct drm_device *, struct drm_file *); void (*lastclose) (struct drm_device *); int (*unload) (struct drm_device *); int (*dma_ioctl) (struct drm_device *dev, void *data, struct drm_file *file_priv); int (*dma_quiescent) (struct drm_device *); int (*context_dtor) (struct drm_device *dev, int context); /** * get_vblank_counter - get raw hardware vblank counter * @dev: DRM device * @crtc: counter to fetch * * Driver callback for fetching a raw hardware vblank counter for @crtc. * If a device doesn't have a hardware counter, the driver can simply * return the value of drm_vblank_count. The DRM core will account for * missed vblank events while interrupts where disabled based on system * timestamps. * * Wraparound handling and loss of events due to modesetting is dealt * with in the DRM core code. * * RETURNS * Raw vblank counter value. */ u32 (*get_vblank_counter) (struct drm_device *dev, int crtc); /** * enable_vblank - enable vblank interrupt events * @dev: DRM device * @crtc: which irq to enable * * Enable vblank interrupts for @crtc. If the device doesn't have * a hardware vblank counter, this routine should be a no-op, since * interrupts will have to stay on to keep the count accurate. * * RETURNS * Zero on success, appropriate errno if the given @crtc's vblank * interrupt cannot be enabled. */ int (*enable_vblank) (struct drm_device *dev, int crtc); /** * disable_vblank - disable vblank interrupt events * @dev: DRM device * @crtc: which irq to enable * * Disable vblank interrupts for @crtc. If the device doesn't have * a hardware vblank counter, this routine should be a no-op, since * interrupts will have to stay on to keep the count accurate. */ void (*disable_vblank) (struct drm_device *dev, int crtc); /** * Called by \c drm_device_is_agp. Typically used to determine if a * card is really attached to AGP or not. * * \param dev DRM device handle * * \returns * One of three values is returned depending on whether or not the * card is absolutely \b not AGP (return of 0), absolutely \b is AGP * (return of 1), or may or may not be AGP (return of 2). */ int (*device_is_agp) (struct drm_device *dev); /** * Called by vblank timestamping code. * * Return the current display scanout position from a crtc. * * \param dev DRM device. * \param crtc Id of the crtc to query. * \param *vpos Target location for current vertical scanout position. * \param *hpos Target location for current horizontal scanout position. * * Returns vpos as a positive number while in active scanout area. * Returns vpos as a negative number inside vblank, counting the number * of scanlines to go until end of vblank, e.g., -1 means "one scanline * until start of active scanout / end of vblank." * * \return Flags, or'ed together as follows: * * DRM_SCANOUTPOS_VALID = Query successful. * DRM_SCANOUTPOS_INVBL = Inside vblank. * DRM_SCANOUTPOS_ACCURATE = Returned position is accurate. A lack of * this flag means that returned position may be offset by a constant * but unknown small number of scanlines wrt. real scanout position. * */ int (*get_scanout_position) (struct drm_device *dev, int crtc, int *vpos, int *hpos); /** * Called by \c drm_get_last_vbltimestamp. Should return a precise * timestamp when the most recent VBLANK interval ended or will end. * * Specifically, the timestamp in @vblank_time should correspond as * closely as possible to the time when the first video scanline of * the video frame after the end of VBLANK will start scanning out, * the time immediately after end of the VBLANK interval. If the * @crtc is currently inside VBLANK, this will be a time in the future. * If the @crtc is currently scanning out a frame, this will be the * past start time of the current scanout. This is meant to adhere * to the OpenML OML_sync_control extension specification. * * \param dev dev DRM device handle. * \param crtc crtc for which timestamp should be returned. * \param *max_error Maximum allowable timestamp error in nanoseconds. * Implementation should strive to provide timestamp * with an error of at most *max_error nanoseconds. * Returns true upper bound on error for timestamp. * \param *vblank_time Target location for returned vblank timestamp. * \param flags 0 = Defaults, no special treatment needed. * \param DRM_CALLED_FROM_VBLIRQ = Function is called from vblank * irq handler. Some drivers need to apply some workarounds * for gpu-specific vblank irq quirks if flag is set. * * \returns * Zero if timestamping isn't supported in current display mode or a * negative number on failure. A positive status code on success, * which describes how the vblank_time timestamp was computed. */ int (*get_vblank_timestamp) (struct drm_device *dev, int crtc, int *max_error, struct timeval *vblank_time, unsigned flags); /* these have to be filled in */ irqreturn_t(*irq_handler) (DRM_IRQ_ARGS); void (*irq_preinstall) (struct drm_device *dev); int (*irq_postinstall) (struct drm_device *dev); void (*irq_uninstall) (struct drm_device *dev); void (*set_version) (struct drm_device *dev, struct drm_set_version *sv); /* Master routines */ int (*master_create)(struct drm_device *dev, struct drm_master *master); void (*master_destroy)(struct drm_device *dev, struct drm_master *master); /** * master_set is called whenever the minor master is set. * master_drop is called whenever the minor master is dropped. */ int (*master_set)(struct drm_device *dev, struct drm_file *file_priv, bool from_open); void (*master_drop)(struct drm_device *dev, struct drm_file *file_priv, bool from_release); /** * 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); int (*gem_open_object) (struct drm_gem_object *, struct drm_file *); void (*gem_close_object) (struct drm_gem_object *, struct drm_file *); #ifdef FREEBSD_NOTYET /* prime: */ /* export handle -> fd (see drm_gem_prime_handle_to_fd() helper) */ int (*prime_handle_to_fd)(struct drm_device *dev, struct drm_file *file_priv, uint32_t handle, uint32_t flags, int *prime_fd); /* import fd -> handle (see drm_gem_prime_fd_to_handle() helper) */ int (*prime_fd_to_handle)(struct drm_device *dev, struct drm_file *file_priv, int prime_fd, uint32_t *handle); /* export GEM -> dmabuf */ struct dma_buf * (*gem_prime_export)(struct drm_device *dev, struct drm_gem_object *obj, int flags); /* import dmabuf -> GEM */ struct drm_gem_object * (*gem_prime_import)(struct drm_device *dev, struct dma_buf *dma_buf); #endif /* defined(FREEBSD_NOTYET) */ /* dumb alloc support */ int (*dumb_create)(struct drm_file *file_priv, struct drm_device *dev, struct drm_mode_create_dumb *args); int (*dumb_map_offset)(struct drm_file *file_priv, struct drm_device *dev, uint32_t handle, uint64_t *offset); int (*dumb_destroy)(struct drm_file *file_priv, struct drm_device *dev, uint32_t handle); /* Driver private ops for this object */ struct cdev_pager_ops *gem_pager_ops; int (*sysctl_init)(struct drm_device *dev, struct sysctl_ctx_list *ctx, struct sysctl_oid *top); void (*sysctl_cleanup)(struct drm_device *dev); int major; int minor; int patchlevel; char *name; char *desc; char *date; u32 driver_features; int dev_priv_size; struct drm_ioctl_desc *ioctls; int num_ioctls; struct drm_bus *bus; #ifdef COMPAT_FREEBSD32 struct drm_ioctl_desc *compat_ioctls; int *num_compat_ioctls; #endif int buf_priv_size; }; #define DRM_MINOR_UNASSIGNED 0 #define DRM_MINOR_LEGACY 1 #define DRM_MINOR_CONTROL 2 #define DRM_MINOR_RENDER 3 /** * DRM minor structure. This structure represents a drm minor number. */ struct drm_minor { int index; /**< Minor device number */ int type; /**< Control or render */ struct cdev *device; /**< Device number for mknod */ device_t kdev; /**< OS device */ struct drm_device *dev; struct drm_master *master; /* currently active master for this node */ struct list_head master_list; struct drm_mode_group mode_group; struct sigio *buf_sigio; /* Processes waiting for SIGIO */ }; /* mode specified on the command line */ struct drm_cmdline_mode { bool specified; bool refresh_specified; bool bpp_specified; int xres, yres; int bpp; int refresh; bool rb; bool interlace; bool cvt; bool margins; enum drm_connector_force force; }; struct drm_pending_vblank_event { struct drm_pending_event base; int pipe; struct drm_event_vblank event; }; /** * DRM device structure. This structure represent a complete card that * may contain multiple heads. */ struct drm_device { int if_version; /**< Highest interface version set */ /** \name Locks */ /*@{ */ struct mtx count_lock; /**< For inuse, drm_device::open_count, drm_device::buf_use */ struct sx dev_struct_lock; /**< For others */ /*@} */ /** \name Usage Counters */ /*@{ */ int open_count; /**< Outstanding files open */ atomic_t ioctl_count; /**< Outstanding IOCTLs pending */ atomic_t vma_count; /**< Outstanding vma areas open */ int buf_use; /**< Buffers in use -- cannot alloc */ atomic_t buf_alloc; /**< Buffer allocation in progress */ /*@} */ /** \name Performance counters */ /*@{ */ unsigned long counters; enum drm_stat_type types[15]; atomic_t counts[15]; /*@} */ struct list_head filelist; /** \name Memory management */ /*@{ */ struct list_head maplist; /**< Linked list of regions */ int map_count; /**< Number of mappable regions */ struct drm_open_hash map_hash; /**< User token hash table for maps */ /** \name Context handle management */ /*@{ */ struct list_head ctxlist; /**< Linked list of context handles */ int ctx_count; /**< Number of context handles */ struct mtx ctxlist_mutex; /**< For ctxlist */ drm_local_map_t **context_sareas; int max_context; unsigned long *ctx_bitmap; /*@} */ /** \name DMA support */ /*@{ */ struct drm_device_dma *dma; /**< Optional pointer for DMA support */ /*@} */ /** \name Context support */ /*@{ */ int irq_enabled; /**< True if irq handler is enabled */ atomic_t context_flag; /**< Context swapping flag */ atomic_t interrupt_flag; /**< Interruption handler flag */ atomic_t dma_flag; /**< DMA dispatch flag */ wait_queue_head_t context_wait; /**< Processes waiting on ctx switch */ int last_checked; /**< Last context checked for DMA */ int last_context; /**< Last current context */ unsigned long last_switch; /**< jiffies at last context switch */ /*@} */ /** \name VBLANK IRQ support */ /*@{ */ /* * 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; atomic_t *_vblank_count; /**< number of VBLANK interrupts (driver must alloc the right number of counters) */ struct timeval *_vblank_time; /**< timestamp of current vblank_count (drivers must alloc right number of fields) */ struct mtx vblank_time_lock; /**< Protects vblank count and time updates during vblank enable/disable */ struct mtx vbl_lock; atomic_t *vblank_refcount; /* number of users of vblank interruptsper crtc */ u32 *last_vblank; /* protected by dev->vbl_lock, used */ /* for wraparound handling */ int *vblank_enabled; /* so we don't call enable more than once per disable */ int *vblank_inmodeset; /* Display driver is setting mode */ u32 *last_vblank_wait; /* Last vblank seqno waited per CRTC */ struct callout vblank_disable_callout; u32 max_vblank_count; /**< size of vblank counter register */ /** * List of events */ struct list_head vblank_event_list; struct mtx event_lock; /*@} */ struct drm_agp_head *agp; /**< AGP data */ device_t dev; /* Device instance from newbus */ uint16_t pci_device; /* PCI device id */ uint16_t pci_vendor; /* PCI vendor id */ uint16_t pci_subdevice; /* PCI subsystem device id */ uint16_t pci_subvendor; /* PCI subsystem vendor id */ struct drm_sg_mem *sg; /**< Scatter gather memory */ unsigned int num_crtcs; /**< Number of CRTCs on this device */ void *dev_private; /**< device private data */ void *mm_private; struct drm_sigdata sigdata; /**< For block_all_signals */ sigset_t sigmask; struct drm_driver *driver; struct drm_local_map *agp_buffer_map; unsigned int agp_buffer_token; struct drm_minor *control; /**< Control node for card */ struct drm_minor *primary; /**< render type primary screen head */ struct drm_mode_config mode_config; /**< Current mode config */ /** \name GEM information */ /*@{ */ struct sx object_name_lock; struct drm_gem_names object_names; /*@} */ int switch_power_state; atomic_t unplugged; /* device has been unplugged or gone away */ /* Locks */ struct mtx dma_lock; /* protects dev->dma */ struct mtx irq_lock; /* protects irq condition checks */ /* Context support */ int irq; /* Interrupt used by board */ int msi_enabled; /* MSI enabled */ int irqrid; /* Interrupt used by board */ struct resource *irqr; /* Resource for interrupt used by board */ void *irqh; /* Handle from bus_setup_intr */ /* Storage of resource pointers for drm_get_resource_* */ #define DRM_MAX_PCI_RESOURCE 6 struct resource *pcir[DRM_MAX_PCI_RESOURCE]; int pcirid[DRM_MAX_PCI_RESOURCE]; struct mtx pcir_lock; int pci_domain; int pci_bus; int pci_slot; int pci_func; /* Sysctl support */ struct drm_sysctl_info *sysctl; int sysctl_node_idx; void *drm_ttm_bdev; void *sysctl_private; char busid_str[128]; int modesetting; drm_pci_id_list_t *id_entry; /* PCI ID, name, and chipset private */ }; #define DRM_SWITCH_POWER_ON 0 #define DRM_SWITCH_POWER_OFF 1 #define DRM_SWITCH_POWER_CHANGING 2 static __inline__ int drm_core_check_feature(struct drm_device *dev, int feature) { return ((dev->driver->driver_features & feature) ? 1 : 0); } static inline int drm_dev_to_irq(struct drm_device *dev) { return dev->driver->bus->get_irq(dev); } #if __OS_HAS_AGP static inline int drm_core_has_AGP(struct drm_device *dev) { return drm_core_check_feature(dev, DRIVER_USE_AGP); } #else #define drm_core_has_AGP(dev) (0) #endif #if __OS_HAS_MTRR static inline int drm_core_has_MTRR(struct drm_device *dev) { return drm_core_check_feature(dev, DRIVER_USE_MTRR); } #define DRM_MTRR_WC MDF_WRITECOMBINE int drm_mtrr_add(unsigned long offset, unsigned long size, unsigned int flags); int drm_mtrr_del(int handle, unsigned long offset, unsigned long size, unsigned int flags); #else #define drm_core_has_MTRR(dev) (0) #define DRM_MTRR_WC 0 static inline int drm_mtrr_add(unsigned long offset, unsigned long size, unsigned int flags) { return 0; } static inline int drm_mtrr_del(int handle, unsigned long offset, unsigned long size, unsigned int flags) { return 0; } #endif /******************************************************************/ /** \name Internal function definitions */ /*@{*/ /* Driver support (drm_drv.h) */ d_ioctl_t drm_ioctl; extern int drm_lastclose(struct drm_device *dev); /* Device support (drm_fops.h) */ extern struct sx drm_global_mutex; d_open_t drm_open; d_read_t drm_read; extern void drm_release(void *data); /* Mapping support (drm_vm.h) */ d_mmap_t drm_mmap; int drm_mmap_single(struct cdev *kdev, vm_ooffset_t *offset, vm_size_t size, struct vm_object **obj_res, int nprot); d_poll_t drm_poll; /* Memory management support (drm_memory.h) */ extern void drm_free_agp(DRM_AGP_MEM * handle, int pages); extern int drm_bind_agp(DRM_AGP_MEM * handle, unsigned int start); #ifdef FREEBSD_NOTYET extern DRM_AGP_MEM *drm_agp_bind_pages(struct drm_device *dev, struct page **pages, unsigned long num_pages, uint32_t gtt_offset, uint32_t type); #endif /* FREEBSD_NOTYET */ extern int drm_unbind_agp(DRM_AGP_MEM * handle); /* Misc. IOCTL support (drm_ioctl.h) */ extern int drm_irq_by_busid(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_getunique(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_setunique(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_getmap(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_getclient(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_getstats(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_getcap(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_setversion(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_noop(struct drm_device *dev, void *data, struct drm_file *file_priv); /* Context IOCTL support (drm_context.h) */ extern int drm_resctx(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_addctx(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_modctx(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_getctx(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_switchctx(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_newctx(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_rmctx(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_ctxbitmap_init(struct drm_device *dev); extern void drm_ctxbitmap_cleanup(struct drm_device *dev); extern void drm_ctxbitmap_free(struct drm_device *dev, int ctx_handle); extern int drm_setsareactx(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_getsareactx(struct drm_device *dev, void *data, struct drm_file *file_priv); /* Authentication IOCTL support (drm_auth.h) */ extern int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_authmagic(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_remove_magic(struct drm_master *master, drm_magic_t magic); /* Cache management (drm_cache.c) */ void drm_clflush_pages(vm_page_t *pages, unsigned long num_pages); void drm_clflush_virt_range(char *addr, unsigned long length); /* Locking IOCTL support (drm_lock.h) */ extern int drm_lock(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_unlock(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_lock_free(struct drm_lock_data *lock_data, unsigned int context); extern void drm_idlelock_take(struct drm_lock_data *lock_data); extern void drm_idlelock_release(struct drm_lock_data *lock_data); /* * These are exported to drivers so that they can implement fencing using * DMA quiscent + idle. DMA quiescent usually requires the hardware lock. */ extern int drm_i_have_hw_lock(struct drm_device *dev, struct drm_file *file_priv); /* Buffer management support (drm_bufs.h) */ extern int drm_addbufs_agp(struct drm_device *dev, struct drm_buf_desc * request); extern int drm_addbufs_pci(struct drm_device *dev, struct drm_buf_desc * request); extern int drm_addmap(struct drm_device *dev, resource_size_t offset, unsigned int size, enum drm_map_type type, enum drm_map_flags flags, struct drm_local_map **map_ptr); extern int drm_addmap_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_rmmap(struct drm_device *dev, struct drm_local_map *map); extern int drm_rmmap_locked(struct drm_device *dev, struct drm_local_map *map); extern int drm_rmmap_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_addbufs(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_infobufs(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_markbufs(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_freebufs(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_mapbufs(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_order(unsigned long size); /* DMA support (drm_dma.h) */ extern int drm_dma_setup(struct drm_device *dev); extern void drm_dma_takedown(struct drm_device *dev); extern void drm_free_buffer(struct drm_device *dev, struct drm_buf * buf); extern void drm_core_reclaim_buffers(struct drm_device *dev, struct drm_file *filp); /* IRQ support (drm_irq.h) */ extern int drm_control(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_irq_install(struct drm_device *dev); extern int drm_irq_uninstall(struct drm_device *dev); extern int drm_vblank_init(struct drm_device *dev, int num_crtcs); extern int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *filp); extern int drm_vblank_wait(struct drm_device *dev, unsigned int *vbl_seq); extern u32 drm_vblank_count(struct drm_device *dev, int crtc); extern u32 drm_vblank_count_and_time(struct drm_device *dev, int crtc, struct timeval *vblanktime); extern void drm_send_vblank_event(struct drm_device *dev, int crtc, struct drm_pending_vblank_event *e); extern bool drm_handle_vblank(struct drm_device *dev, int crtc); extern int drm_vblank_get(struct drm_device *dev, int crtc); extern void drm_vblank_put(struct drm_device *dev, int crtc); extern void drm_vblank_off(struct drm_device *dev, int crtc); extern void drm_vblank_cleanup(struct drm_device *dev); extern u32 drm_get_last_vbltimestamp(struct drm_device *dev, int crtc, struct timeval *tvblank, unsigned flags); extern int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc, int *max_error, struct timeval *vblank_time, unsigned flags, struct drm_crtc *refcrtc); extern void drm_calc_timestamping_constants(struct drm_crtc *crtc); extern bool drm_mode_parse_command_line_for_connector(const char *mode_option, struct drm_connector *connector, struct drm_cmdline_mode *mode); extern struct drm_display_mode * drm_mode_create_from_cmdline_mode(struct drm_device *dev, struct drm_cmdline_mode *cmd); /* Modesetting support */ extern void drm_vblank_pre_modeset(struct drm_device *dev, int crtc); extern void drm_vblank_post_modeset(struct drm_device *dev, int crtc); extern int drm_modeset_ctl(struct drm_device *dev, void *data, struct drm_file *file_priv); /* AGP/GART support (drm_agpsupport.h) */ extern struct drm_agp_head *drm_agp_init(struct drm_device *dev); extern int drm_agp_acquire(struct drm_device *dev); extern int drm_agp_acquire_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_agp_release(struct drm_device *dev); extern int drm_agp_release_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_agp_enable(struct drm_device *dev, struct drm_agp_mode mode); extern int drm_agp_enable_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_agp_info(struct drm_device *dev, struct drm_agp_info *info); extern int drm_agp_info_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_agp_alloc(struct drm_device *dev, struct drm_agp_buffer *request); extern int drm_agp_alloc_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_agp_free(struct drm_device *dev, struct drm_agp_buffer *request); extern int drm_agp_free_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_agp_unbind(struct drm_device *dev, struct drm_agp_binding *request); extern int drm_agp_unbind_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_agp_bind(struct drm_device *dev, struct drm_agp_binding *request); extern int drm_agp_bind_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); /* Stub support (drm_stub.h) */ extern int drm_setmaster_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_dropmaster_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); struct drm_master *drm_master_create(struct drm_minor *minor); extern struct drm_master *drm_master_get(struct drm_master *master); extern void drm_master_put(struct drm_master **master); extern void drm_put_dev(struct drm_device *dev); extern int drm_put_minor(struct drm_minor **minor); extern void drm_unplug_dev(struct drm_device *dev); extern unsigned int drm_debug; extern unsigned int drm_notyet; extern unsigned int drm_vblank_offdelay; extern unsigned int drm_timestamp_precision; extern unsigned int drm_timestamp_monotonic; extern struct drm_local_map *drm_getsarea(struct drm_device *dev); #ifdef FREEBSD_NOTYET extern int drm_gem_prime_handle_to_fd(struct drm_device *dev, struct drm_file *file_priv, uint32_t handle, uint32_t flags, int *prime_fd); extern int drm_gem_prime_fd_to_handle(struct drm_device *dev, struct drm_file *file_priv, int prime_fd, uint32_t *handle); extern int drm_prime_handle_to_fd_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_prime_fd_to_handle_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_prime_sg_to_page_addr_arrays(struct sg_table *sgt, vm_page_t *pages, dma_addr_t *addrs, int max_pages); extern struct sg_table *drm_prime_pages_to_sg(vm_page_t *pages, int nr_pages); extern void drm_prime_gem_destroy(struct drm_gem_object *obj, struct sg_table *sg); void drm_prime_init_file_private(struct drm_prime_file_private *prime_fpriv); void drm_prime_destroy_file_private(struct drm_prime_file_private *prime_fpriv); int drm_prime_add_imported_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf, uint32_t handle); int drm_prime_lookup_imported_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf, uint32_t *handle); void drm_prime_remove_imported_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf); int drm_prime_add_dma_buf(struct drm_device *dev, struct drm_gem_object *obj); int drm_prime_lookup_obj(struct drm_device *dev, struct dma_buf *buf, struct drm_gem_object **obj); #endif /* FREEBSD_NOTYET */ /* Scatter Gather Support (drm_scatter.h) */ extern void drm_sg_cleanup(struct drm_sg_mem * entry); extern int drm_sg_alloc_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int drm_sg_alloc(struct drm_device *dev, struct drm_scatter_gather * request); extern int drm_sg_free(struct drm_device *dev, void *data, struct drm_file *file_priv); /* ATI PCIGART support (ati_pcigart.h) */ extern int drm_ati_pcigart_init(struct drm_device *dev, struct drm_ati_pcigart_info * gart_info); extern int drm_ati_pcigart_cleanup(struct drm_device *dev, struct drm_ati_pcigart_info * gart_info); extern drm_dma_handle_t *drm_pci_alloc(struct drm_device *dev, size_t size, size_t align, dma_addr_t maxaddr); extern void __drm_pci_free(struct drm_device *dev, drm_dma_handle_t * dmah); extern void drm_pci_free(struct drm_device *dev, drm_dma_handle_t * dmah); /* Graphics Execution Manager library functions (drm_gem.c) */ int drm_gem_init(struct drm_device *dev); void drm_gem_destroy(struct drm_device *dev); void drm_gem_object_release(struct drm_gem_object *obj); 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); int drm_gem_object_init(struct drm_device *dev, struct drm_gem_object *obj, size_t size); int drm_gem_private_object_init(struct drm_device *dev, struct drm_gem_object *obj, size_t size); void drm_gem_object_handle_free(struct drm_gem_object *obj); int drm_gem_mmap_single(struct drm_device *dev, vm_ooffset_t *offset, vm_size_t size, struct vm_object **obj_res, int nprot); void drm_gem_pager_dtr(void *obj); #include static inline void drm_gem_object_reference(struct drm_gem_object *obj) { KASSERT(obj->refcount > 0, ("Dangling obj %p", obj)); refcount_acquire(&obj->refcount); } static inline void drm_gem_object_unreference(struct drm_gem_object *obj) { if (obj == NULL) return; if (refcount_release(&obj->refcount)) drm_gem_object_free(obj); } static inline void drm_gem_object_unreference_unlocked(struct drm_gem_object *obj) { if (obj != NULL) { struct drm_device *dev = obj->dev; DRM_LOCK(dev); drm_gem_object_unreference(obj); DRM_UNLOCK(dev); } } int drm_gem_handle_create(struct drm_file *file_priv, struct drm_gem_object *obj, u32 *handlep); int drm_gem_handle_delete(struct drm_file *filp, u32 handle); static inline void drm_gem_object_handle_reference(struct drm_gem_object *obj) { drm_gem_object_reference(obj); atomic_inc(&obj->handle_count); } static inline void drm_gem_object_handle_unreference(struct drm_gem_object *obj) { if (obj == NULL) return; if (atomic_read(&obj->handle_count) == 0) 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 */ if (atomic_dec_and_test(&obj->handle_count)) drm_gem_object_handle_free(obj); drm_gem_object_unreference(obj); } static inline void drm_gem_object_handle_unreference_unlocked(struct drm_gem_object *obj) { if (obj == NULL) return; if (atomic_read(&obj->handle_count) == 0) 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 */ if (atomic_dec_and_test(&obj->handle_count)) drm_gem_object_handle_free(obj); drm_gem_object_unreference_unlocked(obj); } void drm_gem_free_mmap_offset(struct drm_gem_object *obj); int drm_gem_create_mmap_offset(struct drm_gem_object *obj); struct drm_gem_object *drm_gem_object_lookup(struct drm_device *dev, struct drm_file *filp, u32 handle); int drm_gem_close_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_gem_flink_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_gem_open_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); void drm_gem_open(struct drm_device *dev, struct drm_file *file_private); void drm_gem_release(struct drm_device *dev, struct drm_file *file_private); extern void drm_core_ioremap(struct drm_local_map *map, struct drm_device *dev); extern void drm_core_ioremap_wc(struct drm_local_map *map, struct drm_device *dev); extern void drm_core_ioremapfree(struct drm_local_map *map, struct drm_device *dev); static __inline__ struct drm_local_map *drm_core_findmap(struct drm_device *dev, unsigned int token) { struct drm_map_list *_entry; list_for_each_entry(_entry, &dev->maplist, head) if (_entry->user_token == token) return _entry->map; return NULL; } static __inline__ void drm_core_dropmap(struct drm_local_map *map) { } extern int drm_fill_in_dev(struct drm_device *dev, struct drm_driver *driver); extern void drm_cancel_fill_in_dev(struct drm_device *dev); int drm_get_minor(struct drm_device *dev, struct drm_minor **minor, int type); /*@}*/ /* PCI section */ int drm_pci_device_is_agp(struct drm_device *dev); int drm_pci_device_is_pcie(struct drm_device *dev); extern int drm_get_pci_dev(device_t kdev, struct drm_device *dev, struct drm_driver *driver); #define DRM_PCIE_SPEED_25 1 #define DRM_PCIE_SPEED_50 2 #define DRM_PCIE_SPEED_80 4 extern int drm_pcie_get_speed_cap_mask(struct drm_device *dev, u32 *speed_mask); #define drm_can_sleep() (DRM_HZ & 1) /* FreeBSD specific -- should be moved to drm_os_freebsd.h */ #define DRM_GEM_MAPPING_MASK (3ULL << 62) #define DRM_GEM_MAPPING_KEY (2ULL << 62) /* Non-canonical address form */ #define DRM_GEM_MAX_IDX 0x3fffff #define DRM_GEM_MAPPING_IDX(o) (((o) >> 40) & DRM_GEM_MAX_IDX) #define DRM_GEM_MAPPING_OFF(i) (((uint64_t)(i)) << 40) #define DRM_GEM_MAPPING_MAPOFF(o) \ ((o) & ~(DRM_GEM_MAPPING_OFF(DRM_GEM_MAX_IDX) | DRM_GEM_MAPPING_KEY)) SYSCTL_DECL(_hw_drm); #define DRM_DEV_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) #define DRM_DEV_UID 0 #define DRM_DEV_GID 0 #define DRM_WAKEUP(w) wakeup((void *)w) #define DRM_WAKEUP_INT(w) wakeup(w) #define DRM_INIT_WAITQUEUE(queue) do {(void)(queue);} while (0) #define DRM_CURPROC curthread #define DRM_STRUCTPROC struct thread #define DRM_SPINTYPE struct mtx #define DRM_SPININIT(l,name) mtx_init(l, name, NULL, MTX_DEF) #define DRM_SPINUNINIT(l) mtx_destroy(l) #define DRM_SPINLOCK(l) mtx_lock(l) #define DRM_SPINUNLOCK(u) mtx_unlock(u) #define DRM_SPINLOCK_IRQSAVE(l, irqflags) do { \ mtx_lock(l); \ (void)irqflags; \ } while (0) #define DRM_SPINUNLOCK_IRQRESTORE(u, irqflags) mtx_unlock(u) #define DRM_SPINLOCK_ASSERT(l) mtx_assert(l, MA_OWNED) #define DRM_LOCK_SLEEP(dev, chan, flags, msg, timeout) \ (sx_sleep((chan), &(dev)->dev_struct_lock, (flags), (msg), (timeout))) #if defined(INVARIANTS) #define DRM_LOCK_ASSERT(dev) sx_assert(&(dev)->dev_struct_lock, SA_XLOCKED) #define DRM_UNLOCK_ASSERT(dev) sx_assert(&(dev)->dev_struct_lock, SA_UNLOCKED) #else #define DRM_LOCK_ASSERT(d) #define DRM_UNLOCK_ASSERT(d) #endif #define DRM_SYSCTL_HANDLER_ARGS (SYSCTL_HANDLER_ARGS) enum { DRM_IS_NOT_AGP, DRM_IS_AGP, DRM_MIGHT_BE_AGP }; #define DRM_VERIFYAREA_READ( uaddr, size ) \ (!useracc(__DECONST(caddr_t, uaddr), size, VM_PROT_READ)) #define DRM_COPY_TO_USER(user, kern, size) \ copyout(kern, user, size) #define DRM_COPY_FROM_USER(kern, user, size) \ copyin(user, kern, size) #define DRM_COPY_FROM_USER_UNCHECKED(arg1, arg2, arg3) \ copyin(arg2, arg1, arg3) #define DRM_COPY_TO_USER_UNCHECKED(arg1, arg2, arg3) \ copyout(arg2, arg1, arg3) #define DRM_GET_USER_UNCHECKED(val, uaddr) \ ((val) = fuword32(uaddr), 0) #define DRM_GET_PRIV_SAREA(_dev, _ctx, _map) do { \ (_map) = (_dev)->context_sareas[_ctx]; \ } while(0) /* Returns -errno to shared code */ #define DRM_WAIT_ON( ret, queue, timeout, condition ) \ for ( ret = 0 ; !ret && !(condition) ; ) { \ DRM_UNLOCK(dev); \ mtx_lock(&dev->irq_lock); \ if (!(condition)) \ ret = -mtx_sleep(&(queue), &dev->irq_lock, \ PCATCH, "drmwtq", (timeout)); \ if (ret == -ERESTART) \ ret = -ERESTARTSYS; \ mtx_unlock(&dev->irq_lock); \ DRM_LOCK(dev); \ } #define dev_err(dev, fmt, ...) \ device_printf((dev), "error: " fmt, ## __VA_ARGS__) #define dev_warn(dev, fmt, ...) \ device_printf((dev), "warning: " fmt, ## __VA_ARGS__) #define dev_info(dev, fmt, ...) \ device_printf((dev), "info: " fmt, ## __VA_ARGS__) #define dev_dbg(dev, fmt, ...) do { \ if ((drm_debug& DRM_DEBUGBITS_KMS) != 0) { \ device_printf((dev), "debug: " fmt, ## __VA_ARGS__); \ } \ } while (0) struct drm_msi_blacklist_entry { int vendor; int device; }; struct drm_vblank_info { wait_queue_head_t queue; /* vblank wait queue */ atomic_t count; /* number of VBLANK interrupts */ /* (driver must alloc the right number of counters) */ atomic_t refcount; /* number of users of vblank interrupts */ u32 last; /* protected by dev->vbl_lock, used */ /* for wraparound handling */ int enabled; /* so we don't call enable more than */ /* once per disable */ int inmodeset; /* Display driver is setting mode */ }; #ifndef DMA_BIT_MASK #define DMA_BIT_MASK(n) (((n) == 64) ? ~0ULL : (1ULL<<(n)) - 1) #endif #define upper_32_bits(n) ((u32)(((n) >> 16) >> 16)) enum dmi_field { DMI_NONE, DMI_BIOS_VENDOR, DMI_BIOS_VERSION, DMI_BIOS_DATE, DMI_SYS_VENDOR, DMI_PRODUCT_NAME, DMI_PRODUCT_VERSION, DMI_PRODUCT_SERIAL, DMI_PRODUCT_UUID, DMI_BOARD_VENDOR, DMI_BOARD_NAME, DMI_BOARD_VERSION, DMI_BOARD_SERIAL, DMI_BOARD_ASSET_TAG, DMI_CHASSIS_VENDOR, DMI_CHASSIS_TYPE, DMI_CHASSIS_VERSION, DMI_CHASSIS_SERIAL, DMI_CHASSIS_ASSET_TAG, DMI_STRING_MAX, }; struct dmi_strmatch { unsigned char slot; char substr[79]; }; struct dmi_system_id { int (*callback)(const struct dmi_system_id *); const char *ident; struct dmi_strmatch matches[4]; }; #define DMI_MATCH(a, b) {(a), (b)} bool dmi_check_system(const struct dmi_system_id *); /* Device setup support (drm_drv.c) */ int drm_probe_helper(device_t kdev, drm_pci_id_list_t *idlist); int drm_attach_helper(device_t kdev, drm_pci_id_list_t *idlist, struct drm_driver *driver); int drm_generic_detach(device_t kdev); void drm_event_wakeup(struct drm_pending_event *e); int drm_add_busid_modesetting(struct drm_device *dev, struct sysctl_ctx_list *ctx, struct sysctl_oid *top); /* Buffer management support (drm_bufs.c) */ unsigned long drm_get_resource_start(struct drm_device *dev, unsigned int resource); unsigned long drm_get_resource_len(struct drm_device *dev, unsigned int resource); /* IRQ support (drm_irq.c) */ irqreturn_t drm_irq_handler(DRM_IRQ_ARGS); void drm_driver_irq_preinstall(struct drm_device *dev); void drm_driver_irq_postinstall(struct drm_device *dev); void drm_driver_irq_uninstall(struct drm_device *dev); -/* AGP/PCI Express/GART support (drm_agpsupport.c) */ -void *drm_agp_allocate_memory(size_t pages, u32 type); -int drm_agp_free_memory(void *handle); -int drm_agp_bind_memory(void *handle, off_t start); -int drm_agp_unbind_memory(void *handle); - /* sysctl support (drm_sysctl.h) */ extern int drm_sysctl_init(struct drm_device *dev); extern int drm_sysctl_cleanup(struct drm_device *dev); int drm_version(struct drm_device *dev, void *data, struct drm_file *file_priv); /* consistent PCI memory functions (drm_pci.c) */ int drm_pci_set_busid(struct drm_device *dev, struct drm_master *master); int drm_pci_set_unique(struct drm_device *dev, struct drm_master *master, struct drm_unique *u); int drm_pci_agp_init(struct drm_device *dev); int drm_pci_enable_msi(struct drm_device *dev); void drm_pci_disable_msi(struct drm_device *dev); struct ttm_bo_device; int ttm_bo_mmap_single(struct ttm_bo_device *bdev, vm_ooffset_t *offset, vm_size_t size, struct vm_object **obj_res, int nprot); struct ttm_buffer_object; void ttm_bo_release_mmap(struct ttm_buffer_object *bo); #endif /* __KERNEL__ */ #endif Index: stable/10/sys/dev/drm2/drm_fops.c =================================================================== --- stable/10/sys/dev/drm2/drm_fops.c (revision 295622) +++ stable/10/sys/dev/drm2/drm_fops.c (revision 295623) @@ -1,603 +1,603 @@ /** * \file drm_fops.c * File operations for DRM * * \author Rickard E. (Rik) Faith * \author Daryll Strauss * \author Gareth Hughes */ /* * Created: Mon Jan 4 08:58:31 1999 by faith@valinux.com * * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. * 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 * VA LINUX SYSTEMS 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. */ #include __FBSDID("$FreeBSD$"); #include static int drm_open_helper(struct cdev *kdev, int flags, int fmt, DRM_STRUCTPROC *p, struct drm_device *dev); static int drm_setup(struct drm_device * dev) { int i; int ret; if (dev->driver->firstopen) { ret = dev->driver->firstopen(dev); if (ret != 0) return ret; } atomic_set(&dev->ioctl_count, 0); atomic_set(&dev->vma_count, 0); if (drm_core_check_feature(dev, DRIVER_HAVE_DMA) && !drm_core_check_feature(dev, DRIVER_MODESET)) { dev->buf_use = 0; atomic_set(&dev->buf_alloc, 0); i = drm_dma_setup(dev); if (i < 0) return i; } /* * FIXME Linux<->FreeBSD: counter incremented in drm_open() and * reset to 0 here. */ #if 0 for (i = 0; i < ARRAY_SIZE(dev->counts); i++) atomic_set(&dev->counts[i], 0); #endif dev->sigdata.lock = NULL; dev->context_flag = 0; dev->interrupt_flag = 0; dev->dma_flag = 0; dev->last_context = 0; dev->last_switch = 0; dev->last_checked = 0; DRM_INIT_WAITQUEUE(&dev->context_wait); dev->if_version = 0; #ifdef FREEBSD_NOTYET dev->ctx_start = 0; dev->lck_start = 0; dev->buf_async = NULL; DRM_INIT_WAITQUEUE(&dev->buf_readers); DRM_INIT_WAITQUEUE(&dev->buf_writers); #endif /* FREEBSD_NOTYET */ DRM_DEBUG("\n"); /* * The kernel's context could be created here, but is now created * in drm_dma_enqueue. This is more resource-efficient for * hardware that does not do DMA, but may mean that * drm_select_queue fails between the time the interrupt is * initialized and the time the queues are initialized. */ return 0; } /** * Open file. * * \param inode device inode * \param filp file pointer. * \return zero on success or a negative number on failure. * * Searches the DRM device with the same minor number, calls open_helper(), and * increments the device open count. If the open count was previous at zero, * i.e., it's the first that the device is open, then calls setup(). */ int drm_open(struct cdev *kdev, int flags, int fmt, DRM_STRUCTPROC *p) { struct drm_device *dev = NULL; struct drm_minor *minor; int retcode = 0; int need_setup = 0; minor = kdev->si_drv1; if (!minor) return ENODEV; if (!(dev = minor->dev)) return ENODEV; sx_xlock(&drm_global_mutex); /* - * FIXME Linux<->FreeBSD: On Linux, counter updated outisde + * FIXME Linux<->FreeBSD: On Linux, counter updated outside * global mutex. */ if (!dev->open_count++) need_setup = 1; retcode = drm_open_helper(kdev, flags, fmt, p, dev); if (retcode) { sx_xunlock(&drm_global_mutex); return (-retcode); } atomic_inc(&dev->counts[_DRM_STAT_OPENS]); if (need_setup) { retcode = drm_setup(dev); if (retcode) goto err_undo; } sx_xunlock(&drm_global_mutex); return 0; err_undo: mtx_lock(&Giant); /* FIXME: Giant required? */ device_unbusy(dev->dev); mtx_unlock(&Giant); dev->open_count--; sx_xunlock(&drm_global_mutex); return -retcode; } EXPORT_SYMBOL(drm_open); /** * Called whenever a process opens /dev/drm. * * \param inode device inode. * \param filp file pointer. * \param dev device. * \return zero on success or a negative number on failure. * * Creates and initializes a drm_file structure for the file private data in \p * filp and add it into the double linked list in \p dev. */ static int drm_open_helper(struct cdev *kdev, int flags, int fmt, DRM_STRUCTPROC *p, struct drm_device *dev) { struct drm_file *priv; int ret; if (flags & O_EXCL) return -EBUSY; /* No exclusive opens */ if (dev->switch_power_state != DRM_SWITCH_POWER_ON) return -EINVAL; DRM_DEBUG("pid = %d, device = %s\n", DRM_CURRENTPID, devtoname(kdev)); priv = malloc(sizeof(*priv), DRM_MEM_FILES, M_NOWAIT | M_ZERO); if (!priv) return -ENOMEM; priv->uid = p->td_ucred->cr_svuid; priv->pid = p->td_proc->p_pid; priv->minor = kdev->si_drv1; priv->ioctl_count = 0; /* for compatibility root is always authenticated */ priv->authenticated = DRM_SUSER(p); priv->lock_count = 0; INIT_LIST_HEAD(&priv->lhead); INIT_LIST_HEAD(&priv->fbs); INIT_LIST_HEAD(&priv->event_list); priv->event_space = 4096; /* set aside 4k for event buffer */ if (dev->driver->driver_features & DRIVER_GEM) drm_gem_open(dev, priv); #ifdef FREEBSD_NOTYET if (drm_core_check_feature(dev, DRIVER_PRIME)) drm_prime_init_file_private(&priv->prime); #endif /* FREEBSD_NOTYET */ if (dev->driver->open) { ret = dev->driver->open(dev, priv); if (ret < 0) goto out_free; } /* if there is no current master make this fd it */ DRM_LOCK(dev); if (!priv->minor->master) { /* create a new master */ priv->minor->master = drm_master_create(priv->minor); if (!priv->minor->master) { DRM_UNLOCK(dev); ret = -ENOMEM; goto out_free; } priv->is_master = 1; /* take another reference for the copy in the local file priv */ priv->master = drm_master_get(priv->minor->master); priv->authenticated = 1; DRM_UNLOCK(dev); if (dev->driver->master_create) { ret = dev->driver->master_create(dev, priv->master); if (ret) { DRM_LOCK(dev); /* drop both references if this fails */ drm_master_put(&priv->minor->master); drm_master_put(&priv->master); DRM_UNLOCK(dev); goto out_free; } } DRM_LOCK(dev); if (dev->driver->master_set) { ret = dev->driver->master_set(dev, priv, true); if (ret) { /* drop both references if this fails */ drm_master_put(&priv->minor->master); drm_master_put(&priv->master); DRM_UNLOCK(dev); goto out_free; } } DRM_UNLOCK(dev); } else { /* get a reference to the master */ priv->master = drm_master_get(priv->minor->master); DRM_UNLOCK(dev); } DRM_LOCK(dev); list_add(&priv->lhead, &dev->filelist); DRM_UNLOCK(dev); mtx_lock(&Giant); /* FIXME: Giant required? */ device_busy(dev->dev); mtx_unlock(&Giant); ret = devfs_set_cdevpriv(priv, drm_release); if (ret != 0) drm_release(priv); return ret; out_free: free(priv, DRM_MEM_FILES); return ret; } static void drm_master_release(struct drm_device *dev, struct drm_file *file_priv) { if (drm_i_have_hw_lock(dev, file_priv)) { DRM_DEBUG("File %p released, freeing lock for context %d\n", file_priv, _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock)); drm_lock_free(&file_priv->master->lock, _DRM_LOCKING_CONTEXT(file_priv->master->lock.hw_lock->lock)); } } static void drm_events_release(struct drm_file *file_priv) { struct drm_device *dev = file_priv->minor->dev; struct drm_pending_event *e, *et; struct drm_pending_vblank_event *v, *vt; unsigned long flags; DRM_SPINLOCK_IRQSAVE(&dev->event_lock, flags); /* Remove pending flips */ list_for_each_entry_safe(v, vt, &dev->vblank_event_list, base.link) if (v->base.file_priv == file_priv) { list_del(&v->base.link); drm_vblank_put(dev, v->pipe); v->base.destroy(&v->base); } /* Remove unconsumed events */ list_for_each_entry_safe(e, et, &file_priv->event_list, link) e->destroy(e); DRM_SPINUNLOCK_IRQRESTORE(&dev->event_lock, flags); } /** * Release file. * * \param inode device inode * \param file_priv DRM file private. * \return zero on success or a negative number on failure. * * If the hardware lock is held then free it, and take it again for the kernel * context since it's necessary to reclaim buffers. Unlink the file private * data from its list and free it. Decreases the open count and if it reaches * zero calls drm_lastclose(). */ void drm_release(void *data) { struct drm_file *file_priv = data; struct drm_device *dev = file_priv->minor->dev; sx_xlock(&drm_global_mutex); DRM_DEBUG("open_count = %d\n", dev->open_count); if (dev->driver->preclose) dev->driver->preclose(dev, file_priv); /* ======================================================== * Begin inline drm_release */ DRM_DEBUG("pid = %d, device = 0x%lx, open_count = %d\n", DRM_CURRENTPID, (long)file_priv->minor->device, dev->open_count); /* Release any auth tokens that might point to this file_priv, (do that under the drm_global_mutex) */ if (file_priv->magic) (void) drm_remove_magic(file_priv->master, file_priv->magic); /* if the master has gone away we can't do anything with the lock */ if (file_priv->minor->master) drm_master_release(dev, file_priv); if (drm_core_check_feature(dev, DRIVER_HAVE_DMA)) drm_core_reclaim_buffers(dev, file_priv); drm_events_release(file_priv); seldrain(&file_priv->event_poll); if (dev->driver->driver_features & DRIVER_MODESET) drm_fb_release(file_priv); if (dev->driver->driver_features & DRIVER_GEM) drm_gem_release(dev, file_priv); #ifdef FREEBSD_NOTYET mutex_lock(&dev->ctxlist_mutex); if (!list_empty(&dev->ctxlist)) { struct drm_ctx_list *pos, *n; list_for_each_entry_safe(pos, n, &dev->ctxlist, head) { if (pos->tag == file_priv && pos->handle != DRM_KERNEL_CONTEXT) { if (dev->driver->context_dtor) dev->driver->context_dtor(dev, pos->handle); drm_ctxbitmap_free(dev, pos->handle); list_del(&pos->head); kfree(pos); --dev->ctx_count; } } } mutex_unlock(&dev->ctxlist_mutex); #endif /* FREEBSD_NOTYET */ DRM_LOCK(dev); if (file_priv->is_master) { struct drm_master *master = file_priv->master; struct drm_file *temp; list_for_each_entry(temp, &dev->filelist, lhead) { if ((temp->master == file_priv->master) && (temp != file_priv)) temp->authenticated = 0; } /** * Since the master is disappearing, so is the * possibility to lock. */ if (master->lock.hw_lock) { if (dev->sigdata.lock == master->lock.hw_lock) dev->sigdata.lock = NULL; master->lock.hw_lock = NULL; master->lock.file_priv = NULL; DRM_WAKEUP_INT(&master->lock.lock_queue); } if (file_priv->minor->master == file_priv->master) { /* drop the reference held my the minor */ if (dev->driver->master_drop) dev->driver->master_drop(dev, file_priv, true); drm_master_put(&file_priv->minor->master); } } /* drop the reference held my the file priv */ drm_master_put(&file_priv->master); file_priv->is_master = 0; list_del(&file_priv->lhead); DRM_UNLOCK(dev); if (dev->driver->postclose) dev->driver->postclose(dev, file_priv); #ifdef FREEBSD_NOTYET if (drm_core_check_feature(dev, DRIVER_PRIME)) drm_prime_destroy_file_private(&file_priv->prime); #endif /* FREEBSD_NOTYET */ free(file_priv, DRM_MEM_FILES); /* ======================================================== * End inline drm_release */ atomic_inc(&dev->counts[_DRM_STAT_CLOSES]); mtx_lock(&Giant); device_unbusy(dev->dev); mtx_unlock(&Giant); if (!--dev->open_count) { if (atomic_read(&dev->ioctl_count)) { DRM_ERROR("Device busy: %d\n", atomic_read(&dev->ioctl_count)); } else drm_lastclose(dev); } sx_xunlock(&drm_global_mutex); } EXPORT_SYMBOL(drm_release); static bool drm_dequeue_event(struct drm_file *file_priv, struct uio *uio, struct drm_pending_event **out) { struct drm_pending_event *e; bool ret = false; /* Already locked in drm_read(). */ /* DRM_SPINLOCK_IRQSAVE(&dev->event_lock, flags); */ *out = NULL; if (list_empty(&file_priv->event_list)) goto out; e = list_first_entry(&file_priv->event_list, struct drm_pending_event, link); if (e->event->length > uio->uio_resid) goto out; file_priv->event_space += e->event->length; list_del(&e->link); *out = e; ret = true; out: /* DRM_SPINUNLOCK_IRQRESTORE(&dev->event_lock, flags); */ return ret; } int drm_read(struct cdev *kdev, struct uio *uio, int ioflag) { struct drm_file *file_priv; struct drm_device *dev; struct drm_pending_event *e; ssize_t error; error = devfs_get_cdevpriv((void **)&file_priv); if (error != 0) { DRM_ERROR("can't find authenticator\n"); return (EINVAL); } dev = drm_get_device_from_kdev(kdev); mtx_lock(&dev->event_lock); while (list_empty(&file_priv->event_list)) { if ((ioflag & O_NONBLOCK) != 0) { error = EAGAIN; goto out; } error = msleep(&file_priv->event_space, &dev->event_lock, PCATCH, "drmrea", 0); if (error != 0) goto out; } while (drm_dequeue_event(file_priv, uio, &e)) { mtx_unlock(&dev->event_lock); error = uiomove(e->event, e->event->length, uio); CTR3(KTR_DRM, "drm_event_dequeued %d %d %d", curproc->p_pid, e->event->type, e->event->length); e->destroy(e); if (error != 0) return (error); mtx_lock(&dev->event_lock); } out: mtx_unlock(&dev->event_lock); return (error); } EXPORT_SYMBOL(drm_read); void drm_event_wakeup(struct drm_pending_event *e) { struct drm_file *file_priv; struct drm_device *dev; file_priv = e->file_priv; dev = file_priv->minor->dev; mtx_assert(&dev->event_lock, MA_OWNED); wakeup(&file_priv->event_space); selwakeup(&file_priv->event_poll); } int drm_poll(struct cdev *kdev, int events, struct thread *td) { struct drm_file *file_priv; struct drm_device *dev; int error, revents; error = devfs_get_cdevpriv((void **)&file_priv); if (error != 0) { DRM_ERROR("can't find authenticator\n"); return (EINVAL); } dev = drm_get_device_from_kdev(kdev); revents = 0; mtx_lock(&dev->event_lock); if ((events & (POLLIN | POLLRDNORM)) != 0) { if (list_empty(&file_priv->event_list)) { CTR0(KTR_DRM, "drm_poll empty list"); selrecord(td, &file_priv->event_poll); } else { revents |= events & (POLLIN | POLLRDNORM); CTR1(KTR_DRM, "drm_poll revents %x", revents); } } mtx_unlock(&dev->event_lock); return (revents); } EXPORT_SYMBOL(drm_poll); int drm_mmap_single(struct cdev *kdev, vm_ooffset_t *offset, vm_size_t size, struct vm_object **obj_res, int nprot) { struct drm_device *dev; dev = drm_get_device_from_kdev(kdev); if (dev->drm_ttm_bdev != NULL) { return (-ttm_bo_mmap_single(dev->drm_ttm_bdev, offset, size, obj_res, nprot)); } else if ((dev->driver->driver_features & DRIVER_GEM) != 0) { return (-drm_gem_mmap_single(dev, offset, size, obj_res, nprot)); } else { return (ENODEV); } } Index: stable/10/sys/dev/drm2/drm_stub.c =================================================================== --- stable/10/sys/dev/drm2/drm_stub.c (revision 295622) +++ stable/10/sys/dev/drm2/drm_stub.c (revision 295623) @@ -1,501 +1,501 @@ /** * \file drm_stub.h * Stub support * * \author Rickard E. (Rik) Faith */ /* * Created: Fri Jan 19 10:48:35 2001 by faith@acm.org * * Copyright 2001 VA Linux Systems, Inc., Sunnyvale, California. * 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 * PRECISION INSIGHT 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. */ #include __FBSDID("$FreeBSD$"); #include #include #ifdef DRM_DEBUG_DEFAULT_ON unsigned int drm_debug = (DRM_DEBUGBITS_DEBUG | DRM_DEBUGBITS_KMS | DRM_DEBUGBITS_FAILED_IOCTL); #else unsigned int drm_debug = 0; /* 1 to enable debug output */ #endif EXPORT_SYMBOL(drm_debug); unsigned int drm_notyet = 0; unsigned int drm_vblank_offdelay = 5000; /* Default to 5000 msecs. */ EXPORT_SYMBOL(drm_vblank_offdelay); unsigned int drm_timestamp_precision = 20; /* Default to 20 usecs. */ EXPORT_SYMBOL(drm_timestamp_precision); /* * Default to use monotonic timestamps for wait-for-vblank and page-flip * complete events. */ unsigned int drm_timestamp_monotonic = 1; MODULE_AUTHOR(CORE_AUTHOR); MODULE_DESCRIPTION(CORE_DESC); MODULE_LICENSE("GPL and additional rights"); MODULE_PARM_DESC(debug, "Enable debug output"); MODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable [msecs]"); MODULE_PARM_DESC(timestamp_precision_usec, "Max. error on timestamps [usecs]"); MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps"); module_param_named(debug, drm_debug, int, 0600); module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600); module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600); module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600); static struct cdevsw drm_cdevsw = { .d_version = D_VERSION, .d_open = drm_open, .d_read = drm_read, .d_ioctl = drm_ioctl, .d_poll = drm_poll, .d_mmap_single = drm_mmap_single, .d_name = "drm", .d_flags = D_TRACKCLOSE }; static int drm_minor_get_id(struct drm_device *dev, int type) { int new_id; new_id = device_get_unit(dev->dev); if (new_id >= 64) return -EINVAL; if (type == DRM_MINOR_CONTROL) { new_id += 64; - } else if (type == DRM_MINOR_RENDER) { - new_id += 128; - } + } else if (type == DRM_MINOR_RENDER) { + new_id += 128; + } return new_id; } struct drm_master *drm_master_create(struct drm_minor *minor) { struct drm_master *master; master = malloc(sizeof(*master), DRM_MEM_KMS, M_NOWAIT | M_ZERO); if (!master) return NULL; refcount_init(&master->refcount, 1); mtx_init(&master->lock.spinlock, "drm_master__lock__spinlock", NULL, MTX_DEF); DRM_INIT_WAITQUEUE(&master->lock.lock_queue); drm_ht_create(&master->magiclist, DRM_MAGIC_HASH_ORDER); INIT_LIST_HEAD(&master->magicfree); master->minor = minor; list_add_tail(&master->head, &minor->master_list); return master; } struct drm_master *drm_master_get(struct drm_master *master) { refcount_acquire(&master->refcount); return master; } EXPORT_SYMBOL(drm_master_get); static void drm_master_destroy(struct drm_master *master) { struct drm_magic_entry *pt, *next; struct drm_device *dev = master->minor->dev; struct drm_map_list *r_list, *list_temp; list_del(&master->head); if (dev->driver->master_destroy) dev->driver->master_destroy(dev, master); list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head) { if (r_list->master == master) { drm_rmmap_locked(dev, r_list->map); r_list = NULL; } } if (master->unique) { free(master->unique, DRM_MEM_DRIVER); master->unique = NULL; master->unique_len = 0; } list_for_each_entry_safe(pt, next, &master->magicfree, head) { list_del(&pt->head); drm_ht_remove_item(&master->magiclist, &pt->hash_item); free(pt, DRM_MEM_MAGIC); } drm_ht_remove(&master->magiclist); free(master, DRM_MEM_KMS); } void drm_master_put(struct drm_master **master) { if (refcount_release(&(*master)->refcount)) drm_master_destroy(*master); *master = NULL; } EXPORT_SYMBOL(drm_master_put); int drm_setmaster_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { int ret; if (file_priv->is_master) return 0; if (file_priv->minor->master && file_priv->minor->master != file_priv->master) return -EINVAL; if (!file_priv->master) return -EINVAL; if (file_priv->minor->master) return -EINVAL; DRM_LOCK(dev); file_priv->minor->master = drm_master_get(file_priv->master); file_priv->is_master = 1; if (dev->driver->master_set) { ret = dev->driver->master_set(dev, file_priv, false); if (unlikely(ret != 0)) { file_priv->is_master = 0; drm_master_put(&file_priv->minor->master); } } DRM_UNLOCK(dev); return 0; } int drm_dropmaster_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { if (!file_priv->is_master) return -EINVAL; if (!file_priv->minor->master) return -EINVAL; DRM_LOCK(dev); if (dev->driver->master_drop) dev->driver->master_drop(dev, file_priv, false); drm_master_put(&file_priv->minor->master); file_priv->is_master = 0; DRM_UNLOCK(dev); return 0; } int drm_fill_in_dev(struct drm_device *dev, struct drm_driver *driver) { int retcode, i; INIT_LIST_HEAD(&dev->filelist); INIT_LIST_HEAD(&dev->ctxlist); INIT_LIST_HEAD(&dev->maplist); INIT_LIST_HEAD(&dev->vblank_event_list); mtx_init(&dev->irq_lock, "drmirq", NULL, MTX_DEF); mtx_init(&dev->count_lock, "drmcount", NULL, MTX_DEF); mtx_init(&dev->event_lock, "drmev", NULL, MTX_DEF); sx_init(&dev->dev_struct_lock, "drmslk"); mtx_init(&dev->ctxlist_mutex, "drmctxlist", NULL, MTX_DEF); mtx_init(&dev->pcir_lock, "drmpcir", NULL, MTX_DEF); if (drm_ht_create(&dev->map_hash, 12)) { return -ENOMEM; } /* the DRM has 6 basic counters */ dev->counters = 6; dev->types[0] = _DRM_STAT_LOCK; dev->types[1] = _DRM_STAT_OPENS; dev->types[2] = _DRM_STAT_CLOSES; dev->types[3] = _DRM_STAT_IOCTLS; dev->types[4] = _DRM_STAT_LOCKS; dev->types[5] = _DRM_STAT_UNLOCKS; /* * FIXME Linux<->FreeBSD: this is done in drm_setup() on Linux. */ for (i = 0; i < ARRAY_SIZE(dev->counts); i++) atomic_set(&dev->counts[i], 0); dev->driver = driver; retcode = drm_pci_agp_init(dev); if (retcode) goto error_out_unreg; retcode = drm_ctxbitmap_init(dev); if (retcode) { DRM_ERROR("Cannot allocate memory for context bitmap.\n"); goto error_out_unreg; } if (driver->driver_features & DRIVER_GEM) { retcode = drm_gem_init(dev); if (retcode) { DRM_ERROR("Cannot initialize graphics execution " "manager (GEM)\n"); goto error_out_unreg; } } retcode = drm_sysctl_init(dev); if (retcode != 0) { DRM_ERROR("Failed to create hw.dri sysctl entry: %d\n", retcode); } return 0; error_out_unreg: drm_cancel_fill_in_dev(dev); return retcode; } EXPORT_SYMBOL(drm_fill_in_dev); void drm_cancel_fill_in_dev(struct drm_device *dev) { struct drm_driver *driver; driver = dev->driver; drm_sysctl_cleanup(dev); if (driver->driver_features & DRIVER_GEM) drm_gem_destroy(dev); drm_ctxbitmap_cleanup(dev); if (drm_core_has_MTRR(dev) && drm_core_has_AGP(dev) && dev->agp && dev->agp->agp_mtrr >= 0) { int retval; retval = drm_mtrr_del(dev->agp->agp_mtrr, dev->agp->agp_info.ai_aperture_base, dev->agp->agp_info.ai_aperture_size, DRM_MTRR_WC); DRM_DEBUG("mtrr_del=%d\n", retval); } free(dev->agp, DRM_MEM_AGPLISTS); dev->agp = NULL; drm_ht_remove(&dev->map_hash); mtx_destroy(&dev->irq_lock); mtx_destroy(&dev->count_lock); mtx_destroy(&dev->event_lock); sx_destroy(&dev->dev_struct_lock); mtx_destroy(&dev->ctxlist_mutex); mtx_destroy(&dev->pcir_lock); } /** * Get a secondary minor number. * * \param dev device data structure * \param sec-minor structure to hold the assigned minor * \return negative number on failure. * * Search an empty entry and initialize it to the given parameters, and * create the proc init entry via proc_init(). This routines assigns * minor numbers to secondary heads of multi-headed cards */ int drm_get_minor(struct drm_device *dev, struct drm_minor **minor, int type) { struct drm_minor *new_minor; int ret; int minor_id; const char *minor_devname; DRM_DEBUG("\n"); minor_id = drm_minor_get_id(dev, type); if (minor_id < 0) return minor_id; new_minor = malloc(sizeof(struct drm_minor), DRM_MEM_MINOR, M_NOWAIT | M_ZERO); if (!new_minor) { ret = -ENOMEM; goto err_idr; } new_minor->type = type; new_minor->dev = dev; new_minor->index = minor_id; INIT_LIST_HEAD(&new_minor->master_list); new_minor->buf_sigio = NULL; switch (type) { case DRM_MINOR_CONTROL: minor_devname = "dri/controlD%d"; break; case DRM_MINOR_RENDER: minor_devname = "dri/renderD%d"; break; default: minor_devname = "dri/card%d"; break; } ret = make_dev_p(MAKEDEV_WAITOK | MAKEDEV_CHECKNAME, &new_minor->device, &drm_cdevsw, 0, DRM_DEV_UID, DRM_DEV_GID, DRM_DEV_MODE, minor_devname, minor_id); if (ret) { DRM_ERROR("Failed to create cdev: %d\n", ret); goto err_mem; } new_minor->device->si_drv1 = new_minor; *minor = new_minor; DRM_DEBUG("new minor assigned %d\n", minor_id); return 0; err_mem: free(new_minor, DRM_MEM_MINOR); err_idr: *minor = NULL; return ret; } EXPORT_SYMBOL(drm_get_minor); /** * Put a secondary minor number. * * \param sec_minor - structure to be released * \return always zero * * Cleans up the proc resources. Not legal for this to be the * last minor released. * */ int drm_put_minor(struct drm_minor **minor_p) { struct drm_minor *minor = *minor_p; DRM_DEBUG("release secondary minor %d\n", minor->index); funsetown(&minor->buf_sigio); destroy_dev(minor->device); free(minor, DRM_MEM_MINOR); *minor_p = NULL; return 0; } EXPORT_SYMBOL(drm_put_minor); /** * Called via drm_exit() at module unload time or when pci device is * unplugged. * * Cleans up all DRM device, calling drm_lastclose(). * */ void drm_put_dev(struct drm_device *dev) { struct drm_driver *driver; struct drm_map_list *r_list, *list_temp; DRM_DEBUG("\n"); if (!dev) { DRM_ERROR("cleanup called no dev\n"); return; } driver = dev->driver; drm_lastclose(dev); if (drm_core_has_MTRR(dev) && drm_core_has_AGP(dev) && dev->agp && dev->agp->agp_mtrr >= 0) { int retval; retval = drm_mtrr_del(dev->agp->agp_mtrr, dev->agp->agp_info.ai_aperture_base, dev->agp->agp_info.ai_aperture_size, DRM_MTRR_WC); DRM_DEBUG("mtrr_del=%d\n", retval); } if (drm_core_check_feature(dev, DRIVER_MODESET)) drm_mode_group_free(&dev->primary->mode_group); if (dev->driver->unload) dev->driver->unload(dev); drm_sysctl_cleanup(dev); if (drm_core_has_AGP(dev) && dev->agp) { free(dev->agp, DRM_MEM_AGPLISTS); dev->agp = NULL; } drm_vblank_cleanup(dev); list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head) drm_rmmap(dev, r_list->map); drm_ht_remove(&dev->map_hash); drm_ctxbitmap_cleanup(dev); if (drm_core_check_feature(dev, DRIVER_MODESET)) drm_put_minor(&dev->control); if (driver->driver_features & DRIVER_GEM) drm_gem_destroy(dev); drm_put_minor(&dev->primary); mtx_destroy(&dev->irq_lock); mtx_destroy(&dev->count_lock); mtx_destroy(&dev->event_lock); sx_destroy(&dev->dev_struct_lock); mtx_destroy(&dev->ctxlist_mutex); mtx_destroy(&dev->pcir_lock); #ifdef FREEBSD_NOTYET list_del(&dev->driver_item); #endif /* FREEBSD_NOTYET */ } EXPORT_SYMBOL(drm_put_dev); Index: stable/10/sys/dev/drm2/i915/i915_dma.c =================================================================== --- stable/10/sys/dev/drm2/i915/i915_dma.c (revision 295622) +++ stable/10/sys/dev/drm2/i915/i915_dma.c (revision 295623) @@ -1,1705 +1,1707 @@ /* i915_dma.c -- DMA support for the I915 -*- linux-c -*- */ /*- * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. * 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 TUNGSTEN GRAPHICS 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. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #define LP_RING(d) (&((struct drm_i915_private *)(d))->rings[RCS]) #define BEGIN_LP_RING(n) \ intel_ring_begin(LP_RING(dev_priv), (n)) #define OUT_RING(x) \ intel_ring_emit(LP_RING(dev_priv), x) #define ADVANCE_LP_RING() \ intel_ring_advance(LP_RING(dev_priv)) #define RING_LOCK_TEST_WITH_RETURN(dev, file) do { \ if (LP_RING(dev->dev_private)->obj == NULL) \ LOCK_TEST_WITH_RETURN(dev, file); \ } while (0) static inline u32 intel_read_legacy_status_page(struct drm_i915_private *dev_priv, int reg) { if (I915_NEED_GFX_HWS(dev_priv->dev)) return ((volatile u32*)(dev_priv->dri1.gfx_hws_cpu_addr))[reg]; else return intel_read_status_page(LP_RING(dev_priv), reg); } #define READ_HWSP(dev_priv, reg) intel_read_legacy_status_page(dev_priv, reg) #define READ_BREADCRUMB(dev_priv) READ_HWSP(dev_priv, I915_BREADCRUMB_INDEX) #define I915_BREADCRUMB_INDEX 0x21 void i915_update_dri1_breadcrumb(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_master_private *master_priv; if (dev->primary->master) { master_priv = dev->primary->master->driver_priv; if (master_priv->sarea_priv) master_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); } } static void i915_write_hws_pga(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; u32 addr; addr = dev_priv->status_page_dmah->busaddr; if (INTEL_INFO(dev)->gen >= 4) addr |= (dev_priv->status_page_dmah->busaddr >> 28) & 0xf0; I915_WRITE(HWS_PGA, addr); } /** * Sets up the hardware status page for devices that need a physical address * in the register. */ static int i915_init_phys_hws(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; struct intel_ring_buffer *ring = LP_RING(dev_priv); /* * Program Hardware Status Page * XXXKIB Keep 4GB limit for allocation for now. This method * of allocation is used on <= 965 hardware, that has several * erratas regarding the use of physical memory > 4 GB. */ dev_priv->status_page_dmah = drm_pci_alloc(dev, PAGE_SIZE, PAGE_SIZE, BUS_SPACE_MAXADDR); if (!dev_priv->status_page_dmah) { DRM_ERROR("Can not allocate hardware status page\n"); return -ENOMEM; } ring->status_page.page_addr = dev_priv->hw_status_page = dev_priv->status_page_dmah->vaddr; dev_priv->dma_status_page = dev_priv->status_page_dmah->busaddr; memset(dev_priv->hw_status_page, 0, PAGE_SIZE); i915_write_hws_pga(dev); DRM_DEBUG("Enabled hardware status page, phys %jx\n", (uintmax_t)dev_priv->dma_status_page); return 0; } /** * Frees the hardware status page, whether it's a physical address or a virtual * address set up by the X Server. */ static void i915_free_hws(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; struct intel_ring_buffer *ring = LP_RING(dev_priv); if (dev_priv->status_page_dmah) { drm_pci_free(dev, dev_priv->status_page_dmah); dev_priv->status_page_dmah = NULL; } if (dev_priv->status_gfx_addr) { dev_priv->status_gfx_addr = 0; ring->status_page.gfx_addr = 0; pmap_unmapdev((vm_offset_t)dev_priv->dri1.gfx_hws_cpu_addr, PAGE_SIZE); } /* Need to rewrite hardware status page */ I915_WRITE(HWS_PGA, 0x1ffff000); } void i915_kernel_lost_context(struct drm_device * dev) { drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_master_private *master_priv; struct intel_ring_buffer *ring = LP_RING(dev_priv); /* * We should never lose context on the ring with modesetting * as we don't expose it to userspace */ if (drm_core_check_feature(dev, DRIVER_MODESET)) return; ring->head = I915_READ_HEAD(ring) & HEAD_ADDR; ring->tail = I915_READ_TAIL(ring) & TAIL_ADDR; ring->space = ring->head - (ring->tail + 8); if (ring->space < 0) ring->space += ring->size; if (!dev->primary->master) return; master_priv = dev->primary->master->driver_priv; if (ring->head == ring->tail && master_priv->sarea_priv) master_priv->sarea_priv->perf_boxes |= I915_BOX_RING_EMPTY; } static int i915_dma_cleanup(struct drm_device * dev) { drm_i915_private_t *dev_priv = dev->dev_private; int i; /* Make sure interrupts are disabled here because the uninstall ioctl * may not have been called from userspace and after dev_private * is freed, it's too late. */ if (dev->irq_enabled) drm_irq_uninstall(dev); DRM_LOCK(dev); for (i = 0; i < I915_NUM_RINGS; i++) intel_cleanup_ring_buffer(&dev_priv->rings[i]); DRM_UNLOCK(dev); /* Clear the HWS virtual address at teardown */ if (I915_NEED_GFX_HWS(dev)) i915_free_hws(dev); return 0; } static int i915_initialize(struct drm_device * dev, drm_i915_init_t * init) { drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv; int ret; master_priv->sarea = drm_getsarea(dev); if (master_priv->sarea) { master_priv->sarea_priv = (drm_i915_sarea_t *) ((u8 *)master_priv->sarea->handle + init->sarea_priv_offset); } else { DRM_DEBUG_DRIVER("sarea not found assuming DRI2 userspace\n"); } if (init->ring_size != 0) { if (LP_RING(dev_priv)->obj != NULL) { i915_dma_cleanup(dev); DRM_ERROR("Client tried to initialize ringbuffer in " "GEM mode\n"); return -EINVAL; } ret = intel_render_ring_init_dri(dev, init->ring_start, init->ring_size); if (ret) { i915_dma_cleanup(dev); return ret; } } dev_priv->cpp = init->cpp; dev_priv->back_offset = init->back_offset; dev_priv->front_offset = init->front_offset; dev_priv->current_page = 0; if (master_priv->sarea_priv) master_priv->sarea_priv->pf_current_page = 0; /* Allow hardware batchbuffers unless told otherwise. */ dev_priv->dri1.allow_batchbuffer = 1; return 0; } static int i915_dma_resume(struct drm_device * dev) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; struct intel_ring_buffer *ring = LP_RING(dev_priv); DRM_DEBUG("\n"); if (ring->virtual_start == NULL) { DRM_ERROR("can not ioremap virtual address for" " ring buffer\n"); return -ENOMEM; } /* Program Hardware Status Page */ if (!ring->status_page.page_addr) { DRM_ERROR("Can not find hardware status page\n"); return -EINVAL; } DRM_DEBUG("hw status page @ %p\n", ring->status_page.page_addr); if (ring->status_page.gfx_addr != 0) intel_ring_setup_status_page(ring); else i915_write_hws_pga(dev); DRM_DEBUG("Enabled hardware status page\n"); return 0; } static int i915_dma_init(struct drm_device *dev, void *data, struct drm_file *file_priv) { drm_i915_init_t *init = data; int retcode = 0; if (drm_core_check_feature(dev, DRIVER_MODESET)) return -ENODEV; switch (init->func) { case I915_INIT_DMA: retcode = i915_initialize(dev, init); break; case I915_CLEANUP_DMA: retcode = i915_dma_cleanup(dev); break; case I915_RESUME_DMA: retcode = i915_dma_resume(dev); break; default: retcode = -EINVAL; break; } return retcode; } /* Implement basically the same security restrictions as hardware does * for MI_BATCH_NON_SECURE. These can be made stricter at any time. * * Most of the calculations below involve calculating the size of a * particular instruction. It's important to get the size right as * that tells us where the next instruction to check is. Any illegal * instruction detected will be given a size of zero, which is a * signal to abort the rest of the buffer. */ static int do_validate_cmd(int cmd) { switch (((cmd >> 29) & 0x7)) { case 0x0: switch ((cmd >> 23) & 0x3f) { case 0x0: return 1; /* MI_NOOP */ case 0x4: return 1; /* MI_FLUSH */ default: return 0; /* disallow everything else */ } break; case 0x1: return 0; /* reserved */ case 0x2: return (cmd & 0xff) + 2; /* 2d commands */ case 0x3: if (((cmd >> 24) & 0x1f) <= 0x18) return 1; switch ((cmd >> 24) & 0x1f) { case 0x1c: return 1; case 0x1d: switch ((cmd >> 16) & 0xff) { case 0x3: return (cmd & 0x1f) + 2; case 0x4: return (cmd & 0xf) + 2; default: return (cmd & 0xffff) + 2; } case 0x1e: if (cmd & (1 << 23)) return (cmd & 0xffff) + 1; else return 1; case 0x1f: if ((cmd & (1 << 23)) == 0) /* inline vertices */ return (cmd & 0x1ffff) + 2; else if (cmd & (1 << 17)) /* indirect random */ if ((cmd & 0xffff) == 0) return 0; /* unknown length, too hard */ else return (((cmd & 0xffff) + 1) / 2) + 1; else return 2; /* indirect sequential */ default: return 0; } default: return 0; } return 0; } static int validate_cmd(int cmd) { int ret = do_validate_cmd(cmd); /* printk("validate_cmd( %x ): %d\n", cmd, ret); */ return ret; } static int i915_emit_cmds(struct drm_device *dev, int __user *buffer, int dwords) { drm_i915_private_t *dev_priv = dev->dev_private; int i; if ((dwords+1) * sizeof(int) >= LP_RING(dev_priv)->size - 8) 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))) return -EINVAL; if ((sz = validate_cmd(cmd)) == 0 || i + sz > dwords) return -EINVAL; OUT_RING(cmd); while (++i, --sz) { if (DRM_COPY_FROM_USER_UNCHECKED(&cmd, &buffer[i], sizeof(cmd))) { return -EINVAL; } OUT_RING(cmd); } } if (dwords & 1) OUT_RING(0); ADVANCE_LP_RING(); return 0; } int i915_emit_box(struct drm_device * dev, struct drm_clip_rect *boxes, int i, int DR1, int DR4) { struct drm_clip_rect box; if (DRM_COPY_FROM_USER_UNCHECKED(&box, &boxes[i], sizeof(box))) { return -EFAULT; } return (i915_emit_box_p(dev, &box, DR1, DR4)); } int i915_emit_box_p(struct drm_device *dev, struct drm_clip_rect *box, int DR1, int DR4) { drm_i915_private_t *dev_priv = dev->dev_private; int ret; if (box->y2 <= box->y1 || box->x2 <= box->x1 || box->y2 <= 0 || box->x2 <= 0) { DRM_ERROR("Bad box %d,%d..%d,%d\n", box->x1, box->y1, box->x2, box->y2); return -EINVAL; } if (INTEL_INFO(dev)->gen >= 4) { ret = BEGIN_LP_RING(4); if (ret != 0) return (ret); OUT_RING(GFX_OP_DRAWRECT_INFO_I965); OUT_RING((box->x1 & 0xffff) | (box->y1 << 16)); OUT_RING(((box->x2 - 1) & 0xffff) | ((box->y2 - 1) << 16)); OUT_RING(DR4); } else { ret = BEGIN_LP_RING(6); if (ret != 0) return (ret); OUT_RING(GFX_OP_DRAWRECT_INFO); OUT_RING(DR1); OUT_RING((box->x1 & 0xffff) | (box->y1 << 16)); OUT_RING(((box->x2 - 1) & 0xffff) | ((box->y2 - 1) << 16)); OUT_RING(DR4); OUT_RING(0); } ADVANCE_LP_RING(); return 0; } /* XXX: Emitting the counter should really be moved to part of the IRQ * emit. For now, do it in both places: */ static void i915_emit_breadcrumb(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv; if (++dev_priv->counter > 0x7FFFFFFFUL) dev_priv->counter = 0; if (master_priv->sarea_priv) master_priv->sarea_priv->last_enqueue = dev_priv->counter; if (BEGIN_LP_RING(4) == 0) { 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(); } } static int i915_dispatch_cmdbuffer(struct drm_device * dev, drm_i915_cmdbuffer_t * cmd, struct drm_clip_rect *cliprects, void *cmdbuf) { int nbox = cmd->num_cliprects; int i = 0, count, ret; if (cmd->sz & 0x3) { DRM_ERROR("alignment\n"); return -EINVAL; } i915_kernel_lost_context(dev); count = nbox ? nbox : 1; for (i = 0; i < count; i++) { if (i < nbox) { ret = i915_emit_box_p(dev, &cmd->cliprects[i], cmd->DR1, cmd->DR4); if (ret) return ret; } ret = i915_emit_cmds(dev, cmdbuf, cmd->sz / 4); if (ret) return ret; } i915_emit_breadcrumb(dev); return 0; } static int i915_dispatch_batchbuffer(struct drm_device * dev, drm_i915_batchbuffer_t * batch, struct drm_clip_rect *cliprects) { drm_i915_private_t *dev_priv = dev->dev_private; int nbox = batch->num_cliprects; int i, count, ret; if (drm_core_check_feature(dev, DRIVER_MODESET)) return -ENODEV; if ((batch->start | batch->used) & 0x7) { DRM_ERROR("alignment\n"); return -EINVAL; } i915_kernel_lost_context(dev); count = nbox ? nbox : 1; for (i = 0; i < count; i++) { if (i < nbox) { int ret = i915_emit_box_p(dev, &cliprects[i], batch->DR1, batch->DR4); if (ret) return ret; } if (!IS_I830(dev) && !IS_845G(dev)) { ret = BEGIN_LP_RING(2); if (ret != 0) return (ret); if (INTEL_INFO(dev)->gen >= 4) { OUT_RING(MI_BATCH_BUFFER_START | (2 << 6) | MI_BATCH_NON_SECURE_I965); OUT_RING(batch->start); } else { OUT_RING(MI_BATCH_BUFFER_START | (2 << 6)); OUT_RING(batch->start | MI_BATCH_NON_SECURE); } } else { ret = BEGIN_LP_RING(4); if (ret != 0) return (ret); OUT_RING(MI_BATCH_BUFFER); OUT_RING(batch->start | MI_BATCH_NON_SECURE); OUT_RING(batch->start + batch->used - 4); OUT_RING(0); } ADVANCE_LP_RING(); } i915_emit_breadcrumb(dev); return 0; } static int i915_dispatch_flip(struct drm_device * dev) { drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv; int ret; if (!master_priv->sarea_priv) return -EINVAL; DRM_DEBUG("%s: page=%d pfCurrentPage=%d\n", __func__, dev_priv->current_page, master_priv->sarea_priv->pf_current_page); i915_kernel_lost_context(dev); ret = BEGIN_LP_RING(10); if (ret) return ret; OUT_RING(MI_FLUSH | MI_READ_FLUSH); OUT_RING(0); 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); OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_PLANE_A_FLIP); OUT_RING(0); ADVANCE_LP_RING(); master_priv->sarea_priv->last_enqueue = dev_priv->counter++; if (BEGIN_LP_RING(4) == 0) { 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(); } master_priv->sarea_priv->pf_current_page = dev_priv->current_page; return 0; } static int i915_quiescent(struct drm_device *dev) { struct intel_ring_buffer *ring = LP_RING(dev->dev_private); i915_kernel_lost_context(dev); return (intel_wait_ring_idle(ring)); } static int i915_flush_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { int ret; if (drm_core_check_feature(dev, DRIVER_MODESET)) return -ENODEV; RING_LOCK_TEST_WITH_RETURN(dev, file_priv); DRM_LOCK(dev); ret = i915_quiescent(dev); DRM_UNLOCK(dev); return (ret); } int i915_batchbuffer(struct drm_device *dev, void *data, struct drm_file *file_priv) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv; drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *) master_priv->sarea_priv; drm_i915_batchbuffer_t *batch = data; struct drm_clip_rect *cliprects; size_t cliplen; int ret; if (!dev_priv->dri1.allow_batchbuffer) { DRM_ERROR("Batchbuffer ioctl disabled\n"); return -EINVAL; } DRM_DEBUG("i915 batchbuffer, start %x used %d cliprects %d\n", batch->start, batch->used, batch->num_cliprects); cliplen = batch->num_cliprects * sizeof(struct drm_clip_rect); if (batch->num_cliprects < 0) return -EFAULT; if (batch->num_cliprects != 0) { cliprects = malloc(batch->num_cliprects * sizeof(struct drm_clip_rect), DRM_MEM_DMA, M_WAITOK | M_ZERO); ret = -copyin(batch->cliprects, cliprects, batch->num_cliprects * sizeof(struct drm_clip_rect)); if (ret != 0) goto fail_free; } else cliprects = NULL; DRM_LOCK(dev); RING_LOCK_TEST_WITH_RETURN(dev, file_priv); ret = i915_dispatch_batchbuffer(dev, batch, cliprects); DRM_UNLOCK(dev); if (sarea_priv) sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); fail_free: free(cliprects, DRM_MEM_DMA); return ret; } int i915_cmdbuffer(struct drm_device *dev, void *data, struct drm_file *file_priv) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv; drm_i915_sarea_t *sarea_priv = (drm_i915_sarea_t *) master_priv->sarea_priv; drm_i915_cmdbuffer_t *cmdbuf = data; struct drm_clip_rect *cliprects = NULL; void *batch_data; int ret; if (drm_core_check_feature(dev, DRIVER_MODESET)) return -ENODEV; DRM_DEBUG("i915 cmdbuffer, buf %p sz %d cliprects %d\n", cmdbuf->buf, cmdbuf->sz, cmdbuf->num_cliprects); if (cmdbuf->num_cliprects < 0) return -EINVAL; batch_data = malloc(cmdbuf->sz, DRM_MEM_DMA, M_WAITOK); ret = -copyin(cmdbuf->buf, batch_data, cmdbuf->sz); if (ret != 0) goto fail_batch_free; if (cmdbuf->num_cliprects) { cliprects = malloc(cmdbuf->num_cliprects * sizeof(struct drm_clip_rect), DRM_MEM_DMA, M_WAITOK | M_ZERO); ret = -copyin(cmdbuf->cliprects, cliprects, cmdbuf->num_cliprects * sizeof(struct drm_clip_rect)); if (ret != 0) goto fail_clip_free; } DRM_LOCK(dev); RING_LOCK_TEST_WITH_RETURN(dev, file_priv); ret = i915_dispatch_cmdbuffer(dev, cmdbuf, cliprects, batch_data); DRM_UNLOCK(dev); if (ret) { DRM_ERROR("i915_dispatch_cmdbuffer failed\n"); goto fail_clip_free; } if (sarea_priv) sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); fail_clip_free: free(cliprects, DRM_MEM_DMA); fail_batch_free: free(batch_data, DRM_MEM_DMA); return ret; } static int i915_emit_irq(struct drm_device * dev) { drm_i915_private_t *dev_priv = dev->dev_private; struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv; i915_kernel_lost_context(dev); DRM_DEBUG("i915: emit_irq\n"); dev_priv->counter++; if (dev_priv->counter > 0x7FFFFFFFUL) dev_priv->counter = 1; if (master_priv->sarea_priv) master_priv->sarea_priv->last_enqueue = dev_priv->counter; if (BEGIN_LP_RING(4) == 0) { 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(); } return dev_priv->counter; } static int i915_wait_irq(struct drm_device * dev, int irq_nr) { drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv; int ret; struct intel_ring_buffer *ring = LP_RING(dev_priv); DRM_DEBUG("irq_nr=%d breadcrumb=%d\n", irq_nr, READ_BREADCRUMB(dev_priv)); if (READ_BREADCRUMB(dev_priv) >= irq_nr) { if (master_priv->sarea_priv) master_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv); return 0; } if (master_priv->sarea_priv) master_priv->sarea_priv->perf_boxes |= I915_BOX_WAIT; ret = 0; mtx_lock(&dev_priv->irq_lock); if (ring->irq_get(ring)) { while (ret == 0 && READ_BREADCRUMB(dev_priv) < irq_nr) { ret = -msleep(ring, &dev_priv->irq_lock, PCATCH, "915wtq", 3 * hz); if (ret == -ERESTART) ret = -ERESTARTSYS; } ring->irq_put(ring); mtx_unlock(&dev_priv->irq_lock); } else { mtx_unlock(&dev_priv->irq_lock); if (_intel_wait_for(dev, READ_BREADCRUMB(dev_priv) >= irq_nr, 3000, 1, "915wir")) ret = -EBUSY; } if (ret == -EBUSY) { DRM_ERROR("EBUSY -- rec: %d emitted: %d\n", READ_BREADCRUMB(dev_priv), (int)dev_priv->counter); } return ret; } /* Needs the lock as it touches the ring. */ int i915_irq_emit(struct drm_device *dev, void *data, struct drm_file *file_priv) { drm_i915_private_t *dev_priv = dev->dev_private; drm_i915_irq_emit_t *emit = data; int result; if (drm_core_check_feature(dev, DRIVER_MODESET)) return -ENODEV; if (!dev_priv || !LP_RING(dev_priv)->virtual_start) { DRM_ERROR("called with no initialization\n"); return -EINVAL; } RING_LOCK_TEST_WITH_RETURN(dev, file_priv); DRM_LOCK(dev); result = i915_emit_irq(dev); DRM_UNLOCK(dev); if (DRM_COPY_TO_USER(emit->irq_seq, &result, sizeof(int))) { DRM_ERROR("copy_to_user\n"); return -EFAULT; } return 0; } /* Doesn't need the hardware lock. */ static int i915_irq_wait(struct drm_device *dev, void *data, struct drm_file *file_priv) { drm_i915_private_t *dev_priv = dev->dev_private; drm_i915_irq_wait_t *irqwait = data; if (drm_core_check_feature(dev, DRIVER_MODESET)) return -ENODEV; if (!dev_priv) { DRM_ERROR("called with no initialization\n"); return -EINVAL; } return i915_wait_irq(dev, irqwait->irq_seq); } static int i915_vblank_pipe_get(struct drm_device *dev, void *data, struct drm_file *file_priv) { drm_i915_private_t *dev_priv = dev->dev_private; drm_i915_vblank_pipe_t *pipe = data; if (drm_core_check_feature(dev, DRIVER_MODESET)) return -ENODEV; if (!dev_priv) { DRM_ERROR("called with no initialization\n"); return -EINVAL; } pipe->pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B; return 0; } /** * Schedule buffer swap at given vertical blank. */ static int i915_vblank_swap(struct drm_device *dev, void *data, struct drm_file *file_priv) { /* 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; } static int i915_flip_bufs(struct drm_device *dev, void *data, struct drm_file *file_priv) { int ret; if (drm_core_check_feature(dev, DRIVER_MODESET)) return -ENODEV; DRM_DEBUG("%s\n", __func__); RING_LOCK_TEST_WITH_RETURN(dev, file_priv); DRM_LOCK(dev); ret = i915_dispatch_flip(dev); DRM_UNLOCK(dev); return ret; } int i915_getparam(struct drm_device *dev, void *data, struct drm_file *file_priv) { drm_i915_private_t *dev_priv = dev->dev_private; drm_i915_getparam_t *param = data; int value; if (!dev_priv) { DRM_ERROR("called with no initialization\n"); return -EINVAL; } switch (param->param) { case I915_PARAM_IRQ_ACTIVE: value = dev->irq_enabled ? 1 : 0; break; case I915_PARAM_ALLOW_BATCHBUFFER: value = dev_priv->dri1.allow_batchbuffer ? 1 : 0; break; case I915_PARAM_LAST_DISPATCH: value = READ_BREADCRUMB(dev_priv); break; case I915_PARAM_CHIPSET_ID: value = dev->pci_device; break; case I915_PARAM_HAS_GEM: value = 1; break; case I915_PARAM_NUM_FENCES_AVAIL: value = dev_priv->num_fence_regs - dev_priv->fence_reg_start; break; case I915_PARAM_HAS_OVERLAY: value = dev_priv->overlay ? 1 : 0; break; case I915_PARAM_HAS_PAGEFLIPPING: value = 1; break; case I915_PARAM_HAS_EXECBUF2: value = 1; break; case I915_PARAM_HAS_BSD: value = intel_ring_initialized(&dev_priv->rings[VCS]); break; case I915_PARAM_HAS_BLT: value = intel_ring_initialized(&dev_priv->rings[BCS]); break; case I915_PARAM_HAS_RELAXED_FENCING: value = 1; break; case I915_PARAM_HAS_COHERENT_RINGS: value = 1; break; case I915_PARAM_HAS_EXEC_CONSTANTS: value = INTEL_INFO(dev)->gen >= 4; break; case I915_PARAM_HAS_RELAXED_DELTA: value = 1; break; case I915_PARAM_HAS_GEN7_SOL_RESET: value = 1; break; case I915_PARAM_HAS_LLC: value = HAS_LLC(dev); break; case I915_PARAM_HAS_ALIASING_PPGTT: value = dev_priv->mm.aliasing_ppgtt ? 1 : 0; break; default: DRM_DEBUG_DRIVER("Unknown parameter %d\n", param->param); return -EINVAL; } if (DRM_COPY_TO_USER(param->value, &value, sizeof(int))) { DRM_ERROR("DRM_COPY_TO_USER failed\n"); return -EFAULT; } return 0; } static int i915_setparam(struct drm_device *dev, void *data, struct drm_file *file_priv) { drm_i915_private_t *dev_priv = dev->dev_private; drm_i915_setparam_t *param = data; if (!dev_priv) { DRM_ERROR("called with no initialization\n"); return -EINVAL; } switch (param->param) { case I915_SETPARAM_USE_MI_BATCHBUFFER_START: break; case I915_SETPARAM_TEX_LRU_LOG_GRANULARITY: break; case I915_SETPARAM_ALLOW_BATCHBUFFER: dev_priv->dri1.allow_batchbuffer = param->value ? 1 : 0; break; case I915_SETPARAM_NUM_USED_FENCES: if (param->value > dev_priv->num_fence_regs || param->value < 0) return -EINVAL; /* Userspace can use first N regs */ dev_priv->fence_reg_start = param->value; break; default: DRM_DEBUG("unknown parameter %d\n", param->param); return -EINVAL; } return 0; } static int i915_set_status_page(struct drm_device *dev, void *data, struct drm_file *file_priv) { drm_i915_private_t *dev_priv = dev->dev_private; drm_i915_hws_addr_t *hws = data; struct intel_ring_buffer *ring = LP_RING(dev_priv); if (drm_core_check_feature(dev, DRIVER_MODESET)) return -ENODEV; if (!I915_NEED_GFX_HWS(dev)) return -EINVAL; if (!dev_priv) { DRM_ERROR("called with no initialization\n"); return -EINVAL; } DRM_DEBUG("set status page addr 0x%08x\n", (u32)hws->addr); if (drm_core_check_feature(dev, DRIVER_MODESET)) { DRM_ERROR("tried to set status page when mode setting active\n"); return 0; } ring->status_page.gfx_addr = dev_priv->status_gfx_addr = hws->addr & (0x1ffff<<12); dev_priv->dri1.gfx_hws_cpu_addr = pmap_mapdev_attr( dev->agp->base + hws->addr, PAGE_SIZE, VM_MEMATTR_WRITE_COMBINING); if (dev_priv->dri1.gfx_hws_cpu_addr == NULL) { i915_dma_cleanup(dev); ring->status_page.gfx_addr = dev_priv->status_gfx_addr = 0; DRM_ERROR("can not ioremap virtual address for" " G33 hw status page\n"); return -ENOMEM; } memset(dev_priv->dri1.gfx_hws_cpu_addr, 0, PAGE_SIZE); I915_WRITE(HWS_PGA, dev_priv->status_gfx_addr); DRM_DEBUG("load hws HWS_PGA with gfx mem 0x%x\n", dev_priv->status_gfx_addr); DRM_DEBUG("load hws at %p\n", dev_priv->hw_status_page); return 0; } static int i915_load_modeset_init(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; int ret; ret = intel_parse_bios(dev); if (ret) DRM_INFO("failed to find VBIOS tables\n"); #if 0 intel_register_dsm_handler(); #endif /* Initialise stolen first so that we may reserve preallocated * objects for the BIOS to KMS transition. */ ret = i915_gem_init_stolen(dev); if (ret) goto cleanup_vga_switcheroo; intel_modeset_init(dev); ret = i915_gem_init(dev); if (ret != 0) goto cleanup_gem_stolen; intel_modeset_gem_init(dev); ret = drm_irq_install(dev); if (ret) goto cleanup_gem; dev->vblank_disable_allowed = 1; ret = intel_fbdev_init(dev); if (ret) goto cleanup_gem; drm_kms_helper_poll_init(dev); /* We're off and running w/KMS */ dev_priv->mm.suspended = 0; return (0); cleanup_gem: DRM_LOCK(dev); i915_gem_cleanup_ringbuffer(dev); DRM_UNLOCK(dev); i915_gem_cleanup_aliasing_ppgtt(dev); cleanup_gem_stolen: i915_gem_cleanup_stolen(dev); cleanup_vga_switcheroo: return (ret); } int i915_master_create(struct drm_device *dev, struct drm_master *master) { struct drm_i915_master_private *master_priv; master_priv = malloc(sizeof(*master_priv), DRM_MEM_DMA, M_NOWAIT | M_ZERO); if (!master_priv) return -ENOMEM; master->driver_priv = master_priv; return 0; } void i915_master_destroy(struct drm_device *dev, struct drm_master *master) { struct drm_i915_master_private *master_priv = master->driver_priv; if (!master_priv) return; free(master_priv, DRM_MEM_DMA); master->driver_priv = NULL; } static int i915_get_bridge_dev(struct drm_device *dev) { struct drm_i915_private *dev_priv; dev_priv = dev->dev_private; dev_priv->bridge_dev = intel_gtt_get_bridge_device(); if (dev_priv->bridge_dev == NULL) { DRM_ERROR("bridge device not found\n"); return (-1); } return (0); } #define MCHBAR_I915 0x44 #define MCHBAR_I965 0x48 #define MCHBAR_SIZE (4*4096) #define DEVEN_REG 0x54 #define DEVEN_MCHBAR_EN (1 << 28) /* Allocate space for the MCH regs if needed, return nonzero on error */ static int intel_alloc_mchbar_resource(struct drm_device *dev) { drm_i915_private_t *dev_priv; device_t vga; int reg; u32 temp_lo, temp_hi; u64 mchbar_addr, temp; dev_priv = dev->dev_private; reg = INTEL_INFO(dev)->gen >= 4 ? MCHBAR_I965 : MCHBAR_I915; if (INTEL_INFO(dev)->gen >= 4) temp_hi = pci_read_config(dev_priv->bridge_dev, reg + 4, 4); else temp_hi = 0; temp_lo = pci_read_config(dev_priv->bridge_dev, reg, 4); mchbar_addr = ((u64)temp_hi << 32) | temp_lo; /* If ACPI doesn't have it, assume we need to allocate it ourselves */ #ifdef XXX_CONFIG_PNP if (mchbar_addr && pnp_range_reserved(mchbar_addr, mchbar_addr + MCHBAR_SIZE)) return 0; #endif /* Get some space for it */ vga = device_get_parent(dev->dev); dev_priv->mch_res_rid = 0x100; dev_priv->mch_res = BUS_ALLOC_RESOURCE(device_get_parent(vga), dev->dev, SYS_RES_MEMORY, &dev_priv->mch_res_rid, 0, ~0UL, MCHBAR_SIZE, RF_ACTIVE | RF_SHAREABLE); if (dev_priv->mch_res == NULL) { DRM_ERROR("failed mchbar resource alloc\n"); return (-ENOMEM); } if (INTEL_INFO(dev)->gen >= 4) { temp = rman_get_start(dev_priv->mch_res); temp >>= 32; pci_write_config(dev_priv->bridge_dev, reg + 4, temp, 4); } pci_write_config(dev_priv->bridge_dev, reg, rman_get_start(dev_priv->mch_res) & UINT32_MAX, 4); return (0); } static void intel_setup_mchbar(struct drm_device *dev) { drm_i915_private_t *dev_priv; int mchbar_reg; u32 temp; bool enabled; dev_priv = dev->dev_private; mchbar_reg = INTEL_INFO(dev)->gen >= 4 ? MCHBAR_I965 : MCHBAR_I915; dev_priv->mchbar_need_disable = false; if (IS_I915G(dev) || IS_I915GM(dev)) { temp = pci_read_config(dev_priv->bridge_dev, DEVEN_REG, 4); enabled = (temp & DEVEN_MCHBAR_EN) != 0; } else { temp = pci_read_config(dev_priv->bridge_dev, mchbar_reg, 4); enabled = temp & 1; } /* If it's already enabled, don't have to do anything */ if (enabled) { DRM_DEBUG("mchbar already enabled\n"); return; } if (intel_alloc_mchbar_resource(dev)) return; dev_priv->mchbar_need_disable = true; /* Space is allocated or reserved, so enable it. */ if (IS_I915G(dev) || IS_I915GM(dev)) { pci_write_config(dev_priv->bridge_dev, DEVEN_REG, temp | DEVEN_MCHBAR_EN, 4); } else { temp = pci_read_config(dev_priv->bridge_dev, mchbar_reg, 4); pci_write_config(dev_priv->bridge_dev, mchbar_reg, temp | 1, 4); } } static void intel_teardown_mchbar(struct drm_device *dev) { drm_i915_private_t *dev_priv; device_t vga; int mchbar_reg; u32 temp; dev_priv = dev->dev_private; mchbar_reg = INTEL_INFO(dev)->gen >= 4 ? MCHBAR_I965 : MCHBAR_I915; if (dev_priv->mchbar_need_disable) { if (IS_I915G(dev) || IS_I915GM(dev)) { temp = pci_read_config(dev_priv->bridge_dev, DEVEN_REG, 4); temp &= ~DEVEN_MCHBAR_EN; pci_write_config(dev_priv->bridge_dev, DEVEN_REG, temp, 4); } else { temp = pci_read_config(dev_priv->bridge_dev, mchbar_reg, 4); temp &= ~1; pci_write_config(dev_priv->bridge_dev, mchbar_reg, temp, 4); } } if (dev_priv->mch_res != NULL) { vga = device_get_parent(dev->dev); BUS_DEACTIVATE_RESOURCE(device_get_parent(vga), dev->dev, SYS_RES_MEMORY, dev_priv->mch_res_rid, dev_priv->mch_res); BUS_RELEASE_RESOURCE(device_get_parent(vga), dev->dev, SYS_RES_MEMORY, dev_priv->mch_res_rid, dev_priv->mch_res); dev_priv->mch_res = NULL; } } int i915_driver_load(struct drm_device *dev, unsigned long flags) { struct drm_i915_private *dev_priv = dev->dev_private; const struct intel_device_info *info; unsigned long base, size; int mmio_bar, ret; info = i915_get_device_id(dev->pci_device); /* Refuse to load on gen6+ without kms enabled. */ if (info->gen >= 6 && !drm_core_check_feature(dev, DRIVER_MODESET)) return -ENODEV; ret = 0; /* i915 has 4 more counters */ dev->counters += 4; dev->types[6] = _DRM_STAT_IRQ; dev->types[7] = _DRM_STAT_PRIMARY; dev->types[8] = _DRM_STAT_SECONDARY; dev->types[9] = _DRM_STAT_DMA; dev_priv = malloc(sizeof(drm_i915_private_t), DRM_MEM_DRIVER, M_ZERO | M_WAITOK); dev->dev_private = (void *)dev_priv; dev_priv->dev = dev; dev_priv->info = info; if (i915_get_bridge_dev(dev)) { free(dev_priv, DRM_MEM_DRIVER); return (-EIO); } dev_priv->mm.gtt = intel_gtt_get(); /* Add register map (needed for suspend/resume) */ mmio_bar = IS_GEN2(dev) ? 1 : 0; base = drm_get_resource_start(dev, mmio_bar); size = drm_get_resource_len(dev, mmio_bar); ret = drm_addmap(dev, base, size, _DRM_REGISTERS, _DRM_KERNEL | _DRM_DRIVER, &dev_priv->mmio_map); if (ret != 0) { DRM_ERROR("Failed to allocate mmio_map: %d\n", ret); free(dev_priv, DRM_MEM_DRIVER); return (ret); } dev_priv->tq = taskqueue_create("915", M_WAITOK, taskqueue_thread_enqueue, &dev_priv->tq); taskqueue_start_threads(&dev_priv->tq, 1, PWAIT, "i915 taskq"); mtx_init(&dev_priv->gt_lock, "915gt", NULL, MTX_DEF); mtx_init(&dev_priv->error_lock, "915err", NULL, MTX_DEF); mtx_init(&dev_priv->error_completion_lock, "915cmp", NULL, MTX_DEF); mtx_init(&dev_priv->rps_lock, "915rps", NULL, MTX_DEF); mtx_init(&dev_priv->dpio_lock, "915dpi", NULL, MTX_DEF); intel_irq_init(dev); intel_setup_mchbar(dev); intel_setup_gmbus(dev); intel_opregion_setup(dev); intel_setup_bios(dev); i915_gem_load(dev); /* On the 945G/GM, the chipset reports the MSI capability on the * integrated graphics even though the support isn't actually there * according to the published specs. It doesn't appear to function * correctly in testing on 945G. * This may be a side effect of MSI having been made available for PEG * and the registers being closely associated. * * According to chipset errata, on the 965GM, MSI interrupts may * be lost or delayed, but we use them anyways to avoid * stuck interrupts on some machines. */ if (!IS_I945G(dev) && !IS_I945GM(dev)) drm_pci_enable_msi(dev); /* Init HWS */ if (!I915_NEED_GFX_HWS(dev)) { ret = i915_init_phys_hws(dev); if (ret != 0) { drm_rmmap(dev, dev_priv->mmio_map); free(dev_priv, DRM_MEM_DRIVER); return ret; } } mtx_init(&dev_priv->irq_lock, "userirq", NULL, MTX_DEF); if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev)) dev_priv->num_pipe = 3; else if (IS_MOBILE(dev) || !IS_GEN2(dev)) dev_priv->num_pipe = 2; else dev_priv->num_pipe = 1; ret = drm_vblank_init(dev, dev_priv->num_pipe); if (ret) goto out_gem_unload; /* Start out suspended */ dev_priv->mm.suspended = 1; intel_detect_pch(dev); if (drm_core_check_feature(dev, DRIVER_MODESET)) { ret = i915_load_modeset_init(dev); if (ret < 0) { DRM_ERROR("failed to init modeset\n"); goto out_gem_unload; } } + pci_enable_busmaster(dev->dev); + intel_opregion_init(dev); callout_init(&dev_priv->hangcheck_timer, 1); callout_reset(&dev_priv->hangcheck_timer, DRM_I915_HANGCHECK_PERIOD, i915_hangcheck_elapsed, dev); if (IS_GEN5(dev)) intel_gpu_ips_init(dev_priv); return (0); out_gem_unload: /* XXXKIB */ (void) i915_driver_unload(dev); return (ret); } int i915_driver_unload(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; int ret; DRM_LOCK(dev); ret = i915_gpu_idle(dev); if (ret) DRM_ERROR("failed to idle hardware: %d\n", ret); i915_gem_retire_requests(dev); DRM_UNLOCK(dev); i915_free_hws(dev); intel_teardown_mchbar(dev); if (drm_core_check_feature(dev, DRIVER_MODESET)) { intel_fbdev_fini(dev); intel_modeset_cleanup(dev); } /* Free error state after interrupts are fully disabled. */ callout_stop(&dev_priv->hangcheck_timer); callout_drain(&dev_priv->hangcheck_timer); i915_destroy_error_state(dev); if (dev->msi_enabled) drm_pci_disable_msi(dev); intel_opregion_fini(dev); if (drm_core_check_feature(dev, DRIVER_MODESET)) { DRM_LOCK(dev); i915_gem_free_all_phys_object(dev); i915_gem_cleanup_ringbuffer(dev); i915_gem_context_fini(dev); DRM_UNLOCK(dev); i915_gem_cleanup_aliasing_ppgtt(dev); #if 1 KIB_NOTYET(); #else if (I915_HAS_FBC(dev) && i915_powersave) i915_cleanup_compression(dev); #endif drm_mm_takedown(&dev_priv->mm.stolen); intel_cleanup_overlay(dev); if (!I915_NEED_GFX_HWS(dev)) i915_free_hws(dev); } i915_gem_unload(dev); mtx_destroy(&dev_priv->irq_lock); if (dev_priv->tq != NULL) taskqueue_free(dev_priv->tq); bus_generic_detach(dev->dev); drm_rmmap(dev, dev_priv->mmio_map); intel_teardown_gmbus(dev); mtx_destroy(&dev_priv->dpio_lock); mtx_destroy(&dev_priv->error_lock); mtx_destroy(&dev_priv->error_completion_lock); mtx_destroy(&dev_priv->rps_lock); free(dev->dev_private, DRM_MEM_DRIVER); return (0); } int i915_driver_open(struct drm_device *dev, struct drm_file *file_priv) { struct drm_i915_file_private *i915_file_priv; i915_file_priv = malloc(sizeof(*i915_file_priv), DRM_MEM_FILES, M_WAITOK | M_ZERO); mtx_init(&i915_file_priv->mm.lck, "915fp", NULL, MTX_DEF); INIT_LIST_HEAD(&i915_file_priv->mm.request_list); file_priv->driver_priv = i915_file_priv; drm_gem_names_init(&i915_file_priv->context_idr); return (0); } void i915_driver_lastclose(struct drm_device * dev) { drm_i915_private_t *dev_priv = dev->dev_private; if (!dev_priv || drm_core_check_feature(dev, DRIVER_MODESET)) { #if 1 KIB_NOTYET(); #else drm_fb_helper_restore(); vga_switcheroo_process_delayed_switch(); #endif return; } i915_gem_lastclose(dev); i915_dma_cleanup(dev); } void i915_driver_preclose(struct drm_device * dev, struct drm_file *file_priv) { i915_gem_context_close(dev, file_priv); i915_gem_release(dev, file_priv); } void i915_driver_postclose(struct drm_device *dev, struct drm_file *file_priv) { struct drm_i915_file_private *i915_file_priv = file_priv->driver_priv; mtx_destroy(&i915_file_priv->mm.lck); free(i915_file_priv, DRM_MEM_FILES); } struct drm_ioctl_desc i915_ioctls[] = { DRM_IOCTL_DEF(DRM_I915_INIT, i915_dma_init, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_I915_FLUSH, i915_flush_ioctl, DRM_AUTH), DRM_IOCTL_DEF(DRM_I915_FLIP, i915_flip_bufs, DRM_AUTH), DRM_IOCTL_DEF(DRM_I915_BATCHBUFFER, i915_batchbuffer, DRM_AUTH), DRM_IOCTL_DEF(DRM_I915_IRQ_EMIT, i915_irq_emit, DRM_AUTH), DRM_IOCTL_DEF(DRM_I915_IRQ_WAIT, i915_irq_wait, DRM_AUTH), DRM_IOCTL_DEF(DRM_I915_GETPARAM, i915_getparam, DRM_AUTH), DRM_IOCTL_DEF(DRM_I915_SETPARAM, i915_setparam, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_I915_ALLOC, drm_noop, DRM_AUTH), DRM_IOCTL_DEF(DRM_I915_FREE, drm_noop, DRM_AUTH), DRM_IOCTL_DEF(DRM_I915_INIT_HEAP, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_I915_CMDBUFFER, i915_cmdbuffer, DRM_AUTH), DRM_IOCTL_DEF(DRM_I915_DESTROY_HEAP, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY ), DRM_IOCTL_DEF(DRM_I915_SET_VBLANK_PIPE, drm_noop, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY ), DRM_IOCTL_DEF(DRM_I915_GET_VBLANK_PIPE, i915_vblank_pipe_get, DRM_AUTH ), DRM_IOCTL_DEF(DRM_I915_VBLANK_SWAP, i915_vblank_swap, DRM_AUTH), DRM_IOCTL_DEF(DRM_I915_HWS_ADDR, i915_set_status_page, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY), DRM_IOCTL_DEF(DRM_I915_GEM_INIT, i915_gem_init_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_I915_GEM_EXECBUFFER, i915_gem_execbuffer, DRM_AUTH | DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_I915_GEM_EXECBUFFER2, i915_gem_execbuffer2, DRM_AUTH | DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_I915_GEM_PIN, i915_gem_pin_ioctl, DRM_AUTH|DRM_ROOT_ONLY|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_I915_GEM_UNPIN, i915_gem_unpin_ioctl, DRM_AUTH|DRM_ROOT_ONLY|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_I915_GEM_BUSY, i915_gem_busy_ioctl, DRM_AUTH|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_I915_GEM_THROTTLE, i915_gem_throttle_ioctl, DRM_AUTH|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_I915_GEM_ENTERVT, i915_gem_entervt_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_I915_GEM_LEAVEVT, i915_gem_leavevt_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_I915_GEM_CREATE, i915_gem_create_ioctl, DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_I915_GEM_PREAD, i915_gem_pread_ioctl, DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_I915_GEM_PWRITE, i915_gem_pwrite_ioctl, DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_I915_GEM_MMAP, i915_gem_mmap_ioctl, DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_I915_GEM_MMAP_GTT, i915_gem_mmap_gtt_ioctl, DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_I915_GEM_SET_DOMAIN, i915_gem_set_domain_ioctl, DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_I915_GEM_SW_FINISH, i915_gem_sw_finish_ioctl, DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_I915_GEM_SET_TILING, i915_gem_set_tiling, DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_I915_GEM_GET_TILING, i915_gem_get_tiling, DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_I915_GEM_GET_APERTURE, i915_gem_get_aperture_ioctl, DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_I915_GET_PIPE_FROM_CRTC_ID, intel_get_pipe_from_crtc_id, DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_I915_GEM_MADVISE, i915_gem_madvise_ioctl, DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_I915_OVERLAY_PUT_IMAGE, intel_overlay_put_image, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_I915_OVERLAY_ATTRS, intel_overlay_attrs, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_I915_SET_SPRITE_COLORKEY, intel_sprite_set_colorkey, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_I915_GET_SPRITE_COLORKEY, intel_sprite_get_colorkey, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_I915_GEM_CONTEXT_CREATE, i915_gem_context_create_ioctl, DRM_UNLOCKED), DRM_IOCTL_DEF(DRM_I915_GEM_CONTEXT_DESTROY, i915_gem_context_destroy_ioctl, DRM_UNLOCKED), }; #ifdef COMPAT_FREEBSD32 extern struct drm_ioctl_desc i915_compat_ioctls[]; extern int i915_compat_ioctls_nr; #endif struct drm_driver i915_driver_info = { /* * FIXME Linux<->FreeBSD: DRIVER_USE_MTRR is commented out on * Linux. */ .driver_features = DRIVER_USE_AGP | DRIVER_REQUIRE_AGP | DRIVER_USE_MTRR | DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_GEM | DRIVER_PRIME, .buf_priv_size = sizeof(drm_i915_private_t), .load = i915_driver_load, .open = i915_driver_open, .unload = i915_driver_unload, .preclose = i915_driver_preclose, .lastclose = i915_driver_lastclose, .postclose = i915_driver_postclose, .device_is_agp = i915_driver_device_is_agp, .master_create = i915_master_create, .master_destroy = i915_master_destroy, .gem_init_object = i915_gem_init_object, .gem_free_object = i915_gem_free_object, .gem_pager_ops = &i915_gem_pager_ops, .dumb_create = i915_gem_dumb_create, .dumb_map_offset = i915_gem_mmap_gtt, .dumb_destroy = i915_gem_dumb_destroy, .sysctl_init = i915_sysctl_init, .sysctl_cleanup = i915_sysctl_cleanup, .ioctls = i915_ioctls, #ifdef COMPAT_FREEBSD32 .compat_ioctls = i915_compat_ioctls, .num_compat_ioctls = &i915_compat_ioctls_nr, #endif .num_ioctls = ARRAY_SIZE(i915_ioctls), .name = DRIVER_NAME, .desc = DRIVER_DESC, .date = DRIVER_DATE, .major = DRIVER_MAJOR, .minor = DRIVER_MINOR, .patchlevel = DRIVER_PATCHLEVEL, }; /* * This is really ugly: Because old userspace abused the linux agp interface to * manage the gtt, we need to claim that all intel devices are agp. For * otherwise the drm core refuses to initialize the agp support code. */ int i915_driver_device_is_agp(struct drm_device * dev) { return 1; } Index: stable/10/sys/dev/drm2/i915/intel_opregion.c =================================================================== --- stable/10/sys/dev/drm2/i915/intel_opregion.c (revision 295622) +++ stable/10/sys/dev/drm2/i915/intel_opregion.c (revision 295623) @@ -1,616 +1,614 @@ /* * Copyright 2008 Intel Corporation * Copyright 2008 Red Hat * * 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 INTEL 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. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #define PCI_ASLE 0xe4 #define PCI_ASLS 0xfc #define OPREGION_HEADER_OFFSET 0 #define OPREGION_ACPI_OFFSET 0x100 #define ACPI_CLID 0x01ac /* current lid state indicator */ #define ACPI_CDCK 0x01b0 /* current docking state indicator */ #define OPREGION_SWSCI_OFFSET 0x200 #define OPREGION_ASLE_OFFSET 0x300 #define OPREGION_VBT_OFFSET 0x400 #define OPREGION_SIGNATURE "IntelGraphicsMem" #define MBOX_ACPI (1<<0) #define MBOX_SWSCI (1<<1) #define MBOX_ASLE (1<<2) struct opregion_header { u8 signature[16]; u32 size; u32 opregion_ver; u8 bios_ver[32]; u8 vbios_ver[16]; u8 driver_ver[16]; u32 mboxes; u8 reserved[164]; } __attribute__((packed)); /* OpRegion mailbox #1: public ACPI methods */ struct opregion_acpi { u32 drdy; /* driver readiness */ u32 csts; /* notification status */ u32 cevt; /* current event */ u8 rsvd1[20]; u32 didl[8]; /* supported display devices ID list */ u32 cpdl[8]; /* currently presented display list */ u32 cadl[8]; /* currently active display list */ u32 nadl[8]; /* next active devices list */ u32 aslp; /* ASL sleep time-out */ u32 tidx; /* toggle table index */ u32 chpd; /* current hotplug enable indicator */ u32 clid; /* current lid state*/ u32 cdck; /* current docking state */ u32 sxsw; /* Sx state resume */ u32 evts; /* ASL supported events */ u32 cnot; /* current OS notification */ u32 nrdy; /* driver status */ u8 rsvd2[60]; } __attribute__((packed)); /* OpRegion mailbox #2: SWSCI */ struct opregion_swsci { u32 scic; /* SWSCI command|status|data */ u32 parm; /* command parameters */ u32 dslp; /* driver sleep time-out */ u8 rsvd[244]; } __attribute__((packed)); /* OpRegion mailbox #3: ASLE */ struct opregion_asle { u32 ardy; /* driver readiness */ u32 aslc; /* ASLE interrupt command */ u32 tche; /* technology enabled indicator */ u32 alsi; /* current ALS illuminance reading */ u32 bclp; /* backlight brightness to set */ u32 pfit; /* panel fitting state */ u32 cblv; /* current brightness level */ u16 bclm[20]; /* backlight level duty cycle mapping table */ u32 cpfm; /* current panel fitting mode */ u32 epfm; /* enabled panel fitting modes */ u8 plut[74]; /* panel LUT and identifier */ u32 pfmb; /* PWM freq and min brightness */ u8 rsvd[102]; } __attribute__((packed)); /* ASLE irq request bits */ #define ASLE_SET_ALS_ILLUM (1 << 0) #define ASLE_SET_BACKLIGHT (1 << 1) #define ASLE_SET_PFIT (1 << 2) #define ASLE_SET_PWM_FREQ (1 << 3) #define ASLE_REQ_MSK 0xf /* response bits of ASLE irq request */ #define ASLE_ALS_ILLUM_FAILED (1<<10) #define ASLE_BACKLIGHT_FAILED (1<<12) #define ASLE_PFIT_FAILED (1<<14) #define ASLE_PWM_FREQ_FAILED (1<<16) /* ASLE backlight brightness to set */ #define ASLE_BCLP_VALID (1<<31) #define ASLE_BCLP_MSK (~(1<<31)) /* ASLE panel fitting request */ #define ASLE_PFIT_VALID (1<<31) #define ASLE_PFIT_CENTER (1<<0) #define ASLE_PFIT_STRETCH_TEXT (1<<1) #define ASLE_PFIT_STRETCH_GFX (1<<2) /* PWM frequency and minimum brightness */ #define ASLE_PFMB_BRIGHTNESS_MASK (0xff) #define ASLE_PFMB_BRIGHTNESS_VALID (1<<8) #define ASLE_PFMB_PWM_MASK (0x7ffffe00) #define ASLE_PFMB_PWM_VALID (1<<31) #define ASLE_CBLV_VALID (1<<31) #define ACPI_OTHER_OUTPUT (0<<8) #define ACPI_VGA_OUTPUT (1<<8) #define ACPI_TV_OUTPUT (2<<8) #define ACPI_DIGITAL_OUTPUT (3<<8) #define ACPI_LVDS_OUTPUT (4<<8) #if defined(CONFIG_ACPI) static u32 asle_set_backlight(struct drm_device *dev, u32 bclp) { struct drm_i915_private *dev_priv = dev->dev_private; struct opregion_asle *asle = dev_priv->opregion.asle; u32 max; if (!(bclp & ASLE_BCLP_VALID)) return ASLE_BACKLIGHT_FAILED; bclp &= ASLE_BCLP_MSK; if (bclp > 255) return ASLE_BACKLIGHT_FAILED; max = intel_panel_get_max_backlight(dev); intel_panel_set_backlight(dev, bclp * max / 255); asle->cblv = (bclp*0x64)/0xff | ASLE_CBLV_VALID; return 0; } static u32 asle_set_als_illum(struct drm_device *dev, u32 alsi) { /* alsi is the current ALS reading in lux. 0 indicates below sensor range, 0xffff indicates above sensor range. 1-0xfffe are valid */ return 0; } static u32 asle_set_pwm_freq(struct drm_device *dev, u32 pfmb) { struct drm_i915_private *dev_priv = dev->dev_private; if (pfmb & ASLE_PFMB_PWM_VALID) { u32 blc_pwm_ctl = I915_READ(BLC_PWM_CTL); u32 pwm = pfmb & ASLE_PFMB_PWM_MASK; blc_pwm_ctl &= BACKLIGHT_DUTY_CYCLE_MASK; pwm = pwm >> 9; /* FIXME - what do we do with the PWM? */ } return 0; } static u32 asle_set_pfit(struct drm_device *dev, u32 pfit) { /* Panel fitting is currently controlled by the X code, so this is a noop until modesetting support works fully */ if (!(pfit & ASLE_PFIT_VALID)) return ASLE_PFIT_FAILED; return 0; } void intel_opregion_asle_intr(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct opregion_asle *asle = dev_priv->opregion.asle; u32 asle_stat = 0; u32 asle_req; if (!asle) return; asle_req = asle->aslc & ASLE_REQ_MSK; if (!asle_req) { DRM_DEBUG("non asle set request??\n"); return; } if (asle_req & ASLE_SET_ALS_ILLUM) asle_stat |= asle_set_als_illum(dev, asle->alsi); if (asle_req & ASLE_SET_BACKLIGHT) asle_stat |= asle_set_backlight(dev, asle->bclp); if (asle_req & ASLE_SET_PFIT) asle_stat |= asle_set_pfit(dev, asle->pfit); if (asle_req & ASLE_SET_PWM_FREQ) asle_stat |= asle_set_pwm_freq(dev, asle->pfmb); asle->aslc = asle_stat; } void intel_opregion_gse_intr(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct opregion_asle *asle = dev_priv->opregion.asle; u32 asle_stat = 0; u32 asle_req; if (!asle) return; asle_req = asle->aslc & ASLE_REQ_MSK; if (!asle_req) { DRM_DEBUG("non asle set request??\n"); return; } if (asle_req & ASLE_SET_ALS_ILLUM) { DRM_DEBUG("Illum is not supported\n"); asle_stat |= ASLE_ALS_ILLUM_FAILED; } if (asle_req & ASLE_SET_BACKLIGHT) asle_stat |= asle_set_backlight(dev, asle->bclp); if (asle_req & ASLE_SET_PFIT) { DRM_DEBUG("Pfit is not supported\n"); asle_stat |= ASLE_PFIT_FAILED; } if (asle_req & ASLE_SET_PWM_FREQ) { DRM_DEBUG("PWM freq is not supported\n"); asle_stat |= ASLE_PWM_FREQ_FAILED; } asle->aslc = asle_stat; } #define ASLE_ALS_EN (1<<0) #define ASLE_BLC_EN (1<<1) #define ASLE_PFIT_EN (1<<2) #define ASLE_PFMB_EN (1<<3) void intel_opregion_enable_asle(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct opregion_asle *asle = dev_priv->opregion.asle; if (asle) { if (IS_MOBILE(dev)) intel_enable_asle(dev); asle->tche = ASLE_ALS_EN | ASLE_BLC_EN | ASLE_PFIT_EN | ASLE_PFMB_EN; asle->ardy = 1; } } #define ACPI_EV_DISPLAY_SWITCH (1<<0) #define ACPI_EV_LID (1<<1) #define ACPI_EV_DOCK (1<<2) static struct intel_opregion *system_opregion; #if 0 static int intel_opregion_video_event(struct notifier_block *nb, unsigned long val, void *data) { /* The only video events relevant to opregion are 0x80. These indicate either a docking event, lid switch or display switch request. In Linux, these are handled by the dock, button and video drivers. */ struct opregion_acpi *acpi; struct acpi_bus_event *event = data; int ret = NOTIFY_OK; if (strcmp(event->device_class, ACPI_VIDEO_CLASS) != 0) return NOTIFY_DONE; if (!system_opregion) return NOTIFY_DONE; acpi = system_opregion->acpi; if (event->type == 0x80 && !(acpi->cevt & 0x1)) ret = NOTIFY_BAD; acpi->csts = 0; return ret; } static struct notifier_block intel_opregion_notifier = { .notifier_call = intel_opregion_video_event, }; #endif /* * Initialise the DIDL field in opregion. This passes a list of devices to * the firmware. Values are defined by section B.4.2 of the ACPI specification * (version 3) */ static int acpi_is_video_device(ACPI_HANDLE devh) { ACPI_HANDLE h; if (ACPI_FAILURE(AcpiGetHandle(devh, "_DOD", &h)) || ACPI_FAILURE(AcpiGetHandle(devh, "_DOS", &h))) { return 0; } return 1; } static void intel_didl_outputs(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct intel_opregion *opregion = &dev_priv->opregion; struct drm_connector *connector; u32 device_id; ACPI_HANDLE handle, acpi_video_bus, acpi_cdev; ACPI_STATUS status; int i = 0; handle = acpi_get_handle(dev->dev); if (!handle) return; if (acpi_is_video_device(handle)) acpi_video_bus = handle; else { acpi_cdev = NULL; acpi_video_bus = NULL; while (AcpiGetNextObject(ACPI_TYPE_DEVICE, handle, acpi_cdev, &acpi_cdev) != AE_NOT_FOUND) { if (acpi_is_video_device(acpi_cdev)) { acpi_video_bus = acpi_cdev; break; } } #if 0 list_for_each_entry(acpi_cdev, &acpi_dev->children, node) { if (acpi_is_video_device(acpi_cdev)) { acpi_video_bus = acpi_cdev; break; } } #endif } if (!acpi_video_bus) { device_printf(dev->dev, "No ACPI video bus found\n"); return; } acpi_cdev = NULL; while (AcpiGetNextObject(ACPI_TYPE_DEVICE, acpi_video_bus, acpi_cdev, &acpi_cdev) != AE_NOT_FOUND) { if (i >= 8) { device_printf(dev->dev, "More than 8 outputs detected\n"); return; } status = acpi_GetInteger(acpi_cdev, "_ADR", &device_id); if (ACPI_SUCCESS(status)) { if (!device_id) goto blind_set; opregion->acpi->didl[i] = (u32)(device_id & 0x0f0f); i++; } } #if 0 list_for_each_entry(acpi_cdev, &acpi_video_bus->children, node) { if (i >= 8) { dev_printk(KERN_ERR, &dev->pdev->dev, "More than 8 outputs detected\n"); return; } status = acpi_evaluate_integer(acpi_cdev->handle, "_ADR", NULL, &device_id); if (ACPI_SUCCESS(status)) { if (!device_id) goto blind_set; opregion->acpi->didl[i] = (u32)(device_id & 0x0f0f); i++; } } #endif end: /* If fewer than 8 outputs, the list must be null terminated */ if (i < 8) opregion->acpi->didl[i] = 0; return; blind_set: i = 0; list_for_each_entry(connector, &dev->mode_config.connector_list, head) { int output_type = ACPI_OTHER_OUTPUT; if (i >= 8) { device_printf(dev->dev, "More than 8 outputs detected\n"); return; } switch (connector->connector_type) { case DRM_MODE_CONNECTOR_VGA: case DRM_MODE_CONNECTOR_DVIA: output_type = ACPI_VGA_OUTPUT; break; case DRM_MODE_CONNECTOR_Composite: case DRM_MODE_CONNECTOR_SVIDEO: case DRM_MODE_CONNECTOR_Component: case DRM_MODE_CONNECTOR_9PinDIN: output_type = ACPI_TV_OUTPUT; break; case DRM_MODE_CONNECTOR_DVII: case DRM_MODE_CONNECTOR_DVID: case DRM_MODE_CONNECTOR_DisplayPort: case DRM_MODE_CONNECTOR_HDMIA: case DRM_MODE_CONNECTOR_HDMIB: output_type = ACPI_DIGITAL_OUTPUT; break; case DRM_MODE_CONNECTOR_LVDS: output_type = ACPI_LVDS_OUTPUT; break; } opregion->acpi->didl[i] |= (1<<31) | output_type | i; i++; } goto end; } static void intel_setup_cadls(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct intel_opregion *opregion = &dev_priv->opregion; int i = 0; u32 disp_id; /* Initialize the CADL field by duplicating the DIDL values. * Technically, this is not always correct as display outputs may exist, * but not active. This initialization is necessary for some Clevo * laptops that check this field before processing the brightness and * display switching hotkeys. Just like DIDL, CADL is NULL-terminated if * there are less than eight devices. */ do { disp_id = opregion->acpi->didl[i]; opregion->acpi->cadl[i] = disp_id; } while (++i < 8 && disp_id != 0); } void intel_opregion_init(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct intel_opregion *opregion = &dev_priv->opregion; if (!opregion->header) return; if (opregion->acpi) { if (drm_core_check_feature(dev, DRIVER_MODESET)) { intel_didl_outputs(dev); intel_setup_cadls(dev); } /* Notify BIOS we are ready to handle ACPI video ext notifs. * Right now, all the events are handled by the ACPI video module. * We don't actually need to do anything with them. */ opregion->acpi->csts = 0; opregion->acpi->drdy = 1; system_opregion = opregion; #if 0 register_acpi_notifier(&intel_opregion_notifier); #endif } if (opregion->asle) intel_opregion_enable_asle(dev); } void intel_opregion_fini(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct intel_opregion *opregion = &dev_priv->opregion; if (!opregion->header) return; if (opregion->acpi) { opregion->acpi->drdy = 0; system_opregion = NULL; #if 0 unregister_acpi_notifier(&intel_opregion_notifier); #endif } /* just clear all opregion memory pointers now */ pmap_unmapdev((vm_offset_t)opregion->header, OPREGION_SIZE); opregion->header = NULL; opregion->acpi = NULL; opregion->swsci = NULL; opregion->asle = NULL; opregion->vbt = NULL; } #else -int +void intel_opregion_init(struct drm_device *dev) { - - return (0); } void intel_opregion_fini(struct drm_device *dev) { struct drm_i915_private *dev_priv; struct intel_opregion *opregion; dev_priv = dev->dev_private; opregion = &dev_priv->opregion; if (opregion->header == NULL) return; pmap_unmapdev((vm_offset_t)opregion->header, OPREGION_SIZE); opregion->header = NULL; opregion->acpi = NULL; opregion->swsci = NULL; opregion->asle = NULL; opregion->vbt = NULL; } #endif int intel_opregion_setup(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct intel_opregion *opregion = &dev_priv->opregion; char *base; u32 asls, mboxes; int err = 0; asls = pci_read_config(dev->dev, PCI_ASLS, 4); DRM_DEBUG("graphic opregion physical addr: 0x%x\n", asls); if (asls == 0) { DRM_DEBUG("ACPI OpRegion not supported!\n"); return -ENOTSUP; } base = (void *)pmap_mapbios(asls, OPREGION_SIZE); if (!base) return -ENOMEM; if (memcmp(base, OPREGION_SIGNATURE, 16)) { DRM_DEBUG("opregion signature mismatch\n"); err = -EINVAL; goto err_out; } opregion->header = (struct opregion_header *)base; opregion->vbt = base + OPREGION_VBT_OFFSET; opregion->lid_state = (u32 *)(base + ACPI_CLID); mboxes = opregion->header->mboxes; if (mboxes & MBOX_ACPI) { DRM_DEBUG("Public ACPI methods supported\n"); opregion->acpi = (struct opregion_acpi *)(base + OPREGION_ACPI_OFFSET); } if (mboxes & MBOX_SWSCI) { DRM_DEBUG("SWSCI supported\n"); opregion->swsci = (struct opregion_swsci *)(base + OPREGION_SWSCI_OFFSET); } if (mboxes & MBOX_ASLE) { DRM_DEBUG("ASLE supported\n"); opregion->asle = (struct opregion_asle *)(base + OPREGION_ASLE_OFFSET); } return 0; err_out: pmap_unmapdev((vm_offset_t)base, OPREGION_SIZE); return err; } Index: stable/10 =================================================================== --- stable/10 (revision 295622) +++ stable/10 (revision 295623) Property changes on: stable/10 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r288653,288952,293851