xref: /openbsd-src/sys/arch/sparc64/dev/vgafb.c (revision d13be5d47e4149db2549a9828e244d59dbc43f15)
1 /*	$OpenBSD: vgafb.c,v 1.58 2009/06/02 18:51:03 kettenis 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/systm.h>
36 #include <sys/device.h>
37 #include <sys/errno.h>
38 #include <sys/ioctl.h>
39 #include <sys/malloc.h>
40 #include <sys/pciio.h>
41 
42 #include <uvm/uvm_extern.h>
43 
44 #include <machine/autoconf.h>
45 #include <machine/bus.h>
46 #include <machine/intr.h>
47 #include <machine/openfirm.h>
48 
49 #include <dev/pci/pcidevs.h>
50 #include <dev/pci/pcireg.h>
51 #include <dev/pci/pcivar.h>
52 
53 #include <dev/wscons/wsconsio.h>
54 #include <dev/wscons/wsdisplayvar.h>
55 #include <dev/rasops/rasops.h>
56 
57 #include <machine/fbvar.h>
58 
59 struct vgafb_softc {
60 	struct sunfb sc_sunfb;
61 	int sc_nscreens;
62 	int sc_node, sc_ofhandle;
63 	bus_space_tag_t sc_mem_t;
64 	bus_space_tag_t sc_io_t;
65 	pcitag_t sc_pcitag;
66 	bus_space_handle_t sc_mem_h;
67 	bus_addr_t sc_io_addr, sc_mem_addr, sc_mmio_addr;
68 	bus_size_t sc_io_size, sc_mem_size, sc_mmio_size;
69 	int sc_console;
70 	u_int sc_mode;
71 	u_int8_t sc_cmap_red[256];
72 	u_int8_t sc_cmap_green[256];
73 	u_int8_t sc_cmap_blue[256];
74 };
75 
76 int vgafb_mapregs(struct vgafb_softc *, struct pci_attach_args *);
77 int vgafb_rommap(struct vgafb_softc *, struct pci_attach_args *);
78 int vgafb_ioctl(void *, u_long, caddr_t, int, struct proc *);
79 paddr_t vgafb_mmap(void *, off_t, int);
80 int vgafb_is_console(int);
81 int vgafb_getcmap(struct vgafb_softc *, struct wsdisplay_cmap *);
82 int vgafb_putcmap(struct vgafb_softc *, struct wsdisplay_cmap *);
83 void vgafb_setcolor(void *, u_int, u_int8_t, u_int8_t, u_int8_t);
84 
85 struct wsdisplay_accessops vgafb_accessops = {
86 	vgafb_ioctl,
87 	vgafb_mmap,
88 	NULL,	/* alloc_screen */
89 	NULL,	/* free_screen */
90 	NULL,	/* show_screen */
91 	NULL,	/* load_font */
92 	NULL,	/* scrollback */
93 	NULL,	/* getchar */
94 	NULL,	/* burner */
95 	NULL	/* pollc */
96 };
97 
98 int	vgafbmatch(struct device *, void *, void *);
99 void	vgafbattach(struct device *, struct device *, void *);
100 
101 struct cfattach vgafb_ca = {
102 	sizeof (struct vgafb_softc), vgafbmatch, vgafbattach
103 };
104 
105 struct cfdriver vgafb_cd = {
106 	NULL, "vgafb", DV_DULL
107 };
108 
109 #ifdef APERTURE
110 extern int allowaperture;
111 #endif
112 
113 int
114 vgafbmatch(parent, vcf, aux)
115 	struct device *parent;
116 	void *vcf, *aux;
117 {
118 	struct pci_attach_args *pa = aux;
119 	int node;
120 
121 	/*
122 	 * Do not match on Expert3D devices, which are driven by ifb(4).
123 	 */
124 	if (ifb_ident(aux) != 0)
125 		return (0);
126 
127 	/*
128 	 * XXX Non-console devices do not get configured by the PROM,
129 	 * XXX so do not attach them yet.
130 	 */
131 	node = PCITAG_NODE(pa->pa_tag);
132 	if (!vgafb_is_console(node))
133 		return (0);
134 
135 	if (PCI_CLASS(pa->pa_class) == PCI_CLASS_PREHISTORIC &&
136 	    PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_PREHISTORIC_VGA)
137 		return (1);
138 
139 	if (PCI_CLASS(pa->pa_class) == PCI_CLASS_DISPLAY &&
140 	    PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_DISPLAY_VGA)
141 		return (1);
142 
143 	if (PCI_CLASS(pa->pa_class) == PCI_CLASS_DISPLAY &&
144 	    PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_DISPLAY_MISC)
145 		return (1);
146 
147 	return (0);
148 }
149 
150 void
151 vgafbattach(parent, self, aux)
152 	struct device *parent, *self;
153 	void *aux;
154 {
155 	struct vgafb_softc *sc = (struct vgafb_softc *)self;
156 	struct pci_attach_args *pa = aux;
157 
158 	sc->sc_mem_t = pa->pa_memt;
159 	sc->sc_io_t = pa->pa_iot;
160 	sc->sc_node = PCITAG_NODE(pa->pa_tag);
161 	sc->sc_pcitag = pa->pa_tag;
162 
163 	printf("\n");
164 
165 	if (vgafb_mapregs(sc, pa))
166 		return;
167 
168 	sc->sc_console = vgafb_is_console(sc->sc_node);
169 
170 	fb_setsize(&sc->sc_sunfb, 8, 1152, 900, sc->sc_node, 0);
171 	if (sc->sc_sunfb.sf_depth == 24) {
172 		sc->sc_sunfb.sf_depth = 32;
173 		sc->sc_sunfb.sf_linebytes =
174 		    (sc->sc_sunfb.sf_depth / 8) * sc->sc_sunfb.sf_width;
175 		sc->sc_sunfb.sf_fbsize =
176 		    sc->sc_sunfb.sf_height * sc->sc_sunfb.sf_linebytes;
177 	}
178 
179 	sc->sc_sunfb.sf_ro.ri_bits = (void *)bus_space_vaddr(sc->sc_mem_t,
180 	    sc->sc_mem_h);
181 	sc->sc_sunfb.sf_ro.ri_hw = sc;
182 
183 	fbwscons_init(&sc->sc_sunfb,
184 	    RI_BSWAP | (sc->sc_console ? 0 : RI_FORCEMONO), sc->sc_console);
185 
186 	if (sc->sc_console) {
187 		sc->sc_ofhandle = OF_stdout();
188 		fbwscons_setcolormap(&sc->sc_sunfb, vgafb_setcolor);
189 		fbwscons_console_init(&sc->sc_sunfb, -1);
190 	} else {
191 		/* sc->sc_ofhandle = PCITAG_NODE(sc->sc_pcitag); */
192 	}
193 
194 	fbwscons_attach(&sc->sc_sunfb, &vgafb_accessops, sc->sc_console);
195 }
196 
197 int
198 vgafb_ioctl(v, cmd, data, flags, p)
199 	void *v;
200 	u_long cmd;
201 	caddr_t data;
202 	int flags;
203 	struct proc *p;
204 {
205 	struct vgafb_softc *sc = v;
206 	struct wsdisplay_fbinfo *wdf;
207 	struct pcisel *sel;
208 
209 	switch (cmd) {
210 	case WSDISPLAYIO_GTYPE:
211 		*(u_int *)data = WSDISPLAY_TYPE_UNKNOWN;
212 		break;
213 	case WSDISPLAYIO_SMODE:
214 		sc->sc_mode = *(u_int *)data;
215 		if (sc->sc_mode == WSDISPLAYIO_MODE_EMUL) {
216 			if (sc->sc_console)	/* XXX needs sc_ofhandle */
217 				fbwscons_setcolormap(&sc->sc_sunfb,
218 				    vgafb_setcolor);
219 		}
220 		break;
221 	case WSDISPLAYIO_GINFO:
222 		wdf = (void *)data;
223 		wdf->height = sc->sc_sunfb.sf_height;
224 		wdf->width  = sc->sc_sunfb.sf_width;
225 		wdf->depth  = sc->sc_sunfb.sf_depth;
226 		wdf->cmsize = 256;
227 		break;
228 	case WSDISPLAYIO_GETSUPPORTEDDEPTH:
229 		if (sc->sc_sunfb.sf_depth == 32)
230 			*(u_int *)data = WSDISPLAYIO_DEPTH_24_32;
231 		else
232 			return (-1);
233 		break;
234 	case WSDISPLAYIO_LINEBYTES:
235 		*(u_int *)data = sc->sc_sunfb.sf_linebytes;
236 		break;
237 
238 	case WSDISPLAYIO_GETCMAP:
239 		if (sc->sc_console == 0)
240 			return (EINVAL);
241 		return vgafb_getcmap(sc, (struct wsdisplay_cmap *)data);
242 	case WSDISPLAYIO_PUTCMAP:
243 		if (sc->sc_console == 0)
244 			return (EINVAL);
245 		return vgafb_putcmap(sc, (struct wsdisplay_cmap *)data);
246 
247 	case WSDISPLAYIO_GPCIID:
248 		sel = (struct pcisel *)data;
249 		sel->pc_bus = PCITAG_BUS(sc->sc_pcitag);
250 		sel->pc_dev = PCITAG_DEV(sc->sc_pcitag);
251 		sel->pc_func = PCITAG_FUN(sc->sc_pcitag);
252 		break;
253 
254 	case WSDISPLAYIO_SVIDEO:
255 	case WSDISPLAYIO_GVIDEO:
256 		break;
257 
258 	case WSDISPLAYIO_GCURPOS:
259 	case WSDISPLAYIO_SCURPOS:
260 	case WSDISPLAYIO_GCURMAX:
261 	case WSDISPLAYIO_GCURSOR:
262 	case WSDISPLAYIO_SCURSOR:
263 	default:
264 		return -1; /* not supported yet */
265         }
266 
267 	return (0);
268 }
269 
270 int
271 vgafb_getcmap(sc, cm)
272 	struct vgafb_softc *sc;
273 	struct wsdisplay_cmap *cm;
274 {
275 	u_int index = cm->index;
276 	u_int count = cm->count;
277 	int error;
278 
279 	if (index >= 256 || count > 256 - index)
280 		return (EINVAL);
281 
282 	error = copyout(&sc->sc_cmap_red[index], cm->red, count);
283 	if (error)
284 		return (error);
285 	error = copyout(&sc->sc_cmap_green[index], cm->green, count);
286 	if (error)
287 		return (error);
288 	error = copyout(&sc->sc_cmap_blue[index], cm->blue, count);
289 	if (error)
290 		return (error);
291 	return (0);
292 }
293 
294 int
295 vgafb_putcmap(sc, cm)
296 	struct vgafb_softc *sc;
297 	struct wsdisplay_cmap *cm;
298 {
299 	u_int index = cm->index;
300 	u_int count = cm->count;
301 	u_int i;
302 	int error;
303 	u_char *r, *g, *b;
304 
305 	if (index >= 256 || count > 256 - index)
306 		return (EINVAL);
307 
308 	if ((error = copyin(cm->red, &sc->sc_cmap_red[index], count)) != 0)
309 		return (error);
310 	if ((error = copyin(cm->green, &sc->sc_cmap_green[index], count)) != 0)
311 		return (error);
312 	if ((error = copyin(cm->blue, &sc->sc_cmap_blue[index], count)) != 0)
313 		return (error);
314 
315 	r = &sc->sc_cmap_red[index];
316 	g = &sc->sc_cmap_green[index];
317 	b = &sc->sc_cmap_blue[index];
318 
319 	for (i = 0; i < count; i++) {
320 		OF_call_method("color!", sc->sc_ofhandle, 4, 0, *r, *g, *b,
321 		    index);
322 		r++, g++, b++, index++;
323 	}
324 	return (0);
325 }
326 
327 void
328 vgafb_setcolor(v, index, r, g, b)
329 	void *v;
330 	u_int index;
331 	u_int8_t r, g, b;
332 {
333 	struct vgafb_softc *sc = v;
334 
335 	sc->sc_cmap_red[index] = r;
336 	sc->sc_cmap_green[index] = g;
337 	sc->sc_cmap_blue[index] = b;
338 	OF_call_method("color!", sc->sc_ofhandle, 4, 0, r, g, b, index);
339 }
340 
341 paddr_t
342 vgafb_mmap(v, off, prot)
343 	void *v;
344 	off_t off;
345 	int prot;
346 {
347 	struct vgafb_softc *sc = v;
348 
349 	if (off & PGOFSET)
350 		return (-1);
351 
352 	switch (sc->sc_mode) {
353 	case WSDISPLAYIO_MODE_MAPPED:
354 #ifdef APERTURE
355 		if (allowaperture == 0)
356 			return (-1);
357 #endif
358 
359 		if (sc->sc_mmio_size == 0)
360 			return (-1);
361 
362 		if (off >= sc->sc_mem_addr &&
363 		    off < (sc->sc_mem_addr + sc->sc_mem_size))
364 			return (bus_space_mmap(sc->sc_mem_t,
365 			    sc->sc_mem_addr, off - sc->sc_mem_addr,
366 			    prot, BUS_SPACE_MAP_LINEAR));
367 
368 		if (off >= sc->sc_mmio_addr &&
369 		    off < (sc->sc_mmio_addr + sc->sc_mmio_size))
370 			return (bus_space_mmap(sc->sc_mem_t,
371 			    sc->sc_mmio_addr, off - sc->sc_mmio_addr,
372 			    prot, BUS_SPACE_MAP_LINEAR));
373 		break;
374 
375 	case WSDISPLAYIO_MODE_DUMBFB:
376 		if (off >= 0 && off < sc->sc_mem_size)
377 			return (bus_space_mmap(sc->sc_mem_t, sc->sc_mem_addr,
378 			    off, prot, BUS_SPACE_MAP_LINEAR));
379 		break;
380 	}
381 
382 	return (-1);
383 }
384 
385 int
386 vgafb_is_console(node)
387 	int node;
388 {
389 	extern int fbnode;
390 
391 	return (fbnode == node);
392 }
393 
394 int
395 vgafb_mapregs(sc, pa)
396 	struct vgafb_softc *sc;
397 	struct pci_attach_args *pa;
398 {
399 	bus_addr_t ba;
400 	bus_size_t bs;
401 	int hasio = 0, hasmem = 0, hasmmio = 0;
402 	u_int32_t i, cf;
403 	int rv;
404 
405 	for (i = PCI_MAPREG_START; i <= PCI_MAPREG_PPB_END; i += 4) {
406 		cf = pci_conf_read(pa->pa_pc, pa->pa_tag, i);
407 		if (PCI_MAPREG_TYPE(cf) == PCI_MAPREG_TYPE_IO) {
408 			if (hasio)
409 				continue;
410 			rv = pci_io_find(pa->pa_pc, pa->pa_tag, i,
411 			    &sc->sc_io_addr, &sc->sc_io_size);
412 			if (rv != 0) {
413 				if (rv != ENOENT)
414 					printf("%s: failed to find io at 0x%x\n",
415 					    sc->sc_sunfb.sf_dev.dv_xname, i);
416 				continue;
417 			}
418 			hasio = 1;
419 		} else {
420 			/* Memory mapping... frame memory or mmio? */
421 			rv = pci_mem_find(pa->pa_pc, pa->pa_tag, i,
422 			    &ba, &bs, NULL);
423 			if (rv != 0) {
424 				if (rv != ENOENT)
425 					printf("%s: failed to find mem at 0x%x\n",
426 					    sc->sc_sunfb.sf_dev.dv_xname, i);
427 				continue;
428 			}
429 
430 			if (bs == 0 /* || ba == 0 */) {
431 				/* ignore this entry */
432 			} else if (hasmem == 0) {
433 				/*
434 				 * first memory slot found goes into memory,
435 				 * this is for the case of no mmio
436 				 */
437 				sc->sc_mem_addr = ba;
438 				sc->sc_mem_size = bs;
439 				hasmem = 1;
440 			} else {
441 				/*
442 				 * Oh, we have a second `memory'
443 				 * region, is this region the vga memory
444 				 * or mmio, we guess that memory is
445 				 * the larger of the two.
446 				 */
447 				if (sc->sc_mem_size >= bs) {
448 					/* this is the mmio */
449 					sc->sc_mmio_addr = ba;
450 					/* ATI driver maps 0x80000 mmio, grr */
451 					if (bs < 0x80000) {
452 						bs = 0x80000;
453 					}
454 					sc->sc_mmio_size = bs;
455 					hasmmio = 1;
456 				} else {
457 					/* this is the memory */
458 					sc->sc_mmio_addr = sc->sc_mem_addr;
459 					sc->sc_mmio_size = sc->sc_mem_size;
460 					sc->sc_mem_addr = ba;
461 					sc->sc_mem_size = bs;
462 					/* ATI driver maps 0x80000 mmio, grr */
463 					if (sc->sc_mmio_size < 0x80000) {
464 						sc->sc_mmio_size = 0x80000;
465 					}
466 				}
467 			}
468 		}
469 	}
470 
471 	if (hasmem != 0) {
472 		if (bus_space_map(pa->pa_memt, sc->sc_mem_addr, sc->sc_mem_size,
473 		    0, &sc->sc_mem_h)) {
474 			printf("%s: can't map mem space\n",
475 			    sc->sc_sunfb.sf_dev.dv_xname);
476 			return (1);
477 		}
478 	}
479 
480 	/* failure to initialize io ports should not prevent attachment */
481 	if (hasmem == 0) {
482 		printf("%s: could not find memory space\n",
483 		    sc->sc_sunfb.sf_dev.dv_xname);
484 		return (1);
485 	}
486 
487 #ifdef DIAGNOSTIC
488 	if (hasmmio == 0) {
489 		printf("%s: WARNING: no mmio space configured\n",
490 		    sc->sc_sunfb.sf_dev.dv_xname);
491 	}
492 #endif
493 
494 	return (0);
495 }
496