1 /* $NetBSD: agten.c,v 1.8 2007/10/19 12:01:09 ad Exp $ */ 2 3 /*- 4 * Copyright (c) 2007 Michael Lorenz 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of The NetBSD Foundation nor the names of its 16 * contributors may be used to endorse or promote products derived 17 * from this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: agten.c,v 1.8 2007/10/19 12:01:09 ad Exp $"); 34 35 /* 36 * a driver for the Fujitsu AG-10e SBus framebuffer 37 * 38 * this thing is Frankenstein's Monster among graphics boards. 39 * it contains three graphics chips: 40 * a GLint - 24bit stuff, double-buffered 41 * an Imagine 128 which provides an 8bit overlay 42 * a Weitek P9100 which provides WIDs 43 * so here we need to mess only with the P9100 and the I128 - for X we just 44 * hide the overlay and let the Xserver mess with the GLint 45 */ 46 47 #include <sys/param.h> 48 #include <sys/systm.h> 49 #include <sys/kernel.h> 50 #include <sys/device.h> 51 #include <sys/proc.h> 52 #include <sys/mutex.h> 53 #include <sys/ioctl.h> 54 #include <sys/kernel.h> 55 #include <sys/systm.h> 56 #include <sys/conf.h> 57 58 #include <dev/sun/fbio.h> 59 #include <dev/sun/fbvar.h> 60 #include <dev/sun/btreg.h> 61 #include <dev/sun/btvar.h> 62 63 #include <sys/bus.h> 64 #include <machine/autoconf.h> 65 66 #include <dev/sbus/sbusvar.h> 67 68 #include <dev/wscons/wsconsio.h> 69 #include <dev/wscons/wsdisplayvar.h> 70 #include <dev/rasops/rasops.h> 71 #include <dev/wsfont/wsfont.h> 72 73 #include <dev/wscons/wsdisplay_vconsvar.h> 74 75 #include <dev/sbus/p9100reg.h> 76 #include <dev/ic/ibm561reg.h> 77 #include <dev/ic/i128reg.h> 78 #include <dev/ic/i128var.h> 79 80 #include "opt_agten.h" 81 82 static int agten_match(struct device *, struct cfdata *, void *); 83 static void agten_attach(struct device *, struct device *, void *); 84 85 static int agten_ioctl(void *, void *, u_long, void *, int, struct lwp *); 86 static paddr_t agten_mmap(void *, void *, off_t, int); 87 static void agten_init_screen(void *, struct vcons_screen *, int, long *); 88 89 struct agten_softc { 90 struct device sc_dev; /* base device */ 91 struct sbusdev sc_sd; /* sbus device */ 92 struct fbdevice sc_fb; /* frame buffer device */ 93 94 struct vcons_screen sc_console_screen; 95 struct wsscreen_descr sc_defaultscreen_descr; 96 const struct wsscreen_descr *sc_screens[1]; 97 struct wsscreen_list sc_screenlist; 98 99 bus_space_tag_t sc_bustag; 100 101 bus_space_handle_t sc_i128_fbh; 102 bus_size_t sc_i128_fbsz; 103 bus_space_handle_t sc_i128_regh; 104 bus_space_handle_t sc_p9100_regh; 105 bus_addr_t sc_glint_fb; 106 bus_addr_t sc_glint_regs; 107 uint32_t sc_glint_fbsz; 108 109 uint32_t sc_width; 110 uint32_t sc_height; /* panel width / height */ 111 uint32_t sc_stride; 112 uint32_t sc_depth; 113 114 int sc_cursor_x; 115 int sc_cursor_y; 116 int sc_video; /* video output enabled */ 117 118 /* some /dev/fb* stuff */ 119 int sc_fb_is_open; 120 121 union bt_cmap sc_cmap; /* Brooktree color map */ 122 123 int sc_mode; 124 uint32_t sc_bg; 125 struct vcons_data vd; 126 }; 127 128 CFATTACH_DECL(agten, sizeof(struct agten_softc), 129 agten_match, agten_attach, NULL, NULL); 130 131 132 static int agten_putcmap(struct agten_softc *, struct wsdisplay_cmap *); 133 static int agten_getcmap(struct agten_softc *, struct wsdisplay_cmap *); 134 static int agten_putpalreg(struct agten_softc *, uint8_t, uint8_t, 135 uint8_t, uint8_t); 136 static void agten_init(struct agten_softc *); 137 static void agten_gfx(struct agten_softc *); 138 static void agten_set_video(struct agten_softc *, int); 139 static int agten_get_video(struct agten_softc *); 140 141 static void agten_copycols(void *, int, int, int, int); 142 static void agten_erasecols(void *, int, int, int, long); 143 static void agten_copyrows(void *, int, int, int); 144 static void agten_eraserows(void *, int, int, long); 145 146 static void agten_move_cursor(struct agten_softc *, int, int); 147 static int agten_do_cursor(struct agten_softc *sc, 148 struct wsdisplay_cursor *); 149 static int agten_do_sun_cursor(struct agten_softc *sc, 150 struct fbcursor *); 151 152 static uint16_t util_interleave(uint8_t, uint8_t); 153 static uint16_t util_interleave_lin(uint8_t, uint8_t); 154 155 extern const u_char rasops_cmap[768]; 156 157 struct wsdisplay_accessops agten_accessops = { 158 agten_ioctl, 159 agten_mmap, 160 NULL, /* alloc_screen */ 161 NULL, /* free_screen */ 162 NULL, /* show_screen */ 163 NULL, /* load_font */ 164 NULL, /* pollc */ 165 NULL /* scroll */ 166 }; 167 168 /* /dev/fb* stuff */ 169 extern struct cfdriver agten_cd; 170 171 static int agten_fb_open(dev_t, int, int, struct lwp *); 172 static int agten_fb_close(dev_t, int, int, struct lwp *); 173 static int agten_fb_ioctl(dev_t, u_long, void *, int, struct lwp *); 174 static paddr_t agten_fb_mmap(dev_t, off_t, int); 175 static void agten_fb_unblank(struct device *); 176 177 static struct fbdriver agtenfbdriver = { 178 agten_fb_unblank, agten_fb_open, agten_fb_close, agten_fb_ioctl, 179 nopoll, agten_fb_mmap, nokqfilter 180 }; 181 182 static inline void 183 agten_write_dac(struct agten_softc *sc, int reg, uint8_t val) 184 { 185 bus_space_write_4(sc->sc_bustag, sc->sc_p9100_regh, 186 0x200 + (reg << 2), (uint32_t)val << 16); 187 } 188 189 static inline void 190 agten_write_idx(struct agten_softc *sc, int offset) 191 { 192 bus_space_write_4(sc->sc_bustag, sc->sc_p9100_regh, 193 0x200 + (IBM561_ADDR_LOW << 2), (offset & 0xff) << 16); 194 bus_space_write_4(sc->sc_bustag, sc->sc_p9100_regh, 195 0x200 + (IBM561_ADDR_HIGH << 2), ((offset >> 8) & 0xff) << 16); 196 } 197 198 static inline void 199 agten_write_dac_10(struct agten_softc *sc, int reg, uint16_t val) 200 { 201 agten_write_dac(sc, reg, (val >> 2) & 0xff); 202 agten_write_dac(sc, reg, (val & 0x3) << 6); 203 } 204 205 static int 206 agten_match(struct device *dev, struct cfdata *cf, void *aux) 207 { 208 struct sbus_attach_args *sa = aux; 209 210 if (strcmp("PFU,aga", sa->sa_name) == 0) 211 return 100; 212 return 0; 213 } 214 215 static void 216 agten_attach(struct device *parent, struct device *dev, void *aux) 217 { 218 struct agten_softc *sc = (struct agten_softc *)dev; 219 struct sbus_attach_args *sa = aux; 220 struct fbdevice *fb = &sc->sc_fb; 221 struct wsemuldisplaydev_attach_args aa; 222 struct rasops_info *ri; 223 long defattr; 224 uint32_t reg; 225 int node = sa->sa_node; 226 int console; 227 228 sc->sc_defaultscreen_descr = (struct wsscreen_descr){ 229 "default", 230 0, 0, 231 NULL, 232 8, 16, 233 WSSCREEN_WSCOLORS | WSSCREEN_HILIT, 234 NULL 235 }; 236 sc->sc_screens[0] = &sc->sc_defaultscreen_descr; 237 sc->sc_screenlist = (struct wsscreen_list){1, sc->sc_screens}; 238 sc->sc_mode = WSDISPLAYIO_MODE_EMUL; 239 sc->sc_fb_is_open = 0; 240 sc->sc_video = -1; 241 sc->sc_bustag = sa->sa_bustag; 242 243 reg = prom_getpropint(node, "i128_fb_physaddr", -1); 244 sc->sc_i128_fbsz = prom_getpropint(node, "i128_fb_size", -1); 245 if (sbus_bus_map(sc->sc_bustag, 246 sa->sa_reg[0].oa_space, sa->sa_reg[0].oa_base + reg, 247 sc->sc_i128_fbsz, BUS_SPACE_MAP_LINEAR, &sc->sc_i128_fbh) != 0) { 248 249 aprint_error("%s: unable to map the framebuffer\n", 250 dev->dv_xname); 251 return; 252 } 253 fb->fb_pixels = bus_space_vaddr(sc->sc_bustag, sc->sc_i128_fbh); 254 255 reg = prom_getpropint(node, "i128_reg_physaddr", -1); 256 if (sbus_bus_map(sc->sc_bustag, 257 sa->sa_reg[0].oa_space, sa->sa_reg[0].oa_base + reg, 258 0x10000, 0, &sc->sc_i128_regh) != 0) { 259 260 aprint_error("%s: unable to map I128 registers\n", 261 dev->dv_xname); 262 return; 263 } 264 265 reg = prom_getpropint(node, "p9100_reg_physaddr", -1); 266 if (sbus_bus_map(sc->sc_bustag, 267 sa->sa_reg[0].oa_space, sa->sa_reg[0].oa_base + reg, 268 0x8000, 0, &sc->sc_p9100_regh) != 0) { 269 270 aprint_error("%s: unable to map P9100 registers\n", 271 dev->dv_xname); 272 return; 273 } 274 275 reg = prom_getpropint(node, "glint_fb0_physaddr", -1); 276 sc->sc_glint_fb = sbus_bus_addr(sc->sc_bustag, 277 sa->sa_reg[0].oa_space, sa->sa_reg[0].oa_base + reg); 278 sc->sc_glint_fbsz = prom_getpropint(node, "glint_lb_size", -1); 279 reg = prom_getpropint(node, "glint_reg_physaddr", -1); 280 sc->sc_glint_regs = sbus_bus_addr(sc->sc_bustag, 281 sa->sa_reg[0].oa_space, sa->sa_reg[0].oa_base + reg); 282 283 sbus_establish(&sc->sc_sd, &sc->sc_dev); 284 285 #if 0 286 bus_intr_establish(sc->sc_bustag, sa->sa_pri, IPL_BIO, 287 agten_intr, sc); 288 #endif 289 290 sc->sc_width = prom_getpropint(node, "ffb_width", 1152); 291 sc->sc_height = prom_getpropint(node, "ffb_height", 900); 292 sc->sc_depth = prom_getpropint(node, "ffb_depth", 8); 293 sc->sc_stride = sc->sc_width * (sc->sc_depth >> 3); 294 295 printf(": %dx%d\n", sc->sc_width, sc->sc_height); 296 agten_init(sc); 297 298 console = fb_is_console(node); 299 300 vcons_init(&sc->vd, sc, &sc->sc_defaultscreen_descr, 301 &agten_accessops); 302 sc->vd.init_screen = agten_init_screen; 303 304 ri = &sc->sc_console_screen.scr_ri; 305 306 if (console) { 307 vcons_init_screen(&sc->vd, &sc->sc_console_screen, 1, 308 &defattr); 309 sc->sc_console_screen.scr_flags |= VCONS_SCREEN_IS_STATIC; 310 311 sc->sc_defaultscreen_descr.textops = &ri->ri_ops; 312 sc->sc_defaultscreen_descr.capabilities = ri->ri_caps; 313 sc->sc_defaultscreen_descr.nrows = ri->ri_rows; 314 sc->sc_defaultscreen_descr.ncols = ri->ri_cols; 315 wsdisplay_cnattach(&sc->sc_defaultscreen_descr, ri, 0, 0, 316 defattr); 317 i128_rectfill(sc->sc_bustag, sc->sc_i128_regh, 0, 0, 318 sc->sc_width, sc->sc_height, 319 ri->ri_devcmap[(defattr >> 16) & 0xff]); 320 } else { 321 /* 322 * since we're not the console we can postpone the rest 323 * until someone actually allocates a screen for us 324 */ 325 } 326 327 /* Initialize the default color map. */ 328 329 aa.console = console; 330 aa.scrdata = &sc->sc_screenlist; 331 aa.accessops = &agten_accessops; 332 aa.accesscookie = &sc->vd; 333 334 config_found(&sc->sc_dev, &aa, wsemuldisplaydevprint); 335 336 fb->fb_driver = &agtenfbdriver; 337 fb->fb_device = &sc->sc_dev; 338 fb->fb_flags = device_cfdata(&sc->sc_dev)->cf_flags & FB_USERMASK; 339 fb->fb_type.fb_type = FBTYPE_AG10E; 340 fb->fb_type.fb_cmsize = 256; /* doesn't matter, we're always 24bit */ 341 fb->fb_type.fb_size = sc->sc_glint_fbsz; 342 fb->fb_type.fb_width = sc->sc_width; 343 fb->fb_type.fb_height = sc->sc_height; 344 fb->fb_type.fb_depth = 32; 345 fb->fb_linebytes = sc->sc_stride; 346 fb_attach(fb, console); 347 agten_set_video(sc, 1); /* make sure video's on */ 348 } 349 350 static int 351 agten_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, 352 struct lwp *l) 353 { 354 struct vcons_data *vd = v; 355 struct agten_softc *sc = vd->cookie; 356 struct wsdisplay_fbinfo *wdf; 357 struct vcons_screen *ms = vd->active; 358 359 switch (cmd) { 360 361 case WSDISPLAYIO_GTYPE: 362 *(u_int *)data = WSDISPLAY_TYPE_AG10; 363 return 0; 364 365 case WSDISPLAYIO_GINFO: 366 if (ms == NULL) 367 return ENODEV; 368 wdf = (void *)data; 369 wdf->height = ms->scr_ri.ri_height; 370 wdf->width = ms->scr_ri.ri_width; 371 wdf->depth = 32; 372 wdf->cmsize = 256; 373 return 0; 374 375 case WSDISPLAYIO_GVIDEO: 376 *(int *)data = sc->sc_video; 377 return 0; 378 379 case WSDISPLAYIO_SVIDEO: 380 agten_set_video(sc, *(int *)data); 381 return 0; 382 383 case WSDISPLAYIO_GETCMAP: 384 return agten_getcmap(sc, 385 (struct wsdisplay_cmap *)data); 386 387 case WSDISPLAYIO_PUTCMAP: 388 return agten_putcmap(sc, 389 (struct wsdisplay_cmap *)data); 390 391 case WSDISPLAYIO_LINEBYTES: 392 *(u_int *)data = sc->sc_stride << 2; 393 return 0; 394 395 case WSDISPLAYIO_SMODE: 396 { 397 int new_mode = *(int*)data; 398 if (new_mode != sc->sc_mode) { 399 sc->sc_mode = new_mode; 400 if(new_mode == WSDISPLAYIO_MODE_EMUL) { 401 agten_init(sc); 402 vcons_redraw_screen(ms); 403 } else { 404 agten_gfx(sc); 405 } 406 } 407 } 408 return 0; 409 410 case WSDISPLAYIO_GCURPOS: 411 { 412 struct wsdisplay_curpos *cp = (void *)data; 413 414 cp->x = sc->sc_cursor_x; 415 cp->y = sc->sc_cursor_y; 416 } 417 return 0; 418 419 case WSDISPLAYIO_SCURPOS: 420 { 421 struct wsdisplay_curpos *cp = (void *)data; 422 423 agten_move_cursor(sc, cp->x, cp->y); 424 } 425 return 0; 426 427 case WSDISPLAYIO_GCURMAX: 428 { 429 struct wsdisplay_curpos *cp = (void *)data; 430 431 cp->x = 64; 432 cp->y = 64; 433 } 434 return 0; 435 436 case WSDISPLAYIO_SCURSOR: 437 { 438 struct wsdisplay_cursor *cursor = (void *)data; 439 440 return agten_do_cursor(sc, cursor); 441 } 442 } 443 return EPASSTHROUGH; 444 } 445 446 static paddr_t 447 agten_mmap(void *v, void *vs, off_t offset, int prot) 448 { 449 struct vcons_data *vd = v; 450 struct agten_softc *sc = vd->cookie; 451 452 if (offset < sc->sc_glint_fbsz) 453 return bus_space_mmap(sc->sc_bustag, sc->sc_glint_fb, offset, 454 prot, BUS_SPACE_MAP_LINEAR); 455 return -1; 456 } 457 458 static void 459 agten_init_screen(void *cookie, struct vcons_screen *scr, 460 int existing, long *defattr) 461 { 462 struct agten_softc *sc = cookie; 463 struct rasops_info *ri = &scr->scr_ri; 464 465 ri->ri_depth = sc->sc_depth; 466 ri->ri_width = sc->sc_width; 467 ri->ri_height = sc->sc_height; 468 ri->ri_stride = sc->sc_stride; 469 ri->ri_flg = RI_CENTER | RI_FULLCLEAR; 470 471 ri->ri_bits = (char *)sc->sc_fb.fb_pixels; 472 473 if (existing) { 474 ri->ri_flg |= RI_CLEAR; 475 } 476 477 rasops_init(ri, sc->sc_height / 8, sc->sc_width / 8); 478 ri->ri_caps = WSSCREEN_WSCOLORS; 479 480 rasops_reconfig(ri, sc->sc_height / ri->ri_font->fontheight, 481 sc->sc_width / ri->ri_font->fontwidth); 482 483 ri->ri_hw = scr; 484 ri->ri_ops.copyrows = agten_copyrows; 485 ri->ri_ops.eraserows = agten_eraserows; 486 ri->ri_ops.copycols = agten_copycols; 487 ri->ri_ops.erasecols = agten_erasecols; 488 489 } 490 491 static int 492 agten_putcmap(struct agten_softc *sc, struct wsdisplay_cmap *cm) 493 { 494 u_int index = cm->index; 495 u_int count = cm->count; 496 int i, error; 497 u_char rbuf[256], gbuf[256], bbuf[256]; 498 u_char *r, *g, *b; 499 500 if (cm->index >= 256 || cm->count > 256 || 501 (cm->index + cm->count) > 256) 502 return EINVAL; 503 error = copyin(cm->red, &rbuf[index], count); 504 if (error) 505 return error; 506 error = copyin(cm->green, &gbuf[index], count); 507 if (error) 508 return error; 509 error = copyin(cm->blue, &bbuf[index], count); 510 if (error) 511 return error; 512 513 r = &rbuf[index]; 514 g = &gbuf[index]; 515 b = &bbuf[index]; 516 517 for (i = 0; i < count; i++) { 518 agten_putpalreg(sc, index, *r, *g, *b); 519 index++; 520 r++, g++, b++; 521 } 522 return 0; 523 } 524 525 static int 526 agten_getcmap(struct agten_softc *sc, struct wsdisplay_cmap *cm) 527 { 528 u_int index = cm->index; 529 u_int count = cm->count; 530 int error, i; 531 uint8_t red[256], green[256], blue[256]; 532 533 if (index >= 255 || count > 256 || index + count > 256) 534 return EINVAL; 535 536 i = index; 537 while (i < (index + count)) { 538 red[i] = sc->sc_cmap.cm_map[i][0]; 539 green[i] = sc->sc_cmap.cm_map[i][1]; 540 blue[i] = sc->sc_cmap.cm_map[i][2]; 541 i++; 542 } 543 error = copyout(&red[index], cm->red, count); 544 if (error) 545 return error; 546 error = copyout(&green[index], cm->green, count); 547 if (error) 548 return error; 549 error = copyout(&blue[index], cm->blue, count); 550 if (error) 551 return error; 552 553 return 0; 554 } 555 556 static int 557 agten_putpalreg(struct agten_softc *sc, uint8_t idx, uint8_t r, uint8_t g, 558 uint8_t b) 559 { 560 561 sc->sc_cmap.cm_map[idx][0] = r; 562 sc->sc_cmap.cm_map[idx][1] = g; 563 sc->sc_cmap.cm_map[idx][2] = b; 564 agten_write_idx(sc, IBM561_CMAP_TABLE + idx); 565 agten_write_dac(sc, IBM561_CMD_CMAP, r); 566 agten_write_dac(sc, IBM561_CMD_CMAP, g); 567 agten_write_dac(sc, IBM561_CMD_CMAP, b); 568 return 0; 569 } 570 571 static void 572 agten_init(struct agten_softc *sc) 573 { 574 int i, j; 575 uint32_t src, srcw; 576 volatile uint32_t junk; 577 578 /* first we set up the colour map */ 579 j = 0; 580 for (i = 0; i < 256; i++) { 581 582 agten_putpalreg(sc, i, rasops_cmap[j], rasops_cmap[j + 1], 583 rasops_cmap[j + 2]); 584 j += 3; 585 } 586 587 /* then we set up a linear LUT for 24bit colour */ 588 agten_write_idx(sc, IBM561_CMAP_TABLE + 256); 589 for (i = 0; i < 256; i++) { 590 agten_write_dac(sc, IBM561_CMD_CMAP, i); 591 agten_write_dac(sc, IBM561_CMD_CMAP, i); 592 agten_write_dac(sc, IBM561_CMD_CMAP, i); 593 } 594 595 /* and the linear gamma maps */ 596 agten_write_idx(sc, IBM561_RED_GAMMA_TABLE); 597 for (i = 0; i < 0x3ff; i+= 4) 598 agten_write_dac_10(sc, IBM561_CMD_GAMMA, i); 599 agten_write_idx(sc, IBM561_GREEN_GAMMA_TABLE); 600 for (i = 0; i < 0x3ff; i+= 4) 601 agten_write_dac_10(sc, IBM561_CMD_GAMMA, i); 602 agten_write_idx(sc, IBM561_BLUE_GAMMA_TABLE); 603 for (i = 0; i < 0x3ff; i+= 4) 604 agten_write_dac_10(sc, IBM561_CMD_GAMMA, i); 605 606 /* enable outouts, RGB mode */ 607 agten_write_idx(sc, IBM561_CONFIG_REG3); 608 agten_write_dac(sc, IBM561_CMD, 0x41); 609 610 /* MUX 4:1 basic, 8bit overlay, 8bit WIDs */ 611 agten_write_idx(sc, IBM561_CONFIG_REG1); 612 agten_write_dac(sc, IBM561_CMD, 0x2c); 613 614 /* use internal PLL, enable video output */ 615 agten_write_idx(sc, IBM561_CONFIG_REG2); 616 agten_write_dac(sc, IBM561_CMD, CR2_ENABLE_CLC | CR2_PLL_REF_SELECT | 617 CR2_PIXEL_CLOCK_SELECT | CR2_ENABLE_RGB_OUTPUT); 618 619 /* now set up some window attributes */ 620 621 /* 622 * direct colour, 24 bit, transparency off, LUT from 0x100 623 * we need to use direct colour and a linear LUT because for some 624 * reason true color mode gives messed up colours 625 */ 626 agten_write_idx(sc, IBM561_FB_WINTYPE); 627 agten_write_dac_10(sc, IBM561_CMD_FB_WAT, 0x134); 628 629 /* use gamma LUTs, no crosshair, 0 is transparent */ 630 agten_write_idx(sc, IBM561_AUXFB_WINTYPE); 631 agten_write_dac(sc, IBM561_CMD_FB_WAT, 0x0); 632 633 /* overlay is 8 bit, opaque */ 634 agten_write_idx(sc, IBM561_OL_WINTYPE); 635 agten_write_dac_10(sc, IBM561_CMD_FB_WAT, 0x00); 636 637 /* now we fill the WID fb with zeroes */ 638 src = 0; 639 srcw = sc->sc_width << 16 | sc->sc_height; 640 bus_space_write_4(sc->sc_bustag, sc->sc_p9100_regh, FOREGROUND_COLOR, 641 0x0); 642 bus_space_write_4(sc->sc_bustag, sc->sc_p9100_regh, BACKGROUND_COLOR, 643 0x0); 644 bus_space_write_4(sc->sc_bustag, sc->sc_p9100_regh, RASTER_OP, ROP_PAT); 645 bus_space_write_4(sc->sc_bustag, sc->sc_p9100_regh, COORD_INDEX, 0); 646 bus_space_write_4(sc->sc_bustag, sc->sc_p9100_regh, RECT_RTW_XY, src); 647 bus_space_write_4(sc->sc_bustag, sc->sc_p9100_regh, RECT_RTW_XY, srcw); 648 junk = bus_space_read_4(sc->sc_bustag, sc->sc_p9100_regh, COMMAND_QUAD); 649 650 /* initialize the cursor registers */ 651 652 /* initialize the Imagine 128 */ 653 i128_init(sc->sc_bustag, sc->sc_i128_regh, sc->sc_stride, 8); 654 } 655 656 static void 657 agten_gfx(struct agten_softc *sc) 658 { 659 /* enable overlay transparency on colour 0x00 */ 660 agten_write_idx(sc, IBM561_OL_WINTYPE); 661 agten_write_dac_10(sc, IBM561_CMD_FB_WAT, 0x01); 662 663 /* then blit the overlay full of 0x00 */ 664 i128_rectfill(sc->sc_bustag, sc->sc_i128_regh, 0, 0, sc->sc_width, 665 sc->sc_height, 0); 666 667 /* ... so we can see the 24bit framebuffer */ 668 } 669 670 static void 671 agten_set_video(struct agten_softc *sc, int flag) 672 { 673 uint8_t reg = 674 CR2_ENABLE_CLC | CR2_PLL_REF_SELECT | CR2_PIXEL_CLOCK_SELECT; 675 676 if (flag == sc->sc_video) 677 return; 678 679 agten_write_idx(sc, IBM561_CONFIG_REG2); 680 agten_write_dac(sc, IBM561_CMD, flag ? reg | CR2_ENABLE_RGB_OUTPUT : 681 reg); 682 683 sc->sc_video = flag; 684 } 685 686 static int 687 agten_get_video(struct agten_softc *sc) 688 { 689 690 return sc->sc_video; 691 } 692 693 static void 694 agten_copycols(void *cookie, int row, int srccol, int dstcol, int ncols) 695 { 696 struct rasops_info *ri = cookie; 697 struct vcons_screen *scr = ri->ri_hw; 698 struct agten_softc *sc = scr->scr_cookie; 699 int32_t xs, xd, y, width, height; 700 701 xs = ri->ri_xorigin + ri->ri_font->fontwidth * srccol; 702 xd = ri->ri_xorigin + ri->ri_font->fontwidth * dstcol; 703 y = ri->ri_yorigin + ri->ri_font->fontheight * row; 704 width = ri->ri_font->fontwidth * ncols; 705 height = ri->ri_font->fontheight; 706 i128_bitblt(sc->sc_bustag, sc->sc_i128_regh, xs, y, xd, y, width, 707 height, CR_COPY); 708 } 709 710 static void 711 agten_erasecols(void *cookie, int row, int startcol, int ncols, long fillattr) 712 { 713 struct rasops_info *ri = cookie; 714 struct vcons_screen *scr = ri->ri_hw; 715 struct agten_softc *sc = scr->scr_cookie; 716 int32_t x, y, width, height, bg; 717 718 x = ri->ri_xorigin + ri->ri_font->fontwidth * startcol; 719 y = ri->ri_yorigin + ri->ri_font->fontheight * row; 720 width = ri->ri_font->fontwidth * ncols; 721 height = ri->ri_font->fontheight; 722 bg = (uint32_t)ri->ri_devcmap[(fillattr >> 16) & 0xff]; 723 i128_rectfill(sc->sc_bustag, sc->sc_i128_regh, x, y, width, height, bg); 724 } 725 726 static void 727 agten_copyrows(void *cookie, int srcrow, int dstrow, int nrows) 728 { 729 struct rasops_info *ri = cookie; 730 struct vcons_screen *scr = ri->ri_hw; 731 struct agten_softc *sc = scr->scr_cookie; 732 int32_t x, ys, yd, width, height; 733 734 x = ri->ri_xorigin; 735 ys = ri->ri_yorigin + ri->ri_font->fontheight * srcrow; 736 yd = ri->ri_yorigin + ri->ri_font->fontheight * dstrow; 737 width = ri->ri_emuwidth; 738 height = ri->ri_font->fontheight * nrows; 739 i128_bitblt(sc->sc_bustag, sc->sc_i128_regh, x, ys, x, yd, width, 740 height, CR_COPY); 741 } 742 743 static void 744 agten_eraserows(void *cookie, int row, int nrows, long fillattr) 745 { 746 struct rasops_info *ri = cookie; 747 struct vcons_screen *scr = ri->ri_hw; 748 struct agten_softc *sc = scr->scr_cookie; 749 int32_t x, y, width, height, bg; 750 751 if ((row == 0) && (nrows == ri->ri_rows)) { 752 x = y = 0; 753 width = ri->ri_width; 754 height = ri->ri_height; 755 } else { 756 x = ri->ri_xorigin; 757 y = ri->ri_yorigin + ri->ri_font->fontheight * row; 758 width = ri->ri_emuwidth; 759 height = ri->ri_font->fontheight * nrows; 760 } 761 bg = (uint32_t)ri->ri_devcmap[(fillattr >> 16) & 0xff]; 762 i128_rectfill(sc->sc_bustag, sc->sc_i128_regh, x, y, width, height, bg); 763 } 764 765 static void 766 agten_move_cursor(struct agten_softc *sc, int x, int y) 767 { 768 769 sc->sc_cursor_x = x; 770 sc->sc_cursor_y = y; 771 agten_write_idx(sc, IBM561_CURSOR_X_REG); 772 agten_write_dac(sc, IBM561_CMD, x & 0xff); 773 agten_write_dac(sc, IBM561_CMD, (x >> 8) & 0xff); 774 agten_write_dac(sc, IBM561_CMD, y & 0xff); 775 agten_write_dac(sc, IBM561_CMD, (y >> 8) & 0xff); 776 } 777 778 static int 779 agten_do_cursor(struct agten_softc *sc, struct wsdisplay_cursor *cur) 780 { 781 if (cur->which & WSDISPLAY_CURSOR_DOCUR) { 782 783 agten_write_idx(sc, IBM561_CURS_CNTL_REG); 784 agten_write_dac(sc, IBM561_CMD, cur->enable ? 785 CURS_ENABLE : 0); 786 } 787 if (cur->which & WSDISPLAY_CURSOR_DOHOT) { 788 789 agten_write_idx(sc, IBM561_HOTSPOT_X_REG); 790 agten_write_dac(sc, IBM561_CMD, cur->hot.x); 791 agten_write_dac(sc, IBM561_CMD, cur->hot.y); 792 } 793 if (cur->which & WSDISPLAY_CURSOR_DOPOS) { 794 795 agten_move_cursor(sc, cur->pos.x, cur->pos.y); 796 } 797 if (cur->which & WSDISPLAY_CURSOR_DOCMAP) { 798 int i; 799 800 agten_write_idx(sc, IBM561_CURSOR_LUT + cur->cmap.index + 2); 801 for (i = 0; i < cur->cmap.count; i++) { 802 agten_write_dac(sc, IBM561_CMD_CMAP, cur->cmap.red[i]); 803 agten_write_dac(sc, IBM561_CMD_CMAP, cur->cmap.green[i]); 804 agten_write_dac(sc, IBM561_CMD_CMAP, cur->cmap.blue[i]); 805 } 806 } 807 if (cur->which & WSDISPLAY_CURSOR_DOSHAPE) { 808 int i; 809 uint16_t tmp; 810 811 agten_write_idx(sc, IBM561_CURSOR_BITMAP); 812 for (i = 0; i < 512; i++) { 813 tmp = util_interleave(cur->mask[i], cur->image[i]); 814 agten_write_dac(sc, IBM561_CMD, (tmp >> 8) & 0xff); 815 agten_write_dac(sc, IBM561_CMD, tmp & 0xff); 816 } 817 } 818 return 0; 819 } 820 821 static int 822 agten_do_sun_cursor(struct agten_softc *sc, struct fbcursor *cur) 823 { 824 if (cur->set & FB_CUR_SETCUR) { 825 826 agten_write_idx(sc, IBM561_CURS_CNTL_REG); 827 agten_write_dac(sc, IBM561_CMD, cur->enable ? 828 CURS_ENABLE : 0); 829 } 830 if (cur->set & FB_CUR_SETHOT) { 831 832 agten_write_idx(sc, IBM561_HOTSPOT_X_REG); 833 agten_write_dac(sc, IBM561_CMD, cur->hot.x); 834 agten_write_dac(sc, IBM561_CMD, cur->hot.y); 835 } 836 if (cur->set & FB_CUR_SETPOS) { 837 838 agten_move_cursor(sc, cur->pos.x, cur->pos.y); 839 } 840 if (cur->set & FB_CUR_SETCMAP) { 841 int i; 842 843 agten_write_idx(sc, IBM561_CURSOR_LUT + cur->cmap.index + 2); 844 for (i = 0; i < cur->cmap.count; i++) { 845 agten_write_dac(sc, IBM561_CMD_CMAP, cur->cmap.red[i]); 846 agten_write_dac(sc, IBM561_CMD_CMAP, cur->cmap.green[i]); 847 agten_write_dac(sc, IBM561_CMD_CMAP, cur->cmap.blue[i]); 848 } 849 } 850 if (cur->set & FB_CUR_SETSHAPE) { 851 int i; 852 uint16_t tmp; 853 854 agten_write_idx(sc, IBM561_CURSOR_BITMAP); 855 for (i = 0; i < 512; i++) { 856 tmp = util_interleave_lin(cur->mask[i], cur->image[i]); 857 agten_write_dac(sc, IBM561_CMD, (tmp >> 8) & 0xff); 858 agten_write_dac(sc, IBM561_CMD, tmp & 0xff); 859 } 860 } 861 return 0; 862 } 863 864 uint16_t 865 util_interleave(uint8_t b1, uint8_t b2) 866 { 867 int i; 868 uint16_t ret = 0; 869 uint16_t mask = 0x8000; 870 uint8_t mask8 = 0x01; 871 872 for (i = 0; i < 8; i++) { 873 if (b1 & mask8) 874 ret |= mask; 875 mask = mask >> 1; 876 if (b2 & mask8) 877 ret |= mask; 878 mask = mask >> 1; 879 mask8 = mask8 << 1; 880 } 881 return ret; 882 } 883 884 uint16_t 885 util_interleave_lin(uint8_t b1, uint8_t b2) 886 { 887 int i; 888 uint16_t ret = 0; 889 uint16_t mask = 0x8000; 890 uint8_t mask8 = 0x80; 891 892 for (i = 0; i < 8; i++) { 893 if (b1 & mask8) 894 ret |= mask; 895 mask = mask >> 1; 896 if (b2 & mask8) 897 ret |= mask; 898 mask = mask >> 1; 899 mask8 = mask8 >> 1; 900 } 901 return ret; 902 } 903 904 /* and now the /dev/fb* stuff */ 905 static void 906 agten_fb_unblank(struct device *dev) 907 { 908 struct agten_softc *sc = (void *)dev; 909 910 agten_init(sc); 911 agten_set_video(sc, 1); 912 } 913 914 static int 915 agten_fb_open(dev_t dev, int flags, int mode, struct lwp *l) 916 { 917 struct agten_softc *sc = agten_cd.cd_devs[minor(dev)]; 918 int unit = minor(dev); 919 920 if (unit >= agten_cd.cd_ndevs || agten_cd.cd_devs[unit] == NULL) 921 return (ENXIO); 922 if (sc->sc_fb_is_open) 923 return 0; 924 925 sc->sc_fb_is_open++; 926 agten_gfx(sc); 927 928 return (0); 929 } 930 931 static int 932 agten_fb_close(dev_t dev, int flags, int mode, struct lwp *l) 933 { 934 struct agten_softc *sc = agten_cd.cd_devs[minor(dev)]; 935 936 sc->sc_fb_is_open--; 937 if (sc->sc_fb_is_open < 0) 938 sc->sc_fb_is_open = 0; 939 940 if (sc->sc_fb_is_open == 0) { 941 agten_init(sc); 942 vcons_redraw_screen(sc->vd.active); 943 } 944 945 return (0); 946 } 947 948 static int 949 agten_fb_ioctl(dev_t dev, u_long cmd, void *data, int flags, struct lwp *l) 950 { 951 struct agten_softc *sc = agten_cd.cd_devs[minor(dev)]; 952 struct fbgattr *fba; 953 int error; 954 955 switch (cmd) { 956 957 case FBIOGTYPE: 958 *(struct fbtype *)data = sc->sc_fb.fb_type; 959 break; 960 961 case FBIOGATTR: 962 fba = (struct fbgattr *)data; 963 fba->real_type = sc->sc_fb.fb_type.fb_type; 964 fba->owner = 0; /* XXX ??? */ 965 fba->fbtype = sc->sc_fb.fb_type; 966 fba->sattr.flags = 0; 967 fba->sattr.emu_type = sc->sc_fb.fb_type.fb_type; 968 fba->sattr.dev_specific[0] = -1; 969 fba->emu_types[0] = sc->sc_fb.fb_type.fb_type; 970 fba->emu_types[1] = -1; 971 break; 972 973 case FBIOGETCMAP: 974 #define p ((struct fbcmap *)data) 975 return (bt_getcmap(p, &sc->sc_cmap, 256, 1)); 976 977 case FBIOPUTCMAP: 978 /* copy to software map */ 979 error = bt_putcmap(p, &sc->sc_cmap, 256, 1); 980 if (error) 981 return (error); 982 /* now blast them into the chip */ 983 /* don't bother - we're 24bit */ 984 #undef p 985 break; 986 987 case FBIOGVIDEO: 988 *(int *)data = agten_get_video(sc); 989 break; 990 991 case FBIOSVIDEO: 992 agten_set_video(sc, *(int *)data); 993 break; 994 995 /* these are for both FBIOSCURSOR and FBIOGCURSOR */ 996 #define p ((struct fbcursor *)data) 997 #define pc (&sc->sc_cursor) 998 999 case FBIOGCURSOR: 1000 /* does anyone use this ioctl?! */ 1001 p->set = FB_CUR_SETALL; /* close enough, anyway */ 1002 p->enable = 1; 1003 p->pos.x = sc->sc_cursor_x; 1004 p->pos.y = sc->sc_cursor_y; 1005 p->size.x = 64; 1006 p->size.y = 64; 1007 break; 1008 1009 case FBIOSCURSOR: 1010 agten_do_sun_cursor(sc, p); 1011 break; 1012 1013 #undef p 1014 #undef cc 1015 1016 case FBIOGCURPOS: 1017 { 1018 struct fbcurpos *cp = (struct fbcurpos *)data; 1019 cp->x = sc->sc_cursor_x; 1020 cp->y = sc->sc_cursor_y; 1021 } 1022 break; 1023 1024 case FBIOSCURPOS: 1025 { 1026 struct fbcurpos *cp = (struct fbcurpos *)data; 1027 agten_move_cursor(sc, cp->x, cp->y); 1028 } 1029 break; 1030 1031 case FBIOGCURMAX: 1032 /* max cursor size is 64x64 */ 1033 ((struct fbcurpos *)data)->x = 64; 1034 ((struct fbcurpos *)data)->y = 64; 1035 break; 1036 1037 default: 1038 return (ENOTTY); 1039 } 1040 return (0); 1041 } 1042 1043 static paddr_t 1044 agten_fb_mmap(dev_t dev, off_t off, int prot) 1045 { 1046 struct agten_softc *sc = agten_cd.cd_devs[minor(dev)]; 1047 1048 /* 1049 * mappings are subject to change 1050 * for now we put the framebuffer at offset 0 and the GLint registers 1051 * right after that. We may want to expose more register ranges and 1052 * probably will want to map the 2nd framebuffer as well 1053 */ 1054 1055 if (off < 0) 1056 return EINVAL; 1057 1058 if (off >= sc->sc_glint_fbsz + 0x10000) 1059 return EINVAL; 1060 1061 if (off < sc->sc_glint_fbsz) { 1062 return (bus_space_mmap(sc->sc_bustag, 1063 sc->sc_glint_fb, 1064 off, 1065 prot, 1066 BUS_SPACE_MAP_LINEAR)); 1067 } 1068 1069 off -= sc->sc_glint_fbsz; 1070 if (off < 0x10000) { 1071 return (bus_space_mmap(sc->sc_bustag, 1072 sc->sc_glint_regs, 1073 off, 1074 prot, 1075 BUS_SPACE_MAP_LINEAR)); 1076 } 1077 return EINVAL; 1078 } 1079