/* mga_irq.c -- IRQ handling for radeon -*- linux-c -*-1*/2/*3* Copyright (C) The Weather Channel, Inc. 2002. All Rights Reserved.4*5* The Weather Channel (TM) funded Tungsten Graphics to develop the6* initial release of the Radeon 8500 driver under the XFree86 license.7* This notice must be preserved.8*9* Permission is hereby granted, free of charge, to any person obtaining a10* copy of this software and associated documentation files (the "Software"),11* to deal in the Software without restriction, including without limitation12* the rights to use, copy, modify, merge, publish, distribute, sublicense,13* and/or sell copies of the Software, and to permit persons to whom the14* Software is furnished to do so, subject to the following conditions:15*16* The above copyright notice and this permission notice (including the next17* paragraph) shall be included in all copies or substantial portions of the18* Software.19*20* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR21* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,22* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL23* PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR24* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,25* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER26* DEALINGS IN THE SOFTWARE.27*28* Authors:29* Keith Whitwell <[email protected]>30* Eric Anholt <[email protected]>31*/3233#include "drmP.h"34#include "drm.h"35#include "mga_drm.h"36#include "mga_drv.h"3738u32 mga_get_vblank_counter(struct drm_device *dev, int crtc)39{40const drm_mga_private_t *const dev_priv =41(drm_mga_private_t *) dev->dev_private;4243if (crtc != 0)44return 0;4546return atomic_read(&dev_priv->vbl_received);47}484950irqreturn_t mga_driver_irq_handler(DRM_IRQ_ARGS)51{52struct drm_device *dev = (struct drm_device *) arg;53drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private;54int status;55int handled = 0;5657status = MGA_READ(MGA_STATUS);5859/* VBLANK interrupt */60if (status & MGA_VLINEPEN) {61MGA_WRITE(MGA_ICLEAR, MGA_VLINEICLR);62atomic_inc(&dev_priv->vbl_received);63drm_handle_vblank(dev, 0);64handled = 1;65}6667/* SOFTRAP interrupt */68if (status & MGA_SOFTRAPEN) {69const u32 prim_start = MGA_READ(MGA_PRIMADDRESS);70const u32 prim_end = MGA_READ(MGA_PRIMEND);717273MGA_WRITE(MGA_ICLEAR, MGA_SOFTRAPICLR);7475/* In addition to clearing the interrupt-pending bit, we76* have to write to MGA_PRIMEND to re-start the DMA operation.77*/78if ((prim_start & ~0x03) != (prim_end & ~0x03))79MGA_WRITE(MGA_PRIMEND, prim_end);8081atomic_inc(&dev_priv->last_fence_retired);82DRM_WAKEUP(&dev_priv->fence_queue);83handled = 1;84}8586if (handled)87return IRQ_HANDLED;88return IRQ_NONE;89}9091int mga_enable_vblank(struct drm_device *dev, int crtc)92{93drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private;9495if (crtc != 0) {96DRM_ERROR("tried to enable vblank on non-existent crtc %d\n",97crtc);98return 0;99}100101MGA_WRITE(MGA_IEN, MGA_VLINEIEN | MGA_SOFTRAPEN);102return 0;103}104105106void mga_disable_vblank(struct drm_device *dev, int crtc)107{108if (crtc != 0) {109DRM_ERROR("tried to disable vblank on non-existent crtc %d\n",110crtc);111}112113/* Do *NOT* disable the vertical refresh interrupt. MGA doesn't have114* a nice hardware counter that tracks the number of refreshes when115* the interrupt is disabled, and the kernel doesn't know the refresh116* rate to calculate an estimate.117*/118/* MGA_WRITE(MGA_IEN, MGA_VLINEIEN | MGA_SOFTRAPEN); */119}120121int mga_driver_fence_wait(struct drm_device *dev, unsigned int *sequence)122{123drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private;124unsigned int cur_fence;125int ret = 0;126127/* Assume that the user has missed the current sequence number128* by about a day rather than she wants to wait for years129* using fences.130*/131DRM_WAIT_ON(ret, dev_priv->fence_queue, 3 * DRM_HZ,132(((cur_fence = atomic_read(&dev_priv->last_fence_retired))133- *sequence) <= (1 << 23)));134135*sequence = cur_fence;136137return ret;138}139140void mga_driver_irq_preinstall(struct drm_device *dev)141{142drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private;143144/* Disable *all* interrupts */145MGA_WRITE(MGA_IEN, 0);146/* Clear bits if they're already high */147MGA_WRITE(MGA_ICLEAR, ~0);148}149150int mga_driver_irq_postinstall(struct drm_device *dev)151{152drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private;153154DRM_INIT_WAITQUEUE(&dev_priv->fence_queue);155156/* Turn on soft trap interrupt. Vertical blank interrupts are enabled157* in mga_enable_vblank.158*/159MGA_WRITE(MGA_IEN, MGA_SOFTRAPEN);160return 0;161}162163void mga_driver_irq_uninstall(struct drm_device *dev)164{165drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private;166if (!dev_priv)167return;168169/* Disable *all* interrupts */170MGA_WRITE(MGA_IEN, 0);171172dev->irq_enabled = 0;173}174175176