/* drmP.h -- Private header for Direct Rendering Manager -*- linux-c -*- * Created: Mon Jan 4 10:05:05 1999 by faith@precisioninsight.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. * * Authors: * Rickard E. (Rik) Faith * Gareth Hughes * */ #ifndef _DRM_P_H_ #define _DRM_P_H_ #if defined(_KERNEL) || defined(__KERNEL__) typedef struct drm_device drm_device_t; typedef struct drm_file drm_file_t; #include #include #include #include #include #include #include #include #if __FreeBSD_version >= 700000 #include #endif #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(__FreeBSD__) #include #include #include #include #if __FreeBSD_version >= 500000 #include #include #include #else /* __FreeBSD_version >= 500000 */ #include #include #endif /* __FreeBSD_version < 500000 */ #elif defined(__NetBSD__) #include #include #include #include #include #include #include #include #include #include #include #include #elif defined(__OpenBSD__) #include #include #endif #include #include "drm.h" #include "drm_linux_list.h" #include "drm_atomic.h" #ifdef __FreeBSD__ #include #ifdef DRM_DEBUG #undef DRM_DEBUG #define DRM_DEBUG_DEFAULT_ON 1 #endif /* DRM_DEBUG */ #endif #if defined(DRM_LINUX) && DRM_LINUX && !defined(__amd64__) #include #include #include #include #else /* Either it was defined when it shouldn't be (FreeBSD amd64) or it isn't * supported on this OS yet. */ #undef DRM_LINUX #define DRM_LINUX 0 #endif #define DRM_HASH_SIZE 16 /* Size of key hash table */ #define DRM_KERNEL_CONTEXT 0 /* Change drm_resctx if changed */ #define DRM_RESERVED_CONTEXTS 1 /* Change drm_resctx if changed */ #define DRM_MEM_DMA 0 #define DRM_MEM_SAREA 1 #define DRM_MEM_DRIVER 2 #define DRM_MEM_MAGIC 3 #define DRM_MEM_IOCTLS 4 #define DRM_MEM_MAPS 5 #define DRM_MEM_BUFS 6 #define DRM_MEM_SEGS 7 #define DRM_MEM_PAGES 8 #define DRM_MEM_FILES 9 #define DRM_MEM_QUEUES 10 #define DRM_MEM_CMDS 11 #define DRM_MEM_MAPPINGS 12 #define DRM_MEM_BUFLISTS 13 #define DRM_MEM_AGPLISTS 14 #define DRM_MEM_TOTALAGP 15 #define DRM_MEM_BOUNDAGP 16 #define DRM_MEM_CTXBITMAP 17 #define DRM_MEM_STUB 18 #define DRM_MEM_SGLISTS 19 #define DRM_MEM_DRAWABLE 20 #define DRM_MAX_CTXBITMAP (PAGE_SIZE * 8) /* Internal types and structures */ #define DRM_ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) #define DRM_MIN(a,b) ((a)<(b)?(a):(b)) #define DRM_MAX(a,b) ((a)>(b)?(a):(b)) #define DRM_IF_VERSION(maj, min) (maj << 16 | min) MALLOC_DECLARE(M_DRM); #define __OS_HAS_AGP 1 #define DRM_DEV_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) #define DRM_DEV_UID 0 #define DRM_DEV_GID 0 #define wait_queue_head_t atomic_t #define DRM_WAKEUP(w) wakeup((void *)w) #define DRM_WAKEUP_INT(w) wakeup(w) #define DRM_INIT_WAITQUEUE(queue) do {(void)(queue);} while (0) #if defined(__FreeBSD__) && __FreeBSD_version < 502109 #define bus_alloc_resource_any(dev, type, rid, flags) \ bus_alloc_resource(dev, type, rid, 0ul, ~0ul, 1, flags) #endif #if defined(__FreeBSD__) && __FreeBSD_version >= 500000 #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_CURRENTPID curthread->td_proc->p_pid #define DRM_LOCK() mtx_lock(&dev->dev_lock) #define DRM_UNLOCK() mtx_unlock(&dev->dev_lock) #define DRM_SYSCTL_HANDLER_ARGS (SYSCTL_HANDLER_ARGS) #else /* __FreeBSD__ && __FreeBSD_version >= 500000 */ #define DRM_CURPROC curproc #define DRM_STRUCTPROC struct proc #define DRM_SPINTYPE struct simplelock #define DRM_SPININIT(l,name) #define DRM_SPINUNINIT(l) #define DRM_SPINLOCK(l) #define DRM_SPINUNLOCK(u) #define DRM_SPINLOCK_ASSERT(l) #define DRM_CURRENTPID curproc->p_pid #define DRM_LOCK() #define DRM_UNLOCK() #define DRM_SYSCTL_HANDLER_ARGS SYSCTL_HANDLER_ARGS #define spldrm() spltty() #endif /* __NetBSD__ || __OpenBSD__ */ #define DRM_IRQ_ARGS void *arg typedef void irqreturn_t; #define IRQ_HANDLED /* nothing */ #define IRQ_NONE /* nothing */ enum { DRM_IS_NOT_AGP, DRM_IS_AGP, DRM_MIGHT_BE_AGP }; #define DRM_AGP_MEM struct agp_memory_info #if defined(__FreeBSD__) #define drm_get_device_from_kdev(_kdev) (_kdev->si_drv1) #elif defined(__NetBSD__) #define drm_get_device_from_kdev(_kdev) device_lookup(&drm_cd, minor(_kdev)) #elif defined(__OpenBSD__) #define drm_get_device_from_kdev(_kdev) device_lookup(&drm_cd, \ minor(_kdev)))->dv_cfdata->cf_driver->cd_devs[minor(_kdev)] #endif #if defined(__FreeBSD__) #define PAGE_ALIGN(addr) round_page(addr) /* DRM_SUSER returns true if the user is superuser */ #if __FreeBSD_version >= 700000 #define DRM_SUSER(p) (priv_check(p, PRIV_DRIVER) == 0) #else #define DRM_SUSER(p) (suser(p) == 0) #endif #define DRM_AGP_FIND_DEVICE() agp_find_device() #define DRM_MTRR_WC MDF_WRITECOMBINE #define jiffies ticks #else /* __FreeBSD__ */ #define CDEV_MAJOR 34 #define PAGE_ALIGN(addr) (((addr) + PAGE_SIZE - 1) & PAGE_MASK) /* DRM_SUSER returns true if the user is superuser */ #define DRM_SUSER(p) (suser(p->p_ucred, &p->p_acflag) == 0) #define DRM_AGP_FIND_DEVICE() agp_find_device(0) #define DRM_MTRR_WC MTRR_TYPE_WC #define jiffies hardclock_ticks typedef drm_device_t *device_t; extern struct cfdriver drm_cd; #endif /* !__FreeBSD__ */ /* Capabilities taken from src/sys/dev/pci/pcireg.h. */ #ifndef PCIY_AGP #define PCIY_AGP 0x02 #endif #ifndef PCIY_EXPRESS #define PCIY_EXPRESS 0x10 #endif typedef unsigned long dma_addr_t; typedef u_int64_t u64; typedef u_int32_t u32; typedef u_int16_t u16; typedef u_int8_t u8; /* DRM_READMEMORYBARRIER() prevents reordering of reads. * DRM_WRITEMEMORYBARRIER() prevents reordering of writes. * DRM_MEMORYBARRIER() prevents reordering of reads and writes. */ #if defined(__i386__) #define DRM_READMEMORYBARRIER() __asm __volatile( \ "lock; addl $0,0(%%esp)" : : : "memory"); #define DRM_WRITEMEMORYBARRIER() __asm __volatile("" : : : "memory"); #define DRM_MEMORYBARRIER() __asm __volatile( \ "lock; addl $0,0(%%esp)" : : : "memory"); #elif defined(__alpha__) #define DRM_READMEMORYBARRIER() alpha_mb(); #define DRM_WRITEMEMORYBARRIER() alpha_wmb(); #define DRM_MEMORYBARRIER() alpha_mb(); #elif defined(__amd64__) #define DRM_READMEMORYBARRIER() __asm __volatile( \ "lock; addl $0,0(%%rsp)" : : : "memory"); #define DRM_WRITEMEMORYBARRIER() __asm __volatile("" : : : "memory"); #define DRM_MEMORYBARRIER() __asm __volatile( \ "lock; addl $0,0(%%rsp)" : : : "memory"); #endif #ifdef __FreeBSD__ #define DRM_READ8(map, offset) \ *(volatile u_int8_t *) (((unsigned long)(map)->handle) + (offset)) #define DRM_READ16(map, offset) \ *(volatile u_int16_t *) (((unsigned long)(map)->handle) + (offset)) #define DRM_READ32(map, offset) \ *(volatile u_int32_t *)(((unsigned long)(map)->handle) + (offset)) #define DRM_WRITE8(map, offset, val) \ *(volatile u_int8_t *) (((unsigned long)(map)->handle) + (offset)) = val #define DRM_WRITE16(map, offset, val) \ *(volatile u_int16_t *) (((unsigned long)(map)->handle) + (offset)) = val #define DRM_WRITE32(map, offset, val) \ *(volatile u_int32_t *)(((unsigned long)(map)->handle) + (offset)) = val #define DRM_VERIFYAREA_READ( uaddr, size ) \ (!useracc(__DECONST(caddr_t, uaddr), size, VM_PROT_READ)) #else /* __FreeBSD__ */ typedef vaddr_t vm_offset_t; #define DRM_READ8(map, offset) \ bus_space_read_1( (map)->bst, (map)->bsh, (offset)) #define DRM_READ16(map, offset) \ bus_space_read_2( (map)->bst, (map)->bsh, (offset)) #define DRM_READ32(map, offset) \ bus_space_read_4( (map)->bst, (map)->bsh, (offset)) #define DRM_WRITE8(map, offset, val) \ bus_space_write_1((map)->bst, (map)->bsh, (offset), (val)) #define DRM_WRITE16(map, offset, val) \ bus_space_write_2((map)->bst, (map)->bsh, (offset), (val)) #define DRM_WRITE32(map, offset, val) \ bus_space_write_4((map)->bst, (map)->bsh, (offset), (val)) #define DRM_VERIFYAREA_READ( uaddr, size ) \ (!uvm_useracc((caddr_t)uaddr, size, VM_PROT_READ)) #endif /* !__FreeBSD__ */ #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) #if __FreeBSD_version > 500000 #define DRM_GET_USER_UNCHECKED(val, uaddr) \ ((val) = fuword32(uaddr), 0) #else #define DRM_GET_USER_UNCHECKED(val, uaddr) \ ((val) = fuword(uaddr), 0) #endif #define cpu_to_le32(x) htole32(x) #define le32_to_cpu(x) le32toh(x) #define DRM_HZ hz #define DRM_UDELAY(udelay) DELAY(udelay) #define DRM_TIME_SLICE (hz/20) /* Time slice for GLXContexts */ #define DRM_GET_PRIV_SAREA(_dev, _ctx, _map) do { \ (_map) = (_dev)->context_sareas[_ctx]; \ } while(0) #define LOCK_TEST_WITH_RETURN(dev, file_priv) \ do { \ if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) || \ dev->lock.file_priv != file_priv) { \ DRM_ERROR("%s called without lock held\n", \ __FUNCTION__); \ return EINVAL; \ } \ } while (0) #if defined(__FreeBSD__) && __FreeBSD_version > 500000 /* Returns -errno to shared code */ #define DRM_WAIT_ON( ret, queue, timeout, condition ) \ for ( ret = 0 ; !ret && !(condition) ; ) { \ DRM_UNLOCK(); \ mtx_lock(&dev->irq_lock); \ if (!(condition)) \ ret = -msleep(&(queue), &dev->irq_lock, \ PZERO | PCATCH, "drmwtq", (timeout)); \ mtx_unlock(&dev->irq_lock); \ DRM_LOCK(); \ } #else /* Returns -errno to shared code */ #define DRM_WAIT_ON( ret, queue, timeout, condition ) \ for ( ret = 0 ; !ret && !(condition) ; ) { \ int s = spldrm(); \ if (!(condition)) \ ret = -tsleep( &(queue), PZERO | PCATCH, \ "drmwtq", (timeout) ); \ splx(s); \ } #endif #define DRM_ERROR(fmt, arg...) \ printf("error: [" DRM_NAME ":pid%d:%s] *ERROR* " fmt, \ DRM_CURRENTPID, __func__ , ## arg) #define DRM_INFO(fmt, arg...) printf("info: [" DRM_NAME "] " fmt , ## arg) #define DRM_DEBUG(fmt, arg...) do { \ if (drm_debug_flag) \ printf("[" DRM_NAME ":pid%d:%s] " fmt, DRM_CURRENTPID, \ __func__ , ## arg); \ } while (0) typedef struct drm_pci_id_list { int vendor; int device; long driver_private; char *name; } drm_pci_id_list_t; #define DRM_AUTH 0x1 #define DRM_MASTER 0x2 #define DRM_ROOT_ONLY 0x4 typedef struct drm_ioctl_desc { unsigned long cmd; int (*func)(drm_device_t *dev, void *data, struct drm_file *file_priv); int flags; } drm_ioctl_desc_t; /** * 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)] = {ioctl, func, flags} typedef struct drm_magic_entry { drm_magic_t magic; struct drm_file *priv; struct drm_magic_entry *next; } drm_magic_entry_t; typedef struct drm_magic_head { struct drm_magic_entry *head; struct drm_magic_entry *tail; } drm_magic_head_t; typedef 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 pending; /* On hardware DMA queue */ struct drm_file *file_priv; /* Unique identifier of holding process */ int context; /* Kernel queue for this buffer */ 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 stoarge */ void *dev_private; /* Per-buffer private storage */ } drm_buf_t; typedef struct drm_freelist { int initialized; /* Freelist in use */ atomic_t count; /* Number of free buffers */ drm_buf_t *next; /* End pointer */ int low_mark; /* Low water mark */ int high_mark; /* High water mark */ } drm_freelist_t; typedef struct drm_dma_handle { void *vaddr; bus_addr_t busaddr; #if defined(__FreeBSD__) bus_dma_tag_t tag; bus_dmamap_t map; #elif defined(__NetBSD__) bus_dma_segment_t seg; #endif } drm_dma_handle_t; typedef struct drm_buf_entry { int buf_size; int buf_count; drm_buf_t *buflist; int seg_count; drm_dma_handle_t **seglist; int page_order; drm_freelist_t freelist; } drm_buf_entry_t; typedef TAILQ_HEAD(drm_file_list, drm_file) drm_file_list_t; struct drm_file { TAILQ_ENTRY(drm_file) link; int authenticated; int master; int minor; pid_t pid; uid_t uid; int refs; drm_magic_t magic; unsigned long ioctl_count; void *driver_priv; }; typedef struct drm_lock_data { drm_hw_lock_t *hw_lock; /* Hardware lock */ struct drm_file *file_priv; /* Unique identifier of holding process (NULL is kernel)*/ int lock_queue; /* Queue of blocked processes */ unsigned long lock_time; /* Time of last lock in jiffies */ } drm_lock_data_t; /* This structure, in the drm_device_t, is always initialized while the device * is open. dev->dma_lock protects the incrementing of dev->buf_use, which * when set marks that no further bufs may be allocated until device teardown * occurs (when the last open of the device has closed). The high/low * watermarks of bufs are only touched by the X Server, and thus not * concurrently accessed, so no locking is needed. */ typedef struct drm_device_dma { drm_buf_entry_t bufs[DRM_MAX_ORDER+1]; int buf_count; drm_buf_t **buflist; /* Vector of pointers info bufs */ int seg_count; int page_count; unsigned long *pagelist; unsigned long byte_count; enum { _DRM_DMA_USE_AGP = 0x01, _DRM_DMA_USE_SG = 0x02 } flags; } drm_device_dma_t; typedef struct drm_agp_mem { void *handle; unsigned long bound; /* address */ int pages; struct drm_agp_mem *prev; struct drm_agp_mem *next; } drm_agp_mem_t; typedef struct drm_agp_head { device_t agpdev; struct agp_info info; const char *chipset; drm_agp_mem_t *memory; unsigned long mode; int enabled; int acquired; unsigned long base; int mtrr; int cant_use_aperture; unsigned long page_mask; } drm_agp_head_t; typedef struct drm_sg_mem { unsigned long handle; void *virtual; int pages; dma_addr_t *busaddr; drm_dma_handle_t *dmah; /* Handle to PCI memory for ATI PCIGART table */ } drm_sg_mem_t; typedef TAILQ_HEAD(drm_map_list, drm_local_map) drm_map_list_t; typedef struct drm_local_map { unsigned long offset; /* Physical address (0 for SAREA)*/ unsigned long size; /* Physical size (bytes) */ drm_map_type_t type; /* Type of memory mapped */ drm_map_flags_t flags; /* Flags */ void *handle; /* User-space: "Handle" to pass to mmap */ /* Kernel-space: kernel-virtual address */ int mtrr; /* Boolean: MTRR used */ /* Private data */ int rid; /* PCI resource ID for bus_space */ struct resource *bsr; bus_space_tag_t bst; bus_space_handle_t bsh; drm_dma_handle_t *dmah; TAILQ_ENTRY(drm_local_map) link; } drm_local_map_t; TAILQ_HEAD(drm_vbl_sig_list, drm_vbl_sig); typedef struct drm_vbl_sig { TAILQ_ENTRY(drm_vbl_sig) link; unsigned int sequence; int signo; int pid; } drm_vbl_sig_t; /* 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 typedef struct ati_pcigart_info { int gart_table_location; int gart_reg_if; void *addr; dma_addr_t bus_addr; drm_local_map_t mapping; int table_size; } drm_ati_pcigart_info; struct drm_driver_info { int (*load)(struct drm_device *, unsigned long flags); int (*firstopen)(struct drm_device *); int (*open)(struct drm_device *, drm_file_t *); void (*preclose)(struct drm_device *, struct drm_file *file_priv); void (*postclose)(struct drm_device *, drm_file_t *); void (*lastclose)(struct drm_device *); int (*unload)(struct drm_device *); void (*reclaim_buffers_locked)(struct drm_device *, struct drm_file *file_priv); int (*dma_ioctl)(drm_device_t *dev, void *data, struct drm_file *file_priv); void (*dma_ready)(struct drm_device *); int (*dma_quiescent)(struct drm_device *); int (*dma_flush_block_and_flush)(struct drm_device *, int context, drm_lock_flags_t flags); int (*dma_flush_unblock)(struct drm_device *, int context, drm_lock_flags_t flags); int (*context_ctor)(struct drm_device *dev, int context); int (*context_dtor)(struct drm_device *dev, int context); int (*kernel_context_switch)(struct drm_device *dev, int old, int new); int (*kernel_context_switch_unlock)(struct drm_device *dev); void (*irq_preinstall)(drm_device_t *dev); void (*irq_postinstall)(drm_device_t *dev); void (*irq_uninstall)(drm_device_t *dev); void (*irq_handler)(DRM_IRQ_ARGS); int (*vblank_wait)(drm_device_t *dev, unsigned int *sequence); drm_pci_id_list_t *id_entry; /* PCI ID, name, and chipset private */ /** * 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); drm_ioctl_desc_t *ioctls; int max_ioctl; int buf_priv_size; int major; int minor; int patchlevel; const char *name; /* Simple driver name */ const char *desc; /* Longer driver name */ const char *date; /* Date of last major changes. */ unsigned use_agp :1; unsigned require_agp :1; unsigned use_sg :1; unsigned use_dma :1; unsigned use_pci_dma :1; unsigned use_dma_queue :1; unsigned use_irq :1; unsigned use_vbl_irq :1; unsigned use_mtrr :1; }; /* Length for the array of resource pointers for drm_get_resource_*. */ #define DRM_MAX_PCI_RESOURCE 3 /** * DRM device functions structure */ struct drm_device { #if defined(__NetBSD__) || defined(__OpenBSD__) struct device device; /* softc is an extension of struct device */ #endif struct drm_driver_info driver; drm_pci_id_list_t *id_entry; /* PCI ID, name, and chipset private */ u_int16_t pci_device; /* PCI device id */ u_int16_t pci_vendor; /* PCI vendor id */ char *unique; /* Unique identifier: e.g., busid */ int unique_len; /* Length of unique field */ #ifdef __FreeBSD__ device_t device; /* Device instance from newbus */ #endif struct cdev *devnode; /* Device number for mknod */ int if_version; /* Highest interface version set */ int flags; /* Flags to open(2) */ /* Locks */ #if defined(__FreeBSD__) && __FreeBSD_version > 500000 struct mtx dma_lock; /* protects dev->dma */ struct mtx irq_lock; /* protects irq condition checks */ struct mtx dev_lock; /* protects everything else */ #endif DRM_SPINTYPE drw_lock; /* Usage Counters */ int open_count; /* Outstanding files open */ int buf_use; /* Buffers in use -- cannot alloc */ /* Performance counters */ unsigned long counters; drm_stat_type_t types[15]; atomic_t counts[15]; /* Authentication */ drm_file_list_t files; drm_magic_head_t magiclist[DRM_HASH_SIZE]; /* Linked list of mappable regions. Protected by dev_lock */ drm_map_list_t maplist; drm_local_map_t **context_sareas; int max_context; drm_lock_data_t lock; /* Information on hardware lock */ /* DMA queues (contexts) */ drm_device_dma_t *dma; /* Optional pointer for DMA support */ /* Context support */ int irq; /* Interrupt used by board */ int irq_enabled; /* True if the irq handler is enabled */ #ifdef __FreeBSD__ int irqrid; /* Interrupt used by board */ struct resource *irqr; /* Resource for interrupt used by board */ #elif defined(__NetBSD__) || defined(__OpenBSD__) struct pci_attach_args pa; #endif void *irqh; /* Handle from bus_setup_intr */ /* Storage of resource pointers for drm_get_resource_* */ struct resource *pcir[DRM_MAX_PCI_RESOURCE]; int pcirid[DRM_MAX_PCI_RESOURCE]; int pci_domain; int pci_bus; int pci_slot; int pci_func; atomic_t context_flag; /* Context swapping flag */ int last_context; /* Last current context */ int vbl_queue; /* vbl wait channel */ atomic_t vbl_received; atomic_t vbl_received2; #ifdef __FreeBSD__ struct sigio *buf_sigio; /* Processes waiting for SIGIO */ #elif defined(__NetBSD__) pid_t buf_pgid; #endif /* Sysctl support */ struct drm_sysctl_info *sysctl; drm_agp_head_t *agp; drm_sg_mem_t *sg; /* Scatter gather memory */ atomic_t *ctx_bitmap; void *dev_private; unsigned int agp_buffer_token; drm_local_map_t *agp_buffer_map; struct unrhdr *drw_unrhdr; /* RB tree of drawable infos */ RB_HEAD(drawable_tree, bsd_drm_drawable_info) drw_head; struct task locked_task; void (*locked_task_call)(drm_device_t *dev); }; extern int drm_debug_flag; /* Device setup support (drm_drv.c) */ #ifdef __FreeBSD__ int drm_probe(device_t nbdev, drm_pci_id_list_t *idlist); int drm_attach(device_t nbdev, drm_pci_id_list_t *idlist); int drm_detach(device_t nbdev); d_ioctl_t drm_ioctl; d_open_t drm_open; d_close_t drm_close; d_read_t drm_read; d_poll_t drm_poll; d_mmap_t drm_mmap; #elif defined(__NetBSD__) || defined(__OpenBSD__) int drm_probe(struct pci_attach_args *pa, drm_pci_id_list_t *idlist); int drm_attach(struct pci_attach_args *pa, dev_t kdev, drm_pci_id_list_t *idlist); dev_type_ioctl(drm_ioctl); dev_type_open(drm_open); dev_type_close(drm_close); dev_type_read(drm_read); dev_type_poll(drm_poll); dev_type_mmap(drm_mmap); #endif extern drm_local_map_t *drm_getsarea(drm_device_t *dev); /* File operations helpers (drm_fops.c) */ #ifdef __FreeBSD__ extern int drm_open_helper(struct cdev *kdev, int flags, int fmt, DRM_STRUCTPROC *p, drm_device_t *dev); extern drm_file_t *drm_find_file_by_proc(drm_device_t *dev, DRM_STRUCTPROC *p); #elif defined(__NetBSD__) || defined(__OpenBSD__) extern int drm_open_helper(dev_t kdev, int flags, int fmt, DRM_STRUCTPROC *p, drm_device_t *dev); extern drm_file_t *drm_find_file_by_proc(drm_device_t *dev, DRM_STRUCTPROC *p); #endif /* __NetBSD__ || __OpenBSD__ */ /* Memory management support (drm_memory.c) */ void drm_mem_init(void); void drm_mem_uninit(void); void *drm_alloc(size_t size, int area); void *drm_calloc(size_t nmemb, size_t size, int area); void *drm_realloc(void *oldpt, size_t oldsize, size_t size, int area); void drm_free(void *pt, size_t size, int area); void *drm_ioremap(drm_device_t *dev, drm_local_map_t *map); void drm_ioremapfree(drm_local_map_t *map); int drm_mtrr_add(unsigned long offset, size_t size, int flags); int drm_mtrr_del(int handle, unsigned long offset, size_t size, int flags); int drm_context_switch(drm_device_t *dev, int old, int new); int drm_context_switch_complete(drm_device_t *dev, int new); int drm_ctxbitmap_init(drm_device_t *dev); void drm_ctxbitmap_cleanup(drm_device_t *dev); void drm_ctxbitmap_free(drm_device_t *dev, int ctx_handle); int drm_ctxbitmap_next(drm_device_t *dev); /* Locking IOCTL support (drm_lock.c) */ int drm_lock_take(__volatile__ unsigned int *lock, unsigned int context); int drm_lock_transfer(drm_device_t *dev, __volatile__ unsigned int *lock, unsigned int context); int drm_lock_free(drm_device_t *dev, __volatile__ unsigned int *lock, unsigned int context); /* Buffer management support (drm_bufs.c) */ unsigned long drm_get_resource_start(drm_device_t *dev, unsigned int resource); unsigned long drm_get_resource_len(drm_device_t *dev, unsigned int resource); void drm_rmmap(drm_device_t *dev, drm_local_map_t *map); int drm_order(unsigned long size); int drm_addmap(drm_device_t * dev, unsigned long offset, unsigned long size, drm_map_type_t type, drm_map_flags_t flags, drm_local_map_t **map_ptr); int drm_addbufs_pci(drm_device_t *dev, drm_buf_desc_t *request); int drm_addbufs_sg(drm_device_t *dev, drm_buf_desc_t *request); int drm_addbufs_agp(drm_device_t *dev, drm_buf_desc_t *request); /* DMA support (drm_dma.c) */ int drm_dma_setup(drm_device_t *dev); void drm_dma_takedown(drm_device_t *dev); void drm_free_buffer(drm_device_t *dev, drm_buf_t *buf); void drm_reclaim_buffers(drm_device_t *dev, struct drm_file *file_priv); #define drm_core_reclaim_buffers drm_reclaim_buffers /* IRQ support (drm_irq.c) */ int drm_irq_install(drm_device_t *dev); int drm_irq_uninstall(drm_device_t *dev); irqreturn_t drm_irq_handler(DRM_IRQ_ARGS); void drm_driver_irq_preinstall(drm_device_t *dev); void drm_driver_irq_postinstall(drm_device_t *dev); void drm_driver_irq_uninstall(drm_device_t *dev); int drm_vblank_wait(drm_device_t *dev, unsigned int *vbl_seq); void drm_vbl_send_signals(drm_device_t *dev); /* AGP/PCI Express/GART support (drm_agpsupport.c) */ int drm_device_is_agp(drm_device_t *dev); int drm_device_is_pcie(drm_device_t *dev); drm_agp_head_t *drm_agp_init(void); int drm_agp_acquire(drm_device_t *dev); int drm_agp_release(drm_device_t *dev); int drm_agp_info(drm_device_t * dev, drm_agp_info_t *info); int drm_agp_enable(drm_device_t *dev, drm_agp_mode_t mode); 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); int drm_agp_alloc(drm_device_t *dev, drm_agp_buffer_t *request); int drm_agp_free(drm_device_t *dev, drm_agp_buffer_t *request); int drm_agp_bind(drm_device_t *dev, drm_agp_binding_t *request); int drm_agp_unbind(drm_device_t *dev, drm_agp_binding_t *request); /* Scatter Gather Support (drm_scatter.c) */ void drm_sg_cleanup(drm_sg_mem_t *entry); int drm_sg_alloc(drm_device_t * dev, drm_scatter_gather_t * request); #ifdef __FreeBSD__ /* sysctl support (drm_sysctl.h) */ extern int drm_sysctl_init(drm_device_t *dev); extern int drm_sysctl_cleanup(drm_device_t *dev); #endif /* __FreeBSD__ */ /* ATI PCIGART support (ati_pcigart.c) */ int drm_ati_pcigart_init(drm_device_t *dev, drm_ati_pcigart_info *gart_info); int drm_ati_pcigart_cleanup(drm_device_t *dev, drm_ati_pcigart_info *gart_info); /* Locking IOCTL support (drm_drv.c) */ int drm_lock(drm_device_t *dev, void *data, struct drm_file *file_priv); int drm_unlock(drm_device_t *dev, void *data, struct drm_file *file_priv); int drm_version(drm_device_t *dev, void *data, struct drm_file *file_priv); int drm_setversion(drm_device_t *dev, void *data, struct drm_file *file_priv); /* Misc. IOCTL support (drm_ioctl.c) */ int drm_irq_by_busid(drm_device_t *dev, void *data, struct drm_file *file_priv); int drm_getunique(drm_device_t *dev, void *data, struct drm_file *file_priv); int drm_setunique(drm_device_t *dev, void *data, struct drm_file *file_priv); int drm_getmap(drm_device_t *dev, void *data, struct drm_file *file_priv); int drm_getclient(drm_device_t *dev, void *data, struct drm_file *file_priv); int drm_getstats(drm_device_t *dev, void *data, struct drm_file *file_priv); int drm_noop(drm_device_t *dev, void *data, struct drm_file *file_priv); /* Context IOCTL support (drm_context.c) */ int drm_resctx(drm_device_t *dev, void *data, struct drm_file *file_priv); int drm_addctx(drm_device_t *dev, void *data, struct drm_file *file_priv); int drm_modctx(drm_device_t *dev, void *data, struct drm_file *file_priv); int drm_getctx(drm_device_t *dev, void *data, struct drm_file *file_priv); int drm_switchctx(drm_device_t *dev, void *data, struct drm_file *file_priv); int drm_newctx(drm_device_t *dev, void *data, struct drm_file *file_priv); int drm_rmctx(drm_device_t *dev, void *data, struct drm_file *file_priv); int drm_setsareactx(drm_device_t *dev, void *data, struct drm_file *file_priv); int drm_getsareactx(drm_device_t *dev, void *data, struct drm_file *file_priv); /* Drawable IOCTL support (drm_drawable.c) */ int drm_adddraw(drm_device_t *dev, void *data, struct drm_file *file_priv); int drm_rmdraw(drm_device_t *dev, void *data, struct drm_file *file_priv); int drm_update_draw(drm_device_t *dev, void *data, struct drm_file *file_priv); struct drm_drawable_info *drm_get_drawable_info(drm_device_t *dev, int handle); /* Authentication IOCTL support (drm_auth.c) */ int drm_getmagic(drm_device_t *dev, void *data, struct drm_file *file_priv); int drm_authmagic(drm_device_t *dev, void *data, struct drm_file *file_priv); /* Buffer management support (drm_bufs.c) */ int drm_addmap_ioctl(drm_device_t *dev, void *data, struct drm_file *file_priv); int drm_rmmap_ioctl(drm_device_t *dev, void *data, struct drm_file *file_priv); int drm_addbufs_ioctl(drm_device_t *dev, void *data, struct drm_file *file_priv); int drm_infobufs(drm_device_t *dev, void *data, struct drm_file *file_priv); int drm_markbufs(drm_device_t *dev, void *data, struct drm_file *file_priv); int drm_freebufs(drm_device_t *dev, void *data, struct drm_file *file_priv); int drm_mapbufs(drm_device_t *dev, void *data, struct drm_file *file_priv); /* DMA support (drm_dma.c) */ int drm_dma(drm_device_t *dev, void *data, struct drm_file *file_priv); /* IRQ support (drm_irq.c) */ int drm_control(drm_device_t *dev, void *data, struct drm_file *file_priv); int drm_wait_vblank(drm_device_t *dev, void *data, struct drm_file *file_priv); void drm_locked_tasklet(drm_device_t *dev, void (*tasklet)(drm_device_t *dev)); /* AGP/GART support (drm_agpsupport.c) */ int drm_agp_acquire_ioctl(drm_device_t *dev, void *data, struct drm_file *file_priv); int drm_agp_release_ioctl(drm_device_t *dev, void *data, struct drm_file *file_priv); int drm_agp_enable_ioctl(drm_device_t *dev, void *data, struct drm_file *file_priv); int drm_agp_info_ioctl(drm_device_t *dev, void *data, struct drm_file *file_priv); int drm_agp_alloc_ioctl(drm_device_t *dev, void *data, struct drm_file *file_priv); int drm_agp_free_ioctl(drm_device_t *dev, void *data, struct drm_file *file_priv); int drm_agp_unbind_ioctl(drm_device_t *dev, void *data, struct drm_file *file_priv); int drm_agp_bind_ioctl(drm_device_t *dev, void *data, struct drm_file *file_priv); /* Scatter Gather Support (drm_scatter.c) */ int drm_sg_alloc_ioctl(drm_device_t *dev, void *data, struct drm_file *file_priv); int drm_sg_free(drm_device_t *dev, void *data, struct drm_file *file_priv); /* consistent PCI memory functions (drm_pci.c) */ drm_dma_handle_t *drm_pci_alloc(drm_device_t *dev, size_t size, size_t align, dma_addr_t maxaddr); void drm_pci_free(drm_device_t *dev, drm_dma_handle_t *dmah); /* Inline replacements for DRM_IOREMAP macros */ static __inline__ void drm_core_ioremap(struct drm_local_map *map, struct drm_device *dev) { map->handle = drm_ioremap(dev, map); } static __inline__ void drm_core_ioremapfree(struct drm_local_map *map, struct drm_device *dev) { if ( map->handle && map->size ) drm_ioremapfree(map); } static __inline__ struct drm_local_map *drm_core_findmap(struct drm_device *dev, unsigned long offset) { drm_local_map_t *map; DRM_SPINLOCK_ASSERT(&dev->dev_lock); TAILQ_FOREACH(map, &dev->maplist, link) { if (map->offset == offset) return map; } return NULL; } static __inline__ void drm_core_dropmap(struct drm_map *map) { } #endif /* __KERNEL__ */ #endif /* _DRM_P_H_ */ n940' href='#n940'>940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636
/*
 * Copyright © 2006-2007 Intel Corporation
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 * Authors:
 *	Eric Anholt <eric@anholt.net>
 */

#include <linux/i2c.h>
#include "drmP.h"
#include "intel_drv.h"
#include "i915_drm.h"
#include "i915_drv.h"

#include "drm_crtc_helper.h"

bool intel_pipe_has_type (struct drm_crtc *crtc, int type);

typedef struct {
    /* given values */    
    int n;
    int m1, m2;
    int p1, p2;
    /* derived values */
    int	dot;
    int	vco;
    int	m;
    int	p;
} intel_clock_t;

typedef struct {
    int	min, max;
} intel_range_t;

typedef struct {
    int	dot_limit;
    int	p2_slow, p2_fast;
} intel_p2_t;

#define INTEL_P2_NUM		      2

typedef struct {
    intel_range_t   dot, vco, n, m, m1, m2, p, p1;
    intel_p2_t	    p2;
} intel_limit_t;

#define I8XX_DOT_MIN		  25000
#define I8XX_DOT_MAX		 350000
#define I8XX_VCO_MIN		 930000
#define I8XX_VCO_MAX		1400000
#define I8XX_N_MIN		      3
#define I8XX_N_MAX		     16
#define I8XX_M_MIN		     96
#define I8XX_M_MAX		    140
#define I8XX_M1_MIN		     18
#define I8XX_M1_MAX		     26
#define I8XX_M2_MIN		      6
#define I8XX_M2_MAX		     16
#define I8XX_P_MIN		      4
#define I8XX_P_MAX		    128
#define I8XX_P1_MIN		      2
#define I8XX_P1_MAX		     33
#define I8XX_P1_LVDS_MIN	      1
#define I8XX_P1_LVDS_MAX	      6
#define I8XX_P2_SLOW		      4
#define I8XX_P2_FAST		      2
#define I8XX_P2_LVDS_SLOW	      14
#define I8XX_P2_LVDS_FAST	      14 /* No fast option */
#define I8XX_P2_SLOW_LIMIT	 165000

#define I9XX_DOT_MIN		  20000
#define I9XX_DOT_MAX		 400000
#define I9XX_VCO_MIN		1400000
#define I9XX_VCO_MAX		2800000
#define I9XX_N_MIN		      3
#define I9XX_N_MAX		      8
#define I9XX_M_MIN		     70
#define I9XX_M_MAX		    120
#define I9XX_M1_MIN		     10
#define I9XX_M1_MAX		     20
#define I9XX_M2_MIN		      5
#define I9XX_M2_MAX		      9
#define I9XX_P_SDVO_DAC_MIN	      5
#define I9XX_P_SDVO_DAC_MAX	     80
#define I9XX_P_LVDS_MIN		      7
#define I9XX_P_LVDS_MAX		     98
#define I9XX_P1_MIN		      1
#define I9XX_P1_MAX		      8
#define I9XX_P2_SDVO_DAC_SLOW		     10
#define I9XX_P2_SDVO_DAC_FAST		      5
#define I9XX_P2_SDVO_DAC_SLOW_LIMIT	 200000
#define I9XX_P2_LVDS_SLOW		     14
#define I9XX_P2_LVDS_FAST		      7
#define I9XX_P2_LVDS_SLOW_LIMIT		 112000

#define INTEL_LIMIT_I8XX_DVO_DAC    0
#define INTEL_LIMIT_I8XX_LVDS	    1
#define INTEL_LIMIT_I9XX_SDVO_DAC   2
#define INTEL_LIMIT_I9XX_LVDS	    3

static const intel_limit_t intel_limits[] = {
    { /* INTEL_LIMIT_I8XX_DVO_DAC */
        .dot = { .min = I8XX_DOT_MIN,		.max = I8XX_DOT_MAX },
        .vco = { .min = I8XX_VCO_MIN,		.max = I8XX_VCO_MAX },
        .n   = { .min = I8XX_N_MIN,		.max = I8XX_N_MAX },
        .m   = { .min = I8XX_M_MIN,		.max = I8XX_M_MAX },
        .m1  = { .min = I8XX_M1_MIN,		.max = I8XX_M1_MAX },
        .m2  = { .min = I8XX_M2_MIN,		.max = I8XX_M2_MAX },
        .p   = { .min = I8XX_P_MIN,		.max = I8XX_P_MAX },
        .p1  = { .min = I8XX_P1_MIN,		.max = I8XX_P1_MAX },
	.p2  = { .dot_limit = I8XX_P2_SLOW_LIMIT,
		 .p2_slow = I8XX_P2_SLOW,	.p2_fast = I8XX_P2_FAST },
    },
    { /* INTEL_LIMIT_I8XX_LVDS */
        .dot = { .min = I8XX_DOT_MIN,		.max = I8XX_DOT_MAX },
        .vco = { .min = I8XX_VCO_MIN,		.max = I8XX_VCO_MAX },
        .n   = { .min = I8XX_N_MIN,		.max = I8XX_N_MAX },
        .m   = { .min = I8XX_M_MIN,		.max = I8XX_M_MAX },
        .m1  = { .min = I8XX_M1_MIN,		.max = I8XX_M1_MAX },
        .m2  = { .min = I8XX_M2_MIN,		.max = I8XX_M2_MAX },
        .p   = { .min = I8XX_P_MIN,		.max = I8XX_P_MAX },
        .p1  = { .min = I8XX_P1_LVDS_MIN,	.max = I8XX_P1_LVDS_MAX },
	.p2  = { .dot_limit = I8XX_P2_SLOW_LIMIT,
		 .p2_slow = I8XX_P2_LVDS_SLOW,	.p2_fast = I8XX_P2_LVDS_FAST },
    },
    { /* INTEL_LIMIT_I9XX_SDVO_DAC */
        .dot = { .min = I9XX_DOT_MIN,		.max = I9XX_DOT_MAX },
        .vco = { .min = I9XX_VCO_MIN,		.max = I9XX_VCO_MAX },
        .n   = { .min = I9XX_N_MIN,		.max = I9XX_N_MAX },
        .m   = { .min = I9XX_M_MIN,		.max = I9XX_M_MAX },
        .m1  = { .min = I9XX_M1_MIN,		.max = I9XX_M1_MAX },
        .m2  = { .min = I9XX_M2_MIN,		.max = I9XX_M2_MAX },
        .p   = { .min = I9XX_P_SDVO_DAC_MIN,	.max = I9XX_P_SDVO_DAC_MAX },
        .p1  = { .min = I9XX_P1_MIN,		.max = I9XX_P1_MAX },
	.p2  = { .dot_limit = I9XX_P2_SDVO_DAC_SLOW_LIMIT,
		 .p2_slow = I9XX_P2_SDVO_DAC_SLOW,	.p2_fast = I9XX_P2_SDVO_DAC_FAST },
    },
    { /* INTEL_LIMIT_I9XX_LVDS */
        .dot = { .min = I9XX_DOT_MIN,		.max = I9XX_DOT_MAX },
        .vco = { .min = I9XX_VCO_MIN,		.max = I9XX_VCO_MAX },
        .n   = { .min = I9XX_N_MIN,		.max = I9XX_N_MAX },
        .m   = { .min = I9XX_M_MIN,		.max = I9XX_M_MAX },
        .m1  = { .min = I9XX_M1_MIN,		.max = I9XX_M1_MAX },
        .m2  = { .min = I9XX_M2_MIN,		.max = I9XX_M2_MAX },
        .p   = { .min = I9XX_P_LVDS_MIN,	.max = I9XX_P_LVDS_MAX },
        .p1  = { .min = I9XX_P1_MIN,		.max = I9XX_P1_MAX },
	/* The single-channel range is 25-112Mhz, and dual-channel
	 * is 80-224Mhz.  Prefer single channel as much as possible.
	 */
	.p2  = { .dot_limit = I9XX_P2_LVDS_SLOW_LIMIT,
		 .p2_slow = I9XX_P2_LVDS_SLOW,	.p2_fast = I9XX_P2_LVDS_FAST },
    },
};

static const intel_limit_t *intel_limit(struct drm_crtc *crtc)
{
	struct drm_device *dev = crtc->dev;
	const intel_limit_t *limit;
	
	if (IS_I9XX(dev)) {
		if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
			limit = &intel_limits[INTEL_LIMIT_I9XX_LVDS];
		else
			limit = &intel_limits[INTEL_LIMIT_I9XX_SDVO_DAC];
	} else {
		if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
			limit = &intel_limits[INTEL_LIMIT_I8XX_LVDS];
		else
			limit = &intel_limits[INTEL_LIMIT_I8XX_DVO_DAC];
	}
	return limit;
}

/** Derive the pixel clock for the given refclk and divisors for 8xx chips. */

static void i8xx_clock(int refclk, intel_clock_t *clock)
{
	clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2);
	clock->p = clock->p1 * clock->p2;
	clock->vco = refclk * clock->m / (clock->n + 2);
	clock->dot = clock->vco / clock->p;
}

/** Derive the pixel clock for the given refclk and divisors for 9xx chips. */

static void i9xx_clock(int refclk, intel_clock_t *clock)
{
	clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2);
	clock->p = clock->p1 * clock->p2;
	clock->vco = refclk * clock->m / (clock->n + 2);
	clock->dot = clock->vco / clock->p;
}

static void intel_clock(struct drm_device *dev, int refclk,
			intel_clock_t *clock)
{
	if (IS_I9XX(dev))
		return i9xx_clock (refclk, clock);
	else
		return i8xx_clock (refclk, clock);
}

/**
 * Returns whether any output on the specified pipe is of the specified type
 */
bool intel_pipe_has_type (struct drm_crtc *crtc, int type)
{
    struct drm_device *dev = crtc->dev;
    struct drm_mode_config *mode_config = &dev->mode_config;
    struct drm_connector *l_entry;

    list_for_each_entry(l_entry, &mode_config->connector_list, head) {
	    if (l_entry->encoder &&
	        l_entry->encoder->crtc == crtc) {
		    struct intel_output *intel_output = to_intel_output(l_entry);
		    if (intel_output->type == type)
			    return true;
	    }
    }
    return false;
}

#define INTELPllInvalid(s)   { /* ErrorF (s) */; return false; }
/**
 * Returns whether the given set of divisors are valid for a given refclk with
 * the given connectors.
 */

static bool intel_PLL_is_valid(struct drm_crtc *crtc, intel_clock_t *clock)
{
	const intel_limit_t *limit = intel_limit (crtc);
	
	if (clock->p1  < limit->p1.min  || limit->p1.max  < clock->p1)
		INTELPllInvalid ("p1 out of range\n");
	if (clock->p   < limit->p.min   || limit->p.max   < clock->p)
		INTELPllInvalid ("p out of range\n");
	if (clock->m2  < limit->m2.min  || limit->m2.max  < clock->m2)
		INTELPllInvalid ("m2 out of range\n");
	if (clock->m1  < limit->m1.min  || limit->m1.max  < clock->m1)
		INTELPllInvalid ("m1 out of range\n");
	if (clock->m1 <= clock->m2)
		INTELPllInvalid ("m1 <= m2\n");
	if (clock->m   < limit->m.min   || limit->m.max   < clock->m)
		INTELPllInvalid ("m out of range\n");
	if (clock->n   < limit->n.min   || limit->n.max   < clock->n)
		INTELPllInvalid ("n out of range\n");
	if (clock->vco < limit->vco.min || limit->vco.max < clock->vco)
		INTELPllInvalid ("vco out of range\n");
	/* XXX: We may need to be checking "Dot clock" depending on the multiplier,
	 * connector, etc., rather than just a single range.
	 */
	if (clock->dot < limit->dot.min || limit->dot.max < clock->dot)
		INTELPllInvalid ("dot out of range\n");
	
	return true;
}

/**
 * Returns a set of divisors for the desired target clock with the given
 * refclk, or FALSE.  The returned values represent the clock equation:
 * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2.
 */
static bool intel_find_best_PLL(struct drm_crtc *crtc, int target,
				int refclk, intel_clock_t *best_clock)
{
	struct drm_device *dev = crtc->dev;
	struct drm_i915_private *dev_priv = dev->dev_private;
	intel_clock_t clock;
	const intel_limit_t *limit = intel_limit(crtc);
	int err = target;

	if (IS_I9XX(dev) && intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) &&
	    (I915_READ(LVDS) & LVDS_PORT_EN) != 0) {
		/*
		 * For LVDS, if the panel is on, just rely on its current
		 * settings for dual-channel.  We haven't figured out how to
		 * reliably set up different single/dual channel state, if we
		 * even can.
		 */
		if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) ==
		    LVDS_CLKB_POWER_UP)
			clock.p2 = limit->p2.p2_fast;
		else
			clock.p2 = limit->p2.p2_slow;
	} else {
		if (target < limit->p2.dot_limit)
			clock.p2 = limit->p2.p2_slow;
		else
			clock.p2 = limit->p2.p2_fast;
	}
	
	memset (best_clock, 0, sizeof (*best_clock));
	
	for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; clock.m1++) {
		for (clock.m2 = limit->m2.min; clock.m2 < clock.m1 &&
			     clock.m2 <= limit->m2.max; clock.m2++) {
			for (clock.n = limit->n.min; clock.n <= limit->n.max;
			     clock.n++) {
				for (clock.p1 = limit->p1.min;
				     clock.p1 <= limit->p1.max; clock.p1++) {
					int this_err;
					
					intel_clock(dev, refclk, &clock);
					
					if (!intel_PLL_is_valid(crtc, &clock))
						continue;
					
					this_err = abs(clock.dot - target);
					if (this_err < err) {
						*best_clock = clock;
						err = this_err;
					}
				}
			}
		}
	}

	return (err != target);
}

void
intel_set_vblank(struct drm_device *dev)
{
	struct drm_i915_private *dev_priv = dev->dev_private;
	struct drm_crtc *crtc;
	struct intel_crtc *intel_crtc;
	int vbl_pipe = 0;

	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
		intel_crtc = to_intel_crtc(crtc);

		if (crtc->enabled)
			vbl_pipe |= (1<<intel_crtc->pipe);
	}

	dev_priv->vblank_pipe = vbl_pipe;
	i915_enable_interrupt(dev);
}
void
intel_wait_for_vblank(struct drm_device *dev)
{
	/* Wait for 20ms, i.e. one cycle at 50hz. */
	udelay(20000);
}

void
intel_pipe_set_base(struct drm_crtc *crtc, int x, int y)
{
	struct drm_device *dev = crtc->dev;
	struct drm_i915_private *dev_priv = dev->dev_private;
	struct drm_i915_master_private *master_priv;
	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
	struct intel_framebuffer *intel_fb;
	struct drm_i915_gem_object *obj_priv;
	struct drm_gem_object *obj;
	int pipe = intel_crtc->pipe;
	unsigned long Start, Offset;
	int dspbase = (pipe == 0 ? DSPAADDR : DSPBADDR);
	int dspsurf = (pipe == 0 ? DSPASURF : DSPBSURF);
	int dspstride = (pipe == 0) ? DSPASTRIDE : DSPBSTRIDE;
	int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR;
	u32 dspcntr;

	/* no fb bound */
	if (!crtc->fb) {
		DRM_DEBUG("No FB bound\n");
		return;
	}

	intel_fb = to_intel_framebuffer(crtc->fb);

	obj = intel_fb->base.mm_private;
	obj_priv = obj->driver_private;

	Start = obj_priv->gtt_offset;
	Offset = y * crtc->fb->pitch + x * (crtc->fb->bits_per_pixel / 8);

	I915_WRITE(dspstride, crtc->fb->pitch);

	dspcntr = I915_READ(dspcntr_reg);
	switch (crtc->fb->bits_per_pixel) {
	case 8:
		dspcntr |= DISPPLANE_8BPP;
		break;
	case 16:
		if (crtc->fb->depth == 15)
			dspcntr |= DISPPLANE_15_16BPP;
		else
			dspcntr |= DISPPLANE_16BPP;
		break;
	case 24:
	case 32:
		dspcntr |= DISPPLANE_32BPP_NO_ALPHA;
		break;
	default:
		DRM_ERROR("Unknown color depth\n");
		return;
	}
	I915_WRITE(dspcntr_reg, dspcntr);

	DRM_DEBUG("Writing base %08lX %08lX %d %d\n", Start, Offset, x, y);
	if (IS_I965G(dev)) {
		I915_WRITE(dspbase, Offset);
		I915_READ(dspbase);
		I915_WRITE(dspsurf, Start);
		I915_READ(dspsurf);
	} else {
		I915_WRITE(dspbase, Start + Offset);
		I915_READ(dspbase);
	}
	

	if (!dev->primary->master)
		return;

	master_priv = dev->primary->master->driver_priv;
	if (!master_priv->sarea_priv) 
		return;
		
	switch (pipe) {
	case 0:
		master_priv->sarea_priv->planeA_x = x;
		master_priv->sarea_priv->planeA_y = y;
		break;
	case 1:
		master_priv->sarea_priv->planeB_x = x;
		master_priv->sarea_priv->planeB_y = y;
		break;
	default:
		DRM_ERROR("Can't update pipe %d in SAREA\n", pipe);
		break;
	}
}



/**
 * Sets the power management mode of the pipe and plane.
 *
 * This code should probably grow support for turning the cursor off and back
 * on appropriately at the same time as we're turning the pipe off/on.
 */
static void intel_crtc_dpms(struct drm_crtc *crtc, int mode)
{
	struct drm_device *dev = crtc->dev;
	struct drm_i915_master_private *master_priv;
	struct drm_i915_private *dev_priv = dev->dev_private;
	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
	int pipe = intel_crtc->pipe;
	int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B;
	int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR;
	int dspbase_reg = (pipe == 0) ? DSPAADDR : DSPBADDR;
	int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
	u32 temp;
	bool enabled;

	/* XXX: When our outputs are all unaware of DPMS modes other than off
	 * and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC.
	 */
	switch (mode) {
	case DRM_MODE_DPMS_ON:
	case DRM_MODE_DPMS_STANDBY:
	case DRM_MODE_DPMS_SUSPEND:
		/* Enable the DPLL */
		temp = I915_READ(dpll_reg);
		if ((temp & DPLL_VCO_ENABLE) == 0) {
			I915_WRITE(dpll_reg, temp);
			I915_READ(dpll_reg);
			/* Wait for the clocks to stabilize. */
			udelay(150);
			I915_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE);
			I915_READ(dpll_reg);
			/* Wait for the clocks to stabilize. */
			udelay(150);
			I915_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE);
			I915_READ(dpll_reg);
			/* Wait for the clocks to stabilize. */
			udelay(150);
		}
		
		/* Enable the pipe */
		temp = I915_READ(pipeconf_reg);
		if ((temp & PIPEACONF_ENABLE) == 0)
			I915_WRITE(pipeconf_reg, temp | PIPEACONF_ENABLE);
		
		/* Enable the plane */
		temp = I915_READ(dspcntr_reg);
		if ((temp & DISPLAY_PLANE_ENABLE) == 0) {
			I915_WRITE(dspcntr_reg, temp | DISPLAY_PLANE_ENABLE);
			/* Flush the plane changes */
			I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
		}
		
		intel_crtc_load_lut(crtc);
		
		/* Give the overlay scaler a chance to enable if it's on this pipe */
		//intel_crtc_dpms_video(crtc, true); TODO
	break;
	case DRM_MODE_DPMS_OFF:
		/* Give the overlay scaler a chance to disable if it's on this pipe */
		//intel_crtc_dpms_video(crtc, FALSE); TODO
		
		/* Disable the VGA plane that we never use */
		I915_WRITE(VGACNTRL, VGA_DISP_DISABLE);
		
		/* Disable display plane */
		temp = I915_READ(dspcntr_reg);
		if ((temp & DISPLAY_PLANE_ENABLE) != 0) {
			I915_WRITE(dspcntr_reg, temp & ~DISPLAY_PLANE_ENABLE);
			/* Flush the plane changes */
			I915_WRITE(dspbase_reg, I915_READ(dspbase_reg));
			I915_READ(dspbase_reg);
		}
		
		if (!IS_I9XX(dev)) {
			/* Wait for vblank for the disable to take effect */
			intel_wait_for_vblank(dev);
		}
		
		/* Next, disable display pipes */
		temp = I915_READ(pipeconf_reg);
		if ((temp & PIPEACONF_ENABLE) != 0) {
			I915_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE);
			I915_READ(pipeconf_reg);
		}
		
		/* Wait for vblank for the disable to take effect. */
		intel_wait_for_vblank(dev);
		
		temp = I915_READ(dpll_reg);
		if ((temp & DPLL_VCO_ENABLE) != 0) {
			I915_WRITE(dpll_reg, temp & ~DPLL_VCO_ENABLE);
			I915_READ(dpll_reg);
		}
		
		/* Wait for the clocks to turn off. */
		udelay(150);
		break;
	}

	if (!dev->primary->master)
		return;	

	master_priv = dev->primary->master->driver_priv;
	if (!master_priv->sarea_priv)
		return;

	enabled = crtc->enabled && mode != DRM_MODE_DPMS_OFF;
	
	switch (pipe) {
	case 0:
		master_priv->sarea_priv->planeA_w = enabled ? crtc->mode.hdisplay : 0;
		master_priv->sarea_priv->planeA_h = enabled ? crtc->mode.vdisplay : 0;
		break;
	case 1:
		master_priv->sarea_priv->planeB_w = enabled ? crtc->mode.hdisplay : 0;
		master_priv->sarea_priv->planeB_h = enabled ? crtc->mode.vdisplay : 0;
		break;
	default:
		DRM_ERROR("Can't update pipe %d in SAREA\n", pipe);
		break;
	}

	intel_crtc->dpms_mode = mode;
}

static void intel_crtc_prepare (struct drm_crtc *crtc)
{
	struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
	crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
}

static void intel_crtc_commit (struct drm_crtc *crtc)
{
	struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
	crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
}

void intel_encoder_prepare (struct drm_encoder *encoder)
{
	struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
	/* lvds has its own version of prepare see intel_lvds_prepare */
	encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
}

void intel_encoder_commit (struct drm_encoder *encoder)
{
	struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
	/* lvds has its own version of commit see intel_lvds_commit */
	encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
}

static bool intel_crtc_mode_fixup(struct drm_crtc *crtc,
				  struct drm_display_mode *mode,
				  struct drm_display_mode *adjusted_mode)
{
	return true;
}


/** Returns the core display clock speed for i830 - i945 */
static int intel_get_core_clock_speed(struct drm_device *dev)
{

	/* Core clock values taken from the published datasheets.
	 * The 830 may go up to 166 Mhz, which we should check.
	 */
	if (IS_I945G(dev))
		return 400000;
	else if (IS_I915G(dev))
		return 333000;
	else if (IS_I945GM(dev) || IS_845G(dev))
		return 200000;
	else if (IS_I915GM(dev)) {
		u16 gcfgc = 0;

		pci_read_config_word(dev->pdev, GCFGC, &gcfgc);
		
		if (gcfgc & GC_LOW_FREQUENCY_ENABLE)
			return 133000;
		else {
			switch (gcfgc & GC_DISPLAY_CLOCK_MASK) {
			case GC_DISPLAY_CLOCK_333_MHZ:
				return 333000;
			default:
			case GC_DISPLAY_CLOCK_190_200_MHZ:
				return 190000;
			}
		}
	} else if (IS_I865G(dev))
		return 266000;
	else if (IS_I855(dev)) {
#if 0
		PCITAG bridge = pciTag(0, 0, 0); /* This is always the host bridge */
		u16 hpllcc = pciReadWord(bridge, HPLLCC);
		
#endif
		u16 hpllcc = 0;
		/* Assume that the hardware is in the high speed state.  This
		 * should be the default.
		 */
		switch (hpllcc & GC_CLOCK_CONTROL_MASK) {
		case GC_CLOCK_133_200:
		case GC_CLOCK_100_200:
			return 200000;
		case GC_CLOCK_166_250:
			return 250000;
		case GC_CLOCK_100_133:
			return 133000;
		}
	} else /* 852, 830 */
		return 133000;
	
	return 0; /* Silence gcc warning */
}


/**
 * Return the pipe currently connected to the panel fitter,
 * or -1 if the panel fitter is not present or not in use
 */
static int intel_panel_fitter_pipe (struct drm_device *dev)
{
	struct drm_i915_private *dev_priv = dev->dev_private;
	u32  pfit_control;
    
	/* i830 doesn't have a panel fitter */
	if (IS_I830(dev))
		return -1;
    
	pfit_control = I915_READ(PFIT_CONTROL);
    
	/* See if the panel fitter is in use */
	if ((pfit_control & PFIT_ENABLE) == 0)
		return -1;
	
	/* 965 can place panel fitter on either pipe */
	if (IS_I965G(dev))
		return (pfit_control >> 29) & 0x3;
	
	/* older chips can only use pipe 1 */
	return 1;
}

static void intel_crtc_mode_set(struct drm_crtc *crtc,
				struct drm_display_mode *mode,
				struct drm_display_mode *adjusted_mode,
				int x, int y)
{
	struct drm_device *dev = crtc->dev;
	struct drm_i915_private *dev_priv = dev->dev_private;
	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
	int pipe = intel_crtc->pipe;
	int fp_reg = (pipe == 0) ? FPA0 : FPB0;
	int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B;
	int dpll_md_reg = (intel_crtc->pipe == 0) ? DPLL_A_MD : DPLL_B_MD;
	int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR;
	int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
	int htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B;
	int hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B;
	int hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B;
	int vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B;
	int vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B;
	int vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B;
	int dspsize_reg = (pipe == 0) ? DSPASIZE : DSPBSIZE;
	int dsppos_reg = (pipe == 0) ? DSPAPOS : DSPBPOS;
	int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC;
	int refclk;
	intel_clock_t clock;
	u32 dpll = 0, fp = 0, dspcntr, pipeconf;
	bool ok, is_sdvo = false, is_dvo = false;
	bool is_crt = false, is_lvds = false, is_tv = false;
	struct drm_mode_config *mode_config = &dev->mode_config;
	struct drm_connector *connector;

	list_for_each_entry(connector, &mode_config->connector_list, head) {
		struct intel_output *intel_output = to_intel_output(connector);

		if (!connector->encoder || connector->encoder->crtc != crtc)
			continue;

		switch (intel_output->type) {
		case INTEL_OUTPUT_LVDS:
			is_lvds = true;
			break;
		case INTEL_OUTPUT_SDVO:
			is_sdvo = true;
			break;
		case INTEL_OUTPUT_DVO:
			is_dvo = true;
			break;
		case INTEL_OUTPUT_TVOUT:
			is_tv = true;
			break;
		case INTEL_OUTPUT_ANALOG:
			is_crt = true;
			break;
		}
	}
	
	if (IS_I9XX(dev)) {
		refclk = 96000;
	} else {
		refclk = 48000;
	}

	ok = intel_find_best_PLL(crtc, adjusted_mode->clock, refclk, &clock);
	if (!ok) {
		DRM_ERROR("Couldn't find PLL settings for mode!\n");
		return;
	}

	fp = clock.n << 16 | clock.m1 << 8 | clock.m2;
	
	dpll = DPLL_VGA_MODE_DIS;
	if (IS_I9XX(dev)) {
		if (is_lvds)
			dpll |= DPLLB_MODE_LVDS;
		else
			dpll |= DPLLB_MODE_DAC_SERIAL;
		if (is_sdvo) {
			dpll |= DPLL_DVO_HIGH_SPEED;
			if (IS_I945G(dev) || IS_I945GM(dev)) {
				int sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
				dpll |= (sdvo_pixel_multiply - 1) << SDVO_MULTIPLIER_SHIFT_HIRES;
			}
		}
		
		/* compute bitmask from p1 value */
		dpll |= (1 << (clock.p1 - 1)) << 16;
		switch (clock.p2) {
		case 5:
			dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_5;
			break;
		case 7:
			dpll |= DPLLB_LVDS_P2_CLOCK_DIV_7;
			break;
		case 10:
			dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_10;
			break;
		case 14:
			dpll |= DPLLB_LVDS_P2_CLOCK_DIV_14;
			break;
		}
		if (IS_I965G(dev))
			dpll |= (6 << PLL_LOAD_PULSE_PHASE_SHIFT);
	} else {
		if (is_lvds) {
			dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT;
		} else {
			if (clock.p1 == 2)
				dpll |= PLL_P1_DIVIDE_BY_TWO;
			else
				dpll |= (clock.p1 - 2) << DPLL_FPA01_P1_POST_DIV_SHIFT;
			if (clock.p2 == 4)
				dpll |= PLL_P2_DIVIDE_BY_4;
		}
	}
	
	if (is_tv) {
		/* XXX: just matching BIOS for now */
/*	dpll |= PLL_REF_INPUT_TVCLKINBC; */
		dpll |= 3;
	}
#if 0
	else if (is_lvds)
		dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN;
#endif
	else
		dpll |= PLL_REF_INPUT_DREFCLK;
	
	/* setup pipeconf */
	pipeconf = I915_READ(pipeconf_reg);

	/* Set up the display plane register */
	dspcntr = DISPPLANE_GAMMA_ENABLE;

	if (pipe == 0)
		dspcntr |= DISPPLANE_SEL_PIPE_A;
	else
		dspcntr |= DISPPLANE_SEL_PIPE_B;
	
	if (pipe == 0 && !IS_I965G(dev)) {
		/* Enable pixel doubling when the dot clock is > 90% of the (display)
		 * core speed.
		 *
		 * XXX: No double-wide on 915GM pipe B. Is that the only reason for the
		 * pipe == 0 check?
		 */
		if (mode->clock > intel_get_core_clock_speed(dev) * 9 / 10)
			pipeconf |= PIPEACONF_DOUBLE_WIDE;
		else
			pipeconf &= ~PIPEACONF_DOUBLE_WIDE;
	}

	dspcntr |= DISPLAY_PLANE_ENABLE;
	pipeconf |= PIPEACONF_ENABLE;
	dpll |= DPLL_VCO_ENABLE;

	
	/* Disable the panel fitter if it was on our pipe */
	if (intel_panel_fitter_pipe(dev) == pipe)
		I915_WRITE(PFIT_CONTROL, 0);

	DRM_DEBUG("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B');
	drm_mode_debug_printmodeline(mode);
	
#if 0
	if (!xf86ModesEqual(mode, adjusted_mode)) {
		xf86DrvMsg(pScrn->scrnIndex, X_INFO,
			   "Adjusted mode for pipe %c:\n", pipe == 0 ? 'A' : 'B');
		xf86PrintModeline(pScrn->scrnIndex, mode);
	}
	i830PrintPll("chosen", &clock);
#endif

	if (dpll & DPLL_VCO_ENABLE) {
		I915_WRITE(fp_reg, fp);
		I915_WRITE(dpll_reg, dpll & ~DPLL_VCO_ENABLE);
		I915_READ(dpll_reg);
		udelay(150);
	}
	
	/* The LVDS pin pair needs to be on before the DPLLs are enabled.
	 * This is an exception to the general rule that mode_set doesn't turn
	 * things on.
	 */
	if (is_lvds) {
		u32 lvds = I915_READ(LVDS);
		
		lvds |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP | LVDS_PIPEB_SELECT;
		/* Set the B0-B3 data pairs corresponding to whether we're going to
		 * set the DPLLs for dual-channel mode or not.
		 */
		if (clock.p2 == 7)
			lvds |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP;
		else
			lvds &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP);
		
		/* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP)
		 * appropriately here, but we need to look more thoroughly into how
		 * panels behave in the two modes.
		 */
		
		I915_WRITE(LVDS, lvds);
		I915_READ(LVDS);
	}
	
	I915_WRITE(fp_reg, fp);
	I915_WRITE(dpll_reg, dpll);
	I915_READ(dpll_reg);
	/* Wait for the clocks to stabilize. */
	udelay(150);
	
	if (IS_I965G(dev)) {
		int sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
		I915_WRITE(dpll_md_reg, (0 << DPLL_MD_UDI_DIVIDER_SHIFT) |
			   ((sdvo_pixel_multiply - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT));
	} else {
		/* write it again -- the BIOS does, after all */
		I915_WRITE(dpll_reg, dpll);
	}
	I915_READ(dpll_reg);
	/* Wait for the clocks to stabilize. */
	udelay(150);
	
	I915_WRITE(htot_reg, (adjusted_mode->crtc_hdisplay - 1) |
		   ((adjusted_mode->crtc_htotal - 1) << 16));
	I915_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) |
		   ((adjusted_mode->crtc_hblank_end - 1) << 16));
	I915_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) |
		   ((adjusted_mode->crtc_hsync_end - 1) << 16));
	I915_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) |
		   ((adjusted_mode->crtc_vtotal - 1) << 16));
	I915_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) |
		   ((adjusted_mode->crtc_vblank_end - 1) << 16));
	I915_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) |
		   ((adjusted_mode->crtc_vsync_end - 1) << 16));
	/* pipesrc and dspsize control the size that is scaled from, which should
	 * always be the user's requested size.
	 */
	I915_WRITE(dspsize_reg, ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1));
	I915_WRITE(dsppos_reg, 0);
	I915_WRITE(pipesrc_reg, ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1));
	I915_WRITE(pipeconf_reg, pipeconf);
	I915_READ(pipeconf_reg);
	
	intel_wait_for_vblank(dev);
	
	I915_WRITE(dspcntr_reg, dspcntr);
	
	/* Flush the plane changes */
	intel_pipe_set_base(crtc, x, y);
	
	intel_set_vblank(dev);

	intel_wait_for_vblank(dev);    
}

/** Loads the palette/gamma unit for the CRTC with the prepared values */
void intel_crtc_load_lut(struct drm_crtc *crtc)
{
	struct drm_device *dev = crtc->dev;
	struct drm_i915_private *dev_priv = dev->dev_private;
	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
	int palreg = (intel_crtc->pipe == 0) ? PALETTE_A : PALETTE_B;
	int i;

	/* The clocks have to be on to load the palette. */
	if (!crtc->enabled)
		return;

	for (i = 0; i < 256; i++) {
		I915_WRITE(palreg + 4 * i,
			   (intel_crtc->lut_r[i] << 16) |
			   (intel_crtc->lut_g[i] << 8) |
			   intel_crtc->lut_b[i]);
	}
}

static int intel_crtc_cursor_set(struct drm_crtc *crtc,
				 struct drm_file *file_priv,
				 uint32_t handle,
				 uint32_t width, uint32_t height)
{
	struct drm_device *dev = crtc->dev;
	struct drm_i915_private *dev_priv = dev->dev_private;
	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
	struct drm_gem_object *bo;
	struct drm_i915_gem_object *obj_priv;
	int pipe = intel_crtc->pipe;
	uint32_t control = (pipe == 0) ? CURACNTR : CURBCNTR;
	uint32_t base = (pipe == 0) ? CURABASE : CURBBASE;
	uint32_t temp;
	size_t addr;

	DRM_DEBUG("\n");

	/* if we want to turn of the cursor ignore width and height */
	if (!handle) {
		DRM_DEBUG("cursor off\n");
		/* turn of the cursor */
		temp = 0;
		temp |= CURSOR_MODE_DISABLE;

		I915_WRITE(control, temp);
		I915_WRITE(base, 0);
		return 0;
	}

	/* Currently we only support 64x64 cursors */
	if (width != 64 || height != 64) {
		DRM_ERROR("we currently only support 64x64 cursors\n");
		return -EINVAL;
	}

	bo = drm_gem_object_lookup(dev, file_priv, handle);
	if (!bo)
		return -ENOENT;

	obj_priv = bo->driver_private;

	if (bo->size < width * height * 4) {
		DRM_ERROR("buffer is to small\n");
		return -ENOMEM;
	}

	if (dev_priv->cursor_needs_physical) {
		addr = dev->agp->base + obj_priv->gtt_offset;
	} else {
		addr = obj_priv->gtt_offset;
	}

	intel_crtc->cursor_addr = addr;
	temp = 0;
	/* set the pipe for the cursor */
	temp |= (pipe << 28);
	temp |= CURSOR_MODE_64_ARGB_AX | MCURSOR_GAMMA_ENABLE;

	I915_WRITE(control, temp);
	I915_WRITE(base, addr);

	return 0;
}

static int intel_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
{
	struct drm_device *dev = crtc->dev;
	struct drm_i915_private *dev_priv = dev->dev_private;
	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
	int pipe = intel_crtc->pipe;
	uint32_t temp = 0;
	uint32_t adder;

	if (x < 0) {
		temp |= (CURSOR_POS_SIGN << CURSOR_X_SHIFT);
		x = -x;
	}
	if (y < 0) {
		temp |= (CURSOR_POS_SIGN << CURSOR_Y_SHIFT);
		y = -y;
	}

	temp |= ((x & CURSOR_POS_MASK) << CURSOR_X_SHIFT);
	temp |= ((y & CURSOR_POS_MASK) << CURSOR_Y_SHIFT);

	adder = intel_crtc->cursor_addr;
	I915_WRITE((pipe == 0) ? CURAPOS : CURBPOS, temp);
	I915_WRITE((pipe == 0) ? CURABASE : CURBBASE, adder);

	return 0;
}

/** Sets the color ramps on behalf of RandR */