[BACK]Return to vgafb.c CVS log [TXT][DIR] Up to [local] / sys / arch / sparc64 / dev

File: [local] / sys / arch / sparc64 / dev / vgafb.c (download)

Revision 1.1, Tue Mar 4 16:08:31 2008 UTC (16 years, 2 months ago) by nbrk
Branch point for: MAIN

Initial revision

/*	$OpenBSD: vgafb.c,v 1.50 2007/03/06 19:13:13 miod Exp $	*/

/*
 * Copyright (c) 2001 Jason L. Wright (jason@thought.net)
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * Effort sponsored in part by the Defense Advanced Research Projects
 * Agency (DARPA) and Air Force Research Laboratory, Air Force
 * Materiel Command, USAF, under agreement number F30602-01-2-0537.
 *
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/errno.h>
#include <sys/ioctl.h>
#include <sys/malloc.h>
#include <sys/pciio.h>

#include <uvm/uvm_extern.h>

#include <machine/autoconf.h>
#include <machine/bus.h>
#include <machine/intr.h>
#include <machine/openfirm.h>

#include <dev/pci/pcidevs.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>

#include <dev/wscons/wsconsio.h>
#include <dev/wscons/wsdisplayvar.h>
#include <dev/rasops/rasops.h>

#include <machine/fbvar.h>

struct vgafb_softc {
	struct sunfb sc_sunfb;
	int sc_nscreens;
	int sc_node, sc_ofhandle;
	bus_space_tag_t sc_mem_t;
	bus_space_tag_t sc_io_t;
	pcitag_t sc_pcitag;
	bus_space_handle_t sc_mem_h;
	bus_addr_t sc_io_addr, sc_mem_addr, sc_mmio_addr;
	bus_size_t sc_io_size, sc_mem_size, sc_mmio_size;
	int sc_console;
	u_int sc_mode;
	u_int8_t sc_cmap_red[256];
	u_int8_t sc_cmap_green[256];
	u_int8_t sc_cmap_blue[256];
};

int vgafb_mapregs(struct vgafb_softc *, struct pci_attach_args *);
int vgafb_rommap(struct vgafb_softc *, struct pci_attach_args *);
int vgafb_ioctl(void *, u_long, caddr_t, int, struct proc *);
int vgafb_alloc_screen(void *, const struct wsscreen_descr *, void **,
    int *, int *, long *);
void vgafb_free_screen(void *, void *);
int vgafb_show_screen(void *, void *, int,
    void (*cb)(void *, int, int), void *);
paddr_t vgafb_mmap(void *, off_t, int);
int vgafb_is_console(int);
int vgafb_getcmap(struct vgafb_softc *, struct wsdisplay_cmap *);
int vgafb_putcmap(struct vgafb_softc *, struct wsdisplay_cmap *);
void vgafb_setcolor(void *, u_int, u_int8_t, u_int8_t, u_int8_t);

struct wsdisplay_accessops vgafb_accessops = {
	vgafb_ioctl,
	vgafb_mmap,
	vgafb_alloc_screen,
	vgafb_free_screen,
	vgafb_show_screen,
	0 /* load_font */
};

int	vgafbmatch(struct device *, void *, void *);
void	vgafbattach(struct device *, struct device *, void *);

struct cfattach vgafb_ca = {
	sizeof (struct vgafb_softc), vgafbmatch, vgafbattach
};

struct cfdriver vgafb_cd = {
	NULL, "vgafb", DV_DULL
};

#ifdef APERTURE
extern int allowaperture;
#endif

static const struct pci_matchid ifb_devices[] = {
	{ PCI_VENDOR_INTERGRAPH, 0x108 },	/* XXX */
	{ PCI_VENDOR_INTERGRAPH, 0x140 },	/* XXX */
	{ PCI_VENDOR_INTERGRAPH, PCI_PRODUCT_INTERGRAPH_EXPERT3D },
	{ PCI_VENDOR_3DLABS,	 0x7a1 },	/* Wildcat III 6210 */
	{ PCI_VENDOR_3DLABS,	 0x7a2 },	/* Sun XVR-500 */
	{ PCI_VENDOR_3DLABS,	 0x7a3 },	/* Wildcat IV 7210 */
};

int
vgafbmatch(parent, vcf, aux)
	struct device *parent;
	void *vcf, *aux;
{
	struct pci_attach_args *pa = aux;
	int node;
	char *name;

	/*
	 * Do not match on Expert3D devices, which need a different
	 * driver.
	 */
	if (pci_matchbyid(pa, ifb_devices,
	    sizeof(ifb_devices) / sizeof(ifb_devices[0])) != 0)
		return (0);

	node = PCITAG_NODE(pa->pa_tag);
	name = getpropstring(node, "name");
	if (strcmp(name, "SUNW,Expert3D") == 0 ||
	    strcmp(name, "SUNW,Expert3D-Lite") == 0)
		return (0);


	if (PCI_CLASS(pa->pa_class) == PCI_CLASS_PREHISTORIC &&
	    PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_PREHISTORIC_VGA)
		return (1);

	if (PCI_CLASS(pa->pa_class) == PCI_CLASS_DISPLAY &&
	    PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_DISPLAY_VGA)
		return (1);

	if (PCI_CLASS(pa->pa_class) == PCI_CLASS_DISPLAY &&
	    PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_DISPLAY_MISC)
		return (1);

	return (0);
}

void    
vgafbattach(parent, self, aux)
	struct device *parent, *self;
	void *aux;
{
	struct vgafb_softc *sc = (struct vgafb_softc *)self;
	struct pci_attach_args *pa = aux;

	sc->sc_mem_t = pa->pa_memt;
	sc->sc_io_t = pa->pa_iot;
	sc->sc_node = PCITAG_NODE(pa->pa_tag);
	sc->sc_pcitag = pa->pa_tag;

	printf("\n");

	if (vgafb_mapregs(sc, pa))
		return;

	sc->sc_console = vgafb_is_console(sc->sc_node);

	fb_setsize(&sc->sc_sunfb, 8, 1152, 900, sc->sc_node, 0);
	if (sc->sc_sunfb.sf_depth == 24) {
		sc->sc_sunfb.sf_depth = 32;
		sc->sc_sunfb.sf_linebytes =
		    (sc->sc_sunfb.sf_depth / 8) * sc->sc_sunfb.sf_width;
		sc->sc_sunfb.sf_fbsize =
		    sc->sc_sunfb.sf_height * sc->sc_sunfb.sf_linebytes;
	}

	sc->sc_sunfb.sf_ro.ri_bits = (void *)bus_space_vaddr(sc->sc_mem_t,
	    sc->sc_mem_h);
	sc->sc_sunfb.sf_ro.ri_hw = sc;

	fbwscons_init(&sc->sc_sunfb,
	    RI_BSWAP | (sc->sc_console ? 0 : RI_FORCEMONO | RI_CLEAR));

	if (sc->sc_console) {
		sc->sc_ofhandle = OF_stdout();
		fbwscons_setcolormap(&sc->sc_sunfb, vgafb_setcolor);
		fbwscons_console_init(&sc->sc_sunfb, -1);
	} else {
		/* sc->sc_ofhandle = PCITAG_NODE(sc->sc_pcitag); */
	}

	fbwscons_attach(&sc->sc_sunfb, &vgafb_accessops, sc->sc_console);
}

int
vgafb_ioctl(v, cmd, data, flags, p)
	void *v;
	u_long cmd;
	caddr_t data;
	int flags;
	struct proc *p;
{
	struct vgafb_softc *sc = v;
	struct wsdisplay_fbinfo *wdf;
	struct pcisel *sel;

	switch (cmd) {
	case WSDISPLAYIO_GTYPE:
		*(u_int *)data = WSDISPLAY_TYPE_UNKNOWN;
		break;
	case WSDISPLAYIO_SMODE:
		sc->sc_mode = *(u_int *)data;
		if (sc->sc_mode == WSDISPLAYIO_MODE_EMUL) {
			if (sc->sc_console)	/* XXX needs sc_ofhandle */
				fbwscons_setcolormap(&sc->sc_sunfb,
				    vgafb_setcolor);
		}
		break;
	case WSDISPLAYIO_GINFO:
		wdf = (void *)data;
		wdf->height = sc->sc_sunfb.sf_height;
		wdf->width  = sc->sc_sunfb.sf_width;
		wdf->depth  = sc->sc_sunfb.sf_depth;
		wdf->cmsize = 256;
		break;
	case WSDISPLAYIO_LINEBYTES:
		*(u_int *)data = sc->sc_sunfb.sf_linebytes;
		break;
		
	case WSDISPLAYIO_GETCMAP:
		if (sc->sc_console == 0)
			return (EINVAL);
		return vgafb_getcmap(sc, (struct wsdisplay_cmap *)data);
	case WSDISPLAYIO_PUTCMAP:
		if (sc->sc_console == 0)
			return (EINVAL);
		return vgafb_putcmap(sc, (struct wsdisplay_cmap *)data);

	case WSDISPLAYIO_GPCIID:
		sel = (struct pcisel *)data;
		sel->pc_bus = PCITAG_BUS(sc->sc_pcitag);
		sel->pc_dev = PCITAG_DEV(sc->sc_pcitag);
		sel->pc_func = PCITAG_FUN(sc->sc_pcitag);
		break;

	case WSDISPLAYIO_SVIDEO:
	case WSDISPLAYIO_GVIDEO:
		break;

	case WSDISPLAYIO_GCURPOS:
	case WSDISPLAYIO_SCURPOS:
	case WSDISPLAYIO_GCURMAX:
	case WSDISPLAYIO_GCURSOR:
	case WSDISPLAYIO_SCURSOR:
	default:
		return -1; /* not supported yet */
        }

	return (0);
}

int
vgafb_getcmap(sc, cm)
	struct vgafb_softc *sc;
	struct wsdisplay_cmap *cm;
{
	u_int index = cm->index;
	u_int count = cm->count;
	int error;

	if (index >= 256 || count > 256 - index)
		return (EINVAL);

	error = copyout(&sc->sc_cmap_red[index], cm->red, count);
	if (error)
		return (error);
	error = copyout(&sc->sc_cmap_green[index], cm->green, count);
	if (error)
		return (error);
	error = copyout(&sc->sc_cmap_blue[index], cm->blue, count);
	if (error)
		return (error);
	return (0);
}

int
vgafb_putcmap(sc, cm)
	struct vgafb_softc *sc;
	struct wsdisplay_cmap *cm;
{
	u_int index = cm->index;
	u_int count = cm->count;
	u_int i;
	int error;
	u_char *r, *g, *b;

	if (index >= 256 || count > 256 - index)
		return (EINVAL);

	if ((error = copyin(cm->red, &sc->sc_cmap_red[index], count)) != 0)
		return (error);
	if ((error = copyin(cm->green, &sc->sc_cmap_green[index], count)) != 0)
		return (error);
	if ((error = copyin(cm->blue, &sc->sc_cmap_blue[index], count)) != 0)
		return (error);

	r = &sc->sc_cmap_red[index];
	g = &sc->sc_cmap_green[index];
	b = &sc->sc_cmap_blue[index];

	for (i = 0; i < count; i++) {
		OF_call_method("color!", sc->sc_ofhandle, 4, 0, *r, *g, *b,
		    index);
		r++, g++, b++, index++;
	}
	return (0);
}

void
vgafb_setcolor(v, index, r, g, b)
	void *v;
	u_int index;
	u_int8_t r, g, b;
{
	struct vgafb_softc *sc = v;

	sc->sc_cmap_red[index] = r;
	sc->sc_cmap_green[index] = g;
	sc->sc_cmap_blue[index] = b;
	OF_call_method("color!", sc->sc_ofhandle, 4, 0, r, g, b, index);
}

int
vgafb_alloc_screen(v, type, cookiep, curxp, curyp, attrp)
	void *v;
	const struct wsscreen_descr *type;
	void **cookiep;
	int *curxp, *curyp;
	long *attrp;
{
	struct vgafb_softc *sc = v;

	if (sc->sc_nscreens > 0)
		return (ENOMEM);

	*cookiep = &sc->sc_sunfb.sf_ro;
	*curyp = 0;
	*curxp = 0;
	sc->sc_sunfb.sf_ro.ri_ops.alloc_attr(&sc->sc_sunfb.sf_ro,
	    WSCOL_BLACK, WSCOL_WHITE, WSATTR_WSCOLORS, attrp);
	sc->sc_nscreens++;
	return (0);
}

void
vgafb_free_screen(v, cookie)
	void *v;
	void *cookie;
{
	struct vgafb_softc *sc = v;

	sc->sc_nscreens--;
}

int
vgafb_show_screen(v, cookie, waitok, cb, cbarg)
	void *v;
	void *cookie;
	int waitok;
	void (*cb)(void *, int, int);
	void *cbarg;
{
	return (0);
}

paddr_t
vgafb_mmap(v, off, prot)
	void *v;
	off_t off;
	int prot;
{
	struct vgafb_softc *sc = v;

	if (off & PGOFSET)
		return (-1);

	switch (sc->sc_mode) {
	case WSDISPLAYIO_MODE_MAPPED:
#ifdef APERTURE
		if (allowaperture == 0)
			return (-1);
#endif

		if (sc->sc_mmio_size == 0)
			return (-1);

		if (off >= sc->sc_mem_addr &&
		    off < (sc->sc_mem_addr + sc->sc_mem_size))
			return (bus_space_mmap(sc->sc_mem_t,
			    sc->sc_mem_addr, off - sc->sc_mem_addr,
			    prot, BUS_SPACE_MAP_LINEAR));

		if (off >= sc->sc_mmio_addr &&
		    off < (sc->sc_mmio_addr + sc->sc_mmio_size))
			return (bus_space_mmap(sc->sc_mem_t,
			    sc->sc_mmio_addr, off - sc->sc_mmio_addr,
			    prot, BUS_SPACE_MAP_LINEAR));
		break;

	case WSDISPLAYIO_MODE_DUMBFB:
		if (off >= 0 && off < sc->sc_mem_size)
			return (bus_space_mmap(sc->sc_mem_t, sc->sc_mem_addr,
			    off, prot, BUS_SPACE_MAP_LINEAR));
		break;
	}

	return (-1);
}

int
vgafb_is_console(node)
	int node;
{
	extern int fbnode;

	return (fbnode == node);
}

int
vgafb_mapregs(sc, pa)
	struct vgafb_softc *sc;
	struct pci_attach_args *pa;
{
	bus_addr_t ba;
	bus_size_t bs;
	int hasio = 0, hasmem = 0, hasmmio = 0; 
	u_int32_t i, cf;
	int rv;

	for (i = PCI_MAPREG_START; i <= PCI_MAPREG_PPB_END; i += 4) {
		cf = pci_conf_read(pa->pa_pc, pa->pa_tag, i);
		if (PCI_MAPREG_TYPE(cf) == PCI_MAPREG_TYPE_IO) {
			if (hasio)
				continue;
			rv = pci_io_find(pa->pa_pc, pa->pa_tag, i,
			    &sc->sc_io_addr, &sc->sc_io_size);
			if (rv != 0) {
				if (rv != ENOENT)
					printf("%s: failed to find io at 0x%x\n",
					    sc->sc_sunfb.sf_dev.dv_xname, i);
				continue;
			}
			hasio = 1;
		} else {
			/* Memory mapping... frame memory or mmio? */
			rv = pci_mem_find(pa->pa_pc, pa->pa_tag, i,
			    &ba, &bs, NULL);
			if (rv != 0) {
				if (rv != ENOENT)
					printf("%s: failed to find mem at 0x%x\n",
					    sc->sc_sunfb.sf_dev.dv_xname, i);
				continue;
			}

			if (bs == 0 /* || ba == 0 */) {
				/* ignore this entry */
			} else if (hasmem == 0) {
				/*
				 * first memory slot found goes into memory,
				 * this is for the case of no mmio
				 */
				sc->sc_mem_addr = ba;
				sc->sc_mem_size = bs;
				hasmem = 1;
			} else {
				/*
				 * Oh, we have a second `memory'
				 * region, is this region the vga memory
				 * or mmio, we guess that memory is
				 * the larger of the two.
				 */
				if (sc->sc_mem_size > bs) {
					/* this is the mmio */
					sc->sc_mmio_addr = ba;
					/* ATI driver maps 0x80000 mmio, grr */
					if (bs < 0x80000) {
						bs = 0x80000;
					}
					sc->sc_mmio_size = bs;
					hasmmio = 1;
				} else {
					/* this is the memory */
					sc->sc_mmio_addr = sc->sc_mem_addr;
					sc->sc_mmio_size = sc->sc_mem_size;
					sc->sc_mem_addr = ba;
					sc->sc_mem_size = bs;
					/* ATI driver maps 0x80000 mmio, grr */
					if (sc->sc_mmio_size < 0x80000) {
						sc->sc_mmio_size = 0x80000;
					}
				}
			}
		}
	}

	if (hasmem != 0) {
		if (bus_space_map(pa->pa_memt, sc->sc_mem_addr, sc->sc_mem_size,
		    0, &sc->sc_mem_h)) {
			printf("%s: can't map mem space\n",
			    sc->sc_sunfb.sf_dev.dv_xname);
			return (1);
		}
	}

	/* failure to initialize io ports should not prevent attachment */
	if (hasmem == 0) {
		printf("%s: could not find memory space\n",
		    sc->sc_sunfb.sf_dev.dv_xname);
		return (1);
	}

#ifdef DIAGNOSTIC
	if (hasmmio == 0) {
		printf("%s: WARNING: no mmio space configured\n",
		    sc->sc_sunfb.sf_dev.dv_xname);
	}
#endif

	return (0);
}