Index: head/sys/dev/drm2/drmP.h =================================================================== --- head/sys/dev/drm2/drmP.h (revision 288652) +++ head/sys/dev/drm2/drmP.h (revision 288653) @@ -1,1813 +1,1807 @@ /** * \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_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) #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_LOG(fmt, ...) do { \ if ((drm_debug & DRM_DEBUGBITS_KMS) != 0) \ printf("[" DRM_NAME "]:pid%d:%s]" fmt, DRM_CURRENTPID, \ __func__ , ##__VA_ARGS__); \ } while (0) #define DRM_LOG_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_LOG_MODE(fmt, ...) do { \ if ((drm_debug & DRM_DEBUGBITS_KMS) != 0) \ printf("[" DRM_NAME "]:pid%d:%s]" fmt, DRM_CURRENTPID, \ __func__ , ##__VA_ARGS__); \ } while (0) #define DRM_LOG_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 UID_ROOT #define DRM_DEV_GID GID_VIDEO #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: head/sys/dev/drm2/drm_crtc.c =================================================================== --- head/sys/dev/drm2/drm_crtc.c (revision 288652) +++ head/sys/dev/drm2/drm_crtc.c (revision 288653) @@ -1,3931 +1,3931 @@ /* * Copyright (c) 2006-2008 Intel Corporation * Copyright (c) 2007 Dave Airlie * Copyright (c) 2008 Red Hat Inc. * * DRM core CRTC related functions * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting documentation, and * that the name of the copyright holders not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. The copyright holders make no representations * about the suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. * * Authors: * Keith Packard * Eric Anholt * Dave Airlie * Jesse Barnes */ #include __FBSDID("$FreeBSD$"); #include #include #include #include static void drm_property_destroy_blob(struct drm_device *dev, struct drm_property_blob *blob); /* Avoid boilerplate. I'm tired of typing. */ #define DRM_ENUM_NAME_FN(fnname, list) \ char *fnname(int val) \ { \ int i; \ for (i = 0; i < ARRAY_SIZE(list); i++) { \ if (list[i].type == val) \ return list[i].name; \ } \ return "(unknown)"; \ } /* * Global properties */ static struct drm_prop_enum_list drm_dpms_enum_list[] = { { DRM_MODE_DPMS_ON, "On" }, { DRM_MODE_DPMS_STANDBY, "Standby" }, { DRM_MODE_DPMS_SUSPEND, "Suspend" }, { DRM_MODE_DPMS_OFF, "Off" } }; DRM_ENUM_NAME_FN(drm_get_dpms_name, drm_dpms_enum_list) /* * Optional properties */ static struct drm_prop_enum_list drm_scaling_mode_enum_list[] = { { DRM_MODE_SCALE_NONE, "None" }, { DRM_MODE_SCALE_FULLSCREEN, "Full" }, { DRM_MODE_SCALE_CENTER, "Center" }, { DRM_MODE_SCALE_ASPECT, "Full aspect" }, }; static struct drm_prop_enum_list drm_dithering_mode_enum_list[] = { { DRM_MODE_DITHERING_OFF, "Off" }, { DRM_MODE_DITHERING_ON, "On" }, { DRM_MODE_DITHERING_AUTO, "Automatic" }, }; /* * Non-global properties, but "required" for certain connectors. */ static struct drm_prop_enum_list drm_dvi_i_select_enum_list[] = { { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */ { DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */ { DRM_MODE_SUBCONNECTOR_DVIA, "DVI-A" }, /* DVI-I */ }; DRM_ENUM_NAME_FN(drm_get_dvi_i_select_name, drm_dvi_i_select_enum_list) static struct drm_prop_enum_list drm_dvi_i_subconnector_enum_list[] = { { DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I and TV-out */ { DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */ { DRM_MODE_SUBCONNECTOR_DVIA, "DVI-A" }, /* DVI-I */ }; DRM_ENUM_NAME_FN(drm_get_dvi_i_subconnector_name, drm_dvi_i_subconnector_enum_list) static struct drm_prop_enum_list drm_tv_select_enum_list[] = { { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */ { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */ { DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */ { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */ { DRM_MODE_SUBCONNECTOR_SCART, "SCART" }, /* TV-out */ }; DRM_ENUM_NAME_FN(drm_get_tv_select_name, drm_tv_select_enum_list) static struct drm_prop_enum_list drm_tv_subconnector_enum_list[] = { { DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I and TV-out */ { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */ { DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */ { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */ { DRM_MODE_SUBCONNECTOR_SCART, "SCART" }, /* TV-out */ }; DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name, drm_tv_subconnector_enum_list) static struct drm_prop_enum_list drm_dirty_info_enum_list[] = { { DRM_MODE_DIRTY_OFF, "Off" }, { DRM_MODE_DIRTY_ON, "On" }, { DRM_MODE_DIRTY_ANNOTATE, "Annotate" }, }; DRM_ENUM_NAME_FN(drm_get_dirty_info_name, drm_dirty_info_enum_list) struct drm_conn_prop_enum_list { int type; char *name; int count; }; /* * Connector and encoder types. */ static struct drm_conn_prop_enum_list drm_connector_enum_list[] = { { DRM_MODE_CONNECTOR_Unknown, "Unknown", 0 }, { DRM_MODE_CONNECTOR_VGA, "VGA", 0 }, { DRM_MODE_CONNECTOR_DVII, "DVI-I", 0 }, { DRM_MODE_CONNECTOR_DVID, "DVI-D", 0 }, { DRM_MODE_CONNECTOR_DVIA, "DVI-A", 0 }, { DRM_MODE_CONNECTOR_Composite, "Composite", 0 }, { DRM_MODE_CONNECTOR_SVIDEO, "SVIDEO", 0 }, { DRM_MODE_CONNECTOR_LVDS, "LVDS", 0 }, { DRM_MODE_CONNECTOR_Component, "Component", 0 }, { DRM_MODE_CONNECTOR_9PinDIN, "DIN", 0 }, { DRM_MODE_CONNECTOR_DisplayPort, "DP", 0 }, { DRM_MODE_CONNECTOR_HDMIA, "HDMI-A", 0 }, { DRM_MODE_CONNECTOR_HDMIB, "HDMI-B", 0 }, { DRM_MODE_CONNECTOR_TV, "TV", 0 }, { DRM_MODE_CONNECTOR_eDP, "eDP", 0 }, { DRM_MODE_CONNECTOR_VIRTUAL, "Virtual", 0}, }; static struct drm_prop_enum_list drm_encoder_enum_list[] = { { DRM_MODE_ENCODER_NONE, "None" }, { DRM_MODE_ENCODER_DAC, "DAC" }, { DRM_MODE_ENCODER_TMDS, "TMDS" }, { DRM_MODE_ENCODER_LVDS, "LVDS" }, { DRM_MODE_ENCODER_TVDAC, "TV" }, { DRM_MODE_ENCODER_VIRTUAL, "Virtual" }, }; char *drm_get_encoder_name(struct drm_encoder *encoder) { static char buf[32]; snprintf(buf, 32, "%s-%d", drm_encoder_enum_list[encoder->encoder_type].name, encoder->base.id); return buf; } EXPORT_SYMBOL(drm_get_encoder_name); char *drm_get_connector_name(struct drm_connector *connector) { static char buf[32]; snprintf(buf, 32, "%s-%d", drm_connector_enum_list[connector->connector_type].name, connector->connector_type_id); return buf; } EXPORT_SYMBOL(drm_get_connector_name); char *drm_get_connector_status_name(enum drm_connector_status status) { if (status == connector_status_connected) return "connected"; else if (status == connector_status_disconnected) return "disconnected"; else return "unknown"; } /** * drm_mode_object_get - allocate a new identifier * @dev: DRM device * @ptr: object pointer, used to generate unique ID * @type: object type * * LOCKING: * * Create a unique identifier based on @ptr in @dev's identifier space. Used * for tracking modes, CRTCs and connectors. * * RETURNS: * New unique (relative to other objects in @dev) integer identifier for the * object. */ static int drm_mode_object_get(struct drm_device *dev, struct drm_mode_object *obj, uint32_t obj_type) { int new_id = 0; int ret; ret = drm_gem_name_create(&dev->mode_config.crtc_names, obj, &new_id); if (ret) return ret; obj->id = new_id; obj->type = obj_type; return 0; } /** * drm_mode_object_put - free an identifer * @dev: DRM device * @id: ID to free * * LOCKING: * Caller must hold DRM mode_config lock. * * Free @id from @dev's unique identifier pool. */ static void drm_mode_object_put(struct drm_device *dev, struct drm_mode_object *object) { drm_gem_names_remove(&dev->mode_config.crtc_names, object->id); } struct drm_mode_object *drm_mode_object_find(struct drm_device *dev, uint32_t id, uint32_t type) { struct drm_mode_object *obj = NULL; obj = drm_gem_name_ref(&dev->mode_config.crtc_names, id, NULL); if (!obj || (obj->type != type) || (obj->id != id)) obj = NULL; return obj; } EXPORT_SYMBOL(drm_mode_object_find); /** * drm_framebuffer_init - initialize a framebuffer * @dev: DRM device * * LOCKING: * Caller must hold mode config lock. * * Allocates an ID for the framebuffer's parent mode object, sets its mode * functions & device file and adds it to the master fd list. * * RETURNS: * Zero on success, error code on failure. */ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, const struct drm_framebuffer_funcs *funcs) { int ret; refcount_init(&fb->refcount, 1); ret = drm_mode_object_get(dev, &fb->base, DRM_MODE_OBJECT_FB); if (ret) return ret; fb->dev = dev; fb->funcs = funcs; dev->mode_config.num_fb++; list_add(&fb->head, &dev->mode_config.fb_list); return 0; } EXPORT_SYMBOL(drm_framebuffer_init); static void drm_framebuffer_free(struct drm_framebuffer *fb) { fb->funcs->destroy(fb); } /** * drm_framebuffer_unreference - unref a framebuffer * * LOCKING: * Caller must hold mode config lock. */ void drm_framebuffer_unreference(struct drm_framebuffer *fb) { struct drm_device *dev = fb->dev; DRM_DEBUG("FB ID: %d\n", fb->base.id); if (!sx_xlocked(&dev->mode_config.mutex)) DRM_WARNING("%s: dev->mode_config.mutex not locked\n", __func__); if (refcount_release(&fb->refcount)) drm_framebuffer_free(fb); } EXPORT_SYMBOL(drm_framebuffer_unreference); /** * drm_framebuffer_reference - incr the fb refcnt */ void drm_framebuffer_reference(struct drm_framebuffer *fb) { DRM_DEBUG("FB ID: %d\n", fb->base.id); refcount_acquire(&fb->refcount); } EXPORT_SYMBOL(drm_framebuffer_reference); /** * drm_framebuffer_cleanup - remove a framebuffer object * @fb: framebuffer to remove * * LOCKING: * Caller must hold mode config lock. * * Scans all the CRTCs in @dev's mode_config. If they're using @fb, removes * it, setting it to NULL. */ void drm_framebuffer_cleanup(struct drm_framebuffer *fb) { struct drm_device *dev = fb->dev; /* * This could be moved to drm_framebuffer_remove(), but for * debugging is nice to keep around the list of fb's that are * no longer associated w/ a drm_file but are not unreferenced * yet. (i915 and omapdrm have debugfs files which will show * this.) */ drm_mode_object_put(dev, &fb->base); list_del(&fb->head); dev->mode_config.num_fb--; } EXPORT_SYMBOL(drm_framebuffer_cleanup); /** * drm_framebuffer_remove - remove and unreference a framebuffer object * @fb: framebuffer to remove * * LOCKING: * Caller must hold mode config lock. * * Scans all the CRTCs and planes in @dev's mode_config. If they're * using @fb, removes it, setting it to NULL. */ void drm_framebuffer_remove(struct drm_framebuffer *fb) { struct drm_device *dev = fb->dev; struct drm_crtc *crtc; struct drm_plane *plane; struct drm_mode_set set; int ret; /* remove from any CRTC */ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { if (crtc->fb == fb) { /* should turn off the crtc */ memset(&set, 0, sizeof(struct drm_mode_set)); set.crtc = crtc; set.fb = NULL; ret = crtc->funcs->set_config(&set); if (ret) DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc); } } list_for_each_entry(plane, &dev->mode_config.plane_list, head) { if (plane->fb == fb) { /* should turn off the crtc */ ret = plane->funcs->disable_plane(plane); if (ret) DRM_ERROR("failed to disable plane with busy fb\n"); /* disconnect the plane from the fb and crtc: */ plane->fb = NULL; plane->crtc = NULL; } } list_del(&fb->filp_head); drm_framebuffer_unreference(fb); } EXPORT_SYMBOL(drm_framebuffer_remove); /** * drm_crtc_init - Initialise a new CRTC object * @dev: DRM device * @crtc: CRTC object to init * @funcs: callbacks for the new CRTC * * LOCKING: * Takes mode_config lock. * * Inits a new object created as base part of an driver crtc object. * * RETURNS: * Zero on success, error code on failure. */ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, const struct drm_crtc_funcs *funcs) { int ret; crtc->dev = dev; crtc->funcs = funcs; crtc->invert_dimensions = false; sx_xlock(&dev->mode_config.mutex); ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC); if (ret) goto out; crtc->base.properties = &crtc->properties; list_add_tail(&crtc->head, &dev->mode_config.crtc_list); dev->mode_config.num_crtc++; out: sx_xunlock(&dev->mode_config.mutex); return ret; } EXPORT_SYMBOL(drm_crtc_init); /** * drm_crtc_cleanup - Cleans up the core crtc usage. * @crtc: CRTC to cleanup * * LOCKING: * Caller must hold mode config lock. * * Cleanup @crtc. Removes from drm modesetting space * does NOT free object, caller does that. */ void drm_crtc_cleanup(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; free(crtc->gamma_store, DRM_MEM_KMS); crtc->gamma_store = NULL; drm_mode_object_put(dev, &crtc->base); list_del(&crtc->head); dev->mode_config.num_crtc--; } EXPORT_SYMBOL(drm_crtc_cleanup); /** * drm_mode_probed_add - add a mode to a connector's probed mode list * @connector: connector the new mode * @mode: mode data * * LOCKING: * Caller must hold mode config lock. * * Add @mode to @connector's mode list for later use. */ void drm_mode_probed_add(struct drm_connector *connector, struct drm_display_mode *mode) { list_add(&mode->head, &connector->probed_modes); } EXPORT_SYMBOL(drm_mode_probed_add); /** * drm_mode_remove - remove and free a mode * @connector: connector list to modify * @mode: mode to remove * * LOCKING: * Caller must hold mode config lock. * * Remove @mode from @connector's mode list, then free it. */ void drm_mode_remove(struct drm_connector *connector, struct drm_display_mode *mode) { list_del(&mode->head); drm_mode_destroy(connector->dev, mode); } EXPORT_SYMBOL(drm_mode_remove); /** * drm_connector_init - Init a preallocated connector * @dev: DRM device * @connector: the connector to init * @funcs: callbacks for this connector * @name: user visible name of the connector * * LOCKING: * Takes mode config lock. * * Initialises a preallocated connector. Connectors should be * subclassed as part of driver connector objects. * * RETURNS: * Zero on success, error code on failure. */ int drm_connector_init(struct drm_device *dev, struct drm_connector *connector, const struct drm_connector_funcs *funcs, int connector_type) { int ret; sx_xlock(&dev->mode_config.mutex); ret = drm_mode_object_get(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR); if (ret) goto out; connector->base.properties = &connector->properties; connector->dev = dev; connector->funcs = funcs; connector->connector_type = connector_type; connector->connector_type_id = ++drm_connector_enum_list[connector_type].count; /* TODO */ INIT_LIST_HEAD(&connector->user_modes); INIT_LIST_HEAD(&connector->probed_modes); INIT_LIST_HEAD(&connector->modes); connector->edid_blob_ptr = NULL; connector->status = connector_status_unknown; list_add_tail(&connector->head, &dev->mode_config.connector_list); dev->mode_config.num_connector++; if (connector_type != DRM_MODE_CONNECTOR_VIRTUAL) drm_object_attach_property(&connector->base, dev->mode_config.edid_property, 0); drm_object_attach_property(&connector->base, dev->mode_config.dpms_property, 0); out: sx_xunlock(&dev->mode_config.mutex); return ret; } EXPORT_SYMBOL(drm_connector_init); /** * drm_connector_cleanup - cleans up an initialised connector * @connector: connector to cleanup * * LOCKING: * Takes mode config lock. * * Cleans up the connector but doesn't free the object. */ void drm_connector_cleanup(struct drm_connector *connector) { struct drm_device *dev = connector->dev; struct drm_display_mode *mode, *t; list_for_each_entry_safe(mode, t, &connector->probed_modes, head) drm_mode_remove(connector, mode); list_for_each_entry_safe(mode, t, &connector->modes, head) drm_mode_remove(connector, mode); list_for_each_entry_safe(mode, t, &connector->user_modes, head) drm_mode_remove(connector, mode); sx_xlock(&dev->mode_config.mutex); drm_mode_object_put(dev, &connector->base); list_del(&connector->head); dev->mode_config.num_connector--; sx_xunlock(&dev->mode_config.mutex); } EXPORT_SYMBOL(drm_connector_cleanup); void drm_connector_unplug_all(struct drm_device *dev) { #ifdef FREEBSD_NOTYET struct drm_connector *connector; /* taking the mode config mutex ends up in a clash with sysfs */ list_for_each_entry(connector, &dev->mode_config.connector_list, head) drm_sysfs_connector_remove(connector); #endif /* FREEBSD_NOTYET */ } EXPORT_SYMBOL(drm_connector_unplug_all); int drm_encoder_init(struct drm_device *dev, struct drm_encoder *encoder, const struct drm_encoder_funcs *funcs, int encoder_type) { int ret; sx_xlock(&dev->mode_config.mutex); ret = drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER); if (ret) goto out; encoder->dev = dev; encoder->encoder_type = encoder_type; encoder->funcs = funcs; list_add_tail(&encoder->head, &dev->mode_config.encoder_list); dev->mode_config.num_encoder++; out: sx_xunlock(&dev->mode_config.mutex); return ret; } EXPORT_SYMBOL(drm_encoder_init); void drm_encoder_cleanup(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; sx_xlock(&dev->mode_config.mutex); drm_mode_object_put(dev, &encoder->base); list_del(&encoder->head); dev->mode_config.num_encoder--; sx_xunlock(&dev->mode_config.mutex); } EXPORT_SYMBOL(drm_encoder_cleanup); int drm_plane_init(struct drm_device *dev, struct drm_plane *plane, unsigned long possible_crtcs, const struct drm_plane_funcs *funcs, const uint32_t *formats, uint32_t format_count, bool priv) { int ret; sx_xlock(&dev->mode_config.mutex); ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE); if (ret) goto out; plane->base.properties = &plane->properties; plane->dev = dev; plane->funcs = funcs; plane->format_types = malloc(sizeof(uint32_t) * format_count, - DRM_MEM_KMS, M_WAITOK); + DRM_MEM_KMS, M_NOWAIT); if (!plane->format_types) { DRM_DEBUG_KMS("out of memory when allocating plane\n"); drm_mode_object_put(dev, &plane->base); ret = -ENOMEM; goto out; } memcpy(plane->format_types, formats, format_count * sizeof(uint32_t)); plane->format_count = format_count; plane->possible_crtcs = possible_crtcs; /* private planes are not exposed to userspace, but depending on * display hardware, might be convenient to allow sharing programming * for the scanout engine with the crtc implementation. */ if (!priv) { list_add_tail(&plane->head, &dev->mode_config.plane_list); dev->mode_config.num_plane++; } else { INIT_LIST_HEAD(&plane->head); } out: sx_xunlock(&dev->mode_config.mutex); return ret; } EXPORT_SYMBOL(drm_plane_init); void drm_plane_cleanup(struct drm_plane *plane) { struct drm_device *dev = plane->dev; sx_xlock(&dev->mode_config.mutex); free(plane->format_types, DRM_MEM_KMS); drm_mode_object_put(dev, &plane->base); /* if not added to a list, it must be a private plane */ if (!list_empty(&plane->head)) { list_del(&plane->head); dev->mode_config.num_plane--; } sx_xunlock(&dev->mode_config.mutex); } EXPORT_SYMBOL(drm_plane_cleanup); /** * drm_mode_create - create a new display mode * @dev: DRM device * * LOCKING: * Caller must hold DRM mode_config lock. * * Create a new drm_display_mode, give it an ID, and return it. * * RETURNS: * Pointer to new mode on success, NULL on error. */ struct drm_display_mode *drm_mode_create(struct drm_device *dev) { struct drm_display_mode *nmode; nmode = malloc(sizeof(struct drm_display_mode), DRM_MEM_KMS, M_WAITOK | M_ZERO); if (!nmode) return NULL; if (drm_mode_object_get(dev, &nmode->base, DRM_MODE_OBJECT_MODE)) { free(nmode, DRM_MEM_KMS); return NULL; } return nmode; } EXPORT_SYMBOL(drm_mode_create); /** * drm_mode_destroy - remove a mode * @dev: DRM device * @mode: mode to remove * * LOCKING: * Caller must hold mode config lock. * * Free @mode's unique identifier, then free it. */ void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode) { if (!mode) return; drm_mode_object_put(dev, &mode->base); free(mode, DRM_MEM_KMS); } EXPORT_SYMBOL(drm_mode_destroy); static int drm_mode_create_standard_connector_properties(struct drm_device *dev) { struct drm_property *edid; struct drm_property *dpms; /* * Standard properties (apply to all connectors) */ edid = drm_property_create(dev, DRM_MODE_PROP_BLOB | DRM_MODE_PROP_IMMUTABLE, "EDID", 0); dev->mode_config.edid_property = edid; dpms = drm_property_create_enum(dev, 0, "DPMS", drm_dpms_enum_list, ARRAY_SIZE(drm_dpms_enum_list)); dev->mode_config.dpms_property = dpms; return 0; } /** * drm_mode_create_dvi_i_properties - create DVI-I specific connector properties * @dev: DRM device * * Called by a driver the first time a DVI-I connector is made. */ int drm_mode_create_dvi_i_properties(struct drm_device *dev) { struct drm_property *dvi_i_selector; struct drm_property *dvi_i_subconnector; if (dev->mode_config.dvi_i_select_subconnector_property) return 0; dvi_i_selector = drm_property_create_enum(dev, 0, "select subconnector", drm_dvi_i_select_enum_list, ARRAY_SIZE(drm_dvi_i_select_enum_list)); dev->mode_config.dvi_i_select_subconnector_property = dvi_i_selector; dvi_i_subconnector = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, "subconnector", drm_dvi_i_subconnector_enum_list, ARRAY_SIZE(drm_dvi_i_subconnector_enum_list)); dev->mode_config.dvi_i_subconnector_property = dvi_i_subconnector; return 0; } EXPORT_SYMBOL(drm_mode_create_dvi_i_properties); /** * drm_create_tv_properties - create TV specific connector properties * @dev: DRM device * @num_modes: number of different TV formats (modes) supported * @modes: array of pointers to strings containing name of each format * * Called by a driver's TV initialization routine, this function creates * the TV specific connector properties for a given device. Caller is * responsible for allocating a list of format names and passing them to * this routine. */ int drm_mode_create_tv_properties(struct drm_device *dev, int num_modes, char *modes[]) { struct drm_property *tv_selector; struct drm_property *tv_subconnector; int i; if (dev->mode_config.tv_select_subconnector_property) return 0; /* * Basic connector properties */ tv_selector = drm_property_create_enum(dev, 0, "select subconnector", drm_tv_select_enum_list, ARRAY_SIZE(drm_tv_select_enum_list)); dev->mode_config.tv_select_subconnector_property = tv_selector; tv_subconnector = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, "subconnector", drm_tv_subconnector_enum_list, ARRAY_SIZE(drm_tv_subconnector_enum_list)); dev->mode_config.tv_subconnector_property = tv_subconnector; /* * Other, TV specific properties: margins & TV modes. */ dev->mode_config.tv_left_margin_property = drm_property_create_range(dev, 0, "left margin", 0, 100); dev->mode_config.tv_right_margin_property = drm_property_create_range(dev, 0, "right margin", 0, 100); dev->mode_config.tv_top_margin_property = drm_property_create_range(dev, 0, "top margin", 0, 100); dev->mode_config.tv_bottom_margin_property = drm_property_create_range(dev, 0, "bottom margin", 0, 100); dev->mode_config.tv_mode_property = drm_property_create(dev, DRM_MODE_PROP_ENUM, "mode", num_modes); for (i = 0; i < num_modes; i++) drm_property_add_enum(dev->mode_config.tv_mode_property, i, i, modes[i]); dev->mode_config.tv_brightness_property = drm_property_create_range(dev, 0, "brightness", 0, 100); dev->mode_config.tv_contrast_property = drm_property_create_range(dev, 0, "contrast", 0, 100); dev->mode_config.tv_flicker_reduction_property = drm_property_create_range(dev, 0, "flicker reduction", 0, 100); dev->mode_config.tv_overscan_property = drm_property_create_range(dev, 0, "overscan", 0, 100); dev->mode_config.tv_saturation_property = drm_property_create_range(dev, 0, "saturation", 0, 100); dev->mode_config.tv_hue_property = drm_property_create_range(dev, 0, "hue", 0, 100); return 0; } EXPORT_SYMBOL(drm_mode_create_tv_properties); /** * drm_mode_create_scaling_mode_property - create scaling mode property * @dev: DRM device * * Called by a driver the first time it's needed, must be attached to desired * connectors. */ int drm_mode_create_scaling_mode_property(struct drm_device *dev) { struct drm_property *scaling_mode; if (dev->mode_config.scaling_mode_property) return 0; scaling_mode = drm_property_create_enum(dev, 0, "scaling mode", drm_scaling_mode_enum_list, ARRAY_SIZE(drm_scaling_mode_enum_list)); dev->mode_config.scaling_mode_property = scaling_mode; return 0; } EXPORT_SYMBOL(drm_mode_create_scaling_mode_property); /** * drm_mode_create_dithering_property - create dithering property * @dev: DRM device * * Called by a driver the first time it's needed, must be attached to desired * connectors. */ int drm_mode_create_dithering_property(struct drm_device *dev) { struct drm_property *dithering_mode; if (dev->mode_config.dithering_mode_property) return 0; dithering_mode = drm_property_create_enum(dev, 0, "dithering", drm_dithering_mode_enum_list, ARRAY_SIZE(drm_dithering_mode_enum_list)); dev->mode_config.dithering_mode_property = dithering_mode; return 0; } EXPORT_SYMBOL(drm_mode_create_dithering_property); /** * drm_mode_create_dirty_property - create dirty property * @dev: DRM device * * Called by a driver the first time it's needed, must be attached to desired * connectors. */ int drm_mode_create_dirty_info_property(struct drm_device *dev) { struct drm_property *dirty_info; if (dev->mode_config.dirty_info_property) return 0; dirty_info = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, "dirty", drm_dirty_info_enum_list, ARRAY_SIZE(drm_dirty_info_enum_list)); dev->mode_config.dirty_info_property = dirty_info; return 0; } EXPORT_SYMBOL(drm_mode_create_dirty_info_property); /** * drm_mode_config_init - initialize DRM mode_configuration structure * @dev: DRM device * * LOCKING: * None, should happen single threaded at init time. * * Initialize @dev's mode_config structure, used for tracking the graphics * configuration of @dev. */ void drm_mode_config_init(struct drm_device *dev) { sx_init(&dev->mode_config.mutex, "kmslk"); INIT_LIST_HEAD(&dev->mode_config.fb_list); INIT_LIST_HEAD(&dev->mode_config.crtc_list); INIT_LIST_HEAD(&dev->mode_config.connector_list); INIT_LIST_HEAD(&dev->mode_config.encoder_list); INIT_LIST_HEAD(&dev->mode_config.property_list); INIT_LIST_HEAD(&dev->mode_config.property_blob_list); INIT_LIST_HEAD(&dev->mode_config.plane_list); drm_gem_names_init(&dev->mode_config.crtc_names); sx_xlock(&dev->mode_config.mutex); drm_mode_create_standard_connector_properties(dev); sx_xunlock(&dev->mode_config.mutex); /* Just to be sure */ dev->mode_config.num_fb = 0; dev->mode_config.num_connector = 0; dev->mode_config.num_crtc = 0; dev->mode_config.num_encoder = 0; } EXPORT_SYMBOL(drm_mode_config_init); int drm_mode_group_init(struct drm_device *dev, struct drm_mode_group *group) { uint32_t total_objects = 0; total_objects += dev->mode_config.num_crtc; total_objects += dev->mode_config.num_connector; total_objects += dev->mode_config.num_encoder; group->id_list = malloc(total_objects * sizeof(uint32_t), - DRM_MEM_KMS, M_WAITOK | M_ZERO); + DRM_MEM_KMS, M_NOWAIT | M_ZERO); if (!group->id_list) return -ENOMEM; group->num_crtcs = 0; group->num_connectors = 0; group->num_encoders = 0; return 0; } void drm_mode_group_free(struct drm_mode_group *group) { free(group->id_list, DRM_MEM_KMS); group->id_list = NULL; } int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group) { struct drm_crtc *crtc; struct drm_encoder *encoder; struct drm_connector *connector; int ret; if ((ret = drm_mode_group_init(dev, group))) return ret; list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) group->id_list[group->num_crtcs++] = crtc->base.id; list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) group->id_list[group->num_crtcs + group->num_encoders++] = encoder->base.id; list_for_each_entry(connector, &dev->mode_config.connector_list, head) group->id_list[group->num_crtcs + group->num_encoders + group->num_connectors++] = connector->base.id; return 0; } EXPORT_SYMBOL(drm_mode_group_init_legacy_group); /** * drm_mode_config_cleanup - free up DRM mode_config info * @dev: DRM device * * LOCKING: * Caller must hold mode config lock. * * Free up all the connectors and CRTCs associated with this DRM device, then * free up the framebuffers and associated buffer objects. * * FIXME: cleanup any dangling user buffer objects too */ void drm_mode_config_cleanup(struct drm_device *dev) { struct drm_connector *connector, *ot; struct drm_crtc *crtc, *ct; struct drm_encoder *encoder, *enct; struct drm_framebuffer *fb, *fbt; struct drm_property *property, *pt; struct drm_property_blob *blob, *bt; struct drm_plane *plane, *plt; list_for_each_entry_safe(encoder, enct, &dev->mode_config.encoder_list, head) { encoder->funcs->destroy(encoder); } list_for_each_entry_safe(connector, ot, &dev->mode_config.connector_list, head) { connector->funcs->destroy(connector); } list_for_each_entry_safe(property, pt, &dev->mode_config.property_list, head) { drm_property_destroy(dev, property); } list_for_each_entry_safe(blob, bt, &dev->mode_config.property_blob_list, head) { drm_property_destroy_blob(dev, blob); } list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { drm_framebuffer_remove(fb); } list_for_each_entry_safe(plane, plt, &dev->mode_config.plane_list, head) { plane->funcs->destroy(plane); } list_for_each_entry_safe(crtc, ct, &dev->mode_config.crtc_list, head) { crtc->funcs->destroy(crtc); } drm_gem_names_fini(&dev->mode_config.crtc_names); } EXPORT_SYMBOL(drm_mode_config_cleanup); /** * drm_crtc_convert_to_umode - convert a drm_display_mode into a modeinfo * @out: drm_mode_modeinfo struct to return to the user * @in: drm_display_mode to use * * LOCKING: * None. * * Convert a drm_display_mode into a drm_mode_modeinfo structure to return to * the user. */ static void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out, const struct drm_display_mode *in) { if (in->hdisplay > USHRT_MAX || in->hsync_start > USHRT_MAX || in->hsync_end > USHRT_MAX || in->htotal > USHRT_MAX || in->hskew > USHRT_MAX || in->vdisplay > USHRT_MAX || in->vsync_start > USHRT_MAX || in->vsync_end > USHRT_MAX || in->vtotal > USHRT_MAX || in->vscan > USHRT_MAX) DRM_WARNING("timing values too large for mode info\n"); out->clock = in->clock; out->hdisplay = in->hdisplay; out->hsync_start = in->hsync_start; out->hsync_end = in->hsync_end; out->htotal = in->htotal; out->hskew = in->hskew; out->vdisplay = in->vdisplay; out->vsync_start = in->vsync_start; out->vsync_end = in->vsync_end; out->vtotal = in->vtotal; out->vscan = in->vscan; out->vrefresh = in->vrefresh; out->flags = in->flags; out->type = in->type; strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN); out->name[DRM_DISPLAY_MODE_LEN-1] = 0; } /** * drm_crtc_convert_to_umode - convert a modeinfo into a drm_display_mode * @out: drm_display_mode to return to the user * @in: drm_mode_modeinfo to use * * LOCKING: * None. * * Convert a drm_mode_modeinfo into a drm_display_mode structure to return to * the caller. * * RETURNS: * Zero on success, errno on failure. */ static int drm_crtc_convert_umode(struct drm_display_mode *out, const struct drm_mode_modeinfo *in) { if (in->clock > INT_MAX || in->vrefresh > INT_MAX) return -ERANGE; out->clock = in->clock; out->hdisplay = in->hdisplay; out->hsync_start = in->hsync_start; out->hsync_end = in->hsync_end; out->htotal = in->htotal; out->hskew = in->hskew; out->vdisplay = in->vdisplay; out->vsync_start = in->vsync_start; out->vsync_end = in->vsync_end; out->vtotal = in->vtotal; out->vscan = in->vscan; out->vrefresh = in->vrefresh; out->flags = in->flags; out->type = in->type; strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN); out->name[DRM_DISPLAY_MODE_LEN-1] = 0; return 0; } /** * drm_mode_getresources - get graphics configuration * @inode: inode from the ioctl * @filp: file * from the ioctl * @cmd: cmd from ioctl * @arg: arg from ioctl * * LOCKING: * Takes mode config lock. * * Construct a set of configuration description structures and return * them to the user, including CRTC, connector and framebuffer configuration. * * Called by the user via ioctl. * * RETURNS: * Zero on success, errno on failure. */ int drm_mode_getresources(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_card_res *card_res = data; struct list_head *lh; struct drm_framebuffer *fb; struct drm_connector *connector; struct drm_crtc *crtc; struct drm_encoder *encoder; int ret = 0; int connector_count = 0; int crtc_count = 0; int fb_count = 0; int encoder_count = 0; int copied = 0, i; uint32_t __user *fb_id; uint32_t __user *crtc_id; uint32_t __user *connector_id; uint32_t __user *encoder_id; struct drm_mode_group *mode_group; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; sx_xlock(&dev->mode_config.mutex); /* * For the non-control nodes we need to limit the list of resources * by IDs in the group list for this node */ list_for_each(lh, &file_priv->fbs) fb_count++; mode_group = &file_priv->master->minor->mode_group; if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { list_for_each(lh, &dev->mode_config.crtc_list) crtc_count++; list_for_each(lh, &dev->mode_config.connector_list) connector_count++; list_for_each(lh, &dev->mode_config.encoder_list) encoder_count++; } else { crtc_count = mode_group->num_crtcs; connector_count = mode_group->num_connectors; encoder_count = mode_group->num_encoders; } card_res->max_height = dev->mode_config.max_height; card_res->min_height = dev->mode_config.min_height; card_res->max_width = dev->mode_config.max_width; card_res->min_width = dev->mode_config.min_width; /* handle this in 4 parts */ /* FBs */ if (card_res->count_fbs >= fb_count) { copied = 0; fb_id = (uint32_t __user *)(unsigned long)card_res->fb_id_ptr; list_for_each_entry(fb, &file_priv->fbs, filp_head) { if (put_user(fb->base.id, fb_id + copied)) { ret = -EFAULT; goto out; } copied++; } } card_res->count_fbs = fb_count; /* CRTCs */ if (card_res->count_crtcs >= crtc_count) { copied = 0; crtc_id = (uint32_t __user *)(unsigned long)card_res->crtc_id_ptr; if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id); if (put_user(crtc->base.id, crtc_id + copied)) { ret = -EFAULT; goto out; } copied++; } } else { for (i = 0; i < mode_group->num_crtcs; i++) { if (put_user(mode_group->id_list[i], crtc_id + copied)) { ret = -EFAULT; goto out; } copied++; } } } card_res->count_crtcs = crtc_count; /* Encoders */ if (card_res->count_encoders >= encoder_count) { copied = 0; encoder_id = (uint32_t __user *)(unsigned long)card_res->encoder_id_ptr; if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { DRM_DEBUG_KMS("[ENCODER:%d:%s]\n", encoder->base.id, drm_get_encoder_name(encoder)); if (put_user(encoder->base.id, encoder_id + copied)) { ret = -EFAULT; goto out; } copied++; } } else { for (i = mode_group->num_crtcs; i < mode_group->num_crtcs + mode_group->num_encoders; i++) { if (put_user(mode_group->id_list[i], encoder_id + copied)) { ret = -EFAULT; goto out; } copied++; } } } card_res->count_encoders = encoder_count; /* Connectors */ if (card_res->count_connectors >= connector_count) { copied = 0; connector_id = (uint32_t __user *)(unsigned long)card_res->connector_id_ptr; if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { list_for_each_entry(connector, &dev->mode_config.connector_list, head) { DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id, drm_get_connector_name(connector)); if (put_user(connector->base.id, connector_id + copied)) { ret = -EFAULT; goto out; } copied++; } } else { int start = mode_group->num_crtcs + mode_group->num_encoders; for (i = start; i < start + mode_group->num_connectors; i++) { if (put_user(mode_group->id_list[i], connector_id + copied)) { ret = -EFAULT; goto out; } copied++; } } } card_res->count_connectors = connector_count; DRM_DEBUG_KMS("CRTC[%d] CONNECTORS[%d] ENCODERS[%d]\n", card_res->count_crtcs, card_res->count_connectors, card_res->count_encoders); out: sx_xunlock(&dev->mode_config.mutex); return ret; } /** * drm_mode_getcrtc - get CRTC configuration * @inode: inode from the ioctl * @filp: file * from the ioctl * @cmd: cmd from ioctl * @arg: arg from ioctl * * LOCKING: * Takes mode config lock. * * Construct a CRTC configuration structure to return to the user. * * Called by the user via ioctl. * * RETURNS: * Zero on success, errno on failure. */ int drm_mode_getcrtc(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_crtc *crtc_resp = data; struct drm_crtc *crtc; struct drm_mode_object *obj; int ret = 0; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; sx_xlock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, crtc_resp->crtc_id, DRM_MODE_OBJECT_CRTC); if (!obj) { ret = -EINVAL; goto out; } crtc = obj_to_crtc(obj); crtc_resp->x = crtc->x; crtc_resp->y = crtc->y; crtc_resp->gamma_size = crtc->gamma_size; if (crtc->fb) crtc_resp->fb_id = crtc->fb->base.id; else crtc_resp->fb_id = 0; if (crtc->enabled) { drm_crtc_convert_to_umode(&crtc_resp->mode, &crtc->mode); crtc_resp->mode_valid = 1; } else { crtc_resp->mode_valid = 0; } out: sx_xunlock(&dev->mode_config.mutex); return ret; } /** * drm_mode_getconnector - get connector configuration * @inode: inode from the ioctl * @filp: file * from the ioctl * @cmd: cmd from ioctl * @arg: arg from ioctl * * LOCKING: * Takes mode config lock. * * Construct a connector configuration structure to return to the user. * * Called by the user via ioctl. * * RETURNS: * Zero on success, errno on failure. */ int drm_mode_getconnector(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_get_connector *out_resp = data; struct drm_mode_object *obj; struct drm_connector *connector; struct drm_display_mode *mode; int mode_count = 0; int props_count = 0; int encoders_count = 0; int ret = 0; int copied = 0; int i; struct drm_mode_modeinfo u_mode; struct drm_mode_modeinfo __user *mode_ptr; uint32_t __user *prop_ptr; uint64_t __user *prop_values; uint32_t __user *encoder_ptr; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo)); DRM_DEBUG_KMS("[CONNECTOR:%d:?]\n", out_resp->connector_id); sx_xlock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, out_resp->connector_id, DRM_MODE_OBJECT_CONNECTOR); if (!obj) { ret = -EINVAL; goto out; } connector = obj_to_connector(obj); props_count = connector->properties.count; for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { if (connector->encoder_ids[i] != 0) { encoders_count++; } } if (out_resp->count_modes == 0) { connector->funcs->fill_modes(connector, dev->mode_config.max_width, dev->mode_config.max_height); } /* delayed so we get modes regardless of pre-fill_modes state */ list_for_each_entry(mode, &connector->modes, head) mode_count++; out_resp->connector_id = connector->base.id; out_resp->connector_type = connector->connector_type; out_resp->connector_type_id = connector->connector_type_id; out_resp->mm_width = connector->display_info.width_mm; out_resp->mm_height = connector->display_info.height_mm; out_resp->subpixel = connector->display_info.subpixel_order; out_resp->connection = connector->status; if (connector->encoder) out_resp->encoder_id = connector->encoder->base.id; else out_resp->encoder_id = 0; /* * This ioctl is called twice, once to determine how much space is * needed, and the 2nd time to fill it. */ if ((out_resp->count_modes >= mode_count) && mode_count) { copied = 0; mode_ptr = (struct drm_mode_modeinfo __user *)(unsigned long)out_resp->modes_ptr; list_for_each_entry(mode, &connector->modes, head) { drm_crtc_convert_to_umode(&u_mode, mode); if (copy_to_user(mode_ptr + copied, &u_mode, sizeof(u_mode))) { ret = -EFAULT; goto out; } copied++; } } out_resp->count_modes = mode_count; if ((out_resp->count_props >= props_count) && props_count) { copied = 0; prop_ptr = (uint32_t __user *)(unsigned long)(out_resp->props_ptr); prop_values = (uint64_t __user *)(unsigned long)(out_resp->prop_values_ptr); for (i = 0; i < connector->properties.count; i++) { if (put_user(connector->properties.ids[i], prop_ptr + copied)) { ret = -EFAULT; goto out; } if (put_user(connector->properties.values[i], prop_values + copied)) { ret = -EFAULT; goto out; } copied++; } } out_resp->count_props = props_count; if ((out_resp->count_encoders >= encoders_count) && encoders_count) { copied = 0; encoder_ptr = (uint32_t __user *)(unsigned long)(out_resp->encoders_ptr); for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { if (connector->encoder_ids[i] != 0) { if (put_user(connector->encoder_ids[i], encoder_ptr + copied)) { ret = -EFAULT; goto out; } copied++; } } } out_resp->count_encoders = encoders_count; out: sx_xunlock(&dev->mode_config.mutex); return ret; } int drm_mode_getencoder(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_get_encoder *enc_resp = data; struct drm_mode_object *obj; struct drm_encoder *encoder; int ret = 0; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; sx_xlock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, enc_resp->encoder_id, DRM_MODE_OBJECT_ENCODER); if (!obj) { ret = -EINVAL; goto out; } encoder = obj_to_encoder(obj); if (encoder->crtc) enc_resp->crtc_id = encoder->crtc->base.id; else enc_resp->crtc_id = 0; enc_resp->encoder_type = encoder->encoder_type; enc_resp->encoder_id = encoder->base.id; enc_resp->possible_crtcs = encoder->possible_crtcs; enc_resp->possible_clones = encoder->possible_clones; out: sx_xunlock(&dev->mode_config.mutex); return ret; } /** * drm_mode_getplane_res - get plane info * @dev: DRM device * @data: ioctl data * @file_priv: DRM file info * * LOCKING: * Takes mode config lock. * * Return an plane count and set of IDs. */ int drm_mode_getplane_res(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_get_plane_res *plane_resp = data; struct drm_mode_config *config; struct drm_plane *plane; uint32_t __user *plane_ptr; int copied = 0, ret = 0; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; sx_xlock(&dev->mode_config.mutex); config = &dev->mode_config; /* * This ioctl is called twice, once to determine how much space is * needed, and the 2nd time to fill it. */ if (config->num_plane && (plane_resp->count_planes >= config->num_plane)) { plane_ptr = (uint32_t __user *)(unsigned long)plane_resp->plane_id_ptr; list_for_each_entry(plane, &config->plane_list, head) { if (put_user(plane->base.id, plane_ptr + copied)) { ret = -EFAULT; goto out; } copied++; } } plane_resp->count_planes = config->num_plane; out: sx_xunlock(&dev->mode_config.mutex); return ret; } /** * drm_mode_getplane - get plane info * @dev: DRM device * @data: ioctl data * @file_priv: DRM file info * * LOCKING: * Takes mode config lock. * * Return plane info, including formats supported, gamma size, any * current fb, etc. */ int drm_mode_getplane(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_get_plane *plane_resp = data; struct drm_mode_object *obj; struct drm_plane *plane; uint32_t __user *format_ptr; int ret = 0; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; sx_xlock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, plane_resp->plane_id, DRM_MODE_OBJECT_PLANE); if (!obj) { ret = -ENOENT; goto out; } plane = obj_to_plane(obj); if (plane->crtc) plane_resp->crtc_id = plane->crtc->base.id; else plane_resp->crtc_id = 0; if (plane->fb) plane_resp->fb_id = plane->fb->base.id; else plane_resp->fb_id = 0; plane_resp->plane_id = plane->base.id; plane_resp->possible_crtcs = plane->possible_crtcs; plane_resp->gamma_size = plane->gamma_size; /* * This ioctl is called twice, once to determine how much space is * needed, and the 2nd time to fill it. */ if (plane->format_count && (plane_resp->count_format_types >= plane->format_count)) { format_ptr = (uint32_t __user *)(unsigned long)plane_resp->format_type_ptr; if (copy_to_user(format_ptr, plane->format_types, sizeof(uint32_t) * plane->format_count)) { ret = -EFAULT; goto out; } } plane_resp->count_format_types = plane->format_count; out: sx_xunlock(&dev->mode_config.mutex); return ret; } /** * drm_mode_setplane - set up or tear down an plane * @dev: DRM device * @data: ioctl data* * @file_prive: DRM file info * * LOCKING: * Takes mode config lock. * * Set plane info, including placement, fb, scaling, and other factors. * Or pass a NULL fb to disable. */ int drm_mode_setplane(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_set_plane *plane_req = data; struct drm_mode_object *obj; struct drm_plane *plane; struct drm_crtc *crtc; struct drm_framebuffer *fb; int ret = 0; unsigned int fb_width, fb_height; int i; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; sx_xlock(&dev->mode_config.mutex); /* * First, find the plane, crtc, and fb objects. If not available, * we don't bother to call the driver. */ obj = drm_mode_object_find(dev, plane_req->plane_id, DRM_MODE_OBJECT_PLANE); if (!obj) { DRM_DEBUG_KMS("Unknown plane ID %d\n", plane_req->plane_id); ret = -ENOENT; goto out; } plane = obj_to_plane(obj); /* No fb means shut it down */ if (!plane_req->fb_id) { plane->funcs->disable_plane(plane); plane->crtc = NULL; plane->fb = NULL; goto out; } obj = drm_mode_object_find(dev, plane_req->crtc_id, DRM_MODE_OBJECT_CRTC); if (!obj) { DRM_DEBUG_KMS("Unknown crtc ID %d\n", plane_req->crtc_id); ret = -ENOENT; goto out; } crtc = obj_to_crtc(obj); obj = drm_mode_object_find(dev, plane_req->fb_id, DRM_MODE_OBJECT_FB); if (!obj) { DRM_DEBUG_KMS("Unknown framebuffer ID %d\n", plane_req->fb_id); ret = -ENOENT; goto out; } fb = obj_to_fb(obj); /* Check whether this plane supports the fb pixel format. */ for (i = 0; i < plane->format_count; i++) if (fb->pixel_format == plane->format_types[i]) break; if (i == plane->format_count) { DRM_DEBUG_KMS("Invalid pixel format 0x%08x\n", fb->pixel_format); ret = -EINVAL; goto out; } fb_width = fb->width << 16; fb_height = fb->height << 16; /* Make sure source coordinates are inside the fb. */ if (plane_req->src_w > fb_width || plane_req->src_x > fb_width - plane_req->src_w || plane_req->src_h > fb_height || plane_req->src_y > fb_height - plane_req->src_h) { DRM_DEBUG_KMS("Invalid source coordinates " "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n", plane_req->src_w >> 16, ((plane_req->src_w & 0xffff) * 15625) >> 10, plane_req->src_h >> 16, ((plane_req->src_h & 0xffff) * 15625) >> 10, plane_req->src_x >> 16, ((plane_req->src_x & 0xffff) * 15625) >> 10, plane_req->src_y >> 16, ((plane_req->src_y & 0xffff) * 15625) >> 10); ret = -ENOSPC; goto out; } /* Give drivers some help against integer overflows */ if (plane_req->crtc_w > INT_MAX || plane_req->crtc_x > INT_MAX - (int32_t) plane_req->crtc_w || plane_req->crtc_h > INT_MAX || plane_req->crtc_y > INT_MAX - (int32_t) plane_req->crtc_h) { DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n", plane_req->crtc_w, plane_req->crtc_h, plane_req->crtc_x, plane_req->crtc_y); ret = -ERANGE; goto out; } ret = plane->funcs->update_plane(plane, crtc, fb, plane_req->crtc_x, plane_req->crtc_y, plane_req->crtc_w, plane_req->crtc_h, plane_req->src_x, plane_req->src_y, plane_req->src_w, plane_req->src_h); if (!ret) { plane->crtc = crtc; plane->fb = fb; } out: sx_xunlock(&dev->mode_config.mutex); return ret; } /** * drm_mode_setcrtc - set CRTC configuration * @inode: inode from the ioctl * @filp: file * from the ioctl * @cmd: cmd from ioctl * @arg: arg from ioctl * * LOCKING: * Takes mode config lock. * * Build a new CRTC configuration based on user request. * * Called by the user via ioctl. * * RETURNS: * Zero on success, errno on failure. */ int drm_mode_setcrtc(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_config *config = &dev->mode_config; struct drm_mode_crtc *crtc_req = data; struct drm_mode_object *obj; struct drm_crtc *crtc; struct drm_connector **connector_set = NULL, *connector; struct drm_framebuffer *fb = NULL; struct drm_display_mode *mode = NULL; struct drm_mode_set set; uint32_t __user *set_connectors_ptr; int ret; int i; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; /* For some reason crtc x/y offsets are signed internally. */ if (crtc_req->x > INT_MAX || crtc_req->y > INT_MAX) return -ERANGE; sx_xlock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, crtc_req->crtc_id, DRM_MODE_OBJECT_CRTC); if (!obj) { DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id); ret = -EINVAL; goto out; } crtc = obj_to_crtc(obj); DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id); if (crtc_req->mode_valid) { int hdisplay, vdisplay; /* If we have a mode we need a framebuffer. */ /* If we pass -1, set the mode with the currently bound fb */ if (crtc_req->fb_id == -1) { if (!crtc->fb) { DRM_DEBUG_KMS("CRTC doesn't have current FB\n"); ret = -EINVAL; goto out; } fb = crtc->fb; } else { obj = drm_mode_object_find(dev, crtc_req->fb_id, DRM_MODE_OBJECT_FB); if (!obj) { DRM_DEBUG_KMS("Unknown FB ID%d\n", crtc_req->fb_id); ret = -EINVAL; goto out; } fb = obj_to_fb(obj); } mode = drm_mode_create(dev); if (!mode) { ret = -ENOMEM; goto out; } ret = drm_crtc_convert_umode(mode, &crtc_req->mode); if (ret) { DRM_DEBUG_KMS("Invalid mode\n"); goto out; } drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); hdisplay = mode->hdisplay; vdisplay = mode->vdisplay; if (crtc->invert_dimensions) { int tmp; tmp = vdisplay; vdisplay = hdisplay; hdisplay = tmp; } if (hdisplay > fb->width || vdisplay > fb->height || crtc_req->x > fb->width - hdisplay || crtc_req->y > fb->height - vdisplay) { DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d%s.\n", fb->width, fb->height, hdisplay, vdisplay, crtc_req->x, crtc_req->y, crtc->invert_dimensions ? " (inverted)" : ""); ret = -ENOSPC; goto out; } } if (crtc_req->count_connectors == 0 && mode) { DRM_DEBUG_KMS("Count connectors is 0 but mode set\n"); ret = -EINVAL; goto out; } if (crtc_req->count_connectors > 0 && (!mode || !fb)) { DRM_DEBUG_KMS("Count connectors is %d but no mode or fb set\n", crtc_req->count_connectors); ret = -EINVAL; goto out; } if (crtc_req->count_connectors > 0) { u32 out_id; /* Avoid unbounded kernel memory allocation */ if (crtc_req->count_connectors > config->num_connector) { ret = -EINVAL; goto out; } connector_set = malloc(crtc_req->count_connectors * sizeof(struct drm_connector *), - DRM_MEM_KMS, M_WAITOK); + DRM_MEM_KMS, M_NOWAIT); if (!connector_set) { ret = -ENOMEM; goto out; } for (i = 0; i < crtc_req->count_connectors; i++) { set_connectors_ptr = (uint32_t __user *)(unsigned long)crtc_req->set_connectors_ptr; if (get_user(out_id, &set_connectors_ptr[i])) { ret = -EFAULT; goto out; } obj = drm_mode_object_find(dev, out_id, DRM_MODE_OBJECT_CONNECTOR); if (!obj) { DRM_DEBUG_KMS("Connector id %d unknown\n", out_id); ret = -EINVAL; goto out; } connector = obj_to_connector(obj); DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id, drm_get_connector_name(connector)); connector_set[i] = connector; } } set.crtc = crtc; set.x = crtc_req->x; set.y = crtc_req->y; set.mode = mode; set.connectors = connector_set; set.num_connectors = crtc_req->count_connectors; set.fb = fb; ret = crtc->funcs->set_config(&set); out: free(connector_set, DRM_MEM_KMS); drm_mode_destroy(dev, mode); sx_xunlock(&dev->mode_config.mutex); return ret; } int drm_mode_cursor_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_cursor *req = data; struct drm_mode_object *obj; struct drm_crtc *crtc; int ret = 0; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags)) return -EINVAL; sx_xlock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, req->crtc_id, DRM_MODE_OBJECT_CRTC); if (!obj) { DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id); ret = -EINVAL; goto out; } crtc = obj_to_crtc(obj); if (req->flags & DRM_MODE_CURSOR_BO) { if (!crtc->funcs->cursor_set) { ret = -ENXIO; goto out; } /* Turns off the cursor if handle is 0 */ ret = crtc->funcs->cursor_set(crtc, file_priv, req->handle, req->width, req->height); } if (req->flags & DRM_MODE_CURSOR_MOVE) { if (crtc->funcs->cursor_move) { ret = crtc->funcs->cursor_move(crtc, req->x, req->y); } else { ret = -EFAULT; goto out; } } out: sx_xunlock(&dev->mode_config.mutex); return ret; } /* Original addfb only supported RGB formats, so figure out which one */ uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth) { uint32_t fmt; switch (bpp) { case 8: fmt = DRM_FORMAT_C8; break; case 16: if (depth == 15) fmt = DRM_FORMAT_XRGB1555; else fmt = DRM_FORMAT_RGB565; break; case 24: fmt = DRM_FORMAT_RGB888; break; case 32: if (depth == 24) fmt = DRM_FORMAT_XRGB8888; else if (depth == 30) fmt = DRM_FORMAT_XRGB2101010; else fmt = DRM_FORMAT_ARGB8888; break; default: DRM_ERROR("bad bpp, assuming x8r8g8b8 pixel format\n"); fmt = DRM_FORMAT_XRGB8888; break; } return fmt; } EXPORT_SYMBOL(drm_mode_legacy_fb_format); /** * drm_mode_addfb - add an FB to the graphics configuration * @inode: inode from the ioctl * @filp: file * from the ioctl * @cmd: cmd from ioctl * @arg: arg from ioctl * * LOCKING: * Takes mode config lock. * * Add a new FB to the specified CRTC, given a user request. * * Called by the user via ioctl. * * RETURNS: * Zero on success, errno on failure. */ int drm_mode_addfb(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_fb_cmd *or = data; struct drm_mode_fb_cmd2 r = {}; struct drm_mode_config *config = &dev->mode_config; struct drm_framebuffer *fb; int ret = 0; /* Use new struct with format internally */ r.fb_id = or->fb_id; r.width = or->width; r.height = or->height; r.pitches[0] = or->pitch; r.pixel_format = drm_mode_legacy_fb_format(or->bpp, or->depth); r.handles[0] = or->handle; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; if ((config->min_width > r.width) || (r.width > config->max_width)) return -EINVAL; if ((config->min_height > r.height) || (r.height > config->max_height)) return -EINVAL; sx_xlock(&dev->mode_config.mutex); /* TODO check buffer is sufficiently large */ /* TODO setup destructor callback */ ret = dev->mode_config.funcs->fb_create(dev, file_priv, &r, &fb); if (ret != 0) { DRM_DEBUG_KMS("could not create framebuffer\n"); goto out; } or->fb_id = fb->base.id; list_add(&fb->filp_head, &file_priv->fbs); DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); out: sx_xunlock(&dev->mode_config.mutex); return ret; } static int format_check(const struct drm_mode_fb_cmd2 *r) { uint32_t format = r->pixel_format & ~DRM_FORMAT_BIG_ENDIAN; switch (format) { case DRM_FORMAT_C8: case DRM_FORMAT_RGB332: case DRM_FORMAT_BGR233: case DRM_FORMAT_XRGB4444: case DRM_FORMAT_XBGR4444: case DRM_FORMAT_RGBX4444: case DRM_FORMAT_BGRX4444: case DRM_FORMAT_ARGB4444: case DRM_FORMAT_ABGR4444: case DRM_FORMAT_RGBA4444: case DRM_FORMAT_BGRA4444: case DRM_FORMAT_XRGB1555: case DRM_FORMAT_XBGR1555: case DRM_FORMAT_RGBX5551: case DRM_FORMAT_BGRX5551: case DRM_FORMAT_ARGB1555: case DRM_FORMAT_ABGR1555: case DRM_FORMAT_RGBA5551: case DRM_FORMAT_BGRA5551: case DRM_FORMAT_RGB565: case DRM_FORMAT_BGR565: case DRM_FORMAT_RGB888: case DRM_FORMAT_BGR888: case DRM_FORMAT_XRGB8888: case DRM_FORMAT_XBGR8888: case DRM_FORMAT_RGBX8888: case DRM_FORMAT_BGRX8888: case DRM_FORMAT_ARGB8888: case DRM_FORMAT_ABGR8888: case DRM_FORMAT_RGBA8888: case DRM_FORMAT_BGRA8888: case DRM_FORMAT_XRGB2101010: case DRM_FORMAT_XBGR2101010: case DRM_FORMAT_RGBX1010102: case DRM_FORMAT_BGRX1010102: case DRM_FORMAT_ARGB2101010: case DRM_FORMAT_ABGR2101010: case DRM_FORMAT_RGBA1010102: case DRM_FORMAT_BGRA1010102: case DRM_FORMAT_YUYV: case DRM_FORMAT_YVYU: case DRM_FORMAT_UYVY: case DRM_FORMAT_VYUY: case DRM_FORMAT_AYUV: case DRM_FORMAT_NV12: case DRM_FORMAT_NV21: case DRM_FORMAT_NV16: case DRM_FORMAT_NV61: case DRM_FORMAT_NV24: case DRM_FORMAT_NV42: case DRM_FORMAT_YUV410: case DRM_FORMAT_YVU410: case DRM_FORMAT_YUV411: case DRM_FORMAT_YVU411: case DRM_FORMAT_YUV420: case DRM_FORMAT_YVU420: case DRM_FORMAT_YUV422: case DRM_FORMAT_YVU422: case DRM_FORMAT_YUV444: case DRM_FORMAT_YVU444: return 0; default: return -EINVAL; } } static int framebuffer_check(const struct drm_mode_fb_cmd2 *r) { int ret, hsub, vsub, num_planes, i; ret = format_check(r); if (ret) { DRM_DEBUG_KMS("bad framebuffer format 0x%08x\n", r->pixel_format); return ret; } hsub = drm_format_horz_chroma_subsampling(r->pixel_format); vsub = drm_format_vert_chroma_subsampling(r->pixel_format); num_planes = drm_format_num_planes(r->pixel_format); if (r->width == 0 || r->width % hsub) { DRM_DEBUG_KMS("bad framebuffer width %u\n", r->height); return -EINVAL; } if (r->height == 0 || r->height % vsub) { DRM_DEBUG_KMS("bad framebuffer height %u\n", r->height); return -EINVAL; } for (i = 0; i < num_planes; i++) { unsigned int width = r->width / (i != 0 ? hsub : 1); unsigned int height = r->height / (i != 0 ? vsub : 1); unsigned int cpp = drm_format_plane_cpp(r->pixel_format, i); if (!r->handles[i]) { DRM_DEBUG_KMS("no buffer object handle for plane %d\n", i); return -EINVAL; } if ((uint64_t) width * cpp > UINT_MAX) return -ERANGE; if ((uint64_t) height * r->pitches[i] + r->offsets[i] > UINT_MAX) return -ERANGE; if (r->pitches[i] < width * cpp) { DRM_DEBUG_KMS("bad pitch %u for plane %d\n", r->pitches[i], i); return -EINVAL; } } return 0; } /** * drm_mode_addfb2 - add an FB to the graphics configuration * @inode: inode from the ioctl * @filp: file * from the ioctl * @cmd: cmd from ioctl * @arg: arg from ioctl * * LOCKING: * Takes mode config lock. * * Add a new FB to the specified CRTC, given a user request with format. * * Called by the user via ioctl. * * RETURNS: * Zero on success, errno on failure. */ int drm_mode_addfb2(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_fb_cmd2 *r = data; struct drm_mode_config *config = &dev->mode_config; struct drm_framebuffer *fb; int ret; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; if (r->flags & ~DRM_MODE_FB_INTERLACED) { DRM_DEBUG_KMS("bad framebuffer flags 0x%08x\n", r->flags); return -EINVAL; } if ((config->min_width > r->width) || (r->width > config->max_width)) { DRM_DEBUG_KMS("bad framebuffer width %d, should be >= %d && <= %d\n", r->width, config->min_width, config->max_width); return -EINVAL; } if ((config->min_height > r->height) || (r->height > config->max_height)) { DRM_DEBUG_KMS("bad framebuffer height %d, should be >= %d && <= %d\n", r->height, config->min_height, config->max_height); return -EINVAL; } ret = framebuffer_check(r); if (ret) return ret; sx_xlock(&dev->mode_config.mutex); ret = dev->mode_config.funcs->fb_create(dev, file_priv, r, &fb); if (ret != 0) { DRM_DEBUG_KMS("could not create framebuffer\n"); goto out; } r->fb_id = fb->base.id; list_add(&fb->filp_head, &file_priv->fbs); DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); out: sx_xunlock(&dev->mode_config.mutex); return ret; } /** * drm_mode_rmfb - remove an FB from the configuration * @inode: inode from the ioctl * @filp: file * from the ioctl * @cmd: cmd from ioctl * @arg: arg from ioctl * * LOCKING: * Takes mode config lock. * * Remove the FB specified by the user. * * Called by the user via ioctl. * * RETURNS: * Zero on success, errno on failure. */ int drm_mode_rmfb(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_object *obj; struct drm_framebuffer *fb = NULL; struct drm_framebuffer *fbl = NULL; uint32_t *id = data; int ret = 0; int found = 0; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; sx_xlock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, *id, DRM_MODE_OBJECT_FB); /* TODO check that we really get a framebuffer back. */ if (!obj) { ret = -EINVAL; goto out; } fb = obj_to_fb(obj); list_for_each_entry(fbl, &file_priv->fbs, filp_head) if (fb == fbl) found = 1; if (!found) { ret = -EINVAL; goto out; } drm_framebuffer_remove(fb); out: sx_xunlock(&dev->mode_config.mutex); return ret; } /** * drm_mode_getfb - get FB info * @inode: inode from the ioctl * @filp: file * from the ioctl * @cmd: cmd from ioctl * @arg: arg from ioctl * * LOCKING: * Takes mode config lock. * * Lookup the FB given its ID and return info about it. * * Called by the user via ioctl. * * RETURNS: * Zero on success, errno on failure. */ int drm_mode_getfb(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_fb_cmd *r = data; struct drm_mode_object *obj; struct drm_framebuffer *fb; int ret = 0; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; sx_xlock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB); if (!obj) { ret = -EINVAL; goto out; } fb = obj_to_fb(obj); r->height = fb->height; r->width = fb->width; r->depth = fb->depth; r->bpp = fb->bits_per_pixel; r->pitch = fb->pitches[0]; r->handle = 0; fb->funcs->create_handle(fb, file_priv, &r->handle); out: sx_xunlock(&dev->mode_config.mutex); return ret; } int drm_mode_dirtyfb_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_clip_rect __user *clips_ptr; struct drm_clip_rect *clips = NULL; struct drm_mode_fb_dirty_cmd *r = data; struct drm_mode_object *obj; struct drm_framebuffer *fb; unsigned flags; int num_clips; int ret; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; sx_xlock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB); if (!obj) { ret = -EINVAL; goto out_err1; } fb = obj_to_fb(obj); num_clips = r->num_clips; clips_ptr = (struct drm_clip_rect __user *)(unsigned long)r->clips_ptr; if (!num_clips != !clips_ptr) { ret = -EINVAL; goto out_err1; } flags = DRM_MODE_FB_DIRTY_FLAGS & r->flags; /* If userspace annotates copy, clips must come in pairs */ if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY && (num_clips % 2)) { ret = -EINVAL; goto out_err1; } if (num_clips && clips_ptr) { if (num_clips < 0 || num_clips > DRM_MODE_FB_DIRTY_MAX_CLIPS) { ret = -EINVAL; goto out_err1; } clips = malloc(num_clips * sizeof(*clips), DRM_MEM_KMS, - M_WAITOK | M_ZERO); + M_NOWAIT | M_ZERO); if (!clips) { ret = -ENOMEM; goto out_err1; } ret = copy_from_user(clips, clips_ptr, num_clips * sizeof(*clips)); if (ret) { ret = -EFAULT; goto out_err2; } } if (fb->funcs->dirty) { ret = fb->funcs->dirty(fb, file_priv, flags, r->color, clips, num_clips); } else { ret = -ENOSYS; goto out_err2; } out_err2: free(clips, DRM_MEM_KMS); out_err1: sx_xunlock(&dev->mode_config.mutex); return ret; } /** * drm_fb_release - remove and free the FBs on this file * @filp: file * from the ioctl * * LOCKING: * Takes mode config lock. * * Destroy all the FBs associated with @filp. * * Called by the user via ioctl. * * RETURNS: * Zero on success, errno on failure. */ void drm_fb_release(struct drm_file *priv) { struct drm_device *dev = priv->minor->dev; struct drm_framebuffer *fb, *tfb; sx_xlock(&dev->mode_config.mutex); list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) { drm_framebuffer_remove(fb); } sx_xunlock(&dev->mode_config.mutex); } /** * drm_mode_attachmode - add a mode to the user mode list * @dev: DRM device * @connector: connector to add the mode to * @mode: mode to add * * Add @mode to @connector's user mode list. */ static void drm_mode_attachmode(struct drm_device *dev, struct drm_connector *connector, struct drm_display_mode *mode) { list_add_tail(&mode->head, &connector->user_modes); } int drm_mode_attachmode_crtc(struct drm_device *dev, struct drm_crtc *crtc, const struct drm_display_mode *mode) { struct drm_connector *connector; int ret = 0; struct drm_display_mode *dup_mode, *next; DRM_LIST_HEAD(list); list_for_each_entry(connector, &dev->mode_config.connector_list, head) { if (!connector->encoder) continue; if (connector->encoder->crtc == crtc) { dup_mode = drm_mode_duplicate(dev, mode); if (!dup_mode) { ret = -ENOMEM; goto out; } list_add_tail(&dup_mode->head, &list); } } list_for_each_entry(connector, &dev->mode_config.connector_list, head) { if (!connector->encoder) continue; if (connector->encoder->crtc == crtc) list_move_tail(list.next, &connector->user_modes); } MPASS(!list_empty(&list)); out: list_for_each_entry_safe(dup_mode, next, &list, head) drm_mode_destroy(dev, dup_mode); return ret; } EXPORT_SYMBOL(drm_mode_attachmode_crtc); static int drm_mode_detachmode(struct drm_device *dev, struct drm_connector *connector, struct drm_display_mode *mode) { int found = 0; int ret = 0; struct drm_display_mode *match_mode, *t; list_for_each_entry_safe(match_mode, t, &connector->user_modes, head) { if (drm_mode_equal(match_mode, mode)) { list_del(&match_mode->head); drm_mode_destroy(dev, match_mode); found = 1; break; } } if (!found) ret = -EINVAL; return ret; } int drm_mode_detachmode_crtc(struct drm_device *dev, struct drm_display_mode *mode) { struct drm_connector *connector; list_for_each_entry(connector, &dev->mode_config.connector_list, head) { drm_mode_detachmode(dev, connector, mode); } return 0; } EXPORT_SYMBOL(drm_mode_detachmode_crtc); /** * drm_fb_attachmode - Attach a user mode to an connector * @inode: inode from the ioctl * @filp: file * from the ioctl * @cmd: cmd from ioctl * @arg: arg from ioctl * * This attaches a user specified mode to an connector. * Called by the user via ioctl. * * RETURNS: * Zero on success, errno on failure. */ int drm_mode_attachmode_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_mode_cmd *mode_cmd = data; struct drm_connector *connector; struct drm_display_mode *mode; struct drm_mode_object *obj; struct drm_mode_modeinfo *umode = &mode_cmd->mode; int ret; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; sx_xlock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, mode_cmd->connector_id, DRM_MODE_OBJECT_CONNECTOR); if (!obj) { ret = -EINVAL; goto out; } connector = obj_to_connector(obj); mode = drm_mode_create(dev); if (!mode) { ret = -ENOMEM; goto out; } ret = drm_crtc_convert_umode(mode, umode); if (ret) { DRM_DEBUG_KMS("Invalid mode\n"); drm_mode_destroy(dev, mode); goto out; } drm_mode_attachmode(dev, connector, mode); out: sx_xunlock(&dev->mode_config.mutex); return ret; } /** * drm_fb_detachmode - Detach a user specified mode from an connector * @inode: inode from the ioctl * @filp: file * from the ioctl * @cmd: cmd from ioctl * @arg: arg from ioctl * * Called by the user via ioctl. * * RETURNS: * Zero on success, errno on failure. */ int drm_mode_detachmode_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_object *obj; struct drm_mode_mode_cmd *mode_cmd = data; struct drm_connector *connector; struct drm_display_mode mode; struct drm_mode_modeinfo *umode = &mode_cmd->mode; int ret; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; sx_xlock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, mode_cmd->connector_id, DRM_MODE_OBJECT_CONNECTOR); if (!obj) { ret = -EINVAL; goto out; } connector = obj_to_connector(obj); ret = drm_crtc_convert_umode(&mode, umode); if (ret) { DRM_DEBUG_KMS("Invalid mode\n"); goto out; } ret = drm_mode_detachmode(dev, connector, &mode); out: sx_xunlock(&dev->mode_config.mutex); return ret; } struct drm_property *drm_property_create(struct drm_device *dev, int flags, const char *name, int num_values) { struct drm_property *property = NULL; int ret; property = malloc(sizeof(struct drm_property), DRM_MEM_KMS, - M_WAITOK | M_ZERO); + M_NOWAIT | M_ZERO); if (!property) return NULL; if (num_values) { property->values = malloc(sizeof(uint64_t)*num_values, DRM_MEM_KMS, - M_WAITOK | M_ZERO); + M_NOWAIT | M_ZERO); if (!property->values) goto fail; } ret = drm_mode_object_get(dev, &property->base, DRM_MODE_OBJECT_PROPERTY); if (ret) goto fail; property->flags = flags; property->num_values = num_values; INIT_LIST_HEAD(&property->enum_blob_list); if (name) { strncpy(property->name, name, DRM_PROP_NAME_LEN); property->name[DRM_PROP_NAME_LEN-1] = '\0'; } list_add_tail(&property->head, &dev->mode_config.property_list); return property; fail: free(property->values, DRM_MEM_KMS); free(property, DRM_MEM_KMS); return NULL; } EXPORT_SYMBOL(drm_property_create); struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags, const char *name, const struct drm_prop_enum_list *props, int num_values) { struct drm_property *property; int i, ret; flags |= DRM_MODE_PROP_ENUM; property = drm_property_create(dev, flags, name, num_values); if (!property) return NULL; for (i = 0; i < num_values; i++) { ret = drm_property_add_enum(property, i, props[i].type, props[i].name); if (ret) { drm_property_destroy(dev, property); return NULL; } } return property; } EXPORT_SYMBOL(drm_property_create_enum); struct drm_property *drm_property_create_bitmask(struct drm_device *dev, int flags, const char *name, const struct drm_prop_enum_list *props, int num_values) { struct drm_property *property; int i, ret; flags |= DRM_MODE_PROP_BITMASK; property = drm_property_create(dev, flags, name, num_values); if (!property) return NULL; for (i = 0; i < num_values; i++) { ret = drm_property_add_enum(property, i, props[i].type, props[i].name); if (ret) { drm_property_destroy(dev, property); return NULL; } } return property; } EXPORT_SYMBOL(drm_property_create_bitmask); struct drm_property *drm_property_create_range(struct drm_device *dev, int flags, const char *name, uint64_t min, uint64_t max) { struct drm_property *property; flags |= DRM_MODE_PROP_RANGE; property = drm_property_create(dev, flags, name, 2); if (!property) return NULL; property->values[0] = min; property->values[1] = max; return property; } EXPORT_SYMBOL(drm_property_create_range); int drm_property_add_enum(struct drm_property *property, int index, uint64_t value, const char *name) { struct drm_property_enum *prop_enum; if (!(property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK))) return -EINVAL; /* * Bitmask enum properties have the additional constraint of values * from 0 to 63 */ if ((property->flags & DRM_MODE_PROP_BITMASK) && (value > 63)) return -EINVAL; if (!list_empty(&property->enum_blob_list)) { list_for_each_entry(prop_enum, &property->enum_blob_list, head) { if (prop_enum->value == value) { strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN); prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0'; return 0; } } } prop_enum = malloc(sizeof(struct drm_property_enum), DRM_MEM_KMS, - M_WAITOK | M_ZERO); + M_NOWAIT | M_ZERO); if (!prop_enum) return -ENOMEM; strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN); prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0'; prop_enum->value = value; property->values[index] = value; list_add_tail(&prop_enum->head, &property->enum_blob_list); return 0; } EXPORT_SYMBOL(drm_property_add_enum); void drm_property_destroy(struct drm_device *dev, struct drm_property *property) { struct drm_property_enum *prop_enum, *pt; list_for_each_entry_safe(prop_enum, pt, &property->enum_blob_list, head) { list_del(&prop_enum->head); free(prop_enum, DRM_MEM_KMS); } if (property->num_values) free(property->values, DRM_MEM_KMS); drm_mode_object_put(dev, &property->base); list_del(&property->head); free(property, DRM_MEM_KMS); } EXPORT_SYMBOL(drm_property_destroy); void drm_object_attach_property(struct drm_mode_object *obj, struct drm_property *property, uint64_t init_val) { int count = obj->properties->count; if (count == DRM_OBJECT_MAX_PROPERTY) { DRM_WARNING("Failed to attach object property (type: 0x%x). Please " "increase DRM_OBJECT_MAX_PROPERTY by 1 for each time " "you see this message on the same object type.\n", obj->type); return; } obj->properties->ids[count] = property->base.id; obj->properties->values[count] = init_val; obj->properties->count++; } EXPORT_SYMBOL(drm_object_attach_property); int drm_object_property_set_value(struct drm_mode_object *obj, struct drm_property *property, uint64_t val) { int i; for (i = 0; i < obj->properties->count; i++) { if (obj->properties->ids[i] == property->base.id) { obj->properties->values[i] = val; return 0; } } return -EINVAL; } EXPORT_SYMBOL(drm_object_property_set_value); int drm_object_property_get_value(struct drm_mode_object *obj, struct drm_property *property, uint64_t *val) { int i; for (i = 0; i < obj->properties->count; i++) { if (obj->properties->ids[i] == property->base.id) { *val = obj->properties->values[i]; return 0; } } return -EINVAL; } EXPORT_SYMBOL(drm_object_property_get_value); int drm_mode_getproperty_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_object *obj; struct drm_mode_get_property *out_resp = data; struct drm_property *property; int enum_count = 0; int blob_count = 0; int value_count = 0; int ret = 0, i; int copied; struct drm_property_enum *prop_enum; struct drm_mode_property_enum __user *enum_ptr; struct drm_property_blob *prop_blob; uint32_t __user *blob_id_ptr; uint64_t __user *values_ptr; uint32_t __user *blob_length_ptr; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; sx_xlock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY); if (!obj) { ret = -EINVAL; goto done; } property = obj_to_property(obj); if (property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) { list_for_each_entry(prop_enum, &property->enum_blob_list, head) enum_count++; } else if (property->flags & DRM_MODE_PROP_BLOB) { list_for_each_entry(prop_blob, &property->enum_blob_list, head) blob_count++; } value_count = property->num_values; strncpy(out_resp->name, property->name, DRM_PROP_NAME_LEN); out_resp->name[DRM_PROP_NAME_LEN-1] = 0; out_resp->flags = property->flags; if ((out_resp->count_values >= value_count) && value_count) { values_ptr = (uint64_t __user *)(unsigned long)out_resp->values_ptr; for (i = 0; i < value_count; i++) { if (copy_to_user(values_ptr + i, &property->values[i], sizeof(uint64_t))) { ret = -EFAULT; goto done; } } } out_resp->count_values = value_count; if (property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) { if ((out_resp->count_enum_blobs >= enum_count) && enum_count) { copied = 0; enum_ptr = (struct drm_mode_property_enum __user *)(unsigned long)out_resp->enum_blob_ptr; list_for_each_entry(prop_enum, &property->enum_blob_list, head) { if (copy_to_user(&enum_ptr[copied].value, &prop_enum->value, sizeof(uint64_t))) { ret = -EFAULT; goto done; } if (copy_to_user(&enum_ptr[copied].name, &prop_enum->name, DRM_PROP_NAME_LEN)) { ret = -EFAULT; goto done; } copied++; } } out_resp->count_enum_blobs = enum_count; } if (property->flags & DRM_MODE_PROP_BLOB) { if ((out_resp->count_enum_blobs >= blob_count) && blob_count) { copied = 0; blob_id_ptr = (uint32_t __user *)(unsigned long)out_resp->enum_blob_ptr; blob_length_ptr = (uint32_t __user *)(unsigned long)out_resp->values_ptr; list_for_each_entry(prop_blob, &property->enum_blob_list, head) { if (put_user(prop_blob->base.id, blob_id_ptr + copied)) { ret = -EFAULT; goto done; } if (put_user(prop_blob->length, blob_length_ptr + copied)) { ret = -EFAULT; goto done; } copied++; } } out_resp->count_enum_blobs = blob_count; } done: sx_xunlock(&dev->mode_config.mutex); return ret; } static struct drm_property_blob *drm_property_create_blob(struct drm_device *dev, int length, void *data) { struct drm_property_blob *blob; int ret; if (!length || !data) return NULL; blob = malloc(sizeof(struct drm_property_blob)+length, DRM_MEM_KMS, - M_WAITOK | M_ZERO); + M_NOWAIT | M_ZERO); if (!blob) return NULL; ret = drm_mode_object_get(dev, &blob->base, DRM_MODE_OBJECT_BLOB); if (ret) { free(blob, DRM_MEM_KMS); return NULL; } blob->length = length; memcpy(blob->data, data, length); list_add_tail(&blob->head, &dev->mode_config.property_blob_list); return blob; } static void drm_property_destroy_blob(struct drm_device *dev, struct drm_property_blob *blob) { drm_mode_object_put(dev, &blob->base); list_del(&blob->head); free(blob, DRM_MEM_KMS); } int drm_mode_getblob_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_object *obj; struct drm_mode_get_blob *out_resp = data; struct drm_property_blob *blob; int ret = 0; void __user *blob_ptr; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; sx_xlock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, out_resp->blob_id, DRM_MODE_OBJECT_BLOB); if (!obj) { ret = -EINVAL; goto done; } blob = obj_to_blob(obj); if (out_resp->length == blob->length) { blob_ptr = (void __user *)(unsigned long)out_resp->data; if (copy_to_user(blob_ptr, blob->data, blob->length)){ ret = -EFAULT; goto done; } } out_resp->length = blob->length; done: sx_xunlock(&dev->mode_config.mutex); return ret; } int drm_mode_connector_update_edid_property(struct drm_connector *connector, struct edid *edid) { struct drm_device *dev = connector->dev; int ret, size; if (connector->edid_blob_ptr) drm_property_destroy_blob(dev, connector->edid_blob_ptr); /* Delete edid, when there is none. */ if (!edid) { connector->edid_blob_ptr = NULL; ret = drm_object_property_set_value(&connector->base, dev->mode_config.edid_property, 0); return ret; } size = EDID_LENGTH * (1 + edid->extensions); connector->edid_blob_ptr = drm_property_create_blob(connector->dev, size, edid); if (!connector->edid_blob_ptr) return -EINVAL; ret = drm_object_property_set_value(&connector->base, dev->mode_config.edid_property, connector->edid_blob_ptr->base.id); return ret; } EXPORT_SYMBOL(drm_mode_connector_update_edid_property); static bool drm_property_change_is_valid(struct drm_property *property, uint64_t value) { if (property->flags & DRM_MODE_PROP_IMMUTABLE) return false; if (property->flags & DRM_MODE_PROP_RANGE) { if (value < property->values[0] || value > property->values[1]) return false; return true; } else if (property->flags & DRM_MODE_PROP_BITMASK) { int i; uint64_t valid_mask = 0; for (i = 0; i < property->num_values; i++) valid_mask |= (1ULL << property->values[i]); return !(value & ~valid_mask); } else if (property->flags & DRM_MODE_PROP_BLOB) { /* Only the driver knows */ return true; } else { int i; for (i = 0; i < property->num_values; i++) if (property->values[i] == value) return true; return false; } } int drm_mode_connector_property_set_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_connector_set_property *conn_set_prop = data; struct drm_mode_obj_set_property obj_set_prop = { .value = conn_set_prop->value, .prop_id = conn_set_prop->prop_id, .obj_id = conn_set_prop->connector_id, .obj_type = DRM_MODE_OBJECT_CONNECTOR }; /* It does all the locking and checking we need */ return drm_mode_obj_set_property_ioctl(dev, &obj_set_prop, file_priv); } static int drm_mode_connector_set_obj_prop(struct drm_mode_object *obj, struct drm_property *property, uint64_t value) { int ret = -EINVAL; struct drm_connector *connector = obj_to_connector(obj); /* Do DPMS ourselves */ if (property == connector->dev->mode_config.dpms_property) { if (connector->funcs->dpms) (*connector->funcs->dpms)(connector, (int)value); ret = 0; } else if (connector->funcs->set_property) ret = connector->funcs->set_property(connector, property, value); /* store the property value if successful */ if (!ret) drm_object_property_set_value(&connector->base, property, value); return ret; } static int drm_mode_crtc_set_obj_prop(struct drm_mode_object *obj, struct drm_property *property, uint64_t value) { int ret = -EINVAL; struct drm_crtc *crtc = obj_to_crtc(obj); if (crtc->funcs->set_property) ret = crtc->funcs->set_property(crtc, property, value); if (!ret) drm_object_property_set_value(obj, property, value); return ret; } static int drm_mode_plane_set_obj_prop(struct drm_mode_object *obj, struct drm_property *property, uint64_t value) { int ret = -EINVAL; struct drm_plane *plane = obj_to_plane(obj); if (plane->funcs->set_property) ret = plane->funcs->set_property(plane, property, value); if (!ret) drm_object_property_set_value(obj, property, value); return ret; } int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_obj_get_properties *arg = data; struct drm_mode_object *obj; int ret = 0; int i; int copied = 0; int props_count = 0; uint32_t __user *props_ptr; uint64_t __user *prop_values_ptr; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; sx_xlock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type); if (!obj) { ret = -EINVAL; goto out; } if (!obj->properties) { ret = -EINVAL; goto out; } props_count = obj->properties->count; /* This ioctl is called twice, once to determine how much space is * needed, and the 2nd time to fill it. */ if ((arg->count_props >= props_count) && props_count) { copied = 0; props_ptr = (uint32_t __user *)(unsigned long)(arg->props_ptr); prop_values_ptr = (uint64_t __user *)(unsigned long) (arg->prop_values_ptr); for (i = 0; i < props_count; i++) { if (put_user(obj->properties->ids[i], props_ptr + copied)) { ret = -EFAULT; goto out; } if (put_user(obj->properties->values[i], prop_values_ptr + copied)) { ret = -EFAULT; goto out; } copied++; } } arg->count_props = props_count; out: sx_xunlock(&dev->mode_config.mutex); return ret; } int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_obj_set_property *arg = data; struct drm_mode_object *arg_obj; struct drm_mode_object *prop_obj; struct drm_property *property; int ret = -EINVAL; int i; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; sx_xlock(&dev->mode_config.mutex); arg_obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type); if (!arg_obj) goto out; if (!arg_obj->properties) goto out; for (i = 0; i < arg_obj->properties->count; i++) if (arg_obj->properties->ids[i] == arg->prop_id) break; if (i == arg_obj->properties->count) goto out; prop_obj = drm_mode_object_find(dev, arg->prop_id, DRM_MODE_OBJECT_PROPERTY); if (!prop_obj) goto out; property = obj_to_property(prop_obj); if (!drm_property_change_is_valid(property, arg->value)) goto out; switch (arg_obj->type) { case DRM_MODE_OBJECT_CONNECTOR: ret = drm_mode_connector_set_obj_prop(arg_obj, property, arg->value); break; case DRM_MODE_OBJECT_CRTC: ret = drm_mode_crtc_set_obj_prop(arg_obj, property, arg->value); break; case DRM_MODE_OBJECT_PLANE: ret = drm_mode_plane_set_obj_prop(arg_obj, property, arg->value); break; } out: sx_xunlock(&dev->mode_config.mutex); return ret; } int drm_mode_connector_attach_encoder(struct drm_connector *connector, struct drm_encoder *encoder) { int i; for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { if (connector->encoder_ids[i] == 0) { connector->encoder_ids[i] = encoder->base.id; return 0; } } return -ENOMEM; } EXPORT_SYMBOL(drm_mode_connector_attach_encoder); void drm_mode_connector_detach_encoder(struct drm_connector *connector, struct drm_encoder *encoder) { int i; for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { if (connector->encoder_ids[i] == encoder->base.id) { connector->encoder_ids[i] = 0; if (connector->encoder == encoder) connector->encoder = NULL; break; } } } EXPORT_SYMBOL(drm_mode_connector_detach_encoder); int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc, int gamma_size) { crtc->gamma_size = gamma_size; crtc->gamma_store = malloc(gamma_size * sizeof(uint16_t) * 3, - DRM_MEM_KMS, M_WAITOK | M_ZERO); + DRM_MEM_KMS, M_NOWAIT | M_ZERO); if (!crtc->gamma_store) { crtc->gamma_size = 0; return -ENOMEM; } return 0; } EXPORT_SYMBOL(drm_mode_crtc_set_gamma_size); int drm_mode_gamma_set_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_crtc_lut *crtc_lut = data; struct drm_mode_object *obj; struct drm_crtc *crtc; void *r_base, *g_base, *b_base; int size; int ret = 0; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; sx_xlock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC); if (!obj) { ret = -EINVAL; goto out; } crtc = obj_to_crtc(obj); if (crtc->funcs->gamma_set == NULL) { ret = -ENOSYS; goto out; } /* memcpy into gamma store */ if (crtc_lut->gamma_size != crtc->gamma_size) { ret = -EINVAL; goto out; } size = crtc_lut->gamma_size * (sizeof(uint16_t)); r_base = crtc->gamma_store; if (copy_from_user(r_base, (void __user *)(unsigned long)crtc_lut->red, size)) { ret = -EFAULT; goto out; } g_base = (char *)r_base + size; if (copy_from_user(g_base, (void __user *)(unsigned long)crtc_lut->green, size)) { ret = -EFAULT; goto out; } b_base = (char *)g_base + size; if (copy_from_user(b_base, (void __user *)(unsigned long)crtc_lut->blue, size)) { ret = -EFAULT; goto out; } crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size); out: sx_xunlock(&dev->mode_config.mutex); return ret; } int drm_mode_gamma_get_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_crtc_lut *crtc_lut = data; struct drm_mode_object *obj; struct drm_crtc *crtc; void *r_base, *g_base, *b_base; int size; int ret = 0; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EINVAL; sx_xlock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC); if (!obj) { ret = -EINVAL; goto out; } crtc = obj_to_crtc(obj); /* memcpy into gamma store */ if (crtc_lut->gamma_size != crtc->gamma_size) { ret = -EINVAL; goto out; } size = crtc_lut->gamma_size * (sizeof(uint16_t)); r_base = crtc->gamma_store; if (copy_to_user((void __user *)(unsigned long)crtc_lut->red, r_base, size)) { ret = -EFAULT; goto out; } g_base = (char *)r_base + size; if (copy_to_user((void __user *)(unsigned long)crtc_lut->green, g_base, size)) { ret = -EFAULT; goto out; } b_base = (char *)g_base + size; if (copy_to_user((void __user *)(unsigned long)crtc_lut->blue, b_base, size)) { ret = -EFAULT; goto out; } out: sx_xunlock(&dev->mode_config.mutex); return ret; } static void free_vblank_event(void *arg) { free(arg, DRM_MEM_KMS); } int drm_mode_page_flip_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_crtc_page_flip *page_flip = data; struct drm_mode_object *obj; struct drm_crtc *crtc; struct drm_framebuffer *fb; struct drm_pending_vblank_event *e = NULL; #ifdef __linux__ unsigned long flags; #endif int hdisplay, vdisplay; int ret = -EINVAL; if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS || page_flip->reserved != 0) return -EINVAL; sx_xlock(&dev->mode_config.mutex); obj = drm_mode_object_find(dev, page_flip->crtc_id, DRM_MODE_OBJECT_CRTC); if (!obj) goto out; crtc = obj_to_crtc(obj); if (crtc->fb == NULL) { /* The framebuffer is currently unbound, presumably * due to a hotplug event, that userspace has not * yet discovered. */ ret = -EBUSY; goto out; } if (crtc->funcs->page_flip == NULL) goto out; obj = drm_mode_object_find(dev, page_flip->fb_id, DRM_MODE_OBJECT_FB); if (!obj) goto out; fb = obj_to_fb(obj); hdisplay = crtc->mode.hdisplay; vdisplay = crtc->mode.vdisplay; if (crtc->invert_dimensions) { int tmp; tmp = vdisplay; vdisplay = hdisplay; hdisplay = tmp; } if (hdisplay > fb->width || vdisplay > fb->height || crtc->x > fb->width - hdisplay || crtc->y > fb->height - vdisplay) { DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d%s.\n", fb->width, fb->height, hdisplay, vdisplay, crtc->x, crtc->y, crtc->invert_dimensions ? " (inverted)" : ""); ret = -ENOSPC; goto out; } if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) { ret = -ENOMEM; mtx_lock(&dev->event_lock); if (file_priv->event_space < sizeof e->event) { mtx_unlock(&dev->event_lock); goto out; } file_priv->event_space -= sizeof e->event; mtx_unlock(&dev->event_lock); - e = malloc(sizeof *e, DRM_MEM_KMS, M_WAITOK | M_ZERO); + e = malloc(sizeof *e, DRM_MEM_KMS, M_NOWAIT | M_ZERO); if (e == NULL) { mtx_lock(&dev->event_lock); file_priv->event_space += sizeof e->event; mtx_unlock(&dev->event_lock); goto out; } e->event.base.type = DRM_EVENT_FLIP_COMPLETE; e->event.base.length = sizeof e->event; e->event.user_data = page_flip->user_data; e->base.event = &e->event.base; e->base.file_priv = file_priv; e->base.destroy = (void (*) (struct drm_pending_event *)) free_vblank_event; } ret = crtc->funcs->page_flip(crtc, fb, e); if (ret) { if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) { mtx_lock(&dev->event_lock); file_priv->event_space += sizeof e->event; mtx_unlock(&dev->event_lock); free(e, DRM_MEM_KMS); } } out: sx_xunlock(&dev->mode_config.mutex); return ret; } void drm_mode_config_reset(struct drm_device *dev) { struct drm_crtc *crtc; struct drm_encoder *encoder; struct drm_connector *connector; list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) if (crtc->funcs->reset) crtc->funcs->reset(crtc); list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) if (encoder->funcs->reset) encoder->funcs->reset(encoder); list_for_each_entry(connector, &dev->mode_config.connector_list, head) { connector->status = connector_status_unknown; if (connector->funcs->reset) connector->funcs->reset(connector); } } EXPORT_SYMBOL(drm_mode_config_reset); int drm_mode_create_dumb_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_create_dumb *args = data; if (!dev->driver->dumb_create) return -ENOSYS; return dev->driver->dumb_create(file_priv, dev, args); } int drm_mode_mmap_dumb_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_map_dumb *args = data; /* call driver ioctl to get mmap offset */ if (!dev->driver->dumb_map_offset) return -ENOSYS; return dev->driver->dumb_map_offset(file_priv, dev, args->handle, &args->offset); } int drm_mode_destroy_dumb_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_destroy_dumb *args = data; if (!dev->driver->dumb_destroy) return -ENOSYS; return dev->driver->dumb_destroy(file_priv, dev, args->handle); } /* * Just need to support RGB formats here for compat with code that doesn't * use pixel formats directly yet. */ void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth, int *bpp) { switch (format) { case DRM_FORMAT_C8: case DRM_FORMAT_RGB332: case DRM_FORMAT_BGR233: *depth = 8; *bpp = 8; break; case DRM_FORMAT_XRGB1555: case DRM_FORMAT_XBGR1555: case DRM_FORMAT_RGBX5551: case DRM_FORMAT_BGRX5551: case DRM_FORMAT_ARGB1555: case DRM_FORMAT_ABGR1555: case DRM_FORMAT_RGBA5551: case DRM_FORMAT_BGRA5551: *depth = 15; *bpp = 16; break; case DRM_FORMAT_RGB565: case DRM_FORMAT_BGR565: *depth = 16; *bpp = 16; break; case DRM_FORMAT_RGB888: case DRM_FORMAT_BGR888: *depth = 24; *bpp = 24; break; case DRM_FORMAT_XRGB8888: case DRM_FORMAT_XBGR8888: case DRM_FORMAT_RGBX8888: case DRM_FORMAT_BGRX8888: *depth = 24; *bpp = 32; break; case DRM_FORMAT_XRGB2101010: case DRM_FORMAT_XBGR2101010: case DRM_FORMAT_RGBX1010102: case DRM_FORMAT_BGRX1010102: case DRM_FORMAT_ARGB2101010: case DRM_FORMAT_ABGR2101010: case DRM_FORMAT_RGBA1010102: case DRM_FORMAT_BGRA1010102: *depth = 30; *bpp = 32; break; case DRM_FORMAT_ARGB8888: case DRM_FORMAT_ABGR8888: case DRM_FORMAT_RGBA8888: case DRM_FORMAT_BGRA8888: *depth = 32; *bpp = 32; break; default: DRM_DEBUG_KMS("unsupported pixel format\n"); *depth = 0; *bpp = 0; break; } } EXPORT_SYMBOL(drm_fb_get_bpp_depth); /** * drm_format_num_planes - get the number of planes for format * @format: pixel format (DRM_FORMAT_*) * * RETURNS: * The number of planes used by the specified pixel format. */ int drm_format_num_planes(uint32_t format) { switch (format) { case DRM_FORMAT_YUV410: case DRM_FORMAT_YVU410: case DRM_FORMAT_YUV411: case DRM_FORMAT_YVU411: case DRM_FORMAT_YUV420: case DRM_FORMAT_YVU420: case DRM_FORMAT_YUV422: case DRM_FORMAT_YVU422: case DRM_FORMAT_YUV444: case DRM_FORMAT_YVU444: return 3; case DRM_FORMAT_NV12: case DRM_FORMAT_NV21: case DRM_FORMAT_NV16: case DRM_FORMAT_NV61: case DRM_FORMAT_NV24: case DRM_FORMAT_NV42: return 2; default: return 1; } } EXPORT_SYMBOL(drm_format_num_planes); /** * drm_format_plane_cpp - determine the bytes per pixel value * @format: pixel format (DRM_FORMAT_*) * @plane: plane index * * RETURNS: * The bytes per pixel value for the specified plane. */ int drm_format_plane_cpp(uint32_t format, int plane) { unsigned int depth; int bpp; if (plane >= drm_format_num_planes(format)) return 0; switch (format) { case DRM_FORMAT_YUYV: case DRM_FORMAT_YVYU: case DRM_FORMAT_UYVY: case DRM_FORMAT_VYUY: return 2; case DRM_FORMAT_NV12: case DRM_FORMAT_NV21: case DRM_FORMAT_NV16: case DRM_FORMAT_NV61: case DRM_FORMAT_NV24: case DRM_FORMAT_NV42: return plane ? 2 : 1; case DRM_FORMAT_YUV410: case DRM_FORMAT_YVU410: case DRM_FORMAT_YUV411: case DRM_FORMAT_YVU411: case DRM_FORMAT_YUV420: case DRM_FORMAT_YVU420: case DRM_FORMAT_YUV422: case DRM_FORMAT_YVU422: case DRM_FORMAT_YUV444: case DRM_FORMAT_YVU444: return 1; default: drm_fb_get_bpp_depth(format, &depth, &bpp); return bpp >> 3; } } EXPORT_SYMBOL(drm_format_plane_cpp); /** * drm_format_horz_chroma_subsampling - get the horizontal chroma subsampling factor * @format: pixel format (DRM_FORMAT_*) * * RETURNS: * The horizontal chroma subsampling factor for the * specified pixel format. */ int drm_format_horz_chroma_subsampling(uint32_t format) { switch (format) { case DRM_FORMAT_YUV411: case DRM_FORMAT_YVU411: case DRM_FORMAT_YUV410: case DRM_FORMAT_YVU410: return 4; case DRM_FORMAT_YUYV: case DRM_FORMAT_YVYU: case DRM_FORMAT_UYVY: case DRM_FORMAT_VYUY: case DRM_FORMAT_NV12: case DRM_FORMAT_NV21: case DRM_FORMAT_NV16: case DRM_FORMAT_NV61: case DRM_FORMAT_YUV422: case DRM_FORMAT_YVU422: case DRM_FORMAT_YUV420: case DRM_FORMAT_YVU420: return 2; default: return 1; } } EXPORT_SYMBOL(drm_format_horz_chroma_subsampling); /** * drm_format_vert_chroma_subsampling - get the vertical chroma subsampling factor * @format: pixel format (DRM_FORMAT_*) * * RETURNS: * The vertical chroma subsampling factor for the * specified pixel format. */ int drm_format_vert_chroma_subsampling(uint32_t format) { switch (format) { case DRM_FORMAT_YUV410: case DRM_FORMAT_YVU410: return 4; case DRM_FORMAT_YUV420: case DRM_FORMAT_YVU420: case DRM_FORMAT_NV12: case DRM_FORMAT_NV21: return 2; default: return 1; } } EXPORT_SYMBOL(drm_format_vert_chroma_subsampling); Index: head/sys/dev/drm2/drm_fops.c =================================================================== --- head/sys/dev/drm2/drm_fops.c (revision 288652) +++ head/sys/dev/drm2/drm_fops.c (revision 288653) @@ -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: head/sys/dev/drm2/drm_pci.c =================================================================== --- head/sys/dev/drm2/drm_pci.c (revision 288652) +++ head/sys/dev/drm2/drm_pci.c (revision 288653) @@ -1,492 +1,492 @@ /* drm_pci.h -- PCI DMA memory management wrappers for DRM -*- linux-c -*- */ /** * \file drm_pci.c * \brief Functions and ioctls to manage PCI memory * * \warning These interfaces aren't stable yet. * * \todo Implement the remaining ioctl's for the PCI pools. * \todo The wrappers here are so thin that they would be better off inlined.. * * \author José Fonseca * \author Leif Delgass */ /* * Copyright 2003 José Fonseca. * Copyright 2003 Leif Delgass. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS 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_msi = 1; /* Enable by default. */ SYSCTL_NODE(_hw, OID_AUTO, drm, CTLFLAG_RW, NULL, "DRM device"); SYSCTL_INT(_hw_drm, OID_AUTO, msi, CTLFLAG_RDTUN, &drm_msi, 1, "Enable MSI interrupts for drm devices"); /**********************************************************************/ /** \name PCI memory */ /*@{*/ static void drm_pci_busdma_callback(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { drm_dma_handle_t *dmah = arg; if (error != 0) return; KASSERT(nsegs == 1, ("drm_pci_busdma_callback: bad dma segment count")); dmah->busaddr = segs[0].ds_addr; } /** * \brief Allocate a PCI consistent memory block, for DMA. */ drm_dma_handle_t *drm_pci_alloc(struct drm_device * dev, size_t size, size_t align, dma_addr_t maxaddr) { drm_dma_handle_t *dmah; int ret; /* Need power-of-two alignment, so fail the allocation if it isn't. */ if ((align & (align - 1)) != 0) { DRM_ERROR("drm_pci_alloc with non-power-of-two alignment %d\n", (int)align); return NULL; } dmah = malloc(sizeof(drm_dma_handle_t), DRM_MEM_DMA, M_ZERO | M_NOWAIT); if (dmah == NULL) return NULL; /* Make sure we aren't holding mutexes here */ mtx_assert(&dev->dma_lock, MA_NOTOWNED); if (mtx_owned(&dev->dma_lock)) DRM_ERROR("called while holding dma_lock\n"); ret = bus_dma_tag_create( bus_get_dma_tag(dev->dev), /* parent */ align, 0, /* align, boundary */ maxaddr, BUS_SPACE_MAXADDR, /* lowaddr, highaddr */ NULL, NULL, /* filtfunc, filtfuncargs */ size, 1, size, /* maxsize, nsegs, maxsegsize */ 0, NULL, NULL, /* flags, lockfunc, lockfuncargs */ &dmah->tag); if (ret != 0) { free(dmah, DRM_MEM_DMA); return NULL; } ret = bus_dmamem_alloc(dmah->tag, &dmah->vaddr, BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_NOCACHE, &dmah->map); if (ret != 0) { bus_dma_tag_destroy(dmah->tag); free(dmah, DRM_MEM_DMA); return NULL; } ret = bus_dmamap_load(dmah->tag, dmah->map, dmah->vaddr, size, drm_pci_busdma_callback, dmah, BUS_DMA_NOWAIT); if (ret != 0) { bus_dmamem_free(dmah->tag, dmah->vaddr, dmah->map); bus_dma_tag_destroy(dmah->tag); free(dmah, DRM_MEM_DMA); return NULL; } return dmah; } EXPORT_SYMBOL(drm_pci_alloc); /** * \brief Free a PCI consistent memory block without freeing its descriptor. * * This function is for internal use in the Linux-specific DRM core code. */ void __drm_pci_free(struct drm_device * dev, drm_dma_handle_t * dmah) { if (dmah == NULL) return; bus_dmamap_unload(dmah->tag, dmah->map); bus_dmamem_free(dmah->tag, dmah->vaddr, dmah->map); bus_dma_tag_destroy(dmah->tag); } /** * \brief Free a PCI consistent memory block */ void drm_pci_free(struct drm_device * dev, drm_dma_handle_t * dmah) { __drm_pci_free(dev, dmah); free(dmah, DRM_MEM_DMA); } EXPORT_SYMBOL(drm_pci_free); static int drm_get_pci_domain(struct drm_device *dev) { return dev->pci_domain; } static int drm_pci_get_irq(struct drm_device *dev) { if (dev->irqr) return (dev->irq); dev->irqr = bus_alloc_resource_any(dev->dev, SYS_RES_IRQ, &dev->irqrid, RF_SHAREABLE); if (!dev->irqr) { dev_err(dev->dev, "Failed to allocate IRQ\n"); return (0); } dev->irq = (int) rman_get_start(dev->irqr); return (dev->irq); } static void drm_pci_free_irq(struct drm_device *dev) { if (dev->irqr == NULL) return; bus_release_resource(dev->dev, SYS_RES_IRQ, dev->irqrid, dev->irqr); dev->irqr = NULL; dev->irq = 0; } static const char *drm_pci_get_name(struct drm_device *dev) { return dev->driver->name; } int drm_pci_set_busid(struct drm_device *dev, struct drm_master *master) { int len, ret; master->unique_len = 40; master->unique_size = master->unique_len; master->unique = malloc(master->unique_size, DRM_MEM_DRIVER, M_NOWAIT); if (master->unique == NULL) return -ENOMEM; len = snprintf(master->unique, master->unique_len, "pci:%04x:%02x:%02x.%d", dev->pci_domain, dev->pci_bus, dev->pci_slot, dev->pci_func); if (len >= master->unique_len) { DRM_ERROR("buffer overflow"); ret = -EINVAL; goto err; } else master->unique_len = len; return 0; err: return ret; } int drm_pci_set_unique(struct drm_device *dev, struct drm_master *master, struct drm_unique *u) { int domain, bus, slot, func, ret; master->unique_len = u->unique_len; master->unique_size = u->unique_len + 1; - master->unique = malloc(master->unique_size, DRM_MEM_DRIVER, M_WAITOK); + master->unique = malloc(master->unique_size, DRM_MEM_DRIVER, M_NOWAIT); if (!master->unique) { ret = -ENOMEM; goto err; } if (copy_from_user(master->unique, u->unique, master->unique_len)) { ret = -EFAULT; goto err; } master->unique[master->unique_len] = '\0'; /* Return error if the busid submitted doesn't match the device's actual * busid. */ ret = sscanf(master->unique, "PCI:%d:%d:%d", &bus, &slot, &func); if (ret != 3) { ret = -EINVAL; goto err; } domain = bus >> 8; bus &= 0xff; if ((domain != dev->pci_domain) || (bus != dev->pci_bus) || (slot != dev->pci_slot) || (func != dev->pci_func)) { ret = -EINVAL; goto err; } return 0; err: return ret; } static int drm_pci_irq_by_busid(struct drm_device *dev, struct drm_irq_busid *p) { if ((p->busnum >> 8) != drm_get_pci_domain(dev) || (p->busnum & 0xff) != dev->pci_bus || p->devnum != dev->pci_slot || p->funcnum != dev->pci_func) return -EINVAL; p->irq = dev->irq; DRM_DEBUG("%d:%d:%d => IRQ %d\n", p->busnum, p->devnum, p->funcnum, p->irq); return 0; } int drm_pci_agp_init(struct drm_device *dev) { if (drm_core_has_AGP(dev)) { if (drm_pci_device_is_agp(dev)) dev->agp = drm_agp_init(dev); if (drm_core_check_feature(dev, DRIVER_REQUIRE_AGP) && (dev->agp == NULL)) { DRM_ERROR("Cannot initialize the agpgart module.\n"); return -EINVAL; } if (drm_core_has_MTRR(dev)) { if (dev->agp && dev->agp->agp_info.ai_aperture_base != 0) { if (drm_mtrr_add(dev->agp->agp_info.ai_aperture_base, dev->agp->agp_info.ai_aperture_size, DRM_MTRR_WC) == 0) dev->agp->agp_mtrr = 1; else dev->agp->agp_mtrr = -1; } } } return 0; } static struct drm_bus drm_pci_bus = { .bus_type = DRIVER_BUS_PCI, .get_irq = drm_pci_get_irq, .free_irq = drm_pci_free_irq, .get_name = drm_pci_get_name, .set_busid = drm_pci_set_busid, .set_unique = drm_pci_set_unique, .irq_by_busid = drm_pci_irq_by_busid, .agp_init = drm_pci_agp_init, }; /** * Register. * * \param pdev - PCI device structure * \param ent entry from the PCI ID table with device type flags * \return zero on success or a negative number on failure. * * Attempt to gets inter module "drm" information. If we are first * then register the character device and inter module information. * Try and register, if we fail to register, backout previous work. */ int drm_get_pci_dev(device_t kdev, struct drm_device *dev, struct drm_driver *driver) { int ret; DRM_DEBUG("\n"); driver->bus = &drm_pci_bus; dev->dev = kdev; dev->pci_domain = pci_get_domain(dev->dev); dev->pci_bus = pci_get_bus(dev->dev); dev->pci_slot = pci_get_slot(dev->dev); dev->pci_func = pci_get_function(dev->dev); dev->pci_vendor = pci_get_vendor(dev->dev); dev->pci_device = pci_get_device(dev->dev); dev->pci_subvendor = pci_get_subvendor(dev->dev); dev->pci_subdevice = pci_get_subdevice(dev->dev); sx_xlock(&drm_global_mutex); if ((ret = drm_fill_in_dev(dev, driver))) { DRM_ERROR("Failed to fill in dev: %d\n", ret); goto err_g1; } if (drm_core_check_feature(dev, DRIVER_MODESET)) { ret = drm_get_minor(dev, &dev->control, DRM_MINOR_CONTROL); if (ret) goto err_g2; } if ((ret = drm_get_minor(dev, &dev->primary, DRM_MINOR_LEGACY))) goto err_g3; if (dev->driver->load) { ret = dev->driver->load(dev, dev->id_entry->driver_private); if (ret) goto err_g4; } /* setup the grouping for the legacy output */ if (drm_core_check_feature(dev, DRIVER_MODESET)) { ret = drm_mode_group_init_legacy_group(dev, &dev->primary->mode_group); if (ret) goto err_g5; } #ifdef FREEBSD_NOTYET list_add_tail(&dev->driver_item, &driver->device_list); #endif /* FREEBSD_NOTYET */ DRM_INFO("Initialized %s %d.%d.%d %s for %s on minor %d\n", driver->name, driver->major, driver->minor, driver->patchlevel, driver->date, device_get_nameunit(dev->dev), dev->primary->index); sx_xunlock(&drm_global_mutex); return 0; err_g5: if (dev->driver->unload) dev->driver->unload(dev); err_g4: drm_put_minor(&dev->primary); err_g3: if (drm_core_check_feature(dev, DRIVER_MODESET)) drm_put_minor(&dev->control); err_g2: drm_cancel_fill_in_dev(dev); err_g1: sx_xunlock(&drm_global_mutex); return ret; } EXPORT_SYMBOL(drm_get_pci_dev); int drm_pci_enable_msi(struct drm_device *dev) { int msicount, ret; if (!drm_msi) return (-ENOENT); msicount = pci_msi_count(dev->dev); DRM_DEBUG("MSI count = %d\n", msicount); if (msicount > 1) msicount = 1; ret = pci_alloc_msi(dev->dev, &msicount); if (ret == 0) { DRM_INFO("MSI enabled %d message(s)\n", msicount); dev->msi_enabled = 1; dev->irqrid = 1; } return (-ret); } void drm_pci_disable_msi(struct drm_device *dev) { if (!dev->msi_enabled) return; pci_release_msi(dev->dev); dev->msi_enabled = 0; dev->irqrid = 0; } int drm_pcie_get_speed_cap_mask(struct drm_device *dev, u32 *mask) { device_t root; int pos; u32 lnkcap = 0, lnkcap2 = 0; *mask = 0; if (!drm_pci_device_is_pcie(dev)) return -EINVAL; root = device_get_parent( /* pcib */ device_get_parent( /* `-- pci */ device_get_parent( /* `-- vgapci */ dev->dev))); /* `-- drmn */ pos = 0; pci_find_cap(root, PCIY_EXPRESS, &pos); if (!pos) return -EINVAL; /* we've been informed via and serverworks don't make the cut */ if (pci_get_vendor(root) == PCI_VENDOR_ID_VIA || pci_get_vendor(root) == PCI_VENDOR_ID_SERVERWORKS) return -EINVAL; lnkcap = pci_read_config(root, pos + PCIER_LINK_CAP, 4); lnkcap2 = pci_read_config(root, pos + PCIER_LINK_CAP2, 4); lnkcap &= PCIEM_LINK_CAP_MAX_SPEED; lnkcap2 &= 0xfe; #define PCI_EXP_LNKCAP2_SLS_2_5GB 0x02 /* Supported Link Speed 2.5GT/s */ #define PCI_EXP_LNKCAP2_SLS_5_0GB 0x04 /* Supported Link Speed 5.0GT/s */ #define PCI_EXP_LNKCAP2_SLS_8_0GB 0x08 /* Supported Link Speed 8.0GT/s */ if (lnkcap2) { /* PCIE GEN 3.0 */ if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_2_5GB) *mask |= DRM_PCIE_SPEED_25; if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_5_0GB) *mask |= DRM_PCIE_SPEED_50; if (lnkcap2 & PCI_EXP_LNKCAP2_SLS_8_0GB) *mask |= DRM_PCIE_SPEED_80; } else { if (lnkcap & 1) *mask |= DRM_PCIE_SPEED_25; if (lnkcap & 2) *mask |= DRM_PCIE_SPEED_50; } DRM_INFO("probing gen 2 caps for device %x:%x = %x/%x\n", pci_get_vendor(root), pci_get_device(root), lnkcap, lnkcap2); return 0; } EXPORT_SYMBOL(drm_pcie_get_speed_cap_mask); Index: head/sys/dev/drm2/drm_stub.c =================================================================== --- head/sys/dev/drm2/drm_stub.c (revision 288652) +++ head/sys/dev/drm2/drm_stub.c (revision 288653) @@ -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: head/sys/dev/drm2/i915/i915_dma.c =================================================================== --- head/sys/dev/drm2/i915/i915_dma.c (revision 288652) +++ head/sys/dev/drm2/i915/i915_dma.c (revision 288653) @@ -1,1714 +1,1716 @@ /* 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)) /** * Lock test for when it's just for synchronization of ring access. * * In that case, we don't need to do it when GEM is initialized as nobody else * has access to the ring. */ #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 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 i915_emit_cmds(struct drm_device * dev, int *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 *box, int DR1, int DR4) { struct drm_i915_private *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) 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) 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(dev, &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) { struct drm_i915_private *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) { ret = i915_emit_box(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) 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) 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) 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; } } /** * i915_driver_load - setup chip and create an initial config * @dev: DRM device * @flags: startup flags * * The driver load routine has to do several things: * - drive output discovery via intel_modeset_init() * - initialize the memory manager * - allocate initial config memory * - setup the DRM framebuffer with the allocated memory */ 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) { 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->driver_priv = i915_file_priv; drm_gem_names_init(&i915_file_priv->context_idr); return 0; } /** * i915_driver_lastclose - clean up after all DRM clients have exited * @dev: DRM device * * Take care of cleaning up after all DRM clients have exited. In the * mode setting case, we want to restore the kernel's initial mode (just * in case the last client left us in a bad state). * * Additionally, in the non-mode setting case, we'll tear down the GTT * and DMA structures, since the kernel won't be using them, and clea * up any GEM state. */ void i915_driver_lastclose(struct drm_device * dev) { drm_i915_private_t *dev_priv = dev->dev_private; /* On gen6+ we refuse to init without kms enabled, but then the drm core * goes right around and calls lastclose. Check for this and don't clean * up anything. */ if (!dev_priv) return; if (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: head/sys/dev/drm2/i915/i915_drv.c =================================================================== --- head/sys/dev/drm2/i915/i915_drv.c (revision 288652) +++ head/sys/dev/drm2/i915/i915_drv.c (revision 288653) @@ -1,1054 +1,1055 @@ /* i915_drv.c -- Intel i915 driver -*- linux-c -*- * Created: Wed Feb 14 17:10:04 2001 by gareth@valinux.com */ /*- * 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. * * Authors: * Gareth Hughes * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include "fb_if.h" /* drv_PCI_IDs comes from drm_pciids.h, generated from drm_pciids.txt. */ static drm_pci_id_list_t i915_pciidlist[] = { i915_PCI_IDS }; #define INTEL_VGA_DEVICE(id, info_) { \ .device = id, \ .info = info_, \ } static const struct intel_device_info intel_i830_info = { .gen = 2, .is_mobile = 1, .cursor_needs_physical = 1, .has_overlay = 1, .overlay_needs_physical = 1, }; static const struct intel_device_info intel_845g_info = { .gen = 2, .has_overlay = 1, .overlay_needs_physical = 1, }; static const struct intel_device_info intel_i85x_info = { .gen = 2, .is_i85x = 1, .is_mobile = 1, .cursor_needs_physical = 1, .has_overlay = 1, .overlay_needs_physical = 1, }; static const struct intel_device_info intel_i865g_info = { .gen = 2, .has_overlay = 1, .overlay_needs_physical = 1, }; static const struct intel_device_info intel_i915g_info = { .gen = 3, .is_i915g = 1, .cursor_needs_physical = 1, .has_overlay = 1, .overlay_needs_physical = 1, }; static const struct intel_device_info intel_i915gm_info = { .gen = 3, .is_mobile = 1, .cursor_needs_physical = 1, .has_overlay = 1, .overlay_needs_physical = 1, .supports_tv = 1, }; static const struct intel_device_info intel_i945g_info = { .gen = 3, .has_hotplug = 1, .cursor_needs_physical = 1, .has_overlay = 1, .overlay_needs_physical = 1, }; static const struct intel_device_info intel_i945gm_info = { .gen = 3, .is_i945gm = 1, .is_mobile = 1, .has_hotplug = 1, .cursor_needs_physical = 1, .has_overlay = 1, .overlay_needs_physical = 1, .supports_tv = 1, }; static const struct intel_device_info intel_i965g_info = { .gen = 4, .is_broadwater = 1, .has_hotplug = 1, .has_overlay = 1, }; static const struct intel_device_info intel_i965gm_info = { .gen = 4, .is_crestline = 1, .is_mobile = 1, .has_fbc = 1, .has_hotplug = 1, .has_overlay = 1, .supports_tv = 1, }; static const struct intel_device_info intel_g33_info = { .gen = 3, .is_g33 = 1, .need_gfx_hws = 1, .has_hotplug = 1, .has_overlay = 1, }; static const struct intel_device_info intel_g45_info = { .gen = 4, .is_g4x = 1, .need_gfx_hws = 1, .has_pipe_cxsr = 1, .has_hotplug = 1, .has_bsd_ring = 1, }; static const struct intel_device_info intel_gm45_info = { .gen = 4, .is_g4x = 1, .is_mobile = 1, .need_gfx_hws = 1, .has_fbc = 1, .has_pipe_cxsr = 1, .has_hotplug = 1, .supports_tv = 1, .has_bsd_ring = 1, }; static const struct intel_device_info intel_pineview_info = { .gen = 3, .is_g33 = 1, .is_pineview = 1, .is_mobile = 1, .need_gfx_hws = 1, .has_hotplug = 1, .has_overlay = 1, }; static const struct intel_device_info intel_ironlake_d_info = { .gen = 5, .need_gfx_hws = 1, .has_hotplug = 1, .has_bsd_ring = 1, .has_pch_split = 1, }; static const struct intel_device_info intel_ironlake_m_info = { .gen = 5, .is_mobile = 1, .need_gfx_hws = 1, .has_hotplug = 1, .has_fbc = 0, /* disabled due to buggy hardware */ .has_bsd_ring = 1, .has_pch_split = 1, }; static const struct intel_device_info intel_sandybridge_d_info = { .gen = 6, .need_gfx_hws = 1, .has_hotplug = 1, .has_bsd_ring = 1, .has_blt_ring = 1, .has_llc = 1, .has_pch_split = 1, }; static const struct intel_device_info intel_sandybridge_m_info = { .gen = 6, .is_mobile = 1, .need_gfx_hws = 1, .has_hotplug = 1, .has_fbc = 1, .has_bsd_ring = 1, .has_blt_ring = 1, .has_llc = 1, .has_pch_split = 1, }; static const struct intel_device_info intel_ivybridge_d_info = { .is_ivybridge = 1, .gen = 7, .need_gfx_hws = 1, .has_hotplug = 1, .has_bsd_ring = 1, .has_blt_ring = 1, .has_llc = 1, .has_pch_split = 1, }; static const struct intel_device_info intel_ivybridge_m_info = { .is_ivybridge = 1, .gen = 7, .is_mobile = 1, .need_gfx_hws = 1, .has_hotplug = 1, .has_fbc = 0, /* FBC is not enabled on Ivybridge mobile yet */ .has_bsd_ring = 1, .has_blt_ring = 1, .has_llc = 1, .has_pch_split = 1, }; static const struct intel_device_info intel_valleyview_m_info = { .gen = 7, .is_mobile = 1, .need_gfx_hws = 1, .has_hotplug = 1, .has_fbc = 0, .has_bsd_ring = 1, .has_blt_ring = 1, .is_valleyview = 1, .not_supported = 1, }; static const struct intel_device_info intel_valleyview_d_info = { .gen = 7, .need_gfx_hws = 1, .has_hotplug = 1, .has_fbc = 0, .has_bsd_ring = 1, .has_blt_ring = 1, .is_valleyview = 1, .not_supported = 1, }; static const struct intel_device_info intel_haswell_d_info = { .is_haswell = 1, .gen = 7, .need_gfx_hws = 1, .has_hotplug = 1, .has_bsd_ring = 1, .has_blt_ring = 1, .has_llc = 1, .has_pch_split = 1, .not_supported = 1, }; static const struct intel_device_info intel_haswell_m_info = { .is_haswell = 1, .gen = 7, .is_mobile = 1, .need_gfx_hws = 1, .has_hotplug = 1, .has_bsd_ring = 1, .has_blt_ring = 1, .has_llc = 1, .has_pch_split = 1, .not_supported = 1, }; static const struct intel_gfx_device_id { int device; const struct intel_device_info *info; } pciidlist[] = { /* aka */ INTEL_VGA_DEVICE(0x3577, &intel_i830_info), /* I830_M */ INTEL_VGA_DEVICE(0x2562, &intel_845g_info), /* 845_G */ INTEL_VGA_DEVICE(0x3582, &intel_i85x_info), /* I855_GM */ INTEL_VGA_DEVICE(0x358e, &intel_i85x_info), INTEL_VGA_DEVICE(0x2572, &intel_i865g_info), /* I865_G */ INTEL_VGA_DEVICE(0x2582, &intel_i915g_info), /* I915_G */ INTEL_VGA_DEVICE(0x258a, &intel_i915g_info), /* E7221_G */ INTEL_VGA_DEVICE(0x2592, &intel_i915gm_info), /* I915_GM */ INTEL_VGA_DEVICE(0x2772, &intel_i945g_info), /* I945_G */ INTEL_VGA_DEVICE(0x27a2, &intel_i945gm_info), /* I945_GM */ INTEL_VGA_DEVICE(0x27ae, &intel_i945gm_info), /* I945_GME */ INTEL_VGA_DEVICE(0x2972, &intel_i965g_info), /* I946_GZ */ INTEL_VGA_DEVICE(0x2982, &intel_i965g_info), /* G35_G */ INTEL_VGA_DEVICE(0x2992, &intel_i965g_info), /* I965_Q */ INTEL_VGA_DEVICE(0x29a2, &intel_i965g_info), /* I965_G */ INTEL_VGA_DEVICE(0x29b2, &intel_g33_info), /* Q35_G */ INTEL_VGA_DEVICE(0x29c2, &intel_g33_info), /* G33_G */ INTEL_VGA_DEVICE(0x29d2, &intel_g33_info), /* Q33_G */ INTEL_VGA_DEVICE(0x2a02, &intel_i965gm_info), /* I965_GM */ INTEL_VGA_DEVICE(0x2a12, &intel_i965gm_info), /* I965_GME */ INTEL_VGA_DEVICE(0x2a42, &intel_gm45_info), /* GM45_G */ INTEL_VGA_DEVICE(0x2e02, &intel_g45_info), /* IGD_E_G */ INTEL_VGA_DEVICE(0x2e12, &intel_g45_info), /* Q45_G */ INTEL_VGA_DEVICE(0x2e22, &intel_g45_info), /* G45_G */ INTEL_VGA_DEVICE(0x2e32, &intel_g45_info), /* G41_G */ INTEL_VGA_DEVICE(0x2e42, &intel_g45_info), /* B43_G */ INTEL_VGA_DEVICE(0x2e92, &intel_g45_info), /* B43_G.1 */ INTEL_VGA_DEVICE(0xa001, &intel_pineview_info), INTEL_VGA_DEVICE(0xa011, &intel_pineview_info), INTEL_VGA_DEVICE(0x0042, &intel_ironlake_d_info), INTEL_VGA_DEVICE(0x0046, &intel_ironlake_m_info), INTEL_VGA_DEVICE(0x0102, &intel_sandybridge_d_info), INTEL_VGA_DEVICE(0x0112, &intel_sandybridge_d_info), INTEL_VGA_DEVICE(0x0122, &intel_sandybridge_d_info), INTEL_VGA_DEVICE(0x0106, &intel_sandybridge_m_info), INTEL_VGA_DEVICE(0x0116, &intel_sandybridge_m_info), INTEL_VGA_DEVICE(0x0126, &intel_sandybridge_m_info), INTEL_VGA_DEVICE(0x010A, &intel_sandybridge_d_info), INTEL_VGA_DEVICE(0x0156, &intel_ivybridge_m_info), /* GT1 mobile */ INTEL_VGA_DEVICE(0x0166, &intel_ivybridge_m_info), /* GT2 mobile */ INTEL_VGA_DEVICE(0x0152, &intel_ivybridge_d_info), /* GT1 desktop */ INTEL_VGA_DEVICE(0x0162, &intel_ivybridge_d_info), /* GT2 desktop */ INTEL_VGA_DEVICE(0x015a, &intel_ivybridge_d_info), /* GT1 server */ INTEL_VGA_DEVICE(0x016a, &intel_ivybridge_d_info), /* GT2 server */ INTEL_VGA_DEVICE(0x0402, &intel_haswell_d_info), /* GT1 desktop */ INTEL_VGA_DEVICE(0x0412, &intel_haswell_d_info), /* GT2 desktop */ INTEL_VGA_DEVICE(0x0422, &intel_haswell_d_info), /* GT2 desktop */ INTEL_VGA_DEVICE(0x040a, &intel_haswell_d_info), /* GT1 server */ INTEL_VGA_DEVICE(0x041a, &intel_haswell_d_info), /* GT2 server */ INTEL_VGA_DEVICE(0x042a, &intel_haswell_d_info), /* GT2 server */ INTEL_VGA_DEVICE(0x0406, &intel_haswell_m_info), /* GT1 mobile */ INTEL_VGA_DEVICE(0x0416, &intel_haswell_m_info), /* GT2 mobile */ INTEL_VGA_DEVICE(0x0426, &intel_haswell_m_info), /* GT2 mobile */ INTEL_VGA_DEVICE(0x0C02, &intel_haswell_d_info), /* SDV GT1 desktop */ INTEL_VGA_DEVICE(0x0C12, &intel_haswell_d_info), /* SDV GT2 desktop */ INTEL_VGA_DEVICE(0x0C22, &intel_haswell_d_info), /* SDV GT2 desktop */ INTEL_VGA_DEVICE(0x0C0A, &intel_haswell_d_info), /* SDV GT1 server */ INTEL_VGA_DEVICE(0x0C1A, &intel_haswell_d_info), /* SDV GT2 server */ INTEL_VGA_DEVICE(0x0C2A, &intel_haswell_d_info), /* SDV GT2 server */ INTEL_VGA_DEVICE(0x0C06, &intel_haswell_m_info), /* SDV GT1 mobile */ INTEL_VGA_DEVICE(0x0C16, &intel_haswell_m_info), /* SDV GT2 mobile */ INTEL_VGA_DEVICE(0x0C26, &intel_haswell_m_info), /* SDV GT2 mobile */ INTEL_VGA_DEVICE(0x0A02, &intel_haswell_d_info), /* ULT GT1 desktop */ INTEL_VGA_DEVICE(0x0A12, &intel_haswell_d_info), /* ULT GT2 desktop */ INTEL_VGA_DEVICE(0x0A22, &intel_haswell_d_info), /* ULT GT2 desktop */ INTEL_VGA_DEVICE(0x0A0A, &intel_haswell_d_info), /* ULT GT1 server */ INTEL_VGA_DEVICE(0x0A1A, &intel_haswell_d_info), /* ULT GT2 server */ INTEL_VGA_DEVICE(0x0A2A, &intel_haswell_d_info), /* ULT GT2 server */ INTEL_VGA_DEVICE(0x0A06, &intel_haswell_m_info), /* ULT GT1 mobile */ INTEL_VGA_DEVICE(0x0A16, &intel_haswell_m_info), /* ULT GT2 mobile */ INTEL_VGA_DEVICE(0x0A26, &intel_haswell_m_info), /* ULT GT2 mobile */ INTEL_VGA_DEVICE(0x0D02, &intel_haswell_d_info), /* CRW GT1 desktop */ INTEL_VGA_DEVICE(0x0D12, &intel_haswell_d_info), /* CRW GT2 desktop */ INTEL_VGA_DEVICE(0x0D22, &intel_haswell_d_info), /* CRW GT2 desktop */ INTEL_VGA_DEVICE(0x0D0A, &intel_haswell_d_info), /* CRW GT1 server */ INTEL_VGA_DEVICE(0x0D1A, &intel_haswell_d_info), /* CRW GT2 server */ INTEL_VGA_DEVICE(0x0D2A, &intel_haswell_d_info), /* CRW GT2 server */ INTEL_VGA_DEVICE(0x0D06, &intel_haswell_m_info), /* CRW GT1 mobile */ INTEL_VGA_DEVICE(0x0D16, &intel_haswell_m_info), /* CRW GT2 mobile */ INTEL_VGA_DEVICE(0x0D26, &intel_haswell_m_info), /* CRW GT2 mobile */ INTEL_VGA_DEVICE(0x0f30, &intel_valleyview_m_info), INTEL_VGA_DEVICE(0x0157, &intel_valleyview_m_info), INTEL_VGA_DEVICE(0x0155, &intel_valleyview_d_info), {0, 0} }; static int i915_enable_unsupported; static int i915_drm_freeze(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; drm_kms_helper_poll_disable(dev); #if 0 pci_save_state(dev->pdev); #endif /* If KMS is active, we do the leavevt stuff here */ if (drm_core_check_feature(dev, DRIVER_MODESET)) { int error = i915_gem_idle(dev); if (error) { device_printf(dev->dev, "GEM idle failed, resume might fail\n"); return error; } drm_irq_uninstall(dev); } i915_save_state(dev); intel_opregion_fini(dev); /* Modeset on resume, not lid events */ dev_priv->modeset_on_lid = 0; return 0; } static int i915_suspend(device_t kdev) { struct drm_device *dev; int error; dev = device_get_softc(kdev); if (dev == NULL || dev->dev_private == NULL) { DRM_ERROR("DRM not initialized, aborting suspend.\n"); return ENODEV; } DRM_DEBUG_KMS("starting suspend\n"); error = i915_drm_freeze(dev); if (error) return (-error); error = bus_generic_suspend(kdev); DRM_DEBUG_KMS("finished suspend %d\n", error); return (error); } static int i915_drm_thaw(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; int error = 0; if (drm_core_check_feature(dev, DRIVER_MODESET)) { DRM_LOCK(dev); i915_gem_restore_gtt_mappings(dev); DRM_UNLOCK(dev); } i915_restore_state(dev); intel_opregion_setup(dev); /* KMS EnterVT equivalent */ if (drm_core_check_feature(dev, DRIVER_MODESET)) { if (HAS_PCH_SPLIT(dev)) ironlake_init_pch_refclk(dev); DRM_LOCK(dev); dev_priv->mm.suspended = 0; error = i915_gem_init_hw(dev); DRM_UNLOCK(dev); intel_modeset_init_hw(dev); sx_xlock(&dev->mode_config.mutex); drm_mode_config_reset(dev); sx_xunlock(&dev->mode_config.mutex); drm_irq_install(dev); sx_xlock(&dev->mode_config.mutex); /* Resume the modeset for every activated CRTC */ drm_helper_resume_force_mode(dev); sx_xunlock(&dev->mode_config.mutex); } intel_opregion_init(dev); dev_priv->modeset_on_lid = 0; return error; } static int i915_resume(device_t kdev) { struct drm_device *dev; int ret; dev = device_get_softc(kdev); DRM_DEBUG_KMS("starting resume\n"); #if 0 if (pci_enable_device(dev->pdev)) return -EIO; pci_set_master(dev->pdev); #endif ret = i915_drm_thaw(dev); if (ret != 0) return (-ret); drm_kms_helper_poll_enable(dev); ret = bus_generic_resume(kdev); DRM_DEBUG_KMS("finished resume %d\n", ret); return (ret); } static int i915_probe(device_t kdev) { const struct intel_device_info *info; int error; error = drm_probe_helper(kdev, i915_pciidlist); if (error != 0) return (-error); info = i915_get_device_id(pci_get_device(kdev)); if (info == NULL) return (ENXIO); return (0); } int i915_modeset; static int i915_attach(device_t kdev) { if (i915_modeset == 1) i915_driver_info.driver_features |= DRIVER_MODESET; return (-drm_attach_helper(kdev, i915_pciidlist, &i915_driver_info)); } static struct fb_info * i915_fb_helper_getinfo(device_t kdev) { struct intel_fbdev *ifbdev; drm_i915_private_t *dev_priv; struct drm_device *dev; struct fb_info *info; dev = device_get_softc(kdev); dev_priv = dev->dev_private; ifbdev = dev_priv->fbdev; if (ifbdev == NULL) return (NULL); info = ifbdev->helper.fbdev; return (info); } const struct intel_device_info * i915_get_device_id(int device) { const struct intel_gfx_device_id *did; for (did = &pciidlist[0]; did->device != 0; did++) { if (did->device != device) continue; if (did->info->not_supported && !i915_enable_unsupported) return (NULL); return (did->info); } return (NULL); } static device_method_t i915_methods[] = { /* Device interface */ DEVMETHOD(device_probe, i915_probe), DEVMETHOD(device_attach, i915_attach), DEVMETHOD(device_suspend, i915_suspend), DEVMETHOD(device_resume, i915_resume), DEVMETHOD(device_detach, drm_generic_detach), /* Framebuffer service methods */ DEVMETHOD(fb_getinfo, i915_fb_helper_getinfo), DEVMETHOD_END }; static driver_t i915_driver = { "drmn", i915_methods, sizeof(struct drm_device) }; extern devclass_t drm_devclass; DRIVER_MODULE_ORDERED(i915kms, vgapci, i915_driver, drm_devclass, 0, 0, SI_ORDER_ANY); MODULE_DEPEND(i915kms, drmn, 1, 1, 1); MODULE_DEPEND(i915kms, agp, 1, 1, 1); MODULE_DEPEND(i915kms, iicbus, 1, 1, 1); MODULE_DEPEND(i915kms, iic, 1, 1, 1); MODULE_DEPEND(i915kms, iicbb, 1, 1, 1); int intel_iommu_enabled = 0; TUNABLE_INT("drm.i915.intel_iommu_enabled", &intel_iommu_enabled); int intel_iommu_gfx_mapped = 0; TUNABLE_INT("drm.i915.intel_iommu_gfx_mapped", &intel_iommu_gfx_mapped); int i915_prefault_disable; TUNABLE_INT("drm.i915.prefault_disable", &i915_prefault_disable); int i915_semaphores = -1; TUNABLE_INT("drm.i915.semaphores", &i915_semaphores); static int i915_try_reset = 1; TUNABLE_INT("drm.i915.try_reset", &i915_try_reset); unsigned int i915_lvds_downclock = 0; TUNABLE_INT("drm.i915.lvds_downclock", &i915_lvds_downclock); int i915_vbt_sdvo_panel_type = -1; TUNABLE_INT("drm.i915.vbt_sdvo_panel_type", &i915_vbt_sdvo_panel_type); unsigned int i915_powersave = 1; TUNABLE_INT("drm.i915.powersave", &i915_powersave); int i915_enable_fbc = 0; TUNABLE_INT("drm.i915.enable_fbc", &i915_enable_fbc); int i915_enable_rc6 = 0; TUNABLE_INT("drm.i915.enable_rc6", &i915_enable_rc6); int i915_lvds_channel_mode; TUNABLE_INT("drm.i915.lvds_channel_mode", &i915_lvds_channel_mode); int i915_panel_use_ssc = -1; TUNABLE_INT("drm.i915.panel_use_ssc", &i915_panel_use_ssc); int i915_panel_ignore_lid = 0; TUNABLE_INT("drm.i915.panel_ignore_lid", &i915_panel_ignore_lid); int i915_panel_invert_brightness; TUNABLE_INT("drm.i915.panel_invert_brightness", &i915_panel_invert_brightness); int i915_modeset = 1; TUNABLE_INT("drm.i915.modeset", &i915_modeset); int i915_enable_ppgtt = -1; TUNABLE_INT("drm.i915.enable_ppgtt", &i915_enable_ppgtt); int i915_enable_hangcheck = 1; TUNABLE_INT("drm.i915.enable_hangcheck", &i915_enable_hangcheck); TUNABLE_INT("drm.i915.enable_unsupported", &i915_enable_unsupported); #define PCI_VENDOR_INTEL 0x8086 #define INTEL_PCH_DEVICE_ID_MASK 0xff00 #define INTEL_PCH_IBX_DEVICE_ID_TYPE 0x3b00 #define INTEL_PCH_CPT_DEVICE_ID_TYPE 0x1c00 #define INTEL_PCH_PPT_DEVICE_ID_TYPE 0x1e00 #define INTEL_PCH_LPT_DEVICE_ID_TYPE 0x8c00 void intel_detect_pch(struct drm_device *dev) { struct drm_i915_private *dev_priv; device_t pch; uint32_t id; dev_priv = dev->dev_private; pch = pci_find_class(PCIC_BRIDGE, PCIS_BRIDGE_ISA); if (pch != NULL && pci_get_vendor(pch) == PCI_VENDOR_INTEL) { id = pci_get_device(pch) & INTEL_PCH_DEVICE_ID_MASK; if (id == INTEL_PCH_IBX_DEVICE_ID_TYPE) { dev_priv->pch_type = PCH_IBX; dev_priv->num_pch_pll = 2; DRM_DEBUG_KMS("Found Ibex Peak PCH\n"); } else if (id == INTEL_PCH_CPT_DEVICE_ID_TYPE) { dev_priv->pch_type = PCH_CPT; dev_priv->num_pch_pll = 2; DRM_DEBUG_KMS("Found CougarPoint PCH\n"); } else if (id == INTEL_PCH_PPT_DEVICE_ID_TYPE) { /* PantherPoint is CPT compatible */ dev_priv->pch_type = PCH_CPT; dev_priv->num_pch_pll = 2; DRM_DEBUG_KMS("Found PatherPoint PCH\n"); } else if (id == INTEL_PCH_LPT_DEVICE_ID_TYPE) { dev_priv->pch_type = PCH_LPT; dev_priv->num_pch_pll = 0; DRM_DEBUG_KMS("Found LynxPoint PCH\n"); } else DRM_DEBUG_KMS("No PCH detected\n"); KASSERT(dev_priv->num_pch_pll <= I915_NUM_PLLS, ("num_pch_pll %d\n", dev_priv->num_pch_pll)); } else DRM_DEBUG_KMS("No Intel PCI-ISA bridge found\n"); } bool i915_semaphore_is_enabled(struct drm_device *dev) { if (INTEL_INFO(dev)->gen < 6) return 0; if (i915_semaphores >= 0) return i915_semaphores; /* Enable semaphores on SNB when IO remapping is off */ if (INTEL_INFO(dev)->gen == 6 && intel_iommu_gfx_mapped) return false; return 1; } void __gen6_gt_force_wake_get(struct drm_i915_private *dev_priv) { int count; count = 0; while (count++ < 50 && (I915_READ_NOTRACE(FORCEWAKE_ACK) & 1)) DELAY(10); I915_WRITE_NOTRACE(FORCEWAKE, 1); POSTING_READ(FORCEWAKE); count = 0; while (count++ < 50 && (I915_READ_NOTRACE(FORCEWAKE_ACK) & 1) == 0) DELAY(10); } void __gen6_gt_force_wake_mt_get(struct drm_i915_private *dev_priv) { int count; count = 0; while (count++ < 50 && (I915_READ_NOTRACE(FORCEWAKE_MT_ACK) & 1)) DELAY(10); I915_WRITE_NOTRACE(FORCEWAKE_MT, _MASKED_BIT_ENABLE(1)); POSTING_READ(FORCEWAKE_MT); count = 0; while (count++ < 50 && (I915_READ_NOTRACE(FORCEWAKE_MT_ACK) & 1) == 0) DELAY(10); } void gen6_gt_force_wake_get(struct drm_i915_private *dev_priv) { mtx_lock(&dev_priv->gt_lock); if (dev_priv->forcewake_count++ == 0) dev_priv->display.force_wake_get(dev_priv); mtx_unlock(&dev_priv->gt_lock); } static void gen6_gt_check_fifodbg(struct drm_i915_private *dev_priv) { u32 gtfifodbg; gtfifodbg = I915_READ_NOTRACE(GTFIFODBG); if ((gtfifodbg & GT_FIFO_CPU_ERROR_MASK) != 0) { printf("MMIO read or write has been dropped %x\n", gtfifodbg); I915_WRITE_NOTRACE(GTFIFODBG, GT_FIFO_CPU_ERROR_MASK); } } void __gen6_gt_force_wake_put(struct drm_i915_private *dev_priv) { I915_WRITE_NOTRACE(FORCEWAKE, 0); /* The below doubles as a POSTING_READ */ gen6_gt_check_fifodbg(dev_priv); } void __gen6_gt_force_wake_mt_put(struct drm_i915_private *dev_priv) { I915_WRITE_NOTRACE(FORCEWAKE_MT, _MASKED_BIT_DISABLE(1)); /* The below doubles as a POSTING_READ */ gen6_gt_check_fifodbg(dev_priv); } void gen6_gt_force_wake_put(struct drm_i915_private *dev_priv) { mtx_lock(&dev_priv->gt_lock); if (--dev_priv->forcewake_count == 0) dev_priv->display.force_wake_put(dev_priv); mtx_unlock(&dev_priv->gt_lock); } int __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv) { int ret = 0; if (dev_priv->gt_fifo_count < GT_FIFO_NUM_RESERVED_ENTRIES) { int loop = 500; u32 fifo = I915_READ_NOTRACE(GT_FIFO_FREE_ENTRIES); while (fifo <= GT_FIFO_NUM_RESERVED_ENTRIES && loop--) { DELAY(10); fifo = I915_READ_NOTRACE(GT_FIFO_FREE_ENTRIES); } if (loop < 0 && fifo <= GT_FIFO_NUM_RESERVED_ENTRIES) { printf("%s loop\n", __func__); ++ret; } dev_priv->gt_fifo_count = fifo; } dev_priv->gt_fifo_count--; return (ret); } void vlv_force_wake_get(struct drm_i915_private *dev_priv) { int count; count = 0; /* Already awake? */ if ((I915_READ(0x130094) & 0xa1) == 0xa1) return; I915_WRITE_NOTRACE(FORCEWAKE_VLV, 0xffffffff); POSTING_READ(FORCEWAKE_VLV); count = 0; while (count++ < 50 && (I915_READ_NOTRACE(FORCEWAKE_ACK_VLV) & 1) == 0) DELAY(10); } void vlv_force_wake_put(struct drm_i915_private *dev_priv) { I915_WRITE_NOTRACE(FORCEWAKE_VLV, 0xffff0000); /* FIXME: confirm VLV behavior with Punit folks */ POSTING_READ(FORCEWAKE_VLV); } static int i8xx_do_reset(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; int onems; if (IS_I85X(dev)) return -ENODEV; onems = hz / 1000; if (onems == 0) onems = 1; I915_WRITE(D_STATE, I915_READ(D_STATE) | DSTATE_GFX_RESET_I830); POSTING_READ(D_STATE); if (IS_I830(dev) || IS_845G(dev)) { I915_WRITE(DEBUG_RESET_I830, DEBUG_RESET_DISPLAY | DEBUG_RESET_RENDER | DEBUG_RESET_FULL); POSTING_READ(DEBUG_RESET_I830); pause("i8xxrst1", onems); I915_WRITE(DEBUG_RESET_I830, 0); POSTING_READ(DEBUG_RESET_I830); } pause("i8xxrst2", onems); I915_WRITE(D_STATE, I915_READ(D_STATE) & ~DSTATE_GFX_RESET_I830); POSTING_READ(D_STATE); return 0; } static int i965_reset_complete(struct drm_device *dev) { u8 gdrst; gdrst = pci_read_config(dev->dev, I965_GDRST, 1); return (gdrst & GRDOM_RESET_ENABLE) == 0; } static int i965_do_reset(struct drm_device *dev) { int ret; u8 gdrst; /* * Set the domains we want to reset (GRDOM/bits 2 and 3) as * well as the reset bit (GR/bit 0). Setting the GR bit * triggers the reset; when done, the hardware will clear it. */ gdrst = pci_read_config(dev->dev, I965_GDRST, 1); pci_write_config(dev->dev, I965_GDRST, gdrst | GRDOM_RENDER | GRDOM_RESET_ENABLE, 1); ret = wait_for(i965_reset_complete(dev), 500); if (ret) return ret; /* We can't reset render&media without also resetting display ... */ gdrst = pci_read_config(dev->dev, I965_GDRST, 1); pci_write_config(dev->dev, I965_GDRST, gdrst | GRDOM_MEDIA | GRDOM_RESET_ENABLE, 1); return wait_for(i965_reset_complete(dev), 500); } static int ironlake_do_reset(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; u32 gdrst; int ret; gdrst = I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR); I915_WRITE(MCHBAR_MIRROR_BASE + ILK_GDSR, gdrst | GRDOM_RENDER | GRDOM_RESET_ENABLE); ret = wait_for(I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR) & 0x1, 500); if (ret) return ret; /* We can't reset render&media without also resetting display ... */ gdrst = I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR); I915_WRITE(MCHBAR_MIRROR_BASE + ILK_GDSR, gdrst | GRDOM_MEDIA | GRDOM_RESET_ENABLE); return wait_for(I915_READ(MCHBAR_MIRROR_BASE + ILK_GDSR) & 0x1, 500); } static int gen6_do_reset(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; int ret; /* Hold gt_lock across reset to prevent any register access * with forcewake not set correctly */ mtx_lock(&dev_priv->gt_lock); /* Reset the chip */ /* GEN6_GDRST is not in the gt power well, no need to check * for fifo space for the write or forcewake the chip for * the read */ I915_WRITE_NOTRACE(GEN6_GDRST, GEN6_GRDOM_FULL); /* Spin waiting for the device to ack the reset request */ ret = _intel_wait_for(dev, (I915_READ_NOTRACE(GEN6_GDRST) & GEN6_GRDOM_FULL) == 0, 500, 0, "915rst"); /* If reset with a user forcewake, try to restore, otherwise turn it off */ if (dev_priv->forcewake_count) dev_priv->display.force_wake_get(dev_priv); else dev_priv->display.force_wake_put(dev_priv); /* Restore fifo count */ dev_priv->gt_fifo_count = I915_READ_NOTRACE(GT_FIFO_FREE_ENTRIES); mtx_unlock(&dev_priv->gt_lock); return (ret); } int intel_gpu_reset(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; int ret = -ENODEV; switch (INTEL_INFO(dev)->gen) { case 7: case 6: ret = gen6_do_reset(dev); break; case 5: ret = ironlake_do_reset(dev); break; case 4: ret = i965_do_reset(dev); break; + case 3: case 2: ret = i8xx_do_reset(dev); break; } /* Also reset the gpu hangman. */ if (dev_priv->stop_rings) { DRM_DEBUG("Simulated gpu hang, resetting stop_rings\n"); dev_priv->stop_rings = 0; if (ret == -ENODEV) { DRM_ERROR("Reset not implemented, but ignoring " "error for simulated gpu hangs\n"); ret = 0; } } return ret; } /** * i915_reset - reset chip after a hang * @dev: drm device to reset * * Reset the chip. Useful if a hang is detected. Returns zero on successful * reset or otherwise an error code. * * Procedure is fairly simple: * - reset the chip using the reset reg * - re-init context state * - re-init hardware status page * - re-init ring buffer * - re-init interrupt state * - re-init display */ int i915_reset(struct drm_device *dev) { drm_i915_private_t *dev_priv = dev->dev_private; int ret; if (!i915_try_reset) return 0; if (!sx_try_xlock(&dev->dev_struct_lock)) return (-EBUSY); dev_priv->stop_rings = 0; i915_gem_reset(dev); ret = -ENODEV; if (time_second - dev_priv->last_gpu_reset < 5) DRM_ERROR("GPU hanging too fast, declaring wedged!\n"); else ret = intel_gpu_reset(dev); dev_priv->last_gpu_reset = time_second; if (ret) { DRM_ERROR("Failed to reset chip.\n"); DRM_UNLOCK(dev); return ret; } /* Ok, now get things going again... */ /* * Everything depends on having the GTT running, so we need to start * there. Fortunately we don't need to do this unless we reset the * chip at a PCI level. * * Next we need to restore the context, but we don't use those * yet either... * * Ring buffer needs to be re-initialized in the KMS case, or if X * was running at the time of the reset (i.e. we weren't VT * switched away). */ if (drm_core_check_feature(dev, DRIVER_MODESET) || !dev_priv->mm.suspended) { struct intel_ring_buffer *ring; int i; dev_priv->mm.suspended = 0; i915_gem_init_swizzling(dev); for_each_ring(ring, dev_priv, i) ring->init(ring); i915_gem_context_init(dev); i915_gem_init_ppgtt(dev); /* * It would make sense to re-init all the other hw state, at * least the rps/rc6/emon init done within modeset_init_hw. For * some unknown reason, this blows up my ilk, so don't. */ DRM_UNLOCK(dev); if (drm_core_check_feature(dev, DRIVER_MODESET)) intel_modeset_init_hw(dev); drm_irq_uninstall(dev); drm_irq_install(dev); } else { DRM_UNLOCK(dev); } return 0; } /* We give fast paths for the really cool registers */ #define NEEDS_FORCE_WAKE(dev_priv, reg) \ (((dev_priv)->info->gen >= 6) && \ ((reg) < 0x40000) && \ ((reg) != FORCEWAKE)) && \ (!IS_VALLEYVIEW((dev_priv)->dev)) #define __i915_read(x, y) \ u##x i915_read##x(struct drm_i915_private *dev_priv, u32 reg) { \ u##x val = 0; \ if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \ mtx_lock(&dev_priv->gt_lock); \ if (dev_priv->forcewake_count == 0) \ dev_priv->display.force_wake_get(dev_priv); \ val = DRM_READ##y(dev_priv->mmio_map, reg); \ if (dev_priv->forcewake_count == 0) \ dev_priv->display.force_wake_put(dev_priv); \ mtx_unlock(&dev_priv->gt_lock); \ } else { \ val = DRM_READ##y(dev_priv->mmio_map, reg); \ } \ trace_i915_reg_rw(false, reg, val, sizeof(val)); \ return val; \ } __i915_read(8, 8) __i915_read(16, 16) __i915_read(32, 32) __i915_read(64, 64) #undef __i915_read #define __i915_write(x, y) \ void i915_write##x(struct drm_i915_private *dev_priv, u32 reg, u##x val) { \ u32 __fifo_ret = 0; \ trace_i915_reg_rw(true, reg, val, sizeof(val)); \ if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \ __fifo_ret = __gen6_gt_wait_for_fifo(dev_priv); \ } \ DRM_WRITE##y(dev_priv->mmio_map, reg, val); \ if (__predict_false(__fifo_ret)) { \ gen6_gt_check_fifodbg(dev_priv); \ } \ } __i915_write(8, 8) __i915_write(16, 16) __i915_write(32, 32) __i915_write(64, 64) #undef __i915_write Index: head/sys/dev/drm2/i915/intel_opregion.c =================================================================== --- head/sys/dev/drm2/i915/intel_opregion.c (revision 288652) +++ head/sys/dev/drm2/i915/intel_opregion.c (revision 288653) @@ -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; }