Home | History | Annotate | Line # | Download | only in dev
      1 /*	$NetBSD: zz9k_fb.c,v 1.1 2023/05/03 13:49:30 phx Exp $ */
      2 
      3 /*
      4  * Copyright (c) 2020 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Alain Runa.
      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 AUTHOR ``AS IS'' AND ANY EXPRESS OR
     20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     22  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     24  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     28  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #include <sys/cdefs.h>
     32 __KERNEL_RCSID(0, "$NetBSD: zz9k_fb.c,v 1.1 2023/05/03 13:49:30 phx Exp $");
     33 
     34 /* miscellaneous */
     35 #include <sys/errno.h>			/* EPASSTHROUGH */
     36 #include <sys/types.h>			/* size_t */
     37 #include <sys/stdint.h>			/* uintXX_t */
     38 #include <sys/stdbool.h>		/* bool */
     39 
     40 /* driver(9) */
     41 #include <sys/param.h>      		/* NODEV */
     42 #include <sys/device.h>     		/* CFATTACH_DECL_NEW(), device_priv() */
     43 #include <sys/errno.h>			/* EINVAL, ENODEV, EPASSTHROUGH */
     44 
     45 /* bus_space(9) and zorro bus */
     46 #include <sys/bus.h>			/* bus_space_xxx(), bus_space_xxx_t */
     47 #include <sys/cpu.h>			/* kvtop() */
     48 #include <sys/systm.h>			/* aprint_xxx() */
     49 
     50 /* wsdisplay(9) */
     51 #include <dev/wscons/wsconsio.h>	/* WSDISPLAYIO_XXX, wsdisplayio_xxx */
     52 #include <dev/wscons/wsdisplayvar.h>	/* wsscreen_xxx, wsdisplay_xxx */
     53 #include <dev/wscons/wsemulvar.h>	/* ? */
     54 #include <dev/wscons/wsemul_vt100var.h>	/* ? */
     55 
     56 /* rasops(9) */
     57 /* #include <dev/wscons/wsdisplayvar.h> */
     58 #include <dev/rasops/rasops.h>		/* rasops_unpack_attr(), rasops_info */
     59 
     60 /* wsfont(9) */
     61 /* #include <dev/wscons/wsconsio.h> */	/* WSDISPLAYIO_XXX, wsdisplayio_xxx */
     62 #include <dev/wsfont/wsfont.h>		/* wsfont_init() */
     63 
     64 /* vcons(9) */
     65 #include <dev/wscons/wsdisplay_vconsvar.h>	/* vcons_xxx(), vcons_data,
     66 						vcons_screen */
     67 /* cons(9) */
     68 #include <dev/cons.h>			/* consdev, CN_INTERNAL */
     69 
     70 /* zz9k and amiga related */
     71 #include <amiga/dev/kbdvar.h>		/* kbd_cnattach() */
     72 #include <amiga/dev/zz9kvar.h>		/* zz9kbus_attach_args */
     73 #include <amiga/dev/zz9kreg.h>		/* ZZ9000 registers */
     74 #include "opt_zz9k_fb.h"		/* ZZFB_CONSOLE */
     75 #include "zz9k_fb.h"			/* NZZ9K_FB */
     76 #include "kbd.h"			/* NKBD */
     77 
     78 
     79 /*
     80  * One can choose different graphics modes and color depths for the different
     81  * wsdisplay modes emul (console), mapped (raw) and dumbfb (e.g. X11) here.
     82  * Please consult zz9kreg.h for available graphics modes and color depths
     83  * supported by the ZZ9000.
     84  */
     85 #define ZZFB_SET_CON_MODE	ZZ9K_MODE_1280x720	/* Console */
     86 #define ZZFB_SET_CON_BPP	ZZ9K_COLOR_8BIT
     87 #define ZZFB_SET_GFX_MODE	ZZ9K_MODE_1280x720	/* raw FB */
     88 #define ZZFB_SET_GFX_BPP	ZZ9K_COLOR_16BIT
     89 #define ZZFB_SET_DFB_MODE	ZZ9K_MODE_1280x720	/* X11 */
     90 #define ZZFB_SET_DFB_BPP	ZZ9K_COLOR_16BIT
     91 
     92 /*
     93  * This defines ZZ9000 scandoubler's capture mode and it is used only in case
     94  * the ZZ9000 does not have the early console, but also in X11 video off mode.
     95  * NetBSD defaults to NTSC amiga screen, so the scandoubler default is NTSC too.
     96  * On a custom configured kernel for a PAL screen, one should consider to change
     97  * the capture mode to PAL to get the best result. If the attached monitor does
     98  * not support a scandoubled PAL signal in 50Hz, consider using the VGA 800x600
     99  * capture mode which is compatible with most monitors and works fine with NTSC
    100  * and PAL captures at 60Hz.
    101  * Valid values for ZZFB_CAP_MODE: 0: NTSC, 1: PAL, 2:VGA (PAL60)
    102  */
    103 #define ZZFB_CAP_MODE 0
    104 
    105 #if   ZZFB_CAP_MODE == 0
    106 #define ZZFB_CAPTURE_MODE	ZZ9K_MODE_720x480
    107 #define ZZFB_DISPLAY_MODE	ZZ9K_MODE_720x480
    108 #elif ZZFB_CAP_MODE == 1
    109 #define ZZFB_CAPTURE_MODE	ZZ9K_MODE_720x576p50
    110 #define ZZFB_DISPLAY_MODE	ZZ9K_MODE_720x576p50
    111 #elif ZZFB_CAP_MODE == 2
    112 #define ZZFB_CAPTURE_MODE	ZZ9K_MODE_800x600
    113 #define ZZFB_DISPLAY_MODE	ZZ9K_MODE_800x600
    114 #endif
    115 
    116 /* The allmighty softc structure */
    117 struct zzfb_softc {
    118 	device_t sc_dev;
    119 	struct bus_space_tag sc_bst;
    120 	bus_space_tag_t sc_iot;
    121 	bus_space_handle_t sc_regh;
    122 	bus_space_handle_t sc_fbh;
    123 	size_t sc_fbsize;
    124 
    125 	struct vcons_screen sc_console_screen;
    126 	struct vcons_data sc_vd;
    127 	struct wsscreen_descr sc_defaultscreen;
    128 	struct wsscreen_list sc_screenlist;
    129 	const struct wsscreen_descr *sc_screens[1];
    130 	u_int sc_wsmode;
    131 
    132 	uint16_t sc_displaymode;
    133 	uint16_t sc_colormode;
    134 	uint16_t sc_width;
    135 	uint16_t sc_height;
    136 	uint16_t sc_bpp;
    137 	uint16_t sc_stride;
    138 
    139 	u_char red[ZZ9K_PALETTE_SIZE];
    140 	u_char green[ZZ9K_PALETTE_SIZE];
    141 	u_char blue[ZZ9K_PALETTE_SIZE];
    142 
    143 	bool sc_isconsole;
    144 	bool sc_isrtg;
    145 };
    146 
    147 static const struct {
    148 	const char* name;
    149 	uint16_t width;
    150 	uint16_t height;
    151 	uint16_t scale;
    152 } zzfb_modes[] = {				/* Hardcoded in firmware */
    153 	{       "1280x720p60", 1280,  720, ZZ9K_MODE_SCALE_0},
    154 	{        "800x600p60",  800,  600, ZZ9K_MODE_SCALE_0},
    155 	{        "640x480p60",  640,  480, ZZ9K_MODE_SCALE_0},
    156 	{       "1024x768p60", 1024,  768, ZZ9K_MODE_SCALE_0},
    157 	{      "1280x1024p60", 1280, 1024, ZZ9K_MODE_SCALE_0},
    158 	{      "1920x1080p60", 1920, 1080, ZZ9K_MODE_SCALE_0},
    159 	{        "720x576p50",  720,  576, ZZ9K_MODE_SCALE_0},	/* 50 Hz */
    160 	{      "1920x1080p50", 1920, 1080, ZZ9K_MODE_SCALE_0},	/* 50 Hz */
    161 	{        "720x480p60",  720,  480, ZZ9K_MODE_SCALE_0},
    162 	{        "640x512p60",  640,  512, ZZ9K_MODE_SCALE_0},
    163 	{      "1600x1200p60", 1600, 1200, ZZ9K_MODE_SCALE_0},
    164 	{      "2560x1444p30", 2560, 1444, ZZ9K_MODE_SCALE_0},	/* 30 Hz */
    165 	{ "720x576p50-NS-PAL",  720,  576, ZZ9K_MODE_SCALE_0},	/* 50 Hz */
    166 	{ "720x480p60-NS-PAL",  720,  480, ZZ9K_MODE_SCALE_0},
    167 	{"720x576p50-NS-NTSC",  720,  576, ZZ9K_MODE_SCALE_0},	/* 50 Hz */
    168 	{"720x480p60-NS-NTSC",  720,  480, ZZ9K_MODE_SCALE_0},
    169 	{        "640x400p60",  640,  400, ZZ9K_MODE_SCALE_0},
    170 	{       "1920x800p60",  640,  400, ZZ9K_MODE_SCALE_0}
    171 };
    172 
    173 static const struct {
    174 	const char* name;
    175 	uint16_t bpp;
    176 	uint16_t mode;
    177 	uint16_t stride;
    178 } zzfb_colors[] = {				/* Hardcoded in firmware */
    179 	{      "8-bit LUT",  8, ZZ9K_MODE_COLOR_8BIT , 1},
    180 	{  "16-bit RGB565", 16, ZZ9K_MODE_COLOR_16BIT, 2},
    181 	{"32-bit BGRA8888", 32, ZZ9K_MODE_COLOR_32BIT, 4},
    182 	{"16-bit ARGB1555", 15, ZZ9K_MODE_COLOR_15BIT, 2}
    183 };
    184 
    185 #define ZZFB_MODES_SIZE		(sizeof zzfb_modes / sizeof zzfb_modes[0])
    186 #define ZZFB_COLORS_SIZE	(sizeof zzfb_colors / sizeof zzfb_colors[0])
    187 
    188 /* functions to set gfx mode, palette and misc stuff to make life easier. */
    189 static void zzfb_set_capture(struct zzfb_softc *sc, uint16_t display_mode,
    190     uint16_t capture_mode);
    191 static void zzfb_set_mode(struct zzfb_softc *sc, uint16_t display_mode,
    192     uint16_t color_mode);
    193 static void zzfb_wait_vblank(struct zzfb_softc *sc);
    194 static void zzfb_init_palette(struct zzfb_softc *sc);
    195 static void zzfb_set_palette(struct zzfb_softc *sc);
    196 static void zzfb_clearbg(struct zzfb_softc *sc, uint8_t color_index);
    197 static int zzfb_get_fbinfo(struct zzfb_softc *sc,
    198     struct wsdisplayio_fbinfo *fbi);
    199 
    200 /* vcons_data init_screen function */
    201 static void zzfb_init_screen(void *cookie, struct vcons_screen *scr,
    202     int existing, long *defattr);
    203 
    204 /* accelerated raster ops functions */
    205 static void zzfb_eraserows(void *cookie, int row, int nrows, long fillattr);
    206 static void zzfb_copyrows(void *cookie, int srcrow, int dstrow, int nrows);
    207 static void zzfb_copycols(void *cookie, int row, int srccol, int dstcol,
    208     int ncols);
    209 static void zzfb_erasecols(void *cookie, int row, int startcol, int ncols,
    210     long fillattr);
    211 
    212 /* blitter support functions*/
    213 static void zzfb_rectfill(struct zzfb_softc *sc, uint16_t x, uint16_t y,
    214     uint16_t w, uint16_t h, uint32_t color);
    215 static void zzfb_bitblt(struct zzfb_softc *sc, uint16_t x, uint16_t y,
    216     uint16_t w, uint16_t h, uint16_t xs, uint16_t ys);
    217 
    218 /* wsdisplay_accessops stuff */
    219 static paddr_t zzfb_mmap(void *v, void *vs, off_t offset, int prot);
    220 static int zzfb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag,
    221     struct lwp *l);
    222 static int zzfb_getcmap(struct zzfb_softc *sc, struct wsdisplay_cmap *cm);
    223 static int zzfb_putcmap(struct zzfb_softc *sc, struct wsdisplay_cmap *cm);
    224 struct wsdisplay_accessops zzfb_accessops =
    225     {zzfb_ioctl, zzfb_mmap, NULL, NULL, NULL, NULL, NULL, NULL};
    226 
    227 /* driver(9) essentials */
    228 static int zzfb_match(device_t parent, cfdata_t match, void *aux);
    229 static void zzfb_attach(device_t parent, device_t self, void *aux);
    230 CFATTACH_DECL_NEW(
    231     zz9k_fb, sizeof(struct zzfb_softc), zzfb_match, zzfb_attach, NULL, NULL );
    232 
    233 #ifdef ZZFB_CONSOLE
    234 extern bool zz9k_exists;
    235 #endif /* ZZFB_CONSOLE */
    236 
    237 
    238 /* If you build it, they will come... */
    239 
    240 static int
    241 zzfb_match(device_t parent, cfdata_t match, void *aux)
    242 {
    243 	struct zz9kbus_attach_args *bap = aux;
    244 
    245 	if (strcmp(bap->zzaa_name, "zz9k_fb") != 0) {
    246 		return 0;
    247 	}
    248 
    249 	return 1;
    250 }
    251 
    252 static void
    253 zzfb_attach(device_t parent, device_t self, void *aux)
    254 {
    255 	struct zz9kbus_attach_args *bap = aux;
    256 	struct zzfb_softc *sc = device_private(self);
    257 	struct zz9k_softc *psc = device_private(parent);
    258 	struct rasops_info *ri;
    259 	struct wsemuldisplaydev_attach_args ws_aa;
    260 	long defattr;
    261 
    262 	sc->sc_dev = self;
    263 	sc->sc_bst.base = bap->zzaa_base;
    264 	sc->sc_bst.absm = &amiga_bus_stride_1;
    265 	sc->sc_iot = &sc->sc_bst;
    266 	sc->sc_regh = psc->sc_regh;
    267 
    268 	if (psc->sc_zsize >= (ZZ9K_FB_BASE + ZZ9K_FB_SIZE)) {
    269 		sc->sc_fbsize = ZZ9K_FB_SIZE;
    270 	} else {
    271 		sc->sc_fbsize = psc->sc_zsize - ZZ9K_FB_BASE;
    272 	}
    273 
    274 	if (bus_space_map(sc->sc_iot, ZZ9K_FB_BASE, sc->sc_fbsize,
    275 	    BUS_SPACE_MAP_LINEAR, &sc->sc_fbh)) {
    276 		aprint_error(": Failed to map MNT ZZ9000 framebuffer.\n");
    277 		return;
    278 	}
    279 
    280 	zzfb_set_mode(sc, ZZFB_SET_CON_MODE, ZZFB_SET_CON_BPP);
    281     	zzfb_init_palette(sc);
    282 	zzfb_clearbg(sc, WS_DEFAULT_BG);
    283 
    284 	aprint_normal(": Framebuffer resolution: %s, "
    285 	    "depth: %i bpp (%s)\n", zzfb_modes[sc->sc_displaymode].name,
    286 	    sc->sc_bpp, zzfb_colors[sc->sc_colormode].name);
    287 
    288 	aprint_debug_dev(sc->sc_dev, "[DEBUG] registers at %p/%p (pa/va), "
    289 	    "framebuffer at %p/%p (pa/va) with %i MB\n",
    290 	    (void *)kvtop((void *)sc->sc_regh),
    291 	    bus_space_vaddr(sc->sc_iot, sc->sc_regh),
    292 	    (void *)kvtop((void *)sc->sc_fbh),
    293 	    bus_space_vaddr(sc->sc_iot, sc->sc_fbh),
    294 	    sc->sc_fbsize / (1024 * 1024));
    295 
    296 	sc->sc_defaultscreen = (struct wsscreen_descr) {"default", 0, 0, NULL,
    297 	    8, 16, WSSCREEN_WSCOLORS | WSSCREEN_HILIT, NULL};
    298 	sc->sc_screens[0] = &sc->sc_defaultscreen;
    299 	sc->sc_screenlist = (struct wsscreen_list){1, sc->sc_screens};
    300 	sc->sc_wsmode = WSDISPLAYIO_MODE_EMUL;
    301 
    302 	vcons_init(&sc->sc_vd, sc, &sc->sc_defaultscreen, &zzfb_accessops);
    303 	sc->sc_vd.init_screen = zzfb_init_screen;
    304 
    305 	ri = &sc->sc_console_screen.scr_ri;
    306 
    307 #ifdef ZZFB_CONSOLE
    308 	sc->sc_isconsole = true;
    309 	vcons_init_screen(&sc->sc_vd, &sc->sc_console_screen, 1,
    310 	    &defattr);
    311 	sc->sc_console_screen.scr_flags = VCONS_SCREEN_IS_STATIC;
    312 	vcons_redraw_screen(&sc->sc_console_screen);
    313 
    314 	sc->sc_defaultscreen.textops = &ri->ri_ops;
    315 	sc->sc_defaultscreen.capabilities = ri->ri_caps;
    316 	sc->sc_defaultscreen.nrows = ri->ri_rows;
    317 	sc->sc_defaultscreen.ncols = ri->ri_cols;
    318 
    319 	wsdisplay_cnattach(&sc->sc_defaultscreen, ri, 0, 0, defattr);
    320 	vcons_replay_msgbuf(&sc->sc_console_screen);
    321 #else
    322 	sc->sc_isconsole = false;
    323 	if (sc->sc_console_screen.scr_ri.ri_rows == 0) {
    324 		vcons_init_screen(&sc->sc_vd, &sc->sc_console_screen, 1,
    325 		    &defattr);
    326 	} else {
    327 		(*ri->ri_ops.allocattr)(ri, 0, 0, 0, &defattr);
    328 	}
    329 	zzfb_set_capture(sc, ZZFB_DISPLAY_MODE, ZZFB_CAPTURE_MODE);
    330 	aprint_normal_dev (sc->sc_dev, "Scandoubler capture: %s, "
    331 	    "display: %s in %s.\n",
    332 	    zzfb_modes[ZZFB_CAPTURE_MODE].name,
    333 	    zzfb_modes[ZZFB_DISPLAY_MODE].name,
    334 	    zzfb_colors[ZZ9K_COLOR_32BIT].name);
    335 #endif /* ZZFB_CONSOLE */
    336 
    337 	ws_aa.console = sc->sc_isconsole;
    338 	ws_aa.scrdata = &sc->sc_screenlist;
    339 	ws_aa.accessops = &zzfb_accessops;
    340 	ws_aa.accesscookie = &sc->sc_vd;
    341 
    342 	config_found(sc->sc_dev, &ws_aa, wsemuldisplaydevprint, CFARGS_NONE);
    343 }
    344 
    345 static void
    346 zzfb_init_palette(struct zzfb_softc *sc)
    347 {
    348 	for (int index = 0; index < ZZ9K_PALETTE_SIZE; index++) {
    349 		sc->red[index] = rasops_cmap[index * 3 + 0];
    350 		sc->green[index] = rasops_cmap[index * 3 + 1];
    351 		sc->blue[index] = rasops_cmap[index * 3 + 2];
    352 	}
    353 
    354 	zzfb_set_palette(sc);
    355 }
    356 
    357 static void
    358 zzfb_set_palette(struct zzfb_softc *sc)
    359 {
    360 	uint32_t palette;
    361 	uint8_t rVal;
    362 	uint8_t gVal;
    363 	uint8_t bVal;
    364 
    365 	for (int index = 0; index < ZZ9K_PALETTE_SIZE; index++) {
    366 		rVal = sc->red[index];
    367 		gVal = sc->green[index];
    368 		bVal = sc->blue[index];
    369 		palette = ((index << 24) | (rVal << 16) | (gVal << 8) | bVal);
    370 		ZZREG_W(ZZ9K_VIDEO_CTRL_DATA_HI, palette >> 16);
    371 		ZZREG_W(ZZ9K_VIDEO_CTRL_DATA_LO, palette & 0xFFFF);
    372 		ZZREG_W(ZZ9K_VIDEO_CTRL_OP, ZZ9K_OP_PALETTE);
    373 		ZZREG_W(ZZ9K_VIDEO_CTRL_OP, ZZ9K_OP_NOP);
    374 	}
    375 }
    376 
    377 static void
    378 zzfb_init_screen(void *cookie, struct vcons_screen *scr, int existing,
    379     long *defattr)
    380 {
    381 	struct zzfb_softc *sc = cookie;
    382 	struct rasops_info *ri = &scr->scr_ri;
    383 
    384 	scr->scr_flags = VCONS_SCREEN_IS_STATIC;
    385 
    386 	wsfont_init();
    387 
    388 	ri->ri_bits = (u_char *)bus_space_vaddr(sc->sc_iot, sc->sc_fbh);
    389 	ri->ri_depth = sc->sc_bpp;
    390 	ri->ri_width = sc->sc_width;
    391 	ri->ri_height = sc->sc_height;
    392 	ri->ri_stride = sc->sc_stride;
    393 	ri->ri_flg = 0;
    394 
    395 	if (ri->ri_depth == 32) {	/* adjust for BGRA8888 */
    396 		ri->ri_rnum = 8;	/* for other depths default is OK */
    397 		ri->ri_gnum = 8;
    398 		ri->ri_bnum = 8;
    399 		ri->ri_rpos = 8;	/* skip over alpha channel */
    400 		ri->ri_gpos = 8 + ri->ri_rnum;
    401 		ri->ri_bpos = 8 + ri->ri_rnum + ri->ri_gnum;
    402 	}
    403 
    404 	rasops_init(ri, 0, 0);
    405 	ri->ri_caps = WSSCREEN_WSCOLORS;
    406 	rasops_reconfig(ri, ri->ri_height / ri->ri_font->fontheight,
    407 	    ri->ri_width / ri->ri_font->fontwidth);
    408 	ri->ri_hw = scr;
    409 
    410 	ri->ri_ops.eraserows = zzfb_eraserows;
    411 	ri->ri_ops.copyrows = zzfb_copyrows;
    412 	ri->ri_ops.erasecols = zzfb_erasecols;
    413 	ri->ri_ops.copycols = zzfb_copycols;
    414 }
    415 
    416 static void
    417 zzfb_set_capture(struct zzfb_softc *sc, uint16_t display_mode,
    418     uint16_t capture_mode)
    419 {
    420 	uint16_t panPtrHi;
    421 	uint16_t panPtrLo;
    422 	uint16_t new_mode;
    423 
    424 	switch (display_mode) {
    425 	case ZZ9K_MODE_720x480:		/* NTSC */
    426 		panPtrHi = ZZ9K_CAPTURE_PAN_NTSC >> 16;
    427 		panPtrLo = ZZ9K_CAPTURE_PAN_NTSC & 0xFFFF;
    428 		break;
    429 	case ZZ9K_MODE_720x576p50:	/* PAL */
    430 		panPtrHi = ZZ9K_CAPTURE_PAN_PAL >> 16;
    431 		panPtrLo = ZZ9K_CAPTURE_PAN_PAL & 0xFFFF;
    432 		break;
    433 	case ZZ9K_MODE_800x600:		/* VGA */
    434 		panPtrHi = ZZ9K_CAPTURE_PAN_VGA >> 16;
    435 		panPtrLo = ZZ9K_CAPTURE_PAN_VGA & 0xFFFF;
    436 		break;
    437 	default:
    438 		aprint_error_dev(sc->sc_dev, "Unsupported scandoubler "
    439 		    "capture and display mode combination.\n");
    440 		return;
    441 	}
    442 	new_mode = ZZ9K_MODE_SCALE_2 | ZZ9K_MODE_COLOR_32BIT | display_mode;
    443 	zzfb_wait_vblank(sc);
    444 	ZZREG_W(ZZ9K_VIDEOCAP_VMODE, capture_mode);
    445 	ZZREG_W(ZZ9K_BLITTER_USER1, ZZ9K_FEATURE_NONSTANDARD_VSYNC);
    446 	ZZREG_W(ZZ9K_SET_FEATURE, 0x0000);
    447 	ZZREG_W(ZZ9K_VIDEO_CAPTURE_MODE, ZZ9K_CAPTURE_ON);
    448 	ZZREG_W(ZZ9K_BLITTER_X1, 0x0000);
    449 	ZZREG_W(ZZ9K_BLITTER_Y1, 0x0000);
    450 	ZZREG_W(ZZ9K_BLITTER_X2, 0x0000);
    451 	ZZREG_W(ZZ9K_BLITTER_COLORMODE, ZZ9K_COLOR_32BIT);
    452 	ZZREG_W(ZZ9K_PAN_PTR_HI, panPtrHi);
    453 	ZZREG_W(ZZ9K_PAN_PTR_LO, panPtrLo);
    454 	ZZREG_W(ZZ9K_MODE, new_mode);
    455 	sc->sc_isrtg = false;
    456 }
    457 
    458 void
    459 zzfb_set_mode(struct zzfb_softc *sc, uint16_t display_mode,
    460     uint16_t color_mode)
    461 {
    462 	uint16_t new_mode;
    463 
    464 	if ((display_mode < 0) || (display_mode >= ZZFB_MODES_SIZE))
    465 		display_mode = ZZ9K_MODE_1280x720;
    466 
    467 	sc->sc_width	= zzfb_modes[display_mode].width;
    468 	sc->sc_height	= zzfb_modes[display_mode].height;
    469 	new_mode	= zzfb_modes[display_mode].scale;
    470 
    471 	if ((color_mode < 0) || (color_mode >= ZZFB_COLORS_SIZE))
    472 		color_mode = ZZ9K_COLOR_8BIT;
    473 
    474 	if ((color_mode == ZZ9K_COLOR_32BIT) && (sc->sc_width > 1920))
    475 		color_mode = ZZ9K_COLOR_16BIT;
    476 
    477 	sc->sc_bpp    = zzfb_colors[color_mode].bpp;
    478 	sc->sc_stride = sc->sc_width * zzfb_colors[color_mode].stride;
    479 	new_mode      = new_mode | zzfb_colors[color_mode].mode | display_mode;
    480 
    481 	sc->sc_displaymode = display_mode;
    482 	sc->sc_colormode   = color_mode;
    483 
    484 	zzfb_wait_vblank(sc);
    485 	ZZREG_W(ZZ9K_VIDEO_CAPTURE_MODE, ZZ9K_CAPTURE_OFF);
    486 	ZZREG_W(ZZ9K_BLITTER_X1, 0x0000);
    487 	ZZREG_W(ZZ9K_BLITTER_Y1, 0x0000);
    488 	ZZREG_W(ZZ9K_BLITTER_X2, 0x0000);
    489 	ZZREG_W(ZZ9K_BLITTER_COLORMODE, color_mode);
    490 	ZZREG_W(ZZ9K_BLITTER_SRC_HI, 0x0000);
    491 	ZZREG_W(ZZ9K_BLITTER_SRC_LO, 0x0000);
    492 	ZZREG_W(ZZ9K_BLITTER_DST_HI, 0x0000);
    493 	ZZREG_W(ZZ9K_BLITTER_DST_LO, 0x0000);
    494 	ZZREG_W(ZZ9K_BLITTER_SPLIT_POS, 0x0000);
    495 	ZZREG_W(ZZ9K_PAN_PTR_HI, 0x0000);
    496 	ZZREG_W(ZZ9K_PAN_PTR_LO, 0x0000);
    497 	ZZREG_W(ZZ9K_MODE, new_mode);
    498 	sc->sc_isrtg = true;
    499 }
    500 
    501 static void
    502 zzfb_wait_vblank(struct zzfb_softc *sc)
    503 {
    504 	uint16_t vb_status = ZZREG_R(ZZ9K_VIDEO_BLANK_STATUS);
    505 	while (vb_status != 0)
    506 		vb_status = ZZREG_R(ZZ9K_VIDEO_BLANK_STATUS);
    507 	while (vb_status == 0)
    508 		vb_status = ZZREG_R(ZZ9K_VIDEO_BLANK_STATUS);
    509 }
    510 
    511 static void
    512 zzfb_clearbg(struct zzfb_softc *sc, uint8_t color_index)
    513 {
    514 	if (color_index >= ZZ9K_PALETTE_SIZE)
    515 		color_index = 0;
    516 
    517 	uint32_t palette = 0;
    518 	uint8_t rVal = rasops_cmap[color_index * 3 + 0];
    519 	uint8_t gVal = rasops_cmap[color_index * 3 + 1];
    520 	uint8_t bVal = rasops_cmap[color_index * 3 + 2];
    521 
    522 	switch (sc->sc_colormode) {
    523 	case ZZ9K_COLOR_32BIT:	/* BGRA8888 */
    524 		palette = ((bVal << 24) | (gVal << 16) | (rVal << 8) | 0xFF);
    525 		break;
    526 	case ZZ9K_COLOR_16BIT: /* RGB565 at high word, don't ask why. */
    527 		palette = (((rVal & 0xF8) << 8) |
    528 			   ((gVal & 0xFC) << 3) |
    529 			   ((bVal >> 3) & 0x1F) ) << 16;
    530 		break;
    531 	case ZZ9K_COLOR_15BIT: /* ARGB1555 at high word, don't ask why. */
    532 		palette = ((0x8000) |
    533 			   ((rVal & 0xF8) << 7) |
    534 			   ((gVal & 0xF8) << 2) |
    535 			   ((bVal >> 3) & 0x1F) ) << 16;
    536 		break;
    537 	case ZZ9K_COLOR_8BIT: /* 256 LUT */
    538 	default:
    539 		palette = color_index;
    540 		break;
    541 	}
    542 
    543 	zzfb_rectfill(sc, 0, 0, sc->sc_width, sc->sc_height, palette);
    544 }
    545 
    546 static void
    547 zzfb_rectfill(struct zzfb_softc *sc, uint16_t x, uint16_t y,
    548     uint16_t w, uint16_t h, uint32_t color)
    549 {
    550 	ZZREG_W(ZZ9K_BLITTER_X1, x);
    551 	ZZREG_W(ZZ9K_BLITTER_Y1, y);
    552 	ZZREG_W(ZZ9K_BLITTER_X2, w);
    553 	ZZREG_W(ZZ9K_BLITTER_Y2, h);
    554 	ZZREG_W(ZZ9K_BLITTER_ROW_PITCH, sc->sc_stride >> 2);
    555 	ZZREG_W(ZZ9K_BLITTER_COLORMODE, sc->sc_colormode);
    556 	ZZREG_W(ZZ9K_BLITTER_RGB_HI, color >> 16);
    557 	ZZREG_W(ZZ9K_BLITTER_RGB_LO, color & 0xFFFF);
    558 	ZZREG_W(ZZ9K_BLITTER_OP_FILLRECT, 0x00FF);
    559 }
    560 
    561 static void
    562 zzfb_bitblt(struct zzfb_softc *sc, uint16_t x, uint16_t y, uint16_t w,
    563     uint16_t h, uint16_t xs, uint16_t ys)
    564 {
    565 	ZZREG_W(ZZ9K_BLITTER_X1, x);
    566 	ZZREG_W(ZZ9K_BLITTER_Y1, y);
    567 	ZZREG_W(ZZ9K_BLITTER_X2, w);
    568 	ZZREG_W(ZZ9K_BLITTER_Y2, h);
    569 	ZZREG_W(ZZ9K_BLITTER_X3, xs);
    570 	ZZREG_W(ZZ9K_BLITTER_Y3, ys);
    571 	ZZREG_W(ZZ9K_BLITTER_ROW_PITCH, sc->sc_stride >> 2);
    572 	ZZREG_W(ZZ9K_BLITTER_COLORMODE, (0xFF << 8) | sc->sc_colormode);
    573 	ZZREG_W(ZZ9K_BLITTER_OP_COPYRECT, ZZ9K_OPT_REGULAR);
    574 }
    575 
    576 static void
    577 zzfb_copyrows(void *cookie, int srcrow, int dstrow, int nrows)
    578 {
    579 	struct rasops_info *ri = cookie;
    580 	struct vcons_screen *scr = ri->ri_hw;
    581 	struct zzfb_softc *sc = scr->scr_cookie;
    582 	int x, y, w, h, ys;
    583 
    584 	if (sc->sc_wsmode == WSDISPLAYIO_MODE_EMUL) {
    585 		x = ri->ri_xorigin;
    586 		ys = ri->ri_yorigin + ri->ri_font->fontheight * srcrow;
    587 		y = ri->ri_yorigin + ri->ri_font->fontheight * dstrow;
    588 		w = ri->ri_emuwidth;
    589 		h = ri->ri_font->fontheight * nrows;
    590 		zzfb_bitblt(sc, x, y, w, h, x, ys);
    591 	}
    592 }
    593 
    594 static void
    595 zzfb_eraserows(void *cookie, int row, int nrows, long fillattr)
    596 {
    597 	struct rasops_info *ri = cookie;
    598 	struct vcons_screen *scr = ri->ri_hw;
    599 	struct zzfb_softc *sc = scr->scr_cookie;
    600 	int x, y, w, h, fg, bg, ul;
    601 
    602 	if (sc->sc_wsmode == WSDISPLAYIO_MODE_EMUL) {
    603 		x = ri->ri_xorigin;
    604 		y = ri->ri_yorigin + ri->ri_font->fontheight * row;
    605 		w = ri->ri_emuwidth;
    606 		h = ri->ri_font->fontheight * nrows;
    607 		rasops_unpack_attr(fillattr, &fg, &bg, &ul);
    608 		zzfb_rectfill(sc, x, y, w, h, ri->ri_devcmap[bg]);
    609 	}
    610 }
    611 
    612 static void
    613 zzfb_copycols(void *cookie, int row, int srccol, int dstcol, int ncols)
    614 {
    615 	struct rasops_info *ri = cookie;
    616 	struct vcons_screen *scr = ri->ri_hw;
    617 	struct zzfb_softc *sc = scr->scr_cookie;
    618 	int x, y, w, h, xs;
    619 
    620 	if (sc->sc_wsmode == WSDISPLAYIO_MODE_EMUL) {
    621 		xs = ri->ri_xorigin + ri->ri_font->fontwidth * srccol;
    622 		x = ri->ri_xorigin + ri->ri_font->fontwidth * dstcol;
    623 		y = ri->ri_yorigin + ri->ri_font->fontheight * row;
    624 		w = ri->ri_font->fontwidth * ncols;
    625 		h = ri->ri_font->fontheight;
    626 		zzfb_bitblt(sc, x, y, w, h, xs, y);
    627 	}
    628 }
    629 
    630 static void
    631 zzfb_erasecols(void *cookie, int row, int startcol, int ncols, long fillattr)
    632 {
    633 	struct rasops_info *ri = cookie;
    634 	struct vcons_screen *scr = ri->ri_hw;
    635 	struct zzfb_softc *sc = scr->scr_cookie;
    636 	int x, y, w, h, fg, bg, ul;
    637 
    638 	if (sc->sc_wsmode == WSDISPLAYIO_MODE_EMUL) {
    639 		x = ri->ri_xorigin + ri->ri_font->fontwidth * startcol;
    640 		y = ri->ri_yorigin + ri->ri_font->fontheight * row;
    641 		w = ri->ri_font->fontwidth * ncols;
    642 		h = ri->ri_font->fontheight;
    643 		rasops_unpack_attr(fillattr, &fg, &bg, &ul);
    644 		zzfb_rectfill(sc, x, y, w, h, ri->ri_devcmap[bg & 0xf]);
    645 	}
    646 }
    647 
    648 static int
    649 zzfb_ioctl(void *v, void *vs, u_long cmd, void *data, int flag, struct lwp *l)
    650 {
    651 	int retval = 0;
    652 	u_int new_wsmode;
    653 	struct vcons_data *vd = v;
    654 	struct zzfb_softc *sc = vd->cookie;
    655 	struct vcons_screen *scr = vd->active;
    656 	struct wsdisplayio_bus_id *busid;
    657 
    658 	switch (cmd) {
    659 	case WSDISPLAYIO_GTYPE:
    660 		*(u_int *)data = WSDISPLAY_TYPE_UNKNOWN;
    661 		break;
    662 	case WSDISPLAYIO_GET_FBINFO:
    663 		retval = zzfb_get_fbinfo(sc, (struct wsdisplayio_fbinfo *)data);
    664 		break;
    665 	case WSDISPLAYIO_GINFO:
    666 		((struct wsdisplay_fbinfo *)data)->width  = sc->sc_width;
    667 		((struct wsdisplay_fbinfo *)data)->height = sc->sc_height;
    668 		((struct wsdisplay_fbinfo *)data)->depth  = sc->sc_bpp;
    669 		((struct wsdisplay_fbinfo *)data)->cmsize = ZZ9K_PALETTE_SIZE;
    670 		break;
    671 	case WSDISPLAYIO_GETCMAP:
    672 		retval = zzfb_getcmap(sc, (struct wsdisplay_cmap *)data);
    673 		break;
    674 	case WSDISPLAYIO_PUTCMAP:
    675 		retval = zzfb_putcmap(sc, (struct wsdisplay_cmap *)data);
    676 		break;
    677 	case WSDISPLAYIO_GVIDEO:
    678 		*(int *)data = (sc->sc_isrtg == true) ?
    679 		    WSDISPLAYIO_VIDEO_ON : WSDISPLAYIO_VIDEO_OFF;
    680 		break;
    681 	case WSDISPLAYIO_SVIDEO:
    682 		if (*(int *)data == WSDISPLAYIO_VIDEO_ON) {
    683 			zzfb_set_mode(sc, sc->sc_displaymode, sc->sc_colormode);
    684 		} else {
    685 			zzfb_set_capture(sc, ZZFB_DISPLAY_MODE,
    686 			    ZZFB_CAPTURE_MODE);
    687 		}
    688 		break;
    689 	case WSDISPLAYIO_GET_BUSID:
    690 		busid = data;
    691 		busid->bus_type = WSDISPLAYIO_BUS_SOC;
    692 		break;
    693 	case WSDISPLAYIO_GCURPOS:
    694 	case WSDISPLAYIO_SCURPOS:
    695 	case WSDISPLAYIO_GCURMAX:
    696 	case WSDISPLAYIO_GCURSOR:
    697 	case WSDISPLAYIO_SCURSOR:
    698 		retval = EPASSTHROUGH;
    699 		break;
    700 	case WSDISPLAYIO_GMODE:
    701 		*(u_int *)data = sc->sc_wsmode;
    702 		break;
    703 	case WSDISPLAYIO_SMODE:
    704 		new_wsmode = *(u_int *)data;
    705 		if (new_wsmode != sc->sc_wsmode) {
    706 			switch (new_wsmode) {
    707 			case WSDISPLAYIO_MODE_EMUL:
    708 				zzfb_set_mode(sc,
    709 				    ZZFB_SET_CON_MODE, ZZFB_SET_CON_BPP);
    710 				zzfb_init_palette(sc);
    711 				zzfb_clearbg(sc, WS_DEFAULT_BG);
    712 				vcons_redraw_screen(scr);
    713 				sc->sc_wsmode = new_wsmode;
    714 				break;
    715 			case WSDISPLAYIO_MODE_MAPPED:
    716 				zzfb_set_mode(sc,
    717 				    ZZFB_SET_GFX_MODE, ZZFB_SET_GFX_BPP);
    718 				    zzfb_clearbg(sc, WSCOL_BLACK);
    719 				sc->sc_wsmode = new_wsmode;
    720 				break;
    721 			case WSDISPLAYIO_MODE_DUMBFB:
    722 				zzfb_set_mode(sc,
    723 				    ZZFB_SET_DFB_MODE, ZZFB_SET_DFB_BPP);
    724 				    zzfb_clearbg(sc, WSCOL_BLACK);
    725 				sc->sc_wsmode = new_wsmode;
    726 				break;
    727 			default:
    728 				retval = EINVAL;
    729 				break;
    730 			}
    731 		} else {
    732 			retval = EINVAL;
    733 		}
    734 		break;
    735 	case WSDISPLAYIO_LINEBYTES:
    736 		*(u_int *)data = sc->sc_stride;
    737 		break;
    738 	case WSDISPLAYIO_GMSGATTRS:
    739 	case WSDISPLAYIO_SMSGATTRS:
    740 	case WSDISPLAYIO_GBORDER:
    741 	case WSDISPLAYIO_SBORDER:
    742 	case WSDISPLAYIO_GETWSCHAR:
    743 	case WSDISPLAYIO_PUTWSCHAR:
    744 	case WSDISPLAYIO_SSPLASH:
    745 	case WSDISPLAYIO_GET_EDID:
    746 	case WSDISPLAYIO_SETVERSION:
    747 	default:
    748 		retval = EPASSTHROUGH;
    749 		break;
    750 	}
    751 
    752 	return retval;
    753 }
    754 
    755 static paddr_t
    756 zzfb_mmap(void *v, void *vs, off_t offset, int prot)
    757 {
    758 	struct vcons_data *vd = v;
    759 	struct zzfb_softc *sc = vd->cookie;
    760 
    761 	if ( (offset >= 0) && (offset < sc->sc_fbsize) ) {
    762 		return bus_space_mmap( sc->sc_iot,
    763 		    (bus_addr_t)kvtop((void *)sc->sc_fbh), offset, prot,
    764 		    BUS_SPACE_MAP_LINEAR);
    765 	} else {
    766 		return -1;
    767 	}
    768 }
    769 
    770 static int
    771 zzfb_get_fbinfo(struct zzfb_softc *sc, struct wsdisplayio_fbinfo *fbi)
    772 {
    773 	uint32_t bpA, bpR, bpG, bpB;
    774 
    775 	switch (sc->sc_bpp) {
    776 	case 8:
    777 		bpA = 0; bpR = 0; bpG = 0; bpB = 0;
    778 		break;
    779 	case 15:
    780 		bpA = 1; bpR = 5; bpG = 5; bpB = 5;
    781 		break;
    782 	case 16:
    783 		bpA = 0; bpR = 5; bpG = 6; bpB = 5;
    784 		break;
    785 	case 32:
    786 		bpA = 8; bpR = 8; bpG = 8; bpB = 8;
    787 		break;
    788 	default:
    789 		return EINVAL;
    790 	}
    791 
    792 	fbi->fbi_flags		= 0;
    793 	fbi->fbi_fboffset	= 0;
    794 	fbi->fbi_fbsize		= sc->sc_stride * sc->sc_height;
    795 	fbi->fbi_width 		= sc->sc_width;
    796 	fbi->fbi_height		= sc->sc_height;
    797 	fbi->fbi_stride		= sc->sc_stride;
    798 	fbi->fbi_bitsperpixel	= (sc->sc_bpp == 15) ? 16 : sc->sc_bpp;
    799 
    800 	switch (sc->sc_bpp) {
    801 	case 8:
    802 		fbi->fbi_pixeltype			    = WSFB_CI;
    803 		fbi->fbi_subtype.fbi_cmapinfo.cmap_entries  = ZZ9K_PALETTE_SIZE;
    804 		return 0;
    805 	case 15: /* ZZ9000 uses ARGB1555 format for 15 bpp */
    806 	case 16: /* ZZ9000 uses RGB565 format for 16 bpp */
    807 		fbi->fbi_subtype.fbi_rgbmasks.alpha_offset	= bpB+bpG+bpR;
    808 		fbi->fbi_subtype.fbi_rgbmasks.red_offset	= bpB+bpG;
    809 		fbi->fbi_subtype.fbi_rgbmasks.green_offset	= bpB;
    810 		fbi->fbi_subtype.fbi_rgbmasks.blue_offset	= 0;
    811 		break;
    812 	case 32: /* ZZ9000 uses BGRA8888 format for 32 bpp */
    813 		fbi->fbi_subtype.fbi_rgbmasks.alpha_offset	= 0;
    814 		fbi->fbi_subtype.fbi_rgbmasks.red_offset	= bpA;
    815 		fbi->fbi_subtype.fbi_rgbmasks.green_offset	= bpA+bpR;
    816 		fbi->fbi_subtype.fbi_rgbmasks.blue_offset	= bpA+bpR+bpG;
    817 		break;
    818 	default:
    819 		return EINVAL;
    820 	}
    821 
    822 	fbi->fbi_pixeltype				= WSFB_RGB;
    823 	fbi->fbi_subtype.fbi_rgbmasks.alpha_size	= bpA;
    824 	fbi->fbi_subtype.fbi_rgbmasks.red_size		= bpR;
    825 	fbi->fbi_subtype.fbi_rgbmasks.green_size	= bpG;
    826 	fbi->fbi_subtype.fbi_rgbmasks.blue_size		= bpB;
    827 
    828 	return 0;
    829 }
    830 
    831 static int
    832 zzfb_getcmap(struct zzfb_softc *sc, struct wsdisplay_cmap *cm)
    833 {
    834 	int retval = 0;
    835 	u_int index = cm->index;
    836 	u_int count = cm->count;
    837 
    838 	if (index >= ZZ9K_PALETTE_SIZE || index + count > ZZ9K_PALETTE_SIZE)
    839 		return EINVAL;
    840 
    841 	retval = copyout(&sc->red[index], cm->red, count);
    842 	if (retval != 0)
    843 		return retval;
    844 
    845 	retval = copyout(&sc->green[index], cm->green, count);
    846 	if (retval != 0)
    847 		return retval;
    848 
    849 	retval = copyout(&sc->blue[index], cm->blue, count);
    850 	if (retval != 0)
    851 		return retval;
    852 
    853 	return retval;
    854 }
    855 
    856 static int
    857 zzfb_putcmap(struct zzfb_softc *sc, struct wsdisplay_cmap *cm)
    858 {
    859 	int retval = 0;
    860 	u_int index = cm->index;
    861 	u_int count = cm->count;
    862 
    863 	if (index >= ZZ9K_PALETTE_SIZE || index + count > ZZ9K_PALETTE_SIZE)
    864 		return EINVAL;
    865 
    866 	retval = copyin(cm->red, &sc->red[index], count);
    867 	if (retval != 0)
    868 		return retval;
    869 
    870 	retval = copyin(cm->green, &sc->green[index], count);
    871 	if (retval != 0)
    872 		return retval;
    873 
    874 	retval = copyin(cm->blue, &sc->blue[index], count);
    875 	if (retval != 0)
    876 		return retval;
    877 
    878 	zzfb_set_palette(sc);
    879 	return retval;
    880 }
    881 
    882 /*
    883  * Early console handling, associated with amiga/conf.c file which holds a
    884  * table of all console devices. The below functions ensures that ZZ9000 becomes
    885  * wsdisplay0 and wskbd0 gets attached to it.
    886  */
    887 
    888 /* early console handling */
    889 void zzfb_cnprobe(struct consdev *cd);
    890 void zzfb_cninit(struct consdev *cd);
    891 void zzfb_cnpollc(dev_t cd, int on);
    892 void zzfb_cnputc(dev_t cd, int ch);
    893 int zzfb_cngetc(dev_t cd);
    894 
    895 void
    896 zzfb_cnprobe(struct consdev *cd)
    897 {
    898 #ifdef ZZFB_CONSOLE
    899 	if (zz9k_exists == true) {
    900 		cd->cn_pri = CN_INTERNAL;
    901 	} else {
    902 		cd->cn_pri = CN_DEAD;
    903 	}
    904 	cd->cn_dev = NODEV;
    905 #endif /* ZZFB_CONSOLE */
    906 }
    907 
    908 void
    909 zzfb_cninit(struct consdev *cd)
    910 {
    911 #if defined ZZFB_CONSOLE && NKBD > 0
    912 	/* tell kbd device it is used as console keyboard */
    913 	if (zz9k_exists == true)
    914 		kbd_cnattach();
    915 #endif /* ZZFB_CONSOLE && NKBD > 0 */
    916 }
    917 
    918 void
    919 zzfb_cnpollc(dev_t cd, int on)
    920 {
    921 }
    922 
    923 void
    924 zzfb_cnputc(dev_t cd, int ch)
    925 {
    926 }
    927 
    928 int
    929 zzfb_cngetc(dev_t cd)
    930 {
    931 	return 0;
    932 }
    933