xref: /openbsd/sys/arch/loongson/dev/smfb.c (revision 097a140d)
1 /*	$OpenBSD: smfb.c,v 1.20 2021/01/02 14:29:16 visa Exp $	*/
2 
3 /*
4  * Copyright (c) 2009, 2010 Miodrag Vallat.
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /*
20  * SiliconMotion SM502 and SM712 frame buffer driver.
21  *
22  * Assumes its video output is an LCD panel, in 5:6:5 mode, and fixed
23  * 1024x600 (Yeeloong) or 1368x768 (Lynloong) or 800x480 (EBT700)
24  * resolution depending on the system model.
25  */
26 
27 #include <sys/param.h>
28 #include <sys/systm.h>
29 #include <sys/device.h>
30 
31 #include <machine/autoconf.h>
32 #include <machine/bus.h>
33 #include <machine/cpu.h>
34 
35 #include <uvm/uvm_extern.h>
36 
37 #include <dev/ic/vgareg.h>
38 #include <dev/isa/isareg.h>
39 #include <dev/pci/pcireg.h>
40 #include <dev/pci/pcivar.h>
41 #include <dev/pci/pcidevs.h>
42 
43 #include <dev/wscons/wsconsio.h>
44 #include <dev/wscons/wsdisplayvar.h>
45 #include <dev/rasops/rasops.h>
46 
47 #include <loongson/dev/voyagerreg.h>
48 #include <loongson/dev/voyagervar.h>
49 #include <loongson/dev/smfbreg.h>
50 
51 struct smfb_softc;
52 
53 /* minimal frame buffer information, suitable for early console */
54 struct smfb {
55 	struct smfb_softc	*sc;
56 	struct rasops_info	ri;
57 	int			is5xx;
58 
59 	/* DPR registers */
60 	bus_space_tag_t		dprt;
61 	bus_space_handle_t	dprh;
62 	/* MMIO space (SM7xx) or control registers (SM5xx) */
63 	bus_space_tag_t		mmiot;
64 	bus_space_handle_t	mmioh;
65 	/* DCR registers (SM5xx) */
66 	bus_space_tag_t		dcrt;
67 	bus_space_handle_t	dcrh;
68 
69 	struct wsscreen_descr	wsd;
70 };
71 
72 #define	DCR_READ(fb, reg) \
73 	bus_space_read_4((fb)->dcrt, (fb)->dcrh, (reg))
74 #define	DCR_WRITE(fb, reg, val) \
75 	bus_space_write_4((fb)->dcrt, (fb)->dcrh, (reg), (val))
76 #define	DPR_READ(fb, reg) \
77 	bus_space_read_4((fb)->dprt, (fb)->dprh, (reg))
78 #define	DPR_WRITE(fb, reg, val) \
79 	bus_space_write_4((fb)->dprt, (fb)->dprh, (reg), (val))
80 
81 struct smfb_softc {
82 	struct device		 sc_dev;
83 	struct smfb		*sc_fb;
84 	struct smfb		 sc_fb_store;
85 
86 	struct wsscreen_list	 sc_wsl;
87 	struct wsscreen_descr	*sc_scrlist[1];
88 	int			 sc_nscr;
89 };
90 
91 int	smfb_pci_match(struct device *, void *, void *);
92 void	smfb_pci_attach(struct device *, struct device *, void *);
93 int	smfb_voyager_match(struct device *, void *, void *);
94 void	smfb_voyager_attach(struct device *, struct device *, void *);
95 int	smfb_activate(struct device *, int);
96 
97 const struct cfattach smfb_pci_ca = {
98 	sizeof(struct smfb_softc), smfb_pci_match, smfb_pci_attach,
99 	NULL, smfb_activate
100 };
101 
102 const struct cfattach smfb_voyager_ca = {
103 	sizeof(struct smfb_softc), smfb_voyager_match, smfb_voyager_attach,
104 	smfb_activate
105 };
106 
107 struct cfdriver smfb_cd = {
108 	NULL, "smfb", DV_DULL
109 };
110 
111 int	smfb_alloc_screen(void *, const struct wsscreen_descr *, void **, int *,
112 	    int *, uint32_t *);
113 void	smfb_burner(void *, uint, uint);
114 void	smfb_free_screen(void *, void *);
115 int	smfb_ioctl(void *, u_long, caddr_t, int, struct proc *);
116 int	smfb_list_font(void *, struct wsdisplay_font *);
117 int	smfb_load_font(void *, void *, struct wsdisplay_font *);
118 paddr_t	smfb_mmap(void *, off_t, int);
119 int	smfb_show_screen(void *, void *, int, void (*)(void *, int, int),
120 	    void *);
121 
122 struct wsdisplay_accessops smfb_accessops = {
123 	.ioctl = smfb_ioctl,
124 	.mmap = smfb_mmap,
125 	.alloc_screen = smfb_alloc_screen,
126 	.free_screen = smfb_free_screen,
127 	.show_screen = smfb_show_screen,
128 	.load_font = smfb_load_font,
129 	.list_font = smfb_list_font,
130 	.burn_screen = smfb_burner
131 };
132 
133 int	smfb_setup(struct smfb *, bus_space_tag_t, bus_space_handle_t,
134 	    bus_space_tag_t, bus_space_handle_t);
135 
136 void	smfb_copyrect(struct smfb *, int, int, int, int, int, int);
137 void	smfb_fillrect(struct smfb *, int, int, int, int, int);
138 int	smfb_copyrows(void *, int, int, int);
139 int	smfb_copycols(void *, int, int, int, int);
140 int	smfb_do_cursor(struct rasops_info *);
141 int	smfb_erasecols(void *, int, int, int, uint32_t);
142 int	smfb_eraserows(void *, int, int, uint32_t);
143 int	smfb_wait(struct smfb *);
144 
145 void	smfb_wait_panel_vsync(struct smfb *, int);
146 uint8_t	smfb_vgats_read(struct smfb *, uint);
147 void	smfb_vgats_write(struct smfb *, uint, uint8_t);
148 
149 void	smfb_attach_common(struct smfb_softc *, int, bus_space_tag_t,
150 	    bus_space_handle_t, bus_space_tag_t, bus_space_handle_t);
151 
152 static struct smfb smfbcn;
153 
154 const struct pci_matchid smfb_devices[] = {
155 	{ PCI_VENDOR_SMI, PCI_PRODUCT_SMI_SM712 }
156 };
157 
158 int
159 smfb_pci_match(struct device *parent, void *vcf, void *aux)
160 {
161 	struct pci_attach_args *pa = (struct pci_attach_args *)aux;
162 
163 	return pci_matchbyid(pa, smfb_devices, nitems(smfb_devices));
164 }
165 
166 int
167 smfb_voyager_match(struct device *parent, void *vcf, void *aux)
168 {
169 	struct voyager_attach_args *vaa = (struct voyager_attach_args *)aux;
170 	struct cfdata *cf = (struct cfdata *)vcf;
171 
172 	return strcmp(vaa->vaa_name, cf->cf_driver->cd_name) == 0;
173 }
174 
175 void
176 smfb_pci_attach(struct device *parent, struct device *self, void *aux)
177 {
178 	struct smfb_softc *sc = (struct smfb_softc *)self;
179 	struct pci_attach_args *pa = (struct pci_attach_args *)aux;
180 	bus_space_tag_t memt;
181 	bus_space_handle_t memh;
182 
183 	if (pci_mapreg_map(pa, PCI_MAPREG_START, PCI_MAPREG_TYPE_MEM,
184 	    BUS_SPACE_MAP_LINEAR, &memt, &memh, NULL, NULL, 0) != 0) {
185 		printf(": can't map frame buffer\n");
186 		return;
187 	}
188 
189 	smfb_attach_common(sc, 0, memt, memh, memt, memh);
190 }
191 
192 void
193 smfb_voyager_attach(struct device *parent, struct device *self, void *aux)
194 {
195 	struct smfb_softc *sc = (struct smfb_softc *)self;
196 	struct voyager_attach_args *vaa = (struct voyager_attach_args *)aux;
197 
198 	smfb_attach_common(sc, 1, vaa->vaa_fbt, vaa->vaa_fbh, vaa->vaa_mmiot,
199 	    vaa->vaa_mmioh);
200 }
201 
202 void
203 smfb_attach_common(struct smfb_softc *sc, int is5xx, bus_space_tag_t memt,
204     bus_space_handle_t memh, bus_space_tag_t mmiot, bus_space_handle_t mmioh)
205 {
206 	struct wsemuldisplaydev_attach_args waa;
207 	int console;
208 
209 	console = smfbcn.ri.ri_hw != NULL;
210 
211 	if (console) {
212 		sc->sc_fb = &smfbcn;
213 		sc->sc_fb->sc = sc;
214 	} else {
215 		sc->sc_fb = &sc->sc_fb_store;
216 		sc->sc_fb->is5xx = is5xx;
217 		if (smfb_setup(sc->sc_fb, memt, memh, mmiot, mmioh) != 0) {
218 			printf(": can't setup frame buffer\n");
219 			return;
220 		}
221 	}
222 
223 	printf(": %dx%d, %dbpp\n", sc->sc_fb->ri.ri_width,
224 	    sc->sc_fb->ri.ri_height, sc->sc_fb->ri.ri_depth);
225 
226 	sc->sc_scrlist[0] = &sc->sc_fb->wsd;
227 	sc->sc_wsl.nscreens = 1;
228 	sc->sc_wsl.screens = (const struct wsscreen_descr **)sc->sc_scrlist;
229 
230 	waa.console = console;
231 	waa.scrdata = &sc->sc_wsl;
232 	waa.accessops = &smfb_accessops;
233 	waa.accesscookie = sc;
234 	waa.defaultscreens = 0;
235 
236 	config_found((struct device *)sc, &waa, wsemuldisplaydevprint);
237 }
238 
239 /*
240  * wsdisplay accesops
241  */
242 
243 int
244 smfb_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep,
245     int *curxp, int *curyp, uint32_t *attrp)
246 {
247 	struct smfb_softc *sc = (struct smfb_softc *)v;
248 	struct rasops_info *ri = &sc->sc_fb->ri;
249 
250 	if (sc->sc_nscr > 0)
251 		return ENOMEM;
252 
253 	*cookiep = ri;
254 	*curxp = *curyp = 0;
255 	ri->ri_ops.pack_attr(ri, 0, 0, 0, attrp);
256 	sc->sc_nscr++;
257 
258 	return 0;
259 }
260 
261 void
262 smfb_free_screen(void *v, void *cookie)
263 {
264 	struct smfb_softc *sc = (struct smfb_softc *)v;
265 
266 	sc->sc_nscr--;
267 }
268 
269 int
270 smfb_ioctl(void *v, u_long cmd, caddr_t data, int flags, struct proc *p)
271 {
272 	struct smfb_softc *sc = (struct smfb_softc *)v;
273 	struct rasops_info *ri = &sc->sc_fb->ri;
274 	struct wsdisplay_fbinfo *wdf;
275 
276 	switch (cmd) {
277 	case WSDISPLAYIO_GTYPE:
278 		*(uint *)data = WSDISPLAY_TYPE_SMFB;
279 		break;
280 	case WSDISPLAYIO_GINFO:
281 		wdf = (struct wsdisplay_fbinfo *)data;
282 		wdf->width = ri->ri_width;
283 		wdf->height = ri->ri_height;
284 		wdf->depth = ri->ri_depth;
285 		wdf->cmsize = 0;
286 		break;
287 	case WSDISPLAYIO_LINEBYTES:
288 		*(uint *)data = ri->ri_stride;
289 		break;
290 	default:
291 		return -1;
292 	}
293 
294 	return 0;
295 }
296 
297 int
298 smfb_show_screen(void *v, void *cookie, int waitok,
299     void (*cb)(void *, int, int), void *cbarg)
300 {
301 	return 0;
302 }
303 
304 paddr_t
305 smfb_mmap(void *v, off_t offset, int prot)
306 {
307 	struct smfb_softc *sc = (struct smfb_softc *)v;
308 	struct rasops_info *ri = &sc->sc_fb->ri;
309 
310 	if ((offset & PAGE_MASK) != 0)
311 		return -1;
312 
313 	if (offset < 0 || offset >= ri->ri_stride * ri->ri_height)
314 		return -1;
315 
316 	return XKPHYS_TO_PHYS((paddr_t)ri->ri_bits) + offset;
317 }
318 
319 int
320 smfb_load_font(void *v, void *emulcookie, struct wsdisplay_font *font)
321 {
322 	struct smfb_softc *sc = (struct smfb_softc *)v;
323 	struct rasops_info *ri = &sc->sc_fb->ri;
324 
325 	return rasops_load_font(ri, emulcookie, font);
326 }
327 
328 int
329 smfb_list_font(void *v, struct wsdisplay_font *font)
330 {
331 	struct smfb_softc *sc = (struct smfb_softc *)v;
332 	struct rasops_info *ri = &sc->sc_fb->ri;
333 
334 	return rasops_list_font(ri, font);
335 }
336 
337 void
338 smfb_burner(void *v, uint on, uint flg)
339 {
340 	struct smfb_softc *sc = (struct smfb_softc *)v;
341 	struct smfb *fb = sc->sc_fb;
342 
343 	if (fb->is5xx) {
344 		if (on) {
345 			/*
346 			 * Wait for a few cycles after restoring power,
347 			 * to prevent white flickering.
348 			 */
349 			DCR_WRITE(fb, DCR_PANEL_DISPLAY_CONTROL,
350 			    DCR_READ(fb, DCR_PANEL_DISPLAY_CONTROL) | PDC_VDD);
351 			smfb_wait_panel_vsync(fb, 4);
352 			DCR_WRITE(fb, DCR_PANEL_DISPLAY_CONTROL,
353 			    DCR_READ(fb, DCR_PANEL_DISPLAY_CONTROL) | PDC_DATA);
354 			smfb_wait_panel_vsync(fb, 4);
355 			DCR_WRITE(fb, DCR_PANEL_DISPLAY_CONTROL,
356 			    DCR_READ(fb, DCR_PANEL_DISPLAY_CONTROL) |
357 			    (PDC_BIAS | PDC_EN));
358 		} else
359 			DCR_WRITE(fb, DCR_PANEL_DISPLAY_CONTROL,
360 			    DCR_READ(fb, DCR_PANEL_DISPLAY_CONTROL) &
361 			    ~(PDC_EN | PDC_BIAS | PDC_DATA | PDC_VDD));
362 	} else {
363 		if (on) {
364 			smfb_vgats_write(fb, 0x31,
365 			    smfb_vgats_read(fb, 0x31) | 0x01);
366 		} else {
367 			smfb_vgats_write(fb, 0x21,
368 			    smfb_vgats_read(fb, 0x21) | 0x30);
369 			smfb_vgats_write(fb, 0x31,
370 			    smfb_vgats_read(fb, 0x31) & ~0x01);
371 		}
372 	}
373 }
374 
375 /*
376  * Frame buffer initialization.
377  */
378 
379 int
380 smfb_setup(struct smfb *fb, bus_space_tag_t memt, bus_space_handle_t memh,
381     bus_space_tag_t mmiot, bus_space_handle_t mmioh)
382 {
383 	struct rasops_info *ri;
384 	int accel = 0;
385 	int rc;
386 
387 	ri = &fb->ri;
388 	switch (sys_platform->system_type) {
389 	case LOONGSON_EBT700:
390 		ri->ri_width = 800;
391 		ri->ri_height = 480;
392 		break;
393 	case LOONGSON_LYNLOONG:
394 		ri->ri_width = 1368;
395 		ri->ri_height = 768;
396 		break;
397 	default:
398 	case LOONGSON_GDIUM:
399 	case LOONGSON_YEELOONG:
400 		ri->ri_width = 1024;
401 		ri->ri_height = 600;
402 		break;
403 	}
404 	ri->ri_depth = 16;
405 	ri->ri_stride = (ri->ri_width * ri->ri_depth) / 8;
406 	ri->ri_flg = RI_CENTER | RI_CLEAR | RI_FULLCLEAR;
407 	ri->ri_bits = (void *)bus_space_vaddr(memt, memh);
408 	ri->ri_hw = fb;
409 
410 #ifdef __MIPSEL__
411 	/* swap B and R */
412 	ri->ri_rnum = 5;
413 	ri->ri_rpos = 11;
414 	ri->ri_gnum = 6;
415 	ri->ri_gpos = 5;
416 	ri->ri_bnum = 5;
417 	ri->ri_bpos = 0;
418 #endif
419 
420 	rasops_init(ri, 160, 160);
421 
422 	strlcpy(fb->wsd.name, "std", sizeof(fb->wsd.name));
423 	fb->wsd.ncols = ri->ri_cols;
424 	fb->wsd.nrows = ri->ri_rows;
425 	fb->wsd.textops = &ri->ri_ops;
426 	fb->wsd.fontwidth = ri->ri_font->fontwidth;
427 	fb->wsd.fontheight = ri->ri_font->fontheight;
428 	fb->wsd.capabilities = ri->ri_caps;
429 
430 	if (fb->is5xx) {
431 		fb->dcrt = mmiot;
432 		if ((rc = bus_space_subregion(mmiot, mmioh, SM5XX_DCR_BASE,
433 		    SM5XX_DCR_SIZE, &fb->dcrh)) != 0)
434 			return rc;
435 		fb->dprt = mmiot;
436 		if ((rc = bus_space_subregion(mmiot, mmioh, SM5XX_DPR_BASE,
437 		    SMXXX_DPR_SIZE, &fb->dprh)) != 0)
438 			return rc;
439 		fb->mmiot = mmiot;
440 		if ((rc = bus_space_subregion(mmiot, mmioh, SM5XX_MMIO_BASE,
441 		    SM5XX_MMIO_SIZE, &fb->mmioh)) != 0)
442 			return rc;
443 		accel = 1;
444 	} else {
445 		fb->dprt = memt;
446 		if ((rc = bus_space_subregion(memt, memh, SM7XX_DPR_BASE,
447 		    SMXXX_DPR_SIZE, &fb->dprh)) != 0)
448 			return rc;
449 		fb->mmiot = memt;
450 		if ((rc = bus_space_subregion(memt, memh, SM7XX_MMIO_BASE,
451 		    SM7XX_MMIO_SIZE, &fb->mmioh)) != 0)
452 			return rc;
453 		accel = 1;
454 	}
455 
456 	/*
457 	 * Setup 2D acceleration whenever possible
458 	 */
459 
460 	if (accel) {
461 		if (smfb_wait(fb) != 0)
462 			accel = 0;
463 	}
464 	if (accel) {
465 		DPR_WRITE(fb, DPR_CROP_TOPLEFT_COORDS, DPR_COORDS(0, 0));
466 		/* use of width both times is intentional */
467 		DPR_WRITE(fb, DPR_PITCH,
468 		    DPR_COORDS(ri->ri_width, ri->ri_width));
469 		DPR_WRITE(fb, DPR_SRC_WINDOW,
470 		    DPR_COORDS(ri->ri_width, ri->ri_width));
471 		DPR_WRITE(fb, DPR_BYTE_BIT_MASK, 0xffffffff);
472 		DPR_WRITE(fb, DPR_COLOR_COMPARE_MASK, 0);
473 		DPR_WRITE(fb, DPR_COLOR_COMPARE, 0);
474 		DPR_WRITE(fb, DPR_SRC_BASE, 0);
475 		DPR_WRITE(fb, DPR_DST_BASE, 0);
476 		DPR_READ(fb, DPR_DST_BASE);
477 
478 		ri->ri_ops.copycols = smfb_copycols;
479 		ri->ri_ops.copyrows = smfb_copyrows;
480 		ri->ri_ops.erasecols = smfb_erasecols;
481 		ri->ri_ops.eraserows = smfb_eraserows;
482 	}
483 
484 	return 0;
485 }
486 
487 void
488 smfb_copyrect(struct smfb *fb, int sx, int sy, int dx, int dy, int w, int h)
489 {
490 	uint32_t dir;
491 
492 	/* Compute rop direction */
493 	if (sy < dy || (sy == dy && sx <= dx)) {
494 		sx += w - 1;
495 		dx += w - 1;
496 		sy += h - 1;
497 		dy += h - 1;
498 		dir = DE_CTRL_RTOL;
499 	} else
500 		dir = 0;
501 
502 	DPR_WRITE(fb, DPR_SRC_COORDS, DPR_COORDS(sx, sy));
503 	DPR_WRITE(fb, DPR_DST_COORDS, DPR_COORDS(dx, dy));
504 	DPR_WRITE(fb, DPR_SPAN_COORDS, DPR_COORDS(w, h));
505 	DPR_WRITE(fb, DPR_DE_CTRL, DE_CTRL_START | DE_CTRL_ROP_ENABLE | dir |
506 	    (DE_CTRL_COMMAND_BITBLT << DE_CTRL_COMMAND_SHIFT) |
507 	    (DE_CTRL_ROP_SRC << DE_CTRL_ROP_SHIFT));
508 	DPR_READ(fb, DPR_DE_CTRL);
509 
510 	smfb_wait(fb);
511 }
512 
513 void
514 smfb_fillrect(struct smfb *fb, int x, int y, int w, int h, int bg)
515 {
516 	struct rasops_info *ri;
517 
518 	ri = &fb->ri;
519 
520 	DPR_WRITE(fb, DPR_FG_COLOR, ri->ri_devcmap[bg]);
521 	DPR_WRITE(fb, DPR_DST_COORDS, DPR_COORDS(x, y));
522 	DPR_WRITE(fb, DPR_SPAN_COORDS, DPR_COORDS(w, h));
523 	DPR_WRITE(fb, DPR_DE_CTRL, DE_CTRL_START | DE_CTRL_ROP_ENABLE |
524 	    (DE_CTRL_COMMAND_SOLIDFILL << DE_CTRL_COMMAND_SHIFT) |
525 	    (DE_CTRL_ROP_SRC << DE_CTRL_ROP_SHIFT));
526 	DPR_READ(fb, DPR_DE_CTRL);
527 
528 	smfb_wait(fb);
529 }
530 
531 int
532 smfb_copyrows(void *cookie, int src, int dst, int num)
533 {
534 	struct rasops_info *ri = cookie;
535 	struct smfb *fb = ri->ri_hw;
536 	struct wsdisplay_font *f = ri->ri_font;
537 
538 	num *= f->fontheight;
539 	src *= f->fontheight;
540 	dst *= f->fontheight;
541 
542 	smfb_copyrect(fb, ri->ri_xorigin, ri->ri_yorigin + src,
543 	    ri->ri_xorigin, ri->ri_yorigin + dst, ri->ri_emuwidth, num);
544 
545 	return 0;
546 }
547 
548 int
549 smfb_copycols(void *cookie, int row, int src, int dst, int num)
550 {
551 	struct rasops_info *ri = cookie;
552 	struct smfb *fb = ri->ri_hw;
553 	struct wsdisplay_font *f = ri->ri_font;
554 
555 	num *= f->fontwidth;
556 	src *= f->fontwidth;
557 	dst *= f->fontwidth;
558 	row *= f->fontheight;
559 
560 	smfb_copyrect(fb, ri->ri_xorigin + src, ri->ri_yorigin + row,
561 	    ri->ri_xorigin + dst, ri->ri_yorigin + row, num, f->fontheight);
562 
563 	return 0;
564 }
565 
566 int
567 smfb_erasecols(void *cookie, int row, int col, int num, uint32_t attr)
568 {
569 	struct rasops_info *ri = cookie;
570 	struct smfb *fb = ri->ri_hw;
571 	struct wsdisplay_font *f = ri->ri_font;
572 	int bg, fg;
573 
574 	ri->ri_ops.unpack_attr(cookie, attr, &fg, &bg, NULL);
575 
576 	row *= f->fontheight;
577 	col *= f->fontwidth;
578 	num *= f->fontwidth;
579 
580 	smfb_fillrect(fb, ri->ri_xorigin + col, ri->ri_yorigin + row,
581 	    num, f->fontheight, bg);
582 
583 	return 0;
584 }
585 
586 int
587 smfb_eraserows(void *cookie, int row, int num, uint32_t attr)
588 {
589 	struct rasops_info *ri = cookie;
590 	struct smfb *fb = ri->ri_hw;
591 	struct wsdisplay_font *f = ri->ri_font;
592 	int bg, fg;
593 	int x, y, w;
594 
595 	ri->ri_ops.unpack_attr(cookie, attr, &fg, &bg, NULL);
596 
597 	if ((num == ri->ri_rows) && ISSET(ri->ri_flg, RI_FULLCLEAR)) {
598 		num = ri->ri_height;
599 		x = y = 0;
600 		w = ri->ri_width;
601 	} else {
602 		num *= f->fontheight;
603 		x = ri->ri_xorigin;
604 		y = ri->ri_yorigin + row * f->fontheight;
605 		w = ri->ri_emuwidth;
606 	}
607 	smfb_fillrect(fb, x, y, w, num, bg);
608 
609 	return 0;
610 }
611 
612 int
613 smfb_wait(struct smfb *fb)
614 {
615 	uint32_t reg;
616 	int i;
617 
618 	i = 10000;
619 	while (i-- != 0) {
620 		if (fb->is5xx) {
621 			reg = bus_space_read_4(fb->mmiot, fb->mmioh,
622 			    VOYAGER_SYSTEM_CONTROL);
623 			if ((reg & (VSC_FIFO_EMPTY | VSC_2DENGINE_BUSY)) ==
624 			    VSC_FIFO_EMPTY)
625 				return 0;
626 		} else {
627 			reg = smfb_vgats_read(fb, 0x16);
628 			if ((reg & 0x18) == 0x10)
629 				return 0;
630 		}
631 		delay(1);
632 	}
633 
634 	return EBUSY;
635 }
636 
637 /*
638  * wait for a few panel vertical retrace cycles (5xx only)
639  */
640 void
641 smfb_wait_panel_vsync(struct smfb *fb, int ncycles)
642 {
643 	while (ncycles-- != 0) {
644 		/* wait for end of retrace-in-progress */
645 		while (ISSET(bus_space_read_4(fb->mmiot, fb->mmioh,
646 		    VOYAGER_COMMANDLIST_STATUS), VCS_SP))
647 			delay(10);
648 		/* wait for start of retrace */
649 		while (!ISSET(bus_space_read_4(fb->mmiot, fb->mmioh,
650 		    VOYAGER_COMMANDLIST_STATUS), VCS_SP))
651 			delay(10);
652 	}
653 }
654 
655 /*
656  * vga sequencer access through mmio space (non-5xx only)
657  */
658 
659 uint8_t
660 smfb_vgats_read(struct smfb *fb, uint regno)
661 {
662 	bus_space_write_1(fb->mmiot, fb->mmioh, IO_VGA + VGA_TS_INDEX, regno);
663 	return bus_space_read_1(fb->mmiot, fb->mmioh, IO_VGA + VGA_TS_DATA);
664 }
665 
666 void
667 smfb_vgats_write(struct smfb *fb, uint regno, uint8_t value)
668 {
669 	bus_space_write_1(fb->mmiot, fb->mmioh, IO_VGA + VGA_TS_INDEX, regno);
670 	bus_space_write_1(fb->mmiot, fb->mmioh, IO_VGA + VGA_TS_DATA, value);
671 }
672 
673 /*
674  * Early console code
675  */
676 
677 int smfb_cnattach(bus_space_tag_t, bus_space_tag_t, pcitag_t, pcireg_t);
678 
679 int
680 smfb_cnattach(bus_space_tag_t memt, bus_space_tag_t iot, pcitag_t tag,
681     pcireg_t id)
682 {
683 	uint32_t defattr;
684 	struct rasops_info *ri;
685 	bus_space_handle_t fbh, mmioh;
686 	pcireg_t bar;
687 	int rc, is5xx;
688 
689 	/* filter out unrecognized devices */
690 	switch (id) {
691 	default:
692 		return ENODEV;
693 	case PCI_ID_CODE(PCI_VENDOR_SMI, PCI_PRODUCT_SMI_SM712):
694 		is5xx = 0;
695 		break;
696 	case PCI_ID_CODE(PCI_VENDOR_SMI, PCI_PRODUCT_SMI_SM501):
697 		is5xx = 1;
698 		break;
699 	}
700 
701 	smfbcn.is5xx = is5xx;
702 
703 	bar = pci_conf_read_early(tag, PCI_MAPREG_START);
704 	if (PCI_MAPREG_TYPE(bar) != PCI_MAPREG_TYPE_MEM)
705 		return EINVAL;
706 	rc = bus_space_map(memt, PCI_MAPREG_MEM_ADDR(bar), 1 /* XXX */,
707 	    BUS_SPACE_MAP_LINEAR, &fbh);
708 	if (rc != 0)
709 		return rc;
710 
711 	if (smfbcn.is5xx) {
712 		bar = pci_conf_read_early(tag, PCI_MAPREG_START + 0x04);
713 		if (PCI_MAPREG_TYPE(bar) != PCI_MAPREG_TYPE_MEM)
714 			return EINVAL;
715 		rc = bus_space_map(memt, PCI_MAPREG_MEM_ADDR(bar), 1 /* XXX */,
716 		    BUS_SPACE_MAP_LINEAR, &mmioh);
717 		if (rc != 0)
718 			return rc;
719 	} else {
720 		mmioh = fbh;
721 	}
722 
723 	rc = smfb_setup(&smfbcn, memt, fbh, memt, mmioh);
724 	if (rc != 0)
725 		return rc;
726 
727 	ri = &smfbcn.ri;
728 	ri->ri_ops.pack_attr(ri, 0, 0, 0, &defattr);
729 	wsdisplay_cnattach(&smfbcn.wsd, ri, 0, 0, defattr);
730 
731 	return 0;
732 }
733 
734 int
735 smfb_activate(struct device *self, int act)
736 {
737 	struct smfb_softc *sc = (struct smfb_softc *)self;
738 
739 	switch (act) {
740 	case DVACT_SUSPEND:
741 		smfb_burner(sc, 0, 0);
742 		break;
743 	case DVACT_RESUME:
744 		smfb_burner(sc, 1, 0);
745 		break;
746 	}
747 
748 	return 0;
749 }
750