diff options
Diffstat (limited to 'drivers/gpu/drm/mgag200')
-rw-r--r-- | drivers/gpu/drm/mgag200/Kconfig | 15 | ||||
-rw-r--r-- | drivers/gpu/drm/mgag200/Makefile | 5 | ||||
-rw-r--r-- | drivers/gpu/drm/mgag200/mgag200_drv.c | 116 | ||||
-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 |
10 files changed, 3896 insertions, 0 deletions
diff --git a/drivers/gpu/drm/mgag200/Kconfig b/drivers/gpu/drm/mgag200/Kconfig new file mode 100644 index 00000000000..d63013497f6 --- /dev/null +++ b/drivers/gpu/drm/mgag200/Kconfig @@ -0,0 +1,15 @@ +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 + select DRM_TTM + 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..3c8e04f5471 --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_drv.c @@ -0,0 +1,116 @@ +/* + * 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) +{ +#ifdef CONFIG_VGA_CONSOLE + if (vgacon_text_force() && mgag200_modeset == -1) + return -EINVAL; +#endif + + 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 |