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 if (!handle || !file_priv) { 74 mga_hide_cursor(mdev); 75 return 0; 76 } 77 78 obj = drm_gem_object_lookup(dev, file_priv, handle); 79 if (!obj) 80 return -ENOENT; 81 82 ret = mgag200_bo_reserve(pixels_1, true); 83 if (ret) { 84 WREG8(MGA_CURPOSXL, 0); 85 WREG8(MGA_CURPOSXH, 0); 86 goto out_unref; 87 } 88 ret = mgag200_bo_reserve(pixels_2, true); 89 if (ret) { 90 WREG8(MGA_CURPOSXL, 0); 91 WREG8(MGA_CURPOSXH, 0); 92 mgag200_bo_unreserve(pixels_1); 93 goto out_unreserve1; 94 } 95 96 /* Move cursor buffers into VRAM if they aren't already */ 97 if (!pixels_1->pin_count) { 98 ret = mgag200_bo_pin(pixels_1, TTM_PL_FLAG_VRAM, 99 &mdev->cursor.pixels_1_gpu_addr); 100 if (ret) 101 goto out1; 102 } 103 if (!pixels_2->pin_count) { 104 ret = mgag200_bo_pin(pixels_2, TTM_PL_FLAG_VRAM, 105 &mdev->cursor.pixels_2_gpu_addr); 106 if (ret) { 107 mgag200_bo_unpin(pixels_1); 108 goto out1; 109 } 110 } 111 112 bo = gem_to_mga_bo(obj); 113 ret = mgag200_bo_reserve(bo, true); 114 if (ret) { 115 dev_err(&dev->pdev->dev, "failed to reserve user bo\n"); 116 goto out1; 117 } 118 if (!bo->kmap.virtual) { 119 ret = ttm_bo_kmap(&bo->bo, 0, bo->bo.num_pages, &bo->kmap); 120 if (ret) { 121 dev_err(&dev->pdev->dev, "failed to kmap user buffer updates\n"); 122 goto out2; 123 } 124 } 125 126 memset(&colour_set[0], 0, sizeof(uint32_t)*16); 127 /* width*height*4 = 16384 */ 128 for (i = 0; i < 16384; i += 4) { 129 this_colour = ioread32(bo->kmap.virtual + i); 130 /* No transparency */ 131 if (this_colour>>24 != 0xff && 132 this_colour>>24 != 0x0) { 133 if (warn_transparent) { 134 dev_info(&dev->pdev->dev, "Video card doesn't support cursors with partial transparency.\n"); 135 dev_info(&dev->pdev->dev, "Not enabling hardware cursor.\n"); 136 warn_transparent = false; /* Only tell the user once. */ 137 } 138 ret = -EINVAL; 139 goto out3; 140 } 141 /* Don't need to store transparent pixels as colours */ 142 if (this_colour>>24 == 0x0) 143 continue; 144 found = false; 145 for (palette_iter = &colour_set[0]; palette_iter != next_space; palette_iter++) { 146 if (*palette_iter == this_colour) { 147 found = true; 148 break; 149 } 150 } 151 if (found) 152 continue; 153 /* We only support 4bit paletted cursors */ 154 if (colour_count >= 16) { 155 if (warn_palette) { 156 dev_info(&dev->pdev->dev, "Video card only supports cursors with up to 16 colours.\n"); 157 dev_info(&dev->pdev->dev, "Not enabling hardware cursor.\n"); 158 warn_palette = false; /* Only tell the user once. */ 159 } 160 ret = -EINVAL; 161 goto out3; 162 } 163 *next_space = this_colour; 164 next_space++; 165 colour_count++; 166 } 167 168 /* Program colours from cursor icon into palette */ 169 for (i = 0; i < colour_count; i++) { 170 if (i <= 2) 171 reg_index = 0x8 + i*0x4; 172 else 173 reg_index = 0x60 + i*0x3; 174 WREG_DAC(reg_index, colour_set[i] & 0xff); 175 WREG_DAC(reg_index+1, colour_set[i]>>8 & 0xff); 176 WREG_DAC(reg_index+2, colour_set[i]>>16 & 0xff); 177 BUG_ON((colour_set[i]>>24 & 0xff) != 0xff); 178 } 179 180 /* Map up-coming buffer to write colour indices */ 181 if (!pixels_prev->kmap.virtual) { 182 ret = ttm_bo_kmap(&pixels_prev->bo, 0, 183 pixels_prev->bo.num_pages, 184 &pixels_prev->kmap); 185 if (ret) { 186 dev_err(&dev->pdev->dev, "failed to kmap cursor updates\n"); 187 goto out3; 188 } 189 } 190 191 /* now write colour indices into hardware cursor buffer */ 192 for (row = 0; row < 64; row++) { 193 memset(&this_row[0], 0, 48); 194 for (col = 0; col < 64; col++) { 195 this_colour = ioread32(bo->kmap.virtual + 4*(col + 64*row)); 196 /* write transparent pixels */ 197 if (this_colour>>24 == 0x0) { 198 this_row[47 - col/8] |= 0x80>>(col%8); 199 continue; 200 } 201 202 /* write colour index here */ 203 for (i = 0; i < colour_count; i++) { 204 if (colour_set[i] == this_colour) { 205 if (col % 2) 206 this_row[col/2] |= i<<4; 207 else 208 this_row[col/2] |= i; 209 break; 210 } 211 } 212 } 213 memcpy_toio(pixels_prev->kmap.virtual + row*48, &this_row[0], 48); 214 } 215 216 /* Program gpu address of cursor buffer */ 217 if (pixels_prev == pixels_1) 218 gpu_addr = mdev->cursor.pixels_1_gpu_addr; 219 else 220 gpu_addr = mdev->cursor.pixels_2_gpu_addr; 221 WREG_DAC(MGA1064_CURSOR_BASE_ADR_LOW, (u8)((gpu_addr>>10) & 0xff)); 222 WREG_DAC(MGA1064_CURSOR_BASE_ADR_HI, (u8)((gpu_addr>>18) & 0x3f)); 223 224 /* Adjust cursor control register to turn on the cursor */ 225 WREG_DAC(MGA1064_CURSOR_CTL, 4); /* 16-colour palletized cursor mode */ 226 227 /* Now swap internal buffer pointers */ 228 if (mdev->cursor.pixels_1 == mdev->cursor.pixels_prev) { 229 mdev->cursor.pixels_prev = mdev->cursor.pixels_2; 230 mdev->cursor.pixels_current = mdev->cursor.pixels_1; 231 } else if (mdev->cursor.pixels_1 == mdev->cursor.pixels_current) { 232 mdev->cursor.pixels_prev = mdev->cursor.pixels_1; 233 mdev->cursor.pixels_current = mdev->cursor.pixels_2; 234 } else { 235 BUG(); 236 } 237 ret = 0; 238 239 ttm_bo_kunmap(&pixels_prev->kmap); 240 out3: 241 ttm_bo_kunmap(&bo->kmap); 242 out2: 243 mgag200_bo_unreserve(bo); 244 out1: 245 if (ret) 246 mga_hide_cursor(mdev); 247 mgag200_bo_unreserve(pixels_1); 248out_unreserve1: 249 mgag200_bo_unreserve(pixels_2); 250out_unref: 251 drm_gem_object_unreference_unlocked(obj); 252 253 return ret; 254} 255 256int mga_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) 257{ 258 struct mga_device *mdev = (struct mga_device *)crtc->dev->dev_private; 259 /* Our origin is at (64,64) */ 260 x += 64; 261 y += 64; 262 263 BUG_ON(x <= 0); 264 BUG_ON(y <= 0); 265 BUG_ON(x & ~0xffff); 266 BUG_ON(y & ~0xffff); 267 268 WREG8(MGA_CURPOSXL, x & 0xff); 269 WREG8(MGA_CURPOSXH, (x>>8) & 0xff); 270 271 WREG8(MGA_CURPOSYL, y & 0xff); 272 WREG8(MGA_CURPOSYH, (y>>8) & 0xff); 273 return 0; 274} 275