1/* 2 * Copyright 2013 Matrox Graphics 3 * 4 * This file is subject to the terms and conditions of the GNU General 5 * Public License version 2. See the file COPYING in the main 6 * directory of this archive for more details. 7 * 8 * Author: Christopher Harvey <charvey@matrox.com> 9 */ 10 11#include <drm/drmP.h> 12#include "mgag200_drv.h" 13 14static bool warn_transparent = true; 15static bool warn_palette = true; 16 17/* 18 Hide the cursor off screen. We can't disable the cursor hardware because it 19 takes too long to re-activate and causes momentary corruption 20*/ 21static void mga_hide_cursor(struct mga_device *mdev) 22{ 23 WREG8(MGA_CURPOSXL, 0); 24 WREG8(MGA_CURPOSXH, 0); 25 if (mdev->cursor.pixels_1->pin_count) 26 mgag200_bo_unpin(mdev->cursor.pixels_1); 27 if (mdev->cursor.pixels_2->pin_count) 28 mgag200_bo_unpin(mdev->cursor.pixels_2); 29} 30 31int mga_crtc_cursor_set(struct drm_crtc *crtc, 32 struct drm_file *file_priv, 33 uint32_t handle, 34 uint32_t width, 35 uint32_t height) 36{ 37 struct drm_device *dev = crtc->dev; 38 struct mga_device *mdev = (struct mga_device *)dev->dev_private; 39 struct mgag200_bo *pixels_1 = mdev->cursor.pixels_1; 40 struct mgag200_bo *pixels_2 = mdev->cursor.pixels_2; 41 struct mgag200_bo *pixels_current = mdev->cursor.pixels_current; 42 struct mgag200_bo *pixels_prev = mdev->cursor.pixels_prev; 43 struct drm_gem_object *obj; 44 struct mgag200_bo *bo = NULL; 45 int ret = 0; 46 unsigned int i, row, col; 47 uint32_t colour_set[16]; 48 uint32_t *next_space = &colour_set[0]; 49 uint32_t *palette_iter; 50 uint32_t this_colour; 51 bool found = false; 52 int colour_count = 0; 53 u64 gpu_addr; 54 u8 reg_index; 55 u8 this_row[48]; 56 57 if (!pixels_1 || !pixels_2) { 58 WREG8(MGA_CURPOSXL, 0); 59 WREG8(MGA_CURPOSXH, 0); 60 return -ENOTSUPP; /* Didn't allocate space for cursors */ 61 } 62 63 if ((width != 64 || height != 64) && handle) { 64 WREG8(MGA_CURPOSXL, 0); 65 WREG8(MGA_CURPOSXH, 0); 66 return -EINVAL; 67 } 68 69 BUG_ON(pixels_1 != pixels_current && pixels_1 != pixels_prev); 70 BUG_ON(pixels_2 != pixels_current && pixels_2 != pixels_prev); 71 BUG_ON(pixels_current == pixels_prev); 72 73 ret = mgag200_bo_reserve(pixels_1, true); 74 if (ret) { 75 WREG8(MGA_CURPOSXL, 0); 76 WREG8(MGA_CURPOSXH, 0); 77 return ret; 78 } 79 ret = mgag200_bo_reserve(pixels_2, true); 80 if (ret) { 81 WREG8(MGA_CURPOSXL, 0); 82 WREG8(MGA_CURPOSXH, 0); 83 mgag200_bo_unreserve(pixels_1); 84 return ret; 85 } 86 87 if (!handle) { 88 mga_hide_cursor(mdev); 89 ret = 0; 90 goto out1; 91 } 92 93 /* Move cursor buffers into VRAM if they aren't already */ 94 if (!pixels_1->pin_count) { 95 ret = mgag200_bo_pin(pixels_1, TTM_PL_FLAG_VRAM, 96 &mdev->cursor.pixels_1_gpu_addr); 97 if (ret) 98 goto out1; 99 } 100 if (!pixels_2->pin_count) { 101 ret = mgag200_bo_pin(pixels_2, TTM_PL_FLAG_VRAM, 102 &mdev->cursor.pixels_2_gpu_addr); 103 if (ret) { 104 mgag200_bo_unpin(pixels_1); 105 goto out1; 106 } 107 } 108 109 mutex_lock(&dev->struct_mutex); 110 obj = drm_gem_object_lookup(dev, file_priv, handle); 111 if (!obj) { 112 mutex_unlock(&dev->struct_mutex); 113 ret = -ENOENT; 114 goto out1; 115 } 116 drm_gem_object_unreference(obj); 117 mutex_unlock(&dev->struct_mutex); 118 119 bo = gem_to_mga_bo(obj); 120 ret = mgag200_bo_reserve(bo, true); 121 if (ret) { 122 dev_err(&dev->pdev->dev, "failed to reserve user bo\n"); 123 goto out1; 124 } 125 if (!bo->kmap.virtual) { 126 ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap); 127 if (ret) { 128 dev_err(&dev->pdev->dev, "failed to kmap user buffer updates\n"); 129 goto out2; 130 } 131 } 132 133 memset(&colour_set[0], 0, sizeof(uint32_t)*16); 134 /* width*height*4 = 16384 */ 135 for (i = 0; i < 16384; i += 4) { 136 this_colour = ioread32(bo->kmap.virtual + i); 137 /* No transparency */ 138 if (this_colour>>24 != 0xff && 139 this_colour>>24 != 0x0) { 140 if (warn_transparent) { 141 dev_info(&dev->pdev->dev, "Video card doesn't support cursors with partial transparency.\n"); 142 dev_info(&dev->pdev->dev, "Not enabling hardware cursor.\n"); 143 warn_transparent = false; /* Only tell the user once. */ 144 } 145 ret = -EINVAL; 146 goto out3; 147 } 148 /* Don't need to store transparent pixels as colours */ 149 if (this_colour>>24 == 0x0) 150 continue; 151 found = false; 152 for (palette_iter = &colour_set[0]; palette_iter != next_space; palette_iter++) { 153 if (*palette_iter == this_colour) { 154 found = true; 155 break; 156 } 157 } 158 if (found) 159 continue; 160 /* We only support 4bit paletted cursors */ 161 if (colour_count >= 16) { 162 if (warn_palette) { 163 dev_info(&dev->pdev->dev, "Video card only supports cursors with up to 16 colours.\n"); 164 dev_info(&dev->pdev->dev, "Not enabling hardware cursor.\n"); 165 warn_palette = false; /* Only tell the user once. */ 166 } 167 ret = -EINVAL; 168 goto out3; 169 } 170 *next_space = this_colour; 171 next_space++; 172 colour_count++; 173 } 174 175 /* Program colours from cursor icon into palette */ 176 for (i = 0; i < colour_count; i++) { 177 if (i <= 2) 178 reg_index = 0x8 + i*0x4; 179 else 180 reg_index = 0x60 + i*0x3; 181 WREG_DAC(reg_index, colour_set[i] & 0xff); 182 WREG_DAC(reg_index+1, colour_set[i]>>8 & 0xff); 183 WREG_DAC(reg_index+2, colour_set[i]>>16 & 0xff); 184 BUG_ON((colour_set[i]>>24 & 0xff) != 0xff); 185 } 186 187 /* Map up-coming buffer to write colour indices */ 188 if (!pixels_prev->kmap.virtual) { 189 ret = ttm_bo_kmap(&pixels_prev->bo, 0, 190 pixels_prev->bo.num_pages, 191 &pixels_prev->kmap); 192 if (ret) { 193 dev_err(&dev->pdev->dev, "failed to kmap cursor updates\n"); 194 goto out3; 195 } 196 } 197 198 /* now write colour indices into hardware cursor buffer */ 199 for (row = 0; row < 64; row++) { 200 memset(&this_row[0], 0, 48); 201 for (col = 0; col < 64; col++) { 202 this_colour = ioread32(bo->kmap.virtual + 4*(col + 64*row)); 203 /* write transparent pixels */ 204 if (this_colour>>24 == 0x0) { 205 this_row[47 - col/8] |= 0x80>>(col%8); 206 continue; 207 } 208 209 /* write colour index here */ 210 for (i = 0; i < colour_count; i++) { 211 if (colour_set[i] == this_colour) { 212 if (col % 2) 213 this_row[col/2] |= i<<4; 214 else 215 this_row[col/2] |= i; 216 break; 217 } 218 } 219 } 220 memcpy_toio(pixels_prev->kmap.virtual + row*48, &this_row[0], 48); 221 } 222 223 /* Program gpu address of cursor buffer */ 224 if (pixels_prev == pixels_1) 225 gpu_addr = mdev->cursor.pixels_1_gpu_addr; 226 else 227 gpu_addr = mdev->cursor.pixels_2_gpu_addr; 228 WREG_DAC(MGA1064_CURSOR_BASE_ADR_LOW, (u8)((gpu_addr>>10) & 0xff)); 229 WREG_DAC(MGA1064_CURSOR_BASE_ADR_HI, (u8)((gpu_addr>>18) & 0x3f)); 230 231 /* Adjust cursor control register to turn on the cursor */ 232 WREG_DAC(MGA1064_CURSOR_CTL, 4); /* 16-colour palletized cursor mode */ 233 234 /* Now swap internal buffer pointers */ 235 if (mdev->cursor.pixels_1 == mdev->cursor.pixels_prev) { 236 mdev->cursor.pixels_prev = mdev->cursor.pixels_2; 237 mdev->cursor.pixels_current = mdev->cursor.pixels_1; 238 } else if (mdev->cursor.pixels_1 == mdev->cursor.pixels_current) { 239 mdev->cursor.pixels_prev = mdev->cursor.pixels_1; 240 mdev->cursor.pixels_current = mdev->cursor.pixels_2; 241 } else { 242 BUG(); 243 } 244 ret = 0; 245 246 ttm_bo_kunmap(&pixels_prev->kmap); 247 out3: 248 ttm_bo_kunmap(&bo->kmap); 249 out2: 250 mgag200_bo_unreserve(bo); 251 out1: 252 if (ret) 253 mga_hide_cursor(mdev); 254 mgag200_bo_unreserve(pixels_1); 255 mgag200_bo_unreserve(pixels_2); 256 return ret; 257} 258 259int mga_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) 260{ 261 struct mga_device *mdev = (struct mga_device *)crtc->dev->dev_private; 262 /* Our origin is at (64,64) */ 263 x += 64; 264 y += 64; 265 266 BUG_ON(x <= 0); 267 BUG_ON(y <= 0); 268 BUG_ON(x & ~0xffff); 269 BUG_ON(y & ~0xffff); 270 271 WREG8(MGA_CURPOSXL, x & 0xff); 272 WREG8(MGA_CURPOSXH, (x>>8) & 0xff); 273 274 WREG8(MGA_CURPOSYL, y & 0xff); 275 WREG8(MGA_CURPOSYH, (y>>8) & 0xff); 276 return 0; 277} 278