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 "matroxfb_accel.h" 81 #include "matroxfb_DAC1064.h" 82 #include "matroxfb_Ti3026.h" 83 #include "matroxfb_misc.h" 84 85 #define curr_ydstorg(x) ((x)->curr.ydstorg.pixels) 86 87 #define mga_ydstlen(y,l) mga_outl(M_YDSTLEN | M_EXEC, ((y) << 16) | (l)) 88 89 static inline void matrox_cfb4_pal(u_int32_t* pal) { 90 unsigned int i; 91 92 for (i = 0; i < 16; i++) { 93 pal[i] = i * 0x11111111U; 94 } 95 } 96 97 static inline void matrox_cfb8_pal(u_int32_t* pal) { 98 unsigned int i; 99 100 for (i = 0; i < 16; i++) { 101 pal[i] = i * 0x01010101U; 102 } 103 } 104 105 static void matroxfb_copyarea(struct fb_info* info, const struct fb_copyarea* area); 106 static void matroxfb_fillrect(struct fb_info* info, const struct fb_fillrect* rect); 107 static void matroxfb_imageblit(struct fb_info* info, const struct fb_image* image); 108 static void matroxfb_cfb4_fillrect(struct fb_info* info, const struct fb_fillrect* rect); 109 static void matroxfb_cfb4_copyarea(struct fb_info* info, const struct fb_copyarea* area); 110 111 void matrox_cfbX_init(struct matrox_fb_info *minfo) 112 { 113 u_int32_t maccess; 114 u_int32_t mpitch; 115 u_int32_t mopmode; 116 int accel; 117 118 DBG(__func__) 119 120 mpitch = minfo->fbcon.var.xres_virtual; 121 122 minfo->fbops.fb_copyarea = cfb_copyarea; 123 minfo->fbops.fb_fillrect = cfb_fillrect; 124 minfo->fbops.fb_imageblit = cfb_imageblit; 125 minfo->fbops.fb_cursor = NULL; 126 127 accel = (minfo->fbcon.var.accel_flags & FB_ACCELF_TEXT) == FB_ACCELF_TEXT; 128 129 switch (minfo->fbcon.var.bits_per_pixel) { 130 case 4: maccess = 0x00000000; /* accelerate as 8bpp video */ 131 mpitch = (mpitch >> 1) | 0x8000; /* disable linearization */ 132 mopmode = M_OPMODE_4BPP; 133 matrox_cfb4_pal(minfo->cmap); 134 if (accel && !(mpitch & 1)) { 135 minfo->fbops.fb_copyarea = matroxfb_cfb4_copyarea; 136 minfo->fbops.fb_fillrect = matroxfb_cfb4_fillrect; 137 } 138 break; 139 case 8: maccess = 0x00000000; 140 mopmode = M_OPMODE_8BPP; 141 matrox_cfb8_pal(minfo->cmap); 142 if (accel) { 143 minfo->fbops.fb_copyarea = matroxfb_copyarea; 144 minfo->fbops.fb_fillrect = matroxfb_fillrect; 145 minfo->fbops.fb_imageblit = matroxfb_imageblit; 146 } 147 break; 148 case 16: if (minfo->fbcon.var.green.length == 5) 149 maccess = 0xC0000001; 150 else 151 maccess = 0x40000001; 152 mopmode = M_OPMODE_16BPP; 153 if (accel) { 154 minfo->fbops.fb_copyarea = matroxfb_copyarea; 155 minfo->fbops.fb_fillrect = matroxfb_fillrect; 156 minfo->fbops.fb_imageblit = matroxfb_imageblit; 157 } 158 break; 159 case 24: maccess = 0x00000003; 160 mopmode = M_OPMODE_24BPP; 161 if (accel) { 162 minfo->fbops.fb_copyarea = matroxfb_copyarea; 163 minfo->fbops.fb_fillrect = matroxfb_fillrect; 164 minfo->fbops.fb_imageblit = matroxfb_imageblit; 165 } 166 break; 167 case 32: maccess = 0x00000002; 168 mopmode = M_OPMODE_32BPP; 169 if (accel) { 170 minfo->fbops.fb_copyarea = matroxfb_copyarea; 171 minfo->fbops.fb_fillrect = matroxfb_fillrect; 172 minfo->fbops.fb_imageblit = matroxfb_imageblit; 173 } 174 break; 175 default: maccess = 0x00000000; 176 mopmode = 0x00000000; 177 break; /* turn off acceleration!!! */ 178 } 179 mga_fifo(8); 180 mga_outl(M_PITCH, mpitch); 181 mga_outl(M_YDSTORG, curr_ydstorg(minfo)); 182 if (minfo->capable.plnwt) 183 mga_outl(M_PLNWT, -1); 184 if (minfo->capable.srcorg) { 185 mga_outl(M_SRCORG, 0); 186 mga_outl(M_DSTORG, 0); 187 } 188 mga_outl(M_OPMODE, mopmode); 189 mga_outl(M_CXBNDRY, 0xFFFF0000); 190 mga_outl(M_YTOP, 0); 191 mga_outl(M_YBOT, 0x01FFFFFF); 192 mga_outl(M_MACCESS, maccess); 193 minfo->accel.m_dwg_rect = M_DWG_TRAP | M_DWG_SOLID | M_DWG_ARZERO | M_DWG_SGNZERO | M_DWG_SHIFTZERO; 194 if (isMilleniumII(minfo)) minfo->accel.m_dwg_rect |= M_DWG_TRANSC; 195 minfo->accel.m_opmode = mopmode; 196 minfo->accel.m_access = maccess; 197 minfo->accel.m_pitch = mpitch; 198 } 199 200 EXPORT_SYMBOL(matrox_cfbX_init); 201 202 static void matrox_accel_restore_maccess(struct matrox_fb_info *minfo) 203 { 204 mga_outl(M_MACCESS, minfo->accel.m_access); 205 mga_outl(M_PITCH, minfo->accel.m_pitch); 206 } 207 208 static void matrox_accel_bmove(struct matrox_fb_info *minfo, int vxres, int sy, 209 int sx, int dy, int dx, int height, int width) 210 { 211 int start, end; 212 CRITFLAGS 213 214 DBG(__func__) 215 216 CRITBEGIN 217 218 if ((dy < sy) || ((dy == sy) && (dx <= sx))) { 219 mga_fifo(4); 220 matrox_accel_restore_maccess(minfo); 221 mga_outl(M_DWGCTL, M_DWG_BITBLT | M_DWG_SHIFTZERO | M_DWG_SGNZERO | 222 M_DWG_BFCOL | M_DWG_REPLACE); 223 mga_outl(M_AR5, vxres); 224 width--; 225 start = sy*vxres+sx+curr_ydstorg(minfo); 226 end = start+width; 227 } else { 228 mga_fifo(5); 229 matrox_accel_restore_maccess(minfo); 230 mga_outl(M_DWGCTL, M_DWG_BITBLT | M_DWG_SHIFTZERO | M_DWG_BFCOL | M_DWG_REPLACE); 231 mga_outl(M_SGN, 5); 232 mga_outl(M_AR5, -vxres); 233 width--; 234 end = (sy+height-1)*vxres+sx+curr_ydstorg(minfo); 235 start = end+width; 236 dy += height-1; 237 } 238 mga_fifo(6); 239 matrox_accel_restore_maccess(minfo); 240 mga_outl(M_AR0, end); 241 mga_outl(M_AR3, start); 242 mga_outl(M_FXBNDRY, ((dx+width)<<16) | dx); 243 mga_ydstlen(dy, height); 244 WaitTillIdle(); 245 246 CRITEND 247 } 248 249 static void matrox_accel_bmove_lin(struct matrox_fb_info *minfo, int vxres, 250 int sy, int sx, int dy, int dx, int height, 251 int width) 252 { 253 int start, end; 254 CRITFLAGS 255 256 DBG(__func__) 257 258 CRITBEGIN 259 260 if ((dy < sy) || ((dy == sy) && (dx <= sx))) { 261 mga_fifo(4); 262 matrox_accel_restore_maccess(minfo); 263 mga_outl(M_DWGCTL, M_DWG_BITBLT | M_DWG_SHIFTZERO | M_DWG_SGNZERO | 264 M_DWG_BFCOL | M_DWG_REPLACE); 265 mga_outl(M_AR5, vxres); 266 width--; 267 start = sy*vxres+sx+curr_ydstorg(minfo); 268 end = start+width; 269 } else { 270 mga_fifo(5); 271 matrox_accel_restore_maccess(minfo); 272 mga_outl(M_DWGCTL, M_DWG_BITBLT | M_DWG_SHIFTZERO | M_DWG_BFCOL | M_DWG_REPLACE); 273 mga_outl(M_SGN, 5); 274 mga_outl(M_AR5, -vxres); 275 width--; 276 end = (sy+height-1)*vxres+sx+curr_ydstorg(minfo); 277 start = end+width; 278 dy += height-1; 279 } 280 mga_fifo(7); 281 matrox_accel_restore_maccess(minfo); 282 mga_outl(M_AR0, end); 283 mga_outl(M_AR3, start); 284 mga_outl(M_FXBNDRY, ((dx+width)<<16) | dx); 285 mga_outl(M_YDST, dy*vxres >> 5); 286 mga_outl(M_LEN | M_EXEC, height); 287 WaitTillIdle(); 288 289 CRITEND 290 } 291 292 static void matroxfb_cfb4_copyarea(struct fb_info* info, const struct fb_copyarea* area) { 293 struct matrox_fb_info *minfo = info2minfo(info); 294 295 if ((area->sx | area->dx | area->width) & 1) 296 cfb_copyarea(info, area); 297 else 298 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); 299 } 300 301 static void matroxfb_copyarea(struct fb_info* info, const struct fb_copyarea* area) { 302 struct matrox_fb_info *minfo = info2minfo(info); 303 304 matrox_accel_bmove(minfo, minfo->fbcon.var.xres_virtual, area->sy, area->sx, area->dy, area->dx, area->height, area->width); 305 } 306 307 static void matroxfb_accel_clear(struct matrox_fb_info *minfo, u_int32_t color, 308 int sy, int sx, int height, int width) 309 { 310 CRITFLAGS 311 312 DBG(__func__) 313 314 CRITBEGIN 315 316 mga_fifo(7); 317 matrox_accel_restore_maccess(minfo); 318 mga_outl(M_DWGCTL, minfo->accel.m_dwg_rect | M_DWG_REPLACE); 319 mga_outl(M_FCOL, color); 320 mga_outl(M_FXBNDRY, ((sx + width) << 16) | sx); 321 mga_ydstlen(sy, height); 322 WaitTillIdle(); 323 324 CRITEND 325 } 326 327 static void matroxfb_fillrect(struct fb_info* info, const struct fb_fillrect* rect) { 328 struct matrox_fb_info *minfo = info2minfo(info); 329 330 switch (rect->rop) { 331 case ROP_COPY: 332 matroxfb_accel_clear(minfo, ((u_int32_t *)info->pseudo_palette)[rect->color], rect->dy, rect->dx, rect->height, rect->width); 333 break; 334 } 335 } 336 337 static void matroxfb_cfb4_clear(struct matrox_fb_info *minfo, u_int32_t bgx, 338 int sy, int sx, int height, int width) 339 { 340 int whattodo; 341 CRITFLAGS 342 343 DBG(__func__) 344 345 CRITBEGIN 346 347 whattodo = 0; 348 if (sx & 1) { 349 sx ++; 350 if (!width) return; 351 width --; 352 whattodo = 1; 353 } 354 if (width & 1) { 355 whattodo |= 2; 356 } 357 width >>= 1; 358 sx >>= 1; 359 if (width) { 360 mga_fifo(7); 361 matrox_accel_restore_maccess(minfo); 362 mga_outl(M_DWGCTL, minfo->accel.m_dwg_rect | M_DWG_REPLACE2); 363 mga_outl(M_FCOL, bgx); 364 mga_outl(M_FXBNDRY, ((sx + width) << 16) | sx); 365 mga_outl(M_YDST, sy * minfo->fbcon.var.xres_virtual >> 6); 366 mga_outl(M_LEN | M_EXEC, height); 367 WaitTillIdle(); 368 } 369 if (whattodo) { 370 u_int32_t step = minfo->fbcon.var.xres_virtual >> 1; 371 vaddr_t vbase = minfo->video.vbase; 372 if (whattodo & 1) { 373 unsigned int uaddr = sy * step + sx - 1; 374 u_int32_t loop; 375 u_int8_t bgx2 = bgx & 0xF0; 376 for (loop = height; loop > 0; loop --) { 377 mga_writeb(vbase, uaddr, (mga_readb(vbase, uaddr) & 0x0F) | bgx2); 378 uaddr += step; 379 } 380 } 381 if (whattodo & 2) { 382 unsigned int uaddr = sy * step + sx + width; 383 u_int32_t loop; 384 u_int8_t bgx2 = bgx & 0x0F; 385 for (loop = height; loop > 0; loop --) { 386 mga_writeb(vbase, uaddr, (mga_readb(vbase, uaddr) & 0xF0) | bgx2); 387 uaddr += step; 388 } 389 } 390 } 391 392 CRITEND 393 } 394 395 static void matroxfb_cfb4_fillrect(struct fb_info* info, const struct fb_fillrect* rect) { 396 struct matrox_fb_info *minfo = info2minfo(info); 397 398 switch (rect->rop) { 399 case ROP_COPY: 400 matroxfb_cfb4_clear(minfo, ((u_int32_t *)info->pseudo_palette)[rect->color], rect->dy, rect->dx, rect->height, rect->width); 401 break; 402 } 403 } 404 405 static void matroxfb_1bpp_imageblit(struct matrox_fb_info *minfo, u_int32_t fgx, 406 u_int32_t bgx, const u_int8_t *chardata, 407 int width, int height, int yy, int xx) 408 { 409 u_int32_t step; 410 u_int32_t ydstlen; 411 u_int32_t xlen; 412 u_int32_t ar0; 413 u_int32_t charcell; 414 u_int32_t fxbndry; 415 vaddr_t mmio; 416 int easy; 417 CRITFLAGS 418 419 DBG_HEAVY(__func__); 420 421 step = (width + 7) >> 3; 422 charcell = height * step; 423 xlen = (charcell + 3) & ~3; 424 ydstlen = (yy << 16) | height; 425 if (width == step << 3) { 426 ar0 = height * width - 1; 427 easy = 1; 428 } else { 429 ar0 = width - 1; 430 easy = 0; 431 } 432 433 CRITBEGIN 434 435 mga_fifo(5); 436 matrox_accel_restore_maccess(minfo); 437 if (easy) 438 mga_outl(M_DWGCTL, M_DWG_ILOAD | M_DWG_SGNZERO | M_DWG_SHIFTZERO | M_DWG_BMONOWF | M_DWG_LINEAR | M_DWG_REPLACE); 439 else 440 mga_outl(M_DWGCTL, M_DWG_ILOAD | M_DWG_SGNZERO | M_DWG_SHIFTZERO | M_DWG_BMONOWF | M_DWG_REPLACE); 441 mga_outl(M_FCOL, fgx); 442 mga_outl(M_BCOL, bgx); 443 fxbndry = ((xx + width - 1) << 16) | xx; 444 mmio = minfo->mmio.vbase; 445 446 mga_fifo(8); 447 matrox_accel_restore_maccess(minfo); 448 mga_writel(mmio, M_FXBNDRY, fxbndry); 449 mga_writel(mmio, M_AR0, ar0); 450 mga_writel(mmio, M_AR3, 0); 451 if (easy) { 452 mga_writel(mmio, M_YDSTLEN | M_EXEC, ydstlen); 453 mga_memcpy_toio(mmio, chardata, xlen); 454 } else { 455 mga_writel(mmio, M_AR5, 0); 456 mga_writel(mmio, M_YDSTLEN | M_EXEC, ydstlen); 457 if ((step & 3) == 0) { 458 /* Great. Source has 32bit aligned lines, so we can feed them 459 directly to the accelerator. */ 460 mga_memcpy_toio(mmio, chardata, charcell); 461 } else if (step == 1) { 462 /* Special case for 1..8bit widths */ 463 while (height--) { 464 #if defined(__BIG_ENDIAN) 465 fb_writel((*chardata) << 24, mmio.vaddr); 466 #else 467 fb_writel(*chardata, mmio.vaddr); 468 #endif 469 chardata++; 470 } 471 } else if (step == 2) { 472 /* Special case for 9..15bit widths */ 473 while (height--) { 474 #if defined(__BIG_ENDIAN) 475 fb_writel((*(u_int16_t*)chardata) << 16, mmio.vaddr); 476 #else 477 fb_writel(*(u_int16_t*)chardata, mmio.vaddr); 478 #endif 479 chardata += 2; 480 } 481 } else { 482 /* Tell... well, why bother... */ 483 while (height--) { 484 size_t i; 485 486 for (i = 0; i < step; i += 4) { 487 /* Hope that there are at least three readable bytes beyond the end of bitmap */ 488 fb_writel(get_unaligned((u_int32_t*)(chardata + i)),mmio.vaddr); 489 } 490 chardata += step; 491 } 492 } 493 } 494 WaitTillIdle(); 495 CRITEND 496 } 497 498 499 static void matroxfb_imageblit(struct fb_info* info, const struct fb_image* image) { 500 struct matrox_fb_info *minfo = info2minfo(info); 501 502 DBG_HEAVY(__func__); 503 504 if (image->depth == 1) { 505 u_int32_t fgx, bgx; 506 507 fgx = ((u_int32_t*)info->pseudo_palette)[image->fg_color]; 508 bgx = ((u_int32_t*)info->pseudo_palette)[image->bg_color]; 509 matroxfb_1bpp_imageblit(minfo, fgx, bgx, image->data, image->width, image->height, image->dy, image->dx); 510 } else { 511 /* Danger! image->depth is useless: logo painting code always 512 passes framebuffer color depth here, although logo data are 513 always 8bpp and info->pseudo_palette is changed to contain 514 logo palette to be used (but only for true/direct-color... sic...). 515 So do it completely in software... */ 516 cfb_imageblit(info, image); 517 } 518 } 519 520 MODULE_DESCRIPTION("Accelerated fbops for Matrox Millennium/Mystique/G100/G200/G400/G450/G550"); 521 MODULE_LICENSE("GPL"); 522