195584Sanholt/* mga_dma.c -- DMA support for mga g200/g400 -*- linux-c -*-
2152909Sanholt * Created: Mon Dec 13 01:50:01 1999 by jhartmann@precisioninsight.com
3152909Sanholt */
4152909Sanholt/* Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
595584Sanholt * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
695584Sanholt * All Rights Reserved.
795584Sanholt *
895584Sanholt * Permission is hereby granted, free of charge, to any person obtaining a
995584Sanholt * copy of this software and associated documentation files (the "Software"),
1095584Sanholt * to deal in the Software without restriction, including without limitation
1195584Sanholt * the rights to use, copy, modify, merge, publish, distribute, sublicense,
1295584Sanholt * and/or sell copies of the Software, and to permit persons to whom the
1395584Sanholt * Software is furnished to do so, subject to the following conditions:
1495584Sanholt *
1595584Sanholt * The above copyright notice and this permission notice (including the next
1695584Sanholt * paragraph) shall be included in all copies or substantial portions of the
1795584Sanholt * Software.
1895584Sanholt *
1995584Sanholt * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2095584Sanholt * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2195584Sanholt * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
2295584Sanholt * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
2395584Sanholt * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
2495584Sanholt * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2595584Sanholt * DEALINGS IN THE SOFTWARE.
2695584Sanholt */
2795584Sanholt
28152909Sanholt#include <sys/cdefs.h>
29152909Sanholt__FBSDID("$FreeBSD$");
30152909Sanholt
31152909Sanholt/**
32152909Sanholt * \file mga_dma.c
33152909Sanholt * DMA support for MGA G200 / G400.
34182080Srnoland *
35152909Sanholt * \author Rickard E. (Rik) Faith <faith@valinux.com>
36152909Sanholt * \author Jeff Hartmann <jhartmann@valinux.com>
37152909Sanholt * \author Keith Whitwell <keith@tungstengraphics.com>
38152909Sanholt * \author Gareth Hughes <gareth@valinux.com>
39152909Sanholt */
40152909Sanholt
4195584Sanholt#include "dev/drm/drmP.h"
42112015Sanholt#include "dev/drm/drm.h"
43152909Sanholt#include "dev/drm/drm_sarea.h"
4495746Sanholt#include "dev/drm/mga_drm.h"
4595584Sanholt#include "dev/drm/mga_drv.h"
4695584Sanholt
4795584Sanholt#define MGA_DEFAULT_USEC_TIMEOUT	10000
4895584Sanholt#define MGA_FREELIST_DEBUG		0
4995584Sanholt
50152909Sanholt#define MINIMAL_CLEANUP    0
51152909Sanholt#define FULL_CLEANUP       1
52182080Srnolandstatic int mga_do_cleanup_dma(struct drm_device *dev, int full_cleanup);
5395584Sanholt
5495584Sanholt/* ================================================================
5595584Sanholt * Engine control
5695584Sanholt */
5795584Sanholt
58145132Sanholtint mga_do_wait_for_idle(drm_mga_private_t * dev_priv)
5995584Sanholt{
6095584Sanholt	u32 status = 0;
6195584Sanholt	int i;
62145132Sanholt	DRM_DEBUG("\n");
6395584Sanholt
64145132Sanholt	for (i = 0; i < dev_priv->usec_timeout; i++) {
65145132Sanholt		status = MGA_READ(MGA_STATUS) & MGA_ENGINE_IDLE_MASK;
66145132Sanholt		if (status == MGA_ENDPRDMASTS) {
67145132Sanholt			MGA_WRITE8(MGA_CRTC_INDEX, 0);
6895584Sanholt			return 0;
6995584Sanholt		}
70145132Sanholt		DRM_UDELAY(1);
7195584Sanholt	}
7295584Sanholt
7395584Sanholt#if MGA_DMA_DEBUG
74145132Sanholt	DRM_ERROR("failed!\n");
75145132Sanholt	DRM_INFO("   status=0x%08x\n", status);
7695584Sanholt#endif
77182080Srnoland	return -EBUSY;
7895584Sanholt}
7995584Sanholt
80145132Sanholtstatic int mga_do_dma_reset(drm_mga_private_t * dev_priv)
8195584Sanholt{
8295584Sanholt	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
8395584Sanholt	drm_mga_primary_buffer_t *primary = &dev_priv->prim;
8495584Sanholt
85145132Sanholt	DRM_DEBUG("\n");
8695584Sanholt
8795584Sanholt	/* The primary DMA stream should look like new right about now.
8895584Sanholt	 */
8995584Sanholt	primary->tail = 0;
9095584Sanholt	primary->space = primary->size;
9195584Sanholt	primary->last_flush = 0;
9295584Sanholt
9395584Sanholt	sarea_priv->last_wrap = 0;
9495584Sanholt
9595584Sanholt	/* FIXME: Reset counters, buffer ages etc...
9695584Sanholt	 */
9795584Sanholt
9895584Sanholt	/* FIXME: What else do we need to reinitialize?  WARP stuff?
9995584Sanholt	 */
10095584Sanholt
10195584Sanholt	return 0;
10295584Sanholt}
10395584Sanholt
10495584Sanholt/* ================================================================
10595584Sanholt * Primary DMA stream
10695584Sanholt */
10795584Sanholt
108145132Sanholtvoid mga_do_dma_flush(drm_mga_private_t * dev_priv)
10995584Sanholt{
11095584Sanholt	drm_mga_primary_buffer_t *primary = &dev_priv->prim;
11195584Sanholt	u32 head, tail;
112112015Sanholt	u32 status = 0;
113112015Sanholt	int i;
114145132Sanholt	DMA_LOCALS;
115145132Sanholt	DRM_DEBUG("\n");
11695584Sanholt
117145132Sanholt	/* We need to wait so that we can do an safe flush */
118145132Sanholt	for (i = 0; i < dev_priv->usec_timeout; i++) {
119145132Sanholt		status = MGA_READ(MGA_STATUS) & MGA_ENGINE_IDLE_MASK;
120145132Sanholt		if (status == MGA_ENDPRDMASTS)
121145132Sanholt			break;
122145132Sanholt		DRM_UDELAY(1);
123112015Sanholt	}
124112015Sanholt
125145132Sanholt	if (primary->tail == primary->last_flush) {
126145132Sanholt		DRM_DEBUG("   bailing out...\n");
12795584Sanholt		return;
12895584Sanholt	}
12995584Sanholt
13095584Sanholt	tail = primary->tail + dev_priv->primary->offset;
13195584Sanholt
13295584Sanholt	/* We need to pad the stream between flushes, as the card
13395584Sanholt	 * actually (partially?) reads the first of these commands.
13495584Sanholt	 * See page 4-16 in the G400 manual, middle of the page or so.
13595584Sanholt	 */
136145132Sanholt	BEGIN_DMA(1);
13795584Sanholt
138145132Sanholt	DMA_BLOCK(MGA_DMAPAD, 0x00000000,
139145132Sanholt		  MGA_DMAPAD, 0x00000000,
140145132Sanholt		  MGA_DMAPAD, 0x00000000, MGA_DMAPAD, 0x00000000);
14195584Sanholt
14295584Sanholt	ADVANCE_DMA();
14395584Sanholt
14495584Sanholt	primary->last_flush = primary->tail;
14595584Sanholt
146145132Sanholt	head = MGA_READ(MGA_PRIMADDRESS);
14795584Sanholt
148145132Sanholt	if (head <= tail) {
14995584Sanholt		primary->space = primary->size - primary->tail;
15095584Sanholt	} else {
15195584Sanholt		primary->space = head - tail;
15295584Sanholt	}
15395584Sanholt
154145132Sanholt	DRM_DEBUG("   head = 0x%06lx\n", head - dev_priv->primary->offset);
155145132Sanholt	DRM_DEBUG("   tail = 0x%06lx\n", tail - dev_priv->primary->offset);
156145132Sanholt	DRM_DEBUG("  space = 0x%06x\n", primary->space);
15795584Sanholt
15895584Sanholt	mga_flush_write_combine();
159152909Sanholt	MGA_WRITE(MGA_PRIMEND, tail | dev_priv->dma_access);
16095584Sanholt
161145132Sanholt	DRM_DEBUG("done.\n");
16295584Sanholt}
16395584Sanholt
164145132Sanholtvoid mga_do_dma_wrap_start(drm_mga_private_t * dev_priv)
16595584Sanholt{
16695584Sanholt	drm_mga_primary_buffer_t *primary = &dev_priv->prim;
16795584Sanholt	u32 head, tail;
16895584Sanholt	DMA_LOCALS;
169145132Sanholt	DRM_DEBUG("\n");
17095584Sanholt
17195584Sanholt	BEGIN_DMA_WRAP();
17295584Sanholt
173145132Sanholt	DMA_BLOCK(MGA_DMAPAD, 0x00000000,
174145132Sanholt		  MGA_DMAPAD, 0x00000000,
175145132Sanholt		  MGA_DMAPAD, 0x00000000, MGA_DMAPAD, 0x00000000);
17695584Sanholt
17795584Sanholt	ADVANCE_DMA();
17895584Sanholt
17995584Sanholt	tail = primary->tail + dev_priv->primary->offset;
18095584Sanholt
18195584Sanholt	primary->tail = 0;
18295584Sanholt	primary->last_flush = 0;
18395584Sanholt	primary->last_wrap++;
18495584Sanholt
185145132Sanholt	head = MGA_READ(MGA_PRIMADDRESS);
18695584Sanholt
187145132Sanholt	if (head == dev_priv->primary->offset) {
18895584Sanholt		primary->space = primary->size;
18995584Sanholt	} else {
19095584Sanholt		primary->space = head - dev_priv->primary->offset;
19195584Sanholt	}
19295584Sanholt
193145132Sanholt	DRM_DEBUG("   head = 0x%06lx\n", head - dev_priv->primary->offset);
194145132Sanholt	DRM_DEBUG("   tail = 0x%06x\n", primary->tail);
195145132Sanholt	DRM_DEBUG("   wrap = %d\n", primary->last_wrap);
196145132Sanholt	DRM_DEBUG("  space = 0x%06x\n", primary->space);
19795584Sanholt
19895584Sanholt	mga_flush_write_combine();
199152909Sanholt	MGA_WRITE(MGA_PRIMEND, tail | dev_priv->dma_access);
20095584Sanholt
201145132Sanholt	set_bit(0, &primary->wrapped);
202145132Sanholt	DRM_DEBUG("done.\n");
20395584Sanholt}
20495584Sanholt
205145132Sanholtvoid mga_do_dma_wrap_end(drm_mga_private_t * dev_priv)
20695584Sanholt{
20795584Sanholt	drm_mga_primary_buffer_t *primary = &dev_priv->prim;
20895584Sanholt	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
20995584Sanholt	u32 head = dev_priv->primary->offset;
210145132Sanholt	DRM_DEBUG("\n");
21195584Sanholt
21295584Sanholt	sarea_priv->last_wrap++;
213145132Sanholt	DRM_DEBUG("   wrap = %d\n", sarea_priv->last_wrap);
21495584Sanholt
21595584Sanholt	mga_flush_write_combine();
216145132Sanholt	MGA_WRITE(MGA_PRIMADDRESS, head | MGA_DMA_GENERAL);
21795584Sanholt
218145132Sanholt	clear_bit(0, &primary->wrapped);
219145132Sanholt	DRM_DEBUG("done.\n");
22095584Sanholt}
22195584Sanholt
22295584Sanholt/* ================================================================
22395584Sanholt * Freelist management
22495584Sanholt */
22595584Sanholt
22695584Sanholt#define MGA_BUFFER_USED		~0
22795584Sanholt#define MGA_BUFFER_FREE		0
22895584Sanholt
22995584Sanholt#if MGA_FREELIST_DEBUG
230182080Srnolandstatic void mga_freelist_print(struct drm_device * dev)
23195584Sanholt{
23295584Sanholt	drm_mga_private_t *dev_priv = dev->dev_private;
23395584Sanholt	drm_mga_freelist_t *entry;
23495584Sanholt
235145132Sanholt	DRM_INFO("\n");
236145132Sanholt	DRM_INFO("current dispatch: last=0x%x done=0x%x\n",
237145132Sanholt		 dev_priv->sarea_priv->last_dispatch,
238145132Sanholt		 (unsigned int)(MGA_READ(MGA_PRIMADDRESS) -
239145132Sanholt				dev_priv->primary->offset));
240145132Sanholt	DRM_INFO("current freelist:\n");
24195584Sanholt
242145132Sanholt	for (entry = dev_priv->head->next; entry; entry = entry->next) {
243145132Sanholt		DRM_INFO("   %p   idx=%2d  age=0x%x 0x%06lx\n",
244145132Sanholt			 entry, entry->buf->idx, entry->age.head,
245145132Sanholt			 entry->age.head - dev_priv->primary->offset);
24695584Sanholt	}
247145132Sanholt	DRM_INFO("\n");
24895584Sanholt}
24995584Sanholt#endif
25095584Sanholt
251182080Srnolandstatic int mga_freelist_init(struct drm_device * dev, drm_mga_private_t * dev_priv)
25295584Sanholt{
253182080Srnoland	struct drm_device_dma *dma = dev->dma;
254182080Srnoland	struct drm_buf *buf;
25595584Sanholt	drm_mga_buf_priv_t *buf_priv;
25695584Sanholt	drm_mga_freelist_t *entry;
25795584Sanholt	int i;
258145132Sanholt	DRM_DEBUG("count=%d\n", dma->buf_count);
25995584Sanholt
260145132Sanholt	dev_priv->head = drm_alloc(sizeof(drm_mga_freelist_t), DRM_MEM_DRIVER);
261145132Sanholt	if (dev_priv->head == NULL)
262182080Srnoland		return -ENOMEM;
26395584Sanholt
264145132Sanholt	memset(dev_priv->head, 0, sizeof(drm_mga_freelist_t));
265145132Sanholt	SET_AGE(&dev_priv->head->age, MGA_BUFFER_USED, 0);
26695584Sanholt
267145132Sanholt	for (i = 0; i < dma->buf_count; i++) {
26895584Sanholt		buf = dma->buflist[i];
269145132Sanholt		buf_priv = buf->dev_private;
27095584Sanholt
271145132Sanholt		entry = drm_alloc(sizeof(drm_mga_freelist_t), DRM_MEM_DRIVER);
272145132Sanholt		if (entry == NULL)
273182080Srnoland			return -ENOMEM;
27495584Sanholt
275145132Sanholt		memset(entry, 0, sizeof(drm_mga_freelist_t));
27695584Sanholt
27795584Sanholt		entry->next = dev_priv->head->next;
27895584Sanholt		entry->prev = dev_priv->head;
279145132Sanholt		SET_AGE(&entry->age, MGA_BUFFER_FREE, 0);
28095584Sanholt		entry->buf = buf;
28195584Sanholt
282145132Sanholt		if (dev_priv->head->next != NULL)
28395584Sanholt			dev_priv->head->next->prev = entry;
284145132Sanholt		if (entry->next == NULL)
28595584Sanholt			dev_priv->tail = entry;
28695584Sanholt
28795584Sanholt		buf_priv->list_entry = entry;
28895584Sanholt		buf_priv->discard = 0;
28995584Sanholt		buf_priv->dispatched = 0;
29095584Sanholt
29195584Sanholt		dev_priv->head->next = entry;
29295584Sanholt	}
29395584Sanholt
29495584Sanholt	return 0;
29595584Sanholt}
29695584Sanholt
297182080Srnolandstatic void mga_freelist_cleanup(struct drm_device * dev)
29895584Sanholt{
29995584Sanholt	drm_mga_private_t *dev_priv = dev->dev_private;
30095584Sanholt	drm_mga_freelist_t *entry;
30195584Sanholt	drm_mga_freelist_t *next;
302145132Sanholt	DRM_DEBUG("\n");
30395584Sanholt
30495584Sanholt	entry = dev_priv->head;
305145132Sanholt	while (entry) {
30695584Sanholt		next = entry->next;
307145132Sanholt		drm_free(entry, sizeof(drm_mga_freelist_t), DRM_MEM_DRIVER);
30895584Sanholt		entry = next;
30995584Sanholt	}
31095584Sanholt
31195584Sanholt	dev_priv->head = dev_priv->tail = NULL;
31295584Sanholt}
31395584Sanholt
31495584Sanholt#if 0
31595584Sanholt/* FIXME: Still needed?
31695584Sanholt */
317182080Srnolandstatic void mga_freelist_reset(struct drm_device * dev)
31895584Sanholt{
31995584Sanholt	drm_device_dma_t *dma = dev->dma;
320182080Srnoland	struct drm_buf *buf;
32195584Sanholt	drm_mga_buf_priv_t *buf_priv;
32295584Sanholt	int i;
32395584Sanholt
324145132Sanholt	for (i = 0; i < dma->buf_count; i++) {
32595584Sanholt		buf = dma->buflist[i];
326145132Sanholt		buf_priv = buf->dev_private;
327145132Sanholt		SET_AGE(&buf_priv->list_entry->age, MGA_BUFFER_FREE, 0);
32895584Sanholt	}
32995584Sanholt}
33095584Sanholt#endif
33195584Sanholt
332182080Srnolandstatic struct drm_buf *mga_freelist_get(struct drm_device * dev)
33395584Sanholt{
33495584Sanholt	drm_mga_private_t *dev_priv = dev->dev_private;
33595584Sanholt	drm_mga_freelist_t *next;
33695584Sanholt	drm_mga_freelist_t *prev;
33795584Sanholt	drm_mga_freelist_t *tail = dev_priv->tail;
33895584Sanholt	u32 head, wrap;
339145132Sanholt	DRM_DEBUG("\n");
34095584Sanholt
341145132Sanholt	head = MGA_READ(MGA_PRIMADDRESS);
34295584Sanholt	wrap = dev_priv->sarea_priv->last_wrap;
34395584Sanholt
344145132Sanholt	DRM_DEBUG("   tail=0x%06lx %d\n",
345145132Sanholt		  tail->age.head ?
346145132Sanholt		  tail->age.head - dev_priv->primary->offset : 0,
347145132Sanholt		  tail->age.wrap);
348145132Sanholt	DRM_DEBUG("   head=0x%06lx %d\n",
349145132Sanholt		  head - dev_priv->primary->offset, wrap);
35095584Sanholt
351145132Sanholt	if (TEST_AGE(&tail->age, head, wrap)) {
35295584Sanholt		prev = dev_priv->tail->prev;
35395584Sanholt		next = dev_priv->tail;
35495584Sanholt		prev->next = NULL;
35595584Sanholt		next->prev = next->next = NULL;
35695584Sanholt		dev_priv->tail = prev;
357145132Sanholt		SET_AGE(&next->age, MGA_BUFFER_USED, 0);
35895584Sanholt		return next->buf;
35995584Sanholt	}
36095584Sanholt
361145132Sanholt	DRM_DEBUG("returning NULL!\n");
36295584Sanholt	return NULL;
36395584Sanholt}
36495584Sanholt
365182080Srnolandint mga_freelist_put(struct drm_device * dev, struct drm_buf * buf)
36695584Sanholt{
36795584Sanholt	drm_mga_private_t *dev_priv = dev->dev_private;
36895584Sanholt	drm_mga_buf_priv_t *buf_priv = buf->dev_private;
36995584Sanholt	drm_mga_freelist_t *head, *entry, *prev;
37095584Sanholt
371145132Sanholt	DRM_DEBUG("age=0x%06lx wrap=%d\n",
372145132Sanholt		  buf_priv->list_entry->age.head -
373145132Sanholt		  dev_priv->primary->offset, buf_priv->list_entry->age.wrap);
37495584Sanholt
37595584Sanholt	entry = buf_priv->list_entry;
37695584Sanholt	head = dev_priv->head;
37795584Sanholt
378145132Sanholt	if (buf_priv->list_entry->age.head == MGA_BUFFER_USED) {
379145132Sanholt		SET_AGE(&entry->age, MGA_BUFFER_FREE, 0);
38095584Sanholt		prev = dev_priv->tail;
38195584Sanholt		prev->next = entry;
38295584Sanholt		entry->prev = prev;
38395584Sanholt		entry->next = NULL;
38495584Sanholt	} else {
38595584Sanholt		prev = head->next;
38695584Sanholt		head->next = entry;
38795584Sanholt		prev->prev = entry;
38895584Sanholt		entry->prev = head;
38995584Sanholt		entry->next = prev;
39095584Sanholt	}
39195584Sanholt
39295584Sanholt	return 0;
39395584Sanholt}
39495584Sanholt
39595584Sanholt/* ================================================================
39695584Sanholt * DMA initialization, cleanup
39795584Sanholt */
39895584Sanholt
399182080Srnolandint mga_driver_load(struct drm_device *dev, unsigned long flags)
40095584Sanholt{
401182080Srnoland	drm_mga_private_t *dev_priv;
402189130Srnoland	int ret;
40395584Sanholt
404145132Sanholt	dev_priv = drm_alloc(sizeof(drm_mga_private_t), DRM_MEM_DRIVER);
405145132Sanholt	if (!dev_priv)
406182080Srnoland		return -ENOMEM;
40795584Sanholt
408152909Sanholt	dev->dev_private = (void *)dev_priv;
409145132Sanholt	memset(dev_priv, 0, sizeof(drm_mga_private_t));
41095584Sanholt
41195584Sanholt	dev_priv->usec_timeout = MGA_DEFAULT_USEC_TIMEOUT;
412152909Sanholt	dev_priv->chipset = flags;
41395584Sanholt
414152909Sanholt	dev_priv->mmio_base = drm_get_resource_start(dev, 1);
415152909Sanholt	dev_priv->mmio_size = drm_get_resource_len(dev, 1);
416152909Sanholt
417152909Sanholt	dev->counters += 3;
418152909Sanholt	dev->types[6] = _DRM_STAT_IRQ;
419152909Sanholt	dev->types[7] = _DRM_STAT_PRIMARY;
420152909Sanholt	dev->types[8] = _DRM_STAT_SECONDARY;
421152909Sanholt
422189130Srnoland	ret = drm_vblank_init(dev, 1);
423189130Srnoland
424189130Srnoland	if (ret) {
425189130Srnoland		(void) mga_driver_unload(dev);
426189130Srnoland		return ret;
427189130Srnoland	}
428189130Srnoland
429152909Sanholt	return 0;
430152909Sanholt}
431152909Sanholt
432152909Sanholt/**
433152909Sanholt * Bootstrap the driver for AGP DMA.
434182080Srnoland *
435152909Sanholt * \todo
436298955Spfg * Investigate whether there is any benefit to storing the WARP microcode in
437152909Sanholt * AGP memory.  If not, the microcode may as well always be put in PCI
438152909Sanholt * memory.
439152909Sanholt *
440152909Sanholt * \todo
441152909Sanholt * This routine needs to set dma_bs->agp_mode to the mode actually configured
442152909Sanholt * in the hardware.  Looking just at the Linux AGP driver code, I don't see
443152909Sanholt * an easy way to determine this.
444152909Sanholt *
445152909Sanholt * \sa mga_do_dma_bootstrap, mga_do_pci_dma_bootstrap
446152909Sanholt */
447182080Srnolandstatic int mga_do_agp_dma_bootstrap(struct drm_device *dev,
448152909Sanholt				    drm_mga_dma_bootstrap_t * dma_bs)
449152909Sanholt{
450182080Srnoland	drm_mga_private_t *const dev_priv =
451182080Srnoland		(drm_mga_private_t *)dev->dev_private;
452152909Sanholt	unsigned int warp_size = mga_warp_microcode_size(dev_priv);
453152909Sanholt	int err;
454182080Srnoland	unsigned offset;
455152909Sanholt	const unsigned secondary_size = dma_bs->secondary_bin_count
456152909Sanholt		* dma_bs->secondary_bin_size;
457152909Sanholt	const unsigned agp_size = (dma_bs->agp_size << 20);
458182080Srnoland	struct drm_buf_desc req;
459182080Srnoland	struct drm_agp_mode mode;
460182080Srnoland	struct drm_agp_info info;
461182080Srnoland	struct drm_agp_buffer agp_req;
462182080Srnoland	struct drm_agp_binding bind_req;
463152909Sanholt
464152909Sanholt	/* Acquire AGP. */
465152909Sanholt	err = drm_agp_acquire(dev);
466152909Sanholt	if (err) {
467152909Sanholt		DRM_ERROR("Unable to acquire AGP: %d\n", err);
468152909Sanholt		return err;
469152909Sanholt	}
470152909Sanholt
471152909Sanholt	err = drm_agp_info(dev, &info);
472152909Sanholt	if (err) {
473152909Sanholt		DRM_ERROR("Unable to get AGP info: %d\n", err);
474152909Sanholt		return err;
475152909Sanholt	}
476152909Sanholt
477152909Sanholt	mode.mode = (info.mode & ~0x07) | dma_bs->agp_mode;
478152909Sanholt	err = drm_agp_enable(dev, mode);
479152909Sanholt	if (err) {
480152909Sanholt		DRM_ERROR("Unable to enable AGP (mode = 0x%lx)\n", mode.mode);
481152909Sanholt		return err;
482152909Sanholt	}
483152909Sanholt
484152909Sanholt	/* In addition to the usual AGP mode configuration, the G200 AGP cards
485152909Sanholt	 * need to have the AGP mode "manually" set.
486152909Sanholt	 */
487152909Sanholt
488152909Sanholt	if (dev_priv->chipset == MGA_CARD_TYPE_G200) {
489152909Sanholt		if (mode.mode & 0x02) {
490152909Sanholt			MGA_WRITE(MGA_AGP_PLL, MGA_AGP2XPLL_ENABLE);
491152909Sanholt		} else {
492152909Sanholt			MGA_WRITE(MGA_AGP_PLL, MGA_AGP2XPLL_DISABLE);
493152909Sanholt		}
494152909Sanholt	}
495152909Sanholt
496152909Sanholt	/* Allocate and bind AGP memory. */
497152909Sanholt	agp_req.size = agp_size;
498152909Sanholt	agp_req.type = 0;
499182080Srnoland	err = drm_agp_alloc(dev, &agp_req);
500152909Sanholt	if (err) {
501152909Sanholt		dev_priv->agp_size = 0;
502152909Sanholt		DRM_ERROR("Unable to allocate %uMB AGP memory\n",
503152909Sanholt			  dma_bs->agp_size);
504152909Sanholt		return err;
505152909Sanholt	}
506152909Sanholt
507152909Sanholt	dev_priv->agp_size = agp_size;
508152909Sanholt	dev_priv->agp_handle = agp_req.handle;
509152909Sanholt
510152909Sanholt	bind_req.handle = agp_req.handle;
511152909Sanholt	bind_req.offset = 0;
512152909Sanholt	err = drm_agp_bind( dev, &bind_req );
513152909Sanholt	if (err) {
514152909Sanholt		DRM_ERROR("Unable to bind AGP memory: %d\n", err);
515152909Sanholt		return err;
516152909Sanholt	}
517152909Sanholt
518152909Sanholt	/* Make drm_addbufs happy by not trying to create a mapping for less
519152909Sanholt	 * than a page.
520152909Sanholt	 */
521152909Sanholt	if (warp_size < PAGE_SIZE)
522152909Sanholt		warp_size = PAGE_SIZE;
523152909Sanholt
524152909Sanholt	offset = 0;
525182080Srnoland	err = drm_addmap(dev, offset, warp_size,
526182080Srnoland			 _DRM_AGP, _DRM_READ_ONLY, &dev_priv->warp);
527152909Sanholt	if (err) {
528152909Sanholt		DRM_ERROR("Unable to map WARP microcode: %d\n", err);
529152909Sanholt		return err;
530152909Sanholt	}
531152909Sanholt
532152909Sanholt	offset += warp_size;
533182080Srnoland	err = drm_addmap(dev, offset, dma_bs->primary_size,
534182080Srnoland			 _DRM_AGP, _DRM_READ_ONLY, & dev_priv->primary);
535152909Sanholt	if (err) {
536152909Sanholt		DRM_ERROR("Unable to map primary DMA region: %d\n", err);
537152909Sanholt		return err;
538152909Sanholt	}
539152909Sanholt
540152909Sanholt	offset += dma_bs->primary_size;
541182080Srnoland	err = drm_addmap(dev, offset, secondary_size,
542182080Srnoland			 _DRM_AGP, 0, & dev->agp_buffer_map);
543152909Sanholt	if (err) {
544152909Sanholt		DRM_ERROR("Unable to map secondary DMA region: %d\n", err);
545152909Sanholt		return err;
546152909Sanholt	}
547152909Sanholt
548182080Srnoland	(void)memset( &req, 0, sizeof(req) );
549152909Sanholt	req.count = dma_bs->secondary_bin_count;
550152909Sanholt	req.size = dma_bs->secondary_bin_size;
551152909Sanholt	req.flags = _DRM_AGP_BUFFER;
552152909Sanholt	req.agp_start = offset;
553152909Sanholt
554182080Srnoland	err = drm_addbufs_agp(dev, &req);
555152909Sanholt	if (err) {
556152909Sanholt		DRM_ERROR("Unable to add secondary DMA buffers: %d\n", err);
557152909Sanholt		return err;
558152909Sanholt	}
559152909Sanholt
560152909Sanholt#ifdef __linux__
561152909Sanholt	{
562182080Srnoland		struct drm_map_list *_entry;
563152909Sanholt		unsigned long agp_token = 0;
564152909Sanholt
565182080Srnoland		list_for_each_entry(_entry, &dev->maplist, head) {
566152909Sanholt			if (_entry->map == dev->agp_buffer_map)
567152909Sanholt				agp_token = _entry->user_token;
568152909Sanholt		}
569152909Sanholt		if (!agp_token)
570152909Sanholt			return -EFAULT;
571152909Sanholt
572152909Sanholt		dev->agp_buffer_token = agp_token;
573152909Sanholt	}
574152909Sanholt#endif
575152909Sanholt
576152909Sanholt	offset += secondary_size;
577182080Srnoland	err = drm_addmap(dev, offset, agp_size - offset,
578182080Srnoland			 _DRM_AGP, 0, & dev_priv->agp_textures);
579152909Sanholt	if (err) {
580152909Sanholt		DRM_ERROR("Unable to map AGP texture region: %d\n", err);
581152909Sanholt		return err;
582152909Sanholt	}
583152909Sanholt
584152909Sanholt	drm_core_ioremap(dev_priv->warp, dev);
585152909Sanholt	drm_core_ioremap(dev_priv->primary, dev);
586152909Sanholt	drm_core_ioremap(dev->agp_buffer_map, dev);
587152909Sanholt
588207066Srnoland	if (!dev_priv->warp->virtual ||
589207066Srnoland	    !dev_priv->primary->virtual || !dev->agp_buffer_map->virtual) {
590152909Sanholt		DRM_ERROR("failed to ioremap agp regions! (%p, %p, %p)\n",
591207066Srnoland			  dev_priv->warp->virtual, dev_priv->primary->virtual,
592207066Srnoland			  dev->agp_buffer_map->virtual);
593182080Srnoland		return -ENOMEM;
594152909Sanholt	}
595152909Sanholt
596152909Sanholt	dev_priv->dma_access = MGA_PAGPXFER;
597152909Sanholt	dev_priv->wagp_enable = MGA_WAGP_ENABLE;
598152909Sanholt
599152909Sanholt	DRM_INFO("Initialized card for AGP DMA.\n");
600152909Sanholt	return 0;
601152909Sanholt}
602152909Sanholt
603152909Sanholt/**
604152909Sanholt * Bootstrap the driver for PCI DMA.
605182080Srnoland *
606152909Sanholt * \todo
607152909Sanholt * The algorithm for decreasing the size of the primary DMA buffer could be
608152909Sanholt * better.  The size should be rounded up to the nearest page size, then
609152909Sanholt * decrease the request size by a single page each pass through the loop.
610152909Sanholt *
611152909Sanholt * \todo
612152909Sanholt * Determine whether the maximum address passed to drm_pci_alloc is correct.
613152909Sanholt * The same goes for drm_addbufs_pci.
614182080Srnoland *
615152909Sanholt * \sa mga_do_dma_bootstrap, mga_do_agp_dma_bootstrap
616152909Sanholt */
617182080Srnolandstatic int mga_do_pci_dma_bootstrap(struct drm_device * dev,
618152909Sanholt				    drm_mga_dma_bootstrap_t * dma_bs)
619152909Sanholt{
620182080Srnoland	drm_mga_private_t *const dev_priv =
621182080Srnoland		(drm_mga_private_t *) dev->dev_private;
622152909Sanholt	unsigned int warp_size = mga_warp_microcode_size(dev_priv);
623152909Sanholt	unsigned int primary_size;
624152909Sanholt	unsigned int bin_count;
625152909Sanholt	int err;
626182080Srnoland	struct drm_buf_desc req;
627152909Sanholt
628182080Srnoland
629152909Sanholt	if (dev->dma == NULL) {
630152909Sanholt		DRM_ERROR("dev->dma is NULL\n");
631182080Srnoland		return -EFAULT;
632152909Sanholt	}
633152909Sanholt
634152909Sanholt	/* Make drm_addbufs happy by not trying to create a mapping for less
635152909Sanholt	 * than a page.
636152909Sanholt	 */
637152909Sanholt	if (warp_size < PAGE_SIZE)
638152909Sanholt		warp_size = PAGE_SIZE;
639152909Sanholt
640152909Sanholt	/* The proper alignment is 0x100 for this mapping */
641152909Sanholt	err = drm_addmap(dev, 0, warp_size, _DRM_CONSISTENT,
642152909Sanholt			 _DRM_READ_ONLY, &dev_priv->warp);
643152909Sanholt	if (err != 0) {
644152909Sanholt		DRM_ERROR("Unable to create mapping for WARP microcode: %d\n",
645152909Sanholt			  err);
646152909Sanholt		return err;
647152909Sanholt	}
648152909Sanholt
649152909Sanholt	/* Other than the bottom two bits being used to encode other
650152909Sanholt	 * information, there don't appear to be any restrictions on the
651152909Sanholt	 * alignment of the primary or secondary DMA buffers.
652152909Sanholt	 */
653152909Sanholt
654182080Srnoland	for (primary_size = dma_bs->primary_size; primary_size != 0;
655182080Srnoland	     primary_size >>= 1 ) {
656152909Sanholt		/* The proper alignment for this mapping is 0x04 */
657152909Sanholt		err = drm_addmap(dev, 0, primary_size, _DRM_CONSISTENT,
658152909Sanholt				 _DRM_READ_ONLY, &dev_priv->primary);
659152909Sanholt		if (!err)
660152909Sanholt			break;
661152909Sanholt	}
662152909Sanholt
663152909Sanholt	if (err != 0) {
664152909Sanholt		DRM_ERROR("Unable to allocate primary DMA region: %d\n", err);
665182080Srnoland		return -ENOMEM;
666152909Sanholt	}
667152909Sanholt
668152909Sanholt	if (dev_priv->primary->size != dma_bs->primary_size) {
669152909Sanholt		DRM_INFO("Primary DMA buffer size reduced from %u to %u.\n",
670182080Srnoland			 dma_bs->primary_size,
671182080Srnoland			 (unsigned)dev_priv->primary->size);
672152909Sanholt		dma_bs->primary_size = dev_priv->primary->size;
673152909Sanholt	}
674152909Sanholt
675182080Srnoland	for (bin_count = dma_bs->secondary_bin_count; bin_count > 0;
676182080Srnoland	     bin_count-- ) {
677182080Srnoland		(void)memset(&req, 0, sizeof(req));
678152909Sanholt		req.count = bin_count;
679152909Sanholt		req.size = dma_bs->secondary_bin_size;
680152909Sanholt
681182080Srnoland		err = drm_addbufs_pci(dev, &req);
682152909Sanholt		if (!err) {
683152909Sanholt			break;
684152909Sanholt		}
685152909Sanholt	}
686182080Srnoland
687152909Sanholt	if (bin_count == 0) {
688152909Sanholt		DRM_ERROR("Unable to add secondary DMA buffers: %d\n", err);
689152909Sanholt		return err;
690152909Sanholt	}
691152909Sanholt
692152909Sanholt	if (bin_count != dma_bs->secondary_bin_count) {
693152909Sanholt		DRM_INFO("Secondary PCI DMA buffer bin count reduced from %u "
694152909Sanholt			 "to %u.\n", dma_bs->secondary_bin_count, bin_count);
695152909Sanholt
696152909Sanholt		dma_bs->secondary_bin_count = bin_count;
697152909Sanholt	}
698152909Sanholt
699152909Sanholt	dev_priv->dma_access = 0;
700152909Sanholt	dev_priv->wagp_enable = 0;
701152909Sanholt
702152909Sanholt	dma_bs->agp_mode = 0;
703152909Sanholt
704152909Sanholt	DRM_INFO("Initialized card for PCI DMA.\n");
705152909Sanholt	return 0;
706152909Sanholt}
707152909Sanholt
708152909Sanholt
709182080Srnolandstatic int mga_do_dma_bootstrap(struct drm_device *dev,
710182080Srnoland				drm_mga_dma_bootstrap_t *dma_bs)
711152909Sanholt{
712152909Sanholt	const int is_agp = (dma_bs->agp_mode != 0) && drm_device_is_agp(dev);
713152909Sanholt	int err;
714182080Srnoland	drm_mga_private_t *const dev_priv =
715152909Sanholt		(drm_mga_private_t *) dev->dev_private;
716152909Sanholt
717152909Sanholt
718152909Sanholt	dev_priv->used_new_dma_init = 1;
719152909Sanholt
720152909Sanholt	/* The first steps are the same for both PCI and AGP based DMA.  Map
721152909Sanholt	 * the cards MMIO registers and map a status page.
722152909Sanholt	 */
723182080Srnoland	err = drm_addmap(dev, dev_priv->mmio_base, dev_priv->mmio_size,
724182080Srnoland			 _DRM_REGISTERS, _DRM_READ_ONLY, & dev_priv->mmio);
725152909Sanholt	if (err) {
726152909Sanholt		DRM_ERROR("Unable to map MMIO region: %d\n", err);
727152909Sanholt		return err;
728152909Sanholt	}
729152909Sanholt
730152909Sanholt
731182080Srnoland	err = drm_addmap(dev, 0, SAREA_MAX, _DRM_SHM,
732182080Srnoland			 _DRM_READ_ONLY | _DRM_LOCKED | _DRM_KERNEL,
733182080Srnoland			 & dev_priv->status);
734152909Sanholt	if (err) {
735152909Sanholt		DRM_ERROR("Unable to map status region: %d\n", err);
736152909Sanholt		return err;
737152909Sanholt	}
738152909Sanholt
739152909Sanholt
740152909Sanholt	/* The DMA initialization procedure is slightly different for PCI and
741152909Sanholt	 * AGP cards.  AGP cards just allocate a large block of AGP memory and
742152909Sanholt	 * carve off portions of it for internal uses.  The remaining memory
743152909Sanholt	 * is returned to user-mode to be used for AGP textures.
744152909Sanholt	 */
745152909Sanholt
746152909Sanholt	if (is_agp) {
747152909Sanholt		err = mga_do_agp_dma_bootstrap(dev, dma_bs);
748152909Sanholt	}
749182080Srnoland
750152909Sanholt	/* If we attempted to initialize the card for AGP DMA but failed,
751152909Sanholt	 * clean-up any mess that may have been created.
752152909Sanholt	 */
753152909Sanholt
754152909Sanholt	if (err) {
755152909Sanholt		mga_do_cleanup_dma(dev, MINIMAL_CLEANUP);
756152909Sanholt	}
757152909Sanholt
758152909Sanholt
759152909Sanholt	/* Not only do we want to try and initialized PCI cards for PCI DMA,
760152909Sanholt	 * but we also try to initialized AGP cards that could not be
761152909Sanholt	 * initialized for AGP DMA.  This covers the case where we have an AGP
762152909Sanholt	 * card in a system with an unsupported AGP chipset.  In that case the
763152909Sanholt	 * card will be detected as AGP, but we won't be able to allocate any
764152909Sanholt	 * AGP memory, etc.
765152909Sanholt	 */
766152909Sanholt
767152909Sanholt	if (!is_agp || err) {
768152909Sanholt		err = mga_do_pci_dma_bootstrap(dev, dma_bs);
769152909Sanholt	}
770152909Sanholt
771152909Sanholt
772152909Sanholt	return err;
773152909Sanholt}
774152909Sanholt
775182080Srnolandint mga_dma_bootstrap(struct drm_device *dev, void *data,
776182080Srnoland		      struct drm_file *file_priv)
777152909Sanholt{
778182080Srnoland	drm_mga_dma_bootstrap_t *bootstrap = data;
779152909Sanholt	int err;
780152909Sanholt	static const int modes[] = { 0, 1, 2, 2, 4, 4, 4, 4 };
781182080Srnoland	const drm_mga_private_t *const dev_priv =
782152909Sanholt		(drm_mga_private_t *) dev->dev_private;
783152909Sanholt
784152909Sanholt
785182080Srnoland	err = mga_do_dma_bootstrap(dev, bootstrap);
786152909Sanholt	if (err) {
787152909Sanholt		mga_do_cleanup_dma(dev, FULL_CLEANUP);
788152909Sanholt		return err;
789152909Sanholt	}
790152909Sanholt
791152909Sanholt	if (dev_priv->agp_textures != NULL) {
792182080Srnoland		bootstrap->texture_handle = dev_priv->agp_textures->offset;
793182080Srnoland		bootstrap->texture_size = dev_priv->agp_textures->size;
794152909Sanholt	} else {
795182080Srnoland		bootstrap->texture_handle = 0;
796182080Srnoland		bootstrap->texture_size = 0;
797152909Sanholt	}
798152909Sanholt
799182080Srnoland	bootstrap->agp_mode = modes[bootstrap->agp_mode & 0x07];
800152909Sanholt
801152909Sanholt	return 0;
802152909Sanholt}
803152909Sanholt
804152909Sanholt
805182080Srnolandstatic int mga_do_init_dma(struct drm_device * dev, drm_mga_init_t * init)
806152909Sanholt{
807152909Sanholt	drm_mga_private_t *dev_priv;
808152909Sanholt	int ret;
809152909Sanholt	DRM_DEBUG("\n");
810152909Sanholt
811152909Sanholt
812152909Sanholt	dev_priv = dev->dev_private;
813152909Sanholt
814145132Sanholt	if (init->sgram) {
81595584Sanholt		dev_priv->clear_cmd = MGA_DWGCTL_CLEAR | MGA_ATYPE_BLK;
81695584Sanholt	} else {
81795584Sanholt		dev_priv->clear_cmd = MGA_DWGCTL_CLEAR | MGA_ATYPE_RSTR;
81895584Sanholt	}
819145132Sanholt	dev_priv->maccess = init->maccess;
82095584Sanholt
821145132Sanholt	dev_priv->fb_cpp = init->fb_cpp;
822145132Sanholt	dev_priv->front_offset = init->front_offset;
823145132Sanholt	dev_priv->front_pitch = init->front_pitch;
824145132Sanholt	dev_priv->back_offset = init->back_offset;
825145132Sanholt	dev_priv->back_pitch = init->back_pitch;
82695584Sanholt
827145132Sanholt	dev_priv->depth_cpp = init->depth_cpp;
828145132Sanholt	dev_priv->depth_offset = init->depth_offset;
829145132Sanholt	dev_priv->depth_pitch = init->depth_pitch;
83095584Sanholt
83195584Sanholt	/* FIXME: Need to support AGP textures...
83295584Sanholt	 */
83395584Sanholt	dev_priv->texture_offset = init->texture_offset[0];
83495584Sanholt	dev_priv->texture_size = init->texture_size[0];
83595584Sanholt
836182080Srnoland	dev_priv->sarea = drm_getsarea(dev);
837145132Sanholt	if (!dev_priv->sarea) {
838145132Sanholt		DRM_ERROR("failed to find sarea!\n");
839182080Srnoland		return -EINVAL;
84095584Sanholt	}
84195584Sanholt
842182080Srnoland	if (!dev_priv->used_new_dma_init) {
843152909Sanholt
844152909Sanholt		dev_priv->dma_access = MGA_PAGPXFER;
845152909Sanholt		dev_priv->wagp_enable = MGA_WAGP_ENABLE;
846152909Sanholt
847152909Sanholt		dev_priv->status = drm_core_findmap(dev, init->status_offset);
848152909Sanholt		if (!dev_priv->status) {
849152909Sanholt			DRM_ERROR("failed to find status page!\n");
850182080Srnoland			return -EINVAL;
851152909Sanholt		}
852152909Sanholt		dev_priv->mmio = drm_core_findmap(dev, init->mmio_offset);
853152909Sanholt		if (!dev_priv->mmio) {
854152909Sanholt			DRM_ERROR("failed to find mmio region!\n");
855182080Srnoland			return -EINVAL;
856152909Sanholt		}
857152909Sanholt		dev_priv->warp = drm_core_findmap(dev, init->warp_offset);
858152909Sanholt		if (!dev_priv->warp) {
859152909Sanholt			DRM_ERROR("failed to find warp microcode region!\n");
860182080Srnoland			return -EINVAL;
861152909Sanholt		}
862152909Sanholt		dev_priv->primary = drm_core_findmap(dev, init->primary_offset);
863152909Sanholt		if (!dev_priv->primary) {
864152909Sanholt			DRM_ERROR("failed to find primary dma region!\n");
865182080Srnoland			return -EINVAL;
866152909Sanholt		}
867152909Sanholt		dev->agp_buffer_token = init->buffers_offset;
868182080Srnoland		dev->agp_buffer_map =
869182080Srnoland			drm_core_findmap(dev, init->buffers_offset);
870152909Sanholt		if (!dev->agp_buffer_map) {
871152909Sanholt			DRM_ERROR("failed to find dma buffer region!\n");
872182080Srnoland			return -EINVAL;
873152909Sanholt		}
874152909Sanholt
875152909Sanholt		drm_core_ioremap(dev_priv->warp, dev);
876152909Sanholt		drm_core_ioremap(dev_priv->primary, dev);
877152909Sanholt		drm_core_ioremap(dev->agp_buffer_map, dev);
87895584Sanholt	}
87995584Sanholt
88095584Sanholt	dev_priv->sarea_priv =
881207066Srnoland	    (drm_mga_sarea_t *) ((u8 *) dev_priv->sarea->virtual +
882145132Sanholt				 init->sarea_priv_offset);
88395584Sanholt
884207066Srnoland	if (!dev_priv->warp->virtual ||
885207066Srnoland	    !dev_priv->primary->virtual ||
886152909Sanholt	    ((dev_priv->dma_access != 0) &&
887152909Sanholt	     ((dev->agp_buffer_map == NULL) ||
888207066Srnoland	      (dev->agp_buffer_map->virtual == NULL)))) {
889145132Sanholt		DRM_ERROR("failed to ioremap agp regions!\n");
890182080Srnoland		return -ENOMEM;
89195584Sanholt	}
89295584Sanholt
893145132Sanholt	ret = mga_warp_install_microcode(dev_priv);
894152909Sanholt	if (ret != 0) {
895152909Sanholt		DRM_ERROR("failed to install WARP ucode: %d!\n", ret);
896112015Sanholt		return ret;
89795584Sanholt	}
89895584Sanholt
899145132Sanholt	ret = mga_warp_init(dev_priv);
900152909Sanholt	if (ret != 0) {
901152909Sanholt		DRM_ERROR("failed to init WARP engine: %d!\n", ret);
902112015Sanholt		return ret;
90395584Sanholt	}
90495584Sanholt
905207066Srnoland	dev_priv->prim.status = (u32 *) dev_priv->status->virtual;
90695584Sanholt
907145132Sanholt	mga_do_wait_for_idle(dev_priv);
90895584Sanholt
90995584Sanholt	/* Init the primary DMA registers.
91095584Sanholt	 */
911145132Sanholt	MGA_WRITE(MGA_PRIMADDRESS, dev_priv->primary->offset | MGA_DMA_GENERAL);
91295584Sanholt
913207066Srnoland	dev_priv->prim.start = (u8 *) dev_priv->primary->virtual;
914207066Srnoland	dev_priv->prim.end = ((u8 *) dev_priv->primary->virtual
91595584Sanholt			      + dev_priv->primary->size);
91695584Sanholt	dev_priv->prim.size = dev_priv->primary->size;
91795584Sanholt
91895584Sanholt	dev_priv->prim.tail = 0;
91995584Sanholt	dev_priv->prim.space = dev_priv->prim.size;
92095584Sanholt	dev_priv->prim.wrapped = 0;
92195584Sanholt
92295584Sanholt	dev_priv->prim.last_flush = 0;
92395584Sanholt	dev_priv->prim.last_wrap = 0;
92495584Sanholt
92595584Sanholt	dev_priv->prim.high_mark = 256 * DMA_BLOCK_SIZE;
92695584Sanholt
92795584Sanholt	dev_priv->prim.status[0] = dev_priv->primary->offset;
92895584Sanholt	dev_priv->prim.status[1] = 0;
92995584Sanholt
93095584Sanholt	dev_priv->sarea_priv->last_wrap = 0;
93195584Sanholt	dev_priv->sarea_priv->last_frame.head = 0;
93295584Sanholt	dev_priv->sarea_priv->last_frame.wrap = 0;
93395584Sanholt
934145132Sanholt	if (mga_freelist_init(dev, dev_priv) < 0) {
935145132Sanholt		DRM_ERROR("could not initialize freelist\n");
936182080Srnoland		return -ENOMEM;
93795584Sanholt	}
93895584Sanholt
93995584Sanholt	return 0;
94095584Sanholt}
94195584Sanholt
942182080Srnolandstatic int mga_do_cleanup_dma(struct drm_device *dev, int full_cleanup)
94395584Sanholt{
944152909Sanholt	int err = 0;
945145132Sanholt	DRM_DEBUG("\n");
94695584Sanholt
947119098Sanholt	/* Make sure interrupts are disabled here because the uninstall ioctl
948119098Sanholt	 * may not have been called from userspace and after dev_private
949119098Sanholt	 * is freed, it's too late.
950119098Sanholt	 */
951145132Sanholt	if (dev->irq_enabled)
952145132Sanholt		drm_irq_uninstall(dev);
953119098Sanholt
954145132Sanholt	if (dev->dev_private) {
95595584Sanholt		drm_mga_private_t *dev_priv = dev->dev_private;
95695584Sanholt
957152909Sanholt		if ((dev_priv->warp != NULL)
958152909Sanholt		    && (dev_priv->warp->type != _DRM_CONSISTENT))
959145132Sanholt			drm_core_ioremapfree(dev_priv->warp, dev);
960152909Sanholt
961182080Srnoland		if ((dev_priv->primary != NULL)
962152909Sanholt		    && (dev_priv->primary->type != _DRM_CONSISTENT))
963145132Sanholt			drm_core_ioremapfree(dev_priv->primary, dev);
964152909Sanholt
965152909Sanholt		if (dev->agp_buffer_map != NULL)
966145132Sanholt			drm_core_ioremapfree(dev->agp_buffer_map, dev);
967152909Sanholt
968152909Sanholt		if (dev_priv->used_new_dma_init) {
969152909Sanholt			if (dev_priv->agp_handle != 0) {
970182080Srnoland				struct drm_agp_binding unbind_req;
971182080Srnoland				struct drm_agp_buffer free_req;
972152909Sanholt
973152909Sanholt				unbind_req.handle = dev_priv->agp_handle;
974152909Sanholt				drm_agp_unbind(dev, &unbind_req);
975152909Sanholt
976152909Sanholt				free_req.handle = dev_priv->agp_handle;
977152909Sanholt				drm_agp_free(dev, &free_req);
978152909Sanholt
979152909Sanholt				dev_priv->agp_textures = NULL;
980152909Sanholt				dev_priv->agp_size = 0;
981152909Sanholt				dev_priv->agp_handle = 0;
982152909Sanholt			}
983152909Sanholt
984152909Sanholt			if ((dev->agp != NULL) && dev->agp->acquired) {
985152909Sanholt				err = drm_agp_release(dev);
986152909Sanholt			}
987145132Sanholt		}
98895584Sanholt
989152909Sanholt		dev_priv->warp = NULL;
990152909Sanholt		dev_priv->primary = NULL;
991152909Sanholt		dev_priv->sarea = NULL;
992152909Sanholt		dev_priv->sarea_priv = NULL;
993152909Sanholt		dev->agp_buffer_map = NULL;
994152909Sanholt
995152909Sanholt		if (full_cleanup) {
996152909Sanholt			dev_priv->mmio = NULL;
997152909Sanholt			dev_priv->status = NULL;
998152909Sanholt			dev_priv->used_new_dma_init = 0;
999152909Sanholt		}
1000152909Sanholt
1001152909Sanholt		memset(&dev_priv->prim, 0, sizeof(dev_priv->prim));
1002152909Sanholt		dev_priv->warp_pipe = 0;
1003182080Srnoland		memset(dev_priv->warp_pipe_phys, 0,
1004182080Srnoland		       sizeof(dev_priv->warp_pipe_phys));
1005152909Sanholt
1006145132Sanholt		if (dev_priv->head != NULL) {
1007145132Sanholt			mga_freelist_cleanup(dev);
100895584Sanholt		}
100995584Sanholt	}
101095584Sanholt
1011182080Srnoland	return err;
101295584Sanholt}
101395584Sanholt
1014182080Srnolandint mga_dma_init(struct drm_device *dev, void *data,
1015182080Srnoland		 struct drm_file *file_priv)
101695584Sanholt{
1017182080Srnoland	drm_mga_init_t *init = data;
1018152909Sanholt	int err;
101995584Sanholt
1020182080Srnoland	LOCK_TEST_WITH_RETURN(dev, file_priv);
1021119098Sanholt
1022182080Srnoland	switch (init->func) {
102395584Sanholt	case MGA_INIT_DMA:
1024182080Srnoland		err = mga_do_init_dma(dev, init);
1025152909Sanholt		if (err) {
1026182080Srnoland			(void)mga_do_cleanup_dma(dev, FULL_CLEANUP);
1027152909Sanholt		}
1028152909Sanholt		return err;
102995584Sanholt	case MGA_CLEANUP_DMA:
1030152909Sanholt		return mga_do_cleanup_dma(dev, FULL_CLEANUP);
103195584Sanholt	}
103295584Sanholt
1033182080Srnoland	return -EINVAL;
103495584Sanholt}
103595584Sanholt
103695584Sanholt/* ================================================================
103795584Sanholt * Primary DMA stream management
103895584Sanholt */
103995584Sanholt
1040182080Srnolandint mga_dma_flush(struct drm_device *dev, void *data,
1041182080Srnoland		  struct drm_file *file_priv)
104295584Sanholt{
1043145132Sanholt	drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private;
1044182080Srnoland	struct drm_lock *lock = data;
104595584Sanholt
1046182080Srnoland	LOCK_TEST_WITH_RETURN(dev, file_priv);
104795584Sanholt
1048145132Sanholt	DRM_DEBUG("%s%s%s\n",
1049182080Srnoland		  (lock->flags & _DRM_LOCK_FLUSH) ? "flush, " : "",
1050182080Srnoland		  (lock->flags & _DRM_LOCK_FLUSH_ALL) ? "flush all, " : "",
1051182080Srnoland		  (lock->flags & _DRM_LOCK_QUIESCENT) ? "idle, " : "");
105295584Sanholt
1053145132Sanholt	WRAP_WAIT_WITH_RETURN(dev_priv);
105495584Sanholt
1055182080Srnoland	if (lock->flags & (_DRM_LOCK_FLUSH | _DRM_LOCK_FLUSH_ALL)) {
1056145132Sanholt		mga_do_dma_flush(dev_priv);
105795584Sanholt	}
105895584Sanholt
1059182080Srnoland	if (lock->flags & _DRM_LOCK_QUIESCENT) {
106095584Sanholt#if MGA_DMA_DEBUG
1061145132Sanholt		int ret = mga_do_wait_for_idle(dev_priv);
1062145132Sanholt		if (ret < 0)
1063182080Srnoland			DRM_INFO("-EBUSY\n");
106495584Sanholt		return ret;
106595584Sanholt#else
1066145132Sanholt		return mga_do_wait_for_idle(dev_priv);
106795584Sanholt#endif
106895584Sanholt	} else {
106995584Sanholt		return 0;
107095584Sanholt	}
107195584Sanholt}
107295584Sanholt
1073182080Srnolandint mga_dma_reset(struct drm_device *dev, void *data,
1074182080Srnoland		  struct drm_file *file_priv)
107595584Sanholt{
1076145132Sanholt	drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private;
107795584Sanholt
1078182080Srnoland	LOCK_TEST_WITH_RETURN(dev, file_priv);
107995584Sanholt
1080145132Sanholt	return mga_do_dma_reset(dev_priv);
108195584Sanholt}
108295584Sanholt
108395584Sanholt/* ================================================================
108495584Sanholt * DMA buffer management
108595584Sanholt */
108695584Sanholt
1087182080Srnolandstatic int mga_dma_get_buffers(struct drm_device * dev,
1088182080Srnoland			       struct drm_file *file_priv, struct drm_dma * d)
108995584Sanholt{
1090182080Srnoland	struct drm_buf *buf;
109195584Sanholt	int i;
109295584Sanholt
1093145132Sanholt	for (i = d->granted_count; i < d->request_count; i++) {
1094145132Sanholt		buf = mga_freelist_get(dev);
1095145132Sanholt		if (!buf)
1096182080Srnoland			return -EAGAIN;
109795584Sanholt
1098182080Srnoland		buf->file_priv = file_priv;
109995584Sanholt
1100145132Sanholt		if (DRM_COPY_TO_USER(&d->request_indices[i],
1101145132Sanholt				     &buf->idx, sizeof(buf->idx)))
1102182080Srnoland			return -EFAULT;
1103145132Sanholt		if (DRM_COPY_TO_USER(&d->request_sizes[i],
1104145132Sanholt				     &buf->total, sizeof(buf->total)))
1105182080Srnoland			return -EFAULT;
110695584Sanholt
110795584Sanholt		d->granted_count++;
110895584Sanholt	}
110995584Sanholt	return 0;
111095584Sanholt}
111195584Sanholt
1112182080Srnolandint mga_dma_buffers(struct drm_device *dev, void *data,
1113182080Srnoland		    struct drm_file *file_priv)
111495584Sanholt{
1115182080Srnoland	struct drm_device_dma *dma = dev->dma;
1116145132Sanholt	drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private;
1117182080Srnoland	struct drm_dma *d = data;
111895584Sanholt	int ret = 0;
111995584Sanholt
1120182080Srnoland	LOCK_TEST_WITH_RETURN(dev, file_priv);
112195584Sanholt
112295584Sanholt	/* Please don't send us buffers.
112395584Sanholt	 */
1124182080Srnoland	if (d->send_count != 0) {
1125145132Sanholt		DRM_ERROR("Process %d trying to send %d buffers via drmDMA\n",
1126182080Srnoland			  DRM_CURRENTPID, d->send_count);
1127182080Srnoland		return -EINVAL;
112895584Sanholt	}
112995584Sanholt
113095584Sanholt	/* We'll send you buffers.
113195584Sanholt	 */
1132182080Srnoland	if (d->request_count < 0 || d->request_count > dma->buf_count) {
1133145132Sanholt		DRM_ERROR("Process %d trying to get %d buffers (of %d max)\n",
1134182080Srnoland			  DRM_CURRENTPID, d->request_count, dma->buf_count);
1135182080Srnoland		return -EINVAL;
113695584Sanholt	}
113795584Sanholt
1138145132Sanholt	WRAP_TEST_WITH_RETURN(dev_priv);
113995584Sanholt
1140182080Srnoland	d->granted_count = 0;
114195584Sanholt
1142182080Srnoland	if (d->request_count) {
1143182080Srnoland		ret = mga_dma_get_buffers(dev, file_priv, d);
114495584Sanholt	}
114595584Sanholt
114695584Sanholt	return ret;
114795584Sanholt}
1148145132Sanholt
1149152909Sanholt/**
1150152909Sanholt * Called just before the module is unloaded.
1151152909Sanholt */
1152182080Srnolandint mga_driver_unload(struct drm_device * dev)
1153145132Sanholt{
1154152909Sanholt	drm_free(dev->dev_private, sizeof(drm_mga_private_t), DRM_MEM_DRIVER);
1155152909Sanholt	dev->dev_private = NULL;
1156152909Sanholt
1157152909Sanholt	return 0;
1158145132Sanholt}
1159145132Sanholt
1160152909Sanholt/**
1161152909Sanholt * Called when the last opener of the device is closed.
1162152909Sanholt */
1163182080Srnolandvoid mga_driver_lastclose(struct drm_device * dev)
1164152909Sanholt{
1165152909Sanholt	mga_do_cleanup_dma(dev, FULL_CLEANUP);
1166152909Sanholt}
1167152909Sanholt
1168182080Srnolandint mga_driver_dma_quiescent(struct drm_device * dev)
1169145132Sanholt{
1170145132Sanholt	drm_mga_private_t *dev_priv = dev->dev_private;
1171145132Sanholt	return mga_do_wait_for_idle(dev_priv);
1172145132Sanholt}
1173