Home | History | Annotate | Line # | Download | only in dev
      1 /*	$NetBSD: fbbm.c,v 1.2 2026/09/20 11:52:56 tsutsui Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2026 Izumi Tsutsui.  All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  *
     15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     25  */
     26 
     27 #include <sys/cdefs.h>
     28 __KERNEL_RCSID(0, "$NetBSD: fbbm.c,v 1.2 2026/09/20 11:52:56 tsutsui Exp $");
     29 
     30 #include <sys/param.h>
     31 #include <sys/device.h>
     32 #include <sys/errno.h>
     33 #include <sys/ioctl.h>
     34 #include <sys/kmem.h>
     35 #include <sys/systm.h>
     36 
     37 #include <machine/bus.h>
     38 
     39 #include <news68k/dev/hbvar.h>
     40 #include <news68k/dev/nwb225reg.h>
     41 
     42 #include <dev/wscons/wsconsio.h>
     43 #include <dev/wscons/wsdisplayvar.h>
     44 
     45 struct fbbm_devconfig {
     46 	bus_space_tag_t dc_bst;
     47 	bus_space_handle_t dc_rcont_bsh;
     48 	bus_space_handle_t dc_krom_bsh;
     49 
     50 	int dc_nplanes;
     51 	uint32_t dc_planemask;
     52 	int dc_dispver;
     53 
     54 	int dc_fbwidth;
     55 	int dc_fbheight;
     56 	int dc_width;
     57 	int dc_height;
     58 
     59 	int dc_fontwidth;
     60 	int dc_fontheight;
     61 	int dc_cellwidth;
     62 	int dc_cellheight;
     63 
     64 	int dc_ncols;
     65 	int dc_nrows;
     66 	int dc_xorigin;
     67 	int dc_yorigin;
     68 
     69 	int dc_cursoron;
     70 	int dc_cursorrow;
     71 	int dc_cursorcol;
     72 
     73 	struct wsscreen_descr dc_wsscrdescr;
     74 };
     75 
     76 struct fbbm_softc {
     77 	device_t sc_dev;
     78 	struct fbbm_devconfig *sc_dc;
     79 	int sc_nscreens;
     80 	int sc_wsmode;
     81 	const struct wsscreen_descr *sc_scrdescs[1];
     82 	struct wsscreen_list sc_scrlist;
     83 };
     84 
     85 /* autoconf functions and helpers */
     86 static int fbbm_match(device_t, cfdata_t, void *);
     87 static void fbbm_attach(device_t, device_t, void *);
     88 
     89 static bool fbbm_console_initialized(void);
     90 static int fbbm_probe(bus_space_tag_t, bus_addr_t);
     91 
     92 /* bus_space mappings */
     93 static int nwb225_map(struct fbbm_devconfig *, bus_space_tag_t, bus_addr_t);
     94 static void nwb225_unmap(struct fbbm_devconfig *);
     95 
     96 /* ROP helper functions */
     97 static void nwb225_rop_reset(struct fbbm_devconfig *);
     98 static int nwb225_rop_wait(struct fbbm_devconfig *);
     99 static void nwb225_ctl_write(struct fbbm_devconfig *, uint16_t, uint16_t);
    100 static void nwb225_rop_set_plane_mask(struct fbbm_devconfig *, uint32_t);
    101 static void nwb225_rop_make_funcvec(struct fbbm_devconfig *, u_int,
    102     uint32_t, uint32_t, uint8_t *);
    103 static void nwb225_rop_funcvec_constant_s(struct fbbm_devconfig *, uint8_t *,
    104     u_int);
    105 static void nwb225_rop_set_funcvec(struct fbbm_devconfig *, const uint8_t *);
    106 
    107 /* Hardware primitive ROP functions */
    108 static int nwb225_rop_copy_rect(struct fbbm_devconfig *, int, int, int, int,
    109     int, int, u_int);
    110 static int nwb225_rop_fill_rect(struct fbbm_devconfig *, int, int, int, int,
    111     uint32_t);
    112 static int nwb225_rop_hwrite(struct fbbm_devconfig *, const uint16_t *, int,
    113     int, int, int, int, int, int, uint32_t, uint32_t);
    114 
    115 /* ROM font functions */
    116 static bus_size_t nwb225_krom_ascii_offset(u_int);
    117 static void nwb225_krom_load_cell(struct fbbm_devconfig *, u_int,
    118     uint16_t *);
    119 
    120 /* Initialization */
    121 static int nwb225_init_hardware(struct fbbm_devconfig *);
    122 
    123 static void fbbm_init_screen(struct fbbm_devconfig *);
    124 static int fbbm_clear_screen(struct fbbm_devconfig *);
    125 
    126 /* wsdisplay_emulops functions */
    127 static void fbbm_cursor(void *, int, int, int);
    128 static int fbbm_mapchar(void *, int, u_int *);
    129 static void fbbm_putchar(void *, int, int, u_int, long);
    130 static void fbbm_copycols(void *, int, int, int, int);
    131 static void fbbm_erasecols(void *, int, int, int, long);
    132 static void fbbm_copyrows(void *, int, int, int);
    133 static void fbbm_eraserows(void *, int, int, long);
    134 static int fbbm_allocattr(void *, int, int, int, long *);
    135 
    136 /* wsdisplay_accessops functions */
    137 static int fbbm_ioctl(void *, void *, u_long, void *, int, struct lwp *);
    138 static int fbbm_alloc_screen(void *, const struct wsscreen_descr *, void **,
    139     int *, int *, long *);
    140 static void fbbm_free_screen(void *, void *);
    141 static int fbbm_show_screen(void *, void *, int, void (*)(void *, int, int),
    142     void *);
    143 
    144 /* cnattach function for early console */
    145 int fbbm_cnattach(void);
    146 
    147 CFATTACH_DECL_NEW(fbbm, sizeof(struct fbbm_softc),
    148     fbbm_match, fbbm_attach, NULL, NULL);
    149 
    150 static struct fbbm_devconfig fbbm_console_dc;
    151 
    152 static const struct wsdisplay_emulops fbbm_emulops = {
    153 	.cursor = fbbm_cursor,
    154 	.mapchar = fbbm_mapchar,
    155 	.putchar = fbbm_putchar,
    156 	.copycols = fbbm_copycols,
    157 	.erasecols = fbbm_erasecols,
    158 	.copyrows = fbbm_copyrows,
    159 	.eraserows = fbbm_eraserows,
    160 	.allocattr = fbbm_allocattr,
    161 	.replaceattr = NULL,
    162 };
    163 
    164 static const struct wsdisplay_accessops fbbm_accessops = {
    165 	.ioctl = fbbm_ioctl,
    166 	.mmap = NULL,
    167 	.alloc_screen = fbbm_alloc_screen,
    168 	.free_screen = fbbm_free_screen,
    169 	.show_screen = fbbm_show_screen,
    170 	.load_font = NULL,
    171 	.pollc = NULL,
    172 	.scroll = NULL,
    173 };
    174 
    175 /* Timeout count to check ROP op complete */
    176 #define NWB225_WAIT_MAX		100000
    177 
    178 /* Maximum number of planes of NWB-225 (4 or 8) */
    179 #define NWB225_MAX_PLANES	8
    180 
    181 /*
    182  * Software logical functions F(S,D).
    183  * These values index nwb225_fccnvtab[] and are not hardware function values.
    184  */
    185 #define NWB225_ROP_LF_0		0x00
    186 #define NWB225_ROP_LF_S		0x03
    187 #define NWB225_ROP_LF_D		0x05
    188 #define NWB225_ROP_LF_DI	0x0a
    189 #define NWB225_ROP_LF_1		0x0f
    190 
    191 /*
    192  * Logical FBBM function to 51X hardware function conversion table
    193  * taken from NEWS-OS fbbm_51x.o.
    194  */
    195 static const uint8_t nwb225_fccnvtab[16] = {
    196 	0x03, 0x0b, 0x07, 0x0f,
    197 	0x02, 0x0a, 0x06, 0x0e,
    198 	0x01, 0x09, 0x05, 0x0d,
    199 	0x00, 0x08, 0x04, 0x0c,
    200 };
    201 
    202 /*
    203  * Fixed initialization values written by the NWS-1750 PROM and NEWS-OS
    204  * 51X setup.  Their individual timing/control semantics are intentionally
    205  * not inferred here.
    206  */
    207 static const uint16_t nwb225_init_words[] = {
    208 	0x0400, 0x0002, 0x0005, 0x00d8,
    209 	0x0000, 0x0006, 0x0000,
    210 };
    211 
    212 /*
    213  * 4-bit component values taken from the NWS-1750 PROM NWB-225 module.
    214  * The PROM repeats this 16-entry table through all 256 palette indices
    215  * and writes each component after shifting it left by four bits.
    216  */
    217 static const uint8_t nwb225_prom_palette[16][3] = {
    218 	{  0,  0,  0 },
    219 	{  0,  0,  4 },
    220 	{  0,  4,  0 },
    221 	{  0,  4,  4 },
    222 	{  4,  0,  0 },
    223 	{  4,  0,  4 },
    224 	{  4,  4,  0 },
    225 	{  4,  4,  4 },
    226 	{  8,  8,  8 },
    227 	{  0,  0, 15 },
    228 	{  0, 15,  0 },
    229 	{  0, 15, 15 },
    230 	{ 15,  0,  0 },
    231 	{ 15,  0, 15 },
    232 	{ 15, 15,  0 },
    233 	{ 15, 15, 15 },
    234 };
    235 
    236 static bool
    237 fbbm_console_initialized(void)
    238 {
    239 
    240 	/*
    241 	 * This is initialized in fbbm_init_screen() via
    242 	 * fbbm_cnattach() or fbbm_cnattach().
    243 	 */
    244 	return fbbm_console_dc.dc_wsscrdescr.textops == &fbbm_emulops;
    245 }
    246 
    247 static int
    248 fbbm_probe(bus_space_tag_t bst, bus_addr_t rcont_addr)
    249 {
    250 	bus_space_handle_t bsh;
    251 	int rv;
    252 
    253 	if (bus_space_map(bst, rcont_addr, NWB225_RCONT_SIZE, 0, &bsh) != 0)
    254 		return 0;
    255 
    256 	/* NEWS-OS fb225_probe() used a byte-sized badaddr() at this base */
    257 	rv = news68k_bus_space_probe(bst, bsh, 0, 1);
    258 	bus_space_unmap(bst, bsh, NWB225_RCONT_SIZE);
    259 
    260 	return rv;
    261 }
    262 
    263 static int
    264 fbbm_match(device_t parent, cfdata_t cf, void *aux)
    265 {
    266 	struct hb_attach_args *ha = aux;
    267 
    268 	if (strcmp(ha->ha_name, "fbbm") != 0)
    269 		return 0;
    270 	if (ha->ha_address != NWB225_RCONT_BASE)
    271 		return 0;
    272 
    273 	return fbbm_probe(ha->ha_bust, ha->ha_address);
    274 }
    275 
    276 static void
    277 fbbm_attach(device_t parent, device_t self, void *aux)
    278 {
    279 	struct fbbm_softc *sc = device_private(self);
    280 	struct hb_attach_args *ha = aux;
    281 	struct fbbm_devconfig *dc;
    282 	struct wsemuldisplaydev_attach_args waa;
    283 	bool console;
    284 
    285 	sc->sc_dev = self;
    286 	console = fbbm_console_initialized();
    287 
    288 	if (console) {
    289 		dc = &fbbm_console_dc;
    290 		sc->sc_nscreens = 1;
    291 	} else {
    292 		dc = kmem_zalloc(sizeof(*dc), KM_SLEEP);
    293 		if (nwb225_map(dc, ha->ha_bust, ha->ha_address) != 0) {
    294 			aprint_error(": unable to map device registers");
    295 			goto out_free;
    296 		}
    297 
    298 		if (nwb225_init_hardware(dc) != 0) {
    299 			aprint_error(": controller initialization failed");
    300 			goto out_unmap;
    301 		}
    302 
    303 		fbbm_init_screen(dc);
    304 		if (fbbm_clear_screen(dc) != 0) {
    305 			aprint_error(": unable to clear text screen");
    306 			goto out_unmap;
    307 		}
    308 		sc->sc_nscreens = 0;
    309 	}
    310 
    311 	sc->sc_dc = dc;
    312 	sc->sc_wsmode = WSDISPLAYIO_MODE_EMUL;
    313 	sc->sc_scrdescs[0] = &dc->dc_wsscrdescr;
    314 	sc->sc_scrlist.nscreens = __arraycount(sc->sc_scrdescs);
    315 	sc->sc_scrlist.screens = sc->sc_scrdescs;
    316 
    317 	aprint_normal(": NWB-225, %d x %d, %d planes, display version %d\n",
    318 	    dc->dc_width, dc->dc_height, dc->dc_nplanes, dc->dc_dispver);
    319 
    320 	waa.console = console;
    321 	waa.scrdata = &sc->sc_scrlist;
    322 	waa.accessops = &fbbm_accessops;
    323 	waa.accesscookie = sc;
    324 
    325 	config_found(self, &waa, wsemuldisplaydevprint, CFARGS_NONE);
    326 	return;
    327 
    328  out_unmap:
    329 	nwb225_unmap(dc);
    330  out_free:
    331 	kmem_free(dc, sizeof(*dc));
    332 }
    333 
    334 static int
    335 nwb225_map(struct fbbm_devconfig *dc, bus_space_tag_t bst,
    336     bus_addr_t rcont_addr)
    337 {
    338 	int error;
    339 
    340 	dc->dc_bst = bst;
    341 
    342 	error = bus_space_map(bst, rcont_addr, NWB225_RCONT_SIZE, 0,
    343 	    &dc->dc_rcont_bsh);
    344 	if (error != 0)
    345 		return error;
    346 
    347 	error = bus_space_map(bst, NWB225_KROM_BASE, NWB225_KROM_SIZE, 0,
    348 	    &dc->dc_krom_bsh);
    349 	if (error != 0) {
    350 		bus_space_unmap(bst, dc->dc_rcont_bsh, NWB225_RCONT_SIZE);
    351 		return error;
    352 	}
    353 
    354 	return 0;
    355 }
    356 
    357 static void
    358 nwb225_unmap(struct fbbm_devconfig *dc)
    359 {
    360 
    361 	bus_space_unmap(dc->dc_bst, dc->dc_krom_bsh, NWB225_KROM_SIZE);
    362 	bus_space_unmap(dc->dc_bst, dc->dc_rcont_bsh, NWB225_RCONT_SIZE);
    363 }
    364 
    365 static void
    366 nwb225_rop_reset(struct fbbm_devconfig *dc)
    367 {
    368 
    369 	/*
    370 	 * Both NEWS-OS fb51X_rop_reset() and the NWS-1750 PROM perform
    371 	 * this 0 -> 1 sequence.
    372 	 */
    373 	bus_space_write_2(dc->dc_bst, dc->dc_rcont_bsh, NWB225_STATUS, 0);
    374 	bus_space_write_2(dc->dc_bst, dc->dc_rcont_bsh, NWB225_STATUS, 1);
    375 }
    376 
    377 static int
    378 nwb225_rop_wait(struct fbbm_devconfig *dc)
    379 {
    380 	uint16_t status;
    381 	int i;
    382 
    383 	for (i = 0; i < NWB225_WAIT_MAX; i++) {
    384 		status = bus_space_read_2(dc->dc_bst, dc->dc_rcont_bsh,
    385 		    NWB225_STATUS);
    386 		if ((status & NWB225_STATUS_BUSY) == 0)
    387 			return 0;
    388 	}
    389 
    390 	/* Reset ROP engine on timeout as NEWS-OS does */
    391 	nwb225_rop_reset(dc);
    392 	return ETIMEDOUT;
    393 }
    394 
    395 static void
    396 nwb225_ctl_write(struct fbbm_devconfig *dc, uint16_t selector, uint16_t value)
    397 {
    398 
    399 	bus_space_write_2(dc->dc_bst, dc->dc_rcont_bsh,
    400 	    NWB225_CTL_SELECT, selector);
    401 	bus_space_write_2(dc->dc_bst, dc->dc_rcont_bsh,
    402 	    NWB225_CTL_DATA, value);
    403 }
    404 
    405 static void
    406 nwb225_rop_set_plane_mask(struct fbbm_devconfig *dc, uint32_t planemask)
    407 {
    408 	uint8_t disabled_planes;
    409 	uint16_t hwmask;
    410 
    411 	disabled_planes = (uint8_t)~planemask;
    412 	hwmask = (uint16_t)disabled_planes << NWB225_PLANE_CTRL_SHIFT;
    413 	bus_space_write_2(dc->dc_bst, dc->dc_rcont_bsh,
    414 	    NWB225_PLANE_CTRL, hwmask);
    415 }
    416 
    417 static void
    418 nwb225_rop_make_funcvec(struct fbbm_devconfig *dc, u_int logical_func,
    419     uint32_t src1, uint32_t src0, uint8_t *funcvec)
    420 {
    421 	uint8_t f_s0_d, f_s1_d;
    422 	uint8_t func_by_srcmap[4];
    423 	uint8_t src1_bit, src0_bit, src_map;
    424 	int i;
    425 
    426 	KASSERT(logical_func <= 0x0f);
    427 	KASSERT(dc->dc_nplanes == 4 || dc->dc_nplanes == 8);
    428 
    429 	/* F(0,D) */
    430 	f_s0_d = (logical_func >> 2) & 0x03;
    431 	/* F(1,D) */
    432 	f_s1_d = logical_func & 0x03;
    433 
    434 	func_by_srcmap[0] = (f_s0_d << 2) | f_s0_d;
    435 	func_by_srcmap[1] = (f_s1_d << 2) | f_s0_d;
    436 	func_by_srcmap[2] = (f_s0_d << 2) | f_s1_d;
    437 	func_by_srcmap[3] = (f_s1_d << 2) | f_s1_d;
    438 
    439 	for (i = 0; i < dc->dc_nplanes; i++) {
    440 		src1_bit = src1 & 0x01;
    441 		src0_bit = src0 & 0x01;
    442 		src_map = (src1_bit << 1) | src0_bit;
    443 		funcvec[i] = func_by_srcmap[src_map];
    444 		src1 >>= 1;
    445 		src0 >>= 1;
    446 	}
    447 }
    448 
    449 static void
    450 nwb225_rop_funcvec_constant_s(struct fbbm_devconfig *dc, uint8_t *funcvec,
    451     u_int s)
    452 {
    453 	uint8_t logical_func, f_s0_d, f_s1_d, d_func, result;
    454 	int i;
    455 
    456 	KASSERT(dc->dc_nplanes == 4 || dc->dc_nplanes == 8);
    457 	KASSERT(s <= 1);
    458 
    459 	for (i = 0; i < dc->dc_nplanes; i++) {
    460 		logical_func = funcvec[i];
    461 		KASSERT(logical_func <= 0x0f);
    462 
    463 		/* F(0,D) */
    464 		f_s0_d = (logical_func >> 2) & 0x03;
    465 		/* F(1,D) */
    466 		f_s1_d = logical_func & 0x03;
    467 
    468 		d_func = (s == 0) ? f_s0_d : f_s1_d;
    469 		switch (d_func) {
    470 		case 0:
    471 			result = NWB225_ROP_LF_0;
    472 			break;
    473 		case 1:
    474 			result = NWB225_ROP_LF_D;
    475 			break;
    476 		case 2:
    477 			result = NWB225_ROP_LF_DI;
    478 			break;
    479 		case 3:
    480 			result = NWB225_ROP_LF_1;
    481 			break;
    482 		default:
    483 			KASSERT(false);
    484 			result = NWB225_ROP_LF_0;
    485 			break;
    486 		}
    487 		funcvec[i] = result;
    488 	}
    489 }
    490 
    491 static void
    492 nwb225_rop_set_funcvec(struct fbbm_devconfig *dc, const uint8_t *funcvec)
    493 {
    494 	uint8_t logical_func, hw_func;
    495 	int i;
    496 
    497 	for (i = 0; i < dc->dc_nplanes; i++) {
    498 		KASSERT(funcvec[i] <= 0x0f);
    499 		logical_func = funcvec[i];
    500 		hw_func = nwb225_fccnvtab[logical_func];
    501 		bus_space_write_2(dc->dc_bst, dc->dc_rcont_bsh,
    502 		    NWB225_ROP_FUNC(i), hw_func);
    503 	}
    504 }
    505 
    506 static int
    507 nwb225_rect_valid(struct fbbm_devconfig *dc, int x, int y, int width,
    508     int height)
    509 {
    510 
    511 	if (x < 0 || y < 0 || width <= 0 || height <= 0)
    512 		return 0;
    513 	if (x >= dc->dc_fbwidth || y >= dc->dc_fbheight)
    514 		return 0;
    515 	if (width > dc->dc_fbwidth - x || height > dc->dc_fbheight - y)
    516 		return 0;
    517 
    518 	return 1;
    519 }
    520 
    521 static int
    522 nwb225_rop_copy_rect(struct fbbm_devconfig *dc, int sx, int sy, int width,
    523     int height, int dx, int dy, u_int logical_func)
    524 {
    525 	uint8_t funcvec[NWB225_MAX_PLANES];
    526 	bus_size_t copy_mode;
    527 	bool reverse_x, reverse_y;
    528 	int src_x, src_y, dst_x, dst_y, limit_x;
    529 	int error;
    530 
    531 	if (logical_func > 0x0f)
    532 		return EINVAL;
    533 
    534 	if (nwb225_rect_valid(dc, sx, sy, width, height) == 0 ||
    535 	    nwb225_rect_valid(dc, dx, dy, width, height) == 0)
    536 		return EINVAL;
    537 
    538 	error = nwb225_rop_wait(dc);
    539 	if (__predict_false(error != 0))
    540 		return error;
    541 
    542 	nwb225_rop_make_funcvec(dc, logical_func, dc->dc_planemask, 0, funcvec);
    543 	nwb225_rop_set_funcvec(dc, funcvec);
    544 	nwb225_rop_set_plane_mask(dc, dc->dc_planemask);
    545 
    546 	/* Wait again as NEWS-OS does */
    547 	error = nwb225_rop_wait(dc);
    548 	if (__predict_false(error != 0))
    549 		return error;
    550 
    551 	reverse_x = (sx < dx);
    552 	reverse_y = (sy < dy);
    553 	src_x     = sx + (reverse_x ? width : 0);
    554 	dst_x     = dx + (reverse_x ? width : 0);
    555 	limit_x   = dx + (reverse_x ? 0 : width);
    556 	src_y     = sy + (reverse_y ? height - 1 : 0);
    557 	dst_y     = dy + (reverse_y ? height - 1 : 0);
    558 
    559 	if (reverse_x) {
    560 		if (reverse_y) {
    561 			copy_mode = NWB225_ROP_COPY_XR_YR;
    562 		} else {
    563 			copy_mode = NWB225_ROP_COPY_XR_YF;
    564 		}
    565 	} else {
    566 		if (reverse_y) {
    567 			copy_mode = NWB225_ROP_COPY_XF_YR;
    568 		} else {
    569 			copy_mode = NWB225_ROP_COPY_XF_YF;
    570 		}
    571 	}
    572 
    573 	bus_space_write_2(dc->dc_bst, dc->dc_rcont_bsh,
    574 	    copy_mode, 0);
    575 	bus_space_write_2(dc->dc_bst, dc->dc_rcont_bsh,
    576 	    NWB225_DST_ADDR(dst_y), dst_x);
    577 	bus_space_write_2(dc->dc_bst, dc->dc_rcont_bsh,
    578 	    NWB225_SRC_ADDR(src_y) | NWB225_ROP_ADDR_FLAG_SET_SHIFT, src_x);
    579 	bus_space_write_2(dc->dc_bst, dc->dc_rcont_bsh,
    580 	    NWB225_ROP_LIMIT(height) | NWB225_ROP_ADDR_FLAG_EXECUTE, limit_x);
    581 
    582 	/* Wait complete for polling ops */
    583 	error = nwb225_rop_wait(dc);
    584 
    585 	return error;
    586 }
    587 
    588 static int
    589 nwb225_rop_fill_rect(struct fbbm_devconfig *dc, int x, int y, int width,
    590     int height, uint32_t color)
    591 {
    592 	uint8_t funcvec[NWB225_MAX_PLANES];
    593 	int error;
    594 
    595 	if (nwb225_rect_valid(dc, x, y, width, height) == 0)
    596 		return EINVAL;
    597 
    598 	error = nwb225_rop_wait(dc);
    599 	if (__predict_false(error != 0))
    600 		return error;
    601 
    602 	color &= dc->dc_planemask;
    603 	nwb225_rop_make_funcvec(dc, NWB225_ROP_LF_S, color, 0, funcvec);
    604 	nwb225_rop_funcvec_constant_s(dc, funcvec, 1);
    605 	nwb225_rop_set_plane_mask(dc, dc->dc_planemask);
    606 	nwb225_rop_set_funcvec(dc, funcvec);
    607 
    608 	/* Wait again as NEWS-OS does */
    609 	error = nwb225_rop_wait(dc);
    610 	if (__predict_false(error != 0))
    611 		return error;
    612 
    613 	bus_space_write_2(dc->dc_bst, dc->dc_rcont_bsh,
    614 	    NWB225_ROP_CONST_FILL, 0);
    615 	bus_space_write_2(dc->dc_bst, dc->dc_rcont_bsh,
    616 	    NWB225_DST_ADDR(0), 0);
    617 	bus_space_write_2(dc->dc_bst, dc->dc_rcont_bsh,
    618 	    NWB225_SRC_ADDR(0) | NWB225_ROP_ADDR_FLAG_SET_SHIFT, 0);
    619 	bus_space_write_2(dc->dc_bst, dc->dc_rcont_bsh,
    620 	    NWB225_DST_ADDR(y), x);
    621 	bus_space_write_2(dc->dc_bst, dc->dc_rcont_bsh,
    622 	    NWB225_ROP_LIMIT(height) | NWB225_ROP_ADDR_FLAG_EXECUTE, x + width);
    623 
    624 	/* Wait complete for polling ops */
    625 	error = nwb225_rop_wait(dc);
    626 
    627 	return error;
    628 }
    629 
    630 static int
    631 nwb225_rop_hwrite(struct fbbm_devconfig *dc, const uint16_t *src_words,
    632     int src_stride_words, int srcx, int srcy, int width, int height,
    633     int dx, int dy, uint32_t foreground, uint32_t background)
    634 {
    635 	uint8_t funcvec[NWB225_MAX_PLANES];
    636 	int source_word_offset, source_bit_offset;
    637 	int row_words, row_skip_words, row, word;
    638 	const uint16_t *sp;
    639 	int error;
    640 
    641 	if (src_words == NULL || src_stride_words <= 0 || srcx < 0 || srcy < 0)
    642 		return EINVAL;
    643 
    644 	if (nwb225_rect_valid(dc, dx, dy, width, height) == 0)
    645 		return EINVAL;
    646 
    647 	source_word_offset = srcx / NWB225_HWRITE_BITS_PER_WORD;
    648 	source_bit_offset  = srcx % NWB225_HWRITE_BITS_PER_WORD;
    649 	row_words =
    650 	    howmany(source_bit_offset + width, NWB225_HWRITE_BITS_PER_WORD);
    651 	if (source_word_offset + row_words > src_stride_words)
    652 		return EINVAL;
    653 
    654 	error = nwb225_rop_wait(dc);
    655 	if (__predict_false(error != 0))
    656 		return error;
    657 
    658 	foreground &= dc->dc_planemask;
    659 	background &= dc->dc_planemask;
    660 	nwb225_rop_make_funcvec(dc, NWB225_ROP_LF_S, foreground, background,
    661 	    funcvec);
    662 	nwb225_rop_set_funcvec(dc, funcvec);
    663 	nwb225_rop_set_plane_mask(dc, dc->dc_planemask);
    664 
    665 	/* Wait again as NEWS-OS does */
    666 	error = nwb225_rop_wait(dc);
    667 	if (__predict_false(error != 0))
    668 		return error;
    669 
    670 	bus_space_write_2(dc->dc_bst, dc->dc_rcont_bsh,
    671 	    NWB225_DST_ADDR(dy), dx);
    672 	bus_space_write_2(dc->dc_bst, dc->dc_rcont_bsh,
    673 	    NWB225_SRC_ADDR(0) | NWB225_ROP_ADDR_FLAG_SET_SHIFT,
    674 	    source_bit_offset);
    675 	bus_space_write_2(dc->dc_bst, dc->dc_rcont_bsh,
    676 	    NWB225_HWRITE_LIMIT(height) | NWB225_ROP_ADDR_FLAG_EXECUTE,
    677 	    dx + width);
    678 
    679 	sp = src_words + (srcy * src_stride_words) + source_word_offset;
    680 	row_skip_words = src_stride_words - row_words;
    681 	for (row = 0; row < height; row++) {
    682 		for (word = 0; word < row_words; word++)
    683 			bus_space_write_2(dc->dc_bst, dc->dc_rcont_bsh,
    684 			    NWB225_HWRITE_DATA, *sp++);
    685 		sp += row_skip_words;
    686 	}
    687 
    688 	/* Wait complete for polling ops */
    689 	error = nwb225_rop_wait(dc);
    690 
    691 	return error;
    692 }
    693 
    694 static bus_size_t
    695 nwb225_krom_ascii_offset(u_int c)
    696 {
    697 	u_int index;
    698 
    699 	index = c - NWB225_KROM_ASCII_FIRST;
    700 	return ((index & 0xe0) << 9) | ((index & 0x1f) << 7);
    701 }
    702 
    703 static void
    704 nwb225_krom_load_cell(struct fbbm_devconfig *dc, u_int c, uint16_t *cell)
    705 {
    706 	bus_size_t offset;
    707 	int row;
    708 
    709 	memset(cell, 0, NWB225_CELL_HEIGHT * sizeof(*cell));
    710 	offset = nwb225_krom_ascii_offset(c);
    711 
    712 	/*
    713 	 * The PROM renderer reads one 32-bit KROM row, keeps the upper word,
    714 	 * masks its low four bits, and places the 24 glyph rows after four
    715 	 * blank rows in the 16x30 cell.
    716 	 */
    717 	for (row = 0; row < NWB225_FONT_HEIGHT; row++)
    718 		cell[NWB225_GLYPH_YOFFSET + row] =
    719 		    bus_space_read_2(dc->dc_bst, dc->dc_krom_bsh,
    720 		    offset + row * NWB225_KROM_ROWBYTES) &
    721 		    NWB225_KROM_ASCII_MASK;
    722 }
    723 
    724 static int
    725 nwb225_init_hardware(struct fbbm_devconfig *dc)
    726 {
    727 	uint16_t status, value;
    728 	int i, component;
    729 
    730 	/*
    731 	 * Use the cold-init sequence used by the NWB-225 routine in the
    732 	 * NWS-1750 PROM. In particular, the second reset is intentional.
    733 	 * The serial console cold-boot experiment required the init block
    734 	 * followed by this reset before stable sync was established by CRTC.
    735 	 */
    736 	nwb225_rop_reset(dc);
    737 
    738 	for (i = 0; i < __arraycount(nwb225_init_words); i++)
    739 		bus_space_write_2(dc->dc_bst, dc->dc_rcont_bsh,
    740 		    NWB225_INIT_BASE + i * sizeof(uint16_t),
    741 		    nwb225_init_words[i]);
    742 
    743 	status = bus_space_read_2(dc->dc_bst, dc->dc_rcont_bsh, NWB225_STATUS);
    744 	if ((status & NWB225_STATUS_4PLANE) != 0)
    745 		dc->dc_nplanes = 4;
    746 	else
    747 		dc->dc_nplanes = 8;
    748 	dc->dc_planemask = (1U << dc->dc_nplanes) - 1;
    749 	dc->dc_dispver =
    750 	    (status & NWB225_STATUS_DISPVER) >> NWB225_STATUS_DISPVER_SHIFT;
    751 
    752 	nwb225_ctl_write(dc, 4, dc->dc_planemask);
    753 	nwb225_ctl_write(dc, 5, 0);
    754 	nwb225_ctl_write(dc, 6, 0x40);
    755 	nwb225_ctl_write(dc, 7, 0);
    756 
    757 	nwb225_rop_reset(dc);
    758 
    759 	/* Complete the fixed PROM palette/control initialization */
    760 	for (i = 0; i < 256; i++) {
    761 		bus_space_write_2(dc->dc_bst, dc->dc_rcont_bsh,
    762 		    NWB225_PAL_INDEX, i);
    763 		for (component = 0; component < 3; component++) {
    764 			value = nwb225_prom_palette[i & 0x0f][component] << 4;
    765 			bus_space_write_2(dc->dc_bst, dc->dc_rcont_bsh,
    766 			    NWB225_PAL_DATA, value);
    767 		}
    768 	}
    769 
    770 	bus_space_write_2(dc->dc_bst, dc->dc_rcont_bsh, NWB225_PLANE_CTRL, 0);
    771 
    772 	return nwb225_rop_wait(dc);
    773 }
    774 
    775 static void
    776 fbbm_init_screen(struct fbbm_devconfig *dc)
    777 {
    778 
    779 	dc->dc_fbwidth = NWB225_FB_WIDTH;
    780 	dc->dc_fbheight = NWB225_FB_HEIGHT;
    781 	dc->dc_width = NWB225_VIS_WIDTH;
    782 	dc->dc_height = NWB225_VIS_HEIGHT;
    783 
    784 	dc->dc_fontwidth = NWB225_FONT_WIDTH;
    785 	dc->dc_fontheight = NWB225_FONT_HEIGHT;
    786 	dc->dc_cellwidth = NWB225_CELL_WIDTH;
    787 	dc->dc_cellheight = NWB225_CELL_HEIGHT;
    788 
    789 	dc->dc_ncols = NWB225_TEXT_COLS;
    790 	dc->dc_nrows = NWB225_TEXT_ROWS;
    791 	dc->dc_xorigin = NWB225_TEXT_XORIGIN;
    792 	dc->dc_yorigin = NWB225_TEXT_YORIGIN;
    793 
    794 	dc->dc_wsscrdescr.name = "std";
    795 	dc->dc_wsscrdescr.ncols = dc->dc_ncols;
    796 	dc->dc_wsscrdescr.nrows = dc->dc_nrows;
    797 	dc->dc_wsscrdescr.textops = &fbbm_emulops;
    798 	dc->dc_wsscrdescr.fontwidth = dc->dc_cellwidth;
    799 	dc->dc_wsscrdescr.fontheight = dc->dc_cellheight;
    800 	dc->dc_wsscrdescr.capabilities = WSSCREEN_REVERSE;
    801 	dc->dc_wsscrdescr.modecookie = NULL;
    802 
    803 	dc->dc_cursoron = 0;
    804 	dc->dc_cursorrow = 0;
    805 	dc->dc_cursorcol = 0;
    806 }
    807 
    808 /*
    809  * Clear the visible display region.
    810  * Called from both normal attach and early cnattach.
    811  */
    812 static int
    813 fbbm_clear_screen(struct fbbm_devconfig *dc)
    814 {
    815 
    816 	return nwb225_rop_fill_rect(dc, 0, 0, dc->dc_width, dc->dc_height, 0);
    817 }
    818 
    819 static uint32_t
    820 fbbm_attr_background(struct fbbm_devconfig *dc, long attr)
    821 {
    822 
    823 	return (attr & WSATTR_REVERSE) != 0 ? dc->dc_planemask : 0;
    824 }
    825 
    826 /*
    827  * wsdisplay_emulops functions
    828  */
    829 static void
    830 fbbm_cursor(void *cookie, int on, int row, int col)
    831 {
    832 	struct fbbm_devconfig *dc = cookie;
    833 	int x, y, error;
    834 
    835 	if (dc->dc_cursoron != 0) {
    836 		x = dc->dc_xorigin + dc->dc_cursorcol * dc->dc_cellwidth;
    837 		y = dc->dc_yorigin + dc->dc_cursorrow * dc->dc_cellheight;
    838 		error = nwb225_rop_copy_rect(dc, x, y, dc->dc_cellwidth,
    839 		    dc->dc_cellheight, x, y, NWB225_ROP_LF_DI);
    840 		if (error != 0)
    841 			return;
    842 		dc->dc_cursoron = 0;
    843 	}
    844 
    845 	dc->dc_cursorrow = row;
    846 	dc->dc_cursorcol = col;
    847 	if (on == 0)
    848 		return;
    849 	if (row < 0 || row >= dc->dc_nrows || col < 0 || col >= dc->dc_ncols)
    850 		return;
    851 
    852 	x = dc->dc_xorigin + col * dc->dc_cellwidth;
    853 	y = dc->dc_yorigin + row * dc->dc_cellheight;
    854 	error = nwb225_rop_copy_rect(dc, x, y, dc->dc_cellwidth,
    855 	    dc->dc_cellheight, x, y, NWB225_ROP_LF_DI);
    856 	if (error == 0)
    857 		dc->dc_cursoron = 1;
    858 }
    859 
    860 static int
    861 fbbm_mapchar(void *cookie, int c, u_int *cp)
    862 {
    863 
    864 	if (c < NWB225_KROM_ASCII_FIRST || c > NWB225_KROM_ASCII_LAST) {
    865 		*cp = ' ';
    866 		return 0;
    867 	}
    868 
    869 	*cp = c;
    870 	return 5;
    871 }
    872 
    873 static void
    874 fbbm_putchar(void *cookie, int row, int col, u_int uc, long attr)
    875 {
    876 	struct fbbm_devconfig *dc = cookie;
    877 	uint16_t cell[NWB225_CELL_HEIGHT];
    878 	uint32_t foreground, background;
    879 	int x, y;
    880 
    881 	if (__predict_false(row < 0 || row >= dc->dc_nrows ||
    882 	    col < 0 || col >= dc->dc_ncols))
    883 		return;
    884 	if (uc < NWB225_KROM_ASCII_FIRST || uc > NWB225_KROM_ASCII_LAST)
    885 		uc = ' ';
    886 
    887 	nwb225_krom_load_cell(dc, uc, cell);
    888 	background = fbbm_attr_background(dc, attr);
    889 	foreground = (~background) & dc->dc_planemask;
    890 	x = dc->dc_xorigin + col * dc->dc_cellwidth;
    891 	y = dc->dc_yorigin + row * dc->dc_cellheight;
    892 
    893 	(void)nwb225_rop_hwrite(dc, cell, 1, 0, 0, dc->dc_cellwidth,
    894 	    dc->dc_cellheight, x, y, foreground, background);
    895 }
    896 
    897 static void
    898 fbbm_copycols(void *cookie, int row, int srccol, int dstcol, int ncols)
    899 {
    900 	struct fbbm_devconfig *dc = cookie;
    901 	int sx, dx, y, width;
    902 
    903 	if (ncols <= 0 || row < 0 || row >= dc->dc_nrows)
    904 		return;
    905 	if (srccol < 0 || dstcol < 0 || srccol + ncols > dc->dc_ncols ||
    906 	    dstcol + ncols > dc->dc_ncols)
    907 		return;
    908 	if (srccol == dstcol)
    909 		return;
    910 
    911 	sx = dc->dc_xorigin + srccol * dc->dc_cellwidth;
    912 	dx = dc->dc_xorigin + dstcol * dc->dc_cellwidth;
    913 	y = dc->dc_yorigin + row * dc->dc_cellheight;
    914 	width = ncols * dc->dc_cellwidth;
    915 	(void)nwb225_rop_copy_rect(dc, sx, y, width, dc->dc_cellheight,
    916 	    dx, y, NWB225_ROP_LF_S);
    917 }
    918 
    919 static void
    920 fbbm_erasecols(void *cookie, int row, int col, int ncols, long attr)
    921 {
    922 	struct fbbm_devconfig *dc = cookie;
    923 	uint32_t background;
    924 	int x, y, width;
    925 
    926 	if (ncols <= 0 || row < 0 || row >= dc->dc_nrows)
    927 		return;
    928 	if (col < 0 || col + ncols > dc->dc_ncols)
    929 		return;
    930 
    931 	x = dc->dc_xorigin + col * dc->dc_cellwidth;
    932 	y = dc->dc_yorigin + row * dc->dc_cellheight;
    933 	width = ncols * dc->dc_cellwidth;
    934 	background = fbbm_attr_background(dc, attr);
    935 	(void)nwb225_rop_fill_rect(dc, x, y, width, dc->dc_cellheight,
    936 	    background);
    937 }
    938 
    939 static void
    940 fbbm_copyrows(void *cookie, int srcrow, int dstrow, int nrows)
    941 {
    942 	struct fbbm_devconfig *dc = cookie;
    943 	int x, sy, dy, height;
    944 
    945 	if (nrows <= 0 || srcrow < 0 || dstrow < 0 ||
    946 	    srcrow + nrows > dc->dc_nrows || dstrow + nrows > dc->dc_nrows)
    947 		return;
    948 	if (srcrow == dstrow)
    949 		return;
    950 
    951 	x = dc->dc_xorigin;
    952 	sy = dc->dc_yorigin + srcrow * dc->dc_cellheight;
    953 	dy = dc->dc_yorigin + dstrow * dc->dc_cellheight;
    954 	height = nrows * dc->dc_cellheight;
    955 	(void)nwb225_rop_copy_rect(dc, x, sy,
    956 	    dc->dc_ncols * dc->dc_cellwidth, height, x, dy, NWB225_ROP_LF_S);
    957 }
    958 
    959 static void
    960 fbbm_eraserows(void *cookie, int row, int nrows, long attr)
    961 {
    962 	struct fbbm_devconfig *dc = cookie;
    963 	uint32_t background;
    964 	int x, y, height;
    965 
    966 	if (nrows <= 0 || row < 0 || row + nrows > dc->dc_nrows)
    967 		return;
    968 
    969 	x = dc->dc_xorigin;
    970 	y = dc->dc_yorigin + row * dc->dc_cellheight;
    971 	height = nrows * dc->dc_cellheight;
    972 	background = fbbm_attr_background(dc, attr);
    973 	(void)nwb225_rop_fill_rect(dc, x, y,
    974 	    dc->dc_ncols * dc->dc_cellwidth, height, background);
    975 }
    976 
    977 static int
    978 fbbm_allocattr(void *cookie, int fg, int bg, int flags, long *attrp)
    979 {
    980 
    981 	if ((flags & ~WSATTR_REVERSE) != 0)
    982 		return EINVAL;
    983 
    984 	*attrp = flags & WSATTR_REVERSE;
    985 	return 0;
    986 }
    987 
    988 /*
    989  * wsdisplay_accessops functions
    990  */
    991 static int
    992 fbbm_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, struct lwp *l)
    993 {
    994 	struct fbbm_softc *sc = v;
    995 	struct fbbm_devconfig *dc = sc->sc_dc;
    996 	struct wsdisplay_fbinfo *fbinfo;
    997 
    998 	switch (cmd) {
    999 	case WSDISPLAYIO_GTYPE:
   1000 		*(u_int *)data = WSDISPLAY_TYPE_NWB225;
   1001 		return 0;
   1002 
   1003 	case WSDISPLAYIO_GINFO:
   1004 		fbinfo = data;
   1005 		fbinfo->height = dc->dc_height;
   1006 		fbinfo->width = dc->dc_width;
   1007 		fbinfo->depth = dc->dc_nplanes;
   1008 		fbinfo->cmsize = 0;
   1009 		return 0;
   1010 
   1011 	case WSDISPLAYIO_SMODE:
   1012 		sc->sc_wsmode = *(int *)data;
   1013 		return 0;
   1014 
   1015 	default:
   1016 		break;
   1017 	}
   1018 
   1019 	return EPASSTHROUGH;
   1020 }
   1021 
   1022 static int
   1023 fbbm_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep,
   1024     int *ccolp, int *crowp, long *attrp)
   1025 {
   1026 	struct fbbm_softc *sc = v;
   1027 	struct fbbm_devconfig *dc = sc->sc_dc;
   1028 	long attr;
   1029 	int error;
   1030 
   1031 	KASSERT(type == &dc->dc_wsscrdescr);
   1032 
   1033 	if (sc->sc_nscreens != 0)
   1034 		return ENOMEM;
   1035 
   1036 	error = fbbm_allocattr(dc, 0, 0, 0, &attr);
   1037 	if (error != 0)
   1038 		return error;
   1039 
   1040 	*cookiep = dc;
   1041 	*ccolp = 0;
   1042 	*crowp = 0;
   1043 	*attrp = attr;
   1044 	sc->sc_nscreens = 1;
   1045 
   1046 	return 0;
   1047 }
   1048 
   1049 static void
   1050 fbbm_free_screen(void *v, void *cookie)
   1051 {
   1052 	struct fbbm_softc *sc = v;
   1053 
   1054 	KASSERT(sc->sc_nscreens == 1);
   1055 	KASSERT(cookie == sc->sc_dc);
   1056 	KASSERT(sc->sc_dc != &fbbm_console_dc);
   1057 	sc->sc_nscreens = 0;
   1058 }
   1059 
   1060 static int
   1061 fbbm_show_screen(void *v, void *cookie, int waitok,
   1062     void (*cb)(void *, int, int), void *cbarg)
   1063 {
   1064 
   1065 	return 0;
   1066 }
   1067 
   1068 int
   1069 fbbm_cnattach(void)
   1070 {
   1071 	struct fbbm_devconfig *dc;
   1072 	long attr;
   1073 
   1074 	if (fbbm_console_initialized())
   1075 		return 0;
   1076 
   1077 	dc = &fbbm_console_dc;
   1078 	memset(dc, 0, sizeof(*dc));
   1079 	if (fbbm_probe(NEWS68K_BUS_SPACE_EIO, NWB225_RCONT_BASE) == 0)
   1080 		return ENXIO;
   1081 
   1082 	if (nwb225_map(dc, NEWS68K_BUS_SPACE_EIO, NWB225_RCONT_BASE) != 0)
   1083 		goto out_clear;
   1084 
   1085 	if (nwb225_init_hardware(dc) != 0)
   1086 		goto out_unmap;
   1087 
   1088 	fbbm_init_screen(dc);
   1089 	if (fbbm_clear_screen(dc) != 0)
   1090 		goto out_unmap;
   1091 
   1092 	if (fbbm_allocattr(dc, 0, 0, 0, &attr))
   1093 		goto out_unmap;
   1094 
   1095 	wsdisplay_cnattach(&dc->dc_wsscrdescr, dc, 0, 0, attr);
   1096 	return 0;
   1097 
   1098  out_unmap:
   1099 	nwb225_unmap(dc);
   1100  out_clear:
   1101 	memset(dc, 0, sizeof(*dc));
   1102 	return 1;
   1103 }
   1104