diff options
-rw-r--r-- | drivers/gpu/drm/Kconfig | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/mgag200/Kconfig | 14 | ||||
-rw-r--r-- | drivers/gpu/drm/mgag200/Makefile | 5 | ||||
-rw-r--r-- | drivers/gpu/drm/mgag200/mgag200_drv.c | 114 | ||||
-rw-r--r-- | drivers/gpu/drm/mgag200/mgag200_drv.h | 276 | ||||
-rw-r--r-- | drivers/gpu/drm/mgag200/mgag200_fb.c | 294 | ||||
-rw-r--r-- | drivers/gpu/drm/mgag200/mgag200_i2c.c | 156 | ||||
-rw-r--r-- | drivers/gpu/drm/mgag200/mgag200_main.c | 388 | ||||
-rw-r--r-- | drivers/gpu/drm/mgag200/mgag200_mode.c | 1533 | ||||
-rw-r--r-- | drivers/gpu/drm/mgag200/mgag200_reg.h | 661 | ||||
-rw-r--r-- | drivers/gpu/drm/mgag200/mgag200_ttm.c | 452 |
12 files changed, 3895 insertions, 0 deletions
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 9c6244c1fff..8b23bd9d165 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -189,3 +189,4 @@ source "drivers/gpu/drm/udl/Kconfig" source "drivers/gpu/drm/ast/Kconfig" +source "drivers/gpu/drm/mgag200/Kconfig" diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index fb3eac8402f..52c9b5610ca 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -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 index 00000000000..ffffafa1d6d --- /dev/null +++ b/drivers/gpu/drm/mgag200/Kconfig @@ -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 index 00000000000..7db592eedbf --- /dev/null +++ b/drivers/gpu/drm/mgag200/Makefile @@ -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 index 00000000000..f03a636d769 --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_drv.c @@ -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 index 00000000000..6f13b356323 --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_drv.h @@ -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 index 00000000000..880d3369760 --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_fb.c @@ -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 index 00000000000..dd3568a1b6b --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_i2c.c @@ -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 index 00000000000..636a81cd2f3 --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_main.c @@ -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 index 00000000000..d303061b251 --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_mode.c @@ -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 index 00000000000..fb24d8655fe --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_reg.h @@ -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 index 00000000000..12264793108 --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_ttm.c @@ -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); +} |