1 /* 2 * Redistribution and use in source and binary forms, with or without 3 * modification, are permitted provided that the following conditions 4 * are met: 5 * 1. Redistributions of source code must retain the above copyright 6 * notice, this list of conditions and the following disclaimer. 7 * 2. Redistributions in binary form must reproduce the above copyright 8 * notice, this list of conditions and the following disclaimer in the 9 * documentation and/or other materials provided with the distribution. 10 * 11 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 12 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 13 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 14 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 15 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 16 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 17 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 18 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 19 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 20 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 21 */ 22 23 /* A console driver for Apple's Platinum onboard video controller, 24 * found in (all?) Catalyst logic boards including the Powermac 7200. 25 * 26 * Used valkyriefb.c from NetBSD, and platinumfb.c/platinumfb.h from 27 * Linux sources as templates. 28 * 29 * Platinum is broken regarding openfirmware video variables. In OF, 30 * for a powermac 7200, doing "dev /platinum .properties" results in: 31 * 32 * name platinum 33 * device_type display 34 * model AAPL,343S1184 35 * AAPL,connector monitor 36 * reg F8000000 00000800 37 * F1000000 01000000 38 * AAPL,interrupts 0000001E 39 * 40 * The first reg is the register set, and the second is for the 41 * framebuffer. There is also a set of colormap registers hardcoded 42 * in platinumfbreg.h that (I think) aren't in openfirmware. 43 * 44 * powermac 7200 VRAM min and max limits are 1 and 4 Mb respectively. 45 * OF claims 16M so we don't use that value. If other machines can 46 * can have more or less VRAM this code will need to be modified 47 */ 48 49 #include <sys/cdefs.h> 50 __KERNEL_RCSID(0, "$NetBSD: platinumfb.c,v 1.3 2016/09/16 17:32:36 scole Exp $"); 51 52 #include <sys/param.h> 53 #include <sys/systm.h> 54 #include <sys/kernel.h> 55 #include <sys/device.h> 56 57 #include <uvm/uvm_param.h> 58 59 #include <dev/ofw/openfirm.h> 60 61 #include <machine/autoconf.h> 62 #include <machine/pio.h> 63 64 #include <dev/wscons/wsdisplayvar.h> 65 #include <dev/wscons/wsconsio.h> 66 #include <dev/wsfont/wsfont.h> 67 #include <dev/rasops/rasops.h> 68 #include <dev/wscons/wsdisplay_vconsvar.h> 69 70 #include <dev/videomode/videomode.h> 71 72 #include <arch/macppc/dev/platinumfbreg.h> 73 74 #include <sys/sysctl.h> 75 76 #include "opt_wsemul.h" 77 78 /* 79 * here is a link of supported modes and resolutions: 80 * https://support.apple.com/kb/SP343?locale=en_US 81 * 82 * default console and X bpp/depth for built-in X config file, 83 * select 8 or 16 or 32. 84 * 85 */ 86 #define PLATINUM_CONSOLE_DEPTH 8 87 #define PLATINUM_FB_DEPTH 16 88 89 /* 90 * XXX 16 bit console not working without this patch to rasops15.c, 91 * http://mail-index.netbsd.org/current-users/2016/09/14/msg030132.html 92 * see thread, but rasops may need separate flags for host byte order 93 * and video hw byte order 94 */ 95 96 /* 97 * resolution, from one of platinumfb_setting vmode_name's. 98 */ 99 #define PLATINUM_FB_VMODE "1024x768x60" 100 101 struct platinumfb_setting { 102 char vmode_name[24]; 103 int32_t width; 104 int32_t height; 105 uint8_t freq; 106 uint8_t macmode; 107 108 int32_t pitch[3]; 109 uint32_t regs[26]; 110 uint8_t offset[3]; 111 uint8_t mode[3]; 112 uint8_t dacula_ctrl[3]; 113 uint8_t clock_params[2][2]; 114 }; 115 116 struct platinumfb_softc { 117 device_t sc_dev; 118 int sc_node; 119 120 uint8_t *sc_reg; 121 uint32_t sc_reg_size; 122 123 uint8_t *sc_cmap; 124 uint32_t sc_cmap_size; 125 126 uint8_t *sc_fb; 127 uint32_t sc_fb_size; 128 129 int sc_depth; 130 int sc_width, sc_height, sc_linebytes; 131 const struct videomode *sc_videomode; 132 uint8_t sc_modereg; 133 134 int sc_mode; 135 136 u_char sc_cmap_red[256]; 137 u_char sc_cmap_green[256]; 138 u_char sc_cmap_blue[256]; 139 140 struct vcons_data vd; 141 142 uint8_t sc_cmode; 143 uint8_t sc_dac_type; 144 uint32_t sc_vram; 145 int sc_on; 146 struct platinumfb_setting *sc_pfs; 147 }; 148 149 #define DIV2 0x20 150 #define DIV4 0x40 151 #define DIV8 0x60 152 #define DIV16 0x80 153 154 static struct platinumfb_setting platinum_5 = { 155 "640x480x60", 156 640, 480, 60, 5, 157 { 672, 1312, 2592 }, 158 { 0xff0, 4, 0, 0, 0, 0, 0x320, 0, 159 0, 0x15e, 0xc8, 0x18, 0x18f, 0x2f, 0x35, 0x3e, 160 0x42, 0x182, 0x18e, 0x41a, 0x418, 2, 7, 0x44, 161 0x404, 0x408 }, { 0x34, 0x3c, 0x41 }, 162 { 2, 0, 0xff }, { 0x11, 0x15, 0x19 }, 163 {{ 26, 0 + DIV8 }, { 14, 2 + DIV4 }} 164 }; 165 166 static struct platinumfb_setting platinum_12 = { 167 "800x600x75", 168 800, 600, 75, 12, 169 { 832, 1632, 3232 }, 170 { 0xff0, 4, 0, 0, 0, 0, 0x320, 0, 171 0, 0x1ce, 0x108, 0x14, 0x20f, 0x27, 0x30, 0x39, 172 0x72, 0x202, 0x20e, 0x4e2, 0x4e0, 4, 9, 0x2e, 173 0x4de, 0x4df }, { 0x64, 0x6c, 0x71 }, 174 { 2, 0, 0xff }, { 0x11, 0x15, 0x19 }, 175 {{ 122, 7 + DIV4 }, { 62, 9 + DIV2 }} 176 }; 177 178 static struct platinumfb_setting platinum_14 = { 179 "1024x768x60", 180 1024, 768, 60, 14, 181 { 1056, 2080, 4128 }, 182 { 0xff0, 4, 0, 0, 0, 0, 0x320, 0, 183 0, 0x25a, 0x14f, 0x22, 0x29f, 0x43, 0x49, 0x5b, 184 0x8e, 0x28e, 0x29e, 0x64c, 0x64a, 0xa, 0xf, 0x44, 185 0x644, 0x646 }, { 0x80, 0x88, 0x8d }, 186 { 2, 0, 0xff }, { 0x11, 0x15, 0x19 }, 187 {{ 71, 6 + DIV2 }, { 118, 13 + DIV2 }} 188 }; 189 190 static struct platinumfb_setting platinum_20 = { 191 "1280x1024x75", 192 1280, 1024, 75, 20, 193 { 1312, 2592, 2592 }, 194 { 0xffc, 4, 0, 0, 0, 0, 0x428, 0, 195 0, 0xb3, 0xd3, 0x12, 0x1a5, 0x23, 0x28, 0x2d, 196 0x5e, 0x19e, 0x1a4, 0x854, 0x852, 4, 9, 0x50, 197 0x850, 0x851 }, { 0x58, 0x5d, 0x5d }, 198 { 0, 0xff, 0xff }, { 0x51, 0x55, 0x55 }, 199 {{ 45, 3 }, { 66, 7 }} 200 }; 201 202 static struct platinumfb_setting *pfb_setting[] = { 203 &platinum_5, 204 &platinum_12, 205 &platinum_14, 206 &platinum_20 207 }; 208 209 static struct vcons_screen platinumfb_console_screen; 210 211 static int platinumfb_match(device_t, cfdata_t, void *); 212 static void platinumfb_attach(device_t, device_t, void *); 213 214 CFATTACH_DECL_NEW(platinumfb, sizeof(struct platinumfb_softc), 215 platinumfb_match, platinumfb_attach, NULL, NULL); 216 217 static int platinumfb_init(device_t); 218 static int platinumfb_set_mode(struct platinumfb_softc *, 219 const struct videomode *, int); 220 static void platinumfb_set_rasops(struct platinumfb_softc *, 221 struct rasops_info *, int); 222 static int platinumfb_ioctl(void *, void *, u_long, void *, int, 223 struct lwp *); 224 static paddr_t platinumfb_mmap(void *, void *, off_t, int); 225 static void platinumfb_init_screen(void *, struct vcons_screen *, int, 226 long *); 227 static int platinumfb_putcmap(struct platinumfb_softc *, 228 struct wsdisplay_cmap *); 229 static int platinumfb_getcmap(struct platinumfb_softc *, 230 struct wsdisplay_cmap *); 231 static void platinumfb_init_cmap(struct platinumfb_softc *); 232 static void platinumfb_restore_palette(struct platinumfb_softc *); 233 static void platinumfb_putpalreg(struct platinumfb_softc *, 234 uint8_t, uint8_t, uint8_t, uint8_t); 235 static uint32_t platinumfb_line_tweak(struct platinumfb_softc *); 236 static paddr_t platinumfb_page_align_up(struct platinumfb_softc *); 237 static void platinumfb_set_clock(struct platinumfb_softc *); 238 static void platinumfb_dac_type(struct platinumfb_softc *); 239 static void platinumfb_memory_size(struct platinumfb_softc *); 240 static void platinumfb_set_hardware(struct platinumfb_softc *); 241 242 static inline void platinumfb_write_reg(struct platinumfb_softc *, 243 int, uint32_t); 244 245 #ifdef notyet 246 static inline uint32_t platinumfb_read_reg(struct platinumfb_softc *, int); 247 #endif 248 static inline void platinumfb_write_cmap_reg(struct platinumfb_softc *, 249 int, uint8_t); 250 static inline uint8_t platinumfb_read_cmap_reg(struct platinumfb_softc *, int); 251 static inline void platinumfb_store_d2(struct platinumfb_softc *, 252 uint8_t, uint8_t); 253 254 struct wsscreen_descr platinumfb_defaultscreen = { 255 "default", 256 0, 0, 257 NULL, 258 8, 16, 259 WSSCREEN_WSCOLORS | WSSCREEN_HILIT, 260 NULL, 261 }; 262 263 const struct wsscreen_descr *_platinumfb_scrlist[] = { 264 &platinumfb_defaultscreen, 265 /* XXX other formats, graphics screen? */ 266 }; 267 268 struct wsscreen_list platinumfb_screenlist = { 269 sizeof(_platinumfb_scrlist) / sizeof(struct wsscreen_descr *), 270 _platinumfb_scrlist 271 }; 272 273 struct wsdisplay_accessops platinumfb_accessops = { 274 platinumfb_ioctl, 275 platinumfb_mmap, 276 NULL, 277 NULL, 278 NULL, 279 NULL, /* load_font */ 280 NULL, /* polls */ 281 NULL, /* scroll */ 282 }; 283 284 static inline void 285 platinumfb_write_reg(struct platinumfb_softc *sc, int reg, uint32_t val) 286 { 287 out32(sc->sc_reg + PLATINUM_REG_OFFSET_ADDR(reg), val); 288 } 289 290 #ifdef notyet 291 static inline uint32_t 292 platinumfb_read_reg(struct platinumfb_softc *sc, int reg) 293 { 294 return in32(sc->sc_reg + PLATINUM_REG_OFFSET_ADDR(reg)); 295 } 296 #endif 297 298 static inline void 299 platinumfb_write_cmap_reg(struct platinumfb_softc *sc, 300 int reg_offset, uint8_t val) 301 { 302 out8(sc->sc_cmap + reg_offset, val); 303 } 304 305 static inline uint8_t 306 platinumfb_read_cmap_reg(struct platinumfb_softc *sc, int reg_offset) 307 { 308 return in8(sc->sc_cmap + reg_offset); 309 } 310 311 static inline void 312 platinumfb_store_d2(struct platinumfb_softc *sc, 313 uint8_t a, uint8_t d) 314 { 315 platinumfb_write_cmap_reg(sc, PLATINUM_CMAP_ADDR_OFFSET, a + 32); 316 platinumfb_write_cmap_reg(sc, PLATINUM_CMAP_D2_OFFSET, d); 317 } 318 319 static void 320 platinumfb_putpalreg(struct platinumfb_softc *sc, 321 uint8_t reg, uint8_t r, uint8_t g, uint8_t b) 322 { 323 platinumfb_write_cmap_reg(sc, PLATINUM_CMAP_ADDR_OFFSET, reg); 324 platinumfb_write_cmap_reg(sc, PLATINUM_CMAP_LUT_OFFSET, r); 325 platinumfb_write_cmap_reg(sc, PLATINUM_CMAP_LUT_OFFSET, g); 326 platinumfb_write_cmap_reg(sc, PLATINUM_CMAP_LUT_OFFSET, b); 327 } 328 329 static uint32_t 330 platinumfb_line_tweak(struct platinumfb_softc *sc) 331 { 332 /* bytes per line adjustment depending on resolution and depth */ 333 if (sc->sc_cmode > PLATINUM_CMODE_8 && 334 strcmp(sc->sc_pfs->vmode_name, "832x624x75") == 0) 335 return 0x10; 336 else 337 return 0x20; 338 } 339 340 static paddr_t 341 platinumfb_page_align_up(struct platinumfb_softc *sc) 342 { 343 /* round up framebuffer address to the next highest page */ 344 paddr_t addr = (paddr_t)sc->sc_fb; 345 paddr_t ret = round_page(addr); 346 347 if (ret == addr) 348 ret = round_page(addr + 1); 349 350 return ret; 351 } 352 353 /* 2 versions of platinum clock, older one uses clock[1] and 354 * freq = 14.3Mhz * c0 / (c1 & 0x1f) / (1 << (c1 >> 5)) 355 * newer one uses clock[0] and 356 * freq = 15Mhz * c0 / ((c1 & 0x1f) + 2) / (1 << (c1 >> 5)) 357 */ 358 static void 359 platinumfb_set_clock(struct platinumfb_softc *sc) 360 { 361 uint8_t clk_idx = sc->sc_dac_type == PLATINUM_DAC_1 ? 1 : 0; 362 uint8_t d2; 363 364 platinumfb_store_d2(sc, 6, 0xc6); 365 366 platinumfb_write_cmap_reg(sc, PLATINUM_CMAP_ADDR_OFFSET, 3+32); 367 d2 = platinumfb_read_cmap_reg(sc, PLATINUM_CMAP_D2_OFFSET); 368 369 if (d2 == 2) { 370 platinumfb_store_d2(sc, 7, sc->sc_pfs->clock_params[clk_idx][0]); 371 platinumfb_store_d2(sc, 8, sc->sc_pfs->clock_params[clk_idx][1]); 372 platinumfb_store_d2(sc, 3, 3); 373 } else { 374 platinumfb_store_d2(sc, 4, sc->sc_pfs->clock_params[clk_idx][0]); 375 platinumfb_store_d2(sc, 5, sc->sc_pfs->clock_params[clk_idx][1]); 376 platinumfb_store_d2(sc, 3, 2); 377 } 378 379 delay(5000); 380 platinumfb_store_d2(sc, 9, 0xa6); 381 } 382 383 static void 384 platinumfb_dac_type(struct platinumfb_softc *sc) 385 { 386 uint8_t dtype = 0; 387 388 platinumfb_write_cmap_reg(sc, PLATINUM_CMAP_ADDR_OFFSET, 0x40); 389 dtype = platinumfb_read_cmap_reg(sc, PLATINUM_CMAP_D2_OFFSET); 390 391 switch (dtype) { 392 case PLATINUM_DAC_0: 393 case PLATINUM_DAC_1: 394 /* do nothing */ 395 break; 396 default: 397 aprint_error_dev(sc->sc_dev, "unknown dac 0x%x, using 0x%x\n", 398 dtype, PLATINUM_DAC_0); 399 dtype = PLATINUM_DAC_0; 400 break; 401 } 402 403 /* save type */ 404 sc->sc_dac_type = dtype; 405 } 406 407 static void 408 platinumfb_memory_size(struct platinumfb_softc *sc) 409 { 410 int i; 411 off_t offset = PLATINUM_FB_BANK_SIZE; 412 paddr_t total_vram = PLATINUM_FB_MIN_SIZE; 413 414 uint8_t test_val[] = {0x34, 0x56, 0x78}; 415 uint8_t bank[] = {0, 0, 0}; 416 uint8_t num_elems = sizeof(test_val)/sizeof(test_val[0]); 417 418 volatile uint8_t *fbuffer = mapiodev((paddr_t)sc->sc_fb, sc->sc_fb_size, false); 419 420 if (fbuffer == NULL) 421 panic("platinumfb could not mapiodev"); 422 423 /* turn on all banks of RAM */ 424 platinumfb_write_reg(sc, 16, (paddr_t)sc->sc_fb); 425 platinumfb_write_reg(sc, 20, 0x1011); 426 platinumfb_write_reg(sc, 24, 0); 427 428 /* 429 * write "unique" value to each bank of memory and read value 430 * back. if match assumes VRAM bank exists. On the powermac 7200, 431 * bank0 is always there and soldered to motherboard, don't know 432 * if that is the case for others 433 */ 434 for (i = 0 ; i < num_elems; i++) { 435 out8(fbuffer + offset, test_val[i]); 436 out8(fbuffer + offset + 0x8, 0x0); 437 438 __asm volatile ("eieio; dcbf 0,%0"::"r"(&fbuffer[offset]):"memory"); 439 440 bank[i] = fbuffer[offset] == test_val[i]; 441 total_vram += bank[i] * PLATINUM_FB_BANK_SIZE; 442 offset += PLATINUM_FB_BANK_SIZE; 443 } 444 445 /* save total vram or minimum */ 446 if (total_vram >= PLATINUM_FB_MIN_SIZE && total_vram <= PLATINUM_FB_MAX_SIZE) { 447 sc->sc_vram = total_vram; 448 } else { 449 aprint_error_dev(sc->sc_dev, 450 "invalid VRAM size 0x%lx, using min 0x%x\n", 451 total_vram, PLATINUM_FB_MIN_SIZE); 452 sc->sc_vram = PLATINUM_FB_MIN_SIZE; 453 } 454 455 unmapiodev((paddr_t)fbuffer, sc->sc_fb_size); 456 } 457 458 static int 459 platinumfb_match(device_t parent, cfdata_t cf, void *aux) 460 { 461 struct confargs *ca = aux; 462 463 return (strcmp(ca->ca_name, "platinum") == 0); 464 } 465 466 static void 467 platinumfb_attach(device_t parent, device_t self, void *aux) 468 { 469 struct platinumfb_softc *sc = device_private(self); 470 struct confargs *ca = aux; 471 u_int *reg = ca->ca_reg; 472 473 sc->sc_dev = self; 474 sc->sc_node = ca->ca_node; 475 476 sc->sc_reg = (uint8_t *)reg[0]; 477 sc->sc_reg_size = reg[1]; 478 479 sc->sc_fb = (uint8_t *)reg[2]; 480 sc->sc_fb_size = PLATINUM_FB_MAX_SIZE; 481 482 sc->sc_cmap = (uint8_t *)PLATINUM_CMAP_BASE_ADDR; 483 sc->sc_cmap_size = PLATINUM_CMAP_SIZE; 484 485 aprint_normal(" reg-addr 0x%08lx fb-addr 0x%08lx cmap-addr 0x%08lx\n", 486 (paddr_t)sc->sc_reg, 487 (paddr_t)sc->sc_fb, 488 (paddr_t)sc->sc_cmap); 489 490 config_finalize_register(sc->sc_dev, platinumfb_init); 491 } 492 493 static void 494 platinumfb_init_cmap(struct platinumfb_softc *sc) 495 { 496 int i; 497 uint8_t tmp; 498 499 switch (sc->sc_cmode) { 500 case PLATINUM_CMODE_8: 501 default: 502 /* R3G3B2 colormap */ 503 for (i = 0; i < 256; i++) { 504 tmp = i & 0xe0; 505 /* 506 * replicate bits so 0xe0 maps to a red value of 0xff 507 * in order to make white look actually white 508 */ 509 tmp |= (tmp >> 3) | (tmp >> 6); 510 sc->sc_cmap_red[i] = tmp; 511 512 tmp = (i & 0x1c) << 3; 513 tmp |= (tmp >> 3) | (tmp >> 6); 514 sc->sc_cmap_green[i] = tmp; 515 516 tmp = (i & 0x03) << 6; 517 tmp |= tmp >> 2; 518 tmp |= tmp >> 4; 519 sc->sc_cmap_blue[i] = tmp; 520 } 521 break; 522 523 case PLATINUM_CMODE_16: 524 for (i = 0; i < 32; i++) { 525 tmp = 255 * i / 32; 526 sc->sc_cmap_red[i] = tmp; 527 sc->sc_cmap_green[i] = tmp; 528 sc->sc_cmap_blue[i] = tmp; 529 } 530 for (i = 32; i < 256; i++) { 531 sc->sc_cmap_red[i] = 0; 532 sc->sc_cmap_blue[i] = 0; 533 sc->sc_cmap_green[i] = 0; 534 } 535 break; 536 537 case PLATINUM_CMODE_32: 538 for (i = 0; i < 256; i++) { 539 sc->sc_cmap_red[i] = i; 540 sc->sc_cmap_green[i] = i; 541 sc->sc_cmap_blue[i] = i; 542 } 543 break; 544 } 545 } 546 547 static void 548 platinumfb_restore_palette(struct platinumfb_softc *sc) 549 { 550 int i; 551 552 for (i = 0; i < 256; i++) { 553 platinumfb_putpalreg(sc, i, sc->sc_cmap_red[i], 554 sc->sc_cmap_green[i], sc->sc_cmap_blue[i]); 555 } 556 } 557 558 static int 559 platinumfb_init(device_t self) 560 { 561 struct platinumfb_softc *sc = device_private(self); 562 const struct videomode *mode = NULL; 563 struct rasops_info *ri; 564 565 struct wsemuldisplaydev_attach_args aa; 566 bool is_console = FALSE; 567 long defattr; 568 569 int i; 570 571 /* 572 * become console if OF variable "output-device" is "screen" or 573 * contains "platinum", since normal OF video variables are unavailable 574 */ 575 int options; 576 char output_device[128]; 577 options = OF_finddevice("/options"); 578 if (options == 0 || 579 options == -1 || 580 OF_getprop(options, "output-device", output_device, 581 sizeof(output_device)) == 0 ) { 582 aprint_error_dev(sc->sc_dev, 583 "could not get output-device prop, assuming not console\n"); 584 } else { 585 if (strstr(output_device,"platinum") || 586 strcmp(output_device,"screen") == 0 ) { 587 is_console = TRUE; 588 } 589 } 590 591 sc->sc_pfs = NULL; 592 sc->sc_mode = WSDISPLAYIO_MODE_EMUL; 593 sc->sc_on = WSDISPLAYIO_VIDEO_ON; 594 595 /* determine vram memory and dac clock type */ 596 platinumfb_memory_size(sc); 597 platinumfb_dac_type(sc); 598 599 aprint_normal_dev(sc->sc_dev,"is_console %d dac 0x%x vram 0x%x\n", 600 is_console, sc->sc_dac_type, sc->sc_vram); 601 602 for (i=0; i < sizeof(pfb_setting)/sizeof(pfb_setting[0]); i++) { 603 if (strcmp(PLATINUM_FB_VMODE, pfb_setting[i]->vmode_name)==0) { 604 mode = pick_mode_by_ref(pfb_setting[i]->width, 605 pfb_setting[i]->height, 606 pfb_setting[i]->freq); 607 break; 608 } 609 } 610 611 if (!mode) { 612 aprint_error_dev(sc->sc_dev, 613 "pick_mode_by_ref failed, using default\n"); 614 mode = pick_mode_by_ref(800, 600, 75); 615 } 616 617 if (platinumfb_set_mode(sc, mode, PLATINUM_CONSOLE_DEPTH) != 0) { 618 aprint_error_dev(sc->sc_dev, "platinumfb_set_mode failed\n"); 619 return 0; 620 } 621 622 vcons_init(&sc->vd, sc, &platinumfb_defaultscreen, 623 &platinumfb_accessops); 624 sc->vd.init_screen = platinumfb_init_screen; 625 626 ri = &platinumfb_console_screen.scr_ri; 627 vcons_init_screen(&sc->vd, &platinumfb_console_screen, 1, &defattr); 628 629 platinumfb_console_screen.scr_flags |= VCONS_SCREEN_IS_STATIC; 630 631 platinumfb_defaultscreen.textops = &ri->ri_ops; 632 platinumfb_defaultscreen.capabilities = ri->ri_caps; 633 platinumfb_defaultscreen.nrows = ri->ri_rows; 634 platinumfb_defaultscreen.ncols = ri->ri_cols; 635 636 if (is_console) { 637 wsdisplay_cnattach(&platinumfb_defaultscreen, ri, 0, 0, 638 defattr); 639 vcons_replay_msgbuf(&platinumfb_console_screen); 640 } 641 642 aa.console = is_console; 643 aa.scrdata = &platinumfb_screenlist; 644 aa.accessops = &platinumfb_accessops; 645 aa.accesscookie = &sc->vd; 646 647 config_found(self, &aa, wsemuldisplaydevprint); 648 649 return 0; 650 } 651 652 static void 653 platinumfb_set_hardware(struct platinumfb_softc *sc) 654 { 655 int i; 656 bool one_bank = sc->sc_vram == PLATINUM_FB_BANK_SIZE; 657 658 /* now start programming the chip */ 659 platinumfb_write_reg(sc, 24, 7); /* turn off display */ 660 661 for (i = 0; i < 26; ++i) 662 platinumfb_write_reg(sc, i+32, sc->sc_pfs->regs[i]); 663 664 platinumfb_write_reg(sc, 26+32, one_bank ? 665 sc->sc_pfs->offset[sc->sc_cmode] + 4 - sc->sc_cmode : 666 sc->sc_pfs->offset[sc->sc_cmode]); 667 668 /* 669 * reg 16 apparently needs an address 0x10 less the where frame 670 * buffer/ri_bits start for console text to be aligned. In 671 * addition, X memory maps (mmap) the frame buffer starting on 672 * page boundaries. So to get both X and console text aligned we 673 * start at the first page up from sc_fb[0]. Starting at sc_fb[0] 674 * did work on my machine but not sure if this negative offset 675 * would be problematic elsewhere. 676 * 677 * Not sure why linux used different fb offsets for each mode, as 678 * any addresses seemed to work as long as relative difference was 679 * 0x10. 680 */ 681 platinumfb_write_reg(sc, 16, platinumfb_page_align_up(sc) - 0x10); 682 683 platinumfb_write_reg(sc, 18, sc->sc_pfs->pitch[sc->sc_cmode]); 684 685 /* 686 * XXX register 19 setting looks wrong for 1 bank & 32 bpp. 687 * 512x384 is only resolution that would use such a setting, but 688 * that is not currently in videomodes.c 689 */ 690 if (sc->sc_cmode == PLATINUM_CMODE_32 && 691 (sc->sc_pfs->macmode == 1 || sc->sc_pfs->macmode == 2)) 692 aprint_error_dev(sc->sc_dev, 693 "platinumfb reg19 array out-of-bounds"); 694 695 platinumfb_write_reg(sc, 19, one_bank ? 696 sc->sc_pfs->mode[sc->sc_cmode+1] : /* XXX fix this for 32 bpp */ 697 sc->sc_pfs->mode[sc->sc_cmode]); 698 699 platinumfb_write_reg(sc, 20, one_bank ? 0x11 : 0x1011); 700 platinumfb_write_reg(sc, 21, 0x100); 701 platinumfb_write_reg(sc, 22, 1); 702 platinumfb_write_reg(sc, 23, 1); 703 platinumfb_write_reg(sc, 26, 0xc00); 704 platinumfb_write_reg(sc, 27, 0x235); 705 /* platinumfb_write_reg(sc, 27, 0x2aa); */ 706 707 platinumfb_store_d2(sc, 0, 708 one_bank ? sc->sc_pfs->dacula_ctrl[sc->sc_cmode] & 0xf : 709 sc->sc_pfs->dacula_ctrl[sc->sc_cmode]); 710 platinumfb_store_d2(sc, 1, 4); 711 platinumfb_store_d2(sc, 2, 0); 712 713 platinumfb_set_clock(sc); 714 715 platinumfb_write_reg(sc, 24, 0); /* turn display on */ 716 } 717 718 static int 719 platinumfb_set_mode(struct platinumfb_softc *sc, 720 const struct videomode *mode, int depth) 721 { 722 int i; 723 724 /* first find the parameter for the mode register */ 725 i = 0; 726 while((i < __arraycount(pfb_setting)) && 727 (strcmp(mode->name, pfb_setting[i]->vmode_name) != 0)) 728 i++; 729 730 if (i >= __arraycount(pfb_setting)) { 731 aprint_error_dev(sc->sc_dev, 732 "Can't find a mode register value for %s\n", 733 mode->name); 734 return EINVAL; 735 } 736 737 /* found a mode */ 738 sc->sc_pfs = pfb_setting[i]; 739 740 /* determine depth settings */ 741 switch (depth) { 742 case 8: 743 default: 744 sc->sc_depth = 8; 745 sc->sc_cmode = PLATINUM_CMODE_8; 746 break; 747 case 15: 748 case 16: 749 /* 15 bpp but use 16 so X/wsfb works */ 750 sc->sc_depth = 16; 751 sc->sc_cmode = PLATINUM_CMODE_16; 752 break; 753 case 24: 754 case 32: 755 /* 24 bpp but use 32 so X/wsfb works */ 756 sc->sc_depth = 32; 757 sc->sc_cmode = PLATINUM_CMODE_32; 758 break; 759 } 760 761 sc->sc_modereg = sc->sc_pfs->macmode; 762 sc->sc_videomode = mode; 763 sc->sc_height = mode->vdisplay; 764 sc->sc_width = mode->hdisplay; 765 sc->sc_linebytes = sc->sc_width * (1 << sc->sc_cmode) + platinumfb_line_tweak(sc); 766 767 /* check if we have enough video memory */ 768 if (sc->sc_height * sc->sc_linebytes > sc->sc_vram - PAGE_SIZE) { 769 aprint_error_dev(sc->sc_dev, "Not enough video RAM for %s\n", 770 mode->name); 771 return EINVAL; 772 } 773 774 /* set up and write colormap */ 775 platinumfb_init_cmap(sc); 776 platinumfb_restore_palette(sc); 777 778 /* set hardware registers */ 779 platinumfb_set_hardware(sc); 780 781 aprint_normal_dev(sc->sc_dev, "switched to %s in %d bit color\n", 782 sc->sc_pfs->vmode_name, sc->sc_depth); 783 return 0; 784 } 785 786 static int 787 platinumfb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, 788 struct lwp *l) 789 { 790 struct vcons_data *vd = v; 791 struct platinumfb_softc *sc = vd->cookie; 792 struct wsdisplay_fbinfo *wdf; 793 struct vcons_screen *ms = vd->active; 794 int i; 795 796 switch (cmd) { 797 case WSDISPLAYIO_GTYPE: 798 *(u_int *)data = WSDISPLAY_TYPE_PLATINUM; 799 return 0; 800 801 case WSDISPLAYIO_GINFO: 802 wdf = (void *)data; 803 wdf->height = ms->scr_ri.ri_height; 804 wdf->width = ms->scr_ri.ri_width; 805 wdf->depth = ms->scr_ri.ri_depth; 806 wdf->cmsize = 256; 807 return 0; 808 809 case WSDISPLAYIO_GVIDEO: 810 *(int *)data = sc->sc_on; 811 return 0; 812 813 case WSDISPLAYIO_SVIDEO: 814 /* 815 * poor man's screen blanking, just write zeros to colormap 816 * registers but don't save in softc. 817 */ 818 if (*(int *)data != sc->sc_on) { 819 sc->sc_on = (sc->sc_on == WSDISPLAYIO_VIDEO_ON ? 820 WSDISPLAYIO_VIDEO_OFF : WSDISPLAYIO_VIDEO_ON); 821 822 /* XXX need to lock colormap? */ 823 if (sc->sc_on == WSDISPLAYIO_VIDEO_OFF) { 824 for (i=0; i < 256; i++) 825 platinumfb_putpalreg(sc, i, 0, 0, 0); 826 } else { 827 platinumfb_restore_palette(sc); 828 } 829 } 830 return 0; 831 832 case WSDISPLAYIO_GETCMAP: 833 if (sc->sc_cmode == PLATINUM_CMODE_8) { 834 return platinumfb_getcmap(sc, 835 (struct wsdisplay_cmap *)data); 836 } else 837 return 0; 838 839 case WSDISPLAYIO_PUTCMAP: 840 if (sc->sc_cmode == PLATINUM_CMODE_8) { 841 return platinumfb_putcmap(sc, 842 (struct wsdisplay_cmap *)data); 843 } else 844 return 0; 845 846 case WSDISPLAYIO_SMODE: { 847 int new_mode = *(int*)data; 848 849 if (new_mode != sc->sc_mode) { 850 int new_depth = new_mode == WSDISPLAYIO_MODE_EMUL ? 851 PLATINUM_CONSOLE_DEPTH : PLATINUM_FB_DEPTH; 852 853 switch(new_mode) { 854 /* 855 * XXX - not sure how this is supposed to work for 856 * switching bpp between console and X, but cases with 857 * (EMUL MAPPED) or (EMUL MAPPED DUMBFB) work, but 858 * (EMUL DUMBFB) garbles screen for some reason. 859 */ 860 case WSDISPLAYIO_MODE_EMUL: 861 case WSDISPLAYIO_MODE_MAPPED: 862 /* case WSDISPLAYIO_MODE_DUMBFB: XXX */ 863 864 /* in case screen is "blanked" */ 865 platinumfb_restore_palette(sc); 866 867 sc->sc_mode = new_mode; 868 869 platinumfb_set_mode(sc, sc->sc_videomode, new_depth); 870 platinumfb_set_rasops(sc, &ms->scr_ri, true); 871 872 if (new_mode == WSDISPLAYIO_MODE_EMUL) 873 vcons_redraw_screen(ms); 874 } 875 } 876 877 return 0; 878 } 879 880 case WSDISPLAYIO_LINEBYTES: 881 *(u_int *)data = sc->sc_linebytes; 882 return 0; 883 884 case WSDISPLAYIO_GET_FBINFO: { 885 struct wsdisplayio_fbinfo *fbi = data; 886 return wsdisplayio_get_fbinfo(&ms->scr_ri, fbi); 887 } 888 889 } 890 return EPASSTHROUGH; 891 } 892 893 static paddr_t 894 platinumfb_mmap(void *v, void *vs, off_t offset, int prot) 895 { 896 struct vcons_data *vd = v; 897 struct platinumfb_softc *sc = vd->cookie; 898 paddr_t pa = -1; 899 paddr_t fb_aligned = platinumfb_page_align_up(sc); 900 paddr_t fb_vram = sc->sc_vram - (fb_aligned - (paddr_t)sc->sc_fb); 901 902 /* XXX need to worry about superuser or mapping other registers? */ 903 904 if (offset >= 0 && offset < fb_vram) 905 pa = fb_aligned + offset; 906 907 return pa; 908 } 909 910 static void platinumfb_set_rasops(struct platinumfb_softc *sc, 911 struct rasops_info *ri, 912 int existing) 913 { 914 memset(ri, 0, sizeof(struct rasops_info)); 915 916 ri->ri_depth = sc->sc_depth; 917 ri->ri_width = sc->sc_width; 918 ri->ri_height = sc->sc_height; 919 ri->ri_stride = sc->sc_linebytes; 920 ri->ri_bits = (u_char*)platinumfb_page_align_up(sc); 921 ri->ri_flg = RI_FULLCLEAR; 922 923 if (existing) 924 ri->ri_flg |= RI_CLEAR; 925 926 switch (sc->sc_cmode) { 927 case PLATINUM_CMODE_8: 928 default: 929 ri->ri_flg |= RI_ENABLE_ALPHA | RI_8BIT_IS_RGB; 930 931 break; 932 case PLATINUM_CMODE_16: 933 if (strcmp(sc->sc_pfs->vmode_name, "640x480x60") == 0 || 934 strcmp(sc->sc_pfs->vmode_name, "800x600x75") == 0 ) 935 ri->ri_flg |= RI_ENABLE_ALPHA; 936 937 ri->ri_rnum = 5; 938 ri->ri_rpos = 10; 939 ri->ri_gnum = 5; 940 ri->ri_gpos = 5; 941 ri->ri_bnum = 5; 942 ri->ri_bpos = 0; 943 944 break; 945 case PLATINUM_CMODE_32: 946 if (strcmp(sc->sc_pfs->vmode_name, "640x480x60") == 0 || 947 strcmp(sc->sc_pfs->vmode_name, "800x600x75") == 0 ) 948 ri->ri_flg |= RI_ENABLE_ALPHA; 949 950 ri->ri_rnum = 8; 951 ri->ri_rpos = 16; 952 ri->ri_gnum = 8; 953 ri->ri_gpos = 8; 954 ri->ri_bnum = 8; 955 ri->ri_bpos = 0; 956 957 break; 958 } 959 960 rasops_init(ri, 0, 0); 961 ri->ri_caps = WSSCREEN_WSCOLORS; 962 963 rasops_reconfig(ri, sc->sc_height / ri->ri_font->fontheight, 964 sc->sc_width / ri->ri_font->fontwidth); 965 966 } 967 968 static void 969 platinumfb_init_screen(void *cookie, struct vcons_screen *scr, 970 int existing, long *defattr) 971 { 972 struct platinumfb_softc *sc = cookie; 973 struct rasops_info *ri = &scr->scr_ri; 974 975 scr->scr_flags |= VCONS_DONT_READ; 976 977 platinumfb_set_rasops(sc, ri, existing); 978 979 ri->ri_hw = scr; 980 } 981 982 static int 983 platinumfb_putcmap(struct platinumfb_softc *sc, struct wsdisplay_cmap *cm) 984 { 985 u_char *r, *g, *b; 986 u_int index = cm->index; 987 u_int count = cm->count; 988 int i, error; 989 u_char rbuf[256], gbuf[256], bbuf[256]; 990 991 if (cm->index >= 256 || cm->count > 256 || 992 (cm->index + cm->count) > 256) 993 return EINVAL; 994 error = copyin(cm->red, &rbuf[index], count); 995 if (error) 996 return error; 997 error = copyin(cm->green, &gbuf[index], count); 998 if (error) 999 return error; 1000 error = copyin(cm->blue, &bbuf[index], count); 1001 if (error) 1002 return error; 1003 1004 memcpy(&sc->sc_cmap_red[index], &rbuf[index], count); 1005 memcpy(&sc->sc_cmap_green[index], &gbuf[index], count); 1006 memcpy(&sc->sc_cmap_blue[index], &bbuf[index], count); 1007 1008 /* write colormap registers if not currently blanked */ 1009 if (sc->sc_on == WSDISPLAYIO_VIDEO_ON) { 1010 r = &sc->sc_cmap_red[index]; 1011 g = &sc->sc_cmap_green[index]; 1012 b = &sc->sc_cmap_blue[index]; 1013 1014 for (i = 0; i < count; i++) { 1015 platinumfb_putpalreg(sc, index, *r, *g, *b); 1016 index++; 1017 r++, g++, b++; 1018 } 1019 } 1020 1021 return 0; 1022 } 1023 1024 static int 1025 platinumfb_getcmap(struct platinumfb_softc *sc, struct wsdisplay_cmap *cm) 1026 { 1027 u_int index = cm->index; 1028 u_int count = cm->count; 1029 int error; 1030 1031 if (index >= 255 || count > 256 || index + count > 256) 1032 return EINVAL; 1033 1034 error = copyout(&sc->sc_cmap_red[index], cm->red, count); 1035 if (error) 1036 return error; 1037 error = copyout(&sc->sc_cmap_green[index], cm->green, count); 1038 if (error) 1039 return error; 1040 error = copyout(&sc->sc_cmap_blue[index], cm->blue, count); 1041 if (error) 1042 return error; 1043 1044 return 0; 1045 } 1046