diff options
author | Thomas Hellstrom <unichrome@shipmail.org> | 2005-03-28 21:21:42 +0000 |
---|---|---|
committer | Thomas Hellstrom <unichrome@shipmail.org> | 2005-03-28 21:21:42 +0000 |
commit | 532ccb98b5f2946f574a747b90c39edbe783f888 (patch) | |
tree | cfbdf4e8e2b8e0827b35b92a7e57e7f6104e18f1 | |
parent | c6161eff86b250f3113791edcc162dc97322c401 (diff) |
Via updates:
New PCI command parser. Moved from via_dma.c to via_verifier.c so functions
with similar functionality are close to eachother.
Moved video related functions to via_video.c, which might be extended in
the future, as new video functionality is added.
New device-specific generic IRQ IOCTL, similar to the general VBLANK IOCTL,
but with support for multiple device IRQ sources and functionality.
Support for Unichrome Pro PM800/CN400 video DMA commands in verifier and
PCI parser.
Support for Unichrome Pro PM800/CN400 HQV IRQs in the new generic IRQ
IOCTL.
Bumped minor. New version 2.6.0.
-rw-r--r-- | linux-core/Makefile | 2 | ||||
-rw-r--r-- | linux-core/Makefile.kernel | 3 | ||||
-rw-r--r-- | linux-core/drm_irq.c | 4 | ||||
-rw-r--r-- | linux/Makefile | 2 | ||||
-rw-r--r-- | linux/Makefile.kernel | 3 | ||||
-rw-r--r-- | shared-core/via_dma.c | 70 | ||||
-rw-r--r-- | shared-core/via_drm.h | 24 | ||||
-rw-r--r-- | shared-core/via_drv.c | 3 | ||||
-rw-r--r-- | shared-core/via_drv.h | 27 | ||||
-rw-r--r-- | shared-core/via_irq.c | 192 | ||||
-rw-r--r-- | shared-core/via_map.c | 36 | ||||
-rw-r--r-- | shared-core/via_mm.c | 19 | ||||
-rw-r--r-- | shared-core/via_verifier.c | 181 | ||||
-rw-r--r-- | shared/via.h | 11 | ||||
-rw-r--r-- | shared/via_dma.c | 74 | ||||
-rw-r--r-- | shared/via_drm.h | 24 | ||||
-rw-r--r-- | shared/via_drv.h | 22 | ||||
-rw-r--r-- | shared/via_irq.c | 247 | ||||
-rw-r--r-- | shared/via_map.c | 35 | ||||
-rw-r--r-- | shared/via_mm.c | 17 | ||||
-rw-r--r-- | shared/via_verifier.c | 181 |
21 files changed, 865 insertions, 312 deletions
diff --git a/linux-core/Makefile b/linux-core/Makefile index bc9c00a0..64704a35 100644 --- a/linux-core/Makefile +++ b/linux-core/Makefile @@ -93,7 +93,7 @@ VIAHEADERS = via_drm.h via_drv.h via_mm.h via_ds.h \ via_3d_reg.h via_verifier.h $(DRMHEADERS) VIASHARED = via_drm.h via_drv.h via_mm.h via_ds.h \ via_3d_reg.h via_drv.c via_ds.c via_irq.c via_map.c \ - via_mm.c via_dma.c via_verifier.c via_verifier.h + via_mm.c via_dma.c via_verifier.c via_verifier.h via_video.c MACH64HEADERS = mach64_drv.h mach64_drm.h $(DRMHEADERS) MACH64SHARED = mach64_drv.h mach64_drm.h mach64_dma.c \ mach64_irq.c mach64_state.c diff --git a/linux-core/Makefile.kernel b/linux-core/Makefile.kernel index 6868aa0f..93082355 100644 --- a/linux-core/Makefile.kernel +++ b/linux-core/Makefile.kernel @@ -22,7 +22,8 @@ radeon-objs := radeon_drv.o radeon_cp.o radeon_state.o radeon_mem.o radeon_irq.o sis-objs := sis_drv.o sis_ds.o sis_mm.o ffb-objs := ffb_drv.o ffb_context.o savage-objs := savage_drv.o savage_bci.o savage_state.o -via-objs := via_irq.o via_drv.o via_ds.o via_map.o via_mm.o via_dma.o via_verifier.o +via-objs := via_irq.o via_drv.o via_ds.o via_map.o via_mm.o via_dma.o via_verifier.o \ + via_video.o mach64-objs := mach64_drv.o mach64_dma.o mach64_irq.o mach64_state.o obj-m += drm.o diff --git a/linux-core/drm_irq.c b/linux-core/drm_irq.c index a6ebb173..27c9bc18 100644 --- a/linux-core/drm_irq.c +++ b/linux-core/drm_irq.c @@ -222,12 +222,12 @@ int drm_control(struct inode *inode, struct file *filp, * Wait for VBLANK. * * \param inode device inode. - * \param filp file pointer. + * \param filp file pointer.rm. * \param cmd command. * \param data user argument, pointing to a drm_wait_vblank structure. * \return zero on success or a negative number on failure. * - * Verifies the IRQ is installed. + * Verifies the IRQ is installed * * If a signal is requested checks if this task has already scheduled the same signal * for the same vblank sequence number - nothing to be done in diff --git a/linux/Makefile b/linux/Makefile index 08654cf2..ac1d25c0 100644 --- a/linux/Makefile +++ b/linux/Makefile @@ -99,7 +99,7 @@ VIAHEADERS = via_drm.h via_drv.h via.h via_mm.h via_ds.h \ via_3d_reg.h $(DRMHEADERS) $(DRMTEMPLATES) VIASHARED = via_drm.h via_drv.h via.h via_mm.h via_ds.h \ via_3d_reg.h via_drv.c via_ds.c via_irq.c via_map.c \ - via_mm.c via_dma.c via_verifier.c via_verifier.h + via_mm.c via_dma.c via_verifier.c via_verifier.h via_video.c MACH64HEADERS = mach64.h mach64_drv.h mach64_drm.h $(DRMHEADERS) \ $(DRMTEMPLATES) MACH64SHARED = mach64.h mach64_drv.h mach64_drm.h mach64_dma.c \ diff --git a/linux/Makefile.kernel b/linux/Makefile.kernel index edd885f7..40b54e04 100644 --- a/linux/Makefile.kernel +++ b/linux/Makefile.kernel @@ -17,7 +17,8 @@ radeon-objs := radeon_drv.o radeon_cp.o radeon_state.o radeon_mem.o radeon_irq.o sis-objs := sis_drv.o sis_ds.o sis_mm.o ffb-objs := ffb_drv.o ffb_context.o savage-objs := savage_drv.o savage_dma.o -via-objs := via_irq.o via_drv.o via_ds.o via_map.o via_mm.o via_dma.o via_verifier.o +via-objs := via_irq.o via_drv.o via_ds.o via_map.o via_mm.o via_dma.o via_verifier.o \ + via_video.o mach64-objs := mach64_drv.o mach64_dma.o mach64_irq.o mach64_state.o # Kernel version checks diff --git a/shared-core/via_dma.c b/shared-core/via_dma.c index 1f5d3b94..081fefc1 100644 --- a/shared-core/via_dma.c +++ b/shared-core/via_dma.c @@ -28,7 +28,10 @@ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. * - * Authors: Various + * Authors: + * Tungsten Graphics, + * Erdi Chen, + * Thomas Hellstrom. */ #include "drmP.h" @@ -58,7 +61,7 @@ dev_priv->dma_low +=8; \ } -#define via_flush_write_combine() DRM_MEMORYBARRIER() +#define via_flush_write_combine() DRM_MEMORYBARRIER() #define VIA_OUT_RING_QW(w1,w2) \ *vb++ = (w1); \ @@ -275,7 +278,7 @@ static int via_dispatch_cmdbuffer(drm_device_t * dev, drm_via_cmdbuffer_t * cmd) * copy it to AGP memory when ready. */ - + if ((ret = via_verify_command_stream((uint32_t *)dev_priv->pci_buf, cmd->size, dev, 1))) { return ret; } @@ -333,58 +336,8 @@ int via_cmdbuffer(DRM_IOCTL_ARGS) return 0; } -static int via_parse_pci_cmdbuffer(drm_device_t * dev, const char *buf, - unsigned int size) -{ - drm_via_private_t *dev_priv = dev->dev_private; - const uint32_t *regbuf = (const uint32_t *) buf; - const uint32_t *regend = regbuf + (size >> 2); - const uint32_t *next_fire; - int fire_count = 0; - int ret; - int check_2d_cmd = 1; - - - - if ((ret = via_verify_command_stream(regbuf, size, dev, 0))) - return ret; - - next_fire = dev_priv->fire_offsets[fire_count]; - while (regbuf != regend) { - if ( *regbuf == HALCYON_HEADER2 ) { - - regbuf++; - check_2d_cmd = ( *regbuf != HALCYON_SUB_ADDR0 ); - VIA_WRITE(HC_REG_TRANS_SET + HC_REG_BASE, *regbuf++); - - } else if ( check_2d_cmd && ((*regbuf & HALCYON_HEADER1MASK) == HALCYON_HEADER1 )) { - - register uint32_t addr = ( (*regbuf++ ) & ~HALCYON_HEADER1MASK) << 2; - VIA_WRITE( addr, *regbuf++ ); - - } else if ( (fire_count < dev_priv->num_fire_offsets) && - (regbuf == next_fire) && - (( *regbuf & HALCYON_FIREMASK ) == HALCYON_FIRECMD) ) { - - next_fire = dev_priv->fire_offsets[++fire_count]; - - VIA_WRITE(HC_REG_TRANS_SPACE + HC_REG_BASE, *regbuf++); - - if ( ( regbuf != regend ) && - ((*regbuf & HALCYON_FIREMASK) == HALCYON_FIRECMD)) - regbuf++; - if (( *regbuf & HALCYON_CMDBMASK ) != HC_ACMD_HCmdB ) - check_2d_cmd = 1; - } else { - - VIA_WRITE(HC_REG_TRANS_SPACE + HC_REG_BASE , *regbuf++); - - } - } - return 0; - -} - +extern int +via_parse_command_stream(drm_device_t *dev, const uint32_t * buf, unsigned int size); static int via_dispatch_pci_cmdbuffer(drm_device_t * dev, drm_via_cmdbuffer_t * cmd) { @@ -396,7 +349,12 @@ static int via_dispatch_pci_cmdbuffer(drm_device_t * dev, } if (DRM_COPY_FROM_USER(dev_priv->pci_buf, cmd->buf, cmd->size)) return DRM_ERR(EFAULT); - ret = via_parse_pci_cmdbuffer(dev, dev_priv->pci_buf, cmd->size); + + if ((ret = via_verify_command_stream((uint32_t *)dev_priv->pci_buf, cmd->size, dev, 0))) { + return ret; + } + + ret = via_parse_command_stream(dev, (const uint32_t *)dev_priv->pci_buf, cmd->size); return ret; } diff --git a/shared-core/via_drm.h b/shared-core/via_drm.h index f3b4a28a..4588c9bd 100644 --- a/shared-core/via_drm.h +++ b/shared-core/via_drm.h @@ -73,6 +73,8 @@ #define DRM_VIA_FLUSH 0x09 #define DRM_VIA_PCICMD 0x0a #define DRM_VIA_CMDBUF_SIZE 0x0b +#define NOT_USED +#define DRM_VIA_WAIT_IRQ 0x0d #define DRM_IOCTL_VIA_ALLOCMEM DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_ALLOCMEM, drm_via_mem_t) #define DRM_IOCTL_VIA_FREEMEM DRM_IOW( DRM_COMMAND_BASE + DRM_VIA_FREEMEM, drm_via_mem_t) @@ -86,6 +88,7 @@ #define DRM_IOCTL_VIA_PCICMD DRM_IOW( DRM_COMMAND_BASE + DRM_VIA_PCICMD, drm_via_cmdbuffer_t) #define DRM_IOCTL_VIA_CMDBUF_SIZE DRM_IOWR( DRM_COMMAND_BASE + DRM_VIA_CMDBUF_SIZE, \ drm_via_cmdbuf_size_t) +#define DRM_IOCTL_VIA_WAIT_IRQ DRM_IOWR( DRM_COMMAND_BASE + DRM_VIA_WAIT_IRQ, drm_via_irqwait_t) /* Indices into buf.Setup where various bits of state are mirrored per * context and per buffer. These can be fired at the card as a unit, @@ -200,6 +203,26 @@ typedef struct _drm_via_cmdbuf_size { uint32_t size; } drm_via_cmdbuf_size_t; +typedef enum { + VIA_IRQ_ABSOLUTE = 0x0, + VIA_IRQ_RELATIVE = 0x1, + VIA_IRQ_SIGNAL = 0x10000000, + VIA_IRQ_FORCE_SEQUENCE = 0x20000000 +} via_irq_seq_type_t; + +#define VIA_IRQ_FLAGS_MASK 0xF0000000 + +struct drm_via_wait_irq_request{ + unsigned irq; + via_irq_seq_type_t type; + uint32_t sequence; + uint32_t signal; +}; + +typedef union drm_via_irqwait { + struct drm_via_wait_irq_request request; + struct drm_wait_vblank_reply reply; +} drm_via_irqwait_t; #ifdef __KERNEL__ @@ -214,6 +237,7 @@ int via_cmdbuffer(DRM_IOCTL_ARGS); int via_flush_ioctl(DRM_IOCTL_ARGS); int via_pci_cmdbuffer(DRM_IOCTL_ARGS); int via_cmdbuf_size(DRM_IOCTL_ARGS); +int via_wait_irq(DRM_IOCTL_ARGS); #endif #endif /* _VIA_DRM_H_ */ diff --git a/shared-core/via_drv.c b/shared-core/via_drv.c index 13a22eca..21f28a63 100644 --- a/shared-core/via_drv.c +++ b/shared-core/via_drv.c @@ -69,7 +69,8 @@ static drm_ioctl_desc_t ioctls[] = { [DRM_IOCTL_NR(DRM_VIA_CMDBUFFER)] = {via_cmdbuffer, 1, 0}, [DRM_IOCTL_NR(DRM_VIA_FLUSH)] = {via_flush_ioctl, 1, 0}, [DRM_IOCTL_NR(DRM_VIA_PCICMD)] = {via_pci_cmdbuffer, 1, 0}, - [DRM_IOCTL_NR(DRM_VIA_CMDBUF_SIZE)] = {via_cmdbuf_size, 1, 0} + [DRM_IOCTL_NR(DRM_VIA_CMDBUF_SIZE)] = {via_cmdbuf_size, 1, 0}, + [DRM_IOCTL_NR(DRM_VIA_WAIT_IRQ)] = {via_wait_irq, 1, 0} }; static int probe(struct pci_dev *pdev, const struct pci_device_id *ent); diff --git a/shared-core/via_drv.h b/shared-core/via_drv.h index 00382e5d..353a9b9b 100644 --- a/shared-core/via_drv.h +++ b/shared-core/via_drv.h @@ -27,23 +27,35 @@ #define DRIVER_AUTHOR "VIA" #define DRIVER_NAME "via" -#define DRIVER_DESC "VIA Unichrome" -#define DRIVER_DATE "20050314" +#define DRIVER_DESC "VIA Unichrome / Pro" +#define DRIVER_DATE "20050328" #define DRIVER_MAJOR 2 -#define DRIVER_MINOR 5 +#define DRIVER_MINOR 6 #define DRIVER_PATCHLEVEL 0 #include "via_verifier.h" #define VIA_PCI_BUF_SIZE 120000 #define VIA_FIRE_BUF_SIZE 2048 +#define VIA_NUM_IRQS 2 + + typedef struct drm_via_ring_buffer { drm_map_t map; char *virtual_start; } drm_via_ring_buffer_t; +typedef uint32_t maskarray_t[5]; + +typedef struct drm_via_irq { + atomic_t irq_received; + uint32_t pending_mask; + uint32_t enable_mask; + wait_queue_head_t irq_queue; +} drm_via_irq_t; + typedef struct drm_via_private { drm_via_sarea_t *sarea_priv; drm_map_t *sarea; @@ -66,6 +78,12 @@ typedef struct drm_via_private { char pci_buf[VIA_PCI_BUF_SIZE]; const uint32_t *fire_offsets[VIA_FIRE_BUF_SIZE]; uint32_t num_fire_offsets; + int pro_group_a; + drm_via_irq_t via_irqs[VIA_NUM_IRQS]; + unsigned num_irqs; + maskarray_t *irq_masks; + uint32_t irq_enable_mask; + uint32_t irq_pending_mask; } drm_via_private_t; /* VIA MMIO register access */ @@ -92,6 +110,9 @@ extern void via_driver_irq_uninstall(drm_device_t * dev); extern int via_dma_cleanup(drm_device_t * dev); extern void via_init_command_verifier(void); extern int via_driver_dma_quiescent(drm_device_t * dev); +extern void via_init_futex(drm_via_private_t *dev_priv); +extern void via_cleanup_futex(drm_via_private_t *dev_priv); +extern void via_release_futex(drm_via_private_t *dev_priv, int context); #endif diff --git a/shared-core/via_irq.c b/shared-core/via_irq.c index 2d78dd44..c0a30c63 100644 --- a/shared-core/via_irq.c +++ b/shared-core/via_irq.c @@ -2,6 +2,7 @@ * * Copyright 2004 BEAM Ltd. * Copyright 2002 Tungsten Graphics, Inc. + * Copyright 2005 Thomas Hellstrom. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -27,10 +28,11 @@ * Authors: * Terry Barnaby <terry1@beam.ltd.uk> * Keith Whitwell <keith@tungstengraphics.com> + * Thomas Hellstrom <unichrome@shipmail.org> * - * - * This code provides standard DRM access to the Via CLE266's Vertical blank - * interrupt. + * This code provides standard DRM access to the Via Unichrome / Pro Vertical blank + * interrupt, as well as an infrastructure to handle other interrupts of the chip. + * The refresh rate is also calculated for video playback sync purposes. */ #include "drmP.h" @@ -42,9 +44,26 @@ /* VIA_REG_INTERRUPT */ #define VIA_IRQ_GLOBAL (1 << 31) -#define VIA_IRQ_VBI_ENABLE (1 << 19) -#define VIA_IRQ_VBI_PENDING (1 << 3) +#define VIA_IRQ_VBLANK_ENABLE (1 << 19) +#define VIA_IRQ_VBLANK_PENDING (1 << 3) +#define VIA_IRQ_HQV0_ENABLE (1 << 11) +#define VIA_IRQ_HQV1_ENABLE (1 << 25) +#define VIA_IRQ_HQV0_PENDING (1 << 9) +#define VIA_IRQ_HQV1_PENDING (1 << 10) + +/* + * Device-specific IRQs go here. This type might need to be extended with + * the register if there are multiple IRQ control registers. + * Currently we activate the HQV interrupts of Unichrome Pro group A. + */ +static maskarray_t via_pro_group_a_irqs[] = { + {VIA_IRQ_HQV0_ENABLE, VIA_IRQ_HQV0_PENDING, 0x000003D0, 0x00008010, 0x00000000 }, + {VIA_IRQ_HQV1_ENABLE, VIA_IRQ_HQV1_PENDING, 0x000013D0, 0x00008010, 0x00000000 }}; +static int via_num_pro_group_a = sizeof(via_pro_group_a_irqs)/sizeof(maskarray_t); + +static maskarray_t via_unichrome_irqs[] = {}; +static int via_num_unichrome = sizeof(via_unichrome_irqs)/sizeof(maskarray_t); static unsigned time_diff(struct timeval *now,struct timeval *then) @@ -61,9 +80,11 @@ irqreturn_t via_driver_irq_handler(DRM_IRQ_ARGS) u32 status; int handled = 0; struct timeval cur_vblank; + drm_via_irq_t *cur_irq = dev_priv->via_irqs; + int i; status = VIA_READ(VIA_REG_INTERRUPT); - if (status & VIA_IRQ_VBI_PENDING) { + if (status & VIA_IRQ_VBLANK_PENDING) { atomic_inc(&dev->vbl_received); if (!(atomic_read(&dev->vbl_received) & 0x0F)) { do_gettimeofday(&cur_vblank); @@ -82,10 +103,28 @@ irqreturn_t via_driver_irq_handler(DRM_IRQ_ARGS) drm_vbl_send_signals(dev); handled = 1; } + + + for (i=0; i<dev_priv->num_irqs; ++i) { + if (status & cur_irq->pending_mask) { + atomic_inc( &cur_irq->irq_received ); + DRM_WAKEUP( &cur_irq->irq_queue ); + handled = 1; + VIA_WRITE(VIA_REG_INTERRUPT, status); + + if (handled) + return IRQ_HANDLED; + else + return IRQ_NONE; - /* Acknowlege interrupts ?? */ + } + cur_irq++; + } + + /* Acknowlege interrupts */ VIA_WRITE(VIA_REG_INTERRUPT, status); + if (handled) return IRQ_HANDLED; else @@ -97,9 +136,10 @@ static __inline__ void viadrv_acknowledge_irqs(drm_via_private_t * dev_priv) u32 status; if (dev_priv) { - /* Acknowlege interrupts ?? */ + /* Acknowlege interrupts */ status = VIA_READ(VIA_REG_INTERRUPT); - VIA_WRITE(VIA_REG_INTERRUPT, status | VIA_IRQ_VBI_PENDING); + VIA_WRITE(VIA_REG_INTERRUPT, status | + dev_priv->irq_pending_mask); } } @@ -121,32 +161,94 @@ int via_driver_vblank_wait(drm_device_t * dev, unsigned int *sequence) * by about a day rather than she wants to wait for years * using vertical blanks... */ + DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ, (((cur_vblank = atomic_read(&dev->vbl_received)) - *sequence) <= (1 << 23))); - + *sequence = cur_vblank; return ret; } +static int +via_driver_irq_wait(drm_device_t * dev, unsigned int irq, int force_sequence, + unsigned int *sequence) +{ + drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + unsigned int cur_irq_sequence; + drm_via_irq_t *cur_irq = dev_priv->via_irqs; + int ret = 0; + maskarray_t *masks = dev_priv->irq_masks; + + DRM_DEBUG("%s\n", __FUNCTION__); + + if (!dev_priv) { + DRM_ERROR("%s called with no initialization\n", __FUNCTION__); + return DRM_ERR(EINVAL); + } + + if (irq >= dev_priv->num_irqs ) { + DRM_ERROR("%s Trying to wait on unknown irq %d\n", __FUNCTION__, irq); + return DRM_ERR(EINVAL); + } + + cur_irq += irq; + + if (masks[irq][2] && !force_sequence) { + DRM_WAIT_ON(ret, cur_irq->irq_queue, 3 * DRM_HZ, + ((VIA_READ(masks[irq][2]) & masks[irq][3]) == masks[irq][4])); + cur_irq_sequence = atomic_read(&cur_irq->irq_received); + } else { + DRM_WAIT_ON(ret, cur_irq->irq_queue, 3 * DRM_HZ, + (((cur_irq_sequence = atomic_read(&cur_irq->irq_received)) - + *sequence) <= (1 << 23))); + } + *sequence = cur_irq_sequence; + return ret; +} + + /* * drm_dma.h hooks */ + void via_driver_irq_preinstall(drm_device_t * dev) { drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; u32 status; + drm_via_irq_t *cur_irq = dev_priv->via_irqs; + int i; DRM_DEBUG("driver_irq_preinstall: dev_priv: %p\n", dev_priv); if (dev_priv) { + + dev_priv->irq_enable_mask = VIA_IRQ_VBLANK_ENABLE; + dev_priv->irq_pending_mask = VIA_IRQ_VBLANK_PENDING; + + dev_priv->irq_masks = (dev_priv->pro_group_a) ? + via_pro_group_a_irqs : via_unichrome_irqs; + dev_priv->num_irqs = (dev_priv->pro_group_a) ? + via_num_pro_group_a : via_num_unichrome; + + for(i=0; i < dev_priv->num_irqs; ++i) { + atomic_set(&cur_irq->irq_received, 0); + cur_irq->enable_mask = dev_priv->irq_masks[i][0]; + cur_irq->pending_mask = dev_priv->irq_masks[i][1]; + DRM_INIT_WAITQUEUE( &cur_irq->irq_queue ); + dev_priv->irq_enable_mask |= cur_irq->enable_mask; + dev_priv->irq_pending_mask |= cur_irq->pending_mask; + cur_irq++; + + DRM_DEBUG("Initializing IRQ %d\n", i); + } + dev_priv->last_vblank_valid = 0; - DRM_DEBUG("mmio: %p\n", dev_priv->mmio); - status = VIA_READ(VIA_REG_INTERRUPT); - DRM_DEBUG("intreg: %x\n", status & VIA_IRQ_VBI_ENABLE); // Clear VSync interrupt regs - VIA_WRITE(VIA_REG_INTERRUPT, status & ~VIA_IRQ_VBI_ENABLE); - + status = VIA_READ(VIA_REG_INTERRUPT); + VIA_WRITE(VIA_REG_INTERRUPT, status & + ~(dev_priv->irq_enable_mask)); + /* Clear bits if they're already high */ viadrv_acknowledge_irqs(dev_priv); } @@ -161,12 +263,13 @@ void via_driver_irq_postinstall(drm_device_t * dev) if (dev_priv) { status = VIA_READ(VIA_REG_INTERRUPT); VIA_WRITE(VIA_REG_INTERRUPT, status | VIA_IRQ_GLOBAL - | VIA_IRQ_VBI_ENABLE); + | dev_priv->irq_enable_mask); + /* Some magic, oh for some data sheets ! */ VIA_WRITE8(0x83d4, 0x11); VIA_WRITE8(0x83d5, VIA_READ8(0x83d5) | 0x30); - + } } @@ -184,7 +287,60 @@ void via_driver_irq_uninstall(drm_device_t * dev) VIA_WRITE8(0x83d5, VIA_READ8(0x83d5) & ~0x30); status = VIA_READ(VIA_REG_INTERRUPT); - VIA_WRITE(VIA_REG_INTERRUPT, status & ~VIA_IRQ_VBI_ENABLE); + VIA_WRITE(VIA_REG_INTERRUPT, status & + ~(VIA_IRQ_VBLANK_ENABLE | dev_priv->irq_enable_mask)); } } +int via_wait_irq(DRM_IOCTL_ARGS) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->head->dev; + drm_via_irqwait_t __user *argp = (void __user *)data; + drm_via_irqwait_t irqwait; + struct timeval now; + int ret = 0; + drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + drm_via_irq_t *cur_irq = dev_priv->via_irqs; + int force_sequence; + + if (!dev->irq) + return DRM_ERR(EINVAL); + + DRM_COPY_FROM_USER_IOCTL(irqwait, argp, sizeof(irqwait)); + if (irqwait.request.irq >= dev_priv->num_irqs) { + DRM_ERROR("%s Trying to wait on unknown irq %d\n", __FUNCTION__, + irqwait.request.irq); + return DRM_ERR(EINVAL); + } + + cur_irq += irqwait.request.irq; + + switch (irqwait.request.type & ~VIA_IRQ_FLAGS_MASK) { + case VIA_IRQ_RELATIVE: + irqwait.request.sequence += atomic_read(&cur_irq->irq_received); + irqwait.request.type &= ~_DRM_VBLANK_RELATIVE; + case VIA_IRQ_ABSOLUTE: + break; + default: + return DRM_ERR(EINVAL); + } + + if (irqwait.request.type & VIA_IRQ_SIGNAL) { + DRM_ERROR("%s Signals on Via IRQs not implemented yet.\n", + __FUNCTION__); + return DRM_ERR(EINVAL); + } + + force_sequence = (irqwait.request.type & VIA_IRQ_FORCE_SEQUENCE); + + ret = via_driver_irq_wait(dev, irqwait.request.irq, force_sequence, + &irqwait.request.sequence); + do_gettimeofday(&now); + irqwait.reply.tval_sec = now.tv_sec; + irqwait.reply.tval_usec = now.tv_usec; + + DRM_COPY_TO_USER_IOCTL(argp, irqwait, sizeof(irqwait)); + + return ret; +} diff --git a/shared-core/via_map.c b/shared-core/via_map.c index 50ca96cb..0be829b6 100644 --- a/shared-core/via_map.c +++ b/shared-core/via_map.c @@ -28,7 +28,6 @@ static int via_do_init_map(drm_device_t * dev, drm_via_init_t * init) { drm_via_private_t *dev_priv; - unsigned int i; DRM_DEBUG("%s\n", __FUNCTION__); @@ -67,13 +66,10 @@ static int via_do_init_map(drm_device_t * dev, drm_via_init_t * init) dev_priv->agpAddr = init->agpAddr; - for (i = 0; i < VIA_NR_XVMC_LOCKS; ++i) { - DRM_INIT_WAITQUEUE(&(dev_priv->decoder_queue[i])); - XVMCLOCKPTR(dev_priv->sarea_priv, i)->lock = 0; - } + via_init_futex( dev_priv ); + dev_priv->pro_group_a = (dev->pdev->device == 0x3118); dev->dev_private = (void *)dev_priv; - return 0; } @@ -112,31 +108,3 @@ int via_map_init(DRM_IOCTL_ARGS) } -int via_decoder_futex(DRM_IOCTL_ARGS) -{ - DRM_DEVICE; - drm_via_futex_t fx; - volatile int *lock; - drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; - drm_via_sarea_t *sAPriv = dev_priv->sarea_priv; - int ret = 0; - - DRM_COPY_FROM_USER_IOCTL(fx, (drm_via_futex_t *) data, sizeof(fx)); - - if (fx.lock > VIA_NR_XVMC_LOCKS) - return -EFAULT; - - lock = (int *)XVMCLOCKPTR(sAPriv, fx.lock); - - switch (fx.func) { - case VIA_FUTEX_WAIT: - DRM_WAIT_ON(ret, dev_priv->decoder_queue[fx.lock], - (fx.ms / 10) * (DRM_HZ / 100), *lock != fx.val); - return ret; - case VIA_FUTEX_WAKE: - DRM_WAKEUP(&(dev_priv->decoder_queue[fx.lock])); - return 0; - } - return 0; -} - diff --git a/shared-core/via_mm.c b/shared-core/via_mm.c index 5cead8c4..cf286b49 100644 --- a/shared-core/via_mm.c +++ b/shared-core/via_mm.c @@ -135,9 +135,7 @@ int via_init_context(struct drm_device *dev, int context) int via_final_context(struct drm_device *dev, int context) { int i; - volatile int *lock; drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; - drm_via_sarea_t *sAPriv = dev_priv->sarea_priv; for (i = 0; i < MAX_CONTEXT; i++) if (global_ppriv[i].used && @@ -170,23 +168,10 @@ int via_final_context(struct drm_device *dev, int context) retval = via_setNext(set, &item); } via_setDestroy(set); - global_ppriv[i].used = 0; } + via_release_futex(dev_priv, context); - /* - * Release futex locks. - */ - - for (i=0; i < VIA_NR_XVMC_LOCKS; ++i) { - lock = (int *) XVMCLOCKPTR(sAPriv, i); - if ( (_DRM_LOCKING_CONTEXT( *lock ) == context)) { - if (_DRM_LOCK_IS_HELD( *lock ) && (*lock & _DRM_LOCK_CONT)) { - DRM_WAKEUP( &(dev_priv->decoder_queue[i])); - } - *lock = 0; - } - } #if defined(__linux__) /* Linux specific until context tracking code gets ported to BSD */ @@ -196,6 +181,7 @@ int via_final_context(struct drm_device *dev, int context) if (dev->irq) drm_irq_uninstall(dev); + via_cleanup_futex(dev_priv); via_do_cleanup_map(dev); } #endif @@ -208,6 +194,7 @@ int via_mem_alloc(DRM_IOCTL_ARGS) drm_via_mem_t mem; DRM_COPY_FROM_USER_IOCTL(mem, (drm_via_mem_t *) data, sizeof(mem)); + switch (mem.type) { case VIDEO: if (via_fb_alloc(&mem) < 0) diff --git a/shared-core/via_verifier.c b/shared-core/via_verifier.c index 6b08f5d8..5b1f30a7 100644 --- a/shared-core/via_verifier.c +++ b/shared-core/via_verifier.c @@ -1,5 +1,6 @@ /* * Copyright 2004 The Unichrome Project. All Rights Reserved. + * Copyright 2005 Thomas Hellstrom. 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"), @@ -15,12 +16,12 @@ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE UNICHROME PROJECT, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * THE AUTHOR(S), AND/OR THE COPYRIGHT HOLDER(S) 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. * - * Author: Thomas Hellström 2004. + * Author: Thomas Hellstrom 2004, 2005. * This code was written using docs obtained under NDA from VIA Inc. * * Don't run this code directly on an AGP buffer. Due to cache problems it will @@ -677,16 +678,62 @@ via_check_header2( uint32_t const **buffer, const uint32_t *buf_end, return state_command; } +static __inline__ verifier_state_t +via_parse_header2( drm_via_private_t *dev_priv, uint32_t const **buffer, const uint32_t *buf_end, + int *fire_count) +{ + uint32_t cmd; + const uint32_t *buf = *buffer; + const uint32_t *next_fire; + int burst = 0; + + next_fire = dev_priv->fire_offsets[*fire_count]; + buf++; + cmd = (*buf & 0xFFFF0000) >> 16; + VIA_WRITE(HC_REG_TRANS_SET + HC_REG_BASE, *buf++); + switch(cmd) { + case HC_ParaType_CmdVdata: + while ((buf < buf_end) && + (*fire_count < dev_priv->num_fire_offsets) && + (*buf & HC_ACMD_MASK) == HC_ACMD_HCmdB ) { + while(buf <= next_fire) { + VIA_WRITE(HC_REG_TRANS_SPACE + HC_REG_BASE + (burst & 63), *buf++); + burst += 4; + } + if ( ( buf < buf_end ) && ((*buf & HALCYON_FIREMASK) == HALCYON_FIRECMD)) + buf++; + + if (++(*fire_count) < dev_priv->num_fire_offsets) + next_fire = dev_priv->fire_offsets[*fire_count]; + } + break; + default: + while(buf < buf_end) { + + if ( *buf == HC_HEADER2 || + (*buf & HALCYON_HEADER1MASK) == HALCYON_HEADER1 || + (*buf & VIA_VIDEOMASK) == VIA_VIDEO_HEADER5 || + (*buf & VIA_VIDEOMASK) == VIA_VIDEO_HEADER6 ) break; + + VIA_WRITE(HC_REG_TRANS_SPACE + HC_REG_BASE + (burst & 63), *buf++); + burst +=4; + } + } + *buffer = buf; + return state_command; +} + + static __inline__ int verify_mmio_address( uint32_t address) { if ((address > 0x3FF) && (address < 0xC00 )) { - DRM_ERROR("Invalid HALCYON_HEADER1 command. " + DRM_ERROR("Invalid VIDEO DMA command. " "Attempt to access 3D- or command burst area.\n"); return 1; - } else if (address > 0xCFF ) { - DRM_ERROR("Invalid HALCYON_HEADER1 command. " + } else if (address > 0x13FF ) { + DRM_ERROR("Invalid VIDEO DMA command. " "Attempt to access VGA registers.\n"); return 1; } @@ -746,6 +793,22 @@ via_check_header1( uint32_t const **buffer, const uint32_t *buf_end ) } static __inline__ verifier_state_t +via_parse_header1( drm_via_private_t *dev_priv, uint32_t const **buffer, const uint32_t *buf_end ) +{ + register uint32_t cmd; + const uint32_t *buf = *buffer; + + while (buf < buf_end) { + cmd = *buf; + if ((cmd & HALCYON_HEADER1MASK) != HALCYON_HEADER1) break; + VIA_WRITE( (cmd & ~HALCYON_HEADER1MASK) << 2, *++buf); + buf++; + } + *buffer = buf; + return state_command; +} + +static __inline__ verifier_state_t via_check_vheader5( uint32_t const **buffer, const uint32_t *buf_end ) { uint32_t data; @@ -771,7 +834,7 @@ via_check_vheader5( uint32_t const **buffer, const uint32_t *buf_end ) } if (eat_words(&buf, buf_end, data)) return state_error; - if (verify_video_tail(&buf, buf_end, 4 - (data & 3))) + if ((data & 3) && verify_video_tail(&buf, buf_end, 4 - (data & 3))) return state_error; *buffer = buf; return state_command; @@ -779,19 +842,36 @@ via_check_vheader5( uint32_t const **buffer, const uint32_t *buf_end ) } static __inline__ verifier_state_t +via_parse_vheader5( drm_via_private_t *dev_priv, uint32_t const **buffer, const uint32_t *buf_end ) +{ + uint32_t addr, count, i; + const uint32_t *buf = *buffer; + + addr = *buf++ & ~VIA_VIDEOMASK; + i = count = *buf; + buf += 3; + while(i--) { + VIA_WRITE(addr, *buf++); + } + if (count & 3) buf += 4 - (count & 3); + *buffer = buf; + return state_command; +} + + +static __inline__ verifier_state_t via_check_vheader6( uint32_t const **buffer, const uint32_t *buf_end ) { uint32_t data; const uint32_t *buf = *buffer; uint32_t i; - DRM_ERROR("H6\n"); if (buf_end - buf < 4) { DRM_ERROR("Illegal termination of video header6 command\n"); return state_error; } - + buf++; data = *buf++; if (*buf++ != 0x00F60000) { DRM_ERROR("Illegal header6 header data\n"); @@ -803,18 +883,40 @@ via_check_vheader6( uint32_t const **buffer, const uint32_t *buf_end ) } if ((buf_end - buf) < (data << 1)) { DRM_ERROR("Illegal termination of video header6 command\n"); + return state_error; } for (i=0; i<data; ++i) { if (verify_mmio_address(*buf++)) return state_error; buf++; } - if (verify_video_tail(&buf, buf_end, 4 - ((data << 1) & 3))) + data <<= 1; + if ((data & 3) && verify_video_tail(&buf, buf_end, 4 - (data & 3))) return state_error; *buffer = buf; return state_command; } +static __inline__ verifier_state_t +via_parse_vheader6( drm_via_private_t *dev_priv, uint32_t const **buffer, const uint32_t *buf_end ) +{ + + uint32_t addr, count, i; + const uint32_t *buf = *buffer; + + i = count = *++buf; + buf += 3; + while(i--) { + addr = *buf++; + VIA_WRITE(addr, *buf++); + } + count <<= 1; + if (count & 3) buf += 4 - (count & 3); + *buffer = buf; + return state_command; +} + + int via_verify_command_stream(const uint32_t * buf, unsigned int size, drm_device_t *dev, @@ -827,7 +929,7 @@ via_verify_command_stream(const uint32_t * buf, unsigned int size, drm_device_t uint32_t cmd; const uint32_t *buf_end = buf + ( size >> 2 ); verifier_state_t state = state_command; - + int pro_group_a = dev_priv->pro_group_a; hc_state->dev = dev; hc_state->unfinished = no_sequence; @@ -840,7 +942,7 @@ via_verify_command_stream(const uint32_t * buf, unsigned int size, drm_device_t switch (state) { case state_header2: - state = via_check_header2( &buf, buf_end, hc_state ); + state = via_check_header2( &buf, buf_end, hc_state ); break; case state_header1: state = via_check_header1( &buf, buf_end ); @@ -856,9 +958,9 @@ via_verify_command_stream(const uint32_t * buf, unsigned int size, drm_device_t state = state_header2; else if ((cmd & HALCYON_HEADER1MASK) == HALCYON_HEADER1) state = state_header1; - else if ((cmd & VIA_VIDEOMASK) == VIA_VIDEO_HEADER5) + else if (pro_group_a && (cmd & VIA_VIDEOMASK) == VIA_VIDEO_HEADER5) state = state_vheader5; - else if ((cmd & VIA_VIDEOMASK) == VIA_VIDEO_HEADER6) + else if (pro_group_a && (cmd & VIA_VIDEOMASK) == VIA_VIDEO_HEADER6) state = state_vheader6; else { DRM_ERROR("Invalid / Unimplemented DMA HEADER command. 0x%x\n", @@ -879,6 +981,59 @@ via_verify_command_stream(const uint32_t * buf, unsigned int size, drm_device_t return 0; } +int +via_parse_command_stream(drm_device_t *dev, const uint32_t * buf, unsigned int size) +{ + + drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + uint32_t cmd; + const uint32_t *buf_end = buf + ( size >> 2 ); + verifier_state_t state = state_command; + int fire_count = 0; + + while (buf < buf_end) { + + switch (state) { + case state_header2: + state = via_parse_header2( dev_priv, &buf, buf_end, &fire_count ); + break; + case state_header1: + state = via_parse_header1( dev_priv, &buf, buf_end ); + break; + case state_vheader5: + state = via_parse_vheader5( dev_priv, &buf, buf_end ); + break; + case state_vheader6: + state = via_parse_vheader6( dev_priv, &buf, buf_end ); + break; + case state_command: + if (HALCYON_HEADER2 == (cmd = *buf)) + state = state_header2; + else if ((cmd & HALCYON_HEADER1MASK) == HALCYON_HEADER1) + state = state_header1; + else if ((cmd & VIA_VIDEOMASK) == VIA_VIDEO_HEADER5) + state = state_vheader5; + else if ((cmd & VIA_VIDEOMASK) == VIA_VIDEO_HEADER6) + state = state_vheader6; + else { + DRM_ERROR("Invalid / Unimplemented DMA HEADER command. 0x%x\n", + cmd); + state = state_error; + } + break; + case state_error: + default: + return DRM_ERR(EINVAL); + } + } + if (state == state_error) { + return DRM_ERR(EINVAL); + } + return 0; +} + + + static void setup_hazard_table(hz_init_t init_table[], hazard_t table[], int size) { diff --git a/shared/via.h b/shared/via.h index b35164e4..816880e7 100644 --- a/shared/via.h +++ b/shared/via.h @@ -29,11 +29,11 @@ #define DRIVER_AUTHOR "VIA" #define DRIVER_NAME "via" -#define DRIVER_DESC "VIA Unichrome" -#define DRIVER_DATE "20050314" +#define DRIVER_DESC "VIA Unichrome / Pro" +#define DRIVER_DATE "20050328" #define DRIVER_MAJOR 2 -#define DRIVER_MINOR 5 +#define DRIVER_MINOR 6 #define DRIVER_PATCHLEVEL 0 #define DRIVER_IOCTLS \ @@ -46,8 +46,9 @@ [DRM_IOCTL_NR(DRM_IOCTL_VIA_DMA_INIT)] = { via_dma_init, 1, 0}, \ [DRM_IOCTL_NR(DRM_IOCTL_VIA_CMDBUFFER)] = { via_cmdbuffer, 1, 0}, \ [DRM_IOCTL_NR(DRM_IOCTL_VIA_FLUSH)] = { via_flush_ioctl, 1, 0}, \ - [DRM_IOCTL_NR(DRM_IOCTL_VIA_PCICMD)] = { via_pci_cmdbuffer, 1, 0}, \ - [DRM_IOCTL_NR(DRM_IOCTL_VIA_CMDBUF_SIZE)] = {via_cmdbuf_size, 1, 0} + [DRM_IOCTL_NR(DRM_IOCTL_VIA_PCICMD)] = {via_pci_cmdbuffer, 1, 0}, \ + [DRM_IOCTL_NR(DRM_IOCTL_VIA_CMDBUF_SIZE)] = {via_cmdbuf_size, 1, 0}, \ + [DRM_IOCTL_NR(DRM_IOCTL_VIA_WAIT_IRQ)] = {via_wait_irq, 1, 0} #endif diff --git a/shared/via_dma.c b/shared/via_dma.c index 6f877c5c..34ac6cc1 100644 --- a/shared/via_dma.c +++ b/shared/via_dma.c @@ -28,7 +28,10 @@ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. * - * Authors: Various + * Authors: + * Tungsten Graphics, + * Erdi Chen, + * Thomas Hellstrom. */ #include "via.h" @@ -59,7 +62,7 @@ dev_priv->dma_low +=8; \ } -#define via_flush_write_combine() DRM_MEMORYBARRIER() +#define via_flush_write_combine() DRM_MEMORYBARRIER() #define VIA_OUT_RING_QW(w1,w2) \ *vb++ = (w1); \ @@ -70,7 +73,7 @@ static void via_cmdbuf_start(drm_via_private_t * dev_priv); static void via_cmdbuf_pause(drm_via_private_t * dev_priv); static void via_cmdbuf_reset(drm_via_private_t * dev_priv); static void via_cmdbuf_rewind(drm_via_private_t * dev_priv); - +static int via_wait_idle(drm_via_private_t * dev_priv); /* * Free space in command buffer. */ @@ -276,7 +279,7 @@ static int via_dispatch_cmdbuffer(drm_device_t * dev, drm_via_cmdbuffer_t * cmd) * copy it to AGP memory when ready. */ - + if ((ret = via_verify_command_stream((uint32_t *)dev_priv->pci_buf, cmd->size, dev, 1))) { return ret; } @@ -334,58 +337,8 @@ int via_cmdbuffer(DRM_IOCTL_ARGS) return 0; } -static int via_parse_pci_cmdbuffer(drm_device_t * dev, const char *buf, - unsigned int size) -{ - drm_via_private_t *dev_priv = dev->dev_private; - const uint32_t *regbuf = (const uint32_t *) buf; - const uint32_t *regend = regbuf + (size >> 2); - const uint32_t *next_fire; - int fire_count = 0; - int ret; - int check_2d_cmd = 1; - - - - if ((ret = via_verify_command_stream(regbuf, size, dev, 0))) - return ret; - - next_fire = dev_priv->fire_offsets[fire_count]; - while (regbuf != regend) { - if ( *regbuf == HALCYON_HEADER2 ) { - - regbuf++; - check_2d_cmd = ( *regbuf != HALCYON_SUB_ADDR0 ); - VIA_WRITE(HC_REG_TRANS_SET + HC_REG_BASE, *regbuf++); - - } else if ( check_2d_cmd && ((*regbuf & HALCYON_HEADER1MASK) == HALCYON_HEADER1 )) { - - register uint32_t addr = ( (*regbuf++ ) & ~HALCYON_HEADER1MASK) << 2; - VIA_WRITE( addr, *regbuf++ ); - - } else if ( (fire_count < dev_priv->num_fire_offsets) && - (regbuf == next_fire) && - (( *regbuf & HALCYON_FIREMASK ) == HALCYON_FIRECMD) ) { - - next_fire = dev_priv->fire_offsets[++fire_count]; - - VIA_WRITE(HC_REG_TRANS_SPACE + HC_REG_BASE, *regbuf++); - - if ( ( regbuf != regend ) && - ((*regbuf & HALCYON_FIREMASK) == HALCYON_FIRECMD)) - regbuf++; - if (( *regbuf & HALCYON_CMDBMASK ) != HC_ACMD_HCmdB ) - check_2d_cmd = 1; - } else { - - VIA_WRITE(HC_REG_TRANS_SPACE + HC_REG_BASE , *regbuf++); - - } - } - return 0; - -} - +extern int +via_parse_command_stream(drm_device_t *dev, const uint32_t * buf, unsigned int size); static int via_dispatch_pci_cmdbuffer(drm_device_t * dev, drm_via_cmdbuffer_t * cmd) { @@ -397,7 +350,12 @@ static int via_dispatch_pci_cmdbuffer(drm_device_t * dev, } if (DRM_COPY_FROM_USER(dev_priv->pci_buf, cmd->buf, cmd->size)) return DRM_ERR(EFAULT); - ret = via_parse_pci_cmdbuffer(dev, dev_priv->pci_buf, cmd->size); + + if ((ret = via_verify_command_stream((uint32_t *)dev_priv->pci_buf, cmd->size, dev, 0))) { + return ret; + } + + ret = via_parse_command_stream(dev, (const uint32_t *)dev_priv->pci_buf, cmd->size); return ret; } @@ -515,7 +473,7 @@ static int via_hook_segment(drm_via_private_t *dev_priv, -int via_wait_idle(drm_via_private_t * dev_priv) +static int via_wait_idle(drm_via_private_t * dev_priv) { int count = 10000000; while (count-- && (VIA_READ(VIA_REG_STATUS) & diff --git a/shared/via_drm.h b/shared/via_drm.h index f3b4a28a..4588c9bd 100644 --- a/shared/via_drm.h +++ b/shared/via_drm.h @@ -73,6 +73,8 @@ #define DRM_VIA_FLUSH 0x09 #define DRM_VIA_PCICMD 0x0a #define DRM_VIA_CMDBUF_SIZE 0x0b +#define NOT_USED +#define DRM_VIA_WAIT_IRQ 0x0d #define DRM_IOCTL_VIA_ALLOCMEM DRM_IOWR(DRM_COMMAND_BASE + DRM_VIA_ALLOCMEM, drm_via_mem_t) #define DRM_IOCTL_VIA_FREEMEM DRM_IOW( DRM_COMMAND_BASE + DRM_VIA_FREEMEM, drm_via_mem_t) @@ -86,6 +88,7 @@ #define DRM_IOCTL_VIA_PCICMD DRM_IOW( DRM_COMMAND_BASE + DRM_VIA_PCICMD, drm_via_cmdbuffer_t) #define DRM_IOCTL_VIA_CMDBUF_SIZE DRM_IOWR( DRM_COMMAND_BASE + DRM_VIA_CMDBUF_SIZE, \ drm_via_cmdbuf_size_t) +#define DRM_IOCTL_VIA_WAIT_IRQ DRM_IOWR( DRM_COMMAND_BASE + DRM_VIA_WAIT_IRQ, drm_via_irqwait_t) /* Indices into buf.Setup where various bits of state are mirrored per * context and per buffer. These can be fired at the card as a unit, @@ -200,6 +203,26 @@ typedef struct _drm_via_cmdbuf_size { uint32_t size; } drm_via_cmdbuf_size_t; +typedef enum { + VIA_IRQ_ABSOLUTE = 0x0, + VIA_IRQ_RELATIVE = 0x1, + VIA_IRQ_SIGNAL = 0x10000000, + VIA_IRQ_FORCE_SEQUENCE = 0x20000000 +} via_irq_seq_type_t; + +#define VIA_IRQ_FLAGS_MASK 0xF0000000 + +struct drm_via_wait_irq_request{ + unsigned irq; + via_irq_seq_type_t type; + uint32_t sequence; + uint32_t signal; +}; + +typedef union drm_via_irqwait { + struct drm_via_wait_irq_request request; + struct drm_wait_vblank_reply reply; +} drm_via_irqwait_t; #ifdef __KERNEL__ @@ -214,6 +237,7 @@ int via_cmdbuffer(DRM_IOCTL_ARGS); int via_flush_ioctl(DRM_IOCTL_ARGS); int via_pci_cmdbuffer(DRM_IOCTL_ARGS); int via_cmdbuf_size(DRM_IOCTL_ARGS); +int via_wait_irq(DRM_IOCTL_ARGS); #endif #endif /* _VIA_DRM_H_ */ diff --git a/shared/via_drv.h b/shared/via_drv.h index 5d90c9ca..8227b735 100644 --- a/shared/via_drv.h +++ b/shared/via_drv.h @@ -29,12 +29,23 @@ #define VIA_PCI_BUF_SIZE 120000 #define VIA_FIRE_BUF_SIZE 2048 +#define VIA_NUM_IRQS 2 + typedef struct drm_via_ring_buffer { drm_map_t map; char *virtual_start; } drm_via_ring_buffer_t; +typedef uint32_t maskarray_t[5]; + +typedef struct drm_via_irq { + atomic_t irq_received; + uint32_t pending_mask; + uint32_t enable_mask; + wait_queue_head_t irq_queue; +} drm_via_irq_t; + typedef struct drm_via_private { drm_via_sarea_t *sarea_priv; drm_map_t *sarea; @@ -57,6 +68,12 @@ typedef struct drm_via_private { char pci_buf[VIA_PCI_BUF_SIZE]; const uint32_t *fire_offsets[VIA_FIRE_BUF_SIZE]; uint32_t num_fire_offsets; + int pro_group_a; + drm_via_irq_t via_irqs[VIA_NUM_IRQS]; + unsigned num_irqs; + maskarray_t *irq_masks; + uint32_t irq_enable_mask; + uint32_t irq_pending_mask; } drm_via_private_t; /* VIA MMIO register access */ @@ -82,10 +99,13 @@ extern void via_driver_irq_postinstall(drm_device_t * dev); extern void via_driver_irq_uninstall(drm_device_t * dev); extern int via_dma_cleanup(drm_device_t * dev); -extern int via_wait_idle(drm_via_private_t * dev_priv); extern int via_driver_dma_quiescent(drm_device_t * dev); extern void via_init_command_verifier( void ); extern int via_fb_free(drm_via_mem_t * mem); extern int via_fb_alloc(drm_via_mem_t * mem); +extern void via_init_futex(drm_via_private_t *dev_priv); +extern void via_cleanup_futex(drm_via_private_t *dev_priv); +extern void via_release_futex(drm_via_private_t *dev_priv, int context); + #endif diff --git a/shared/via_irq.c b/shared/via_irq.c index 1e22a773..f61081c2 100644 --- a/shared/via_irq.c +++ b/shared/via_irq.c @@ -2,6 +2,7 @@ * * Copyright 2004 BEAM Ltd. * Copyright 2002 Tungsten Graphics, Inc. + * Copyright 2005 Thomas Hellstrom. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a @@ -10,27 +11,28 @@ * 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 - * BEAM LTD, TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * BEAM LTD, 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. * - * Authors: + * Authors: * Terry Barnaby <terry1@beam.ltd.uk> * Keith Whitwell <keith@tungstengraphics.com> + * Thomas Hellstrom <unichrome@shipmail.org> * - * - * This code provides standard DRM access to the Via CLE266's Vertical blank - * interrupt. + * This code provides standard DRM access to the Via Unichrome / Pro Vertical blank + * interrupt, as well as an infrastructure to handle other interrupts of the chip. + * The refresh rate is also calculated for video playback sync purposes. */ #include "via.h" @@ -43,39 +45,91 @@ /* VIA_REG_INTERRUPT */ #define VIA_IRQ_GLOBAL (1 << 31) -#define VIA_IRQ_VBI_ENABLE (1 << 19) -#define VIA_IRQ_SEC_VBI_ENABLE (1 << 17) -#define VIA_IRQ_SEC_VBI_PENDING (1 << 15) -#define VIA_IRQ_VBI_PENDING (1 << 3) +#define VIA_IRQ_VBLANK_ENABLE (1 << 19) +#define VIA_IRQ_VBLANK_PENDING (1 << 3) +#define VIA_IRQ_HQV0_ENABLE (1 << 11) +#define VIA_IRQ_HQV1_ENABLE (1 << 25) +#define VIA_IRQ_HQV0_PENDING (1 << 9) +#define VIA_IRQ_HQV1_PENDING (1 << 10) + +/* + * Device-specific IRQs go here. This type might need to be extended with + * the register if there are multiple IRQ control registers. + * Currently we activate the HQV interrupts of Unichrome Pro group A. + */ + +static maskarray_t via_pro_group_a_irqs[] = { + {VIA_IRQ_HQV0_ENABLE, VIA_IRQ_HQV0_PENDING, 0x000003D0, 0x00008010, 0x00000000 }, + {VIA_IRQ_HQV1_ENABLE, VIA_IRQ_HQV1_PENDING, 0x000013D0, 0x00008010, 0x00000000 }}; +static int via_num_pro_group_a = sizeof(via_pro_group_a_irqs)/sizeof(maskarray_t); + +static maskarray_t via_unichrome_irqs[] = {}; +static int via_num_unichrome = sizeof(via_unichrome_irqs)/sizeof(maskarray_t); +static unsigned time_diff(struct timeval *now,struct timeval *then) +{ + return (now->tv_usec >= then->tv_usec) ? + now->tv_usec - then->tv_usec : + 1000000 - (then->tv_usec - now->tv_usec); +} irqreturn_t via_driver_irq_handler(DRM_IRQ_ARGS) { drm_device_t *dev = (drm_device_t *) arg; drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; - u32 status = VIA_READ(VIA_REG_INTERRUPT); + u32 status; + int handled = 0; + struct timeval cur_vblank; + drm_via_irq_t *cur_irq = dev_priv->via_irqs; + int i; - if (status & VIA_IRQ_VBI_PENDING) { + status = VIA_READ(VIA_REG_INTERRUPT); + if (status & VIA_IRQ_VBLANK_PENDING) { atomic_inc(&dev->vbl_received); + if (!(atomic_read(&dev->vbl_received) & 0x0F)) { + do_gettimeofday(&cur_vblank); + if (dev_priv->last_vblank_valid) { + dev_priv->usec_per_vblank = + time_diff( &cur_vblank,&dev_priv->last_vblank) >> 4; + } + dev_priv->last_vblank = cur_vblank; + dev_priv->last_vblank_valid = 1; + } + if (!(atomic_read(&dev->vbl_received) & 0xFF)) { + DRM_DEBUG("US per vblank is: %u\n", + dev_priv->usec_per_vblank); + } DRM_WAKEUP(&dev->vbl_queue); - DRM(vbl_send_signals) (dev); - - VIA_WRITE(VIA_REG_INTERRUPT, status); - return IRQ_HANDLED; + DRM(vbl_send_signals)( dev ); + handled = 1; } + -#if 0 - if (status & VIA_IRQ_SEC_VBI_PENDING) { - atomic_inc(&dev->sec_vbl_received); - DRM_WAKEUP(&dev->sec_vbl_queue); - DRM(vbl_send_signals)(dev); /* KW: Need a parameter here? */ - handled = IRQ_HANDLED; - } -#endif + for (i=0; i<dev_priv->num_irqs; ++i) { + if (status & cur_irq->pending_mask) { + atomic_inc( &cur_irq->irq_received ); + DRM_WAKEUP( &cur_irq->irq_queue ); + handled = 1; + VIA_WRITE(VIA_REG_INTERRUPT, status); + if (handled) + return IRQ_HANDLED; + else + return IRQ_NONE; + + } + cur_irq++; + } + + /* Acknowlege interrupts */ VIA_WRITE(VIA_REG_INTERRUPT, status); - return IRQ_NONE; + + + if (handled) + return IRQ_HANDLED; + else + return IRQ_NONE; } static __inline__ void viadrv_acknowledge_irqs(drm_via_private_t * dev_priv) @@ -83,9 +137,10 @@ static __inline__ void viadrv_acknowledge_irqs(drm_via_private_t * dev_priv) u32 status; if (dev_priv) { - /* Acknowlege interrupts ?? */ + /* Acknowlege interrupts */ status = VIA_READ(VIA_REG_INTERRUPT); - VIA_WRITE(VIA_REG_INTERRUPT, status | VIA_IRQ_VBI_PENDING); + VIA_WRITE(VIA_REG_INTERRUPT, status | + dev_priv->irq_pending_mask); } } @@ -105,34 +160,96 @@ int via_driver_vblank_wait(drm_device_t * dev, unsigned int *sequence) /* Assume that the user has missed the current sequence number * by about a day rather than she wants to wait for years - * using vertical blanks... + * using vertical blanks... */ + DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ, (((cur_vblank = atomic_read(&dev->vbl_received)) - *sequence) <= (1 << 23))); - + *sequence = cur_vblank; return ret; } +static int +via_driver_irq_wait(drm_device_t * dev, unsigned int irq, int force_sequence, + unsigned int *sequence) +{ + drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + unsigned int cur_irq_sequence; + drm_via_irq_t *cur_irq = dev_priv->via_irqs; + int ret = 0; + maskarray_t *masks = dev_priv->irq_masks; + + DRM_DEBUG("%s\n", __FUNCTION__); + + if (!dev_priv) { + DRM_ERROR("%s called with no initialization\n", __FUNCTION__); + return DRM_ERR(EINVAL); + } + + if (irq >= dev_priv->num_irqs ) { + DRM_ERROR("%s Trying to wait on unknown irq %d\n", __FUNCTION__, irq); + return DRM_ERR(EINVAL); + } + + cur_irq += irq; + + if (masks[irq][2] && !force_sequence) { + DRM_WAIT_ON(ret, cur_irq->irq_queue, 3 * DRM_HZ, + ((VIA_READ(masks[irq][2]) & masks[irq][3]) == masks[irq][4])); + cur_irq_sequence = atomic_read(&cur_irq->irq_received); + } else { + DRM_WAIT_ON(ret, cur_irq->irq_queue, 3 * DRM_HZ, + (((cur_irq_sequence = atomic_read(&cur_irq->irq_received)) - + *sequence) <= (1 << 23))); + } + *sequence = cur_irq_sequence; + return ret; +} + + /* * drm_dma.h hooks */ + void via_driver_irq_preinstall(drm_device_t * dev) { drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; u32 status; + drm_via_irq_t *cur_irq = dev_priv->via_irqs; + int i; DRM_DEBUG("driver_irq_preinstall: dev_priv: %p\n", dev_priv); if (dev_priv) { + + dev_priv->irq_enable_mask = VIA_IRQ_VBLANK_ENABLE; + dev_priv->irq_pending_mask = VIA_IRQ_VBLANK_PENDING; + + dev_priv->irq_masks = (dev_priv->pro_group_a) ? + via_pro_group_a_irqs : via_unichrome_irqs; + dev_priv->num_irqs = (dev_priv->pro_group_a) ? + via_num_pro_group_a : via_num_unichrome; + + for(i=0; i < dev_priv->num_irqs; ++i) { + atomic_set(&cur_irq->irq_received, 0); + cur_irq->enable_mask = dev_priv->irq_masks[i][0]; + cur_irq->pending_mask = dev_priv->irq_masks[i][1]; + DRM_INIT_WAITQUEUE( &cur_irq->irq_queue ); + dev_priv->irq_enable_mask |= cur_irq->enable_mask; + dev_priv->irq_pending_mask |= cur_irq->pending_mask; + cur_irq++; + + DRM_DEBUG("Initializing IRQ %d\n", i); + } + dev_priv->last_vblank_valid = 0; - DRM_DEBUG("mmio: %p\n", dev_priv->mmio); - status = VIA_READ(VIA_REG_INTERRUPT); - DRM_DEBUG("intreg: %x\n", status & VIA_IRQ_VBI_ENABLE); // Clear VSync interrupt regs - VIA_WRITE(VIA_REG_INTERRUPT, status & ~VIA_IRQ_VBI_ENABLE); - + status = VIA_READ(VIA_REG_INTERRUPT); + VIA_WRITE(VIA_REG_INTERRUPT, status & + ~(dev_priv->irq_enable_mask)); + /* Clear bits if they're already high */ viadrv_acknowledge_irqs(dev_priv); } @@ -147,12 +264,13 @@ void via_driver_irq_postinstall(drm_device_t * dev) if (dev_priv) { status = VIA_READ(VIA_REG_INTERRUPT); VIA_WRITE(VIA_REG_INTERRUPT, status | VIA_IRQ_GLOBAL - | VIA_IRQ_VBI_ENABLE); + | dev_priv->irq_enable_mask); + /* Some magic, oh for some data sheets ! */ VIA_WRITE8(0x83d4, 0x11); VIA_WRITE8(0x83d5, VIA_READ8(0x83d5) | 0x30); - + } } @@ -170,7 +288,60 @@ void via_driver_irq_uninstall(drm_device_t * dev) VIA_WRITE8(0x83d5, VIA_READ8(0x83d5) & ~0x30); status = VIA_READ(VIA_REG_INTERRUPT); - VIA_WRITE(VIA_REG_INTERRUPT, status & ~VIA_IRQ_VBI_ENABLE); + VIA_WRITE(VIA_REG_INTERRUPT, status & + ~(VIA_IRQ_VBLANK_ENABLE | dev_priv->irq_enable_mask)); } } +int via_wait_irq(DRM_IOCTL_ARGS) +{ + DRM_DEVICE; + + drm_via_irqwait_t __user *argp = (void __user *)data; + drm_via_irqwait_t irqwait; + struct timeval now; + int ret = 0; + drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + drm_via_irq_t *cur_irq = dev_priv->via_irqs; + int force_sequence; + + if (!dev->irq) + return DRM_ERR(EINVAL); + + DRM_COPY_FROM_USER_IOCTL(irqwait, argp, sizeof(irqwait)); + if (irqwait.request.irq >= dev_priv->num_irqs) { + DRM_ERROR("%s Trying to wait on unknown irq %d\n", __FUNCTION__, + irqwait.request.irq); + return DRM_ERR(EINVAL); + } + + cur_irq += irqwait.request.irq; + + switch (irqwait.request.type & ~VIA_IRQ_FLAGS_MASK) { + case VIA_IRQ_RELATIVE: + irqwait.request.sequence += atomic_read(&cur_irq->irq_received); + irqwait.request.type &= ~_DRM_VBLANK_RELATIVE; + case VIA_IRQ_ABSOLUTE: + break; + default: + return DRM_ERR(EINVAL); + } + + if (irqwait.request.type & VIA_IRQ_SIGNAL) { + DRM_ERROR("%s Signals on Via IRQs not implemented yet.\n", + __FUNCTION__); + return DRM_ERR(EINVAL); + } + + force_sequence = (irqwait.request.type & VIA_IRQ_FORCE_SEQUENCE); + + ret = via_driver_irq_wait(dev, irqwait.request.irq, force_sequence, + &irqwait.request.sequence); + do_gettimeofday(&now); + irqwait.reply.tval_sec = now.tv_sec; + irqwait.reply.tval_usec = now.tv_usec; + + DRM_COPY_TO_USER_IOCTL(argp, irqwait, sizeof(irqwait)); + + return ret; +} diff --git a/shared/via_map.c b/shared/via_map.c index 178284c1..af850d73 100644 --- a/shared/via_map.c +++ b/shared/via_map.c @@ -29,7 +29,6 @@ int via_do_init_map(drm_device_t * dev, drm_via_init_t * init) { drm_via_private_t *dev_priv; - unsigned int i; DRM_DEBUG("%s\n", __FUNCTION__); @@ -69,10 +68,8 @@ int via_do_init_map(drm_device_t * dev, drm_via_init_t * init) dev_priv->agpAddr = init->agpAddr; - for (i = 0; i < VIA_NR_XVMC_LOCKS; ++i) { - DRM_INIT_WAITQUEUE(&(dev_priv->decoder_queue[i])); - XVMCLOCKPTR(dev_priv->sarea_priv, i)->lock = 0; - } + via_init_futex( dev_priv ); + dev_priv->pro_group_a = (dev->pdev->device == 0x3118); dev->dev_private = (void *)dev_priv; @@ -113,31 +110,3 @@ int via_map_init(DRM_IOCTL_ARGS) return -EINVAL; } - -int via_decoder_futex(DRM_IOCTL_ARGS) -{ - DRM_DEVICE; - drm_via_futex_t fx; - volatile int *lock; - drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; - drm_via_sarea_t *sAPriv = dev_priv->sarea_priv; - int ret = 0; - - DRM_COPY_FROM_USER_IOCTL(fx, (drm_via_futex_t *) data, sizeof(fx)); - - if (fx.lock > VIA_NR_XVMC_LOCKS) - return -EFAULT; - - lock = (int *)XVMCLOCKPTR(sAPriv, fx.lock); - - switch (fx.func) { - case VIA_FUTEX_WAIT: - DRM_WAIT_ON(ret, dev_priv->decoder_queue[fx.lock], - (fx.ms / 10) * (DRM_HZ / 100), *lock != fx.val); - return ret; - case VIA_FUTEX_WAKE: - DRM_WAKEUP(&(dev_priv->decoder_queue[fx.lock])); - return 0; - } - return 0; -} diff --git a/shared/via_mm.c b/shared/via_mm.c index fadfbfba..d450cdd5 100644 --- a/shared/via_mm.c +++ b/shared/via_mm.c @@ -133,9 +133,7 @@ int via_init_context(struct drm_device *dev, int context) int via_final_context(struct drm_device *dev, int context) { int i; - volatile int *lock; drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; - drm_via_sarea_t *sAPriv = dev_priv->sarea_priv; for (i = 0; i < MAX_CONTEXT; i++) if (global_ppriv[i].used && @@ -172,20 +170,6 @@ int via_final_context(struct drm_device *dev, int context) global_ppriv[i].used = 0; } - /* - * Release futex locks. - */ - - for (i=0; i < VIA_NR_XVMC_LOCKS; ++i) { - lock = (int *) XVMCLOCKPTR(sAPriv, i); - if ( (_DRM_LOCKING_CONTEXT( *lock ) == context)) { - if (_DRM_LOCK_IS_HELD( *lock ) && (*lock & _DRM_LOCK_CONT)) { - DRM_WAKEUP( &(dev_priv->decoder_queue[i])); - } - *lock = 0; - } - } - #if defined(__linux__) /* Linux specific until context tracking code gets ported to BSD */ /* Last context, perform cleanup */ @@ -193,6 +177,7 @@ int via_final_context(struct drm_device *dev, int context) if (dev->irq) DRM(irq_uninstall) (dev); + via_cleanup_futex(dev_priv); via_do_cleanup_map(dev); } #endif diff --git a/shared/via_verifier.c b/shared/via_verifier.c index edc9decd..742d7cea 100644 --- a/shared/via_verifier.c +++ b/shared/via_verifier.c @@ -1,5 +1,6 @@ /* * Copyright 2004 The Unichrome Project. All Rights Reserved. + * Copyright 2005 Thomas Hellstrom. 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"), @@ -15,19 +16,18 @@ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL - * THE UNICHROME PROJECT, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * THE AUTHOR(S), AND/OR THE COPYRIGHT HOLDER(S) 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. * - * Author: Thomas Hellström 2004. + * Author: Thomas Hellstrom 2004, 2005. * This code was written using docs obtained under NDA from VIA Inc. * * Don't run this code directly on an AGP buffer. Due to cache problems it will * be very slow. */ - #include "via.h" #include "via_3d_reg.h" #include "drmP.h" @@ -678,16 +678,61 @@ via_check_header2( uint32_t const **buffer, const uint32_t *buf_end, return state_command; } +static __inline__ verifier_state_t +via_parse_header2( drm_via_private_t *dev_priv, uint32_t const **buffer, const uint32_t *buf_end, + int *fire_count) +{ + uint32_t cmd; + const uint32_t *buf = *buffer; + const uint32_t *next_fire; + int burst = 0; + + next_fire = dev_priv->fire_offsets[*fire_count]; + buf++; + cmd = (*buf & 0xFFFF0000) >> 16; + VIA_WRITE(HC_REG_TRANS_SET + HC_REG_BASE, *buf++); + switch(cmd) { + case HC_ParaType_CmdVdata: + while ((*fire_count < dev_priv->num_fire_offsets) && + (*buf & HC_ACMD_MASK) == HC_ACMD_HCmdB ) { + while(buf <= next_fire) { + VIA_WRITE(HC_REG_TRANS_SPACE + HC_REG_BASE + (burst & 63), *buf++); + burst += 4; + } + if ( ( buf < buf_end ) && ((*buf & HALCYON_FIREMASK) == HALCYON_FIRECMD)) + buf++; + + if (++(*fire_count) < dev_priv->num_fire_offsets) + next_fire = dev_priv->fire_offsets[*fire_count]; + } + break; + default: + while(buf < buf_end) { + + if ( *buf == HC_HEADER2 || + (*buf & HALCYON_HEADER1MASK) == HALCYON_HEADER1 || + (*buf & VIA_VIDEOMASK) == VIA_VIDEO_HEADER5 || + (*buf & VIA_VIDEOMASK) == VIA_VIDEO_HEADER6 ) break; + + VIA_WRITE(HC_REG_TRANS_SPACE + HC_REG_BASE + (burst & 63), *buf++); + burst +=4; + } + } + *buffer = buf; + return state_command; +} + + static __inline__ int verify_mmio_address( uint32_t address) { if ((address > 0x3FF) && (address < 0xC00 )) { - DRM_ERROR("Invalid HALCYON_HEADER1 command. " + DRM_ERROR("Invalid VIDEO DMA command. " "Attempt to access 3D- or command burst area.\n"); return 1; - } else if (address > 0xCFF ) { - DRM_ERROR("Invalid HALCYON_HEADER1 command. " + } else if (address > 0x13FF ) { + DRM_ERROR("Invalid VIDEO DMA command. " "Attempt to access VGA registers.\n"); return 1; } @@ -747,6 +792,22 @@ via_check_header1( uint32_t const **buffer, const uint32_t *buf_end ) } static __inline__ verifier_state_t +via_parse_header1( drm_via_private_t *dev_priv, uint32_t const **buffer, const uint32_t *buf_end ) +{ + register uint32_t cmd; + const uint32_t *buf = *buffer; + + while (buf < buf_end) { + cmd = *buf; + if ((cmd & HALCYON_HEADER1MASK) != HALCYON_HEADER1) break; + VIA_WRITE( (cmd & ~HALCYON_HEADER1MASK) << 2, *++buf); + buf++; + } + *buffer = buf; + return state_command; +} + +static __inline__ verifier_state_t via_check_vheader5( uint32_t const **buffer, const uint32_t *buf_end ) { uint32_t data; @@ -772,7 +833,7 @@ via_check_vheader5( uint32_t const **buffer, const uint32_t *buf_end ) } if (eat_words(&buf, buf_end, data)) return state_error; - if (verify_video_tail(&buf, buf_end, 4 - (data & 3))) + if ((data & 3) && verify_video_tail(&buf, buf_end, 4 - (data & 3))) return state_error; *buffer = buf; return state_command; @@ -780,19 +841,36 @@ via_check_vheader5( uint32_t const **buffer, const uint32_t *buf_end ) } static __inline__ verifier_state_t +via_parse_vheader5( drm_via_private_t *dev_priv, uint32_t const **buffer, const uint32_t *buf_end ) +{ + uint32_t addr, count, i; + const uint32_t *buf = *buffer; + + addr = *buf++ & ~VIA_VIDEOMASK; + i = count = *buf; + buf += 3; + while(i--) { + VIA_WRITE(addr, *buf++); + } + if (count & 3) buf += 4 - (count & 3); + *buffer = buf; + return state_command; +} + + +static __inline__ verifier_state_t via_check_vheader6( uint32_t const **buffer, const uint32_t *buf_end ) { uint32_t data; const uint32_t *buf = *buffer; uint32_t i; - DRM_ERROR("H6\n"); if (buf_end - buf < 4) { DRM_ERROR("Illegal termination of video header6 command\n"); return state_error; } - + buf++; data = *buf++; if (*buf++ != 0x00F60000) { DRM_ERROR("Illegal header6 header data\n"); @@ -804,18 +882,40 @@ via_check_vheader6( uint32_t const **buffer, const uint32_t *buf_end ) } if ((buf_end - buf) < (data << 1)) { DRM_ERROR("Illegal termination of video header6 command\n"); + return state_error; } for (i=0; i<data; ++i) { if (verify_mmio_address(*buf++)) return state_error; buf++; } - if (verify_video_tail(&buf, buf_end, 4 - ((data << 1) & 3))) + data <<= 1; + if ((data & 3) && verify_video_tail(&buf, buf_end, 4 - (data & 3))) return state_error; *buffer = buf; return state_command; } +static __inline__ verifier_state_t +via_parse_vheader6( drm_via_private_t *dev_priv, uint32_t const **buffer, const uint32_t *buf_end ) +{ + + uint32_t addr, count, i; + const uint32_t *buf = *buffer; + + i = count = *++buf; + buf += 3; + while(i--) { + addr = *buf++; + VIA_WRITE(addr, *buf++); + } + count <<= 1; + if (count & 3) buf += 4 - (count & 3); + *buffer = buf; + return state_command; +} + + int via_verify_command_stream(const uint32_t * buf, unsigned int size, drm_device_t *dev, @@ -828,7 +928,7 @@ via_verify_command_stream(const uint32_t * buf, unsigned int size, drm_device_t uint32_t cmd; const uint32_t *buf_end = buf + ( size >> 2 ); verifier_state_t state = state_command; - + int pro_group_a = dev_priv->pro_group_a; hc_state->dev = dev; hc_state->unfinished = no_sequence; @@ -841,7 +941,7 @@ via_verify_command_stream(const uint32_t * buf, unsigned int size, drm_device_t switch (state) { case state_header2: - state = via_check_header2( &buf, buf_end, hc_state ); + state = via_check_header2( &buf, buf_end, hc_state ); break; case state_header1: state = via_check_header1( &buf, buf_end ); @@ -857,9 +957,9 @@ via_verify_command_stream(const uint32_t * buf, unsigned int size, drm_device_t state = state_header2; else if ((cmd & HALCYON_HEADER1MASK) == HALCYON_HEADER1) state = state_header1; - else if ((cmd & VIA_VIDEOMASK) == VIA_VIDEO_HEADER5) + else if (pro_group_a && (cmd & VIA_VIDEOMASK) == VIA_VIDEO_HEADER5) state = state_vheader5; - else if ((cmd & VIA_VIDEOMASK) == VIA_VIDEO_HEADER6) + else if (pro_group_a && (cmd & VIA_VIDEOMASK) == VIA_VIDEO_HEADER6) state = state_vheader6; else { DRM_ERROR("Invalid / Unimplemented DMA HEADER command. 0x%x\n", @@ -880,6 +980,59 @@ via_verify_command_stream(const uint32_t * buf, unsigned int size, drm_device_t return 0; } +int +via_parse_command_stream(drm_device_t *dev, const uint32_t * buf, unsigned int size) +{ + + drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; + uint32_t cmd; + const uint32_t *buf_end = buf + ( size >> 2 ); + verifier_state_t state = state_command; + int fire_count = 0; + + while (buf < buf_end) { + + switch (state) { + case state_header2: + state = via_parse_header2( dev_priv, &buf, buf_end, &fire_count ); + break; + case state_header1: + state = via_parse_header1( dev_priv, &buf, buf_end ); + break; + case state_vheader5: + state = via_parse_vheader5( dev_priv, &buf, buf_end ); + break; + case state_vheader6: + state = via_parse_vheader6( dev_priv, &buf, buf_end ); + break; + case state_command: + if (HALCYON_HEADER2 == (cmd = *buf)) + state = state_header2; + else if ((cmd & HALCYON_HEADER1MASK) == HALCYON_HEADER1) + state = state_header1; + else if ((cmd & VIA_VIDEOMASK) == VIA_VIDEO_HEADER5) + state = state_vheader5; + else if ((cmd & VIA_VIDEOMASK) == VIA_VIDEO_HEADER6) + state = state_vheader6; + else { + DRM_ERROR("Invalid / Unimplemented DMA HEADER command. 0x%x\n", + cmd); + state = state_error; + } + break; + case state_error: + default: + return DRM_ERR(EINVAL); + } + } + if (state == state_error) { + return DRM_ERR(EINVAL); + } + return 0; +} + + + static void setup_hazard_table(hz_init_t init_table[], hazard_t table[], int size) { |