mga_irq.c revision 1.2
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
43u32 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
55irqreturn_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
96int 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
111void 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
126int 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
145void 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
155int 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
168void 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