#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/pci/vga_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_ioctl(void *, u_long, caddr_t, int, struct proc *);
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 = {
.ioctl = vgafb_ioctl,
.mmap = vgafb_mmap
};
int vgafbmatch(struct device *, void *, void *);
void vgafbattach(struct device *, struct device *, void *);
const struct cfattach vgafb_ca = {
sizeof (struct vgafb_softc), vgafbmatch, vgafbattach
};
struct cfdriver vgafb_cd = {
NULL, "vgafb", DV_DULL
};
#ifdef APERTURE
extern int allowaperture;
#endif
int
vgafbmatch(struct device *parent, void *vcf, void *aux)
{
struct pci_attach_args *pa = aux;
int node;
if (ifb_ident(aux) != 0)
return (0);
node = PCITAG_NODE(pa->pa_tag);
if (!vgafb_is_console(node))
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(struct device *parent, struct device *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), sc->sc_console);
if (sc->sc_console) {
sc->sc_ofhandle = OF_stdout();
fbwscons_setcolormap(&sc->sc_sunfb, vgafb_setcolor);
fbwscons_console_init(&sc->sc_sunfb, -1);
} else {
}
#ifdef RAMDISK_HOOKS
if (vga_aperture_needed(pa))
printf("%s: aperture needed\n", sc->sc_sunfb.sf_dev.dv_xname);
#endif
fbwscons_attach(&sc->sc_sunfb, &vgafb_accessops, sc->sc_console);
}
int
vgafb_ioctl(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_PCIVGA;
break;
case WSDISPLAYIO_SMODE:
sc->sc_mode = *(u_int *)data;
if (sc->sc_mode == WSDISPLAYIO_MODE_EMUL) {
if (sc->sc_console)
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->stride = sc->sc_sunfb.sf_linebytes;
wdf->offset = 0;
wdf->cmsize = 256;
break;
case WSDISPLAYIO_GETSUPPORTEDDEPTH:
if (sc->sc_sunfb.sf_depth == 32)
*(u_int *)data = WSDISPLAYIO_DEPTH_24_32;
else
return (-1);
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;
}
return (0);
}
int
vgafb_getcmap(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(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(void *v, u_int index, u_int8_t r, u_int8_t g, u_int8_t 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);
}
paddr_t
vgafb_mmap(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(int node)
{
extern int fbnode;
return (fbnode == node);
}
int
vgafb_mapregs(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 bar, cf;
int rv;
for (bar = PCI_MAPREG_START; bar <= PCI_MAPREG_PPB_END; bar += 4) {
cf = pci_conf_read(pa->pa_pc, pa->pa_tag, bar);
if (PCI_MAPREG_TYPE(cf) == PCI_MAPREG_TYPE_IO) {
if (hasio)
continue;
rv = pci_mapreg_info(pa->pa_pc, pa->pa_tag, bar,
_PCI_MAPREG_TYPEBITS(cf),
&sc->sc_io_addr, &sc->sc_io_size, NULL);
if (rv != 0) {
if (rv != ENOENT)
printf("%s: failed to find io at 0x%x\n",
sc->sc_sunfb.sf_dev.dv_xname, bar);
continue;
}
hasio = 1;
} else {
rv = pci_mapreg_info(pa->pa_pc, pa->pa_tag, bar,
_PCI_MAPREG_TYPEBITS(cf), &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, bar);
continue;
}
if (bs == 0 ) {
} else if (hasmem == 0) {
sc->sc_mem_addr = ba;
sc->sc_mem_size = bs;
hasmem = 1;
} else {
if (sc->sc_mem_size >= bs) {
sc->sc_mmio_addr = ba;
sc->sc_mmio_size = bs;
hasmmio = 1;
} else {
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;
}
}
if (PCI_MAPREG_MEM_TYPE(cf) ==
PCI_MAPREG_MEM_TYPE_64BIT)
bar += 4;
}
}
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);
}
}
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);
}