xref: /openbsd-src/sys/arch/loongson/dev/smfb.c (revision 25c4e8bd056e974b28f4a0ffd39d76c190a56013)
1 /*	$OpenBSD: smfb.c,v 1.21 2022/07/15 17:57:26 kettenis 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->stride = ri->ri_stride;
286 		wdf->offset = 0;
287 		wdf->cmsize = 0;
288 		break;
289 	case WSDISPLAYIO_LINEBYTES:
290 		*(uint *)data = ri->ri_stride;
291 		break;
292 	default:
293 		return -1;
294 	}
295 
296 	return 0;
297 }
298 
299 int
300 smfb_show_screen(void *v, void *cookie, int waitok,
301     void (*cb)(void *, int, int), void *cbarg)
302 {
303 	return 0;
304 }
305 
306 paddr_t
307 smfb_mmap(void *v, off_t offset, int prot)
308 {
309 	struct smfb_softc *sc = (struct smfb_softc *)v;
310 	struct rasops_info *ri = &sc->sc_fb->ri;
311 
312 	if ((offset & PAGE_MASK) != 0)
313 		return -1;
314 
315 	if (offset < 0 || offset >= ri->ri_stride * ri->ri_height)
316 		return -1;
317 
318 	return XKPHYS_TO_PHYS((paddr_t)ri->ri_bits) + offset;
319 }
320 
321 int
322 smfb_load_font(void *v, void *emulcookie, struct wsdisplay_font *font)
323 {
324 	struct smfb_softc *sc = (struct smfb_softc *)v;
325 	struct rasops_info *ri = &sc->sc_fb->ri;
326 
327 	return rasops_load_font(ri, emulcookie, font);
328 }
329 
330 int
331 smfb_list_font(void *v, struct wsdisplay_font *font)
332 {
333 	struct smfb_softc *sc = (struct smfb_softc *)v;
334 	struct rasops_info *ri = &sc->sc_fb->ri;
335 
336 	return rasops_list_font(ri, font);
337 }
338 
339 void
340 smfb_burner(void *v, uint on, uint flg)
341 {
342 	struct smfb_softc *sc = (struct smfb_softc *)v;
343 	struct smfb *fb = sc->sc_fb;
344 
345 	if (fb->is5xx) {
346 		if (on) {
347 			/*
348 			 * Wait for a few cycles after restoring power,
349 			 * to prevent white flickering.
350 			 */
351 			DCR_WRITE(fb, DCR_PANEL_DISPLAY_CONTROL,
352 			    DCR_READ(fb, DCR_PANEL_DISPLAY_CONTROL) | PDC_VDD);
353 			smfb_wait_panel_vsync(fb, 4);
354 			DCR_WRITE(fb, DCR_PANEL_DISPLAY_CONTROL,
355 			    DCR_READ(fb, DCR_PANEL_DISPLAY_CONTROL) | PDC_DATA);
356 			smfb_wait_panel_vsync(fb, 4);
357 			DCR_WRITE(fb, DCR_PANEL_DISPLAY_CONTROL,
358 			    DCR_READ(fb, DCR_PANEL_DISPLAY_CONTROL) |
359 			    (PDC_BIAS | PDC_EN));
360 		} else
361 			DCR_WRITE(fb, DCR_PANEL_DISPLAY_CONTROL,
362 			    DCR_READ(fb, DCR_PANEL_DISPLAY_CONTROL) &
363 			    ~(PDC_EN | PDC_BIAS | PDC_DATA | PDC_VDD));
364 	} else {
365 		if (on) {
366 			smfb_vgats_write(fb, 0x31,
367 			    smfb_vgats_read(fb, 0x31) | 0x01);
368 		} else {
369 			smfb_vgats_write(fb, 0x21,
370 			    smfb_vgats_read(fb, 0x21) | 0x30);
371 			smfb_vgats_write(fb, 0x31,
372 			    smfb_vgats_read(fb, 0x31) & ~0x01);
373 		}
374 	}
375 }
376 
377 /*
378  * Frame buffer initialization.
379  */
380 
381 int
382 smfb_setup(struct smfb *fb, bus_space_tag_t memt, bus_space_handle_t memh,
383     bus_space_tag_t mmiot, bus_space_handle_t mmioh)
384 {
385 	struct rasops_info *ri;
386 	int accel = 0;
387 	int rc;
388 
389 	ri = &fb->ri;
390 	switch (sys_platform->system_type) {
391 	case LOONGSON_EBT700:
392 		ri->ri_width = 800;
393 		ri->ri_height = 480;
394 		break;
395 	case LOONGSON_LYNLOONG:
396 		ri->ri_width = 1368;
397 		ri->ri_height = 768;
398 		break;
399 	default:
400 	case LOONGSON_GDIUM:
401 	case LOONGSON_YEELOONG:
402 		ri->ri_width = 1024;
403 		ri->ri_height = 600;
404 		break;
405 	}
406 	ri->ri_depth = 16;
407 	ri->ri_stride = (ri->ri_width * ri->ri_depth) / 8;
408 	ri->ri_flg = RI_CENTER | RI_CLEAR | RI_FULLCLEAR;
409 	ri->ri_bits = (void *)bus_space_vaddr(memt, memh);
410 	ri->ri_hw = fb;
411 
412 #ifdef __MIPSEL__
413 	/* swap B and R */
414 	ri->ri_rnum = 5;
415 	ri->ri_rpos = 11;
416 	ri->ri_gnum = 6;
417 	ri->ri_gpos = 5;
418 	ri->ri_bnum = 5;
419 	ri->ri_bpos = 0;
420 #endif
421 
422 	rasops_init(ri, 160, 160);
423 
424 	strlcpy(fb->wsd.name, "std", sizeof(fb->wsd.name));
425 	fb->wsd.ncols = ri->ri_cols;
426 	fb->wsd.nrows = ri->ri_rows;
427 	fb->wsd.textops = &ri->ri_ops;
428 	fb->wsd.fontwidth = ri->ri_font->fontwidth;
429 	fb->wsd.fontheight = ri->ri_font->fontheight;
430 	fb->wsd.capabilities = ri->ri_caps;
431 
432 	if (fb->is5xx) {
433 		fb->dcrt = mmiot;
434 		if ((rc = bus_space_subregion(mmiot, mmioh, SM5XX_DCR_BASE,
435 		    SM5XX_DCR_SIZE, &fb->dcrh)) != 0)
436 			return rc;
437 		fb->dprt = mmiot;
438 		if ((rc = bus_space_subregion(mmiot, mmioh, SM5XX_DPR_BASE,
439 		    SMXXX_DPR_SIZE, &fb->dprh)) != 0)
440 			return rc;
441 		fb->mmiot = mmiot;
442 		if ((rc = bus_space_subregion(mmiot, mmioh, SM5XX_MMIO_BASE,
443 		    SM5XX_MMIO_SIZE, &fb->mmioh)) != 0)
444 			return rc;
445 		accel = 1;
446 	} else {
447 		fb->dprt = memt;
448 		if ((rc = bus_space_subregion(memt, memh, SM7XX_DPR_BASE,
449 		    SMXXX_DPR_SIZE, &fb->dprh)) != 0)
450 			return rc;
451 		fb->mmiot = memt;
452 		if ((rc = bus_space_subregion(memt, memh, SM7XX_MMIO_BASE,
453 		    SM7XX_MMIO_SIZE, &fb->mmioh)) != 0)
454 			return rc;
455 		accel = 1;
456 	}
457 
458 	/*
459 	 * Setup 2D acceleration whenever possible
460 	 */
461 
462 	if (accel) {
463 		if (smfb_wait(fb) != 0)
464 			accel = 0;
465 	}
466 	if (accel) {
467 		DPR_WRITE(fb, DPR_CROP_TOPLEFT_COORDS, DPR_COORDS(0, 0));
468 		/* use of width both times is intentional */
469 		DPR_WRITE(fb, DPR_PITCH,
470 		    DPR_COORDS(ri->ri_width, ri->ri_width));
471 		DPR_WRITE(fb, DPR_SRC_WINDOW,
472 		    DPR_COORDS(ri->ri_width, ri->ri_width));
473 		DPR_WRITE(fb, DPR_BYTE_BIT_MASK, 0xffffffff);
474 		DPR_WRITE(fb, DPR_COLOR_COMPARE_MASK, 0);
475 		DPR_WRITE(fb, DPR_COLOR_COMPARE, 0);
476 		DPR_WRITE(fb, DPR_SRC_BASE, 0);
477 		DPR_WRITE(fb, DPR_DST_BASE, 0);
478 		DPR_READ(fb, DPR_DST_BASE);
479 
480 		ri->ri_ops.copycols = smfb_copycols;
481 		ri->ri_ops.copyrows = smfb_copyrows;
482 		ri->ri_ops.erasecols = smfb_erasecols;
483 		ri->ri_ops.eraserows = smfb_eraserows;
484 	}
485 
486 	return 0;
487 }
488 
489 void
490 smfb_copyrect(struct smfb *fb, int sx, int sy, int dx, int dy, int w, int h)
491 {
492 	uint32_t dir;
493 
494 	/* Compute rop direction */
495 	if (sy < dy || (sy == dy && sx <= dx)) {
496 		sx += w - 1;
497 		dx += w - 1;
498 		sy += h - 1;
499 		dy += h - 1;
500 		dir = DE_CTRL_RTOL;
501 	} else
502 		dir = 0;
503 
504 	DPR_WRITE(fb, DPR_SRC_COORDS, DPR_COORDS(sx, sy));
505 	DPR_WRITE(fb, DPR_DST_COORDS, DPR_COORDS(dx, dy));
506 	DPR_WRITE(fb, DPR_SPAN_COORDS, DPR_COORDS(w, h));
507 	DPR_WRITE(fb, DPR_DE_CTRL, DE_CTRL_START | DE_CTRL_ROP_ENABLE | dir |
508 	    (DE_CTRL_COMMAND_BITBLT << DE_CTRL_COMMAND_SHIFT) |
509 	    (DE_CTRL_ROP_SRC << DE_CTRL_ROP_SHIFT));
510 	DPR_READ(fb, DPR_DE_CTRL);
511 
512 	smfb_wait(fb);
513 }
514 
515 void
516 smfb_fillrect(struct smfb *fb, int x, int y, int w, int h, int bg)
517 {
518 	struct rasops_info *ri;
519 
520 	ri = &fb->ri;
521 
522 	DPR_WRITE(fb, DPR_FG_COLOR, ri->ri_devcmap[bg]);
523 	DPR_WRITE(fb, DPR_DST_COORDS, DPR_COORDS(x, y));
524 	DPR_WRITE(fb, DPR_SPAN_COORDS, DPR_COORDS(w, h));
525 	DPR_WRITE(fb, DPR_DE_CTRL, DE_CTRL_START | DE_CTRL_ROP_ENABLE |
526 	    (DE_CTRL_COMMAND_SOLIDFILL << DE_CTRL_COMMAND_SHIFT) |
527 	    (DE_CTRL_ROP_SRC << DE_CTRL_ROP_SHIFT));
528 	DPR_READ(fb, DPR_DE_CTRL);
529 
530 	smfb_wait(fb);
531 }
532 
533 int
534 smfb_copyrows(void *cookie, int src, int dst, int num)
535 {
536 	struct rasops_info *ri = cookie;
537 	struct smfb *fb = ri->ri_hw;
538 	struct wsdisplay_font *f = ri->ri_font;
539 
540 	num *= f->fontheight;
541 	src *= f->fontheight;
542 	dst *= f->fontheight;
543 
544 	smfb_copyrect(fb, ri->ri_xorigin, ri->ri_yorigin + src,
545 	    ri->ri_xorigin, ri->ri_yorigin + dst, ri->ri_emuwidth, num);
546 
547 	return 0;
548 }
549 
550 int
551 smfb_copycols(void *cookie, int row, int src, int dst, int num)
552 {
553 	struct rasops_info *ri = cookie;
554 	struct smfb *fb = ri->ri_hw;
555 	struct wsdisplay_font *f = ri->ri_font;
556 
557 	num *= f->fontwidth;
558 	src *= f->fontwidth;
559 	dst *= f->fontwidth;
560 	row *= f->fontheight;
561 
562 	smfb_copyrect(fb, ri->ri_xorigin + src, ri->ri_yorigin + row,
563 	    ri->ri_xorigin + dst, ri->ri_yorigin + row, num, f->fontheight);
564 
565 	return 0;
566 }
567 
568 int
569 smfb_erasecols(void *cookie, int row, int col, int num, uint32_t attr)
570 {
571 	struct rasops_info *ri = cookie;
572 	struct smfb *fb = ri->ri_hw;
573 	struct wsdisplay_font *f = ri->ri_font;
574 	int bg, fg;
575 
576 	ri->ri_ops.unpack_attr(cookie, attr, &fg, &bg, NULL);
577 
578 	row *= f->fontheight;
579 	col *= f->fontwidth;
580 	num *= f->fontwidth;
581 
582 	smfb_fillrect(fb, ri->ri_xorigin + col, ri->ri_yorigin + row,
583 	    num, f->fontheight, bg);
584 
585 	return 0;
586 }
587 
588 int
589 smfb_eraserows(void *cookie, int row, int num, uint32_t attr)
590 {
591 	struct rasops_info *ri = cookie;
592 	struct smfb *fb = ri->ri_hw;
593 	struct wsdisplay_font *f = ri->ri_font;
594 	int bg, fg;
595 	int x, y, w;
596 
597 	ri->ri_ops.unpack_attr(cookie, attr, &fg, &bg, NULL);
598 
599 	if ((num == ri->ri_rows) && ISSET(ri->ri_flg, RI_FULLCLEAR)) {
600 		num = ri->ri_height;
601 		x = y = 0;
602 		w = ri->ri_width;
603 	} else {
604 		num *= f->fontheight;
605 		x = ri->ri_xorigin;
606 		y = ri->ri_yorigin + row * f->fontheight;
607 		w = ri->ri_emuwidth;
608 	}
609 	smfb_fillrect(fb, x, y, w, num, bg);
610 
611 	return 0;
612 }
613 
614 int
615 smfb_wait(struct smfb *fb)
616 {
617 	uint32_t reg;
618 	int i;
619 
620 	i = 10000;
621 	while (i-- != 0) {
622 		if (fb->is5xx) {
623 			reg = bus_space_read_4(fb->mmiot, fb->mmioh,
624 			    VOYAGER_SYSTEM_CONTROL);
625 			if ((reg & (VSC_FIFO_EMPTY | VSC_2DENGINE_BUSY)) ==
626 			    VSC_FIFO_EMPTY)
627 				return 0;
628 		} else {
629 			reg = smfb_vgats_read(fb, 0x16);
630 			if ((reg & 0x18) == 0x10)
631 				return 0;
632 		}
633 		delay(1);
634 	}
635 
636 	return EBUSY;
637 }
638 
639 /*
640  * wait for a few panel vertical retrace cycles (5xx only)
641  */
642 void
643 smfb_wait_panel_vsync(struct smfb *fb, int ncycles)
644 {
645 	while (ncycles-- != 0) {
646 		/* wait for end of retrace-in-progress */
647 		while (ISSET(bus_space_read_4(fb->mmiot, fb->mmioh,
648 		    VOYAGER_COMMANDLIST_STATUS), VCS_SP))
649 			delay(10);
650 		/* wait for start of retrace */
651 		while (!ISSET(bus_space_read_4(fb->mmiot, fb->mmioh,
652 		    VOYAGER_COMMANDLIST_STATUS), VCS_SP))
653 			delay(10);
654 	}
655 }
656 
657 /*
658  * vga sequencer access through mmio space (non-5xx only)
659  */
660 
661 uint8_t
662 smfb_vgats_read(struct smfb *fb, uint regno)
663 {
664 	bus_space_write_1(fb->mmiot, fb->mmioh, IO_VGA + VGA_TS_INDEX, regno);
665 	return bus_space_read_1(fb->mmiot, fb->mmioh, IO_VGA + VGA_TS_DATA);
666 }
667 
668 void
669 smfb_vgats_write(struct smfb *fb, uint regno, uint8_t value)
670 {
671 	bus_space_write_1(fb->mmiot, fb->mmioh, IO_VGA + VGA_TS_INDEX, regno);
672 	bus_space_write_1(fb->mmiot, fb->mmioh, IO_VGA + VGA_TS_DATA, value);
673 }
674 
675 /*
676  * Early console code
677  */
678 
679 int smfb_cnattach(bus_space_tag_t, bus_space_tag_t, pcitag_t, pcireg_t);
680 
681 int
682 smfb_cnattach(bus_space_tag_t memt, bus_space_tag_t iot, pcitag_t tag,
683     pcireg_t id)
684 {
685 	uint32_t defattr;
686 	struct rasops_info *ri;
687 	bus_space_handle_t fbh, mmioh;
688 	pcireg_t bar;
689 	int rc, is5xx;
690 
691 	/* filter out unrecognized devices */
692 	switch (id) {
693 	default:
694 		return ENODEV;
695 	case PCI_ID_CODE(PCI_VENDOR_SMI, PCI_PRODUCT_SMI_SM712):
696 		is5xx = 0;
697 		break;
698 	case PCI_ID_CODE(PCI_VENDOR_SMI, PCI_PRODUCT_SMI_SM501):
699 		is5xx = 1;
700 		break;
701 	}
702 
703 	smfbcn.is5xx = is5xx;
704 
705 	bar = pci_conf_read_early(tag, PCI_MAPREG_START);
706 	if (PCI_MAPREG_TYPE(bar) != PCI_MAPREG_TYPE_MEM)
707 		return EINVAL;
708 	rc = bus_space_map(memt, PCI_MAPREG_MEM_ADDR(bar), 1 /* XXX */,
709 	    BUS_SPACE_MAP_LINEAR, &fbh);
710 	if (rc != 0)
711 		return rc;
712 
713 	if (smfbcn.is5xx) {
714 		bar = pci_conf_read_early(tag, PCI_MAPREG_START + 0x04);
715 		if (PCI_MAPREG_TYPE(bar) != PCI_MAPREG_TYPE_MEM)
716 			return EINVAL;
717 		rc = bus_space_map(memt, PCI_MAPREG_MEM_ADDR(bar), 1 /* XXX */,
718 		    BUS_SPACE_MAP_LINEAR, &mmioh);
719 		if (rc != 0)
720 			return rc;
721 	} else {
722 		mmioh = fbh;
723 	}
724 
725 	rc = smfb_setup(&smfbcn, memt, fbh, memt, mmioh);
726 	if (rc != 0)
727 		return rc;
728 
729 	ri = &smfbcn.ri;
730 	ri->ri_ops.pack_attr(ri, 0, 0, 0, &defattr);
731 	wsdisplay_cnattach(&smfbcn.wsd, ri, 0, 0, defattr);
732 
733 	return 0;
734 }
735 
736 int
737 smfb_activate(struct device *self, int act)
738 {
739 	struct smfb_softc *sc = (struct smfb_softc *)self;
740 
741 	switch (act) {
742 	case DVACT_SUSPEND:
743 		smfb_burner(sc, 0, 0);
744 		break;
745 	case DVACT_RESUME:
746 		smfb_burner(sc, 1, 0);
747 		break;
748 	}
749 
750 	return 0;
751 }
752