xref: /openbsd-src/sys/arch/loongson/dev/smfb.c (revision 4c1e55dc91edd6e69ccc60ce855900fbc12cf34f)
1 /*	$OpenBSD: smfb.c,v 1.13 2011/07/21 20:36:12 miod 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 or 800x480 resolution, depending on the system model.
24  */
25 
26 #include <sys/param.h>
27 #include <sys/systm.h>
28 #include <sys/device.h>
29 
30 #include <mips64/include/archtype.h>
31 
32 #include <machine/autoconf.h>
33 #include <machine/bus.h>
34 #include <machine/cpu.h>
35 
36 #include <uvm/uvm_extern.h>
37 
38 #include <dev/ic/vgareg.h>
39 #include <dev/isa/isareg.h>
40 #include <dev/pci/pcireg.h>
41 #include <dev/pci/pcivar.h>
42 #include <dev/pci/pcidevs.h>
43 
44 #include <dev/wscons/wsconsio.h>
45 #include <dev/wscons/wsdisplayvar.h>
46 #include <dev/rasops/rasops.h>
47 
48 #include <loongson/dev/voyagerreg.h>
49 #include <loongson/dev/voyagervar.h>
50 #include <loongson/dev/smfbreg.h>
51 
52 struct smfb_softc;
53 
54 /* minimal frame buffer information, suitable for early console */
55 struct smfb {
56 	struct smfb_softc	*sc;
57 	struct rasops_info	ri;
58 	int			is5xx;
59 
60 	/* DPR registers */
61 	bus_space_tag_t		dprt;
62 	bus_space_handle_t	dprh;
63 	/* MMIO space (SM7xx) or control registers (SM5xx) */
64 	bus_space_tag_t		mmiot;
65 	bus_space_handle_t	mmioh;
66 	/* DCR registers (SM5xx) */
67 	bus_space_tag_t		dcrt;
68 	bus_space_handle_t	dcrh;
69 
70 	struct wsscreen_descr	wsd;
71 };
72 
73 #define	DCR_READ(fb, reg) \
74 	bus_space_read_4((fb)->dcrt, (fb)->dcrh, (reg))
75 #define	DCR_WRITE(fb, reg, val) \
76 	bus_space_write_4((fb)->dcrt, (fb)->dcrh, (reg), (val))
77 #define	DPR_READ(fb, reg) \
78 	bus_space_read_4((fb)->dprt, (fb)->dprh, (reg))
79 #define	DPR_WRITE(fb, reg, val) \
80 	bus_space_write_4((fb)->dprt, (fb)->dprh, (reg), (val))
81 
82 struct smfb_softc {
83 	struct device		 sc_dev;
84 	struct smfb		*sc_fb;
85 	struct smfb		 sc_fb_store;
86 
87 	struct wsscreen_list	 sc_wsl;
88 	struct wsscreen_descr	*sc_scrlist[1];
89 	int			 sc_nscr;
90 };
91 
92 int	smfb_pci_match(struct device *, void *, void *);
93 void	smfb_pci_attach(struct device *, struct device *, void *);
94 int	smfb_voyager_match(struct device *, void *, void *);
95 void	smfb_voyager_attach(struct device *, struct device *, void *);
96 int	smfb_activate(struct device *, int);
97 
98 const struct cfattach smfb_pci_ca = {
99 	sizeof(struct smfb_softc), smfb_pci_match, smfb_pci_attach,
100 	NULL, smfb_activate
101 };
102 
103 const struct cfattach smfb_voyager_ca = {
104 	sizeof(struct smfb_softc), smfb_voyager_match, smfb_voyager_attach,
105 	smfb_activate
106 };
107 
108 struct cfdriver smfb_cd = {
109 	NULL, "smfb", DV_DULL
110 };
111 
112 int	smfb_alloc_screen(void *, const struct wsscreen_descr *, void **, int *,
113 	    int *, long *);
114 void	smfb_free_screen(void *, void *);
115 int	smfb_ioctl(void *, u_long, caddr_t, int, struct proc *);
116 int	smfb_show_screen(void *, void *, int, void (*)(void *, int, int),
117 	    void *);
118 paddr_t	smfb_mmap(void *, off_t, int);
119 void	smfb_burner(void *, uint, uint);
120 
121 struct wsdisplay_accessops smfb_accessops = {
122 	smfb_ioctl,
123 	smfb_mmap,
124 	smfb_alloc_screen,
125 	smfb_free_screen,
126 	smfb_show_screen,
127 	NULL,	/* load_font */
128 	NULL,	/* scrollback */
129 	NULL,	/* getchar */
130 	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, long);
142 int	smfb_eraserows(void *, int, int, long);
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 	/* XXX print resolution */
224 	printf("\n");
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, long *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.alloc_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 void
320 smfb_burner(void *v, uint on, uint flg)
321 {
322 	struct smfb_softc *sc = (struct smfb_softc *)v;
323 	struct smfb *fb = sc->sc_fb;
324 
325 	if (fb->is5xx) {
326 		if (on) {
327 			/*
328 			 * Wait for a few cycles after restoring power,
329 			 * to prevent white flickering.
330 			 */
331 			DCR_WRITE(fb, DCR_PANEL_DISPLAY_CONTROL,
332 			    DCR_READ(fb, DCR_PANEL_DISPLAY_CONTROL) | PDC_VDD);
333 			smfb_wait_panel_vsync(fb, 4);
334 			DCR_WRITE(fb, DCR_PANEL_DISPLAY_CONTROL,
335 			    DCR_READ(fb, DCR_PANEL_DISPLAY_CONTROL) | PDC_DATA);
336 			smfb_wait_panel_vsync(fb, 4);
337 			DCR_WRITE(fb, DCR_PANEL_DISPLAY_CONTROL,
338 			    DCR_READ(fb, DCR_PANEL_DISPLAY_CONTROL) |
339 			    (PDC_BIAS | PDC_EN));
340 		} else
341 			DCR_WRITE(fb, DCR_PANEL_DISPLAY_CONTROL,
342 			    DCR_READ(fb, DCR_PANEL_DISPLAY_CONTROL) &
343 			    ~(PDC_EN | PDC_BIAS | PDC_DATA | PDC_VDD));
344 	} else {
345 		if (on) {
346 			smfb_vgats_write(fb, 0x31,
347 			    smfb_vgats_read(fb, 0x31) | 0x01);
348 		} else {
349 			smfb_vgats_write(fb, 0x21,
350 			    smfb_vgats_read(fb, 0x21) | 0x30);
351 			smfb_vgats_write(fb, 0x31,
352 			    smfb_vgats_read(fb, 0x31) & ~0x01);
353 		}
354 	}
355 }
356 
357 /*
358  * Frame buffer initialization.
359  */
360 
361 int
362 smfb_setup(struct smfb *fb, bus_space_tag_t memt, bus_space_handle_t memh,
363     bus_space_tag_t mmiot, bus_space_handle_t mmioh)
364 {
365 	struct rasops_info *ri;
366 	int accel = 0;
367 	int rc;
368 
369 	ri = &fb->ri;
370 	switch (sys_platform->system_type) {
371 	case LOONGSON_EBT700:
372 		ri->ri_width = 800;
373 		ri->ri_height = 480;
374 		break;
375 	default:
376 	case LOONGSON_GDIUM:
377 	case LOONGSON_LYNLOONG:
378 	case LOONGSON_YEELOONG:
379 		ri->ri_width = 1024;
380 		ri->ri_height = 600;
381 		break;
382 	}
383 	ri->ri_depth = 16;
384 	ri->ri_stride = (ri->ri_width * ri->ri_depth) / 8;
385 	ri->ri_flg = RI_CENTER | RI_CLEAR | RI_FULLCLEAR;
386 	ri->ri_bits = (void *)bus_space_vaddr(memt, memh);
387 	ri->ri_hw = fb;
388 
389 #ifdef __MIPSEL__
390 	/* swap B and R */
391 	ri->ri_rnum = 5;
392 	ri->ri_rpos = 11;
393 	ri->ri_gnum = 6;
394 	ri->ri_gpos = 5;
395 	ri->ri_bnum = 5;
396 	ri->ri_bpos = 0;
397 #endif
398 
399 	rasops_init(ri, 160, 160);
400 
401 	strlcpy(fb->wsd.name, "std", sizeof(fb->wsd.name));
402 	fb->wsd.ncols = ri->ri_cols;
403 	fb->wsd.nrows = ri->ri_rows;
404 	fb->wsd.textops = &ri->ri_ops;
405 	fb->wsd.fontwidth = ri->ri_font->fontwidth;
406 	fb->wsd.fontheight = ri->ri_font->fontheight;
407 	fb->wsd.capabilities = ri->ri_caps;
408 
409 	if (fb->is5xx) {
410 		fb->dcrt = mmiot;
411 		if ((rc = bus_space_subregion(mmiot, mmioh, SM5XX_DCR_BASE,
412 		    SM5XX_DCR_SIZE, &fb->dcrh)) != 0)
413 			return rc;
414 		fb->dprt = mmiot;
415 		if ((rc = bus_space_subregion(mmiot, mmioh, SM5XX_DPR_BASE,
416 		    SMXXX_DPR_SIZE, &fb->dprh)) != 0)
417 			return rc;
418 		fb->mmiot = mmiot;
419 		if ((rc = bus_space_subregion(mmiot, mmioh, SM5XX_MMIO_BASE,
420 		    SM5XX_MMIO_SIZE, &fb->mmioh)) != 0)
421 			return rc;
422 		accel = 1;
423 	} else {
424 		fb->dprt = memt;
425 		if ((rc = bus_space_subregion(memt, memh, SM7XX_DPR_BASE,
426 		    SMXXX_DPR_SIZE, &fb->dprh)) != 0)
427 			return rc;
428 		fb->mmiot = memt;
429 		if ((rc = bus_space_subregion(memt, memh, SM7XX_MMIO_BASE,
430 		    SM7XX_MMIO_SIZE, &fb->mmioh)) != 0)
431 			return rc;
432 		accel = 1;
433 	}
434 
435 	/*
436 	 * Setup 2D acceleration whenever possible
437 	 */
438 
439 	if (accel) {
440 		if (smfb_wait(fb) != 0)
441 			accel = 0;
442 	}
443 	if (accel) {
444 		DPR_WRITE(fb, DPR_CROP_TOPLEFT_COORDS, DPR_COORDS(0, 0));
445 		/* use of width both times is intentional */
446 		DPR_WRITE(fb, DPR_PITCH,
447 		    DPR_COORDS(ri->ri_width, ri->ri_width));
448 		DPR_WRITE(fb, DPR_SRC_WINDOW,
449 		    DPR_COORDS(ri->ri_width, ri->ri_width));
450 		DPR_WRITE(fb, DPR_BYTE_BIT_MASK, 0xffffffff);
451 		DPR_WRITE(fb, DPR_COLOR_COMPARE_MASK, 0);
452 		DPR_WRITE(fb, DPR_COLOR_COMPARE, 0);
453 		DPR_WRITE(fb, DPR_SRC_BASE, 0);
454 		DPR_WRITE(fb, DPR_DST_BASE, 0);
455 		DPR_READ(fb, DPR_DST_BASE);
456 
457 		ri->ri_ops.copycols = smfb_copycols;
458 		ri->ri_ops.copyrows = smfb_copyrows;
459 		ri->ri_ops.erasecols = smfb_erasecols;
460 		ri->ri_ops.eraserows = smfb_eraserows;
461 	}
462 
463 	return 0;
464 }
465 
466 void
467 smfb_copyrect(struct smfb *fb, int sx, int sy, int dx, int dy, int w, int h)
468 {
469 	uint32_t dir;
470 
471 	/* Compute rop direction */
472 	if (sy < dy || (sy == dy && sx <= dx)) {
473 		sx += w - 1;
474 		dx += w - 1;
475 		sy += h - 1;
476 		dy += h - 1;
477 		dir = DE_CTRL_RTOL;
478 	} else
479 		dir = 0;
480 
481 	DPR_WRITE(fb, DPR_SRC_COORDS, DPR_COORDS(sx, sy));
482 	DPR_WRITE(fb, DPR_DST_COORDS, DPR_COORDS(dx, dy));
483 	DPR_WRITE(fb, DPR_SPAN_COORDS, DPR_COORDS(w, h));
484 	DPR_WRITE(fb, DPR_DE_CTRL, DE_CTRL_START | DE_CTRL_ROP_ENABLE | dir |
485 	    (DE_CTRL_COMMAND_BITBLT << DE_CTRL_COMMAND_SHIFT) |
486 	    (DE_CTRL_ROP_SRC << DE_CTRL_ROP_SHIFT));
487 	DPR_READ(fb, DPR_DE_CTRL);
488 
489 	smfb_wait(fb);
490 }
491 
492 void
493 smfb_fillrect(struct smfb *fb, int x, int y, int w, int h, int bg)
494 {
495 	struct rasops_info *ri;
496 
497 	ri = &fb->ri;
498 
499 	DPR_WRITE(fb, DPR_FG_COLOR, ri->ri_devcmap[bg]);
500 	DPR_WRITE(fb, DPR_DST_COORDS, DPR_COORDS(x, y));
501 	DPR_WRITE(fb, DPR_SPAN_COORDS, DPR_COORDS(w, h));
502 	DPR_WRITE(fb, DPR_DE_CTRL, DE_CTRL_START | DE_CTRL_ROP_ENABLE |
503 	    (DE_CTRL_COMMAND_SOLIDFILL << DE_CTRL_COMMAND_SHIFT) |
504 	    (DE_CTRL_ROP_SRC << DE_CTRL_ROP_SHIFT));
505 	DPR_READ(fb, DPR_DE_CTRL);
506 
507 	smfb_wait(fb);
508 }
509 
510 int
511 smfb_copyrows(void *cookie, int src, int dst, int num)
512 {
513 	struct rasops_info *ri = cookie;
514 	struct smfb *fb = ri->ri_hw;
515 	struct wsdisplay_font *f = ri->ri_font;
516 
517 	num *= f->fontheight;
518 	src *= f->fontheight;
519 	dst *= f->fontheight;
520 
521 	smfb_copyrect(fb, ri->ri_xorigin, ri->ri_yorigin + src,
522 	    ri->ri_xorigin, ri->ri_yorigin + dst, ri->ri_emuwidth, num);
523 
524 	return 0;
525 }
526 
527 int
528 smfb_copycols(void *cookie, int row, int src, int dst, int num)
529 {
530 	struct rasops_info *ri = cookie;
531 	struct smfb *fb = ri->ri_hw;
532 	struct wsdisplay_font *f = ri->ri_font;
533 
534 	num *= f->fontwidth;
535 	src *= f->fontwidth;
536 	dst *= f->fontwidth;
537 	row *= f->fontheight;
538 
539 	smfb_copyrect(fb, ri->ri_xorigin + src, ri->ri_yorigin + row,
540 	    ri->ri_xorigin + dst, ri->ri_yorigin + row, num, f->fontheight);
541 
542 	return 0;
543 }
544 
545 int
546 smfb_erasecols(void *cookie, int row, int col, int num, long attr)
547 {
548 	struct rasops_info *ri = cookie;
549 	struct smfb *fb = ri->ri_hw;
550 	struct wsdisplay_font *f = ri->ri_font;
551 	int bg, fg;
552 
553 	ri->ri_ops.unpack_attr(cookie, attr, &fg, &bg, NULL);
554 
555 	row *= f->fontheight;
556 	col *= f->fontwidth;
557 	num *= f->fontwidth;
558 
559 	smfb_fillrect(fb, ri->ri_xorigin + col, ri->ri_yorigin + row,
560 	    num, f->fontheight, bg);
561 
562 	return 0;
563 }
564 
565 int
566 smfb_eraserows(void *cookie, int row, int num, long attr)
567 {
568 	struct rasops_info *ri = cookie;
569 	struct smfb *fb = ri->ri_hw;
570 	struct wsdisplay_font *f = ri->ri_font;
571 	int bg, fg;
572 	int x, y, w;
573 
574 	ri->ri_ops.unpack_attr(cookie, attr, &fg, &bg, NULL);
575 
576 	if ((num == ri->ri_rows) && ISSET(ri->ri_flg, RI_FULLCLEAR)) {
577 		num = ri->ri_height;
578 		x = y = 0;
579 		w = ri->ri_width;
580 	} else {
581 		num *= f->fontheight;
582 		x = ri->ri_xorigin;
583 		y = ri->ri_yorigin + row * f->fontheight;
584 		w = ri->ri_emuwidth;
585 	}
586 	smfb_fillrect(fb, x, y, w, num, bg);
587 
588 	return 0;
589 }
590 
591 int
592 smfb_wait(struct smfb *fb)
593 {
594 	uint32_t reg;
595 	int i;
596 
597 	i = 10000;
598 	while (i-- != 0) {
599 		if (fb->is5xx) {
600 			reg = bus_space_read_4(fb->mmiot, fb->mmioh,
601 			    VOYAGER_SYSTEM_CONTROL);
602 			if ((reg & (VSC_FIFO_EMPTY | VSC_2DENGINE_BUSY)) ==
603 			    VSC_FIFO_EMPTY)
604 				return 0;
605 		} else {
606 			reg = smfb_vgats_read(fb, 0x16);
607 			if ((reg & 0x18) == 0x10)
608 				return 0;
609 		}
610 		delay(1);
611 	}
612 
613 	return EBUSY;
614 }
615 
616 /*
617  * wait for a few panel vertical retrace cycles (5xx only)
618  */
619 void
620 smfb_wait_panel_vsync(struct smfb *fb, int ncycles)
621 {
622 	while (ncycles-- != 0) {
623 		/* wait for end of retrace-in-progress */
624 		while (ISSET(bus_space_read_4(fb->mmiot, fb->mmioh,
625 		    VOYAGER_COMMANDLIST_STATUS), VCS_SP))
626 			delay(10);
627 		/* wait for start of retrace */
628 		while (!ISSET(bus_space_read_4(fb->mmiot, fb->mmioh,
629 		    VOYAGER_COMMANDLIST_STATUS), VCS_SP))
630 			delay(10);
631 	}
632 }
633 
634 /*
635  * vga sequencer access through mmio space (non-5xx only)
636  */
637 
638 uint8_t
639 smfb_vgats_read(struct smfb *fb, uint regno)
640 {
641 	bus_space_write_1(fb->mmiot, fb->mmioh, IO_VGA + VGA_TS_INDEX, regno);
642 	return bus_space_read_1(fb->mmiot, fb->mmioh, IO_VGA + VGA_TS_DATA);
643 }
644 
645 void
646 smfb_vgats_write(struct smfb *fb, uint regno, uint8_t value)
647 {
648 	bus_space_write_1(fb->mmiot, fb->mmioh, IO_VGA + VGA_TS_INDEX, regno);
649 	bus_space_write_1(fb->mmiot, fb->mmioh, IO_VGA + VGA_TS_DATA, value);
650 }
651 
652 /*
653  * Early console code
654  */
655 
656 int smfb_cnattach(bus_space_tag_t, bus_space_tag_t, pcitag_t, pcireg_t);
657 
658 int
659 smfb_cnattach(bus_space_tag_t memt, bus_space_tag_t iot, pcitag_t tag,
660     pcireg_t id)
661 {
662 	long defattr;
663 	struct rasops_info *ri;
664 	bus_space_handle_t fbh, mmioh;
665 	pcireg_t bar;
666 	int rc, is5xx;
667 
668 	/* filter out unrecognized devices */
669 	switch (id) {
670 	default:
671 		return ENODEV;
672 	case PCI_ID_CODE(PCI_VENDOR_SMI, PCI_PRODUCT_SMI_SM712):
673 		is5xx = 0;
674 		break;
675 	case PCI_ID_CODE(PCI_VENDOR_SMI, PCI_PRODUCT_SMI_SM501):
676 		is5xx = 1;
677 		break;
678 	}
679 
680 	smfbcn.is5xx = is5xx;
681 
682 	bar = pci_conf_read_early(tag, PCI_MAPREG_START);
683 	if (PCI_MAPREG_TYPE(bar) != PCI_MAPREG_TYPE_MEM)
684 		return EINVAL;
685 	rc = bus_space_map(memt, PCI_MAPREG_MEM_ADDR(bar), 1 /* XXX */,
686 	    BUS_SPACE_MAP_LINEAR, &fbh);
687 	if (rc != 0)
688 		return rc;
689 
690 	if (smfbcn.is5xx) {
691 		bar = pci_conf_read_early(tag, PCI_MAPREG_START + 0x04);
692 		if (PCI_MAPREG_TYPE(bar) != PCI_MAPREG_TYPE_MEM)
693 			return EINVAL;
694 		rc = bus_space_map(memt, PCI_MAPREG_MEM_ADDR(bar), 1 /* XXX */,
695 		    BUS_SPACE_MAP_LINEAR, &mmioh);
696 		if (rc != 0)
697 			return rc;
698 	} else {
699 		mmioh = fbh;
700 	}
701 
702 	rc = smfb_setup(&smfbcn, memt, fbh, memt, mmioh);
703 	if (rc != 0)
704 		return rc;
705 
706 	ri = &smfbcn.ri;
707 	ri->ri_ops.alloc_attr(ri, 0, 0, 0, &defattr);
708 	wsdisplay_cnattach(&smfbcn.wsd, ri, 0, 0, defattr);
709 
710 	return 0;
711 }
712 
713 int
714 smfb_activate(struct device *self, int act)
715 {
716 	struct smfb_softc *sc = (struct smfb_softc *)self;
717 
718 	switch (act) {
719 	case DVACT_SUSPEND:
720 		smfb_burner(sc, 0, 0);
721 		break;
722 	case DVACT_RESUME:
723 		smfb_burner(sc, 1, 0);
724 		break;
725 	}
726 
727 	return 0;
728 }
729