ASR_BASE

Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/marvell/linux/drivers/gpu/drm/mga/Makefile b/marvell/linux/drivers/gpu/drm/mga/Makefile
new file mode 100644
index 0000000..db07c7f
--- /dev/null
+++ b/marvell/linux/drivers/gpu/drm/mga/Makefile
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Makefile for the drm device driver.  This driver provides support for the
+# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
+
+mga-y := mga_drv.o mga_dma.o mga_state.o mga_warp.o mga_irq.o
+
+mga-$(CONFIG_COMPAT) += mga_ioc32.o
+
+obj-$(CONFIG_DRM_MGA)	+= mga.o
+
diff --git a/marvell/linux/drivers/gpu/drm/mga/mga_dma.c b/marvell/linux/drivers/gpu/drm/mga/mga_dma.c
new file mode 100644
index 0000000..85c7436
--- /dev/null
+++ b/marvell/linux/drivers/gpu/drm/mga/mga_dma.c
@@ -0,0 +1,1167 @@
+/* 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.
+ */
+
+/**
+ * \file mga_dma.c
+ * DMA support for MGA G200 / G400.
+ *
+ * \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>
+ */
+
+#include <linux/delay.h>
+
+#include "mga_drv.h"
+
+#define MGA_DEFAULT_USEC_TIMEOUT	10000
+#define MGA_FREELIST_DEBUG		0
+
+#define MINIMAL_CLEANUP 0
+#define FULL_CLEANUP 1
+static int mga_do_cleanup_dma(struct drm_device *dev, int full_cleanup);
+
+/* ================================================================
+ * Engine control
+ */
+
+int mga_do_wait_for_idle(drm_mga_private_t *dev_priv)
+{
+	u32 status = 0;
+	int i;
+	DRM_DEBUG("\n");
+
+	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);
+			return 0;
+		}
+		udelay(1);
+	}
+
+#if MGA_DMA_DEBUG
+	DRM_ERROR("failed!\n");
+	DRM_INFO("   status=0x%08x\n", status);
+#endif
+	return -EBUSY;
+}
+
+static int mga_do_dma_reset(drm_mga_private_t *dev_priv)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_mga_primary_buffer_t *primary = &dev_priv->prim;
+
+	DRM_DEBUG("\n");
+
+	/* 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
+ */
+
+void mga_do_dma_flush(drm_mga_private_t *dev_priv)
+{
+	drm_mga_primary_buffer_t *primary = &dev_priv->prim;
+	u32 head, tail;
+	u32 status = 0;
+	int i;
+	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;
+		udelay(1);
+	}
+
+	if (primary->tail == primary->last_flush) {
+		DRM_DEBUG("   bailing out...\n");
+		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.
+	 */
+	BEGIN_DMA(1);
+
+	DMA_BLOCK(MGA_DMAPAD, 0x00000000,
+		  MGA_DMAPAD, 0x00000000,
+		  MGA_DMAPAD, 0x00000000, MGA_DMAPAD, 0x00000000);
+
+	ADVANCE_DMA();
+
+	primary->last_flush = primary->tail;
+
+	head = MGA_READ(MGA_PRIMADDRESS);
+
+	if (head <= tail)
+		primary->space = primary->size - primary->tail;
+	else
+		primary->space = head - tail;
+
+	DRM_DEBUG("   head = 0x%06lx\n", (unsigned long)(head - dev_priv->primary->offset));
+	DRM_DEBUG("   tail = 0x%06lx\n", (unsigned long)(tail - dev_priv->primary->offset));
+	DRM_DEBUG("  space = 0x%06x\n", primary->space);
+
+	mga_flush_write_combine();
+	MGA_WRITE(MGA_PRIMEND, tail | dev_priv->dma_access);
+
+	DRM_DEBUG("done.\n");
+}
+
+void mga_do_dma_wrap_start(drm_mga_private_t *dev_priv)
+{
+	drm_mga_primary_buffer_t *primary = &dev_priv->prim;
+	u32 head, tail;
+	DMA_LOCALS;
+	DRM_DEBUG("\n");
+
+	BEGIN_DMA_WRAP();
+
+	DMA_BLOCK(MGA_DMAPAD, 0x00000000,
+		  MGA_DMAPAD, 0x00000000,
+		  MGA_DMAPAD, 0x00000000, MGA_DMAPAD, 0x00000000);
+
+	ADVANCE_DMA();
+
+	tail = primary->tail + dev_priv->primary->offset;
+
+	primary->tail = 0;
+	primary->last_flush = 0;
+	primary->last_wrap++;
+
+	head = MGA_READ(MGA_PRIMADDRESS);
+
+	if (head == dev_priv->primary->offset)
+		primary->space = primary->size;
+	else
+		primary->space = head - dev_priv->primary->offset;
+
+	DRM_DEBUG("   head = 0x%06lx\n", (unsigned long)(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);
+
+	mga_flush_write_combine();
+	MGA_WRITE(MGA_PRIMEND, tail | dev_priv->dma_access);
+
+	set_bit(0, &primary->wrapped);
+	DRM_DEBUG("done.\n");
+}
+
+void mga_do_dma_wrap_end(drm_mga_private_t *dev_priv)
+{
+	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;
+	DRM_DEBUG("\n");
+
+	sarea_priv->last_wrap++;
+	DRM_DEBUG("   wrap = %d\n", sarea_priv->last_wrap);
+
+	mga_flush_write_combine();
+	MGA_WRITE(MGA_PRIMADDRESS, head | MGA_DMA_GENERAL);
+
+	clear_bit(0, &primary->wrapped);
+	DRM_DEBUG("done.\n");
+}
+
+/* ================================================================
+ * Freelist management
+ */
+
+#define MGA_BUFFER_USED		(~0)
+#define MGA_BUFFER_FREE		0
+
+#if MGA_FREELIST_DEBUG
+static void mga_freelist_print(struct drm_device *dev)
+{
+	drm_mga_private_t *dev_priv = dev->dev_private;
+	drm_mga_freelist_t *entry;
+
+	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,
+			 (unsigned long)(entry->age.head - dev_priv->primary->offset));
+	}
+	DRM_INFO("\n");
+}
+#endif
+
+static int mga_freelist_init(struct drm_device *dev, drm_mga_private_t *dev_priv)
+{
+	struct drm_device_dma *dma = dev->dma;
+	struct drm_buf *buf;
+	drm_mga_buf_priv_t *buf_priv;
+	drm_mga_freelist_t *entry;
+	int i;
+	DRM_DEBUG("count=%d\n", dma->buf_count);
+
+	dev_priv->head = kzalloc(sizeof(drm_mga_freelist_t), GFP_KERNEL);
+	if (dev_priv->head == NULL)
+		return -ENOMEM;
+
+	SET_AGE(&dev_priv->head->age, MGA_BUFFER_USED, 0);
+
+	for (i = 0; i < dma->buf_count; i++) {
+		buf = dma->buflist[i];
+		buf_priv = buf->dev_private;
+
+		entry = kzalloc(sizeof(drm_mga_freelist_t), GFP_KERNEL);
+		if (entry == NULL)
+			return -ENOMEM;
+
+		entry->next = dev_priv->head->next;
+		entry->prev = dev_priv->head;
+		SET_AGE(&entry->age, MGA_BUFFER_FREE, 0);
+		entry->buf = buf;
+
+		if (dev_priv->head->next != NULL)
+			dev_priv->head->next->prev = entry;
+		if (entry->next == NULL)
+			dev_priv->tail = entry;
+
+		buf_priv->list_entry = entry;
+		buf_priv->discard = 0;
+		buf_priv->dispatched = 0;
+
+		dev_priv->head->next = entry;
+	}
+
+	return 0;
+}
+
+static void mga_freelist_cleanup(struct drm_device *dev)
+{
+	drm_mga_private_t *dev_priv = dev->dev_private;
+	drm_mga_freelist_t *entry;
+	drm_mga_freelist_t *next;
+	DRM_DEBUG("\n");
+
+	entry = dev_priv->head;
+	while (entry) {
+		next = entry->next;
+		kfree(entry);
+		entry = next;
+	}
+
+	dev_priv->head = dev_priv->tail = NULL;
+}
+
+#if 0
+/* FIXME: Still needed?
+ */
+static void mga_freelist_reset(struct drm_device *dev)
+{
+	struct drm_device_dma *dma = dev->dma;
+	struct drm_buf *buf;
+	drm_mga_buf_priv_t *buf_priv;
+	int i;
+
+	for (i = 0; i < dma->buf_count; i++) {
+		buf = dma->buflist[i];
+		buf_priv = buf->dev_private;
+		SET_AGE(&buf_priv->list_entry->age, MGA_BUFFER_FREE, 0);
+	}
+}
+#endif
+
+static struct drm_buf *mga_freelist_get(struct drm_device * dev)
+{
+	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;
+	DRM_DEBUG("\n");
+
+	head = MGA_READ(MGA_PRIMADDRESS);
+	wrap = dev_priv->sarea_priv->last_wrap;
+
+	DRM_DEBUG("   tail=0x%06lx %d\n",
+		  tail->age.head ?
+		  (unsigned long)(tail->age.head - dev_priv->primary->offset) : 0,
+		  tail->age.wrap);
+	DRM_DEBUG("   head=0x%06lx %d\n",
+		  (unsigned long)(head - dev_priv->primary->offset), wrap);
+
+	if (TEST_AGE(&tail->age, head, wrap)) {
+		prev = dev_priv->tail->prev;
+		next = dev_priv->tail;
+		prev->next = NULL;
+		next->prev = next->next = NULL;
+		dev_priv->tail = prev;
+		SET_AGE(&next->age, MGA_BUFFER_USED, 0);
+		return next->buf;
+	}
+
+	DRM_DEBUG("returning NULL!\n");
+	return NULL;
+}
+
+int mga_freelist_put(struct drm_device *dev, struct drm_buf *buf)
+{
+	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;
+
+	DRM_DEBUG("age=0x%06lx wrap=%d\n",
+		  (unsigned long)(buf_priv->list_entry->age.head -
+				  dev_priv->primary->offset),
+		  buf_priv->list_entry->age.wrap);
+
+	entry = buf_priv->list_entry;
+	head = dev_priv->head;
+
+	if (buf_priv->list_entry->age.head == MGA_BUFFER_USED) {
+		SET_AGE(&entry->age, MGA_BUFFER_FREE, 0);
+		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
+ */
+
+int mga_driver_load(struct drm_device *dev, unsigned long flags)
+{
+	drm_mga_private_t *dev_priv;
+	int ret;
+
+	/* There are PCI versions of the G450.  These cards have the
+	 * same PCI ID as the AGP G450, but have an additional PCI-to-PCI
+	 * bridge chip.  We detect these cards, which are not currently
+	 * supported by this driver, by looking at the device ID of the
+	 * bus the "card" is on.  If vendor is 0x3388 (Hint Corp) and the
+	 * device is 0x0021 (HB6 Universal PCI-PCI bridge), we reject the
+	 * device.
+	 */
+	if ((dev->pdev->device == 0x0525) && dev->pdev->bus->self
+	    && (dev->pdev->bus->self->vendor == 0x3388)
+	    && (dev->pdev->bus->self->device == 0x0021)
+	    && dev->agp) {
+		/* FIXME: This should be quirked in the pci core, but oh well
+		 * the hw probably stopped existing. */
+		arch_phys_wc_del(dev->agp->agp_mtrr);
+		kfree(dev->agp);
+		dev->agp = NULL;
+	}
+	dev_priv = kzalloc(sizeof(drm_mga_private_t), GFP_KERNEL);
+	if (!dev_priv)
+		return -ENOMEM;
+
+	dev->dev_private = (void *)dev_priv;
+
+	dev_priv->usec_timeout = MGA_DEFAULT_USEC_TIMEOUT;
+	dev_priv->chipset = flags;
+
+	pci_set_master(dev->pdev);
+
+	dev_priv->mmio_base = pci_resource_start(dev->pdev, 1);
+	dev_priv->mmio_size = pci_resource_len(dev->pdev, 1);
+
+	ret = drm_vblank_init(dev, 1);
+
+	if (ret) {
+		(void) mga_driver_unload(dev);
+		return ret;
+	}
+
+	return 0;
+}
+
+#if IS_ENABLED(CONFIG_AGP)
+/**
+ * Bootstrap the driver for AGP DMA.
+ *
+ * \todo
+ * Investigate whether there is any benefit 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(struct drm_device *dev,
+				    drm_mga_dma_bootstrap_t *dma_bs)
+{
+	drm_mga_private_t *const dev_priv =
+	    (drm_mga_private_t *) dev->dev_private;
+	unsigned int warp_size = MGA_WARP_UCODE_SIZE;
+	int err;
+	unsigned offset;
+	const unsigned secondary_size = dma_bs->secondary_bin_count
+	    * dma_bs->secondary_bin_size;
+	const unsigned agp_size = (dma_bs->agp_size << 20);
+	struct drm_buf_desc req;
+	struct drm_agp_mode mode;
+	struct drm_agp_info info;
+	struct drm_agp_buffer agp_req;
+	struct drm_agp_binding bind_req;
+
+	/* Acquire AGP. */
+	err = drm_agp_acquire(dev);
+	if (err) {
+		DRM_ERROR("Unable to acquire AGP: %d\n", err);
+		return err;
+	}
+
+	err = drm_agp_info(dev, &info);
+	if (err) {
+		DRM_ERROR("Unable to get AGP info: %d\n", err);
+		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);
+		else
+			MGA_WRITE(MGA_AGP_PLL, MGA_AGP2XPLL_DISABLE);
+	}
+
+	/* Allocate and bind AGP memory. */
+	agp_req.size = agp_size;
+	agp_req.type = 0;
+	err = drm_agp_alloc(dev, &agp_req);
+	if (err) {
+		dev_priv->agp_size = 0;
+		DRM_ERROR("Unable to allocate %uMB AGP memory\n",
+			  dma_bs->agp_size);
+		return err;
+	}
+
+	dev_priv->agp_size = agp_size;
+	dev_priv->agp_handle = agp_req.handle;
+
+	bind_req.handle = agp_req.handle;
+	bind_req.offset = 0;
+	err = drm_agp_bind(dev, &bind_req);
+	if (err) {
+		DRM_ERROR("Unable to bind AGP memory: %d\n", err);
+		return err;
+	}
+
+	/* Make drm_legacy_addbufs happy by not trying to create a mapping for
+	 * less than a page.
+	 */
+	if (warp_size < PAGE_SIZE)
+		warp_size = PAGE_SIZE;
+
+	offset = 0;
+	err = drm_legacy_addmap(dev, offset, warp_size,
+				_DRM_AGP, _DRM_READ_ONLY, &dev_priv->warp);
+	if (err) {
+		DRM_ERROR("Unable to map WARP microcode: %d\n", err);
+		return err;
+	}
+
+	offset += warp_size;
+	err = drm_legacy_addmap(dev, offset, dma_bs->primary_size,
+				_DRM_AGP, _DRM_READ_ONLY, &dev_priv->primary);
+	if (err) {
+		DRM_ERROR("Unable to map primary DMA region: %d\n", err);
+		return err;
+	}
+
+	offset += dma_bs->primary_size;
+	err = drm_legacy_addmap(dev, offset, secondary_size,
+				_DRM_AGP, 0, &dev->agp_buffer_map);
+	if (err) {
+		DRM_ERROR("Unable to map secondary DMA region: %d\n", err);
+		return err;
+	}
+
+	(void)memset(&req, 0, sizeof(req));
+	req.count = dma_bs->secondary_bin_count;
+	req.size = dma_bs->secondary_bin_size;
+	req.flags = _DRM_AGP_BUFFER;
+	req.agp_start = offset;
+
+	err = drm_legacy_addbufs_agp(dev, &req);
+	if (err) {
+		DRM_ERROR("Unable to add secondary DMA buffers: %d\n", err);
+		return err;
+	}
+
+	{
+		struct drm_map_list *_entry;
+		unsigned long agp_token = 0;
+
+		list_for_each_entry(_entry, &dev->maplist, head) {
+			if (_entry->map == dev->agp_buffer_map)
+				agp_token = _entry->user_token;
+		}
+		if (!agp_token)
+			return -EFAULT;
+
+		dev->agp_buffer_token = agp_token;
+	}
+
+	offset += secondary_size;
+	err = drm_legacy_addmap(dev, offset, agp_size - offset,
+				_DRM_AGP, 0, &dev_priv->agp_textures);
+	if (err) {
+		DRM_ERROR("Unable to map AGP texture region %d\n", err);
+		return err;
+	}
+
+	drm_legacy_ioremap(dev_priv->warp, dev);
+	drm_legacy_ioremap(dev_priv->primary, dev);
+	drm_legacy_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 -ENOMEM;
+	}
+
+	dev_priv->dma_access = MGA_PAGPXFER;
+	dev_priv->wagp_enable = MGA_WAGP_ENABLE;
+
+	DRM_INFO("Initialized card for AGP DMA.\n");
+	return 0;
+}
+#else
+static int mga_do_agp_dma_bootstrap(struct drm_device *dev,
+				    drm_mga_dma_bootstrap_t *dma_bs)
+{
+	return -EINVAL;
+}
+#endif
+
+/**
+ * Bootstrap the driver for PCI DMA.
+ *
+ * \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_legacy_addbufs_pci.
+ *
+ * \sa mga_do_dma_bootstrap, mga_do_agp_dma_bootstrap
+ */
+static int mga_do_pci_dma_bootstrap(struct drm_device *dev,
+				    drm_mga_dma_bootstrap_t *dma_bs)
+{
+	drm_mga_private_t *const dev_priv =
+	    (drm_mga_private_t *) dev->dev_private;
+	unsigned int warp_size = MGA_WARP_UCODE_SIZE;
+	unsigned int primary_size;
+	unsigned int bin_count;
+	int err;
+	struct drm_buf_desc req;
+
+	if (dev->dma == NULL) {
+		DRM_ERROR("dev->dma is NULL\n");
+		return -EFAULT;
+	}
+
+	/* Make drm_legacy_addbufs happy by not trying to create a mapping for
+	 * less than a page.
+	 */
+	if (warp_size < PAGE_SIZE)
+		warp_size = PAGE_SIZE;
+
+	/* The proper alignment is 0x100 for this mapping */
+	err = drm_legacy_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: %d\n",
+			  err);
+		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.
+	 */
+
+	for (primary_size = dma_bs->primary_size; primary_size != 0;
+	     primary_size >>= 1) {
+		/* The proper alignment for this mapping is 0x04 */
+		err = drm_legacy_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: %d\n", err);
+		return -ENOMEM;
+	}
+
+	if (dev_priv->primary->size != dma_bs->primary_size) {
+		DRM_INFO("Primary DMA buffer size reduced from %u to %u.\n",
+			 dma_bs->primary_size,
+			 (unsigned)dev_priv->primary->size);
+		dma_bs->primary_size = dev_priv->primary->size;
+	}
+
+	for (bin_count = dma_bs->secondary_bin_count; bin_count > 0;
+	     bin_count--) {
+		(void)memset(&req, 0, sizeof(req));
+		req.count = bin_count;
+		req.size = dma_bs->secondary_bin_size;
+
+		err = drm_legacy_addbufs_pci(dev, &req);
+		if (!err)
+			break;
+	}
+
+	if (bin_count == 0) {
+		DRM_ERROR("Unable to add secondary DMA buffers: %d\n", err);
+		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(struct drm_device *dev,
+				drm_mga_dma_bootstrap_t *dma_bs)
+{
+	const int is_agp = (dma_bs->agp_mode != 0) && dev->agp;
+	int err;
+	drm_mga_private_t *const dev_priv =
+	    (drm_mga_private_t *) dev->dev_private;
+
+	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.
+	 */
+	err = drm_legacy_addmap(dev, dev_priv->mmio_base, dev_priv->mmio_size,
+				_DRM_REGISTERS, _DRM_READ_ONLY,
+				&dev_priv->mmio);
+	if (err) {
+		DRM_ERROR("Unable to map MMIO region: %d\n", err);
+		return err;
+	}
+
+	err = drm_legacy_addmap(dev, 0, SAREA_MAX, _DRM_SHM,
+				_DRM_READ_ONLY | _DRM_LOCKED | _DRM_KERNEL,
+			 &dev_priv->status);
+	if (err) {
+		DRM_ERROR("Unable to map status region: %d\n", err);
+		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);
+
+	/* 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, MINIMAL_CLEANUP);
+
+	/* 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(struct drm_device *dev, void *data,
+		      struct drm_file *file_priv)
+{
+	drm_mga_dma_bootstrap_t *bootstrap = data;
+	int err;
+	static const int modes[] = { 0, 1, 2, 2, 4, 4, 4, 4 };
+	const drm_mga_private_t *const dev_priv =
+		(drm_mga_private_t *) dev->dev_private;
+
+	err = mga_do_dma_bootstrap(dev, bootstrap);
+	if (err) {
+		mga_do_cleanup_dma(dev, FULL_CLEANUP);
+		return err;
+	}
+
+	if (dev_priv->agp_textures != NULL) {
+		bootstrap->texture_handle = dev_priv->agp_textures->offset;
+		bootstrap->texture_size = dev_priv->agp_textures->size;
+	} else {
+		bootstrap->texture_handle = 0;
+		bootstrap->texture_size = 0;
+	}
+
+	bootstrap->agp_mode = modes[bootstrap->agp_mode & 0x07];
+
+	return err;
+}
+
+static int mga_do_init_dma(struct drm_device *dev, drm_mga_init_t *init)
+{
+	drm_mga_private_t *dev_priv;
+	int ret;
+	DRM_DEBUG("\n");
+
+	dev_priv = dev->dev_private;
+
+	if (init->sgram)
+		dev_priv->clear_cmd = MGA_DWGCTL_CLEAR | MGA_ATYPE_BLK;
+	else
+		dev_priv->clear_cmd = MGA_DWGCTL_CLEAR | MGA_ATYPE_RSTR;
+	dev_priv->maccess = init->maccess;
+
+	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;
+
+	dev_priv->depth_cpp = init->depth_cpp;
+	dev_priv->depth_offset = init->depth_offset;
+	dev_priv->depth_pitch = init->depth_pitch;
+
+	/* FIXME: Need to support AGP textures...
+	 */
+	dev_priv->texture_offset = init->texture_offset[0];
+	dev_priv->texture_size = init->texture_size[0];
+
+	dev_priv->sarea = drm_legacy_getsarea(dev);
+	if (!dev_priv->sarea) {
+		DRM_ERROR("failed to find sarea!\n");
+		return -EINVAL;
+	}
+
+	if (!dev_priv->used_new_dma_init) {
+
+		dev_priv->dma_access = MGA_PAGPXFER;
+		dev_priv->wagp_enable = MGA_WAGP_ENABLE;
+
+		dev_priv->status = drm_legacy_findmap(dev, init->status_offset);
+		if (!dev_priv->status) {
+			DRM_ERROR("failed to find status page!\n");
+			return -EINVAL;
+		}
+		dev_priv->mmio = drm_legacy_findmap(dev, init->mmio_offset);
+		if (!dev_priv->mmio) {
+			DRM_ERROR("failed to find mmio region!\n");
+			return -EINVAL;
+		}
+		dev_priv->warp = drm_legacy_findmap(dev, init->warp_offset);
+		if (!dev_priv->warp) {
+			DRM_ERROR("failed to find warp microcode region!\n");
+			return -EINVAL;
+		}
+		dev_priv->primary = drm_legacy_findmap(dev, init->primary_offset);
+		if (!dev_priv->primary) {
+			DRM_ERROR("failed to find primary dma region!\n");
+			return -EINVAL;
+		}
+		dev->agp_buffer_token = init->buffers_offset;
+		dev->agp_buffer_map =
+		    drm_legacy_findmap(dev, init->buffers_offset);
+		if (!dev->agp_buffer_map) {
+			DRM_ERROR("failed to find dma buffer region!\n");
+			return -EINVAL;
+		}
+
+		drm_legacy_ioremap(dev_priv->warp, dev);
+		drm_legacy_ioremap(dev_priv->primary, dev);
+		drm_legacy_ioremap(dev->agp_buffer_map, dev);
+	}
+
+	dev_priv->sarea_priv =
+	    (drm_mga_sarea_t *) ((u8 *) dev_priv->sarea->handle +
+				 init->sarea_priv_offset);
+
+	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");
+		return -ENOMEM;
+	}
+
+	ret = mga_warp_install_microcode(dev_priv);
+	if (ret < 0) {
+		DRM_ERROR("failed to install WARP ucode!: %d\n", ret);
+		return ret;
+	}
+
+	ret = mga_warp_init(dev_priv);
+	if (ret < 0) {
+		DRM_ERROR("failed to init WARP engine!: %d\n", ret);
+		return ret;
+	}
+
+	dev_priv->prim.status = (u32 *) dev_priv->status->handle;
+
+	mga_do_wait_for_idle(dev_priv);
+
+	/* Init the primary DMA registers.
+	 */
+	MGA_WRITE(MGA_PRIMADDRESS, dev_priv->primary->offset | MGA_DMA_GENERAL);
+#if 0
+	MGA_WRITE(MGA_PRIMPTR, virt_to_bus((void *)dev_priv->prim.status) | MGA_PRIMPTREN0 |	/* Soft trap, SECEND, SETUPEND */
+		  MGA_PRIMPTREN1);	/* DWGSYNC */
+#endif
+
+	dev_priv->prim.start = (u8 *) dev_priv->primary->handle;
+	dev_priv->prim.end = ((u8 *) dev_priv->primary->handle
+			      + 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;
+
+	if (mga_freelist_init(dev, dev_priv) < 0) {
+		DRM_ERROR("could not initialize freelist\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static int mga_do_cleanup_dma(struct drm_device *dev, int full_cleanup)
+{
+	int err = 0;
+	DRM_DEBUG("\n");
+
+	/* 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.
+	 */
+	if (dev->irq_enabled)
+		drm_irq_uninstall(dev);
+
+	if (dev->dev_private) {
+		drm_mga_private_t *dev_priv = dev->dev_private;
+
+		if ((dev_priv->warp != NULL)
+		    && (dev_priv->warp->type != _DRM_CONSISTENT))
+			drm_legacy_ioremapfree(dev_priv->warp, dev);
+
+		if ((dev_priv->primary != NULL)
+		    && (dev_priv->primary->type != _DRM_CONSISTENT))
+			drm_legacy_ioremapfree(dev_priv->primary, dev);
+
+		if (dev->agp_buffer_map != NULL)
+			drm_legacy_ioremapfree(dev->agp_buffer_map, dev);
+
+		if (dev_priv->used_new_dma_init) {
+#if IS_ENABLED(CONFIG_AGP)
+			if (dev_priv->agp_handle != 0) {
+				struct drm_agp_binding unbind_req;
+				struct drm_agp_buffer free_req;
+
+				unbind_req.handle = dev_priv->agp_handle;
+				drm_agp_unbind(dev, &unbind_req);
+
+				free_req.handle = dev_priv->agp_handle;
+				drm_agp_free(dev, &free_req);
+
+				dev_priv->agp_textures = NULL;
+				dev_priv->agp_size = 0;
+				dev_priv->agp_handle = 0;
+			}
+
+			if ((dev->agp != NULL) && dev->agp->acquired)
+				err = drm_agp_release(dev);
+#endif
+		}
+
+		dev_priv->warp = NULL;
+		dev_priv->primary = NULL;
+		dev_priv->sarea = NULL;
+		dev_priv->sarea_priv = NULL;
+		dev->agp_buffer_map = NULL;
+
+		if (full_cleanup) {
+			dev_priv->mmio = NULL;
+			dev_priv->status = NULL;
+			dev_priv->used_new_dma_init = 0;
+		}
+
+		memset(&dev_priv->prim, 0, sizeof(dev_priv->prim));
+		dev_priv->warp_pipe = 0;
+		memset(dev_priv->warp_pipe_phys, 0,
+		       sizeof(dev_priv->warp_pipe_phys));
+
+		if (dev_priv->head != NULL)
+			mga_freelist_cleanup(dev);
+	}
+
+	return err;
+}
+
+int mga_dma_init(struct drm_device *dev, void *data,
+		 struct drm_file *file_priv)
+{
+	drm_mga_init_t *init = data;
+	int err;
+
+	LOCK_TEST_WITH_RETURN(dev, file_priv);
+
+	switch (init->func) {
+	case MGA_INIT_DMA:
+		err = mga_do_init_dma(dev, init);
+		if (err)
+			(void)mga_do_cleanup_dma(dev, FULL_CLEANUP);
+		return err;
+	case MGA_CLEANUP_DMA:
+		return mga_do_cleanup_dma(dev, FULL_CLEANUP);
+	}
+
+	return -EINVAL;
+}
+
+/* ================================================================
+ * Primary DMA stream management
+ */
+
+int mga_dma_flush(struct drm_device *dev, void *data,
+		  struct drm_file *file_priv)
+{
+	drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private;
+	struct drm_lock *lock = data;
+
+	LOCK_TEST_WITH_RETURN(dev, file_priv);
+
+	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, " : "");
+
+	WRAP_WAIT_WITH_RETURN(dev_priv);
+
+	if (lock->flags & (_DRM_LOCK_FLUSH | _DRM_LOCK_FLUSH_ALL))
+		mga_do_dma_flush(dev_priv);
+
+	if (lock->flags & _DRM_LOCK_QUIESCENT) {
+#if MGA_DMA_DEBUG
+		int ret = mga_do_wait_for_idle(dev_priv);
+		if (ret < 0)
+			DRM_INFO("-EBUSY\n");
+		return ret;
+#else
+		return mga_do_wait_for_idle(dev_priv);
+#endif
+	} else {
+		return 0;
+	}
+}
+
+int mga_dma_reset(struct drm_device *dev, void *data,
+		  struct drm_file *file_priv)
+{
+	drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private;
+
+	LOCK_TEST_WITH_RETURN(dev, file_priv);
+
+	return mga_do_dma_reset(dev_priv);
+}
+
+/* ================================================================
+ * DMA buffer management
+ */
+
+static int mga_dma_get_buffers(struct drm_device *dev,
+			       struct drm_file *file_priv, struct drm_dma *d)
+{
+	struct drm_buf *buf;
+	int i;
+
+	for (i = d->granted_count; i < d->request_count; i++) {
+		buf = mga_freelist_get(dev);
+		if (!buf)
+			return -EAGAIN;
+
+		buf->file_priv = file_priv;
+
+		if (copy_to_user(&d->request_indices[i],
+				     &buf->idx, sizeof(buf->idx)))
+			return -EFAULT;
+		if (copy_to_user(&d->request_sizes[i],
+				     &buf->total, sizeof(buf->total)))
+			return -EFAULT;
+
+		d->granted_count++;
+	}
+	return 0;
+}
+
+int mga_dma_buffers(struct drm_device *dev, void *data,
+		    struct drm_file *file_priv)
+{
+	struct drm_device_dma *dma = dev->dma;
+	drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private;
+	struct drm_dma *d = data;
+	int ret = 0;
+
+	LOCK_TEST_WITH_RETURN(dev, file_priv);
+
+	/* Please don't send us buffers.
+	 */
+	if (d->send_count != 0) {
+		DRM_ERROR("Process %d trying to send %d buffers via drmDMA\n",
+			  task_pid_nr(current), d->send_count);
+		return -EINVAL;
+	}
+
+	/* We'll send you buffers.
+	 */
+	if (d->request_count < 0 || d->request_count > dma->buf_count) {
+		DRM_ERROR("Process %d trying to get %d buffers (of %d max)\n",
+			  task_pid_nr(current), d->request_count,
+			  dma->buf_count);
+		return -EINVAL;
+	}
+
+	WRAP_TEST_WITH_RETURN(dev_priv);
+
+	d->granted_count = 0;
+
+	if (d->request_count)
+		ret = mga_dma_get_buffers(dev, file_priv, d);
+
+	return ret;
+}
+
+/**
+ * Called just before the module is unloaded.
+ */
+void mga_driver_unload(struct drm_device *dev)
+{
+	kfree(dev->dev_private);
+	dev->dev_private = NULL;
+}
+
+/**
+ * Called when the last opener of the device is closed.
+ */
+void mga_driver_lastclose(struct drm_device *dev)
+{
+	mga_do_cleanup_dma(dev, FULL_CLEANUP);
+}
+
+int mga_driver_dma_quiescent(struct drm_device *dev)
+{
+	drm_mga_private_t *dev_priv = dev->dev_private;
+	return mga_do_wait_for_idle(dev_priv);
+}
diff --git a/marvell/linux/drivers/gpu/drm/mga/mga_drv.c b/marvell/linux/drivers/gpu/drm/mga/mga_drv.c
new file mode 100644
index 0000000..71128e6
--- /dev/null
+++ b/marvell/linux/drivers/gpu/drm/mga/mga_drv.c
@@ -0,0 +1,104 @@
+/* mga_drv.c -- Matrox G200/G400 driver -*- linux-c -*-
+ * Created: Mon Dec 13 01:56:22 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
+ * VA LINUX SYSTEMS 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:
+ *    Rickard E. (Rik) Faith <faith@valinux.com>
+ *    Gareth Hughes <gareth@valinux.com>
+ */
+
+#include <linux/module.h>
+
+#include <drm/drm_drv.h>
+#include <drm/drm_pciids.h>
+
+#include "mga_drv.h"
+
+static struct pci_device_id pciidlist[] = {
+	mga_PCI_IDS
+};
+
+static const struct file_operations mga_driver_fops = {
+	.owner = THIS_MODULE,
+	.open = drm_open,
+	.release = drm_release,
+	.unlocked_ioctl = drm_ioctl,
+	.mmap = drm_legacy_mmap,
+	.poll = drm_poll,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl = mga_compat_ioctl,
+#endif
+	.llseek = noop_llseek,
+};
+
+static struct drm_driver driver = {
+	.driver_features =
+	    DRIVER_USE_AGP | DRIVER_PCI_DMA | DRIVER_LEGACY |
+	    DRIVER_HAVE_DMA | DRIVER_HAVE_IRQ,
+	.dev_priv_size = sizeof(drm_mga_buf_priv_t),
+	.load = mga_driver_load,
+	.unload = mga_driver_unload,
+	.lastclose = mga_driver_lastclose,
+	.dma_quiescent = mga_driver_dma_quiescent,
+	.get_vblank_counter = mga_get_vblank_counter,
+	.enable_vblank = mga_enable_vblank,
+	.disable_vblank = mga_disable_vblank,
+	.irq_preinstall = mga_driver_irq_preinstall,
+	.irq_postinstall = mga_driver_irq_postinstall,
+	.irq_uninstall = mga_driver_irq_uninstall,
+	.irq_handler = mga_driver_irq_handler,
+	.ioctls = mga_ioctls,
+	.dma_ioctl = mga_dma_buffers,
+	.fops = &mga_driver_fops,
+	.name = DRIVER_NAME,
+	.desc = DRIVER_DESC,
+	.date = DRIVER_DATE,
+	.major = DRIVER_MAJOR,
+	.minor = DRIVER_MINOR,
+	.patchlevel = DRIVER_PATCHLEVEL,
+};
+
+static struct pci_driver mga_pci_driver = {
+	.name = DRIVER_NAME,
+	.id_table = pciidlist,
+};
+
+static int __init mga_init(void)
+{
+	driver.num_ioctls = mga_max_ioctl;
+	return drm_legacy_pci_init(&driver, &mga_pci_driver);
+}
+
+static void __exit mga_exit(void)
+{
+	drm_legacy_pci_exit(&driver, &mga_pci_driver);
+}
+
+module_init(mga_init);
+module_exit(mga_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL and additional rights");
diff --git a/marvell/linux/drivers/gpu/drm/mga/mga_drv.h b/marvell/linux/drivers/gpu/drm/mga/mga_drv.h
new file mode 100644
index 0000000..d5deecb
--- /dev/null
+++ b/marvell/linux/drivers/gpu/drm/mga/mga_drv.h
@@ -0,0 +1,687 @@
+/* mga_drv.h -- Private header for the Matrox G200/G400 driver -*- 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
+ * VA LINUX SYSTEMS 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:
+ *    Gareth Hughes <gareth@valinux.com>
+ */
+
+#ifndef __MGA_DRV_H__
+#define __MGA_DRV_H__
+
+#include <linux/irqreturn.h>
+#include <linux/slab.h>
+
+#include <drm/drm_agpsupport.h>
+#include <drm/drm_device.h>
+#include <drm/drm_file.h>
+#include <drm/drm_ioctl.h>
+#include <drm/drm_irq.h>
+#include <drm/drm_legacy.h>
+#include <drm/drm_pci.h>
+#include <drm/drm_print.h>
+#include <drm/drm_sarea.h>
+#include <drm/drm_vblank.h>
+#include <drm/mga_drm.h>
+
+/* General customization:
+ */
+
+#define DRIVER_AUTHOR		"Gareth Hughes, VA Linux Systems Inc."
+
+#define DRIVER_NAME		"mga"
+#define DRIVER_DESC		"Matrox G200/G400"
+#define DRIVER_DATE		"20051102"
+
+#define DRIVER_MAJOR		3
+#define DRIVER_MINOR		2
+#define DRIVER_PATCHLEVEL	1
+
+typedef struct drm_mga_primary_buffer {
+	u8 *start;
+	u8 *end;
+	int size;
+
+	u32 tail;
+	int space;
+	volatile long wrapped;
+
+	volatile u32 *status;
+
+	u32 last_flush;
+	u32 last_wrap;
+
+	u32 high_mark;
+} drm_mga_primary_buffer_t;
+
+typedef struct drm_mga_freelist {
+	struct drm_mga_freelist *next;
+	struct drm_mga_freelist *prev;
+	drm_mga_age_t age;
+	struct drm_buf *buf;
+} drm_mga_freelist_t;
+
+typedef struct {
+	drm_mga_freelist_t *list_entry;
+	int discard;
+	int dispatched;
+} drm_mga_buf_priv_t;
+
+typedef struct drm_mga_private {
+	drm_mga_primary_buffer_t prim;
+	drm_mga_sarea_t *sarea_priv;
+
+	drm_mga_freelist_t *head;
+	drm_mga_freelist_t *tail;
+
+	unsigned int warp_pipe;
+	unsigned long warp_pipe_phys[MGA_MAX_WARP_PIPES];
+
+	int chipset;
+	int usec_timeout;
+
+	/**
+	 * If set, the new DMA initialization sequence was used.  This is
+	 * primarilly used to select how the driver should uninitialized its
+	 * internal DMA structures.
+	 */
+	int used_new_dma_init;
+
+	/**
+	 * If AGP memory is used for DMA buffers, this will be the value
+	 * \c MGA_PAGPXFER.  Otherwise, it will be zero (for a PCI transfer).
+	 */
+	u32 dma_access;
+
+	/**
+	 * If AGP memory is used for DMA buffers, this will be the value
+	 * \c MGA_WAGP_ENABLE.  Otherwise, it will be zero (for a PCI
+	 * transfer).
+	 */
+	u32 wagp_enable;
+
+	/**
+	 * \name MMIO region parameters.
+	 *
+	 * \sa drm_mga_private_t::mmio
+	 */
+	/*@{ */
+	resource_size_t mmio_base;	   /**< Bus address of base of MMIO. */
+	resource_size_t mmio_size;	   /**< Size of the MMIO region. */
+	/*@} */
+
+	u32 clear_cmd;
+	u32 maccess;
+
+	atomic_t vbl_received;          /**< Number of vblanks received. */
+	wait_queue_head_t fence_queue;
+	atomic_t last_fence_retired;
+	u32 next_fence_to_post;
+
+	unsigned int fb_cpp;
+	unsigned int front_offset;
+	unsigned int front_pitch;
+	unsigned int back_offset;
+	unsigned int back_pitch;
+
+	unsigned int depth_cpp;
+	unsigned int depth_offset;
+	unsigned int depth_pitch;
+
+	unsigned int texture_offset;
+	unsigned int texture_size;
+
+	drm_local_map_t *sarea;
+	drm_local_map_t *mmio;
+	drm_local_map_t *status;
+	drm_local_map_t *warp;
+	drm_local_map_t *primary;
+	drm_local_map_t *agp_textures;
+
+	unsigned long agp_handle;
+	unsigned int agp_size;
+} drm_mga_private_t;
+
+extern const struct drm_ioctl_desc mga_ioctls[];
+extern int mga_max_ioctl;
+
+				/* mga_dma.c */
+extern int mga_dma_bootstrap(struct drm_device *dev, void *data,
+			     struct drm_file *file_priv);
+extern int mga_dma_init(struct drm_device *dev, void *data,
+			struct drm_file *file_priv);
+extern int mga_getparam(struct drm_device *dev, void *data,
+			struct drm_file *file_priv);
+extern int mga_dma_flush(struct drm_device *dev, void *data,
+			 struct drm_file *file_priv);
+extern int mga_dma_reset(struct drm_device *dev, void *data,
+			 struct drm_file *file_priv);
+extern int mga_dma_buffers(struct drm_device *dev, void *data,
+			   struct drm_file *file_priv);
+extern int mga_driver_load(struct drm_device *dev, unsigned long flags);
+extern void mga_driver_unload(struct drm_device *dev);
+extern void mga_driver_lastclose(struct drm_device *dev);
+extern int mga_driver_dma_quiescent(struct drm_device *dev);
+
+extern int mga_do_wait_for_idle(drm_mga_private_t *dev_priv);
+
+extern void mga_do_dma_flush(drm_mga_private_t *dev_priv);
+extern void mga_do_dma_wrap_start(drm_mga_private_t *dev_priv);
+extern void mga_do_dma_wrap_end(drm_mga_private_t *dev_priv);
+
+extern int mga_freelist_put(struct drm_device *dev, struct drm_buf *buf);
+
+				/* mga_warp.c */
+extern int mga_warp_install_microcode(drm_mga_private_t *dev_priv);
+extern int mga_warp_init(drm_mga_private_t *dev_priv);
+
+				/* mga_irq.c */
+extern int mga_enable_vblank(struct drm_device *dev, unsigned int pipe);
+extern void mga_disable_vblank(struct drm_device *dev, unsigned int pipe);
+extern u32 mga_get_vblank_counter(struct drm_device *dev, unsigned int pipe);
+extern void mga_driver_fence_wait(struct drm_device *dev, unsigned int *sequence);
+extern int mga_driver_vblank_wait(struct drm_device *dev, unsigned int *sequence);
+extern irqreturn_t mga_driver_irq_handler(int irq, void *arg);
+extern void mga_driver_irq_preinstall(struct drm_device *dev);
+extern int mga_driver_irq_postinstall(struct drm_device *dev);
+extern void mga_driver_irq_uninstall(struct drm_device *dev);
+extern long mga_compat_ioctl(struct file *filp, unsigned int cmd,
+			     unsigned long arg);
+
+#define mga_flush_write_combine()	wmb()
+
+#define MGA_READ8(reg) \
+	readb(((void __iomem *)dev_priv->mmio->handle) + (reg))
+#define MGA_READ(reg) \
+	readl(((void __iomem *)dev_priv->mmio->handle) + (reg))
+#define MGA_WRITE8(reg, val) \
+	writeb(val, ((void __iomem *)dev_priv->mmio->handle) + (reg))
+#define MGA_WRITE(reg, val) \
+	writel(val, ((void __iomem *)dev_priv->mmio->handle) + (reg))
+
+#define DWGREG0		0x1c00
+#define DWGREG0_END	0x1dff
+#define DWGREG1		0x2c00
+#define DWGREG1_END	0x2dff
+
+#define ISREG0(r)	(r >= DWGREG0 && r <= DWGREG0_END)
+#define DMAREG0(r)	(u8)((r - DWGREG0) >> 2)
+#define DMAREG1(r)	(u8)(((r - DWGREG1) >> 2) | 0x80)
+#define DMAREG(r)	(ISREG0(r) ? DMAREG0(r) : DMAREG1(r))
+
+/* ================================================================
+ * Helper macross...
+ */
+
+#define MGA_EMIT_STATE(dev_priv, dirty)					\
+do {									\
+	if ((dirty) & ~MGA_UPLOAD_CLIPRECTS) {				\
+		if (dev_priv->chipset >= MGA_CARD_TYPE_G400)		\
+			mga_g400_emit_state(dev_priv);			\
+		else							\
+			mga_g200_emit_state(dev_priv);			\
+	}								\
+} while (0)
+
+#define WRAP_TEST_WITH_RETURN(dev_priv)					\
+do {									\
+	if (test_bit(0, &dev_priv->prim.wrapped)) {			\
+		if (mga_is_idle(dev_priv)) {				\
+			mga_do_dma_wrap_end(dev_priv);			\
+		} else if (dev_priv->prim.space <			\
+			   dev_priv->prim.high_mark) {			\
+			if (MGA_DMA_DEBUG)				\
+				DRM_INFO("wrap...\n");			\
+			return -EBUSY;					\
+		}							\
+	}								\
+} while (0)
+
+#define WRAP_WAIT_WITH_RETURN(dev_priv)					\
+do {									\
+	if (test_bit(0, &dev_priv->prim.wrapped)) {			\
+		if (mga_do_wait_for_idle(dev_priv) < 0) {		\
+			if (MGA_DMA_DEBUG)				\
+				DRM_INFO("wrap...\n");			\
+			return -EBUSY;					\
+		}							\
+		mga_do_dma_wrap_end(dev_priv);				\
+	}								\
+} while (0)
+
+/* ================================================================
+ * Primary DMA command stream
+ */
+
+#define MGA_VERBOSE	0
+
+#define DMA_LOCALS	unsigned int write; volatile u8 *prim;
+
+#define DMA_BLOCK_SIZE	(5 * sizeof(u32))
+
+#define BEGIN_DMA(n)							\
+do {									\
+	if (MGA_VERBOSE) {						\
+		DRM_INFO("BEGIN_DMA(%d)\n", (n));			\
+		DRM_INFO("   space=0x%x req=0x%zx\n",			\
+			 dev_priv->prim.space, (n) * DMA_BLOCK_SIZE);	\
+	}								\
+	prim = dev_priv->prim.start;					\
+	write = dev_priv->prim.tail;					\
+} while (0)
+
+#define BEGIN_DMA_WRAP()						\
+do {									\
+	if (MGA_VERBOSE) {						\
+		DRM_INFO("BEGIN_DMA()\n");				\
+		DRM_INFO("   space=0x%x\n", dev_priv->prim.space);	\
+	}								\
+	prim = dev_priv->prim.start;					\
+	write = dev_priv->prim.tail;					\
+} while (0)
+
+#define ADVANCE_DMA()							\
+do {									\
+	dev_priv->prim.tail = write;					\
+	if (MGA_VERBOSE)						\
+		DRM_INFO("ADVANCE_DMA() tail=0x%05x sp=0x%x\n",		\
+			 write, dev_priv->prim.space);			\
+} while (0)
+
+#define FLUSH_DMA()							\
+do {									\
+	if (0) {							\
+		DRM_INFO("\n");						\
+		DRM_INFO("   tail=0x%06x head=0x%06lx\n",		\
+			 dev_priv->prim.tail,				\
+			 (unsigned long)(MGA_READ(MGA_PRIMADDRESS) -	\
+					 dev_priv->primary->offset));	\
+	}								\
+	if (!test_bit(0, &dev_priv->prim.wrapped)) {			\
+		if (dev_priv->prim.space < dev_priv->prim.high_mark)	\
+			mga_do_dma_wrap_start(dev_priv);		\
+		else							\
+			mga_do_dma_flush(dev_priv);			\
+	}								\
+} while (0)
+
+/* Never use this, always use DMA_BLOCK(...) for primary DMA output.
+ */
+#define DMA_WRITE(offset, val)						\
+do {									\
+	if (MGA_VERBOSE)						\
+		DRM_INFO("   DMA_WRITE( 0x%08x ) at 0x%04zx\n",		\
+			 (u32)(val), write + (offset) * sizeof(u32));	\
+	*(volatile u32 *)(prim + write + (offset) * sizeof(u32)) = val;	\
+} while (0)
+
+#define DMA_BLOCK(reg0, val0, reg1, val1, reg2, val2, reg3, val3)	\
+do {									\
+	DMA_WRITE(0, ((DMAREG(reg0) << 0) |				\
+		      (DMAREG(reg1) << 8) |				\
+		      (DMAREG(reg2) << 16) |				\
+		      (DMAREG(reg3) << 24)));				\
+	DMA_WRITE(1, val0);						\
+	DMA_WRITE(2, val1);						\
+	DMA_WRITE(3, val2);						\
+	DMA_WRITE(4, val3);						\
+	write += DMA_BLOCK_SIZE;					\
+} while (0)
+
+/* Buffer aging via primary DMA stream head pointer.
+ */
+
+#define SET_AGE(age, h, w)						\
+do {									\
+	(age)->head = h;						\
+	(age)->wrap = w;						\
+} while (0)
+
+#define TEST_AGE(age, h, w)		((age)->wrap < w ||		\
+					 ((age)->wrap == w &&		\
+					  (age)->head < h))
+
+#define AGE_BUFFER(buf_priv)						\
+do {									\
+	drm_mga_freelist_t *entry = (buf_priv)->list_entry;		\
+	if ((buf_priv)->dispatched) {					\
+		entry->age.head = (dev_priv->prim.tail +		\
+				   dev_priv->primary->offset);		\
+		entry->age.wrap = dev_priv->sarea_priv->last_wrap;	\
+	} else {							\
+		entry->age.head = 0;					\
+		entry->age.wrap = 0;					\
+	}								\
+} while (0)
+
+#define MGA_ENGINE_IDLE_MASK		(MGA_SOFTRAPEN |		\
+					 MGA_DWGENGSTS |		\
+					 MGA_ENDPRDMASTS)
+#define MGA_DMA_IDLE_MASK		(MGA_SOFTRAPEN |		\
+					 MGA_ENDPRDMASTS)
+
+#define MGA_DMA_DEBUG			0
+
+/* A reduced set of the mga registers.
+ */
+#define MGA_CRTC_INDEX			0x1fd4
+#define MGA_CRTC_DATA			0x1fd5
+
+/* CRTC11 */
+#define MGA_VINTCLR			(1 << 4)
+#define MGA_VINTEN			(1 << 5)
+
+#define MGA_ALPHACTRL			0x2c7c
+#define MGA_AR0				0x1c60
+#define MGA_AR1				0x1c64
+#define MGA_AR2				0x1c68
+#define MGA_AR3				0x1c6c
+#define MGA_AR4				0x1c70
+#define MGA_AR5				0x1c74
+#define MGA_AR6				0x1c78
+
+#define MGA_CXBNDRY			0x1c80
+#define MGA_CXLEFT			0x1ca0
+#define MGA_CXRIGHT			0x1ca4
+
+#define MGA_DMAPAD			0x1c54
+#define MGA_DSTORG			0x2cb8
+#define MGA_DWGCTL			0x1c00
+#	define MGA_OPCOD_MASK			(15 << 0)
+#	define MGA_OPCOD_TRAP			(4 << 0)
+#	define MGA_OPCOD_TEXTURE_TRAP		(6 << 0)
+#	define MGA_OPCOD_BITBLT			(8 << 0)
+#	define MGA_OPCOD_ILOAD			(9 << 0)
+#	define MGA_ATYPE_MASK			(7 << 4)
+#	define MGA_ATYPE_RPL			(0 << 4)
+#	define MGA_ATYPE_RSTR			(1 << 4)
+#	define MGA_ATYPE_ZI			(3 << 4)
+#	define MGA_ATYPE_BLK			(4 << 4)
+#	define MGA_ATYPE_I			(7 << 4)
+#	define MGA_LINEAR			(1 << 7)
+#	define MGA_ZMODE_MASK			(7 << 8)
+#	define MGA_ZMODE_NOZCMP			(0 << 8)
+#	define MGA_ZMODE_ZE			(2 << 8)
+#	define MGA_ZMODE_ZNE			(3 << 8)
+#	define MGA_ZMODE_ZLT			(4 << 8)
+#	define MGA_ZMODE_ZLTE			(5 << 8)
+#	define MGA_ZMODE_ZGT			(6 << 8)
+#	define MGA_ZMODE_ZGTE			(7 << 8)
+#	define MGA_SOLID			(1 << 11)
+#	define MGA_ARZERO			(1 << 12)
+#	define MGA_SGNZERO			(1 << 13)
+#	define MGA_SHIFTZERO			(1 << 14)
+#	define MGA_BOP_MASK			(15 << 16)
+#	define MGA_BOP_ZERO			(0 << 16)
+#	define MGA_BOP_DST			(10 << 16)
+#	define MGA_BOP_SRC			(12 << 16)
+#	define MGA_BOP_ONE			(15 << 16)
+#	define MGA_TRANS_SHIFT			20
+#	define MGA_TRANS_MASK			(15 << 20)
+#	define MGA_BLTMOD_MASK			(15 << 25)
+#	define MGA_BLTMOD_BMONOLEF		(0 << 25)
+#	define MGA_BLTMOD_BMONOWF		(4 << 25)
+#	define MGA_BLTMOD_PLAN			(1 << 25)
+#	define MGA_BLTMOD_BFCOL			(2 << 25)
+#	define MGA_BLTMOD_BU32BGR		(3 << 25)
+#	define MGA_BLTMOD_BU32RGB		(7 << 25)
+#	define MGA_BLTMOD_BU24BGR		(11 << 25)
+#	define MGA_BLTMOD_BU24RGB		(15 << 25)
+#	define MGA_PATTERN			(1 << 29)
+#	define MGA_TRANSC			(1 << 30)
+#	define MGA_CLIPDIS			(1 << 31)
+#define MGA_DWGSYNC			0x2c4c
+
+#define MGA_FCOL			0x1c24
+#define MGA_FIFOSTATUS			0x1e10
+#define MGA_FOGCOL			0x1cf4
+#define MGA_FXBNDRY			0x1c84
+#define MGA_FXLEFT			0x1ca8
+#define MGA_FXRIGHT			0x1cac
+
+#define MGA_ICLEAR			0x1e18
+#	define MGA_SOFTRAPICLR			(1 << 0)
+#	define MGA_VLINEICLR			(1 << 5)
+#define MGA_IEN				0x1e1c
+#	define MGA_SOFTRAPIEN			(1 << 0)
+#	define MGA_VLINEIEN			(1 << 5)
+
+#define MGA_LEN				0x1c5c
+
+#define MGA_MACCESS			0x1c04
+
+#define MGA_PITCH			0x1c8c
+#define MGA_PLNWT			0x1c1c
+#define MGA_PRIMADDRESS			0x1e58
+#	define MGA_DMA_GENERAL			(0 << 0)
+#	define MGA_DMA_BLIT			(1 << 0)
+#	define MGA_DMA_VECTOR			(2 << 0)
+#	define MGA_DMA_VERTEX			(3 << 0)
+#define MGA_PRIMEND			0x1e5c
+#	define MGA_PRIMNOSTART			(1 << 0)
+#	define MGA_PAGPXFER			(1 << 1)
+#define MGA_PRIMPTR			0x1e50
+#	define MGA_PRIMPTREN0			(1 << 0)
+#	define MGA_PRIMPTREN1			(1 << 1)
+
+#define MGA_RST				0x1e40
+#	define MGA_SOFTRESET			(1 << 0)
+#	define MGA_SOFTEXTRST			(1 << 1)
+
+#define MGA_SECADDRESS			0x2c40
+#define MGA_SECEND			0x2c44
+#define MGA_SETUPADDRESS		0x2cd0
+#define MGA_SETUPEND			0x2cd4
+#define MGA_SGN				0x1c58
+#define MGA_SOFTRAP			0x2c48
+#define MGA_SRCORG			0x2cb4
+#	define MGA_SRMMAP_MASK			(1 << 0)
+#	define MGA_SRCMAP_FB			(0 << 0)
+#	define MGA_SRCMAP_SYSMEM		(1 << 0)
+#	define MGA_SRCACC_MASK			(1 << 1)
+#	define MGA_SRCACC_PCI			(0 << 1)
+#	define MGA_SRCACC_AGP			(1 << 1)
+#define MGA_STATUS			0x1e14
+#	define MGA_SOFTRAPEN			(1 << 0)
+#	define MGA_VSYNCPEN			(1 << 4)
+#	define MGA_VLINEPEN			(1 << 5)
+#	define MGA_DWGENGSTS			(1 << 16)
+#	define MGA_ENDPRDMASTS			(1 << 17)
+#define MGA_STENCIL			0x2cc8
+#define MGA_STENCILCTL			0x2ccc
+
+#define MGA_TDUALSTAGE0			0x2cf8
+#define MGA_TDUALSTAGE1			0x2cfc
+#define MGA_TEXBORDERCOL		0x2c5c
+#define MGA_TEXCTL			0x2c30
+#define MGA_TEXCTL2			0x2c3c
+#	define MGA_DUALTEX			(1 << 7)
+#	define MGA_G400_TC2_MAGIC		(1 << 15)
+#	define MGA_MAP1_ENABLE			(1 << 31)
+#define MGA_TEXFILTER			0x2c58
+#define MGA_TEXHEIGHT			0x2c2c
+#define MGA_TEXORG			0x2c24
+#	define MGA_TEXORGMAP_MASK		(1 << 0)
+#	define MGA_TEXORGMAP_FB			(0 << 0)
+#	define MGA_TEXORGMAP_SYSMEM		(1 << 0)
+#	define MGA_TEXORGACC_MASK		(1 << 1)
+#	define MGA_TEXORGACC_PCI		(0 << 1)
+#	define MGA_TEXORGACC_AGP		(1 << 1)
+#define MGA_TEXORG1			0x2ca4
+#define MGA_TEXORG2			0x2ca8
+#define MGA_TEXORG3			0x2cac
+#define MGA_TEXORG4			0x2cb0
+#define MGA_TEXTRANS			0x2c34
+#define MGA_TEXTRANSHIGH		0x2c38
+#define MGA_TEXWIDTH			0x2c28
+
+#define MGA_WACCEPTSEQ			0x1dd4
+#define MGA_WCODEADDR			0x1e6c
+#define MGA_WFLAG			0x1dc4
+#define MGA_WFLAG1			0x1de0
+#define MGA_WFLAGNB			0x1e64
+#define MGA_WFLAGNB1			0x1e08
+#define MGA_WGETMSB			0x1dc8
+#define MGA_WIADDR			0x1dc0
+#define MGA_WIADDR2			0x1dd8
+#	define MGA_WMODE_SUSPEND		(0 << 0)
+#	define MGA_WMODE_RESUME			(1 << 0)
+#	define MGA_WMODE_JUMP			(2 << 0)
+#	define MGA_WMODE_START			(3 << 0)
+#	define MGA_WAGP_ENABLE			(1 << 2)
+#define MGA_WMISC			0x1e70
+#	define MGA_WUCODECACHE_ENABLE		(1 << 0)
+#	define MGA_WMASTER_ENABLE		(1 << 1)
+#	define MGA_WCACHEFLUSH_ENABLE		(1 << 3)
+#define MGA_WVRTXSZ			0x1dcc
+
+#define MGA_YBOT			0x1c9c
+#define MGA_YDST			0x1c90
+#define MGA_YDSTLEN			0x1c88
+#define MGA_YDSTORG			0x1c94
+#define MGA_YTOP			0x1c98
+
+#define MGA_ZORG			0x1c0c
+
+/* This finishes the current batch of commands
+ */
+#define MGA_EXEC			0x0100
+
+/* AGP PLL encoding (for G200 only).
+ */
+#define MGA_AGP_PLL			0x1e4c
+#	define MGA_AGP2XPLL_DISABLE		(0 << 0)
+#	define MGA_AGP2XPLL_ENABLE		(1 << 0)
+
+/* Warp registers
+ */
+#define MGA_WR0				0x2d00
+#define MGA_WR1				0x2d04
+#define MGA_WR2				0x2d08
+#define MGA_WR3				0x2d0c
+#define MGA_WR4				0x2d10
+#define MGA_WR5				0x2d14
+#define MGA_WR6				0x2d18
+#define MGA_WR7				0x2d1c
+#define MGA_WR8				0x2d20
+#define MGA_WR9				0x2d24
+#define MGA_WR10			0x2d28
+#define MGA_WR11			0x2d2c
+#define MGA_WR12			0x2d30
+#define MGA_WR13			0x2d34
+#define MGA_WR14			0x2d38
+#define MGA_WR15			0x2d3c
+#define MGA_WR16			0x2d40
+#define MGA_WR17			0x2d44
+#define MGA_WR18			0x2d48
+#define MGA_WR19			0x2d4c
+#define MGA_WR20			0x2d50
+#define MGA_WR21			0x2d54
+#define MGA_WR22			0x2d58
+#define MGA_WR23			0x2d5c
+#define MGA_WR24			0x2d60
+#define MGA_WR25			0x2d64
+#define MGA_WR26			0x2d68
+#define MGA_WR27			0x2d6c
+#define MGA_WR28			0x2d70
+#define MGA_WR29			0x2d74
+#define MGA_WR30			0x2d78
+#define MGA_WR31			0x2d7c
+#define MGA_WR32			0x2d80
+#define MGA_WR33			0x2d84
+#define MGA_WR34			0x2d88
+#define MGA_WR35			0x2d8c
+#define MGA_WR36			0x2d90
+#define MGA_WR37			0x2d94
+#define MGA_WR38			0x2d98
+#define MGA_WR39			0x2d9c
+#define MGA_WR40			0x2da0
+#define MGA_WR41			0x2da4
+#define MGA_WR42			0x2da8
+#define MGA_WR43			0x2dac
+#define MGA_WR44			0x2db0
+#define MGA_WR45			0x2db4
+#define MGA_WR46			0x2db8
+#define MGA_WR47			0x2dbc
+#define MGA_WR48			0x2dc0
+#define MGA_WR49			0x2dc4
+#define MGA_WR50			0x2dc8
+#define MGA_WR51			0x2dcc
+#define MGA_WR52			0x2dd0
+#define MGA_WR53			0x2dd4
+#define MGA_WR54			0x2dd8
+#define MGA_WR55			0x2ddc
+#define MGA_WR56			0x2de0
+#define MGA_WR57			0x2de4
+#define MGA_WR58			0x2de8
+#define MGA_WR59			0x2dec
+#define MGA_WR60			0x2df0
+#define MGA_WR61			0x2df4
+#define MGA_WR62			0x2df8
+#define MGA_WR63			0x2dfc
+#	define MGA_G400_WR_MAGIC		(1 << 6)
+#	define MGA_G400_WR56_MAGIC		0x46480000	/* 12800.0f */
+
+#define MGA_ILOAD_ALIGN		64
+#define MGA_ILOAD_MASK		(MGA_ILOAD_ALIGN - 1)
+
+#define MGA_DWGCTL_FLUSH	(MGA_OPCOD_TEXTURE_TRAP |		\
+				 MGA_ATYPE_I |				\
+				 MGA_ZMODE_NOZCMP |			\
+				 MGA_ARZERO |				\
+				 MGA_SGNZERO |				\
+				 MGA_BOP_SRC |				\
+				 (15 << MGA_TRANS_SHIFT))
+
+#define MGA_DWGCTL_CLEAR	(MGA_OPCOD_TRAP |			\
+				 MGA_ZMODE_NOZCMP |			\
+				 MGA_SOLID |				\
+				 MGA_ARZERO |				\
+				 MGA_SGNZERO |				\
+				 MGA_SHIFTZERO |			\
+				 MGA_BOP_SRC |				\
+				 (0 << MGA_TRANS_SHIFT) |		\
+				 MGA_BLTMOD_BMONOLEF |			\
+				 MGA_TRANSC |				\
+				 MGA_CLIPDIS)
+
+#define MGA_DWGCTL_COPY		(MGA_OPCOD_BITBLT |			\
+				 MGA_ATYPE_RPL |			\
+				 MGA_SGNZERO |				\
+				 MGA_SHIFTZERO |			\
+				 MGA_BOP_SRC |				\
+				 (0 << MGA_TRANS_SHIFT) |		\
+				 MGA_BLTMOD_BFCOL |			\
+				 MGA_CLIPDIS)
+
+/* Simple idle test.
+ */
+static __inline__ int mga_is_idle(drm_mga_private_t *dev_priv)
+{
+	u32 status = MGA_READ(MGA_STATUS) & MGA_ENGINE_IDLE_MASK;
+	return (status == MGA_ENDPRDMASTS);
+}
+
+#endif
diff --git a/marvell/linux/drivers/gpu/drm/mga/mga_ioc32.c b/marvell/linux/drivers/gpu/drm/mga/mga_ioc32.c
new file mode 100644
index 0000000..6ccd270
--- /dev/null
+++ b/marvell/linux/drivers/gpu/drm/mga/mga_ioc32.c
@@ -0,0 +1,196 @@
+/**
+ * \file mga_ioc32.c
+ *
+ * 32-bit ioctl compatibility routines for the MGA DRM.
+ *
+ * \author Dave Airlie <airlied@linux.ie> with code from patches by Egbert Eich
+ *
+ *
+ * Copyright (C) Paul Mackerras 2005
+ * Copyright (C) Egbert Eich 2003,2004
+ * Copyright (C) Dave Airlie 2005
+ * 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
+ * THE AUTHOR 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.
+ */
+
+#include <linux/compat.h>
+
+#include "mga_drv.h"
+
+typedef struct drm32_mga_init {
+	int func;
+	u32 sarea_priv_offset;
+	int chipset;
+	int sgram;
+	unsigned int maccess;
+	unsigned int fb_cpp;
+	unsigned int front_offset, front_pitch;
+	unsigned int back_offset, back_pitch;
+	unsigned int depth_cpp;
+	unsigned int depth_offset, depth_pitch;
+	unsigned int texture_offset[MGA_NR_TEX_HEAPS];
+	unsigned int texture_size[MGA_NR_TEX_HEAPS];
+	u32 fb_offset;
+	u32 mmio_offset;
+	u32 status_offset;
+	u32 warp_offset;
+	u32 primary_offset;
+	u32 buffers_offset;
+} drm_mga_init32_t;
+
+static int compat_mga_init(struct file *file, unsigned int cmd,
+			   unsigned long arg)
+{
+	drm_mga_init32_t init32;
+	drm_mga_init_t init;
+
+	if (copy_from_user(&init32, (void __user *)arg, sizeof(init32)))
+		return -EFAULT;
+
+	init.func = init32.func;
+	init.sarea_priv_offset = init32.sarea_priv_offset;
+	memcpy(&init.chipset, &init32.chipset,
+		offsetof(drm_mga_init_t, fb_offset) -
+		offsetof(drm_mga_init_t, chipset));
+	init.fb_offset = init32.fb_offset;
+	init.mmio_offset = init32.mmio_offset;
+	init.status_offset = init32.status_offset;
+	init.warp_offset = init32.warp_offset;
+	init.primary_offset = init32.primary_offset;
+	init.buffers_offset = init32.buffers_offset;
+
+	return drm_ioctl_kernel(file, mga_dma_init, &init,
+				DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY);
+}
+
+typedef struct drm_mga_getparam32 {
+	int param;
+	u32 value;
+} drm_mga_getparam32_t;
+
+static int compat_mga_getparam(struct file *file, unsigned int cmd,
+			       unsigned long arg)
+{
+	drm_mga_getparam32_t getparam32;
+	drm_mga_getparam_t getparam;
+
+	if (copy_from_user(&getparam32, (void __user *)arg, sizeof(getparam32)))
+		return -EFAULT;
+
+	getparam.param = getparam32.param;
+	getparam.value = compat_ptr(getparam32.value);
+	return drm_ioctl_kernel(file, mga_getparam, &getparam, DRM_AUTH);
+}
+
+typedef struct drm_mga_drm_bootstrap32 {
+	u32 texture_handle;
+	u32 texture_size;
+	u32 primary_size;
+	u32 secondary_bin_count;
+	u32 secondary_bin_size;
+	u32 agp_mode;
+	u8 agp_size;
+} drm_mga_dma_bootstrap32_t;
+
+static int compat_mga_dma_bootstrap(struct file *file, unsigned int cmd,
+				    unsigned long arg)
+{
+	drm_mga_dma_bootstrap32_t dma_bootstrap32;
+	drm_mga_dma_bootstrap_t dma_bootstrap;
+	int err;
+
+	if (copy_from_user(&dma_bootstrap32, (void __user *)arg,
+			   sizeof(dma_bootstrap32)))
+		return -EFAULT;
+
+	dma_bootstrap.texture_handle = dma_bootstrap32.texture_handle;
+	dma_bootstrap.texture_size = dma_bootstrap32.texture_size;
+	dma_bootstrap.primary_size = dma_bootstrap32.primary_size;
+	dma_bootstrap.secondary_bin_count = dma_bootstrap32.secondary_bin_count;
+	dma_bootstrap.secondary_bin_size = dma_bootstrap32.secondary_bin_size;
+	dma_bootstrap.agp_mode = dma_bootstrap32.agp_mode;
+	dma_bootstrap.agp_size = dma_bootstrap32.agp_size;
+
+	err = drm_ioctl_kernel(file, mga_dma_bootstrap, &dma_bootstrap,
+				DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY);
+	if (err)
+		return err;
+
+	dma_bootstrap32.texture_handle = dma_bootstrap.texture_handle;
+	dma_bootstrap32.texture_size = dma_bootstrap.texture_size;
+	dma_bootstrap32.primary_size = dma_bootstrap.primary_size;
+	dma_bootstrap32.secondary_bin_count = dma_bootstrap.secondary_bin_count;
+	dma_bootstrap32.secondary_bin_size = dma_bootstrap.secondary_bin_size;
+	dma_bootstrap32.agp_mode = dma_bootstrap.agp_mode;
+	dma_bootstrap32.agp_size = dma_bootstrap.agp_size;
+	if (copy_to_user((void __user *)arg, &dma_bootstrap32,
+			 sizeof(dma_bootstrap32)))
+		return -EFAULT;
+
+	return 0;
+}
+
+static struct {
+	drm_ioctl_compat_t *fn;
+	char *name;
+} mga_compat_ioctls[] = {
+#define DRM_IOCTL32_DEF(n, f)[DRM_##n] = {.fn = f, .name = #n}
+	DRM_IOCTL32_DEF(MGA_INIT, compat_mga_init),
+	DRM_IOCTL32_DEF(MGA_GETPARAM, compat_mga_getparam),
+	DRM_IOCTL32_DEF(MGA_DMA_BOOTSTRAP, compat_mga_dma_bootstrap),
+};
+
+/**
+ * Called whenever a 32-bit process running under a 64-bit kernel
+ * performs an ioctl on /dev/dri/card<n>.
+ *
+ * \param filp file pointer.
+ * \param cmd command.
+ * \param arg user argument.
+ * \return zero on success or negative number on failure.
+ */
+long mga_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	unsigned int nr = DRM_IOCTL_NR(cmd);
+	struct drm_file *file_priv = filp->private_data;
+	drm_ioctl_compat_t *fn = NULL;
+	int ret;
+
+	if (nr < DRM_COMMAND_BASE)
+		return drm_compat_ioctl(filp, cmd, arg);
+
+	if (nr >= DRM_COMMAND_BASE + ARRAY_SIZE(mga_compat_ioctls))
+		return drm_ioctl(filp, cmd, arg);
+
+	fn = mga_compat_ioctls[nr - DRM_COMMAND_BASE].fn;
+	if (!fn)
+		return drm_ioctl(filp, cmd, arg);
+
+	DRM_DEBUG("pid=%d, dev=0x%lx, auth=%d, %s\n",
+		  task_pid_nr(current),
+		  (long)old_encode_dev(file_priv->minor->kdev->devt),
+		  file_priv->authenticated,
+		  mga_compat_ioctls[nr - DRM_COMMAND_BASE].name);
+	ret = (*fn) (filp, cmd, arg);
+	if (ret)
+		DRM_DEBUG("ret = %d\n", ret);
+	return ret;
+}
diff --git a/marvell/linux/drivers/gpu/drm/mga/mga_irq.c b/marvell/linux/drivers/gpu/drm/mga/mga_irq.c
new file mode 100644
index 0000000..a7e6ffc
--- /dev/null
+++ b/marvell/linux/drivers/gpu/drm/mga/mga_irq.c
@@ -0,0 +1,169 @@
+/* mga_irq.c -- IRQ handling for radeon -*- linux-c -*-
+ */
+/*
+ * Copyright (C) The Weather Channel, Inc.  2002.  All Rights Reserved.
+ *
+ * The Weather Channel (TM) funded Tungsten Graphics to develop the
+ * initial release of the Radeon 8500 driver under the XFree86 license.
+ * This notice must be preserved.
+ *
+ * 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.
+ *
+ * Authors:
+ *    Keith Whitwell <keith@tungstengraphics.com>
+ *    Eric Anholt <anholt@FreeBSD.org>
+ */
+
+#include "mga_drv.h"
+
+u32 mga_get_vblank_counter(struct drm_device *dev, unsigned int pipe)
+{
+	const drm_mga_private_t *const dev_priv =
+		(drm_mga_private_t *) dev->dev_private;
+
+	if (pipe != 0)
+		return 0;
+
+	return atomic_read(&dev_priv->vbl_received);
+}
+
+
+irqreturn_t mga_driver_irq_handler(int irq, void *arg)
+{
+	struct drm_device *dev = (struct drm_device *) arg;
+	drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private;
+	int status;
+	int handled = 0;
+
+	status = MGA_READ(MGA_STATUS);
+
+	/* VBLANK interrupt */
+	if (status & MGA_VLINEPEN) {
+		MGA_WRITE(MGA_ICLEAR, MGA_VLINEICLR);
+		atomic_inc(&dev_priv->vbl_received);
+		drm_handle_vblank(dev, 0);
+		handled = 1;
+	}
+
+	/* SOFTRAP interrupt */
+	if (status & MGA_SOFTRAPEN) {
+		const u32 prim_start = MGA_READ(MGA_PRIMADDRESS);
+		const u32 prim_end = MGA_READ(MGA_PRIMEND);
+
+
+		MGA_WRITE(MGA_ICLEAR, MGA_SOFTRAPICLR);
+
+		/* In addition to clearing the interrupt-pending bit, we
+		 * have to write to MGA_PRIMEND to re-start the DMA operation.
+		 */
+		if ((prim_start & ~0x03) != (prim_end & ~0x03))
+			MGA_WRITE(MGA_PRIMEND, prim_end);
+
+		atomic_inc(&dev_priv->last_fence_retired);
+		wake_up(&dev_priv->fence_queue);
+		handled = 1;
+	}
+
+	if (handled)
+		return IRQ_HANDLED;
+	return IRQ_NONE;
+}
+
+int mga_enable_vblank(struct drm_device *dev, unsigned int pipe)
+{
+	drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private;
+
+	if (pipe != 0) {
+		DRM_ERROR("tried to enable vblank on non-existent crtc %u\n",
+			  pipe);
+		return 0;
+	}
+
+	MGA_WRITE(MGA_IEN, MGA_VLINEIEN | MGA_SOFTRAPEN);
+	return 0;
+}
+
+
+void mga_disable_vblank(struct drm_device *dev, unsigned int pipe)
+{
+	if (pipe != 0) {
+		DRM_ERROR("tried to disable vblank on non-existent crtc %u\n",
+			  pipe);
+	}
+
+	/* Do *NOT* disable the vertical refresh interrupt.  MGA doesn't have
+	 * a nice hardware counter that tracks the number of refreshes when
+	 * the interrupt is disabled, and the kernel doesn't know the refresh
+	 * rate to calculate an estimate.
+	 */
+	/* MGA_WRITE(MGA_IEN, MGA_VLINEIEN | MGA_SOFTRAPEN); */
+}
+
+void mga_driver_fence_wait(struct drm_device *dev, unsigned int *sequence)
+{
+	drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private;
+	unsigned int cur_fence;
+
+	/* Assume that the user has missed the current sequence number
+	 * by about a day rather than she wants to wait for years
+	 * using fences.
+	 */
+	wait_event_timeout(dev_priv->fence_queue,
+		    (((cur_fence = atomic_read(&dev_priv->last_fence_retired))
+		      - *sequence) <= (1 << 23)),
+		    msecs_to_jiffies(3000));
+
+	*sequence = cur_fence;
+}
+
+void mga_driver_irq_preinstall(struct drm_device *dev)
+{
+	drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private;
+
+	/* Disable *all* interrupts */
+	MGA_WRITE(MGA_IEN, 0);
+	/* Clear bits if they're already high */
+	MGA_WRITE(MGA_ICLEAR, ~0);
+}
+
+int mga_driver_irq_postinstall(struct drm_device *dev)
+{
+	drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private;
+
+	init_waitqueue_head(&dev_priv->fence_queue);
+
+	/* Turn on soft trap interrupt.  Vertical blank interrupts are enabled
+	 * in mga_enable_vblank.
+	 */
+	MGA_WRITE(MGA_IEN, MGA_SOFTRAPEN);
+	return 0;
+}
+
+void mga_driver_irq_uninstall(struct drm_device *dev)
+{
+	drm_mga_private_t *dev_priv = (drm_mga_private_t *) dev->dev_private;
+	if (!dev_priv)
+		return;
+
+	/* Disable *all* interrupts */
+	MGA_WRITE(MGA_IEN, 0);
+
+	dev->irq_enabled = false;
+}
diff --git a/marvell/linux/drivers/gpu/drm/mga/mga_state.c b/marvell/linux/drivers/gpu/drm/mga/mga_state.c
new file mode 100644
index 0000000..77a0b00
--- /dev/null
+++ b/marvell/linux/drivers/gpu/drm/mga/mga_state.c
@@ -0,0 +1,1100 @@
+/* mga_state.c -- State support for MGA G200/G400 -*- linux-c -*-
+ * Created: Thu Jan 27 02:53:43 2000 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
+ * VA LINUX SYSTEMS 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:
+ *    Jeff Hartmann <jhartmann@valinux.com>
+ *    Keith Whitwell <keith@tungstengraphics.com>
+ *
+ * Rewritten by:
+ *    Gareth Hughes <gareth@valinux.com>
+ */
+
+#include "mga_drv.h"
+
+/* ================================================================
+ * DMA hardware state programming functions
+ */
+
+static void mga_emit_clip_rect(drm_mga_private_t *dev_priv,
+			       struct drm_clip_rect *box)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_mga_context_regs_t *ctx = &sarea_priv->context_state;
+	unsigned int pitch = dev_priv->front_pitch;
+	DMA_LOCALS;
+
+	BEGIN_DMA(2);
+
+	/* Force reset of DWGCTL on G400 (eliminates clip disable bit).
+	 */
+	if (dev_priv->chipset >= MGA_CARD_TYPE_G400) {
+		DMA_BLOCK(MGA_DWGCTL, ctx->dwgctl,
+			  MGA_LEN + MGA_EXEC, 0x80000000,
+			  MGA_DWGCTL, ctx->dwgctl,
+			  MGA_LEN + MGA_EXEC, 0x80000000);
+	}
+	DMA_BLOCK(MGA_DMAPAD, 0x00000000,
+		  MGA_CXBNDRY, ((box->x2 - 1) << 16) | box->x1,
+		  MGA_YTOP, box->y1 * pitch, MGA_YBOT, (box->y2 - 1) * pitch);
+
+	ADVANCE_DMA();
+}
+
+static __inline__ void mga_g200_emit_context(drm_mga_private_t *dev_priv)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_mga_context_regs_t *ctx = &sarea_priv->context_state;
+	DMA_LOCALS;
+
+	BEGIN_DMA(3);
+
+	DMA_BLOCK(MGA_DSTORG, ctx->dstorg,
+		  MGA_MACCESS, ctx->maccess,
+		  MGA_PLNWT, ctx->plnwt, MGA_DWGCTL, ctx->dwgctl);
+
+	DMA_BLOCK(MGA_ALPHACTRL, ctx->alphactrl,
+		  MGA_FOGCOL, ctx->fogcolor,
+		  MGA_WFLAG, ctx->wflag, MGA_ZORG, dev_priv->depth_offset);
+
+	DMA_BLOCK(MGA_FCOL, ctx->fcol,
+		  MGA_DMAPAD, 0x00000000,
+		  MGA_DMAPAD, 0x00000000, MGA_DMAPAD, 0x00000000);
+
+	ADVANCE_DMA();
+}
+
+static __inline__ void mga_g400_emit_context(drm_mga_private_t *dev_priv)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_mga_context_regs_t *ctx = &sarea_priv->context_state;
+	DMA_LOCALS;
+
+	BEGIN_DMA(4);
+
+	DMA_BLOCK(MGA_DSTORG, ctx->dstorg,
+		  MGA_MACCESS, ctx->maccess,
+		  MGA_PLNWT, ctx->plnwt, MGA_DWGCTL, ctx->dwgctl);
+
+	DMA_BLOCK(MGA_ALPHACTRL, ctx->alphactrl,
+		  MGA_FOGCOL, ctx->fogcolor,
+		  MGA_WFLAG, ctx->wflag, MGA_ZORG, dev_priv->depth_offset);
+
+	DMA_BLOCK(MGA_WFLAG1, ctx->wflag,
+		  MGA_TDUALSTAGE0, ctx->tdualstage0,
+		  MGA_TDUALSTAGE1, ctx->tdualstage1, MGA_FCOL, ctx->fcol);
+
+	DMA_BLOCK(MGA_STENCIL, ctx->stencil,
+		  MGA_STENCILCTL, ctx->stencilctl,
+		  MGA_DMAPAD, 0x00000000, MGA_DMAPAD, 0x00000000);
+
+	ADVANCE_DMA();
+}
+
+static __inline__ void mga_g200_emit_tex0(drm_mga_private_t *dev_priv)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_mga_texture_regs_t *tex = &sarea_priv->tex_state[0];
+	DMA_LOCALS;
+
+	BEGIN_DMA(4);
+
+	DMA_BLOCK(MGA_TEXCTL2, tex->texctl2,
+		  MGA_TEXCTL, tex->texctl,
+		  MGA_TEXFILTER, tex->texfilter,
+		  MGA_TEXBORDERCOL, tex->texbordercol);
+
+	DMA_BLOCK(MGA_TEXORG, tex->texorg,
+		  MGA_TEXORG1, tex->texorg1,
+		  MGA_TEXORG2, tex->texorg2, MGA_TEXORG3, tex->texorg3);
+
+	DMA_BLOCK(MGA_TEXORG4, tex->texorg4,
+		  MGA_TEXWIDTH, tex->texwidth,
+		  MGA_TEXHEIGHT, tex->texheight, MGA_WR24, tex->texwidth);
+
+	DMA_BLOCK(MGA_WR34, tex->texheight,
+		  MGA_TEXTRANS, 0x0000ffff,
+		  MGA_TEXTRANSHIGH, 0x0000ffff, MGA_DMAPAD, 0x00000000);
+
+	ADVANCE_DMA();
+}
+
+static __inline__ void mga_g400_emit_tex0(drm_mga_private_t *dev_priv)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_mga_texture_regs_t *tex = &sarea_priv->tex_state[0];
+	DMA_LOCALS;
+
+/*	printk("mga_g400_emit_tex0 %x %x %x\n", tex->texorg, */
+/*	       tex->texctl, tex->texctl2); */
+
+	BEGIN_DMA(6);
+
+	DMA_BLOCK(MGA_TEXCTL2, tex->texctl2 | MGA_G400_TC2_MAGIC,
+		  MGA_TEXCTL, tex->texctl,
+		  MGA_TEXFILTER, tex->texfilter,
+		  MGA_TEXBORDERCOL, tex->texbordercol);
+
+	DMA_BLOCK(MGA_TEXORG, tex->texorg,
+		  MGA_TEXORG1, tex->texorg1,
+		  MGA_TEXORG2, tex->texorg2, MGA_TEXORG3, tex->texorg3);
+
+	DMA_BLOCK(MGA_TEXORG4, tex->texorg4,
+		  MGA_TEXWIDTH, tex->texwidth,
+		  MGA_TEXHEIGHT, tex->texheight, MGA_WR49, 0x00000000);
+
+	DMA_BLOCK(MGA_WR57, 0x00000000,
+		  MGA_WR53, 0x00000000,
+		  MGA_WR61, 0x00000000, MGA_WR52, MGA_G400_WR_MAGIC);
+
+	DMA_BLOCK(MGA_WR60, MGA_G400_WR_MAGIC,
+		  MGA_WR54, tex->texwidth | MGA_G400_WR_MAGIC,
+		  MGA_WR62, tex->texheight | MGA_G400_WR_MAGIC,
+		  MGA_DMAPAD, 0x00000000);
+
+	DMA_BLOCK(MGA_DMAPAD, 0x00000000,
+		  MGA_DMAPAD, 0x00000000,
+		  MGA_TEXTRANS, 0x0000ffff, MGA_TEXTRANSHIGH, 0x0000ffff);
+
+	ADVANCE_DMA();
+}
+
+static __inline__ void mga_g400_emit_tex1(drm_mga_private_t *dev_priv)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_mga_texture_regs_t *tex = &sarea_priv->tex_state[1];
+	DMA_LOCALS;
+
+/*	printk("mga_g400_emit_tex1 %x %x %x\n", tex->texorg,  */
+/*	       tex->texctl, tex->texctl2); */
+
+	BEGIN_DMA(5);
+
+	DMA_BLOCK(MGA_TEXCTL2, (tex->texctl2 |
+				MGA_MAP1_ENABLE |
+				MGA_G400_TC2_MAGIC),
+		  MGA_TEXCTL, tex->texctl,
+		  MGA_TEXFILTER, tex->texfilter,
+		  MGA_TEXBORDERCOL, tex->texbordercol);
+
+	DMA_BLOCK(MGA_TEXORG, tex->texorg,
+		  MGA_TEXORG1, tex->texorg1,
+		  MGA_TEXORG2, tex->texorg2, MGA_TEXORG3, tex->texorg3);
+
+	DMA_BLOCK(MGA_TEXORG4, tex->texorg4,
+		  MGA_TEXWIDTH, tex->texwidth,
+		  MGA_TEXHEIGHT, tex->texheight, MGA_WR49, 0x00000000);
+
+	DMA_BLOCK(MGA_WR57, 0x00000000,
+		  MGA_WR53, 0x00000000,
+		  MGA_WR61, 0x00000000,
+		  MGA_WR52, tex->texwidth | MGA_G400_WR_MAGIC);
+
+	DMA_BLOCK(MGA_WR60, tex->texheight | MGA_G400_WR_MAGIC,
+		  MGA_TEXTRANS, 0x0000ffff,
+		  MGA_TEXTRANSHIGH, 0x0000ffff,
+		  MGA_TEXCTL2, tex->texctl2 | MGA_G400_TC2_MAGIC);
+
+	ADVANCE_DMA();
+}
+
+static __inline__ void mga_g200_emit_pipe(drm_mga_private_t *dev_priv)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int pipe = sarea_priv->warp_pipe;
+	DMA_LOCALS;
+
+	BEGIN_DMA(3);
+
+	DMA_BLOCK(MGA_WIADDR, MGA_WMODE_SUSPEND,
+		  MGA_WVRTXSZ, 0x00000007,
+		  MGA_WFLAG, 0x00000000, MGA_WR24, 0x00000000);
+
+	DMA_BLOCK(MGA_WR25, 0x00000100,
+		  MGA_WR34, 0x00000000,
+		  MGA_WR42, 0x0000ffff, MGA_WR60, 0x0000ffff);
+
+	/* Padding required due to hardware bug.
+	 */
+	DMA_BLOCK(MGA_DMAPAD, 0xffffffff,
+		  MGA_DMAPAD, 0xffffffff,
+		  MGA_DMAPAD, 0xffffffff,
+		  MGA_WIADDR, (dev_priv->warp_pipe_phys[pipe] |
+			       MGA_WMODE_START | dev_priv->wagp_enable));
+
+	ADVANCE_DMA();
+}
+
+static __inline__ void mga_g400_emit_pipe(drm_mga_private_t *dev_priv)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int pipe = sarea_priv->warp_pipe;
+	DMA_LOCALS;
+
+/*	printk("mga_g400_emit_pipe %x\n", pipe); */
+
+	BEGIN_DMA(10);
+
+	DMA_BLOCK(MGA_WIADDR2, MGA_WMODE_SUSPEND,
+		  MGA_DMAPAD, 0x00000000,
+		  MGA_DMAPAD, 0x00000000, MGA_DMAPAD, 0x00000000);
+
+	if (pipe & MGA_T2) {
+		DMA_BLOCK(MGA_WVRTXSZ, 0x00001e09,
+			  MGA_DMAPAD, 0x00000000,
+			  MGA_DMAPAD, 0x00000000, MGA_DMAPAD, 0x00000000);
+
+		DMA_BLOCK(MGA_WACCEPTSEQ, 0x00000000,
+			  MGA_WACCEPTSEQ, 0x00000000,
+			  MGA_WACCEPTSEQ, 0x00000000,
+			  MGA_WACCEPTSEQ, 0x1e000000);
+	} else {
+		if (dev_priv->warp_pipe & MGA_T2) {
+			/* Flush the WARP pipe */
+			DMA_BLOCK(MGA_YDST, 0x00000000,
+				  MGA_FXLEFT, 0x00000000,
+				  MGA_FXRIGHT, 0x00000001,
+				  MGA_DWGCTL, MGA_DWGCTL_FLUSH);
+
+			DMA_BLOCK(MGA_LEN + MGA_EXEC, 0x00000001,
+				  MGA_DWGSYNC, 0x00007000,
+				  MGA_TEXCTL2, MGA_G400_TC2_MAGIC,
+				  MGA_LEN + MGA_EXEC, 0x00000000);
+
+			DMA_BLOCK(MGA_TEXCTL2, (MGA_DUALTEX |
+						MGA_G400_TC2_MAGIC),
+				  MGA_LEN + MGA_EXEC, 0x00000000,
+				  MGA_TEXCTL2, MGA_G400_TC2_MAGIC,
+				  MGA_DMAPAD, 0x00000000);
+		}
+
+		DMA_BLOCK(MGA_WVRTXSZ, 0x00001807,
+			  MGA_DMAPAD, 0x00000000,
+			  MGA_DMAPAD, 0x00000000, MGA_DMAPAD, 0x00000000);
+
+		DMA_BLOCK(MGA_WACCEPTSEQ, 0x00000000,
+			  MGA_WACCEPTSEQ, 0x00000000,
+			  MGA_WACCEPTSEQ, 0x00000000,
+			  MGA_WACCEPTSEQ, 0x18000000);
+	}
+
+	DMA_BLOCK(MGA_WFLAG, 0x00000000,
+		  MGA_WFLAG1, 0x00000000,
+		  MGA_WR56, MGA_G400_WR56_MAGIC, MGA_DMAPAD, 0x00000000);
+
+	DMA_BLOCK(MGA_WR49, 0x00000000,	/* tex0              */
+		  MGA_WR57, 0x00000000,	/* tex0              */
+		  MGA_WR53, 0x00000000,	/* tex1              */
+		  MGA_WR61, 0x00000000);	/* tex1              */
+
+	DMA_BLOCK(MGA_WR54, MGA_G400_WR_MAGIC,	/* tex0 width        */
+		  MGA_WR62, MGA_G400_WR_MAGIC,	/* tex0 height       */
+		  MGA_WR52, MGA_G400_WR_MAGIC,	/* tex1 width        */
+		  MGA_WR60, MGA_G400_WR_MAGIC);	/* tex1 height       */
+
+	/* Padding required due to hardware bug */
+	DMA_BLOCK(MGA_DMAPAD, 0xffffffff,
+		  MGA_DMAPAD, 0xffffffff,
+		  MGA_DMAPAD, 0xffffffff,
+		  MGA_WIADDR2, (dev_priv->warp_pipe_phys[pipe] |
+				MGA_WMODE_START | dev_priv->wagp_enable));
+
+	ADVANCE_DMA();
+}
+
+static void mga_g200_emit_state(drm_mga_private_t *dev_priv)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int dirty = sarea_priv->dirty;
+
+	if (sarea_priv->warp_pipe != dev_priv->warp_pipe) {
+		mga_g200_emit_pipe(dev_priv);
+		dev_priv->warp_pipe = sarea_priv->warp_pipe;
+	}
+
+	if (dirty & MGA_UPLOAD_CONTEXT) {
+		mga_g200_emit_context(dev_priv);
+		sarea_priv->dirty &= ~MGA_UPLOAD_CONTEXT;
+	}
+
+	if (dirty & MGA_UPLOAD_TEX0) {
+		mga_g200_emit_tex0(dev_priv);
+		sarea_priv->dirty &= ~MGA_UPLOAD_TEX0;
+	}
+}
+
+static void mga_g400_emit_state(drm_mga_private_t *dev_priv)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int dirty = sarea_priv->dirty;
+	int multitex = sarea_priv->warp_pipe & MGA_T2;
+
+	if (sarea_priv->warp_pipe != dev_priv->warp_pipe) {
+		mga_g400_emit_pipe(dev_priv);
+		dev_priv->warp_pipe = sarea_priv->warp_pipe;
+	}
+
+	if (dirty & MGA_UPLOAD_CONTEXT) {
+		mga_g400_emit_context(dev_priv);
+		sarea_priv->dirty &= ~MGA_UPLOAD_CONTEXT;
+	}
+
+	if (dirty & MGA_UPLOAD_TEX0) {
+		mga_g400_emit_tex0(dev_priv);
+		sarea_priv->dirty &= ~MGA_UPLOAD_TEX0;
+	}
+
+	if ((dirty & MGA_UPLOAD_TEX1) && multitex) {
+		mga_g400_emit_tex1(dev_priv);
+		sarea_priv->dirty &= ~MGA_UPLOAD_TEX1;
+	}
+}
+
+/* ================================================================
+ * SAREA state verification
+ */
+
+/* Disallow all write destinations except the front and backbuffer.
+ */
+static int mga_verify_context(drm_mga_private_t *dev_priv)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_mga_context_regs_t *ctx = &sarea_priv->context_state;
+
+	if (ctx->dstorg != dev_priv->front_offset &&
+	    ctx->dstorg != dev_priv->back_offset) {
+		DRM_ERROR("*** bad DSTORG: %x (front %x, back %x)\n\n",
+			  ctx->dstorg, dev_priv->front_offset,
+			  dev_priv->back_offset);
+		ctx->dstorg = 0;
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* Disallow texture reads from PCI space.
+ */
+static int mga_verify_tex(drm_mga_private_t *dev_priv, int unit)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_mga_texture_regs_t *tex = &sarea_priv->tex_state[unit];
+	unsigned int org;
+
+	org = tex->texorg & (MGA_TEXORGMAP_MASK | MGA_TEXORGACC_MASK);
+
+	if (org == (MGA_TEXORGMAP_SYSMEM | MGA_TEXORGACC_PCI)) {
+		DRM_ERROR("*** bad TEXORG: 0x%x, unit %d\n", tex->texorg, unit);
+		tex->texorg = 0;
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int mga_verify_state(drm_mga_private_t *dev_priv)
+{
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	unsigned int dirty = sarea_priv->dirty;
+	int ret = 0;
+
+	if (sarea_priv->nbox > MGA_NR_SAREA_CLIPRECTS)
+		sarea_priv->nbox = MGA_NR_SAREA_CLIPRECTS;
+
+	if (dirty & MGA_UPLOAD_CONTEXT)
+		ret |= mga_verify_context(dev_priv);
+
+	if (dirty & MGA_UPLOAD_TEX0)
+		ret |= mga_verify_tex(dev_priv, 0);
+
+	if (dev_priv->chipset >= MGA_CARD_TYPE_G400) {
+		if (dirty & MGA_UPLOAD_TEX1)
+			ret |= mga_verify_tex(dev_priv, 1);
+
+		if (dirty & MGA_UPLOAD_PIPE)
+			ret |= (sarea_priv->warp_pipe > MGA_MAX_G400_PIPES);
+	} else {
+		if (dirty & MGA_UPLOAD_PIPE)
+			ret |= (sarea_priv->warp_pipe > MGA_MAX_G200_PIPES);
+	}
+
+	return (ret == 0);
+}
+
+static int mga_verify_iload(drm_mga_private_t *dev_priv,
+			    unsigned int dstorg, unsigned int length)
+{
+	if (dstorg < dev_priv->texture_offset ||
+	    dstorg + length > (dev_priv->texture_offset +
+			       dev_priv->texture_size)) {
+		DRM_ERROR("*** bad iload DSTORG: 0x%x\n", dstorg);
+		return -EINVAL;
+	}
+
+	if (length & MGA_ILOAD_MASK) {
+		DRM_ERROR("*** bad iload length: 0x%x\n",
+			  length & MGA_ILOAD_MASK);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int mga_verify_blit(drm_mga_private_t *dev_priv,
+			   unsigned int srcorg, unsigned int dstorg)
+{
+	if ((srcorg & 0x3) == (MGA_SRCACC_PCI | MGA_SRCMAP_SYSMEM) ||
+	    (dstorg & 0x3) == (MGA_SRCACC_PCI | MGA_SRCMAP_SYSMEM)) {
+		DRM_ERROR("*** bad blit: src=0x%x dst=0x%x\n", srcorg, dstorg);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+/* ================================================================
+ *
+ */
+
+static void mga_dma_dispatch_clear(struct drm_device *dev, drm_mga_clear_t *clear)
+{
+	drm_mga_private_t *dev_priv = dev->dev_private;
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_mga_context_regs_t *ctx = &sarea_priv->context_state;
+	struct drm_clip_rect *pbox = sarea_priv->boxes;
+	int nbox = sarea_priv->nbox;
+	int i;
+	DMA_LOCALS;
+	DRM_DEBUG("\n");
+
+	BEGIN_DMA(1);
+
+	DMA_BLOCK(MGA_DMAPAD, 0x00000000,
+		  MGA_DMAPAD, 0x00000000,
+		  MGA_DWGSYNC, 0x00007100, MGA_DWGSYNC, 0x00007000);
+
+	ADVANCE_DMA();
+
+	for (i = 0; i < nbox; i++) {
+		struct drm_clip_rect *box = &pbox[i];
+		u32 height = box->y2 - box->y1;
+
+		DRM_DEBUG("   from=%d,%d to=%d,%d\n",
+			  box->x1, box->y1, box->x2, box->y2);
+
+		if (clear->flags & MGA_FRONT) {
+			BEGIN_DMA(2);
+
+			DMA_BLOCK(MGA_DMAPAD, 0x00000000,
+				  MGA_PLNWT, clear->color_mask,
+				  MGA_YDSTLEN, (box->y1 << 16) | height,
+				  MGA_FXBNDRY, (box->x2 << 16) | box->x1);
+
+			DMA_BLOCK(MGA_DMAPAD, 0x00000000,
+				  MGA_FCOL, clear->clear_color,
+				  MGA_DSTORG, dev_priv->front_offset,
+				  MGA_DWGCTL + MGA_EXEC, dev_priv->clear_cmd);
+
+			ADVANCE_DMA();
+		}
+
+		if (clear->flags & MGA_BACK) {
+			BEGIN_DMA(2);
+
+			DMA_BLOCK(MGA_DMAPAD, 0x00000000,
+				  MGA_PLNWT, clear->color_mask,
+				  MGA_YDSTLEN, (box->y1 << 16) | height,
+				  MGA_FXBNDRY, (box->x2 << 16) | box->x1);
+
+			DMA_BLOCK(MGA_DMAPAD, 0x00000000,
+				  MGA_FCOL, clear->clear_color,
+				  MGA_DSTORG, dev_priv->back_offset,
+				  MGA_DWGCTL + MGA_EXEC, dev_priv->clear_cmd);
+
+			ADVANCE_DMA();
+		}
+
+		if (clear->flags & MGA_DEPTH) {
+			BEGIN_DMA(2);
+
+			DMA_BLOCK(MGA_DMAPAD, 0x00000000,
+				  MGA_PLNWT, clear->depth_mask,
+				  MGA_YDSTLEN, (box->y1 << 16) | height,
+				  MGA_FXBNDRY, (box->x2 << 16) | box->x1);
+
+			DMA_BLOCK(MGA_DMAPAD, 0x00000000,
+				  MGA_FCOL, clear->clear_depth,
+				  MGA_DSTORG, dev_priv->depth_offset,
+				  MGA_DWGCTL + MGA_EXEC, dev_priv->clear_cmd);
+
+			ADVANCE_DMA();
+		}
+
+	}
+
+	BEGIN_DMA(1);
+
+	/* Force reset of DWGCTL */
+	DMA_BLOCK(MGA_DMAPAD, 0x00000000,
+		  MGA_DMAPAD, 0x00000000,
+		  MGA_PLNWT, ctx->plnwt, MGA_DWGCTL, ctx->dwgctl);
+
+	ADVANCE_DMA();
+
+	FLUSH_DMA();
+}
+
+static void mga_dma_dispatch_swap(struct drm_device *dev)
+{
+	drm_mga_private_t *dev_priv = dev->dev_private;
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_mga_context_regs_t *ctx = &sarea_priv->context_state;
+	struct drm_clip_rect *pbox = sarea_priv->boxes;
+	int nbox = sarea_priv->nbox;
+	int i;
+	DMA_LOCALS;
+	DRM_DEBUG("\n");
+
+	sarea_priv->last_frame.head = dev_priv->prim.tail;
+	sarea_priv->last_frame.wrap = dev_priv->prim.last_wrap;
+
+	BEGIN_DMA(4 + nbox);
+
+	DMA_BLOCK(MGA_DMAPAD, 0x00000000,
+		  MGA_DMAPAD, 0x00000000,
+		  MGA_DWGSYNC, 0x00007100, MGA_DWGSYNC, 0x00007000);
+
+	DMA_BLOCK(MGA_DSTORG, dev_priv->front_offset,
+		  MGA_MACCESS, dev_priv->maccess,
+		  MGA_SRCORG, dev_priv->back_offset,
+		  MGA_AR5, dev_priv->front_pitch);
+
+	DMA_BLOCK(MGA_DMAPAD, 0x00000000,
+		  MGA_DMAPAD, 0x00000000,
+		  MGA_PLNWT, 0xffffffff, MGA_DWGCTL, MGA_DWGCTL_COPY);
+
+	for (i = 0; i < nbox; i++) {
+		struct drm_clip_rect *box = &pbox[i];
+		u32 height = box->y2 - box->y1;
+		u32 start = box->y1 * dev_priv->front_pitch;
+
+		DRM_DEBUG("   from=%d,%d to=%d,%d\n",
+			  box->x1, box->y1, box->x2, box->y2);
+
+		DMA_BLOCK(MGA_AR0, start + box->x2 - 1,
+			  MGA_AR3, start + box->x1,
+			  MGA_FXBNDRY, ((box->x2 - 1) << 16) | box->x1,
+			  MGA_YDSTLEN + MGA_EXEC, (box->y1 << 16) | height);
+	}
+
+	DMA_BLOCK(MGA_DMAPAD, 0x00000000,
+		  MGA_PLNWT, ctx->plnwt,
+		  MGA_SRCORG, dev_priv->front_offset, MGA_DWGCTL, ctx->dwgctl);
+
+	ADVANCE_DMA();
+
+	FLUSH_DMA();
+
+	DRM_DEBUG("... done.\n");
+}
+
+static void mga_dma_dispatch_vertex(struct drm_device *dev, struct drm_buf *buf)
+{
+	drm_mga_private_t *dev_priv = dev->dev_private;
+	drm_mga_buf_priv_t *buf_priv = buf->dev_private;
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	u32 address = (u32) buf->bus_address;
+	u32 length = (u32) buf->used;
+	int i = 0;
+	DMA_LOCALS;
+	DRM_DEBUG("buf=%d used=%d\n", buf->idx, buf->used);
+
+	if (buf->used) {
+		buf_priv->dispatched = 1;
+
+		MGA_EMIT_STATE(dev_priv, sarea_priv->dirty);
+
+		do {
+			if (i < sarea_priv->nbox) {
+				mga_emit_clip_rect(dev_priv,
+						   &sarea_priv->boxes[i]);
+			}
+
+			BEGIN_DMA(1);
+
+			DMA_BLOCK(MGA_DMAPAD, 0x00000000,
+				  MGA_DMAPAD, 0x00000000,
+				  MGA_SECADDRESS, (address |
+						   MGA_DMA_VERTEX),
+				  MGA_SECEND, ((address + length) |
+					       dev_priv->dma_access));
+
+			ADVANCE_DMA();
+		} while (++i < sarea_priv->nbox);
+	}
+
+	if (buf_priv->discard) {
+		AGE_BUFFER(buf_priv);
+		buf->pending = 0;
+		buf->used = 0;
+		buf_priv->dispatched = 0;
+
+		mga_freelist_put(dev, buf);
+	}
+
+	FLUSH_DMA();
+}
+
+static void mga_dma_dispatch_indices(struct drm_device *dev, struct drm_buf *buf,
+				     unsigned int start, unsigned int end)
+{
+	drm_mga_private_t *dev_priv = dev->dev_private;
+	drm_mga_buf_priv_t *buf_priv = buf->dev_private;
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	u32 address = (u32) buf->bus_address;
+	int i = 0;
+	DMA_LOCALS;
+	DRM_DEBUG("buf=%d start=%d end=%d\n", buf->idx, start, end);
+
+	if (start != end) {
+		buf_priv->dispatched = 1;
+
+		MGA_EMIT_STATE(dev_priv, sarea_priv->dirty);
+
+		do {
+			if (i < sarea_priv->nbox) {
+				mga_emit_clip_rect(dev_priv,
+						   &sarea_priv->boxes[i]);
+			}
+
+			BEGIN_DMA(1);
+
+			DMA_BLOCK(MGA_DMAPAD, 0x00000000,
+				  MGA_DMAPAD, 0x00000000,
+				  MGA_SETUPADDRESS, address + start,
+				  MGA_SETUPEND, ((address + end) |
+						 dev_priv->dma_access));
+
+			ADVANCE_DMA();
+		} while (++i < sarea_priv->nbox);
+	}
+
+	if (buf_priv->discard) {
+		AGE_BUFFER(buf_priv);
+		buf->pending = 0;
+		buf->used = 0;
+		buf_priv->dispatched = 0;
+
+		mga_freelist_put(dev, buf);
+	}
+
+	FLUSH_DMA();
+}
+
+/* This copies a 64 byte aligned agp region to the frambuffer with a
+ * standard blit, the ioctl needs to do checking.
+ */
+static void mga_dma_dispatch_iload(struct drm_device *dev, struct drm_buf *buf,
+				   unsigned int dstorg, unsigned int length)
+{
+	drm_mga_private_t *dev_priv = dev->dev_private;
+	drm_mga_buf_priv_t *buf_priv = buf->dev_private;
+	drm_mga_context_regs_t *ctx = &dev_priv->sarea_priv->context_state;
+	u32 srcorg =
+	    buf->bus_address | dev_priv->dma_access | MGA_SRCMAP_SYSMEM;
+	u32 y2;
+	DMA_LOCALS;
+	DRM_DEBUG("buf=%d used=%d\n", buf->idx, buf->used);
+
+	y2 = length / 64;
+
+	BEGIN_DMA(5);
+
+	DMA_BLOCK(MGA_DMAPAD, 0x00000000,
+		  MGA_DMAPAD, 0x00000000,
+		  MGA_DWGSYNC, 0x00007100, MGA_DWGSYNC, 0x00007000);
+
+	DMA_BLOCK(MGA_DSTORG, dstorg,
+		  MGA_MACCESS, 0x00000000, MGA_SRCORG, srcorg, MGA_AR5, 64);
+
+	DMA_BLOCK(MGA_PITCH, 64,
+		  MGA_PLNWT, 0xffffffff,
+		  MGA_DMAPAD, 0x00000000, MGA_DWGCTL, MGA_DWGCTL_COPY);
+
+	DMA_BLOCK(MGA_AR0, 63,
+		  MGA_AR3, 0,
+		  MGA_FXBNDRY, (63 << 16) | 0, MGA_YDSTLEN + MGA_EXEC, y2);
+
+	DMA_BLOCK(MGA_PLNWT, ctx->plnwt,
+		  MGA_SRCORG, dev_priv->front_offset,
+		  MGA_PITCH, dev_priv->front_pitch, MGA_DWGSYNC, 0x00007000);
+
+	ADVANCE_DMA();
+
+	AGE_BUFFER(buf_priv);
+
+	buf->pending = 0;
+	buf->used = 0;
+	buf_priv->dispatched = 0;
+
+	mga_freelist_put(dev, buf);
+
+	FLUSH_DMA();
+}
+
+static void mga_dma_dispatch_blit(struct drm_device *dev, drm_mga_blit_t *blit)
+{
+	drm_mga_private_t *dev_priv = dev->dev_private;
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_mga_context_regs_t *ctx = &sarea_priv->context_state;
+	struct drm_clip_rect *pbox = sarea_priv->boxes;
+	int nbox = sarea_priv->nbox;
+	u32 scandir = 0, i;
+	DMA_LOCALS;
+	DRM_DEBUG("\n");
+
+	BEGIN_DMA(4 + nbox);
+
+	DMA_BLOCK(MGA_DMAPAD, 0x00000000,
+		  MGA_DMAPAD, 0x00000000,
+		  MGA_DWGSYNC, 0x00007100, MGA_DWGSYNC, 0x00007000);
+
+	DMA_BLOCK(MGA_DWGCTL, MGA_DWGCTL_COPY,
+		  MGA_PLNWT, blit->planemask,
+		  MGA_SRCORG, blit->srcorg, MGA_DSTORG, blit->dstorg);
+
+	DMA_BLOCK(MGA_SGN, scandir,
+		  MGA_MACCESS, dev_priv->maccess,
+		  MGA_AR5, blit->ydir * blit->src_pitch,
+		  MGA_PITCH, blit->dst_pitch);
+
+	for (i = 0; i < nbox; i++) {
+		int srcx = pbox[i].x1 + blit->delta_sx;
+		int srcy = pbox[i].y1 + blit->delta_sy;
+		int dstx = pbox[i].x1 + blit->delta_dx;
+		int dsty = pbox[i].y1 + blit->delta_dy;
+		int h = pbox[i].y2 - pbox[i].y1;
+		int w = pbox[i].x2 - pbox[i].x1 - 1;
+		int start;
+
+		if (blit->ydir == -1)
+			srcy = blit->height - srcy - 1;
+
+		start = srcy * blit->src_pitch + srcx;
+
+		DMA_BLOCK(MGA_AR0, start + w,
+			  MGA_AR3, start,
+			  MGA_FXBNDRY, ((dstx + w) << 16) | (dstx & 0xffff),
+			  MGA_YDSTLEN + MGA_EXEC, (dsty << 16) | h);
+	}
+
+	/* Do something to flush AGP?
+	 */
+
+	/* Force reset of DWGCTL */
+	DMA_BLOCK(MGA_DMAPAD, 0x00000000,
+		  MGA_PLNWT, ctx->plnwt,
+		  MGA_PITCH, dev_priv->front_pitch, MGA_DWGCTL, ctx->dwgctl);
+
+	ADVANCE_DMA();
+}
+
+/* ================================================================
+ *
+ */
+
+static int mga_dma_clear(struct drm_device *dev, void *data, struct drm_file *file_priv)
+{
+	drm_mga_private_t *dev_priv = dev->dev_private;
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_mga_clear_t *clear = data;
+
+	LOCK_TEST_WITH_RETURN(dev, file_priv);
+
+	if (sarea_priv->nbox > MGA_NR_SAREA_CLIPRECTS)
+		sarea_priv->nbox = MGA_NR_SAREA_CLIPRECTS;
+
+	WRAP_TEST_WITH_RETURN(dev_priv);
+
+	mga_dma_dispatch_clear(dev, clear);
+
+	/* Make sure we restore the 3D state next time.
+	 */
+	dev_priv->sarea_priv->dirty |= MGA_UPLOAD_CONTEXT;
+
+	return 0;
+}
+
+static int mga_dma_swap(struct drm_device *dev, void *data, struct drm_file *file_priv)
+{
+	drm_mga_private_t *dev_priv = dev->dev_private;
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+
+	LOCK_TEST_WITH_RETURN(dev, file_priv);
+
+	if (sarea_priv->nbox > MGA_NR_SAREA_CLIPRECTS)
+		sarea_priv->nbox = MGA_NR_SAREA_CLIPRECTS;
+
+	WRAP_TEST_WITH_RETURN(dev_priv);
+
+	mga_dma_dispatch_swap(dev);
+
+	/* Make sure we restore the 3D state next time.
+	 */
+	dev_priv->sarea_priv->dirty |= MGA_UPLOAD_CONTEXT;
+
+	return 0;
+}
+
+static int mga_dma_vertex(struct drm_device *dev, void *data, struct drm_file *file_priv)
+{
+	drm_mga_private_t *dev_priv = dev->dev_private;
+	struct drm_device_dma *dma = dev->dma;
+	struct drm_buf *buf;
+	drm_mga_buf_priv_t *buf_priv;
+	drm_mga_vertex_t *vertex = data;
+
+	LOCK_TEST_WITH_RETURN(dev, file_priv);
+
+	if (vertex->idx < 0 || vertex->idx > dma->buf_count)
+		return -EINVAL;
+	buf = dma->buflist[vertex->idx];
+	buf_priv = buf->dev_private;
+
+	buf->used = vertex->used;
+	buf_priv->discard = vertex->discard;
+
+	if (!mga_verify_state(dev_priv)) {
+		if (vertex->discard) {
+			if (buf_priv->dispatched == 1)
+				AGE_BUFFER(buf_priv);
+			buf_priv->dispatched = 0;
+			mga_freelist_put(dev, buf);
+		}
+		return -EINVAL;
+	}
+
+	WRAP_TEST_WITH_RETURN(dev_priv);
+
+	mga_dma_dispatch_vertex(dev, buf);
+
+	return 0;
+}
+
+static int mga_dma_indices(struct drm_device *dev, void *data, struct drm_file *file_priv)
+{
+	drm_mga_private_t *dev_priv = dev->dev_private;
+	struct drm_device_dma *dma = dev->dma;
+	struct drm_buf *buf;
+	drm_mga_buf_priv_t *buf_priv;
+	drm_mga_indices_t *indices = data;
+
+	LOCK_TEST_WITH_RETURN(dev, file_priv);
+
+	if (indices->idx < 0 || indices->idx > dma->buf_count)
+		return -EINVAL;
+
+	buf = dma->buflist[indices->idx];
+	buf_priv = buf->dev_private;
+
+	buf_priv->discard = indices->discard;
+
+	if (!mga_verify_state(dev_priv)) {
+		if (indices->discard) {
+			if (buf_priv->dispatched == 1)
+				AGE_BUFFER(buf_priv);
+			buf_priv->dispatched = 0;
+			mga_freelist_put(dev, buf);
+		}
+		return -EINVAL;
+	}
+
+	WRAP_TEST_WITH_RETURN(dev_priv);
+
+	mga_dma_dispatch_indices(dev, buf, indices->start, indices->end);
+
+	return 0;
+}
+
+static int mga_dma_iload(struct drm_device *dev, void *data, struct drm_file *file_priv)
+{
+	struct drm_device_dma *dma = dev->dma;
+	drm_mga_private_t *dev_priv = dev->dev_private;
+	struct drm_buf *buf;
+	drm_mga_buf_priv_t *buf_priv;
+	drm_mga_iload_t *iload = data;
+	DRM_DEBUG("\n");
+
+	LOCK_TEST_WITH_RETURN(dev, file_priv);
+
+#if 0
+	if (mga_do_wait_for_idle(dev_priv) < 0) {
+		if (MGA_DMA_DEBUG)
+			DRM_INFO("-EBUSY\n");
+		return -EBUSY;
+	}
+#endif
+	if (iload->idx < 0 || iload->idx > dma->buf_count)
+		return -EINVAL;
+
+	buf = dma->buflist[iload->idx];
+	buf_priv = buf->dev_private;
+
+	if (mga_verify_iload(dev_priv, iload->dstorg, iload->length)) {
+		mga_freelist_put(dev, buf);
+		return -EINVAL;
+	}
+
+	WRAP_TEST_WITH_RETURN(dev_priv);
+
+	mga_dma_dispatch_iload(dev, buf, iload->dstorg, iload->length);
+
+	/* Make sure we restore the 3D state next time.
+	 */
+	dev_priv->sarea_priv->dirty |= MGA_UPLOAD_CONTEXT;
+
+	return 0;
+}
+
+static int mga_dma_blit(struct drm_device *dev, void *data, struct drm_file *file_priv)
+{
+	drm_mga_private_t *dev_priv = dev->dev_private;
+	drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv;
+	drm_mga_blit_t *blit = data;
+	DRM_DEBUG("\n");
+
+	LOCK_TEST_WITH_RETURN(dev, file_priv);
+
+	if (sarea_priv->nbox > MGA_NR_SAREA_CLIPRECTS)
+		sarea_priv->nbox = MGA_NR_SAREA_CLIPRECTS;
+
+	if (mga_verify_blit(dev_priv, blit->srcorg, blit->dstorg))
+		return -EINVAL;
+
+	WRAP_TEST_WITH_RETURN(dev_priv);
+
+	mga_dma_dispatch_blit(dev, blit);
+
+	/* Make sure we restore the 3D state next time.
+	 */
+	dev_priv->sarea_priv->dirty |= MGA_UPLOAD_CONTEXT;
+
+	return 0;
+}
+
+int mga_getparam(struct drm_device *dev, void *data, struct drm_file *file_priv)
+{
+	drm_mga_private_t *dev_priv = dev->dev_private;
+	drm_mga_getparam_t *param = data;
+	int value;
+
+	if (!dev_priv) {
+		DRM_ERROR("called with no initialization\n");
+		return -EINVAL;
+	}
+
+	DRM_DEBUG("pid=%d\n", task_pid_nr(current));
+
+	switch (param->param) {
+	case MGA_PARAM_IRQ_NR:
+		value = dev->pdev->irq;
+		break;
+	case MGA_PARAM_CARD_TYPE:
+		value = dev_priv->chipset;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (copy_to_user(param->value, &value, sizeof(int))) {
+		DRM_ERROR("copy_to_user\n");
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int mga_set_fence(struct drm_device *dev, void *data, struct drm_file *file_priv)
+{
+	drm_mga_private_t *dev_priv = dev->dev_private;
+	u32 *fence = data;
+	DMA_LOCALS;
+
+	if (!dev_priv) {
+		DRM_ERROR("called with no initialization\n");
+		return -EINVAL;
+	}
+
+	DRM_DEBUG("pid=%d\n", task_pid_nr(current));
+
+	/* I would normal do this assignment in the declaration of fence,
+	 * but dev_priv may be NULL.
+	 */
+
+	*fence = dev_priv->next_fence_to_post;
+	dev_priv->next_fence_to_post++;
+
+	BEGIN_DMA(1);
+	DMA_BLOCK(MGA_DMAPAD, 0x00000000,
+		  MGA_DMAPAD, 0x00000000,
+		  MGA_DMAPAD, 0x00000000, MGA_SOFTRAP, 0x00000000);
+	ADVANCE_DMA();
+
+	return 0;
+}
+
+static int mga_wait_fence(struct drm_device *dev, void *data, struct drm_file *
+file_priv)
+{
+	drm_mga_private_t *dev_priv = dev->dev_private;
+	u32 *fence = data;
+
+	if (!dev_priv) {
+		DRM_ERROR("called with no initialization\n");
+		return -EINVAL;
+	}
+
+	DRM_DEBUG("pid=%d\n", task_pid_nr(current));
+
+	mga_driver_fence_wait(dev, fence);
+	return 0;
+}
+
+const struct drm_ioctl_desc mga_ioctls[] = {
+	DRM_IOCTL_DEF_DRV(MGA_INIT, mga_dma_init, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+	DRM_IOCTL_DEF_DRV(MGA_FLUSH, mga_dma_flush, DRM_AUTH),
+	DRM_IOCTL_DEF_DRV(MGA_RESET, mga_dma_reset, DRM_AUTH),
+	DRM_IOCTL_DEF_DRV(MGA_SWAP, mga_dma_swap, DRM_AUTH),
+	DRM_IOCTL_DEF_DRV(MGA_CLEAR, mga_dma_clear, DRM_AUTH),
+	DRM_IOCTL_DEF_DRV(MGA_VERTEX, mga_dma_vertex, DRM_AUTH),
+	DRM_IOCTL_DEF_DRV(MGA_INDICES, mga_dma_indices, DRM_AUTH),
+	DRM_IOCTL_DEF_DRV(MGA_ILOAD, mga_dma_iload, DRM_AUTH),
+	DRM_IOCTL_DEF_DRV(MGA_BLIT, mga_dma_blit, DRM_AUTH),
+	DRM_IOCTL_DEF_DRV(MGA_GETPARAM, mga_getparam, DRM_AUTH),
+	DRM_IOCTL_DEF_DRV(MGA_SET_FENCE, mga_set_fence, DRM_AUTH),
+	DRM_IOCTL_DEF_DRV(MGA_WAIT_FENCE, mga_wait_fence, DRM_AUTH),
+	DRM_IOCTL_DEF_DRV(MGA_DMA_BOOTSTRAP, mga_dma_bootstrap, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+};
+
+int mga_max_ioctl = ARRAY_SIZE(mga_ioctls);
diff --git a/marvell/linux/drivers/gpu/drm/mga/mga_warp.c b/marvell/linux/drivers/gpu/drm/mga/mga_warp.c
new file mode 100644
index 0000000..b5ef1d2
--- /dev/null
+++ b/marvell/linux/drivers/gpu/drm/mga/mga_warp.c
@@ -0,0 +1,167 @@
+/* mga_warp.c -- Matrox G200/G400 WARP engine management -*- linux-c -*-
+ * Created: Thu Jan 11 21:29:32 2001 by gareth@valinux.com
+ *
+ * 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
+ * VA LINUX SYSTEMS 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:
+ *    Gareth Hughes <gareth@valinux.com>
+ */
+
+#include <linux/firmware.h>
+#include <linux/ihex.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include "mga_drv.h"
+
+#define FIRMWARE_G200 "matrox/g200_warp.fw"
+#define FIRMWARE_G400 "matrox/g400_warp.fw"
+
+MODULE_FIRMWARE(FIRMWARE_G200);
+MODULE_FIRMWARE(FIRMWARE_G400);
+
+#define MGA_WARP_CODE_ALIGN		256	/* in bytes */
+
+#define WARP_UCODE_SIZE(size)		ALIGN(size, MGA_WARP_CODE_ALIGN)
+
+int mga_warp_install_microcode(drm_mga_private_t *dev_priv)
+{
+	unsigned char *vcbase = dev_priv->warp->handle;
+	unsigned long pcbase = dev_priv->warp->offset;
+	const char *firmware_name;
+	struct platform_device *pdev;
+	const struct firmware *fw = NULL;
+	const struct ihex_binrec *rec;
+	unsigned int size;
+	int n_pipes, where;
+	int rc = 0;
+
+	switch (dev_priv->chipset) {
+	case MGA_CARD_TYPE_G400:
+	case MGA_CARD_TYPE_G550:
+		firmware_name = FIRMWARE_G400;
+		n_pipes = MGA_MAX_G400_PIPES;
+		break;
+	case MGA_CARD_TYPE_G200:
+		firmware_name = FIRMWARE_G200;
+		n_pipes = MGA_MAX_G200_PIPES;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	pdev = platform_device_register_simple("mga_warp", 0, NULL, 0);
+	if (IS_ERR(pdev)) {
+		DRM_ERROR("mga: Failed to register microcode\n");
+		return PTR_ERR(pdev);
+	}
+	rc = request_ihex_firmware(&fw, firmware_name, &pdev->dev);
+	platform_device_unregister(pdev);
+	if (rc) {
+		DRM_ERROR("mga: Failed to load microcode \"%s\"\n",
+			  firmware_name);
+		return rc;
+	}
+
+	size = 0;
+	where = 0;
+	for (rec = (const struct ihex_binrec *)fw->data;
+	     rec;
+	     rec = ihex_next_binrec(rec)) {
+		size += WARP_UCODE_SIZE(be16_to_cpu(rec->len));
+		where++;
+	}
+
+	if (where != n_pipes) {
+		DRM_ERROR("mga: Invalid microcode \"%s\"\n", firmware_name);
+		rc = -EINVAL;
+		goto out;
+	}
+	size = PAGE_ALIGN(size);
+	DRM_DEBUG("MGA ucode size = %d bytes\n", size);
+	if (size > dev_priv->warp->size) {
+		DRM_ERROR("microcode too large! (%u > %lu)\n",
+			  size, dev_priv->warp->size);
+		rc = -ENOMEM;
+		goto out;
+	}
+
+	memset(dev_priv->warp_pipe_phys, 0, sizeof(dev_priv->warp_pipe_phys));
+
+	where = 0;
+	for (rec = (const struct ihex_binrec *)fw->data;
+	     rec;
+	     rec = ihex_next_binrec(rec)) {
+		unsigned int src_size, dst_size;
+
+		DRM_DEBUG(" pcbase = 0x%08lx  vcbase = %p\n", pcbase, vcbase);
+		dev_priv->warp_pipe_phys[where] = pcbase;
+		src_size = be16_to_cpu(rec->len);
+		dst_size = WARP_UCODE_SIZE(src_size);
+		memcpy(vcbase, rec->data, src_size);
+		pcbase += dst_size;
+		vcbase += dst_size;
+		where++;
+	}
+
+out:
+	release_firmware(fw);
+	return rc;
+}
+
+#define WMISC_EXPECTED		(MGA_WUCODECACHE_ENABLE | MGA_WMASTER_ENABLE)
+
+int mga_warp_init(drm_mga_private_t *dev_priv)
+{
+	u32 wmisc;
+
+	/* FIXME: Get rid of these damned magic numbers...
+	 */
+	switch (dev_priv->chipset) {
+	case MGA_CARD_TYPE_G400:
+	case MGA_CARD_TYPE_G550:
+		MGA_WRITE(MGA_WIADDR2, MGA_WMODE_SUSPEND);
+		MGA_WRITE(MGA_WGETMSB, 0x00000E00);
+		MGA_WRITE(MGA_WVRTXSZ, 0x00001807);
+		MGA_WRITE(MGA_WACCEPTSEQ, 0x18000000);
+		break;
+	case MGA_CARD_TYPE_G200:
+		MGA_WRITE(MGA_WIADDR, MGA_WMODE_SUSPEND);
+		MGA_WRITE(MGA_WGETMSB, 0x1606);
+		MGA_WRITE(MGA_WVRTXSZ, 7);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	MGA_WRITE(MGA_WMISC, (MGA_WUCODECACHE_ENABLE |
+			      MGA_WMASTER_ENABLE | MGA_WCACHEFLUSH_ENABLE));
+	wmisc = MGA_READ(MGA_WMISC);
+	if (wmisc != WMISC_EXPECTED) {
+		DRM_ERROR("WARP engine config failed! 0x%x != 0x%x\n",
+			  wmisc, WMISC_EXPECTED);
+		return -EINVAL;
+	}
+
+	return 0;
+}