mga_dma.c 28.1 KB
Newer Older
L
Linus Torvalds 已提交
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
/* mga_dma.c -- DMA support for mga g200/g400 -*- linux-c -*-
 * Created: Mon Dec 13 01:50:01 1999 by jhartmann@precisioninsight.com
 *
 * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
 * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
 * 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
 * PRECISION INSIGHT 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.
26 27 28 29 30
 */

/**
 * \file mga_dma.c
 * DMA support for MGA G200 / G400.
D
Dave Airlie 已提交
31
 *
32 33 34 35
 * \author Rickard E. (Rik) Faith <faith@valinux.com>
 * \author Jeff Hartmann <jhartmann@valinux.com>
 * \author Keith Whitwell <keith@tungstengraphics.com>
 * \author Gareth Hughes <gareth@valinux.com>
L
Linus Torvalds 已提交
36 37 38 39
 */

#include "drmP.h"
#include "drm.h"
40
#include "drm_sarea.h"
L
Linus Torvalds 已提交
41 42 43 44 45 46
#include "mga_drm.h"
#include "mga_drv.h"

#define MGA_DEFAULT_USEC_TIMEOUT	10000
#define MGA_FREELIST_DEBUG		0

D
Dave Airlie 已提交
47
static int mga_do_cleanup_dma(drm_device_t * dev);
L
Linus Torvalds 已提交
48 49 50 51 52

/* ================================================================
 * Engine control
 */

D
Dave Airlie 已提交
53
int mga_do_wait_for_idle(drm_mga_private_t * dev_priv)
L
Linus Torvalds 已提交
54 55 56
{
	u32 status = 0;
	int i;
D
Dave Airlie 已提交
57
	DRM_DEBUG("\n");
L
Linus Torvalds 已提交
58

D
Dave Airlie 已提交
59 60 61 62
	for (i = 0; i < dev_priv->usec_timeout; i++) {
		status = MGA_READ(MGA_STATUS) & MGA_ENGINE_IDLE_MASK;
		if (status == MGA_ENDPRDMASTS) {
			MGA_WRITE8(MGA_CRTC_INDEX, 0);
L
Linus Torvalds 已提交
63 64
			return 0;
		}
D
Dave Airlie 已提交
65
		DRM_UDELAY(1);
L
Linus Torvalds 已提交
66 67 68
	}

#if MGA_DMA_DEBUG
D
Dave Airlie 已提交
69 70
	DRM_ERROR("failed!\n");
	DRM_INFO("   status=0x%08x\n", status);
L
Linus Torvalds 已提交
71 72 73 74
#endif
	return DRM_ERR(EBUSY);
}

D
Dave Airlie 已提交
75
static int mga_do_dma_reset(drm_mga_private_t * dev_priv)
L
Linus Torvalds 已提交
76 77 78 79
{
	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
	drm_mga_primary_buffer_t *primary = &dev_priv->prim;

D
Dave Airlie 已提交
80
	DRM_DEBUG("\n");
L
Linus Torvalds 已提交
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102

	/* The primary DMA stream should look like new right about now.
	 */
	primary->tail = 0;
	primary->space = primary->size;
	primary->last_flush = 0;

	sarea_priv->last_wrap = 0;

	/* FIXME: Reset counters, buffer ages etc...
	 */

	/* FIXME: What else do we need to reinitialize?  WARP stuff?
	 */

	return 0;
}

/* ================================================================
 * Primary DMA stream
 */

D
Dave Airlie 已提交
103
void mga_do_dma_flush(drm_mga_private_t * dev_priv)
L
Linus Torvalds 已提交
104 105 106 107 108
{
	drm_mga_primary_buffer_t *primary = &dev_priv->prim;
	u32 head, tail;
	u32 status = 0;
	int i;
D
Dave Airlie 已提交
109 110 111 112 113 114 115 116 117
	DMA_LOCALS;
	DRM_DEBUG("\n");

	/* We need to wait so that we can do an safe flush */
	for (i = 0; i < dev_priv->usec_timeout; i++) {
		status = MGA_READ(MGA_STATUS) & MGA_ENGINE_IDLE_MASK;
		if (status == MGA_ENDPRDMASTS)
			break;
		DRM_UDELAY(1);
L
Linus Torvalds 已提交
118 119
	}

D
Dave Airlie 已提交
120 121
	if (primary->tail == primary->last_flush) {
		DRM_DEBUG("   bailing out...\n");
L
Linus Torvalds 已提交
122 123 124 125 126 127 128 129 130
		return;
	}

	tail = primary->tail + dev_priv->primary->offset;

	/* We need to pad the stream between flushes, as the card
	 * actually (partially?) reads the first of these commands.
	 * See page 4-16 in the G400 manual, middle of the page or so.
	 */
D
Dave Airlie 已提交
131
	BEGIN_DMA(1);
L
Linus Torvalds 已提交
132

D
Dave Airlie 已提交
133 134 135
	DMA_BLOCK(MGA_DMAPAD, 0x00000000,
		  MGA_DMAPAD, 0x00000000,
		  MGA_DMAPAD, 0x00000000, MGA_DMAPAD, 0x00000000);
L
Linus Torvalds 已提交
136 137 138 139 140

	ADVANCE_DMA();

	primary->last_flush = primary->tail;

D
Dave Airlie 已提交
141
	head = MGA_READ(MGA_PRIMADDRESS);
L
Linus Torvalds 已提交
142

D
Dave Airlie 已提交
143
	if (head <= tail) {
L
Linus Torvalds 已提交
144 145 146 147 148
		primary->space = primary->size - primary->tail;
	} else {
		primary->space = head - tail;
	}

D
Dave Airlie 已提交
149 150 151
	DRM_DEBUG("   head = 0x%06lx\n", head - dev_priv->primary->offset);
	DRM_DEBUG("   tail = 0x%06lx\n", tail - dev_priv->primary->offset);
	DRM_DEBUG("  space = 0x%06x\n", primary->space);
L
Linus Torvalds 已提交
152 153

	mga_flush_write_combine();
154
	MGA_WRITE(MGA_PRIMEND, tail | dev_priv->dma_access);
L
Linus Torvalds 已提交
155

D
Dave Airlie 已提交
156
	DRM_DEBUG("done.\n");
L
Linus Torvalds 已提交
157 158
}

D
Dave Airlie 已提交
159
void mga_do_dma_wrap_start(drm_mga_private_t * dev_priv)
L
Linus Torvalds 已提交
160 161 162 163
{
	drm_mga_primary_buffer_t *primary = &dev_priv->prim;
	u32 head, tail;
	DMA_LOCALS;
D
Dave Airlie 已提交
164
	DRM_DEBUG("\n");
L
Linus Torvalds 已提交
165 166 167

	BEGIN_DMA_WRAP();

D
Dave Airlie 已提交
168 169 170
	DMA_BLOCK(MGA_DMAPAD, 0x00000000,
		  MGA_DMAPAD, 0x00000000,
		  MGA_DMAPAD, 0x00000000, MGA_DMAPAD, 0x00000000);
L
Linus Torvalds 已提交
171 172 173 174 175 176 177 178 179

	ADVANCE_DMA();

	tail = primary->tail + dev_priv->primary->offset;

	primary->tail = 0;
	primary->last_flush = 0;
	primary->last_wrap++;

D
Dave Airlie 已提交
180
	head = MGA_READ(MGA_PRIMADDRESS);
L
Linus Torvalds 已提交
181

D
Dave Airlie 已提交
182
	if (head == dev_priv->primary->offset) {
L
Linus Torvalds 已提交
183 184 185 186 187
		primary->space = primary->size;
	} else {
		primary->space = head - dev_priv->primary->offset;
	}

D
Dave Airlie 已提交
188 189 190 191
	DRM_DEBUG("   head = 0x%06lx\n", head - dev_priv->primary->offset);
	DRM_DEBUG("   tail = 0x%06x\n", primary->tail);
	DRM_DEBUG("   wrap = %d\n", primary->last_wrap);
	DRM_DEBUG("  space = 0x%06x\n", primary->space);
L
Linus Torvalds 已提交
192 193

	mga_flush_write_combine();
194
	MGA_WRITE(MGA_PRIMEND, tail | dev_priv->dma_access);
L
Linus Torvalds 已提交
195

D
Dave Airlie 已提交
196 197
	set_bit(0, &primary->wrapped);
	DRM_DEBUG("done.\n");
L
Linus Torvalds 已提交
198 199
}

D
Dave Airlie 已提交
200
void mga_do_dma_wrap_end(drm_mga_private_t * dev_priv)
L
Linus Torvalds 已提交
201 202 203 204
{
	drm_mga_primary_buffer_t *primary = &dev_priv->prim;
	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
	u32 head = dev_priv->primary->offset;
D
Dave Airlie 已提交
205
	DRM_DEBUG("\n");
L
Linus Torvalds 已提交
206 207

	sarea_priv->last_wrap++;
D
Dave Airlie 已提交
208
	DRM_DEBUG("   wrap = %d\n", sarea_priv->last_wrap);
L
Linus Torvalds 已提交
209 210

	mga_flush_write_combine();
D
Dave Airlie 已提交
211
	MGA_WRITE(MGA_PRIMADDRESS, head | MGA_DMA_GENERAL);
L
Linus Torvalds 已提交
212

D
Dave Airlie 已提交
213 214
	clear_bit(0, &primary->wrapped);
	DRM_DEBUG("done.\n");
L
Linus Torvalds 已提交
215 216 217 218 219 220 221 222 223 224
}

/* ================================================================
 * Freelist management
 */

#define MGA_BUFFER_USED		~0
#define MGA_BUFFER_FREE		0

#if MGA_FREELIST_DEBUG
D
Dave Airlie 已提交
225
static void mga_freelist_print(drm_device_t * dev)
L
Linus Torvalds 已提交
226 227 228 229
{
	drm_mga_private_t *dev_priv = dev->dev_private;
	drm_mga_freelist_t *entry;

D
Dave Airlie 已提交
230 231 232 233 234 235 236 237 238 239 240
	DRM_INFO("\n");
	DRM_INFO("current dispatch: last=0x%x done=0x%x\n",
		 dev_priv->sarea_priv->last_dispatch,
		 (unsigned int)(MGA_READ(MGA_PRIMADDRESS) -
				dev_priv->primary->offset));
	DRM_INFO("current freelist:\n");

	for (entry = dev_priv->head->next; entry; entry = entry->next) {
		DRM_INFO("   %p   idx=%2d  age=0x%x 0x%06lx\n",
			 entry, entry->buf->idx, entry->age.head,
			 entry->age.head - dev_priv->primary->offset);
L
Linus Torvalds 已提交
241
	}
D
Dave Airlie 已提交
242
	DRM_INFO("\n");
L
Linus Torvalds 已提交
243 244 245
}
#endif

D
Dave Airlie 已提交
246
static int mga_freelist_init(drm_device_t * dev, drm_mga_private_t * dev_priv)
L
Linus Torvalds 已提交
247 248 249 250 251 252
{
	drm_device_dma_t *dma = dev->dma;
	drm_buf_t *buf;
	drm_mga_buf_priv_t *buf_priv;
	drm_mga_freelist_t *entry;
	int i;
D
Dave Airlie 已提交
253
	DRM_DEBUG("count=%d\n", dma->buf_count);
L
Linus Torvalds 已提交
254

D
Dave Airlie 已提交
255 256
	dev_priv->head = drm_alloc(sizeof(drm_mga_freelist_t), DRM_MEM_DRIVER);
	if (dev_priv->head == NULL)
L
Linus Torvalds 已提交
257 258
		return DRM_ERR(ENOMEM);

D
Dave Airlie 已提交
259 260
	memset(dev_priv->head, 0, sizeof(drm_mga_freelist_t));
	SET_AGE(&dev_priv->head->age, MGA_BUFFER_USED, 0);
L
Linus Torvalds 已提交
261

D
Dave Airlie 已提交
262
	for (i = 0; i < dma->buf_count; i++) {
L
Linus Torvalds 已提交
263
		buf = dma->buflist[i];
D
Dave Airlie 已提交
264
		buf_priv = buf->dev_private;
L
Linus Torvalds 已提交
265

D
Dave Airlie 已提交
266 267
		entry = drm_alloc(sizeof(drm_mga_freelist_t), DRM_MEM_DRIVER);
		if (entry == NULL)
L
Linus Torvalds 已提交
268 269
			return DRM_ERR(ENOMEM);

D
Dave Airlie 已提交
270
		memset(entry, 0, sizeof(drm_mga_freelist_t));
L
Linus Torvalds 已提交
271 272 273

		entry->next = dev_priv->head->next;
		entry->prev = dev_priv->head;
D
Dave Airlie 已提交
274
		SET_AGE(&entry->age, MGA_BUFFER_FREE, 0);
L
Linus Torvalds 已提交
275 276
		entry->buf = buf;

D
Dave Airlie 已提交
277
		if (dev_priv->head->next != NULL)
L
Linus Torvalds 已提交
278
			dev_priv->head->next->prev = entry;
D
Dave Airlie 已提交
279
		if (entry->next == NULL)
L
Linus Torvalds 已提交
280 281 282 283 284 285 286 287 288 289 290 291
			dev_priv->tail = entry;

		buf_priv->list_entry = entry;
		buf_priv->discard = 0;
		buf_priv->dispatched = 0;

		dev_priv->head->next = entry;
	}

	return 0;
}

D
Dave Airlie 已提交
292
static void mga_freelist_cleanup(drm_device_t * dev)
L
Linus Torvalds 已提交
293 294 295 296
{
	drm_mga_private_t *dev_priv = dev->dev_private;
	drm_mga_freelist_t *entry;
	drm_mga_freelist_t *next;
D
Dave Airlie 已提交
297
	DRM_DEBUG("\n");
L
Linus Torvalds 已提交
298 299

	entry = dev_priv->head;
D
Dave Airlie 已提交
300
	while (entry) {
L
Linus Torvalds 已提交
301
		next = entry->next;
D
Dave Airlie 已提交
302
		drm_free(entry, sizeof(drm_mga_freelist_t), DRM_MEM_DRIVER);
L
Linus Torvalds 已提交
303 304 305 306 307 308 309 310 311
		entry = next;
	}

	dev_priv->head = dev_priv->tail = NULL;
}

#if 0
/* FIXME: Still needed?
 */
D
Dave Airlie 已提交
312
static void mga_freelist_reset(drm_device_t * dev)
L
Linus Torvalds 已提交
313 314 315 316 317 318
{
	drm_device_dma_t *dma = dev->dma;
	drm_buf_t *buf;
	drm_mga_buf_priv_t *buf_priv;
	int i;

D
Dave Airlie 已提交
319
	for (i = 0; i < dma->buf_count; i++) {
L
Linus Torvalds 已提交
320
		buf = dma->buflist[i];
D
Dave Airlie 已提交
321 322
		buf_priv = buf->dev_private;
		SET_AGE(&buf_priv->list_entry->age, MGA_BUFFER_FREE, 0);
L
Linus Torvalds 已提交
323 324 325 326
	}
}
#endif

D
Dave Airlie 已提交
327
static drm_buf_t *mga_freelist_get(drm_device_t * dev)
L
Linus Torvalds 已提交
328 329 330 331 332 333
{
	drm_mga_private_t *dev_priv = dev->dev_private;
	drm_mga_freelist_t *next;
	drm_mga_freelist_t *prev;
	drm_mga_freelist_t *tail = dev_priv->tail;
	u32 head, wrap;
D
Dave Airlie 已提交
334
	DRM_DEBUG("\n");
L
Linus Torvalds 已提交
335

D
Dave Airlie 已提交
336
	head = MGA_READ(MGA_PRIMADDRESS);
L
Linus Torvalds 已提交
337 338
	wrap = dev_priv->sarea_priv->last_wrap;

D
Dave Airlie 已提交
339 340 341 342 343 344
	DRM_DEBUG("   tail=0x%06lx %d\n",
		  tail->age.head ?
		  tail->age.head - dev_priv->primary->offset : 0,
		  tail->age.wrap);
	DRM_DEBUG("   head=0x%06lx %d\n",
		  head - dev_priv->primary->offset, wrap);
L
Linus Torvalds 已提交
345

D
Dave Airlie 已提交
346
	if (TEST_AGE(&tail->age, head, wrap)) {
L
Linus Torvalds 已提交
347 348 349 350 351
		prev = dev_priv->tail->prev;
		next = dev_priv->tail;
		prev->next = NULL;
		next->prev = next->next = NULL;
		dev_priv->tail = prev;
D
Dave Airlie 已提交
352
		SET_AGE(&next->age, MGA_BUFFER_USED, 0);
L
Linus Torvalds 已提交
353 354 355
		return next->buf;
	}

D
Dave Airlie 已提交
356
	DRM_DEBUG("returning NULL!\n");
L
Linus Torvalds 已提交
357 358 359
	return NULL;
}

D
Dave Airlie 已提交
360
int mga_freelist_put(drm_device_t * dev, drm_buf_t * buf)
L
Linus Torvalds 已提交
361 362 363 364 365
{
	drm_mga_private_t *dev_priv = dev->dev_private;
	drm_mga_buf_priv_t *buf_priv = buf->dev_private;
	drm_mga_freelist_t *head, *entry, *prev;

D
Dave Airlie 已提交
366 367 368
	DRM_DEBUG("age=0x%06lx wrap=%d\n",
		  buf_priv->list_entry->age.head -
		  dev_priv->primary->offset, buf_priv->list_entry->age.wrap);
L
Linus Torvalds 已提交
369 370 371 372

	entry = buf_priv->list_entry;
	head = dev_priv->head;

D
Dave Airlie 已提交
373 374
	if (buf_priv->list_entry->age.head == MGA_BUFFER_USED) {
		SET_AGE(&entry->age, MGA_BUFFER_FREE, 0);
L
Linus Torvalds 已提交
375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393
		prev = dev_priv->tail;
		prev->next = entry;
		entry->prev = prev;
		entry->next = NULL;
	} else {
		prev = head->next;
		head->next = entry;
		prev->prev = entry;
		entry->prev = head;
		entry->next = prev;
	}

	return 0;
}

/* ================================================================
 * DMA initialization, cleanup
 */

D
Dave Airlie 已提交
394
int mga_driver_preinit(drm_device_t * dev, unsigned long flags)
395
{
D
Dave Airlie 已提交
396
	drm_mga_private_t *dev_priv;
397 398 399 400 401 402 403 404 405 406 407 408 409 410

	dev_priv = drm_alloc(sizeof(drm_mga_private_t), DRM_MEM_DRIVER);
	if (!dev_priv)
		return DRM_ERR(ENOMEM);

	dev->dev_private = (void *)dev_priv;
	memset(dev_priv, 0, sizeof(drm_mga_private_t));

	dev_priv->usec_timeout = MGA_DEFAULT_USEC_TIMEOUT;
	dev_priv->chipset = flags;

	return 0;
}

D
Dave Airlie 已提交
411
#if __OS_HAS_AGP
412 413
/**
 * Bootstrap the driver for AGP DMA.
D
Dave Airlie 已提交
414
 *
415 416 417 418 419 420 421 422 423 424 425 426 427 428 429
 * \todo
 * Investigate whether there is any benifit to storing the WARP microcode in
 * AGP memory.  If not, the microcode may as well always be put in PCI
 * memory.
 *
 * \todo
 * This routine needs to set dma_bs->agp_mode to the mode actually configured
 * in the hardware.  Looking just at the Linux AGP driver code, I don't see
 * an easy way to determine this.
 *
 * \sa mga_do_dma_bootstrap, mga_do_pci_dma_bootstrap
 */
static int mga_do_agp_dma_bootstrap(drm_device_t * dev,
				    drm_mga_dma_bootstrap_t * dma_bs)
{
D
Dave Airlie 已提交
430 431
	drm_mga_private_t *const dev_priv =
	    (drm_mga_private_t *) dev->dev_private;
432 433
	const unsigned int warp_size = mga_warp_microcode_size(dev_priv);
	int err;
D
Dave Airlie 已提交
434
	unsigned offset;
435
	const unsigned secondary_size = dma_bs->secondary_bin_count
D
Dave Airlie 已提交
436
	    * dma_bs->secondary_bin_size;
437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468
	const unsigned agp_size = (dma_bs->agp_size << 20);
	drm_buf_desc_t req;
	drm_agp_mode_t mode;
	drm_agp_info_t info;

	/* Acquire AGP. */
	err = drm_agp_acquire(dev);
	if (err) {
		DRM_ERROR("Unable to acquire AGP\n");
		return err;
	}

	err = drm_agp_info(dev, &info);
	if (err) {
		DRM_ERROR("Unable to get AGP info\n");
		return err;
	}

	mode.mode = (info.mode & ~0x07) | dma_bs->agp_mode;
	err = drm_agp_enable(dev, mode);
	if (err) {
		DRM_ERROR("Unable to enable AGP (mode = 0x%lx)\n", mode.mode);
		return err;
	}

	/* In addition to the usual AGP mode configuration, the G200 AGP cards
	 * need to have the AGP mode "manually" set.
	 */

	if (dev_priv->chipset == MGA_CARD_TYPE_G200) {
		if (mode.mode & 0x02) {
			MGA_WRITE(MGA_AGP_PLL, MGA_AGP2XPLL_ENABLE);
D
Dave Airlie 已提交
469
		} else {
470 471 472 473 474 475
			MGA_WRITE(MGA_AGP_PLL, MGA_AGP2XPLL_DISABLE);
		}
	}

	/* Allocate and bind AGP memory. */
	dev_priv->agp_pages = agp_size / PAGE_SIZE;
D
Dave Airlie 已提交
476
	dev_priv->agp_mem = drm_alloc_agp(dev, dev_priv->agp_pages, 0);
477 478 479 480 481 482
	if (dev_priv->agp_mem == NULL) {
		dev_priv->agp_pages = 0;
		DRM_ERROR("Unable to allocate %uMB AGP memory\n",
			  dma_bs->agp_size);
		return DRM_ERR(ENOMEM);
	}
D
Dave Airlie 已提交
483 484

	err = drm_bind_agp(dev_priv->agp_mem, 0);
485 486 487 488 489 490
	if (err) {
		DRM_ERROR("Unable to bind AGP memory\n");
		return err;
	}

	offset = 0;
D
Dave Airlie 已提交
491 492
	err = drm_addmap(dev, offset, warp_size,
			 _DRM_AGP, _DRM_READ_ONLY, &dev_priv->warp);
493 494 495 496 497 498
	if (err) {
		DRM_ERROR("Unable to map WARP microcode\n");
		return err;
	}

	offset += warp_size;
D
Dave Airlie 已提交
499 500
	err = drm_addmap(dev, offset, dma_bs->primary_size,
			 _DRM_AGP, _DRM_READ_ONLY, &dev_priv->primary);
501 502 503 504 505 506
	if (err) {
		DRM_ERROR("Unable to map primary DMA region\n");
		return err;
	}

	offset += dma_bs->primary_size;
D
Dave Airlie 已提交
507 508
	err = drm_addmap(dev, offset, secondary_size,
			 _DRM_AGP, 0, &dev->agp_buffer_map);
509 510 511 512 513
	if (err) {
		DRM_ERROR("Unable to map secondary DMA region\n");
		return err;
	}

D
Dave Airlie 已提交
514
	(void)memset(&req, 0, sizeof(req));
515 516 517 518 519
	req.count = dma_bs->secondary_bin_count;
	req.size = dma_bs->secondary_bin_size;
	req.flags = _DRM_AGP_BUFFER;
	req.agp_start = offset;

D
Dave Airlie 已提交
520
	err = drm_addbufs_agp(dev, &req);
521 522 523 524 525 526
	if (err) {
		DRM_ERROR("Unable to add secondary DMA buffers\n");
		return err;
	}

	offset += secondary_size;
D
Dave Airlie 已提交
527 528
	err = drm_addmap(dev, offset, agp_size - offset,
			 _DRM_AGP, 0, &dev_priv->agp_textures);
529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551
	if (err) {
		DRM_ERROR("Unable to map AGP texture region\n");
		return err;
	}

	drm_core_ioremap(dev_priv->warp, dev);
	drm_core_ioremap(dev_priv->primary, dev);
	drm_core_ioremap(dev->agp_buffer_map, dev);

	if (!dev_priv->warp->handle ||
	    !dev_priv->primary->handle || !dev->agp_buffer_map->handle) {
		DRM_ERROR("failed to ioremap agp regions! (%p, %p, %p)\n",
			  dev_priv->warp->handle, dev_priv->primary->handle,
			  dev->agp_buffer_map->handle);
		return DRM_ERR(ENOMEM);
	}

	dev_priv->dma_access = MGA_PAGPXFER;
	dev_priv->wagp_enable = MGA_WAGP_ENABLE;

	DRM_INFO("Initialized card for AGP DMA.\n");
	return 0;
}
D
Dave Airlie 已提交
552 553 554 555 556 557 558
#else
static int mga_do_agp_dma_bootstrap(drm_device_t * dev,
				    drm_mga_dma_bootstrap_t * dma_bs)
{
	return -EINVAL;
}
#endif
559 560 561

/**
 * Bootstrap the driver for PCI DMA.
D
Dave Airlie 已提交
562
 *
563 564 565 566 567 568 569 570
 * \todo
 * The algorithm for decreasing the size of the primary DMA buffer could be
 * better.  The size should be rounded up to the nearest page size, then
 * decrease the request size by a single page each pass through the loop.
 *
 * \todo
 * Determine whether the maximum address passed to drm_pci_alloc is correct.
 * The same goes for drm_addbufs_pci.
D
Dave Airlie 已提交
571
 *
572 573 574 575 576
 * \sa mga_do_dma_bootstrap, mga_do_agp_dma_bootstrap
 */
static int mga_do_pci_dma_bootstrap(drm_device_t * dev,
				    drm_mga_dma_bootstrap_t * dma_bs)
{
D
Dave Airlie 已提交
577 578
	drm_mga_private_t *const dev_priv =
	    (drm_mga_private_t *) dev->dev_private;
579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602
	const unsigned int warp_size = mga_warp_microcode_size(dev_priv);
	unsigned int primary_size;
	unsigned int bin_count;
	int err;
	drm_buf_desc_t req;

	if (dev->dma == NULL) {
		DRM_ERROR("dev->dma is NULL\n");
		return DRM_ERR(EFAULT);
	}

	/* The proper alignment is 0x100 for this mapping */
	err = drm_addmap(dev, 0, warp_size, _DRM_CONSISTENT,
			 _DRM_READ_ONLY, &dev_priv->warp);
	if (err != 0) {
		DRM_ERROR("Unable to create mapping for WARP microcode\n");
		return err;
	}

	/* Other than the bottom two bits being used to encode other
	 * information, there don't appear to be any restrictions on the
	 * alignment of the primary or secondary DMA buffers.
	 */

D
Dave Airlie 已提交
603 604
	for (primary_size = dma_bs->primary_size; primary_size != 0;
	     primary_size >>= 1) {
605 606 607 608 609 610 611 612 613 614 615 616 617 618
		/* The proper alignment for this mapping is 0x04 */
		err = drm_addmap(dev, 0, primary_size, _DRM_CONSISTENT,
				 _DRM_READ_ONLY, &dev_priv->primary);
		if (!err)
			break;
	}

	if (err != 0) {
		DRM_ERROR("Unable to allocate primary DMA region\n");
		return DRM_ERR(ENOMEM);
	}

	if (dev_priv->primary->size != dma_bs->primary_size) {
		DRM_INFO("Primary DMA buffer size reduced from %u to %u.\n",
D
Dave Airlie 已提交
619 620
			 dma_bs->primary_size,
			 (unsigned)dev_priv->primary->size);
621 622 623
		dma_bs->primary_size = dev_priv->primary->size;
	}

D
Dave Airlie 已提交
624 625 626
	for (bin_count = dma_bs->secondary_bin_count; bin_count > 0;
	     bin_count--) {
		(void)memset(&req, 0, sizeof(req));
627 628 629
		req.count = bin_count;
		req.size = dma_bs->secondary_bin_size;

D
Dave Airlie 已提交
630
		err = drm_addbufs_pci(dev, &req);
631 632 633 634
		if (!err) {
			break;
		}
	}
D
Dave Airlie 已提交
635

636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661
	if (bin_count == 0) {
		DRM_ERROR("Unable to add secondary DMA buffers\n");
		return err;
	}

	if (bin_count != dma_bs->secondary_bin_count) {
		DRM_INFO("Secondary PCI DMA buffer bin count reduced from %u "
			 "to %u.\n", dma_bs->secondary_bin_count, bin_count);

		dma_bs->secondary_bin_count = bin_count;
	}

	dev_priv->dma_access = 0;
	dev_priv->wagp_enable = 0;

	dma_bs->agp_mode = 0;

	DRM_INFO("Initialized card for PCI DMA.\n");
	return 0;
}

static int mga_do_dma_bootstrap(drm_device_t * dev,
				drm_mga_dma_bootstrap_t * dma_bs)
{
	const int is_agp = (dma_bs->agp_mode != 0) && drm_device_is_agp(dev);
	int err;
D
Dave Airlie 已提交
662 663
	drm_mga_private_t *const dev_priv =
	    (drm_mga_private_t *) dev->dev_private;
664 665 666 667 668 669

	dev_priv->used_new_dma_init = 1;

	/* The first steps are the same for both PCI and AGP based DMA.  Map
	 * the cards MMIO registers and map a status page.
	 */
D
Dave Airlie 已提交
670 671
	err = drm_addmap(dev, dev_priv->mmio_base, dev_priv->mmio_size,
			 _DRM_REGISTERS, _DRM_READ_ONLY, &dev_priv->mmio);
672 673 674 675 676
	if (err) {
		DRM_ERROR("Unable to map MMIO region\n");
		return err;
	}

D
Dave Airlie 已提交
677 678 679
	err = drm_addmap(dev, 0, SAREA_MAX, _DRM_SHM,
			 _DRM_READ_ONLY | _DRM_LOCKED | _DRM_KERNEL,
			 &dev_priv->status);
680 681 682 683 684 685 686 687 688 689 690 691 692
	if (err) {
		DRM_ERROR("Unable to map status region\n");
		return err;
	}

	/* The DMA initialization procedure is slightly different for PCI and
	 * AGP cards.  AGP cards just allocate a large block of AGP memory and
	 * carve off portions of it for internal uses.  The remaining memory
	 * is returned to user-mode to be used for AGP textures.
	 */
	if (is_agp) {
		err = mga_do_agp_dma_bootstrap(dev, dma_bs);
	}
D
Dave Airlie 已提交
693

694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726
	/* If we attempted to initialize the card for AGP DMA but failed,
	 * clean-up any mess that may have been created.
	 */

	if (err) {
		mga_do_cleanup_dma(dev);
	}

	/* Not only do we want to try and initialized PCI cards for PCI DMA,
	 * but we also try to initialized AGP cards that could not be
	 * initialized for AGP DMA.  This covers the case where we have an AGP
	 * card in a system with an unsupported AGP chipset.  In that case the
	 * card will be detected as AGP, but we won't be able to allocate any
	 * AGP memory, etc.
	 */

	if (!is_agp || err) {
		err = mga_do_pci_dma_bootstrap(dev, dma_bs);
	}

	return err;
}

int mga_dma_bootstrap(DRM_IOCTL_ARGS)
{
	DRM_DEVICE;
	drm_mga_dma_bootstrap_t bootstrap;
	int err;

	DRM_COPY_FROM_USER_IOCTL(bootstrap,
				 (drm_mga_dma_bootstrap_t __user *) data,
				 sizeof(bootstrap));

D
Dave Airlie 已提交
727 728
	err = mga_do_dma_bootstrap(dev, &bootstrap);
	if (!err) {
729
		static const int modes[] = { 0, 1, 2, 2, 4, 4, 4, 4 };
D
Dave Airlie 已提交
730 731
		const drm_mga_private_t *const dev_priv =
		    (drm_mga_private_t *) dev->dev_private;
732 733

		if (dev_priv->agp_textures != NULL) {
D
Dave Airlie 已提交
734 735
			bootstrap.texture_handle =
			    dev_priv->agp_textures->offset;
736
			bootstrap.texture_size = dev_priv->agp_textures->size;
D
Dave Airlie 已提交
737
		} else {
738 739 740 741
			bootstrap.texture_handle = 0;
			bootstrap.texture_size = 0;
		}

D
Dave Airlie 已提交
742 743
		bootstrap.agp_mode = modes[bootstrap.agp_mode & 0x07];
		if (DRM_COPY_TO_USER((void __user *)data, &bootstrap,
744 745 746
				     sizeof(bootstrap))) {
			err = DRM_ERR(EFAULT);
		}
D
Dave Airlie 已提交
747
	} else {
748 749 750 751 752 753
		mga_do_cleanup_dma(dev);
	}

	return err;
}

D
Dave Airlie 已提交
754
static int mga_do_init_dma(drm_device_t * dev, drm_mga_init_t * init)
L
Linus Torvalds 已提交
755 756 757
{
	drm_mga_private_t *dev_priv;
	int ret;
D
Dave Airlie 已提交
758
	DRM_DEBUG("\n");
L
Linus Torvalds 已提交
759

760
	dev_priv = dev->dev_private;
L
Linus Torvalds 已提交
761

762
	if (init->sgram) {
L
Linus Torvalds 已提交
763 764 765 766
		dev_priv->clear_cmd = MGA_DWGCTL_CLEAR | MGA_ATYPE_BLK;
	} else {
		dev_priv->clear_cmd = MGA_DWGCTL_CLEAR | MGA_ATYPE_RSTR;
	}
D
Dave Airlie 已提交
767
	dev_priv->maccess = init->maccess;
L
Linus Torvalds 已提交
768

D
Dave Airlie 已提交
769 770 771 772 773
	dev_priv->fb_cpp = init->fb_cpp;
	dev_priv->front_offset = init->front_offset;
	dev_priv->front_pitch = init->front_pitch;
	dev_priv->back_offset = init->back_offset;
	dev_priv->back_pitch = init->back_pitch;
L
Linus Torvalds 已提交
774

D
Dave Airlie 已提交
775 776 777
	dev_priv->depth_cpp = init->depth_cpp;
	dev_priv->depth_offset = init->depth_offset;
	dev_priv->depth_pitch = init->depth_pitch;
L
Linus Torvalds 已提交
778 779 780 781 782 783 784 785

	/* FIXME: Need to support AGP textures...
	 */
	dev_priv->texture_offset = init->texture_offset[0];
	dev_priv->texture_size = init->texture_size[0];

	DRM_GETSAREA();

786 787
	if (!dev_priv->sarea) {
		DRM_ERROR("failed to find sarea!\n");
L
Linus Torvalds 已提交
788 789 790
		return DRM_ERR(EINVAL);
	}

D
Dave Airlie 已提交
791
	if (!dev_priv->used_new_dma_init) {
792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811
		dev_priv->status = drm_core_findmap(dev, init->status_offset);
		if (!dev_priv->status) {
			DRM_ERROR("failed to find status page!\n");
			return DRM_ERR(EINVAL);
		}
		dev_priv->mmio = drm_core_findmap(dev, init->mmio_offset);
		if (!dev_priv->mmio) {
			DRM_ERROR("failed to find mmio region!\n");
			return DRM_ERR(EINVAL);
		}
		dev_priv->warp = drm_core_findmap(dev, init->warp_offset);
		if (!dev_priv->warp) {
			DRM_ERROR("failed to find warp microcode region!\n");
			return DRM_ERR(EINVAL);
		}
		dev_priv->primary = drm_core_findmap(dev, init->primary_offset);
		if (!dev_priv->primary) {
			DRM_ERROR("failed to find primary dma region!\n");
			return DRM_ERR(EINVAL);
		}
812
		dev->agp_buffer_token = init->buffers_offset;
D
Dave Airlie 已提交
813 814
		dev->agp_buffer_map =
		    drm_core_findmap(dev, init->buffers_offset);
815 816 817 818 819 820 821 822
		if (!dev->agp_buffer_map) {
			DRM_ERROR("failed to find dma buffer region!\n");
			return DRM_ERR(EINVAL);
		}

		drm_core_ioremap(dev_priv->warp, dev);
		drm_core_ioremap(dev_priv->primary, dev);
		drm_core_ioremap(dev->agp_buffer_map, dev);
L
Linus Torvalds 已提交
823 824 825
	}

	dev_priv->sarea_priv =
D
Dave Airlie 已提交
826 827
	    (drm_mga_sarea_t *) ((u8 *) dev_priv->sarea->handle +
				 init->sarea_priv_offset);
L
Linus Torvalds 已提交
828

829 830 831 832 833 834
	if (!dev_priv->warp->handle ||
	    !dev_priv->primary->handle ||
	    ((dev_priv->dma_access != 0) &&
	     ((dev->agp_buffer_map == NULL) ||
	      (dev->agp_buffer_map->handle == NULL)))) {
		DRM_ERROR("failed to ioremap agp regions!\n");
L
Linus Torvalds 已提交
835 836 837
		return DRM_ERR(ENOMEM);
	}

838 839 840
	ret = mga_warp_install_microcode(dev_priv);
	if (ret < 0) {
		DRM_ERROR("failed to install WARP ucode!\n");
L
Linus Torvalds 已提交
841 842 843
		return ret;
	}

844 845 846
	ret = mga_warp_init(dev_priv);
	if (ret < 0) {
		DRM_ERROR("failed to init WARP engine!\n");
L
Linus Torvalds 已提交
847 848 849
		return ret;
	}

D
Dave Airlie 已提交
850
	dev_priv->prim.status = (u32 *) dev_priv->status->handle;
L
Linus Torvalds 已提交
851

D
Dave Airlie 已提交
852
	mga_do_wait_for_idle(dev_priv);
L
Linus Torvalds 已提交
853 854 855

	/* Init the primary DMA registers.
	 */
D
Dave Airlie 已提交
856
	MGA_WRITE(MGA_PRIMADDRESS, dev_priv->primary->offset | MGA_DMA_GENERAL);
L
Linus Torvalds 已提交
857
#if 0
D
Dave Airlie 已提交
858 859
	MGA_WRITE(MGA_PRIMPTR, virt_to_bus((void *)dev_priv->prim.status) | MGA_PRIMPTREN0 |	/* Soft trap, SECEND, SETUPEND */
		  MGA_PRIMPTREN1);	/* DWGSYNC */
L
Linus Torvalds 已提交
860 861
#endif

D
Dave Airlie 已提交
862 863
	dev_priv->prim.start = (u8 *) dev_priv->primary->handle;
	dev_priv->prim.end = ((u8 *) dev_priv->primary->handle
L
Linus Torvalds 已提交
864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882
			      + dev_priv->primary->size);
	dev_priv->prim.size = dev_priv->primary->size;

	dev_priv->prim.tail = 0;
	dev_priv->prim.space = dev_priv->prim.size;
	dev_priv->prim.wrapped = 0;

	dev_priv->prim.last_flush = 0;
	dev_priv->prim.last_wrap = 0;

	dev_priv->prim.high_mark = 256 * DMA_BLOCK_SIZE;

	dev_priv->prim.status[0] = dev_priv->primary->offset;
	dev_priv->prim.status[1] = 0;

	dev_priv->sarea_priv->last_wrap = 0;
	dev_priv->sarea_priv->last_frame.head = 0;
	dev_priv->sarea_priv->last_frame.wrap = 0;

883 884
	if (mga_freelist_init(dev, dev_priv) < 0) {
		DRM_ERROR("could not initialize freelist\n");
L
Linus Torvalds 已提交
885 886 887 888 889 890
		return DRM_ERR(ENOMEM);
	}

	return 0;
}

D
Dave Airlie 已提交
891
static int mga_do_cleanup_dma(drm_device_t * dev)
L
Linus Torvalds 已提交
892
{
893 894
	int err = 0;
	DRM_DEBUG("\n");
L
Linus Torvalds 已提交
895 896 897 898 899

	/* Make sure interrupts are disabled here because the uninstall ioctl
	 * may not have been called from userspace and after dev_private
	 * is freed, it's too late.
	 */
D
Dave Airlie 已提交
900 901
	if (dev->irq_enabled)
		drm_irq_uninstall(dev);
L
Linus Torvalds 已提交
902

D
Dave Airlie 已提交
903
	if (dev->dev_private) {
L
Linus Torvalds 已提交
904 905
		drm_mga_private_t *dev_priv = dev->dev_private;

D
Dave Airlie 已提交
906
		if ((dev_priv->warp != NULL)
907 908 909
		    && (dev_priv->mmio->type != _DRM_CONSISTENT))
			drm_core_ioremapfree(dev_priv->warp, dev);

D
Dave Airlie 已提交
910
		if ((dev_priv->primary != NULL)
911 912
		    && (dev_priv->primary->type != _DRM_CONSISTENT))
			drm_core_ioremapfree(dev_priv->primary, dev);
L
Linus Torvalds 已提交
913

914 915 916 917
		if (dev->agp_buffer_map != NULL)
			drm_core_ioremapfree(dev->agp_buffer_map, dev);

		if (dev_priv->used_new_dma_init) {
D
Dave Airlie 已提交
918
#if __OS_HAS_AGP
919 920 921 922
			if (dev_priv->agp_mem != NULL) {
				dev_priv->agp_textures = NULL;
				drm_unbind_agp(dev_priv->agp_mem);

D
Dave Airlie 已提交
923 924
				drm_free_agp(dev_priv->agp_mem,
					     dev_priv->agp_pages);
925 926 927 928 929 930 931
				dev_priv->agp_pages = 0;
				dev_priv->agp_mem = NULL;
			}

			if ((dev->agp != NULL) && dev->agp->acquired) {
				err = drm_agp_release(dev);
			}
D
Dave Airlie 已提交
932
#endif
933
			dev_priv->used_new_dma_init = 0;
L
Linus Torvalds 已提交
934 935
		}

936 937 938 939 940 941 942 943 944 945
		dev_priv->warp = NULL;
		dev_priv->primary = NULL;
		dev_priv->mmio = NULL;
		dev_priv->status = NULL;
		dev_priv->sarea = NULL;
		dev_priv->sarea_priv = NULL;
		dev->agp_buffer_map = NULL;

		memset(&dev_priv->prim, 0, sizeof(dev_priv->prim));
		dev_priv->warp_pipe = 0;
D
Dave Airlie 已提交
946 947
		memset(dev_priv->warp_pipe_phys, 0,
		       sizeof(dev_priv->warp_pipe_phys));
948 949 950 951

		if (dev_priv->head != NULL) {
			mga_freelist_cleanup(dev);
		}
L
Linus Torvalds 已提交
952 953
	}

D
Dave Airlie 已提交
954
	return err;
L
Linus Torvalds 已提交
955 956
}

D
Dave Airlie 已提交
957
int mga_dma_init(DRM_IOCTL_ARGS)
L
Linus Torvalds 已提交
958 959 960
{
	DRM_DEVICE;
	drm_mga_init_t init;
961
	int err;
L
Linus Torvalds 已提交
962

D
Dave Airlie 已提交
963
	LOCK_TEST_WITH_RETURN(dev, filp);
L
Linus Torvalds 已提交
964

965 966
	DRM_COPY_FROM_USER_IOCTL(init, (drm_mga_init_t __user *) data,
				 sizeof(init));
L
Linus Torvalds 已提交
967

D
Dave Airlie 已提交
968
	switch (init.func) {
L
Linus Torvalds 已提交
969
	case MGA_INIT_DMA:
970 971
		err = mga_do_init_dma(dev, &init);
		if (err) {
D
Dave Airlie 已提交
972
			(void)mga_do_cleanup_dma(dev);
973 974
		}
		return err;
L
Linus Torvalds 已提交
975
	case MGA_CLEANUP_DMA:
D
Dave Airlie 已提交
976
		return mga_do_cleanup_dma(dev);
L
Linus Torvalds 已提交
977 978 979 980 981 982 983 984 985
	}

	return DRM_ERR(EINVAL);
}

/* ================================================================
 * Primary DMA stream management
 */

D
Dave Airlie 已提交
986
int mga_dma_flush(DRM_IOCTL_ARGS)
L
Linus Torvalds 已提交
987 988
{
	DRM_DEVICE;
D
Dave Airlie 已提交
989
	drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private;
L
Linus Torvalds 已提交
990 991
	drm_lock_t lock;

D
Dave Airlie 已提交
992
	LOCK_TEST_WITH_RETURN(dev, filp);
L
Linus Torvalds 已提交
993

D
Dave Airlie 已提交
994 995
	DRM_COPY_FROM_USER_IOCTL(lock, (drm_lock_t __user *) data,
				 sizeof(lock));
L
Linus Torvalds 已提交
996

D
Dave Airlie 已提交
997 998 999 1000
	DRM_DEBUG("%s%s%s\n",
		  (lock.flags & _DRM_LOCK_FLUSH) ? "flush, " : "",
		  (lock.flags & _DRM_LOCK_FLUSH_ALL) ? "flush all, " : "",
		  (lock.flags & _DRM_LOCK_QUIESCENT) ? "idle, " : "");
L
Linus Torvalds 已提交
1001

D
Dave Airlie 已提交
1002
	WRAP_WAIT_WITH_RETURN(dev_priv);
L
Linus Torvalds 已提交
1003

D
Dave Airlie 已提交
1004 1005
	if (lock.flags & (_DRM_LOCK_FLUSH | _DRM_LOCK_FLUSH_ALL)) {
		mga_do_dma_flush(dev_priv);
L
Linus Torvalds 已提交
1006 1007
	}

D
Dave Airlie 已提交
1008
	if (lock.flags & _DRM_LOCK_QUIESCENT) {
L
Linus Torvalds 已提交
1009
#if MGA_DMA_DEBUG
D
Dave Airlie 已提交
1010 1011 1012
		int ret = mga_do_wait_for_idle(dev_priv);
		if (ret < 0)
			DRM_INFO("%s: -EBUSY\n", __FUNCTION__);
L
Linus Torvalds 已提交
1013 1014
		return ret;
#else
D
Dave Airlie 已提交
1015
		return mga_do_wait_for_idle(dev_priv);
L
Linus Torvalds 已提交
1016 1017 1018 1019 1020 1021
#endif
	} else {
		return 0;
	}
}

D
Dave Airlie 已提交
1022
int mga_dma_reset(DRM_IOCTL_ARGS)
L
Linus Torvalds 已提交
1023 1024
{
	DRM_DEVICE;
D
Dave Airlie 已提交
1025
	drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private;
L
Linus Torvalds 已提交
1026

D
Dave Airlie 已提交
1027
	LOCK_TEST_WITH_RETURN(dev, filp);
L
Linus Torvalds 已提交
1028

D
Dave Airlie 已提交
1029
	return mga_do_dma_reset(dev_priv);
L
Linus Torvalds 已提交
1030 1031 1032 1033 1034 1035
}

/* ================================================================
 * DMA buffer management
 */

D
Dave Airlie 已提交
1036
static int mga_dma_get_buffers(DRMFILE filp, drm_device_t * dev, drm_dma_t * d)
L
Linus Torvalds 已提交
1037 1038 1039 1040
{
	drm_buf_t *buf;
	int i;

D
Dave Airlie 已提交
1041 1042 1043 1044
	for (i = d->granted_count; i < d->request_count; i++) {
		buf = mga_freelist_get(dev);
		if (!buf)
			return DRM_ERR(EAGAIN);
L
Linus Torvalds 已提交
1045 1046 1047

		buf->filp = filp;

D
Dave Airlie 已提交
1048 1049
		if (DRM_COPY_TO_USER(&d->request_indices[i],
				     &buf->idx, sizeof(buf->idx)))
L
Linus Torvalds 已提交
1050
			return DRM_ERR(EFAULT);
D
Dave Airlie 已提交
1051 1052
		if (DRM_COPY_TO_USER(&d->request_sizes[i],
				     &buf->total, sizeof(buf->total)))
L
Linus Torvalds 已提交
1053 1054 1055 1056 1057 1058 1059
			return DRM_ERR(EFAULT);

		d->granted_count++;
	}
	return 0;
}

D
Dave Airlie 已提交
1060
int mga_dma_buffers(DRM_IOCTL_ARGS)
L
Linus Torvalds 已提交
1061 1062 1063
{
	DRM_DEVICE;
	drm_device_dma_t *dma = dev->dma;
D
Dave Airlie 已提交
1064
	drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private;
L
Linus Torvalds 已提交
1065 1066 1067 1068
	drm_dma_t __user *argp = (void __user *)data;
	drm_dma_t d;
	int ret = 0;

D
Dave Airlie 已提交
1069
	LOCK_TEST_WITH_RETURN(dev, filp);
L
Linus Torvalds 已提交
1070

D
Dave Airlie 已提交
1071
	DRM_COPY_FROM_USER_IOCTL(d, argp, sizeof(d));
L
Linus Torvalds 已提交
1072 1073 1074

	/* Please don't send us buffers.
	 */
D
Dave Airlie 已提交
1075 1076 1077
	if (d.send_count != 0) {
		DRM_ERROR("Process %d trying to send %d buffers via drmDMA\n",
			  DRM_CURRENTPID, d.send_count);
L
Linus Torvalds 已提交
1078 1079 1080 1081 1082
		return DRM_ERR(EINVAL);
	}

	/* We'll send you buffers.
	 */
D
Dave Airlie 已提交
1083 1084 1085
	if (d.request_count < 0 || d.request_count > dma->buf_count) {
		DRM_ERROR("Process %d trying to get %d buffers (of %d max)\n",
			  DRM_CURRENTPID, d.request_count, dma->buf_count);
L
Linus Torvalds 已提交
1086 1087 1088
		return DRM_ERR(EINVAL);
	}

D
Dave Airlie 已提交
1089
	WRAP_TEST_WITH_RETURN(dev_priv);
L
Linus Torvalds 已提交
1090 1091 1092

	d.granted_count = 0;

D
Dave Airlie 已提交
1093 1094
	if (d.request_count) {
		ret = mga_dma_get_buffers(filp, dev, &d);
L
Linus Torvalds 已提交
1095 1096
	}

D
Dave Airlie 已提交
1097
	DRM_COPY_TO_USER_IOCTL(argp, d, sizeof(d));
L
Linus Torvalds 已提交
1098 1099 1100 1101

	return ret;
}

1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116
/**
 * Called just before the module is unloaded.
 */
int mga_driver_postcleanup(drm_device_t * dev)
{
	drm_free(dev->dev_private, sizeof(drm_mga_private_t), DRM_MEM_DRIVER);
	dev->dev_private = NULL;

	return 0;
}

/**
 * Called when the last opener of the device is closed.
 */
void mga_driver_pretakedown(drm_device_t * dev)
L
Linus Torvalds 已提交
1117
{
D
Dave Airlie 已提交
1118
	mga_do_cleanup_dma(dev);
L
Linus Torvalds 已提交
1119 1120
}

D
Dave Airlie 已提交
1121
int mga_driver_dma_quiescent(drm_device_t * dev)
L
Linus Torvalds 已提交
1122 1123
{
	drm_mga_private_t *dev_priv = dev->dev_private;
D
Dave Airlie 已提交
1124
	return mga_do_wait_for_idle(dev_priv);
L
Linus Torvalds 已提交
1125
}