vgafb.c revision 1.54
1/*	$OpenBSD: vgafb.c,v 1.54 2013/08/27 21:00:52 mpi Exp $	*/
2/*	$NetBSD: vga.c,v 1.3 1996/12/02 22:24:54 cgd Exp $	*/
3
4/*
5 * Copyright (c) 1995, 1996 Carnegie-Mellon University.
6 * All rights reserved.
7 *
8 * Author: Chris G. Demetriou
9 *
10 * Permission to use, copy, modify and distribute this software and
11 * its documentation is hereby granted, provided that both the copyright
12 * notice and this permission notice appear in all copies of the
13 * software, derivative works or modified versions, and any portions
14 * thereof, and that both notices appear in supporting documentation.
15 *
16 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
17 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
18 * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
19 *
20 * Carnegie Mellon requests users of this software to return to
21 *
22 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
23 *  School of Computer Science
24 *  Carnegie Mellon University
25 *  Pittsburgh PA 15213-3890
26 *
27 * any improvements or extensions that they make and grant Carnegie the
28 * rights to redistribute these changes.
29 */
30
31#include <sys/param.h>
32#include <sys/systm.h>
33#include <sys/device.h>
34
35#include <machine/bus.h>
36
37#include <dev/wscons/wsconsio.h>
38#include <dev/wscons/wsdisplayvar.h>
39#include <dev/rasops/rasops.h>
40
41#include <dev/ofw/openfirm.h>
42#include <macppc/macppc/ofw_machdep.h>
43
44#include <dev/pci/pcireg.h>
45#include <dev/pci/pcivar.h>
46#include <dev/pci/vga_pcivar.h>
47
48struct vga_config {
49	bus_space_tag_t		vc_memt;
50	bus_space_handle_t	vc_memh;
51
52	/* Colormap */
53	u_char vc_cmap_red[256];
54	u_char vc_cmap_green[256];
55	u_char vc_cmap_blue[256];
56
57	struct rasops_info	ri;
58
59	bus_addr_t	iobase, membase, mmiobase;
60	bus_size_t	iosize, memsize, mmiosize;
61
62	int vc_backlight_on;
63	u_int vc_mode;
64};
65
66int	vgafb_ioctl(void *, u_long, caddr_t, int, struct proc *);
67paddr_t	vgafb_mmap(void *, off_t, int);
68int	vgafb_alloc_screen(void *, const struct wsscreen_descr *, void **,
69	    int *, int *, long *);
70void	vgafb_free_screen(void *, void *);
71int	vgafb_show_screen(void *, void *, int, void (*cb)(void *, int, int),
72	    void *);
73void	vgafb_burn(void *v, u_int , u_int);
74void	vgafb_restore_default_colors(struct vga_config *);
75int	vgafb_is_console(int);
76void	vgafb_wsdisplay_attach(struct device *, struct vga_config *);
77int	vgafb_mapregs(struct vga_config *, struct pci_attach_args *);
78
79struct vga_config vgafbcn;
80
81struct wsscreen_descr vgafb_stdscreen = {
82	"std",
83	0, 0,
84	0,
85	0, 0,
86	WSSCREEN_UNDERLINE | WSSCREEN_HILIT |
87	WSSCREEN_REVERSE | WSSCREEN_WSCOLORS
88};
89
90const struct wsscreen_descr *vgafb_scrlist[] = {
91	&vgafb_stdscreen,
92};
93
94struct wsscreen_list vgafb_screenlist = {
95	nitems(vgafb_scrlist), vgafb_scrlist
96};
97
98struct wsdisplay_accessops vgafb_accessops = {
99	vgafb_ioctl,
100	vgafb_mmap,
101	vgafb_alloc_screen,
102	vgafb_free_screen,
103	vgafb_show_screen,
104	NULL,		/* load_font */
105	NULL,		/* scrollback */
106	NULL,		/* getchar */
107	vgafb_burn,	/* burner */
108};
109
110int	vgafb_getcmap(struct vga_config *vc, struct wsdisplay_cmap *cm);
111int	vgafb_putcmap(struct vga_config *vc, struct wsdisplay_cmap *cm);
112
113int	vgafb_match(struct device *, void *, void *);
114void	vgafb_attach(struct device *, struct device *, void *);
115
116const struct cfattach vgafb_ca = {
117	sizeof(struct device), vgafb_match, vgafb_attach,
118};
119
120struct cfdriver vgafb_cd = {
121	NULL, "vgafb", DV_DULL,
122};
123
124#ifdef APERTURE
125extern int allowaperture;
126#endif
127
128int
129vgafb_match(struct device *parent, void *match, void *aux)
130{
131	struct pci_attach_args *pa = aux;
132	int node;
133
134	if (DEVICE_IS_VGA_PCI(pa->pa_class) == 0) {
135		/*
136		 * XXX Graphic cards found in iMac G3 have a ``Misc''
137		 * subclass, match them all.
138		 */
139		if (PCI_CLASS(pa->pa_class) != PCI_CLASS_DISPLAY ||
140		    PCI_SUBCLASS(pa->pa_class) != PCI_SUBCLASS_DISPLAY_MISC)
141			return (0);
142	}
143
144	/*
145	 * XXX Non-console devices do not get configured by the PROM,
146	 * XXX so do not attach them yet.
147	 */
148	node = PCITAG_NODE(pa->pa_tag);
149	if (!vgafb_is_console(node))
150		return (0);
151
152	return (1);
153}
154
155void
156vgafb_attach(struct device *parent, struct device  *self, void *aux)
157{
158	struct pci_attach_args *pa = aux;
159	struct vga_config *vc = &vgafbcn;
160
161	if (vgafb_mapregs(vc, pa))
162		return;
163
164	vgafb_wsdisplay_attach(self, vc);
165}
166
167void
168vgafb_restore_default_colors(struct vga_config *vc)
169{
170	const uint8_t *color;
171	int i;
172
173	for (i = 0; i < 256; i++) {
174
175		color = &rasops_cmap[i * 3];
176
177		vc->vc_cmap_red[i] = color[0];
178		vc->vc_cmap_green[i] = color[1];
179		vc->vc_cmap_blue[i] = color[2];
180	}
181
182	of_setcolors(0, 256, vc->vc_cmap_red, vc->vc_cmap_green,
183	    vc->vc_cmap_blue);
184}
185
186void
187vgafb_wsdisplay_attach(struct device *parent, struct vga_config *vc)
188{
189	struct wsemuldisplaydev_attach_args aa;
190	struct rasops_info *ri = &vc->ri;
191	long defattr;
192
193	ri->ri_flg = RI_CENTER | RI_VCONS | RI_WRONLY;
194	rasops_init(ri, 160, 160);
195
196	ri->ri_ops.alloc_attr(ri->ri_active, 0, 0, 0, &defattr);
197	wsdisplay_cnattach(&vgafb_stdscreen, ri->ri_active, 0, 0, defattr);
198
199	aa.console = 1;
200	aa.scrdata = &vgafb_screenlist;
201	aa.accessops = &vgafb_accessops;
202	aa.accesscookie = vc;
203	aa.defaultscreens = 0;
204
205	/* no need to keep the burner function if no hw support */
206	if (cons_backlight_available == 0)
207		vgafb_accessops.burn_screen = NULL;
208	else {
209		vc->vc_backlight_on = WSDISPLAYIO_VIDEO_OFF;
210		vgafb_burn(vc, WSDISPLAYIO_VIDEO_ON, 0);	/* paranoia */
211	}
212
213	config_found(parent, &aa, wsemuldisplaydevprint);
214}
215
216int
217vgafb_ioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
218{
219	struct vga_config *vc = v;
220	struct wsdisplay_fbinfo *wdf;
221
222	switch (cmd) {
223	case WSDISPLAYIO_GTYPE:
224		*(u_int *)data = WSDISPLAY_TYPE_PCIVGA;
225		return 0;
226	case WSDISPLAYIO_GINFO:
227		wdf = (void *)data;
228		wdf->height = cons_height;
229		wdf->width  = cons_width;
230		wdf->depth  = cons_depth;
231		wdf->cmsize = 256;
232		return 0;
233
234	case WSDISPLAYIO_LINEBYTES:
235		*(u_int *)data = cons_linebytes;
236		return 0;
237
238	case WSDISPLAYIO_GETCMAP:
239		return vgafb_getcmap(vc, (struct wsdisplay_cmap *)data);
240
241	case WSDISPLAYIO_PUTCMAP:
242		return vgafb_putcmap(vc, (struct wsdisplay_cmap *)data);
243
244	case WSDISPLAYIO_SMODE:
245		vc->vc_mode = *(u_int *)data;
246		/* track the state of the display,
247		 * if returning to WSDISPLAYIO_MODE_EMUL
248		 * restore the last palette, workaround for
249		 * bad accellerated X servers that does not restore
250		 * the correct palette.
251		 */
252		if (cons_depth == 8)
253			vgafb_restore_default_colors(vc);
254		break;
255
256	case WSDISPLAYIO_GETPARAM:
257	{
258		struct wsdisplay_param *dp = (struct wsdisplay_param *)data;
259
260		switch (dp->param) {
261		case WSDISPLAYIO_PARAM_BRIGHTNESS:
262			if (cons_backlight_available != 0) {
263				dp->min = MIN_BRIGHTNESS;
264				dp->max = MAX_BRIGHTNESS;
265				dp->curval = cons_brightness;
266				return 0;
267			}
268			return -1;
269		case WSDISPLAYIO_PARAM_BACKLIGHT:
270			if (cons_backlight_available != 0) {
271				dp->min = 0;
272				dp->max = 1;
273				dp->curval = vc->vc_backlight_on;
274				return 0;
275			} else
276				return -1;
277		}
278	}
279		return -1;
280
281	case WSDISPLAYIO_SETPARAM:
282	{
283		struct wsdisplay_param *dp = (struct wsdisplay_param *)data;
284
285		switch (dp->param) {
286		case WSDISPLAYIO_PARAM_BRIGHTNESS:
287			if (cons_backlight_available == 1) {
288				of_setbrightness(dp->curval);
289				return 0;
290			} else
291				return -1;
292		case WSDISPLAYIO_PARAM_BACKLIGHT:
293			if (cons_backlight_available != 0) {
294				vgafb_burn(vc,
295				    dp->curval ? WSDISPLAYIO_VIDEO_ON :
296				      WSDISPLAYIO_VIDEO_OFF, 0);
297				return 0;
298			} else
299				return -1;
300		}
301	}
302		return -1;
303
304	case WSDISPLAYIO_SVIDEO:
305	case WSDISPLAYIO_GVIDEO:
306		break;
307
308	case WSDISPLAYIO_GCURPOS:
309	case WSDISPLAYIO_SCURPOS:
310	case WSDISPLAYIO_GCURMAX:
311	case WSDISPLAYIO_GCURSOR:
312	case WSDISPLAYIO_SCURSOR:
313	default:
314		return -1; /* not supported yet */
315	}
316
317	return (0);
318}
319
320paddr_t
321vgafb_mmap(void *v, off_t off, int prot)
322{
323	struct vga_config *vc = v;
324
325	if (off & PGOFSET)
326		return (-1);
327
328	switch (vc->vc_mode) {
329	case WSDISPLAYIO_MODE_MAPPED:
330#ifdef APERTURE
331		if (allowaperture == 0)
332			return (-1);
333#endif
334
335		if (vc->mmiosize == 0)
336			return (-1);
337
338		if (off >= vc->membase && off < (vc->membase + vc->memsize))
339			return (off);
340
341		if (off >= vc->mmiobase && off < (vc->mmiobase + vc->mmiosize))
342			return (off);
343		break;
344
345	case WSDISPLAYIO_MODE_DUMBFB:
346		if (off >= 0x00000 && off < vc->memsize)
347			return (vc->membase + off);
348		break;
349
350	}
351
352	return (-1);
353}
354
355int
356vgafb_is_console(int node)
357{
358	extern int fbnode;
359
360	return (fbnode == node);
361}
362
363int
364vgafb_cnattach(bus_space_tag_t iot, bus_space_tag_t memt, int type, int check)
365{
366	struct vga_config *vc = &vgafbcn;
367	struct rasops_info *ri = &vc->ri;
368	long defattr;
369
370	vc->vc_memt = memt;
371	vc->membase = cons_addr;
372	vc->memsize = cons_linebytes * cons_height;
373	vc->vc_memh = (bus_space_handle_t)mapiodev(vc->membase, vc->memsize);
374
375	if (cons_depth == 8)
376		vgafb_restore_default_colors(vc);
377
378	ri->ri_flg = RI_FULLCLEAR | RI_CLEAR;
379	ri->ri_depth = cons_depth;
380	ri->ri_bits = (void *)vc->vc_memh;
381	ri->ri_width = cons_width;
382	ri->ri_height = cons_height;
383	ri->ri_stride = cons_linebytes;
384	ri->ri_hw = vc;
385
386	rasops_init(ri, 160, 160);
387
388	vgafb_stdscreen.nrows = ri->ri_rows;
389	vgafb_stdscreen.ncols = ri->ri_cols;
390	vgafb_stdscreen.textops = &ri->ri_ops;
391
392	ri->ri_ops.alloc_attr(ri, 0, 0, 0, &defattr);
393
394	wsdisplay_cnattach(&vgafb_stdscreen, ri, 0, 0, defattr);
395
396	return (0);
397}
398
399int
400vgafb_getcmap(struct vga_config *vc, struct wsdisplay_cmap *cm)
401{
402	u_int index = cm->index;
403	u_int count = cm->count;
404	int error;
405
406	if (index >= 256 || count > 256 - index)
407		return EINVAL;
408
409	error = copyout(&vc->vc_cmap_red[index],   cm->red,   count);
410	if (error)
411		return error;
412	error = copyout(&vc->vc_cmap_green[index], cm->green, count);
413	if (error)
414		return error;
415	error = copyout(&vc->vc_cmap_blue[index],  cm->blue,  count);
416	if (error)
417		return error;
418
419	return 0;
420}
421
422int
423vgafb_putcmap(struct vga_config *vc, struct wsdisplay_cmap *cm)
424{
425	u_int index = cm->index;
426	u_int count = cm->count;
427	int error;
428	u_int8_t *r, *g, *b;
429
430	if (index >= 256 || count > 256 - index)
431		return EINVAL;
432
433	if ((error = copyin(cm->red, &vc->vc_cmap_red[index], count)) != 0)
434		return (error);
435	if ((error = copyin(cm->green, &vc->vc_cmap_green[index], count)) != 0)
436		return (error);
437	if ((error = copyin(cm->blue, &vc->vc_cmap_blue[index], count)) != 0)
438		return (error);
439
440	r = &(vc->vc_cmap_red[index]);
441	g = &(vc->vc_cmap_green[index]);
442	b = &(vc->vc_cmap_blue[index]);
443
444	of_setcolors(index, count, r, g, b);
445
446	return 0;
447}
448
449void
450vgafb_burn(void *v, u_int on, u_int flags)
451{
452	struct vga_config *vc = v;
453
454	if (vc->vc_backlight_on != on) {
455		of_setbacklight(on == WSDISPLAYIO_VIDEO_ON);
456		vc->vc_backlight_on = on;
457	}
458}
459
460int
461vgafb_alloc_screen(void *v, const struct wsscreen_descr *type, void **cookiep,
462    int *curxp, int *curyp, long *attrp)
463{
464	struct vga_config *vc = v;
465	struct rasops_info *ri = &vc->ri;
466
467	return rasops_alloc_screen(ri, cookiep, curxp, curyp, attrp);
468}
469
470void
471vgafb_free_screen(void *v, void *cookie)
472{
473	struct vga_config *vc = v;
474	struct rasops_info *ri = &vc->ri;
475
476	return rasops_free_screen(ri, cookie);
477}
478
479int
480vgafb_show_screen(void *v, void *cookie, int waitok,
481    void (*cb)(void *, int, int), void *cbarg)
482{
483	struct vga_config *vc = v;
484	struct rasops_info *ri = &vc->ri;
485
486	if (cookie == ri->ri_active)
487		return (0);
488
489	return rasops_show_screen(ri, cookie, waitok, cb, cbarg);
490}
491
492int
493vgafb_mapregs(struct vga_config *vc, struct pci_attach_args *pa)
494{
495	bus_addr_t ba;
496	bus_size_t bs;
497	int hasio = 0, hasmem = 0, hasmmio = 0;
498	uint32_t i, cf;
499	int rv;
500
501	for (i = PCI_MAPREG_START; i <= PCI_MAPREG_PPB_END; i += 4) {
502		cf = pci_conf_read(pa->pa_pc, pa->pa_tag, i);
503		if (PCI_MAPREG_TYPE(cf) == PCI_MAPREG_TYPE_IO) {
504			if (hasio)
505				continue;
506			rv = pci_io_find(pa->pa_pc, pa->pa_tag, i,
507			    &vc->iobase, &vc->iosize);
508			if (rv != 0) {
509#if notyet
510				if (rv != ENOENT)
511					printf("%s: failed to find io at 0x%x\n",
512					    DEVNAME(sc), i);
513#endif
514				continue;
515			}
516			hasio = 1;
517		} else {
518			/* Memory mapping... frame memory or mmio? */
519			rv = pci_mem_find(pa->pa_pc, pa->pa_tag, i,
520			    &ba, &bs, NULL);
521			if (rv != 0) {
522#if notyet
523				if (rv != ENOENT)
524					printf("%s: failed to find mem at 0x%x\n",
525					    DEVNAME(sc), i);
526#endif
527				continue;
528			}
529
530			if (bs == 0 /* || ba == 0 */) {
531				/* ignore this entry */
532			} else if (hasmem == 0) {
533				/*
534				 * first memory slot found goes into memory,
535				 * this is for the case of no mmio
536				 */
537				vc->membase = ba;
538				vc->memsize = bs;
539				hasmem = 1;
540			} else {
541				/*
542				 * Oh, we have a second `memory'
543				 * region, is this region the vga memory
544				 * or mmio, we guess that memory is
545				 * the larger of the two.
546				 */
547				if (vc->memsize >= bs) {
548					/* this is the mmio */
549					vc->mmiobase = ba;
550					vc->mmiosize = bs;
551					hasmmio = 1;
552				} else {
553					/* this is the memory */
554					vc->mmiobase = vc->membase;
555					vc->mmiosize = vc->memsize;
556					vc->membase = ba;
557					vc->memsize = bs;
558				}
559			}
560		}
561	}
562
563	/* failure to initialize io ports should not prevent attachment */
564	if (hasmem == 0) {
565		printf(": could not find memory space\n");
566		return (1);
567	}
568
569	if (hasmmio)
570		printf (", mmio");
571	printf("\n");
572
573	return (0);
574}
575