xref: /openbsd/sys/arch/sparc64/dev/vgafb.c (revision db3296cf)
1 /*	$OpenBSD: vgafb.c,v 1.34 2003/06/16 21:46:23 miod Exp $	*/
2 
3 /*
4  * Copyright (c) 2001 Jason L. Wright (jason@thought.net)
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
20  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  *
28  * Effort sponsored in part by the Defense Advanced Research Projects
29  * Agency (DARPA) and Air Force Research Laboratory, Air Force
30  * Materiel Command, USAF, under agreement number F30602-01-2-0537.
31  *
32  */
33 
34 #include <sys/param.h>
35 #include <sys/buf.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/errno.h>
39 #include <sys/device.h>
40 #include <sys/ioctl.h>
41 #include <sys/malloc.h>
42 #include <sys/pciio.h>
43 
44 #include <uvm/uvm_extern.h>
45 
46 #include <machine/bus.h>
47 #include <machine/intr.h>
48 #include <machine/autoconf.h>
49 #include <machine/openfirm.h>
50 
51 #include <dev/pci/pcivar.h>
52 #include <dev/wscons/wsconsio.h>
53 #include <dev/wscons/wsdisplayvar.h>
54 #include <dev/wscons/wscons_raster.h>
55 #include <dev/rasops/rasops.h>
56 #include <machine/fbvar.h>
57 
58 struct vgafb_softc {
59 	struct sunfb sc_sunfb;
60 	int sc_nscreens;
61 	int sc_node, sc_ofhandle;
62 	bus_space_tag_t sc_mem_t;
63 	bus_space_tag_t sc_io_t;
64 	pcitag_t sc_pcitag;
65 	bus_space_handle_t sc_mem_h;
66 	bus_addr_t sc_io_addr, sc_mem_addr, sc_mmio_addr;
67 	bus_size_t sc_io_size, sc_mem_size, sc_mmio_size;
68 	pci_chipset_tag_t sc_pci_chip;
69 	int sc_has_rom;
70 	int sc_console;
71 	u_int sc_mode;
72 	u_int8_t sc_cmap_red[256];
73 	u_int8_t sc_cmap_green[256];
74 	u_int8_t sc_cmap_blue[256];
75 	int *sc_crowp, *sc_ccolp;
76 };
77 
78 struct wsscreen_descr vgafb_stdscreen = {
79 	"std",
80 };
81 
82 const struct wsscreen_descr *vgafb_scrlist[] = {
83 	&vgafb_stdscreen,
84 	/* XXX other formats? */
85 };
86 
87 struct wsscreen_list vgafb_screenlist = {
88 	sizeof(vgafb_scrlist) / sizeof(struct wsscreen_descr *), vgafb_scrlist
89 };
90 
91 int vgafb_mapregs(struct vgafb_softc *, struct pci_attach_args *);
92 int vgafb_rommap(struct vgafb_softc *, struct pci_attach_args *);
93 int vgafb_ioctl(void *, u_long, caddr_t, int, struct proc *);
94 int vgafb_alloc_screen(void *, const struct wsscreen_descr *, void **,
95     int *, int *, long *);
96 void vgafb_free_screen(void *, void *);
97 int vgafb_show_screen(void *, void *, int,
98     void (*cb)(void *, int, int), void *);
99 paddr_t vgafb_mmap(void *, off_t, int);
100 int vgafb_is_console(int);
101 int vgafb_getcmap(struct vgafb_softc *, struct wsdisplay_cmap *);
102 int vgafb_putcmap(struct vgafb_softc *, struct wsdisplay_cmap *);
103 void vgafb_setcolor(void *, u_int, u_int8_t, u_int8_t, u_int8_t);
104 void vgafb_updatecursor(struct rasops_info *ri);
105 
106 struct wsdisplay_accessops vgafb_accessops = {
107 	vgafb_ioctl,
108 	vgafb_mmap,
109 	vgafb_alloc_screen,
110 	vgafb_free_screen,
111 	vgafb_show_screen,
112 	0 /* load_font */
113 };
114 
115 int	vgafbmatch(struct device *, void *, void *);
116 void	vgafbattach(struct device *, struct device *, void *);
117 
118 struct cfattach vgafb_ca = {
119 	sizeof (struct vgafb_softc), vgafbmatch, vgafbattach
120 };
121 
122 struct cfdriver vgafb_cd = {
123 	NULL, "vgafb", DV_DULL
124 };
125 
126 int
127 vgafbmatch(parent, vcf, aux)
128 	struct device *parent;
129 	void *vcf, *aux;
130 {
131 	struct pci_attach_args *pa = aux;
132 
133 	if (PCI_CLASS(pa->pa_class) == PCI_CLASS_PREHISTORIC &&
134 	    PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_PREHISTORIC_VGA)
135 		return (1);
136 
137 	if (PCI_CLASS(pa->pa_class) == PCI_CLASS_DISPLAY &&
138 	    PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_DISPLAY_VGA)
139 		return (1);
140 
141 	if (PCI_CLASS(pa->pa_class) == PCI_CLASS_DISPLAY &&
142 	    PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_DISPLAY_MISC)
143 		return (1);
144 
145 	return (0);
146 }
147 
148 void
149 vgafbattach(parent, self, aux)
150 	struct device *parent, *self;
151 	void *aux;
152 {
153 	struct vgafb_softc *sc = (struct vgafb_softc *)self;
154 	struct pci_attach_args *pa = aux;
155 	struct wsemuldisplaydev_attach_args waa;
156 
157 	sc->sc_mem_t = pa->pa_memt;
158 	sc->sc_io_t = pa->pa_iot;
159 	sc->sc_node = PCITAG_NODE(pa->pa_tag);
160 	sc->sc_pcitag = pa->pa_tag;
161 
162 	printf("\n");
163 
164 	if (vgafb_mapregs(sc, pa))
165 		return;
166 
167 	sc->sc_console = vgafb_is_console(sc->sc_node);
168 
169 	fb_setsize(&sc->sc_sunfb, 8, 1152, 900, sc->sc_node, 0);
170 	if (sc->sc_sunfb.sf_depth == 24) {
171 		sc->sc_sunfb.sf_depth = 32;
172 		sc->sc_sunfb.sf_linebytes =
173 		    (sc->sc_sunfb.sf_depth / 8) * sc->sc_sunfb.sf_width;
174 		sc->sc_sunfb.sf_fbsize =
175 		    sc->sc_sunfb.sf_height * sc->sc_sunfb.sf_linebytes;
176 	}
177 
178 	sc->sc_sunfb.sf_ro.ri_bits = (void *)bus_space_vaddr(sc->sc_mem_t,
179 	    sc->sc_mem_h);
180 	sc->sc_sunfb.sf_ro.ri_hw = sc;
181 
182 	fbwscons_init(&sc->sc_sunfb,
183 	    RI_BSWAP | (sc->sc_console ? 0 : RI_CLEAR));
184 
185 	vgafb_stdscreen.capabilities = sc->sc_sunfb.sf_ro.ri_caps;
186 	vgafb_stdscreen.nrows = sc->sc_sunfb.sf_ro.ri_rows;
187 	vgafb_stdscreen.ncols = sc->sc_sunfb.sf_ro.ri_cols;
188 	vgafb_stdscreen.textops = &sc->sc_sunfb.sf_ro.ri_ops;
189 
190 	if (sc->sc_console) {
191 		sc->sc_ofhandle = OF_stdout();
192 		fbwscons_setcolormap(&sc->sc_sunfb, vgafb_setcolor);
193 		sc->sc_sunfb.sf_ro.ri_updatecursor = vgafb_updatecursor;
194 		fbwscons_console_init(&sc->sc_sunfb, &vgafb_stdscreen, -1,
195 		    NULL);
196 	} else {
197 		/* sc->sc_ofhandle = XXX */
198 	}
199 
200 	waa.console = sc->sc_console;
201 	waa.scrdata = &vgafb_screenlist;
202 	waa.accessops = &vgafb_accessops;
203 	waa.accesscookie = sc;
204 	config_found(self, &waa, wsemuldisplaydevprint);
205 }
206 
207 int
208 vgafb_ioctl(v, cmd, data, flags, p)
209 	void *v;
210 	u_long cmd;
211 	caddr_t data;
212 	int flags;
213 	struct proc *p;
214 {
215 	struct vgafb_softc *sc = v;
216 	struct wsdisplay_fbinfo *wdf;
217 	struct pcisel *sel;
218 
219 	switch (cmd) {
220 	case WSDISPLAYIO_GTYPE:
221 		*(u_int *)data = WSDISPLAY_TYPE_UNKNOWN;
222 		break;
223 	case WSDISPLAYIO_SMODE:
224 		sc->sc_mode = *(u_int *)data;
225 		if (sc->sc_mode == WSDISPLAYIO_MODE_EMUL)
226 			fbwscons_setcolormap(&sc->sc_sunfb, vgafb_setcolor);
227 		break;
228 	case WSDISPLAYIO_GINFO:
229 		wdf = (void *)data;
230 		wdf->height = sc->sc_sunfb.sf_height;
231 		wdf->width  = sc->sc_sunfb.sf_width;
232 		wdf->depth  = sc->sc_sunfb.sf_depth;
233 		wdf->cmsize = 256;
234 		break;
235 	case WSDISPLAYIO_LINEBYTES:
236 		*(u_int *)data = sc->sc_sunfb.sf_linebytes;
237 		break;
238 
239 	case WSDISPLAYIO_GETCMAP:
240 		if (sc->sc_console == 0)
241 			return (EINVAL);
242 		return vgafb_getcmap(sc, (struct wsdisplay_cmap *)data);
243 	case WSDISPLAYIO_PUTCMAP:
244 		if (sc->sc_console == 0)
245 			return (EINVAL);
246 		return vgafb_putcmap(sc, (struct wsdisplay_cmap *)data);
247 
248 	case WSDISPLAYIO_GPCIID:
249 		sel = (struct pcisel *)data;
250 		sel->pc_bus = PCITAG_BUS(sc->sc_pcitag);
251 		sel->pc_dev = PCITAG_DEV(sc->sc_pcitag);
252 		sel->pc_func = PCITAG_FUNC(sc->sc_pcitag);
253 		break;
254 
255 	case WSDISPLAYIO_SVIDEO:
256 	case WSDISPLAYIO_GVIDEO:
257 	case WSDISPLAYIO_GCURPOS:
258 	case WSDISPLAYIO_SCURPOS:
259 	case WSDISPLAYIO_GCURMAX:
260 	case WSDISPLAYIO_GCURSOR:
261 	case WSDISPLAYIO_SCURSOR:
262 	default:
263 		return -1; /* not supported yet */
264         }
265 
266 	return (0);
267 }
268 
269 int
270 vgafb_getcmap(sc, cm)
271 	struct vgafb_softc *sc;
272 	struct wsdisplay_cmap *cm;
273 {
274 	u_int index = cm->index;
275 	u_int count = cm->count;
276 	int error;
277 
278 	if (index >= 256 || count > 256 - index)
279 		return (EINVAL);
280 
281 	error = copyout(&sc->sc_cmap_red[index], cm->red, count);
282 	if (error)
283 		return (error);
284 	error = copyout(&sc->sc_cmap_green[index], cm->green, count);
285 	if (error)
286 		return (error);
287 	error = copyout(&sc->sc_cmap_blue[index], cm->blue, count);
288 	if (error)
289 		return (error);
290 	return (0);
291 }
292 
293 int
294 vgafb_putcmap(sc, cm)
295 	struct vgafb_softc *sc;
296 	struct wsdisplay_cmap *cm;
297 {
298 	u_int index = cm->index;
299 	u_int count = cm->count;
300 	u_int i;
301 	int error;
302 	u_char *r, *g, *b;
303 
304 	if (index >= 256 || count > 256 - index)
305 		return (EINVAL);
306 
307 	if ((error = copyin(cm->red, &sc->sc_cmap_red[index], count)) != 0)
308 		return (error);
309 	if ((error = copyin(cm->green, &sc->sc_cmap_green[index], count)) != 0)
310 		return (error);
311 	if ((error = copyin(cm->blue, &sc->sc_cmap_blue[index], count)) != 0)
312 		return (error);
313 
314 	r = &sc->sc_cmap_red[index];
315 	g = &sc->sc_cmap_green[index];
316 	b = &sc->sc_cmap_blue[index];
317 
318 	for (i = 0; i < count; i++) {
319 		OF_call_method("color!", sc->sc_ofhandle, 4, 0, *r, *g, *b,
320 		    index);
321 		r++, g++, b++, index++;
322 	}
323 	return (0);
324 }
325 
326 void
327 vgafb_setcolor(v, index, r, g, b)
328 	void *v;
329 	u_int index;
330 	u_int8_t r, g, b;
331 {
332 	struct vgafb_softc *sc = v;
333 
334 	sc->sc_cmap_red[index] = r;
335 	sc->sc_cmap_green[index] = g;
336 	sc->sc_cmap_blue[index] = b;
337 	OF_call_method("color!", sc->sc_ofhandle, 4, 0, r, g, b, index);
338 }
339 
340 int
341 vgafb_alloc_screen(v, type, cookiep, curxp, curyp, attrp)
342 	void *v;
343 	const struct wsscreen_descr *type;
344 	void **cookiep;
345 	int *curxp, *curyp;
346 	long *attrp;
347 {
348 	struct vgafb_softc *sc = v;
349 
350 	if (sc->sc_nscreens > 0)
351 		return (ENOMEM);
352 
353 	*cookiep = &sc->sc_sunfb.sf_ro;
354 	*curyp = 0;
355 	*curxp = 0;
356 	sc->sc_sunfb.sf_ro.ri_ops.alloc_attr(&sc->sc_sunfb.sf_ro,
357 	    WSCOL_BLACK, WSCOL_WHITE, WSATTR_WSCOLORS, attrp);
358 	sc->sc_nscreens++;
359 	return (0);
360 }
361 
362 void
363 vgafb_free_screen(v, cookie)
364 	void *v;
365 	void *cookie;
366 {
367 	struct vgafb_softc *sc = v;
368 
369 	sc->sc_nscreens--;
370 }
371 
372 int
373 vgafb_show_screen(v, cookie, waitok, cb, cbarg)
374 	void *v;
375 	void *cookie;
376 	int waitok;
377 	void (*cb)(void *, int, int);
378 	void *cbarg;
379 {
380 	return (0);
381 }
382 
383 paddr_t
384 vgafb_mmap(v, off, prot)
385 	void *v;
386 	off_t off;
387 	int prot;
388 {
389 	struct vgafb_softc *sc = v;
390 
391 	if (off & PGOFSET)
392 		return (-1);
393 
394 	switch (sc->sc_mode) {
395 	case WSDISPLAYIO_MODE_MAPPED:
396 		if (off >= sc->sc_mem_addr &&
397 		    off < (sc->sc_mem_addr + sc->sc_mem_size))
398 			return (bus_space_mmap(sc->sc_mem_t,
399 			    sc->sc_mem_addr, off - sc->sc_mem_addr,
400 			    prot, BUS_SPACE_MAP_LINEAR));
401 
402 		if (off >= sc->sc_mmio_addr &&
403 		    off < (sc->sc_mmio_addr + sc->sc_mmio_size))
404 			return (bus_space_mmap(sc->sc_mem_t,
405 			    sc->sc_mmio_addr, off - sc->sc_mmio_addr,
406 			    prot, BUS_SPACE_MAP_LINEAR));
407 		break;
408 
409 	case WSDISPLAYIO_MODE_DUMBFB:
410 		if (off >= 0 && off < sc->sc_mem_size)
411 			return (bus_space_mmap(sc->sc_mem_t, sc->sc_mem_addr,
412 			    off, prot, BUS_SPACE_MAP_LINEAR));
413 		break;
414 	}
415 
416 	return (-1);
417 }
418 
419 int
420 vgafb_is_console(node)
421 	int node;
422 {
423 	extern int fbnode;
424 
425 	return (fbnode == node);
426 }
427 
428 int
429 vgafb_mapregs(sc, pa)
430 	struct vgafb_softc *sc;
431 	struct pci_attach_args *pa;
432 {
433 	bus_addr_t ba;
434 	bus_size_t bs;
435 	int hasio = 0, hasmem = 0, hasmmio = 0;
436 	u_int32_t i, cf;
437 
438 	for (i = PCI_MAPREG_START; i < PCI_MAPREG_END; i += 4) {
439 		cf = pci_conf_read(pa->pa_pc, pa->pa_tag, i);
440 		if (PCI_MAPREG_TYPE(cf) == PCI_MAPREG_TYPE_IO) {
441 			if (hasio)
442 				continue;
443 			if (pci_io_find(pa->pa_pc, pa->pa_tag, i,
444 			    &sc->sc_io_addr, &sc->sc_io_size)) {
445 				printf(": failed to find io at 0x%x\n", i);
446 				continue;
447 			}
448 			hasio = 1;
449 		} else {
450 			/* Memory mapping... frame memory or mmio? */
451 			if (pci_mem_find(pa->pa_pc, pa->pa_tag, i,
452 			    &ba, &bs, NULL)) {
453 				printf(": failed to find mem at 0x%x\n", i);
454 				continue;
455 			}
456 
457 			if (bs <= 0x10000) {	/* mmio */
458 				if (hasmmio)
459 					continue;
460 				sc->sc_mmio_addr = ba;
461 				sc->sc_mmio_size = bs;
462 				hasmmio = 1;
463 			} else {
464 				if (hasmem)
465 					continue;
466 				if (bus_space_map(pa->pa_memt, ba, bs,
467 				    0, &sc->sc_mem_h)) {
468 					printf(": can't map mem space\n");
469 					continue;
470 				}
471 				sc->sc_mem_addr = ba;
472 				sc->sc_mem_size = bs;
473 				hasmem = 1;
474 			}
475 		}
476 	}
477 
478 	if (hasmmio == 0 || hasmem == 0 || hasio == 0) {
479 		printf(": failed to find all ports\n");
480 		goto fail;
481 	}
482 
483 	return (0);
484 
485 fail:
486 	if (hasmem)
487 		bus_space_unmap(pa->pa_memt, sc->sc_mem_h, sc->sc_mem_size);
488 	return (1);
489 }
490 
491 void
492 vgafb_updatecursor(ri)
493 	struct rasops_info *ri;
494 {
495 	struct vgafb_softc *sc = ri->ri_hw;
496 
497 	if (sc->sc_crowp != NULL)
498 		*sc->sc_crowp = ri->ri_crow;
499 	if (sc->sc_ccolp != NULL)
500 		*sc->sc_ccolp = ri->ri_ccol;
501 }
502