1 /* $NetBSD: zx.c,v 1.41 2016/04/21 18:10:57 macallan Exp $ */ 2 3 /* 4 * Copyright (c) 2002 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Andrew Doran. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 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 /* 33 * Driver for the Sun ZX display adapter. This would be called 'leo', but 34 * NetBSD/amiga already has a driver by that name. The XFree86 and Linux 35 * drivers were used as "living documentation" when writing this; thanks 36 * to the authors. 37 * 38 * Issues (which can be solved with wscons, happily enough): 39 * 40 * o There is lots of unnecessary mucking about rasops in here, primarily 41 * to appease the sparc fb code. 42 */ 43 44 #include <sys/cdefs.h> 45 __KERNEL_RCSID(0, "$NetBSD: zx.c,v 1.41 2016/04/21 18:10:57 macallan Exp $"); 46 47 #include <sys/param.h> 48 #include <sys/systm.h> 49 #include <sys/device.h> 50 #include <sys/ioctl.h> 51 #include <sys/malloc.h> 52 #include <sys/mman.h> 53 #include <sys/tty.h> 54 #include <sys/conf.h> 55 #include <sys/syslog.h> 56 #include <sys/buf.h> 57 #ifdef DEBUG 58 /* for log(9) in zxioctl() */ 59 #include <sys/lwp.h> 60 #include <sys/proc.h> 61 #endif 62 63 #include <sys/bus.h> 64 #include <machine/autoconf.h> 65 66 #include <dev/sun/fbio.h> 67 #include <dev/sun/fbvar.h> 68 69 #include "wsdisplay.h" 70 #include <dev/wscons/wsconsio.h> 71 #include <dev/wsfont/wsfont.h> 72 #include <dev/rasops/rasops.h> 73 #include <dev/wscons/wsdisplay_vconsvar.h> 74 75 #include "opt_wsemul.h" 76 77 #include <dev/sbus/zxreg.h> 78 #include <dev/sbus/zxvar.h> 79 #include <dev/sbus/sbusvar.h> 80 81 #include <dev/wscons/wsconsio.h> 82 83 #include "ioconf.h" 84 85 #define ZX_STD_ROP (ZX_ROP_NEW | ZX_ATTR_WE_ENABLE | \ 86 ZX_ATTR_OE_ENABLE | ZX_ATTR_FORCE_WID) 87 88 static void zx_attach(device_t, device_t, void *); 89 static int zx_match(device_t, cfdata_t, void *); 90 91 static void zx_blank(device_t); 92 static int zx_cmap_put(struct zx_softc *); 93 static void zx_copyrect(struct zx_softc *, int, int, int, int, int, int); 94 static int zx_cross_loadwid(struct zx_softc *, u_int, u_int, u_int); 95 static int zx_cross_wait(struct zx_softc *); 96 static void zx_fillrect(struct zx_softc *, int, int, int, int, uint32_t, int); 97 static int zx_intr(void *); 98 static void zx_reset(struct zx_softc *); 99 static void zx_unblank(device_t); 100 101 static void zx_cursor_blank(struct zx_softc *); 102 static void zx_cursor_color(struct zx_softc *); 103 static void zx_cursor_move(struct zx_softc *); 104 static void zx_cursor_set(struct zx_softc *); 105 static void zx_cursor_unblank(struct zx_softc *); 106 107 static void zx_copycols(void *, int, int, int, int); 108 static void zx_copyrows(void *, int, int, int); 109 static void zx_do_cursor(void *, int, int, int); 110 static void zx_erasecols(void *, int, int, int, long); 111 static void zx_eraserows(void *, int, int, long); 112 static void zx_putchar(void *, int, int, u_int, long); 113 114 struct zx_mmo { 115 off_t mo_va; 116 off_t mo_pa; 117 off_t mo_size; 118 } static const zx_mmo[] = { 119 { ZX_FB0_VOFF, ZX_OFF_SS0, 0x00800000 }, 120 { ZX_LC0_VOFF, ZX_OFF_LC_SS0_USR, 0x00001000 }, 121 { ZX_LD0_VOFF, ZX_OFF_LD_SS0, 0x00001000 }, 122 { ZX_LX0_CURSOR_VOFF, ZX_OFF_LX_CURSOR, 0x00001000 }, 123 { ZX_FB1_VOFF, ZX_OFF_SS1, 0x00800000 }, 124 { ZX_LC1_VOFF, ZX_OFF_LC_SS1_USR, 0x00001000 }, 125 { ZX_LD1_VOFF, ZX_OFF_LD_SS1, 0x00001000 }, 126 { ZX_LX_KRN_VOFF, ZX_OFF_LX_CROSS, 0x00001000 }, 127 { ZX_LC0_KRN_VOFF, ZX_OFF_LC_SS0_KRN, 0x00001000 }, 128 { ZX_LC1_KRN_VOFF, ZX_OFF_LC_SS1_KRN, 0x00001000 }, 129 { ZX_LD_GBL_VOFF, ZX_OFF_LD_GBL, 0x00001000 }, 130 }; 131 132 CFATTACH_DECL_NEW(zx, sizeof(struct zx_softc), 133 zx_match, zx_attach, NULL, NULL); 134 135 static dev_type_open(zxopen); 136 static dev_type_close(zxclose); 137 static dev_type_ioctl(zxioctl); 138 static dev_type_mmap(zxmmap); 139 140 static struct fbdriver zx_fbdriver = { 141 zx_unblank, zxopen, zxclose, zxioctl, nopoll, zxmmap 142 }; 143 144 struct wsscreen_descr zx_defaultscreen = { 145 "std", 146 0, 0, /* will be filled in -- XXX shouldn't, it's global */ 147 /* doesn't matter - you can't really have more than one leo */ 148 NULL, /* textops */ 149 8, 16, /* font width/height */ 150 WSSCREEN_WSCOLORS, /* capabilities */ 151 NULL /* modecookie */ 152 }; 153 154 static int zx_ioctl(void *, void *, u_long, void *, int, struct lwp *); 155 static paddr_t zx_mmap(void *, void *, off_t, int); 156 static void zx_init_screen(void *, struct vcons_screen *, int, long *); 157 158 static int zx_putcmap(struct zx_softc *, struct wsdisplay_cmap *); 159 static int zx_getcmap(struct zx_softc *, struct wsdisplay_cmap *); 160 161 struct wsdisplay_accessops zx_accessops = { 162 zx_ioctl, 163 zx_mmap, 164 NULL, /* alloc_screen */ 165 NULL, /* free_screen */ 166 NULL, /* show_screen */ 167 NULL, /* load_font */ 168 NULL, /* pollc */ 169 NULL /* scroll */ 170 }; 171 172 const struct wsscreen_descr *_zx_scrlist[] = { 173 &zx_defaultscreen 174 }; 175 176 struct wsscreen_list zx_screenlist = { 177 sizeof(_zx_scrlist) / sizeof(struct wsscreen_descr *), 178 _zx_scrlist 179 }; 180 181 182 extern const u_char rasops_cmap[768]; 183 184 static struct vcons_screen zx_console_screen; 185 186 static int 187 zx_match(device_t parent, cfdata_t cf, void *aux) 188 { 189 struct sbus_attach_args *sa; 190 191 sa = (struct sbus_attach_args *)aux; 192 193 return (strcmp(sa->sa_name, "SUNW,leo") == 0); 194 } 195 196 static void 197 zx_attach(device_t parent, device_t self, void *args) 198 { 199 struct zx_softc *sc; 200 struct sbus_attach_args *sa; 201 bus_space_handle_t bh; 202 bus_space_tag_t bt; 203 struct fbdevice *fb; 204 struct wsemuldisplaydev_attach_args aa; 205 struct rasops_info *ri = &zx_console_screen.scr_ri; 206 unsigned long defattr; 207 int isconsole, width, height; 208 209 sc = device_private(self); 210 sc->sc_dv = self; 211 212 sa = args; 213 fb = &sc->sc_fb; 214 bt = sa->sa_bustag; 215 sc->sc_bt = bt; 216 sc->sc_paddr = sbus_bus_addr(bt, sa->sa_slot, sa->sa_offset); 217 218 if (sbus_bus_map(bt, sa->sa_slot, sa->sa_offset + ZX_OFF_SS0, 219 0x800000, BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_LARGE, &bh) != 0) { 220 aprint_error_dev(self, "can't map bits\n"); 221 return; 222 } 223 fb->fb_pixels = (void *)bus_space_vaddr(bt, bh); 224 sc->sc_pixels = (uint32_t *)fb->fb_pixels; 225 226 if (sbus_bus_map(bt, sa->sa_slot, sa->sa_offset + ZX_OFF_LC_SS0_USR, 227 PAGE_SIZE, BUS_SPACE_MAP_LINEAR, &bh) != 0) { 228 aprint_error_dev(self, "can't map zc\n"); 229 return; 230 } 231 232 sc->sc_bhzc = bh; 233 234 if (sbus_bus_map(bt, sa->sa_slot, sa->sa_offset + ZX_OFF_LD_SS0, 235 PAGE_SIZE, BUS_SPACE_MAP_LINEAR, &bh) != 0) { 236 aprint_error_dev(self, "can't map ld/ss0\n"); 237 return; 238 } 239 sc->sc_bhzdss0 = bh; 240 241 if (sbus_bus_map(bt, sa->sa_slot, sa->sa_offset + ZX_OFF_LD_SS1, 242 PAGE_SIZE, BUS_SPACE_MAP_LINEAR, &bh) != 0) { 243 aprint_error_dev(self, "can't map ld/ss1\n"); 244 return; 245 } 246 sc->sc_bhzdss1 = bh; 247 248 if (sbus_bus_map(bt, sa->sa_slot, sa->sa_offset + ZX_OFF_LX_CROSS, 249 PAGE_SIZE, BUS_SPACE_MAP_LINEAR, &bh) != 0) { 250 aprint_error_dev(self, "can't map zx\n"); 251 return; 252 } 253 sc->sc_bhzx = bh; 254 255 if (sbus_bus_map(bt, sa->sa_slot, sa->sa_offset + ZX_OFF_LX_CURSOR, 256 PAGE_SIZE, BUS_SPACE_MAP_LINEAR, &bh) != 0) { 257 aprint_error_dev(self, "can't map zcu\n"); 258 return; 259 } 260 sc->sc_bhzcu = bh; 261 262 fb->fb_driver = &zx_fbdriver; 263 fb->fb_device = sc->sc_dv; 264 fb->fb_flags = device_cfdata(sc->sc_dv)->cf_flags & FB_USERMASK; 265 fb->fb_pfour = NULL; 266 fb->fb_linebytes = prom_getpropint(sa->sa_node, "linebytes", 8192); 267 268 width = prom_getpropint(sa->sa_node, "width", 1280); 269 height = prom_getpropint(sa->sa_node, "height", 1024); 270 fb_setsize_obp(fb, 32, width, height, sa->sa_node); 271 272 fb->fb_type.fb_cmsize = 256; 273 fb->fb_type.fb_depth = 32; 274 fb->fb_type.fb_size = fb->fb_type.fb_height * fb->fb_linebytes; 275 fb->fb_type.fb_type = FBTYPE_SUNLEO; 276 277 printf(": %d x %d", fb->fb_type.fb_width, fb->fb_type.fb_height); 278 isconsole = fb_is_console(sa->sa_node); 279 if (isconsole) 280 printf(" (console)"); 281 printf("\n"); 282 283 if (sa->sa_nintr != 0) 284 bus_intr_establish(bt, sa->sa_pri, IPL_NONE, zx_intr, sc); 285 286 sc->sc_cmap = malloc(768, M_DEVBUF, M_NOWAIT); 287 zx_reset(sc); 288 289 sc->sc_width = fb->fb_type.fb_width; 290 sc->sc_stride = 8192; /* 32 bit */ 291 sc->sc_height = fb->fb_type.fb_height; 292 293 /* setup rasops and so on for wsdisplay */ 294 wsfont_init(); 295 sc->sc_mode = WSDISPLAYIO_MODE_EMUL; 296 sc->sc_bg = WS_DEFAULT_BG; 297 298 vcons_init(&sc->vd, sc, &zx_defaultscreen, &zx_accessops); 299 sc->vd.init_screen = zx_init_screen; 300 301 if (isconsole) { 302 /* we mess with zx_console_screen only once */ 303 vcons_init_screen(&sc->vd, &zx_console_screen, 1, 304 &defattr); 305 zx_console_screen.scr_flags |= VCONS_SCREEN_IS_STATIC; 306 307 zx_defaultscreen.textops = &ri->ri_ops; 308 zx_defaultscreen.capabilities = WSSCREEN_WSCOLORS; 309 zx_defaultscreen.nrows = ri->ri_rows; 310 zx_defaultscreen.ncols = ri->ri_cols; 311 zx_fillrect(sc, 0, 0, width, height, 312 ri->ri_devcmap[defattr >> 16], ZX_STD_ROP); 313 wsdisplay_cnattach(&zx_defaultscreen, ri, 0, 0, defattr); 314 vcons_replay_msgbuf(&zx_console_screen); 315 } else { 316 /* 317 * we're not the console so we just clear the screen and don't 318 * set up any sort of text display 319 */ 320 if (zx_defaultscreen.textops == NULL) { 321 /* 322 * ugly, but... 323 * we want the console settings to win, so we only 324 * touch anything when we find an untouched screen 325 * definition. In this case we fill it from fb to 326 * avoid problems in case no zx is the console 327 */ 328 zx_defaultscreen.textops = &ri->ri_ops; 329 zx_defaultscreen.capabilities = ri->ri_caps; 330 zx_defaultscreen.nrows = ri->ri_rows; 331 zx_defaultscreen.ncols = ri->ri_cols; 332 } 333 } 334 335 aa.scrdata = &zx_screenlist; 336 aa.console = isconsole; 337 aa.accessops = &zx_accessops; 338 aa.accesscookie = &sc->vd; 339 config_found(sc->sc_dv, &aa, wsemuldisplaydevprint); 340 fb_attach(&sc->sc_fb, isconsole); 341 } 342 343 static int 344 zxopen(dev_t dev, int flags, int mode, struct lwp *l) 345 { 346 347 if (device_lookup(&zx_cd, minor(dev)) == NULL) 348 return (ENXIO); 349 return (0); 350 } 351 352 static int 353 zxclose(dev_t dev, int flags, int mode, struct lwp *l) 354 { 355 struct zx_softc *sc; 356 357 sc = device_lookup_private(&zx_cd, minor(dev)); 358 359 zx_reset(sc); 360 zx_cursor_blank(sc); 361 return (0); 362 } 363 364 static int 365 zxioctl(dev_t dev, u_long cmd, void *data, int flags, struct lwp *l) 366 { 367 struct zx_softc *sc; 368 struct fbcmap *cm; 369 struct fbcursor *cu; 370 uint32_t curbits[2][32]; 371 int rv, v, count, i, error; 372 373 sc = device_lookup_private(&zx_cd, minor(dev)); 374 375 switch (cmd) { 376 case FBIOGTYPE: 377 *(struct fbtype *)data = sc->sc_fb.fb_type; 378 break; 379 380 case FBIOGATTR: 381 #define fba ((struct fbgattr *)data) 382 fba->real_type = sc->sc_fb.fb_type.fb_type; 383 fba->owner = 0; /* XXX ??? */ 384 fba->fbtype = sc->sc_fb.fb_type; 385 fba->sattr.flags = 0; 386 fba->sattr.emu_type = sc->sc_fb.fb_type.fb_type; 387 fba->sattr.dev_specific[0] = -1; 388 fba->emu_types[0] = sc->sc_fb.fb_type.fb_type; 389 fba->emu_types[1] = -1; 390 fba->emu_types[2] = -1; 391 #undef fba 392 break; 393 394 case FBIOGVIDEO: 395 *(int *)data = ((sc->sc_flags & ZX_BLANKED) != 0); 396 break; 397 398 case FBIOSVIDEO: 399 if (*(int *)data) 400 zx_unblank(sc->sc_dv); 401 else 402 zx_blank(sc->sc_dv); 403 break; 404 405 case FBIOGETCMAP: 406 cm = (struct fbcmap *)data; 407 if (cm->index > 256 || cm->count > 256 - cm->index) 408 return (EINVAL); 409 rv = copyout(sc->sc_cmap + cm->index, cm->red, cm->count); 410 if (rv == 0) 411 rv = copyout(sc->sc_cmap + 256 + cm->index, cm->green, 412 cm->count); 413 if (rv == 0) 414 rv = copyout(sc->sc_cmap + 512 + cm->index, cm->blue, 415 cm->count); 416 return (rv); 417 418 case FBIOPUTCMAP: 419 cm = (struct fbcmap *)data; 420 if (cm->index > 256 || cm->count > 256 - cm->index) 421 return (EINVAL); 422 rv = copyin(cm->red, sc->sc_cmap + cm->index, cm->count); 423 if (rv == 0) 424 rv = copyin(cm->green, sc->sc_cmap + 256 + cm->index, 425 cm->count); 426 if (rv == 0) 427 rv = copyin(cm->blue, sc->sc_cmap + 512 + cm->index, 428 cm->count); 429 zx_cmap_put(sc); 430 return (rv); 431 432 case FBIOGCURPOS: 433 *(struct fbcurpos *)data = sc->sc_curpos; 434 break; 435 436 case FBIOSCURPOS: 437 sc->sc_curpos = *(struct fbcurpos *)data; 438 zx_cursor_move(sc); 439 break; 440 441 case FBIOGCURMAX: 442 ((struct fbcurpos *)data)->x = 32; 443 ((struct fbcurpos *)data)->y = 32; 444 break; 445 446 case FBIOSCURSOR: 447 cu = (struct fbcursor *)data; 448 v = cu->set; 449 450 if ((v & FB_CUR_SETSHAPE) != 0) { 451 if ((u_int)cu->size.x > 32 || (u_int)cu->size.y > 32) 452 return (EINVAL); 453 count = cu->size.y * 4; 454 rv = copyin(cu->mask, curbits[0], count); 455 if (rv) 456 return rv; 457 rv = copyin(cu->image, curbits[1], count); 458 if (rv) 459 return rv; 460 } 461 if ((v & FB_CUR_SETCUR) != 0) { 462 if (cu->enable) 463 zx_cursor_unblank(sc); 464 else 465 zx_cursor_blank(sc); 466 } 467 if ((v & (FB_CUR_SETPOS | FB_CUR_SETHOT)) != 0) { 468 if ((v & FB_CUR_SETPOS) != 0) 469 sc->sc_curpos = cu->pos; 470 if ((v & FB_CUR_SETHOT) != 0) 471 sc->sc_curhot = cu->hot; 472 zx_cursor_move(sc); 473 } 474 if ((v & FB_CUR_SETCMAP) != 0) { 475 if (cu->cmap.index > 2 || 476 cu->cmap.count > 2 - cu->cmap.index) 477 return (EINVAL); 478 for (i = 0; i < cu->cmap.count; i++) { 479 if ((v = fubyte(&cu->cmap.red[i])) < 0) 480 return (EFAULT); 481 sc->sc_curcmap[i + cu->cmap.index + 0] = v; 482 if ((v = fubyte(&cu->cmap.green[i])) < 0) 483 return (EFAULT); 484 sc->sc_curcmap[i + cu->cmap.index + 2] = v; 485 if ((v = fubyte(&cu->cmap.blue[i])) < 0) 486 return (EFAULT); 487 sc->sc_curcmap[i + cu->cmap.index + 4] = v; 488 } 489 zx_cursor_color(sc); 490 } 491 if ((v & FB_CUR_SETSHAPE) != 0) { 492 sc->sc_cursize = cu->size; 493 count = cu->size.y * 4; 494 memset(sc->sc_curbits, 0, sizeof(sc->sc_curbits)); 495 memcpy(sc->sc_curbits[0], curbits[0], count); 496 memcpy(sc->sc_curbits[1], curbits[1], count); 497 zx_cursor_set(sc); 498 } 499 break; 500 501 case FBIOGCURSOR: 502 cu = (struct fbcursor *)data; 503 504 cu->set = FB_CUR_SETALL; 505 cu->enable = ((sc->sc_flags & ZX_CURSOR) != 0); 506 cu->pos = sc->sc_curpos; 507 cu->hot = sc->sc_curhot; 508 cu->size = sc->sc_cursize; 509 510 if (cu->image != NULL) { 511 count = sc->sc_cursize.y * 4; 512 rv = copyout(sc->sc_curbits[1], cu->image, count); 513 if (rv) 514 return (rv); 515 rv = copyout(sc->sc_curbits[0], cu->mask, count); 516 if (rv) 517 return (rv); 518 } 519 if (cu->cmap.red != NULL) { 520 uint8_t red[2], green[2], blue[2]; 521 const uint8_t *ccm = sc->sc_curcmap; 522 cm = &cu->cmap; 523 524 if (cm->index > 2 || cm->count > 2 - cm->index) 525 return EINVAL; 526 527 for (i = 0; i < cm->count; i++) { 528 red[i] = ccm[i + cm->index + 0]; 529 green[i] = ccm[i + cm->index + 2]; 530 blue[i] = ccm[i + cm->index + 4]; 531 } 532 533 if ((error = copyout(red, cm->red, cm->count)) || 534 (error = copyout(green, cm->green, cm->count)) || 535 (error = copyout(blue, cm->blue, cm->count))) 536 return error; 537 } else { 538 cu->cmap.index = 0; 539 cu->cmap.count = 2; 540 } 541 break; 542 543 default: 544 #ifdef DEBUG 545 log(LOG_NOTICE, "zxioctl(0x%lx) (%s[%d])\n", cmd, 546 l->l_proc->p_comm, l->l_proc->p_pid); 547 #endif 548 return (ENOTTY); 549 } 550 551 return (0); 552 } 553 554 static int 555 zx_intr(void *cookie) 556 { 557 558 return (1); 559 } 560 561 static void 562 zx_reset(struct zx_softc *sc) 563 { 564 struct fbtype *fbt; 565 u_int i; 566 567 fbt = &sc->sc_fb.fb_type; 568 569 zx_cross_loadwid(sc, ZX_WID_DBL_8, 0, 0x2c0); 570 zx_cross_loadwid(sc, ZX_WID_DBL_8, 1, 0x30); 571 zx_cross_loadwid(sc, ZX_WID_DBL_8, 2, 0x20); 572 zx_cross_loadwid(sc, ZX_WID_DBL_24, 1, 0x30); 573 574 i = bus_space_read_4(sc->sc_bt, sc->sc_bhzdss1, zd_misc); 575 i |= ZX_SS1_MISC_ENABLE; 576 bus_space_write_4(sc->sc_bt, sc->sc_bhzdss1, zd_misc, i); 577 578 bus_space_write_4(sc->sc_bt, sc->sc_bhzdss0, zd_wid, 1); 579 bus_space_write_4(sc->sc_bt, sc->sc_bhzdss0, zd_widclip, 0); 580 bus_space_write_4(sc->sc_bt, sc->sc_bhzdss0, zd_wmask, 0xffff); 581 bus_space_write_4(sc->sc_bt, sc->sc_bhzdss0, zd_vclipmin, 0); 582 bus_space_write_4(sc->sc_bt, sc->sc_bhzdss0, zd_vclipmax, 583 (fbt->fb_width - 1) | ((fbt->fb_height - 1) << 16)); 584 bus_space_write_4(sc->sc_bt, sc->sc_bhzdss0, zd_fg, 0); 585 bus_space_write_4(sc->sc_bt, sc->sc_bhzdss0, zd_planemask, 0xffffffff); 586 bus_space_write_4(sc->sc_bt, sc->sc_bhzdss0, zd_rop, ZX_STD_ROP); 587 588 bus_space_write_4(sc->sc_bt, sc->sc_bhzc, zc_extent, 589 (fbt->fb_width - 1) | ((fbt->fb_height - 1) << 11)); 590 bus_space_write_4(sc->sc_bt, sc->sc_bhzc, zc_addrspace, 591 ZX_ADDRSPC_FONT_OBGR); 592 bus_space_write_4(sc->sc_bt, sc->sc_bhzc, zc_fontt, 0); 593 594 for (i = 0; i < 256; i++) { 595 sc->sc_cmap[i] = rasops_cmap[i * 3]; 596 sc->sc_cmap[i + 256] = rasops_cmap[i * 3 + 1]; 597 sc->sc_cmap[i + 512] = rasops_cmap[i * 3 + 2]; 598 } 599 600 zx_cmap_put(sc); 601 } 602 603 static int 604 zx_cross_wait(struct zx_softc *sc) 605 { 606 int i; 607 608 for (i = 300000; i != 0; i--) { 609 if ((bus_space_read_4(sc->sc_bt, sc->sc_bhzx, zx_csr) & 610 ZX_CROSS_CSR_PROGRESS) == 0) 611 break; 612 DELAY(1); 613 } 614 615 if (i == 0) 616 printf("zx_cross_wait: timed out\n"); 617 618 return (i); 619 } 620 621 static int 622 zx_cross_loadwid(struct zx_softc *sc, u_int type, u_int index, u_int value) 623 { 624 u_int tmp = 0; 625 626 bus_space_write_4(sc->sc_bt, sc->sc_bhzx, zx_type, ZX_CROSS_TYPE_WID); 627 628 if (zx_cross_wait(sc)) 629 return (1); 630 631 if (type == ZX_WID_DBL_8) 632 tmp = (index & 0x0f) + 0x40; 633 else if (type == ZX_WID_DBL_24) 634 tmp = index & 0x3f; 635 636 bus_space_write_4(sc->sc_bt, sc->sc_bhzx, zx_type, 0x5800 + tmp); 637 bus_space_write_4(sc->sc_bt, sc->sc_bhzx, zx_value, value); 638 bus_space_write_4(sc->sc_bt, sc->sc_bhzx, zx_type, ZX_CROSS_TYPE_WID); 639 bus_space_write_4(sc->sc_bt, sc->sc_bhzx, zx_csr, 640 ZX_CROSS_CSR_UNK | ZX_CROSS_CSR_UNK2); 641 642 return (0); 643 } 644 645 static int 646 zx_cmap_put(struct zx_softc *sc) 647 { 648 const u_char *b; 649 u_int i, t; 650 651 bus_space_write_4(sc->sc_bt, sc->sc_bhzx, zx_type, ZX_CROSS_TYPE_CLUT0); 652 653 zx_cross_wait(sc); 654 655 bus_space_write_4(sc->sc_bt, sc->sc_bhzx, zx_type, 656 ZX_CROSS_TYPE_CLUTDATA); 657 658 for (i = 0, b = sc->sc_cmap; i < 256; i++) { 659 t = b[i]; 660 t |= b[i + 256] << 8; 661 t |= b[i + 512] << 16; 662 bus_space_write_4(sc->sc_bt, sc->sc_bhzx, zx_value, t); 663 } 664 665 bus_space_write_4(sc->sc_bt, sc->sc_bhzx, zx_type, ZX_CROSS_TYPE_CLUT0); 666 i = bus_space_read_4(sc->sc_bt, sc->sc_bhzx, zx_csr); 667 i = i | ZX_CROSS_CSR_UNK | ZX_CROSS_CSR_UNK2; 668 bus_space_write_4(sc->sc_bt, sc->sc_bhzx, zx_csr, i); 669 return (0); 670 } 671 672 static void 673 zx_cursor_move(struct zx_softc *sc) 674 { 675 int sx, sy, x, y; 676 677 x = sc->sc_curpos.x - sc->sc_curhot.x; 678 y = sc->sc_curpos.y - sc->sc_curhot.y; 679 680 if (x < 0) { 681 sx = min(-x, 32); 682 x = 0; 683 } else 684 sx = 0; 685 686 if (y < 0) { 687 sy = min(-y, 32); 688 y = 0; 689 } else 690 sy = 0; 691 692 if (sx != sc->sc_shiftx || sy != sc->sc_shifty) { 693 sc->sc_shiftx = sx; 694 sc->sc_shifty = sy; 695 zx_cursor_set(sc); 696 } 697 698 bus_space_write_4(sc->sc_bt, sc->sc_bhzcu, zcu_sxy, 699 ((y & 0x7ff) << 11) | (x & 0x7ff)); 700 bus_space_write_4(sc->sc_bt, sc->sc_bhzcu, zcu_misc, 701 bus_space_read_4(sc->sc_bt, sc->sc_bhzcu, zcu_misc) | 0x30); 702 703 /* XXX Necessary? */ 704 bus_space_write_4(sc->sc_bt, sc->sc_bhzcu, zcu_misc, 705 bus_space_read_4(sc->sc_bt, sc->sc_bhzcu, zcu_misc) | 0x80); 706 } 707 708 static void 709 zx_cursor_set(struct zx_softc *sc) 710 { 711 int i, j, data; 712 713 if ((sc->sc_flags & ZX_CURSOR) != 0) 714 bus_space_write_4(sc->sc_bt, sc->sc_bhzcu, zcu_misc, 715 bus_space_read_4(sc->sc_bt, sc->sc_bhzcu, zcu_misc) & 716 ~0x80); 717 718 for (j = 0; j < 2; j++) { 719 bus_space_write_4(sc->sc_bt, sc->sc_bhzcu, zcu_type, 0x20 << j); 720 721 for (i = sc->sc_shifty; i < 32; i++) { 722 data = sc->sc_curbits[j][i]; 723 bus_space_write_4(sc->sc_bt, sc->sc_bhzcu, zcu_data, 724 data >> sc->sc_shiftx); 725 } 726 for (i = sc->sc_shifty; i != 0; i--) 727 bus_space_write_4(sc->sc_bt, sc->sc_bhzcu, zcu_data, 0); 728 } 729 730 if ((sc->sc_flags & ZX_CURSOR) != 0) 731 bus_space_write_4(sc->sc_bt, sc->sc_bhzcu, zcu_misc, 732 bus_space_read_4(sc->sc_bt, sc->sc_bhzcu, zcu_misc) | 0x80); 733 } 734 735 static void 736 zx_cursor_blank(struct zx_softc *sc) 737 { 738 739 sc->sc_flags &= ~ZX_CURSOR; 740 bus_space_write_4(sc->sc_bt, sc->sc_bhzcu, zcu_misc, 741 bus_space_read_4(sc->sc_bt, sc->sc_bhzcu, zcu_misc) & ~0x80); 742 } 743 744 static void 745 zx_cursor_unblank(struct zx_softc *sc) 746 { 747 748 sc->sc_flags |= ZX_CURSOR; 749 bus_space_write_4(sc->sc_bt, sc->sc_bhzcu, zcu_misc, 750 bus_space_read_4(sc->sc_bt, sc->sc_bhzcu, zcu_misc) | 0x80); 751 } 752 753 static void 754 zx_cursor_color(struct zx_softc *sc) 755 { 756 uint8_t tmp; 757 758 bus_space_write_4(sc->sc_bt, sc->sc_bhzcu, zcu_type, 0x50); 759 760 tmp = sc->sc_curcmap[0] | (sc->sc_curcmap[2] << 8) | 761 (sc->sc_curcmap[4] << 16); 762 bus_space_write_4(sc->sc_bt, sc->sc_bhzcu, zcu_data, tmp); 763 764 tmp = sc->sc_curcmap[1] | (sc->sc_curcmap[3] << 8) | 765 (sc->sc_curcmap[5] << 16); 766 bus_space_write_4(sc->sc_bt, sc->sc_bhzcu, zcu_data, sc->sc_curcmap[1]); 767 768 bus_space_write_4(sc->sc_bt, sc->sc_bhzcu, zcu_misc, 769 bus_space_read_4(sc->sc_bt, sc->sc_bhzcu, zcu_misc) | 0x03); 770 } 771 772 static void 773 zx_blank(device_t dv) 774 { 775 struct zx_softc *sc; 776 777 sc = device_private(dv); 778 779 if ((sc->sc_flags & ZX_BLANKED) != 0) 780 return; 781 sc->sc_flags |= ZX_BLANKED; 782 783 bus_space_write_4(sc->sc_bt, sc->sc_bhzx, zx_type, ZX_CROSS_TYPE_VIDEO); 784 bus_space_write_4(sc->sc_bt, sc->sc_bhzx, zx_csr, 785 bus_space_read_4(sc->sc_bt, sc->sc_bhzx, zx_csr) & 786 ~ZX_CROSS_CSR_ENABLE); 787 } 788 789 static void 790 zx_unblank(device_t dv) 791 { 792 struct zx_softc *sc; 793 794 sc = device_private(dv); 795 796 if ((sc->sc_flags & ZX_BLANKED) == 0) 797 return; 798 sc->sc_flags &= ~ZX_BLANKED; 799 800 bus_space_write_4(sc->sc_bt, sc->sc_bhzx, zx_type, ZX_CROSS_TYPE_VIDEO); 801 bus_space_write_4(sc->sc_bt, sc->sc_bhzx, zx_csr, 802 bus_space_read_4(sc->sc_bt, sc->sc_bhzx, zx_csr) | 803 ZX_CROSS_CSR_ENABLE); 804 } 805 806 static paddr_t 807 zxmmap(dev_t dev, off_t off, int prot) 808 { 809 struct zx_softc *sc; 810 const struct zx_mmo *mm, *mmmax; 811 812 sc = device_lookup_private(&zx_cd, minor(dev)); 813 off = trunc_page(off); 814 mm = zx_mmo; 815 mmmax = mm + sizeof(zx_mmo) / sizeof(zx_mmo[0]); 816 817 for (; mm < mmmax; mm++) 818 if (off >= mm->mo_va && off < mm->mo_va + mm->mo_size) { 819 off = off - mm->mo_va + mm->mo_pa; 820 return (bus_space_mmap(sc->sc_bt, sc->sc_paddr, 821 off, prot, BUS_SPACE_MAP_LINEAR)); 822 } 823 824 return (-1); 825 } 826 827 static void 828 zx_fillrect(struct zx_softc *sc, int x, int y, int w, int h, uint32_t bg, 829 int rop) 830 { 831 832 833 while ((bus_space_read_4(sc->sc_bt, sc->sc_bhzc, zc_csr) & 834 ZX_CSR_BLT_BUSY) != 0) 835 ; 836 837 bus_space_write_4(sc->sc_bt, sc->sc_bhzdss0, zd_rop, rop); 838 bus_space_write_4(sc->sc_bt, sc->sc_bhzdss0, zd_fg, bg); 839 bus_space_write_4(sc->sc_bt, sc->sc_bhzc, zc_extent, 840 (w - 1) | ((h - 1) << 11)); 841 bus_space_write_4(sc->sc_bt, sc->sc_bhzc, zc_fill, 842 x | (y << 11) | 0x80000000); 843 } 844 845 static void 846 zx_copyrect(struct zx_softc *sc, int sx, int sy, int dx, int dy, int w, 847 int h) 848 { 849 uint32_t dir; 850 851 w -= 1; 852 h -= 1; 853 854 if (sy < dy || sx < dx) { 855 dir = 0x80000000; 856 sx += w; 857 sy += h; 858 dx += w; 859 dy += h; 860 } else 861 dir = 0; 862 863 while ((bus_space_read_4(sc->sc_bt, sc->sc_bhzc, zc_csr) & 864 ZX_CSR_BLT_BUSY) != 0) 865 ; 866 867 bus_space_write_4(sc->sc_bt, sc->sc_bhzdss0, zd_rop, ZX_STD_ROP); 868 bus_space_write_4(sc->sc_bt, sc->sc_bhzc, zc_extent, 869 w | (h << 11) | dir); 870 bus_space_write_4(sc->sc_bt, sc->sc_bhzc, zc_src, sx | (sy << 11)); 871 bus_space_write_4(sc->sc_bt, sc->sc_bhzc, zc_copy, dx | (dy << 11)); 872 } 873 874 static void 875 zx_do_cursor(void *cookie, int on, int row, int col) 876 { 877 struct rasops_info *ri = cookie; 878 struct vcons_screen *scr = ri->ri_hw; 879 struct zx_softc *sc = scr->scr_cookie; 880 int x, y, wi, he; 881 882 wi = ri->ri_font->fontwidth; 883 he = ri->ri_font->fontheight; 884 885 if (ri->ri_flg & RI_CURSOR) { 886 x = ri->ri_ccol * wi + ri->ri_xorigin; 887 y = ri->ri_crow * he + ri->ri_yorigin; 888 zx_fillrect(sc, x, y, wi, he, 0xff000000, 889 ZX_ROP_NEW_XOR_OLD | ZX_ATTR_WE_ENABLE | ZX_ATTR_OE_ENABLE | 890 ZX_ATTR_FORCE_WID); 891 ri->ri_flg &= ~RI_CURSOR; 892 } 893 894 ri->ri_crow = row; 895 ri->ri_ccol = col; 896 897 if (on) 898 { 899 x = ri->ri_ccol * wi + ri->ri_xorigin; 900 y = ri->ri_crow * he + ri->ri_yorigin; 901 zx_fillrect(sc, x, y, wi, he, 0xff000000, 902 ZX_ROP_NEW_XOR_OLD | ZX_ATTR_WE_ENABLE | ZX_ATTR_OE_ENABLE | 903 ZX_ATTR_FORCE_WID); 904 ri->ri_flg |= RI_CURSOR; 905 } 906 } 907 908 static void 909 zx_erasecols(void *cookie, int row, int startcol, int ncols, long attr) 910 { 911 struct rasops_info *ri = cookie; 912 struct vcons_screen *scr = ri->ri_hw; 913 struct zx_softc *sc = scr->scr_cookie; 914 int32_t x, y, width, height, bg; 915 916 x = ri->ri_xorigin + ri->ri_font->fontwidth * startcol; 917 y = ri->ri_yorigin + ri->ri_font->fontheight * row; 918 width = ri->ri_font->fontwidth * ncols; 919 height = ri->ri_font->fontheight; 920 bg = ((uint32_t)ri->ri_devcmap[(attr >> 16) & 0xff]) << 24; 921 zx_fillrect(sc, x, y, width, height, bg, ZX_STD_ROP); 922 } 923 924 static void 925 zx_eraserows(void *cookie, int row, int nrows, long attr) 926 { 927 struct rasops_info *ri = cookie; 928 struct vcons_screen *scr = ri->ri_hw; 929 struct zx_softc *sc = scr->scr_cookie; 930 int32_t x, y, width, height, bg; 931 932 if ((row == 0) && (nrows == ri->ri_rows)) { 933 x = y = 0; 934 width = ri->ri_width; 935 height = ri->ri_height; 936 } else { 937 x = ri->ri_xorigin; 938 y = ri->ri_yorigin + ri->ri_font->fontheight * row; 939 width = ri->ri_emuwidth; 940 height = ri->ri_font->fontheight * nrows; 941 } 942 bg = ((uint32_t)ri->ri_devcmap[(attr >> 16) & 0xff]) << 24; 943 zx_fillrect(sc, x, y, width, height, bg, ZX_STD_ROP); 944 } 945 946 static void 947 zx_copyrows(void *cookie, int srcrow, int dstrow, int nrows) 948 { 949 struct rasops_info *ri = cookie; 950 struct vcons_screen *scr = ri->ri_hw; 951 struct zx_softc *sc = scr->scr_cookie; 952 int32_t x, ys, yd, width, height; 953 954 x = ri->ri_xorigin; 955 ys = ri->ri_yorigin + ri->ri_font->fontheight * srcrow; 956 yd = ri->ri_yorigin + ri->ri_font->fontheight * dstrow; 957 width = ri->ri_emuwidth; 958 height = ri->ri_font->fontheight * nrows; 959 zx_copyrect(sc, x, ys, x, yd, width, height); 960 } 961 962 static void 963 zx_copycols(void *cookie, int row, int srccol, int dstcol, int ncols) 964 { 965 struct rasops_info *ri = cookie; 966 struct vcons_screen *scr = ri->ri_hw; 967 struct zx_softc *sc = scr->scr_cookie; 968 int32_t xs, xd, y, width, height; 969 970 xs = ri->ri_xorigin + ri->ri_font->fontwidth * srccol; 971 xd = ri->ri_xorigin + ri->ri_font->fontwidth * dstcol; 972 y = ri->ri_yorigin + ri->ri_font->fontheight * row; 973 width = ri->ri_font->fontwidth * ncols; 974 height = ri->ri_font->fontheight; 975 zx_copyrect(sc, xs, y, xd, y, width, height); 976 } 977 978 static void 979 zx_putchar(void *cookie, int row, int col, u_int uc, long attr) 980 { 981 struct rasops_info *ri = cookie; 982 struct wsdisplay_font *font = PICK_FONT(ri, uc); 983 struct vcons_screen *scr = ri->ri_hw; 984 struct zx_softc *sc = scr->scr_cookie; 985 volatile uint32_t *dp; 986 uint8_t *fb; 987 int fs, i, ul; 988 uint32_t fg, bg; 989 990 rasops_unpack_attr(attr, &fg, &bg, &ul); 991 bg = ((uint32_t)ri->ri_devcmap[bg]) << 24; 992 fg = ((uint32_t)ri->ri_devcmap[fg]) << 24; 993 if (uc == ' ') { 994 int x, y; 995 996 x = ri->ri_xorigin + font->fontwidth * col; 997 y = ri->ri_yorigin + font->fontheight * row; 998 zx_fillrect(sc, x, y, font->fontwidth, 999 font->fontheight, bg, ZX_STD_ROP); 1000 return; 1001 } 1002 1003 dp = (volatile uint32_t *)sc->sc_pixels + 1004 ((row * font->fontheight + ri->ri_yorigin) << 11) + 1005 (col * font->fontwidth + ri->ri_xorigin); 1006 fb = (uint8_t *)font->data + (uc - font->firstchar) * 1007 ri->ri_fontscale; 1008 fs = font->stride; 1009 1010 while ((bus_space_read_4(sc->sc_bt, sc->sc_bhzc, zc_csr) & 1011 ZX_CSR_BLT_BUSY) != 0) 1012 ; 1013 1014 bus_space_write_4(sc->sc_bt, sc->sc_bhzdss0, zd_rop, ZX_STD_ROP); 1015 bus_space_write_4(sc->sc_bt, sc->sc_bhzdss0, zd_fg, fg); 1016 bus_space_write_4(sc->sc_bt, sc->sc_bhzdss0, zd_bg, bg); 1017 bus_space_write_4(sc->sc_bt, sc->sc_bhzc, zc_fontmsk, 1018 0xffffffff << (32 - font->fontwidth)); 1019 1020 if (font->fontwidth <= 8) { 1021 for (i = font->fontheight; i != 0; i--, dp += 2048) { 1022 *dp = *fb << 24; 1023 fb += fs; 1024 } 1025 } else { 1026 for (i = font->fontheight; i != 0; i--, dp += 2048) { 1027 *dp = *((uint16_t *)fb) << 16; 1028 fb += fs; 1029 } 1030 } 1031 1032 if (ul) { 1033 dp -= 4096; 1034 *dp = 0xffffffff; 1035 } 1036 } 1037 1038 static int 1039 zx_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, 1040 struct lwp *l) 1041 { 1042 /* we'll probably need to add more stuff here */ 1043 struct vcons_data *vd = v; 1044 struct zx_softc *sc = vd->cookie; 1045 struct wsdisplay_fbinfo *wdf; 1046 struct vcons_screen *ms = sc->vd.active; 1047 struct rasops_info *ri = &ms->scr_ri; 1048 switch (cmd) { 1049 case WSDISPLAYIO_GTYPE: 1050 *(u_int *)data = WSDISPLAY_TYPE_SUNTCX; 1051 return 0; 1052 case WSDISPLAYIO_GINFO: 1053 wdf = (void *)data; 1054 wdf->height = ri->ri_height; 1055 wdf->width = ri->ri_width; 1056 wdf->depth = ri->ri_depth; 1057 wdf->cmsize = 256; 1058 return 0; 1059 1060 case WSDISPLAYIO_GETCMAP: 1061 return zx_getcmap(sc, 1062 (struct wsdisplay_cmap *)data); 1063 case WSDISPLAYIO_PUTCMAP: 1064 return zx_putcmap(sc, 1065 (struct wsdisplay_cmap *)data); 1066 1067 case WSDISPLAYIO_SMODE: 1068 { 1069 int new_mode = *(int*)data; 1070 if (new_mode != sc->sc_mode) 1071 { 1072 sc->sc_mode = new_mode; 1073 if(new_mode == WSDISPLAYIO_MODE_EMUL) 1074 { 1075 zx_reset(sc); 1076 vcons_redraw_screen(ms); 1077 } 1078 } 1079 } 1080 return 0; 1081 } 1082 return EPASSTHROUGH; 1083 } 1084 1085 static paddr_t 1086 zx_mmap(void *v, void *vs, off_t offset, int prot) 1087 { 1088 /* I'm not at all sure this is the right thing to do */ 1089 return zxmmap(0, offset, prot); /* assume minor dev 0 for now */ 1090 } 1091 1092 static int 1093 zx_putcmap(struct zx_softc *sc, struct wsdisplay_cmap *cm) 1094 { 1095 u_int index = cm->index; 1096 u_int count = cm->count; 1097 int error,i; 1098 if (index >= 256 || count > 256 || index + count > 256) 1099 return EINVAL; 1100 1101 for (i = 0; i < count; i++) 1102 { 1103 error = copyin(&cm->red[i], 1104 &sc->sc_cmap[index + i], 1); 1105 if (error) 1106 return error; 1107 error = copyin(&cm->green[i], 1108 &sc->sc_cmap[index + i + 256], 1); 1109 if (error) 1110 return error; 1111 error = copyin(&cm->blue[i], 1112 &sc->sc_cmap[index + i + 512], 1); 1113 if (error) 1114 return error; 1115 } 1116 zx_cmap_put(sc); 1117 1118 return 0; 1119 } 1120 1121 static int 1122 zx_getcmap(struct zx_softc *sc, struct wsdisplay_cmap *cm) 1123 { 1124 u_int index = cm->index; 1125 u_int count = cm->count; 1126 int error,i; 1127 1128 if (index >= 256 || count > 256 || index + count > 256) 1129 return EINVAL; 1130 1131 for (i = 0; i < count; i++) 1132 { 1133 error = copyout(&sc->sc_cmap[index + i], 1134 &cm->red[i], 1); 1135 if (error) 1136 return error; 1137 error = copyout(&sc->sc_cmap[index + i + 256], 1138 &cm->green[i], 1); 1139 if (error) 1140 return error; 1141 error = copyout(&sc->sc_cmap[index + i + 256], 1142 &cm->blue[i], 1); 1143 if (error) 1144 return error; 1145 } 1146 1147 return 0; 1148 } 1149 1150 static void 1151 zx_init_screen(void *cookie, struct vcons_screen *scr, 1152 int existing, long *defattr) 1153 { 1154 struct zx_softc *sc = cookie; 1155 struct rasops_info *ri = &scr->scr_ri; 1156 1157 ri->ri_depth = 8; /*sc->sc_fb.fb_type.fb_depth = 32;*/ 1158 ri->ri_width = sc->sc_width; 1159 ri->ri_height = sc->sc_height; 1160 ri->ri_stride = sc->sc_stride; 1161 ri->ri_flg = RI_CENTER; 1162 1163 ri->ri_bits = (void *)sc->sc_pixels; 1164 1165 rasops_init(ri, 0, 0); 1166 ri->ri_caps = WSSCREEN_WSCOLORS | WSSCREEN_REVERSE; 1167 rasops_reconfig(ri, sc->sc_height / ri->ri_font->fontheight, 1168 sc->sc_width / ri->ri_font->fontwidth); 1169 1170 ri->ri_hw = scr; 1171 1172 ri->ri_ops.cursor = zx_do_cursor; 1173 ri->ri_ops.copycols = zx_copycols; 1174 ri->ri_ops.copyrows = zx_copyrows; 1175 ri->ri_ops.erasecols = zx_erasecols; 1176 ri->ri_ops.eraserows = zx_eraserows; 1177 ri->ri_ops.putchar = zx_putchar; 1178 } 1179