xref: /netbsd-src/sys/external/bsd/drm2/dist/drm/mga/mga_irq.c (revision 82d56013d7b633d116a93943de88e08335357a7c)
1 /*	$NetBSD: mga_irq.c,v 1.2 2018/08/27 04:58:24 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.2 2018/08/27 04:58:24 riastradh Exp $");
38 
39 #include <drm/drmP.h>
40 #include <drm/mga_drm.h>
41 #include "mga_drv.h"
42 
43 u32 mga_get_vblank_counter(struct drm_device *dev, unsigned int pipe)
44 {
45 	const drm_mga_private_t *const dev_priv =
46 		(drm_mga_private_t *) dev->dev_private;
47 
48 	if (pipe != 0)
49 		return 0;
50 
51 	return atomic_read(&dev_priv->vbl_received);
52 }
53 
54 
55 irqreturn_t mga_driver_irq_handler(int irq, void *arg)
56 {
57 	struct drm_device *dev = (struct drm_device *) arg;
58 	drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private;
59 	int status;
60 	int handled = 0;
61 
62 	status = MGA_READ(MGA_STATUS);
63 
64 	/* VBLANK interrupt */
65 	if (status & MGA_VLINEPEN) {
66 		MGA_WRITE(MGA_ICLEAR, MGA_VLINEICLR);
67 		atomic_inc(&dev_priv->vbl_received);
68 		drm_handle_vblank(dev, 0);
69 		handled = 1;
70 	}
71 
72 	/* SOFTRAP interrupt */
73 	if (status & MGA_SOFTRAPEN) {
74 		const u32 prim_start = MGA_READ(MGA_PRIMADDRESS);
75 		const u32 prim_end = MGA_READ(MGA_PRIMEND);
76 
77 
78 		MGA_WRITE(MGA_ICLEAR, MGA_SOFTRAPICLR);
79 
80 		/* In addition to clearing the interrupt-pending bit, we
81 		 * have to write to MGA_PRIMEND to re-start the DMA operation.
82 		 */
83 		if ((prim_start & ~0x03) != (prim_end & ~0x03))
84 			MGA_WRITE(MGA_PRIMEND, prim_end);
85 
86 		atomic_inc(&dev_priv->last_fence_retired);
87 		wake_up(&dev_priv->fence_queue);
88 		handled = 1;
89 	}
90 
91 	if (handled)
92 		return IRQ_HANDLED;
93 	return IRQ_NONE;
94 }
95 
96 int mga_enable_vblank(struct drm_device *dev, unsigned int pipe)
97 {
98 	drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private;
99 
100 	if (pipe != 0) {
101 		DRM_ERROR("tried to enable vblank on non-existent crtc %u\n",
102 			  pipe);
103 		return 0;
104 	}
105 
106 	MGA_WRITE(MGA_IEN, MGA_VLINEIEN | MGA_SOFTRAPEN);
107 	return 0;
108 }
109 
110 
111 void mga_disable_vblank(struct drm_device *dev, unsigned int pipe)
112 {
113 	if (pipe != 0) {
114 		DRM_ERROR("tried to disable vblank on non-existent crtc %u\n",
115 			  pipe);
116 	}
117 
118 	/* Do *NOT* disable the vertical refresh interrupt.  MGA doesn't have
119 	 * a nice hardware counter that tracks the number of refreshes when
120 	 * the interrupt is disabled, and the kernel doesn't know the refresh
121 	 * rate to calculate an estimate.
122 	 */
123 	/* MGA_WRITE(MGA_IEN, MGA_VLINEIEN | MGA_SOFTRAPEN); */
124 }
125 
126 int mga_driver_fence_wait(struct drm_device *dev, unsigned int *sequence)
127 {
128 	drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private;
129 	unsigned int cur_fence;
130 	int ret = 0;
131 
132 	/* Assume that the user has missed the current sequence number
133 	 * by about a day rather than she wants to wait for years
134 	 * using fences.
135 	 */
136 	DRM_WAIT_ON(ret, dev_priv->fence_queue, 3 * HZ,
137 		    (((cur_fence = atomic_read(&dev_priv->last_fence_retired))
138 		      - *sequence) <= (1 << 23)));
139 
140 	*sequence = cur_fence;
141 
142 	return ret;
143 }
144 
145 void mga_driver_irq_preinstall(struct drm_device *dev)
146 {
147 	drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private;
148 
149 	/* Disable *all* interrupts */
150 	MGA_WRITE(MGA_IEN, 0);
151 	/* Clear bits if they're already high */
152 	MGA_WRITE(MGA_ICLEAR, ~0);
153 }
154 
155 int mga_driver_irq_postinstall(struct drm_device *dev)
156 {
157 	drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private;
158 
159 	init_waitqueue_head(&dev_priv->fence_queue);
160 
161 	/* Turn on soft trap interrupt.  Vertical blank interrupts are enabled
162 	 * in mga_enable_vblank.
163 	 */
164 	MGA_WRITE(MGA_IEN, MGA_SOFTRAPEN);
165 	return 0;
166 }
167 
168 void mga_driver_irq_uninstall(struct drm_device *dev)
169 {
170 	drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private;
171 	if (!dev_priv)
172 		return;
173 
174 	/* Disable *all* interrupts */
175 	MGA_WRITE(MGA_IEN, 0);
176 
177 	dev->irq_enabled = false;
178 }
179