diff options
Diffstat (limited to 'drivers/gpu/drm/mgag200')
| -rw-r--r-- | drivers/gpu/drm/mgag200/Kconfig | 16 | ||||
| -rw-r--r-- | drivers/gpu/drm/mgag200/Makefile | 5 | ||||
| -rw-r--r-- | drivers/gpu/drm/mgag200/mgag200_cursor.c | 277 | ||||
| -rw-r--r-- | drivers/gpu/drm/mgag200/mgag200_drv.c | 137 | ||||
| -rw-r--r-- | drivers/gpu/drm/mgag200/mgag200_drv.h | 310 | ||||
| -rw-r--r-- | drivers/gpu/drm/mgag200/mgag200_fb.c | 320 | ||||
| -rw-r--r-- | drivers/gpu/drm/mgag200/mgag200_i2c.c | 156 | ||||
| -rw-r--r-- | drivers/gpu/drm/mgag200/mgag200_main.c | 367 | ||||
| -rw-r--r-- | drivers/gpu/drm/mgag200/mgag200_mode.c | 1675 | ||||
| -rw-r--r-- | drivers/gpu/drm/mgag200/mgag200_reg.h | 665 | ||||
| -rw-r--r-- | drivers/gpu/drm/mgag200/mgag200_ttm.c | 432 | 
11 files changed, 4360 insertions, 0 deletions
diff --git a/drivers/gpu/drm/mgag200/Kconfig b/drivers/gpu/drm/mgag200/Kconfig new file mode 100644 index 00000000000..3a1c5fbae54 --- /dev/null +++ b/drivers/gpu/drm/mgag200/Kconfig @@ -0,0 +1,16 @@ +config DRM_MGAG200 +	tristate "Kernel modesetting driver for MGA G200 server engines" +	depends on DRM && PCI +	select FB_SYS_FILLRECT +	select FB_SYS_COPYAREA +	select FB_SYS_IMAGEBLIT +	select DRM_KMS_HELPER +	select DRM_KMS_FB_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..a9a0300f09f --- /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_cursor.o \ +	mgag200_drv.o mgag200_fb.o mgag200_i2c.o mgag200_ttm.o + +obj-$(CONFIG_DRM_MGAG200) += mgag200.o diff --git a/drivers/gpu/drm/mgag200/mgag200_cursor.c b/drivers/gpu/drm/mgag200/mgag200_cursor.c new file mode 100644 index 00000000000..9f9780b7ddf --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_cursor.c @@ -0,0 +1,277 @@ +/* + * Copyright 2013 Matrox Graphics + * + * This file is subject to the terms and conditions of the GNU General + * Public License version 2. See the file COPYING in the main + * directory of this archive for more details. + * + * Author: Christopher Harvey <charvey@matrox.com> + */ + +#include <drm/drmP.h> +#include "mgag200_drv.h" + +static bool warn_transparent = true; +static bool warn_palette = true; + +/* +  Hide the cursor off screen. We can't disable the cursor hardware because it +  takes too long to re-activate and causes momentary corruption +*/ +static void mga_hide_cursor(struct mga_device *mdev) +{ +	WREG8(MGA_CURPOSXL, 0); +	WREG8(MGA_CURPOSXH, 0); +	if (mdev->cursor.pixels_1->pin_count) +		mgag200_bo_unpin(mdev->cursor.pixels_1); +	if (mdev->cursor.pixels_2->pin_count) +		mgag200_bo_unpin(mdev->cursor.pixels_2); +} + +int mga_crtc_cursor_set(struct drm_crtc *crtc, +			struct drm_file *file_priv, +			uint32_t handle, +			uint32_t width, +			uint32_t height) +{ +	struct drm_device *dev = crtc->dev; +	struct mga_device *mdev = (struct mga_device *)dev->dev_private; +	struct mgag200_bo *pixels_1 = mdev->cursor.pixels_1; +	struct mgag200_bo *pixels_2 = mdev->cursor.pixels_2; +	struct mgag200_bo *pixels_current = mdev->cursor.pixels_current; +	struct mgag200_bo *pixels_prev = mdev->cursor.pixels_prev; +	struct drm_gem_object *obj; +	struct mgag200_bo *bo = NULL; +	int ret = 0; +	unsigned int i, row, col; +	uint32_t colour_set[16]; +	uint32_t *next_space = &colour_set[0]; +	uint32_t *palette_iter; +	uint32_t this_colour; +	bool found = false; +	int colour_count = 0; +	u64 gpu_addr; +	u8 reg_index; +	u8 this_row[48]; + +	if (!pixels_1 || !pixels_2) { +		WREG8(MGA_CURPOSXL, 0); +		WREG8(MGA_CURPOSXH, 0); +		return -ENOTSUPP; /* Didn't allocate space for cursors */ +	} + +	if ((width != 64 || height != 64) && handle) { +		WREG8(MGA_CURPOSXL, 0); +		WREG8(MGA_CURPOSXH, 0); +		return -EINVAL; +	} + +	BUG_ON(pixels_1 != pixels_current && pixels_1 != pixels_prev); +	BUG_ON(pixels_2 != pixels_current && pixels_2 != pixels_prev); +	BUG_ON(pixels_current == pixels_prev); + +	ret = mgag200_bo_reserve(pixels_1, true); +	if (ret) { +		WREG8(MGA_CURPOSXL, 0); +		WREG8(MGA_CURPOSXH, 0); +		return ret; +	} +	ret = mgag200_bo_reserve(pixels_2, true); +	if (ret) { +		WREG8(MGA_CURPOSXL, 0); +		WREG8(MGA_CURPOSXH, 0); +		mgag200_bo_unreserve(pixels_1); +		return ret; +	} + +	if (!handle) { +		mga_hide_cursor(mdev); +		ret = 0; +		goto out1; +	} + +	/* Move cursor buffers into VRAM if they aren't already */ +	if (!pixels_1->pin_count) { +		ret = mgag200_bo_pin(pixels_1, TTM_PL_FLAG_VRAM, +				     &mdev->cursor.pixels_1_gpu_addr); +		if (ret) +			goto out1; +	} +	if (!pixels_2->pin_count) { +		ret = mgag200_bo_pin(pixels_2, TTM_PL_FLAG_VRAM, +				     &mdev->cursor.pixels_2_gpu_addr); +		if (ret) { +			mgag200_bo_unpin(pixels_1); +			goto out1; +		} +	} + +	mutex_lock(&dev->struct_mutex); +	obj = drm_gem_object_lookup(dev, file_priv, handle); +	if (!obj) { +		mutex_unlock(&dev->struct_mutex); +		ret = -ENOENT; +		goto out1; +	} +	drm_gem_object_unreference(obj); +	mutex_unlock(&dev->struct_mutex); + +	bo = gem_to_mga_bo(obj); +	ret = mgag200_bo_reserve(bo, true); +	if (ret) { +		dev_err(&dev->pdev->dev, "failed to reserve user bo\n"); +		goto out1; +	} +	if (!bo->kmap.virtual) { +		ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap); +		if (ret) { +			dev_err(&dev->pdev->dev, "failed to kmap user buffer updates\n"); +			goto out2; +		} +	} + +	memset(&colour_set[0], 0, sizeof(uint32_t)*16); +	/* width*height*4 = 16384 */ +	for (i = 0; i < 16384; i += 4) { +		this_colour = ioread32(bo->kmap.virtual + i); +		/* No transparency */ +		if (this_colour>>24 != 0xff && +			this_colour>>24 != 0x0) { +			if (warn_transparent) { +				dev_info(&dev->pdev->dev, "Video card doesn't support cursors with partial transparency.\n"); +				dev_info(&dev->pdev->dev, "Not enabling hardware cursor.\n"); +				warn_transparent = false; /* Only tell the user once. */ +			} +			ret = -EINVAL; +			goto out3; +		} +		/* Don't need to store transparent pixels as colours */ +		if (this_colour>>24 == 0x0) +			continue; +		found = false; +		for (palette_iter = &colour_set[0]; palette_iter != next_space; palette_iter++) { +			if (*palette_iter == this_colour) { +				found = true; +				break; +			} +		} +		if (found) +			continue; +		/* We only support 4bit paletted cursors */ +		if (colour_count >= 16) { +			if (warn_palette) { +				dev_info(&dev->pdev->dev, "Video card only supports cursors with up to 16 colours.\n"); +				dev_info(&dev->pdev->dev, "Not enabling hardware cursor.\n"); +				warn_palette = false; /* Only tell the user once. */ +			} +			ret = -EINVAL; +			goto out3; +		} +		*next_space = this_colour; +		next_space++; +		colour_count++; +	} + +	/* Program colours from cursor icon into palette */ +	for (i = 0; i < colour_count; i++) { +		if (i <= 2) +			reg_index = 0x8 + i*0x4; +		else +			reg_index = 0x60 + i*0x3; +		WREG_DAC(reg_index, colour_set[i] & 0xff); +		WREG_DAC(reg_index+1, colour_set[i]>>8 & 0xff); +		WREG_DAC(reg_index+2, colour_set[i]>>16 & 0xff); +		BUG_ON((colour_set[i]>>24 & 0xff) != 0xff); +	} + +	/* Map up-coming buffer to write colour indices */ +	if (!pixels_prev->kmap.virtual) { +		ret = ttm_bo_kmap(&pixels_prev->bo, 0, +				  pixels_prev->bo.num_pages, +				  &pixels_prev->kmap); +		if (ret) { +			dev_err(&dev->pdev->dev, "failed to kmap cursor updates\n"); +			goto out3; +		} +	} + +	/* now write colour indices into hardware cursor buffer */ +	for (row = 0; row < 64; row++) { +		memset(&this_row[0], 0, 48); +		for (col = 0; col < 64; col++) { +			this_colour = ioread32(bo->kmap.virtual + 4*(col + 64*row)); +			/* write transparent pixels */ +			if (this_colour>>24 == 0x0) { +				this_row[47 - col/8] |= 0x80>>(col%8); +				continue; +			} + +			/* write colour index here */ +			for (i = 0; i < colour_count; i++) { +				if (colour_set[i] == this_colour) { +					if (col % 2) +						this_row[col/2] |= i<<4; +					else +						this_row[col/2] |= i; +					break; +				} +			} +		} +		memcpy_toio(pixels_prev->kmap.virtual + row*48, &this_row[0], 48); +	} + +	/* Program gpu address of cursor buffer */ +	if (pixels_prev == pixels_1) +		gpu_addr = mdev->cursor.pixels_1_gpu_addr; +	else +		gpu_addr = mdev->cursor.pixels_2_gpu_addr; +	WREG_DAC(MGA1064_CURSOR_BASE_ADR_LOW, (u8)((gpu_addr>>10) & 0xff)); +	WREG_DAC(MGA1064_CURSOR_BASE_ADR_HI, (u8)((gpu_addr>>18) & 0x3f)); + +	/* Adjust cursor control register to turn on the cursor */ +	WREG_DAC(MGA1064_CURSOR_CTL, 4); /* 16-colour palletized cursor mode */ + +	/* Now swap internal buffer pointers */ +	if (mdev->cursor.pixels_1 == mdev->cursor.pixels_prev) { +		mdev->cursor.pixels_prev = mdev->cursor.pixels_2; +		mdev->cursor.pixels_current = mdev->cursor.pixels_1; +	} else if (mdev->cursor.pixels_1 == mdev->cursor.pixels_current) { +		mdev->cursor.pixels_prev = mdev->cursor.pixels_1; +		mdev->cursor.pixels_current = mdev->cursor.pixels_2; +	} else { +		BUG(); +	} +	ret = 0; + +	ttm_bo_kunmap(&pixels_prev->kmap); + out3: +	ttm_bo_kunmap(&bo->kmap); + out2: +	mgag200_bo_unreserve(bo); + out1: +	if (ret) +		mga_hide_cursor(mdev); +	mgag200_bo_unreserve(pixels_1); +	mgag200_bo_unreserve(pixels_2); +	return ret; +} + +int mga_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) +{ +	struct mga_device *mdev = (struct mga_device *)crtc->dev->dev_private; +	/* Our origin is at (64,64) */ +	x += 64; +	y += 64; + +	BUG_ON(x <= 0); +	BUG_ON(y <= 0); +	BUG_ON(x & ~0xffff); +	BUG_ON(y & ~0xffff); + +	WREG8(MGA_CURPOSXL, x & 0xff); +	WREG8(MGA_CURPOSXH, (x>>8) & 0xff); + +	WREG8(MGA_CURPOSYL, y & 0xff); +	WREG8(MGA_CURPOSYH, (y>>8) & 0xff); +	return 0; +} diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c new file mode 100644 index 00000000000..f15ea3c4a90 --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_drv.c @@ -0,0 +1,137 @@ +/* + * 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 <drm/drmP.h> + +#include "mgag200_drv.h" + +#include <drm/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 void mgag200_kick_out_firmware_fb(struct pci_dev *pdev) +{ +	struct apertures_struct *ap; +	bool primary = false; + +	ap = alloc_apertures(1); +	if (!ap) +		return; + +	ap->ranges[0].base = pci_resource_start(pdev, 0); +	ap->ranges[0].size = pci_resource_len(pdev, 0); + +#ifdef CONFIG_X86 +	primary = pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW; +#endif +	remove_conflicting_framebuffers(ap, "mgag200drmfb", primary); +	kfree(ap); +} + + +static int mga_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ +	mgag200_kick_out_firmware_fb(pdev); + +	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, +#ifdef CONFIG_COMPAT +	.compat_ioctl = drm_compat_ioctl, +#endif +	.read = drm_read, +}; + +static struct drm_driver driver = { +	.driver_features = DRIVER_GEM | DRIVER_MODESET, +	.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_free_object = mgag200_gem_free_object, +	.dumb_create = mgag200_dumb_create, +	.dumb_map_offset = mgag200_dumb_mmap_offset, +	.dumb_destroy = drm_gem_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..cf11ee68a6d --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_drv.h @@ -0,0 +1,310 @@ +/* + * 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 <drm/ttm/ttm_bo_api.h> +#include <drm/ttm/ttm_bo_driver.h> +#include <drm/ttm/ttm_placement.h> +#include <drm/ttm/ttm_memory.h> +#include <drm/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; +	void *sysram; +	int size; +	struct ttm_bo_kmap_obj mapping; +	int x1, y1, x2, y2; /* dirty rect */ +	spinlock_t dirty_lock; +}; + +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_cursor { +	/* +	   We have to have 2 buffers for the cursor to avoid occasional +	   corruption while switching cursor icons. +	   If either of these is NULL, then don't do hardware cursors, and +	   fall back to software. +	*/ +	struct mgag200_bo *pixels_1; +	struct mgag200_bo *pixels_2; +	u64 pixels_1_gpu_addr, pixels_2_gpu_addr; +	/* The currently displayed icon, this points to one of pixels_1, or pixels_2 */ +	struct mgag200_bo *pixels_current; +	/* The previously displayed icon */ +	struct mgag200_bo *pixels_prev; +}; + +struct mga_mc { +	resource_size_t			vram_size; +	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; +	struct mga_cursor cursor; + +	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; +	} ttm; + +	/* SE model number stored in reg 0x1e24 */ +	u32 unique_rev_id; +}; + + +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); +} +				/* mgag200_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); + +				/* mgag200_fb.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_dumb_create(struct drm_file *file, +			struct drm_device *dev, +			struct drm_mode_create_dumb *args); +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); +				/* mgag200_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); + +static inline 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 && ret != -EBUSY) +			DRM_ERROR("reserve failed %p\n", bo); +		return ret; +	} +	return 0; +} + +static inline 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 **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); +			   /* mgag200_cursor.c */ +int mga_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, +						uint32_t handle, uint32_t width, uint32_t height); +int mga_crtc_cursor_move(struct drm_crtc *crtc, int x, int y); + +#endif				/* __MGAG200_DRV_H__ */ diff --git a/drivers/gpu/drm/mgag200/mgag200_fb.c b/drivers/gpu/drm/mgag200/mgag200_fb.c new file mode 100644 index 00000000000..13b7dd83faa --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_fb.c @@ -0,0 +1,320 @@ +/* + * 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 <drm/drmP.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_crtc_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 = -EBUSY; +	bool unmap = false; +	bool store_for_later = false; +	int x2, y2; +	unsigned long flags; + +	obj = mfbdev->mfb.obj; +	bo = gem_to_mga_bo(obj); + +	/* +	 * try and reserve the BO, if we fail with busy +	 * then the BO is being moved and we should +	 * store up the damage until later. +	 */ +	if (drm_can_sleep()) +		ret = mgag200_bo_reserve(bo, true); +	if (ret) { +		if (ret != -EBUSY) +			return; + +		store_for_later = true; +	} + +	x2 = x + width - 1; +	y2 = y + height - 1; +	spin_lock_irqsave(&mfbdev->dirty_lock, flags); + +	if (mfbdev->y1 < y) +		y = mfbdev->y1; +	if (mfbdev->y2 > y2) +		y2 = mfbdev->y2; +	if (mfbdev->x1 < x) +		x = mfbdev->x1; +	if (mfbdev->x2 > x2) +		x2 = mfbdev->x2; + +	if (store_for_later) { +		mfbdev->x1 = x; +		mfbdev->x2 = x2; +		mfbdev->y1 = y; +		mfbdev->y2 = y2; +		spin_unlock_irqrestore(&mfbdev->dirty_lock, flags); +		return; +	} + +	mfbdev->x1 = mfbdev->y1 = INT_MAX; +	mfbdev->x2 = mfbdev->y2 = 0; +	spin_unlock_irqrestore(&mfbdev->dirty_lock, flags); + +	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 <= y2; 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, (x2 - x + 1) * 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 size; +	struct drm_gem_object *gobj; +	int ret = 0; + +	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 drm_fb_helper *helper, +			   struct drm_fb_helper_surface_size *sizes) +{ +	struct mga_fbdev *mfbdev = (struct mga_fbdev *)helper; +	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_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_unregister_private(&mfb->base); +	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 = mgag200fb_create, +}; + +int mgag200_fbdev_init(struct mga_device *mdev) +{ +	struct mga_fbdev *mfbdev; +	int ret; +	int bpp_sel = 32; + +	/* prefer 16bpp on low end gpus with limited VRAM */ +	if (IS_G200_SE(mdev) && mdev->mc.vram_size < (2048*1024)) +		bpp_sel = 16; + +	mfbdev = devm_kzalloc(mdev->dev->dev, sizeof(struct mga_fbdev), GFP_KERNEL); +	if (!mfbdev) +		return -ENOMEM; + +	mdev->mfbdev = mfbdev; +	mfbdev->helper.funcs = &mga_fb_helper_funcs; +	spin_lock_init(&mfbdev->dirty_lock); + +	ret = drm_fb_helper_init(mdev->dev, &mfbdev->helper, +				 mdev->num_crtc, MGAG200FB_CONN_LIMIT); +	if (ret) +		return ret; + +	drm_fb_helper_single_add_all_connectors(&mfbdev->helper); + +	/* disable all the possible outputs/crtcs before entering KMS mode */ +	drm_helper_disable_unused_functions(mdev->dev); + +	drm_fb_helper_initial_config(&mfbdev->helper, bpp_sel); + +	return 0; +} + +void mgag200_fbdev_fini(struct mga_device *mdev) +{ +	if (!mdev->mfbdev) +		return; + +	mga_fbdev_destroy(mdev->dev, mdev->mfbdev); +} diff --git a/drivers/gpu/drm/mgag200/mgag200_i2c.c b/drivers/gpu/drm/mgag200/mgag200_i2c.c new file mode 100644 index 00000000000..d3dcf54e623 --- /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 <drm/drmP.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_CTL2, 1); +	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..f6b283b8375 --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_main.c @@ -0,0 +1,367 @@ +/* + * 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 <drm/drmP.h> +#include <drm/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 const struct drm_framebuffer_funcs mga_fb_funcs = { +	.destroy = mga_user_framebuffer_destroy, +}; + +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_helper_mode_fill_fb_struct(&gfb->base, mode_cmd); +	gfb->obj = obj; +	ret = drm_framebuffer_init(dev, &gfb->base, &mga_fb_funcs); +	if (ret) { +		DRM_ERROR("drm_framebuffer_init failed: %d\n", ret); +		return ret; +	} +	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, +}; + +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); +	if (!aper) +		return -ENOMEM; + +	/* 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; + +	remove_conflicting_framebuffers(aper, "mgafb", true); +	kfree(aper); + +	if (!devm_request_mem_region(mdev->dev->dev, 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 (!devm_request_mem_region(mdev->dev->dev, mdev->rmmio_base, mdev->rmmio_size, +				"mgadrmfb_mmio")) { +		DRM_ERROR("can't reserve mmio registers\n"); +		return -ENOMEM; +	} + +	mdev->rmmio = pcim_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->unique_rev_id = RREG32(0x1e24); + +	ret = mga_vram_init(mdev); +	if (ret) +		return ret; + +	mdev->bpp_shifts[0] = 0; +	mdev->bpp_shifts[1] = 1; +	mdev->bpp_shifts[2] = 0; +	mdev->bpp_shifts[3] = 2; +	return 0; +} + +/* + * 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 = devm_kzalloc(dev->dev, 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); +		return r; +	} +	r = mgag200_mm_init(mdev); +	if (r) +		goto out; + +	drm_mode_config_init(dev); +	dev->mode_config.funcs = (void *)&mga_mode_funcs; +	if (IS_G200_SE(mdev) && mdev->mc.vram_size < (2048*1024)) +		dev->mode_config.preferred_depth = 16; +	else +		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); +		goto out; +	} + +	/* Make small buffers to store a hardware cursor (double buffered icon updates) */ +	mgag200_bo_create(dev, roundup(48*64, PAGE_SIZE), 0, 0, +					  &mdev->cursor.pixels_1); +	mgag200_bo_create(dev, roundup(48*64, PAGE_SIZE), 0, 0, +					  &mdev->cursor.pixels_2); +	if (!mdev->cursor.pixels_2 || !mdev->cursor.pixels_1) +		goto cursor_nospace; +	mdev->cursor.pixels_current = mdev->cursor.pixels_1; +	mdev->cursor.pixels_prev = mdev->cursor.pixels_2; +	goto cursor_done; + cursor_nospace: +	mdev->cursor.pixels_1 = NULL; +	mdev->cursor.pixels_2 = NULL; +	dev_warn(&dev->pdev->dev, "Could not allocate space for cursors. Not doing hardware cursors.\n"); + cursor_done: + +out: +	if (r) +		mgag200_driver_unload(dev); +	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); +	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; +} + +static void mgag200_bo_unref(struct mgag200_bo **bo) +{ +	struct ttm_buffer_object *tbo; + +	if ((*bo) == NULL) +		return; + +	tbo = &((*bo)->bo); +	ttm_bo_unref(&tbo); +	*bo = NULL; +} + +void mgag200_gem_free_object(struct drm_gem_object *obj) +{ +	struct mgag200_bo *mgag200_bo = gem_to_mga_bo(obj); + +	mgag200_bo_unref(&mgag200_bo); +} + + +static inline u64 mgag200_bo_mmap_offset(struct mgag200_bo *bo) +{ +	return drm_vma_node_offset_addr(&bo->bo.vma_node); +} + +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..a034ed40825 --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_mode.c @@ -0,0 +1,1675 @@ +/* + * 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 <drm/drmP.h> +#include <drm/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; +	struct drm_framebuffer *fb = crtc->primary->fb; +	int i; + +	if (!crtc->enabled) +		return; + +	WREG8(DAC_INDEX + MGA1064_INDEX, 0); + +	if (fb && fb->bits_per_pixel == 16) { +		int inc = (fb->depth == 15) ? 8 : 4; +		u8 r, b; +		for (i = 0; i < MGAG200_LUT_SIZE; i += inc) { +			if (fb->depth == 16) { +				if (i > (MGAG200_LUT_SIZE >> 1)) { +					r = b = 0; +				} else { +					r = mga_crtc->lut_r[i << 1]; +					b = mga_crtc->lut_b[i << 1]; +				} +			} else { +				r = mga_crtc->lut_r[i]; +				b = mga_crtc->lut_b[i]; +			} +			/* VGA registers */ +			WREG8(DAC_INDEX + MGA1064_COL_PAL, r); +			WREG8(DAC_INDEX + MGA1064_COL_PAL, mga_crtc->lut_g[i]); +			WREG8(DAC_INDEX + MGA1064_COL_PAL, b); +		} +		return; +	} +	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 long timeout = jiffies + HZ/10; +	unsigned int status = 0; + +	do { +		status = RREG32(MGAREG_Status); +	} while ((status & 0x08) && time_before(jiffies, timeout)); +	timeout = jiffies + HZ/10; +	status = 0; +	do { +		status = RREG32(MGAREG_Status); +	} while (!(status & 0x08) && time_before(jiffies, timeout)); +} + +static inline void mga_wait_busy(struct mga_device *mdev) +{ +	unsigned long timeout = jiffies + HZ; +	unsigned int status = 0; +	do { +		status = RREG8(MGAREG_Status + 2); +	} while ((status & 0x01) && time_before(jiffies, timeout)); +} + +/* + * 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, +				const 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; +		WREG8(DAC_DATA, tmp); + +		WREG8(DAC_INDEX, MGA1064_REMHEADCTL); +		tmp = RREG8(DAC_DATA); +		tmp |= MGA1064_REMHEADCTL_CLKDIS; +		WREG8(DAC_DATA, 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; +		WREG8(DAC_DATA, tmp); + +		udelay(500); + +		/* reset the PLL */ +		WREG8(DAC_INDEX, MGA1064_VREF_CTL); +		tmp = RREG8(DAC_DATA); +		tmp &= ~0x04; +		WREG8(DAC_DATA, 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; +		WREG8(DAC_DATA, tmp); + +		WREG8(DAC_INDEX, MGA1064_REMHEADCTL); +		tmp = RREG8(DAC_DATA); +		tmp &= ~MGA1064_REMHEADCTL_CLKSL_MSK; +		tmp |= MGA1064_REMHEADCTL_CLKSL_PLL; +		WREG8(DAC_DATA, 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; +		WREG8(DAC_DATA, 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; +	WREG8(DAC_DATA, 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); +	WREG8(DAC_DATA, tmp & ~0x40); + +	WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL); +	tmp = RREG8(DAC_DATA); +	tmp |= MGA1064_PIX_CLK_CTL_CLK_POW_DOWN; +	WREG8(DAC_DATA, 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; +	WREG8(DAC_DATA, 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; +	WREG8(DAC_DATA, tmp); + +	WREG8(DAC_INDEX, MGA1064_PIX_PLL_STAT); +	tmp = RREG8(DAC_DATA); +	WREG8(DAC_DATA, 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; +	WREG8(DAC_DATA, 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 = 33333; + +	delta = 0xffffffff; +	permitteddelta = clock * 5 / 1000; + +	for (testp = 16; testp > 0; testp >>= 1) { +		if (clock * testp > vcomax) +			continue; +		if (clock * testp < vcomin) +			continue; + +		for (testm = 1; testm < 33; testm++) { +			for (testn = 17; 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); +					p = testp - 1; +				} +				if ((clock * testp) >= 600000) +					p |= 0x80; +			} +		} +	} +	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; +		WREG8(DAC_DATA, 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; +		WREG8(DAC_DATA, 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; +		WREG8(DAC_DATA, 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; +		WREG8(DAC_DATA, 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; +	int testr, testn, testm, testo; +	unsigned int p, m, n; +	unsigned int computed, vco; +	int tmp; +	const unsigned int m_div_val[] = { 1, 2, 4, 8 }; + +	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++) { +					vco = pllreffreq * (testn + 1) / +						(testr + 1); +					if (vco < vcomin) +						continue; +					if (vco > vcomax) +						continue; +					computed = vco / (m_div_val[testm] * (testo + 1)); +					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; +	WREG8(DAC_DATA, tmp); + +	WREG8(DAC_INDEX, MGA1064_REMHEADCTL); +	tmp = RREG8(DAC_DATA); +	tmp |= MGA1064_REMHEADCTL_CLKDIS; +	WREG8(DAC_DATA, 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; +	WREG8(DAC_DATA, 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); +} + +/* +   This is how the framebuffer base address is stored in g200 cards: +   * Assume @offset is the gpu_addr variable of the framebuffer object +   * Then addr is the number of _pixels_ (not bytes) from the start of +     VRAM to the first pixel we want to display. (divided by 2 for 32bit +     framebuffers) +   * addr is stored in the CRTCEXT0, CRTCC and CRTCD registers +   addr<20> -> CRTCEXT0<6> +   addr<19-16> -> CRTCEXT0<3-0> +   addr<15-8> -> CRTCC<7-0> +   addr<7-0> -> CRTCD<7-0> +   CRTCEXT0 has to be programmed last to trigger an update and make the +   new addr variable take effect. + */ +static void mga_set_start_address(struct drm_crtc *crtc, unsigned offset) +{ +	struct mga_device *mdev = crtc->dev->dev_private; +	u32 addr; +	int count; +	u8 crtcext0; + +	while (RREG8(0x1fda) & 0x08); +	while (!(RREG8(0x1fda) & 0x08)); + +	count = RREG8(MGAREG_VCOUNT) + 2; +	while (RREG8(MGAREG_VCOUNT) < count); + +	WREG8(MGAREG_CRTCEXT_INDEX, 0); +	crtcext0 = RREG8(MGAREG_CRTCEXT_DATA); +	crtcext0 &= 0xB0; +	addr = offset / 8; +	/* Can't store addresses any higher than that... +	   but we also don't have more than 16MB of memory, so it should be fine. */ +	WARN_ON(addr > 0x1fffff); +	crtcext0 |= (!!(addr & (1<<20)))<<6; +	WREG_CRT(0x0d, (u8)(addr & 0xff)); +	WREG_CRT(0x0c, (u8)(addr >> 8) & 0xff); +	WREG_ECRT(0x0, ((u8)(addr >> 16) & 0xf) | crtcext0); +} + + +/* 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->primary->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); + +	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]; +	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->primary->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: +		break; +	} + +	switch (crtc->primary->fb->bits_per_pixel) { +	case 8: +		dacvalue[MGA1064_MUL_CTL] = MGA1064_MUL_CTL_8bits; +		break; +	case 16: +		if (crtc->primary->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 <= 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, 0); + +	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->primary->fb->pitches[0] / (crtc->primary->fb->bits_per_pixel / 8); +	if (crtc->primary->fb->bits_per_pixel == 24) +		pitch = (pitch * 3) >> (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->primary->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; + +	/* 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(0x24, 0x5); + +	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->unique_rev_id >= 0x02) { +			u8 hi_pri_lvl; +			u32 bpp; +			u32 mb; + +			if (crtc->primary->fb->bits_per_pixel > 16) +				bpp = 32; +			else if (crtc->primary->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(MGAREG_CRTCEXT_INDEX, 0x06); +			WREG8(MGAREG_CRTCEXT_DATA, hi_pri_lvl); +		} else { +			WREG8(MGAREG_CRTCEXT_INDEX, 0x06); +			if (mdev->unique_rev_id >= 0x01) +				WREG8(MGAREG_CRTCEXT_DATA, 0x03); +			else +				WREG8(MGAREG_CRTCEXT_DATA, 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); +} + +static void mga_crtc_disable(struct drm_crtc *crtc) +{ +	int ret; +	DRM_DEBUG_KMS("\n"); +	mga_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); +	if (crtc->primary->fb) { +		struct mga_framebuffer *mga_fb = to_mga_framebuffer(crtc->primary->fb); +		struct drm_gem_object *obj = mga_fb->obj; +		struct mgag200_bo *bo = gem_to_mga_bo(obj); +		ret = mgag200_bo_reserve(bo, false); +		if (ret) +			return; +		mgag200_bo_push_sysram(bo); +		mgag200_bo_unreserve(bo); +	} +	crtc->primary->fb = NULL; +} + +/* These provide the minimum set of functions required to handle a CRTC */ +static const struct drm_crtc_funcs mga_crtc_funcs = { +	.cursor_set = mga_crtc_cursor_set, +	.cursor_move = mga_crtc_cursor_move, +	.gamma_set = mga_crtc_gamma_set, +	.set_config = drm_crtc_helper_set_config, +	.destroy = mga_crtc_destroy, +}; + +static const struct drm_crtc_helper_funcs mga_helper_funcs = { +	.disable = mga_crtc_disable, +	.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 mga_device *mdev) +{ +	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(mdev->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, +				   const 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) +{ +} + +static 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); +		kfree(edid); +	} +	return ret; +} + +static uint32_t mga_vga_calculate_mode_bandwidth(struct drm_display_mode *mode, +							int bits_per_pixel) +{ +	uint32_t total_area, divisor; +	int64_t active_area, pixels_per_second, bandwidth; +	uint64_t bytes_per_pixel = (bits_per_pixel + 7) / 8; + +	divisor = 1024; + +	if (!mode->htotal || !mode->vtotal || !mode->clock) +		return 0; + +	active_area = mode->hdisplay * mode->vdisplay; +	total_area = mode->htotal * mode->vtotal; + +	pixels_per_second = active_area * mode->clock * 1000; +	do_div(pixels_per_second, total_area); + +	bandwidth = pixels_per_second * bytes_per_pixel * 100; +	do_div(bandwidth, divisor); + +	return (uint32_t)(bandwidth); +} + +#define MODE_BANDWIDTH	MODE_BAD + +static int mga_vga_mode_valid(struct drm_connector *connector, +				 struct drm_display_mode *mode) +{ +	struct drm_device *dev = connector->dev; +	struct mga_device *mdev = (struct mga_device*)dev->dev_private; +	struct mga_fbdev *mfbdev = mdev->mfbdev; +	struct drm_fb_helper *fb_helper = &mfbdev->helper; +	struct drm_fb_helper_connector *fb_helper_conn = NULL; +	int bpp = 32; +	int i = 0; + +	if (IS_G200_SE(mdev)) { +		if (mdev->unique_rev_id == 0x01) { +			if (mode->hdisplay > 1600) +				return MODE_VIRTUAL_X; +			if (mode->vdisplay > 1200) +				return MODE_VIRTUAL_Y; +			if (mga_vga_calculate_mode_bandwidth(mode, bpp) +				> (24400 * 1024)) +				return MODE_BANDWIDTH; +		} else if (mdev->unique_rev_id >= 0x02) { +			if (mode->hdisplay > 1920) +				return MODE_VIRTUAL_X; +			if (mode->vdisplay > 1200) +				return MODE_VIRTUAL_Y; +			if (mga_vga_calculate_mode_bandwidth(mode, bpp) +				> (30100 * 1024)) +				return MODE_BANDWIDTH; +		} +	} else if (mdev->type == G200_WB) { +		if (mode->hdisplay > 1280) +			return MODE_VIRTUAL_X; +		if (mode->vdisplay > 1024) +			return MODE_VIRTUAL_Y; +		if (mga_vga_calculate_mode_bandwidth(mode, +			bpp > (31877 * 1024))) +			return MODE_BANDWIDTH; +	} else if (mdev->type == G200_EV && +		(mga_vga_calculate_mode_bandwidth(mode, bpp) +			> (32700 * 1024))) { +		return MODE_BANDWIDTH; +	} else if (mdev->type == G200_EH && +		(mga_vga_calculate_mode_bandwidth(mode, bpp) +			> (37500 * 1024))) { +		return MODE_BANDWIDTH; +	} else if (mdev->type == G200_ER && +		(mga_vga_calculate_mode_bandwidth(mode, +			bpp) > (55000 * 1024))) { +		return MODE_BANDWIDTH; +	} + +	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; +	} + +	/* Validate the mode input by the user */ +	for (i = 0; i < fb_helper->connector_count; i++) { +		if (fb_helper->connector_info[i]->connector == connector) { +			/* Found the helper for this connector */ +			fb_helper_conn = fb_helper->connector_info[i]; +			if (fb_helper_conn->cmdline_mode.specified) { +				if (fb_helper_conn->cmdline_mode.bpp_specified) { +					bpp = fb_helper_conn->cmdline_mode.bpp; +				} +			} +		} +	} + +	if ((mode->hdisplay * mode->vdisplay * (bpp/8)) > mdev->mc.vram_size) { +		if (fb_helper_conn) +			fb_helper_conn->cmdline_mode.specified = false; +		return MODE_BAD; +	} + +	return MODE_OK; +} + +static 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); + +	drm_sysfs_connector_add(connector); + +	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); + +	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..3ae442a64bd --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_reg.h @@ -0,0 +1,665 @@ +/* + * 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 + +/* Cursor X and Y position */ +#define MGA_CURPOSXL 0x3c0c +#define MGA_CURPOSXH 0x3c0d +#define MGA_CURPOSYL 0x3c0e +#define MGA_CURPOSYH 0x3c0f + +/* MGA bits for registers PCI_OPTION_REG */ +#define MGA1064_OPT_SYS_CLK_PCI   		( 0x00 << 0 ) +#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..5a00e90696d --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_ttm.c @@ -0,0 +1,432 @@ +/* + * 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 <drm/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; +} + +static 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); +} + +static 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) +{ +	struct mgag200_bo *mgabo = mgag200_bo(bo); + +	return drm_vma_node_verify_access(&mgabo->gem.vma_node, filp); +} + +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_gpu, +		       struct ttm_mem_reg *new_mem) +{ +	int r; +	r = ttm_bo_move_memcpy(bo, evict, 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, +}; + + +static 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, +				 dev->anon_inode->i_mapping, +				 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 = arch_phys_wc_add(pci_resource_start(dev->pdev, 0), +					 pci_resource_len(dev->pdev, 0)); + +	return 0; +} + +void mgag200_mm_fini(struct mga_device *mdev) +{ +	ttm_bo_device_release(&mdev->ttm.bdev); + +	mgag200_ttm_global_release(mdev); + +	arch_phys_wc_del(mdev->fb_mtrr); +	mdev->fb_mtrr = 0; +} + +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_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->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, false, NULL, acc_size, +			  NULL, 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); +		return 0; +	} + +	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); +	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); +	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); +	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); +}  | 
