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.6 2021/08/07 16:18:57 thorpej 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 101struct 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 116struct 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 154static 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 166static 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 178static 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 190static 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 202static struct platinumfb_setting *pfb_setting[] = { 203 &platinum_5, 204 &platinum_12, 205 &platinum_14, 206 &platinum_20 207}; 208 209static struct vcons_screen platinumfb_console_screen; 210 211static int platinumfb_match(device_t, cfdata_t, void *); 212static void platinumfb_attach(device_t, device_t, void *); 213 214CFATTACH_DECL_NEW(platinumfb, sizeof(struct platinumfb_softc), 215 platinumfb_match, platinumfb_attach, NULL, NULL); 216 217static int platinumfb_init(device_t); 218static int platinumfb_set_mode(struct platinumfb_softc *, 219 const struct videomode *, int); 220static void platinumfb_set_rasops(struct platinumfb_softc *, 221 struct rasops_info *, int); 222static int platinumfb_ioctl(void *, void *, u_long, void *, int, 223 struct lwp *); 224static paddr_t platinumfb_mmap(void *, void *, off_t, int); 225static void platinumfb_init_screen(void *, struct vcons_screen *, int, 226 long *); 227static int platinumfb_putcmap(struct platinumfb_softc *, 228 struct wsdisplay_cmap *); 229static int platinumfb_getcmap(struct platinumfb_softc *, 230 struct wsdisplay_cmap *); 231static void platinumfb_init_cmap(struct platinumfb_softc *); 232static void platinumfb_restore_palette(struct platinumfb_softc *); 233static void platinumfb_putpalreg(struct platinumfb_softc *, 234 uint8_t, uint8_t, uint8_t, uint8_t); 235static uint32_t platinumfb_line_tweak(struct platinumfb_softc *); 236static paddr_t platinumfb_page_align_up(struct platinumfb_softc *); 237static void platinumfb_set_clock(struct platinumfb_softc *); 238static void platinumfb_dac_type(struct platinumfb_softc *); 239static void platinumfb_memory_size(struct platinumfb_softc *); 240static void platinumfb_set_hardware(struct platinumfb_softc *); 241 242static inline void platinumfb_write_reg(struct platinumfb_softc *, 243 int, uint32_t); 244 245#ifdef notyet 246static inline uint32_t platinumfb_read_reg(struct platinumfb_softc *, int); 247#endif 248static inline void platinumfb_write_cmap_reg(struct platinumfb_softc *, 249 int, uint8_t); 250static inline uint8_t platinumfb_read_cmap_reg(struct platinumfb_softc *, int); 251static inline void platinumfb_store_d2(struct platinumfb_softc *, 252 uint8_t, uint8_t); 253 254struct wsscreen_descr platinumfb_defaultscreen = { 255 "default", 256 0, 0, 257 NULL, 258 8, 16, 259 WSSCREEN_WSCOLORS | WSSCREEN_HILIT, 260 NULL, 261}; 262 263const struct wsscreen_descr *_platinumfb_scrlist[] = { 264 &platinumfb_defaultscreen, 265 /* XXX other formats, graphics screen? */ 266}; 267 268struct wsscreen_list platinumfb_screenlist = { 269 sizeof(_platinumfb_scrlist) / sizeof(struct wsscreen_descr *), 270 _platinumfb_scrlist 271}; 272 273struct 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 284static inline void 285platinumfb_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 291static inline uint32_t 292platinumfb_read_reg(struct platinumfb_softc *sc, int reg) 293{ 294 return in32(sc->sc_reg + PLATINUM_REG_OFFSET_ADDR(reg)); 295} 296#endif 297 298static inline void 299platinumfb_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 305static inline uint8_t 306platinumfb_read_cmap_reg(struct platinumfb_softc *sc, int reg_offset) 307{ 308 return in8(sc->sc_cmap + reg_offset); 309} 310 311static inline void 312platinumfb_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 319static void 320platinumfb_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 329static uint32_t 330platinumfb_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 340static paddr_t 341platinumfb_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 */ 358static void 359platinumfb_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 383static void 384platinumfb_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 407static void 408platinumfb_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 458static int 459platinumfb_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 466static void 467platinumfb_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 493static void 494platinumfb_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 547static void 548platinumfb_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 558static int 559platinumfb_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" contains "platinum", 573 * 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 is_console = TRUE; 587 } 588 } 589 590 sc->sc_pfs = NULL; 591 sc->sc_mode = WSDISPLAYIO_MODE_EMUL; 592 sc->sc_on = WSDISPLAYIO_VIDEO_ON; 593 594 /* determine vram memory and dac clock type */ 595 platinumfb_memory_size(sc); 596 platinumfb_dac_type(sc); 597 598 aprint_normal_dev(sc->sc_dev,"is_console %d dac 0x%x vram 0x%x\n", 599 is_console, sc->sc_dac_type, sc->sc_vram); 600 601 for (i=0; i < sizeof(pfb_setting)/sizeof(pfb_setting[0]); i++) { 602 if (strcmp(PLATINUM_FB_VMODE, pfb_setting[i]->vmode_name)==0) { 603 mode = pick_mode_by_ref(pfb_setting[i]->width, 604 pfb_setting[i]->height, 605 pfb_setting[i]->freq); 606 break; 607 } 608 } 609 610 if (!mode) { 611 aprint_error_dev(sc->sc_dev, 612 "pick_mode_by_ref failed, using default\n"); 613 mode = pick_mode_by_ref(800, 600, 75); 614 } 615 616 if (platinumfb_set_mode(sc, mode, PLATINUM_CONSOLE_DEPTH) != 0) { 617 aprint_error_dev(sc->sc_dev, "platinumfb_set_mode failed\n"); 618 return 0; 619 } 620 621 vcons_init(&sc->vd, sc, &platinumfb_defaultscreen, 622 &platinumfb_accessops); 623 sc->vd.init_screen = platinumfb_init_screen; 624 625 ri = &platinumfb_console_screen.scr_ri; 626 vcons_init_screen(&sc->vd, &platinumfb_console_screen, 1, &defattr); 627 628 platinumfb_console_screen.scr_flags |= VCONS_SCREEN_IS_STATIC; 629 630 platinumfb_defaultscreen.textops = &ri->ri_ops; 631 platinumfb_defaultscreen.capabilities = ri->ri_caps; 632 platinumfb_defaultscreen.nrows = ri->ri_rows; 633 platinumfb_defaultscreen.ncols = ri->ri_cols; 634 635 if (is_console) { 636 wsdisplay_cnattach(&platinumfb_defaultscreen, ri, 0, 0, 637 defattr); 638 vcons_replay_msgbuf(&platinumfb_console_screen); 639 } 640 641 aa.console = is_console; 642 aa.scrdata = &platinumfb_screenlist; 643 aa.accessops = &platinumfb_accessops; 644 aa.accesscookie = &sc->vd; 645 646 config_found(self, &aa, wsemuldisplaydevprint, CFARGS_NONE); 647 648 return 0; 649} 650 651static void 652platinumfb_set_hardware(struct platinumfb_softc *sc) 653{ 654 int i; 655 bool one_bank = sc->sc_vram == PLATINUM_FB_BANK_SIZE; 656 657 /* now start programming the chip */ 658 platinumfb_write_reg(sc, 24, 7); /* turn off display */ 659 660 for (i = 0; i < 26; ++i) 661 platinumfb_write_reg(sc, i+32, sc->sc_pfs->regs[i]); 662 663 platinumfb_write_reg(sc, 26+32, one_bank ? 664 sc->sc_pfs->offset[sc->sc_cmode] + 4 - sc->sc_cmode : 665 sc->sc_pfs->offset[sc->sc_cmode]); 666 667 /* 668 * reg 16 apparently needs an address 0x10 less the where frame 669 * buffer/ri_bits start for console text to be aligned. In 670 * addition, X memory maps (mmap) the frame buffer starting on 671 * page boundaries. So to get both X and console text aligned we 672 * start at the first page up from sc_fb[0]. Starting at sc_fb[0] 673 * did work on my machine but not sure if this negative offset 674 * would be problematic elsewhere. 675 * 676 * Not sure why linux used different fb offsets for each mode, as 677 * any addresses seemed to work as long as relative difference was 678 * 0x10. 679 */ 680 platinumfb_write_reg(sc, 16, platinumfb_page_align_up(sc) - 0x10); 681 682 platinumfb_write_reg(sc, 18, sc->sc_pfs->pitch[sc->sc_cmode]); 683 684 /* 685 * XXX register 19 setting looks wrong for 1 bank & 32 bpp. 686 * 512x384 is only resolution that would use such a setting, but 687 * that is not currently in videomodes.c 688 */ 689 if (sc->sc_cmode == PLATINUM_CMODE_32 && 690 (sc->sc_pfs->macmode == 1 || sc->sc_pfs->macmode == 2)) 691 aprint_error_dev(sc->sc_dev, 692 "platinumfb reg19 array out-of-bounds"); 693 694 platinumfb_write_reg(sc, 19, one_bank ? 695 sc->sc_pfs->mode[sc->sc_cmode+1] : /* XXX fix this for 32 bpp */ 696 sc->sc_pfs->mode[sc->sc_cmode]); 697 698 platinumfb_write_reg(sc, 20, one_bank ? 0x11 : 0x1011); 699 platinumfb_write_reg(sc, 21, 0x100); 700 platinumfb_write_reg(sc, 22, 1); 701 platinumfb_write_reg(sc, 23, 1); 702 platinumfb_write_reg(sc, 26, 0xc00); 703 platinumfb_write_reg(sc, 27, 0x235); 704 /* platinumfb_write_reg(sc, 27, 0x2aa); */ 705 706 platinumfb_store_d2(sc, 0, 707 one_bank ? sc->sc_pfs->dacula_ctrl[sc->sc_cmode] & 0xf : 708 sc->sc_pfs->dacula_ctrl[sc->sc_cmode]); 709 platinumfb_store_d2(sc, 1, 4); 710 platinumfb_store_d2(sc, 2, 0); 711 712 platinumfb_set_clock(sc); 713 714 platinumfb_write_reg(sc, 24, 0); /* turn display on */ 715} 716 717static int 718platinumfb_set_mode(struct platinumfb_softc *sc, 719 const struct videomode *mode, int depth) 720{ 721 int i; 722 723 /* first find the parameter for the mode register */ 724 i = 0; 725 while((i < __arraycount(pfb_setting)) && 726 (strcmp(mode->name, pfb_setting[i]->vmode_name) != 0)) 727 i++; 728 729 if (i >= __arraycount(pfb_setting)) { 730 aprint_error_dev(sc->sc_dev, 731 "Can't find a mode register value for %s\n", 732 mode->name); 733 return EINVAL; 734 } 735 736 /* found a mode */ 737 sc->sc_pfs = pfb_setting[i]; 738 739 /* determine depth settings */ 740 switch (depth) { 741 case 8: 742 default: 743 sc->sc_depth = 8; 744 sc->sc_cmode = PLATINUM_CMODE_8; 745 break; 746 case 15: 747 case 16: 748 /* 15 bpp but use 16 so X/wsfb works */ 749 sc->sc_depth = 16; 750 sc->sc_cmode = PLATINUM_CMODE_16; 751 break; 752 case 24: 753 case 32: 754 /* 24 bpp but use 32 so X/wsfb works */ 755 sc->sc_depth = 32; 756 sc->sc_cmode = PLATINUM_CMODE_32; 757 break; 758 } 759 760 sc->sc_modereg = sc->sc_pfs->macmode; 761 sc->sc_videomode = mode; 762 sc->sc_height = mode->vdisplay; 763 sc->sc_width = mode->hdisplay; 764 sc->sc_linebytes = sc->sc_width * (1 << sc->sc_cmode) + platinumfb_line_tweak(sc); 765 766 /* check if we have enough video memory */ 767 if (sc->sc_height * sc->sc_linebytes > sc->sc_vram - PAGE_SIZE) { 768 aprint_error_dev(sc->sc_dev, "Not enough video RAM for %s\n", 769 mode->name); 770 return EINVAL; 771 } 772 773 /* set up and write colormap */ 774 platinumfb_init_cmap(sc); 775 platinumfb_restore_palette(sc); 776 777 /* set hardware registers */ 778 platinumfb_set_hardware(sc); 779 780 aprint_normal_dev(sc->sc_dev, "switched to %s in %d bit color\n", 781 sc->sc_pfs->vmode_name, sc->sc_depth); 782 return 0; 783} 784 785static int 786platinumfb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, 787 struct lwp *l) 788{ 789 struct vcons_data *vd = v; 790 struct platinumfb_softc *sc = vd->cookie; 791 struct wsdisplay_fbinfo *wdf; 792 struct vcons_screen *ms = vd->active; 793 int i; 794 795 switch (cmd) { 796 case WSDISPLAYIO_GTYPE: 797 *(u_int *)data = WSDISPLAY_TYPE_PLATINUM; 798 return 0; 799 800 case WSDISPLAYIO_GINFO: 801 wdf = (void *)data; 802 wdf->height = ms->scr_ri.ri_height; 803 wdf->width = ms->scr_ri.ri_width; 804 wdf->depth = ms->scr_ri.ri_depth; 805 wdf->cmsize = 256; 806 return 0; 807 808 case WSDISPLAYIO_GVIDEO: 809 *(int *)data = sc->sc_on; 810 return 0; 811 812 case WSDISPLAYIO_SVIDEO: 813 /* 814 * poor man's screen blanking, just write zeros to colormap 815 * registers but don't save in softc. 816 */ 817 if (*(int *)data != sc->sc_on) { 818 sc->sc_on = (sc->sc_on == WSDISPLAYIO_VIDEO_ON ? 819 WSDISPLAYIO_VIDEO_OFF : WSDISPLAYIO_VIDEO_ON); 820 821 /* XXX need to lock colormap? */ 822 if (sc->sc_on == WSDISPLAYIO_VIDEO_OFF) { 823 for (i=0; i < 256; i++) 824 platinumfb_putpalreg(sc, i, 0, 0, 0); 825 } else { 826 platinumfb_restore_palette(sc); 827 } 828 } 829 return 0; 830 831 case WSDISPLAYIO_GETCMAP: 832 if (sc->sc_cmode == PLATINUM_CMODE_8) { 833 return platinumfb_getcmap(sc, 834 (struct wsdisplay_cmap *)data); 835 } else 836 return 0; 837 838 case WSDISPLAYIO_PUTCMAP: 839 if (sc->sc_cmode == PLATINUM_CMODE_8) { 840 return platinumfb_putcmap(sc, 841 (struct wsdisplay_cmap *)data); 842 } else 843 return 0; 844 845 case WSDISPLAYIO_SMODE: { 846 int new_mode = *(int*)data; 847 848 if (new_mode != sc->sc_mode) { 849 int new_depth = new_mode == WSDISPLAYIO_MODE_EMUL ? 850 PLATINUM_CONSOLE_DEPTH : PLATINUM_FB_DEPTH; 851 852 switch(new_mode) { 853 /* 854 * XXX - not sure how this is supposed to work for 855 * switching bpp between console and X, but cases with 856 * (EMUL MAPPED) or (EMUL MAPPED DUMBFB) work, but 857 * (EMUL DUMBFB) garbles screen for some reason. 858 */ 859 case WSDISPLAYIO_MODE_EMUL: 860 case WSDISPLAYIO_MODE_MAPPED: 861 /* case WSDISPLAYIO_MODE_DUMBFB: XXX */ 862 863 /* in case screen is "blanked" */ 864 platinumfb_restore_palette(sc); 865 866 sc->sc_mode = new_mode; 867 868 platinumfb_set_mode(sc, sc->sc_videomode, new_depth); 869 platinumfb_set_rasops(sc, &ms->scr_ri, true); 870 871 if (new_mode == WSDISPLAYIO_MODE_EMUL) 872 vcons_redraw_screen(ms); 873 } 874 } 875 876 return 0; 877 } 878 879 case WSDISPLAYIO_LINEBYTES: 880 *(u_int *)data = sc->sc_linebytes; 881 return 0; 882 883 case WSDISPLAYIO_GET_FBINFO: { 884 struct wsdisplayio_fbinfo *fbi = data; 885 return wsdisplayio_get_fbinfo(&ms->scr_ri, fbi); 886 } 887 888 } 889 return EPASSTHROUGH; 890} 891 892static paddr_t 893platinumfb_mmap(void *v, void *vs, off_t offset, int prot) 894{ 895 struct vcons_data *vd = v; 896 struct platinumfb_softc *sc = vd->cookie; 897 paddr_t pa = -1; 898 paddr_t fb_aligned = platinumfb_page_align_up(sc); 899 paddr_t fb_vram = sc->sc_vram - (fb_aligned - (paddr_t)sc->sc_fb); 900 901 /* XXX need to worry about superuser or mapping other registers? */ 902 903 if (offset >= 0 && offset < fb_vram) 904 pa = fb_aligned + offset; 905 906 return pa; 907} 908 909static void platinumfb_set_rasops(struct platinumfb_softc *sc, 910 struct rasops_info *ri, 911 int existing) 912{ 913 memset(ri, 0, sizeof(struct rasops_info)); 914 915 ri->ri_depth = sc->sc_depth; 916 ri->ri_width = sc->sc_width; 917 ri->ri_height = sc->sc_height; 918 ri->ri_stride = sc->sc_linebytes; 919 ri->ri_bits = (u_char*)platinumfb_page_align_up(sc); 920 ri->ri_flg = RI_FULLCLEAR; 921 922 if (existing) 923 ri->ri_flg |= RI_CLEAR; 924 925 switch (sc->sc_cmode) { 926 case PLATINUM_CMODE_8: 927 default: 928 ri->ri_flg |= RI_ENABLE_ALPHA | RI_8BIT_IS_RGB; 929 930 break; 931 case PLATINUM_CMODE_16: 932 if (strcmp(sc->sc_pfs->vmode_name, "640x480x60") == 0 || 933 strcmp(sc->sc_pfs->vmode_name, "800x600x75") == 0 ) 934 ri->ri_flg |= RI_ENABLE_ALPHA; 935 936 ri->ri_rnum = 5; 937 ri->ri_rpos = 10; 938 ri->ri_gnum = 5; 939 ri->ri_gpos = 5; 940 ri->ri_bnum = 5; 941 ri->ri_bpos = 0; 942 943 break; 944 case PLATINUM_CMODE_32: 945 if (strcmp(sc->sc_pfs->vmode_name, "640x480x60") == 0 || 946 strcmp(sc->sc_pfs->vmode_name, "800x600x75") == 0 ) 947 ri->ri_flg |= RI_ENABLE_ALPHA; 948 949 ri->ri_rnum = 8; 950 ri->ri_rpos = 16; 951 ri->ri_gnum = 8; 952 ri->ri_gpos = 8; 953 ri->ri_bnum = 8; 954 ri->ri_bpos = 0; 955 956 break; 957 } 958 959 rasops_init(ri, 0, 0); 960 ri->ri_caps = WSSCREEN_WSCOLORS; 961 962 rasops_reconfig(ri, sc->sc_height / ri->ri_font->fontheight, 963 sc->sc_width / ri->ri_font->fontwidth); 964 965} 966 967static void 968platinumfb_init_screen(void *cookie, struct vcons_screen *scr, 969 int existing, long *defattr) 970{ 971 struct platinumfb_softc *sc = cookie; 972 struct rasops_info *ri = &scr->scr_ri; 973 974 scr->scr_flags |= VCONS_DONT_READ; 975 976 platinumfb_set_rasops(sc, ri, existing); 977 978 ri->ri_hw = scr; 979} 980 981static int 982platinumfb_putcmap(struct platinumfb_softc *sc, struct wsdisplay_cmap *cm) 983{ 984 u_char *r, *g, *b; 985 u_int index = cm->index; 986 u_int count = cm->count; 987 int i, error; 988 u_char rbuf[256], gbuf[256], bbuf[256]; 989 990 if (cm->index >= 256 || cm->count > 256 || 991 (cm->index + cm->count) > 256) 992 return EINVAL; 993 error = copyin(cm->red, &rbuf[index], count); 994 if (error) 995 return error; 996 error = copyin(cm->green, &gbuf[index], count); 997 if (error) 998 return error; 999 error = copyin(cm->blue, &bbuf[index], count); 1000 if (error) 1001 return error; 1002 1003 memcpy(&sc->sc_cmap_red[index], &rbuf[index], count); 1004 memcpy(&sc->sc_cmap_green[index], &gbuf[index], count); 1005 memcpy(&sc->sc_cmap_blue[index], &bbuf[index], count); 1006 1007 /* write colormap registers if not currently blanked */ 1008 if (sc->sc_on == WSDISPLAYIO_VIDEO_ON) { 1009 r = &sc->sc_cmap_red[index]; 1010 g = &sc->sc_cmap_green[index]; 1011 b = &sc->sc_cmap_blue[index]; 1012 1013 for (i = 0; i < count; i++) { 1014 platinumfb_putpalreg(sc, index, *r, *g, *b); 1015 index++; 1016 r++, g++, b++; 1017 } 1018 } 1019 1020 return 0; 1021} 1022 1023static int 1024platinumfb_getcmap(struct platinumfb_softc *sc, struct wsdisplay_cmap *cm) 1025{ 1026 u_int index = cm->index; 1027 u_int count = cm->count; 1028 int error; 1029 1030 if (index >= 255 || count > 256 || index + count > 256) 1031 return EINVAL; 1032 1033 error = copyout(&sc->sc_cmap_red[index], cm->red, count); 1034 if (error) 1035 return error; 1036 error = copyout(&sc->sc_cmap_green[index], cm->green, count); 1037 if (error) 1038 return error; 1039 error = copyout(&sc->sc_cmap_blue[index], cm->blue, count); 1040 if (error) 1041 return error; 1042 1043 return 0; 1044} 1045