1 /* $OpenBSD: sisfb.c,v 1.9 2021/03/11 11:16:58 jsg Exp $ */ 2 3 /* 4 * Copyright (c) 2010 Miodrag Vallat. 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /* 20 * Minimalistic driver for the SIS315 Pro frame buffer found on the 21 * Lemote Fuloong 2F systems. 22 * Does not support acceleration, mode change, secondary output, or 23 * anything fancy. 24 */ 25 26 #include <sys/param.h> 27 #include <sys/systm.h> 28 #include <sys/device.h> 29 30 #include <machine/bus.h> 31 #include <machine/cpu.h> 32 33 #include <uvm/uvm_extern.h> 34 35 #include <dev/pci/pcireg.h> 36 #include <dev/pci/pcivar.h> 37 #include <dev/pci/pcidevs.h> 38 39 #include <dev/ic/mc6845.h> 40 41 #include <dev/wscons/wsconsio.h> 42 #include <dev/wscons/wsdisplayvar.h> 43 #include <dev/rasops/rasops.h> 44 45 struct sisfb_softc; 46 47 /* minimal frame buffer information, suitable for early console */ 48 struct sisfb { 49 struct sisfb_softc *sc; 50 struct rasops_info ri; 51 uint8_t cmap[256 * 3]; 52 53 bus_space_tag_t fbt; 54 bus_space_handle_t fbh; 55 56 bus_space_tag_t mmiot; 57 bus_space_handle_t mmioh; 58 59 bus_space_tag_t iot; 60 bus_space_handle_t ioh; 61 62 struct wsscreen_descr wsd; 63 }; 64 65 struct sisfb_softc { 66 struct device sc_dev; 67 struct sisfb *sc_fb; 68 struct sisfb sc_fb_store; 69 70 struct wsscreen_list sc_wsl; 71 struct wsscreen_descr *sc_scrlist[1]; 72 int sc_nscr; 73 }; 74 75 int sisfb_match(struct device *, void *, void *); 76 void sisfb_attach(struct device *, struct device *, void *); 77 78 const struct cfattach sisfb_ca = { 79 sizeof(struct sisfb_softc), sisfb_match, sisfb_attach 80 }; 81 82 struct cfdriver sisfb_cd = { 83 NULL, "sisfb", DV_DULL 84 }; 85 86 int sisfb_alloc_screen(void *, const struct wsscreen_descr *, void **, int *, 87 int *, uint32_t *); 88 void sisfb_free_screen(void *, void *); 89 int sisfb_ioctl(void *, u_long, caddr_t, int, struct proc *); 90 int sisfb_list_font(void *, struct wsdisplay_font *); 91 int sisfb_load_font(void *, void *, struct wsdisplay_font *); 92 paddr_t sisfb_mmap(void *, off_t, int); 93 int sisfb_show_screen(void *, void *, int, void (*)(void *, int, int), 94 void *); 95 96 struct wsdisplay_accessops sisfb_accessops = { 97 .ioctl = sisfb_ioctl, 98 .mmap = sisfb_mmap, 99 .alloc_screen = sisfb_alloc_screen, 100 .free_screen = sisfb_free_screen, 101 .show_screen = sisfb_show_screen, 102 .load_font = sisfb_load_font, 103 .list_font = sisfb_list_font 104 }; 105 106 int sisfb_getcmap(uint8_t *, struct wsdisplay_cmap *); 107 void sisfb_loadcmap(struct sisfb *, int, int); 108 int sisfb_putcmap(uint8_t *, struct wsdisplay_cmap *); 109 int sisfb_setup(struct sisfb *); 110 111 static struct sisfb sisfbcn; 112 113 const struct pci_matchid sisfb_devices[] = { 114 { PCI_VENDOR_SIS, PCI_PRODUCT_SIS_315PRO_VGA } 115 }; 116 117 /* 118 * Control Register access 119 * 120 * These are 8 bit registers; the choice of larger width types is intentional. 121 */ 122 123 #define SIS_VGA_PORT_OFFSET 0x380 124 125 #define SEQ_ADDR (0x3c4 - SIS_VGA_PORT_OFFSET) 126 #define SEQ_DATA (0x3c5 - SIS_VGA_PORT_OFFSET) 127 #define DAC_ADDR (0x3c8 - SIS_VGA_PORT_OFFSET) 128 #define DAC_DATA (0x3c9 - SIS_VGA_PORT_OFFSET) 129 #undef CRTC_ADDR 130 #define CRTC_ADDR (0x3d4 - SIS_VGA_PORT_OFFSET) 131 #define CRTC_DATA (0x3d5 - SIS_VGA_PORT_OFFSET) 132 133 static inline uint sisfb_crtc_read(struct sisfb *, uint); 134 static inline uint sisfb_seq_read(struct sisfb *, uint); 135 static inline void sisfb_seq_write(struct sisfb *, uint, uint); 136 137 static inline uint 138 sisfb_crtc_read(struct sisfb *fb, uint idx) 139 { 140 uint val; 141 bus_space_write_1(fb->iot, fb->ioh, CRTC_ADDR, idx); 142 val = bus_space_read_1(fb->iot, fb->ioh, CRTC_DATA); 143 #ifdef SIS_DEBUG 144 printf("CRTC %04x -> %02x\n", idx, val); 145 #endif 146 return val; 147 } 148 149 static inline uint 150 sisfb_seq_read(struct sisfb *fb, uint idx) 151 { 152 uint val; 153 bus_space_write_1(fb->iot, fb->ioh, SEQ_ADDR, idx); 154 val = bus_space_read_1(fb->iot, fb->ioh, SEQ_DATA); 155 #ifdef SIS_DEBUG 156 printf("SEQ %04x -> %02x\n", idx, val); 157 #endif 158 return val; 159 } 160 161 static inline void 162 sisfb_seq_write(struct sisfb *fb, uint idx, uint val) 163 { 164 #ifdef SIS_DEBUG 165 printf("SEQ %04x <- %02x\n", idx, val); 166 #endif 167 bus_space_write_1(fb->iot, fb->ioh, SEQ_ADDR, idx); 168 bus_space_write_1(fb->iot, fb->ioh, SEQ_DATA, val); 169 } 170 171 int 172 sisfb_match(struct device *parent, void *vcf, void *aux) 173 { 174 struct pci_attach_args *pa = (struct pci_attach_args *)aux; 175 176 return pci_matchbyid(pa, sisfb_devices, nitems(sisfb_devices)); 177 } 178 179 void 180 sisfb_attach(struct device *parent, struct device *self, void *aux) 181 { 182 struct sisfb_softc *sc = (struct sisfb_softc *)self; 183 struct pci_attach_args *pa = (struct pci_attach_args *)aux; 184 struct wsemuldisplaydev_attach_args waa; 185 bus_space_tag_t fbt, mmiot, iot; 186 bus_space_handle_t fbh, mmioh, ioh; 187 bus_size_t fbsize, mmiosize; 188 struct sisfb *fb; 189 int console; 190 191 if (pci_mapreg_map(pa, PCI_MAPREG_START, PCI_MAPREG_TYPE_MEM, 192 BUS_SPACE_MAP_LINEAR, &fbt, &fbh, NULL, &fbsize, 0) != 0) { 193 printf(": can't map frame buffer\n"); 194 return; 195 } 196 197 if (pci_mapreg_map(pa, PCI_MAPREG_START + 4, PCI_MAPREG_TYPE_MEM, 198 0, &mmiot, &mmioh, NULL, &mmiosize, 0) != 0) { 199 printf(": can't map mmio area\n"); 200 goto fail1; 201 } 202 203 if (pci_mapreg_map(pa, PCI_MAPREG_START + 8, PCI_MAPREG_TYPE_IO, 204 0, &iot, &ioh, NULL, NULL, 0) != 0) { 205 printf(": can't map registers\n"); 206 goto fail2; 207 } 208 209 console = sisfbcn.ri.ri_hw != NULL; 210 211 if (console) 212 fb = &sisfbcn; 213 else 214 fb = &sc->sc_fb_store; 215 216 fb->sc = sc; 217 fb->fbt = fbt; 218 fb->fbh = fbh; 219 fb->mmiot = mmiot; 220 fb->mmioh = mmioh; 221 fb->iot = iot; 222 fb->ioh = ioh; 223 sc->sc_fb = fb; 224 225 if (!console) { 226 if (sisfb_setup(fb) != 0) { 227 printf(": can't setup frame buffer\n"); 228 return; 229 } 230 } 231 232 printf(": %dx%d, %dbpp\n", 233 fb->ri.ri_width, fb->ri.ri_height, fb->ri.ri_depth); 234 235 sc->sc_scrlist[0] = &fb->wsd; 236 sc->sc_wsl.nscreens = 1; 237 sc->sc_wsl.screens = (const struct wsscreen_descr **)sc->sc_scrlist; 238 239 waa.console = console; 240 waa.scrdata = &sc->sc_wsl; 241 waa.accessops = &sisfb_accessops; 242 waa.accesscookie = sc; 243 waa.defaultscreens = 0; 244 245 config_found((struct device *)sc, &waa, wsemuldisplaydevprint); 246 return; 247 248 fail2: 249 bus_space_unmap(mmiot, mmioh, mmiosize); 250 fail1: 251 bus_space_unmap(fbt, fbh, fbsize); 252 } 253 254 /* 255 * wsdisplay accesops 256 */ 257 258 int 259 sisfb_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep, 260 int *curxp, int *curyp, uint32_t *attrp) 261 { 262 struct sisfb_softc *sc = (struct sisfb_softc *)v; 263 struct rasops_info *ri = &sc->sc_fb->ri; 264 265 if (sc->sc_nscr > 0) 266 return ENOMEM; 267 268 *cookiep = ri; 269 *curxp = *curyp = 0; 270 ri->ri_ops.pack_attr(ri, 0, 0, 0, attrp); 271 sc->sc_nscr++; 272 273 return 0; 274 } 275 276 void 277 sisfb_free_screen(void *v, void *cookie) 278 { 279 struct sisfb_softc *sc = (struct sisfb_softc *)v; 280 281 sc->sc_nscr--; 282 } 283 284 int 285 sisfb_ioctl(void *v, u_long cmd, caddr_t data, int flags, struct proc *p) 286 { 287 struct sisfb_softc *sc = (struct sisfb_softc *)v; 288 struct sisfb *fb = sc->sc_fb; 289 struct rasops_info *ri = &fb->ri; 290 struct wsdisplay_cmap *cm; 291 struct wsdisplay_fbinfo *wdf; 292 int rc; 293 294 switch (cmd) { 295 case WSDISPLAYIO_GTYPE: 296 *(uint *)data = WSDISPLAY_TYPE_SISFB; 297 break; 298 case WSDISPLAYIO_GINFO: 299 wdf = (struct wsdisplay_fbinfo *)data; 300 wdf->width = ri->ri_width; 301 wdf->height = ri->ri_height; 302 wdf->depth = ri->ri_depth; 303 wdf->cmsize = 256; 304 break; 305 case WSDISPLAYIO_LINEBYTES: 306 *(uint *)data = ri->ri_stride; 307 break; 308 case WSDISPLAYIO_GETCMAP: 309 cm = (struct wsdisplay_cmap *)data; 310 rc = sisfb_getcmap(fb->cmap, cm); 311 if (rc != 0) 312 return rc; 313 break; 314 case WSDISPLAYIO_PUTCMAP: 315 cm = (struct wsdisplay_cmap *)data; 316 rc = sisfb_putcmap(fb->cmap, cm); 317 if (rc != 0) 318 return rc; 319 if (ri->ri_depth == 8) 320 sisfb_loadcmap(fb, cm->index, cm->count); 321 break; 322 default: 323 return -1; 324 } 325 326 return 0; 327 } 328 329 int 330 sisfb_show_screen(void *v, void *cookie, int waitok, 331 void (*cb)(void *, int, int), void *cbarg) 332 { 333 return 0; 334 } 335 336 paddr_t 337 sisfb_mmap(void *v, off_t offset, int prot) 338 { 339 struct sisfb_softc *sc = (struct sisfb_softc *)v; 340 struct rasops_info *ri = &sc->sc_fb->ri; 341 342 if ((offset & PAGE_MASK) != 0) 343 return -1; 344 345 if (offset < 0 || offset >= ri->ri_stride * ri->ri_height) 346 return -1; 347 348 /* 349 * Don't allow mmap if the frame buffer area is not page aligned. 350 * XXX we should reprogram it to a page aligned boundary at attach 351 * XXX time if this isn't the case. 352 */ 353 if (((paddr_t)ri->ri_bits & PAGE_MASK) != 0) 354 return -1; 355 356 return XKPHYS_TO_PHYS((paddr_t)ri->ri_bits) + offset; 357 } 358 359 int 360 sisfb_load_font(void *v, void *emulcookie, struct wsdisplay_font *font) 361 { 362 struct sisfb_softc *sc = (struct sisfb_softc *)v; 363 struct rasops_info *ri = &sc->sc_fb->ri; 364 365 return rasops_load_font(ri, emulcookie, font); 366 } 367 368 int 369 sisfb_list_font(void *v, struct wsdisplay_font *font) 370 { 371 struct sisfb_softc *sc = (struct sisfb_softc *)v; 372 struct rasops_info *ri = &sc->sc_fb->ri; 373 374 return rasops_list_font(ri, font); 375 } 376 377 /* 378 * Frame buffer initialization. 379 */ 380 381 int 382 sisfb_setup(struct sisfb *fb) 383 { 384 struct rasops_info *ri; 385 uint width, height, bpp; 386 bus_size_t fbaddr; 387 uint tmp; 388 389 /* 390 * Unlock access to extended registers. 391 */ 392 393 sisfb_seq_write(fb, 0x05, 0x86); 394 395 /* 396 * Try and figure out display settings. 397 */ 398 399 height = sisfb_crtc_read(fb, CRTC_VDE); 400 tmp = sisfb_crtc_read(fb, CRTC_OVERFLL); 401 if (ISSET(tmp, 1 << 1)) 402 height |= 1 << 8; 403 if (ISSET(tmp, 1 << 6)) 404 height |= 1 << 9; 405 tmp = sisfb_seq_read(fb, 0x0a); 406 if (ISSET(tmp, 1 << 1)) 407 height |= 1 << 10; 408 height++; 409 410 width = sisfb_crtc_read(fb, CRTC_HDISPLE); 411 tmp = sisfb_seq_read(fb, 0x0b); 412 if (ISSET(tmp, 1 << 2)) 413 width |= 1 << 8; 414 if (ISSET(tmp, 1 << 3)) 415 width |= 1 << 9; 416 width++; 417 width <<= 3; 418 419 fbaddr = sisfb_crtc_read(fb, CRTC_STARTADRL) | 420 (sisfb_crtc_read(fb, CRTC_STARTADRH) << 8) | 421 (sisfb_seq_read(fb, 0x0d) << 16) | 422 ((sisfb_seq_read(fb, 0x37) & 0x03) << 24); 423 fbaddr <<= 2; 424 #ifdef SIS_DEBUG 425 printf("FBADDR %08x\n", fbaddr); 426 #endif 427 428 tmp = sisfb_seq_read(fb, 0x06); 429 switch (tmp & 0x1c) { 430 case 0x00: 431 bpp = 8; 432 break; 433 case 0x04: 434 bpp = 15; 435 break; 436 case 0x08: 437 bpp = 16; 438 break; 439 case 0x10: 440 bpp = 32; 441 break; 442 default: 443 return EINVAL; 444 } 445 446 ri = &fb->ri; 447 ri->ri_width = width; 448 ri->ri_height = height; 449 ri->ri_depth = bpp; 450 ri->ri_stride = (ri->ri_width * ri->ri_depth) / 8; 451 ri->ri_flg = RI_CENTER | RI_CLEAR | RI_FULLCLEAR; 452 ri->ri_bits = (void *)(bus_space_vaddr(fb->fbt, fb->fbh) + fbaddr); 453 ri->ri_hw = fb; 454 455 #ifdef __MIPSEL__ 456 /* swap B and R */ 457 switch (bpp) { 458 case 15: 459 ri->ri_rnum = 5; 460 ri->ri_rpos = 10; 461 ri->ri_gnum = 5; 462 ri->ri_gpos = 5; 463 ri->ri_bnum = 5; 464 ri->ri_bpos = 0; 465 break; 466 case 16: 467 ri->ri_rnum = 5; 468 ri->ri_rpos = 11; 469 ri->ri_gnum = 6; 470 ri->ri_gpos = 5; 471 ri->ri_bnum = 5; 472 ri->ri_bpos = 0; 473 break; 474 } 475 #endif 476 477 bcopy(rasops_cmap, fb->cmap, sizeof(fb->cmap)); 478 if (bpp == 8) 479 sisfb_loadcmap(fb, 0, 256); 480 481 rasops_init(ri, 160, 160); 482 483 strlcpy(fb->wsd.name, "std", sizeof(fb->wsd.name)); 484 fb->wsd.ncols = ri->ri_cols; 485 fb->wsd.nrows = ri->ri_rows; 486 fb->wsd.textops = &ri->ri_ops; 487 fb->wsd.fontwidth = ri->ri_font->fontwidth; 488 fb->wsd.fontheight = ri->ri_font->fontheight; 489 fb->wsd.capabilities = ri->ri_caps; 490 491 return 0; 492 } 493 494 /* 495 * Colormap handling routines. 496 */ 497 498 void 499 sisfb_loadcmap(struct sisfb *fb, int baseidx, int count) 500 { 501 uint8_t *cmap = fb->cmap + baseidx * 3; 502 503 bus_space_write_1(fb->iot, fb->ioh, DAC_ADDR, baseidx); 504 while (count-- != 0) { 505 bus_space_write_1(fb->iot, fb->ioh, DAC_DATA, *cmap++ >> 2); 506 bus_space_write_1(fb->iot, fb->ioh, DAC_DATA, *cmap++ >> 2); 507 bus_space_write_1(fb->iot, fb->ioh, DAC_DATA, *cmap++ >> 2); 508 } 509 } 510 511 int 512 sisfb_getcmap(uint8_t *cmap, struct wsdisplay_cmap *cm) 513 { 514 uint index = cm->index, count = cm->count, i; 515 uint8_t ramp[256], *dst, *src; 516 int rc; 517 518 if (index >= 256 || count > 256 - index) 519 return EINVAL; 520 521 index *= 3; 522 523 src = cmap + index; 524 dst = ramp; 525 for (i = 0; i < count; i++) 526 *dst++ = *src, src += 3; 527 rc = copyout(ramp, cm->red, count); 528 if (rc != 0) 529 return rc; 530 531 src = cmap + index + 1; 532 dst = ramp; 533 for (i = 0; i < count; i++) 534 *dst++ = *src, src += 3; 535 rc = copyout(ramp, cm->green, count); 536 if (rc != 0) 537 return rc; 538 539 src = cmap + index + 2; 540 dst = ramp; 541 for (i = 0; i < count; i++) 542 *dst++ = *src, src += 3; 543 rc = copyout(ramp, cm->blue, count); 544 if (rc != 0) 545 return rc; 546 547 return 0; 548 } 549 550 int 551 sisfb_putcmap(uint8_t *cmap, struct wsdisplay_cmap *cm) 552 { 553 uint index = cm->index, count = cm->count, i; 554 uint8_t ramp[256], *dst, *src; 555 int rc; 556 557 if (index >= 256 || count > 256 - index) 558 return EINVAL; 559 560 index *= 3; 561 562 rc = copyin(cm->red, ramp, count); 563 if (rc != 0) 564 return rc; 565 dst = cmap + index; 566 src = ramp; 567 for (i = 0; i < count; i++) 568 *dst = *src++, dst += 3; 569 570 rc = copyin(cm->green, ramp, count); 571 if (rc != 0) 572 return rc; 573 dst = cmap + index + 1; 574 src = ramp; 575 for (i = 0; i < count; i++) 576 *dst = *src++, dst += 3; 577 578 rc = copyin(cm->blue, ramp, count); 579 if (rc != 0) 580 return rc; 581 dst = cmap + index + 2; 582 src = ramp; 583 for (i = 0; i < count; i++) 584 *dst = *src++, dst += 3; 585 586 return 0; 587 } 588 589 /* 590 * Early console code 591 */ 592 593 int sisfb_cnattach(bus_space_tag_t, bus_space_tag_t, pcitag_t, pcireg_t); 594 595 int 596 sisfb_cnattach(bus_space_tag_t memt, bus_space_tag_t iot, pcitag_t tag, 597 pcireg_t id) 598 { 599 uint32_t defattr; 600 struct rasops_info *ri; 601 pcireg_t bar; 602 int rc; 603 604 /* filter out unrecognized devices */ 605 switch (id) { 606 default: 607 return ENODEV; 608 case PCI_ID_CODE(PCI_VENDOR_SIS, PCI_PRODUCT_SIS_315PRO_VGA): 609 break; 610 } 611 612 bar = pci_conf_read_early(tag, PCI_MAPREG_START); 613 if (PCI_MAPREG_TYPE(bar) != PCI_MAPREG_TYPE_MEM) 614 return EINVAL; 615 sisfbcn.fbt = memt; 616 rc = bus_space_map(memt, PCI_MAPREG_MEM_ADDR(bar), 1 /* XXX */, 617 BUS_SPACE_MAP_LINEAR, &sisfbcn.fbh); 618 if (rc != 0) 619 return rc; 620 621 bar = pci_conf_read_early(tag, PCI_MAPREG_START + 4); 622 if (PCI_MAPREG_TYPE(bar) != PCI_MAPREG_TYPE_MEM) 623 return EINVAL; 624 sisfbcn.mmiot = memt; 625 rc = bus_space_map(memt, PCI_MAPREG_MEM_ADDR(bar), 1 /* XXX */, 626 BUS_SPACE_MAP_LINEAR, &sisfbcn.mmioh); 627 if (rc != 0) 628 return rc; 629 630 bar = pci_conf_read_early(tag, PCI_MAPREG_START + 8); 631 if (PCI_MAPREG_TYPE(bar) != PCI_MAPREG_TYPE_IO) 632 return EINVAL; 633 sisfbcn.iot = iot; 634 rc = bus_space_map(iot, PCI_MAPREG_MEM_ADDR(bar), 1 /* XXX */, 635 0, &sisfbcn.ioh); 636 if (rc != 0) 637 return rc; 638 639 rc = sisfb_setup(&sisfbcn); 640 if (rc != 0) 641 return rc; 642 643 ri = &sisfbcn.ri; 644 ri->ri_ops.pack_attr(ri, 0, 0, 0, &defattr); 645 wsdisplay_cnattach(&sisfbcn.wsd, ri, 0, 0, defattr); 646 647 return 0; 648 } 649