Merge master.kernel.org:/pub/scm/linux/kernel/git/davem/sparc-2.6
[powerpc.git] / drivers / char / drm / mga_irq.c
index bc0b6b5..52eaa4e 100644 (file)
@@ -41,15 +41,40 @@ irqreturn_t mga_driver_irq_handler( DRM_IRQ_ARGS )
        drm_mga_private_t *dev_priv = 
           (drm_mga_private_t *)dev->dev_private;
        int status;
+       int handled = 0;
+
+       status = MGA_READ(MGA_STATUS);
 
-       status = MGA_READ( MGA_STATUS );
-       
        /* VBLANK interrupt */
        if ( status & MGA_VLINEPEN ) {
                MGA_WRITE( MGA_ICLEAR, MGA_VLINEICLR );
                atomic_inc(&dev->vbl_received);
                DRM_WAKEUP(&dev->vbl_queue);
-               drm_vbl_send_signals( dev );
+               drm_vbl_send_signals(dev);
+               handled = 1;
+       }
+
+       /* SOFTRAP interrupt */
+       if (status & MGA_SOFTRAPEN) {
+               const u32 prim_start = MGA_READ(MGA_PRIMADDRESS);
+               const u32 prim_end   = MGA_READ(MGA_PRIMEND);
+
+
+               MGA_WRITE(MGA_ICLEAR, MGA_SOFTRAPICLR);
+
+               /* In addition to clearing the interrupt-pending bit, we
+                * have to write to MGA_PRIMEND to re-start the DMA operation.
+                */
+               if ( (prim_start & ~0x03) != (prim_end & ~0x03) ) {
+                       MGA_WRITE(MGA_PRIMEND, prim_end);
+               }
+
+               atomic_inc(&dev_priv->last_fence_retired);
+               DRM_WAKEUP(&dev_priv->fence_queue);
+               handled = 1;
+       }
+
+       if ( handled ) {
                return IRQ_HANDLED;
        }
        return IRQ_NONE;
@@ -73,9 +98,28 @@ int mga_driver_vblank_wait(drm_device_t *dev, unsigned int *sequence)
        return ret;
 }
 
-void mga_driver_irq_preinstall( drm_device_t *dev ) {
-       drm_mga_private_t *dev_priv = 
-          (drm_mga_private_t *)dev->dev_private;
+int mga_driver_fence_wait(drm_device_t * dev, unsigned int *sequence)
+{
+       drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private;
+       unsigned int cur_fence;
+       int ret = 0;
+
+       /* Assume that the user has missed the current sequence number
+        * by about a day rather than she wants to wait for years
+        * using fences.
+        */
+       DRM_WAIT_ON(ret, dev_priv->fence_queue, 3 * DRM_HZ,
+                   (((cur_fence = atomic_read(&dev_priv->last_fence_retired))
+                     - *sequence) <= (1 << 23)));
+
+       *sequence = cur_fence;
+
+       return ret;
+}
+
+void mga_driver_irq_preinstall(drm_device_t * dev)
+{
+       drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private;
 
        /* Disable *all* interrupts */
        MGA_WRITE( MGA_IEN, 0 );
@@ -83,12 +127,14 @@ void mga_driver_irq_preinstall( drm_device_t *dev ) {
        MGA_WRITE( MGA_ICLEAR, ~0 );
 }
 
-void mga_driver_irq_postinstall( drm_device_t *dev ) {
-       drm_mga_private_t *dev_priv = 
-          (drm_mga_private_t *)dev->dev_private;
+void mga_driver_irq_postinstall(drm_device_t * dev)
+{
+       drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private;
+
+       DRM_INIT_WAITQUEUE( &dev_priv->fence_queue );
 
-       /* Turn on VBL interrupt */
-       MGA_WRITE( MGA_IEN, MGA_VLINEIEN );
+       /* Turn on vertical blank interrupt and soft trap interrupt. */
+       MGA_WRITE(MGA_IEN, MGA_VLINEIEN | MGA_SOFTRAPEN);
 }
 
 void mga_driver_irq_uninstall( drm_device_t *dev ) {
@@ -98,5 +144,7 @@ void mga_driver_irq_uninstall( drm_device_t *dev ) {
                return;
 
        /* Disable *all* interrupts */
-       MGA_WRITE( MGA_IEN, 0 );
+       MGA_WRITE(MGA_IEN, 0);
+       
+       dev->irq_enabled = 0;
 }