summaryrefslogtreecommitdiff
path: root/shared-core/via_irq.c
diff options
context:
space:
mode:
authorThomas Hellstrom <unichrome@shipmail.org>2005-03-28 21:21:42 +0000
committerThomas Hellstrom <unichrome@shipmail.org>2005-03-28 21:21:42 +0000
commit532ccb98b5f2946f574a747b90c39edbe783f888 (patch)
treecfbdf4e8e2b8e0827b35b92a7e57e7f6104e18f1 /shared-core/via_irq.c
parentc6161eff86b250f3113791edcc162dc97322c401 (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.
Diffstat (limited to 'shared-core/via_irq.c')
-rw-r--r--shared-core/via_irq.c192
1 files changed, 174 insertions, 18 deletions
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;
+}