1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * 4 * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200 and G400 5 * 6 * (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz> 7 * 8 * Version: 1.65 2002/08/14 9 * 10 * MTRR stuff: 1998 Tom Rini <trini@kernel.crashing.org> 11 * 12 * Contributors: "menion?" <menion@mindless.com> 13 * Betatesting, fixes, ideas 14 * 15 * "Kurt Garloff" <garloff@suse.de> 16 * Betatesting, fixes, ideas, videomodes, videomodes timmings 17 * 18 * "Tom Rini" <trini@kernel.crashing.org> 19 * MTRR stuff, PPC cleanups, betatesting, fixes, ideas 20 * 21 * "Bibek Sahu" <scorpio@dodds.net> 22 * Access device through readb|w|l and write b|w|l 23 * Extensive debugging stuff 24 * 25 * "Daniel Haun" <haund@usa.net> 26 * Testing, hardware cursor fixes 27 * 28 * "Scott Wood" <sawst46+@pitt.edu> 29 * Fixes 30 * 31 * "Gerd Knorr" <kraxel@goldbach.isdn.cs.tu-berlin.de> 32 * Betatesting 33 * 34 * "Kelly French" <targon@hazmat.com> 35 * "Fernando Herrera" <fherrera@eurielec.etsit.upm.es> 36 * Betatesting, bug reporting 37 * 38 * "Pablo Bianucci" <pbian@pccp.com.ar> 39 * Fixes, ideas, betatesting 40 * 41 * "Inaky Perez Gonzalez" <inaky@peloncho.fis.ucm.es> 42 * Fixes, enhandcements, ideas, betatesting 43 * 44 * "Ryuichi Oikawa" <roikawa@rr.iiij4u.or.jp> 45 * PPC betatesting, PPC support, backward compatibility 46 * 47 * "Paul Womar" <Paul@pwomar.demon.co.uk> 48 * "Owen Waller" <O.Waller@ee.qub.ac.uk> 49 * PPC betatesting 50 * 51 * "Thomas Pornin" <pornin@bolet.ens.fr> 52 * Alpha betatesting 53 * 54 * "Pieter van Leuven" <pvl@iae.nl> 55 * "Ulf Jaenicke-Roessler" <ujr@physik.phy.tu-dresden.de> 56 * G100 testing 57 * 58 * "H. Peter Arvin" <hpa@transmeta.com> 59 * Ideas 60 * 61 * "Cort Dougan" <cort@cs.nmt.edu> 62 * CHRP fixes and PReP cleanup 63 * 64 * "Mark Vojkovich" <mvojkovi@ucsd.edu> 65 * G400 support 66 * 67 * (following author is not in any relation with this code, but his code 68 * is included in this driver) 69 * 70 * Based on framebuffer driver for VBE 2.0 compliant graphic boards 71 * (c) 1998 Gerd Knorr <kraxel@cs.tu-berlin.de> 72 * 73 * (following author is not in any relation with this code, but his ideas 74 * were used when writing this driver) 75 * 76 * FreeVBE/AF (Matrox), "Shawn Hargreaves" <shawn@talula.demon.co.uk> 77 * 78 */ 79 80 #include <linux/export.h> 81 82 #include "matroxfb_accel.h" 83 #include "matroxfb_DAC1064.h" 84 #include "matroxfb_Ti3026.h" 85 #include "matroxfb_misc.h" 86 87 #define curr_ydstorg(x) ((x)->curr.ydstorg.pixels) 88 89 #define mga_ydstlen(y,l) mga_outl(M_YDSTLEN | M_EXEC, ((y) << 16) | (l)) 90 91 static inline void matrox_cfb4_pal(u_int32_t* pal) { 92 unsigned int i; 93 94 for (i = 0; i < 16; i++) { 95 pal[i] = i * 0x11111111U; 96 } 97 } 98 99 static inline void matrox_cfb8_pal(u_int32_t* pal) { 100 unsigned int i; 101 102 for (i = 0; i < 16; i++) { 103 pal[i] = i * 0x01010101U; 104 } 105 } 106 107 static void matroxfb_copyarea(struct fb_info* info, const struct fb_copyarea* area); 108 static void matroxfb_fillrect(struct fb_info* info, const struct fb_fillrect* rect); 109 static void matroxfb_imageblit(struct fb_info* info, const struct fb_image* image); 110 static void matroxfb_cfb4_fillrect(struct fb_info* info, const struct fb_fillrect* rect); 111 static void matroxfb_cfb4_copyarea(struct fb_info* info, const struct fb_copyarea* area); 112 113 void matrox_cfbX_init(struct matrox_fb_info *minfo) 114 { 115 u_int32_t maccess; 116 u_int32_t mpitch; 117 u_int32_t mopmode; 118 int accel; 119 120 DBG(__func__) 121 122 mpitch = minfo->fbcon.var.xres_virtual; 123 124 minfo->fbops.fb_copyarea = cfb_copyarea; 125 minfo->fbops.fb_fillrect = cfb_fillrect; 126 minfo->fbops.fb_imageblit = cfb_imageblit; 127 minfo->fbops.fb_cursor = NULL; 128 129 accel = (minfo->fbcon.var.accel_flags & FB_ACCELF_TEXT) == FB_ACCELF_TEXT; 130 131 switch (minfo->fbcon.var.bits_per_pixel) { 132 case 4: maccess = 0x00000000; /* accelerate as 8bpp video */ 133 mpitch = (mpitch >> 1) | 0x8000; /* disable linearization */ 134 mopmode = M_OPMODE_4BPP; 135 matrox_cfb4_pal(minfo->cmap); 136 if (accel && !(mpitch & 1)) { 137 minfo->fbops.fb_copyarea = matroxfb_cfb4_copyarea; 138 minfo->fbops.fb_fillrect = matroxfb_cfb4_fillrect; 139 } 140 break; 141 case 8: maccess = 0x00000000; 142 mopmode = M_OPMODE_8BPP; 143 matrox_cfb8_pal(minfo->cmap); 144 if (accel) { 145 minfo->fbops.fb_copyarea = matroxfb_copyarea; 146 minfo->fbops.fb_fillrect = matroxfb_fillrect; 147 minfo->fbops.fb_imageblit = matroxfb_imageblit; 148 } 149 break; 150 case 16: if (minfo->fbcon.var.green.length == 5) 151 maccess = 0xC0000001; 152 else 153 maccess = 0x40000001; 154 mopmode = M_OPMODE_16BPP; 155 if (accel) { 156 minfo->fbops.fb_copyarea = matroxfb_copyarea; 157 minfo->fbops.fb_fillrect = matroxfb_fillrect; 158 minfo->fbops.fb_imageblit = matroxfb_imageblit; 159 } 160 break; 161 case 24: maccess = 0x00000003; 162 mopmode = M_OPMODE_24BPP; 163 if (accel) { 164 minfo->fbops.fb_copyarea = matroxfb_copyarea; 165 minfo->fbops.fb_fillrect = matroxfb_fillrect; 166 minfo->fbops.fb_imageblit = matroxfb_imageblit; 167 } 168 break; 169 case 32: maccess = 0x00000002; 170 mopmode = M_OPMODE_32BPP; 171 if (accel) { 172 minfo->fbops.fb_copyarea = matroxfb_copyarea; 173 minfo->fbops.fb_fillrect = matroxfb_fillrect; 174 minfo->fbops.fb_imageblit = matroxfb_imageblit; 175 } 176 break; 177 default: maccess = 0x00000000; 178 mopmode = 0x00000000; 179 break; /* turn off acceleration!!! */ 180 } 181 mga_fifo(8); 182 mga_outl(M_PITCH, mpitch); 183 mga_outl(M_YDSTORG, curr_ydstorg(minfo)); 184 if (minfo->capable.plnwt) 185 mga_outl(M_PLNWT, -1); 186 if (minfo->capable.srcorg) { 187 mga_outl(M_SRCORG, 0); 188 mga_outl(M_DSTORG, 0); 189 } 190 mga_outl(M_OPMODE, mopmode); 191 mga_outl(M_CXBNDRY, 0xFFFF0000); 192 mga_outl(M_YTOP, 0); 193 mga_outl(M_YBOT, 0x01FFFFFF); 194 mga_outl(M_MACCESS, maccess); 195 minfo->accel.m_dwg_rect = M_DWG_TRAP | M_DWG_SOLID | M_DWG_ARZERO | M_DWG_SGNZERO | M_DWG_SHIFTZERO; 196 if (isMilleniumII(minfo)) minfo->accel.m_dwg_rect |= M_DWG_TRANSC; 197 minfo->accel.m_opmode = mopmode; 198 minfo->accel.m_access = maccess; 199 minfo->accel.m_pitch = mpitch; 200 } 201 202 EXPORT_SYMBOL(matrox_cfbX_init); 203 204 static void matrox_accel_restore_maccess(struct matrox_fb_info *minfo) 205 { 206 mga_outl(M_MACCESS, minfo->accel.m_access); 207 mga_outl(M_PITCH, minfo->accel.m_pitch); 208 } 209 210 static void matrox_accel_bmove(struct matrox_fb_info *minfo, int vxres, int sy, 211 int sx, int dy, int dx, int height, int width) 212 { 213 int start, end; 214 CRITFLAGS 215 216 DBG(__func__) 217 218 CRITBEGIN 219 220 if ((dy < sy) || ((dy == sy) && (dx <= sx))) { 221 mga_fifo(4); 222 matrox_accel_restore_maccess(minfo); 223 mga_outl(M_DWGCTL, M_DWG_BITBLT | M_DWG_SHIFTZERO | M_DWG_SGNZERO | 224 M_DWG_BFCOL | M_DWG_REPLACE); 225 mga_outl(M_AR5, vxres); 226 width--; 227 start = sy*vxres+sx+curr_ydstorg(minfo); 228 end = start+width; 229 } else { 230 mga_fifo(5); 231 matrox_accel_restore_maccess(minfo); 232 mga_outl(M_DWGCTL, M_DWG_BITBLT | M_DWG_SHIFTZERO | M_DWG_BFCOL | M_DWG_REPLACE); 233 mga_outl(M_SGN, 5); 234 mga_outl(M_AR5, -vxres); 235 width--; 236 end = (sy+height-1)*vxres+sx+curr_ydstorg(minfo); 237 start = end+width; 238 dy += height-1; 239 } 240 mga_fifo(6); 241 matrox_accel_restore_maccess(minfo); 242 mga_outl(M_AR0, end); 243 mga_outl(M_AR3, start); 244 mga_outl(M_FXBNDRY, ((dx+width)<<16) | dx); 245 mga_ydstlen(dy, height); 246 WaitTillIdle(); 247 248 CRITEND 249 } 250 251 static void matrox_accel_bmove_lin(struct matrox_fb_info *minfo, int vxres, 252 int sy, int sx, int dy, int dx, int height, 253 int width) 254 { 255 int start, end; 256 CRITFLAGS 257 258 DBG(__func__) 259 260 CRITBEGIN 261 262 if ((dy < sy) || ((dy == sy) && (dx <= sx))) { 263 mga_fifo(4); 264 matrox_accel_restore_maccess(minfo); 265 mga_outl(M_DWGCTL, M_DWG_BITBLT | M_DWG_SHIFTZERO | M_DWG_SGNZERO | 266 M_DWG_BFCOL | M_DWG_REPLACE); 267 mga_outl(M_AR5, vxres); 268 width--; 269 start = sy*vxres+sx+curr_ydstorg(minfo); 270 end = start+width; 271 } else { 272 mga_fifo(5); 273 matrox_accel_restore_maccess(minfo); 274 mga_outl(M_DWGCTL, M_DWG_BITBLT | M_DWG_SHIFTZERO | M_DWG_BFCOL | M_DWG_REPLACE); 275 mga_outl(M_SGN, 5); 276 mga_outl(M_AR5, -vxres); 277 width--; 278 end = (sy+height-1)*vxres+sx+curr_ydstorg(minfo); 279 start = end+width; 280 dy += height-1; 281 } 282 mga_fifo(7); 283 matrox_accel_restore_maccess(minfo); 284 mga_outl(M_AR0, end); 285 mga_outl(M_AR3, start); 286 mga_outl(M_FXBNDRY, ((dx+width)<<16) | dx); 287 mga_outl(M_YDST, dy*vxres >> 5); 288 mga_outl(M_LEN | M_EXEC, height); 289 WaitTillIdle(); 290 291 CRITEND 292 } 293 294 static void matroxfb_cfb4_copyarea(struct fb_info* info, const struct fb_copyarea* area) { 295 struct matrox_fb_info *minfo = info2minfo(info); 296 297 if ((area->sx | area->dx | area->width) & 1) 298 cfb_copyarea(info, area); 299 else 300 matrox_accel_bmove_lin(minfo, minfo->fbcon.var.xres_virtual >> 1, area->sy, area->sx >> 1, area->dy, area->dx >> 1, area->height, area->width >> 1); 301 } 302 303 static void matroxfb_copyarea(struct fb_info* info, const struct fb_copyarea* area) { 304 struct matrox_fb_info *minfo = info2minfo(info); 305 306 matrox_accel_bmove(minfo, minfo->fbcon.var.xres_virtual, area->sy, area->sx, area->dy, area->dx, area->height, area->width); 307 } 308 309 static void matroxfb_accel_clear(struct matrox_fb_info *minfo, u_int32_t color, 310 int sy, int sx, int height, int width) 311 { 312 CRITFLAGS 313 314 DBG(__func__) 315 316 CRITBEGIN 317 318 mga_fifo(7); 319 matrox_accel_restore_maccess(minfo); 320 mga_outl(M_DWGCTL, minfo->accel.m_dwg_rect | M_DWG_REPLACE); 321 mga_outl(M_FCOL, color); 322 mga_outl(M_FXBNDRY, ((sx + width) << 16) | sx); 323 mga_ydstlen(sy, height); 324 WaitTillIdle(); 325 326 CRITEND 327 } 328 329 static void matroxfb_fillrect(struct fb_info* info, const struct fb_fillrect* rect) { 330 struct matrox_fb_info *minfo = info2minfo(info); 331 332 switch (rect->rop) { 333 case ROP_COPY: 334 matroxfb_accel_clear(minfo, ((u_int32_t *)info->pseudo_palette)[rect->color], rect->dy, rect->dx, rect->height, rect->width); 335 break; 336 } 337 } 338 339 static void matroxfb_cfb4_clear(struct matrox_fb_info *minfo, u_int32_t bgx, 340 int sy, int sx, int height, int width) 341 { 342 int whattodo; 343 CRITFLAGS 344 345 DBG(__func__) 346 347 CRITBEGIN 348 349 whattodo = 0; 350 if (sx & 1) { 351 sx ++; 352 if (!width) return; 353 width --; 354 whattodo = 1; 355 } 356 if (width & 1) { 357 whattodo |= 2; 358 } 359 width >>= 1; 360 sx >>= 1; 361 if (width) { 362 mga_fifo(7); 363 matrox_accel_restore_maccess(minfo); 364 mga_outl(M_DWGCTL, minfo->accel.m_dwg_rect | M_DWG_REPLACE2); 365 mga_outl(M_FCOL, bgx); 366 mga_outl(M_FXBNDRY, ((sx + width) << 16) | sx); 367 mga_outl(M_YDST, sy * minfo->fbcon.var.xres_virtual >> 6); 368 mga_outl(M_LEN | M_EXEC, height); 369 WaitTillIdle(); 370 } 371 if (whattodo) { 372 u_int32_t step = minfo->fbcon.var.xres_virtual >> 1; 373 vaddr_t vbase = minfo->video.vbase; 374 if (whattodo & 1) { 375 unsigned int uaddr = sy * step + sx - 1; 376 u_int32_t loop; 377 u_int8_t bgx2 = bgx & 0xF0; 378 for (loop = height; loop > 0; loop --) { 379 mga_writeb(vbase, uaddr, (mga_readb(vbase, uaddr) & 0x0F) | bgx2); 380 uaddr += step; 381 } 382 } 383 if (whattodo & 2) { 384 unsigned int uaddr = sy * step + sx + width; 385 u_int32_t loop; 386 u_int8_t bgx2 = bgx & 0x0F; 387 for (loop = height; loop > 0; loop --) { 388 mga_writeb(vbase, uaddr, (mga_readb(vbase, uaddr) & 0xF0) | bgx2); 389 uaddr += step; 390 } 391 } 392 } 393 394 CRITEND 395 } 396 397 static void matroxfb_cfb4_fillrect(struct fb_info* info, const struct fb_fillrect* rect) { 398 struct matrox_fb_info *minfo = info2minfo(info); 399 400 switch (rect->rop) { 401 case ROP_COPY: 402 matroxfb_cfb4_clear(minfo, ((u_int32_t *)info->pseudo_palette)[rect->color], rect->dy, rect->dx, rect->height, rect->width); 403 break; 404 } 405 } 406 407 static void matroxfb_1bpp_imageblit(struct matrox_fb_info *minfo, u_int32_t fgx, 408 u_int32_t bgx, const u_int8_t *chardata, 409 int width, int height, int yy, int xx) 410 { 411 u_int32_t step; 412 u_int32_t ydstlen; 413 u_int32_t xlen; 414 u_int32_t ar0; 415 u_int32_t charcell; 416 u_int32_t fxbndry; 417 vaddr_t mmio; 418 int easy; 419 CRITFLAGS 420 421 DBG_HEAVY(__func__); 422 423 step = (width + 7) >> 3; 424 charcell = height * step; 425 xlen = (charcell + 3) & ~3; 426 ydstlen = (yy << 16) | height; 427 if (width == step << 3) { 428 ar0 = height * width - 1; 429 easy = 1; 430 } else { 431 ar0 = width - 1; 432 easy = 0; 433 } 434 435 CRITBEGIN 436 437 mga_fifo(5); 438 matrox_accel_restore_maccess(minfo); 439 if (easy) 440 mga_outl(M_DWGCTL, M_DWG_ILOAD | M_DWG_SGNZERO | M_DWG_SHIFTZERO | M_DWG_BMONOWF | M_DWG_LINEAR | M_DWG_REPLACE); 441 else 442 mga_outl(M_DWGCTL, M_DWG_ILOAD | M_DWG_SGNZERO | M_DWG_SHIFTZERO | M_DWG_BMONOWF | M_DWG_REPLACE); 443 mga_outl(M_FCOL, fgx); 444 mga_outl(M_BCOL, bgx); 445 fxbndry = ((xx + width - 1) << 16) | xx; 446 mmio = minfo->mmio.vbase; 447 448 mga_fifo(8); 449 matrox_accel_restore_maccess(minfo); 450 mga_writel(mmio, M_FXBNDRY, fxbndry); 451 mga_writel(mmio, M_AR0, ar0); 452 mga_writel(mmio, M_AR3, 0); 453 if (easy) { 454 mga_writel(mmio, M_YDSTLEN | M_EXEC, ydstlen); 455 mga_memcpy_toio(mmio, chardata, xlen); 456 } else { 457 mga_writel(mmio, M_AR5, 0); 458 mga_writel(mmio, M_YDSTLEN | M_EXEC, ydstlen); 459 if ((step & 3) == 0) { 460 /* Great. Source has 32bit aligned lines, so we can feed them 461 directly to the accelerator. */ 462 mga_memcpy_toio(mmio, chardata, charcell); 463 } else if (step == 1) { 464 /* Special case for 1..8bit widths */ 465 while (height--) { 466 #if defined(__BIG_ENDIAN) 467 fb_writel((*chardata) << 24, mmio.vaddr); 468 #else 469 fb_writel(*chardata, mmio.vaddr); 470 #endif 471 chardata++; 472 } 473 } else if (step == 2) { 474 /* Special case for 9..15bit widths */ 475 while (height--) { 476 #if defined(__BIG_ENDIAN) 477 fb_writel((*(u_int16_t*)chardata) << 16, mmio.vaddr); 478 #else 479 fb_writel(*(u_int16_t*)chardata, mmio.vaddr); 480 #endif 481 chardata += 2; 482 } 483 } else { 484 /* Tell... well, why bother... */ 485 while (height--) { 486 size_t i; 487 488 for (i = 0; i < step; i += 4) { 489 /* Hope that there are at least three readable bytes beyond the end of bitmap */ 490 fb_writel(get_unaligned((u_int32_t*)(chardata + i)),mmio.vaddr); 491 } 492 chardata += step; 493 } 494 } 495 } 496 WaitTillIdle(); 497 CRITEND 498 } 499 500 501 static void matroxfb_imageblit(struct fb_info* info, const struct fb_image* image) { 502 struct matrox_fb_info *minfo = info2minfo(info); 503 504 DBG_HEAVY(__func__); 505 506 if (image->depth == 1) { 507 u_int32_t fgx, bgx; 508 509 fgx = ((u_int32_t*)info->pseudo_palette)[image->fg_color]; 510 bgx = ((u_int32_t*)info->pseudo_palette)[image->bg_color]; 511 matroxfb_1bpp_imageblit(minfo, fgx, bgx, image->data, image->width, image->height, image->dy, image->dx); 512 } else { 513 /* Danger! image->depth is useless: logo painting code always 514 passes framebuffer color depth here, although logo data are 515 always 8bpp and info->pseudo_palette is changed to contain 516 logo palette to be used (but only for true/direct-color... sic...). 517 So do it completely in software... */ 518 cfb_imageblit(info, image); 519 } 520 } 521 522 MODULE_DESCRIPTION("Accelerated fbops for Matrox Millennium/Mystique/G100/G200/G400/G450/G550"); 523 MODULE_LICENSE("GPL"); 524