aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/drm/via_irq.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/char/drm/via_irq.c')
-rw-r--r--drivers/char/drm/via_irq.c339
1 files changed, 339 insertions, 0 deletions
diff --git a/drivers/char/drm/via_irq.c b/drivers/char/drm/via_irq.c
new file mode 100644
index 000000000000..e8027f3a93b0
--- /dev/null
+++ b/drivers/char/drm/via_irq.c
@@ -0,0 +1,339 @@
1/* via_irq.c
2 *
3 * Copyright 2004 BEAM Ltd.
4 * Copyright 2002 Tungsten Graphics, Inc.
5 * Copyright 2005 Thomas Hellstrom.
6 * All Rights Reserved.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice (including the next
16 * paragraph) shall be included in all copies or substantial portions of the
17 * Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22 * BEAM LTD, TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
23 * DAMAGES OR
24 * 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 OTHER
26 * DEALINGS IN THE SOFTWARE.
27 *
28 * Authors:
29 * Terry Barnaby <terry1@beam.ltd.uk>
30 * Keith Whitwell <keith@tungstengraphics.com>
31 * Thomas Hellstrom <unichrome@shipmail.org>
32 *
33 * This code provides standard DRM access to the Via Unichrome / Pro Vertical blank
34 * interrupt, as well as an infrastructure to handle other interrupts of the chip.
35 * The refresh rate is also calculated for video playback sync purposes.
36 */
37
38#include "drmP.h"
39#include "drm.h"
40#include "via_drm.h"
41#include "via_drv.h"
42
43#define VIA_REG_INTERRUPT 0x200
44
45/* VIA_REG_INTERRUPT */
46#define VIA_IRQ_GLOBAL (1 << 31)
47#define VIA_IRQ_VBLANK_ENABLE (1 << 19)
48#define VIA_IRQ_VBLANK_PENDING (1 << 3)
49#define VIA_IRQ_HQV0_ENABLE (1 << 11)
50#define VIA_IRQ_HQV1_ENABLE (1 << 25)
51#define VIA_IRQ_HQV0_PENDING (1 << 9)
52#define VIA_IRQ_HQV1_PENDING (1 << 10)
53
54/*
55 * Device-specific IRQs go here. This type might need to be extended with
56 * the register if there are multiple IRQ control registers.
57 * Currently we activate the HQV interrupts of Unichrome Pro group A.
58 */
59
60static maskarray_t via_pro_group_a_irqs[] = {
61 {VIA_IRQ_HQV0_ENABLE, VIA_IRQ_HQV0_PENDING, 0x000003D0, 0x00008010, 0x00000000 },
62 {VIA_IRQ_HQV1_ENABLE, VIA_IRQ_HQV1_PENDING, 0x000013D0, 0x00008010, 0x00000000 }};
63static int via_num_pro_group_a = sizeof(via_pro_group_a_irqs)/sizeof(maskarray_t);
64
65static maskarray_t via_unichrome_irqs[] = {};
66static int via_num_unichrome = sizeof(via_unichrome_irqs)/sizeof(maskarray_t);
67
68
69static unsigned time_diff(struct timeval *now,struct timeval *then)
70{
71 return (now->tv_usec >= then->tv_usec) ?
72 now->tv_usec - then->tv_usec :
73 1000000 - (then->tv_usec - now->tv_usec);
74}
75
76irqreturn_t via_driver_irq_handler(DRM_IRQ_ARGS)
77{
78 drm_device_t *dev = (drm_device_t *) arg;
79 drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private;
80 u32 status;
81 int handled = 0;
82 struct timeval cur_vblank;
83 drm_via_irq_t *cur_irq = dev_priv->via_irqs;
84 int i;
85
86 status = VIA_READ(VIA_REG_INTERRUPT);
87 if (status & VIA_IRQ_VBLANK_PENDING) {
88 atomic_inc(&dev->vbl_received);
89 if (!(atomic_read(&dev->vbl_received) & 0x0F)) {
90 do_gettimeofday(&cur_vblank);
91 if (dev_priv->last_vblank_valid) {
92 dev_priv->usec_per_vblank =
93 time_diff( &cur_vblank,&dev_priv->last_vblank) >> 4;
94 }
95 dev_priv->last_vblank = cur_vblank;
96 dev_priv->last_vblank_valid = 1;
97 }
98 if (!(atomic_read(&dev->vbl_received) & 0xFF)) {
99 DRM_DEBUG("US per vblank is: %u\n",
100 dev_priv->usec_per_vblank);
101 }
102 DRM_WAKEUP(&dev->vbl_queue);
103 drm_vbl_send_signals(dev);
104 handled = 1;
105 }
106
107
108 for (i=0; i<dev_priv->num_irqs; ++i) {
109 if (status & cur_irq->pending_mask) {
110 atomic_inc( &cur_irq->irq_received );
111 DRM_WAKEUP( &cur_irq->irq_queue );
112 handled = 1;
113 }
114 cur_irq++;
115 }
116
117 /* Acknowlege interrupts */
118 VIA_WRITE(VIA_REG_INTERRUPT, status);
119
120
121 if (handled)
122 return IRQ_HANDLED;
123 else
124 return IRQ_NONE;
125}
126
127static __inline__ void viadrv_acknowledge_irqs(drm_via_private_t * dev_priv)
128{
129 u32 status;
130
131 if (dev_priv) {
132 /* Acknowlege interrupts */
133 status = VIA_READ(VIA_REG_INTERRUPT);
134 VIA_WRITE(VIA_REG_INTERRUPT, status |
135 dev_priv->irq_pending_mask);
136 }
137}
138
139int via_driver_vblank_wait(drm_device_t * dev, unsigned int *sequence)
140{
141 drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private;
142 unsigned int cur_vblank;
143 int ret = 0;
144
145 DRM_DEBUG("viadrv_vblank_wait\n");
146 if (!dev_priv) {
147 DRM_ERROR("%s called with no initialization\n", __FUNCTION__);
148 return -EINVAL;
149 }
150
151 viadrv_acknowledge_irqs(dev_priv);
152
153 /* Assume that the user has missed the current sequence number
154 * by about a day rather than she wants to wait for years
155 * using vertical blanks...
156 */
157
158 DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ,
159 (((cur_vblank = atomic_read(&dev->vbl_received)) -
160 *sequence) <= (1 << 23)));
161
162 *sequence = cur_vblank;
163 return ret;
164}
165
166static int
167via_driver_irq_wait(drm_device_t * dev, unsigned int irq, int force_sequence,
168 unsigned int *sequence)
169{
170 drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private;
171 unsigned int cur_irq_sequence;
172 drm_via_irq_t *cur_irq = dev_priv->via_irqs;
173 int ret = 0;
174 maskarray_t *masks = dev_priv->irq_masks;
175
176 DRM_DEBUG("%s\n", __FUNCTION__);
177
178 if (!dev_priv) {
179 DRM_ERROR("%s called with no initialization\n", __FUNCTION__);
180 return DRM_ERR(EINVAL);
181 }
182
183 if (irq >= dev_priv->num_irqs ) {
184 DRM_ERROR("%s Trying to wait on unknown irq %d\n", __FUNCTION__, irq);
185 return DRM_ERR(EINVAL);
186 }
187
188 cur_irq += irq;
189
190 if (masks[irq][2] && !force_sequence) {
191 DRM_WAIT_ON(ret, cur_irq->irq_queue, 3 * DRM_HZ,
192 ((VIA_READ(masks[irq][2]) & masks[irq][3]) == masks[irq][4]));
193 cur_irq_sequence = atomic_read(&cur_irq->irq_received);
194 } else {
195 DRM_WAIT_ON(ret, cur_irq->irq_queue, 3 * DRM_HZ,
196 (((cur_irq_sequence = atomic_read(&cur_irq->irq_received)) -
197 *sequence) <= (1 << 23)));
198 }
199 *sequence = cur_irq_sequence;
200 return ret;
201}
202
203
204/*
205 * drm_dma.h hooks
206 */
207
208void via_driver_irq_preinstall(drm_device_t * dev)
209{
210 drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private;
211 u32 status;
212 drm_via_irq_t *cur_irq = dev_priv->via_irqs;
213 int i;
214
215 DRM_DEBUG("driver_irq_preinstall: dev_priv: %p\n", dev_priv);
216 if (dev_priv) {
217
218 dev_priv->irq_enable_mask = VIA_IRQ_VBLANK_ENABLE;
219 dev_priv->irq_pending_mask = VIA_IRQ_VBLANK_PENDING;
220
221 dev_priv->irq_masks = (dev_priv->pro_group_a) ?
222 via_pro_group_a_irqs : via_unichrome_irqs;
223 dev_priv->num_irqs = (dev_priv->pro_group_a) ?
224 via_num_pro_group_a : via_num_unichrome;
225
226 for(i=0; i < dev_priv->num_irqs; ++i) {
227 atomic_set(&cur_irq->irq_received, 0);
228 cur_irq->enable_mask = dev_priv->irq_masks[i][0];
229 cur_irq->pending_mask = dev_priv->irq_masks[i][1];
230 DRM_INIT_WAITQUEUE( &cur_irq->irq_queue );
231 dev_priv->irq_enable_mask |= cur_irq->enable_mask;
232 dev_priv->irq_pending_mask |= cur_irq->pending_mask;
233 cur_irq++;
234
235 DRM_DEBUG("Initializing IRQ %d\n", i);
236 }
237
238 dev_priv->last_vblank_valid = 0;
239
240 // Clear VSync interrupt regs
241 status = VIA_READ(VIA_REG_INTERRUPT);
242 VIA_WRITE(VIA_REG_INTERRUPT, status &
243 ~(dev_priv->irq_enable_mask));
244
245 /* Clear bits if they're already high */
246 viadrv_acknowledge_irqs(dev_priv);
247 }
248}
249
250void via_driver_irq_postinstall(drm_device_t * dev)
251{
252 drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private;
253 u32 status;
254
255 DRM_DEBUG("via_driver_irq_postinstall\n");
256 if (dev_priv) {
257 status = VIA_READ(VIA_REG_INTERRUPT);
258 VIA_WRITE(VIA_REG_INTERRUPT, status | VIA_IRQ_GLOBAL
259 | dev_priv->irq_enable_mask);
260
261 /* Some magic, oh for some data sheets ! */
262
263 VIA_WRITE8(0x83d4, 0x11);
264 VIA_WRITE8(0x83d5, VIA_READ8(0x83d5) | 0x30);
265
266 }
267}
268
269void via_driver_irq_uninstall(drm_device_t * dev)
270{
271 drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private;
272 u32 status;
273
274 DRM_DEBUG("driver_irq_uninstall)\n");
275 if (dev_priv) {
276
277 /* Some more magic, oh for some data sheets ! */
278
279 VIA_WRITE8(0x83d4, 0x11);
280 VIA_WRITE8(0x83d5, VIA_READ8(0x83d5) & ~0x30);
281
282 status = VIA_READ(VIA_REG_INTERRUPT);
283 VIA_WRITE(VIA_REG_INTERRUPT, status &
284 ~(VIA_IRQ_VBLANK_ENABLE | dev_priv->irq_enable_mask));
285 }
286}
287
288int via_wait_irq(DRM_IOCTL_ARGS)
289{
290 drm_file_t *priv = filp->private_data;
291 drm_device_t *dev = priv->head->dev;
292 drm_via_irqwait_t __user *argp = (void __user *)data;
293 drm_via_irqwait_t irqwait;
294 struct timeval now;
295 int ret = 0;
296 drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private;
297 drm_via_irq_t *cur_irq = dev_priv->via_irqs;
298 int force_sequence;
299
300 if (!dev->irq)
301 return DRM_ERR(EINVAL);
302
303 DRM_COPY_FROM_USER_IOCTL(irqwait, argp, sizeof(irqwait));
304 if (irqwait.request.irq >= dev_priv->num_irqs) {
305 DRM_ERROR("%s Trying to wait on unknown irq %d\n", __FUNCTION__,
306 irqwait.request.irq);
307 return DRM_ERR(EINVAL);
308 }
309
310 cur_irq += irqwait.request.irq;
311
312 switch (irqwait.request.type & ~VIA_IRQ_FLAGS_MASK) {
313 case VIA_IRQ_RELATIVE:
314 irqwait.request.sequence += atomic_read(&cur_irq->irq_received);
315 irqwait.request.type &= ~_DRM_VBLANK_RELATIVE;
316 case VIA_IRQ_ABSOLUTE:
317 break;
318 default:
319 return DRM_ERR(EINVAL);
320 }
321
322 if (irqwait.request.type & VIA_IRQ_SIGNAL) {
323 DRM_ERROR("%s Signals on Via IRQs not implemented yet.\n",
324 __FUNCTION__);
325 return DRM_ERR(EINVAL);
326 }
327
328 force_sequence = (irqwait.request.type & VIA_IRQ_FORCE_SEQUENCE);
329
330 ret = via_driver_irq_wait(dev, irqwait.request.irq, force_sequence,
331 &irqwait.request.sequence);
332 do_gettimeofday(&now);
333 irqwait.reply.tval_sec = now.tv_sec;
334 irqwait.reply.tval_usec = now.tv_usec;
335
336 DRM_COPY_TO_USER_IOCTL(argp, irqwait, sizeof(irqwait));
337
338 return ret;
339}