xref: /openbsd-src/sys/dev/pcmcia/cfxga.c (revision 2b0358df1d88d06ef4139321dd05bd5e05d91eaf)
1 /*	$OpenBSD: cfxga.c,v 1.17 2008/11/22 11:35:58 deraadt Exp $	*/
2 
3 /*
4  * Copyright (c) 2005, 2006, Matthieu Herrb and 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 AUTHORS DISCLAIM ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS 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  * Display driver for the Colorgraphic CompactFlash ``VoyagerVGA'' card.
21  * based upon the Epson S1D13806 graphics chip.
22  *
23  * Our goals are:
24  * - to provide a somewhat usable emulation mode for extra text display.
25  * - to let an application (such as an X server) map the controller registers
26  *   in order to do its own display game.
27  *
28  * Driving this card is somewhat a challenge since:
29  * - its video memory is not directly accessible.
30  * - no operation can make use of DMA.
31  */
32 
33 #include <sys/param.h>
34 #include <sys/kernel.h>
35 #include <sys/device.h>
36 #include <sys/systm.h>
37 #include <sys/malloc.h>
38 #include <sys/conf.h>
39 
40 #include <dev/pcmcia/pcmciavar.h>
41 #include <dev/pcmcia/pcmciareg.h>
42 
43 #include <dev/wscons/wsconsio.h>
44 #include <dev/wscons/wsdisplayvar.h>
45 #include <dev/rasops/rasops.h>
46 
47 #include <dev/pcmcia/cfxgareg.h>
48 
49 /*
50 #define CFXGADEBUG
51 #define ENABLE_8BIT_MODES
52 */
53 
54 #ifdef CFXGADEBUG
55 #define	DPRINTF(arg) printf arg
56 #else
57 #define	DPRINTF(arg)
58 #endif
59 
60 struct cfxga_screen;
61 
62 #define	CFXGA_MODE_640x480x16	0
63 #define	CFXGA_MODE_800x600x16	1
64 #ifdef ENABLE_8BIT_MODES
65 #define	CFXGA_MODE_640x480x8	2
66 #define	CFXGA_MODE_800x600x8	3
67 #define	CFXGA_NMODES		4
68 #else
69 #define	CFXGA_NMODES		2
70 #endif
71 
72 struct cfxga_softc {
73 	struct device sc_dev;
74 	struct pcmcia_function *sc_pf;
75 	int	sc_state;
76 #define	CS_MAPPED	0x0001
77 #define	CS_RESET	0x0002
78 
79 	struct pcmcia_mem_handle sc_pmemh;
80 	int sc_memwin;
81 	bus_addr_t sc_offset;
82 
83 	int sc_mode;
84 
85 	int sc_nscreens;
86 	LIST_HEAD(, cfxga_screen) sc_scr;
87 	struct cfxga_screen *sc_active;
88 
89 	/* wsdisplay glue */
90 	struct wsscreen_descr sc_wsd[CFXGA_NMODES];
91 	struct wsscreen_list sc_wsl;
92 	struct wsscreen_descr *sc_scrlist[CFXGA_NMODES];
93 	struct wsdisplay_emulops sc_ops;
94 	struct device *sc_wsdisplay;
95 };
96 
97 int	cfxga_match(struct device *, void *,  void *);
98 void	cfxga_attach(struct device *, struct device *, void *);
99 int	cfxga_detach(struct device *, int);
100 int	cfxga_activate(struct device *, enum devact);
101 
102 struct cfattach cfxga_ca = {
103 	sizeof(struct cfxga_softc), cfxga_match, cfxga_attach,
104 	cfxga_detach, cfxga_activate
105 };
106 
107 struct cfdriver cfxga_cd = {
108 	NULL, "cfxga", DV_DULL
109 };
110 
111 int	cfxga_alloc_screen(void *, const struct wsscreen_descr *, void **,
112 	    int *, int *, long *);
113 void	cfxga_burner(void *, u_int, u_int);
114 void	cfxga_free_screen(void *, void *);
115 int	cfxga_ioctl(void *, u_long, caddr_t, int, struct proc *);
116 paddr_t	cfxga_mmap(void *, off_t, int);
117 int	cfxga_show_screen(void *, void *, int, void (*)(void *, int, int),
118 	    void *);
119 
120 struct wsdisplay_accessops cfxga_accessops = {
121 	cfxga_ioctl,
122 	cfxga_mmap,
123 	cfxga_alloc_screen,
124 	cfxga_free_screen,
125 	cfxga_show_screen,
126 	NULL,
127 	NULL,
128 	NULL,
129 	cfxga_burner
130 };
131 
132 /*
133  * Per-screen structure
134  */
135 
136 struct cfxga_screen {
137 	LIST_ENTRY(cfxga_screen) scr_link;
138 	struct cfxga_softc *scr_sc;	/* parent reference */
139 	struct rasops_info scr_ri;	/* raster op glue */
140 	struct wsdisplay_charcell *scr_mem;	/* backing memory */
141 };
142 
143 void	cfxga_copycols(void *, int, int, int, int);
144 void	cfxga_copyrows(void *, int, int, int);
145 void	cfxga_do_cursor(struct rasops_info *);
146 void	cfxga_erasecols(void *, int, int, int, long);
147 void	cfxga_eraserows(void *, int, int, long);
148 void	cfxga_putchar(void *, int, int, u_int, long);
149 
150 int	cfxga_install_function(struct pcmcia_function *);
151 void	cfxga_remove_function(struct pcmcia_function *);
152 
153 int	cfxga_expand_char(struct cfxga_screen *, u_int, int, int, long);
154 int	cfxga_repaint_screen(struct cfxga_screen *);
155 void	cfxga_reset_video(struct cfxga_softc *);
156 void	cfxga_reset_and_repaint(struct cfxga_softc *);
157 int	cfxga_solid_fill(struct cfxga_screen *, int, int, int, int, int32_t);
158 int	cfxga_standalone_rop(struct cfxga_screen *, u_int,
159 	    int, int, int, int, int, int);
160 int	cfxga_synchronize(struct cfxga_softc *);
161 u_int	cfxga_wait(struct cfxga_softc *, u_int, u_int);
162 
163 #define	cfxga_clear_screen(scr) \
164 	cfxga_solid_fill(scr, 0, 0, scr->scr_ri.ri_width, \
165 	    scr->scr_ri.ri_height, scr->scr_ri.ri_devcmap[WSCOL_BLACK])
166 
167 #define	cfxga_read_1(sc, addr) \
168 	bus_space_read_1((sc)->sc_pmemh.memt, (sc)->sc_pmemh.memh, \
169 	    (sc)->sc_offset + (addr))
170 #define	cfxga_read_2(sc, addr) \
171 	bus_space_read_2((sc)->sc_pmemh.memt, (sc)->sc_pmemh.memh, \
172 	    (sc)->sc_offset + (addr))
173 #define	cfxga_write_1(sc, addr, val) \
174 	bus_space_write_1((sc)->sc_pmemh.memt, (sc)->sc_pmemh.memh, \
175 	    (sc)->sc_offset + (addr), (val))
176 #define	cfxga_write_2(sc, addr, val) \
177 	bus_space_write_2((sc)->sc_pmemh.memt, (sc)->sc_pmemh.memh, \
178 	    (sc)->sc_offset + (addr), (val))
179 
180 #define	cfxga_stop_memory_blt(sc) \
181 	(void)cfxga_read_2(sc, CFREG_BITBLT_DATA)
182 
183 const char *cfxga_modenames[CFXGA_NMODES] = {
184 	"640x480x16",
185 	"800x600x16",
186 #ifdef ENABLE_8BIT_MODES
187 	"640x480x8",
188 	"800x600x8"
189 #endif
190 };
191 
192 /*
193  * This card is very poorly engineered, specificationwise. It does not
194  * provide any CIS information, and has no vendor/product numbers as
195  * well: as such, there is no easy way to differentiate it from any
196  * other cheapo PCMCIA card.
197  *
198  * The best we can do is probe for a chip ID. This is not perfect but better
199  * than matching blindly. Of course this requires us to play some nasty games
200  * behind the PCMCIA framework to be able to do this probe, and correctly fail
201  * if this is not the card we are looking for.
202  *
203  * In shorter words: some card designers ought to be shot, as a service
204  * to the community.
205  */
206 
207 /*
208  * Create the necessary pcmcia function structures to alleviate the lack
209  * of any CIS information on this device.
210  * Actually, we hijack the fake function created by the pcmcia framework.
211  */
212 int
213 cfxga_install_function(struct pcmcia_function *pf)
214 {
215 	struct pcmcia_config_entry *cfe;
216 
217 	/* Get real. */
218 	pf->pf_flags &= ~PFF_FAKE;
219 
220 	/* Tell the pcmcia framework where the CCR is. */
221 	pf->ccr_base = 0x800;
222 	pf->ccr_mask = 0x67;
223 
224 	/* Create a simple cfe. */
225 	cfe = (struct pcmcia_config_entry *)malloc(sizeof *cfe,
226 	    M_DEVBUF, M_NOWAIT | M_ZERO);
227 	if (cfe == NULL) {
228 		DPRINTF(("%s: cfe allocation failed\n", __func__));
229 		return (ENOMEM);
230 	}
231 
232 	cfe->number = 42;	/* have to put some value... */
233 	cfe->flags = PCMCIA_CFE_IO16;
234 	cfe->iftype = PCMCIA_IFTYPE_MEMORY;
235 
236 	SIMPLEQ_INSERT_TAIL(&pf->cfe_head, cfe, cfe_list);
237 
238 	pcmcia_function_init(pf, cfe);
239 	return (0);
240 }
241 
242 /*
243  * Undo the changes done above.
244  * Such a function is necessary since we need a full-blown pcmcia world
245  * set up in order to do the device probe, but if we don't match the card,
246  * leaving this state will cause trouble during other probes.
247  */
248 void
249 cfxga_remove_function(struct pcmcia_function *pf)
250 {
251 	struct pcmcia_config_entry *cfe;
252 
253 	/* we are the first and only entry... */
254 	cfe = SIMPLEQ_FIRST(&pf->cfe_head);
255 	SIMPLEQ_REMOVE_HEAD(&pf->cfe_head, cfe_list);
256 	free(cfe, M_DEVBUF);
257 
258 	/* And we're a figment of the kernel's imagination again. */
259 	pf->pf_flags |= PFF_FAKE;
260 }
261 
262 int
263 cfxga_match(struct device *parent, void *match, void *aux)
264 {
265 	struct pcmcia_attach_args *pa = aux;
266 	struct pcmcia_function *pf = pa->pf;
267 	struct pcmcia_mem_handle h;
268 	int rc;
269 	int win;
270 	bus_addr_t ptr;
271 	u_int8_t id = 0;
272 
273 	if (pa->product != PCMCIA_PRODUCT_INVALID ||
274 	    pa->manufacturer != PCMCIA_VENDOR_INVALID)
275 		return (0);
276 
277 	/* Only a card with no CIS will have a fake function... */
278 	if ((pf->pf_flags & PFF_FAKE) == 0)
279 		return (0);
280 
281 	if (cfxga_install_function(pf) != 0)
282 		return (0);
283 
284 	if (pcmcia_function_enable(pf) != 0) {
285 		DPRINTF(("%s: function enable failed\n"));
286 		return (0);
287 	}
288 
289 	rc = pcmcia_mem_alloc(pf, CFXGA_MEM_RANGE, &h);
290 	if (rc != 0)
291 		goto out;
292 
293 	rc = pcmcia_mem_map(pf, PCMCIA_MEM_ATTR, 0, CFXGA_MEM_RANGE,
294 	    &h, &ptr, &win);
295 	if (rc != 0)
296 		goto out2;
297 
298 	id = (bus_space_read_1(h.memt, h.memh, ptr + CFREG_REV) &
299 	    CR_PRODUCT_MASK) >> CR_PRODUCT_SHIFT;
300 
301 	pcmcia_mem_unmap(pa->pf, win);
302 out2:
303 	pcmcia_mem_free(pa->pf, &h);
304 out:
305 	pcmcia_function_disable(pf);
306 	cfxga_remove_function(pf);
307 
308 	/*
309 	 * Be sure to return a value greater than com's if we match,
310 	 * otherwise it can win due to the way config(8) will order devices...
311 	 */
312 	return (id == PRODUCT_S1D13806 ? 10 : 0);
313 }
314 
315 int
316 cfxga_activate(struct device *dev, enum devact act)
317 {
318 	struct cfxga_softc *sc = (void *)dev;
319 
320 	switch (act) {
321 	case DVACT_ACTIVATE:
322 		if (pcmcia_function_enable(sc->sc_pf) != 0) {
323 			printf("%s: function enable failed\n",
324 			    sc->sc_dev.dv_xname);
325 		} else {
326 			cfxga_reset_and_repaint(sc);
327 		}
328 		break;
329 	case DVACT_DEACTIVATE:
330 		pcmcia_function_disable(sc->sc_pf);
331 		break;
332 	}
333 	return (0);
334 }
335 
336 void
337 cfxga_attach(struct device *parent, struct device *self, void *aux)
338 {
339 	struct cfxga_softc *sc = (void *)self;
340 	struct pcmcia_attach_args *pa = aux;
341 	struct pcmcia_function *pf = pa->pf;
342 	struct wsemuldisplaydev_attach_args waa;
343 	struct wsscreen_descr *wsd;
344 	u_int i;
345 
346 	LIST_INIT(&sc->sc_scr);
347 	sc->sc_nscreens = 0;
348 	sc->sc_pf = pf;
349 
350 	if (cfxga_install_function(pf) != 0) {
351 		printf(": pcmcia function setup failed\n");
352 		return;
353 	}
354 
355 	if (pcmcia_function_enable(pf)) {
356 		printf(": function enable failed\n");
357 		return;
358 	}
359 
360 	if (pcmcia_mem_alloc(pf, CFXGA_MEM_RANGE, &sc->sc_pmemh) != 0) {
361 		printf(": can't allocate memory space\n");
362 		return;
363 	}
364 
365 	if (pcmcia_mem_map(pf, PCMCIA_MEM_ATTR, 0, CFXGA_MEM_RANGE,
366 	    &sc->sc_pmemh, &sc->sc_offset, &sc->sc_memwin) != 0) {
367 		printf(": can't map frame buffer registers\n");
368 		pcmcia_mem_free(pf, &sc->sc_pmemh);
369 		return;
370 	}
371 
372 	SET(sc->sc_state, CS_MAPPED);
373 
374 	printf("\n");
375 
376 	sc->sc_mode = WSDISPLAYIO_MODE_EMUL;
377 
378 	/*
379 	 * We actually defer real initialization to the creation of the
380 	 * first wsdisplay screen, since we do not know which mode to pick
381 	 * yet.
382 	 */
383 
384 	for (wsd = sc->sc_wsd, i = 0; i < CFXGA_NMODES; wsd++, i++) {
385 		strlcpy(wsd->name, cfxga_modenames[i], sizeof(wsd->name));
386 		wsd->textops = &sc->sc_ops;
387 		sc->sc_scrlist[i] = wsd;
388 	}
389 	sc->sc_wsl.nscreens = CFXGA_NMODES;
390 	sc->sc_wsl.screens = (const struct wsscreen_descr **)sc->sc_scrlist;
391 
392 	waa.console = 0;
393 	waa.scrdata = &sc->sc_wsl;
394 	waa.accessops = &cfxga_accessops;
395 	waa.accesscookie = sc;
396 	waa.defaultscreens = 1;
397 
398 	if ((sc->sc_wsdisplay =
399 	    config_found(self, &waa, wsemuldisplaydevprint)) == NULL) {
400 		/* otherwise wscons will do this */
401 		if (sc->sc_active != NULL)
402 			cfxga_clear_screen(sc->sc_active);
403 		else
404 			cfxga_burner(sc, 0, 0);
405 	}
406 }
407 
408 int
409 cfxga_detach(struct device *dev, int flags)
410 {
411 	struct cfxga_softc *sc = (void *)dev;
412 
413 	/*
414 	 * Detach all children, and hope wsdisplay detach code is correct...
415 	 */
416 	if (sc->sc_wsdisplay != NULL) {
417 		config_detach(sc->sc_wsdisplay, DETACH_FORCE);
418 		/* sc->sc_wsdisplay = NULL; */
419 	}
420 
421 	if (ISSET(sc->sc_state, CS_MAPPED)) {
422 		pcmcia_mem_unmap(sc->sc_pf, sc->sc_memwin);
423 		pcmcia_mem_free(sc->sc_pf, &sc->sc_pmemh);
424 		/* CLR(sc->sc_state, CS_MAPPED); */
425 	}
426 
427 	return (0);
428 }
429 
430 /*
431  * Wscons operations
432  */
433 
434 int
435 cfxga_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep,
436     int *curxp, int *curyp, long *attrp)
437 {
438 	struct cfxga_softc *sc = v;
439 	struct cfxga_screen *scr;
440 	struct rasops_info *ri;
441 	u_int mode, width, height, depth, scrsize;
442 
443 	scr = malloc(sizeof *scr, M_DEVBUF,
444 	    (cold ? M_NOWAIT : M_WAITOK) | M_ZERO);
445 	if (scr == NULL)
446 		return (ENOMEM);
447 
448 	mode = type - sc->sc_wsd;
449 #ifdef DIAGNOSTIC
450 	if (mode >= CFXGA_NMODES)
451 		mode = CFXGA_MODE_640x480x16;
452 #endif
453 	switch (mode) {
454 	default:
455 	case CFXGA_MODE_640x480x16:
456 		width = 640;
457 		height = 480;
458 		depth = 16;
459 		break;
460 	case CFXGA_MODE_800x600x16:
461 		width = 800;
462 		height = 600;
463 		depth = 16;
464 		break;
465 #ifdef ENABLE_8BIT_MODES
466 	case CFXGA_MODE_640x480x8:
467 		width = 640;
468 		height = 480;
469 		depth = 8;
470 		break;
471 	case CFXGA_MODE_800x600x8:
472 		width = 800;
473 		height = 600;
474 		depth = 8;
475 		break;
476 #endif
477 	}
478 
479 	ri = &scr->scr_ri;
480 	ri->ri_hw = (void *)scr;
481 	ri->ri_bits = NULL;
482 	ri->ri_depth = depth;
483 	ri->ri_width = width;
484 	ri->ri_height = height;
485 	ri->ri_stride = width * depth / 8;
486 	ri->ri_flg = 0;
487 
488 	/* swap B and R at 16 bpp */
489 	if (depth == 16) {
490 		ri->ri_rnum = 5;
491 		ri->ri_rpos = 11;
492 		ri->ri_gnum = 6;
493 		ri->ri_gpos = 5;
494 		ri->ri_bnum = 5;
495 		ri->ri_bpos = 0;
496 	}
497 
498 	if (type->nrows == 0)	/* first screen creation */
499 		rasops_init(ri, 100, 100);
500 	else
501 		rasops_init(ri, type->nrows, type->ncols);
502 
503 	/*
504 	 * Allocate backing store to remember non-visible screen contents in
505 	 * emulation mode.
506 	 */
507 	scrsize = ri->ri_rows * ri->ri_cols * sizeof(struct wsdisplay_charcell);
508 	scr->scr_mem = malloc(scrsize, M_DEVBUF,
509 	    (cold ? M_NOWAIT : M_WAITOK) | M_ZERO);
510 	if (scr->scr_mem == NULL) {
511 		free(scr, M_DEVBUF);
512 		return (ENOMEM);
513 	}
514 
515 	ri->ri_ops.copycols = cfxga_copycols;
516 	ri->ri_ops.copyrows = cfxga_copyrows;
517 	ri->ri_ops.erasecols = cfxga_erasecols;
518 	ri->ri_ops.eraserows = cfxga_eraserows;
519 	ri->ri_ops.putchar = cfxga_putchar;
520 	ri->ri_do_cursor = cfxga_do_cursor;
521 
522 	/*
523 	 * Finish initializing our screen descriptions, now that we know
524 	 * the actual console emulation parameters.
525 	 */
526 	if (type->nrows == 0) {
527 		struct wsscreen_descr *wsd = (struct wsscreen_descr *)type;
528 
529 		wsd->nrows = ri->ri_rows;
530 		wsd->ncols = ri->ri_cols;
531 		bcopy(&ri->ri_ops, &sc->sc_ops, sizeof(sc->sc_ops));
532 		wsd->fontwidth = ri->ri_font->fontwidth;
533 		wsd->fontheight = ri->ri_font->fontheight;
534 		wsd->capabilities = ri->ri_caps;
535 	}
536 
537 	scr->scr_sc = sc;
538 	LIST_INSERT_HEAD(&sc->sc_scr, scr, scr_link);
539 	sc->sc_nscreens++;
540 
541 	ri->ri_ops.alloc_attr(ri, 0, 0, 0, attrp);
542 
543 	*cookiep = ri;
544 	*curxp = *curyp = 0;
545 
546 	return (0);
547 }
548 
549 void
550 cfxga_burner(void *v, u_int on, u_int flags)
551 {
552 	struct cfxga_softc *sc = (void *)v;
553 	u_int8_t mode;
554 
555 	mode = cfxga_read_1(sc, CFREG_MODE) & LCD_MODE_SWIVEL_BIT_0;
556 
557 	if (on)
558 		cfxga_write_1(sc, CFREG_MODE, mode | MODE_CRT);
559 	else
560 		cfxga_write_1(sc, CFREG_MODE, mode | MODE_NO_DISPLAY);
561 }
562 
563 void
564 cfxga_free_screen(void *v, void *cookie)
565 {
566 	struct cfxga_softc *sc = v;
567 	struct rasops_info *ri = cookie;
568 	struct cfxga_screen *scr = ri->ri_hw;
569 
570 	LIST_REMOVE(scr, scr_link);
571 	sc->sc_nscreens--;
572 
573 	if (scr == sc->sc_active) {
574 		sc->sc_active = NULL;
575 		cfxga_burner(sc, 0, 0);
576 	}
577 
578 	free(scr->scr_mem, M_DEVBUF);
579 	free(scr, M_DEVBUF);
580 }
581 
582 int
583 cfxga_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
584 {
585 	struct cfxga_softc *sc = v;
586 	struct cfxga_screen *scr;
587 	struct wsdisplay_fbinfo *wdf;
588 	int mode;
589 
590 	switch (cmd) {
591 	case WSDISPLAYIO_GTYPE:
592 		*(u_int *)data = WSDISPLAY_TYPE_CFXGA;
593 		break;
594 
595 	case WSDISPLAYIO_GINFO:
596 		wdf = (struct wsdisplay_fbinfo *)data;
597 		scr = sc->sc_active;
598 		if (scr == NULL) {
599 			/* try later...after running wsconscfg to add screens */
600 			wdf->height = wdf->width = wdf->depth = wdf->cmsize = 0;
601 		} else {
602 			wdf->height = scr->scr_ri.ri_height;
603 			wdf->width = scr->scr_ri.ri_width;
604 			wdf->depth = scr->scr_ri.ri_depth;
605 			wdf->cmsize = scr->scr_ri.ri_depth <= 8 ?
606 			    (1 << scr->scr_ri.ri_depth) : 0;
607 		}
608 		break;
609 
610 	case WSDISPLAYIO_SMODE:
611 		mode = *(u_int *)data;
612 		if (mode == sc->sc_mode)
613 			break;
614 		switch (mode) {
615 		case WSDISPLAYIO_MODE_EMUL:
616 			cfxga_reset_and_repaint(sc);
617 			break;
618 		case WSDISPLAYIO_MODE_MAPPED:
619 			break;
620 		default:
621 			return (EINVAL);
622 		}
623 		sc->sc_mode = mode;
624 		break;
625 
626 	/* these operations are handled by the wscons code... */
627 	case WSDISPLAYIO_GVIDEO:
628 	case WSDISPLAYIO_SVIDEO:
629 		break;
630 
631 	/* these operations are not supported... */
632 	case WSDISPLAYIO_GETCMAP:
633 	case WSDISPLAYIO_PUTCMAP:
634 	case WSDISPLAYIO_LINEBYTES:
635 	case WSDISPLAYIO_GCURPOS:
636 	case WSDISPLAYIO_SCURPOS:
637 	case WSDISPLAYIO_GCURMAX:
638 	case WSDISPLAYIO_GCURSOR:
639 	case WSDISPLAYIO_SCURSOR:
640 	default:
641 		return (-1);
642 	}
643 
644 	return (0);
645 }
646 
647 paddr_t
648 cfxga_mmap(void *v, off_t off, int prot)
649 {
650 	return (-1);
651 }
652 
653 int
654 cfxga_show_screen(void *v, void *cookie, int waitok,
655     void (*cb)(void *, int, int), void *cbarg)
656 {
657 	struct cfxga_softc *sc = v;
658 	struct rasops_info *ri = cookie;
659 	struct cfxga_screen *scr = ri->ri_hw, *old;
660 
661 	old = sc->sc_active;
662 	if (old == scr)
663 		return (0);
664 
665 	sc->sc_active = scr;
666 	cfxga_reset_and_repaint(sc);	/* will turn video on if scr != NULL */
667 
668 	return (0);
669 }
670 
671 /*
672  * Real frame buffer operations
673  */
674 
675 void
676 cfxga_reset_video(struct cfxga_softc *sc)
677 {
678 	struct cfxga_screen *scr = sc->sc_active;
679 	struct rasops_info *ri;
680 #ifdef ENABLE_8BIT_MODES
681 	const u_int8_t *cmap;
682 	u_int i;
683 #endif
684 
685 	/*
686 	 * Reset controller
687 	 */
688 
689 	/* need to write to both REV and MISC at the same time */
690 	cfxga_write_2(sc, CFREG_REV, 0x80 | (CM_REGSEL << 8));
691 	delay(25000);	/* maintain reset for a short while */
692 	/* need to write to both REV and MISC at the same time */
693 	cfxga_write_2(sc, CFREG_REV, 0 | (CM_MEMSEL << 8));
694 	delay(25000);
695 	/* stop any pending blt operation */
696 	cfxga_write_2(sc, CFREG_BITBLT_CONTROL, 0);
697 	cfxga_stop_memory_blt(sc);
698 	cfxga_write_1(sc, CFREG_MODE, 0);	/* disable all displays */
699 
700 	/*
701 	 * Setup common video mode parameters.
702 	 */
703 
704 	cfxga_write_2(sc, CFREG_MEMCLK, MEMCLK_SRC_CLK3);
705 #if 0
706 	cfxga_write_1(sc, CFREG_LCD_PCLK, LCD_PCLK_SRC_CLKI | LCD_PCLK_DIV_1);
707 	cfxga_write_1(sc, CFREG_MPLUG_CLK,
708 	    MPLUG_PCLK_SRC_CLKI2 | MPLUG_PCLK_DIV_1);
709 #endif
710 	cfxga_write_2(sc, CFREG_CRTTV_PCLK, CRT_PCLK_SRC_CLKI | CRT_PCLK_DIV_1);
711 	cfxga_write_2(sc, CFREG_WSTATE, WSTATE_MCLK);
712 
713 	/* MEMCNF and DRAM_RFRSH need to be programmed at the same time */
714 	cfxga_write_2(sc, CFREG_MEMCNF,
715 	    MEMCNF_SDRAM_INIT | (DRAM_RFRSH_50MHZ << 8));
716 	delay(250);
717 	cfxga_write_2(sc, CFREG_DRAM_TIMING, DRAM_TIMING_50MHZ);
718 
719 	/*
720 	 * Setup mode-dependent parameters.
721 	 */
722 	if (scr == NULL)
723 		return;
724 
725 	ri = &scr->scr_ri;
726 	switch (scr->scr_ri.ri_width) {
727 	default:
728 	case 640:
729 		cfxga_write_1(sc, CFREG_CRT_HWIDTH, (640 / 8) - 1);
730 		/* HNDISP and HSTART need to be programmed at the same time */
731 		cfxga_write_2(sc, CFREG_CRT_HNDISP, 23 | (2 << 8));
732 		cfxga_write_1(sc, CFREG_CRT_HPULSE, 4);
733 		cfxga_write_2(sc, CFREG_CRT_VHEIGHT, 480 - 1);
734 		/* VNDISP and VSTART need to be programmed at the same time */
735 		cfxga_write_2(sc, CFREG_CRT_VNDISP, 39 | (8 << 8));
736 		cfxga_write_1(sc, CFREG_CRT_VPULSE, 2);
737 		break;
738 	case 800:
739 		cfxga_write_1(sc, CFREG_CRT_HWIDTH, (800 / 8) - 1);
740 		/* HNDISP and HSTART need to be programmed at the same time */
741 		cfxga_write_2(sc, CFREG_CRT_HNDISP, 27 | (2 << 8));
742 		cfxga_write_1(sc, CFREG_CRT_HPULSE, 4);
743 		cfxga_write_2(sc, CFREG_CRT_VHEIGHT, 600 - 1);
744 		/* VNDISP and VSTART need to be programmed at the same time */
745 		cfxga_write_2(sc, CFREG_CRT_VNDISP, 25 | (8 << 8));
746 		cfxga_write_1(sc, CFREG_CRT_VPULSE, 2);
747 		break;
748 	}
749 	cfxga_write_1(sc, CFREG_CRT_MODE,
750 	    ri->ri_depth == 16 ? CRT_MODE_16BPP : CRT_MODE_8BPP);
751 	cfxga_write_2(sc, CFREG_CRT_START_LOW, 0);
752 	cfxga_write_1(sc, CFREG_CRT_START_HIGH, 0);
753 	cfxga_write_2(sc, CFREG_CRT_MEMORY, ri->ri_width * ri->ri_depth / 16);
754 	cfxga_write_1(sc, CFREG_CRT_PANNING, 0);
755 	cfxga_write_1(sc, CFREG_CRT_FIFO_THRESHOLD_HIGH, 0);
756 	cfxga_write_1(sc, CFREG_CRT_FIFO_THRESHOLD_LOW, 0);
757 	cfxga_write_1(sc, CFREG_CRT_CURSOR_CONTROL, CURSOR_INACTIVE);
758 
759 #ifdef ENABLE_8BIT_MODES
760 	/*
761 	 * On 8bpp video modes, program the LUT
762 	 */
763 	if (ri->ri_depth == 8) {
764 #if 0
765 		/* Wait for retrace */
766 		while ((cfxga_read_1(sc, CFREG_CRT_VNDISP) &
767 		    CRT_VNDISP_STATUS) == 0)
768 			delay(1);
769 #endif
770 		cfxga_write_1(sc, CFREG_LUT_MODE, LUT_CRT);
771 		cfxga_write_1(sc, CFREG_LUT_ADDRESS, 0); /* autoincrements */
772 		cmap = rasops_cmap;
773 		for (i = 256 * 3; i != 0; i--)
774 			cfxga_write_1(sc, CFREG_LUT_DATA, *cmap++ & 0xf0);
775 	}
776 #endif
777 
778 	cfxga_write_1(sc, CFREG_TV_CONTROL,
779 	    TV_LUMINANCE_FILTER | TV_SVIDEO_OUTPUT | TV_NTSC_OUTPUT);
780 
781 	cfxga_write_1(sc, CFREG_POWER_CONF, POWERSAVE_MBO);
782 	cfxga_write_1(sc, CFREG_WATCHDOG, 0);
783 
784 	cfxga_write_1(sc, CFREG_MODE, MODE_CRT);
785 	delay(25000);
786 }
787 
788 void
789 cfxga_reset_and_repaint(struct cfxga_softc *sc)
790 {
791 	cfxga_reset_video(sc);
792 
793 	if (sc->sc_active != NULL)
794 		cfxga_repaint_screen(sc->sc_active);
795 	else
796 		cfxga_burner(sc, 0, 0);
797 }
798 
799 /*
800  * Wait for the blitter to be in a given state.
801  */
802 u_int
803 cfxga_wait(struct cfxga_softc *sc, u_int mask, u_int result)
804 {
805 	u_int tries;
806 
807 	for (tries = 10000; tries != 0; tries--) {
808 		if ((cfxga_read_1(sc, CFREG_BITBLT_CONTROL) & mask) == result)
809 			break;
810 		delay(10);
811 	}
812 
813 	return (tries);
814 }
815 
816 /*
817  * Wait for all pending blitter operations to be complete.
818  * Returns non-zero if the blitter got stuck.
819  */
820 int
821 cfxga_synchronize(struct cfxga_softc *sc)
822 {
823 	/* Wait for previous operations to complete */
824 	if (cfxga_wait(sc, BITBLT_ACTIVE, 0) == 0) {
825 		DPRINTF(("%s: not ready\n", __func__));
826 		if (ISSET(sc->sc_state, CS_RESET))
827 			return (EAGAIN);
828 		else {
829 			DPRINTF(("%s: resetting...\n", sc->sc_dev.dv_xname));
830 			SET(sc->sc_state, CS_RESET);
831 			cfxga_reset_and_repaint(sc);
832 			CLR(sc->sc_state, CS_RESET);
833 		}
834 	}
835 	cfxga_stop_memory_blt(sc);
836 	return (0);
837 }
838 
839 /*
840  * Display a character.
841  */
842 int
843 cfxga_expand_char(struct cfxga_screen *scr, u_int uc, int x, int y, long attr)
844 {
845 	struct cfxga_softc *sc = scr->scr_sc;
846 	struct rasops_info *ri = &scr->scr_ri;
847 	struct wsdisplay_font *font = ri->ri_font;
848 	u_int pos, sts, fifo_avail, chunk;
849 	u_int8_t *fontbits;
850 	int bg, fg, ul;
851 	u_int i;
852 	int rc;
853 
854 	pos = (y * ri->ri_width + x) * ri->ri_depth / 8;
855 	fontbits = (u_int8_t *)(font->data + (uc - font->firstchar) *
856 	    ri->ri_fontscale);
857 	ri->ri_ops.unpack_attr(ri, attr, &fg, &bg, &ul);
858 
859 	/* Wait for previous operations to complete */
860 	if ((rc = cfxga_synchronize(sc)) != 0)
861 		return (rc);
862 
863 	cfxga_write_2(sc, CFREG_COLOR_EXPANSION,
864 	    ((font->fontwidth - 1) & 7) | (OP_COLOR_EXPANSION << 8));
865 	cfxga_write_2(sc, CFREG_BITBLT_SRC_LOW, font->fontwidth <= 8 ? 0 : 1);
866 	cfxga_write_2(sc, CFREG_BITBLT_SRC_HIGH, 0);
867 	cfxga_write_2(sc, CFREG_BITBLT_DST_LOW, pos);
868 	cfxga_write_2(sc, CFREG_BITBLT_DST_HIGH, pos >> 16);
869 	cfxga_write_2(sc, CFREG_BITBLT_OFFSET,
870 	    ri->ri_width * ri->ri_depth / 16);
871 	cfxga_write_2(sc, CFREG_BITBLT_WIDTH, font->fontwidth - 1);
872 	cfxga_write_2(sc, CFREG_BITBLT_HEIGHT, font->fontheight - 1);
873 	cfxga_write_2(sc, CFREG_BITBLT_FG, ri->ri_devcmap[fg]);
874 	cfxga_write_2(sc, CFREG_BITBLT_BG, ri->ri_devcmap[bg]);
875 	cfxga_write_2(sc, CFREG_BITBLT_CONTROL, BITBLT_ACTIVE |
876 	    (ri->ri_depth > 8 ? BITBLT_COLOR_16 : BITBLT_COLOR_8));
877 
878 	if (cfxga_wait(sc, BITBLT_ACTIVE, BITBLT_ACTIVE) == 0)
879 		goto fail;	/* unlikely */
880 	fifo_avail = 0;
881 
882 	for (i = font->fontheight; i != 0; i--) {
883 		/*
884 		 * Find out how much words we can feed before
885 		 * a FIFO check is needed.
886 		 */
887 		if (fifo_avail == 0) {
888 			sts = cfxga_read_1(sc, CFREG_BITBLT_CONTROL);
889 			if ((sts & BITBLT_FIFO_NOT_EMPTY) == 0)
890 				fifo_avail = font->fontwidth <= 8 ? 2 : 1;
891 			else if ((sts & BITBLT_FIFO_HALF_FULL) == 0)
892 				fifo_avail = font->fontwidth <= 8 ? 1 : 0;
893 			else {
894 				/*
895 				 * Let the cheap breathe for a short while.
896 				 * If this is not enough to free some FIFO
897 				 * entries, abort the operation.
898 				 */
899 				if (cfxga_wait(sc, BITBLT_FIFO_FULL, 0) == 0)
900 					goto fail;
901 			}
902 		}
903 
904 		if (font->fontwidth <= 8) {
905 			chunk = *fontbits;
906 			if (ul && i == 1)
907 				chunk = 0xff;
908 		} else {
909 			chunk = *(u_int16_t *)fontbits;
910 			if (ul && i == 1)
911 				chunk = 0xffff;
912 		}
913 		cfxga_write_2(sc, CFREG_BITBLT_DATA, chunk);
914 		fontbits += font->stride;
915 		fifo_avail--;
916 	}
917 
918 	return (0);
919 
920 fail:
921 	DPRINTF(("%s: abort\n", __func__));
922 	cfxga_write_2(sc, CFREG_BITBLT_CONTROL, 0);
923 	cfxga_stop_memory_blt(sc);
924 	return (EINTR);
925 }
926 
927 /*
928  * Copy a memory bitmap to the frame buffer.
929  *
930  * This is slow - we only use this to repaint the whole frame buffer on
931  * screen switches.
932  */
933 int
934 cfxga_repaint_screen(struct cfxga_screen *scr)
935 {
936 	struct wsdisplay_charcell *cell = scr->scr_mem;
937 	struct rasops_info *ri = &scr->scr_ri;
938 	int x, y, cx, cy, lx, ly;
939 	int fg, bg;
940 	int rc;
941 
942 	cfxga_clear_screen(scr);
943 
944 	cx = ri->ri_font->fontwidth;
945 	cy = ri->ri_font->fontheight;
946 
947 	for (ly = 0, y = ri->ri_yorigin; ly < ri->ri_rows; ly++, y += cy) {
948 		for (lx = 0, x = ri->ri_xorigin; lx < ri->ri_cols;
949 		    lx++, x += cx) {
950 			if (cell->uc == 0 || cell->uc == ' ') {
951 				ri->ri_ops.unpack_attr(ri, cell->attr,
952 				    &fg, &bg, NULL);
953 				rc = cfxga_solid_fill(scr, x, y, cx, cy,
954 				    ri->ri_devcmap[bg]);
955 			} else {
956 				rc = cfxga_expand_char(scr, cell->uc,
957 				    x, y, cell->attr);
958 			}
959 			cell++;
960 			if (rc != 0)
961 				return (rc);
962 		}
963 	}
964 
965 	return (0);
966 }
967 
968 /*
969  * Perform a solid fill operation.
970  */
971 int
972 cfxga_solid_fill(struct cfxga_screen *scr, int x, int y, int cx, int cy,
973     int32_t srccolor)
974 {
975 	struct cfxga_softc *sc = scr->scr_sc;
976 	struct rasops_info *ri = &scr->scr_ri;
977 	u_int pos;
978 	int rc;
979 
980 	pos = (y * ri->ri_width + x) * ri->ri_depth / 8;
981 
982 	/* Wait for previous operations to complete */
983 	if ((rc = cfxga_synchronize(sc)) != 0)
984 		return (rc);
985 
986 	cfxga_write_2(sc, CFREG_BITBLT_ROP, 0 | (OP_SOLID_FILL << 8));
987 	cfxga_write_2(sc, CFREG_BITBLT_SRC_LOW, pos);
988 	cfxga_write_2(sc, CFREG_BITBLT_SRC_HIGH, pos >> 16);
989 	cfxga_write_2(sc, CFREG_BITBLT_DST_LOW, pos);
990 	cfxga_write_2(sc, CFREG_BITBLT_DST_HIGH, pos >> 16);
991 	cfxga_write_2(sc, CFREG_BITBLT_OFFSET,
992 	    ri->ri_width * ri->ri_depth / 16);
993 	cfxga_write_2(sc, CFREG_BITBLT_WIDTH, cx - 1);
994 	cfxga_write_2(sc, CFREG_BITBLT_HEIGHT, cy - 1);
995 	cfxga_write_2(sc, CFREG_BITBLT_FG, (u_int16_t)srccolor);
996 	cfxga_write_2(sc, CFREG_BITBLT_CONTROL, BITBLT_ACTIVE |
997 	    (ri->ri_depth > 8 ? BITBLT_COLOR_16 : BITBLT_COLOR_8));
998 
999 	return (0);
1000 }
1001 
1002 /*
1003  * Perform an internal frame buffer operation.
1004  */
1005 int
1006 cfxga_standalone_rop(struct cfxga_screen *scr, u_int rop, int sx, int sy,
1007     int dx, int dy, int cx, int cy)
1008 {
1009 	struct cfxga_softc *sc = scr->scr_sc;
1010 	struct rasops_info *ri = &scr->scr_ri;
1011 	u_int srcpos, dstpos;
1012 	u_int opcode;
1013 	int rc;
1014 
1015 	srcpos = (sy * ri->ri_width + sx) * ri->ri_depth / 8;
1016 	dstpos = (dy * ri->ri_width + dx) * ri->ri_depth / 8;
1017 
1018 	if (dstpos <= srcpos)
1019 		opcode = (OP_MOVE_POSITIVE_ROP << 8) | rop;
1020 	else
1021 		opcode = (OP_MOVE_NEGATIVE_ROP << 8) | rop;
1022 
1023 	/* Wait for previous operations to complete */
1024 	if ((rc = cfxga_synchronize(sc)) != 0)
1025 		return (rc);
1026 
1027 	cfxga_write_2(sc, CFREG_BITBLT_ROP, opcode);
1028 	cfxga_write_2(sc, CFREG_BITBLT_SRC_LOW, srcpos);
1029 	cfxga_write_2(sc, CFREG_BITBLT_SRC_HIGH, srcpos >> 16);
1030 	cfxga_write_2(sc, CFREG_BITBLT_DST_LOW, dstpos);
1031 	cfxga_write_2(sc, CFREG_BITBLT_DST_HIGH, dstpos >> 16);
1032 	cfxga_write_2(sc, CFREG_BITBLT_OFFSET,
1033 	    ri->ri_width * ri->ri_depth / 16);
1034 	cfxga_write_2(sc, CFREG_BITBLT_WIDTH, cx - 1);
1035 	cfxga_write_2(sc, CFREG_BITBLT_HEIGHT, cy - 1);
1036 	cfxga_write_2(sc, CFREG_BITBLT_CONTROL, BITBLT_ACTIVE |
1037 	    (ri->ri_depth > 8 ? BITBLT_COLOR_16 : BITBLT_COLOR_8));
1038 
1039 	return (0);
1040 }
1041 
1042 /*
1043  * Text console raster operations.
1044  *
1045  * We shadow all these operations on a memory copy of the frame buffer.
1046  * Since we are running in emulation mode only, this could be optimized
1047  * by only storing actual character cell values (a la mda).
1048  */
1049 
1050 void
1051 cfxga_copycols(void *cookie, int row, int src, int dst, int num)
1052 {
1053 	struct rasops_info *ri = cookie;
1054 	struct cfxga_screen *scr = ri->ri_hw;
1055 	int sx, dx, y, cx, cy;
1056 
1057 	/* Copy columns in backing store. */
1058 	ovbcopy(scr->scr_mem + row * ri->ri_cols + src,
1059 	    scr->scr_mem + row * ri->ri_cols + dst,
1060 	    num * sizeof(struct wsdisplay_charcell));
1061 
1062 	if (scr != scr->scr_sc->sc_active)
1063 		return;
1064 
1065 	sx = src * ri->ri_font->fontwidth + ri->ri_xorigin;
1066 	dx = dst * ri->ri_font->fontwidth + ri->ri_xorigin;
1067 	y = row * ri->ri_font->fontheight + ri->ri_yorigin;
1068 	cx = num * ri->ri_font->fontwidth;
1069 	cy = ri->ri_font->fontheight;
1070 	cfxga_standalone_rop(scr, ROP_SRC, sx, y, dx, y, cx, cy);
1071 }
1072 
1073 void
1074 cfxga_copyrows(void *cookie, int src, int dst, int num)
1075 {
1076 	struct rasops_info *ri = cookie;
1077 	struct cfxga_screen *scr = ri->ri_hw;
1078 	int x, sy, dy, cx, cy;
1079 
1080 	/* Copy rows in backing store. */
1081 	ovbcopy(scr->scr_mem + src * ri->ri_cols,
1082 	    scr->scr_mem + dst * ri->ri_cols,
1083 	    num * ri->ri_cols * sizeof(struct wsdisplay_charcell));
1084 
1085 	if (scr != scr->scr_sc->sc_active)
1086 		return;
1087 
1088 	x = ri->ri_xorigin;
1089 	sy = src * ri->ri_font->fontheight + ri->ri_yorigin;
1090 	dy = dst * ri->ri_font->fontheight + ri->ri_yorigin;
1091 	cx = ri->ri_emuwidth;
1092 	cy = num * ri->ri_font->fontheight;
1093 	cfxga_standalone_rop(scr, ROP_SRC, x, sy, x, dy, cx, cy);
1094 }
1095 
1096 void
1097 cfxga_do_cursor(struct rasops_info *ri)
1098 {
1099 	struct cfxga_screen *scr = ri->ri_hw;
1100 	int x, y, cx, cy;
1101 
1102 	if (scr != scr->scr_sc->sc_active)
1103 		return;
1104 
1105 	x = ri->ri_ccol * ri->ri_font->fontwidth + ri->ri_xorigin;
1106 	y = ri->ri_crow * ri->ri_font->fontheight + ri->ri_yorigin;
1107 	cx = ri->ri_font->fontwidth;
1108 	cy = ri->ri_font->fontheight;
1109 	cfxga_standalone_rop(scr, ROP_ONES ^ ROP_SRC /* i.e. not SRC */,
1110 	    x, y, x, y, cx, cy);
1111 }
1112 
1113 void
1114 cfxga_erasecols(void *cookie, int row, int col, int num, long attr)
1115 {
1116 	struct rasops_info *ri = cookie;
1117 	struct cfxga_screen *scr = ri->ri_hw;
1118 	int fg, bg;
1119 	int x, y, cx, cy;
1120 
1121 	/* Erase columns in backing store. */
1122 	for (x = col; x < col + num; x++) {
1123 		scr->scr_mem[row * ri->ri_cols + x].uc = 0;
1124 		scr->scr_mem[row * ri->ri_cols + x].attr = attr;
1125 	}
1126 
1127 	if (scr != scr->scr_sc->sc_active)
1128 		return;
1129 
1130 	ri->ri_ops.unpack_attr(cookie, attr, &fg, &bg, NULL);
1131 	x = col * ri->ri_font->fontwidth + ri->ri_xorigin;
1132 	y = row * ri->ri_font->fontheight + ri->ri_yorigin;
1133 	cx = num * ri->ri_font->fontwidth;
1134 	cy = ri->ri_font->fontheight;
1135 	cfxga_solid_fill(scr, x, y, cx, cy, ri->ri_devcmap[bg]);
1136 }
1137 
1138 void
1139 cfxga_eraserows(void *cookie, int row, int num, long attr)
1140 {
1141 	struct rasops_info *ri = cookie;
1142 	struct cfxga_screen *scr = ri->ri_hw;
1143 	int fg, bg;
1144 	int x, y, cx, cy;
1145 
1146 	/* Erase rows in backing store. */
1147 	for (x = 0; x < ri->ri_cols; x++) {
1148 		scr->scr_mem[row * ri->ri_cols + x].uc = 0;
1149 		scr->scr_mem[row * ri->ri_cols + x].attr = attr;
1150 	}
1151 	for (y = 1; y < num; y++)
1152 		ovbcopy(scr->scr_mem + row * ri->ri_cols,
1153 		    scr->scr_mem + (row + y) * ri->ri_cols,
1154 		    ri->ri_cols * sizeof(struct wsdisplay_charcell));
1155 
1156 	if (scr != scr->scr_sc->sc_active)
1157 		return;
1158 
1159 	ri->ri_ops.unpack_attr(cookie, attr, &fg, &bg, NULL);
1160 	x = ri->ri_xorigin;
1161 	y = row * ri->ri_font->fontheight + ri->ri_yorigin;
1162 	cx = ri->ri_emuwidth;
1163 	cy = num * ri->ri_font->fontheight;
1164 	cfxga_solid_fill(scr, x, y, cx, cy, ri->ri_devcmap[bg]);
1165 }
1166 
1167 void
1168 cfxga_putchar(void *cookie, int row, int col, u_int uc, long attr)
1169 {
1170 	struct rasops_info *ri = cookie;
1171 	struct cfxga_screen *scr = ri->ri_hw;
1172 	int x, y;
1173 
1174 	scr->scr_mem[row * ri->ri_cols + col].uc = uc;
1175 	scr->scr_mem[row * ri->ri_cols + col].attr = attr;
1176 
1177 	if (scr != scr->scr_sc->sc_active)
1178 		return;
1179 
1180 	x = col * ri->ri_font->fontwidth + ri->ri_xorigin;
1181 	y = row * ri->ri_font->fontheight + ri->ri_yorigin;
1182 
1183 	if (uc == ' ') {
1184 		int cx, cy, fg, bg;
1185 
1186 		ri->ri_ops.unpack_attr(cookie, attr, &fg, &bg, NULL);
1187 		cx = ri->ri_font->fontwidth;
1188 		cy = ri->ri_font->fontheight;
1189 		cfxga_solid_fill(scr, x, y, cx, cy, ri->ri_devcmap[bg]);
1190 	} else {
1191 		cfxga_expand_char(scr, uc, x, y, attr);
1192 	}
1193 }
1194