diff options
Diffstat (limited to 'drivers/gpu/drm/mgag200/mgag200_cursor.c')
| -rw-r--r-- | drivers/gpu/drm/mgag200/mgag200_cursor.c | 275 | 
1 files changed, 275 insertions, 0 deletions
| diff --git a/drivers/gpu/drm/mgag200/mgag200_cursor.c b/drivers/gpu/drm/mgag200/mgag200_cursor.c new file mode 100644 index 000000000000..801731aeab61 --- /dev/null +++ b/drivers/gpu/drm/mgag200/mgag200_cursor.c @@ -0,0 +1,275 @@ +/* + * Copyright 2013 Matrox Graphics + * + * This file is subject to the terms and conditions of the GNU General + * Public License version 2. See the file COPYING in the main + * directory of this archive for more details. + * + * Author: Christopher Harvey <[email protected]> + */ + +#include <drm/drmP.h> +#include "mgag200_drv.h" + +static bool warn_transparent = true; +static bool warn_palette = true; + +/* +  Hide the cursor off screen. We can't disable the cursor hardware because it +  takes too long to re-activate and causes momentary corruption +*/ +static void mga_hide_cursor(struct mga_device *mdev) +{ +	WREG8(MGA_CURPOSXL, 0); +	WREG8(MGA_CURPOSXH, 0); +	mgag200_bo_unpin(mdev->cursor.pixels_1); +	mgag200_bo_unpin(mdev->cursor.pixels_2); +} + +int mga_crtc_cursor_set(struct drm_crtc *crtc, +			struct drm_file *file_priv, +			uint32_t handle, +			uint32_t width, +			uint32_t height) +{ +	struct drm_device *dev = (struct drm_device *)file_priv->minor->dev; +	struct mga_device *mdev = (struct mga_device *)dev->dev_private; +	struct mgag200_bo *pixels_1 = mdev->cursor.pixels_1; +	struct mgag200_bo *pixels_2 = mdev->cursor.pixels_2; +	struct mgag200_bo *pixels_current = mdev->cursor.pixels_current; +	struct mgag200_bo *pixels_prev = mdev->cursor.pixels_prev; +	struct drm_gem_object *obj; +	struct mgag200_bo *bo = NULL; +	int ret = 0; +	unsigned int i, row, col; +	uint32_t colour_set[16]; +	uint32_t *next_space = &colour_set[0]; +	uint32_t *palette_iter; +	uint32_t this_colour; +	bool found = false; +	int colour_count = 0; +	u64 gpu_addr; +	u8 reg_index; +	u8 this_row[48]; + +	if (!pixels_1 || !pixels_2) { +		WREG8(MGA_CURPOSXL, 0); +		WREG8(MGA_CURPOSXH, 0); +		return -ENOTSUPP; /* Didn't allocate space for cursors */ +	} + +	if ((width != 64 || height != 64) && handle) { +		WREG8(MGA_CURPOSXL, 0); +		WREG8(MGA_CURPOSXH, 0); +		return -EINVAL; +	} + +	BUG_ON(pixels_1 != pixels_current && pixels_1 != pixels_prev); +	BUG_ON(pixels_2 != pixels_current && pixels_2 != pixels_prev); +	BUG_ON(pixels_current == pixels_prev); + +	ret = mgag200_bo_reserve(pixels_1, true); +	if (ret) { +		WREG8(MGA_CURPOSXL, 0); +		WREG8(MGA_CURPOSXH, 0); +		return ret; +	} +	ret = mgag200_bo_reserve(pixels_2, true); +	if (ret) { +		WREG8(MGA_CURPOSXL, 0); +		WREG8(MGA_CURPOSXH, 0); +		mgag200_bo_unreserve(pixels_1); +		return ret; +	} + +	if (!handle) { +		mga_hide_cursor(mdev); +		ret = 0; +		goto out1; +	} + +	/* Move cursor buffers into VRAM if they aren't already */ +	if (!pixels_1->pin_count) { +		ret = mgag200_bo_pin(pixels_1, TTM_PL_FLAG_VRAM, +				     &mdev->cursor.pixels_1_gpu_addr); +		if (ret) +			goto out1; +	} +	if (!pixels_2->pin_count) { +		ret = mgag200_bo_pin(pixels_2, TTM_PL_FLAG_VRAM, +				     &mdev->cursor.pixels_2_gpu_addr); +		if (ret) { +			mgag200_bo_unpin(pixels_1); +			goto out1; +		} +	} + +	mutex_lock(&dev->struct_mutex); +	obj = drm_gem_object_lookup(dev, file_priv, handle); +	if (!obj) { +		mutex_unlock(&dev->struct_mutex); +		ret = -ENOENT; +		goto out1; +	} +	drm_gem_object_unreference(obj); +	mutex_unlock(&dev->struct_mutex); + +	bo = gem_to_mga_bo(obj); +	ret = mgag200_bo_reserve(bo, true); +	if (ret) { +		dev_err(&dev->pdev->dev, "failed to reserve user bo\n"); +		goto out1; +	} +	if (!bo->kmap.virtual) { +		ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap); +		if (ret) { +			dev_err(&dev->pdev->dev, "failed to kmap user buffer updates\n"); +			goto out2; +		} +	} + +	memset(&colour_set[0], 0, sizeof(uint32_t)*16); +	/* width*height*4 = 16384 */ +	for (i = 0; i < 16384; i += 4) { +		this_colour = ioread32(bo->kmap.virtual + i); +		/* No transparency */ +		if (this_colour>>24 != 0xff && +			this_colour>>24 != 0x0) { +			if (warn_transparent) { +				dev_info(&dev->pdev->dev, "Video card doesn't support cursors with partial transparency.\n"); +				dev_info(&dev->pdev->dev, "Not enabling hardware cursor.\n"); +				warn_transparent = false; /* Only tell the user once. */ +			} +			ret = -EINVAL; +			goto out3; +		} +		/* Don't need to store transparent pixels as colours */ +		if (this_colour>>24 == 0x0) +			continue; +		found = false; +		for (palette_iter = &colour_set[0]; palette_iter != next_space; palette_iter++) { +			if (*palette_iter == this_colour) { +				found = true; +				break; +			} +		} +		if (found) +			continue; +		/* We only support 4bit paletted cursors */ +		if (colour_count >= 16) { +			if (warn_palette) { +				dev_info(&dev->pdev->dev, "Video card only supports cursors with up to 16 colours.\n"); +				dev_info(&dev->pdev->dev, "Not enabling hardware cursor.\n"); +				warn_palette = false; /* Only tell the user once. */ +			} +			ret = -EINVAL; +			goto out3; +		} +		*next_space = this_colour; +		next_space++; +		colour_count++; +	} + +	/* Program colours from cursor icon into palette */ +	for (i = 0; i < colour_count; i++) { +		if (i <= 2) +			reg_index = 0x8 + i*0x4; +		else +			reg_index = 0x60 + i*0x3; +		WREG_DAC(reg_index, colour_set[i] & 0xff); +		WREG_DAC(reg_index+1, colour_set[i]>>8 & 0xff); +		WREG_DAC(reg_index+2, colour_set[i]>>16 & 0xff); +		BUG_ON((colour_set[i]>>24 & 0xff) != 0xff); +	} + +	/* Map up-coming buffer to write colour indices */ +	if (!pixels_prev->kmap.virtual) { +		ret = ttm_bo_kmap(&pixels_prev->bo, 0, +				  pixels_prev->bo.num_pages, +				  &pixels_prev->kmap); +		if (ret) { +			dev_err(&dev->pdev->dev, "failed to kmap cursor updates\n"); +			goto out3; +		} +	} + +	/* now write colour indices into hardware cursor buffer */ +	for (row = 0; row < 64; row++) { +		memset(&this_row[0], 0, 48); +		for (col = 0; col < 64; col++) { +			this_colour = ioread32(bo->kmap.virtual + 4*(col + 64*row)); +			/* write transparent pixels */ +			if (this_colour>>24 == 0x0) { +				this_row[47 - col/8] |= 0x80>>(col%8); +				continue; +			} + +			/* write colour index here */ +			for (i = 0; i < colour_count; i++) { +				if (colour_set[i] == this_colour) { +					if (col % 2) +						this_row[col/2] |= i<<4; +					else +						this_row[col/2] |= i; +					break; +				} +			} +		} +		memcpy_toio(pixels_prev->kmap.virtual + row*48, &this_row[0], 48); +	} + +	/* Program gpu address of cursor buffer */ +	if (pixels_prev == pixels_1) +		gpu_addr = mdev->cursor.pixels_1_gpu_addr; +	else +		gpu_addr = mdev->cursor.pixels_2_gpu_addr; +	WREG_DAC(MGA1064_CURSOR_BASE_ADR_LOW, (u8)((gpu_addr>>10) & 0xff)); +	WREG_DAC(MGA1064_CURSOR_BASE_ADR_HI, (u8)((gpu_addr>>18) & 0x3f)); + +	/* Adjust cursor control register to turn on the cursor */ +	WREG_DAC(MGA1064_CURSOR_CTL, 4); /* 16-colour palletized cursor mode */ + +	/* Now swap internal buffer pointers */ +	if (mdev->cursor.pixels_1 == mdev->cursor.pixels_prev) { +		mdev->cursor.pixels_prev = mdev->cursor.pixels_2; +		mdev->cursor.pixels_current = mdev->cursor.pixels_1; +	} else if (mdev->cursor.pixels_1 == mdev->cursor.pixels_current) { +		mdev->cursor.pixels_prev = mdev->cursor.pixels_1; +		mdev->cursor.pixels_current = mdev->cursor.pixels_2; +	} else { +		BUG(); +	} +	ret = 0; + +	ttm_bo_kunmap(&pixels_prev->kmap); + out3: +	ttm_bo_kunmap(&bo->kmap); + out2: +	mgag200_bo_unreserve(bo); + out1: +	if (ret) +		mga_hide_cursor(mdev); +	mgag200_bo_unreserve(pixels_1); +	mgag200_bo_unreserve(pixels_2); +	return ret; +} + +int mga_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) +{ +	struct mga_device *mdev = (struct mga_device *)crtc->dev->dev_private; +	/* Our origin is at (64,64) */ +	x += 64; +	y += 64; + +	BUG_ON(x <= 0); +	BUG_ON(y <= 0); +	BUG_ON(x & ~0xffff); +	BUG_ON(y & ~0xffff); + +	WREG8(MGA_CURPOSXL, x & 0xff); +	WREG8(MGA_CURPOSXH, (x>>8) & 0xff); + +	WREG8(MGA_CURPOSYL, y & 0xff); +	WREG8(MGA_CURPOSYH, (y>>8) & 0xff); +	return 0; +} |