Home | History | Annotate | Line # | Download | only in pci
lynxfb.c revision 1.1
      1 /*	$NetBSD: lynxfb.c,v 1.1 2012/03/02 13:20:57 nonaka Exp $	*/
      2 /*	$OpenBSD: smfb.c,v 1.13 2011/07/21 20:36:12 miod Exp $	*/
      3 
      4 /*
      5  * Copyright (c) 2012 NONAKA Kimihiro <nonaka (at) netbsd.org>
      6  * Copyright (c) 2009, 2010 Miodrag Vallat.
      7  *
      8  * Permission to use, copy, modify, and distribute this software for any
      9  * purpose with or without fee is hereby granted, provided that the above
     10  * copyright notice and this permission notice appear in all copies.
     11  *
     12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     13  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     14  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     15  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     16  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     17  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     18  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     19  */
     20 
     21 /*
     22  * SiliconMotion SM712 frame buffer driver.
     23  *
     24  * Assumes its video output is an LCD panel, in 5:6:5 mode, and fixed
     25  * 1024x600 resolution, depending on the system model.
     26  */
     27 
     28 #include <sys/cdefs.h>
     29 __KERNEL_RCSID(0, "$NetBSD: lynxfb.c,v 1.1 2012/03/02 13:20:57 nonaka Exp $");
     30 
     31 #include "opt_wsemul.h"
     32 
     33 #include <sys/param.h>
     34 #include <sys/systm.h>
     35 #include <sys/device.h>
     36 #include <sys/endian.h>
     37 
     38 #include <sys/bus.h>
     39 
     40 #include <dev/ic/vgareg.h>
     41 #include <dev/isa/isareg.h>
     42 #include <dev/pci/pcireg.h>
     43 #include <dev/pci/pcivar.h>
     44 #include <dev/pci/pcidevs.h>
     45 #include <dev/pci/pciio.h>
     46 
     47 #include <dev/wscons/wsconsio.h>
     48 #include <dev/wscons/wsdisplayvar.h>
     49 #include <dev/rasops/rasops.h>
     50 #include <dev/wscons/wsdisplay_vconsvar.h>
     51 #include <dev/pci/wsdisplay_pci.h>
     52 
     53 #include <dev/pci/lynxfbreg.h>
     54 #include <dev/pci/lynxfbvar.h>
     55 
     56 #ifndef	LYNXFB_DEFAULT_WIDTH
     57 #define	LYNXFB_DEFAULT_WIDTH	1024
     58 #endif
     59 #ifndef	LYNXFB_DEFAULT_HEIGHT
     60 #define	LYNXFB_DEFAULT_HEIGHT	600
     61 #endif
     62 #ifndef	LYNXFB_DEFAULT_DEPTH
     63 #define	LYNXFB_DEFAULT_DEPTH	16
     64 #endif
     65 #ifndef	LYNXFB_DEFAULT_STRIDE
     66 #define	LYNXFB_DEFAULT_STRIDE	0
     67 #endif
     68 #ifndef	LYNXFB_DEFAULT_FLAGS
     69 #ifdef __MIPSEL__
     70 #define	LYNXFB_DEFAULT_FLAGS	LYNXFB_FLAG_SWAPBR
     71 #else
     72 #define	LYNXFB_DEFAULT_FLAGS	0
     73 #endif
     74 #endif
     75 
     76 struct lynxfb_softc;
     77 
     78 /* minimal frame buffer information, suitable for early console */
     79 struct lynxfb {
     80 	struct lynxfb_softc	*sc;
     81 	void			*fbaddr;
     82 
     83 	bus_space_tag_t		memt;
     84 	bus_space_handle_t	memh;
     85 
     86 	/* DPR registers */
     87 	bus_space_tag_t		dprt;
     88 	bus_space_handle_t	dprh;
     89 	/* MMIO space */
     90 	bus_space_tag_t		mmiot;
     91 	bus_space_handle_t	mmioh;
     92 
     93 	struct vcons_screen	vcs;
     94 	struct wsscreen_descr	wsd;
     95 
     96 	int			width, height, depth, stride;
     97 	int			accel;
     98 	int			blank;
     99 
    100 	/* LYNXFB_FLAG_* */
    101 	int			flags;
    102 #define	LYNXFB_FLAG_SWAPBR	(1 << 0)
    103 };
    104 
    105 #define	DPR_READ(fb, reg) \
    106 	bus_space_read_4((fb)->memt, (fb)->dprh, (reg))
    107 #define	DPR_WRITE(fb, reg, val) \
    108 	bus_space_write_4((fb)->memt, (fb)->dprh, (reg), (val))
    109 
    110 struct lynxfb_softc {
    111 	device_t		sc_dev;
    112 	bus_space_tag_t		sc_memt;
    113 	bus_space_handle_t	sc_memh;
    114 
    115 	pci_chipset_tag_t	sc_pc;
    116 	pcitag_t		sc_pcitag;
    117 
    118 	struct lynxfb		*sc_fb;
    119 	struct lynxfb		sc_fb_store;
    120 
    121 	struct vcons_data	sc_vd;
    122 	struct wsscreen_list	sc_screenlist;
    123 	const struct wsscreen_descr *sc_screens[1];
    124 	int			sc_mode;
    125 };
    126 
    127 static int	lynxfb_match_by_id(pcireg_t);
    128 static int	lynxfb_match(device_t, cfdata_t, void *);
    129 static void	lynxfb_attach(device_t, device_t, void *);
    130 
    131 CFATTACH_DECL_NEW(lynxfb, sizeof(struct lynxfb_softc),
    132     lynxfb_match, lynxfb_attach, NULL, NULL);
    133 
    134 static int	lynxfb_ioctl(void *, void *, u_long, void *, int, struct lwp *);
    135 static paddr_t	lynxfb_mmap(void *, void *, off_t, int);
    136 
    137 static struct wsdisplay_accessops lynxfb_accessops = {
    138 	lynxfb_ioctl,
    139 	lynxfb_mmap,
    140 	NULL,	/* alloc_screen */
    141 	NULL,	/* free_screen */
    142 	NULL,	/* show_screen */
    143 	NULL,	/* load_font */
    144 	NULL,	/* pollc */
    145 	NULL,	/* scroll */
    146 };
    147 
    148 static bool	lynxfb_is_console(struct lynxfb_softc *, pcitag_t);
    149 static void	lynxfb_init_screen(void *, struct vcons_screen *, int, long *);
    150 static int	lynxfb_setup(struct lynxfb *);
    151 
    152 static int	lynxfb_wait(struct lynxfb *);
    153 static void	lynxfb_copyrect(struct lynxfb *, int, int, int, int, int, int);
    154 static void	lynxfb_fillrect(struct lynxfb *, int, int, int, int, int);
    155 static void	lynxfb_copyrows(void *, int, int, int);
    156 static void	lynxfb_copycols(void *, int, int, int, int);
    157 static void	lynxfb_erasecols(void *, int, int, int, long);
    158 static void	lynxfb_eraserows(void *, int, int, long);
    159 static void	lynxfb_vcons_copyrows(void *, int, int, int);
    160 static void	lynxfb_vcons_copycols(void *, int, int, int, int);
    161 static void	lynxfb_vcons_erasecols(void *, int, int, int, long);
    162 static void	lynxfb_vcons_eraserows(void *, int, int, long);
    163 static void	lynxfb_blank(struct lynxfb *, int);
    164 
    165 static struct {
    166 	bool is_console;
    167 	pcitag_t tag;
    168 	bus_space_tag_t memt;
    169 	bus_space_handle_t memh;
    170 	struct lynxfb fb;
    171 } lynxfb_console;
    172 
    173 int lynxfb_default_width = LYNXFB_DEFAULT_WIDTH;
    174 int lynxfb_default_height = LYNXFB_DEFAULT_HEIGHT;
    175 int lynxfb_default_depth = LYNXFB_DEFAULT_DEPTH;
    176 int lynxfb_default_stride = LYNXFB_DEFAULT_STRIDE;
    177 int lynxfb_default_flags = LYNXFB_DEFAULT_FLAGS;
    178 
    179 static int
    180 lynxfb_match_by_id(pcireg_t id)
    181 {
    182 
    183 	if (PCI_VENDOR(id) != PCI_VENDOR_SILMOTION)
    184 		return (0);
    185 	if (PCI_PRODUCT(id) != PCI_PRODUCT_SILMOTION_SM712)
    186 		return (0);
    187 	return (1);
    188 }
    189 
    190 int
    191 lynxfb_cnattach(bus_space_tag_t memt, bus_space_tag_t iot, pci_chipset_tag_t pc,
    192     pcitag_t tag, pcireg_t id)
    193 {
    194 	struct rasops_info *ri = &lynxfb_console.fb.vcs.scr_ri;
    195 	struct lynxfb *fb;
    196 	bus_space_handle_t memh;
    197 	bus_addr_t base;
    198 	bus_size_t size;
    199 	long defattr;
    200 	int mapflags;
    201 	int error;
    202 
    203 	if (!lynxfb_match_by_id(id))
    204 		return (ENODEV);
    205 
    206 	if (pci_mapreg_info(pc, tag, PCI_MAPREG_START, PCI_MAPREG_TYPE_MEM,
    207 	    &base, &size, &mapflags))
    208 		return (EINVAL);
    209 	error = bus_space_map(memt, base, size, BUS_SPACE_MAP_LINEAR | mapflags,
    210 	    &memh);
    211 	if (error)
    212 		return (error);
    213 
    214 	fb = &lynxfb_console.fb;
    215 	fb->memt = memt;
    216 	fb->memh = memh;
    217 	fb->width = lynxfb_default_width;
    218 	fb->height = lynxfb_default_height;
    219 	fb->depth = lynxfb_default_depth;
    220 	fb->stride = lynxfb_default_stride;
    221 	if (fb->stride == 0)
    222 		fb->stride = fb->width * fb->depth / 8;
    223 	fb->flags = lynxfb_default_flags;
    224 	error = lynxfb_setup(fb);
    225 	if (error) {
    226 		bus_space_unmap(memt, memh, size);
    227 		return (error);
    228 	}
    229 
    230 	lynxfb_console.is_console = true;
    231 	lynxfb_console.tag = tag;
    232 	(*ri->ri_ops.allocattr)(ri, 0, 0, 0, &defattr);
    233 	wsdisplay_preattach(&fb->wsd, ri, 0, 0, defattr);
    234 
    235 	return (0);
    236 }
    237 
    238 static bool
    239 lynxfb_is_console(struct lynxfb_softc *sc, pcitag_t tag)
    240 {
    241 
    242 	return (lynxfb_console.is_console &&
    243 	    !memcmp(&lynxfb_console.tag, &tag, sizeof(tag)));
    244 }
    245 
    246 static int
    247 lynxfb_match(device_t parent, cfdata_t match, void *aux)
    248 {
    249 	struct pci_attach_args *pa = (struct pci_attach_args *)aux;
    250 
    251 	if (PCI_CLASS(pa->pa_class) != PCI_CLASS_DISPLAY)
    252 		return (0);
    253 	if (!lynxfb_match_by_id(pa->pa_id))
    254 		return (0);
    255 	return (1);
    256 }
    257 
    258 static void
    259 lynxfb_attach(device_t parent, device_t self, void *aux)
    260 {
    261 	struct lynxfb_softc *sc = device_private(self);
    262 	struct pci_attach_args *pa = (struct pci_attach_args *)aux;
    263 	struct wsemuldisplaydev_attach_args waa;
    264 	struct lynxfb *fb;
    265 	struct rasops_info *ri;
    266 	prop_dictionary_t dict;
    267 	bus_size_t size;
    268 	long defattr;
    269 	bool is_console;
    270 
    271 	sc->sc_dev = self;
    272 	sc->sc_pc = pa->pa_pc;
    273 	sc->sc_pcitag = pa->pa_tag;
    274 
    275 	pci_aprint_devinfo(pa, NULL);
    276 
    277 	is_console = lynxfb_is_console(sc, sc->sc_pcitag);
    278 
    279 	fb = is_console ? &lynxfb_console.fb : &sc->sc_fb_store;
    280 	sc->sc_fb = fb;
    281 	fb->sc = sc;
    282 
    283 	if (!is_console) {
    284 		if (pci_mapreg_map(pa, PCI_MAPREG_START, PCI_MAPREG_TYPE_MEM,
    285 		    BUS_SPACE_MAP_LINEAR, &fb->memt, &fb->memh, NULL, &size)) {
    286 			aprint_error_dev(self, "can't map frame buffer\n");
    287 			return;
    288 		}
    289 
    290 		dict = device_properties(self);
    291 		if (!prop_dictionary_get_uint32(dict, "width", &fb->width))
    292 			fb->width = lynxfb_default_width;
    293 		if (!prop_dictionary_get_uint32(dict, "height", &fb->height))
    294 			fb->height = lynxfb_default_height;
    295 		if (!prop_dictionary_get_uint32(dict, "depth", &fb->depth))
    296 			fb->depth = lynxfb_default_depth;
    297 		if (!prop_dictionary_get_uint32(dict, "linebytes", &fb->stride)) {
    298 			if (lynxfb_default_stride == 0)
    299 				fb->stride = fb->width * fb->depth / 8;
    300 			else
    301 				fb->stride = lynxfb_default_stride;
    302 		}
    303 		if (!prop_dictionary_get_uint32(dict, "flags", &fb->flags))
    304 			fb->flags = lynxfb_default_flags;
    305 
    306 		if (lynxfb_setup(fb)) {
    307 			aprint_error_dev(self, "can't setup frame buffer\n");
    308 			bus_space_unmap(fb->memt, fb->memh, size);
    309 			return;
    310 		}
    311 	}
    312 	sc->sc_memt = fb->memt;
    313 	sc->sc_memh = fb->memh;
    314 
    315 	aprint_normal_dev(self, "%d x %d, %d bpp, stride %d\n", fb->width,
    316 	    fb->height, fb->depth, fb->stride);
    317 
    318 	fb->wsd = (struct wsscreen_descr) {
    319 		"default",
    320 		0, 0,
    321 		NULL,
    322 		8, 16,
    323 		WSSCREEN_WSCOLORS | WSSCREEN_HILIT,
    324 		NULL,
    325 	};
    326 	sc->sc_screens[0] = &fb->wsd;
    327 	sc->sc_screenlist = (struct wsscreen_list){ 1, sc->sc_screens };
    328 	sc->sc_mode = WSDISPLAYIO_MODE_EMUL;
    329 
    330 	vcons_init(&sc->sc_vd, sc, &fb->wsd, &lynxfb_accessops);
    331 	sc->sc_vd.init_screen = lynxfb_init_screen;
    332 
    333 	ri = &fb->vcs.scr_ri;
    334 	if (is_console) {
    335 		vcons_init_screen(&sc->sc_vd, &fb->vcs, 1, &defattr);
    336 		fb->vcs.scr_flags |= VCONS_SCREEN_IS_STATIC;
    337 
    338 		fb->wsd.textops = &ri->ri_ops;
    339 		fb->wsd.capabilities = ri->ri_caps;
    340 		fb->wsd.nrows = ri->ri_rows;
    341 		fb->wsd.ncols = ri->ri_cols;
    342 		wsdisplay_cnattach(&fb->wsd, ri, 0, 0, defattr);
    343 		vcons_replay_msgbuf(&fb->vcs);
    344 	} else {
    345 		/*
    346 		 * since we're not the console we can postpone the rest
    347 		 * until someone actually allocates a screen for us
    348 		 */
    349 		(*ri->ri_ops.allocattr)(ri, 0, 0, 0, &defattr);
    350 	}
    351 
    352 	waa.console = is_console;
    353 	waa.scrdata = &sc->sc_screenlist;
    354 	waa.accessops = &lynxfb_accessops;
    355 	waa.accesscookie = &sc->sc_vd;
    356 
    357 	config_found(self, &waa, wsemuldisplaydevprint);
    358 }
    359 
    360 /*
    361  * vga sequencer access through MMIO space
    362  */
    363 static __inline uint8_t
    364 lynxfb_vgats_read(struct lynxfb *fb, uint regno)
    365 {
    366 
    367 	bus_space_write_1(fb->mmiot, fb->mmioh, IO_VGA + VGA_TS_INDEX, regno);
    368 	return bus_space_read_1(fb->mmiot, fb->mmioh, IO_VGA + VGA_TS_DATA);
    369 }
    370 
    371 static __inline void
    372 lynxfb_vgats_write(struct lynxfb *fb, uint regno, uint8_t value)
    373 {
    374 
    375 	bus_space_write_1(fb->mmiot, fb->mmioh, IO_VGA + VGA_TS_INDEX, regno);
    376 	bus_space_write_1(fb->mmiot, fb->mmioh, IO_VGA + VGA_TS_DATA, value);
    377 }
    378 
    379 /*
    380  * wsdisplay accesops
    381  */
    382 static int
    383 lynxfb_ioctl(void *v, void *vs, u_long cmd, void *data, int flags,
    384     struct lwp *l)
    385 {
    386 	struct vcons_data *vd = v;
    387 	struct lynxfb_softc *sc = vd->cookie;
    388 	struct lynxfb *fb = sc->sc_fb;
    389 	struct vcons_screen *ms = vd->active;
    390 	struct wsdisplay_fbinfo *wdf;
    391 	struct wsdisplay_param *param;
    392 
    393 	switch (cmd) {
    394 	case WSDISPLAYIO_GTYPE:
    395 		*(uint *)data = WSDISPLAY_TYPE_PCIMISC;
    396 		return (0);
    397 
    398 	/* PCI config read/write passthrough. */
    399 	case PCI_IOC_CFGREAD:
    400 	case PCI_IOC_CFGWRITE:
    401 		return pci_devioctl(sc->sc_pc, sc->sc_pcitag,
    402 		    cmd, data, flags, l);
    403 
    404 	case WSDISPLAYIO_GET_BUSID:
    405 		return wsdisplayio_busid_pci(device_parent(sc->sc_dev),
    406 		    sc->sc_pc, sc->sc_pcitag, data);
    407 
    408 	case WSDISPLAYIO_GINFO:
    409 		if (ms == NULL)
    410 			return (ENODEV);
    411 		wdf = (struct wsdisplay_fbinfo *)data;
    412 		wdf->width = ms->scr_ri.ri_width;
    413 		wdf->height = ms->scr_ri.ri_height;
    414 		wdf->depth = ms->scr_ri.ri_depth;
    415 		wdf->cmsize = 0;
    416 		return (0);
    417 
    418 	case WSDISPLAYIO_LINEBYTES:
    419 		*(uint *)data = fb->stride;
    420 		return (0);
    421 
    422 	case WSDISPLAYIO_GVIDEO:
    423 		*(int *)data = fb->blank ? WSDISPLAYIO_VIDEO_OFF :
    424 		    WSDISPLAYIO_VIDEO_ON;
    425 		return (0);
    426 
    427 	case WSDISPLAYIO_SVIDEO:
    428 		lynxfb_blank(fb, *(int *)data);
    429 		return (0);
    430 
    431 	case WSDISPLAYIO_GETPARAM:
    432 		param = (struct wsdisplay_param *)data;
    433 		switch (param->param) {
    434 		case WSDISPLAYIO_PARAM_BACKLIGHT:
    435 			param->min = 0;
    436 			param->max = 1;
    437 			param->curval = fb->blank;
    438 			return (0);
    439 		}
    440 		break;
    441 
    442 	case WSDISPLAYIO_SETPARAM:
    443 		param = (struct wsdisplay_param *)data;
    444 		switch (param->param) {
    445 		case WSDISPLAYIO_PARAM_BACKLIGHT:
    446 			lynxfb_blank(fb, param->curval);
    447 			return (0);
    448 		}
    449 		break;
    450 	}
    451 	return (EPASSTHROUGH);
    452 }
    453 
    454 static paddr_t
    455 lynxfb_mmap(void *v, void *vs, off_t offset, int prot)
    456 {
    457 	struct vcons_data *vd = v;
    458 	struct lynxfb_softc *sc = vd->cookie;
    459 	struct rasops_info *ri = &sc->sc_fb->vcs.scr_ri;
    460 
    461 	if ((offset & PAGE_MASK) != 0)
    462 		return (-1);
    463 
    464 	if (offset < 0 || offset >= ri->ri_stride * ri->ri_height)
    465 		return (-1);
    466 
    467 	return bus_space_mmap(sc->sc_memt, offset, 0, prot,
    468 	    BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE);
    469 }
    470 
    471 static void
    472 lynxfb_init_screen(void *cookie, struct vcons_screen *scr,
    473     int existing, long *defattr)
    474 {
    475 	struct lynxfb_softc *sc = cookie;
    476 	struct lynxfb *fb = sc->sc_fb;
    477 	struct rasops_info *ri = &scr->scr_ri;
    478 
    479 	ri->ri_width = fb->width;
    480 	ri->ri_height = fb->height;
    481 	ri->ri_depth = fb->depth;
    482 	ri->ri_stride = fb->stride;
    483 	ri->ri_flg = RI_CENTER;
    484 	ri->ri_bits = fb->fbaddr;
    485 
    486 #ifdef VCONS_DRAW_INTR
    487 	scr->scr_flags |= VCONS_DONT_READ;
    488 #endif
    489 
    490 	if (existing)
    491 		ri->ri_flg |= RI_CLEAR;
    492 
    493 	rasops_init(ri, 0, 0);
    494 	ri->ri_caps = WSSCREEN_WSCOLORS;
    495 	rasops_reconfig(ri, fb->height / ri->ri_font->fontheight,
    496 	    fb->width / ri->ri_font->fontwidth);
    497 
    498 	ri->ri_hw = scr;
    499 
    500 	if (fb->accel) {
    501 		ri->ri_ops.copycols = lynxfb_vcons_copycols;
    502 		ri->ri_ops.copyrows = lynxfb_vcons_copyrows;
    503 		ri->ri_ops.erasecols = lynxfb_vcons_erasecols;
    504 		ri->ri_ops.eraserows = lynxfb_vcons_eraserows;
    505 	}
    506 }
    507 
    508 /*
    509  * Frame buffer initialization.
    510  */
    511 static int
    512 lynxfb_setup(struct lynxfb *fb)
    513 {
    514 	struct rasops_info *ri = &fb->vcs.scr_ri;
    515 	int error;
    516 
    517 	fb->dprt = fb->memt;
    518 	error = bus_space_subregion(fb->memt, fb->memh, SM7XX_DPR_BASE,
    519 	    SMXXX_DPR_SIZE, &fb->dprh);
    520 	if (error != 0)
    521 		return (error);
    522 
    523 	fb->mmiot = fb->memt;
    524 	error = bus_space_subregion(fb->mmiot, fb->memh, SM7XX_MMIO_BASE,
    525 	    SM7XX_MMIO_SIZE, &fb->mmioh);
    526 	if (error != 0)
    527 		return (error);
    528 
    529 	ri->ri_width = fb->width;
    530 	ri->ri_height = fb->height;
    531 	ri->ri_depth = fb->depth;
    532 	ri->ri_stride = fb->stride;
    533 	ri->ri_flg = RI_CENTER | RI_CLEAR | RI_NO_AUTO;
    534 	fb->fbaddr = ri->ri_bits = (void *)bus_space_vaddr(fb->memt, fb->memh);
    535 	ri->ri_hw = fb;
    536 
    537 	if (fb->flags & LYNXFB_FLAG_SWAPBR) {
    538 		switch (fb->depth) {
    539 		case 16:
    540 			ri->ri_rnum = 5;
    541 			ri->ri_rpos = 11;
    542 			ri->ri_gnum = 6;
    543 			ri->ri_gpos = 5;
    544 			ri->ri_bnum = 5;
    545 			ri->ri_bpos = 0;
    546 			break;
    547 		}
    548 	}
    549 
    550 	rasops_init(ri, 0, 0);
    551 	rasops_reconfig(ri, ri->ri_height / ri->ri_font->fontheight,
    552 	    ri->ri_width / ri->ri_font->fontwidth);
    553 
    554 	fb->wsd.name = "std";
    555 	fb->wsd.ncols = ri->ri_cols;
    556 	fb->wsd.nrows = ri->ri_rows;
    557 	fb->wsd.textops = &ri->ri_ops;
    558 	fb->wsd.fontwidth = ri->ri_font->fontwidth;
    559 	fb->wsd.fontheight = ri->ri_font->fontheight;
    560 	fb->wsd.capabilities = ri->ri_caps;
    561 
    562 	/*
    563 	 * Setup 2D acceleration whenever possible
    564 	 */
    565 	if (lynxfb_wait(fb) == 0) {
    566 		fb->accel = 1;
    567 
    568 		DPR_WRITE(fb, DPR_CROP_TOPLEFT_COORDS, DPR_COORDS(0, 0));
    569 		/* use of width both times is intentional */
    570 		DPR_WRITE(fb, DPR_PITCH,
    571 		    DPR_COORDS(ri->ri_width, ri->ri_width));
    572 		DPR_WRITE(fb, DPR_SRC_WINDOW,
    573 		    DPR_COORDS(ri->ri_width, ri->ri_width));
    574 		DPR_WRITE(fb, DPR_BYTE_BIT_MASK, 0xffffffff);
    575 		DPR_WRITE(fb, DPR_COLOR_COMPARE_MASK, 0);
    576 		DPR_WRITE(fb, DPR_COLOR_COMPARE, 0);
    577 		DPR_WRITE(fb, DPR_SRC_BASE, 0);
    578 		DPR_WRITE(fb, DPR_DST_BASE, 0);
    579 		DPR_READ(fb, DPR_DST_BASE);
    580 
    581 		ri->ri_ops.copycols = lynxfb_copycols;
    582 		ri->ri_ops.copyrows = lynxfb_copyrows;
    583 		ri->ri_ops.erasecols = lynxfb_erasecols;
    584 		ri->ri_ops.eraserows = lynxfb_eraserows;
    585 	}
    586 
    587 	return (0);
    588 }
    589 
    590 static int
    591 lynxfb_wait(struct lynxfb *fb)
    592 {
    593 	uint32_t reg;
    594 	int i;
    595 
    596 	for (i = 10000; i > 0; i--) {
    597 		reg = lynxfb_vgats_read(fb, 0x16);
    598 		if ((reg & 0x18) == 0x10)
    599 			return (0);
    600 		delay(1);
    601 	}
    602 	return (EBUSY);
    603 }
    604 
    605 static void
    606 lynxfb_copyrect(struct lynxfb *fb, int sx, int sy, int dx, int dy, int w, int h)
    607 {
    608 	uint32_t dir;
    609 
    610 	/* Compute rop direction */
    611 	if (sy < dy || (sy == dy && sx <= dx)) {
    612 		sx += w - 1;
    613 		dx += w - 1;
    614 		sy += h - 1;
    615 		dy += h - 1;
    616 		dir = DE_CTRL_RTOL;
    617 	} else
    618 		dir = 0;
    619 
    620 	DPR_WRITE(fb, DPR_SRC_COORDS, DPR_COORDS(sx, sy));
    621 	DPR_WRITE(fb, DPR_DST_COORDS, DPR_COORDS(dx, dy));
    622 	DPR_WRITE(fb, DPR_SPAN_COORDS, DPR_COORDS(w, h));
    623 	DPR_WRITE(fb, DPR_DE_CTRL, DE_CTRL_START | DE_CTRL_ROP_ENABLE | dir |
    624 	    (DE_CTRL_COMMAND_BITBLT << DE_CTRL_COMMAND_SHIFT) |
    625 	    (DE_CTRL_ROP_SRC << DE_CTRL_ROP_SHIFT));
    626 	DPR_READ(fb, DPR_DE_CTRL);
    627 
    628 	lynxfb_wait(fb);
    629 }
    630 
    631 static void
    632 lynxfb_fillrect(struct lynxfb *fb, int x, int y, int w, int h, int bg)
    633 {
    634 	struct rasops_info *ri = &fb->vcs.scr_ri;
    635 
    636 	DPR_WRITE(fb, DPR_FG_COLOR, ri->ri_devcmap[bg]);
    637 	DPR_WRITE(fb, DPR_DST_COORDS, DPR_COORDS(x, y));
    638 	DPR_WRITE(fb, DPR_SPAN_COORDS, DPR_COORDS(w, h));
    639 	DPR_WRITE(fb, DPR_DE_CTRL, DE_CTRL_START | DE_CTRL_ROP_ENABLE |
    640 	    (DE_CTRL_COMMAND_SOLIDFILL << DE_CTRL_COMMAND_SHIFT) |
    641 	    (DE_CTRL_ROP_SRC << DE_CTRL_ROP_SHIFT));
    642 	DPR_READ(fb, DPR_DE_CTRL);
    643 
    644 	lynxfb_wait(fb);
    645 }
    646 
    647 static inline void
    648 lynxfb_copyrows1(struct rasops_info *ri, int src, int dst, int num,
    649     struct lynxfb *fb)
    650 {
    651 	struct wsdisplay_font *f = ri->ri_font;
    652 
    653 	num *= f->fontheight;
    654 	src *= f->fontheight;
    655 	dst *= f->fontheight;
    656 
    657 	lynxfb_copyrect(fb, ri->ri_xorigin, ri->ri_yorigin + src,
    658 	    ri->ri_xorigin, ri->ri_yorigin + dst, ri->ri_emuwidth, num);
    659 }
    660 
    661 static inline void
    662 lynxfb_copycols1(struct rasops_info *ri, int row, int src, int dst, int num,
    663     struct lynxfb *fb)
    664 {
    665 	struct wsdisplay_font *f = ri->ri_font;
    666 
    667 	num *= f->fontwidth;
    668 	src *= f->fontwidth;
    669 	dst *= f->fontwidth;
    670 	row *= f->fontheight;
    671 
    672 	lynxfb_copyrect(fb, ri->ri_xorigin + src, ri->ri_yorigin + row,
    673 	    ri->ri_xorigin + dst, ri->ri_yorigin + row, num, f->fontheight);
    674 }
    675 
    676 static inline void
    677 lynxfb_erasecols1(struct rasops_info *ri, int row, int col, int num, long attr,
    678     struct lynxfb *fb)
    679 {
    680 	struct wsdisplay_font *f = ri->ri_font;
    681 	int32_t bg, fg, ul;
    682 
    683 	row *= f->fontheight;
    684 	col *= f->fontwidth;
    685 	num *= f->fontwidth;
    686 	rasops_unpack_attr(attr, &fg, &bg, &ul);
    687 
    688 	lynxfb_fillrect(fb, ri->ri_xorigin + col, ri->ri_yorigin + row,
    689 	    num, f->fontheight, bg);
    690 }
    691 
    692 static inline void
    693 lynxfb_eraserows1(struct rasops_info *ri, int row, int num, long attr,
    694     struct lynxfb *fb)
    695 {
    696 	struct wsdisplay_font *f = ri->ri_font;
    697 	int32_t bg, fg, ul;
    698 	int x, y, w;
    699 
    700 	if ((num == ri->ri_rows) && ISSET(ri->ri_flg, RI_FULLCLEAR)) {
    701 		num = ri->ri_height;
    702 		x = y = 0;
    703 		w = ri->ri_width;
    704 	} else {
    705 		num *= f->fontheight;
    706 		x = ri->ri_xorigin;
    707 		y = ri->ri_yorigin + row * f->fontheight;
    708 		w = ri->ri_emuwidth;
    709 	}
    710 	rasops_unpack_attr(attr, &fg, &bg, &ul);
    711 	lynxfb_fillrect(fb, x, y, w, num, bg);
    712 }
    713 
    714 static void
    715 lynxfb_copyrows(void *cookie, int src, int dst, int num)
    716 {
    717 	struct rasops_info *ri = cookie;
    718 	struct lynxfb *fb = ri->ri_hw;
    719 
    720 	lynxfb_copyrows1(ri, src, dst, num, fb);
    721 }
    722 
    723 static void
    724 lynxfb_copycols(void *cookie, int row, int src, int dst, int num)
    725 {
    726 	struct rasops_info *ri = cookie;
    727 	struct lynxfb *fb = ri->ri_hw;
    728 
    729 	lynxfb_copycols1(ri, row, src, dst, num, fb);
    730 }
    731 
    732 static void
    733 lynxfb_erasecols(void *cookie, int row, int col, int num, long attr)
    734 {
    735 	struct rasops_info *ri = cookie;
    736 	struct lynxfb *fb = ri->ri_hw;
    737 
    738 	lynxfb_erasecols1(ri, row, col, num, attr, fb);
    739 }
    740 
    741 static void
    742 lynxfb_eraserows(void *cookie, int row, int num, long attr)
    743 {
    744 	struct rasops_info *ri = cookie;
    745 	struct lynxfb *fb = ri->ri_hw;
    746 
    747 	lynxfb_eraserows1(ri, row, num, attr, fb);
    748 }
    749 
    750 static void
    751 lynxfb_vcons_copyrows(void *cookie, int src, int dst, int num)
    752 {
    753 	struct rasops_info *ri = cookie;
    754 	struct vcons_screen *scr = ri->ri_hw;
    755 	struct lynxfb_softc *sc = scr->scr_cookie;
    756 	struct lynxfb *fb = sc->sc_fb;
    757 
    758 	lynxfb_copyrows1(ri, src, dst, num, fb);
    759 }
    760 
    761 static void
    762 lynxfb_vcons_copycols(void *cookie, int row, int src, int dst, int num)
    763 {
    764 	struct rasops_info *ri = cookie;
    765 	struct vcons_screen *scr = ri->ri_hw;
    766 	struct lynxfb_softc *sc = scr->scr_cookie;
    767 	struct lynxfb *fb = sc->sc_fb;
    768 
    769 	lynxfb_copycols1(ri, row, src, dst, num, fb);
    770 }
    771 
    772 static void
    773 lynxfb_vcons_erasecols(void *cookie, int row, int col, int num, long attr)
    774 {
    775 	struct rasops_info *ri = cookie;
    776 	struct vcons_screen *scr = ri->ri_hw;
    777 	struct lynxfb_softc *sc = scr->scr_cookie;
    778 	struct lynxfb *fb = sc->sc_fb;
    779 
    780 	lynxfb_erasecols1(ri, row, col, num, attr, fb);
    781 }
    782 
    783 static void
    784 lynxfb_vcons_eraserows(void *cookie, int row, int num, long attr)
    785 {
    786 	struct rasops_info *ri = cookie;
    787 	struct vcons_screen *scr = ri->ri_hw;
    788 	struct lynxfb_softc *sc = scr->scr_cookie;
    789 	struct lynxfb *fb = sc->sc_fb;
    790 
    791 	lynxfb_eraserows1(ri, row, num, attr, fb);
    792 }
    793 
    794 static void
    795 lynxfb_blank(struct lynxfb *fb, int blank)
    796 {
    797 
    798 	fb->blank = blank;
    799 	if (!blank) {
    800 		lynxfb_vgats_write(fb, 0x31,
    801 		    lynxfb_vgats_read(fb, 0x31) | 0x01);
    802 	} else {
    803 		lynxfb_vgats_write(fb, 0x21,
    804 		    lynxfb_vgats_read(fb, 0x21) | 0x30);
    805 		lynxfb_vgats_write(fb, 0x31,
    806 		    lynxfb_vgats_read(fb, 0x31) & ~0x01);
    807 	}
    808 }
    809