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 <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); + 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; +} |