1 /* $NetBSD: mga_irq.c,v 1.3 2021/12/18 23:45:32 riastradh Exp $ */ 2 3 /* mga_irq.c -- IRQ handling for radeon -*- linux-c -*- 4 */ 5 /* 6 * Copyright (C) The Weather Channel, Inc. 2002. All Rights Reserved. 7 * 8 * The Weather Channel (TM) funded Tungsten Graphics to develop the 9 * initial release of the Radeon 8500 driver under the XFree86 license. 10 * This notice must be preserved. 11 * 12 * Permission is hereby granted, free of charge, to any person obtaining a 13 * copy of this software and associated documentation files (the "Software"), 14 * to deal in the Software without restriction, including without limitation 15 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 16 * and/or sell copies of the Software, and to permit persons to whom the 17 * Software is furnished to do so, subject to the following conditions: 18 * 19 * The above copyright notice and this permission notice (including the next 20 * paragraph) shall be included in all copies or substantial portions of the 21 * Software. 22 * 23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 26 * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 27 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 28 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 29 * DEALINGS IN THE SOFTWARE. 30 * 31 * Authors: 32 * Keith Whitwell <keith@tungstengraphics.com> 33 * Eric Anholt <anholt@FreeBSD.org> 34 */ 35 36 #include <sys/cdefs.h> 37 __KERNEL_RCSID(0, "$NetBSD: mga_irq.c,v 1.3 2021/12/18 23:45:32 riastradh Exp $"); 38 39 #include "mga_drv.h" 40 41 u32 mga_get_vblank_counter(struct drm_device *dev, unsigned int pipe) 42 { 43 const drm_mga_private_t *const dev_priv = 44 (drm_mga_private_t *) dev->dev_private; 45 46 if (pipe != 0) 47 return 0; 48 49 return atomic_read(&dev_priv->vbl_received); 50 } 51 52 53 irqreturn_t mga_driver_irq_handler(int irq, void *arg) 54 { 55 struct drm_device *dev = (struct drm_device *) arg; 56 drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private; 57 int status; 58 int handled = 0; 59 60 status = MGA_READ(MGA_STATUS); 61 62 /* VBLANK interrupt */ 63 if (status & MGA_VLINEPEN) { 64 MGA_WRITE(MGA_ICLEAR, MGA_VLINEICLR); 65 atomic_inc(&dev_priv->vbl_received); 66 drm_handle_vblank(dev, 0); 67 handled = 1; 68 } 69 70 /* SOFTRAP interrupt */ 71 if (status & MGA_SOFTRAPEN) { 72 const u32 prim_start = MGA_READ(MGA_PRIMADDRESS); 73 const u32 prim_end = MGA_READ(MGA_PRIMEND); 74 75 76 MGA_WRITE(MGA_ICLEAR, MGA_SOFTRAPICLR); 77 78 /* In addition to clearing the interrupt-pending bit, we 79 * have to write to MGA_PRIMEND to re-start the DMA operation. 80 */ 81 if ((prim_start & ~0x03) != (prim_end & ~0x03)) 82 MGA_WRITE(MGA_PRIMEND, prim_end); 83 84 atomic_inc(&dev_priv->last_fence_retired); 85 wake_up(&dev_priv->fence_queue); 86 handled = 1; 87 } 88 89 if (handled) 90 return IRQ_HANDLED; 91 return IRQ_NONE; 92 } 93 94 int mga_enable_vblank(struct drm_device *dev, unsigned int pipe) 95 { 96 drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private; 97 98 if (pipe != 0) { 99 DRM_ERROR("tried to enable vblank on non-existent crtc %u\n", 100 pipe); 101 return 0; 102 } 103 104 MGA_WRITE(MGA_IEN, MGA_VLINEIEN | MGA_SOFTRAPEN); 105 return 0; 106 } 107 108 109 void mga_disable_vblank(struct drm_device *dev, unsigned int pipe) 110 { 111 if (pipe != 0) { 112 DRM_ERROR("tried to disable vblank on non-existent crtc %u\n", 113 pipe); 114 } 115 116 /* Do *NOT* disable the vertical refresh interrupt. MGA doesn't have 117 * a nice hardware counter that tracks the number of refreshes when 118 * the interrupt is disabled, and the kernel doesn't know the refresh 119 * rate to calculate an estimate. 120 */ 121 /* MGA_WRITE(MGA_IEN, MGA_VLINEIEN | MGA_SOFTRAPEN); */ 122 } 123 124 void mga_driver_fence_wait(struct drm_device *dev, unsigned int *sequence) 125 { 126 drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private; 127 unsigned int cur_fence; 128 129 /* Assume that the user has missed the current sequence number 130 * by about a day rather than she wants to wait for years 131 * using fences. 132 */ 133 wait_event_timeout(dev_priv->fence_queue, 134 (((cur_fence = atomic_read(&dev_priv->last_fence_retired)) 135 - *sequence) <= (1 << 23)), 136 msecs_to_jiffies(3000)); 137 138 *sequence = cur_fence; 139 } 140 141 void mga_driver_irq_preinstall(struct drm_device *dev) 142 { 143 drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private; 144 145 /* Disable *all* interrupts */ 146 MGA_WRITE(MGA_IEN, 0); 147 /* Clear bits if they're already high */ 148 MGA_WRITE(MGA_ICLEAR, ~0); 149 } 150 151 int mga_driver_irq_postinstall(struct drm_device *dev) 152 { 153 drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private; 154 155 init_waitqueue_head(&dev_priv->fence_queue); 156 157 /* Turn on soft trap interrupt. Vertical blank interrupts are enabled 158 * in mga_enable_vblank. 159 */ 160 MGA_WRITE(MGA_IEN, MGA_SOFTRAPEN); 161 return 0; 162 } 163 164 void mga_driver_irq_uninstall(struct drm_device *dev) 165 { 166 drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private; 167 if (!dev_priv) 168 return; 169 170 /* Disable *all* interrupts */ 171 MGA_WRITE(MGA_IEN, 0); 172 173 dev->irq_enabled = false; 174 } 175