From 8cd045079e21093437b99cb150b97403e945d2c2 Mon Sep 17 00:00:00 2001 From: Robert Noland Date: Fri, 23 May 2008 14:36:05 -0400 Subject: [FreeBSD] Add vblank-rework support and get drivers building. The i915 driver now works again. --- bsd-core/drmP.h | 51 +++++++++-- bsd-core/drm_irq.c | 241 +++++++++++++++++++++++++++++++++++++++++++++++--- bsd-core/i915_drv.c | 5 +- bsd-core/mach64_drv.c | 4 +- bsd-core/mga_drv.c | 4 +- bsd-core/r128_drv.c | 4 +- bsd-core/radeon_drv.c | 5 +- 7 files changed, 284 insertions(+), 30 deletions(-) (limited to 'bsd-core') diff --git a/bsd-core/drmP.h b/bsd-core/drmP.h index 7a8ec183..4c35cdb2 100644 --- a/bsd-core/drmP.h +++ b/bsd-core/drmP.h @@ -621,10 +621,19 @@ struct drm_ati_pcigart_info { int gart_reg_if; void *addr; dma_addr_t bus_addr; + dma_addr_t table_mask; + dma_addr_t member_mask; + struct drm_dma_handle *table_handle; drm_local_map_t mapping; int table_size; }; +#ifndef DMA_BIT_MASK +#define DMA_BIT_MASK(n) (((n) == 64) ? ~0ULL : (1ULL<<(n)) - 1) +#endif + +#define upper_32_bits(_val) (((u64)(_val)) >> 32) + struct drm_driver_info { int (*load)(struct drm_device *, unsigned long flags); int (*firstopen)(struct drm_device *); @@ -649,11 +658,12 @@ struct drm_driver_info { int new); int (*kernel_context_switch_unlock)(struct drm_device *dev); void (*irq_preinstall)(struct drm_device *dev); - void (*irq_postinstall)(struct drm_device *dev); + int (*irq_postinstall)(struct drm_device *dev); void (*irq_uninstall)(struct drm_device *dev); void (*irq_handler)(DRM_IRQ_ARGS); - int (*vblank_wait)(struct drm_device *dev, unsigned int *sequence); - int (*vblank_wait2)(struct drm_device *dev, unsigned int *sequence); + u32 (*get_vblank_counter)(struct drm_device *dev, int crtc); + int (*enable_vblank)(struct drm_device *dev, int crtc); + void (*disable_vblank)(struct drm_device *dev, int crtc); drm_pci_id_list_t *id_entry; /* PCI ID, name, and chipset private */ @@ -775,9 +785,25 @@ struct drm_device { 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; + wait_queue_head_t *vbl_queue; /* vblank wait queue */ + atomic_t *_vblank_count; /* number of VBLANK interrupts */ + /* (driver must alloc the right number of counters) */ + struct mtx vbl_lock; + struct drm_vbl_sig_list *vbl_sigs; /* signal list to send on VBLANK */ + atomic_t vbl_signal_pending; /* number of signals pending on all crtcs*/ + atomic_t *vblank_refcount; /* number of users of vblank interrupts per crtc */ + u32 *last_vblank; /* protected by dev->vbl_lock, used */ + /* for wraparound handling */ + + u32 *vblank_offset; /* used to track how many vblanks */ + int *vblank_enabled; /* so we don't call enable more than */ + /* once per disable */ + u32 *vblank_premodeset; /* were lost during modeset */ + struct callout vblank_disable_timer; + unsigned long max_vblank_count; /* size of vblank counter register */ + int num_crtcs; + atomic_t vbl_received; + atomic_t vbl_received2; #ifdef __FreeBSD__ struct sigio *buf_sigio; /* Processes waiting for SIGIO */ @@ -903,8 +929,16 @@ 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); +void drm_handle_vblank(struct drm_device *dev, int crtc); +u32 drm_vblank_count(struct drm_device *dev, int crtc); +int drm_vblank_get(struct drm_device *dev, int crtc); +void drm_vblank_put(struct drm_device *dev, int crtc); +void drm_update_vblank_count(struct drm_device *dev, int crtc); int drm_vblank_wait(struct drm_device *dev, unsigned int *vbl_seq); -void drm_vbl_send_signals(struct drm_device *dev); +int drm_vblank_init(struct drm_device *dev, int num_crtcs); +void drm_vbl_send_signals(struct drm_device *dev, int crtc); +int drm_modeset_ctl(struct drm_device *dev, void *data, + struct drm_file *file_priv); /* AGP/PCI Express/GART support (drm_agpsupport.c) */ int drm_device_is_agp(struct drm_device *dev); @@ -1021,8 +1055,7 @@ int drm_mapbufs(struct drm_device *dev, void *data, int drm_dma(struct drm_device *dev, void *data, struct drm_file *file_priv); /* IRQ support (drm_irq.c) */ -int drm_control(struct drm_device *dev, void *data, - struct drm_file *file_priv); +int drm_control(struct drm_device *dev, void *data, struct drm_file *file_priv); int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_priv); void drm_locked_tasklet(struct drm_device *dev, diff --git a/bsd-core/drm_irq.c b/bsd-core/drm_irq.c index b2442888..592e2ea8 100644 --- a/bsd-core/drm_irq.c +++ b/bsd-core/drm_irq.c @@ -208,12 +208,130 @@ int drm_control(struct drm_device *dev, void *data, struct drm_file *file_priv) } } +static void vblank_disable_fn(void *arg) +{ + struct drm_device *dev = (struct drm_device *)arg; + unsigned long irqflags; + int i; + + for (i = 0; i < dev->num_crtcs; i++) { + DRM_SPINLOCK_IRQSAVE(&dev->vbl_lock, irqflags); + if (atomic_read(&dev->vblank_refcount[i]) == 0 && + dev->vblank_enabled[i]) { + dev->driver.disable_vblank(dev, i); + dev->vblank_enabled[i] = 0; + } + DRM_SPINUNLOCK_IRQRESTORE(&dev->vbl_lock, irqflags); + } +} + +u32 drm_vblank_count(struct drm_device *dev, int crtc) +{ + return atomic_read(&dev->_vblank_count[crtc]) + + dev->vblank_offset[crtc]; +} + +int drm_vblank_get(struct drm_device *dev, int crtc) +{ + unsigned long irqflags; + int ret = 0; + + DRM_SPINLOCK_IRQSAVE(&dev->vbl_lock, irqflags); + /* Going from 0->1 means we have to enable interrupts again */ + atomic_add_acq_int(&dev->vblank_refcount[crtc], 1); + if (dev->vblank_refcount[crtc] == 1 && + !dev->vblank_enabled[crtc]) { + ret = dev->driver.enable_vblank(dev, crtc); + if (ret) + atomic_dec(&dev->vblank_refcount[crtc]); + else + dev->vblank_enabled[crtc] = 1; + } + DRM_SPINUNLOCK_IRQRESTORE(&dev->vbl_lock, irqflags); + + return ret; +} + +void drm_vblank_put(struct drm_device *dev, int crtc) +{ + /* Last user schedules interrupt disable */ + atomic_subtract_acq_int(&dev->vblank_refcount[crtc], 1); + if (dev->vblank_refcount[crtc] == 0) + callout_reset(&dev->vblank_disable_timer, jiffies + 5*DRM_HZ, + (timeout_t *)vblank_disable_fn, (void *)dev); +} + +void drm_handle_vblank(struct drm_device *dev, int crtc) +{ + drm_update_vblank_count(dev, crtc); + DRM_WAKEUP(&dev->vbl_queue[crtc]); + drm_vbl_send_signals(dev, crtc); +} + +void drm_update_vblank_count(struct drm_device *dev, int crtc) +{ + unsigned long irqflags; + u32 cur_vblank, diff; + + /* + * Interrupts were disabled prior to this call, so deal with counter + * wrap if needed. + * NOTE! It's possible we lost a full dev->max_vblank_count events + * here if the register is small or we had vblank interrupts off for + * a long time. + */ + cur_vblank = dev->driver.get_vblank_counter(dev, crtc); + DRM_SPINLOCK_IRQSAVE(&dev->vbl_lock, irqflags); + if (cur_vblank < dev->last_vblank[crtc]) { + diff = dev->max_vblank_count - + dev->last_vblank[crtc]; + diff += cur_vblank; + } else { + diff = cur_vblank - dev->last_vblank[crtc]; + } + dev->last_vblank[crtc] = cur_vblank; + DRM_SPINUNLOCK_IRQRESTORE(&dev->vbl_lock, irqflags); + + atomic_add(diff, &dev->_vblank_count[crtc]); +} + +int drm_modeset_ctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_modeset_ctl *modeset = data; + int crtc, ret = 0; + u32 new; + + crtc = modeset->crtc; + if (crtc >= dev->num_crtcs) { + ret = -EINVAL; + goto out; + } + + switch (modeset->cmd) { + case _DRM_PRE_MODESET: + dev->vblank_premodeset[crtc] = + dev->driver.get_vblank_counter(dev, crtc); + break; + case _DRM_POST_MODESET: + new = dev->driver.get_vblank_counter(dev, crtc); + dev->vblank_offset[crtc] = dev->vblank_premodeset[crtc] - new; + break; + default: + ret = -EINVAL; + break; + } + +out: + return ret; +} + int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_priv) { drm_wait_vblank_t *vblwait = data; struct timeval now; int ret = 0; - int flags, seq; + int flags, seq, crtc; if (!dev->irq_enabled) return EINVAL; @@ -227,12 +345,13 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr } flags = vblwait->request.type & _DRM_VBLANK_FLAGS_MASK; + crtc = flags & _DRM_VBLANK_SECONDARY ? 1 : 0; - if ((flags & _DRM_VBLANK_SECONDARY) && !dev->driver.use_vbl_irq2) + if (crtc >= dev->num_crtcs) return EINVAL; - - seq = atomic_read((flags & _DRM_VBLANK_SECONDARY) ? - &dev->vbl_received2 : &dev->vbl_received); + + drm_update_vblank_count(dev, crtc); + seq = drm_vblank_count(dev, crtc); switch (vblwait->request.type & _DRM_VBLANK_TYPES_MASK) { case _DRM_VBLANK_RELATIVE: @@ -269,16 +388,18 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr #endif ret = EINVAL; } else { + unsigned long cur_vblank; + DRM_LOCK(); /* shared code returns -errno */ - if (flags & _DRM_VBLANK_SECONDARY) { - if (dev->driver.vblank_wait2) - ret = -dev->driver.vblank_wait2(dev, - &vblwait->request.sequence); - } else if (dev->driver.vblank_wait) - ret = -dev->driver.vblank_wait(dev, - &vblwait->request.sequence); + ret = drm_vblank_get(dev, crtc); + if (ret) + return ret; + DRM_WAIT_ON(ret, dev->vbl_queue[crtc], 3 * DRM_HZ, + (((cur_vblank = drm_vblank_count(dev, crtc)) + - vblwait->request.sequence) <= (1 << 23))); + drm_vblank_put(dev, crtc); DRM_UNLOCK(); microtime(&now); @@ -289,12 +410,104 @@ int drm_wait_vblank(struct drm_device *dev, void *data, struct drm_file *file_pr return ret; } -void drm_vbl_send_signals(struct drm_device *dev) +static void drm_vblank_cleanup(struct drm_device *dev) +{ + /* Bail if the driver didn't call drm_vblank_init() */ + if (dev->num_crtcs == 0) + return; + + callout_stop(&dev->vblank_disable_timer); + + vblank_disable_fn((void *)dev); + + drm_free(dev->vbl_queue, sizeof(*dev->vbl_queue) * dev->num_crtcs, + DRM_MEM_DRIVER); + drm_free(dev->vbl_sigs, sizeof(*dev->vbl_sigs) * dev->num_crtcs, + DRM_MEM_DRIVER); + drm_free(dev->_vblank_count, sizeof(*dev->_vblank_count) * + dev->num_crtcs, DRM_MEM_DRIVER); + drm_free(dev->vblank_refcount, sizeof(*dev->vblank_refcount) * + dev->num_crtcs, DRM_MEM_DRIVER); + drm_free(dev->vblank_enabled, sizeof(*dev->vblank_enabled) * + dev->num_crtcs, DRM_MEM_DRIVER); + drm_free(dev->last_vblank, sizeof(*dev->last_vblank) * dev->num_crtcs, + DRM_MEM_DRIVER); + drm_free(dev->vblank_premodeset, sizeof(*dev->vblank_premodeset) * + dev->num_crtcs, DRM_MEM_DRIVER); + drm_free(dev->vblank_offset, sizeof(*dev->vblank_offset) * dev->num_crtcs, + DRM_MEM_DRIVER); + + dev->num_crtcs = 0; +} + +int drm_vblank_init(struct drm_device *dev, int num_crtcs) +{ + int i, ret = -ENOMEM; + + callout_init(&dev->vblank_disable_timer, 0); + DRM_SPININIT(&dev->vbl_lock, "drm_vblk"); + atomic_set(&dev->vbl_signal_pending, 0); + dev->num_crtcs = num_crtcs; + + dev->vbl_queue = drm_alloc(sizeof(wait_queue_head_t) * num_crtcs, + DRM_MEM_DRIVER); + if (!dev->vbl_queue) + goto err; + + dev->vbl_sigs = drm_alloc(sizeof(struct drm_vbl_sig) * num_crtcs, + DRM_MEM_DRIVER); + if (!dev->vbl_sigs) + goto err; + + dev->_vblank_count = drm_alloc(sizeof(atomic_t) * num_crtcs, + DRM_MEM_DRIVER); + if (!dev->_vblank_count) + goto err; + + dev->vblank_refcount = drm_alloc(sizeof(atomic_t) * num_crtcs, + DRM_MEM_DRIVER); + if (!dev->vblank_refcount) + goto err; + + dev->vblank_enabled = drm_calloc(num_crtcs, sizeof(int), + DRM_MEM_DRIVER); + if (!dev->vblank_enabled) + goto err; + + dev->last_vblank = drm_calloc(num_crtcs, sizeof(u32), DRM_MEM_DRIVER); + if (!dev->last_vblank) + goto err; + + dev->vblank_premodeset = drm_calloc(num_crtcs, sizeof(u32), + DRM_MEM_DRIVER); + if (!dev->vblank_premodeset) + goto err; + + dev->vblank_offset = drm_calloc(num_crtcs, sizeof(u32), DRM_MEM_DRIVER); + if (!dev->vblank_offset) + goto err; + + /* Zero per-crtc vblank stuff */ + for (i = 0; i < num_crtcs; i++) { + DRM_INIT_WAITQUEUE(&dev->vbl_queue[i]); + TAILQ_INIT(&dev->vbl_sigs[i]); + atomic_set(&dev->_vblank_count[i], 0); + atomic_set(&dev->vblank_refcount[i], 0); + } + + return 0; + +err: + drm_vblank_cleanup(dev); + return ret; +} + +void drm_vbl_send_signals(struct drm_device *dev, int crtc) { } #if 0 /* disabled */ -void drm_vbl_send_signals( struct drm_device *dev ) +void drm_vbl_send_signals(struct drm_device *dev, int crtc ) { drm_vbl_sig_t *vbl_sig; unsigned int vbl_seq = atomic_read( &dev->vbl_received ); diff --git a/bsd-core/i915_drv.c b/bsd-core/i915_drv.c index 3639c62a..e6769d17 100644 --- a/bsd-core/i915_drv.c +++ b/bsd-core/i915_drv.c @@ -47,8 +47,9 @@ static void i915_configure(struct drm_device *dev) dev->driver.preclose = i915_driver_preclose; dev->driver.lastclose = i915_driver_lastclose; dev->driver.device_is_agp = i915_driver_device_is_agp; - dev->driver.vblank_wait = i915_driver_vblank_wait; - dev->driver.vblank_wait2 = i915_driver_vblank_wait2; + dev->driver.get_vblank_counter = i915_get_vblank_counter; + dev->driver.enable_vblank = i915_enable_vblank; + dev->driver.disable_vblank = i915_disable_vblank; dev->driver.irq_preinstall = i915_driver_irq_preinstall; dev->driver.irq_postinstall = i915_driver_irq_postinstall; dev->driver.irq_uninstall = i915_driver_irq_uninstall; diff --git a/bsd-core/mach64_drv.c b/bsd-core/mach64_drv.c index 35d0c805..06e0133d 100644 --- a/bsd-core/mach64_drv.c +++ b/bsd-core/mach64_drv.c @@ -48,7 +48,9 @@ static void mach64_configure(struct drm_device *dev) { dev->driver.buf_priv_size = 1; /* No dev_priv */ dev->driver.lastclose = mach64_driver_lastclose; - dev->driver.vblank_wait = mach64_driver_vblank_wait; + dev->driver.get_vblank_counter = mach64_get_vblank_counter; + dev->driver.enable_vblank = mach64_enable_vblank; + dev->driver.disable_vblank = mach64_disable_vblank; dev->driver.irq_preinstall = mach64_driver_irq_preinstall; dev->driver.irq_postinstall = mach64_driver_irq_postinstall; dev->driver.irq_uninstall = mach64_driver_irq_uninstall; diff --git a/bsd-core/mga_drv.c b/bsd-core/mga_drv.c index 7b20ea00..15d8175c 100644 --- a/bsd-core/mga_drv.c +++ b/bsd-core/mga_drv.c @@ -90,7 +90,9 @@ static void mga_configure(struct drm_device *dev) dev->driver.load = mga_driver_load; dev->driver.unload = mga_driver_unload; dev->driver.lastclose = mga_driver_lastclose; - dev->driver.vblank_wait = mga_driver_vblank_wait; + dev->driver.get_vblank_counter = mga_get_vblank_counter; + dev->driver.enable_vblank = mga_enable_vblank; + dev->driver.disable_vblank = mga_disable_vblank; dev->driver.irq_preinstall = mga_driver_irq_preinstall; dev->driver.irq_postinstall = mga_driver_irq_postinstall; dev->driver.irq_uninstall = mga_driver_irq_uninstall; diff --git a/bsd-core/r128_drv.c b/bsd-core/r128_drv.c index cdbfc014..b149d512 100644 --- a/bsd-core/r128_drv.c +++ b/bsd-core/r128_drv.c @@ -47,7 +47,9 @@ static void r128_configure(struct drm_device *dev) dev->driver.buf_priv_size = sizeof(drm_r128_buf_priv_t); dev->driver.preclose = r128_driver_preclose; dev->driver.lastclose = r128_driver_lastclose; - dev->driver.vblank_wait = r128_driver_vblank_wait; + dev->driver.get_vblank_counter = r128_get_vblank_counter; + dev->driver.enable_vblank = r128_enable_vblank; + dev->driver.disable_vblank = r128_disable_vblank; dev->driver.irq_preinstall = r128_driver_irq_preinstall; dev->driver.irq_postinstall = r128_driver_irq_postinstall; dev->driver.irq_uninstall = r128_driver_irq_uninstall; diff --git a/bsd-core/radeon_drv.c b/bsd-core/radeon_drv.c index dd04d2c4..0b4dba18 100644 --- a/bsd-core/radeon_drv.c +++ b/bsd-core/radeon_drv.c @@ -52,8 +52,9 @@ static void radeon_configure(struct drm_device *dev) dev->driver.preclose = radeon_driver_preclose; dev->driver.postclose = radeon_driver_postclose; dev->driver.lastclose = radeon_driver_lastclose; - dev->driver.vblank_wait = radeon_driver_vblank_wait; - dev->driver.vblank_wait2 = radeon_driver_vblank_wait2; + dev->driver.get_vblank_counter = radeon_get_vblank_counter; + dev->driver.enable_vblank = radeon_enable_vblank; + dev->driver.disable_vblank = radeon_disable_vblank; dev->driver.irq_preinstall = radeon_driver_irq_preinstall; dev->driver.irq_postinstall = radeon_driver_irq_postinstall; dev->driver.irq_uninstall = radeon_driver_irq_uninstall; -- cgit v1.2.3