mgag200: initial g200se driver (v2)
authorDave Airlie <airlied@redhat.com>
Tue, 17 Apr 2012 14:01:25 +0000 (15:01 +0100)
committerDave Airlie <airlied@redhat.com>
Thu, 17 May 2012 09:53:41 +0000 (10:53 +0100)
This is a driver for the G200 server engines chips,
it doesn't driver any of the Matrix G series desktop cards.

It will bind to G200 SE A,B, G200EV, G200WB, G200EH and G200ER cards.

Its based on previous work done my Matthew Garrett but remodelled
to follow the same style and flow as the AST server driver. It also
works along the same lines as the AST server driver wrt memory management.

There is no userspace driver planned, xf86-video-modesetting should be used.
It also appears these GPUs have no ARGB hw cursors.

v2: add missing tagfifo reset + G200 SE memory bw setup pieces.

Signed-off-by: Dave Airlie <airlied@redhat.com>
12 files changed:
drivers/gpu/drm/Kconfig
drivers/gpu/drm/Makefile
drivers/gpu/drm/mgag200/Kconfig [new file with mode: 0644]
drivers/gpu/drm/mgag200/Makefile [new file with mode: 0644]
drivers/gpu/drm/mgag200/mgag200_drv.c [new file with mode: 0644]
drivers/gpu/drm/mgag200/mgag200_drv.h [new file with mode: 0644]
drivers/gpu/drm/mgag200/mgag200_fb.c [new file with mode: 0644]
drivers/gpu/drm/mgag200/mgag200_i2c.c [new file with mode: 0644]
drivers/gpu/drm/mgag200/mgag200_main.c [new file with mode: 0644]
drivers/gpu/drm/mgag200/mgag200_mode.c [new file with mode: 0644]
drivers/gpu/drm/mgag200/mgag200_reg.h [new file with mode: 0644]
drivers/gpu/drm/mgag200/mgag200_ttm.c [new file with mode: 0644]

index 9c6244c..8b23bd9 100644 (file)
@@ -189,3 +189,4 @@ source "drivers/gpu/drm/udl/Kconfig"
 
 source "drivers/gpu/drm/ast/Kconfig"
 
+source "drivers/gpu/drm/mgag200/Kconfig"
index fb3eac8..52c9b56 100644 (file)
@@ -34,6 +34,7 @@ obj-$(CONFIG_DRM_RADEON)+= radeon/
 obj-$(CONFIG_DRM_MGA)  += mga/
 obj-$(CONFIG_DRM_I810) += i810/
 obj-$(CONFIG_DRM_I915)  += i915/
+obj-$(CONFIG_DRM_MGAG200) += mgag200/
 obj-$(CONFIG_DRM_SIS)   += sis/
 obj-$(CONFIG_DRM_SAVAGE)+= savage/
 obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/
diff --git a/drivers/gpu/drm/mgag200/Kconfig b/drivers/gpu/drm/mgag200/Kconfig
new file mode 100644 (file)
index 0000000..ffffafa
--- /dev/null
@@ -0,0 +1,14 @@
+config DRM_MGAG200
+       tristate "Kernel modesetting driver for MGA G200 server engines"
+       depends on DRM && PCI && EXPERIMENTAL
+       select FB_SYS_FILLRECT
+       select FB_SYS_COPYAREA
+       select FB_SYS_IMAGEBLIT
+       select DRM_KMS_HELPER
+       help
+        This is a KMS driver for the MGA G200 server chips, it
+         does not support the original MGA G200 or any of the desktop
+         chips. It requires 0.3.0 of the modesetting userspace driver,
+         and a version of mga driver that will fail on KMS enabled
+         devices.
+
diff --git a/drivers/gpu/drm/mgag200/Makefile b/drivers/gpu/drm/mgag200/Makefile
new file mode 100644 (file)
index 0000000..7db592e
--- /dev/null
@@ -0,0 +1,5 @@
+ccflags-y := -Iinclude/drm
+mgag200-y   := mgag200_main.o mgag200_mode.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_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c
new file mode 100644 (file)
index 0000000..f03a636
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2012 Red Hat
+ *
+ * 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.
+ *
+ * Authors: Matthew Garrett
+ *          Dave Airlie
+ */
+#include <linux/module.h>
+#include <linux/console.h>
+#include "drmP.h"
+#include "drm.h"
+
+#include "mgag200_drv.h"
+
+#include "drm_pciids.h"
+
+/*
+ * This is the generic driver code. This binds the driver to the drm core,
+ * which then performs further device association and calls our graphics init
+ * functions
+ */
+int mgag200_modeset = -1;
+
+MODULE_PARM_DESC(modeset, "Disable/Enable modesetting");
+module_param_named(modeset, mgag200_modeset, int, 0400);
+
+static struct drm_driver driver;
+
+static DEFINE_PCI_DEVICE_TABLE(pciidlist) = {
+       { PCI_VENDOR_ID_MATROX, 0x522, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_SE_A },
+       { PCI_VENDOR_ID_MATROX, 0x524, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_SE_B },
+       { PCI_VENDOR_ID_MATROX, 0x530, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EV },
+       { PCI_VENDOR_ID_MATROX, 0x532, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_WB },
+       { PCI_VENDOR_ID_MATROX, 0x533, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_EH },
+       { PCI_VENDOR_ID_MATROX, 0x534, PCI_ANY_ID, PCI_ANY_ID, 0, 0, G200_ER },
+       {0,}
+};
+
+MODULE_DEVICE_TABLE(pci, pciidlist);
+
+static int __devinit
+mga_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+       return drm_get_pci_dev(pdev, ent, &driver);
+}
+
+static void mga_pci_remove(struct pci_dev *pdev)
+{
+       struct drm_device *dev = pci_get_drvdata(pdev);
+
+       drm_put_dev(dev);
+}
+
+static const struct file_operations mgag200_driver_fops = {
+       .owner = THIS_MODULE,
+       .open = drm_open,
+       .release = drm_release,
+       .unlocked_ioctl = drm_ioctl,
+       .mmap = mgag200_mmap,
+       .poll = drm_poll,
+       .fasync = drm_fasync,
+       .read = drm_read,
+};
+
+static struct drm_driver driver = {
+       .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_USE_MTRR,
+       .load = mgag200_driver_load,
+       .unload = mgag200_driver_unload,
+       .fops = &mgag200_driver_fops,
+       .name = DRIVER_NAME,
+       .desc = DRIVER_DESC,
+       .date = DRIVER_DATE,
+       .major = DRIVER_MAJOR,
+       .minor = DRIVER_MINOR,
+       .patchlevel = DRIVER_PATCHLEVEL,
+
+       .gem_init_object = mgag200_gem_init_object,
+       .gem_free_object = mgag200_gem_free_object,
+       .dumb_create = mgag200_dumb_create,
+       .dumb_map_offset = mgag200_dumb_mmap_offset,
+       .dumb_destroy = mgag200_dumb_destroy,
+};
+
+static struct pci_driver mgag200_pci_driver = {
+       .name = DRIVER_NAME,
+       .id_table = pciidlist,
+       .probe = mga_pci_probe,
+       .remove = mga_pci_remove,
+};
+
+static int __init mgag200_init(void)
+{
+       if (vgacon_text_force() && mgag200_modeset == -1)
+               return -EINVAL;
+
+       if (mgag200_modeset == 0)
+               return -EINVAL;
+       return drm_pci_init(&driver, &mgag200_pci_driver);
+}
+
+static void __exit mgag200_exit(void)
+{
+       drm_pci_exit(&driver, &mgag200_pci_driver);
+}
+
+module_init(mgag200_init);
+module_exit(mgag200_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.h b/drivers/gpu/drm/mgag200/mgag200_drv.h
new file mode 100644 (file)
index 0000000..6f13b35
--- /dev/null
@@ -0,0 +1,276 @@
+/*
+ * Copyright 2010 Matt Turner.
+ * Copyright 2012 Red Hat 
+ *
+ * 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.
+ *
+ * Authors: Matthew Garrett
+ *         Matt Turner
+ *         Dave Airlie
+ */
+#ifndef __MGAG200_DRV_H__
+#define __MGAG200_DRV_H__
+
+#include <video/vga.h>
+
+#include "drm/drm_fb_helper.h"
+#include "ttm/ttm_bo_api.h"
+#include "ttm/ttm_bo_driver.h"
+#include "ttm/ttm_placement.h"
+#include "ttm/ttm_memory.h"
+#include "ttm/ttm_module.h"
+
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+
+#include "mgag200_reg.h"
+
+#define DRIVER_AUTHOR          "Matthew Garrett"
+
+#define DRIVER_NAME            "mgag200"
+#define DRIVER_DESC            "MGA G200 SE"
+#define DRIVER_DATE            "20110418"
+
+#define DRIVER_MAJOR           1
+#define DRIVER_MINOR           0
+#define DRIVER_PATCHLEVEL      0
+
+#define MGAG200FB_CONN_LIMIT 1
+
+#define RREG8(reg) ioread8(((void __iomem *)mdev->rmmio) + (reg))
+#define WREG8(reg, v) iowrite8(v, ((void __iomem *)mdev->rmmio) + (reg))
+#define RREG32(reg) ioread32(((void __iomem *)mdev->rmmio) + (reg))
+#define WREG32(reg, v) iowrite32(v, ((void __iomem *)mdev->rmmio) + (reg))
+
+#define ATTR_INDEX 0x1fc0
+#define ATTR_DATA 0x1fc1
+
+#define WREG_ATTR(reg, v)                                      \
+       do {                                                    \
+               RREG8(0x1fda);                                  \
+               WREG8(ATTR_INDEX, reg);                         \
+               WREG8(ATTR_DATA, v);                            \
+       } while (0)                                             \
+
+#define WREG_SEQ(reg, v)                                       \
+       do {                                                    \
+               WREG8(MGAREG_SEQ_INDEX, reg);                   \
+               WREG8(MGAREG_SEQ_DATA, v);                      \
+       } while (0)                                             \
+
+#define WREG_CRT(reg, v)                                       \
+       do {                                                    \
+               WREG8(MGAREG_CRTC_INDEX, reg);                  \
+               WREG8(MGAREG_CRTC_DATA, v);                     \
+       } while (0)                                             \
+
+
+#define WREG_ECRT(reg, v)                                      \
+       do {                                                    \
+               WREG8(MGAREG_CRTCEXT_INDEX, reg);                               \
+               WREG8(MGAREG_CRTCEXT_DATA, v);                          \
+       } while (0)                                             \
+
+#define GFX_INDEX 0x1fce
+#define GFX_DATA 0x1fcf
+
+#define WREG_GFX(reg, v)                                       \
+       do {                                                    \
+               WREG8(GFX_INDEX, reg);                          \
+               WREG8(GFX_DATA, v);                             \
+       } while (0)                                             \
+
+#define DAC_INDEX 0x3c00
+#define DAC_DATA 0x3c0a
+
+#define WREG_DAC(reg, v)                                       \
+       do {                                                    \
+               WREG8(DAC_INDEX, reg);                          \
+               WREG8(DAC_DATA, v);                             \
+       } while (0)                                             \
+
+#define MGA_MISC_OUT 0x1fc2
+#define MGA_MISC_IN 0x1fcc
+
+#define MGAG200_MAX_FB_HEIGHT 4096
+#define MGAG200_MAX_FB_WIDTH 4096
+
+#define MATROX_DPMS_CLEARED (-1)
+
+#define to_mga_crtc(x) container_of(x, struct mga_crtc, base)
+#define to_mga_encoder(x) container_of(x, struct mga_encoder, base)
+#define to_mga_connector(x) container_of(x, struct mga_connector, base)
+#define to_mga_framebuffer(x) container_of(x, struct mga_framebuffer, base)
+
+struct mga_framebuffer {
+       struct drm_framebuffer base;
+       struct drm_gem_object *obj;
+};
+
+struct mga_fbdev {
+       struct drm_fb_helper helper;
+       struct mga_framebuffer mfb;
+       struct list_head fbdev_list;
+       void *sysram;
+       int size;
+       struct ttm_bo_kmap_obj mapping;
+};
+
+struct mga_crtc {
+       struct drm_crtc base;
+       u8 lut_r[256], lut_g[256], lut_b[256];
+       int last_dpms;
+       bool enabled;
+};
+
+struct mga_mode_info {
+       bool mode_config_initialized;
+       struct mga_crtc *crtc;
+};
+
+struct mga_encoder {
+       struct drm_encoder base;
+       int last_dpms;
+};
+
+
+struct mga_i2c_chan {
+       struct i2c_adapter adapter;
+       struct drm_device *dev;
+       struct i2c_algo_bit_data bit;
+       int data, clock;
+};
+
+struct mga_connector {
+       struct drm_connector base;
+       struct mga_i2c_chan *i2c;
+};
+
+
+struct mga_mc {
+       resource_size_t                 vram_size;
+       resource_size_t                 vram_base;
+       resource_size_t                 vram_window;
+};
+
+enum mga_type {
+       G200_SE_A,
+       G200_SE_B,
+       G200_WB,
+       G200_EV,
+       G200_EH,
+       G200_ER,
+};
+
+#define IS_G200_SE(mdev) (mdev->type == G200_SE_A || mdev->type == G200_SE_B)
+
+struct mga_device {
+       struct drm_device               *dev;
+       unsigned long                   flags;
+
+       resource_size_t                 rmmio_base;
+       resource_size_t                 rmmio_size;
+       void __iomem                    *rmmio;
+
+       drm_local_map_t                 *framebuffer;
+
+       struct mga_mc                   mc;
+       struct mga_mode_info            mode_info;
+
+       struct mga_fbdev *mfbdev;
+
+       bool                            suspended;
+       int                             num_crtc;
+       enum mga_type                   type;
+       int                             has_sdram;
+       struct drm_display_mode         mode;
+
+       int bpp_shifts[4];
+
+       int fb_mtrr;
+
+       struct {
+               struct drm_global_reference mem_global_ref;
+               struct ttm_bo_global_ref bo_global_ref;
+               struct ttm_bo_device bdev;
+               atomic_t validate_sequence;
+       } ttm;
+
+       u32 reg_1e24; /* SE model number */
+};
+
+
+struct mgag200_bo {
+       struct ttm_buffer_object bo;
+       struct ttm_placement placement;
+       struct ttm_bo_kmap_obj kmap;
+       struct drm_gem_object gem;
+       u32 placements[3];
+       int pin_count;
+};
+#define gem_to_mga_bo(gobj) container_of((gobj), struct mgag200_bo, gem)
+
+static inline struct mgag200_bo *
+mgag200_bo(struct ttm_buffer_object *bo)
+{
+       return container_of(bo, struct mgag200_bo, bo);
+}
+                               /* mga_crtc.c */
+void mga_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
+                            u16 blue, int regno);
+void mga_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
+                            u16 *blue, int regno);
+
+                               /* mgag200_mode.c */
+int mgag200_modeset_init(struct mga_device *mdev);
+void mgag200_modeset_fini(struct mga_device *mdev);
+
+                               /* mga_fbdev.c */
+int mgag200_fbdev_init(struct mga_device *mdev);
+void mgag200_fbdev_fini(struct mga_device *mdev);
+
+                               /* mgag200_main.c */
+int mgag200_framebuffer_init(struct drm_device *dev,
+                            struct mga_framebuffer *mfb,
+                            struct drm_mode_fb_cmd2 *mode_cmd,
+                            struct drm_gem_object *obj);
+
+
+int mgag200_driver_load(struct drm_device *dev, unsigned long flags);
+int mgag200_driver_unload(struct drm_device *dev);
+int mgag200_gem_create(struct drm_device *dev,
+                  u32 size, bool iskernel,
+                      struct drm_gem_object **obj);
+int mgag200_gem_init_object(struct drm_gem_object *obj);
+int mgag200_dumb_create(struct drm_file *file,
+                       struct drm_device *dev,
+                       struct drm_mode_create_dumb *args);
+int mgag200_dumb_destroy(struct drm_file *file,
+                        struct drm_device *dev,
+                        uint32_t handle);
+void mgag200_gem_free_object(struct drm_gem_object *obj);
+int
+mgag200_dumb_mmap_offset(struct drm_file *file,
+                        struct drm_device *dev,
+                        uint32_t handle,
+                        uint64_t *offset);
+                               /* mga_i2c.c */
+struct mga_i2c_chan *mgag200_i2c_create(struct drm_device *dev);
+void mgag200_i2c_destroy(struct mga_i2c_chan *i2c);
+
+#define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT)
+void mgag200_ttm_placement(struct mgag200_bo *bo, int domain);
+
+int mgag200_bo_reserve(struct mgag200_bo *bo, bool no_wait);
+void mgag200_bo_unreserve(struct mgag200_bo *bo);
+int mgag200_bo_create(struct drm_device *dev, int size, int align,
+                     uint32_t flags, struct mgag200_bo **pastbo);
+int mgag200_mm_init(struct mga_device *mdev);
+void mgag200_mm_fini(struct mga_device *mdev);
+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);
+#endif                         /* __MGAG200_DRV_H__ */
diff --git a/drivers/gpu/drm/mgag200/mgag200_fb.c b/drivers/gpu/drm/mgag200/mgag200_fb.c
new file mode 100644 (file)
index 0000000..880d336
--- /dev/null
@@ -0,0 +1,294 @@
+/*
+ * Copyright 2010 Matt Turner.
+ * Copyright 2012 Red Hat
+ *
+ * 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.
+ *
+ * Authors: Matthew Garrett
+ *          Matt Turner
+ *          Dave Airlie
+ */
+#include <linux/module.h>
+#include "drmP.h"
+#include "drm.h"
+#include "drm_fb_helper.h"
+
+#include <linux/fb.h>
+
+#include "mgag200_drv.h"
+
+static void mga_dirty_update(struct mga_fbdev *mfbdev,
+                            int x, int y, int width, int height)
+{
+       int i;
+       struct drm_gem_object *obj;
+       struct mgag200_bo *bo;
+       int src_offset, dst_offset;
+       int bpp = (mfbdev->mfb.base.bits_per_pixel + 7)/8;
+       int ret;
+       bool unmap = false;
+
+       obj = mfbdev->mfb.obj;
+       bo = gem_to_mga_bo(obj);
+
+       ret = mgag200_bo_reserve(bo, true);
+       if (ret) {
+               DRM_ERROR("failed to reserve fb bo\n");
+               return;
+       }
+
+       if (!bo->kmap.virtual) {
+               ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap);
+               if (ret) {
+                       DRM_ERROR("failed to kmap fb updates\n");
+                       mgag200_bo_unreserve(bo);
+                       return;
+               }
+               unmap = true;
+       }
+       for (i = y; i < y + height; i++) {
+               /* assume equal stride for now */
+               src_offset = dst_offset = i * mfbdev->mfb.base.pitches[0] + (x * bpp);
+               memcpy_toio(bo->kmap.virtual + src_offset, mfbdev->sysram + src_offset, width * bpp);
+
+       }
+       if (unmap)
+               ttm_bo_kunmap(&bo->kmap);
+
+       mgag200_bo_unreserve(bo);
+}
+
+static void mga_fillrect(struct fb_info *info,
+                        const struct fb_fillrect *rect)
+{
+       struct mga_fbdev *mfbdev = info->par;
+       sys_fillrect(info, rect);
+       mga_dirty_update(mfbdev, rect->dx, rect->dy, rect->width,
+                        rect->height);
+}
+
+static void mga_copyarea(struct fb_info *info,
+                        const struct fb_copyarea *area)
+{
+       struct mga_fbdev *mfbdev = info->par;
+       sys_copyarea(info, area);
+       mga_dirty_update(mfbdev, area->dx, area->dy, area->width,
+                        area->height);
+}
+
+static void mga_imageblit(struct fb_info *info,
+                         const struct fb_image *image)
+{
+       struct mga_fbdev *mfbdev = info->par;
+       sys_imageblit(info, image);
+       mga_dirty_update(mfbdev, image->dx, image->dy, image->width,
+                        image->height);
+}
+
+
+static struct fb_ops mgag200fb_ops = {
+       .owner = THIS_MODULE,
+       .fb_check_var = drm_fb_helper_check_var,
+       .fb_set_par = drm_fb_helper_set_par,
+       .fb_fillrect = mga_fillrect,
+       .fb_copyarea = mga_copyarea,
+       .fb_imageblit = mga_imageblit,
+       .fb_pan_display = drm_fb_helper_pan_display,
+       .fb_blank = drm_fb_helper_blank,
+       .fb_setcmap = drm_fb_helper_setcmap,
+};
+
+static int mgag200fb_create_object(struct mga_fbdev *afbdev,
+                                  struct drm_mode_fb_cmd2 *mode_cmd,
+                                  struct drm_gem_object **gobj_p)
+{
+       struct drm_device *dev = afbdev->helper.dev;
+       u32 bpp, depth;
+       u32 size;
+       struct drm_gem_object *gobj;
+
+       int ret = 0;
+       drm_fb_get_bpp_depth(mode_cmd->pixel_format, &depth, &bpp);
+
+       size = mode_cmd->pitches[0] * mode_cmd->height;
+       ret = mgag200_gem_create(dev, size, true, &gobj);
+       if (ret)
+               return ret;
+
+       *gobj_p = gobj;
+       return ret;
+}
+
+static int mgag200fb_create(struct mga_fbdev *mfbdev,
+                          struct drm_fb_helper_surface_size *sizes)
+{
+       struct drm_device *dev = mfbdev->helper.dev;
+       struct drm_mode_fb_cmd2 mode_cmd;
+       struct mga_device *mdev = dev->dev_private;
+       struct fb_info *info;
+       struct drm_framebuffer *fb;
+       struct drm_gem_object *gobj = NULL;
+       struct device *device = &dev->pdev->dev;
+       struct mgag200_bo *bo;
+       int ret;
+       void *sysram;
+       int size;
+
+       mode_cmd.width = sizes->surface_width;
+       mode_cmd.height = sizes->surface_height;
+       mode_cmd.pitches[0] = mode_cmd.width * ((sizes->surface_bpp + 7) / 8);
+
+       mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
+                                                         sizes->surface_depth);
+       size = mode_cmd.pitches[0] * mode_cmd.height;
+
+       ret = mgag200fb_create_object(mfbdev, &mode_cmd, &gobj);
+       if (ret) {
+               DRM_ERROR("failed to create fbcon backing object %d\n", ret);
+               return ret;
+       }
+       bo = gem_to_mga_bo(gobj);
+
+       sysram = vmalloc(size);
+       if (!sysram)
+               return -ENOMEM;
+
+       info = framebuffer_alloc(0, device);
+       if (info == NULL)
+               return -ENOMEM;
+
+       info->par = mfbdev;
+
+       ret = mgag200_framebuffer_init(dev, &mfbdev->mfb, &mode_cmd, gobj);
+       if (ret)
+               return ret;
+
+       mfbdev->sysram = sysram;
+       mfbdev->size = size;
+
+       fb = &mfbdev->mfb.base;
+
+       /* setup helper */
+       mfbdev->helper.fb = fb;
+       mfbdev->helper.fbdev = info;
+
+       ret = fb_alloc_cmap(&info->cmap, 256, 0);
+       if (ret) {
+               DRM_ERROR("%s: can't allocate color map\n", info->fix.id);
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       strcpy(info->fix.id, "mgadrmfb");
+
+       info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT;
+       info->fbops = &mgag200fb_ops;
+
+       /* setup aperture base/size for vesafb takeover */
+       info->apertures = alloc_apertures(1);
+       if (!info->apertures) {
+               ret = -ENOMEM;
+               goto out;
+       }
+       info->apertures->ranges[0].base = mdev->dev->mode_config.fb_base;
+       info->apertures->ranges[0].size = mdev->mc.vram_size;
+
+       drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
+       drm_fb_helper_fill_var(info, &mfbdev->helper, sizes->fb_width,
+                              sizes->fb_height);
+
+       info->screen_base = sysram;
+       info->screen_size = size;
+       info->pixmap.flags = FB_PIXMAP_SYSTEM;
+
+       DRM_DEBUG_KMS("allocated %dx%d\n",
+                     fb->width, fb->height);
+       return 0;
+out:
+       return ret;
+}
+
+static int mga_fb_find_or_create_single(struct drm_fb_helper *helper,
+                                          struct drm_fb_helper_surface_size
+                                          *sizes)
+{
+       struct mga_fbdev *mfbdev = (struct mga_fbdev *)helper;
+       int new_fb = 0;
+       int ret;
+
+       if (!helper->fb) {
+               ret = mgag200fb_create(mfbdev, sizes);
+               if (ret)
+                       return ret;
+               new_fb = 1;
+       }
+       return new_fb;
+}
+
+static int mga_fbdev_destroy(struct drm_device *dev,
+                               struct mga_fbdev *mfbdev)
+{
+       struct fb_info *info;
+       struct mga_framebuffer *mfb = &mfbdev->mfb;
+
+       if (mfbdev->helper.fbdev) {
+               info = mfbdev->helper.fbdev;
+
+               unregister_framebuffer(info);
+               if (info->cmap.len)
+                       fb_dealloc_cmap(&info->cmap);
+               framebuffer_release(info);
+       }
+
+       if (mfb->obj) {
+               drm_gem_object_unreference_unlocked(mfb->obj);
+               mfb->obj = NULL;
+       }
+       drm_fb_helper_fini(&mfbdev->helper);
+       vfree(mfbdev->sysram);
+       drm_framebuffer_cleanup(&mfb->base);
+
+       return 0;
+}
+
+static struct drm_fb_helper_funcs mga_fb_helper_funcs = {
+       .gamma_set = mga_crtc_fb_gamma_set,
+       .gamma_get = mga_crtc_fb_gamma_get,
+       .fb_probe = mga_fb_find_or_create_single,
+};
+
+int mgag200_fbdev_init(struct mga_device *mdev)
+{
+       struct mga_fbdev *mfbdev;
+       int ret;
+
+       mfbdev = kzalloc(sizeof(struct mga_fbdev), GFP_KERNEL);
+       if (!mfbdev)
+               return -ENOMEM;
+
+       mdev->mfbdev = mfbdev;
+       mfbdev->helper.funcs = &mga_fb_helper_funcs;
+
+       ret = drm_fb_helper_init(mdev->dev, &mfbdev->helper,
+                                mdev->num_crtc, MGAG200FB_CONN_LIMIT);
+       if (ret) {
+               kfree(mfbdev);
+               return ret;
+       }
+       drm_fb_helper_single_add_all_connectors(&mfbdev->helper);
+       drm_fb_helper_initial_config(&mfbdev->helper, 32);
+
+       return 0;
+}
+
+void mgag200_fbdev_fini(struct mga_device *mdev)
+{
+       if (!mdev->mfbdev)
+               return;
+
+       mga_fbdev_destroy(mdev->dev, mdev->mfbdev);
+       kfree(mdev->mfbdev);
+       mdev->mfbdev = NULL;
+}
diff --git a/drivers/gpu/drm/mgag200/mgag200_i2c.c b/drivers/gpu/drm/mgag200/mgag200_i2c.c
new file mode 100644 (file)
index 0000000..dd3568a
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * 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, sub license, 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 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 NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS 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.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ */
+/*
+ * Authors: Dave Airlie <airlied@redhat.com>
+ */
+#include <linux/export.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include "drmP.h"
+#include "drm.h"
+
+#include "mgag200_drv.h"
+
+static int mga_i2c_read_gpio(struct mga_device *mdev)
+{
+       WREG8(DAC_INDEX, MGA1064_GEN_IO_DATA);
+       return RREG8(DAC_DATA);
+}
+
+static void mga_i2c_set_gpio(struct mga_device *mdev, int mask, int val)
+{
+       int tmp;
+
+       WREG8(DAC_INDEX, MGA1064_GEN_IO_CTL);
+       tmp = (RREG8(DAC_DATA) & mask) | val;
+       WREG_DAC(MGA1064_GEN_IO_CTL, tmp);
+       WREG_DAC(MGA1064_GEN_IO_DATA, 0);
+}
+
+static inline void mga_i2c_set(struct mga_device *mdev, int mask, int state)
+{
+       if (state)
+               state = 0;
+       else
+               state = mask;
+       mga_i2c_set_gpio(mdev, ~mask, state);
+}
+
+static void mga_gpio_setsda(void *data, int state)
+{
+       struct mga_i2c_chan *i2c = data;
+       struct mga_device *mdev = i2c->dev->dev_private;
+       mga_i2c_set(mdev, i2c->data, state);
+}
+
+static void mga_gpio_setscl(void *data, int state)
+{
+       struct mga_i2c_chan *i2c = data;
+       struct mga_device *mdev = i2c->dev->dev_private;
+       mga_i2c_set(mdev, i2c->clock, state);
+}
+
+static int mga_gpio_getsda(void *data)
+{
+       struct mga_i2c_chan *i2c = data;
+       struct mga_device *mdev = i2c->dev->dev_private;
+       return (mga_i2c_read_gpio(mdev) & i2c->data) ? 1 : 0;
+}
+
+static int mga_gpio_getscl(void *data)
+{
+       struct mga_i2c_chan *i2c = data;
+       struct mga_device *mdev = i2c->dev->dev_private;
+       return (mga_i2c_read_gpio(mdev) & i2c->clock) ? 1 : 0;
+}
+
+struct mga_i2c_chan *mgag200_i2c_create(struct drm_device *dev)
+{
+       struct mga_device *mdev = dev->dev_private;
+       struct mga_i2c_chan *i2c;
+       int ret;
+       int data, clock;
+
+       WREG_DAC(MGA1064_GEN_IO_DATA, 0xff);
+       WREG_DAC(MGA1064_GEN_IO_CTL, 0);
+
+       switch (mdev->type) {
+       case G200_SE_A:
+       case G200_SE_B:
+       case G200_EV:
+       case G200_WB:
+               data = 1;
+               clock = 2;
+               break;
+       case G200_EH:
+       case G200_ER:
+               data = 2;
+               clock = 1;
+               break;
+       default:
+               data = 2;
+               clock = 8;
+               break;
+       }
+
+       i2c = kzalloc(sizeof(struct mga_i2c_chan), GFP_KERNEL);
+       if (!i2c)
+               return NULL;
+
+       i2c->data = data;
+       i2c->clock = clock;
+       i2c->adapter.owner = THIS_MODULE;
+       i2c->adapter.class = I2C_CLASS_DDC;
+       i2c->adapter.dev.parent = &dev->pdev->dev;
+       i2c->dev = dev;
+       i2c_set_adapdata(&i2c->adapter, i2c);
+       snprintf(i2c->adapter.name, sizeof(i2c->adapter.name), "mga i2c");
+
+       i2c->adapter.algo_data = &i2c->bit;
+
+       i2c->bit.udelay = 10;
+       i2c->bit.timeout = 2;
+       i2c->bit.data = i2c;
+       i2c->bit.setsda         = mga_gpio_setsda;
+       i2c->bit.setscl         = mga_gpio_setscl;
+       i2c->bit.getsda         = mga_gpio_getsda;
+       i2c->bit.getscl         = mga_gpio_getscl;
+
+       ret = i2c_bit_add_bus(&i2c->adapter);
+       if (ret) {
+               kfree(i2c);
+               i2c = NULL;
+       }
+       return i2c;
+}
+
+void mgag200_i2c_destroy(struct mga_i2c_chan *i2c)
+{
+       if (!i2c)
+               return;
+       i2c_del_adapter(&i2c->adapter);
+       kfree(i2c);
+}
+
diff --git a/drivers/gpu/drm/mgag200/mgag200_main.c b/drivers/gpu/drm/mgag200/mgag200_main.c
new file mode 100644 (file)
index 0000000..636a81c
--- /dev/null
@@ -0,0 +1,388 @@
+/*
+ * Copyright 2010 Matt Turner.
+ * Copyright 2012 Red Hat
+ *
+ * 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.
+ *
+ * Authors: Matthew Garrett
+ *          Matt Turner
+ *          Dave Airlie
+ */
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc_helper.h"
+#include "mgag200_drv.h"
+
+static void mga_user_framebuffer_destroy(struct drm_framebuffer *fb)
+{
+       struct mga_framebuffer *mga_fb = to_mga_framebuffer(fb);
+       if (mga_fb->obj)
+               drm_gem_object_unreference_unlocked(mga_fb->obj);
+       drm_framebuffer_cleanup(fb);
+       kfree(fb);
+}
+
+static int mga_user_framebuffer_create_handle(struct drm_framebuffer *fb,
+                                                struct drm_file *file_priv,
+                                                unsigned int *handle)
+{
+       return 0;
+}
+
+static const struct drm_framebuffer_funcs mga_fb_funcs = {
+       .destroy = mga_user_framebuffer_destroy,
+       .create_handle = mga_user_framebuffer_create_handle,
+};
+
+int mgag200_framebuffer_init(struct drm_device *dev,
+                            struct mga_framebuffer *gfb,
+                            struct drm_mode_fb_cmd2 *mode_cmd,
+                            struct drm_gem_object *obj)
+{
+       int ret = drm_framebuffer_init(dev, &gfb->base, &mga_fb_funcs);
+       if (ret) {
+               DRM_ERROR("drm_framebuffer_init failed: %d\n", ret);
+               return ret;
+       }
+       drm_helper_mode_fill_fb_struct(&gfb->base, mode_cmd);
+       gfb->obj = obj;
+       return 0;
+}
+
+static struct drm_framebuffer *
+mgag200_user_framebuffer_create(struct drm_device *dev,
+                               struct drm_file *filp,
+                               struct drm_mode_fb_cmd2 *mode_cmd)
+{
+       struct drm_gem_object *obj;
+       struct mga_framebuffer *mga_fb;
+       int ret;
+
+       obj = drm_gem_object_lookup(dev, filp, mode_cmd->handles[0]);
+       if (obj == NULL)
+               return ERR_PTR(-ENOENT);
+
+       mga_fb = kzalloc(sizeof(*mga_fb), GFP_KERNEL);
+       if (!mga_fb) {
+               drm_gem_object_unreference_unlocked(obj);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       ret = mgag200_framebuffer_init(dev, mga_fb, mode_cmd, obj);
+       if (ret) {
+               drm_gem_object_unreference_unlocked(obj);
+               kfree(mga_fb);
+               return ERR_PTR(ret);
+       }
+       return &mga_fb->base;
+}
+
+static const struct drm_mode_config_funcs mga_mode_funcs = {
+       .fb_create = mgag200_user_framebuffer_create,
+};
+
+/* Unmap the framebuffer from the core and release the memory */
+static void mga_vram_fini(struct mga_device *mdev)
+{
+       pci_iounmap(mdev->dev->pdev, mdev->rmmio);
+       mdev->rmmio = NULL;
+       if (mdev->mc.vram_base)
+               release_mem_region(mdev->mc.vram_base, mdev->mc.vram_window);
+}
+
+static int mga_probe_vram(struct mga_device *mdev, void __iomem *mem)
+{
+       int offset;
+       int orig;
+       int test1, test2;
+       int orig1, orig2;
+
+       /* Probe */
+       orig = ioread16(mem);
+       iowrite16(0, mem);
+
+       for (offset = 0x100000; offset < mdev->mc.vram_window; offset += 0x4000) {
+               orig1 = ioread8(mem + offset);
+               orig2 = ioread8(mem + offset + 0x100);
+
+               iowrite16(0xaa55, mem + offset);
+               iowrite16(0xaa55, mem + offset + 0x100);
+
+               test1 = ioread16(mem + offset);
+               test2 = ioread16(mem);
+
+               iowrite16(orig1, mem + offset);
+               iowrite16(orig2, mem + offset + 0x100);
+
+               if (test1 != 0xaa55) {
+                       break;
+               }
+
+               if (test2) {
+                       break;
+               }
+       }
+
+       iowrite16(orig, mem);
+       return offset - 65536;
+}
+
+/* Map the framebuffer from the card and configure the core */
+static int mga_vram_init(struct mga_device *mdev)
+{
+       void __iomem *mem;
+       struct apertures_struct *aper = alloc_apertures(1);
+
+       /* BAR 0 is VRAM */
+       mdev->mc.vram_base = pci_resource_start(mdev->dev->pdev, 0);
+       mdev->mc.vram_window = pci_resource_len(mdev->dev->pdev, 0);
+
+       aper->ranges[0].base = mdev->mc.vram_base;
+       aper->ranges[0].size = mdev->mc.vram_window;
+       aper->count = 1;
+
+       remove_conflicting_framebuffers(aper, "mgafb", true);
+
+       if (!request_mem_region(mdev->mc.vram_base, mdev->mc.vram_window,
+                               "mgadrmfb_vram")) {
+               DRM_ERROR("can't reserve VRAM\n");
+               return -ENXIO;
+       }
+
+       mem = pci_iomap(mdev->dev->pdev, 0, 0);
+
+       mdev->mc.vram_size = mga_probe_vram(mdev, mem);
+
+       pci_iounmap(mdev->dev->pdev, mem);
+
+       return 0;
+}
+
+static int mgag200_device_init(struct drm_device *dev,
+                              uint32_t flags)
+{
+       struct mga_device *mdev = dev->dev_private;
+       int ret, option;
+
+       mdev->type = flags;
+
+       /* Hardcode the number of CRTCs to 1 */
+       mdev->num_crtc = 1;
+
+       pci_read_config_dword(dev->pdev, PCI_MGA_OPTION, &option);
+       mdev->has_sdram = !(option & (1 << 14));
+
+       /* BAR 0 is the framebuffer, BAR 1 contains registers */
+       mdev->rmmio_base = pci_resource_start(mdev->dev->pdev, 1);
+       mdev->rmmio_size = pci_resource_len(mdev->dev->pdev, 1);
+
+       if (!request_mem_region(mdev->rmmio_base, mdev->rmmio_size,
+                               "mgadrmfb_mmio")) {
+               DRM_ERROR("can't reserve mmio registers\n");
+               return -ENOMEM;
+       }
+
+       mdev->rmmio = pci_iomap(dev->pdev, 1, 0);
+       if (mdev->rmmio == NULL)
+               return -ENOMEM;
+
+       /* stash G200 SE model number for later use */
+       if (IS_G200_SE(mdev))
+               mdev->reg_1e24 = RREG32(0x1e24);
+
+       ret = mga_vram_init(mdev);
+       if (ret) {
+               release_mem_region(mdev->rmmio_base, mdev->rmmio_size);
+               return ret;
+       }
+
+       mdev->bpp_shifts[0] = 0;
+       mdev->bpp_shifts[1] = 1;
+       mdev->bpp_shifts[2] = 0;
+       mdev->bpp_shifts[3] = 2;
+       return 0;
+}
+
+void mgag200_device_fini(struct mga_device *mdev)
+{
+       release_mem_region(mdev->rmmio_base, mdev->rmmio_size);
+       mga_vram_fini(mdev);
+}
+
+/*
+ * Functions here will be called by the core once it's bound the driver to
+ * a PCI device
+ */
+
+
+int mgag200_driver_load(struct drm_device *dev, unsigned long flags)
+{
+       struct mga_device *mdev;
+       int r;
+
+       mdev = kzalloc(sizeof(struct mga_device), GFP_KERNEL);
+       if (mdev == NULL)
+               return -ENOMEM;
+       dev->dev_private = (void *)mdev;
+       mdev->dev = dev;
+
+       r = mgag200_device_init(dev, flags);
+       if (r) {
+               dev_err(&dev->pdev->dev, "Fatal error during GPU init: %d\n", r);
+               goto out;
+       }
+       r = mgag200_mm_init(mdev);
+       if (r)
+               goto out;
+
+       drm_mode_config_init(dev);
+       dev->mode_config.funcs = (void *)&mga_mode_funcs;
+       dev->mode_config.min_width = 0;
+       dev->mode_config.min_height = 0;
+       dev->mode_config.preferred_depth = 24;
+       dev->mode_config.prefer_shadow = 1;
+
+       r = mgag200_modeset_init(mdev);
+       if (r)
+               dev_err(&dev->pdev->dev, "Fatal error during modeset init: %d\n", r);
+out:
+       if (r)
+               mgag200_driver_unload(dev);
+       return r;
+}
+
+int mgag200_driver_unload(struct drm_device *dev)
+{
+       struct mga_device *mdev = dev->dev_private;
+
+       if (mdev == NULL)
+               return 0;
+       mgag200_modeset_fini(mdev);
+       mgag200_fbdev_fini(mdev);
+       drm_mode_config_cleanup(dev);
+       mgag200_mm_fini(mdev);
+       mgag200_device_fini(mdev);
+       kfree(mdev);
+       dev->dev_private = NULL;
+       return 0;
+}
+
+int mgag200_gem_create(struct drm_device *dev,
+                  u32 size, bool iskernel,
+                  struct drm_gem_object **obj)
+{
+       struct mgag200_bo *astbo;
+       int ret;
+
+       *obj = NULL;
+
+       size = roundup(size, PAGE_SIZE);
+       if (size == 0)
+               return -EINVAL;
+
+       ret = mgag200_bo_create(dev, size, 0, 0, &astbo);
+       if (ret) {
+               if (ret != -ERESTARTSYS)
+                       DRM_ERROR("failed to allocate GEM object\n");
+               return ret;
+       }
+       *obj = &astbo->gem;
+       return 0;
+}
+
+int mgag200_dumb_create(struct drm_file *file,
+                   struct drm_device *dev,
+                   struct drm_mode_create_dumb *args)
+{
+       int ret;
+       struct drm_gem_object *gobj;
+       u32 handle;
+
+       args->pitch = args->width * ((args->bpp + 7) / 8);
+       args->size = args->pitch * args->height;
+
+       ret = mgag200_gem_create(dev, args->size, false,
+                            &gobj);
+       if (ret)
+               return ret;
+
+       ret = drm_gem_handle_create(file, gobj, &handle);
+       drm_gem_object_unreference_unlocked(gobj);
+       if (ret)
+               return ret;
+
+       args->handle = handle;
+       return 0;
+}
+
+int mgag200_dumb_destroy(struct drm_file *file,
+                    struct drm_device *dev,
+                    uint32_t handle)
+{
+       return drm_gem_handle_delete(file, handle);
+}
+
+int mgag200_gem_init_object(struct drm_gem_object *obj)
+{
+       BUG();
+       return 0;
+}
+
+void mgag200_bo_unref(struct mgag200_bo **bo)
+{
+       struct ttm_buffer_object *tbo;
+
+       if ((*bo) == NULL)
+               return;
+
+       tbo = &((*bo)->bo);
+       ttm_bo_unref(&tbo);
+       if (tbo == NULL)
+               *bo = NULL;
+
+}
+
+void mgag200_gem_free_object(struct drm_gem_object *obj)
+{
+       struct mgag200_bo *mgag200_bo = gem_to_mga_bo(obj);
+
+       if (!mgag200_bo)
+               return;
+       mgag200_bo_unref(&mgag200_bo);
+}
+
+
+static inline u64 mgag200_bo_mmap_offset(struct mgag200_bo *bo)
+{
+       return bo->bo.addr_space_offset;
+}
+
+int
+mgag200_dumb_mmap_offset(struct drm_file *file,
+                    struct drm_device *dev,
+                    uint32_t handle,
+                    uint64_t *offset)
+{
+       struct drm_gem_object *obj;
+       int ret;
+       struct mgag200_bo *bo;
+
+       mutex_lock(&dev->struct_mutex);
+       obj = drm_gem_object_lookup(dev, file, handle);
+       if (obj == NULL) {
+               ret = -ENOENT;
+               goto out_unlock;
+       }
+
+       bo = gem_to_mga_bo(obj);
+       *offset = mgag200_bo_mmap_offset(bo);
+
+       drm_gem_object_unreference(obj);
+       ret = 0;
+out_unlock:
+       mutex_unlock(&dev->struct_mutex);
+       return ret;
+
+}
diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c
new file mode 100644 (file)
index 0000000..d303061
--- /dev/null
@@ -0,0 +1,1533 @@
+/*
+ * Copyright 2010 Matt Turner.
+ * Copyright 2012 Red Hat
+ *
+ * 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.
+ *
+ * Authors: Matthew Garrett
+ *         Matt Turner
+ *         Dave Airlie
+ */
+
+#include <linux/delay.h>
+
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc_helper.h"
+
+#include "mgag200_drv.h"
+
+#define MGAG200_LUT_SIZE 256
+
+/*
+ * This file contains setup code for the CRTC.
+ */
+
+static void mga_crtc_load_lut(struct drm_crtc *crtc)
+{
+       struct mga_crtc *mga_crtc = to_mga_crtc(crtc);
+       struct drm_device *dev = crtc->dev;
+       struct mga_device *mdev = dev->dev_private;
+       int i;
+
+       if (!crtc->enabled)
+               return;
+
+       WREG8(DAC_INDEX + MGA1064_INDEX, 0);
+
+       for (i = 0; i < MGAG200_LUT_SIZE; i++) {
+               /* VGA registers */
+               WREG8(DAC_INDEX + MGA1064_COL_PAL, mga_crtc->lut_r[i]);
+               WREG8(DAC_INDEX + MGA1064_COL_PAL, mga_crtc->lut_g[i]);
+               WREG8(DAC_INDEX + MGA1064_COL_PAL, mga_crtc->lut_b[i]);
+       }
+}
+
+static inline void mga_wait_vsync(struct mga_device *mdev)
+{
+       unsigned int count = 0;
+       unsigned int status = 0;
+
+       do {
+               status = RREG32(MGAREG_Status);
+               count++;
+       } while ((status & 0x08) && (count < 250000));
+       count = 0;
+       status = 0;
+       do {
+               status = RREG32(MGAREG_Status);
+               count++;
+       } while (!(status & 0x08) && (count < 250000));
+}
+
+static inline void mga_wait_busy(struct mga_device *mdev)
+{
+       unsigned int count = 0;
+       unsigned int status = 0;
+       do {
+               status = RREG8(MGAREG_Status + 2);
+               count++;
+       } while ((status & 0x01) && (count < 500000));
+}
+
+/*
+ * The core passes the desired mode to the CRTC code to see whether any
+ * CRTC-specific modifications need to be made to it. We're in a position
+ * to just pass that straight through, so this does nothing
+ */
+static bool mga_crtc_mode_fixup(struct drm_crtc *crtc,
+                                  struct drm_display_mode *mode,
+                                  struct drm_display_mode *adjusted_mode)
+{
+       return true;
+}
+
+static int mga_g200se_set_plls(struct mga_device *mdev, long clock)
+{
+       unsigned int vcomax, vcomin, pllreffreq;
+       unsigned int delta, tmpdelta, permitteddelta;
+       unsigned int testp, testm, testn;
+       unsigned int p, m, n;
+       unsigned int computed;
+
+       m = n = p = 0;
+       vcomax = 320000;
+       vcomin = 160000;
+       pllreffreq = 25000;
+
+       delta = 0xffffffff;
+       permitteddelta = clock * 5 / 1000;
+
+       for (testp = 8; testp > 0; testp /= 2) {
+               if (clock * testp > vcomax)
+                       continue;
+               if (clock * testp < vcomin)
+                       continue;
+
+               for (testn = 17; testn < 256; testn++) {
+                       for (testm = 1; testm < 32; testm++) {
+                               computed = (pllreffreq * testn) /
+                                       (testm * testp);
+                               if (computed > clock)
+                                       tmpdelta = computed - clock;
+                               else
+                                       tmpdelta = clock - computed;
+                               if (tmpdelta < delta) {
+                                       delta = tmpdelta;
+                                       m = testm - 1;
+                                       n = testn - 1;
+                                       p = testp - 1;
+                               }
+                       }
+               }
+       }
+
+       if (delta > permitteddelta) {
+               printk(KERN_WARNING "PLL delta too large\n");
+               return 1;
+       }
+
+       WREG_DAC(MGA1064_PIX_PLLC_M, m);
+       WREG_DAC(MGA1064_PIX_PLLC_N, n);
+       WREG_DAC(MGA1064_PIX_PLLC_P, p);
+       return 0;
+}
+
+static int mga_g200wb_set_plls(struct mga_device *mdev, long clock)
+{
+       unsigned int vcomax, vcomin, pllreffreq;
+       unsigned int delta, tmpdelta, permitteddelta;
+       unsigned int testp, testm, testn;
+       unsigned int p, m, n;
+       unsigned int computed;
+       int i, j, tmpcount, vcount;
+       bool pll_locked = false;
+       u8 tmp;
+
+       m = n = p = 0;
+       vcomax = 550000;
+       vcomin = 150000;
+       pllreffreq = 48000;
+
+       delta = 0xffffffff;
+       permitteddelta = clock * 5 / 1000;
+
+       for (testp = 1; testp < 9; testp++) {
+               if (clock * testp > vcomax)
+                       continue;
+               if (clock * testp < vcomin)
+                       continue;
+
+               for (testm = 1; testm < 17; testm++) {
+                       for (testn = 1; testn < 151; testn++) {
+                               computed = (pllreffreq * testn) /
+                                       (testm * testp);
+                               if (computed > clock)
+                                       tmpdelta = computed - clock;
+                               else
+                                       tmpdelta = clock - computed;
+                               if (tmpdelta < delta) {
+                                       delta = tmpdelta;
+                                       n = testn - 1;
+                                       m = (testm - 1) | ((n >> 1) & 0x80);
+                                       p = testp - 1;
+                               }
+                       }
+               }
+       }
+
+       for (i = 0; i <= 32 && pll_locked == false; i++) {
+               if (i > 0) {
+                       WREG8(MGAREG_CRTC_INDEX, 0x1e);
+                       tmp = RREG8(MGAREG_CRTC_DATA);
+                       if (tmp < 0xff)
+                               WREG8(MGAREG_CRTC_DATA, tmp+1);
+               }
+
+               /* set pixclkdis to 1 */
+               WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
+               tmp = RREG8(DAC_DATA);
+               tmp |= MGA1064_PIX_CLK_CTL_CLK_DIS;
+               WREG_DAC(MGA1064_PIX_CLK_CTL_CLK_DIS, tmp);
+
+               WREG8(DAC_INDEX, MGA1064_REMHEADCTL);
+               tmp = RREG8(DAC_DATA);
+               tmp |= MGA1064_REMHEADCTL_CLKDIS;
+               WREG_DAC(MGA1064_REMHEADCTL, tmp);
+
+               /* select PLL Set C */
+               tmp = RREG8(MGAREG_MEM_MISC_READ);
+               tmp |= 0x3 << 2;
+               WREG8(MGAREG_MEM_MISC_WRITE, tmp);
+
+               WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
+               tmp = RREG8(DAC_DATA);
+               tmp |= MGA1064_PIX_CLK_CTL_CLK_POW_DOWN | 0x80;
+               WREG_DAC(MGA1064_PIX_CLK_CTL, tmp);
+
+               udelay(500);
+
+               /* reset the PLL */
+               WREG8(DAC_INDEX, MGA1064_VREF_CTL);
+               tmp = RREG8(DAC_DATA);
+               tmp &= ~0x04;
+               WREG_DAC(MGA1064_VREF_CTL, tmp);
+
+               udelay(50);
+
+               /* program pixel pll register */
+               WREG_DAC(MGA1064_WB_PIX_PLLC_N, n);
+               WREG_DAC(MGA1064_WB_PIX_PLLC_M, m);
+               WREG_DAC(MGA1064_WB_PIX_PLLC_P, p);
+
+               udelay(50);
+
+               /* turn pll on */
+               WREG8(DAC_INDEX, MGA1064_VREF_CTL);
+               tmp = RREG8(DAC_DATA);
+               tmp |= 0x04;
+               WREG_DAC(MGA1064_VREF_CTL, tmp);
+
+               udelay(500);
+
+               /* select the pixel pll */
+               WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
+               tmp = RREG8(DAC_DATA);
+               tmp &= ~MGA1064_PIX_CLK_CTL_SEL_MSK;
+               tmp |= MGA1064_PIX_CLK_CTL_SEL_PLL;
+               WREG_DAC(MGA1064_PIX_CLK_CTL, tmp);
+
+               WREG8(DAC_INDEX, MGA1064_REMHEADCTL);
+               tmp = RREG8(DAC_DATA);
+               tmp &= ~MGA1064_REMHEADCTL_CLKSL_MSK;
+               tmp |= MGA1064_REMHEADCTL_CLKSL_PLL;
+               WREG_DAC(MGA1064_REMHEADCTL, tmp);
+
+               /* reset dotclock rate bit */
+               WREG8(MGAREG_SEQ_INDEX, 1);
+               tmp = RREG8(MGAREG_SEQ_DATA);
+               tmp &= ~0x8;
+               WREG8(MGAREG_SEQ_DATA, tmp);
+
+               WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
+               tmp = RREG8(DAC_DATA);
+               tmp &= ~MGA1064_PIX_CLK_CTL_CLK_DIS;
+               WREG_DAC(MGA1064_PIX_CLK_CTL, tmp);
+
+               vcount = RREG8(MGAREG_VCOUNT);
+
+               for (j = 0; j < 30 && pll_locked == false; j++) {
+                       tmpcount = RREG8(MGAREG_VCOUNT);
+                       if (tmpcount < vcount)
+                               vcount = 0;
+                       if ((tmpcount - vcount) > 2)
+                               pll_locked = true;
+                       else
+                               udelay(5);
+               }
+       }
+       WREG8(DAC_INDEX, MGA1064_REMHEADCTL);
+       tmp = RREG8(DAC_DATA);
+       tmp &= ~MGA1064_REMHEADCTL_CLKDIS;
+       WREG_DAC(MGA1064_REMHEADCTL, tmp);
+       return 0;
+}
+
+static int mga_g200ev_set_plls(struct mga_device *mdev, long clock)
+{
+       unsigned int vcomax, vcomin, pllreffreq;
+       unsigned int delta, tmpdelta, permitteddelta;
+       unsigned int testp, testm, testn;
+       unsigned int p, m, n;
+       unsigned int computed;
+       u8 tmp;
+
+       m = n = p = 0;
+       vcomax = 550000;
+       vcomin = 150000;
+       pllreffreq = 50000;
+
+       delta = 0xffffffff;
+       permitteddelta = clock * 5 / 1000;
+
+       for (testp = 16; testp > 0; testp--) {
+               if (clock * testp > vcomax)
+                       continue;
+               if (clock * testp < vcomin)
+                       continue;
+
+               for (testn = 1; testn < 257; testn++) {
+                       for (testm = 1; testm < 17; testm++) {
+                               computed = (pllreffreq * testn) /
+                                       (testm * testp);
+                               if (computed > clock)
+                                       tmpdelta = computed - clock;
+                               else
+                                       tmpdelta = clock - computed;
+                               if (tmpdelta < delta) {
+                                       delta = tmpdelta;
+                                       n = testn - 1;
+                                       m = testm - 1;
+                                       p = testp - 1;
+                               }
+                       }
+               }
+       }
+
+       WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
+       tmp = RREG8(DAC_DATA);
+       tmp |= MGA1064_PIX_CLK_CTL_CLK_DIS;
+       WREG_DAC(MGA1064_PIX_CLK_CTL_CLK_DIS, tmp);
+
+       tmp = RREG8(MGAREG_MEM_MISC_READ);
+       tmp |= 0x3 << 2;
+       WREG8(MGAREG_MEM_MISC_WRITE, tmp);
+
+       WREG8(DAC_INDEX, MGA1064_PIX_PLL_STAT);
+       tmp = RREG8(DAC_DATA);
+       WREG_DAC(MGA1064_PIX_PLL_STAT, tmp & ~0x40);
+
+       WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
+       tmp = RREG8(DAC_DATA);
+       tmp |= MGA1064_PIX_CLK_CTL_CLK_POW_DOWN;
+       WREG_DAC(MGA1064_PIX_CLK_CTL, tmp);
+
+       WREG_DAC(MGA1064_EV_PIX_PLLC_M, m);
+       WREG_DAC(MGA1064_EV_PIX_PLLC_N, n);
+       WREG_DAC(MGA1064_EV_PIX_PLLC_P, p);
+
+       udelay(50);
+
+       WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
+       tmp = RREG8(DAC_DATA);
+       tmp &= ~MGA1064_PIX_CLK_CTL_CLK_POW_DOWN;
+       WREG_DAC(MGA1064_PIX_CLK_CTL, tmp);
+
+       udelay(500);
+
+       WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
+       tmp = RREG8(DAC_DATA);
+       tmp &= ~MGA1064_PIX_CLK_CTL_SEL_MSK;
+       tmp |= MGA1064_PIX_CLK_CTL_SEL_PLL;
+       WREG_DAC(MGA1064_PIX_CLK_CTL, tmp);
+
+       WREG8(DAC_INDEX, MGA1064_PIX_PLL_STAT);
+       tmp = RREG8(DAC_DATA);
+       WREG_DAC(MGA1064_PIX_PLL_STAT, tmp | 0x40);
+
+       tmp = RREG8(MGAREG_MEM_MISC_READ);
+       tmp |= (0x3 << 2);
+       WREG8(MGAREG_MEM_MISC_WRITE, tmp);
+
+       WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
+       tmp = RREG8(DAC_DATA);
+       tmp &= ~MGA1064_PIX_CLK_CTL_CLK_DIS;
+       WREG_DAC(MGA1064_PIX_CLK_CTL, tmp);
+
+       return 0;
+}
+
+static int mga_g200eh_set_plls(struct mga_device *mdev, long clock)
+{
+       unsigned int vcomax, vcomin, pllreffreq;
+       unsigned int delta, tmpdelta, permitteddelta;
+       unsigned int testp, testm, testn;
+       unsigned int p, m, n;
+       unsigned int computed;
+       int i, j, tmpcount, vcount;
+       u8 tmp;
+       bool pll_locked = false;
+
+       m = n = p = 0;
+       vcomax = 800000;
+       vcomin = 400000;
+       pllreffreq = 3333;
+
+       delta = 0xffffffff;
+       permitteddelta = clock * 5 / 1000;
+
+       for (testp = 16; testp > 0; testp--) {
+               if (clock * testp > vcomax)
+                       continue;
+               if (clock * testp < vcomin)
+                       continue;
+
+               for (testm = 1; testm < 33; testm++) {
+                       for (testn = 1; testn < 257; testn++) {
+                               computed = (pllreffreq * testn) /
+                                       (testm * testp);
+                               if (computed > clock)
+                                       tmpdelta = computed - clock;
+                               else
+                                       tmpdelta = clock - computed;
+                               if (tmpdelta < delta) {
+                                       delta = tmpdelta;
+                                       n = testn - 1;
+                                       m = (testm - 1) | ((n >> 1) & 0x80);
+                                       p = testp - 1;
+                               }
+                               if ((clock * testp) >= 600000)
+                                       p |= 80;
+                       }
+               }
+       }
+       for (i = 0; i <= 32 && pll_locked == false; i++) {
+               WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
+               tmp = RREG8(DAC_DATA);
+               tmp |= MGA1064_PIX_CLK_CTL_CLK_DIS;
+               WREG_DAC(MGA1064_PIX_CLK_CTL_CLK_DIS, tmp);
+
+               tmp = RREG8(MGAREG_MEM_MISC_READ);
+               tmp |= 0x3 << 2;
+               WREG8(MGAREG_MEM_MISC_WRITE, tmp);
+
+               WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
+               tmp = RREG8(DAC_DATA);
+               tmp |= MGA1064_PIX_CLK_CTL_CLK_POW_DOWN;
+               WREG_DAC(MGA1064_PIX_CLK_CTL, tmp);
+
+               udelay(500);
+
+               WREG_DAC(MGA1064_EH_PIX_PLLC_M, m);
+               WREG_DAC(MGA1064_EH_PIX_PLLC_N, n);
+               WREG_DAC(MGA1064_EH_PIX_PLLC_P, p);
+
+               udelay(500);
+
+               WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
+               tmp = RREG8(DAC_DATA);
+               tmp &= ~MGA1064_PIX_CLK_CTL_SEL_MSK;
+               tmp |= MGA1064_PIX_CLK_CTL_SEL_PLL;
+               WREG_DAC(MGA1064_PIX_CLK_CTL, tmp);
+
+               WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
+               tmp = RREG8(DAC_DATA);
+               tmp &= ~MGA1064_PIX_CLK_CTL_CLK_DIS;
+               tmp &= ~MGA1064_PIX_CLK_CTL_CLK_POW_DOWN;
+               WREG_DAC(MGA1064_PIX_CLK_CTL, tmp);
+
+               vcount = RREG8(MGAREG_VCOUNT);
+
+               for (j = 0; j < 30 && pll_locked == false; j++) {
+                       tmpcount = RREG8(MGAREG_VCOUNT);
+                       if (tmpcount < vcount)
+                               vcount = 0;
+                       if ((tmpcount - vcount) > 2)
+                               pll_locked = true;
+                       else
+                               udelay(5);
+               }
+       }
+
+       return 0;
+}
+
+static int mga_g200er_set_plls(struct mga_device *mdev, long clock)
+{
+       unsigned int vcomax, vcomin, pllreffreq;
+       unsigned int delta, tmpdelta;
+       unsigned int testr, testn, testm, testo;
+       unsigned int p, m, n;
+       unsigned int computed;
+       int tmp;
+
+       m = n = p = 0;
+       vcomax = 1488000;
+       vcomin = 1056000;
+       pllreffreq = 48000;
+
+       delta = 0xffffffff;
+
+       for (testr = 0; testr < 4; testr++) {
+               if (delta == 0)
+                       break;
+               for (testn = 5; testn < 129; testn++) {
+                       if (delta == 0)
+                               break;
+                       for (testm = 3; testm >= 0; testm--) {
+                               if (delta == 0)
+                                       break;
+                               for (testo = 5; testo < 33; testo++) {
+                                       computed = pllreffreq * (testn + 1) /
+                                               (testr + 1);
+                                       if (computed < vcomin)
+                                               continue;
+                                       if (computed > vcomax)
+                                               continue;
+                                       if (computed > clock)
+                                               tmpdelta = computed - clock;
+                                       else
+                                               tmpdelta = clock - computed;
+                                       if (tmpdelta < delta) {
+                                               delta = tmpdelta;
+                                               m = testm | (testo << 3);
+                                               n = testn;
+                                               p = testr | (testr << 3);
+                                       }
+                               }
+                       }
+               }
+       }
+
+       WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
+       tmp = RREG8(DAC_DATA);
+       tmp |= MGA1064_PIX_CLK_CTL_CLK_DIS;
+       WREG_DAC(MGA1064_PIX_CLK_CTL_CLK_DIS, tmp);
+
+       WREG8(DAC_INDEX, MGA1064_REMHEADCTL);
+       tmp = RREG8(DAC_DATA);
+       tmp |= MGA1064_REMHEADCTL_CLKDIS;
+       WREG_DAC(MGA1064_REMHEADCTL, tmp);
+
+       tmp = RREG8(MGAREG_MEM_MISC_READ);
+       tmp |= (0x3<<2) | 0xc0;
+       WREG8(MGAREG_MEM_MISC_WRITE, tmp);
+
+       WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
+       tmp = RREG8(DAC_DATA);
+       tmp &= ~MGA1064_PIX_CLK_CTL_CLK_DIS;
+       tmp |= MGA1064_PIX_CLK_CTL_CLK_POW_DOWN;
+       WREG_DAC(MGA1064_PIX_CLK_CTL, tmp);
+
+       udelay(500);
+
+       WREG_DAC(MGA1064_ER_PIX_PLLC_N, n);
+       WREG_DAC(MGA1064_ER_PIX_PLLC_M, m);
+       WREG_DAC(MGA1064_ER_PIX_PLLC_P, p);
+
+       udelay(50);
+
+       return 0;
+}
+
+static int mga_crtc_set_plls(struct mga_device *mdev, long clock)
+{
+       switch(mdev->type) {
+       case G200_SE_A:
+       case G200_SE_B:
+               return mga_g200se_set_plls(mdev, clock);
+               break;
+       case G200_WB:
+               return mga_g200wb_set_plls(mdev, clock);
+               break;
+       case G200_EV:
+               return mga_g200ev_set_plls(mdev, clock);
+               break;
+       case G200_EH:
+               return mga_g200eh_set_plls(mdev, clock);
+               break;
+       case G200_ER:
+               return mga_g200er_set_plls(mdev, clock);
+               break;
+       }
+       return 0;
+}
+
+static void mga_g200wb_prepare(struct drm_crtc *crtc)
+{
+       struct mga_device *mdev = crtc->dev->dev_private;
+       u8 tmp;
+       int iter_max;
+
+       /* 1- The first step is to warn the BMC of an upcoming mode change.
+        * We are putting the misc<0> to output.*/
+
+       WREG8(DAC_INDEX, MGA1064_GEN_IO_CTL);
+       tmp = RREG8(DAC_DATA);
+       tmp |= 0x10;
+       WREG_DAC(MGA1064_GEN_IO_CTL, tmp);
+
+       /* we are putting a 1 on the misc<0> line */
+       WREG8(DAC_INDEX, MGA1064_GEN_IO_DATA);
+       tmp = RREG8(DAC_DATA);
+       tmp |= 0x10;
+       WREG_DAC(MGA1064_GEN_IO_DATA, tmp);
+
+       /* 2- Second step to mask and further scan request
+        * This will be done by asserting the remfreqmsk bit (XSPAREREG<7>)
+        */
+       WREG8(DAC_INDEX, MGA1064_SPAREREG);
+       tmp = RREG8(DAC_DATA);
+       tmp |= 0x80;
+       WREG_DAC(MGA1064_SPAREREG, tmp);
+
+       /* 3a- the third step is to verifu if there is an active scan
+        * We are searching for a 0 on remhsyncsts <XSPAREREG<0>)
+        */
+       iter_max = 300;
+       while (!(tmp & 0x1) && iter_max) {
+               WREG8(DAC_INDEX, MGA1064_SPAREREG);
+               tmp = RREG8(DAC_DATA);
+               udelay(1000);
+               iter_max--;
+       }
+
+       /* 3b- this step occurs only if the remove is actually scanning
+        * we are waiting for the end of the frame which is a 1 on
+        * remvsyncsts (XSPAREREG<1>)
+        */
+       if (iter_max) {
+               iter_max = 300;
+               while ((tmp & 0x2) && iter_max) {
+                       WREG8(DAC_INDEX, MGA1064_SPAREREG);
+                       tmp = RREG8(DAC_DATA);
+                       udelay(1000);
+                       iter_max--;
+               }
+       }
+}
+
+static void mga_g200wb_commit(struct drm_crtc *crtc)
+{
+       u8 tmp;
+       struct mga_device *mdev = crtc->dev->dev_private;
+
+       /* 1- The first step is to ensure that the vrsten and hrsten are set */
+       WREG8(MGAREG_CRTCEXT_INDEX, 1);
+       tmp = RREG8(MGAREG_CRTCEXT_DATA);
+       WREG8(MGAREG_CRTCEXT_DATA, tmp | 0x88);
+
+       /* 2- second step is to assert the rstlvl2 */
+       WREG8(DAC_INDEX, MGA1064_REMHEADCTL2);
+       tmp = RREG8(DAC_DATA);
+       tmp |= 0x8;
+       WREG8(DAC_DATA, tmp);
+
+       /* wait 10 us */
+       udelay(10);
+
+       /* 3- deassert rstlvl2 */
+       tmp &= ~0x08;
+       WREG8(DAC_INDEX, MGA1064_REMHEADCTL2);
+       WREG8(DAC_DATA, tmp);
+
+       /* 4- remove mask of scan request */
+       WREG8(DAC_INDEX, MGA1064_SPAREREG);
+       tmp = RREG8(DAC_DATA);
+       tmp &= ~0x80;
+       WREG8(DAC_DATA, tmp);
+
+       /* 5- put back a 0 on the misc<0> line */
+       WREG8(DAC_INDEX, MGA1064_GEN_IO_DATA);
+       tmp = RREG8(DAC_DATA);
+       tmp &= ~0x10;
+       WREG_DAC(MGA1064_GEN_IO_DATA, tmp);
+}
+
+
+void mga_set_start_address(struct drm_crtc *crtc, unsigned offset)
+{
+       struct mga_device *mdev = crtc->dev->dev_private;
+       u32 addr;
+       int count;
+
+       while (RREG8(0x1fda) & 0x08);
+       while (!(RREG8(0x1fda) & 0x08));
+
+       count = RREG8(MGAREG_VCOUNT) + 2;
+       while (RREG8(MGAREG_VCOUNT) < count);
+
+       addr = offset >> 2;
+       WREG_CRT(0x0d, (u8)(addr & 0xff));
+       WREG_CRT(0x0c, (u8)(addr >> 8) & 0xff);
+       WREG_CRT(0xaf, (u8)(addr >> 16) & 0xf);
+}
+
+
+/* ast is different - we will force move buffers out of VRAM */
+static int mga_crtc_do_set_base(struct drm_crtc *crtc,
+                               struct drm_framebuffer *fb,
+                               int x, int y, int atomic)
+{
+       struct mga_device *mdev = crtc->dev->dev_private;
+       struct drm_gem_object *obj;
+       struct mga_framebuffer *mga_fb;
+       struct mgag200_bo *bo;
+       int ret;
+       u64 gpu_addr;
+
+       /* push the previous fb to system ram */
+       if (!atomic && fb) {
+               mga_fb = to_mga_framebuffer(fb);
+               obj = mga_fb->obj;
+               bo = gem_to_mga_bo(obj);
+               ret = mgag200_bo_reserve(bo, false);
+               if (ret)
+                       return ret;
+               mgag200_bo_push_sysram(bo);
+               mgag200_bo_unreserve(bo);
+       }
+
+       mga_fb = to_mga_framebuffer(crtc->fb);
+       obj = mga_fb->obj;
+       bo = gem_to_mga_bo(obj);
+
+       ret = mgag200_bo_reserve(bo, false);
+       if (ret)
+               return ret;
+
+       ret = mgag200_bo_pin(bo, TTM_PL_FLAG_VRAM, &gpu_addr);
+       if (ret) {
+               mgag200_bo_unreserve(bo);
+               return ret;
+       }
+
+       if (&mdev->mfbdev->mfb == mga_fb) {
+               /* if pushing console in kmap it */
+               ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap);
+               if (ret)
+                       DRM_ERROR("failed to kmap fbcon\n");
+
+       }
+       mgag200_bo_unreserve(bo);
+
+       DRM_INFO("mga base %llx\n", gpu_addr);
+
+       mga_set_start_address(crtc, (u32)gpu_addr);
+
+       return 0;
+}
+
+static int mga_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+                                 struct drm_framebuffer *old_fb)
+{
+       return mga_crtc_do_set_base(crtc, old_fb, x, y, 0);
+}
+
+static int mga_crtc_mode_set(struct drm_crtc *crtc,
+                               struct drm_display_mode *mode,
+                               struct drm_display_mode *adjusted_mode,
+                               int x, int y, struct drm_framebuffer *old_fb)
+{
+       struct drm_device *dev = crtc->dev;
+       struct mga_device *mdev = dev->dev_private;
+       int hdisplay, hsyncstart, hsyncend, htotal;
+       int vdisplay, vsyncstart, vsyncend, vtotal;
+       int pitch;
+       int option = 0, option2 = 0;
+       int i;
+       unsigned char misc = 0;
+       unsigned char ext_vga[6];
+       unsigned char ext_vga_index24;
+       unsigned char dac_index90 = 0;
+       u8 bppshift;
+
+       static unsigned char dacvalue[] = {
+               /* 0x00: */        0,    0,    0,    0,    0,    0, 0x00,    0,
+               /* 0x08: */        0,    0,    0,    0,    0,    0,    0,    0,
+               /* 0x10: */        0,    0,    0,    0,    0,    0,    0,    0,
+               /* 0x18: */     0x00,    0, 0xC9, 0xFF, 0xBF, 0x20, 0x1F, 0x20,
+               /* 0x20: */     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               /* 0x28: */     0x00, 0x00, 0x00, 0x00,    0,    0,    0, 0x40,
+               /* 0x30: */     0x00, 0xB0, 0x00, 0xC2, 0x34, 0x14, 0x02, 0x83,
+               /* 0x38: */     0x00, 0x93, 0x00, 0x77, 0x00, 0x00, 0x00, 0x3A,
+               /* 0x40: */        0,    0,    0,    0,    0,    0,    0,    0,
+               /* 0x48: */        0,    0,    0,    0,    0,    0,    0,    0
+       };
+
+       bppshift = mdev->bpp_shifts[(crtc->fb->bits_per_pixel >> 3) - 1];
+
+       switch (mdev->type) {
+       case G200_SE_A:
+       case G200_SE_B:
+               dacvalue[MGA1064_VREF_CTL] = 0x03;
+               dacvalue[MGA1064_PIX_CLK_CTL] = MGA1064_PIX_CLK_CTL_SEL_PLL;
+               dacvalue[MGA1064_MISC_CTL] = MGA1064_MISC_CTL_DAC_EN |
+                                            MGA1064_MISC_CTL_VGA8 |
+                                            MGA1064_MISC_CTL_DAC_RAM_CS;
+               if (mdev->has_sdram)
+                       option = 0x40049120;
+               else
+                       option = 0x4004d120;
+               option2 = 0x00008000;
+               break;
+       case G200_WB:
+               dacvalue[MGA1064_VREF_CTL] = 0x07;
+               option = 0x41049120;
+               option2 = 0x0000b000;
+               break;
+       case G200_EV:
+               dacvalue[MGA1064_PIX_CLK_CTL] = MGA1064_PIX_CLK_CTL_SEL_PLL;
+               dacvalue[MGA1064_MISC_CTL] = MGA1064_MISC_CTL_VGA8 |
+                                            MGA1064_MISC_CTL_DAC_RAM_CS;
+               option = 0x00000120;
+               option2 = 0x0000b000;
+               break;
+       case G200_EH:
+               dacvalue[MGA1064_MISC_CTL] = MGA1064_MISC_CTL_VGA8 |
+                                            MGA1064_MISC_CTL_DAC_RAM_CS;
+               option = 0x00000120;
+               option2 = 0x0000b000;
+               break;
+       case G200_ER:
+               dac_index90 = 0;
+               break;
+       }
+
+       switch (crtc->fb->bits_per_pixel) {
+       case 8:
+               dacvalue[MGA1064_MUL_CTL] = MGA1064_MUL_CTL_8bits;
+               break;
+       case 16:
+               if (crtc->fb->depth == 15)
+                       dacvalue[MGA1064_MUL_CTL] = MGA1064_MUL_CTL_15bits;
+               else
+                       dacvalue[MGA1064_MUL_CTL] = MGA1064_MUL_CTL_16bits;
+               break;
+       case 24:
+               dacvalue[MGA1064_MUL_CTL] = MGA1064_MUL_CTL_24bits;
+               break;
+       case 32:
+               dacvalue[MGA1064_MUL_CTL] = MGA1064_MUL_CTL_32_24bits;
+               break;
+       }
+
+       if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+               misc |= 0x40;
+       if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+               misc |= 0x80;
+
+
+       for (i = 0; i < sizeof(dacvalue); i++) {
+               if ((i <= 0x03) ||
+                   (i == 0x07) ||
+                   (i == 0x0b) ||
+                   (i == 0x0f) ||
+                   ((i >= 0x13) && (i <= 0x17)) ||
+                   (i == 0x1b) ||
+                   (i == 0x1c) ||
+                   ((i >= 0x1f) && (i <= 0x29)) ||
+                   ((i >= 0x30) && (i <= 0x37)))
+                       continue;
+               if (IS_G200_SE(mdev) &&
+                   ((i == 0x2c) || (i == 0x2d) || (i == 0x2e)))
+                       continue;
+               if ((mdev->type == G200_EV || mdev->type == G200_WB || mdev->type == G200_EH) &&
+                   (i >= 0x44) && (i <= 0x4e))
+                       continue;
+
+               WREG_DAC(i, dacvalue[i]);
+       }
+
+       if (mdev->type == G200_ER) {
+               WREG_DAC(0x90, dac_index90);
+       }
+
+
+       if (option)
+               pci_write_config_dword(dev->pdev, PCI_MGA_OPTION, option);
+       if (option2)
+               pci_write_config_dword(dev->pdev, PCI_MGA_OPTION2, option2);
+
+       WREG_SEQ(2, 0xf);
+       WREG_SEQ(3, 0);
+       WREG_SEQ(4, 0xe);
+
+       pitch = crtc->fb->pitches[0] / (crtc->fb->bits_per_pixel / 8);
+       if (crtc->fb->bits_per_pixel == 24)
+               pitch = pitch >> (4 - bppshift);
+       else
+               pitch = pitch >> (4 - bppshift);
+
+       hdisplay = mode->hdisplay / 8 - 1;
+       hsyncstart = mode->hsync_start / 8 - 1;
+       hsyncend = mode->hsync_end / 8 - 1;
+       htotal = mode->htotal / 8 - 1;
+
+       /* Work around hardware quirk */
+       if ((htotal & 0x07) == 0x06 || (htotal & 0x07) == 0x04)
+               htotal++;
+
+       vdisplay = mode->vdisplay - 1;
+       vsyncstart = mode->vsync_start - 1;
+       vsyncend = mode->vsync_end - 1;
+       vtotal = mode->vtotal - 2;
+
+       WREG_GFX(0, 0);
+       WREG_GFX(1, 0);
+       WREG_GFX(2, 0);
+       WREG_GFX(3, 0);
+       WREG_GFX(4, 0);
+       WREG_GFX(5, 0x40);
+       WREG_GFX(6, 0x5);
+       WREG_GFX(7, 0xf);
+       WREG_GFX(8, 0xf);
+
+       WREG_CRT(0, htotal - 4);
+       WREG_CRT(1, hdisplay);
+       WREG_CRT(2, hdisplay);
+       WREG_CRT(3, (htotal & 0x1F) | 0x80);
+       WREG_CRT(4, hsyncstart);
+       WREG_CRT(5, ((htotal & 0x20) << 2) | (hsyncend & 0x1F));
+       WREG_CRT(6, vtotal & 0xFF);
+       WREG_CRT(7, ((vtotal & 0x100) >> 8) |
+                ((vdisplay & 0x100) >> 7) |
+                ((vsyncstart & 0x100) >> 6) |
+                ((vdisplay & 0x100) >> 5) |
+                ((vdisplay & 0x100) >> 4) | /* linecomp */
+                ((vtotal & 0x200) >> 4)|
+                ((vdisplay & 0x200) >> 3) |
+                ((vsyncstart & 0x200) >> 2));
+       WREG_CRT(9, ((vdisplay & 0x200) >> 4) |
+                ((vdisplay & 0x200) >> 3));
+       WREG_CRT(10, 0);
+       WREG_CRT(11, 0);
+       WREG_CRT(12, 0);
+       WREG_CRT(13, 0);
+       WREG_CRT(14, 0);
+       WREG_CRT(15, 0);
+       WREG_CRT(16, vsyncstart & 0xFF);
+       WREG_CRT(17, (vsyncend & 0x0F) | 0x20);
+       WREG_CRT(18, vdisplay & 0xFF);
+       WREG_CRT(19, pitch & 0xFF);
+       WREG_CRT(20, 0);
+       WREG_CRT(21, vdisplay & 0xFF);
+       WREG_CRT(22, (vtotal + 1) & 0xFF);
+       WREG_CRT(23, 0xc3);
+       WREG_CRT(24, vdisplay & 0xFF);
+
+       ext_vga[0] = 0;
+       ext_vga[5] = 0;
+
+       /* TODO interlace */
+
+       ext_vga[0] |= (pitch & 0x300) >> 4;
+       ext_vga[1] = (((htotal - 4) & 0x100) >> 8) |
+               ((hdisplay & 0x100) >> 7) |
+               ((hsyncstart & 0x100) >> 6) |
+               (htotal & 0x40);
+       ext_vga[2] = ((vtotal & 0xc00) >> 10) |
+               ((vdisplay & 0x400) >> 8) |
+               ((vdisplay & 0xc00) >> 7) |
+               ((vsyncstart & 0xc00) >> 5) |
+               ((vdisplay & 0x400) >> 3);
+       if (crtc->fb->bits_per_pixel == 24)
+               ext_vga[3] = (((1 << bppshift) * 3) - 1) | 0x80;
+       else
+               ext_vga[3] = ((1 << bppshift) - 1) | 0x80;
+       ext_vga[4] = 0;
+       if (mdev->type == G200_WB)
+               ext_vga[1] |= 0x88;
+
+       ext_vga_index24 = 0x05;
+
+       /* Set pixel clocks */
+       misc = 0x2d;
+       WREG8(MGA_MISC_OUT, misc);
+
+       mga_crtc_set_plls(mdev, mode->clock);
+
+       for (i = 0; i < 6; i++) {
+               WREG_ECRT(i, ext_vga[i]);
+       }
+
+       if (mdev->type == G200_ER)
+               WREG_ECRT(24, ext_vga_index24);
+
+       if (mdev->type == G200_EV) {
+               WREG_ECRT(6, 0);
+       }
+
+       WREG_ECRT(0, ext_vga[0]);
+       /* Enable mga pixel clock */
+       misc = 0x2d;
+
+       WREG8(MGA_MISC_OUT, misc);
+
+       if (adjusted_mode)
+               memcpy(&mdev->mode, mode, sizeof(struct drm_display_mode));
+
+       mga_crtc_do_set_base(crtc, old_fb, x, y, 0);
+
+       /* reset tagfifo */
+       if (mdev->type == G200_ER) {
+               u32 mem_ctl = RREG32(MGAREG_MEMCTL);
+               u8 seq1;
+
+               /* screen off */
+               WREG8(MGAREG_SEQ_INDEX, 0x01);
+               seq1 = RREG8(MGAREG_SEQ_DATA) | 0x20;
+               WREG8(MGAREG_SEQ_DATA, seq1);
+
+               WREG32(MGAREG_MEMCTL, mem_ctl | 0x00200000);
+               udelay(1000);
+               WREG32(MGAREG_MEMCTL, mem_ctl & ~0x00200000);
+
+               WREG8(MGAREG_SEQ_DATA, seq1 & ~0x20);
+       }
+
+
+       if (IS_G200_SE(mdev)) {
+               if (mdev->reg_1e24 >= 0x02) {
+                       u8 hi_pri_lvl;
+                       u32 bpp;
+                       u32 mb;
+
+                       if (crtc->fb->bits_per_pixel > 16)
+                               bpp = 32;
+                       else if (crtc->fb->bits_per_pixel > 8)
+                               bpp = 16;
+                       else
+                               bpp = 8;
+
+                       mb = (mode->clock * bpp) / 1000;
+                       if (mb > 3100)
+                               hi_pri_lvl = 0;
+                       else if (mb > 2600)
+                               hi_pri_lvl = 1;
+                       else if (mb > 1900)
+                               hi_pri_lvl = 2;
+                       else if (mb > 1160)
+                               hi_pri_lvl = 3;
+                       else if (mb > 440)
+                               hi_pri_lvl = 4;
+                       else
+                               hi_pri_lvl = 5;
+
+                       WREG8(0x1fde, 0x06);
+                       WREG8(0x1fdf, hi_pri_lvl);
+               } else {
+                       if (mdev->reg_1e24 >= 0x01)
+                               WREG8(0x1fdf, 0x03);
+                       else
+                               WREG8(0x1fdf, 0x04);
+               }
+       }
+       return 0;
+}
+
+#if 0 /* code from mjg to attempt D3 on crtc dpms off - revisit later */
+static int mga_suspend(struct drm_crtc *crtc)
+{
+       struct mga_crtc *mga_crtc = to_mga_crtc(crtc);
+       struct drm_device *dev = crtc->dev;
+       struct mga_device *mdev = dev->dev_private;
+       struct pci_dev *pdev = dev->pdev;
+       int option;
+
+       if (mdev->suspended)
+               return 0;
+
+       WREG_SEQ(1, 0x20);
+       WREG_ECRT(1, 0x30);
+       /* Disable the pixel clock */
+       WREG_DAC(0x1a, 0x05);
+       /* Power down the DAC */
+       WREG_DAC(0x1e, 0x18);
+       /* Power down the pixel PLL */
+       WREG_DAC(0x1a, 0x0d);
+
+       /* Disable PLLs and clocks */
+       pci_read_config_dword(pdev, PCI_MGA_OPTION, &option);
+       option &= ~(0x1F8024);
+       pci_write_config_dword(pdev, PCI_MGA_OPTION, option);
+       pci_set_power_state(pdev, PCI_D3hot);
+       pci_disable_device(pdev);
+
+       mdev->suspended = true;
+
+       return 0;
+}
+
+static int mga_resume(struct drm_crtc *crtc)
+{
+       struct mga_crtc *mga_crtc = to_mga_crtc(crtc);
+       struct drm_device *dev = crtc->dev;
+       struct mga_device *mdev = dev->dev_private;
+       struct pci_dev *pdev = dev->pdev;
+       int option;
+
+       if (!mdev->suspended)
+               return 0;
+
+       pci_set_power_state(pdev, PCI_D0);
+       pci_enable_device(pdev);
+
+       /* Disable sysclk */
+       pci_read_config_dword(pdev, PCI_MGA_OPTION, &option);
+       option &= ~(0x4);
+       pci_write_config_dword(pdev, PCI_MGA_OPTION, option);
+
+       mdev->suspended = false;
+
+       return 0;
+}
+
+#endif
+
+static void mga_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+       struct drm_device *dev = crtc->dev;
+       struct mga_device *mdev = dev->dev_private;
+       u8 seq1 = 0, crtcext1 = 0;
+
+       switch (mode) {
+       case DRM_MODE_DPMS_ON:
+               seq1 = 0;
+               crtcext1 = 0;
+               mga_crtc_load_lut(crtc);
+               break;
+       case DRM_MODE_DPMS_STANDBY:
+               seq1 = 0x20;
+               crtcext1 = 0x10;
+               break;
+       case DRM_MODE_DPMS_SUSPEND:
+               seq1 = 0x20;
+               crtcext1 = 0x20;
+               break;
+       case DRM_MODE_DPMS_OFF:
+               seq1 = 0x20;
+               crtcext1 = 0x30;
+               break;
+       }
+
+#if 0
+       if (mode == DRM_MODE_DPMS_OFF) {
+               mga_suspend(crtc);
+       }
+#endif
+       WREG8(MGAREG_SEQ_INDEX, 0x01);
+       seq1 |= RREG8(MGAREG_SEQ_DATA) & ~0x20;
+       mga_wait_vsync(mdev);
+       mga_wait_busy(mdev);
+       WREG8(MGAREG_SEQ_DATA, seq1);
+       msleep(20);
+       WREG8(MGAREG_CRTCEXT_INDEX, 0x01);
+       crtcext1 |= RREG8(MGAREG_CRTCEXT_DATA) & ~0x30;
+       WREG8(MGAREG_CRTCEXT_DATA, crtcext1);
+
+#if 0
+       if (mode == DRM_MODE_DPMS_ON && mdev->suspended == true) {
+               mga_resume(crtc);
+               drm_helper_resume_force_mode(dev);
+       }
+#endif
+}
+
+/*
+ * This is called before a mode is programmed. A typical use might be to
+ * enable DPMS during the programming to avoid seeing intermediate stages,
+ * but that's not relevant to us
+ */
+static void mga_crtc_prepare(struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       struct mga_device *mdev = dev->dev_private;
+       u8 tmp;
+
+       /*      mga_resume(crtc);*/
+
+       WREG8(MGAREG_CRTC_INDEX, 0x11);
+       tmp = RREG8(MGAREG_CRTC_DATA);
+       WREG_CRT(0x11, tmp | 0x80);
+
+       if (mdev->type == G200_SE_A || mdev->type == G200_SE_B) {
+               WREG_SEQ(0, 1);
+               msleep(50);
+               WREG_SEQ(1, 0x20);
+               msleep(20);
+       } else {
+               WREG8(MGAREG_SEQ_INDEX, 0x1);
+               tmp = RREG8(MGAREG_SEQ_DATA);
+
+               /* start sync reset */
+               WREG_SEQ(0, 1);
+               WREG_SEQ(1, tmp | 0x20);
+       }
+
+       if (mdev->type == G200_WB)
+               mga_g200wb_prepare(crtc);
+
+       WREG_CRT(17, 0);
+}
+
+/*
+ * This is called after a mode is programmed. It should reverse anything done
+ * by the prepare function
+ */
+static void mga_crtc_commit(struct drm_crtc *crtc)
+{
+       struct drm_device *dev = crtc->dev;
+       struct mga_device *mdev = dev->dev_private;
+       struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+       u8 tmp;
+
+       if (mdev->type == G200_WB)
+               mga_g200wb_commit(crtc);
+
+       if (mdev->type == G200_SE_A || mdev->type == G200_SE_B) {
+               msleep(50);
+               WREG_SEQ(1, 0x0);
+               msleep(20);
+               WREG_SEQ(0, 0x3);
+       } else {
+               WREG8(MGAREG_SEQ_INDEX, 0x1);
+               tmp = RREG8(MGAREG_SEQ_DATA);
+
+               tmp &= ~0x20;
+               WREG_SEQ(0x1, tmp);
+               WREG_SEQ(0, 3);
+       }
+       crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
+}
+
+/*
+ * The core can pass us a set of gamma values to program. We actually only
+ * use this for 8-bit mode so can't perform smooth fades on deeper modes,
+ * but it's a requirement that we provide the function
+ */
+static void mga_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green,
+                                 u16 *blue, uint32_t start, uint32_t size)
+{
+       struct mga_crtc *mga_crtc = to_mga_crtc(crtc);
+       int end = (start + size > MGAG200_LUT_SIZE) ? MGAG200_LUT_SIZE : start + size;
+       int i;
+
+       for (i = start; i < end; i++) {
+               mga_crtc->lut_r[i] = red[i] >> 8;
+               mga_crtc->lut_g[i] = green[i] >> 8;
+               mga_crtc->lut_b[i] = blue[i] >> 8;
+       }
+       mga_crtc_load_lut(crtc);
+}
+
+/* Simple cleanup function */
+static void mga_crtc_destroy(struct drm_crtc *crtc)
+{
+       struct mga_crtc *mga_crtc = to_mga_crtc(crtc);
+
+       drm_crtc_cleanup(crtc);
+       kfree(mga_crtc);
+}
+
+/* These provide the minimum set of functions required to handle a CRTC */
+static const struct drm_crtc_funcs mga_crtc_funcs = {
+       .gamma_set = mga_crtc_gamma_set,
+       .set_config = drm_crtc_helper_set_config,
+       .destroy = mga_crtc_destroy,
+};
+
+static const struct drm_crtc_helper_funcs mga_helper_funcs = {
+       .dpms = mga_crtc_dpms,
+       .mode_fixup = mga_crtc_mode_fixup,
+       .mode_set = mga_crtc_mode_set,
+       .mode_set_base = mga_crtc_mode_set_base,
+       .prepare = mga_crtc_prepare,
+       .commit = mga_crtc_commit,
+       .load_lut = mga_crtc_load_lut,
+};
+
+/* CRTC setup */
+static void mga_crtc_init(struct drm_device *dev)
+{
+       struct mga_device *mdev = dev->dev_private;
+       struct mga_crtc *mga_crtc;
+       int i;
+
+       mga_crtc = kzalloc(sizeof(struct mga_crtc) +
+                             (MGAG200FB_CONN_LIMIT * sizeof(struct drm_connector *)),
+                             GFP_KERNEL);
+
+       if (mga_crtc == NULL)
+               return;
+
+       drm_crtc_init(dev, &mga_crtc->base, &mga_crtc_funcs);
+
+       drm_mode_crtc_set_gamma_size(&mga_crtc->base, MGAG200_LUT_SIZE);
+       mdev->mode_info.crtc = mga_crtc;
+
+       for (i = 0; i < MGAG200_LUT_SIZE; i++) {
+               mga_crtc->lut_r[i] = i;
+               mga_crtc->lut_g[i] = i;
+               mga_crtc->lut_b[i] = i;
+       }
+
+       drm_crtc_helper_add(&mga_crtc->base, &mga_helper_funcs);
+}
+
+/** Sets the color ramps on behalf of fbcon */
+void mga_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
+                             u16 blue, int regno)
+{
+       struct mga_crtc *mga_crtc = to_mga_crtc(crtc);
+
+       mga_crtc->lut_r[regno] = red >> 8;
+       mga_crtc->lut_g[regno] = green >> 8;
+       mga_crtc->lut_b[regno] = blue >> 8;
+}
+
+/** Gets the color ramps on behalf of fbcon */
+void mga_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
+                             u16 *blue, int regno)
+{
+       struct mga_crtc *mga_crtc = to_mga_crtc(crtc);
+
+       *red = (u16)mga_crtc->lut_r[regno] << 8;
+       *green = (u16)mga_crtc->lut_g[regno] << 8;
+       *blue = (u16)mga_crtc->lut_b[regno] << 8;
+}
+
+/*
+ * The encoder comes after the CRTC in the output pipeline, but before
+ * the connector. It's responsible for ensuring that the digital
+ * stream is appropriately converted into the output format. Setup is
+ * very simple in this case - all we have to do is inform qemu of the
+ * colour depth in order to ensure that it displays appropriately
+ */
+
+/*
+ * These functions are analagous to those in the CRTC code, but are intended
+ * to handle any encoder-specific limitations
+ */
+static bool mga_encoder_mode_fixup(struct drm_encoder *encoder,
+                                 struct drm_display_mode *mode,
+                                 struct drm_display_mode *adjusted_mode)
+{
+       return true;
+}
+
+static void mga_encoder_mode_set(struct drm_encoder *encoder,
+                               struct drm_display_mode *mode,
+                               struct drm_display_mode *adjusted_mode)
+{
+
+}
+
+static void mga_encoder_dpms(struct drm_encoder *encoder, int state)
+{
+       return;
+}
+
+static void mga_encoder_prepare(struct drm_encoder *encoder)
+{
+}
+
+static void mga_encoder_commit(struct drm_encoder *encoder)
+{
+}
+
+void mga_encoder_destroy(struct drm_encoder *encoder)
+{
+       struct mga_encoder *mga_encoder = to_mga_encoder(encoder);
+       drm_encoder_cleanup(encoder);
+       kfree(mga_encoder);
+}
+
+static const struct drm_encoder_helper_funcs mga_encoder_helper_funcs = {
+       .dpms = mga_encoder_dpms,
+       .mode_fixup = mga_encoder_mode_fixup,
+       .mode_set = mga_encoder_mode_set,
+       .prepare = mga_encoder_prepare,
+       .commit = mga_encoder_commit,
+};
+
+static const struct drm_encoder_funcs mga_encoder_encoder_funcs = {
+       .destroy = mga_encoder_destroy,
+};
+
+static struct drm_encoder *mga_encoder_init(struct drm_device *dev)
+{
+       struct drm_encoder *encoder;
+       struct mga_encoder *mga_encoder;
+
+       mga_encoder = kzalloc(sizeof(struct mga_encoder), GFP_KERNEL);
+       if (!mga_encoder)
+               return NULL;
+
+       encoder = &mga_encoder->base;
+       encoder->possible_crtcs = 0x1;
+
+       drm_encoder_init(dev, encoder, &mga_encoder_encoder_funcs,
+                        DRM_MODE_ENCODER_DAC);
+       drm_encoder_helper_add(encoder, &mga_encoder_helper_funcs);
+
+       return encoder;
+}
+
+
+static int mga_vga_get_modes(struct drm_connector *connector)
+{
+       struct mga_connector *mga_connector = to_mga_connector(connector);
+       struct edid *edid;
+       int ret = 0;
+
+       edid = drm_get_edid(connector, &mga_connector->i2c->adapter);
+       if (edid) {
+               drm_mode_connector_update_edid_property(connector, edid);
+               ret = drm_add_edid_modes(connector, edid);
+               connector->display_info.raw_edid = NULL;
+               kfree(edid);
+       }
+       return ret;
+}
+
+static int mga_vga_mode_valid(struct drm_connector *connector,
+                                struct drm_display_mode *mode)
+{
+       /* FIXME: Add bandwidth and g200se limitations */
+
+       if (mode->crtc_hdisplay > 2048 || mode->crtc_hsync_start > 4096 ||
+           mode->crtc_hsync_end > 4096 || mode->crtc_htotal > 4096 ||
+           mode->crtc_vdisplay > 2048 || mode->crtc_vsync_start > 4096 ||
+           mode->crtc_vsync_end > 4096 || mode->crtc_vtotal > 4096) {
+               return MODE_BAD;
+       }
+
+       return MODE_OK;
+}
+
+struct drm_encoder *mga_connector_best_encoder(struct drm_connector
+                                                 *connector)
+{
+       int enc_id = connector->encoder_ids[0];
+       struct drm_mode_object *obj;
+       struct drm_encoder *encoder;
+
+       /* pick the encoder ids */
+       if (enc_id) {
+               obj =
+                   drm_mode_object_find(connector->dev, enc_id,
+                                        DRM_MODE_OBJECT_ENCODER);
+               if (!obj)
+                       return NULL;
+               encoder = obj_to_encoder(obj);
+               return encoder;
+       }
+       return NULL;
+}
+
+static enum drm_connector_status mga_vga_detect(struct drm_connector
+                                                  *connector, bool force)
+{
+       return connector_status_connected;
+}
+
+static void mga_connector_destroy(struct drm_connector *connector)
+{
+       struct mga_connector *mga_connector = to_mga_connector(connector);
+       mgag200_i2c_destroy(mga_connector->i2c);
+       drm_connector_cleanup(connector);
+       kfree(connector);
+}
+
+struct drm_connector_helper_funcs mga_vga_connector_helper_funcs = {
+       .get_modes = mga_vga_get_modes,
+       .mode_valid = mga_vga_mode_valid,
+       .best_encoder = mga_connector_best_encoder,
+};
+
+struct drm_connector_funcs mga_vga_connector_funcs = {
+       .dpms = drm_helper_connector_dpms,
+       .detect = mga_vga_detect,
+       .fill_modes = drm_helper_probe_single_connector_modes,
+       .destroy = mga_connector_destroy,
+};
+
+static struct drm_connector *mga_vga_init(struct drm_device *dev)
+{
+       struct drm_connector *connector;
+       struct mga_connector *mga_connector;
+
+       mga_connector = kzalloc(sizeof(struct mga_connector), GFP_KERNEL);
+       if (!mga_connector)
+               return NULL;
+
+       connector = &mga_connector->base;
+
+       drm_connector_init(dev, connector,
+                          &mga_vga_connector_funcs, DRM_MODE_CONNECTOR_VGA);
+
+       drm_connector_helper_add(connector, &mga_vga_connector_helper_funcs);
+
+       mga_connector->i2c = mgag200_i2c_create(dev);
+       if (!mga_connector->i2c)
+               DRM_ERROR("failed to add ddc bus\n");
+
+       return connector;
+}
+
+
+int mgag200_modeset_init(struct mga_device *mdev)
+{
+       struct drm_encoder *encoder;
+       struct drm_connector *connector;
+       int ret;
+
+       mdev->mode_info.mode_config_initialized = true;
+
+       mdev->dev->mode_config.max_width = MGAG200_MAX_FB_WIDTH;
+       mdev->dev->mode_config.max_height = MGAG200_MAX_FB_HEIGHT;
+
+       mdev->dev->mode_config.fb_base = mdev->mc.vram_base;
+
+       mga_crtc_init(mdev->dev);
+
+       encoder = mga_encoder_init(mdev->dev);
+       if (!encoder) {
+               DRM_ERROR("mga_encoder_init failed\n");
+               return -1;
+       }
+
+       connector = mga_vga_init(mdev->dev);
+       if (!connector) {
+               DRM_ERROR("mga_vga_init failed\n");
+               return -1;
+       }
+
+       drm_mode_connector_attach_encoder(connector, encoder);
+
+       ret = mgag200_fbdev_init(mdev);
+       if (ret) {
+               DRM_ERROR("mga_fbdev_init failed\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+void mgag200_modeset_fini(struct mga_device *mdev)
+{
+
+}
diff --git a/drivers/gpu/drm/mgag200/mgag200_reg.h b/drivers/gpu/drm/mgag200/mgag200_reg.h
new file mode 100644 (file)
index 0000000..fb24d86
--- /dev/null
@@ -0,0 +1,661 @@
+/*
+ * MGA Millennium (MGA2064W) functions
+ * MGA Mystique (MGA1064SG) functions
+ *
+ * Copyright 1996 The XFree86 Project, Inc.
+ *
+ * Authors
+ *             Dirk Hohndel
+ *                     hohndel@XFree86.Org
+ *             David Dawes
+ *                     dawes@XFree86.Org
+ * Contributors:
+ *             Guy DESBIEF, Aix-en-provence, France
+ *                     g.desbief@aix.pacwan.net
+ *             MGA1064SG Mystique register file
+ */
+
+
+#ifndef _MGA_REG_H_
+#define _MGA_REG_H_
+
+#define        MGAREG_DWGCTL           0x1c00
+#define        MGAREG_MACCESS          0x1c04
+/* the following is a mystique only register */
+#define MGAREG_MCTLWTST                0x1c08
+#define        MGAREG_ZORG             0x1c0c
+
+#define        MGAREG_PAT0             0x1c10
+#define        MGAREG_PAT1             0x1c14
+#define        MGAREG_PLNWT            0x1c1c
+
+#define        MGAREG_BCOL             0x1c20
+#define        MGAREG_FCOL             0x1c24
+
+#define        MGAREG_SRC0             0x1c30
+#define        MGAREG_SRC1             0x1c34
+#define        MGAREG_SRC2             0x1c38
+#define        MGAREG_SRC3             0x1c3c
+
+#define        MGAREG_XYSTRT           0x1c40
+#define        MGAREG_XYEND            0x1c44
+
+#define        MGAREG_SHIFT            0x1c50
+/* the following is a mystique only register */
+#define MGAREG_DMAPAD          0x1c54
+#define        MGAREG_SGN              0x1c58
+#define        MGAREG_LEN              0x1c5c
+
+#define        MGAREG_AR0              0x1c60
+#define        MGAREG_AR1              0x1c64
+#define        MGAREG_AR2              0x1c68
+#define        MGAREG_AR3              0x1c6c
+#define        MGAREG_AR4              0x1c70
+#define        MGAREG_AR5              0x1c74
+#define        MGAREG_AR6              0x1c78
+
+#define        MGAREG_CXBNDRY          0x1c80
+#define        MGAREG_FXBNDRY          0x1c84
+#define        MGAREG_YDSTLEN          0x1c88
+#define        MGAREG_PITCH            0x1c8c
+
+#define        MGAREG_YDST             0x1c90
+#define        MGAREG_YDSTORG          0x1c94
+#define        MGAREG_YTOP             0x1c98
+#define        MGAREG_YBOT             0x1c9c
+
+#define        MGAREG_CXLEFT           0x1ca0
+#define        MGAREG_CXRIGHT          0x1ca4
+#define        MGAREG_FXLEFT           0x1ca8
+#define        MGAREG_FXRIGHT          0x1cac
+
+#define        MGAREG_XDST             0x1cb0
+
+#define        MGAREG_DR0              0x1cc0
+#define        MGAREG_DR1              0x1cc4
+#define        MGAREG_DR2              0x1cc8
+#define        MGAREG_DR3              0x1ccc
+
+#define        MGAREG_DR4              0x1cd0
+#define        MGAREG_DR5              0x1cd4
+#define        MGAREG_DR6              0x1cd8
+#define        MGAREG_DR7              0x1cdc
+
+#define        MGAREG_DR8              0x1ce0
+#define        MGAREG_DR9              0x1ce4
+#define        MGAREG_DR10             0x1ce8
+#define        MGAREG_DR11             0x1cec
+
+#define        MGAREG_DR12             0x1cf0
+#define        MGAREG_DR13             0x1cf4
+#define        MGAREG_DR14             0x1cf8
+#define        MGAREG_DR15             0x1cfc
+
+#define MGAREG_SRCORG          0x2cb4
+#define MGAREG_DSTORG          0x2cb8
+
+/* add or or this to one of the previous "power registers" to start
+   the drawing engine */
+
+#define MGAREG_EXEC            0x0100
+
+#define        MGAREG_FIFOSTATUS       0x1e10
+#define        MGAREG_Status           0x1e14
+#define MGAREG_CACHEFLUSH       0x1fff
+#define        MGAREG_ICLEAR           0x1e18
+#define        MGAREG_IEN              0x1e1c
+
+#define        MGAREG_VCOUNT           0x1e20
+
+#define        MGAREG_Reset            0x1e40
+
+#define        MGAREG_OPMODE           0x1e54
+
+/* Warp Registers */
+#define MGAREG_WIADDR           0x1dc0
+#define MGAREG_WIADDR2          0x1dd8
+#define MGAREG_WGETMSB          0x1dc8
+#define MGAREG_WVRTXSZ          0x1dcc
+#define MGAREG_WACCEPTSEQ       0x1dd4
+#define MGAREG_WMISC            0x1e70
+
+#define MGAREG_MEMCTL           0x2e08
+
+/* OPMODE register additives */
+
+#define MGAOPM_DMA_GENERAL     (0x00 << 2)
+#define MGAOPM_DMA_BLIT                (0x01 << 2)
+#define MGAOPM_DMA_VECTOR      (0x10 << 2)
+
+/* MACCESS register additives */
+#define MGAMAC_PW8               0x00
+#define MGAMAC_PW16              0x01
+#define MGAMAC_PW24              0x03 /* not a typo */
+#define MGAMAC_PW32              0x02 /* not a typo */
+#define MGAMAC_BYPASS332         0x10000000
+#define MGAMAC_NODITHER          0x40000000
+#define MGAMAC_DIT555            0x80000000
+
+/* DWGCTL register additives */
+
+/* Lines */
+
+#define MGADWG_LINE_OPEN       0x00
+#define MGADWG_AUTOLINE_OPEN   0x01
+#define MGADWG_LINE_CLOSE      0x02
+#define MGADWG_AUTOLINE_CLOSE  0x03
+
+/* Trapezoids */
+#define MGADWG_TRAP            0x04
+#define MGADWG_TEXTURE_TRAP    0x06
+
+/* BitBlts */
+
+#define MGADWG_BITBLT          0x08
+#define MGADWG_FBITBLT         0x0c
+#define MGADWG_ILOAD           0x09
+#define MGADWG_ILOAD_SCALE     0x0d
+#define MGADWG_ILOAD_FILTER    0x0f
+#define MGADWG_ILOAD_HIQH      0x07
+#define MGADWG_ILOAD_HIQHV     0x0e
+#define MGADWG_IDUMP           0x0a
+
+/* atype access to WRAM */
+
+#define MGADWG_RPL             ( 0x00 << 4 )
+#define MGADWG_RSTR            ( 0x01 << 4 )
+#define MGADWG_ZI              ( 0x03 << 4 )
+#define MGADWG_BLK             ( 0x04 << 4 )
+#define MGADWG_I               ( 0x07 << 4 )
+
+/* specifies whether bit blits are linear or xy */
+#define MGADWG_LINEAR          ( 0x01 << 7 )
+
+/* z drawing mode. use MGADWG_NOZCMP for always */
+
+#define MGADWG_NOZCMP          ( 0x00 << 8 )
+#define MGADWG_ZE              ( 0x02 << 8 )
+#define MGADWG_ZNE             ( 0x03 << 8 )
+#define MGADWG_ZLT             ( 0x04 << 8 )
+#define MGADWG_ZLTE            ( 0x05 << 8 )
+#define MGADWG_GT              ( 0x06 << 8 )
+#define MGADWG_GTE             ( 0x07 << 8 )
+
+/* use this to force colour expansion circuitry to do its stuff */
+
+#define MGADWG_SOLID           ( 0x01 << 11 )
+
+/* ar register at zero */
+
+#define MGADWG_ARZERO          ( 0x01 << 12 )
+
+#define MGADWG_SGNZERO         ( 0x01 << 13 )
+
+#define MGADWG_SHIFTZERO       ( 0x01 << 14 )
+
+/* See table on 4-43 for bop ALU operations */
+
+/* See table on 4-44 for translucidity masks */
+
+#define MGADWG_BMONOLEF                ( 0x00 << 25 )
+#define MGADWG_BMONOWF         ( 0x04 << 25 )
+#define MGADWG_BPLAN           ( 0x01 << 25 )
+
+/* note that if bfcol is specified and you're doing a bitblt, it causes
+   a fbitblt to be performed, so check that you obey the fbitblt rules */
+
+#define MGADWG_BFCOL                   ( 0x02 << 25 )
+#define MGADWG_BUYUV           ( 0x0e << 25 )
+#define MGADWG_BU32BGR         ( 0x03 << 25 )
+#define MGADWG_BU32RGB         ( 0x07 << 25 )
+#define MGADWG_BU24BGR         ( 0x0b << 25 )
+#define MGADWG_BU24RGB         ( 0x0f << 25 )
+
+#define MGADWG_PATTERN         ( 0x01 << 29 )
+#define MGADWG_TRANSC          ( 0x01 << 30 )
+#define MGAREG_MISC_WRITE      0x3c2
+#define MGAREG_MISC_READ       0x3cc
+#define MGAREG_MEM_MISC_WRITE       0x1fc2
+#define MGAREG_MEM_MISC_READ        0x1fcc
+
+#define MGAREG_MISC_IOADSEL    (0x1 << 0)
+#define MGAREG_MISC_RAMMAPEN   (0x1 << 1)
+#define MGAREG_MISC_CLK_SEL_VGA25      (0x0 << 2)
+#define MGAREG_MISC_CLK_SEL_VGA28      (0x1 << 2)
+#define MGAREG_MISC_CLK_SEL_MGA_PIX    (0x2 << 2)
+#define MGAREG_MISC_CLK_SEL_MGA_MSK    (0x3 << 2)
+#define MGAREG_MISC_VIDEO_DIS  (0x1 << 4)
+#define MGAREG_MISC_HIGH_PG_SEL        (0x1 << 5)
+
+/* MMIO VGA registers */
+#define MGAREG_SEQ_INDEX       0x1fc4
+#define MGAREG_SEQ_DATA                0x1fc5
+#define MGAREG_CRTC_INDEX      0x1fd4
+#define MGAREG_CRTC_DATA       0x1fd5
+#define MGAREG_CRTCEXT_INDEX   0x1fde
+#define MGAREG_CRTCEXT_DATA    0x1fdf
+
+
+
+/* MGA bits for registers PCI_OPTION_REG */
+#define MGA1064_OPT_SYS_CLK_PCI                ( 0x00 << 0 )
+#define MGA1064_OPT_SYS_CLK_PLL                ( 0x01 << 0 )
+#define MGA1064_OPT_SYS_CLK_EXT                ( 0x02 << 0 )
+#define MGA1064_OPT_SYS_CLK_MSK                ( 0x03 << 0 )
+
+#define MGA1064_OPT_SYS_CLK_DIS                ( 0x01 << 2 )
+#define MGA1064_OPT_G_CLK_DIV_1                ( 0x01 << 3 )
+#define MGA1064_OPT_M_CLK_DIV_1                ( 0x01 << 4 )
+
+#define MGA1064_OPT_SYS_PLL_PDN                ( 0x01 << 5 )
+#define MGA1064_OPT_VGA_ION            ( 0x01 << 8 )
+
+/* MGA registers in PCI config space */
+#define PCI_MGA_INDEX          0x44
+#define PCI_MGA_DATA           0x48
+#define PCI_MGA_OPTION         0x40
+#define PCI_MGA_OPTION2                0x50
+#define PCI_MGA_OPTION3                0x54
+
+#define RAMDAC_OFFSET          0x3c00
+
+/* TVP3026 direct registers */
+
+#define TVP3026_INDEX          0x00
+#define TVP3026_WADR_PAL       0x00
+#define TVP3026_COL_PAL                0x01
+#define TVP3026_PIX_RD_MSK     0x02
+#define TVP3026_RADR_PAL       0x03
+#define TVP3026_CUR_COL_ADDR   0x04
+#define TVP3026_CUR_COL_DATA   0x05
+#define TVP3026_DATA           0x0a
+#define TVP3026_CUR_RAM                0x0b
+#define TVP3026_CUR_XLOW       0x0c
+#define TVP3026_CUR_XHI                0x0d
+#define TVP3026_CUR_YLOW       0x0e
+#define TVP3026_CUR_YHI                0x0f
+
+/* TVP3026 indirect registers */
+
+#define TVP3026_SILICON_REV    0x01
+#define TVP3026_CURSOR_CTL     0x06
+#define TVP3026_LATCH_CTL      0x0f
+#define TVP3026_TRUE_COLOR_CTL 0x18
+#define TVP3026_MUX_CTL                0x19
+#define TVP3026_CLK_SEL                0x1a
+#define TVP3026_PAL_PAGE       0x1c
+#define TVP3026_GEN_CTL                0x1d
+#define TVP3026_MISC_CTL       0x1e
+#define TVP3026_GEN_IO_CTL     0x2a
+#define TVP3026_GEN_IO_DATA    0x2b
+#define TVP3026_PLL_ADDR       0x2c
+#define TVP3026_PIX_CLK_DATA   0x2d
+#define TVP3026_MEM_CLK_DATA   0x2e
+#define TVP3026_LOAD_CLK_DATA  0x2f
+#define TVP3026_KEY_RED_LOW    0x32
+#define TVP3026_KEY_RED_HI     0x33
+#define TVP3026_KEY_GREEN_LOW  0x34
+#define TVP3026_KEY_GREEN_HI   0x35
+#define TVP3026_KEY_BLUE_LOW   0x36
+#define TVP3026_KEY_BLUE_HI    0x37
+#define TVP3026_KEY_CTL                0x38
+#define TVP3026_MCLK_CTL       0x39
+#define TVP3026_SENSE_TEST     0x3a
+#define TVP3026_TEST_DATA      0x3b
+#define TVP3026_CRC_LSB                0x3c
+#define TVP3026_CRC_MSB                0x3d
+#define TVP3026_CRC_CTL                0x3e
+#define TVP3026_ID             0x3f
+#define TVP3026_RESET          0xff
+
+
+/* MGA1064 DAC Register file */
+/* MGA1064 direct registers */
+
+#define MGA1064_INDEX          0x00
+#define MGA1064_WADR_PAL       0x00
+#define MGA1064_SPAREREG        0x00
+#define MGA1064_COL_PAL                0x01
+#define MGA1064_PIX_RD_MSK     0x02
+#define MGA1064_RADR_PAL       0x03
+#define MGA1064_DATA           0x0a
+
+#define MGA1064_CUR_XLOW       0x0c
+#define MGA1064_CUR_XHI                0x0d
+#define MGA1064_CUR_YLOW       0x0e
+#define MGA1064_CUR_YHI                0x0f
+
+/* MGA1064 indirect registers */
+#define MGA1064_DVI_PIPE_CTL    0x03
+#define MGA1064_CURSOR_BASE_ADR_LOW    0x04
+#define MGA1064_CURSOR_BASE_ADR_HI     0x05
+#define MGA1064_CURSOR_CTL     0x06
+#define MGA1064_CURSOR_COL0_RED        0x08
+#define MGA1064_CURSOR_COL0_GREEN      0x09
+#define MGA1064_CURSOR_COL0_BLUE       0x0a
+
+#define MGA1064_CURSOR_COL1_RED        0x0c
+#define MGA1064_CURSOR_COL1_GREEN      0x0d
+#define MGA1064_CURSOR_COL1_BLUE       0x0e
+
+#define MGA1064_CURSOR_COL2_RED        0x010
+#define MGA1064_CURSOR_COL2_GREEN      0x011
+#define MGA1064_CURSOR_COL2_BLUE       0x012
+
+#define MGA1064_VREF_CTL       0x018
+
+#define MGA1064_MUL_CTL                0x19
+#define MGA1064_MUL_CTL_8bits          0x0
+#define MGA1064_MUL_CTL_15bits         0x01
+#define MGA1064_MUL_CTL_16bits         0x02
+#define MGA1064_MUL_CTL_24bits         0x03
+#define MGA1064_MUL_CTL_32bits         0x04
+#define MGA1064_MUL_CTL_2G8V16bits             0x05
+#define MGA1064_MUL_CTL_G16V16bits             0x06
+#define MGA1064_MUL_CTL_32_24bits              0x07
+
+#define MGA1064_PIX_CLK_CTL            0x1a
+#define MGA1064_PIX_CLK_CTL_CLK_DIS            ( 0x01 << 2 )
+#define MGA1064_PIX_CLK_CTL_CLK_POW_DOWN       ( 0x01 << 3 )
+#define MGA1064_PIX_CLK_CTL_SEL_PCI            ( 0x00 << 0 )
+#define MGA1064_PIX_CLK_CTL_SEL_PLL            ( 0x01 << 0 )
+#define MGA1064_PIX_CLK_CTL_SEL_EXT            ( 0x02 << 0 )
+#define MGA1064_PIX_CLK_CTL_SEL_MSK            ( 0x03 << 0 )
+
+#define MGA1064_GEN_CTL                0x1d
+#define MGA1064_GEN_CTL_SYNC_ON_GREEN_DIS      (0x01 << 5)
+#define MGA1064_MISC_CTL       0x1e
+#define MGA1064_MISC_CTL_DAC_EN                ( 0x01 << 0 )
+#define MGA1064_MISC_CTL_VGA                   ( 0x01 << 1 )
+#define MGA1064_MISC_CTL_DIS_CON               ( 0x03 << 1 )
+#define MGA1064_MISC_CTL_MAFC                  ( 0x02 << 1 )
+#define MGA1064_MISC_CTL_VGA8                  ( 0x01 << 3 )
+#define MGA1064_MISC_CTL_DAC_RAM_CS            ( 0x01 << 4 )
+
+#define MGA1064_GEN_IO_CTL2    0x29
+#define MGA1064_GEN_IO_CTL     0x2a
+#define MGA1064_GEN_IO_DATA    0x2b
+#define MGA1064_SYS_PLL_M      0x2c
+#define MGA1064_SYS_PLL_N      0x2d
+#define MGA1064_SYS_PLL_P      0x2e
+#define MGA1064_SYS_PLL_STAT   0x2f
+
+#define MGA1064_REMHEADCTL     0x30
+#define MGA1064_REMHEADCTL_CLKDIS ( 0x01 << 0 )
+#define MGA1064_REMHEADCTL_CLKSL_OFF ( 0x00 << 1 )
+#define MGA1064_REMHEADCTL_CLKSL_PLL ( 0x01 << 1 )
+#define MGA1064_REMHEADCTL_CLKSL_PCI ( 0x02 << 1 )
+#define MGA1064_REMHEADCTL_CLKSL_MSK ( 0x03 << 1 )
+
+#define MGA1064_REMHEADCTL2     0x31
+
+#define MGA1064_ZOOM_CTL       0x38
+#define MGA1064_SENSE_TST      0x3a
+
+#define MGA1064_CRC_LSB                0x3c
+#define MGA1064_CRC_MSB                0x3d
+#define MGA1064_CRC_CTL                0x3e
+#define MGA1064_COL_KEY_MSK_LSB                0x40
+#define MGA1064_COL_KEY_MSK_MSB                0x41
+#define MGA1064_COL_KEY_LSB            0x42
+#define MGA1064_COL_KEY_MSB            0x43
+#define MGA1064_PIX_PLLA_M     0x44
+#define MGA1064_PIX_PLLA_N     0x45
+#define MGA1064_PIX_PLLA_P     0x46
+#define MGA1064_PIX_PLLB_M     0x48
+#define MGA1064_PIX_PLLB_N     0x49
+#define MGA1064_PIX_PLLB_P     0x4a
+#define MGA1064_PIX_PLLC_M     0x4c
+#define MGA1064_PIX_PLLC_N     0x4d
+#define MGA1064_PIX_PLLC_P     0x4e
+
+#define MGA1064_PIX_PLL_STAT   0x4f
+
+/*Added for G450 dual head*/
+
+#define MGA1064_VID_PLL_STAT    0x8c
+#define MGA1064_VID_PLL_P       0x8D
+#define MGA1064_VID_PLL_M       0x8E
+#define MGA1064_VID_PLL_N       0x8F
+
+/* Modified PLL for G200 Winbond (G200WB) */
+#define MGA1064_WB_PIX_PLLC_M  0xb7
+#define MGA1064_WB_PIX_PLLC_N  0xb6
+#define MGA1064_WB_PIX_PLLC_P  0xb8
+
+/* Modified PLL for G200 Maxim (G200EV) */
+#define MGA1064_EV_PIX_PLLC_M  0xb6
+#define MGA1064_EV_PIX_PLLC_N  0xb7
+#define MGA1064_EV_PIX_PLLC_P  0xb8
+
+/* Modified PLL for G200 EH */
+#define MGA1064_EH_PIX_PLLC_M   0xb6
+#define MGA1064_EH_PIX_PLLC_N   0xb7
+#define MGA1064_EH_PIX_PLLC_P   0xb8
+
+/* Modified PLL for G200 Maxim (G200ER) */
+#define MGA1064_ER_PIX_PLLC_M  0xb7
+#define MGA1064_ER_PIX_PLLC_N  0xb6
+#define MGA1064_ER_PIX_PLLC_P  0xb8
+
+#define MGA1064_DISP_CTL        0x8a
+#define MGA1064_DISP_CTL_DAC1OUTSEL_MASK       0x01
+#define MGA1064_DISP_CTL_DAC1OUTSEL_DIS        0x00
+#define MGA1064_DISP_CTL_DAC1OUTSEL_EN         0x01
+#define MGA1064_DISP_CTL_DAC2OUTSEL_MASK       (0x03 << 2)
+#define MGA1064_DISP_CTL_DAC2OUTSEL_DIS        0x00
+#define MGA1064_DISP_CTL_DAC2OUTSEL_CRTC1      (0x01 << 2)
+#define MGA1064_DISP_CTL_DAC2OUTSEL_CRTC2      (0x02 << 2)
+#define MGA1064_DISP_CTL_DAC2OUTSEL_TVE        (0x03 << 2)
+#define MGA1064_DISP_CTL_PANOUTSEL_MASK        (0x03 << 5)
+#define MGA1064_DISP_CTL_PANOUTSEL_DIS         0x00
+#define MGA1064_DISP_CTL_PANOUTSEL_CRTC1       (0x01 << 5)
+#define MGA1064_DISP_CTL_PANOUTSEL_CRTC2RGB    (0x02 << 5)
+#define MGA1064_DISP_CTL_PANOUTSEL_CRTC2656    (0x03 << 5)
+
+#define MGA1064_SYNC_CTL        0x8b
+
+#define MGA1064_PWR_CTL         0xa0
+#define MGA1064_PWR_CTL_DAC2_EN                (0x01 << 0)
+#define MGA1064_PWR_CTL_VID_PLL_EN             (0x01 << 1)
+#define MGA1064_PWR_CTL_PANEL_EN               (0x01 << 2)
+#define MGA1064_PWR_CTL_RFIFO_EN               (0x01 << 3)
+#define MGA1064_PWR_CTL_CFIFO_EN               (0x01 << 4)
+
+#define MGA1064_PAN_CTL         0xa2
+
+/* Using crtc2 */
+#define MGAREG2_C2CTL            0x10
+#define MGAREG2_C2HPARAM         0x14
+#define MGAREG2_C2HSYNC          0x18
+#define MGAREG2_C2VPARAM         0x1c
+#define MGAREG2_C2VSYNC          0x20
+#define MGAREG2_C2STARTADD0      0x28
+
+#define MGAREG2_C2OFFSET         0x40
+#define MGAREG2_C2DATACTL        0x4c
+
+#define MGAREG_C2CTL            0x3c10
+#define MGAREG_C2CTL_C2_EN                     0x01
+
+#define MGAREG_C2_HIPRILVL_M                   (0x07 << 4)
+#define MGAREG_C2_MAXHIPRI_M                   (0x07 << 8)
+
+#define MGAREG_C2CTL_PIXCLKSEL_MASK            (0x03 << 1)
+#define MGAREG_C2CTL_PIXCLKSELH_MASK           (0x01 << 14)
+#define MGAREG_C2CTL_PIXCLKSEL_PCICLK          0x00
+#define MGAREG_C2CTL_PIXCLKSEL_VDOCLK          (0x01 << 1)
+#define MGAREG_C2CTL_PIXCLKSEL_PIXELPLL        (0x02 << 1)
+#define MGAREG_C2CTL_PIXCLKSEL_VIDEOPLL        (0x03 << 1)
+#define MGAREG_C2CTL_PIXCLKSEL_VDCLK           (0x01 << 14)
+
+#define MGAREG_C2CTL_PIXCLKSEL_CRISTAL         (0x01 << 1) | (0x01 << 14)
+#define MGAREG_C2CTL_PIXCLKSEL_SYSTEMPLL       (0x02 << 1) | (0x01 << 14)
+
+#define MGAREG_C2CTL_PIXCLKDIS_MASK            (0x01 << 3)
+#define MGAREG_C2CTL_PIXCLKDIS_DISABLE         (0x01 << 3)
+
+#define MGAREG_C2CTL_CRTCDACSEL_MASK           (0x01 << 20)
+#define MGAREG_C2CTL_CRTCDACSEL_CRTC1          0x00
+#define MGAREG_C2CTL_CRTCDACSEL_CRTC2          (0x01 << 20)
+
+#define MGAREG_C2HPARAM         0x3c14
+#define MGAREG_C2HSYNC          0x3c18
+#define MGAREG_C2VPARAM         0x3c1c
+#define MGAREG_C2VSYNC          0x3c20
+#define MGAREG_C2STARTADD0      0x3c28
+
+#define MGAREG_C2OFFSET         0x3c40
+#define MGAREG_C2DATACTL        0x3c4c
+
+/* video register */
+
+#define MGAREG_BESA1C3ORG      0x3d60
+#define MGAREG_BESA1CORG       0x3d10
+#define MGAREG_BESA1ORG                0x3d00
+#define MGAREG_BESCTL          0x3d20
+#define MGAREG_BESGLOBCTL      0x3dc0
+#define MGAREG_BESHCOORD       0x3d28
+#define MGAREG_BESHISCAL       0x3d30
+#define MGAREG_BESHSRCEND      0x3d3c
+#define MGAREG_BESHSRCLST      0x3d50
+#define MGAREG_BESHSRCST       0x3d38
+#define MGAREG_BESLUMACTL      0x3d40
+#define MGAREG_BESPITCH                0x3d24
+#define MGAREG_BESV1SRCLST     0x3d54
+#define MGAREG_BESV1WGHT       0x3d48
+#define MGAREG_BESVCOORD       0x3d2c
+#define MGAREG_BESVISCAL       0x3d34
+
+/* texture engine registers */
+
+#define MGAREG_TMR0            0x2c00
+#define MGAREG_TMR1            0x2c04
+#define MGAREG_TMR2            0x2c08
+#define MGAREG_TMR3            0x2c0c
+#define MGAREG_TMR4            0x2c10
+#define MGAREG_TMR5            0x2c14
+#define MGAREG_TMR6            0x2c18
+#define MGAREG_TMR7            0x2c1c
+#define MGAREG_TMR8            0x2c20
+#define MGAREG_TEXORG          0x2c24
+#define MGAREG_TEXWIDTH                0x2c28
+#define MGAREG_TEXHEIGHT       0x2c2c
+#define MGAREG_TEXCTL          0x2c30
+#    define MGA_TW4                             (0x00000000)
+#    define MGA_TW8                             (0x00000001)
+#    define MGA_TW15                            (0x00000002)
+#    define MGA_TW16                            (0x00000003)
+#    define MGA_TW12                            (0x00000004)
+#    define MGA_TW32                            (0x00000006)
+#    define MGA_TW8A                            (0x00000007)
+#    define MGA_TW8AL                           (0x00000008)
+#    define MGA_TW422                           (0x0000000A)
+#    define MGA_TW422UYVY                       (0x0000000B)
+#    define MGA_PITCHLIN                        (0x00000100)
+#    define MGA_NOPERSPECTIVE                   (0x00200000)
+#    define MGA_TAKEY                           (0x02000000)
+#    define MGA_TAMASK                          (0x04000000)
+#    define MGA_CLAMPUV                         (0x18000000)
+#    define MGA_TEXMODULATE                     (0x20000000)
+#define MGAREG_TEXCTL2         0x2c3c
+#    define MGA_G400_TC2_MAGIC                  (0x00008000)
+#    define MGA_TC2_DECALBLEND                  (0x00000001)
+#    define MGA_TC2_IDECAL                      (0x00000002)
+#    define MGA_TC2_DECALDIS                    (0x00000004)
+#    define MGA_TC2_CKSTRANSDIS                 (0x00000010)
+#    define MGA_TC2_BORDEREN                    (0x00000020)
+#    define MGA_TC2_SPECEN                      (0x00000040)
+#    define MGA_TC2_DUALTEX                     (0x00000080)
+#    define MGA_TC2_TABLEFOG                    (0x00000100)
+#    define MGA_TC2_BUMPMAP                     (0x00000200)
+#    define MGA_TC2_SELECT_TMU1                 (0x80000000)
+#define MGAREG_TEXTRANS                0x2c34
+#define MGAREG_TEXTRANSHIGH    0x2c38
+#define MGAREG_TEXFILTER       0x2c58
+#    define MGA_MIN_NRST                        (0x00000000)
+#    define MGA_MIN_BILIN                       (0x00000002)
+#    define MGA_MIN_ANISO                       (0x0000000D)
+#    define MGA_MAG_NRST                        (0x00000000)
+#    define MGA_MAG_BILIN                       (0x00000020)
+#    define MGA_FILTERALPHA                     (0x00100000)
+#define MGAREG_ALPHASTART      0x2c70
+#define MGAREG_ALPHAXINC       0x2c74
+#define MGAREG_ALPHAYINC       0x2c78
+#define MGAREG_ALPHACTRL       0x2c7c
+#    define MGA_SRC_ZERO                        (0x00000000)
+#    define MGA_SRC_ONE                         (0x00000001)
+#    define MGA_SRC_DST_COLOR                   (0x00000002)
+#    define MGA_SRC_ONE_MINUS_DST_COLOR         (0x00000003)
+#    define MGA_SRC_ALPHA                       (0x00000004)
+#    define MGA_SRC_ONE_MINUS_SRC_ALPHA         (0x00000005)
+#    define MGA_SRC_DST_ALPHA                   (0x00000006)
+#    define MGA_SRC_ONE_MINUS_DST_ALPHA         (0x00000007)
+#    define MGA_SRC_SRC_ALPHA_SATURATE          (0x00000008)
+#    define MGA_SRC_BLEND_MASK                  (0x0000000f)
+#    define MGA_DST_ZERO                        (0x00000000)
+#    define MGA_DST_ONE                         (0x00000010)
+#    define MGA_DST_SRC_COLOR                   (0x00000020)
+#    define MGA_DST_ONE_MINUS_SRC_COLOR         (0x00000030)
+#    define MGA_DST_SRC_ALPHA                   (0x00000040)
+#    define MGA_DST_ONE_MINUS_SRC_ALPHA         (0x00000050)
+#    define MGA_DST_DST_ALPHA                   (0x00000060)
+#    define MGA_DST_ONE_MINUS_DST_ALPHA         (0x00000070)
+#    define MGA_DST_BLEND_MASK                  (0x00000070)
+#    define MGA_ALPHACHANNEL                    (0x00000100)
+#    define MGA_VIDEOALPHA                      (0x00000200)
+#    define MGA_DIFFUSEDALPHA                   (0x01000000)
+#    define MGA_MODULATEDALPHA                  (0x02000000)
+#define MGAREG_TDUALSTAGE0                      (0x2CF8)
+#define MGAREG_TDUALSTAGE1                      (0x2CFC)
+#    define MGA_TDS_COLOR_ARG2_DIFFUSE          (0x00000000)
+#    define MGA_TDS_COLOR_ARG2_SPECULAR         (0x00000001)
+#    define MGA_TDS_COLOR_ARG2_FCOL             (0x00000002)
+#    define MGA_TDS_COLOR_ARG2_PREVSTAGE        (0x00000003)
+#    define MGA_TDS_COLOR_ALPHA_DIFFUSE         (0x00000000)
+#    define MGA_TDS_COLOR_ALPHA_FCOL            (0x00000004)
+#    define MGA_TDS_COLOR_ALPHA_CURRTEX         (0x00000008)
+#    define MGA_TDS_COLOR_ALPHA_PREVTEX         (0x0000000c)
+#    define MGA_TDS_COLOR_ALPHA_PREVSTAGE       (0x00000010)
+#    define MGA_TDS_COLOR_ARG1_REPLICATEALPHA   (0x00000020)
+#    define MGA_TDS_COLOR_ARG1_INV              (0x00000040)
+#    define MGA_TDS_COLOR_ARG2_REPLICATEALPHA   (0x00000080)
+#    define MGA_TDS_COLOR_ARG2_INV              (0x00000100)
+#    define MGA_TDS_COLOR_ALPHA1INV             (0x00000200)
+#    define MGA_TDS_COLOR_ALPHA2INV             (0x00000400)
+#    define MGA_TDS_COLOR_ARG1MUL_ALPHA1        (0x00000800)
+#    define MGA_TDS_COLOR_ARG2MUL_ALPHA2        (0x00001000)
+#    define MGA_TDS_COLOR_ARG1ADD_MULOUT        (0x00002000)
+#    define MGA_TDS_COLOR_ARG2ADD_MULOUT        (0x00004000)
+#    define MGA_TDS_COLOR_MODBRIGHT_2X          (0x00008000)
+#    define MGA_TDS_COLOR_MODBRIGHT_4X          (0x00010000)
+#    define MGA_TDS_COLOR_ADD_SUB               (0x00000000)
+#    define MGA_TDS_COLOR_ADD_ADD               (0x00020000)
+#    define MGA_TDS_COLOR_ADD2X                 (0x00040000)
+#    define MGA_TDS_COLOR_ADDBIAS               (0x00080000)
+#    define MGA_TDS_COLOR_BLEND                 (0x00100000)
+#    define MGA_TDS_COLOR_SEL_ARG1              (0x00000000)
+#    define MGA_TDS_COLOR_SEL_ARG2              (0x00200000)
+#    define MGA_TDS_COLOR_SEL_ADD               (0x00400000)
+#    define MGA_TDS_COLOR_SEL_MUL               (0x00600000)
+#    define MGA_TDS_ALPHA_ARG1_INV              (0x00800000)
+#    define MGA_TDS_ALPHA_ARG2_DIFFUSE          (0x00000000)
+#    define MGA_TDS_ALPHA_ARG2_FCOL             (0x01000000)
+#    define MGA_TDS_ALPHA_ARG2_PREVTEX          (0x02000000)
+#    define MGA_TDS_ALPHA_ARG2_PREVSTAGE        (0x03000000)
+#    define MGA_TDS_ALPHA_ARG2_INV              (0x04000000)
+#    define MGA_TDS_ALPHA_ADD                   (0x08000000)
+#    define MGA_TDS_ALPHA_ADDBIAS               (0x10000000)
+#    define MGA_TDS_ALPHA_ADD2X                 (0x20000000)
+#    define MGA_TDS_ALPHA_SEL_ARG1              (0x00000000)
+#    define MGA_TDS_ALPHA_SEL_ARG2              (0x40000000)
+#    define MGA_TDS_ALPHA_SEL_ADD               (0x80000000)
+#    define MGA_TDS_ALPHA_SEL_MUL               (0xc0000000)
+
+#define MGAREG_DWGSYNC         0x2c4c
+
+#define MGAREG_AGP_PLL         0x1e4c
+#define MGA_AGP2XPLL_ENABLE            0x1
+#define MGA_AGP2XPLL_DISABLE           0x0
+
+#endif
diff --git a/drivers/gpu/drm/mgag200/mgag200_ttm.c b/drivers/gpu/drm/mgag200/mgag200_ttm.c
new file mode 100644 (file)
index 0000000..1226479
--- /dev/null
@@ -0,0 +1,452 @@
+/*
+ * Copyright 2012 Red Hat Inc.
+ *
+ * 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, sub license, 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 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 NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS 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.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ */
+/*
+ * Authors: Dave Airlie <airlied@redhat.com>
+ */
+#include "drmP.h"
+#include "mgag200_drv.h"
+#include <ttm/ttm_page_alloc.h>
+
+static inline struct mga_device *
+mgag200_bdev(struct ttm_bo_device *bd)
+{
+       return container_of(bd, struct mga_device, ttm.bdev);
+}
+
+static int
+mgag200_ttm_mem_global_init(struct drm_global_reference *ref)
+{
+       return ttm_mem_global_init(ref->object);
+}
+
+static void
+mgag200_ttm_mem_global_release(struct drm_global_reference *ref)
+{
+       ttm_mem_global_release(ref->object);
+}
+
+static int mgag200_ttm_global_init(struct mga_device *ast)
+{
+       struct drm_global_reference *global_ref;
+       int r;
+
+       global_ref = &ast->ttm.mem_global_ref;
+       global_ref->global_type = DRM_GLOBAL_TTM_MEM;
+       global_ref->size = sizeof(struct ttm_mem_global);
+       global_ref->init = &mgag200_ttm_mem_global_init;
+       global_ref->release = &mgag200_ttm_mem_global_release;
+       r = drm_global_item_ref(global_ref);
+       if (r != 0) {
+               DRM_ERROR("Failed setting up TTM memory accounting "
+                         "subsystem.\n");
+               return r;
+       }
+
+       ast->ttm.bo_global_ref.mem_glob =
+               ast->ttm.mem_global_ref.object;
+       global_ref = &ast->ttm.bo_global_ref.ref;
+       global_ref->global_type = DRM_GLOBAL_TTM_BO;
+       global_ref->size = sizeof(struct ttm_bo_global);
+       global_ref->init = &ttm_bo_global_init;
+       global_ref->release = &ttm_bo_global_release;
+       r = drm_global_item_ref(global_ref);
+       if (r != 0) {
+               DRM_ERROR("Failed setting up TTM BO subsystem.\n");
+               drm_global_item_unref(&ast->ttm.mem_global_ref);
+               return r;
+       }
+       return 0;
+}
+
+void
+mgag200_ttm_global_release(struct mga_device *ast)
+{
+       if (ast->ttm.mem_global_ref.release == NULL)
+               return;
+
+       drm_global_item_unref(&ast->ttm.bo_global_ref.ref);
+       drm_global_item_unref(&ast->ttm.mem_global_ref);
+       ast->ttm.mem_global_ref.release = NULL;
+}
+
+
+static void mgag200_bo_ttm_destroy(struct ttm_buffer_object *tbo)
+{
+       struct mgag200_bo *bo;
+
+       bo = container_of(tbo, struct mgag200_bo, bo);
+
+       drm_gem_object_release(&bo->gem);
+       kfree(bo);
+}
+
+bool mgag200_ttm_bo_is_mgag200_bo(struct ttm_buffer_object *bo)
+{
+       if (bo->destroy == &mgag200_bo_ttm_destroy)
+               return true;
+       return false;
+}
+
+static int
+mgag200_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
+                    struct ttm_mem_type_manager *man)
+{
+       switch (type) {
+       case TTM_PL_SYSTEM:
+               man->flags = TTM_MEMTYPE_FLAG_MAPPABLE;
+               man->available_caching = TTM_PL_MASK_CACHING;
+               man->default_caching = TTM_PL_FLAG_CACHED;
+               break;
+       case TTM_PL_VRAM:
+               man->func = &ttm_bo_manager_func;
+               man->flags = TTM_MEMTYPE_FLAG_FIXED |
+                       TTM_MEMTYPE_FLAG_MAPPABLE;
+               man->available_caching = TTM_PL_FLAG_UNCACHED |
+                       TTM_PL_FLAG_WC;
+               man->default_caching = TTM_PL_FLAG_WC;
+               break;
+       default:
+               DRM_ERROR("Unsupported memory type %u\n", (unsigned)type);
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static void
+mgag200_bo_evict_flags(struct ttm_buffer_object *bo, struct ttm_placement *pl)
+{
+       struct mgag200_bo *mgabo = mgag200_bo(bo);
+
+       if (!mgag200_ttm_bo_is_mgag200_bo(bo))
+               return;
+
+       mgag200_ttm_placement(mgabo, TTM_PL_FLAG_SYSTEM);
+       *pl = mgabo->placement;
+}
+
+static int mgag200_bo_verify_access(struct ttm_buffer_object *bo, struct file *filp)
+{
+       return 0;
+}
+
+static int mgag200_ttm_io_mem_reserve(struct ttm_bo_device *bdev,
+                                 struct ttm_mem_reg *mem)
+{
+       struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type];
+       struct mga_device *mdev = mgag200_bdev(bdev);
+
+       mem->bus.addr = NULL;
+       mem->bus.offset = 0;
+       mem->bus.size = mem->num_pages << PAGE_SHIFT;
+       mem->bus.base = 0;
+       mem->bus.is_iomem = false;
+       if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE))
+               return -EINVAL;
+       switch (mem->mem_type) {
+       case TTM_PL_SYSTEM:
+               /* system memory */
+               return 0;
+       case TTM_PL_VRAM:
+               mem->bus.offset = mem->start << PAGE_SHIFT;
+               mem->bus.base = pci_resource_start(mdev->dev->pdev, 0);
+               mem->bus.is_iomem = true;
+               break;
+       default:
+               return -EINVAL;
+               break;
+       }
+       return 0;
+}
+
+static void mgag200_ttm_io_mem_free(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem)
+{
+}
+
+static int mgag200_bo_move(struct ttm_buffer_object *bo,
+                      bool evict, bool interruptible,
+                      bool no_wait_reserve, bool no_wait_gpu,
+                      struct ttm_mem_reg *new_mem)
+{
+       int r;
+       r = ttm_bo_move_memcpy(bo, evict, no_wait_reserve, no_wait_gpu, new_mem);
+       return r;
+}
+
+
+static void mgag200_ttm_backend_destroy(struct ttm_tt *tt)
+{
+       ttm_tt_fini(tt);
+       kfree(tt);
+}
+
+static struct ttm_backend_func mgag200_tt_backend_func = {
+       .destroy = &mgag200_ttm_backend_destroy,
+};
+
+
+struct ttm_tt *mgag200_ttm_tt_create(struct ttm_bo_device *bdev,
+                                unsigned long size, uint32_t page_flags,
+                                struct page *dummy_read_page)
+{
+       struct ttm_tt *tt;
+
+       tt = kzalloc(sizeof(struct ttm_tt), GFP_KERNEL);
+       if (tt == NULL)
+               return NULL;
+       tt->func = &mgag200_tt_backend_func;
+       if (ttm_tt_init(tt, bdev, size, page_flags, dummy_read_page)) {
+               kfree(tt);
+               return NULL;
+       }
+       return tt;
+}
+
+static int mgag200_ttm_tt_populate(struct ttm_tt *ttm)
+{
+       return ttm_pool_populate(ttm);
+}
+
+static void mgag200_ttm_tt_unpopulate(struct ttm_tt *ttm)
+{
+       ttm_pool_unpopulate(ttm);
+}
+
+struct ttm_bo_driver mgag200_bo_driver = {
+       .ttm_tt_create = mgag200_ttm_tt_create,
+       .ttm_tt_populate = mgag200_ttm_tt_populate,
+       .ttm_tt_unpopulate = mgag200_ttm_tt_unpopulate,
+       .init_mem_type = mgag200_bo_init_mem_type,
+       .evict_flags = mgag200_bo_evict_flags,
+       .move = mgag200_bo_move,
+       .verify_access = mgag200_bo_verify_access,
+       .io_mem_reserve = &mgag200_ttm_io_mem_reserve,
+       .io_mem_free = &mgag200_ttm_io_mem_free,
+};
+
+int mgag200_mm_init(struct mga_device *mdev)
+{
+       int ret;
+       struct drm_device *dev = mdev->dev;
+       struct ttm_bo_device *bdev = &mdev->ttm.bdev;
+
+       ret = mgag200_ttm_global_init(mdev);
+       if (ret)
+               return ret;
+
+       ret = ttm_bo_device_init(&mdev->ttm.bdev,
+                                mdev->ttm.bo_global_ref.ref.object,
+                                &mgag200_bo_driver, DRM_FILE_PAGE_OFFSET,
+                                true);
+       if (ret) {
+               DRM_ERROR("Error initialising bo driver; %d\n", ret);
+               return ret;
+       }
+
+       ret = ttm_bo_init_mm(bdev, TTM_PL_VRAM, mdev->mc.vram_size >> PAGE_SHIFT);
+       if (ret) {
+               DRM_ERROR("Failed ttm VRAM init: %d\n", ret);
+               return ret;
+       }
+
+       mdev->fb_mtrr = drm_mtrr_add(pci_resource_start(dev->pdev, 0),
+                                   pci_resource_len(dev->pdev, 0),
+                                   DRM_MTRR_WC);
+
+       return 0;
+}
+
+void mgag200_mm_fini(struct mga_device *mdev)
+{
+       struct drm_device *dev = mdev->dev;
+       ttm_bo_device_release(&mdev->ttm.bdev);
+
+       mgag200_ttm_global_release(mdev);
+
+       if (mdev->fb_mtrr >= 0) {
+               drm_mtrr_del(mdev->fb_mtrr,
+                            pci_resource_start(dev->pdev, 0),
+                            pci_resource_len(dev->pdev, 0), DRM_MTRR_WC);
+               mdev->fb_mtrr = -1;
+       }
+}
+
+void mgag200_ttm_placement(struct mgag200_bo *bo, int domain)
+{
+       u32 c = 0;
+       bo->placement.fpfn = 0;
+       bo->placement.lpfn = 0;
+       bo->placement.placement = bo->placements;
+       bo->placement.busy_placement = bo->placements;
+       if (domain & TTM_PL_FLAG_VRAM)
+               bo->placements[c++] = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_VRAM;
+       if (domain & TTM_PL_FLAG_SYSTEM)
+               bo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
+       if (!c)
+               bo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
+       bo->placement.num_placement = c;
+       bo->placement.num_busy_placement = c;
+}
+
+int mgag200_bo_reserve(struct mgag200_bo *bo, bool no_wait)
+{
+       int ret;
+
+       ret = ttm_bo_reserve(&bo->bo, true, no_wait, false, 0);
+       if (ret) {
+               if (ret != -ERESTARTSYS)
+                       DRM_ERROR("reserve failed %p\n", bo);
+               return ret;
+       }
+       return 0;
+}
+
+void mgag200_bo_unreserve(struct mgag200_bo *bo)
+{
+       ttm_bo_unreserve(&bo->bo);
+}
+
+int mgag200_bo_create(struct drm_device *dev, int size, int align,
+                 uint32_t flags, struct mgag200_bo **pmgabo)
+{
+       struct mga_device *mdev = dev->dev_private;
+       struct mgag200_bo *mgabo;
+       size_t acc_size;
+       int ret;
+
+       mgabo = kzalloc(sizeof(struct mgag200_bo), GFP_KERNEL);
+       if (!mgabo)
+               return -ENOMEM;
+
+       ret = drm_gem_object_init(dev, &mgabo->gem, size);
+       if (ret) {
+               kfree(mgabo);
+               return ret;
+       }
+
+       mgabo->gem.driver_private = NULL;
+       mgabo->bo.bdev = &mdev->ttm.bdev;
+
+       mgag200_ttm_placement(mgabo, TTM_PL_FLAG_VRAM | TTM_PL_FLAG_SYSTEM);
+
+       acc_size = ttm_bo_dma_acc_size(&mdev->ttm.bdev, size,
+                                      sizeof(struct mgag200_bo));
+
+       ret = ttm_bo_init(&mdev->ttm.bdev, &mgabo->bo, size,
+                         ttm_bo_type_device, &mgabo->placement,
+                         align >> PAGE_SHIFT, 0, false, NULL, acc_size,
+                         mgag200_bo_ttm_destroy);
+       if (ret)
+               return ret;
+
+       *pmgabo = mgabo;
+       return 0;
+}
+
+static inline u64 mgag200_bo_gpu_offset(struct mgag200_bo *bo)
+{
+       return bo->bo.offset;
+}
+
+int mgag200_bo_pin(struct mgag200_bo *bo, u32 pl_flag, u64 *gpu_addr)
+{
+       int i, ret;
+
+       if (bo->pin_count) {
+               bo->pin_count++;
+               if (gpu_addr)
+                       *gpu_addr = mgag200_bo_gpu_offset(bo);
+       }
+
+       mgag200_ttm_placement(bo, pl_flag);
+       for (i = 0; i < bo->placement.num_placement; i++)
+               bo->placements[i] |= TTM_PL_FLAG_NO_EVICT;
+       ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false, false);
+       if (ret)
+               return ret;
+
+       bo->pin_count = 1;
+       if (gpu_addr)
+               *gpu_addr = mgag200_bo_gpu_offset(bo);
+       return 0;
+}
+
+int mgag200_bo_unpin(struct mgag200_bo *bo)
+{
+       int i, ret;
+       if (!bo->pin_count) {
+               DRM_ERROR("unpin bad %p\n", bo);
+               return 0;
+       }
+       bo->pin_count--;
+       if (bo->pin_count)
+               return 0;
+
+       for (i = 0; i < bo->placement.num_placement ; i++)
+               bo->placements[i] &= ~TTM_PL_FLAG_NO_EVICT;
+       ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false, false);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+int mgag200_bo_push_sysram(struct mgag200_bo *bo)
+{
+       int i, ret;
+       if (!bo->pin_count) {
+               DRM_ERROR("unpin bad %p\n", bo);
+               return 0;
+       }
+       bo->pin_count--;
+       if (bo->pin_count)
+               return 0;
+
+       if (bo->kmap.virtual)
+               ttm_bo_kunmap(&bo->kmap);
+
+       mgag200_ttm_placement(bo, TTM_PL_FLAG_SYSTEM);
+       for (i = 0; i < bo->placement.num_placement ; i++)
+               bo->placements[i] |= TTM_PL_FLAG_NO_EVICT;
+
+       ret = ttm_bo_validate(&bo->bo, &bo->placement, false, false, false);
+       if (ret) {
+               DRM_ERROR("pushing to VRAM failed\n");
+               return ret;
+       }
+       return 0;
+}
+
+int mgag200_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+       struct drm_file *file_priv;
+       struct mga_device *mdev;
+
+       if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET))
+               return drm_mmap(filp, vma);
+
+       file_priv = filp->private_data;
+       mdev = file_priv->minor->dev->dev_private;
+       return ttm_bo_mmap(filp, vma, &mdev->ttm.bdev);
+}