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