via_irq.c 11 KB
Newer Older
Dave Airlie's avatar
Dave Airlie committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
/* via_irq.c
 *
 * Copyright 2004 BEAM Ltd.
 * Copyright 2002 Tungsten Graphics, Inc.
 * Copyright 2005 Thomas Hellstrom.
 * All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * BEAM LTD, TUNGSTEN GRAPHICS  AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR
 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 * Authors:
 *    Terry Barnaby <terry1@beam.ltd.uk>
 *    Keith Whitwell <keith@tungstengraphics.com>
 *    Thomas Hellstrom <unichrome@shipmail.org>
 *
 * This code provides standard DRM access to the Via Unichrome / Pro Vertical blank
 * interrupt, as well as an infrastructure to handle other interrupts of the chip.
 * The refresh rate is also calculated for video playback sync purposes.
 */

#include "drmP.h"
#include "drm.h"
#include "via_drm.h"
#include "via_drv.h"

#define VIA_REG_INTERRUPT       0x200

/* VIA_REG_INTERRUPT */
#define VIA_IRQ_GLOBAL          (1 << 31)
#define VIA_IRQ_VBLANK_ENABLE   (1 << 19)
#define VIA_IRQ_VBLANK_PENDING  (1 << 3)
#define VIA_IRQ_HQV0_ENABLE     (1 << 11)
#define VIA_IRQ_HQV1_ENABLE     (1 << 25)
#define VIA_IRQ_HQV0_PENDING    (1 << 9)
#define VIA_IRQ_HQV1_PENDING    (1 << 10)
53 54 55 56 57 58 59 60 61
#define VIA_IRQ_DMA0_DD_ENABLE  (1 << 20)
#define VIA_IRQ_DMA0_TD_ENABLE  (1 << 21)
#define VIA_IRQ_DMA1_DD_ENABLE  (1 << 22)
#define VIA_IRQ_DMA1_TD_ENABLE  (1 << 23)
#define VIA_IRQ_DMA0_DD_PENDING (1 << 4)
#define VIA_IRQ_DMA0_TD_PENDING (1 << 5)
#define VIA_IRQ_DMA1_DD_PENDING (1 << 6)
#define VIA_IRQ_DMA1_TD_PENDING (1 << 7)

Dave Airlie's avatar
Dave Airlie committed
62 63 64 65

/*
 * Device-specific IRQs go here. This type might need to be extended with
 * the register if there are multiple IRQ control registers.
66
 * Currently we activate the HQV interrupts of  Unichrome Pro group A.
Dave Airlie's avatar
Dave Airlie committed
67 68 69
 */

static maskarray_t via_pro_group_a_irqs[] = {
70 71 72
	{VIA_IRQ_HQV0_ENABLE, VIA_IRQ_HQV0_PENDING, 0x000003D0, 0x00008010,
	 0x00000000},
	{VIA_IRQ_HQV1_ENABLE, VIA_IRQ_HQV1_PENDING, 0x000013D0, 0x00008010,
73 74 75 76 77
	 0x00000000},
	{VIA_IRQ_DMA0_TD_ENABLE, VIA_IRQ_DMA0_TD_PENDING, VIA_PCI_DMA_CSR0,
	 VIA_DMA_CSR_TA | VIA_DMA_CSR_TD, 0x00000008},
	{VIA_IRQ_DMA1_TD_ENABLE, VIA_IRQ_DMA1_TD_PENDING, VIA_PCI_DMA_CSR1,
	 VIA_DMA_CSR_TA | VIA_DMA_CSR_TD, 0x00000008},
78 79 80
};
static int via_num_pro_group_a =
    sizeof(via_pro_group_a_irqs) / sizeof(maskarray_t);
81
static int via_irqmap_pro_group_a[] = {0, 1, -1, 2, -1, 3};
82

83 84 85 86 87 88
static maskarray_t via_unichrome_irqs[] = {
	{VIA_IRQ_DMA0_TD_ENABLE, VIA_IRQ_DMA0_TD_PENDING, VIA_PCI_DMA_CSR0,
	 VIA_DMA_CSR_TA | VIA_DMA_CSR_TD, 0x00000008},
	{VIA_IRQ_DMA1_TD_ENABLE, VIA_IRQ_DMA1_TD_PENDING, VIA_PCI_DMA_CSR1,
	 VIA_DMA_CSR_TA | VIA_DMA_CSR_TD, 0x00000008}
};
89
static int via_num_unichrome = sizeof(via_unichrome_irqs) / sizeof(maskarray_t);
90
static int via_irqmap_unichrome[] = {-1, -1, -1, 0, -1, 1};
91 92

static unsigned time_diff(struct timeval *now, struct timeval *then)
Dave Airlie's avatar
Dave Airlie committed
93
{
94 95 96
	return (now->tv_usec >= then->tv_usec) ?
	    now->tv_usec - then->tv_usec :
	    1000000 - (then->tv_usec - now->tv_usec);
Dave Airlie's avatar
Dave Airlie committed
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
}

irqreturn_t via_driver_irq_handler(DRM_IRQ_ARGS)
{
	drm_device_t *dev = (drm_device_t *) arg;
	drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private;
	u32 status;
	int handled = 0;
	struct timeval cur_vblank;
	drm_via_irq_t *cur_irq = dev_priv->via_irqs;
	int i;

	status = VIA_READ(VIA_REG_INTERRUPT);
	if (status & VIA_IRQ_VBLANK_PENDING) {
		atomic_inc(&dev->vbl_received);
112
		if (!(atomic_read(&dev->vbl_received) & 0x0F)) {
Dave Airlie's avatar
Dave Airlie committed
113
			do_gettimeofday(&cur_vblank);
114 115 116 117
			if (dev_priv->last_vblank_valid) {
				dev_priv->usec_per_vblank =
				    time_diff(&cur_vblank,
					      &dev_priv->last_vblank) >> 4;
Dave Airlie's avatar
Dave Airlie committed
118 119 120
			}
			dev_priv->last_vblank = cur_vblank;
			dev_priv->last_vblank_valid = 1;
121 122
		}
		if (!(atomic_read(&dev->vbl_received) & 0xFF)) {
Dave Airlie's avatar
Dave Airlie committed
123
			DRM_DEBUG("US per vblank is: %u\n",
124
				  dev_priv->usec_per_vblank);
Dave Airlie's avatar
Dave Airlie committed
125 126 127 128 129 130
		}
		DRM_WAKEUP(&dev->vbl_queue);
		drm_vbl_send_signals(dev);
		handled = 1;
	}

131
	for (i = 0; i < dev_priv->num_irqs; ++i) {
Dave Airlie's avatar
Dave Airlie committed
132
		if (status & cur_irq->pending_mask) {
133 134
			atomic_inc(&cur_irq->irq_received);
			DRM_WAKEUP(&cur_irq->irq_queue);
Dave Airlie's avatar
Dave Airlie committed
135
			handled = 1;
136 137 138 139 140
			if (dev_priv->irq_map[drm_via_irq_dma0_td] == i) {
				via_dmablit_handler(dev, 0, 1);
			} else if (dev_priv->irq_map[drm_via_irq_dma1_td] == i) {
				via_dmablit_handler(dev, 1, 1);
			}
Dave Airlie's avatar
Dave Airlie committed
141 142 143
		}
		cur_irq++;
	}
144

Dave Airlie's avatar
Dave Airlie committed
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
	/* Acknowlege interrupts */
	VIA_WRITE(VIA_REG_INTERRUPT, status);

	if (handled)
		return IRQ_HANDLED;
	else
		return IRQ_NONE;
}

static __inline__ void viadrv_acknowledge_irqs(drm_via_private_t * dev_priv)
{
	u32 status;

	if (dev_priv) {
		/* Acknowlege interrupts */
		status = VIA_READ(VIA_REG_INTERRUPT);
161
		VIA_WRITE(VIA_REG_INTERRUPT, status |
Dave Airlie's avatar
Dave Airlie committed
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
			  dev_priv->irq_pending_mask);
	}
}

int via_driver_vblank_wait(drm_device_t * dev, unsigned int *sequence)
{
	drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private;
	unsigned int cur_vblank;
	int ret = 0;

	DRM_DEBUG("viadrv_vblank_wait\n");
	if (!dev_priv) {
		DRM_ERROR("%s called with no initialization\n", __FUNCTION__);
		return -EINVAL;
	}

	viadrv_acknowledge_irqs(dev_priv);

	/* Assume that the user has missed the current sequence number
	 * by about a day rather than she wants to wait for years
	 * using vertical blanks...
	 */

	DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ,
		    (((cur_vblank = atomic_read(&dev->vbl_received)) -
		      *sequence) <= (1 << 23)));
188

Dave Airlie's avatar
Dave Airlie committed
189 190 191 192
	*sequence = cur_vblank;
	return ret;
}

193
static int
Dave Airlie's avatar
Dave Airlie committed
194 195 196 197 198 199 200
via_driver_irq_wait(drm_device_t * dev, unsigned int irq, int force_sequence,
		    unsigned int *sequence)
{
	drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private;
	unsigned int cur_irq_sequence;
	drm_via_irq_t *cur_irq = dev_priv->via_irqs;
	int ret = 0;
201
	maskarray_t *masks;
202
	int real_irq;
Dave Airlie's avatar
Dave Airlie committed
203 204 205 206 207 208 209 210

	DRM_DEBUG("%s\n", __FUNCTION__);

	if (!dev_priv) {
		DRM_ERROR("%s called with no initialization\n", __FUNCTION__);
		return DRM_ERR(EINVAL);
	}

211
	if (irq >= drm_via_irq_num) {
212 213
		DRM_ERROR("%s Trying to wait on unknown irq %d\n", __FUNCTION__,
			  irq);
Dave Airlie's avatar
Dave Airlie committed
214 215
		return DRM_ERR(EINVAL);
	}
216

217 218 219 220 221 222 223
	real_irq = dev_priv->irq_map[irq];

	if (real_irq < 0) {
		DRM_ERROR("%s Video IRQ %d not available on this hardware.\n",
			  __FUNCTION__, irq);
		return DRM_ERR(EINVAL);
	}
224 225

	masks = dev_priv->irq_masks;
226
	cur_irq += real_irq;
Dave Airlie's avatar
Dave Airlie committed
227

228
	if (masks[real_irq][2] && !force_sequence) {
Dave Airlie's avatar
Dave Airlie committed
229
		DRM_WAIT_ON(ret, cur_irq->irq_queue, 3 * DRM_HZ,
230 231
			    ((VIA_READ(masks[irq][2]) & masks[irq][3]) ==
			     masks[irq][4]));
Dave Airlie's avatar
Dave Airlie committed
232 233 234
		cur_irq_sequence = atomic_read(&cur_irq->irq_received);
	} else {
		DRM_WAIT_ON(ret, cur_irq->irq_queue, 3 * DRM_HZ,
235 236 237
			    (((cur_irq_sequence =
			       atomic_read(&cur_irq->irq_received)) -
			      *sequence) <= (1 << 23)));
Dave Airlie's avatar
Dave Airlie committed
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
	}
	*sequence = cur_irq_sequence;
	return ret;
}

/*
 * drm_dma.h hooks
 */

void via_driver_irq_preinstall(drm_device_t * dev)
{
	drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private;
	u32 status;
	drm_via_irq_t *cur_irq = dev_priv->via_irqs;
	int i;

	DRM_DEBUG("driver_irq_preinstall: dev_priv: %p\n", dev_priv);
	if (dev_priv) {

		dev_priv->irq_enable_mask = VIA_IRQ_VBLANK_ENABLE;
		dev_priv->irq_pending_mask = VIA_IRQ_VBLANK_PENDING;

		dev_priv->irq_masks = (dev_priv->pro_group_a) ?
261
		    via_pro_group_a_irqs : via_unichrome_irqs;
Dave Airlie's avatar
Dave Airlie committed
262
		dev_priv->num_irqs = (dev_priv->pro_group_a) ?
263
		    via_num_pro_group_a : via_num_unichrome;
264 265
		dev_priv->irq_map = (dev_priv->pro_group_a) ?
			via_irqmap_pro_group_a : via_irqmap_unichrome;
266 267

		for (i = 0; i < dev_priv->num_irqs; ++i) {
Dave Airlie's avatar
Dave Airlie committed
268
			atomic_set(&cur_irq->irq_received, 0);
269
			cur_irq->enable_mask = dev_priv->irq_masks[i][0];
Dave Airlie's avatar
Dave Airlie committed
270
			cur_irq->pending_mask = dev_priv->irq_masks[i][1];
271
			DRM_INIT_WAITQUEUE(&cur_irq->irq_queue);
Dave Airlie's avatar
Dave Airlie committed
272 273 274
			dev_priv->irq_enable_mask |= cur_irq->enable_mask;
			dev_priv->irq_pending_mask |= cur_irq->pending_mask;
			cur_irq++;
275

Dave Airlie's avatar
Dave Airlie committed
276 277
			DRM_DEBUG("Initializing IRQ %d\n", i);
		}
278 279

		dev_priv->last_vblank_valid = 0;
Dave Airlie's avatar
Dave Airlie committed
280

281
		/* Clear VSync interrupt regs */
Dave Airlie's avatar
Dave Airlie committed
282
		status = VIA_READ(VIA_REG_INTERRUPT);
283
		VIA_WRITE(VIA_REG_INTERRUPT, status &
Dave Airlie's avatar
Dave Airlie committed
284
			  ~(dev_priv->irq_enable_mask));
285

Dave Airlie's avatar
Dave Airlie committed
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
		/* Clear bits if they're already high */
		viadrv_acknowledge_irqs(dev_priv);
	}
}

void via_driver_irq_postinstall(drm_device_t * dev)
{
	drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private;
	u32 status;

	DRM_DEBUG("via_driver_irq_postinstall\n");
	if (dev_priv) {
		status = VIA_READ(VIA_REG_INTERRUPT);
		VIA_WRITE(VIA_REG_INTERRUPT, status | VIA_IRQ_GLOBAL
			  | dev_priv->irq_enable_mask);

		/* Some magic, oh for some data sheets ! */

		VIA_WRITE8(0x83d4, 0x11);
		VIA_WRITE8(0x83d5, VIA_READ8(0x83d5) | 0x30);
306

Dave Airlie's avatar
Dave Airlie committed
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
	}
}

void via_driver_irq_uninstall(drm_device_t * dev)
{
	drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private;
	u32 status;

	DRM_DEBUG("driver_irq_uninstall)\n");
	if (dev_priv) {

		/* Some more magic, oh for some data sheets ! */

		VIA_WRITE8(0x83d4, 0x11);
		VIA_WRITE8(0x83d5, VIA_READ8(0x83d5) & ~0x30);

		status = VIA_READ(VIA_REG_INTERRUPT);
324
		VIA_WRITE(VIA_REG_INTERRUPT, status &
Dave Airlie's avatar
Dave Airlie committed
325 326 327 328 329 330
			  ~(VIA_IRQ_VBLANK_ENABLE | dev_priv->irq_enable_mask));
	}
}

int via_wait_irq(DRM_IOCTL_ARGS)
{
331
	DRM_DEVICE;
Dave Airlie's avatar
Dave Airlie committed
332 333 334 335 336 337 338 339 340 341 342 343 344
	drm_via_irqwait_t __user *argp = (void __user *)data;
	drm_via_irqwait_t irqwait;
	struct timeval now;
	int ret = 0;
	drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private;
	drm_via_irq_t *cur_irq = dev_priv->via_irqs;
	int force_sequence;

	if (!dev->irq)
		return DRM_ERR(EINVAL);

	DRM_COPY_FROM_USER_IOCTL(irqwait, argp, sizeof(irqwait));
	if (irqwait.request.irq >= dev_priv->num_irqs) {
345
		DRM_ERROR("%s Trying to wait on unknown irq %d\n", __FUNCTION__,
Dave Airlie's avatar
Dave Airlie committed
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362
			  irqwait.request.irq);
		return DRM_ERR(EINVAL);
	}

	cur_irq += irqwait.request.irq;

	switch (irqwait.request.type & ~VIA_IRQ_FLAGS_MASK) {
	case VIA_IRQ_RELATIVE:
		irqwait.request.sequence += atomic_read(&cur_irq->irq_received);
		irqwait.request.type &= ~_DRM_VBLANK_RELATIVE;
	case VIA_IRQ_ABSOLUTE:
		break;
	default:
		return DRM_ERR(EINVAL);
	}

	if (irqwait.request.type & VIA_IRQ_SIGNAL) {
363
		DRM_ERROR("%s Signals on Via IRQs not implemented yet.\n",
Dave Airlie's avatar
Dave Airlie committed
364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
			  __FUNCTION__);
		return DRM_ERR(EINVAL);
	}

	force_sequence = (irqwait.request.type & VIA_IRQ_FORCE_SEQUENCE);

	ret = via_driver_irq_wait(dev, irqwait.request.irq, force_sequence,
				  &irqwait.request.sequence);
	do_gettimeofday(&now);
	irqwait.reply.tval_sec = now.tv_sec;
	irqwait.reply.tval_usec = now.tv_usec;

	DRM_COPY_TO_USER_IOCTL(argp, irqwait, sizeof(irqwait));

	return ret;
}