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