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