diff --git a/drivers/gpu/drm/mgag200/Makefile b/drivers/gpu/drm/mgag200/Makefile
index 7db592e..a9a0300 100644
--- a/drivers/gpu/drm/mgag200/Makefile
+++ b/drivers/gpu/drm/mgag200/Makefile
@@ -1,5 +1,5 @@
 ccflags-y := -Iinclude/drm
-mgag200-y   := mgag200_main.o mgag200_mode.o \
+mgag200-y   := mgag200_main.o mgag200_mode.o mgag200_cursor.o \
 	mgag200_drv.o mgag200_fb.o mgag200_i2c.o mgag200_ttm.o
 
 obj-$(CONFIG_DRM_MGAG200) += mgag200.o
diff --git a/drivers/gpu/drm/mgag200/mgag200_cursor.c b/drivers/gpu/drm/mgag200/mgag200_cursor.c
new file mode 100644
index 0000000..801731a
--- /dev/null
+++ b/drivers/gpu/drm/mgag200/mgag200_cursor.c
@@ -0,0 +1,275 @@
+/*
+ * Copyright 2013 Matrox Graphics
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License version 2. See the file COPYING in the main
+ * directory of this archive for more details.
+ *
+ * Author: Christopher Harvey <charvey@matrox.com>
+ */
+
+#include <drm/drmP.h>
+#include "mgag200_drv.h"
+
+static bool warn_transparent = true;
+static bool warn_palette = true;
+
+/*
+  Hide the cursor off screen. We can't disable the cursor hardware because it
+  takes too long to re-activate and causes momentary corruption
+*/
+static void mga_hide_cursor(struct mga_device *mdev)
+{
+	WREG8(MGA_CURPOSXL, 0);
+	WREG8(MGA_CURPOSXH, 0);
+	mgag200_bo_unpin(mdev->cursor.pixels_1);
+	mgag200_bo_unpin(mdev->cursor.pixels_2);
+}
+
+int mga_crtc_cursor_set(struct drm_crtc *crtc,
+			struct drm_file *file_priv,
+			uint32_t handle,
+			uint32_t width,
+			uint32_t height)
+{
+	struct drm_device *dev = (struct drm_device *)file_priv->minor->dev;
+	struct mga_device *mdev = (struct mga_device *)dev->dev_private;
+	struct mgag200_bo *pixels_1 = mdev->cursor.pixels_1;
+	struct mgag200_bo *pixels_2 = mdev->cursor.pixels_2;
+	struct mgag200_bo *pixels_current = mdev->cursor.pixels_current;
+	struct mgag200_bo *pixels_prev = mdev->cursor.pixels_prev;
+	struct drm_gem_object *obj;
+	struct mgag200_bo *bo = NULL;
+	int ret = 0;
+	unsigned int i, row, col;
+	uint32_t colour_set[16];
+	uint32_t *next_space = &colour_set[0];
+	uint32_t *palette_iter;
+	uint32_t this_colour;
+	bool found = false;
+	int colour_count = 0;
+	u64 gpu_addr;
+	u8 reg_index;
+	u8 this_row[48];
+
+	if (!pixels_1 || !pixels_2) {
+		WREG8(MGA_CURPOSXL, 0);
+		WREG8(MGA_CURPOSXH, 0);
+		return -ENOTSUPP; /* Didn't allocate space for cursors */
+	}
+
+	if ((width != 64 || height != 64) && handle) {
+		WREG8(MGA_CURPOSXL, 0);
+		WREG8(MGA_CURPOSXH, 0);
+		return -EINVAL;
+	}
+
+	BUG_ON(pixels_1 != pixels_current && pixels_1 != pixels_prev);
+	BUG_ON(pixels_2 != pixels_current && pixels_2 != pixels_prev);
+	BUG_ON(pixels_current == pixels_prev);
+
+	ret = mgag200_bo_reserve(pixels_1, true);
+	if (ret) {
+		WREG8(MGA_CURPOSXL, 0);
+		WREG8(MGA_CURPOSXH, 0);
+		return ret;
+	}
+	ret = mgag200_bo_reserve(pixels_2, true);
+	if (ret) {
+		WREG8(MGA_CURPOSXL, 0);
+		WREG8(MGA_CURPOSXH, 0);
+		mgag200_bo_unreserve(pixels_1);
+		return ret;
+	}
+
+	if (!handle) {
+		mga_hide_cursor(mdev);
+		ret = 0;
+		goto out1;
+	}
+
+	/* Move cursor buffers into VRAM if they aren't already */
+	if (!pixels_1->pin_count) {
+		ret = mgag200_bo_pin(pixels_1, TTM_PL_FLAG_VRAM,
+				     &mdev->cursor.pixels_1_gpu_addr);
+		if (ret)
+			goto out1;
+	}
+	if (!pixels_2->pin_count) {
+		ret = mgag200_bo_pin(pixels_2, TTM_PL_FLAG_VRAM,
+				     &mdev->cursor.pixels_2_gpu_addr);
+		if (ret) {
+			mgag200_bo_unpin(pixels_1);
+			goto out1;
+		}
+	}
+
+	mutex_lock(&dev->struct_mutex);
+	obj = drm_gem_object_lookup(dev, file_priv, handle);
+	if (!obj) {
+		mutex_unlock(&dev->struct_mutex);
+		ret = -ENOENT;
+		goto out1;
+	}
+	drm_gem_object_unreference(obj);
+	mutex_unlock(&dev->struct_mutex);
+
+	bo = gem_to_mga_bo(obj);
+	ret = mgag200_bo_reserve(bo, true);
+	if (ret) {
+		dev_err(&dev->pdev->dev, "failed to reserve user bo\n");
+		goto out1;
+	}
+	if (!bo->kmap.virtual) {
+		ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap);
+		if (ret) {
+			dev_err(&dev->pdev->dev, "failed to kmap user buffer updates\n");
+			goto out2;
+		}
+	}
+
+	memset(&colour_set[0], 0, sizeof(uint32_t)*16);
+	/* width*height*4 = 16384 */
+	for (i = 0; i < 16384; i += 4) {
+		this_colour = ioread32(bo->kmap.virtual + i);
+		/* No transparency */
+		if (this_colour>>24 != 0xff &&
+			this_colour>>24 != 0x0) {
+			if (warn_transparent) {
+				dev_info(&dev->pdev->dev, "Video card doesn't support cursors with partial transparency.\n");
+				dev_info(&dev->pdev->dev, "Not enabling hardware cursor.\n");
+				warn_transparent = false; /* Only tell the user once. */
+			}
+			ret = -EINVAL;
+			goto out3;
+		}
+		/* Don't need to store transparent pixels as colours */
+		if (this_colour>>24 == 0x0)
+			continue;
+		found = false;
+		for (palette_iter = &colour_set[0]; palette_iter != next_space; palette_iter++) {
+			if (*palette_iter == this_colour) {
+				found = true;
+				break;
+			}
+		}
+		if (found)
+			continue;
+		/* We only support 4bit paletted cursors */
+		if (colour_count >= 16) {
+			if (warn_palette) {
+				dev_info(&dev->pdev->dev, "Video card only supports cursors with up to 16 colours.\n");
+				dev_info(&dev->pdev->dev, "Not enabling hardware cursor.\n");
+				warn_palette = false; /* Only tell the user once. */
+			}
+			ret = -EINVAL;
+			goto out3;
+		}
+		*next_space = this_colour;
+		next_space++;
+		colour_count++;
+	}
+
+	/* Program colours from cursor icon into palette */
+	for (i = 0; i < colour_count; i++) {
+		if (i <= 2)
+			reg_index = 0x8 + i*0x4;
+		else
+			reg_index = 0x60 + i*0x3;
+		WREG_DAC(reg_index, colour_set[i] & 0xff);
+		WREG_DAC(reg_index+1, colour_set[i]>>8 & 0xff);
+		WREG_DAC(reg_index+2, colour_set[i]>>16 & 0xff);
+		BUG_ON((colour_set[i]>>24 & 0xff) != 0xff);
+	}
+
+	/* Map up-coming buffer to write colour indices */
+	if (!pixels_prev->kmap.virtual) {
+		ret = ttm_bo_kmap(&pixels_prev->bo, 0,
+				  pixels_prev->bo.num_pages,
+				  &pixels_prev->kmap);
+		if (ret) {
+			dev_err(&dev->pdev->dev, "failed to kmap cursor updates\n");
+			goto out3;
+		}
+	}
+
+	/* now write colour indices into hardware cursor buffer */
+	for (row = 0; row < 64; row++) {
+		memset(&this_row[0], 0, 48);
+		for (col = 0; col < 64; col++) {
+			this_colour = ioread32(bo->kmap.virtual + 4*(col + 64*row));
+			/* write transparent pixels */
+			if (this_colour>>24 == 0x0) {
+				this_row[47 - col/8] |= 0x80>>(col%8);
+				continue;
+			}
+
+			/* write colour index here */
+			for (i = 0; i < colour_count; i++) {
+				if (colour_set[i] == this_colour) {
+					if (col % 2)
+						this_row[col/2] |= i<<4;
+					else
+						this_row[col/2] |= i;
+					break;
+				}
+			}
+		}
+		memcpy_toio(pixels_prev->kmap.virtual + row*48, &this_row[0], 48);
+	}
+
+	/* Program gpu address of cursor buffer */
+	if (pixels_prev == pixels_1)
+		gpu_addr = mdev->cursor.pixels_1_gpu_addr;
+	else
+		gpu_addr = mdev->cursor.pixels_2_gpu_addr;
+	WREG_DAC(MGA1064_CURSOR_BASE_ADR_LOW, (u8)((gpu_addr>>10) & 0xff));
+	WREG_DAC(MGA1064_CURSOR_BASE_ADR_HI, (u8)((gpu_addr>>18) & 0x3f));
+
+	/* Adjust cursor control register to turn on the cursor */
+	WREG_DAC(MGA1064_CURSOR_CTL, 4); /* 16-colour palletized cursor mode */
+
+	/* Now swap internal buffer pointers */
+	if (mdev->cursor.pixels_1 == mdev->cursor.pixels_prev) {
+		mdev->cursor.pixels_prev = mdev->cursor.pixels_2;
+		mdev->cursor.pixels_current = mdev->cursor.pixels_1;
+	} else if (mdev->cursor.pixels_1 == mdev->cursor.pixels_current) {
+		mdev->cursor.pixels_prev = mdev->cursor.pixels_1;
+		mdev->cursor.pixels_current = mdev->cursor.pixels_2;
+	} else {
+		BUG();
+	}
+	ret = 0;
+
+	ttm_bo_kunmap(&pixels_prev->kmap);
+ out3:
+	ttm_bo_kunmap(&bo->kmap);
+ out2:
+	mgag200_bo_unreserve(bo);
+ out1:
+	if (ret)
+		mga_hide_cursor(mdev);
+	mgag200_bo_unreserve(pixels_1);
+	mgag200_bo_unreserve(pixels_2);
+	return ret;
+}
+
+int mga_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
+{
+	struct mga_device *mdev = (struct mga_device *)crtc->dev->dev_private;
+	/* Our origin is at (64,64) */
+	x += 64;
+	y += 64;
+
+	BUG_ON(x <= 0);
+	BUG_ON(y <= 0);
+	BUG_ON(x & ~0xffff);
+	BUG_ON(y & ~0xffff);
+
+	WREG8(MGA_CURPOSXL, x & 0xff);
+	WREG8(MGA_CURPOSXH, (x>>8) & 0xff);
+
+	WREG8(MGA_CURPOSYL, y & 0xff);
+	WREG8(MGA_CURPOSYH, (y>>8) & 0xff);
+	return 0;
+}
diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.h b/drivers/gpu/drm/mgag200/mgag200_drv.h
index bf29b2f..364a05a 100644
--- a/drivers/gpu/drm/mgag200/mgag200_drv.h
+++ b/drivers/gpu/drm/mgag200/mgag200_drv.h
@@ -149,6 +149,21 @@ struct mga_connector {
 	struct mga_i2c_chan *i2c;
 };
 
+struct mga_cursor {
+	/*
+	   We have to have 2 buffers for the cursor to avoid occasional
+	   corruption while switching cursor icons.
+	   If either of these is NULL, then don't do hardware cursors, and
+	   fall back to software.
+	*/
+	struct mgag200_bo *pixels_1;
+	struct mgag200_bo *pixels_2;
+	u64 pixels_1_gpu_addr, pixels_2_gpu_addr;
+	/* The currently displayed icon, this points to one of pixels_1, or pixels_2 */
+	struct mgag200_bo *pixels_current;
+	/* The previously displayed icon */
+	struct mgag200_bo *pixels_prev;
+};
 
 struct mga_mc {
 	resource_size_t			vram_size;
@@ -181,6 +196,7 @@ struct mga_device {
 	struct mga_mode_info		mode_info;
 
 	struct mga_fbdev *mfbdev;
+	struct mga_cursor cursor;
 
 	bool				suspended;
 	int				num_crtc;
@@ -273,4 +289,9 @@ int mgag200_mmap(struct file *filp, struct vm_area_struct *vma);
 int mgag200_bo_pin(struct mgag200_bo *bo, u32 pl_flag, u64 *gpu_addr);
 int mgag200_bo_unpin(struct mgag200_bo *bo);
 int mgag200_bo_push_sysram(struct mgag200_bo *bo);
+			   /* mgag200_cursor.c */
+int mga_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
+						uint32_t handle, uint32_t width, uint32_t height);
+int mga_crtc_cursor_move(struct drm_crtc *crtc, int x, int y);
+
 #endif				/* __MGAG200_DRV_H__ */
diff --git a/drivers/gpu/drm/mgag200/mgag200_main.c b/drivers/gpu/drm/mgag200/mgag200_main.c
index 9905923..5189675 100644
--- a/drivers/gpu/drm/mgag200/mgag200_main.c
+++ b/drivers/gpu/drm/mgag200/mgag200_main.c
@@ -221,8 +221,27 @@ int mgag200_driver_load(struct drm_device *dev, unsigned long flags)
 	dev->mode_config.prefer_shadow = 1;
 
 	r = mgag200_modeset_init(mdev);
-	if (r)
+	if (r) {
 		dev_err(&dev->pdev->dev, "Fatal error during modeset init: %d\n", r);
+		goto out;
+	}
+
+	/* Make small buffers to store a hardware cursor (double buffered icon updates) */
+	mgag200_bo_create(dev, roundup(48*64, PAGE_SIZE), 0, 0,
+					  &mdev->cursor.pixels_1);
+	mgag200_bo_create(dev, roundup(48*64, PAGE_SIZE), 0, 0,
+					  &mdev->cursor.pixels_2);
+	if (!mdev->cursor.pixels_2 || !mdev->cursor.pixels_1)
+		goto cursor_nospace;
+	mdev->cursor.pixels_current = mdev->cursor.pixels_1;
+	mdev->cursor.pixels_prev = mdev->cursor.pixels_2;
+	goto cursor_done;
+ cursor_nospace:
+	mdev->cursor.pixels_1 = NULL;
+	mdev->cursor.pixels_2 = NULL;
+	dev_warn(&dev->pdev->dev, "Could not allocate space for cursors. Not doing hardware cursors.\n");
+ cursor_done:
+
 out:
 	if (r)
 		mgag200_driver_unload(dev);
diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c
index 77b8a45..deed0bd 100644
--- a/drivers/gpu/drm/mgag200/mgag200_mode.c
+++ b/drivers/gpu/drm/mgag200/mgag200_mode.c
@@ -1252,6 +1252,8 @@ static void mga_crtc_destroy(struct drm_crtc *crtc)
 
 /* These provide the minimum set of functions required to handle a CRTC */
 static const struct drm_crtc_funcs mga_crtc_funcs = {
+	.cursor_set = mga_crtc_cursor_set,
+	.cursor_move = mga_crtc_cursor_move,
 	.gamma_set = mga_crtc_gamma_set,
 	.set_config = drm_crtc_helper_set_config,
 	.destroy = mga_crtc_destroy,
diff --git a/drivers/gpu/drm/mgag200/mgag200_reg.h b/drivers/gpu/drm/mgag200/mgag200_reg.h
index fb24d86..3ae442a6 100644
--- a/drivers/gpu/drm/mgag200/mgag200_reg.h
+++ b/drivers/gpu/drm/mgag200/mgag200_reg.h
@@ -235,7 +235,11 @@
 #define MGAREG_CRTCEXT_INDEX	0x1fde
 #define MGAREG_CRTCEXT_DATA	0x1fdf
 
-
+/* Cursor X and Y position */
+#define MGA_CURPOSXL 0x3c0c
+#define MGA_CURPOSXH 0x3c0d
+#define MGA_CURPOSYL 0x3c0e
+#define MGA_CURPOSYH 0x3c0f
 
 /* MGA bits for registers PCI_OPTION_REG */
 #define MGA1064_OPT_SYS_CLK_PCI   		( 0x00 << 0 )
