1 /* 2 * ATI Radeon [789]XXX vga driver 3 * see /sys/src/cmd/aux/vga/radeon.c 4 */ 5 #include "u.h" 6 #include "../port/lib.h" 7 #include "mem.h" 8 #include "dat.h" 9 #include "fns.h" 10 #include "io.h" 11 #include "../port/error.h" 12 13 #define Image IMAGE 14 #include <draw.h> 15 #include <memdraw.h> 16 #include <cursor.h> 17 #include "screen.h" 18 19 #include "/sys/src/cmd/aux/vga/radeon.h" /* ugh */ 20 21 /* #define HW_ACCEL */ 22 23 enum { 24 Kilo = 1024, 25 Meg = Kilo * Kilo, 26 }; 27 28 static Pcidev* 29 radeonpci(void) 30 { 31 static Pcidev *p = nil; 32 struct pciids *ids; 33 34 while ((p = pcimatch(p, ATI_PCIVID, 0)) != nil) 35 for (ids = radeon_pciids; ids->did; ids++) 36 if (ids->did == p->did) 37 return p; 38 return nil; 39 } 40 41 /* mmio access */ 42 43 static void 44 OUTREG8(ulong mmio, ulong offset, uchar val) 45 { 46 ((uchar*)KADDR((mmio + offset)))[0] = val; 47 } 48 49 static void 50 OUTREG(ulong mmio, ulong offset, ulong val) 51 { 52 ((ulong*)KADDR((mmio + offset)))[0] = val; 53 } 54 55 static ulong 56 INREG(ulong mmio, ulong offset) 57 { 58 return ((ulong*)KADDR((mmio + offset)))[0]; 59 } 60 61 static void 62 OUTREGP(ulong mmio, ulong offset, ulong val, ulong mask) 63 { 64 OUTREG(mmio, offset, (INREG(mmio, offset) & mask) | val); 65 } 66 67 static void 68 OUTPLL(ulong mmio, ulong offset, ulong val) 69 { 70 OUTREG8(mmio, CLOCK_CNTL_INDEX, (offset & 0x3f) | PLL_WR_EN); 71 OUTREG(mmio, CLOCK_CNTL_DATA, val); 72 } 73 74 static ulong 75 INPLL(ulong mmio, ulong offset) 76 { 77 OUTREG8(mmio, CLOCK_CNTL_INDEX, offset & 0x3f); 78 return INREG(mmio, CLOCK_CNTL_DATA); 79 } 80 81 static void 82 OUTPLLP(ulong mmio, ulong offset, ulong val, ulong mask) 83 { 84 OUTPLL(mmio, offset, (INPLL(mmio, offset) & mask) | val); 85 } 86 87 static void 88 radeonlinear(VGAscr *, int, int) 89 { 90 } 91 92 static void 93 radeonenable(VGAscr *scr) 94 { 95 Pcidev *p; 96 97 if (scr->mmio) 98 return; 99 p = radeonpci(); 100 if (p == nil) 101 return; 102 scr->id = p->did; 103 scr->pci = p; 104 105 scr->mmio = vmap(p->mem[2].bar & ~0x0f, p->mem[2].size); 106 if(scr->mmio == 0) 107 return; 108 addvgaseg("radeonmmio", p->mem[2].bar & ~0x0f, p->mem[2].size); 109 110 vgalinearpci(scr); 111 if(scr->apsize) 112 addvgaseg("radeonscreen", scr->paddr, scr->apsize); 113 } 114 115 static void 116 radeoncurload(VGAscr *scr, Cursor *curs) 117 { 118 int x, y; 119 ulong *p; 120 121 if(scr->mmio == nil) 122 return; 123 124 p = (ulong*)KADDR(scr->storage); 125 126 for(y = 0; y < 64; y++){ 127 int cv, sv; 128 129 if (y < 16) { 130 cv = curs->clr[2*y] << 8 | curs->clr[2*y+1]; 131 sv = curs->set[2*y] << 8 | curs->set[2*y+1]; 132 } else 133 cv = sv = 0; 134 135 for(x = 0; x < 64; x++){ 136 ulong col = 0; 137 int c, s; 138 139 if (x < 16) { 140 c = (cv >> (15 - x)) & 1; 141 s = (sv >> (15 - x)) & 1; 142 } else 143 c = s = 0; 144 145 switch(c | s<<1) { 146 case 0: 147 col = 0; 148 break; 149 case 1: 150 col = ~0ul; /* white */ 151 break; 152 case 2: 153 case 3: 154 col = 0xff000000; /* black */ 155 break; 156 } 157 158 *p++ = col; 159 } 160 } 161 162 scr->offset.x = curs->offset.x; 163 scr->offset.y = curs->offset.y; 164 } 165 166 static int 167 radeoncurmove(VGAscr *scr, Point p) 168 { 169 int x, y, ox, oy; 170 static ulong storage = 0; 171 172 if(scr->mmio == nil) 173 return 1; 174 175 if (storage == 0) 176 storage = scr->apsize - 1*Meg; 177 178 x = p.x + scr->offset.x; 179 y = p.y + scr->offset.y; 180 ox = oy = 0; 181 182 if (x < 0) { 183 ox = -x - 1; 184 x = 0; 185 } 186 if (y < 0) { 187 oy = -y - 1; 188 y = 0; 189 } 190 191 OUTREG((ulong)scr->mmio, CUR_OFFSET, storage + oy * 256); 192 OUTREG((ulong)scr->mmio, CUR_HORZ_VERT_OFF, 193 (ox & 0x7fff) << 16 | (oy & 0x7fff)); 194 OUTREG((ulong)scr->mmio, CUR_HORZ_VERT_POSN, 195 (x & 0x7fff) << 16 | (y & 0x7fff)); 196 return 0; 197 } 198 199 static void 200 radeoncurdisable(VGAscr *scr) 201 { 202 if(scr->mmio == nil) 203 return; 204 OUTREGP((ulong)scr->mmio, CRTC_GEN_CNTL, 0, ~CRTC_CUR_EN); 205 } 206 207 static void 208 radeoncurenable(VGAscr *scr) 209 { 210 ulong storage; 211 212 if(scr->mmio == 0) 213 return; 214 215 radeoncurdisable(scr); 216 storage = scr->apsize - 1*Meg; 217 scr->storage = (ulong)KADDR(scr->paddr + storage); 218 radeoncurload(scr, &arrow); 219 radeoncurmove(scr, ZP); 220 221 OUTREGP((ulong)scr->mmio, CRTC_GEN_CNTL, CRTC_CUR_EN | 2<<20, 222 ~(CRTC_CUR_EN | 3<<20)); 223 } 224 225 /* hw blank */ 226 227 static void 228 radeonblank(VGAscr* scr, int blank) 229 { 230 ulong mask; 231 char *cp; 232 233 if (scr->mmio == 0) 234 return; 235 236 // iprint("radeon: hwblank(%d)\n", blank); 237 238 mask = CRTC_DISPLAY_DIS | CRTC_HSYNC_DIS | CRTC_VSYNC_DIS; 239 if (blank == 0) { 240 OUTREGP((ulong)scr->mmio, CRTC_EXT_CNTL, 0, ~mask); 241 return; 242 } 243 244 cp = getconf("*dpms"); 245 if (cp) { 246 if (strcmp(cp, "standby") == 0) 247 OUTREGP((ulong)scr->mmio, CRTC_EXT_CNTL, 248 CRTC_DISPLAY_DIS | CRTC_HSYNC_DIS, ~mask); 249 else if (strcmp(cp, "suspend") == 0) 250 OUTREGP((ulong)scr->mmio, CRTC_EXT_CNTL, 251 CRTC_DISPLAY_DIS | CRTC_VSYNC_DIS, ~mask); 252 else if (strcmp(cp, "off") == 0) 253 OUTREGP((ulong)scr->mmio, CRTC_EXT_CNTL, mask, ~mask); 254 return; 255 } 256 257 OUTREGP((ulong)scr->mmio, CRTC_EXT_CNTL, mask, ~mask); 258 } 259 260 /* hw acceleration */ 261 262 static void 263 radeonwaitfifo(VGAscr *scr, int entries) 264 { 265 int i; 266 267 for (i = 0; i < 2000000; i++) 268 if (INREG((ulong)scr->mmio, RBBM_STATUS) & RBBM_FIFOCNT_MASK >= 269 entries) 270 return; 271 iprint("radeon: fifo timeout\n"); 272 } 273 274 static void 275 radeonwaitidle(VGAscr *scr) 276 { 277 radeonwaitfifo(scr, 64); 278 279 for (; ; ) { 280 int i; 281 282 for (i = 0; i < 2000000; i++) 283 if (!(INREG((ulong)scr->mmio, RBBM_STATUS) & RBBM_ACTIVE)) 284 return; 285 286 iprint("radeon: idle timed out: %uld entries, stat=0x%.8ulx\n", 287 INREG((ulong)scr->mmio, RBBM_STATUS) & RBBM_FIFOCNT_MASK, 288 INREG((ulong)scr->mmio, RBBM_STATUS)); 289 } 290 } 291 292 static ulong dp_gui_master_cntl = 0; 293 294 static int 295 radeonfill(VGAscr *scr, Rectangle r, ulong color) 296 { 297 if (scr->mmio == nil) 298 return 0; 299 300 radeonwaitfifo(scr, 6); 301 OUTREG((ulong)scr->mmio, DP_GUI_MASTER_CNTL, 302 dp_gui_master_cntl | GMC_BRUSH_SOLID_COLOR | 303 GMC_SRC_DATATYPE_COLOR | ROP3_P); 304 OUTREG((ulong)scr->mmio, DP_BRUSH_FRGD_CLR, color); 305 OUTREG((ulong)scr->mmio, DP_WRITE_MASK, ~0ul); 306 OUTREG((ulong)scr->mmio, DP_CNTL, 307 DST_X_LEFT_TO_RIGHT | DST_Y_TOP_TO_BOTTOM); 308 OUTREG((ulong)scr->mmio, DST_Y_X, r.min.y << 16 | r.min.x); 309 OUTREG((ulong)scr->mmio, DST_WIDTH_HEIGHT, Dx(r) << 16 | Dy(r)); 310 311 radeonwaitidle(scr); 312 return 1; 313 } 314 315 static int 316 radeonscroll(VGAscr*scr, Rectangle dst, Rectangle src) 317 { 318 int xs, ys, xd, yd, w, h; 319 ulong dp_cntl = 0x20; 320 321 if (scr->mmio == nil) 322 return 0; 323 324 // iprint("radeon: hwscroll(dst:%R, src:%R)\n", dst, src); 325 326 xd = dst.min.x; 327 yd = dst.min.y; 328 xs = src.min.x; 329 ys = src.min.y; 330 w = Dx(dst); 331 h = Dy(dst); 332 333 if (ys < yd) { 334 ys += h - 1; 335 yd += h - 1; 336 } else 337 dp_cntl |= DST_Y_TOP_TO_BOTTOM; 338 339 if (xs < xd) { 340 xs += w - 1; 341 xd += w - 1; 342 } else 343 dp_cntl |= DST_X_LEFT_TO_RIGHT; 344 345 radeonwaitfifo(scr, 6); 346 OUTREG((ulong)scr->mmio, DP_GUI_MASTER_CNTL, dp_gui_master_cntl | 347 GMC_BRUSH_NONE | GMC_SRC_DATATYPE_COLOR | DP_SRC_SOURCE_MEMORY | 348 ROP3_S); 349 OUTREG((ulong)scr->mmio, DP_WRITE_MASK, ~0ul); 350 OUTREG((ulong)scr->mmio, DP_CNTL, dp_cntl); 351 OUTREG((ulong)scr->mmio, SRC_Y_X, ys << 16 | xs); 352 OUTREG((ulong)scr->mmio, DST_Y_X, yd << 16 | xd); 353 OUTREG((ulong)scr->mmio, DST_WIDTH_HEIGHT, w << 16 | h); 354 355 radeonwaitidle(scr); 356 357 // iprint("radeon: hwscroll(xs=%d ys=%d xd=%d yd=%d w=%d h=%d)\n", 358 // xs, ys, xd, yd, w, h); 359 return 1; 360 } 361 362 static void 363 radeondrawinit(VGAscr*scr) 364 { 365 ulong bpp, dtype, i, pitch, clock_cntl_index, mclk_cntl, rbbm_soft_reset; 366 367 if (scr->mmio == 0) 368 return; 369 370 switch (scr->gscreen->depth) { 371 case 6: 372 case 8: 373 dtype = 2; 374 bpp = 1; 375 break; 376 case 15: 377 dtype = 3; 378 bpp = 2; 379 break; 380 case 16: 381 dtype = 4; 382 bpp = 2; 383 break; 384 case 32: 385 dtype = 6; 386 bpp = 4; 387 break; 388 default: 389 return; 390 } 391 392 /* disable 3D */ 393 OUTREG((ulong)scr->mmio, RB3D_CNTL, 0); 394 395 /* flush engine */ 396 OUTREGP((ulong)scr->mmio, RB2D_DSTCACHE_CTLSTAT, 397 RB2D_DC_FLUSH_ALL, ~RB2D_DC_FLUSH_ALL); 398 for (i = 0; i < 2000000; i++) 399 if (!(INREG((ulong)scr->mmio, RB2D_DSTCACHE_CTLSTAT) & 400 RB2D_DC_BUSY)) 401 break; 402 403 /* reset 2D engine */ 404 clock_cntl_index = INREG((ulong)scr->mmio, CLOCK_CNTL_INDEX); 405 406 mclk_cntl = INPLL((ulong)scr->mmio, MCLK_CNTL); 407 OUTPLL((ulong)scr->mmio, MCLK_CNTL, mclk_cntl | FORCEON_MCLKA | 408 FORCEON_MCLKB | FORCEON_YCLKA | FORCEON_YCLKB | FORCEON_MC | 409 FORCEON_AIC); 410 rbbm_soft_reset = INREG((ulong)scr->mmio, RBBM_SOFT_RESET); 411 412 OUTREG((ulong)scr->mmio, RBBM_SOFT_RESET, rbbm_soft_reset | 413 SOFT_RESET_CP | SOFT_RESET_HI | SOFT_RESET_SE | SOFT_RESET_RE | 414 SOFT_RESET_PP | SOFT_RESET_E2 | SOFT_RESET_RB); 415 INREG((ulong)scr->mmio, RBBM_SOFT_RESET); 416 OUTREG((ulong)scr->mmio, RBBM_SOFT_RESET, rbbm_soft_reset & 417 ~(SOFT_RESET_CP | SOFT_RESET_HI | SOFT_RESET_SE | SOFT_RESET_RE | 418 SOFT_RESET_PP | SOFT_RESET_E2 | SOFT_RESET_RB)); 419 INREG((ulong)scr->mmio, RBBM_SOFT_RESET); 420 421 OUTPLL((ulong)scr->mmio, MCLK_CNTL, mclk_cntl); 422 OUTREG((ulong)scr->mmio, CLOCK_CNTL_INDEX, clock_cntl_index); 423 424 /* init 2D engine */ 425 radeonwaitfifo(scr, 1); 426 OUTREG((ulong)scr->mmio, RB2D_DSTCACHE_MODE, 0); 427 428 pitch = Dx(scr->gscreen->r) * bpp; 429 radeonwaitfifo(scr, 4); 430 OUTREG((ulong)scr->mmio, DEFAULT_PITCH, pitch); 431 OUTREG((ulong)scr->mmio, DST_PITCH, pitch); 432 OUTREG((ulong)scr->mmio, SRC_PITCH, pitch); 433 OUTREG((ulong)scr->mmio, DST_PITCH_OFFSET_C, 0); 434 435 radeonwaitfifo(scr, 3); 436 OUTREG((ulong)scr->mmio, DEFAULT_OFFSET, 0); 437 OUTREG((ulong)scr->mmio, DST_OFFSET, 0); 438 OUTREG((ulong)scr->mmio, SRC_OFFSET, 0); 439 440 radeonwaitfifo(scr, 1); 441 OUTREGP((ulong)scr->mmio, DP_DATATYPE, 0, ~HOST_BIG_ENDIAN_EN); 442 443 radeonwaitfifo(scr, 1); 444 OUTREG((ulong)scr->mmio, DEFAULT_SC_BOTTOM_RIGHT, 445 DEFAULT_SC_RIGHT_MAX | DEFAULT_SC_BOTTOM_MAX); 446 447 dp_gui_master_cntl = dtype << GMC_DST_DATATYPE_SHIFT | 448 GMC_SRC_PITCH_OFFSET_CNTL | GMC_DST_PITCH_OFFSET_CNTL | 449 GMC_CLR_CMP_CNTL_DIS; 450 radeonwaitfifo(scr, 1); 451 OUTREG((ulong)scr->mmio, DP_GUI_MASTER_CNTL, 452 dp_gui_master_cntl | GMC_BRUSH_SOLID_COLOR | GMC_SRC_DATATYPE_COLOR); 453 454 radeonwaitfifo(scr, 7); 455 OUTREG((ulong)scr->mmio, DST_LINE_START, 0); 456 OUTREG((ulong)scr->mmio, DST_LINE_END, 0); 457 OUTREG((ulong)scr->mmio, DP_BRUSH_FRGD_CLR, ~0ul); 458 OUTREG((ulong)scr->mmio, DP_BRUSH_BKGD_CLR, 0); 459 OUTREG((ulong)scr->mmio, DP_SRC_FRGD_CLR, ~0ul); 460 OUTREG((ulong)scr->mmio, DP_SRC_BKGD_CLR, 0); 461 OUTREG((ulong)scr->mmio, DP_WRITE_MASK, ~0ul); 462 463 radeonwaitidle(scr); 464 465 scr->fill = radeonfill; 466 scr->scroll = radeonscroll; 467 hwaccel = 1; 468 469 scr->blank = radeonblank; 470 hwblank = 1; 471 } 472 473 /* hw overlay */ 474 475 static void 476 radeonovlctl(VGAscr *scr, Chan *c, void *data, int len) 477 { 478 USED(scr, c, data, len); 479 } 480 481 static int 482 radeonovlwrite(VGAscr *scr, void *data, int len, vlong opt) 483 { 484 USED(scr, data, len, opt); 485 return -1; 486 } 487 488 static void 489 radeonflush(VGAscr *scr, Rectangle r) 490 { 491 USED(scr, r); 492 } 493 494 /* Export */ 495 496 VGAdev vgaradeondev = { 497 "radeon", 498 499 radeonenable, 500 0, /* disable */ 501 0, /* page */ 502 radeonlinear, 503 504 radeondrawinit, 505 #ifdef HW_ACCEL 506 radeonfill, 507 508 radeonovlctl, 509 radeonovlwrite, 510 radeonflush, 511 #endif 512 }; 513 VGAcur vgaradeoncur = { 514 "radeonhwgc", 515 radeoncurenable, 516 radeoncurdisable, 517 radeoncurload, 518 radeoncurmove, 519 0 /* doespanning */ 520 }; 521